[
  {
    "path": ".circleci/config.yml",
    "content": "# Java Gradle CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-java/ for more details\n#\nversion: 2\ngeneral:\n  branches:\n    only:\n      - main\n      - dev\njobs:\n  build:\n    docker:\n      - image: circleci/openjdk:11-jdk\n\n    working_directory: ~/repo\n\n    environment:\n      JVM_OPTS: -Xmx3200m\n      TERM: dumb\n\n    steps:\n      - checkout\n      # - run: if [ -z \"$SONAR_TOKEN\" ]; then echo \"SONAR_TOKEN missing\"; else echo \"SONAR_TOKEN is here\"; fi\n      # - run: echo $SONAR_TOKEN\n      # Run jar first, as the checkstyle plugin depends on atlas itself here.\n      - run:\n          name: Quality checks (No tests)\n          command: ./gradlew jar check -x test -x integrationTest\n      - run:\n          name: Tests\n          command: ./gradlew check\n          no_output_timeout: 30m\n      # - run: .circleci/sonar.sh\n      # - run: ./gradlew cleanPyatlas buildPyatlas\n"
  },
  {
    "path": ".circleci/sonar.sh",
    "content": "#!/usr/bin/env sh\n\nif [ -z \"$CIRCLE_PR_NUMBER\" ];\nthen\n    echo \"Running sonarqube in a regular build\"\n    echo \"sonar.branch.name=$CIRCLE_BRANCH\"\n\t#TODO: Remove echo below\n\techo ./gradlew jacocoTestReport sonarqube \\\n\t\t-Dsonar.branch.name=$CIRCLE_BRANCH \\\n\t\t-Dsonar.organization=osmlab \\\n\t\t-Dsonar.host.url=https://sonarcloud.io \\\n\t\t-Dsonar.login=$SONAR_TOKEN \\\n\t\t-Dsonar.junit.reportPaths=build/test-results/test \\\n\t\t-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml\nelse\n    CIRCLE_PR_BRANCH=`curl -s https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pulls/$CIRCLE_PR_NUMBER | jq -r '.head.ref'`\n    CIRCLE_PR_TARGET_BRANCH=`curl -s https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pulls/$CIRCLE_PR_NUMBER | jq -r '.base.ref'`\n\tSONAR_PULLREQUEST_BRANCH=\"$CIRCLE_PR_USERNAME/$CIRCLE_PR_BRANCH\"\n\techo \"Running sonarqube in Pull Request $CIRCLE_PR_NUMBER\"\n\techo \"sonar.pullrequest.key=$CIRCLE_PR_NUMBER\"\n\techo \"sonar.pullrequest.branch=$SONAR_PULLREQUEST_BRANCH\"\n\techo \"sonar.pullrequest.base=$CIRCLE_PR_TARGET_BRANCH\"\n\t#TODO: Remove echo below\n\techo ./gradlew jacocoTestReport sonarqube \\\n\t\t-Dsonar.organization=osmlab \\\n\t\t-Dsonar.host.url=https://sonarcloud.io \\\n\t\t-Dsonar.login=$SONAR_TOKEN \\\n\t\t-Dsonar.junit.reportPaths=build/test-results/test \\\n\t\t-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \\\n\t\t-Dsonar.pullrequest.key=$CIRCLE_PR_NUMBER \\\n\t\t-Dsonar.pullrequest.branch=$SONAR_PULLREQUEST_BRANCH \\\n\t\t-Dsonar.pullrequest.base=$CIRCLE_PR_TARGET_BRANCH \\\n\t\t-Dsonar.pullrequest.provider=github \\\n\t\t-Dsonar.pullrequest.github.repository=osmlab/atlas \\\n\t\t-Dsonar.pullrequest.github.endpoint=https://api.github.com/ \\\n\t\t-Dsonar.pullrequest.github.token.secured=$SONAR_PR_DECORATION_GITHUB_TOKEN\nfi\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "### Description:\n\nAdd description here.\n\n### Potential Impact:\n\nList potential impact to downstream libraries here (ex. atlas-checks, atlas-generator)\n\n### Unit Test Approach:\n\nDescribe unit tests being added with that PR.\n\n### Test Results:\n\nDescribe other (non-unit) test results here.\n\n------\n\nIn doubt: [Contributing Guidelines](CONTRIBUTING.md)\n"
  },
  {
    "path": ".github/workflow_scripts/decrypt_gpg_key.sh",
    "content": "#!/bin/sh\n\n# Decrypt the file\n\nif [ \"$MANUAL_RELEASE_TRIGGERED\" = \"true\" ];\nthen\n    # --batch to prevent interactive command\n    # --yes to assume \"yes\" for questions\n    gpg --quiet --batch --yes --decrypt --passphrase=\"$GPG_AES256_PASSPHRASE\" \\\n    --output \"$GPG_KEY_LOCATION\" .github/workflow_data/secret.gpg.aes256\n\n    chmod 700 \"$GPG_KEY_LOCATION\"\nelse\n    echo \"Not decrypting key, since MANUAL_RELEASE_TRIGGERED=$MANUAL_RELEASE_TRIGGERED\"\nfi\n"
  },
  {
    "path": ".github/workflow_scripts/deploy.sh",
    "content": "#!/bin/sh\n\nCURRENT_BRANCH=$(echo \"$GITHUB_REF\" | awk 'BEGIN { FS = \"/\" } ; { print $3 }')\necho \"Criterion for Publishing artifacts to Maven Central:\"\necho \"Current Branch (Should be main): $CURRENT_BRANCH\"\necho \"Pull Request (Should be empty): $GITHUB_HEAD_REF\"\necho \"Manual Release Triggered (Should be true): $MANUAL_RELEASE_TRIGGERED\"\n\nif [ \"$CURRENT_BRANCH\" = \"main\" ] && [ -z \"$GITHUB_HEAD_REF\" ];\nthen\n    echo \"On branch main, not in a Pull Request\"\n    if [ \"$MANUAL_RELEASE_TRIGGERED\" = \"true\" ];\n    then\n        echo \"Sign, Upload archives to local repo, Upload archives to Sonatype, Close and release repository.\"\n        ./gradlew publish publishToNexusAndClose\n        #python -m pip install --user --upgrade twine\n        #twine upload ./pyatlas/dist/*\n    else\n        echo \"Not publishing artifacts, since MANUAL_RELEASE_TRIGGERED=$MANUAL_RELEASE_TRIGGERED\"\n    fi\nelse\n    echo \"Not publishing artifacts, since not on branch main, or in a Pull Request\"\nfi\n"
  },
  {
    "path": ".github/workflow_scripts/merge-dev-to-main.sh",
    "content": "#!/usr/bin/env sh\n\nGITHUB_REPO=\"osmlab/atlas\"\nMERGE_BRANCH=main\nSOURCE_BRANCH=dev\n\nFUNCTION_NAME=\"merge-$SOURCE_BRANCH-to-$MERGE_BRANCH\"\nCURRENT_BRANCH=$(echo \"$GITHUB_REF\" | awk 'BEGIN { FS = \"/\" } ; { print $3 }')\n\necho \"$FUNCTION_NAME: $GITHUB_REPO\"\necho \"$FUNCTION_NAME: CURRENT_BRANCH = $CURRENT_BRANCH\"\necho \"$FUNCTION_NAME: GITHUB_HEAD_REF = $GITHUB_HEAD_REF\"\n\nif [ \"$CURRENT_BRANCH\" != \"$SOURCE_BRANCH\" ];\nthen\n    echo \"$FUNCTION_NAME: Exiting! Branch is not $SOURCE_BRANCH: ($CURRENT_BRANCH)\"\n    exit 0;\nfi\n\nif [ -n \"$GITHUB_HEAD_REF\" ];\nthen\n    PULL_REQUEST_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = \"/\" } ; { print $3 }')\n    echo \"$FUNCTION_NAME: Exiting! This is a Pull Request: $PULL_REQUEST_NUMBER\"\n    exit 0;\nfi\n\n: ${MERGE_TAG_GITHUB_SECRET_TOKEN:?\"MERGE_TAG_GITHUB_SECRET_TOKEN needs to be set in the workflow yml file!\"}\n: ${GITHUB_SHA:?\"GITHUB_SHA needs to be available to tag the right commit!\"}\n\nTEMPORARY_REPOSITORY=$(mktemp -d)\ngit clone \"https://github.com/$GITHUB_REPO\" \"$TEMPORARY_REPOSITORY\"\ncd \"$TEMPORARY_REPOSITORY\"\n\necho \"Checking out $SOURCE_BRANCH\"\ngit checkout $SOURCE_BRANCH\ngit checkout -b tomerge \"$GITHUB_SHA\"\n\necho \"Checking out $MERGE_BRANCH\"\ngit checkout \"$MERGE_BRANCH\"\n\necho \"Merging temporary branch tomerge ($GITHUB_SHA) from $SOURCE_BRANCH into $MERGE_BRANCH\"\ngit merge --ff-only \"tomerge\"\n\necho \"Pushing to $GITHUB_REPO\"\n# Redirect to /dev/null to avoid secret leakage\ngit push \"https://$MERGE_TAG_GITHUB_SECRET_TOKEN@github.com/$GITHUB_REPO\" \"$MERGE_BRANCH\" > /dev/null 2>&1\n"
  },
  {
    "path": ".github/workflow_scripts/sonar.sh",
    "content": "#!/usr/bin/env sh\n\nif [ -n \"$GITHUB_HEAD_REF\" ];\nthen\n    PULL_REQUEST_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = \"/\" } ; { print $3 }')\n    echo \"Running sonarqube in Pull Request $PULL_REQUEST_NUMBER\"\n    echo \"sonar.pullrequest.key=$PULL_REQUEST_NUMBER\"\n    echo \"sonar.pullrequest.branch=$GITHUB_HEAD_REF\"\n    echo \"sonar.pullrequest.base=$GITHUB_BASE_REF\"\n    ./gradlew jacocoTestReport sonarqube \\\n        -Dsonar.organization=osmlab \\\n        -Dsonar.host.url=https://sonarcloud.io \\\n        -Dsonar.login=\"$SONAR_TOKEN\" \\\n        -Dsonar.junit.reportPaths=build/test-results/test \\\n        -Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \\\n        -Dsonar.pullrequest.key=\"$PULL_REQUEST_NUMBER\" \\\n        -Dsonar.pullrequest.branch=\"$GITHUB_HEAD_REF\" \\\n        -Dsonar.pullrequest.base=\"$GITHUB_BASE_REF\" \\\n        -Dsonar.pullrequest.provider=github \\\n        -Dsonar.pullrequest.github.repository=osmlab/atlas \\\n        -Dsonar.pullrequest.github.endpoint=https://api.github.com/ \\\n        -Dsonar.pullrequest.github.token.secured=\"$SONAR_PR_DECORATION_GITHUB_TOKEN\"\nelse\n    CURRENT_BRANCH=$(echo \"$GITHUB_REF\" | awk 'BEGIN { FS = \"/\" } ; { print $3 }')\n    echo \"Running sonarqube in branch $CURRENT_BRANCH\"\n    ./gradlew jacocoTestReport sonarqube \\\n        -Dsonar.branch.name=\"$CURRENT_BRANCH\" \\\n        -Dsonar.organization=osmlab \\\n        -Dsonar.host.url=https://sonarcloud.io \\\n        -Dsonar.login=\"$SONAR_TOKEN\" \\\n        -Dsonar.junit.reportPaths=build/test-results/test \\\n        -Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml\nfi\n"
  },
  {
    "path": ".github/workflow_scripts/tag-main.sh",
    "content": "#!/usr/bin/env sh\n\nGITHUB_REPO=\"osmlab/atlas\"\nRELEASE_BRANCH=main\n\nFUNCTION_NAME=\"tag-$RELEASE_BRANCH\"\nCURRENT_BRANCH=$(echo $GITHUB_REF | awk 'BEGIN { FS = \"/\" } ; { print $3 }')\n\necho \"$FUNCTION_NAME: $GITHUB_REPO\"\necho \"$FUNCTION_NAME: CURRENT_BRANCH = $CURRENT_BRANCH\"\necho \"$FUNCTION_NAME: GITHUB_HEAD_REF = $GITHUB_HEAD_REF\"\n\nif [ \"$CURRENT_BRANCH\" != \"$RELEASE_BRANCH\" ];\nthen\n    echo \"$FUNCTION_NAME: Exiting! Branch is not $RELEASE_BRANCH: ($CURRENT_BRANCH)\"\n    exit 0;\nfi\n\nif [ -n \"$GITHUB_HEAD_REF\" ];\nthen\n    PULL_REQUEST_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = \"/\" } ; { print $3 }')\n    echo \"$FUNCTION_NAME: Exiting! This is a Pull Request: $PULL_REQUEST_NUMBER\"\n    exit 0;\nfi\n\nif [ \"$MANUAL_RELEASE_TRIGGERED\" != \"true\" ];\nthen\n    echo \"$FUNCTION_NAME: Exiting! This is not a release build.\"\n    exit 0;\nfi\n\n: ${MERGE_TAG_GITHUB_SECRET_TOKEN:?\"MERGE_TAG_GITHUB_SECRET_TOKEN needs to be set in the workflow yml file!\"}\n: ${GITHUB_SHA:?\"GITHUB_SHA needs to be available to tag the right commit!\"}\n\nexport GIT_COMMITTER_EMAIL=\"github-actions@github.com\"\nexport GIT_COMMITTER_NAME=\"Github Actions CI\"\n\nTEMPORARY_REPOSITORY=$(mktemp -d)\ngit clone \"https://github.com/$GITHUB_REPO\" \"$TEMPORARY_REPOSITORY\"\ncd \"$TEMPORARY_REPOSITORY\"\n\necho \"Checking out $RELEASE_BRANCH (commit $GITHUB_SHA)\"\ngit checkout $GITHUB_SHA\n\nPROJECT_VERSION=$(cat gradle.properties | grep \"\\-SNAPSHOT\" | awk -F '=' '{print $2}' | awk -F '-' '{print $1}')\n: ${PROJECT_VERSION:?\"PROJECT_VERSION could not be found.\"}\n\necho \"Tagging $RELEASE_BRANCH (commit $GITHUB_SHA) at version $PROJECT_VERSION\"\ngit tag -a $PROJECT_VERSION -m \"Release $PROJECT_VERSION\"\n\necho \"Pushing tag $PROJECT_VERSION to $GITHUB_REPO\"\n# Redirect to /dev/null to avoid secret leakage\ngit push \"https://$MERGE_TAG_GITHUB_SECRET_TOKEN@github.com/$GITHUB_REPO\" \"$PROJECT_VERSION\" > /dev/null 2>&1\n"
  },
  {
    "path": ".github/workflow_scripts/update_project_version.sh",
    "content": "#!/bin/sh\n\nCURRENT_BRANCH=$(echo \"$GITHUB_REF\" | awk 'BEGIN { FS = \"/\" } ; { print $3 }')\necho \"Current Branch: $CURRENT_BRANCH\"\n\nif [ \"$CURRENT_BRANCH\" = \"main\" ] && [ \"$MANUAL_RELEASE_TRIGGERED\" = \"true\" ];\nthen\n\techo \"This is a manual release, change the version locally to remove the -SNAPSHOT\"\n\tsed -i \"s/-SNAPSHOT//g\" gradle.properties\nelse\n\techo \"Not a manual release, keeping -SNAPSHOT\"\nfi\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Continuous Integration\n\non:\n  push:\n    branches: [ main, dev ]\n  pull_request:\n    branches: [ dev ]\n  workflow_dispatch:\n    inputs:\n      MANUAL_RELEASE_TRIGGERED:\n        description: \"Environment Variable used to trigger a Maven Central release\"\n        required: false\n        default: \"true\"\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n\n# Setup\n    - uses: actions/checkout@v2\n    - name: Set up JDK 11\n      uses: actions/setup-java@v4\n      with:\n        distribution: 'zulu'\n        java-version: 11\n#    - name: Set up Python\n#      uses: actions/setup-python@v2\n#      with:\n#        python-version: 3.7\n#    - name: Install Virtualenv\n#      run: pip install virtualenv\n    - name: Install GEOS\n      run: sudo apt-get install libgeos-dev\n    - name: Grant execute permission for gradlew\n      run: chmod +x gradlew\n    - name: Grant execute permission for Github Actions Workflows\n      run: chmod -R ug+x .github/workflow_scripts\n    - name: For release builds, remove -SNAPSHOT\n      env:\n        MANUAL_RELEASE_TRIGGERED: ${{ github.event.inputs.MANUAL_RELEASE_TRIGGERED }}\n      run: .github/workflow_scripts/update_project_version.sh\n\n# Quality Checks\n#    - name: ShellCheck PyAtlas\n#      run: shellcheck pyatlas/*.sh\n#    - name: Build PyAtlas\n#      run: ./gradlew cleanPyatlas buildPyatlas\n    - name: Quality checks (No tests)\n      run: ./gradlew jar check -x test -x integrationTest\n    - name: Tests\n      run: ./gradlew check build\n#    - name: Sonar\n#      env:\n#        SONAR_TOKEN: 374d4e512b90257ba50c21c37202ee01af40c6a0\n#        SONAR_PR_DECORATION_GITHUB_TOKEN: ${{ secrets.SONAR_PR_DECORATION_GITHUB_TOKEN }}\n#      run: .github/workflow_scripts/sonar.sh\n\n# Merge to Main\n    - name: Merge dev to main\n      env:\n        MERGE_TAG_GITHUB_SECRET_TOKEN: ${{ secrets.MERGE_TAG_GITHUB_SECRET_TOKEN }}\n      run: .github/workflow_scripts/merge-dev-to-main.sh\n\n# Sign and Publish\n    - name: Decrypt GPG key (To sign artifacts)\n      env:\n        GPG_KEY_LOCATION: secring.gpg\n        GPG_AES256_PASSPHRASE: ${{ secrets.GPG_AES256_PASSPHRASE }}\n        MANUAL_RELEASE_TRIGGERED: ${{ github.event.inputs.MANUAL_RELEASE_TRIGGERED }}\n      run: .github/workflow_scripts/decrypt_gpg_key.sh\n    - name: Sign and Upload Archives\n      env:\n        GPG_KEY_LOCATION: secring.gpg\n        GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}\n        GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}\n        SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}\n        SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}\n        MANUAL_RELEASE_TRIGGERED: ${{ github.event.inputs.MANUAL_RELEASE_TRIGGERED }}\n      run: .github/workflow_scripts/deploy.sh\n    - name: Tag main branch\n      env:\n        MANUAL_RELEASE_TRIGGERED: ${{ github.event.inputs.MANUAL_RELEASE_TRIGGERED }}\n        MERGE_TAG_GITHUB_SECRET_TOKEN: ${{ secrets.MERGE_TAG_GITHUB_SECRET_TOKEN }}\n      run: .github/workflow_scripts/tag-main.sh\n"
  },
  {
    "path": ".gitignore",
    "content": "**/build/\n**/bin/\n.classpath\n.project\n.settings\n.checkstyle\n.gradle\n.out\n.dist\n*.ipr\n*.iml\n*.iws\n*.ws~\n.idea/\n/tags\n/build/\n.DS_Store\n**/.DS_Store\n**/out/\n/bin/\n**/ignored/\nsrc/generated/\n**/gradle/\ngradlew*\n\n# pyatlas stuff\n*_pb2.py\n*.pyc\npyatlas/dist/\npyatlas/pyatlas.egg-info/\n__*venv__\npyatlas/protoc\npyatlas/doc/*.html\npyatlas/LICENSE\n"
  },
  {
    "path": ".travis/build-pyatlas-gate.sh",
    "content": "#!/usr/bin/env sh\n\nif [ $TRAVIS_TEST_RESULT -eq 0 ];\nthen\n\t./gradlew cleanPyatlas buildPyatlas\n\tRETURN_VALUE=$?\n\tif [ \"$RETURN_VALUE\" != \"0\" ];\n\tthen\n\t\texit $RETURN_VALUE\n\tfi\nfi\n"
  },
  {
    "path": ".travis/build.sh",
    "content": "#!/usr/bin/env sh\n\nchmod u+x gradlew\n\nif [ \"$MANUAL_RELEASE_TRIGGERED\" = \"true\" ];\nthen\n\t# This is a release job, triggered manually\n\t# Change the version locally to remove the -SNAPSHOT\n\tsed -i \"s/-SNAPSHOT//g\" gradle.properties\n\techo \"This is a manual release!\"\nelse\n\techo \"Not a manual release\"\nfi\n\nif [ \"$TRAVIS_PULL_REQUEST\" != \"false\" ];\nthen\n\t./gradlew clean build\nelse\n\t./gradlew clean build\nfi\n"
  },
  {
    "path": ".travis/deploy-gate.sh",
    "content": "#!/usr/bin/env sh\n\nif [ $TRAVIS_TEST_RESULT -eq 0 ];\nthen\n\t.travis/deploy.sh\n\tRETURN_VALUE=$?\n\tif [ \"$RETURN_VALUE\" != \"0\" ];\n\tthen\n\t\texit $RETURN_VALUE\n\tfi\nfi\n"
  },
  {
    "path": ".travis/deploy.sh",
    "content": "#!/usr/bin/env sh\n\nif [ \"$TRAVIS_BRANCH\" = \"main\" ] && [ \"$TRAVIS_PULL_REQUEST\" = \"false\" ];\nthen\n\tif [ \"$MANUAL_RELEASE_TRIGGERED\" = \"true\" ];\n\tthen\n\t\techo \"Sign, Upload archives to local repo, Upload archives to Sonatype, Close and release repository.\"\n\t\t./gradlew uploadArchives publishToNexusAndClose\n\t\t#python -m pip install --user --upgrade twine\n\t\t#twine upload ./pyatlas/dist/*\n\tfi\nfi\n"
  },
  {
    "path": ".travis/install.sh",
    "content": "#!/usr/bin/env sh\n\nif [ \"$TRAVIS_BRANCH\" = \"main\" ] && [ \"$TRAVIS_PULL_REQUEST\" = \"false\" ];\nthen\n    openssl aes-256-cbc -K $encrypted_7b323cc104d6_key -iv $encrypted_7b323cc104d6_iv -in $ENCRYPTED_GPG_KEY_LOCATION -out $GPG_KEY_LOCATION -d\nfi\n"
  },
  {
    "path": ".travis/merge-dev-to-main-gate.sh",
    "content": "#!/usr/bin/env sh\n\nif [ $TRAVIS_TEST_RESULT -eq 0 ];\nthen\n\t.travis/merge-dev-to-main.sh\n\tRETURN_VALUE=$?\n\tif [ \"$RETURN_VALUE\" != \"0\" ];\n\tthen\n\t\texit $RETURN_VALUE\n\tfi\nfi\n"
  },
  {
    "path": ".travis/merge-dev-to-main.sh",
    "content": "#!/usr/bin/env sh\n\nGITHUB_REPO=\"osmlab/atlas\"\nMERGE_BRANCH=main\nSOURCE_BRANCH=dev\n\nFUNCTION_NAME=\"merge-$SOURCE_BRANCH-to-$MERGE_BRANCH\"\n\necho \"$FUNCTION_NAME: $GITHUB_REPO\"\necho \"$FUNCTION_NAME: TRAVIS_BRANCH = $TRAVIS_BRANCH\"\necho \"$FUNCTION_NAME: TRAVIS_PULL_REQUEST = $TRAVIS_PULL_REQUEST\"\n\nif [ \"$TRAVIS_BRANCH\" != \"$SOURCE_BRANCH\" ];\nthen\n\techo \"$FUNCTION_NAME: Exiting! Branch is not $SOURCE_BRANCH: ($TRAVIS_BRANCH)\"\n    exit 0;\nfi\n\nif [ \"$TRAVIS_PULL_REQUEST\" != \"false\" ];\nthen\n\techo \"$FUNCTION_NAME: Exiting! This is a Pull Request: $TRAVIS_PULL_REQUEST\"\n    exit 0;\nfi\n\n: ${GITHUB_SECRET_TOKEN:?\"GITHUB_SECRET_TOKEN needs to be set in .travis.yml!\"}\n: ${TRAVIS_COMMIT:?\"TRAVIS_COMMIT needs to be available to merge the right commit to main!\"}\n\nTEMPORARY_REPOSITORY=$(mktemp -d)\ngit clone \"https://github.com/$GITHUB_REPO\" \"$TEMPORARY_REPOSITORY\"\ncd $TEMPORARY_REPOSITORY\n\necho \"Checking out $SOURCE_BRANCH\"\ngit checkout $SOURCE_BRANCH\ngit checkout -b tomerge $TRAVIS_COMMIT\n\necho \"Checking out $MERGE_BRANCH\"\ngit checkout $MERGE_BRANCH\n\necho \"Merging temporary branch tomerge ($TRAVIS_COMMIT) from $SOURCE_BRANCH into $MERGE_BRANCH\"\ngit merge --ff-only \"tomerge\"\n\necho \"Pushing to $GITHUB_REPO\"\n# Redirect to /dev/null to avoid secret leakage\ngit push \"https://$GITHUB_SECRET_TOKEN@github.com/$GITHUB_REPO\" $MERGE_BRANCH > /dev/null 2>&1\n"
  },
  {
    "path": ".travis/sonar-gate.sh",
    "content": "#!/usr/bin/env sh\n\nif [ $TRAVIS_TEST_RESULT -eq 0 ];\nthen\n\t.travis/sonar.sh\n\tRETURN_VALUE=$?\n\tif [ \"$RETURN_VALUE\" != \"0\" ];\n\tthen\n\t\texit $RETURN_VALUE\n\tfi\nfi\n"
  },
  {
    "path": ".travis/sonar.sh",
    "content": "#!/usr/bin/env sh\n\nif [ \"$TRAVIS_PULL_REQUEST\" != \"false\" ];\nthen\n\tSONAR_PULLREQUEST_BRANCH=\"$(echo $TRAVIS_PULL_REQUEST_SLUG | awk '{split($0,a,\"/\"); print a[1]}')/$TRAVIS_PULL_REQUEST_BRANCH\"\n\techo \"Running sonarqube in Pull Request $TRAVIS_PULL_REQUEST\"\n\techo \"sonar.pullrequest.key=$TRAVIS_PULL_REQUEST\"\n\techo \"sonar.pullrequest.branch=$SONAR_PULLREQUEST_BRANCH\"\n\techo \"sonar.pullrequest.base=$TRAVIS_BRANCH\"\n\t./gradlew jacocoTestReport sonarqube \\\n\t\t-Dsonar.organization=osmlab \\\n\t\t-Dsonar.host.url=https://sonarcloud.io \\\n\t\t-Dsonar.login=$SONAR_TOKEN \\\n\t\t-Dsonar.junit.reportPaths=build/test-results/test \\\n\t\t-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \\\n\t\t-Dsonar.pullrequest.key=$TRAVIS_PULL_REQUEST \\\n\t\t-Dsonar.pullrequest.branch=$SONAR_PULLREQUEST_BRANCH \\\n\t\t-Dsonar.pullrequest.base=$TRAVIS_BRANCH \\\n\t\t-Dsonar.pullrequest.provider=github \\\n\t\t-Dsonar.pullrequest.github.repository=osmlab/atlas \\\n\t\t-Dsonar.pullrequest.github.endpoint=https://api.github.com/ \\\n\t\t-Dsonar.pullrequest.github.token.secured=$SONAR_PR_DECORATION_GITHUB_TOKEN\nelse\n\techo \"Running sonarqube in a regular build\"\n\t./gradlew jacocoTestReport sonarqube \\\n\t\t-Dsonar.branch.name=$TRAVIS_BRANCH \\\n\t\t-Dsonar.organization=osmlab \\\n\t\t-Dsonar.host.url=https://sonarcloud.io \\\n\t\t-Dsonar.login=$SONAR_TOKEN \\\n\t\t-Dsonar.junit.reportPaths=build/test-results/test \\\n\t\t-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml\nfi\n"
  },
  {
    "path": ".travis/tag-main-gate.sh",
    "content": "#!/usr/bin/env sh\n\nif [ $TRAVIS_TEST_RESULT -eq 0 ];\nthen\n\t.travis/tag-main.sh\n\tRETURN_VALUE=$?\n\tif [ \"$RETURN_VALUE\" != \"0\" ];\n\tthen\n\t\texit $RETURN_VALUE\n\tfi\nfi\n"
  },
  {
    "path": ".travis/tag-main.sh",
    "content": "#!/usr/bin/env sh\n\nGITHUB_REPO=\"osmlab/atlas\"\nRELEASE_BRANCH=main\n\nFUNCTION_NAME=\"tag-$RELEASE_BRANCH\"\n\necho \"$FUNCTION_NAME: $GITHUB_REPO\"\necho \"$FUNCTION_NAME: TRAVIS_BRANCH = $TRAVIS_BRANCH\"\necho \"$FUNCTION_NAME: TRAVIS_PULL_REQUEST = $TRAVIS_PULL_REQUEST\"\n\nif [ \"$TRAVIS_BRANCH\" != \"$RELEASE_BRANCH\" ];\nthen\n\techo \"$FUNCTION_NAME: Exiting! Branch is not $RELEASE_BRANCH: ($TRAVIS_BRANCH)\"\n    exit 0;\nfi\n\nif [ \"$TRAVIS_PULL_REQUEST\" != \"false\" ];\nthen\n\techo \"$FUNCTION_NAME: Exiting! This is a Pull Request: $TRAVIS_PULL_REQUEST\"\n    exit 0;\nfi\n\nif [ \"$MANUAL_RELEASE_TRIGGERED\" != \"true\" ];\nthen\n\techo \"$FUNCTION_NAME: Exiting! This is not a release build.\"\n    exit 0;\nfi\n\n: ${GITHUB_SECRET_TOKEN:?\"GITHUB_SECRET_TOKEN needs to be set in .travis.yml!\"}\n: ${TRAVIS_COMMIT:?\"TRAVIS_COMMIT needs to be available to tag the right commit!\"}\n\nexport GIT_COMMITTER_EMAIL=\"travis@travis.org\"\nexport GIT_COMMITTER_NAME=\"Travis CI\"\n\nTEMPORARY_REPOSITORY=$(mktemp -d)\ngit clone \"https://github.com/$GITHUB_REPO\" \"$TEMPORARY_REPOSITORY\"\ncd $TEMPORARY_REPOSITORY\n\necho \"Checking out $RELEASE_BRANCH (commit $TRAVIS_COMMIT)\"\ngit checkout $TRAVIS_COMMIT\n\nPROJECT_VERSION=$(cat gradle.properties | grep \"\\-SNAPSHOT\" | awk -F '=' '{print $2}' | awk -F '-' '{print $1}')\n: ${PROJECT_VERSION:?\"PROJECT_VERSION could not be found.\"}\n\necho \"Tagging $RELEASE_BRANCH (commit $TRAVIS_COMMIT) at version $PROJECT_VERSION\"\ngit tag -a $PROJECT_VERSION -m \"Release $PROJECT_VERSION\"\n\necho \"Pushing tag $PROJECT_VERSION to $GITHUB_REPO\"\n# Redirect to /dev/null to avoid secret leakage\ngit push \"https://$GITHUB_SECRET_TOKEN@github.com/$GITHUB_REPO\" $PROJECT_VERSION > /dev/null 2>&1\n"
  },
  {
    "path": ".travis/trigger-release.sh",
    "content": "#!/usr/bin/env sh\n\n# Use Travis to trigger a release from Main\n\nGITHUB_ORGANIZATION=osmlab\nGITHUB_REPOSITORY_NAME=atlas\n\n# Assumptions\n# - This is called from the root of the project\n# - The travis client is installed: gem install travis\n# - travis login --org has been called to authenticate\n\nTRAVIS_PERSONAL_TOKEN=$(travis token)\n\n: ${TRAVIS_PERSONAL_TOKEN:?\"TRAVIS_PERSONAL_TOKEN needs to be set to access the Travis API to trigger the build\"}\n\nbody='\n{\n\t\"request\":\n\t{\n\t\t\"branch\": \"main\",\n\t\t\"config\":\n\t\t{\n\t\t\t\"before_script\": \"export MANUAL_RELEASE_TRIGGERED=true\"\n\t\t}\n\t}\n}'\n\ncurl -s -X POST \\\n\t-H \"Content-Type: application/json\" \\\n\t-H \"Accept: application/json\" \\\n\t-H \"Travis-API-Version: 3\" \\\n\t-H \"Authorization: token $TRAVIS_PERSONAL_TOKEN\" \\\n\t-d \"$body\" \\\n\thttps://api.travis-ci.org/repo/$GITHUB_ORGANIZATION%2F$GITHUB_REPOSITORY_NAME/requests\n"
  },
  {
    "path": ".travis.yml",
    "content": "env:\n  global:\n    - # GITHUB_SECRET_TOKEN\n    - secure: \"hYLV25stwJ4GX1BXlr7AOdeLHDCE7z395ibQAo0NDCnCqDZdoD3ySZAJteWxuBu3kpWQ1QUsCmOMn3A8m3DzqZnWQB4Hrp0aY/d5oC50LFFBbuBvuj8gqcog1t80fLAKDNNGlSxjYDPOlBVkHwez706zun0frmy3zHJ3TnfhM66CUnZ0UlKDzHwpXSplEHXJKARIX94A4O0aBsBusoHpbLYC1jqhQJWjH1D/gEcxqd2CNDRDge/xK4BZp9YiiQ1b9JSmw5D2fc1F2rXFv7FWWc9YoJt0HFGxJezVw+kmLEEJH3rk6TNxYPobsTYkwsoie6yKhwWN9xCBVmIXAzhFxjmdd02hlyAuNuK49qDKzFcFxlie3cgjxHFBdfKI/FY5jyRhcvS+IiV02qdNb+g9+Mfwz4SXFbJGUwiSM7VG/ezKxxMi9vjSisfeHSPrVNNDEbS2Nqdu/9zF1IjTsYTY6jTOMBM7hx+u0UpDEA09S280w0wZw+lym9RO3HGQvyZOEFYOQB1/IBvzDvOmWs+IrRFxgKytiZGoHMAZBL9uhYq0arqIs7GcD0ULtWRAba4X7Z1ll2kuIfUe1MruLpEGdABVLxUOwUdFLQwOl6WGL+paCSqXMghpXxP0Lfo8onjI7hLhu1bWdYQrcvnZnyQ1AwxilYjKkZc8wf9KsKeZrpk=\"\n    - # SONATYPE_USERNAME\n    - secure: \"E4anu/qe0DoKRrC6eHqYu6jYb69N5xgGnsb/yq4Fjb/6MPH3Hqe3VIny+iC/TmmG5qSeQR8FG5FM/Q9CAi0lb1kT8/UgRJdy81cfsmq1kIqhv7I1xxmwIHXLwtc14Bfjw2yhKobjCSQ4f3BhCX7H1q0GLTQRalg7PDUXLIP7Za5CWtKv7XH7Uywfz0f2pOHQ72Leqb/k93ZHGAUNHWVce++CzP+MupVQqTkR15I3DRcxH3IVj/pKGDg5u7xyDQ34lXQNSn6O19Ahbp/a2EnHjoIGSzt5pUjGtijVUqbUG++d0F9avqXhrvknADbSziUn/WaxUGypwfBXFYQ7pzZq1Wb41dngMmyszuSFuDYkLshm9aggJY+/fxSa1jKzWxi/0ArMA3dbbEHNEPjoE7Sp2bsK2yFJrx6R5F9qlNW8D7dvrMU4mx9ykJAce/6aD3l4QdZbmOYlaxdtMaf7DsRxM86LAzEiu7c+FrWqwdroR9TvWq6YYLb4Lwv5LotmhN9i68CPEq/ohiPRHhoD66WXJlEYirVd5o26z02IFWwxCvYP5yZM+3e7pXPM7WGvBwkvqM1kMmJ0NLSZAZr4gxv+AS3t4fvwrxWEVOMC9N7DVr3m9KiwD92OEP0tKY8N727/fr+ybfvPM6bgqGFKu4WDHZWSz27PWvckLhZ+PLBUoMM=\"\n    - # SONATYPE_PASSWORD\n    - secure: \"sZ3l0cznOkcMF+z7TE0SELgXNmF+RSPl+HX/kNEvVU3vDNNztbGTuaZsGZIQEYvw3FpTzGawMQB+yU+HCVlMOgllvQM7H5X/GYRrWlUNomlx8Flpc0GImz8HCUiJLtZyYO/1nFetmZmPDkMY0MgPstlduNz//+K/c+RzhGIEmNZ0FzAzdNArlgjcWMWKUARcUTf2uwb4rVvkYOhX2RcfcgZ+BJMPYR4svHV8F0U8PiHqn8DOcYAeLmlMVOZ3f3fpYFHQN9tWGoVtdMEMCPBlUcVq3/L9gp4mhj3YE7OTOAYtjfl14WktIGZ2wK2nvNHwsYqx7DeEYg/Gswap0/8xEJhf5gW5Gd6sTIVfpeLojaYacHEcLK/olqDevqKaFxxnN3hUlS6M7k8D9n6j0ZxMYKFDMja8/OduOccs69wz7/tN2RPiqWH10wh+wKDJx/vjvyq5ozmKfh3ebZn87l9hWtDGi53ZRpfqv3GFce/29kOyj0YwTYuIPG10/w6wsSoRqVl4t/WXgAPWMmodLbo9PqvAjVJuFxbfWv60UICg8zmPadc0+t4GkfE7VXI+46jU9ZGoWAWuf4CzmGhsuDkbBh1rhg1R6Xdg8nupWGuDE1rEfEHr6yhq1hcXxxnEXoZZChbRqabjlUDnXtBpCjjimjq4a+LEgWyufCG95EaWthc=\"\n    - # GPG_KEY_ID\n    - secure: \"ow4WEQXjjCi8hBfLNnhtuOorOn0k//K3QBGALcwynDpJIOxwvrV2jJPcNjoQ4aNmeE5a+zl/JomtUBz0mASe9j/JTGhhqrYC1Tpgoo3naEvFdOaucaTG2RV3u2jv0UdQm+wU4APf9t2HbzrZaa+SDMJwF/qpgH2gUE6v0wEQQyZJvuk9EKDsZL+dZkM5UlSGFJ1bUzgvHPEVXrj/gsh0NOQ7BUdjwfIpxKytUXLmQWs8NkP5ptygeifpaJ+D7IQ5HTJCthZ/MuInAIkD0bDPc0nbjhZSriZzCOUlhLDxb7F/iHUomQhm/EvcvMOmCtyDuaEZZxOEK2+lpOcoaMvRj/cXqY2tLQJgjgC95Ka3GvB7m7UY1QCoRWePUrHAbcZYCugD4vx5y8GJqxdBgtOMbI5blzSsaY7D/knXv2lAp8JBPylrNQJQPty1gl7Hp6CFwa1WkKh7C3mBl2a8oyYXlKIqd+NSGQGbWoy+ULv77tH15L2KwTdxYllQY2P1xj+k6CsEmRfTK8lW7c7AhbmHsW5gqGjK3rFThTPurwaJ92rsc4AusiW3uor3/z13zuHH+kgH58dc6h21lmouchvw4177xxj5aPjA09puRikGhEr2SyVwu2h1HU5+l/YwVOEMJZ6oDfYwug4ktmPjFRvF+PEo/Dw6kCBcCerVh7A0EL8=\"\n    - # GPG_PASSPHRASE\n    - secure: \"gGAS4pDztf35LwEAj7tuA5rYuh9F3lFqUJ/jW+AkIeS4yaQVB5xvj3Wx6pqFpQyGvr0XYmUYDKTfPxHyXI6Rsr2JEtYHRcYhJXWYmTmfX/rzflN7yvmbkq1Io9UA7q28ZqCirrEoigT+sI6jA5hOdvmYX+kUAju5U1moMDRRn9GGOnoE1rB74mnpo4Pl1OvMk0XTZSMMAnTG2FmpQMBVJkJFz81e+xUGcUa0X25U3nLVvnVf2gwbC0Mhi87ITlH4twzsEBr5nJJNrlidDca6OCWv96pKB1VI1/dgVOBUmQudMKxeAYgwxPepyC1jpMyABC+JWQAvUFu275c/hNPTs5pUdvw6FSEgELjkPwoLjamSLCO8NIfymIXurm7c+3z8FZeA+ny3XWvC7loREh8XpzUoAxzbZP3h9GqDhLljtIdquZp2sEVMRfMCGoe5Gb1YPRlDYPQxgTk2SlZMwjPQitv9wF3QFWSbL3p/f/0qYkOSYYb0XshMu9Q+66kkpLflY9XIXEqAylODXTAjfIOIXQxO+rwBapEDNAhtFK2gSdjztwZDm3cF+V4HNonLstvSHGKUl5ki2f7+/SdzizwLY6RQ1ufwuhRt0hkeUHK+AYOaq2J0yduEJKzMr4PJUPb2r/F6ecplE0vPOXu93E63+E57NbeoB6ESV7YTx1sCnEY=\"\n    - # TWINE_USERNAME\n    - secure: \"IP7AYNIUI5BtUF5I2/8I+JaAERe9UKUEKrxB8BTvndpC3wF+FlZyzbESl2DUgGV/8JiIAbpHnuAlR0Tm/R0NdFMtqy+QwBAuVkGCI3UPrZ4uWiQGWqnZ/wqH+uX0zZAXNvikvfz2lJfJyHpggIQWegn1MN75ypqvr1MgzpOKHF1RPcFL+MkW8GmfO6S1YMm0+FVffv5DRagZ8ouzS0HwW8t1UfRhVFCanUafgdDt8s3yvhKCMnjq2Q5of+yr6Vcr3wH3IUWF+6rMI8fLo9yr3bG3gP2GnVfxq7JtvgZH7GpZ+5PH9D9Cg7HoWb2Ggt2edFyCRCTYKgqivYUruzd8V9k/a6+JmWByi5QqZSCSQ/ZZOxF7yHz+5vKOwYHJjBg+4qq+CIN+o3cKorqLQ3UFPA8Oaat8WPdB5inAyzfVha5RXP5pucwYTFtnZAojTImYEXKGCK9x7oIjJFoLlVkZO8Wa7GuguG0IloM6tACL4uWognEUMHXADKmjWohcgcwa71PA3I2ZIbdIkoVjjD9RofWfImG/vVu/im3z2BDiuYYbg0kCXn23PD9aPTlJWsJ2XPLARjQlBWrSkvbaX233VLzKKQH3wHq6O1+qBYEFqILT1kBUJfEBseJfMBCwwJajGGDxswZ63n2sKC3UI+WBRZynNgkxhDLqwZKjR9VpxAk=\"\n    - # TWINE_PASSWORD\n    - secure: \"Nymg9duf3qwC6BAZqGTtoxmYWa5qy/FbTk8Rqy3GAa/YxtfyOHqClwCzY454cO5pIs5K1Z72Y8C/EpzsK3OHVDRQDNYpkJbhvYrHh7lCfl5EsuGQxp8eieXdAlwaDcDCjHIwcZXMaxZqQTKfMoJHzNQf3ktzjBXnUS6d7MygPIVcfCCIpFM40K/qVGvVY+nF2nakd/uveEifEENnts43PfMUr7aXo4WtrCNTOq9OFATnAOMNn/dt0Qx8Rx5HBvfLeXDBUUCHY2Bnc33WTCQWu2uP6KceGtKotWnFhuVxMNbxBPzRmGmiBYzoFydlnYB/t7UgLUJK1r9y+F0SB7mlK1kU6jNW1VYm/1gkNujzxQhKr8Z5CsB+K3ytxFG1W5OMGCfiZYVtz6C2scbEW6HJaogS/7yTPd4/tahxE/7Pf9Aa6RppcZsrz88kHKThkPxzAbgslMLieCIJFI+kkIrr02om3kSSSJq9XfW33YC5FdUpZbm/1DqkFxvm5sawwu8u1I951OgsYbsJWRuquWedeDnxen7fijeDizYsL9J7PcpgsPXr8gNOFbyTD8dCP2fpLi35iRqvjJOqspQ5wiLprUB5Hoj/La/XloCqws+8cjGw4IP/6dmRrimyuEs6g5RsIMAWUhpdhZm95sfe09sSKtXmMj/g/eYrTgxZbED7Hd8=\"\n    - # SONAR_TOKEN\n    - secure: \"F7d2WgJMrUe6GMGZaob3wBxXC4mSRUq23M18L7bGzrjFhF2eIGqwGBIfpzVuNO1lq3J6AVHwNsal1RiEg+beIsvXC1fOPJwa/FY1QcXWyi7pvdDAPsaRDzBpdZU4sKLbs6bnotiHAx8jMD6T1JFi87Qf2wit2UG/HSjSCOVF8AvONwFaU2Hv4nisvE6gdOcXEo5y3kUruJ3DKbnHyGKtuYacYkmsn90wroNJCSiGtJ3lACyxy5oqq5QX9JU971et3wQtfy1wwTovCsZcSBEg+XnHGlxEDacVb0/m5pbvLep8LKOaDrsxGrMYerJiO2NBVA5b+xI+esVBk8LhhL9BtMXWKMBQ0I/+GhfdThrws97nJhC7DdEh1xhW/bcVg9mkuTXekNgucXCor43xKohUGEwa7efe2upiNpRZMelMgTJzb/dyC/BhNkU3YGZaNuPWWPp4j9aORJ/ul4aMOtPcFW/YX/ZLSszd6Y57B9yGfGlCyYKeFIAabCOi1RXp03eqssAfb+WLNW3AAnRji9NGXIx0LZSAL+Q5V2KJE+t4+45VHJbdUD1Rvu31bEtCg8Qy6X8ySZRxWLmPHiEVHzi7ZAOQ46JaVLRrZfaCYT6TpT6cQ7hUkeWBOEdnEQbpuJaI2SfwQIo/1wfqfJSP2EAr+iQEAYBd/kfooECQK9pozrs=\"\n    - # SONAR_PR_DECORATION_GITHUB_TOKEN\n    - secure: \"tf1efkXd1zFeIb0CZimlpO39ECIl7yZz5fbO0+Fmx9NTptyguOQOZwEecgkoaIEyCSM25lXeUiGsPbheHWRF5ifs7+2axqM2BeMPX9Yd1frYaQ5yNBcqTmrMKYGkXsovMSZU3qDiQcDTzFotHydsO+KPGIs7LhPYLTw7Y4dDEMUqNAvzBLHUf0reMm5h+0CBIxOfMgSjW73wxBp1baqVvqpj5qM9R07e3EdpnMPszX5HZTvOAWqjLrh0re20wU0aNrMld5xfkP7qkSMO1zC68fzRWnFcwGCIt2FP9Vy7AgV8eq1dIqk+EdnilfY4Gq7KhwML+k0b3qmYWDS4NCc5j4EXrAWZLffoY4dYZp4hnv0JLCI+lq1fSn8Je0+AadmW9NjOKZyWg4iNTaHcBVA52OlWxw7KE4cTccuOwpbQXeatR8ayrYALV0UEc+F14bLZAkswbCA10XV4ghe1gLeCCjtqde1D/oltumR6lcXxsRsOGzAdf9DO0fuIJyvhFhKbSp51MFn0Fo5/SQMuZTO6P/YBnVIuzEZDo8xkhMczUxqB+kc2tlFns+BWt0J1H39G9WEGtgn28bUIDZA5CaY2WAwuFRSy95GFSYXkyNi6cm8Iu9xJ/PjZcLCyeQpviLE8am3oc9Mhjx6RMnTh1nc/B8TtuWTVU+PwuVnJzqo+F44=\"\n    - GPG_KEY_LOCATION=\".travis/secring.gpg\"\n    - ENCRYPTED_GPG_KEY_LOCATION=\".travis/secring.gpg.enc\"\nbranches:\n  only:\n  - main\n  - dev\nlanguage: java\ndist: xenial\njdk:\n  - openjdk11\nbefore_install:\n  - chmod -R ug+x .travis\n  - .travis/install.sh\nscript:\n  - chmod -R ug+x .travis\n  - set -e\n  - shellcheck pyatlas/*.sh\n  - set +e\n  - .travis/build.sh\n  - .travis/build-pyatlas-gate.sh\n  # - .travis/sonar-gate.sh\n  # - .travis/merge-dev-to-main-gate.sh\n  - .travis/deploy-gate.sh\n  - .travis/tag-main-gate.sh\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Atlas\n\nThanks for taking the time to contribute!\n\n## Where to ask a question\n\nThis project uses [StackOverflow](https://stackoverflow.com/) to handle all software related questions. If unsure about an issue, please first ask a question on StackOverflow, and then file an issue if necessary. Do not ask questions in GitHub issues as those will be closed and redirected to StackOverflow.\n\n## Suggestions and bug reports\n\n### Reporting bugs\n\nIf you have found a bug to report, that is great! Please search for similar bugs in GitHub issues, as your bug might have been filed already. If not, filing a GitHub issue is the next thing to do!\n\n### Filing a Github issue\n\nWhen submitting an issue, here is the information to include:\n\n* Title: Use a title that is as self explanatory as possible\n* Summary: Detailed description of the issue, including screenshots and/or stack traces\n* Steps to reproduce: Do not forget to include links to data samples that can help in reproducing the issue\n* Actual vs. Expected: Describe the results and how they differ from the expected behavior\n* Workaround: If you have found a temporary workaround the issue, please also include it there!\n\n### Suggesting enhancements\n\nEnhancements are also handled with GitHub issues. Make sure to include the following:\n\n* Title: Use a title that is as self explanatory as possible\n* Summary: Use-case description of the proposed enhancement\n* Desired: Describe the desired behavior of the proposed enhancement\n* Implementation proposal: If you have an idea of how to implement the enhancement\n\n## Submitting code\n\n### Requirements\n\n* OpenJDK 11\n* Gradle\n\n### First contribution\n\nThe first step would be to fork the project in your own github account, then clone the project locally using `git clone`. Then use gradle to build the code, and run tests:\n\n```\ncd <my clone location>\n./gradlew build\n```\n\nAtlas is serialized using protobuf. The proto-generated sources are not checked-in to the repository, so any IDE setup requires to run `./gradlew build` to generate the proto-generated sources in the project. At that point, the IDE of your choice will be able to resolve all the necessary classes.\n\nTo start contributing to your fork, the best way is to make sure that your IDE is setup to follow the [code formatting template](config/format/code_format.xml).\n\nOnce you have fixed an issue or added a new feature, it is time to submit [a pull request](#pull-request-guidelines)!\n\n### Building\n\nYou can build a shaded JAR that will allow you to execute atlas. \n\n#### Log4j Properties\n\nMake sure you first have a `log4j.properties` file in `src/main/resources`. \nAlternatively, you can have as a VM parameter:\n\n``` \n-Dlog4j.configuration=file://<path_to_config>\n```\n\nhttps://github.com/osmlab/atlas-checks/blob/dev/config/log4j/log4j.properties\n\nThen, you can build your shaded JAR with:\n\n``` \n./gradlew shaded\n```\n\nFrom there, you can run command line tools in atlas, like the following:\n\n``` \njava -cp atlas-5.1.8-SNAPSHOT-shaded.jar org.openstreetmap.atlas.geography.atlas.delta.AtlasDeltaGenerator <args...>\n```\n\n#### IntelliJ Setup\n\nIntellJ IDEA works pretty much out of the box. However, you still need to mess with log4j. First, add a \n`log4j.properties` file to your project or your VM Options.\n\nAlso, you need to manually add [slf4j-simple.jar](https://mvnrepository.com/artifact/org.slf4j/slf4j-simple/1.7.25) \nto your Project Module Dependencies:\n\n    File > Project Structure > Modules > Dependencies\n\nIn `atlas_main` add the the JAR that you downloaded.\n\n![slf4j in Intellij](images/slf4j.png)\n\n### Code formatting\n\nThe project's code is checked by Checkstyle as part of the `gradle check` step.\n\nThere also is an eclipse code formatting template [here](config/format/code_format.xml) that is used by [Spotless](https://github.com/diffplug/spotless) to check that the formatting rules are being followed. In case those are not, the `gradle spotlessCheck` step will fail and the build will not pass. Spotless provides an easy fix though, with `gradle spotlessApply` which will refactor your code so it follows the formatting guidelines.\n\n### Testing\n\nThe codebase contains an extensive range of unit tests, and integration tests. Unit tests are supposed to run fairly fast. If the test takes a long time to run, we put it in the integrationTest repository, to allow users to run them only when wanted. All the tests will be run for every pull request build, though! When contributing new code, make sure to not break existing tests (or modify them and explain why the modification is needed) and to add new tests for new features.\n\n### Pull Request Guidelines\n\nPull requests comments should follow the template below:\n\n* An as extensive as reasonable description of the change proposed, in easy to read [Markdown](https://guides.github.com/features/mastering-markdown/), with as many code snippet examples, screen captures links and diagrams as possible\n* A Benefit/Drawback analysis: what does this improve, and at what cost? Is the performance impacted or improved?\n* Label: If applicable, apply one of the available labels to the pull request\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2015-2024, Apple Inc. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, \nare permitted provided that the following conditions are met:  \n\n    1. Redistributions of source code must retain the above copyright notice, \n       this list of conditions and the following disclaimer.\n\n    2. Redistributions in binary form must reproduce the above copyright notice, \n       this list of conditions and the following disclaimer in the documentation \n       and/or other materials provided with the distribution.\n\n    3. Neither the name of the copyright holder(s) nor the names of any \n       contributors may be used to endorse or promote products derived from this \n       software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND \nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. \nIN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, \nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, \nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, \nOR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \nPOSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# Atlas\n\n![Continuous Integration](https://github.com/osmlab/atlas/workflows/Continuous%20Integration/badge.svg?branch=main)\n[![quality gate](https://sonarcloud.io/api/project_badges/measure?project=org.openstreetmap.atlas%3Aatlas&metric=alert_status)](https://sonarcloud.io/dashboard?id=org.openstreetmap.atlas%3Aatlas)\n[![Maven Central](https://img.shields.io/maven-central/v/org.openstreetmap.atlas/atlas.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22org.openstreetmap.atlas%22%20AND%20a:%22atlas%22)\n[![CircleCI](https://circleci.com/gh/osmlab/atlas/tree/main.svg?style=shield)](https://circleci.com/gh/osmlab/atlas/tree/main)\n\n---\n\n[`Atlas`](src/main/java/org/openstreetmap/atlas/geography/atlas/Atlas.java) is a way to efficiently represent [OpenStreetMap](http://www.openstreetmap.org/) data in memory. A subset of the data is in a \"navigable network\" form, meaning anything that is assumed to be navigable will be in a form of `Node`s and `Edge`s in a way a routing algorithm could traverse it. It also provides easy to use APIs to access geographical data. On top of it all, it is easy to shard and re-stitch, making it perfect for distributed processing!\n\nProjects using Atlas:\n* [atlas-generator](https://github.com/osmlab/atlas-generator): A Spark job to distribute Atlas shards generation\n* [atlas-checks](https://github.com/osmlab/atlas-checks): A suite of tools to check OSM data integrity using Atlas, and Spark.\n* [josm-atlas](https://github.com/osmlab/josm-atlas): A JOSM plugin to visualize Atlas data.\n\n# Getting started\n\nFor build instructions and to contribute, please see the [contributing guidelines](CONTRIBUTING.md).\n\nStart playing with Atlas directly with [this sample project](/sample)!\n\n# APIs\n\nLanguage|Level\n---|---\n[Java](/src/main/java/org/openstreetmap/atlas/geography/atlas#using-atlas)|Full feature\n[Python](/pyatlas#pyatlas)|Basic\n\n# What's in it?\n\n* A uni-directional navigable network ([`Edge`](src/main/java/org/openstreetmap/atlas/geography/atlas/items/Edge.java), [`Node`](src/main/java/org/openstreetmap/atlas/geography/atlas/items/Node.java))\n* Non-navigable features ([`Area`](src/main/java/org/openstreetmap/atlas/geography/atlas/items/Area.java), [`Line`](src/main/java/org/openstreetmap/atlas/geography/atlas/items/Line.java), [`Point`](src/main/java/org/openstreetmap/atlas/geography/atlas/items/Point.java), [`Relation`](src/main/java/org/openstreetmap/atlas/geography/atlas/items/Relation.java))\n* All tags\n\nAs well as other handy tools:\n\n* [Create it from `.osm.pbf`](/src/main/java/org/openstreetmap/atlas/geography/atlas#building-an-atlas-from-an-osmpbf-file)\n* [Sharding](/src/main/java/org/openstreetmap/atlas/geography/sharding#sharding)\n* [Shard Stitching](/src/main/java/org/openstreetmap/atlas/geography/atlas/multi#multiatlas)\n* [Shard Exploration](/src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic#dynamicatlas)\n* [Tag Filtering](/src/main/java/org/openstreetmap/atlas/tags/filters#tag-filtering)\n* [Atlas Filtering](/src/main/java/org/openstreetmap/atlas/geography/atlas#filtering-an-atlas)\n* [PBF Ingest](/src/main/java/org/openstreetmap/atlas/geography/atlas/raw/creation)\n* [Country Slicing](/src/main/java/org/openstreetmap/atlas/geography/atlas/raw/slicing)\n* [Way Sectioning](/src/main/java/org/openstreetmap/atlas/geography/atlas/raw/sectioning)\n* [Cutting](/src/main/java/org/openstreetmap/atlas/geography/atlas#country-slicing)\n* [Routing](/src/main/java/org/openstreetmap/atlas/geography/atlas/routing#routing)\n* [Higher-level entities](/src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex#complex-entities)\n* [Saving](/src/main/java/org/openstreetmap/atlas/geography/atlas#saving-an-atlas) / [Loading](/src/main/java/org/openstreetmap/atlas/geography/atlas#using-atlas)\n* [GeoJSON Parser](/src/main/java/org/openstreetmap/atlas/geography/geojson/parser)\n* [Command Line Tools](atlas-shell-tools)\n* [Atlas Query Language i.e. AQL](/src/main/groovy/org/openstreetmap/atlas/geography/atlas/dsl)\n\n# Community\n\nFor more information, please contact our community projects lead [Andrew Wiseman](https://github.com/awisemanapple).\n"
  },
  {
    "path": "atlas-shell-tools/.atlas-shell-tools-integrity-file",
    "content": "# This file is used by Atlas Shell Tools to ensure that ATLAS_SHELL_TOOLS_HOME\n# is set correctly.\n\n"
  },
  {
    "path": "atlas-shell-tools/README.md",
    "content": "# Atlas Shell Tools\nA command line interface for [`osmlab/atlas`](https://github.com/osmlab/atlas) (and downstream repositories) tools.\n\n##### Table of Contents\n1. [What are the Atlas Shell Tools?](#what-are-the-atlas-shell-tools)\n2. [Installation](#installation)\n   * [Auto-install for `bash` users](#auto-install-for-bash-users)\n   * [Auto-install for `zsh` users](#auto-install-for-zsh-users)\n   * [Manual install](#manual-install)\n3. [Managing Your Installation](#managing-your-installation)\n   * [Installing A New Module From A Repo](#installing-a-new-module-from-a-repo)\n   * [Switching The Active Module](#switching-the-active-module)\n   * [Viewing Command Documentation](#viewing-command-documentation)\n   * [Saving Command Option Presets](#saving-command-option-presets)\n   * [And Much More](#and-much-more)\n4. [Creating Your Own Command](#creating-your-own-command)\n5. [Updating The Commands And Tools](#updating-the-commands-and-tools)\n\n## What are the Atlas Shell Tools?\nAtlas Shell Tools is a command line interface for executing commands defined in [`osmlab/atlas`](https://github.com/osmlab/atlas)\nand its downstream repositories (like [`osmlab/atlas-generator`](https://github.com/osmlab/atlas-generator)). It provides Unix-like option parsing,\nautocomplete functionality, a feature-ful option preset system (for commands that need lots\nof options), module/repository management, and much more.\n\nTo get a basic installation running, see the [**Installation**](#install) section.\n\nYou can manage your installation with the `atlas-config(1)` command. Management features include:\n1. Installing modules (JARs containing command classes) from various repositories\n2. Switching the active module\n3. Viewing command documentation\n4. Saving command option presets\n5. And much more...\n\nSee the [**Managing Your Installation**](#managing) section for more information.\n\nTo build a command, all you need to do is subclass `AbstractAtlasShellToolsCommand`(or one of its further subclasses) - \nthen your command will be automatically integrated into the tools! For more information on this,\nsee the [**Creating Your Own Command**](#creating) section.\n\n## Installation\nAtlas Shell Tools comes with some quick install scripts for users of select shells.\nNote that the quick install scripts prompt to modify your shell's startup file(s)\nwith some code Atlas Shell Tools needs to run (`.bash_profile` and `~/.bashrc`\nfor `~/bash`, `~/.zshrc` and `~/.zshenv` for `zsh`). If you do not want this behaviour\nand just want to configure your startup files yourself, select 'n' at the appropriate prompts.\n\n#### Auto-install for `bash` users:\n```\n$ curl -O https://raw.githubusercontent.com/osmlab/atlas/main/atlas-shell-tools/quick_install_bash.sh\n# Inspect the downloaded file and ensure you are satisfied it is safe to run:\n$ vim quick_install_bash.sh\n$ sh quick_install_bash.sh\n# Answer the prompts, and restart your terminal once this finishes to get started!\n```\n\n#### Auto-install for `zsh` users:\n```\n$ curl -O https://raw.githubusercontent.com/osmlab/atlas/main/atlas-shell-tools/quick_install_zsh.sh\n# Inspect the downloaded file and ensure you are satisfied it is safe to run:\n$ vim quick_install_zsh.sh\n$ sh quick_install_zsh.sh\n# Answer the prompts, and restart your terminal once this finishes to get started!\n```\n\n#### Manual install:\nIf you are not running one of the supported shells, or you want to manually\ninstall Atlas Shell Tools, please follow these steps:\n##### Building and Installing\n```\n$ cd /path/to/desired/install/location\n$ git clone https://github.com/osmlab/atlas.git atlas-shell-tools\n$ cd atlas-shell-tools\n$ git checkout main\n$ ./gradlew clean shaded -x check -x javadoc\n$ chmod +x ./atlas-shell-tools/scripts/atlas ./atlas-shell-tools/scripts/atlas-config\n$ ./atlas-shell-tools/scripts/atlas-config repo add atlas https://github.com/osmlab/atlas.git\n$ ./atlas-shell-tools/scripts/atlas-config repo install atlas\n```\n##### Setting up your shell startup file\nNext, open whichever configuration file your shell uses to set environment variables\n(e.g. `~/.bash_profile` for `bash`, or `~/.zshenv` for `zsh`). Export the following\nenvironment variables using your shell's equivalent `export` statement.\n```\n# In bash/zsh we can use 'export', other shells may use different syntax\n\n# Point ATLAS_SHELL_TOOLS_HOME at the 'atlas-shell-tools' subfolder within your 'atlas-shell-tools' installation\nexport ATLAS_SHELL_TOOLS_HOME=/path/to/atlas-shell-tools/atlas-shell-tools\n\n# configure your PATH\nexport PATH=\"$PATH:$ATLAS_SHELL_TOOLS_HOME/scripts\"\n```\n##### Autocomplete support\nAdditionally, Atlas Shell Tools supports autocomplete for `bash` and `zsh`\nthrough [ast_completions.bash](https://github.com/osmlab/atlas/blob/main/atlas-shell-tools/ast_completions.bash)\nand [ast_completions.zsh](https://github.com/osmlab/atlas/blob/main/atlas-shell-tools/ast_completions.zsh),\nrespectively. To get these set up, you'll need to source them in your shell's appropriate\nstartup file (`~/.bashrc` for `bash` or `~/.zshrc` for `zsh`).\n\nAn example for `bash`:\n```\n##### ~/.bashrc file #####\n#\n# other stuff here....\n#\nsource \"$ATLAS_SHELL_TOOLS_HOME/ast_completions.bash\"\n```\n\nAn example for `zsh`:\n```\n##### ~/.zshrc file #####\n#\n# other stuff here....\n#\nsource \"$ATLAS_SHELL_TOOLS_HOME/ast_completions.zsh\"\n```\n\n## Managing Your Installation\nBoth `atlas(1)` and `atlas-config(1)` provide numerous ways to manage your installation. Some common\noperations include:\n\n#### Installing A New Module From A Repo\nSuppose the git repository `me/my-repo` depends on [`osmlab/atlas`](https://github.com/osmlab/atlas) and contains a command implementation you would\nlike to run from the CLI. First, you can save the `my-repo` information with the `repo` subcommand of `atlas-config(1)`:\n```\n$ atlas-config repo add my-repo 'https://github.com/me/my-repo'\n```\nThen, to install a new module based on `my-repo`'s `main` branch, simply run:\n```\n$ atlas-config repo install my-repo\n```\nSee the `atlas-config-repo(1)` man page for more information about repos.\n\n#### Switching The Active Module\nAfter running the command from `me/my-repo`, you may want to switch back to another repo, like `me/my-other-repo`.\nAssuming you have a module from `me/my-other-repo` installed, this is straightforward. First, you can list your installed\nmodules with:\n```\n$ atlas-config list\n```\nThen, assuming you see the module you want - for sake of example say it's called `my-other-repo-ef3381a` - run:\n```\n$ atlas-config activate my-other-repo-ef3381a\n```\nYou can check to see that it worked with `$ atlas-config list` again.\n\n#### Viewing Command Documentation\nAtlas Shell Tools comes with extensive documentation, both in the form of man pages as well as subcommand documentation.\nTo see all available man pages, check the Atlas Shell Tools index man page: `atlas-shell-tools(7)`:\n```\n$ man atlas-shell-tools\n```\nFrom there you can jump to whichever page interests you. Subcommand implementations also provide their own documentation, part\nof which is auto-generated and part of which can be optionally supplied by the command author. To view this documentation, try the\nsubcommand's `--help` option (every subcommand automatically responds to `--help`):\n```\n$ atlas some-command --help\n```\nOR\n```\n$ atlas --help some-command\n```\n\n#### Saving Command Option Presets\nSome commands require many options, and option presets can make repeated use much easier. Assuming your command is called `my-command`,\nyou might save an option preset like:\n```\n$ atlas --save-preset my-command-preset-1 my-command arg1 --opt1=optarg1 --opt2\n```\nYou could then run\n```\n$ atlas --preset my-command-preset-1 my-command arg1\n```\nand Atlas Shell Tools would fill in `--opt1=optarg1 --opt2` for you automatically. There is much more to be said about the presets feature.\nPlease see the `atlas-presets(7)`, `atlas-config-preset(1)`, and `atlas(1)` man pages for all the details.\n\n#### And Much More...\nThere are many more Atlas Shell Tools features just waiting to be found. Feel free to peruse all the man pages\navailable in `atlas-shell-tools(7)`.\n\n## Creating Your Own Command\nTo create a new command for Atlas Shell Tools, simply create a class that `extends`\n[AbstractAtlasShellToolsCommand](https://github.com/osmlab/atlas/blob/main/src/main/java/org/openstreetmap/atlas/utilities/command/abstractcommand/AbstractAtlasShellToolsCommand.java).\nOnce you fill in the abstract methods appropriately (and add a main method), you should build a\nfat JAR file containing your command, and install it with:\n```\n$ atlas-config install /path/to/JARfile.jar --symlink\n```\nThis will install the JAR file to the module workspace using a symlink, so iterative\nchanges to the JAR will be automatically picked up by Atlas Shell Tools.\n\nFor a comprehensive example of the `AbstractAtlasShellToolsCommand` API, check out the\ndemo class [DemoSubcommand](https://github.com/osmlab/atlas/blob/main/src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShellToolsDemoCommand.java).\nThis class demonstrates how to implement the abstract methods, as well as how to structure\nthe main method.\n\n## Updating The Commands And Tools\nIf you just want to quickly update everything, go ahead and run:\n```\n$ atlas-config update\n```\nThis will update the toolkit, which includes the code for `atlas(1)`, `atlas-config(1)`, and the various man pages\n(see the `atlas-glossary(7)` man page for a definition of the toolkit).\n\nThen, to update the basic commands by installing a new module from the default [`osmlab/atlas`](https://github.com/osmlab/atlas)\nrepo, run:\n```\n$ atlas-config repo install atlas\n```\nIf you have any other repos you'd like to update, you can run a `repo install` on those as well to get\nthe latest versions of any commands.\n"
  },
  {
    "path": "atlas-shell-tools/ast_completions.bash",
    "content": "\n# What is this script for?\n#\n# This script provides autocomplete functionality for Atlas Shell Tools in the\n# bash shell.\n#\n# How do I use it?\n#\n# Run the following command:\n#    $ source \"$ATLAS_SHELL_TOOLS_HOME/ast_completions.bash\"\n# Then add 'source \"$ATLAS_SHELL_TOOLS_HOME/ast_completions.bash\"' to your '~/.bashrc'\n# file to pick up the completions in every new shell!\n\n\n# Return \"true\" if bash major version is greater than or equal to 4.\n# Bash4 has a better auto-complete API, so using bash4 if possible is better.\nis_bash_at_least_version_4 ()\n{\n    local version=$BASH_VERSION\n    local major=$(echo \"$version\" | cut -d. -f1)\n    if [ \"$major\" -ge \"4\" ];\n    then\n        echo \"true\"\n    else\n        echo \"false\"\n    fi\n}\n\n_complete_atlas_shell_tools ()\n{\n    local completion_mode=\"default\";\n    if [ \"$1\" = \"atlas\" ];\n    then\n        local completion_mode=\"__completion_atlas__\"\n    elif [ \"$1\" = \"atlas-config\" ];\n    then\n        local completion_mode=\"__completion_atlascfg__\"\n    fi\n\n    if [ \"$completion_mode\" = \"default\" ];\n    then\n        echo \"complete error: ${completion_mode} was still default\"\n        return 1\n    fi\n\n    # disable readline default autocompletion, we are going to customize\n    if [ \"$(is_bash_at_least_version_4)\" = \"true\" ];\n    then\n        compopt +o default\n    fi\n\n    local reply=$(atlas-config \"${completion_mode}\" \"${COMP_CWORD}\" \"${COMP_WORDS[@]}\");\n\n    if [ \"$reply\" = \"__atlas-shell-tools_sentinel_complete_filenames__\" ];\n    then\n        # re-enable bash default completion for filenames\n        if [ \"$(is_bash_at_least_version_4)\" = \"true\" ];\n        then\n            compopt -o default\n            COMPREPLY=()\n        else\n            local cur=${COMP_WORDS[COMP_CWORD]}\n\n            # We must locally set IFS to '\\n' in case there are filenames with whitespace.\n            # Without this, a filename like \"file with spaces\" would present itself\n            # as 3 discrete completion options, \"file\", \"with\", and \"spaces\".\n            local IFS=$'\\n'\n\n            COMPREPLY=($(compgen -o filenames -f -- \"$cur\"))\n        fi\n        return 0\n    else\n        COMPREPLY=(${reply})\n    fi\n}\n\ncomplete -o filenames -o bashdefault -F _complete_atlas_shell_tools atlas\ncomplete -o filenames -o bashdefault -F _complete_atlas_shell_tools atlas-config\n"
  },
  {
    "path": "atlas-shell-tools/ast_completions.zsh",
    "content": "\n# What is this script for?\n#\n# This script provides autocomplete functionality for Atlas Shell Tools in the\n# zsh shell.\n#\n# How do I use it?\n#\n# Run the following command:\n#    $ source \"$ATLAS_SHELL_TOOLS_HOME/ast_completions.zsh\"\n# Then add 'source \"$ATLAS_SHELL_TOOLS_HOME/ast_completions.zsh\"' to your '~/.zshrc'\n# file to pick up the completions in every new shell!\n\n_complete_atlas_shell_tools_zsh ()\n{\n    local completion_mode=\"default\";\n    if [ \"$1\" = \"atlas\" ];\n    then\n        local completion_mode=\"__completion_atlas_zsh__\"\n    elif [ \"$1\" = \"atlas-config\" ];\n    then\n        local completion_mode=\"__completion_atlascfg_zsh__\"\n    fi\n\n    if [ \"$completion_mode\" = \"default\" ];\n    then\n        echo \"complete error: ${completion_mode} was still default\"\n        return 1\n    fi\n\n    local reply=$(atlas-config \"${completion_mode}\" \"${COMP_CWORD}\" \"${COMP_WORDS[@]}\");\n\n    if [ \"$reply\" = \"__atlas-shell-tools_sentinel_complete_filenames__\" ];\n    then\n        local cur=${COMP_WORDS[COMP_CWORD]}\n\n        # We must locally set IFS to '\\n' in case there are filenames with whitespace.\n        # Without this, a filename like \"file with spaces\" would present itself\n        # as 3 discrete completion options, \"file\", \"with\", and \"spaces\".\n        local IFS=$'\\n'\n\n        COMPREPLY=($(compgen -o filenames -f -- \"$cur\"))\n        return 0\n    else\n        COMPREPLY=(${reply})\n    fi\n}\n\nautoload compinit\ncompinit\nautoload bashcompinit\nbashcompinit\ncomplete -o filenames -o bashdefault -F _complete_atlas_shell_tools_zsh atlas\ncomplete -o filenames -o bashdefault -F _complete_atlas_shell_tools_zsh atlas-config\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-activate.1",
    "content": ".\\\"     Title: atlas-config-activate\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-ACTIVATE\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-activate \\-\\- Activate an installed Atlas Shell Tools module\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas-config activate\\fR \\-\\-help\n\\fIatlas-config activate\\fR <\\fImodule\\fR>\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nActivate the provided <\\fImodule\\fR>. Commands defined in an activated module\nbecome available for execution by the \\fBatlas\\fR tool. Activating a\nmodule deactivates any other active modules.\n\n.SH \"OPTIONS\"\n.sp\n\n.PP\n<\\fImodule\\fR>\n.RS 4\nThis indicates a module. The module should be referred to using its name, as\nreported by \\fBatlas-config-list\\fR(1).\n.RE\n\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1), \\fBatlas\\-config\\-deactivate\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-deactivate.1",
    "content": ".\\\"     Title: atlas-config-deactivate\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-DEACTIVATE\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-deactivate \\-\\- Deactivate an installed Atlas Shell Tools module\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas-config deactivate\\fR \\-\\-help\n\\fIatlas-config deactivate\\fR <\\fImodule\\fR>\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nDeactivate the provided <\\fImodule\\fR>. Commands defined in a deactivated module\ncannot be run by the \\fBatlas\\fR tool until the module is reactivated.\nDeactivating a module will also prevent the commands from showing in the\n\\fBatlas\\fR tool command list.\n\n.SH \"OPTIONS\"\n.sp\n\n.PP\n<\\fImodule\\fR>\n.RS 4\nThis indicates a module. The module should be referred to using its name, as\nreported by \\fBatlas-config-list\\fR(1).\n.RE\n\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1), \\fBatlas\\-config\\-activate\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-install.1",
    "content": ".\\\"     Title: atlas-config-install\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-INSTALL\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-install \\-\\- Install a new Atlas Shell Tools module from a local JAR\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIinstall\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIinstall\\fR [\\-\\-symlink] [\\-\\-force] [\\-\\-skip] [\\-\\-deactivated]\n                     [\\-\\-name=<\\fIname\\fR>] <\\fIJARfile\\fR>\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nInstalls and activates a given <\\fIJARfile\\fR> as a new \\fBatlas\\fR module.\nThis will deactivate any currently activated module. If you are trying to install\na module from a specific repo, please see \\fBatlas-config-repo\\fR(1) for more information.\n\nWhen installing directly from a <\\fIJARfile\\fR>, the \\fB\\-\\-symlink\\fR option\ncan be enabled to install using a symbolic link. This is great for cases where\nthe underlying <\\fIJARfile\\fR> may change dynamically (e.g. when incrementally\nrebuilding the JAR during development/testing of a new command).\n\nNote that once a module is installed, it should be referred to by its module\nname and not by the <\\fIJARfile\\fR> name. The module names are displayed by\n\\fBatlas-config-list\\fR(1). Unless the \\fB\\-\\-name\\fR option is used, the module name will be\nthe same as the <\\fIJARfile\\fR> name but without the '.jar' extension.\n\n.SH \"OPTIONS\"\n.sp\n\n.PP\n<\\fIJARfile\\fR>\n.RS 4\nThe path to a JARfile containing \\fBatlas\\fR commands. Once installed, this JARfile\nis referred to as a module.\n.RE\n\n.PP\n\\fB\\-\\-deactivated\\fR\n.RS 4\nInstall a module without actually activating it.\n.RE\n\n.PP\n\\fB\\-\\-force\\fR\n.RS 4\nForce overwrite when re-installing an existing module.\n.RE\n\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.PP\n\\fB\\-\\-name\\fR=<name>\n.RS 4\nInstall a module using a custom name.\n.RE\n\n.PP\n\\fB\\-\\-skip\\fR\n.RS 4\nSkip installation when re-installing an existing module.\n.RE\n\n.PP\n\\fB\\-\\-symlink\\fR, \\fB-s\\fR\n.RS 4\nInstall a module using a symlink as opposed to a copy.\n.RE\n\n\n.SH \"EXAMPLES\"\n.sp\nInstall a module with custom name 'MyModule':\n.sp\n.RS 4\n$ atlas\\-config install \\-\\-name=MyModule ~/Desktop/example.jar\n.RE\n.sp\nInstall a symlinked module but do not activate it:\n.sp\n.RS 4\n$ atlas\\-config install \\-\\-symlink \\-\\-deactivated ~/example.jar\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1), \\fBatlas\\-config\\-uninstall\\fR(1), \\fBatlas\\-config\\-activate\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-list.1",
    "content": ".\\\"     Title: atlas-config-list\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-LIST\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-list \\-\\- List installed Atlas Shell Tools modules and module statuses\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIlist\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIlist\\fR [\\-\\-current] [\\-\\-one\\-line]\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nList installed modules with additional contextual information. This listing\nis also known as the module workspace. The \\fB\\-\\-current\\fR option can be used to view\nonly the current activated module, if there is one. The \\fB\\-\\-one\\-line\\fR option can be used to\nsuppress metadata output.\n\nA \\fB*\\fR next to a module denotes that the module is activated. Symlinked\nmodules display their targets using the familiar arrow notation (\\fB\\->\\fR). If a\nsymlink is broken, the warning \\fB(BROKEN SYMLINK)\\fR is displayed next to the\nmodule name.\n\n.SH \"OPTIONS\"\n.sp\n\n.PP\n\\fB\\-\\-current\\fR, \\fB-c\\fR\n.RS 4\nShow only the currently activated module, if one is activated.\n.RE\n\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.PP\n\\fB\\-\\-one\\-line\\fR, \\fB-1\\fR\n.RS 4\nSuppress the module metadata output, instead displaying each module on a single line.\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-log.1",
    "content": ".\\\"     Title: atlas-config-log\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-LOG\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-log \\-\\- Check or set Atlas Shell Tools log parameters\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIlog\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIlog\\fR reset\n\\fIatlas\\-config\\fR \\fIlog\\fR set\\-level <new\\-level>\n\\fIatlas\\-config\\fR \\fIlog\\fR set\\-stream <new\\-stream>\n\\fIatlas\\-config\\fR \\fIlog\\fR show\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nUse this command to check or set the current log parameters. Under the hood,\nthis command is a shortcut for modifying the \\fBatlas\\fR log4j configuration\nfile. It expects that file to follow a well-defined format. For best\nresults, avoid manually updating the configuration file.\n\n.SH \"OPTIONS\"\n.sp\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.SH \"DIRECTIVES\"\n\\fBatlas\\-config\\-log\\fR(1) exposes its interface through various directives,\ndetailed below.\n.sp\n\n.PP\n\\fIreset\\fR\n.RS 4\nReset the log parameters to default, i.e. level=\\fBERROR\\fR and stream=\\fBstderr\\fR.\nThis is equivalent to running \\fBatlas\\-config reset log\\fR.\n.RE\n\n.PP\n\\fIset\\-level\\fR\n.RS 4\nSet the current logging level to <new\\-level>.\nValid log levels: \\fBALL\\fR, \\fBTRACE\\fR, \\fBDEBUG\\fR, \\fBINFO\\fR, \\fBWARN\\fR, \\fBERROR\\fR, \\fBFATAL\\fR, and \\fBOFF\\fR.\n.RE\n\n.PP\n\\fIset\\-stream\\fR\n.RS 4\nSet the current logging stream to <new\\-stream>.\nValid stream settings: \\fBstdout\\fR and \\fBstderr\\fR.\n.RE\n\n.PP\n\\fIshow\\fR\n.RS 4\nShow the current log settings.\n.RE\n\n.SH \"EXAMPLES\"\n.sp\nSet the log level to \\fBINFO\\fR:\n.sp\n.RS 4\n$ atlas\\-config log set\\-level INFO\n.RE\n.sp\nShow the log parameters:\n.sp\n.RS 4\n$ atlas\\-config log show\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-preset.1",
    "content": ".\\\"     Title: atlas-config-preset\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-PRESET\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-preset \\-\\- Create and manage Atlas Shell Tools command presets\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIcopy\\fR <command> <src> <dest>\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIcopy\\-global\\fR <src> <dest>\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIedit\\fR <command> <preset>\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIedit\\-global\\fR <preset>\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIlist\\fR [command [preset]]\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIlist\\-global\\fR [preset]\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fInamespace\\fR <subdirective> [namespace]\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIremove\\fR <command> [preset]\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIremove\\-global\\fR [preset]\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIsave\\fR <command> <preset> <options...>\n\\fIatlas\\-config\\fR \\fIpreset\\fR \\fIsave\\-global\\fR <preset> <options...>\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\n\\fBatlas\\-config\\-preset\\fR(1) provides a more granular preset management interface\nthan the basic \\fBatlas\\fR(1) options (\\fB\\-\\-preset\\fR, \\fB\\-\\-save\\-preset\\fR, \\fB\\-\\-save\\-global\\-preset\\fR).\nIt can handle more complex tasks like editing a preset in place, or managing the\npreset namespaces. For full details on the workings of \\fBatlas\\-config\\-preset\\fR(1),\nsee the \\fBTier 2\\fR and \\fBTier 3\\fR sections in \\fBatlas\\-presets\\fR(7).\n\n.SH \"OPTIONS\"\n.sp\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.SH \"DIRECTIVES\"\n\\fBatlas\\-config\\-preset\\fR(1) exposes its interface through various directives,\ndetailed below. Again, this information is presented in more detail in\n\\fBatlas\\-presets\\fR(7).\n.sp\n\n.PP\n\\fIcopy\\fR\n.RS 4\nFor <command>, copy preset <src> into new preset <dest>. <dest> must not already\nexist, else the copy will fail.\n.RE\n\n.PP\n\\fIcopy\\-global\\fR\n.RS 4\nCopy global preset <src> into new global preset <dest>. <dest> must not already\nexist, else the copy will fail.\n.RE\n\n.PP\n\\fIedit\\fR\n.RS 4\nEdit preset <preset> for <command>. If <preset> does not exist, then it will be\ncreated when the edit is successfully saved. The default preset editor is \\fBvim\\fR,\nbut this can be changed by setting the \\fBATLAS_SHELL_TOOLS_EDITOR\\fR environment variable.\n.RE\n\n.PP\n\\fIedit\\-global\\fR\n.RS 4\nEdit global preset <preset>. If <preset> does not exist, then it will be\ncreated when the edit is successfully saved. The default preset editor is \\fBvim\\fR,\nbut this can be changed by setting the \\fBATLAS_SHELL_TOOLS_EDITOR\\fR environment variable.\n.RE\n\n.PP\n\\fIlist\\fR\n.RS 4\nList all available presets (including globals), or list all presets for a given [command], or\nlist contents of preset [preset] for [command].\n.RE\n\n.PP\n\\fIlist\\-global\\fR\n.RS 4\nList all available global presets, or list contents of global preset [preset].\n.RE\n\n.PP\n\\fInamespace\\fR\n.RS 4\nExecute a <subdirective> on a given preset [namespace]. Available subdirectives\nare \\fBcreate\\fR, \\fBlist\\fR, \\fBremove\\fR, and \\fBuse\\fR. See \\fBTier 3\\fR in\n\\fBatlas\\-presets\\fR(7) for more details.\n.RE\n\n.PP\n\\fIremove\\fR\n.RS 4\nRemove all presets for a given <command>, or remove the preset [name] for\n<command>.\n.RE\n\n.PP\n\\fIremove\\-global\\fR\n.RS 4\nRemove all global presets, or remove the global preset [name].\n.RE\n\n.PP\n\\fIsave\\fR\n.RS 4\nSave a preset <preset> for <command> without actually running the command.\n<options...> is a sequence of options to be saved in the preset.\nAgain, recall that you must use the long option '=' syntax for specifying option\narguments when saving a preset (e.g. '--opt=arg' and \\fInot\\fR '--opt arg').\n.RE\n\n.PP\n\\fIsave\\-global\\fR\n.RS 4\nSave a global preset <preset>. <options...> is a sequence of options to be saved in the preset.\nAgain, recall that you must use the long option '=' syntax for specifying option\narguments when saving a preset (e.g. '--opt=arg' and \\fInot\\fR '--opt arg').\n.RE\n\n.SH \"EXAMPLES\"\n.sp\nEdit preset \"p1\" for command \"MyCommand\":\n.sp\n.RS 4\n$ atlas\\-config preset edit MyCommand p1\n.RE\n.sp\nCreate a namespace called \"namespace1\":\n.sp\n.RS 4\n$ atlas\\-config preset namespace create namespace1\n.RE\n.sp\nSave a global preset \"p1\":\n.sp\n.RS 4\n$ atlas\\-config preset save\\-global p1 --opt1 --opt2=opt2Arg\n.RE\n.sp\nFor more examples, see \\fBatlas\\-presets\\fR(7).\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-presets\\fR(7)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-repo.1",
    "content": ".\\\"     Title: atlas-config-repo\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-REPO\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-repo \\-\\- Register and manage Atlas Shell Tools repositories\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIrepo\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIrepo\\fR \\fIadd\\fR <repo\\-name> <URL> [ref]\n\\fIatlas\\-config\\fR \\fIrepo\\fR \\fIadd\\-gradle\\-exclude\\fR <repo\\-name> <package>\n\\fIatlas\\-config\\fR \\fIrepo\\fR \\fIadd\\-gradle\\-skip\\fR <repo\\-name> <task>\n\\fIatlas\\-config\\fR \\fIrepo\\fR \\fIedit\\fR <repo\\-name>\n\\fIatlas\\-config\\fR \\fIrepo\\fR \\fIinstall\\fR <repo\\-name> [\\-\\-ref=ref]\n\\fIatlas\\-config\\fR \\fIrepo\\fR \\fIlist\\fR [repo\\-name]\n\\fIatlas\\-config\\fR \\fIrepo\\fR \\fIremove\\fR <repo\\-name>\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\n\\fBatlas\\-config\\-repo\\fR(1) provides an interface for managing repo objects (By repo,\nwe simply mean any \\fBgit\\fR(1) repository that contains Atlas Shell Tools command\nimplementations). Once a new repo object has been registered using \\fIadd\\fR,\nnew modules can be installed from the repo using the \\fIinstall\\fR directive.\nThis makes it easy to fetch the newest versions of Atlas Shell Tools commands and\nmanage their multiple sources. Additional directives facilitate more advanced use\ncases. See the \\fBDIRECTIVES\\fR section for details.\n.sp\nFor module installation, \\fBatlas\\-config\\-repo\\fR(1) uses the default \\fBbuild.gradle\\fR\nof the referenced repo, but with a special injected configuration designed to build a fat\nJAR that will work with Atlas Shell Tools. See the \\fIinstall\\fR directive for more details\non this process. Additionally, \\fBatlas\\-config\\-repo\\fR(1) provides some other directives to\nmanipulate this configuration.\n.sp\nAlso note that registering new repos with \\fIadd\\fR does not actually save the referenced\nrepository on your machine. Rather it simply stores some metadata (which we refer to as a repo\nobject) that is used to clone the referenced repo at install\\-time. The clone is saved in\na temporary location and is removed when module installation completes.\n\n.SH \"DIRECTIVES\"\n\\fBatlas\\-config\\-repo\\fR(1) exposes its interface through various directives,\ndetailed below.\n.sp\n\n.PP\n\\fIadd\\fR\n.RS 4\nAdd a new repo object with a given <repo\\-name>, \\fBgit\\fR(1) remote <URL>, and an\noptional default [ref]. Here, [ref] is a \\fBgit\\fR(1) ref (e.g. a branch name, a tag, or\neven a commit hash). If no explicit [ref] is provided, \\fIadd\\fR defaults to 'main'. The ref\nassociated with a repo object is used by \\fIinstall\\fR as the default. See \\fIinstall\\fR for more\ninformation. Note that for <URL>, you can use any git\\-clonable URL, i.e. an https:// URL, a file://\nURL, or even a local path like /Users/you/somerepo.\n.RE\n\n.PP\n\\fIadd\\-gradle\\-exclude\\fR\n.RS 4\nAdd an excluded <package> to repo <repo\\-name>. When the \\fIinstall\\fR directive\nbuilds a module, it is using Gradle (with a custom Atlas Shell Tools configuration) to create a\nfat JAR file for that module. Excluded packages are removed from the Atlas Shell Tools Gradle\nconfiguration using Gradle's 'exclude group:' directive. The 'exclude module:' directive is\nnot currently supported.\n.RE\n\n.PP\n\\fIadd\\-gradle\\-skip\\fR\n.RS 4\nAdd a skipped <task> to repo <repo\\-name>. When the \\fIinstall\\fR directive\nbuilds a module, it is using Gradle (with a custom Atlas Shell Tools configuration) to create a\nfat JAR file for that module. By default, \\fIinstall\\fR is running a \\fB./gradlew clean build\\fR.\nAdding skipped tasks effectively adds \\fB\\-x <task>\\fR arguments to the above command.\n.RE\n\n.PP\n\\fIedit\\fR\n.RS 4\nEdit the configuration for repo <repo\\-name> if it exists. The default repo editor\nis \\fBvim\\fR, but this can be changed by setting the \\fBATLAS_SHELL_TOOLS_EDITOR\\fR\nenvironment variable.\n.RE\n\n.PP\n\\fIinstall\\fR\n.RS 4\nInstall a module from a repo object with name <repo\\-name>. This command will clone\nthe repo referenced by <repo\\-name>'s URL to a temporary location. Next, it will check\nout the ref specified in <repo\\-name>'s config or use the override provided by \\-\\-ref\nif present. It will then initiate a Gradle build, using the repo's \\fBbuild.gradle\\fR\nfile with a custom injected Atlas Shell Tools configuration. Finally, it will install\nand activate the freshly built module. When the install finishes, the cloned repo will\nbe removed.\n.RE\n\n.PP\n\\fIlist\\fR\n.RS 4\nDisplay all registered repo objects, along with their URLs and default refs. If a [repo\\-name]\nis provided, then display the full configuration of the given repo object.\n.RE\n\n.PP\n\\fIremove\\fR\n.RS 4\nRemove the registration for repo object <repo\\-name>, if it exists.\n.RE\n\n.SH \"EXAMPLES\"\n.sp\nRegister a new repo object with name \\fBmy\\-repo\\fR and default branch \\fBmain\\fR:\n.sp\n.RS 4\n$ atlas\\-config repo add my\\-repo https://github.com/me/my\\-repo.git\n.RE\n.sp\nRegister a new repo object with name \\fBmy\\-repo\\fR and default branch \\fBmy\\-branch\\fR:\n.sp\n.RS 4\n$ atlas\\-config repo add my\\-repo https://github.com/me/my\\-repo.git my\\-branch\n.RE\n.sp\nSkip Gradle task 'integrationTest' when installing from \\fBmy\\-repo\\fR:\n.sp\n.RS 4\n$ atlas\\-config repo add\\-gradle\\-skip my\\-repo integrationTest\n.RE\n.sp\nInstall a new module from repo \\fBmy\\-repo\\fR, but override the default ref with a specific release:\n.sp\n.RS 4\n$ atlas\\-config repo install my\\-repo \\-\\-ref=1.3.9\n.RE\n.sp\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1), \\fBatlas\\-glossary\\fR(7)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-reset.1",
    "content": ".\\\"     Title: atlas-config-reset\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-RESET\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-reset \\-\\- Reset the Atlas Shell Tools installation\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIreset\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIreset\\fR all\n\\fIatlas\\-config\\fR \\fIreset\\fR index\n\\fIatlas\\-config\\fR \\fIreset\\fR log\n\\fIatlas\\-config\\fR \\fIreset\\fR modules\n\\fIatlas\\-config\\fR \\fIreset\\fR presets\n\\fIatlas\\-config\\fR \\fIreset\\fR repos\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nCompletely reset the \\fBatlas\\fR installation. A full reset (using \\fIall\\fR) consists\nof the following steps:\n\n.RS 4\n\\fB1)\\fR Uninstalling all installed modules\n\n\\fB2)\\fR Removing all presets\n\n\\fB3)\\fR Setting the log level back to \\fBERROR\\fR\n\n\\fB4)\\fR Setting the log stream back to \\fBstderr\\fR\n\n\\fB5)\\fR Deleting the active command index\n\n\\fB6)\\fR Removing all saved repos\n.RE\n\nYou can use various directives to only run some subset of these steps. See\nthe \\fBDIRECTIVES\\fR section for more details.\n\n.SH \"OPTIONS\"\n.sp\n\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.SH \"DIRECTIVES\"\n\\fBatlas\\-config\\-reset\\fR(1) exposes its interface through various directives,\ndetailed below.\n.sp\n\n.PP\n\\fIall\\fR\n.RS 4\nPerform all the reset steps, fully resetting the environment to the\nstate it was in at install time.\n.RE\n\n.PP\n\\fIindex\\fR\n.RS 4\nReset the active module index.\n.RE\n\n.PP\n\\fIlog\\fR\n.RS 4\nReset the log parameters to default, i.e. level=\\fBERROR\\fR and stream=\\fBstderr\\fR.\nThis is equivalent to running \\fBatlas\\-config log reset\\fR.\n.RE\n\n.PP\n\\fImodules\\fR\n.RS 4\nUninstall all installed modules. This is equivalent to running \\fBatlas\\-config uninstall \\-\\-all\\fR.\n.RE\n\n.PP\n\\fIpresets\\fR\n.RS 4\nRemove all saved presets in all namespaces. Also resets the current namespace\nto 'default'.\n.RE\n\n.PP\n\\fIrepos\\fR\n.RS 4\nRemove all saved repos.\n.RE\n\n.SH \"EXAMPLES\"\n.sp\nReset everything:\n.sp\n.RS 4\n$ atlas\\-config reset all\n.RE\n.sp\nReset only the repos:\n.sp\n.RS 4\n$ atlas\\-config reset repos\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-sync.1",
    "content": ".\\\"     Title: atlas-config-sync\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-SYNC\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-sync \\-\\- Refresh the Atlas Shell Tools installation based on the active module\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIsync\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIsync\\fR\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nRefresh the command index for the current active module. Additionally, this will\nperform some runtime checks to validate that all commands in the active module\nregistered their documentation and options/arguments correctly.\n\nThis command is useful when a module JAR is installed using a symlink, and has\nbeen updated at the other end of the link. If you made changes to a command\nname or description, running this command will reflect those changes in\n\\fBatlas \\-\\-list\\fR. If you altered some option/argument or documentation\nregistration, running \\fBsync\\fR will perform a quick validation \\- preventing\nyou from having to test each altered command individually.\n\n.SH \"OPTIONS\"\n.sp\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-uninstall.1",
    "content": ".\\\"     Title: atlas-config-uninstall\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-UNINSTALL\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-uninstall \\-\\- Uninstall Atlas Shell Tools module(s)\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIuninstall\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIuninstall\\fR \\-\\-all\n\\fIatlas\\-config\\fR \\fIuninstall\\fR [\\-\\-force] <\\fImodules...\\fR>\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nRemove installed module(s). If the module is a symlink, this will only\nremove the link. It will \\fBnot\\fR delete the linked\\-to file. By default, this\ncommand will not uninstall an activated module. This can be overridden with the\n\\fB\\-\\-force\\fR or \\fB\\-\\-all\\fR options.\n\n.SH \"OPTIONS\"\n.sp\n\n.PP\n<\\fImodules...\\fR>\n.RS 4\nThis indicates one or more modules. The modules should be referred to using their names as\nreported by \\fBatlas-config-list\\fR(1).\n.RE\n\n.PP\n\\fB\\-\\-all\\fR, \\fB\\-a\\fR\n.RS 4\nUninstall all modules including the activated module, ignoring the supplied <\\fImodule\\fR>\nargument if present. This is equivalent to running \\fBatlas\\-config reset \\-\\-modules\\fR.\n.RE\n\n.PP\n\\fB\\-\\-force\\fR\n.RS 4\nForce uninstall an activated module.\n.RE\n\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1), \\fBatlas\\-config\\-install\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config-update.1",
    "content": ".\\\"     Title: atlas-config-update\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG-UPDATE\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config\\-update \\-\\- Update the Atlas Shell Tools core toolkit\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR \\fIupdate\\fR \\-\\-help\n\\fIatlas\\-config\\fR \\fIupdate\\fR\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\nUpdate the \\fBatlas\\-shell\\-tools\\fR(7) core toolkit using \\fBgit\\fR(1). This will not\nactually update any modules. To update modules using your repos, see\n\\fBatlas\\-config\\-repo\\fR(1).\n\n.SH \"OPTIONS\"\n.sp\n.PP\n\\fB\\-\\-help\\fR\n.RS 4\nShow this help menu.\n.RE\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1), \\fBatlas\\-config\\-repo\\fR(1)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas-config.1",
    "content": ".\\\"     Title: atlas-config\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CONFIG\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-config \\-\\- Configure the Atlas Shell Tools installation\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\-config\\fR [\\-\\-no-pager] \\-\\-help[=<\\fIcommand\\fR>]\n\\fIatlas\\-config\\fR \\-\\-version\n\\fIatlas\\-config\\fR [\\-\\-no-pager] [\\-\\-quiet] <\\fIcommand\\fR> [arg...]\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\n\\fBatlas\\-config\\fR exposes an interface for configuring the Atlas Shell Tools\ninstallation. It provides a way to install new modules, change the currently\nactivated module, reset the installation, and more. \\fBatlas\\-config\\fR operates\nusing a subcommand interface, where each subcommand performs a different\nconfiguration action. See the \\fBATLAS\\-CONFIG COMMANDS\\fR section for more\ninformation.\n.sp\nFor running actual Atlas Shell Tools commands, see \\fBatlas\\fR(1).\n.sp\nFor a glossary of Atlas Shell Tools terms, see \\fBatlas\\-glossary\\fR(7).\n\n.SH \"OPTIONS\"\n\n.PP\n<\\fIcommand\\fR>\n.RS 4\nThis indicates a command. See the \\fBATLAS\\-CONFIG COMMANDS\\fR section for a list of available\ncommands.\n.RE\n\n.PP\n\\fB\\-\\-help\\fR[=<\\fIcommand\\fR>]\\fR\n.RS 4\nShow the help menu for a given command. If no command is given, show\na general help menu and then exit.\n.RE\n\n.PP\n\\fB\\-\\-no\\-pager\\fR\n.RS 4\nDisable pagination for all documentation.\n.RE\n\n.PP\n\\fB\\-\\-quiet\\fR, \\fB\\-q\\fR\n.RS 4\nSuppress non-essential output.\n.RE\n\n.PP\n\\fB\\-\\-version\\fR, \\fB-V\\fR\n.RS 4\nShow the \\fBatlas\\-config\\fR version info and then exit.\n.RE\n.sp\n\n.SH \"ATLAS-CONFIG COMMANDS\"\n.sp\n\\fBatlas-config\\fR uses a set of subcommands to present a high\\-level interface\nfor installation configuration.\n\n\\fBatlas\\-config\\-activate\\fR(1)\n.RS 4\nActivate an installed module.\n.RE\n\n\\fBatlas\\-config\\-deactivate\\fR(1)\n.RS 4\nDeactivate an installed module.\n.RE\n\n\\fBatlas\\-config\\-install\\fR(1)\n.RS 4\nInstall a new module.\n.RE\n\n\\fBatlas\\-config\\-list\\fR(1)\n.RS 4\nList all installed modules and module statuses.\n.RE\n\n\\fBatlas\\-config\\-log\\fR(1)\n.RS 4\nCheck or set log parameters.\n.RE\n\n\\fBatlas\\-config\\-preset\\fR(1)\n.RS 4\nCreate and manage Atlas Shell Tools command presets.\n.RE\n\n\\fBatlas\\-config\\-repo\\fR(1)\n.RS 4\nRegister and manage Atlas Shell Tools repositories.\n.RE\n\n\\fBatlas\\-config\\-reset\\fR(1)\n.RS 4\nReset the installation.\n.RE\n\n\\fBatlas\\-config\\-sync\\fR(1)\n.RS 4\nRefresh the installation based on the active module.\n.RE\n\n\\fBatlas\\-config\\-uninstall\\fR(1)\n.RS 4\nUninstall a module.\n.RE\n\n\\fBatlas\\-config\\-update\\fR(1)\n.RS 4\nUpdate the Atlas Shell Tools core toolkit.\n.RE\n\n.SH \"TERMINAL AND ENVIRONMENT\"\n.sp\n\\fBatlas\\-config\\fR displays its various help and manual pages using \\fBman\\fR,\nwhich is paged by default. To disable paged output for all documentation, try\nthe \\fB\\-\\-no\\-pager\\fR option.\n.sp\n\\fBatlas-config\\fR uses formatted output when appropriate. To change this behavior,\n\\fBatlas-config\\fR checks for existence of the following environment variables:\n.sp\n.RS 4\n\\fBNO_COLOR\\fR \\-\nDisable all special formatted output. Other popular CLI tools also\nrespect this variable. See <https://no\\-color.org>.\n.sp\n\\fBATLAS_SHELL_TOOLS_NO_COLOR\\fR \\-\nDisable special formatted output for \\fBatlas\\fR and \\fBatlas-config\\fR only.\n.sp\n\\fBATLAS_SHELL_TOOLS_USE_COLOR\\fR \\-\nEnable special formatted output. Overrides the setting of \\fBNO_COLOR\\fR and \\fBATLAS_SHELL_TOOLS_NO_COLOR\\fR.\n.sp\n.RE\n\\fBatlas-config\\fR stores program data in compliance with the XDG Base Directory\nspecification, i.e. at $HOME/.local/share/atlas\\-shell\\-tools. It also respects the\n\\fBXDG_DATA_HOME\\fR environment variable \\- if set, \\fBatlas-config\\fR will store program\ndata at the base path specified by that variable. See \\fBatlas\\-plumbing\\fR(5) for\nmore information.\n.sp\nFor more details about all the environment variables used by \\fBatlas\\-shell\\-tools\\fR(7),\nsee the \\fBatlas\\-environment\\fR(7) manpage.\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\fR(1), \\fBatlas\\-plumbing\\fR(5), \\fBatlas\\-presets\\fR(7), \\fBatlas\\-environment\\fR(7)\n\n.SH \"AUTHOR\"\n.sp\nThis program was written by Lucas Cram <lucaspcram@gmail.com>.\nPlease report any bugs you find.\n\n.SH \"BUGS\"\n.sp\nIf you run the command:\n.sp\n.RS 4\n$ atlas\\-config install <active\\-module>\n.RE\n.sp\nwhere <active\\-module> is the path to the currently active module JARfile, this\nwill start the installation process but then delete the JARfile.\n.sp\nPlease report any bugs you find to the \\fBAUTHOR\\fR.\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man1/atlas.1",
    "content": ".\\\"     Title: atlas\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS\" \"1\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas \\-\\- Run an Atlas Shell Tools command\n\n.SH \"SYNOPSIS\"\n.sp\n.nf\n\\fIatlas\\fR [\\-\\-no-pager] \\-\\-help[=<\\fIcommand\\fR>]\n\\fIatlas\\fR \\-\\-version\n\\fIatlas\\fR [\\-\\-no-pager] \\-\\-list\n\\fIatlas\\fR \\-\\-class\\-of=<\\fIcommand\\fR>\n\\fIatlas\\fR [\\-\\-no\\-pager] [\\-\\-debug] [\\-\\-memory=<amount>] [\\-\\-preset=<names>] \n      [\\-\\-save\\-preset=<new\\-preset>] [\\-\\-save\\-global\\-preset=<new\\-preset>] <\\fIcommand\\fR> [arg...]\n.fi\n\n.SH \"DESCRIPTION\"\n.sp\n\\fBatlas\\fR is an easy\\-to\\-use wrapper for launching Atlas Shell Tools commands.\nIt provides a simple interface to find and run any commands defined in the currently\nactivated Atlas Shell Tools module.\nAtlas Shell Tools also implements a robust preset management system, which can save\nyou tons of typing for commands that require a lot of options. See \\fBatlas-presets\\fR(7)\nsection for more on this topic.\n.sp\nFor advanced module management capabilites and other installation configuration\ncommands, see \\fBatlas\\-config\\fR(1).\n.sp\nFor a glossary of Atlas Shell Tools terms, see \\fBatlas-glossary\\fR(7).\n\n.SH \"OPTIONS\"\n\n.PP\n<\\fIcommand\\fR>\n.RS 4\nThis indicates a command. A list of available commands can be\nseen with the \\fB--list\\fR option.\n.RE\n\n.PP\n\\fB\\-\\-class\\-of\\fR=<\\fIcommand\\fR>\n.RS 4\nShow the full classname of a given command and then exit.\n.RE\n\n.PP\n\\fB\\-\\-debug\\fR\n.RS 4\nRun \\fBatlas\\fR in debug mode. While in debug mode, \\fBatlas\\fR will not actually\nrun any commands. Instead, it will print debug information and exit. Use this\noption to see the full JVM launch command, preset debug diagnostics, and more.\n.RE\n\n.PP\n\\fB\\-\\-help\\fR[=<\\fIcommand\\fR>]\\fR\n.RS 4\nShow the help menu for a given command. If no command is given, show\na general help menu and then exit.\n.RE\n\n.PP\n\\fB\\-\\-list\\fR, \\fB-l\\fR\n.RS 4\nDisplay a list of all available commands and exit. Command name collision is\nresolved when the active module index is generated. The resolution exists at\nthe index level only and is not reflected in the actual command\nclass files.\n.RE\n\n.PP\n\\fB\\-\\-memory\\fR=<amount>, \\fB\\-m\\fR<amount>\n.RS 4\nSet the maximum memory pool size for the JVM that runs your command.\n<amount> should be specified in bytes. \\fB--memory\\fR also understands\nshorthand for metric prefixes, e.g. 1024K, 512M, 4G, etc. If this option\nis not supplied, the memory pool defaults to 8G.\n.RE\n\n.PP\n\\fB\\-\\-no\\-pager\\fR\n.RS 4\nDisable pagination for all documentation.\n.RE\n\n.PP\n\\fB\\-\\-preset\\fR=<names>, \\fB\\-p\\fR<names>\n.RS 4\nApply the given preset(s) before running <\\fIcommand\\fR>. A list of presets\ncan be provided by separating preset names with either a colon or a comma.\nSee \\fBatlas-presets\\fR(7) for more information.\n.RE\n\n.PP\n\\fB\\-\\-quiet\\fR, \\fB\\-q\\fR\n.RS 4\nSuppress non-essential \\fBatlas\\fR output.\n.RE\n\n.PP\n\\fB\\-\\-save\\-preset\\fR=<new\\-preset>\n.RS 4\nSave the command ARGV to <new\\-preset> before running <\\fIcommand\\fR>. If\n\\fB\\-\\-preset\\fR is also supplied, the save operation will occur after the\nargument to \\fB\\-\\-preset\\fR is applied. So <new\\-preset> will include ARGV\nelements inserted by the \\fB\\-\\-preset\\fR application in addition to those\ndirectly supplied by the user. See \\fBatlas-presets\\fR(7) for more information.\n.RE\n\n.PP\n\\fB\\-\\-save\\-global\\-preset\\fR=<new\\-preset>\n.RS 4\nThis option is the same as \\fB\\-\\-save\\-preset\\fR, except it will instead save\n<new\\-preset> to the global presets instead of the command\\-specific presets.\nNote that it can be used in conjuction with \\fB\\-\\-save\\-preset\\fR to save both\na global and command preset at the same time. Also like \\fB\\-\\-save\\-preset\\fR,\n\\fB\\-\\-save\\-global\\-preset\\fR saves ARGV after the the application of\n\\fB\\-\\-preset\\fR if present. See \\fBatlas-presets\\fR(7) for more information.\n.RE\n\n.PP\n\\fB\\-\\-version\\fR, \\fB-V\\fR\n.RS 4\nShow the \\fBatlas\\fR version info and then exit.\n.RE\n.sp\n\n.SH \"EXAMPLES\"\n.sp\nLet's break down how to run a command called MyCommand with some args and options.\nHere, '--opt1' is an option that takes no arguments. '--opt2' is an option that takes\nan optional argument, so we use the '=' operator to disambiguate 'optarg2' from a regular\nargument. '--opt3' is an option that takes a required argument, so \\- if desired \\- we\ncan omit the '=' when specifying 'optarg3'. Finally, 'arg1' and 'arg2' are regular\nprogram arguments:\n.sp\n.RS 4\n$ atlas MyCommand \\-\\-opt1 arg1 \\-\\-opt2=optarg2 arg2 --opt3 optarg3\n.RE\n.sp\nSee the manual page for MyCommand:\n.sp\n.RS 4\n$ atlas \\-\\-help MyCommand\n.RE\n.sp\nSee a list of all available commands:\n.sp\n.RS 4\n$ atlas \\-\\-list\n.RE\n.sp\nSave preset 'p1' for MyCommand and then run:\n.sp\n.RS 4\n$ atlas \\-\\-save\\-preset p1 MyCommand arg1 \\-\\-opt1=opt1arg \\-\\-opt2=opt2arg\n.RE\n.sp\nRun MyCommand with preset 'p1':\n.sp\n.RS 4\n$ atlas \\-\\-preset p1 MyCommand arg1 \\-\\-opt2=overrideOpt2Arg\n.RE\n.sp\nRun MyCommand with preset 'p1', while also saving new preset 'p2':\n.sp\n.RS 4\n$ atlas \\-\\-preset p1 \\-\\-save\\-preset p2 MyCommand arg1 \\-\\-opt2=overrideOpt2Arg\n.RE\n.sp\n\n.SH \"TERMINAL AND ENVIRONMENT\"\n.sp\n\\fBatlas\\fR pages the output of the various help messages using a combination of\n\\fBless\\fR and \\fBman\\fR. Subcommand help pages are piped through \\fBless\\fR\nby default, but this can be overridden with the \\fBATLAS_SHELL_TOOLS_PAGER\\fR environment variable.\nThe actual \\fBatlas\\fR manual page (which you are currently reading) is displayed\nusing \\fBman\\fR. To disable paged output for all documentation, try the\n\\fB\\-\\-no\\-pager\\fR option.\n.sp\n\\fBatlas\\fR uses formatted output when appropriate. To change this behavior,\n\\fBatlas\\fR checks for existence of the following environment variables:\n.sp\n.RS 4\n\\fBNO_COLOR\\fR \\-\nDisable all special formatted output. Other popular CLI tools also\nrespect this variable. See <https://no\\-color.org>.\n.sp\n\\fBATLAS_SHELL_TOOLS_NO_COLOR\\fR \\-\nDisable special formatted output for \\fBatlas\\fR and \\fBatlas-config\\fR only.\n.sp\n\\fBATLAS_SHELL_TOOLS_USE_COLOR\\fR \\-\nEnable special formatted output. Overrides the setting of \\fBNO_COLOR\\fR and \\fBATLAS_SHELL_TOOLS_NO_COLOR\\fR.\n.sp\n.RE\n\\fBatlas\\fR stores program data in compliance with the XDG Base Directory\nspecification, i.e. at $HOME/.local/share/atlas\\-shell\\-tools. It also respects the\n\\fBXDG_DATA_HOME\\fR environment variable \\- if set, \\fBatlas\\fR will store program\ndata at the base path specified by that variable. See \\fBatlas\\-plumbing\\fR(5) for\nmore information.\n.sp\nFor more details about all the environment variables used by \\fBatlas\\-shell\\-tools\\fR(7),\nsee the \\fBatlas\\-environment\\fR(7) manpage.\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-config\\fR(1), \\fBatlas\\-presets\\fR(7), \\fBatlas\\-environment\\fR(7)\n\n.SH \"AUTHOR\"\n.sp\nThis program was written by Lucas Cram <lucaspcram@gmail.com>.\n\n.SH \"BUGS\"\n.sp\nPlease report any bugs you find to the \\fBAUTHOR\\fR.\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man5/atlas-plumbing.5",
    "content": ".\\\"     Title: atlas-plumbing\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-PLUMBING\" \"5\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-plumbing \\-\\- Details about how Atlas Shell Tools stores toolkit program data\n\n.SH \"SYNOPSIS\"\n$HOME/.local/share/atlas-shell-tools/*\n\n.SH \"DESCRIPTION\"\n.sp\nUnless otherwise specified via the \\fBXDG_DATA_HOME\\fR environment variable,\n\\fBatlas\\-shell-tools\\fR(7) will store data at $HOME/.local/share/atlas-shell-tools.\nThis data directory itself contains four other directories, each of which is detailed\nin its own section below. These directories store all user data for the toolkit (the toolkit\nlocation is specified by the \\fBATLAS_SHELL_TOOLS_HOME\\fR environment variable).\n\n.SH \"LOG DIRECTORY\"\n$HOME/.local/share/atlas-shell-tools/log4j\n\nThis directory stores the default log4j configuration file for the toolkit. The parameters\nin this file can be edited with \\fBatlas\\-config\\-log\\fR(1) \\- they should \\fBnot\\fR be edited\nmanually. This directory also stores a backup log4j file that can be restored using the aforementioned\n\\fBatlas\\-config\\-log\\fR(1) command should the default file become corrupted.\n\n.SH \"MODULE DIRECTORY\"\n$HOME/.local/share/atlas-shell-tools/modules\n\nAlso known as the module workspace, this directory stores modules (fat JARs), module metadata files,\nand the active module index. Any JAR that is not the current active module will have a '.deactivated'\nfile extension. Files with a '.metadata' extension store additional information about the JAR file\nwith the same name. The active module index file, '.active_module_index', stores data about the\ncommands found in the current activated module and is used by \\fBatlas\\fR(1) to look up command\nclasses and provide the command list for the '\\-\\-list' option. The active module index stores\neach command name, class, and description line\\-wise with the fields separated by an ASCII record\nseparator character. This index can be refreshed with \\fBatlas\\-config\\-sync\\fR(1).\n\n.SH \"PRESET DIRECTORY\"\n$HOME/.local/share/atlas-shell-tools/presets\n\nThis directory stores all preset data for the toolkit. At root it contains a directory\nfor each preset namespace, using the 'default' namespace by default. The current active namespace\nis stored in a file called '.current_namespace'. Within each namespace is 1) a directory called '.global'\nwhich stores the global presets for the namespace and 2) directories\nfor any command that has saved presets under that namespace. The actual preset\ndata files are found within the '.global' and command directories. Preset data is stored\nline\\-wise exactly as each element will appear in ARGV, e.g. the line \"--option=argument\" will\nbecome a single ARGV element \"--option=argument\".\n\nThe data in this folder can be managed with \\fBatlas\\-config\\-preset\\fR(1).\n\n.SH \"REPO DIRECTORY\"\n$HOME/.local/share/atlas-shell-tools/repos\n\nThis directory stores the repo data for all the registered repos. Each registered repo\nhas its own directory that contains the repo data file 'repo_config'. Repo data is stored\nas line\\-wise \"key = value\" pairs.\n\nThe data in this folder can be managed with \\fBatlas\\-config\\-repo\\fR(1).\n\n.SH \"SEE ALSO\"\n.sp\n\\fBatlas\\-environment\\fR(7)\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man7/atlas-cli.7",
    "content": ".\\\"     Title: atlas-cli\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-CLI\" \"7\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-cli \\-\\- Atlas Shell Tools command line interface and conventions\n\n.SH \"SYNOPSIS\"\n*\n\n.SH \"DESCRIPTION\"\n.sp\n\\fBatlas\\-shell\\-tools\\fR(7) makes use of various command line conventions and\nutilities to provide a comprehensive user experience.\n\nThe \\fBShells\\fR section discusses the various shells supported by \\fBatlas\\-shell\\-tools\\fR(7).\n\nThe \\fBOptions\\fR section explains the various ways \\fBatlas\\-shell\\-tools\\fR(7) parses\ncommand line options.\n\nThe \\fBTAB Complete\\fR section shows the various ways that TAB completions can make command\nline interaction easier.\n\n.SH \"SHELLS\"\n.sp\nWhile \\fBatlas\\-shell\\-tools\\fR(7) is compatible with any command shell, \\fBbash\\fR(1) and\n\\fBzsh\\fR(1) users can take advantage of both the quick installation scripts and advanced TAB\ncompletion features.\n\nFor \\fBbash\\fR(1) users, note that while \\fBatlas\\-shell\\-tools\\fR(7) works best with \\fBbash\\fR(1)\nversion 4, version 3 can still utilize the full TAB complete feature set.\n\n.SH \"OPTIONS\"\n.sp\n\\fBatlas\\-shell\\-tools\\fR(7) has two ways of dealing with options:\n.sp\n1. Perl's Getopt::Long semantics are used when parsing global options suppled to the \\fBatlas\\fR(1) and\n\\fBatlas\\-config\\fR(1) scripts. In the following command line, the '\\-\\-debug' option is a global option:\n.sp\n.RS 4\n$ atlas \\-\\-debug my\\-command \\-\\-opt1\n.RE\n.sp\n2. Subcommand options are parsed with the SimpleOptionAndArgumentParser class, found in the\nAtlas repo <https://github.com/osmlab/atlas>. Please check the source of that class for\njavadoc documentation comments detailing the parsing semantics. They are similar to the\nGNU semantics found here:\n<https://www.gnu.org/software/libc/manual/html_node/Argument\\-Syntax.html>.\nMore links are provided in the class documentation. In the following command line,\nthe '\\-\\-opt1' option is a subcommand option:\n.sp\n.RS 4\n$ atlas \\-\\-debug my\\-command \\-\\-opt1\n.RE\n.sp\nAlso note that both the Perl option parsing and the SimpleOptionAndArgumentParser class recognize options\nusing a shortest non-ambiguous prefix. For example, suppose 'my\\-command' takes two options '\\-\\-large\\-option'\nand '\\-\\-lazy\\-option'. You could specify the '\\-\\-lazy\\-option' by simply\nsupplying '\\-\\-lazy' at the command line. However, supplying '\\-\\-la' would cause an error since 'la'\nis an ambiguous prefix between those two options.\n\n.SH \"TAB COMPLETE\"\n.sp\n\\fBatlas\\-shell\\-tools\\fR(7) supports various TAB completions for the \\fBbash\\fR(1) and\n\\fBzsh\\fR(1) shells. \\fBatlas\\fR(1) will contextually complete subcommand names in the current activated\nmodule, the '\\-\\-preset' option with various available presets, and local files. \\fBatlas\\-config\\fR(1)\nwill contextually complete subcommands as well as relevant arguments to whatever subcommand is provided\non the command line.\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man7/atlas-cookbook.7",
    "content": ".\\\"     Title: atlas-cookbook\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-GLOSSARY\" \"7\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-cookbook \\-\\- Atlas Shell Tools command cookbook\n\n.SH \"SYNOPSIS\"\n*\n\n.SH \"RECIPES\"\n\n.sp\n\\fBUpdate everything from the default installation to latest:\\fR\n.RS 4\n.sp\n# First, update the toolkit\n.sp\n$ atlas\\-config update\n.sp\n# Now, update the default atlas commands using the atlas repo\n.sp\n$ atlas\\-config repo install atlas\n.sp\n# You can also update any other registered repos in a similar way\n.RE\n\n.sp\n\\fBSee a list of all installed modules, activate <MODULE>, then list its commands:\\fR\n.RS 4\n.sp\n$ atlas\\-config list\n.sp\n$ atlas\\-config activate <MODULE>\n.sp\n$ atlas \\-\\-list\n.sp\n# You can also use the abbreviated \\-l flag\n.sp\n$ atlas \\-l\n.RE\n\n.sp\n\\fBAdd a new repo, check your registered repos, and then install a module from it:\\fR\n.sp\n.RS 4\n.sp\n$ atlas\\-config repo add my\\-new\\-repo https://github.com/me/my\\-repo.git\n.sp\n$ atlas\\-config repo list\n.sp\n$ atlas\\-config repo install my\\-new\\-repo\n.RE\n\n.sp\n\\fBBuild a fat JAR and then symlink install for local testing:\\fR\n.sp\n.RS 4\n.sp\n$ cd /path/to/my/project\n.sp\n$ ./gradlew clean fat \\-x check\n.sp\n$ atlas\\-config install \\-\\-symlink <path/to/wherever/gradle/puts/jar>\n.sp\n# Now you can make some changes in your code, then simply rebuild to test them!\n.sp\n$ vim src/MySourceFile.java\n.sp\n$ ./gradlew clean fat \\-x check\n.RE\n\n.sp\n\\fBLower the log level to DEBUG and run a command, then reset everything to default:\\fR\n.sp\n.RS 4\n.sp\n$ atlas\\-config log set\\-level DEBUG\n.sp\n$ atlas my\\-command\n.sp\n$ atlas\\-config log reset\n.sp\n# Now show the log settings to check that it reset\n.sp\n$ atlas\\-config log show\n.RE\n\n.sp\n\\fBSave a global preset called 'foo' for some common options, then use it:\\fR\n.sp\n.RS 4\n.sp\n$ atlas\\-config preset save\\-global foo \\-\\-foo=bar \\-\\-baz=bat\n.sp\n$ atlas \\-\\-preset foo my\\-command\n.sp\n$ atlas \\-\\-preset foo another\\-command\n.RE\n\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man7/atlas-environment.7",
    "content": ".\\\"     Title: atlas-environment\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-ENVIRONMENT\" \"7\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-environment \\-\\- Atlas Shell Tools environment variables\n\n.SH \"SYNOPSIS\"\n*\n\n.SH \"DESCRIPTION\"\n.sp\nThe following are all environment variables utilized in some way by\n\\fBatlas\\-shell\\-tools\\fR(7).\n.sp\n\n\\fBATLAS_SHELL_TOOLS_EDITOR\\fR\n.sp\n.RS 4\nUse the specified editor when performing tasks that require an editor. This value\nmust either be a full path to an editor executable or an executable name that is\nfound on the system path. \\fBatlas\\-shell\\-tools\\fR(7) will split the value of\nthis variable on whitespace to create ARGV, thus you can include editor flags\nand options in the standard way.\n\n\\fBatlas\\-shell\\-tools\\fR(7) respects this variable first. If it is not set, it\nwill try the system\\-global \\fBEDITOR\\fR variable. If that is also not set, it will\nuse a default editor.\n.RE\n.sp\n\n\\fBATLAS_SHELL_TOOLS_HOME\\fR\n.sp\n.RS 4\nThis variable should point to the location of the current \\fBatlas\\-shell\\-tools\\fR(7)\ninstallation. Provided you installed \\fBatlas\\-shell\\-tools\\fR(7) in the usual way\n(using the setup scripts), this variable should be automatically set for you in\nyour shell startup file.\n\nYou can temporarily switch to an alternate installation (e.g. for debugging\npurposes) by resetting this variable. You can also specify a new data directory\nby resetting \\fBXDG_DATA_HOME\\fR.\n.RE\n.sp\n\n\\fBATLAS_SHELL_TOOLS_NO_COLOR\\fR\n.sp\n.RS 4\nDisable special formatted output for \\fBatlas\\-shell\\-tools\\fR(7) only. See info\non \\fBNO_COLOR\\fR for contrast.\n.RE\n.sp\n\n\\fBATLAS_SHELL_TOOLS_PAGER\\fR\n.sp\n.RS 4\nUse the specified pager when performing tasks that require a pager. This value\nmust either be a full path to a pager executable or an executable name that is\nfound on the system path. \\fBatlas\\-shell\\-tools\\fR(7) will split the value of\nthis variable on whitespace to create ARGV, thus you can include pager flags\nand options in the standard way.\n\n\\fBatlas\\-shell\\-tools\\fR(7) respects this variable first. If it is not set, it\nwill try the system\\-global \\fBPAGER\\fR variable. If that is also not set, it will\nuse a default pager.\n.RE\n.sp\n\n\\fBATLAS_SHELL_TOOLS_USE_COLOR\\fR\n.sp\n.RS 4\nEnable special formatted output. Overrides the settings of \\fBNO_COLOR\\fR and\n\\fBATLAS_SHELL_TOOLS_NO_COLOR\\fR.\n.RE\n.sp\n\n\\fBEDITOR\\fR\n.sp\n.RS 4\nUse the specified editor when performing tasks that require an editor. This value\nmust either be a full path to an editor executable or an executable name that is\nfound on the system path. \\fBatlas\\-shell\\-tools\\fR(7) will split the value of\nthis variable on whitespace to create ARGV, thus you can include editor flags\nand options in the standard way.\n\n\\fBatlas\\-shell\\-tools\\fR(7) respects the system's \\fBEDITOR\\fR variable in the\nevent that \\fBATLAS_SHELL_TOOLS_EDITOR\\fR is not set. If neither of these are\nset, it will use a default editor.\n.RE\n.sp\n\n\\fBNO_COLOR\\fR\n.sp\n.RS 4\nDisable all special formatted output. \\fBatlas\\-shell\\-tools\\fR(7) respects this\nvariable, as do other popular CLI tools. See <https://no\\-color.org>.\n.RE\n.sp\n\n\\fBPAGER\\fR\n.sp\n.RS 4\nUse the specified pager when performing tasks that require a pager. This value\nmust either be a full path to a pager executable or an executable name that is\nfound on the system path. \\fBatlas\\-shell\\-tools\\fR(7) will split the value of\nthis variable on whitespace to create ARGV, thus you can include pager flags\nand options in the standard way.\n\n\\fBatlas\\-shell\\-tools\\fR(7) respects the system's \\fBPAGER\\fR variable in the\nevent that \\fBATLAS_SHELL_TOOLS_PAGER\\fR is not set. If neither of these are\nset, it will use a default pager.\n.RE\n.sp\n\n\\fBXDG_DATA_HOME\\fR\n.sp\n.RS 4\n\\fBatlas\\-shell\\-tools\\fR(7) stores program data in compliance with the XDG Base Directory\nspecification, i.e. at $HOME/.local/share/atlas\\-shell\\-tools. Accordingly, it respects the\n\\fBXDG_DATA_HOME\\fR environment variable \\- if set, \\fBatlas\\-shell\\-tools\\fR(7) will store program\ndata at the base path specified by that variable. See \\fBatlas\\-plumbing\\fR(5) for\nmore information.\n.RE\n.sp\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man7/atlas-glossary.7",
    "content": ".\\\"     Title: atlas-glossary\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-GLOSSARY\" \"7\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-glossary \\-\\- A glossary of Atlas Shell Tools terms and concepts\n\n.SH \"SYNOPSIS\"\n*\n\n.SH \"DESCRIPTION\"\n\n.sp\n\\fBactive module index\\fR\n.RS 4\nThe active module index is a file containing metadata about\nthe commands in the presently activated module. The metadata includes the command names\nas well as the full classnames of the command implementations. A new index is generated\neach time a new module is activated. The index can also be regenerated with\n\\fBatlas-config-sync\\fR(1). See \\fBatlas\\-plumbing\\fR(5) for info on where the index\nis stored.\n.RE\n\n.sp\n\\fBARGV\\fR\n.RS 4\nThroughout the Atlas Shell Tools manual, ARGV refers to the argument vector from the\ncommand line. In the context of \\fBatlas\\fR(1), it generally refers to the\nactual arguments passed to the command. \\fBatlas\\fR(1) converts your command line\nlike \"$ atlas \\-\\-globalOpt MyCommand arg1 \\-\\-opt1\" into a JVM call, so here ARGV refers\nto just [\"arg1\", \"\\-\\-opt1\"], the actual arguments given to the JVM. In the aforementioned\nexample, '\\-\\-globalOpt' is referred to as a \"global\" option.\n\nSee \\fBoption\\fR.\n.RE\n\n.sp\n\\fBcommand\\fR\n.RS 4\nA command generally refers to a Java class that implements an Atlas Shell Tools\ncommand. In order to implement an Atlas Shell Tools command, the class must be\na subclass of \\fBAbstractAtlasShellToolsCommand\\fR, found in the Atlas project:\nsee <https://github.com/osmlab/atlas>.\n.RE\n\n.sp\n\\fBmodule\\fR\n.RS 4\nAtlas Shell Tools lingo for a JARfile that contains implementations of commands\n(see \"command\" in this glossary). Once a JARfile has been installed into\nAtlas Shell Tools using \\fBatlas\\-config\\fR(1), it is known as a module and\nis referred to using its module name, as reported by \\fBatlas\\-config\\-list\\fR(1).\n.sp\nSee \\fBmodule workspace\\fR.\n.RE\n\n.sp\n\\fBmodule workspace\\fR\n.RS 4\nThe module workspace is where \\fBatlas\\-shell\\-tools\\fR(7) stores installed modules.\nThe workspace can be displayed with \\fBatlas\\-config\\-list\\fR(1). More info about\nthe workspace structure is detailed in \\fBatlas\\-plumbing\\fR(5).\n.sp\nSee \\fBmodule\\fR.\n.RE\n\n.sp\n\\fBoption\\fR\n.RS 4\nAn option refers to a special command line argument prefixed with a \"\\-\"\n(a.k.a. a short option) or with a \"\\-\\-\" (a.k.a. a long option). Options can be\neither global (i.e. they come before the subcommand on the command line), or they\ncan be tied to the subcommand itself. For example, in the command\n\"atlas \\-\\-opt1 MyCommand \\-\\-opt2\", \"\\-\\-opt1\" is a global option whereas \"\\-\\-opt2\" is an\noption of the MyCommand subcommand. Options can also\ntake their own additional optional or required arguments. For more details on\nthe intricacies of option syntax, see \\fBatlas\\-cli\\fR(7).\n.sp\nFinally it should be noted that the option prefix dashes without attached options have special\nmeanings. \"\\-\" on its own is treated as a regular argument, and \"\\-\\-\" on its own is treated as\nthe end\\-of\\-options marker. Again, see \\fBatlas\\-cli\\fR(7) for more information.\n.RE\n\n.sp\n\\fBpreset\\fR\n.RS 4\nA preset is a list of saved options for a given command. Presets have a name,\nbut they are meaningless without their associated command context \\- i.e. the\npreset \"p1\" is not meaningful unless you say the preset \"p1\" for command\n\"MyCommand\". Technically speaking, presets are additionally qualified by a\nnamespace, but this can be effectively ignored unless that behavior is desired.\nPresets and their namespaces are manipulated using the \\fBatlas\\fR(1) and \\fBatlas\\-config\\fR(1) programs.\nSee \\fBatlas\\-presets\\fR(7) for more information.\n.RE\n\n.sp\n\\fBrepo\\fR\n.RS 4\nA repo is a remote \\fBgit\\fR(1) repository containing Atlas Shell Tools command implementations.\nThe repo management interface is exposed by \\fBatlas\\-config\\-repo\\fR(1), and repo installation\nis performed using the \\fBinstall\\fR directive of \\fBatlas\\-config\\-repo\\fR(1). The repo object\nitself simply consists of some metadata \\- specifically a remote URL, a ref\nname (could be a branch, commit, tag, etc.), and optional Gradle configuration data.\nMore info on repo objects can be found in \\fBatlas\\-plumbing\\fR(5).\n.RE\n\n.sp\n\\fBtoolkit\\fR\n.RS 4\nThroughout the Atlas Shell Tools documentation, toolkit generally refers to the\n\\fBatlas\\fR(1) and \\fBatlas\\-config\\fR(1) programs, the manpages included\nin the \\fBatlas\\-shell\\-tools\\fR(7) suite, and anything else stored in the directory pointed to\nby the \\fBATLAS_SHELL_TOOLS_HOME\\fR environment variable. The toolkit does \\fBnot\\fR refer to\ninstalled modules, repos, or any saved presets. Those are stored in the toolkit's data directory.\nSee \\fBatlas\\-plumbing\\fR(5) for more on the data directory.\n.RE\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man7/atlas-presets.7",
    "content": ".\\\"     Title: atlas-presets\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-PRESETS\" \"7\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-presets \\-\\- Atlas Shell Tools preset interface\n\n.SH \"SYNOPSIS\"\n*\n\n.SH \"DESCRIPTION\"\n.sp\nAtlas Shell Tools features a robust preset management system. Presets provide a way to save\ncommand options (and long option arguments) for future application or modification.\nFor more information about how Atlas Shell Tools saves and manipulates presets under\nthe hood, see \\fBatlas\\-plumbing\\fR(5).\n.sp\nThe preset interface is divided into 3 tiers. Each tier supports an increasing\nnumber of potential use cases at the cost of added complexity for the user. The\ntiers are designed such that you can stop at any tier you feel comfortable using\n\\- without needing to understand how the next tier up works. Each tier builds\non concepts introduced in the previous tiers.\n\nThe three interface tiers are:\n.sp\n.RS 4\n\\fBTier 1)\\fR Basic preset creation and use with \\fBatlas\\fR(1) options \\fB\\-\\-save\\-preset\\fR and \\fB\\-\\-preset\\fR.\n\n.RS 8\n\\fBGlobal Presets)\\fR A brief addendum to Tier 1 about global preset saving.\n.RE\n\n\\fBTier 2)\\fR More precise preset management with \\fBatlas-config-preset\\fR(1), including editing, copying, etc.\n\n\\fBTier 3)\\fR Preset namespace management with the \\fBatlas-config-preset\\fR(1) \\fBnamespace\\fR directive.\n.RE\n\n.SH \"TIER 1\"\nTier 1 provides a simple, lightweight interface for preset usage through the \\fBatlas\\fR(1) options\n\\fB\\-\\-preset\\fR=\\fInames\\fR and \\fB\\-\\-save\\-preset\\fR=\\fInew\\-name\\fR, where\n\\fInames\\fR are the name(s) of the preset(s) you would like to apply and \\fInew\\-name\\fR is\nthe name of the preset you would like to create.\n.sp\nWhen running a command with the \\fB\\-\\-preset\\fR=\\fInames\\fR option,\n\\fBatlas\\fR(1) checks the list of saved presets associated with that command.\nIf one of those presets matches with one of the given \\fInames\\fR, \\fBatlas\\fR(1) will apply that\npreset. It will perform this check and application for each preset provided in \\fInames\\fR (which should\nbe comma or colon separated). If one of the given \\fInames\\fR does not refer to a saved preset,\n\\fBatlas\\fR(1) displays the list of presets and exits with an error.\n.sp\nWhen running a command with the \\fB\\-\\-save\\-preset\\fR=\\fInew\\-name\\fR option,\n\\fBatlas\\fR attempts to save the current ARGV to a new preset called \\fInew\\-name\\fR.\nIf \\fInew\\-name\\fR already exists, \\fBatlas\\fR(1) will exit with an error.\n.sp\nIf both \\fB\\-\\-preset\\fR=\\fInames\\fR and \\fB\\-\\-save\\-preset\\fR=\\fInew\\-name\\fR\nare applied at the same time, \\fBatlas\\fR(1) will attempt to apply \\fInames\\fR before\nsaving \\fInew\\-name\\fR. This allows you to easily save new presets that iterate\non previously created presets.\n.sp\nNote that preset names are bound to the target command at save\\-time \\- \na preset name without its command context is meaningless. This means that preset\n\"p1\" for \"ExampleCommand\" and preset \"p1\" for \"MyCommand\" may contain\ncompletely different values.\n.sp\nLet's look at an example of the Tier 1 interface in action.\nWe will run a command called \"MyCommand\" and assume preset \"p1\" does not yet exist:\n.sp\n.RS 4\n$ atlas \\-\\-save\\-preset=p1 MyCommand arg1 arg2 \\-\\-opt1 \\-\\-opt2=opt2Arg\n.RE\n.sp\nThis will save a preset \"p1\" for \"MyCommand\" with contents [\"\\-\\-opt1\", \"\\-\\-opt2=opt2Arg\"]\nand then run the command. Note that the preset engine only saves options, and so will\nautomatically discard any ARGV element that does not look like an option. For this reason,\nyou \\fImust\\fR use the long option '=' syntax for specifying option arguments when saving\na preset (e.g. '--opt=arg' and \\fInot\\fR '--opt arg').\n.sp\nNow that you have saved preset \"p1\" for \"MyCommand\", you can apply it like:\n.sp\n.RS 4\n$ atlas \\-\\-preset=p1 MyCommand arg1 arg2\n.RE\n.sp\nThis will run the exact same command as before, but it saves us from having\nto type out \"\\-\\-opt1\" and \"\\-\\-opt2=opt2Arg\". In addition to simply applying\na preset verbatim, you can also override a saved option by specifying it again\non the command line:\n.sp\n.RS 4\n$ atlas \\-\\-preset=p1 MyCommand arg1 arg2 \\-\\-opt2=OverrideOpt2Arg\n.RE\n.sp\nThis will run the same command as the above 2 examples, except it will override\nthe preset value of \\-\\-opt2 with your new value \"OverrideOpt2Arg\".\n.sp\nYou can extend presets by applying and saving at the same time. Here,\nwe apply our preset \"p1\" while also saving a new preset called \"p2\", based off\nthe contents of \"p1\":\n.sp\n.RS 4\n$ atlas \\-\\-preset=p1 \\-\\-save\\-preset=p2 MyCommand arg1 arg2 --opt2=OverrideOpt2Arg --opt3\n.RE\n.sp\nThe new preset \"p2\" will contain the following contents:\n[\"\\-\\-opt1\", \"\\-\\-opt2=opt2Arg\", \"\\-\\-opt2=OverrideOpt2Arg\", \"\\-\\-opt3\"]. Even though\n\\-\\-opt2 is repeated, this is OK. When multiple instances of the same option are\nsupplied, the option parser will use ARGV's rightmost instance of that option.\n.sp\nLet's look at one final example where we apply two presets at once. Assuming you now have\na preset \"p3\" with contents: [\"\\-\\-opt3\"], you could run:\n.sp\n.RS 4\n$ atlas \\-\\-preset=p1,p3 MyCommand\n.RE\n.sp\nThis would run MyCommand with [\"\\-\\-opt1\", \"\\-\\-opt2=opt2Arg\", \"\\-\\-opt3\"] as the effective ARGV.\nNote that the rightmost ARGV selection strategy for duplicate options applies here as well. Keep this in\nmind when applying multiple presets that contain the same option.\n\n.SS \"Global Presets\"\nIn addition to the presets tied to each command, you can also save global presets\nusing the \\fBatlas\\fR(1) option \\fB\\-\\-save\\-global\\-preset\\fR=\\fInew\\-name\\fR. Global presets\ncan be applied to any command using the \\fB\\-\\-preset\\fR=\\fInames\\fR option.\nWhen checking the \\fInames\\fR given to \\fB\\-\\-preset\\fR, the \\fBatlas\\fR(1) preset system will always attempt\nto use global presets as a fall back. This means, for example, that if you have a preset \"p1\" for \"MyCommand\" as well\nas global presets \"p1\" and \"p2\", supplying \\fB\\-\\-preset=p1,p2\\fR when running MyCommand will use the \"p1\" associated with \"MyCommand\",\nbut it will use the global preset \"p2\".\n.sp\nFor the following examples, let's assume we have a command \"MyCommand\" with a preset \"p1\". We also have a command\n\"AnotherCommand\" with no presets. Finally we have global presets \"p1\" and \"p2\".\n.sp\nThe following will run \"MyCommand\" with its version of \"p1\" but with global \"p2\":\n.sp\n.RS 4\n$ atlas \\-\\-preset=p1,p2 MyCommand\n.RE\n.sp\nThis next command will run \"AnotherCommand\" with globals \"p1\" and \"p2\":\n.sp\n.RS 4\n$ atlas \\-\\-preset=p1,p2 AnotherCommand\n.RE\n.sp\nFinally, this last command will fail since there is no preset \"p3\" associated with \"MyCommand\", nor is\nthere a global definition of \"p3\":\n.sp\n.RS 4\n$ atlas \\-\\-preset=p1,p2,p3 MyCommand\n.RE\n.sp\n\n\n.SH \"TIER 2\"\nTier 2 provides more precise preset management using the \\fBatlas\\-config\\fR(1)\nsubcommand \\fBpreset\\fR. \\fBatlas\\-config\\-preset\\fR(1) takes a mandatory \\fIdirective\\fR,\nwhich is just a verb specifying a configuration action. Note that many of the\ndirectives require additional command context (recall that preset names are meaningless without an associated command).\nBelow are the available directives:\n\n.sp\n.RS 4\n\\fBcopy\\fR \\fIcommand\\fR <source> <destination>\n.RS 4\nCopy \\fIcommand\\fR preset <source> into new preset <destination>.\n<destination> must not already exist, else the copy will fail. The following example\ncopies the preset \"p1\" into new preset \"p2\", for command \"MyCommand\":\n.sp\n.RS 4\n$ atlas\\-config preset copy MyCommand p1 p2\n.RE\n.sp\nThe \\fBcopy\\fR directive is useful in combination with the \\fBedit\\fR directive \\-\nwhen you want to make multiple versions of a long preset, each with some minor differences.\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBcopy\\-global\\fR <source> <destination>\n.RS 4\nCopy global preset <source> into new preset <destination>.\n<destination> must not already exist, else the copy will fail. The following example\ncopies the global preset \"p1\" into new global preset \"p2\":\n.sp\n.RS 4\n$ atlas\\-config preset copy\\-global p1 p2\n.RE\n.sp\nLike the \\fBcopy\\fR directive, the \\fBcopy\\-global\\fR is useful in combination\nwith the \\fBedit\\-global\\fR directive for quick preset iteration.\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBedit\\fR \\fIcommand\\fR <name>\n.RS 4\nEdit preset <name> for \\fIcommand\\fR. If <name> does not exist, then it will be\ncreated when the edit is successfully saved. The default preset editor is \\fBvim\\fR,\nbut this can be changed by setting the \\fBATLAS_SHELL_TOOLS_EDITOR\\fR environment variable.\nThe following example will edit preset \"p1\" for command \"MyCommand\":\n.sp\n.RS 4\n$ atlas\\-config preset edit MyCommand p1\n.RE\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBedit\\-global\\fR <name>\n.RS 4\nEdit global preset <name>. If <name> does not exist, then it will be\ncreated when the edit is successfully saved. The default preset editor is \\fBvim\\fR,\nbut this can be changed by setting the \\fBATLAS_SHELL_TOOLS_EDITOR\\fR environment variable.\nThe following example will edit global preset \"p1\":\n.sp\n.RS 4\n$ atlas\\-config preset edit\\-global p1\n.RE\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBlist\\fR [\\fIcommand\\fR [name]]\n.RS 4\nList all available presets (including globals), or list all presets for a given [\\fIcommand\\fR], or\nlist contents of preset [name] for [\\fIcommand\\fR]. The following example lists\nall available presets, then lists all presets for \"MyCommand\", and finally lists\nthe contents of preset \"p1\" for command \"MyCommand\":\n.sp\n.RS 4\n$ atlas\\-config preset list\n\n$ atlas\\-config preset list MyCommand\n\n$ atlas\\-config preset list MyCommand p1\n.RE\n.sp\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBlist\\-global\\fR [name]\n.RS 4\nList all available global presets, or list contents of global preset [name]. The\nfollowing example lists all available global presets, then lists the contents of\nglobal preset \"p1\":\n.sp\n.RS 4\n$ atlas\\-config preset list\\-global\n\n$ atlas\\-config preset list\\-global p1\n.RE\n.sp\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBnamespace\\fR <subdirective> [namespace]\n.RS 4\nExecute a <subdirective> on a given preset [namespace]. Available subdirectives\nare \\fBlist\\fR, \\fBuse\\fR, \\fBcreate\\fR, and \\fBremove\\fR. Preset namespaces \\-\nand the \\fBnamespace\\fR directive \\- are explained in more detail in the\n\\fBTIER 3\\fR section found below.\n.RE\n.RE\n.sp\n\n.sp\n.RS 4\n\\fBremove\\fR \\fIcommand\\fR [name]\n.RS 4\nRemove all presets for a given \\fIcommand\\fR, or remove the preset [name] for\n\\fIcommand\\fR. The following example removes all presets for command \"MyCommand\",\nthen removes preset \"p1\" for command \"AnotherCommand\":\n.sp\n.RS 4\n$ atlas\\-config preset remove MyCommand\n\n$ atlas\\-config preset remove AnotherCommand p1\n.RE\n.sp\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBremove\\-global\\fR [name]\n.RS 4\nRemove all global presets , or remove the global preset [name].\nThe following example removes all global presets, then removes globa preset \"p1\":\n.sp\n.RS 4\n$ atlas\\-config preset remove\\-global\n\n$ atlas\\-config preset remove\\-global p1\n.RE\n.sp\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBsave\\fR \\fIcommand\\fR <name> <options...>\n.RS 4\nSave a preset <name> for \\fIcommand\\fR without actually running the command.\n<options...> is a sequence of options to be saved in the preset.\nAgain, recall that you must use the long option '=' syntax for specifying option\narguments when saving a preset (e.g. '--opt=arg' and \\fInot\\fR '--opt arg').\nThe following example saves preset \"p1\" to command \"MyCommand\" with some options\n--opt1 and --opt2=opt2Arg:\n.sp\n.RS 4\n$ atlas\\-config preset save MyCommand p1 --opt1 --opt2=opt2Arg\n.RE\n.sp\n.RE\n.RE\n\n.sp\n.RS 4\n\\fBsave\\-global\\fR <name> <options...>\n.RS 4\nSave a global preset <name>. <options...> is a sequence of options to be savedin the preset.\nAgain, recall that you must use the long option '=' syntax for specifying option\narguments when saving a preset (e.g. '--opt=arg' and \\fInot\\fR '--opt arg').\nThe following example saves global preset \"p1\" with some options\n--opt1 and --opt2=opt2Arg:\n.sp\n.RS 4\n$ atlas\\-config preset save\\-global p1 --opt1 --opt2=opt2Arg\n.RE\n.sp\n.RE\n.RE\n\n.SH \"TIER 3\"\nTier 3 provides preset namespaces. A namespace creates an enclosing\nscope for the presets associated with each command. For example, preset\n\"p1\" for command \"MyCommand\" under \"namespace1\" and preset \"p1\" for command\n\"MyCommand\" under \"namespace2\" may have completely different contents. Up to\nthis point, we have been working under the default namespace, appropriately\ncalled \"default\". You can create and manage namespaces using the\n\\fBatlas\\-config\\-preset\\fR(1) \\fBnamespace\\fR directive, which takes a subdirective to denote\nthe desired action. The available subdirectives are below:\n.sp\n\n.RS 4\n\\fBcreate\\fR <namespace>\n.RS 4\nCreate a new <namespace>, throwing an error if <namespace> already exists. This\nwill not actually switch to the new namespace. The following example creates a\nnamespace called \"namespace1\":\n.sp\n.RS 4\n$ atlas\\-config preset namespace create namespace1\n.RE\n.RE\n.RE\n\n.RS 4\n\\fBlist\\fR [namespace]\n.RS 4\nList all namespaces while highlighting the current namespace with a \"*\", or list the contents of namespace [namespace].\nThe following example lists all namespaces, then lists the contents of namespace \"namespace1\".\n.sp\n.RS 4\n$ atlas\\-config preset namespace list\n\n$ atlas\\-config preset namespace list namespace1\n.RE\n.RE\n.RE\n\n.RS 4\n\\fBremove\\fR <namespace>\n.RS 4\nDelete a <namespace>, including all associated presets. The \\fBremove\\fR will\nfail if <namespace> does not exist, if <namespace> is currently in-use, or if\n<namespace> is the default namespace. The following example removes a namespace\ncalled \"namespace1\":\n.sp\n.RS 4\n$ atlas\\-config preset namespace remove namespace1\n.RE\n.RE\n.RE\n\n.RS 4\n\\fBuse\\fR <namespace>\n.RS 4\nSwitch to <namespace>, throwing an error if <namespace> does not exist. Any new\npresets you create will now be saved under <namespace>, and presets you apply\nwill be sourced from <namespace>. The following example switches to a namespace\ncalled \"namespace1\":\n.sp\n.RS 4\n$ atlas\\-config preset namespace use namespace1\n.RE\n.RE\n.RE\n\n.sp\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/man/man7/atlas-shell-tools.7",
    "content": ".\\\"     Title: atlas-shell-tools\n.\\\"    Author: Lucas Cram\n.\\\"    Source: atlas-shell-tools 1.0.0\n.\\\"  Language: English\n.\\\"\n.TH \"ATLAS-SHELL-TOOLS\" \"7\" \"28 September 2020\" \"atlas\\-shell\\-tools 1\\&.0\\&.0\" \"Atlas Shell Tools Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n\n.SH \"NAME\"\n.sp\natlas\\-shell\\-tools \\-\\- A command line toolkit for Atlas and related projects\n\n.SH \"SYNOPSIS\"\n*\n\n.SH \"DESCRIPTION\"\nThe Atlas Shell Tools are a command line toolkit for running commands defined\nin Atlas and related projects. The main tool for running commands is\n\\fBatlas\\fR(1), and the installation can be configured with \\fBatlas\\-config\\fR(1)\nand its various subcommands.\n\n.SH \"MANUAL PAGES\"\n.RS 4\n\n\\fBatlas\\fR(1)\n.RS 4\nRun an Atlas Shell Tools command\n.RE\n\n\\fBatlas\\-config\\fR(1)\n.RS 4\nConfigure the Atlas Shell Tools installation\n.RE\n\n\\fBatlas\\-config\\-activate\\fR(1)\n.RS 4\nActivate an installed module\n.RE\n\n\\fBatlas\\-config\\-deactivate\\fR(1)\n.RS 4\nDeactivate an installed module\n.RE\n\n\\fBatlas\\-config\\-install\\fR(1)\n.RS 4\nInstall a new Atlas Shell Tools module from a local JAR\n.RE\n\n\\fBatlas\\-config\\-list\\fR(1)\n.RS 4\nList installed modules and module statuses\n.RE\n\n\\fBatlas\\-config\\-log\\fR(1)\n.RS 4\nCheck or set log parameters\n.RE\n\n\\fBatlas\\-config\\-preset\\fR(1)\n.RS 4\nCreate and manage Atlas Shell Tools command presets\n.RE\n\n\\fBatlas\\-config\\-repo\\fR(1)\n.RS 4\nRegister and manage Atlas Shell Tools repositories\n.RE\n\n\\fBatlas\\-config\\-reset\\fR(1)\n.RS 4\nReset the installation\n.RE\n\n\\fBatlas\\-config\\-sync\\fR(1)\n.RS 4\nRefresh the installation based on the active module\n.RE\n\n\\fBatlas\\-config\\-uninstall\\fR(1)\n.RS 4\nUninstall Atlas Shell Tools module(s)\n.RE\n\n\\fBatlas\\-config\\-update\\fR(1)\n.RS 4\nUpdate the Atlas Shell Tools core toolkit\n.RE\n\n\\fBatlas\\-plumbing\\fR(5)\n.RS 4\nDetails about how Atlas Shell Tools stores toolkit program data\n.RE\n\n\\fBatlas\\-cli\\fR(7)\n.RS 4\nAtlas Shell Tools command line interface and conventions\n.RE\n\n\\fBatlas\\-cookbook\\fR(7)\n.RS 4\nAtlas Shell Tools command cookbook\n.RE\n\n\\fBatlas\\-environment\\fR(7)\n.RS 4\nAtlas Shell Tools environment variables\n.RE\n\n\\fBatlas\\-glossary\\fR(7)\n.RS 4\nA glossary of Atlas Shell Tools terms and concepts\n.RE\n\n\\fBatlas\\-presets\\fR(7)\n.RS 4\nAtlas Shell Tools preset interface\n.RE\n\n.RE\n\n\n.SH \"ATLAS SHELL TOOLS\"\n.sp\nPart of the \\fBatlas\\-shell\\-tools\\fR(7) suite\n"
  },
  {
    "path": "atlas-shell-tools/quick_install_bash.sh",
    "content": "#!/bin/sh\n\n# Define a prompt function for re-use\nprompt_yn_was_yes() {\n    prompt=$1\n    while true;\n    do\n        echo \"$prompt\"\n        # handle EOF case\n        if ! read -r answer;\n        then\n            exit 0\n        fi\n        if [ \"$answer\" != \"${answer#[Yy]}\" ];\n        then\n            # case user entered 'y'\n            return 0\n        elif [ \"$answer\" != \"${answer#[Nn]}\" ];\n        then\n            # case user entered 'n'\n            return 1\n        fi\n        echo \"Please enter 'y' or 'n'...\"\n    done\n}\n\n# Utilize POSIX sh features ONLY for installation. Also, we exit the script\n# on any error.\nset -e\n\n# Grab the install location from first argument if present.\ninstall_location=$1\n\n# Verify that you have programs needed by atlas-shell-tools\nif ! command -v less > /dev/null;\nthen\n    echo \"Error: atlas-shell-tools requires the 'less' paging program.\"\n    # Unfortunately, we cannot use $LINENO in POSIX sh. Make sure to manually\n    # maintain this line number.\n    echo \"To install anyway, open $0 and comment out check on lines 35-42.\"\n    exit 1\nfi\n\nif ! command -v man > /dev/null;\nthen\n    echo \"Error: atlas-shell-tools requires the 'man' program.\"\n    # Unfortunately, we cannot use $LINENO in POSIX sh. Make sure to manually\n    # maintain this line number.\n    echo \"To install anyway, open $0 and comment out check on lines 44-51.\"\n    exit 1\nfi\n\nif ! command -v vim > /dev/null;\nthen\n    echo \"Error: atlas-shell-tools requires the 'vim' editor.\"\n    # Unfortunately, we cannot use $LINENO in POSIX sh. Make sure to manually\n    # maintain this line number.\n    echo \"To install anyway, open $0 and comment out check on lines 53-60.\"\n    exit 1\nfi\n\n# Check the install location. If blank, we will use $HOME as default after\n# prompting the user for confirmation.\nif [ -z \"$install_location\" ];\nthen\n    echo \"Using the value of \\$HOME ($HOME) as install location.\"\n    echo \"If an alternative location is desired, select 'n' and try:\"\n    echo\n    echo \"    \\$ sh quick_install_bash.sh /install/path\"\n    echo\n    if prompt_yn_was_yes \"OK to continue (y/n)?\";\n    then\n        install_location=\"$HOME\"\n    else\n        exit 0\n    fi\nfi\n\nif [ ! -d \"$install_location\" ];\nthen\n    echo \"Error: $install_location does not exist\"\n    exit 1\nfi\n\nif [ ! -w \"$install_location\" ];\nthen\n    echo \"Error: you do not have write permissions for $install_location\"\n    exit 1\nfi\n\nbase_folder=\"atlas-shell-tools\"\nfull_installation_path=\"$install_location/$base_folder\"\n\nif [ -e \"$full_installation_path\" ];\nthen\n    echo \"Error: $full_installation_path already exists\"\n    exit 1\nfi\n\ngit clone https://github.com/osmlab/atlas.git \"$full_installation_path\"\ncd \"$full_installation_path\"\ngit checkout main\nchmod +x atlas-shell-tools/scripts/atlas atlas-shell-tools/scripts/atlas-config\nexport ATLAS_SHELL_TOOLS_HOME=\"$full_installation_path/atlas-shell-tools\"\nexport PATH=\"$PATH:$ATLAS_SHELL_TOOLS_HOME/scripts\"\n\n# Install the core Atlas module using a repo\n./atlas-shell-tools/scripts/atlas-config repo add atlas https://github.com/osmlab/atlas.git main\n./atlas-shell-tools/scripts/atlas-config repo install atlas\n\n# Modify the bash startup files with appropriate settings\nstart_startup_line=\"# atlas-shell-tools startup: added automatically by quick_install_bash.sh\"\nend_startup_line=\"# END atlas-shell-tools startup\"\nexport_home_line=\"export ATLAS_SHELL_TOOLS_HOME=\\\"$full_installation_path/atlas-shell-tools\\\"\"\nexport_path_line=\"export PATH=\\\"\\$PATH:\\$ATLAS_SHELL_TOOLS_HOME/scripts\\\"\"\nsource_line=\"source \\\"\\$ATLAS_SHELL_TOOLS_HOME/ast_completions.bash\\\"\"\n\necho\necho \"About to append ~/.bash_profile with:\"\necho\necho \"    $start_startup_line\";\necho \"    $export_home_line\";\necho \"    $export_path_line\";\necho \"    $source_line\";\necho \"    $end_startup_line\";\necho\nif prompt_yn_was_yes \"Is this OK (y/n)?\";\nthen\n    {\n        echo;\n        echo \"$start_startup_line\";\n        echo \"$export_home_line\";\n        echo \"$export_path_line\";\n        echo \"$source_line\";\n        echo \"$end_startup_line\";\n        echo;\n    } >> \"$HOME/.bash_profile\"\nfi\necho\necho \"About to append ~/.bashrc with:\"\necho\necho \"    $start_startup_line\";\necho \"    $source_line\";\necho \"    $end_startup_line\";\necho\nif prompt_yn_was_yes \"Is this OK (y/n)?\";\nthen\n    {\n        echo;\n        echo \"$start_startup_line\";\n        echo \"$source_line\";\n        echo \"$end_startup_line\";\n        echo;\n    } >> \"$HOME/.bashrc\"\nfi\n\n# Complete the installation\necho\necho \"Installation complete.\"\necho \"Restart your terminal and try 'man atlas-shell-tools' to get started.\"\n"
  },
  {
    "path": "atlas-shell-tools/quick_install_zsh.sh",
    "content": "#!/bin/sh\n\n# Define a prompt function for re-use\nprompt_yn_was_yes() {\n    prompt=$1\n    while true;\n    do\n        echo \"$prompt\"\n        # handle EOF case\n        if ! read -r answer;\n        then\n            exit 0\n        fi\n        if [ \"$answer\" != \"${answer#[Yy]}\" ];\n        then\n            # case user entered 'y'\n            return 0\n        elif [ \"$answer\" != \"${answer#[Nn]}\" ];\n        then\n            # case user entered 'n'\n            return 1\n        fi\n        echo \"Please enter 'y' or 'n'...\"\n    done\n}\n\n# Utilize POSIX sh features ONLY for installation. Also, we exit the script\n# on any error.\nset -e\n\n# Grab the install location from first argument if present.\ninstall_location=$1\n\n# Verify that you have programs needed by atlas-shell-tools\nif ! command -v less > /dev/null;\nthen\n    echo \"Error: atlas-shell-tools requires the 'less' paging program.\"\n    # Unfortunately, we cannot use $LINENO in POSIX sh. Make sure to manually\n    # maintain this line number.\n    echo \"To install anyway, open $0 and comment out check on lines 35-42.\"\n    exit 1\nfi\n\nif ! command -v man > /dev/null;\nthen\n    echo \"Error: atlas-shell-tools requires the 'man' program.\"\n    # Unfortunately, we cannot use $LINENO in POSIX sh. Make sure to manually\n    # maintain this line number.\n    echo \"To install anyway, open $0 and comment out check on lines 44-51.\"\n    exit 1\nfi\n\nif ! command -v vim > /dev/null;\nthen\n    echo \"Error: atlas-shell-tools requires the 'vim' editor.\"\n    # Unfortunately, we cannot use $LINENO in POSIX sh. Make sure to manually\n    # maintain this line number.\n    echo \"To install anyway, open $0 and comment out check on lines 53-60.\"\n    exit 1\nfi\n\n# Check the install location. If blank, we will use $HOME as default after\n# prompting the user for confirmation.\nif [ -z \"$install_location\" ];\nthen\n    echo \"Using the value of \\$HOME ($HOME) as install location.\"\n    echo \"If an alternative location is desired, select 'n' and try:\"\n    echo\n    echo \"    \\$ sh quick_install_bash.sh /install/path\"\n    echo\n    if prompt_yn_was_yes \"OK to continue (y/n)?\";\n    then\n        install_location=\"$HOME\"\n    else\n        exit 0\n    fi\nfi\n\nif [ ! -d \"$install_location\" ];\nthen\n    echo \"Error: $install_location does not exist\"\n    exit 1\nfi\n\nif [ ! -w \"$install_location\" ];\nthen\n    echo \"Error: you do not have write permissions for $install_location\"\n    exit 1\nfi\n\nbase_folder=\"atlas-shell-tools\"\nfull_installation_path=\"$install_location/$base_folder\"\n\nif [ -e \"$full_installation_path\" ];\nthen\n    echo \"Error: $full_installation_path already exists\"\n    exit 1\nfi\n\ngit clone https://github.com/osmlab/atlas.git \"$full_installation_path\"\ncd \"$full_installation_path\"\ngit checkout main\nchmod +x atlas-shell-tools/scripts/atlas atlas-shell-tools/scripts/atlas-config\nexport ATLAS_SHELL_TOOLS_HOME=\"$full_installation_path/atlas-shell-tools\"\nexport PATH=\"$PATH:$ATLAS_SHELL_TOOLS_HOME/scripts\"\n\n# Install the core Atlas module using a repo\n./atlas-shell-tools/scripts/atlas-config repo add atlas https://github.com/osmlab/atlas.git main\n./atlas-shell-tools/scripts/atlas-config repo install atlas\n\n# Modify the bash startup files with appropriate settings\nstart_startup_line=\"# atlas-shell-tools startup: added automatically by quick_install_zsh.sh\"\nend_startup_line=\"# END atlas-shell-tools startup\"\nexport_home_line=\"export ATLAS_SHELL_TOOLS_HOME=\\\"$full_installation_path/atlas-shell-tools\\\"\"\nexport_path_line=\"export PATH=\\\"\\$PATH:\\$ATLAS_SHELL_TOOLS_HOME/scripts\\\"\"\nsource_line=\"source \\\"\\$ATLAS_SHELL_TOOLS_HOME/ast_completions.zsh\\\"\"\n\necho\necho \"About to append ~/.zshenv with:\"\necho\necho \"    $start_startup_line\";\necho \"    $export_home_line\";\necho \"    $export_path_line\";\necho \"    $source_line\";\necho \"    $end_startup_line\";\necho\nif prompt_yn_was_yes \"Is this OK (y/n)?\";\nthen\n    {\n        echo;\n        echo \"$start_startup_line\";\n        echo \"$export_home_line\";\n        echo \"$export_path_line\";\n        echo \"$source_line\";\n        echo \"$end_startup_line\";\n        echo;\n    } >> \"$HOME/.zshenv\"\nfi\necho\necho \"About to append ~/.zshrc with:\"\necho\necho \"    $start_startup_line\";\necho \"    $source_line\";\necho \"    $end_startup_line\";\necho\nif prompt_yn_was_yes \"Is this OK (y/n)?\";\nthen\n    {\n        echo;\n        echo \"$start_startup_line\";\n        echo \"$source_line\";\n        echo \"$end_startup_line\";\n        echo;\n    } >> \"$HOME/.zshrc\"\nfi\n\n# Complete the installation\necho\necho \"Installation complete.\"\necho \"Restart your terminal and try 'man atlas-shell-tools' to get started.\"\n"
  },
  {
    "path": "atlas-shell-tools/scripts/atlas",
    "content": "#!/usr/bin/env perl\n\nuse warnings;\nuse strict;\n\nuse File::Spec;\nuse Getopt::Long qw(GetOptions);\nuse POSIX;\n\n# Pull in code from the common modules\nuse FindBin;\nuse lib \"$FindBin::Bin/common\";\nuse ast_log_subsystem;\nuse ast_preset_subsystem;\nuse ast_module_subsystem;\nuse ast_tty;\nuse ast_utilities;\n\n\n## ORGANIZATION\n## This script is organized into 3 sections:\n## 1) GLOBAL INITIALIZATION - initialize some useful global constants\n## 2) SUBROUTINES - subroutines used by the command logic\n## 3) EXECUTION LOGIC - the actual command logic, ie. 'main'\n\n\n########## BEGIN GLOBAL INITIALIZATION ##########\nmy $ansi_red = ast_tty::ansi_red();\nmy $ansi_green = ast_tty::ansi_green();\nmy $ansi_magenta = ast_tty::ansi_magenta();\nmy $ansi_bold = ast_tty::ansi_bold();\nmy $ansi_reset = ast_tty::ansi_reset();\nmy $ansi_bunl = ast_tty::ansi_begin_underln();\nmy $ansi_eunl = ast_tty::ansi_end_underln();\n\nmy $no_colors_stdout = ast_tty::is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : $ansi_red;\nmy $green_stdout = $no_colors_stdout ? \"\" : $ansi_green;\nmy $magenta_stdout = $no_colors_stdout ? \"\" : $ansi_magenta;\nmy $bold_stdout = $no_colors_stdout ? \"\" : $ansi_bold;\nmy $reset_stdout = $no_colors_stdout ? \"\" : $ansi_reset;\nmy $bunl_stdout = $no_colors_stdout ? \"\" : $ansi_bunl;\nmy $eunl_stdout = $no_colors_stdout ? \"\" : $ansi_eunl;\n\nmy $no_colors_stderr = ast_tty::is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : $ansi_red;\nmy $green_stderr = $no_colors_stderr ? \"\" : $ansi_green;\nmy $magenta_stderr = $no_colors_stderr ? \"\" : $ansi_magenta;\nmy $bold_stderr = $no_colors_stderr ? \"\" : $ansi_bold;\nmy $reset_stderr = $no_colors_stderr ? \"\" : $ansi_reset;\nmy $bunl_stderr = $no_colors_stderr ? \"\" : $ansi_bunl;\nmy $eunl_stderr = $no_colors_stderr ? \"\" : $ansi_eunl;\n\nmy $ast_path;\nmy $skip_paging;\nmy $quiet;\nmy $debug_flag;\n\nmy $program_name = $ast_utilities::COMMAND_PROGRAM;\nmy $program_version = \"$ast_utilities::ATLAS_SHELL_TOOLS_VERSION ($program_name program)\";\n\n########## END GLOBAL INITIALIZATION ##########\n\n\n########## BEGIN SUBROUTINES ##########\n\nsub atlas_unrecognized_command_message_and_exit {\n    my $command = shift;\n    ast_utilities::error_output($program_name, \"no such command ${bold_stderr}${command}${reset_stderr}\");\n    print STDERR \"Try '${bold_stderr}${program_name} --list${reset_stderr}' for a list of commands.\\n\";\n    print STDERR \"Try '${bold_stderr}${program_name} --help${reset_stderr}' for more information.\\n\\n\";\n\n    my %subcommand_classes = ast_module_subsystem::get_subcommand_to_class_hash($ast_path);\n    my @subcommands = keys %subcommand_classes;\n\n    # Determine the most similar command using Levenshtein distance\n    my $closest_command;\n    my $min_distance = undef;\n    foreach my $candidate_command (@subcommands) {\n        my $distance = ast_utilities::levenshtein($command, $candidate_command);\n        if (!defined $min_distance) {\n            $closest_command = $candidate_command;\n            $min_distance = $distance;\n        }\n        elsif ($distance < $min_distance) {\n            $closest_command = $candidate_command;\n            $min_distance = $distance;\n        }\n    }\n    print STDERR \"The most similar command is: ${bold_stderr}${closest_command}${reset_stderr}\\n\";\n\n    exit 127;\n}\n\nsub atlas_show_contextual_help_menu_and_exit {\n    my $context = shift;\n    my $skip_paging = shift;\n    my $ast_path = shift;\n\n    unless (defined $skip_paging) {\n        $skip_paging = 0;\n    }\n\n    my %subcommand_classes = ast_module_subsystem::get_subcommand_to_class_hash($ast_path);\n    my $subcommand_class = $subcommand_classes{$context};\n\n    unless (defined $subcommand_class) {\n        atlas_unrecognized_command_message_and_exit($context);\n    }\n\n    my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n    my @activated_modules = ast_module_subsystem::get_activated_modules(\\%modules);\n    my $module = $activated_modules[0];\n    my $full_path_to_modules_folder = File::Spec->catfile($ast_path, $ast_module_subsystem::MODULES_FOLDER, \"$module\" . $ast_module_subsystem::MODULE_SUFFIX);\n    my $full_path_to_log4j = File::Spec->catfile($ast_path, $ast_log_subsystem::LOG4J_FILE_PATH);\n\n    my @java_command = ();\n    push @java_command, 'java';\n    push @java_command, '-Xms2G';\n    push @java_command, '-Xmx2G';\n    push @java_command, '-cp';\n    push @java_command, \"${full_path_to_modules_folder}\";\n    push @java_command, \"-Dlog4j.configuration=file:${full_path_to_log4j}\";\n    push @java_command, \"${subcommand_class}\";\n    push @java_command, \"--help\";\n\n    if (ast_tty::is_no_colors_stdout()) {\n        push @java_command, \"$ast_utilities::JAVA_NO_COLOR_STDOUT\";\n    }\n    else {\n        push @java_command, \"$ast_utilities::JAVA_COLOR_STDOUT\";\n    }\n\n    if (ast_tty::is_no_colors_stderr()) {\n        push @java_command, \"$ast_utilities::JAVA_NO_COLOR_STDERR\";\n    }\n    else {\n        push @java_command, \"$ast_utilities::JAVA_COLOR_STDERR\";\n    }\n\n    push @java_command, \"$ast_utilities::JAVA_NO_USE_PAGER\";\n\n    my $terminal_width = ast_tty::terminal_width();\n    push @java_command, \"$terminal_width\";\n    push @java_command, \"$ast_utilities::JAVA_MARKER_SENTINEL\";\n\n    my @pager_command = ast_utilities::get_pager();\n\n    if ($debug_flag) {\n        print(\"Would execute JVM command:\\n\");\n        print(\"@java_command\\n\");\n        print(\"Then pipe into:\\n\");\n        print(\"@pager_command\\n\");\n        exit 0;\n    }\n\n    # NOTE: there is no easy way to prevent shell interference should the java\n    # command array contain only one element.\n    open JAVA, \"-|\", @java_command or die $!;\n    my $output = '';\n    while (<JAVA>) {\n        # Not the most efficient way to do things.\n        # Perhaps some kind of slurp is needed. File::Slurp could work but does\n        # have an outstanding Unicode bug. Need to investigate more.\n        $output = $output . $_;\n    }\n    close JAVA;\n\n    if ($skip_paging) {\n        print \"$output\";\n    }\n    else {\n        # NOTE: there is no easy way to prevent shell interference should the pager\n        # command array contain only one element.\n        open PAGER, \"|-\", @pager_command or die $!;\n        print PAGER \"$output\";\n        close PAGER;\n    }\n\n    exit 0;\n}\n\nsub atlas_show_class_of_and_exit {\n    my $ast_path = shift;\n    my $class_of = shift;\n\n    my %subcommand_classes = ast_module_subsystem::get_subcommand_to_class_hash($ast_path);\n    my $subcommand_class = $subcommand_classes{$class_of};\n\n    unless (defined $subcommand_class) {\n        atlas_unrecognized_command_message_and_exit($class_of);\n    }\n\n    print \"$subcommand_class\\n\";\n    exit 0;\n}\n\nsub atlas_list_subcommands_and_exit {\n    my $ast_path = shift;\n    my $skip_paging = shift;\n\n    unless (defined $skip_paging) {\n        $skip_paging = 0;\n    }\n\n    my %subcommand_desc = ast_module_subsystem::get_subcommand_to_description_hash($ast_path);\n    my @pager_command = ast_utilities::get_pager();\n\n    if ($skip_paging) {\n        print \"\\n\";\n        print \"${bold_stdout}AVAILABLE COMMANDS${reset_stdout}\\n\";\n        print \"See the help page for a command with ${bold_stdout}${program_name} --help <command>${reset_stdout}.\\n\\n\";\n        foreach my $subcommand (sort {lc $a cmp lc $b} keys %subcommand_desc) {\n            print \"    ${bold_stdout}$subcommand${reset_stdout}\\n\";\n            print \"        $subcommand_desc{$subcommand}\\n\\n\";\n        }\n        print \"\\n\";\n    }\n    else {\n        # NOTE: there is no easy way to prevent shell interference should the pager\n        # command array contain only one element.\n        open PAGER, \"|-\", @pager_command or die $!;\n        print PAGER \"${bold_stdout}AVAILABLE COMMANDS${reset_stdout}\\n\";\n        print PAGER \"See the help page for a command with ${bold_stdout}${program_name} --help <command>${reset_stdout}.\\n\";\n        print PAGER \"You can open a new page directly from this window with ${bold_stdout}!${program_name} --help <command>${reset_stdout}.\\n\\n\";\n        foreach my $subcommand (sort {lc $a cmp lc $b} keys %subcommand_desc) {\n            print PAGER \"    ${bold_stdout}$subcommand${reset_stdout}\\n\";\n            print PAGER \"        $subcommand_desc{$subcommand}\\n\\n\";\n        }\n        close PAGER;\n    }\n\n    exit 0;\n}\n\n########## END SUBROUTINES ##########\n\n\n########## BEGIN EXECUTION LOGIC ##########\n\nast_utilities::verify_environment_or_exit();\n$ast_path = ast_utilities::create_data_directory();\n\n# Handle atlas global options. Global options are options that come before the\n# supplied subcommand. Subcommand options are handled by the command implementation.\nmy $memory = '8G';\nmy $help_argument;\nmy $show_list;\nmy $class_of;\nmy $save_preset;\nmy $save_global_preset;\nmy $use_preset;\nmy $remove_preset;\nmy $all_presets;\nmy $show_preset;\nmy $edit_preset;\nmy $allow_run_as_root;\nGetopt::Long::Configure(qw(no_ignore_case_always));\nGetOptions(\n    \"no-pager\"          => \\$skip_paging,\n    \"memory|m=s\"        => \\$memory,\n    \"help|h:s\"          => \\$help_argument,\n    \"version|V\"         => sub {\n        print \"$program_version\\n\";\n        exit 0;\n    },\n    \"quiet|q\"               => \\$quiet,\n    \"list|l\"                => \\$show_list,\n    \"class-of=s\"            => \\$class_of,\n    \"preset|p=s\"            => \\$use_preset,\n    \"save-preset=s\"         => \\$save_preset,\n    \"save-global-preset=s\"  => \\$save_global_preset,\n    \"debug\"                 => \\$debug_flag,\n    \"allow-run-as-root\"     => \\$allow_run_as_root,\n    # This callback occurs the first time we see a non-option argument.\n    # In our case, this will be the subcommand.\n    \"<>\"                => sub {\n        my ($arg) = @_;\n        if ($arg =~ m{^-}) {\n            unless ($arg eq '-') {\n                die \"FATAL error: unhandled global option $arg\";\n            }\n        }\n        # add the subcommand to the front of ARGV\n        unshift @ARGV, $arg;\n        die \"!FINISH\";\n    }\n) or ast_utilities::getopt_failure_and_exit($program_name);\n\nif (geteuid() == 0) {\n    unless (defined $allow_run_as_root) {\n        print STDERR \"For security reasons, you are highly discouraged from running atlas-shell-tools\\n\";\n        print STDERR \"as the root user. Atlas-shell-tools cannot guarantee that modules installed from\\n\";\n        print STDERR \"external repositories are safe to run with root privileges.\\n\\n\";\n        print STDERR \"To disregard this warning and run as root anyway, please use the option:\\n\";\n        print STDERR \"--allow-run-as-root\\n\\n\";\n        exit 1;\n    }\n}\n\n# Handle the case where the user supplied a --help flag with no arg.\n# We can show this without doing any other verification.\n# Just display the man page and exit.\nif (defined $help_argument) {\n    if ($help_argument eq '') {\n        my @man_command = ast_utilities::get_man($skip_paging);\n        if (scalar @man_command == 0) {\n            ast_utilities::error_output($program_name, \"could not obtain \\'man\\' command\");\n            print STDERR \"Please ensure a valid \\'man\\' command is on your path.\\n\";\n            exit 1;\n        }\n        my @command = ();\n        push @command, @man_command;\n        push @command, \"$program_name\";\n        system {$command[0]} @command;\n        my $exitcode = $? >> 8;\n        if ($exitcode != 0) {\n            exit 1;\n        }\n        exit 0;\n    }\n}\n\nmy %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\nmy %modules_links = ast_module_subsystem::get_module_to_symlink_hash($ast_path);\n\n# If there are no modules, let's throw an error\nunless (keys %modules) {\n    ast_utilities::error_output($program_name, 'found no installed modules');\n    print STDERR \"Try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} install /path/to/module.jar${reset_stderr}' to install a module.\\n\";\n    exit 1;\n}\n\nmy @activated_modules = ast_module_subsystem::get_activated_modules(\\%modules);\n\n# If there are modules but none are active, warn the user\nif (scalar @activated_modules == 0) {\n    ast_utilities::error_output($program_name, 'no activated module');\n    print STDERR \"Try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} list${reset_stderr}' to see all installed modules.\\n\";\n    print STDERR \"Then try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} activate <module>${reset_stderr}' to activate.\\n\";\n    exit 1;\n}\n\n# If the currently active module is a broken symlink, warn the user\nif ($modules_links{$activated_modules[0]} == $ast_module_subsystem::BROKEN_SYMLINK) {\n    ast_utilities::error_output($program_name, 'current active module is a broken symlink');\n    print STDERR \"To see the link value, try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} list${reset_stderr}'.\\n\";\n    print STDERR \"Fix the link, then run '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} sync${reset_stderr}' to resolve.\\n\";\n    exit 1;\n}\n\n# If there is no active module index, warn the user\nmy $index_path = File::Spec->catfile($ast_path, $ast_module_subsystem::ACTIVE_INDEX_PATH);\nunless (-f $index_path) {\n    ast_utilities::error_output($program_name, 'could not find active module index');\n    print STDERR \"Try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} sync${reset_stderr}' to resolve.\\n\";\n    exit 1;\n}\n\nif ($debug_flag) {\n    print \"---- ATLAS DEBUG MODE ----\\n\";\n}\n\n# Handle case where user entered --help=TOPIC flag\n# We waited until after verifying that a command index exists\nif (defined $help_argument) {\n    unless ($help_argument eq '') {\n        atlas_show_contextual_help_menu_and_exit($help_argument, $skip_paging, $ast_path);\n    }\n}\n\nmy %subcommand_classes = ast_module_subsystem::get_subcommand_to_class_hash($ast_path);\n\n# Handle --class-of option\nif (defined $class_of) {\n    atlas_show_class_of_and_exit($ast_path, $class_of);\n}\n\n# Handle --list option\nif (defined $show_list) {\n    atlas_list_subcommands_and_exit($ast_path, $skip_paging);\n}\n\n# All global options have been processed, so shift the subcommand off of ARGV\nmy $subcommand = shift @ARGV;\n\nunless (defined $subcommand) {\n    ast_utilities::error_output($program_name, \"missing required command or option\");\n    print STDERR \"Try '${bold_stderr}${program_name} --list${reset_stderr}' for a list of commands.\\n\";\n    print STDERR \"Try '${bold_stderr}${program_name} --help${reset_stderr}' for more information.\\n\";\n    exit 1;\n}\n\nmy $subcommand_class = $subcommand_classes{$subcommand};\n\n# Case where the user entered an invalid subcommand\nunless (defined $subcommand_class) {\n    atlas_unrecognized_command_message_and_exit($subcommand);\n}\n\n# Set @new_argv to @ARGV for now. @new_argv will get overriden by an applied\n# preset if the user supplied one.\nmy @new_argv = @ARGV;\nmy $current_namespace = ast_preset_subsystem::get_namespace($ast_path);\n\n# Check if a preset is being saved or used. We check for preset application\n# first, and apply it if necessary. We then check for a preset save. This allows\n# users to easily extend presets they have already created by applying and\n# saving in a single step.\nif (defined $use_preset) {\n    @new_argv = ast_preset_subsystem::apply_preset_or_exit($ast_path, $program_name, $quiet, $use_preset, $subcommand, $current_namespace, \\@ARGV);\n    if ($debug_flag) {\n        print \"Applying preset(s) ${use_preset} for ${subcommand} in namespace ${current_namespace}\\n\";\n    }\n}\n\nif (defined $save_preset) {\n    unless (ast_preset_subsystem::preset_regex_ok($save_preset)) {\n            ast_utilities::error_output($program_name, \"invalid preset name ${bold_stderr}${save_preset}${reset_stderr}\");\n            print STDERR \"Name must match regex: \" . ast_preset_subsystem::preset_regex() . \"\\n\";\n            exit 1;\n    }\n    if ($debug_flag) {\n        print \"Would save preset ${save_preset} for ${subcommand} to namespace ${current_namespace}\\n\";\n        print \"Preset ARGV: \\\"@new_argv\\\"\\n\\n\";\n    }\n    else {\n        my $success = ast_preset_subsystem::save_preset($ast_path, $program_name, $quiet, $save_preset, $subcommand, $current_namespace, \\@new_argv);\n        unless ($success) {\n            exit 1;\n        }\n        print \"Launching command ${bold_stdout}${subcommand}${reset_stdout}...\\n\\n\";\n    }\n}\n\nif (defined $save_global_preset) {\n    unless (ast_preset_subsystem::preset_regex_ok($save_global_preset)) {\n            ast_utilities::error_output($program_name, \"invalid preset name ${bold_stderr}${save_global_preset}${reset_stderr}\");\n            print STDERR \"Name must match regex: \" . ast_preset_subsystem::preset_regex() . \"\\n\";\n            exit 1;\n    }\n    if ($debug_flag) {\n        print \"Would save global preset ${save_global_preset} to namespace ${current_namespace}\\n\";\n        print \"Preset ARGV: \\\"@new_argv\\\"\\n\\n\";\n    }\n    else {\n        my $success = ast_preset_subsystem::save_preset($ast_path, $program_name, $quiet, $save_global_preset, $ast_preset_subsystem::GLOBAL_FOLDER, $current_namespace, \\@new_argv);\n        unless ($success) {\n            exit 1;\n        }\n        print \"Launching command ${bold_stdout}${subcommand}${reset_stdout}...\\n\\n\";\n    }\n}\n\n# Set up the subcommand to execute using the JVM\nmy $module = $activated_modules[0];\nmy $full_path_to_modules_folder = File::Spec->catfile($ast_path, $ast_module_subsystem::MODULES_FOLDER, \"$module\" . $ast_module_subsystem::MODULE_SUFFIX);\nmy $full_path_to_log4j = File::Spec->catfile($ast_path, $ast_log_subsystem::LOG4J_FILE_PATH);\n\nmy @java_command = ();\npush @java_command, \"java\";\npush @java_command, \"-Xms$memory\";\npush @java_command, \"-Xmx$memory\";\npush @java_command, \"-cp\";\npush @java_command, \"${full_path_to_modules_folder}\";\npush @java_command, \"-Dlog4j.configuration=file:${full_path_to_log4j}\";\npush @java_command, \"${subcommand_class}\";\n\n# Surround each arg in quotes in case it contains whitespace\nforeach my $arg (@new_argv) {\n    push @java_command, \"$arg\";\n}\n\n# Append the special TTY formatting sentinel arguments\nif (ast_tty::is_no_colors_stdout()) {\n    push @java_command, \"${ast_utilities::JAVA_NO_COLOR_STDOUT}\";\n}\nelse {\n    push @java_command, \"${ast_utilities::JAVA_COLOR_STDOUT}\";\n}\n\nif (ast_tty::is_no_colors_stderr()) {\n    push @java_command, \"${ast_utilities::JAVA_NO_COLOR_STDERR}\";\n}\nelse {\n    push @java_command, \"${ast_utilities::JAVA_COLOR_STDERR}\";\n}\n\nif ($skip_paging) {\n    push @java_command, \"${ast_utilities::JAVA_NO_USE_PAGER}\";\n}\nelse {\n    push @java_command, \"${ast_utilities::JAVA_USE_PAGER}\";\n}\n\nmy $terminal_width = ast_tty::terminal_width();\npush @java_command, \"${terminal_width}\";\npush @java_command, \"${ast_utilities::JAVA_MARKER_SENTINEL}\";\n\nif ($debug_flag) {\n    print(\"Would execute JVM command:\\n\");\n    print(\"@java_command\\n\");\n    exit 0;\n}\n\n# Here we use system's indirect object syntax to correctly handle all possible\n# edge cases regarding the configuration of @java_command.\n# See bottom of page: http://perldoc.perl.org/functions/exec.html\nsystem {$java_command[0]} @java_command;\nmy $exitcode = $? >> 8;\nexit $exitcode;\n\n\n########## END EXECUTION LOGIC ##########\n"
  },
  {
    "path": "atlas-shell-tools/scripts/atlas-config",
    "content": "#!/usr/bin/env perl\n\nuse warnings;\nuse strict;\n\nuse Getopt::Long qw(GetOptions);\nuse File::Basename;\nuse File::Copy qw(copy);\nuse File::Spec;\nuse File::Path;\nuse POSIX;\n\n# Pull in code from the common modules\nuse FindBin;\nuse lib \"$FindBin::Bin/common\";\nuse ast_completions;\nuse ast_log_subsystem;\nuse ast_module_subsystem;\nuse ast_preset_subsystem;\nuse ast_repo_subsystem;\nuse ast_utilities;\nuse ast_tty;\n\n\n## ORGANIZATION\n## This script is organized into 3 sections:\n## 1) GLOBAL INITIALIZATION - initialize some useful global constants\n## 2) SUBROUTINES - helper subroutines\n## 3) COMMAND SUBROUTINES - subcommand execution subroutines\n## 4) EXECUTION LOGIC - the actual command logic, ie. 'main'\n\n########## BEGIN GLOBAL INITIALIZATION ##########\nmy $ansi_red = ast_tty::ansi_red();\nmy $ansi_green = ast_tty::ansi_green();\nmy $ansi_magenta = ast_tty::ansi_magenta();\nmy $ansi_bold = ast_tty::ansi_bold();\nmy $ansi_blink = ast_tty::ansi_blink();\nmy $ansi_reset = ast_tty::ansi_reset();\nmy $ansi_bunl = ast_tty::ansi_begin_underln();\nmy $ansi_eunl = ast_tty::ansi_end_underln();\n\nmy $no_colors_stdout = ast_tty::is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : $ansi_red;\nmy $green_stdout = $no_colors_stdout ? \"\" : $ansi_green;\nmy $magenta_stdout = $no_colors_stdout ? \"\" : $ansi_magenta;\nmy $bold_stdout = $no_colors_stdout ? \"\" : $ansi_bold;\nmy $blink_stdout = $no_colors_stdout ? \"\" : $ansi_blink;\nmy $reset_stdout = $no_colors_stdout ? \"\" : $ansi_reset;\nmy $bunl_stdout = $no_colors_stdout ? \"\" : $ansi_bunl;\nmy $eunl_stdout = $no_colors_stdout ? \"\" : $ansi_eunl;\n\nmy $no_colors_stderr = ast_tty::is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : $ansi_red;\nmy $green_stderr = $no_colors_stderr ? \"\" : $ansi_green;\nmy $magenta_stderr = $no_colors_stderr ? \"\" : $ansi_magenta;\nmy $bold_stderr = $no_colors_stderr ? \"\" : $ansi_bold;\nmy $blink_stderr = $no_colors_stderr ? \"\" : $ansi_blink;\nmy $reset_stderr = $no_colors_stderr ? \"\" : $ansi_reset;\nmy $bunl_stderr = $no_colors_stderr ? \"\" : $ansi_bunl;\nmy $eunl_stderr = $no_colors_stderr ? \"\" : $ansi_eunl;\n\nmy $ast_path;\nmy $skip_paging;\nmy $quiet;\n\nmy $program_name = $ast_utilities::CONFIG_PROGRAM;\nmy $program_version = \"$ast_utilities::ATLAS_SHELL_TOOLS_VERSION ($program_name program)\";\n\n# logging definitions\nmy %valid_levels = (\n    \"ALL\"   => 1,\n    \"TRACE\" => 1,\n    \"DEBUG\" => 1,\n    \"INFO\"  => 1,\n    \"WARN\"  => 1,\n    \"ERROR\" => 1,\n    \"FATAL\" => 1,\n    \"OFF\"   => 1\n);\nmy @valid_level_keys = keys %valid_levels;\n\nmy %valid_streams = (\n    \"stdout\" => 1,\n    \"stderr\" => 1\n);\nmy @valid_stream_keys = keys %valid_streams;\n\n########## END GLOBAL INITIALIZATION ##########\n\n\n########## BEGIN SUBROUTINES ##########\n\nsub show_submanpage_and_exit {\n    my $subpage = shift;\n\n    my @man_command = ast_utilities::get_man($skip_paging);\n    if (scalar @man_command == 0) {\n        ast_utilities::error_output($program_name, \"could not obtain \\'man\\' command\");\n        print STDERR \"Please ensure a valid \\'man\\' command is on your path.\\n\";\n        exit 1;\n    }\n\n    my @command = ();\n    push @command, @man_command;\n    if ($subpage eq '') {\n        push @command, \"$program_name\";\n    }\n    else {\n        push @command, \"$program_name-$subpage\";\n    }\n    system {$command[0]} @command;\n    exit 0;\n}\n\n# helper routine for the 'preset' subcommand\nsub atlas_cfgpreset_namespace {\n    my $current_namespace = shift;\n\n    my %subcommand_classes = ast_module_subsystem::get_subcommand_to_class_hash($ast_path);\n    my $success = 1;\n\n    my $subdirective = shift @ARGV;\n\n    unless (defined $subdirective) {\n        ast_utilities::error_output($program_name, \"${bold_stderr}${program_name} preset namespace${reset_stderr} requires a subdirective\");\n        print STDERR \"Try ${bold_stderr}create${reset_stderr}, ${bold_stderr}list${reset_stderr}, ${bold_stderr}remove${reset_stderr}, or ${bold_stderr}use${reset_stderr}.\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    if ($subdirective eq 'list') {\n        my $namespace = shift @ARGV;\n        if (defined $namespace) {\n            $success = ast_preset_subsystem::all_presets($ast_path, $program_name, $quiet, $namespace);\n        }\n        else {\n            $success = ast_preset_subsystem::all_namespaces($ast_path, $program_name, $quiet);\n        }\n    }\n    elsif ($subdirective eq 'use') {\n        my $namespace = shift @ARGV;\n        unless (defined $namespace) {\n            ast_utilities::error_output($program_name, \"must specify a namespace\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset namespace use <namespace>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::use_namespace($ast_path, $program_name, $quiet, $namespace);\n    }\n    elsif ($subdirective eq 'create') {\n        my $new_namespace = shift @ARGV;\n        unless (defined $new_namespace) {\n            ast_utilities::error_output($program_name, \"must specify a new namespace\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset namespace create <namespace>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::create_namespace($ast_path, $program_name, $quiet, $new_namespace);\n    }\n    elsif ($subdirective eq 'remove') {\n        my $namespace = shift @ARGV;\n        unless (defined $namespace) {\n            ast_utilities::error_output($program_name, \"must specify a namespace\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset namespace remove <namespace>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::remove_namespace($ast_path, $program_name, $quiet, $namespace);\n    }\n    else {\n        ast_utilities::error_output($program_name, \"unrecognized ${bold_stderr}${program_name} preset namespace${reset_stderr} subdirective '${bold_stderr}${subdirective}${reset_stderr}'\");\n        print STDERR \"Try ${bold_stderr}create${reset_stderr}, ${bold_stderr}list${reset_stderr}, ${bold_stderr}remove${reset_stderr}, or ${bold_stderr}use${reset_stderr}.\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    return 1;\n}\n\n########## END SUBROUTINES ##########\n\n\n########## BEGIN COMMAND SUBROUTINES ##########\n\nsub execute_command_activate {\n    my $help_flag;\n    GetOptions(\n        'help|h' => \\$help_flag\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"activate\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('activate');\n    }\n\n    my $module_to_activate = shift @ARGV;\n    unless (defined $module_to_activate) {\n        ast_utilities::error_output($program_name . \": activate\", \"missing required argument\");\n        ast_utilities::getopt_failure_and_exit($program_name, \"activate\");\n    }\n\n    my %modules = get_module_to_status_hash($ast_path);\n    unless (exists $modules{$module_to_activate}) {\n        ast_utilities::error_output($program_name . \": activate\", \"no such module ${bold_stderr}${module_to_activate}${reset_stderr}\");\n        return 0;\n    }\n\n    # deactivate all other modules\n    foreach my $module (keys %modules) {\n        if ($modules{$module} == $ast_module_subsystem::ACTIVATED) {\n            ast_module_subsystem::perform_deactivate($module, $ast_path, $program_name, $quiet);\n        }\n    }\n\n    my $success = ast_module_subsystem::perform_activate($module_to_activate, $ast_path, $program_name, $quiet);\n    return $success;\n}\n\nsub execute_command_deactivate {\n    my $help_flag;\n    GetOptions(\n        'help|h' => \\$help_flag\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"deactivate\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('deactivate');\n    }\n\n    my $module_to_deactivate = shift @ARGV;\n    unless (defined $module_to_deactivate) {\n        ast_utilities::error_output($program_name . \": deactivate\", \"missing required argument\");\n        ast_utilities::getopt_failure_and_exit($program_name, \"deactivate\");\n    }\n\n    my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n    unless (exists $modules{$module_to_deactivate}) {\n        ast_utilities::error_output($program_name . \": deactivate\", \"no such module ${bold_stderr}${module_to_deactivate}${reset_stderr}\");\n        return 0;\n    }\n\n    my $success = ast_module_subsystem::perform_deactivate($module_to_deactivate, $ast_path, $program_name, $quiet);\n    if ($success) {\n        ast_module_subsystem::remove_active_module_index($ast_path, $program_name, $quiet);\n    }\n    return $success;\n}\n\nsub execute_command_install {\n    my $syminstall = 0;\n    my $force_install = 0;\n    my $skip_install = 0;\n    my $install_deactivated = 0;\n    my $alternate_name = \"\";\n    my $help_flag;\n    GetOptions(\n        'symlink|s'   => \\$syminstall,\n        'deactivated' => \\$install_deactivated,\n        'force'       => \\$force_install,\n        'skip'        => \\$skip_install,\n        'name=s'      => \\$alternate_name,\n        'help|h'      => \\$help_flag\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"install\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('install');\n    }\n\n    my $module_to_install = shift @ARGV;\n    unless (defined $module_to_install) {\n        ast_utilities::error_output($program_name . \": install\", \"missing required argument\");\n        ast_utilities::getopt_failure_and_exit($program_name, \"install\");\n    }\n\n    my %metadata;\n    if ($syminstall) {\n        $metadata{$ast_module_subsystem::SOURCE_KEY} = \"symlink\";\n    }\n    else {\n        $metadata{$ast_module_subsystem::SOURCE_KEY} = \"local_file\";\n    }\n    my $uri_abs = Cwd::realpath($module_to_install);\n    $metadata{$ast_module_subsystem::URI_KEY} = \"file://\" . $uri_abs;\n    $metadata{$ast_module_subsystem::DATE_TIME_KEY} = POSIX::strftime(\"%Y-%m-%d %H:%M:%S UTC\", gmtime(time));\n    my $success = ast_module_subsystem::perform_install($module_to_install, $ast_path, $program_name,\n        $alternate_name, $syminstall, $skip_install,\n        $force_install, $install_deactivated, \\%metadata, $quiet);\n\n    unless ($success) {\n        return 0;\n    }\n\n    return 1;\n}\n\nsub execute_command_list {\n    my $current = 0;\n    my $help_flag;\n    my $one_line = 0;\n    GetOptions(\n        'current|c' => \\$current,\n        'help|h'    => \\$help_flag,\n        'one-line|1' => \\$one_line\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"list\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('list');\n    }\n\n    my $modules_folder = File::Spec->catfile($ast_path, $ast_module_subsystem::MODULES_FOLDER);\n\n    my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n    my %symlinks = ast_module_subsystem::get_module_to_symlink_hash($ast_path);\n    my %targets = ast_module_subsystem::get_module_to_target_hash($ast_path);\n    my %metadata = ast_module_subsystem::get_module_to_metadata_hash($ast_path);\n\n    unless (keys %modules) {\n        ast_utilities::error_output($program_name . \": list\", \"found no installed modules\");\n        print STDERR \"Try '${bold_stderr}${program_name} install /path/to/module.jar${reset_stderr}' to install a module.\\n\";\n        print STDERR \"Or try '${bold_stderr}${program_name} repo install atlas${reset_stderr}' to install the commands from the atlas repo.\\n\";\n        return 0;\n    }\n\n    if ($current) {\n        print \"${bold_stdout}Currently active module:${reset_stdout}\\n\\n\";\n    }\n    else {\n        print \"${bold_stdout}Installed modules:${reset_stdout}\\n\\n\";\n    }\n    # Sort the module names alphabetically. We use 'lc' to convert them to\n    # lowercase, since by default 'sort' uses ASCII ordering.\n    foreach my $module (sort {lc $a cmp lc $b} keys %modules) {\n        my $status = $modules{$module};\n        my $symlink = $symlinks{$module};\n        my $target = $targets{$module};\n        my %module_metadata;\n        if (defined $metadata{$module}) {\n            %module_metadata = %{$metadata{$module}};\n        }\n        else {\n            %module_metadata = ();\n        }\n        my $display = '    ';\n\n        if ($current && $status != 1) {\n            next;\n        }\n\n        # if activated, place a star next to the name\n        if ($status == 1) {\n            $display = $display . '*';\n        }\n        else {\n            $display = $display . ' ';\n        }\n\n        # choose an appropriate color for the display\n        if ($status == $ast_module_subsystem::ACTIVATED\n            && ($symlink == $ast_module_subsystem::REAL_FILE || $symlink == $ast_module_subsystem::GOOD_SYMLINK)) {\n            $display = $display . \"${green_stdout}${bold_stdout}\";\n        }\n        elsif ($symlink == $ast_module_subsystem::BROKEN_SYMLINK) {\n            $display = $display . \"${red_stdout}${bold_stdout}\";\n        }\n\n        # show the module name!\n        $display = $display . \" ${bold_stdout}${module}${reset_stdout}\";\n\n        # show a big message if the symlink is broken, blink if also activated\n        if ($symlink == $ast_module_subsystem::BROKEN_SYMLINK) {\n            if ($status == $ast_module_subsystem::ACTIVATED) {\n                $display = $display . \" ${bold_stdout}${blink_stdout}(BROKEN SYMLINK)${reset_stdout}\";\n            }\n            else {\n                $display = $display . \" ${bold_stdout}(BROKEN SYMLINK)${reset_stdout}\";\n            }\n        }\n\n        # if we were a symlink, show the target after the module name\n        if ($symlink != $ast_module_subsystem::REAL_FILE) {\n            $display = $display . \" -> ${target}\";\n        }\n\n        print \"$display\\n\";\n\n        unless ($one_line) {\n            if (%module_metadata) {\n                foreach my $metadata_key (sort {lc $a cmp lc $b} keys %module_metadata) {\n                    print \"          ${metadata_key}: $module_metadata{$metadata_key}\\n\";\n                }\n            }\n            print \"\\n\";\n        }\n    }\n    if ($one_line) {\n        print \"\\n\";\n    }\n\n    return 1;\n}\n\nsub execute_command_log {\n    my $help_flag;\n    GetOptions(\n        'help|h' => \\$help_flag\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"log\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('log');\n    }\n\n    my $directive = shift @ARGV;\n    my $success;\n\n    unless (defined $directive) {\n        ast_utilities::error_output($program_name . \": log\", \"missing required directive\");\n        print STDERR \"Usage: ${bold_stderr}${program_name} log <directive> <args...>${reset_stderr}\\n\";\n        print STDERR \"Available directives: ${bold_stderr}reset${reset_stderr}, ${bold_stderr}set-level${reset_stderr}, ${bold_stderr}set-stream${reset_stderr}, or ${bold_stderr}show${reset_stderr}\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} log --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    my $log_folder = File::Spec->catfile($ast_path, $ast_log_subsystem::LOG4J_FOLDER);\n    my $logfile_path = File::Spec->catfile($log_folder, $ast_log_subsystem::LOG4J_FILE);\n    my $current_level = ast_log_subsystem::read_loglevel_from_file($logfile_path);\n    my $current_stream = ast_log_subsystem::read_logstream_from_file($logfile_path);\n\n    if ($directive eq 'reset') {\n        ast_log_subsystem::reset_log4j($ast_path);\n    }\n    elsif ($directive eq 'set-level') {\n        my $new_level = shift @ARGV;\n        unless (defined $new_level) {\n            ast_utilities::error_output($program_name . \": log\", \"must specify a new level\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} log set-level <new-level>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} log --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        if (exists($valid_levels{$new_level})) {\n            ast_log_subsystem::replace_loglevel_in_file($logfile_path, $new_level);\n        }\n        else {\n            ast_utilities::error_output($program_name . \": log\", \"unrecognized log level ${bold_stderr}${new_level}${reset_stderr}\");\n            print STDERR \"Try \\'${bold_stderr}${program_name} log --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n    }\n    elsif ($directive eq 'set-stream') {\n        my $new_stream = shift @ARGV;\n        unless (defined $new_stream) {\n            ast_utilities::error_output($program_name . \": log\", \"must specify a new stream\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} log set-stream <new-stream>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} log --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        if (exists($valid_streams{$new_stream})) {\n            ast_log_subsystem::replace_logstream_in_file($logfile_path, $new_stream);\n        }\n        else {\n            ast_utilities::error_output($program_name . \": log\", \"unrecognized log stream ${bold_stderr}${new_stream}${reset_stderr}\");\n            print STDERR \"Try \\'${bold_stderr}${program_name} log --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n    }\n    elsif ($directive eq 'show') {\n        $current_level = ast_log_subsystem::read_loglevel_from_file($logfile_path);\n        $current_stream = ast_log_subsystem::read_logstream_from_file($logfile_path);\n        print \"Current log level:    ${bold_stdout}${current_level}${reset_stdout}\\n\";\n        print \"Current log stream:   ${bold_stdout}${current_stream}${reset_stdout}\\n\";\n    }\n    else {\n        ast_utilities::error_output($program_name . \": log\", \"unrecognized ${bold_stderr}${program_name} log${reset_stderr} directive '${bold_stderr}${directive}${reset_stderr}'\");\n        print STDERR \"Available directives: ${bold_stderr}reset${reset_stderr}, ${bold_stderr}set-level${reset_stderr}, ${bold_stderr}set-stream${reset_stderr}, or ${bold_stderr}show${reset_stderr}\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} log --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    return 1;\n}\n\nsub execute_command_preset {\n    my $help_flag;\n    GetOptions(\n        'help|h' => \\$help_flag,\n        # This callback occurs the first time we see a non-option argument.\n        # In our case, this will be the subcommand.\n        \"<>\"     => sub {\n            my ($arg) = @_;\n            if ($arg =~ m{^-}) {\n                unless ($arg eq '-') {\n                    die \"FATAL error: unhandled global option $arg\";\n                }\n            }\n            # add the subcommand to the front of ARGV\n            unshift @ARGV, $arg;\n            die \"!FINISH\";\n        }\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"preset\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('preset');\n    }\n\n    my %subcommand_classes = ast_module_subsystem::get_subcommand_to_class_hash($ast_path);\n    my $current_namespace = ast_preset_subsystem::get_namespace($ast_path);\n\n    my $directive = shift @ARGV;\n    my $success;\n\n    unless (defined $directive) {\n        ast_utilities::error_output($program_name . \": preset\", \"missing required directive\");\n        print STDERR \"Usage: ${bold_stderr}${program_name} preset <directive> <args...>${reset_stderr}\\n\";\n        print STDERR \"Available directives: ${bold_stderr}copy${reset_stderr}, ${bold_stderr}edit${reset_stderr}, ${bold_stderr}list${reset_stderr}, ${bold_stderr}namespace${reset_stderr}, ${bold_stderr}remove${reset_stderr}, or ${bold_stderr}save${reset_stderr}\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    if ($directive eq 'copy') {\n        my $command_context = shift @ARGV;\n        unless (defined $command_context) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a command\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset copy <command> <src> <dest>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $subcommand_classes{$command_context}) {\n            ast_utilities::error_output($program_name . \": preset\", \"no such command ${bold_stderr}${command_context}${reset_stderr}\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset copy <command> <src> <dest>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        my $src_preset = shift @ARGV;\n        my $dest_preset = shift @ARGV;\n        unless (defined $src_preset) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a source preset\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset copy <command> <src> <dest>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $dest_preset) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a destination preset\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset copy <command> <src> <dest>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::copy_preset($ast_path, $program_name, $quiet, $src_preset, $dest_preset, $command_context, $current_namespace);\n    }\n    elsif ($directive eq 'copy-global') {\n        my $src_preset = shift @ARGV;\n        my $dest_preset = shift @ARGV;\n        unless (defined $src_preset) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a source preset\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset copy-global <src> <dest>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $dest_preset) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a destination preset\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset copy-global <src> <dest>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::copy_preset($ast_path, $program_name, $quiet, $src_preset, $dest_preset, $ast_preset_subsystem::GLOBAL_FOLDER, $current_namespace);\n    }\n    elsif ($directive eq 'edit') {\n        my $command_context = shift @ARGV;\n        unless (defined $command_context) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a command\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset edit <command> <preset>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $subcommand_classes{$command_context}) {\n            ast_utilities::error_output($program_name . \": preset\", \"no such command ${bold_stderr}${command_context}${reset_stderr}\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset edit <command> <preset>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        my $preset = shift @ARGV;\n        unless (defined $preset) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a preset\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset edit <command> <preset>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::edit_preset($ast_path, $program_name, $quiet, $preset, $command_context, $current_namespace);\n    }\n    elsif ($directive eq 'edit-global') {\n        my $preset = shift @ARGV;\n        unless (defined $preset) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a preset\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset edit-global <preset>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::edit_preset($ast_path, $program_name, $quiet, $preset, $ast_preset_subsystem::GLOBAL_FOLDER, $current_namespace);\n    }\n    elsif ($directive eq 'list') {\n        my $command_context = shift @ARGV;\n        unless (defined $command_context) {\n            return ast_preset_subsystem::all_presets($ast_path, $program_name, $quiet, $current_namespace);\n        }\n        unless (defined $subcommand_classes{$command_context}) {\n            ast_utilities::error_output($program_name . \": preset\", \"no such command ${bold_stderr}${command_context}${reset_stderr}\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset list [command [preset]]${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        my $preset = shift @ARGV;\n        if (defined $preset) {\n            $success = ast_preset_subsystem::show_preset($ast_path, $program_name, $quiet, $preset, $command_context, $current_namespace);\n        }\n        else {\n            $success = ast_preset_subsystem::all_presets_for_command($ast_path, $program_name, $quiet, $command_context, $current_namespace);\n        }\n    }\n    elsif ($directive eq 'list-global') {\n        my $preset = shift @ARGV;\n        if (defined $preset) {\n            $success = ast_preset_subsystem::show_preset($ast_path, $program_name, $quiet, $preset, $ast_preset_subsystem::GLOBAL_FOLDER, $current_namespace);\n        }\n        else {\n            $success = ast_preset_subsystem::all_presets_for_command($ast_path, $program_name, $quiet, $ast_preset_subsystem::GLOBAL_FOLDER, $current_namespace);\n        }\n    }\n    elsif ($directive eq 'namespace') {\n        $success = atlas_cfgpreset_namespace($current_namespace);\n    }\n    elsif ($directive eq 'remove') {\n        my $command_context = shift @ARGV;\n        unless (defined $command_context) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a command\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset remove <command> [preset]${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $subcommand_classes{$command_context}) {\n            ast_utilities::error_output($program_name . \": preset\", \"no such command ${bold_stderr}${command_context}${reset_stderr}\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset remove <command> [preset]${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        my $preset = shift @ARGV;\n        if (defined $preset) {\n            $success = ast_preset_subsystem::remove_preset($ast_path, $program_name, $quiet, $preset, $command_context, $current_namespace);\n        }\n        else {\n            $success = ast_preset_subsystem::remove_all_presets_for_command($ast_path, $program_name, $quiet, $command_context, $current_namespace);\n        }\n    }\n    elsif ($directive eq 'remove-global') {\n        my $preset = shift @ARGV;\n        if (defined $preset) {\n            $success = ast_preset_subsystem::remove_preset($ast_path, $program_name, $quiet, $preset, $ast_preset_subsystem::GLOBAL_FOLDER, $current_namespace);\n        }\n        else {\n            $success = ast_preset_subsystem::remove_all_presets_for_command($ast_path, $program_name, $quiet, $ast_preset_subsystem::GLOBAL_FOLDER, $current_namespace);\n        }\n    }\n    elsif ($directive eq 'save') {\n        my $command_context = shift @ARGV;\n        unless (defined $command_context) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a command\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset save <command> <preset> <options...>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $subcommand_classes{$command_context}) {\n            ast_utilities::error_output($program_name . \": preset\", \"no such command ${bold_stderr}${command_context}${reset_stderr}\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset save <command> <preset> <options...>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        my $preset = shift @ARGV;\n        unless (defined $preset) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a preset\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset save <command> <preset> <options...>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (ast_preset_subsystem::preset_regex_ok($preset)) {\n            ast_utilities::error_output($program_name . \": preset\", \"invalid preset name ${bold_stderr}${preset}${reset_stderr}\");\n            print STDERR \"Name must match regex: \" . ast_preset_subsystem::preset_regex() . \"\\n\";\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset save <command> <preset> <options...>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::save_preset($ast_path, $program_name, $quiet, $preset, $command_context, $current_namespace, \\@ARGV);\n    }\n    elsif ($directive eq 'save-global') {\n        my $preset = shift @ARGV;\n        unless (defined $preset) {\n            ast_utilities::error_output($program_name . \": preset\", \"must specify a preset\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset save-global <preset> <options...>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (ast_preset_subsystem::preset_regex_ok($preset)) {\n            ast_utilities::error_output($program_name . \": preset\", \"invalid preset name ${bold_stderr}${preset}${reset_stderr}\");\n            print STDERR \"Name must match regex: \" . ast_preset_subsystem::preset_regex() . \"\\n\";\n            print STDERR \"Usage: ${bold_stderr}${program_name} preset save-global <preset> <options...>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_preset_subsystem::save_preset($ast_path, $program_name, $quiet, $preset, $ast_preset_subsystem::GLOBAL_FOLDER, $current_namespace, \\@ARGV);\n    }\n    else {\n        ast_utilities::error_output($program_name . \": preset\", \"unrecognized ${bold_stderr}${program_name} preset${reset_stderr} directive '${bold_stderr}${directive}${reset_stderr}'\");\n        print STDERR \"Available directives: ${bold_stderr}copy[-global]${reset_stderr}, ${bold_stderr}edit[-global]${reset_stderr}, ${bold_stderr}list[-global]${reset_stderr}, ${bold_stderr}namespace${reset_stderr}, ${bold_stderr}remove[-global]${reset_stderr}, or ${bold_stderr}save[-global]${reset_stderr}\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} preset --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    return 1;\n}\n\nsub execute_command_repo {\n    my $help_flag;\n    my $ref_flag = '';\n    GetOptions(\n        'help|h' => \\$help_flag,\n        'ref=s'  => \\$ref_flag\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"repo\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('repo');\n    }\n\n    my $directive = shift @ARGV;\n    my $success;\n\n    unless (defined $directive) {\n        ast_utilities::error_output($program_name . \": repo\", \"missing required directive\");\n        print STDERR \"Usage: ${bold_stderr}${program_name} repo <directive> <args...>${reset_stderr}\\n\";\n        print STDERR \"Available directives: ${bold_stderr}add${reset_stderr}, ${bold_stderr}add-gradle-exclude${reset_stderr}, ${bold_stderr}add-gradle-skip${reset_stderr}, ${bold_stderr}edit${reset_stderr}, ${bold_stderr}install${reset_stderr}, ${bold_stderr}list${reset_stderr}, or ${bold_stderr}remove${reset_stderr}\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    if ($directive eq 'add') {\n        my $repo_name = shift @ARGV;\n        my $repo_url = shift @ARGV;\n        my $repo_ref = shift @ARGV;\n        unless (defined $repo_name) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing repo name\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo add <repo-name> <URL> [ref]${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $repo_url) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing repo url\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo add <repo-name> <URL> [ref]${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (ast_repo_subsystem::repo_regex_ok($repo_name)) {\n            ast_utilities::error_output($program_name . \": repo\", \"invalid repo name ${bold_stderr}${repo_name}${reset_stderr}\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo add <repo-name> <URL> [ref]${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $repo_ref) {\n            $repo_ref = 'main';\n        }\n        $success = ast_repo_subsystem::create_repo($ast_path, $program_name, $quiet, $repo_name, $repo_url, $repo_ref);\n    }\n    elsif ($directive eq 'add-gradle-exclude') {\n        my $repo_name = shift @ARGV;\n        my $exclude = shift @ARGV;\n        unless (defined $repo_name) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing repo name\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo add-gradle-exclude <repo-name> <package>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $exclude) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing exclude package\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo add-gradle-exclude <repo-name> <package>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_repo_subsystem::add_exclude_variable($ast_path, $program_name, $quiet, $repo_name, $exclude);\n        if ($success) {\n            ast_repo_subsystem::print_repo_settings($ast_path, $program_name, $quiet, $repo_name);\n        }\n    }\n    elsif ($directive eq 'add-gradle-skip') {\n        my $repo_name = shift @ARGV;\n        my $skip = shift @ARGV;\n        unless (defined $repo_name) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing repo name\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo add-gradle-skip <repo-name> <task>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        unless (defined $skip) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing skip task\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo add-gradle-skip <repo-name> <task>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_repo_subsystem::add_skip_variable($ast_path, $program_name, $quiet, $repo_name, $skip);\n        if ($success) {\n            ast_repo_subsystem::print_repo_settings($ast_path, $program_name, $quiet, $repo_name);\n        }\n    }\n    elsif ($directive eq 'edit') {\n        my $repo_name = shift @ARGV;\n        unless (defined $repo_name) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing repo name\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo edit <repo-name>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_repo_subsystem::edit_repo($ast_path, $program_name, $quiet, $repo_name);\n        if ($success) {\n            ast_repo_subsystem::print_repo_settings($ast_path, $program_name, $quiet, $repo_name);\n        }\n    }\n    elsif ($directive eq 'install') {\n        my $repo_name = shift @ARGV;\n        unless (defined $repo_name) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing repo name\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo install <repo-name>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_repo_subsystem::install_repo($ast_path, $program_name, $quiet, $repo_name, $ref_flag);\n    }\n    elsif ($directive eq 'list') {\n        my $repo_name = shift @ARGV;\n        if (defined $repo_name) {\n            $success = ast_repo_subsystem::print_repo_settings($ast_path, $program_name, $quiet, $repo_name);\n        }\n        else {\n            $success = ast_repo_subsystem::list_repos($ast_path, $program_name, $quiet);\n        }\n    }\n    elsif ($directive eq 'remove') {\n        my $repo_name = shift @ARGV;\n        unless (defined $repo_name) {\n            ast_utilities::error_output($program_name . \": repo\", \"missing repo name\");\n            print STDERR \"Usage: ${bold_stderr}${program_name} repo remove <repo-name>${reset_stderr}\\n\";\n            print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n            return 0;\n        }\n        $success = ast_repo_subsystem::remove_repo($ast_path, $program_name, $quiet, $repo_name);\n    }\n    else {\n        ast_utilities::error_output($program_name . \": repo\", \"unrecognized directive '${bold_stderr}${directive}${reset_stderr}'\");\n        print STDERR \"Available directives: ${bold_stderr}add${reset_stderr}, ${bold_stderr}add-gradle-exclude${reset_stderr}, ${bold_stderr}add-gradle-skip${reset_stderr}, ${bold_stderr}edit${reset_stderr}, ${bold_stderr}install${reset_stderr}, ${bold_stderr}list${reset_stderr}, or ${bold_stderr}remove${reset_stderr}\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} repo --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    return 1;\n}\n\nsub execute_command_reset {\n    my $help_flag;\n    GetOptions(\n        'help|h' => \\$help_flag\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"reset\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('reset');\n    }\n\n    my $directive = shift @ARGV;\n    my $ran_at_least_one_directive = 0;\n\n    unless (defined $directive) {\n        ast_utilities::error_output($program_name . \": reset\", \"missing required directive\");\n        print STDERR \"Usage: ${bold_stderr}${program_name} reset <directive> <args...>${reset_stderr}\\n\";\n        print STDERR \"Available directives: ${bold_stderr}all${reset_stderr}, ${bold_stderr}index${reset_stderr}, ${bold_stderr}log${reset_stderr}, ${bold_stderr}modules${reset_stderr}, ${bold_stderr}presets${reset_stderr}, or ${bold_stderr}repos${reset_stderr}\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} reset --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    if ($directive eq 'index' || $directive eq 'all') {\n        ast_module_subsystem::remove_active_module_index($ast_path, $program_name, $quiet);\n        $ran_at_least_one_directive = 1;\n    }\n\n    if ($directive eq 'log' || $directive eq 'all') {\n        my $log_folder = File::Spec->catfile($ast_path, $ast_log_subsystem::LOG4J_FOLDER);\n        my $logfile_path = File::Spec->catfile($log_folder, $ast_log_subsystem::LOG4J_FILE);\n        ast_log_subsystem::replace_loglevel_in_file($logfile_path, 'ERROR');\n        ast_log_subsystem::replace_logstream_in_file($logfile_path, 'stderr');\n        unless ($quiet) {\n            print \"Reset log parameters.\\n\";\n        }\n        $ran_at_least_one_directive = 1;\n    }\n\n    if ($directive eq 'modules' || $directive eq 'all') {\n        my $modules_folder = File::Spec->catfile($ast_path, $ast_module_subsystem::MODULES_FOLDER);\n        my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n\n        my $modules_length = keys %modules;\n        if ($modules_length == 0) {\n            ast_utilities::warn_output($program_name . \": reset\", \"found no modules to uninstall\");\n        }\n        else {\n            foreach my $module (keys %modules) {\n                ast_module_subsystem::perform_uninstall($module, $ast_path, $program_name, $quiet, 1);\n            }\n            ast_module_subsystem::remove_active_module_index($ast_path, $program_name, $quiet);\n        }\n        $ran_at_least_one_directive = 1;\n    }\n\n    if ($directive eq 'presets' || $directive eq 'all') {\n        my $presets_folder = File::Spec->catfile($ast_path, $ast_preset_subsystem::PRESETS_FOLDER);\n        rmtree($presets_folder);\n        unless ($quiet) {\n            print \"Cleared presets.\\n\";\n        }\n        $ran_at_least_one_directive = 1;\n    }\n\n    if ($directive eq 'repos' || $directive eq 'all') {\n        my $repos_folder = File::Spec->catfile($ast_path, $ast_repo_subsystem::REPOS_FOLDER);\n        rmtree($repos_folder);\n        unless ($quiet) {\n            print \"Cleared repos.\\n\";\n        }\n        $ran_at_least_one_directive = 1;\n    }\n\n    if (!$ran_at_least_one_directive) {\n        ast_utilities::error_output($program_name . \": reset\", \"unrecognized ${bold_stderr}${program_name} reset${reset_stderr} directive '${bold_stderr}${directive}${reset_stderr}'\");\n        print STDERR \"Available directives: ${bold_stderr}all${reset_stderr}, ${bold_stderr}index${reset_stderr}, ${bold_stderr}log${reset_stderr}, ${bold_stderr}modules${reset_stderr}, ${bold_stderr}presets${reset_stderr}, or ${bold_stderr}repos${reset_stderr}\\n\";\n        print STDERR \"Try \\'${bold_stderr}${program_name} reset --help${reset_stderr}\\' for more information.\\n\";\n        return 0;\n    }\n\n    return 1;\n}\n\nsub execute_command_sync {\n    my $help_flag;\n    GetOptions(\n        'help|h' => \\$help_flag\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"sync\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('sync');\n    }\n\n    my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n    my %modules_links = ast_module_subsystem::get_module_to_symlink_hash($ast_path);\n    my @activated_modules = ast_module_subsystem::get_activated_modules(\\%modules);\n\n    if (scalar @activated_modules == 0) {\n        ast_utilities::error_output($program_name . \": sync\", 'found no activated module');\n        print STDERR \"To see installed modules, try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} list${reset_stderr}'.\\n\";\n        print STDERR \"Then run '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} activate <module>${reset_stderr}' to activate <module>.\\n\";\n        return 0;\n    }\n\n    # If the currently active module is a broken symlink, warn the user\n    if ($modules_links{$activated_modules[0]} == $ast_module_subsystem::BROKEN_SYMLINK) {\n        ast_utilities::error_output($program_name . \": sync\", 'current active module is a broken symlink');\n        print STDERR \"To see link value, try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} list${reset_stderr}'.\\n\";\n        print STDERR \"Fix the link, then run '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} sync${reset_stderr}' to resolve.\\n\";\n        return 1;\n    }\n\n    ast_module_subsystem::remove_active_module_index($ast_path, $program_name, $quiet);\n    ast_module_subsystem::generate_active_module_index($ast_path, $program_name, $quiet, 1);\n\n    return 1;\n}\n\nsub execute_command_uninstall {\n    my $allflag;\n    my $forceflag = 0;\n    my $help_flag;\n    GetOptions(\n        'all|a'  => \\$allflag,\n        'force'  => \\$forceflag,\n        'help|h' => \\$help_flag\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"uninstall\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('uninstall');\n    }\n\n    if (defined $allflag) {\n        my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n        foreach my $module (keys %modules) {\n            ast_module_subsystem::perform_uninstall($module, $ast_path, $program_name, $quiet, 1);\n        }\n        ast_module_subsystem::remove_active_module_index($ast_path, $program_name, $quiet);\n        return 1;\n    }\n\n    my $module_to_uninstall = shift @ARGV;\n    unless (defined $module_to_uninstall) {\n        ast_utilities::error_output($program_name . \": uninstall\", \"missing required argument\");\n        ast_utilities::getopt_failure_and_exit($program_name, \"uninstall\");\n    }\n\n    my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n    my @activated_modules = ast_module_subsystem::get_activated_modules(\\%modules);\n    my $activated_module = $activated_modules[0];\n\n    while (defined $module_to_uninstall) {\n        my $success = ast_module_subsystem::perform_uninstall($module_to_uninstall, $ast_path, $program_name, $quiet, $forceflag);\n        unless ($success) {\n            ast_utilities::error_output($program_name . \": uninstall\", \"failed to uninstall ${bold_stderr}${module_to_uninstall}${reset_stderr}\");\n        }\n        elsif (defined $activated_module && $module_to_uninstall eq $activated_module) {\n                ast_module_subsystem::remove_active_module_index($ast_path, $program_name, $quiet);\n        }\n        $module_to_uninstall = shift @ARGV;\n    }\n\n    return 1;\n}\n\nsub execute_command_update {\n    my $help_flag;\n    GetOptions(\n        'help|h' => \\$help_flag,\n    ) or ast_utilities::getopt_failure_and_exit($program_name, \"update\");\n\n    if (defined $help_flag) {\n        show_submanpage_and_exit('update');\n    }\n\n    chdir $ENV{ATLAS_SHELL_TOOLS_HOME} or die \"$!\";\n\n    my @command = ();\n    push @command, \"git\";\n    push @command, \"checkout\";\n    push @command, \"main\";\n    my $success = system {$command[0]} @command;\n    unless ($success == 0) {\n        ast_utilities::error_output($program_name . \": update\", \"update operation failed\");\n        return 1;\n    }\n\n    @command = ();\n    push @command, \"git\";\n    push @command, \"pull\";\n    push @command, \"origin\";\n    push @command, \"main\";\n    $success = system {$command[0]} @command;\n    unless ($success == 0) {\n        ast_utilities::error_output($program_name . \": update\", \"update operation failed\");\n        return 1;\n    }\n\n    return 1;\n}\n\n########## END COMMAND SUBROUTINES ##########\n\n\n########## BEGIN EXECUTION LOGIC ##########\n\nast_utilities::verify_environment_or_exit();\n$ast_path = ast_utilities::create_data_directory();\n\nmy $help_argument;\nmy $allow_run_as_root;\nGetopt::Long::Configure(qw(no_ignore_case_always));\nGetOptions(\n    \"no-pager\"          => \\$skip_paging,\n    \"help|h:s\"          => \\$help_argument,\n    \"version|V\"         => sub {\n        print \"$program_version\\n\";\n        exit 0;\n    },\n    \"quiet|q\"           => \\$quiet,\n    \"allow-run-as-root\" => \\$allow_run_as_root,\n    # This callback occurs the first time we see a non-option argument.\n    # In our case, this will be the subcommand.\n    \"<>\"                => sub {\n        my ($arg) = @_;\n        if ($arg =~ m{^-}) {\n            unless ($arg eq '-') {\n                die \"FATAL error: unhandled global option $arg\";\n            }\n        }\n        # add the subcommand to the front of ARGV\n        unshift @ARGV, $arg;\n        die \"!FINISH\";\n    }\n) or ast_utilities::getopt_failure_and_exit($program_name);\n\nif (geteuid() == 0) {\n    unless (defined $allow_run_as_root) {\n        print STDERR \"For security reasons, you are highly discouraged from running atlas-shell-tools\\n\";\n        print STDERR \"as the root user. Atlas-shell-tools cannot guarantee that modules installed from\\n\";\n        print STDERR \"external repositories are safe to run with root privileges.\\n\\n\";\n        print STDERR \"To disregard this warning and run as root anyway, please use the option:\\n\";\n        print STDERR \"--allow-run-as-root\\n\\n\";\n        exit 1;\n    }\n}\n\n# Handle the cases where the user supplied a --help flag\n# 1) --help -> print the default help menu and exit\n# 2) --help=TOPIC -> print the TOPIC help menu and exit\nif (defined $help_argument) {\n    show_submanpage_and_exit($help_argument);\n}\n\n# All global options have been processed, so shift the subcommand off of ARGV\nmy $subcommand = shift @ARGV;\n\nunless (defined $subcommand) {\n    ast_utilities::error_output($program_name, \"missing required command or option\");\n    print STDERR \"Try '${bold_stderr}${program_name} --help${reset_stderr}' for more information.\\n\";\n    exit 1;\n}\n\nmy $success = 0;\nif ($subcommand eq \"activate\") {\n    $success = execute_command_activate();\n}\nelsif ($subcommand eq \"deactivate\") {\n    $success = execute_command_deactivate();\n}\nelsif ($subcommand eq \"install\") {\n    $success = execute_command_install();\n}\nelsif ($subcommand eq \"list\") {\n    $success = execute_command_list();\n}\nelsif ($subcommand eq \"log\") {\n    $success = execute_command_log();\n}\nelsif ($subcommand eq \"preset\") {\n    $success = execute_command_preset();\n}\nelsif ($subcommand eq \"repo\") {\n    $success = execute_command_repo();\n}\nelsif ($subcommand eq \"reset\") {\n    $success = execute_command_reset();\n}\nelsif ($subcommand eq \"sync\") {\n    $success = execute_command_sync();\n}\nelsif ($subcommand eq \"uninstall\") {\n    $success = execute_command_uninstall();\n}\nelsif ($subcommand eq \"update\") {\n    $success = execute_command_update();\n}\n# These subcommands are \"hidden\", they are used by the completion scripts\n# to generate autocomplete suggestions.\nelsif ($subcommand eq \"__completion_atlas__\") {\n    $success = ast_completions::completion_atlas($ast_path, 0, \\@ARGV);\n}\nelsif ($subcommand eq \"__completion_atlascfg__\") {\n    $success = ast_completions::completion_atlascfg($ast_path, 0, \\@ARGV);\n}\nelsif ($subcommand eq \"__completion_atlas_zsh__\") {\n    $success = ast_completions::completion_atlas($ast_path, 1, \\@ARGV);\n}\nelsif ($subcommand eq \"__completion_atlascfg_zsh__\") {\n    $success = ast_completions::completion_atlascfg($ast_path, 1, \\@ARGV);\n}\nelse {\n    ast_utilities::error_output($program_name, \"no such command ${bold_stderr}${subcommand}${reset_stderr}\");\n    print STDERR \"Try '${bold_stderr}${program_name} --help${reset_stderr}' for more information.\\n\";\n    exit 127;\n}\n\nif ($success) {\n    exit 0;\n}\nelse {\n    exit 1;\n}\n\n########## END EXECUTION LOGIC ##########\n"
  },
  {
    "path": "atlas-shell-tools/scripts/common/ast_completions.pm",
    "content": "package ast_completions;\n\nuse warnings;\nuse strict;\n\nuse Exporter qw(import);\nuse ast_module_subsystem;\nuse ast_preset_subsystem;\nuse ast_utilities;\nuse ast_tty;\n\n# Export symbols: variables and subroutines\nour @EXPORT = qw(\n    completion_match_prefix\n    completion_atlas\n    completion_atlascfg\n);\n\nmy $FILE_COMPLETE_SENTINEL = \"__atlas-shell-tools_sentinel_complete_filenames__\";\n\nmy $no_colors_stdout = ast_tty::is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_red();\nmy $green_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_green();\nmy $magenta_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_bold();\nmy $bunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_begin_underln();\nmy $eunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_end_underln();\nmy $reset_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_reset();\n\nmy $no_colors_stderr = ast_tty::is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_red();\nmy $green_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_green();\nmy $magenta_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_bold();\nmy $reset_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_reset();\n\nsub completion_match_prefix {\n    my $prefix_to_complete = shift;\n    my $possible_words_ref = shift;\n\n    my @possible_words = @{$possible_words_ref};\n\n    if ($prefix_to_complete eq \"\") {\n        return @possible_words;\n    }\n\n    my @matched_words = ();\n\n    foreach my $word (@possible_words) {\n        if (ast_utilities::string_starts_with($word, $prefix_to_complete)) {\n            push @matched_words, $word;\n        }\n    }\n\n    return @matched_words;\n}\n\n# TODO implement a function that takes in the current argv and returns the name\n# of the current subcommand (stripping away the global options)\n# TODO implement a function that takes in the current argv and returns an array\n# containing just the subcommand and its arguments\n# The above ideas would simplify all the 'rargv_mX' variables\n\nsub completion_atlas {\n    my $ast_path = shift;\n    my $zsh_mode = shift;\n    my $argv_ref = shift;\n\n    my @argv = @{$argv_ref};\n    my %argv_map = map { $_ => 1 } @argv;\n\n    # Shift COMP_CWORD off the front of ARGV\n    my $comp_cword = shift @argv;\n\n    # Shift \"atlas\" off the front of ARGV\n    shift @argv;\n\n    if ($zsh_mode && $comp_cword == (scalar @argv + 1)) {\n        push @argv, \"\";\n    }\n\n    my %subcommand_classes = ast_module_subsystem::get_subcommand_to_class_hash($ast_path);\n    my @commands = keys %subcommand_classes;\n\n    # In the completion code, we use the following conventions to name variables\n    # containing ARGV elements. Assume ARGV looks like the following:\n    #\n    # ARGV of length N:\n    # ARGV[0] ... ARGV[N - K] ... ARGV[N - 3], ARGV[N - 2], ARGV[N - 1]\n    #             ^ rargv_m(k-1)  ^ rargv_m2   ^ rargv_m1   ^ rargv\n    #\n    # Essentially, \"rargv\" means the \"rightmost\" ARGV element. Then \"rargv_m1\"\n    # means the \"rightmost\" ARGV element minus 1, and so on. Since perl plays fast\n    # and loose with array indexing, we can index into elements that may or may\n    # not actually exist, and then check if they are defined before we actually\n    # use them. The '-1' indexing syntax just indexes from the end of the array.\n    #\n    my $argv_len = scalar @argv;\n    my $rargv = $argv[-1];\n    my $rargv_m1 = $argv[-2];\n    my $rargv_m2 = $argv[-3];\n    my $rargv_m3 = $argv[-4];\n\n    # Autocomplete the '--preset' and '--save-preset' flags, since they are probably the most used flags.\n    # Since they are global options, we only complete them if we have not yet seen a command in ARGV.\n    my $saw_command = 0;\n    if (ast_utilities::string_starts_with($rargv, '-')) {\n        foreach my $command (@commands) {\n            if (exists($argv_map{$command})) {\n                $saw_command = 1;\n            }\n        }\n        unless ($saw_command) {\n            my @flags = qw(--preset --save-preset --save-global-preset);\n            my @completion_matches = completion_match_prefix($rargv, \\@flags);\n            print join(\"\\n\", @completion_matches) . \"\\n\";\n            return 1;\n        }\n    }\n\n    # Handle special case where user is applying a preset with \"--preset\"\n    if (defined $rargv_m1) {\n        if ($rargv_m1 eq '-p' || ast_utilities::string_starts_with($rargv_m1, '--p')) {\n            my @presets = ast_preset_subsystem::get_all_presets_in_current_namespace($ast_path);\n            my @completion_matches = completion_match_prefix($rargv, \\@presets);\n            print join(\"\\n\", @completion_matches) . \"\\n\";\n            return 1;\n        }\n    }\n\n    # If we see a command anywhere in ARGV, stop special completions and signal\n    # the completion wrapper script to use its filename defaults.\n    foreach my $command (@commands) {\n        if (exists($argv_map{$command})) {\n            print $FILE_COMPLETE_SENTINEL;\n            return 1;\n        }\n    }\n\n    # Default to completing available command names\n    my @completion_matches = completion_match_prefix($rargv, \\@commands);\n    print join(\"\\n\", @completion_matches) . \"\\n\";\n\n    return 1;\n}\n\nsub completion_atlascfg {\n    my $ast_path = shift;\n    my $zsh_mode = shift;\n    my $argv_ref = shift;\n\n    my @argv = @{$argv_ref};\n\n    # Shift COMP_CWORD off the front of ARGV\n    my $comp_cword = shift @argv;\n\n    # Shift \"atlas-config\" off the front of ARGV\n    shift @argv;\n\n    my %subcommand_classes = ast_module_subsystem::get_subcommand_to_class_hash($ast_path);\n\n    # Shift global options off the front of ARGV\n    foreach my $element (@argv) {\n        if (ast_utilities::string_starts_with($element, '-')) {\n            shift @argv;\n            # We need to decrement $comp_cword, since it will contain an extra\n            # count for each of the global options.\n            $comp_cword = $comp_cword - 1;\n        }\n    }\n\n    if ($zsh_mode && $comp_cword == (scalar @argv + 1)) {\n        push @argv, \"\";\n    }\n\n    # If no more ARGV is left, exit\n    if (scalar @argv == 0) {\n        return 1;\n    }\n\n    my @commands = ();\n    my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n\n    # In the completion code, we use the following conventions to name variables\n    # containing ARGV elements. Assume ARGV looks like the following:\n    #\n    # ARGV of length N:\n    # ARGV[0] ... ARGV[N - K] ... ARGV[N - 3], ARGV[N - 2], ARGV[N - 1]\n    #             ^ rargv_m(k-1)  ^ rargv_m2   ^ rargv_m1   ^ rargv\n    #\n    # Essentially, \"rargv\" means the \"rightmost\" ARGV element. Then \"rargv_m1\"\n    # means the \"rightmost\" ARGV element minus 1, and so on. Since perl plays fast\n    # and loose with array indexing, we can index into elements that may or may\n    # not actually exist, and then check if they are defined before we actually\n    # use them. The '-1' indexing syntax just indexes from the end of the array.\n    #\n    my $rargv = $argv[-1];\n    my $rargv_m1 = $argv[-2];\n    my $rargv_m2 = $argv[-3];\n    my $rargv_m3 = $argv[-4];\n\n    unless (defined $argv[1]) {\n        @commands = qw(activate deactivate install list log preset repo reset sync uninstall update);\n    }\n\n    #\n    # This really long if-else handles all the possible command-lines\n    #\n\n    # 'atlas-config install' command will complete file names\n    if ((defined $argv[0] && $argv[0] eq 'install') && (defined $rargv_m1 && $rargv_m1 eq 'install')) {\n        print $FILE_COMPLETE_SENTINEL;\n        return 1;\n    }\n\n    # 'atlas-config activate' command will complete deactivated modules\n    elsif ((defined $argv[0] && $argv[0]) eq 'activate' && (defined $rargv_m1 && $rargv_m1 eq 'activate')) {\n        @commands = ast_module_subsystem::get_deactivated_modules(\\%modules);\n    }\n\n    # 'atlas-config deactivate' command will complete activated modules\n    elsif ((defined $argv[0] && $argv[0] eq 'deactivate') && (defined $rargv_m1 && $rargv_m1 eq 'deactivate')) {\n        @commands = ast_module_subsystem::get_activated_modules(\\%modules);\n    }\n\n    # 'atlas-config uninstall' command will complete all modules as many times as desired\n    elsif ((defined $argv[0] && $argv[0] eq 'uninstall')) {\n        @commands = keys %modules;\n    }\n\n    # 'atlas-config repo' command will complete repo subcommands\n    elsif ((defined $argv[0] && $argv[0] eq 'repo') && (defined $rargv_m1 && $rargv_m1 eq 'repo')) {\n        @commands = qw(add list remove edit install add-gradle-skip add-gradle-exclude);\n    }\n\n    # 'atlas-config repo remove' command will complete repos\n    elsif ((defined $argv[0] && $argv[0] eq 'repo') && (defined $rargv_m2 && $rargv_m2 eq 'repo') && (defined $rargv_m1 && $rargv_m1 eq 'remove')) {\n        @commands = ast_repo_subsystem::get_all_repos($ast_path);\n    }\n\n    # 'atlas-config repo edit' command will complete repos\n    elsif ((defined $argv[0] && $argv[0] eq 'repo') && (defined $rargv_m2 && $rargv_m2 eq 'repo') && (defined $rargv_m1 && $rargv_m1 eq 'edit')) {\n        @commands = ast_repo_subsystem::get_all_repos($ast_path);\n    }\n\n    # 'atlas-config repo install' command will complete repos\n    elsif ((defined $argv[0] && $argv[0] eq 'repo') && (defined $rargv_m2 && $rargv_m2 eq 'repo') && (defined $rargv_m1 && $rargv_m1 eq 'install')) {\n        @commands = ast_repo_subsystem::get_all_repos($ast_path);\n    }\n\n    # 'atlas-config repo list' command will complete repos\n    elsif ((defined $argv[0] && $argv[0] eq 'repo') && (defined $rargv_m2 && $rargv_m2 eq 'repo') && (defined $rargv_m1 && $rargv_m1 eq 'list')) {\n        @commands = ast_repo_subsystem::get_all_repos($ast_path);\n    }\n\n    # 'atlas-config repo add-gradle-skip' command will complete repos\n    elsif ((defined $argv[0] && $argv[0] eq 'repo') && (defined $rargv_m2 && $rargv_m2 eq 'repo') && (defined $rargv_m1 && $rargv_m1 eq 'add-gradle-skip')) {\n        @commands = ast_repo_subsystem::get_all_repos($ast_path);\n    }\n\n    # 'atlas-config repo add-gradle-exclude' command will complete repos\n    elsif ((defined $argv[0] && $argv[0] eq 'repo') && (defined $rargv_m2 && $rargv_m2 eq 'repo') && (defined $rargv_m1 && $rargv_m1 eq 'add-gradle-exclude')) {\n        @commands = ast_repo_subsystem::get_all_repos($ast_path);\n    }\n\n    # 'atlas-config preset' command will complete 'preset' subcommands\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'preset')) {\n        @commands = qw(save save-global edit edit-global remove remove-global list list-global namespace copy copy-global);\n    }\n\n    # 'atlas-config preset save' command will complete atlas shell tools commands\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'save')) {\n        @commands = keys %subcommand_classes;\n    }\n\n    # 'atlas-config preset edit' command will complete atlas shell tools commands\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'edit')) {\n        @commands = keys %subcommand_classes;\n    }\n\n    # 'atlas-config preset edit <command>' command will complete any presets for <command>\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m3 && $rargv_m3 eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'edit') && (defined $rargv_m1)) {\n        @commands = ast_preset_subsystem::get_all_presets_for_command($ast_path, $rargv_m1);\n    }\n\n    # 'atlas-config preset edit-global' command will complete any global presets\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'edit-global')) {\n        @commands = ast_preset_subsystem::get_all_presets_for_command($ast_path, $ast_preset_subsystem::GLOBAL_FOLDER);\n    }\n\n    # 'atlas-config preset remove' command will complete atlas shell tools commands\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'remove')) {\n        @commands = keys %subcommand_classes;\n    }\n\n    # 'atlas-config preset remove <command>' command will complete any presets for <command>\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m3 && $rargv_m3 eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'remove') && (defined $rargv_m1)) {\n        @commands = ast_preset_subsystem::get_all_presets_for_command($ast_path, $rargv_m1);\n    }\n\n    # 'atlas-config preset remove-global' command will complete any global presets\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'remove-global')) {\n        @commands = ast_preset_subsystem::get_all_presets_for_command($ast_path, $ast_preset_subsystem::GLOBAL_FOLDER);\n    }\n\n    # 'atlas-config preset list' command will complete atlas shell tools commands\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'list')) {\n        @commands = keys %subcommand_classes;\n    }\n\n    # 'atlas-config preset list <command>' command will complete any presets for <command>\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m3 && $rargv_m3 eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'list') && (defined $rargv_m1)) {\n        @commands = ast_preset_subsystem::get_all_presets_for_command($ast_path, $rargv_m1);\n    }\n\n    # 'atlas-config preset list-global' will complete any global presets\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'list-global')) {\n        @commands = ast_preset_subsystem::get_all_presets_for_command($ast_path, $ast_preset_subsystem::GLOBAL_FOLDER);\n    }\n\n    # 'atlas-config preset namespace' command will complete 'namespace' subcommands\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'namespace')) {\n        @commands = qw(create remove list use);\n    }\n\n    # 'atlas-config preset namespace remove' command will complete namespaces\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m3 && $rargv_m3 eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'namespace') && (defined $rargv_m1 && $rargv_m1 eq 'remove')) {\n        @commands = ast_preset_subsystem::get_namespaces_array($ast_path);\n    }\n\n    # 'atlas-config preset namespace list' command will complete namespaces\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m3 && $rargv_m3 eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'namespace') && (defined $rargv_m1 && $rargv_m1 eq 'list')) {\n        @commands = ast_preset_subsystem::get_namespaces_array($ast_path);\n    }\n\n    # 'atlas-config preset namespace use' command will complete namespaces\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m3 && $rargv_m3 eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'namespace') && (defined $rargv_m1 && $rargv_m1 eq 'use')) {\n        @commands = ast_preset_subsystem::get_namespaces_array($ast_path);\n    }\n\n    # 'atlas-config preset copy' command will complete atlas shell tools commands\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'copy')) {\n        @commands = keys %subcommand_classes;\n    }\n\n    # 'atlas-config preset copy <command>' command will complete any presets for <command>\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m3 && $rargv_m3 eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'copy') && (defined $rargv_m1)) {\n        @commands = ast_preset_subsystem::get_all_presets_for_command($ast_path, $rargv_m1);\n    }\n\n    # 'atlas-config preset copy-global' command will complete any global presets\n    elsif ((defined $argv[0] && $argv[0] eq 'preset') && (defined $rargv_m2 && $rargv_m2 eq 'preset') && (defined $rargv_m1 && $rargv_m1 eq 'copy-global')) {\n        @commands = ast_preset_subsystem::get_all_presets_for_command($ast_path, $ast_preset_subsystem::GLOBAL_FOLDER);\n    }\n\n    # 'atlas-config log' command will complete log subcommands\n    elsif ((defined $argv[0] && $argv[0] eq 'log') && (defined $rargv_m1 && $rargv_m1 eq 'log')) {\n        @commands = qw(reset set-level set-stream show);\n    }\n\n    # 'atlas-config log set-level' command will complete log levels\n    elsif ((defined $argv[0] && $argv[0] eq 'log') && (defined $rargv_m2 && $rargv_m2 eq 'log') && (defined $rargv_m1 && $rargv_m1 eq 'set-level')) {\n        @commands = qw(ALL TRACE DEBUG INFO WARN ERROR FATAL OFF);\n    }\n\n    # 'atlas-config log set-stream' command will complete log streams\n    elsif ((defined $argv[0] && $argv[0] eq 'log') && (defined $rargv_m2 && $rargv_m2 eq 'log') && (defined $rargv_m1 && $rargv_m1 eq 'set-stream')) {\n        @commands = qw(stdout stderr);\n    }\n\n    # 'atlas-config reset' command will complete reset subcommands\n    elsif ((defined $argv[0] && $argv[0] eq 'reset') && (defined $rargv_m1 && $rargv_m1 eq 'reset')) {\n        @commands = qw(all index log modules presets repos);\n    }\n\n    # Generate completion matches based on prefix of current word\n    my @completion_matches = completion_match_prefix($rargv, \\@commands);\n    print join(\"\\n\", @completion_matches) . \"\\n\";\n\n    return 1;\n}\n\nsub debug_dump_string {\n    my $string = shift;\n    my $file = shift;\n    open my $handle, '>>', $file;\n    print $handle $string;\n    close $handle;\n}\n\n# Perl modules must return a value. Returning a value perl considers \"truthy\"\n# signals that the module loaded successfully.\n1;\n"
  },
  {
    "path": "atlas-shell-tools/scripts/common/ast_log_subsystem.pm",
    "content": "package ast_log_subsystem;\n\nuse warnings;\nuse strict;\n\nuse Exporter qw(import);\nuse File::Spec;\nuse ast_tty;\nuse ast_utilities;\n\n# Export symbols: variables and subroutines\nour @EXPORT = qw(\n    LOG4J_FOLDER\n    LOG4J_FILE\n    LOG4J_FILE_PATH\n    DEFAULT_LOG4J_CONTENTS\n    reset_log4j\n    read_loglevel_from_file\n    read_logstream_from_file\n    replace_loglevel_in_file\n    replace_logstream_in_file\n);\n\nmy $no_colors_stdout = ast_tty::is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_red();\nmy $green_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_green();\nmy $magenta_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_bold();\nmy $bunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_begin_underln();\nmy $eunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_end_underln();\nmy $reset_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_reset();\n\nmy $no_colors_stderr = ast_tty::is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_red();\nmy $green_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_green();\nmy $magenta_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_bold();\nmy $reset_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_reset();\n\nour $LOG4J_FOLDER = 'log4j';\nour $LOG4J_FILE = 'log4j.properties';\nour $LOG4J_FILE_PATH = File::Spec->catfile($LOG4J_FOLDER, $LOG4J_FILE);\n\n# The default setting for the log4j file.\nmy $DEFAULT_LOG4J_CONTENTS = \"log4j.rootLogger=ERROR, stderr\n# DO NOT REMOVE/MODIFY THE ABOVE LINE OR ANY OF THIS FILE\n# Use 'atlas-config log' subcommand to manage the log configuration\n# If this file is corrupted, use 'atlas-config log --reset' to fix.\n\n# Direct log messages to stderr\nlog4j.appender.stderr=org.apache.log4j.ConsoleAppender\nlog4j.appender.stderr.Target=System.err\nlog4j.appender.stderr.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stderr.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n\n\";\n\n# Reset the log4j file to default.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: none\nsub reset_log4j {\n    my $ast_path = shift;\n    my $log4j_file = File::Spec->catfile($ast_path, $LOG4J_FOLDER, $LOG4J_FILE);\n    open my $file_handle, '>', \"$log4j_file\";\n    print $file_handle $DEFAULT_LOG4J_CONTENTS;\n    close $file_handle;\n}\n\n# Read the log level from a given logfile. The log file should be a log4j file\n# where the first line looks like:\n#    log4j.rootLogger=LEVEL, STREAM\n#Params:\n#   $logfile_path: the path to the log4j file\n# Return: the string level, eg. DEBUG, WARN, ERROR, etc.\nsub read_loglevel_from_file {\n    my $logfile_path = shift;\n\n    # Grab the first line and chomp the trailing newline.\n    open my $logfile, '<', $logfile_path or die \"could not read logfile from path $logfile_path\";\n    my $firstline = <$logfile>;\n    chomp $firstline;\n    close $logfile;\n\n    # firstline should now look like\n    # log4j.rootLogger=LEVEL, STREAM\n    my @split1 = split('=', $firstline);\n    my @split2 = split(',', $split1[1]);\n    my $level = $split2[0];\n    # remove leading and trailing whitespace\n    $level =~ s/^\\s+|\\s+$//g;\n\n    return $level;\n}\n\n# Read the log stream from a given logfile. The log file should be a log4j file\n# where the first line looks like:\n#    log4j.rootLogger=LEVEL, STREAM\n#Params:\n#   $logfile_path: the path to the log4j file\n# Return: the string stream, eg. stdout or stderr\nsub read_logstream_from_file {\n    my $logfile_path = shift;\n\n    # Grab the first line and chomp the trailing newline.\n    open my $logfile, '<', $logfile_path or die \"could not read logfile from path $logfile_path\";\n    my $firstline = <$logfile>;\n    chomp $firstline;\n    close $logfile;\n\n    # firstline should now look like\n    # log4j.rootLogger=LEVEL, STREAM\n    my @split1 = split('=', $firstline);\n    my @split2 = split(',', $split1[1]);\n    my $stream = $split2[1];\n    # remove leading and trailing whitespace\n    $stream =~ s/^\\s+|\\s+$//g;\n\n    return $stream;\n}\n\n# Replace the log level in a given logfile. The log file should be a log4j file\n# where the first line looks like:\n#    log4j.rootLogger=LEVEL, stdout\n# Note that this function will not validate that the new level is actually a\n# valid log4j level. This is left up to the caller.\n#Params:\n#   $logfile_path: the path to the log4j file\n#   $new_level: the new level\n# Return: none\nsub replace_loglevel_in_file {\n    my $logfile_path = shift;\n    my $new_level = shift;\n\n    # read the old level that we are replacing\n    my $old_level = read_loglevel_from_file($logfile_path);\n\n    # create a backup of the old log4j file\n    rename $logfile_path, $logfile_path . '.bak';\n    open my $logfileIN, '<', $logfile_path . '.bak';\n    open my $logfileOUT, '>', $logfile_path;\n\n    # replace the level\n    my $firstline = <$logfileIN>;\n    $firstline =~ s/${old_level}/${new_level}/;\n    print $logfileOUT $firstline;\n\n    # write the rest of the log4j file\n    while (<$logfileIN>) {\n        print $logfileOUT $_;\n    }\n\n    close $logfileIN;\n    close $logfileOUT;\n}\n\n# Replace the log stream in a given logfile. The log file should be a log4j file\n# where the first line looks like:\n#    log4j.rootLogger=LEVEL, STREAM\n# This function will die if the provided stream is not 'stdout' or 'stderr'.\n# This is necessary because we must compute stderr -> System.err and stdout ->\n# System.out to properly update the log4j file.\n#Params:\n#   $logfile_path: the path to the log4j file\n#   $new_stream: the new stream\n# Return: none\nsub replace_logstream_in_file {\n    my $logfile_path = shift;\n    my $new_stream = shift;\n\n    # read the old stream that we are replacing\n    my $old_stream = read_logstream_from_file($logfile_path);\n\n    my $old_system;\n    my $new_system;\n    if ($new_stream eq 'stdout') {\n        $old_system = 'System\\.err';\n        $new_system = 'System.out';\n    }\n    elsif ($new_stream eq 'stderr') {\n        $old_system = 'System\\.out';\n        $new_system = 'System.err';\n    }\n    else {\n        die \"Invalid stream setting $new_stream, must be stdout or stderr\";\n    }\n\n    # create a backup of the old log4j file\n    rename $logfile_path, $logfile_path . '.bak';\n    open my $logfileIN, '<', $logfile_path . '.bak';\n    open my $logfileOUT, '>', $logfile_path;\n\n    while (<$logfileIN>) {\n        $_ =~ s/${old_stream}/${new_stream}/g;\n        $_ =~ s/${old_system}/${new_system}/g;\n        print $logfileOUT $_;\n    }\n\n    close $logfileIN;\n    close $logfileOUT;\n}\n\n# Perl modules must return a value. Returning a value perl considers \"truthy\"\n# signals that the module loaded successfully.\n1;\n"
  },
  {
    "path": "atlas-shell-tools/scripts/common/ast_module_subsystem.pm",
    "content": "package ast_module_subsystem;\n\nuse warnings;\nuse strict;\n\nuse Exporter qw(import);\nuse File::Basename qw(basename);\nuse File::Spec;\nuse ast_utilities;\nuse ast_tty;\nuse ast_log_subsystem;\n\n# Export symbols: variables and subroutines\nour @EXPORT = qw(\n    MODULE_SUFFIX\n    DEACTIVATED_SUFFIX\n    DEACTIVATED_MODULE_SUFFIX\n    MODULES_FOLDER\n    ACTIVE_INDEX_FILE\n    ACTIVE_INDEX_PATH\n    ACTIVATED\n    DEACTIVATED\n    GOOD_SYMLINK\n    BROKEN_SYMLINK\n    REAL_FILE\n    SOURCE_KEY\n    URI_KEY\n    DATE_TIME_KEY\n    REPO_NAME_KEY\n    REPO_REF_KEY\n    REPO_COMMIT_KEY\n    METADATA_SEPARATOR\n    get_subcommand_to_class_hash\n    get_subcommand_to_description_hash\n    get_module_to_status_hash\n    get_module_to_symlink_hash\n    get_module_to_target_hash\n    get_module_to_metadata_hash\n    get_activated_modules\n    get_deactivated_modules\n    perform_uninstall\n    perform_activate\n    perform_deactivate\n    generate_active_module_index\n    remove_active_module_index\n);\n\nour $METADATA_SUFFIX = '.metadata';\nour $MODULE_SUFFIX = '.jar';\nour $DEACTIVATED_SUFFIX = '.deactivated';\nour $DEACTIVATED_MODULE_SUFFIX = $MODULE_SUFFIX . $DEACTIVATED_SUFFIX;\nour $MODULES_FOLDER = 'modules';\nour $ACTIVE_INDEX_FILE = '.active_module_index';\nour $ACTIVE_INDEX_PATH = File::Spec->catfile($MODULES_FOLDER, $ACTIVE_INDEX_FILE);\n\nour $ACTIVATED = 1;\nour $DEACTIVATED = 0;\n\nour $GOOD_SYMLINK = 1;\nour $BROKEN_SYMLINK = -1;\nour $REAL_FILE = 0;\n\n# Module metadata keys/data\nour $SOURCE_KEY = \"source-type\";\nour $URI_KEY = \"URI\";\nour $DATE_TIME_KEY = \"date-time\";\nour $REPO_NAME_KEY = \"repo-name\";\nour $REPO_REF_KEY = \"repo-ref\";\nour $REPO_COMMIT_KEY = \"repo-commit\";\nour $METADATA_SEPARATOR = \":\";\n\n# Use ASCII record separator as delimiter.\n# This is also defined in Atlas class ActiveModuleIndexWriter.\nmy $INDEX_DELIMITER = \"\\x1E\";\n\n# The Java class that creates the active_module_index\nmy $INDEX_WRITER_CLASS = \"org.openstreetmap.atlas.utilities.command.ActiveModuleIndexWriter\";\n\nmy $no_colors_stdout = ast_tty::is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_red();\nmy $green_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_green();\nmy $magenta_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_bold();\nmy $bunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_begin_underln();\nmy $eunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_end_underln();\nmy $reset_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_reset();\n\nmy $no_colors_stderr = ast_tty::is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_red();\nmy $green_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_green();\nmy $magenta_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_bold();\nmy $reset_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_reset();\n\n# Get a hash that maps subcommand names to their respective classes. The hash is\n# computed from the current active module index.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: a hash of all subcommands to their classes.\nsub get_subcommand_to_class_hash {\n    my $ast_path = shift;\n\n    my $index_path = File::Spec->catfile($ast_path, $ACTIVE_INDEX_PATH);\n    open my $index_fileIN, '<', $index_path or return();\n    my %subcommand_to_class = ();\n\n    while (<$index_fileIN>) {\n        my $line = $_;\n        chomp $line;\n        my @line_elements = split($INDEX_DELIMITER, $line);\n        $subcommand_to_class{$line_elements[0]} = $line_elements[1];\n    }\n\n    return %subcommand_to_class;\n}\n\n# Get a hash that maps subcommand names to their respective descriptions. The\n# hash is computed from the current active module index.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: a hash of all subcommands to their descriptions.\nsub get_subcommand_to_description_hash {\n    my $ast_path = shift;\n\n    my $index_path = File::Spec->catfile($ast_path, $ACTIVE_INDEX_PATH);\n    open my $index_fileIN, '<', $index_path or return();\n    my %subcommand_to_description = ();\n\n    while (<$index_fileIN>) {\n        my $line = $_;\n        chomp $line;\n        my @line_elements = split($INDEX_DELIMITER, $line);\n        $subcommand_to_description{$line_elements[0]} = $line_elements[2];\n    }\n\n    return %subcommand_to_description;\n}\n\n# Get a hash that maps all present module names to their activation status.\n# Activated modules are mapped to ACTIVATED, while deactivated modules are mapped\n# to DEACTIVATED.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: a hash of all modules to their activation status.\nsub get_module_to_status_hash {\n    my $ast_path = shift;\n\n    my @find_command = (\n        \"find\", \"${ast_path}/${MODULES_FOLDER}\",\n        \"-maxdepth\", \"1\",\n        \"(\", \"-name\", \"*$MODULE_SUFFIX\", \"-o\", \"-name\", \"*$DEACTIVATED_SUFFIX\", \")\",\n        \"-print0\"\n    );\n    my %modules = ();\n    open FIND, \"-|\", @find_command;\n    # TODO 'local' modifier makes sense here? confirm this, 'my' may make more sense\n    # see https://www.perlmonks.org/?node_id=94007\n    local $/ = \"\\0\";\n    while (<FIND>) {\n        # FIND command is printing full paths, we just want the basename.\n        # Also, we must chomp to remove the terminating null byte left over from\n        # the '-print0' flag given to 'find'.\n        my $module = $_;\n        chomp $module;\n        $module = basename($module);\n\n        my $module_activated;\n\n        # TODO: figure out how to interpolate $DEACTIVATED_SUFFIX into this regex\n        if ($module =~ /.*\\.deactivated$/) {\n            $module_activated = $DEACTIVATED;\n        }\n        else {\n            $module_activated = $ACTIVATED;\n        }\n        $module =~ s{$MODULE_SUFFIX}{};\n        $module =~ s{$DEACTIVATED_SUFFIX}{};\n        $modules{$module} = $module_activated;\n    }\n    close FIND;\n\n    return %modules;\n}\n\n# Get a hash that maps all present module names to their symlink state.\n# Symlinked modules are mapped to GOOD_SYMLINK, regular modules are mapped to\n# REAL_FILE. Broken symlink modules are mapped to BROKEN_SYMLINK.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: a hash of all modules to their symlink state.\nsub get_module_to_symlink_hash {\n    my $ast_path = shift;\n\n    my @find_command = (\n        \"find\", \"${ast_path}/${MODULES_FOLDER}\",\n        \"-maxdepth\", \"1\",\n        \"(\", \"-name\", \"*$MODULE_SUFFIX\", \"-o\", \"-name\", \"*$DEACTIVATED_SUFFIX\", \")\",\n        \"-print0\"\n    );\n    my %modules = ();\n    open FIND, \"-|\", @find_command;\n    local $/ = \"\\0\";\n    while (<FIND>) {\n        # FIND command is printing full paths, we just want the basename.\n        # Also, we must chomp to remove the terminating null byte left over from\n        # the '-print0' flag given to 'find'.\n        my $module = $_;\n        chomp $module;\n        $module = basename($module);\n\n        my $module_islink;\n\n        if (-l $_) {\n            if (lstat $_ and not stat $_) {\n                $module_islink = $BROKEN_SYMLINK;\n            }\n            else {\n                $module_islink = $GOOD_SYMLINK;\n            }\n        }\n        else {\n            $module_islink = $REAL_FILE;\n        }\n        $module =~ s{$MODULE_SUFFIX}{};\n        $module =~ s{$DEACTIVATED_SUFFIX}{};\n        $modules{$module} = $module_islink;\n    }\n    close FIND;\n\n    return %modules;\n}\n\n# Get a hash that maps all present module names to their symlink target.\n# If a module is not a symlink, it maps to an empty string.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: a hash of all modules to their symlink target.\nsub get_module_to_target_hash {\n    my $ast_path = shift;\n\n    my @find_command = (\n        \"find\", \"${ast_path}/${MODULES_FOLDER}\",\n        \"-maxdepth\", \"1\",\n        \"(\", \"-name\", \"*$MODULE_SUFFIX\", \"-o\", \"-name\", \"*$DEACTIVATED_SUFFIX\", \")\",\n        \"-print0\"\n    );\n    my %modules = ();\n    open FIND, \"-|\", @find_command;\n    local $/ = \"\\0\";\n    while (<FIND>) {\n        # FIND command is printing full paths, we just want the basename.\n        # Also, we must chomp to remove the terminating null byte left over from\n        # the '-print0' flag given to 'find'.\n        my $module = $_;\n        chomp $module;\n        $module = basename($module);\n\n        my $module_target;\n\n        if (-l $_) {\n            $module_target = readlink $_;\n        }\n        else {\n            $module_target = '';\n        }\n        $module =~ s{$MODULE_SUFFIX}{};\n        $module =~ s{$DEACTIVATED_SUFFIX}{};\n        $modules{$module} = $module_target;\n    }\n    close FIND;\n\n    return %modules;\n}\n\n# Get a hash that maps all present module names to their metadata hashes, if present.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: a hash of all modules to their metadata hashes.\nsub get_module_to_metadata_hash {\n    my $ast_path = shift;\n\n    my @find_command = (\n        \"find\", \"${ast_path}/${MODULES_FOLDER}\",\n        \"-maxdepth\", \"1\",\n        \"-name\", \"*$METADATA_SUFFIX\",\n        \"-print0\"\n    );\n    my %modules = ();\n    open FIND, \"-|\", @find_command;\n    local $/ = \"\\0\";\n    while (<FIND>) {\n        # FIND command is printing full paths, we just want the basename.\n        # Also, we must chomp to remove the terminating null byte left over from\n        # the '-print0' flag given to 'find'.\n        my $module = $_;\n        chomp $module;\n        my $module_basename = basename($module);\n        $module_basename =~ s{$METADATA_SUFFIX}{};\n\n        my %metadata;\n        open my $file_handle, '<', $module or die \"Could not open module metadata file $module $!\";\n        # we have to change the separator to newline in order to parse the file properly\n        $/ = \"\\n\";\n        while (my $line = <$file_handle>) {\n            chomp $line;\n            # trim excess whitespace from left and right\n            $line =~ s/^\\s+|\\s+$//g;\n            if ($line eq '' || substr($line, 0, 1) eq '#') {\n                next;\n            }\n            my @line_split = split $METADATA_SEPARATOR, $line, 2;\n            unless (defined $line_split[0]) {\n                next;\n            }\n            # trim excess whitespace from left and right\n            $line_split[0] =~ s/^\\s+|\\s+$//g;\n            if (defined $line_split[1] && $line_split[1] !~ /^\\s*$/) {\n                # trim excess whitespace from left and right\n                $line_split[1] =~ s/^\\s+|\\s+$//g;\n                $metadata{$line_split[0]} = $line_split[1];\n            }\n        }\n        close $file_handle;\n        $modules{$module_basename} = \\%metadata;\n        # change the separator back to \\0 for FIND\n        $/ = \"\\0\";\n    }\n    close FIND;\n\n    return %modules;\n}\n\n# Get an array of all active modules, computed from the modules hash returned by\n# the 'get_module_to_status_hash' subroutine.\n# Params:\n#   $modules_ref: a reference to the module hash returned by 'get_module_to_status_hash'\n# Return: an array of all active modules\nsub get_activated_modules {\n    my $modules_ref = shift;\n\n    my %modules = %{$modules_ref};\n    my @activated_modules = ();\n\n    foreach my $module (keys %modules) {\n        if ($modules{$module} == $ACTIVATED) {\n            push @activated_modules, $module;\n        }\n    }\n\n    return @activated_modules;\n}\n\n# Get an array of all deactived modules, computed from the modules hash returned by\n# the 'get_module_to_status_hash' subroutine.\n# Params:\n#   $modules_ref: a reference to the module hash returned by 'get_module_to_status_hash'\n# Return: an array of all deactived modules\nsub get_deactivated_modules {\n    my $modules_ref = shift;\n\n    my %modules = %{$modules_ref};\n    my @deactivated_modules = ();\n\n    foreach my $module (keys %modules) {\n        if ($modules{$module} == $DEACTIVATED) {\n            push @deactivated_modules, $module;\n        }\n    }\n\n    return @deactivated_modules;\n}\n\n# Install a module using some given parameters.\n# Params:\n#   $module_to_install: the path to the module to install\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the running program\n#   $alternate_name: an alternate name for the module, empty to use path basename\n#   $syminstall: install using a symlink instead of a copy\n#   $skip_install: skip installation if module exists\n#   $force_install: force overwrite if module exists\n#   $install_deactivated: install module but do not activate\n#   $metadata_ref: a reference to a metadata hash\n#   $quiet: suppress non-essential output\n#\n# Return: 1 on success, 0 on failure\nsub perform_install {\n    my $module_to_install = shift;\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $alternate_name = shift;\n    my $syminstall = shift;\n    my $skip_install = shift;\n    my $force_install = shift;\n    my $install_deactivated = shift;\n    my $metadata_ref = shift;\n    my $quiet = shift;\n\n    unless (-f $module_to_install || -l $module_to_install) {\n        ast_utilities::error_output($program_name, \"no such file ${bold_stderr}${module_to_install}${reset_stderr}\");\n        return 0;\n    }\n\n    my $modules_folder = File::Spec->catfile($ast_path, $MODULES_FOLDER);\n\n    # Create the module name, respecting the alternate name if provided.\n    my $module_basename;\n    if (!$alternate_name eq \"\") {\n        $module_basename = $alternate_name . $MODULE_SUFFIX;\n    }\n    else {\n        $module_basename = basename($module_to_install);\n    }\n    # TODO: figure out how to interpolate $MODULE_SUFFIX into this regex\n    unless ($module_basename =~ /.*\\.jar$/) {\n        ast_utilities::error_output($program_name, \"module must end with '.jar' extension\");\n        return 0;\n    }\n    $module_basename =~ s{$MODULE_SUFFIX}{};\n\n    # Handle the case where the module is already installed\n    my %modules = get_module_to_status_hash($ast_path);\n    if (defined $modules{$module_basename}) {\n        ast_utilities::warn_output($program_name, \"module ${bold_stderr}${module_basename}${reset_stderr} is already installed\");\n        if ($skip_install) {\n            print STDERR \"Skipping installation.\\n\";\n            return 0;\n        }\n        unless ($force_install) {\n            my $overwrite = ast_utilities::prompt_yn(\"Overwrite?\");\n            unless ($overwrite) {\n                print STDERR \"Skipping installation.\\n\";\n                return 0;\n            }\n        }\n        ast_utilities::warn_output($program_name, \"overwriting ${bold_stderr}${module_basename}${reset_stderr}\");\n    }\n\n    # Construct the new module paths.\n    # We create a path for both an activated and deactivated version.\n    my $module_new_path =\n        File::Spec->catfile($modules_folder, $module_basename . $MODULE_SUFFIX);\n    my $module_new_path_deactivated =\n        File::Spec->catfile($modules_folder, $module_basename . $DEACTIVATED_MODULE_SUFFIX);\n\n    # If we made it here we are go to overwrite, so clean up any matching existing modules.\n    unlink $module_new_path;\n    unlink $module_new_path_deactivated;\n\n    my $exitcode;\n    if ($syminstall) {\n        my $module_to_install_abs = Cwd::realpath($module_to_install);\n        my $module_to_install_rel = File::Spec->abs2rel($module_to_install_abs, $modules_folder);\n\n        if ($install_deactivated) {\n            symlink($module_to_install_rel, $module_new_path_deactivated);\n        }\n        else {\n            symlink($module_to_install_rel, $module_new_path);\n        }\n        $exitcode = $? >> 8;\n    }\n    else {\n        my @command = ();\n        push @command, \"cp\";\n        push @command, \"$module_to_install\";\n        if ($install_deactivated) {\n            push @command, \"$module_new_path_deactivated\";\n            system {$command[0]} @command;\n        }\n        else {\n            push @command, \"$module_new_path\";\n            system {$command[0]} @command;\n        }\n        $exitcode = $? >> 8;\n    }\n\n    if ($exitcode) {\n        print STDERR \"${red_stderr}${bold_stderr}Installation of module ${module_basename} failed.${reset_stderr} Operation exited with $exitcode.\\n\";\n        return 0;\n    }\n    else {\n        unless ($install_deactivated) {\n            my %modules = get_module_to_status_hash($ast_path);\n            foreach my $module (keys %modules) {\n                if ($modules{$module} == $ACTIVATED\n                    && $module ne $module_basename) {\n                    my $success = perform_deactivate($module, $ast_path, $program_name, $quiet);\n                    unless ($success) {\n                        ast_utilities::warn_output($program_name, \"installation may not function properly\");\n                    }\n                }\n            }\n            remove_active_module_index($ast_path, $program_name, $quiet);\n            my $success = generate_active_module_index($ast_path, $program_name, $quiet, 0);\n            unless ($success) {\n                ast_utilities::warn_output($program_name, \"partial installation may not function properly\");\n            }\n        }\n\n        my $success = write_module_metadata($modules_folder, $module_basename, $metadata_ref, $program_name);\n        unless ($success) {\n            ast_utilities::warn_output($program_name, \"metadata not written\");\n        }\n\n        unless ($quiet) {\n            print \"Module ${green_stdout}${bold_stdout}${module_basename}${reset_stdout} installed.\\n\";\n        }\n    }\n\n    return 1;\n}\n\n# Uninstall a module with a given name.\n# Params:\n#   $module_to_uninstall: the name of the module to uninstall\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the running program\n#   $quiet: suppress non-essential output\n#   $force: force uninstall on activated module\n# Return: 1 on success, 0 on failure\nsub perform_uninstall {\n    my $module_to_uninstall = shift;\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $force = shift;\n\n    my %modules = get_module_to_status_hash($ast_path);\n\n    unless (exists $modules{$module_to_uninstall}) {\n        error_output($program_name, \"no such module ${bold_stderr}${module_to_uninstall}${reset_stderr}\");\n        return 0;\n    }\n\n    if ($modules{$module_to_uninstall} eq $ACTIVATED && $force != 1) {\n        warn_output($program_name, \"skipping activated module ${bold_stderr}${module_to_uninstall}${reset_stderr}\");\n        return 0;\n    }\n\n    # try to remove the module\n    my $modules_folder = File::Spec->catfile($ast_path, $MODULES_FOLDER);\n    my $module_remove_path =\n        File::Spec->catfile($modules_folder, $module_to_uninstall . $MODULE_SUFFIX);\n    my $module_remove_path_deactivated =\n        File::Spec->catfile($modules_folder, $module_to_uninstall . $DEACTIVATED_MODULE_SUFFIX);\n    my $module_remove_path_metadata =\n        File::Spec->catfile($modules_folder, $module_to_uninstall . $METADATA_SUFFIX);\n\n    unlink $module_remove_path;\n    unlink $module_remove_path_deactivated;\n    unlink $module_remove_path_metadata;\n    unless ($quiet) {\n        print \"Module ${bold_stdout}${module_to_uninstall}${reset_stdout} uninstalled.\\n\";\n    }\n\n    return 1;\n}\n\n# Activate a module with a given name.\n# Params:\n#   $module_to_activate: the name of the module to activate\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the running program\n#   $quiet: suppress non-essential messages\n# Return: 1 on success, 0 on failure\nsub perform_activate {\n    my $module_to_activate = shift;\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n\n    my %modules = get_module_to_status_hash($ast_path);\n\n    unless (exists $modules{$module_to_activate}) {\n        ast_utilities::error_output($program_name, \"no such module ${bold_stderr}${module_to_activate}${reset_stderr}\");\n        return 0;\n    }\n\n    my $status = $modules{$module_to_activate};\n    if ($status == $ACTIVATED) {\n        ast_utilities::warn_output($program_name, \"module ${bold_stderr}${module_to_activate}${reset_stderr} already activated\");\n        return 1;\n    }\n\n    # We made it here, so we are good to activate the module!\n    # Effectively, this just means removing the DEACTIVATED_MODULE_SUFFIX\n    my $module_path_nosuffix = File::Spec->catfile($ast_path, $MODULES_FOLDER, $module_to_activate);\n    my $old_module_path = $module_path_nosuffix . $DEACTIVATED_MODULE_SUFFIX;\n    my $new_module_path = $module_path_nosuffix . $MODULE_SUFFIX;\n\n    rename $old_module_path, $new_module_path;\n    my $exitcode = $? >> 8;\n    if ($exitcode) {\n        print STDERR \"${red_stderr}${bold_stderr}Activation of module ${module_to_activate} failed.${reset_stderr} Rename exited with $exitcode.\\n\";\n        return 0;\n    }\n    else {\n        remove_active_module_index($ast_path, $program_name, $quiet);\n        generate_active_module_index($ast_path, $program_name, $quiet, 0);\n        unless ($quiet) {\n            print \"Module ${green_stdout}${bold_stdout}${module_to_activate}${reset_stdout} activated.\\n\";\n        }\n    }\n\n    return 1;\n}\n\n# Deactivate a module with a given name.\n# Params:\n#   $module_to_deactivate: the name of the module to deactivate\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the running program\n#   $quiet: suppress non-essential messages\n# Return: 1 on success, 0 on failure\nsub perform_deactivate {\n    my $module_to_deactivate = shift;\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n\n    my %modules = get_module_to_status_hash($ast_path);\n\n    unless (exists $modules{$module_to_deactivate}) {\n        ast_utilities::error_output($program_name, \"no such module ${bold_stderr}${module_to_deactivate}${reset_stderr}\");\n        return 0;\n    }\n\n    my $status = $modules{$module_to_deactivate};\n    if ($status == $DEACTIVATED) {\n        ast_utilities::warn_output($program_name, \"module ${bold_stderr}${module_to_deactivate}${reset_stderr} already deactivated\");\n        return 0;\n    }\n\n    # We made it here, so we are good to deactivate the module!\n    # Effectively, this just means adding the DEACTIVATED_MODULE_SUFFIX\n    my $module_path_nosuffix = File::Spec->catfile($ast_path, $MODULES_FOLDER, $module_to_deactivate);\n    my $old_module_path = $module_path_nosuffix . $MODULE_SUFFIX;\n    my $new_module_path = $module_path_nosuffix . $DEACTIVATED_MODULE_SUFFIX;\n\n    rename $old_module_path, $new_module_path;\n    my $exitcode = $? >> 8;\n    if ($exitcode) {\n        print STDERR \"${red_stderr}${bold_stderr}Deactivation of module ${module_to_deactivate} failed.${reset_stderr} Rename exited with $exitcode.\\n\";\n        return 0;\n    }\n    else {\n        unless ($quiet) {\n            print \"Module ${bold_stdout}${module_to_deactivate}${reset_stdout} deactivated.\\n\";\n        }\n    }\n\n    return 1;\n}\n\n# Create the active module index for the currently activated module.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $verbose_java: make the Java table printer use verbose output\n# Return: 1 on success, 0 on failure\nsub generate_active_module_index {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $verbose_java = shift;\n\n    my %modules = get_module_to_status_hash($ast_path);\n    my @activated_modules = get_activated_modules(\\%modules);\n\n    my $module = $activated_modules[0];\n    my $full_path_to_modules_folder = File::Spec->catfile($ast_path, $MODULES_FOLDER, \"$module\" . $MODULE_SUFFIX);\n    my $full_path_to_log4j = File::Spec->catfile($ast_path, $ast_log_subsystem::LOG4J_FILE_PATH);\n    my $full_index_path = File::Spec->catfile($ast_path, $ACTIVE_INDEX_PATH);\n    my @java_command = ();\n    push @java_command, \"java\";\n    push @java_command, \"-Xms2G\";\n    push @java_command, \"-Xmx2G\";\n    push @java_command, \"-cp\";\n    push @java_command, \"${full_path_to_modules_folder}\";\n    push @java_command, \"-Dlog4j.configuration=file:${full_path_to_log4j}\";\n    push @java_command, \"${INDEX_WRITER_CLASS}\";\n    push @java_command, \"${full_index_path}\";\n\n    if ($verbose_java && !$quiet) {\n        push @java_command, \"--verbose\";\n    }\n\n    if (scalar @activated_modules == 0) {\n        ast_utilities::error_output($program_name, 'could not generate index');\n        print STDERR \"No active modules found\\n\";\n        print STDERR \"Try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} list${reset_stderr}' to see all installed modules.\\n\";\n        print STDERR \"Then try '${bold_stderr}${ast_utilities::CONFIG_PROGRAM} activate <module>${reset_stderr}' to activate.\\n\";\n        return 0;\n    }\n\n    unless ($quiet) {\n        print \"Generating new index...\\n\";\n    }\n\n    system {$java_command[0]} @java_command;\n    my $exitcode = $? >> 8;\n    unless ($exitcode == 0) {\n        ast_utilities::error_output($program_name, 'could not generate index');\n        return 0;\n    }\n\n    unless ($quiet) {\n        print \"New index successfully generated.\\n\";\n    }\n\n    return 1;\n}\n\n# Delete the active module index.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n# Return: 1 on success, 0 on failure\nsub remove_active_module_index {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n\n    my $full_index_path = File::Spec->catfile($ast_path, $ACTIVE_INDEX_PATH);\n    unlink $full_index_path;\n\n    unless ($quiet) {\n        print \"Cleared index.\\n\";\n    }\n}\n\n# Write a metadata hash for a module.\n# Params:\n#   $modules_folder: the path to the module folder\n#   $module_basename: the module basename\n#   $metadata_ref: a reference to the metadata hash\n#   $program_name: the name of the calling program\n# Return: 1 on success, 0 on failure\nsub write_module_metadata {\n    my $modules_folder = shift;\n    my $module_basename = shift;\n    my $metadata_ref = shift;\n    my $program_name = shift;\n\n    my %metadata = %{$metadata_ref};\n\n    my $module_new_path_metadata =\n        File::Spec->catfile($modules_folder, $module_basename . $METADATA_SUFFIX);\n\n    open my $metadata_handle, '>', \"$module_new_path_metadata\";\n    if (defined $metadata{$SOURCE_KEY}) {\n        print $metadata_handle $SOURCE_KEY . $METADATA_SEPARATOR . $metadata{$SOURCE_KEY} . \"\\n\";\n    }\n    else {\n        ast_utilities::warn_output($program_name, \"bad metadata: missing SOURCE_KEY\");\n        close $metadata_handle;\n        unlink $module_new_path_metadata;\n        return 0;\n    }\n    if (defined $metadata{$URI_KEY}) {\n        print $metadata_handle $URI_KEY . $METADATA_SEPARATOR . $metadata{$URI_KEY} . \"\\n\";\n    }\n    else {\n        ast_utilities::warn_output($program_name, \"bad metadata: missing URI_KEY\");\n        close $metadata_handle;\n        unlink $module_new_path_metadata;\n        return 0;\n    }\n    if (defined $metadata{$DATE_TIME_KEY}) {\n        print $metadata_handle $DATE_TIME_KEY . $METADATA_SEPARATOR . $metadata{$DATE_TIME_KEY} . \"\\n\";\n    }\n    else {\n        ast_utilities::warn_output($program_name, \"bad metadata: missing DATE_TIME_KEY\");\n        close $metadata_handle;\n        unlink $module_new_path_metadata;\n        return 0;\n    }\n    if (defined $metadata{$REPO_NAME_KEY}) {\n        print $metadata_handle $REPO_NAME_KEY . $METADATA_SEPARATOR . $metadata{$REPO_NAME_KEY} . \"\\n\";\n    }\n    if (defined $metadata{$REPO_REF_KEY}) {\n        print $metadata_handle $REPO_REF_KEY . $METADATA_SEPARATOR . $metadata{$REPO_REF_KEY} . \"\\n\";\n    }\n    if (defined $metadata{$REPO_COMMIT_KEY}) {\n        print $metadata_handle $REPO_COMMIT_KEY . $METADATA_SEPARATOR . $metadata{$REPO_COMMIT_KEY} . \"\\n\";\n    }\n    close $metadata_handle;\n\n    return 1;\n}\n\n# Perl modules must return a value. Returning a value perl considers \"truthy\"\n# signals that the module loaded successfully.\n1;\n"
  },
  {
    "path": "atlas-shell-tools/scripts/common/ast_preset_subsystem.pm",
    "content": "package ast_preset_subsystem;\n\nuse warnings;\nuse strict;\n\nuse Exporter qw(import);\nuse File::Spec;\nuse File::Path qw(make_path rmtree);\nuse File::Temp qw(tempdir tempfile);\nuse ast_utilities;\nuse ast_tty;\n\n# Export symbols: variables and subroutines\nour @EXPORT = qw(\n    PRESETS_FOLDER\n    CURRENT_NAMESPACE_FILE\n    NAMESPACE_PATH\n    DEFAULT_NAMESPACE\n    reset_namespace\n    get_namespace\n    save_preset\n    remove_preset\n    remove_all_presets_for_command\n    all_presets_for_command\n    all_presets\n    show_preset\n    edit_preset\n    copy_preset\n    apply_preset_or_exit\n    read_preset\n    get_namespaces_array\n    all_namespaces\n    create_namespace\n    use_namespace\n    remove_namespace\n    get_all_presets_in_current_namespace\n    get_all_presets_for_command\n    get_all_global_presets\n    preset_regex_ok\n    preset_regex\n);\n\nour $PRESETS_FOLDER = 'presets';\nour $GLOBAL_FOLDER = '.global';\n\nour $CURRENT_NAMESPACE_FILE = '.current_namespace';\nour $NAMESPACE_PATH = File::Spec->catfile($PRESETS_FOLDER, $CURRENT_NAMESPACE_FILE);\nour $DEFAULT_NAMESPACE = 'default';\n\nmy $no_colors_stdout = ast_tty::is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_red();\nmy $green_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_green();\nmy $magenta_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_bold();\nmy $bunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_begin_underln();\nmy $eunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_end_underln();\nmy $reset_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_reset();\n\nmy $no_colors_stderr = ast_tty::is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_red();\nmy $green_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_green();\nmy $magenta_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_bold();\nmy $reset_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_reset();\n\n# A header for the preset edit screen.\nmy $PRESET_EDIT_HEADER = \"# Each line in this file will become a discrete ARGV element. For this\n# reason, please use '--option=optionArgument' syntax (note the '=')\n# for option arguments. Otherwise, your preset will not function properly.\n# Also note that all non-option arguments will be dropped, and any line\n# beginning with a '#' will be ignored.\n#\n# If you're stuck, hit <ESC> then type :q!<Enter> to abort the edit.\n# To save your changes, hit <ESC> then type :wq<Enter>\";\n\n# Reset the preset namespace to default. If an extra argument is supplied,\n# then reset the namespace to the supplied argument.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $new_namespace: an optional namespace to swap to\n# Return: none\nsub reset_namespace {\n    my $ast_path = shift;\n    my $new_namespace = shift;\n    my $namespace_file = File::Spec->catfile($ast_path, $ast_preset_subsystem::NAMESPACE_PATH);\n    open my $file_handle, '>', \"$namespace_file\";\n    if (defined $new_namespace) {\n        print $file_handle \"${new_namespace}\\n\";\n    }\n    else {\n        print $file_handle \"${DEFAULT_NAMESPACE}\\n\";\n    }\n    close $file_handle;\n}\n\n# Read and return the current namespace.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: the current namespace\nsub get_namespace {\n    my $ast_path = shift;\n    my $namespace_file = File::Spec->catfile($ast_path, $NAMESPACE_PATH);\n    open my $file_handle, '<', \"$namespace_file\";\n    my $firstline = <$file_handle>;\n    chomp $firstline;\n    close $file_handle;\n    return $firstline;\n}\n\n# Save a preset for a given command.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output output\n#   $preset: the name of the preset\n#   $command: the name of the command\n#   $namespace: the namespace to which to save\n#   $argv_ref: a reference to an array containing all the options and args\n# Return: 1 on success, 0 on failure\nsub save_preset {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $preset = shift;\n    my $command = shift;\n    my $namespace = shift;\n    my $argv_ref = shift;\n\n    my @argv = @{$argv_ref};\n\n    my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n    make_path(\"$preset_subfolder\", {\n        verbose => 0,\n        mode    => 0755\n    });\n    my $preset_file = File::Spec->catfile($preset_subfolder, $preset);\n\n    if (-f $preset_file) {\n        if ($command eq $GLOBAL_FOLDER) {\n            ast_utilities::error_output($program_name, \"global preset ${bold_stderr}${preset}${reset_stderr} already exists\");\n        } else {\n            ast_utilities::error_output($program_name, \"preset ${bold_stderr}${preset}${reset_stderr} already exists for ${bold_stderr}${command}${reset_stderr}\");\n        }\n        return 0;\n    }\n\n    my @detected_options = ();\n    foreach my $arg (@argv) {\n        # treat '-' as a regular argument\n        if ($arg eq '-') {\n            ast_utilities::warn_output($program_name, \"preset discarding non-option arg \\'${bold_stderr}$arg${reset_stderr}\\'\");\n            next;\n        }\n\n        # stop processing once we see '--'\n        if ($arg eq '--') {\n            last;\n        }\n\n        # detect an option or skip non-option arguments\n        if (ast_utilities::string_starts_with($arg, '--') || ast_utilities::string_starts_with($arg, '-')) {\n            push @detected_options, $arg;\n        }\n        else {\n            ast_utilities::warn_output($program_name, \"preset discarding non-option arg \\'${bold_stderr}$arg${reset_stderr}\\'\");\n        }\n    }\n\n    if (scalar @detected_options == 0) {\n        if (ast_utilities::is_dir_empty($preset_subfolder)) {\n            rmdir($preset_subfolder);\n        }\n        ast_utilities::error_output($program_name, \"cannot save an empty preset\");\n        return 0;\n    }\n\n    if ($command eq $GLOBAL_FOLDER) {\n        print \"Global preset ${bold_stdout}${preset}${reset_stdout}:\\n\";\n    } else {\n        print \"Preset ${bold_stdout}${preset}${reset_stdout} for command ${bold_stdout}${command}${reset_stdout}:\\n\";\n    }\n    print \"\\n${bunl_stdout}Preset ARGV${eunl_stdout}${reset_stdout}\\n\";\n    open my $file_handle, '>', \"$preset_file\";\n    foreach my $option (@detected_options) {\n        print \"${bold_stdout}${option}${reset_stdout}\\n\";\n        print $file_handle \"${option}\\n\";\n    }\n    close $file_handle;\n\n    unless ($quiet) {\n        if ($command eq $GLOBAL_FOLDER) {\n            print \"\\nGlobal preset ${bold_stdout}${preset}${reset_stdout} saved.\\n\";\n        } else {\n            print \"\\nPreset ${bold_stdout}${preset}${reset_stdout} saved for command ${bold_stdout}${command}${reset_stdout}.\\n\";\n        }\n    }\n\n    return 1;\n}\n\n# Remove a given preset for a given command.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $preset: the name of the preset\n#   $command: the name of the command\n#   $namespace: the namespace from which to remove\n# Return: 1 on success, 0 on failure\nsub remove_preset {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $preset = shift;\n    my $command = shift;\n    my $namespace = shift;\n\n    my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n    my $preset_file = File::Spec->catfile($preset_subfolder, $preset);\n\n    unless (-f $preset_file) {\n        if ($command eq $GLOBAL_FOLDER) {\n            ast_utilities::error_output($program_name, \"no such global preset ${bold_stderr}${preset}${reset_stderr}\");\n        }\n        else {\n            ast_utilities::error_output($program_name, \"no such preset ${bold_stderr}${preset}${reset_stderr} for command ${bold_stderr}${command}${reset_stderr}\");\n        }\n        return 0;\n    }\n\n    unlink $preset_file;\n\n    unless ($quiet) {\n        if ($command eq $GLOBAL_FOLDER) {\n            print \"Removed global preset ${bold_stdout}${preset}${reset_stdout}.\\n\";\n        }\n        else {\n            print \"Removed preset ${bold_stdout}${preset}${reset_stdout} for ${bold_stdout}${command}${reset_stdout}.\\n\";\n        }\n    }\n\n    if (ast_utilities::is_dir_empty($preset_subfolder)) {\n        rmdir($preset_subfolder);\n    }\n\n    return 1;\n}\n\n# Remove all presets for a given command.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $command: the name of the command\n#   $namespace: the namespace from which to remove\n# Return: 1 on success, 0 on failure\nsub remove_all_presets_for_command {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $command = shift;\n    my $namespace = shift;\n\n    my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n\n    unless (-d $preset_subfolder) {\n        if ($command eq $GLOBAL_FOLDER) {\n            ast_utilities::error_output($program_name, \"no global presets found\");\n        } else {\n            ast_utilities::error_output($program_name, \"no presets found for command ${bold_stderr}${command}${reset_stderr}\");\n        }\n        return 0;\n    }\n\n    rmtree($preset_subfolder);\n\n    unless ($quiet) {\n        if ($command eq $GLOBAL_FOLDER) {\n            print \"Removed all global presets.\\n\";\n        }\n        else {\n            print \"Removed all presets for ${bold_stdout}${command}${reset_stdout}.\\n\";\n        }\n    }\n\n    return 1;\n}\n\n# List all presets for a given command.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $command: the name of the command\n#   $namespace: the namespace to save to\n# Return: 1 on success, 0 on failure\nsub all_presets_for_command {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $command = shift;\n    my $namespace = shift;\n\n    my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n\n    unless (-d $preset_subfolder) {\n        if ($command eq $GLOBAL_FOLDER) {\n            die \"no global presets folder found in namespace ${namespace}\";\n        }\n        ast_utilities::error_output($program_name, \"no presets found for ${bold_stderr}${command}${reset_stderr}\");\n        return 0;\n    }\n\n    opendir my $presets_dir_handle, $preset_subfolder or die \"Something went wrong opening dir: $!\";\n    my @presets = readdir $presets_dir_handle;\n    closedir $presets_dir_handle;\n\n    # we need to filter '.' and '..'\n    my @filtered_presets = ();\n    for my $found_preset (@presets) {\n        unless ($found_preset eq '.' || $found_preset eq '..') {\n            push @filtered_presets, $found_preset;\n        }\n    }\n\n    if (scalar @filtered_presets == 0) {\n        if ($command eq $GLOBAL_FOLDER) {\n            return 0;\n        }\n        return 0;\n    }\n\n    if ($command eq $GLOBAL_FOLDER) {\n        print \"Global presets:\\n\\n\";\n    } else {\n        print \"Command ${bold_stdout}${command}${reset_stdout} presets:\\n\\n\";\n    }\n    foreach my $found_preset (sort {lc $a cmp lc $b} @filtered_presets) {\n        print \"    ${bold_stdout}${found_preset}${reset_stdout}\\n\";\n    }\n    print \"\\n\";\n\n    return 1;\n}\n\n\n# List all presets in a given namespace.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $namespace: the namespace to display\n# Return: 1 on success, 0 on failure\nsub all_presets {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $namespace = shift;\n\n    my $namespace_folder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace);\n\n    unless (-d $namespace_folder) {\n        ast_utilities::error_output($program_name, \"no such namespace ${bold_stderr}${namespace}${reset_stderr}\");\n        return 0;\n    }\n\n    if (ast_utilities::is_dir_empty($namespace_folder)) {\n        ast_utilities::error_output($program_name, \"no presets found\");\n        return 0;\n    }\n\n    opendir my $namespace_dir_handle, $namespace_folder or die \"Something went wrong opening dir: $!\";\n    my @command_folders = readdir $namespace_dir_handle;\n    closedir $namespace_dir_handle;\n\n    my $found_at_least_one = 0;\n    foreach my $found_command (@command_folders) {\n        my $command_folder = File::Spec->catfile($namespace_folder, $found_command);\n        # we need to filter '.', '..', and the '.global' folder\n        unless ($found_command eq '.' || $found_command eq '..' || $found_command eq $GLOBAL_FOLDER) {\n            $found_at_least_one |= all_presets_for_command($ast_path, $program_name, $quiet, $found_command, $namespace);\n        }\n    }\n\n    my $global_folder = File::Spec->catfile($namespace_folder, $GLOBAL_FOLDER);\n    $found_at_least_one |= all_presets_for_command($ast_path, $program_name, $quiet, $GLOBAL_FOLDER, $namespace);\n\n    unless ($found_at_least_one) {\n        ast_utilities::error_output($program_name, \"no presets found\");\n        return 0;\n    }\n\n    return 1;\n}\n\n# Show a given preset for a given command.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $preset: the name of the preset\n#   $command: the name of the command\n#   $namespace: the namespace to save to\n# Return: 1 on success, 0 on failure\nsub show_preset {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $preset = shift;\n    my $command = shift;\n    my $namespace = shift;\n\n    my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n    my $preset_file = File::Spec->catfile($preset_subfolder, $preset);\n\n    unless (-f $preset_file) {\n        if ($command eq $GLOBAL_FOLDER) {\n            ast_utilities::error_output($program_name, \"no such global preset ${bold_stderr}${preset}${reset_stderr}\");\n        } else {\n            ast_utilities::error_output($program_name, \"no such preset ${bold_stderr}${preset}${reset_stderr} for command ${bold_stderr}${command}${reset_stderr}\");\n        }\n        return 0;\n    }\n\n    my @presets_from_file = read_preset($ast_path, $program_name, $quiet, $preset, $command, $namespace);\n    if ($command eq $GLOBAL_FOLDER) {\n        print \"Global preset ${bold_stdout}${preset}${reset_stdout}:\\n\";\n    } else {\n        print \"Preset ${bold_stdout}${preset}${reset_stdout} for command ${bold_stdout}${command}${reset_stdout}:\\n\";\n    }\n    print \"\\n${bunl_stdout}Preset ARGV${eunl_stdout}${reset_stdout}\\n\";\n    for my $preset_from_file (@presets_from_file) {\n        print \"${bold_stdout}${preset_from_file}${reset_stderr}\\n\";\n    }\n    print \"\\n\";\n\n    return 1;\n}\n\n# Edit a given preset for a given command.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $preset: the name of the preset\n#   $command: the name of the command\n#   $namespace: the namespace\n# Return: 1 on success, 0 on failure\nsub edit_preset {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $preset = shift;\n    my $command = shift;\n    my $namespace = shift;\n\n    my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n    make_path(\"$preset_subfolder\", {\n        verbose => 0,\n        mode    => 0755\n    });\n    my $preset_file = File::Spec->catfile($preset_subfolder, $preset);\n\n    my $creating_from_scratch = 0;\n    unless (-f $preset_file) {\n        $creating_from_scratch = 1;\n    }\n\n    my @presets_from_file = read_preset($ast_path, $program_name, $quiet, $preset, $command, $namespace);\n\n    my $handle;\n    my $stage_handle;\n    my $tmpfile;\n    my $preset_stage_file;\n    my $tmpdir = tempdir(CLEANUP => 1);\n    ($handle, $tmpfile) = tempfile(DIR => $tmpdir);\n    ($stage_handle, $preset_stage_file) = tempfile(DIR => $tmpdir);\n    close $handle;\n    close $stage_handle;\n\n    open $handle, '>', \"$tmpfile\";\n    if ($creating_from_scratch) {\n        print $handle \"# CREATING PRESET\\n\";\n    }\n    else {\n        print $handle \"# EDITING PRESET\\n\";\n    }\n    print $handle \"# Preset: ${preset}\\n\";\n    print $handle \"# Command: ${command}\\n#\\n\";\n    print $handle \"${PRESET_EDIT_HEADER}\\n\";\n    foreach my $cur_preset (@presets_from_file) {\n        print $handle \"${cur_preset}\\n\";\n    }\n    print $handle \"\\n\";\n    close $handle;\n\n    my @editor = ast_utilities::get_editor();\n\n    push @editor, \"$tmpfile\";\n    system {$editor[0]} @editor;\n\n    open $handle, '<', \"$tmpfile\";\n    open $stage_handle, '>', \"$preset_stage_file\";\n    while (my $line = <$handle>) {\n        chomp $line;\n\n        # skip empty lines\n        if ($line eq '') {\n            next;\n        }\n\n        # trim excess whitespace from left and right\n        $line =~ s/^\\s+|\\s+$//g;\n\n        # skip line if it starts with '#'\n        if ($line =~ /^#.*/) {\n            next;\n        }\n\n        # treat '-' as a regular argument\n        if ($line eq '-') {\n            ast_utilities::warn_output($program_name, \"preset discarding non-option arg \\'${bold_stderr}${line}${reset_stderr}\\'\");\n            next;\n        }\n\n        # stop processing once we see '--'\n        if ($line eq '--') {\n            last;\n        }\n\n        # detect an option or skip non-option arguments\n        if (ast_utilities::string_starts_with($line, '--') || ast_utilities::string_starts_with($line, '-')) {\n            print $stage_handle \"$line\\n\";\n        }\n        else {\n            ast_utilities::warn_output($program_name, \"preset discarding non-option arg \\'${bold_stderr}${line}${reset_stderr}\\'\");\n        }\n    }\n    close $handle;\n    close $stage_handle;\n\n    # Verify that the staged preset file is not empty\n    if (-z $preset_stage_file) {\n        ast_utilities::error_output($program_name, \"preset cannot be empty\");\n        if (ast_utilities::is_dir_empty($preset_subfolder)) {\n            rmdir($preset_subfolder);\n        }\n        return 0;\n    }\n\n    my @command = ();\n    push @command, \"cp\";\n    push @command, \"$preset_stage_file\";\n    push @command, \"$preset_file\";\n    system {$command[0]} @command;\n    close $tmpdir;\n    show_preset($ast_path, $program_name, $quiet, $preset, $command, $namespace);\n\n    return 1;\n}\n\n# For a given command, copy a source preset to a destination preset.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $src_preset: the source preset\n#   $dest_preset: the destination preset\n#   $command: the name of the command\n#   $namespace: the namespace\n# Return: 1 on success, 0 on failure\nsub copy_preset {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $src_preset = shift;\n    my $dest_preset = shift;\n    my $command = shift;\n    my $namespace = shift;\n\n    my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n    my $source_file = File::Spec->catfile($preset_subfolder, $src_preset);\n    my $dest_file = File::Spec->catfile($preset_subfolder, $dest_preset);\n\n    unless (-e $source_file) {\n        if ($command eq $GLOBAL_FOLDER) {\n            ast_utilities::error_output($program_name, \"no such global preset ${bold_stderr}${src_preset}${reset_stderr}\");\n        } else {\n            ast_utilities::error_output($program_name, \"no such preset ${bold_stderr}${src_preset}${reset_stderr} for command ${bold_stderr}${command}${reset_stderr}\");\n        }\n        return 0;\n    }\n\n    if (-e $dest_file) {\n        if ($command eq $GLOBAL_FOLDER) {\n            ast_utilities::error_output($program_name, \"global preset ${bold_stderr}${dest_preset}${reset_stderr} already exists\");\n        }\n        else {\n            ast_utilities::error_output($program_name, \"preset ${bold_stderr}${dest_preset}${reset_stderr} already exists for ${bold_stderr}${command}${reset_stderr}\");\n        }\n        return 0;\n    }\n\n    my @command = ();\n    push @command, \"cp\";\n    push @command, \"$source_file\";\n    push @command, \"$dest_file\";\n    system {$command[0]} @command;\n\n    unless ($quiet) {\n        print \"Copied contents of preset ${bold_stdout}${src_preset}${reset_stdout} into new preset ${bold_stdout}${dest_preset}${reset_stdout}.\\n\";\n    }\n\n    return 1;\n}\n\n# Apply preset(s) for a given command. Returns an updated argv array with the\n# preset(s) applied. If the preset(s) do not exist, it will error and exit.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppres non-essential output\n#   $presets: the name of the preset(s)\n#   $command: the name of the command\n#   $namespace: the namespace\n#   $argv_ref: a reference to an array containing all the options and args\n# Return: the updated argv array\nsub apply_preset_or_exit {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $presets = shift;\n    my $command = shift;\n    my $namespace = shift;\n    my $argv_ref = shift;\n\n    my @argv = @{$argv_ref};\n\n    my @presets_comma = split ',', $presets;\n    my @presets_colon = split ':', $presets;\n    my @presets_array;\n\n    # User cannot use both ',' and ':', pick one\n    if (scalar @presets_colon > 1 && scalar @presets_comma > 1) {\n        ast_utilities::error_output($program_name, \"cannot use both \\',\\' and \\':\\' to split presets, choose one\");\n        exit 1;\n    }\n\n    # In this case, either user split on ':' to separate or there was only 1 preset\n    if (scalar @presets_colon >= scalar @presets_comma) {\n        @presets_array = @presets_colon;\n    }\n    # Otherwise, the user split on ',' so use that\n    else {\n        @presets_array = @presets_comma;\n    }\n\n    my @final_argv = ();\n    my $use_global = 0;\n    foreach my $preset (@presets_array) {\n        my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n        my $preset_file = File::Spec->catfile($preset_subfolder, $preset);\n        unless (-f $preset_file) {\n            # Fall back and try global presets\n            my $global_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $GLOBAL_FOLDER);\n            my $global_file = File::Spec->catfile($global_subfolder, $preset);\n            unless (-f $global_file) {\n                ast_utilities::error_output($program_name, \"preset ${bold_stderr}${preset}${reset_stderr} not found for command ${bold_stderr}${command}${reset_stderr}\");\n                ast_utilities::error_output($program_name, \"preset ${bold_stderr}${preset}${reset_stderr} not found in globals\");\n                all_presets_for_command($ast_path, $program_name, $quiet, $command, $namespace);\n                all_presets_for_command($ast_path, $program_name, $quiet, $GLOBAL_FOLDER, $namespace);\n                exit 1;\n            }\n            $use_global = 1;\n        }\n\n        my @argv_from_presets = ();\n        if ($use_global) {\n            @argv_from_presets = read_preset($ast_path, $program_name, $quiet, $preset, $GLOBAL_FOLDER, $namespace);\n        } else {\n            @argv_from_presets = read_preset($ast_path, $program_name, $quiet, $preset, $command, $namespace);\n        }\n        foreach my $preset_argv_elem (@argv_from_presets) {\n            push @final_argv, $preset_argv_elem;\n        }\n    }\n    foreach my $argv_elem (@argv) {\n        push @final_argv, $argv_elem;\n    }\n\n    return @final_argv;\n}\n\n# Read a preset for a given command. Returns the preset in an array\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $preset: the name of the preset\n#   $command: the name of the command\n#   $namespace: the current namespace\n# Return: the preset array, or an empty array on error\nsub read_preset {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $preset = shift;\n    my $command = shift;\n    my $namespace = shift;\n\n    my $preset_subfolder = File::Spec->catfile($ast_path, $PRESETS_FOLDER, $namespace, $command);\n    my $preset_file = File::Spec->catfile($preset_subfolder, $preset);\n\n    unless (-f $preset_file) {\n        return();\n    }\n\n    my @options = ();\n\n    open my $file_handle, '<', $preset_file;\n\n    # NOTE this fails if a preset option argument contained a newline\n    while (my $line = <$file_handle>) {\n        chomp $line;\n        push @options, $line;\n    }\n\n    close($file_handle);\n    return @options;\n}\n\n# Get an array containing each namespace as an element.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: the namespace array\nsub get_namespaces_array {\n    my $ast_path = shift;\n\n    my $preset_folder = File::Spec->catfile($ast_path, $PRESETS_FOLDER);\n\n    unless (-d $preset_folder) {\n        die \"The folder $PRESETS_FOLDER did not exist at $ast_path\";\n    }\n\n    opendir my $presets_dir_handle, $preset_folder or die \"Something went wrong opening dir: $!\";\n    my @namespaces = readdir $presets_dir_handle;\n    closedir $presets_dir_handle;\n\n    # we need to filter '.', '..', and '.current_namespace'\n    my @filtered_namespaces = ();\n    for my $found_namespace (@namespaces) {\n        unless ($found_namespace eq '.' || $found_namespace eq '..'\n            || $found_namespace eq $CURRENT_NAMESPACE_FILE) {\n            push @filtered_namespaces, $found_namespace;\n        }\n    }\n\n    return @filtered_namespaces;\n}\n\n# Print all namespaces, and denote the current checked out namespace.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n# Return: 1 on success, 0 on failure\nsub all_namespaces {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n\n    my $current_namespace = get_namespace($ast_path);\n    my @namespaces = get_namespaces_array($ast_path);\n\n    if (scalar @namespaces == 0) {\n        error_output($program_name, \"no namespaces found\");\n        return 0;\n    }\n\n    print \"${bold_stdout}Preset namespaces:${reset_stdout}\\n\\n\";\n    for my $found_namespace (sort {lc $a cmp lc $b} @namespaces) {\n        if ($found_namespace eq $current_namespace) {\n            print \"    * ${bold_stdout}${green_stdout}${found_namespace}${reset_stdout}\\n\";\n        }\n        else {\n            print \"      ${found_namespace}\\n\";\n        }\n    }\n    print \"\\n\";\n\n    return 1;\n}\n\n# Create a new namespace.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $new_namespace: the namespace to create\n# Return: 1 on success, 0 on failure\nsub create_namespace {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $new_namespace = shift;\n\n    my $current_namespace = get_namespace($ast_path);\n    my $preset_folder = File::Spec->catfile($ast_path, $PRESETS_FOLDER);\n    my $new_namespace_folder = File::Spec->catfile($preset_folder, $new_namespace);\n    my $global_folder = File::Spec->catfile($preset_folder, $new_namespace, $GLOBAL_FOLDER);\n\n    unless (-d $preset_folder) {\n        die \"The folder $PRESETS_FOLDER did not exist at $ast_path\";\n    }\n\n    if (-d $new_namespace_folder) {\n        ast_utilities::error_output($program_name, \"namespace ${bold_stderr}${new_namespace}${reset_stderr} already exists\");\n        return 0;\n    }\n\n    make_path(\"$new_namespace_folder\", {\n        verbose => 0,\n        mode    => 0755\n    });\n\n    make_path(\"$global_folder\", {\n        verbose => 0,\n        mode    => 0755\n    });\n\n    unless ($quiet) {\n        print \"Created namespace ${bold_stdout}${new_namespace}${reset_stdout}.\\n\";\n    }\n\n    return 1;\n}\n\n# Check out a given namespace.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $namespace: the namespace to use\n# Return: 1 on success, 0 on failure\nsub use_namespace {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $namespace = shift;\n\n    my $preset_folder = File::Spec->catfile($ast_path, $PRESETS_FOLDER);\n    my $namespace_folder = File::Spec->catfile($preset_folder, $namespace);\n\n    unless (-d $preset_folder) {\n        die \"The folder $PRESETS_FOLDER did not exist at $ast_path\";\n    }\n\n    unless (-d $namespace_folder) {\n        ast_utilities::error_output($program_name, \"no such namespace ${bold_stderr}${namespace}${reset_stderr}\");\n        return 0;\n    }\n\n    reset_namespace($ast_path, $namespace);\n\n    unless ($quiet) {\n        print \"Now using namespace ${bold_stdout}${namespace}${reset_stdout}.\\n\";\n    }\n\n    return 1;\n}\n\n# Remove a namespace.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output\n#   $namespace: the namespace to remove\n# Return: 1 on success, 0 on failure\nsub remove_namespace {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $namespace = shift;\n\n    my $current_namespace = get_namespace($ast_path);\n    my $preset_folder = File::Spec->catfile($ast_path, $PRESETS_FOLDER);\n    my $namespace_folder = File::Spec->catfile($preset_folder, $namespace);\n\n    unless (-d $preset_folder) {\n        die \"The folder $PRESETS_FOLDER did not exist at $ast_path\";\n    }\n\n    unless (-d $namespace_folder) {\n        ast_utilities::error_output($program_name, \"no such namespace ${bold_stderr}${namespace}${reset_stderr}\");\n        return 0;\n    }\n\n    if ($namespace eq $DEFAULT_NAMESPACE) {\n        ast_utilities::error_output($program_name, \"cannot remove the default namespace\");\n        return 0;\n    }\n\n    if ($namespace eq $current_namespace) {\n        ast_utilities::error_output($program_name, \"cannot remove in-use namespace ${bold_stderr}${namespace}${reset_stderr}\");\n        return 0;\n    }\n\n    rmtree($namespace_folder);\n\n    unless ($quiet) {\n        print \"Removed namespace ${bold_stdout}${namespace}${reset_stdout}.\\n\";\n    }\n\n    return 1;\n}\n\n# Get an array of all presets in the current namespace, including global presets.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: the preset array\nsub get_all_presets_in_current_namespace {\n    my $ast_path = shift;\n\n    my $namespace = get_namespace($ast_path);\n    my @all_presets = ();\n\n    my $preset_folder = File::Spec->catfile($ast_path, $PRESETS_FOLDER);\n    my $namespace_folder = File::Spec->catfile($preset_folder, $namespace);\n    my $global_folder = File::Spec->catfile($namespace_folder, $GLOBAL_FOLDER);\n\n    opendir my $namespace_dir_handle, $namespace_folder or die \"Something went wrong opening dir: $!\";\n    my @command_folders = readdir $namespace_dir_handle;\n    closedir $namespace_dir_handle;\n\n    foreach my $found_command (@command_folders) {\n        my $command_folder = File::Spec->catfile($namespace_folder, $found_command);\n        # we need to filter '.', '..'\n        unless ($found_command eq '.' || $found_command eq '..') {\n            opendir my $command_dir_handle, $command_folder or die \"Something went wrong opening dir: $!\";\n            my @preset_files = readdir $command_dir_handle;\n            closedir $command_dir_handle;\n\n            foreach my $found_preset (@preset_files) {\n                # we need to filter '.', '..'\n                unless ($found_preset eq '.' || $found_preset eq '..') {\n                    push @all_presets, $found_preset;\n                }\n            }\n        }\n    }\n\n    opendir my $global_dir_handle, $global_folder or die \"Something went wrong opening dir: $!\";\n    my @preset_files = readdir $global_dir_handle;\n    closedir $global_dir_handle;\n\n    foreach my $found_preset (@preset_files) {\n        # we need to filter '.', '..'\n        unless ($found_preset eq '.' || $found_preset eq '..') {\n            push @all_presets, $found_preset;\n        }\n    }\n\n    return @all_presets;\n}\n\n# Get an array of all presets in the current namespace for a given command.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $command: the command from which to check presets\n# Return: the preset array\nsub get_all_presets_for_command {\n    my $ast_path = shift;\n    my $command = shift;\n\n    my $namespace = get_namespace($ast_path);\n    my @all_presets = ();\n\n    my $preset_folder = File::Spec->catfile($ast_path, $PRESETS_FOLDER);\n    my $namespace_folder = File::Spec->catfile($preset_folder, $namespace);\n    my $command_folder = File::Spec->catfile($namespace_folder, $command);\n\n    unless (-d $command_folder) {\n        return @all_presets;\n    }\n\n    opendir my $command_dir_handle, $command_folder or die \"Something went wrong opening dir: $!\";\n    my @preset_files = readdir $command_dir_handle;\n    closedir $command_dir_handle;\n\n    foreach my $found_preset (@preset_files) {\n        # we need to filter '.', '..'\n        unless ($found_preset eq '.' || $found_preset eq '..') {\n            push @all_presets, $found_preset;\n        }\n    }\n\n    return @all_presets;\n}\n\n# Get an array of all global presets in the current namespace\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n# Return: the preset array\nsub get_all_global_presets {\n    my $ast_path = shift;\n\n    my $namespace = get_namespace($ast_path);\n    my @all_presets = ();\n\n    my $preset_folder = File::Spec->catfile($ast_path, $PRESETS_FOLDER);\n    my $namespace_folder = File::Spec->catfile($preset_folder, $namespace);\n    my $global_folder = File::Spec->catfile($namespace_folder, $GLOBAL_FOLDER);\n\n    unless (-d $global_folder) {\n        return @all_presets;\n    }\n\n    opendir my $global_dir_handle, $global_folder or die \"Something went wrong opening dir: $!\";\n    my @preset_files = readdir $global_dir_handle;\n    closedir $global_dir_handle;\n\n    foreach my $found_preset (@preset_files) {\n        # we need to filter '.', '..'\n        unless ($found_preset eq '.' || $found_preset eq '..') {\n            push @all_presets, $found_preset;\n        }\n    }\n\n    return @all_presets;\n}\n\n# Check that a preset name matches the approved name regex.\n# Params:\n#   $preset: the preset to check\n# Return: if the preset name matched the regex\nsub preset_regex_ok {\n    my $preset = shift;\n\n    my $regex = preset_regex();\n\n    if ($preset =~ m/$regex/) {\n        return 1;\n    }\n    return 0;\n}\n\n# Get the valid preset name regex.\n# Params: none\n# Return: the valid preset name regex\nsub preset_regex {\n    return \"^[_a-zA-Z0-9][_a-zA-Z0-9-]*\\$\";\n}\n\n# Perl modules must return a value. Returning a value perl considers \"truthy\"\n# signals that the module loaded successfully.\n1;\n"
  },
  {
    "path": "atlas-shell-tools/scripts/common/ast_repo_subsystem.pm",
    "content": "package ast_repo_subsystem;\n\nuse warnings;\nuse strict;\n\nuse Exporter qw(import);\nuse File::Basename qw(basename);\nuse File::Path qw(make_path rmtree);\nuse File::Temp qw(tempdir tempfile);\nuse POSIX qw(strftime);\nuse ast_tty;\nuse ast_module_subsystem;\nuse ast_utilities;\n\n# Export symbols: variables and subroutines\nour @EXPORT = qw(\n    REPOS_FOLDER\n    create_repo\n    edit_repo\n    list_repos\n    remove_repo\n    install_repo\n    get_all_repos\n    add_skip_variable\n    add_exclude_variable\n    get_repo_settings\n    print_repo_settings\n);\n\nour $REPOS_FOLDER = 'repos';\nour $REPO_CONFIG = 'repo_config';\n\nmy $no_colors_stdout = ast_tty::is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_red();\nmy $green_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_green();\nmy $magenta_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_bold();\nmy $bunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_begin_underln();\nmy $eunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_end_underln();\nmy $reset_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_reset();\n\nmy $no_colors_stderr = ast_tty::is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_red();\nmy $green_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_green();\nmy $magenta_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_bold();\nmy $reset_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_reset();\n\nmy $REPO_EDIT_HEADER = \"# Lines beginning with \\\"#\\\" are ignored\n#\n# Add exclude packages for gradle using \\\"exclude\\\".\n# To exclude multiple packages, simply repeat this config variable for each package. E.g.\n# exclude = com.example.package\n# exclude = com.example.anotherpackage\n#\n# Skip gradle tasks using \\\"skip\\\".\n# To skip multiple tasks, simply repeat this config variable for each task. E.g.\n# skip = javadoc\n# skip = integrationTest\n#\n# If you're stuck, hit <ESC> then type :q!<Enter> to abort the edit.\n# To save your changes, hit <ESC> then type :wq<Enter>\";\n\n#\n# TODO fix all the hardcoded stuff.\n# e.g. 'url', 'ref', 'exclude', etc.\n#\n\n# Create a new repo.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output output\n#   $repo: the name of the repo\n#   $url: the repo URL\n#   $ref: the ref (a branch, tag, even a commit)\n# Return: 1 on success, 0 on failure\nsub create_repo {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n    my $url = shift;\n    my $ref = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    if (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"repo ${bold_stderr}${repo}${reset_stderr} already exists\");\n        return 0;\n    }\n\n    make_path(\"$repo_subfolder\", {\n        verbose => 0,\n        mode    => 0755\n    });\n\n    my $repo_config_file = File::Spec->catfile($repo_subfolder, $REPO_CONFIG);\n    open my $file_handle, '>', \"$repo_config_file\";\n    print $file_handle \"url = ${url}\\n\";\n    print $file_handle \"ref = ${ref}\\n\";\n    close $file_handle;\n\n    unless ($quiet) {\n        print \"New repo: ${bold_stdout}${repo}${reset_stdout}\\nURL: ${bold_stdout}${url}${reset_stdout}\\nRef: ${bold_stdout}${ref}${reset_stdout}\\n\";\n    }\n\n    return 1;\n}\n\n# Edit a repo.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output output\n#   $repo: the name of the repo\n# Return: 1 on success, 0 on failure\nsub edit_repo {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    unless (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"no such repo ${bold_stderr}${repo}${reset_stderr}\");\n        return 0;\n    }\n\n    my $repo_config_file = File::Spec->catfile($repo_subfolder, $REPO_CONFIG);\n    unless (-f $repo_config_file) {\n        ast_utilities::error_output($program_name, \"could not find config file for repo ${bold_stderr}${repo}${reset_stderr}\");\n        print STDERR \"To resolve, please remove the repo and re-add.\\n\";\n        return 0;\n    }\n\n    # Create the staging file\n    my $handle;\n    my $staging_file;\n    my $tmpdir = tempdir(CLEANUP => 1);\n    ($handle, $staging_file) = tempfile(DIR => $tmpdir);\n    close $handle;\n\n    # copy the current config file into the staging file\n    open my $stage_handle, '>', \"$staging_file\";\n    print $stage_handle \"# Config for repo ${repo}\\n\";\n    print $stage_handle \"${REPO_EDIT_HEADER}\\n\";\n    my $url = read_single_config_variable_from_arbitrary_file($repo_config_file, 'url');\n    my $ref = read_single_config_variable_from_arbitrary_file($repo_config_file, 'ref');\n    my @skips = read_multiple_config_variables_from_arbitrary_file($repo_config_file, 'skip');\n    my @excludes = read_multiple_config_variables_from_arbitrary_file($repo_config_file, 'exclude');\n    print $stage_handle \"url = ${url}\\n\";\n    print $stage_handle \"ref = ${ref}\\n\";\n    foreach my $skip (@skips) {\n        print $stage_handle \"skip = ${skip}\\n\";\n    }\n    foreach my $exclude (@excludes) {\n        print $stage_handle \"exclude = ${exclude}\\n\";\n    }\n    close $stage_handle;\n\n    # open the staging file in the user's editor\n    my @editor = ast_utilities::get_editor();\n    push @editor, \"$staging_file\";\n    system {$editor[0]} @editor;\n\n    # confirm that the staging file is not malformed, i.e. it must have a valid URL and ref\n    $url = read_single_config_variable_from_arbitrary_file($staging_file, 'url');\n    $ref = read_single_config_variable_from_arbitrary_file($staging_file, 'ref');\n    if ($url eq '' || $ref eq '') {\n        ast_utilities::error_output($program_name, \"failed to parse \\'url\\' and \\'ref\\' config variables\");\n        print STDERR \"Aborting edit without saving...\\n\";\n        return 0;\n    }\n\n    # copy the staging file back into the actual config file\n    open $handle, '>', \"$repo_config_file\";\n    $url = read_single_config_variable_from_arbitrary_file($staging_file, 'url');\n    $ref = read_single_config_variable_from_arbitrary_file($staging_file, 'ref');\n    @skips = read_multiple_config_variables_from_arbitrary_file($staging_file, 'skip');\n    @excludes = read_multiple_config_variables_from_arbitrary_file($staging_file, 'exclude');\n    print $handle \"url = ${url}\\n\";\n    print $handle \"ref = ${ref}\\n\";\n    foreach my $skip (@skips) {\n        print $handle \"skip = ${skip}\\n\";\n    }\n    foreach my $exclude (@excludes) {\n        print $handle \"exclude = ${exclude}\\n\";\n    }\n    close $handle;\n\n    return 1;\n}\n\n# List the repos.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output output\n# Return: 1 on success, 0 on failure\nsub list_repos {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n\n    my @filtered_repos = get_all_repos($ast_path);\n\n    if (scalar @filtered_repos == 0) {\n        ast_utilities::error_output($program_name, \"found no repos\");\n        return 0;\n    }\n\n    print \"${bold_stdout}Registered repos:${reset_stdout}\\n\\n\";\n    for my $found_repo (sort {lc $a cmp lc $b} @filtered_repos) {\n        my $url = read_single_config_variable($ast_path, $program_name, $quiet, $found_repo, 'url');\n        my $ref = read_single_config_variable($ast_path, $program_name, $quiet, $found_repo, 'ref');\n        unless (defined $url && defined $ref) {\n            ast_utilities::error_output($program_name, \"repo list operation failed\");\n            return 0;\n        }\n        print \"    ${bold_stdout}${found_repo}${reset_stdout} : ${url} (${ref})\\n\";\n    }\n    print \"\\n\";\n\n    return 1;\n}\n\n# Remove an existing repo.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output output\n#   $repo: the name of the repo\n# Return: 1 on success, 0 on failure\nsub remove_repo {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    unless (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"no such repo ${bold_stderr}${repo}${reset_stderr}\");\n        return 0;\n    }\n\n    rmtree($repo_subfolder);\n\n    unless ($quiet) {\n        print \"Removed repo ${bold_stdout}${repo}${reset_stdout}.\\n\";\n    }\n\n    return 1;\n}\n\n# Install module using an existing repo.\n# Params:\n#   $ast_path: the path to the atlas-shell-tools data folder\n#   $program_name: the name of the calling program\n#   $quiet: suppress non-essential output output\n#   $repo: the name of the repo\n#   $ref_override: an optional override ref\n# Return: 1 on success, 0 on failure\nsub install_repo {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n    my $ref_override = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    unless (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"no such repo ${bold_stderr}${repo}${reset_stderr}\");\n        return 0;\n    }\n\n    my %module_metadata = ast_module_subsystem::get_module_to_metadata_hash($ast_path);\n\n    my $url = read_single_config_variable($ast_path, $program_name, $quiet, $repo, 'url');\n    my $ref = read_single_config_variable($ast_path, $program_name, $quiet, $repo, 'ref');\n    my @excludes = read_multiple_config_variables($ast_path, $program_name, $quiet, $repo, 'exclude');\n    my @skips = read_multiple_config_variables($ast_path, $program_name, $quiet, $repo, 'skip');\n    unless (defined $url && defined $ref) {\n        ast_utilities::error_output($program_name, \"repo install operation failed\");\n        return 0;\n    }\n    my $ref_to_use;\n    if ($ref_override eq '') {\n        $ref_to_use = $ref;\n    }\n    else {\n        $ref_to_use = $ref_override;\n    }\n\n    my $tmpdir = tempdir(CLEANUP => 1);\n\n    # First, we can check to see if the provided ref is already represented in one of the installed\n    # modules. Since this uses git ls-remote, this only works when the ref is a tag or a branch. If\n    # the ref was a commit hash, then we will need to clone the repo first to determine if an install\n    # is necessary.\n    my @command = ();\n    push @command, \"git\";\n    push @command, \"ls-remote\";\n    push @command, \"${url}\";\n    push @command, \"${ref_to_use}\";\n    my $lsremote_result = ast_utilities::read_command_output(\\@command);\n    my @remote_ref = split /\\s+/, $lsremote_result;\n    my $remote_commit_hash;\n    if (scalar @remote_ref > 0) {\n        $remote_commit_hash = $remote_ref[0];\n    }\n    if (defined $remote_commit_hash) {\n        foreach my $module_key (keys %module_metadata) {\n            my %metadata = %{$module_metadata{$module_key}};\n            my $module_commit_hash = $metadata{$ast_module_subsystem::REPO_COMMIT_KEY};\n            if (defined $module_commit_hash && $module_commit_hash eq $remote_commit_hash) {\n                ast_utilities::warn_output($program_name, \"nothing to do\");\n                print STDERR \"Ref ${bold_stderr}${ref_to_use}${reset_stderr} in repo ${bold_stderr}${repo}${reset_stderr} refers to commit ${bold_stderr}${remote_commit_hash}${reset_stderr}.\\n\";\n                print STDERR \"Installed module ${bold_stderr}${module_key}${reset_stderr} was built from this commit.\\n\";\n                print STDERR \"Try \\'${bold_stderr}${ast_utilities::CONFIG_PROGRAM} activate ${module_key}${reset_stderr}\\' to use this version.\\n\";\n                return 1;\n            }\n        }\n    }\n\n    @command = ();\n    push @command, \"git\";\n    push @command, \"clone\";\n    push @command, \"${url}\";\n    push @command, \"${tmpdir}\";\n    my $success = system {$command[0]} @command;\n    unless ($success == 0) {\n        ast_utilities::error_output($program_name, \"repo install operation failed\");\n        return 0;\n    }\n\n    chdir $tmpdir or die \"$!\";\n\n    @command = ();\n    push @command, \"git\";\n    push @command, \"checkout\";\n    push @command, \"${ref_to_use}\";\n    $success = system {$command[0]} @command;\n    unless ($success == 0) {\n        ast_utilities::error_output($program_name, \"repo install operation failed\");\n        return 0;\n    }\n    my $installed_commit_hash = `git rev-parse HEAD`;\n    my $installed_commit_hash_short = `git rev-parse --short HEAD`;\n    chomp $installed_commit_hash;\n    chomp $installed_commit_hash_short;\n\n    my $tentative_module_name = \"${repo}-${installed_commit_hash_short}\";\n    my %modules = ast_module_subsystem::get_module_to_status_hash($ast_path);\n    my @module_names = keys %modules;\n    foreach my $module_name (@module_names) {\n        if ($tentative_module_name eq $module_name) {\n            print STDERR \"\\n\";\n            ast_utilities::warn_output($program_name, \"nothing to do\");\n            print STDERR \"Ref ${bold_stderr}${ref_to_use}${reset_stderr} in repo ${bold_stderr}${repo}${reset_stderr} refers to commit ${bold_stderr}${installed_commit_hash}${reset_stderr}.\\n\";\n            print STDERR \"Installed module ${bold_stderr}${module_name}${reset_stderr} was built from this commit.\\n\";\n            print STDERR \"Try \\'${bold_stderr}${ast_utilities::CONFIG_PROGRAM} activate ${module_name}${reset_stderr}\\' to use this version.\\n\";\n            return 1;\n        }\n    }\n\n    my $gradle_injection = \"\n    task atlasshelltools(type: Jar) {\n        baseName = project.name\n        classifier = '-AST'\n        duplicatesStrategy = 'exclude'\n        from {\n            configurations.atlasshelltools.collect\n            {\n                it.isDirectory() ? it : zipTree(it).matching {\n                    exclude\n                    {\n                        it.path.contains('META-INF') && (it.path.endsWith('.SF') || it.path.endsWith('.DSA') || it.path.endsWith('.RSA'))\n                    }\n                }\n            }\n        }\n        with jar\n        zip64 = true\n    }\n\n    configurations\n    {\n        atlasshelltools\n        {\n            %s\n        }\n    }\n\n    dependencies\n    {\n        atlasshelltools project.configurations.getByName('implementation')\n        if (packages.slf4j != null) {\n            atlasshelltools packages.slf4j.api\n        }\n        if (packages.log4j != null) {\n            atlasshelltools packages.log4j.api\n            atlasshelltools packages.log4j.slf4j\n        }\n    }\n    \";\n    my @excludes_mapped = ();\n    foreach my $exclude_element (@excludes) {\n        push @excludes_mapped, \"exclude group: \\'${exclude_element}\\';\";\n    }\n    $gradle_injection = sprintf($gradle_injection, join(\"\\n\", @excludes_mapped));\n    open my $file_handle, '>>', \"$tmpdir/build.gradle\" or die \"Could not open build.gradle $!\";\n    print $file_handle \"${gradle_injection}\\n\";\n    close $file_handle;\n\n    @command = ();\n    push @command, \"./gradlew\";\n    push @command, \"clean\";\n    push @command, \"atlasshelltools\";\n    foreach my $skip_element (@skips) {\n        push @command, \"-x\";\n        push @command, \"$skip_element\";\n    }\n    $success = system {$command[0]} @command;\n    unless ($success == 0) {\n        ast_utilities::error_output($program_name, \"repo install operation failed\");\n        return 0;\n    }\n\n    my @find_command = (\n        \"find\", \".\",\n        \"-type\", \"f\",\n        \"-name\", \"*-AST.jar\",\n        \"-print0\"\n    );\n    open FIND, \"-|\", @find_command;\n    # TODO 'local' modifier makes sense here? confirm this, 'my' may make more sense\n    # see https://www.perlmonks.org/?node_id=94007\n    local $/ = \"\\0\";\n    while (<FIND>) {\n        # FIND command is printing full paths, we just want the basename.\n        # Also, we must chomp to remove the terminating null byte left over from\n        # the '-print0' flag given to 'find'.\n        my $module = $_;\n        chomp $module;\n        my $module_basename = basename($module);\n\n        my %local_metadata;\n        $local_metadata{$ast_module_subsystem::SOURCE_KEY} = \"repo\";\n        $local_metadata{$ast_module_subsystem::URI_KEY} = \"${url}\";\n        $local_metadata{$ast_module_subsystem::REPO_NAME_KEY} = \"${repo}\";\n        $local_metadata{$ast_module_subsystem::REPO_REF_KEY} = \"${ref_to_use}\";\n        $local_metadata{$ast_module_subsystem::REPO_COMMIT_KEY} = \"${installed_commit_hash}\";\n        $local_metadata{$ast_module_subsystem::DATE_TIME_KEY} = strftime(\"%Y-%m-%d %H:%M:%S UTC\", gmtime(time));\n        # install the module!\n        my $success = ast_module_subsystem::perform_install($module, $ast_path, $program_name,\n            \"${repo}-${installed_commit_hash_short}\", 0, 0, 1, 0, \\%local_metadata, 0);\n\n        unless ($success) {\n            ast_utilities::error_output($program_name, \"repo install operation failed\");\n            return 0;\n        }\n    }\n    return 1;\n}\n\n# Get an array containing all repo names. Useful for autocomplete and listing code.\nsub get_all_repos {\n    my $ast_path = shift;\n\n    my $repo_folder = File::Spec->catfile($ast_path, $REPOS_FOLDER);\n\n    opendir my $repo_dir_handle, $repo_folder or die \"Something went wrong opening dir: $!\";\n    my @repos = readdir $repo_dir_handle;\n    closedir $repo_dir_handle;\n\n    # we need to filter '.' and '..'\n    my @filtered_repos = ();\n    for my $found_repo (@repos) {\n        unless ($found_repo eq '.' || $found_repo eq '..') {\n            push @filtered_repos, $found_repo;\n        }\n    }\n\n    return @filtered_repos;\n}\n\n# TODO refactor DRY\n# Wrapper for append_config_variable_to_file. Used by UI code.\nsub add_skip_variable {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n    my $value = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    unless (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"repo ${bold_stderr}${repo}${reset_stderr} does not exist\");\n        return 0;\n    }\n\n    my $repo_config_file = File::Spec->catfile($repo_subfolder, $REPO_CONFIG);\n    unless (-f $repo_config_file) {\n        ast_utilities::error_output($program_name, \"could not find config file for repo ${bold_stderr}${repo}${reset_stderr}\");\n        return 0;\n    }\n\n    append_config_variable_to_file($repo_config_file, 'skip', $value);\n\n    return 1;\n}\n\n# TODO refactor DRY\n# Wrapper for append_config_variable_to_file. Used by UI code.\nsub add_exclude_variable {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n    my $value = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    unless (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"repo ${bold_stderr}${repo}${reset_stderr} does not exist\");\n        return 0;\n    }\n\n    my $repo_config_file = File::Spec->catfile($repo_subfolder, $REPO_CONFIG);\n    unless (-f $repo_config_file) {\n        ast_utilities::error_output($program_name, \"could not find config file for repo ${bold_stderr}${repo}${reset_stderr}\");\n        return 0;\n    }\n\n    append_config_variable_to_file($repo_config_file, 'exclude', $value);\n\n    return 1;\n}\n\n# Get an array containing string-ified repo settings. The array is useful for\n# output purposes.\nsub get_repo_settings {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    unless (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"repo ${bold_stderr}${repo}${reset_stderr} does not exist\");\n        return();\n    }\n\n    my $repo_config_file = File::Spec->catfile($repo_subfolder, $REPO_CONFIG);\n    unless (-f $repo_config_file) {\n        ast_utilities::error_output($program_name, \"could not find config file for repo ${bold_stderr}${repo}${reset_stderr}\");\n        return();\n    }\n\n    my @settings = ();\n\n    open my $file_handle, '<', $repo_config_file or die \"Could not open file $repo_config_file $!\";\n    while (my $line = <$file_handle>) {\n        chomp $line;\n        if ($line eq '' || substr($line, 0, 1) eq '#') {\n            next;\n        }\n        # trim excess whitespace from left and right\n        $line =~ s/^\\s+|\\s+$//g;\n        if ($line eq '' || substr($line, 0, 1) eq '#') {\n            next;\n        }\n        my @line_split = split '=', $line, 2;\n        unless (defined $line_split[0]) {\n            next;\n        }\n        # trim excess whitespace from left and right\n        $line_split[0] =~ s/^\\s+|\\s+$//g;\n        if (defined $line_split[1] && $line_split[1] !~ /^\\s*$/) {\n            # trim excess whitespace from left and right\n            $line_split[1] =~ s/^\\s+|\\s+$//g;\n            push @settings, \"$line_split[0] = $line_split[1]\";\n        }\n    }\n    close $file_handle;\n\n    return @settings;\n}\n\n# Print repo settings using get_repo_settings.\nsub print_repo_settings {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n\n    if ($quiet) {\n        return;\n    }\n\n    my @settings = get_repo_settings($ast_path, $program_name, $quiet, $repo);\n    if (scalar @settings != 0) {\n        print \"${bold_stdout}${repo} settings:${reset_stdout}\\n\";\n    }\n    foreach my $setting (@settings) {\n        print \"${setting}\\n\";\n    }\n}\n\n# TODO refactor DRY\n# Given an arbitrary file path, opens it and attempts to read the first config\n# variable that matches the given variable. If the value cannot be read, returns\n# an empty string.\nsub read_single_config_variable_from_arbitrary_file {\n    my $file = shift;\n    my $variable = shift;\n\n    my $value = '';\n    open my $file_handle, '<', $file or die \"Could not open file $file $!\";\n    while (my $line = <$file_handle>) {\n        chomp $line;\n        # trim excess whitespace from left and right\n        $line =~ s/^\\s+|\\s+$//g;\n        if ($line eq '' || substr($line, 0, 1) eq '#') {\n            next;\n        }\n        my @line_split = split '=', $line, 2;\n        unless (defined $line_split[0]) {\n            next;\n        }\n        # trim excess whitespace from left and right\n        $line_split[0] =~ s/^\\s+|\\s+$//g;\n        if ($line_split[0] eq $variable) {\n            if (defined $line_split[1] && $line_split[1] !~ /^\\s*$/) {\n                # trim excess whitespace from left and right\n                $line_split[1] =~ s/^\\s+|\\s+$//g;\n                $value = $line_split[1];\n            }\n        }\n    }\n    close $file_handle;\n\n    return $value;\n}\n\n# TODO refactor DRY\n# Given an arbitrary file path, opens it and attempts to read the all config\n# variables that match the given variable. The values will be returned in an array.\n# If no values could be read, the array will be empty.\nsub read_multiple_config_variables_from_arbitrary_file {\n    my $file = shift;\n    my $variable = shift;\n\n    my @values = ();\n    open my $file_handle, '<', $file or die \"Could not open file $file $!\";\n    while (my $line = <$file_handle>) {\n        chomp $line;\n        # trim excess whitespace from left and right\n        $line =~ s/^\\s+|\\s+$//g;\n        if ($line eq '' || substr($line, 0, 1) eq '#') {\n            next;\n        }\n        my @line_split = split '=', $line, 2;\n        unless (defined $line_split[0]) {\n            next;\n        }\n        # trim excess whitespace from left and right\n        $line_split[0] =~ s/^\\s+|\\s+$//g;\n        if ($line_split[0] eq $variable) {\n            if (defined $line_split[1] && $line_split[1] !~ /^\\s*$/) {\n                # trim excess whitespace from left and right\n                $line_split[1] =~ s/^\\s+|\\s+$//g;\n                push @values, $line_split[1];\n            }\n        }\n    }\n    close $file_handle;\n\n    return @values;\n}\n\n# Given a file, a variable, and a value, append the variable setting to the file.\nsub append_config_variable_to_file {\n    my $file = shift;\n    my $variable = shift;\n    my $value = shift;\n\n    open my $file_handle, '>>', $file or die \"Could not open file $file $!\";\n    print $file_handle \"${variable} = ${value}\\n\";\n    close $file_handle;\n}\n\n# Wrapper for read_single_config_variable_from_arbitrary_file that uses a given\n# ast_path and repo name. This makes it easier for some of the repo subroutines to\n# do proper error handling.\nsub read_single_config_variable {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n    my $variable = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    unless (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"repo ${bold_stderr}${repo}${reset_stderr} does not exist\");\n        return 0;\n    }\n\n    my $repo_config_file = File::Spec->catfile($repo_subfolder, $REPO_CONFIG);\n    unless (-f $repo_config_file) {\n        ast_utilities::error_output($program_name, \"could not find config file for repo ${bold_stderr}${repo}${reset_stderr}\");\n        return 0;\n    }\n\n    my $value = read_single_config_variable_from_arbitrary_file($repo_config_file, $variable);\n\n    if ($value eq '') {\n        ast_utilities::error_output($program_name, \"failed to parse config setting \\'${variable}\\' for repo ${bold_stderr}${repo}${reset_stderr}\");\n        return undef;\n    }\n\n    return $value;\n}\n\n# Wrapper for read_multiple_config_variables_from_arbitrary_file that uses a given\n# ast_path and repo name. This makes it easier for some of the repo subroutines to\n# do proper error handling.\nsub read_multiple_config_variables {\n    my $ast_path = shift;\n    my $program_name = shift;\n    my $quiet = shift;\n    my $repo = shift;\n    my $variable = shift;\n\n    my $repo_subfolder = File::Spec->catfile($ast_path, $REPOS_FOLDER, $repo);\n    unless (-d $repo_subfolder) {\n        ast_utilities::error_output($program_name, \"repo ${bold_stderr}${repo}${reset_stderr} does not exist\");\n        return 0;\n    }\n\n    my $repo_config_file = File::Spec->catfile($repo_subfolder, $REPO_CONFIG);\n    unless (-f $repo_config_file) {\n        ast_utilities::error_output($program_name, \"could not find config file for repo ${bold_stderr}${repo}${reset_stderr}\");\n        return 0;\n    }\n\n    my @values = read_multiple_config_variables_from_arbitrary_file($repo_config_file, $variable);\n\n    return @values;\n}\n\n# Check that a repo name matches the approved name regex.\n# Params:\n#   $repo: the repo to check\n# Return: if the repo name matched the regex\nsub repo_regex_ok {\n    my $repo = shift;\n\n    if ($repo =~ m/^[_a-zA-Z0-9][_a-zA-Z0-9-]*$/) {\n        return 1;\n    }\n    return 0;\n}\n\n# Perl modules must return a value. Returning a value perl considers \"truthy\"\n# signals that the module loaded successfully.\n1;\n"
  },
  {
    "path": "atlas-shell-tools/scripts/common/ast_tty.pm",
    "content": "package ast_tty;\n\nuse warnings;\nuse strict;\n\nuse Exporter qw(import);\n\n# Export symbols: variables and subroutines\nour @EXPORT = qw(\n    is_no_colors\n    is_no_colors_stdout\n    is_no_colors_stderr\n    ansi_red\n    ansi_green\n    ansi_magenta\n    ansi_bold\n    ansi_reset\n    ansi_begin_underln\n    ansi_end_underln\n    terminal_width\n);\n\nmy $no_colors_stdout = is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : ansi_red();\nmy $green_stdout = $no_colors_stdout ? \"\" : ansi_green();\nmy $magenta_stdout = $no_colors_stdout ? \"\" : ansi_magenta();\nmy $bold_stdout = $no_colors_stdout ? \"\" : ansi_bold();\nmy $bunl_stdout = $no_colors_stdout ? \"\" : ansi_begin_underln();\nmy $eunl_stdout = $no_colors_stdout ? \"\" : ansi_end_underln();\nmy $reset_stdout = $no_colors_stdout ? \"\" : ansi_reset();\n\nmy $no_colors_stderr = is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : ansi_red();\nmy $green_stderr = $no_colors_stderr ? \"\" : ansi_green();\nmy $magenta_stderr = $no_colors_stderr ? \"\" : ansi_magenta();\nmy $bold_stderr = $no_colors_stderr ? \"\" : ansi_bold();\nmy $reset_stderr = $no_colors_stderr ? \"\" : ansi_reset();\n\n# Determine if we should disable text/color formatting for output. Various\n# conditions are checked, and if none of them trigger then we can use colors!\n# We also make one check for explicit use of colors, to allow a case where a\n# user has set NO_COLOR, but would like to make an exception for atlas-shell-tools.\n# Params: none\n# Return: 1 if no colors, 0 otherwise\nsub is_no_colors {\n    # check for dumb\n    # TODO need to check for xterm too?\n    if ($ENV{'TERM'} eq \"dumb\") {\n        return 1;\n    }\n\n    # explicitly use colors for atlas-shell-tools\n    if (exists $ENV{'ATLAS_SHELL_TOOLS_USE_COLOR'}) {\n        return 0;\n    }\n\n    # respect the NO_COLOR env var\n    if (exists $ENV{'NO_COLOR'}) {\n        return 1;\n    }\n\n    if (exists $ENV{'ATLAS_SHELL_TOOLS_NO_COLOR'}) {\n        return 1;\n    }\n\n    return 0;\n}\n\n# Same as the is_no_colors check, but also looks to see if stdout is a TTY.\n# Params: none\n# Return: 1 if no colors, 0 otherwise\nsub is_no_colors_stdout {\n    my $no_colors = is_no_colors();\n    my $is_stdout_tty = -t STDOUT ? 1 : 0;\n    return 1 if $no_colors || !$is_stdout_tty;\n    return 0;\n}\n\n# Same as the is_no_colors check, but also looks to see if stderr is a TTY.\n# Params: none\n# Return: 1 if no colors, 0 otherwise\nsub is_no_colors_stderr {\n    my $no_colors = is_no_colors();\n    my $is_stderr_tty = -t STDERR ? 1 : 0;\n    return 1 if $no_colors || !$is_stderr_tty;\n    return 0;\n}\n\nsub ansi_red {\n    return `tput setaf 1`;\n}\n\nsub ansi_green {\n    return `tput setaf 2`;\n}\n\nsub ansi_magenta {\n    return `tput setaf 5`;\n}\n\nsub ansi_bold {\n    return `tput bold`;\n}\n\nsub ansi_blink {\n    return `tput blink`;\n}\n\nsub ansi_reset {\n    return `tput sgr0`;\n}\n\nsub ansi_begin_underln {\n    return `tput smul`;\n}\n\nsub ansi_end_underln {\n    return `tput rmul`;\n}\n\nsub terminal_width {\n    # 'tput' returns a string with a trailing newline\n    my $cols = `tput cols`;\n\n    # Explicitly convert to an integer here, removing the newline\n    # This allows allows for calling code to do math with the value\n    return int($cols);\n}\n\n# Perl modules must return a value. Returning a value perl considers \"truthy\"\n# signals that the module loaded successfully.\n1;\n"
  },
  {
    "path": "atlas-shell-tools/scripts/common/ast_utilities.pm",
    "content": "package ast_utilities;\n\nuse warnings;\nuse strict;\n\nuse Exporter qw(import);\nuse File::Path qw(make_path);\nuse List::Util qw(min);\nuse ast_tty;\nuse ast_log_subsystem;\nuse ast_preset_subsystem;\nuse ast_repo_subsystem;\n\n# Export symbols: variables and subroutines\nour @EXPORT = qw(\n    ATLAS_SHELL_TOOLS_VERSION\n    COMMAND_PROGRAM\n    CONFIG_PROGRAM\n    JAVA_NO_COLOR_SENTINEL\n    JAVA_COLOR_STDOUT\n    JAVA_NO_COLOR_STDOUT\n    JAVA_COLOR_STDERR\n    JAVA_NO_COLOR_STDERR\n    JAVA_USE_PAGER\n    JAVA_NO_USE_PAGER\n    JAVA_MARKER_SENTINEL\n    verify_environment_or_exit\n    create_data_directory\n    display_and_exit\n    getopt_failure_and_exit\n    error_output\n    warn_output\n    prompt\n    prompt_yn\n    get_pager\n    get_editor\n    get_man\n    string_starts_with\n    is_dir_empty\n    levenshtein\n    read_command_output\n);\n\nour $ATLAS_SHELL_TOOLS_VERSION = \"atlas-shell-tools version 1.0.0\";\n\nour $COMMAND_PROGRAM = 'atlas';\nour $CONFIG_PROGRAM = 'atlas-config';\n\nour $JAVA_COLOR_STDOUT = \"___atlas-shell-tools_color_stdout_SPECIALARGUMENT___\";\nour $JAVA_NO_COLOR_STDOUT = \"___atlas-shell-tools_nocolor_stdout_SPECIALARGUMENT___\";\nour $JAVA_COLOR_STDERR = \"___atlas-shell-tools_color_stderr_SPECIALARGUMENT___\";\nour $JAVA_NO_COLOR_STDERR = \"___atlas-shell-tools_nocolor_stderr_SPECIALARGUMENT___\";\nour $JAVA_USE_PAGER = \"___atlas-shell-tools_use_pager_SPECIALARGUMENT___\";\nour $JAVA_NO_USE_PAGER = \"___atlas-shell-tools_no_use_pager_SPECIALARGUMENT___\";\nour $JAVA_MARKER_SENTINEL = \"___atlas-shell-tools_LAST_ARG_MARKER_SENTINEL___\";\n\nmy $integrity_file = \".atlas-shell-tools-integrity-file\";\n\nmy $no_colors_stdout = ast_tty::is_no_colors_stdout();\nmy $red_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_red();\nmy $green_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_green();\nmy $magenta_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_bold();\nmy $bunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_begin_underln();\nmy $eunl_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_end_underln();\nmy $reset_stdout = $no_colors_stdout ? \"\" : ast_tty::ansi_reset();\n\nmy $no_colors_stderr = ast_tty::is_no_colors_stderr();\nmy $red_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_red();\nmy $green_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_green();\nmy $magenta_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_magenta();\nmy $bold_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_bold();\nmy $reset_stderr = $no_colors_stderr ? \"\" : ast_tty::ansi_reset();\n\n# Ensure that the necessary environment variables are configured. If not,\n# exit with an error.\n# Params: none\n# Return: none\nsub verify_environment_or_exit {\n    unless (defined $ENV{HOME}) {\n        print STDERR \"Error: HOME environment variable is not set\\n\";\n        exit 1;\n    }\n\n    unless (-d $ENV{HOME}) {\n        print STDERR \"Error: the directory referenced by HOME does not exist\\n\";\n        exit 1;\n    }\n\n    unless (-w $ENV{HOME}) {\n        print STDERR \"Error: the directory referenced by HOME is not writable\\n\";\n        exit 1;\n    }\n\n    unless (defined $ENV{ATLAS_SHELL_TOOLS_HOME}) {\n        print STDERR \"Error: ATLAS_SHELL_TOOLS_HOME environment variable is not set\\n\";\n        exit 1;\n    }\n\n    unless (-f \"$ENV{ATLAS_SHELL_TOOLS_HOME}/${integrity_file}\") {\n        print STDERR \"Error: ATLAS_SHELL_TOOLS_HOME environment variable is not a valid installation\\n\";\n        exit 1\n    }\n}\n\n# Create the XDG data directory. Defaults to \"$HOME/.local/share\" but respects\n# the XDG_DATA_HOME env variable if set.\n# Params: none\n# Return: the newly set data directory\nsub create_data_directory {\n    # The directory for data storage. Client code must access this variable thru\n    # create_data_directory(), which optionally modifies this variable based on the\n    # XDG_DATA_HOME environment variable.\n    my $data_directory = \"$ENV{HOME}/.local/share\";\n\n    # Respect XDG_DATA_HOME per the XDG Base Directory specification\n    # https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html\n    if (defined $ENV{XDG_DATA_HOME}) {\n        $data_directory = $ENV{XDG_DATA_HOME};\n    }\n\n    # Create data subdirectories if necessary\n    $data_directory = File::Spec->catfile($data_directory, 'atlas-shell-tools');\n    my $full_log4j_path = File::Spec->catfile($data_directory, $ast_log_subsystem::LOG4J_FOLDER);\n    my $full_module_path = File::Spec->catfile($data_directory, $ast_module_subsystem::MODULES_FOLDER);\n    my $full_presets_path = File::Spec->catfile($data_directory, $ast_preset_subsystem::PRESETS_FOLDER);\n    my $default_namespace_path = File::Spec->catfile($data_directory, $ast_preset_subsystem::PRESETS_FOLDER, $ast_preset_subsystem::DEFAULT_NAMESPACE);\n    my $full_repos_path = File::Spec->catfile($data_directory, $ast_repo_subsystem::REPOS_FOLDER);\n    make_path(\"$data_directory\", \"$full_module_path\", \"$full_log4j_path\",\n        \"$full_presets_path\", \"$default_namespace_path\", \"$full_repos_path\", {\n            verbose => 0,\n            mode    => 0755\n        });\n\n    # reset the log4j file if it is missing\n    my $log4j_file = File::Spec->catfile($data_directory, $ast_log_subsystem::LOG4J_FOLDER, $ast_log_subsystem::LOG4J_FILE);\n    unless (-f $log4j_file) {\n        ast_log_subsystem::reset_log4j($data_directory);\n    }\n\n    # reset the current namespace file if it is missing\n    my $current_namespace_file = File::Spec->catfile($data_directory, $ast_preset_subsystem::NAMESPACE_PATH);\n    unless (-f $current_namespace_file) {\n        ast_preset_subsystem::reset_namespace($data_directory);\n    }\n\n    # add a .global folder to all namespaces if it is missing\n    my @namespaces = ast_preset_subsystem::get_namespaces_array($data_directory);\n    foreach my $namespace (@namespaces) {\n        my $global_subfolder = File::Spec->catfile($data_directory, $ast_preset_subsystem::PRESETS_FOLDER, $namespace, $ast_preset_subsystem::GLOBAL_FOLDER);\n        make_path(\"$global_subfolder\", {\n            verbose => 0,\n            mode    => 0755\n        });\n    }\n\n    return $data_directory;\n}\n\n# Display the given message and exit. Default behaviour is to use pagination,\n# but this can be disabled with the \"skip_paging\" parameter.\n# Params:\n#   $message: the message text\n#   $skip_paging: a boolean value that determines if the pager should be skipped\n# Return: none\nsub display_and_exit {\n    my $message = shift;\n    my $skip_paging = shift;\n\n    unless (defined $skip_paging) {\n        $skip_paging = 0;\n    }\n\n    my @pager_command = get_pager();\n\n    if ($skip_paging) {\n        print \"$message\";\n    }\n    else {\n        # NOTE: there is no easy way to prevent shell interference should the pager\n        # command array contain only one element.\n        open PAGER, \"|-\", @pager_command or die $!;\n        print PAGER \"$message\";\n        close PAGER;\n    }\n\n    exit 0;\n}\n\n# Print a failure message for getopt failures.\n# Params:\n#   $program_name: the name of the failing program\n#   $subcommand_name: the optional name of the subcommand\n# Returns: none\nsub getopt_failure_and_exit {\n    my $program_name = shift;\n    my $subcommand_name = shift;\n    if (defined $subcommand_name) {\n        print STDERR \"Try '${bold_stderr}${program_name} ${subcommand_name} --help${reset_stderr}' for more information.\\n\";\n    }\n    else {\n        print STDERR \"Try '${bold_stderr}${program_name} --help${reset_stderr}' for more information.\\n\";\n    }\n    exit 1;\n}\n\n# Print a command error message. The format is:\n# \"$command: error: $message\"\n# This routine will use colors/formatting if allowed by environment settings.\n# This routine will place output on stderr.\n# Params:\n#   $command: the name of the command\n#   $message: the message\n# Return: none\nsub error_output {\n    my $command = shift;\n    my $message = shift;\n    my $no_colors = ast_tty::is_no_colors_stderr();\n    my $red = $no_colors ? \"\" : ast_tty::ansi_red();\n    my $bold = $no_colors ? \"\" : ast_tty::ansi_bold();\n    my $reset = $no_colors ? \"\" : ast_tty::ansi_reset();\n\n    print STDERR \"$command: ${red}${bold}error:${reset} $message\\n\"\n}\n\n# Print a command warn message. The format is:\n# \"$command: warn: $message\"\n# This routine will use colors/formatting if allowed by environment settings.\n# This routine will place output on stderr.\n# Params:\n#   $command: the name of the command\n#   $message: the message\n# Return: none\nsub warn_output {\n    my $command = shift;\n    my $message = shift;\n    my $no_colors = ast_tty::is_no_colors_stderr();\n    my $magenta = $no_colors ? \"\" : ast_tty::ansi_magenta();\n    my $bold = $no_colors ? \"\" : ast_tty::ansi_bold();\n    my $reset = $no_colors ? \"\" : ast_tty::ansi_reset();\n\n    print STDERR \"$command: ${magenta}${bold}warn:${reset} $message\\n\"\n}\n\n# Prompt the user for input.\n# Params:\n#   $query: the prompt string\n# Return: the input\nsub prompt {\n    my $query = shift; # take a prompt string as argument\n    local $| = 1;      # activate autoflush to immediately show the prompt\n    print $query;\n    my $answer = <STDIN>;\n    if (defined $answer) {\n        chomp($answer);\n    }\n    return $answer;\n}\n\n# Prompt the user for y/n confirmation.\n# Params:\n#   $query: the prompt string\n# Return: 1 if user gave y, 0 if user gave n\nsub prompt_yn {\n    my $query = shift;\n    my $answer = prompt(\"$query\\n[y/N]: \");\n    unless (defined $answer) {\n        return 0;\n    }\n    return 1 if lc($answer) eq 'y';\n    return 0;\n}\n\n# Get a pager command capable of displaying formatting codes. Checks the value\n# of the ATLAS_SHELL_TOOLS_PAGER env variable, and uses that instead if it is set.\n# If that is unset, fall back to PAGER. If that is unset, try a default.\n# Params: none\n# Return: the pager command array\nsub get_pager {\n    my @pager_command = ();\n\n    if (defined $ENV{ATLAS_SHELL_TOOLS_PAGER} && $ENV{ATLAS_SHELL_TOOLS_PAGER} ne '') {\n        @pager_command = split /\\s+/, $ENV{ATLAS_SHELL_TOOLS_PAGER};\n    }\n    elsif (defined $ENV{PAGER} && $ENV{PAGER} ne '') {\n        @pager_command = split /\\s+/, $ENV{PAGER};\n    }\n    else {\n        push @pager_command, 'less';\n\n        # Options (see less(1) for more info):\n        # -c -> clear the screen before displaying\n        # -S -> chop long lines instead of wrapping them\n        # -R -> actually display ANSI \"color\" control sequences as colors/formatting\n        # -M -> use verbose prompt\n        # -i -> searches ignore case\n        # -s -> squeeze consecutive blank lines\n        # TODO consider -F, -X options here?\n        # https://unix.stackexchange.com/questions/107315/less-quit-if-one-screen-without-no-init\n        push @pager_command, '-cSRMis';\n    }\n\n    return @pager_command;\n}\n\n# Get an editor command capable of displaying and editing a text file. Checks\n# the value of the ATLAS_SHELL_TOOLS_EDITOR env variable, and uses that instead if it points to an\n# editor. If that is unset, fall back to EDITOR. If that is unset, try a default.\n# Params: none\n# Return: the editor command\nsub get_editor {\n    my @editor_command = ();\n\n    if (defined $ENV{ATLAS_SHELL_TOOLS_EDITOR} && $ENV{ATLAS_SHELL_TOOLS_EDITOR} ne '') {\n        @editor_command = split /\\s+/, $ENV{ATLAS_SHELL_TOOLS_EDITOR};\n    }\n    elsif (defined $ENV{EDITOR} && $ENV{EDITOR} ne '') {\n        @editor_command = split /\\s+/, $ENV{EDITOR};\n    }\n    else {\n        push @editor_command, 'vim';\n\n        # The '+' option tells vim to start with the cursor at the end of the file.\n        # This is generally convenient for most atlas-shell-tools use-cases.\n        push @editor_command, '+';\n    }\n\n    return @editor_command;\n}\n\n# Get a man command capable of displaying some desired manpage.\n# Params:\n#   $skip_paging: optionally disable paging for man\n# Return: the man command array\nsub get_man {\n    my @man_command = ();\n    my $skip_paging = shift;\n\n    push @man_command, 'man';\n\n    if ($skip_paging) {\n        push @man_command, '-P';\n        push @man_command, 'cat';\n    }\n\n    return @man_command;\n}\n\n# Check if a given string starts with a given prefix.\n# Params:\n#   $string: the string to search\n#   $prefix: the prefix to check\n# Return: 1 if $string starts with $prefix, 0 otherwise\nsub string_starts_with {\n    my $string = shift;\n    my $prefix = shift;\n\n    return substr($string, 0, length($prefix)) eq $prefix;\n}\n\n# Check if a given directory is empty.\n# Params:\n#   $dir: the directory to check\n# Return: 1 if the directory is empty, 0 otherwise\nsub is_dir_empty {\n    my ($dir) = @_;\n\n    opendir my $h, $dir\n        or die \"Cannot open directory: '$dir': $!\";\n\n    while (defined(my $entry = readdir $h)) {\n        return unless $entry =~ /^[.][.]?\\z/;\n    }\n\n    closedir $h;\n\n    return 1;\n}\n\n# Compute the Levenshtein distance between two strings.\n# Params:\n#   $string1: the first string\n#   $string2: the second string\n# Return: the Levenshtein distnace\nsub levenshtein {\n    my $string1 = shift;\n    my $string2 = shift;\n\n    # split the strings at each character\n    my @letters1 = split //, $string1;\n    my @letters2 = split //, $string2;\n\n    # memoization table\n    my @distance;\n    $distance[$_][0] = $_ foreach (0 .. @letters1);\n    $distance[0][$_] = $_ foreach (0 .. @letters2);\n\n    foreach my $i (1 .. @letters1) {\n        foreach my $j (1 .. @letters2) {\n            my $cost = $letters1[$i - 1] eq $letters2[$j - 1] ? 0 : 1;\n            $distance[$i][$j] = min($distance[$i - 1][$j] + 1, $distance[$i][$j - 1] + 1, $distance[$i - 1][$j - 1] + $cost);\n        }\n    }\n\n    return $distance[@letters1][@letters2];\n}\n\n# Read the output of a given command array.\n# Params:\n#   $command_ref: a reference to an array containing the command args\n# Return: the output of a given command array\nsub read_command_output {\n    my $command_ref = shift;\n\n    my @command = @{$command_ref};\n    open COMMAND, \"-|\", @command or die $!;\n    my $output = '';\n    while (<COMMAND>) {\n        # Not the most efficient way to do things.\n        # Perhaps some kind of slurp is needed. File::Slurp could work but does\n        # have an outstanding Unicode bug. Need to investigate more.\n        $output = $output . $_;\n    }\n    close COMMAND;\n\n    return $output;\n}\n\n# Perl modules must return a value. Returning a value perl considers \"truthy\"\n# signals that the module loaded successfully.\n1;\n"
  },
  {
    "path": "build.gradle",
    "content": "plugins {\n    id 'checkstyle'\n    id 'groovy'\n    id 'idea'\n    id 'jacoco'\n    id 'java'\n    id 'java-library'\n    id 'maven-publish'\n    id 'signing'\n    id 'com.diffplug.spotless' version '6.25.0'\n    id 'com.google.protobuf' version '0.8.18'\n    id 'org.sonarqube' version '3.3'\n}\n\napply from: 'dependencies.gradle'\napply from: 'gradle/quality.gradle'\napply from: 'gradle/protobuf.gradle'\napply from: 'gradle/pyatlas.gradle'\napply from: 'gradle/deployment.gradle'\n\ndescription = \"Atlas Library\"\n\nsourceCompatibility=11\ntargetCompatibility=11\n\nrepositories\n{\n    // For geotools\n    maven {\n      url \"https://repo.osgeo.org/repository/release/\"\n      content {\n        // osgeo removed the jar and added a -norce version\n        excludeVersion(\"log4j\", \"log4j\", \"1.2.17\")\n      }\n    }\n    mavenCentral()\n}\n\nconfigurations\n{\n    shaded.extendsFrom(implementation)\n}\n\ndependencies\n{\n    implementation packages.artifact\n    implementation packages.checkstyle\n    api            packages.classgraph\n    implementation packages.commons.cli\n    implementation packages.commons.compress\n    implementation packages.commons.csv\n    implementation packages.commons.io\n    implementation packages.commons.lang\n    implementation packages.commons.math\n    implementation packages.commons.text\n    implementation packages.diff_utils\n    api packages.geotools\n    api            packages.groovy\n    implementation packages.groovy_json\n    implementation packages.gson\n    api            packages.guava\n    api            packages.http\n    api            packages.jackson.core\n    api            packages.jackson.databind\n    api            packages.jackson.dataformat\n    api            packages.jim_fs\n    api packages.jsonassert\n    api            packages.jts\n    // Support JUnit 3/4 tests\n    api            packages.junit.junit4 // only API level due to CoreTestRule\n    implementation packages.opencsv\n    api            packages.osmosis.core\n    implementation packages.osmosis.hstore\n    implementation packages.osmosis.osmbinary\n    implementation packages.osmosis.pbf\n    implementation packages.osmosis.xml\n    implementation packages.protobuf_java\n    implementation packages.protoc\n    implementation packages.slf4j.api\n    implementation packages.spatial4j\n\n    testImplementation packages.checkstyle_tests\n    // Google Truth is needed for checkstyle tests (as of checkstyle 9.2.1)\n    testImplementation packages.google_truth\n    testImplementation packages.junit.api\n    testImplementation packages.junit.engine\n    testImplementation packages.junit.junit4\n    testImplementation packages.junit.params\n    testImplementation packages.junit.vintage\n\n    checkstyle files(\"build/libs/${project.name}-${project.version}.jar\")\n    checkstyle packages.checkstyle\n\n    shaded packages.log4j.api\n    shaded packages.log4j.slf4j\n\n}\n\ntask shaded(type: Jar) {\n    archiveBaseName = project.name\n    classifier = 'shaded'\n    from\n    {\n        configurations.shaded.collect\n        {\n            it.isDirectory() ? it : zipTree(it).matching{\n                exclude\n                {\n                    it.path.contains('META-INF') && (it.path.endsWith('.SF') || it.path.endsWith('.DSA') || it.path.endsWith('.RSA'))\n                }\n            }\n        }\n    }\n    with jar\n    zip64 = true\n}\n\n/**\n * Artifact related items\n */\ntask javadocJar(type: Jar) {\n    classifier = 'javadoc'\n    from javadoc\n}\n\ntask sourcesJar(type: Jar) {\n    classifier = 'sources'\n    from sourceSets.main.allSource\n}\n\nartifacts\n{\n    archives javadocJar, sourcesJar\n}\n\n/*\n * This is to skip the tasks for which there is a skip<TaskName>=true\n * environment variable\n */\ndef skippedTaskNames = System.getenv().findAll { key, value ->\n    key.startsWith(\"skip\") && value.equalsIgnoreCase(\"true\")\n}.keySet().collect { it.substring(4) }\ngradle.startParameter.excludedTaskNames += skippedTaskNames\n\nidea {\n    project {\n        languageLevel = '1.8'\n    }\n}\n\n/*\n * Workaround Gradle not liking duplicate files\n * I suspect that it is due to fake duplicates.\n * For more information, see https://github.com/gradle/gradle/issues/17236\n * (AKA, remove this when that issue is fixed)\n *\n * Note: It may be useful to disable this from time to time on Gradle update\n *       to see if either it is fixed or if there is better debugging\n *       information available.\n */\ntasks.each {\n  task -> if (task.hasProperty(\"duplicatesStrategy\")) {\n    task.setProperty(\"duplicatesStrategy\", \"EXCLUDE\")\n  }\n}\n"
  },
  {
    "path": "config/checkstyle/arrangement.txt",
    "content": "interface,public,non_static\ninterface,protected,non_static\ninterface,package_private,non_static\ninterface,private,non_static\n\nenum,public,non_static\nenum,protected,non_static\nenum,package_private,non_static\nenum,private,non_static\n\nclass,public,non_static\nclass,protected,non_static\nclass,package_private,non_static\nclass,private,non_static\n\n# Here do not force order on static variables\n# as they might have dependencies on each other\n# field,public,static\n# field,protected,static\n# field,package_private,static\n# field,private,static\n\nstatic_initializer_block,,non_static\n\nfield,public,non_static\nfield,protected,non_static\nfield,package_private,non_static\nfield,private,non_static\n\nmethod,public,static\nmethod,protected,static\nmethod,package_private,static\nmethod,private,static\n\ninitializer_block,,non_static\n\nconstructor,public,non_static\nconstructor,protected,non_static\nconstructor,package_private,non_static\nconstructor,private,non_static\n\nmethod,public,non_static\nmethod,protected,non_static\nmethod,package_private,non_static\nmethod,private,non_static\n\n"
  },
  {
    "path": "config/checkstyle/checkstyle.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE module PUBLIC\n    \"-//Puppy Crawl//DTD Check Configuration 1.1//EN\"\n    \"http://www.puppycrawl.com/dtds/configuration_1_1.dtd\">\n\n<module name=\"Checker\">\n\t<module name=\"NewlineAtEndOfFile\" />\n\t<module name=\"TreeWalker\">\n\t\t<!-- Checks for Javadoc comments. -->\n\t\t<module name=\"JavadocMethod\">\n\t\t\t<property name=\"accessModifiers\" value=\"public, protected, package\" />\n\t\t</module>\n\t\t<module name=\"JavadocType\">\n\t\t\t<property name=\"authorFormat\" value=\"\\S\" />\n\t\t</module>\n\t\t<module name=\"JavadocStyle\">\n\t\t\t<property name=\"checkFirstSentence\" value=\"false\" />\n\t\t</module>\n\n\t\t<!-- Checks for Naming Conventions. -->\n\t\t<module name=\"LocalFinalVariableName\" >\n\t\t\t<property name=\"format\" value=\"^([a-z][a-zA-Z0-9]{2,}$|[e])$\"/>\n\t\t\t<property name=\"tokens\" value=\"VARIABLE_DEF,PARAMETER_DEF\"/>\n\t\t</module>\n\t\t<module name=\"LocalVariableName\" >\n\t\t\t<property name=\"format\" value=\"^([a-z][a-zA-Z0-9]{2,}$|[e])$\"/>\n\t\t\t<property name=\"allowOneCharVarInForLoop\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"MemberName\" >\n\t\t\t<property name=\"format\" value=\"^([a-z][a-zA-Z0-9]{2,}$|[e])$\"/>\n\t\t</module>\n\t\t<module name=\"MethodName\" >\n\t\t\t<property name=\"format\" value=\"^([a-z][a-zA-Z0-9]{2,}$|[e])$\"/>\n\t\t</module>\n\t\t<module name=\"PackageName\" />\n\t\t<module name=\"ParameterName\" >\n\t\t\t<property name=\"format\" value=\"^([a-z][a-zA-Z0-9]{2,}$|[e])$\"/>\n\t\t</module>\n\t\t<module name=\"TypeName\" />\n\n\t\t<!-- Checks for imports -->\n\t\t<module name=\"AvoidStarImport\" />\n\t\t<!-- Spotless allows for more groups than CustomImportOrder.\n\t\tSpotless: importOrder 'static java', 'static javax', 'static org', 'static com', 'static scala', 'java', 'javax', 'org', 'com', 'scala'\n\t\tThis enforces blank lines between `import java.*`, `import javax.*`, `import org*`, etc. CustomImportOrder only enforces spaces between groups,\n\t\tand expects no spaces between packages of the same group.\n\t\t<module name=\"CustomImportOrder\">\n\t\t\t<property name=\"customImportOrderRules\" value=\"STATIC###STANDARD_JAVA_PACKAGE###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE\"/>\n\t\t\t<property name=\"separateLineBetweenGroups\" value=\"true\"/>\n\t\t\t<property name=\"sortImportsInGroupAlphabetically\" value=\"true\"/>\n\t\t\t<property name=\"specialImportsRegExp\" value=\"^org\\.\"/>\n\t\t\t<property name=\"thirdPartyPackageRegExp\" value=\"^com\\.\"/>\n\t\t</module>\n\t\t-->\n\t\t<module name=\"IllegalImport\" />\n\t\t<module name=\"ImportOrder\">\n\t\t\t<property name=\"groups\" value=\"java,javax,org,com,*\"/>\n\t\t\t<property name=\"ordered\" value=\"true\"/>\n\t\t\t<property name=\"separated\" value=\"true\"/>\n\t\t\t<property name=\"option\" value=\"top\"/>\n\t\t</module>\n\t\t<module name=\"RedundantImport\" />\n\t\t<module name=\"UnusedImports\">\n\t\t\t<property name=\"processJavadoc\" value=\"true\" />\n\t\t</module>\n\n\t\t<!-- Checks for metrics -->\n\n\t\t<!-- Checks for Size Violations. -->\n\n\t\t<!-- Checks for whitespace -->\n\n\t\t<!-- Modifier Checks -->\n\t\t<module name=\"ModifierOrder\" />\n\t\t<module name=\"RedundantModifier\" />\n\n\n\t\t<!-- Checks for blocks -->\n\t\t<module name=\"AvoidNestedBlocks\" >\n            <property name=\"allowInSwitchCase\" value=\"true\" />\n\t\t</module>\n\t\t<module name=\"LeftCurly\">\n\t\t\t<property name=\"option\" value=\"nl\" />\n\t\t</module>\n\t\t<module name=\"EmptyCatchBlock\" />\n\t\t<module name=\"NeedBraces\" />\n\t\t<module name=\"RightCurly\">\n\t\t\t<property name=\"option\" value=\"alone\" />\n\t\t</module>\n\n\n\t\t<!-- Checks for common coding problems -->\n\t\t<module name=\"CovariantEquals\"/>\n\t\t<module name=\"DefaultComesLast\"/>\n\t\t<module name=\"EmptyStatement\" />\n\t\t<module name=\"EqualsAvoidNull\"/>\n\t\t<module name=\"EqualsHashCode\" />\n\t\t<module name=\"FallThrough\"/>\n\t\t<module name=\"FinalLocalVariable\" >\n\t\t\t<property name=\"tokens\" value=\"VARIABLE_DEF,PARAMETER_DEF\"/>\n\t\t\t<property name=\"validateEnhancedForLoopVariable\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"IllegalInstantiation\" />\n\t\t<module name=\"IllegalThrows\"/>\n\t\t<module name=\"InnerAssignment\"/>\n        <module name=\"MagicNumber\" >\n\t\t\t<property name=\"ignoreAnnotation\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"MissingSwitchDefault\" />\n\t\t<module name=\"ModifiedControlVariable\"/>\n\t\t<module name=\"MultipleStringLiterals\" >\n            <property name=\"allowedDuplicates\" value=\"10\"/>\n        </module>\n\t\t<module name=\"MultipleVariableDeclarations\"/>\n\t\t<module name=\"NoClone\"/>\n\t\t<module name=\"NoFinalizer\"/>\n\t\t<module name=\"OneStatementPerLine\" />\n\t\t<module name=\"PackageDeclaration\"/>\n\t\t<module name=\"ParameterAssignment\"/>\n\t\t<module name=\"RequireThis\">\n    \t\t<property name=\"checkMethods\" value=\"false\"/>\n\t\t\t<property name=\"validateOnlyOverlapping\" value=\"false\"/>\n\t\t</module>\n\t\t<module name=\"SimplifyBooleanExpression\" />\n\t\t<module name=\"SimplifyBooleanReturn\" />\n\t\t<module name=\"StringLiteralEquality\"/>\n\t\t<module name=\"UnnecessaryParentheses\" />\n\n\t\t<!-- Checks for class design -->\n\t\t<module name=\"FinalClass\" />\n\t\t<module name=\"HideUtilityClassConstructor\" />\n\t\t<module name=\"MutableException\"/>\n\t\t<module name=\"OneTopLevelClass\"/>\n\t\t<module name=\"ThrowsCount\"/>\n\t\t<module name=\"VisibilityModifier\">\n\t\t\t\t<!-- Depending upon use, the @TempDir annotation may need to be added to the list -->\n\t\t\t\t<property name=\"ignoreAnnotationCanonicalNames\" value=\"com.google.common.annotations.VisibleForTesting,org.junit.ClassRule,org.junit.Rule,org.junit.jupiter.api.extension.RegisterExtension\" />\n\t\t</module>\n\n\n\t\t<!-- Miscellaneous other checks. -->\n\t\t<module name=\"ArrayTypeStyle\" />\n\t\t<module name=\"AvoidEscapedUnicodeCharacters\">\n\t\t\t<property name=\"allowEscapesForControlCharacters\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"CommentsIndentation\"/>\n\t\t<module name=\"FinalParameters\" />\n\t\t<!-- Skipping indentation until https://github.com/checkstyle/checkstyle/issues/3342 is resolved\n\t\t<module name=\"Indentation\" >\n\t\t\t<property name=\"lineWrappingIndentation\" value=\"8\"/>\n\t\t\t<property name=\"forceStrictCondition\" value=\"false\"/>\n\t\t</module> -->\n\t\t<module name=\"OuterTypeFilename\"/>\n\t\t<module name=\"TrailingComment\">\n\t\t\t<property name=\"legalComment\" value=\"NOSONAR\"/>\n \t\t</module>\n\t\t<module name=\"TodoComment\" />\n\t\t<module name=\"UpperEll\" />\n\t\t<module name=\"org.openstreetmap.atlas.utilities.checkstyle.ArrangementCheck\">\n\t\t\t<property name=\"arrangementDefinition\" value=\"${config_loc}/arrangement.txt\" />\n\t\t</module>\n\t</module>\n\n\t<module name=\"SuppressionFilter\">\n\t\t<property name=\"file\" value=\"${config_loc}/suppressions.xml\" />\n\t</module>\n</module>\n"
  },
  {
    "path": "config/checkstyle/suppressions.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE suppressions PUBLIC\n    \"-//Puppy Crawl//DTD Suppressions 1.1//EN\"\n    \"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd\">\n\n<suppressions>\n    <suppress checks=\"MagicNumberCheck\" files=\"src/test/*\" />\n    <suppress checks=\"MagicNumberCheck\" files=\"src/integrationTest/*\" />\n    <suppress checks=\"MultipleStringLiterals\" files=\"src/test/*\" />\n    <suppress checks=\"MultipleStringLiterals\" files=\"src/integrationTest/*\" />\n    <suppress checks=\"InnerAssignment\" files=\"src/test/*\" />\n    <suppress checks=\"InnerAssignment\" files=\"src/integrationTest/*\" />\n    <suppress checks=\"AvoidEscapedUnicodeCharacters\" files=\"src/main/java/org/openstreetmap/atlas/utilities/command/terminal/TTYAttribute.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/utilities/function/TernaryFunction.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/utilities/function/TernaryOperator.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/utilities/function/TernaryConsumer.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/utilities/function/QuaternaryFunction.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/utilities/function/QuaternaryOperator.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/utilities/function/SenaryFunction.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/utilities/function/SenaryOperator.java\" />\n    <suppress checks=\"MethodName\" files=\"src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/Parser.java\" />\n    <suppress checks=\"MethodName\" files=\"src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/Lexer.java\" />\n\n    <!-- Field name for \"WKT\" does not follow Java Bean standards-->\n    <suppress checks=\"MemberName\" files=\"src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/properties/ext/change/FeatureChangeProperties.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/properties/ext/change/FeatureChangeProperties.java\" />\n\n    <!-- Field name for \"id\" (which is used in some specs of geojson too) doesn't comply with checkstyle member name size rule (min 3 characters) -->\n    <suppress checks=\"MemberName\" files=\"src/test/java/org/openstreetmap/atlas/geography/geojson/parser/testdomain/BeanA.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/test/java/org/openstreetmap/atlas/geography/geojson/parser/testdomain/BeanA.java\" />\n    <suppress checks=\"MemberName\" files=\"src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/properties/ext/change/Descriptor.java\" />\n    <suppress checks=\"ParameterName\" files=\"src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/properties/ext/change/Descriptor.java\" />\n\n    <suppress checks=\".\" files=\"src/generated/*\" />\n</suppressions>\n"
  },
  {
    "path": "config/format/code_format.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<profiles version=\"12\">\n<profile kind=\"CodeFormatterProfile\" name=\"OSM Code Format\" version=\"12\">\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"49\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"100\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_lambda_body\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"enabled\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines\" value=\"2147483647\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_resources_in_try\" value=\"80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.source\" value=\"1.8\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.8\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_binary_operator\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.8\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"next_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"space\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"100\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n</profile>\n</profiles>\n"
  },
  {
    "path": "config/log4j/log4j.properties",
    "content": "log4j.rootLogger=INFO, stdout\n\n# Direct log messages to stdout\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] %c{1}:%L - %m%n\n\n# Remove unused logging\nlog4j.logger.org.reflections=WARN\n\n# Show our logs\nlog4j.logger.org.openstreetmap.atlas=TRACE\nlog4j.logger.org.openstreetmap.atlas.utilities=INFO\nlog4j.logger.org.openstreetmap.atlas.streaming.resource=INFO\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.items.complex.bignode=DEBUG\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.items.TurnRestriction=DEBUG\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.pbf=INFO\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.packed=INFO\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.delta=INFO\nlog4j.logger.org.openstreetmap.atlas.tags=INFO\n"
  },
  {
    "path": "dependencies.gradle",
    "content": "project.ext.versions = [\n    checkstyle: '9.3',\n    junit4: '4.13.2',\n    junit5: '5.8.2',\n    jacoco: '0.8.7',\n    slf4j: '1.7.32',\n    log4j: '2.17.1',\n    opencsv: '2.3',\n    gson: '2.9.0',\n    http: '4.5.13',\n    jts: '1.18.2',\n    spatial4j: '0.8',\n    geotools: '26.2',\n    osmosis: '0.48.3',\n    commons_cli: '1.5.0',\n    commons_compress: '1.21',\n    commons_csv: '1.9.0',\n    commons_io: '2.11.0',\n    commons_lang: '3.12.0',\n    commons_math: '3.6.1',\n    commons_text: '1.9',\n    classgraph: '4.8.139',\n    guava: '31.0.1-jre',\n    google_truth: '1.1.3',\n    jsonassert:'1.5.0',\n    jackson_core:'2.10.5',\n    jackson_databind:'2.10.5',\n    jackson_dataformat_yaml:'2.10.5',\n    protobuf_java:'3.19.1',\n    protoc:'3.19.1',\n    artifact:'3.8.4',\n    groovy: '3.0.9',\n    atlas_checkstyle: '6.6.1',\n    diff_utils: '4.0',\n    groovy_json: '3.0.9',\n    jim_fs: '1.2'\n]\n\nproject.ext.packages = [\n    junit: [\n        junit4: \"junit:junit:${versions.junit4}\",\n        api: \"org.junit.jupiter:junit-jupiter-api:${versions.junit5}\",\n        engine: \"org.junit.jupiter:junit-jupiter-engine:${versions.junit5}\",\n        params: \"org.junit.jupiter:junit-jupiter-params:${versions.junit5}\",\n        vintage: \"org.junit.vintage:junit-vintage-engine:${versions.junit5}\",\n    ],\n    slf4j: [\n        api: \"org.slf4j:slf4j-api:${versions.slf4j}\",\n    ],\n    log4j: [\n      api: \"org.apache.logging.log4j:log4j:${versions.log4j}\",\n      slf4j: \"org.apache.logging.log4j:log4j-slf4j-impl:${versions.log4j}\",\n    ],\n    opencsv: \"net.sf.opencsv:opencsv:${versions.opencsv}\",\n    google_truth: \"com.google.truth:truth:${versions.google_truth}\",\n    gson: \"com.google.code.gson:gson:${versions.gson}\",\n    http: \"org.apache.httpcomponents:httpclient:${versions.http}\",\n    jts: \"org.locationtech.jts:jts-core:${versions.jts}\",\n    spatial4j: \"org.locationtech.spatial4j:spatial4j:${versions.spatial4j}\",\n    geotools: \"org.geotools:gt-shapefile:${versions.geotools}\",\n    osmosis: [\n        core: \"org.openstreetmap.osmosis:osmosis-core:${versions.osmosis}\",\n        hstore:\"org.openstreetmap.osmosis:osmosis-hstore-jdbc:${versions.osmosis}\",\n        osmbinary: \"org.openstreetmap.osmosis:osmosis-osm-binary:${versions.osmosis}\",\n        pbf: \"org.openstreetmap.osmosis:osmosis-pbf:${versions.osmosis}\",\n        pg:\"org.openstreetmap.osmosis:osmosis-pgsnapshot:${versions.osmosis}\",\n        xml: \"org.openstreetmap.osmosis:osmosis-xml:${versions.osmosis}\",\n    ],\n    commons:[\n        cli: \"commons-cli:commons-cli:${versions.commons_cli}\",\n        compress: \"org.apache.commons:commons-compress:${versions.commons_compress}\",\n        csv : \"org.apache.commons:commons-csv:${versions.commons_csv}\",\n        io: \"commons-io:commons-io:${versions.commons_io}\",\n        lang: \"org.apache.commons:commons-lang3:${versions.commons_lang}\",\n        math: \"org.apache.commons:commons-math3:${versions.commons_math}\",\n        text: \"org.apache.commons:commons-text:${versions.commons_text}\"\n    ],\n    classgraph: \"io.github.classgraph:classgraph:${versions.classgraph}\",\n    guava: \"com.google.guava:guava:${versions.guava}\",\n    jsonassert: \"org.skyscreamer:jsonassert:${versions.jsonassert}\",\n    jackson:[\n        core: \"com.fasterxml.jackson.core:jackson-core:${versions.jackson_core}\",\n        databind: \"com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}\",\n        dataformat: \"com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${versions.jackson_dataformat_yaml}\"\n    ],\n    protobuf_java: \"com.google.protobuf:protobuf-java:${versions.protobuf_java}\",\n    protoc: \"com.google.protobuf:protoc:${versions.protoc}\",\n    artifact: \"org.apache.maven:maven-artifact:${versions.artifact}\",\n    groovy: \"org.codehaus.groovy:groovy:${versions.groovy}\",\n    checkstyle: \"com.puppycrawl.tools:checkstyle:${versions.checkstyle}\",\n    checkstyle_tests: \"com.puppycrawl.tools:checkstyle:${versions.checkstyle}:tests\",\n    atlas_checkstyle: \"org.openstreetmap.atlas:atlas:${versions.atlas_checkstyle}\",\n    diff_utils: \"io.github.java-diff-utils:java-diff-utils:${versions.diff_utils}\",\n    groovy_json: \"org.codehaus.groovy:groovy-json:${versions.groovy_json}\",\n    jim_fs: \"com.google.jimfs:jimfs:${versions.jim_fs}\"\n]\n"
  },
  {
    "path": "gradle.properties",
    "content": "group=org.openstreetmap.atlas\nversion=7.0.10-SNAPSHOT\n\nmaven2_url=https://oss.sonatype.org/service/local/staging/deploy/maven2/\nsnapshot_url=https://oss.sonatype.org/content/repositories/snapshots/\nproject_name=\"OSM Atlas Library\"\nproject_description=\"Library to load OSM data into an Atlas format\"\nproject_url=https://github.com/osmlab/atlas\nproject_license_url=https://github.com/osmlab/atlas/blob/main/LICENSE\nproject_license_slug=\"BSD 3 Clause\"\nproject_developer=matthieun\nproject_scm=scm:git:https://github.com/osmlab/atlas.git\n"
  },
  {
    "path": "pyatlas/README.md",
    "content": "# PyAtlas\n#### A simplified Atlas API for Python\n\n----\n## Getting Started\nTo get setup in a new project folder, run:\n\n    $ mkdir newproj && cd newproj\n    $ virtualenv venv --python=python3\n    $ source venv/bin/activate\n\nNOTE: `pyatlas` will automatically install the dependencies it needs, including\nthe Protocol Buffers Python runtime - `protobuf-2.6.1`. \nTherefore, it is highly recommended that you develop `pyatlas` based projects in\na Python virtual environment - you may need to install `virtualenv` if you have not already. \n(If you want to create a `pyatlas` distribution that does not automatically pull\nin dependencies, see the next section.)\n\nNow that you have your virtual environment set up, you can install `pyatlas` with:\n\n    (venv) $ pip install pyatlas\n\nIf pip is unable to find the `pyatlas` package, you may need to build it from\nsource yourself. Check the next section for more info.\n\nTo test that everything went smoothly, create a file `helloatlas.py` with the following code:\n```python\nfrom pyatlas import pyatlas_globalfunc\npyatlas_globalfunc.hello_atlas()\n```\nNow run:\n\n    (venv) $ python helloatlas.py\n\nIf you see:\n\n    Hello Atlas!\n\nthen you're good to go!\n\n----\n## Building the `pyatlas` module\nTo build the `pyatlas` module from source, run:\n\n    $ git clone https://github.com/osmlab/atlas.git\n    $ cd atlas\n    $ ./gradlew cleanPyatlas buildPyatlas\n\nThis will generate a wheel file at `pyatlas/dist`. You can now install this with `pip` like\n\n    $ cd /path/to/project/that/uses/pyatlas\n    $ virtualenv venv --python=python2.7\n    $ source venv/bin/activate\n    $ pip install /path/to/atlas/pyatlas/dist/pyatlas-VERSION.whl\n\nAgain, it is recommended that you do this in the desired virtual environment.\n\nIf you want to build a `pyatlas` wheel file that does not automatically pull dependencies,\nopen up `setup.py` and remove the lines that say\n    \n    install_requires=[\n    .\n    .\n    .\n    ],\n\nThen re-run the `./gradlew cleanPyatlas buildPyatlas` command from above and reinstall\nusing `pip`. Note that you will now need to manage the required dependencies manually.\n\n### Note on the formatter\n`pyatlas` uses the `yapf` formatting library to check for code format issues when building.\nIf you are running into issues after modifying `pyatlas`, try running\n\n    ./gradlew applyFormatPyatlas\n\nNow `pyatlas` should make it past the `CHECK` format step!\n\nNote there is an issue that causes the formatter to goof if a source file does not\nend with a newline (\\n) character.\nIf the `CHECK` format step is consistently failing after repeated `APPLY` steps,\nand you are seeing a message like the following:\n\n    atlas.py: found issue, reformatting...\n\nwith no formatter diff being displayed, check to make sure that the file has an ending newline. \n\n----\n## Documentation\n`pyatlas` documentation is automatically generated using the `pydoc` tool and\nstored in the `doc` folder. To build\nthe documentation, run the gradle build command:\n\n    $ ./gradlew cleanPyatlas buildPyatlas\n\nThis will generate HTML files detailing the functions and classes available in each module.\n\n----\n## Some sample use cases\n`pyatlas` is a highly capable subset of the API provided by the Java `Atlas`. Here are some examples\nto get you started. Note that all of these examples were ran using the `test.atlas`\nprovided in the `resources` folder, and assume that you have an atlas variable defined like:\n\n```python\nfrom pyatlas.atlas import Atlas\natlas = Atlas('/path/to/atlas/pyatlas/resources/test.atlas')\n```\n\n#### Getting features and metadata\nYou can get filtered iterables over an `Atlas`'s features using the methods provided in the `Atlas` class.\n\n```python\n# print all Nodes\nfor node in atlas.nodes():\n    print node\n\n# print all Edges that have 'key1' as a tag key\nfor edge in atlas.edges(predicate=lambda e: 'key1' in e.get_tags().keys()):\n    print edge\n```\n\nYou can also get a feature with a specific identifier like:\n\n```python\n# print the Relation with Atlas ID 2\nprint atlas.relation(2)\n```\n\nMetadata about the `Atlas` is also available. For a quick sample, try something like:\n\n```python\nmetadata = atlas.metadata()\nprint metadata.number_of_points\nprint metadata.country\n```\n\nCheck out `doc/atlas.html` and `doc/atlas_metadata.html` for more information.\n\n#### Operating on features\nThe `Atlas` features themselves support a set of operations defined in their respective classes.\n\nHere is a quick example:\n\n```python\n# print the tag dict for Point with Atlas ID 3\nprint atlas.point(3).get_tags()\n\n# print all Relations of which the Node with Atlas ID 1 is a member\nfor relation in atlas.node(1).relations():\n    print relation\n\n# print all the members of Relation with Atlas ID 1\nfor member in atlas.relation(1).get_members():\n    # print the RelationMember object\n    print member\n    # print the actual AtlasEntity contained in the RelationMember\n    print member.get_entity()\n```\n\n`Node`s and `Edge`s, in particular, support traversal through their connectivity API.\nHere are just the basics of what you can do with the connectivity interface:\n\n```python\n# print Edges connected to Node with ID 3\nfor edge in atlas.node(3).in_edges():\n    print edge\nfor edge in atlas.node(3).out_edges():\n    print edge\nfor edge in atlas.node(3).connected_edges():\n    print edge\n\n# print the start and end Nodes of Edge 1\nprint atlas.edge(1).start()\nprint atlas.edge(1).end()\n```\n\nMany more methods are provided. See the classes in `doc/atlas_entities.html` for more information.\n\n#### Geometry\n`pyatlas` features some really simple geometry primitives for working with locations and shapes on the\nsurface of the Earth. Here is a simple example that uses these primitives:\n\n```python\nfrom pyatlas import geometry\nfrom pyatlas.geometry import Location, PolyLine, Polygon, Rectangle\n\n# Location constructor (lat/lon ordering) uses dm7 by default, see Location docs for info on dm7\nloc1 = Location(385000000, -1160200000)\n# create the same Location but with degree values instead (lat/lon ordering)\nloc2 = geometry.location_with_degrees(38.5, -116.02)\nprint loc1.get_latitude_deg()\nprint loc2.get_latitude()\n\n# create a new PolyLine with two shape points\npolyline1 = PolyLine([Location(385000000, -1160200000), Location(395000000, -116300000)])\nfor loc in polyline1.locations():\n    print loc\nprint polyline1.bounds()\n\n# create a new Polygon with specified vertices\npolygon1 = Polygon([geometry.location_with_degrees(0, 0),\n                    geometry.location_with_degrees(10, 0),\n                    geometry.location_with_degrees(5, 10)])\nprint polygon1\n# print the vertices, will print the first again at the end to simulate closedness\nfor loc in polygon1.closed_loop():\n    print loc\nprint polygon1.bounds()\n# will print True, since the point lies inside the triangle\nprint polygon1.fully_geometrically_encloses_location(geometry.location_with_degrees(5, 5))\n\n# create a new Rectangle with given lower left and upper right corners\nrect = Rectangle(geometry.location_with_degrees(0, 0), geometry.location_with_degrees(20, 20))\nprint rect\n# this Rectangle intersects (overlaps at any point) polygon1\nprint rect.intersects(polygon1)\n```\n\nSee the classes in `doc/geometry.html` for more information.\n\n#### Spatial queries\n`pyatlas` supports some simple spatial queries over its feature space. The queries use the geometry\nprimitives provided by the `geometry` module, but convert to [Shapely](https://github.com/Toblerity/Shapely)\nprimitives under the hood to make queries into a native [libgeos-backed](https://github.com/OSGeo/geos) R-tree.\nBelow are examples for a few of the spatial queries the `Atlas` supports:\n\n```python\nfrom pyatlas import geometry\nfrom pyatlas.geometry import Rectangle\n\n# print all Points intersecting a given Polygon that also have \"key1\" as a tag key\nlower_left = geometry.location_with_degrees(37, -118.02)\nupper_right = geometry.location_with_degrees(39, -118)\nfor point in atlas.points_within(Rectangle(lower_left, upper_right),\n                                 predicate=lambda e: 'key1' in e.get_tags().keys()):\n    print point\n\n# print all Relations with at least one member intersecting a given Polygon\nlower_left = geometry.location_with_degrees(37.999, -118.001)\nupper_right = geometry.location_with_degrees(38.001, -117.999)\nfor relation in atlas.relations_with_entities_intersecting(Rectangle(lower_left, upper_right)):\n    print relation\n\n# print all Edges that intersect a given Polygon\nlower_left = geometry.location_with_degrees(38, -120)\nupper_right = geometry.location_with_degrees(40, -117)\nfor edge in atlas.edges_intersecting(Rectangle(lower_left, upper_right)):\n    print edge\n```\n\nSee `doc/atlas.html` for more information on the available spatial queries.\n\n"
  },
  {
    "path": "pyatlas/clean.sh",
    "content": "#!/usr/bin/env bash\n\n# general case script abort if a command fails\n# this can be overridden with a custom error message using '|| err_shutdown'\nset -e\nset -o pipefail\n\n### define utility functions ###\n################################\nerr_shutdown() {\n    echo \"clean.sh: ERROR: $1\"\n    deactivate\n    exit 1\n}\n\n\n### check to prevent users from running this script directly ###\n################################################################\nif [ \"$1\" != \"ranFromGradle\" ];\nthen\n    err_shutdown \"this script should be run using the atlas gradle task 'cleanPyatlas'\"\nfi\n\n\n### set up variables to store directory names ###\n#################################################\ngradle_project_root_dir=\"$(pwd)\"\npyatlas_dir=\"pyatlas\"\npyatlas_srcdir=\"pyatlas\"\npyatlas_testdir=\"unit_tests\"\npyatlas_docdir=\"doc\"\npyatlas_root_dir=\"$gradle_project_root_dir/$pyatlas_dir\"\nvenv_path=\"$pyatlas_root_dir/__pyatlas_venv__\"\nprotoc_path=\"$pyatlas_root_dir/protoc\"\n\n\n### abort the script if the pyatlas source folder is not present ###\n####################################################################\nif [ ! -d \"$pyatlas_root_dir/$pyatlas_srcdir\" ];\nthen\n    err_shutdown \"pyatlas source folder not found\"\nfi\n\n\n### clean up the build artifacts ###\n####################################\necho \"Cleaning build artifacts if present...\"\nrm -rf \"$pyatlas_root_dir/build\"\nrm -rf \"$pyatlas_root_dir/dist\"\nrm -rf \"$pyatlas_root_dir/pyatlas.egg-info\"\nrm -f \"$pyatlas_root_dir/LICENSE\"\nrm -rf \"$venv_path\"\nrm -f \"$protoc_path\"\n# use 'find' to handle case where filenames contain spaces\nfind \"$pyatlas_root_dir/$pyatlas_srcdir/autogen\" -type f -name \"*_pb2.py\" -delete\nfind \"$pyatlas_root_dir/$pyatlas_srcdir\" -type f -name \"*.pyc\" -delete\nfind \"$pyatlas_root_dir/$pyatlas_srcdir/autogen\" -type f -name \"*.pyc\" -delete\nfind \"$pyatlas_root_dir/$pyatlas_testdir\" -type f -name \"*.pyc\" -delete\nfind \"$pyatlas_root_dir/$pyatlas_docdir\" -type f -name \"*.html\" -delete\n"
  },
  {
    "path": "pyatlas/doc/how_to_get_the_docs.txt",
    "content": "Use the 'buildPyatlas' gradle target to generate the docs!\n\nFrom the root atlas project directory run:\n\n$ ./gradlew buildPyatlas\n\n"
  },
  {
    "path": "pyatlas/format.sh",
    "content": "#!/usr/bin/env bash\n\n# general case script abort if a command fails\n# this can be overridden with a custom error message using '|| err_shutdown'\nset -e\nset -o pipefail\n\n### define utility functions ###\n################################\nerr_shutdown() {\n    echo \"format.sh: ERROR: $1\"\n    deactivate\n    exit 1\n}\n\n\n### check to prevent users from running this script directly ###\n################################################################\nif [ \"$1\" != \"ranFromGradle\" ];\nthen\n    err_shutdown \"this script should be run using the atlas gradle task 'formatPyatlas'\"\nfi\n\n\n### get CHECK or APPLY mode ###\n###############################\nformat_mode=$2\n\n### set up variables to store directory names ###\n#################################################\ngradle_project_root_dir=\"$(pwd)\"\npyatlas_dir=\"pyatlas\"\npyatlas_srcdir=\"pyatlas\"\npyatlas_testdir=\"unit_tests\"\npyatlas_root_dir=\"$gradle_project_root_dir/$pyatlas_dir\"\nvenv_path=\"$pyatlas_root_dir/__pyatlas_venv__\"\npyatlas_format_script=\"yapf_format.py\"\n\n\n### abort the script if the pyatlas source folder is not present ###\n####################################################################\nif [ ! -d \"$pyatlas_root_dir/$pyatlas_srcdir\" ];\nthen\n    err_shutdown \"pyatlas source folder not found\"\nfi\n\n\n### format the module source code ###\n#####################################\n# start the venv\nif [ ! -d \"$venv_path\" ];\nthen\n    err_shutdown \"missing $venv_path\"\nfi\n# shellcheck source=/dev/null\nsource \"$venv_path/bin/activate\"\n\n# enter the pyatlas project directory so the formatting script will work\npushd \"$pyatlas_root_dir\"\npip install yapf==0.22.0\n\nif ! python \"$pyatlas_format_script\" \"$pyatlas_srcdir\" \"$format_mode\";\nthen\n    err_shutdown \"CHECK format step failed: run './gradlew applyFormatPyatlas'\"\nfi\n\nif ! python \"$pyatlas_format_script\" \"$pyatlas_testdir\" \"$format_mode\";\nthen\n    err_shutdown \"CHECK format step failed: run './gradlew applyFormatPyatlas'\"\nfi\n\n# get back to gradle project directory\npopd\n\n# shutdown the venv\ndeactivate\n"
  },
  {
    "path": "pyatlas/package.sh",
    "content": "#!/usr/bin/env bash\n\n# THIS ENTIRE SCRIPT IS A MASSIVE HACK\n# THIS SHOULD REALLY BE DONE WITH GRADLE\n\n# general case script abort if a command fails\n# this can be overridden with a custom error message using '|| err_shutdown'\nset -e\nset -o pipefail\n\n### define utility functions ###\n################################\nerr_shutdown() {\n    echo \"package.sh: ERROR: $1\"\n    deactivate\n    exit 1\n}\n\n\n### check to prevent users from running this script directly ###\n################################################################\nif [ \"$1\" != \"ranFromGradle\" ];\nthen\n    err_shutdown \"this script should be run using the atlas gradle task 'packagePyatlas'\"\nfi\n\n\n### set up variables to store directory names ###\n#################################################\ngradle_project_root_dir=\"$(pwd)\"\npyatlas_dir=\"pyatlas\"\npyatlas_srcdir=\"pyatlas\"\ndoc_dir=\"doc\"\npyatlas_root_dir=\"$gradle_project_root_dir/$pyatlas_dir\"\nvenv_path=\"$pyatlas_root_dir/__pyatlas_venv__\"\nprotofiles_dir=\"$gradle_project_root_dir/src/main/proto\"\n\n\n### abort the script if the pyatlas source folder is not present ###\n####################################################################\nif [ ! -d \"$pyatlas_root_dir/$pyatlas_srcdir\" ];\nthen\n    err_shutdown \"pyatlas source folder not found\"\nfi\n\n\n### determine if wget is installed ###\n######################################\nif command -v wget ;\nthen\n    wget_command=\"$(command -v wget)\"\nelse\n    err_shutdown \"'command -v wget' returned non-zero exit status: install wget to run this script\"\nfi\n\n\n### download protoc and compile the atlas proto files into python ###\n#####################################################################\necho \"Preparing protoc...\"\n# hack to grab the protoc version from dependencies.gradle\n#protoc_version=$(grep 'protoc' \"$gradle_project_root_dir/dependencies.gradle\" | awk -F':' '{print $2; exit}' | tr -d \"'\" | tr -d ',')\n# TODO HACK FIXME hardcoding a proto3 version until we can upgrade atlas, proto2 is not compat with python3\nprotoc_version='3.11.1'\nprotoc_path=\"$pyatlas_root_dir/protoc\"\n# detemine what platform we are on\nif [ ! -f \"$protoc_path\" ];\nthen\n    if [ \"$(uname)\" == \"Darwin\" ];\n    then\n        download_link=\"https://repo1.maven.org/maven2/com/google/protobuf/protoc/${protoc_version}/protoc-${protoc_version}-osx-x86_64.exe\"\n        \"$wget_command\" \"$download_link\" -O \"$protoc_path\" || err_shutdown \"wget of '$download_link' failed\"\n    elif [ \"$(uname)\" == \"Linux\" ];\n    then\n        download_link=\"https://repo1.maven.org/maven2/com/google/protobuf/protoc/${protoc_version}/protoc-${protoc_version}-linux-x86_64.exe\"\n        \"$wget_command\" \"$download_link\" -O \"$protoc_path\" || err_shutdown \"wget of '$download_link' failed\"\n    else\n        err_shutdown \"unrecognized platform $(uname)\"\nfi\nfi\nchmod 700 \"$protoc_path\"\n\n# complicated mess to handle case where a proto filename has a space\n# basically, 'find' outputs each file separated by a NUL terminator\n# read -r -d '' reads raw input delimited by NUL characters\nwhile IFS= read -r -d '' protofile\ndo\n    \"$protoc_path\" \"$protofile\" --proto_path=\"$protofiles_dir\" --python_out=\"$pyatlas_root_dir/$pyatlas_srcdir/autogen\" || err_shutdown \"protoc invocation failed\"\ndone < <(find \"$protofiles_dir\" -type f -name \"*.proto\" -print0)\n\n# TODO HACK FIXME this is to fix badly generated import statements by proto3 library\n# See this solution: https://github.com/protocolbuffers/protobuf/issues/4546#issuecomment-384252863\n# FIXME this will fail if source file has a space\npushd \"$pyatlas_root_dir/$pyatlas_srcdir/autogen\"\nif [ \"$(uname)\" == \"Darwin\" ];\nthen\n    sed -i '' 's/^\\(import.*pb2\\)/from . \\1/g' ./*.py\nelif [ \"$(uname)\" == \"Linux\" ];\nthen\n    sed --in-place=\"\" \"s/^\\(import.*pb2\\)/from . \\1/g\" ./*.py\nelse\n    err_shutdown \"unrecognized platform $(uname)\"\nfi\npopd\n\n\n### build the module and documentation ###\n##########################################\n# start the venv\nif [ ! -d \"$venv_path\" ];\nthen\n    err_shutdown \"missing $venv_path\"\nfi\n# shellcheck source=/dev/null\nsource \"$venv_path/bin/activate\"\n\n# copy the LICENSE to the pyatlas folder\ncp \"$gradle_project_root_dir/LICENSE\" \"$pyatlas_root_dir\"\n\n# grab the build version from gradle.properties and inject it into setup.py\n# remove the -SNAPSHOT text if present\natlas_version=$(grep \"version=\" \"$gradle_project_root_dir/gradle.properties\" | cut -f2 -d \"=\" | sed 's/-SNAPSHOT//g')\n# GNU and BSD sed have different \"in-place\" flag syntax\nif [ \"$(uname)\" == \"Darwin\" ];\nthen\n    sed -i \"\" \"s/version=.*/version=\\\"$atlas_version\\\",/\" \"$pyatlas_root_dir/setup.py\"\nelif [ \"$(uname)\" == \"Linux\" ];\nthen\n    sed --in-place=\"\" \"s/version=.*/version=\\\"$atlas_version\\\",/\" \"$pyatlas_root_dir/setup.py\"\nelse\n    err_shutdown \"unrecognized platform $(uname)\"\nfi\n\n# enter the pyatlas project directory so module metadata is generated correctly\npushd \"$pyatlas_root_dir\"\n\necho \"Building and packaging pyatlas module...\"\npython \"setup.py\" sdist -d \"dist\" bdist_wheel -d \"dist\"\n\n# self-install and create the docs\npip install -e .\n# hack to make pydoc work\nexport PYTHONPATH=\"$PYTHONPATH:$pyatlas_root_dir/$pyatlas_srcdir:$pyatlas_root_dir/$pyatlas_srcdir/autogen\"\n# FIXME this will fail if source file has a space\npydoc -w \"$pyatlas_srcdir\"/*.py\nmv ./*.html \"$doc_dir\"\n# this would be the correct way, but for some reason the 'find exec' fails on atlas.py\n#find \"$pyatlas_srcdir\"/*.py -exec pydoc -w {} \\;\n#find \"$pyatlas_root_dir/\"*.html -exec mv {} \"$doc_dir\" \\;\n\n# get back to gradle project directory\npopd\n\n# reset version field in setup.py\n# GNU and BSD sed have different \"in-place\" flag syntax\nif [ \"$(uname)\" == \"Darwin\" ];\nthen\n    sed -i \"\" \"s/version=.*/version=/\" \"$pyatlas_root_dir/setup.py\"\nelif [ \"$(uname)\" == \"Linux\" ];\nthen\n    sed --in-place=\"\" \"s/version=.*/version=/\" \"$pyatlas_root_dir/setup.py\"\nelse\n    err_shutdown \"unrecognized platform $(uname)\"\nfi\n\n# shutdown the venv\ndeactivate\n"
  },
  {
    "path": "pyatlas/pyatlas/__init__.py",
    "content": "\n"
  },
  {
    "path": "pyatlas/pyatlas/atlas.py",
    "content": "\"\"\"\nThis module defines the Atlas and the various helper types it needs to operate.\n\nAtlas is a representation of an OpenStreetMap region in memory. It is a\nnavigable collection of unidirectional Edges and Nodes. It is designed to\nbe close to the OpenStreetMap model. It also contains a collection of\nnon-navigable geolocated items that can be Points, Lines or Areas. All can\nbe members of Relations.\n\"\"\"\n\nimport zipfile\n\nimport pyatlas.autogen.ProtoAtlasMetaData_pb2\nimport pyatlas.autogen.ProtoLongArray_pb2\nimport pyatlas.autogen.ProtoLongArrayOfArrays_pb2\nimport pyatlas.autogen.ProtoIntegerStringDictionary_pb2\nimport pyatlas.autogen.ProtoPackedTagStore_pb2\nimport pyatlas.autogen.ProtoLongToLongMap_pb2\nimport pyatlas.autogen.ProtoLongToLongMultiMap_pb2\nimport pyatlas.autogen.ProtoPolyLineArray_pb2\nimport pyatlas.autogen.ProtoPolygonArray_pb2\nimport pyatlas.autogen.ProtoByteArrayOfArrays_pb2\nimport pyatlas.autogen.ProtoIntegerArrayOfArrays_pb2\n\nimport pyatlas.atlas_entities\nimport pyatlas.geometry\nimport pyatlas.atlas_metadata\nimport pyatlas.spatial_index\n\n\nclass Atlas(object):\n    \"\"\"\n    The Atlas - current implementation is *not* threadsafe.\n    \"\"\"\n\n    def __init__(self, atlas_file_path, lazy_loading=True):\n        \"\"\"\n        Create a new Atlas backed by the atlas file at the provided path (string).\n        Disabling lazy loading will force the full Atlas into memory. This will\n        incur a significant perfomance penalty at creation time, but will make\n        subsequent queries as fast as possible.\n        \"\"\"\n        self.serializer = _AtlasSerializer(atlas_file_path, self)\n        self.lazy_loading = lazy_loading\n\n        # --- PackedAtlas fields ---\n        # The field names match up with the name of their corresponding ZIP entry.\n        # These ZIP entry names come from from the PackedAtlas Java implementation.\n        self.metaData = None\n        self.dictionary = None\n\n        self.pointIdentifiers = None\n        self.pointIdentifierToPointArrayIndex = None\n        self.pointLocations = None\n        self.pointTags = None\n        self.pointIndexToRelationIndices = None\n\n        self.lineIdentifiers = None\n        self.lineIdentifierToLineArrayIndex = None\n        self.linePolyLines = None\n        self.lineTags = None\n        self.lineIndexToRelationIndices = None\n\n        self.areaIdentifiers = None\n        self.areaIdentifierToAreaArrayIndex = None\n        self.areaPolygons = None\n        self.areaTags = None\n        self.areaIndexToRelationIndices = None\n\n        self.nodeIdentifiers = None\n        self.nodeIdentifierToNodeArrayIndex = None\n        self.nodeLocations = None\n        self.nodeTags = None\n        self.nodeInEdgesIndices = None\n        self.nodeOutEdgesIndices = None\n        self.nodeIndexToRelationIndices = None\n\n        self.edgeIdentifiers = None\n        self.edgeIdentifierToEdgeArrayIndex = None\n        self.edgeStartNodeIndex = None\n        self.edgeEndNodeIndex = None\n        self.edgePolyLines = None\n        self.edgeTags = None\n        self.edgeIndexToRelationIndices = None\n\n        self.relationIdentifiers = None\n        self.relationIdentifierToRelationArrayIndex = None\n        self.relationMemberTypes = None\n        self.relationMemberIndices = None\n        self.relationMemberRoles = None\n        self.relationTags = None\n        self.relationIndexToRelationIndices = None\n\n        # --- spatial indices ---\n        self.point_spatial_index = None\n        self.line_spatial_index = None\n        self.area_spatial_index = None\n        self.node_spatial_index = None\n        self.edge_spatial_index = None\n        self.relation_spatial_index = None\n\n        if not self.lazy_loading:\n            self.load_all_fields()\n\n    def metadata(self):\n        \"\"\"\n        Get the metadata associated with this Atlas. See the AtlasMetaData class\n        for more information on the metadata format.\n        \"\"\"\n        if self.metaData is None:\n            self.serializer._load_field(self.serializer._FIELD_METADATA)\n        return self.metaData\n\n    def points(self, predicate=lambda p: True):\n        \"\"\"\n        Get a generator for Points in this Atlas. Can optionally also accept a\n        predicate to filter the generated Points.\n        \"\"\"\n        for i, element in enumerate(self._get_pointIdentifiers().elements):\n            point = pyatlas.atlas_entities.Point(self, i)\n            if predicate(point):\n                yield point\n\n    def point(self, identifier):\n        \"\"\"\n        Get a Point with a given Atlas identifier. Returns None if there is no\n        Point with the given identifier.\n        \"\"\"\n        identifier_to_index = self._get_pointIdentifierToPointArrayIndex()\n        if identifier in identifier_to_index:\n            return pyatlas.atlas_entities.Point(self, identifier_to_index[identifier])\n        return None\n\n    def lines(self, predicate=lambda l: True):\n        \"\"\"\n        Get a generator for Lines in this Atlas. Can optionally also accept a\n        predicate to filter the generated Lines.\n        \"\"\"\n        for i, element in enumerate(self._get_lineIdentifiers().elements):\n            line = pyatlas.atlas_entities.Line(self, i)\n            if predicate(line):\n                yield line\n\n    def line(self, identifier):\n        \"\"\"\n        Get a Line with a given Atlas identifier. Returns None if there is no\n        Line with the given identifier.\n        \"\"\"\n        identifier_to_index = self._get_lineIdentifierToLineArrayIndex()\n        if identifier in identifier_to_index:\n            return pyatlas.atlas_entities.Line(self, identifier_to_index[identifier])\n        return None\n\n    def areas(self, predicate=lambda a: True):\n        \"\"\"\n        Get a generator for Areas in this Atlas. Can optionally also accept a\n        predicate to filter the generated Areas.\n        \"\"\"\n        for i, element in enumerate(self._get_areaIdentifiers().elements):\n            area = pyatlas.atlas_entities.Area(self, i)\n            if predicate(area):\n                yield area\n\n    def area(self, identifier):\n        \"\"\"\n        Get an Area with a given Atlas identifier. Returns None if there is no\n        Area with the given identifier.\n        \"\"\"\n        identifier_to_index = self._get_areaIdentifierToAreaArrayIndex()\n        if identifier in identifier_to_index:\n            return pyatlas.atlas_entities.Area(self, identifier_to_index[identifier])\n        return None\n\n    def nodes(self, predicate=lambda n: True):\n        \"\"\"\n        Get a generator for Nodes in this Atlas. Can optionally also accept a\n        predicate to filter the generated Nodes.\n        \"\"\"\n        for i, element in enumerate(self._get_nodeIdentifiers().elements):\n            node = pyatlas.atlas_entities.Node(self, i)\n            if predicate(node):\n                yield node\n\n    def node(self, identifier):\n        \"\"\"\n        Get a Node with a given Atlas identifier. Returns None if there is no\n        Node with the given identifier.\n        \"\"\"\n        identifier_to_index = self._get_nodeIdentifierToNodeArrayIndex()\n        if identifier in identifier_to_index:\n            return pyatlas.atlas_entities.Node(self, identifier_to_index[identifier])\n        return None\n\n    def edges(self, predicate=lambda e: True):\n        \"\"\"\n        Get a generator for Edges in this Atlas. Can optionally also accept a\n        predicate to filter the generated Edges.\n        \"\"\"\n        for i, element in enumerate(self._get_edgeIdentifiers().elements):\n            edge = pyatlas.atlas_entities.Edge(self, i)\n            if predicate(edge):\n                yield edge\n\n    def edge(self, identifier):\n        \"\"\"\n        Get an Edge with a given Atlas identifier. Returns None if there is no\n        Edge with the given identifier.\n        \"\"\"\n        identifier_to_index = self._get_edgeIdentifierToEdgeArrayIndex()\n        if identifier in identifier_to_index:\n            return pyatlas.atlas_entities.Edge(self, identifier_to_index[identifier])\n        return None\n\n    def relations(self, predicate=lambda r: True):\n        \"\"\"\n        Get a generator for Relations in this Atlas. Can optionally also accept a\n        predicate to filter the generated Relations.\n        \"\"\"\n        for i, element in enumerate(self._get_relationIdentifiers().elements):\n            relation = pyatlas.atlas_entities.Relation(self, i)\n            if predicate(relation):\n                yield relation\n\n    def relation(self, identifier):\n        \"\"\"\n        Get a Relation with a given Atlas identifier. Returns None if there is no\n        Relation with the given identifier.\n        \"\"\"\n        identifier_to_index = self._get_relationIdentifierToRelationArrayIndex()\n        if identifier in identifier_to_index:\n            return pyatlas.atlas_entities.Relation(self, identifier_to_index[identifier])\n        return None\n\n    def entities(self, predicate=lambda e: True):\n        \"\"\"\n        Get a generator for all AtlasEntities in this Atlas. Can optionally also\n        accept a predicate to filter the generated entities.\n        \"\"\"\n        for point in self.points():\n            if predicate(point):\n                yield point\n        for line in self.lines():\n            if predicate(line):\n                yield line\n        for area in self.areas():\n            if predicate(area):\n                yield area\n        for node in self.nodes():\n            if predicate(node):\n                yield node\n        for edge in self.edges():\n            if predicate(edge):\n                yield edge\n        for relation in self.relations():\n            if predicate(relation):\n                yield relation\n\n    def entity(self, identifier, entity_type):\n        \"\"\"\n        Get an AtlasEntity with a given Atlas identifier and EntityType.\n        Returns None if there is no entity with the given identifier and type.\n        \"\"\"\n        if entity_type == pyatlas.atlas_entities.EntityType.POINT:\n            return self.point(identifier)\n        elif entity_type == pyatlas.atlas_entities.EntityType.LINE:\n            return self.line(identifier)\n        elif entity_type == pyatlas.atlas_entities.EntityType.AREA:\n            return self.area(identifier)\n        elif entity_type == pyatlas.atlas_entities.EntityType.NODE:\n            return self.node(identifier)\n        elif entity_type == pyatlas.atlas_entities.EntityType.EDGE:\n            return self.edge(identifier)\n        elif entity_type == pyatlas.atlas_entities.EntityType.RELATION:\n            return self.relation(identifier)\n        else:\n            raise ValueError('invalid EntityType value ' + str(entity_type))\n\n    def points_at(self, location, predicate=lambda p: True):\n        \"\"\"\n        Get a frozenset of all Points at some Location. Can optionally accept\n        a predicate to further filter the Points.\n        \"\"\"\n        points_list = self._get_point_spatial_index().get(location.bounds(), predicate=predicate)\n        return points_list\n\n    def points_within(self, polygon, predicate=lambda p: True):\n        \"\"\"\n        Get a frozenset of all Points within some polygon. Can optionally accept\n        a predicate to further filter the Points.\n        \"\"\"\n        points = self._get_point_spatial_index().get(polygon.bounds(), predicate=predicate)\n        points_set = set()\n        for point in points:\n            if polygon.fully_geometrically_encloses_location(point.as_location()):\n                points_set.add(point)\n\n        return frozenset(points_set)\n\n    def lines_containing(self, location, predicate=lambda l: True):\n        \"\"\"\n        Get a frozenset of all Lines containing some Location. Can optionally accept\n        a predicate to further filter the Lines.\n        \"\"\"\n        lines = self._get_line_spatial_index().get(location.bounds(), predicate=predicate)\n        lines_set = set()\n        for line in lines:\n            polyline = line.as_polyline()\n            if location.bounds().overlaps_polyline(polyline):\n                lines_set.add(line)\n\n        return frozenset(lines_set)\n\n    def lines_intersecting_polyline(self, polyline, predicate=lambda l: True):\n        \"\"\"\n        Get a frozenset of all Lines within or intersecting some PolyLine. Can\n        optionally accept a predicate to further filter the Lines.\n        \"\"\"\n        lines = self._get_line_spatial_index().get(polyline.bounds(), predicate=predicate)\n        lines_set = set()\n        for line in lines:\n            polyline = line.as_polyline()\n            if polyline.intersects_polyline(polyline):\n                lines_set.add(line)\n\n        return frozenset(lines_set)\n\n    def lines_intersecting(self, polygon, predicate=lambda l: True):\n        \"\"\"\n        Get a frozenset of all Lines within or intersecting some Polygon. Can\n        optionally accept a predicate to further filter the Lines.\n        \"\"\"\n        lines = self._get_line_spatial_index().get(polygon.bounds(), predicate=predicate)\n        lines_set = set()\n        for line in lines:\n            polyline = line.as_polyline()\n            if polygon.overlaps_polyline(polyline):\n                lines_set.add(line)\n\n        return frozenset(lines_set)\n\n    def areas_covering(self, location, predicate=lambda a: True):\n        \"\"\"\n        Get a frozenset of all Areas covering some Location. Can optionally accept\n        a predicate to further filter the Areas.\n        \"\"\"\n        areas = self._get_area_spatial_index().get(location.bounds(), predicate=predicate)\n        areas_set = set()\n        for area in areas:\n            if area.as_polygon().fully_geometrically_encloses_location(location):\n                areas_set.add(area)\n\n        return frozenset(areas_set)\n\n    def areas_intersecting(self, polygon, predicate=lambda a: True):\n        \"\"\"\n        Get a frozenset of all Areas within or intersecting some Polygon. Can\n        optionally accept a predicate to further filter the Areas.\n        \"\"\"\n        areas = self._get_area_spatial_index().get(polygon.bounds(), predicate=predicate)\n        areas_set = set()\n        for area in areas:\n            if polygon.intersects(area.as_polygon()):\n                areas_set.add(area)\n\n        return frozenset(areas_set)\n\n    def nodes_at(self, location, predicate=lambda n: True):\n        \"\"\"\n        Get a frozenset of all Nodes at some Location. Can optionally accept a\n        predicate to further filter the Nodes.\n        \"\"\"\n        nodes_list = self._get_node_spatial_index().get(location.bounds(), predicate=predicate)\n        return nodes_list\n\n    def nodes_within(self, polygon, predicate=lambda n: True):\n        \"\"\"\n        Get a frozenset of all Nodes within some Polygon. Can optionally accept\n        a predicate to further filter the Nodes.\n        \"\"\"\n        nodes = self._get_node_spatial_index().get(polygon.bounds(), predicate=predicate)\n        nodes_set = set()\n        for node in nodes:\n            if polygon.fully_geometrically_encloses_location(node.as_location()):\n                nodes_set.add(node)\n\n        return frozenset(nodes_set)\n\n    def edges_containing(self, location, predicate=lambda e: True):\n        \"\"\"\n        Get a frozenset of all Edges containing some Location. Can optionally accept\n        a predicate to further filter the Edges.\n        \"\"\"\n        edges = self._get_edge_spatial_index().get(location.bounds(), predicate=predicate)\n        edges_set = set()\n        for edge in edges:\n            polyline = edge.as_polyline()\n            if location.bounds().overlaps_polyline(polyline):\n                edges_set.add(edge)\n\n        return frozenset(edges_set)\n\n    def edges_intersecting_polyline(self, polyline, predicate=lambda e: True):\n        \"\"\"\n        Get a frozenset of all Edges within or intersecting some PolyLine. Can\n        optionally accept a predicate to further filter the Edges.\n        \"\"\"\n        edges = self._get_edge_spatial_index().get(polyline.bounds(), predicate=predicate)\n        edges_set = set()\n        for edge in edges:\n            polyline = edge.as_polyline()\n            if polyline.intersects_polyline(polyline):\n                edges_set.add(edge)\n\n        return frozenset(edges_set)\n\n    def edges_intersecting(self, polygon, predicate=lambda e: True):\n        \"\"\"\n        Get a frozenset of all Edges within or intersecting some Polygon. Can\n        optionally accept a predicate to further filter the Edges.\n        \"\"\"\n        edges = self._get_edge_spatial_index().get(polygon.bounds(), predicate=predicate)\n        edges_set = set()\n        for edge in edges:\n            polyline = edge.as_polyline()\n            if polygon.overlaps_polyline(polyline):\n                edges_set.add(edge)\n\n        return frozenset(edges_set)\n\n    def relations_with_entities_intersecting(self, polygon, predicate=lambda r: True):\n        \"\"\"\n        Return a frozenset of Relations which have at least one feature intersecting some\n        Polygon. Can optionally accept a predicate to further filter the Relations.\n        \"\"\"\n        relations = self._get_relation_spatial_index().get(polygon.bounds(), predicate=predicate)\n        relations_set = set()\n        for relation in relations:\n            if relation.intersects(polygon):\n                relations_set.add(relation)\n\n        return frozenset(relations_set)\n\n    def load_all_fields(self):\n        \"\"\"\n        Force this Atlas to load all its fields from its backing store.\n        \"\"\"\n        self.serializer._load_all_fields()\n        self._get_point_spatial_index()\n        self._get_line_spatial_index()\n        self._get_area_spatial_index()\n        self._get_node_spatial_index()\n        self._get_edge_spatial_index()\n        self._get_relation_spatial_index()\n\n    def number_of_points(self):\n        \"\"\"\n        Get the number of Points in this Atlas\n        \"\"\"\n        return len(self._get_pointIdentifiers().elements)\n\n    def number_of_lines(self):\n        \"\"\"\n        Get the number of Lines in this Atlas\n        \"\"\"\n        return len(self._get_lineIdentifiers().elements)\n\n    def number_of_areas(self):\n        \"\"\"\n        Get the number of Areas in this Atlas\n        \"\"\"\n        return len(self._get_areaIdentifiers().elements)\n\n    def number_of_nodes(self):\n        \"\"\"\n        Get the number of Nodes in this Atlas\n        \"\"\"\n        return len(self._get_nodeIdentifiers().elements)\n\n    def number_of_edges(self):\n        \"\"\"\n        Get the number of Edges in this Atlas\n        \"\"\"\n        return len(self._get_edgeIdentifiers().elements)\n\n    def number_of_relations(self):\n        \"\"\"\n        Get the number of Relations in this Atlas\n        \"\"\"\n        return len(self._get_relationIdentifiers().elements)\n\n    # --- PackedAtlas field loading functions ---\n    def _get_dictionary(self):\n        if self.dictionary is None:\n            self.serializer._load_field(self.serializer._FIELD_DICTIONARY)\n        return self.dictionary\n\n    def _get_pointIdentifiers(self):\n        if self.pointIdentifiers is None:\n            self.serializer._load_field(self.serializer._FIELD_POINT_IDENTIFIERS)\n        return self.pointIdentifiers\n\n    def _get_pointIdentifierToPointArrayIndex(self):\n        if self.pointIdentifierToPointArrayIndex is None:\n            self.serializer._load_field(\n                self.serializer._FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX)\n        return self.pointIdentifierToPointArrayIndex\n\n    def _get_pointLocations(self):\n        if self.pointLocations is None:\n            self.serializer._load_field(self.serializer._FIELD_POINT_LOCATIONS)\n        return self.pointLocations\n\n    def _get_pointTags(self):\n        if self.pointTags is None:\n            self.serializer._load_field(self.serializer._FIELD_POINT_TAGS)\n        self.pointTags.set_dictionary(self._get_dictionary())\n        return self.pointTags\n\n    def _get_pointIndexToRelationIndices(self):\n        if self.pointIndexToRelationIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_POINT_INDEX_TO_RELATION_INDICES)\n        return self.pointIndexToRelationIndices\n\n    def _get_lineIdentifiers(self):\n        if self.lineIdentifiers is None:\n            self.serializer._load_field(self.serializer._FIELD_LINE_IDENTIFIERS)\n        return self.lineIdentifiers\n\n    def _get_lineIdentifierToLineArrayIndex(self):\n        if self.lineIdentifierToLineArrayIndex is None:\n            self.serializer._load_field(self.serializer._FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX)\n        return self.lineIdentifierToLineArrayIndex\n\n    def _get_linePolyLines(self):\n        if self.linePolyLines is None:\n            self.serializer._load_field(self.serializer._FIELD_LINE_POLYLINES)\n        return self.linePolyLines\n\n    def _get_lineTags(self):\n        if self.lineTags is None:\n            self.serializer._load_field(self.serializer._FIELD_LINE_TAGS)\n        self.lineTags.set_dictionary(self._get_dictionary())\n        return self.lineTags\n\n    def _get_lineIndexToRelationIndices(self):\n        if self.lineIndexToRelationIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_LINE_INDEX_TO_RELATION_INDICES)\n        return self.lineIndexToRelationIndices\n\n    def _get_areaIdentifiers(self):\n        if self.areaIdentifiers is None:\n            self.serializer._load_field(self.serializer._FIELD_AREA_IDENTIFIERS)\n        return self.areaIdentifiers\n\n    def _get_areaIdentifierToAreaArrayIndex(self):\n        if self.areaIdentifierToAreaArrayIndex is None:\n            self.serializer._load_field(self.serializer._FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX)\n        return self.areaIdentifierToAreaArrayIndex\n\n    def _get_areaPolygons(self):\n        if self.areaPolygons is None:\n            self.serializer._load_field(self.serializer._FIELD_AREA_POLYGONS)\n        return self.areaPolygons\n\n    def _get_areaTags(self):\n        if self.areaTags is None:\n            self.serializer._load_field(self.serializer._FIELD_AREA_TAGS)\n        self.areaTags.set_dictionary(self._get_dictionary())\n        return self.areaTags\n\n    def _get_areaIndexToRelationIndices(self):\n        if self.areaIndexToRelationIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_AREA_INDEX_TO_RELATION_INDICES)\n        return self.areaIndexToRelationIndices\n\n    def _get_nodeIdentifiers(self):\n        if self.nodeIdentifiers is None:\n            self.serializer._load_field(self.serializer._FIELD_NODE_IDENTIFIERS)\n        return self.nodeIdentifiers\n\n    def _get_nodeIdentifierToNodeArrayIndex(self):\n        if self.nodeIdentifierToNodeArrayIndex is None:\n            self.serializer._load_field(self.serializer._FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX)\n        return self.nodeIdentifierToNodeArrayIndex\n\n    def _get_nodeLocations(self):\n        if self.nodeLocations is None:\n            self.serializer._load_field(self.serializer._FIELD_NODE_LOCATIONS)\n        return self.nodeLocations\n\n    def _get_nodeTags(self):\n        if self.nodeTags is None:\n            self.serializer._load_field(self.serializer._FIELD_NODE_TAGS)\n        self.nodeTags.set_dictionary(self._get_dictionary())\n        return self.nodeTags\n\n    def _get_nodeInEdgesIndices(self):\n        if self.nodeInEdgesIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_NODE_IN_EDGES_INDICES)\n        return self.nodeInEdgesIndices\n\n    def _get_nodeOutEdgesIndices(self):\n        if self.nodeOutEdgesIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_NODE_OUT_EDGES_INDICES)\n        return self.nodeOutEdgesIndices\n\n    def _get_nodeIndexToRelationIndices(self):\n        if self.nodeIndexToRelationIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_NODE_INDEX_TO_RELATION_INDICES)\n        return self.nodeIndexToRelationIndices\n\n    def _get_edgeIdentifiers(self):\n        if self.edgeIdentifiers is None:\n            self.serializer._load_field(self.serializer._FIELD_EDGE_IDENTIFIERS)\n        return self.edgeIdentifiers\n\n    def _get_edgeIdentifierToEdgeArrayIndex(self):\n        if self.edgeIdentifierToEdgeArrayIndex is None:\n            self.serializer._load_field(self.serializer._FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX)\n        return self.edgeIdentifierToEdgeArrayIndex\n\n    def _get_edgeStartNodeIndex(self):\n        if self.edgeStartNodeIndex is None:\n            self.serializer._load_field(self.serializer._FIELD_EDGE_START_NODE_INDEX)\n        return self.edgeStartNodeIndex\n\n    def _get_edgeEndNodeIndex(self):\n        if self.edgeEndNodeIndex is None:\n            self.serializer._load_field(self.serializer._FIELD_EDGE_END_NODE_INDEX)\n        return self.edgeEndNodeIndex\n\n    def _get_edgePolyLines(self):\n        if self.edgePolyLines is None:\n            self.serializer._load_field(self.serializer._FIELD_EDGE_POLYLINES)\n        return self.edgePolyLines\n\n    def _get_edgeTags(self):\n        if self.edgeTags is None:\n            self.serializer._load_field(self.serializer._FIELD_EDGE_TAGS)\n        self.edgeTags.set_dictionary(self._get_dictionary())\n        return self.edgeTags\n\n    def _get_edgeIndexToRelationIndices(self):\n        if self.edgeIndexToRelationIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_EDGE_INDEX_TO_RELATION_INDICES)\n        return self.edgeIndexToRelationIndices\n\n    def _get_relationIdentifiers(self):\n        if self.relationIdentifiers is None:\n            self.serializer._load_field(self.serializer._FIELD_RELATION_IDENTIFIERS)\n        return self.relationIdentifiers\n\n    def _get_relationIdentifierToRelationArrayIndex(self):\n        if self.relationIdentifierToRelationArrayIndex is None:\n            self.serializer._load_field(\n                self.serializer._FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX)\n        return self.relationIdentifierToRelationArrayIndex\n\n    def _get_relationMemberTypes(self):\n        if self.relationMemberTypes is None:\n            self.serializer._load_field(self.serializer._FIELD_RELATION_MEMBER_TYPES)\n        return self.relationMemberTypes\n\n    def _get_relationMemberIndices(self):\n        if self.relationMemberIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_RELATION_MEMBER_INDICES)\n        return self.relationMemberIndices\n\n    def _get_relationMemberRoles(self):\n        if self.relationMemberRoles is None:\n            self.serializer._load_field(self.serializer._FIELD_RELATION_MEMBER_ROLES)\n        return self.relationMemberRoles\n\n    def _get_relationTags(self):\n        if self.relationTags is None:\n            self.serializer._load_field(self.serializer._FIELD_RELATION_TAGS)\n        self.relationTags.set_dictionary(self._get_dictionary())\n        return self.relationTags\n\n    def _get_relationIndexToRelationIndices(self):\n        if self.relationIndexToRelationIndices is None:\n            self.serializer._load_field(self.serializer._FIELD_RELATION_INDEX_TO_RELATION_INDICES)\n        return self.relationIndexToRelationIndices\n\n    # --- spatial index loading functions ---\n    def _get_point_spatial_index(self):\n        if self.point_spatial_index is None:\n            self.point_spatial_index = pyatlas.spatial_index.SpatialIndex(\n                self, pyatlas.atlas_entities.EntityType.POINT, self.points())\n            self.point_spatial_index.initialize_rtree()\n        return self.point_spatial_index\n\n    def _get_line_spatial_index(self):\n        if self.line_spatial_index is None:\n            self.line_spatial_index = pyatlas.spatial_index.SpatialIndex(\n                self, pyatlas.atlas_entities.EntityType.LINE, self.lines())\n            self.line_spatial_index.initialize_rtree()\n        return self.line_spatial_index\n\n    def _get_area_spatial_index(self):\n        if self.area_spatial_index is None:\n            self.area_spatial_index = pyatlas.spatial_index.SpatialIndex(\n                self, pyatlas.atlas_entities.EntityType.AREA, self.areas())\n            self.area_spatial_index.initialize_rtree()\n        return self.area_spatial_index\n\n    def _get_node_spatial_index(self):\n        if self.node_spatial_index is None:\n            self.node_spatial_index = pyatlas.spatial_index.SpatialIndex(\n                self, pyatlas.atlas_entities.EntityType.NODE, self.nodes())\n            self.node_spatial_index.initialize_rtree()\n        return self.node_spatial_index\n\n    def _get_edge_spatial_index(self):\n        if self.edge_spatial_index is None:\n            self.edge_spatial_index = pyatlas.spatial_index.SpatialIndex(\n                self, pyatlas.atlas_entities.EntityType.EDGE, self.edges())\n            self.edge_spatial_index.initialize_rtree()\n        return self.edge_spatial_index\n\n    def _get_relation_spatial_index(self):\n        if self.relation_spatial_index is None:\n            self.relation_spatial_index = pyatlas.spatial_index.SpatialIndex(\n                self, pyatlas.atlas_entities.EntityType.RELATION, self.relations())\n            self.relation_spatial_index.initialize_rtree()\n        return self.relation_spatial_index\n\n\nclass _AtlasSerializer(object):\n    \"\"\"\n    The Atlas serializer. Used by Atlas to read ZIP entries from the\n    backing store. This class should not be used directly.\n    \"\"\"\n\n    _FIELD_METADATA = 'metaData'\n    _FIELD_DICTIONARY = 'dictionary'\n\n    _FIELD_POINT_IDENTIFIERS = 'pointIdentifiers'\n    _FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX = 'pointIdentifierToPointArrayIndex'\n    _FIELD_POINT_LOCATIONS = 'pointLocations'\n    _FIELD_POINT_TAGS = 'pointTags'\n    _FIELD_POINT_INDEX_TO_RELATION_INDICES = 'pointIndexToRelationIndices'\n\n    _FIELD_LINE_IDENTIFIERS = 'lineIdentifiers'\n    _FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX = 'lineIdentifierToLineArrayIndex'\n    _FIELD_LINE_POLYLINES = 'linePolyLines'\n    _FIELD_LINE_TAGS = 'lineTags'\n    _FIELD_LINE_INDEX_TO_RELATION_INDICES = 'lineIndexToRelationIndices'\n\n    _FIELD_AREA_IDENTIFIERS = 'areaIdentifiers'\n    _FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX = 'areaIdentifierToAreaArrayIndex'\n    _FIELD_AREA_POLYGONS = 'areaPolygons'\n    _FIELD_AREA_TAGS = 'areaTags'\n    _FIELD_AREA_INDEX_TO_RELATION_INDICES = 'areaIndexToRelationIndices'\n\n    _FIELD_NODE_IDENTIFIERS = 'nodeIdentifiers'\n    _FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX = 'nodeIdentifierToNodeArrayIndex'\n    _FIELD_NODE_LOCATIONS = 'nodeLocations'\n    _FIELD_NODE_TAGS = 'nodeTags'\n    _FIELD_NODE_IN_EDGES_INDICES = 'nodeInEdgesIndices'\n    _FIELD_NODE_OUT_EDGES_INDICES = 'nodeOutEdgesIndices'\n    _FIELD_NODE_INDEX_TO_RELATION_INDICES = 'nodeIndexToRelationIndices'\n\n    _FIELD_EDGE_IDENTIFIERS = 'edgeIdentifiers'\n    _FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX = 'edgeIdentifierToEdgeArrayIndex'\n    _FIELD_EDGE_START_NODE_INDEX = 'edgeStartNodeIndex'\n    _FIELD_EDGE_END_NODE_INDEX = 'edgeEndNodeIndex'\n    _FIELD_EDGE_POLYLINES = 'edgePolyLines'\n    _FIELD_EDGE_TAGS = 'edgeTags'\n    _FIELD_EDGE_INDEX_TO_RELATION_INDICES = 'edgeIndexToRelationIndices'\n\n    _FIELD_RELATION_IDENTIFIERS = 'relationIdentifiers'\n    _FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX = 'relationIdentifierToRelationArrayIndex'\n    _FIELD_RELATION_MEMBER_TYPES = 'relationMemberTypes'\n    _FIELD_RELATION_MEMBER_INDICES = 'relationMemberIndices'\n    _FIELD_RELATION_MEMBER_ROLES = 'relationMemberRoles'\n    _FIELD_RELATION_TAGS = 'relationTags'\n    _FIELD_RELATION_INDEX_TO_RELATION_INDICES = 'relationIndexToRelationIndices'\n\n    # yapf: disable\n    _FIELD_NAMES_TO_LOAD_METHODS = {\n        _FIELD_METADATA:\n        '_load_metadata',\n        _FIELD_DICTIONARY:\n        '_load_dictionary',\n\n        _FIELD_POINT_IDENTIFIERS:\n        '_load_pointIdentifiers',\n        _FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX:\n        '_load_pointIdentifierToPointArrayIndex',\n        _FIELD_POINT_LOCATIONS:\n        '_load_pointLocations',\n        _FIELD_POINT_TAGS:\n        '_load_pointTags',\n        _FIELD_POINT_INDEX_TO_RELATION_INDICES:\n        '_load_pointIndexToRelationIndices',\n\n        _FIELD_LINE_IDENTIFIERS:\n        '_load_lineIdentifiers',\n        _FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX:\n        '_load_lineIdentifierToLineArrayIndex',\n        _FIELD_LINE_POLYLINES:\n        '_load_linePolylines',\n        _FIELD_LINE_TAGS:\n        '_load_lineTags',\n        _FIELD_LINE_INDEX_TO_RELATION_INDICES:\n        '_load_lineIndexToRelationIndices',\n\n        _FIELD_AREA_IDENTIFIERS:\n        '_load_areaIdentifiers',\n        _FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX:\n        '_load_areaIdentifierToAreaArrayIndex',\n        _FIELD_AREA_POLYGONS:\n        '_load_areaPolygons',\n        _FIELD_AREA_TAGS:\n        '_load_areaTags',\n        _FIELD_AREA_INDEX_TO_RELATION_INDICES:\n        '_load_areaIndexToRelationIndices',\n\n        _FIELD_NODE_IDENTIFIERS:\n        '_load_nodeIdentifiers',\n        _FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX:\n        '_load_nodeIdentifierToNodeArrayIndex',\n        _FIELD_NODE_LOCATIONS:\n        '_load_nodeLocations',\n        _FIELD_NODE_TAGS:\n        '_load_nodeTags',\n        _FIELD_NODE_IN_EDGES_INDICES:\n        '_load_nodeInEdgesIndices',\n        _FIELD_NODE_OUT_EDGES_INDICES:\n        '_load_nodeOutEdgesIndices',\n        _FIELD_NODE_INDEX_TO_RELATION_INDICES:\n        '_load_nodeIndexToRelationIndices',\n\n        _FIELD_EDGE_IDENTIFIERS:\n        '_load_edgeIdentifiers',\n        _FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX:\n        '_load_edgeIdentifierToEdgeArrayIndex',\n        _FIELD_EDGE_START_NODE_INDEX:\n        '_load_edgeStartNodeIndex',\n        _FIELD_EDGE_END_NODE_INDEX:\n        '_load_edgeEndNodeIndex',\n        _FIELD_EDGE_POLYLINES:\n        '_load_edgePolylines',\n        _FIELD_EDGE_TAGS:\n        '_load_edgeTags',\n        _FIELD_EDGE_INDEX_TO_RELATION_INDICES:\n        '_load_edgeIndexToRelationIndices',\n\n        _FIELD_RELATION_IDENTIFIERS:\n        '_load_relationIdentifiers',\n        _FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX:\n        '_load_relationIdentifierToRelationArrayIndex',\n        _FIELD_RELATION_MEMBER_TYPES:\n        '_load_relationMemberTypes',\n        _FIELD_RELATION_MEMBER_INDICES:\n        '_load_relationMemberIndices',\n        _FIELD_RELATION_MEMBER_ROLES:\n        '_load_relationMemberRoles',\n        _FIELD_RELATION_TAGS:\n        '_load_relationTags',\n        _FIELD_RELATION_INDEX_TO_RELATION_INDICES:\n        '_load_relationIndexToRelationIndices'\n    }\n    # yapf: enable\n\n    def __init__(self, atlas_file, atlas):\n        self.atlas_file = atlas_file\n        self.atlas = atlas\n\n    def _load_all_fields(self):\n        for field in self._FIELD_NAMES_TO_LOAD_METHODS:\n            self._load_field(field)\n\n    def _load_metadata(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_METADATA)\n        proto_metadata = pyatlas.autogen.ProtoAtlasMetaData_pb2.ProtoAtlasMetaData()\n        proto_metadata.ParseFromString(zip_entry_data)\n        self.atlas.metaData = pyatlas.atlas_metadata._get_atlas_metadata_from_proto(proto_metadata)\n\n    def _load_dictionary(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_DICTIONARY)\n        proto_dictionary = pyatlas.autogen.ProtoIntegerStringDictionary_pb2.ProtoIntegerStringDictionary(\n        )\n        proto_dictionary.ParseFromString(zip_entry_data)\n        self.atlas.dictionary = _get_integer_dictionary_from_proto(proto_dictionary)\n\n    def _load_pointIdentifiers(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_POINT_IDENTIFIERS)\n        self.atlas.pointIdentifiers = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.pointIdentifiers.ParseFromString(zip_entry_data)\n\n    def _load_pointIdentifierToPointArrayIndex(self):\n        zip_entry_data = _read_zipentry(self.atlas_file,\n                                        self._FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX)\n        proto_map = pyatlas.autogen.ProtoLongToLongMap_pb2.ProtoLongToLongMap()\n        proto_map.ParseFromString(zip_entry_data)\n        self.atlas.pointIdentifierToPointArrayIndex = _get_dict_from_longtolongmap(proto_map)\n\n    def _load_pointLocations(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_POINT_LOCATIONS)\n        self.atlas.pointLocations = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.pointLocations.ParseFromString(zip_entry_data)\n\n    def _load_pointTags(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_POINT_TAGS)\n        proto_point_tags = pyatlas.autogen.ProtoPackedTagStore_pb2.ProtoPackedTagStore()\n        proto_point_tags.ParseFromString(zip_entry_data)\n        self.atlas.pointTags = _get_packed_tag_store_from_proto(proto_point_tags)\n\n    def _load_pointIndexToRelationIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file,\n                                        self._FIELD_POINT_INDEX_TO_RELATION_INDICES)\n        proto_multimap = pyatlas.autogen.ProtoLongToLongMultiMap_pb2.ProtoLongToLongMultiMap()\n        proto_multimap.ParseFromString(zip_entry_data)\n        self.atlas.pointIndexToRelationIndices = _get_dict_from_protolongtolongmultimap(\n            proto_multimap)\n\n    def _load_lineIdentifiers(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_LINE_IDENTIFIERS)\n        self.atlas.lineIdentifiers = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.lineIdentifiers.ParseFromString(zip_entry_data)\n\n    def _load_lineIdentifierToLineArrayIndex(self):\n        zip_entry_data = _read_zipentry(self.atlas_file,\n                                        self._FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX)\n        proto_map = pyatlas.autogen.ProtoLongToLongMap_pb2.ProtoLongToLongMap()\n        proto_map.ParseFromString(zip_entry_data)\n        self.atlas.lineIdentifierToLineArrayIndex = _get_dict_from_longtolongmap(proto_map)\n\n    def _load_linePolylines(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_LINE_POLYLINES)\n        proto_array = pyatlas.autogen.ProtoPolyLineArray_pb2.ProtoPolyLineArray()\n        proto_array.ParseFromString(zip_entry_data)\n        result = []\n        for encoding in proto_array.encodings:\n            poly_line = pyatlas.geometry.decompress_polyline(encoding)\n            result.append(poly_line)\n        self.atlas.linePolyLines = result\n\n    def _load_lineTags(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_LINE_TAGS)\n        proto_line_tags = pyatlas.autogen.ProtoPackedTagStore_pb2.ProtoPackedTagStore()\n        proto_line_tags.ParseFromString(zip_entry_data)\n        self.atlas.lineTags = _get_packed_tag_store_from_proto(proto_line_tags)\n\n    def _load_lineIndexToRelationIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_LINE_INDEX_TO_RELATION_INDICES)\n        proto_multimap = pyatlas.autogen.ProtoLongToLongMultiMap_pb2.ProtoLongToLongMultiMap()\n        proto_multimap.ParseFromString(zip_entry_data)\n        self.atlas.lineIndexToRelationIndices = _get_dict_from_protolongtolongmultimap(\n            proto_multimap)\n\n    def _load_areaIdentifiers(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_AREA_IDENTIFIERS)\n        self.atlas.areaIdentifiers = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.areaIdentifiers.ParseFromString(zip_entry_data)\n\n    def _load_areaIdentifierToAreaArrayIndex(self):\n        zip_entry_data = _read_zipentry(self.atlas_file,\n                                        self._FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX)\n        proto_map = pyatlas.autogen.ProtoLongToLongMap_pb2.ProtoLongToLongMap()\n        proto_map.ParseFromString(zip_entry_data)\n        self.atlas.areaIdentifierToAreaArrayIndex = _get_dict_from_longtolongmap(proto_map)\n\n    def _load_areaPolygons(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_AREA_POLYGONS)\n        proto_array = pyatlas.autogen.ProtoPolygonArray_pb2.ProtoPolygonArray()\n        proto_array.ParseFromString(zip_entry_data)\n        result = []\n        for encoding in proto_array.encodings:\n            polygon0 = pyatlas.geometry.decompress_polygon(encoding)\n            result.append(polygon0)\n        self.atlas.areaPolygons = result\n\n    def _load_areaTags(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_AREA_TAGS)\n        proto_area_tags = pyatlas.autogen.ProtoPackedTagStore_pb2.ProtoPackedTagStore()\n        proto_area_tags.ParseFromString(zip_entry_data)\n        self.atlas.areaTags = _get_packed_tag_store_from_proto(proto_area_tags)\n\n    def _load_areaIndexToRelationIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_AREA_INDEX_TO_RELATION_INDICES)\n        proto_multimap = pyatlas.autogen.ProtoLongToLongMultiMap_pb2.ProtoLongToLongMultiMap()\n        proto_multimap.ParseFromString(zip_entry_data)\n        self.atlas.areaIndexToRelationIndices = _get_dict_from_protolongtolongmultimap(\n            proto_multimap)\n\n    def _load_nodeIdentifiers(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_NODE_IDENTIFIERS)\n        self.atlas.nodeIdentifiers = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.nodeIdentifiers.ParseFromString(zip_entry_data)\n\n    def _load_nodeIdentifierToNodeArrayIndex(self):\n        zip_entry_data = _read_zipentry(self.atlas_file,\n                                        self._FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX)\n        proto_map = pyatlas.autogen.ProtoLongToLongMap_pb2.ProtoLongToLongMap()\n        proto_map.ParseFromString(zip_entry_data)\n        self.atlas.nodeIdentifierToNodeArrayIndex = _get_dict_from_longtolongmap(proto_map)\n\n    def _load_nodeLocations(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_NODE_LOCATIONS)\n        self.atlas.nodeLocations = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.nodeLocations.ParseFromString(zip_entry_data)\n\n    def _load_nodeTags(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_NODE_TAGS)\n        proto_node_tags = pyatlas.autogen.ProtoPackedTagStore_pb2.ProtoPackedTagStore()\n        proto_node_tags.ParseFromString(zip_entry_data)\n        self.atlas.nodeTags = _get_packed_tag_store_from_proto(proto_node_tags)\n\n    def _load_nodeInEdgesIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_NODE_IN_EDGES_INDICES)\n        self.atlas.nodeInEdgesIndices = pyatlas.autogen.ProtoLongArrayOfArrays_pb2.ProtoLongArrayOfArrays(\n        )\n        self.atlas.nodeInEdgesIndices.ParseFromString(zip_entry_data)\n\n    def _load_nodeOutEdgesIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_NODE_OUT_EDGES_INDICES)\n        self.atlas.nodeOutEdgesIndices = pyatlas.autogen.ProtoLongArrayOfArrays_pb2.ProtoLongArrayOfArrays(\n        )\n        self.atlas.nodeOutEdgesIndices.ParseFromString(zip_entry_data)\n\n    def _load_nodeIndexToRelationIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_NODE_INDEX_TO_RELATION_INDICES)\n        proto_multimap = pyatlas.autogen.ProtoLongToLongMultiMap_pb2.ProtoLongToLongMultiMap()\n        proto_multimap.ParseFromString(zip_entry_data)\n        self.atlas.nodeIndexToRelationIndices = _get_dict_from_protolongtolongmultimap(\n            proto_multimap)\n\n    def _load_edgeIdentifiers(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_EDGE_IDENTIFIERS)\n        self.atlas.edgeIdentifiers = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.edgeIdentifiers.ParseFromString(zip_entry_data)\n\n    def _load_edgeIdentifierToEdgeArrayIndex(self):\n        zip_entry_data = _read_zipentry(self.atlas_file,\n                                        self._FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX)\n        proto_map = pyatlas.autogen.ProtoLongToLongMap_pb2.ProtoLongToLongMap()\n        proto_map.ParseFromString(zip_entry_data)\n        self.atlas.edgeIdentifierToEdgeArrayIndex = _get_dict_from_longtolongmap(proto_map)\n\n    def _load_edgeStartNodeIndex(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_EDGE_START_NODE_INDEX)\n        self.atlas.edgeStartNodeIndex = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.edgeStartNodeIndex.ParseFromString(zip_entry_data)\n\n    def _load_edgeEndNodeIndex(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_EDGE_END_NODE_INDEX)\n        self.atlas.edgeEndNodeIndex = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.edgeEndNodeIndex.ParseFromString(zip_entry_data)\n\n    def _load_edgePolylines(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_EDGE_POLYLINES)\n        proto_array = pyatlas.autogen.ProtoPolyLineArray_pb2.ProtoPolyLineArray()\n        proto_array.ParseFromString(zip_entry_data)\n        result = []\n        for encoding in proto_array.encodings:\n            poly_line = pyatlas.geometry.decompress_polyline(encoding)\n            result.append(poly_line)\n        self.atlas.edgePolyLines = result\n\n    def _load_edgeTags(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_EDGE_TAGS)\n        proto_edge_tags = pyatlas.autogen.ProtoPackedTagStore_pb2.ProtoPackedTagStore()\n        proto_edge_tags.ParseFromString(zip_entry_data)\n        self.atlas.edgeTags = _get_packed_tag_store_from_proto(proto_edge_tags)\n\n    def _load_edgeIndexToRelationIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_EDGE_INDEX_TO_RELATION_INDICES)\n        proto_multimap = pyatlas.autogen.ProtoLongToLongMultiMap_pb2.ProtoLongToLongMultiMap()\n        proto_multimap.ParseFromString(zip_entry_data)\n        self.atlas.edgeIndexToRelationIndices = _get_dict_from_protolongtolongmultimap(\n            proto_multimap)\n\n    def _load_relationIdentifiers(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_RELATION_IDENTIFIERS)\n        self.atlas.relationIdentifiers = pyatlas.autogen.ProtoLongArray_pb2.ProtoLongArray()\n        self.atlas.relationIdentifiers.ParseFromString(zip_entry_data)\n\n    def _load_relationIdentifierToRelationArrayIndex(self):\n        zip_entry_data = _read_zipentry(self.atlas_file,\n                                        self._FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX)\n        proto_map = pyatlas.autogen.ProtoLongToLongMap_pb2.ProtoLongToLongMap()\n        proto_map.ParseFromString(zip_entry_data)\n        self.atlas.relationIdentifierToRelationArrayIndex = _get_dict_from_longtolongmap(proto_map)\n\n    def _load_relationMemberTypes(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_RELATION_MEMBER_TYPES)\n        self.atlas.relationMemberTypes = pyatlas.autogen.ProtoByteArrayOfArrays_pb2.ProtoByteArrayOfArrays(\n        )\n        self.atlas.relationMemberTypes.ParseFromString(zip_entry_data)\n\n    def _load_relationMemberIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_RELATION_MEMBER_INDICES)\n        self.atlas.relationMemberIndices = pyatlas.autogen.ProtoLongArrayOfArrays_pb2.ProtoLongArrayOfArrays(\n        )\n        self.atlas.relationMemberIndices.ParseFromString(zip_entry_data)\n\n    def _load_relationMemberRoles(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_RELATION_MEMBER_ROLES)\n        self.atlas.relationMemberRoles = pyatlas.autogen.ProtoIntegerArrayOfArrays_pb2.ProtoIntegerArrayOfArrays(\n        )\n        self.atlas.relationMemberRoles.ParseFromString(zip_entry_data)\n\n    def _load_relationTags(self):\n        zip_entry_data = _read_zipentry(self.atlas_file, self._FIELD_RELATION_TAGS)\n        proto_relation_tags = pyatlas.autogen.ProtoPackedTagStore_pb2.ProtoPackedTagStore()\n        proto_relation_tags.ParseFromString(zip_entry_data)\n        self.atlas.relationTags = _get_packed_tag_store_from_proto(proto_relation_tags)\n\n    def _load_relationIndexToRelationIndices(self):\n        zip_entry_data = _read_zipentry(self.atlas_file,\n                                        self._FIELD_RELATION_INDEX_TO_RELATION_INDICES)\n        proto_multimap = pyatlas.autogen.ProtoLongToLongMultiMap_pb2.ProtoLongToLongMultiMap()\n        proto_multimap.ParseFromString(zip_entry_data)\n        self.atlas.relationIndexToRelationIndices = _get_dict_from_protolongtolongmultimap(\n            proto_multimap)\n\n    def _load_field(self, field_name):\n        if field_name not in self._FIELD_NAMES_TO_LOAD_METHODS:\n            raise KeyError('unrecognized field {}'.format(field_name))\n\n        # reflection code to get the appropriate load method for the field we are loading\n        method_name = self._FIELD_NAMES_TO_LOAD_METHODS[field_name]\n        load_method_to_call = getattr(self, method_name)\n        load_method_to_call()\n\n\nclass _PackedTagStore(object):\n    \"\"\"\n    Stores indexes into the global tag dictionary.\n    \"\"\"\n\n    def __init__(self, dictionary):\n        self.keys = []\n        self.values = []\n        self.dictionary = dictionary\n\n    def set_dictionary(self, dictionary):\n        self.dictionary = dictionary\n\n    def __str__(self):\n        string = \"KEYS:\\n\"\n        string += str(self.keys)\n        string += \"\\nVALUES:\\n\"\n        string += str(self.values)\n        return string\n\n    def to_key_value_dict(self, index):\n        if self.dictionary is None:\n            raise ValueError('dictionary is not set')\n\n        result = {}\n        if len(self.keys) == 0:\n            return result\n        key_array = self.keys[index]\n        value_array = self.values[index]\n\n        if len(key_array) != len(value_array):\n            raise IndexError('array length mismatch')\n\n        for key, value in zip(key_array, value_array):\n            result[self.dictionary.word(key)] = self.dictionary.word(value)\n\n        return result\n\n\nclass _IntegerDictionary(object):\n    \"\"\"\n    Integer string two-way dictionary.\n    \"\"\"\n\n    def __init__(self):\n        self.word_to_index = {}\n        self.index_to_word = {}\n\n    def add(self, index, word):\n        if word in self.word_to_index:\n            return self.word_to_index[word]\n        self.word_to_index[word] = index\n        self.index_to_word[index] = word\n\n    def size(self):\n        return len(self.word_to_index)\n\n    def word(self, index):\n        # TODO this could throw a KeyError, how to handle?\n        return self.index_to_word[index]\n\n\n# --- static utility functions ---\n\n\ndef _read_zipentry(zip_file, entry):\n    \"\"\"\n    Read a zip entry from a given zip file.\n    \"\"\"\n    with zipfile.ZipFile(zip_file, 'r') as myzip:\n        return myzip.read(entry)\n\n\ndef _get_packed_tag_store_from_proto(proto_store):\n    \"\"\"\n    Take a decoded ProtoPackedTagStore object and turn\n    it into a more user friendly PackedTagStore object.\n    \"\"\"\n    new_store = _PackedTagStore(None)\n\n    for key_array in proto_store.keys.arrays:\n        new_sub_array = []\n        for element in key_array.elements:\n            new_sub_array.append(element)\n        new_store.keys.append(new_sub_array)\n\n    for value_array in proto_store.values.arrays:\n        new_sub_array = []\n        for element in value_array.elements:\n            new_sub_array.append(element)\n        new_store.values.append(new_sub_array)\n\n    return new_store\n\n\ndef _get_integer_dictionary_from_proto(proto_integer_dictionary):\n    \"\"\"\n    Takes a decoded ProtoIntegerStringDictionary object and turns\n    it into a more user friendly IntegerDictionary object.\n    \"\"\"\n    new_dict = _IntegerDictionary()\n    size_indexes = len(proto_integer_dictionary.indexes)\n    size_words = len(proto_integer_dictionary.words)\n    if size_words != size_indexes:\n        raise IndexError('proto array sizes do not match')\n\n    for index, word in zip(proto_integer_dictionary.indexes, proto_integer_dictionary.words):\n        new_dict.add(index, word)\n\n    return new_dict\n\n\ndef _get_dict_from_longtolongmap(proto_map):\n    \"\"\"\n    Convert the ProtoLongToLongMap_pb2 type to a simple dict.\n    \"\"\"\n    if len(proto_map.keys.elements) != len(proto_map.values.elements):\n        raise IndexError('array length mismatch')\n    new_dict = {}\n    for key, value in zip(proto_map.keys.elements, proto_map.values.elements):\n        new_dict[key] = value\n    return new_dict\n\n\ndef _get_dict_from_protolongtolongmultimap(proto_map):\n    \"\"\"\n    Convert the ProtoLongToLongMultiMap_pb2 type to a simple dict.\n    \"\"\"\n    if len(proto_map.keys.elements) != len(proto_map.values):\n        raise IndexError('array length mismatch')\n    new_dict = {}\n    for key, array_value in zip(proto_map.keys.elements, proto_map.values):\n        value_list = []\n        for value in array_value.elements:\n            value_list.append(value)\n        new_dict[key] = value_list\n    return new_dict\n"
  },
  {
    "path": "pyatlas/pyatlas/atlas_entities.py",
    "content": "\"\"\"\nThis module defines the Atlas entity types. Entities are features that can be\nqueried from the Atlas, so things like Nodes, Lines, Relations, etc. The Atlas\nentities themselves are flyweight classes, and simply store references into the\nAtlas feature arrays.\n\nIn general, the entity classes in this module should not be instantiated\ndirectly. Instead, entity objects should be obtained through the appropriate\nAtlas API method.\n\"\"\"\n\nimport pyatlas.identifier_converters\nimport pyatlas.geometry\n\n\nclass EntityType(object):\n    \"\"\"\n    An enum for AtlasEntity types. Valid settings are NODE, EDGE, AREA, LINE,\n    POINT, and RELATION.\n    \"\"\"\n\n    def __init__(self):\n        raise NotImplementedError\n\n    # these MUST match the Java implementation for serialization compatibility\n    NODE = 0\n    EDGE = 1\n    AREA = 2\n    LINE = 3\n    POINT = 4\n    RELATION = 5\n\n    _strs = {\n        NODE: \"NODE\",\n        EDGE: \"EDGE\",\n        AREA: \"AREA\",\n        LINE: \"LINE\",\n        POINT: \"POINT\",\n        RELATION: \"RELATION\",\n    }\n\n\nclass AtlasEntity(pyatlas.geometry.Boundable):\n    \"\"\"\n    A tagged, located entity in an Atlas. Can be a member of a relation.\n    An AtlasEntity should not be instantiated directly. Use one of the\n    appropriate sub-classes.\n    \"\"\"\n\n    def __init__(self, parent_atlas):\n        \"\"\"\n        AtlasEntity should not be instantiated directly.\n        \"\"\"\n        self.parent_atlas = parent_atlas\n\n    def __eq__(self, other):\n        \"\"\"\n        Determine if this AtlasEntity is equal to another. Two entities are\n        considered equal if they have the same identifier and the same type.\n        \"\"\"\n        return self.get_identifier() == other.get_identifier() and self.get_type(\n        ) == other.get_type()\n\n    def __ne__(self, other):\n        \"\"\"\n        Determine if this AtlasEntity is NOT equal to another. Inverse of the\n        comparison made by the __eq__() method.\n        \"\"\"\n        return not (self.get_identifier() == other.get_identifier()\n                    and self.get_type() == other.get_type())\n\n    def __hash__(self):\n        \"\"\"\n        Compute a hashcode for this AtlasEntity.\n        \"\"\"\n        return self.get_identifier() * 31 + self.get_type()\n\n    def get_identifier(self):\n        \"\"\"\n        Get the Atlas identifier of this AtlasEntity.\n        \"\"\"\n        raise NotImplementedError('subclass must implement')\n\n    def get_tags(self):\n        \"\"\"\n        Get a dictionary of this AtlasEntity's tags.\n        \"\"\"\n        raise NotImplementedError('subclass must implement')\n\n    def bounds(self):\n        \"\"\"\n        Compute the bounding Rectangle of this AtlasEntity.\n        \"\"\"\n        raise NotImplementedError('subclass must implement')\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this AtlasEntity intersects some Polygon.\n        \"\"\"\n        raise NotImplementedError('subclass must implement')\n\n    def relations(self):\n        \"\"\"\n        Get the set of Relations of which this AtlasEntity is a member.\n        \"\"\"\n        raise NotImplementedError('subclass must implement')\n\n    def get_type(self):\n        \"\"\"\n        Get the EntityType of this AtlasEntity\n        \"\"\"\n        raise NotImplementedError('subclass must implement')\n\n    def get_osm_identifier(self):\n        \"\"\"\n        Get the OSM identifier of this AtlasEntity.\n        \"\"\"\n        atlas_id = self.get_identifier()\n        return pyatlas.identifier_converters.get_osm_identifier(atlas_id)\n\n    def get_parent_atlas(self):\n        \"\"\"\n        Get the Atlas that contains this AtlasEntity.\n        \"\"\"\n        return self.parent_atlas\n\n    def _get_relations_helper(self, relation_map, index):\n        \"\"\"\n        Subclasses of AtlasEntity can use this helper function to\n        avoid code duplication in their relations() implementations.\n        \"\"\"\n        relation_set = set()\n\n        if index not in relation_map:\n            return relation_set\n\n        for relation_index in relation_map[index]:\n            relation = Relation(self.get_parent_atlas(), relation_index)\n            relation_set.add(relation)\n\n        return frozenset(relation_set)\n\n\nclass Point(AtlasEntity):\n    \"\"\"\n    An Atlas Point. Points are non-navigable.\n    \"\"\"\n\n    def __init__(self, parent_atlas, index):\n        \"\"\"\n        Constuct a new Point. This should not be called directly.\n        \"\"\"\n        super(Point, self).__init__(parent_atlas)\n        self.index = index\n\n    def __str__(self):\n        \"\"\"\n        Get a string representation of this Point.\n        \"\"\"\n        result = '[ '\n        result += 'Point: id=' + str(self.get_identifier())\n        result += ', geom=' + str(self.as_location())\n        result += ', tags=' + str(self.get_tags())\n\n        string = ''\n        for relation in self.relations():\n            string += str(relation.get_identifier()) + ','\n        result += ', relations=[' + string + ']'\n\n        result += ' ]'\n        return result\n\n    def get_identifier(self):\n        \"\"\"\n        Get the Atlas identifier of this Point.\n        \"\"\"\n        return self.get_parent_atlas()._get_pointIdentifiers().elements[self.index]\n\n    def as_location(self):\n        \"\"\"\n        Get the Location of this Point.\n        \"\"\"\n        long_location = self.get_parent_atlas()._get_pointLocations().elements[self.index]\n        return pyatlas.geometry.location_from_packed_int(long_location)\n\n    def get_tags(self):\n        \"\"\"\n        Get a dictionary of this Point's tags.\n        \"\"\"\n        point_tag_store = self.get_parent_atlas()._get_pointTags()\n        return point_tag_store.to_key_value_dict(self.index)\n\n    def bounds(self):\n        \"\"\"\n        Compute the bounding Rectangle of this Point.\n        \"\"\"\n        return self.as_location().bounds()\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this Point intersects some Polygon.\n        \"\"\"\n        return self.as_location().intersects(polygon)\n\n    def relations(self):\n        \"\"\"\n        Get the frozenset of Relations of which this Point is a member.\n        Returns an empty set if this Point is not a member of any Relations.\n        \"\"\"\n        relation_map = self.get_parent_atlas()._get_pointIndexToRelationIndices()\n        return self._get_relations_helper(relation_map, self.index)\n\n    def get_type(self):\n        \"\"\"\n        Overrides superclass get_type(). Always returns EntityType.POINT.\n        \"\"\"\n        return EntityType.POINT\n\n\nclass Line(AtlasEntity):\n    \"\"\"\n    An Atlas Line. Lines are non-navigable.\n    \"\"\"\n\n    def __init__(self, parent_atlas, index):\n        \"\"\"\n        Constuct a new Line. This should not be called directly.\n        \"\"\"\n        super(Line, self).__init__(parent_atlas)\n        self.index = index\n\n    def __str__(self):\n        \"\"\"\n        Get a string representation of this Line.\n        \"\"\"\n        result = '[ '\n        result += 'Line: id=' + str(self.get_identifier())\n        result += ', geom=' + str(self.as_polyline())\n        result += ', tags=' + str(self.get_tags())\n\n        string = ''\n        for relation in self.relations():\n            string += str(relation.get_identifier()) + ','\n        result += ', relations=[' + string + ']'\n\n        result += ' ]'\n        return result\n\n    def as_polyline(self):\n        \"\"\"\n        Get the PolyLine geometry of this Line.\n        \"\"\"\n        return self.get_parent_atlas()._get_linePolyLines()[self.index]\n\n    def get_identifier(self):\n        \"\"\"\n        Get the Atlas identifier of this Line.\n        \"\"\"\n        return self.get_parent_atlas()._get_lineIdentifiers().elements[self.index]\n\n    def get_tags(self):\n        \"\"\"\n        Get a dictionary of this Line's tags.\n        \"\"\"\n        line_tag_store = self.get_parent_atlas()._get_lineTags()\n        return line_tag_store.to_key_value_dict(self.index)\n\n    def bounds(self):\n        \"\"\"\n        Compute the bounding Rectangle of this Line.\n        \"\"\"\n        return self.as_polyline().bounds()\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this Line intersects some Polygon.\n        \"\"\"\n        return self.as_polyline().intersects(polygon)\n\n    def relations(self):\n        \"\"\"\n        Get the frozenset of Relations of which this Line is a member.\n        Returns an empty set if this Line is not a member of any Relations.\n        \"\"\"\n        relation_map = self.get_parent_atlas()._get_lineIndexToRelationIndices()\n        return self._get_relations_helper(relation_map, self.index)\n\n    def get_type(self):\n        \"\"\"\n        Overrides superclass get_type(). Always returns EntityType.LINE.\n        \"\"\"\n        return EntityType.LINE\n\n\nclass Area(AtlasEntity):\n    \"\"\"\n    An Atlas Area.\n    \"\"\"\n\n    def __init__(self, parent_atlas, index):\n        \"\"\"\n        Constuct a new Area. This should not be called directly.\n        \"\"\"\n        super(Area, self).__init__(parent_atlas)\n        self.index = index\n\n    def __str__(self):\n        \"\"\"\n        Get a string representation of this Area.\n        \"\"\"\n        result = '[ '\n        result += 'Area: id=' + str(self.get_identifier())\n        result += ', geom=' + str(self.as_polygon())\n        result += ', tags=' + str(self.get_tags())\n\n        string = ''\n        for relation in self.relations():\n            string += str(relation.get_identifier()) + ','\n        result += ', relations=[' + string + ']'\n\n        result += ' ]'\n        return result\n\n    def get_identifier(self):\n        \"\"\"\n        Get the Atlas identifier of this Area.\n        \"\"\"\n        return self.get_parent_atlas()._get_areaIdentifiers().elements[self.index]\n\n    def as_polygon(self):\n        \"\"\"\n        Get the Polygon geometry of this Area.\n        \"\"\"\n        return self.get_parent_atlas()._get_areaPolygons()[self.index]\n\n    def get_tags(self):\n        \"\"\"\n        Get a dictionary of this Area's tags.\n        \"\"\"\n        area_tag_store = self.get_parent_atlas()._get_areaTags()\n        return area_tag_store.to_key_value_dict(self.index)\n\n    def bounds(self):\n        \"\"\"\n        Compute the bounding Rectangle of this Area.\n        \"\"\"\n        return self.as_polygon().bounds()\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this Area intersects some Polygon.\n        \"\"\"\n        return self.as_polygon().intersects(polygon)\n\n    def relations(self):\n        \"\"\"\n        Get the frozenset of Relations of which this Area is a member.\n        Returns an empty set if this Area is not a member of any Relations.\n        \"\"\"\n        relation_map = self.get_parent_atlas()._get_areaIndexToRelationIndices()\n        return self._get_relations_helper(relation_map, self.index)\n\n    def get_type(self):\n        \"\"\"\n        Overrides superclass get_type(). Always returns EntityType.AREA.\n        \"\"\"\n        return EntityType.AREA\n\n\nclass Node(AtlasEntity):\n    \"\"\"\n    An Atlas Node. A Node is like a Point, except it is part of a navigable\n    structure that can be traversed using the Node and Edge API methods.\n    \"\"\"\n\n    def __init__(self, parent_atlas, index):\n        \"\"\"\n        Constuct a new Node. This should not be called directly.\n        \"\"\"\n        super(Node, self).__init__(parent_atlas)\n        self.index = index\n\n    def __str__(self):\n        \"\"\"\n        Get a string representation of this Node.\n        \"\"\"\n        result = '[ '\n        result += 'Node: id=' + str(self.get_identifier())\n        result += ', geom=' + str(self.as_location())\n        result += ', tags=' + str(self.get_tags())\n\n        string = \"\"\n        for edge in self.in_edges():\n            string += str(edge.get_identifier()) + ','\n        result += ', inEdges=[' + string + ']'\n\n        string = \"\"\n        for edge in self.out_edges():\n            string += str(edge.get_identifier()) + ','\n        result += ', outEdges=[' + string + ']'\n\n        string = ''\n        for relation in self.relations():\n            string += str(relation.get_identifier()) + ','\n        result += ', relations=[' + string + ']'\n\n        result += ' ]'\n        return result\n\n    def get_identifier(self):\n        \"\"\"\n        Get the Atlas identifier of this node.\n        \"\"\"\n        return self.get_parent_atlas()._get_nodeIdentifiers().elements[self.index]\n\n    def as_location(self):\n        \"\"\"\n        Get the Location of this Node.\n        \"\"\"\n        long_location = self.get_parent_atlas()._get_nodeLocations().elements[self.index]\n        return pyatlas.geometry.location_from_packed_int(long_location)\n\n    def get_tags(self):\n        \"\"\"\n        Get a dictionary of this Node's tags.\n        \"\"\"\n        node_tag_store = self.get_parent_atlas()._get_nodeTags()\n        return node_tag_store.to_key_value_dict(self.index)\n\n    def bounds(self):\n        \"\"\"\n        Compute the bounding Rectangle of this Point.\n        \"\"\"\n        return self.as_location().bounds()\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this Node intersects some Polygon.\n        \"\"\"\n        return self.as_location().intersects(polygon)\n\n    def in_edges(self):\n        \"\"\"\n        Get a list of incoming Edges to this Node. The list is sorted by the\n        Edges' Atlas IDs.\n        \"\"\"\n        result = []\n        node_in_edges_indices = self.get_parent_atlas()._get_nodeInEdgesIndices()\n        for index in node_in_edges_indices.arrays[self.index].elements:\n            result.append(Edge(self.get_parent_atlas(), index))\n        return sorted(result)\n\n    def out_edges(self):\n        \"\"\"\n        Get a list of outgoing Edges from this Node. The list is sorted by the\n        Edges' Atlas IDs.\n        \"\"\"\n        result = []\n        node_out_edges_indices = self.get_parent_atlas()._get_nodeOutEdgesIndices()\n        for index in node_out_edges_indices.arrays[self.index].elements:\n            result.append(Edge(self.get_parent_atlas(), index))\n        return sorted(result)\n\n    def connected_edges(self):\n        \"\"\"\n        Get a list of all Edges connected to this Node. The list is sorted by\n        the Edges' Atlas IDs.\n        \"\"\"\n        result = []\n        for edge in self.in_edges():\n            result.append(edge)\n        for edge in self.out_edges():\n            result.append(edge)\n        return sorted(result)\n\n    def get_absolute_valence(self):\n        \"\"\"\n        Get the number of Edges connected to this node. Considers all Edges, not\n        just master Edges.\n        \"\"\"\n        return len(self.connected_edges())\n\n    def get_valence(self):\n        \"\"\"\n        Get the number of Edges connected to this node. Only considers the\n        master Edges.\n        \"\"\"\n        connected_edges = self.connected_edges()\n        valence = 0\n        for edge in connected_edges:\n            if edge.is_master_edge():\n                valence += 1\n        return valence\n\n    def relations(self):\n        \"\"\"\n        Get the frozenset of Relations of which this Node is a member.\n        Returns an empty set if this Node is not a member of any Relations.\n        \"\"\"\n        relation_map = self.get_parent_atlas()._get_nodeIndexToRelationIndices()\n        return self._get_relations_helper(relation_map, self.index)\n\n    def get_type(self):\n        \"\"\"\n        Overrides superclass get_type(). Always returns EntityType.NODE.\n        \"\"\"\n        return EntityType.NODE\n\n\nclass Edge(AtlasEntity):\n    \"\"\"\n    A unidirectional Atlas Edge. An Edge is like a Line, except it is part of a\n    navigable structure that can be traversed using the Node and Edge API methods.\n\n    Bidirectional OSM ways are represented with two opposing Edges, where one\n    of them is the master Edge. The master Edge will have a positive identifier\n    and the same traffic direction as OSM.\n    \"\"\"\n\n    def __init__(self, parent_atlas, index):\n        \"\"\"\n        Constuct a new Edge. This should not be called directly.\n        \"\"\"\n        super(Edge, self).__init__(parent_atlas)\n        self.index = index\n\n    def __str__(self):\n        \"\"\"\n        Get a string representation of this Edge.\n        \"\"\"\n        result = '[ '\n        result += 'Edge: id=' + str(self.get_identifier())\n\n        string = \"\"\n        string += str(self.start().get_identifier()) + ','\n        result += ', start=[' + string + ']'\n\n        string = \"\"\n        string += str(self.end().get_identifier()) + ','\n        result += ', end=[' + string + ']'\n\n        result += ', geom=' + str(self.as_polyline())\n        result += ', tags=' + str(self.get_tags())\n\n        string = ''\n        for relation in self.relations():\n            string += str(relation.get_identifier()) + ','\n        result += ', relations=[' + string + ']'\n\n        result += ' ]'\n        return result\n\n    def __lt__(self, other):\n        \"\"\"\n        Custom implementation of less-than so that collections of Edges can be\n        easily sorted.\n        \"\"\"\n        return self.get_identifier() < other.get_identifier()\n\n    def as_polyline(self):\n        \"\"\"\n        Get the PolyLine geometry of this Edge.\n        \"\"\"\n        return self.get_parent_atlas()._get_edgePolyLines()[self.index]\n\n    def get_identifier(self):\n        \"\"\"\n        Get the Atlas identifier of this Edge.\n        :return: the Atlas id\n        \"\"\"\n        return self.get_parent_atlas()._get_edgeIdentifiers().elements[self.index]\n\n    def get_tags(self):\n        \"\"\"\n        Get a dictionary of this Edge's tags.\n        \"\"\"\n        edge_tag_store = self.get_parent_atlas()._get_edgeTags()\n        return edge_tag_store.to_key_value_dict(self.index)\n\n    def bounds(self):\n        \"\"\"\n        Compute the bounding Rectangle of this Edge.\n        \"\"\"\n        return self.as_polyline().bounds()\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this Edge intersects some Polygon.\n        \"\"\"\n        return self.as_polyline().intersects(polygon)\n\n    def relations(self):\n        \"\"\"\n        Get the frozenset of Relations of which this Edge is a member.\n        Returns an empty set if this Edge is not a member of any Relations.\n        \"\"\"\n        relation_map = self.get_parent_atlas()._get_edgeIndexToRelationIndices()\n        return self._get_relations_helper(relation_map, self.index)\n\n    def connected_nodes(self):\n        \"\"\"\n        Get a frozenset of the Nodes connected to this Edge.\n        \"\"\"\n        result = set()\n        result.add(self.start())\n        result.add(self.end())\n        return frozenset(result)\n\n    def connected_edges(self):\n        \"\"\"\n        Get a frozenset of the Edges connected at the ends of the Nodes of this\n        Edge. The set will not contain the Edge this method is called on, but\n        will contain the reversed Edge if this Edge is part of a two-way road.\n        \"\"\"\n        result = set()\n        for edge in self.end().connected_edges():\n            if self != edge:\n                result.add(edge)\n        for edge in self.start().connected_edges():\n            if self != edge:\n                result.add(edge)\n        return result\n\n    def start(self):\n        \"\"\"\n        Get the starting Node of this Edge.\n        \"\"\"\n        edge_start_node_index = self.get_parent_atlas()._get_edgeStartNodeIndex()\n        index = edge_start_node_index.elements[self.index]\n        return Node(self.get_parent_atlas(), index)\n\n    def end(self):\n        \"\"\"\n        Get the ending Node of this Edge.\n        \"\"\"\n        edge_end_node_index = self.get_parent_atlas()._get_edgeEndNodeIndex()\n        index = edge_end_node_index.elements[self.index]\n        return Node(self.get_parent_atlas(), index)\n\n    def get_master_edge(self):\n        \"\"\"\n        Get the master for this Edge. Returns itself if this is the master Edge.\n        \"\"\"\n        if self.is_master_edge():\n            return self\n        else:\n            return self.get_parent_atlas().edge(-1 * self.get_identifier())\n\n    def is_master_edge(self):\n        \"\"\"\n        Checks if this Edge is a master edge.\n        \"\"\"\n        return self.get_identifier() > 0\n\n    def has_reversed_edge(self):\n        \"\"\"\n        Checks if this Edge is a member of a bidirectional Edge pairing.\n        \"\"\"\n        return self.get_parent_atlas().edge(-1 * self.get_identifier()) is not None\n\n    def is_reversed_edge(self, candidate):\n        \"\"\"\n        Check if the candidate Edge is the bidirectional reverse of this Edge.\n        \"\"\"\n        if candidate.get_type() != EntityType.EDGE:\n            return False\n        return self.get_identifier() == -1 * candidate.get_identifier()\n\n    def get_reversed_edge(self):\n        \"\"\"\n        Get the bidirectional pair Edge to this Edge, if it exists. Returns None\n        if it does not.\n        \"\"\"\n        if not self.has_reversed_edge():\n            return None\n        return self.get_parent_atlas().edge(-1 * self.get_identifier())\n\n    def get_highway_tag_value(self):\n        \"\"\"\n        Get the value of the \"highway\" tag of this Edge, if present. Returns\n        None if there is no \"highway\" tag.\n        \"\"\"\n        tags = self.get_tags()\n        if 'highway' in tags:\n            return tags['highway']\n        else:\n            return None\n\n    def is_connected_at_end_to(self, candidates):\n        \"\"\"\n        Given a set of AtlasEntity candidates, test if this edge is directly\n        connected at its end to at least one of the candidates.\n        \"\"\"\n        for entity in candidates:\n            if entity.get_type() == EntityType.NODE:\n                if self.end() == entity:\n                    return True\n            if entity.get_type() == EntityType.EDGE:\n                if self.end() == entity.start():\n                    return True\n        return False\n\n    def is_connected_at_start_to(self, candidates):\n        \"\"\"\n        Given a set of AtlasEntity candidates, test if this edge is directly\n        connected at its start to at least one of the candidates.\n        \"\"\"\n        for entity in candidates:\n            if entity.get_type() == EntityType.NODE:\n                if self.start() == entity:\n                    return True\n            if entity.get_type() == EntityType.EDGE:\n                if self.start() == entity.end():\n                    return True\n        return False\n\n    def is_way_sectioned(self):\n        \"\"\"\n        Determine if this Edge is a way-sectioned road.\n        \"\"\"\n        return pyatlas.identifier_converters.get_way_section_index(self.get_identifier()) != 0\n\n    def get_type(self):\n        \"\"\"\n        Overrides superclass get_type(). Always returns EntityType.EDGE.\n        \"\"\"\n        return EntityType.EDGE\n\n\nclass Relation(AtlasEntity):\n    \"\"\"\n    An Atlas Relation. Aggregates AtlasEntities in a logical relationship.\n    Can contain other Relations as members.\n    \"\"\"\n\n    def __init__(self, parent_atlas, index):\n        \"\"\"\n        Constuct a new Relation. This should not be called directly.\n        \"\"\"\n        super(Relation, self).__init__(parent_atlas)\n        self.index = index\n\n    def __str__(self):\n        \"\"\"\n        Get a string representation of this Relation.\n        \"\"\"\n        result = '[ '\n        result += 'Relation: id=' + str(self.get_identifier())\n\n        string = ''\n        for member in self.get_members():\n            string += str(member) + ','\n        result += ', members=[' + string + ']'\n\n        string = ''\n        for relation in self.relations():\n            string += str(relation.get_identifier()) + ','\n        result += ', relations=[' + string + ']'\n\n        result += ', tags=' + str(self.get_tags())\n        result += ' ]'\n        return result\n\n    def get_identifier(self):\n        \"\"\"\n        Get the Atlas identifier of this Relation.\n        \"\"\"\n        return self.get_parent_atlas()._get_relationIdentifiers().elements[self.index]\n\n    def get_members(self):\n        \"\"\"\n        Get a sorted list of this Relation's members. The members are in\n        RelationMember form.\n        \"\"\"\n        result = []\n        relation_identifiers = self.get_parent_atlas()._get_relationIdentifiers()\n        relation_member_types = self.get_parent_atlas()._get_relationMemberTypes()\n        relation_member_indices = self.get_parent_atlas()._get_relationMemberIndices()\n        relation_member_roles = self.get_parent_atlas()._get_relationMemberRoles()\n        dictionary = self.get_parent_atlas()._get_dictionary()\n\n        array_index = 0\n        # the relationMemberTypes field is a byte array, so the Python treats\n        # it as a string. We need to convert it to a true byte array.\n        for type_value in bytearray(relation_member_types.arrays[self.index].elements):\n            member_index = relation_member_indices.arrays[self.index].elements[array_index]\n            role = dictionary.word(relation_member_roles.arrays[self.index].elements[array_index])\n\n            if type_value == EntityType.NODE:\n                entity = Node(self.get_parent_atlas(), member_index)\n            elif type_value == EntityType.EDGE:\n                entity = Edge(self.get_parent_atlas(), member_index)\n            elif type_value == EntityType.AREA:\n                entity = Area(self.get_parent_atlas(), member_index)\n            elif type_value == EntityType.LINE:\n                entity = Line(self.get_parent_atlas(), member_index)\n            elif type_value == EntityType.POINT:\n                entity = Point(self.get_parent_atlas(), member_index)\n            elif type_value == EntityType.RELATION:\n                entity = Relation(self.get_parent_atlas(), member_index)\n            else:\n                raise ValueError('invalid EntityType value ' + str(type_value))\n            result.append(RelationMember(role, entity, relation_identifiers.elements[self.index]))\n            array_index += 1\n\n        return sorted(result)\n\n    def get_tags(self):\n        \"\"\"\n        Get a dictionary of this Relation's tags.\n        \"\"\"\n        relation_tag_store = self.get_parent_atlas()._get_relationTags()\n        return relation_tag_store.to_key_value_dict(self.index)\n\n    def bounds(self):\n        \"\"\"\n        Compute the bounding Rectangle of this Relation's members.\n        \"\"\"\n        # FIXME this fails if Relations have self-referencing members\n        # this will never happen in a PackedAtlas so it should be OK for now\n        # if pyatlas ever supports MultiAtlas then this will be a concern\n\n        members = self.get_members()\n        if len(members) == 0:\n            return pyatlas.geometry.Rectangle(0, 0)\n\n        entities_to_consider = []\n        for member in self.get_members():\n            entity = member.get_entity()\n            if entity is None:\n                raise ValueError('entity was None, how did this happen?')\n            entities_to_consider.append(entity)\n\n        return pyatlas.geometry.bounds_atlasentities(entities_to_consider)\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if any member of this Relation intersects some Polygon.\n        \"\"\"\n        # FIXME this fails if Relations have self-referencing members\n        # this will never happen in a PackedAtlas so it should be OK for now\n        # if pyatlas ever supports MultiAtlas then this will be a concern\n\n        for member in self.get_members():\n            entity = member.get_entity()\n            if entity.intersects(polygon):\n                return True\n\n        return False\n\n    def relations(self):\n        \"\"\"\n        Get the frozenset of Relations of which this Relation is a member.\n        Returns an empty set if this Relation is not a member of any Relations.\n        \"\"\"\n        relation_map = self.get_parent_atlas()._get_relationIndexToRelationIndices()\n        return self._get_relations_helper(relation_map, self.index)\n\n    def get_type(self):\n        \"\"\"\n        Overrides superclass get_type(). Always returns EntityType.RELATION.\n        \"\"\"\n        return EntityType.RELATION\n\n\nclass RelationMember(object):\n    \"\"\"\n    A container type for Relation members. A RelationMember has a role as well\n    as a reference to its AtlasEntity.\n    \"\"\"\n\n    def __init__(self, role, entity, identifier):\n        \"\"\"\n        Create a new RelationMember.\n        \"\"\"\n        self.role = role\n        self.entity = entity\n        self.identifier = identifier\n\n    def __lt__(self, other):\n        \"\"\"\n        Define an ordering for RelationMembers. Compare EntityTypes, then\n        identifiers, then roles.\n        \"\"\"\n        if self.entity.get_type() < other.entity.get_type():\n            return True\n        elif self.entity.get_type() > other.entity.get_type():\n            return False\n        else:\n            if self.identifier < other.identifier:\n                return True\n            elif self.identifier > other.identifier:\n                return False\n            else:\n                if self.role < other.role:\n                    return True\n                else:\n                    return False\n\n    def __str__(self):\n        \"\"\"\n        Get a string representation of this RelationMember.\n        \"\"\"\n        result = '[ '\n        result += 'id=' + str(self.get_entity().get_identifier())\n        result += ', type=' + entity_type_to_str(self.entity.get_type())\n        result += ', role=' + str(self.get_role())\n        result += ' ]'\n        return result\n\n    def get_entity(self):\n        \"\"\"\n        Get this RelationMember's AtlasEntity.\n        \"\"\"\n        return self.entity\n\n    def get_relation_identifier(self):\n        \"\"\"\n        Get the identifier of the Relation of which this RelationMember is a member.\n        \"\"\"\n        return self.identifier\n\n    def get_role(self):\n        \"\"\"\n        Get the role of this RelationMember.\n        \"\"\"\n        return self.role\n\n\ndef entity_type_to_str(value):\n    \"\"\"\n    Convert an EntityType enum to a string representation.\n    \"\"\"\n    return EntityType._strs[value]\n"
  },
  {
    "path": "pyatlas/pyatlas/atlas_metadata.py",
    "content": "\"\"\"\nThis module defines the AtlasMetaData container type.\n\"\"\"\n\n\nclass AtlasMetaData(object):\n    \"\"\"\n    Container class for an Atlas's metadata.\n\n    Readable metadata fields:\n    number_of_points (long)\n    number_of_lines (long)\n    number_of_areas (long)\n    number_of_nodes (long)\n    number_of_edges (long)\n    number_of_relations (long)\n    original (bool)\n    code_version (string)\n    data_version (string)\n    country (string)\n    shard_name (string)\n    tags (dict)\n    \"\"\"\n\n    def __init__(self):\n        self.number_of_edges = 0\n        self.number_of_nodes = 0\n        self.number_of_areas = 0\n        self.number_of_lines = 0\n        self.number_of_points = 0\n        self.number_of_relations = 0\n        self.original = False\n        self.code_version = \"\"\n        self.data_version = \"\"\n        self.country = \"\"\n        self.shard_name = \"\"\n        self.tags = {}\n\n\ndef _get_atlas_metadata_from_proto(proto_atlas_metadata):\n    \"\"\"\n    Take a decoded ProtoAtlasMetaData object and turn\n    it into a more user friendly AtlasMetaData object.\n    \"\"\"\n    new_atlas_metadata = AtlasMetaData()\n    new_atlas_metadata.number_of_edges = proto_atlas_metadata.edgeNumber\n    new_atlas_metadata.number_of_nodes = proto_atlas_metadata.nodeNumber\n    new_atlas_metadata.number_of_areas = proto_atlas_metadata.areaNumber\n    new_atlas_metadata.number_of_lines = proto_atlas_metadata.lineNumber\n    new_atlas_metadata.number_of_points = proto_atlas_metadata.pointNumber\n    new_atlas_metadata.number_of_relations = proto_atlas_metadata.relationNumber\n    new_atlas_metadata.original = proto_atlas_metadata.original\n    new_atlas_metadata.code_version = proto_atlas_metadata.codeVersion\n    new_atlas_metadata.data_version = proto_atlas_metadata.dataVersion\n    new_atlas_metadata.country = proto_atlas_metadata.country\n    new_atlas_metadata.shard_name = proto_atlas_metadata.shardName\n\n    # convert prototags and fill the tag dict\n    for proto_tag in proto_atlas_metadata.tags:\n        new_atlas_metadata.tags[proto_tag.key] = proto_tag.value\n\n    return new_atlas_metadata\n"
  },
  {
    "path": "pyatlas/pyatlas/autogen/__init__.py",
    "content": ""
  },
  {
    "path": "pyatlas/pyatlas/geometry.py",
    "content": "\"\"\"\nThis module defines the pyatlas geometry primitives as well as various helping\nfunctions for manipulating the geometry. These primitives are built using\nlat-long locations on the Earth.\n\"\"\"\n\nimport math\nimport shapely.geometry\n\n# --- Location definition constants ---\n_LATITUDE_MIN_DM7 = -900000000\n_LATITUDE_MAX_DM7 = 900000000\n_LONGITUDE_MIN_DM7 = -1800000000\n_LONGITUDE_MAX_DM7 = 1800000000 - 1\n# There are 10 million dm7 in a degree\n_DM7_PER_DEGREE = 10000000\n\n# --- PolyLine encoding constants ---\n# Do not change the precision. It matches the default in the Java implementation.\n_PRECISION = 7\n_ENCODING_OFFSET_MINUS_ONE = 63\n_FIVE_BIT_MASK = 0x1f\n_SIXTH_BIT_MASK = 0x20\n_BIT_SHIFT = 5\n_MAXIMUM_DELTA_LONGITUDE = 180 * pow(10, _PRECISION)\n\n\nclass Boundable(object):\n    \"\"\"\n    A Boundable is any geometric object that can be bounded by Rectangle.\n    \"\"\"\n\n    def __init__(self):\n        raise NotImplementedError('Boundable should not be instantiated')\n\n    def bounds(self):\n        \"\"\"\n        Get the bounding Rectangle of this object.\n        \"\"\"\n        raise NotImplementedError('subclass must implement')\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this Boundable intersects some Polygon.\n        \"\"\"\n        raise NotImplementedError('subclass must implement')\n\n\nclass Location(Boundable):\n    \"\"\"\n    A latitude-longitude location. This is the building block of the other\n    geometric types.\n\n    Uses the dm7 type to represent coordinates.\n    dm7 is an integral representation of a decimal degree fixed to 7 places of\n    precision. 7 places are enough to specify any location on Earth with\n    submeter accuracy.\n\n    Examples:\n    45.01 degrees -> 450_100_000 dm7\n    -90 degrees -> -900_000_000 dm7\n    150.5 degrees -> 1_505_000_000 dm7\n    \"\"\"\n\n    def __init__(self, latitude, longitude):\n        \"\"\"\n        Create a new Location with a dm7 latitude and longitude. To create a\n        Location with degrees, use the geometry.location_with_degrees() module\n        function.\n        \"\"\"\n        if not isinstance(latitude, int):\n            raise TypeError('latitude must be an integer')\n        if not isinstance(longitude, int):\n            raise TypeError('longitude must be an integer')\n\n        if latitude > _LATITUDE_MAX_DM7 or latitude < _LATITUDE_MIN_DM7:\n            raise ValueError('latitude {} out of range'.format(str(latitude)))\n        if longitude > _LONGITUDE_MAX_DM7 or longitude < _LONGITUDE_MIN_DM7:\n            raise ValueError('longitude {} out of range'.format(str(longitude)))\n\n        self.latitude = latitude\n        self.longitude = longitude\n\n    def __str__(self):\n        \"\"\"\n        Get the wkt string representation of this Location. The pair ordering\n        of Locations is always (LATITUDE, LONGITUDE)\n        \"\"\"\n        shapely_point = location_to_shapely_point_wkt_compat(self)\n        return shapely_point.wkt\n\n    def __eq__(self, other):\n        \"\"\"\n        Check if two Locations are equal. Equivalent Locations have equal\n        latitude and equal longitude.\n        \"\"\"\n        return self.latitude == other.latitude and self.longitude == other.longitude\n\n    def __ne__(self, other):\n        \"\"\"\n        Check if two Locations are NOT equal.\n        \"\"\"\n        return not (self.latitude == other.latitude and self.longitude == other.longitude)\n\n    def __hash__(self):\n        \"\"\"\n        Compute a hashcode for this Location.\n        \"\"\"\n        hash_value = self.latitude * 31\n        hash_value = hash_value * 31 + self.longitude\n        return hash_value\n\n    def get_as_packed_int(self):\n        \"\"\"\n        Pack this Location into a 64 bit integer. The higher order 32 bits are\n        the latitude, the lower order 32 bits are the longitude.\n        \"\"\"\n        packed = self.latitude\n        packed = packed << 32\n        packed = packed | (self.longitude & 0xFFFFFFFF)\n        return packed\n\n    def bounds(self):\n        \"\"\"\n        Get the bounding Rectangle of this Location.\n        \"\"\"\n        return Rectangle(self, self)\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this Location intersects some Polygon.\n        \"\"\"\n        return polygon.fully_geometrically_encloses_location(self)\n\n    def get_latitude(self):\n        \"\"\"\n        Get the latitude of this Location as a dm7.\n        \"\"\"\n        return self.latitude\n\n    def get_latitude_deg(self):\n        \"\"\"\n        Get the latitude of this Location as a degree.\n        \"\"\"\n        return dm7_as_degree(self.get_latitude())\n\n    def get_longitude(self):\n        \"\"\"\n        Get the longitude of this Location as a dm7.\n        \"\"\"\n        return self.longitude\n\n    def get_longitude_deg(self):\n        \"\"\"\n        Get the latitude of this Location as a degree.\n        \"\"\"\n        return dm7_as_degree(self.get_longitude())\n\n\nclass PolyLine(Boundable):\n    \"\"\"\n    A PolyLine is a set of Locations in a specific order.\n    \"\"\"\n\n    def __init__(self, location_list, deep=False):\n        \"\"\"\n        Create a new PolyLine given a Location list. By default, it will perform\n        a reference copy of the Location list. Can optionally perform a deep copy\n        of the list instead.\n        \"\"\"\n        if len(location_list) == 0:\n            raise ValueError('cannot have an empty PolyLine')\n        if deep:\n            self.location_list = []\n            for point in location_list:\n                new_point = Location(point.get_latitude(), point.get_longitude())\n                self.location_list.append(new_point)\n        else:\n            self.location_list = location_list\n\n    def __str__(self):\n        \"\"\"\n        Get the wkt string representation of this PolyLine.\n        \"\"\"\n        shapely_poly = polyline_to_shapely_linestring_wkt_compat(self)\n        return shapely_poly.wkt\n\n    def __eq__(self, other):\n        \"\"\"\n        Check if this PolyLine is the same as another PolyLine. Compares their\n        internal Location list.\n        \"\"\"\n        if len(self.location_list) != len(other.location_list):\n            return False\n        for point, other_point in zip(self.locations(), other.locations()):\n            if not point == other_point:\n                return False\n        return True\n\n    def __ne__(self, other):\n        \"\"\"\n        Check if this PolyLine is NOT the same as another PolyLine.\n        \"\"\"\n        if len(self.location_list) != len(other.location_list):\n            return True\n        for point, other_point in zip(self.locations(), other.locations()):\n            if not point == other_point:\n                return True\n        return False\n\n    def __hash__(self):\n        \"\"\"\n        Compute a hashcode for this PolyLine.\n        \"\"\"\n        hash_value = 31\n        for point in self.locations():\n            hash_value = hash_value * 31 + point.__hash__()\n        return hash_value\n\n    def compress(self):\n        \"\"\"\n        Transform this PolyLine into its compressed representation. The\n        compression is based on the MapQuest compressed lat/lon encoding\n        found here:\n        https://developer.mapquest.com/documentation/common/encode-decode/\n        \"\"\"\n        old_latitude = 0\n        old_longitude = 0\n        encoded = \"\"\n        precision = pow(10, _PRECISION)\n        last = Location(0, 0)\n\n        for point in self.locations():\n            latitude = int(round(dm7_as_degree(point.latitude) * precision))\n            longitude = int(round(dm7_as_degree(point.longitude) * precision))\n            encoded += _encode_number(latitude - old_latitude)\n            delta_longitude = longitude - old_longitude\n            if delta_longitude > _MAXIMUM_DELTA_LONGITUDE:\n                raise ValueError(\n                    'unable to compress polyline, consecutive points {} and {} too far apart', last,\n                    point)\n            encoded += _encode_number(delta_longitude)\n\n            old_latitude = latitude\n            old_longitude = longitude\n            last = point\n\n        if type(encoded) is str:\n            encoded = bytes(encoded, 'utf-8')\n\n        return encoded\n\n    def bounds(self):\n        \"\"\"\n        Get the bounding Rectangle of this PolyLine.\n        \"\"\"\n        return bounds_locations(self.locations())\n\n    def intersects_polyline(self, polyline):\n        \"\"\"\n        Check if this PolyLine intersects some PolyLine.\n        \"\"\"\n        shapely_polyline = polyline_to_shapely_linestring(polyline)\n        shapely_polyline_self = polyline_to_shapely_linestring(self)\n        return shapely_polyline_self.intersects(shapely_polyline)\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this PolyLine intersects some Polygon.\n        \"\"\"\n        return polygon.overlaps_polyline(self)\n\n    def get_locations_list(self):\n        \"\"\"\n        Get the underlying Location list for this PolyLine.\n        \"\"\"\n        return self.location_list\n\n    def length(self):\n        \"\"\"\n        Get the length of this PolyLine along the surface of the Earth, in meters.\n        \"\"\"\n        prev_location = None\n        sum_distance = 0\n\n        for cur_location in self.locations():\n            if prev_location is not None:\n                sum_distance += location_haversine_distance(cur_location, prev_location)\n            prev_location = cur_location\n\n        return sum_distance\n\n    def locations(self):\n        \"\"\"\n        Get a generator for the Locations in this PolyLine.\n        \"\"\"\n        for point in self.location_list:\n            yield point\n\n\nclass Polygon(PolyLine):\n    \"\"\"\n    A special case of PolyLine that has an extra segment between the last and\n    first point - effectively a closed PolyLine. The Polygon does not actually\n    re-store the last (first) Location. Instead, the API simulates its presence.\n    \"\"\"\n\n    def __init__(self, location_list, deep=False):\n        \"\"\"\n        Create a new Polygon given a Location list. By default, it will perform\n        a shallow copy of the parameter list. Can optionally perform a deep copy\n        of the list.\n        \"\"\"\n        super(Polygon, self).__init__(location_list, deep)\n\n    def __str__(self):\n        \"\"\"\n        Get the wkt string representation of this Polygon.\n        \"\"\"\n        shapely_poly = polygon_to_shapely_polygon_wkt_compat(self)\n        return shapely_poly.wkt\n\n    def fully_geometrically_encloses_location(self, location):\n        \"\"\"\n        Test if this Polygon fully geometrically encloses a given Location. Will\n        return False if the Location lies perfectly on the Polygon's boundary.\n        \"\"\"\n        shapely_point = location_to_shapely_point(location)\n        shapely_poly_self = polygon_to_shapely_polygon(self)\n        return shapely_poly_self.contains(shapely_point)\n\n    def _overlaps_polygon(self, polygon):\n        \"\"\"\n        Test if this Polygon overlaps a given Polygon at any point.\n        \"\"\"\n        # TODO Shapely differentiates between overlaps and intersects\n        # Shapely intersects() allows one to contain the other\n        # Shapely overlaps() means they intersect, but neither contains the other\n        # which is the right choice here?\n        shapely_polyg_self = polygon_to_shapely_polygon(self)\n        shapely_polyg_other = polygon_to_shapely_polygon(polygon)\n        return shapely_polyg_self.intersects(shapely_polyg_other)\n\n    def overlaps_polyline(self, polyline):\n        \"\"\"\n        Test if this Polygon overlaps a given PolyLine at any point.\n        \"\"\"\n        # TODO Shapely differentiates between overlaps and intersects\n        # Shapely intersects() allows one to contain the other\n        # Shapely overlaps() means they intersect, but neither contains the other\n        # which is the right choice here?\n        shapely_polyline = polyline_to_shapely_linestring(polyline)\n        shapely_poly_self = polygon_to_shapely_polygon(self)\n        return shapely_poly_self.intersects(shapely_polyline)\n\n    def closed_loop(self):\n        \"\"\"\n        Get a generator for the Locations in this Polygon. Will generate the\n        first item again at the end, simulating the closedness of the Polygon.\n        \"\"\"\n        for point in self.locations():\n            yield point\n        yield self.location_list[0]\n\n    def intersects(self, polygon):\n        \"\"\"\n        Check if this Polygon intersects some other Polygon (ie. overlaps it\n        at any point).\n        \"\"\"\n        return self._overlaps_polygon(polygon)\n\n\nclass Rectangle(Polygon):\n    \"\"\"\n    A special case of Polygon.\n    \"\"\"\n\n    def __init__(self, lower_left, upper_right):\n        \"\"\"\n        Create a new Rectangle using a lower left corner Location and an\n        upper right corner Location.\n        \"\"\"\n        upper_left = Location(upper_right.get_latitude(), lower_left.get_longitude())\n        lower_right = Location(lower_left.get_latitude(), upper_right.get_longitude())\n        locations = [lower_left, upper_left, upper_right, lower_right]\n        super(Rectangle, self).__init__(locations, deep=True)\n        self.lower_left = lower_left\n        self.upper_right = upper_right\n\n    def get_lower_left(self):\n        \"\"\"\n        Get the lower left corner Location of this Rectangle.\n        \"\"\"\n        return self.lower_left\n\n    def get_upper_right(self):\n        \"\"\"\n        Get the upper right corner Location of this Rectangle.\n        \"\"\"\n        return self.upper_right\n\n\ndef location_with_degrees(latitude, longitude):\n    \"\"\"\n    Get a new Location with a latitude and longitude specified in degree values.\n    \"\"\"\n    latitude = degree_as_dm7(latitude)\n    longitude = degree_as_dm7(longitude)\n    return Location(latitude, longitude)\n\n\ndef location_from_packed_int(packed_location):\n    \"\"\"\n    Decode a Location object from a packed 64 bit integer. See\n    Location.get_as_packed_int() for more information.\n    \"\"\"\n    longitude = packed_location & 0xFFFFFFFF\n    if longitude & 0x80000000 > 0:\n        longitude = longitude - (1 << 32)\n\n    latitude = (packed_location >> 32) & 0xFFFFFFFF\n    if latitude & 0x80000000 > 0:\n        latitude = latitude - (1 << 32)\n\n    return Location(latitude, longitude)\n\n\ndef degree_as_dm7(degree):\n    \"\"\"\n    Given a degree, return the equivalent dm7. Does not perform range validation.\n    Performs integer conversion of the result.\n    \"\"\"\n    return int(round(_DM7_PER_DEGREE * degree))\n\n\ndef dm7_as_degree(dm7):\n    \"\"\"\n    Given a dm7, return the equivalent degree. Does not perform range validation.\n    \"\"\"\n    return float(dm7) / _DM7_PER_DEGREE\n\n\ndef decompress_polyline(bytestring):\n    \"\"\"\n    Given a PolyLine bytestring obtained using PolyLine.compress(),\n    decompress it and return it as a PolyLine.\n    \"\"\"\n    locations = _decompress_bytestring(bytestring)\n    return PolyLine(locations)\n\n\ndef decompress_polygon(bytestring):\n    \"\"\"\n    Given a PolyLine bytestring obtained using PolyLine.compress(),\n    decompress it and return it as a Polygon.\n    \"\"\"\n    locations = _decompress_bytestring(bytestring)\n    return Polygon(locations)\n\n\ndef bounds_locations(locations):\n    \"\"\"\n    Build a Rectangle that bounds an iterable of Locations.\n    \"\"\"\n    yielded_at_least_one = False\n    lower_lat = None\n    upper_lat = None\n    left_lon = None\n    right_lon = None\n\n    for location in locations:\n        yielded_at_least_one = True\n        latitude = location.get_latitude()\n        longitude = location.get_longitude()\n        if lower_lat is None or latitude < lower_lat:\n            lower_lat = latitude\n        if upper_lat is None or latitude > upper_lat:\n            upper_lat = latitude\n        if left_lon is None or longitude < left_lon:\n            left_lon = longitude\n        if right_lon is None or longitude > right_lon:\n            right_lon = longitude\n\n    if not yielded_at_least_one:\n        raise ValueError('location iterable must yield at least one value')\n\n    return Rectangle(Location(lower_lat, left_lon), Location(upper_lat, right_lon))\n\n\ndef bounds_atlasentities(entities):\n    \"\"\"\n    Build a Rectangle that bounds an iterable of AtlasEntities.\n    \"\"\"\n    yielded_at_least_one = False\n    lower_lat = None\n    upper_lat = None\n    left_lon = None\n    right_lon = None\n\n    for entity in entities:\n        yielded_at_least_one = True\n        for location in entity.bounds().locations():\n            latitude = location.get_latitude()\n            longitude = location.get_longitude()\n            if lower_lat is None or latitude < lower_lat:\n                lower_lat = latitude\n            if upper_lat is None or latitude > upper_lat:\n                upper_lat = latitude\n            if left_lon is None or longitude < left_lon:\n                left_lon = longitude\n            if right_lon is None or longitude > right_lon:\n                right_lon = longitude\n\n    if not yielded_at_least_one:\n        raise ValueError('entity iterable must yield at least one value')\n\n    return Rectangle(Location(lower_lat, left_lon), Location(upper_lat, right_lon))\n\n\ndef location_haversine_distance(location1, location2):\n    \"\"\"\n    Given two locations, compute the Haversine distance between them. This is\n    the great circle distance between the points on the sphere of the Earth.\n    See https://en.wikipedia.org/wiki/Haversine_formula.\n    \"\"\"\n    mean_radius = 6371000  # meters\n    lat1 = convert_to_radians(location1.get_latitude_deg())\n    lat2 = convert_to_radians(location2.get_latitude_deg())\n    delta_lat = lat2 - lat1\n    delta_lon = convert_to_radians(location2.get_longitude_deg()) - convert_to_radians(\n        location1.get_longitude_deg())\n\n    hav = (math.sin(delta_lat / 2)**\n           2) + math.cos(lat1) * math.cos(lat2) * (math.sin(delta_lon / 2)**2)\n    constant = 2 * math.atan2(math.sqrt(hav), math.sqrt(1 - hav))\n\n    return constant * mean_radius\n\n\ndef convert_to_radians(degree):\n    \"\"\"\n    Convert an angle in degrees to the equivalent angle in radians.\n    \"\"\"\n    return degree * (math.pi / 180)\n\n\ndef convert_to_degrees(radian):\n    \"\"\"\n    Convert an angle in radians to the equivalent angle in degrees.\n    \"\"\"\n    return radian * (180 / math.pi)\n\n\ndef boundable_to_shapely_box(boundable):\n    \"\"\"\n    Convert a pyatlas Boundable type to its Shapely Polygon representation.\n    The Shapely Polygon will always be a rectangle.\n    \"\"\"\n    return polygon_to_shapely_polygon(boundable.bounds())\n\n\ndef polygon_to_shapely_polygon(polygon):\n    \"\"\"\n    Convert a Polygon to its Shapely Polygon representation.\n    \"\"\"\n    shapely_points = []\n    for location in polygon.locations():\n        shapely_points.append(location_to_shapely_point(location))\n\n    return shapely.geometry.Polygon(shapely.geometry.LineString(shapely_points))\n\n\ndef polygon_to_shapely_polygon_wkt_compat(polygon):\n    \"\"\"\n    Convert a Polygon to its Shapely Polygon representation but with WKT\n    compatible coordinates.\n    \"\"\"\n    shapely_points = []\n    for location in polygon.locations():\n        shapely_points.append(location_to_shapely_point_wkt_compat(location))\n\n    return shapely.geometry.Polygon(shapely.geometry.LineString(shapely_points))\n\n\ndef location_to_shapely_point(location):\n    \"\"\"\n    Convert a Location to its Shapely Point representation.\n    \"\"\"\n    latitude = location.get_latitude()\n    longitude = location.get_longitude()\n\n    return shapely.geometry.Point(latitude, longitude)\n\n\ndef location_to_shapely_point_wkt_compat(location):\n    \"\"\"\n    Convert a Location to its Shapely Point representation but with WKT\n    compatible coordinates.\n    \"\"\"\n    latitude = location.get_latitude_deg()\n    longitude = location.get_longitude_deg()\n\n    return shapely.geometry.Point(longitude, latitude)\n\n\ndef polyline_to_shapely_linestring(polyline):\n    \"\"\"\n    Convert a PolyLine to its Shapely LineString representation.\n    \"\"\"\n    shapely_points = []\n    for location in polyline.locations():\n        shapely_points.append(location_to_shapely_point(location))\n\n    return shapely.geometry.LineString(shapely_points)\n\n\ndef polyline_to_shapely_linestring_wkt_compat(polyline):\n    \"\"\"\n    Convert a PolyLine to its Shapely LineString representation but with\n    WKT compatible coordinates.\n    \"\"\"\n    shapely_points = []\n    for location in polyline.locations():\n        shapely_points.append(location_to_shapely_point_wkt_compat(location))\n\n    return shapely.geometry.LineString(shapely_points)\n\n\ndef _encode_number(number):\n    \"\"\"\n    Encode a number as a unicode character.\n    \"\"\"\n    number = number << 1\n    if number < 0:\n        number = ~number\n    encoded = \"\"\n    while number >= _SIXTH_BIT_MASK:\n        code_point = (_SIXTH_BIT_MASK | number & _FIVE_BIT_MASK) + _ENCODING_OFFSET_MINUS_ONE\n        encoded += chr(code_point)\n        number = _urshift32(number, _BIT_SHIFT)\n    encoded += chr(number + _ENCODING_OFFSET_MINUS_ONE)\n    return encoded\n\n\ndef _decompress_bytestring(bytestring):\n    \"\"\"\n    Reverse the compression algorithm in PolyLine.compress().\n    \"\"\"\n    precision = pow(10, -1 * _PRECISION)\n    length = len(bytestring)\n    index = 0\n    latitude = 0\n    longitude = 0\n    locations = []\n\n    while index < length:\n        shift = 0\n        result = 0\n        while True:\n            byte_encoded = bytestring[index] - _ENCODING_OFFSET_MINUS_ONE\n            result |= (byte_encoded & _FIVE_BIT_MASK) << shift\n            shift += _BIT_SHIFT\n            index += 1\n            if byte_encoded < _SIXTH_BIT_MASK:\n                break\n\n        if result & 1 > 0:\n            delta_latitude = ~(_urshift32(result, 1))\n        else:\n            delta_latitude = _urshift32(result, 1)\n        latitude += delta_latitude\n\n        shift = 0\n        result = 0\n        while True:\n            byte_encoded = bytestring[index] - _ENCODING_OFFSET_MINUS_ONE\n            result |= (byte_encoded & _FIVE_BIT_MASK) << shift\n            shift += _BIT_SHIFT\n            index += 1\n            if byte_encoded < _SIXTH_BIT_MASK:\n                break\n\n        if result & 1 > 0:\n            delta_longitude = ~(_urshift32(result, 1))\n        else:\n            delta_longitude = _urshift32(result, 1)\n        longitude += delta_longitude\n\n        latitude = latitude * precision\n        longitude = longitude * precision\n\n        # convert lat/lon to dm7\n        latitude = degree_as_dm7(latitude)\n        longitude = degree_as_dm7(longitude)\n\n        locations.append(Location(latitude, longitude))\n\n    return locations\n\n\ndef _urshift32(to_shift, shift_amount):\n    \"\"\"\n    Perform a 32 bit unsigned right shift (drag in leading 0s).\n    \"\"\"\n    return (to_shift % 0x100000000) >> shift_amount\n"
  },
  {
    "path": "pyatlas/pyatlas/identifier_converters.py",
    "content": "\"\"\"\nThis module defines helpful functions to extract information from Atlas identifiers.\n\"\"\"\n\n# Country code and way sectioned identifiers are 3 decimal digits\n_IDENTIFIER_SCALE = 1000\n\n\ndef get_osm_identifier(full_atlas_identifier):\n    \"\"\"\n     Get the OSM identifier from the full Atlas identifier by removing the\n     country code and way section index.\n\n     Example:\n     Atlas ID: 222222001003 would return OSM ID: 222222\n    \"\"\"\n    full_atlas_identifier = abs(full_atlas_identifier)\n    return full_atlas_identifier // (_IDENTIFIER_SCALE * _IDENTIFIER_SCALE)\n\n\ndef get_country_code(full_atlas_identifier):\n    \"\"\"\n    Get the country code from the full Atlas identifier.\n\n    Example:\n    Atlas ID: 222222001003 would return country code: 1\n    \"\"\"\n    full_atlas_identifier = abs(full_atlas_identifier)\n    return (full_atlas_identifier // _IDENTIFIER_SCALE) % _IDENTIFIER_SCALE\n\n\ndef get_way_section_index(full_atlas_identifier):\n    \"\"\"\n    Get the way section index from the full Atlas identifier.\n\n    Example:\n    Atlas ID: 222222001003 would return index: 3\n    \"\"\"\n    full_atlas_identifier = abs(full_atlas_identifier)\n    return full_atlas_identifier % _IDENTIFIER_SCALE\n"
  },
  {
    "path": "pyatlas/pyatlas/pyatlas_globalfunc.py",
    "content": "\"\"\"\nThis module contains global utility functions for pyatlas.\n\"\"\"\n\n\ndef hello_atlas():\n    \"\"\"\n    Print a welcome message!\n    \"\"\"\n    print(\"Hello Atlas!\")\n"
  },
  {
    "path": "pyatlas/pyatlas/spatial_index.py",
    "content": "\"\"\"\nThis module defines the spatial index class for use by the Atlas.\n\"\"\"\n\nimport ctypes\nimport shapely.geometry\nfrom shapely.geos import lgeos as _lgeos\n\nimport pyatlas.geometry\n\n\nclass SpatialIndex(object):\n    \"\"\"\n    An optimized container class for making spatial queries on AtlasEntities.\n    The Atlas will automatically construct the SpatialIndices it needs as it\n    receives queries, so it is unlikely you will ever need to create instances\n    of this class manually.\n    \"\"\"\n\n    def __init__(self, parent_atlas, entity_type, initial_entities=None):\n        \"\"\"\n        Create a new SpatialIndex. Requires a reference to the parent Atlas of\n        this index, as well as the EntityType of the AtlasEntities it will store.\n        Can optionally accept an iterable of AtlasEntities with which to initialize\n        the index.\n\n        In order to start using the index, one must specify which backing tree\n        implementation to use.\n        \"\"\"\n        self.tree = None\n        self.atlas = parent_atlas\n        self.entity_type = entity_type\n        self.initial_entities = initial_entities\n\n    def initialize_rtree(self):\n        \"\"\"\n        Initialize an R-tree to back this SpatialIndex. The underlying R-tree\n        implementation is immutable, so repeated calls to add() will degrade\n        performance. For more information, see the documentation in the _RTree class.\n        \"\"\"\n        self.tree = _RTree(self.initial_entities)\n\n    def initialize_quadtree(self):\n        \"\"\"\n        Intitialize a quadtree to back this SpatialIndex.\n        CURRENTLY UNIMPLEMENTED\n        \"\"\"\n        # TODO implement the quadtree\n        raise NotImplementedError('quadtree currently not implemented')\n\n    def add(self, entity):\n        \"\"\"\n        Insert an AtlasEntity into the index.\n        \"\"\"\n        if self.tree is None:\n            raise ValueError('must select a tree implementation before using')\n\n        if entity.bounds() is not None:\n            self.tree.add(entity)\n        else:\n            raise ValueError('bounds cannot be None')\n\n    def get(self, bounds, predicate=lambda e: True):\n        \"\"\"\n        Get a frozenset of AtlasEntities that are within or intersecting some bounds.\n        Can optionally accept a matching predicate function.\n        \"\"\"\n        if self.tree is None:\n            raise ValueError('must select a tree implementation before using')\n\n        results = []\n        for item_index in self.tree.get(bounds):\n            result = self.atlas.entity(item_index, self.entity_type)\n            if predicate(result):\n                results.append(result)\n\n        return frozenset(results)\n\n\nclass _RTree(object):\n    \"\"\"\n    A wrapper class for the _CustomSTRtree implementation, which calls into the\n    native GEOS library using machinery from Shapely.\n    \n    This class stores raw AtlasEntity identifiers without any\n    EntityType information. For this reason, users of _RTree should avoid adding\n    AtlasEntities with different EntityTypes to the same tree.\n    \n    Note also that the underlying tree implementation (GEOS R-tree) this class uses\n    is immutable. _RTree simulates mutability by maintaining a parallel list of\n    elements, and rebuilding the underlying tree on each add. For this reason,\n    extensive use of the add() method is not recommended. For more information on\n    the immutability of the GEOS R-tree, check out this class in the GEOS codebase:\n    https://github.com/OSGeo/geos/blob/master/src/index/strtree/AbstractSTRtree.cpp\n    \"\"\"\n\n    def __init__(self, initial_entities=None):\n        \"\"\"\n        Create a new _RTree, optionally with an iterable of initial AtlasEntities.\n        \"\"\"\n        self.contents = []\n        self.tree = None\n\n        if initial_entities is not None:\n            for entity in initial_entities:\n                self.contents.append(entity)\n            self._construct_tree_from_contents()\n\n    def _construct_tree_from_contents(self):\n        \"\"\"\n        Use the tree's contents list (of AtlasEntities) to rebuild the backing\n        _CustomSTRtree.\n        \"\"\"\n        contents_shapely_format = [\n            pyatlas.geometry.boundable_to_shapely_box(entity) for entity in self.contents\n        ]\n\n        # pack the arguments in format expected by the _CustomSTRtree\n        hacktree_arguments = []\n        for entity, cont in zip(self.contents, contents_shapely_format):\n            hacktree_arguments.append((entity.get_identifier(), cont))\n\n        self.tree = _CustomSTRtree(hacktree_arguments)\n\n    def add(self, entity):\n        \"\"\"\n        Insert an AtlasEntity into the _RTree. The underlying _CustomSTRtree\n        (which trivially wraps the GEOS STRtree) is immutable once \"built\", so\n        this method forces a rebuild of the entire tree. The STRtree is \"built\"\n        once any type of query has been made on it.\n        \"\"\"\n        self.contents.append(entity)\n        self._construct_tree_from_contents()\n\n    def get(self, boundable):\n        \"\"\"\n        Given a Boundable object, get a list of the identifiers of all\n        AtlasEntities within the bounds of the Boundable.\n        \"\"\"\n        if self.tree is not None:\n            boundable = pyatlas.geometry.boundable_to_shapely_box(boundable)\n            return self.tree.get(boundable)\n        else:\n            raise ValueError('R-tree is empty')\n\n\nclass _CustomSTRtree(object):\n    \"\"\"\n    Hack re-implementation of the shapely STRtree. Changes the behaviour of the\n    STRtree to allow for insertion of the entity atlas identifier (type long).\n    \"\"\"\n\n    def __init__(self, items):\n        \"\"\"\n        Parameter items is a list of tuples, where each tuple lookes like:\n        (entity_id: long, entity_geometry: shapely.geometry.polygon)\n        \"\"\"\n        self._n_geoms = len(items)\n\n        self._tree_handle = shapely.geos.lgeos.GEOSSTRtree_create(max(4, len(items)))\n        for item in items:\n            _lgeos.GEOSSTRtree_insert(self._tree_handle, item[1]._geom,\n                                      ctypes.py_object(int(item[0])))\n\n        geoms = [item[1] for item in items]\n        self._geoms = list(geoms)\n\n    def __del__(self):\n        if self._tree_handle is not None:\n            _lgeos.GEOSSTRtree_destroy(self._tree_handle)\n            self._tree_handle = None\n\n    def get(self, geom):\n        \"\"\"\n        Get a list of identifiers of AtlasEntities whose bounds intersect the\n        bounds defined by the geom parameter.\n        \"\"\"\n        if self._n_geoms == 0:\n            return []\n\n        result = []\n\n        def callback(item, userdata):\n            identifier = ctypes.cast(item, ctypes.py_object).value\n            result.append(identifier)\n\n        _lgeos.GEOSSTRtree_query(self._tree_handle, geom._geom, _lgeos.GEOSQueryCallback(callback),\n                                 None)\n\n        return result\n"
  },
  {
    "path": "pyatlas/resources/CreateTestAtlas.java",
    "content": "package org.openstreetmap.atlas.test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas.AtlasSerializationFormat;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/*\n * Code sample that uses Java Atlas API to create the test.atlas used by Pyatlas unit tests.\n */\n\npublic class CreateTestAtlas\n{\n    public static void main(final String[] args)\n    {\n        createTestAtlas();\n    }\n\n    private static void createTestAtlas()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n\n        final AtlasMetaData metaData = new AtlasMetaData(AtlasSize.DEFAULT, true, \"testCodeVersion\",\n                \"testDataVersion\", \"testCountry\", \"testShardName\",\n                Maps.hashMap(\"metakey\", \"metaval\"));\n        builder.setMetaData(metaData);\n\n        // add points\n        builder.addPoint(1, new Location(Latitude.degrees(38), Longitude.degrees(-118)),\n                Maps.hashMap());\n        builder.addPoint(2, new Location(Latitude.degrees(38.01), Longitude.degrees(-118.01)),\n                Maps.hashMap(\"some_tag\", \"some_value\"));\n        builder.addPoint(3, new Location(Latitude.degrees(38.02), Longitude.degrees(-118.01)),\n                Maps.hashMap(\"key1\", \"value2\", \"key2\", \"value2\"));\n        builder.addPoint(4, new Location(Latitude.degrees(38), Longitude.degrees(-118.05)),\n                Maps.hashMap(\"\", \"\"));\n        builder.addPoint(5, new Location(Latitude.degrees(38.05), Longitude.degrees(-118.03)), Maps\n                .hashMap(\"key1\", \"value2\", \"key2\", \"value2\", \"key3:subkey1\", \"value3:subvalue1\"));\n\n        // add lines\n        final List<Location> shapePoints = new ArrayList<>();\n        shapePoints.add(new Location(Latitude.degrees(38.02), Longitude.degrees(-118.02)));\n        shapePoints.add(new Location(Latitude.degrees(38.03), Longitude.degrees(-118.01)));\n        shapePoints.add(new Location(Latitude.degrees(38.06), Longitude.degrees(-118.05)));\n        builder.addLine(1, new PolyLine(shapePoints),\n                Maps.hashMap(\"key1\", \"value2\", \"key2\", \"value2\"));\n        shapePoints.clear();\n        shapePoints.add(new Location(Latitude.degrees(38.06), Longitude.degrees(-118.09)));\n        shapePoints.add(new Location(Latitude.degrees(38.03), Longitude.degrees(-118.1)));\n        builder.addLine(2, new PolyLine(shapePoints),\n                Maps.hashMap(\"key1\", \"value2\", \"key2\", \"value2\"));\n\n        // add areas\n        shapePoints.add(new Location(Latitude.degrees(38.1), Longitude.degrees(-118.02)));\n        shapePoints.add(new Location(Latitude.degrees(38.2), Longitude.degrees(-118.01)));\n        shapePoints.add(new Location(Latitude.degrees(38.09), Longitude.degrees(-118.05)));\n        builder.addArea(1, new Polygon(shapePoints),\n                Maps.hashMap(\"key1\", \"value2\", \"key2\", \"value2\"));\n        shapePoints.clear();\n        shapePoints.add(new Location(Latitude.degrees(39.1), Longitude.degrees(-118.06)));\n        shapePoints.add(new Location(Latitude.degrees(39.2), Longitude.degrees(-118.02)));\n        shapePoints.add(new Location(Latitude.degrees(38.09), Longitude.degrees(-118.03)));\n        builder.addArea(2, new Polygon(shapePoints),\n                Maps.hashMap(\"random key\", \"value2\", \"key2\", \"somenewvalue\"));\n\n        // add nodes\n        builder.addNode(1, new Location(Latitude.degrees(39), Longitude.degrees(-118)),\n                Maps.hashMap());\n        builder.addNode(2, new Location(Latitude.degrees(39.02), Longitude.degrees(-119.01)),\n                Maps.hashMap(\"key1\", \"value2\", \"key2\", \"value2\"));\n        builder.addNode(3, new Location(Latitude.degrees(39), Longitude.degrees(-119.05)),\n                Maps.hashMap(\"asd\", \"asdf\"));\n        builder.addNode(4, new Location(Latitude.degrees(39.05), Longitude.degrees(-119.03)), Maps\n                .hashMap(\"key1\", \"value2\", \"key2\", \"value2\", \"key3:subkey1\", \"value3:subvalue1\"));\n\n        // add edges\n        shapePoints.clear();\n        shapePoints.add(new Location(Latitude.degrees(39), Longitude.degrees(-119.05)));\n        shapePoints.add(new Location(Latitude.degrees(39.02), Longitude.degrees(-119.01)));\n        builder.addEdge(1, new PolyLine(shapePoints),\n                Maps.hashMap(\"key1\", \"value2\", \"key2\", \"value2\"));\n        shapePoints.clear();\n        shapePoints.add(new Location(Latitude.degrees(39), Longitude.degrees(-118)));\n        shapePoints.add(new Location(Latitude.degrees(39.05), Longitude.degrees(-119.03)));\n        builder.addEdge(2, new PolyLine(shapePoints),\n                Maps.hashMap(\"key5\", \"asdsavalue2\", \"key2\", \"value2\"));\n        shapePoints.clear();\n        shapePoints.add(new Location(Latitude.degrees(39.05), Longitude.degrees(-119.03)));\n        shapePoints.add(new Location(Latitude.degrees(39), Longitude.degrees(-119.05)));\n        builder.addEdge(3, new PolyLine(shapePoints),\n                Maps.hashMap(\"key5\", \"asdsavalue2\", \"key2\", \"value2\"));\n\n        // add relations\n        RelationBean bean = new RelationBean();\n        bean.addItem(1L, \"node 1\", ItemType.NODE);\n        bean.addItem(2L, \"an edge\", ItemType.EDGE);\n        builder.addRelation(1, 1, bean, Maps.hashMap(\"type\", \"road\", \"status\", \"foo\"));\n\n        bean = new RelationBean();\n        bean.addItem(1L, \"a point\", ItemType.POINT);\n        bean.addItem(2L, \"another point\", ItemType.POINT);\n        builder.addRelation(2, 2, bean, Maps.hashMap(\"key5\", \"qwertyvalue2\", \"key5\", \"asdvalue2\"));\n\n        final PackedAtlas atlas = (PackedAtlas) builder.get();\n        atlas.setSaveSerializationFormat(AtlasSerializationFormat.PROTOBUF);\n        final File resource = new File(\"test.atlas\");\n        System.out.println(\"Test atlas dumped to: \" + resource.getAbsolutePath());\n        atlas.save(resource);\n    }\n}\n"
  },
  {
    "path": "pyatlas/setup.py",
    "content": "import setuptools\n\nwith open(\"README.md\", \"r\") as fh:\n    long_description = fh.read()\n\n# The version field is left blank, and is populated automatically by\n# the 'packagePyatlas' gradle target at build time. The target then resets\n# the field to blank before completing.\nsetuptools.setup(\n    name=\"pyatlas\",\n    version=\n    author=\"lucaspcram\",\n    author_email=\"lucaspcram@gmail.com\",\n    license=\"BSD License\",\n    description=\"A simplified Python API for Atlas\",\n    long_description=long_description,\n    long_description_content_type=\"text/markdown\",\n    url=\"https://github.com/osmlab/atlas\",\n    packages=setuptools.find_packages(exclude=(\"unit_tests\",)),\n    install_requires=[\n        'protobuf==3.11.1',\n        'shapely==1.6.4'\n    ],\n    classifiers=(\n        \"Programming Language :: Python :: 3\",\n        \"Operating System :: OS Independent\",\n        \"License :: OSI Approved :: BSD License\"\n    ),\n)\n"
  },
  {
    "path": "pyatlas/style.yapf",
    "content": "[style]\nbased_on_style = pep8\ncolumn_limit = 100"
  },
  {
    "path": "pyatlas/test.sh",
    "content": "#!/usr/bin/env bash\n\n# general case script abort if a command fails\n# this can be overridden with a custom error message using '|| err_shutdown'\nset -e\nset -o pipefail\n\n### define utility functions ###\n################################\nerr_shutdown() {\n    echo \"test.sh: ERROR: $1\"\n    deactivate\n    exit 1\n}\n\n\n### check to prevent users from running this script directly ###\n################################################################\nif [ \"$1\" != \"ranFromGradle\" ];\nthen\n    err_shutdown \"this script should be run using the atlas gradle task 'testPyatlas'\"\nfi\n\n\n### set up variables to store directory names ###\n#################################################\ngradle_project_root_dir=\"$(pwd)\"\npyatlas_dir=\"pyatlas\"\npyatlas_testdir=\"unit_tests\"\npyatlas_root_dir=\"$gradle_project_root_dir/$pyatlas_dir\"\nvenv_path=\"$pyatlas_root_dir/__pyatlas_venv__\"\n\n\n### abort the script if the pyatlas tests folder is not present ###\n###################################################################\nif [ ! -d \"$pyatlas_root_dir/$pyatlas_testdir\" ];\nthen\n    err_shutdown \"pyatlas tests folder not found\"\nfi\n\n\n### run the tests ###\n#####################\n# start the venv\nif [ ! -d \"$venv_path\" ];\nthen\n    err_shutdown \"missing $venv_path\"\nfi\n# shellcheck source=/dev/null\nsource \"$venv_path/bin/activate\"\n\n# grab the build version from gradle.properties and inject it into setup.py\natlas_version=$(grep \"version=\" \"$gradle_project_root_dir/gradle.properties\" | cut -f2 -d \"=\")\n# GNU and BSD sed have different \"in-place\" flag syntax\nif [ \"$(uname)\" == \"Darwin\" ];\nthen\n    sed -i \"\" \"s/version=.*/version=\\\"$atlas_version\\\",/\" \"$pyatlas_root_dir/setup.py\"\nelif [ \"$(uname)\" == \"Linux\" ];\nthen\n    sed --in-place=\"\" \"s/version=.*/version=\\\"$atlas_version\\\",/\" \"$pyatlas_root_dir/setup.py\"\nelse\n    err_shutdown \"unrecognized platform $(uname)\"\nfi\n\n# enter the pyatlas project directory so the unittest code can discover tests\npushd \"$pyatlas_root_dir\"\npip install -e \"$pyatlas_root_dir\"\necho \"Discovering and running unit tests...\"\necho \"----------------------------------------------------------------------\"\npython -m unittest discover -v -s \"$pyatlas_testdir\" || err_shutdown \"a test failed, aborting early...\"\n# get back to gradle project directory\npopd\n\n# reset version field in setup.py\n# GNU and BSD sed have different \"in-place\" flag syntax\nif [ \"$(uname)\" == \"Darwin\" ];\nthen\n    sed -i \"\" \"s/version=.*/version=/\" \"$pyatlas_root_dir/setup.py\"\nelif [ \"$(uname)\" == \"Linux\" ];\nthen\n    sed --in-place=\"\" \"s/version=.*/version=/\" \"$pyatlas_root_dir/setup.py\"\nelse\n    err_shutdown \"unrecognized platform $(uname)\"\nfi\n\n# shutdown the venv\ndeactivate\n"
  },
  {
    "path": "pyatlas/unit_tests/test_atlas.py",
    "content": "import unittest\n\nfrom pyatlas.atlas import Atlas\nfrom pyatlas import geometry\nfrom pyatlas import atlas_entities\nfrom pyatlas.geometry import Rectangle\n\n\nclass AtlasTest(unittest.TestCase):\n    def setUp(self):\n        pass\n\n    def test_lazy_loading(self):\n        atlas = Atlas(\"resources/test.atlas\")\n        _touch_all_atlas_features(atlas)\n        self.assertEqual(atlas.number_of_points(), 5)\n        self.assertEqual(atlas.number_of_lines(), 2)\n        self.assertEqual(atlas.number_of_areas(), 2)\n        self.assertEqual(atlas.number_of_nodes(), 4)\n        self.assertEqual(atlas.number_of_edges(), 3)\n        self.assertEqual(atlas.number_of_relations(), 2)\n\n    def test_upfront_loading(self):\n        atlas = Atlas(\"resources/test.atlas\", lazy_loading=False)\n        _touch_all_atlas_features(atlas)\n        self.assertEqual(atlas.number_of_points(), 5)\n        self.assertEqual(atlas.number_of_lines(), 2)\n        self.assertEqual(atlas.number_of_areas(), 2)\n        self.assertEqual(atlas.number_of_nodes(), 4)\n        self.assertEqual(atlas.number_of_edges(), 3)\n        self.assertEqual(atlas.number_of_relations(), 2)\n\n    def test_point_spatial_index(self):\n        atlas = Atlas(\"resources/test.atlas\")\n\n        lower_left = geometry.location_with_degrees(37, -118.02)\n        upper_right = geometry.location_with_degrees(39, -118)\n\n        # NOTE point 1 does not show up in the results because it lies on the polygon border\n        test_results = atlas.points_within(Rectangle(lower_left, upper_right))\n        self.assertEqual({atlas.point(2), atlas.point(3)}, test_results)\n\n        test_results = atlas.points_within(\n            Rectangle(lower_left, upper_right), lambda p: p.get_identifier() % 2 != 0)\n        self.assertEqual({atlas.point(3)}, test_results)\n\n        test_results = atlas.points_at(geometry.location_with_degrees(38, -118))\n        self.assertEqual({atlas.point(1)}, test_results)\n\n    def test_line_spatial_index(self):\n        atlas = Atlas(\"resources/test.atlas\")\n\n        test_location = geometry.location_with_degrees(38.02, -118.02)\n        test_results = atlas.lines_containing(test_location)\n        self.assertEqual({atlas.line(1)}, test_results)\n\n        poly = Rectangle(\n            geometry.location_with_degrees(38, -118), geometry.location_with_degrees(39, -119))\n        test_results = atlas.lines_intersecting(poly)\n        self.assertEqual({atlas.line(1), atlas.line(2)}, test_results)\n\n    def test_area_spatial_index(self):\n        atlas = Atlas(\"resources/test.atlas\")\n\n        test_location = geometry.location_with_degrees(38.15, -118.03)\n        test_results = atlas.areas_covering(test_location)\n        self.assertEqual({atlas.area(2)}, test_results)\n\n        test_results = atlas.areas_intersecting(atlas.area(2).as_polygon())\n        self.assertEqual({atlas.area(1), atlas.area(2)}, test_results)\n\n    def test_node_spatial_index(self):\n        atlas = Atlas(\"resources/test.atlas\")\n\n        lower_left = geometry.location_with_degrees(39, -119.04)\n        upper_right = geometry.location_with_degrees(39.05, -119)\n\n        # NOTE node 4 does not show up in results because it lies on the the polygon border\n        test_results = atlas.nodes_within(Rectangle(lower_left, upper_right))\n        self.assertEqual({atlas.node(2)}, test_results)\n\n        test_results = atlas.nodes_within(\n            Rectangle(lower_left, upper_right), lambda n: n.get_identifier() == 3)\n        self.assertEqual(frozenset(), test_results)\n\n        test_results = atlas.nodes_at(geometry.location_with_degrees(39, -119.05))\n        self.assertEqual({atlas.node(3)}, test_results)\n\n    def test_edge_spatial_index(self):\n        atlas = Atlas(\"resources/test.atlas\")\n\n        test_location = geometry.location_with_degrees(39, -119.05)\n        test_results = atlas.edges_containing(test_location)\n        self.assertEqual({atlas.edge(1), atlas.edge(3)}, test_results)\n\n        poly = Rectangle(\n            geometry.location_with_degrees(38, -120), geometry.location_with_degrees(40, -117))\n        test_results = atlas.edges_intersecting(poly)\n        self.assertEqual({atlas.edge(1), atlas.edge(2), atlas.edge(3)}, test_results)\n\n    def test_relation_spatial_index(self):\n        atlas = Atlas(\"resources/test.atlas\")\n\n        lower_left = geometry.location_with_degrees(37.999, -118.001)\n        upper_right = geometry.location_with_degrees(38.001, -117.999)\n\n        test_results = atlas.relations_with_entities_intersecting(\n            Rectangle(lower_left, upper_right))\n        self.assertEqual({atlas.relation(2)}, test_results)\n\n\ndef _touch_all_atlas_features(atlas):\n    for point in atlas.points():\n        string = point.__str__()\n\n    for line in atlas.lines():\n        string = line.__str__()\n\n    for area in atlas.areas():\n        string = area.__str__()\n\n    for node in atlas.nodes():\n        string = node.__str__()\n\n    for edge in atlas.edges():\n        string = edge.__str__()\n\n    for relation in atlas.relations():\n        string = relation.__str__()\n\n    string = str(atlas.point(1))\n    string = str(atlas.line(1))\n    string = str(atlas.area(1))\n    string = str(atlas.node(1))\n    string = str(atlas.edge(1))\n    string = str(atlas.relation(1))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "pyatlas/unit_tests/test_identifier_converters.py",
    "content": "import unittest\n\nfrom pyatlas import identifier_converters\n\n\nclass IdentifierConvertersTest(unittest.TestCase):\n    def setUp(self):\n        pass\n\n    def test_osm_conversion(self):\n        atlas_id = 222222000000\n        osm_id = 222222\n        self.assertEqual(osm_id, identifier_converters.get_osm_identifier(atlas_id))\n\n        atlas_id = 123001002\n        osm_id = 123\n        self.assertEqual(osm_id, identifier_converters.get_osm_identifier(atlas_id))\n\n        atlas_id = 3101220\n        osm_id = 3\n        self.assertEqual(osm_id, identifier_converters.get_osm_identifier(atlas_id))\n\n        atlas_id = -222222000001\n        osm_id = 222222\n        self.assertEqual(osm_id, identifier_converters.get_osm_identifier(atlas_id))\n\n    def test_country_code_conversion(self):\n        atlas_id = 222222000000\n        country_code = 0\n        self.assertEqual(country_code, identifier_converters.get_country_code(atlas_id))\n\n        atlas_id = 123001002\n        country_code = 1\n        self.assertEqual(country_code, identifier_converters.get_country_code(atlas_id))\n\n        atlas_id = 3101220\n        country_code = 101\n        self.assertEqual(country_code, identifier_converters.get_country_code(atlas_id))\n\n        atlas_id = -222222002001\n        country_code = 2\n        self.assertEqual(country_code, identifier_converters.get_country_code(atlas_id))\n\n    def test_way_section_conversion(self):\n        atlas_id = 222222000000\n        way_section = 0\n        self.assertEqual(way_section, identifier_converters.get_way_section_index(atlas_id))\n\n        atlas_id = 123001002\n        way_section = 2\n        self.assertEqual(way_section, identifier_converters.get_way_section_index(atlas_id))\n\n        atlas_id = 3101220\n        way_section = 220\n        self.assertEqual(way_section, identifier_converters.get_way_section_index(atlas_id))\n\n        atlas_id = -222222002001\n        way_section = 1\n        self.assertEqual(way_section, identifier_converters.get_way_section_index(atlas_id))\n"
  },
  {
    "path": "pyatlas/unit_tests/test_location.py",
    "content": "import unittest\n\nfrom pyatlas import geometry\nfrom pyatlas.geometry import Location, Rectangle\n\n\nclass LocationTest(unittest.TestCase):\n    def setUp(self):\n        pass\n\n    def test_location_packing(self):\n        testlocation = Location(1, 1)\n        self.assertEqual(testlocation,\n                         geometry.location_from_packed_int(testlocation.get_as_packed_int()))\n\n        testlocation = Location(1, -3)\n        self.assertEqual(testlocation,\n                         geometry.location_from_packed_int(testlocation.get_as_packed_int()))\n\n        testlocation = Location(-3, -3)\n        self.assertEqual(testlocation,\n                         geometry.location_from_packed_int(testlocation.get_as_packed_int()))\n\n        testlocation = Location(-900000000, 450000000)\n        self.assertEqual(testlocation,\n                         geometry.location_from_packed_int(testlocation.get_as_packed_int()))\n\n        testlocation = Location(900000000, -1800000000)\n        self.assertEqual(testlocation,\n                         geometry.location_from_packed_int(testlocation.get_as_packed_int()))\n\n        testlocation = Location(900000000, 1800000000 - 1)\n        self.assertEqual(testlocation,\n                         geometry.location_from_packed_int(testlocation.get_as_packed_int()))\n\n    def test_location_conversion(self):\n        loc_deg = 45.0\n        loc_dm7 = 450000000\n        self.assertEqual(loc_deg, geometry.dm7_as_degree(loc_dm7))\n        self.assertEqual(loc_dm7, geometry.degree_as_dm7(loc_deg))\n\n        loc_deg = 90\n        loc_dm7 = 900000000\n        self.assertEqual(loc_deg, geometry.dm7_as_degree(loc_dm7))\n        self.assertEqual(loc_dm7, geometry.degree_as_dm7(loc_deg))\n\n        loc_deg = -30\n        loc_dm7 = -300000000\n        self.assertEqual(loc_deg, geometry.dm7_as_degree(loc_dm7))\n        self.assertEqual(loc_dm7, geometry.degree_as_dm7(loc_deg))\n\n        loc_deg = -180\n        loc_dm7 = -1800000000\n        self.assertEqual(loc_deg, geometry.dm7_as_degree(loc_dm7))\n        self.assertEqual(loc_dm7, geometry.degree_as_dm7(loc_deg))\n\n        loc_deg = 179.9999\n        loc_dm7 = 1799999000\n        self.assertEqual(loc_deg, geometry.dm7_as_degree(loc_dm7))\n        self.assertEqual(loc_dm7, geometry.degree_as_dm7(loc_deg))\n\n    def test_location_bounds(self):\n        testlocation = Location(450000000, 450000000)\n        testlocationbounds = Rectangle(testlocation, testlocation)\n        bound = testlocation.bounds()\n        self.assertEqual(bound, testlocationbounds)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "pyatlas/unit_tests/test_polygon_converters.py",
    "content": "import unittest\n\nfrom pyatlas import geometry\nfrom pyatlas.geometry import Location\nfrom pyatlas.geometry import Polygon\nfrom pyatlas.geometry import PolyLine\nimport shapely.geometry\n\n\nclass PolygonConvertersTest(unittest.TestCase):\n    def setUp(self):\n        pass\n\n    def test_boundable_to_shapely_box(self):\n        loclist = [\n            Location(0, 0),\n            Location(400000000, 0),\n            Location(350000000, 300000000),\n            Location(450000000, 450000000),\n            Location(1000, 450000000)\n        ]\n        bounds = Polygon(loclist).bounds()\n\n        shapely_box = geometry.boundable_to_shapely_box(bounds)\n        test_against = shapely.geometry.LineString([(0, 0), (450000000, 0), (450000000, 450000000),\n                                                    (0, 450000000)])\n        test_against = shapely.geometry.Polygon(test_against)\n\n        self.assertTrue(shapely_box, test_against)\n\n    def test_polygon_to_shapely_polygon(self):\n        loclist = [\n            Location(0, 0),\n            Location(400000000, 0),\n            Location(350000000, 300000000),\n            Location(450000000, 450000000),\n            Location(1000, 450000000)\n        ]\n        polygon = Polygon(loclist)\n\n        shapely_poly = geometry.polygon_to_shapely_polygon(polygon)\n        test_against = shapely.geometry.LineString([(0, 0), (400000000, 0), (350000000, 300000000),\n                                                    (450000000, 450000000), (1000, 450000000)])\n        test_against = shapely.geometry.Polygon(test_against)\n\n        self.assertTrue(shapely_poly, test_against)\n\n    def test_location_to_shapely_point(self):\n        l1 = Location(0, 0)\n        l2 = Location(1000, 2000)\n        l3 = Location(50000, -1000000)\n\n        p1 = geometry.location_to_shapely_point(l1)\n        p2 = geometry.location_to_shapely_point(l2)\n        p3 = geometry.location_to_shapely_point(l3)\n\n        self.assertEqual(shapely.geometry.Point(0, 0), p1)\n        self.assertEqual(shapely.geometry.Point(1000, 2000), p2)\n        self.assertEqual(shapely.geometry.Point(50000, -1000000), p3)\n\n    def test_polyline_to_shapely_linestring(self):\n        polyline1 = PolyLine([Location(-1000, -1000), Location(0, 0), Location(5000, 8000)])\n        linestring1 = geometry.polyline_to_shapely_linestring(polyline1)\n        test_against = shapely.geometry.LineString([(-1000, -1000), (0, 0), (5000, 8000)])\n        self.assertEqual(linestring1, test_against)\n"
  },
  {
    "path": "pyatlas/unit_tests/test_polyline_polygon.py",
    "content": "import unittest\n\nfrom pyatlas import geometry\nfrom pyatlas.geometry import Location, PolyLine, Polygon, Rectangle\n\n\nclass PolyLinePolygonTest(unittest.TestCase):\n    def setUp(self):\n        pass\n\n    def test_polyline_compression(self):\n        loclist = [Location(1, 1), Location(2, 2), Location(5, 5)]\n        correct_polyline = PolyLine(loclist, deep=True)\n        test_polyline = geometry.decompress_polyline(correct_polyline.compress())\n        self.assertEqual(correct_polyline, test_polyline)\n\n        loclist = [\n            Location(382117269, -1193153616),\n            Location(382117927, -1193152951),\n            Location(382116912, -1193151049),\n            Location(382116546, -1193151382),\n            Location(382116134, -1193150734),\n            Location(382115440, -1193151494)\n        ]\n        correct_polyline = PolyLine(loclist, deep=True)\n        test_polyline = geometry.decompress_polyline(correct_polyline.compress())\n        self.assertEqual(correct_polyline, test_polyline)\n\n    def test_polygon_closedness(self):\n        loclist = [\n            Location(382117269, -1193153616),\n            Location(382117927, -1193152951),\n            Location(382116912, -1193151049),\n            Location(382116546, -1193151382),\n            Location(382116134, -1193150734),\n            Location(382115440, -1193151494)\n        ]\n        correct_polygon = Polygon(loclist, deep=True)\n        closed_list = []\n        for point in correct_polygon.closed_loop():\n            closed_list.append(point)\n        self.assertEqual(closed_list[0], closed_list[len(closed_list) - 1])\n\n    def test_poly_bounds(self):\n        # create a lopsided PolyLine to test bounding box\n        loclist = [\n            Location(0, 0),\n            Location(400000000, 0),\n            Location(350000000, 300000000),\n            Location(450000000, 450000000),\n            Location(1000, 450000000)\n        ]\n        expected_rect = Rectangle(Location(0, 0), Location(450000000, 450000000))\n        computed_rect = PolyLine(loclist).bounds()\n        self.assertEqual(expected_rect, computed_rect)\n\n        # now test again but with a Polygon\n        loclist = [\n            Location(0, 0),\n            Location(400000000, 0),\n            Location(350000000, 300000000),\n            Location(450000000, 450000000),\n            Location(1000, 450000000)\n        ]\n        expected_rect = Rectangle(Location(0, 0), Location(450000000, 450000000))\n        computed_rect = Polygon(loclist).bounds()\n        self.assertEqual(expected_rect, computed_rect)\n\n    def test_poly_length(self):\n        loclist = [Location(0, 0), Location(5000000, 5000000)]\n        polyline = PolyLine(loclist, deep=True)\n        self.assertEqual(78626, int(polyline.length()))\n\n        loclist2 = [Location(0, 0), Location(20000000, 20000000)]\n        polyline = PolyLine(loclist2, deep=True)\n        self.assertEqual(314474, int(polyline.length()))\n\n    def test_fully_geometrically_encloses_location(self):\n        loclist = [\n            Location(0, 0),\n            Location(400000000, 0),\n            Location(350000000, 300000000),\n            Location(450000000, 450000000),\n            Location(1000, 450000000)\n        ]\n        polygon = Polygon(loclist)\n        point = Location(1200, 1500)\n        self.assertTrue(polygon.fully_geometrically_encloses_location(point))\n\n        point = Location(0, 0)\n        self.assertFalse(polygon.fully_geometrically_encloses_location(point))\n\n        point = Location(-34, -1)\n        self.assertFalse(polygon.fully_geometrically_encloses_location(point))\n\n    def test_overlaps_polyline(self):\n        loclist = [\n            Location(0, 0),\n            Location(400000000, 0),\n            Location(350000000, 300000000),\n            Location(450000000, 450000000),\n            Location(1000, 450000000)\n        ]\n        loclist2 = [Location(1, 1), Location(2000, 3000)]\n        polygon = Polygon(loclist)\n        polyline0 = PolyLine(loclist2)\n        self.assertTrue(polygon.overlaps_polyline(polyline0))\n\n    def test_intersects_polygon(self):\n        loclist = [\n            Location(0, 0),\n            Location(400000000, 0),\n            Location(350000000, 300000000),\n            Location(450000000, 450000000),\n            Location(1000, 450000000)\n        ]\n        loclist2 = [Location(1, 1), Location(10, 10), Location(2000, 3000)]\n        polygon = Polygon(loclist)\n        polygon2 = Polygon(loclist2)\n        self.assertTrue(polygon.intersects(polygon2))\n\n    def test_intersects_polyline(self):\n        loclist = [\n            Location(-3, -3),\n            Location(-2, -2),\n            Location(-1, -1),\n            Location(0, 0),\n            Location(1, 1),\n            Location(2, 2),\n            Location(3, 3)\n        ]\n        loclist2 = [\n            Location(-3, 3),\n            Location(-2, 2),\n            Location(-1, 1),\n            Location(0, 0),\n            Location(-1, 1),\n            Location(-2, 2),\n            Location(-3, 3)\n        ]\n        polyline = PolyLine(loclist)\n        polyline2 = PolyLine(loclist2)\n        self.assertTrue(polyline.intersects_polyline(polyline2))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "pyatlas/unit_tests/test_rectangle.py",
    "content": "import unittest\n\nfrom pyatlas.atlas import Atlas\nfrom pyatlas import geometry\nfrom pyatlas.geometry import Location, Rectangle\n\n\nclass RectangleTest(unittest.TestCase):\n    def setUp(self):\n        pass\n\n    def test_rectangle_construction(self):\n        rect = Rectangle(Location(0, 0), Location(450000000, 450000000))\n        loop = []\n        for point in rect.closed_loop():\n            loop.append(point)\n        # first and last points should be the same\n        self.assertEqual(loop[0], loop[len(loop) - 1])\n        # test consistency\n        self.assertEqual(loop[0], Location(0, 0))\n        self.assertEqual(loop[1], Location(450000000, 0))\n        self.assertEqual(loop[2], Location(450000000, 450000000))\n        self.assertEqual(loop[3], Location(0, 450000000))\n        self.assertEqual(loop[4], Location(0, 0))\n\n    def test_location_bounding_calculation(self):\n        loclist = [\n            Location(0, 0),\n            Location(450000000, 0),\n            Location(450000000, 450000000),\n            Location(0, 450000000)\n        ]\n        expected_rect = Rectangle(Location(0, 0), Location(450000000, 450000000))\n        computed_rect = geometry.bounds_locations(loclist)\n        self.assertEqual(expected_rect, computed_rect)\n\n        # create a lopsided polygon to test bounding box\n        loclist = [\n            Location(0, 0),\n            Location(400000000, 0),\n            Location(350000000, 300000000),\n            Location(450000000, 450000000),\n            Location(1000, 450000000)\n        ]\n        expected_rect = Rectangle(Location(0, 0), Location(450000000, 450000000))\n        computed_rect = geometry.bounds_locations(loclist)\n        self.assertEqual(expected_rect, computed_rect)\n\n    def test_entity_bounding_calculation_on_relations(self):\n        atlas = Atlas(\"resources/test.atlas\")\n\n        relation = atlas.relation(1)\n        expected_rect = Rectangle(\n            Location(390000000, -1190300000), Location(390500000, -1180000000))\n        computed_rect = geometry.bounds_atlasentities([relation])\n        self.assertEqual(expected_rect, computed_rect)\n\n        relation = atlas.relation(2)\n        expected_rect = Rectangle(\n            Location(380000000, -1180100000), Location(380100000, -1180000000))\n        computed_rect = geometry.bounds_atlasentities([relation])\n        self.assertEqual(expected_rect, computed_rect)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "pyatlas/unit_tests/test_spatial_index.py",
    "content": "import unittest\n\nfrom pyatlas import geometry\nfrom pyatlas.geometry import Rectangle\nfrom pyatlas.atlas import Atlas\nfrom pyatlas.spatial_index import SpatialIndex\nfrom pyatlas.spatial_index import _RTree\nfrom pyatlas.atlas_entities import EntityType\n\n\nclass SpatialIndexTest(unittest.TestCase):\n    def setUp(self):\n        pass\n\n    def test_rtree(self):\n        # The bounding box defined in this test should only encompass\n        # test Points 1, 2, and 3 from the test atlas\n\n        atlas = Atlas(\"resources/test.atlas\")\n        tree = _RTree(atlas.points())\n\n        lower_left = geometry.location_with_degrees(37, -118.02)\n        upper_right = geometry.location_with_degrees(39, -118)\n\n        test_results = []\n        for element in tree.get(Rectangle(lower_left, upper_right)):\n            test_results.append(element)\n\n        self.assertEqual({1, 2, 3}, set(test_results))\n\n    def test_basic_spatial_index_operations(self):\n        atlas = Atlas(\"resources/test.atlas\")\n\n        index = SpatialIndex(atlas, EntityType.POINT, atlas.points())\n        index.initialize_rtree()\n\n        lower_left = geometry.location_with_degrees(37, -118.02)\n        upper_right = geometry.location_with_degrees(39, -118)\n\n        test_results = index.get(Rectangle(lower_left, upper_right))\n        self.assertEqual({atlas.point(2), atlas.point(3), atlas.point(1)}, test_results)\n\n        test_results = index.get(\n            Rectangle(lower_left, upper_right), lambda p: p.get_identifier() == 2)\n        self.assertEqual({atlas.point(2)}, test_results)\n"
  },
  {
    "path": "pyatlas/venv.sh",
    "content": "#!/usr/bin/env bash\n\n# general case script abort if a command fails\n# this can be overridden with a custom error message using '|| err_shutdown'\nset -e\nset -o pipefail\n\n### define utility functions ###\n################################\nerr_shutdown() {\n    echo \"venv.sh: ERROR: $1\"\n    deactivate\n    exit 1\n}\n\n\n### check to prevent users from running this script directly ###\n################################################################\nif [ \"$1\" != \"ranFromGradle\" ];\nthen\n    err_shutdown \"this script should be run using the atlas gradle task 'formatPyatlas'\"\nfi\n\n\n### set up variables to store directory names ###\n#################################################\ngradle_project_root_dir=\"$(pwd)\"\npyatlas_dir=\"pyatlas\"\npyatlas_srcdir=\"pyatlas\"\npyatlas_root_dir=\"$gradle_project_root_dir/$pyatlas_dir\"\nvenv_path=\"$pyatlas_root_dir/__pyatlas_venv__\"\n\n\n### abort the script if the pyatlas source folder is not present ###\n####################################################################\nif [ ! -d \"$pyatlas_root_dir/$pyatlas_srcdir\" ];\nthen\n    err_shutdown \"pyatlas source folder not found\"\nfi\n\n\n### exit the script if the venv folder already exists ###\n#########################################################\nif [ -d \"$venv_path\" ];\nthen\n    echo \"INFO: $venv_path exists. Proceeding. Run './gradlew cleanPyatlas' to refresh.\"\n    exit 0\nfi\n\n\n### determine if virtualenv is installed ###\n############################################\nif command -v virtualenv;\nthen\n    virtualenv_command=\"$(command -v virtualenv)\"\nelse\n    err_shutdown \"'command -v virtualenv' returned non-zero exit status\"\nfi\n\n\n### set up the virtual environment ###\n######################################\necho \"Setting up pyatlas venv...\"\nvenv_path=\"$pyatlas_root_dir/__pyatlas_venv__\"\nif ! ${virtualenv_command} --python=python3 \"$venv_path\";\nthen\n    err_shutdown \"virtualenv command returned non-zero exit status\"\nfi\n"
  },
  {
    "path": "pyatlas/yapf_format.py",
    "content": "import sys\nimport os\nfrom yapf.yapflib.yapf_api import FormatFile\nfrom yapf.yapflib.yapf_api import FormatCode\n\n# NOTE: If a source file does not end with a newline, the formatter will\n# complain, but fail to actually fix the issue. To resolve, manually\n# insert a newline at the end of the file.\n\ndef main(argv):\n    srcdir = argv[1]\n    mode = argv[2]\n    violation_detected = False\n    for file_to_style in os.listdir(srcdir):\n\n        if file_to_style.endswith('.py'):\n            filepath = os.path.join(srcdir, file_to_style)\n\n            if mode == \"CHECK\":\n                if detect_formatting_violation(filepath):\n                    print(str(argv[0]) + \": ERROR: formatting violation detected in \" + str(file_to_style))\n                    violation_detected = True\n\n            elif mode == \"APPLY\":\n                if detect_formatting_violation(filepath):\n                    print(str(file_to_style) + \": found issue, reformatting...\")\n                    FormatFile(filepath, in_place=True, style_config='style.yapf')\n                    violation_detected = True\n\n            else:\n                print(\"ERROR: invalid mode \" + str(mode))\n                exit(1)\n\n    if mode == 'CHECK' and violation_detected:\n        exit(1)\n    elif not violation_detected:\n        print(str(argv[0]) + \" INFO: all formatting for targets in \" + str(argv[1]) + \" OK!\")\n\ndef detect_formatting_violation(filepath):\n    original = read_file_contents(filepath)\n    reformatted = FormatFile(filepath, style_config='style.yapf')\n    if original != reformatted[0]:\n        print(FormatCode(original, filename=filepath, print_diff=True, style_config='style.yapf')[0])\n        return True\n    return False\n\n\ndef read_file_contents(filepath):\n    with open(filepath, 'r') as srcfile:\n        filecontents = srcfile.read()\n    return filecontents\n\nif __name__ == \"__main__\":\n    if len(sys.argv) < 3:\n        print(\"usage: \" + str(sys.argv[0]) + \" <srcpath> <mode>\")\n        exit(1)\n    main(sys.argv)\n"
  },
  {
    "path": "sample/Readme.md",
    "content": "# Sample Project\n\n- Copy the `sample` project folder locally, and open it as a Gradle project in your favorite IDE.\n- Grab one or more Atlas files from the links at the top of [that](/src/main/java/org/openstreetmap/atlas/geography/atlas#using-atlas) readme\n- Open the [`Sample.java`](src/main/java/org/openstreetmap/atlas/sample/Sample.java) file, and take a look at what it is doing.\n- Run the `Sample` class with the following switch:\n\n```\n-atlas=/path/to/some.atlas\n```\n"
  },
  {
    "path": "sample/build.gradle",
    "content": "plugins {\n    id 'java'\n}\n\nrepositories\n{\n    // For geotools\n    maven { url \"http://repo.osgeo.org/repository/release/\" }\n    mavenCentral()\n}\n\ndependencies\n{\n    compile 'org.slf4j:slf4j-api:1.7.12'\n    compile 'org.slf4j:slf4j-log4j12:1.7.12'\n    compile 'log4j:log4j:1.2.17'\n    compile 'org.openstreetmap.atlas:atlas:6.3.0'\n}\n"
  },
  {
    "path": "sample/settings.gradle",
    "content": "rootProject.name = 'atlas-sample'\n"
  },
  {
    "path": "sample/src/main/java/org/openstreetmap/atlas/sample/Sample.java",
    "content": "package org.openstreetmap.atlas.sample;\n\nimport java.nio.file.Path;\nimport java.util.Comparator;\nimport java.util.Random;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class Sample extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(Sample.class);\n    private static final int VALENCE_CUTOFF = 4;\n    private static final int PRINT_CUTOFF = 10;\n\n    private static final Switch<Resource> ATLAS = new Switch<>(\"atlas\",\n            \"Path to the Atlas file to load\", value -> new File(Path.of(value)),\n            Optionality.REQUIRED);\n    private static final Switch<Sharding> SHARDING = new Switch<>(\"sharding\",\n            \"Sharding tree to load\", Sharding::forString, Optionality.OPTIONAL, \"geohash@8\");\n\n    public static void main(final String[] args)\n    {\n        new Sample().runWithoutQuitting(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        // Get resource from command\n        final Resource atlasResource = (Resource) command.get(ATLAS);\n        logger.info(\"\");\n        logger.info(\"Loading {}\", atlasResource);\n        // Load Atlas object in memory\n        final Time start = Time.now();\n        final Atlas atlas = new AtlasResourceLoader().load(atlasResource);\n        // Print Atlas summary\n        logger.info(\"\");\n        logger.info(\"Loaded Atlas in {}: {}\", start.elapsedSince(), atlas);\n\n        // Count the number of dual carriageway Edges:\n        final long numberOfReverseEdges = Iterables.size(atlas.edges(Edge::hasReverseEdge));\n        logger.info(\"\");\n        logger.info(\"The Atlas has {} dual carriageway edges.\", numberOfReverseEdges);\n\n        // Count the number of Nodes with valence >= VALENCE_CUTOFF (At least VALENCE_CUTOFF edges\n        // connected), and print the most southern ones\n        final Iterable<Node> valencePlusNodes = atlas\n                .nodes(node -> node.valence() >= VALENCE_CUTOFF);\n        final long numberOfValencePlusNodes = Iterables.size(valencePlusNodes);\n        logger.info(\"\");\n        logger.info(\"The Atlas has {} nodes with valence greater than or equal to {}.\",\n                numberOfValencePlusNodes, VALENCE_CUTOFF);\n        final Comparator<Node> latitudeComparator = (node1, node2) ->\n        {\n            final Latitude latitude1 = node1.getLocation().getLatitude();\n            final Latitude latitude2 = node2.getLocation().getLatitude();\n            return Double.compare(latitude1.asDegrees(), latitude2.asDegrees());\n        };\n        final SortedSet<Node> sortedValencePlusNodes = new TreeSet<>(latitudeComparator);\n        valencePlusNodes.forEach(sortedValencePlusNodes::add);\n        logger.info(\"\");\n        logger.info(\"{} southernmost nodes with valence greater than or equal to {}:\", PRINT_CUTOFF,\n                VALENCE_CUTOFF);\n        sortedValencePlusNodes.stream().limit(PRINT_CUTOFF)\n                .forEach(node -> logger.info(\"{}\", node));\n\n        // Take a random edge and see what sharding box it intersects\n        final Sharding sharding = (Sharding) command.get(SHARDING);\n        final Edge randomEdge = Iterables.stream(atlas.edges()).collectToList()\n                .get(new Random().nextInt((int) atlas.numberOfEdges()));\n        final Iterable<Shard> shards = sharding.shardsIntersecting(randomEdge.asPolyLine());\n        logger.info(\"\");\n        logger.info(\"Shards intersecting Edge {}:\", randomEdge.getIdentifier());\n        shards.forEach(shard -> logger.info(\"{} with shape \\\"{}\\\"\", shard, shard.toWkt()));\n\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(ATLAS, SHARDING);\n    }\n}\n"
  },
  {
    "path": "sample/src/main/resources/log4j.properties",
    "content": "log4j.rootLogger=INFO, stdout\n\n# Direct log messages to stdout\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] %c{1}:%L - %m%n\n"
  },
  {
    "path": "scripts/log4j-atlas/log4j.properties",
    "content": "log4j.rootLogger=ERROR, stdout\n\n# Direct log messages to stdout\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n\n"
  },
  {
    "path": "settings.gradle",
    "content": "rootProject.name = 'atlas'\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/PolygonPerformanceTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.List;\nimport java.util.Random;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.Assert;\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link Polygon #fullyGeometricallyEncloses(Location)} performance test.\n *\n * @author mkalender\n */\npublic class PolygonPerformanceTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(PolygonPerformanceTest.class);\n    private static final int size = 5_000;\n    private static final Random random = new Random();\n\n    private static void testCoverPerformanceHelper(final int size, final Rectangle polygonBounds,\n            final Function<Polygon, Location> locationRetrievalFunction,\n            final Function<Boolean, Boolean> extraAssertFunction) throws Exception\n    {\n        final int iteration = 1_000;\n        final int polygonPointBound = 100;\n        final int polygonMinPointCount = 3;\n        final List<Polygon> randomPolygons = Stream\n                .generate(() -> Polygon.random(\n                        random.nextInt(polygonPointBound) + polygonMinPointCount, polygonBounds))\n                .limit(size).collect(Collectors.toList());\n\n        double overallTotalTimeWithBoundCheck = 0;\n        double overallTotalTimeWithoutBoundCheck = 0;\n        for (int i = 0; i < iteration; i++)\n        {\n            double totalTimeWithBoundCheck = 0;\n            final double totalTimeWithoutBoundCheck = 0;\n            for (int j = 0; j < size; j++)\n            {\n                final Polygon polygon = randomPolygons.get(j);\n\n                // Use location retrieval method, but send a copy of Polygon\n                final Location location = locationRetrievalFunction\n                        .apply(new Polygon(polygon.getPoints()));\n\n                // Do enclosure check\n                final Time timer = Time.now();\n                final boolean resultWithBoundCheck = polygon.fullyGeometricallyEncloses(location);\n                final Duration duration = timer.elapsedSince();\n                totalTimeWithBoundCheck += duration.asMilliseconds();\n\n                // Do extra checks\n                Assert.assertTrue(extraAssertFunction.apply(resultWithBoundCheck));\n            }\n\n            overallTotalTimeWithBoundCheck += totalTimeWithBoundCheck;\n            overallTotalTimeWithoutBoundCheck += totalTimeWithoutBoundCheck;\n        }\n\n        logger.info(\n                \"Overall time with bound check: {} ms, without bound check: {} ms (iteration: {}) \\n\",\n                overallTotalTimeWithBoundCheck, overallTotalTimeWithoutBoundCheck, iteration);\n    }\n\n    @Ignore\n    @Test\n    public void testPerformance() throws Exception\n    {\n        // Rectangles to test\n        final Rectangle waState = Rectangle.forCorners(Location.forString(\"45.53714, -123.70605\"),\n                Location.forString(\"48.93693, -117.13623\"));\n        final Rectangle waStateNorthBorder = Rectangle.forCorners(\n                Location.forString(\"45.53714, -123.70605\"),\n                Location.forString(\"45.53814, -117.13623\"));\n        final Rectangle coState = Rectangle.forCorners(Location.forString(\"36.87962, -108.7207\"),\n                Location.forString(\"40.6473, -102.30469\"));\n\n        // Case 1\n        logger.info(\"Testing locations completely outside of the polygon's bounding box\");\n        logger.info(\"Size: {}, polygon bounds: {}, location bounds: {}\", size, waState, coState);\n        testCoverPerformanceHelper(size, waState, polygon -> Location.random(coState),\n                result -> !result);\n\n        // Case 2\n        logger.info(\"Testing locations on the bounding box border\");\n        logger.info(\"Size: {}, polygon bounds: {}, location bounds: {}\", size, waState,\n                waStateNorthBorder);\n        testCoverPerformanceHelper(size, waState, polygon -> Location.random(waStateNorthBorder),\n                result -> true);\n\n        // Case 3\n        logger.info(\"Testing locations within the bounding box but outside of the polygon\");\n        logger.info(\"Size: {}, polygon bounds: {}, location bounds: {}\", size, waState, waState);\n        testCoverPerformanceHelper(size, waState, polygon ->\n        {\n            // Find a location that is in the same bounding box, but outside polygon\n            Location randomLocation;\n            do\n            {\n                randomLocation = Location.random(waState);\n            }\n            while (polygon.fullyGeometricallyEncloses(randomLocation));\n\n            return randomLocation;\n        }, result -> !result);\n\n        // Case 4\n        logger.info(\"Testing locations point on the polygon\");\n        logger.info(\"Size: {}, polygon bounds: {}\", size, waState);\n        testCoverPerformanceHelper(size, waState, polygon ->\n        {\n            final List<Location> polygonPoints = polygon.getPoints();\n            final int aIndex = random.nextInt(polygonPoints.size());\n            return polygonPoints.get(aIndex);\n        }, result -> true);\n\n        // Case 5\n        logger.info(\"Testing locations inside the polygon\");\n        logger.info(\"Size: {}, polygon bounds: {}\", size, waState);\n        testCoverPerformanceHelper(size, waState, polygon ->\n        {\n            // Find a location that is inside the polygon\n            Location randomLocation;\n            do\n            {\n                randomLocation = Location.random(waState);\n            }\n            while (!polygon.fullyGeometricallyEncloses(randomLocation));\n\n            return randomLocation;\n        }, result -> result);\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/AtlasIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.io.InputStream;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.OsmPbfIngestIntegrationTest;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author tony\n */\npublic class AtlasIntegrationTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasIntegrationTest.class);\n\n    public static Atlas loadCuba()\n    {\n        final Time start = Time.now();\n        final Supplier<InputStream> supplier = () -> AtlasIntegrationTest.class\n                .getResourceAsStream(\"CUB_7-37-56.txt.gz\");\n        final InputStreamResource resource = new InputStreamResource(supplier);\n        resource.setDecompressor(Decompressor.GZIP);\n        final Atlas result = new TextAtlasBuilder().read(resource);\n        logger.info(\"Loaded a Cuba slice in {}\", start.elapsedSince());\n        return result;\n    }\n\n    protected Atlas loadBahamas(final Polygon polygon)\n    {\n        final String path = OsmPbfIngestIntegrationTest.class.getResource(\"BHS-6-18-27.pbf\")\n                .getPath();\n        final AtlasLoadingOption loadingOption = AtlasLoadingOption.createOptionWithOnlySectioning()\n                .setLoadWaysSpanningCountryBoundaries(false);\n        final Atlas atlas = new RawAtlasGenerator(new File(path), loadingOption,\n                MultiPolygon.forPolygon(polygon)).build();\n        return new AtlasSectionProcessor(atlas, loadingOption).run();\n    }\n\n    protected Atlas loadBelizeRaw(final Polygon polygon,\n            final AtlasLoadingOption atlasLoadingOption)\n    {\n        final String path = OsmPbfIngestIntegrationTest.class\n                .getResource(\"BLZ_raw_08242015.osm.pbf\").getPath();\n        Atlas atlas = new RawAtlasGenerator(new File(path), atlasLoadingOption,\n                MultiPolygon.forPolygon(polygon)).build();\n        if (atlasLoadingOption.isCountrySlicing())\n        {\n            atlas = new RawAtlasSlicer(atlasLoadingOption, atlas).slice();\n        }\n        if (atlasLoadingOption.isWaySectioning())\n        {\n            atlas = new AtlasSectionProcessor(atlas, atlasLoadingOption).run();\n        }\n        return atlas;\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/SubAtlasIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\n\n/**\n * @author matthieun\n */\npublic class SubAtlasIntegrationTest extends AtlasIntegrationTest\n{\n    @Test\n    public void testSubCuba()\n    {\n        final Atlas cuba = loadCuba();\n        final Atlas sub = cuba\n                .subAtlas(Rectangle.forCorners(Location.forString(\"20.049468, -74.368043\"),\n                        Location.forString(\"20.382402, -74.077814\")), AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n        Assert.assertEquals(523, sub.metaData().getSize().getNodeNumber());\n        Assert.assertEquals(1344, sub.metaData().getSize().getEdgeNumber());\n        Assert.assertEquals(223, sub.metaData().getSize().getAreaNumber());\n        Assert.assertEquals(18, sub.metaData().getSize().getLineNumber());\n        Assert.assertEquals(28, sub.metaData().getSize().getPointNumber());\n        Assert.assertEquals(2, sub.metaData().getSize().getRelationNumber());\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/builder/text/TextAtlasBuilderIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.text;\n\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasIntegrationTest;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class TextAtlasBuilderIntegrationTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(TextAtlasBuilderIntegrationTest.class);\n\n    @Test\n    public void testLoad()\n    {\n        final Atlas atlas = AtlasIntegrationTest.loadCuba();\n        logger.info(\"{}\", atlas.metaData());\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaGeoJsonIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport org.hamcrest.CoreMatchers;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\n/**\n * @author hallahan\n */\npublic class AtlasDeltaGeoJsonIntegrationTest\n{\n    private Atlas before;\n    private Atlas after;\n    private AtlasDelta delta;\n\n    /**\n     * Tries parsing the GeoJSON string. We then check a few things about it, such as if it has the\n     * applicable diff properties. Also, we count the number of features.\n     */\n    @Test\n    public void parseGeoJson()\n    {\n        final String geoJsonStr = this.delta.toGeoJson();\n\n        final JsonObject geoJson = new JsonParser().parse(geoJsonStr).getAsJsonObject();\n        final JsonArray features = geoJson.getAsJsonArray(\"features\");\n\n        int idx = 0;\n        for (; idx < features.size(); ++idx)\n        {\n            final JsonObject feature = features.get(idx).getAsJsonObject();\n            final JsonObject properties = feature.getAsJsonObject(\"properties\");\n\n            final JsonElement diffVal = properties.get(\"diff\");\n            Assert.assertNotNull(diffVal);\n            final JsonElement diffReasonVal = properties.get(\"diff:reason\");\n            Assert.assertNotNull(diffReasonVal);\n            final JsonElement diffTypeVal = properties.get(\"diff:type\");\n            Assert.assertNotNull(diffTypeVal);\n\n            // a diff property should be before or after.\n            Assert.assertThat(diffVal.getAsString(),\n                    CoreMatchers.anyOf(CoreMatchers.is(\"BEFORE\"), CoreMatchers.is(\"AFTER\")));\n\n            // Make sure we have a reason and a type.\n            Assert.assertTrue(diffReasonVal.getAsString().length() > 0);\n            Assert.assertTrue(diffTypeVal.getAsString().length() > 0);\n        }\n\n        Assert.assertEquals(47646, idx);\n    }\n\n    @Before\n    public void readAtlases()\n    {\n        this.before = new TextAtlasBuilder()\n                .read(new InputStreamResource(() -> AtlasDeltaIntegrationTest.class\n                        .getResourceAsStream(\"DMA_9-168-233-base.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP).withName(\"DMA_9-168-233-base.txt.gz\"));\n        this.after = new TextAtlasBuilder()\n                .read(new InputStreamResource(() -> AtlasDeltaIntegrationTest.class\n                        .getResourceAsStream(\"DMA_9-168-233-alter.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP)\n                        .withName(\"DMA_9-168-233-alter.txt.gz\"));\n        this.delta = new AtlasDelta(this.before, this.after, false).generate();\n    }\n\n    /**\n     * This is a basic test that should start failing if you change what the delta GeoJSON looks\n     * like.\n     */\n    @Test\n    public void testGeoJson()\n    {\n        final String geoJson = this.delta.toGeoJson();\n        Assert.assertEquals(22424431, geoJson.length());\n    }\n\n    @Test\n    public void testRelationsGeoJson()\n    {\n        final String relationsGeoJson = this.delta.toRelationsGeoJson();\n        Assert.assertEquals(454484, relationsGeoJson.length());\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaIntegrationTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaIntegrationTest.class);\n\n    @Test\n    public void testDiff()\n    {\n        final Atlas before = new TextAtlasBuilder()\n                .read(new InputStreamResource(() -> AtlasDeltaIntegrationTest.class\n                        .getResourceAsStream(\"DMA_9-168-233-base.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP).withName(\"DMA_9-168-233-base.txt.gz\"));\n        final Atlas after = new TextAtlasBuilder()\n                .read(new InputStreamResource(() -> AtlasDeltaIntegrationTest.class\n                        .getResourceAsStream(\"DMA_9-168-233-alter.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP)\n                        .withName(\"DMA_9-168-233-alter.txt.gz\"));\n        final AtlasDelta delta = new AtlasDelta(before, after, true).generate();\n        final SortedSet<Diff> differences = delta.getDifferences();\n        final long size = differences.size();\n        final long sizeNodes = differences.stream()\n                .filter(diff -> diff.getItemType() == ItemType.NODE).count();\n        final long sizeEdges = differences.stream()\n                .filter(diff -> diff.getItemType() == ItemType.EDGE).count();\n        final long sizeAreas = differences.stream()\n                .filter(diff -> diff.getItemType() == ItemType.AREA).count();\n        final long sizeLines = differences.stream()\n                .filter(diff -> diff.getItemType() == ItemType.LINE).count();\n        final long sizePoints = differences.stream()\n                .filter(diff -> diff.getItemType() == ItemType.POINT).count();\n        final long sizeRelations = differences.stream()\n                .filter(diff -> diff.getItemType() == ItemType.RELATION).count();\n        logger.info(\"Size of the Delta: {}\", size);\n        logger.info(\"Size of the Delta Nodes: {}\", sizeNodes);\n        logger.info(\"Size of the Delta Edges: {}\", sizeEdges);\n        logger.info(\"Size of the Delta Areas: {}\", sizeAreas);\n        logger.info(\"Size of the Delta Lines: {}\", sizeLines);\n        logger.info(\"Size of the Delta Points: {}\", sizePoints);\n        logger.info(\"Size of the Delta Relations: {}\", sizeRelations);\n        Assert.assertEquals(33475, size);\n        Assert.assertEquals(3519, sizeNodes);\n        Assert.assertEquals(16239, sizeEdges);\n        Assert.assertEquals(13285, sizeAreas);\n        Assert.assertEquals(94, sizeLines);\n        Assert.assertEquals(324, sizePoints);\n        Assert.assertEquals(14, sizeRelations);\n        Assert.assertEquals(size,\n                sizeNodes + sizeEdges + sizeAreas + sizeLines + sizePoints + sizeRelations);\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasIntegrationTest\n{\n    private static final DynamicAtlasPolicy POLICY = new DynamicAtlasPolicy(shard ->\n    {\n        final String fileName = \"DMA_\" + shard.getName() + \".atlas.txt.gz\";\n        return Optional.of(new TextAtlasBuilder().read(new InputStreamResource(\n                () -> DynamicAtlasIntegrationTest.class.getResourceAsStream(fileName))\n                .withName(fileName).withDecompressor(Decompressor.GZIP)));\n    }, new SlippyTileSharding(9), SlippyTile.forName(\"9-168-234\"), Rectangle.MAXIMUM);\n\n    @Test\n    public void testAreas()\n    {\n        final Atlas atlas = new DynamicAtlas(POLICY);\n        Assert.assertEquals(39672, Iterables.size(atlas.areas()));\n    }\n\n    @Test\n    public void testEdges()\n    {\n        final Atlas atlas = new DynamicAtlas(POLICY);\n        Assert.assertEquals(18936, Iterables.size(atlas.edges()));\n    }\n\n    @Test\n    public void testLines()\n    {\n        final Atlas atlas = new DynamicAtlas(POLICY);\n        Assert.assertEquals(1572, Iterables.size(atlas.lines()));\n    }\n\n    @Test\n    public void testNodes()\n    {\n        final Atlas atlas = new DynamicAtlas(POLICY);\n        Assert.assertEquals(8615, Iterables.size(atlas.nodes()));\n    }\n\n    @Test\n    public void testPoints()\n    {\n        final Atlas atlas = new DynamicAtlas(POLICY);\n        Assert.assertEquals(348, Iterables.size(atlas.points()));\n    }\n\n    @Test\n    public void testRelations()\n    {\n        final Atlas atlas = new DynamicAtlas(POLICY);\n        Assert.assertEquals(2, Iterables.size(atlas.relations()));\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/items/AtlasEntityTypeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasIntegrationTest;\n\n/**\n * Tests Atlas's type retrieval\n *\n * @author mkalender\n */\npublic class AtlasEntityTypeTest extends AtlasIntegrationTest\n{\n\n    @Test\n    public void testTypeRetrieval()\n    {\n        final Atlas atlas = loadCuba();\n        for (final AtlasEntity entity : atlas.entities(entity -> true))\n        {\n            if (entity instanceof Node)\n            {\n                Assert.assertEquals(((Node) entity).getType(), ItemType.NODE);\n            }\n            else if (entity instanceof Edge)\n            {\n                Assert.assertEquals(((Edge) entity).getType(), ItemType.EDGE);\n            }\n            else if (entity instanceof Area)\n            {\n                Assert.assertEquals(((Area) entity).getType(), ItemType.AREA);\n            }\n            else if (entity instanceof Line)\n            {\n                Assert.assertEquals(((Line) entity).getType(), ItemType.LINE);\n            }\n            else if (entity instanceof Point)\n            {\n                Assert.assertEquals(((Point) entity).getType(), ItemType.POINT);\n            }\n            else if (entity instanceof Relation)\n            {\n                Assert.assertEquals(((Relation) entity).getType(), ItemType.RELATION);\n            }\n            else\n            {\n                Assert.fail(\"Unknown type\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/items/complex/boundaries/ComplexBoundariesIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.boundaries;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class ComplexBoundariesIntegrationTest\n{\n    @Rule\n    public final ComplexBoundaryIntegrationTestRule rule = new ComplexBoundaryIntegrationTestRule();\n\n    @Test\n    public void testComplexBoundary()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final List<ComplexBoundary> badEntities = new ArrayList<>();\n        final ComplexBoundaryFinder finder = new ComplexBoundaryFinder();\n        final List<ComplexBoundary> result = Iterables.stream(finder.find(atlas, badEntities::add))\n                .collectToList();\n        Assert.assertEquals(710, result.size());\n\n        final List<ComplexBoundary> withCountry = Iterables.stream(result)\n                .filter(boundary -> Iterables.size(boundary.getCountries()) > 0).collectToList();\n        Assert.assertEquals(2, withCountry.size());\n\n        Assert.assertEquals(139, badEntities.size());\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/items/complex/boundaries/ComplexBoundaryIntegrationTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.boundaries;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class ComplexBoundaryIntegrationTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"HTI-DOM-Boundaries.atlas.txt.gz\")\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlasIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasIntegrationTest;\nimport org.openstreetmap.atlas.geography.atlas.packed.RandomPackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class MultiAtlasIntegrationTest extends AtlasIntegrationTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(MultiAtlasIntegrationTest.class);\n\n    private MultiAtlas multi;\n\n    public static void main(final String[] args)\n    {\n        new MultiAtlasIntegrationTest().loadTest(Boolean.valueOf(args[0]), Integer.valueOf(args[1]),\n                Long.valueOf(args[2]));\n    }\n\n    public MultiAtlas largeMultiAtlas(final int count, final long eachSize)\n    {\n        final List<Atlas> atlases = new ArrayList<>();\n        for (int i = 0; i < count; i++)\n        {\n            final long startIdentifier = i > 0 ? i * eachSize - eachSize / 100 : 0;\n            final long size = i > 0 ? eachSize + eachSize / 100 : eachSize;\n            logger.info(\"Generating sub-atlas with identifiers starting at {} and ending at {}\",\n                    startIdentifier, startIdentifier + size - 1);\n            atlases.add(RandomPackedAtlasBuilder.generate(size, startIdentifier));\n        }\n        return new MultiAtlas(atlases, false);\n    }\n\n    public void loadTest(final boolean overWrite, final int count, final long eachSize)\n    {\n        final File atlasFile = new File(\n                System.getProperty(\"user.home\") + \"/projects/data/unitTest/multiatlas.atlas\");\n        final MultiAtlas large;\n        Time start;\n        final Rectangle queryBounds = Location.TEST_5.boxAround(Distance.miles(1));\n        if (!overWrite && atlasFile.getFile().exists())\n        {\n            start = Time.now();\n            large = MultiAtlas.load(atlasFile);\n            logger.info(\"Finished Loading from atlas file {} in {}\", atlasFile,\n                    start.elapsedSince());\n        }\n        else\n        {\n            start = Time.now();\n            large = largeMultiAtlas(count, eachSize);\n            logger.info(\"Created MultiAtlas in {}\", start.elapsedSince());\n\n            start = Time.now();\n            large.save(atlasFile);\n            logger.info(\"Finished writing to multiatlas file {} in {}\", atlasFile,\n                    start.elapsedSince());\n        }\n\n        // Edges\n        start = Time.now();\n        final long edgesSize = Iterables.size(large.edgesIntersecting(queryBounds));\n        logger.info(\"Spatial queried and counted {} edges in {}\", edgesSize, start.elapsedSince());\n\n        start = Time.now();\n        final long edgesSize2 = Iterables.size(large.edges());\n        logger.info(\"Total: Counted {} edges in {}\", edgesSize2, start.elapsedSince());\n\n        // Nodes\n        start = Time.now();\n        final long nodesSize = Iterables.size(large.nodesWithin(queryBounds));\n        logger.info(\"Spatial queried and counted {} nodes in {}\", nodesSize, start.elapsedSince());\n\n        start = Time.now();\n        final long nodesSize2 = Iterables.size(large.nodes());\n        logger.info(\"Total: Counted {} nodes in {}\", nodesSize2, start.elapsedSince());\n    }\n\n    @Test\n    public void testPolygonRetrieval()\n    {\n        final Rectangle bound = new Location(Latitude.degrees(25.0288172),\n                Longitude.degrees(-77.5420233)).boxAround(Distance.miles(1));\n        final Atlas atlas1 = loadBahamas(bound);\n        final Rectangle overlapBound = new Location(Latitude.degrees(25.0213741),\n                Longitude.degrees(-77.5237397)).boxAround(Distance.miles(1));\n        final Atlas atlas2 = loadBahamas(overlapBound);\n        final Rectangle noOverlapBound = new Location(Latitude.degrees(24.3973491),\n                Longitude.degrees(-77.8862342)).boxAround(Distance.miles(1));\n        final Atlas atlas3 = loadBahamas(noOverlapBound);\n        this.multi = new MultiAtlas(atlas1, atlas2, atlas3);\n        final Polygon polygon = this.multi.area(24601488000000L).asPolygon();\n\n        Assert.assertEquals(40, Iterables.size(this.multi.edgesIntersecting(polygon)));\n        Assert.assertEquals(8, Iterables.size(this.multi.areasIntersecting(polygon)));\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlasIntegrationTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class MultiAtlasIntegrationTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"DEU_11-1084-708.atlas.txt.gz\")\n    private Atlas atlas1;\n    @TestAtlas(loadFromTextResource = \"DEU_11-1084-709.atlas.txt.gz\")\n    private Atlas atlas2;\n\n    public Atlas getAtlas1()\n    {\n        return this.atlas1;\n    }\n\n    public Atlas getAtlas2()\n    {\n        return this.atlas2;\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasClonerIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasIntegrationTest;\nimport org.openstreetmap.atlas.geography.atlas.delta.AtlasDelta;\n\n/**\n * @author matthieun\n */\npublic class PackedAtlasClonerIntegrationTest extends AtlasIntegrationTest\n{\n    @Test\n    public void cloneRealCountry()\n    {\n        cloneAndCompare(loadCuba());\n    }\n\n    @Test\n    public void cloneTest()\n    {\n        for (int i = 0; i < 10; i++)\n        {\n            final Atlas atlas = RandomPackedAtlasBuilder.generate(1000, 0);\n            cloneAndCompare(atlas);\n        }\n    }\n\n    private void cloneAndCompare(final Atlas atlas)\n    {\n        final PackedAtlasCloner cloner = new PackedAtlasCloner();\n        final Atlas copy = cloner.cloneFrom(atlas);\n        Assert.assertTrue(new AtlasDelta(atlas, copy).generate().getDifferences().isEmpty());\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasIntegrationTest;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class PackedAtlasIntegrationTest extends AtlasIntegrationTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(PackedAtlasIntegrationTest.class);\n\n    public static void main(final String[] args)\n    {\n        new PackedAtlasIntegrationTest().loadTest(false, 10000);\n    }\n\n    public void loadTest(final boolean overWrite, final long size)\n    {\n        final File atlasFile = new File(\n                System.getProperty(\"user.home\") + \"/projects/data/unitTest/atlas.atlas\");\n        final PackedAtlas large;\n        Time start = Time.now();\n        final Rectangle queryBoundsEdges = Location.TEST_5.boxAround(Distance.miles(1));\n        final Rectangle queryBoundsNodes = Location.TEST_5.boxAround(Distance.miles(10));\n        if (!overWrite && atlasFile.getFile().exists())\n        {\n            start = Time.now();\n            large = PackedAtlas.load(atlasFile);\n            logger.info(\"Finished Loading from atlas file {} in {}\", atlasFile,\n                    start.elapsedSince());\n        }\n        else\n        {\n            large = RandomPackedAtlasBuilder.generate(size, 1);\n            start = Time.now();\n            large.save(atlasFile);\n            logger.info(\"Finished writing to atlas file {} in {}\", atlasFile, start.elapsedSince());\n        }\n\n        // Queries\n        start = Time.now();\n        final long edgesSize = Iterables.size(large.edgesIntersecting(queryBoundsEdges));\n        logger.info(\"Spatial queried and counted {} edges in {}\", edgesSize, start.elapsedSince());\n\n        start = Time.now();\n        final long nodesSize = Iterables.size(large.nodesWithin(queryBoundsNodes));\n        logger.info(\"Spatial queried and counted {} nodes in {}\", nodesSize, start.elapsedSince());\n\n        start = Time.now();\n        final long nodes = Iterables.size(large.nodes());\n        logger.info(\"Total: Counted {} nodes in {}\", nodes, start.elapsedSince());\n\n        start = Time.now();\n        final long edgeShapePoints = Iterables.count(large.edgesIntersecting(queryBoundsEdges),\n                edge -> (long) edge.asPolyLine().size());\n        logger.info(\"Spatial queried and counted {} edge shape points in {}\", edgeShapePoints,\n                start.elapsedSince());\n\n        start = Time.now();\n        final long edgesSize2 = Iterables.size(large.edges());\n        logger.info(\"Total: Counted {} edges in {}\", edgesSize2, start.elapsedSince());\n\n        start = Time.now();\n        final long relationsSize = Iterables.size(large.relations());\n        logger.info(\"Total: Counted {} relations in {}\", relationsSize, start.elapsedSince());\n    }\n\n    @Test\n    public void testPolygonRetrieval()\n    {\n        final Rectangle largerBound = new Location(Latitude.degrees(25.0288172),\n                Longitude.degrees(-77.5420233)).boxAround(Distance.miles(1));\n        final Atlas atlas = loadBahamas(largerBound);\n\n        final Polygon polygon = atlas.area(24601488000000L).asPolygon();\n\n        Assert.assertEquals(40, Iterables.size(atlas.edgesIntersecting(polygon)));\n        Assert.assertEquals(8, Iterables.size(atlas.areasIntersecting(polygon)));\n    }\n\n    @Test\n    public void testSerialization()\n    {\n        // Raw\n        final ByteArrayResource resource = new ByteArrayResource()\n                .withName(\"testSerializationByteArray\");\n        final Atlas atlas = RandomPackedAtlasBuilder.generate(1000, 0);\n        atlas.save(resource);\n        final PackedAtlas deserialized = PackedAtlas.load(resource);\n        Assert.assertTrue(atlas.equals(deserialized));\n\n        // With compression\n        final ByteArrayResource compressedResource = new ByteArrayResource()\n                .withName(\"testSerializationByteArrayCompressed\");\n        compressedResource.setCompressor(Compressor.GZIP);\n        compressedResource.setDecompressor(Decompressor.GZIP);\n        atlas.save(compressedResource);\n        final PackedAtlas compressedeserialized = PackedAtlas.load(compressedResource);\n        Assert.assertTrue(atlas.equals(compressedeserialized));\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/pbf/OsmPbfIngestIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasIntegrationTest;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.clipping.Clip.ClipType;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.tags.BuildingTag;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.LastEditTimeTag;\nimport org.openstreetmap.atlas.tags.LastEditUserIdentifierTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * @author tony\n */\npublic class OsmPbfIngestIntegrationTest extends AtlasIntegrationTest\n{\n    @Test\n    public void testAreaAndLine()\n    {\n        final Rectangle bound = Location.forString(\"25.0771736, -77.3597574\")\n                .boxAround(Distance.meters(300));\n        final Atlas atlas = loadBahamas(bound);\n\n        Assert.assertEquals(28, atlas.numberOfAreas());\n        Assert.assertEquals(98, atlas.numberOfLines());\n\n        final Area smallArea = atlas.area(522592143000000L);\n        Assert.assertEquals(true, BuildingTag.isBuilding(smallArea));\n        Assert.assertEquals(4, Iterables.size(smallArea));\n        Assert.assertNotNull(smallArea.tag(LastEditTimeTag.KEY));\n        Assert.assertNotNull(smallArea.tag(LastEditUserIdentifierTag.KEY));\n\n        final Area bigArea = atlas.area(191814889000000L);\n        Assert.assertEquals(false, BuildingTag.isBuilding(bigArea));\n        Assert.assertEquals(\"attraction\", bigArea.getTags().get(\"tourism\"));\n        Assert.assertEquals(\"Fort Charlotte\", bigArea.getTags().get(\"name\"));\n        Assert.assertEquals(20, Iterables.size(bigArea));\n        Assert.assertNotNull(bigArea.tag(LastEditTimeTag.KEY));\n        Assert.assertNotNull(bigArea.tag(LastEditUserIdentifierTag.KEY));\n\n        final Line longLine = atlas.line(374341334000000L);\n        Assert.assertEquals(\"drain\", longLine.getTags().get(\"waterway\"));\n        Assert.assertEquals(\"Storm Water Drain\", longLine.getTags().get(\"name\"));\n        Assert.assertEquals(24, Iterables.size(longLine));\n        Assert.assertNotNull(longLine.tag(LastEditTimeTag.KEY));\n        Assert.assertNotNull(longLine.tag(LastEditUserIdentifierTag.KEY));\n    }\n\n    @Test\n    public void testEdgeAndNode()\n    {\n        final Rectangle bound = Location.forString(\"25.0693383, -77.3160218\")\n                .boxAround(Distance.feet(1));\n        final Atlas atlas = loadBahamas(bound);\n        Assert.assertEquals(80, atlas.numberOfEdges());\n\n        final Edge edgeForward = atlas.edge(63423376000000L);\n        Assert.assertEquals(63423376000000L, edgeForward.getIdentifier());\n        Assert.assertEquals(HighwayTag.RESIDENTIAL, edgeForward.highwayTag());\n        Assert.assertEquals(6, Iterables.size(edgeForward.getRawGeometry()));\n        Assert.assertNotNull(edgeForward.tag(LastEditTimeTag.KEY));\n        Assert.assertNotNull(edgeForward.tag(LastEditUserIdentifierTag.KEY));\n\n        final Edge edgeBackward = atlas.edge(-63423376000000L);\n        Assert.assertEquals(-63423376000000L, edgeBackward.getIdentifier());\n        Assert.assertEquals(HighwayTag.RESIDENTIAL, edgeBackward.highwayTag());\n        Assert.assertEquals(6, Iterables.size(edgeBackward.getRawGeometry()));\n        Assert.assertNotNull(edgeBackward.tag(LastEditTimeTag.KEY));\n        Assert.assertNotNull(edgeBackward.tag(LastEditUserIdentifierTag.KEY));\n\n        Assert.assertEquals(Iterables.first(edgeForward.getRawGeometry()).get(),\n                Iterables.last(edgeBackward.getRawGeometry()).get());\n\n        final Node startNodeOfForwardEdge = atlas.node(786050062000000L);\n        Assert.assertEquals(\"POINT (-77.3160218 25.0693383)\",\n                startNodeOfForwardEdge.getLocation().toString());\n        Assert.assertEquals(3, startNodeOfForwardEdge.inEdges().size());\n        Assert.assertTrue(\n                startNodeOfForwardEdge.inEdges().stream().map(edge -> edge.getIdentifier())\n                        .collect(Collectors.toList()).contains(edgeBackward.getIdentifier()));\n        Assert.assertEquals(3, startNodeOfForwardEdge.outEdges().size());\n        Assert.assertTrue(\n                startNodeOfForwardEdge.outEdges().stream().map(edge -> edge.getIdentifier())\n                        .collect(Collectors.toList()).contains(edgeForward.getIdentifier()));\n        Assert.assertEquals(startNodeOfForwardEdge, edgeForward.start());\n        Assert.assertNotNull(startNodeOfForwardEdge.tag(LastEditTimeTag.KEY));\n        Assert.assertNotNull(startNodeOfForwardEdge.tag(LastEditUserIdentifierTag.KEY));\n\n        final Node endNodeOfForwardEdge = atlas.node(4354620579000000L);\n        Assert.assertEquals(\"POINT (-77.3149029 25.0691753)\",\n                endNodeOfForwardEdge.getLocation().toString());\n        Assert.assertEquals(1, endNodeOfForwardEdge.inEdges().size());\n        Assert.assertTrue(endNodeOfForwardEdge.inEdges().stream().map(edge -> edge.getIdentifier())\n                .collect(Collectors.toList()).contains(edgeForward.getIdentifier()));\n        Assert.assertEquals(1, endNodeOfForwardEdge.outEdges().size());\n        Assert.assertTrue(endNodeOfForwardEdge.outEdges().stream().map(edge -> edge.getIdentifier())\n                .collect(Collectors.toList()).contains(edgeBackward.getIdentifier()));\n        Assert.assertEquals(endNodeOfForwardEdge, edgeForward.end());\n        Assert.assertNotNull(endNodeOfForwardEdge.tag(LastEditTimeTag.KEY));\n        Assert.assertNotNull(endNodeOfForwardEdge.tag(LastEditUserIdentifierTag.KEY));\n    }\n\n    @Test\n    public void testPoint()\n    {\n        final Rectangle bound = Location.forString(\"25.0735519,-77.3073068\")\n                .boxAround(Distance.inches(1));\n        final Atlas atlas = loadBahamas(bound);\n        Assert.assertEquals(97, atlas.numberOfPoints());\n        final Point point = atlas.point(5665510971000000L);\n\n        Assert.assertEquals(5665510971000000L, point.getIdentifier());\n        Assert.assertEquals(\"POINT (-77.3073068 25.0735519)\", point.getLocation().toString());\n        Assert.assertEquals(6, point.getTags().size());\n        Assert.assertEquals(\"viewpoint\", point.getTags().get(\"tourism\"));\n        Assert.assertNotNull(point.tag(LastEditTimeTag.KEY));\n        Assert.assertNotNull(point.tag(LastEditUserIdentifierTag.KEY));\n    }\n\n    @Test\n    public void testPolygonLoading()\n    {\n        final Rectangle largerBound = Rectangle.forLocated(\n                Location.forString(\"25.0288172,-77.5420233\"),\n                Location.forString(\"25.0213741,-77.5237397\"));\n        final Atlas bigAtlas = loadBahamas(largerBound);\n\n        final Polygon polygon = bigAtlas.area(24601488000000L).asPolygon();\n        final Atlas smallAtlas = loadBahamas(polygon);\n\n        Assert.assertTrue(bigAtlas.numberOfEdges() > smallAtlas.numberOfEdges());\n        Assert.assertEquals(126, smallAtlas.numberOfEdges());\n        Assert.assertEquals(13, smallAtlas.numberOfAreas());\n    }\n\n    @Test\n    public void testRestrictionAndMultiPolygon()\n    {\n        final Rectangle bound = Location.forString(\"25.0812893, -77.3216682\")\n                .boxAround(Distance.kilometers(0.5));\n        final Atlas atlas = loadBahamas(bound);\n\n        final Map<Long, Relation> relations = new HashMap<>();\n        atlas.relations().forEach(relation -> relations.put(relation.getIdentifier(), relation));\n        Assert.assertEquals(38, relations.size());\n\n        final Relation relation1 = relations.get(4309052000000L);\n        Assert.assertEquals(\"restriction\", relation1.getTags().get(\"type\"));\n        final RelationMemberList members1 = relation1.members();\n        Assert.assertEquals(3, members1.size());\n        Assert.assertEquals(1635708993000000L, members1.get(0).getEntity().getIdentifier());\n        Assert.assertEquals(\"via\", members1.get(0).getRole());\n        Assert.assertEquals(150683353000000L, members1.get(1).getEntity().getIdentifier());\n        Assert.assertEquals(\"from\", members1.get(1).getRole());\n        Assert.assertEquals(150683354000000L, members1.get(2).getEntity().getIdentifier());\n        Assert.assertEquals(\"to\", members1.get(2).getRole());\n\n        final Relation relation2 = relations.get(1621692000000L);\n        Assert.assertEquals(\"multipolygon\", relation2.getTags().get(\"type\"));\n        final RelationMemberList members2 = relation2.members();\n        Assert.assertEquals(2, members2.size());\n        Assert.assertEquals(117365339000000L, members2.get(1).getEntity().getIdentifier());\n        Assert.assertEquals(\"outer\", members2.get(1).getRole());\n        Assert.assertEquals(117365338000000L, members2.get(0).getEntity().getIdentifier());\n        Assert.assertEquals(\"inner\", members2.get(0).getRole());\n\n        final Relation relation3 = relations.get(4309051000000L);\n        Assert.assertEquals(\"restriction\", relation3.getTags().get(\"type\"));\n        final RelationMemberList members3 = relation3.members();\n        Assert.assertEquals(3, members3.size());\n        Assert.assertEquals(719062796000000L, members3.get(0).getEntity().getIdentifier());\n        Assert.assertEquals(\"via\", members3.get(0).getRole());\n        Assert.assertEquals(57942389000000L, members3.get(1).getEntity().getIdentifier());\n        Assert.assertEquals(\"from\", members3.get(1).getRole());\n        Assert.assertEquals(318515851000000L, members3.get(2).getEntity().getIdentifier());\n        Assert.assertEquals(\"to\", members3.get(2).getRole());\n    }\n\n    @Test\n    public void testRoute()\n    {\n        // This 250 meters range just covers part of the relation1\n        final Rectangle bound = Location.forString(\"26.0845577, -77.5369822\")\n                .boxAround(Distance.meters(250));\n        final Atlas atlas = loadBahamas(bound);\n\n        final Map<Long, Relation> relations = new HashMap<>();\n        atlas.relations().forEach(relation -> relations.put(relation.getIdentifier(), relation));\n        Assert.assertEquals(9, relations.size());\n\n        final Relation relation1 = relations.get(1251624000000L);\n        Assert.assertEquals(\"route\", relation1.getTags().get(\"type\"));\n        Assert.assertEquals(\"bus\", relation1.getTags().get(\"route\"));\n        Assert.assertEquals(\"Adult Tram\", relation1.getTags().get(\"name\"));\n        Assert.assertEquals(23, relation1.members().size());\n\n        final Relation relation2 = relations.get(1245746000000L);\n        Assert.assertEquals(\"route\", relation2.getTags().get(\"type\"));\n        Assert.assertEquals(\"bus\", relation2.getTags().get(\"route\"));\n        Assert.assertEquals(\"Tram\", relation2.getTags().get(\"name\"));\n        Assert.assertEquals(18, relation2.members().size());\n    }\n\n    @Test\n    public void testWaysSpanningOutsideOfCountry()\n    {\n        final Resource pbf = new InputStreamResource(\n                () -> OsmPbfIngestIntegrationTest.class.getResourceAsStream(\"CUB_72-111.pbf\"));\n        final CountryBoundaryMap map = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> OsmPbfIngestIntegrationTest.class\n                        .getResourceAsStream(\"CUB_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n        final SlippyTile tile = SlippyTile.forName(\"8-72-111\");\n        final MultiPolygon boundary = new JtsPolygonToMultiPolygonConverter()\n                .convert(map.countryBoundary(\"CUB\").get(0));\n        final MultiPolygon loadingArea = tile.bounds().clip(boundary, ClipType.AND)\n                .getClipMultiPolygon();\n\n        final AtlasLoadingOption loadingOption = AtlasLoadingOption.createOptionWithAllEnabled(map);\n        Atlas atlas = new RawAtlasGenerator(pbf, loadingOption, loadingArea).build();\n        atlas = new RawAtlasSlicer(loadingOption, atlas).slice();\n        atlas = new AtlasSectionProcessor(atlas, loadingOption).run();\n        // Make sure that the big bridge over water made it to the Atlas\n        Assert.assertNotNull(atlas.edge(308541861000000L));\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/pbf/slicing/AtlasSectionProcessorIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.slicing;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasIntegrationTest;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.AbstractIdentifierFactory;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.PaddingIdentifierFactory;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.WaySectionIdentifierFactory;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author tony\n */\npublic class AtlasSectionProcessorIntegrationTest extends AtlasIntegrationTest\n{\n    private static Atlas rawAtlas;\n    private static Atlas sectionedAtlas;\n\n    @Before\n    public void createAtlas()\n    {\n        final Rectangle belizeCity = Rectangle.forLocated(\n                Location.forString(\"17.521983, -88.213739\"),\n                Location.forString(\"17.491327, -88.178071\"));\n\n        // Both atlases are not country-sliced\n        if (rawAtlas == null)\n        {\n            rawAtlas = loadBelizeRaw(belizeCity, AtlasLoadingOption.createOptionWithNoSlicing()\n                    .setLoadWaysSpanningCountryBoundaries(false));\n        }\n        if (sectionedAtlas == null)\n        {\n            sectionedAtlas = loadBelizeRaw(belizeCity,\n                    AtlasLoadingOption.createOptionWithNoSlicing().setWaySectioning(true)\n                            .setLoadWaysSpanningCountryBoundaries(false));\n        }\n    }\n\n    @Test\n    public void testPrimaryWay()\n    {\n        // Primary way should be sectioned\n        final long primaryWayShouldBeSectioned = 279042065L;\n        final AbstractIdentifierFactory factory = new WaySectionIdentifierFactory(\n                PaddingIdentifierFactory.pad(primaryWayShouldBeSectioned));\n        Assert.assertEquals(21,\n                Iterables.size(rawAtlas.line(primaryWayShouldBeSectioned).getRawGeometry()));\n        Assert.assertNull(sectionedAtlas.edge(factory.getReferenceIdentifier()));\n        Assert.assertEquals(2,\n                Iterables.size(sectionedAtlas.edge(factory.nextIdentifier()).getRawGeometry()));\n        Assert.assertEquals(2,\n                Iterables.size(sectionedAtlas.edge(factory.nextIdentifier()).getRawGeometry()));\n        Assert.assertEquals(2,\n                Iterables.size(sectionedAtlas.edge(factory.nextIdentifier()).getRawGeometry()));\n        Assert.assertEquals(2,\n                Iterables.size(sectionedAtlas.edge(279042065000015L).getRawGeometry()));\n        Assert.assertEquals(5,\n                Iterables.size(sectionedAtlas.edge(279042065000016L).getRawGeometry()));\n        Assert.assertEquals(2,\n                Iterables.size(sectionedAtlas.edge(279042065000017L).getRawGeometry()));\n        Assert.assertNull(sectionedAtlas.edge(279042065000018L));\n    }\n\n    @Test\n    public void testSelfIntersectionRing()\n    {\n        // Way 295734091 will be sectioned into 295734091000001 and 295734091000002. However because\n        // 295734091000002 is a ring, which should be further sectioned into 295734091000003 and\n        // 295734091000004\n        final long selfIntersectionWay = 295734091;\n        Assert.assertNotNull(rawAtlas.line(selfIntersectionWay));\n        final AbstractIdentifierFactory factory = new WaySectionIdentifierFactory(\n                PaddingIdentifierFactory.pad(selfIntersectionWay));\n        Assert.assertNull(sectionedAtlas.edge(factory.getReferenceIdentifier()));\n        Assert.assertNotNull(sectionedAtlas.edge(factory.nextIdentifier()));\n        Assert.assertNotNull(sectionedAtlas.edge(factory.nextIdentifier()));\n    }\n\n    @Test\n    public void testShapePointOrder()\n    {\n        // The identifier order of split way should be same as the order of shape point for way with\n        // tag oneway=-1\n        final long reversedWay = 25977940L;\n        final Line nonSplit = rawAtlas.line(reversedWay);\n        Assert.assertNotNull(nonSplit);\n        final AbstractIdentifierFactory factory = new WaySectionIdentifierFactory(\n                PaddingIdentifierFactory.pad(reversedWay));\n        Assert.assertNull(sectionedAtlas.edge(factory.getReferenceIdentifier()));\n        final Edge firstSplit = sectionedAtlas.edge(factory.nextIdentifier());\n        final Edge secondSplit = sectionedAtlas.edge(factory.nextIdentifier());\n        Assert.assertNotNull(firstSplit);\n        Assert.assertNotNull(secondSplit);\n        Assert.assertEquals(nonSplit.asPolyLine().last(), secondSplit.start().getLocation());\n        Assert.assertEquals(nonSplit.asPolyLine().first(), firstSplit.end().getLocation());\n    }\n\n    @Test\n    public void testTotalCounts()\n    {\n        Assert.assertEquals(0, rawAtlas.numberOfEdges());\n        Assert.assertEquals(2841, sectionedAtlas.numberOfEdges());\n\n        Assert.assertEquals(582, rawAtlas.numberOfLines());\n        Assert.assertEquals(9, sectionedAtlas.numberOfLines());\n\n        Assert.assertEquals(0, rawAtlas.numberOfNodes());\n        Assert.assertEquals(1165, sectionedAtlas.numberOfNodes());\n\n        Assert.assertEquals(336, rawAtlas.numberOfAreas());\n        Assert.assertEquals(336, sectionedAtlas.numberOfAreas());\n\n        Assert.assertEquals(1, rawAtlas.numberOfRelations());\n        Assert.assertEquals(1, sectionedAtlas.numberOfRelations());\n\n        Assert.assertEquals(5380, rawAtlas.numberOfPoints());\n        Assert.assertEquals(82, sectionedAtlas.numberOfPoints());\n    }\n\n    @Test\n    public void testWaterWay()\n    {\n        // Waterway (Atlas area) should not be sectioned\n        final long waterway = 25977495L;\n        Assert.assertNotNull(rawAtlas.area(waterway));\n        Assert.assertNotNull(sectionedAtlas.area(PaddingIdentifierFactory.pad(waterway)));\n    }\n\n    @Test\n    public void testWayAcrossArea()\n    {\n        // Way across an area should not be sectioned\n        final long wayAcrossArea = 194237644L;\n        Assert.assertEquals(2, Iterables.size(rawAtlas.line(wayAcrossArea).getRawGeometry()));\n        Assert.assertEquals(2, Iterables.size(\n                sectionedAtlas.edge(PaddingIdentifierFactory.pad(wayAcrossArea)).getRawGeometry()));\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/raw/DynamicRawAtlasSectioningTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\n\n/**\n * {@link RawAtlasIntegrationTest} test data.\n *\n * @author mgostintsev\n */\npublic class DynamicRawAtlasSectioningTestRule extends CoreTestRule\n{\n    // Fully inside 8-123-122\n    private static final String ONE = \"7.9747091, -6.6837721\";\n    private static final String TWO = \"7.9737754, -6.6823776\";\n\n    // Fully inside 8-123-123\n    private static final String THREE = \"5.7826593, -6.5920337\";\n    private static final String FOUR = \"5.7827163, -6.5914695\";\n\n    // Fully inside 7-62-61\n    private static final String FIVE = \"6.7240498, -3.4840235\";\n    private static final String SIX = \"6.7260445, -3.4840096\";\n\n    // Crossing 8-123-122 into 8-123-123\n    private static final String SEVEN = \"7.0401813, -6.4733942\";\n    private static final String EIGHT = \"6.8870387, -6.4641103\";\n    private static final String NINE = \" 6.8832099, -6.4636300\";\n    private static final String TEN = \"6.8866444, -6.4654545\";\n\n    // Crossing 8-123-122 into 7-62-61\n    private static final String ELEVEN = \"7.6829818, -5.6379654\";\n    private static final String TWELVE = \"7.6735590, -5.5867130\";\n    private static final String THIRTEEN = \"7.6717846, -5.5812728\";\n    private static final String FOURTEEN = \"7.6681651, -5.5865950\";\n\n    // Crossing 8-123-123 into 7-62-61\n    private static final String FIFTEEN = \"5.8751274, -5.6744400\";\n    private static final String SIXTEEN = \"5.8641274, -5.5664451\";\n    private static final String SEVENTEEN = \"5.8545285, -5.5181042\";\n    private static final String EIGHTEEN = \"5.8813557, -5.5808398\";\n\n    // Starting in 8-123-122, through 8-123-123, ending in 7-62-61\n    private static final String NINETEEN = \"7.3884501, -6.4772718\";\n    private static final String TWENTY = \"7.3864604, -6.4728799\";\n    private static final String TWENTY_ONE = \"6.8787867, -6.4534416\";\n    private static final String TWENTY_TWO = \"6.8738255, -6.4553079\";\n    private static final String TWENTY_THREE = \"6.8299549, -5.2608135\";\n    private static final String TWENTY_FOUR = \"6.8298791, -5.2562899\";\n    private static final String TWENTY_FIVE = \"7.3868182, -6.4796125\";\n    private static final String TWENTY_SIX = \"6.8769819, -6.4596778\";\n    private static final String TWENTY_SEVEN = \"6.8333518, -5.2585345\";\n\n    @TestAtlas(lines = {\n            @Line(id = \"541701001000\", coordinates = { @Loc(value = THREE),\n                    @Loc(value = FOUR) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541702001000\", coordinates = { @Loc(value = SEVEN), @Loc(value = EIGHT),\n                    @Loc(value = NINE) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541703001000\", coordinates = { @Loc(value = EIGHT),\n                    @Loc(value = TEN) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541704001000\", coordinates = { @Loc(value = TWENTY_SIX),\n                    @Loc(value = TWENTY_ONE) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541705001000\", coordinates = { @Loc(value = FIFTEEN), @Loc(value = SIXTEEN),\n                    @Loc(value = SEVENTEEN) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541706001000\", coordinates = { @Loc(value = NINETEEN), @Loc(value = TWENTY),\n                    @Loc(value = TWENTY_ONE), @Loc(value = TWENTY_TWO), @Loc(value = TWENTY_THREE),\n                    @Loc(value = TWENTY_FOUR) }, tags = { \"highway=primary\" })\n\n    }, points = {\n\n            @Point(id = \"511111003\", coordinates = @Loc(value = THREE)),\n            @Point(id = \"511111004\", coordinates = @Loc(value = FOUR)),\n            @Point(id = \"511111009\", coordinates = @Loc(value = NINE)),\n            @Point(id = \"511111007\", coordinates = @Loc(value = SEVEN)),\n            @Point(id = \"511111008\", coordinates = @Loc(value = EIGHT)),\n            @Point(id = \"511111010\", coordinates = @Loc(value = TEN)),\n            @Point(id = \"511111015\", coordinates = @Loc(value = FIFTEEN)),\n            @Point(id = \"511111017\", coordinates = @Loc(value = SEVENTEEN)),\n            @Point(id = \"511111019\", coordinates = @Loc(value = NINETEEN)),\n            @Point(id = \"511111021\", coordinates = @Loc(value = TWENTY_ONE)),\n            @Point(id = \"511111026\", coordinates = @Loc(value = TWENTY_SIX)),\n            @Point(id = \"511111022\", coordinates = @Loc(value = TWENTY_TWO)),\n            @Point(id = \"511111024\", coordinates = @Loc(value = TWENTY_FOUR)) })\n    private Atlas atlasZ8x123y123;\n\n    @TestAtlas(lines = {\n            @Line(id = \"541707001000\", coordinates = { @Loc(value = ONE),\n                    @Loc(value = TWO) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541702001000\", coordinates = { @Loc(value = SEVEN), @Loc(value = EIGHT),\n                    @Loc(value = NINE) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541708001000\", coordinates = { @Loc(value = ELEVEN), @Loc(value = TWELVE),\n                    @Loc(value = THIRTEEN) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541709001000\", coordinates = { @Loc(value = TWENTY_FIVE),\n                    @Loc(value = TWENTY) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541706001000\", coordinates = { @Loc(value = NINETEEN), @Loc(value = TWENTY),\n                    @Loc(value = TWENTY_ONE), @Loc(value = TWENTY_TWO), @Loc(value = TWENTY_THREE),\n                    @Loc(value = TWENTY_FOUR) }, tags = { \"highway=primary\" })\n\n    }, points = {\n\n            @Point(id = \"511111001\", coordinates = @Loc(value = ONE)),\n            @Point(id = \"511111002\", coordinates = @Loc(value = TWO)),\n            @Point(id = \"511111011\", coordinates = @Loc(value = ELEVEN)),\n            @Point(id = \"511111013\", coordinates = @Loc(value = THIRTEEN)),\n            @Point(id = \"511111007\", coordinates = @Loc(value = SEVEN)),\n            @Point(id = \"511111009\", coordinates = @Loc(value = NINE)),\n            @Point(id = \"511111019\", coordinates = @Loc(value = NINETEEN)),\n            @Point(id = \"511111020\", coordinates = @Loc(value = TWENTY)),\n            @Point(id = \"511111025\", coordinates = @Loc(value = TWENTY_FIVE)),\n            @Point(id = \"511111022\", coordinates = @Loc(value = TWENTY_TWO)),\n            @Point(id = \"511111024\", coordinates = @Loc(value = TWENTY_FOUR))\n\n    })\n    private Atlas atlasZ8x123y122;\n\n    @TestAtlas(lines = {\n\n            @Line(id = \"541710001000\", coordinates = { @Loc(value = FIVE),\n                    @Loc(value = SIX) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541711001000\", coordinates = { @Loc(value = TWELVE),\n                    @Loc(value = FOURTEEN) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541708001000\", coordinates = { @Loc(value = ELEVEN), @Loc(value = TWELVE),\n                    @Loc(value = THIRTEEN) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541712001000\", coordinates = { @Loc(value = TWENTY_SEVEN),\n                    @Loc(value = TWENTY_THREE) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541713001000\", coordinates = { @Loc(value = SIXTEEN),\n                    @Loc(value = EIGHTEEN) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541705001000\", coordinates = { @Loc(value = FIFTEEN), @Loc(value = SIXTEEN),\n                    @Loc(value = SEVENTEEN) }, tags = { \"highway=primary\" }),\n            @Line(id = \"541706001000\", coordinates = { @Loc(value = NINETEEN), @Loc(value = TWENTY),\n                    @Loc(value = TWENTY_ONE), @Loc(value = TWENTY_TWO), @Loc(value = TWENTY_THREE),\n                    @Loc(value = TWENTY_FOUR) }, tags = { \"highway=primary\" }) }, points = {\n\n                            @Point(id = \"511111005\", coordinates = @Loc(value = FIVE)),\n                            @Point(id = \"511111006\", coordinates = @Loc(value = SIX)),\n                            @Point(id = \"511111011\", coordinates = @Loc(value = ELEVEN)),\n                            @Point(id = \"511111012\", coordinates = @Loc(value = TWELVE)),\n                            @Point(id = \"511111013\", coordinates = @Loc(value = THIRTEEN)),\n                            @Point(id = \"511111014\", coordinates = @Loc(value = FOURTEEN)),\n                            @Point(id = \"511111015\", coordinates = @Loc(value = FIFTEEN)),\n                            @Point(id = \"511111016\", coordinates = @Loc(value = SIXTEEN)),\n                            @Point(id = \"511111017\", coordinates = @Loc(value = SEVENTEEN)),\n                            @Point(id = \"511111018\", coordinates = @Loc(value = EIGHTEEN)),\n                            @Point(id = \"511111019\", coordinates = @Loc(value = NINETEEN)),\n                            @Point(id = \"511111022\", coordinates = @Loc(value = TWENTY_TWO)),\n                            @Point(id = \"511111024\", coordinates = @Loc(value = TWENTY_FOUR)),\n                            @Point(id = \"511111023\", coordinates = @Loc(value = TWENTY_THREE)),\n                            @Point(id = \"511111027\", coordinates = @Loc(value = TWENTY_SEVEN))\n\n    })\n    private Atlas atlasZ7x62y61;\n\n    public Atlas getAtlasz7x62y61()\n    {\n        return this.atlasZ7x62y61;\n    }\n\n    public Atlas getAtlasz8x123y122()\n    {\n        return this.atlasZ8x123y122;\n    }\n\n    public Atlas getAtlasz8x123y123()\n    {\n        return this.atlasZ8x123y123;\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/raw/RawAtlasIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.junit.Assert;\nimport org.junit.Ignore;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.ShardFileOverlapsPolygonTest;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.sharding.DynamicTileSharding;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Integration tests for creating, slicing and sectioning with the raw Atlas ingest flow.\n *\n * @author mgostintsev\n */\npublic class RawAtlasIntegrationTest\n{\n    private static final AtlasLoadingOption loadingOptionAll;\n    private static final AtlasLoadingOption loadingOptionAntarctica;\n    private static final AtlasLoadingOption loadingOptionIvoryCoast;\n\n    private static final AtlasLoadingOption loadingOptionIntersectionAtEnd;\n    private static final AtlasLoadingOption loadingOptionIntersectionAtStart;\n\n    private static final AtlasLoadingOption loadingOptionIntersectionAtMiddle;\n\n    private static final long LINE_OSM_IDENTIFIER_CROSSING_3_SHARDS = 541706;\n\n    private static final Logger logger = LoggerFactory.getLogger(RawAtlasIntegrationTest.class);\n\n    static\n    {\n        final CountryBoundaryMap boundary = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> RawAtlasIntegrationTest.class\n                        .getResourceAsStream(\"CIV_GIN_LBR_osm_boundaries_with_grid_index.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n        loadingOptionAll = AtlasLoadingOption.createOptionWithAllEnabled(boundary);\n\n        loadingOptionIvoryCoast = AtlasLoadingOption.createOptionWithAllEnabled(boundary)\n                .setCountryCode(\"CIV\");\n\n        // This is an OSM node that doesn't have any tags, is not a member of a relation or part of\n        // a way. It should end up as a point in the final atlas.\n        final String antarctica = \"ATA\";\n\n        // Create a fake boundary as a bounding box around the target point\n        final Map<String, List<Polygon>> boundaries = new HashMap<>();\n        final Location targetPoint = Location.forString(\"-81.2022146, 51.6408578\");\n        final List<Polygon> ataPolygons = new ArrayList<>();\n        ataPolygons\n                .add(new JtsPolygonConverter().convert(targetPoint.boxAround(Distance.meters(1))));\n        boundaries.put(antarctica, ataPolygons);\n\n        // Create a country boundary map with the fake Antarctica country boundary\n        final CountryBoundaryMap antarticaBoundary = CountryBoundaryMap.fromBoundaryMap(boundaries);\n        loadingOptionAntarctica = AtlasLoadingOption.createOptionWithAllEnabled(antarticaBoundary)\n                .setCountryCode(antarctica);\n\n        final CountryBoundaryMap intersectionAtEndBoundary = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> RawAtlasIntegrationTest.class\n                        .getResourceAsStream(\"layerIntersectionAtEndBoundaryMap.txt\")));\n        loadingOptionIntersectionAtEnd = AtlasLoadingOption\n                .createOptionWithAllEnabled(intersectionAtEndBoundary).setCountryCode(\"RUS\");\n\n        final CountryBoundaryMap intersectionAtStartBoundary = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> RawAtlasIntegrationTest.class\n                        .getResourceAsStream(\"layerIntersectionAtStartBoundaryMap.txt\")));\n        loadingOptionIntersectionAtStart = AtlasLoadingOption\n                .createOptionWithAllEnabled(intersectionAtStartBoundary).setCountryCode(\"RUS\");\n\n        final CountryBoundaryMap intersectionAtMiddleBoundary = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> RawAtlasIntegrationTest.class\n                        .getResourceAsStream(\"layerIntersectionInMiddleBoundaryMap.txt\")));\n        loadingOptionIntersectionAtMiddle = AtlasLoadingOption\n                .createOptionWithAllEnabled(intersectionAtMiddleBoundary).setCountryCode(\"SGP\");\n    }\n\n    @Rule\n    public DynamicRawAtlasSectioningTestRule setup = new DynamicRawAtlasSectioningTestRule();\n\n    @Test\n    public void testPbfToSlicedAtlasWithExpansion()\n    {\n        // Create a simple store, populated with 3 shards and the corresponding atlases\n        final Map<Shard, Atlas> store = prepareShardStore();\n        final Function<Shard, Optional<Atlas>> rawAtlasFetcher = shard ->\n        {\n            if (store.containsKey(shard))\n            {\n                return Optional.of(store.get(shard));\n            }\n            else\n            {\n                return Optional.empty();\n            }\n        };\n\n        // Create 3 atlas files, starting from each of the different shards\n        final Atlas atlasFromz8x123y122 = generateSectionedAtlasStartingAtShard(\n                new SlippyTile(123, 122, 8), rawAtlasFetcher);\n        logger.info(atlasFromz8x123y122.summary());\n\n        final Atlas atlasFromz8x123y123 = generateSectionedAtlasStartingAtShard(\n                new SlippyTile(123, 123, 8), rawAtlasFetcher);\n        logger.info(atlasFromz8x123y123.summary());\n\n        final Atlas atlasFromz7x62y61 = generateSectionedAtlasStartingAtShard(\n                new SlippyTile(62, 61, 7), rawAtlasFetcher);\n        logger.info(atlasFromz7x62y61.summary());\n\n        // Let's focus on the edge spanning all 3 shards and verify it got sectioned properly\n        final Iterable<Edge> firstGroupOfEdges = atlasFromz8x123y122\n                .edges(edge -> edge.getOsmIdentifier() == LINE_OSM_IDENTIFIER_CROSSING_3_SHARDS);\n        final Iterable<Edge> secondGroupOfEdges = atlasFromz8x123y123\n                .edges(edge -> edge.getOsmIdentifier() == LINE_OSM_IDENTIFIER_CROSSING_3_SHARDS);\n        final Iterable<Edge> thirdGroupOfEdges = atlasFromz7x62y61\n                .edges(edge -> edge.getOsmIdentifier() == LINE_OSM_IDENTIFIER_CROSSING_3_SHARDS);\n\n        // First look at absolute counts. Each shard will have two forward and reverse edges\n        Assert.assertEquals(4, Iterables.size(firstGroupOfEdges));\n        Assert.assertEquals(4, Iterables.size(secondGroupOfEdges));\n        Assert.assertEquals(4, Iterables.size(thirdGroupOfEdges));\n\n        // Next, let's check identifier consistency\n        final Set<Long> uniqueIdentifiers = new HashSet<>();\n        Iterables.stream(firstGroupOfEdges)\n                .forEach(edge -> uniqueIdentifiers.add(edge.getIdentifier()));\n        Iterables.stream(secondGroupOfEdges)\n                .forEach(edge -> uniqueIdentifiers.add(edge.getIdentifier()));\n        Iterables.stream(thirdGroupOfEdges)\n                .forEach(edge -> uniqueIdentifiers.add(edge.getIdentifier()));\n\n        // There should be 4 pieces (each having a forward and reverse edge) total\n        Assert.assertTrue(uniqueIdentifiers.size() == 8);\n\n        // Validate the same edge identifiers built from different shards to test equality\n        final Edge piece2from122 = atlasFromz8x123y122.edge(541706001002L);\n        final Edge piece2from123 = atlasFromz8x123y123.edge(541706001002L);\n        Assert.assertTrue(piece2from122.asPolyLine().equals(piece2from123.asPolyLine()));\n\n        final Edge piece3from123 = atlasFromz8x123y123.edge(541706001003L);\n        final Edge piece3from62 = atlasFromz7x62y61.edge(541706001003L);\n        Assert.assertTrue(piece3from123.asPolyLine().equals(piece3from62.asPolyLine()));\n\n        // Let's validate absolute number of edges in each shard\n        Assert.assertEquals(12, atlasFromz8x123y122.numberOfEdges());\n        Assert.assertEquals(16, atlasFromz8x123y123.numberOfEdges());\n        Assert.assertEquals(20, atlasFromz7x62y61.numberOfEdges());\n    }\n\n    @Test\n    public void testPbfToSlicedRawAtlas()\n    {\n        // This PBF file contains really interesting data. 1. MultiPolygon with multiple inners and\n        // outers spanning 3 countries (http://www.openstreetmap.org/relation/3638082) 2. Multiple\n        // nested relations (http://www.openstreetmap.org/relation/3314886)\n        final String pbfPath = RawAtlasIntegrationTest.class\n                .getResource(\"8-122-122-trimmed.osm.pbf\").getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(pbfPath));\n        final Atlas rawAtlas = rawAtlasGenerator.build();\n\n        Assert.assertEquals(0, rawAtlas.numberOfNodes());\n        Assert.assertEquals(0, rawAtlas.numberOfEdges());\n        Assert.assertEquals(5119, rawAtlas.numberOfAreas());\n        Assert.assertEquals(79634, rawAtlas.numberOfPoints());\n\n        Assert.assertEquals(2729, rawAtlas.numberOfLines());\n        Assert.assertEquals(24, rawAtlas.numberOfRelations());\n\n        final Atlas slicedRawAtlas = new RawAtlasSlicer(loadingOptionAll, rawAtlas).slice();\n\n        Assert.assertEquals(0, slicedRawAtlas.numberOfNodes());\n        Assert.assertEquals(0, slicedRawAtlas.numberOfEdges());\n        Assert.assertEquals(5132, slicedRawAtlas.numberOfAreas());\n        Assert.assertEquals(32524, slicedRawAtlas.numberOfPoints());\n        Assert.assertEquals(3003, slicedRawAtlas.numberOfLines());\n        Assert.assertEquals(24, slicedRawAtlas.numberOfRelations());\n\n        // Assert all raw Atlas entities have a country code\n        assertAllEntitiesHaveCountryCode(slicedRawAtlas);\n\n        final Atlas ivoryCoast = new RawAtlasSlicer(loadingOptionIvoryCoast, rawAtlas).slice();\n\n        Assert.assertEquals(0, ivoryCoast.numberOfNodes());\n        Assert.assertEquals(0, ivoryCoast.numberOfEdges());\n        Assert.assertEquals(2400, ivoryCoast.numberOfAreas());\n        Assert.assertEquals(15075, ivoryCoast.numberOfPoints());\n        Assert.assertEquals(1265, ivoryCoast.numberOfLines());\n        Assert.assertEquals(19, ivoryCoast.numberOfRelations());\n\n        // Assert all raw Atlas entities have a country code\n        assertAllEntitiesHaveCountryCode(ivoryCoast);\n\n        // Test sectioning!\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas, loadingOptionAll).run();\n\n        Assert.assertEquals(5011, finalAtlas.numberOfNodes());\n        Assert.assertEquals(9764, finalAtlas.numberOfEdges());\n        Assert.assertEquals(5132, finalAtlas.numberOfAreas());\n        Assert.assertEquals(184, finalAtlas.numberOfPoints());\n        Assert.assertEquals(376, finalAtlas.numberOfLines());\n        Assert.assertEquals(24, finalAtlas.numberOfRelations());\n    }\n\n    @Ignore\n    @Test\n    public void testSectioningFromShard()\n    {\n        final String path = RawAtlasIntegrationTest.class.getResource(\"8-122-122-trimmed.osm.pbf\")\n                .getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));\n        final Atlas rawAtlas = rawAtlasGenerator.build();\n        final Atlas slicedRawAtlas = new RawAtlasSlicer(loadingOptionAll, rawAtlas).slice();\n\n        // Simple fetcher that returns the atlas from above for the corresponding shard\n        final Map<Shard, Atlas> store = new HashMap<>();\n        store.put(new SlippyTile(122, 122, 8), slicedRawAtlas);\n        final Function<Shard, Optional<Atlas>> rawAtlasFetcher = shard ->\n        {\n            if (store.containsKey(shard))\n            {\n                return Optional.of(store.get(shard));\n            }\n            else\n            {\n                return Optional.empty();\n            }\n        };\n\n        final Atlas finalAtlas = new AtlasSectionProcessor(new SlippyTile(122, 122, 8),\n                loadingOptionAll,\n                new DynamicTileSharding(new File(ShardFileOverlapsPolygonTest.class.getResource(\n                        \"/org/openstreetmap/atlas/geography/boundary/tree-6-14-100000.txt.gz\")\n                        .getFile())),\n                rawAtlasFetcher).run();\n\n        Assert.assertEquals(5009, finalAtlas.numberOfNodes());\n        Assert.assertEquals(9760, finalAtlas.numberOfEdges());\n        Assert.assertEquals(5128, finalAtlas.numberOfAreas());\n        Assert.assertEquals(184, finalAtlas.numberOfPoints());\n        Assert.assertEquals(271, finalAtlas.numberOfLines());\n        Assert.assertEquals(23, finalAtlas.numberOfRelations());\n    }\n\n    @Ignore\n    @Test\n    public void testStandAloneNodeIngest()\n    {\n        // Create a raw atlas, slice and section it\n        final String pbfPath = RawAtlasIntegrationTest.class.getResource(\"node-4353689487.pbf\")\n                .getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(pbfPath));\n        final Atlas rawAtlas = rawAtlasGenerator.build();\n\n        final Atlas slicedRawAtlas = new RawAtlasSlicer(loadingOptionAntarctica, rawAtlas).slice();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas, loadingOptionAntarctica)\n                .run();\n\n        // Verify only a single point exists\n        Assert.assertEquals(0, finalAtlas.numberOfNodes());\n        Assert.assertEquals(0, finalAtlas.numberOfEdges());\n        Assert.assertEquals(0, finalAtlas.numberOfAreas());\n        Assert.assertEquals(1, finalAtlas.numberOfPoints());\n        Assert.assertEquals(0, finalAtlas.numberOfLines());\n        Assert.assertEquals(0, finalAtlas.numberOfRelations());\n    }\n\n    @Test\n    public void testTwoWaysWithDifferentLayersIntersectingAtEnd()\n    {\n        // Based on https://www.openstreetmap.org/way/26071941 and\n        // https://www.openstreetmap.org/way/405246856 having two different layer tag values and\n        // having a shared node (https://www.openstreetmap.org/node/281526976) at which one of the\n        // ways ends. This is a fairly common OSM use-case, where two roads (often ramps or links)\n        // having different layer tags should be connected.\n        final Location intersection = Location.forString(\"55.0480165, 82.9406646\");\n        final String path = RawAtlasIntegrationTest.class\n                .getResource(\"twoWaysWithDifferentLayersIntersectingAtEnd.pbf\").getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));\n        final Atlas rawAtlas = rawAtlasGenerator.build();\n\n        final Atlas slicedRawAtlas = new RawAtlasSlicer(loadingOptionIntersectionAtEnd, rawAtlas)\n                .slice();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                loadingOptionIntersectionAtEnd).run();\n\n        // Make sure there are exactly three edges created. Both ways are one-way and one of them\n        // gets way-sectioned into two edges.\n        Assert.assertEquals(3, finalAtlas.numberOfEdges());\n\n        // Make sure there are exactly 4 nodes\n        Assert.assertEquals(4, finalAtlas.numberOfNodes());\n\n        // Explicitly check for a single node at the intersection location\n        Assert.assertEquals(1, Iterables.size(finalAtlas.nodesAt(intersection)));\n\n        // Explicitly check that the layer=0 link is connected to both the layer=-1 trunk edges\n        Assert.assertEquals(2, finalAtlas.edge(26071941000000L).connectedEdges().size());\n    }\n\n    @Test\n    public void testTwoWaysWithDifferentLayersIntersectingAtStart()\n    {\n        // Based on https://www.openstreetmap.org/way/551411163 and partial piece of\n        // https://www.openstreetmap.org/way/67803311 having two different layer tag values and\n        // having a shared node (https://www.openstreetmap.org/node/5325270497) at which one of the\n        // ways ends. This is a fairly common OSM use-case, where two roads (often ramps or links)\n        // having different layer tags should be connected. In this case, we also check that the\n        // trunk link is connected to the trunk at both the start and end nodes.\n        final Location intersection = Location.forString(\"52.4819691, 38.7603042\");\n        final String path = RawAtlasIntegrationTest.class\n                .getResource(\"twoWaysWithDifferentLayersIntersectingAtStart.pbf\").getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));\n        final Atlas rawAtlas = rawAtlasGenerator.build();\n\n        final Atlas slicedRawAtlas = new RawAtlasSlicer(loadingOptionIntersectionAtStart, rawAtlas)\n                .slice();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                loadingOptionIntersectionAtStart).run();\n\n        // Make sure there are exactly six edges created. The trunk link (551411163) is\n        // way-sectioned into 2 pieces - at an intermediate crossing, while the trunk (67803311) is\n        // sectioned into 4 pieces - once at the start of the link, once at an intermediate crossing\n        // and again at the end of the link.\n        Assert.assertEquals(6, finalAtlas.numberOfEdges());\n\n        // Make sure there are exactly 6 nodes\n        Assert.assertEquals(6, finalAtlas.numberOfNodes());\n\n        // Explicitly check for a single node at the intersection location\n        Assert.assertEquals(1, Iterables.size(finalAtlas.nodesAt(intersection)));\n\n        // Explicitly check that the layer=0 link is connected to both the layer=1 trunk edges and\n        // its own sectioned edge\n        Assert.assertEquals(3, finalAtlas.edge(551411163000001L).connectedEdges().size());\n        Assert.assertEquals(3, finalAtlas.edge(551411163000002L).connectedEdges().size());\n    }\n\n    @Test\n    public void testTwoWaysWithDifferentLayersIntersectingInMiddle()\n    {\n        // Based on https://www.openstreetmap.org/way/467880095 and\n        // https://www.openstreetmap.org/way/28247094 having two different layer tag values and\n        // having overlapping nodes (https://www.openstreetmap.org/node/4661272336 and\n        // https://www.openstreetmap.org/node/5501637097) that should not be merged.\n        final Location overlappingLocation = Location.forString(\"1.3248985,103.6452864\");\n        final String path = RawAtlasIntegrationTest.class\n                .getResource(\"twoWaysWithDifferentLayersIntersectingInMiddle.pbf\").getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));\n        final Atlas rawAtlas = rawAtlasGenerator.build();\n\n        // Verify both points made it into the raw atlas\n        Assert.assertTrue(Iterables.size(rawAtlas.pointsAt(overlappingLocation)) == 2);\n\n        final Atlas slicedRawAtlas = new RawAtlasSlicer(loadingOptionIntersectionAtMiddle, rawAtlas)\n                .slice();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                loadingOptionIntersectionAtMiddle).run();\n\n        // Make sure there is no sectioning happening between the two ways with different layer tag\n        // values. There is a one-way overpass and a bi-directional residential street, resulting in\n        // 3 total edges and 4 nodes (one on both ends of the two segments)\n        Assert.assertEquals(3, finalAtlas.numberOfEdges());\n        Assert.assertEquals(4, finalAtlas.numberOfNodes());\n\n        // Again, verify there is no node at the duplicated location\n        Assert.assertTrue(Iterables.size(finalAtlas.nodesAt(overlappingLocation)) == 0);\n        Assert.assertEquals(0, finalAtlas.numberOfPoints());\n    }\n\n    private void assertAllEntitiesHaveCountryCode(final Atlas atlas)\n    {\n        atlas.lines().forEach(line ->\n        {\n            Assert.assertTrue(Validators.hasValuesFor(line, ISOCountryTag.class));\n        });\n        atlas.points().forEach(point ->\n        {\n            Assert.assertTrue(Validators.hasValuesFor(point, ISOCountryTag.class));\n        });\n        atlas.relations().forEach(point ->\n        {\n            Assert.assertTrue(Validators.hasValuesFor(point, ISOCountryTag.class));\n        });\n    }\n\n    private Atlas generateSectionedAtlasStartingAtShard(final Shard shard,\n            final Function<Shard, Optional<Atlas>> rawAtlasFetcher)\n    {\n        return new AtlasSectionProcessor(shard, loadingOptionAll,\n                new DynamicTileSharding(new File(ShardFileOverlapsPolygonTest.class.getResource(\n                        \"/org/openstreetmap/atlas/geography/boundary/tree-6-14-100000.txt.gz\")\n                        .getFile())),\n                rawAtlasFetcher).run();\n    }\n\n    private Map<Shard, Atlas> prepareShardStore()\n    {\n        final Map<Shard, Atlas> store = new HashMap<>();\n        store.put(new SlippyTile(62, 61, 7), this.setup.getAtlasz7x62y61());\n        store.put(new SlippyTile(123, 123, 8), this.setup.getAtlasz8x123y123());\n        store.put(new SlippyTile(123, 122, 8), this.setup.getAtlasz8x123y122());\n        return store;\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/atlas/routing/AtlasRoutingIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasIntegrationTest;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasRoutingIntegrationTest extends AtlasIntegrationTest\n{\n    /**\n     * @author matthieun\n     */\n    private static final class ExpectedRoute\n    {\n        private final Location start;\n        private final Location end;\n        private final Distance expectedMaximumLength;\n        private final int expectedMaximumSize;\n\n        ExpectedRoute(final Location start, final Location end,\n                final double expectedMaximumLengthInMeters, final int expectedMaximumSize)\n        {\n            this.start = start;\n            this.end = end;\n            this.expectedMaximumLength = Distance.meters(expectedMaximumLengthInMeters);\n            this.expectedMaximumSize = expectedMaximumSize;\n        }\n\n        public Location getEnd()\n        {\n            return this.end;\n        }\n\n        public Distance getExpectedMaximumLength()\n        {\n            return this.expectedMaximumLength;\n        }\n\n        public int getExpectedMaximumSize()\n        {\n            return this.expectedMaximumSize;\n        }\n\n        public Location getStart()\n        {\n            return this.start;\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(AtlasRoutingIntegrationTest.class);\n\n    private static final Location ONE = Location.forString(\"20.245996,-74.1502787\");\n    private static final Location TWO = Location.forString(\"20.0774286,-74.4971688\");\n    private static final Location THREE = Location.forString(\"20.6079142,-74.7530228\");\n    private static final Location FOUR = Location.forString(\"20.6649509,-74.9532838\");\n    private static final Location FIVE = Location.forString(\"20.58069,-75.24285\");\n    private static final Location SIX = Location.forString(\"20.13990,-75.20937\");\n\n    private static final List<ExpectedRoute> EXPECTED_ROUTES = new ArrayList<>();\n\n    static\n    {\n        EXPECTED_ROUTES.add(new ExpectedRoute(ONE, TWO, 70_000, 50));\n        EXPECTED_ROUTES.add(new ExpectedRoute(TWO, THREE, 160_000, 150));\n        EXPECTED_ROUTES.add(new ExpectedRoute(THREE, FOUR, 30_000, 80));\n        EXPECTED_ROUTES.add(new ExpectedRoute(FOUR, FIVE, 60_000, 90));\n        EXPECTED_ROUTES.add(new ExpectedRoute(FIVE, SIX, 100_000, 110));\n    }\n\n    private Atlas cuba;\n    private Router router;\n\n    @After\n    public void destroy()\n    {\n        this.cuba = null;\n        this.router = null;\n    }\n\n    @Before\n    public void initialize()\n    {\n        this.cuba = loadCuba();\n        this.router = AStarRouter.fastComputationAndSubOptimalRoute(this.cuba,\n                Distance.meters(100));\n    }\n\n    @Test\n    public void testRouting()\n    {\n        for (final ExpectedRoute expectedRoute : EXPECTED_ROUTES)\n        {\n            final Route route = route(expectedRoute.getStart(), expectedRoute.getEnd());\n            Assert.assertTrue(expectedRoute.getExpectedMaximumSize() >= route.size());\n            Assert.assertTrue(expectedRoute.getExpectedMaximumLength()\n                    .isGreaterThanOrEqualTo(route.length()));\n        }\n    }\n\n    private Route route(final Location start, final Location end)\n    {\n        final Time beginning = Time.now();\n        final Route route = this.router.route(start, end);\n        if (route == null)\n        {\n            throw new CoreException(\"Could not find route between {} and {}.\", start, end);\n        }\n        logger.info(\"Computed route between {} and {}, with {} edges, {} long, in {}\", start, end,\n                route.size(), route.length(), beginning.elapsedSince());\n        return route;\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/boundary/CountryBoundaryMapArchiverIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.io.IOException;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.TemporaryFile;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * Test CountryBoundaryMap generation.\n *\n * @author james-gage\n * @author matthieun\n */\npublic class CountryBoundaryMapArchiverIntegrationTest\n{\n    @Test\n    public void testOceanBoundary() throws IOException\n    {\n        try (TemporaryFile temporary = File.temporary(\"CountryBoundaryMapArchiverTest\", \".txt.gz\"))\n        {\n            final StringList arguments = new StringList();\n            arguments.add(\"-\" + CountryBoundaryMapArchiver.BOUNDARY_FILE.getName() + \"=\"\n                    + CountryBoundaryMapArchiverIntegrationTest.class\n                            .getResource(\"oceanTestBoundary.txt\").getPath());\n\n            arguments.add(\"-\" + CountryBoundaryMapArchiver.OUTPUT.getName() + \"=\"\n                    + temporary.getAbsolutePathString());\n            arguments.add(\n                    \"-\" + CountryBoundaryMapArchiver.OCEAN_BOUNDARY_ZOOM_LEVEL.getName() + \"=3\");\n            arguments\n                    .add(\"-\" + CountryBoundaryMapArchiver.CREATE_SPATIAL_INDEX.getName() + \"=true\");\n            new CountryBoundaryMapArchiver().runWithoutQuitting(arguments.toArray());\n            // ensure that the correct number of ocean boundaries are generated\n            final CountryBoundaryMap oceanBoundaryMap = CountryBoundaryMap.fromPlainText(temporary);\n            Assert.assertEquals(57, oceanBoundaryMap.size());\n        }\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/java/org/openstreetmap/atlas/geography/boundary/CountryBoundaryMapIntegrationTest.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.boundaries.ComplexBoundaryIntegrationTestRule;\n\n/**\n * @author matthieun\n */\npublic class CountryBoundaryMapIntegrationTest\n{\n    // Use the same rule as ComplexBoundaryIntegrationTest\n    @Rule\n    public final ComplexBoundaryIntegrationTestRule rule = new ComplexBoundaryIntegrationTestRule();\n\n    @Test\n    public void testCountryBoundaryMapFromAtlas()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final CountryBoundaryMap map = CountryBoundaryMap.fromAtlas(atlas);\n        Assert.assertEquals(2, map.allCountryNames().size());\n        Assert.assertEquals(\n                \"POLYGON ((-75.2288309 18.3520806, -75.2336999 18.3664654, -75.2384618 18.398702, \"\n                        + \"-75.2377069 18.4312495, -75.2322562 18.4610773, -75.222672 18.4886399, -75.2172678 18.4999677, \"\n                        + \"-75.1993364 18.5289778, -75.1765697 18.5547196, -75.1596528 18.5688721, -75.1492275 18.5767252, \"\n                        + \"-75.1198508 18.5933921, -75.0880291 18.6053034, -75.0546028 18.6121442, -75.0199034 18.6137166, \"\n                        + \"-74.9832495 18.6098363, -74.952276 18.6044011, -74.9225014 18.5946192, -74.893673 18.5801641, \"\n                        + \"-74.8681576 18.5623586, -74.8457299 18.5411084, -74.825893 18.5159259, -74.8084096 18.4854595, \"\n                        + \"-74.7966939 18.4525742, -74.7907319 18.418, -74.790449 18.3896744, -74.7920482 18.3693394, \"\n                        + \"-74.804815 18.320668, -74.8230994 18.285888, -74.848054 18.2551258, -74.8801877 18.2283852, \"\n                        + \"-74.9134925 18.2093121, -74.9498941 18.1964118, -74.9941897 18.1892548, -75.0374985 18.1907252, \"\n                        + \"-75.0814085 18.201251, -75.1219061 18.2195619, -75.1482084 18.2370322, -75.1721527 18.2582151, \"\n                        + \"-75.1923834 18.2826809, -75.2052088 18.3013364, -75.2192144 18.3256833, -75.2288309 18.3520806))\",\n                map.countryBoundary(\"UMI\").get(0).toString());\n    }\n}\n"
  },
  {
    "path": "src/integrationTest/resources/org/openstreetmap/atlas/geography/atlas/raw/layerIntersectionAtEndBoundaryMap.txt",
    "content": "RUS||POLYGON((82.9358393 55.0504618, 82.9440158 55.0514274, 82.9463965 55.0445087, 82.9378292 55.0434533, 82.9358393 55.0504618))"
  },
  {
    "path": "src/integrationTest/resources/org/openstreetmap/atlas/geography/atlas/raw/layerIntersectionAtStartBoundaryMap.txt",
    "content": "RUS||POLYGON((38.7 52.4, 38.8 52.4, 38.8 52.5, 38.7 52.5, 38.7 52.4))"
  },
  {
    "path": "src/integrationTest/resources/org/openstreetmap/atlas/geography/atlas/raw/layerIntersectionInMiddleBoundaryMap.txt",
    "content": "SGP||POLYGON((103.6350863 1.3492437, 103.6863940 1.3448244, 103.6839910 1.2996620, 103.6219767 1.3022755, 103.6350863 1.3492437))"
  },
  {
    "path": "src/integrationTest/resources/org/openstreetmap/atlas/geography/boundary/oceanTestBoundary.txt",
    "content": "ABC||POLYGON((-106.02905273438 -46.1865234375, -129.23217773438 -21.5771484375, -125.71655273438 15.6884765625, -99.700927734375 57.8759765625, -49.075927734375 61.3916015625, 7.174072265625 67.0166015625, 52.877197265625 52.2509765625, 93.658447265625 46.6259765625, 124.59594726563 10.7666015625, 126.00219726563 -20.1708984375, 85.220947265625 -49.7021484375, -4.075927734375 -56.7333984375, -79.310302734375 -59.5458984375, -106.02905273438 -46.1865234375))#\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/event/Event.java",
    "content": "package org.openstreetmap.atlas.event;\n\nimport java.util.Date;\n\n/**\n * Useful base class to hold common information for {@link Event} implementations\n *\n * @author mkalender\n */\npublic abstract class Event\n{\n    private final Date timestamp;\n\n    /**\n     * Default constructor\n     */\n    protected Event()\n    {\n        this.timestamp = new Date();\n    }\n\n    protected Date getTimestamp()\n    {\n        return this.timestamp;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/event/EventService.java",
    "content": "package org.openstreetmap.atlas.event;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.openstreetmap.atlas.utilities.threads.Pool;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.eventbus.EventBus;\n\n/**\n * A simple in-memory publish-subscribe service built on top of {@link EventBus}\n *\n * @param <T>\n *            - event type that is going to be posted\n * @author mkalender\n * @author jklamer\n * @author Yazad Khambata\n */\npublic final class EventService<T extends Event> implements EventServiceable<T>\n{\n    private static final Logger logger = LoggerFactory.getLogger(EventService.class);\n\n    // A key-value mapping for multiple event services\n    private static Map<String, EventService> serviceMap = new ConcurrentHashMap<>();\n\n    // Event bus to dispatch events\n    private final EventBus eventBus;\n\n    // Container of processors on the EventService\n    private final Collection<Processor<T>> processors = new HashSet<>();\n\n    // Thread-safe complete indicator\n    private final AtomicBoolean completed = new AtomicBoolean();\n\n    /**\n     * @param key\n     *            key to retrieve {@link EventService}\n     * @param <T>\n     *            - event type that is going to be posted\n     * @return {@link EventService} instance for given key\n     */\n    public static <T extends Event> EventService get(final String key)\n    {\n        serviceMap.putIfAbsent(key, new EventService<T>());\n        return serviceMap.get(key);\n    }\n\n    private EventService()\n    {\n        this.eventBus = new EventBus((exception, context) -> logger\n                .warn(\"An exception is thrown in EventBus.\", exception));\n    }\n\n    /**\n     * Stops event processing {@link Pool} and posts a {@link ShutdownEvent} event\n     */\n    @Override\n    public void complete()\n    {\n        if (!this.completed.compareAndSet(false, true))\n        {\n            logger.warn(\"EventService is already completed. Skipping completion.\");\n            return;\n        }\n\n        this.eventBus.post(new ShutdownEvent());\n        new HashSet<>(this.processors).forEach(this::unregister);\n    }\n\n    /**\n     * Publishes/posts a new event {@link Object}\n     *\n     * @param event\n     *            {@link Object} to post\n     */\n    @Override\n    public void post(final T event)\n    {\n        if (event == null)\n        {\n            logger.warn(\"EventService received a null event. Skipping posting.\");\n            return;\n        }\n\n        if (this.completed.get())\n        {\n            logger.warn(\"EventService is already completed. Skipping posting.\");\n            return;\n        }\n\n        this.eventBus.post(event);\n    }\n\n    /**\n     * Registers given {@link Processor} to subscribe for events\n     *\n     * @param processor\n     *            {@link Processor} to register\n     */\n    @Override\n    public void register(final Processor<T> processor)\n    {\n        this.eventBus.register(processor);\n        this.processors.add(processor);\n    }\n\n    /**\n     * Unregisters given {@link Processor}\n     *\n     * @param processor\n     *            {@link Processor} to unregister\n     */\n    @Override\n    public void unregister(final Processor<T> processor)\n    {\n        this.eventBus.unregister(processor);\n        this.processors.remove(processor);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/event/EventServiceable.java",
    "content": "package org.openstreetmap.atlas.event;\n\nimport org.openstreetmap.atlas.utilities.threads.Pool;\n\n/**\n * Contract for Event pub-sub implementations.\n *\n * @param <T>\n *            - event type that is going to be posted\n * @author Yazad Khambata\n */\npublic interface EventServiceable<T extends Event>\n{\n\n    /**\n     * Stops event processing {@link Pool} and posts a {@link ShutdownEvent} event\n     */\n    void complete();\n\n    /**\n     * Publishes/posts a new event {@link Object}\n     *\n     * @param event\n     *            {@link Object} to post\n     */\n    void post(T event);\n\n    /**\n     * Registers given {@link Processor} to subscribe for events\n     *\n     * @param processor\n     *            {@link Processor} to register\n     */\n    void register(Processor<T> processor);\n\n    /**\n     * Unregisters given {@link Processor}\n     *\n     * @param processor\n     *            {@link Processor} to unregister\n     */\n    void unregister(Processor<T> processor);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/event/Processor.java",
    "content": "package org.openstreetmap.atlas.event;\n\n/**\n * The {@link Processor} interface provides simple hooks for implementations to handle events.\n *\n * @author mkalender\n * @param <T>\n *            Type that is going to be processed\n */\npublic interface Processor<T extends Event>\n{\n    /**\n     * Method to process {@link ShutdownEvent}. This method will be called only once.<br>\n     * <strong>Please make sure to add {@code @Subscribe} annotation to the method that is\n     * implementing this method.</strong>\n     *\n     * @param event\n     *            {@link ShutdownEvent} to process\n     */\n    void process(ShutdownEvent event);\n\n    /**\n     * Method to process {@link Event}. If your method can process multiple events simultaneously,\n     * then mark your method with {@code @AllowConcurrentEvents} annotation.<strong>Please make sure\n     * to add {@code @Subscribe} annotation to the method that is implementing this method.</strong>\n     *\n     * @param event\n     *            {@link Event} to process\n     */\n    void process(T event);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/event/README.md",
    "content": "# EventService\n\nThe [EventService](EventService.java) is a class build on top of Guava's EventBus with\nstatic keyword construction. It provides an non-blocking endpoint to\nsend events of various types to be processed asynchronously during program execution.\n\n\n# When to use\nWhen you want to record activities of events during program execution\nand have those be processed, recorded, or control other threads on the fly.\n\n# How To Use\n1.   Get an [EventService](EventService.java) object with\n\n```java\npublic static EventService get(final String key)\n```\nAll threads on the same JVM that use the same keyword to get an EventService have the same EventService object.\n\n2.  Extend [Event](Event.java) into a class that contains the message information that is needed.\n3.  Implement the [Processor](Processor.java) interface\n    *   Be sure to include a function that processes your desired event type\n    *   Make sure all [Processor](Processor.java) implementations have the @Subscribe annotation above both their specific event process method and above the ```process(ShutdownEvent)``` method\n4.  Construct and register all [Processor](Processor.java) objects with the EventService before sending events.\n5.  Post [Event](Event.java) objects to the [EventService](EventService.java) to be processed by the appropriate registered [Processor](Processor.java)s.\n6.  At the end of your job or JVM instance call EventService::complete to communicate to the processors that the program execution is complete.\n\n\nThere are multiple and varied flexible use cases for this usage pattern.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/event/ShutdownEvent.java",
    "content": "package org.openstreetmap.atlas.event;\n\n/**\n * An {@link Event} that is posted when {@link EventService} is shutting down\n *\n * @author mkalender\n */\npublic class ShutdownEvent extends Event\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/exception/CoreException.java",
    "content": "package org.openstreetmap.atlas.exception;\n\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.function.UnaryOperator;\n\nimport org.slf4j.helpers.MessageFormatter;\n\n/**\n * This is a universal Exception in Core. Can use substitutions in messages according to very simple\n * substitution rules. Substitutions can be made 1, 2 or more arguments.\n * <p>\n * For example,\n * <p>\n * <code>\n * new CoreException(&quot;Hi {}, how are {}?&quot;, &quot;there&quot;, &quot;you&quot;);\n * </code>\n * <p>\n * is same as\n * <p>\n * <code>\n * new CoreException(&quot;Hi there, how are you?&quot;);\n * </code>\n *\n * @author matthieun\n * @author tony\n * @author Yazad Khambata\n */\npublic class CoreException extends RuntimeException\n{\n    public static final String TOKEN = CoreException.class.getSimpleName();\n    private static final long serialVersionUID = 5019327451085548495L;\n    protected static final UnaryOperator<Object[]> REFINE_ARGUMENTS = arguments ->\n    {\n        if (arguments.length > 0 && arguments[arguments.length - 1] instanceof Throwable)\n        {\n            final Object[] result = new Object[arguments.length - 1];\n            for (int i = 0; i < arguments.length - 1; i++)\n            {\n                result[i] = arguments[i];\n            }\n            return result;\n        }\n        else\n        {\n            return arguments;\n        }\n    };\n    protected static final Function<Object[], Optional<Throwable>> CAUSE_FROM = arguments -> arguments.length != REFINE_ARGUMENTS\n            .apply(arguments).length ? Optional.of((Throwable) arguments[arguments.length - 1])\n                    : Optional.empty();\n\n    public static Supplier<CoreException> supplier(final String message)\n    {\n        return () -> new CoreException(message);\n    }\n\n    public static Supplier<CoreException> supplier(final String message, final Object... arguments)\n    {\n        return () -> new CoreException(message, arguments);\n    }\n\n    public static Supplier<CoreException> supplier(final String message, final Throwable cause)\n    {\n        return () -> new CoreException(message, cause);\n    }\n\n    protected static String messageWithToken(final String message)\n    {\n        final String separator = \"; \";\n\n        return new StringBuilder(TOKEN).append(separator).append(message).toString();\n    }\n\n    public CoreException(final String message)\n    {\n        super(message);\n    }\n\n    /**\n     * Create a new CoreException with a specified message\n     *\n     * @param message\n     *            The message (formatted with {@link MessageFormatter#arrayFormat})\n     * @param arguments\n     *            The arguments (if the <i>last</i> argument is a {@link Throwable}, that becomes\n     *            the cause)\n     */\n    public CoreException(final String message, final Object... arguments)\n    {\n        super(MessageFormatter.arrayFormat(message, REFINE_ARGUMENTS.apply(arguments)).getMessage(),\n                CAUSE_FROM.apply(arguments).orElse(null));\n    }\n\n    public CoreException(final String message, final Throwable cause)\n    {\n        super(message, cause);\n    }\n\n    public CoreException(final String message, final Throwable cause, final Object... arguments)\n    {\n        super(MessageFormatter.arrayFormat(message, arguments).getMessage(), cause);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/exception/ExceptionSearch.java",
    "content": "package org.openstreetmap.atlas.exception;\n\nimport java.util.Optional;\n\n/**\n * Utility class for searching an exception cause chain for a particular exception type\n *\n * @author cstaylor\n * @param <T>\n *            the type of exception we're searching for\n */\npublic final class ExceptionSearch<T extends Throwable>\n{\n    private final Class<T> target;\n\n    public static <T extends Throwable> ExceptionSearch<T> find(final Class<T> target)\n    {\n        if (target == null)\n        {\n            throw new IllegalArgumentException(\"target is null\");\n        }\n        return new ExceptionSearch<>(target);\n    }\n\n    private ExceptionSearch(final Class<T> target)\n    {\n        this.target = target;\n    }\n\n    public Optional<T> within(final Throwable source)\n    {\n        if (this.target == null)\n        {\n            throw new IllegalStateException(\"target is null\");\n        }\n        return Optional.ofNullable(within0(source));\n    }\n\n    private T within0(final Throwable source)\n    {\n        if (source == null)\n        {\n            return null;\n        }\n        if (this.target.isInstance(source))\n        {\n            return this.target.cast(source);\n        }\n        return within0(source.getCause());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/exception/LoadAtlasFromResourceException.java",
    "content": "package org.openstreetmap.atlas.exception;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * Thrown when there's a problem loading an atlas from a packed atlas so we can quickly collect all\n * of the missing or damaged files.\n *\n * @author cstaylor\n */\npublic class LoadAtlasFromResourceException extends CoreException\n{\n    private static final long serialVersionUID = 65439602944966080L;\n\n    private final transient Resource resource;\n\n    public LoadAtlasFromResourceException(final Resource resource, final String message)\n    {\n        super(message);\n        this.resource = resource;\n    }\n\n    public LoadAtlasFromResourceException(final Resource resource, final String message,\n            final Object... arguments)\n    {\n        super(message, arguments);\n        this.resource = resource;\n    }\n\n    public LoadAtlasFromResourceException(final Resource resource, final String message,\n            final Throwable cause)\n    {\n        super(message, cause);\n        this.resource = resource;\n    }\n\n    public LoadAtlasFromResourceException(final Resource resource, final String message,\n            final Throwable cause, final Object... arguments)\n    {\n        super(message, cause, arguments);\n        this.resource = resource;\n    }\n\n    public Resource getResource()\n    {\n        return this.resource;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/exception/change/FeatureChangeMergeException.java",
    "content": "package org.openstreetmap.atlas.exception.change;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.slf4j.helpers.MessageFormatter;\n\n/**\n * A special exception for {@link FeatureChange} merge errors.\n * \n * @author lcram\n */\npublic class FeatureChangeMergeException extends CoreException\n{\n    private static final long serialVersionUID = -3583945839922744755L;\n    static final int MAXIMUM_MESSAGE_SIZE = 2000;\n\n    private final List<MergeFailureType> failureTypeTrace;\n\n    static String truncate(final String input)\n    {\n        return input.substring(0, Math.min(input.length(), MAXIMUM_MESSAGE_SIZE));\n    }\n\n    public FeatureChangeMergeException(final List<MergeFailureType> failureTypeTrace,\n            final String message)\n    {\n        super(truncate(message));\n        this.failureTypeTrace = failureTypeTrace;\n        if (this.failureTypeTrace == null || this.failureTypeTrace.isEmpty())\n        {\n            throw new CoreException(\"failureTypeTrace cannot be null or empty\");\n        }\n    }\n\n    public FeatureChangeMergeException(final List<MergeFailureType> failureTypeTrace,\n            final String message, final Object... arguments)\n    {\n        super(truncate(MessageFormatter.arrayFormat(message, REFINE_ARGUMENTS.apply(arguments))\n                .getMessage()), CAUSE_FROM.apply(arguments).orElse(null));\n        this.failureTypeTrace = failureTypeTrace;\n        if (this.failureTypeTrace == null || this.failureTypeTrace.isEmpty())\n        {\n            throw new CoreException(\"failureTypeTrace cannot be null or empty\");\n        }\n    }\n\n    public FeatureChangeMergeException(final MergeFailureType rootLevelFailure,\n            final String message, final Object... arguments)\n    {\n        super(truncate(MessageFormatter.arrayFormat(message, REFINE_ARGUMENTS.apply(arguments))\n                .getMessage()), CAUSE_FROM.apply(arguments).orElse(null));\n        if (rootLevelFailure == null)\n        {\n            throw new CoreException(\"rootLevelFailure cannot be null\");\n        }\n        this.failureTypeTrace = new ArrayList<>();\n        this.failureTypeTrace.add(rootLevelFailure);\n    }\n\n    public FeatureChangeMergeException(final MergeFailureType rootLevelFailure,\n            final String message)\n    {\n        super(truncate(message));\n        if (rootLevelFailure == null)\n        {\n            throw new CoreException(\"rootLevelFailure cannot be null\");\n        }\n        this.failureTypeTrace = new ArrayList<>();\n        this.failureTypeTrace.add(rootLevelFailure);\n    }\n\n    /**\n     * Return the {@link MergeFailureType} at the provided stack index. If the index is out of\n     * bounds, this will return the top level failure.\n     * \n     * @param index\n     *            the index to check\n     * @return the {@link MergeFailureType} at the provided index, or the most top level failure if\n     *         the index is out of bounds\n     */\n    public MergeFailureType failureAtFrameIndex(final int index)\n    {\n        if (index >= this.failureTypeTrace.size())\n        {\n            return topLevelFailure();\n        }\n        return this.failureTypeTrace.get(index);\n    }\n\n    /**\n     * Get the number of {@link MergeFailureType}s in the failure trace.\n     *\n     * @return the number of {@link MergeFailureType}s in the failure trace\n     */\n    public int failureTraceSize()\n    {\n        return this.failureTypeTrace.size();\n    }\n\n    public List<MergeFailureType> getMergeFailureTrace()\n    {\n        return new ArrayList<>(this.failureTypeTrace);\n    }\n\n    /**\n     * Return the root level {@link MergeFailureType}. This is the bottom-most failure reason, and\n     * will generally be the most specific failure reason available for a given\n     * {@link FeatureChangeMergeException}.\n     *\n     * @return the root level {@link MergeFailureType}\n     */\n    public MergeFailureType rootLevelFailure()\n    {\n        return this.failureTypeTrace.get(0);\n    }\n\n    /**\n     * Return the top level {@link MergeFailureType}. This is the top-most failure reason, and will\n     * generally be the most general failure reason available for a given\n     * {@link FeatureChangeMergeException}.\n     *\n     * @return the top level {@link MergeFailureType}\n     */\n    public MergeFailureType topLevelFailure()\n    {\n        return this.failureTypeTrace.get(this.failureTypeTrace.size() - 1);\n    }\n\n    /**\n     * Check if the failure trace contains an exact failure sequence of {@link MergeFailureType}s,\n     * in level order from root to top. E.g. suppose the failure trace list is [root: A, B, C, D,\n     * top: E], and our provided subsequence is [A, B]. In this case, the method would return true.\n     * Now suppose the provided subsequence is [A, C]. Now, the method will return false. This\n     * method may be useful if callers want to check for and recover from a specific failure\n     * sequence.\n     *\n     * @param subSequence\n     *            the subsequence of {@link MergeFailureType}s to check\n     * @return if the subsequence is present\n     */\n    public boolean traceContainsExactFailureSubSequence(final List<MergeFailureType> subSequence)\n    {\n        if (subSequence.isEmpty())\n        {\n            return true;\n        }\n        if (subSequence.size() > this.failureTypeTrace.size())\n        {\n            return false;\n        }\n\n        for (int i = 0; i < this.failureTypeTrace.size(); i++)\n        {\n            boolean foundSubSequenceThisIteration = true;\n            for (int j = 0, tmpI = i; j < subSequence.size(); j++, tmpI++)\n            {\n                // We hit the end of the failure trace early or we found a sequence mismatch\n                if (tmpI >= this.failureTypeTrace.size()\n                        || subSequence.get(j) != this.failureTypeTrace.get(tmpI))\n                {\n                    foundSubSequenceThisIteration = false;\n                    break;\n                }\n            }\n            if (foundSubSequenceThisIteration)\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Check if this exception trace contains a {@link MergeFailureType} that matches the given\n     * type.\n     *\n     * @param type\n     *            the {@link MergeFailureType} for which to check\n     * @return true if the trace contains the provided type\n     */\n    public boolean traceContainsFailureType(final MergeFailureType type)\n    {\n        for (final MergeFailureType currentType : this.failureTypeTrace)\n        {\n            if (type == currentType)\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Check if the failure trace matches exact failure sequence of {@link MergeFailureType}s, in\n     * level order from root to top.\n     *\n     * @param sequence\n     *            the sequence of {@link MergeFailureType}s to check\n     * @return if the sequence is matches exactly\n     */\n    public boolean traceMatchesExactFailureSequence(final List<MergeFailureType> sequence)\n    {\n        if (sequence == null)\n        {\n            return false;\n        }\n        if (sequence.size() != this.failureTypeTrace.size())\n        {\n            return false;\n        }\n        return this.failureTypeTrace.equals(sequence);\n    }\n\n    /**\n     * Add a new top-level {@link MergeFailureType} to the failure trace.\n     * \n     * @param type\n     *            the {@link MergeFailureType} to add\n     * @return the modified trace\n     */\n    public List<MergeFailureType> withNewTopLevelFailure(final MergeFailureType type)\n    {\n        final List<MergeFailureType> newList = new ArrayList<>(this.failureTypeTrace);\n        newList.add(type);\n        return newList;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/exception/change/MergeFailureType.java",
    "content": "package org.openstreetmap.atlas.exception.change;\n\n/**\n * @author lcram\n */\npublic enum MergeFailureType\n{\n    /*\n     * These are all root level failures.\n     */\n    AUTOFAIL_TAG_MERGE(\"tag Map merge failed due to autofail strategy\"),\n    AUTOFAIL_LONG_SET_MERGE(\"Long Set merge failed due to autofail strategy\"),\n    AUTOFAIL_LONG_SORTED_SET_MERGE(\"Long SortedSet merge failed due to autofail strategy\"),\n    AUTOFAIL_LOCATION_MERGE(\"Location merge failed due to autofail strategy\"),\n    AUTOFAIL_POLYLINE_MERGE(\"PolyLine merge failed due to autofail strategy\"),\n    AUTOFAIL_POLYGON_MERGE(\"Polygon merge failed due to autofail strategy\"),\n    AUTOFAIL_LONG_MERGE(\"Long merge failed due to autofail strategy\"),\n    SIMPLE_TAG_MERGE_FAIL(\"simpleTagMerger failed\"),\n    SIMPLE_LONG_SET_MERGE_FAIL(\"simpleLongSetMerger failed\"),\n    SIMPLE_LONG_SORTED_SET_MERGE_FAIL(\"simpleLongSortedSetMerger failed\"),\n    SIMPLE_RELATION_BEAN_MERGE_FAIL(\"simpleRelationBeanMerger failed\"),\n    DIFF_BASED_RELATION_BEAN_REMOVE_REMOVE_CONFLICT(\n            \"diffBasedRelationBeanMerger failed due to REMOVE/REMOVE conflict\"),\n    DIFF_BASED_RELATION_BEAN_ADD_REMOVE_CONFLICT(\n            \"diffBasedRelationBeanMerger failed due to ADD/REMOVE conflict\"),\n    DIFF_BASED_RELATION_BEAN_ADD_ADD_CONFLICT(\n            \"diffBasedRelationBeanMerger failed due to ADD/ADD conflict\"),\n    DIFF_BASED_TAG_ADD_ADD_CONFLICT(\"diffBasedTagMerger failed due to ADD/ADD conflict\"),\n    DIFF_BASED_TAG_ADD_REMOVE_CONFLICT(\"diffBasedTagMerger failed due to ADD/REMOVE conflict\"),\n    DIFF_BASED_LONG_MERGE_FAIL(\"diffBasedLongMerger failed\"),\n    DIFF_BASED_LOCATION_MERGE_FAIL(\"diffBasedLocationMerger failed\"),\n    DIFF_BASED_POLYLINE_MERGE_FAIL(\"diffBasedPolyLineMerger failed\"),\n    DIFF_BASED_POLYGON_MERGE_FAIL(\"diffBasedPolygonMerger failed\"),\n    CONFLICTING_BEFORE_VIEW_SET_ADD_REMOVE_CONFLICT(\n            \"conflictingBeforeViewSetMerger failed due to ADD/REMOVE conflict\"),\n    CONFLICTING_BEFORE_VIEW_RELATION_BEAN_ADD_REMOVE_CONFLICT(\n            \"conflictingBeforeViewRelationBeanMerger failed due to ADD/REMOVE conflict\"),\n    MUTUALLY_EXCLUSIVE_ADD_ADD_CONFLICT(\n            \"diffBasedMutuallyExclusiveMerger failed due to ADD/ADD conflict\"),\n    FEATURE_CHANGE_INVALID_ADD_REMOVE_MERGE(\n            \"left and right FeatureChanges disagreed on ChangeType\"),\n    FEATURE_CHANGE_INVALID_PROPERTIES_MERGE(\n            \"left and right FeatureChanges disagreed on ID or ItemType\"),\n    FEATURE_CHANGE_IMBALANCED_BEFORE_VIEW(\n            \"left and right FeatureChanges did not both have beforeViews\"),\n\n    /*\n     * These failures occur at the next level up from root. They differentiate between\n     * afterView/beforeView merge errors, and do not contain specific member information.\n     */\n    AFTER_VIEW_NO_BEFORE_VIEW_MERGE_STRATEGY_FAILED(\n            \"the afterView merging function (that ignores beforeView) failed\"),\n    AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED(\n            \"the afterView merging function (that assumes consistent beforeViews) failed\"),\n    AFTER_VIEW_CONFLICTING_BEFORE_VIEW_MERGE_STRATEGY_FAILED(\n            \"the afterView merging function (that accounts for conflicting beforeViews) failed\"),\n    BEFORE_VIEW_MERGE_STRATEGY_FAILED(\"the beforeView merging function failed\"),\n    MISSING_BEFORE_VIEW_MERGE_STRATEGY(\n            \"beforeMembers conflict and no beforeView merging strategy provided\"),\n    MISSING_AFTER_VIEW_MERGE_STRATEGY_WITH_BEFORE_MEMBER_CONFLICT_HANDLING(\n            \"beforeMembers conflict and no beforeView-conflict-capable afterView merging strategy provided\"),\n    MISSING_AFTER_VIEW_MERGE_STRATEGY(\n            \"afterMembers conflict and no afterView merging strategy provided\"),\n\n    /*\n     * The generic highest level merge failure.\n     */\n    HIGHEST_LEVEL_MERGE_FAILURE(\"the FeatureChange merge failed\");\n\n    private final String description;\n\n    MergeFailureType(final String description)\n    {\n        this.description = description;\n    }\n\n    public String getDescription()\n    {\n        return this.description;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Altitude.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.ElevationTag;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * This is the height or elevation (usually defined in meters) above the Earth ellipsoid. The Earth\n * ellipsoid is a mathematical surface defined by a semi-major axis and a semi-minor axis. The most\n * common values for these two parameters are defined by the World Geodetic Standard 1984 (WGS-84).\n * The WGS-84 ellipsoid is intended to correspond to mean sea level. An {@link Altitude} of zero\n * corresponds roughly to sea level, with positive values increasing away from the Earth’s center.\n * Altitude values range from the center of the Earth (see {@link Distance#AVERAGE_EARTH_RADIUS}) to\n * positive infinity. For more detail, see\n * <a href= \"http://danceswithcode.net/engineeringnotes/geodetic_to_ecef/geodetic_to_ecef.html\">\n * here</a>.\n * <p>\n * Please also note that this is NOT the same elevation (height above sea level) as referenced by\n * the {@link ElevationTag} in OSM.\n *\n * @author mgostintsev\n */\npublic final class Altitude implements Serializable\n{\n    private static final long serialVersionUID = -9064525655677062110L;\n\n    public static final Altitude MEAN_SEA_LEVEL = Altitude.meters(0);\n\n    private final Distance distance;\n\n    // The altitude will be negative in the range between the center of the earth and sea level:\n    // [-AVERAGE_EARTH_RADIUS to 0). Even though the underlying altitude is negative, the\n    // representation will be positive to make use of the Distance functionality.\n    private boolean isNegative = false;\n\n    public static Altitude meters(final double meters)\n    {\n        return new Altitude(meters);\n    }\n\n    private Altitude(final double meters)\n    {\n        if (meters < 0)\n        {\n            if (-meters > Distance.AVERAGE_EARTH_RADIUS.asMeters())\n            {\n                throw new CoreException(\"Cannot have an altitude below the center of the Earth.\");\n            }\n            this.isNegative = true;\n            this.distance = Distance.meters(-meters);\n        }\n        else\n        {\n            this.distance = Distance.meters(meters);\n        }\n    }\n\n    public double asMeters()\n    {\n        return this.isNegative ? -this.distance.asMeters() : this.distance.asMeters();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof Altitude)\n        {\n            final Altitude that = (Altitude) other;\n            return this.asMeters() == that.asMeters();\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Double.hashCode(this.asMeters());\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.valueOf(this.asMeters());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/CompressedPolyLine.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * A {@link PolyLine} that is compressed using delta encoding. This is efficient when the\n * {@link PolyLine} has a lot of points close by.\n *\n * @author matthieun\n */\npublic class CompressedPolyLine implements Located, Serializable\n{\n    /**\n     * @author matthieun\n     */\n    private static class ByteSign\n    {\n        private final byte[] bytes;\n        private final boolean sign;\n\n        ByteSign(final byte[] bytes, final boolean sign)\n        {\n            this.bytes = bytes;\n            this.sign = sign;\n        }\n\n        public byte[] getBytes()\n        {\n            return this.bytes;\n        }\n\n        public boolean isSign()\n        {\n            return this.sign;\n        }\n    }\n\n    private static final long serialVersionUID = -7813027521625470225L;\n\n    private static final int BYTE_FULL_MASK = 0xFF;\n    private static final int BYTE_SIZE = 8;\n    private static final int INT_SIGN_MASK = 0x80000000;\n    private static final int INT_NO_SIGN_MASK = 0x7FFFFFFF;\n\n    private final byte[][] positions;\n\n    private final boolean[] signs;\n\n    public CompressedPolyLine(final byte[][] positions, final boolean[] signs)\n    {\n        this.positions = positions;\n        this.signs = signs;\n    }\n\n    /**\n     * Create a compressed version of a {@link PolyLine}\n     *\n     * @param polyLine\n     *            The {@link PolyLine} to compress.\n     */\n    public CompressedPolyLine(final PolyLine polyLine)\n    {\n        final List<byte[]> positions = new ArrayList<>();\n        final List<Boolean> signs = new ArrayList<>();\n        int formerLatitude = 0;\n        int formerLongitude = 0;\n        for (final Location location : polyLine)\n        {\n            final int latitude = (int) location.getLatitude().asDm7();\n            final int longitude = (int) location.getLongitude().asDm7();\n            final int deltaLatitude = latitude - formerLatitude;\n            final int deltaLongitude = longitude - formerLongitude;\n            formerLatitude = latitude;\n            formerLongitude = longitude;\n            final ByteSign latShrink = shrink(deltaLatitude);\n            final ByteSign lonShrink = shrink(deltaLongitude);\n            positions.add(latShrink.getBytes());\n            signs.add(latShrink.isSign());\n            positions.add(lonShrink.getBytes());\n            signs.add(lonShrink.isSign());\n        }\n        this.positions = new byte[positions.size()][];\n        for (int i = 0; i < positions.size(); i++)\n        {\n            this.positions[i] = positions.get(i);\n        }\n        this.signs = new boolean[signs.size()];\n        for (int i = 0; i < signs.size(); i++)\n        {\n            this.signs[i] = signs.get(i);\n        }\n    }\n\n    /**\n     * @return An expanded {@link PolyLine}\n     */\n    public PolyLine asPolyLine()\n    {\n        boolean lat = true;\n        int latitude = 0;\n        int longitude = 0;\n        final List<Location> locations = new ArrayList<>();\n        for (int index = 0; index < this.positions.length; index++)\n        {\n            final byte[] result = this.positions[index];\n            if (lat)\n            {\n                latitude += expand(result, index);\n                lat = false;\n            }\n            else\n            {\n                longitude += expand(result, index);\n                locations.add(new Location(Latitude.dm7(latitude), Longitude.dm7(longitude)));\n                lat = true;\n            }\n        }\n        return new PolyLine(locations);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return asPolyLine().bounds();\n    }\n\n    public byte[][] getPositions()\n    {\n        return this.positions;\n    }\n\n    public boolean[] getSigns()\n    {\n        return this.signs;\n    }\n\n    @Override\n    public String toString()\n    {\n        return asPolyLine().toString();\n    }\n\n    /**\n     * Transform an array of bytes into an int. If the bytes are 0x4A and 0x0F, with a negative sign\n     * (from the index in the signs array) the returned int will be 0x80000F4A.\n     *\n     * @param result\n     *            The shrunk value\n     * @param index\n     *            The index of the sign\n     * @return The expanded value\n     */\n    private int expand(final byte[] result, final int index)\n    {\n        int placeholder = 0;\n        for (int i = 0; i < result.length; i++)\n        {\n            final byte byteValue = result[i];\n            placeholder |= byteValue & BYTE_FULL_MASK;\n            if (i < result.length - 1)\n            {\n                placeholder <<= BYTE_SIZE;\n            }\n        }\n        final boolean negative = this.signs[index];\n        if (negative)\n        {\n            placeholder |= INT_SIGN_MASK;\n        }\n        return placeholder;\n    }\n\n    /**\n     * Browse the value, byte after byte, and keep only the bytes that have significance. So an int\n     * 0x80000F4A will return an array of two bytes, 0x4A and 0x0F, and a negative sign. All the 0\n     * bytes will be thrown out.\n     *\n     * @param value\n     *            The value to shrink\n     * @return The shrunk value.\n     */\n    private ByteSign shrink(final int value)\n    {\n        // Get rid of the sign\n        int placeholder = value & INT_NO_SIGN_MASK;\n        final List<Byte> bytes = new ArrayList<>();\n        while (Math.abs(placeholder) > 0)\n        {\n            final byte byteValue = (byte) placeholder;\n            bytes.add(byteValue);\n            placeholder >>>= BYTE_SIZE;\n        }\n        final int size = bytes.size();\n        final byte[] result = new byte[size];\n        for (int i = 0; i < size; i++)\n        {\n            result[i] = bytes.get(size - 1 - i);\n        }\n        return new ByteSign(result, value < 0);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/CompressedPolygon.java",
    "content": "package org.openstreetmap.atlas.geography;\n\n/**\n * Compressed {@link Polygon}. This simply extends {@link CompressedPolyLine}\n *\n * @author matthieun\n */\npublic class CompressedPolygon extends CompressedPolyLine\n{\n    private static final long serialVersionUID = 2762361356248033855L;\n\n    public CompressedPolygon(final byte[][] positions, final boolean[] signs)\n    {\n        super(positions, signs);\n    }\n\n    public CompressedPolygon(final Polygon polygon)\n    {\n        super(polygon);\n    }\n\n    public Polygon asPolygon()\n    {\n        return new Polygon(asPolyLine());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/GeometricObject.java",
    "content": "package org.openstreetmap.atlas.geography;\n\n/**\n * @author matthieun\n */\npublic interface GeometricObject\n{\n    double SIMILARITY_THRESHOLD = 0.9999999;\n\n    boolean intersects(PolyLine other);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/GeometricSurface.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\n\n/**\n * An interface for all geometric surface objects\n *\n * @author jklamer\n */\npublic interface GeometricSurface extends Located, GeometryPrintable, GeometricObject\n{\n    boolean fullyGeometricallyEncloses(Location location);\n\n    boolean fullyGeometricallyEncloses(MultiPolygon multiPolygon);\n\n    boolean fullyGeometricallyEncloses(PolyLine polyLine);\n\n    boolean overlaps(MultiPolygon multiPolygon);\n\n    boolean overlaps(PolyLine polyLine);\n\n    /**\n     * @return The {@link Surface} of this {@link GeometricSurface}. Not valid if the\n     *         {@link GeometricSurface} self-intersects, and/or overlaps itself\n     * @see \"http://www.mathopenref.com/coordpolygonarea2.html\"\n     */\n    Surface surface();\n\n    /**\n     * @return The approximate {@link Surface} of this {@link GeometricSurface} if it were projected\n     *         onto the Earth. Not valid if the {@link GeometricSurface} self-intersects, and/or\n     *         overlaps itself. Uses \"Some Algorithms for Polygons on a Sphere\" paper as reference.\n     * @see \"https://trs.jpl.nasa.gov/bitstream/handle/2014/41271/07-0286.pdf\"\n     */\n    Surface surfaceOnSphere();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/GeometryPrintable.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.geography.geojson.GeoJson;\n\n/**\n * @author matthieun\n */\npublic interface GeometryPrintable extends GeoJson, WktPrintable, WkbPrintable\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Heading.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\n\n/**\n * A Heading, that is between 0 included and 360 degrees excluded, using the standard 0 = north, 90\n * = east, 180 = south and 270 = west. Heading's degree circle starts from 0 and increases in the\n * clock-wise direction. Heading's circle is shifted forward 180 degrees (with\n * {@link Heading#DELTA_DM7}) compared to Angle's degree circle.\n *\n * @author matthieun\n */\npublic class Heading extends Angle\n{\n    private static final long serialVersionUID = -7621572408790801458L;\n\n    public static final Heading NORTH = Heading.dm7(0L);\n    public static final Heading SOUTH = Heading.dm7(1_800_000_000L);\n    public static final Heading EAST = Heading.dm7(900_000_000L);\n    public static final Heading WEST = Heading.dm7(2_700_000_000L);\n\n    /**\n     * Delta between {@link Angle}'s degree circle and {@link Heading}'s degree circle\n     */\n    protected static final int DELTA_DM7 = 1_800_000_000;\n\n    /**\n     * @param degrees\n     *            A heading value in degrees\n     * @return The built {@link Heading} object using the degrees value\n     */\n    public static Heading degrees(final double degrees)\n    {\n        return dm7(Math.round(degrees * DM7_PER_DEGREE));\n    }\n\n    /**\n     * @param dm7\n     *            A heading value in degree of magnitude 7 (dm7)\n     * @return The built {@link Heading} object using the dm7 value\n     */\n    public static Heading dm7(final long dm7)\n    {\n        // Roll dm7 value from 0->360 to the -180->180 that the angle expects.\n        // Heading's circle is 180 degree ahead of Angle's\n        long rollingDm7 = (dm7 - DELTA_DM7) % REVOLUTION_DM7;\n\n        // After the roll operation, dm7 value could fall into (-180, -360) degrees.\n        // This addition of 360 degrees will shift that degree back into (0, 180)\n        if (rollingDm7 < MINIMUM_DM7)\n        {\n            rollingDm7 += REVOLUTION_DM7;\n        }\n\n        // After the roll operation, dm7 value could fall into [180, 360) degrees.\n        // This subtraction of 360 degrees will shift that degree back into [-180, 0)\n        if (rollingDm7 >= MAXIMUM_DM7)\n        {\n            rollingDm7 -= REVOLUTION_DM7;\n        }\n        return new Heading((int) rollingDm7);\n    }\n\n    /**\n     * @param radians\n     *            A heading value in Radians\n     * @return The built {@link Heading} object using the Radians value\n     */\n    public static Heading radians(final double radians)\n    {\n        return dm7(Math.round(radians * DM7_PER_RADIAN));\n    }\n\n    protected Heading(final int dm7)\n    {\n        super(dm7);\n    }\n\n    @Override\n    public long asDm7()\n    {\n        // Override to scale back to 0->360\n        return super.asDm7() + DELTA_DM7;\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.valueOf(this.asDegrees());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Latitude.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\n\n/**\n * A Latitude between -90 degrees and +90 degrees, both included.\n *\n * @author matthieun\n * @author tony\n */\npublic class Latitude extends Angle\n{\n    private static final long serialVersionUID = -1737858321716005802L;\n\n    protected static final int MINIMUM_DM7 = -900_000_000;\n    protected static final int MAXIMUM_DM7 = 900_000_000;\n\n    public static final Latitude MINIMUM = Latitude.dm7(MINIMUM_DM7);\n    public static final Latitude ZERO = Latitude.dm7(0L);\n    public static final Latitude MAXIMUM = Latitude.dm7(MAXIMUM_DM7);\n\n    /**\n     * @param degrees\n     *            A Latitude value in degrees\n     * @return The built {@link Latitude} object using the degrees value\n     */\n    public static Latitude degrees(final double degrees)\n    {\n        return dm7(Math.round(degrees * DM7_PER_DEGREE));\n    }\n\n    /**\n     * @param dm7\n     *            A latitude value in degree of magnitude 7 (dm7)\n     * @return The built {@link Latitude} object using the dm7 value\n     */\n    public static Latitude dm7(final long dm7)\n    {\n        if (dm7 < MINIMUM_DM7 || dm7 > MAXIMUM_DM7)\n        {\n            throw new IllegalArgumentException(\"Cannot have a latitude of \" + dm7 / DM7_PER_DEGREE\n                    + \" degrees which is outside of \" + MINIMUM_DM7 / DM7_PER_DEGREE\n                    + \" degrees -> \" + MAXIMUM_DM7 / DM7_PER_DEGREE + \" degrees.\");\n        }\n        return new Latitude((int) dm7);\n    }\n\n    /**\n     * @param radians\n     *            A Latitude value in Radians\n     * @return The built {@link Latitude} object using the Radians value\n     */\n    public static Latitude radians(final double radians)\n    {\n        return dm7(Math.round(radians * DM7_PER_RADIAN));\n    }\n\n    /**\n     * If the given radian exceeds the latitude boundary, will return the boundary value.\n     *\n     * @param radians\n     *            The radian of latitude\n     * @return The adjusted latitude if exceeds the boundary, otherwise the normal latitude\n     */\n    public static Latitude radiansBounded(final double radians)\n    {\n        long dm7 = Math.round(radians * DM7_PER_RADIAN);\n        if (dm7 < MINIMUM_DM7)\n        {\n            dm7 = MINIMUM_DM7;\n        }\n        if (dm7 > MAXIMUM_DM7)\n        {\n            dm7 = MAXIMUM_DM7;\n        }\n        return dm7(dm7);\n    }\n\n    /**\n     * Constructor\n     *\n     * @param dm7\n     *            The latitude value in dm7\n     */\n    protected Latitude(final int dm7)\n    {\n        super(dm7);\n        if (dm7 < MINIMUM_DM7 || dm7 > MAXIMUM_DM7)\n        {\n            throw new IllegalArgumentException(\"Invalid Latitude microdegrees value: \" + dm7);\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.valueOf(this.asDegrees());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Located.java",
    "content": "package org.openstreetmap.atlas.geography;\n\n/**\n * Contract for any item that can be geographcally bound by a {@link Rectangle}\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic interface Located\n{\n    /**\n     * @return The bounds around this located object\n     */\n    Rectangle bounds();\n\n    /**\n     * Return {@code true} if surface fully geometrically encloses {@code this}.\n     * <p>\n     * For backward compatibility a default implementation that fails is added.\n     *\n     * @param surface\n     *            - check if {@code this} is fully within the surface.\n     * @return - {@code true} if fully within surface, false otherwise.\n     */\n    default boolean within(GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Location.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.awt.Point;\nimport java.awt.geom.Point2D;\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.Random;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Snapper.SnappedLocation;\nimport org.openstreetmap.atlas.geography.converters.WkbLocationConverter;\nimport org.openstreetmap.atlas.geography.converters.WktLocationConverter;\nimport org.openstreetmap.atlas.geography.coordinates.EarthCenteredEarthFixedCoordinate;\nimport org.openstreetmap.atlas.geography.coordinates.GeodeticCoordinate;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonGeometry;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Location on the surface of the earth\n *\n * @author matthieun\n * @author mgostintsev\n */\npublic class Location implements Located, Iterable<Location>, Serializable, GeometryPrintable,\n        GeoJsonGeometry, GeometricObject\n{\n    public static final String TEST_1_COORDINATES = \"37.335310,-122.009566\";\n    public static final String TEST_2_COORDINATES = \"37.321628,-122.028464\";\n    public static final String TEST_3_COORDINATES = \"37.317585,-122.052138\";\n    public static final String TEST_4_COORDINATES = \"37.332451,-122.028932\";\n    public static final String TEST_5_COORDINATES = \"37.390535,-122.031007\";\n    public static final String TEST_6_COORDINATES = \"37.325440,-122.033948\";\n    public static final String TEST_7_COORDINATES = \"37.3314171,-122.0304871\";\n    public static final String TEST_8_COORDINATES = \"37.3214159,-122.0303831\";\n    // Quick-access locations, mostly used for testing.\n    public static final Location TEST_1 = Location.forString(TEST_1_COORDINATES);\n    public static final Location TEST_2 = Location.forString(TEST_2_COORDINATES);\n    public static final Location TEST_3 = Location.forString(TEST_3_COORDINATES);\n    public static final Location TEST_4 = Location.forString(TEST_4_COORDINATES);\n    public static final Location TEST_5 = Location.forString(TEST_5_COORDINATES);\n    public static final Location TEST_6 = Location.forString(TEST_6_COORDINATES);\n    public static final Location TEST_7 = Location.forString(TEST_7_COORDINATES);\n    public static final Location TEST_8 = Location.forString(TEST_8_COORDINATES);\n    public static final Location STEVENS_CREEK = Location.forString(\"37.324233,-122.003467\");\n    public static final Location CROSSING_85_280 = Location.forString(\"37.332439,-122.055760\");\n    public static final Location CROSSING_85_17 = Location.forString(\"37.255731,-121.955918\");\n    public static final Location EIFFEL_TOWER = Location.forString(\"48.858241,2.294495\");\n    public static final Location COLOSSEUM = Location.forString(\"41.890224,12.492340\");\n    public static final Location CENTER = new Location(0L);\n    private static final long serialVersionUID = 3770424147251047128L;\n    private static final int INT_FULL_MASK = 0xFFFFFFFF;\n    private static final long INT_FULL_MASK_AS_LONG = 0xFFFFFFFFL;\n    private static final int INT_SIZE = 32;\n    private static final int FACTOR_OF_3 = 3;\n    private static final Random RANDOM = new Random();\n\n    private final Latitude latitude;\n    private final Longitude longitude;\n\n    /**\n     * @param locationString\n     *            The {@link Location} as a {@link String} in \"latitude(degrees),longitude(degrees)\"\n     *            format\n     * @return The corresponding {@link Location}\n     */\n    public static Location forString(final String locationString)\n    {\n        final StringList split = StringList.split(locationString, \",\");\n        if (split.size() != 2)\n        {\n            throw new CoreException(\"Invalid Location String: {}\", locationString);\n        }\n        final double latitude = Double.parseDouble(split.get(0));\n        final double longitude = Double.parseDouble(split.get(1));\n        return new Location(Latitude.degrees(latitude), Longitude.degrees(longitude));\n    }\n\n    /**\n     * @param locationString\n     *            The {@link Location} as a {@link String} in \"longitude(degrees),latitude(degrees)\"\n     *            format\n     * @return The corresponding {@link Location}\n     */\n    public static Location forStringLongitudeLatitude(final String locationString)\n    {\n        final StringList split = StringList.split(locationString, \",\");\n        if (split.size() != 2)\n        {\n            throw new CoreException(\"Invalid Location String: {}\", locationString);\n        }\n        final double latitude = Double.parseDouble(split.get(1));\n        final double longitude = Double.parseDouble(split.get(0));\n        return new Location(Latitude.degrees(latitude), Longitude.degrees(longitude));\n    }\n\n    /**\n     * @param wkt\n     *            The {@link Location} as a Well Known Text (WKT) {@link String} format\n     * @return The corresponding {@link Location}\n     */\n    public static Location forWkt(final String wkt)\n    {\n        return new WktLocationConverter().backwardConvert(wkt);\n    }\n\n    /**\n     * @param bounds\n     *            Bounds to constrain the result\n     * @return A random location within the bounds\n     */\n    public static Location random(final Rectangle bounds)\n    {\n        final int latitude = RANDOM.ints((int) bounds.lowerLeft().getLatitude().asDm7(),\n                (int) bounds.upperRight().getLatitude().asDm7()).iterator().next();\n        final int longitude = RANDOM.ints((int) bounds.lowerLeft().getLongitude().asDm7(),\n                (int) bounds.upperRight().getLongitude().asDm7()).iterator().next();\n        return new Location(Latitude.dm7(latitude), Longitude.dm7(longitude));\n    }\n\n    /**\n     * Build a {@link Location} from a {@link Latitude} and a {@link Longitude} objects.\n     *\n     * @param latitude\n     *            The {@link Latitude} to use\n     * @param longitude\n     *            The {@link Longitude} to use\n     */\n    public Location(final Latitude latitude, final Longitude longitude)\n    {\n        if (latitude == null)\n        {\n            throw new CoreException(\"Latitude is null.\");\n        }\n        if (longitude == null)\n        {\n            throw new CoreException(\"Longitude is null.\");\n        }\n        this.latitude = latitude;\n        this.longitude = longitude;\n    }\n\n    /**\n     * Copy constructor for {@link Location}\n     *\n     * @param other\n     *            the {@link Location} from which to copy\n     */\n    public Location(final Location other)\n    {\n        if (other == null)\n        {\n            throw new CoreException(\"Other Location was null\");\n        }\n        this.latitude = other.latitude;\n        this.longitude = other.longitude;\n    }\n\n    /**\n     * Create a location from a dm7 latitude and dm7 longitude concatenated in a long\n     *\n     * @param concatenation\n     *            The first 32 bits are for the dm7 latitude, and the last 32 bits are for the dm7\n     *            longitude.\n     */\n    public Location(final long concatenation)\n    {\n        final int lon = (int) concatenation;\n        final int lat = (int) (concatenation >>> INT_SIZE) & INT_FULL_MASK;\n        this.longitude = Longitude.dm7(lon);\n        this.latitude = Latitude.dm7(lat);\n    }\n\n    /**\n     * @return A dm7 latitude and dm7 longitude concatenated in a long. The first 32 bits are for\n     *         the dm7 latitude, and the last 32 bits are for the dm7 longitude.\n     */\n    public long asConcatenation()\n    {\n        long result = this.latitude.asDm7();\n        result <<= INT_SIZE;\n        result |= this.longitude.asDm7() & INT_FULL_MASK_AS_LONG;\n        return result;\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        return GeoJsonUtils.geometry(GeoJsonType.POINT, GeoJsonUtils.coordinate(this));\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return Rectangle.forCorners(this, this);\n    }\n\n    /**\n     * Get a {@link Rectangle} around this {@link Location}\n     *\n     * @param extension\n     *            The height of the 1/2 {@link Rectangle}. The height of the total {@link Rectangle}\n     *            will be twice that. Same for width.\n     * @return The {@link Rectangle} around this {@link Location}\n     */\n    public Rectangle boxAround(final Distance extension)\n    {\n        final Location north = this.shiftAlongGreatCircle(Heading.NORTH, extension);\n        final Location south = this.shiftAlongGreatCircle(Heading.SOUTH, extension);\n        final Location east = this.shiftAlongGreatCircle(Heading.EAST, extension);\n        final Location west = this.shiftAlongGreatCircle(Heading.WEST, extension);\n        return Rectangle.forLocations(north, south, east, west);\n    }\n\n    /**\n     * @param that\n     *            The other {@link Location} to compute the {@link Distance} to\n     * @return The {@link Distance} between the two {@link Location}\n     */\n    public Distance distanceTo(final Location that)\n    {\n        // Do a quick check on Longitude\n        if (this.getLongitude().isCloserViaAntimeridianTo(that.getLongitude()))\n        {\n            // Use the method that is not annoyed by the antimeridian\n            return haversineDistanceTo(that);\n        }\n        return equirectangularDistanceTo(that);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof Location)\n        {\n            final Location that = (Location) other;\n            return this.getLatitude().equals(that.getLatitude())\n                    && this.getLongitude().equals(that.getLongitude());\n        }\n        return false;\n    }\n\n    /**\n     * An equirectangular approximation distance between two locations, better performance but less\n     * accurate. It is especially not able to handle distances that cross the antimeridian. It would\n     * compute the distance all the way around the world instead.\n     *\n     * @param that\n     *            The other point to compute the distance to\n     * @return The equirectangular distance\n     * @see \"http://www.movable-type.co.uk/scripts/latlong.html\"\n     */\n    public Distance equirectangularDistanceTo(final Location that)\n    {\n        // convert to radians\n        final double lat1 = this.getLatitude().asRadians();\n        final double lon1 = this.getLongitude().asRadians();\n        final double lat2 = that.getLatitude().asRadians();\n        final double lon2 = that.getLongitude().asRadians();\n\n        final double xAxis = (lon2 - lon1) * Math.cos((lat1 + lat2) / 2);\n        final double yAxis = lat2 - lat1;\n\n        return Distance.AVERAGE_EARTH_RADIUS.scaleBy(Math.sqrt(xAxis * xAxis + yAxis * yAxis));\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.POINT;\n    }\n\n    /**\n     * @return This {@link Location}'s {@link Latitude}\n     */\n    public Latitude getLatitude()\n    {\n        return this.latitude;\n    }\n\n    /**\n     * @return This {@link Location}'s {@link Longitude}\n     */\n    public Longitude getLongitude()\n    {\n        return this.longitude;\n    }\n\n    /**\n     * @param other\n     *            The other {@link Location} to test\n     * @return True if this {@link Location} and the other {@link Location} to test are on the same\n     *         East-West line.\n     */\n    public boolean hasSameLatitudeAs(final Location other)\n    {\n        return this.getLatitude().equals(other.getLatitude());\n    }\n\n    /**\n     * @param other\n     *            The other {@link Location} to test\n     * @return True if this {@link Location} and the other {@link Location} to test are on the same\n     *         North-South line.\n     */\n    public boolean hasSameLongitudeAs(final Location other)\n    {\n        return this.getLongitude().equals(other.getLongitude());\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (this.latitude == null ? 0 : this.latitude.hashCode());\n        result = prime * result + (this.longitude == null ? 0 : this.longitude.hashCode());\n        return result;\n    }\n\n    /**\n     * This uses the ‘haversine’ formula to calculate the great-circle distance between two\n     * locations, more calculation but more accurate\n     *\n     * @param that\n     *            The other point to compute the distance to\n     * @return The haversine distance\n     * @see \"http://www.movable-type.co.uk/scripts/latlong.html\"\n     */\n    public Distance haversineDistanceTo(final Location that)\n    {\n        // convert to radians\n        final double lat1 = this.getLatitude().asRadians();\n        final double lon1 = this.getLongitude().asRadians();\n        final double lat2 = that.getLatitude().asRadians();\n        final double lon2 = that.getLongitude().asRadians();\n\n        final double deltaLat = lat2 - lat1;\n        final double deltaLon = lon2 - lon1;\n\n        final double hav = Math.pow(Math.sin(deltaLat / 2), 2)\n                + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon / 2), 2);\n        final double result = 2 * Math.atan2(Math.sqrt(hav), Math.sqrt(1 - hav));\n        return Distance.AVERAGE_EARTH_RADIUS.scaleBy(result);\n    }\n\n    /**\n     * This computes the initial heading (heading at the start point) of the segment on the surface\n     * of earth between two locations\n     *\n     * @see \"http://www.movable-type.co.uk/scripts/latlong.html\"\n     * @param that\n     *            The other point to compute the heading to\n     * @return The heading between two points\n     */\n    public Heading headingTo(final Location that)\n    {\n        if (this.equals(that))\n        {\n            throw new CoreException(\"Cannot compute some heading when two points are the same.\");\n        }\n\n        // convert to radians\n        final double lat1 = this.getLatitude().asRadians();\n        final double lon1 = this.getLongitude().asRadians();\n        final double lat2 = that.getLatitude().asRadians();\n        final double lon2 = that.getLongitude().asRadians();\n\n        final double deltaLon = lon2 - lon1;\n\n        final double yAxis = Math.sin(deltaLon) * Math.cos(lat2);\n        final double xAxis = Math.cos(lat1) * Math.sin(lat2)\n                - Math.sin(lat1) * Math.cos(lat2) * Math.cos(deltaLon);\n        return Heading.radians(Math.atan2(yAxis, xAxis));\n    }\n\n    @Override\n    public boolean intersects(final PolyLine polyLine)\n    {\n        return polyLine.intersects(new Segment(this, this));\n    }\n\n    /**\n     * @param other\n     *            The other {@link Location} to test\n     * @return True if this {@link Location} is east of the other {@link Location}.\n     */\n    public boolean isEastOf(final Location other)\n    {\n        return this.getLongitude().isGreaterThan(other.getLongitude());\n    }\n\n    /**\n     * @param other\n     *            The other {@link Location} to test\n     * @return True if this {@link Location} is east or on the same {@link Longitude} as the other\n     *         {@link Location}.\n     */\n    public boolean isEastOfOrOnTheSameLatitudeAs(final Location other)\n    {\n        return this.getLongitude().isGreaterThanOrEqualTo(other.getLongitude());\n    }\n\n    /**\n     * @param other\n     *            The other {@link Location} to test\n     * @return True if this {@link Location} is north of the other {@link Location}.\n     */\n    public boolean isNorthOf(final Location other)\n    {\n        return this.getLatitude().isGreaterThan(other.getLatitude());\n    }\n\n    /**\n     * @param other\n     *            The other {@link Location} to test\n     * @return True if this {@link Location} is north or on the same {@link Latitude} as the other\n     *         {@link Location}.\n     */\n    public boolean isNorthOfOrOnTheSameLatitudeAs(final Location other)\n    {\n        return this.getLatitude().isGreaterThanOrEqualTo(other.getLatitude());\n    }\n\n    @Override\n    public Iterator<Location> iterator()\n    {\n        return Iterables.from(this).iterator();\n    }\n\n    /**\n     * Midpoint along a Rhumb line between this point and that point\n     *\n     * @param that\n     *            The other point to compute the midpoint between\n     * @return The {@link Location} of the loxodromic midpoint\n     * @see \"http://www.movable-type.co.uk/scripts/latlong.html\"\n     */\n    public Location loxodromicMidPoint(final Location that)\n    {\n        // Convert to Radians\n        final double lat1 = this.getLatitude().asRadians();\n        double lon1 = this.getLongitude().asRadians();\n        final double lat2 = that.getLatitude().asRadians();\n        final double lon2 = that.getLongitude().asRadians();\n\n        // Crossing anti-meridian\n        if (Math.abs(lon2 - lon1) > Math.PI)\n        {\n            lon1 += 2 * Math.PI;\n        }\n\n        final double pheta = (lat1 + lat2) / 2;\n        final double phi1 = Math.tan(Math.PI / 4 + lat1 / 2);\n        final double phi2 = Math.tan(Math.PI / 4 + lat2 / 2);\n        final double phi3 = Math.tan(Math.PI / 4 + pheta / 2);\n        double lambda = ((lon2 - lon1) * Math.log(phi3) + lon1 * Math.log(phi2)\n                - lon2 * Math.log(phi1)) / Math.log(phi2 / phi1);\n\n        // Locations on the same circle of latitude do not produce a finite lambda value above.\n        // Locations at the same longitude (especially the antimeridian) should preserve their sign.\n        // All other locations should be be normalized within [-180, +180).\n        if (!Double.isFinite(lambda) || lon1 == lon2)\n        {\n            lambda = (lon1 + lon2) / 2;\n        }\n        else\n        {\n            lambda = (lambda + FACTOR_OF_3 * Math.PI) % (2 * Math.PI) - Math.PI;\n        }\n\n        return new Location(Latitude.radians(pheta), Longitude.radians(lambda));\n    }\n\n    /**\n     * The half-way point along a great-circle path between this and that point\n     *\n     * @param that\n     *            The other point to compute the midpoint between\n     * @return The {@link Location} of the midpoint\n     * @see \"http://www.movable-type.co.uk/scripts/latlong.html\"\n     */\n    public Location midPoint(final Location that)\n    {\n        // Convert to Radians\n        final double lat1 = this.getLatitude().asRadians();\n        final double lon1 = this.getLongitude().asRadians();\n        final double lat2 = that.getLatitude().asRadians();\n        final double lon2 = that.getLongitude().asRadians();\n\n        final double longitudeDelta = lon2 - lon1;\n\n        final double xBearing = Math.cos(lat2) * Math.cos(longitudeDelta);\n        final double yBearing = Math.cos(lat2) * Math.sin(longitudeDelta);\n\n        final double pheta = Math.atan2(Math.sin(lat1) + Math.sin(lat2), Math.sqrt(\n                (Math.cos(lat1) + xBearing) * (Math.cos(lat1) + xBearing) + yBearing * yBearing));\n        double lambda = lon1 + Math.atan2(yBearing, Math.cos(lat1) + xBearing);\n\n        // Normalize to -180/180\n        lambda = (lambda + FACTOR_OF_3 * Math.PI) % (2 * Math.PI) - Math.PI;\n        if (this.getLongitude().equals(Longitude.MAXIMUM)\n                && that.getLongitude().equals(Longitude.MAXIMUM))\n        {\n            lambda *= -1;\n        }\n        return new Location(Latitude.radians(pheta), Longitude.radians(lambda));\n    }\n\n    /**\n     * Shift a location along a great circle. Note that if the shifted location exceeds the boundary\n     * of latitude (-90 to 90 degrees) or longitude (-180 to 180 degrees), it will use the boundary\n     * instead\n     *\n     * @param initialHeading\n     *            Initial heading\n     * @param distance\n     *            Distance along the great circle\n     * @return The shifted location\n     * @see \"http://www.movable-type.co.uk/scripts/latlong.html\"\n     */\n    public Location shiftAlongGreatCircle(final Heading initialHeading, final Distance distance)\n    {\n        if (Distance.ZERO.equals(distance))\n        {\n            return this;\n        }\n        // convert to radians\n        final double latitude1 = this.getLatitude().asRadians();\n        final double longitude1 = this.getLongitude().asRadians();\n        final double bearing = initialHeading.asRadians();\n\n        final double latitude2 = Math.asin(Math.sin(latitude1)\n                * Math.cos(distance.asMillimeters() / Distance.AVERAGE_EARTH_RADIUS.asMillimeters())\n                + Math.cos(latitude1)\n                        * Math.sin(distance.asMillimeters()\n                                / Distance.AVERAGE_EARTH_RADIUS.asMillimeters())\n                        * Math.cos(bearing));\n        final double longitude2 = longitude1 + Math.atan2(\n                Math.sin(bearing)\n                        * Math.sin(distance.asMillimeters()\n                                / Distance.AVERAGE_EARTH_RADIUS.asMillimeters())\n                        * Math.cos(latitude1),\n                Math.cos(distance.asMillimeters() / Distance.AVERAGE_EARTH_RADIUS.asMillimeters())\n                        - Math.sin(latitude1) * Math.sin(latitude2));\n        return new Location(Latitude.radiansBounded(latitude2),\n                Longitude.radiansBounded(longitude2));\n    }\n\n    /**\n     * Snap this {@link Location} to a {@link MultiPolygon} using a {@link Snapper}\n     *\n     * @param shape\n     *            The shape to snap to\n     * @return The corresponding {@link SnappedLocation}\n     */\n    public SnappedLocation snapTo(final MultiPolygon shape)\n    {\n        return new Snapper().snap(this, shape);\n    }\n\n    /**\n     * Snap this {@link Location} to a {@link PolyLine} using a {@link Snapper}\n     *\n     * @param shape\n     *            The shape to snap to\n     * @return The corresponding {@link SnappedLocation}\n     */\n    public SnappedLocation snapTo(final PolyLine shape)\n    {\n        return new Snapper().snap(this, shape);\n    }\n\n    public String toCompactString()\n    {\n        return this.getLatitude() + \",\" + this.getLongitude();\n    }\n\n    /**\n     * @return the {@link EarthCenteredEarthFixedCoordinate} for this {@link Location}.\n     */\n    public EarthCenteredEarthFixedCoordinate toEarthCenteredEarthFixedCoordinate()\n    {\n        return new EarthCenteredEarthFixedCoordinate(this);\n    }\n\n    /**\n     * @return the {@link GeodeticCoordinate} for this {@link Location}.\n     */\n    public GeodeticCoordinate toGeodeticCoordinate()\n    {\n        return new GeodeticCoordinate(this);\n    }\n\n    @Override\n    public String toString()\n    {\n        return toWkt();\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return new WkbLocationConverter().convert(this);\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return new WktLocationConverter().convert(this);\n    }\n\n    @Override\n    public boolean within(final GeometricSurface surface)\n    {\n        return surface.fullyGeometricallyEncloses(this);\n    }\n\n    protected Point2D asAwtPoint()\n    {\n        return new Point((int) getLongitude().asDm7(), (int) getLatitude().asDm7());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Longitude.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\n\n/**\n * A Longitude between -180 degrees and +180 degrees, inclusive.\n *\n * @author matthieun\n * @author tony\n */\npublic class Longitude extends Angle\n{\n    public static final Longitude MINIMUM = Longitude.dm7(MINIMUM_DM7);\n    public static final Longitude ZERO = Longitude.dm7(0L);\n    public static final Longitude MAXIMUM = Longitude.dm7(MAXIMUM_DM7);\n    public static final Longitude ANTIMERIDIAN_WEST = Longitude.MINIMUM;\n    public static final Longitude ANTIMERIDIAN_EAST = Longitude.MAXIMUM;\n    private static final long serialVersionUID = 4222162088144937632L;\n    private boolean isMaximumDm7 = false;\n\n    /**\n     * @param degrees\n     *            A Longitude value in degrees\n     * @return The built {@link Longitude} object using the degrees value\n     */\n    public static Longitude degrees(final double degrees)\n    {\n        return dm7(Math.round(degrees * DM7_PER_DEGREE));\n    }\n\n    /**\n     * @param dm7\n     *            A longitude value in degree of magnitude 7 (dm7)\n     * @return The built {@link Longitude} object using the dm7 value\n     */\n    public static Longitude dm7(final long dm7)\n    {\n        if (dm7 < MINIMUM_DM7 || dm7 > MAXIMUM_DM7)\n        {\n            throw new IllegalArgumentException(\"Cannot have a longitude of \" + dm7 / DM7_PER_DEGREE\n                    + \" degrees which is outside of \" + MINIMUM_DM7 / DM7_PER_DEGREE\n                    + \" degrees -> \" + MAXIMUM_DM7 / DM7_PER_DEGREE + \" degrees.\");\n        }\n        // This constructor depends on the Angle Constructor, which allows for overriding the\n        // \"assertDm7\" method.\n        return new Longitude((int) dm7);\n    }\n\n    /**\n     * @param radians\n     *            A Longitude value in Radians\n     * @return The built {@link Longitude} object using the Radians value\n     */\n    public static Longitude radians(final double radians)\n    {\n        return dm7(Math.round(radians * DM7_PER_RADIAN));\n    }\n\n    /**\n     * If the given radian exceeds the longitude boundary, will return the boundary value.\n     *\n     * @param radians\n     *            The radian of longitude\n     * @return The adjusted longitude if exceeds the boundary, otherwise the normal longitude\n     */\n    public static Longitude radiansBounded(final double radians)\n    {\n        long dm7 = Math.round(radians * DM7_PER_RADIAN);\n        if (dm7 < MINIMUM_DM7)\n        {\n            dm7 = MINIMUM_DM7;\n        }\n        if (dm7 >= MAXIMUM_DM7)\n        {\n            dm7 = MAXIMUM_DM7 - 1L;\n        }\n        return dm7(dm7);\n    }\n\n    /**\n     * Constructor\n     *\n     * @param dm7\n     *            The longitude value in dm7\n     */\n    protected Longitude(final int dm7)\n    {\n        super(dm7);\n        if (dm7 == MAXIMUM_DM7)\n        {\n            this.isMaximumDm7 = true;\n        }\n    }\n\n    @Override\n    public long asDm7()\n    {\n        if (this.isMaximumDm7)\n        {\n            // This longitude was built with +180 and not -180, so make sure to return the same.\n            return MAXIMUM_DM7;\n        }\n        else\n        {\n            return super.asDm7();\n        }\n    }\n\n    /**\n     * @param that\n     *            The other {@link Longitude} to relate to\n     * @return True if those two longitudes are closer to each other on the Antimeridian side than\n     *         on the Greenwich side.\n     */\n    public boolean isCloserViaAntimeridianTo(final Longitude that)\n    {\n        // The difference in numerical longitude is larger than half a revolution\n        return Math.abs(this.asDm7() - that.asDm7()) > REVOLUTION_DM7 / 2;\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.valueOf(this.asDegrees());\n    }\n\n    @Override\n    protected int assertDm7(final int dm7)\n    {\n        if (dm7 < MINIMUM_DM7 || dm7 > MAXIMUM_DM7)\n        {\n            throw new IllegalArgumentException(\"Longitude dm7 value \" + dm7 + \" is invalid.\");\n        }\n        return dm7;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/MultiPolyLine.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.converters.WkbMultiPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.WktMultiPolyLineConverter;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonGeometry;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.PolyLineCoordinateConverter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\nimport com.google.common.collect.Lists;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\n/**\n * A MultiPolyLine is a set of distinct {@link PolyLine}s in a specific order\n *\n * @author yalimu\n */\npublic class MultiPolyLine\n        implements Iterable<PolyLine>, Located, Serializable, GeometryPrintable, GeoJsonGeometry\n{\n    private static final long serialVersionUID = 5907807607388840698L;\n    private final List<PolyLine> polyLineList;\n\n    /**\n     * Create a {@link MultiPolyLine} from Well Known Text\n     *\n     * @param wkt\n     *            The Well Known Text\n     * @return The {@link MultiPolyLine}\n     */\n    public static MultiPolyLine wkt(final String wkt)\n    {\n        return new WktMultiPolyLineConverter().backwardConvert(wkt);\n    }\n\n    public MultiPolyLine(final Iterable<? extends PolyLine> polyLines)\n    {\n        this(Iterables.asList(polyLines));\n    }\n\n    public MultiPolyLine(final List<? extends PolyLine> polyLines)\n    {\n        if (polyLines.isEmpty())\n        {\n            throw new CoreException(\"Cannot have an empty list of PolyLine or Polygon.\");\n        }\n        this.polyLineList = polyLines.stream().distinct().collect(Collectors.toList());\n    }\n\n    public MultiPolyLine(final PolyLine... polyLines)\n    {\n        this(Iterables.iterable(polyLines));\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        final PolyLineCoordinateConverter converter = new PolyLineCoordinateConverter();\n        final JsonArray coordinateArray = new JsonArray();\n        Iterables.stream(this).map(converter::convert).forEach(coordinateArray::add);\n        return GeoJsonUtils.geometry(this.getGeoJsonType(), coordinateArray);\n    }\n\n    public Iterable<GeoJsonBuilder.LocationIterableProperties> asLocationIterableProperties()\n    {\n        return this.polyLineList.stream()\n                .map(polyLine -> new GeoJsonBuilder.LocationIterableProperties(polyLine,\n                        new HashMap<>()))\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        final List<Location> locations = Lists.newArrayList();\n        this.polyLineList.stream().map(PolyLine::getPoints).forEach(locations::addAll);\n        return Rectangle.forLocations(locations);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (!(other instanceof MultiPolyLine))\n        {\n            return false;\n        }\n        final MultiPolyLine otherItem = (MultiPolyLine) other;\n        return new HashSet<>(this.polyLineList).equals(new HashSet<>(otherItem.getPolyLineList()));\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.MULTI_LINESTRING;\n    }\n\n    public List<PolyLine> getPolyLineList()\n    {\n        return this.polyLineList;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final StringBuilder stringBuilder = new StringBuilder();\n        for (final PolyLine polyLine : this.polyLineList)\n        {\n            stringBuilder.append(polyLine.hashCode());\n        }\n        return stringBuilder.toString().hashCode();\n    }\n\n    @Override\n    public Iterator<PolyLine> iterator()\n    {\n        return this.polyLineList.iterator();\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return new WkbMultiPolyLineConverter().convert(this);\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return new WktMultiPolyLineConverter().convert(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/MultiPolygon.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.algorithm.match.HausdorffSimilarityMeasure;\nimport org.locationtech.jts.geom.prep.PreparedGeometry;\nimport org.locationtech.jts.geom.prep.PreparedGeometryFactory;\nimport org.locationtech.jts.operation.valid.IsValidOp;\nimport org.locationtech.jts.operation.valid.TopologyValidationError;\nimport org.openstreetmap.atlas.geography.clipping.Clip;\nimport org.openstreetmap.atlas.geography.clipping.Clip.ClipType;\nimport org.openstreetmap.atlas.geography.clipping.GeometryOperation;\nimport org.openstreetmap.atlas.geography.converters.MultiPolygonStringConverter;\nimport org.openstreetmap.atlas.geography.converters.WkbMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.WktMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsLocationConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonGeometry;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Multiple {@link Polygon}s some inner, some outer.\n *\n * @author matthieun\n */\npublic class MultiPolygon\n        implements Iterable<Polygon>, GeometricSurface, Serializable, GeoJsonGeometry\n{\n    public static final MultiPolygon MAXIMUM = forPolygon(Rectangle.MAXIMUM);\n    public static final MultiPolygon TEST_MULTI_POLYGON;\n    private static final Logger logger = LoggerFactory.getLogger(MultiPolygon.class);\n    private static final long serialVersionUID = 4198234682870043547L;\n    private static final int SIMPLE_STRING_LENGTH = 200;\n    private static final JtsMultiPolygonToMultiPolygonConverter JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n    private static final JtsPointConverter JTS_POINT_CONVERTER = new JtsPointConverter();\n    private static final JtsPolyLineConverter JTS_POLYLINE_CONVERTER = new JtsPolyLineConverter();\n    private static final JtsPolygonConverter JTS_POLYGON_CONVERTER = new JtsPolygonConverter();\n\n    static\n    {\n        final MultiMap<Polygon, Polygon> outerToInners = new MultiMap<>();\n        final Polygon outer = new Polygon(Location.CROSSING_85_280, Location.CROSSING_85_17,\n                Location.TEST_1, Location.TEST_5);\n        final Polygon inner = new Polygon(Location.TEST_6, Location.TEST_2, Location.TEST_7);\n        outerToInners.add(outer, inner);\n        TEST_MULTI_POLYGON = new MultiPolygon(outerToInners);\n    }\n\n    private transient PreparedGeometry prepared;\n\n    private final MultiMap<Polygon, Polygon> outerToInners;\n    private Rectangle bounds;\n\n    /**\n     * @param polygons\n     *            The outers of the multipolygon\n     * @return A {@link MultiPolygon} with the provided {@link Polygon} as a single outer\n     *         {@link Polygon}, with no inner {@link Polygon}\n     */\n    public static MultiPolygon forOuters(final Iterable<Polygon> polygons)\n    {\n        final MultiMap<Polygon, Polygon> multiMap = new MultiMap<>();\n        polygons.forEach(polygon -> multiMap.put(polygon, Collections.emptyList()));\n        return new MultiPolygon(multiMap);\n    }\n\n    public static MultiPolygon forOuters(final Polygon... polygons)\n    {\n        return MultiPolygon.forOuters(Arrays.asList(polygons));\n    }\n\n    /**\n     * @param polygon\n     *            A simple {@link Polygon}\n     * @return A {@link MultiPolygon} with the provided {@link Polygon} as a single outer\n     *         {@link Polygon}, with no inner {@link Polygon}\n     */\n    public static MultiPolygon forPolygon(final Polygon polygon)\n    {\n        final MultiMap<Polygon, Polygon> multiMap = new MultiMap<>();\n        multiMap.put(polygon, new ArrayList<>());\n        return new MultiPolygon(multiMap);\n    }\n\n    /**\n     * Generate a {@link MultiPolygon} from Well Known Text\n     *\n     * @param wkt\n     *            The {@link MultiPolygon} in well known text\n     * @return The parsed {@link MultiPolygon}\n     */\n    public static MultiPolygon wkt(final String wkt)\n    {\n        return new WktMultiPolygonConverter().backwardConvert(wkt);\n    }\n\n    public MultiPolygon(final MultiMap<Polygon, Polygon> outerToInners)\n    {\n        this.outerToInners = outerToInners;\n    }\n\n    public GeoJsonObject asGeoJsonFeatureCollection()\n    {\n        final GeoJsonBuilder builder = new GeoJsonBuilder();\n        return builder.createFeatureCollection(Iterables.translate(outers(),\n                outerPolygon -> builder.createOneOuterMultiPolygon(\n                        new MultiIterable<>(Collections.singleton(outerPolygon),\n                                this.outerToInners.get(outerPolygon)))));\n    }\n\n    /**\n     * Creates a JsonObject with GeoJSON geometry representing this multi-polygon.\n     *\n     * @return A JsonObject with GeoJSON geometry\n     */\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        return GeoJsonUtils.geometry(GeoJsonType.MULTI_POLYGON,\n                GeoJsonUtils.multiPolygonToCoordinates(this));\n    }\n\n    public Iterable<LocationIterableProperties> asLocationIterableProperties()\n    {\n        final Iterable<LocationIterableProperties> outers = Iterables.translate(outers(), polygon ->\n        {\n            final Map<String, String> tags = new HashMap<>();\n            tags.put(\"MultiPolygon\", \"outer\");\n            return new LocationIterableProperties(polygon, tags);\n        });\n        final Iterable<LocationIterableProperties> inners = Iterables.translate(inners(), polygon ->\n        {\n            final Map<String, String> tags = new HashMap<>();\n            tags.put(\"MultiPolygon\", \"inner\");\n            return new LocationIterableProperties(polygon, tags);\n        });\n        return new MultiIterable<>(outers, inners);\n    }\n\n    /**\n     * @return Optional of {@link Polygon} representation if possible\n     */\n    public Optional<Polygon> asSimplePolygon()\n    {\n        if (this.isSimplePolygon())\n        {\n            return outers().stream().findFirst();\n        }\n        logger.warn(\"Trying to read complex MultiPolygon as simple Polygon\");\n        return Optional.empty();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        if (this.bounds == null && !this.isEmpty())\n        {\n            final Set<Location> locations = new HashSet<>();\n            forEach(polygon -> polygon.forEach(locations::add));\n            this.bounds = Rectangle.forLocations(locations);\n        }\n        return this.bounds;\n    }\n\n    /**\n     * @param clipping\n     *            The {@link MultiPolygon} clipping that {@link MultiPolygon}\n     * @param clipType\n     *            The type of clip (union, or, and or xor)\n     * @return The {@link Clip} container, that can return the clipped {@link MultiPolygon}\n     */\n    public Clip clip(final MultiPolygon clipping, final ClipType clipType)\n    {\n        return new Clip(clipType, this, clipping);\n    }\n\n    /**\n     * Concatenate multiple {@link MultiPolygon}s into one. If the two {@link MultiPolygon}s happen\n     * to have the same outer polygon, then the other's inner polygons will be added and the\n     * current's inner polygons will be erased.\n     *\n     * @param other\n     *            The other {@link MultiPolygon} to concatenate.\n     * @return The concatenated {@link MultiPolygon}\n     */\n    public MultiPolygon concatenate(final MultiPolygon other)\n    {\n        final MultiMap<Polygon, Polygon> result = new MultiMap<>();\n        result.putAll(getOuterToInners());\n        result.putAll(other.getOuterToInners());\n        return new MultiPolygon(result);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof MultiPolygon)\n        {\n            final MultiPolygon that = (MultiPolygon) other;\n            final Set<Polygon> thatOuters = that.outers();\n            if (thatOuters.size() != this.outers().size())\n            {\n                return false;\n            }\n            for (final Polygon outer : this.outers())\n            {\n                if (!thatOuters.contains(outer))\n                {\n                    return false;\n                }\n                final List<Polygon> thatInners = that.innersOf(outer);\n                if (thatInners.size() != this.innersOf(outer).size())\n                {\n                    return false;\n                }\n                for (final Polygon inner : this.innersOf(outer))\n                {\n                    if (!thatInners.contains(inner))\n                    {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * @param location\n     *            A {@link Location} item\n     * @return True if the {@link MultiPolygon} contains the provided item (i.e. it is within the\n     *         outer polygons and not within the inner polygons)\n     */\n    @Override\n    public boolean fullyGeometricallyEncloses(final Location location)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory\n                    .prepare(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(this));\n        }\n        return this.prepared.covers(JTS_POINT_CONVERTER.convert(location));\n    }\n\n    /**\n     * Tests to see if entire surface of the provided {@link MultiPolygon} lies within this\n     * {@link MultiPolygon}\n     *\n     * @param that\n     *            the provided {@link MultiPolygon} to test\n     * @return true if the conditions are met, false otherwise\n     */\n    @Override\n    public boolean fullyGeometricallyEncloses(final MultiPolygon that)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory\n                    .prepare(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(this));\n        }\n        return this.prepared\n                .covers(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(that));\n    }\n\n    /**\n     * @param polyLine\n     *            A {@link PolyLine} item\n     * @return True if the {@link MultiPolygon} contains the provided {@link PolyLine}.\n     */\n    @Override\n    public boolean fullyGeometricallyEncloses(final PolyLine polyLine)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory\n                    .prepare(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(this));\n        }\n        if (polyLine instanceof Polygon)\n        {\n            return this.prepared.covers(JTS_POLYGON_CONVERTER.convert((Polygon) polyLine));\n        }\n        return this.prepared.covers(JTS_POLYLINE_CONVERTER.convert(polyLine));\n\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.MULTI_POLYGON;\n    }\n\n    public MultiMap<Polygon, Polygon> getOuterToInners()\n    {\n        return this.outerToInners;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        int result = 0;\n        for (final Polygon polygon : this)\n        {\n            result += polygon.hashCode();\n        }\n        return result;\n    }\n\n    public List<Polygon> inners()\n    {\n        return this.outerToInners.allValues();\n    }\n\n    public List<Polygon> innersOf(final Polygon outer)\n    {\n        if (this.outerToInners.containsKey(outer))\n        {\n            return this.outerToInners.get(outer);\n        }\n        return new ArrayList<>();\n    }\n\n    @Override\n    public boolean intersects(final PolyLine polyLine)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory\n                    .prepare(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(this));\n        }\n        if (polyLine instanceof Polygon)\n        {\n            return this.prepared.intersects(JTS_POLYGON_CONVERTER.convert((Polygon) polyLine));\n        }\n        return this.prepared.intersects(JTS_POLYLINE_CONVERTER.convert(polyLine));\n    }\n\n    /**\n     * @return {@code true} if this {@link MultiPolygon} doesn't have any outer members\n     */\n    public boolean isEmpty()\n    {\n        return this.outerToInners.isEmpty();\n    }\n\n    /**\n     * @return True if this {@link MultiPolygon} is valid according to the OGC SFS specification.\n     *         See {@code org.locationtech.jts.geom.Geometry.isValid()}\n     */\n    public boolean isOGCValid()\n    {\n        return new JtsMultiPolygonToMultiPolygonConverter().backwardConvert(this).isValid();\n    }\n\n    /**\n     * @return True if this {@link MultiPolygon} is valid according to the OSM specification. OSM\n     *         allows some inners of the MultiPolygon to touch on more than a single point, to allow\n     *         for one inner to be split in multiple parts tagged differently. Example: a forest\n     *         with an inner, that is one side a meadow, and on the other side some marshland.\n     */\n    public boolean isOSMValid()\n    {\n        final org.locationtech.jts.geom.MultiPolygon jtsMultiPolygon = new JtsMultiPolygonToMultiPolygonConverter()\n                .backwardConvert(this);\n        final TopologyValidationError topologyValidationError = new IsValidOp(jtsMultiPolygon)\n                .getValidationError();\n        if (topologyValidationError != null)\n        {\n            // In this case, the geometry is not OGC valid, here we capture the\n            // TopologyValidationError to know what to do next.\n            if (TopologyValidationError.SELF_INTERSECTION == topologyValidationError.getErrorType())\n            {\n                final Location errorLocation = new JtsLocationConverter()\n                        .backwardConvert(topologyValidationError.getCoordinate());\n                final Rectangle errorExpandedBoundingBox = errorLocation\n                        .boxAround(Distance.ONE_METER);\n                return isOSMValidSelfIntersection(errorExpandedBoundingBox);\n            }\n            return false;\n        }\n        else\n        {\n            return true;\n        }\n    }\n\n    public boolean isSimilarTo(final MultiPolygon other)\n    {\n        final double similarity = new HausdorffSimilarityMeasure().measure(\n                JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(this),\n                JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(other));\n        return similarity > SIMILARITY_THRESHOLD;\n    }\n\n    /**\n     * @return whether this Multipolygon can be represented as a {@link Polygon}\n     */\n    public boolean isSimplePolygon()\n    {\n        return this.outers().size() == 1;\n    }\n\n    @Override\n    public Iterator<Polygon> iterator()\n    {\n        return new MultiIterable<>(outers(), inners()).iterator();\n    }\n\n    /**\n     * Merge multiple {@link MultiPolygon}s into one. If the two {@link MultiPolygon}s happen to\n     * have the same outer polygon, then the two's inner polygons will be added to the same list.\n     *\n     * @param other\n     *            The other {@link MultiPolygon} to merge.\n     * @return The concatenated {@link MultiPolygon}\n     */\n    public MultiPolygon merge(final MultiPolygon other)\n    {\n        final MultiMap<Polygon, Polygon> result = new MultiMap<>();\n        result.putAll(getOuterToInners());\n        result.addAll(other.getOuterToInners());\n        return new MultiPolygon(result);\n    }\n\n    public Set<Polygon> outers()\n    {\n        return this.outerToInners.keySet();\n    }\n\n    @Override\n    public boolean overlaps(final MultiPolygon otherMultiPolygon)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory\n                    .prepare(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(this));\n        }\n        return this.prepared.intersects(\n                JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(otherMultiPolygon));\n    }\n\n    @Override\n    public boolean overlaps(final PolyLine polyLine)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory\n                    .prepare(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(this));\n        }\n        if (polyLine instanceof Polygon)\n        {\n            return this.prepared.intersects(JTS_POLYGON_CONVERTER.convert((Polygon) polyLine));\n        }\n        return this.prepared.intersects(JTS_POLYLINE_CONVERTER.convert(polyLine));\n    }\n\n    public void saveAsGeoJson(final WritableResource resource)\n    {\n        final JsonWriter writer = new JsonWriter(resource);\n        writer.write(asGeoJson());\n        writer.close();\n    }\n\n    @Override\n    public Surface surface()\n    {\n        Surface result = Surface.MINIMUM;\n        for (final Polygon outer : this.outers())\n        {\n            result = result.add(outer.surface());\n        }\n        for (final Polygon inner : this.inners())\n        {\n            result = result.subtract(inner.surface());\n        }\n        return result;\n    }\n\n    @Override\n    public Surface surfaceOnSphere()\n    {\n        Surface result = Surface.MINIMUM;\n        for (final Polygon outer : this.outers())\n        {\n            result = result.add(outer.surfaceOnSphere());\n        }\n        for (final Polygon inner : this.inners())\n        {\n            result = result.subtract(inner.surfaceOnSphere());\n        }\n        return result;\n    }\n\n    public String toCompactString()\n    {\n        return new MultiPolygonStringConverter().backwardConvert(this);\n    }\n\n    public String toReadableString()\n    {\n        final String separator1 = \"\\n\\t\";\n        final String separator2 = \"\\n\\t\\t\";\n        final StringBuilder builder = new StringBuilder();\n        final StringList outers = new StringList();\n        for (final Polygon outer : this.outers())\n        {\n            final StringList inners = new StringList();\n            for (final Polygon inner : innersOf(outer))\n            {\n                inners.add(\"Inner: \" + inner.toCompactString());\n            }\n            outers.add(\"Outer: \" + outer.toCompactString() + separator2 + inners.join(separator2));\n        }\n        builder.append(outers.join(separator1));\n        return builder.toString();\n    }\n\n    public String toSimpleString()\n    {\n        final String string = toCompactString();\n        if (string.length() > SIMPLE_STRING_LENGTH + 1)\n        {\n            return string.substring(0, SIMPLE_STRING_LENGTH) + \"...\";\n        }\n        return string;\n    }\n\n    @Override\n    public String toString()\n    {\n        return toWkt();\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return new WkbMultiPolygonConverter().convert(this);\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return new WktMultiPolygonConverter().convert(this);\n    }\n\n    private boolean isLinear(final GeometricObject geometricObject)\n    {\n        return geometricObject instanceof PolyLine && !(geometricObject instanceof Polygon)\n                || geometricObject instanceof MultiPolyLine || geometricObject instanceof Location;\n    }\n\n    /**\n     * Given an OGC intersection location, check all the touching features to see if they are OSM\n     * valid.\n     *\n     * @param errorExpandedBoundingBox\n     *            Small bounding box around the error location\n     * @return True if the error is not an error according to OSM\n     */\n    private boolean isOSMValidSelfIntersection(final Rectangle errorExpandedBoundingBox)\n    {\n        final List<Tuple<Boolean, Polygon>> ringsOfInterest = Iterables\n                .stream(new MultiIterable<>(\n                        outers().stream().map(outer -> new Tuple<>(true, outer))\n                                .collect(Collectors.toList()),\n                        inners().stream().map(inner -> new Tuple<>(false, inner))\n                                .collect(Collectors.toList())))\n                .filter(ringOfInterest -> ringOfInterest.getSecond()\n                        .intersects(errorExpandedBoundingBox))\n                .collectToList();\n        final List<GeometricObject> intersections = new ArrayList<>();\n        for (int i = 0; i < ringsOfInterest.size(); i++)\n        {\n            for (int j = i + 1; j < ringsOfInterest.size(); j++)\n            {\n                // Make sure this is just a PolyLine\n                final List<Tuple<Boolean, Polygon>> candidates = new ArrayList<>();\n                candidates.add(ringsOfInterest.get(i));\n                candidates.add(ringsOfInterest.get(j));\n                if (candidates.get(0).getFirst() || candidates.get(1).getFirst())\n                {\n                    // There is a self intersection between at least one outer, this is not\n                    // OSM valid\n                    return false;\n                }\n                GeometryOperation.intersection(\n                        candidates.stream().map(Tuple::getSecond).collect(Collectors.toList()))\n                        .ifPresent(intersections::add);\n            }\n        }\n        boolean allIntersectionsArePolyLines = true;\n        for (final GeometricObject intersection : intersections)\n        {\n            if (!isLinear(intersection))\n            {\n                allIntersectionsArePolyLines = false;\n                break;\n            }\n        }\n        return ringsOfInterest.size() > 1 && allIntersectionsArePolyLines;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/PolyLine.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport org.locationtech.jts.geom.prep.PreparedGeometry;\nimport org.locationtech.jts.geom.prep.PreparedGeometryFactory;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Snapper.SnappedLocation;\nimport org.openstreetmap.atlas.geography.clipping.Clip;\nimport org.openstreetmap.atlas.geography.clipping.Clip.ClipType;\nimport org.openstreetmap.atlas.geography.converters.WkbLocationConverter;\nimport org.openstreetmap.atlas.geography.converters.WkbPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.WktLocationConverter;\nimport org.openstreetmap.atlas.geography.converters.WktPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonGeometry;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.geography.matching.PolyLineMatch;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonObject;\n\n/**\n * A PolyLine is a set of {@link Location}s in a specific order\n *\n * @author matthieun\n * @author mgostintsev\n * @author Sid\n */\npublic class PolyLine implements Collection<Location>, Located, Serializable, GeometryPrintable,\n        GeoJsonGeometry, GeometricObject\n{\n    public static final PolyLine TEST_POLYLINE = new PolyLine(Location.TEST_3, Location.TEST_7,\n            Location.TEST_4, Location.TEST_1, Location.TEST_5);\n    public static final PolyLine TEST_POLYLINE_2 = new PolyLine(Location.TEST_1, Location.TEST_5,\n            Location.TEST_4, Location.TEST_3, Location.TEST_7);\n    public static final PolyLine CENTER = new PolyLine(Location.CENTER);\n    public static final PolyLine SIMPLE_POLYLINE = new PolyLine(Location.forString(\"1,1\"),\n            Location.forString(\"2,2\"));\n\n    public static final String SEPARATOR = \":\";\n\n    protected static final int SIMPLE_STRING_LENGTH = 200;\n\n    private static final JtsMultiPolygonToMultiPolygonConverter JTS_MULTIPOLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n    private static final JtsPolygonConverter JTS_POLYGON_CONVERTER = new JtsPolygonConverter();\n    private static final JtsPointConverter JTS_POINT_CONVERTER = new JtsPointConverter();\n    private static final JtsPolyLineConverter JTS_POLYLINE_CONVERTER = new JtsPolyLineConverter();\n\n    private static final long serialVersionUID = -3291779878869865427L;\n    private static final Logger logger = LoggerFactory.getLogger(PolyLine.class);\n    private static final String IMMUTABLE_POLYLINE = \"A polyline is immutable\";\n    private final List<Location> points;\n    private transient PreparedGeometry prepared;\n\n    public static GeoJsonObject asGeoJson(final Iterable<? extends Iterable<Location>> geometries)\n    {\n        return new GeoJsonBuilder().create(Iterables.translate(geometries,\n                geometry -> new LocationIterableProperties(geometry, new HashMap<>())));\n    }\n\n    /**\n     * Generate a random {@link PolyLine} within bounds.\n     *\n     * @param numberPoints\n     *            The number of points in the {@link PolyLine}\n     * @param bounds\n     *            The bounds for the points to be in\n     * @return The random {@link PolyLine}\n     */\n    public static PolyLine random(final int numberPoints, final Rectangle bounds)\n    {\n        final List<Location> locations = new ArrayList<>();\n        IntStream.range(0, numberPoints).forEach(index -> locations.add(Location.random(bounds)));\n        return new PolyLine(locations);\n    }\n\n    public static void saveAsGeoJson(final Iterable<? extends Iterable<Location>> geometries,\n            final WritableResource resource)\n    {\n        try (JsonWriter writer = new JsonWriter(resource))\n        {\n            writer.write(asGeoJson(geometries).jsonObject());\n        }\n    }\n\n    /**\n     * Create a {@link PolyLine} from Well Known Binary\n     *\n     * @param wkb\n     *            The Well Known Binary\n     * @return The {@link PolyLine}\n     */\n    public static PolyLine wkb(final byte[] wkb)\n    {\n        return new WkbPolyLineConverter().backwardConvert(wkb);\n    }\n\n    /**\n     * Create a {@link PolyLine} from Well Known Text\n     *\n     * @param wkt\n     *            The Well Known Text\n     * @return The {@link PolyLine}\n     */\n    public static PolyLine wkt(final String wkt)\n    {\n        return new WktPolyLineConverter().backwardConvert(wkt);\n    }\n\n    public PolyLine(final Iterable<? extends Location> points)\n    {\n        this(Iterables.asList(points));\n    }\n\n    public PolyLine(final List<? extends Location> points)\n    {\n        if (points.isEmpty())\n        {\n            throw new CoreException(\"Cannot have an empty PolyLine or Polygon.\");\n        }\n        this.points = new ArrayList<>(points);\n    }\n\n    public PolyLine(final Location... points)\n    {\n        this(Iterables.iterable(points));\n    }\n\n    @Override\n    public boolean add(final Location e)\n    {\n        throw new IllegalAccessError(\"Cannot add a Location to a PolyLine.\");\n    }\n\n    @Override\n    public boolean addAll(final Collection<? extends Location> collection)\n    {\n        throw new IllegalAccessError(\"Cannot add Locations to a PolyLine.\");\n    }\n\n    /**\n     * Return a {@link List} of {@link Tuple} that contains the Angle {@link Angle} and\n     * {@link Location} of all {@link Angle}s that are greater than or equal to the target\n     * {@link Angle}.\n     *\n     * @param target\n     *            The threshold {@link Angle} used for comparison.\n     * @return The {@link List} of {@link Tuple} that contains the {@link Angle} and\n     *         {@link Location} of all results\n     */\n    public List<Tuple<Angle, Location>> anglesGreaterThanOrEqualTo(final Angle target)\n    {\n        final List<Tuple<Angle, Location>> result = new ArrayList<>();\n        final List<Segment> segments = segments();\n        if (segments.isEmpty() || segments.size() == 1)\n        {\n            return result;\n        }\n\n        for (int i = 1; i < segments.size(); i++)\n        {\n            final Segment first = segments.get(i - 1);\n            final Segment second = segments.get(i);\n            final Optional<Heading> firstHeading = first.heading();\n            final Optional<Heading> secondHeading = second.heading();\n            if (firstHeading.isPresent() && secondHeading.isPresent())\n            {\n                final Angle candidate = firstHeading.get().difference(secondHeading.get());\n                if (candidate.isGreaterThanOrEqualTo(target))\n                {\n                    final Tuple<Angle, Location> tuple = Tuple.createTuple(candidate, first.end());\n                    result.add(tuple);\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Return a {@link List} of {@link Tuple} that contains the {@link Angle} and {@link Location}\n     * of all {@link Angle}s that are less than or equal to the target {@link Angle}.\n     *\n     * @param target\n     *            The threshold {@link Angle} used for comparison.\n     * @return The {@link List} of {@link Tuple} that contains the {@link Angle} and\n     *         {@link Location} of all results\n     */\n    public List<Tuple<Angle, Location>> anglesLessThanOrEqualTo(final Angle target)\n    {\n        final List<Tuple<Angle, Location>> result = new ArrayList<>();\n        final List<Segment> segments = segments();\n        if (segments.isEmpty() || segments.size() == 1)\n        {\n            return result;\n        }\n\n        for (int i = 1; i < segments.size(); i++)\n        {\n            final Segment first = segments.get(i - 1);\n            final Segment second = segments.get(i);\n            final Optional<Heading> firstHeading = first.heading();\n            final Optional<Heading> secondHeading = second.heading();\n            if (firstHeading.isPresent() && secondHeading.isPresent())\n            {\n                final Angle candidate = firstHeading.get().difference(secondHeading.get());\n                if (candidate.isLessThanOrEqualTo(target))\n                {\n                    final Tuple<Angle, Location> tuple = Tuple.createTuple(candidate, first.end());\n                    result.add(tuple);\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Append the given {@link PolyLine} to this one, if possible.\n     *\n     * @param other\n     *            The {@link PolyLine} to append\n     * @return the new, combined {@link PolyLine}\n     */\n    public PolyLine append(final PolyLine other)\n    {\n        if (this.last().equals(other.first()))\n        {\n            return new PolyLine(new MultiIterable<>(this, other.truncate(1, 0)));\n        }\n        else\n        {\n            throw new CoreException(\n                    \"Cannot append {} to {} - the end and start points do not match.\",\n                    other.toWkt(), this.toWkt());\n        }\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        return GeoJsonUtils.geometry(GeoJsonType.LINESTRING,\n                GeoJsonUtils.locationsToCoordinates(this.points));\n    }\n\n    /**\n     * Return the average distance from this {@link PolyLine}'s shape points to the other shape, and\n     * the other shape's shape points to this polyline.\n     *\n     * @param other\n     *            The other shape to compare to\n     * @return The two way cost distance to the other {@link PolyLine}\n     */\n    public Distance averageDistanceTo(final PolyLine other)\n    {\n        return averageOneWayDistanceTo(other).add(other.averageOneWayDistanceTo(this))\n                .scaleBy(Ratio.HALF);\n    }\n\n    /**\n     * Return the average distance from this {@link PolyLine}'s shape points to the other shape,\n     * using a one-way snapping.\n     *\n     * @param other\n     *            The other shape to compare to\n     * @return The one way cost distance to the other {@link PolyLine}\n     */\n    public Distance averageOneWayDistanceTo(final PolyLine other)\n    {\n        Distance costDistance = Distance.ZERO;\n        for (final Location shapePoint : this)\n        {\n            costDistance = costDistance.add(shapePoint.snapTo(other).getDistance());\n        }\n        return costDistance.scaleBy(1.0 / this.size());\n    }\n\n    /**\n     * Return a sub-{@link PolyLine} of this {@link PolyLine}\n     *\n     * @param start\n     *            The start location to include\n     * @param startOccurrence\n     *            The occurrence index starting from 0 for the end location, in case of self\n     *            intersecting or ring polylines.\n     * @param end\n     *            The end location to include\n     * @param endOccurrence\n     *            The occurrence index starting from 0 for the end location, in case of self\n     *            intersecting or ring polylines.\n     * @return The sub-{@link PolyLine} including start and end\n     */\n    public PolyLine between(final Location start, final int startOccurrence, final Location end,\n            final int endOccurrence)\n    {\n        final List<Location> result = new ArrayList<>();\n        boolean started = false;\n        int startIndex = 0;\n        int endIndex = 0;\n        for (final Location location : this)\n        {\n            if (location.equals(start) && startOccurrence == startIndex++)\n            {\n                started = true;\n            }\n            if (location.equals(end) && endOccurrence == endIndex++)\n            {\n                if (!started)\n                {\n                    throw new CoreException(\n                            \"Found end first! {}(occurrence {}) and {}(occurrence {}) are not in order with respect to {}\",\n                            start, startOccurrence, end, endOccurrence, this.toWkt());\n                }\n                started = false;\n                result.add(location);\n                // Break here to avoid confusion with self-intersecting polylines.\n                break;\n            }\n            if (started)\n            {\n                result.add(location);\n            }\n        }\n        if (started)\n        {\n            throw new CoreException(\"(Start was {}) End {} is not in polyLine {}\", start, end,\n                    this);\n        }\n        return new PolyLine(result);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return Rectangle.forLocations(this);\n    }\n\n    @Override\n    public void clear()\n    {\n        throw new IllegalAccessError(IMMUTABLE_POLYLINE);\n    }\n\n    /**\n     * Clip this feature on a {@link MultiPolygon}\n     *\n     * @param clipping\n     *            The {@link MultiPolygon} to clip to\n     * @param clipType\n     *            The clip type (AND, OR, XOR or NOT).\n     * @return The clip object containing the clipped features.\n     */\n    public Clip clip(final MultiPolygon clipping, final ClipType clipType)\n    {\n        return new Clip(clipType, this, clipping);\n    }\n\n    /**\n     * Clip this feature on a {@link Polygon}\n     *\n     * @param clipping\n     *            The {@link Polygon} to clip to\n     * @param clipType\n     *            The clip type (AND, OR, XOR or NOT).\n     * @return The clip object containing the clipped features.\n     */\n    public Clip clip(final Polygon clipping, final ClipType clipType)\n    {\n        return new Clip(clipType, this, clipping);\n    }\n\n    /**\n     * @param location\n     *            The {@link Location} to test\n     * @return True if one of the vertices of this {@link PolyLine} is the provided {@link Location}\n     */\n    public boolean contains(final Location location)\n    {\n        for (final Location thisLocation : this)\n        {\n            if (thisLocation.equals(location))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public final boolean contains(final Object object)\n    {\n        if (object instanceof Location)\n        {\n            return contains((Location) object);\n        }\n        if (object instanceof Segment)\n        {\n            return contains((Segment) object);\n        }\n        throw new IllegalAccessError(\n                \"A polyline can contain a Segment or Location only. Maybe you meant \\\"covers\\\"?\");\n    }\n\n    /**\n     * @param segment\n     *            The {@link Segment} to test\n     * @return True if one of the segments of this {@link PolyLine} is the provided {@link Segment}\n     */\n    public boolean contains(final Segment segment)\n    {\n        final List<Segment> segments = this.segments();\n        for (final Segment thisSegment : segments)\n        {\n            if (thisSegment.equals(segment))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean containsAll(final Collection<?> collection)\n    {\n        throw new IllegalAccessError();\n    }\n\n    /**\n     * Get the cost {@link Distance} to the set of {@link Segment}s that are connected and that\n     * provide a new {@link PolyLine} that is the closest in shape to this {@link PolyLine}\n     *\n     * @param candidates\n     *            The candidate {@link PolyLine}s to match to this {@link PolyLine}\n     * @return The best reconstructed match from this PolyLine.\n     */\n    public PolyLineMatch costDistanceToOneWay(final Iterable<PolyLine> candidates)\n    {\n        return new PolyLineMatch(this, Iterables.asList(candidates));\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof PolyLine)\n        {\n            final PolyLine that = (PolyLine) other;\n            return Iterables.equals(this, that);\n        }\n        return false;\n    }\n\n    /**\n     * Tests if this {@link PolyLine} has the same shape as another {@link PolyLine}. This is\n     * different from equals as some {@link PolyLine}s that are different can still have the same\n     * shape, by being reversed or by self intersecting in the same point for example.\n     *\n     * @param other\n     *            The other {@link PolyLine} to compare to\n     * @return True if they both have the same shape\n     */\n    public boolean equalsShape(final PolyLine other)\n    {\n        return this.overlapsShapeOf(other) && other.overlapsShapeOf(this);\n    }\n\n    /**\n     * @return the final {@link Heading} for this {@link PolyLine}, based on the {@link Heading} of\n     *         the last {@link Segment}.\n     */\n    public Optional<Heading> finalHeading()\n    {\n        final List<Segment> segments = this.segments();\n        return segments.size() > 0 ? segments.get(segments.size() - 1).heading() : Optional.empty();\n    }\n\n    /**\n     * @return The first {@link Location} of this {@link PolyLine}\n     */\n    public Location first()\n    {\n        return size() > 0 ? get(0) : null;\n    }\n\n    /**\n     * @param index\n     *            The index to query\n     * @return The {@link Location} at the index provided in this {@link PolyLine}\n     */\n    public Location get(final int index)\n    {\n        if (index < 0 || index >= size())\n        {\n            throw new CoreException(\"Cannot get a Location with index \" + index\n                    + \", which is not between 0 and \" + size());\n        }\n        return this.points.get(index);\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.LINESTRING;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        int result = 0;\n        for (final Location location : this)\n        {\n            result += location.hashCode();\n        }\n        return result;\n    }\n\n    /**\n     * @return The difference, if available, between the last {@link Segment}'s {@link Heading} and\n     *         the first {@link Segment}'s {@link Heading}\n     */\n    public Optional<Angle> headingDifference()\n    {\n        if (this.size() <= 1)\n        {\n            return Optional.empty();\n        }\n        if (this.size() == 2)\n        {\n            return Optional.of(Angle.NONE);\n        }\n        else\n        {\n            final List<Segment> segments = this.segments();\n            final Segment first = segments.get(0);\n            final Segment last = segments.get(segments.size() - 1);\n            final Optional<Heading> heading1 = first.heading();\n            if (!heading1.isPresent())\n            {\n                return Optional.empty();\n            }\n            final Optional<Heading> heading2 = last.heading();\n            if (!heading2.isPresent())\n            {\n                return Optional.empty();\n            }\n            return Optional.of(heading2.get().subtract(heading1.get()));\n        }\n    }\n\n    /**\n     * @return the initial {@link Heading} for this {@link PolyLine}, based on the {@link Heading}\n     *         of the first {@link Segment}.\n     */\n    public Optional<Heading> initialHeading()\n    {\n        final List<Segment> segments = this.segments();\n        return segments.size() > 0 ? segments.get(0).heading() : Optional.empty();\n    }\n\n    /**\n     * @return All the locations in this {@link PolyLine} except the first and last.\n     */\n    public Iterable<Location> innerLocations()\n    {\n        return this.truncate(1, 1);\n    }\n\n    public Set<Location> intersections(final PolyLine candidate)\n    {\n        final Set<Location> result = new HashSet<>();\n        if (this instanceof Segment)\n        {\n            result.addAll(candidate.intersections((Segment) this));\n        }\n        else\n        {\n            final List<Segment> segments = this.segments();\n            segments.forEach(segment ->\n            {\n                final Set<Location> intersections = segment.intersections(candidate);\n                result.addAll(intersections);\n            });\n        }\n        return result;\n    }\n\n    public Set<Location> intersections(final Segment candidate)\n    {\n        final Set<Location> result = new HashSet<>();\n        final List<Segment> segments = this.segments();\n        segments.forEach(segment ->\n        {\n            final Location intersection = segment.intersection(candidate);\n            if (intersection != null)\n            {\n                result.add(intersection);\n            }\n        });\n        return result;\n    }\n\n    /**\n     * Test if two {@link PolyLine}s intersect.\n     *\n     * @param other\n     *            The other {@link PolyLine}\n     * @return True if this {@link PolyLine} intersects the other at least once.\n     */\n    @Override\n    public boolean intersects(final PolyLine other)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory.prepare(JTS_POLYLINE_CONVERTER.convert(this));\n        }\n        return this.prepared.intersects(JTS_POLYLINE_CONVERTER.convert(other));\n    }\n\n    public boolean isClosed()\n    {\n        return JTS_POLYLINE_CONVERTER.convert(this).isClosed();\n    }\n\n    @Override\n    public final boolean isEmpty()\n    {\n        return this.points.isEmpty();\n    }\n\n    /**\n     * @return True if this {@link PolyLine} is a single point, i.e. all the points are the same.\n     */\n    public boolean isPoint()\n    {\n        Location firstPoint = null;\n        for (final Location point : this.points)\n        {\n            if (firstPoint == null)\n            {\n                firstPoint = point;\n                continue;\n            }\n            if (!point.equals(firstPoint))\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public boolean isSimple()\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory.prepare(JTS_POLYLINE_CONVERTER.convert(this));\n        }\n        return this.prepared.getGeometry().isSimple();\n    }\n\n    @Override\n    public Iterator<Location> iterator()\n    {\n        return this.points.iterator();\n    }\n\n    public Location last()\n    {\n        return this.points.size() > 0 ? get(size() - 1) : null;\n    }\n\n    public Distance length()\n    {\n        Distance result = Distance.ZERO;\n        final List<Segment> segments = this.segments();\n        for (final Segment segment : segments)\n        {\n            result = result.add(segment.length());\n        }\n        return result;\n    }\n\n    /**\n     * @return The biggest Angle in this {@link PolyLine}\n     */\n    public Angle maximumAngle()\n    {\n        final List<Segment> segments = segments();\n        if (segments.isEmpty())\n        {\n            return null;\n        }\n        if (segments.size() == 1)\n        {\n            return Angle.NONE;\n        }\n        Angle maximum = Angle.NONE;\n        for (int i = 1; i < segments.size(); i++)\n        {\n            final Segment first = segments.get(i - 1);\n            final Segment second = segments.get(i);\n            final Optional<Heading> firstHeading = first.heading();\n            final Optional<Heading> secondHeading = second.heading();\n            if (firstHeading.isPresent() && secondHeading.isPresent())\n            {\n                final Angle candidate = firstHeading.get().difference(secondHeading.get());\n                if (candidate.isGreaterThan(maximum))\n                {\n                    maximum = candidate;\n                }\n            }\n        }\n        return maximum;\n    }\n\n    /**\n     * @return The location of the biggest Angle in this {@link PolyLine}\n     */\n    public Optional<Location> maximumAngleLocation()\n    {\n        final List<Segment> segments = segments();\n        if (segments.isEmpty() || segments.size() == 1)\n        {\n            return Optional.empty();\n        }\n\n        Angle maximum = Angle.NONE;\n        Location maximumAngleLocation = null;\n        for (int i = 1; i < segments.size(); i++)\n        {\n            final Segment first = segments.get(i - 1);\n            final Segment second = segments.get(i);\n            final Optional<Heading> firstHeading = first.heading();\n            final Optional<Heading> secondHeading = second.heading();\n\n            if (firstHeading.isPresent() && secondHeading.isPresent())\n            {\n                final Angle candidate = firstHeading.get().difference(secondHeading.get());\n                if (candidate.isGreaterThan(maximum) || maximumAngleLocation == null)\n                {\n                    maximum = candidate;\n                    maximumAngleLocation = first.end();\n                }\n            }\n        }\n        return Optional.ofNullable(maximumAngleLocation);\n    }\n\n    public Location middle()\n    {\n        return offsetFromStart(Ratio.HALF);\n    }\n\n    /**\n     * Get the number of times a location appears in this {@link PolyLine}. Most useful for self\n     * intersecting or ring {@link PolyLine}s.\n     *\n     * @param node\n     *            The location to test\n     * @return The number of occurrences in this {@link PolyLine}. 0 if it never shows up.\n     */\n    public int occurrences(final Location node)\n    {\n        int result = 0;\n        for (final Location location : this)\n        {\n            if (location.equals(node))\n            {\n                result++;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Get the offset from the start of the node's location\n     *\n     * @param node\n     *            The location to test\n     * @param occurrenceIndex\n     *            In case of a self intersecting polyline (one or more locations appear more than\n     *            once), indicate the index at which this method should return the location. 0 would\n     *            be first occurrence, 1 second, etc.\n     * @return The offset ratio from the start of the {@link PolyLine}\n     */\n    public Ratio offsetFromStart(final Location node, final int occurrenceIndex)\n    {\n        final Distance max = this.length();\n        Distance candidate = Distance.ZERO;\n        Location previous = this.first();\n        int index = 0;\n        for (final Location location : this)\n        {\n            candidate = candidate.add(previous.distanceTo(location));\n            if (location.equals(node) && occurrenceIndex == index++)\n            {\n                return Ratio.ratio(candidate.asMeters() / max.asMeters());\n            }\n            previous = location;\n        }\n        throw new CoreException(\"The location {} is not a node of the PolyLine\", node);\n    }\n\n    public Location offsetFromStart(final Ratio ratio)\n    {\n        final Distance length = length();\n        final Distance stop = length.scaleBy(ratio);\n        Distance accumulated = Distance.ZERO;\n        final List<Segment> segments = this.segments();\n\n        for (final Segment segment : segments)\n        {\n            if (accumulated.add(segment.length()).isGreaterThan(stop))\n            {\n                // This is the proper segment\n                final Ratio segmentRatio = Ratio.ratio(\n                        stop.substract(accumulated).asMeters() / segment.length().asMeters());\n                return segment.offsetFromStart(segmentRatio);\n            }\n            if (accumulated.add(segment.length()).equals(stop))\n            {\n                return segment.end();\n            }\n            accumulated = accumulated.add(segment.length());\n        }\n        throw new CoreException(\"This exception should never be thrown.\");\n    }\n\n    /**\n     * @return The overall heading of the {@link PolyLine}: the heading between the start point and\n     *         the end point.\n     */\n    public Optional<Heading> overallHeading()\n    {\n        if (this.isPoint())\n        {\n            logger.warn(\"Cannot compute a PolyLine's heading when it has zero length : {}\", this);\n            return Optional.empty();\n        }\n        if (this.first().equals(this.last()))\n        {\n            if (logger.isWarnEnabled())\n            {\n                logger.warn(\"Cannot compute overall heading when the polyline has \"\n                        + \"same start and end locations : {}\", this.first().toWkt());\n            }\n            return Optional.empty();\n        }\n        return Optional.ofNullable(this.first().headingTo(this.last()));\n    }\n\n    /**\n     * Tests if this {@link PolyLine} has at least the same shape as another {@link PolyLine}. If\n     * this {@link PolyLine} is made up of {@link Segment}s ABC and the given {@link PolyLine} is\n     * made up of BC, this would return true, despite the excess {@link Segment}.\n     *\n     * @param other\n     *            The other {@link PolyLine} to compare to\n     * @return True if this {@link PolyLine} has at least the same shape as the other (but possibly\n     *         more)\n     */\n    public boolean overlapsShapeOf(final PolyLine other)\n    {\n        final Set<Segment> thisSegments = new HashSet<>();\n        final List<Segment> segments = this.segments();\n        segments.forEach(segment ->\n        {\n            thisSegments.add(segment);\n            thisSegments.add(segment.reversed());\n        });\n\n        final List<Segment> otherSegments = other.segments();\n        for (final Segment otherSegment : otherSegments)\n        {\n            if (!thisSegments.contains(otherSegment))\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Prepends the given {@link PolyLine} to this one, if possible.\n     *\n     * @param other\n     *            The {@link PolyLine} to prepend\n     * @return the new, combined {@link PolyLine}\n     */\n    public PolyLine prepend(final PolyLine other)\n    {\n        if (this.first().equals(other.last()))\n        {\n            return new PolyLine(new MultiIterable<>(other, this.truncate(1, 0)));\n        }\n        else\n        {\n            throw new CoreException(\n                    \"Cannot prepend {} to {} - the end and start points do not match.\",\n                    other.toWkt(), this.toWkt());\n        }\n    }\n\n    @Override\n    public boolean remove(final Object object)\n    {\n        throw new IllegalAccessError(IMMUTABLE_POLYLINE);\n    }\n\n    @Override\n    public boolean removeAll(final Collection<?> collection)\n    {\n        throw new IllegalAccessError(IMMUTABLE_POLYLINE);\n    }\n\n    @Override\n    public boolean retainAll(final Collection<?> collection)\n    {\n        throw new IllegalAccessError(IMMUTABLE_POLYLINE);\n    }\n\n    public PolyLine reversed()\n    {\n        final List<Location> reversed = new ArrayList<>();\n        for (int i = this.size() - 1; i >= 0; i--)\n        {\n            reversed.add(this.get(i));\n        }\n        return new PolyLine(reversed);\n    }\n\n    public void saveAsGeoJson(final WritableResource resource)\n    {\n        final List<Iterable<Location>> geometries = new ArrayList<>();\n        geometries.add(this);\n        saveAsGeoJson(geometries, resource);\n    }\n\n    /**\n     * @return All the {@link Segment}s that represent this {@link PolyLine}. If the\n     *         {@link PolyLine} is empty, then the {@link Segment} list is empty. If the\n     *         {@link PolyLine} has only one item, the {@link Segment} list contains only one\n     *         {@link Segment} made of twice the same {@link Location}. If the {@link PolyLine} has\n     *         more than one {@link Location}, then the result is a list of {@link Segment}s. Note:\n     *         This method should be used carefully. Each call to it will cause a rebuild of the\n     *         {@link List}, which can be very inefficient for long {@link PolyLine}s. To avoid\n     *         this, the caller can call segments once and cache the results.\n     */\n    public List<Segment> segments()\n    {\n        final List<Segment> result = new ArrayList<>(this.size());\n        if (size() == 1)\n        {\n            result.add(new Segment(get(0), get(0)));\n        }\n        else if (this instanceof Segment)\n        {\n            result.add((Segment) this);\n        }\n        else\n        {\n            Location previous = null;\n            for (final Location location : this)\n            {\n                if (previous == null)\n                {\n                    previous = location;\n                    continue;\n                }\n                result.add(new Segment(previous, location));\n                previous = location;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Returns a Set of {@link Location} for all self-intersections, other than shape points for\n     * this {@link PolyLine}. Separated from selfIntersects() to avoid degrading its performance.\n     *\n     * @return the set of locations\n     */\n    public Set<Location> selfIntersections()\n    {\n        Set<Location> intersections = null;\n        final boolean isPolygon = this instanceof Polygon;\n        if (this.isSimple() && !isPolygon)\n        {\n            if (this.isClosed())\n            {\n                intersections = new HashSet<>();\n                intersections.add(this.first());\n                return intersections;\n            }\n            return Collections.emptySet();\n        }\n\n        // Exclude point-segments, so we know which segments are actually consecutive\n        final List<Segment> segments = this.segments().stream()\n                .filter(segment -> !segment.isPoint()).collect(Collectors.toList());\n\n        // Consecutive segments should not be considered (they always have common point)\n        for (int i = 0; i < segments.size() - 2; i++)\n        {\n            // For Polygons the last segment is consecutive to the first\n            final int limit = isPolygon && i == 0 ? segments.size() - 1 : segments.size();\n\n            // Only consider 'higher' segments.\n            // No need to do a reverse check with the 'lower' segments again.\n            for (int j = i + 2; j < limit; j++)\n            {\n                final Location intersection = segments.get(i).intersection(segments.get(j));\n\n                if (intersection != null)\n                {\n                    // Self-intersection is a low probability event.\n                    // Only allocate if needed\n                    if (intersections == null)\n                    {\n                        intersections = new HashSet<>();\n                    }\n\n                    intersections.add(intersection);\n                }\n            }\n        }\n\n        return intersections == null ? Collections.emptySet() : intersections;\n    }\n\n    /**\n     * @return True if the {@link PolyLine} self intersects at locations other than shape points.\n     */\n    public boolean selfIntersects()\n    {\n        // See comments on algorithm in selfIntersections()\n\n        final boolean isPolygon = this instanceof Polygon;\n        final List<Segment> segments = this.segments().stream()\n                .filter(segment -> !segment.isPoint()).collect(Collectors.toList());\n\n        for (int i = 0; i < segments.size() - 2; i++)\n        {\n            final int limit = isPolygon && i == 0 ? segments.size() - 1 : segments.size();\n            for (int j = i + 2; j < limit; j++)\n            {\n                if (segments.get(i).intersection(segments.get(j)) != null)\n                {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    public PolyLine shiftFirstAlongGreatCircle(final Heading initialHeading,\n            final Distance distance)\n    {\n        return new PolyLine(new MultiIterable<>(\n                Iterables.from(this.first().shiftAlongGreatCircle(initialHeading, distance)),\n                this.truncate(1, 0)));\n    }\n\n    public PolyLine shiftLastAlongGreatCircle(final Heading initialHeading, final Distance distance)\n    {\n        return new PolyLine(new MultiIterable<>(this.truncate(0, 1),\n                Iterables.from(this.last().shiftAlongGreatCircle(initialHeading, distance))));\n    }\n\n    /**\n     * Return the smaller one between the shortest distance from this {@link PolyLine}'s shape\n     * points to the other shape, and the other shape's shape points to this polyline.\n     *\n     * @param other\n     *            The other shape to compare to\n     * @return The two way shortest distance to the other {@link PolyLine}\n     */\n    public Distance shortestDistanceTo(final PolyLine other)\n    {\n        final Distance one = shortestOneWayDistanceTo(other);\n        final Distance two = other.shortestOneWayDistanceTo(this);\n        return one.isLessThan(two) ? one : two;\n    }\n\n    /**\n     * Return the shortest distance from this {@link PolyLine}'s shape points to the other shape,\n     * using a one-way snapping.\n     *\n     * @param other\n     *            The other shape to compare to\n     * @return The shortest one way cost distance to the other {@link PolyLine}\n     */\n    public Distance shortestOneWayDistanceTo(final PolyLine other)\n    {\n        Distance shortest = Distance.MAXIMUM;\n        for (final Location shapePoint : this)\n        {\n            final Distance current = shapePoint.snapTo(other).getDistance();\n            shortest = current.isLessThan(shortest) ? current : shortest;\n        }\n        return shortest;\n    }\n\n    @Override\n    public int size()\n    {\n        return this.points.size();\n    }\n\n    /**\n     * Snap an origin {@link Location} to this {@link PolyLine} using a {@link Snapper}\n     *\n     * @param origin\n     *            The origin {@link Location} to snap\n     * @return The corresponding {@link SnappedLocation}\n     */\n    public SnappedLocation snapFrom(final Location origin)\n    {\n        return new Snapper().snap(origin, this);\n    }\n\n    @Override\n    public Object[] toArray()\n    {\n        return this.points.toArray();\n    }\n\n    @Override\n    public <T> T[] toArray(final T[] array)\n    {\n        return this.points.toArray(array);\n    }\n\n    public String toCompactString()\n    {\n        final StringList stringList = new StringList();\n        this.forEach(location ->\n        {\n            stringList.add(location.toCompactString());\n        });\n        return stringList.join(SEPARATOR);\n    }\n\n    public String toSimpleString()\n    {\n        final String string = toCompactString();\n        if (string.length() > SIMPLE_STRING_LENGTH + 1)\n        {\n            return string.substring(0, SIMPLE_STRING_LENGTH / 2) + \"...\"\n                    + string.substring(string.length() - SIMPLE_STRING_LENGTH / 2);\n        }\n        return string;\n    }\n\n    @Override\n    public String toString()\n    {\n        return toWkt();\n    }\n\n    /**\n     * @return This {@link PolyLine} as Well Known Binary\n     */\n    @Override\n    public byte[] toWkb()\n    {\n        if (this.size() == 1)\n        {\n            // Handle a single location polyLine\n            return new WkbLocationConverter().convert(this.first());\n        }\n        return new WkbPolyLineConverter().convert(this);\n    }\n\n    /**\n     * @return This {@link PolyLine} as Well Known Text\n     */\n    @Override\n    public String toWkt()\n    {\n        if (this.size() == 1)\n        {\n            // Handle a single location polyLine\n            return new WktLocationConverter().convert(this.first());\n        }\n        return new WktPolyLineConverter().convert(this);\n    }\n\n    /**\n     * Truncates this {@link PolyLine} at the given start and end index. If the provided indices are\n     * invalid, an empty Iterable will be returned.\n     *\n     * @param indexFromStart\n     *            The index before which to truncate from the start\n     * @param indexFromEnd\n     *            The index after which to truncate from the end\n     * @return all the locations in this {@link PolyLine} after truncation.\n     */\n    public Iterable<Location> truncate(final int indexFromStart, final int indexFromEnd)\n    {\n        if (indexFromStart < 0 || indexFromEnd < 0 || indexFromStart >= this.size()\n                || indexFromEnd >= this.size() || indexFromStart + indexFromEnd >= this.size())\n        {\n            logger.debug(\"Invalid start index {} or end index {} supplied.\", indexFromStart,\n                    indexFromEnd);\n            return Collections.emptyList();\n        }\n\n        return Iterables.stream(this).truncate(indexFromStart, indexFromEnd);\n    }\n\n    @Override\n    public boolean within(final GeometricSurface surface)\n    {\n        return surface.fullyGeometricallyEncloses(this);\n    }\n\n    /**\n     * @return This {@link PolyLine} without duplicate consecutive shape points. Non-consecutive\n     *         shape points will remain unchanged.\n     */\n    public PolyLine withoutDuplicateConsecutiveShapePoints()\n    {\n        final List<Location> shapePoints = new ArrayList<>();\n        boolean hasDuplicates = false;\n\n        final Iterator<Location> locationIterator = this.iterator();\n        // PolyLines are only valid if at least one point exists, so it is safe to call next() once.\n        Location previousLocation = locationIterator.next();\n        shapePoints.add(previousLocation);\n\n        while (locationIterator.hasNext())\n        {\n            final Location currentLocation = locationIterator.next();\n\n            if (!currentLocation.equals(previousLocation))\n            {\n                shapePoints.add(currentLocation);\n            }\n            else\n            {\n                hasDuplicates = true;\n            }\n            previousLocation = currentLocation;\n        }\n        return hasDuplicates ? new PolyLine(shapePoints) : this;\n    }\n\n    protected final List<Location> getPoints()\n    {\n        return this.points;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Polygon.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.NoSuchElementException;\nimport java.util.Optional;\nimport java.util.stream.IntStream;\n\nimport org.locationtech.jts.algorithm.match.HausdorffSimilarityMeasure;\nimport org.locationtech.jts.geom.GeometryCollection;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.geom.prep.PreparedGeometry;\nimport org.locationtech.jts.geom.prep.PreparedGeometryFactory;\nimport org.locationtech.jts.triangulate.ConformingDelaunayTriangulationBuilder;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.converters.WkbPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.WktPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.GeometryStreamer;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsLocationConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPrecisionManager;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Lists;\nimport com.google.gson.JsonObject;\n\n/**\n * A {@link Polygon} is a {@link PolyLine} with an extra {@link Segment} between the last\n * {@link Location} and the first {@link Location}.\n *\n * @author matthieun\n */\npublic class Polygon extends PolyLine implements GeometricSurface\n{\n    public static final Polygon SILICON_VALLEY = new Polygon(Location.TEST_3, Location.TEST_7,\n            Location.TEST_4, Location.TEST_1, Location.TEST_5);\n    public static final Polygon SILICON_VALLEY_2 = new Polygon(Location.TEST_3, Location.TEST_7,\n            Location.TEST_2, Location.TEST_1, Location.TEST_5);\n    public static final Polygon TEST_BUILDING = new Polygon(\n            Location.forString(\"37.3909505256542,-122.03104734420775\"),\n            Location.forString(\"37.39031973417266,-122.03141212463377\"),\n            Location.forString(\"37.390106627742895,-122.03113317489623\"),\n            Location.forString(\"37.39084823550426,-122.03062891960144\"),\n            Location.forString(\"37.3909505256542,-122.03104734420775\"));\n    public static final Polygon TEST_BUILDING_PART = new Polygon(\n            Location.forString(\"37.390234491673446,-122.03111171722412\"),\n            Location.forString(\"37.39020252571126,-122.0311439037323\"),\n            Location.forString(\"37.39018121506223,-122.03110367059708\"),\n            Location.forString(\"37.39021104996917,-122.0310714840889\"),\n            Location.forString(\"37.390234491673446,-122.03111171722412\"));\n    public static final Polygon CENTER = new Polygon(Location.CENTER);\n\n    private static final JtsMultiPolygonToMultiPolygonConverter JTS_MULTIPOLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n    private static final JtsPolygonConverter JTS_POLYGON_CONVERTER = new JtsPolygonConverter();\n    private static final JtsPointConverter JTS_POINT_CONVERTER = new JtsPointConverter();\n    private static final JtsPolyLineConverter JTS_POLYLINE_CONVERTER = new JtsPolyLineConverter();\n\n    private static final Logger logger = LoggerFactory.getLogger(Polygon.class);\n    private static final long serialVersionUID = 2877026648358594354L;\n\n    // Calculate sides starting from triangles\n    private static final int MINIMUM_N_FOR_SIDE_CALCULATION = 3;\n    private transient PreparedGeometry prepared;\n\n    /**\n     * Generate a random polygon within bounds.\n     *\n     * @param numberPoints\n     *            The number of points in the polygon\n     * @param bounds\n     *            The bounds for the points to be in\n     * @return The random {@link Polygon}\n     */\n    public static Polygon random(final int numberPoints, final Rectangle bounds)\n    {\n        final List<Location> locations = new ArrayList<>();\n        IntStream.range(0, numberPoints).forEach(index -> locations.add(Location.random(bounds)));\n        return new Polygon(locations);\n    }\n\n    /**\n     * Generate a Polygon from Well Known Text\n     *\n     * @param wkt\n     *            The polygon in well known text\n     * @return The parsed {@link Polygon}\n     */\n    public static Polygon wkt(final String wkt)\n    {\n        return new WktPolygonConverter().backwardConvert(wkt);\n    }\n\n    public Polygon(final Iterable<Location> points)\n    {\n        this(Iterables.asList(points));\n    }\n\n    public Polygon(final List<Location> points)\n    {\n        super(points);\n    }\n\n    public Polygon(final Location... points)\n    {\n        // This was Iterables.asList. `super` creates a new ArrayList, so we don't have to worry\n        // about the backing array being modified.\n        // This was 6% of a test run in a single validation (there were other validations run, so\n        // this may be larger). After the new run, it was 3% (async Allocation Profiler)\n        this(Arrays.asList(points));\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        return GeoJsonUtils.geometry(GeoJsonType.POLYGON, GeoJsonUtils.polygonToCoordinates(this));\n    }\n\n    /**\n     * The segments that belong to this {@link Polygon} that are attached to this vertex\n     *\n     * @param vertexIndex\n     *            the index of the vertex\n     * @return The segments that belong to this {@link Polygon} that are attached to this vertex\n     */\n    public List<Segment> attachedSegments(final int vertexIndex)\n    {\n        verifyVertexIndex(vertexIndex);\n        final List<Segment> result = new ArrayList<>();\n        // Previous\n        if (vertexIndex > 0)\n        {\n            result.add(segmentForIndex(vertexIndex - 1));\n        }\n        else\n        {\n            result.add(segmentForIndex(size() - 1));\n        }\n        // Next\n        result.add(segmentForIndex(vertexIndex));\n        return result;\n    }\n\n    /**\n     * This will return the centroid of a given polygon. It can handle complex polygons including\n     * multiple polygons. This will not necessarily return a location that is contained within the\n     * original polygon. For example if you have two concentric circles forming a donut shape, one\n     * smaller one contained within the bigger one. The centroid of that polygon will be at the\n     * center technically outside of the polygon. This is a very different concept to a\n     * representative point.\n     *\n     * @return a Location object that is the centroid of the polygon\n     */\n    public Location center()\n    {\n        final Point point = JTS_POLYGON_CONVERTER.convert(this).getCentroid();\n        return new JtsLocationConverter().backwardConvert(point.getCoordinate());\n    }\n\n    /**\n     * @return An iterable of {@link Location}s that will return the first item again at the end.\n     */\n    public Iterable<Location> closedLoop()\n    {\n        if (!this.first().equals(this.last()))\n        {\n            return new MultiIterable<>(this, Iterables.from(this.first()));\n        }\n        return this;\n    }\n\n    /**\n     * Tests if this {@link Polygon} fully encloses (geometrically contains) a {@link Location}\n     * <p>\n     * Here is the definition of contains (insideness) of awt point.\n     * <p>\n     * Definition of insideness: A point is considered to lie inside a Shape if and only if: it lies\n     * completely inside the Shape boundary or it lies exactly on the Shape boundary and the space\n     * immediately adjacent to the point in the increasing X direction is entirely inside the\n     * boundary or it lies exactly on a horizontal boundary segment and the space immediately\n     * adjacent to the point in the increasing Y direction is inside the boundary.\n     * <p>\n     * In the case of a massive polygon (larger than 75% of the earth's width) the JTS definition of\n     * covers is used instead, which will return true if the location lies within the polygon or\n     * anywhere on the boundary.\n     * <p>\n     *\n     * @param location\n     *            The {@link Location} to test\n     * @return True if the {@link Polygon} contains the {@link Location}\n     */\n    @Override\n    public boolean fullyGeometricallyEncloses(final Location location)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory.prepare(JTS_POLYGON_CONVERTER.convert(this));\n        }\n        return this.prepared.covers(JTS_POINT_CONVERTER.convert(location));\n    }\n\n    @Override\n    public boolean fullyGeometricallyEncloses(final MultiPolygon multiPolygon)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory.prepare(JTS_POLYGON_CONVERTER.convert(this));\n        }\n        return this.prepared.covers(JTS_MULTIPOLYGON_CONVERTER.backwardConvert(multiPolygon));\n    }\n\n    /**\n     * Tests if this {@link Polygon} fully encloses (geometrically contains) a {@link PolyLine}.\n     * Note: this will return false for the case when the {@link Polygon} and given {@link PolyLine}\n     * are stacked on top of each other - i.e. have an identical shape as one another.\n     *\n     * @param polyLine\n     *            The {@link PolyLine} to test\n     * @return True if this {@link Polygon} wraps (geometrically contains) the provided\n     *         {@link PolyLine}\n     */\n    @Override\n    public boolean fullyGeometricallyEncloses(final PolyLine polyLine)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory.prepare(JTS_POLYGON_CONVERTER.convert(this));\n        }\n        if (polyLine instanceof Polygon)\n        {\n            return this.prepared.covers(JTS_POLYGON_CONVERTER.convert((Polygon) polyLine));\n        }\n        return this.prepared.covers(JTS_POLYLINE_CONVERTER.convert(polyLine));\n    }\n\n    /**\n     * Tests if this {@link Polygon} fully encloses (geometrically contains) a {@link Rectangle}.\n     * Note: this will return false for the case when the {@link Polygon} has an identical shape as\n     * the given {@link Rectangle}.\n     *\n     * @param rectangle\n     *            The {@link Rectangle} to test\n     * @return True if this {@link Polygon} wraps (geometrically contains) the provided\n     *         {@link Rectangle}\n     */\n    public boolean fullyGeometricallyEncloses(final Rectangle rectangle)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory.prepare(JTS_POLYGON_CONVERTER.convert(this));\n        }\n        return this.prepared.covers(JTS_POLYGON_CONVERTER.convert(rectangle));\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.POLYGON;\n    }\n\n    /**\n     * Returns a location that is the closest point within the polygon to the centroid. The function\n     * delegates to the Geometry class which delegates to the InteriorPointPoint class. You can see\n     * the javadocs in the link below. <a href=\n     * \"https://locationtech.github.io/jts/javadoc/org/locationtech/jts/algorithm/InteriorPointPoint.html\"></a>\n     *\n     * @return location that is the closest point within the polygon to the centroid\n     */\n    public Location interiorCenter()\n    {\n        final Point point = JTS_POLYGON_CONVERTER.convert(this).getInteriorPoint();\n        return new JtsLocationConverter().backwardConvert(point.getCoordinate());\n    }\n\n    /**\n     * @param expectedNumberOfSides\n     *            Expected number of sides\n     * @param threshold\n     *            {@link Angle} threshold that decides whether a {@link Heading} difference between\n     *            segments should be counted towards heading change count or not\n     * @return true if this {@link Polygon} has approximately n sides while ignoring {@link Heading}\n     *         differences between inner segments that are below given threshold.\n     */\n    public boolean isApproximatelyNSided(final int expectedNumberOfSides, final Angle threshold)\n    {\n        // Ignore if polygon doesn't have enough inner shape points\n        if (expectedNumberOfSides < MINIMUM_N_FOR_SIDE_CALCULATION\n                || this.size() < expectedNumberOfSides)\n        {\n            return false;\n        }\n\n        // An N sided shape should have (n-1) heading changes\n        final int expectedHeadingChangeCount = expectedNumberOfSides - 1;\n\n        // Fetch segments and count them\n        final List<Segment> segments = this.segments();\n        final int segmentSize = segments.size();\n\n        // Index to keep track of segment to work on\n        int segmentIndex = 0;\n\n        // Keep track of heading changes\n        int headingChangeCount = 0;\n\n        // Find initial heading\n        Optional<Heading> previousHeading = Optional.empty();\n        while (segmentIndex < segmentSize)\n        {\n            // Make sure we start with some heading. Edges with single points do not have heading.\n            previousHeading = segments.get(segmentIndex++).heading();\n            if (previousHeading.isPresent())\n            {\n                break;\n            }\n        }\n\n        // Make sure we start with some heading\n        if (!previousHeading.isPresent())\n        {\n            logger.trace(\"{} doesn't have a heading to calculate number of sides.\", this);\n            return false;\n        }\n\n        // Go over rest of the segments and count heading changes\n        while (segmentIndex < segmentSize && headingChangeCount <= expectedHeadingChangeCount)\n        {\n            final Optional<Heading> nextHeading = segments.get(segmentIndex++).heading();\n\n            // If heading difference is greater than threshold, then increment heading\n            // change counter and update previous heading, which is used as reference\n            if (nextHeading.isPresent()\n                    && previousHeading.get().difference(nextHeading.get()).isGreaterThan(threshold))\n            {\n                headingChangeCount++;\n                previousHeading = nextHeading;\n            }\n        }\n\n        return headingChangeCount == expectedHeadingChangeCount;\n    }\n\n    /**\n     * @return True if this {@link Polygon} is arranged clockwise, false otherwise.\n     * @see <a href=\n     *      \"http://www.gutenberg.org/files/19770/19770-pdf.pdf?session_id=374cbf5aca81b1a742aac0879dbea5eb35f914ea\"></a>\n     * @see <a href=\"http://mathforum.org/library/drmath/view/51879.html\"></a>\n     * @see <a href=\"http://mathforum.org/library/drmath/view/65316.html\"></a>\n     */\n    public boolean isClockwise()\n    {\n        // Formula to calculate the area of triangle on a sphere is (A + B + C - Pi) * radius *\n        // radius.\n        // Equation (A + B + C - Pi) is called the spherical excess. We are going to divide our\n        // polygon in triangles and then calculate the signed area of each triangle. Sum of the\n        // areas of these triangles will be the area of this polygon\n        double sphericalExcess = 0;\n        Location previousLocation = null;\n\n        for (final Location point : this.closedLoop())\n        {\n            final Location currentLocation = point;\n\n            if (previousLocation != null)\n            {\n                // for the sake of simplicity we are using two vertices from the polygon and the\n                // third vertex would be North Pole.\n                // Please refer \"Spherical Trigonometry by I.Todhunter\".\n                // Section starting on page 7 and 17 for triangle identities and trigonometric\n                // functions.\n                // Also look on page 71 for getting the area of triangle\n                final double latitudeOne = previousLocation.getLatitude().asRadians();\n                final double latitudeTwo = currentLocation.getLatitude().asRadians();\n                final double deltaLongitude = currentLocation.getLongitude().asRadians()\n                        - previousLocation.getLongitude().asRadians();\n\n                final double alpha = Math\n                        .sqrt((1 - Math.sin(latitudeOne)) / (1 + Math.sin(latitudeOne)))\n                        * Math.sqrt((1 - Math.sin(latitudeTwo)) / (1 + Math.sin(latitudeTwo)));\n\n                // You can derive this from the formula on Page 74, point 102 of the book\n                sphericalExcess += 2 * Math.atan2(alpha * Math.sin(deltaLongitude),\n                        1 + alpha * Math.cos(deltaLongitude));\n            }\n            previousLocation = currentLocation;\n        }\n\n        // Instead of area of polygon this method returns the spherical access as multiplying with\n        // Earth (radius) ^ 2 is not going to change the sign of the area\n        return sphericalExcess <= 0;\n    }\n\n    public boolean isSimilarTo(final Polygon other)\n    {\n        final double similarity = new HausdorffSimilarityMeasure()\n                .measure(JTS_POLYGON_CONVERTER.convert(this), JTS_POLYGON_CONVERTER.convert(other));\n        return similarity > SIMILARITY_THRESHOLD;\n    }\n\n    public int nextSegmentIndex(final int currentVertexIndex)\n    {\n        verifyVertexIndex(currentVertexIndex);\n        return currentVertexIndex;\n    }\n\n    public int nextVertexIndex(final int currentVertexIndex)\n    {\n        verifyVertexIndex(currentVertexIndex);\n        if (currentVertexIndex == size() - 1)\n        {\n            return 0;\n        }\n        else\n        {\n            return currentVertexIndex + 1;\n        }\n    }\n\n    @Override\n    public boolean overlaps(final MultiPolygon multiPolygon)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory.prepare(JTS_POLYGON_CONVERTER.convert(this));\n        }\n        return this.prepared.intersects(JTS_MULTIPOLYGON_CONVERTER.backwardConvert(multiPolygon));\n    }\n\n    /**\n     * Tests if this {@link Polygon} intersects/overlaps the given {@link PolyLine} at any point.\n     * This is different than the {@link #fullyGeometricallyEncloses(PolyLine)} method, in that it\n     * doesn't require full containment within the {@link Polygon}, just any overlap.\n     *\n     * @param polyline\n     *            The {@link PolyLine} to test\n     * @return True if this {@link Polygon} intersects/overlaps the given {@link PolyLine}.\n     */\n    @Override\n    public boolean overlaps(final PolyLine polyline)\n    {\n        if (this.prepared == null)\n        {\n            this.prepared = PreparedGeometryFactory.prepare(JTS_POLYGON_CONVERTER.convert(this));\n        }\n        if (polyline instanceof Polygon)\n        {\n            return this.prepared.intersects(JTS_POLYGON_CONVERTER.convert((Polygon) polyline));\n        }\n        return this.prepared.intersects(JTS_POLYLINE_CONVERTER.convert(polyline));\n    }\n\n    public int previousSegmentIndex(final int currentVertexIndex)\n    {\n        return previousVertexIndex(currentVertexIndex);\n    }\n\n    public int previousVertexIndex(final int currentVertexIndex)\n    {\n        verifyVertexIndex(currentVertexIndex);\n        if (currentVertexIndex == 0)\n        {\n            return size() - 1;\n        }\n        else\n        {\n            return currentVertexIndex - 1;\n        }\n    }\n\n    @Override\n    public Polygon reversed()\n    {\n        return new Polygon(super.reversed().getPoints());\n    }\n\n    public Segment segmentForIndex(final int index)\n    {\n        if (index >= size())\n        {\n            throw new CoreException(\"Invalid index\");\n        }\n        return new Segment(this.get(index),\n                index == size() - 1 ? this.get(0) : this.get(index + 1));\n    }\n\n    @Override\n    public List<Segment> segments()\n    {\n        final List<Segment> result = super.segments();\n        // close the loop\n        result.add(new Segment(last(), first()));\n        return result;\n    }\n\n    /**\n     * @return The surface of this polygon. Not valid if the polygon self-intersects, and/or\n     *         overlaps itself\n     * @see \"http://www.mathopenref.com/coordpolygonarea2.html\"\n     */\n    @Override\n    public Surface surface()\n    {\n        long dm7Squared = 0L;\n        final Iterator<Location> loopOnItself = loopOnItself().iterator();\n        if (!loopOnItself.hasNext())\n        {\n            return Surface.forDm7Squared(0);\n        }\n        Location current = loopOnItself.next();\n        Location next = null;\n        while (loopOnItself.hasNext())\n        {\n            next = loopOnItself.next();\n            dm7Squared += (current.getLongitude().asDm7() + next.getLongitude().asDm7())\n                    * (current.getLatitude().asDm7() - next.getLatitude().asDm7());\n            current = next;\n        }\n        return Surface.forDm7Squared(Math.abs(Math.round(dm7Squared / 2.0)));\n    }\n\n    /**\n     * @return The approximate surface area of this polygon if it were projected onto the Earth. Not\n     *         valid if the polygon self-intersects, and/or overlaps itself. Uses \"Some Algorithms\n     *         for Polygons on a Sphere\" paper as reference.\n     * @see \"https://trs.jpl.nasa.gov/bitstream/handle/2014/41271/07-0286.pdf\"\n     */\n    @Override\n    public Surface surfaceOnSphere()\n    {\n        double dm7 = 0L;\n\n        final List<Location> locations = Lists.newArrayList(this.closedLoop());\n        if (locations.size() > 2)\n        {\n            double radians = 0L;\n            for (int index = 0; index < locations.size() - 1; index++)\n            {\n                radians += (locations.get(index + 1).getLongitude().asRadians()\n                        - locations.get(index).getLongitude().asRadians())\n                        * (2 + Math.sin(locations.get(index).getLatitude().asRadians())\n                                + Math.sin(locations.get(index + 1).getLatitude().asRadians()));\n            }\n            radians = Math.abs(radians / 2.0);\n\n            // Calculations are in Radians, convert to Degrees.\n            dm7 = radians * ((double) Angle.DM7_PER_RADIAN * (double) Angle.DM7_PER_RADIAN);\n        }\n\n        return Surface.forDm7Squared(Math.round(dm7));\n    }\n\n    /**\n     * @return This {@link Polygon} as Well Known Binary\n     */\n    @Override\n    public byte[] toWkb()\n    {\n        return new WkbPolygonConverter().convert(this);\n    }\n\n    /**\n     * @return This {@link Polygon} as Well Known Text\n     */\n    @Override\n    public String toWkt()\n    {\n        return new WktPolygonConverter().convert(this);\n    }\n\n    /**\n     * Triangulate this {@link Polygon}, using the JTS library.\n     *\n     * @return All the triangles that form this {@link Polygon}.\n     */\n    public List<Polygon> triangles()\n    {\n        final ConformingDelaunayTriangulationBuilder trianguler = new ConformingDelaunayTriangulationBuilder();\n        // Populate the delaunay triangulation builder\n        trianguler.setSites(JTS_POLYGON_CONVERTER.convert(this));\n        final GeometryCollection triangleCollection = (GeometryCollection) trianguler\n                .getTriangles(JtsPrecisionManager.getGeometryFactory());\n        // Get the output and convert back to Core Polygons, filter out the extraneous polygons from\n        // the Delaunay triangulation.\n        return Iterables.stream(GeometryStreamer.streamPolygons(triangleCollection))\n                .map(JTS_POLYGON_CONVERTER.revert())\n                .filter(polygon -> fullyGeometricallyEncloses(polygon.center())).collectToList();\n    }\n\n    /**\n     * Remove a vertex\n     *\n     * @param index\n     *            The index of the vertex to remove\n     * @return The new {@link Polygon} without the specified vertex\n     */\n    public Polygon withoutVertex(final int index)\n    {\n        if (index < 0 || index >= this.size())\n        {\n            throw new CoreException(\"{} is not a vertex index of {}\", index, this);\n        }\n        final List<Location> vertices = Iterables.asList(this);\n        vertices.remove(index);\n        return new Polygon(vertices);\n    }\n\n    /**\n     * Remove a vertex\n     *\n     * @param vertex\n     *            The vertex to remove\n     * @return The new {@link Polygon} without the specified vertex\n     */\n    public Polygon withoutVertex(final Location vertex)\n    {\n        int index = 0;\n        for (final Location location : this)\n        {\n            if (location.equals(vertex))\n            {\n                return withoutVertex(index);\n            }\n            index++;\n        }\n        throw new CoreException(\"{} is not a vertex of {}\", vertex, this);\n    }\n\n    private Iterable<Location> loopOnItself()\n    {\n        return new MultiIterable<>(this, () -> new Iterator<Location>()\n        {\n            private boolean read = false;\n\n            @Override\n            public boolean hasNext()\n            {\n                return !this.read;\n            }\n\n            @Override\n            public Location next()\n            {\n                if (hasNext())\n                {\n                    this.read = true;\n                    return first();\n                }\n                else\n                {\n                    throw new NoSuchElementException();\n                }\n            }\n        });\n    }\n\n    private void verifyVertexIndex(final int index)\n    {\n        if (index < 0 || index >= size())\n        {\n            throw new CoreException(\"Invalid Vertex Index {}.\", index);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/README.md",
    "content": "# Geography Package\n\nThis package contains all the geography tools that make handling geographical data easy.\n\n## Atlas package\n\nThe [Atlas package](atlas) contains the `Atlas`, an in-memory representation of the OSM data.\n\n## Boundary package\n\nAll the classes that help with defining administrative boundaries, and associating them with three digit country codes.\n\n## Sharding package\n\nTools that help eavenly divide the world into multiple shards. `DynamicTileSharding` is basically a quad tree.\n\n## Geography Classes\n\n### `Latitude`\n\nA `Latitude` is an `Angle` extension which is bound between -90 degrees and 90 degrees. Like `Angle`, it can be created from degrees, radians, or a degree of magnitude 7 (dm7) long.\n\n### `Longitude`\n\nA `Longitude` is an `Angle` extension which is bound between -180 degrees and 179.9999999 degrees. Like `Angle`, it can be created from degrees, radians, or a degree of magnitude 7 (dm7) long. Unlike OSM which allows -180 and 180 alike for values that touch the antimeridian, `Longitude` allows only -180.\n\n### `Location`\n\nA `Location` is a set of two coordinates, one `Latitude` and one `Longitude`.\n\n### `Heading`\n\nA `Heading` is an extension of `Angle` which represents the 0 to 360 degrees cardinal direction between North and some other direction. North has a heading of 0 degrees, East 90 degrees, South 180 degrees, and West 270 degrees.\n\n### `PolyLine`, `Polygon` and `Segment`\n\nA `PolyLine` is a list of `Location`s. A `Polygon` is an extension of `PolyLine` where we assume that the first and last point are joined. The first and last point are not duplicated here. A `Segment` is a subset of `PolyLine` with only two points. `PolyLine`s and `Polygon`s can return the list of their `Segment`s.\n\n### `Rectangle` and `Located`\n\n`Rectangle` is a bounding box between two `Latitude`s and `Longitude`s. Anything that is `Located` has to return bounds as a `Rectangle`. A `Rectangle` is also a `Polygon` with 4 points.\n\n### `MultiPolygon`\n\nA `MultiPolygon` is a collection of outer `Polygon`s and their set of inner `Polygon`s. It is generally used to represent shapes with holes.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Rectangle.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.awt.geom.Rectangle2D;\nimport java.util.Iterator;\nimport java.util.Set;\n\nimport org.locationtech.jts.geom.Envelope;\nimport org.opengis.geometry.BoundingBox;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * A rectangle on the surface of earth. It cannot span the date change line (longitude -180)\n *\n * @author matthieun\n */\npublic final class Rectangle extends Polygon\n{\n    public static final Rectangle MAXIMUM = forCorners(\n            new Location(Latitude.MINIMUM, Longitude.MINIMUM),\n            new Location(Latitude.MAXIMUM, Longitude.MAXIMUM));\n    public static final Rectangle MINIMUM = forCorners(Location.CENTER, Location.CENTER);\n    public static final Rectangle TEST_RECTANGLE = forString(\n            \"37.328167,-122.031905:37.330394,-122.029051\");\n    public static final Rectangle TEST_RECTANGLE_2 = forString(\n            \"37.325194,-122.034281:37.325683,-122.033500\");\n    private static final long serialVersionUID = 6940095569975683891L;\n    private static final Logger logger = LoggerFactory.getLogger(Rectangle.class);\n    // A rectangle stores only two locations, despite being a 4 location Polygon.\n    private final Location lowerLeft;\n    private final Location upperRight;\n\n    public static Rectangle forBoundingBox(final BoundingBox boundingBox)\n    {\n        return Rectangle.forLocations(\n                new Location(Latitude.degrees(boundingBox.getMinY()),\n                        Longitude.degrees(boundingBox.getMinX())),\n                new Location(Latitude.degrees(boundingBox.getMaxY()),\n                        Longitude.degrees(boundingBox.getMaxX())));\n    }\n\n    /**\n     * Create a {@link Rectangle} from the lower left and upper right corners\n     *\n     * @param lowerLeft\n     *            The lower left corner\n     * @param upperRight\n     *            The upper right corner\n     * @return The resulting {@link Rectangle}\n     */\n    public static Rectangle forCorners(final Location lowerLeft, final Location upperRight)\n    {\n        if (lowerLeft == null || upperRight == null)\n        {\n            throw new CoreException(\"Cannot build a Rectangle with one of the corners being null.\");\n        }\n        // Sanity check to avoid invalid Rectangles\n        else if (lowerLeft.isNorthOf(upperRight))\n        {\n            throw new CoreException(\"Lower left cannot be higher than upper right.\");\n        }\n        else if (lowerLeft.isEastOf(upperRight))\n        {\n            throw new CoreException(\"Lower left cannot be to the right of the upper right.\");\n        }\n        return new Rectangle(lowerLeft, upperRight);\n    }\n\n    /**\n     * Build a {@link Rectangle} that wraps around an {@link Iterable} of {@link Located} objects.\n     *\n     * @param locateds\n     *            The {@link Iterable} of {@link Located} objects\n     * @param <T>\n     *            The type of located object.\n     * @return The resulting {@link Rectangle}\n     */\n    public static <T extends Located> Rectangle forLocated(final Iterable<T> locateds)\n    {\n        Latitude lower = null;\n        Latitude upper = null;\n        Longitude left = null;\n        Longitude right = null;\n        boolean hasOneItem = false;\n        for (final Located located : locateds)\n        {\n            hasOneItem = true;\n            for (final Location location : located.bounds())\n            {\n                final Latitude latitude = location.getLatitude();\n                final Longitude longitude = location.getLongitude();\n                if (lower == null || latitude.isLessThan(lower))\n                {\n                    lower = latitude;\n                }\n                if (upper == null || latitude.isGreaterThan(upper))\n                {\n                    upper = latitude;\n                }\n                if (left == null || longitude.isLessThan(left))\n                {\n                    left = longitude;\n                }\n                if (right == null || longitude.isGreaterThan(right))\n                {\n                    right = longitude;\n                }\n            }\n        }\n        if (!hasOneItem)\n        {\n            throw new CoreException(\n                    \"Rectangle.forLocated(Iterable<Located>) has to have at least one item in the Iterable<Located>\");\n        }\n        return forCorners(new Location(lower, left), new Location(upper, right));\n    }\n\n    /**\n     * Build a {@link Rectangle} that wraps around an array of {@link Located} objects.\n     *\n     * @param locateds\n     *            The array of {@link Located} objects\n     * @return The resulting {@link Rectangle}\n     */\n    public static Rectangle forLocated(final Located... locateds)\n    {\n        return forLocated(Iterables.iterable(locateds));\n    }\n\n    /**\n     * Build a {@link Rectangle} that wraps around an {@link Iterable} of {@link Location} objects.\n     *\n     * @param locations\n     *            The {@link Iterable} of {@link Location} objects\n     * @return The resulting {@link Rectangle}\n     */\n    public static Rectangle forLocations(final Iterable<Location> locations)\n    {\n        Latitude lower = null;\n        Latitude upper = null;\n        Longitude left = null;\n        Longitude right = null;\n        for (final Location location : locations)\n        {\n            final Latitude latitude = location.getLatitude();\n            final Longitude longitude = location.getLongitude();\n            if (lower == null || latitude.isLessThan(lower))\n            {\n                lower = latitude;\n            }\n            if (upper == null || latitude.isGreaterThan(upper))\n            {\n                upper = latitude;\n            }\n            if (left == null || longitude.isLessThan(left))\n            {\n                left = longitude;\n            }\n            if (right == null || longitude.isGreaterThan(right))\n            {\n                right = longitude;\n            }\n        }\n        return forCorners(new Location(lower, left), new Location(upper, right));\n    }\n\n    /**\n     * Build a {@link Rectangle} that wraps around an array of {@link Location} objects.\n     *\n     * @param locations\n     *            The array of {@link Location} objects\n     * @return The resulting {@link Rectangle}\n     */\n    public static Rectangle forLocations(final Location... locations)\n    {\n        return Rectangle.forLocations(Iterables.iterable(locations));\n    }\n\n    /**\n     * @param rectangleString\n     *            The string definition\n     * @return The resulting {@link Rectangle} parsed from its string definition\n     */\n    public static Rectangle forString(final String rectangleString)\n    {\n        final StringList split = StringList.split(rectangleString, \":\");\n        if (split.size() != 2)\n        {\n            throw new CoreException(\"Invalid Rectangle String: {}\", rectangleString);\n        }\n        return forLocations(Location.forString(split.get(0)), Location.forString(split.get(1)));\n    }\n\n    /**\n     * Private constructor using the two corners\n     *\n     * @param lowerLeft\n     *            The lower left corner\n     * @param upperRight\n     *            The upper right corner\n     */\n    private Rectangle(final Location lowerLeft, final Location upperRight)\n    {\n        super(lowerLeft, new Location(upperRight.getLatitude(), lowerLeft.getLongitude()),\n                upperRight, new Location(lowerLeft.getLatitude(), upperRight.getLongitude()));\n        this.lowerLeft = lowerLeft;\n        this.upperRight = upperRight;\n    }\n\n    /**\n     * @return JTS object {@link Envelope}, which is an equivalent of {@link Rectangle}\n     */\n    public Envelope asEnvelope()\n    {\n        return new Envelope(this.lowerLeft.getLongitude().asDegrees(),\n                this.upperRight.getLongitude().asDegrees(),\n                this.lowerLeft.getLatitude().asDegrees(),\n                this.upperRight.getLatitude().asDegrees());\n    }\n\n    public JsonArray asGeoJsonBbox()\n    {\n        final JsonArray array = new JsonArray();\n        array.add(new JsonPrimitive(this.lowerLeft.getLongitude().asDegrees()));\n        array.add(new JsonPrimitive(this.lowerLeft.getLatitude().asDegrees()));\n        array.add(new JsonPrimitive(this.upperRight.getLongitude().asDegrees()));\n        array.add(new JsonPrimitive(this.upperRight.getLatitude().asDegrees()));\n        return array;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this;\n    }\n\n    @Override\n    public Location center()\n    {\n        return new Segment(this.lowerLeft, this.upperRight).middle();\n    }\n\n    /**\n     * @param that\n     *            The other {@link Rectangle} to combine\n     * @return The {@link Rectangle} wrapping this {@link Rectangle} and the one passed as an\n     *         argument.\n     */\n    public Rectangle combine(final Rectangle that)\n    {\n        return Rectangle.forLocations(this.lowerLeft, this.upperRight, that.lowerLeft,\n                that.upperRight);\n    }\n\n    /**\n     * Contract the rectangle in 4 directions as far as possible. If the distance to move the\n     * corners would invert the rectangle then the side(s) will collapse into length 0. The most\n     * that it can contract is to a single point in the middle.\n     *\n     * @param distance\n     *            to contract the four corners\n     * @return new rectangle with contracted dimensions\n     */\n    public Rectangle contract(final Distance distance)\n    {\n        final Location newLowerLeft = this.lowerLeft.shiftAlongGreatCircle(Heading.NORTH, distance)\n                .shiftAlongGreatCircle(Heading.EAST, distance);\n        final Location newUpperRight = this.upperRight\n                .shiftAlongGreatCircle(Heading.SOUTH, distance)\n                .shiftAlongGreatCircle(Heading.WEST, distance);\n        final boolean tooShortHeight = newLowerLeft.getLatitude()\n                .isGreaterThan(newUpperRight.getLatitude());\n        final boolean tooShortWidth = newLowerLeft.getLongitude()\n                .isGreaterThan(newUpperRight.getLongitude());\n        if (tooShortHeight && tooShortWidth)\n        {\n            return this.center().bounds();\n        }\n        else\n        {\n            final Location lowerRight = new Location(this.lowerLeft().getLatitude(),\n                    this.upperRight().getLongitude());\n            if (tooShortHeight)\n            {\n                final Latitude sharedLatitude = lowerRight.midPoint(this.upperRight())\n                        .getLatitude();\n                return forCorners(new Location(sharedLatitude, newLowerLeft.getLongitude()),\n                        new Location(sharedLatitude, newUpperRight.getLongitude()));\n            }\n            else if (tooShortWidth)\n            {\n                final Longitude sharedLongitude = lowerRight.midPoint(this.lowerLeft())\n                        .getLongitude();\n                return forCorners(new Location(newLowerLeft.getLatitude(), sharedLongitude),\n                        new Location(newUpperRight.getLatitude(), sharedLongitude));\n            }\n            else\n            {\n                return forCorners(newLowerLeft, newUpperRight);\n            }\n        }\n    }\n\n    /**\n     * Expand a given distance in all four directions\n     *\n     * @param distance\n     *            The {@link Distance} to expand\n     * @return The expanded {@link Rectangle}\n     */\n    public Rectangle expand(final Distance distance)\n    {\n        final Rectangle expandedVertically = this.expandVertically(distance);\n        return expandedVertically.expandHorizontally(distance);\n    }\n\n    /**\n     * Expand a given distance horizontally, on both directions\n     *\n     * @param distance\n     *            The {@link Distance} to expand\n     * @return The expanded {@link Rectangle}\n     */\n    public Rectangle expandHorizontally(final Distance distance)\n    {\n        final Location newLowerLeft = this.lowerLeft.shiftAlongGreatCircle(Heading.WEST, distance);\n        final Location newUpperRight = this.upperRight.shiftAlongGreatCircle(Heading.EAST,\n                distance);\n        return forCorners(newLowerLeft, newUpperRight);\n    }\n\n    /**\n     * Expand a given distance vertically, on both directions\n     *\n     * @param distance\n     *            The {@link Distance} to expand\n     * @return The expanded {@link Rectangle}\n     */\n    public Rectangle expandVertically(final Distance distance)\n    {\n        final double degreesLatitudeToShift = distance.asMeters()\n                / Distance.APPROXIMATE_DISTANCE_PER_DEGREE_AT_EQUATOR.asMeters();\n        final long meterBuffer = 1;\n\n        final Location oldLowerLeft = this.lowerLeft;\n        Distance southShiftDistance = distance;\n        // If the lowerLeft is about to be shifted south of the South Pole, stop it!\n        if (oldLowerLeft.getLatitude().asDegrees() - degreesLatitudeToShift <= Latitude.MINIMUM\n                .asDegrees())\n        {\n            logger.warn(\n                    \"Provided distance {} would have shifted past the South Pole, truncating southward expansion...\",\n                    distance);\n            final double degreesToHitMinimum = -1\n                    * (Latitude.MINIMUM.asDegrees() - oldLowerLeft.getLatitude().asDegrees());\n            // subtract a small buffer off the distance to just miss the pole.\n            southShiftDistance = Distance\n                    .meters((Distance.APPROXIMATE_DISTANCE_PER_DEGREE_AT_EQUATOR.asMeters()\n                            * degreesToHitMinimum) - meterBuffer);\n        }\n        final Location lowerLeftShiftedSouth = oldLowerLeft.shiftAlongGreatCircle(Heading.SOUTH,\n                southShiftDistance);\n\n        final Location oldUpperRight = this.upperRight;\n        Distance northShiftDistance = distance;\n        // If upperRight is about to be shifted north of the North Pole, stop it!\n        if (oldUpperRight.getLatitude().asDegrees() + degreesLatitudeToShift >= Latitude.MAXIMUM\n                .asDegrees())\n        {\n            logger.warn(\n                    \"Provided distance {} would have shifted past the North Pole, truncating northward expansion...\",\n                    distance);\n            final double degreesToHitMaximum = Latitude.MAXIMUM.asDegrees()\n                    - oldUpperRight.getLatitude().asDegrees();\n            // subtract a small buffer off the distance to just miss the pole.\n            northShiftDistance = Distance\n                    .meters((Distance.APPROXIMATE_DISTANCE_PER_DEGREE_AT_EQUATOR.asMeters()\n                            * degreesToHitMaximum) - meterBuffer);\n        }\n        final Location upperRightShiftedNorth = oldUpperRight.shiftAlongGreatCircle(Heading.NORTH,\n                northShiftDistance);\n\n        return forCorners(lowerLeftShiftedSouth, upperRightShiftedNorth);\n    }\n\n    /**\n     * Test if this rectangle fully encloses a {@link Located} item\n     *\n     * @param item\n     *            The item to test\n     * @return True if this rectangle contains a {@link Located} item\n     */\n    public boolean fullyGeometricallyEncloses(final Located item)\n    {\n        final Rectangle bounds = item instanceof Rectangle ? (Rectangle) item : item.bounds();\n        return this.lowerLeft().getLatitude().isLessThanOrEqualTo(bounds.lowerLeft().getLatitude())\n                && this.lowerLeft().getLongitude()\n                        .isLessThanOrEqualTo(bounds.lowerLeft().getLongitude())\n                && this.upperRight().getLatitude()\n                        .isGreaterThanOrEqualTo(bounds.upperRight().getLatitude())\n                && this.upperRight().getLongitude()\n                        .isGreaterThanOrEqualTo(bounds.upperRight().getLongitude());\n    }\n\n    @Override\n    public boolean fullyGeometricallyEncloses(final Location item)\n    {\n        return this.fullyGeometricallyEncloses((Located) item);\n    }\n\n    @Override\n    public boolean fullyGeometricallyEncloses(final Rectangle item)\n    {\n        return this.fullyGeometricallyEncloses((Located) item);\n    }\n\n    /**\n     * @return The height of this {@link Rectangle}\n     */\n    public Angle height()\n    {\n        return Angle\n                .dm7(this.upperRight.getLatitude().asDm7() - this.lowerLeft.getLatitude().asDm7());\n    }\n\n    /**\n     * @param other\n     *            The other {@link Rectangle} to intersect\n     * @return The intersection of the two rectangles\n     */\n    public Rectangle intersection(final Rectangle other)\n    {\n        if (other == null)\n        {\n            return null;\n        }\n        if (this.equals(other))\n        {\n            return this;\n        }\n        if (this.fullyGeometricallyEncloses(other))\n        {\n            return other;\n        }\n        if (other.fullyGeometricallyEncloses(this))\n        {\n            return this;\n        }\n        final Set<Location> intersections = this.intersections(other);\n        if (intersections.size() == 0)\n        {\n            return null;\n        }\n        if (intersections.size() == 1)\n        {\n            return Rectangle.forLocations(intersections.iterator().next());\n        }\n        if (intersections.size() == 2)\n        {\n            final Iterator<Location> iterator = intersections.iterator();\n            final Location location1 = iterator.next();\n            final Location location2 = iterator.next();\n            if (!location1.getLatitude().equals(location2.getLatitude())\n                    && !location1.getLongitude().equals(location2.getLongitude()))\n            {\n                return Rectangle.forLocations(location1, location2);\n            }\n            else\n            {\n                if (location1.getLatitude().equals(location2.getLatitude()))\n                {\n                    if (this.width().isLessThanOrEqualTo(other.width()))\n                    {\n                        for (final Location missing : this)\n                        {\n                            if (other.fullyGeometricallyEncloses(missing))\n                            {\n                                return Rectangle.forLocations(location1, location2, missing);\n                            }\n                        }\n                    }\n                    else\n                    {\n                        for (final Location missing : other)\n                        {\n                            if (this.fullyGeometricallyEncloses(missing))\n                            {\n                                return Rectangle.forLocations(location1, location2, missing);\n                            }\n                        }\n                    }\n                }\n                if (location1.getLongitude().equals(location2.getLongitude()))\n                {\n                    if (this.height().isLessThanOrEqualTo(other.height()))\n                    {\n                        for (final Location missing : this)\n                        {\n                            if (other.fullyGeometricallyEncloses(missing))\n                            {\n                                return Rectangle.forLocations(location1, location2, missing);\n                            }\n                        }\n                    }\n                    else\n                    {\n                        for (final Location missing : other)\n                        {\n                            if (this.fullyGeometricallyEncloses(missing))\n                            {\n                                return Rectangle.forLocations(location1, location2, missing);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        throw new CoreException(\"Cannot have more than 2 intersections.\");\n    }\n\n    /**\n     * @return The lower left corner {@link Location} of this {@link Rectangle}\n     */\n    public Location lowerLeft()\n    {\n        return this.lowerLeft;\n    }\n\n    /**\n     * @return The lower right corner {@link Location} of this {@link Rectangle}\n     */\n    public Location lowerRight()\n    {\n        return new Location(this.lowerLeft.getLatitude(), this.upperRight.getLongitude());\n    }\n\n    @Override\n    public boolean overlaps(final PolyLine other)\n    {\n        if (other instanceof Rectangle)\n        {\n            final Rectangle otherRectangle = (Rectangle) other;\n            return !(otherRectangle.lowerLeft.getLongitude()\n                    .isGreaterThan(this.upperRight.getLongitude())\n                    || otherRectangle.upperRight.getLongitude()\n                            .isLessThan(this.lowerLeft.getLongitude())\n                    || otherRectangle.upperRight.getLatitude()\n                            .isLessThan(this.lowerLeft.getLatitude())\n                    || otherRectangle.lowerLeft.getLatitude()\n                            .isGreaterThan(this.upperRight.getLatitude()));\n        }\n        else\n        {\n            return super.overlaps(other);\n        }\n    }\n\n    @Override\n    public Surface surface()\n    {\n        return Surface.forAngles(height(), width());\n    }\n\n    @Override\n    public String toCompactString()\n    {\n        return this.lowerLeft.toCompactString() + \":\" + this.upperRight.toCompactString();\n    }\n\n    /**\n     * @return The upper left corner {@link Location} of this {@link Rectangle}\n     */\n    public Location upperLeft()\n    {\n        return new Location(this.upperRight.getLatitude(), this.lowerLeft.getLongitude());\n    }\n\n    /**\n     * @return The upper right corner {@link Location} of this {@link Rectangle}\n     */\n    public Location upperRight()\n    {\n        return this.upperRight;\n    }\n\n    /**\n     * @return The width of this {@link Rectangle}\n     */\n    public Angle width()\n    {\n        long dm7Difference = this.upperRight.getLongitude().asDm7()\n                - this.lowerLeft.getLongitude().asDm7();\n        if (dm7Difference >= Angle.REVOLUTION_DM7)\n        {\n            dm7Difference = Angle.REVOLUTION_DM7 - 1;\n        }\n        return Angle.dm7(dm7Difference);\n    }\n\n    protected Rectangle2D asAwtRectangle()\n    {\n        final int xAxis = (int) this.upperLeft().getLongitude().asDm7();\n        final int yAxis = (int) this.upperLeft().getLatitude().asDm7();\n        final int width = (int) (this.upperRight().getLongitude().asDm7()\n                - this.upperLeft().getLongitude().asDm7());\n        final int height = (int) (this.upperLeft().getLatitude().asDm7()\n                - this.lowerLeft().getLatitude().asDm7());\n        return new java.awt.Rectangle(xAxis, yAxis, width, height);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Segment.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link PolyLine} made of two {@link Location}s\n *\n * @author matthieun\n */\npublic class Segment extends PolyLine\n{\n    private static final Logger logger = LoggerFactory.getLogger(Segment.class);\n    private static final long serialVersionUID = -5796676985841139897L;\n\n    /**\n     * Convenience method to gather all {@link Location}s for a list of segments.\n     *\n     * @param segments\n     *            target segments\n     * @return a list of {@link Location}s for the given segments\n     */\n    public static List<Location> asList(final Iterable<Segment> segments)\n    {\n        final List<Location> result = new ArrayList<>();\n        Iterables.stream(segments).forEach(segment ->\n        {\n            if (result.isEmpty() || !result.get(result.size() - 1).equals(segment.start()))\n            {\n                result.add(segment.start());\n            }\n            result.add(segment.end());\n        });\n        return result;\n    }\n\n    /**\n     * Convenience method to speed up the construction of the parent {@link PolyLine}.\n     */\n    private static List<Location> asList(final Location start, final Location end)\n    {\n        // avoid the initial grow calls for ArrayList (there would be at least one grow call,\n        // possibly two here)\n        // The grow calls are ~50% of the cost for this method.\n        // This should decrease the cost for segment creation by ~1/3.\n        final List<Location> result = new ArrayList<>(2);\n        result.add(start);\n        result.add(end);\n        return result;\n    }\n\n    /**\n     * Ensures that numerator/denominator is within the range [0,1] without doing the division\n     *\n     * @param denominator\n     *            The denominator of the fraction\n     * @param numerator\n     *            The numerator of the fraction\n     * @return {@code true} if the fraction is in the range [0,1]\n     */\n    private static boolean rangeCheck(final double denominator, final double numerator)\n    {\n        return denominator > 0 && (numerator < 0 || numerator > denominator)\n                || denominator < 0 && (numerator > 0 || numerator < denominator);\n    }\n\n    /**\n     * Ensures that numerator/denominator is within the range [0,1] without doing the division\n     *\n     * @param denominator\n     *            The denominator of the fraction\n     * @param numerator\n     *            The numerator of the fraction\n     * @return {@code true} if the fraction is in the range [0,1]\n     */\n    private static boolean rangeCheck(final long denominator, final long numerator)\n    {\n        return denominator > 0 && (numerator < 0 || numerator > denominator)\n                || denominator < 0 && (numerator > 0 || numerator < denominator);\n    }\n\n    public Segment(final Location start, final Location end)\n    {\n        super(asList(start, end));\n    }\n\n    public Location end()\n    {\n        return this.last();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof Segment)\n        {\n            final Segment that = (Segment) other;\n            return this.start().equals(that.start()) && this.end().equals(that.end());\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (this.end() == null ? 0 : this.end().hashCode());\n        result = prime * result + (this.start() == null ? 0 : this.start().hashCode());\n        return result;\n    }\n\n    /**\n     * @return The {@link Segment}'s {@link Heading}. In case the segment is the same start and end\n     *         locations, then the result is empty.\n     */\n    public Optional<Heading> heading()\n    {\n        if (this.isPoint())\n        {\n            logger.warn(\n                    \"Cannot compute a segment's heading when the segment is a point with same start and end {}\",\n                    this.start());\n            return Optional.empty();\n        }\n        return Optional.of(this.start().headingTo(this.end()));\n    }\n\n    /**\n     * Intersection of two segments\n     *\n     * @param that\n     *            The other segment to intersect\n     * @return The intersection point if any, null otherwise\n     * @see \"http://stackoverflow.com/a/1968345/1558687\"\n     */\n    public Location intersection(final Segment that)\n    {\n        final double p0X = this.start().getLongitude().asDegrees();\n        final double p0Y = this.start().getLatitude().asDegrees();\n        final double p1X = this.end().getLongitude().asDegrees();\n        final double p1Y = this.end().getLatitude().asDegrees();\n        final double p2X = that.start().getLongitude().asDegrees();\n        final double p2Y = that.start().getLatitude().asDegrees();\n        final double p3X = that.end().getLongitude().asDegrees();\n        final double p3Y = that.end().getLatitude().asDegrees();\n\n        final double s1X;\n        final double s1Y;\n        final double s2X;\n        final double s2Y;\n        s1X = p1X - p0X;\n        s1Y = p1Y - p0Y;\n        s2X = p3X - p2X;\n        s2Y = p3Y - p2Y;\n\n        final double sValue;\n        final double tValue;\n        sValue = (-s1Y * (p0X - p2X) + s1X * (p0Y - p2Y)) / (-s2X * s1Y + s1X * s2Y);\n        tValue = (s2X * (p0Y - p2Y) - s2Y * (p0X - p2X)) / (-s2X * s1Y + s1X * s2Y);\n\n        if (sValue >= 0 && sValue <= 1 && tValue >= 0 && tValue <= 1)\n        {\n            // Collision detected\n            return new Location(Latitude.degrees(p0Y + tValue * s1Y),\n                    Longitude.degrees(p0X + tValue * s1X));\n        }\n        // No collision\n        return null;\n    }\n\n    /**\n     * A fast method to test if two segments intersect\n     *\n     * @param that\n     *            The other {@link Segment} to test with\n     * @return True if this segment intersects that segment\n     * @see \"http://www.java-gaming.org/index.php?topic=22590.0\"\n     */\n    public boolean intersects(final Segment that)\n    {\n        final long xAxis1 = this.start().getLongitude().asDm7();\n        final long yAxis1 = this.start().getLatitude().asDm7();\n        final long xAxis2 = this.end().getLongitude().asDm7();\n        final long yAxis2 = this.end().getLatitude().asDm7();\n        final long xAxis3 = that.start().getLongitude().asDm7();\n        final long yAxis3 = that.start().getLatitude().asDm7();\n        final long xAxis4 = that.end().getLongitude().asDm7();\n        final long yAxis4 = that.end().getLatitude().asDm7();\n\n        // Return false if either of the lines have zero length\n        if (xAxis1 == xAxis2 && yAxis1 == yAxis2 || xAxis3 == xAxis4 && yAxis3 == yAxis4)\n        {\n            return false;\n        }\n        // Fastest method, based on Franklin Antonio's \"Faster Line Segment Intersection\" topic\n        // \"in Graphics Gems III\" book (http://www.graphicsgems.org/)\n        final long axAxis = xAxis2 - xAxis1;\n        final long ayAxis = yAxis2 - yAxis1;\n        final long bxAxis = xAxis3 - xAxis4;\n        final long byAxis = yAxis3 - yAxis4;\n        final long cxAxis = xAxis1 - xAxis3;\n        final long cyAxis = yAxis1 - yAxis3;\n\n        try\n        {\n            final long alphaNumerator = Math.subtractExact(byAxis * cxAxis, bxAxis * cyAxis);\n            final long commonDenominator = Math.subtractExact(ayAxis * bxAxis, axAxis * byAxis);\n            // ensures that alpha is within the range [0,1] without doing the division\n            if (rangeCheck(commonDenominator, alphaNumerator))\n            {\n                return false;\n\n            }\n            final long betaNumerator = Math.subtractExact(axAxis * cyAxis, ayAxis * cxAxis);\n            // ensures that beta is within the range [0,1] without doing the division\n            if (rangeCheck(commonDenominator, betaNumerator))\n            {\n                return false;\n            }\n\n            if (commonDenominator == 0)\n            {\n                // This code wasn't in Franklin Antonio's method. It was added by Keith Woodward.\n                // The\n                // lines are parallel. Check if they're collinear.\n                // see \"http://mathworld.wolfram.com/Collinear.html\"\n                final long collinearityTestForP3 = xAxis1 * (yAxis2 - yAxis3)\n                        + xAxis2 * (yAxis3 - yAxis1) + xAxis3 * (yAxis1 - yAxis2);\n                // If p3 is collinear with p1 and p2 then p4 will also be collinear, since p1-p2 is\n                // parallel with p3-p4\n                if (collinearityTestForP3 == 0)\n                {\n                    // The lines are collinear. Now check if they overlap.\n                    if ((xAxis1 >= xAxis3 && xAxis1 <= xAxis4\n                            || xAxis1 <= xAxis3 && xAxis1 >= xAxis4\n                            || xAxis2 >= xAxis3 && xAxis2 <= xAxis4\n                            || xAxis2 <= xAxis3 && xAxis2 >= xAxis4\n                            || xAxis3 >= xAxis1 && xAxis3 <= xAxis2\n                            || xAxis3 <= xAxis1 && xAxis3 >= xAxis2)\n                            && (yAxis1 >= yAxis3 && yAxis1 <= yAxis4\n                                    || yAxis1 <= yAxis3 && yAxis1 >= yAxis4\n                                    || yAxis2 >= yAxis3 && yAxis2 <= yAxis4\n                                    || yAxis2 <= yAxis3 && yAxis2 >= yAxis4\n                                    || yAxis3 >= yAxis1 && yAxis3 <= yAxis2\n                                    || yAxis3 <= yAxis1 && yAxis3 >= yAxis2))\n                    {\n                        return true;\n                    }\n                }\n                return false;\n            }\n            return true;\n        }\n        catch (final ArithmeticException overflow)\n        {\n            return this.intersectsApproximate(that);\n        }\n    }\n\n    /**\n     * @return True if this segment is exactly east west (the two latitudes are the same)\n     */\n    public boolean isEastWest()\n    {\n        return start().hasSameLatitudeAs(end());\n    }\n\n    /**\n     * @return True if this segment is exactly north south (the two longitudes are the same)\n     */\n    public boolean isNorthSouth()\n    {\n        return start().hasSameLongitudeAs(end());\n    }\n\n    @Override\n    public boolean isPoint()\n    {\n        return start().equals(end());\n    }\n\n    @Override\n    public Distance length()\n    {\n        return this.start().distanceTo(this.end());\n    }\n\n    @Override\n    public Location middle()\n    {\n        return new Location(\n                Latitude.degrees((this.start().getLatitude().asDegrees()\n                        + this.end().getLatitude().asDegrees()) / 2.0),\n                Longitude.degrees((this.start().getLongitude().asDegrees()\n                        + this.end().getLongitude().asDegrees()) / 2.0));\n    }\n\n    @Override\n    public Location offsetFromStart(final Ratio ratio)\n    {\n        final Optional<Heading> heading = heading();\n        if (heading.isPresent())\n        {\n            return this.start().shiftAlongGreatCircle(heading.get(), length().scaleBy(ratio));\n        }\n        return this.start();\n    }\n\n    /**\n     * @return The same segment but pointing north if it is not already, by reversing it if it\n     *         points south.\n     */\n    public Segment pointingNorth()\n    {\n        if (this.isEastWest())\n        {\n            return this;\n        }\n        if (start().getLatitude().isLessThan(end().getLatitude()))\n        {\n            return this;\n        }\n        return new Segment(end(), start());\n    }\n\n    @Override\n    public Segment reversed()\n    {\n        return new Segment(end(), start());\n    }\n\n    public Location start()\n    {\n        return this.first();\n    }\n\n    /**\n     * The Dot Product of two segments (seen as 2D space vectors)\n     *\n     * @param that\n     *            The other {@link Segment}\n     * @return The Dot Product of the two segments\n     */\n    protected double dotProduct(final Segment that)\n    {\n        final double thisLatitudeSpan = this.latitudeSpan();\n        final double thatLatitudeSpan = that.latitudeSpan();\n        final double thisLongitudeSpan = this.longitudeSpan();\n        final double thatLongitudeSpan = that.longitudeSpan();\n        return thisLatitudeSpan * thatLatitudeSpan + thisLongitudeSpan * thatLongitudeSpan;\n    }\n\n    protected double dotProductLength()\n    {\n        return Math.sqrt(dotProduct(this));\n    }\n\n    protected long latitudeSpan()\n    {\n        return this.end().getLatitude().asDm7() - this.start().getLatitude().asDm7();\n    }\n\n    protected long longitudeSpan()\n    {\n        return this.end().getLongitude().asDm7() - this.start().getLongitude().asDm7();\n    }\n\n    /**\n     * Implements the same function as intersects but with doubles to avoid overlflow issues. Should\n     * only happen for cross world intersection.\n     *\n     * @param that\n     * @return\n     */\n    private boolean intersectsApproximate(final Segment that)\n    {\n        final double xAxis1 = this.start().getLongitude().asDegrees();\n        final double yAxis1 = this.start().getLatitude().asDegrees();\n        final double xAxis2 = this.end().getLongitude().asDegrees();\n        final double yAxis2 = this.end().getLatitude().asDegrees();\n        final double xAxis3 = that.start().getLongitude().asDegrees();\n        final double yAxis3 = that.start().getLatitude().asDegrees();\n        final double xAxis4 = that.end().getLongitude().asDegrees();\n        final double yAxis4 = that.end().getLatitude().asDegrees();\n\n        // Return false if either of the lines have zero length\n        if (xAxis1 == xAxis2 && yAxis1 == yAxis2 || xAxis3 == xAxis4 && yAxis3 == yAxis4)\n        {\n            return false;\n        }\n        // Fastest method, based on Franklin Antonio's \"Faster Line Segment Intersection\" topic\n        // \"in Graphics Gems III\" book (http://www.graphicsgems.org/)\n        final double axAxis = xAxis2 - xAxis1;\n        final double ayAxis = yAxis2 - yAxis1;\n        final double bxAxis = xAxis3 - xAxis4;\n        final double byAxis = yAxis3 - yAxis4;\n        final double cxAxis = xAxis1 - xAxis3;\n        final double cyAxis = yAxis1 - yAxis3;\n\n        final double alphaNumerator = byAxis * cxAxis - bxAxis * cyAxis;\n        final double commonDenominator = ayAxis * bxAxis - axAxis * byAxis;\n        // ensures that alpha is within the range [0,1] without doing the division\n        if (rangeCheck(commonDenominator, alphaNumerator))\n        {\n            return false;\n\n        }\n        final double betaNumerator = axAxis * cyAxis - ayAxis * cxAxis;\n        // ensures that beta is within the range [0,1] without doing the division\n        if (rangeCheck(commonDenominator, betaNumerator))\n        {\n            return false;\n        }\n\n        if (commonDenominator == 0)\n        {\n            // This code wasn't in Franklin Antonio's method. It was added by Keith Woodward. The\n            // lines are parallel. Check if they're collinear.\n            // see \"http://mathworld.wolfram.com/Collinear.html\"\n            final double collinearityTestForP3 = xAxis1 * (yAxis2 - yAxis3)\n                    + xAxis2 * (yAxis3 - yAxis1) + xAxis3 * (yAxis1 - yAxis2);\n            // If p3 is collinear with p1 and p2 then p4 will also be collinear, since p1-p2 is\n            // parallel with p3-p4\n            if (collinearityTestForP3 == 0)\n            {\n                // The lines are collinear. Now check if they overlap.\n                if ((xAxis1 >= xAxis3 && xAxis1 <= xAxis4 || xAxis1 <= xAxis3 && xAxis1 >= xAxis4\n                        || xAxis2 >= xAxis3 && xAxis2 <= xAxis4\n                        || xAxis2 <= xAxis3 && xAxis2 >= xAxis4\n                        || xAxis3 >= xAxis1 && xAxis3 <= xAxis2\n                        || xAxis3 <= xAxis1 && xAxis3 >= xAxis2)\n                        && (yAxis1 >= yAxis3 && yAxis1 <= yAxis4\n                                || yAxis1 <= yAxis3 && yAxis1 >= yAxis4\n                                || yAxis2 >= yAxis3 && yAxis2 <= yAxis4\n                                || yAxis2 <= yAxis3 && yAxis2 >= yAxis4\n                                || yAxis3 >= yAxis1 && yAxis3 <= yAxis2\n                                || yAxis3 <= yAxis1 && yAxis3 >= yAxis2))\n                {\n                    return true;\n                }\n            }\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/Snapper.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * Snap a {@link Location} to a {@link PolyLine}.\n *\n * @author matthieun\n * @author bbreithaupt\n */\npublic class Snapper\n{\n    /**\n     * A snapped location on a shape.\n     *\n     * @author matthieun\n     */\n    public static class SnappedLocation extends Location implements Comparable<SnappedLocation>\n    {\n        private static final long serialVersionUID = -3283158797347353372L;\n\n        private final Location origin;\n        private final PolyLine target;\n\n        public SnappedLocation(final Location origin, final Location snapped, final PolyLine target)\n        {\n            super(snapped.asConcatenation());\n            this.origin = origin;\n            this.target = target;\n        }\n\n        @Override\n        public int compareTo(final SnappedLocation other)\n        {\n            if (getDistance().isLessThan(other.getDistance()))\n            {\n                return -1;\n            }\n            else if (getDistance().equals(other.getDistance()))\n            {\n                return 0;\n            }\n            else\n            {\n                return 1;\n            }\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (other instanceof SnappedLocation)\n            {\n                return this.origin.equals(((SnappedLocation) other).getOrigin())\n                        && this.target.equals(((SnappedLocation) other).getTarget());\n            }\n            if (other instanceof Location)\n            {\n                return super.equals(other);\n            }\n            return false;\n        }\n\n        /**\n         * @return The distance between the origin and the snapped {@link Location}\n         */\n        public Distance getDistance()\n        {\n            return this.origin.distanceTo(this);\n        }\n\n        public Location getOrigin()\n        {\n            return this.origin;\n        }\n\n        public PolyLine getTarget()\n        {\n            return this.target;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return Objects.hash(this.origin, this.target);\n        }\n    }\n\n    /**\n     * Snap a point on a {@link PolyLine}\n     *\n     * @param origin\n     *            The point to snap\n     * @param shape\n     *            The {@link PolyLine} to snap to\n     * @return The resulting {@link SnappedLocation}\n     */\n    public SnappedLocation snap(final Location origin, final Iterable<? extends Location> shape)\n    {\n        if (shape instanceof Segment)\n        {\n            final Segment target = (Segment) shape;\n            return snapSegment(origin, target);\n        }\n        else if (Iterables.size(shape) > 1)\n        {\n            final PolyLine target;\n            if (shape instanceof PolyLine)\n            {\n                target = (PolyLine) shape;\n            }\n            else if (shape instanceof Polygon)\n            {\n                target = (Polygon) shape;\n            }\n            else\n            {\n                target = new PolyLine(shape);\n            }\n            SnappedLocation best = null;\n            for (final Segment segment : target.segments())\n            {\n                final SnappedLocation candidate = snap(origin, segment);\n                if (best == null || candidate.getDistance().isLessThan(best.getDistance()))\n                {\n                    best = candidate;\n                }\n            }\n            // Return a SnappedLocation with the full shape\n            return new SnappedLocation(origin, best, target);\n        }\n        else if (Iterables.size(shape) == 1)\n        {\n            // We have a single location in the Iterable\n            final Location target = shape.iterator().next();\n            return new SnappedLocation(origin, target, new PolyLine(target));\n        }\n        return null;\n    }\n\n    public SnappedLocation snap(final Location origin, final MultiPolygon shape)\n    {\n        SnappedLocation best = null;\n        for (final Polygon member : new MultiIterable<>(shape.outers(), shape.inners()))\n        {\n            final SnappedLocation candidate = snap(origin, member);\n            if (best == null || candidate.getDistance().isLessThan(best.getDistance()))\n            {\n                best = candidate;\n            }\n        }\n        return best;\n    }\n\n    private SnappedLocation snapSegment(final Location origin, final Segment shape)\n    {\n        // Use the dot product to determine if the snapped point is within the segment, or at\n        // the edge points\n        final Segment variable = new Segment(shape.start(), origin);\n        final double dotProduct = shape.dotProduct(variable);\n        if (dotProduct <= 0)\n        {\n            return new SnappedLocation(origin, shape.start(), shape);\n        }\n        // Here, NOSONAR to avoid \"Collections should not be passed as arguments to their own\n        // methods (squid:S2114)\"\n        // It is triggered because Segment is also a collection.\n        if (dotProduct >= shape.dotProduct(shape)) // NOSONAR\n        {\n            return new SnappedLocation(origin, shape.end(), shape);\n        }\n        // Find the point in the middle.\n        // Inspired from http://www.sunshine2k.de/coding/java/PointOnLine/PointOnLine.html#step5\n        // The angle between the target and variable segment is alpha\n        final double cosAlpha = dotProduct\n                / (shape.dotProductLength() * variable.dotProductLength());\n        // Cos Alpha is also defined as (offset distance on target) / (variable's length)\n        final double offsetDistance = cosAlpha * variable.dotProductLength();\n        final double latitudeAsDm7 = shape.start().getLatitude().asDm7()\n                + offsetDistance / shape.dotProductLength() * shape.latitudeSpan();\n        final double longitudeAsDm7 = shape.start().getLongitude().asDm7()\n                + offsetDistance / shape.dotProductLength() * shape.longitudeSpan();\n        final Location snapped = new Location(Latitude.dm7(Math.round(latitudeAsDm7)),\n                Longitude.dm7(Math.round(longitudeAsDm7)));\n        return new SnappedLocation(origin, snapped, shape);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/StringCompressedPolyLine.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.io.Serializable;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Encode a {@link PolyLine} using an algorithm derived from the MapQuest variant of the Google\n * Polyline Encoding Format. The encoding scheme falls back on WKB in edge cases when the algorithm\n * fails (such as when two consecutive points in the polyline have a longitude difference of greater\n * than 180 degrees).\n *\n * @see <a href=\"https://developer.mapquest.com/documentation/common/encode-decode\">The MapQuest\n *      algorithm</a>\n * @see <a href=\n *      \"https://developers.google.com/maps/documentation/utilities/polylinealgorithm?csw=1\">Google\n *      Polyline Encoding Format</a>\n * @author matthieun\n */\npublic class StringCompressedPolyLine implements Serializable\n{\n    /**\n     * @author matthieun\n     */\n    public static class PolyLineCompressionException extends CoreException\n    {\n        private static final long serialVersionUID = -3974024747280370420L;\n\n        public PolyLineCompressionException(final String message, final Object... items)\n        {\n            super(message, items);\n        }\n    }\n\n    private static final long serialVersionUID = 5315700936842774861L;\n\n    // dm7\n    private static final int PRECISION = 7;\n    private static final Charset CHARSET = StandardCharsets.UTF_8;\n    private static final int ENCODING_OFFSET_MINUS_ONE = 63;\n    private static final int FIVE_BIT_MASK = 0x1f;\n    private static final int SIXTH_BIT_MASK = 0x20;\n    private static final int BIT_SHIFT = 5;\n    // To allow for degree of magnitude 7 precision, two longitudes should not be too far apart.\n    // Using 180 degrees as a limit.\n    private static final long MAXIMUM_DELTA_LONGITUDE_IN_DEGREES = 180;\n    private static final long MAXIMUM_DELTA_LONGITUDE = (long) (MAXIMUM_DELTA_LONGITUDE_IN_DEGREES\n            * Math.pow(10, PRECISION));\n\n    /*\n     * If the first byte of the encoding array is this sentinel value, then the following encoding\n     * is WKB and not string-compressed. We use '0' as the sentinel value since the string\n     * compression algorithm will always use printable ASCII characters. There will never be a 0\n     * byte in a valid string-compressed polyline.\n     */\n    private static final byte WKB_SENTINEL = 0;\n\n    private static final Logger logger = LoggerFactory.getLogger(StringCompressedPolyLine.class);\n\n    private byte[] encoding;\n\n    public StringCompressedPolyLine(final byte[] encoding)\n    {\n        this.encoding = encoding;\n    }\n\n    public StringCompressedPolyLine(final PolyLine polyLine)\n    {\n        try\n        {\n            this.encoding = compress(polyLine, PRECISION).getBytes(CHARSET);\n        }\n        catch (final PolyLineCompressionException exception)\n        {\n            logger.warn(\"Unable to use string compression\", exception);\n            this.encoding = getWkbFallback(polyLine);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Could not compress polyline.\", exception);\n        }\n    }\n\n    public PolyLine asPolyLine()\n    {\n        if (this.encoding[0] == WKB_SENTINEL)\n        {\n            final byte[] strippedEncoding = new byte[this.encoding.length - 1];\n            System.arraycopy(this.encoding, 1, strippedEncoding, 0, strippedEncoding.length);\n            return PolyLine.wkb(strippedEncoding);\n        }\n        else\n        {\n            String encodedString = null;\n            try\n            {\n                encodedString = new String(this.encoding, CHARSET);\n                return asPolyLine(encodedString, PRECISION);\n            }\n            catch (final Exception exception)\n            {\n                throw new CoreException(\n                        \"Could not decompress polyline:\\nEncoding: \\'{}\\'\\nString: \\'\\'.\",\n                        this.encoding, encodedString, exception);\n            }\n        }\n    }\n\n    public byte[] getEncoding()\n    {\n        return this.encoding;\n    }\n\n    @Override\n    public String toString()\n    {\n        if (this.encoding[0] == WKB_SENTINEL)\n        {\n            final byte[] strippedEncoding = new byte[this.encoding.length - 1];\n            System.arraycopy(this.encoding, 1, strippedEncoding, 0, strippedEncoding.length);\n            return PolyLine.wkb(strippedEncoding).toWkt();\n        }\n        else\n        {\n            try\n            {\n                return new String(this.encoding, CHARSET);\n            }\n            catch (final Exception e)\n            {\n                throw new CoreException(\"Could not stringify byte array.\", e);\n            }\n        }\n    }\n\n    private PolyLine asPolyLine(final String encoded, final int precision)\n    {\n        final double precision2 = Math.pow(10, -precision);\n        final int length = encoded.length();\n        int index = 0;\n        int latitude = 0;\n        int longitude = 0;\n        final List<Location> array = new ArrayList<>();\n        while (index < length)\n        {\n            int byteEncoded;\n            int shift = 0;\n            int result = 0;\n            do\n            {\n                byteEncoded = Character.codePointAt(encoded, index++) - ENCODING_OFFSET_MINUS_ONE;\n                result |= (byteEncoded & FIVE_BIT_MASK) << shift;\n                shift += BIT_SHIFT;\n            }\n            while (byteEncoded >= SIXTH_BIT_MASK);\n            final int deltaLatitude = (result & 1) > 0 ? ~(result >>> 1) : result >>> 1;\n            latitude += deltaLatitude;\n            shift = 0;\n            result = 0;\n            do\n            {\n                byteEncoded = Character.codePointAt(encoded, index++) - ENCODING_OFFSET_MINUS_ONE;\n                result |= (byteEncoded & FIVE_BIT_MASK) << shift;\n                shift += BIT_SHIFT;\n            }\n            while (byteEncoded >= SIXTH_BIT_MASK);\n            final int deltalongitude = (result & 1) > 0 ? ~(result >>> 1) : result >>> 1;\n            longitude += deltalongitude;\n            array.add(new Location(Latitude.degrees(latitude * precision2),\n                    Longitude.degrees(longitude * precision2)));\n        }\n        return new PolyLine(array);\n    }\n\n    private String compress(final PolyLine points, final int precision0)\n    {\n        long oldLatitude = 0;\n        long oldLongitude = 0;\n        final StringBuilder encoded = new StringBuilder();\n        final double precision = Math.pow(10, precision0);\n        Location last = Location.CENTER;\n        for (final Location location : points)\n        {\n            // Round to N decimal places\n            final long latitude = Math.round(location.getLatitude().asDegrees() * precision);\n            final long longitude = Math.round(location.getLongitude().asDegrees() * precision);\n\n            // Encode the differences between the points\n            encoded.append(encodeNumber(latitude - oldLatitude));\n            final long deltaLongitude = longitude - oldLongitude;\n            if (Math.abs(deltaLongitude) > MAXIMUM_DELTA_LONGITUDE)\n            {\n                throw new PolyLineCompressionException(\n                        \"Unable to compress the polyLine, two consecutive points ({} and {}) are too far apart in longitude: {} degrees.\",\n                        last, location, deltaLongitude / precision);\n            }\n            encoded.append(encodeNumber(deltaLongitude));\n\n            oldLatitude = latitude;\n            oldLongitude = longitude;\n            last = location;\n        }\n        return encoded.toString();\n    }\n\n    private String encodeNumber(final long number0)\n    {\n        long number = number0 << 1;\n        if (number < 0)\n        {\n            number = ~number;\n        }\n        final StringBuilder encoded = new StringBuilder();\n        while (number >= SIXTH_BIT_MASK)\n        {\n            encoded.append(String.valueOf(Character.toChars(\n                    (SIXTH_BIT_MASK | (int) number & FIVE_BIT_MASK) + ENCODING_OFFSET_MINUS_ONE)));\n            number >>>= BIT_SHIFT;\n        }\n        encoded.append(Character.toChars((int) number + ENCODING_OFFSET_MINUS_ONE));\n        return encoded.toString();\n    }\n\n    private byte[] getWkbFallback(final PolyLine polyLine)\n    {\n        final byte[] wkbEncoding = polyLine.toWkb();\n        final byte[] finalEncoding = new byte[1 + wkbEncoding.length];\n        finalEncoding[0] = WKB_SENTINEL;\n        System.arraycopy(wkbEncoding, 0, finalEncoding, 1, wkbEncoding.length);\n        return finalEncoding;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/StringCompressedPolygon.java",
    "content": "package org.openstreetmap.atlas.geography;\n\n/**\n * Compressed {@link Polygon}\n *\n * @author matthieun\n */\npublic class StringCompressedPolygon extends StringCompressedPolyLine\n{\n    private static final long serialVersionUID = 7681617657249431319L;\n\n    public StringCompressedPolygon(final byte[] encoding)\n    {\n        super(encoding);\n    }\n\n    public StringCompressedPolygon(final Polygon polygon)\n    {\n        super(polygon);\n    }\n\n    public Polygon asPolygon()\n    {\n        return new Polygon(asPolyLine());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/WkbPrintable.java",
    "content": "package org.openstreetmap.atlas.geography;\n\n/**\n * @author matthieun\n */\npublic interface WkbPrintable\n{\n    byte[] toWkb();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/WktPrintable.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * @author matthieun\n */\npublic interface WktPrintable\n{\n    static String toWktCollection(final Iterable<? extends WktPrintable> input)\n    {\n        final StringList wktList = new StringList();\n        input.forEach(wktPrintable -> wktList.add(wktPrintable.toWkt()));\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"GEOMETRYCOLLECTION (\");\n        builder.append(wktList.join(\", \"));\n        builder.append(\")\");\n        return builder.toString();\n    }\n\n    String toWkt();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/AbstractAtlas.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.util.function.BiFunction;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.index.PackedSpatialIndex;\nimport org.openstreetmap.atlas.geography.index.RTree;\nimport org.openstreetmap.atlas.geography.index.SpatialIndex;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Abstract implementation of {@link Atlas} that covers common methods.\n *\n * @author matthieun\n * @author tony\n * @author mgostintsev\n */\npublic abstract class AbstractAtlas extends BareAtlas\n{\n    protected static final long DEFAULT_NUMBER_OF_ITEMS = 1024;\n    protected static final int HASH_MODULO_RATIO = 10;\n\n    private static final long serialVersionUID = -1408393006815178776L;\n    private static final Logger logger = LoggerFactory.getLogger(AbstractAtlas.class);\n\n    // Spatial index lock objects for thread protection. Even though it looks not necessary, those\n    // locks are static to avoid issues when deserializing Atlas files. If non static, they might\n    // end up being null in some cases right after deserialization.\n    private static final Object NODE_LOCK = new Object();\n    private static final Object EDGE_LOCK = new Object();\n    private static final Object AREA_LOCK = new Object();\n    private static final Object LINE_LOCK = new Object();\n    private static final Object POINT_LOCK = new Object();\n    private static final Object RELATION_LOCK = new Object();\n\n    // Spatial indices\n    // Transient: Those are not serialized, and re-generated on the fly\n    // Volatile: This is to allow double checked locking to be safe in the\n    // this.buildXXXXXSpatialIndexIfNecessary() methods.\n    // http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html\n    // See: \"Fixing Double-Checked Locking using Volatile\"\n    private transient volatile SpatialIndex<Node> nodeSpatialIndex;\n    private transient volatile SpatialIndex<Edge> edgeSpatialIndex;\n    private transient volatile SpatialIndex<Area> areaSpatialIndex;\n    private transient volatile SpatialIndex<Line> lineSpatialIndex;\n    private transient volatile SpatialIndex<Point> pointSpatialIndex;\n    private transient volatile SpatialIndex<Relation> relationSpatialIndex;\n\n    /**\n     * Create an {@link Atlas} from an OSM protobuf and save it to a resource. Skip slicing.\n     *\n     * @param osmPbf\n     *            The OSM protobuf\n     * @param atlasResource\n     *            The {@link WritableResource} to save the {@link Atlas} to\n     * @return The created {@link Atlas}\n     */\n    public static Atlas createAndSaveOsmPbf(final Resource osmPbf,\n            final WritableResource atlasResource)\n    {\n        Atlas atlas = new RawAtlasGenerator(osmPbf).build();\n        atlas = new AtlasSectionProcessor(atlas, AtlasLoadingOption.createOptionWithNoSlicing())\n                .run();\n        atlas.save(atlasResource);\n        return atlas;\n    }\n\n    /**\n     * Create an {@link Atlas} from an OSM protobuf that has already been sliced and save it to a\n     * resource\n     *\n     * @param osmPbf\n     *            The OSM protobuf\n     * @param atlasResource\n     *            The {@link WritableResource} to save the {@link Atlas} to\n     * @param boundaryMap\n     *            The {@link CountryBoundaryMap} to use for country-slicing\n     * @return The created {@link Atlas}\n     */\n    public static Atlas createAndSaveOsmPbfWithSlicing(final Resource osmPbf,\n            final WritableResource atlasResource, final CountryBoundaryMap boundaryMap)\n    {\n        Atlas atlas = new RawAtlasGenerator(osmPbf).build();\n        final AtlasLoadingOption loadingOption = AtlasLoadingOption\n                .createOptionWithAllEnabled(boundaryMap);\n        atlas = new RawAtlasSlicer(loadingOption, atlas).slice();\n        atlas = new AtlasSectionProcessor(atlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(boundaryMap)).run();\n        atlas.save(atlasResource);\n        return atlas;\n    }\n\n    /**\n     * Create from an OSM protobuf resource. Skip slicing.\n     *\n     * @param resource\n     *            The OSM protobuf resource\n     * @return The Atlas read from the pbf\n     */\n    public static Atlas forOsmPbf(final Resource resource)\n    {\n        Atlas atlas = new RawAtlasGenerator(resource).build();\n        atlas = new AtlasSectionProcessor(atlas, AtlasLoadingOption.createOptionWithNoSlicing())\n                .run();\n        return atlas;\n    }\n\n    @Override\n    public Iterable<Area> areasCovering(final Location location)\n    {\n        return Iterables.stream(this.getAreaSpatialIndex().get(location.bounds())).filter(area ->\n        {\n            final Polygon areaPolygon = area.asPolygon();\n            return areaPolygon.fullyGeometricallyEncloses(location);\n        });\n    }\n\n    @Override\n    public Iterable<Area> areasCovering(final Location location, final Predicate<Area> matcher)\n    {\n        return Iterables.filter(areasCovering(location), matcher);\n    }\n\n    @Override\n    public Iterable<Area> areasIntersecting(final GeometricSurface surface)\n    {\n        return Iterables.stream(this.getAreaSpatialIndex().get(surface.bounds())).filter(area ->\n        {\n            final Polygon areaPolygon = area.asPolygon();\n            return surface.overlaps(areaPolygon);\n        });\n    }\n\n    @Override\n    public Iterable<Area> areasIntersecting(final GeometricSurface surface,\n            final Predicate<Area> matcher)\n    {\n        return Iterables.filterTranslate(areasIntersecting(surface), item -> item, matcher);\n    }\n\n    @Override\n    public Iterable<Area> areasWithin(final GeometricSurface surface)\n    {\n        return Iterables.stream(this.getAreaSpatialIndex().get(surface.bounds())).filter(area ->\n        {\n            final Polygon areaPolygon = area.asPolygon();\n            return surface.fullyGeometricallyEncloses(areaPolygon);\n        });\n    }\n\n    @Override\n    public Iterable<Edge> edgesContaining(final Location location)\n    {\n        return Iterables.stream(this.getEdgeSpatialIndex().get(location.bounds())).filter(edge ->\n        {\n            final PolyLine polyline = edge.asPolyLine();\n            return polyline.contains(location);\n        });\n    }\n\n    @Override\n    public Iterable<Edge> edgesContaining(final Location location, final Predicate<Edge> matcher)\n    {\n        return Iterables.filter(edgesContaining(location), matcher);\n    }\n\n    @Override\n    public Iterable<Edge> edgesIntersecting(final GeometricSurface surface)\n    {\n        return Iterables.stream(this.getEdgeSpatialIndex().get(surface.bounds())).filter(edge ->\n        {\n            final PolyLine polyline = edge.asPolyLine();\n            return surface.overlaps(polyline);\n        });\n    }\n\n    @Override\n    public Iterable<Edge> edgesIntersecting(final GeometricSurface surface,\n            final Predicate<Edge> matcher)\n    {\n        return Iterables.filter(edgesIntersecting(surface), matcher);\n    }\n\n    @Override\n    public Iterable<Edge> edgesWithin(final GeometricSurface surface)\n    {\n        return Iterables.stream(this.getEdgeSpatialIndex().get(surface.bounds())).filter(edge ->\n        {\n            final PolyLine polyline = edge.asPolyLine();\n            return surface.fullyGeometricallyEncloses(polyline);\n        });\n    }\n\n    public SpatialIndex<Area> getAreaSpatialIndex()\n    {\n        buildAreaSpatialIndexIfNecessary();\n        return this.areaSpatialIndex;\n    }\n\n    public SpatialIndex<Edge> getEdgeSpatialIndex()\n    {\n        buildEdgeSpatialIndexIfNecessary();\n        return this.edgeSpatialIndex;\n    }\n\n    public SpatialIndex<Line> getLineSpatialIndex()\n    {\n        buildLineSpatialIndexIfNecessary();\n        return this.lineSpatialIndex;\n    }\n\n    public SpatialIndex<Node> getNodeSpatialIndex()\n    {\n        buildNodeSpatialIndexIfNecessary();\n        return this.nodeSpatialIndex;\n    }\n\n    public SpatialIndex<Point> getPointSpatialIndex()\n    {\n        buildPointSpatialIndexIfNecessary();\n        return this.pointSpatialIndex;\n    }\n\n    public SpatialIndex<Relation> getRelationSpatialIndex()\n    {\n        buildRelationSpatialIndexIfNecessary();\n        return this.relationSpatialIndex;\n    }\n\n    @Override\n    public Iterable<Line> linesContaining(final Location location)\n    {\n        return Iterables.stream(this.getLineSpatialIndex().get(location.bounds())).filter(line ->\n        {\n            final PolyLine polyline = line.asPolyLine();\n            return polyline.contains(location);\n        });\n    }\n\n    @Override\n    public Iterable<Line> linesContaining(final Location location, final Predicate<Line> matcher)\n    {\n        return Iterables.filter(linesContaining(location), matcher);\n    }\n\n    @Override\n    public Iterable<Line> linesIntersecting(final GeometricSurface surface)\n    {\n        return Iterables.stream(this.getLineSpatialIndex().get(surface.bounds())).filter(line ->\n        {\n            final PolyLine polyline = line.asPolyLine();\n            return surface.overlaps(polyline);\n        });\n    }\n\n    @Override\n    public Iterable<Line> linesIntersecting(final GeometricSurface surface,\n            final Predicate<Line> matcher)\n    {\n        return Iterables.filter(linesIntersecting(surface), matcher);\n    }\n\n    @Override\n    public Iterable<Line> linesWithin(final GeometricSurface surface)\n    {\n        return Iterables.stream(this.getLineSpatialIndex().get(surface.bounds())).filter(line ->\n        {\n            final PolyLine polyline = line.asPolyLine();\n            return surface.fullyGeometricallyEncloses(polyline);\n        });\n    }\n\n    @Override\n    public Iterable<Node> nodesAt(final Location location)\n    {\n        return this.getNodeSpatialIndex().get(location.bounds());\n    }\n\n    @Override\n    public Iterable<Node> nodesWithin(final GeometricSurface surface)\n    {\n        final Iterable<Node> nodes = this.getNodeSpatialIndex().get(surface.bounds());\n        if (surface instanceof Rectangle)\n        {\n            return nodes;\n        }\n        return Iterables.filter(nodes,\n                node -> surface.fullyGeometricallyEncloses(node.getLocation()));\n    }\n\n    @Override\n    public Iterable<Node> nodesWithin(final GeometricSurface surface, final Predicate<Node> matcher)\n    {\n        return Iterables.filter(nodesWithin(surface), matcher);\n    }\n\n    @Override\n    public Iterable<Point> pointsAt(final Location location)\n    {\n        return this.getPointSpatialIndex().get(location.bounds());\n    }\n\n    @Override\n    public Iterable<Point> pointsWithin(final GeometricSurface surface)\n    {\n        final Iterable<Point> points = this.getPointSpatialIndex().get(surface.bounds());\n        if (surface instanceof Rectangle)\n        {\n            return points;\n        }\n        return Iterables.filter(points,\n                point -> surface.fullyGeometricallyEncloses(point.getLocation()));\n    }\n\n    @Override\n    public Iterable<Point> pointsWithin(final GeometricSurface surface,\n            final Predicate<Point> matcher)\n    {\n        return Iterables.filterTranslate(pointsWithin(surface), item -> item, matcher);\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesIntersecting(final GeometricSurface surface)\n    {\n        final Iterable<Relation> relations = this.getRelationSpatialIndex().get(surface.bounds());\n        return Iterables.filter(relations, relation -> relation.intersects(surface));\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesIntersecting(final GeometricSurface surface,\n            final Predicate<Relation> matcher)\n    {\n        return Iterables.filter(relationsWithEntitiesIntersecting(surface), matcher);\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesWithin(final GeometricSurface surface)\n    {\n        final Iterable<Relation> relations = this.getRelationSpatialIndex().get(surface.bounds());\n        return Iterables.filter(relations, relation -> relation.within(surface));\n    }\n\n    @Override\n    public void save(final WritableResource writableResource)\n    {\n        throw new CoreException(\n                \"{} does not support saving. Consider using {} instead. A {} can be had using Atlas.cloneToPackedAtlas()\",\n                this.getClass().getName(), PackedAtlas.class.getName(),\n                PackedAtlas.class.getName());\n    }\n\n    /**\n     * This method is useful for de-serialized Atlases. When an Atlas is serialized, the indices are\n     * not saved (transient). When de-serialized, they will be null, until this method is called.\n     */\n    protected void buildAreaSpatialIndexIfNecessary()\n    {\n        buildSpatialIndexIfNecessary(AREA_LOCK, ItemType.AREA, this::newAreaSpatialIndex,\n                () -> this.areaSpatialIndex,\n                newSpatialIndex -> this.areaSpatialIndex = newSpatialIndex);\n    }\n\n    /**\n     * This method is useful for de-serialized Atlases. When an Atlas is serialized, the indices are\n     * not saved (transient). When de-serialized, they will be null, until this method is called.\n     */\n    protected void buildEdgeSpatialIndexIfNecessary()\n    {\n        buildSpatialIndexIfNecessary(EDGE_LOCK, ItemType.EDGE, this::newEdgeSpatialIndex,\n                () -> this.edgeSpatialIndex,\n                newSpatialIndex -> this.edgeSpatialIndex = newSpatialIndex);\n    }\n\n    /**\n     * This method is useful for de-serialized Atlases. When an Atlas is serialized, the indices are\n     * not saved (transient). When de-serialized, they will be null, until this method is called.\n     */\n    protected void buildLineSpatialIndexIfNecessary()\n    {\n        buildSpatialIndexIfNecessary(LINE_LOCK, ItemType.LINE, this::newLineSpatialIndex,\n                () -> this.lineSpatialIndex,\n                newSpatialIndex -> this.lineSpatialIndex = newSpatialIndex);\n    }\n\n    /**\n     * This method is useful for de-serialized Atlases. When an Atlas is serialized, the indices are\n     * not saved (transient). When de-serialized, they will be null, until this method is called.\n     */\n    protected void buildNodeSpatialIndexIfNecessary()\n    {\n        buildSpatialIndexIfNecessary(NODE_LOCK, ItemType.NODE, this::newNodeSpatialIndex,\n                () -> this.nodeSpatialIndex,\n                newSpatialIndex -> this.nodeSpatialIndex = newSpatialIndex);\n    }\n\n    /**\n     * This method is useful for de-serialized Atlases. When an Atlas is serialized, the indices are\n     * not saved (transient). When de-serialized, they will be null, until this method is called.\n     */\n    protected void buildPointSpatialIndexIfNecessary()\n    {\n        buildSpatialIndexIfNecessary(POINT_LOCK, ItemType.POINT, this::newPointSpatialIndex,\n                () -> this.pointSpatialIndex,\n                newSpatialIndex -> this.pointSpatialIndex = newSpatialIndex);\n    }\n\n    /**\n     * This method is useful for de-serialized Atlases. When an Atlas is serialized, the indices are\n     * not saved (transient). When de-serialized, they will be null, until this method is called.\n     */\n    protected void buildRelationSpatialIndexIfNecessary()\n    {\n        buildSpatialIndexIfNecessary(RELATION_LOCK, ItemType.RELATION,\n                this::newRelationSpatialIndex, () -> this.relationSpatialIndex,\n                newSpatialIndex -> this.relationSpatialIndex = newSpatialIndex);\n    }\n\n    /**\n     * @return The spatial index as new (meaning empty). This has to be used only in the protected\n     *         constructors, to not conflict with the thread safe methods that re-build spatial\n     *         indices as needed.\n     */\n    protected SpatialIndex<Area> getAsNewAreaSpatialIndex()\n    {\n        if (this.areaSpatialIndex == null)\n        {\n            this.areaSpatialIndex = newAreaSpatialIndex();\n        }\n        return this.areaSpatialIndex;\n    }\n\n    /**\n     * @return The spatial index as new (meaning empty). This has to be used only in the protected\n     *         constructors, to not conflict with the thread safe methods that re-build spatial\n     *         indices as needed.\n     */\n    protected SpatialIndex<Edge> getAsNewEdgeSpatialIndex()\n    {\n        if (this.edgeSpatialIndex == null)\n        {\n            this.edgeSpatialIndex = newEdgeSpatialIndex();\n        }\n        return this.edgeSpatialIndex;\n    }\n\n    /**\n     * @return The spatial index as new (meaning empty). This has to be used only in the protected\n     *         constructors, to not conflict with the thread safe methods that re-build spatial\n     *         indices as needed.\n     */\n    protected SpatialIndex<Line> getAsNewLineSpatialIndex()\n    {\n        if (this.lineSpatialIndex == null)\n        {\n            this.lineSpatialIndex = newLineSpatialIndex();\n        }\n        return this.lineSpatialIndex;\n    }\n\n    /**\n     * @return The spatial index as new (meaning empty). This has to be used only in the protected\n     *         constructors, to not conflict with the thread safe methods that re-build spatial\n     *         indices as needed.\n     */\n    protected SpatialIndex<Node> getAsNewNodeSpatialIndex()\n    {\n        if (this.nodeSpatialIndex == null)\n        {\n            this.nodeSpatialIndex = newNodeSpatialIndex();\n        }\n        return this.nodeSpatialIndex;\n    }\n\n    /**\n     * @return The spatial index as new (meaning empty). This has to be used only in the protected\n     *         constructors, to not conflict with the thread safe methods that re-build spatial\n     *         indices as needed.\n     */\n    protected SpatialIndex<Point> getAsNewPointSpatialIndex()\n    {\n        if (this.pointSpatialIndex == null)\n        {\n            this.pointSpatialIndex = newPointSpatialIndex();\n        }\n        return this.pointSpatialIndex;\n    }\n\n    /**\n     * @return The spatial index as new (meaning empty). This has to be used only in the protected\n     *         constructors, to not conflict with the thread safe methods that re-build spatial\n     *         indices as needed.\n     */\n    protected SpatialIndex<Relation> getAsNewRelationSpatialIndex()\n    {\n        if (this.relationSpatialIndex == null)\n        {\n            this.relationSpatialIndex = newRelationSpatialIndex();\n        }\n        return this.relationSpatialIndex;\n    }\n\n    /**\n     * Implementation of double-checked locking with volatile global variable as suggested by sonar\n     *\n     * @see \"https://rules.sonarsource.com/java/tag/multi-threading/RSPEC-2168\"\n     * @param lock\n     *            An object to lock on. Needs to be a global static variable.\n     * @param type\n     *            The type of the Spatial Index object to create\n     * @param newIndexSupplier\n     *            A function that returns a new built-out index\n     * @param globalIndexSupplier\n     *            A function that returns the existing global index\n     * @param globalIndexConsumer\n     *            A function that resets the existing global index\n     */\n    @SuppressWarnings(\"unchecked\")\n    private <M extends AtlasEntity> void buildSpatialIndexIfNecessary(final Object lock,\n            final ItemType type, final Supplier<SpatialIndex<M>> newIndexSupplier,\n            final Supplier<SpatialIndex<M>> globalIndexSupplier,\n            final Consumer<SpatialIndex<M>> globalIndexConsumer)\n    {\n        SpatialIndex<M> localIndex = globalIndexSupplier.get();\n        if (localIndex == null)\n        {\n            // Here lock is a global static variable. Sonar cannot see it here, hence the trailing\n            // comment.\n            synchronized (lock) // NOSONAR\n            {\n                localIndex = globalIndexSupplier.get();\n                if (localIndex == null)\n                {\n                    logger.info(\"Re-Building {} Spatial Index...\", type);\n                    final SpatialIndex<M> temporaryIndex = newIndexSupplier.get();\n                    Iterables.stream(this.entities(type, type.getMemberClass()))\n                            .map(entity -> (M) entity).forEach(temporaryIndex::add);\n                    globalIndexConsumer.accept(temporaryIndex);\n                }\n            }\n        }\n    }\n\n    /**\n     * Create a new spatial index\n     *\n     * @return A newly created spatial index\n     */\n    private SpatialIndex<Area> newAreaSpatialIndex()\n    {\n        return newSpatialIndex((item, bounds) -> bounds.overlaps(item.asPolygon()), this::area);\n    }\n\n    /**\n     * Create a new spatial index\n     *\n     * @return A newly created spatial index\n     */\n    private SpatialIndex<Edge> newEdgeSpatialIndex()\n    {\n        return newSpatialIndex((item, bounds) -> bounds.overlaps(item.asPolyLine()), this::edge);\n    }\n\n    /**\n     * Create a new spatial index\n     *\n     * @return A newly created spatial index\n     */\n    private SpatialIndex<Line> newLineSpatialIndex()\n    {\n        return newSpatialIndex((item, bounds) -> bounds.overlaps(item.asPolyLine()), this::line);\n    }\n\n    /**\n     * Create a new spatial index\n     *\n     * @return A newly created spatial index\n     */\n    private SpatialIndex<Node> newNodeSpatialIndex()\n    {\n        return newSpatialIndex((item, bounds) -> bounds.fullyGeometricallyEncloses(item),\n                this::node);\n    }\n\n    /**\n     * Create a new spatial index\n     *\n     * @return A newly created spatial index\n     */\n    private SpatialIndex<Point> newPointSpatialIndex()\n    {\n        return newSpatialIndex((item, bounds) -> bounds.fullyGeometricallyEncloses(item),\n                this::point);\n    }\n\n    /**\n     * Create a new spatial index\n     *\n     * @return A newly created spatial index\n     */\n    private SpatialIndex<Relation> newRelationSpatialIndex()\n    {\n        return newSpatialIndex((item, bounds) -> item.intersects(bounds), this::relation);\n    }\n\n    /**\n     * @param memberValidForBounds\n     *            A function that decides if a member is included in bounds or not.\n     * @param memberFromIdentifier\n     *            A function that re-builds a member from its identifier.\n     * @return A {@link SpatialIndex} tailored to the specified type\n     */\n    private <M extends AtlasEntity> SpatialIndex<M> newSpatialIndex(\n            final BiFunction<M, Rectangle, Boolean> memberValidForBounds,\n            final Function<Long, M> memberFromIdentifier)\n    {\n        return new PackedSpatialIndex<M, Long>(new RTree<>())\n        {\n            private static final long serialVersionUID = 6569644967280192054L;\n\n            @Override\n            protected Long compress(final M item)\n            {\n                return item.getIdentifier();\n            }\n\n            @Override\n            protected boolean isValid(final M item, final Rectangle bounds)\n            {\n                return memberValidForBounds.apply(item, bounds);\n            }\n\n            @Override\n            protected M restore(final Long packed)\n            {\n                return memberFromIdentifier.apply(packed);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/Atlas.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.function.BiConsumer;\nimport java.util.function.LongFunction;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.SnappedEdge;\nimport org.openstreetmap.atlas.geography.atlas.items.SnappedLineItem;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonFeatureCollection;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Atlas is a representation of an OpenStreetMap region in memory. It is a navigable collection of\n * unidirectional {@link Edge}s and {@link Node}s. It is designed to be close to the OpenStreetMap\n * model. It also contains a collection of non-navigable geolocated items that can be {@link Point}\n * s, {@link Line}s or {@link Area}s. All can be members of {@link Relation}s.\n *\n * @author matthieun\n * @author tony\n */\npublic interface Atlas\n        extends Located, Iterable<AtlasEntity>, Serializable, GeoJsonFeatureCollection<AtlasEntity>\n{\n    static <E extends AtlasEntity> Iterable<E> entitiesMatchingId(final Long[] identifiers,\n            final LongFunction<E> function)\n    {\n        return Arrays.stream(identifiers).map(function::apply).filter(Objects::nonNull)\n                .collect(Collectors.toSet());\n    }\n\n    /**\n     * @param identifier\n     *            The {@link Area}'s identifier\n     * @return The {@link Area} that corresponds to the provided identifier\n     */\n    Area area(long identifier);\n\n    /**\n     * @return All the {@link Area}s in this {@link Atlas}\n     */\n    Iterable<Area> areas();\n\n    /**\n     * A wrapper over {@link #area(long)} for multiple ids.\n     *\n     * @param identifiers\n     *            - The area identifiers to fetch.\n     * @return The {@link Area}s that corresponds to the provided identifier.\n     */\n    default Iterable<Area> areas(final Long... identifiers)\n    {\n        return entitiesMatchingId(identifiers, this::area);\n    }\n\n    /**\n     * Return all the {@link Area}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link Area}s matching the {@link Predicate}.\n     */\n    Iterable<Area> areas(Predicate<Area> matcher);\n\n    /**\n     * Return all the {@link Area}s covering some {@link Location}.\n     *\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link Area}s covering the {@link Location}.\n     */\n    Iterable<Area> areasCovering(Location location);\n\n    /**\n     * Return all the {@link Area}s matching a {@link Predicate} and covering some {@link Location}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link Area}s matching the {@link Predicate} and covering the\n     *         {@link Location}.\n     */\n    Iterable<Area> areasCovering(Location location, Predicate<Area> matcher);\n\n    /**\n     * Return all the {@link Area}s within and/or intersecting some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Area}s within and/or intersecting the surface.\n     */\n    Iterable<Area> areasIntersecting(GeometricSurface surface);\n\n    /**\n     * Return all the {@link Area}s matching a {@link Predicate}, and within and/or intersecting\n     * some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Area}s matching the {@link Predicate}, and within and/or intersecting\n     *         the surface.\n     */\n    Iterable<Area> areasIntersecting(GeometricSurface surface, Predicate<Area> matcher);\n\n    /**\n     * Return all the {@link Area}s fully within some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Area}s fully within the surface.\n     */\n    Iterable<Area> areasWithin(GeometricSurface surface);\n\n    /**\n     * @param matcher\n     *            The matcher to consider\n     * @return A {@link GeoJsonObject} that contains part the features in this {@link Atlas} which\n     *         matches the given matcher\n     */\n    JsonObject asGeoJson(Predicate<AtlasEntity> matcher);\n\n    /**\n     * Clone this {@link Atlas} to a {@link PackedAtlas}. Do not uses \"clone\" so as not to be\n     * confused with the clone function in {@link Cloneable}, which this interface does not extend.\n     *\n     * @return The {@link PackedAtlas} copy of this {@link Atlas}.\n     */\n    default PackedAtlas cloneToPackedAtlas()\n    {\n        return new PackedAtlasCloner().cloneFrom(this);\n    }\n\n    /**\n     * @param identifier\n     *            The {@link Edge}'s identifier\n     * @return The {@link Edge} that corresponds to the provided identifier\n     */\n    Edge edge(long identifier);\n\n    /**\n     * @return All the {@link Edge}s in this {@link Atlas}\n     */\n    Iterable<Edge> edges();\n\n    /**\n     * A wrapper over {@link #edge(long)} for multiple ids.\n     *\n     * @param identifiers\n     *            - The edge identifiers to fetch.\n     * @return The {@link Edge}s that corresponds to the provided identifier.\n     */\n    default Iterable<Edge> edges(final Long... identifiers)\n    {\n        return entitiesMatchingId(identifiers, this::edge);\n    }\n\n    /**\n     * Return all the {@link Edge}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link Edge}s matching the {@link Predicate}.\n     */\n    Iterable<Edge> edges(Predicate<Edge> matcher);\n\n    /**\n     * Return all the {@link Edge}s containing some {@link Location}.\n     *\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link Edge}s containing the {@link Location}.\n     */\n    Iterable<Edge> edgesContaining(Location location);\n\n    /**\n     * Return all the {@link Edge}s matching a {@link Predicate} and containing some\n     * {@link Location}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link Edge}s matching the {@link Predicate} and containing the\n     *         {@link Location}.\n     */\n    Iterable<Edge> edgesContaining(Location location, Predicate<Edge> matcher);\n\n    /**\n     * Return all the {@link Edge}s within and/or intersecting some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Edge}s within and/or intersecting the surface.\n     */\n    Iterable<Edge> edgesIntersecting(GeometricSurface surface);\n\n    /**\n     * Return all the {@link Edge}s matching a {@link Predicate}, and within and/or intersecting\n     * some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Edge}s matching the {@link Predicate}, and within and/or intersecting\n     *         the surface.\n     */\n    Iterable<Edge> edgesIntersecting(GeometricSurface surface, Predicate<Edge> matcher);\n\n    /**\n     * Return all the {@link Edge}s fully within some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Edge}s fully within the surface.\n     */\n    Iterable<Edge> edgesWithin(GeometricSurface surface);\n\n    /**\n     * Return all the {@link AtlasEntity}s\n     *\n     * @return All the {@link AtlasEntity}s\n     */\n    Iterable<AtlasEntity> entities();\n\n    /**\n     * Return all the {@link AtlasEntity}s of a specific type\n     *\n     * @param type\n     *            The type to restrain to\n     * @param memberClass\n     *            The class of the member\n     * @param <M>\n     *            The AtlasEntity type\n     * @return All the {@link AtlasEntity}s of a specific type\n     */\n    <M extends AtlasEntity> Iterable<M> entities(ItemType type, Class<M> memberClass);\n\n    /**\n     * Return all the {@link AtlasEntity}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link AtlasEntity}s matching the {@link Predicate}.\n     */\n    Iterable<AtlasEntity> entities(Predicate<AtlasEntity> matcher);\n\n    /**\n     * Return all the {@link AtlasEntity}s within and/or intersecting some surface.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to consider\n     * @return All the {@link AtlasEntity}s within and/or intersecting the {@link GeometricSurface}.\n     */\n    Iterable<AtlasEntity> entitiesIntersecting(GeometricSurface surface);\n\n    /**\n     * Return all the {@link AtlasEntity}s matching a {@link Predicate}, and within and/or\n     * intersecting some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link AtlasEntity}s matching the {@link Predicate}, and within and/or\n     *         intersecting the surface.\n     */\n    Iterable<AtlasEntity> entitiesIntersecting(GeometricSurface surface,\n            Predicate<AtlasEntity> matcher);\n\n    /**\n     * Return all the {@link AtlasEntity}s fully within some surface.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to consider\n     * @return All the {@link AtlasEntity}s fully within the {@link GeometricSurface}.\n     */\n    Iterable<AtlasEntity> entitiesWithin(GeometricSurface surface);\n\n    /**\n     * Return all the {@link AtlasEntity}s matching a {@link Predicate}, and fully within some\n     * surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The {@link GeometricSurface} to consider\n     * @return All the {@link AtlasEntity}s fully within the {@link GeometricSurface}.\n     */\n    Iterable<AtlasEntity> entitiesWithin(GeometricSurface surface, Predicate<AtlasEntity> matcher);\n\n    /**\n     * Get an entity from identifier and type\n     *\n     * @param identifier\n     *            the identifier\n     * @param type\n     *            The type\n     * @return The corresponding AtlasEntity, null if any.\n     */\n    AtlasEntity entity(long identifier, ItemType type);\n\n    /**\n     * @return This Atlas' identifier\n     */\n    UUID getIdentifier();\n\n    /**\n     * @return This Atlas' optional name. If not specified, should return a String version of the\n     *         identifier.\n     */\n    String getName();\n\n    /**\n     * Return all the {@link AtlasItem}s\n     *\n     * @return All the {@link AtlasItem}s\n     */\n    Iterable<AtlasItem> items();\n\n    /**\n     * Return all the {@link AtlasItem}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link AtlasItem}s matching the {@link Predicate}.\n     */\n    Iterable<AtlasItem> items(Predicate<AtlasItem> matcher);\n\n    /**\n     * Return all the {@link AtlasItem}s containing some {@link Location}.\n     *\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link AtlasItem}s containing the {@link Location}.\n     */\n    Iterable<AtlasItem> itemsContaining(Location location);\n\n    /**\n     * Return all the {@link AtlasItem}s matching a {@link Predicate} and containing some\n     * {@link Location}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link AtlasItem}s matching the {@link Predicate} and containing the\n     *         {@link Location}.\n     */\n    Iterable<AtlasItem> itemsContaining(Location location, Predicate<AtlasItem> matcher);\n\n    /**\n     * Return all the {@link AtlasItem}s within and/or intersecting some surface.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to consider\n     * @return All the {@link AtlasItem}s within and/or intersecting the {@link GeometricSurface}.\n     */\n    Iterable<AtlasItem> itemsIntersecting(GeometricSurface surface);\n\n    /**\n     * Return all the {@link AtlasItem}s matching a {@link Predicate}, and within and/or\n     * intersecting some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link AtlasItem}s matching the {@link Predicate}, and within and/or\n     *         intersecting the surface.\n     */\n    Iterable<AtlasItem> itemsIntersecting(GeometricSurface surface, Predicate<AtlasItem> matcher);\n\n    /**\n     * Return all the {@link AtlasItem}s fully within some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link AtlasItem}s fully within the surface.\n     */\n    Iterable<AtlasItem> itemsWithin(GeometricSurface surface);\n\n    /**\n     * @param identifier\n     *            The {@link Line}'s identifier\n     * @return The {@link Line} that corresponds to the provided identifier\n     */\n    Line line(long identifier);\n\n    /**\n     * Return all the {@link LineItem}s\n     *\n     * @return All the {@link LineItem}s\n     */\n    Iterable<LineItem> lineItems();\n\n    /**\n     * Return all the {@link LineItem}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link LineItem}s matching the {@link Predicate}.\n     */\n    Iterable<LineItem> lineItems(Predicate<LineItem> matcher);\n\n    /**\n     * Return all the {@link LineItem}s containing some {@link Location}.\n     *\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link LineItem}s containing the {@link Location}.\n     */\n    Iterable<LineItem> lineItemsContaining(Location location);\n\n    /**\n     * Return all the {@link LineItem}s matching a {@link Predicate} and containing some\n     * {@link Location}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link LineItem}s matching the {@link Predicate} and containing the\n     *         {@link Location}.\n     */\n    Iterable<LineItem> lineItemsContaining(Location location, Predicate<LineItem> matcher);\n\n    /**\n     * Return all the {@link LineItem}s within and/or intersecting some surface.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to consider\n     * @return All the {@link LineItem}s within and/or intersecting the {@link GeometricSurface}.\n     */\n    Iterable<LineItem> lineItemsIntersecting(GeometricSurface surface);\n\n    /**\n     * Return all the {@link LineItem}s matching a {@link Predicate}, and within and/or intersecting\n     * some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link LineItem}s matching the {@link Predicate}, and within and/or\n     *         intersecting the surface.\n     */\n    Iterable<LineItem> lineItemsIntersecting(GeometricSurface surface, Predicate<LineItem> matcher);\n\n    /**\n     * Return all the {@link LineItem}s fully within some surface.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to consider\n     * @return All the {@link LineItem}s within and/or intersecting the {@link GeometricSurface}.\n     */\n    Iterable<LineItem> lineItemsWithin(GeometricSurface surface);\n\n    /**\n     * @return All the {@link Line}s in this {@link Atlas}\n     */\n    Iterable<Line> lines();\n\n    /**\n     * A wrapper over {@link #line(long)} for multiple ids.\n     *\n     * @param identifiers\n     *            - The line identifiers to fetch.\n     * @return The {@link Line}s that corresponds to the provided identifier.\n     */\n    default Iterable<Line> lines(final Long... identifiers)\n    {\n        return entitiesMatchingId(identifiers, this::line);\n    }\n\n    /**\n     * Return all the {@link Line}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link Line}s matching the {@link Predicate}.\n     */\n    Iterable<Line> lines(Predicate<Line> matcher);\n\n    /**\n     * Return all the {@link Line}s containing some {@link Location}.\n     *\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link Line}s containing the location.\n     */\n    Iterable<Line> linesContaining(Location location);\n\n    /**\n     * Return all the {@link Line}s matching a {@link Predicate} and containing some\n     * {@link Location}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link Line}s matching the {@link Predicate} and containing the\n     *         {@link Location}.\n     */\n    Iterable<Line> linesContaining(Location location, Predicate<Line> matcher);\n\n    /**\n     * Return all the {@link Line}s within and/or intersecting some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Line}s within and/or intersecting the surface.\n     */\n    Iterable<Line> linesIntersecting(GeometricSurface surface);\n\n    /**\n     * Return all the {@link Line}s matching a {@link Predicate}, and within and/or intersecting\n     * some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Line}s matching the {@link Predicate}, and within and/or intersecting\n     *         the surface.\n     */\n    Iterable<Line> linesIntersecting(GeometricSurface surface, Predicate<Line> matcher);\n\n    /**\n     * Return all the {@link Line}s fully within some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Line}s fully within the surface.\n     */\n    Iterable<Line> linesWithin(GeometricSurface surface);\n\n    /**\n     * Return all the {@link LocationItem}s\n     *\n     * @return All the {@link LocationItem}s\n     */\n    Iterable<LocationItem> locationItems();\n\n    /**\n     * Return all the {@link LocationItem}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link LocationItem}s matching the {@link Predicate}.\n     */\n    Iterable<LocationItem> locationItems(Predicate<LocationItem> matcher);\n\n    /**\n     * Return all the {@link LocationItem}s within and/or intersecting some surface.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to consider\n     * @return All the {@link LocationItem}s within and/or intersecting the\n     *         {@link GeometricSurface}.\n     */\n    Iterable<LocationItem> locationItemsWithin(GeometricSurface surface);\n\n    /**\n     * Return all the {@link LocationItem}s matching a {@link Predicate}, and within and/or\n     * intersecting some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link LocationItem}s matching the {@link Predicate}, and within and/or\n     *         intersecting the surface.\n     */\n    Iterable<LocationItem> locationItemsWithin(GeometricSurface surface,\n            Predicate<LocationItem> matcher);\n\n    /**\n     * @return The meta data for this {@link Atlas}.\n     */\n    AtlasMetaData metaData();\n\n    /**\n     * @param identifier\n     *            The {@link Node}'s identifier\n     * @return The {@link Node} that corresponds to the provided identifier\n     */\n    Node node(long identifier);\n\n    /**\n     * @return All the {@link Node}s in this Atlas\n     */\n    Iterable<Node> nodes();\n\n    /**\n     * A wrapper over {@link #node(long)} for multiple ids.\n     *\n     * @param identifiers\n     *            - The node identifiers to fetch.\n     * @return The {@link Node}s that corresponds to the provided identifier.\n     */\n    default Iterable<Node> nodes(final Long... identifiers)\n    {\n        return entitiesMatchingId(identifiers, this::node);\n    }\n\n    /**\n     * Return all the {@link Node}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link Node}s matching the {@link Predicate}.\n     */\n    Iterable<Node> nodes(Predicate<Node> matcher);\n\n    /**\n     * Return all the {@link Node}s at some {@link Location}.\n     *\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link Node}s at the {@link Location}.\n     */\n    Iterable<Node> nodesAt(Location location);\n\n    /**\n     * Return all the {@link Node}s within and/or intersecting some surface. Note: results may vary,\n     * for an identical boundary, depending on the type, {@link Rectangle} or\n     * {@link GeometricSurface} of the input. This is due to an underlying dependency on the awt\n     * definition of insideness.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Node}s within and/or intersecting the surface.\n     */\n    Iterable<Node> nodesWithin(GeometricSurface surface);\n\n    /**\n     * Return all the {@link Node}s matching a {@link Predicate}, and within and/or intersecting\n     * some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Node}s matching the {@link Predicate}, and within and/or intersecting\n     *         the surface.\n     */\n    Iterable<Node> nodesWithin(GeometricSurface surface, Predicate<Node> matcher);\n\n    /**\n     * @return The number of {@link Area}s\n     */\n    long numberOfAreas();\n\n    /**\n     * @return The number of {@link Edge}s\n     */\n    long numberOfEdges();\n\n    /**\n     * @return The number of {@link Line}s\n     */\n    long numberOfLines();\n\n    /**\n     * @return The number of {@link Node}s\n     */\n    long numberOfNodes();\n\n    /**\n     * @return The number of {@link Point}s\n     */\n    long numberOfPoints();\n\n    /**\n     * @return The number of {@link Relation}s\n     */\n    long numberOfRelations();\n\n    /**\n     * @param identifier\n     *            The {@link Point}'s identifier\n     * @return The {@link Point} that corresponds to the provided identifier\n     */\n    Point point(long identifier);\n\n    /**\n     * @return All the {@link Point}s in this Atlas\n     */\n    Iterable<Point> points();\n\n    /**\n     * A wrapper over {@link #point(long)} for multiple ids.\n     *\n     * @param identifiers\n     *            - The point identifiers to fetch.\n     * @return The {@link Point}s that corresponds to the provided identifier.\n     */\n    default Iterable<Point> points(final Long... identifiers)\n    {\n        return entitiesMatchingId(identifiers, this::point);\n    }\n\n    /**\n     * Return all the {@link Point}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link Point}s matching the {@link Predicate}.\n     */\n    Iterable<Point> points(Predicate<Point> matcher);\n\n    /**\n     * Return all the {@link Point}s at some {@link Location}.\n     *\n     * @param location\n     *            The {@link Location} to consider\n     * @return All the {@link Point}s at the {@link Location}.\n     */\n    Iterable<Point> pointsAt(Location location);\n\n    /**\n     * Return all the {@link Point}s within some surface. Note: results may vary, for an identical\n     * boundary, depending on the type, {@link Rectangle} or {@link GeometricSurface} of the input.\n     * This is due to an underlying dependency on the awt definition of insideness.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Point}s within the surface.\n     */\n    Iterable<Point> pointsWithin(GeometricSurface surface);\n\n    /**\n     * Return all the {@link Point}s matching a {@link Predicate}. Note: results may vary, for an\n     * identical boundary, depending on the type, {@link Rectangle} or {@link GeometricSurface} of\n     * the input. This is due to an underlying dependency on the awt definition of insideness.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Point}s matching the {@link Predicate}, and within and/or intersecting\n     *         the surface.\n     */\n    Iterable<Point> pointsWithin(GeometricSurface surface, Predicate<Point> matcher);\n\n    /**\n     * @param identifier\n     *            The {@link Relation}'s identifier\n     * @return The {@link Relation} that corresponds to the provided identifier\n     */\n    Relation relation(long identifier);\n\n    /**\n     * @return All the {@link Relation}s in this Atlas\n     */\n    Iterable<Relation> relations();\n\n    /**\n     * A wrapper over {@link #relation(long)} for multiple ids.\n     *\n     * @param identifiers\n     *            - The relation identifiers to fetch.\n     * @return The {@link Relation}s that corresponds to the provided identifier.\n     */\n    default Iterable<Relation> relations(final Long... identifiers)\n    {\n        return entitiesMatchingId(identifiers, this::relation);\n    }\n\n    /**\n     * Return all the {@link Relation}s matching a {@link Predicate}.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @return All the {@link Relation}s matching the {@link Predicate}.\n     */\n    Iterable<Relation> relations(Predicate<Relation> matcher);\n\n    /**\n     * @return All the {@link Relation}s in this Atlas, with the lower order relations first. This\n     *         means at any point, if a relation is returned by this {@link Iterable}, then all the\n     *         relations that belong to this relation have already been returned.\n     */\n    Iterable<Relation> relationsLowerOrderFirst();\n\n    /**\n     * Return all the {@link Relation}s which have at least one feature intersecting some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Relation}s which have at least one feature intersecting the surface.\n     */\n    Iterable<Relation> relationsWithEntitiesIntersecting(GeometricSurface surface);\n\n    /**\n     * Return all the {@link Relation}s which have at least one feature intersecting some surface.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Relation}s matching the {@link Predicate}, and which have at least one\n     *         feature intersecting the surface.\n     */\n    Iterable<Relation> relationsWithEntitiesIntersecting(GeometricSurface surface,\n            Predicate<Relation> matcher);\n\n    /**\n     * Return all the {@link Relation}s which have all features within some surface.\n     *\n     * @param surface\n     *            The surface to consider\n     * @return All the {@link Relation}s which have all features within the surface.\n     */\n    Iterable<Relation> relationsWithEntitiesWithin(GeometricSurface surface);\n\n    /**\n     * Serialize this {@link Atlas} to a {@link WritableResource}\n     *\n     * @param writableResource\n     *            The resource to write to\n     */\n    void save(WritableResource writableResource);\n\n    /**\n     * Save as GeoJSON\n     *\n     * @param resource\n     *            The resource to write to\n     */\n    void saveAsGeoJson(WritableResource resource);\n\n    /**\n     * Save as GeoJSON with matcher\n     *\n     * @param resource\n     *            The resource to write to\n     * @param matcher\n     *            The matcher to consider\n     */\n    void saveAsGeoJson(WritableResource resource, Predicate<AtlasEntity> matcher);\n\n    /**\n     * Save as line-delimited GeoJSON. This is one feature per line, with no wrapping\n     * FeatureCollection.\n     *\n     * @param resource\n     *            The resource to write to\n     * @param jsonMutator\n     *            The callback function that will let you change what is in the Feature's JSON.\n     */\n    void saveAsLineDelimitedGeoJsonFeatures(WritableResource resource,\n            BiConsumer<AtlasEntity, JsonObject> jsonMutator);\n\n    /**\n     * Save as line-delimited GeoJSON with a matcher. This is one feature per line, with no wrapping\n     * FeatureCollection.\n     *\n     * @param resource\n     *            The resource to write to\n     * @param matcher\n     *            The matcher to consider\n     * @param jsonMutator\n     *            The callback function that will let you change what is in the Feature's JSON.\n     */\n    void saveAsLineDelimitedGeoJsonFeatures(WritableResource resource,\n            Predicate<AtlasEntity> matcher, BiConsumer<AtlasEntity, JsonObject> jsonMutator);\n\n    /**\n     * Save as list of items\n     *\n     * @param resource\n     *            The resource to write to\n     */\n    void saveAsList(WritableResource resource);\n\n    /**\n     * Save as a naive proto file\n     *\n     * @param resource\n     *            The resource to write to\n     */\n    void saveAsProto(WritableResource resource);\n\n    /**\n     * Save as a text file\n     *\n     * @param resource\n     *            The resource to write to\n     */\n    void saveAsText(WritableResource resource);\n\n    /**\n     * @return The size for this {@link Atlas}.\n     */\n    default AtlasSize size()\n    {\n        return new AtlasSize(numberOfEdges(), numberOfNodes(), numberOfAreas(), numberOfLines(),\n                numberOfPoints(), numberOfRelations());\n    }\n\n    /**\n     * @param point\n     *            A {@link Location} to snap\n     * @param threshold\n     *            A {@link Distance} threshold to look for edges around the {@link Location}\n     * @return The best snapped result, or null if there is no valid snap\n     */\n    SnappedEdge snapped(Location point, Distance threshold);\n\n    /**\n     * @param point\n     *            A {@link Location} to snap\n     * @param threshold\n     *            A {@link Distance} threshold to look for edges around the {@link Location}\n     * @return A sorted {@link List} of all the candidate snaps. The list is empty if there are no\n     *         candidates.\n     */\n    List<SnappedEdge> snaps(Location point, Distance threshold);\n\n    /**\n     * @param point\n     *            A {@link Location} to snap\n     * @param threshold\n     *            A {@link Distance} threshold to look for edges around the {@link Location}\n     * @return A sorted {@link List} of all the candidate snaps. The list is empty if there are no\n     *         candidates.\n     */\n    List<SnappedLineItem> snapsLineItem(Location point, Distance threshold);\n\n    /**\n     * Return a sub-atlas from this Atlas.\n     *\n     * @param boundary\n     *            The boundary within which the sub atlas will be built\n     * @param cutType\n     *            The type of cut to perform\n     * @return An optional sub-atlas. The optional will be empty in case there is nothing in the\n     *         {@link GeometricSurface} after the cut was applied. Returning an empty atlas is not\n     *         allowed.\n     */\n    Optional<Atlas> subAtlas(GeometricSurface boundary, AtlasCutType cutType);\n\n    /**\n     * Return a sub-atlas from this Atlas.\n     *\n     * @param matcher\n     *            The matcher to consider\n     * @param cutType\n     *            The type of cut to perform\n     * @return An optional sub-atlas. The optional will be empty in case the matcher and cut-type\n     *         return an empty atlas, which is not allowed.\n     */\n    Optional<Atlas> subAtlas(Predicate<AtlasEntity> matcher, AtlasCutType cutType);\n\n    /**\n     * Get a summary of this {@link Atlas}. This string should be relatively compact, for e.g. just\n     * the entity counts.\n     *\n     * @return A summary of this {@link Atlas}.\n     */\n    String summary();\n\n    /**\n     * Get a complete string representation of this {@link Atlas}. This string may include details\n     * on all contained entities.\n     *\n     * @return a complete string representation of this {@link Atlas}\n     */\n    String toStringDetailed();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/AtlasLoadingCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * Any command that wants to load Atlas files based on certain criteria (ISO country codes, etc...)\n * can subclass this command and receive that behavior for free. Currently we only support the ISO\n * country codes with the -include-only flag\n *\n * @author cstaylor\n */\npublic abstract class AtlasLoadingCommand extends Command\n{\n    /**\n     * Lets us filter both Paths and Strings based on the ISO country names to be included or\n     * excluded\n     *\n     * @author cstaylor\n     */\n    public static class AcceptableInputFileFilter implements Predicate<Resource>\n    {\n        /**\n         * These are for extracting the ISO3 country code from the filename\n         */\n        private static final int START_ISO3_NAME_INDEX = 0;\n        private static final int END_ISO3_NAME_INDEX = 3;\n\n        private final Set<String> acceptableISOCodes = new HashSet<>();\n        private final Set<String> excludedISOCodes = new HashSet<>();\n\n        public AcceptableInputFileFilter exclude(final Iterable<String> exclude)\n        {\n            if (exclude != null)\n            {\n                Iterables.addAll(this.excludedISOCodes, exclude);\n            }\n            return this;\n        }\n\n        public AcceptableInputFileFilter include(final Iterable<String> include)\n        {\n            if (include != null)\n            {\n                Iterables.addAll(this.acceptableISOCodes, include);\n            }\n            return this;\n        }\n\n        @Override\n        public boolean test(final Resource fileName)\n        {\n            final String isoCode = fileName.getName().substring(START_ISO3_NAME_INDEX,\n                    END_ISO3_NAME_INDEX);\n            if (this.acceptableISOCodes.size() > 0)\n            {\n                if (!this.acceptableISOCodes.contains(isoCode))\n                {\n                    return false;\n                }\n            }\n            return this.excludedISOCodes.size() == 0 || !this.excludedISOCodes.contains(isoCode);\n        }\n    }\n\n    protected static final Switch<Set<String>> INCLUDE_ONLY_THESE_COUNTRIES_PARAMETER = new Switch<>(\n            \"include-only\",\n            \"list of comma-delimited ISO country codes that we'll include when searching folders for atlas files\",\n            possiblyMultipleISOs -> StringList.split(possiblyMultipleISOs, \",\").stream()\n                    .collect(Collectors.toSet()),\n            Optionality.OPTIONAL);\n\n    protected static final Switch<File> INPUT_FOLDER = new Switch<>(\"inputFolder\",\n            \"Path of folder which contains Atlas files\", File::new, Command.Optionality.OPTIONAL);\n\n    protected static final Switch<File> INPUT = new Switch<>(\"input\", \"Path of Atlas file\",\n            File::new, Command.Optionality.OPTIONAL);\n\n    protected static final Switch<Set<String>> EXCLUDE_THESE_COUNTRIES_PARAMETER = new Switch<>(\n            \"exclude\",\n            \"list of comma-delimited ISO country codes that we'll exclude when searching folders for atlas files\",\n            possiblyMultipleISOs -> StringList.split(possiblyMultipleISOs, \",\").stream()\n                    .collect(Collectors.toSet()),\n            Optionality.OPTIONAL);\n\n    /**\n     * Helper method for loading a MultiAtlas based on the criteria passed in through the command\n     * line.\n     *\n     * @param commandMap\n     *            the list of command line arguments\n     * @return a MultiAtlas containing the data from files who matched our search criteria\n     */\n    protected Atlas loadAtlas(final CommandMap commandMap)\n    {\n        return loadAtlas(INPUT_FOLDER, commandMap);\n    }\n\n    /**\n     * Helper method for loading a MultiAtlas based on the criteria passed in through the command\n     * line.\n     *\n     * @param parameter\n     *            the command line parameter to use for loading the atlas files\n     * @param commandMap\n     *            the list of command line arguments\n     * @return a MultiAtlas containing the data from files who matched our search criteria\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected Atlas loadAtlas(final Switch<File> parameter, final CommandMap commandMap)\n    {\n        final AcceptableInputFileFilter filter = new AcceptableInputFileFilter()\n                .include((Set<String>) commandMap.get(INCLUDE_ONLY_THESE_COUNTRIES_PARAMETER))\n                .exclude((Set<String>) commandMap.get(EXCLUDE_THESE_COUNTRIES_PARAMETER));\n        final File input = (File) commandMap.get(INPUT);\n        if (input != null)\n        {\n            return new AtlasResourceLoader().load(input);\n        }\n        final File inputFolder = (File) commandMap.get(parameter);\n        if (inputFolder == null)\n        {\n            throw new CoreException(\"Switch missing: input file or input folder\");\n        }\n        return new AtlasResourceLoader().withResourceFilter(filter).loadRecursively(inputFolder);\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(INPUT, INPUT_FOLDER, INCLUDE_ONLY_THESE_COUNTRIES_PARAMETER,\n                EXCLUDE_THESE_COUNTRIES_PARAMETER);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/AtlasMetaData.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonProperties;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAtlasMetaDataAdapter;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Meta data for an {@link Atlas}\n *\n * @author matthieun\n * @author lcram\n */\npublic final class AtlasMetaData\n        implements Serializable, Taggable, ProtoSerializable, GeoJsonProperties\n{\n    public static final String EDGE_CONFIGURATION = \"edgeConfiguration\";\n    public static final String AREA_CONFIGURATION = \"areaConfiguration\";\n    public static final String WAY_SECTIONING_CONFIGURATION = \"waySectioningConfiguration\";\n    public static final String OSM_PBF_WAY_CONFIGURATION = \"osmPbfWayConfiguration\";\n    public static final String OSM_PBF_NODE_CONFIGURATION = \"osmPbfNodeConfiguration\";\n    public static final String OSM_PBF_RELATION_CONFIGURATION = \"osmPbfRelationConfiguration\";\n    /** Set to \"true\" if -keepAll was passed on the command line */\n    public static final String KEEP_ALL_CONFIGURATION = \"keepAll\";\n    private static final long serialVersionUID = -285346019736489425L;\n    private static final String UNKNOWN_VALUE = \"unknown\";\n\n    private final AtlasSize size;\n    private final boolean original;\n    private final String codeVersion;\n    private final String dataVersion;\n    private final String country;\n    private final String shardName;\n    private final Map<String, String> tags;\n\n    public AtlasMetaData()\n    {\n        this(AtlasSize.DEFAULT);\n    }\n\n    public AtlasMetaData(final AtlasSize size)\n    {\n        this(size, true, UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE,\n                Maps.hashMap());\n    }\n\n    public AtlasMetaData(final AtlasSize size, final boolean original, final String codeVersion,\n            final String dataVersion, final String country, final String shardName,\n            final Map<String, String> tags)\n    {\n        this.size = size;\n        this.original = original;\n        this.codeVersion = codeVersion;\n        this.dataVersion = dataVersion;\n        this.country = country;\n        this.shardName = shardName;\n        this.tags = tags;\n    }\n\n    public AtlasMetaData copyWithNewOriginal(final boolean original)\n    {\n        return new AtlasMetaData(this.size, original, this.codeVersion, this.dataVersion,\n                this.country, this.shardName, this.tags);\n    }\n\n    public AtlasMetaData copyWithNewShardName(final String shardName)\n    {\n        return new AtlasMetaData(this.size, this.original, this.codeVersion, this.dataVersion,\n                this.country, shardName, this.tags);\n    }\n\n    public AtlasMetaData copyWithNewSize(final AtlasSize size)\n    {\n        return new AtlasMetaData(size, this.original, this.codeVersion, this.dataVersion,\n                this.country, this.shardName, this.tags);\n    }\n\n    /**\n     * Copy this metadata with new tags\n     *\n     * @param tags\n     *            The tags to copy\n     * @return The new AtlasMetaData to use\n     */\n    public AtlasMetaData copyWithNewTags(final Map<String, String> tags)\n    {\n        return new AtlasMetaData(this.size, this.original, this.codeVersion, this.dataVersion,\n                this.country, this.shardName, tags);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof AtlasMetaData)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            final AtlasMetaData that = (AtlasMetaData) other;\n            if (!Objects.equals(this.getSize(), that.getSize()))\n            {\n                return false;\n            }\n            if (this.isOriginal() != that.isOriginal())\n            {\n                return false;\n            }\n            if (!Objects.equals(this.getCodeVersion(), that.getCodeVersion()))\n            {\n                return false;\n            }\n            if (!Objects.equals(this.getDataVersion(), that.getDataVersion()))\n            {\n                return false;\n            }\n            if (!Objects.equals(this.getCountry(), that.getCountry()))\n            {\n                return false;\n            }\n            if (!Objects.equals(this.getShardName(), that.getShardName()))\n            {\n                return false;\n            }\n            if (!Objects.equals(this.getTags(), that.getTags()))\n            {\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    public Optional<String> getCodeVersion()\n    {\n        return Optional.ofNullable(this.codeVersion);\n    }\n\n    public Optional<String> getCountry()\n    {\n        return Optional.ofNullable(this.country);\n    }\n\n    public Optional<String> getDataVersion()\n    {\n        return Optional.ofNullable(this.dataVersion);\n    }\n\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        final JsonObject properties = new JsonObject();\n        properties.add(\"size\", this.getSize().getGeoJsonProperties());\n        properties.addProperty(\"original\", this.isOriginal());\n        this.getCodeVersion()\n                .ifPresent(versionString -> properties.addProperty(\"Code Version\", versionString));\n        this.getDataVersion()\n                .ifPresent(versionString -> properties.addProperty(\"Data Version\", versionString));\n        this.getCountry()\n                .ifPresent(countryString -> properties.addProperty(\"Country\", countryString));\n        this.getShardName()\n                .ifPresent(theShardName -> properties.addProperty(\"Shard Name\", theShardName));\n        this.getTags().forEach((key, value) ->\n        {\n            if (!properties.has(key))\n            {\n                properties.addProperty(key, value);\n            }\n        });\n        return properties;\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoAtlasMetaDataAdapter();\n    }\n\n    public Optional<String> getShardName()\n    {\n        return Optional.ofNullable(this.shardName);\n    }\n\n    public AtlasSize getSize()\n    {\n        return this.size;\n    }\n\n    @Override\n    public Optional<String> getTag(final String key)\n    {\n        return Optional.ofNullable(this.tags.get(key));\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        if (this.tags == null)\n        {\n            return new HashMap<>();\n        }\n        return new HashMap<>(this.tags);\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final int sizeHash = this.getSize().hashCode();\n        return Objects.hash(Integer.valueOf(sizeHash), Boolean.valueOf(this.original),\n                this.codeVersion, this.dataVersion, this.country, this.shardName, this.tags);\n    }\n\n    public boolean isOriginal()\n    {\n        return this.original;\n    }\n\n    public String toReadableString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"Size: \");\n        builder.append(\"\\n\\tNodes: \");\n        builder.append(this.size.getNodeNumber());\n        builder.append(\"\\n\\tEdges: \");\n        builder.append(this.size.getEdgeNumber());\n        builder.append(\"\\n\\tAreas: \");\n        builder.append(this.size.getAreaNumber());\n        builder.append(\"\\n\\tLines: \");\n        builder.append(this.size.getLineNumber());\n        builder.append(\"\\n\\tPoints: \");\n        builder.append(this.size.getPointNumber());\n        builder.append(\"\\n\\tRelations: \");\n        builder.append(this.size.getRelationNumber());\n        builder.append(\"\\n\");\n        builder.append(\"Original: \");\n        builder.append(this.original);\n        builder.append(\"\\n\");\n        builder.append(\"Code Version: \");\n        builder.append(this.codeVersion);\n        builder.append(\"\\n\");\n        builder.append(\"Data Version: \");\n        builder.append(this.dataVersion);\n        builder.append(\"\\n\");\n        builder.append(\"Country: \");\n        builder.append(this.country);\n        builder.append(\"\\n\");\n        builder.append(\"Shard: \");\n        builder.append(this.shardName);\n        builder.append(\"\\n\");\n        builder.append(\"Tags:\\n\\t\");\n        final SortedSet<String> sortedTags = this.tags.entrySet().stream()\n                .map(entry -> entry.getKey() + \" -> \" + entry.getValue())\n                .collect(Collectors.toCollection(TreeSet::new));\n        builder.append(new StringList(sortedTags).join(\"\\n\\t\"));\n        builder.append(\"\\n\");\n        return builder.toString();\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[AtlasMetaData: size=\" + this.size + \", original=\" + this.original\n                + \", codeVersion=\" + this.codeVersion + \", dataVersion=\" + this.dataVersion\n                + \", country=\" + this.country + \", shardName=\" + this.shardName + \", tags=\"\n                + this.tags + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/AtlasResourceLoader.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.AbstractResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Load an {@link Atlas} from a {@link Resource} or an {@link Iterable} of {@link Resource}s. Also\n * supports loading based on a resource name filter. To recursively load all {@link Atlas}es in a\n * directory, see the {@link AtlasResourceLoader#loadRecursively} method.\n *\n * @author lcram\n */\npublic class AtlasResourceLoader\n{\n    public static final Predicate<Resource> HAS_TEXT_ATLAS_EXTENSION = FileSuffix\n            .resourceFilter(FileSuffix.ATLAS, FileSuffix.TEXT)\n            .or(FileSuffix.resourceFilter(FileSuffix.ATLAS, FileSuffix.TEXT, FileSuffix.GZIP));\n    public static final Predicate<Resource> HAS_ATLAS_EXTENSION = FileSuffix\n            .resourceFilter(FileSuffix.ATLAS)\n            .or(FileSuffix.resourceFilter(FileSuffix.ATLAS, FileSuffix.GZIP));\n\n    private static final Logger logger = LoggerFactory.getLogger(AtlasResourceLoader.class);\n\n    private static final Predicate<Resource> CONTENTS_LOOK_LIKE_TEXT_ATLAS = resource ->\n    {\n        checkFileExistsAndIsNotDirectory(resource);\n        setDecompressorFor(resource);\n\n        return resource.firstLine().equals(TextAtlasBuilder.getNodesHeader());\n    };\n    private Predicate<Resource> resourceFilter;\n    private Predicate<AtlasEntity> atlasEntityFilter;\n    private String multiAtlasName;\n\n    private static void checkFileExistsAndIsNotDirectory(final Resource resource)\n    {\n        if (resource instanceof File)\n        {\n            final File fileResource = (File) resource;\n            if (!fileResource.exists())\n            {\n                throw new CoreException(\"Resource {} was of type File but it could not be found\",\n                        resource.getName());\n            }\n            else if (fileResource.isDirectory())\n            {\n                throw new CoreException(\n                        \"Resource {} was of type File but it was a directory. Try loadRecursively instead.\",\n                        resource.getName());\n            }\n        }\n    }\n\n    private static void setDecompressorFor(final Resource resource)\n    {\n        if (FileSuffix.GZIP.matches(resource))\n        {\n            if (resource instanceof AbstractResource)\n            {\n                ((AbstractResource) resource).setDecompressor(Decompressor.GZIP);\n            }\n            else\n            {\n                throw new CoreException(\n                        \"Provide resource was of type {} which does not support decompression.\",\n                        resource.getClass().getName());\n            }\n        }\n    }\n\n    public AtlasResourceLoader()\n    {\n        this.resourceFilter = resource -> true;\n        this.atlasEntityFilter = null;\n    }\n\n    /**\n     * Load an {@link Atlas} from the provided {@link Resource}(s). If more than one\n     * {@link Resource} is provided, the method will utilize the {@link MultiAtlas} to combine them.\n     * This method will fail with an exception if any of the provided {@link Resource}s do not\n     * contain a valid binary or text {@link Atlas}. This method should never return null.\n     *\n     * @param resources\n     *            the {@link Resource}(s) from which to load\n     * @return the non-null loaded {@link Atlas}\n     */\n    public Atlas load(final Resource... resources)\n    {\n        return load(Iterables.from(resources));\n    }\n\n    /**\n     * Load an {@link Atlas} from an {@link Iterable} of {@link Resource}s. If more than one\n     * {@link Resource} is provided, the method will utilize the {@link MultiAtlas} to combine them.\n     * This method will fail with an exception if any of the provided {@link Resource}s do not\n     * contain a valid binary or text {@link Atlas}. This method should never return null.\n     *\n     * @param resources\n     *            the {@link Iterable} of {@link Resource}s from which to load\n     * @return the non-null loaded {@link Atlas}\n     */\n    public Atlas load(final Iterable<? extends Resource> resources)\n    {\n        final List<Resource> atlasResources = Iterables.stream(resources)\n                .flatMap(this::upcastAndRemoveNullResources).filter(this.resourceFilter)\n                .collectToList();\n\n        final Optional<Atlas> resultAtlasOptional;\n        if (atlasResources.isEmpty())\n        {\n            throw new CoreException(\"No loadable Resources were found.\");\n        }\n        else if (atlasResources.size() == 1)\n        {\n            resultAtlasOptional = loadAtlasResource(atlasResources.get(0));\n        }\n        else\n        {\n            resultAtlasOptional = loadMultipleAtlasResources(atlasResources);\n        }\n\n        if (resultAtlasOptional.isEmpty())\n        {\n            throw new CoreException(\n                    \"Unable to load atlas from provided Resources. If you are seeing this you likely found a bug with AtlasResourceLoader. Please report it.\");\n        }\n        // Apply the filter at the end\n        return applyEntityFilter(resultAtlasOptional.get());\n    }\n\n    /**\n     * Load an {@link Atlas} from the provided {@link File} {@link Resource}(s). If any of the\n     * provided {@link File}(s) are directories, the method will recursively descend into the\n     * directory and include every {@link Atlas} it discovers. It identifies {@link Atlas}es by\n     * looking for {@link FileSuffix#ATLAS} file extensions. Like with the\n     * {@link AtlasResourceLoader#load} method, this method will utilize the {@link MultiAtlas} to\n     * combine the {@link Atlas}es. This method should never return null.\n     *\n     * @param resources\n     *            the {@link File} {@link Resource}(s) from which to load\n     * @return the non-null loaded {@link Atlas}\n     */\n    public Atlas loadRecursively(final Resource... resources)\n    {\n        return loadRecursively(Iterables.from(resources));\n    }\n\n    /**\n     * Load an {@link Atlas} from an {@link Iterable} of {@link File} {@link Resource}s. If any of\n     * the provided {@link File}(s) are directories, the method will recursively descend into the\n     * directory and include every {@link Atlas} it discovers. It identifies {@link Atlas}es by\n     * looking for {@link FileSuffix#ATLAS} file extensions. Like with the\n     * {@link AtlasResourceLoader#load} method, this method will utilize the {@link MultiAtlas} to\n     * combine the {@link Atlas}es. This method should never return null.\n     *\n     * @param resources\n     *            the {@link Iterable} of {@link File} {@link Resource}s from which to load\n     * @return the non-null loaded {@link Atlas}\n     */\n    public Atlas loadRecursively(final Iterable<Resource> resources)\n    {\n        final List<Resource> atlasResources = Iterables.stream(resources).filter(Objects::nonNull)\n                .flatMap(this::expandFileOrDirectoryRecursively)\n                .filter(HAS_ATLAS_EXTENSION.or(HAS_TEXT_ATLAS_EXTENSION))\n                .filter(this.resourceFilter).collectToList();\n\n        final Optional<Atlas> resultAtlasOptional = loadMultipleAtlasResources(atlasResources);\n        if (!resultAtlasOptional.isPresent())\n        {\n            throw new CoreException(\n                    \"Unable to load atlas from provided Resources. If you are seeing this you likely found a bug with AtlasResourceLoader. Please report it.\");\n        }\n        // Apply the filter at the end\n        return applyEntityFilter(resultAtlasOptional.get());\n    }\n\n    /**\n     * This safe load method will never throw an exception. If any if the provided {@link Resource}s\n     * cannot be loaded into an {@link Atlas}, it will simply return an empty {@link Optional}.\n     *\n     * @param resources\n     *            the {@link Resource}(s) from which to load\n     * @return an {@link Optional} wrapping the loaded {@link Atlas} if present\n     */\n    public Optional<Atlas> safeLoad(final Resource... resources)\n    {\n        return safeLoad(Iterables.from(resources));\n    }\n\n    /**\n     * This safe load method will never throw an exception. If any if the provided {@link Resource}s\n     * cannot be loaded into an {@link Atlas}, it will simply return an empty {@link Optional}.\n     *\n     * @param resources\n     *            the {@link Iterable} of {@link Resource}(s) from which to load\n     * @return an {@link Optional} wrapping the loaded {@link Atlas} if present\n     */\n    public Optional<Atlas> safeLoad(final Iterable<Resource> resources)\n    {\n        try\n        {\n            return Optional.of(load(resources));\n        }\n        catch (final Exception exception)\n        {\n            logger.error(\"Could not load atlas from supplied resources\", exception);\n            return Optional.empty();\n        }\n    }\n\n    /**\n     * This safe load method will never throw an exception. If any if the provided {@link Resource}s\n     * cannot be loaded into an {@link Atlas}, it will simply return an empty {@link Optional}. See\n     * the documentation for {@link AtlasResourceLoader#loadRecursively(Resource...)} for details on\n     * how the recursive load works.\n     *\n     * @param resources\n     *            the {@link Iterable} of {@link Resource}(s) from which to load\n     * @return an {@link Optional} wrapping the loaded {@link Atlas} if present\n     */\n    public Optional<Atlas> safeLoadRecursively(final Resource... resources)\n    {\n        return safeLoadRecursively(Iterables.from(resources));\n    }\n\n    /**\n     * This safe load method will never throw an exception. If any if the provided {@link Resource}s\n     * cannot be loaded into an {@link Atlas}, it will simply return an empty {@link Optional}. See\n     * the documentation for {@link AtlasResourceLoader#loadRecursively(Resource...)} for details on\n     * how the recursive load works.\n     *\n     * @param resources\n     *            the {@link Iterable} of {@link Resource}(s) from which to load\n     * @return an {@link Optional} wrapping the loaded {@link Atlas} if present\n     */\n    public Optional<Atlas> safeLoadRecursively(final Iterable<Resource> resources)\n    {\n        try\n        {\n            return Optional.of(loadRecursively(resources));\n        }\n        catch (final Exception exception)\n        {\n            logger.error(\"Could not load atlas from supplied resources\", exception);\n            return Optional.empty();\n        }\n    }\n\n    /**\n     * Optionally add an {@link AtlasEntity} filter\n     *\n     * @param filter\n     *            filter which {@link AtlasEntity}s to include/exclude in the {@link Atlas}\n     */\n    public void setAtlasEntityFilter(final Predicate<AtlasEntity> filter)\n    {\n        this.atlasEntityFilter = filter;\n    }\n\n    /**\n     * Optionally add a {@link Resource} filter\n     *\n     * @param filter\n     *            filter which {@link Resource} to load\n     */\n    public void setResourceFilter(final Predicate<Resource> filter)\n    {\n        this.resourceFilter = filter;\n    }\n\n    /**\n     * Optionally add an {@link AtlasEntity} filter\n     *\n     * @param filter\n     *            filter which {@link AtlasEntity}s to include/exclude in the {@link Atlas}\n     * @return fluent interface requires this be returned\n     */\n    public AtlasResourceLoader withAtlasEntityFilter(final Predicate<AtlasEntity> filter)\n    {\n        setAtlasEntityFilter(filter);\n        return this;\n    }\n\n    /**\n     * Set the name for the {@link MultiAtlas} that results from the load.\n     *\n     * @param multiAtlasName\n     *            the name\n     * @return instance of {@link AtlasResourceLoader} for method chaining\n     */\n    public AtlasResourceLoader withMultiAtlasName(final String multiAtlasName)\n    {\n        this.multiAtlasName = multiAtlasName;\n        return this;\n    }\n\n    /**\n     * Optionally add a {@link Resource} filter\n     *\n     * @param filter\n     *            filter which {@link Resource} to load\n     * @return instance of {@link AtlasResourceLoader} for method chaining\n     */\n    public AtlasResourceLoader withResourceFilter(final Predicate<Resource> filter)\n    {\n        setResourceFilter(filter);\n        return this;\n    }\n\n    private Atlas applyEntityFilter(final Atlas atlasToFilter)\n    {\n        if (this.atlasEntityFilter != null)\n        {\n            final Optional<Atlas> subAtlas = atlasToFilter.subAtlas(this.atlasEntityFilter,\n                    AtlasCutType.SOFT_CUT);\n            return subAtlas.orElseThrow(\n                    () -> new CoreException(\"Entity filter resulted in an empty atlas\"));\n        }\n        return atlasToFilter;\n    }\n\n    private List<Resource> expandFileOrDirectoryRecursively(final Resource resource)\n    {\n        if (resource == null)\n        {\n            return new ArrayList<>();\n        }\n\n        if (!(resource instanceof File))\n        {\n            throw new CoreException(\"Resource {} was not a File, instead was {}\",\n                    resource.getName(), resource.getClass().getName());\n        }\n\n        final File file = (File) resource;\n        final List<Resource> result = new ArrayList<>();\n        if (file.isDirectory())\n        {\n            file.listFilesRecursively().forEach(child ->\n            {\n                if (child.isGzipped())\n                {\n                    child.setDecompressor(Decompressor.GZIP);\n                }\n                result.add(child);\n            });\n        }\n        else\n        {\n            result.add(file);\n        }\n\n        return result;\n    }\n\n    private List<Resource> filterForBinaryAtlasResources(final List<Resource> atlasResources)\n    {\n        return atlasResources.stream().filter(CONTENTS_LOOK_LIKE_TEXT_ATLAS.negate())\n                .collect(Collectors.toList());\n    }\n\n    private List<Resource> filterForTextAtlasResources(final List<Resource> atlasResources)\n    {\n        return atlasResources.stream().filter(CONTENTS_LOOK_LIKE_TEXT_ATLAS)\n                .collect(Collectors.toList());\n    }\n\n    private Optional<Atlas> loadAtlasResource(final Resource resource)\n    {\n        final Atlas result;\n\n        if (resource instanceof File)\n        {\n            checkFileExistsAndIsNotDirectory(resource);\n        }\n\n        if (resource.length() == 0L)\n        {\n            throw new CoreException(\"{} {} had zero length!\", resource.getClass().getName(),\n                    resource.getName());\n        }\n\n        if (CONTENTS_LOOK_LIKE_TEXT_ATLAS.test(resource))\n        {\n            setDecompressorFor(resource);\n            result = new TextAtlasBuilder().read(resource);\n        }\n        else\n        {\n            try\n            {\n                result = PackedAtlas.load(resource);\n            }\n            catch (final Exception exception)\n            {\n                throw new CoreException(\"Failed to load an atlas from {} with name {}\",\n                        resource.getClass().getName(), resource.getName(), exception);\n            }\n        }\n        return Optional.ofNullable(result);\n    }\n\n    private Optional<Atlas> loadMultipleAtlasResources(final List<Resource> atlasResources)\n    {\n        atlasResources.forEach(resource ->\n        {\n            if (resource instanceof File)\n            {\n                checkFileExistsAndIsNotDirectory(resource);\n            }\n            if (resource.length() == 0L)\n            {\n                throw new CoreException(\"{} {} had zero length!\", resource.getClass().getName(),\n                        resource.getName());\n            }\n        });\n\n        final List<Resource> binaryResources = filterForBinaryAtlasResources(atlasResources);\n        final List<Resource> textResources = filterForTextAtlasResources(atlasResources);\n\n        if (binaryResources.isEmpty() && textResources.isEmpty())\n        {\n            throw new CoreException(\"No loadable Resources were found.\");\n        }\n\n        /*\n         * There are three scenarios that must be handled. 1) There were only binary atlases. 2)\n         * There was a mix of binary and text atlases. 3) There were only text atlases.\n         */\n        MultiAtlas resultAtlas = null;\n        if (!binaryResources.isEmpty())\n        {\n            resultAtlas = MultiAtlas.loadFromPackedAtlas(binaryResources);\n        }\n        if (!textResources.isEmpty())\n        {\n            final List<Atlas> textAtlases = loadTextAtlases(textResources);\n            if (!textAtlases.isEmpty())\n            {\n                final MultiAtlas textMultiAtlas = new MultiAtlas(textAtlases);\n                /*\n                 * In this case, 'resultAtlas' is not null because there was a mix of binary and\n                 * text atlases.\n                 */\n                if (resultAtlas != null)\n                {\n                    resultAtlas = new MultiAtlas(resultAtlas, textMultiAtlas);\n                }\n                /*\n                 * For this case, there was no previous resultAtlas since no binary atlases were\n                 * found.\n                 */\n                else\n                {\n                    resultAtlas = textMultiAtlas;\n                }\n            }\n        }\n\n        if (this.multiAtlasName != null && resultAtlas != null)\n        {\n            resultAtlas.setName(this.multiAtlasName);\n        }\n        return Optional.ofNullable(resultAtlas);\n    }\n\n    private List<Atlas> loadTextAtlases(final List<Resource> textAtlasResources)\n    {\n        final List<Atlas> textAtlases = new ArrayList<>();\n        for (final Resource textResource : textAtlasResources)\n        {\n            setDecompressorFor(textResource);\n            final Atlas atlas = new TextAtlasBuilder().read(textResource);\n            textAtlases.add(atlas);\n        }\n        return textAtlases;\n    }\n\n    private List<Resource> upcastAndRemoveNullResources(final Resource resource)\n    {\n        final List<Resource> result = new ArrayList<>();\n        if (resource != null)\n        {\n            result.add(resource);\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/BareAtlas.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.text.NumberFormat;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.function.BiConsumer;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Snapper;\nimport org.openstreetmap.atlas.geography.Snapper.SnappedLocation;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.atlas.items.SnappedEdge;\nimport org.openstreetmap.atlas.geography.atlas.items.SnappedLineItem;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.atlas.sub.SubAtlasCreator;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonFeatureCollection;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.proto.builder.ProtoAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\nimport com.google.gson.JsonObject;\n\n/**\n * @author matthieun\n * @author tony\n * @author mgostintsev\n * @author hallahan\n */\npublic abstract class BareAtlas implements Atlas\n{\n    public static final int MAXIMUM_RELATION_DEPTH = 500;\n    private static final long serialVersionUID = 4733707438968864018L;\n    private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance();\n\n    static\n    {\n        NUMBER_FORMAT.setGroupingUsed(true);\n    }\n\n    // Transient name\n    private transient String name;\n    private final UUID identifier;\n\n    protected BareAtlas()\n    {\n        this.identifier = UUID.randomUUID();\n    }\n\n    @Override\n    public Iterable<Area> areas(final Predicate<Area> matcher)\n    {\n        return Iterables.filter(areas(), matcher);\n    }\n\n    @Override\n    public JsonObject asGeoJson()\n    {\n        return GeoJsonUtils.featureCollection(this);\n    }\n\n    @Override\n    public JsonObject asGeoJson(final Predicate<AtlasEntity> matcher)\n    {\n        return GeoJsonUtils.featureCollection(new GeoJsonFeatureCollection<AtlasEntity>()\n        {\n            @Override\n            public Iterable<AtlasEntity> getGeoJsonObjects()\n            {\n                return entities(matcher);\n            }\n\n            @Override\n            public JsonObject getGeoJsonProperties()\n            {\n                final JsonObject properties = BareAtlas.this.getGeoJsonProperties();\n                properties.addProperty(\"Entity filter used\", true);\n                return properties;\n            }\n        });\n    }\n\n    @Override\n    public Iterable<Edge> edges(final Predicate<Edge> matcher)\n    {\n        return Iterables.filter(edges(), matcher);\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entities()\n    {\n        return new MultiIterable<>(items(), relations());\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <M extends AtlasEntity> Iterable<M> entities(final ItemType type,\n            final Class<M> memberClass)\n    {\n        if (type.getMemberClass() != memberClass)\n        {\n            throw new CoreException(\"ItemType {} and class {} do not match!\", type,\n                    memberClass.getSimpleName());\n        }\n        switch (type)\n        {\n            case NODE:\n                return (Iterable<M>) nodes();\n            case EDGE:\n                return (Iterable<M>) edges();\n            case AREA:\n                return (Iterable<M>) areas();\n            case LINE:\n                return (Iterable<M>) lines();\n            case POINT:\n                return (Iterable<M>) points();\n            case RELATION:\n                return (Iterable<M>) relations();\n            default:\n                throw new CoreException(\"ItemType {} unknown.\", type);\n        }\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entities(final Predicate<AtlasEntity> matcher)\n    {\n        return Iterables.filter(this, matcher);\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entitiesIntersecting(final GeometricSurface surface)\n    {\n        return new MultiIterable<>(itemsIntersecting(surface),\n                relationsWithEntitiesIntersecting(surface));\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entitiesIntersecting(final GeometricSurface surface,\n            final Predicate<AtlasEntity> matcher)\n    {\n        return Iterables.filter(entitiesIntersecting(surface), matcher);\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entitiesWithin(final GeometricSurface surface)\n    {\n        return new MultiIterable<>(itemsWithin(surface), relationsWithEntitiesWithin(surface));\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entitiesWithin(final GeometricSurface surface,\n            final Predicate<AtlasEntity> matcher)\n    {\n        return Iterables.filter(entitiesWithin(surface), matcher);\n    }\n\n    @Override\n    public AtlasEntity entity(final long identifier, final ItemType type)\n    {\n        switch (type)\n        {\n            case NODE:\n                return node(identifier);\n            case EDGE:\n                return edge(identifier);\n            case AREA:\n                return area(identifier);\n            case LINE:\n                return line(identifier);\n            case POINT:\n                return point(identifier);\n            case RELATION:\n                return relation(identifier);\n            default:\n                throw new CoreException(\"Unknown type {}\", type);\n        }\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof Atlas)\n        {\n            if (this == other)\n            {\n                // Avoid comparing each item.\n                return true;\n            }\n            final Atlas that = (Atlas) other;\n            for (final AtlasEntity thisEntity : this)\n            {\n                final AtlasEntity thatEntity = that.entity(thisEntity.getIdentifier(),\n                        thisEntity.getType());\n                if (thatEntity == null || !thisEntity.getTags().equals(thatEntity.getTags()))\n                {\n                    return false;\n                }\n                if (thisEntity instanceof Area)\n                {\n                    final Polygon thisPolygon = ((Area) thisEntity).asPolygon();\n                    final Polygon thatPolygon = ((Area) thatEntity).asPolygon();\n                    if (!thisPolygon.equals(thatPolygon))\n                    {\n                        return false;\n                    }\n                }\n                else if (thisEntity instanceof LineItem)\n                {\n                    final PolyLine thisPolyLine = ((LineItem) thisEntity).asPolyLine();\n                    final PolyLine thatPolyLine = ((LineItem) thatEntity).asPolyLine();\n                    if (!thisPolyLine.equals(thatPolyLine))\n                    {\n                        return false;\n                    }\n                }\n                else if (thisEntity instanceof LocationItem)\n                {\n                    final Location thisLocation = ((LocationItem) thisEntity).getLocation();\n                    final Location thatLocation = ((LocationItem) thatEntity).getLocation();\n                    if (!thisLocation.equals(thatLocation))\n                    {\n                        return false;\n                    }\n                }\n                else if (thisEntity instanceof Relation)\n                {\n                    final RelationMemberList thisMembers = ((Relation) thisEntity).members();\n                    final RelationMemberList thatMembers = ((Relation) thatEntity).members();\n                    if (!thisMembers.equals(thatMembers))\n                    {\n                        return false;\n                    }\n                }\n                else\n                {\n                    throw new CoreException(\"Unknown type: {}\", thisEntity.getClass().getName());\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public Iterable<AtlasEntity> getGeoJsonObjects()\n    {\n        return entities();\n    }\n\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        final JsonObject properties = this.metaData().getGeoJsonProperties();\n        properties.addProperty(\"name\", this.getName());\n        return properties;\n    }\n\n    @Override\n    public UUID getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public String getName()\n    {\n        if (this.name == null)\n        {\n            return String.valueOf(this.getIdentifier());\n        }\n        else\n        {\n            return this.name;\n        }\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Long.hashCode(this.numberOfNodes() + this.numberOfEdges() + this.numberOfAreas()\n                + this.numberOfLines() + this.numberOfPoints() + this.numberOfRelations());\n    }\n\n    @Override\n    public Iterable<AtlasItem> items()\n    {\n        return new MultiIterable<>(nodes(), edges(), areas(), lines(), points());\n    }\n\n    @Override\n    public Iterable<AtlasItem> items(final Predicate<AtlasItem> matcher)\n    {\n        return Iterables.filter(items(), matcher);\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsContaining(final Location location)\n    {\n        return new MultiIterable<>(edgesContaining(location), nodesAt(location),\n                areasCovering(location), linesContaining(location), pointsAt(location));\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsContaining(final Location location,\n            final Predicate<AtlasItem> matcher)\n    {\n        return Iterables.filter(itemsContaining(location), matcher);\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsIntersecting(final GeometricSurface surface)\n    {\n        return new MultiIterable<>(edgesIntersecting(surface), nodesWithin(surface),\n                areasIntersecting(surface), linesIntersecting(surface), pointsWithin(surface));\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsIntersecting(final GeometricSurface surface,\n            final Predicate<AtlasItem> matcher)\n    {\n        return Iterables.filter(itemsIntersecting(surface), matcher);\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsWithin(final GeometricSurface surface)\n    {\n        return new MultiIterable<>(locationItemsWithin(surface), lineItemsWithin(surface),\n                areasWithin(surface));\n    }\n\n    @Override\n    public Iterator<AtlasEntity> iterator()\n    {\n        return new MultiIterable<AtlasEntity>(nodes(), edges(), areas(), lines(), points(),\n                relations()).iterator();\n    }\n\n    @Override\n    public Iterable<LineItem> lineItems()\n    {\n        return new MultiIterable<>(edges(), lines());\n    }\n\n    @Override\n    public Iterable<LineItem> lineItems(final Predicate<LineItem> matcher)\n    {\n        return Iterables.filter(lineItems(), matcher);\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsContaining(final Location location)\n    {\n        return new MultiIterable<>(edgesContaining(location), linesContaining(location));\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsContaining(final Location location,\n            final Predicate<LineItem> matcher)\n    {\n        return Iterables.filter(lineItemsContaining(location), matcher);\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsIntersecting(final GeometricSurface surface)\n    {\n        return new MultiIterable<>(edgesIntersecting(surface), linesIntersecting(surface));\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsIntersecting(final GeometricSurface surface,\n            final Predicate<LineItem> matcher)\n    {\n        return Iterables.filter(lineItemsIntersecting(surface), matcher);\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsWithin(final GeometricSurface surface)\n    {\n        return new MultiIterable<>(edgesWithin(surface), linesWithin(surface));\n    }\n\n    @Override\n    public Iterable<Line> lines(final Predicate<Line> matcher)\n    {\n        return Iterables.filter(lines(), matcher);\n    }\n\n    @Override\n    public Iterable<LocationItem> locationItems()\n    {\n        return new MultiIterable<>(nodes(), points());\n    }\n\n    @Override\n    public Iterable<LocationItem> locationItems(final Predicate<LocationItem> matcher)\n    {\n        return Iterables.filter(locationItems(), matcher);\n    }\n\n    @Override\n    public Iterable<LocationItem> locationItemsWithin(final GeometricSurface surface)\n    {\n        return new MultiIterable<>(nodesWithin(surface), pointsWithin(surface));\n    }\n\n    @Override\n    public Iterable<LocationItem> locationItemsWithin(final GeometricSurface surface,\n            final Predicate<LocationItem> matcher)\n    {\n        return Iterables.filter(locationItemsWithin(surface), matcher);\n    }\n\n    @Override\n    public Iterable<Node> nodes(final Predicate<Node> matcher)\n    {\n        return Iterables.filter(nodes(), matcher);\n    }\n\n    @Override\n    public Iterable<Point> points(final Predicate<Point> matcher)\n    {\n        return Iterables.filter(points(), matcher);\n    }\n\n    @Override\n    public Iterable<Relation> relations(final Predicate<Relation> matcher)\n    {\n        return Iterables.filter(relations(), matcher);\n    }\n\n    @Override\n    public Iterable<Relation> relationsLowerOrderFirst()\n    {\n        List<Relation> stagedRelations = new ArrayList<>();\n        final Set<Relation> result = new LinkedHashSet<>();\n        // First pass\n        for (final Relation relation : relations())\n        {\n            boolean stageable = false;\n            final RelationMemberList members = relation.members();\n            for (final RelationMember member : members)\n            {\n                if (member.getEntity() instanceof Relation)\n                {\n                    stageable = true;\n                }\n            }\n            if (stageable)\n            {\n                stagedRelations.add(relation);\n            }\n            else\n            {\n                result.add(relation);\n            }\n        }\n        // Second pass\n        int depth = 0;\n        while (!stagedRelations.isEmpty() && depth < MAXIMUM_RELATION_DEPTH)\n        {\n            final List<Relation> newStagedRelations = new ArrayList<>();\n            for (final Relation relation : stagedRelations)\n            {\n                boolean stageable = false;\n                final RelationMemberList members = relation.members();\n                for (final RelationMember member : members)\n                {\n                    if (member.getEntity() instanceof Relation\n                            && !result.contains(member.getEntity()))\n                    {\n                        stageable = true;\n                    }\n                }\n                if (stageable)\n                {\n                    newStagedRelations.add(relation);\n                }\n                else\n                {\n                    result.add(relation);\n                }\n            }\n            stagedRelations = newStagedRelations;\n            depth++;\n        }\n        return result;\n    }\n\n    @Override\n    public void saveAsGeoJson(final WritableResource resource)\n    {\n        saveAsGeoJson(resource, item -> true);\n    }\n\n    @Override\n    public void saveAsGeoJson(final WritableResource resource, final Predicate<AtlasEntity> matcher)\n    {\n        try (JsonWriter writer = new JsonWriter(resource))\n        {\n            writer.write(this.asGeoJson(matcher));\n        }\n    }\n\n    @Override\n    public void saveAsLineDelimitedGeoJsonFeatures(final WritableResource resource,\n            final BiConsumer<AtlasEntity, JsonObject> jsonMutator)\n    {\n        saveAsLineDelimitedGeoJsonFeatures(resource, item -> true, jsonMutator);\n    }\n\n    @Override\n    public void saveAsLineDelimitedGeoJsonFeatures(final WritableResource resource,\n            final Predicate<AtlasEntity> matcher,\n            final BiConsumer<AtlasEntity, JsonObject> jsonMutator)\n    {\n        try (JsonWriter writer = new JsonWriter(resource))\n        {\n            entities(matcher).forEach(entity ->\n            {\n                final JsonObject feature = entity.asGeoJson();\n                jsonMutator.accept(entity, feature);\n                writer.writeLine(feature);\n            });\n        }\n    }\n\n    @Override\n    public void saveAsList(final WritableResource resource)\n    {\n        final BufferedWriter writer = new BufferedWriter(\n                new OutputStreamWriter(resource.write(), StandardCharsets.UTF_8));\n        try\n        {\n            writer.write(this.toString());\n            Streams.close(writer);\n        }\n        catch (final IOException e)\n        {\n            Streams.close(writer);\n            throw new CoreException(\"Could not save atlas as list\", e);\n        }\n    }\n\n    @Override\n    public void saveAsProto(final WritableResource resource)\n    {\n        new ProtoAtlasBuilder().write(this, resource);\n    }\n\n    @Override\n    public void saveAsText(final WritableResource resource)\n    {\n        new TextAtlasBuilder().write(this, resource);\n    }\n\n    @Override\n    public SnappedEdge snapped(final Location point, final Distance threshold)\n    {\n        SnappedEdge result = null;\n        for (final Edge edge : this.edgesIntersecting(point.boxAround(threshold)))\n        {\n            final SnappedEdge candidate = new SnappedEdge(point.snapTo(edge.asPolyLine()), edge);\n            if (result == null || candidate.getDistance().isLessThan(result.getDistance()))\n            {\n                result = candidate;\n            }\n        }\n        return result;\n    }\n\n    @Override\n    public List<SnappedEdge> snaps(final Location point, final Distance threshold)\n    {\n        final List<SnappedEdge> snaps = new ArrayList<>();\n        for (final Edge edge : this.edgesIntersecting(point.boxAround(threshold)))\n        {\n            final SnappedEdge candidate = new SnappedEdge(point.snapTo(edge.asPolyLine()), edge);\n            snaps.add(candidate);\n        }\n        snaps.sort(SnappedLocation::compareTo);\n        return snaps;\n    }\n\n    @Override\n    public List<SnappedLineItem> snapsLineItem(final Location point, final Distance threshold)\n    {\n        final List<SnappedLineItem> snaps = new ArrayList<>();\n        for (final LineItem lineItem : this.lineItemsIntersecting(point.boxAround(threshold)))\n        {\n            final SnappedLineItem candidate = new SnappedLineItem(\n                    point.snapTo(lineItem.asPolyLine()), lineItem);\n            snaps.add(candidate);\n        }\n        snaps.sort(Snapper.SnappedLocation::compareTo);\n        return snaps;\n    }\n\n    @Override\n    public Optional<Atlas> subAtlas(final GeometricSurface boundary, final AtlasCutType cutType)\n    {\n        switch (cutType)\n        {\n            case SILK_CUT:\n                return SubAtlasCreator.silkCut(this, boundary);\n            case SOFT_CUT:\n                return SubAtlasCreator.softCut(this, boundary, false);\n            case HARD_CUT_ALL:\n                return SubAtlasCreator.hardCutAllEntities(this, boundary);\n            case HARD_CUT_RELATIONS_ONLY:\n                return SubAtlasCreator.softCut(this, boundary, true);\n            default:\n                throw new CoreException(\"Unsupported Atlas cut type: {}\", cutType);\n        }\n    }\n\n    @Override\n    public Optional<Atlas> subAtlas(final Predicate<AtlasEntity> matcher,\n            final AtlasCutType cutType)\n    {\n        switch (cutType)\n        {\n            case SILK_CUT:\n                return SubAtlasCreator.silkCut(this, matcher);\n            case SOFT_CUT:\n                return SubAtlasCreator.softCut(this, matcher);\n            case HARD_CUT_ALL:\n                return SubAtlasCreator.hardCutAllEntities(this, matcher);\n            case HARD_CUT_RELATIONS_ONLY:\n                return SubAtlasCreator.hardCutRelationsOnly(this, matcher);\n            default:\n                throw new CoreException(\"Unsupported Atlas cut type: {}\", cutType);\n        }\n    }\n\n    @Override\n    public String summary()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[\");\n        builder.append(this.getClass().getSimpleName());\n        builder.append(\": Nodes = \");\n        builder.append(NUMBER_FORMAT.format(this.numberOfNodes()));\n        builder.append(\", Edges = \");\n        builder.append(NUMBER_FORMAT.format(this.numberOfEdges()));\n        builder.append(\", Areas = \");\n        builder.append(NUMBER_FORMAT.format(this.numberOfAreas()));\n        builder.append(\", Lines = \");\n        builder.append(NUMBER_FORMAT.format(this.numberOfLines()));\n        builder.append(\", Points = \");\n        builder.append(NUMBER_FORMAT.format(this.numberOfPoints()));\n        builder.append(\", Relations = \");\n        builder.append(NUMBER_FORMAT.format(this.numberOfRelations()));\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    @Override\n    public String toString()\n    {\n        return summary();\n    }\n\n    @Override\n    public String toStringDetailed()\n    {\n        final String newLineAfterFeature = \",\\n\\t\\t\";\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[Atlas <\");\n        builder.append(getName());\n        builder.append(\">: \");\n        final StringList list = new StringList();\n        list.add(Iterables.toString(this.nodes(), \"Nodes\", newLineAfterFeature));\n        list.add(Iterables.toString(this.edges(), \"Edges\", newLineAfterFeature));\n        list.add(Iterables.toString(this.areas(), \"Areas\", newLineAfterFeature));\n        list.add(Iterables.toString(this.lines(), \"Lines\", newLineAfterFeature));\n        list.add(Iterables.toString(this.points(), \"Points\", newLineAfterFeature));\n        list.add(Iterables.toString(this.relations(), \"Relations\", newLineAfterFeature));\n        builder.append(list.join(\",\\n\\t\"));\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    protected void setName(final String name)\n    {\n        this.name = name;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/Crawler.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\n\n/**\n * Crawl map data, to flag issues for example.\n *\n * @author matthieun\n * @author mgostintsev\n */\npublic abstract class Crawler extends AtlasLoadingCommand\n{\n    private static final Switch<File> OUTPUT_FOLDER = new Switch<>(\"outputFolder\",\n            \"Location of the output folder\", File::new, Optionality.REQUIRED);\n\n    private final Logger logger;\n\n    public Crawler(final Logger logger)\n    {\n        this.logger = logger;\n    }\n\n    protected void initialize(final CommandMap command)\n    {\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final File inputFolder = (File) command.get(INPUT_FOLDER);\n        final String atlasName = inputFolder.getName();\n        final File outputFolder = (File) command.get(OUTPUT_FOLDER);\n        initialize(command);\n        if (inputFolder != null)\n        {\n            this.logger.info(\"Loading Atlas from {}\", inputFolder);\n            final Atlas atlas = loadAtlas(command);\n            processAtlas(atlasName, atlas, outputFolder.getPathString());\n        }\n        return 0;\n    }\n\n    protected abstract void processAtlas(String atlasName, Atlas atlas, String folder);\n\n    @Override\n    protected SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_FOLDER);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/README.md",
    "content": "# Using Atlas\n\n* Grab one or more Atlas files (from [here](https://www.dropbox.com/sh/54aqfbs12suqd9t/AACGxhjCaJiRcJUBuFh0iLiHa) or after [building them yourself](src/main/java/org/openstreetmap/atlas/geography/atlas/README.md#building-an-atlas-from-an-osmpbf-file)), and open them with [`AtlasResourceLoader`](src/main/java/org/openstreetmap/atlas/geography/atlas/AtlasResourceLoader.java):\n```\nfinal File atlasFile = new File(\"/path/to/source.atlas\");\nfinal Atlas atlas = new AtlasResourceLoader().load(atlasFile);\n```\nQuery the data. Examples:\n\n* Get all edges within boundaries:\n```\nRectangle rectangle = ...;\natlas.edgesIntersecting(rectangle).forEach(edge -> ...);\n```\n* Find all the parks with less than 6 shape points:\n```\nPredicate<Area> filter = area -> {\n    return Validators.isOfType(area, LeisureTag.class, LeisureTag.PARK)\n        && area.asPolygon().size() < 6;\n}\natlas.areas(filter).forEach(area -> ...);\n```\nor\n```\nPredicate<Area> filter = area -> {\n    return \"park\".equals(area.getTags().get(\"leisure\"))\n        && area.asPolygon().size() < 6;\n}\natlas.areas(filter).forEach(area -> ...);\n```\n* Find all buildings with a hole:\n```\nComplexBuildingFinder finder = new ComplexBuildingFinder();\nIterables.stream(finder.find(atlas))\n    .filter(complexBuilding -> !complexBuilding.getOutline().inners().isEmpty())\n    .forEach(complexBuilding -> ...);\n```\n* How many `Edge`s are connected to a `Node`:\n```\nlong identifier = 123;\nint numberOfConnectedEdges = atlas.nodeForIdentifier(identifier).connectedEdges().size();\n```\nor\n```\nlong identifier = 123;\nint numberOfConnectedEdges = atlas.nodeForIdentifier(identifier).absoluteValence();\n```\n\n# Building an `Atlas` from an `.osm.pbf` file\n\nBuilding an `Atlas` from an `.osm.pbf` file involves multiple steps, described below. First create a \"Raw Atlas\" that is a simple copy of all the items in the PBF file into the Atlas format. Then (optionally) apply country slicing, and finally call the way-sectioning algorithm to create the \"navigable network\" part of the Atlas.\n\nWithout country slicing:\n\n```\nfinal File pbfFile;\n\nfinal Atlas rawAtlas = new RawAtlasGenerator(pbfFile).build();\nfinal Atlas atlas = new WaySectionProcessor(rawAtlas, AtlasLoadingOption.createOptionWithAllEnabled()).run();\n```\n\nWith country slicing:\n\n```\nfinal File pbfFile;\nfinal Set<String> countries;\nfinal CountryBoundaryMap boundaries;\n        \nfinal Atlas rawAtlas = new RawAtlasGenerator(pbfFile).build();\nfinal Atlas slicedRawAtlas = new RawAtlasCountrySlicer(countries, boundaries).slice(rawAtlas);\nfinal Atlas atlas = new WaySectionProcessor(slicedRawAtlas, AtlasLoadingOption.createOptionWithAllEnabled(boundaries)).run();\n```\n\n## Way Sectioning\n\nOSM ways usually span multiple intersections in the case of roads. To make the road network a navigable network, the process of loading an `.osm.pbf` file runs \"way sectioning\". It will follow a set of rules to break ways at intersections, and create `Atlas` `Edge`s. For that, it pads the OSM feature identifiers with six digits starting from 1 to the number of sections. For example, way 123 would become `Edge`s 123000001, 123000002 and 123000003 if it has to be broken twice. If no sectioning takes place, the edge identifier would end in `000`.\n\n## Country Slicing\n\nIn case of building an Atlas that is hard-cut along a polygon (usually a country boundary with boundary=administrative and admin_level=2), the process of loading an `.osm.pbf` file runs \"country slicing\". All the features that span outside of the boundary will be cut at the boundary and excluded. All the features that are inside will be assigned a country code tag if a country code is given with the AtlasLoadingOption.\n\nThe `AtlasLoadingOption` contains all the country boundaries, along with the country codes in a `CountryBoundaryMap`.\n\nAll feature identifiers will be padded and the first 3 digits of the 6 digit padding (described above) will be a country counter. If a `Line` 123 spans 2 countries, gets out and comes back for example, it will ship with 123001000 and 123001002 within the first country, and 123002001 in the country where it spans out (in a separate `Atlas`).\n\n## Configuration\n\nWay-sectioning logic, edge definition and which pbf entities (Way, Node, Relation) are brought into an `Atlas` are all configurable. The default configurations can be found in the main resources directory as json files (see atlas-way-section.json for an example of the way section configuration). These configurations are initialized and can be set in `AtlasLoadingOption`.\n\n# Building an `Atlas` from scratch\n\nThe `PackedAtlasBuilder` is here for that. It ensures that all the data that makes its way to an Atlas is consistent (for example making sure that if an `Edge` says its start `Node` is 123, then `Node` 123 really exists) and that an Atlas is final and cannot be modified once it has been accessed once.\n\n* First add all the `Node`s\n* Then add all the `Edge`s, `Area`s, `Line`s and `Point`s in any order.\n* Finally add all the `Relation`s from the lowest order (no other `Relation` is within its members) to the higher order (other `Relation`s are within its members). The `PackedAtlasBuilder` will throw an exception if a `Relation` is added and any of the listed members have not already been added.\n\n# Saving an `Atlas`\n\nThe `Atlas` API offers a `save(WritableResource)` method, that is implemented by `PackedAtlas`. Trying to save a `MultiAtlas` will result in an exception suggesting to copy the `Atlas` to a `PackedAtlas` first.\n\n# Copying an `Atlas`\n\nFrom any `Atlas`, a `PackedAtlas` can be created and saved to a `WritableResource` (A File for example). This is done with the `PackedAtlasCloner`:\n\n```\nfinal Atlas atlas1;\nfinal Atlas atlas2;\nnew PackedAtlasCloner().cloneFrom(new MultiAtlas(atlas1, atlas2)).save(new File(\"/path/to/file.atlas\"));\n```\n\n# Filtering an `Atlas`\n\nAtlas objects can be soft-filtered based on a `Predicate` or a `Polygon`.\n\n```\nfinal Atlas atlas;\n\nfinal Predicate<AtlasEntity> predicate;\nfinal Atlas predicateAtlas = atlas.subAtlas(predicate, AtlasCutType.SOFT_CUT);\n\nfinal Polygon polygon;\nfinal Atlas polygonAtlas = atlas.subAtlas(polygon, AtlasCutType.SOFT_CUT);\n```\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/ShardFileOverlapsPolygon.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.sharding.DynamicTileSharding;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Predicate that uses a sharding tree to determine whether a given atlas shard file overlaps a\n * given Polygon. By default it depends on shard files following the naming convention of\n * [name]_[zoom]-[x]-[y].atlas.gz, where the .gz extension is optional. For example,\n * XYZ_9-272-162.atlas.gz and XYZ_9-272-162.atlas are valid name formats. The shard filename pattern\n * can be overridden to work with other naming conventions as long as the [zoom]-[x]-[y] portion of\n * the name still exists as the first group in the pattern.\n *\n * @author rmegraw\n */\npublic class ShardFileOverlapsPolygon implements Predicate<Resource>\n{\n    private static final Logger logger = LoggerFactory.getLogger(ShardFileOverlapsPolygon.class);\n\n    /**\n     * Matches shard filenames such as XYZ_9-272-162.atlas.gz and XYZ_9-272-162.atlas\n     */\n    public static final String DEFAULT_SHARD_FILE_REGEX = \"^.+_(\\\\d{1,2}-\\\\d+-\\\\d+)\\\\.atlas(\\\\.gz)?$\";\n\n    private final Pattern shardFilePattern;\n\n    private final Set<String> shardsOverlappingPolygon;\n\n    /**\n     * @param shardingTree\n     *            Sharding tree\n     * @param bounds\n     *            Polygon over which shard file overlap is tested\n     */\n    public ShardFileOverlapsPolygon(final DynamicTileSharding shardingTree, final Polygon bounds)\n    {\n        this(shardingTree, bounds, DEFAULT_SHARD_FILE_REGEX);\n    }\n\n    /**\n     * @param shardingTree\n     *            Sharding tree\n     * @param bounds\n     *            Polygon over which shard file overlap is tested\n     * @param shardFileRegex\n     *            Regex which must extract [zoom]-[x]-[y] portion of shard filename as the first\n     *            group (see default regex for example)\n     */\n    public ShardFileOverlapsPolygon(final DynamicTileSharding shardingTree, final Polygon bounds,\n            final String shardFileRegex)\n    {\n        this.shardFilePattern = Pattern.compile(shardFileRegex);\n        this.shardsOverlappingPolygon = new HashSet<>();\n        shardingTree.shards(bounds)\n                .forEach(shard -> this.shardsOverlappingPolygon.add(shard.getName()));\n    }\n\n    @Override\n    public boolean test(final Resource resource)\n    {\n        boolean result = false;\n        final String resourceName = resource.getName();\n        if (resourceName != null)\n        {\n            final Matcher matcher = this.shardFilePattern.matcher(resourceName);\n            if (matcher.find())\n            {\n                final String shardName = matcher.group(1);\n                if (this.shardsOverlappingPolygon.contains(shardName))\n                {\n                    logger.debug(\"Resource {} overlaps polygon.\", resourceName);\n                    result = true;\n                }\n                else\n                {\n                    logger.debug(\"Resource {} does not overlap polygon.\", resourceName);\n                }\n            }\n            else\n            {\n                logger.debug(\"Resource {} does not match shard filename pattern.\", resourceName);\n            }\n        }\n        else\n        {\n            logger.debug(\"Resource {} name is null.\", resource.toString());\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/AtlasBuilder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Build an {@link Atlas} from {@link Node} and {@link Edge} data.\n *\n * @author matthieun\n */\npublic interface AtlasBuilder\n{\n    /**\n     * Add a {@link Area} to the {@link Atlas}.\n     *\n     * @param identifier\n     *            The {@link Area}'s identifier.\n     * @param geometry\n     *            The geometry of the {@link Area}\n     * @param tags\n     *            An arbitrary set of OSM key-value pairs that are attached to this {@link Area}\n     * @throws IllegalAccessError\n     *             In case the {@link Atlas} has already been generated and this builder is locked.\n     */\n    void addArea(long identifier, Polygon geometry, Map<String, String> tags);\n\n    /**\n     * Add an {@link Edge} to the {@link Atlas}. Its start and end {@link Node} should have been\n     * added already when this is called.\n     *\n     * @param identifier\n     *            The {@link Edge}'s identifier.\n     * @param geometry\n     *            The geometry of the {@link Edge}\n     * @param tags\n     *            An arbitrary set of OSM key-value pairs that are attached to this {@link Edge}\n     * @throws IllegalAccessError\n     *             In case the {@link Atlas} has already been generated and this builder is locked.\n     */\n    void addEdge(long identifier, PolyLine geometry, Map<String, String> tags);\n\n    /**\n     * Add a {@link Line} to the {@link Atlas}.\n     *\n     * @param identifier\n     *            The {@link Line}'s identifier.\n     * @param geometry\n     *            The geometry of the {@link Line}\n     * @param tags\n     *            An arbitrary set of OSM key-value pairs that are attached to this {@link Line}\n     * @throws IllegalAccessError\n     *             In case the {@link Atlas} has already been generated and this builder is locked.\n     */\n    void addLine(long identifier, PolyLine geometry, Map<String, String> tags);\n\n    /**\n     * Add a {@link Node} to the {@link Atlas}\n     *\n     * @param identifier\n     *            The {@link Node}'s identifier\n     * @param geometry\n     *            The {@link Node}'s {@link Location}\n     * @param tags\n     *            An arbitrary set of OSM key-value pairs that are attached to this {@link Node}\n     * @throws IllegalAccessError\n     *             In case the {@link Atlas} has already been generated and this builder is locked.\n     */\n    void addNode(long identifier, Location geometry, Map<String, String> tags);\n\n    /**\n     * Add a {@link Point} to the {@link Atlas}.\n     *\n     * @param identifier\n     *            The {@link Point}'s identifier.\n     * @param geometry\n     *            The geometry of the {@link Point}\n     * @param tags\n     *            An arbitrary set of OSM key-value pairs that are attached to this {@link Point}\n     * @throws IllegalAccessError\n     *             In case the {@link Atlas} has already been generated and this builder is locked.\n     */\n    void addPoint(long identifier, Location geometry, Map<String, String> tags);\n\n    /**\n     * Add a {@link Relation} to the {@link Atlas}.\n     *\n     * @param identifier\n     *            The {@link Relation}'s identifier.\n     * @param osmIdentifier\n     *            The {@link Relation}'s OSM identifier for split relations. The same identifier\n     *            otherwise.\n     * @param structure\n     *            The structure of the {@link Relation}. This cannot be empty!\n     * @param tags\n     *            An arbitrary set of OSM key-value pairs that are attached to this {@link Relation}\n     * @throws IllegalAccessError\n     *             In case the {@link Atlas} has already been generated and this builder is locked.\n     */\n    void addRelation(long identifier, long osmIdentifier, RelationBean structure,\n            Map<String, String> tags);\n\n    /**\n     * @return The {@link Atlas} comprising all the {@link Edge}s, {@link Node}s, {@link Area}s,\n     *         {@link Line}s, and {@link Point}s added using this builder. Once this is called, the\n     *         addEdge or addNode methods should throw an exception.\n     */\n    Atlas get();\n\n    /**\n     * Give the meta data of the {@link Atlas} to be created.\n     *\n     * @param metaData\n     *            The meta data\n     */\n    void setMetaData(AtlasMetaData metaData);\n\n    /**\n     * Give an estimate of the size of the Atlas.\n     *\n     * @param estimates\n     *            The estimates of the size of the Atlas\n     */\n    void setSizeEstimates(AtlasSize estimates);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/AtlasSize.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonProperties;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Size estimates for an {@link AtlasBuilder}\n *\n * @author matthieun\n */\npublic class AtlasSize implements Serializable, GeoJsonProperties\n{\n    /**\n     * A simple builder class for creating {@link AtlasSize} objects with custom sizes.\n     *\n     * @author lcram\n     */\n    public static class AtlasSizeBuilder\n    {\n        private long edgeEstimate;\n        private long nodeEstimate;\n        private long areaEstimate;\n        private long lineEstimate;\n        private long pointEstimate;\n        private long relationEstimate;\n\n        public AtlasSizeBuilder()\n        {\n            this.edgeEstimate = DEFAULT_ESTIMATE;\n            this.nodeEstimate = DEFAULT_ESTIMATE;\n            this.areaEstimate = DEFAULT_ESTIMATE;\n            this.lineEstimate = DEFAULT_ESTIMATE;\n            this.pointEstimate = DEFAULT_ESTIMATE;\n            this.relationEstimate = DEFAULT_ESTIMATE;\n        }\n\n        /**\n         * Builds an {@link AtlasSize}. By default it uses {@link AtlasSize#DEFAULT_ESTIMATE} for\n         * the size estimates.\n         *\n         * @return A new {@link AtlasSize}\n         */\n        public AtlasSize build()\n        {\n            return new AtlasSize(this.edgeEstimate, this.nodeEstimate, this.areaEstimate,\n                    this.lineEstimate, this.pointEstimate, this.relationEstimate);\n        }\n\n        public AtlasSizeBuilder withAreaEstimate(final long areaNumber)\n        {\n            this.areaEstimate = areaNumber;\n            return this;\n        }\n\n        public AtlasSizeBuilder withEdgeEstimate(final long edgeNumber)\n        {\n            this.edgeEstimate = edgeNumber;\n            return this;\n        }\n\n        public AtlasSizeBuilder withLineEstimate(final long lineNumber)\n        {\n            this.lineEstimate = lineNumber;\n            return this;\n        }\n\n        public AtlasSizeBuilder withNodeEstimate(final long nodeNumber)\n        {\n            this.nodeEstimate = nodeNumber;\n            return this;\n        }\n\n        public AtlasSizeBuilder withPointEstimate(final long pointNumber)\n        {\n            this.pointEstimate = pointNumber;\n            return this;\n        }\n\n        public AtlasSizeBuilder withRelationEstimate(final long relationNumber)\n        {\n            this.relationEstimate = relationNumber;\n            return this;\n        }\n    }\n\n    private static final long serialVersionUID = -4365680097735345765L;\n    private static final long DEFAULT_ESTIMATE = 1024L;\n\n    public static final AtlasSize DEFAULT = new AtlasSize(DEFAULT_ESTIMATE, DEFAULT_ESTIMATE,\n            DEFAULT_ESTIMATE, DEFAULT_ESTIMATE, DEFAULT_ESTIMATE, DEFAULT_ESTIMATE);\n\n    private final long edgeNumber;\n    private final long nodeNumber;\n    private final long areaNumber;\n    private final long lineNumber;\n    private final long pointNumber;\n    private final long relationNumber;\n\n    /**\n     * Constructor that calculates the number of occurrences for each {@link AtlasEntity}.\n     *\n     * @param entities\n     *            The {@link AtlasEntity}s to use for generating an {@link AtlasSize}\n     */\n    public AtlasSize(final Iterable<AtlasEntity> entities)\n    {\n        long nodeNumber = 0L;\n        long edgeNumber = 0L;\n        long areaNumber = 0L;\n        long lineNumber = 0L;\n        long pointNumber = 0L;\n        long relationNumber = 0L;\n\n        final Iterator<AtlasEntity> entityIterator = entities.iterator();\n        while (entityIterator.hasNext())\n        {\n            final AtlasEntity entity = entityIterator.next();\n            final ItemType type = entity.getType();\n            switch (type)\n            {\n                case NODE:\n                    nodeNumber++;\n                    break;\n                case EDGE:\n                    edgeNumber++;\n                    break;\n                case AREA:\n                    areaNumber++;\n                    break;\n                case LINE:\n                    lineNumber++;\n                    break;\n                case POINT:\n                    pointNumber++;\n                    break;\n                case RELATION:\n                    relationNumber++;\n                    break;\n                default:\n                    throw new CoreException(\"Invalid Item Type {}\", type);\n            }\n        }\n\n        this.edgeNumber = edgeNumber;\n        this.nodeNumber = nodeNumber;\n        this.areaNumber = areaNumber;\n        this.lineNumber = lineNumber;\n        this.pointNumber = pointNumber;\n        this.relationNumber = relationNumber;\n    }\n\n    /**\n     * Default constructor that takes explicit number of occurrences of each {@link AtlasEntity}.\n     *\n     * @param edgeNumber\n     *            Number of {@link Edge}s\n     * @param nodeNumber\n     *            Number of {@link Node}s\n     * @param areaNumber\n     *            Number of {@link Area}s\n     * @param lineNumber\n     *            Number of {@link Line}s\n     * @param pointNumber\n     *            Number of {@link Point}s\n     * @param relationNumber\n     *            Number of {@link Relation}s\n     */\n    public AtlasSize(final long edgeNumber, final long nodeNumber, final long areaNumber,\n            final long lineNumber, final long pointNumber, final long relationNumber)\n    {\n        this.edgeNumber = edgeNumber;\n        this.nodeNumber = nodeNumber;\n        this.areaNumber = areaNumber;\n        this.lineNumber = lineNumber;\n        this.pointNumber = pointNumber;\n        this.relationNumber = relationNumber;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof AtlasSize)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            final AtlasSize that = (AtlasSize) other;\n            if (this.getEdgeNumber() != that.getEdgeNumber())\n            {\n                return false;\n            }\n            if (this.getNodeNumber() != that.getNodeNumber())\n            {\n                return false;\n            }\n            if (this.getAreaNumber() != that.getAreaNumber())\n            {\n                return false;\n            }\n            if (this.getLineNumber() != that.getLineNumber())\n            {\n                return false;\n            }\n            if (this.getPointNumber() != that.getPointNumber())\n            {\n                return false;\n            }\n            if (this.getRelationNumber() != that.getRelationNumber())\n            {\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    public long getAreaNumber()\n    {\n        return this.areaNumber;\n    }\n\n    public long getEdgeNumber()\n    {\n        return this.edgeNumber;\n    }\n\n    public long getEntityNumber()\n    {\n        return this.nodeNumber + this.edgeNumber + this.pointNumber + this.lineNumber\n                + this.areaNumber + this.relationNumber;\n    }\n\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        final JsonObject properties = new JsonObject();\n        properties.addProperty(\"Number of Edges\", this.getEdgeNumber());\n        properties.addProperty(\"Number of Nodes\", this.getNodeNumber());\n        properties.addProperty(\"Number of Areas\", this.getAreaNumber());\n        properties.addProperty(\"Number of Lines\", this.getLineNumber());\n        properties.addProperty(\"Number of Points\", this.getPointNumber());\n        properties.addProperty(\"Number of Relations\", this.getRelationNumber());\n        return properties;\n    }\n\n    public long getLineNumber()\n    {\n        return this.lineNumber;\n    }\n\n    public long getNodeNumber()\n    {\n        return this.nodeNumber;\n    }\n\n    public long getNonRelationEntityNumber()\n    {\n        return this.nodeNumber + this.edgeNumber + this.pointNumber + this.lineNumber\n                + this.areaNumber;\n    }\n\n    public long getPointNumber()\n    {\n        return this.pointNumber;\n    }\n\n    public long getRelationNumber()\n    {\n        return this.relationNumber;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(Long.valueOf(this.edgeNumber), Long.valueOf(this.nodeNumber),\n                Long.valueOf(this.areaNumber), Long.valueOf(this.lineNumber),\n                Long.valueOf(this.pointNumber), Long.valueOf(this.relationNumber));\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[AtlasSize: edgeNumber=\" + this.edgeNumber + \", nodeNumber=\" + this.nodeNumber\n                + \", areaNumber=\" + this.areaNumber + \", lineNumber=\" + this.lineNumber\n                + \", pointNumber=\" + this.pointNumber + \", relationNumber=\" + this.relationNumber\n                + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/GeoJsonAtlasBuilder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.readers.GeoJsonReader;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.tags.oneway.OneWayTag;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport com.google.gson.JsonElement;\n\n/**\n * Create an Atlas from an non-way-sectioned overpass-turbo GeoJson resource.\n *\n * @author matthieun\n */\npublic class GeoJsonAtlasBuilder\n{\n    /**\n     * @author matthieun\n     */\n    private static class GeoJsonEdge\n    {\n        private final long identifier;\n        private final Map<String, String> tags;\n        private final PolyLine polyLine;\n\n        GeoJsonEdge(final long identifier, final Map<String, String> tags, final PolyLine polyLine)\n        {\n            this.identifier = identifier;\n            this.tags = tags;\n            this.polyLine = polyLine;\n        }\n\n        public long getIdentifier()\n        {\n            return this.identifier;\n        }\n\n        public PolyLine getPolyLine()\n        {\n            return this.polyLine;\n        }\n\n        public Map<String, String> getTags()\n        {\n            return this.tags;\n        }\n    }\n\n    public Atlas create(final Resource geoJson)\n    {\n        GeoJsonReader reader = new GeoJsonReader(geoJson);\n        final AtlasBuilder builder = new PackedAtlasBuilder();\n        final List<GeoJsonEdge> edges = new ArrayList<>();\n        long nodeIdentifier = 0L;\n        reader.forEachRemaining(item ->\n        {\n            if (item.getItem() instanceof PolyLine && !(item.getItem() instanceof Polygon))\n            {\n                // We have an edge\n                Long identifier = null;\n                final Set<Map.Entry<String, JsonElement>> jsonTags = item.getProperties()\n                        .entrySet();\n                final Map<String, String> tags = new HashMap<>();\n                for (final Map.Entry<String, JsonElement> entry : jsonTags)\n                {\n                    final String key = entry.getKey();\n                    final String value = entry.getValue().getAsString();\n                    if (key.contains(\"@id\"))\n                    {\n                        identifier = Long.valueOf(StringList.split(value, \"/\").get(1));\n                    }\n                    else\n                    {\n                        tags.put(key, value);\n                    }\n                }\n                if (!tags.containsKey(\"highway\"))\n                {\n                    // it was not an edge after all\n                    return;\n                }\n                edges.add(new GeoJsonEdge(identifier, tags, (PolyLine) item.getItem()));\n            }\n        });\n        final Set<Location> locations = new HashSet<>();\n        for (final GeoJsonEdge edge : edges)\n        {\n            locations.add(edge.getPolyLine().first());\n            locations.add(edge.getPolyLine().last());\n        }\n        for (final Location location : locations)\n        {\n            // Node\n            builder.addNode(nodeIdentifier++, location, new HashMap<String, String>());\n        }\n        for (final GeoJsonEdge edge : edges)\n        {\n            // Edge\n\n            if (edge.getTags().containsKey(OneWayTag.KEY)\n                    && !OneWayTag.NO.name().equalsIgnoreCase(edge.getTags().get(OneWayTag.KEY)))\n            {\n                final String onewayTag = edge.getTags().get(OneWayTag.KEY);\n                if (OneWayTag.YES.name().equalsIgnoreCase(onewayTag) || \"1\".equals(onewayTag))\n                {\n                    builder.addEdge(edge.getIdentifier(), edge.getPolyLine(), edge.getTags());\n                }\n                else if (\"-1\".equals(onewayTag))\n                {\n                    builder.addEdge(edge.getIdentifier(), edge.getPolyLine().reversed(),\n                            edge.getTags());\n                }\n            }\n            else\n            {\n                builder.addEdge(edge.getIdentifier(), edge.getPolyLine(), edge.getTags());\n                builder.addEdge(-edge.getIdentifier(), edge.getPolyLine().reversed(),\n                        edge.getTags());\n            }\n        }\n        reader = new GeoJsonReader(geoJson);\n        reader.forEachRemaining(item ->\n        {\n            if (item.getItem() instanceof Polygon)\n            {\n                // Area\n                Long identifier = null;\n                final Set<Map.Entry<String, JsonElement>> jsonTags = item.getProperties()\n                        .entrySet();\n                final Map<String, String> tags = new HashMap<>();\n                for (final Map.Entry<String, JsonElement> entry : jsonTags)\n                {\n                    final String key = entry.getKey();\n                    final String value = entry.getValue().getAsString();\n                    if (key.contains(\"@id\"))\n                    {\n                        identifier = Long.valueOf(StringList.split(value, \"/\").get(1));\n                    }\n                    else\n                    {\n                        tags.put(key, value);\n                    }\n                }\n                builder.addArea(identifier, (Polygon) item.getItem(), tags);\n            }\n            if (item.getItem() instanceof PolyLine && !(item.getItem() instanceof Polygon))\n            {\n                // Line\n                Long identifier = null;\n                final Set<Map.Entry<String, JsonElement>> jsonTags = item.getProperties()\n                        .entrySet();\n                final Map<String, String> tags = new HashMap<>();\n                for (final Map.Entry<String, JsonElement> entry : jsonTags)\n                {\n                    final String key = entry.getKey();\n                    final String value = entry.getValue().getAsString();\n                    if (key.contains(\"@id\"))\n                    {\n                        identifier = Long.valueOf(StringList.split(value, \"/\").get(1));\n                    }\n                    else\n                    {\n                        tags.put(key, value);\n                    }\n                }\n                if (tags.containsKey(\"highway\"))\n                {\n                    // it was an edge after all\n                    return;\n                }\n                builder.addLine(identifier, (PolyLine) item.getItem(), tags);\n            }\n            if (item.getItem() instanceof Location)\n            {\n                // Area\n                Long identifier = null;\n                final Set<Map.Entry<String, JsonElement>> jsonTags = item.getProperties()\n                        .entrySet();\n                final Map<String, String> tags = new HashMap<>();\n                for (final Map.Entry<String, JsonElement> entry : jsonTags)\n                {\n                    final String key = entry.getKey();\n                    final String value = entry.getValue().getAsString();\n                    if (key.contains(\"@id\"))\n                    {\n                        identifier = Long.valueOf(StringList.split(value, \"/\").get(1));\n                    }\n                    else\n                    {\n                        tags.put(key, value);\n                    }\n                }\n                try\n                {\n                    builder.addPoint(identifier, (Location) item.getItem(), tags);\n                }\n                catch (final CoreException e)\n                {\n                    if (!tags.isEmpty())\n                    {\n                        throw e;\n                    }\n                    // ignore. It is a duplicated node in GeoJson without any tags\n                }\n            }\n        });\n        return builder.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/RelationBean.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder;\n\nimport java.io.Serializable;\nimport java.util.AbstractCollection;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedRelation;\n\n/**\n * @author matthieun\n */\npublic class RelationBean extends AbstractCollection<RelationBeanItem> implements Serializable\n{\n    /**\n     * @author matthieun\n     */\n    public static class RelationBeanItem implements Serializable, Comparable<RelationBeanItem>\n    {\n        private static final long serialVersionUID = 441160361936498695L;\n\n        private final Long identifier;\n        private final String role;\n        private final ItemType type;\n\n        public RelationBeanItem(final Long identifier, final String role, final ItemType type)\n        {\n            this.identifier = identifier;\n            this.role = role;\n            this.type = type;\n        }\n\n        public RelationBeanItem(final RelationBeanItem item)\n        {\n            this.identifier = item.identifier;\n            this.role = item.role;\n            this.type = item.type;\n        }\n\n        @Override\n        public int compareTo(final RelationBeanItem other)\n        {\n            int result = this.getType().compareTo(other.getType());\n            if (result == 0)\n            {\n                result = Long.compare(this.getIdentifier(), other.getIdentifier());\n            }\n            if (result == 0)\n            {\n                result = this.getRole().compareTo(other.getRole());\n            }\n            return result;\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (other instanceof RelationBeanItem)\n            {\n                final RelationBeanItem that = (RelationBeanItem) other;\n                return this.getIdentifier().equals(that.getIdentifier())\n                        && this.getRole().equals(that.getRole())\n                        && this.getType() == that.getType();\n            }\n            return false;\n        }\n\n        public Long getIdentifier()\n        {\n            return this.identifier;\n        }\n\n        public String getRole()\n        {\n            return this.role;\n        }\n\n        public ItemType getType()\n        {\n            return this.type;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return Objects.hash(this.identifier, this.role, this.type);\n        }\n\n        @Override\n        public String toString()\n        {\n            return \"[\" + this.type + \", \" + this.identifier + \", \" + this.role + \"]\";\n        }\n    }\n\n    private static final long serialVersionUID = 8511830231633569713L;\n\n    private List<RelationBeanItem> beanItems;\n    /**\n     * This set has no concept of how many {@link RelationBeanItem}s of a given value have been\n     * removed. Technically, OSM allows for duplicate {@link RelationBeanItem}s in a given relation.\n     * However, these duplicates are disallowed by {@link PackedAtlas#relationMembers} and by\n     * extension {@link PackedRelation#members}. As a result, we need not worry about that edge case\n     * here.\n     */\n    private final Set<RelationBeanItem> explicitlyExcluded;\n\n    public static RelationBean fromSet(final Set<RelationBeanItem> set)\n    {\n        final RelationBean bean = new RelationBean();\n        for (final RelationBeanItem item : set)\n        {\n            bean.addItem(item);\n        }\n        return bean;\n    }\n\n    public static RelationBean mergeBeans(final RelationBean left, final RelationBean right)\n    {\n        final RelationBean result = new RelationBean();\n        for (final RelationBeanItem leftItem : left)\n        {\n            if (!right.isExplicitlyExcluded(leftItem))\n            {\n                result.addItem(leftItem);\n            }\n        }\n        for (final RelationBeanItem rightItem : right)\n        {\n            final Optional<RelationBeanItem> existingLeftItem = left\n                    .getItemFor(rightItem.getIdentifier(), rightItem.getType());\n            if (existingLeftItem.isPresent()\n                    && existingLeftItem.get().getRole().equals(rightItem.getRole()))\n            {\n                // Role already exists, continue.\n                continue;\n            }\n            if (!left.isExplicitlyExcluded(rightItem))\n            {\n                result.addItem(rightItem);\n            }\n        }\n        left.explicitlyExcluded.forEach(result::addItemExplicitlyExcluded);\n        right.explicitlyExcluded.forEach(result::addItemExplicitlyExcluded);\n        return result;\n    }\n\n    public RelationBean()\n    {\n        this.beanItems = new ArrayList<>();\n        this.explicitlyExcluded = new HashSet<>();\n    }\n\n    @Override\n    public boolean add(final RelationBeanItem item)\n    {\n        this.addItem(item);\n        return true;\n    }\n\n    public void addItem(final Long identifier, final String role, final ItemType itemType)\n    {\n        this.beanItems.add(new RelationBeanItem(identifier, role, itemType));\n    }\n\n    public void addItem(final RelationBeanItem item)\n    {\n        addItem(item.getIdentifier(), item.getRole(), item.getType());\n    }\n\n    public void addItemExplicitlyExcluded(final Long identifier, final String role,\n            final ItemType itemType)\n    {\n        addItemExplicitlyExcluded(new RelationBeanItem(identifier, role, itemType));\n    }\n\n    public void addItemExplicitlyExcluded(final RelationBeanItem item)\n    {\n        this.explicitlyExcluded.add(item);\n    }\n\n    /**\n     * Get this {@link RelationBean} as a {@link List} of its {@link RelationBeanItem}s.\n     *\n     * @return the item list representing this bean\n     */\n    public List<RelationBeanItem> asList()\n    {\n        return new ArrayList<>(this.beanItems);\n    }\n\n    /**\n     * Get this {@link RelationBean} as a {@link Map} from its constituent {@link RelationBeanItem}s\n     * to their counts (Here, counts refers to the number of times a given {@link RelationBeanItem}\n     * appears in the bean. While abnormal, duplicate bean items are technically allowed by OSM).\n     * This method is useful for comparing the equality of two {@link RelationBean}s, since the map\n     * representation intrinsically ignores the internal ordering of the constituent\n     * {@link RelationBeanItem}s (this ordering is irrelevant as far as equality is concerned).\n     *\n     * @return the item map representing this bean\n     */\n    public Map<RelationBeanItem, Integer> asMap()\n    {\n        final Map<RelationBeanItem, Integer> result = new HashMap<>();\n\n        for (final RelationBeanItem beanItem : this)\n        {\n            if (result.containsKey(beanItem))\n            {\n                int count = result.get(beanItem);\n                count += 1;\n                result.put(beanItem, count);\n            }\n            else\n            {\n                result.put(beanItem, 1);\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Get this {@link RelationBean} as a {@link Set} from its constituent\n     * {@link RelationBeanItem}s. This will ignore the fact that a {@link RelationBean} can\n     * technically contain duplicate items.\n     *\n     * @return the set representing this bean\n     */\n    public Set<RelationBeanItem> asSet()\n    {\n        return new HashSet<>(this.asMap().keySet());\n    }\n\n    /**\n     * Get this {@link RelationBean} as a sorted {@link List} of its {@link RelationBeanItem}s.\n     *\n     * @return the item list representing this bean\n     */\n    public List<RelationBeanItem> asSortedList()\n    {\n        final List<RelationBeanItem> result = asList();\n        Collections.sort(result);\n        return result;\n    }\n\n    /**\n     * Get this {@link RelationBean} as a {@link SortedSet} from its constituent\n     * {@link RelationBeanItem}s. This will ignore the fact that a {@link RelationBean} can\n     * technically contain duplicate items.\n     *\n     * @return the set representing this bean\n     */\n    public SortedSet<RelationBeanItem> asSortedSet()\n    {\n        return new TreeSet<>(this.asMap().keySet());\n    }\n\n    /**\n     * Check if the two beans are the same, without looking at the bean order.\n     *\n     * @param other\n     *            The other object\n     * @return True if the other object is a {@link RelationBean} and is equal regardless of order.\n     */\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof RelationBean)\n        {\n            final RelationBean that = (RelationBean) other;\n            return this.asMap().equals(that.asMap());\n        }\n        return false;\n    }\n\n    /**\n     * Check if the two beans are the same, without looking at the bean order. Also, ensure that\n     * their explicitlyExcluded sets match.\n     *\n     * @param other\n     *            The other object\n     * @return True if the other object satisfies {@link RelationBean#equals(Object)} AND has a\n     *         matching explicitlyExcluded set.\n     */\n    public boolean equalsIncludingExplicitlyExcluded(final Object other)\n    {\n        if (other instanceof RelationBean)\n        {\n            return other instanceof RelationBean && this.equals(other)\n                    && this.explicitlyExcluded.equals(((RelationBean) other).explicitlyExcluded);\n        }\n        return false;\n    }\n\n    public Set<RelationBeanItem> getExplicitlyExcluded()\n    {\n        return this.explicitlyExcluded;\n    }\n\n    public Optional<RelationBeanItem> getItemFor(final long identifier, final ItemType type)\n    {\n        for (int index = 0; index < this.beanItems.size(); index++)\n        {\n            if (this.beanItems.get(index).getIdentifier() == identifier\n                    && this.beanItems.get(index).getType() == type)\n            {\n                return Optional.of(getItemFor(index));\n            }\n        }\n        return Optional.empty();\n    }\n\n    public Optional<RelationBeanItem> getItemFor(final long identifier, final String role,\n            final ItemType type)\n    {\n        for (int index = 0; index < this.beanItems.size(); index++)\n        {\n            if (this.beanItems.get(index).getIdentifier() == identifier\n                    && role.equals(this.beanItems.get(index).getRole())\n                    && this.beanItems.get(index).getType() == type)\n            {\n                return Optional.of(getItemFor(index));\n            }\n        }\n        return Optional.empty();\n    }\n\n    public List<Long> getMemberIdentifiers()\n    {\n        return this.beanItems.stream().map(RelationBeanItem::getIdentifier)\n                .collect(Collectors.toList());\n    }\n\n    public List<String> getMemberRoles()\n    {\n        return this.beanItems.stream().map(RelationBeanItem::getRole).collect(Collectors.toList());\n    }\n\n    public List<ItemType> getMemberTypes()\n    {\n        return this.beanItems.stream().map(RelationBeanItem::getType).collect(Collectors.toList());\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(this.getMemberIdentifiers(), this.getMemberRoles(),\n                this.getMemberTypes());\n    }\n\n    /**\n     * @return True if this bean has no members\n     */\n    @Override\n    public boolean isEmpty()\n    {\n        return this.beanItems.isEmpty();\n    }\n\n    @Override\n    public Iterator<RelationBeanItem> iterator()\n    {\n        return this.beanItems.iterator();\n    }\n\n    public RelationBean merge(final RelationBean other)\n    {\n        return RelationBean.mergeBeans(this, other);\n    }\n\n    /**\n     * Remove all matching item from this {@link RelationBean} with a given identifier and\n     * {@link ItemType}. If item(s) are actually removed, this method will return a {@link List} of\n     * their roles. If nothing was removed, then the {@link List} will be empty.\n     * \n     * @param identifier\n     *            the id to remove\n     * @param itemType\n     *            the type to remove\n     * @return a {@link List} containing the roles of the removed items\n     */\n    public List<String> removeAllMatchingItems(final Long identifier, final ItemType itemType)\n    {\n        final List<RelationBeanItem> newBeanItems = new ArrayList<>();\n        final List<String> removedRoles = new ArrayList<>();\n\n        for (final RelationBeanItem item : this.beanItems)\n        {\n            if (item.getIdentifier().equals(identifier) && item.getType().equals(itemType))\n            {\n                removedRoles.add(item.getRole());\n            }\n            else\n            {\n                newBeanItems.add(\n                        new RelationBeanItem(item.getIdentifier(), item.getRole(), item.getType()));\n            }\n        }\n        if (!removedRoles.isEmpty())\n        {\n            this.beanItems = newBeanItems;\n        }\n        return removedRoles;\n    }\n\n    public boolean removeItem(final Long identifier, final String role, final ItemType itemType)\n    {\n        return removeItem(new RelationBeanItem(identifier, role, itemType));\n    }\n\n    public boolean removeItem(final RelationBeanItem item)\n    {\n        return this.beanItems.remove(item);\n    }\n\n    /**\n     * @return The number of members in this {@link RelationBean}\n     */\n    @Override\n    public int size()\n    {\n        return this.beanItems.size();\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"RelationBean [\" + this.beanItems + \"]\";\n    }\n\n    private RelationBeanItem getItemFor(final int index)\n    {\n        if (index < 0 || index >= size())\n        {\n            throw new CoreException(\"Invalid index {}\", index);\n        }\n        return new RelationBeanItem(this.beanItems.get(index));\n    }\n\n    private boolean isExplicitlyExcluded(final RelationBeanItem relationBeanItem)\n    {\n        return this.explicitlyExcluded.contains(relationBeanItem);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveArea.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\n\n/**\n * A primitive object for {@link Area}\n *\n * @author tony\n */\npublic class AtlasPrimitiveArea extends AtlasPrimitiveEntity\n{\n    private static final long serialVersionUID = -8890808695358609272L;\n    private final Polygon polygon;\n\n    public AtlasPrimitiveArea(final long identifier, final Polygon polygon,\n            final Map<String, String> tags)\n    {\n        super(identifier, tags);\n        this.polygon = polygon;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.polygon.bounds();\n    }\n\n    public Polygon getPolygon()\n    {\n        return this.polygon;\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"AtlasPrimitiveArea [polygon=\" + this.polygon + \", getIdentifier()=\"\n                + getIdentifier() + \", getTags()=\" + getTags() + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveBigNode.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNode;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNode.Path;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNode.Type;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.RestrictedPath;\n\n/**\n * A serializable Big Node thats useful in spark jobs\n *\n * @author Sid\n */\npublic class AtlasPrimitiveBigNode extends AtlasPrimitiveEntity\n{\n    private static final long serialVersionUID = -1722511104597663348L;\n    // All the nodes defining this BigNode\n    private final Set<AtlasPrimitiveLocationItem> nodes;\n    // Set of connected edges\n    private final Set<AtlasPrimitiveEdge> edges;\n    // All the possible paths in then out of this BigNode\n    private final Set<AtlasPrimitiveRoute> paths;\n    // All the restricted paths of this BigNode\n    private final Set<AtlasPrimitiveRouteIdentifier> restrictedPaths;\n    // Type of Big Node\n    private final Type type;\n\n    public static AtlasPrimitiveBigNode from(final BigNode bigNode)\n    {\n        return from(bigNode, false, Path.SHORTEST);\n    }\n\n    public static AtlasPrimitiveBigNode from(final BigNode bigNode,\n            final boolean storeRestrictedPaths, final Path pathType)\n    {\n        final Set<AtlasPrimitiveLocationItem> nodes = bigNode.nodes().stream()\n                .map(node -> AtlasPrimitiveLocationItem.from(node)).collect(Collectors.toSet());\n        final Set<AtlasPrimitiveEdge> edges = bigNode.edges().stream()\n                .map(edge -> AtlasPrimitiveEdge.from(edge)).collect(Collectors.toSet());\n\n        // All the paths in then out of this BigNode - the type (shortest/all/etc.) of path is\n        // configurable\n        final Set<AtlasPrimitiveRoute> paths;\n        switch (pathType)\n        {\n            case SHORTEST:\n                paths = bigNode.shortestPaths().stream().map(AtlasPrimitiveRoute::from)\n                        .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toSet());\n                break;\n            case ALL:\n                paths = bigNode.allPaths().stream().map(AtlasPrimitiveRoute::from)\n                        .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toSet());\n                break;\n            default:\n                throw new CoreException(\"Invalid Path Type: {}\", pathType);\n        }\n\n        // All the restricted paths of this BigNode - will use all possible paths under the covers\n        // to capture as many restrictions as possible.\n        final Set<AtlasPrimitiveRouteIdentifier> restrictedPaths = storeRestrictedPaths ? bigNode\n                .turnRestrictions().stream().map(RestrictedPath::getRoute)\n                .map(AtlasPrimitiveRoute::from).filter(Optional::isPresent).map(Optional::get)\n                .map(AtlasPrimitiveRouteIdentifier::from).collect(Collectors.toSet())\n                : Collections.emptySet();\n        return new AtlasPrimitiveBigNode(bigNode.getIdentifier(), nodes, edges, paths,\n                restrictedPaths, bigNode.getTags(), bigNode.getType());\n    }\n\n    public AtlasPrimitiveBigNode(final long identifier, final Set<AtlasPrimitiveLocationItem> nodes,\n            final Set<AtlasPrimitiveEdge> edges, final Set<AtlasPrimitiveRoute> paths,\n            final Set<AtlasPrimitiveRouteIdentifier> restrictedPaths,\n            final Map<String, String> tags, final Type type)\n    {\n        super(identifier, tags);\n        this.nodes = nodes;\n        this.edges = edges;\n        this.paths = paths;\n        this.type = type;\n        this.restrictedPaths = restrictedPaths;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return Rectangle.forLocated(this.nodes);\n    }\n\n    public Set<AtlasPrimitiveEdge> edges()\n    {\n        return this.edges;\n    }\n\n    public Set<AtlasPrimitiveRouteIdentifier> getRestrictedPaths()\n    {\n        return this.restrictedPaths;\n    }\n\n    public Set<AtlasPrimitiveEdge> inEdges()\n    {\n        return edges().stream().filter(edge -> !nodesContain(edge.start()))\n                .collect(Collectors.toSet());\n    }\n\n    public Set<AtlasPrimitiveEdge> junctionEdges()\n    {\n        return edges().stream()\n                .filter(edge -> nodesContain(edge.start()) && nodesContain(edge.end()))\n                .collect(Collectors.toSet());\n    }\n\n    public Set<Location> nodeLocations()\n    {\n        return this.nodes().stream().map(AtlasPrimitiveLocationItem::getLocation)\n                .collect(Collectors.toSet());\n    }\n\n    public Set<AtlasPrimitiveLocationItem> nodes()\n    {\n        return this.nodes;\n    }\n\n    public boolean nodesContain(final Location location)\n    {\n        return this.nodes.stream().filter(node -> node.getLocation().equals(location)).count() > 0;\n    }\n\n    public Set<AtlasPrimitiveEdge> outEdges()\n    {\n        return edges().stream().filter(edge -> !this.nodeLocations().contains(edge.end()))\n                .collect(Collectors.toSet());\n    }\n\n    public Set<AtlasPrimitiveRoute> paths()\n    {\n        return this.paths;\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[AtlasPrimitiveBigNode: nodes=\"\n                + nodes().stream().map(node -> node.getIdentifier()).collect(Collectors.toSet())\n                + \"]\";\n    }\n\n    public Type type()\n    {\n        return this.type;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\n\n/**\n * @author Sid\n */\npublic class AtlasPrimitiveEdge extends AtlasPrimitiveLineItem\n{\n    private static final long serialVersionUID = 4693146600590040648L;\n\n    public static AtlasPrimitiveEdge from(final Edge edge)\n    {\n        return new AtlasPrimitiveEdge(edge.getIdentifier(), edge.asPolyLine(), edge.getTags());\n    }\n\n    public AtlasPrimitiveEdge(final long identifier, final PolyLine polyLine,\n            final Map<String, String> tags)\n    {\n        super(identifier, polyLine, tags);\n    }\n\n    public Location end()\n    {\n        return this.getPolyLine().last();\n    }\n\n    public boolean isReversedEdge(final AtlasPrimitiveEdge reverseEdgeCandidate)\n    {\n        return this.getIdentifier() == -reverseEdgeCandidate.getIdentifier();\n    }\n\n    public Location start()\n    {\n        return this.getPolyLine().first();\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[AtlasPrimitiveEdge : \");\n        builder.append(\"id = \" + getIdentifier());\n        builder.append(\" : polyLine = \" + getPolyLine());\n        builder.append(\" ]\");\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveEdgeIdentifier.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.io.Serializable;\n\n/**\n * @author Sid\n */\npublic class AtlasPrimitiveEdgeIdentifier implements Serializable\n{\n    private static final long serialVersionUID = 6082277761179021192L;\n\n    private long identifier;\n\n    public static AtlasPrimitiveEdgeIdentifier from(final AtlasPrimitiveEdge atlasPrimitiveEdge)\n    {\n        return new AtlasPrimitiveEdgeIdentifier(atlasPrimitiveEdge.getIdentifier());\n    }\n\n    public AtlasPrimitiveEdgeIdentifier()\n    {\n    }\n\n    public AtlasPrimitiveEdgeIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        return other instanceof AtlasPrimitiveEdgeIdentifier\n                && this.getIdentifier() == ((AtlasPrimitiveEdgeIdentifier) other).getIdentifier();\n    }\n\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Long.hashCode(this.identifier);\n    }\n\n    public void setIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"AtlasPrimitiveEdgeIdentifier [identifier=\" + this.identifier + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveEntity.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * A primitive object for {@link AtlasEntity}\n *\n * @author tony\n * @author Sid\n */\npublic abstract class AtlasPrimitiveEntity implements Serializable, Taggable, Located\n{\n    private static final long serialVersionUID = -4372740269485938585L;\n    private final long identifier;\n    private final Map<String, String> tags;\n\n    public AtlasPrimitiveEntity(final long identifier, final Map<String, String> tags)\n    {\n        this.identifier = identifier;\n        this.tags = tags;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        return other != null && other instanceof AtlasPrimitiveEntity\n                && this.getIdentifier() == ((AtlasPrimitiveEntity) other).getIdentifier();\n    }\n\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Optional<String> getTag(final String key)\n    {\n        return Optional.ofNullable(getTags().get(key));\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Long.hashCode(this.identifier);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveLineItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\n\n/**\n * A primitive object for {@link LineItem}\n *\n * @author tony\n */\npublic class AtlasPrimitiveLineItem extends AtlasPrimitiveEntity\n{\n    private static final long serialVersionUID = 4435750184254868724L;\n    private final PolyLine polyLine;\n\n    public AtlasPrimitiveLineItem(final long identifier, final PolyLine polyLine,\n            final Map<String, String> tags)\n    {\n        super(identifier, tags);\n        this.polyLine = polyLine;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.polyLine.bounds();\n    }\n\n    public PolyLine getPolyLine()\n    {\n        return this.polyLine;\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"AtlasPrimitiveLineItem [polyLine=\" + this.polyLine + \", getIdentifier()=\"\n                + getIdentifier() + \", getTags()=\" + getTags() + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveLocationItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\n\n/**\n * A primitive object for {@link LocationItem}\n *\n * @author tony\n */\npublic class AtlasPrimitiveLocationItem extends AtlasPrimitiveEntity\n{\n    private static final long serialVersionUID = -6767702793907654973L;\n    private final Location location;\n\n    public static AtlasPrimitiveLocationItem from(final Node node)\n    {\n        final AtlasPrimitiveLocationItem locationItem = new AtlasPrimitiveLocationItem(\n                node.getIdentifier(), node.getLocation(), node.getTags());\n        return locationItem;\n    }\n\n    public AtlasPrimitiveLocationItem(final long identifier, final Location location,\n            final Map<String, String> tags)\n    {\n        super(identifier, tags);\n        this.location = location;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.location.bounds();\n    }\n\n    public Location getLocation()\n    {\n        return this.location;\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"AtlasPrimitiveLocationItem [location=\" + this.location + \", getIdentifier()=\"\n                + getIdentifier() + \", getTags()=\" + getTags() + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveObjectStore.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.collections.JoinedCollection;\nimport org.openstreetmap.atlas.utilities.collections.ParallelIterable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Store all primitive entities which can then be used to create @{link Atlas}\n *\n * @author tony\n */\npublic class AtlasPrimitiveObjectStore\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasPrimitiveObjectStore.class);\n\n    private final Map<Long, AtlasPrimitiveLocationItem> nodes = new HashMap<>();\n    private final Map<Long, AtlasPrimitiveLocationItem> points = new HashMap<>();\n    private final Map<Long, AtlasPrimitiveLineItem> edges = new HashMap<>();\n    private final Map<Long, AtlasPrimitiveLineItem> lines = new HashMap<>();\n    private final Map<Long, AtlasPrimitiveArea> areas = new HashMap<>();\n    private final Map<Long, AtlasPrimitiveRelation> relations = new HashMap<>();\n\n    public void addArea(final AtlasPrimitiveArea area)\n    {\n        this.areas.put(area.getIdentifier(), area);\n    }\n\n    public void addEdge(final AtlasPrimitiveLineItem edge)\n    {\n        this.edges.put(edge.getIdentifier(), edge);\n    }\n\n    public void addLine(final AtlasPrimitiveLineItem line)\n    {\n        this.lines.put(line.getIdentifier(), line);\n    }\n\n    public void addNode(final AtlasPrimitiveLocationItem node)\n    {\n        this.nodes.put(node.getIdentifier(), node);\n    }\n\n    public void addPoint(final AtlasPrimitiveLocationItem point)\n    {\n        this.points.put(point.getIdentifier(), point);\n    }\n\n    public void addRelation(final AtlasPrimitiveRelation relation)\n    {\n        this.relations.put(relation.getIdentifier(), relation);\n    }\n\n    /**\n     * @return an {@link Atlas} object based on this object store\n     */\n    public Atlas build()\n    {\n        // There should be no missing identifiers if the data is complete\n        final Optional<TemporaryObjectStore> missingIdentifiers = checkDataIntegrity();\n        missingIdentifiers.ifPresent(missingObjects ->\n        {\n            throw new CoreException(\"Data is not complete, still missing {} \",\n                    missingObjects.toDebugString());\n        });\n\n        logger.debug(\"Data is complete, starts to build atlas\");\n\n        // Data is complete, run atlas builder\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder().withSizeEstimates(new AtlasSize(\n                this.getEdges().size(), this.getNodes().size(), this.getAreas().size(),\n                this.getLines().size(), this.getPoints().size(), this.getRelations().size()));\n\n        logger.debug(\"Building atlas nodes...\");\n        getNodes().values().forEach(node ->\n        {\n            builder.addNode(node.getIdentifier(), node.getLocation(), node.getTags());\n        });\n\n        logger.debug(\"Building atlas points...\");\n        getPoints().values().forEach(point ->\n        {\n            builder.addPoint(point.getIdentifier(), point.getLocation(), point.getTags());\n        });\n\n        logger.debug(\"Building atlas edges...\");\n        getEdges().values().forEach(edge ->\n        {\n            builder.addEdge(edge.getIdentifier(), edge.getPolyLine(), edge.getTags());\n        });\n\n        logger.debug(\"Building atlas lines...\");\n        getLines().values().forEach(line ->\n        {\n            builder.addLine(line.getIdentifier(), line.getPolyLine(), line.getTags());\n        });\n\n        logger.debug(\"Building atlas areas...\");\n        getAreas().values().forEach(area ->\n        {\n            builder.addArea(area.getIdentifier(), area.getPolygon(), area.getTags());\n        });\n\n        logger.debug(\"Building atlas relations...\");\n        getRelations().values().forEach(relation ->\n        {\n            builder.addRelation(relation.getIdentifier(), relation.getOsmIdentifier(),\n                    relation.getRelationBean(), relation.getTags());\n        });\n\n        return builder.get();\n    }\n\n    /**\n     * This method checks the data integrity and will be run at the beginning of build() method.\n     * Also different readers (PBF/other) can call this method to check if the current data set is\n     * complete. If not the reader needs to make data set complete\n     *\n     * @return A {@link TemporaryObjectStore} if the data in current store is not complete\n     */\n    public Optional<TemporaryObjectStore> checkDataIntegrity()\n    {\n        logger.debug(\"Checking object store data integrity\");\n        final TemporaryObjectStore missingObjects = new TemporaryObjectStore();\n\n        // Check relations\n        getRelations().values().forEach(relation ->\n        {\n            final RelationBean bean = relation.getRelationBean();\n            final ParallelIterable parallel = new ParallelIterable(bean.getMemberIdentifiers(),\n                    bean.getMemberTypes());\n            final Iterator<JoinedCollection> iterator = parallel.iterator();\n            while (iterator.hasNext())\n            {\n                final JoinedCollection relationMember = iterator.next();\n                final long identifier = relationMember.get(0);\n                final ItemType type = (ItemType) relationMember.get(1);\n                switch (type)\n                {\n                    case NODE:\n                        if (!this.nodes.containsKey(identifier))\n                        {\n                            missingObjects.addNode(identifier);\n                        }\n                        break;\n                    case EDGE:\n                        if (!this.edges.containsKey(identifier))\n                        {\n                            missingObjects.addEdge(identifier);\n                        }\n                        break;\n                    case AREA:\n                        if (!this.areas.containsKey(identifier))\n                        {\n                            missingObjects.addArea(identifier);\n                        }\n                        break;\n                    case LINE:\n                        if (!this.lines.containsKey(identifier))\n                        {\n                            missingObjects.addLine(identifier);\n                        }\n                        break;\n                    case POINT:\n                        if (!this.points.containsKey(identifier))\n                        {\n                            missingObjects.addPoint(identifier);\n                        }\n                        break;\n                    case RELATION:\n                        if (!this.relations.containsKey(identifier))\n                        {\n                            missingObjects.addRelation(identifier);\n                        }\n                        break;\n                    default:\n                        throw new CoreException(\"Unknown type {}\", type);\n                }\n            }\n        });\n\n        // Put location of all nodes into a set\n        final Set<Location> locationOfNodes = new HashSet<>(this.nodes.size(), 1);\n        getNodes().values().forEach(node -> locationOfNodes.add(node.getLocation()));\n\n        // Check Edge\n        getEdges().values().forEach(edge ->\n        {\n            final PolyLine shape = edge.getPolyLine();\n            final Location first = shape.first();\n            final Location last = shape.last();\n            if (!locationOfNodes.contains(first))\n            {\n                missingObjects.addLocation(first);\n            }\n            if (!locationOfNodes.contains(last))\n            {\n                missingObjects.addLocation(last);\n            }\n        });\n\n        return missingObjects.isEmpty() ? Optional.empty() : Optional.of(missingObjects);\n    }\n\n    public Map<Long, AtlasPrimitiveArea> getAreas()\n    {\n        return this.areas;\n    }\n\n    public Map<Long, AtlasPrimitiveLineItem> getEdges()\n    {\n        return this.edges;\n    }\n\n    public Map<Long, AtlasPrimitiveLineItem> getLines()\n    {\n        return this.lines;\n    }\n\n    public Map<Long, AtlasPrimitiveLocationItem> getNodes()\n    {\n        return this.nodes;\n    }\n\n    public Map<Long, AtlasPrimitiveLocationItem> getPoints()\n    {\n        return this.points;\n    }\n\n    public Map<Long, AtlasPrimitiveRelation> getRelations()\n    {\n        return this.relations;\n    }\n\n    public String summary()\n    {\n        return \"The store has \" + this.nodes.size() + \" nodes, \" + this.points.size() + \" points, \"\n                + this.edges.size() + \" edges, \" + this.lines.size() + \" lines, \"\n                + this.areas.size() + \" areas, \" + this.relations.size() + \" relations\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveRelation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\n\n/**\n * A primitive object for {@link RelationBean}\n *\n * @author tony\n */\npublic class AtlasPrimitiveRelation extends AtlasPrimitiveEntity\n{\n    private static final long serialVersionUID = 4189537752256202439L;\n    private final long osmIdentifier;\n    private final RelationBean relationBean;\n    private final Rectangle bounds;\n\n    public AtlasPrimitiveRelation(final long identifier, final long osmIdentifier,\n            final RelationBean relationBean, final Map<String, String> tags, final Rectangle bounds)\n    {\n        super(identifier, tags);\n        this.osmIdentifier = osmIdentifier;\n        this.relationBean = relationBean;\n        this.bounds = bounds;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bounds;\n    }\n\n    public long getOsmIdentifier()\n    {\n        return this.osmIdentifier;\n    }\n\n    public RelationBean getRelationBean()\n    {\n        return this.relationBean;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveRoute.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.builder.CompareToBuilder;\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\nimport com.google.common.collect.ImmutableList;\n\n/**\n * @author Sid\n */\npublic final class AtlasPrimitiveRoute implements Iterable<AtlasPrimitiveEdge>, Serializable\n{\n    private static final long serialVersionUID = 804872809334744220L;\n\n    /*\n     * This comparator can be used to sort AtlasPrimitiveRoutes in descending order of size.\n     */\n    public static final Comparator<AtlasPrimitiveRoute> ROUTE_SIZE_COMPARATOR = (\n            final AtlasPrimitiveRoute route1,\n            final AtlasPrimitiveRoute route2) -> new CompareToBuilder()\n                    .append(route2.size(), route1.size())\n                    // The hashcode needs to be deterministic to ensure deterministic order\n                    .append(route1.hashCode(), route2.hashCode()).toComparison();\n\n    private final List<AtlasPrimitiveEdge> primitiveRoute;\n\n    public static AtlasPrimitiveRoute from(final AtlasPrimitiveEdge... primitiveEdges)\n    {\n        return new AtlasPrimitiveRoute(Arrays.asList(primitiveEdges));\n    }\n\n    public static AtlasPrimitiveRoute from(\n            final AtlasPrimitiveRouteIdentifier atlasPrimitiveRouteIdentifier, final Atlas atlas)\n    {\n        final List<Edge> fromEdges = Iterables.stream(atlasPrimitiveRouteIdentifier)\n                .map(edgeIdentifier -> atlas.edge(edgeIdentifier.getIdentifier())).collectToList();\n        return AtlasPrimitiveRoute.from(fromEdges);\n    }\n\n    public static AtlasPrimitiveRoute from(final List<Edge> edges)\n    {\n        return new AtlasPrimitiveRoute(\n                edges.stream().map(AtlasPrimitiveEdge::from).collect(Collectors.toList()));\n    }\n\n    public static Optional<AtlasPrimitiveRoute> from(final Route route)\n    {\n        if (route != null && route.size() > 0)\n        {\n            return Optional.of(\n                    new AtlasPrimitiveRoute(Iterables.translate(route, AtlasPrimitiveEdge::from)));\n        }\n        return Optional.empty();\n    }\n\n    public AtlasPrimitiveRoute(final Iterable<AtlasPrimitiveEdge> primitiveEdges)\n    {\n        this.primitiveRoute = ImmutableList.copyOf(primitiveEdges);\n    }\n\n    public PolyLine asPolyLine()\n    {\n        final List<Location> locations = new ArrayList<>();\n        Location lastLocation = null;\n        for (final AtlasPrimitiveEdge edge : this.primitiveRoute)\n        {\n            final PolyLine polyLine = edge.getPolyLine();\n            locations.addAll(polyLine);\n            lastLocation = polyLine.last();\n            locations.remove(lastLocation);\n        }\n        locations.add(lastLocation);\n        return new PolyLine(locations);\n    }\n\n    public AtlasPrimitiveEdge end()\n    {\n        if (this.primitiveRoute.size() > 0)\n        {\n            return this.primitiveRoute.get(this.primitiveRoute.size() - 1);\n        }\n        throw new CoreException(\"Illegal State : Empty route\");\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof AtlasPrimitiveRoute)\n        {\n            final AtlasPrimitiveRoute that = (AtlasPrimitiveRoute) other;\n            if (this.size() == that.size())\n            {\n                return new EqualsBuilder().append(this.start().start(), that.start().start())\n                        .append(this.end().end(), that.end().end())\n                        .append(this.primitiveRoute, that.primitiveRoute).isEquals();\n            }\n        }\n        return false;\n    }\n\n    /**\n     * This method uses the provided {@link Atlas} to return the {@link Route} corresponding to this\n     * {@link AtlasPrimitiveRoute}\n     *\n     * @param atlas\n     *            {@link Atlas} containing the {@link Route} {@link Edge}s\n     * @return {@link Route} corresponding to this {@link AtlasPrimitiveRoute}\n     */\n    public Optional<Route> getRoute(final Atlas atlas)\n    {\n        final List<Edge> edges = new ArrayList<>();\n        for (final AtlasPrimitiveEdge primitiveEdge : this.primitiveRoute)\n        {\n            final Edge edge = atlas.edge(primitiveEdge.getIdentifier());\n            if (edge == null)\n            {\n                return Optional.empty();\n            }\n            edges.add(edge);\n        }\n        return Optional.of(Route.forEdges(edges));\n    }\n\n    /**\n     * Note: The start and end {@link Node}s are part of the hash code to reduce the probability of\n     * a collision. There are other candidates to add here, like distance between start/end, but\n     * start/end by themselves are the least computationally intensive to derive. For best practice,\n     * this is consistent with {@link Route}'s hash code.\n     */\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.start().start()).append(this.end().end())\n                .append(Iterables.asList(this)).hashCode();\n    }\n\n    public int indexOf(final AtlasPrimitiveEdge primitiveEdge)\n    {\n        return this.primitiveRoute.indexOf(primitiveEdge);\n    }\n\n    public boolean isOverlappedBy(final AtlasPrimitiveRoute primitiveRoute)\n    {\n        return AtlasPrimitiveRouteIdentifier.from(this)\n                .isOverlappedBy(AtlasPrimitiveRouteIdentifier.from(primitiveRoute));\n    }\n\n    public boolean isOverlappedBy(final Route route)\n    {\n        final Optional<AtlasPrimitiveRoute> atlasPrimitiveRoute = AtlasPrimitiveRoute.from(route);\n        return atlasPrimitiveRoute.isPresent() && isOverlappedBy(atlasPrimitiveRoute.get());\n    }\n\n    @Override\n    public Iterator<AtlasPrimitiveEdge> iterator()\n    {\n        return this.primitiveRoute.iterator();\n    }\n\n    /**\n     * Counts the number of times a subRoute overlaps a route. Currently this doesn't handle cases\n     * with loops within subRoute E.g [1,2,3,1,2,3,1,4,5], subRoute is [1,2,3,1,4]\n     *\n     * @param subRoute\n     *            Smaller subsequence of the route that can overlap with route\n     * @return overlapCount\n     */\n    public int overlapCount(final AtlasPrimitiveRoute subRoute)\n    {\n        int overlapCount = 0;\n        if (this.primitiveRoute == null || subRoute == null)\n        {\n            return overlapCount;\n        }\n        Iterator<AtlasPrimitiveEdge> subRouteIterator = subRoute.iterator();\n        AtlasPrimitiveEdge subRouteEdge = subRouteIterator.hasNext() ? subRouteIterator.next()\n                : null;\n        for (final AtlasPrimitiveEdge edge : this.primitiveRoute)\n        {\n            if (subRouteEdge == null)\n            {\n                break;\n            }\n            if (edge.equals(subRouteEdge))\n            {\n                if (!subRouteIterator.hasNext())\n                {\n                    overlapCount++;\n                    subRouteIterator = subRoute.iterator();\n                }\n            }\n            else\n            {\n                subRouteIterator = subRoute.iterator();\n                if (edge.equals(subRoute.start()))\n                {\n                    subRouteEdge = subRouteIterator.hasNext() ? subRouteIterator.next() : null;\n                }\n            }\n            subRouteEdge = subRouteIterator.hasNext() ? subRouteIterator.next() : null;\n        }\n        return overlapCount;\n    }\n\n    public int size()\n    {\n        return this.primitiveRoute.size();\n    }\n\n    public AtlasPrimitiveEdge start()\n    {\n        if (this.primitiveRoute.size() > 0)\n        {\n            return this.primitiveRoute.get(0);\n        }\n        throw new CoreException(\"Illegal State : Empty route\");\n    }\n\n    public List<AtlasPrimitiveEdge> subRoute(final int fromIndex, final int toIndex)\n    {\n        return this.primitiveRoute.subList(fromIndex, toIndex);\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[AtlasPrimitiveRoute: \");\n        builder.append(this.primitiveRoute.stream().map(edge -> edge.toString())\n                .collect(Collectors.joining(\", \")));\n        builder.append(\"]\");\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveRouteIdentifier.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.exception.CoreException;\n\nimport com.google.common.collect.ImmutableList;\n\n/**\n * @author Sid\n */\npublic class AtlasPrimitiveRouteIdentifier\n        implements Iterable<AtlasPrimitiveEdgeIdentifier>, Serializable\n{\n    private static final long serialVersionUID = 2321636844479248974L;\n\n    private List<AtlasPrimitiveEdgeIdentifier> primitiveRouteIdentifier;\n\n    public static AtlasPrimitiveRouteIdentifier from(final AtlasPrimitiveRoute atlasPrimitiveRoute)\n    {\n        final List<AtlasPrimitiveEdgeIdentifier> atlasPrimitiveEdgeIds = new ArrayList<>();\n        atlasPrimitiveRoute.forEach(\n                edge -> atlasPrimitiveEdgeIds.add(AtlasPrimitiveEdgeIdentifier.from(edge)));\n        return new AtlasPrimitiveRouteIdentifier(atlasPrimitiveEdgeIds);\n    }\n\n    public AtlasPrimitiveRouteIdentifier()\n    {\n    }\n\n    public AtlasPrimitiveRouteIdentifier(final AtlasPrimitiveEdgeIdentifier... primitiveEdgeIds)\n    {\n        this.primitiveRouteIdentifier = ImmutableList.copyOf(primitiveEdgeIds);\n    }\n\n    public AtlasPrimitiveRouteIdentifier(\n            final Iterable<AtlasPrimitiveEdgeIdentifier> primitiveEdgeIds)\n    {\n        this.primitiveRouteIdentifier = ImmutableList.copyOf(primitiveEdgeIds);\n    }\n\n    public AtlasPrimitiveEdgeIdentifier end()\n    {\n        if (this.primitiveRouteIdentifier.size() > 0)\n        {\n            return this.primitiveRouteIdentifier.get(this.primitiveRouteIdentifier.size() - 1);\n        }\n        throw new CoreException(\"Illegal State : Empty route\");\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof AtlasPrimitiveRouteIdentifier)\n        {\n            final AtlasPrimitiveRouteIdentifier that = (AtlasPrimitiveRouteIdentifier) other;\n            if (this.primitiveRouteIdentifier.size() == that.primitiveRouteIdentifier.size())\n            {\n                return new EqualsBuilder()\n                        .append(this.primitiveRouteIdentifier, that.primitiveRouteIdentifier)\n                        .isEquals();\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.primitiveRouteIdentifier).hashCode();\n    }\n\n    /*\n     * Similar to {@link Route#overlapIndex}\n     */\n    public boolean isOverlappedBy(final AtlasPrimitiveRouteIdentifier primitiveRouteIdentifier)\n    {\n        if (primitiveRouteIdentifier == null)\n        {\n            return false;\n        }\n        // Keep track of the last index at which the last Edge was overlapping this route, to avoid\n        // returning false positives in case of routes making a loop.\n        int lastOverlapIndex = -1;\n        for (final AtlasPrimitiveEdgeIdentifier primitiveEdgeIdentifier : primitiveRouteIdentifier)\n        {\n            final int index = this.primitiveRouteIdentifier.indexOf(primitiveEdgeIdentifier);\n            if (index <= lastOverlapIndex)\n            {\n                // The edge does not overlap, or it does but at a smaller index which would indicate\n                // a loop.\n                return false;\n            }\n            lastOverlapIndex = index;\n        }\n        return true;\n    }\n\n    @Override\n    public Iterator<AtlasPrimitiveEdgeIdentifier> iterator()\n    {\n        return this.primitiveRouteIdentifier.iterator();\n    }\n\n    /**\n     * Counts the number of times a subRoute overlaps a route.\n     *\n     * @param subRouteIdentifier\n     *            - Smaller subsequence of the route that can overlap with route\n     * @return overlapCount\n     */\n    public int overlapCount(final AtlasPrimitiveRouteIdentifier subRouteIdentifier)\n    {\n        int overlapCount = 0;\n        if (this.primitiveRouteIdentifier == null || subRouteIdentifier == null)\n        {\n            return overlapCount;\n        }\n        Iterator<AtlasPrimitiveEdgeIdentifier> subRouteIdentifierIterator = subRouteIdentifier\n                .iterator();\n        AtlasPrimitiveEdgeIdentifier subRouteEdgeIdentifier = subRouteIdentifierIterator.hasNext()\n                ? subRouteIdentifierIterator.next()\n                : null;\n        for (final AtlasPrimitiveEdgeIdentifier edge : this.primitiveRouteIdentifier)\n        {\n            if (subRouteEdgeIdentifier == null)\n            {\n                break;\n            }\n            if (edge.equals(subRouteEdgeIdentifier))\n            {\n                if (!subRouteIdentifierIterator.hasNext())\n                {\n                    overlapCount++;\n                    subRouteIdentifierIterator = subRouteIdentifier.iterator();\n                }\n            }\n            else\n            {\n                subRouteIdentifierIterator = subRouteIdentifier.iterator();\n                if (edge.equals(subRouteIdentifier.start()))\n                {\n                    subRouteEdgeIdentifier = subRouteIdentifierIterator.hasNext()\n                            ? subRouteIdentifierIterator.next()\n                            : null;\n                }\n            }\n            subRouteEdgeIdentifier = subRouteIdentifierIterator.hasNext()\n                    ? subRouteIdentifierIterator.next()\n                    : null;\n        }\n        return overlapCount;\n    }\n\n    public void setPrimitiveRouteIdentifier(\n            final List<AtlasPrimitiveEdgeIdentifier> primitiveRouteIdentifier)\n    {\n        this.primitiveRouteIdentifier = primitiveRouteIdentifier;\n    }\n\n    public int size()\n    {\n        return this.primitiveRouteIdentifier.size();\n    }\n\n    public AtlasPrimitiveEdgeIdentifier start()\n    {\n        if (this.primitiveRouteIdentifier.size() > 0)\n        {\n            return this.primitiveRouteIdentifier.get(0);\n        }\n        throw new CoreException(\"Illegal State : Empty route\");\n    }\n\n    public List<AtlasPrimitiveEdgeIdentifier> subRoute(final int fromIndex, final int toIndex)\n    {\n        return this.primitiveRouteIdentifier.subList(fromIndex, toIndex);\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"AtlasPrimitiveRouteIdentifier [identifier=\" + this.primitiveRouteIdentifier + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/store/TemporaryObjectStore.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.Location;\n\n/**\n * A temporary object store to hold missing objects when checking data integrity of\n * {@link AtlasPrimitiveObjectStore}\n *\n * @author tony\n */\npublic class TemporaryObjectStore\n{\n    private final Set<Long> nodes = new HashSet<>();\n    private final Set<Long> points = new HashSet<>();\n    private final Set<Long> edges = new HashSet<>();\n    private final Set<Long> lines = new HashSet<>();\n    private final Set<Long> areas = new HashSet<>();\n    private final Set<Long> relations = new HashSet<>();\n    private final Set<Location> locations = new HashSet<>();\n\n    public void addArea(final long identifier)\n    {\n        this.areas.add(identifier);\n    }\n\n    public void addEdge(final long identifier)\n    {\n        this.edges.add(identifier);\n    }\n\n    public void addLine(final long identifier)\n    {\n        this.lines.add(identifier);\n    }\n\n    public void addLocation(final Location location)\n    {\n        this.locations.add(location);\n    }\n\n    public void addNode(final long identifier)\n    {\n        this.nodes.add(identifier);\n    }\n\n    public void addPoint(final long identifier)\n    {\n        this.points.add(identifier);\n    }\n\n    public void addRelation(final long identifier)\n    {\n        this.relations.add(identifier);\n    }\n\n    public Set<Long> getAreas()\n    {\n        return this.areas;\n    }\n\n    public Set<Long> getEdges()\n    {\n        return this.edges;\n    }\n\n    public Set<Long> getLines()\n    {\n        return this.lines;\n    }\n\n    public Set<Location> getLocations()\n    {\n        return this.locations;\n    }\n\n    public Set<Long> getNodes()\n    {\n        return this.nodes;\n    }\n\n    public Set<Long> getPoints()\n    {\n        return this.points;\n    }\n\n    public Set<Long> getRelations()\n    {\n        return this.relations;\n    }\n\n    public boolean isEmpty()\n    {\n        return this.nodes.isEmpty() && this.points.isEmpty() && this.edges.isEmpty()\n                && this.lines.isEmpty() && this.areas.isEmpty() && this.relations.isEmpty()\n                && this.locations.isEmpty();\n    }\n\n    public int size()\n    {\n        return this.nodes.size() + this.points.size() + this.edges.size() + this.lines.size()\n                + this.areas.size() + this.relations.size() + this.locations.size();\n    }\n\n    public String toDebugString()\n    {\n        return \"TemporaryIdentifierStore has \" + this.nodes.size() + \" nodes, \" + this.points.size()\n                + \" points, \" + this.edges.size() + \" edges, \" + this.lines.size() + \" lines, \"\n                + this.areas.size() + \" areas, \" + this.relations.size() + \" relations, \"\n                + this.locations.size() + \" locations\";\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/builder/text/TextAtlasBuilder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.text;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.converters.PolyLineStringConverter;\nimport org.openstreetmap.atlas.geography.converters.PolygonStringConverter;\nimport org.openstreetmap.atlas.streaming.resource.LineWriter;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Build some Atlas from a simple text file\n *\n * @author matthieun\n */\npublic class TextAtlasBuilder\n{\n    /**\n     * @author matthieun\n     */\n    private enum WriteMode\n    {\n        NODE(NODES_HEADER),\n        EDGE(EDGES_HEADER),\n        AREA(AREAS_HEADER),\n        LINE(LINES_HEADER),\n        POINT(POINTS_HEADER),\n        RELATION(RELATIONS_HEADER);\n\n        private final String header;\n\n        protected static WriteMode forHeader(final String header)\n        {\n            switch (header)\n            {\n                case NODES_HEADER:\n                    return WriteMode.NODE;\n                case EDGES_HEADER:\n                    return WriteMode.EDGE;\n                case AREAS_HEADER:\n                    return WriteMode.AREA;\n                case LINES_HEADER:\n                    return WriteMode.LINE;\n                case POINTS_HEADER:\n                    return WriteMode.POINT;\n                case RELATIONS_HEADER:\n                    return WriteMode.RELATION;\n                default:\n                    throw new CoreException(\"Invalid Header {}\", header);\n            }\n        }\n\n        WriteMode(final String header)\n        {\n            this.header = header;\n        }\n\n        protected String getHeader()\n        {\n            return this.header;\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(TextAtlasBuilder.class);\n    private static final String NODES_HEADER = \"# Nodes\";\n    private static final String EDGES_HEADER = \"# Edges\";\n    private static final String AREAS_HEADER = \"# Areas\";\n    private static final String LINES_HEADER = \"# Lines\";\n    private static final String POINTS_HEADER = \"# Points\";\n    private static final String RELATIONS_HEADER = \"# Relations\";\n    private static final String SEPARATOR = \" && \";\n    private static final String SECONDARY_SEPARATOR = \" || \";\n    private static final String TERTIARY_SEPARATOR = \" -> \";\n    private static final String SEPARATOR_REPLACEMENT = \" \";\n\n    public static String getNodesHeader()\n    {\n        return NODES_HEADER;\n    }\n\n    public PackedAtlas read(final Resource resource)\n    {\n        WriteMode mode = null;\n        long numberOfNodes = 0L;\n        long numberOfEdges = 0L;\n        long numberOfAreas = 0L;\n        long numberOfLines = 0L;\n        long numberOfPoints = 0L;\n        long numberOfRelations = 0L;\n        for (final String line : resource.lines())\n        {\n            if (line.startsWith(\"#\"))\n            {\n                mode = WriteMode.forHeader(line);\n            }\n            else\n            {\n                if (mode == null)\n                {\n                    throw new CoreException(\n                            \"Failed reading {}. Is that a text Atlas? Is it compressed?\", resource);\n                }\n                switch (mode)\n                {\n                    case NODE:\n                        numberOfNodes++;\n                        break;\n                    case EDGE:\n                        numberOfEdges++;\n                        break;\n                    case AREA:\n                        numberOfAreas++;\n                        break;\n                    case LINE:\n                        numberOfLines++;\n                        break;\n                    case POINT:\n                        numberOfPoints++;\n                        break;\n                    case RELATION:\n                        numberOfRelations++;\n                        break;\n                    default:\n                        break;\n                }\n            }\n        }\n        final AtlasSize size = new AtlasSize(numberOfEdges, numberOfNodes, numberOfAreas,\n                numberOfLines, numberOfPoints, numberOfRelations);\n\n        if (size.getEntityNumber() == 0)\n        {\n            throw new CoreException(\"Invalid text Atlas, it appears to be empty!\");\n        }\n\n        if (size.getNonRelationEntityNumber() == 0 && size.getRelationNumber() > 0)\n        {\n            throw new CoreException(\"Invalid text Atlas, it only contained Relations!\");\n        }\n\n        final AtlasMetaData metaData = new AtlasMetaData(size, true, \"unknown\", \"TextAtlas\",\n                \"unknown\", \"unknown\", Maps.hashMap());\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder().withSizeEstimates(size)\n                .withMetaData(metaData).withName(resource.getName()).withEnhancedRelationGeometry();\n        for (final String line : resource.lines())\n        {\n            if (line.startsWith(\"#\"))\n            {\n                mode = WriteMode.forHeader(line);\n            }\n            else\n            {\n                switch (mode)\n                {\n                    case NODE:\n                        parseNode(builder, line);\n                        break;\n                    case EDGE:\n                        parseEdge(builder, line);\n                        break;\n                    case AREA:\n                        parseArea(builder, line);\n                        break;\n                    case LINE:\n                        parseLine(builder, line);\n                        break;\n                    case POINT:\n                        parsePoint(builder, line);\n                        break;\n                    case RELATION:\n                        parseRelation(builder, line);\n                        break;\n                    default:\n                        break;\n                }\n            }\n        }\n        final PackedAtlas atlas = (PackedAtlas) builder.get();\n        if (atlas == null)\n        {\n            throw new CoreException(\"Atlas resulting from PackedAtlasBuilder was null.\");\n        }\n        return atlas;\n    }\n\n    public void write(final Atlas atlas, final WritableResource resource)\n    {\n        try (LineWriter writer = new LineWriter(resource))\n        {\n            for (final WriteMode mode : WriteMode.values())\n            {\n                writer.writeLine(mode.getHeader());\n                switch (mode)\n                {\n                    case NODE:\n                        atlas.nodes().forEach(item -> writer.writeLine(convertNode(item)));\n                        break;\n                    case EDGE:\n                        atlas.edges().forEach(item -> writer.writeLine(convertEdge(item)));\n                        break;\n                    case AREA:\n                        atlas.areas().forEach(item -> writer.writeLine(convertArea(item)));\n                        break;\n                    case LINE:\n                        atlas.lines().forEach(item -> writer.writeLine(convertLine(item)));\n                        break;\n                    case POINT:\n                        atlas.points().forEach(item -> writer.writeLine(convertPoint(item)));\n                        break;\n                    case RELATION:\n                        atlas.relations().forEach(item -> writer.writeLine(convertRelation(item)));\n                        break;\n                    default:\n                        break;\n                }\n            }\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to write Atlas to {}\", resource, e);\n        }\n    }\n\n    private String cleanupTags(final String value)\n    {\n        if (value != null)\n        {\n            String result = value;\n            if (value.contains(SEPARATOR))\n            {\n                logger.warn(\"Tag {} contains {}. Replacing it with \\\"{}\\\"\", value, SEPARATOR,\n                        SEPARATOR_REPLACEMENT);\n                result = result.replace(SEPARATOR, SEPARATOR_REPLACEMENT);\n            }\n            if (value.contains(SECONDARY_SEPARATOR))\n            {\n                logger.warn(\"Tag {} contains {}. Replacing it with \\\"{}\\\"\", value,\n                        SECONDARY_SEPARATOR, SEPARATOR_REPLACEMENT);\n                result = result.replace(SECONDARY_SEPARATOR, SEPARATOR_REPLACEMENT);\n            }\n            if (value.contains(TERTIARY_SEPARATOR))\n            {\n                logger.warn(\"Tag {} contains {}. Replacing it with \\\"{}\\\"\", value,\n                        TERTIARY_SEPARATOR, SEPARATOR_REPLACEMENT);\n                result = result.replace(TERTIARY_SEPARATOR, SEPARATOR_REPLACEMENT);\n            }\n            if (value.contains(System.lineSeparator()))\n            {\n                logger.warn(\"Tag {} contains a new line. Removing it.\", value);\n                result = result.replace(System.lineSeparator(), \"\");\n            }\n            return result;\n        }\n        return value;\n    }\n\n    private String convertArea(final Area item)\n    {\n        final StringList list = new StringList();\n        list.add(item.getIdentifier());\n        list.add(item.asPolygon().toCompactString());\n        list.add(convertTags(item));\n        return list.join(SEPARATOR);\n    }\n\n    private String convertEdge(final Edge item)\n    {\n        final StringList list = new StringList();\n        list.add(item.getIdentifier());\n        list.add(item.asPolyLine().toCompactString());\n        list.add(convertTags(item));\n        return list.join(SEPARATOR);\n    }\n\n    private String convertLine(final Line item)\n    {\n        final StringList list = new StringList();\n        list.add(item.getIdentifier());\n        list.add(item.asPolyLine().toCompactString());\n        list.add(convertTags(item));\n        return list.join(SEPARATOR);\n    }\n\n    private String convertNode(final Node item)\n    {\n        final StringList list = new StringList();\n        list.add(item.getIdentifier());\n        list.add(item.getLocation().toCompactString());\n        list.add(convertTags(item));\n        return list.join(SEPARATOR);\n    }\n\n    private String convertPoint(final Point item)\n    {\n        final StringList list = new StringList();\n        list.add(item.getIdentifier());\n        list.add(item.getLocation().toCompactString());\n        list.add(convertTags(item));\n        return list.join(SEPARATOR);\n    }\n\n    private String convertRelation(final Relation item)\n    {\n        final StringList list = new StringList();\n        list.add(item.getIdentifier());\n        list.add(convertRelationBean(item));\n        list.add(convertTags(item));\n        return list.join(SEPARATOR);\n    }\n\n    private String convertRelationBean(final Relation relation)\n    {\n        final StringList bean = new StringList();\n        for (final RelationMember member : relation.members())\n        {\n            final StringList list = new StringList();\n            list.add(member.getEntity().getIdentifier());\n            list.add(member.getRole());\n            final ItemType type = ItemType.forEntity(member.getEntity());\n            list.add(type.toShortString());\n            bean.add(list.join(TERTIARY_SEPARATOR));\n        }\n        return bean.join(SECONDARY_SEPARATOR);\n    }\n\n    private String convertTags(final Taggable taggable)\n    {\n        final StringList tags = new StringList();\n        for (final Entry<String, String> entry : taggable.getTags().entrySet())\n        {\n            final StringBuilder builder = new StringBuilder();\n            builder.append(cleanupTags(entry.getKey()));\n            builder.append(TERTIARY_SEPARATOR);\n            builder.append(cleanupTags(entry.getValue()));\n            tags.add(builder.toString());\n        }\n        return tags.join(SECONDARY_SEPARATOR);\n    }\n\n    private void parseArea(final PackedAtlasBuilder builder, final String line)\n    {\n        final StringList split = StringList.split(line, SEPARATOR);\n        final long identifier = Long.parseLong(split.get(0));\n        final Polygon geometry = new PolygonStringConverter().convert(split.get(1));\n        final Map<String, String> tags = new HashMap<>();\n        if (split.size() > 2)\n        {\n            tags.putAll(parseTags(split.get(2)));\n        }\n        builder.addArea(identifier, geometry, tags);\n    }\n\n    private void parseEdge(final PackedAtlasBuilder builder, final String line)\n    {\n        final StringList split = StringList.split(line, SEPARATOR);\n        final long identifier = Long.parseLong(split.get(0));\n        final PolyLine geometry = new PolyLineStringConverter().convert(split.get(1));\n        final Map<String, String> tags = new HashMap<>();\n        if (split.size() > 2)\n        {\n            tags.putAll(parseTags(split.get(2)));\n        }\n        builder.addEdge(identifier, geometry, tags);\n    }\n\n    private void parseLine(final PackedAtlasBuilder builder, final String line)\n    {\n        final StringList split = StringList.split(line, SEPARATOR);\n        final long identifier = Long.parseLong(split.get(0));\n        final PolyLine geometry = new PolyLineStringConverter().convert(split.get(1));\n        final Map<String, String> tags = new HashMap<>();\n        if (split.size() > 2)\n        {\n            tags.putAll(parseTags(split.get(2)));\n        }\n        builder.addLine(identifier, geometry, tags);\n    }\n\n    private void parseNode(final PackedAtlasBuilder builder, final String line)\n    {\n        final StringList split = StringList.split(line, SEPARATOR);\n        final long identifier = Long.parseLong(split.get(0));\n        final Location geometry = Location.forString(split.get(1));\n        final Map<String, String> tags = new HashMap<>();\n        if (split.size() > 2)\n        {\n            tags.putAll(parseTags(split.get(2)));\n        }\n        builder.addNode(identifier, geometry, tags);\n    }\n\n    private void parsePoint(final PackedAtlasBuilder builder, final String line)\n    {\n        final StringList split = StringList.split(line, SEPARATOR);\n        final long identifier = Long.parseLong(split.get(0));\n        final Location geometry = Location.forString(split.get(1));\n        final Map<String, String> tags = new HashMap<>();\n        if (split.size() > 2)\n        {\n            tags.putAll(parseTags(split.get(2)));\n        }\n        builder.addPoint(identifier, geometry, tags);\n    }\n\n    private void parseRelation(final PackedAtlasBuilder builder, final String line)\n    {\n        final WKTReader reader = new WKTReader();\n        final StringList split = StringList.split(line, SEPARATOR);\n\n        final Iterator<String> itr = split.iterator();\n        final long identifier = Long.parseLong(itr.next());\n        final RelationBean structure = parseRelationBean(itr.next());\n        final Map<String, String> tags = new HashMap<>();\n        if (itr.hasNext())\n        {\n            tags.putAll(parseTags(itr.next()));\n        }\n        try\n        {\n            if (itr.hasNext())\n            {\n                builder.addRelation(identifier, identifier, structure, tags,\n                        (MultiPolygon) reader.read(itr.next()));\n            }\n            else\n            {\n                builder.addRelation(identifier, identifier, structure, tags);\n            }\n        }\n        catch (final ParseException e)\n        {\n            throw new CoreException(\"Bad relation data for relation {}\", identifier);\n        }\n    }\n\n    private RelationBean parseRelationBean(final String value)\n    {\n        final RelationBean bean = new RelationBean();\n        final StringList split = StringList.split(value, SECONDARY_SEPARATOR);\n        for (final String beanValue : split)\n        {\n            final StringList valueSplit = StringList.split(beanValue, TERTIARY_SEPARATOR);\n            final long identifier = Long.parseLong(valueSplit.get(0));\n            final String role = valueSplit.get(1);\n            final ItemType itemType = ItemType.shortValueOf(valueSplit.get(2));\n            bean.addItem(identifier, role, itemType);\n        }\n        return bean;\n    }\n\n    private Map<String, String> parseTags(final String value)\n    {\n        try\n        {\n            final Map<String, String> result = Maps.hashMap();\n            final StringList split = StringList.split(value, SECONDARY_SEPARATOR);\n            for (final String tag : split)\n            {\n                final StringList tagSplit = StringList.split(tag, TERTIARY_SEPARATOR);\n                if (tagSplit.size() == 2)\n                {\n                    result.put(tagSplit.get(0), tagSplit.get(1));\n                }\n                if (tagSplit.size() == 1)\n                {\n                    result.put(tagSplit.get(0), \"\");\n                }\n            }\n            return result;\n        }\n        catch (final Throwable error)\n        {\n            throw new CoreException(\"Unable to parse tags from \\\"{}\\\"\", value, error);\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/AtlasChangeGenerator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.io.Serializable;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * Something that takes an {@link Atlas} and produces a set of {@link FeatureChange} that should be\n * apply-able back to the initial {@link Atlas}\n *\n * @author matthieun\n */\npublic interface AtlasChangeGenerator extends Converter<Atlas, Set<FeatureChange>>, Serializable\n{\n    @Override\n    default Set<FeatureChange> convert(final Atlas atlas)\n    {\n        return generate(atlas);\n    }\n\n    /**\n     * Generate a set of changes that make sense out of the gate.\n     *\n     * @param atlas\n     *            The Atlas to generate the changes from.\n     * @return The validated set of {@link FeatureChange}s\n     */\n    default Set<FeatureChange> generate(final Atlas atlas)\n    {\n        final Set<FeatureChange> result = new FeatureChangeBoundsExpander(\n                generateWithoutValidation(atlas), atlas).apply();\n        result.stream().forEach(featureChange -> featureChange.withAtlasContext(atlas));\n\n        if (result.isEmpty())\n        {\n            return result;\n        }\n\n        final ChangeBuilder builder = new ChangeBuilder();\n        result.forEach(builder::add);\n        final Change change = builder.get();\n\n        // Validate\n        validate(atlas, change);\n        // Return the already merged changes\n        return change.changes().collect(Collectors.toSet());\n    }\n\n    /**\n     * Generate a set of changes.\n     *\n     * @param atlas\n     *            The Atlas to generate the changes from.\n     * @return The un-validated set of {@link FeatureChange}s\n     */\n    Set<FeatureChange> generateWithoutValidation(Atlas atlas);\n\n    default String getName()\n    {\n        return this.getClass().getSimpleName();\n    }\n\n    default void validate(final Atlas source, final Change change)\n    {\n        new ChangeAtlas(source, change).validate();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/AtlasEntityKey.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * Caters to use cases where {@link AtlasEntity}-ies need to be grouped by the\n * {@link AtlasEntity#getIdentifier()}. {@link AtlasEntity#getIdentifier()} could repeat across\n * different entity types, and hence combined with {@link ItemType}. This class extends\n * {@link Tuple} and adds some functionality to reduce code verbosity.\n *\n * @author Yazad Khambata\n */\npublic class AtlasEntityKey extends Tuple<ItemType, Long>\n{\n    private static final long serialVersionUID = -3670403373644942819L;\n\n    public static AtlasEntityKey from(final ItemType itemType, final Long identifier)\n    {\n        return new AtlasEntityKey(itemType, identifier);\n    }\n\n    public static AtlasEntityKey from(final FeatureChange featureChange)\n    {\n        return from(featureChange.getItemType(), featureChange.getIdentifier());\n    }\n\n    protected AtlasEntityKey(final ItemType itemType, final Long identifier)\n    {\n        super(itemType, identifier);\n    }\n\n    public AtlasEntity getAtlasEntity(final Atlas atlas)\n    {\n        return getItemType().entityForIdentifier(atlas, getIdentifier());\n    }\n\n    public CompleteItemType getCompleteItemType()\n    {\n        return CompleteItemType.from(getItemType());\n    }\n\n    public Long getIdentifier()\n    {\n        return getSecond();\n    }\n\n    public ItemType getItemType()\n    {\n        return getFirst();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/Change.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumMap;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescription;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.ChangeDescriptorName;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.TagChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.change.serializer.ChangeGeoJsonSerializer;\nimport org.openstreetmap.atlas.geography.atlas.change.serializer.FeatureChangeGeoJsonSerializer;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.PrettifyStringFormat;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A change that can be applied to an {@link Atlas} to generate a {@link ChangeAtlas}.\n * <p>\n * It contains a collection of {@link FeatureChange} objects, which describe the changes.\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class Change implements Located, Serializable\n{\n    private static final long serialVersionUID = 1048481626851547987L;\n    private static final Logger logger = LoggerFactory.getLogger(Change.class);\n    private static final AtomicInteger CHANGE_IDENTIFIER_FACTORY = new AtomicInteger();\n\n    private final List<FeatureChange> featureChanges;\n    private final Map<AtlasEntityKey, Integer> identifierToIndex;\n    private final int identifier;\n    private Rectangle bounds;\n    private String name;\n    private transient Map<AtlasEntityKey, FeatureChange> allChangesMappedByAtlasEntityKey;\n\n    /**\n     * Merge {@link FeatureChange}s inside {@link Change} objects and create a\n     * {@link Change#newInstance()} with the merged {@link FeatureChange}s. The\n     * {@link #merge(Change...)} is guided by groupings based on {@link FeatureChangeMergeGroup}.\n     *\n     * @param changeInstances\n     *            - the {@link Change} instances to merge.\n     * @return - A {@link #newInstance()} of Change with {@link FeatureChange}s\n     *         {@link FeatureChange#merge(FeatureChange)}-ed.\n     */\n    public static Change merge(final Change... changeInstances)\n    {\n        final FeatureChange[] mergedFeatureChanges = Arrays.stream(changeInstances)\n                .flatMap(Change::changes)\n                .collect(Collectors.groupingBy(FeatureChangeMergeGroup::from, LinkedHashMap::new,\n                        Collectors.reducing(FeatureChange::merge)))\n                .values().stream().map(Optional::get).toArray(FeatureChange[]::new);\n\n        return Change.newInstance().withName(\"Merged Change\").addAll(mergedFeatureChanges);\n    }\n\n    public static Change newInstance()\n    {\n        return new Change();\n    }\n\n    protected Change()\n    {\n        this.featureChanges = new ArrayList<>();\n        this.identifierToIndex = new HashMap<>();\n        this.identifier = CHANGE_IDENTIFIER_FACTORY.getAndIncrement();\n    }\n\n    public Map<AtlasEntityKey, FeatureChange> allChangesMappedByAtlasEntityKey()\n    {\n        if (this.allChangesMappedByAtlasEntityKey == null)\n        {\n            this.allChangesMappedByAtlasEntityKey = changes()\n                    .map(featureChange -> Tuple.createTuple(AtlasEntityKey.from(featureChange),\n                            featureChange))\n                    .collect(Collectors.toMap(Tuple::getFirst, Tuple::getSecond));\n        }\n        return this.allChangesMappedByAtlasEntityKey;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bounds;\n    }\n\n    public int changeCount()\n    {\n        return this.featureChanges.size();\n    }\n\n    public Optional<FeatureChange> changeFor(final ItemType itemType, final Long identifier)\n    {\n        final AtlasEntityKey key = AtlasEntityKey.from(itemType, identifier);\n        if (!this.identifierToIndex.containsKey(key))\n        {\n            return Optional.empty();\n        }\n        return Optional.ofNullable(this.featureChanges.get(this.identifierToIndex.get(key)));\n    }\n\n    public Stream<FeatureChange> changes()\n    {\n        return this.featureChanges.stream();\n    }\n\n    public Stream<FeatureChange> changesFor(final ItemType itemType)\n    {\n        return this.identifierToIndex.keySet().stream()\n                .filter(tuple -> tuple.getFirst() == itemType)\n                .map(tuple -> this.featureChanges.get(this.identifierToIndex.get(tuple)));\n    }\n\n    /**\n     * An Object{@link #equals(Object)} implementation based on {@link #featureChanges} in the\n     * {@link Change} objects being compared.\n     *\n     * @param other\n     *            - the object to compare.\n     * @return boolean - true if the objects are equal\n     * @see Objects#equals(Object, Object)\n     * @see Object#equals(Object)\n     */\n    @Override\n    public boolean equals(final Object other)\n    {\n        // self check\n        if (this == other)\n        {\n            return true;\n        }\n        // null check\n        if (other == null)\n        {\n            return false;\n        }\n        // type check and cast\n        if (getClass() != other.getClass())\n        {\n            return false;\n        }\n\n        final Change that = (Change) other;\n\n        return Objects.equals(this.featureChanges, that.featureChanges);\n    }\n\n    public List<FeatureChange> getFeatureChanges()\n    {\n        return this.featureChanges;\n    }\n\n    public int getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public String getName()\n    {\n        return Optional.ofNullable(this.name).orElse(String.valueOf(this.getIdentifier()));\n    }\n\n    /**\n     * An Object{@link #hashCode()} implementation based on {@link #featureChanges} in the\n     * {@link Change}.\n     *\n     * @return - the hash code.\n     * @see Objects#hashCode(Object)\n     * @see Object#hashCode()\n     */\n    @Override\n    public int hashCode()\n    {\n        return Objects.hashCode(this.featureChanges);\n    }\n\n    /**\n     * Transform this {@link Change} into a pretty string. This will use the pretty strings for\n     * {@link CompleteEntity} classes that make up this {@link Change}'s constituent\n     * {@link FeatureChange}s.\n     *\n     * @param featureChangeFormat\n     *            the format type for the the constituent {@link FeatureChange}s\n     * @param completeEntityFormat\n     *            the format type for the constituent {@link CompleteEntity}s\n     * @return the pretty string\n     */\n    public String prettify(final PrettifyStringFormat featureChangeFormat,\n            final PrettifyStringFormat completeEntityFormat)\n    {\n        return this.prettify(featureChangeFormat, completeEntityFormat, true);\n    }\n\n    /**\n     * Transform this {@link Change} into a pretty string. This will use the pretty strings for\n     * {@link CompleteEntity} classes that make up this {@link Change}'s constituent\n     * {@link FeatureChange}s.\n     *\n     * @param featureChangeFormat\n     *            the format type for the the constituent {@link FeatureChange}s\n     * @param completeEntityFormat\n     *            the format type for the constituent {@link CompleteEntity}s\n     * @param truncate\n     *            whether or not to truncate long fields\n     * @return the pretty string\n     */\n    public String prettify(final PrettifyStringFormat featureChangeFormat,\n            final PrettifyStringFormat completeEntityFormat, final boolean truncate)\n    {\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName() + \" [\");\n        builder.append(\"\\n\");\n        for (final FeatureChange featureChange : this.featureChanges)\n        {\n            builder.append(\n                    featureChange.prettify(featureChangeFormat, completeEntityFormat, truncate));\n            builder.append(\"\\n\");\n        }\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    /**\n     * Save a JSON representation of that feature change.\n     *\n     * @param resource\n     *            The {@link WritableResource} to save the JSON to.\n     */\n    public void save(final WritableResource resource)\n    {\n        new ChangeGeoJsonSerializer().accept(this, resource);\n    }\n\n    /**\n     * Save a JSON representation of that change.\n     *\n     * @param resource\n     *            The {@link WritableResource} to save the JSON to.\n     * @param showDescription\n     *            whether or not to show the {@link ChangeDescription} for each component\n     *            {@link FeatureChange}\n     */\n    public void save(final WritableResource resource, final boolean showDescription)\n    {\n        new ChangeGeoJsonSerializer(true, showDescription).accept(this, resource);\n    }\n\n    public String summaryString()\n    {\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName() + \" [\");\n        builder.append(\"\\n\");\n        for (final ItemType itemType : ItemType.values())\n        {\n            for (final ChangeDescriptorType changeType : ChangeDescriptorType.values())\n            {\n                builder.append(itemType);\n                builder.append(\" had \");\n                builder.append(changeCountFor(itemType, changeType));\n                builder.append(\" \");\n                builder.append(changeType);\n                builder.append(\" changes\\n\");\n            }\n        }\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    public Map<ItemType, Map<ChangeDescriptorType, Map<String, AtomicLong>>> tagCountMap()\n    {\n        final Map<ItemType, Map<ChangeDescriptorType, Map<String, AtomicLong>>> tagMap = new EnumMap<>(\n                ItemType.class);\n        for (final ItemType itemType : ItemType.values())\n        {\n            final Map<ChangeDescriptorType, Map<String, AtomicLong>> descriptorMap = new EnumMap<>(\n                    ChangeDescriptorType.class);\n            for (final ChangeDescriptorType type : ChangeDescriptorType.values())\n            {\n                descriptorMap.put(type, new HashMap<>());\n            }\n            tagMap.put(itemType, descriptorMap);\n\n            // The first stream here gets all update changes with Tag changes; the second\n            // iterates over those changes and places them in the map based on their tag key\n            // and update type\n            this.featureChanges.stream().filter(change -> change.getItemType().equals(itemType)\n                    && change.explain().getChangeDescriptorType()\n                            .equals(ChangeDescriptorType.UPDATE)\n                    && change.explain().getChangeDescriptors().stream().anyMatch(\n                            descriptor -> descriptor.getName().equals(ChangeDescriptorName.TAG)))\n                    .forEach(\n                            change -> change.explain().getChangeDescriptors().stream()\n                                    .filter(changeDescriptor -> changeDescriptor.getName()\n                                            .equals(ChangeDescriptorName.TAG))\n                                    .forEach(changeDescriptor ->\n                                    {\n                                        final TagChangeDescriptor tagChangeDescriptor = (TagChangeDescriptor) changeDescriptor;\n                                        if (tagMap.get(itemType)\n                                                .get(tagChangeDescriptor.getChangeDescriptorType())\n                                                .containsKey(tagChangeDescriptor.getKey()))\n                                        {\n                                            tagMap.get(itemType)\n                                                    .get(tagChangeDescriptor\n                                                            .getChangeDescriptorType())\n                                                    .get(tagChangeDescriptor.getKey())\n                                                    .incrementAndGet();\n                                        }\n                                        else\n                                        {\n                                            tagMap.get(itemType)\n                                                    .get(tagChangeDescriptor\n                                                            .getChangeDescriptorType())\n                                                    .put(tagChangeDescriptor.getKey(),\n                                                            new AtomicLong(1));\n                                        }\n                                    }));\n        }\n        return tagMap;\n    }\n\n    public String toJson()\n    {\n        return new ChangeGeoJsonSerializer().convert(this);\n    }\n\n    public String toJson(final boolean showDescription)\n    {\n        return new ChangeGeoJsonSerializer(true, showDescription).convert(this);\n    }\n\n    public String toLineDelimitedFeatureChanges(final boolean sorted)\n    {\n        final StringBuilder builder = new StringBuilder();\n        final FeatureChangeGeoJsonSerializer serializer = new FeatureChangeGeoJsonSerializer(false);\n        final List<FeatureChange> sortedFeatureChanges = new ArrayList<>(this.getFeatureChanges());\n        if (sorted)\n        {\n            Collections.sort(sortedFeatureChanges);\n        }\n\n        for (final FeatureChange featureChange : sortedFeatureChanges)\n        {\n            builder.append(serializer.apply(featureChange) + \"\\n\");\n        }\n        return builder.toString();\n    }\n\n    public String toLineDelimitedFeatureChanges()\n    {\n        return toLineDelimitedFeatureChanges(false);\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringList split = new StringList();\n        final StringBuilder builder = new StringBuilder();\n        for (int index = 0; index < this.featureChanges.size(); index++)\n        {\n            split.add(index + \" - \" + this.featureChanges.get(index));\n        }\n        builder.append(\"[Change:\");\n        builder.append(System.lineSeparator());\n        builder.append(split.join(System.lineSeparator()));\n        builder.append(System.lineSeparator());\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    public Change withName(final String name)\n    {\n        this.name = name;\n        return this;\n    }\n\n    protected Change add(final FeatureChange featureChange)\n    {\n        final int currentIndex = this.featureChanges.size();\n        final AtlasEntityKey key = AtlasEntityKey.from(featureChange.getItemType(),\n                featureChange.getIdentifier());\n\n        FeatureChange chosen = featureChange;\n        if (!this.identifierToIndex.containsKey(key))\n        {\n            this.identifierToIndex.put(key, currentIndex);\n            this.featureChanges.add(featureChange);\n        }\n        else\n        {\n            final int existingIndex = this.identifierToIndex.get(key);\n            final FeatureChange existing = this.featureChanges.get(existingIndex);\n            logger.trace(\n                    \"Change already has a similar feature change. Triggered a merge attempt! Existing: {}; New: {}\",\n                    existing, featureChange);\n            chosen = existing.merge(featureChange);\n            this.featureChanges.set(existingIndex, chosen);\n        }\n        final Rectangle featureBounds = chosen.bounds();\n        if (this.bounds != null)\n        {\n            this.bounds = featureBounds != null ? this.bounds.combine(featureBounds) : this.bounds;\n        }\n        else\n        {\n            this.bounds = featureBounds;\n        }\n\n        return this;\n    }\n\n    protected Change addAll(final FeatureChange... featureChanges)\n    {\n        Arrays.stream(featureChanges).forEach(this::add);\n        return this;\n    }\n\n    private long changeCountFor(final ItemType itemType, final ChangeDescriptorType changeType)\n    {\n        return this.featureChanges.stream().filter(change -> change.getItemType().equals(itemType)\n                && change.explain().getChangeDescriptorType().equals(changeType)).count();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeArea.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Area} that references a {@link ChangeAtlas}. That {@link Area} makes sure that all the\n * parent {@link Relation}s are {@link ChangeRelation}s.\n * <p>\n * NOSONAR here to avoid \"Subclasses that add fields should override \"equals\" (squid:S2160)\". Here\n * the equals from the parent works.\n *\n * @author matthieun\n */\npublic class ChangeArea extends Area // NOSONAR\n{\n    private static final long serialVersionUID = -5658471275390043045L;\n\n    // At most one of those two can be null. Not using Optional here as it is not Serializable.\n    private final Area source;\n    private final Area override;\n\n    // Computing Parent Relations is very expensive, so we cache it here.\n    private transient Set<Relation> relationsCache;\n    private transient Object relationsCacheLock = new Object();\n\n    protected ChangeArea(final ChangeAtlas atlas, final Area source, final Area override)\n    {\n        super(atlas);\n        this.source = source;\n        this.override = override;\n    }\n\n    @Override\n    public Polygon asPolygon()\n    {\n        return attribute(Area::asPolygon, \"polygon\");\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return attribute(Area::getIdentifier, \"identifier\");\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return attribute(Area::getTags, \"tags\");\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        final Supplier<Set<Relation>> creator = () -> ChangeEntity\n                .filterRelations(attribute(AtlasEntity::relations, \"relations\"), getChangeAtlas());\n        return ChangeEntity.getOrCreateCache(this.relationsCache,\n                cache -> this.relationsCache = cache, this.relationsCacheLock, creator);\n    }\n\n    private <T extends Object> T attribute(final Function<Area, T> memberExtractor,\n            final String name)\n    {\n        return ChangeEntity.getAttributeOrBackup(this.source, this.override, memberExtractor, name);\n    }\n\n    private ChangeAtlas getChangeAtlas()\n    {\n        return (ChangeAtlas) getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeAtlas.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.BiFunction;\nimport java.util.function.Consumer;\nimport java.util.function.LongFunction;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.AbstractAtlas;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.validators.AtlasValidator;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\n\n/**\n * Shallow atlas view that applies a set of change objects and presents the result without updating\n * the whole dataset.\n *\n * @author matthieun\n */\npublic class ChangeAtlas extends AbstractAtlas // NOSONAR\n{\n    private static final long serialVersionUID = -5741815439928958165L;\n    private static final ChangeRelation NULL_PLACEHOLDER_RELATION = new ChangeRelation(null, null,\n            null);\n    private static final ChangeNode NULL_PLACEHOLDER_NODE = new ChangeNode(null, null, null);\n    private static final ChangeEdge NULL_PLACEHOLDER_EDGE = new ChangeEdge(null, null, null);\n    private static final ChangeArea NULL_PLACEHOLDER_AREA = new ChangeArea(null, null, null);\n    private static final ChangeLine NULL_PLACEHOLDER_LINE = new ChangeLine(null, null, null);\n    private static final ChangePoint NULL_PLACEHOLDER_POINT = new ChangePoint(null, null, null);\n\n    private final Change change;\n    private final Atlas source;\n    private String name;\n    private boolean validated = false;\n\n    private transient Rectangle bounds;\n    private transient AtlasMetaData metaData;\n    private transient Long numberOfNodes;\n    private transient Long numberOfEdges;\n    private transient Long numberOfAreas;\n    private transient Long numberOfLines;\n    private transient Long numberOfPoints;\n    private transient Long numberOfRelations;\n\n    // Computing relations in ChangeAtlas is very expensive, so we cache them here.\n    private transient Map<Long, ChangeRelation> relationsCache;\n    private transient Object relationsCacheLock = new Object();\n\n    // Computing relations in ChangeAtlas is very expensive, so we cache them here.\n    private transient Map<Long, ChangeNode> nodesCache;\n    private transient Object nodesCacheLock = new Object();\n\n    // Computing relations in ChangeAtlas is very expensive, so we cache them here.\n    private transient Map<Long, ChangeEdge> edgesCache;\n    private transient Object edgesCacheLock = new Object();\n\n    // Computing relations in ChangeAtlas is very expensive, so we cache them here.\n    private transient Map<Long, ChangeArea> areasCache;\n    private transient Object areasCacheLock = new Object();\n\n    // Computing relations in ChangeAtlas is very expensive, so we cache them here.\n    private transient Map<Long, ChangeLine> linesCache;\n    private transient Object linesCacheLock = new Object();\n\n    // Computing relations in ChangeAtlas is very expensive, so we cache them here.\n    private transient Map<Long, ChangePoint> pointsCache;\n    private transient Object pointsCacheLock = new Object();\n\n    private static void checkChanges(final Change... changes)\n    {\n        if (changes == null)\n        {\n            throw new CoreException(\"Change cannot be null in a ChangeAtlas.\");\n        }\n        if (changes.length < 1)\n        {\n            throw new CoreException(\"ChangeAtlas has to have at least one Change.\");\n        }\n    }\n\n    private static void checkSource(final Atlas source)\n    {\n        if (source == null)\n        {\n            throw new CoreException(\"Source Atlas cannot be null in a ChangeAtlas.\");\n        }\n    }\n\n    public ChangeAtlas(final Atlas source, final Change... changes)\n    {\n        this(source, \"\", changes);\n    }\n\n    public ChangeAtlas(final Atlas source, final String name, final Change... changes)\n    {\n        checkSource(source);\n        checkChanges(changes);\n        this.change = Change.merge(changes);\n        this.source = source;\n        this.name = name == null || name.isEmpty() ? source.getName() : name;\n        this.validate();\n    }\n\n    public ChangeAtlas(final Change... changes)\n    {\n        this(\"\", changes);\n    }\n\n    public ChangeAtlas(final String name, final Change... changes)\n    {\n        checkChanges(changes);\n        final Change changeInternal = Change.merge(changes);\n        boolean valid = false;\n        Atlas sourceInternal = null;\n        FeatureChange dummy = null;\n        for (final FeatureChange featureChange : changeInternal.getFeatureChanges())\n        {\n            if (featureChange.getChangeType() == ChangeType.ADD)\n            {\n                if (!featureChange.afterViewIsFull())\n                {\n                    throw new CoreException(\n                            \"ChangeAtlas needs all ADD featureChanges to be full (no partial after view) to exist with no source Atlas.\");\n                }\n                if (sourceInternal == null)\n                {\n                    final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n                    builder.addPoint(-1L, Location.CENTER, Maps.hashMap());\n                    sourceInternal = builder.get();\n                    dummy = FeatureChange\n                            .remove(CompletePoint.shallowFrom(sourceInternal.point(-1L)));\n                }\n                valid = true;\n            }\n        }\n        if (valid)\n        {\n            final ChangeBuilder changeBuilder = new ChangeBuilder();\n            changeBuilder.addAll(changeInternal.changes());\n            changeBuilder.add(dummy);\n            this.change = changeBuilder.get();\n            this.source = sourceInternal;\n            this.name = name == null || name.isEmpty() ? sourceInternal.getName() : name;\n            new AtlasValidator(this).validate();\n        }\n        else\n        {\n            throw new CoreException(\n                    \"ChangeAtlas needs at least a full ADD featureChange to exist with no source Atlas.\");\n        }\n    }\n\n    @Override\n    public Area area(final long identifier)\n    {\n        final Supplier<ChangeArea> creator = () -> entityFor(identifier, ItemType.AREA,\n                () -> this.source.area(identifier),\n                (sourceEntity, overrideEntity) -> new ChangeArea(this, (Area) sourceEntity,\n                        (Area) overrideEntity));\n        return getFromCacheOrCreate(this.areasCache, cache -> this.areasCache = cache,\n                this.areasCacheLock, NULL_PLACEHOLDER_AREA, identifier, creator);\n    }\n\n    @Override\n    public Iterable<Area> areas()\n    {\n        return entitiesFor(ItemType.AREA, this::area, this.source.areas());\n    }\n\n    @Override\n    public synchronized Rectangle bounds()\n    {\n        if (this.bounds == null)\n        {\n            // Stream it to make sure the \"Iterable\" signature is used here (vs. Located, which\n            // would stack overflow).\n            this.bounds = Rectangle.forLocated(Iterables.stream(this));\n        }\n        return this.bounds;\n    }\n\n    @Override\n    public Edge edge(final long identifier)\n    {\n        /*\n         * If the edge was not found in this atlas, return null. Additionally, we then check to see\n         * if this edge is missing a start or end node (which may have been removed by a\n         * FeatureChange). In this case, we also want to \"remove\" the edge by returning null.\n         */\n        final Predicate<ChangeEdge> nullableEdge = edge -> edge.start() == null\n                || edge.end() == null;\n\n        final Supplier<ChangeEdge> creator = () -> entityFor(identifier, ItemType.EDGE,\n                () -> this.source.edge(identifier),\n                (sourceEntity, overrideEntity) -> new ChangeEdge(this, (Edge) sourceEntity,\n                        (Edge) overrideEntity));\n\n        return getFromCacheOrCreate(this.edgesCache, cache -> this.edgesCache = cache,\n                this.edgesCacheLock, NULL_PLACEHOLDER_EDGE, identifier, creator,\n                Optional.of(nullableEdge));\n    }\n\n    @Override\n    public Iterable<Edge> edges()\n    {\n        return entitiesFor(ItemType.EDGE, this::edge, this.source.edges());\n    }\n\n    @Override\n    public String getName()\n    {\n        if (this.name == null)\n        {\n            return super.getName();\n        }\n        return this.name;\n    }\n\n    @Override\n    public Line line(final long identifier)\n    {\n        final Supplier<ChangeLine> creator = () -> entityFor(identifier, ItemType.LINE,\n                () -> this.source.line(identifier),\n                (sourceEntity, overrideEntity) -> new ChangeLine(this, (Line) sourceEntity,\n                        (Line) overrideEntity));\n        return getFromCacheOrCreate(this.linesCache, cache -> this.linesCache = cache,\n                this.linesCacheLock, NULL_PLACEHOLDER_LINE, identifier, creator);\n    }\n\n    @Override\n    public Iterable<Line> lines()\n    {\n        return entitiesFor(ItemType.LINE, this::line, this.source.lines());\n    }\n\n    @Override\n    public synchronized AtlasMetaData metaData()\n    {\n        if (this.metaData == null)\n        {\n            AtlasMetaData sourceMetaData = this.source.metaData();\n            if (sourceMetaData == null)\n            {\n                sourceMetaData = new AtlasMetaData();\n            }\n            final AtlasSize size = new AtlasSize(this);\n            this.metaData = sourceMetaData.copyWithNewSize(size).copyWithNewOriginal(false);\n        }\n        return this.metaData;\n    }\n\n    @Override\n    public Node node(final long identifier)\n    {\n        final Supplier<ChangeNode> creator = () -> entityFor(identifier, ItemType.NODE,\n                () -> this.source.node(identifier),\n                (sourceEntity, overrideEntity) -> new ChangeNode(this, (Node) sourceEntity,\n                        (Node) overrideEntity));\n        return getFromCacheOrCreate(this.nodesCache, cache -> this.nodesCache = cache,\n                this.nodesCacheLock, NULL_PLACEHOLDER_NODE, identifier, creator);\n    }\n\n    @Override\n    public Iterable<Node> nodes()\n    {\n        return entitiesFor(ItemType.NODE, this::node, this.source.nodes());\n    }\n\n    @Override\n    public synchronized long numberOfAreas()\n    {\n        if (this.numberOfAreas == null)\n        {\n            this.numberOfAreas = Iterables.size(areas());\n        }\n        return this.numberOfAreas;\n    }\n\n    @Override\n    public synchronized long numberOfEdges()\n    {\n        if (this.numberOfEdges == null)\n        {\n            this.numberOfEdges = Iterables.size(edges());\n        }\n        return this.numberOfEdges;\n    }\n\n    @Override\n    public synchronized long numberOfLines()\n    {\n        if (this.numberOfLines == null)\n        {\n            this.numberOfLines = Iterables.size(lines());\n        }\n        return this.numberOfLines;\n    }\n\n    @Override\n    public synchronized long numberOfNodes()\n    {\n        if (this.numberOfNodes == null)\n        {\n            this.numberOfNodes = Iterables.size(nodes());\n        }\n        return this.numberOfNodes;\n    }\n\n    @Override\n    public synchronized long numberOfPoints()\n    {\n        if (this.numberOfPoints == null)\n        {\n            this.numberOfPoints = Iterables.size(points());\n        }\n        return this.numberOfPoints;\n    }\n\n    @Override\n    public synchronized long numberOfRelations()\n    {\n        if (this.numberOfRelations == null)\n        {\n            this.numberOfRelations = Iterables.size(relations());\n        }\n        return this.numberOfRelations;\n    }\n\n    @Override\n    public Point point(final long identifier)\n    {\n        final Supplier<ChangePoint> creator = () -> entityFor(identifier, ItemType.POINT,\n                () -> this.source.point(identifier),\n                (sourceEntity, overrideEntity) -> new ChangePoint(this, (Point) sourceEntity,\n                        (Point) overrideEntity));\n        return getFromCacheOrCreate(this.pointsCache, cache -> this.pointsCache = cache,\n                this.pointsCacheLock, NULL_PLACEHOLDER_POINT, identifier, creator);\n    }\n\n    @Override\n    public Iterable<Point> points()\n    {\n        return entitiesFor(ItemType.POINT, this::point, this.source.points());\n    }\n\n    @Override\n    public Relation relation(final long identifier)\n    {\n        /*\n         * If the relation was not found in this atlas, return null. Additionally, we check to see\n         * if the relation has no members. If so, it is considered empty and is dropped from the\n         * atlas. This logic, combined with the logic in ChangeRelation.membersFor, will\n         * automatically handle removing non-empty but shallow relations as well.\n         */\n        final Predicate<ChangeRelation> nullableRelation = relationCandidate -> relationCandidate\n                .members().isEmpty();\n\n        final Supplier<ChangeRelation> creator = () -> entityFor(identifier, ItemType.RELATION,\n                () -> this.source.relation(identifier),\n                (sourceEntity, overrideEntity) -> new ChangeRelation(this, (Relation) sourceEntity,\n                        (Relation) overrideEntity));\n\n        return getFromCacheOrCreate(this.relationsCache, cache -> this.relationsCache = cache,\n                this.relationsCacheLock, NULL_PLACEHOLDER_RELATION, identifier, creator,\n                Optional.of(nullableRelation));\n    }\n\n    @Override\n    public Iterable<Relation> relations()\n    {\n        return entitiesFor(ItemType.RELATION, this::relation, this.source.relations());\n    }\n\n    public void validate()\n    {\n        if (!this.validated)\n        {\n            new AtlasValidator(this).validate();\n            this.validated = true;\n        }\n    }\n\n    public ChangeAtlas withName(final String name)\n    {\n        this.name = name;\n        return this;\n    }\n\n    /**\n     * Get the {@link Iterable} of entities corresponding to the right type. This takes care of\n     * surfacing only the ones not deleted, or if added or modified, the new ones.\n     *\n     * @param <M>\n     *            The {@link AtlasEntity} subclass.\n     * @param itemType\n     *            The type of entity\n     * @param entityForIdentifier\n     *            A function that creates a new object from its identifier.\n     * @param sourceEntities\n     *            All the corresponding entities from the source atlas.\n     * @return All the corresponding entities in this atlas.\n     */\n    private <M extends AtlasEntity> Iterable<M> entitiesFor(final ItemType itemType,\n            final LongFunction<M> entityForIdentifier, final Iterable<M> sourceEntities)\n    {\n        return new MultiIterable<>(\n                this.change.getFeatureChanges().stream()\n                        .filter(featureChange -> featureChange.getItemType() == itemType\n                                && featureChange.getChangeType() == ChangeType.ADD)\n                        .map(featureChange -> entityForIdentifier\n                                .apply(featureChange.getIdentifier()))\n                        .filter(Objects::nonNull).collect(Collectors.toList()),\n                Iterables.stream(sourceEntities)\n                        .filter(entity -> !this.change.changeFor(itemType, entity.getIdentifier())\n                                .isPresent())\n                        .map(entity -> entityForIdentifier.apply(entity.getIdentifier()))\n                        .filter(Objects::nonNull).collect());\n    }\n\n    /**\n     * Build a \"Change\" feature for this {@link ChangeAtlas} by querying the change object for\n     * matching features. Use the source atlas otherwise.\n     *\n     * @param <M>\n     *            The type of the feature to be built. Has to extend {@link AtlasEntity}.\n     * @param identifier\n     *            The feature identifier\n     * @param itemType\n     *            The feature type\n     * @param sourceSupplier\n     *            A supplier function that creates the entity from the source. Can return null if\n     *            the source atlas does not contain that feature.\n     * @param entityConstructorFromSource\n     *            A function that takes the updated feature from the {@link Change} object, and\n     *            constructs a new ChangeItem from it, that attaches to this Atlas.\n     * @return The ChangeItem that corresponds to that feature. Can be a ChangeNode, ChangeEdge,\n     *         etc. It links back to this Atlas.\n     */\n    private <M extends AtlasEntity> M entityFor(final long identifier, final ItemType itemType,\n            final Supplier<AtlasEntity> sourceSupplier,\n            final BiFunction<AtlasEntity, AtlasEntity, M> entityConstructorFromSource)\n    {\n        final Optional<FeatureChange> itemChangeOption = this.change.changeFor(itemType,\n                identifier);\n        final AtlasEntity sourceItem = sourceSupplier.get();\n        if (itemChangeOption.isPresent())\n        {\n            // That Entity is affected by a change\n            final FeatureChange itemChange = itemChangeOption.get();\n            if (ChangeType.REMOVE == itemChange.getChangeType())\n            {\n                return null;\n            }\n            else\n            {\n                // Create the ChangeItem from the change object (the override). The source item\n                // might be null (In case of an ADD which is a create and not a modify)\n                return entityConstructorFromSource.apply(sourceItem, itemChange.getAfterView());\n            }\n        }\n        else\n        {\n            if (sourceItem != null)\n            {\n                // Create the ChangeItem from the untouched source; the override is null\n                return entityConstructorFromSource.apply(sourceItem, null);\n            }\n        }\n        return null;\n    }\n\n    private <E> E getFromCacheOrCreate(final Map<Long, E> cache,\n            final Consumer<Map<Long, E>> cacheSetter, final Object lock, final E nullPlaceholder,\n            final Long identifier, final Supplier<E> creator)\n    {\n        return getFromCacheOrCreate(cache, cacheSetter, lock, nullPlaceholder, identifier, creator,\n                Optional.empty());\n    }\n\n    /**\n     * @param <E>\n     *            The type of the entity returned. Intended to be a {@link ChangeArea},\n     *            {@link ChangeNode}, etc.\n     * @param cache\n     *            The cache to use to retrieve the entity\n     * @param cacheSetter\n     *            A function that will set the cache not null in case it was null.\n     * @param lock\n     *            The synchronization lock used for that specific type\n     * @param nullPlaceholder\n     *            What placeholder in the cache specifies a null object at some identifier\n     * @param identifier\n     *            The identifier to return\n     * @param creator\n     *            A {@link Supplier} that provides the correct object for the specified identifier\n     *            above\n     * @param entityNullable\n     *            A predicate that decides if a non null object should still return null. Example a\n     *            relation with no members.\n     * @return\n     */\n    private <E> E getFromCacheOrCreate(final Map<Long, E> cache,\n            final Consumer<Map<Long, E>> cacheSetter, final Object lock, final E nullPlaceholder,\n            final Long identifier, final Supplier<E> creator,\n            final Optional<Predicate<E>> entityNullable)\n    {\n        // Get or create the cache (in case it was null)\n        final Map<Long, E> cacheIn = ChangeEntity.getOrCreateCache(cache, cacheSetter, lock,\n                ConcurrentHashMap::new);\n        E result;\n        if (cacheIn.containsKey(identifier))\n        {\n            // Retrieve an existing object\n            result = cacheIn.get(identifier);\n            result = result == nullPlaceholder ? null : result;\n        }\n        else\n        {\n            // Create a new object\n            result = creator.get();\n            if (result == null || entityNullable.isPresent() && entityNullable.get().test(result))\n            {\n                // If the created object is null, or nullable, use the null placeholder\n                cacheIn.put(identifier, nullPlaceholder);\n                result = null;\n            }\n            else\n            {\n                cacheIn.put(identifier, result);\n            }\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeBuilder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Arrays;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.change.validators.ChangeValidator;\n\n/**\n * Construct a {@link Change}. This is a gatekeeper that ensures validation.\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class ChangeBuilder\n{\n    private final Change change;\n    private boolean open;\n\n    /**\n     * A factory-method to construct a new {@link ChangeBuilder}. Constructs a {@code new}\n     * {@link ChangeBuilder} with default values.\n     *\n     * @return - a new ChangeBuilder.\n     */\n    public static ChangeBuilder newInstance()\n    {\n        return new ChangeBuilder();\n    }\n\n    public ChangeBuilder()\n    {\n        this.change = new Change();\n        this.open = true;\n    }\n\n    /**\n     * @param featureChange\n     *            - the {@link FeatureChange} to add to the builder.\n     * @return ChangeBuilder - returns itself to allow fluency in calls.\n     */\n    public synchronized ChangeBuilder add(final FeatureChange featureChange)\n    {\n        if (!this.open)\n        {\n            throw new CoreException(\n                    \"Cannot append to a Change object that has already been validated\");\n        }\n        this.change.add(featureChange);\n\n        return this;\n    }\n\n    /**\n     * @see #addAll(Stream)\n     * @param featureChanges\n     *            - The featureChanges to add.\n     * @return ChangeBuilder - returns itself to allow fluency in calls.\n     */\n    public synchronized ChangeBuilder addAll(final FeatureChange... featureChanges)\n    {\n        return addAll(Arrays.stream(featureChanges));\n    }\n\n    /**\n     * @see #addAll(Stream)\n     * @param featureChanges\n     *            - The featureChanges to add.\n     * @return ChangeBuilder - returns itself to allow fluency in calls.\n     */\n    public synchronized ChangeBuilder addAll(final Iterable<FeatureChange> featureChanges)\n    {\n        return addAll(StreamSupport.stream(featureChanges.spliterator(), false));\n    }\n\n    /**\n     * Iteratively {@link #add(FeatureChange)} all the FeatureChanges.\n     *\n     * @param featureChanges\n     *            - The featureChanges to add.\n     * @return ChangeBuilder - returns itself to allow fluency in calls.\n     */\n    public synchronized ChangeBuilder addAll(final Stream<FeatureChange> featureChanges)\n    {\n        featureChanges.forEach(this::add);\n        return this;\n    }\n\n    public synchronized Change get()\n    {\n        new ChangeValidator(this.change).validate();\n        this.open = false;\n        return this.change;\n    }\n\n    public synchronized int peekNumberOfChanges()\n    {\n        return this.change.changeCount();\n    }\n\n    /**\n     * Assign a name to the change being constructed.\n     *\n     * @param name\n     *            - a name for the change.\n     * @return ChangeBuilder - returns itself to allow fluency in calls.\n     */\n    public ChangeBuilder withName(final String name)\n    {\n        this.change.withName(name);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Edge} that references a {@link ChangeAtlas}. That {@link Edge} makes sure that all the\n * connected {@link Node}s are {@link ChangeNode}s, and that all the parent {@link Relation}s are\n * {@link ChangeRelation}s.\n * <p>\n * NOSONAR here to avoid \"Subclasses that add fields should override \"equals\" (squid:S2160)\". Here\n * the equals from the parent works.\n *\n * @author matthieun\n */\npublic class ChangeEdge extends Edge // NOSONAR\n{\n    private static final long serialVersionUID = -5658471275390043045L;\n\n    // At most one of those two can be null. Not using Optional here as it is not Serializable.\n    private final Edge source;\n    private final Edge override;\n\n    // Computing Parent Relations is very expensive, so we cache it here.\n    private transient Set<Relation> relationsCache;\n    private transient Object relationsCacheLock = new Object();\n\n    // Computing Start Node is very expensive, so we cache it here.\n    private transient Node startNodeCache;\n    private transient Object startNodeCacheLock = new Object();\n\n    // Computing End Node is very expensive, so we cache it here.\n    private transient Node endNodeCache;\n    private transient Object endNodeCacheLock = new Object();\n\n    protected ChangeEdge(final ChangeAtlas atlas, final Edge source, final Edge override)\n    {\n        super(atlas);\n        this.source = source;\n        this.override = override;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return attribute(Edge::asPolyLine, \"polyLine\");\n    }\n\n    @Override\n    public Node end()\n    {\n        final Supplier<Node> creator = () -> getChangeAtlas().node(endNodeIdentifier());\n        return ChangeEntity.getOrCreateCache(this.endNodeCache, cache -> this.endNodeCache = cache,\n                this.endNodeCacheLock, creator);\n    }\n\n    public long endNodeIdentifier()\n    {\n        return attribute(Edge::end, \"end node\").getIdentifier();\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return attribute(Edge::getIdentifier, \"identifier\");\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return attribute(Edge::getTags, \"tags\");\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        final Supplier<Set<Relation>> creator = () -> ChangeEntity\n                .filterRelations(attribute(AtlasEntity::relations, \"relations\"), getChangeAtlas());\n        return ChangeEntity.getOrCreateCache(this.relationsCache,\n                cache -> this.relationsCache = cache, this.relationsCacheLock, creator);\n    }\n\n    @Override\n    public Node start()\n    {\n        final Supplier<Node> creator = () -> getChangeAtlas().node(startNodeIdentifier());\n        return ChangeEntity.getOrCreateCache(this.startNodeCache,\n                cache -> this.startNodeCache = cache, this.startNodeCacheLock, creator);\n    }\n\n    public long startNodeIdentifier()\n    {\n        return attribute(Edge::start, \"start node\").getIdentifier();\n    }\n\n    private <T extends Object> T attribute(final Function<Edge, T> memberExtractor,\n            final String name)\n    {\n        return ChangeEntity.getAttributeOrBackup(this.source, this.override, memberExtractor, name);\n    }\n\n    private ChangeAtlas getChangeAtlas()\n    {\n        return (ChangeAtlas) getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeEntity.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Utility class for Change entities: ChangeNode, ChangeEdge, etc.\n *\n * @author matthieun\n */\npublic final class ChangeEntity\n{\n    /**\n     * Filter parent relations that are mentioned in a ChangeEntity to only those that are not null.\n     *\n     * @param listed\n     *            The relation set to filter\n     * @param parent\n     *            The parent {@link ChangeAtlas}\n     * @return the set of {@link ChangeRelation} that are not null.\n     */\n    static Set<Relation> filterRelations(final Set<Relation> listed, final ChangeAtlas parent)\n    {\n        return listed.stream().map(relation -> parent.relation(relation.getIdentifier()))\n                .filter(Objects::nonNull).collect(Collectors.toSet());\n    }\n\n    /**\n     * Get either the attribute asked from the source entity\n     *\n     * @param source\n     *            The source entity\n     * @param memberExtractor\n     *            Extract the member attribute from that entity\n     * @param <M>\n     *            The entity type that some object will be extracted from\n     * @param <T>\n     *            The object type that will be extracted and returned\n     * @return The corresponding attribute\n     */\n    static <T, M extends AtlasEntity> T getAttribute(final M source,\n            final Function<M, T> memberExtractor)\n    {\n        T result = null;\n        if (source != null)\n        {\n            result = memberExtractor.apply(source);\n        }\n        return result;\n    }\n\n    /**\n     * Get all the available attributes asked from the change entity (override), and/or from the\n     * backup entity.\n     *\n     * @param name\n     *            The name of the extraction operation\n     * @param source\n     *            The source entity\n     * @param override\n     *            The change entity (override)\n     * @param memberExtractor\n     *            Extract the member attribute from that entity\n     * @param <M>\n     *            The entity type to extract information from\n     * @param <T>\n     *            The type that will be extracted from the entity\n     * @return The corresponding attribute list. Will not be empty.\n     */\n    static <T, M extends AtlasEntity> List<T> getAttributeAndOptionallyBackup(final M source,\n            final M override, final Function<M, T> memberExtractor, final String name)\n    {\n        final List<T> result = new ArrayList<>();\n        if (override != null)\n        {\n            final T member = memberExtractor.apply(override);\n            if (member != null)\n            {\n                result.add(member);\n            }\n\n        }\n        if (source != null)\n        {\n            final T member = memberExtractor.apply(source);\n            if (member != null)\n            {\n                result.add(member);\n            }\n        }\n        if (result.isEmpty())\n        {\n            throw new CoreException(\n                    \"Could not retrieve attribute \\\"{}\\\" from override nor source!\\noverride: {}\\nsource:{}\",\n                    name, override, source);\n        }\n        return result;\n    }\n\n    /**\n     * Get either the attribute asked from the change entity (override), or from the backup entity\n     * if unavailable.\n     *\n     * @param name\n     *            The name of the extraction operation\n     * @param source\n     *            The source entity\n     * @param override\n     *            The change entity (override)\n     * @param memberExtractor\n     *            Extract the member attribute from that entity\n     * @param <M>\n     *            The atlas entity type that will be extracted from\n     * @param <T>\n     *            The expected return type\n     * @return The corresponding attribute\n     */\n    static <T, M extends AtlasEntity> T getAttributeOrBackup(final M source, final M override,\n            final Function<M, T> memberExtractor, final String name)\n    {\n        T result = null;\n        if (override != null)\n        {\n            result = memberExtractor.apply(override);\n        }\n        if (result == null && source != null)\n        {\n            result = memberExtractor.apply(source);\n        }\n        if (result == null)\n        {\n            throw new CoreException(\n                    \"Could not retrieve attribute \\\"{}\\\" from override nor source!\\noverride: {}\\nsource:{}\",\n                    name, override, source);\n        }\n        return result;\n    }\n\n    /**\n     * @param <V>\n     *            The cached value type\n     * @param fieldCache\n     *            The cache\n     * @param cacheSetter\n     *            A function that will set the cache not null in case it was null.\n     * @param lock\n     *            The synchronization lock to access the cache\n     * @param creator\n     *            The original creator of the type if the cache does not contain it.\n     * @return Either the cached value or the freshly created one.\n     */\n    static <V> V getOrCreateCache(final V fieldCache, final Consumer<V> cacheSetter,\n            final Object lock, final Supplier<V> creator)\n    {\n        V localRelationCache = fieldCache;\n        if (localRelationCache == null)\n        {\n            synchronized (lock) // NOSONAR\n            {\n                localRelationCache = fieldCache; // NOSONAR\n                if (localRelationCache == null) // NOSONAR\n                {\n                    localRelationCache = creator.get();\n                    cacheSetter.accept(localRelationCache);\n                }\n            }\n        }\n        return localRelationCache;\n    }\n\n    private ChangeEntity()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeLine.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Line} that references a {@link ChangeAtlas}. That {@link Line} makes sure that all the\n * parent {@link Relation}s are {@link ChangeRelation}s.\n * <p>\n * NOSONAR here to avoid \"Subclasses that add fields should override \"equals\" (squid:S2160)\". Here\n * the equals from the parent works.\n *\n * @author matthieun\n */\npublic class ChangeLine extends Line // NOSONAR\n{\n    private static final long serialVersionUID = -5658471275390043045L;\n\n    // At most one of those two can be null. Not using Optional here as it is not Serializable.\n    private final Line source;\n    private final Line override;\n\n    // Computing Parent Relations is very expensive, so we cache it here.\n    private transient Set<Relation> relationsCache;\n    private transient Object relationsCacheLock = new Object();\n\n    protected ChangeLine(final ChangeAtlas atlas, final Line source, final Line override)\n    {\n        super(atlas);\n        this.source = source;\n        this.override = override;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return attribute(Line::asPolyLine, \"polyLine\");\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return attribute(Line::getIdentifier, \"identifier\");\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return attribute(Line::getTags, \"tags\");\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        final Supplier<Set<Relation>> creator = () -> ChangeEntity\n                .filterRelations(attribute(AtlasEntity::relations, \"relations\"), getChangeAtlas());\n        return ChangeEntity.getOrCreateCache(this.relationsCache,\n                cache -> this.relationsCache = cache, this.relationsCacheLock, creator);\n    }\n\n    private <T extends Object> T attribute(final Function<Line, T> memberExtractor,\n            final String name)\n    {\n        return ChangeEntity.getAttributeOrBackup(this.source, this.override, memberExtractor, name);\n    }\n\n    private ChangeAtlas getChangeAtlas()\n    {\n        return (ChangeAtlas) getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeNode.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link Node} that references a {@link ChangeAtlas}. That {@link Node} makes sure that all the\n * connected {@link Edge}s are {@link ChangeEdge}s, and that all the parent {@link Relation}s are\n * {@link ChangeRelation}s.\n * <p>\n * NOSONAR here to avoid \"Subclasses that add fields should override \"equals\" (squid:S2160)\". Here\n * the equals from the parent works.\n *\n * @author matthieun\n */\npublic class ChangeNode extends Node // NOSONAR\n{\n    private static final long serialVersionUID = 4353679260691518275L;\n    private static final Logger logger = LoggerFactory.getLogger(ChangeNode.class);\n\n    private final Node source;\n    private final Node override;\n\n    // Computing Parent Relations is very expensive, so we cache it here.\n    private transient Set<Relation> relationsCache;\n    private transient Object relationsCacheLock = new Object();\n\n    // Computing In Edges is very expensive, so we cache it here.\n    private transient SortedSet<Edge> inEdgesCache;\n    private transient Object inEdgesCacheLock = new Object();\n\n    // Computing Out Edges is very expensive, so we cache it here.\n    private transient SortedSet<Edge> outEdgesCache;\n    private transient Object outEdgesCacheLock = new Object();\n\n    protected ChangeNode(final ChangeAtlas atlas, final Node source, final Node override)\n    {\n        super(atlas);\n        this.source = source;\n        this.override = override;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return attribute(Node::getIdentifier, \"identifier\");\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return attribute(Node::getLocation, \"location\");\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return attribute(Node::getTags, \"tags\");\n    }\n\n    public SortedSet<Long> inEdgeIdentifiers()\n    {\n        final List<SortedSet<Edge>> inEdgeSetList = allAvailableAttributes(Node::inEdges,\n                \"in edges\");\n        Set<Long> mergedIdentifiers = inEdgeSetList.stream().flatMap(Set::stream)\n                .map(Edge::getIdentifier)\n                .filter(edgeIdentifier -> getChangeAtlas().edge(edgeIdentifier) != null)\n                .collect(Collectors.toSet());\n\n        if (this.override != null)\n        {\n            /*\n             * We need to filter any identifiers that were marked as explicitly excluded in the\n             * override. It is possible that the atlas view used to generate a FeatureChange context\n             * will differ from the atlas on which the FeatureChange will be applied. In that case,\n             * we must distinguish between two kinds of missing edge identifiers: 1) those that are\n             * missing because a shard simply couldn't see them and 2) those that are missing\n             * because a FeatureChange explicitly removed them.\n             */\n            final CompleteNode completeNodeOverride = (CompleteNode) this.override;\n            mergedIdentifiers = mergedIdentifiers.stream()\n                    .filter(edgeIdentifier -> !completeNodeOverride\n                            .explicitlyExcludedInEdgeIdentifiers().contains(edgeIdentifier))\n                    .collect(Collectors.toSet());\n        }\n\n        return new TreeSet<>(mergedIdentifiers);\n    }\n\n    @Override\n    public SortedSet<Edge> inEdges()\n    {\n        final Supplier<SortedSet<Edge>> creator = () -> inEdgeIdentifiers().stream()\n                .map(edgeIdentifier -> getChangeAtlas().edge(edgeIdentifier))\n                .collect(Collectors.toCollection(TreeSet::new));\n\n        return ChangeEntity.getOrCreateCache(this.inEdgesCache, cache -> this.inEdgesCache = cache,\n                this.inEdgesCacheLock, creator);\n    }\n\n    public SortedSet<Long> outEdgeIdentifiers()\n    {\n        final List<SortedSet<Edge>> outEdgeSetList = allAvailableAttributes(Node::outEdges,\n                \"out edges\");\n        Set<Long> mergedIdentifiers = outEdgeSetList.stream().flatMap(Set::stream)\n                .map(Edge::getIdentifier)\n                .filter(edgeIdentifier -> getChangeAtlas().edge(edgeIdentifier) != null)\n                .collect(Collectors.toSet());\n\n        if (this.override != null)\n        {\n            /*\n             * We need to filter any identifiers that were marked as explicitly excluded in the\n             * override. It is possible that the atlas view used to generate a FeatureChange context\n             * will differ from the atlas on which the FeatureChange will be applied. In that case,\n             * we must distinguish between two kinds of missing edge identifiers: 1) those that are\n             * missing because a shard simply couldn't see them and 2) those that are missing\n             * because a FeatureChange explicitly removed them.\n             */\n            final CompleteNode completeNodeOverride = (CompleteNode) this.override;\n            mergedIdentifiers = mergedIdentifiers.stream()\n                    .filter(edgeIdentifier -> !completeNodeOverride\n                            .explicitlyExcludedOutEdgeIdentifiers().contains(edgeIdentifier))\n                    .collect(Collectors.toSet());\n        }\n\n        return new TreeSet<>(mergedIdentifiers);\n    }\n\n    @Override\n    public SortedSet<Edge> outEdges()\n    {\n        final Supplier<SortedSet<Edge>> creator = () -> outEdgeIdentifiers().stream()\n                .map(edgeIdentifier -> getChangeAtlas().edge(edgeIdentifier))\n                .collect(Collectors.toCollection(TreeSet::new));\n\n        return ChangeEntity.getOrCreateCache(this.outEdgesCache,\n                cache -> this.outEdgesCache = cache, this.outEdgesCacheLock, creator);\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        final Supplier<Set<Relation>> creator = () -> ChangeEntity\n                .filterRelations(attribute(AtlasEntity::relations, \"relations\"), getChangeAtlas());\n        return ChangeEntity.getOrCreateCache(this.relationsCache,\n                cache -> this.relationsCache = cache, this.relationsCacheLock, creator);\n    }\n\n    private <T extends Object> List<T> allAvailableAttributes(\n            final Function<Node, T> memberExtractor, final String name)\n    {\n        return ChangeEntity.getAttributeAndOptionallyBackup(this.source, this.override,\n                memberExtractor, name);\n    }\n\n    private <T extends Object> T attribute(final Function<Node, T> memberExtractor,\n            final String name)\n    {\n        return ChangeEntity.getAttributeOrBackup(this.source, this.override, memberExtractor, name);\n    }\n\n    private ChangeAtlas getChangeAtlas()\n    {\n        return (ChangeAtlas) getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangePoint.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Point} that references a {@link ChangeAtlas}. That {@link Point} makes sure that all the\n * parent {@link Relation}s are {@link ChangeRelation}s.\n * <p>\n * NOSONAR here to avoid \"Subclasses that add fields should override \"equals\" (squid:S2160)\". Here\n * the equals from the parent works.\n *\n * @author matthieun\n */\npublic class ChangePoint extends Point // NOSONAR\n{\n    private static final long serialVersionUID = 4353679260691518275L;\n\n    private final Point source;\n    private final Point override;\n\n    // Computing Parent Relations is very expensive, so we cache it here.\n    private transient Set<Relation> relationsCache;\n    private transient Object relationsCacheLock = new Object();\n\n    protected ChangePoint(final ChangeAtlas atlas, final Point source, final Point override)\n    {\n        super(atlas);\n        this.source = source;\n        this.override = override;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return attribute(Point::getIdentifier, \"identifier\");\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return attribute(Point::getLocation, \"location\");\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return attribute(Point::getTags, \"tags\");\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        final Supplier<Set<Relation>> creator = () -> ChangeEntity\n                .filterRelations(attribute(AtlasEntity::relations, \"relations\"), getChangeAtlas());\n        return ChangeEntity.getOrCreateCache(this.relationsCache,\n                cache -> this.relationsCache = cache, this.relationsCacheLock, creator);\n    }\n\n    private <T extends Object> T attribute(final Function<Point, T> memberExtractor,\n            final String name)\n    {\n        return ChangeEntity.getAttributeOrBackup(this.source, this.override, memberExtractor, name);\n    }\n\n    private ChangeAtlas getChangeAtlas()\n    {\n        return (ChangeAtlas) getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeRelation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryCollection;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Polygon;\nimport org.locationtech.jts.geom.util.GeometryFixer;\nimport org.locationtech.jts.operation.overlayng.OverlayNG;\nimport org.locationtech.jts.operation.polygonize.Polygonizer;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiLineStringConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPrecisionManager;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link Relation} that references a {@link ChangeAtlas}. That {@link Relation} makes sure that all\n * the member entitiess are \"Change\" types, and that all the parent {@link Relation}s are\n * {@link ChangeRelation}s.\n * <p>\n * NOSONAR here to avoid \"Subclasses that add fields should override \"equals\" (squid:S2160)\". Here\n * the equals from the parent works.\n *\n * @author matthieun\n */\npublic class ChangeRelation extends Relation // NOSONAR\n{\n    private static final long serialVersionUID = 4353679260691518275L;\n    private static final Logger logger = LoggerFactory.getLogger(ChangeRelation.class);\n    private static final JtsMultiPolygonToMultiLineStringConverter converter = new JtsMultiPolygonToMultiLineStringConverter();\n\n    private final Relation source;\n    private final Relation override;\n\n    // Computing ChangeRelation members is very expensive, so we cache it here.\n    private transient RelationMemberList membersCache;\n    private transient Object membersCacheLock = new Object();\n\n    private transient Optional<MultiPolygon> geometryCache;\n    private transient Object geometryCacheLock = new Object();\n\n    // Computing Parent Relations is very expensive, so we cache it here.\n    private transient Set<Relation> relationsCache;\n    private transient Object relationsCacheLock = new Object();\n\n    protected ChangeRelation(final ChangeAtlas atlas, final Relation source,\n            final Relation override)\n    {\n        super(atlas);\n        this.source = source;\n        this.override = override;\n    }\n\n    @Override\n    public RelationMemberList allKnownOsmMembers()\n    {\n        return membersFor(\n                attribute(Relation::allKnownOsmMembers, \"all known osm members\").asBean());\n    }\n\n    @Override\n    public List<Relation> allRelationsWithSameOsmIdentifier()\n    {\n        return attribute(Relation::allRelationsWithSameOsmIdentifier,\n                \"all relations with same osm identifier\").stream()\n                .map(relation -> getChangeAtlas().relation(relation.getIdentifier()))\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public Optional<MultiPolygon> asMultiPolygon()\n    {\n        if (!this.isGeometric())\n        {\n            return Optional.empty();\n        }\n        final Supplier<Optional<MultiPolygon>> creator = () ->\n        {\n            if (this.override != null && ((CompleteRelation) this.override).isOverrideGeometry())\n            {\n                return this.override.asMultiPolygon();\n            }\n            else if (this.source != null)\n            {\n                final Optional<MultiPolygon> sourceJtsGeometry = ChangeEntity\n                        .getAttribute(this.source, Relation::asMultiPolygon);\n                if (sourceJtsGeometry.isPresent())\n                {\n                    // don't do anything to invalid geom\n                    if (!sourceJtsGeometry.get().isValid())\n                    {\n                        return sourceJtsGeometry;\n                    }\n\n                    final org.locationtech.jts.geom.MultiPolygon sourceGeom;\n                    sourceGeom = sourceJtsGeometry.get();\n\n                    final Set<LineString> removed = removedMembers();\n                    final Set<LineString> added = addedMembers();\n\n                    // if nothing was changed, return the original geometry\n                    if (removed.isEmpty() && added.isEmpty())\n                    {\n                        return sourceJtsGeometry;\n                    }\n\n                    // get the constituent linework and remove the old geometry and add in the new\n                    // geometry\n                    Geometry updatedGeometry = converter.convert(sourceGeom);\n                    for (final Geometry memberGeometry : removed)\n                    {\n                        updatedGeometry = OverlayNG.overlay(updatedGeometry, memberGeometry,\n                                OverlayNG.DIFFERENCE);\n                    }\n                    for (final Geometry memberGeometry : added)\n                    {\n                        updatedGeometry = OverlayNG.overlay(updatedGeometry, memberGeometry,\n                                OverlayNG.UNION);\n                    }\n\n                    // turn it into a multipolygon, fixing if necessary\n                    final Polygonizer update = new Polygonizer(true);\n                    update.add(updatedGeometry);\n                    MultiPolygon built = converter.backwardConvert(new GeometryCollection(\n                            (Geometry[]) update.getPolygons()\n                                    .toArray(new Polygon[update.getPolygons().size()]),\n                            JtsPrecisionManager.getGeometryFactory()));\n                    if (!built.isValid())\n                    {\n                        final Geometry fixed = GeometryFixer.fix(built);\n                        if (fixed instanceof Polygon)\n                        {\n                            built = new MultiPolygon(new Polygon[] { (Polygon) fixed },\n                                    JtsPrecisionManager.getGeometryFactory());\n                        }\n                        else if (fixed instanceof MultiPolygon)\n                        {\n                            built = (MultiPolygon) fixed;\n                        }\n                        else\n                        {\n                            throw new CoreException(\n                                    \"Fixed geometry {} included unexpected type! {}\",\n                                    fixed.toText(), fixed.getGeometryType());\n                        }\n                        logger.error(\"Had to fix geometry for relation {}\", this.getIdentifier());\n                    }\n\n                    return Optional.ofNullable(built);\n                }\n            }\n            else if (this.override != null\n                    && !((CompleteRelation) this.override).asMultiPolygon().isPresent())\n            {\n                // new ChangeRelation that never had geometry-- reconstruct it\n                ((CompleteRelation) this.override).updateGeometry();\n            }\n            return attribute(Relation::asMultiPolygon, \"geometry\");\n        };\n        return ChangeEntity.getOrCreateCache(this.geometryCache,\n                cache -> this.geometryCache = cache, this.geometryCacheLock, creator);\n\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return attribute(Relation::getIdentifier, \"identifier\");\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return attribute(Relation::getTags, \"tags\");\n    }\n\n    @Override\n    public RelationMemberList members()\n    {\n        final Supplier<RelationMemberList> creator = () ->\n        {\n            final List<RelationMemberList> availableMemberLists = allAvailableAttributes(\n                    Relation::members, \"members\");\n            final RelationBean mergedMembersBean = availableMemberLists.stream()\n                    .map(RelationMemberList::asBean)\n                    .reduce(new RelationBean(), RelationBean::merge);\n            final RelationBean filteredAndMergedMembersBean = new RelationBean();\n            mergedMembersBean.forEach(relationBeanItem ->\n            {\n                if (getChangeAtlas().entity(relationBeanItem.getIdentifier(),\n                        relationBeanItem.getType()) != null)\n                {\n                    filteredAndMergedMembersBean.addItem(relationBeanItem);\n                }\n            });\n            return membersFor(filteredAndMergedMembersBean);\n        };\n\n        return ChangeEntity.getOrCreateCache(this.membersCache, cache -> this.membersCache = cache,\n                this.membersCacheLock, creator);\n    }\n\n    @Override\n    public Long osmRelationIdentifier()\n    {\n        return attribute(Relation::osmRelationIdentifier, \"osm relation identifier\");\n    }\n\n    public boolean preservedValidGeometry()\n    {\n        if (this.source != null && (!addedMembers().isEmpty() || !removedMembers().isEmpty()))\n        {\n            final Optional<MultiPolygon> sourceGeom = this.source.asMultiPolygon();\n            if (sourceGeom.isPresent() && !sourceGeom.get().isEmpty() && sourceGeom.get().isValid())\n            {\n                final Optional<MultiPolygon> geom = this.asMultiPolygon();\n                return geom.isPresent() && !geom.get().isEmpty() && geom.get().isValid();\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        final Supplier<Set<Relation>> creator = () -> ChangeEntity\n                .filterRelations(attribute(AtlasEntity::relations, \"relations\"), getChangeAtlas());\n        return ChangeEntity.getOrCreateCache(this.relationsCache,\n                cache -> this.relationsCache = cache, this.relationsCacheLock, creator);\n    }\n\n    private Set<LineString> addedMembers()\n    {\n        if (this.override == null)\n        {\n            return new HashSet<>();\n        }\n        return ((CompleteRelation) this.override).getAddedGeometry();\n    }\n\n    private <T extends Object> List<T> allAvailableAttributes(\n            final Function<Relation, T> memberExtractor, final String name)\n    {\n        return ChangeEntity.getAttributeAndOptionallyBackup(this.source, this.override,\n                memberExtractor, name);\n    }\n\n    private <T extends Object> T attribute(final Function<Relation, T> memberExtractor,\n            final String name)\n    {\n        return ChangeEntity.getAttributeOrBackup(this.source, this.override, memberExtractor, name);\n    }\n\n    private ChangeAtlas getChangeAtlas()\n    {\n        return (ChangeAtlas) getAtlas();\n    }\n\n    private RelationMemberList membersFor(final RelationBean bean)\n    {\n        if (bean == null)\n        {\n            return null;\n        }\n        final List<RelationMember> memberList = new ArrayList<>();\n        for (final RelationBeanItem item : bean)\n        {\n            final AtlasEntity memberChangeEntity = getChangeAtlas().entity(item.getIdentifier(),\n                    item.getType());\n            if (memberChangeEntity != null)\n            {\n                memberList.add(\n                        new RelationMember(item.getRole(), memberChangeEntity, getIdentifier()));\n            }\n        }\n        return new RelationMemberList(memberList);\n    }\n\n    private Set<LineString> removedMembers()\n    {\n        if (this.override == null)\n        {\n            return new HashSet<>();\n        }\n        return ((CompleteRelation) this.override).getRemovedGeometry();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/ChangeType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\n/**\n * Types of changes. Here, MODIFY is omitted, as it can be translated to an ADD, for simplicity.\n *\n * @author matthieun\n */\npublic enum ChangeType\n{\n    ADD,\n    REMOVE\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/FeatureChange.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.exception.change.FeatureChangeMergeException;\nimport org.openstreetmap.atlas.exception.change.MergeFailureType;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescription;\nimport org.openstreetmap.atlas.geography.atlas.change.serializer.FeatureChangeGeoJsonSerializer;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLineItem;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLocationItem;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.complete.PrettifyStringFormat;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasObject;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.lightweight.LightEntity;\nimport org.openstreetmap.atlas.geography.atlas.lightweight.LightPoint;\nimport org.openstreetmap.atlas.geography.atlas.walker.OsmWayWalker;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Single feature change, does not include any consistency checks.\n * <p>\n * To add a new, non existing feature: {@link ChangeType} is ADD, and the included reference needs\n * to contain all the information related to that new feature.\n * <p>\n * To modify an existing feature: {@link ChangeType} is ADD, and the included reference needs to\n * contain the only the changed information related to that changed feature.\n * <p>\n * To remove an existing feature: {@link ChangeType} is REMOVE. The reference entity need only\n * contain the identifier of the feature to remove.\n * <p>\n * For all {@link FeatureChange}s, you may indirectly include a reference to the before view of the\n * entity using the {@link FeatureChange#add(AtlasEntity, Atlas)} and\n * {@link FeatureChange#remove(AtlasEntity, Atlas)} methods. Providing the atlas context allows\n * {@link FeatureChange} to perform more sophisticated merge logic.\n *\n * @author matthieun\n * @author lcram\n * @author Yazad Khambata\n */\npublic class FeatureChange implements Located, Taggable, Serializable, Comparable<FeatureChange>\n{\n    /**\n     * Options to use for the feature change\n     */\n    public enum Options\n    {\n        /** This performs expensive calculations when {@link #withAtlasContext(Atlas)} is called */\n        OSC_IF_POSSIBLE\n    }\n\n    private static final long serialVersionUID = 9172045162819925515L;\n\n    private final String featureChangeIdentifier = UUID.randomUUID().toString();\n\n    private final ChangeType changeType;\n    private AtlasEntity beforeView;\n    private final AtlasEntity afterView;\n    private final Map<String, String> metaData;\n    /**\n     * The collection will be empty, have one item, or have multiple items.\n     */\n    private Collection<LocationItem> nodes;\n    private Map<String, String> originalTags;\n    private String osc;\n\n    /** The options for this FeatureChange */\n    private final EnumSet<Options> options = EnumSet.noneOf(Options.class);\n\n    /**\n     * Create a new {@link ChangeType#ADD} {@link FeatureChange} with a given afterView. The\n     * afterView should be a {@link CompleteEntity} that specifies how the newly added or modified\n     * feature should look. For the modified case, the afterView {@link CompleteEntity} need only\n     * contain the fields that were modified. For ADDs that are adding a brand new feature, it\n     * should be fully populated.\n     *\n     * @param afterView\n     *            the after view {@link CompleteEntity}\n     * @return the created {@link FeatureChange}\n     */\n    public static FeatureChange add(final AtlasEntity afterView)\n    {\n        return new FeatureChange(ChangeType.ADD, afterView);\n    }\n\n    /**\n     * Create a new {@link ChangeType#ADD} {@link FeatureChange} with a given after view. The\n     * afterView should be a {@link CompleteEntity} that specifies how the newly added or modified\n     * feature should look. For the modified case, the afterView {@link CompleteEntity} need only\n     * contain the fields that were modified. For ADDs that are adding a brand new feature, it\n     * should be fully populated. The atlasContext parameter creates a richer {@link FeatureChange}\n     * that contains information on how the entity looked before the update. This allows for more\n     * sophisticated merge logic.\n     *\n     * @param afterView\n     *            the after view {@link CompleteEntity}\n     * @param atlasContext\n     *            the atlas context\n     * @return the created {@link FeatureChange}\n     */\n    public static FeatureChange add(final AtlasEntity afterView, final Atlas atlasContext)\n    {\n        return add(afterView, atlasContext, (Options) null);\n    }\n\n    /**\n     * Create a new {@link ChangeType#ADD} {@link FeatureChange} with a given after view. The\n     * afterView should be a {@link CompleteEntity} that specifies how the newly added or modified\n     * feature should look. For the modified case, the afterView {@link CompleteEntity} need only\n     * contain the fields that were modified. For ADDs that are adding a brand new feature, it\n     * should be fully populated. The atlasContext parameter creates a richer {@link FeatureChange}\n     * that contains information on how the entity looked before the update. This allows for more\n     * sophisticated merge logic.\n     *\n     * @param afterView\n     *            the after view {@link CompleteEntity}\n     * @param atlasContext\n     *            the atlas context\n     * @param options\n     *            The options for this {@link FeatureChange}\n     * @return the created {@link FeatureChange}\n     */\n    public static FeatureChange add(final AtlasEntity afterView, final Atlas atlasContext,\n            final Options... options)\n    {\n        return new FeatureChange(ChangeType.ADD, afterView).setOptions(options)\n                .withAtlasContext(atlasContext);\n    }\n\n    /**\n     * Create a new {@link ChangeType#REMOVE} {@link FeatureChange} using a given reference. The\n     * reference can be a shallow {@link CompleteEntity}, i.e. containing only the identifier of the\n     * feature to be removed.\n     *\n     * @param reference\n     *            the {@link CompleteEntity} to remove\n     * @return the created {@link FeatureChange}\n     */\n    public static FeatureChange remove(final AtlasEntity reference)\n    {\n        return new FeatureChange(ChangeType.REMOVE, reference);\n    }\n\n    /**\n     * Create a new {@link ChangeType#REMOVE} {@link FeatureChange} using a given reference. The\n     * reference can be a shallow {@link CompleteEntity}, i.e. containing only the identifier of the\n     * feature to be removed. The atlasContext parameter creates a richer {@link FeatureChange} that\n     * contains information on how the entity looked before the update. This allows for more\n     * sophisticated merge logic.\n     *\n     * @param reference\n     *            the {@link CompleteEntity} to remove\n     * @param atlasContext\n     *            the atlas context\n     * @return the created {@link FeatureChange}\n     */\n    public static FeatureChange remove(final AtlasEntity reference, final Atlas atlasContext)\n    {\n        return remove(reference, atlasContext, (Options) null);\n    }\n\n    /**\n     * Create a new {@link ChangeType#REMOVE} {@link FeatureChange} using a given reference. The\n     * reference can be a shallow {@link CompleteEntity}, i.e. containing only the identifier of the\n     * feature to be removed. The atlasContext parameter creates a richer {@link FeatureChange} that\n     * contains information on how the entity looked before the update. This allows for more\n     * sophisticated merge logic.\n     *\n     * @param reference\n     *            the {@link CompleteEntity} to remove\n     * @param atlasContext\n     *            the atlas context\n     * @param options\n     *            The options for this {@link FeatureChange}\n     * @return the created {@link FeatureChange}\n     */\n    public static FeatureChange remove(final AtlasEntity reference, final Atlas atlasContext,\n            final Options... options)\n    {\n        return new FeatureChange(ChangeType.REMOVE, reference).setOptions(options)\n                .withAtlasContext(atlasContext);\n    }\n\n    /**\n     * Get the polyline for a view\n     *\n     * @param atlas\n     *            The atlas to use (used if the view parameter is an Edge)\n     * @param view\n     *            The view to use\n     * @return A PolyLine\n     */\n    @Nullable\n    static PolyLine getPolyline(final Atlas atlas, final AtlasEntity view)\n    {\n        if (view instanceof Line)\n        {\n            return ((Line) view).asPolyLine();\n        }\n        else if (view instanceof Area)\n        {\n            return ((Area) view).asPolygon();\n        }\n        else if (view instanceof Edge && ((Edge) view).asPolyLine() != null)\n        {\n            // Edges are special. We kind of need to get all the edges for that way to make the full\n            // polyline\n            return new PolyLine(new OsmWayWalker(atlas.edge(((Edge) view).getMainEdgeIdentifier()))\n                    .collectEdges().stream().map(Edge::asPolyLine).flatMap(PolyLine::stream)\n                    .collect(Collectors.toList()));\n        }\n        // Despite `PolyLine` implementing Collection, we cannot return an empty PolyLine here (the\n        // polyline fails to be created).\n        return null;\n    }\n\n    /**\n     * Get the tags from an entity\n     *\n     * @param entity\n     *            The entity to get tags from\n     * @return The tags\n     */\n    @Nonnull\n    private static Map<String, String> getTags(@Nullable final AtlasEntity entity)\n    {\n        if (entity != null)\n        {\n            return Optional.ofNullable(entity.getTags()).orElseGet(Collections::emptyMap);\n        }\n        return Collections.emptyMap();\n    }\n\n    /**\n     * Create a new {@link FeatureChange} with a given type and after view.\n     *\n     * @param changeType\n     *            the type, either ADD or REMOVE.\n     * @param afterView\n     *            the after view of the changed entity\n     */\n    public FeatureChange(final ChangeType changeType, final AtlasEntity afterView)\n    {\n        this(changeType, afterView, null);\n    }\n\n    /**\n     * Create a new {@link FeatureChange} with a given type, after view, and before view. This\n     * constructor is provided for exact control over the before view of a change. It is kept\n     * package private, and is used for testing purposes only.\n     *\n     * @param changeType\n     *            the change type\n     * @param afterView\n     *            the updated entity\n     * @param beforeView\n     *            the before entity\n     */\n    public FeatureChange(final ChangeType changeType, final AtlasEntity afterView,\n            final AtlasEntity beforeView)\n    {\n        if (afterView == null)\n        {\n            throw new CoreException(\"After view cannot be null.\");\n        }\n        if (!(afterView instanceof CompleteEntity))\n        {\n            throw new CoreException(\n                    \"FeatureChange afterView requires CompleteEntity, found reference of type {}\",\n                    afterView.getClass().getName());\n        }\n        if (beforeView != null && !(beforeView instanceof CompleteEntity))\n        {\n            throw new CoreException(\n                    \"FeatureChange beforeView requires CompleteEntity, found reference of type {}\",\n                    beforeView.getClass().getName());\n        }\n        if (changeType == null)\n        {\n            throw new CoreException(\"changeType cannot be null.\");\n        }\n\n        this.changeType = changeType;\n        this.afterView = afterView;\n        this.beforeView = beforeView;\n\n        if (this.afterView.bounds() == null)\n        {\n            throw new CoreException(\"afterView {} bounds was null for {}\", this.afterView,\n                    this.toString());\n        }\n        if (this.beforeView != null && this.beforeView.bounds() == null)\n        {\n            throw new CoreException(\"beforeView {} bounds was null for {}\", this.beforeView,\n                    this.toString());\n        }\n\n        this.validateNotShallow();\n        this.metaData = new HashMap<>();\n    }\n\n    /**\n     * Add a new key value pair to this FeatureChange's meta-data\n     *\n     * @param key\n     *            The key\n     * @param value\n     *            The value\n     */\n    public void addMetaData(final String key, final String value)\n    {\n        if (key == null)\n        {\n            throw new CoreException(\"Meta-Data key (value={}) cannot be null!\", value);\n        }\n        if (value == null)\n        {\n            throw new CoreException(\"Meta-Data value (key={}) cannot be null!\", key);\n        }\n        this.metaData.put(key, value);\n    }\n\n    /**\n     * Check if this {@link FeatureChange}'s afterView is full. A full afterView is a\n     * {@link CompleteEntity} that has all its fields set to non-null values.\n     *\n     * @return if this {@link FeatureChange} has a full afterView\n     */\n    public boolean afterViewIsFull()\n    {\n        if (this.getAfterView().getTags() == null || this.getAfterView().relations() == null)\n        {\n            return false;\n        }\n        switch (this.getItemType())\n        {\n            case NODE:\n                final Node nodeReference = (Node) this.getAfterView();\n                if (nodeReference.inEdges() == null || nodeReference.outEdges() == null\n                        || nodeReference.getLocation() == null)\n                {\n                    return false;\n                }\n                break;\n            case EDGE:\n                final Edge edgeReference = (Edge) this.getAfterView();\n                if (edgeReference.start() == null || edgeReference.end() == null\n                        || edgeReference.asPolyLine() == null)\n                {\n                    return false;\n                }\n                break;\n            case AREA:\n                final Area areaReference = (Area) this.getAfterView();\n                if (areaReference.asPolygon() == null)\n                {\n                    return false;\n                }\n                break;\n            case LINE:\n                final Line lineReference = (Line) this.getAfterView();\n                if (lineReference.asPolyLine() == null)\n                {\n                    return false;\n                }\n                break;\n            case POINT:\n                final Point pointReference = (Point) this.getAfterView();\n                if (pointReference.getLocation() == null)\n                {\n                    return false;\n                }\n                break;\n            case RELATION:\n                final Relation relationReference = (Relation) this.getAfterView();\n                if (relationReference.members() == null\n                        || relationReference.allKnownOsmMembers() == null\n                        || relationReference.allRelationsWithSameOsmIdentifier() == null)\n                {\n                    return false;\n                }\n                break;\n            default:\n                throw new CoreException(\"Unknown Item Type {}\", this.getItemType());\n        }\n        return true;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        final Rectangle updatedBounds = this.afterView.bounds();\n        if (updatedBounds == null)\n        {\n            throw new CoreException(\"Corrupted FeatureChange: afterView bounds were null\");\n        }\n        if (this.beforeView == null)\n        {\n            return updatedBounds;\n        }\n        if (this.beforeView.bounds() == null)\n        {\n            throw new CoreException(\"Corrupted FeatureChange: beforeView bounds were null\");\n        }\n        return Rectangle.forLocated(this.beforeView.bounds(), updatedBounds);\n    }\n\n    @Override\n    public int compareTo(final FeatureChange otherFeatureChange)\n    {\n        return Comparator.comparing(FeatureChange::getChangeType)\n                .thenComparing(FeatureChange::getItemType)\n                .thenComparing(FeatureChange::getIdentifier).compare(this, otherFeatureChange);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof FeatureChange)\n        {\n            final FeatureChange that = (FeatureChange) other;\n            return this.getChangeType() == that.getChangeType()\n                    && this.getAfterView().equals(that.getAfterView());\n        }\n        return false;\n    }\n\n    /**\n     * Return a {@link ChangeDescription} object that explains the differences represented by this\n     * {@link FeatureChange}.\n     *\n     * @return the {@link ChangeDescription} representing this {@link FeatureChange}\n     */\n    public ChangeDescription explain()\n    {\n        if (this.afterView == null)\n        {\n            throw new CoreException(\"Cannot explain a FeatureChange with a null afterView!\");\n        }\n        final var changeDescription = new ChangeDescription(this.getIdentifier(),\n                this.getItemType(), this.beforeView, this.afterView, this.changeType,\n                this.originalTags, this.nodes);\n        if (this.osc != null)\n        {\n            changeDescription.setOsc(this.osc);\n        }\n        return changeDescription;\n    }\n\n    public AtlasEntity getAfterView()\n    {\n        return this.afterView;\n    }\n\n    public AtlasEntity getBeforeView()\n    {\n        return this.beforeView;\n    }\n\n    public ChangeType getChangeType()\n    {\n        return this.changeType;\n    }\n\n    public String getFeatureChangeIdentifier()\n    {\n        return this.featureChangeIdentifier;\n    }\n\n    public long getIdentifier()\n    {\n        return getAfterView().getIdentifier();\n    }\n\n    public ItemType getItemType()\n    {\n        return getAfterView().getType();\n    }\n\n    public Map<String, String> getMetaData()\n    {\n        return new HashMap<>(this.metaData);\n    }\n\n    /**\n     * Get a tag based on a key, taking the changes into account.\n     *\n     * @param key\n     *            - The tag key to look for.\n     * @return - the changed value of the tag, if available.\n     */\n    @Override\n    public Optional<String> getTag(final String key)\n    {\n        return this.getAfterView().getTag(key);\n    }\n\n    /**\n     * Get the changed tags.\n     *\n     * @return Map - the changed tags.\n     */\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.getAfterView().getTags();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        if (this.afterView instanceof Relation)\n        {\n            return Objects.hash(this.changeType, this.afterView,\n                    ((Relation) this.afterView).members());\n        }\n        if (this.afterView instanceof Node)\n        {\n            return Objects.hash(this.changeType, this.afterView, ((Node) this.afterView).inEdges(),\n                    ((Node) this.afterView).outEdges());\n        }\n        else\n        {\n            return Objects.hash(this.changeType, this.afterView, this.afterView.getTags());\n        }\n    }\n\n    /**\n     * Merge two feature changes together. If it cannot succeed, this method will throw a\n     * {@link CoreException} explaining why.\n     *\n     * @param other\n     *            The other to merge into this one.\n     * @return The merged {@link FeatureChange}\n     */\n    public FeatureChange merge(final FeatureChange other)\n    {\n        /*\n         * FeatureChanges are mergeable under certain pre-conditions. If those pre-conditions are\n         * satisfied, then we can proceed with attempting to merge the FeatureChanges.\n         */\n        // Pre-conditions:\n        // 1) The left and right FeatureChanges must be operating on the same entity identifier and\n        // ItemType. Additionally, the ChangeType (i.e. ADD or REMOVE) must match. If these\n        // conditions do not hold, there is no logical way to merge the FeatureChanges.\n        //\n        // 2) Either both FeatureChanges must provide a beforeView, or neither should provide one.\n        // Attempting to merge two FeatureChanges where one has a beforeView and one does not\n        // will always fail. We enforce this assumption in order to make the ADD/REMOVE merge logic\n        // simpler.\n        /*\n         * Once basic mergeability is established, the merge logic proceeds:\n         */\n        // Merging two REMOVE changes:\n        // There is no need to merge the afterViews (since they are shallow), but we must ensure\n        // that the beforeViews are properly merged. There are 3 possibilities,\n        // outlined below.\n        //\n        // 1) Both FeatureChanges had fully populated, equivalent beforeViews, which are computed\n        // automatically when a REMOVE FeatureChange is created (except possibly in the case of Node\n        // and Relation, see 3) below)\n        //\n        // 2) Neither FeatureChange had a beforeView, in which case no merge is required.\n        //\n        // 3) In cases where the REMOVE is acting on a Relation, we first need to check if\n        // there are inconsistencies in the beforeViews of members and allKnownOsmMembers. If the\n        // REMOVE is acting on a Node, we need to check if there are inconsistencies in the\n        // beforeViews of the in/out Edge identifier sets. Any inconsistencies must be merged. We\n        // allow for inconsistencies in these specific cases, since it is possible that\n        // FeatureChanges generated in different shards will have slightly different views of the\n        // same Feature (since RelationMemberLists and in/out edge sets can be inconsistent across\n        // shards).\n        //\n        // Merging two ADD changes:\n        // In this case, we need to perform additional checks to ensure that the FeatureChanges can\n        // indeed properly merge. We also must ensure that the potentially differing beforeViews can\n        // merge. For more information on this, see\n        // FeatureChangeMergingHelpers#mergeADDFeatureChangePair.\n        FeatureChange result = this;\n        try\n        {\n            // Pre-condition 1)\n            if (this.getIdentifier() != other.getIdentifier()\n                    || this.getItemType() != other.getItemType())\n            {\n                throw new FeatureChangeMergeException(\n                        MergeFailureType.FEATURE_CHANGE_INVALID_PROPERTIES_MERGE,\n                        \"Cannot merge FeatureChanges with mismatching properties: [{}, {}, {}] vs [{}, {}, {}]\",\n                        this.getIdentifier(), this.getItemType(), this.getChangeType(),\n                        other.getIdentifier(), other.getItemType(), other.getChangeType());\n            }\n\n            // Pre-condition 1A) (we separate this one to provide a better exception)\n            if (this.getIdentifier() == other.getIdentifier()\n                    && this.getItemType() == other.getItemType()\n                    && this.getChangeType() != other.getChangeType())\n            {\n                throw new FeatureChangeMergeException(\n                        MergeFailureType.FEATURE_CHANGE_INVALID_ADD_REMOVE_MERGE,\n                        \"Cannot merge FeatureChanges for [{}, {}], one is ADD and one is REMOVE\",\n                        this.getIdentifier(), this.getItemType());\n            }\n\n            // Pre-condition 2)\n            if (this.getBeforeView() == null && other.getBeforeView() != null\n                    || this.getBeforeView() != null && other.getBeforeView() == null)\n            {\n                throw new FeatureChangeMergeException(\n                        MergeFailureType.FEATURE_CHANGE_IMBALANCED_BEFORE_VIEW,\n                        \"One of the FeatureChanges was missing a beforeView - \"\n                                + \"cannot merge two FeatureChanges unless both either explicitly provide or explicitly exclude a beforeView, {} and {}\",\n                        this.toString(), other.toString());\n            }\n\n            // Actually merge the changes\n            if (this.getChangeType() == ChangeType.REMOVE)\n            {\n                /*\n                 * Pre-condition 2 implies that if one beforeView is null, both are null so it is\n                 * safe to arbitrarily pick from the left or right side of the merge.\n                 */\n                if (this.getBeforeView() != null)\n                {\n                    result = FeatureChangeMergingHelpers.mergeREMOVEFeatureChangePair(this, other);\n                }\n            }\n            else if (this.getChangeType() == ChangeType.ADD)\n            {\n                result = FeatureChangeMergingHelpers.mergeADDFeatureChangePair(this, other);\n            }\n            else\n            {\n                // If we get here, something very unexpected happened.\n                throw new CoreException(\"Unexpected merge failure for {} and {}\", this.prettify(),\n                        other.prettify());\n            }\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            final List<MergeFailureType> newFailureTrace = exception\n                    .withNewTopLevelFailure(MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE);\n            throw new FeatureChangeMergeException(newFailureTrace,\n                    \"Cannot merge two feature changes:\\n{}\\nAND\\n{}\\nFailureTrace: {}\",\n                    this.prettify(), other.prettify(), newFailureTrace, exception);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE,\n                    \"Cannot merge two feature changes:\\n{}\\nAND\\n{}\", this.prettify(),\n                    other.prettify(), exception);\n        }\n        FeatureChangeMergingHelpers.mergeMetaData(this, other).forEach(result::addMetaData);\n        return result;\n    }\n\n    /**\n     * Transform this {@link FeatureChange} into a pretty string. This will use the pretty strings\n     * for {@link CompleteEntity} classes. By default, this method will use\n     * {@link PrettifyStringFormat#MINIMAL_MULTI_LINE} for the {@link FeatureChange} itself, but\n     * will use {@link PrettifyStringFormat#MINIMAL_SINGLE_LINE} for the constituent\n     * {@link CompleteEntity}s.\n     *\n     * @return the pretty string\n     */\n    public String prettify()\n    {\n        return this.prettify(PrettifyStringFormat.MINIMAL_MULTI_LINE,\n                PrettifyStringFormat.MINIMAL_SINGLE_LINE);\n    }\n\n    /**\n     * Transform this {@link FeatureChange} into a pretty string. This will use the pretty strings\n     * for {@link CompleteEntity} classes. If you are unsure about which\n     * {@link PrettifyStringFormat}s to use, try {@link FeatureChange#prettify()} which has some\n     * sane defaults.\n     *\n     * @param format\n     *            the format type for the this {@link FeatureChange}\n     * @param completeEntityFormat\n     *            the format type for the constituent {@link CompleteEntity}s\n     * @return the pretty string\n     */\n    public String prettify(final PrettifyStringFormat format,\n            final PrettifyStringFormat completeEntityFormat)\n    {\n        return this.prettify(PrettifyStringFormat.MINIMAL_MULTI_LINE,\n                PrettifyStringFormat.MINIMAL_SINGLE_LINE, true);\n    }\n\n    /**\n     * Transform this {@link FeatureChange} into a pretty string. This will use the pretty strings\n     * for {@link CompleteEntity} classes. If you are unsure about which\n     * {@link PrettifyStringFormat}s to use, try {@link FeatureChange#prettify()} which has some\n     * sane defaults.\n     *\n     * @param format\n     *            the format type for the this {@link FeatureChange}\n     * @param completeEntityFormat\n     *            the format type for the constituent {@link CompleteEntity}s\n     * @param truncate\n     *            whether or not to truncate long fields\n     * @return the pretty string\n     */\n    public String prettify(final PrettifyStringFormat format,\n            final PrettifyStringFormat completeEntityFormat, final boolean truncate)\n    {\n        String separator = \"\";\n        if (format == PrettifyStringFormat.MINIMAL_SINGLE_LINE)\n        {\n            separator = \"\";\n        }\n        else if (format == PrettifyStringFormat.MINIMAL_MULTI_LINE)\n        {\n            separator = \"\\n\";\n        }\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName()).append(\" \").append(\"[\").append(separator)\n                .append(\"changeType: \").append(this.getChangeType()).append(\", \").append(separator)\n                .append(\"itemType: \").append(this.getItemType()).append(\", \").append(separator)\n                .append(\"identifier: \").append(this.getIdentifier()).append(\", \").append(separator)\n                .append(\"bounds: \").append(this.bounds()).append(\", \").append(separator);\n        if (this.beforeView != null)\n        {\n            builder.append(\"bfView: \").append(\n                    ((CompleteEntity<?>) this.beforeView).prettify(completeEntityFormat, truncate))\n                    .append(\", \").append(separator);\n        }\n        builder.append(\"afView: \")\n                .append(((CompleteEntity<?>) this.afterView).prettify(completeEntityFormat,\n                        truncate))\n                .append(\", \").append(separator).append(\"metadata: \").append(this.metaData)\n                .append(separator).append(this.explain()).append(separator).append(\"]\");\n\n        return builder.toString();\n    }\n\n    /**\n     * Save a GeoJSON representation of that feature change.\n     *\n     * @param resource\n     *            The {@link WritableResource} to save the GeoJSON to.\n     */\n    public void save(final WritableResource resource)\n    {\n        new FeatureChangeGeoJsonSerializer(true).accept(this, resource);\n    }\n\n    /**\n     * Save a GeoJSON representation of that feature change.\n     *\n     * @param resource\n     *            The {@link WritableResource} to save the GeoJSON to.\n     * @param showDescription\n     *            whether or not to show the {@link ChangeDescription}\n     */\n    public void save(final WritableResource resource, final boolean showDescription)\n    {\n        new FeatureChangeGeoJsonSerializer(true, showDescription).accept(this, resource);\n    }\n\n    /**\n     * Set the options for this FeatureChange. This should be called as soon as possible, and always\n     * before any method that the {@link FeatureChange.Options} specifies.\n     *\n     * @param options\n     *            the options to set. {@code null} clears the options.\n     * @return {@code this}, for easy chaining\n     */\n    public FeatureChange setOptions(final Options... options)\n    {\n        this.options.clear();\n        if (options != null)\n        {\n            Stream.of(options).filter(Objects::nonNull).forEach(this.options::add);\n        }\n        return this;\n    }\n\n    public String toGeoJson()\n    {\n        return new FeatureChangeGeoJsonSerializer(false).convert(this);\n    }\n\n    public String toGeoJson(final boolean showDescription)\n    {\n        return new FeatureChangeGeoJsonSerializer(false, showDescription).convert(this);\n    }\n\n    public String toPrettyGeoJson()\n    {\n        return new FeatureChangeGeoJsonSerializer(true, true).convert(this);\n    }\n\n    public String toPrettyGeoJson(final boolean showDescription)\n    {\n        return new FeatureChangeGeoJsonSerializer(true, showDescription).convert(this);\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"FeatureChange [changeType: \" + this.changeType + \", reference: {\"\n                + this.afterView.getType() + \",\" + this.afterView.getIdentifier() + \"}, tags: \"\n                + getTags() + \", bounds: \" + bounds() + \"]\";\n    }\n\n    /**\n     * Specify the Atlas on which this {@link FeatureChange} is based. {@link FeatureChange} objects\n     * with a contextual Atlas are able to calculate their before view, and so are able to leverage\n     * richer and more robust merging mechanics.\n     *\n     * @param atlas\n     *            the contextual atlas\n     * @return the updated {@link FeatureChange}\n     */\n    public FeatureChange withAtlasContext(final Atlas atlas)\n    {\n        this.computeBeforeViewUsingAtlasContext(atlas, this.changeType);\n        if (this.options.contains(Options.OSC_IF_POSSIBLE))\n        {\n            final long identifier = this.afterView.getIdentifier();\n            // Don't keep the original object, as this keeps the atlas alive\n            if (this.afterView instanceof Line)\n            {\n                this.originalTags = getTags(atlas.line(identifier));\n            }\n            else if (this.afterView instanceof Edge)\n            {\n                final var edge = atlas.edge(identifier);\n                this.originalTags = edge == null ? null : getTags(edge.getMainEdge());\n            }\n            else if (this.afterView instanceof Point)\n            {\n                this.originalTags = getTags(atlas.point(identifier));\n            }\n            else if (this.afterView instanceof Node)\n            {\n                this.originalTags = getTags(atlas.node(identifier));\n            }\n            else if (this.afterView instanceof Area)\n            {\n                this.originalTags = getTags(atlas.area(identifier));\n            }\n            else if (this.afterView instanceof Relation)\n            {\n                this.originalTags = getTags(atlas.relation(identifier));\n            }\n            this.computeRequiredOpenStreetMapChangeInformation(atlas, this.changeType);\n        }\n        return this;\n    }\n\n    /**\n     * Use the OSC information for OpenStreetMap diffs. Used by deserialization.\n     *\n     * @param osc\n     *            The OSC to use\n     * @return this, for easy chaining\n     */\n    public FeatureChange withOsc(final String osc)\n    {\n        this.osc = osc;\n        return this;\n    }\n\n    /**\n     * Build the nodes needed for this feature change\n     *\n     * @param atlas\n     *            The atlas with the required nodes\n     * @param locationsToFind\n     *            The locations to map to nodes in the atlas\n     */\n    private void buildNodes(final Atlas atlas, final Collection<Location> locationsToFind)\n    {\n        this.nodes = new ArrayList<>(locationsToFind.size());\n        long currentNewId = -1;\n        for (final Location point : locationsToFind)\n        {\n            final List<Node> localNodes = Iterables.asList(atlas.nodesAt(point));\n            final List<Point> nodePoints = Iterables.asList(atlas.pointsAt(point));\n            final List<LocationItem> locationItems = Stream\n                    .concat(localNodes.stream(), nodePoints.stream())\n                    .filter(LocationItem.class::isInstance).map(LocationItem.class::cast)\n                    .collect(Collectors.toList());\n            final long possibleNodes = locationItems.stream()\n                    .mapToLong(AtlasObject::getOsmIdentifier).distinct().count();\n            if (possibleNodes == 1)\n            {\n                // CompletePoint and CompleteNode both extend Point and Node respectively\n                this.nodes.add((LocationItem) LightEntity.from(locationItems.get(0)));\n            }\n            else if (possibleNodes == 0)\n            {\n                // OK. New node.\n                this.nodes.add(new LightPoint(currentNewId, point, Collections.emptySet()));\n                currentNewId--;\n            }\n            else\n            {\n                // We cannot determine the nodes of the way. This will have to be manually edited.\n                localNodes.clear();\n                break;\n            }\n        }\n    }\n\n    /**\n     * Compute the beforeView using a given afterView and Atlas context. The beforeView is always a\n     * CompleteEntity. For ChangeType.ADD, the beforeView will only contain references to fields\n     * that were updated in the afterView. For ChangeType.REMOVE, the beforeView will be fully\n     * populated. This will facilitate better debug printouts.\n     *\n     * @param atlas\n     *            the atlas context\n     * @param changeType\n     *            the change type\n     */\n    private void computeBeforeViewUsingAtlasContext(final Atlas atlas, final ChangeType changeType)\n    {\n        if (atlas == null)\n        {\n            throw new CoreException(\"Atlas context cannot be null for {}\", this.toString());\n        }\n\n        final AtlasEntity beforeViewUpdatesOnly;\n        final AtlasEntity beforeViewFromAtlas = atlas.entity(this.afterView.getIdentifier(),\n                this.afterView.getType());\n\n        /*\n         * Check that the beforeViewFromAtlas is non-null. In case of REMOVE, this must be the case.\n         * In case of ADD, it is possible the beforeViewFromAtlas is null when adding a brand new\n         * feature.\n         */\n        if (beforeViewFromAtlas == null && changeType != ChangeType.ADD)\n        {\n            throw new CoreException(\n                    \"Could not find {} with ID {} in atlas context, ChangeType was {}\",\n                    this.afterView.getType(), this.afterView.getIdentifier(), changeType);\n        }\n        /*\n         * For the REMOVE case, we fully populate the beforeView and return.\n         */\n        if (changeType == ChangeType.REMOVE)\n        {\n            if (beforeViewFromAtlas instanceof Edge\n                    && this.options.contains(Options.OSC_IF_POSSIBLE))\n            {\n                // Edges are sectioned on a per-intersection basis, along with some other special\n                // cases.\n                // With OSC, we need to know the original way geometry.\n                final List<Location> locations = new OsmWayWalker((Edge) beforeViewFromAtlas)\n                        .collectEdges().stream().map(Edge::asPolyLine).flatMap(PolyLine::stream)\n                        .collect(Collectors.toList());\n                this.beforeView = (AtlasEntity) CompleteEdge\n                        .from(((Edge) beforeViewFromAtlas).getMainEdge()).withGeometry(locations);\n            }\n            else\n            {\n                this.beforeView = CompleteEntity.from(beforeViewFromAtlas);\n            }\n            return;\n        }\n\n        /*\n         * Otherwise, we continue with the ADD case.\n         */\n        if (changeType != ChangeType.ADD)\n        {\n            throw new CoreException(\"Unknown ChangeType {}\", changeType);\n        }\n\n        /*\n         * If the beforeViewFromAtlas is null, then this is a brand new ADD. We just set the\n         * beforeView to null and return.\n         */\n        if (beforeViewFromAtlas == null)\n        {\n            this.beforeView = null;\n            return;\n        }\n\n        /*\n         * Make type specific updates first.\n         */\n        if (this.afterView instanceof Area)\n        {\n            /*\n             * Area specific updates. The only Area-specific field is the polygon.\n             */\n            final Area afterAreaView = (Area) this.afterView;\n            final Area beforeAreaViewFromAtlas = (Area) beforeViewFromAtlas;\n            beforeViewUpdatesOnly = CompleteArea.shallowFrom(beforeAreaViewFromAtlas);\n            if (afterAreaView.asPolygon() != null)\n            {\n                ((CompleteArea) beforeViewUpdatesOnly)\n                        .withPolygon(beforeAreaViewFromAtlas.asPolygon());\n            }\n        }\n        else if (this.afterView instanceof LineItem)\n        {\n            /*\n             * LineItem specific updates. The LineItem-specific fields are the polyline, and the\n             * start/end nodes in case of an Edge LineItem.\n             */\n            final LineItem afterLineItemView = (LineItem) this.afterView;\n            final LineItem beforeLineItemViewFromAtlas = (LineItem) beforeViewFromAtlas;\n            beforeViewUpdatesOnly = CompleteEntity.shallowFrom(beforeLineItemViewFromAtlas);\n            if (afterLineItemView.asPolyLine() != null)\n            {\n                ((CompleteLineItem) beforeViewUpdatesOnly)\n                        .withPolyLine(beforeLineItemViewFromAtlas.asPolyLine());\n            }\n            if (this.afterView instanceof Edge)\n            {\n                final Edge afterEdgeView = (Edge) afterLineItemView;\n                final Edge beforeEdgeViewFromAtlas = (Edge) beforeViewFromAtlas;\n                if (afterEdgeView.start() != null)\n                {\n                    ((CompleteEdge) beforeViewUpdatesOnly).withStartNodeIdentifier(\n                            beforeEdgeViewFromAtlas.start().getIdentifier());\n                }\n                if (afterEdgeView.end() != null)\n                {\n                    ((CompleteEdge) beforeViewUpdatesOnly)\n                            .withEndNodeIdentifier(beforeEdgeViewFromAtlas.end().getIdentifier());\n                }\n            }\n        }\n        else if (this.afterView instanceof LocationItem)\n        {\n            /*\n             * LocationItem specific updates. The LocationItem-specific fields are the location, and\n             * the in/out edge sets in case of a Node LocationItem.\n             */\n            final LocationItem afterLocationItemView = (LocationItem) this.afterView;\n            final LocationItem beforeLocationItemViewFromAtlas = (LocationItem) beforeViewFromAtlas;\n            beforeViewUpdatesOnly = CompleteEntity.shallowFrom(beforeLocationItemViewFromAtlas);\n            if (afterLocationItemView.getLocation() != null)\n            {\n                ((CompleteLocationItem) beforeViewUpdatesOnly)\n                        .withLocation(beforeLocationItemViewFromAtlas.getLocation());\n            }\n            if (this.afterView instanceof Node)\n            {\n                final Node afterNodeView = (Node) afterLocationItemView;\n                final Node beforeNodeViewFromAtlas = (Node) beforeViewFromAtlas;\n                if (afterNodeView.inEdges() != null)\n                {\n                    ((CompleteNode) beforeViewUpdatesOnly)\n                            .withInEdges(beforeNodeViewFromAtlas.inEdges());\n                }\n                if (afterNodeView.outEdges() != null)\n                {\n                    ((CompleteNode) beforeViewUpdatesOnly)\n                            .withOutEdges(beforeNodeViewFromAtlas.outEdges());\n                }\n            }\n        }\n        else if (this.afterView instanceof Relation)\n        {\n            /*\n             * Relation specific updates. There are quite a few Relation specific fields: members,\n             * allRelationsWithSameOsmIdentifier, allKnownOsmMembers, and osmRelationIdentifier.\n             */\n            final Relation afterRelationView = (Relation) this.afterView;\n            final Relation beforeRelationViewFromAtlas = (Relation) beforeViewFromAtlas;\n            beforeViewUpdatesOnly = CompleteRelation.shallowFrom(afterRelationView);\n            if (afterRelationView.members() != null)\n            {\n                ((CompleteRelation) beforeViewUpdatesOnly)\n                        .withMembers(beforeRelationViewFromAtlas.members());\n            }\n            if (afterRelationView.allRelationsWithSameOsmIdentifier() != null)\n            {\n                ((CompleteRelation) beforeViewUpdatesOnly).withAllRelationsWithSameOsmIdentifier(\n                        beforeRelationViewFromAtlas.allRelationsWithSameOsmIdentifier().stream()\n                                .map(Relation::getIdentifier).collect(Collectors.toList()));\n            }\n            if (afterRelationView.allKnownOsmMembers() != null)\n            {\n                ((CompleteRelation) beforeViewUpdatesOnly).withAllKnownOsmMembers(\n                        beforeRelationViewFromAtlas.allKnownOsmMembers().asBean());\n            }\n            if (afterRelationView.osmRelationIdentifier() != null)\n            {\n                ((CompleteRelation) beforeViewUpdatesOnly).withOsmRelationIdentifier(\n                        beforeRelationViewFromAtlas.osmRelationIdentifier());\n            }\n            final Optional<MultiPolygon> afterGeom = afterRelationView.asMultiPolygon();\n            final Optional<MultiPolygon> beforeGeom = beforeRelationViewFromAtlas.asMultiPolygon();\n            if (afterGeom.isPresent() && beforeGeom.isPresent())\n            {\n                ((CompleteRelation) beforeViewUpdatesOnly)\n                        .withMultiPolygonGeometry(beforeGeom.get());\n            }\n        }\n        else\n        {\n            throw new CoreException(\"Unknown entity type {}\", this.afterView.getType());\n        }\n\n        /*\n         * Add before view of the tags if the updatedView updated the tags.\n         */\n        final Map<String, String> updatedViewTags = this.afterView.getTags();\n        if (updatedViewTags != null)\n        {\n            ((CompleteEntity) beforeViewUpdatesOnly).withTags(beforeViewFromAtlas.getTags());\n        }\n\n        /*\n         * Add before view of relations if updatedView updated relations.\n         */\n        final Set<Relation> updatedViewRelations = this.afterView.relations();\n        if (updatedViewRelations != null)\n        {\n            ((CompleteEntity) beforeViewUpdatesOnly).withRelations(beforeViewFromAtlas.relations());\n        }\n\n        this.beforeView = beforeViewUpdatesOnly;\n    }\n\n    /**\n     * Compute information needed for an OpenStreetMap Change file\n     *\n     * @param atlas\n     *            The atlas with all the needed information (all nodes, etc.)\n     * @param changeType\n     *            The type of change\n     */\n    private void computeRequiredOpenStreetMapChangeInformation(final Atlas atlas,\n            final ChangeType changeType)\n    {\n        final Collection<Location> locationsToFind = new HashSet<>();\n        if (changeType == ChangeType.ADD)\n        {\n            if (Arrays.asList(ItemType.AREA, ItemType.EDGE, ItemType.LINE)\n                    .contains(this.afterView.getType()))\n            {\n                final PolyLine polyLine = getPolyline(atlas, this.afterView);\n                if (polyLine == null)\n                {\n                    return;\n                }\n                locationsToFind.addAll(polyLine);\n            }\n        }\n        else if (changeType == ChangeType.REMOVE\n                && Arrays.asList(ItemType.AREA, ItemType.EDGE, ItemType.LINE)\n                        .contains(this.afterView.getType()))\n        // Only add remove points if there is <i>no</i> chance that a point is used by another\n        // object\n        {\n            // In contrast with ChangeType.ADD, we must use the beforeView.\n            final PolyLine polyLine = getPolyline(atlas, this.beforeView);\n            if (polyLine == null)\n            {\n                return;\n            }\n            this.findNodesToRemove(atlas, polyLine, locationsToFind);\n        }\n        this.buildNodes(atlas, locationsToFind);\n    }\n\n    /**\n     * Find nodes to remove\n     *\n     * @param atlas\n     *            The atlas with the information needed to determine if a node should be removed\n     * @param polyLine\n     *            The polyline that we are deleting -- we check if the only parent of a node is this\n     *            line, and if so, remove it.\n     * @param locationsToFind\n     *            The collection to add the locations to remove to\n     */\n    private void findNodesToRemove(final Atlas atlas, final PolyLine polyLine,\n            final Collection<Location> locationsToFind)\n    {\n        for (final Location point : polyLine)\n        {\n            final Rectangle pointBounds = point.bounds();\n            final Set<LineItem> lines = Iterables.asSet(atlas.lineItemsContaining(point));\n            if (this.afterView instanceof LineItem)\n            {\n                lines.removeIf(\n                        line -> line.getOsmIdentifier() == this.afterView.getOsmIdentifier());\n            }\n\n            final Set<Area> areas = Iterables.asSet(atlas.areasIntersecting(pointBounds));\n            if (this.afterView instanceof Area)\n            {\n                areas.removeIf(\n                        area -> area.getOsmIdentifier() == this.afterView.getOsmIdentifier());\n            }\n\n            final Set<Relation> relations = Iterables\n                    .asSet(atlas.relationsWithEntitiesIntersecting(pointBounds));\n            atlas.relationsWithEntitiesWithin(point.bounds()).forEach(relations::add);\n            if (this.afterView instanceof Relation)\n            {\n                relations.removeIf(relation -> relation.getOsmIdentifier() == this.afterView\n                        .getOsmIdentifier());\n            }\n            if (lines.isEmpty() && relations.isEmpty() && areas.isEmpty())\n            {\n                locationsToFind.add(point);\n            }\n        }\n\n    }\n\n    /**\n     * Check that this {@link FeatureChange} is not shallow. A shallow {@link FeatureChange} is one\n     * whose CompleteEntity only contains an identifier.\n     */\n    private void validateNotShallow()\n    {\n        if (this.changeType == ChangeType.ADD && ((CompleteEntity) this.afterView).isShallow())\n        {\n            throw new CoreException(\"{} was shallow (i.e. it contained only an identifier)\", this);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/FeatureChangeBoundsExpander.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.utilities.maps.MultiMapWithSet;\n\n/**\n * Expand the size of bounds for features that belong to a relation, or for nodes that are connected\n * to edges, thus expanding the full geographical impact of a FeatureChange\n * \n * @author matthieun\n */\npublic class FeatureChangeBoundsExpander\n{\n    private final Set<FeatureChange> featureChanges;\n    private Atlas atlas;\n    private final Predicate<FeatureChange> needsUpdate = featureChange ->\n    {\n        if (featureChange.getItemType() == ItemType.NODE)\n        {\n            return true;\n        }\n        if (featureChange.getChangeType() == ChangeType.ADD)\n        {\n            // For relation members, only look at removes\n            return false;\n        }\n        final Set<Relation> relations = featureChange.getAfterView().relations();\n        if (relations != null && !relations.isEmpty())\n        {\n            return true;\n        }\n        final AtlasEntity entity = this.atlas.entity(featureChange.getIdentifier(),\n                featureChange.getItemType());\n        return entity != null && !entity.relations().isEmpty();\n    };\n    private final Set<FeatureChange> result = new HashSet<>();\n    private final Set<FeatureChange> featureChangesToUpdate = new HashSet<>();\n    private final MultiMapWithSet<AtlasEntityKey, Rectangle> typeIdentifierToExtensionBounds = new MultiMapWithSet<>();\n    private final Map<AtlasEntityKey, FeatureChange> typeIdentifierToFeatureChange = new HashMap<>();\n\n    public FeatureChangeBoundsExpander(final Set<FeatureChange> featureChanges, final Atlas atlas)\n    {\n        this.featureChanges = featureChanges;\n        this.atlas = atlas;\n    }\n\n    public Set<FeatureChange> apply()\n    {\n        if (!this.result.isEmpty())\n        {\n            throw new CoreException(\"Cannot apply the same bounds expander twice!\");\n        }\n        this.featureChanges.forEach(featureChange -> this.typeIdentifierToFeatureChange.put(\n                AtlasEntityKey.from(featureChange.getItemType(), featureChange.getIdentifier()),\n                featureChange));\n        findBounds();\n        for (final FeatureChange featureChange : this.featureChangesToUpdate)\n        {\n            final Set<Rectangle> expansionRectangles = this.typeIdentifierToExtensionBounds\n                    .get(AtlasEntityKey.from(featureChange.getItemType(),\n                            featureChange.getIdentifier()));\n            FeatureChange newFeatureChange = featureChange;\n            if (expansionRectangles != null)\n            {\n                newFeatureChange = new FeatureChange(featureChange.getChangeType(),\n                        expanded(featureChange.getAfterView(), expansionRectangles),\n                        featureChange.getBeforeView());\n            }\n\n            this.result.add(newFeatureChange);\n        }\n        return this.result;\n    }\n\n    private AtlasEntity expanded(final AtlasEntity other, final Set<Rectangle> expansionRectangles)\n    {\n        final Rectangle newBounds = Rectangle.forLocated(expansionRectangles);\n        if (other instanceof CompleteNode)\n        {\n            return ((CompleteNode) other).withBoundsExtendedBy(newBounds);\n        }\n        if (other instanceof CompleteEdge)\n        {\n            return ((CompleteEdge) other).withBoundsExtendedBy(newBounds);\n        }\n        if (other instanceof CompleteArea)\n        {\n            return ((CompleteArea) other).withBoundsExtendedBy(newBounds);\n        }\n        if (other instanceof CompleteLine)\n        {\n            return ((CompleteLine) other).withBoundsExtendedBy(newBounds);\n        }\n        if (other instanceof CompletePoint)\n        {\n            return ((CompletePoint) other).withBoundsExtendedBy(newBounds);\n        }\n        if (other instanceof CompleteRelation)\n        {\n            return ((CompleteRelation) other).withBoundsExtendedBy(newBounds);\n        }\n        throw new CoreException(\"AtlasEntity is of a non-workable type: {}\",\n                other.getClass().getName());\n    }\n\n    private void findBounds() // NOSONAR\n    {\n        for (final FeatureChange featureChange : this.featureChanges)\n        {\n            final ItemType itemType = featureChange.getItemType();\n            if (this.needsUpdate.test(featureChange))\n            {\n                this.featureChangesToUpdate.add(featureChange);\n            }\n            else\n            {\n                this.result.add(featureChange);\n            }\n            if (itemType == ItemType.RELATION)\n            {\n                findBoundsFromRelation((Relation) featureChange.getAfterView());\n                final Relation relationFromAtlas = this.atlas\n                        .relation(featureChange.getIdentifier());\n                if (relationFromAtlas != null)\n                {\n                    findBoundsFromRelation(relationFromAtlas);\n                }\n            }\n            if (itemType == ItemType.EDGE)\n            {\n                findBoundsFromEdge((Edge) featureChange.getAfterView());\n                final Edge edgeFromAtlas = this.atlas.edge(featureChange.getIdentifier());\n                if (edgeFromAtlas != null)\n                {\n                    findBoundsFromEdge(edgeFromAtlas);\n                }\n            }\n        }\n        for (final Edge edge : this.atlas.edges())\n        {\n            final AtlasEntityKey startKey = AtlasEntityKey.from(ItemType.NODE,\n                    edge.start().getIdentifier());\n            final AtlasEntityKey endKey = AtlasEntityKey.from(ItemType.NODE,\n                    edge.end().getIdentifier());\n            if (this.typeIdentifierToFeatureChange.containsKey(startKey)\n                    || this.typeIdentifierToFeatureChange.containsKey(endKey))\n            {\n                findBoundsFromEdge(edge);\n            }\n        }\n        for (final Relation relation : this.atlas.relations())\n        {\n            final Set<AtlasEntityKey> memberKeys = new HashSet<>();\n            relation.members().forEach(member -> memberKeys.add(AtlasEntityKey\n                    .from(member.getEntity().getType(), member.getEntity().getIdentifier())));\n            if (memberKeys.stream().anyMatch(this.typeIdentifierToFeatureChange::containsKey))\n            {\n                findBoundsFromRelation(relation);\n            }\n        }\n    }\n\n    private void findBoundsFromEdge(final Edge edge)\n    {\n        final Node start = edge.start();\n        final Node end = edge.end();\n        final Rectangle bounds = edge.bounds();\n        if (start != null)\n        {\n            this.typeIdentifierToExtensionBounds\n                    .add(AtlasEntityKey.from(ItemType.NODE, start.getIdentifier()), bounds);\n        }\n        if (end != null)\n        {\n            this.typeIdentifierToExtensionBounds\n                    .add(AtlasEntityKey.from(ItemType.NODE, end.getIdentifier()), bounds);\n        }\n    }\n\n    private void findBoundsFromRelation(final Relation relation)\n    {\n        final RelationMemberList members = relation.members();\n        if (members != null && !members.isEmpty())\n        {\n            final Rectangle bounds = relation.bounds();\n            members.forEach(relationMember ->\n            {\n                final AtlasEntity entity = relationMember.getEntity();\n                this.typeIdentifierToExtensionBounds\n                        .add(AtlasEntityKey.from(entity.getType(), entity.getIdentifier()), bounds);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/FeatureChangeMergeGroup.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * Defines the guard-rails that guides the groups of {@link FeatureChange}s that can be merged\n * together in a {@link Change}.\n *\n * @author Yazad Khambata\n */\npublic class FeatureChangeMergeGroup\n{\n    private final ItemType itemType;\n    private final Long identifier;\n    private final ChangeType changeType;\n\n    public static FeatureChangeMergeGroup from(final FeatureChange featureChange)\n    {\n        return new FeatureChangeMergeGroup(featureChange.getItemType(),\n                featureChange.getIdentifier(), featureChange.getChangeType());\n    }\n\n    public FeatureChangeMergeGroup(final ItemType itemType, final Long identifier,\n            final ChangeType changeType)\n    {\n        super();\n        this.itemType = itemType;\n        this.identifier = identifier;\n        this.changeType = changeType;\n    }\n\n    public ChangeType getChangeType()\n    {\n        return this.changeType;\n    }\n\n    public Long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public ItemType getItemType()\n    {\n        return this.itemType;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/FeatureChangeMergingHelpers.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.change.MemberMerger.MergedMemberBean;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A utility class for the various {@link FeatureChange} merge helper functions.\n *\n * @author lcram\n */\npublic final class FeatureChangeMergingHelpers\n{\n    static final String IN_EDGE_IDENTIFIERS_FIELD = \"inEdgeIdentifiers\";\n    static final String OUT_EDGE_IDENTIFIERS_FIELD = \"outEdgeIdentifiers\";\n    static final String GEOMETRIC_RELATIONS_FIELD = \"geometricRelations\";\n    private static final String AFTER_ENTITY_RIGHT_WAS_NULL = \"afterEntityRight was null, this should never happen!\";\n    private static final String AFTER_ENTITY_LEFT_WAS_NULL = \"afterEntityLeft was null, this should never happen!\";\n    private static final Logger logger = LoggerFactory.getLogger(FeatureChangeMergingHelpers.class);\n\n    /**\n     * Merge two {@link ChangeType#ADD} {@link FeatureChange}s into a single {@link FeatureChange}.\n     *\n     * @param left\n     *            the left {@link FeatureChange}\n     * @param right\n     *            the right {@link FeatureChange}\n     * @return the merged {@link FeatureChange}s\n     */\n    public static FeatureChange mergeADDFeatureChangePair(final FeatureChange left,\n            final FeatureChange right)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        final MergedMemberBean<Map<String, String>> mergedTagsBean = new MemberMerger.Builder<Map<String, String>>()\n                .withMemberName(\"tags\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight).withMemberExtractor(Taggable::getTags)\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleTagMerger)\n                .withAfterViewConsistentBeforeViewMerger(MemberMergeStrategies.diffBasedTagMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryTagMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryTagMerger).build()\n                .mergeMember();\n\n        final MergedMemberBean<Set<Long>> mergedParentRelationsBean = new MemberMerger.Builder<Set<Long>>()\n                .withMemberName(\"parentRelations\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(atlasEntity -> atlasEntity.relations() == null ? null\n                        : atlasEntity.relations().stream().map(Relation::getIdentifier)\n                                .collect(Collectors.toSet()))\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleLongSetMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedLongSetMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongSetMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLongSetMerger).build()\n                .mergeMember();\n\n        if (afterEntityLeft instanceof LocationItem)\n        {\n            return mergeLocationItems(left, right, mergedTagsBean, mergedParentRelationsBean);\n        }\n        else if (afterEntityLeft instanceof LineItem)\n        {\n            return mergeLineItems(left, right, mergedTagsBean, mergedParentRelationsBean);\n        }\n        else if (afterEntityLeft instanceof Area)\n        {\n            return mergeAreas(left, right, mergedTagsBean, mergedParentRelationsBean);\n        }\n        else if (afterEntityLeft instanceof Relation)\n        {\n            return mergeRelations(left, right, mergedTagsBean, mergedParentRelationsBean);\n        }\n        else\n        {\n            throw new CoreException(\"Unknown AtlasEntity subtype {}\",\n                    afterEntityLeft.getClass().getName());\n        }\n    }\n\n    /**\n     * Merge the meta-data of two {@link FeatureChange}s\n     *\n     * @param left\n     *            The left {@link FeatureChange}\n     * @param right\n     *            The right {@link FeatureChange}\n     * @return The concatenated meta-data map\n     */\n    public static Map<String, String> mergeMetaData(final FeatureChange left,\n            final FeatureChange right)\n    {\n        final Map<String, String> result = new HashMap<>();\n        final Map<String, String> leftMap = left.getMetaData();\n        final Map<String, String> rightMap = right.getMetaData();\n        for (final Map.Entry<String, String> leftEntry : leftMap.entrySet())\n        {\n            if (rightMap.containsKey(leftEntry.getKey()))\n            {\n                final List<String> leftValues = Arrays.asList(leftEntry.getValue().split(\",\"));\n                final List<String> rightValues = Arrays\n                        .asList(rightMap.get(leftEntry.getKey()).split(\",\"));\n                final String mergedValue;\n                if (leftValues.equals(rightValues))\n                {\n                    mergedValue = new StringList(leftValues).join(\",\");\n                }\n                else\n                {\n                    final SortedSet<String> values = new TreeSet<>();\n                    values.addAll(leftValues);\n                    values.addAll(rightValues);\n                    mergedValue = new StringList(values).join(\",\");\n                }\n                result.put(leftEntry.getKey(), mergedValue);\n            }\n            else\n            {\n                result.put(leftEntry.getKey(), leftEntry.getValue());\n            }\n        }\n        for (final Map.Entry<String, String> rightEntry : rightMap.entrySet())\n        {\n            if (!result.containsKey(rightEntry.getKey()))\n            {\n                result.put(rightEntry.getKey(), rightEntry.getValue());\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Merge two {@link ChangeType#REMOVE} {@link FeatureChange}s into a single\n     * {@link FeatureChange}. This method only needs to handle merging the beforeViews, since the\n     * afterViews would be null. Additionally, there are only a few beforeView fields that even need\n     * to be merged in the first place, namely {@link RelationBean}s and {@link Node} in/out edge\n     * identifier sets.\n     *\n     * @param left\n     *            the left {@link FeatureChange}\n     * @param right\n     *            the right {@link FeatureChange}\n     * @return the merged {@link FeatureChange}s\n     */\n    public static FeatureChange mergeREMOVEFeatureChangePair(final FeatureChange left,\n            final FeatureChange right)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n\n        /*\n         * For nodes, we need to merge the beforeViews of the in/out edge identifier sets.\n         */\n        if (beforeEntityLeft instanceof Node)\n        {\n            final Node beforeNodeLeft = (Node) beforeEntityLeft;\n            final Node beforeNodeRight = (Node) beforeEntityRight;\n            final CompleteNode mergedBeforeNode = CompleteNode.from(beforeNodeLeft);\n            final SortedSet<Long> leftInEdgeIdentifiers = new TreeSet<>(beforeNodeLeft.inEdges()\n                    .stream().map(Edge::getIdentifier).collect(Collectors.toSet()));\n            final SortedSet<Long> rightInEdgeIdentifiers = new TreeSet<>(beforeNodeRight.inEdges()\n                    .stream().map(Edge::getIdentifier).collect(Collectors.toSet()));\n            final SortedSet<Long> leftOutEdgeIdentifiers = new TreeSet<>(beforeNodeLeft.outEdges()\n                    .stream().map(Edge::getIdentifier).collect(Collectors.toSet()));\n            final SortedSet<Long> rightOutEdgeIdentifiers = new TreeSet<>(beforeNodeRight.outEdges()\n                    .stream().map(Edge::getIdentifier).collect(Collectors.toSet()));\n\n            if (!leftInEdgeIdentifiers.equals(rightInEdgeIdentifiers))\n            {\n                mergedBeforeNode\n                        .withInEdgeIdentifiers(MemberMergeStrategies.simpleLongSortedSetMerger\n                                .apply(leftInEdgeIdentifiers, rightInEdgeIdentifiers));\n            }\n            if (!leftOutEdgeIdentifiers.equals(rightOutEdgeIdentifiers))\n            {\n                mergedBeforeNode\n                        .withOutEdgeIdentifiers(MemberMergeStrategies.simpleLongSortedSetMerger\n                                .apply(leftOutEdgeIdentifiers, rightOutEdgeIdentifiers));\n            }\n            return new FeatureChange(ChangeType.REMOVE, left.getAfterView(), mergedBeforeNode);\n        }\n        /*\n         * For relations, we need to merge the beforeViews of the members RelationBean.\n         */\n        else if (beforeEntityLeft instanceof Relation)\n        {\n            final Relation beforeRelationLeft = (Relation) beforeEntityLeft;\n            final Relation beforeRelationRight = (Relation) beforeEntityRight;\n            final CompleteRelation mergedBeforeRelation = CompleteRelation.from(beforeRelationLeft);\n            final RelationBean leftMembers = beforeRelationLeft.members().asBean();\n            final RelationBean rightMembers = beforeRelationRight.members().asBean();\n\n            if (!leftMembers.equalsIncludingExplicitlyExcluded(rightMembers))\n            {\n                mergedBeforeRelation.withMembers(RelationBean.mergeBeans(leftMembers, rightMembers),\n                        Rectangle.forLocated(beforeRelationLeft, beforeRelationRight));\n            }\n            return new FeatureChange(ChangeType.REMOVE, left.getAfterView(), mergedBeforeRelation);\n        }\n        /*\n         * For any other case, there is no need to merge anything. Just arbitrarily return the left\n         * side.\n         */\n        else\n        {\n            return left;\n        }\n    }\n\n    private static FeatureChange mergeAreas(final FeatureChange left, final FeatureChange right,\n            final MergedMemberBean<Map<String, String>> mergedTagsBean,\n            final MergedMemberBean<Set<Long>> mergedParentRelationsBean)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        /*\n         * The polygon merger ensure that the afterEntity polygons either: 1) exactly match or 2)\n         * are reconcilable based on the beforeView. Additionally, this step also ensures that the\n         * beforeViews, if present, had equivalent geometry.\n         */\n        final MergedMemberBean<Polygon> mergedPolygonBean = new MemberMerger.Builder<Polygon>()\n                .withMemberName(\"polygon\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(atlasEntity -> ((Area) atlasEntity).asPolygon())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.autofailBinaryPolygonMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedPolygonMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryPolygonMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryPolygonMerger).build()\n                .mergeMember();\n\n        final MergedMemberBean<Set<Long>> mergedGeometricParentRelationsBean = new MemberMerger.Builder<Set<Long>>()\n                .withMemberName(GEOMETRIC_RELATIONS_FIELD).withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(\n                        atlasEntity -> ((CompleteArea) atlasEntity).geometricRelationIdentifiers())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleLongSetMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedLongSetMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongSetMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLongSetMerger).build()\n                .mergeMember();\n\n        final CompleteArea mergedAfterArea = new CompleteArea(left.getIdentifier(),\n                mergedPolygonBean.getMergedAfterMember(), mergedTagsBean.getMergedAfterMember(),\n                mergedParentRelationsBean.getMergedAfterMember());\n        mergedAfterArea.withGeometricRelationIdentifiers(\n                mergedGeometricParentRelationsBean.getMergedAfterMember());\n        mergedAfterArea.withBoundsExtendedBy(afterEntityLeft.bounds());\n        mergedAfterArea.withBoundsExtendedBy(afterEntityRight.bounds());\n\n        final CompleteArea mergedBeforeArea;\n        /*\n         * Here we just arbitrarily use the left side entity. We have already asserted that both\n         * left and right explicitly provided or explicitly excluded a beforeView. At this point, we\n         * have also ensured that both beforeViews, if present, were consistent (or we merged them\n         * if necessary). Therefore it is safe to arbitrarily choose one from which to \"shallowFrom\"\n         * clone a new CompleteEntity.\n         */\n        if (beforeEntityLeft != null)\n        {\n            mergedBeforeArea = CompleteArea.shallowFrom((Area) beforeEntityLeft)\n                    .withTags(mergedTagsBean.getMergedBeforeMember())\n                    .withPolygon(mergedPolygonBean.getMergedBeforeMember())\n                    .withRelationIdentifiers(mergedParentRelationsBean.getMergedBeforeMember())\n                    .withGeometricRelationIdentifiers(\n                            mergedGeometricParentRelationsBean.getMergedBeforeMember());\n        }\n        else\n        {\n            mergedBeforeArea = null;\n        }\n        return new FeatureChange(ChangeType.ADD, mergedAfterArea, mergedBeforeArea);\n    }\n\n    private static FeatureChange mergeEdges(final FeatureChange left, final FeatureChange right,\n            final MergedMemberBean<PolyLine> mergedPolyLineBean,\n            final MergedMemberBean<Map<String, String>> mergedTagsBean,\n            final MergedMemberBean<Set<Long>> mergedParentRelationsBean)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        final MergedMemberBean<Long> mergedStartNodeIdentifierBean = new MemberMerger.Builder<Long>()\n                .withMemberName(\"startNode\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(edge -> ((Edge) edge).start() == null ? null\n                        : ((Edge) edge).start().getIdentifier())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.autofailBinaryLongMerger)\n                .withAfterViewConsistentBeforeViewMerger(MemberMergeStrategies.diffBasedLongMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLongMerger).build()\n                .mergeMember();\n\n        final MergedMemberBean<Long> mergedEndNodeIdentifierBean = new MemberMerger.Builder<Long>()\n                .withMemberName(\"endNode\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(edge -> ((Edge) edge).end() == null ? null\n                        : ((Edge) edge).end().getIdentifier())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.autofailBinaryLongMerger)\n                .withAfterViewConsistentBeforeViewMerger(MemberMergeStrategies.diffBasedLongMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLongMerger).build()\n                .mergeMember();\n\n        final MergedMemberBean<Set<Long>> mergedGeometricParentRelationsBean = new MemberMerger.Builder<Set<Long>>()\n                .withMemberName(GEOMETRIC_RELATIONS_FIELD).withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(\n                        atlasEntity -> ((CompleteEdge) atlasEntity).geometricRelationIdentifiers())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleLongSetMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedLongSetMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongSetMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLongSetMerger).build()\n                .mergeMember();\n\n        final CompleteEdge mergedAfterEdge = new CompleteEdge(left.getIdentifier(),\n                mergedPolyLineBean.getMergedAfterMember(), mergedTagsBean.getMergedAfterMember(),\n                mergedStartNodeIdentifierBean.getMergedAfterMember(),\n                mergedEndNodeIdentifierBean.getMergedAfterMember(),\n                mergedParentRelationsBean.getMergedAfterMember());\n        mergedAfterEdge.withBoundsExtendedBy(afterEntityLeft.bounds());\n        mergedAfterEdge.withBoundsExtendedBy(afterEntityRight.bounds());\n        mergedAfterEdge.withGeometricRelationIdentifiers(\n                mergedGeometricParentRelationsBean.getMergedAfterMember());\n\n        final CompleteEdge mergedBeforeEdge;\n        /*\n         * Here we just arbitrarily use the left side entity. We have already asserted that both\n         * left and right explicitly provided or explicitly excluded a beforeView. At this point, we\n         * have also ensured that both beforeViews, if present, were consistent (or we merged them\n         * if necessary). Therefore it is safe to arbitrarily choose one from which to \"shallowFrom\"\n         * clone a new CompleteEntity.\n         */\n        if (beforeEntityLeft != null)\n        {\n            mergedBeforeEdge = CompleteEdge.shallowFrom((Edge) beforeEntityLeft)\n                    .withStartNodeIdentifier(mergedStartNodeIdentifierBean.getMergedBeforeMember())\n                    .withEndNodeIdentifier(mergedEndNodeIdentifierBean.getMergedBeforeMember())\n                    .withTags(mergedTagsBean.getMergedBeforeMember())\n                    .withPolyLine(mergedPolyLineBean.getMergedBeforeMember())\n                    .withRelationIdentifiers(mergedParentRelationsBean.getMergedBeforeMember())\n                    .withGeometricRelationIdentifiers(\n                            mergedGeometricParentRelationsBean.getMergedBeforeMember());\n        }\n        else\n        {\n            mergedBeforeEdge = null;\n        }\n        return new FeatureChange(ChangeType.ADD, mergedAfterEdge, mergedBeforeEdge);\n    }\n\n    private static FeatureChange mergeLineItems(final FeatureChange left, final FeatureChange right,\n            final MergedMemberBean<Map<String, String>> mergedTagsBean,\n            final MergedMemberBean<Set<Long>> mergedParentRelationsBean)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        /*\n         * The polyline merger ensure that the afterEntity polylines either: 1) exactly match or 2)\n         * are reconcilable based on the beforeView. Additionally, this step also ensures that the\n         * beforeViews, if present, had equivalent geometry.\n         */\n        final MergedMemberBean<PolyLine> mergedPolyLineBean = new MemberMerger.Builder<PolyLine>()\n                .withMemberName(\"polyLine\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(atlasEntity -> ((LineItem) atlasEntity).asPolyLine())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.autofailBinaryPolyLineMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedPolyLineMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryPolyLineMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryPolyLineMerger).build()\n                .mergeMember();\n\n        if (afterEntityLeft instanceof Edge)\n        {\n            return mergeEdges(left, right, mergedPolyLineBean, mergedTagsBean,\n                    mergedParentRelationsBean);\n        }\n        else if (afterEntityLeft instanceof Line)\n        {\n            return mergeLines(left, right, mergedPolyLineBean, mergedTagsBean,\n                    mergedParentRelationsBean);\n        }\n        else\n        {\n            throw new CoreException(\"Unknown AtlasEntity subtype {}\",\n                    afterEntityLeft.getClass().getName());\n        }\n    }\n\n    private static FeatureChange mergeLines(final FeatureChange left, final FeatureChange right,\n            final MergedMemberBean<PolyLine> mergedPolyLineBean,\n            final MergedMemberBean<Map<String, String>> mergedTagsBean,\n            final MergedMemberBean<Set<Long>> mergedParentRelationsBean)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        final MergedMemberBean<Set<Long>> mergedGeometricParentRelationsBean = new MemberMerger.Builder<Set<Long>>()\n                .withMemberName(GEOMETRIC_RELATIONS_FIELD).withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(\n                        atlasEntity -> ((CompleteLine) atlasEntity).geometricRelationIdentifiers())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleLongSetMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedLongSetMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongSetMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLongSetMerger).build()\n                .mergeMember();\n\n        final CompleteLine mergedAfterLine = new CompleteLine(left.getIdentifier(),\n                mergedPolyLineBean.getMergedAfterMember(), mergedTagsBean.getMergedAfterMember(),\n                mergedParentRelationsBean.getMergedAfterMember());\n        mergedAfterLine.withBoundsExtendedBy(afterEntityLeft.bounds());\n        mergedAfterLine.withBoundsExtendedBy(afterEntityRight.bounds());\n        mergedAfterLine.withGeometricRelationIdentifiers(\n                mergedGeometricParentRelationsBean.getMergedAfterMember());\n\n        final CompleteLine mergedBeforeLine;\n        /*\n         * Here we just arbitrarily use the left side entity. We have already asserted that both\n         * left and right explicitly provided or explicitly excluded a beforeView. At this point, we\n         * have also ensured that both beforeViews, if present, were consistent (or we merged them\n         * if necessary). Therefore it is safe to arbitrarily choose one from which to \"shallowFrom\"\n         * clone a new CompleteEntity.\n         */\n        if (beforeEntityLeft != null)\n        {\n            mergedBeforeLine = CompleteLine.shallowFrom((Line) beforeEntityLeft)\n                    .withTags(mergedTagsBean.getMergedBeforeMember())\n                    .withPolyLine(mergedPolyLineBean.getMergedBeforeMember())\n                    .withRelationIdentifiers(mergedParentRelationsBean.getMergedBeforeMember())\n                    .withGeometricRelationIdentifiers(\n                            mergedGeometricParentRelationsBean.getMergedBeforeMember());\n        }\n        else\n        {\n            mergedBeforeLine = null;\n        }\n        return new FeatureChange(ChangeType.ADD, mergedAfterLine, mergedBeforeLine);\n    }\n\n    private static FeatureChange mergeLocationItems(final FeatureChange left,\n            final FeatureChange right, final MergedMemberBean<Map<String, String>> mergedTagsBean,\n            final MergedMemberBean<Set<Long>> mergedParentRelationsBean)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        /*\n         * The location merger ensure that the afterEntity locations either: 1) exactly match or 2)\n         * are reconcilable based on the beforeView. Additionally, this step also ensures that the\n         * beforeViews, if present, had equivalent geometry.\n         */\n        final MergedMemberBean<Location> mergedLocationBean = new MemberMerger.Builder<Location>()\n                .withMemberName(\"location\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(atlasEntity -> ((LocationItem) atlasEntity).getLocation())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.autofailBinaryLocationMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedLocationMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLocationMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLocationMerger).build()\n                .mergeMember();\n\n        if (afterEntityLeft instanceof Node)\n        {\n            return mergeNodes(left, right, mergedLocationBean, mergedTagsBean,\n                    mergedParentRelationsBean);\n        }\n        else if (afterEntityLeft instanceof Point)\n        {\n            return mergePoints(left, right, mergedLocationBean, mergedTagsBean,\n                    mergedParentRelationsBean);\n        }\n        else\n        {\n            throw new CoreException(\"Unknown LocationItem subtype {}\",\n                    afterEntityLeft.getClass().getName());\n        }\n    }\n\n    private static FeatureChange mergeNodes(final FeatureChange left, final FeatureChange right,\n            final MergedMemberBean<Location> mergedLocationBean,\n            final MergedMemberBean<Map<String, String>> mergedTagsBean,\n            final MergedMemberBean<Set<Long>> mergedParentRelationsBean)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        final MergedMemberBean<SortedSet<Long>> mergedInEdgeIdentifiersBean = new MemberMerger.Builder<SortedSet<Long>>()\n                .withMemberName(IN_EDGE_IDENTIFIERS_FIELD).withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(atlasEntity -> ((Node) atlasEntity).inEdges() == null ? null\n                        : ((Node) atlasEntity).inEdges().stream().map(Edge::getIdentifier)\n                                .collect(Collectors.toCollection(TreeSet::new)))\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleLongSortedSetMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedLongSortedSetMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongSortedSetMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.simpleLongSortedSetMerger)\n                .useHackForMergingConflictingConnectedEdgeSetBeforeViews(\n                        (CompleteNode) afterEntityLeft, (CompleteNode) afterEntityRight)\n                .build().mergeMember();\n\n        final MergedMemberBean<SortedSet<Long>> mergedOutEdgeIdentifiersBean = new MemberMerger.Builder<SortedSet<Long>>()\n                .withMemberName(OUT_EDGE_IDENTIFIERS_FIELD).withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(atlasEntity -> ((Node) atlasEntity).outEdges() == null ? null\n                        : ((Node) atlasEntity).outEdges().stream().map(Edge::getIdentifier)\n                                .collect(Collectors.toCollection(TreeSet::new)))\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleLongSortedSetMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedLongSortedSetMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongSortedSetMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.simpleLongSortedSetMerger)\n                .useHackForMergingConflictingConnectedEdgeSetBeforeViews(\n                        (CompleteNode) afterEntityLeft, (CompleteNode) afterEntityRight)\n                .build().mergeMember();\n\n        final CompleteNode mergedAfterNode = new CompleteNode(left.getIdentifier(),\n                mergedLocationBean.getMergedAfterMember(), mergedTagsBean.getMergedAfterMember(),\n                mergedInEdgeIdentifiersBean.getMergedAfterMember(),\n                mergedOutEdgeIdentifiersBean.getMergedAfterMember(),\n                mergedParentRelationsBean.getMergedAfterMember());\n        mergedAfterNode.withBoundsExtendedBy(afterEntityLeft.bounds());\n        mergedAfterNode.withBoundsExtendedBy(afterEntityRight.bounds());\n        /*\n         * We need to merge the explicitlyExcluded sets from the left and right CompleteNodes. This\n         * simple merge will always succeed, since the sets are key only.\n         */\n        mergedAfterNode.setExplicitlyExcludedInEdgeIdentifiers(\n                MemberMergeStrategies.simpleLongSetMerger.apply(\n                        ((CompleteNode) afterEntityLeft).explicitlyExcludedInEdgeIdentifiers(),\n                        ((CompleteNode) afterEntityRight).explicitlyExcludedInEdgeIdentifiers()));\n\n        mergedAfterNode.setExplicitlyExcludedOutEdgeIdentifiers(\n                MemberMergeStrategies.simpleLongSetMerger.apply(\n                        ((CompleteNode) afterEntityLeft).explicitlyExcludedOutEdgeIdentifiers(),\n                        ((CompleteNode) afterEntityRight).explicitlyExcludedOutEdgeIdentifiers()));\n\n        final CompleteNode mergedBeforeNode;\n        /*\n         * Here we just arbitrarily use the left side entity. We have already asserted that both\n         * left and right explicitly provided or explicitly excluded a beforeView. At this point, we\n         * have also ensured that both beforeViews, if present, were consistent (or we merged them\n         * if necessary). Therefore it is safe to arbitrarily choose one from which to \"shallowFrom\"\n         * clone a new CompleteEntity.\n         */\n        if (beforeEntityLeft != null)\n        {\n            mergedBeforeNode = CompleteNode.shallowFrom((Node) beforeEntityLeft)\n                    .withInEdgeIdentifiers(mergedInEdgeIdentifiersBean.getMergedBeforeMember())\n                    .withOutEdgeIdentifiers(mergedOutEdgeIdentifiersBean.getMergedBeforeMember())\n                    .withTags(mergedTagsBean.getMergedBeforeMember())\n                    .withLocation(mergedLocationBean.getMergedBeforeMember())\n                    .withRelationIdentifiers(mergedParentRelationsBean.getMergedBeforeMember());\n        }\n        else\n        {\n            mergedBeforeNode = null;\n        }\n        return new FeatureChange(ChangeType.ADD, mergedAfterNode, mergedBeforeNode);\n    }\n\n    private static FeatureChange mergePoints(final FeatureChange left, final FeatureChange right,\n            final MergedMemberBean<Location> mergedLocationBean,\n            final MergedMemberBean<Map<String, String>> mergedTagsBean,\n            final MergedMemberBean<Set<Long>> mergedParentRelationsBean)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        final CompletePoint mergedAfterPoint = new CompletePoint(left.getIdentifier(),\n                mergedLocationBean.getMergedAfterMember(), mergedTagsBean.getMergedAfterMember(),\n                mergedParentRelationsBean.getMergedAfterMember());\n        mergedAfterPoint.withBoundsExtendedBy(afterEntityLeft.bounds());\n        mergedAfterPoint.withBoundsExtendedBy(afterEntityRight.bounds());\n\n        final CompletePoint mergedBeforePoint;\n        /*\n         * Here we just arbitrarily use the left side entity. We have already asserted that both\n         * left and right explicitly provided or explicitly excluded a beforeView. At this point, we\n         * have also ensured that both beforeViews, if present, were consistent (or we merged them\n         * if necessary). Therefore it is safe to arbitrarily choose one from which to \"shallowFrom\"\n         * clone a new CompleteEntity.\n         */\n        if (beforeEntityLeft != null)\n        {\n            mergedBeforePoint = CompletePoint.shallowFrom((Point) beforeEntityLeft)\n                    .withTags(mergedTagsBean.getMergedBeforeMember())\n                    .withLocation(mergedLocationBean.getMergedBeforeMember())\n                    .withRelationIdentifiers(mergedParentRelationsBean.getMergedBeforeMember());\n        }\n        else\n        {\n            mergedBeforePoint = null;\n        }\n        return new FeatureChange(ChangeType.ADD, mergedAfterPoint, mergedBeforePoint);\n    }\n\n    private static FeatureChange mergeRelations(final FeatureChange left, final FeatureChange right,\n            final MergedMemberBean<Map<String, String>> mergedTagsBean,\n            final MergedMemberBean<Set<Long>> mergedParentRelationsBean)\n    {\n        final AtlasEntity beforeEntityLeft = left.getBeforeView();\n        final AtlasEntity afterEntityLeft = left.getAfterView();\n        final AtlasEntity beforeEntityRight = right.getBeforeView();\n        final AtlasEntity afterEntityRight = right.getAfterView();\n\n        if (afterEntityLeft == null)\n        {\n            throw new CoreException(AFTER_ENTITY_LEFT_WAS_NULL);\n        }\n        if (afterEntityRight == null)\n        {\n            throw new CoreException(AFTER_ENTITY_RIGHT_WAS_NULL);\n        }\n\n        final MergedMemberBean<RelationBean> mergedMembersBean = new MemberMerger.Builder<RelationBean>()\n                .withMemberName(\"relationMembers\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(entity -> ((Relation) entity).members() == null ? null\n                        : ((Relation) entity).members().asBean())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleRelationBeanMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedRelationBeanMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.conflictingBeforeViewRelationBeanMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.simpleRelationBeanMerger).build()\n                .mergeMember();\n\n        final MergedMemberBean<Set<Long>> mergedAllRelationsWithSameOsmIdentifierBean = new MemberMerger.Builder<Set<Long>>()\n                .withMemberName(\"allRelationsWithSameOsmIdentifier\")\n                .withBeforeEntityLeft(beforeEntityLeft).withAfterEntityLeft(afterEntityLeft)\n                .withBeforeEntityRight(beforeEntityRight).withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(atlasEntity -> ((Relation) atlasEntity)\n                        .allRelationsWithSameOsmIdentifier() == null\n                                ? null\n                                : ((Relation) atlasEntity).allRelationsWithSameOsmIdentifier()\n                                        .stream().map(Relation::getIdentifier)\n                                        .collect(Collectors.toSet()))\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleLongSetMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedLongSetMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongSetMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLongSetMerger).build()\n                .mergeMember();\n\n        final MergedMemberBean<RelationBean> mergedAllKnownMembersBean = new MemberMerger.Builder<RelationBean>()\n                .withMemberName(\"allKnownOsmMembers\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(\n                        entity -> ((Relation) entity).allKnownOsmMembers() == null ? null\n                                : ((Relation) entity).allKnownOsmMembers().asBean())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.simpleRelationBeanMerger)\n                .withAfterViewConsistentBeforeViewMerger(\n                        MemberMergeStrategies.diffBasedRelationBeanMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.conflictingBeforeViewRelationBeanMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.simpleRelationBeanMerger).build()\n                .mergeMember();\n\n        final MergedMemberBean<Long> mergedOsmRelationIdentifier = new MemberMerger.Builder<Long>()\n                .withMemberName(\"osmRelationIdentifier\").withBeforeEntityLeft(beforeEntityLeft)\n                .withAfterEntityLeft(afterEntityLeft).withBeforeEntityRight(beforeEntityRight)\n                .withAfterEntityRight(afterEntityRight)\n                .withMemberExtractor(entity -> ((Relation) entity).osmRelationIdentifier())\n                .withAfterViewNoBeforeMerger(MemberMergeStrategies.autofailBinaryLongMerger)\n                .withAfterViewConsistentBeforeViewMerger(MemberMergeStrategies.diffBasedLongMerger)\n                .withAfterViewConflictingBeforeViewMerger(\n                        MemberMergeStrategies.autofailQuaternaryLongMerger)\n                .withBeforeViewMerger(MemberMergeStrategies.autofailBinaryLongMerger).build()\n                .mergeMember();\n\n        final Rectangle mergedBounds = Rectangle.forLocated(afterEntityLeft, afterEntityRight);\n\n        final CompleteRelation mergedAfterRelation = new CompleteRelation(left.getIdentifier(),\n                mergedTagsBean.getMergedAfterMember(), mergedBounds,\n                mergedMembersBean.getMergedAfterMember(),\n                mergedAllRelationsWithSameOsmIdentifierBean.getMergedAfterMember() != null\n                        ? mergedAllRelationsWithSameOsmIdentifierBean.getMergedAfterMember()\n                                .stream().collect(Collectors.toList())\n                        : null,\n                mergedAllKnownMembersBean.getMergedAfterMember(),\n                mergedOsmRelationIdentifier.getMergedAfterMember(),\n                mergedParentRelationsBean.getMergedAfterMember());\n        mergedAfterRelation.withBoundsExtendedBy(afterEntityLeft.bounds());\n        mergedAfterRelation.withBoundsExtendedBy(afterEntityRight.bounds());\n\n        if (((CompleteRelation) afterEntityLeft).isOverrideGeometry())\n        {\n            final Optional<MultiPolygon> leftGeom = ((CompleteRelation) afterEntityLeft)\n                    .asMultiPolygon();\n            if (((CompleteRelation) afterEntityRight).isOverrideGeometry())\n            {\n                final Optional<MultiPolygon> rightGeom = ((CompleteRelation) afterEntityRight)\n                        .asMultiPolygon();\n                if (leftGeom.isPresent() && rightGeom.isPresent()\n                        && leftGeom.get().equalsNorm(rightGeom.get()))\n                {\n                    mergedAfterRelation.withMultiPolygonGeometry(rightGeom.get());\n                }\n                else\n                {\n                    throw new CoreException(\n                            \"Problem merging relation override geometries for relation {}!\",\n                            mergedAfterRelation.getIdentifier());\n                }\n            }\n            else\n            {\n                if (leftGeom.isPresent())\n                {\n                    mergedAfterRelation.withMultiPolygonGeometry(leftGeom.get());\n                }\n            }\n        }\n        else if (((CompleteRelation) afterEntityRight).isOverrideGeometry())\n        {\n            final Optional<MultiPolygon> rightGeom = ((CompleteRelation) afterEntityRight)\n                    .asMultiPolygon();\n            if (rightGeom.isPresent())\n            {\n                mergedAfterRelation.withMultiPolygonGeometry(rightGeom.get());\n            }\n        }\n\n        mergedAfterRelation.getRemovedGeometry()\n                .addAll(((CompleteRelation) afterEntityLeft).getRemovedGeometry());\n        mergedAfterRelation.getRemovedGeometry()\n                .addAll(((CompleteRelation) afterEntityRight).getRemovedGeometry());\n\n        mergedAfterRelation.getAddedGeometry()\n                .addAll(((CompleteRelation) afterEntityLeft).getAddedGeometry());\n        mergedAfterRelation.getAddedGeometry()\n                .addAll(((CompleteRelation) afterEntityRight).getAddedGeometry());\n\n        final CompleteRelation mergedBeforeRelation;\n        /*\n         * Here we just arbitrarily use the left side entity. We have already asserted that both\n         * left and right explicitly provided or explicitly excluded a beforeView. At this point, we\n         * have also ensured that both beforeViews, if present, were consistent (or we merged them\n         * if necessary). Therefore it is safe to arbitrarily choose one from which to \"shallowFrom\"\n         * clone a new CompleteEntity.\n         */\n        if (beforeEntityLeft != null)\n        {\n            mergedBeforeRelation = CompleteRelation.shallowFrom((Relation) beforeEntityLeft)\n                    .withMembers(mergedMembersBean.getMergedBeforeMember(),\n                            beforeEntityLeft.bounds())\n                    .withAllRelationsWithSameOsmIdentifier(\n                            mergedAllRelationsWithSameOsmIdentifierBean\n                                    .getMergedAfterMember() != null\n                                            ? mergedAllRelationsWithSameOsmIdentifierBean\n                                                    .getMergedBeforeMember().stream()\n                                                    .collect(Collectors.toList())\n                                            : null)\n                    .withAllKnownOsmMembers(mergedAllKnownMembersBean.getMergedBeforeMember())\n                    .withOsmRelationIdentifier(mergedOsmRelationIdentifier.getMergedBeforeMember())\n                    .withTags(mergedTagsBean.getMergedBeforeMember())\n                    .withRelationIdentifiers(mergedParentRelationsBean.getMergedBeforeMember());\n        }\n        else\n        {\n            mergedBeforeRelation = null;\n        }\n        return new FeatureChange(ChangeType.ADD, mergedAfterRelation, mergedBeforeRelation);\n    }\n\n    private FeatureChangeMergingHelpers()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/MemberMergeStrategies.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.BinaryOperator;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.exception.change.FeatureChangeMergeException;\nimport org.openstreetmap.atlas.exception.change.MergeFailureType;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedRelation;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\nimport org.openstreetmap.atlas.utilities.function.QuaternaryOperator;\nimport org.openstreetmap.atlas.utilities.function.SenaryFunction;\nimport org.openstreetmap.atlas.utilities.function.TernaryOperator;\n\n/**\n * A utility class to store the various merge strategies utilized by the {@link FeatureChange} merge\n * code. These are low-level strategies used to merge the underlying data structures.\n *\n * @author lcram\n */\npublic final class MemberMergeStrategies\n{\n    static final BinaryOperator<Long> autofailBinaryLongMerger = (afterLeft, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_LONG_MERGE,\n                \"autofailBinaryLongMerger:\\n{}\\nvs\\n{}\", afterLeft, afterRight);\n    };\n\n    static final QuaternaryOperator<Long> autofailQuaternaryLongMerger = (beforeLeft, beforeRight,\n            afterLeft, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_LONG_MERGE,\n                \"autofailQuaternaryLongMerger: before:\\n{}\\nvs\\n{}\\nafter:\\n{}\\nvs\\n{}\", beforeLeft,\n                beforeRight, afterLeft, afterRight);\n    };\n\n    static final BinaryOperator<Map<String, String>> autofailBinaryTagMerger = (afterMapLeft,\n            afterMapRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_TAG_MERGE,\n                \"autofailBinaryTagMerger:\\n{}\\nvs\\n{}\", afterMapLeft, afterMapRight);\n    };\n\n    static final QuaternaryOperator<Map<String, String>> autofailQuaternaryTagMerger = (\n            beforeMapLeft, afterMapLeft, beforeMapRight, afterMapRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_TAG_MERGE,\n                \"autofailQuaternaryTagMerger: before:\\n{}\\nvs\\n{}\\nafter:\\n{}\\nvs\\n{}\",\n                beforeMapLeft, beforeMapRight, afterMapLeft, afterMapRight);\n    };\n\n    static final BinaryOperator<Set<Long>> autofailBinaryLongSetMerger = (afterLeft, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_LONG_SET_MERGE,\n                \"autofailBinaryLongSetMerger:\\n{}\\nvs\\n{}\", afterLeft, afterRight);\n    };\n\n    static final QuaternaryOperator<Set<Long>> autofailQuaternaryLongSetMerger = (beforeLeft,\n            afterLeft, beforeRight, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_LONG_SET_MERGE,\n                \"autofailQuaternaryLongSetMerger: before\\n{}\\nvs\\n{}\\nafter:\\n{}\\nvs\\n{}\",\n                beforeLeft, beforeRight, afterLeft, afterRight);\n    };\n\n    static final BinaryOperator<SortedSet<Long>> autofailBinaryLongSortedSetMerger = (afterLeft,\n            afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_LONG_SORTED_SET_MERGE,\n                \"autofailBinaryLongSortedSetMerger:\\n{}\\nvs\\n{}\", afterLeft, afterRight);\n    };\n\n    static final QuaternaryOperator<SortedSet<Long>> autofailQuaternaryLongSortedSetMerger = (\n            beforeLeft, afterLeft, beforeRight, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_LONG_SORTED_SET_MERGE,\n                \"autofailQuaternaryLongSortedSetMerger: before:\\n{}\\nvs\\n{}\\nafter:\\n{}\\nvs\\n{}\",\n                beforeLeft, beforeRight, afterLeft, afterRight);\n    };\n\n    static final BinaryOperator<Location> autofailBinaryLocationMerger = (afterLeft, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_LOCATION_MERGE,\n                \"autofailBinaryLocationMerger:\\n{}\\nvs\\n{}\", afterLeft, afterRight);\n    };\n\n    static final QuaternaryOperator<Location> autofailQuaternaryLocationMerger = (beforeLeft,\n            afterLeft, beforeRight, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_LOCATION_MERGE,\n                \"autofailQuaternaryLocationMerger: before:\\n{}\\nvs\\n{}\\nafter:\\n{}\\nvs\\n{}\",\n                beforeLeft, beforeRight, afterLeft, afterRight);\n    };\n\n    static final BinaryOperator<PolyLine> autofailBinaryPolyLineMerger = (afterLeft, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_POLYLINE_MERGE,\n                \"autofailBinaryPolyLineMerger:\\n{}\\nvs\\n{}\", afterLeft, afterRight);\n    };\n\n    static final QuaternaryOperator<PolyLine> autofailQuaternaryPolyLineMerger = (beforeLeft,\n            afterLeft, beforeRight, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_POLYLINE_MERGE,\n                \"autofailQuaternaryPolyLineMerger: before:\\n{}\\nvs\\n{}\\nafter:\\n{}\\nvs\\n{}\",\n                beforeLeft, beforeRight, afterLeft, afterRight);\n    };\n\n    static final BinaryOperator<Polygon> autofailBinaryPolygonMerger = (afterLeft, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_POLYGON_MERGE,\n                \"autofailBinaryPolygonMerger:\\n{}\\nvs\\n{}\", afterLeft, afterRight);\n    };\n\n    static final QuaternaryOperator<Polygon> autofailQuaternaryPolygonMerger = (beforeLeft,\n            afterLeft, beforeRight, afterRight) ->\n    {\n        throw new FeatureChangeMergeException(MergeFailureType.AUTOFAIL_POLYGON_MERGE,\n                \"autofailQuaternaryPolygonMerger: before:\\n{}\\nvs\\n{}\\nafter:\\n{}\\nvs\\n{}\",\n                beforeLeft, beforeRight, afterLeft, afterRight);\n    };\n\n    static final BinaryOperator<Map<String, String>> simpleTagMerger = (afterMapLeft,\n            afterMapRight) ->\n    {\n        try\n        {\n            return Maps.withMaps(afterMapLeft, afterMapRight);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(MergeFailureType.SIMPLE_TAG_MERGE_FAIL,\n                    \"simpleTagMerger failed\", exception);\n        }\n    };\n\n    static final BinaryOperator<Set<Long>> simpleLongSetMerger = (afterSetLeft, afterSetRight) ->\n    {\n        try\n        {\n            return Sets.withSets(false, afterSetLeft, afterSetRight);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(MergeFailureType.SIMPLE_LONG_SET_MERGE_FAIL,\n                    \"simpleLongSetMerger failed\", exception);\n        }\n    };\n\n    static final BinaryOperator<SortedSet<Long>> simpleLongSortedSetMerger = (afterSetLeft,\n            afterSetRight) ->\n    {\n        try\n        {\n            return Sets.withSortedSets(false, afterSetLeft, afterSetRight);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.SIMPLE_LONG_SORTED_SET_MERGE_FAIL,\n                    \"simpleLongSortedSetMerger failed\", exception);\n        }\n    };\n\n    static final BinaryOperator<RelationBean> simpleRelationBeanMerger = (afterBeanLeft,\n            afterBeanRight) ->\n    {\n        try\n        {\n            return RelationBean.mergeBeans(afterBeanLeft, afterBeanRight);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(MergeFailureType.SIMPLE_RELATION_BEAN_MERGE_FAIL,\n                    \"simpleRelationBeanMerger failed\", exception);\n        }\n    };\n\n    static final TernaryOperator<Long> diffBasedLongMerger = (beforeLong, afterLongLeft,\n            afterLongRight) ->\n    {\n        try\n        {\n            return (Long) getDiffBasedMutuallyExclusiveMerger().apply(beforeLong, afterLongLeft,\n                    afterLongRight);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            throw new FeatureChangeMergeException(\n                    exception.withNewTopLevelFailure(MergeFailureType.DIFF_BASED_LONG_MERGE_FAIL),\n                    \"mutually exclusive Long merge failed\", exception);\n        }\n    };\n\n    static final TernaryOperator<Location> diffBasedLocationMerger = (beforeLocation,\n            afterLocationLeft, afterLocationRight) ->\n    {\n        try\n        {\n            return (Location) getDiffBasedMutuallyExclusiveMerger().apply(beforeLocation,\n                    afterLocationLeft, afterLocationRight);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            throw new FeatureChangeMergeException(\n                    exception.withNewTopLevelFailure(\n                            MergeFailureType.DIFF_BASED_LOCATION_MERGE_FAIL),\n                    \"mutually exclusive Location merge failed\", exception);\n        }\n    };\n\n    static final TernaryOperator<PolyLine> diffBasedPolyLineMerger = (beforePolyLine,\n            afterPolyLineLeft, afterPolyLineRight) ->\n    {\n        try\n        {\n            return (PolyLine) getDiffBasedMutuallyExclusiveMerger().apply(beforePolyLine,\n                    afterPolyLineLeft, afterPolyLineRight);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            throw new FeatureChangeMergeException(\n                    exception.withNewTopLevelFailure(\n                            MergeFailureType.DIFF_BASED_POLYLINE_MERGE_FAIL),\n                    \"mutually exclusive PolyLine merge failed\", exception);\n        }\n    };\n\n    static final TernaryOperator<Polygon> diffBasedPolygonMerger = (beforePolygon, afterPolygonLeft,\n            afterPolygonRight) ->\n    {\n        try\n        {\n            return (Polygon) getDiffBasedMutuallyExclusiveMerger().apply(beforePolygon,\n                    afterPolygonLeft, afterPolygonRight);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            throw new FeatureChangeMergeException(\n                    exception\n                            .withNewTopLevelFailure(MergeFailureType.DIFF_BASED_POLYGON_MERGE_FAIL),\n                    \"mutually exclusive Polygon merge failed\", exception);\n        }\n    };\n\n    static final TernaryOperator<RelationBean> diffBasedRelationBeanMerger = (beforeBean,\n            afterLeftBean, afterRightBean) ->\n    {\n        final Map<RelationBeanItem, Integer> beforeBeanMap = beforeBean.asMap();\n        final Map<RelationBeanItem, Integer> afterLeftBeanMap = afterLeftBean.asMap();\n        final Map<RelationBeanItem, Integer> afterRightBeanMap = afterRightBean.asMap();\n\n        verifyExplicitlyExcludedSets(beforeBean.asSet(), afterLeftBean.asSet(),\n                afterLeftBean.getExplicitlyExcluded(), beforeBean.asSet(), afterRightBean.asSet(),\n                afterRightBean.getExplicitlyExcluded());\n\n        /*\n         * Compute the difference set between the beforeView and the afterViews (which is equivalent\n         * to the keys removed from the after views). We filter any entries that have a count <= 0,\n         * since this corresponds to unchanged/added keys in the afterView.\n         */\n        final Map<RelationBeanItem, Integer> removedFromLeftView = computeMapDifferenceCounts(\n                beforeBeanMap, afterLeftBeanMap).entrySet().stream()\n                .filter(entry -> entry.getValue() > 0)\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n        final Map<RelationBeanItem, Integer> removedFromRightView = computeMapDifferenceCounts(\n                beforeBeanMap, afterRightBeanMap).entrySet().stream()\n                .filter(entry -> entry.getValue() > 0)\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n\n        /*\n         * Compute the difference set between the afterViews and the beforeView (which is equivalent\n         * to the keys added to the after views). We filter any entries that have a count <= 0,\n         * since this corresponds to unchanged/removed keys in the afterView.\n         */\n        final Map<RelationBeanItem, Integer> addedToLeftView = computeMapDifferenceCounts(\n                afterLeftBeanMap, beforeBeanMap).entrySet().stream()\n                .filter(entry -> entry.getValue() > 0)\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n        final Map<RelationBeanItem, Integer> addedToRightView = computeMapDifferenceCounts(\n                afterRightBeanMap, beforeBeanMap).entrySet().stream()\n                .filter(entry -> entry.getValue() > 0)\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n\n        /*\n         * Check for REMOVE/REMOVE conflicts. A REMOVE/REMOVE conflict occurs when\n         * removedFromLeftView and removedFromRightView share a key, but the values differ.\n         */\n        for (final Map.Entry<RelationBeanItem, Integer> removedFromLeftEntry : removedFromLeftView\n                .entrySet())\n        {\n            final RelationBeanItem leftKey = removedFromLeftEntry.getKey();\n            final Integer leftValue = removedFromLeftEntry.getValue();\n            final Integer rightValue = removedFromRightView.get(leftKey);\n            if (rightValue != null && !leftValue.equals(rightValue))\n            {\n                throw new FeatureChangeMergeException(\n                        MergeFailureType.DIFF_BASED_RELATION_BEAN_REMOVE_REMOVE_CONFLICT,\n                        \"diffBasedRelationBeanMerger failed due to REMOVE/REMOVE conflict on key: [{}]: beforeValue absolute count was {} but removedLeft/Right diff counts conflict [{} vs {}]\",\n                        leftKey, beforeBeanMap.get(leftKey), leftValue, rightValue);\n            }\n        }\n\n        /*\n         * Check for ADD/REMOVE conflicts. An ADD/REMOVE conflict occurs when the addedToLeftView\n         * and removedFromRightView maps share a key (or the addedToRightView and\n         * removedFromLeftView share a key).\n         */\n        final Set<RelationBeanItem> addedLeftRemovedRightConflicts = com.google.common.collect.Sets\n                .intersection(addedToLeftView.keySet(), removedFromRightView.keySet());\n        if (!addedLeftRemovedRightConflicts.isEmpty())\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.DIFF_BASED_RELATION_BEAN_ADD_REMOVE_CONFLICT,\n                    \"diffBasedRelationBeanMerger failed due to ADD/REMOVE conflict(s) on key(s): {}\",\n                    addedLeftRemovedRightConflicts);\n        }\n        final Set<RelationBeanItem> addedRightRemovedLeftConflicts = com.google.common.collect.Sets\n                .intersection(addedToRightView.keySet(), removedFromLeftView.keySet());\n        if (!addedRightRemovedLeftConflicts.isEmpty())\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.DIFF_BASED_RELATION_BEAN_ADD_REMOVE_CONFLICT,\n                    \"diffBasedRelationBeanMerger failed due to ADD/REMOVE conflict(s) on key(s): {}\",\n                    addedLeftRemovedRightConflicts);\n        }\n\n        /*\n         * Check for ADD/ADD conflicts. A ADD/ADD conflict occurs when addedToLeftView and\n         * addedToRightView share a key, but the values differ.\n         */\n        for (final Map.Entry<RelationBeanItem, Integer> addedToLeftEntry : addedToLeftView\n                .entrySet())\n        {\n            final RelationBeanItem leftKey = addedToLeftEntry.getKey();\n            final Integer leftValue = addedToLeftEntry.getValue();\n            final Integer rightValue = addedToRightView.get(leftKey);\n            if (rightValue != null && !leftValue.equals(rightValue))\n            {\n                throw new FeatureChangeMergeException(\n                        MergeFailureType.DIFF_BASED_RELATION_BEAN_ADD_ADD_CONFLICT,\n                        \"diffBasedRelationBeanMerger failed due to ADD/ADD conflict on key: [{}]: beforeValue absolute count was {} but addedLeft/Right diff counts conflict [{} vs {}]\",\n                        leftKey,\n                        beforeBeanMap.get(leftKey) != null ? beforeBeanMap.get(leftKey) : 0,\n                        leftValue, rightValue);\n            }\n        }\n\n        /*\n         * Since there were no ADD/REMOVE or REMOVE/REMOVE conflicts, we can safely merge the\n         * REMOVED maps.\n         */\n        final Map<RelationBeanItem, Integer> removedMergedView = new HashMap<>();\n        removedFromLeftView.entrySet().stream()\n                .forEach(entry -> removedMergedView.put(entry.getKey(), entry.getValue()));\n        removedFromRightView.entrySet().stream()\n                .forEach(entry -> removedMergedView.put(entry.getKey(), entry.getValue()));\n\n        /*\n         * Since there were no ADD/REMOVE or ADD/ADD conflicts. we can safely merge the ADD maps.\n         */\n        final Map<RelationBeanItem, Integer> addedMergedView = new HashMap<>();\n        addedToLeftView.entrySet().stream()\n                .forEach(entry -> addedMergedView.put(entry.getKey(), entry.getValue()));\n        addedToRightView.entrySet().stream()\n                .forEach(entry -> addedMergedView.put(entry.getKey(), entry.getValue()));\n\n        /*\n         * Construct the final product using our merged REMOVE and ADD views. First we create a\n         * resultMap with merged counts. We operate on the beforeView - subtract count for each key\n         * in removedMergedView, add count for each key in addedMergedView. Then, using the\n         * resultMap, we can construct the result bean with the proper key counts.\n         */\n        final Map<RelationBeanItem, Integer> resultMap = new HashMap<>(beforeBeanMap);\n        for (final Map.Entry<RelationBeanItem, Integer> removedEntry : removedMergedView.entrySet())\n        {\n            final RelationBeanItem removedKey = removedEntry.getKey();\n            final Integer removedCount = removedEntry.getValue();\n            final Integer beforeValue = beforeBeanMap.get(removedKey);\n            final Integer newValue = beforeValue - removedCount;\n            resultMap.put(removedKey, newValue);\n        }\n        for (final Map.Entry<RelationBeanItem, Integer> addedEntry : addedMergedView.entrySet())\n        {\n            final RelationBeanItem addedKey = addedEntry.getKey();\n            final Integer addedCount = addedEntry.getValue();\n            final Integer beforeValue = beforeBeanMap.get(addedKey);\n            final Integer newValue;\n\n            /*\n             * The beforeValue will be null if the added key is brand new. In that case, we just\n             * need to set the count. Otherwise, we add the count to the before value.\n             */\n            if (beforeValue == null)\n            {\n                newValue = 0 + addedCount;\n            }\n            else\n            {\n                newValue = beforeValue + addedCount;\n            }\n            resultMap.put(addedKey, newValue);\n        }\n\n        final RelationBean resultBean = new RelationBean();\n        for (final Map.Entry<RelationBeanItem, Integer> resultEntry : resultMap.entrySet())\n        {\n            final RelationBeanItem resultKey = resultEntry.getKey();\n            final Integer resultCount = resultEntry.getValue();\n            for (int count = 0; count < resultCount; count++)\n            {\n                resultBean.addItem(resultKey);\n            }\n        }\n\n        Stream.concat(afterLeftBean.getExplicitlyExcluded().stream(),\n                afterRightBean.getExplicitlyExcluded().stream())\n                .forEach(resultBean::addItemExplicitlyExcluded);\n\n        return resultBean;\n    };\n\n    /*\n     * Merge two differing Set<Long> created using ADDs and REMOVEs on a common ancestor. For\n     * example, consider set A: [2, 3, 4] and set B: [1, 2, 3, 5]. Assume these were both based on\n     * initial set I: [1, 2, 3, 4]. Neither A nor B make any changes that conflict with each other,\n     * so we should be able to merge them into set C: [2, 3, 5].\n     */\n    static final TernaryOperator<Set<Long>> diffBasedLongSetMerger = (beforeSet, afterLeftSet,\n            afterRightSet) ->\n    {\n        final Set<Long> removedFromLeftView = com.google.common.collect.Sets.difference(beforeSet,\n                afterLeftSet);\n        final Set<Long> removedFromRightView = com.google.common.collect.Sets.difference(beforeSet,\n                afterRightSet);\n        final Set<Long> addedToLeftView = com.google.common.collect.Sets.difference(afterLeftSet,\n                beforeSet);\n        final Set<Long> addedToRightView = com.google.common.collect.Sets.difference(afterRightSet,\n                beforeSet);\n\n        /*\n         * Easy key-merge of left and right ADDs and REMOVEs. We can safely ignore duplicate keys,\n         * since it is feasible that two FeatureChanges made the same ADD or REMOVE. We also do not\n         * need to rectify the ADD/ADD or ADD/REMOVE conflicts. Since these are keys-only, there is\n         * no possibility of an ADD/ADD conflict. And because we enforce a shared beforeView, there\n         * is no possibility of an ADD/REMOVE conflict.\n         */\n        final Set<Long> removedMerged = Sets.withSets(false, removedFromLeftView,\n                removedFromRightView);\n        final Set<Long> addedMerged = Sets.withSets(false, addedToLeftView, addedToRightView);\n\n        /*\n         * Build the result set by performing the REMOVEs on the beforeView, then performing the\n         * ADDs on the beforeView.\n         */\n        final Set<Long> result = new HashSet<>(beforeSet);\n        result.removeAll(removedMerged);\n        result.addAll(addedMerged);\n\n        return result;\n    };\n\n    /*\n     * This is essentially the same thing as diffBasedLongSetMerger. However, we construct a\n     * SortedSet before returning the result. This is useful for members that require a sorted\n     * property.\n     */\n    static final TernaryOperator<SortedSet<Long>> diffBasedLongSortedSetMerger = (beforeSet,\n            afterLeftSet, afterRightSet) -> new TreeSet<>(\n                    diffBasedLongSetMerger.apply(beforeSet, afterLeftSet, afterRightSet));\n\n    /*\n     * Merge two differing Map<String, String> created using ADD/MODIFYs and REMOVEs on a common\n     * ancestor. For example, consider map A: [a=1, b=12, c=3] and map B: [a=1, b=2, c=3, d=4, e=5].\n     * Assume these were both based on initial map I: [a=1, b=2, c=3, d=4]. Neither A nor B make any\n     * changes that conflict with each other, so we should be able to merge them into map C: [a=1,\n     * b=12, c=3, e=5].\n     */\n    static final TernaryOperator<Map<String, String>> diffBasedTagMerger = (beforeMap, afterLeftMap,\n            afterRightMap) ->\n    {\n        /*\n         * Simple key removal. A REMOVE looks like [a=1] -> []\n         */\n        final Set<String> keysRemovedFromLeftView = com.google.common.collect.Sets\n                .difference(beforeMap.keySet(), afterLeftMap.keySet());\n        final Set<String> keysRemovedFromRightView = com.google.common.collect.Sets\n                .difference(beforeMap.keySet(), afterRightMap.keySet());\n\n        /*\n         * Consider the difference between an ADD [] -> [a=1] and a MODIFY [a=1] -> [a=2]. Below we\n         * group key ADDs and MODIFYs together, since we operate on the entire key->value pair. In\n         * light of this fact, ADDs and MODIFYs are effectively the same operation as far as merge\n         * logic is concerned. From here on, we will only refer to ADD and never MODIFY.\n         */\n        final Map<String, String> addedToLeftView = com.google.common.collect.Sets\n                .difference(afterLeftMap.entrySet(), beforeMap.entrySet()).stream()\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n        final Map<String, String> addedToRightView = com.google.common.collect.Sets\n                .difference(afterRightMap.entrySet(), beforeMap.entrySet()).stream()\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n\n        /*\n         * Check to see if any of the shared ADD keys generate an ADD/ADD conflict. An ADD/ADD\n         * conflict is when the same key maps to two different values.\n         */\n        final Set<String> sharedAddedKeys = com.google.common.collect.Sets\n                .intersection(addedToLeftView.keySet(), addedToRightView.keySet());\n        for (final String sharedKey : sharedAddedKeys)\n        {\n            final String leftValue = addedToLeftView.get(sharedKey);\n            final String rightValue = addedToRightView.get(sharedKey);\n            if (!Objects.equals(leftValue, rightValue))\n            {\n                throw new FeatureChangeMergeException(\n                        MergeFailureType.DIFF_BASED_TAG_ADD_ADD_CONFLICT,\n                        \"diffBasedTagMerger failed due to ADD/ADD conflict on keys: [{} -> {}] vs [{} -> {}]\",\n                        sharedKey, leftValue, sharedKey, rightValue);\n            }\n        }\n\n        /*\n         * Now, check for any ADD/REMOVE conflicts. An ADD/REMOVE conflict is when one view of the\n         * map contains an update to a key, but another view of the map removes the key entirely.\n         */\n        final Set<String> keysRemovedMerged = Sets.withSets(false, keysRemovedFromLeftView,\n                keysRemovedFromRightView);\n        final Set<String> keysAddedMerged = Sets.withSets(false, addedToLeftView.keySet(),\n                addedToRightView.keySet());\n        final Set<String> conflicts = com.google.common.collect.Sets.intersection(keysRemovedMerged,\n                keysAddedMerged);\n        if (!conflicts.isEmpty())\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.DIFF_BASED_TAG_ADD_REMOVE_CONFLICT,\n                    \"diffBasedTagMerger failed due to ADD/REMOVE conflict(s) on key(s): {}\",\n                    conflicts);\n        }\n\n        /*\n         * Now construct the merged map. Take the beforeView and REMOVE all keys that were in the\n         * removedMerged set. Then, ADD all keys in the addMerged set. To get the values for those\n         * keys, we select from whichever addedTo view (left or right) gives us a non-null mapping.\n         * This correctly handles the case where one of the FeatureChanges is ADDing a brand new key\n         * value pair (since the other FeatureChange will not contain a mapping).\n         */\n        final Map<String, String> result = new HashMap<>(beforeMap);\n        keysRemovedMerged.forEach(result::remove);\n        keysAddedMerged.forEach(key ->\n        {\n            String value = addedToLeftView.get(key);\n            if (value == null)\n            {\n                value = addedToRightView.get(key);\n            }\n            result.put(key, value);\n        });\n\n        return result;\n    };\n\n    /**\n     * A merger for cases when two {@link Set}s have conflicting beforeViews. This is useful for\n     * merging {@link Node} in/out {@link Edge} sets, since different shards may occasionally have\n     * inconsistent views of a {@link Node}'s connected {@link Edge}s. While this merger uses\n     * explicitlyExcluded state to properly compute the merge, it does not handle merging the\n     * explicitlyExcluded state itself. This responsibility lies with the caller.\n     */\n    static final SenaryFunction<SortedSet<Long>, SortedSet<Long>, Set<Long>, SortedSet<Long>, SortedSet<Long>, Set<Long>, SortedSet<Long>> conflictingBeforeViewSetMerger = (\n            beforeLeftSet, afterLeftSet, explicitlyExcludedLeftSet, beforeRightSet, afterRightSet,\n            explicitlyExcludedRightSet) ->\n    {\n        verifyExplicitlyExcludedSets(beforeLeftSet, afterLeftSet, explicitlyExcludedLeftSet,\n                beforeRightSet, afterRightSet, explicitlyExcludedRightSet);\n\n        /*\n         * Merge the removed sets. This should just work, since we are doing a key-only merge.\n         */\n        final Set<Long> removedMerged = Sets.withSets(false, explicitlyExcludedLeftSet,\n                explicitlyExcludedRightSet);\n\n        /*\n         * Compute the added sets. We do this by comparing the before and after views of the given\n         * identifier sets on each side of the merge.\n         */\n        final Set<Long> addedToLeft = com.google.common.collect.Sets.difference(afterLeftSet,\n                beforeLeftSet);\n        final Set<Long> addedToRight = com.google.common.collect.Sets.difference(afterRightSet,\n                beforeRightSet);\n\n        /*\n         * Merge the added sets. This should just work, since we are doing a key-only merge.\n         */\n        final Set<Long> addedMerged = Sets.withSets(false, addedToLeft, addedToRight);\n\n        /*\n         * Check for ADD/REMOVE conflicts. This occurs if one side of the merge adds an identifier\n         * which was explicitly removed by the other side.\n         */\n        final Set<Long> conflicts = com.google.common.collect.Sets.intersection(removedMerged,\n                addedMerged);\n        if (!conflicts.isEmpty())\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.CONFLICTING_BEFORE_VIEW_SET_ADD_REMOVE_CONFLICT,\n                    \"conflictingBeforeViewSetMerger failed due to ADD/REMOVE conflict(s) on key(s): {}\",\n                    conflicts);\n        }\n\n        /*\n         * Now, we need to construct a proper merged beforeView for the left and right sides (this\n         * view will be tolerant of inconsistencies in the left and right sides). Once we have this,\n         * we can apply the changes from our removedMerged and addedMerged sets to get the final\n         * result.\n         */\n        final Set<Long> mergedBeforeView = simpleLongSetMerger.apply(beforeLeftSet, beforeRightSet);\n        mergedBeforeView.removeAll(removedMerged);\n        mergedBeforeView.addAll(addedMerged);\n\n        final SortedSet<Long> resultSet = new TreeSet<>();\n        mergedBeforeView.forEach(resultSet::add);\n\n        return resultSet;\n    };\n\n    /**\n     * A merger for cases when two {@link RelationBean}s have conflicting beforeViews. This can\n     * happen occasionally, since different shards may have slightly inconsistent relation views.\n     * <p>\n     * Also note that this lambda does not respect duplicate {@link RelationBeanItem}s of a given\n     * value. Technically, OSM allows for duplicate {@link RelationBeanItem}s in a given relation.\n     * However, these duplicates are disallowed by {@link PackedAtlas#relationMembers} and by\n     * extension {@link PackedRelation#members}. As a result, we need not worry about that edge case\n     * here.\n     */\n    static final QuaternaryOperator<RelationBean> conflictingBeforeViewRelationBeanMerger = (\n            beforeLeftBean, afterLeftBean, beforeRightBean, afterRightBean) ->\n    {\n        verifyExplicitlyExcludedSets(beforeLeftBean.asSet(), afterLeftBean.asSet(),\n                afterLeftBean.getExplicitlyExcluded(), beforeRightBean.asSet(),\n                afterRightBean.asSet(), afterRightBean.getExplicitlyExcluded());\n\n        /*\n         * Merge the removed sets. This should just work, since we are doing a key-only merge and\n         * have already assumed that there cannot be duplicate RelationBeanItems.\n         */\n        final Set<RelationBeanItem> removedMerged = Sets.withSets(false,\n                afterLeftBean.getExplicitlyExcluded(), afterRightBean.getExplicitlyExcluded());\n\n        /*\n         * Compute the added sets. We do this by comparing the before and after views of the given\n         * RelationBeans on each side of the merge.\n         */\n        final Set<RelationBeanItem> addedToLeft = com.google.common.collect.Sets\n                .difference(afterLeftBean.asSet(), beforeLeftBean.asSet());\n        final Set<RelationBeanItem> addedToRight = com.google.common.collect.Sets\n                .difference(afterRightBean.asSet(), beforeRightBean.asSet());\n\n        /*\n         * Merge the added sets. This should just work, since we are doing a key-only merge and have\n         * already assumed that there cannot be duplicate RelationBeanItems.\n         */\n        final Set<RelationBeanItem> addedMerged = Sets.withSets(false, addedToLeft, addedToRight);\n\n        /*\n         * Check for ADD/REMOVE conflicts. This occurs if one side of the merge adds a member which\n         * was explicitly removed by the other side.\n         */\n        final Set<RelationBeanItem> conflicts = com.google.common.collect.Sets\n                .intersection(removedMerged, addedMerged);\n        if (!conflicts.isEmpty())\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.CONFLICTING_BEFORE_VIEW_RELATION_BEAN_ADD_REMOVE_CONFLICT,\n                    \"conflictingBeforeViewRelationBeanMerger failed due to ADD/REMOVE conflict(s) on key(s): {}\",\n                    conflicts);\n        }\n\n        /*\n         * Now, we need to construct a proper merged beforeView for the left and right sides (this\n         * view will be tolerant of inconsistencies in the left and right sides). Once we have this,\n         * we can apply the changes from our removedMerged and addedMerged sets to get the final\n         * result.\n         */\n        final Set<RelationBeanItem> mergedBeforeView = simpleRelationBeanMerger\n                .apply(beforeLeftBean, beforeRightBean).asSet();\n        mergedBeforeView.removeAll(removedMerged);\n        mergedBeforeView.addAll(addedMerged);\n\n        final RelationBean resultBean = new RelationBean();\n        mergedBeforeView.forEach(resultBean::addItem);\n        Stream.concat(afterLeftBean.getExplicitlyExcluded().stream(),\n                afterRightBean.getExplicitlyExcluded().stream())\n                .forEach(resultBean::addItemExplicitlyExcluded);\n\n        return resultBean;\n    };\n\n    /**\n     * Compute the difference counts between two Map<T, Integer>. The difference is computing by\n     * subtracting the after Integer value from the before Integer value. If the before key is not\n     * present in the after map, then we use the before Integer value for that key. Any after keys\n     * not present in the before map are ignored.\n     *\n     * @param before\n     *            the before view of the map\n     * @param after\n     *            the after view of the map\n     * @return the difference map\n     */\n    private static <T> Map<T, Integer> computeMapDifferenceCounts(final Map<T, Integer> before,\n            final Map<T, Integer> after)\n    {\n        final Map<T, Integer> result = new HashMap<>();\n\n        for (final Map.Entry<T, Integer> beforeEntry : before.entrySet())\n        {\n            final T beforeKey = beforeEntry.getKey();\n            final Integer beforeCount = beforeEntry.getValue();\n            final Integer afterCount = after.get(beforeKey);\n            if (afterCount != null)\n            {\n                result.put(beforeKey, beforeCount - afterCount);\n            }\n            else\n            {\n                result.put(beforeKey, beforeCount);\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Returns a TernaryOperator that acts as a diff based, mutually exclusive chooser. The operator\n     * can successfully merge two afterViews if: 1) both afterViews match OR 2) the afterViews are\n     * mismatched, but one of the afterViews matches the beforeView. In case 2) the merger will\n     * select the afterView which differs from the beforeView. In any other case, the operator will\n     * fail with an ADD/ADD conflict.\n     *\n     * @return the operator\n     */\n    private static <T> TernaryOperator<T> getDiffBasedMutuallyExclusiveMerger()\n    {\n        return (beforeView, afterViewLeft, afterViewRight) ->\n        {\n            /*\n             * If the afterViews are equivalent, arbitrarily return one of them.\n             */\n            if (afterViewLeft.equals(afterViewRight))\n            {\n                return afterViewLeft;\n            }\n\n            /*\n             * afterViewLeft and afterViewRight were not equivalent. If one of them matches the\n             * beforeView, return the opposing one.\n             */\n            if (afterViewLeft.equals(beforeView))\n            {\n                return afterViewRight;\n            }\n            if (afterViewRight.equals(beforeView))\n            {\n                return afterViewLeft;\n            }\n\n            /*\n             * If we get here, we have an ADD/ADD conflict.\n             */\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.MUTUALLY_EXCLUSIVE_ADD_ADD_CONFLICT,\n                    \"diffBasedMutuallyExclusiveMerger failed due to ADD/ADD conflict: beforeView was:\\n{}\\nbut afterViews were:\\n{}\\nvs\\n{}\",\n                    beforeView, afterViewLeft, afterViewRight);\n        };\n    }\n\n    private static <T> void verifyExplicitlyExcludedSets(final Set<T> beforeLeft,\n            final Set<T> afterLeft, final Set<T> explicitlyExcludedLeft, final Set<T> beforeRight,\n            final Set<T> afterRight, final Set<T> explicitlyExcludedRight)\n    {\n        /*\n         * The implicitly computed removed sets. These are computed by comparing the before and\n         * after views of the given set-based entities on each side of the merge.\n         */\n        final Set<T> implicitlyRemovedFromLeft = com.google.common.collect.Sets\n                .difference(beforeLeft, afterLeft);\n        final Set<T> implicitlyRemovedFromRight = com.google.common.collect.Sets\n                .difference(beforeRight, afterRight);\n\n        /*\n         * It must be the case that the explicitly and implicitly computed removed sets match for a\n         * given set-based entity from one side of the merge. If they don't, that means the user\n         * likely removed some elements without using the contextual API (withMembersAndSource for\n         * Relations, withXEdgeIdentifiersAndSource for Nodes), which means the explicitlyExcluded\n         * sets are not properly populated. This will lead to corrupt and unexpected results.\n         */\n        if (!explicitlyExcludedLeft.equals(implicitlyRemovedFromLeft))\n        {\n            throw new CoreException(\n                    \"explicitlyExcludedLeft set did not match the implicitly computed removedFromLeft set:\\n{}\\nvs\\n{}\\n\"\n                            + \"This is likely because members were removed without using the correct withXAndSource API\",\n                    explicitlyExcludedLeft, implicitlyRemovedFromLeft);\n        }\n        if (!explicitlyExcludedRight.equals(implicitlyRemovedFromRight))\n        {\n            throw new CoreException(\n                    \"explicitlyExcludedRight set did not match the implicitly computed removedFromRight set:\\n{}\\nvs\\n{}\\n\"\n                            + \"This is likely because members were removed without using the correct withXAndSource API\",\n                    explicitlyExcludedRight, implicitlyRemovedFromRight);\n        }\n    }\n\n    private MemberMergeStrategies()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/MemberMerger.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.function.BinaryOperator;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.exception.change.FeatureChangeMergeException;\nimport org.openstreetmap.atlas.exception.change.MergeFailureType;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.utilities.function.QuaternaryOperator;\nimport org.openstreetmap.atlas.utilities.function.TernaryOperator;\n\n/**\n * This class encapsulates the logic and configuration for {@link CompleteEntity} member merging in\n * the context of {@link FeatureChange} merges.\n *\n * @author lcram\n * @param <M>\n *            the type of the member this {@link MemberMerger} will be merging\n */\npublic final class MemberMerger<M>\n{\n    /**\n     * A builder class for {@link MemberMerger}.\n     *\n     * @author lcram\n     * @param <M>\n     *            the type of the member this {@link MemberMerger} will be merging\n     */\n    public static class Builder<M>\n    {\n        private String memberName;\n        private AtlasEntity beforeEntityLeft;\n        private AtlasEntity afterEntityLeft;\n        private AtlasEntity beforeEntityRight;\n        private AtlasEntity afterEntityRight;\n        private Function<AtlasEntity, M> memberExtractor;\n        private BinaryOperator<M> afterViewNoBeforeViewMerger;\n        private TernaryOperator<M> afterViewConsistentBeforeViewMerger;\n        private QuaternaryOperator<M> afterViewConflictingBeforeViewMerger;\n        private BinaryOperator<M> beforeViewMerger;\n        private boolean useHackForMergingConflictingConnectedEdgeSetBeforeViews = false;\n\n        private Optional<CompleteNode> leftNode;\n        private Optional<CompleteNode> rightNode;\n\n        public MemberMerger<M> build()\n        {\n            assertRequiredFieldsNonNull();\n\n            final MemberMerger<M> merger = new MemberMerger<>();\n            merger.memberName = this.memberName;\n            merger.beforeEntityLeft = this.beforeEntityLeft;\n            merger.afterEntityLeft = this.afterEntityLeft;\n            merger.beforeEntityRight = this.beforeEntityRight;\n            merger.afterEntityRight = this.afterEntityRight;\n            merger.memberExtractor = this.memberExtractor;\n            merger.afterViewNoBeforeViewMerger = this.afterViewNoBeforeViewMerger;\n            merger.afterViewConsistentBeforeViewMerger = this.afterViewConsistentBeforeViewMerger;\n            merger.afterViewConflictingBeforeViewMerger = this.afterViewConflictingBeforeViewMerger;\n            merger.beforeViewMerger = this.beforeViewMerger;\n            merger.useHackForMergingConflictingConnectedEdgeSetBeforeViews = this.useHackForMergingConflictingConnectedEdgeSetBeforeViews;\n            merger.leftNode = this.leftNode;\n            merger.rightNode = this.rightNode;\n\n            return merger;\n        }\n\n        public Builder<M> useHackForMergingConflictingConnectedEdgeSetBeforeViews(\n                final CompleteNode left, final CompleteNode right)\n        {\n            this.useHackForMergingConflictingConnectedEdgeSetBeforeViews = true;\n            this.leftNode = Optional.ofNullable(left);\n            this.rightNode = Optional.ofNullable(right);\n            return this;\n        }\n\n        public Builder<M> withAfterEntityLeft(final AtlasEntity afterEntityLeft)\n        {\n            this.afterEntityLeft = afterEntityLeft;\n            return this;\n        }\n\n        public Builder<M> withAfterEntityRight(final AtlasEntity afterEntityRight)\n        {\n            this.afterEntityRight = afterEntityRight;\n            return this;\n        }\n\n        public Builder<M> withAfterViewConflictingBeforeViewMerger(\n                final QuaternaryOperator<M> afterViewConflictingBeforeViewMerger)\n        {\n            this.afterViewConflictingBeforeViewMerger = afterViewConflictingBeforeViewMerger;\n            return this;\n        }\n\n        public Builder<M> withAfterViewConsistentBeforeViewMerger(\n                final TernaryOperator<M> afterViewConsistentBeforeViewMerger)\n        {\n            this.afterViewConsistentBeforeViewMerger = afterViewConsistentBeforeViewMerger;\n            return this;\n        }\n\n        public Builder<M> withAfterViewNoBeforeMerger(\n                final BinaryOperator<M> afterViewNoBeforeMerger)\n        {\n            this.afterViewNoBeforeViewMerger = afterViewNoBeforeMerger;\n            return this;\n        }\n\n        public Builder<M> withBeforeEntityLeft(final AtlasEntity beforeEntityLeft)\n        {\n            this.beforeEntityLeft = beforeEntityLeft;\n            return this;\n        }\n\n        public Builder<M> withBeforeEntityRight(final AtlasEntity beforeEntityRight)\n        {\n            this.beforeEntityRight = beforeEntityRight;\n            return this;\n        }\n\n        public Builder<M> withBeforeViewMerger(final BinaryOperator<M> beforeViewMerger)\n        {\n            this.beforeViewMerger = beforeViewMerger;\n            return this;\n        }\n\n        public Builder<M> withMemberExtractor(final Function<AtlasEntity, M> memberExtractor)\n        {\n            this.memberExtractor = memberExtractor;\n            return this;\n        }\n\n        public Builder<M> withMemberName(final String memberName)\n        {\n            this.memberName = memberName;\n            return this;\n        }\n\n        private void assertRequiredFieldsNonNull()\n        {\n            if (this.memberName == null)\n            {\n                throw new CoreException(\"Required field \\'memberName\\' was unset\");\n            }\n\n            if (this.afterEntityLeft == null)\n            {\n                throw new CoreException(\"Required field \\'afterEntityLeft\\' was unset\");\n            }\n\n            if (this.afterEntityRight == null)\n            {\n                throw new CoreException(\"Required field \\'afterEntityRight\\' was unset\");\n            }\n\n            if (this.beforeEntityLeft != null && this.beforeEntityRight == null\n                    || this.beforeEntityLeft == null && this.beforeEntityRight != null)\n            {\n                throw new CoreException(\"Both \\'beforeEntity\\' fields must either be set or null\");\n            }\n\n            if (this.memberExtractor == null)\n            {\n                throw new CoreException(\"Required field \\'memberExtractor\\' was unset\");\n            }\n        }\n    }\n\n    /**\n     * A bean class to store the merged before and after members. This is useful as a return type\n     * for the member merger, which needs to correctly merge the before and after entity view of\n     * each {@link FeatureChange}.\n     *\n     * @author lcram\n     * @param <M>\n     *            the member type\n     */\n    public static class MergedMemberBean<M>\n    {\n        private final M beforeMemberMerged;\n        private final M afterMemberMerged;\n\n        public MergedMemberBean(final M before, final M after)\n        {\n            this.beforeMemberMerged = before;\n            this.afterMemberMerged = after;\n        }\n\n        public M getMergedAfterMember()\n        {\n            return this.afterMemberMerged;\n        }\n\n        public M getMergedBeforeMember()\n        {\n            return this.beforeMemberMerged;\n        }\n    }\n\n    private String memberName;\n    private AtlasEntity beforeEntityLeft;\n    private AtlasEntity afterEntityLeft;\n    private AtlasEntity beforeEntityRight;\n    private AtlasEntity afterEntityRight;\n    private Function<AtlasEntity, M> memberExtractor;\n    private BinaryOperator<M> afterViewNoBeforeViewMerger;\n    private TernaryOperator<M> afterViewConsistentBeforeViewMerger;\n    private QuaternaryOperator<M> afterViewConflictingBeforeViewMerger;\n    private BinaryOperator<M> beforeViewMerger;\n\n    private boolean useHackForMergingConflictingConnectedEdgeSetBeforeViews;\n    private Optional<CompleteNode> leftNode;\n    private Optional<CompleteNode> rightNode;\n\n    private MemberMerger()\n    {\n\n    }\n\n    /**\n     * Merge some feature member using a left and right before/after view.\n     *\n     * @return a {@link MergedMemberBean} containing the merged beforeMember view and the merged\n     *         afterMember view\n     */\n    public MergedMemberBean<M> mergeMember()\n    {\n        final M beforeMemberResult;\n        final M afterMemberResult;\n\n        final M beforeMemberLeft = this.beforeEntityLeft == null ? null\n                : this.memberExtractor.apply(this.beforeEntityLeft);\n        final M afterMemberLeft = this.afterEntityLeft == null ? null\n                : this.memberExtractor.apply(this.afterEntityLeft);\n        final M beforeMemberRight = this.beforeEntityRight == null ? null\n                : this.memberExtractor.apply(this.beforeEntityRight);\n        final M afterMemberRight = this.afterEntityRight == null ? null\n                : this.memberExtractor.apply(this.afterEntityRight);\n\n        /*\n         * In the case that both beforeMembers are present, we check their equivalence before\n         * continuing. If they are not equivalent, then we try to use our special beforeView\n         * conflict resolution merge logic. Otherwise, we can continue as normal.\n         */\n        if (beforeMemberLeft != null && beforeMemberRight != null\n                && !beforeMemberLeft.equals(beforeMemberRight))\n        {\n            /*\n             * In the case that we are merging the inEdges or outEdges members of Node, we perform a\n             * different merge logic. The in/outEdge sets have a possibility of beforeView\n             * conflicts, and since we are unable to attach additional explicitlyExcluded state\n             * directly to a set, we cannot use the same logic utilized for merging other members\n             * with conflicting beforeViews.\n             */\n            if (this.useHackForMergingConflictingConnectedEdgeSetBeforeViews)\n            {\n                return mergeMemberHackForConflictingConnectedEdgeSetBeforeViews(beforeMemberLeft,\n                        afterMemberLeft, beforeMemberRight, afterMemberRight);\n            }\n            return mergeMemberWithConflictingBeforeViews(beforeMemberLeft, afterMemberLeft,\n                    beforeMemberRight, afterMemberRight);\n        }\n\n        beforeMemberResult = chooseNonNullMemberIfPossible(beforeMemberLeft, beforeMemberRight);\n\n        /*\n         * In the case that both afterMembers are present, then we will need to resolve the\n         * afterMember merge using one of the supplied merge strategies. In this case, beforeMembers\n         * are either consistent or both null - so we can use the merged beforeMemberResult.\n         */\n        if (afterMemberLeft != null && afterMemberRight != null)\n        {\n            return mergeMembersWithConsistentBeforeViews(beforeMemberResult, afterMemberLeft,\n                    afterMemberRight);\n        }\n\n        /*\n         * If only one of the afterMembers is present, we just take whichever one is present.\n         */\n        if (afterMemberLeft != null)\n        {\n            afterMemberResult = afterMemberLeft;\n        }\n        else if (afterMemberRight != null)\n        {\n            afterMemberResult = afterMemberRight;\n        }\n        /*\n         * If neither afterMember is present, then just move on.\n         */\n        else\n        {\n            afterMemberResult = null;\n        }\n\n        return new MergedMemberBean<>(beforeMemberResult, afterMemberResult);\n    }\n\n    /**\n     * Choose the non-null member between two choices if possible. If both the left and right\n     * members are non-null, then this method will arbitrarily select one of them. Due to this\n     * condition, you may see unexpected results if you pass two non-null members that are unequal.\n     *\n     * @param memberLeft\n     *            the left side before view of the member\n     * @param memberRight\n     *            the right side before view of the member\n     * @return The non-null beforeMember among the two if present. Otherwise, returns {@code null};\n     */\n    private M chooseNonNullMemberIfPossible(final M memberLeft, final M memberRight)\n    {\n        /*\n         * Properly merge the members. If both are non-null, we arbitrarily take the left (since\n         * this method makes no guarantee on which side it will select when both are non-null). If\n         * one is null and one is not, then we take the non-null. If both were null, then the result\n         * remains null.\n         */\n        if (memberLeft != null && memberRight != null)\n        {\n            return memberLeft;\n        }\n        else if (memberLeft != null)\n        {\n            return memberLeft;\n        }\n        else if (memberRight != null)\n        {\n            return memberRight;\n        }\n        else\n        {\n            return null;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private MergedMemberBean<M> mergeMemberHackForConflictingConnectedEdgeSetBeforeViews(\n            final M beforeMemberLeft, final M afterMemberLeft, final M beforeMemberRight,\n            final M afterMemberRight)\n    {\n        final M beforeMemberResult;\n        final M afterMemberResult;\n\n        final Set<Long> explicitlyExcludedLeft;\n        final Set<Long> explicitlyExcludedRight;\n\n        if (!this.leftNode.isPresent())\n        {\n            throw new CoreException(\n                    \"Attempted merge failed for {}: tried to use hackForConflictingConnectedEdgeSet but was missing leftNode\",\n                    this.memberName);\n        }\n        if (!this.rightNode.isPresent())\n        {\n            throw new CoreException(\n                    \"Attempted merge failed for {}: tried to use hackForConflictingConnectedEdgeSet but was missing rightNode\",\n                    this.memberName);\n        }\n\n        if (FeatureChangeMergingHelpers.IN_EDGE_IDENTIFIERS_FIELD.equals(this.memberName))\n        {\n            explicitlyExcludedLeft = this.leftNode.get().explicitlyExcludedInEdgeIdentifiers();\n            explicitlyExcludedRight = this.rightNode.get().explicitlyExcludedInEdgeIdentifiers();\n        }\n        else if (FeatureChangeMergingHelpers.OUT_EDGE_IDENTIFIERS_FIELD.equals(this.memberName))\n        {\n            explicitlyExcludedLeft = this.leftNode.get().explicitlyExcludedOutEdgeIdentifiers();\n            explicitlyExcludedRight = this.rightNode.get().explicitlyExcludedOutEdgeIdentifiers();\n        }\n        else\n        {\n            throw new CoreException(\n                    \"Attempted merge failed for {}: hackForConflictingConnectedEdgeSet is not a valid strategy for {}\",\n                    this.memberName, this.memberName);\n        }\n\n        if (this.beforeViewMerger == null)\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.MISSING_BEFORE_VIEW_MERGE_STRATEGY,\n                    \"Conflicting beforeMembers {} and no beforeView merge strategy was provided; beforeView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight);\n        }\n\n        try\n        {\n            beforeMemberResult = this.beforeViewMerger.apply(beforeMemberLeft, beforeMemberRight);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            throw new FeatureChangeMergeException(\n                    exception.withNewTopLevelFailure(\n                            MergeFailureType.BEFORE_VIEW_MERGE_STRATEGY_FAILED),\n                    \"Attempted beforeView merge strategy failed for {} with beforeView:\\n{}\\nvs\\n{}\", // NOSONAR\n                    this.memberName, beforeMemberLeft, beforeMemberRight, exception);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    \"Attempted beforeView merge strategy failed for {} with beforeView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight, exception);\n        }\n\n        /*\n         * Here we hardcode the application of the SenaryOperator node connected edge set merger. We\n         * can cast back and forth between M and SortedSet<Long> here, since we know that M is of\n         * type SortedSet<Long> based on the constraints imposed when calling this function.\n         */\n        try\n        {\n            final SortedSet<Long> mergeResult = MemberMergeStrategies.conflictingBeforeViewSetMerger\n                    .apply((SortedSet<Long>) beforeMemberLeft, (SortedSet<Long>) afterMemberLeft,\n                            explicitlyExcludedLeft, (SortedSet<Long>) beforeMemberRight,\n                            (SortedSet<Long>) afterMemberRight, explicitlyExcludedRight);\n            afterMemberResult = (M) mergeResult;\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            throw new FeatureChangeMergeException(exception.withNewTopLevelFailure(\n                    MergeFailureType.AFTER_VIEW_CONFLICTING_BEFORE_VIEW_MERGE_STRATEGY_FAILED),\n                    \"Tried merge strategy for hackForConflictingConnectedEdgeSet, but it failed for {}\"\n                            + \"\\nbeforeView:\\n{}\\nvs\\n{}\\nafterView:\\n{}\\nvs\\n{}\", // NOSONAR\n                    this.memberName, beforeMemberLeft, beforeMemberRight, afterMemberLeft,\n                    afterMemberRight, exception);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.AFTER_VIEW_CONFLICTING_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    \"Tried merge strategy for hackForConflictingConnectedEdgeSet, but it failed for {}\"\n                            + \"\\nbeforeView:\\n{}\\nvs\\n{}\\nafterView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight, afterMemberLeft,\n                    afterMemberRight, exception);\n        }\n\n        return new MergedMemberBean<>(beforeMemberResult, afterMemberResult);\n    }\n\n    /**\n     * Merge a member that has conflicting beforeViews. This can happen occasionally with\n     * {@link RelationBean}s and the in/out {@link Edge} identifier sets in {@link Node}, since\n     * these may be inconsistent across shards.\n     *\n     * @param beforeMemberLeft\n     *            the left side before view of the member\n     * @param afterMemberLeft\n     *            the left side after view of the member\n     * @param beforeMemberRight\n     *            the right side before view of the member\n     * @param afterMemberRight\n     *            the right side after view of the member\n     * @return a {@link MergedMemberBean} containing the merged beforeMember view and the merged\n     *         afterMember view\n     */\n    private MergedMemberBean<M> mergeMemberWithConflictingBeforeViews(final M beforeMemberLeft,\n            final M afterMemberLeft, final M beforeMemberRight, final M afterMemberRight)\n    {\n        final M beforeMemberResult;\n        final M afterMemberResult;\n\n        if (this.afterViewConflictingBeforeViewMerger == null)\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.MISSING_AFTER_VIEW_MERGE_STRATEGY_WITH_BEFORE_MEMBER_CONFLICT_HANDLING,\n                    \"Conflicting beforeMembers {} and no afterView merge strategy capable of handling\"\n                            + \" conflicting beforeViews was provided; beforeView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight);\n        }\n\n        if (this.beforeViewMerger == null)\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.MISSING_BEFORE_VIEW_MERGE_STRATEGY,\n                    \"Conflicting beforeMembers {} and no beforeView merge strategy was provided; beforeView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight);\n        }\n\n        try\n        {\n            beforeMemberResult = this.beforeViewMerger.apply(beforeMemberLeft, beforeMemberRight);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            throw new FeatureChangeMergeException(\n                    exception.withNewTopLevelFailure(\n                            MergeFailureType.BEFORE_VIEW_MERGE_STRATEGY_FAILED),\n                    \"Attempted beforeView merge strategy failed for {} with beforeView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight, exception);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    \"Attempted beforeView merge strategy failed for {} with beforeView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight, exception);\n        }\n\n        try\n        {\n            afterMemberResult = this.afterViewConflictingBeforeViewMerger.apply(beforeMemberLeft,\n                    afterMemberLeft, beforeMemberRight, afterMemberRight);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            throw new FeatureChangeMergeException(exception.withNewTopLevelFailure(\n                    MergeFailureType.AFTER_VIEW_CONFLICTING_BEFORE_VIEW_MERGE_STRATEGY_FAILED),\n                    \"Tried merge strategy for handling conflicting beforeViews. but it failed for {}\"\n                            + \"\\nbeforeView:\\n{}\\nvs\\n{}\\nafterView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight, afterMemberLeft,\n                    afterMemberRight, exception);\n        }\n        catch (final Exception exception)\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.AFTER_VIEW_CONFLICTING_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    \"Tried merge strategy for handling conflicting beforeViews. but it failed for {}\"\n                            + \"\\nbeforeView:\\n{}\\nvs\\n{}\\nafterView:\\n{}\\nvs\\n{}\",\n                    this.memberName, beforeMemberLeft, beforeMemberRight, afterMemberLeft,\n                    afterMemberRight, exception);\n        }\n\n        return new MergedMemberBean<>(beforeMemberResult, afterMemberResult);\n    }\n\n    /**\n     * Merge a member that has consistent (possibly null) beforeViews.\n     *\n     * @param beforeMemberResult\n     *            the pre-merged before member view\n     * @param afterMemberLeft\n     *            the left side after view of the member\n     * @param afterMemberRight\n     *            the right side after view of the member\n     * @return a {@link MergedMemberBean} containing the merged beforeMember view and the merged\n     *         afterMember view\n     */\n    private MergedMemberBean<M> mergeMembersWithConsistentBeforeViews(final M beforeMemberResult,\n            final M afterMemberLeft, final M afterMemberRight)\n    {\n        final M afterMemberResult;\n\n        /*\n         * In the case that both afterMembers are non-null and equivalent, we arbitrarily pick the\n         * left one.\n         */\n        if (afterMemberLeft.equals(afterMemberRight))\n        {\n            return new MergedMemberBean<>(beforeMemberResult, afterMemberLeft);\n        }\n\n        /*\n         * If both beforeMembers are present (we have already asserted their equivalence so we just\n         * arbitrarily use beforeMemberLeft), we use the diffBased strategy if present.\n         */\n        if (beforeMemberResult != null && this.afterViewConsistentBeforeViewMerger != null)\n        {\n            try\n            {\n                afterMemberResult = this.afterViewConsistentBeforeViewMerger\n                        .apply(beforeMemberResult, afterMemberLeft, afterMemberRight);\n            }\n            catch (final FeatureChangeMergeException exception)\n            {\n                throw new FeatureChangeMergeException(exception.withNewTopLevelFailure(\n                        MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED),\n                        \"Attempted afterViewConsistentBeforeMerge failed for {} with beforeView:\\n{}\\nafterView:\\n{}\\nvs\\n{}\",\n                        this.memberName, beforeMemberResult, afterMemberLeft, afterMemberRight,\n                        exception);\n            }\n            catch (final Exception exception)\n            {\n                throw new FeatureChangeMergeException(\n                        MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                        \"Attempted afterViewConsistentBeforeMerge failed for {} with beforeView:\\n{}\\nafterView:\\n{}\\nvs\\n{}\",\n                        this.memberName, beforeMemberResult, afterMemberLeft, afterMemberRight,\n                        exception);\n            }\n        }\n        /*\n         * If the beforeMember is not present, or we don't have a diffBased strategy, we try the\n         * simple strategy.\n         */\n        else if (this.afterViewNoBeforeViewMerger != null)\n        {\n            try\n            {\n                afterMemberResult = this.afterViewNoBeforeViewMerger.apply(afterMemberLeft,\n                        afterMemberRight);\n            }\n            catch (final FeatureChangeMergeException exception)\n            {\n                throw new FeatureChangeMergeException(\n                        exception.withNewTopLevelFailure(\n                                MergeFailureType.AFTER_VIEW_NO_BEFORE_VIEW_MERGE_STRATEGY_FAILED),\n                        \"Attempted afterViewNoBeforeMerge failed for {}; afterView:\\n{}\\nvs\\n{}\",\n                        this.memberName, afterMemberLeft, afterMemberRight, exception);\n            }\n            catch (final Exception exception)\n            {\n                throw new FeatureChangeMergeException(\n                        MergeFailureType.AFTER_VIEW_NO_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                        \"Attempted afterViewNoBeforeMerge failed for {}; afterView:\\n{}\\nvs\\n{}\",\n                        this.memberName, afterMemberLeft, afterMemberRight, exception);\n            }\n        }\n        /*\n         * If there was no simple strategy, we have to fail.\n         */\n        else\n        {\n            throw new FeatureChangeMergeException(\n                    MergeFailureType.MISSING_AFTER_VIEW_MERGE_STRATEGY,\n                    \"Conflicting members and no merge strategy for {}; afterView:\\n{}\\nvs\\n{}\",\n                    this.memberName, afterMemberLeft, afterMemberRight);\n        }\n\n        return new MergedMemberBean<>(beforeMemberResult, afterMemberResult);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/ChangeDescription.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description;\n\nimport java.io.StringWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Base64;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport javax.annotation.Nullable;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeType;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.ChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.ChangeDescriptorComparator;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.tags.AtlasTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * A basic description of the internal contents of a {@link FeatureChange}. A\n * {@link ChangeDescription} consists of a {@link List} of {@link ChangeDescriptor}s as well as some\n * other details (like an identifier, an {@link ItemType}, and a {@link ChangeDescriptorType}).\n *\n * @author lcram\n */\npublic class ChangeDescription\n{\n    /**\n     * A transformer factory for writing data -- creation is <i>surprisingly</i> expensive (~90% of\n     * the cost for saveAsOsc)\n     */\n    private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();\n    /** This improves performance for writing OSC files */\n    private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory\n            .newInstance();\n    private static final Logger logger = LoggerFactory.getLogger(ChangeDescription.class);\n    private static final ChangeDescriptorComparator COMPARATOR = new ChangeDescriptorComparator();\n\n    /** `visible` is used in OSC files to denote whether or not an object should be shown */\n    private static final String VISIBLE = \"visible\";\n    /** `version` is used in OSC files to help determine if there is a change conflict */\n    private static final String VERSION = \"version\";\n    /**\n     * `if-unused` is used in OSC files to indicate that something should be deleted, if it is\n     * unused. Any value enables it.\n     */\n    private static final String IF_UNUSED = \"if-unused\";\n    /** A relation is a collection of other objects */\n    private static final String RELATION = \"relation\";\n    /** A relation member is part of a relation */\n    private static final String RELATION_MEMBER = \"member\";\n    /** The action for JOSM to use */\n    private static final String ACTION = \"action\";\n    /** A modify string */\n    private static final String MODIFY = \"modify\";\n    /** A delete string */\n    private static final String DELETE = \"delete\";\n\n    /** The specified line separator for json (windows \\r\\n works, since \\r is whitespace) */\n    private static final String JSON_LINE_SEPARATOR = System.lineSeparator();\n\n    private final long identifier;\n    private final ItemType itemType;\n    private final ChangeDescriptorType changeDescriptorType;\n    private final List<ChangeDescriptor> descriptors;\n    private final Collection<LocationItem> nodes;\n    private final Map<String, String> originalTags;\n    private final AtlasEntity afterView;\n    private final AtlasEntity beforeView;\n    private String osc;\n\n    /**\n     * Convert a LocationItem to OSC information\n     *\n     * @param information\n     *            The information to add to\n     * @param entity\n     *            The node\n     */\n    private static void atlasEntityToOscInformationNode(final JsonObject information,\n            final LocationItem entity)\n    {\n        // Nodes have two additional attributes for latitude and longitude\n        information.addProperty(\"type\", \"node\");\n        information.addProperty(\"lat\", entity.getLocation().getLatitude().asDegrees());\n        information.addProperty(\"lon\", entity.getLocation().getLongitude().asDegrees());\n    }\n\n    /**\n     * Convert a Relation object to OSC information\n     *\n     * @param information\n     *            The information to add to\n     * @param entity\n     *            The relation\n     */\n    private static void atlasEntityToOscInformationRelation(final JsonObject information,\n            final Relation entity)\n    {\n        information.addProperty(\"type\", RELATION);\n        // Relations have an array of members\n        // <relation><member type=\"node|way|relation\" ref=\"id\" role=\"role\"/></relation>\n        final var members = new JsonArray();\n        // Try to account for relations spread across atlases\n        for (final RelationMember memberInformation : entity.allKnownOsmMembers())\n        {\n            final var memberObject = new JsonObject();\n            final String type;\n            if (memberInformation.getEntity() instanceof LocationItem)\n            {\n                type = \"node\";\n            }\n            else if (memberInformation.getEntity() instanceof LineItem)\n            {\n                type = \"way\";\n            }\n            else\n            {\n                type = RELATION;\n            }\n            memberObject.addProperty(\"type\", type);\n            if (memberInformation.getEntity().getOsmIdentifier() > 0)\n            {\n                memberObject.addProperty(\"ref\", memberInformation.getEntity().getOsmIdentifier());\n            }\n            else\n            {\n                memberObject.addProperty(\"ref\",\n                        newId(memberInformation.getEntity().getIdentifier()));\n            }\n            memberObject.addProperty(\"role\", memberInformation.getRole());\n            members.add(memberObject);\n        }\n    }\n\n    private static void createOscXmlElement(final Document document, final Element parentElement,\n            final JsonObject object)\n    {\n        if (!object.has(\"type\"))\n        {\n            return;\n        }\n        final var type = object.get(\"type\").getAsString();\n        final var objectElement = document.createElement(type);\n        objectElement.setAttribute(VISIBLE, Optional.ofNullable(object.get(VISIBLE))\n                .orElse(new JsonPrimitive(true)).getAsString());\n        objectElement.setAttribute(\"id\",\n                Optional.ofNullable(object.get(\"id\")).orElse(new JsonPrimitive(0)).getAsString());\n        objectElement.setAttribute(VERSION, Optional.ofNullable(object.get(VERSION))\n                .orElse(new JsonPrimitive(1)).getAsString());\n        if (Long.parseLong(objectElement.getAttribute(\"id\")) <= 0)\n        {\n            // New elements are \"modified\", at least as far as JOSM is concerned.\n            objectElement.setAttribute(ACTION, MODIFY);\n        }\n        Optional.ofNullable(object.get(ACTION)).map(JsonElement::getAsString)\n                .ifPresent(action -> objectElement.setAttribute(ACTION, action));\n        Optional.ofNullable(object.get(IF_UNUSED)).map(JsonElement::getAsString)\n                .ifPresent(ifUnused -> objectElement.setAttribute(IF_UNUSED, ifUnused));\n        if (object.has(\"tags\"))\n        {\n            for (final Map.Entry<String, JsonElement> tag : object.get(\"tags\").getAsJsonObject()\n                    .entrySet())\n            {\n                final var tagElement = document.createElement(\"tag\");\n                tagElement.setAttribute(\"k\", tag.getKey());\n                tagElement.setAttribute(\"v\", tag.getValue().getAsString());\n                objectElement.appendChild(tagElement);\n            }\n        }\n        if (\"node\".equals(type))\n        {\n            if (object.has(\"lat\") && object.has(\"lon\"))\n            {\n                final var lat = object.get(\"lat\").getAsDouble();\n                final var lon = object.get(\"lon\").getAsDouble();\n                objectElement.setAttribute(\"lat\", Double.toString(lat));\n                objectElement.setAttribute(\"lon\", Double.toString(lon));\n            }\n            parentElement.appendChild(objectElement);\n        }\n        else if (\"way\".equals(type))\n        {\n            if (object.has(\"nd\"))\n            {\n                object.get(\"nd\").getAsJsonArray().forEach(element ->\n                {\n                    final var ndElement = document.createElement(\"nd\");\n                    ndElement.setAttribute(\"ref\", element.getAsString());\n                    objectElement.appendChild(ndElement);\n                });\n            }\n            parentElement.appendChild(objectElement);\n        }\n        else if (RELATION.equals(type))\n        {\n            if (object.has(RELATION_MEMBER))\n            {\n                object.get(RELATION_MEMBER).getAsJsonArray().forEach(element ->\n                {\n                    final var member = element.getAsJsonObject();\n                    final var memberElement = document.createElement(RELATION_MEMBER);\n                    memberElement.setAttribute(\"type\", member.get(\"type\").getAsString());\n                    memberElement.setAttribute(\"ref\", member.get(\"ref\").getAsString());\n                    memberElement.setAttribute(\"role\", member.get(\"role\").getAsString());\n                    objectElement.appendChild(memberElement);\n                });\n            }\n            parentElement.appendChild(objectElement);\n        }\n    }\n\n    /**\n     * Check if the entity has tags.\n     *\n     * @param entity\n     *            The entity to check\n     * @return {@code true} if the entity has tags\n     */\n    private static boolean hasTags(@Nullable final AtlasEntity entity)\n    {\n        return entity != null && entity.getTags() != null && !entity.getTags().isEmpty();\n    }\n\n    /**\n     * Convert an identifier to a negative number, as this is commonly seen as a placeholder for new\n     * objects\n     *\n     * @param identifier\n     *            the current id\n     * @return The new id (<i>always</i> negative)\n     */\n    private static long newId(final long identifier)\n    {\n        if (identifier < 0)\n        {\n            return identifier;\n        }\n        return -identifier;\n    }\n\n    /**\n     * Convert create, modify, and delete objects to an OSC type json object\n     *\n     * @param description\n     *            The JsonObject to add the OSC info to\n     * @param create\n     *            The objects being created\n     * @param modify\n     *            The objects being modified\n     * @param delete\n     *            The objects being deleted\n     * @throws ParserConfigurationException\n     *             if a parser cannot be configured\n     * @throws TransformerException\n     *             if we could not transform the generated XML to a string.\n     */\n    // Suppress java:S2755 -- external entity vulnerabilities. We are building the XML, not parsing\n    // it.\n    @SuppressWarnings(\"java:S2755\")\n    private static void saveAsOsc(final JsonObject description, final Collection<JsonObject> create,\n            final Collection<JsonObject> modify, final Collection<JsonObject> delete)\n            throws ParserConfigurationException, TransformerException\n    {\n        DOCUMENT_BUILDER_FACTORY.setExpandEntityReferences(false);\n\n        final var builder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();\n        final var document = builder.newDocument();\n        final var rootElement = document.createElement(\"osmChange\");\n        rootElement.setAttribute(VERSION, \"0.6\");\n        // Increment the version when this code changes\n        rootElement.setAttribute(\"generator\", \"atlas ChangeDescription v0.0.1\");\n        document.appendChild(rootElement);\n        if (!create.isEmpty())\n        {\n            final var createElement = document.createElement(\"create\");\n            create.forEach(\n                    createObject -> createOscXmlElement(document, createElement, createObject));\n            rootElement.appendChild(createElement);\n        }\n        if (!modify.isEmpty())\n        {\n            final var modifyElement = document.createElement(MODIFY);\n            modify.forEach(jsonObject -> createOscXmlElement(document, modifyElement, jsonObject));\n            rootElement.appendChild(modifyElement);\n        }\n        if (!delete.isEmpty())\n        {\n            final var deleteElement = document.createElement(DELETE);\n            delete.forEach(\n                    deleteObject -> createOscXmlElement(document, deleteElement, deleteObject));\n            rootElement.appendChild(deleteElement);\n        }\n        if (rootElement.hasChildNodes())\n        {\n            final var transformer = TRANSFORMER_FACTORY.newTransformer();\n            final var stringWriter = new StringWriter();\n            transformer.transform(new DOMSource(document), new StreamResult(stringWriter));\n            final var docString = stringWriter.getBuffer().toString();\n\n            // MapRoulette uses OSC in base64 encoded format, and this is a good way to ensure that\n            // everything is in place\n            saveOsc(description,\n                    Base64.getEncoder().encodeToString(docString.getBytes(StandardCharsets.UTF_8)));\n        }\n    }\n\n    /**\n     * Save OSC to a JsonObject\n     *\n     * @param description\n     *            The object to save to\n     * @param osc\n     *            The osc to save\n     */\n    private static void saveOsc(final JsonObject description, final String osc)\n    {\n        description.add(\"osc\", new JsonPrimitive(osc));\n    }\n\n    public ChangeDescription(final long identifier, final ItemType itemType,\n            final AtlasEntity beforeView, final AtlasEntity afterView,\n            final ChangeType sourceFeatureChangeType)\n    {\n        this(identifier, itemType, beforeView, afterView, sourceFeatureChangeType, null,\n                Collections.emptyList());\n    }\n\n    /**\n     * Create a new ChangeDescription\n     *\n     * @param identifier\n     *            The identifier for the change object\n     * @param itemType\n     *            The item type\n     * @param beforeView\n     *            The unmodified object\n     * @param afterView\n     *            The modified object\n     * @param sourceFeatureChangeType\n     *            Change type\n     * @param originalTags\n     *            The original object's tags\n     * @param nodes\n     *            The nodes to be used for way geometry changes, in order. If a collection has\n     *            multiple nodes, then no geometry changes for an OSC will be written.\n     */\n    public ChangeDescription(final long identifier, final ItemType itemType,\n            final AtlasEntity beforeView, final AtlasEntity afterView,\n            final ChangeType sourceFeatureChangeType, final Map<String, String> originalTags,\n            final Collection<LocationItem> nodes)\n    {\n        this.identifier = identifier;\n        this.itemType = itemType;\n        this.descriptors = new ArrayList<>();\n\n        this.nodes = nodes != null ? nodes : Collections.emptyList();\n        this.originalTags = originalTags != null ? originalTags : Collections.emptyMap();\n        // Avoid saving before/after views if we don't need them to generate the json (if no nodes,\n        // then not needed)\n        if (!this.nodes.isEmpty())\n        {\n            this.afterView = afterView;\n            this.beforeView = beforeView;\n        }\n        else if (sourceFeatureChangeType == ChangeType.REMOVE)\n        {\n            // We need before views for removal\n            this.afterView = null;\n            this.beforeView = beforeView;\n        }\n        else\n        {\n            this.afterView = null;\n            this.beforeView = null;\n        }\n\n        if (sourceFeatureChangeType == ChangeType.ADD)\n        {\n            if (beforeView != null)\n            {\n                this.changeDescriptorType = ChangeDescriptorType.UPDATE;\n            }\n            else\n            {\n                this.changeDescriptorType = ChangeDescriptorType.ADD;\n            }\n        }\n        else\n        {\n            this.changeDescriptorType = ChangeDescriptorType.REMOVE;\n        }\n        this.descriptors.addAll(\n                new ChangeDescriptorGenerator(beforeView, afterView, this.changeDescriptorType)\n                        .generate());\n    }\n\n    public ChangeDescriptorType getChangeDescriptorType()\n    {\n        return this.changeDescriptorType;\n    }\n\n    /**\n     * Get a sorted copy of the underlying {@link ChangeDescriptor} list.\n     *\n     * @return the sorted list\n     */\n    public List<ChangeDescriptor> getChangeDescriptors()\n    {\n        return new ArrayList<>(this.descriptors);\n    }\n\n    /**\n     * Get the identifier of the feature described by this {@link ChangeDescription}.\n     *\n     * @return the identifier\n     */\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    /**\n     * Get the {@link ItemType} of the feature described by this {@link ChangeDescription}.\n     *\n     * @return the type\n     */\n    public ItemType getItemType()\n    {\n        return this.itemType;\n    }\n\n    /**\n     * Get the OSC, if one exists\n     *\n     * @return An optional with the osc, if present\n     */\n    public Optional<String> getOsc()\n    {\n        return Optional.ofNullable(this.osc);\n    }\n\n    /**\n     * Set the OSC information (this is for deserialization, please do not call when not\n     * deserializing)\n     *\n     * @param osc\n     *            The osc to set (base64 encoded)\n     */\n    public void setOsc(final String osc)\n    {\n        this.osc = osc;\n    }\n\n    public JsonElement toJsonElement()\n    {\n        final JsonObject description = new JsonObject();\n        description.addProperty(\"type\", this.changeDescriptorType.toString());\n        final JsonArray descriptorArray = new JsonArray();\n        for (final ChangeDescriptor descriptor : this.descriptors)\n        {\n            descriptorArray.add(descriptor.toJsonElement());\n        }\n        description.add(\"descriptors\", descriptorArray);\n\n        if (this.osc == null)\n        {\n            this.createOsc(description);\n        }\n        else\n        {\n            saveOsc(description, this.osc);\n        }\n        return description;\n    }\n\n    @Override\n    public String toString()\n    {\n        this.descriptors.sort(COMPARATOR);\n        final var builder = new StringBuilder(22 + 2 * JSON_LINE_SEPARATOR.length());\n        builder.append(\"ChangeDescription [\");\n        builder.append(JSON_LINE_SEPARATOR);\n        builder.append(this.changeDescriptorType);\n        builder.append(\" \");\n        builder.append(this.itemType);\n        builder.append(\" \");\n        builder.append(this.getIdentifier());\n        builder.append(JSON_LINE_SEPARATOR);\n\n        if (this.descriptors.isEmpty())\n        {\n            builder.append(\"]\");\n            return builder.toString();\n        }\n\n        for (int i = 0; i < this.descriptors.size() - 1; i++)\n        {\n            builder.append(this.descriptors.get(i));\n            builder.append(JSON_LINE_SEPARATOR);\n        }\n        builder.append(this.descriptors.get(this.descriptors.size() - 1));\n        builder.append(JSON_LINE_SEPARATOR);\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    /**\n     * Convert an entity to a JsonObject which can be used to create an OSC file. Note: You still\n     * have to add the {@code visible=\"true|false\"} information, as we don't know if this is\n     * creating, updating, or removing.\n     *\n     * @param entity\n     *            the entity to convert\n     * @param nodes\n     *            nodes for the entity (may include unrelated nodes)\n     * @param tags\n     *            The OSM tags for the entity. May be {@code null}. If {@code null}, tags from the\n     *            entity are used.\n     * @return A JsonObject with the information needed to create an OSC file\n     */\n    private JsonObject atlasEntityToOscInformation(final AtlasEntity entity,\n            final Collection<? extends LocationItem> nodes,\n            @Nullable final Map<String, String> tags)\n    {\n        final var information = new JsonObject();\n        final var tagsToUse = Optional.ofNullable(tags).orElseGet(entity::getTags);\n        final var osmTagsToUse = Optional.ofNullable(tags).orElseGet(Collections::emptyMap)\n                .entrySet().stream()\n                .filter(tag -> !AtlasTag.TAGS_FROM_OSM.contains(tag.getKey())\n                        && !AtlasTag.TAGS_FROM_ATLAS.contains(tag.getKey()))\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n        // Common requirements\n        if (entity.getOsmIdentifier() > 0)\n        {\n            information.addProperty(\"id\", entity.getOsmIdentifier());\n        }\n        else\n        {\n            information.addProperty(\"id\", newId(entity.getIdentifier()));\n        }\n        // This is used to ensure that this will apply cleanly\n        if (tagsToUse != null)\n        {\n            final Optional<String> lastEditVersion = Optional\n                    .ofNullable(tagsToUse.get(\"last_edit_version\"));\n            lastEditVersion.ifPresent(s -> information.addProperty(VERSION, Long.parseLong(s)));\n        }\n        // Add the tags (OSC files are idempotent, in that they require <i>all</i> information for\n        // an object)\n        final Map<String, String> tagsToAdd;\n        if (!osmTagsToUse.isEmpty())\n        {\n            tagsToAdd = osmTagsToUse;\n        }\n        else if (entity.getTags() != null && !entity.getOsmTags().isEmpty())\n        {\n            tagsToAdd = entity.getOsmTags();\n        }\n        else\n        {\n            tagsToAdd = null;\n        }\n        if (tagsToAdd != null)\n        {\n            final var tagsObject = new JsonObject();\n            tagsToAdd.forEach(tagsObject::addProperty);\n            information.add(\"tags\", tagsObject);\n        }\n\n        if (entity instanceof LocationItem)\n        {\n            atlasEntityToOscInformationNode(information, (LocationItem) entity);\n        }\n        else if (entity instanceof LineItem && ((LineItem) entity).asPolyLine() != null)\n        {\n            return this.atlasEntityToOscInformationWay(information,\n                    ((LineItem) entity).asPolyLine(), nodes);\n        }\n        else if (entity instanceof Area && ((Area) entity).asPolygon() != null)\n        {\n            return this.atlasEntityToOscInformationWay(information, ((Area) entity).asPolygon(),\n                    nodes);\n        }\n        else if (entity instanceof Relation)\n        {\n            atlasEntityToOscInformationRelation(information, (Relation) entity);\n        }\n        return information;\n    }\n\n    /**\n     * Convert a LineItem to OSC information\n     *\n     * @param information\n     *            The information to add to\n     * @param polyLine\n     *            The polyline for the way\n     * @return {@code null} if we cannot create the way with the available information\n     */\n    private JsonObject atlasEntityToOscInformationWay(final JsonObject information,\n            final PolyLine polyLine, final Collection<? extends LocationItem> nodes)\n    {\n        information.addProperty(\"type\", \"way\");\n        // Lines have an array of node references\n        // <way><nd ref=\"-1\"/><nd ref=\"-2\"</></way>\n        final var nodeIds = new JsonArray();\n        for (final Location location : polyLine)\n        {\n            // Don't short-circuit on the first found, since there is no guarantee that it is\n            // the only location at that point.\n            final List<LocationItem> foundNodes = nodes.stream()\n                    .filter(locationItem -> location.equals(locationItem.getLocation()))\n                    .collect(Collectors.toList());\n            // Atlases don't store what nodes belong to what ways, so we cannot create an OSC\n            // change for this way.\n            final long nodeCount = foundNodes.stream().mapToLong(LocationItem::getIdentifier)\n                    .distinct().count();\n            if (nodeCount == 1)\n            {\n                if (foundNodes.get(0).getOsmIdentifier() > 0)\n                {\n                    nodeIds.add(new JsonPrimitive(foundNodes.get(0).getOsmIdentifier()));\n                }\n                else\n                {\n                    nodeIds.add(new JsonPrimitive(newId(foundNodes.get(0).getIdentifier())));\n                }\n            }\n            else if (nodeCount != 0 || this.changeDescriptorType != ChangeDescriptorType.REMOVE)\n            {\n                // Ways that are being removed may have no nodes to remove (for example, if all\n                // nodes are part of other ways)\n                return null;\n            }\n        }\n        information.add(\"nd\", nodeIds);\n        return information;\n    }\n\n    /**\n     * Create an object for use in generating an OSC\n     *\n     * @param description\n     *            The object to add the OSC info to\n     */\n    private void createOsc(final JsonObject description)\n    {\n        final List<JsonObject> create = new ArrayList<>();\n        final List<JsonObject> modify = new ArrayList<>();\n        final List<JsonObject> delete = new ArrayList<>();\n        final Collection<Location> requiredLocations = new HashSet<>();\n        this.oscCreateUpdate(create, modify);\n        if (this.updateRequiredLocations(create, modify, requiredLocations))\n        {\n            this.oscDelete(delete, requiredLocations);\n            try\n            {\n                saveAsOsc(description, create, modify, delete);\n            }\n            catch (final TransformerException | ParserConfigurationException e)\n            {\n                logger.error(\"Could not save OpenStreetMap Change information\", e);\n            }\n        }\n    }\n\n    /**\n     * Fill information in for create/update objects\n     *\n     * @param create\n     *            The collection of objects that will be created (this is added to)\n     * @param modify\n     *            The collection of objects that will be modified (this is added to)\n     */\n    private void oscCreateUpdate(final List<? super JsonObject> create,\n            final Collection<? super JsonObject> modify)\n    {\n        if ((this.changeDescriptorType == ChangeDescriptorType.ADD\n                || this.changeDescriptorType == ChangeDescriptorType.UPDATE)\n                && this.afterView != null)\n        {\n            final Map<String, String> tags;\n            if (!hasTags(this.afterView) && !hasTags(this.beforeView)\n                    && !this.originalTags.isEmpty())\n            {\n                tags = this.originalTags;\n            }\n            else if (hasTags(this.afterView))\n            {\n                tags = this.afterView.getTags();\n            }\n            else\n            {\n                tags = null;\n            }\n\n            final JsonObject createObject = this.atlasEntityToOscInformation(this.afterView,\n                    this.nodes, tags);\n            if (createObject != null)\n            {\n                createObject.addProperty(VISIBLE, true);\n                // This is needed for JOSM to know that something has been modified, and needs to be\n                // uploaded.\n                createObject.addProperty(ACTION, MODIFY);\n                if (this.changeDescriptorType == ChangeDescriptorType.ADD)\n                {\n                    create.add(createObject);\n                }\n                else\n                {\n                    modify.add(createObject);\n                }\n            }\n        }\n    }\n\n    /**\n     * Fill items in for delete objects\n     *\n     * @param delete\n     *            The collection of objects to delete\n     * @param requiredLocations\n     *            The locations where nodes must not be deleted\n     */\n    private void oscDelete(final List<JsonObject> delete,\n            final Collection<Location> requiredLocations)\n    {\n        if (this.changeDescriptorType == ChangeDescriptorType.REMOVE && this.beforeView != null)\n        {\n            final JsonObject deleteObject = this.atlasEntityToOscInformation(this.beforeView,\n                    this.nodes, null);\n            if (deleteObject != null)\n            {\n                // JOSM does <i>not</i> mark an object as modified if it is visible.\n                // See following link for logic in JOSM\n                // https://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/io/AbstractReader.java?rev=17822#L512\n                deleteObject.addProperty(VISIBLE, true);\n                // This helps ensure that we don't accidentally delete something if the OSC is\n                // directly applied to the OSM API.\n                deleteObject.addProperty(IF_UNUSED, true);\n                // Remove lat/lon, since we are deleting the object.\n                deleteObject.remove(\"lat\");\n                deleteObject.remove(\"lon\");\n                // Needed for JOSM to know that it needs to be uploaded\n                deleteObject.addProperty(ACTION, DELETE);\n                delete.add(deleteObject);\n            }\n        }\n        this.oscDeleteNodes(delete, requiredLocations);\n    }\n\n    /**\n     * Delete nodes\n     *\n     * @param delete\n     *            The JsonObjects that may have deletable nodes\n     * @param requiredLocations\n     *            The locations that must not be deleted\n     */\n    private void oscDeleteNodes(final List<JsonObject> delete,\n            final Collection<Location> requiredLocations)\n    {\n        // new ArrayList avoids a concurrent modification exception\n        for (final JsonObject entityObject : new ArrayList<>(delete))\n        {\n            if (entityObject.get(\"nd\") != null)\n            {\n                final var localNodes = entityObject.get(\"nd\").getAsJsonArray();\n                for (final JsonElement nodeElement : localNodes)\n                {\n                    final var nodeId = nodeElement.getAsLong();\n                    final LocationItem node = this.nodes.stream()\n                            .filter(node1 -> node1.getOsmIdentifier() == nodeId).findAny()\n                            .filter(node1 -> node1.getTags() != null)\n                            .filter(node1 -> node1.getOsmTags().isEmpty())\n                            .filter(node1 -> !requiredLocations.contains(node1.getLocation()))\n                            .orElse(null);\n                    // Don't delete nodes with tags\n                    if (node != null)\n                    {\n                        final JsonObject nodeDelete = this.atlasEntityToOscInformation(node, null,\n                                Collections.emptyMap());\n                        nodeDelete.addProperty(VISIBLE, false);\n                        // This helps ensure that we don't accidentally delete something if the\n                        // OSC is directly applied to the OSM API.\n                        nodeDelete.addProperty(IF_UNUSED, true);\n                        // JOSM needs the action prop to mark the object as modified\n                        nodeDelete.addProperty(ACTION, DELETE);\n                        // Remove lat/lon, since we are deleting the object.\n                        nodeDelete.remove(\"lat\");\n                        nodeDelete.remove(\"lon\");\n                        delete.add(nodeDelete);\n                    }\n                }\n                entityObject.addProperty(ACTION, DELETE);\n            }\n        }\n    }\n\n    /**\n     * Fill information in for create/update objects\n     *\n     * @param create\n     *            The collection of objects that will be created\n     * @param modify\n     *            The collection of objects that will be modified\n     * @param requiredLocations\n     *            The collection of locations that must be present (this is added to)\n     * @return {@code true} if we can continue with the osc creation process\n     */\n    private boolean updateRequiredLocations(final List<JsonObject> create,\n            final Collection<JsonObject> modify,\n            final Collection<? super Location> requiredLocations)\n    {\n        for (final JsonObject entityObject : Stream.concat(create.stream(), modify.stream())\n                .collect(Collectors.toList()))\n        {\n            if (entityObject.get(\"nd\") == null)\n            {\n                continue;\n            }\n            final var localNodes = entityObject.get(\"nd\").getAsJsonArray();\n            for (final JsonElement nodeElement : localNodes)\n            {\n                final var nodeId = nodeElement.getAsLong();\n                final List<LocationItem> nodesFound = this.nodes.stream().filter(\n                        node -> node.getIdentifier() == nodeId || node.getOsmIdentifier() == nodeId)\n                        .collect(Collectors.toList());\n                if (nodesFound.size() != 1)\n                {\n                    return false;\n                }\n                if (nodeId < 0)\n                {\n                    // New nodes should come prior to anything else\n                    final JsonObject newNode = this.atlasEntityToOscInformation(nodesFound.get(0),\n                            null, null);\n                    newNode.addProperty(VISIBLE, true);\n                    create.add(0, newNode);\n                }\n                requiredLocations.add(nodesFound.get(0).getLocation());\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/ChangeDescriptorGenerator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.ChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.ChangeDescriptorComparator;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.ChangeDescriptorName;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.GeometricRelationGeometryChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.GeometryChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.LongElementChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.RelationMemberChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.TagChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * A helper class for generating a list of {@link ChangeDescriptor}s based on some\n * {@link AtlasEntity} beforeView and afterView.\n *\n * @author lcram\n */\npublic final class ChangeDescriptorGenerator\n{\n    private static final ChangeDescriptorComparator COMPARATOR = new ChangeDescriptorComparator();\n    private static final String CORRUPTED_FEATURECHANGE_MESSAGE = \"Corrupted FeatureChange: afterView {} != null but beforeView {} == null\";\n\n    private final AtlasEntity beforeView;\n    private final AtlasEntity afterView;\n    private final ChangeDescriptorType changeDescriptorType;\n    /**\n     * Expected nodes for a way. This may be empty.\n     */\n    private final List<LocationItem> nodes = new ArrayList<>(0);\n\n    ChangeDescriptorGenerator(final AtlasEntity beforeView, final AtlasEntity afterView,\n            final ChangeDescriptorType changeDescriptorType)\n    {\n        this(beforeView, afterView, changeDescriptorType, null);\n    }\n\n    /**\n     * Create a new change description\n     *\n     * @param beforeView\n     *            The object prior to the change\n     * @param afterView\n     *            The view after the change\n     * @param changeDescriptorType\n     *            The change type\n     * @param nodes\n     *            If modifying a way geometry, this <i>must</i> contain <i>all</i> nodes needed for\n     *            the way, if openStreetChange is needed (MapRoulette challenges can use this for\n     *            quick fixes).\n     */\n    ChangeDescriptorGenerator(final AtlasEntity beforeView, final AtlasEntity afterView,\n            final ChangeDescriptorType changeDescriptorType, final Collection<LocationItem> nodes)\n    {\n        this.beforeView = beforeView;\n        this.afterView = afterView;\n        this.changeDescriptorType = changeDescriptorType;\n        this.setNodes(nodes);\n    }\n\n    /**\n     * Set nodes needed for a full OSC file\n     *\n     * @param nodes\n     *            The nodes needed for an OSC.\n     * @return this, for easy chaining\n     */\n    public ChangeDescriptorGenerator setNodes(final Collection<LocationItem> nodes)\n    {\n        this.nodes.clear();\n        if (nodes != null)\n        {\n            this.nodes.addAll(nodes);\n        }\n        return this;\n    }\n\n    List<ChangeDescriptor> generate()\n    {\n        final List<ChangeDescriptor> descriptors = new ArrayList<>();\n\n        /*\n         * For the REMOVE case, there's no point showing any details. Users can just look at the\n         * FeatureChange output itself to see the beforeView and afterView.\n         */\n        if (this.changeDescriptorType == ChangeDescriptorType.REMOVE)\n        {\n            return descriptors;\n        }\n\n        descriptors.addAll(generateTagDescriptors());\n        descriptors.addAll(generateGeometryDescriptors());\n        descriptors.addAll(generateParentRelationDescriptors(CompleteEntity::relationIdentifiers));\n        if (this.afterView.getType() == ItemType.NODE)\n        {\n            descriptors.addAll(generateNodeInOutDescriptors(ChangeDescriptorName.IN_EDGE,\n                    CompleteNode::inEdgeIdentifiers));\n            descriptors.addAll(generateNodeInOutDescriptors(ChangeDescriptorName.OUT_EDGE,\n                    CompleteNode::outEdgeIdentifiers));\n        }\n        if (this.afterView.getType() == ItemType.EDGE)\n        {\n            descriptors.addAll(generateEdgeStartEndDescriptors(ChangeDescriptorName.START_NODE,\n                    CompleteEdge::startNodeIdentifier));\n            descriptors.addAll(generateEdgeStartEndDescriptors(ChangeDescriptorName.END_NODE,\n                    CompleteEdge::endNodeIdentifier));\n        }\n        if (this.afterView.getType() == ItemType.RELATION)\n        {\n            descriptors.addAll(generateRelationMemberDescriptors());\n            /*\n             * Should we handle the other special relation fields here?\n             * allRelationsWithSameOsmIdentifier, allKnownOsmMembers, and osmRelationIdentifier are\n             * fields that may be altered.\n             */\n        }\n\n        descriptors.sort(COMPARATOR);\n        return descriptors;\n    }\n\n    ChangeDescriptorType getChangeDescriptorType()\n    {\n        return this.changeDescriptorType;\n    }\n\n    private List<LongElementChangeDescriptor> generateEdgeStartEndDescriptors(\n            final ChangeDescriptorName name, final Function<CompleteEdge, Long> memberExtractor) // NOSONAR\n    {\n        final CompleteEdge beforeEntity = (CompleteEdge) this.beforeView;\n        final CompleteEdge afterEntity = (CompleteEdge) this.afterView;\n\n        /*\n         * If the afterView identifier was null, then we know that it was not updated. We can just\n         * return nothing.\n         */\n        if (memberExtractor.apply(afterEntity) == null)\n        {\n            return new ArrayList<>();\n        }\n\n        final Long beforeIdentifier;\n        if (beforeEntity != null)\n        {\n            if (memberExtractor.apply(beforeEntity) == null)\n            {\n                throw new CoreException(CORRUPTED_FEATURECHANGE_MESSAGE, name, name);\n            }\n            beforeIdentifier = memberExtractor.apply(beforeEntity);\n        }\n        else\n        {\n            beforeIdentifier = null;\n        }\n        final Long afterIdentifier = memberExtractor.apply(afterEntity);\n\n        return generateLongValueDescriptors(name, beforeIdentifier, afterIdentifier);\n    }\n\n    private List<ChangeDescriptor> generateGeometryDescriptors()\n    {\n        final List<ChangeDescriptor> descriptors = new ArrayList<>();\n\n        /*\n         * Relations do not have explicit geometry, so return nothing.\n         */\n        if (this.afterView.getType() == ItemType.RELATION)\n        {\n            final Relation beforeRelation = (Relation) this.beforeView;\n            final Relation afterRelation = (Relation) this.afterView;\n            final ChangeDescriptor descriptor = GeometricRelationGeometryChangeDescriptor\n                    .getDescriptorsForGeometricRelations(beforeRelation, afterRelation);\n            if (descriptor != null)\n            {\n                descriptors.add(descriptor);\n            }\n            return descriptors;\n        }\n\n        final CompleteEntity<? extends CompleteEntity<?>> beforeEntity = (CompleteEntity<? extends CompleteEntity<?>>) this.beforeView;\n        final CompleteEntity<? extends CompleteEntity<?>> afterEntity = (CompleteEntity<? extends CompleteEntity<?>>) this.afterView;\n\n        /*\n         * If the afterView geometry is null, then we know the geometry was not updated.\n         */\n        if (afterEntity.getGeometry() == null)\n        {\n            return descriptors;\n        }\n\n        final List<Location> beforeGeometry = new ArrayList<>();\n        final List<Location> afterGeometry = new ArrayList<>();\n        afterEntity.getGeometry().forEach(afterGeometry::add);\n        if (beforeEntity != null)\n        {\n            if (beforeEntity.getGeometry() == null)\n            {\n                throw new CoreException(CORRUPTED_FEATURECHANGE_MESSAGE, \"geometry\", \"geometry\");\n            }\n            beforeEntity.getGeometry().forEach(beforeGeometry::add);\n        }\n        descriptors.addAll(\n                GeometryChangeDescriptor.getDescriptorsForGeometry(beforeGeometry, afterGeometry));\n\n        return descriptors;\n    }\n\n    private List<LongElementChangeDescriptor> generateLongSetDescriptors(\n            final ChangeDescriptorName name, final Set<Long> beforeSet, final Set<Long> afterSet)\n    {\n        final List<LongElementChangeDescriptor> descriptors = new ArrayList<>();\n\n        final Set<Long> removedFromAfterView = com.google.common.collect.Sets.difference(beforeSet,\n                afterSet);\n        final Set<Long> addedToAfterView = com.google.common.collect.Sets.difference(afterSet,\n                beforeSet);\n        for (final Long identifier : removedFromAfterView)\n        {\n            descriptors.add(new LongElementChangeDescriptor(ChangeDescriptorType.REMOVE, identifier,\n                    null, name));\n        }\n        for (final Long identifier : addedToAfterView)\n        {\n            descriptors.add(\n                    new LongElementChangeDescriptor(ChangeDescriptorType.ADD, identifier, name));\n        }\n        return descriptors;\n    }\n\n    private List<LongElementChangeDescriptor> generateLongValueDescriptors(\n            final ChangeDescriptorName name, final Long beforeIdentifier,\n            final Long afterIdentifier)\n    {\n        final List<LongElementChangeDescriptor> descriptors = new ArrayList<>();\n\n        /*\n         * This case occurs when an brand new Long value (e.g. startNode, endNode, etc.) is being\n         * added, and so there is no beforeElement.\n         */\n        if (beforeIdentifier == null)\n        {\n            descriptors.add(new LongElementChangeDescriptor(ChangeDescriptorType.ADD, null,\n                    afterIdentifier, name));\n        }\n        else\n        {\n            descriptors.add(new LongElementChangeDescriptor(ChangeDescriptorType.UPDATE,\n                    beforeIdentifier, afterIdentifier, name));\n        }\n\n        return descriptors;\n    }\n\n    private List<LongElementChangeDescriptor> generateNodeInOutDescriptors(\n            final ChangeDescriptorName name,\n            final Function<CompleteNode, Set<Long>> memberExtractor)\n    {\n        final CompleteNode beforeEntity = (CompleteNode) this.beforeView;\n        final CompleteNode afterEntity = (CompleteNode) this.afterView;\n\n        /*\n         * If the afterView in/out edge set was null, then we know that it was not updated. We can\n         * just return nothing.\n         */\n        if (memberExtractor.apply(afterEntity) == null)\n        {\n            return new ArrayList<>();\n        }\n\n        final Set<Long> beforeSet;\n        if (beforeEntity != null)\n        {\n            if (memberExtractor.apply(beforeEntity) == null)\n            {\n                throw new CoreException(CORRUPTED_FEATURECHANGE_MESSAGE, name, name);\n            }\n            beforeSet = memberExtractor.apply(beforeEntity);\n        }\n        else\n        {\n            beforeSet = new HashSet<>();\n        }\n        final Set<Long> afterSet = memberExtractor.apply(afterEntity);\n\n        return generateLongSetDescriptors(name, beforeSet, afterSet);\n    }\n\n    private List<LongElementChangeDescriptor> generateParentRelationDescriptors(\n            final Function<CompleteEntity, Set<Long>> memberExtractor)\n    {\n        final ChangeDescriptorName name = ChangeDescriptorName.PARENT_RELATION;\n\n        final CompleteEntity<? extends CompleteEntity<?>> beforeEntity = (CompleteEntity<? extends CompleteEntity<?>>) this.beforeView;\n        final CompleteEntity<? extends CompleteEntity<?>> afterEntity = (CompleteEntity<? extends CompleteEntity<?>>) this.afterView;\n\n        /*\n         * If the afterView parent relations were null, then we know that they were not updated. We\n         * can just return nothing.\n         */\n        if (memberExtractor.apply(afterEntity) == null)\n        {\n            return new ArrayList<>();\n        }\n\n        final Set<Long> beforeSet;\n        if (beforeEntity != null)\n        {\n            if (memberExtractor.apply(beforeEntity) == null)\n            {\n                throw new CoreException(CORRUPTED_FEATURECHANGE_MESSAGE, name, name);\n            }\n            beforeSet = memberExtractor.apply(beforeEntity);\n        }\n        else\n        {\n            beforeSet = new HashSet<>();\n        }\n        final Set<Long> afterSet = memberExtractor.apply(afterEntity);\n\n        return generateLongSetDescriptors(name, beforeSet, afterSet);\n    }\n\n    private List<ChangeDescriptor> generateRelationMemberDescriptors()\n    {\n        final List<ChangeDescriptor> descriptors = new ArrayList<>();\n\n        final CompleteRelation beforeEntity = (CompleteRelation) this.beforeView;\n        final CompleteRelation afterEntity = (CompleteRelation) this.afterView;\n\n        /*\n         * If the afterView members were null, then we know that the members were not updated. We\n         * can just return nothing.\n         */\n        if (afterEntity.members() == null)\n        {\n            return descriptors;\n        }\n\n        final Set<RelationBean.RelationBeanItem> beforeBeanSet;\n        if (beforeEntity != null)\n        {\n            if (beforeEntity.members() == null)\n            {\n                throw new CoreException(CORRUPTED_FEATURECHANGE_MESSAGE, \"relation members\",\n                        \"relation members\");\n            }\n            beforeBeanSet = beforeEntity.members().asBean().asSet();\n        }\n        else\n        {\n            beforeBeanSet = new HashSet<>();\n        }\n        final Set<RelationBean.RelationBeanItem> afterBeanSet = afterEntity.members().asBean()\n                .asSet();\n\n        final Set<RelationBean.RelationBeanItem> itemsRemovedFromAfterView = com.google.common.collect.Sets\n                .difference(beforeBeanSet, afterBeanSet);\n        final Set<RelationBean.RelationBeanItem> itemsAddedToAfterView = com.google.common.collect.Sets\n                .difference(afterBeanSet, beforeBeanSet);\n\n        for (final RelationBean.RelationBeanItem item : itemsRemovedFromAfterView)\n        {\n            descriptors.add(new RelationMemberChangeDescriptor(ChangeDescriptorType.REMOVE,\n                    item.getIdentifier(), item.getType(), item.getRole()));\n        }\n        for (final RelationBean.RelationBeanItem item : itemsAddedToAfterView)\n        {\n            descriptors.add(new RelationMemberChangeDescriptor(ChangeDescriptorType.ADD,\n                    item.getIdentifier(), item.getType(), item.getRole()));\n        }\n\n        return descriptors;\n    }\n\n    private List<ChangeDescriptor> generateTagDescriptors()\n    {\n        final List<ChangeDescriptor> descriptors = new ArrayList<>();\n\n        /*\n         * If the afterView tags were null, then we know that the tags were not updated. We can just\n         * return nothing.\n         */\n        if (this.afterView.getTags() == null)\n        {\n            return descriptors;\n        }\n\n        final Map<String, String> beforeTags;\n        if (this.beforeView != null)\n        {\n            if (this.beforeView.getTags() == null)\n            {\n                throw new CoreException(CORRUPTED_FEATURECHANGE_MESSAGE, \"tags\", \"tags\");\n            }\n            beforeTags = this.beforeView.getTags();\n        }\n        else\n        {\n            beforeTags = new HashMap<>();\n        }\n        final Map<String, String> afterTags = this.afterView.getTags();\n\n        final Set<String> keysRemovedFromAfterView = com.google.common.collect.Sets\n                .difference(beforeTags.keySet(), afterTags.keySet());\n        final Set<String> keysAddedToAfterView = com.google.common.collect.Sets\n                .difference(afterTags.keySet(), beforeTags.keySet());\n        final Set<String> keysShared = com.google.common.collect.Sets\n                .intersection(beforeTags.keySet(), afterTags.keySet());\n        for (final String key : keysRemovedFromAfterView)\n        {\n            descriptors.add(\n                    new TagChangeDescriptor(ChangeDescriptorType.REMOVE, key, beforeTags.get(key)));\n        }\n        for (final String key : keysAddedToAfterView)\n        {\n            descriptors.add(\n                    new TagChangeDescriptor(ChangeDescriptorType.ADD, key, afterTags.get(key)));\n        }\n        for (final String key : keysShared)\n        {\n            if (!beforeTags.get(key).equals(afterTags.get(key)))\n            {\n                descriptors.add(new TagChangeDescriptor(ChangeDescriptorType.UPDATE, key,\n                        afterTags.get(key), beforeTags.get(key)));\n            }\n        }\n\n        return descriptors;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/ChangeDescriptorType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description;\n\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeType;\n\n/**\n * Three basic types to characterize the change descriptions. This is similar to the\n * {@link ChangeType} enum, but with additional support for a more granular\n * {@link ChangeDescriptorType#UPDATE} type.\n * \n * @author lcram\n */\npublic enum ChangeDescriptorType\n{\n    ADD,\n    UPDATE,\n    REMOVE\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/ChangeDescriptor.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescription;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * A basic change unit, which when grouped together form a {@link ChangeDescription}.\n * \n * @author lcram\n */\npublic interface ChangeDescriptor\n{\n    ChangeDescriptorType getChangeDescriptorType();\n\n    ChangeDescriptorName getName();\n\n    default JsonElement toJsonElement()\n    {\n        final JsonObject descriptor = new JsonObject();\n        descriptor.addProperty(\"name\", getName().toString());\n        descriptor.addProperty(\"type\", getChangeDescriptorType().toString());\n        return descriptor;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/ChangeDescriptorComparator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\nimport java.util.Comparator;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescription;\n\n/**\n * A {@link Comparator} for {@link ChangeDescriptor}s, which defines an ordering. This is useful\n * when printing {@link ChangeDescription}s, so the display can show a consistent element ordering\n * \n * @author lcram\n */\npublic class ChangeDescriptorComparator implements Comparator<ChangeDescriptor>\n{\n    @Override\n    public int compare(final ChangeDescriptor left, final ChangeDescriptor right)\n    {\n        if (left.getName() != right.getName())\n        {\n            return left.getName().compareTo(right.getName());\n        }\n        return complexCompare(left, right);\n    }\n\n    private int complexCompare(final ChangeDescriptor left, final ChangeDescriptor right)\n    {\n        if (left instanceof TagChangeDescriptor && right instanceof TagChangeDescriptor)\n        {\n            return tagChangeCompare((TagChangeDescriptor) left, (TagChangeDescriptor) right);\n        }\n        if (left instanceof GeometryChangeDescriptor && right instanceof GeometryChangeDescriptor)\n        {\n            return geometryChangeCompare((GeometryChangeDescriptor) left,\n                    (GeometryChangeDescriptor) right);\n        }\n        if (left instanceof LongElementChangeDescriptor\n                && right instanceof LongElementChangeDescriptor)\n        {\n            return genericSetChangeCompare((LongElementChangeDescriptor) left,\n                    (LongElementChangeDescriptor) right);\n        }\n        if (left instanceof RelationMemberChangeDescriptor\n                && right instanceof RelationMemberChangeDescriptor)\n        {\n            return relationMemberChangeCompare((RelationMemberChangeDescriptor) left,\n                    (RelationMemberChangeDescriptor) right);\n        }\n\n        throw new CoreException(\"Could not compare {} vs {}\", left, right);\n    }\n\n    private int genericSetChangeCompare(final LongElementChangeDescriptor left,\n            final LongElementChangeDescriptor right)\n    {\n        if (left.getName() != right.getName())\n        {\n            return left.getName().compareTo(right.getName());\n        }\n        if (left.getChangeDescriptorType() != right.getChangeDescriptorType())\n        {\n            return left.getChangeDescriptorType().compareTo(right.getChangeDescriptorType());\n        }\n        final Comparable leftBeforeComparable = (Comparable) left.getBeforeElement();\n        final Comparable rightBeforeComparable = (Comparable) right.getBeforeElement();\n        if (leftBeforeComparable != null && rightBeforeComparable != null\n                && !leftBeforeComparable.equals(rightBeforeComparable))\n        {\n            return leftBeforeComparable.compareTo(rightBeforeComparable);\n        }\n        final Comparable leftAfterComparable = (Comparable) left.getAfterElement();\n        final Comparable rightAfterComparable = (Comparable) right.getAfterElement();\n        if (leftAfterComparable != null && rightAfterComparable != null\n                && !leftAfterComparable.equals(rightAfterComparable))\n        {\n            return leftAfterComparable.compareTo(rightAfterComparable);\n        }\n        else\n        {\n            /*\n             * If this message appears in production, then that means either this comparison logic\n             * or ChangeDescriptor generation is dubious. But based on the way ChangeDescriptors are\n             * generated, it should never show up in practice.\n             */\n            throw new CoreException(\"No comparable criteria for {} vs {}\", left, right);\n        }\n    }\n\n    private int geometryChangeCompare(final GeometryChangeDescriptor left,\n            final GeometryChangeDescriptor right)\n    {\n        if (left.getChangeDescriptorType() != right.getChangeDescriptorType())\n        {\n            return left.getChangeDescriptorType().compareTo(right.getChangeDescriptorType());\n        }\n        if (left.getSourcePosition() != right.getSourcePosition())\n        {\n            return Integer.compare(left.getSourcePosition(), right.getSourcePosition());\n        }\n        final Optional<String> leftBeforeViewWkt = left.getBeforeViewWkt();\n        final Optional<String> rightBeforeViewWkt = right.getBeforeViewWkt();\n        if (leftBeforeViewWkt.isPresent() && rightBeforeViewWkt.isPresent()\n                && !leftBeforeViewWkt.get().equals(rightBeforeViewWkt.get()))\n        {\n            return leftBeforeViewWkt.get().compareTo(rightBeforeViewWkt.get());\n        }\n        final Optional<String> leftAfterViewWkt = left.getAfterViewWkt();\n        final Optional<String> rightAfterViewWkt = right.getAfterViewWkt();\n        if (leftAfterViewWkt.isPresent() && rightAfterViewWkt.isPresent())\n        {\n            return leftAfterViewWkt.get().compareTo(rightAfterViewWkt.get());\n        }\n        else\n        {\n            /*\n             * If this message appears in production, then that means either this comparison logic\n             * or ChangeDescriptor generation is dubious. But based on the way ChangeDescriptors are\n             * generated, it should never show up in practice.\n             */\n            throw new CoreException(\"No comparable criteria for {} vs {}\", left, right);\n        }\n    }\n\n    private int relationMemberChangeCompare(final RelationMemberChangeDescriptor left,\n            final RelationMemberChangeDescriptor right)\n    {\n        if (left.getChangeDescriptorType() != right.getChangeDescriptorType())\n        {\n            return left.getChangeDescriptorType().compareTo(right.getChangeDescriptorType());\n        }\n        if (left.getItemType() != right.getItemType())\n        {\n            return left.getItemType().compareTo(right.getItemType());\n        }\n        if (left.getIdentifier() != right.getIdentifier())\n        {\n            return Long.compare(left.getIdentifier(), right.getIdentifier());\n        }\n        return left.getRole().compareTo(right.getRole());\n    }\n\n    private int tagChangeCompare(final TagChangeDescriptor left, final TagChangeDescriptor right)\n    {\n        if (left.getChangeDescriptorType() != right.getChangeDescriptorType())\n        {\n            return left.getChangeDescriptorType().compareTo(right.getChangeDescriptorType());\n        }\n        return left.getKey().compareTo(right.getKey());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/ChangeDescriptorName.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\n/**\n * The various values of the name field in the {@link ChangeDescriptor} JSON serialization. This\n * enum mostly exists to prevent us from having to hardcode strings all over the place.\n * \n * @author lcram\n */\npublic enum ChangeDescriptorName\n{\n    TAG,\n    GEOMETRY,\n    PARENT_RELATION,\n    RELATION_MEMBER,\n    IN_EDGE,\n    OUT_EDGE,\n    START_NODE,\n    END_NODE\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/GeometricRelationGeometryChangeDescriptor.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\nimport java.util.Optional;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * @author samg\n */\npublic final class GeometricRelationGeometryChangeDescriptor implements ChangeDescriptor\n{\n\n    private static final Logger logger = LoggerFactory\n            .getLogger(GeometricRelationGeometryChangeDescriptor.class);\n    private final ChangeDescriptorType changeType;\n    private final String diff;\n\n    public static GeometricRelationGeometryChangeDescriptor getDescriptorsForGeometricRelations(\n            final Relation before, final Relation after)\n    {\n        if (before == null)\n        {\n            if (after == null)\n            {\n                return null;\n            }\n            final Optional<MultiPolygon> afterGeometry = after.asMultiPolygon();\n            if (afterGeometry.isPresent())\n            {\n                return new GeometricRelationGeometryChangeDescriptor(afterGeometry.get().toText(),\n                        ChangeDescriptorType.ADD);\n            }\n            return null;\n        }\n        else if (after == null)\n        {\n            final Optional<MultiPolygon> beforeGeometry = before.asMultiPolygon();\n            if (beforeGeometry.isPresent())\n            {\n                return new GeometricRelationGeometryChangeDescriptor(beforeGeometry.get().toText(),\n                        ChangeDescriptorType.REMOVE);\n            }\n            return null;\n        }\n        final Optional<MultiPolygon> beforeGeometry = before.asMultiPolygon();\n        final Optional<MultiPolygon> afterGeometry = after.asMultiPolygon();\n        if (beforeGeometry.isEmpty() && afterGeometry.isEmpty())\n        {\n            return null;\n        }\n        else if (beforeGeometry.isPresent() && afterGeometry.isEmpty())\n        {\n            return new GeometricRelationGeometryChangeDescriptor(beforeGeometry.get().toText(),\n                    ChangeDescriptorType.REMOVE);\n        }\n        else if (beforeGeometry.isEmpty() && afterGeometry.isPresent())\n        {\n            return new GeometricRelationGeometryChangeDescriptor(afterGeometry.get().toText(),\n                    ChangeDescriptorType.ADD);\n        }\n        try\n        {\n            if (beforeGeometry.get().equals(afterGeometry.get()))\n            {\n                return null;\n            }\n        }\n        catch (final Exception exc)\n        {\n            logger.error(\"Geometry equals failed for relation {}\", before.getIdentifier(), exc);\n        }\n\n        try\n        {\n            if (beforeGeometry.get().getArea() == afterGeometry.get().getArea())\n            {\n                return null;\n            }\n            return new GeometricRelationGeometryChangeDescriptor(\n                    Double.toString(beforeGeometry.get().getArea() - afterGeometry.get().getArea()),\n                    ChangeDescriptorType.UPDATE);\n        }\n        catch (final Exception exc)\n        {\n            logger.error(\"Geometry intersection failed for relation {}\", before.getIdentifier(),\n                    exc);\n            throw new CoreException(\"Couldn't calculate diff for relation {}\",\n                    before.getIdentifier());\n        }\n    }\n\n    private GeometricRelationGeometryChangeDescriptor(final String diff,\n            final ChangeDescriptorType changeType)\n    {\n        this.changeType = changeType;\n        this.diff = diff;\n    }\n\n    @Override\n    public ChangeDescriptorType getChangeDescriptorType()\n    {\n        return this.changeType;\n    }\n\n    @Override\n    public ChangeDescriptorName getName()\n    {\n        return ChangeDescriptorName.GEOMETRY;\n    }\n\n    @Override\n    public JsonElement toJsonElement()\n    {\n        final JsonObject descriptor = (JsonObject) ChangeDescriptor.super.toJsonElement();\n        descriptor.addProperty(\"diff\", this.diff);\n        return descriptor;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder diffString = new StringBuilder();\n        diffString.append(this.changeType.toString());\n        diffString.append(\", \");\n        switch (this.changeType)\n        {\n            case UPDATE:\n                diffString.append(\", \");\n                diffString.append(this.diff);\n                break;\n            case REMOVE:\n                diffString.append(\", \");\n                diffString.append(this.diff);\n                break;\n            case ADD:\n                diffString.append(\", \");\n                diffString.append(this.diff);\n                break;\n            default:\n                throw new CoreException(\"Unexpected ChangeType value: \" + this.changeType);\n        }\n        return getName().toString() + \"(\" + diffString.toString() + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/GeometryChangeDescriptor.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\n\nimport com.github.difflib.DiffUtils;\nimport com.github.difflib.algorithm.DiffException;\nimport com.github.difflib.patch.AbstractDelta;\nimport com.github.difflib.patch.Patch;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * A {@link ChangeDescriptor} for geometry changes. Utilizes a granular diff algorithm to show the\n * individual {@link Location}s within the linestring that actually changed.\n *\n * @author lcram\n */\npublic final class GeometryChangeDescriptor implements ChangeDescriptor\n{\n    private final ChangeDescriptorType changeType;\n    private final AbstractDelta<Location> delta;\n    private final int sourceMaterialSize;\n\n    /**\n     * Create a descriptor for a geometry change\n     *\n     * @param beforeList\n     *            The node locations of the geometry prior to the change\n     * @param afterList\n     *            The node locations of the geometry after the change\n     * @return A collection of GeometryChangeDescriptors\n     */\n    public static List<GeometryChangeDescriptor> getDescriptorsForGeometry(\n            final List<Location> beforeList, final List<Location> afterList)\n    {\n        final Patch<Location> diff;\n        try\n        {\n            diff = DiffUtils.diff(beforeList, afterList);\n        }\n        catch (final DiffException exception)\n        {\n            throw new CoreException(\"Failed to compute diff for GeometryChangeDescriptor\",\n                    exception);\n        }\n        final List<GeometryChangeDescriptor> descriptors = new ArrayList<>();\n        for (final AbstractDelta<Location> delta : diff.getDeltas())\n        {\n            descriptors.add(new GeometryChangeDescriptor(delta, beforeList.size()));\n        }\n\n        return descriptors;\n    }\n\n    public static Patch getPatch(final List<GeometryChangeDescriptor> descriptors)\n    {\n        final Patch<Location> patch = new Patch<>();\n        for (final GeometryChangeDescriptor descriptor : descriptors)\n        {\n            patch.addDelta(descriptor.getDelta());\n        }\n        return patch;\n    }\n\n    private GeometryChangeDescriptor(final AbstractDelta<Location> delta,\n            final int sourceMaterialSize)\n    {\n        switch (delta.getType())\n        {\n            case CHANGE:\n                this.changeType = ChangeDescriptorType.UPDATE;\n                break;\n            case DELETE:\n                this.changeType = ChangeDescriptorType.REMOVE;\n                break;\n            case INSERT:\n                this.changeType = ChangeDescriptorType.ADD;\n                break;\n            default:\n                throw new CoreException(\"Unexpected Delta value: \" + delta.getType());\n        }\n        this.delta = delta;\n        this.sourceMaterialSize = sourceMaterialSize;\n    }\n\n    public Optional<String> getAfterViewWkt()\n    {\n        if (this.changeType == ChangeDescriptorType.ADD\n                || this.changeType == ChangeDescriptorType.UPDATE)\n        {\n            return Optional.of(new PolyLine(this.delta.getTarget().getLines()).toWkt());\n        }\n        return Optional.empty();\n    }\n\n    public Optional<String> getBeforeViewWkt()\n    {\n        if (this.changeType == ChangeDescriptorType.REMOVE\n                || this.changeType == ChangeDescriptorType.UPDATE)\n        {\n            return Optional.of(new PolyLine(this.delta.getSource().getLines()).toWkt());\n        }\n        return Optional.empty();\n    }\n\n    @Override\n    public ChangeDescriptorType getChangeDescriptorType()\n    {\n        return this.changeType;\n    }\n\n    public AbstractDelta<Location> getDelta()\n    {\n        return this.delta;\n    }\n\n    @Override\n    public ChangeDescriptorName getName()\n    {\n        return ChangeDescriptorName.GEOMETRY;\n    }\n\n    public int getSourcePosition()\n    {\n        return this.delta.getSource().getPosition();\n    }\n\n    @Override\n    public JsonElement toJsonElement()\n    {\n        final JsonObject descriptor = (JsonObject) ChangeDescriptor.super.toJsonElement();\n        descriptor.addProperty(\"position\",\n                this.delta.getSource().getPosition() + \"/\" + this.sourceMaterialSize);\n        switch (this.changeType)\n        {\n            // Don't truncate, since some geometry changes can be very long (for example, reversing\n            // a way)\n            case UPDATE:\n                descriptor.addProperty(\"beforeView\",\n                        new PolyLine(this.delta.getSource().getLines()).toWkt());\n                descriptor.addProperty(\"afterView\",\n                        new PolyLine(this.delta.getTarget().getLines()).toWkt());\n                break;\n            case REMOVE:\n                descriptor.addProperty(\"beforeView\",\n                        new PolyLine(this.delta.getSource().getLines()).toWkt());\n                break;\n            case ADD:\n                descriptor.addProperty(\"afterView\",\n                        new PolyLine(this.delta.getTarget().getLines()).toWkt());\n                break;\n            default:\n                throw new CoreException(\"Unexpected ChangeType value: \" + this.delta.getType());\n        }\n        return descriptor;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder diffString = new StringBuilder();\n        diffString.append(this.changeType.toString());\n        diffString.append(\", \");\n        diffString.append(this.delta.getSource().getPosition());\n        diffString.append(\"/\");\n        diffString.append(this.sourceMaterialSize);\n        switch (this.changeType)\n        {\n            case UPDATE:\n                diffString.append(\", \");\n                diffString.append(new PolyLine(this.delta.getSource().getLines()).toWkt());\n                diffString.append(\" => \");\n                diffString.append(new PolyLine(this.delta.getTarget().getLines()).toWkt());\n                break;\n            case REMOVE:\n                diffString.append(\", \");\n                diffString.append(new PolyLine(this.delta.getSource().getLines()).toWkt());\n                break;\n            case ADD:\n                diffString.append(\", \");\n                diffString.append(new PolyLine(this.delta.getTarget().getLines()).toWkt());\n                break;\n            default:\n                throw new CoreException(\"Unexpected ChangeType value: \" + this.delta.getType());\n        }\n        return getName().toString() + \"(\" + diffString.toString() + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/LongElementChangeDescriptor.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * A {@link ChangeDescriptor} for an element of type {@link Long}. E.g. cases include\n * startNodeIdentifiers, inEdgeIdentifiers, parentRelations, etc.\n * \n * @author lcram\n */\npublic class LongElementChangeDescriptor implements ChangeDescriptor\n{\n    private final ChangeDescriptorType changeType;\n    private final Long beforeElement;\n    private final Long afterElement;\n    private final ChangeDescriptorName name;\n\n    public LongElementChangeDescriptor(final ChangeDescriptorType changeType,\n            final Long beforeElement, final Long afterElement, final ChangeDescriptorName name)\n    {\n        this.changeType = changeType;\n        this.beforeElement = beforeElement;\n        this.afterElement = afterElement;\n        this.name = name;\n    }\n\n    public LongElementChangeDescriptor(final ChangeDescriptorType changeType,\n            final Long afterElement, final ChangeDescriptorName name)\n    {\n        this.changeType = changeType;\n        this.beforeElement = null;\n        this.afterElement = afterElement;\n        this.name = name;\n    }\n\n    public Long getAfterElement()\n    {\n        return this.afterElement;\n    }\n\n    public Long getBeforeElement()\n    {\n        return this.beforeElement;\n    }\n\n    @Override\n    public ChangeDescriptorType getChangeDescriptorType()\n    {\n        return this.changeType;\n    }\n\n    @Override\n    public ChangeDescriptorName getName()\n    {\n        return this.name;\n    }\n\n    @Override\n    public JsonElement toJsonElement()\n    {\n        final JsonObject descriptor = (JsonObject) ChangeDescriptor.super.toJsonElement();\n        if (this.beforeElement != null)\n        {\n            descriptor.addProperty(\"beforeElement\", this.beforeElement.toString());\n        }\n        if (this.afterElement != null)\n        {\n            descriptor.addProperty(\"afterElement\", this.afterElement.toString());\n        }\n        return descriptor;\n    }\n\n    @Override\n    public String toString()\n    {\n        if (this.changeType == ChangeDescriptorType.UPDATE)\n        {\n            return this.name + \"(\" + this.getChangeDescriptorType() + \", \" + this.getBeforeElement()\n                    + \" => \" + this.getAfterElement() + \")\";\n        }\n        if (this.changeType == ChangeDescriptorType.REMOVE)\n        {\n            return this.name + \"(\" + this.getChangeDescriptorType() + \", \" + this.getBeforeElement()\n                    + \")\";\n        }\n        return this.name + \"(\" + this.getChangeDescriptorType() + \", \" + this.getAfterElement()\n                + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/RelationMemberChangeDescriptor.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * A {@link ChangeDescriptor} for relation member changes.\n * \n * @author lcram\n */\npublic class RelationMemberChangeDescriptor implements ChangeDescriptor\n{\n    private final ChangeDescriptorType changeType;\n    private final long identifier;\n    private final ItemType type;\n    private final String role;\n\n    public RelationMemberChangeDescriptor(final ChangeDescriptorType changeType,\n            final long identifier, final ItemType type, final String role)\n    {\n        this.changeType = changeType;\n        this.identifier = identifier;\n        this.type = type;\n        this.role = role;\n    }\n\n    @Override\n    public ChangeDescriptorType getChangeDescriptorType()\n    {\n        return this.changeType;\n    }\n\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public ItemType getItemType()\n    {\n        return this.type;\n    }\n\n    @Override\n    public ChangeDescriptorName getName()\n    {\n        return ChangeDescriptorName.RELATION_MEMBER;\n    }\n\n    public String getRole()\n    {\n        return this.role;\n    }\n\n    @Override\n    public JsonElement toJsonElement()\n    {\n        final JsonObject descriptor = (JsonObject) ChangeDescriptor.super.toJsonElement();\n        descriptor.addProperty(\"itemType\", this.type.toString());\n        descriptor.addProperty(\"id\", this.identifier);\n        descriptor.addProperty(\"role\", this.role);\n        return descriptor;\n    }\n\n    @Override\n    public String toString()\n    {\n        return getName().toString() + \"(\" + this.changeType + \", \" + this.type + \", \"\n                + this.identifier + \", \" + this.role + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/TagChangeDescriptor.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * A {@link ChangeDescriptor} for tag related changes.\n * \n * @author lcram\n */\npublic class TagChangeDescriptor implements ChangeDescriptor\n{\n    private final ChangeDescriptorType changeType;\n    private final String key;\n    private final String value;\n    private final String originalValue;\n\n    public TagChangeDescriptor(final ChangeDescriptorType changeType, final String key,\n            final String value, final String originalValue)\n    {\n        this.changeType = changeType;\n        this.key = key;\n        this.value = value;\n        this.originalValue = originalValue;\n    }\n\n    public TagChangeDescriptor(final ChangeDescriptorType changeType, final String key,\n            final String value)\n    {\n        this(changeType, key, value, null);\n    }\n\n    @Override\n    public ChangeDescriptorType getChangeDescriptorType()\n    {\n        return this.changeType;\n    }\n\n    public String getKey()\n    {\n        return this.key;\n    }\n\n    @Override\n    public ChangeDescriptorName getName()\n    {\n        return ChangeDescriptorName.TAG;\n    }\n\n    public Optional<String> getOriginalValue()\n    {\n        return Optional.ofNullable(this.originalValue);\n    }\n\n    public String getValue()\n    {\n        return this.value;\n    }\n\n    @Override\n    public JsonElement toJsonElement()\n    {\n        final JsonObject descriptor = (JsonObject) ChangeDescriptor.super.toJsonElement();\n        descriptor.addProperty(\"key\", this.key);\n        descriptor.addProperty(\"value\", this.value);\n        if (this.originalValue != null)\n        {\n            descriptor.addProperty(\"originalValue\", this.originalValue);\n        }\n        return descriptor;\n    }\n\n    @Override\n    public String toString()\n    {\n        String string = getName().toString() + \"(\" + this.changeType + \", \" + this.key + \", \";\n        if (this.originalValue == null)\n        {\n            string += this.value + \")\";\n        }\n        else\n        {\n            string += this.originalValue + \" => \" + this.value + \")\";\n        }\n\n        return string;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/diff/AtlasDiff.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.diff;\n\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.BareAtlas;\nimport org.openstreetmap.atlas.geography.atlas.change.Change;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeAtlas;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeBuilder;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeType;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Generate {@link Change} objects based on the differences between two {@link Atlas}es. The main\n * usage of this class is the {@link AtlasDiff#generateChange()} method.<br>\n * <br>\n *\n * @author lcram\n */\npublic class AtlasDiff\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDiff.class);\n\n    private final Atlas before;\n    private final Atlas after;\n    private Change change;\n\n    /*\n     * By default, AtlasDiff will save geometries of all changed features, even if that feature's\n     * geometry was not changed. This makes it really easy to visualize changed features using\n     * GeoJSON (e.g. if a Node tag changes, we still want to be able to see it in the GeoJSON\n     * serialized Change object). This behaviour can be disabled.\n     */\n    private boolean saveAllGeometries = true;\n\n    /**\n     * Construct an {@link AtlasDiff} with a given before {@link Atlas} and after {@link Atlas}. The\n     * resulting {@link Change} will effectively transform the before atlas into the after atlas.\n     *\n     * @param before\n     *            the initial {@link Atlas}\n     * @param after\n     *            the changed {@link Atlas}\n     */\n    public AtlasDiff(final Atlas before, final Atlas after)\n    {\n        this.before = before;\n        this.after = after;\n        this.change = null;\n\n        if (this.before == null)\n        {\n            throw new CoreException(\"Before atlas cannot be null.\");\n        }\n\n        if (this.after == null)\n        {\n            throw new CoreException(\"After atlas cannot be null.\");\n        }\n    }\n\n    /**\n     * Generate a {@link Change} that represents a transformation from the before {@link Atlas} to\n     * the after {@link Atlas}. If there were no changes (i.e. the atlases had no differences), then\n     * return an empty {@link Optional}. Repeated calls to this method will return a cached\n     * {@link Change} instead of performing a re-computation.<br>\n     * <br>\n     * Now suppose we create a {@link ChangeAtlas} based on the before {@link Atlas} and the\n     * generated {@link Change}. All of the following will be true:<br>\n     * <br>\n     * 1) Computing the {@link AtlasDiff} of the {@link ChangeAtlas} with the after {@link Atlas}\n     * would yield an empty {@link Change}.<br>\n     * <br>\n     * 2) The {@link ChangeAtlas} will be equivalent to the after {@link Atlas},\n     * feature-for-feature.<br>\n     * <br>\n     * 3) The {@link PackedAtlas}es created by cloning both the {@link ChangeAtlas} and the after\n     * {@link Atlas} will be equivalent under {@link BareAtlas#equals(Object)}, but may not\n     * necessarily be byte-for-byte equivalent.<br>\n     * <br>\n     *\n     * @return an {@link Optional} wrapping the generated {@link Change}\n     */\n    public Optional<Change> generateChange()\n    {\n        final Time start = Time.now();\n        if (this.change != null)\n        {\n            return Optional.of(this.change);\n        }\n\n        final Set<AtlasEntity> addedEntities = new HashSet<>();\n        final Set<AtlasEntity> removedEntities = new HashSet<>();\n        final Set<AtlasEntity> potentiallyModifiedEntities = new HashSet<>();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n\n        /*\n         * Check for entities that were removed in the after atlas. If we find any, add them to a\n         * removedEntities set for later processing. We will use this set to create FeatureChanges.\n         * We also check for entities that were potentially modified in the after atlas, i.e. any\n         * entities present in both the before and after atlases. We add them to a\n         * potentiallyModified set, and will check if any modifications occurred later.\n         */\n        Iterables.stream(this.before).forEach(beforeEntity ->\n        {\n            if (isEntityMissingFromGivenAtlas(beforeEntity, this.after))\n            {\n                removedEntities.add(beforeEntity);\n            }\n            else\n            {\n                potentiallyModifiedEntities.add(beforeEntity);\n            }\n        });\n\n        /*\n         * Check for entities that were added in the after atlas. If we find any, add them to a\n         * addedEntities set for later processing. We will use this set to create FeatureChanges.\n         */\n        Iterables.stream(this.after)\n                .filter(afterEntity -> isEntityMissingFromGivenAtlas(afterEntity, this.before))\n                .forEach(addedEntities::add);\n\n        /*\n         * Aggregate the results stored in the sets, creating the FeatureChange objects if there are\n         * necessary changes.\n         */\n        createFeatureChangesBasedOnEntitySets(addedEntities, removedEntities,\n                potentiallyModifiedEntities, this.before, this.after, this.saveAllGeometries)\n                .stream().forEach(changeBuilder::add);\n\n        if (changeBuilder.peekNumberOfChanges() == 0)\n        {\n            logger.debug(\"Computed AtlasDiff ({} vs {}) in {}\", this.before.getName(),\n                    this.after.getName(), start.elapsedSince());\n            return Optional.empty();\n        }\n        this.change = changeBuilder.get();\n\n        logger.debug(\"Computed AtlasDiff ({} vs {}) in {}\", this.before.getName(),\n                this.after.getName(), start.elapsedSince());\n\n        return Optional.of(this.change);\n    }\n\n    public Atlas getAfterAtlas()\n    {\n        return this.after;\n    }\n\n    public Atlas getBeforeAtlas()\n    {\n        return this.before;\n    }\n\n    /**\n     * Saving all geometries means that we enrich features with their geometry even if they do not\n     * change the geometry. This is useful for visualization.\n     *\n     * @param saveAllGeometries\n     *            save all geometries\n     * @return a configured {@link AtlasDiff}\n     */\n    public AtlasDiff saveAllGeometries(final boolean saveAllGeometries)\n    {\n        this.saveAllGeometries = saveAllGeometries;\n        return this;\n    }\n\n    /**\n     * Given a set of added, removed, and potentially modified entities, construct a set of\n     * {@link FeatureChange}s that transform a given before atlas into a given after atlas.\n     *\n     * @param addedEntities\n     *            the set of entities added in the after atlas\n     * @param removedEntities\n     *            the set of entities removed in the after atlas\n     * @param potentiallyModifiedEntities\n     *            the set of entities potentially modified in the after atlas (i.e. any entity, with\n     *            the same ID, found in both before and after)\n     * @param beforeAtlas\n     *            the before atlas\n     * @param afterAtlas\n     *            the after atlas\n     * @param saveAllGeometries\n     *            if we are saving all geometries, even when not modified\n     * @return the set of {@link FeatureChange}s that would turn before into after\n     */\n    private Set<FeatureChange> createFeatureChangesBasedOnEntitySets(\n            final Set<AtlasEntity> addedEntities, final Set<AtlasEntity> removedEntities,\n            final Set<AtlasEntity> potentiallyModifiedEntities, final Atlas beforeAtlas,\n            final Atlas afterAtlas, final boolean saveAllGeometries)\n    {\n        final Set<FeatureChange> featureChanges = new HashSet<>();\n\n        addedEntities\n                .stream().map(addedEntity -> createSimpleFeatureChangeWithType(ChangeType.ADD,\n                        addedEntity, afterAtlas, beforeAtlas, saveAllGeometries))\n                .forEach(featureChanges::add);\n\n        removedEntities.stream()\n                .map(removedEntity -> createSimpleFeatureChangeWithType(ChangeType.REMOVE,\n                        removedEntity, beforeAtlas, beforeAtlas, saveAllGeometries))\n                .forEach(featureChanges::add);\n\n        potentiallyModifiedEntities.stream()\n                .map(modifiedEntity -> createModifyFeatureChanges(modifiedEntity, beforeAtlas,\n                        saveAllGeometries))\n                .forEach(modifyFeatureChangeSet -> modifyFeatureChangeSet\n                        .forEach(featureChanges::add));\n\n        return featureChanges;\n    }\n\n    /**\n     * Create a set of modify {@link FeatureChange}s ie. ADDs that change an existing feature. The\n     * set will contain an individual {@link FeatureChange} for certain types of differences. Eg. if\n     * a Point's location AND tags changed, then the set will contain two feature changes, one for\n     * the location and one for the tags. However, if a Node's location and connected edges change,\n     * this will come in as one feature change.<br>\n     *\n     * @param entity\n     *            the entity being modified\n     * @param beforeViewAtlas\n     *            the atlas from which to compute the beforeView\n     * @param saveAllGeometries\n     *            whether or not we are default saving all geometry\n     * @return a {@link Set} containing the possibly constructed {@link FeatureChange}s\n     */\n    private Set<FeatureChange> createModifyFeatureChanges(final AtlasEntity entity,\n            final Atlas beforeViewAtlas, final boolean saveAllGeometries)\n    {\n        final Set<FeatureChange> featureChanges = new HashSet<>();\n\n        final AtlasEntity beforeEntity = entity.getType().entityForIdentifier(this.before,\n                entity.getIdentifier());\n        final AtlasEntity afterEntity = entity.getType().entityForIdentifier(this.after,\n                entity.getIdentifier());\n\n        if (beforeEntity == null || afterEntity == null)\n        {\n            throw new CoreException(\"Unexpected null entity. This should never happen.\");\n        }\n\n        /*\n         * Determine if the entities are actually different in any relevant way. If so, then we can\n         * decide how to create the feature change.\n         */\n\n        /*\n         * Detect changed tags.\n         */\n        AtlasDiffHelper.getTagChangeIfNecessary(beforeEntity, afterEntity, beforeViewAtlas,\n                saveAllGeometries).ifPresent(featureChanges::add);\n\n        /*\n         * Detect if the entity changed its parent relation membership.\n         */\n        AtlasDiffHelper.getParentRelationMembershipChangeIfNecessary(beforeEntity, afterEntity,\n                beforeViewAtlas, saveAllGeometries).ifPresent(featureChanges::add);\n\n        /*\n         * Detect if the entities were Nodes and some Node properties changed. We check for Node\n         * locations, as well as inEdges and outEdges.\n         */\n        if (entity instanceof Node)\n        {\n            AtlasDiffHelper.getNodeChangeIfNecessary((Node) beforeEntity, (Node) afterEntity,\n                    beforeViewAtlas, saveAllGeometries).ifPresent(featureChanges::add);\n        }\n        /*\n         * Detect if the entities were Edges and some Edge properties changed. We check for Edge\n         * polylines, as well as the start and end Node.\n         */\n        else if (entity instanceof Edge)\n        {\n            AtlasDiffHelper.getEdgeChangeIfNecessary((Edge) beforeEntity, (Edge) afterEntity,\n                    beforeViewAtlas, saveAllGeometries).ifPresent(featureChanges::add);\n        }\n        /*\n         * Detect if the entities were Points and some Point properties changed. We just check the\n         * location.\n         */\n        else if (entity instanceof Point)\n        {\n            AtlasDiffHelper.getPointChangeIfNecessary((Point) beforeEntity, (Point) afterEntity,\n                    beforeViewAtlas).ifPresent(featureChanges::add);\n        }\n        /*\n         * Detect if the entities were Lines and some Line properties changed. We just check the\n         * polyline.\n         */\n        else if (entity instanceof Line)\n        {\n            AtlasDiffHelper.getLineChangeIfNecessary((Line) beforeEntity, (Line) afterEntity,\n                    beforeViewAtlas).ifPresent(featureChanges::add);\n        }\n        /*\n         * Detect if the entities were Areas and some Area properties changed. We just check the\n         * polygon.\n         */\n        else if (entity instanceof Area)\n        {\n            AtlasDiffHelper.getAreaChangeIfNecessary((Area) beforeEntity, (Area) afterEntity,\n                    beforeViewAtlas).ifPresent(featureChanges::add);\n        }\n        /*\n         * Detect if the entities were Relations and some Relation properties changed. We check the\n         * member lists for changes (changes include a removed or added member or a member role\n         * change). Note that the relation diff checking is not deep, i.e. it only looks at the\n         * relation data itself (member ID, role, and type). For example, if a contained Area's\n         * geometry or tags change, this will not generate a relation change for relations\n         * containing that Area.\n         */\n        else if (entity instanceof Relation)\n        {\n            AtlasDiffHelper.getRelationChangeIfNecessary((Relation) beforeEntity,\n                    (Relation) afterEntity, beforeViewAtlas).ifPresent(featureChanges::add);\n        }\n\n        return featureChanges;\n    }\n\n    /**\n     * Create a simple {@link FeatureChange} ie. a REMOVE, or an ADD that is adding a new feature\n     * (as opposed to modifying an existing feature). The feature change will be created from the\n     * given entity in the given atlas.\n     *\n     * @param changeType\n     *            the change type to use, e.g. ADD or REMOVE\n     * @param entity\n     *            the entity to add or remove\n     * @param atlasContainingTheEntity\n     *            the atlas that actually contains this entity. In the ADD case, this should be the\n     *            afterAtlas, since we are adding a brand new feature not found in the beforeAtlas.\n     *            In the REMOVE case, this should be the beforeAtlas, since we are removing a\n     *            feature that only exists in the beforeAtlas.\n     * @param beforeViewAtlas\n     *            the atlas from which to compute the {@link FeatureChange} beforeView. This should\n     *            always be the beforeAtlas. In the ADD case, the beforeView will be null since this\n     *            is a brand new feature. In the REMOVE case, the beforeView will be fully\n     *            populated, since we are wiping the entity.\n     * @param saveAllGeometries\n     *            if we are saving all geometries, even when they are not modified\n     * @return the feature change\n     */\n    private FeatureChange createSimpleFeatureChangeWithType(final ChangeType changeType,\n            final AtlasEntity entity, final Atlas atlasContainingTheEntity,\n            final Atlas beforeViewAtlas, final boolean saveAllGeometries)\n    {\n        final FeatureChange featureChange;\n        switch (entity.getType())\n        {\n            case NODE:\n                featureChange = AtlasDiffHelper.simpleCompleteNodeChange(changeType,\n                        atlasContainingTheEntity, beforeViewAtlas, entity, saveAllGeometries);\n                break;\n            case EDGE:\n                featureChange = AtlasDiffHelper.simpleCompleteEdgeChange(changeType,\n                        atlasContainingTheEntity, beforeViewAtlas, entity, saveAllGeometries);\n                break;\n            case POINT:\n                featureChange = AtlasDiffHelper.simpleCompletePointChange(changeType,\n                        atlasContainingTheEntity, beforeViewAtlas, entity, saveAllGeometries);\n                break;\n            case LINE:\n                featureChange = AtlasDiffHelper.simpleCompleteLineChange(changeType,\n                        atlasContainingTheEntity, beforeViewAtlas, entity, saveAllGeometries);\n                break;\n            case AREA:\n                featureChange = AtlasDiffHelper.simpleCompleteAreaChange(changeType,\n                        atlasContainingTheEntity, beforeViewAtlas, entity, saveAllGeometries);\n                break;\n            case RELATION:\n                featureChange = AtlasDiffHelper.simpleCompleteRelationChange(changeType,\n                        atlasContainingTheEntity, beforeViewAtlas, entity);\n                break;\n            default:\n                throw new CoreException(\"Unknown item type {}\", entity.getType());\n        }\n        return featureChange;\n    }\n\n    /**\n     * Check if a given entity is missing from a given atlas. Optionally, we can match using the\n     * underlying geometry if the itemType/identifier check fails.\n     *\n     * @param entity\n     *            the entity to check for\n     * @param atlasToCheck\n     *            the atlas to check\n     * @return if the entity was missing from the atlas\n     */\n    private boolean isEntityMissingFromGivenAtlas(final AtlasEntity entity,\n            final Atlas atlasToCheck)\n    {\n        /*\n         * Look up the given entity's ID in the atlasToCheck. If the returned entity is null, we\n         * know it was NOT PRESENT in the atlasToCheck.\n         */\n        return entity.getType().entityForIdentifier(atlasToCheck, entity.getIdentifier()) == null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/diff/AtlasDiffHelper.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.diff;\n\nimport java.util.Iterator;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeType;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPrecisionManager;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A helper class for {@link AtlasDiff}. Contains lots of static utilities.\n *\n * @author lcram\n */\npublic final class AtlasDiffHelper\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDiffHelper.class);\n\n    public static Optional<FeatureChange> getAreaChangeIfNecessary(final Area beforeArea,\n            final Area afterArea, final Atlas beforeViewAtlas)\n    {\n        try\n        {\n            boolean featureChangeWouldBeUseful = false;\n            final CompleteArea completeArea = CompleteArea.shallowFrom(afterArea);\n            if (!beforeArea.asPolygon().equals(afterArea.asPolygon()))\n            {\n                completeArea.withPolygon(afterArea.asPolygon());\n                featureChangeWouldBeUseful = true;\n            }\n            if (featureChangeWouldBeUseful)\n            {\n                return Optional.of(FeatureChange.add(completeArea, beforeViewAtlas));\n            }\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to compare areas {} and {}\", beforeArea, afterArea,\n                    exception);\n        }\n        return Optional.empty();\n    }\n\n    public static Optional<FeatureChange> getEdgeChangeIfNecessary(final Edge beforeEdge,\n            final Edge afterEdge, final Atlas beforeViewAtlas, final boolean saveAllGeometries)\n    {\n        try\n        {\n            boolean featureChangeWouldBeUseful = false;\n            final CompleteEdge completeEdge = CompleteEdge.shallowFrom(afterEdge);\n            if (!beforeEdge.asPolyLine().equals(afterEdge.asPolyLine()))\n            {\n                completeEdge.withPolyLine(afterEdge.asPolyLine());\n                featureChangeWouldBeUseful = true;\n            }\n            if (beforeEdge.start().getIdentifier() != afterEdge.start().getIdentifier())\n            {\n                completeEdge.withStartNodeIdentifier(afterEdge.start().getIdentifier());\n                featureChangeWouldBeUseful = true;\n            }\n            if (beforeEdge.end().getIdentifier() != afterEdge.end().getIdentifier())\n            {\n                completeEdge.withEndNodeIdentifier(afterEdge.end().getIdentifier());\n                featureChangeWouldBeUseful = true;\n            }\n            if (featureChangeWouldBeUseful)\n            {\n                /*\n                 * Explicitly check for saveAllGeometry. This will resave if the featureChange was\n                 * due to a geometry change, but that is OK. We want to ensure we save in the cases\n                 * where a start/end node was changed (for visualization purposes).\n                 */\n                if (saveAllGeometries)\n                {\n                    completeEdge.withPolyLine(afterEdge.asPolyLine());\n                }\n                return Optional.of(FeatureChange.add(completeEdge, beforeViewAtlas));\n            }\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to compare edges {} and {}\", beforeEdge, afterEdge,\n                    exception);\n        }\n        return Optional.empty();\n    }\n\n    public static Optional<FeatureChange> getLineChangeIfNecessary(final Line beforeLine,\n            final Line afterLine, final Atlas beforeViewAtlas)\n    {\n        try\n        {\n            boolean featureChangeWouldBeUseful = false;\n            final CompleteLine completeLine = CompleteLine.shallowFrom(afterLine);\n            if (!beforeLine.asPolyLine().equals(afterLine.asPolyLine()))\n            {\n                completeLine.withPolyLine(afterLine.asPolyLine());\n                featureChangeWouldBeUseful = true;\n            }\n            if (featureChangeWouldBeUseful)\n            {\n                return Optional.of(FeatureChange.add(completeLine, beforeViewAtlas));\n            }\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to compare lines {} and {}\", beforeLine, afterLine,\n                    exception);\n        }\n        return Optional.empty();\n    }\n\n    public static Optional<FeatureChange> getNodeChangeIfNecessary(final Node beforeNode,\n            final Node afterNode, final Atlas beforeViewAtlas, final boolean saveAllGeometries)\n    {\n        try\n        {\n            boolean featureChangeWouldBeUseful = false;\n            final CompleteNode completeNode = CompleteNode.shallowFrom(afterNode);\n            if (!beforeNode.getLocation().equals(afterNode.getLocation()))\n            {\n                completeNode.withLocation(afterNode.getLocation());\n                featureChangeWouldBeUseful = true;\n            }\n            if (differentEdgeSet(beforeNode.inEdges(), afterNode.inEdges()))\n            {\n                completeNode.withInEdgeIdentifiersAndSource(new TreeSet<>(afterNode.inEdges()\n                        .stream().map(Edge::getIdentifier).collect(Collectors.toSet())),\n                        beforeNode);\n                if (saveAllGeometries)\n                {\n                    completeNode.withLocation(afterNode.getLocation());\n                }\n                featureChangeWouldBeUseful = true;\n            }\n            if (differentEdgeSet(beforeNode.outEdges(), afterNode.outEdges()))\n            {\n                completeNode.withOutEdgeIdentifiersAndSource(new TreeSet<>(afterNode.outEdges()\n                        .stream().map(Edge::getIdentifier).collect(Collectors.toSet())),\n                        beforeNode);\n                if (saveAllGeometries)\n                {\n                    completeNode.withLocation(afterNode.getLocation());\n                }\n                featureChangeWouldBeUseful = true;\n            }\n            if (featureChangeWouldBeUseful)\n            {\n                return Optional.of(FeatureChange.add(completeNode, beforeViewAtlas));\n            }\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to compare nodes {} and {}\", beforeNode, afterNode,\n                    exception);\n        }\n        return Optional.empty();\n    }\n\n    public static Optional<FeatureChange> getParentRelationMembershipChangeIfNecessary(\n            final AtlasEntity beforeEntity, final AtlasEntity afterEntity,\n            final Atlas beforeViewAtlas, final boolean saveAllGeometries)\n    {\n        try\n        {\n            final Set<Long> beforeRelationIdentifiers = beforeEntity.relations().stream()\n                    .map(Relation::getIdentifier).collect(Collectors.toSet());\n            final Set<Long> afterRelationIdentifiers = afterEntity.relations().stream()\n                    .map(Relation::getIdentifier).collect(Collectors.toSet());\n\n            /*\n             * We never had any parent relations. We want to explicitly return empty so as not to\n             * generate a misleading FeatureChange changing a null into an empty set.\n             */\n            if (beforeRelationIdentifiers.isEmpty() && afterRelationIdentifiers.isEmpty())\n            {\n                return Optional.empty();\n            }\n\n            /*\n             * If the relation identifier sets are equivalent, then there was no parent relation\n             * membership change.\n             */\n            if (beforeRelationIdentifiers.equals(afterRelationIdentifiers))\n            {\n                return Optional.empty();\n            }\n\n            /*\n             * OK! We made it here because we have confirmed that the entities have differing\n             * declared parent relation identifiers. We create a feature change to reflect this.\n             */\n            final AtlasEntity completeEntity;\n            switch (afterEntity.getType())\n            {\n                case AREA:\n                    CompleteArea area = CompleteArea.shallowFrom((Area) afterEntity)\n                            .withRelationIdentifiers(afterRelationIdentifiers);\n                    if (saveAllGeometries)\n                    {\n                        area = area.withPolygon(((Area) afterEntity).asPolygon());\n                    }\n                    completeEntity = area;\n                    break;\n                case EDGE:\n                    CompleteEdge edge = CompleteEdge.shallowFrom((Edge) afterEntity)\n                            .withRelationIdentifiers(afterRelationIdentifiers);\n                    if (saveAllGeometries)\n                    {\n                        edge = edge.withPolyLine(((Edge) afterEntity).asPolyLine());\n                    }\n                    completeEntity = edge;\n                    break;\n                case LINE:\n                    CompleteLine line = CompleteLine.shallowFrom((Line) afterEntity)\n                            .withRelationIdentifiers(afterRelationIdentifiers);\n                    if (saveAllGeometries)\n                    {\n                        line = line.withPolyLine(((Line) afterEntity).asPolyLine());\n                    }\n                    completeEntity = line;\n                    break;\n                case NODE:\n                    CompleteNode node = CompleteNode.shallowFrom((Node) afterEntity)\n                            .withRelationIdentifiers(afterRelationIdentifiers);\n                    if (saveAllGeometries)\n                    {\n                        node = node.withLocation(((Node) afterEntity).getLocation());\n                    }\n                    completeEntity = node;\n                    break;\n                case POINT:\n                    CompletePoint point = CompletePoint.shallowFrom((Point) afterEntity)\n                            .withRelationIdentifiers(afterRelationIdentifiers);\n                    if (saveAllGeometries)\n                    {\n                        point = point.withLocation(((Point) afterEntity).getLocation());\n                    }\n                    completeEntity = point;\n                    break;\n                case RELATION:\n                    final CompleteRelation relation = CompleteRelation\n                            .shallowFrom((Relation) afterEntity)\n                            .withRelationIdentifiers(afterRelationIdentifiers);\n                    completeEntity = relation;\n                    break;\n                default:\n                    throw new CoreException(\"Unknown item type {}\", afterEntity.getType());\n            }\n            // featureChange should never be null\n            return Optional.of(FeatureChange.add(completeEntity, beforeViewAtlas));\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to compare relations for {} and {}\", beforeEntity,\n                    afterEntity, exception);\n        }\n    }\n\n    public static Optional<FeatureChange> getPointChangeIfNecessary(final Point beforePoint,\n            final Point afterPoint, final Atlas beforeViewAtlas)\n    {\n        try\n        {\n            boolean featureChangeWouldBeUseful = false;\n            final CompletePoint completePoint = CompletePoint.shallowFrom(afterPoint);\n            if (!beforePoint.getLocation().equals(afterPoint.getLocation()))\n            {\n                completePoint.withLocation(afterPoint.getLocation());\n                featureChangeWouldBeUseful = true;\n            }\n            if (featureChangeWouldBeUseful)\n            {\n                return Optional.of(FeatureChange.add(completePoint, beforeViewAtlas));\n            }\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to compare points {} and {}\", beforePoint, afterPoint,\n                    exception);\n        }\n        return Optional.empty();\n    }\n\n    public static Optional<FeatureChange> getRelationChangeIfNecessary(\n            final Relation beforeRelation, final Relation afterRelation,\n            final Atlas beforeViewAtlas)\n    {\n        try\n        {\n            boolean featureChangeWouldBeUseful = false;\n            final CompleteRelation completeRelation = CompleteRelation.shallowFrom(afterRelation);\n            final RelationMemberList beforeMembers = beforeRelation.members();\n            final RelationMemberList afterMembers = afterRelation.members();\n            if (!afterMembers.equals(beforeMembers))\n            {\n                completeRelation.withMembersAndSource(afterRelation.members(), beforeRelation);\n                featureChangeWouldBeUseful = true;\n            }\n\n            if (beforeRelation.isGeometric())\n            {\n                final Optional<MultiPolygon> afterGeom = afterRelation.asMultiPolygon();\n                if (afterGeom.isPresent())\n                {\n                    final Optional<MultiPolygon> beforeGeom = beforeRelation.asMultiPolygon();\n                    if (beforeGeom.isPresent() && beforeGeom.get().equals(afterGeom.get()))\n                    {\n                        // nothing to see here, move along!\n                    }\n                    else\n                    {\n                        completeRelation.withMultiPolygonGeometry(afterGeom.get());\n                        featureChangeWouldBeUseful = true;\n                    }\n                }\n                else if (beforeRelation.asMultiPolygon().isPresent())\n                {\n                    completeRelation.withMultiPolygonGeometry(\n                            JtsPrecisionManager.getGeometryFactory().createMultiPolygon());\n                    featureChangeWouldBeUseful = true;\n                }\n            }\n\n            if (featureChangeWouldBeUseful)\n            {\n                return Optional.of(FeatureChange.add(completeRelation, beforeViewAtlas));\n            }\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to compare relations {} and {}\", beforeRelation,\n                    afterRelation, exception);\n        }\n        return Optional.empty();\n    }\n\n    public static Optional<FeatureChange> getTagChangeIfNecessary(final AtlasEntity beforeEntity,\n            final AtlasEntity afterEntity, final Atlas beforeViewAtlas,\n            final boolean saveAllGeometries)\n    {\n        if (beforeEntity.getTags().equals(afterEntity.getTags()))\n        {\n            return Optional.empty();\n        }\n\n        final AtlasEntity completeEntity;\n        switch (afterEntity.getType())\n        {\n            case AREA:\n                CompleteArea area = CompleteArea.shallowFrom((Area) afterEntity)\n                        .withTags(afterEntity.getTags());\n                if (saveAllGeometries)\n                {\n                    area = area.withPolygon(((Area) afterEntity).asPolygon());\n                }\n                completeEntity = area;\n                break;\n            case EDGE:\n                CompleteEdge edge = CompleteEdge.shallowFrom((Edge) afterEntity)\n                        .withTags(afterEntity.getTags());\n                if (saveAllGeometries)\n                {\n                    edge = edge.withPolyLine(((Edge) afterEntity).asPolyLine());\n                }\n                completeEntity = edge;\n                break;\n            case LINE:\n                CompleteLine line = CompleteLine.shallowFrom((Line) afterEntity)\n                        .withTags(afterEntity.getTags());\n                if (saveAllGeometries)\n                {\n                    line = line.withPolyLine(((Line) afterEntity).asPolyLine());\n                }\n                completeEntity = line;\n                break;\n            case NODE:\n                CompleteNode node = CompleteNode.shallowFrom((Node) afterEntity)\n                        .withTags(afterEntity.getTags());\n                if (saveAllGeometries)\n                {\n                    node = node.withLocation(((Node) afterEntity).getLocation());\n                }\n                completeEntity = node;\n                break;\n            case POINT:\n                CompletePoint point = CompletePoint.shallowFrom((Point) afterEntity)\n                        .withTags(afterEntity.getTags());\n                if (saveAllGeometries)\n                {\n                    point = point.withLocation(((Point) afterEntity).getLocation());\n                }\n                completeEntity = point;\n                break;\n            case RELATION:\n                final CompleteRelation relation = CompleteRelation\n                        .shallowFrom((Relation) afterEntity).withTags(afterEntity.getTags());\n                completeEntity = relation;\n                break;\n            default:\n                throw new CoreException(\"Unknown item type {}\", afterEntity.getType());\n        }\n        return Optional.of(FeatureChange.add(completeEntity, beforeViewAtlas));\n    }\n\n    public static FeatureChange simpleCompleteAreaChange(final ChangeType changeType,\n            final Atlas atlasContainingTheEntity, final Atlas beforeViewAtlas,\n            final AtlasEntity entity, final boolean saveAllGeometries)\n    {\n        final Long entityIdentifier = entity.getIdentifier();\n        if (changeType == ChangeType.REMOVE)\n        {\n            CompleteArea completeArea = CompleteArea\n                    .shallowFrom(atlasContainingTheEntity.area(entityIdentifier));\n            if (saveAllGeometries)\n            {\n                completeArea = completeArea.withPolygon(((Area) entity).asPolygon());\n            }\n            return FeatureChange.remove(completeArea, beforeViewAtlas);\n        }\n        else\n        {\n            return FeatureChange.add(\n                    CompleteArea.from(atlasContainingTheEntity.area(entityIdentifier)),\n                    beforeViewAtlas);\n        }\n    }\n\n    public static FeatureChange simpleCompleteEdgeChange(final ChangeType changeType,\n            final Atlas atlasContainingTheEntity, final Atlas beforeViewAtlas,\n            final AtlasEntity entity, final boolean saveAllGeometries)\n    {\n        final Long entityIdentifier = entity.getIdentifier();\n        if (changeType == ChangeType.REMOVE)\n        {\n            CompleteEdge completeEdge = CompleteEdge\n                    .shallowFrom(atlasContainingTheEntity.edge(entityIdentifier));\n            if (saveAllGeometries)\n            {\n                completeEdge = completeEdge.withPolyLine(((Edge) entity).asPolyLine());\n            }\n            return FeatureChange.remove(completeEdge, beforeViewAtlas);\n        }\n        else\n        {\n            return FeatureChange.add(\n                    CompleteEdge.from(atlasContainingTheEntity.edge(entityIdentifier)),\n                    beforeViewAtlas);\n        }\n    }\n\n    public static FeatureChange simpleCompleteLineChange(final ChangeType changeType,\n            final Atlas atlasContainingTheEntity, final Atlas beforeViewAtlas,\n            final AtlasEntity entity, final boolean saveAllGeometries)\n    {\n        final Long entityIdentifier = entity.getIdentifier();\n        if (changeType == ChangeType.REMOVE)\n        {\n            CompleteLine completeLine = CompleteLine\n                    .shallowFrom(atlasContainingTheEntity.line(entityIdentifier));\n            if (saveAllGeometries)\n            {\n                completeLine = completeLine.withPolyLine(((Line) entity).asPolyLine());\n            }\n            return FeatureChange.remove(completeLine, beforeViewAtlas);\n        }\n        else\n        {\n            return FeatureChange.add(\n                    CompleteLine.from(atlasContainingTheEntity.line(entityIdentifier)),\n                    beforeViewAtlas);\n        }\n    }\n\n    public static FeatureChange simpleCompleteNodeChange(final ChangeType changeType,\n            final Atlas atlasContainingTheEntity, final Atlas beforeViewAtlas,\n            final AtlasEntity entity, final boolean saveAllGeometries)\n    {\n        final Long entityIdentifier = entity.getIdentifier();\n        if (changeType == ChangeType.REMOVE)\n        {\n            CompleteNode completeNode = CompleteNode\n                    .shallowFrom(atlasContainingTheEntity.node(entityIdentifier));\n            if (saveAllGeometries)\n            {\n                completeNode = completeNode.withLocation(((Node) entity).getLocation());\n            }\n            return FeatureChange.remove(completeNode, beforeViewAtlas);\n        }\n        else\n        {\n            return FeatureChange.add(\n                    CompleteNode.from(atlasContainingTheEntity.node(entityIdentifier)),\n                    beforeViewAtlas);\n        }\n    }\n\n    public static FeatureChange simpleCompletePointChange(final ChangeType changeType,\n            final Atlas atlasContainingTheEntity, final Atlas beforeViewAtlas,\n            final AtlasEntity entity, final boolean saveAllGeometries)\n    {\n        final Long entityIdentifier = entity.getIdentifier();\n        if (changeType == ChangeType.REMOVE)\n        {\n            CompletePoint completePoint = CompletePoint\n                    .shallowFrom(atlasContainingTheEntity.point(entityIdentifier));\n            if (saveAllGeometries)\n            {\n                completePoint = completePoint.withLocation(((Point) entity).getLocation());\n            }\n            return FeatureChange.remove(completePoint, beforeViewAtlas);\n        }\n        else\n        {\n            return FeatureChange.add(\n                    CompletePoint.from(atlasContainingTheEntity.point(entityIdentifier)),\n                    beforeViewAtlas);\n        }\n    }\n\n    public static FeatureChange simpleCompleteRelationChange(final ChangeType changeType,\n            final Atlas atlasContainingTheEntity, final Atlas beforeViewAtlas,\n            final AtlasEntity entity)\n    {\n        final Long entityIdentifier = entity.getIdentifier();\n        if (changeType == ChangeType.REMOVE)\n        {\n            final CompleteRelation completeRelation = CompleteRelation\n                    .shallowFrom(atlasContainingTheEntity.relation(entityIdentifier));\n            return FeatureChange.remove(completeRelation, beforeViewAtlas);\n        }\n        else\n        {\n            return FeatureChange.add(\n                    CompleteRelation.from(atlasContainingTheEntity.relation(entityIdentifier)),\n                    beforeViewAtlas);\n        }\n    }\n\n    /*\n     * NOTE: this method considers two edges to be distinct if and only if they have distinct\n     * identifiers.\n     */\n    private static boolean differentEdgeSet(final SortedSet<Edge> beforeEdges,\n            final SortedSet<Edge> afterEdges)\n    {\n        if (beforeEdges.size() != afterEdges.size())\n        {\n            return true;\n        }\n        final Iterator<Edge> beforeInEdgeIterator = beforeEdges.iterator();\n        final Iterator<Edge> afterInEdgeIterator = afterEdges.iterator();\n        for (int i = 0; i < beforeEdges.size(); i++)\n        {\n            final Edge beforeInEdge = beforeInEdgeIterator.next();\n            final Edge afterInEdge = afterInEdgeIterator.next();\n            if (beforeInEdge.getIdentifier() != afterInEdge.getIdentifier())\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private AtlasDiffHelper()\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/event/EntityChangeEvent.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.event;\n\nimport java.io.Serializable;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteItemType;\n\n/**\n * An abstract representation of a change in a\n * {@link org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity}.\n *\n * @author Yazad Khambata\n */\npublic abstract class EntityChangeEvent implements Serializable\n{\n    private static final long serialVersionUID = -9159065869173338344L;\n\n    private final CompleteItemType completeItemType;\n    private final long identifier;\n    private final Optional<Object> newValue;\n\n    public EntityChangeEvent(final CompleteItemType completeItemType, final long identifier)\n    {\n        this(completeItemType, identifier, Optional.empty());\n    }\n\n    public EntityChangeEvent(final CompleteItemType completeItemType, final long identifier,\n            final Optional<Object> newValue)\n    {\n        super();\n        this.completeItemType = completeItemType;\n        this.identifier = identifier;\n        this.newValue = newValue;\n    }\n\n    public CompleteItemType getCompleteItemType()\n    {\n        return this.completeItemType;\n    }\n\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public Optional<Object> getNewValue()\n    {\n        return this.newValue;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/event/TagChangeEvent.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.event;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.consts.FieldChangeOperation;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteItemType;\n\n/**\n * Represents a tag change event in a\n * {@link org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity}.\n *\n * @author Yazad Khambata\n */\npublic class TagChangeEvent extends EntityChangeEvent\n{\n    private static final long serialVersionUID = -3108915161471760840L;\n\n    private final FieldChangeOperation fieldOperation;\n\n    public static TagChangeEvent added(final CompleteItemType completeItemType,\n            final long identifier, final Pair<String, String> addedTagPair)\n    {\n        return new TagChangeEvent(completeItemType, identifier, Optional.of(addedTagPair),\n                FieldChangeOperation.ADD);\n    }\n\n    public static TagChangeEvent overwrite(final CompleteItemType completeItemType,\n            final long identifier, final Map<String, String> newTags)\n    {\n        return new TagChangeEvent(completeItemType, identifier, Optional.ofNullable(newTags),\n                FieldChangeOperation.OVERWRITE);\n    }\n\n    public static TagChangeEvent remove(final CompleteItemType completeItemType,\n            final long identifier, final String key)\n    {\n        return new TagChangeEvent(completeItemType, identifier, Optional.of(key),\n                FieldChangeOperation.REMOVE);\n    }\n\n    public static TagChangeEvent replaced(final CompleteItemType completeItemType,\n            final long identifier, final Triple<String, String, String> tagReplacementInfo)\n    {\n        return new TagChangeEvent(completeItemType, identifier, Optional.of(tagReplacementInfo),\n                FieldChangeOperation.REPLACE);\n    }\n\n    protected TagChangeEvent(final CompleteItemType completeItemType, final long identifier,\n            final Optional<Object> newValue, final FieldChangeOperation fieldOperation)\n    {\n        super(completeItemType, identifier, newValue);\n        this.fieldOperation = fieldOperation;\n    }\n\n    public FieldChangeOperation getFieldOperation()\n    {\n        return this.fieldOperation;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/event/consts/FieldChangeOperation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.consts;\n\n/**\n * Indicates an operation being performed on a field of a\n * {@link org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity}, like tags, geometry,\n * relations, etc.\n *\n * @author Yazad Khambata\n */\npublic enum FieldChangeOperation\n{\n    ADD,\n    REMOVE,\n    REPLACE,\n    OVERWRITE;\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/listenable/EntityChangeListenable.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.listenable;\n\n/**\n * An object that can be \"listened\" to. Typically a\n * {@link org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity}.\n *\n * @author Yazad Khambata\n */\npublic interface EntityChangeListenable\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/listenable/TagChangeListenable.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.listenable;\n\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;\n\n/**\n * A {@link org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity} whose tag changes can\n * be \"listened\" to.\n *\n * @author Yazad Khambata\n */\npublic interface TagChangeListenable extends EntityChangeListenable\n{\n    void addTagChangeListener(TagChangeListener tagChangeListener);\n\n    void fireTagChangeEvent(TagChangeEvent tagChangeEvent);\n\n    void removeTagChangeListeners();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/listener/EntityChangeListener.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.EntityChangeEvent;\n\n/**\n * The basic contract for tracking changes to a\n * {@link org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity}.\n *\n * @param <E>\n *            - the entity being observed.\n * @author Yazad Khambata\n */\n@FunctionalInterface\npublic interface EntityChangeListener<E extends EntityChangeEvent> extends Serializable\n{\n    void entityChanged(E entityChangeEvent);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/listener/TagChangeListener.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener;\n\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\n\n/**\n * Tracking changes to the tags of the\n * {@link org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity}.\n *\n * @author Yazad Khambata\n */\npublic interface TagChangeListener extends EntityChangeListener<TagChangeEvent>\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/exception/EmptyChangeException.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.exception;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author Yazad Khambata\n */\npublic class EmptyChangeException extends CoreException\n{\n    private static final String MESSAGE = \"Change cannot be empty.\";\n\n    public EmptyChangeException()\n    {\n        super(messageWithToken(MESSAGE));\n    }\n\n    public EmptyChangeException(final Throwable cause)\n    {\n        super(messageWithToken(MESSAGE), cause);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/serializer/ChangeGeoJsonSerializer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.serializer;\n\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.lang.reflect.Type;\nimport java.util.function.BiConsumer;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.change.Change;\nimport org.openstreetmap.atlas.geography.atlas.change.serializer.FeatureChangeGeoJsonSerializer.FeatureChangeTypeHierarchyAdapter;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonSerializationContext;\nimport com.google.gson.JsonSerializer;\n\n/**\n * @author matthieun\n */\npublic class ChangeGeoJsonSerializer\n        implements BiConsumer<Change, WritableResource>, Converter<Change, String>\n{\n    /**\n     * @author matthieun\n     */\n    private static class ChangeTypeHierarchyAdapter implements JsonSerializer<Change>\n    {\n        private final FeatureChangeTypeHierarchyAdapter featureChangeTypeHierarchyAdapter;\n\n        ChangeTypeHierarchyAdapter(final boolean showDescription)\n        {\n            this.featureChangeTypeHierarchyAdapter = new FeatureChangeTypeHierarchyAdapter(\n                    showDescription);\n        }\n\n        @Override\n        public JsonElement serialize(final Change source, final Type typeOfSource,\n                final JsonSerializationContext context)\n        {\n            final JsonObject result = new JsonObject();\n\n            result.addProperty(\"type\", \"FeatureCollection\");\n            final Rectangle bounds = source.bounds();\n            result.add(\"bbox\", bounds.asGeoJsonBbox());\n\n            final JsonArray features = new JsonArray();\n            source.changes().map(this.featureChangeTypeHierarchyAdapter::serialize)\n                    .forEach(features::add);\n            result.add(\"features\", features);\n\n            final JsonObject properties = new JsonObject();\n            properties.addProperty(\"bboxWKT\", source.bounds().toWkt());\n            result.add(\"properties\", properties);\n            return result;\n        }\n    }\n\n    private final Gson jsonSerializer;\n\n    public ChangeGeoJsonSerializer()\n    {\n        this(true);\n    }\n\n    public ChangeGeoJsonSerializer(final boolean prettyPrint, final boolean showDescription)\n    {\n        final GsonBuilder gsonBuilder = new GsonBuilder();\n        if (prettyPrint)\n        {\n            gsonBuilder.setPrettyPrinting();\n        }\n        gsonBuilder.disableHtmlEscaping();\n        gsonBuilder.registerTypeHierarchyAdapter(Change.class,\n                new ChangeTypeHierarchyAdapter(showDescription));\n        this.jsonSerializer = gsonBuilder.create();\n    }\n\n    public ChangeGeoJsonSerializer(final boolean prettyPrint)\n    {\n        this(prettyPrint, true);\n    }\n\n    @Override\n    public void accept(final Change change, final WritableResource resource)\n    {\n        try (Writer writer = resource.writer())\n        {\n            this.jsonSerializer.toJson(change, writer);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Could not save FeatureChange to resource {}\",\n                    resource.getName(), e);\n        }\n    }\n\n    @Override\n    public String convert(final Change change)\n    {\n        return this.jsonSerializer.toJson(change);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/serializer/FeatureChangeGeoJsonSerializer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.serializer;\n\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.lang.reflect.Type;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.TreeMap;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometryPrintable;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescription;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\nimport com.google.gson.JsonSerializationContext;\nimport com.google.gson.JsonSerializer;\n\n/**\n * @author matthieun\n */\npublic class FeatureChangeGeoJsonSerializer\n        implements BiConsumer<FeatureChange, WritableResource>, Converter<FeatureChange, String>\n{\n    /**\n     * @author matthieun\n     */\n    protected static class FeatureChangeTypeHierarchyAdapter\n            implements JsonSerializer<FeatureChange>\n    {\n        private final boolean showDescription;\n\n        private static void addGeometryGeojson(final JsonObject result,\n                final GeometryPrintable property)\n        {\n            add(result, \"geometry\", property, GeometryPrintable::asGeoJson);\n        }\n\n        private static void addGeometryWkt(final JsonObject result,\n                final GeometryPrintable property)\n        {\n            addProperty(result, \"WKT\", property, GeometryPrintable::toWkt);\n        }\n\n        FeatureChangeTypeHierarchyAdapter(final boolean showDescription)\n        {\n            this.showDescription = showDescription;\n        }\n\n        public JsonElement serialize(final FeatureChange source)\n        {\n            final JsonObject result = new JsonObject();\n\n            result.addProperty(\"type\", \"Feature\");\n            final Rectangle bounds = source.bounds();\n            result.add(\"bbox\", bounds.asGeoJsonBbox());\n\n            final GeometryPrintable geometryPrintable = new AtlasEntityGeometryPrintableConverter()\n                    .convert(source);\n            addGeometryGeojson(result, geometryPrintable);\n\n            final JsonObject properties = new JsonObject();\n            properties.addProperty(\"featureChangeType\", source.getChangeType().toString());\n            add(properties, \"metadata\", source.getMetaData(), tagPrinter);\n            if (this.showDescription)\n            {\n                add(properties, \"description\", source.explain(), ChangeDescription::toJsonElement);\n            }\n            new AtlasEntityPropertiesConverter().convert(source.getAfterView()).entrySet()\n                    .forEach(entry -> properties.add(entry.getKey(), entry.getValue()));\n            addGeometryWkt(properties, geometryPrintable);\n            properties.addProperty(\"bboxWKT\", source.bounds().toWkt());\n            result.add(\"properties\", properties);\n            return result;\n        }\n\n        @Override\n        public JsonElement serialize(final FeatureChange source, final Type typeOfSource,\n                final JsonSerializationContext context)\n        {\n            return serialize(source);\n        }\n    }\n\n    /**\n     * @author matthieun\n     */\n    private static class AtlasEntityGeometryPrintableConverter\n            implements Converter<FeatureChange, GeometryPrintable>\n    {\n        @Override\n        public GeometryPrintable convert(final FeatureChange featureChange)\n        {\n            final AtlasEntity source = featureChange.getAfterView();\n            GeometryPrintable result;\n            if (source instanceof Area)\n            {\n                result = ((Area) source).asPolygon();\n                if (result == null && featureChange.getBeforeView() != null)\n                {\n                    result = ((Area) featureChange.getBeforeView()).asPolygon();\n                }\n            }\n            else if (source instanceof LineItem)\n            {\n                result = ((LineItem) source).asPolyLine();\n                if (result == null && featureChange.getBeforeView() != null)\n                {\n                    result = ((LineItem) featureChange.getBeforeView()).asPolyLine();\n                }\n            }\n            else if (source instanceof LocationItem)\n            {\n                result = ((LocationItem) source).getLocation();\n                if (result == null && featureChange.getBeforeView() != null)\n                {\n                    result = ((LocationItem) featureChange.getBeforeView()).getLocation();\n                }\n            }\n            else\n            {\n                // Relation\n                final Optional<MultiPolygon> geom = ((Relation) source).asMultiPolygon();\n                if (geom.isPresent())\n                {\n                    result = new JtsMultiPolygonToMultiPolygonConverter().convert(geom.get());\n                }\n                else\n                {\n                    result = ((Relation) source).bounds();\n                }\n            }\n            if (result == null)\n            {\n                result = source.bounds();\n            }\n            return result;\n        }\n    }\n\n    /**\n     * @author matthieun\n     */\n    private static class AtlasEntityPropertiesConverter\n            implements Converter<AtlasEntity, JsonObject>\n    {\n        @Override\n        public JsonObject convert(final AtlasEntity source)\n        {\n            final JsonObject properties = new JsonObject();\n            properties.addProperty(\"entityType\", source.getType().toString());\n            properties.addProperty(\"completeEntityClass\", source.getClass().getName());\n            properties.addProperty(\"identifier\", source.getIdentifier());\n            add(properties, \"tags\", source.getTags(), tagPrinter);\n            add(properties, \"relations\", source.relations(), identifierMapper);\n\n            if (source instanceof Edge)\n            {\n                addProperty(properties, \"startNode\", ((Edge) source).start(), Node::getIdentifier);\n                addProperty(properties, \"endNode\", ((Edge) source).end(), Node::getIdentifier);\n            }\n            else if (source instanceof Node)\n            {\n                add(properties, \"inEdges\", ((Node) source).inEdges(), identifierMapper);\n                add(properties, \"outEdges\", ((Node) source).outEdges(), identifierMapper);\n            }\n            else if (source instanceof Relation)\n            {\n                // Relation\n                final Relation relation = (Relation) source;\n                add(properties, \"members\", relation.members(), members ->\n                {\n                    final JsonArray beanResult = new JsonArray();\n                    members.forEach(member -> beanResult.add(new JsonPrimitive(member.toString())));\n                    return beanResult;\n                });\n            }\n            return properties;\n        }\n    }\n\n    private static final Function<Iterable<? extends AtlasEntity>, JsonElement> identifierMapper = entity ->\n    {\n        final JsonArray result = new JsonArray();\n        Iterables.stream(entity).map(AtlasEntity::getIdentifier).collectToSortedSet()\n                .forEach(number -> result.add(new JsonPrimitive(number)));\n        return result;\n    };\n\n    private static final Function<Map<String, String>, JsonElement> tagPrinter = map ->\n    {\n        final JsonObject result = new JsonObject();\n        final Map<String, String> sortedMap = new TreeMap<>(map);\n        for (final Map.Entry<String, String> entry : sortedMap.entrySet())\n        {\n            result.addProperty(entry.getKey(), entry.getValue());\n        }\n        return result;\n    };\n\n    private final Gson jsonSerializer;\n\n    private static <T> void add(final JsonObject result, final String name, final T property,\n            final Function<T, JsonElement> writer)\n    {\n        if (property == null)\n        {\n            result.addProperty(name, (String) null);\n        }\n        else\n        {\n            result.add(name, writer.apply(property));\n        }\n    }\n\n    private static <T> void addProperty(final JsonObject result, final String name,\n            final T property, final Function<T, ? extends Object> writer)\n    {\n        result.addProperty(name, property == null ? null : writer.apply(property).toString());\n    }\n\n    public FeatureChangeGeoJsonSerializer(final boolean prettyPrint)\n    {\n        this(prettyPrint, true);\n    }\n\n    public FeatureChangeGeoJsonSerializer(final boolean prettyPrint, final boolean showDescription)\n    {\n        final GsonBuilder gsonBuilder = new GsonBuilder();\n        if (prettyPrint)\n        {\n            gsonBuilder.setPrettyPrinting();\n        }\n        gsonBuilder.disableHtmlEscaping();\n        gsonBuilder.registerTypeHierarchyAdapter(FeatureChange.class,\n                new FeatureChangeTypeHierarchyAdapter(showDescription));\n        this.jsonSerializer = gsonBuilder.create();\n    }\n\n    @Override\n    public void accept(final FeatureChange featureChange, final WritableResource resource)\n    {\n        try (Writer writer = resource.writer())\n        {\n            this.jsonSerializer.toJson(featureChange, writer);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Could not save FeatureChange to resource {}\",\n                    resource.getName(), e);\n        }\n    }\n\n    @Override\n    public String convert(final FeatureChange featureChange)\n    {\n        return this.jsonSerializer.toJson(featureChange);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/testing/AtlasChangeGeneratorAddTurnRestrictions.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.testing;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.change.AtlasChangeGenerator;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\nimport com.google.common.collect.Lists;\n\n/**\n * @author matthieun\n */\npublic class AtlasChangeGeneratorAddTurnRestrictions implements AtlasChangeGenerator\n{\n    private static final long serialVersionUID = -518515697422424803L;\n    private static final int MINIMUM_NODE_VALENCE = 3;\n\n    private final int minimumNodeValence;\n\n    public AtlasChangeGeneratorAddTurnRestrictions()\n    {\n        this(MINIMUM_NODE_VALENCE);\n    }\n\n    public AtlasChangeGeneratorAddTurnRestrictions(final int minimumNodeValence)\n    {\n        this.minimumNodeValence = minimumNodeValence;\n    }\n\n    @Override\n    public Set<FeatureChange> generateWithoutValidation(final Atlas atlas)\n    {\n        final AtomicLong identifierGenerator = new AtomicLong();\n        final Set<FeatureChange> result = new HashSet<>();\n        final Long parentRelationIdentifier = identifierGenerator.incrementAndGet();\n        final RelationBean parentMembers = new RelationBean();\n        Rectangle parentBounds = null;\n        for (final Node node : atlas.nodes(node -> node.valence() > this.minimumNodeValence))\n        {\n            final SortedSet<Edge> inEdges = node.inEdges();\n            final SortedSet<Edge> outEdges = node.outEdges();\n            for (final Edge inEdge : inEdges)\n            {\n                for (final Edge outEdge : outEdges)\n                {\n                    final RelationBean members = new RelationBean();\n                    members.addItem(inEdge.getIdentifier(), \"from\", ItemType.EDGE);\n                    inEdge.reversed().ifPresent(reversed -> members\n                            .addItem(reversed.getIdentifier(), \"from\", ItemType.EDGE));\n                    members.addItem(node.getIdentifier(), \"via\", ItemType.NODE);\n                    members.addItem(outEdge.getIdentifier(), \"to\", ItemType.EDGE);\n                    outEdge.reversed().ifPresent(reversed -> members\n                            .addItem(reversed.getIdentifier(), \"to\", ItemType.EDGE));\n                    final Long relationIdentifier = identifierGenerator.incrementAndGet();\n                    final Rectangle bounds = Rectangle.forLocated(inEdge, outEdge);\n                    if (parentBounds == null)\n                    {\n                        parentBounds = bounds;\n                    }\n                    else\n                    {\n                        parentBounds = Rectangle.forLocated(parentBounds, bounds);\n                    }\n                    parentMembers.addItem(relationIdentifier, \"addition\", ItemType.RELATION);\n                    result.add(FeatureChange.add(new CompleteRelation(relationIdentifier,\n                            Maps.hashMap(\"type\", \"restriction\", \"restriction\", \"no_left_turn\"),\n                            bounds, members, Lists.newArrayList(relationIdentifier), members,\n                            relationIdentifier, Sets.hashSet(parentRelationIdentifier))));\n                    result.add(FeatureChange\n                            .add(CompleteEdge.shallowFrom(inEdge).withRelationIdentifiers(\n                                    mergeRelationMembers(inEdge.relations(), relationIdentifier))));\n                    if (inEdge.hasReverseEdge())\n                    {\n                        result.add(\n                                FeatureChange.add(CompleteEdge.shallowFrom(inEdge.reversed().get())\n                                        .withRelationIdentifiers(mergeRelationMembers(\n                                                inEdge.relations(), relationIdentifier))));\n                    }\n                    result.add(FeatureChange\n                            .add(CompleteNode.shallowFrom(node).withRelationIdentifiers(\n                                    mergeRelationMembers(node.relations(), relationIdentifier))));\n                    result.add(FeatureChange.add(CompleteEdge.shallowFrom(outEdge)\n                            .withRelationIdentifiers(mergeRelationMembers(outEdge.relations(),\n                                    relationIdentifier))));\n                    if (outEdge.hasReverseEdge())\n                    {\n                        result.add(\n                                FeatureChange.add(CompleteEdge.shallowFrom(outEdge.reversed().get())\n                                        .withRelationIdentifiers(mergeRelationMembers(\n                                                outEdge.relations(), relationIdentifier))));\n                    }\n                    // Break here to avoid too many Relation FeatureChanges and make validation\n                    // super slow for unit tests.\n                    break;\n                }\n                // Break here to avoid too many Relation FeatureChanges and make validation super\n                // slow for unit tests.\n                break;\n            }\n        }\n        if (!result.isEmpty())\n        {\n            result.add(FeatureChange.add(new CompleteRelation(parentRelationIdentifier,\n                    Maps.hashMap(\"name\", \"parent_of_new_restrictions\"), parentBounds, parentMembers,\n                    Lists.newArrayList(parentRelationIdentifier), parentMembers,\n                    parentRelationIdentifier, Sets.hashSet())));\n        }\n        return result;\n    }\n\n    private Set<Long> mergeRelationMembers(final Set<Relation> relations, final Long newIdentifier)\n    {\n        return Sets.withSets(\n                relations.stream().map(Relation::getIdentifier).collect(Collectors.toSet()),\n                Sets.hashSet(newIdentifier));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/testing/AtlasChangeGeneratorRemoveReverseEdges.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.testing;\n\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.AtlasChangeGenerator;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class AtlasChangeGeneratorRemoveReverseEdges implements AtlasChangeGenerator\n{\n    private static final long serialVersionUID = 2378086577050982603L;\n\n    @Override\n    public Set<FeatureChange> generateWithoutValidation(final Atlas atlas)\n    {\n        return Iterables.stream(atlas.edges()).filter(Edge::isMainEdge).filter(Edge::hasReverseEdge)\n                .map(Edge::reversed).filter(Optional::isPresent).map(Optional::get)\n                .map(edge -> FeatureChange.remove(CompleteEntity.shallowFrom(edge), atlas))\n                .collectToSet();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/testing/AtlasChangeGeneratorSplitRoundabout.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.testing;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.AtlasChangeGenerator;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.tags.JunctionTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class AtlasChangeGeneratorSplitRoundabout implements AtlasChangeGenerator\n{\n    private static final long serialVersionUID = -6596053817414805897L;\n\n    @Override\n    public Set<FeatureChange> generateWithoutValidation(final Atlas atlas)\n    {\n        final AtomicLong identifierGenerator = new AtomicLong();\n        final Set<FeatureChange> result = new HashSet<>();\n        for (final Edge edge : atlas.edges(JunctionTag::isRoundabout))\n        {\n            final PolyLine currentShape = edge.asPolyLine();\n            if (currentShape.size() > 2 && !edge.hasReverseEdge())\n            {\n                // Prepare members to fill out: shapes, ids, etc.\n                final Location cut = currentShape.get(currentShape.size() / 2);\n                final PolyLine shape1 = currentShape.between(currentShape.first(), 0, cut, 0);\n                final PolyLine shape2 = currentShape.between(cut, 0, currentShape.last(),\n                        currentShape.last().equals(currentShape.first()) ? 1 : 0);\n                final long middleNodeIdentifier = identifierGenerator.incrementAndGet();\n                final long oldEdgeIdentifier = edge.getIdentifier();\n                final long newEdgeIdentifier1 = identifierGenerator.incrementAndGet();\n                final long newEdgeIdentifier2 = identifierGenerator.incrementAndGet();\n\n                // This is a new Edge, use \"from\" instead of \"shallowFrom\"\n                final CompleteEdge firstEdge = CompleteEdge.from(edge)\n                        .withIdentifier(newEdgeIdentifier1).withPolyLine(shape1)\n                        .withEndNodeIdentifier(middleNodeIdentifier);\n                // This is a new Edge, use \"from\" instead of \"shallowFrom\"\n                final CompleteEdge secondEdge = CompleteEdge.from(edge)\n                        .withIdentifier(newEdgeIdentifier2).withPolyLine(shape2)\n                        .withStartNodeIdentifier(middleNodeIdentifier);\n\n                // Update relations of edge to instead list first and second edge that have new IDs\n                edge.relations().stream().map(relation ->\n                {\n                    // newMembers exclude the old edge explicitly\n                    final RelationMemberList newMembers = new RelationMemberList(relation.members()\n                            .stream().filter(member -> !member.getEntity().equals(edge))\n                            .collect(Collectors.toList()));\n                    return CompleteRelation.shallowFrom(relation)\n                            .withMembersAndSource(newMembers, relation)\n                            // With the new relation members\n                            .withAddedMember(firstEdge, edge).withAddedMember(secondEdge, edge);\n                }).map(relation -> FeatureChange.add(relation, atlas)).forEach(result::add);\n\n                // Add the two new edges.\n                result.add(FeatureChange.remove(CompleteEdge.shallowFrom(edge), atlas));\n                result.add(FeatureChange.add(firstEdge, atlas));\n                result.add(FeatureChange.add(secondEdge, atlas));\n\n                // Middle node is new. Create from scratch\n                result.add(FeatureChange.add(new CompleteNode(middleNodeIdentifier, cut,\n                        Maps.hashMap(), Sets.treeSet(newEdgeIdentifier1),\n                        Sets.treeSet(newEdgeIdentifier2), Sets.hashSet()), atlas));\n\n                // End node has a replaced start edge identifier\n                result.add(FeatureChange.add(CompleteNode.shallowFrom(edge.end())\n                        .withInEdges(edge.end().inEdges()).withReplacedInEdgeIdentifier(\n                                oldEdgeIdentifier, newEdgeIdentifier2),\n                        atlas));\n                // Start node has a replaced end edge identifier\n                result.add(FeatureChange.add(CompleteNode.shallowFrom(edge.start())\n                        .withOutEdges(edge.start().outEdges()).withReplacedOutEdgeIdentifier(\n                                oldEdgeIdentifier, newEdgeIdentifier1),\n                        atlas));\n            }\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/change/validators/ChangeValidator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.validators;\n\nimport java.util.Optional;\nimport java.util.function.BiPredicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.change.Change;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeType;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.change.exception.EmptyChangeException;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Validate a {@link Change}\n *\n * @author matthieun\n */\npublic class ChangeValidator\n{\n    private static final Logger logger = LoggerFactory.getLogger(ChangeValidator.class);\n\n    private final Change change;\n\n    public ChangeValidator(final Change change)\n    {\n        this.change = change;\n    }\n\n    public void validate()\n    {\n        logger.trace(\"Starting validation of Change {}\", this.change.getName());\n        final Time start = Time.now();\n        validateChangeNotEmpty();\n        validateReverseEdgesHaveForwardMatchingCounterpart();\n        validateAreaGeometricRelationsUpdated();\n        validateLineGeometricRelationsUpdated();\n        validateEdgeGeometricRelationsUpdated();\n        logger.trace(\"Finished validation of Change {} in {}\", this.change.getName(),\n                start.elapsedSince());\n    }\n\n    protected void validateAreaGeometricRelationsUpdated()\n    {\n        this.change.changesFor(ItemType.AREA)\n                .filter(featureChange -> featureChange.getChangeType() != ChangeType.REMOVE)\n                .filter(featureChange -> ((CompleteArea) featureChange.getAfterView())\n                        .asPolygon() != null)\n                .filter(featureChange -> ((CompleteArea) featureChange.getAfterView())\n                        .geometricRelationIdentifiers() != null\n                        && !((CompleteArea) featureChange.getAfterView())\n                                .geometricRelationIdentifiers().isEmpty())\n                .filter(featureChange -> featureChange.getBeforeView() == null\n                        || ((Area) featureChange.getBeforeView()).asPolygon() == null\n                        || !((Area) featureChange.getBeforeView()).asPolygon()\n                                .equals(((CompleteArea) featureChange.getAfterView()).asPolygon()))\n                .forEach(featureChange ->\n                {\n                    final CompleteArea after = (CompleteArea) featureChange.getAfterView();\n                    after.geometricRelationIdentifiers().forEach(relationId ->\n                    {\n                        final Optional<FeatureChange> changeRelationOptional = this.change\n                                .changeFor(ItemType.RELATION, relationId);\n                        if (changeRelationOptional.isEmpty())\n                        {\n                            throw new CoreException(\n                                    \"Geometric relation {} had no change for area member {} with updated geometry!\",\n                                    relationId, after.getIdentifier());\n                        }\n                        if (!changeRelationOptional.get().getChangeType().equals(ChangeType.REMOVE))\n                        {\n                            final CompleteRelation updatedRelation = (CompleteRelation) changeRelationOptional\n                                    .get().getAfterView();\n                            if (!updatedRelation.isOverrideGeometry())\n                            {\n                                if (updatedRelation.getRemovedGeometry().isEmpty()\n                                        && updatedRelation.getAddedGeometry().isEmpty())\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no change for area member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                                if (featureChange.getBeforeView() != null\n                                        && ((Area) featureChange.getBeforeView())\n                                                .asPolygon() != null\n                                        && !updatedRelation.getRemovedGeometry()\n                                                .contains(new JtsPolyLineConverter().convert(\n                                                        ((Area) featureChange.getBeforeView())\n                                                                .asPolygon())))\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no removed geometry for area member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                                if (!updatedRelation.getAddedGeometry().contains(\n                                        new JtsPolyLineConverter().convert(after.asPolygon())))\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no added geometry for area member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                            }\n                        }\n                    });\n                });\n    }\n\n    protected void validateChangeNotEmpty()\n    {\n        if (this.change.changeCount() == 0)\n        {\n            throw new EmptyChangeException();\n        }\n    }\n\n    protected void validateEdgeGeometricRelationsUpdated()\n    {\n        this.change.changesFor(ItemType.EDGE)\n                .filter(featureChange -> featureChange.getChangeType() != ChangeType.REMOVE)\n                .filter(featureChange -> ((Edge) featureChange.getAfterView()).asPolyLine() != null)\n                .filter(featureChange -> ((CompleteEdge) featureChange.getAfterView())\n                        .geometricRelationIdentifiers() != null\n                        && !((CompleteEdge) featureChange.getAfterView())\n                                .geometricRelationIdentifiers().isEmpty())\n                .filter(featureChange -> featureChange.getBeforeView() == null\n                        || ((Edge) featureChange.getBeforeView()).asPolyLine() == null\n                        || !((Edge) featureChange.getBeforeView()).asPolyLine()\n                                .equals(((CompleteEdge) featureChange.getAfterView()).asPolyLine()))\n                .forEach(featureChange ->\n                {\n                    final CompleteEdge after = (CompleteEdge) featureChange.getAfterView();\n                    after.geometricRelationIdentifiers().forEach(relationId ->\n                    {\n                        final Optional<FeatureChange> changeRelationOptional = this.change\n                                .changeFor(ItemType.RELATION, relationId);\n                        if (changeRelationOptional.isEmpty())\n                        {\n                            throw new CoreException(\n                                    \"Geometric relation {} had no change for edge member {} with updated geometry!\",\n                                    relationId, after.getIdentifier());\n                        }\n                        if (!changeRelationOptional.get().getChangeType().equals(ChangeType.REMOVE))\n                        {\n                            final CompleteRelation updatedRelation = (CompleteRelation) changeRelationOptional\n                                    .get().getAfterView();\n                            if (!updatedRelation.isOverrideGeometry())\n                            {\n                                if (updatedRelation.getRemovedGeometry().isEmpty()\n                                        && updatedRelation.getAddedGeometry().isEmpty())\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no change for edge member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                                if (featureChange.getBeforeView() != null\n                                        && ((Edge) featureChange.getBeforeView())\n                                                .asPolyLine() != null\n                                        && !updatedRelation.getRemovedGeometry()\n                                                .contains(new JtsPolyLineConverter().convert(\n                                                        ((Edge) featureChange.getBeforeView())\n                                                                .asPolyLine())))\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no removed geometry for edge member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                                if (!updatedRelation.getAddedGeometry().contains(\n                                        new JtsPolyLineConverter().convert(after.asPolyLine())))\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no added geometry for edge member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                            }\n                        }\n                    });\n                });\n    }\n\n    protected void validateLineGeometricRelationsUpdated()\n    {\n        this.change.changesFor(ItemType.LINE)\n                .filter(featureChange -> featureChange.getChangeType() != ChangeType.REMOVE)\n                .filter(featureChange -> ((Line) featureChange.getAfterView()).asPolyLine() != null)\n                .filter(featureChange -> ((CompleteLine) featureChange.getAfterView())\n                        .geometricRelationIdentifiers() != null\n                        && !((CompleteLine) featureChange.getAfterView())\n                                .geometricRelationIdentifiers().isEmpty())\n                .filter(featureChange -> featureChange.getBeforeView() == null\n                        || ((Line) featureChange.getBeforeView()).asPolyLine() == null\n                        || !((Line) featureChange.getBeforeView()).asPolyLine()\n                                .equals(((CompleteLine) featureChange.getAfterView()).asPolyLine()))\n                .forEach(featureChange ->\n                {\n                    final CompleteLine after = (CompleteLine) featureChange.getAfterView();\n                    after.geometricRelationIdentifiers().forEach(relationId ->\n                    {\n                        final Optional<FeatureChange> changeRelationOptional = this.change\n                                .changeFor(ItemType.RELATION, relationId);\n                        if (changeRelationOptional.isEmpty())\n                        {\n                            throw new CoreException(\n                                    \"Geometric relation {} had no change for Line member {} with updated geometry!\",\n                                    relationId, after.getIdentifier());\n                        }\n                        if (!changeRelationOptional.get().getChangeType().equals(ChangeType.REMOVE))\n                        {\n                            final CompleteRelation updatedRelation = (CompleteRelation) changeRelationOptional\n                                    .get().getAfterView();\n                            if (!updatedRelation.isOverrideGeometry())\n                            {\n                                if (updatedRelation.getRemovedGeometry().isEmpty()\n                                        && updatedRelation.getAddedGeometry().isEmpty())\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no change for Line member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                                if (featureChange.getBeforeView() != null\n                                        && ((Line) featureChange.getBeforeView())\n                                                .asPolyLine() != null\n                                        && !updatedRelation.getRemovedGeometry()\n                                                .contains(new JtsPolyLineConverter().convert(\n                                                        ((Line) featureChange.getBeforeView())\n                                                                .asPolyLine())))\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no removed geometry for Line member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                                if (!updatedRelation.getAddedGeometry().contains(\n                                        new JtsPolyLineConverter().convert(after.asPolyLine())))\n                                {\n                                    throw new CoreException(\n                                            \"Geometric relation {} had no added geometry for Line member {} with updated geometry!\",\n                                            relationId, after.getIdentifier());\n                                }\n                            }\n                        }\n                    });\n                });\n    }\n\n    protected void validateReverseEdgesHaveForwardMatchingCounterpart()\n    {\n        this.change.changesFor(ItemType.EDGE)\n                .filter(featureChange -> !((Edge) featureChange.getAfterView()).isMainEdge())\n                .filter(featureChange -> featureChange.getChangeType() != ChangeType.REMOVE)\n                .forEach(backwardFeatureChange ->\n                {\n                    final long backwardEdgeIdentifier = backwardFeatureChange.getAfterView()\n                            .getIdentifier();\n                    final long forwardEdgeIdentifier = -backwardEdgeIdentifier;\n\n                    final Edge backwardEdge = (Edge) backwardFeatureChange.getAfterView();\n                    final Optional<FeatureChange> forwardFeatureChangeOption = this.change\n                            .changeFor(ItemType.EDGE, forwardEdgeIdentifier);\n                    if (forwardFeatureChangeOption.isPresent())\n                    {\n                        final FeatureChange forwardFeatureChange = forwardFeatureChangeOption.get();\n                        if (forwardFeatureChange.getChangeType() != backwardFeatureChange\n                                .getChangeType())\n                        {\n                            throw new CoreException(\n                                    \"Forward edge {} is {} when backward edge is {}\",\n                                    forwardEdgeIdentifier, forwardFeatureChange.getChangeType(),\n                                    backwardFeatureChange.getChangeType());\n                        }\n                        if (forwardFeatureChange.getChangeType() == ChangeType.ADD)\n                        {\n                            final Edge forwardEdge = (Edge) forwardFeatureChange.getAfterView();\n                            validateEdgeConnectedNodesMatch(forwardEdge, backwardEdge);\n                            validateEdgePolyLinesMatch(forwardEdge, backwardEdge);\n                        }\n                    }\n                });\n    }\n\n    private <T> boolean differ(final T left, final T right, final BiPredicate<T, T> equal)\n    {\n        if (left != null && right != null)\n        {\n            return !equal.test(left, right);\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n    private void validateEdgeConnectedNodesMatch(final Edge forwardEdge, final Edge backwardEdge)\n    {\n        final BiPredicate<Node, Node> equal = (left,\n                right) -> left.getIdentifier() == right.getIdentifier();\n        final long forwardEdgeIdentifier = forwardEdge.getIdentifier();\n        final Node forwardStartNode = forwardEdge.start();\n        final Node backwardEndNode = backwardEdge.end();\n        if (differ(forwardStartNode, backwardEndNode, equal))\n        {\n            throw new CoreException(\n                    \"Forward edge {} start node {} does not match its backward edge end node {}\",\n                    forwardEdgeIdentifier, forwardStartNode, backwardEndNode);\n        }\n        final Node forwardEndNode = forwardEdge.end();\n        final Node backwardStartNode = backwardEdge.start();\n        if (differ(forwardEndNode, backwardStartNode, equal))\n        {\n            throw new CoreException(\n                    \"Forward edge {} end node {} does not match its backward edge start node {}\",\n                    forwardEdgeIdentifier, forwardEndNode, backwardStartNode);\n        }\n    }\n\n    private void validateEdgePolyLinesMatch(final Edge forwardEdge, final Edge backwardEdge)\n    {\n        final BiPredicate<PolyLine, PolyLine> equal = (left, right) -> left\n                .equals(right.reversed());\n        final long forwardEdgeIdentifier = forwardEdge.getIdentifier();\n        final PolyLine forwardPolyLine = forwardEdge.asPolyLine();\n        final PolyLine backwardPolyLine = backwardEdge.asPolyLine();\n        if (differ(forwardPolyLine, backwardPolyLine, equal))\n        {\n            throw new CoreException(\n                    \"Forward edge {} polyline {} does not match its backward edge polyline {}\",\n                    forwardEdgeIdentifier, forwardPolyLine, backwardPolyLine);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/BinaryChangeSetDeserializer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.OutputStream;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Deserializes {@link ChangeSet} objects from {@link OutputStream}s back into {@link ChangeSet}\n * objects.\n *\n * @author mkalender\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic class BinaryChangeSetDeserializer implements ChangeSetDeserializer\n{\n    private static final Logger logger = LoggerFactory.getLogger(BinaryChangeSetDeserializer.class);\n\n    private final ObjectInputStream resource;\n    private boolean hasMore;\n\n    public BinaryChangeSetDeserializer(final Resource resourceToReadFrom) throws IOException\n    {\n        this.resource = new ObjectInputStream(resourceToReadFrom.read());\n        this.hasMore = true;\n    }\n\n    @Override\n    public void close() throws Exception\n    {\n        this.resource.close();\n    }\n\n    @Override\n    public Optional<ChangeSet> get()\n    {\n        if (!this.hasMore)\n        {\n            return Optional.empty();\n        }\n\n        ChangeSet changeSet = null;\n\n        try\n        {\n            changeSet = (ChangeSet) this.resource.readObject();\n        }\n        catch (final EOFException e)\n        {\n            this.hasMore = false;\n\n            try\n            {\n                this.close();\n            }\n            catch (final Exception closeException)\n            {\n                logger.error(\"ChangeSet resource close is failed.\", closeException);\n            }\n        }\n        catch (final ClassNotFoundException | IOException e)\n        {\n            logger.error(\"ChangeSet deserialization is failed.\", e);\n        }\n\n        return Optional.ofNullable(changeSet);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/BinaryChangeSetSerializer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\nimport java.io.OutputStream;\n\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Serializes {@link ChangeSet} objects and writes them into {@link OutputStream}s in binary format.\n *\n * @author mkalender\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic class BinaryChangeSetSerializer implements ChangeSetSerializer\n{\n    private static final Logger logger = LoggerFactory.getLogger(BinaryChangeSetSerializer.class);\n\n    private final ObjectOutputStream resource;\n\n    public BinaryChangeSetSerializer(final WritableResource resourceToWriteInto) throws IOException\n    {\n        this.resource = new ObjectOutputStream(resourceToWriteInto.write());\n    }\n\n    @Override\n    public void accept(final ChangeSet changeSet)\n    {\n        try\n        {\n            this.resource.writeObject(changeSet);\n            this.resource.flush();\n        }\n        catch (final IOException e)\n        {\n            logger.error(\"ChangeSet serialization is failed.\", e);\n        }\n    }\n\n    @Override\n    public void close() throws Exception\n    {\n        this.resource.close();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/ChangeAction.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\n/**\n * Enumeration of all action types of a change.\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic enum ChangeAction\n{\n    CREATE,\n    UPDATE,\n    DELETE,\n    READ\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/ChangeItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * A change item represent a result generated from data enhancement process.\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic interface ChangeItem extends Taggable, Serializable\n{\n    /**\n     * @return action type of this change item\n     */\n    ChangeAction getAction();\n\n    /**\n     * this needs further thought. We should use a Atlas data type but right now there is no generic\n     * type for all geometry types.\n     *\n     * @return the geometry of the change item, if has it.\n     */\n    Iterable<Location> getGeometry();\n\n    /**\n     * identifier of change item. It's required to be unique within one change set. For newly\n     * created item please use negative value\n     *\n     * @return the unique identifier of change item\n     */\n    long getIdentifier();\n\n    /**\n     * @return item member list if exists. It should only have value if ItemType is RELATION\n     */\n    Iterable<ChangeItemMember> getMembers();\n\n    /**\n     * create a new RelationBean object from item members\n     *\n     * @return a relation bean object to use for AtlasBuilder.\n     */\n    Optional<RelationBean> getRelationBean();\n\n    /**\n     * @return the score of the item, actual meaning varies base on source type and conflation\n     *         process, value should always be between 0-1 with 0 for lowest rank and 1 for highest\n     *         rank. If score is not needed it should always return 1.\n     */\n    default double getScore()\n    {\n        return 1;\n    }\n\n    /**\n     * @return the source name from which the change set is generated.\n     */\n    String getSourceName();\n\n    /**\n     * @return tag map of this item\n     */\n    @Override\n    Map<String, String> getTags();\n\n    /**\n     * @return item type\n     */\n    ItemType getType();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/ChangeItemMember.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * Represents a member of a {@link ChangeItem}, only valid if {@link ChangeItem} type is\n * {@code ItemType.RELATION}.\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic interface ChangeItemMember\n{\n    /**\n     * @return id of the item member, which is a reference of the member's entity id.\n     */\n    long getIdentifier();\n\n    /**\n     * @return role of the member. refer to OSM WIKI for details about roles in different relation\n     *         type.\n     */\n    String getRole();\n\n    /**\n     * @return ItemType of the member\n     */\n    ItemType getType();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/ChangeSet.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * A ChangeSet is a set of {@link ChangeItem} generated from side files for a specific Atlas data\n * set. A side file is a data file generated from other sources or validation program to improve the\n * data quality and/or coverage.\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic interface ChangeSet extends Set<ChangeItem>, Serializable\n{\n    /**\n     * @param identifier\n     *            the identifier of changeItem\n     * @param type\n     *            item type\n     * @return true if item exists and false if not\n     */\n    boolean contains(long identifier, ItemType type);\n\n    /**\n     * @param identifier\n     *            the identifier of changeItem\n     * @param type\n     *            item type\n     * @param action\n     *            the action of change item\n     * @return true if item exists and false if not\n     */\n    boolean contains(long identifier, ItemType type, ChangeAction action);\n\n    /**\n     * Ideally there should only be one ChangeItem for the combination of identifier and ItemType.\n     * the method will ONLY return ONE item that matches. If the implementation allows multiple\n     * ChangeAction in one change set, please use {@link #get(long, ItemType, ChangeAction)}\n     *\n     * @param identifier\n     *            the identifier of change item\n     * @param type\n     *            the type of item\n     * @return the {@link ChangeItem} that matches the input.\n     * @see #get(long, ItemType, ChangeAction)\n     */\n    Optional<ChangeItem> get(long identifier, ItemType type);\n\n    /**\n     * @param identifier\n     *            the identifier of change item\n     * @param type\n     *            the type of item\n     * @param action\n     *            the action of change item\n     * @return the {@link ChangeItem} that matches the input\n     */\n    Optional<ChangeItem> get(long identifier, ItemType type, ChangeAction action);\n\n    /**\n     * human readable description of the change set\n     *\n     * @return description of change set\n     */\n    String getDescription();\n\n    /**\n     * @return the source name from which the change set is generated. In case of multiple sources\n     *         names are joined by comma(,)\n     */\n    Iterable<String> getSourceNames();\n\n    /**\n     * Change set version should be same as atlas version against which it's generated.\n     *\n     * @return version of change set, same as atlas version.\n     */\n    String getVersion();\n\n    /**\n     * @param action\n     *            action to filter\n     * @return a iterator for all {@link ChangeItem}s that matches the action\n     */\n    Iterator<ChangeItem> iterator(ChangeAction action);\n\n    /**\n     * @param type\n     *            type to filer\n     * @return a iterator for all {@link ChangeItem}s that matches the type\n     */\n    Iterator<ChangeItem> iterator(ItemType type);\n\n    /**\n     * @param type\n     *            type to filter\n     * @param action\n     *            action to filter\n     * @return a iterator for all {@link ChangeItem}s that matches both the type and the action\n     */\n    Iterator<ChangeItem> iterator(ItemType type, ChangeAction action);\n\n    /**\n     * @param description\n     *            description of the change set\n     */\n    void setDescription(String description);\n\n    /**\n     * set version of the ChangeSet, the version should be same as atlas version against which it's\n     * generated.\n     *\n     * @param version\n     *            version of the change set\n     */\n    void setVersion(String version);\n\n    /**\n     * @param action\n     *            {@link ChangeAction} the return sub set should have.\n     * @return a set of all items with given action\n     */\n    Set<ChangeItem> subSet(ChangeAction action);\n\n    /**\n     * @param type\n     *            the type of item\n     * @return a set of all items with given type\n     */\n    Set<ChangeItem> subSet(ItemType type);\n\n    /**\n     * get all items with given type and action.\n     *\n     * @param type\n     *            the type of item\n     * @param action\n     *            item action\n     * @return a set of items with given type and action\n     */\n    Set<ChangeItem> subSet(ItemType type, ChangeAction action);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/ChangeSetAtlasBuilder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * ChangeSetAtlasBuilder takes an original atlas and accept a ChangeSet to build a new atlas with\n * all changes applied. Changes follows CUD(Create, Update, Delete) pattern with partial support of\n * edges and relations.\n * <p>\n * <b>this class is not thread safe</b>\n * </p>\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic class ChangeSetAtlasBuilder\n{\n    private static final Logger logger = LoggerFactory.getLogger(ChangeSetAtlasBuilder.class);\n    private static final int MAXIMUM_RELATION_LOOPS = 500;\n    private final PackedAtlasBuilder builder;\n    private final Atlas originalAtlas;\n    private final ChangeSet changeSet;\n    private Atlas conflatedAtlas;\n\n    public ChangeSetAtlasBuilder(final Atlas atlas, final ChangeSet changeSet)\n    {\n        this.originalAtlas = atlas;\n        this.changeSet = changeSet;\n        this.builder = new PackedAtlasBuilder().withMetaData(new AtlasMetaData())\n                .withSizeEstimates(atlas.size());\n    }\n\n    /**\n     * build and return atlas object\n     *\n     * @return atlas been built\n     */\n    public Atlas get()\n    {\n        if (this.conflatedAtlas != null)\n        {\n            return this.conflatedAtlas;\n        }\n        handleSimple(this.originalAtlas.nodes());\n        handleSimple(this.originalAtlas.points());\n        handleSimple(this.originalAtlas.lines());\n        handleSimple(this.originalAtlas.edges());\n        handleSimple(this.originalAtlas.areas());\n        handleRelations();\n        this.conflatedAtlas = this.builder.get();\n        return this.conflatedAtlas;\n    }\n\n    private void addItem(final long identifier, final ItemType type,\n            final Iterable<Location> geometry, final Map<String, String> tags)\n    {\n        switch (type)\n        {\n            case POINT:\n                this.builder.addPoint(identifier, (Location) geometry, tags);\n                break;\n            case NODE:\n                this.builder.addNode(identifier, (Location) geometry, tags);\n                break;\n            case LINE:\n                this.builder.addLine(identifier, (PolyLine) geometry, tags);\n                break;\n            case EDGE:\n                this.builder.addEdge(identifier, (PolyLine) geometry, tags);\n                break;\n            case AREA:\n                this.builder.addArea(identifier, (Polygon) geometry, tags);\n                break;\n            case RELATION:\n            default:\n                throw new IllegalArgumentException(\"addItem can't take relation type\");\n        }\n    }\n\n    private void handleRelations()\n    {\n        this.originalAtlas.relations().forEach(relation ->\n        {\n            if (this.changeSet.contains(relation.getIdentifier(), ItemType.RELATION,\n                    ChangeAction.DELETE))\n            {\n                return;\n            }\n            final Optional<ChangeItem> optionalItem = this.changeSet.get(relation.getIdentifier(),\n                    ItemType.RELATION, ChangeAction.UPDATE);\n            if (optionalItem.isPresent())\n            {\n                final ChangeItem changeItem = optionalItem.get();\n                this.builder.addRelation(relation.getIdentifier(), relation.getOsmIdentifier(),\n                        changeItem.getRelationBean().get(), changeItem.getTags());\n            }\n            else\n            {\n                final RelationBean bean = new RelationBean();\n                relation.members()\n                        .forEach(member -> bean.addItem(member.getEntity().getIdentifier(),\n                                member.getRole(), member.getEntity().getType()));\n                this.builder.addRelation(relation.getIdentifier(), relation.osmRelationIdentifier(),\n                        bean, relation.getTags());\n            }\n        });\n        // Add all the relations that do not have members that are relations.\n        Set<Long> stagedRelationIdentifiers = new HashSet<>();\n        final Iterator<ChangeItem> iterator = this.changeSet.iterator(ItemType.RELATION,\n                ChangeAction.CREATE);\n        while (iterator.hasNext())\n        {\n            final ChangeItem relationMatchResult = iterator.next();\n            if (StreamSupport.stream(relationMatchResult.getMembers().spliterator(), false)\n                    .anyMatch(member -> member.getType() == ItemType.RELATION))\n            {\n                stagedRelationIdentifiers.add(relationMatchResult.getIdentifier());\n            }\n            else\n            {\n                this.builder.addRelation(relationMatchResult.getIdentifier(),\n                        relationMatchResult.getIdentifier(),\n                        relationMatchResult.getRelationBean().get(), relationMatchResult.getTags());\n            }\n        }\n        // Add all the other relations\n        int iterations = 0;\n        while (++iterations < MAXIMUM_RELATION_LOOPS && !stagedRelationIdentifiers.isEmpty())\n        {\n            logger.trace(\"Copying relations level {} deep.\", iterations);\n            final Set<Long> stagedRelationIdentifiersCopy = new HashSet<>();\n            for (final Long relationIdentifier : stagedRelationIdentifiers)\n            {\n                final Optional<ChangeItem> optionalItem = this.changeSet.get(relationIdentifier,\n                        ItemType.RELATION, ChangeAction.CREATE);\n                if (!optionalItem.isPresent())\n                {\n                    logger.error(\"can't find relation with id {}\", relationIdentifier);\n                    continue;\n                }\n                final ChangeItem relationItem = optionalItem.get();\n                final boolean skip = StreamSupport\n                        .stream(relationItem.getMembers().spliterator(), false)\n                        .allMatch(member -> member.getType() == ItemType.RELATION\n                                && this.builder.peek().relation(member.getIdentifier()) == null);\n                if (!skip)\n                {\n                    this.builder.addRelation(relationItem.getIdentifier(),\n                            relationItem.getIdentifier(), relationItem.getRelationBean().get(),\n                            relationItem.getTags());\n                }\n                else\n                {\n                    stagedRelationIdentifiersCopy.add(relationIdentifier);\n                }\n            }\n            stagedRelationIdentifiers = stagedRelationIdentifiersCopy;\n        }\n        if (iterations >= MAXIMUM_RELATION_LOOPS)\n        {\n            throw new CoreException(\n                    \"There might be a loop in relations! It took more than {} loops to copy the relation.\",\n                    MAXIMUM_RELATION_LOOPS);\n        }\n    }\n\n    private void handleSimple(final Iterable<? extends AtlasItem> items)\n    {\n        items.forEach(item ->\n        {\n            if (this.changeSet.contains(item.getIdentifier(), item.getType(), ChangeAction.DELETE))\n            {\n                return;\n            }\n            final Optional<ChangeItem> optionalItem = this.changeSet.get(item.getIdentifier(),\n                    item.getType(), ChangeAction.UPDATE);\n            if (optionalItem.isPresent())\n            {\n                final ChangeItem changeItem = optionalItem.get();\n                addItem(changeItem.getIdentifier(), item.getType(), changeItem.getGeometry(),\n                        changeItem.getTags());\n            }\n            else\n            {\n                addItem(item.getIdentifier(), item.getType(), item.getRawGeometry(),\n                        item.getTags());\n            }\n        });\n        final Iterator<? extends AtlasItem> iterator = items.iterator();\n        if (!iterator.hasNext())\n        {\n            return;\n        }\n        final ItemType type = iterator.next().getType();\n        this.changeSet.iterator(type, ChangeAction.CREATE).forEachRemaining(changeItem ->\n        {\n            addItem(changeItem.getIdentifier(), type, changeItem.getGeometry(),\n                    changeItem.getTags());\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/ChangeSetDeserializer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * Deerializer interface for {@link ChangeSet}.\n *\n * @author yiqing-jin\n * @author mkalender\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic interface ChangeSetDeserializer extends Supplier<Optional<ChangeSet>>, AutoCloseable\n{\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/ChangeSetSerializer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.util.function.Consumer;\n\n/**\n * Serializer interface for {@link ChangeSet}.\n *\n * @author yiqing-jin\n * @author mkalender\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic interface ChangeSetSerializer extends Consumer<ChangeSet>, AutoCloseable\n{\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/GeoJSONChangeSetSerializer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\n\n/**\n * Serializes {@link ChangeSet} objects and writes them into {@link OutputStream}s in geojson\n * format.\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic class GeoJSONChangeSetSerializer implements ChangeSetSerializer\n{\n    private final WritableResource resource;\n\n    public GeoJSONChangeSetSerializer(final WritableResource resourceToWriteInto)\n    {\n        this.resource = resourceToWriteInto;\n    }\n\n    @Override\n    public void accept(final ChangeSet changeSet)\n    {\n        toGeoJson(changeSet).save(this.resource);\n    }\n\n    @Override\n    public void close() throws Exception\n    {\n        // Do nothing as close is handled in GeoJsonObject.save method\n    }\n\n    public GeoJsonObject toGeoJson(final ChangeSet changeSet)\n    {\n        final List<GeoJsonBuilder.LocationIterableProperties> collection = new ArrayList<>();\n        changeSet.iterator().forEachRemaining(changeItem ->\n        {\n            if (changeItem.getGeometry() == null)\n            {\n                return;\n            }\n            changeItem.getTags().put(\"action\", changeItem.getAction().name());\n            changeItem.getTags().put(\"id\", String.valueOf(changeItem.getIdentifier()));\n            collection.add(new GeoJsonBuilder.LocationIterableProperties(changeItem.getGeometry(),\n                    changeItem.getTags()));\n        });\n        final GeoJsonBuilder builder = new GeoJsonBuilder();\n\n        return builder.create(collection);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/MutableChangeItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * This interface is designed to be used by data enhancing and data merging programs which may\n * require frequently changing values on the fly.\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic interface MutableChangeItem extends ChangeItem\n{\n    void addAllMembers(Iterable<ChangeItemMember> members) throws CoreException;\n\n    void addMember(ChangeItemMember member) throws CoreException;\n\n    boolean removeMember(long identifier, String role, ItemType type) throws CoreException;\n\n    void setAction(ChangeAction action);\n\n    void setGeometry(Iterable<Location> geometry) throws CoreException;\n\n    void setIdentifier(long identifier);\n\n    /**\n     * @param score\n     *            the score of the item, actual meaning varies base on source type and conflation\n     *            process\n     */\n    void setScore(double score);\n\n    void setSourceName(String sourceName);\n\n    void setTags(Map<String, String> tags);\n\n    void setType(ItemType type);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/SimpleChangeItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * A implementation of {@link ChangeItem} that should work for most cases.\n *\n * @author Yiqing Jin\n * @author mkalender\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic class SimpleChangeItem implements MutableChangeItem\n{\n    private static final long serialVersionUID = 3817694187693336803L;\n\n    private String sourceName;\n    private Map<String, String> tags;\n    private long identifier;\n    private ItemType type;\n    private ChangeAction action;\n    private Iterable<Location> geometry;\n    private double score = 1;\n    private final Set<ChangeItemMember> members;\n\n    public SimpleChangeItem()\n    {\n        this.members = new HashSet<>();\n    }\n\n    public SimpleChangeItem(final long identifier, final String sourceName, final ItemType type,\n            final ChangeAction action, final Iterable<Location> geometry,\n            final Map<String, String> tags)\n    {\n        this.identifier = identifier;\n        this.sourceName = sourceName;\n        this.type = type;\n        this.action = action;\n        this.geometry = geometry;\n        this.tags = tags;\n        this.members = new HashSet<>();\n    }\n\n    @Override\n    public void addAllMembers(final Iterable<ChangeItemMember> members) throws CoreException\n    {\n        assertTypeIsRelation();\n        members.forEach(member -> this.members.add(member));\n    }\n\n    @Override\n    public void addMember(final ChangeItemMember member) throws CoreException\n    {\n        assertTypeIsRelation();\n        this.members.add(member);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (!(other instanceof SimpleChangeItem))\n        {\n            return false;\n        }\n\n        final SimpleChangeItem that = (SimpleChangeItem) other;\n        return this.getIdentifier() == that.getIdentifier() && this.getType() == that.getType()\n                && this.getAction() == that.getAction()\n                && StringUtils.equals(this.getSourceName(), that.getSourceName())\n                && this.getScore() == that.getScore()\n                && Iterables.equals(this.getMembers(), that.getMembers())\n                && Iterables.equals(this.getGeometry(), that.getGeometry());\n    }\n\n    @Override\n    public ChangeAction getAction()\n    {\n        return this.action;\n    }\n\n    @Override\n    public Iterable<Location> getGeometry()\n    {\n        return this.geometry;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Iterable<ChangeItemMember> getMembers()\n    {\n        return this.members;\n    }\n\n    @Override\n    public Optional<RelationBean> getRelationBean()\n    {\n        assertTypeIsRelation();\n        if (this.members.isEmpty())\n        {\n            return Optional.empty();\n        }\n        final RelationBean bean = new RelationBean();\n        this.members.forEach(\n                member -> bean.addItem(member.getIdentifier(), member.getRole(), member.getType()));\n        return Optional.of(bean);\n    }\n\n    @Override\n    public double getScore()\n    {\n        return this.score;\n    }\n\n    @Override\n    public String getSourceName()\n    {\n        return this.sourceName;\n    }\n\n    @Override\n    public Optional<String> getTag(final String key)\n    {\n        return Optional.ofNullable(this.tags.get(key));\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public ItemType getType()\n    {\n        return this.type;\n    }\n\n    @Override\n    /**\n     * ItemType and ChangeAction combined should have no more than 9 variants and ideally should\n     * have less than 3. so identifier itself should be good enough for hash code\n     */\n    public int hashCode()\n    {\n        return (int) this.identifier;\n    }\n\n    @Override\n    public boolean removeMember(final long identifier, final String role, final ItemType type)\n            throws CoreException\n    {\n        assertTypeIsRelation();\n        return this.members.remove(new SimpleChangeItemMember(identifier, role, type));\n    }\n\n    @Override\n    public void setAction(final ChangeAction action)\n    {\n        this.action = action;\n    }\n\n    @Override\n    public void setGeometry(final Iterable<Location> geometry) throws CoreException\n    {\n        assertTypeIsNotRelation();\n        this.geometry = geometry;\n    }\n\n    @Override\n    public void setIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n    }\n\n    public void setMembers(final Iterable<ChangeItemMember> members)\n    {\n        this.members.clear();\n        this.addAllMembers(members);\n    }\n\n    @Override\n    public void setScore(final double score)\n    {\n        this.score = score;\n    }\n\n    @Override\n    public void setSourceName(final String sourceName)\n    {\n        this.sourceName = sourceName;\n    }\n\n    @Override\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags;\n    }\n\n    @Override\n    public void setType(final ItemType type)\n    {\n        this.type = type;\n    }\n\n    private void assertTypeIsNotRelation()\n    {\n        if (this.getType() == ItemType.RELATION)\n        {\n            throw new CoreException(\"Cannot execute this on a ChangeItem with type {}\",\n                    this.getType());\n        }\n    }\n\n    private void assertTypeIsRelation()\n    {\n        if (this.getType() != ItemType.RELATION)\n        {\n            throw new CoreException(\"Cannot execute this on a ChangeItem with type {}\",\n                    this.getType());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/SimpleChangeItemMember.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * A simple implementation of {@link ChangeItemMember} interface.\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic class SimpleChangeItemMember implements ChangeItemMember, Serializable\n{\n    private static final long serialVersionUID = 3261727439156010800L;\n\n    private long identifier;\n    private ItemType type;\n    private String role;\n\n    public SimpleChangeItemMember(final long identifier, final String role, final ItemType type)\n    {\n        this.identifier = identifier;\n        this.type = type;\n        this.role = role;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (!(other instanceof SimpleChangeItemMember))\n        {\n            return false;\n        }\n        final SimpleChangeItemMember that = (SimpleChangeItemMember) other;\n        return this.getIdentifier() == that.getIdentifier() && this.getType() == that.getType()\n                && StringUtils.equals(this.getRole(), that.getRole());\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public String getRole()\n    {\n        return this.role;\n    }\n\n    @Override\n    public ItemType getType()\n    {\n        return this.type;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(this.getIdentifier(), this.getRole(), this.getType());\n    }\n\n    public void setIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n    }\n\n    public void setRole(final String role)\n    {\n        this.role = role;\n    }\n\n    public void setType(final ItemType type)\n    {\n        this.type = type;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[Identifier: \");\n        builder.append(this.getIdentifier());\n        builder.append(\", Role: \");\n        builder.append(this.getRole());\n        builder.append(\", Type: \");\n        builder.append(this.getType());\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/changeset/SimpleChangeSet.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\nimport com.google.common.collect.AbstractIterator;\n\n/**\n * A simple implementation of {@link ChangeSet} interface. This should be good for most common use\n * cases but if necessary user can choose to implement their own or extend from this one.\n * <p>\n * <b>This implementation is not thread safe</b>\n * </p>\n *\n * @author Yiqing Jin\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic class SimpleChangeSet implements ChangeSet\n{\n    private static final long serialVersionUID = -6499530503182134327L;\n\n    // internal data structure containing all data objects. key should be combination of identifier,\n    // ItemType and ChangeAction.\n    private final Map<String, ChangeItem> map = new HashMap<>();\n    private String version;\n    private String description;\n\n    private static String computeKey(final long identifier, final ItemType type,\n            final ChangeAction action)\n    {\n        return identifier + type.toShortString() + action;\n    }\n\n    public SimpleChangeSet()\n    {\n        this.version = \"unknown\";\n        this.description = \"\";\n    }\n\n    @Override\n    public boolean add(final ChangeItem changeItem)\n    {\n        return this.map.put(computeKey(changeItem), changeItem) != null;\n    }\n\n    @Override\n    public boolean addAll(final Collection<? extends ChangeItem> changeItems)\n    {\n        boolean state = false;\n        for (final ChangeItem changeItem : changeItems)\n        {\n            state = add(changeItem) || state;\n        }\n        return state;\n    }\n\n    @Override\n    public void clear()\n    {\n        this.map.clear();\n    }\n\n    @Override\n    public boolean contains(final long identifier, final ItemType type)\n    {\n        for (final ChangeAction action : ChangeAction.values())\n        {\n            if (this.map.containsKey(computeKey(identifier, type, action)))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean contains(final long identifier, final ItemType type, final ChangeAction action)\n    {\n        return this.map.containsKey(computeKey(identifier, type, action));\n    }\n\n    @Override\n    public boolean contains(final Object object)\n    {\n        if (!(object instanceof ChangeItem))\n        {\n            return false;\n        }\n        return this.map.containsKey(computeKey((ChangeItem) object));\n    }\n\n    @Override\n    public boolean containsAll(final Collection<?> collection)\n    {\n        return collection.stream().allMatch(this::contains);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (!(other instanceof SimpleChangeSet))\n        {\n            return false;\n        }\n        final SimpleChangeSet that = (SimpleChangeSet) other;\n        return StringUtils.equals(this.getVersion(), that.getVersion())\n                && StringUtils.equals(this.getDescription(), that.getDescription())\n                && this.map.equals(that.map);\n    }\n\n    @Override\n    public Optional<ChangeItem> get(final long identifier, final ItemType type)\n    {\n        for (final ChangeAction action : ChangeAction.values())\n        {\n            final ChangeItem item = this.map.get(computeKey(identifier, type, action));\n            if (item != null)\n            {\n                return Optional.of(item);\n            }\n        }\n        return Optional.empty();\n    }\n\n    @Override\n    public Optional<ChangeItem> get(final long identifier, final ItemType type,\n            final ChangeAction action)\n    {\n        return Optional.ofNullable(this.map.get(computeKey(identifier, type, action)));\n    }\n\n    @Override\n    public String getDescription()\n    {\n        return this.description;\n    }\n\n    @Override\n    public Iterable<String> getSourceNames()\n    {\n        final List<String> sourceNames = new ArrayList<>();\n        this.iterator().forEachRemaining(changeItem -> sourceNames.add(changeItem.getSourceName()));\n        return sourceNames;\n    }\n\n    @Override\n    public String getVersion()\n    {\n        return this.version;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(this.getVersion(), this.getDescription(), this.map);\n    }\n\n    @Override\n    public boolean isEmpty()\n    {\n        return this.map.isEmpty();\n    }\n\n    @Override\n    public Iterator<ChangeItem> iterator()\n    {\n        return this.map.values().iterator();\n    }\n\n    @Override\n    public Iterator<ChangeItem> iterator(final ChangeAction action)\n    {\n        return filterIterator(iterator(), item -> item.getAction() == action);\n    }\n\n    @Override\n    public Iterator<ChangeItem> iterator(final ItemType type)\n    {\n        return filterIterator(iterator(), item -> item.getType() == type);\n    }\n\n    @Override\n    public Iterator<ChangeItem> iterator(final ItemType type, final ChangeAction action)\n    {\n        return filterIterator(iterator(),\n                item -> item.getType() == type && item.getAction() == action);\n    }\n\n    @Override\n    public boolean remove(final Object obj)\n    {\n        return this.map.remove(computeKey((ChangeItem) obj)) != null;\n    }\n\n    @Override\n    public boolean removeAll(final Collection<?> collection)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public boolean retainAll(final Collection<?> collection)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void setDescription(final String description)\n    {\n        this.description = description;\n    }\n\n    @Override\n    public void setVersion(final String version)\n    {\n        this.version = version;\n    }\n\n    @Override\n    public int size()\n    {\n        return this.map.size();\n    }\n\n    @Override\n    public Set<ChangeItem> subSet(final ChangeAction action)\n    {\n        return this.stream().filter(item -> item.getAction() == action).collect(Collectors.toSet());\n    }\n\n    @Override\n    public Set<ChangeItem> subSet(final ItemType type)\n    {\n        return this.stream().filter(item -> item.getType() == type).collect(Collectors.toSet());\n    }\n\n    @Override\n    public Set<ChangeItem> subSet(final ItemType type, final ChangeAction action)\n    {\n        return this.stream().filter(item -> item.getType() == type && item.getAction() == action)\n                .collect(Collectors.toSet());\n    }\n\n    @Override\n    public Object[] toArray()\n    {\n        return this.map.values().toArray();\n    }\n\n    @Override\n    public <T> T[] toArray(final T[] array)\n    {\n        return this.map.values().toArray(array);\n    }\n\n    private String computeKey(final ChangeItem item)\n    {\n        return computeKey(item.getIdentifier(), item.getType(), item.getAction());\n    }\n\n    private Iterator<ChangeItem> filterIterator(final Iterator<ChangeItem> parent,\n            final Predicate<ChangeItem> predicate)\n    {\n        return new AbstractIterator<ChangeItem>()\n        {\n            @Override\n            protected ChangeItem computeNext()\n            {\n                while (parent.hasNext())\n                {\n                    final ChangeItem item = parent.next();\n                    if (item != null && predicate.test(item))\n                    {\n                        return item;\n                    }\n                }\n                return endOfData();\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AbstractAtlasOutputTestSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentHashMap.KeySetView;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.ImmutableList;\n\n/**\n * Shared code for generating a single text-based test atlas for any building that passes the filter\n * as defined by concrete subclasses.\n *\n * @author cstaylor\n */\nabstract class AbstractAtlasOutputTestSubCommand extends AbstractAtlasSubCommand\n{\n    private static final Switch<Path> OUTPUT_TO_PACKED_ATLAS_PARAMETER = new Switch<>(\n            \"packed-output\", \"Outputs the found atlas pieces into a packed atlas for testing\",\n            Paths::get, Optionality.OPTIONAL);\n    private static final Switch<Path> OUTPUT_TO_TEXT_PARAMETER = new Switch<>(\"text-output\",\n            \"Outputs the found atlas pieces into text for testing\", Paths::get,\n            Optionality.OPTIONAL);\n\n    private static final Switch<Double> DISTANCE_IN_METERS_PARAMETER = new Switch<>(\"expand\",\n            \"Expands the search bounds by this optional parameter in meters\", Double::new,\n            Optionality.OPTIONAL);\n\n    private KeySetView<Atlas, Boolean> subAtlases;\n\n    private Optional<Double> distanceInMeters;\n\n    private Path outputTextPath;\n    private Path packedAtlasPath;\n\n    protected AbstractAtlasOutputTestSubCommand(final String name, final String description)\n    {\n        super(name, description);\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_TO_PACKED_ATLAS_PARAMETER, OUTPUT_TO_TEXT_PARAMETER,\n                DISTANCE_IN_METERS_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\n                \"-packed-output=/output/to/packed/atlas : the file we should send the packed Atlas to if it exists\\n\");\n        writer.printf(\n                \"-text-output=/output/to/text/file : the file we should send the text data of the Atlas if it exists\\n\");\n        writer.printf(\n                \"-expand=[distance in meters] : how far we should expand around the building we've found for the subatlas we're saving\\n\");\n    }\n\n    protected abstract boolean filter(AtlasEntity entity);\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        if (this.subAtlases.isEmpty())\n        {\n            LoggerFactory.getLogger(getClass()).info(\"No items found\");\n            return -1;\n        }\n        if (this.packedAtlasPath != null)\n        {\n            final Atlas atlas = new MultiAtlas(this.subAtlases);\n            try\n            {\n                final PackedAtlas saveMe = new PackedAtlasCloner().cloneFrom(atlas);\n                Files.createDirectories(this.packedAtlasPath.getParent());\n                saveMe.save(new File(this.packedAtlasPath.toFile()));\n                LoggerFactory.getLogger(getClass()).info(\"Packed atlas saved to {}\",\n                        this.packedAtlasPath);\n            }\n            catch (final IOException oops)\n            {\n                throw new CoreException(\"Error when saving packed atlas\", oops);\n            }\n\n        }\n        if (this.outputTextPath != null)\n        {\n            new MultiAtlas(ImmutableList.copyOf(this.subAtlases.iterator()))\n                    .saveAsText(new File(this.outputTextPath.toFile()));\n            LoggerFactory.getLogger(getClass()).info(\"Text atlas saved to {}\", this.outputTextPath);\n        }\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        StreamSupport.stream(atlas.entities(i -> i.getOsmIdentifier() > 0).spliterator(), false)\n                .filter(this::filter).forEach(this::output);\n\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    protected void start(final CommandMap command)\n    {\n        this.subAtlases = ConcurrentHashMap.newKeySet();\n        this.distanceInMeters = (Optional<Double>) command.getOption(DISTANCE_IN_METERS_PARAMETER);\n        ((Optional<Path>) command.getOption(OUTPUT_TO_TEXT_PARAMETER))\n                .ifPresent(path -> this.outputTextPath = path);\n        ((Optional<Path>) command.getOption(OUTPUT_TO_PACKED_ATLAS_PARAMETER))\n                .ifPresent(path -> this.packedAtlasPath = path);\n\n        if (this.outputTextPath == null && this.packedAtlasPath == null)\n        {\n            throw new CoreException(\"Either -packed-output or -text-output must have a value\");\n        }\n    }\n\n    private void output(final AtlasEntity item)\n    {\n        Rectangle rectangle = item.bounds();\n        if (this.distanceInMeters.isPresent())\n        {\n            rectangle = rectangle.expand(Distance.meters(this.distanceInMeters.get()));\n        }\n        item.getAtlas().subAtlas(rectangle, AtlasCutType.SOFT_CUT).ifPresent(this.subAtlases::add);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AbstractAtlasSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Flag;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleSubCommand;\n\n/**\n * Helper class that makes it easier to implement ReaderCommands that need loaded atlases\n *\n * @author cstaylor\n */\npublic abstract class AbstractAtlasSubCommand implements FlexibleSubCommand\n{\n    private static final Switch<File> INPUT_FOLDER_PARAMETER = new Switch<>(\"input\",\n            \"Input atlas file or folder containing atlas files to load\", File::new,\n            Optionality.REQUIRED);\n\n    private static final Flag COMBINE_PARAMETER = new Flag(\"combine\",\n            \"Will combine all atlases found into a MultiAtlas before reading the metadata\");\n\n    private static final Flag PARALLEL_FLAG = new Flag(\"parallel\",\n            \"Will process multiple atlases in parallel\");\n\n    private final String name;\n\n    private final String description;\n\n    protected AbstractAtlasSubCommand(final String name, final String description)\n    {\n        this.name = name;\n        this.description = description;\n    }\n\n    @Override\n    public int execute(final CommandMap command)\n    {\n        start(command);\n        final File path = (File) command.get(INPUT_FOLDER_PARAMETER);\n\n        Stream<Atlas> atlases = path.listFilesRecursively().stream()\n                .filter(AtlasResourceLoader.HAS_ATLAS_EXTENSION)\n                .map(atlas -> new AtlasResourceLoader().load(atlas));\n\n        if ((Boolean) command.get(PARALLEL_FLAG))\n        {\n            atlases = atlases.parallel();\n        }\n\n        if ((Boolean) command.get(COMBINE_PARAMETER))\n        {\n            handle(new MultiAtlas(atlases.collect(Collectors.toList())), command);\n        }\n        else\n        {\n            atlases.forEach(atlas ->\n            {\n                handle(atlas, command);\n            });\n        }\n        return finish(command);\n    }\n\n    @Override\n    public String getDescription()\n    {\n        return this.description;\n    }\n\n    @Override\n    public final String getName()\n    {\n        return this.name;\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return new SwitchList().with(INPUT_FOLDER_PARAMETER, COMBINE_PARAMETER, PARALLEL_FLAG);\n    }\n\n    /**\n     * After all atlas files have been handled, the subclass can override this method for a final\n     * notification and processing. The return value is sent back to the caller through System.exit\n     *\n     * @param command\n     *            arguments to this subcommand that may affect processing\n     * @return a status value retured through System.exit\n     */\n    protected int finish(final CommandMap command)\n    {\n        return 0;\n    }\n\n    /**\n     * Subclasses will implement this method for processing each atlas object as it is loaded.\n     *\n     * @param atlas\n     *            the atlas to process\n     * @param command\n     *            arguments to this subcommand that may affect processing\n     */\n    protected abstract void handle(Atlas atlas, CommandMap command);\n\n    /**\n     * Subclasses can override this method if they want to do something once before processing all\n     * of the atlases\n     *\n     * @param command\n     *            arguments to this subcommand that may affect processing\n     */\n    protected void start(final CommandMap command)\n    {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasCommandConstants.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\n/**\n * Putting all of those strings in one place\n *\n * @author cstaylor\n */\npublic interface AtlasCommandConstants\n{\n    String INPUT_PARAMETER_DESCRIPTION = \"-input=/path/to/atlas/files : the atlas files we want to load\\n\";\n    String INPUT_ZOOM_LEVEL = \"-zoom_level=<the zoom level you want to check>\\n\";\n    String OUTPUT_FOLDER_DESCRIPTION = \"-output=/path/to/atlas/output/to/save : the path to output atlas files\\n\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasCountriesSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Lists all of the countries found in the metadata within the input atlas files\n *\n * @author cstaylor\n */\npublic class AtlasCountriesSubCommand extends AbstractAtlasSubCommand\n{\n    private final Set<String> countries;\n\n    public AtlasCountriesSubCommand()\n    {\n        super(\"countries\", \"lists all of the countries found in alphabetical order\");\n        this.countries = new TreeSet<>();\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        System.out.printf(\"Countries: %d\\n\", this.countries.size());\n        this.countries.forEach(System.out::println);\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        atlas.metaData().getCountry().ifPresent(this.countries::add);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasFeatureCountsSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.BufferedOutputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNodeFinder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuildingFinder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.islands.ComplexIslandFinder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.restriction.ComplexTurnRestrictionFinder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.finder.ComplexWaterEntityFinder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\nimport com.google.common.collect.Table;\nimport com.google.common.collect.TreeBasedTable;\n\n/**\n * Prints out the number of features for the given atlas files\n *\n * @author ahsieh\n */\npublic class AtlasFeatureCountsSubCommand extends AbstractAtlasSubCommand\n{\n    /**\n     * The types of Atlas entities to be processed (edges, lines, areas, etc.)\n     */\n    protected enum AtlasType\n    {\n        NODE,\n        LINE,\n        AREA,\n        POINT,\n        EDGE,\n        RELATION,\n        COMPLEX_BUILDING,\n        COMPLEX_WATER,\n        COMPLEX_ISLAND,\n        COMPLEX_TURN_RESTRICTION,\n        COMPLEX_BIG_NODE\n    }\n\n    private static final Switch<File> OUTPUT_PARAMETER = new Switch<>(\"output\",\n            \"The output file to save the statistics\", value -> new File(value),\n            Optionality.REQUIRED);\n\n    private final Table<String, AtlasType, Long> featureCounts;\n\n    public AtlasFeatureCountsSubCommand()\n    {\n        super(\"featureCounts\", \"lists counts of objects found\");\n        this.featureCounts = TreeBasedTable.create();\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        final File file = (File) command.get(OUTPUT_PARAMETER);\n\n        try (PrintStream out = new PrintStream(\n                new BufferedOutputStream(new FileOutputStream(file.getFile(), true))))\n        {\n            for (final String country : this.featureCounts.rowKeySet())\n            {\n                for (final AtlasType type : AtlasType.values())\n                {\n                    out.println(String.format(\"%s-%s: %d\", country, type,\n                            this.featureCounts.contains(country, type)\n                                    ? this.featureCounts.get(country, type)\n                                    : 0));\n                }\n\n                out.println();\n            }\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Error writing to file: {}\",\n                    file.getAbsolutePathString().toString(), oops);\n        }\n\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        for (final AtlasType type : AtlasType.values())\n        {\n            updateHashMapForAtlasType(type, atlas);\n        }\n    }\n\n    private void updateHashMapForAtlasType(final AtlasType type, final Atlas atlas)\n    {\n        long oldCount = 0;\n        final long additionalCount;\n        final String country = atlas.metaData().getCountry().orElseGet(() -> \"UNKNOWN\");\n\n        switch (type)\n        {\n            case NODE:\n                additionalCount = atlas.numberOfNodes();\n                break;\n            case LINE:\n                additionalCount = atlas.numberOfLines();\n                break;\n            case AREA:\n                additionalCount = atlas.numberOfAreas();\n                break;\n            case POINT:\n                additionalCount = atlas.numberOfPoints();\n                break;\n            case EDGE:\n                additionalCount = atlas.numberOfEdges();\n                break;\n            case RELATION:\n                additionalCount = atlas.numberOfRelations();\n                break;\n            case COMPLEX_BUILDING:\n                additionalCount = Iterables.count(new ComplexBuildingFinder().find(atlas), i -> 1L);\n                break;\n            case COMPLEX_WATER:\n                additionalCount = Iterables.count(new ComplexWaterEntityFinder().find(atlas),\n                        i -> 1L);\n                break;\n            case COMPLEX_ISLAND:\n                additionalCount = Iterables.count(new ComplexIslandFinder().find(atlas), i -> 1L);\n                break;\n            case COMPLEX_TURN_RESTRICTION:\n                additionalCount = Iterables.count(new ComplexTurnRestrictionFinder().find(atlas),\n                        i -> 1L);\n                break;\n            case COMPLEX_BIG_NODE:\n                additionalCount = Iterables.count(new BigNodeFinder().find(atlas), i -> 1L);\n                break;\n            default:\n                throw new CoreException(\"Unexpected AtlasType: \" + type.toString());\n        }\n\n        // check if there's already a value in the Table\n        synchronized (this)\n        {\n            if (this.featureCounts.contains(country, type))\n            {\n                oldCount = this.featureCounts.get(country, type);\n            }\n\n            this.featureCounts.put(country, type, oldCount + additionalCount);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasFindByAtlasIdentifierSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasObject;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Searches a collection of Atlases for a set of ids. The Atlases containing each id are reported.\n * Optionally, all Atlases that contain one of the ids are joined and output to a single Atlas file.\n *\n * @author bbreithaupt\n */\npublic class AtlasFindByAtlasIdentifierSubCommand extends AbstractAtlasSubCommand\n{\n    private static final Command.Switch<Set<Long>> ATLAS_ID_PARAMETER = new Command.Switch<>(\"id\",\n            \"List of comma-delimited Atlas identifiers\",\n            possibleMultipleOSMIdentifier -> Stream.of(possibleMultipleOSMIdentifier.split(\",\"))\n                    .map(Long::parseLong).collect(Collectors.toSet()),\n            Command.Optionality.REQUIRED);\n\n    private static final Command.Switch<String> JOINED_OUTPUT_PARAMETER = new Command.Switch<>(\n            \"joinedOutput\",\n            \"The Atlas file to save the joined output to (optional). If not passed the found shards will not be joined and only appear in the console.\",\n            String::toString, Command.Optionality.OPTIONAL);\n\n    private final Set<Long> identifiers = new HashSet<>();\n\n    private final Set<String> shardNames = new HashSet<>();\n\n    public AtlasFindByAtlasIdentifierSubCommand()\n    {\n        super(\"find-atlas-id\",\n                \"Find which atlas files contain particular Atlas features using a given set of Atlas identifiers\");\n    }\n\n    @Override\n    public Command.SwitchList switches()\n    {\n        return super.switches().with(ATLAS_ID_PARAMETER, JOINED_OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.print(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.print(\"-id=1000000,2000000 : comma separated Atlas identifiers to search for%n\");\n        writer.print(\"-joinedOutput=path/to/joined.atlas : the path to the output Atlas file%n\");\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        final Optional output = command.getOption(JOINED_OUTPUT_PARAMETER);\n        // If joining is requested and there are shards to join...\n        if (output.isPresent() && !this.shardNames.isEmpty())\n        {\n            System.out.printf(\"Stitching shards and saving to output %s%n\", output.get());\n            // Use AtlasJoinerSubCommand to join found atlases\n            AtlasReader.main(\"join\",\n                    String.format(\"-input=%s\", command.get(super.switches().get(0))),\n                    String.format(\"-output=%s\", output.get()),\n                    String.format(\"-atlases=%s\", String.join(\",\", this.shardNames)));\n        }\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        // Get all atlas entities with ids matching the input list\n        atlas.entities(identifierCheck()).forEach(item ->\n        {\n            // Print atlas and item information\n            System.out.print(formatAtlasObject(item));\n            // Record shard name\n            this.shardNames.add(atlas.getName());\n        });\n    }\n\n    @Override\n    protected void start(final CommandMap command)\n    {\n        // Collect ids\n        this.identifiers.addAll((Set) command.get(ATLAS_ID_PARAMETER));\n    }\n\n    /**\n     * Creates an informative {@link String} for an {@link AtlasEntity} and the {@link Atlas} that\n     * contains it.\n     *\n     * @param entity\n     *            {@link AtlasEntity} to create the string for\n     * @return formatted string\n     */\n    private String formatAtlasObject(final AtlasEntity entity)\n    {\n        final String shardName = entity.getAtlas().metaData().getShardName().orElse(\"UNKNOWN\");\n        return String.format(\"[%s] [%d] [%d] --> [%s:%s] Tags: [%s]%n\", entity.getType(),\n                entity.getOsmIdentifier(), entity.getIdentifier(), shardName,\n                entity.getAtlas().getName(), entity.getTags());\n    }\n\n    /**\n     * Predicate to check an {@link AtlasObject} against the list of ids.\n     *\n     * @param <T>\n     *            Object type\n     * @return {@link Predicate}\n     */\n    private <T extends AtlasObject> Predicate<T> identifierCheck()\n    {\n        return object -> this.identifiers.contains(object.getIdentifier());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasFindByFeatureIdentifierLocatorSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Parses an extended identifier including the way-sectioned id and feature type and searches for\n * the matching atlas objects in a collection of atlases.\n *\n * @author cstaylor\n */\npublic class AtlasFindByFeatureIdentifierLocatorSubCommand extends AbstractAtlasSubCommand\n{\n    private static final Switch<Set<String>> FEATURE_ID_PARAMETER = new Switch<>(\"id\",\n            \"list of comma-delimited Atlas extended feature identifier\",\n            possibleMultipleOSMIdentifier -> Stream.of(possibleMultipleOSMIdentifier.split(\",\"))\n                    .collect(Collectors.toSet()),\n            Optionality.REQUIRED);\n\n    private static final int TYPE_INDEX = 1;\n\n    private static final int ID_INDEX = 2;\n\n    private static final int EXPECTED_IDENTIFIER_LENGTH = 4;\n\n    private final Predicate<Node> nodeCheck = item -> this.nodeIds\n            .contains(item.getOsmIdentifier());\n\n    private final Predicate<Point> pointCheck = item -> this.pointIds\n            .contains(item.getOsmIdentifier());\n\n    private final Predicate<Area> areaCheck = item -> this.areaIds\n            .contains(item.getOsmIdentifier());\n\n    private final Predicate<Edge> edgeCheck = item -> item.getOsmIdentifier() > 0L\n            && this.edgeIds.contains(item.getOsmIdentifier());\n\n    private final Predicate<Line> lineCheck = item -> this.lineIds\n            .contains(item.getOsmIdentifier());\n\n    private final Predicate<Relation> relationCheck = item -> this.relationIds\n            .contains(item.getOsmIdentifier());\n\n    private final Set<Long> nodeIds = new HashSet<>();\n\n    private final Set<Long> pointIds = new HashSet<>();\n\n    private final Set<Long> areaIds = new HashSet<>();\n\n    private final Set<Long> edgeIds = new HashSet<>();\n\n    private final Set<Long> lineIds = new HashSet<>();\n\n    private final Set<Long> relationIds = new HashSet<>();\n\n    public AtlasFindByFeatureIdentifierLocatorSubCommand()\n    {\n        super(\"find\",\n                \"find which atlas files contain particular OSM features using a given set of extended identifiers\");\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(FEATURE_ID_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\"-id=comma-separated atlas extended feature identifiers to search for\\n\");\n    }\n\n    protected String formatAtlasObject(final String type, final Atlas atlas,\n            final AtlasEntity entity)\n    {\n        final String shardName = atlas.metaData().getShardName().orElse(\"UNKNOWN\");\n        return String.format(\"[%10s] [%d] [%d] --> [%s:%s] Tags: [%s]\\n\", type,\n                entity.getOsmIdentifier(), entity.getIdentifier(), shardName, atlas.getName(),\n                entity.getTags());\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        final int[] found = { 0 };\n        atlas.nodes(this.nodeCheck).forEach(item ->\n        {\n            System.out.printf(formatAtlasObject(\"NODE\", atlas, item));\n            found[0]++;\n        });\n\n        atlas.points(this.pointCheck).forEach(item ->\n        {\n            System.out.printf(formatAtlasObject(\"POINT\", atlas, item));\n            found[0]++;\n        });\n\n        atlas.edges(this.edgeCheck).forEach(item ->\n        {\n            System.out.printf(formatAtlasObject(\"EDGE\", atlas, item));\n            found[0]++;\n        });\n\n        atlas.lines(this.lineCheck).forEach(item ->\n        {\n            System.out.printf(formatAtlasObject(\"LINE\", atlas, item));\n            found[0]++;\n        });\n\n        atlas.areas(this.areaCheck).forEach(item ->\n        {\n            System.out.printf(formatAtlasObject(\"AREA\", atlas, item));\n            found[0]++;\n        });\n\n        atlas.relations(this.relationCheck).forEach(item ->\n        {\n            System.out.printf(formatAtlasObject(\"RELATION\", atlas, item));\n            found[0]++;\n        });\n    }\n\n    @Override\n    protected void start(final CommandMap command)\n    {\n        @SuppressWarnings(\"unchecked\")\n        final Set<String> unparsedFeatureIds = (Set<String>) command.get(FEATURE_ID_PARAMETER);\n\n        for (final String unparsedFeatureId : unparsedFeatureIds)\n        {\n            final String[] parsedFeatureIds = unparsedFeatureId.split(\"_\");\n            // By convention the third piece is the OSM ID, and the second piece is the type of item\n            // we're searching for.\n            if (parsedFeatureIds.length != EXPECTED_IDENTIFIER_LENGTH)\n            {\n                throw new CoreException(\n                        \"There should be four pieces in an extended feature identifier: {}\",\n                        unparsedFeatureId);\n            }\n\n            final long osmId = Long.parseLong(parsedFeatureIds[ID_INDEX]);\n\n            if (parsedFeatureIds[TYPE_INDEX].equals(\"N\"))\n            {\n                this.nodeIds.add(osmId);\n                this.pointIds.add(osmId);\n            }\n            else if (parsedFeatureIds[TYPE_INDEX].equals(\"W\"))\n            {\n                this.areaIds.add(osmId);\n                this.lineIds.add(osmId);\n                this.edgeIds.add(osmId);\n            }\n            else if (parsedFeatureIds[TYPE_INDEX].equals(\"R\"))\n            {\n                this.relationIds.add(osmId);\n            }\n            else\n            {\n                throw new CoreException(\"Unknown type: {}\", parsedFeatureIds[TYPE_INDEX]);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasFindEntitiesByIdSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Creates a new packed atlas for testing purposes on any atlas items if an item matches a set of\n * expected osm identifiers.\n *\n * @author cstaylor\n */\npublic class AtlasFindEntitiesByIdSubCommand extends AbstractAtlasOutputTestSubCommand\n{\n    private static final Switch<Set<Long>> OSM_ID_PARAMETER = new Switch<>(\"osmid\",\n            \"list of comma-delimited OSM Identifiers of the entities we want to export\",\n            possibleMultipleOSMIdentifier -> Stream.of(possibleMultipleOSMIdentifier.split(\",\"))\n                    .map(Long::parseLong).collect(Collectors.toSet()),\n            Optionality.REQUIRED);\n\n    private Set<Long> identifiers;\n\n    public AtlasFindEntitiesByIdSubCommand()\n    {\n        super(\"atlas-with-this-entity\",\n                \"Creates a new atlas containing the area around any items found that match the set of ids provided by the -osmid parameter\");\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OSM_ID_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        super.usage(writer);\n        writer.println(\n                \"-osmid=OSM identifier : comma-separated numeric osm identifiers of the items we're trying to locate\");\n    }\n\n    @Override\n    protected boolean filter(final AtlasEntity item)\n    {\n        return this.identifiers.contains(item.getOsmIdentifier());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    protected void start(final CommandMap command)\n    {\n        super.start(command);\n        this.identifiers = (Set<Long>) command.get(OSM_ID_PARAMETER);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasGeoJSONSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.OutputStreamWritableResource;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Outputs GeoJSON data to stdout or to an optional file\n *\n * @author cstaylor\n */\npublic class AtlasGeoJSONSubCommand extends AbstractAtlasSubCommand\n{\n    private static final Switch<Path> OUTPUT_PARAMETER = new Switch<>(\"output\",\n            \"The geojson file to save the Atlas as geojson to: otherwise sent to stdout\",\n            Paths::get, Optionality.OPTIONAL);\n\n    private JsonWriter writer;\n\n    public AtlasGeoJSONSubCommand()\n    {\n        super(\"geojson\", \"outputs the atlas as geojson data\");\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\n                \"-output=/path/to/output/geojson/file: the path to the output geojson file\\n\");\n        writer.printf(\n                \"-combine : merge all of the atlas files into a MultiAtlas before outputting geojson\\n\");\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        this.writer.close();\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        getWriter(command).write(atlas.asGeoJson());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private JsonWriter getWriter(final CommandMap map)\n    {\n        if (this.writer == null)\n        {\n            ((Optional<Path>) map.getOption(OUTPUT_PARAMETER)).ifPresent(path ->\n            {\n                try\n                {\n                    Files.createDirectories(path.getParent());\n                    this.writer = new JsonWriter(new File(path.toString()));\n                }\n                catch (final IOException oops)\n                {\n                    throw new CoreException(\"Error when creating output stream\", oops);\n                }\n            });\n\n            if (this.writer == null)\n            {\n                this.writer = new JsonWriter(new OutputStreamWritableResource(System.out));\n            }\n        }\n        return this.writer;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasItemsWithSharedShapepointsSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.BufferedOutputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.util.Collection;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.ArrayListMultimap;\nimport com.google.common.collect.Multimap;\n\n/**\n * Checks atlas items in a given atlas for geometry with consecutive identical shapepoints. While\n * this is acceptable in OSM data, it could cause problems in downstream processing tools, so this\n * tool can give us an early warning.\n * <p>\n * Two required parameters:\n * <ul>\n * <li>-input: where we should read the atlas files</li>\n * <li>-output: where we should write information about consecutive identical shapepoints. These\n * files are organized by country</li>\n * </ul>\n *\n * @author cstaylor\n */\npublic class AtlasItemsWithSharedShapepointsSubCommand extends AbstractAtlasSubCommand\n{\n    /**\n     * Helper class for capturing the geometry of an item as a PolyLine and deciding if it has any\n     * consecutive identical shapepoints\n     *\n     * @author cstaylor\n     */\n    private static final class PolyLineTrouble\n    {\n        private final long osmId;\n        private final PolyLine polyline;\n\n        PolyLineTrouble(final AtlasItem item)\n        {\n            this.osmId = item.getOsmIdentifier();\n            this.polyline = new PolyLine(item.getRawGeometry());\n        }\n\n        Long getOsmId()\n        {\n            return this.osmId;\n        }\n\n        /**\n         * Simple algorithm for checking if the polyline has consecutive identical shapepoints by\n         * comparing the current point with the previous one\n         *\n         * @return true if there is a least one overlapping point, false otherwise\n         */\n        boolean hasDuplicatePoints()\n        {\n            Location previous = null;\n            for (final Location location : this.polyline)\n            {\n                if (location.equals(previous))\n                {\n                    return true;\n                }\n                previous = location;\n            }\n            return false;\n        }\n    }\n\n    private static final Logger logger = LoggerFactory\n            .getLogger(AtlasItemsWithSharedShapepointsSubCommand.class);\n\n    private static final Switch<Path> OUTPUT_FILE_PARAMETER = new Switch<>(\"output\",\n            \"Where we want to store the duplicate point error logs\", Paths::get,\n            Optionality.REQUIRED);\n\n    private Multimap<String, Long> countryToOSMids;\n\n    private Time start;\n\n    private int atlasFiles;\n\n    public AtlasItemsWithSharedShapepointsSubCommand()\n    {\n        super(\"duplicate-points\",\n                \"Outputs all of the OSM ids for items with consecutive identical shapepoints\");\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_FILE_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\"\\t-output=/path/to/output/folder/for/each/country%n\");\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        for (final Entry<String, Collection<Long>> badIdsForCountry : this.countryToOSMids.asMap()\n                .entrySet())\n        {\n            try (PrintStream stream = outputFor(badIdsForCountry.getKey(), command))\n            {\n                stream.println(badIdsForCountry.getValue().stream().sorted().map(String::valueOf)\n                        .collect(Collectors.joining(\"\\n\")));\n            }\n        }\n        final long totalErrors = this.countryToOSMids.asMap().values().parallelStream()\n                .flatMap(Collection::parallelStream).count();\n        final NumberFormat formatter = DecimalFormat.getIntegerInstance();\n        if (logger.isInfoEnabled())\n        {\n            logger.info(\"Completed: {} atlas files and found {} errors from {} countries took {}\",\n                    formatter.format(this.atlasFiles), formatter.format(totalErrors),\n                    formatter.format(this.countryToOSMids.keySet().size()),\n                    this.start.elapsedSince());\n        }\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        logger.info(\"Starting {}\", atlas.getName());\n        this.atlasFiles++;\n        final Set<Long> badOsmIDS = ConcurrentHashMap.newKeySet();\n        StreamSupport.stream(atlas.items().spliterator(), true).map(PolyLineTrouble::new)\n                .filter(PolyLineTrouble::hasDuplicatePoints).map(PolyLineTrouble::getOsmId)\n                .forEach(badOsmIDS::add);\n        final Optional<String> countryNameOption = atlas.metaData().getCountry();\n        if (countryNameOption.isPresent())\n        {\n            final String countryName = countryNameOption.get();\n            badOsmIDS.forEach(id ->\n            {\n                this.countryToOSMids.put(countryName, id);\n            });\n            if (!badOsmIDS.isEmpty())\n            {\n                logger.warn(\"Found {} overlaps in {}\", badOsmIDS.size(), atlas.getName());\n            }\n        }\n    }\n\n    protected PrintStream outputFor(final String isoCountry, final CommandMap command)\n    {\n        final Path outputDirectory = (Path) command.get(OUTPUT_FILE_PARAMETER);\n        try\n        {\n            Files.createDirectories(outputDirectory);\n            final Path outputFile = outputDirectory\n                    .resolve(String.format(\"%s.duplicatepoints\", isoCountry));\n            return new PrintStream(\n                    new BufferedOutputStream(new FileOutputStream(outputFile.toFile())));\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Failure when creating outputstream for {}\", isoCountry, oops);\n        }\n    }\n\n    @Override\n    protected void start(final CommandMap command)\n    {\n        super.start(command);\n        this.countryToOSMids = ArrayListMultimap.create();\n        this.start = Time.now();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasJoinerSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Create a MultiAtlas from the set of input Atlas files, creates a PackedAtlas from the MultiAtlas,\n * and then writes that PackedAtlas to the specified output file. Input files are defined through\n * the -input parameter as directory of Atlas shards. All shards are joined by default. Only select\n * shards can be joined by using the -atlases parameter.\n *\n * @author cstaylor\n * @author bbreithaupt\n */\npublic class AtlasJoinerSubCommand extends AbstractAtlasSubCommand\n{\n    private static final Switch<Path> OUTPUT_PARAMETER = new Switch<>(\"output\",\n            \"The Atlas file to save to\", Paths::get, Optionality.REQUIRED);\n\n    private static final Switch<Set<String>> ATLAS_NAMES_PARAMETER = new Switch<>(\"atlases\",\n            \"A comma separated List of Atlas files to join\",\n            atlasNames -> Stream.of(atlasNames.split(\",\")).collect(Collectors.toSet()),\n            Optionality.OPTIONAL);\n\n    private final List<Atlas> atlases = new ArrayList<>();\n\n    public AtlasJoinerSubCommand()\n    {\n        super(\"join\", \"joins multiple atlas files into a single packed atlas\");\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_PARAMETER, ATLAS_NAMES_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\n                \"-output=/path/to/atlas/output/to/save : the path to the output atlas file\\n\");\n        writer.printf(\n                \"-atlases=example.atlas,example2.atlas : comma separated list of atlas file names\\n\");\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        final Atlas atlas = new MultiAtlas(this.atlases);\n        try\n        {\n            final PackedAtlas saveMe = new PackedAtlasCloner().cloneFrom(atlas);\n            final Path path = (Path) command.get(OUTPUT_PARAMETER);\n            Files.createDirectories(path.getParent());\n            saveMe.save(new File(path.toString()));\n            return 0;\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Error when saving packed atlas\", oops);\n        }\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        final Optional atlasNames = command.getOption(ATLAS_NAMES_PARAMETER);\n        if (atlasNames.isPresent() && ((Set<String>) atlasNames.get()).contains(atlas.getName()))\n        {\n            this.atlases.add(atlas);\n        }\n        else if (!atlasNames.isPresent())\n        {\n            this.atlases.add(atlas);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasListRestrictedPathsCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Optional;\nimport java.util.TreeSet;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNodeFinder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.RestrictedPath;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * List all valid turnRestriction Id's in the output file.\n *\n * @author ahsieh\n */\npublic class AtlasListRestrictedPathsCommand extends AbstractAtlasSubCommand\n{\n    private static final Switch<File> OUTPUT_PARAMETER = new Switch<>(\"output\",\n            \"The output file to list all turn restriction ids\", value -> new File(value),\n            Optionality.OPTIONAL);\n\n    private final TreeSet<RestrictedPath> restrictedPaths;\n\n    public AtlasListRestrictedPathsCommand()\n    {\n        super(\"restrictedPaths\", \"lists restrictedPaths\");\n        this.restrictedPaths = new TreeSet<>((restriction1, restriction2) -> Integer\n                .compare(restriction1.hashCode(), restriction2.hashCode()));\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        @SuppressWarnings(\"unchecked\")\n        final Optional<File> possibleFile = (Optional<File>) command.getOption(OUTPUT_PARAMETER);\n\n        if (possibleFile.isPresent())\n        {\n            try (PrintStream out = new PrintStream(\n                    new FileOutputStream(possibleFile.get().getFile(), true)))\n            {\n                this.restrictedPaths.forEach(value -> out.println(value));\n            }\n            catch (final IOException oops)\n            {\n                throw new CoreException(\"Error writing restrictedPaths to file\", oops);\n            }\n        }\n        else\n        {\n            try (PrintStream out = new PrintStream(System.out))\n            {\n                this.restrictedPaths.forEach(value -> out.println(value));\n            }\n        }\n\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        StreamSupport.stream(new BigNodeFinder().find(atlas).spliterator(), false)\n                .flatMap(bigNode ->\n                {\n                    return bigNode.turnRestrictions().stream();\n                }).forEach(this.restrictedPaths::add);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasListValidTurnRestrictionIds.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.TreeSet;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.restriction.ComplexTurnRestrictionFinder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * List all valid turnRestriction Id's in the output file.\n *\n * @author ahsieh\n */\npublic class AtlasListValidTurnRestrictionIds extends AbstractAtlasSubCommand\n{\n    private static final Switch<File> OUTPUT_PARAMETER = new Switch<>(\"output\",\n            \"The output file to list all turn restriction ids\", value -> new File(value),\n            Optionality.REQUIRED);\n\n    private final TreeSet<Long> turnRestrictions;\n\n    public AtlasListValidTurnRestrictionIds()\n    {\n        super(\"turnRestrictions\", \"lists turn restriction OsmIds\");\n        this.turnRestrictions = new TreeSet<>();\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        final File file = (File) command.get(OUTPUT_PARAMETER);\n\n        try (PrintStream out = new PrintStream(new FileOutputStream(file.getFile(), true)))\n        {\n            this.turnRestrictions.forEach(value -> out.println(value));\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Error writing turnRestriction ids to file\", oops);\n        }\n\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        StreamSupport.stream(new ComplexTurnRestrictionFinder().find(atlas).spliterator(), false)\n                .filter(turnRestriction -> turnRestriction.isValid())\n                .map(turnRestriction -> turnRestriction.getOsmIdentifier())\n                .forEach(this.turnRestrictions::add);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasMetadataSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Reads the metadata from the input atlas files and writes it to stdout\n *\n * @author cstaylor\n */\npublic class AtlasMetadataSubCommand extends AbstractAtlasSubCommand\n{\n    public AtlasMetadataSubCommand()\n    {\n        super(\"metadata\", \"outputs all of the metadata found in the atlas files\");\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\n                \"-combine : merge all of the atlas files into a MultiAtlas before outputting geojson\\n\");\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        final AtlasMetaData metaData = atlas.metaData();\n        System.out.printf(\"Atlas Meta Data for %s:\\n%s\\n\", atlas.getName(),\n                metaData.toReadableString());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasMissingISOSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.text.DecimalFormat;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Predicate;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Command that outputs the atlas file and OSM id of any areas missing ISO country codes from the\n * supplied atlas\n *\n * @author cstaylor\n */\npublic class AtlasMissingISOSubCommand extends AbstractAtlasSubCommand\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasMissingISOSubCommand.class);\n\n    private AtomicInteger counter;\n    private Time start;\n\n    public AtlasMissingISOSubCommand()\n    {\n        super(\"isoloss\", \"outputs all of the atlas objects that are missing ISO country codes\");\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\n                \"-combine : merge all of the atlas files into a MultiAtlas before outputting geojson\\n\");\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        final int value = this.counter.get();\n        if (value > 0)\n        {\n            logger.error(String.format(\"Total Items missing ISO Codes: %s\",\n                    DecimalFormat.getNumberInstance().format(value)));\n        }\n        logger.info(\"Time elapsed {}\", this.start.elapsedSince());\n        return Math.min(value, 1);\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        final Predicate<Taggable> missingISOCountryTag = Validators\n                .hasValuesFor(ISOCountryTag.class).negate();\n        StreamSupport.stream(atlas.items().spliterator(), true).filter(missingISOCountryTag)\n                .forEach(this::log);\n    }\n\n    @Override\n    protected void start(final CommandMap command)\n    {\n        this.counter = new AtomicInteger();\n        this.start = Time.now();\n    }\n\n    private void log(final AtlasItem item)\n    {\n        this.counter.incrementAndGet();\n        logger.error(String.format(\"[item] [%25s] [%9d]\", item.getAtlas().getName(),\n                item.getOsmIdentifier()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasReader.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleCommand;\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleSubCommand;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfoList;\nimport io.github.classgraph.ScanResult;\n\n/**\n * Shell for running atlas commands. Run this command with no arguments to learn more about it.\n *\n * @author cstaylor\n */\npublic class AtlasReader extends FlexibleCommand\n{\n    public static void main(final String... args)\n    {\n        final AtlasReader reader = new AtlasReader(args);\n        try\n        {\n            reader.runWithoutQuitting(args);\n        }\n        catch (final Exception e)\n        {\n            e.printStackTrace();\n            reader.printUsageAndExit(1);\n        }\n    }\n\n    public AtlasReader(final String... args)\n    {\n        super(args);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    protected Stream<Class<? extends FlexibleSubCommand>> getSupportedCommands()\n    {\n        final List<Class<? extends FlexibleSubCommand>> returnValue = new ArrayList<>();\n        try (ScanResult scanResult = new ClassGraph().enableAllInfo()\n                .whitelistPackages(AtlasReader.class.getPackage().getName()).scan())\n        {\n            final ClassInfoList classInfoList = scanResult\n                    .getClassesImplementing(FlexibleSubCommand.class.getName());\n            classInfoList.loadClasses()\n                    .forEach(klass -> returnValue.add((Class<? extends FlexibleSubCommand>) klass));\n        }\n        return returnValue.stream();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasResourceLoaderErrorSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.io.StreamCorruptedException;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.exception.ExceptionSearch;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleSubCommand;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Quick way of testing various problems when loading atlas files (missing files, corrupt files,\n * etc...)\n *\n * @author cstaylor\n */\npublic class AtlasResourceLoaderErrorSubCommand implements FlexibleSubCommand\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(AtlasResourceLoaderErrorSubCommand.class);\n\n    private static final Switch<File> INPUT_PARAMETER = new Switch<>(\"input\", \"Path of Atlas file\",\n            File::new, Command.Optionality.OPTIONAL);\n\n    @Override\n    public int execute(final CommandMap map)\n    {\n        final File input = (File) map.get(INPUT_PARAMETER);\n        try\n        {\n            new AtlasResourceLoader().load(input);\n        }\n        catch (final CoreException oops)\n        {\n            logger.error(\"\", oops);\n            ExceptionSearch.find(StreamCorruptedException.class).within(oops)\n                    .ifPresent(error -> logger.error(\"\", error));\n        }\n        return 0;\n    }\n\n    @Override\n    public String getDescription()\n    {\n        return \"Testing the abstract resource loader\";\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"resource-load-testing\";\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return new SwitchList().with(INPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.print(\"-input=/path/to/resources/for/loading/atlas/files\");\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/AtlasSplitterWithSlippyTileCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Create several atlas files from one atlas file as input with fixed zoom level using SlippyTile as\n * the sharding method\n *\n * @author yalimu\n */\npublic class AtlasSplitterWithSlippyTileCommand extends AbstractAtlasSubCommand\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(AtlasSplitterWithSlippyTileCommand.class);\n\n    private static final Command.Switch<Integer> ZOOM_LEVEL = new Command.Switch<>(\"zoom_level\",\n            \"Input zoom level\", Integer::new, Command.Optionality.REQUIRED);\n    private static final Command.Switch<Path> OUTPUT_FOLDER_PARAMETER = new Command.Switch<>(\n            \"output\", \"The path to save Atlas files\", Paths::get, Command.Optionality.REQUIRED);\n\n    public AtlasSplitterWithSlippyTileCommand()\n    {\n        super(\"split\", \"Split one Atlas file into several small Atlas files with fixed zoom level\");\n    }\n\n    @Override\n    public Command.SwitchList switches()\n    {\n        return super.switches().with(ZOOM_LEVEL, OUTPUT_FOLDER_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(AtlasCommandConstants.INPUT_ZOOM_LEVEL);\n        writer.printf(AtlasCommandConstants.OUTPUT_FOLDER_DESCRIPTION);\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        final int zoomLevel = (int) command.get(ZOOM_LEVEL);\n        final Path path = (Path) command.get(OUTPUT_FOLDER_PARAMETER);\n        try\n        {\n            Files.createDirectories(path);\n        }\n        catch (final IOException e)\n        {\n            logger.error(\"Error when creating output directory\", e);\n            return;\n        }\n\n        StreamSupport.stream(SlippyTile.allTiles(zoomLevel, atlas.bounds()).spliterator(), false)\n                .map(tile -> buildAtlasBasedOnTile(tile, atlas)).forEach(newAtlas ->\n                {\n                    if (newAtlas != null)\n                    {\n                        final String outputFileName = path.toString() + \"/\" + atlas.getName() + \"_\"\n                                + newAtlas.getIdentifier() + FileSuffix.ATLAS;\n                        logger.info(\"Saving Atlas file into {}\", outputFileName);\n                        newAtlas.save(new File(outputFileName));\n                    }\n                });\n    }\n\n    private PackedAtlas buildAtlasBasedOnTile(final SlippyTile tile, final Atlas atlas)\n    {\n        return atlas.subAtlas(tile.bounds(), AtlasCutType.SOFT_CUT)\n                .map(subAtlas -> new PackedAtlasCloner().cloneFrom(subAtlas)).orElse(null);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/FerrySearchSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.tags.RouteTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Command for listing countries that have at least one ferry line\n *\n * @author cstaylor\n */\npublic class FerrySearchSubCommand extends AbstractAtlasSubCommand\n{\n    private Set<String> countries;\n\n    public FerrySearchSubCommand()\n    {\n        super(\"ferries\", \"Searching for ferries\");\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        System.out.println(this.countries);\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        if (StreamSupport.stream(atlas.entities(i -> i.getIdentifier() > 0).spliterator(), true)\n                .filter(entity -> Validators.isOfType(entity, RouteTag.class, RouteTag.FERRY))\n                .findFirst().isPresent())\n        {\n            atlas.metaData().getCountry().ifPresent(this.countries::add);\n        }\n    }\n\n    @Override\n    protected void start(final CommandMap command)\n    {\n        this.countries = new TreeSet<>();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/OsmPbfToAtlasSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.BridgeConfiguredFilter;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.tags.filters.ConfiguredTaggableFilter;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleSubCommand;\n\n/**\n * This command converts an OSM PBF file to an Atlas file. It requires the path to a pbf and an\n * output file. It also takes a number of optional parameters.\n *\n * @author bbreithaupt\n */\npublic class OsmPbfToAtlasSubCommand implements FlexibleSubCommand\n{\n    private static final String NAME = \"pbf-to-atlas\";\n    private static final String DESCRIPTION = \"Converts a PBF to an Atlas file.\";\n\n    // Required parameters\n    private static final Switch<File> INPUT_PARAMETER = new Switch<>(\"pbf\", \"Input PBF path\",\n            File::new, Optionality.REQUIRED);\n    private static final Switch<File> OUTPUT_PARAMETER = new Switch<>(\"output\",\n            \"Output Atlas file path\", File::new, Optionality.REQUIRED);\n\n    // Filter parameters\n    private static final Switch<File> EDGE_FILTER_PARAMETER = new Switch<>(\"edge-filter\",\n            \"Path to a local json filter for determining Edges\", File::new, Optionality.OPTIONAL);\n    private static final Switch<File> NODE_FILTER_PARAMETER = new Switch<>(\"node-filter\",\n            \"Path to a local json filter for OSM nodes\", File::new, Optionality.OPTIONAL);\n    private static final Switch<File> RELATION_FILTER_PARAMETER = new Switch<>(\"relation-filter\",\n            \"Path to a local json filter for OSM relations\", File::new, Optionality.OPTIONAL);\n    private static final Switch<File> WAY_FILTER_PARAMETER = new Switch<>(\"way-filter\",\n            \"Path to a local json filter for OSM ways\", File::new, Optionality.OPTIONAL);\n    private static final Switch<File> WAY_SECTION_FILTER_PARAMETER = new Switch<>(\n            \"way-section-filter\",\n            \"Path to a local json filter for determining where to way section\", File::new,\n            Optionality.OPTIONAL);\n\n    // Load Parameters\n    private static final Switch<Boolean> LOAD_RELATIONS_PARAMETER = new Switch<>(\"load-relations\",\n            \"Whether to load Relations (boolean)\", Boolean::parseBoolean, Optionality.OPTIONAL,\n            \"true\");\n    private static final Switch<Boolean> LOAD_WAYS_PARAMETER = new Switch<>(\"load-ways\",\n            \"Whether to load ways (boolean)\", Boolean::parseBoolean, Optionality.OPTIONAL, \"true\");\n\n    // Country parameters\n    private static final Switch<String> COUNTRY_CODE_PARAMETER = new Switch<>(\"country-code\",\n            \"Country from the country map to convert (comma separated ISO3 codes)\",\n            StringConverter.IDENTITY, Optionality.OPTIONAL);\n    private static final Switch<File> COUNTRY_MAP_PARAMETER = new Switch<>(\"country-boundary-map\",\n            \"Path to a local WKT or shp file containing a country boundary map\", File::new,\n            Optionality.OPTIONAL);\n    private static final Switch<Boolean> COUNTRY_SLICING_PARAMETER = new Switch<>(\"country-slicing\",\n            \"Whether to perform country slicing (boolean)\", Boolean::parseBoolean,\n            Optionality.OPTIONAL, \"true\");\n\n    @Override\n    public int execute(final CommandMap map)\n    {\n        final AtlasLoadingOption options = this.getAtlasLoadingOption(map);\n        Atlas atlas = new RawAtlasGenerator((File) map.get(INPUT_PARAMETER), options,\n                MultiPolygon.MAXIMUM).build();\n        if (options.isCountrySlicing())\n        {\n            atlas = new RawAtlasSlicer(options, atlas).slice();\n        }\n        atlas = new AtlasSectionProcessor(atlas, options).run();\n        atlas.save((File) map.get(OUTPUT_PARAMETER));\n        return 0;\n    }\n\n    @Override\n    public String getDescription()\n    {\n        return DESCRIPTION;\n    }\n\n    @Override\n    public String getName()\n    {\n        return NAME;\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return new SwitchList().with(INPUT_PARAMETER, OUTPUT_PARAMETER, EDGE_FILTER_PARAMETER,\n                NODE_FILTER_PARAMETER, RELATION_FILTER_PARAMETER, WAY_FILTER_PARAMETER,\n                WAY_SECTION_FILTER_PARAMETER, LOAD_RELATIONS_PARAMETER, LOAD_WAYS_PARAMETER,\n                COUNTRY_CODE_PARAMETER, COUNTRY_MAP_PARAMETER, COUNTRY_SLICING_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.println(\"-pbf=/path/to/pbf : pbf to convert\");\n        writer.println(\"-output=/path/to/output/atlas : Atlas file to output to\");\n        writer.println(\"-edge-filter=/path/to/json/edge/filter : json filter to determine Edges\");\n        writer.println(\"-node-filter=/path/to/json/node/filter : json filter for OSM nodes\");\n        writer.println(\n                \"-relation-filter=/path/to/json/relation/filter : json filter for OSM relations\");\n        writer.println(\"-way-filter=/path/to/json/way/filter : json filter for OSM ways\");\n        writer.println(\"-load-relations=boolean : whether to load Relations; defaults to true\");\n        writer.println(\"-load-ways=boolean : whether to load ways; defaults to true\");\n        writer.println(\n                \"-country-codes=list,of,ISO3,codes : countries from the country map to convert\");\n        writer.println(\n                \"-country-boundary-map=/path/to/WKT/or/shp : a WKT or shp file containing a country boundary map\");\n        writer.println(\n                \"-country-slicing=boolean : whether to perform country slicing; defaults to true\");\n        writer.println(\n                \"-way-section-filter=/path/to/json/way/section/filter : json filter to determine where to way section\");\n    }\n\n    /**\n     * Creates an {@link AtlasLoadingOption} using configurable parameters. If any of the parameters\n     * are not set the defaults from {@link AtlasLoadingOption} are used.\n     *\n     * @param map\n     *            {@link CommandMap}\n     * @return {@link AtlasLoadingOption}\n     */\n    private AtlasLoadingOption getAtlasLoadingOption(final CommandMap map)\n    {\n        final CountryBoundaryMap countryMap = this.getCountryBoundaryMap(map);\n        final AtlasLoadingOption options = AtlasLoadingOption\n                .createOptionWithAllEnabled(countryMap);\n\n        // Set filters\n        map.getOption(EDGE_FILTER_PARAMETER)\n                .ifPresent(filter -> options.setEdgeFilter(\n                        new BridgeConfiguredFilter(\"\", AtlasLoadingOption.ATLAS_EDGE_FILTER_NAME,\n                                new StandardConfiguration((File) filter))));\n        map.getOption(NODE_FILTER_PARAMETER).ifPresent(filter -> options.setOsmPbfNodeFilter(\n                new ConfiguredTaggableFilter(new StandardConfiguration((File) filter))));\n        map.getOption(RELATION_FILTER_PARAMETER)\n                .ifPresent(filter -> options.setOsmPbfRelationFilter(\n                        new ConfiguredTaggableFilter(new StandardConfiguration((File) filter))));\n        map.getOption(WAY_FILTER_PARAMETER).ifPresent(filter -> options.setOsmPbfWayFilter(\n                new ConfiguredTaggableFilter(new StandardConfiguration((File) filter))));\n        map.getOption(WAY_SECTION_FILTER_PARAMETER)\n                .ifPresent(filter -> options.setWaySectionFilter(new BridgeConfiguredFilter(\"\",\n                        AtlasLoadingOption.ATLAS_WAY_SECTION_FILTER_NAME,\n                        new StandardConfiguration((File) filter))));\n\n        // Set loading options\n        ((Optional<Boolean>) map.getOption(LOAD_RELATIONS_PARAMETER))\n                .ifPresent(options::setLoadAtlasRelation);\n        ((Optional<Boolean>) map.getOption(LOAD_WAYS_PARAMETER)).ifPresent(bool ->\n        {\n            options.setLoadAtlasLine(bool);\n            options.setLoadAtlasEdge(bool);\n        });\n\n        // Set country options\n        ((Optional<String>) map.getOption(COUNTRY_CODE_PARAMETER))\n                .ifPresent(options::setCountryCode);\n        ((Optional<Boolean>) map.getOption(COUNTRY_SLICING_PARAMETER))\n                .ifPresent(options::setCountrySlicing);\n\n        return options;\n    }\n\n    /**\n     * Get or create a {@link CountryBoundaryMap}. If the country-boundary-map parameter is set,\n     * this will attempt to load the text or shape file from that parameter. Else, this will load\n     * using the entire world as the country UNK (unknown).\n     *\n     * @param map\n     *            {@link CommandMap} containing the {@code COUNTRY_MAP_PARAMETER}\n     * @return {@link CountryBoundaryMap} loaded from a file or default\n     */\n    private CountryBoundaryMap getCountryBoundaryMap(final CommandMap map)\n    {\n        final Optional<File> countryMapOption = (Optional<File>) map\n                .getOption(COUNTRY_MAP_PARAMETER);\n        final List<org.locationtech.jts.geom.Polygon> boundaries = new ArrayList<>();\n        MultiPolygon.MAXIMUM.outers()\n                .forEach(outer -> boundaries.add(new JtsPolygonConverter().convert(outer)));\n        CountryBoundaryMap countryMap = CountryBoundaryMap\n                .fromBoundaryMap(Collections.singletonMap(\"UNK\", boundaries));\n        if (countryMapOption.isPresent())\n        {\n            final File countryMapFile = countryMapOption.get();\n            if (FilenameUtils.isExtension(countryMapFile.getName(), \"txt\"))\n            {\n                countryMap = CountryBoundaryMap.fromPlainText(countryMapFile);\n            }\n            else if (FilenameUtils.isExtension(countryMapFile.getName(), \"shp\"))\n            {\n                countryMap = CountryBoundaryMap.fromShapeFile(countryMapFile.getFile());\n            }\n        }\n        return countryMap;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/PackedToTextAtlasSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleSubCommand;\n\n/**\n * Flexible command for converting a {@link PackedAtlas} to a text Atlas\n *\n * @author cstaylor\n */\npublic class PackedToTextAtlasSubCommand implements FlexibleSubCommand\n{\n    private static final String NAME = \"packed-to-text\";\n\n    private static final String DESCRIPTION = \"converts a packed atlas to a text-based atlas\";\n\n    private static final Switch<Path> INPUT_PARAMETER = new Switch<>(\"packed-atlas\",\n            \"Input atlas data in packed atlas format\", Paths::get, Optionality.REQUIRED);\n\n    private static final Switch<Path> OUTPUT_PARAMETER = new Switch<>(\"text-atlas\",\n            \"Output text atlas data path\", Paths::get, Optionality.REQUIRED);\n\n    private Path inputPath;\n    private Path outputPath;\n\n    @Override\n    public int execute(final CommandMap map)\n    {\n        this.inputPath = (Path) map.get(INPUT_PARAMETER);\n        this.outputPath = (Path) map.get(OUTPUT_PARAMETER);\n        preVerify();\n        PackedAtlas.load(new File(this.inputPath.toFile()))\n                .saveAsText(new File(this.outputPath.toFile()));\n        return 0;\n    }\n\n    @Override\n    public String getDescription()\n    {\n        return DESCRIPTION;\n    }\n\n    @Override\n    public String getName()\n    {\n        return NAME;\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return new SwitchList().with(INPUT_PARAMETER, OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.println(\"-text-atlas=/output/path/to/text/atlas\");\n        writer.println(\"-packed-atlas=/input/path/to/packed/atlas\");\n    }\n\n    private void preVerify()\n    {\n        if (!Files.isRegularFile(this.inputPath))\n        {\n            throw new CoreException(\"{} is not a readable file\", this.inputPath);\n        }\n\n        try\n        {\n            if (Files.isDirectory(this.outputPath))\n            {\n                throw new CoreException(\"{} is a directory.  Aborting\", this.outputPath);\n            }\n            Files.createDirectories(this.outputPath.getParent());\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Error when creating directories {}\",\n                    this.outputPath.getParent(), oops);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/SubAtlasSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Flag;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Create a {@link MultiAtlas} from the set of input atlas files, creates a {@link PackedAtlas} from\n * the {@link MultiAtlas}, and then writes that {@link PackedAtlas} to the specified output file.\n *\n * @author mnahoum\n */\npublic class SubAtlasSubCommand extends AbstractAtlasSubCommand\n{\n    private static final Switch<Path> OUTPUT = new Switch<>(\"output\",\n            \"The file to save the Atlas to\", Paths::get, Optionality.OPTIONAL);\n    private static final Switch<Rectangle> SUB = new Switch<>(\"sub\",\n            \"The rectangle to soft-cut this Atlas to\", Rectangle::forString, Optionality.REQUIRED);\n    private static final Switch<Path> GEOJSON = new Switch<>(\"geojson\",\n            \"The geojson file to save this sub atlas to\", Paths::get, Optionality.OPTIONAL);\n    private static final Flag SAVE_MEMORY = new Flag(\"saveMemory\",\n            \"Reduce momery cost if this flag is existed\");\n\n    private final List<Atlas> atlases = new ArrayList<>();\n\n    public SubAtlasSubCommand()\n    {\n        super(\"sub\", \"Subs an Atlas into another smaller Atlas\");\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT, SUB, GEOJSON, SAVE_MEMORY);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\n                \"-output=/path/to/atlas/output/to/save : the path to the output atlas file\\n\");\n        writer.printf(\n                \"-geojson=/path/to/atlas/geojson/to/save : the path to the output geojson file (optional)\\n\");\n        writer.printf(\"-sub=minLat,minLon:maxLat,maxLon : the rectangle to sub the Atlas with\\n\");\n        writer.printf(\"-saveMemory : a flag to save memory\\n\");\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        final Atlas atlas = new MultiAtlas(this.atlases);\n        final Rectangle rectangle = (Rectangle) command.get(SUB);\n        try\n        {\n            final Atlas saveMe = new PackedAtlasCloner().cloneFrom(atlas)\n                    .subAtlas(rectangle, AtlasCutType.SOFT_CUT).orElseThrow(\n                            () -> new CoreException(\"There are no features in the sub rectangle.\"));\n            final Path path = (Path) command.get(OUTPUT);\n            Files.createDirectories(path.getParent());\n            saveMe.save(new File(path.toString()));\n            final Path path2 = (Path) command.get(GEOJSON);\n            if (path2 != null)\n            {\n                Files.createDirectories(path2.getParent());\n                saveMe.saveAsGeoJson(new File(path2.toString()));\n            }\n            return 0;\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Error when saving packed atlas\", oops);\n        }\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        final boolean saveMemory = (boolean) command.get(SAVE_MEMORY);\n        final Rectangle rectangle = (Rectangle) command.get(SUB);\n        if (!saveMemory || rectangle.overlaps(atlas.bounds()))\n        {\n            this.atlases.add(atlas);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/TextToPackedAtlasSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleSubCommand;\n\n/**\n * Flexible command for converting a text-based Atlas to a serialized PackedAtlas\n *\n * @author cstaylor\n */\npublic class TextToPackedAtlasSubCommand implements FlexibleSubCommand\n{\n    private static final String NAME = \"text-to-packed\";\n\n    private static final String DESCRIPTION = \"converts a text-based atlas to a packed atlas\";\n\n    private static final Switch<Path> INPUT_PARAMETER = new Switch<>(\"text-atlas\",\n            \"Input atlas data in text atlas format\", Paths::get, Optionality.REQUIRED);\n\n    private static final Switch<Path> OUTPUT_PARAMETER = new Switch<>(\"packed-atlas\",\n            \"Output atlas data path\", Paths::get, Optionality.REQUIRED);\n\n    private Path inputPath;\n    private Path outputPath;\n\n    @Override\n    public int execute(final CommandMap map)\n    {\n        this.inputPath = (Path) map.get(INPUT_PARAMETER);\n        this.outputPath = (Path) map.get(OUTPUT_PARAMETER);\n        preVerify();\n        new TextAtlasBuilder().read(new File(this.inputPath.toFile()))\n                .save(new File(this.outputPath.toFile()));\n        return 0;\n    }\n\n    @Override\n    public String getDescription()\n    {\n        return DESCRIPTION;\n    }\n\n    @Override\n    public String getName()\n    {\n        return NAME;\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return new SwitchList().with(INPUT_PARAMETER, OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.println(\"-text-atlas=/input/path/to/text/atlas\");\n        writer.println(\"-packed-atlas=/output/path/to/packed/atlas\");\n    }\n\n    private void preVerify()\n    {\n        if (!Files.isRegularFile(this.inputPath))\n        {\n            throw new CoreException(\"{} is not a readable file\", this.inputPath);\n        }\n\n        try\n        {\n            if (Files.isDirectory(this.outputPath))\n            {\n                throw new CoreException(\"{} is a directory.  Aborting\", this.outputPath);\n            }\n            Files.createDirectories(this.outputPath.getParent());\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Error when creating directories {}\",\n                    this.outputPath.getParent(), oops);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/buildings/AtlasFindBuildingPartsSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command.buildings;\n\nimport java.io.PrintStream;\nimport java.util.function.Predicate;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.command.AbstractAtlasSubCommand;\nimport org.openstreetmap.atlas.geography.atlas.command.AtlasCommandConstants;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.tags.BuildingPartTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Outputs all of the atlas items that have building:part tags\n *\n * @author cstaylor\n */\npublic class AtlasFindBuildingPartsSubCommand extends AbstractAtlasSubCommand\n{\n    private static void output(final AtlasItem item)\n    {\n        System.out.printf(\"[%25s] [%9d] %s\\n\", item.getAtlas().getName(), item.getOsmIdentifier(),\n                item);\n    }\n\n    public AtlasFindBuildingPartsSubCommand()\n    {\n        super(\"building-parts\", \"Task for finding all of the OSM ids with building:part=yes\");\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        final Predicate<Taggable> filter = Validators.hasValuesFor(BuildingPartTag.class);\n        StreamSupport.stream(atlas.items().spliterator(), true).filter(filter)\n                .forEach(AtlasFindBuildingPartsSubCommand::output);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/buildings/BuildingsWithHeightSearchSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command.buildings;\n\nimport java.io.BufferedOutputStream;\nimport java.io.Closeable;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.TreeSet;\nimport java.util.function.Consumer;\nimport java.util.stream.StreamSupport;\n\nimport org.apache.commons.compress.utils.IOUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.command.AbstractAtlasSubCommand;\nimport org.openstreetmap.atlas.geography.atlas.command.AtlasCommandConstants;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuilding;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuildingFinder;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\nimport com.google.common.collect.ComparisonChain;\n\n/**\n * Outputs information about buildings that have heights (making them 3D)\n *\n * @author cstaylor\n */\npublic class BuildingsWithHeightSearchSubCommand extends AbstractAtlasSubCommand\n{\n    /**\n     * Data item class for holding onto the information we need to log about building heights\n     *\n     * @author cstaylor\n     */\n    private static final class BuildingHeightItem implements Comparable<BuildingHeightItem>\n    {\n        private final String iso3;\n        private double height;\n        private final long osmIdentifier;\n        private final long atlasIdentifier;\n        private final String latitude;\n        private final String longitude;\n        private final String url;\n        private final boolean valid;\n\n        BuildingHeightItem(final ComplexBuilding building)\n        {\n            this.url = String.format(\"http://www.openstreetmap.org/%s/%d\",\n                    building.getSource().getType() == ItemType.AREA ? \"way\" : \"relation\",\n                    building.getOsmIdentifier());\n            final Optional<MultiPolygon> outline = building.getOutline();\n            this.valid = outline.isPresent();\n            if (outline.isPresent())\n            {\n                final Location location = outline.get().outers().iterator().next().first();\n                this.iso3 = building.getTag(ISOCountryTag.class, Optional.empty()).orElse(\"UNK\");\n                this.atlasIdentifier = building.getIdentifier();\n                this.osmIdentifier = building.getOsmIdentifier();\n                building.topHeight().ifPresent(height ->\n                {\n                    this.height = height.asMeters();\n                });\n                this.latitude = location.getLatitude().toString();\n                this.longitude = location.getLongitude().toString();\n            }\n            else\n            {\n                this.iso3 = null;\n                this.atlasIdentifier = -1;\n                this.osmIdentifier = -1;\n                this.latitude = null;\n                this.longitude = null;\n            }\n        }\n\n        @Override\n        public int compareTo(final BuildingHeightItem otherBuilding)\n        {\n            return ComparisonChain.start().compare(this.iso3, otherBuilding.iso3)\n                    .compare(this.height, otherBuilding.height)\n                    .compare(this.atlasIdentifier, otherBuilding.atlasIdentifier).result();\n        }\n\n        @Override\n        public boolean equals(final Object otherObject)\n        {\n            if (this == otherObject)\n            {\n                return true;\n            }\n            if (otherObject instanceof BuildingHeightItem)\n            {\n                final BuildingHeightItem otherItem = (BuildingHeightItem) otherObject;\n                return this.atlasIdentifier == otherItem.atlasIdentifier;\n            }\n            return false;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return Objects.hash(this.atlasIdentifier);\n        }\n\n        private boolean isValid()\n        {\n            return this.valid;\n        }\n\n        private void output(final int count, final PrintStream stream)\n        {\n            stream.printf(\n                    \"<tr><td>%d</td><td>%s</td><td align=\\\"right\\\">%s</td><td align=\\\"right\\\">%s</td><td><a href=\\\"%s\\\">%s</td><td>%.2f</td><td>%s,%s</td></tr>\\n\",\n                    count, this.iso3, this.atlasIdentifier, this.osmIdentifier, this.url, this.url,\n                    this.height, this.latitude, this.longitude);\n        }\n    }\n\n    /**\n     * Logs some basic information about each building that has a height\n     *\n     * @author cstaylor\n     */\n    private static final class BuildingsWithHeightLogger\n            implements Consumer<ComplexBuilding>, Closeable\n    {\n        private final PrintStream output;\n        private final TreeSet<BuildingHeightItem> items;\n\n        private BuildingsWithHeightLogger(final PrintStream output)\n        {\n            this.output = output;\n            output.printf(\n                    \"<html><head><style>table { font-family: Menlo; }</style></head><body><table>\\n\");\n            this.items = new TreeSet<>();\n        }\n\n        @Override\n        public void accept(final ComplexBuilding buildingsWithHeight)\n        {\n            final BuildingHeightItem item = new BuildingHeightItem(buildingsWithHeight);\n            if (item.isValid())\n            {\n                this.items.add(item);\n            }\n        }\n\n        @Override\n        public void close() throws IOException\n        {\n            final int[] counter = { 0 };\n            this.items.stream().forEach(item ->\n            {\n                item.output(++counter[0], this.output);\n            });\n            this.output.printf(\"</table></body></html>\\n\");\n            IOUtils.closeQuietly(this.output);\n        }\n    }\n\n    private static final Switch<Path> OUTPUT_FILE_PARAMETER = new Switch<>(\"output\",\n            \"HTML file containing information about each 3D building\", Paths::get,\n            Optionality.REQUIRED);\n\n    private BuildingsWithHeightLogger counter;\n\n    public BuildingsWithHeightSearchSubCommand()\n    {\n        super(\"3d-buildings\", \"Lists all of the buildings that have a height value\");\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(OUTPUT_FILE_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\"-output=/path/to/output/file\\n\");\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        try\n        {\n            this.counter.close();\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Failure to close\", oops);\n        }\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        StreamSupport.stream(new ComplexBuildingFinder().find(atlas).spliterator(), false)\n                .map(ComplexBuilding.class::cast).filter(this::hasHeight).forEach(this.counter);\n    }\n\n    @Override\n    protected void start(final CommandMap command)\n    {\n        super.start(command);\n        this.counter = new BuildingsWithHeightLogger(createStream(command));\n    }\n\n    private PrintStream createStream(final CommandMap command)\n    {\n        try\n        {\n            final Path output = (Path) command.get(OUTPUT_FILE_PARAMETER);\n            try\n            {\n                Files.createDirectories(output.getParent());\n            }\n            catch (final IOException oops)\n            {\n                throw new CoreException(\"Error when creating output directory\", oops);\n            }\n            return new PrintStream(new BufferedOutputStream(new FileOutputStream(output.toFile())));\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Failure to open output\", oops);\n        }\n    }\n\n    private boolean hasHeight(final ComplexBuilding building)\n    {\n        return building.topHeight().isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/command/buildings/TinyBuildingsSearchSubCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command.buildings;\n\nimport java.io.BufferedOutputStream;\nimport java.io.Closeable;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.stream.StreamSupport;\n\nimport org.apache.commons.compress.utils.IOUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.command.AbstractAtlasSubCommand;\nimport org.openstreetmap.atlas.geography.atlas.command.AtlasCommandConstants;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuilding;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuildingFinder;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\n\n/**\n * Outputs information about buildings that have a surface area of less than 1 square meter\n *\n * @author cstaylor\n */\npublic class TinyBuildingsSearchSubCommand extends AbstractAtlasSubCommand\n{\n    /**\n     * Logs some basic information about each building that fails the 1 sq meter test\n     *\n     * @author cstaylor\n     */\n    private static final class TinyBuildingLogger implements Consumer<ComplexBuilding>, Closeable\n    {\n        private final PrintStream output;\n\n        private TinyBuildingLogger(final PrintStream output)\n        {\n            this.output = output;\n        }\n\n        @Override\n        public void accept(final ComplexBuilding tinyBuilding)\n        {\n            final String url = String.format(\"http://www.openstreetmap.org/%s/%d\",\n                    tinyBuilding.getSource().getType() == ItemType.AREA ? \"way\" : \"relation\",\n                    tinyBuilding.getOsmIdentifier());\n            final Optional<MultiPolygon> outline = tinyBuilding.getOutline();\n            if (outline.isPresent())\n            {\n                this.output.printf(\"%s,%d,%d,%s,%.2f\\n\",\n                        tinyBuilding.getTag(ISOCountryTag.class, Optional.empty()).orElse(\"UNK\"),\n                        tinyBuilding.getIdentifier(), tinyBuilding.getOsmIdentifier(), url,\n                        outline.get().surface().asMeterSquared());\n            }\n        }\n\n        @Override\n        public void close() throws IOException\n        {\n            IOUtils.closeQuietly(this.output);\n        }\n    }\n\n    private static final Switch<Surface> MINIMUM_BUILDING_SIZE_PARAMETER = new Switch<>(\"minimum\",\n            \"The minimum area permitted for a building\",\n            value -> Surface.UNIT_METER_SQUARED_ON_EARTH_SURFACE.scaleBy(Double.valueOf(value)),\n            Optionality.REQUIRED);\n\n    private static final Switch<Path> OUTPUT_FILE_PARAMETER = new Switch<>(\"output\",\n            \"File containing the CSV information about each tiny building\", Paths::get,\n            Optionality.REQUIRED);\n\n    private Surface minimumSurface = Surface.UNIT_METER_SQUARED_ON_EARTH_SURFACE;\n    private TinyBuildingLogger counter;\n\n    public TinyBuildingsSearchSubCommand()\n    {\n        super(\"buildings-for-ants\",\n                \"Lists all of the buildings with areas smaller than a given size\");\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return super.switches().with(MINIMUM_BUILDING_SIZE_PARAMETER, OUTPUT_FILE_PARAMETER);\n    }\n\n    public boolean tooSmall(final ComplexBuilding building)\n    {\n        final Optional<MultiPolygon> outline = building.getOutline();\n        if (outline.isPresent())\n        {\n            return outline.get().surface().isLessThanOrEqualTo(this.minimumSurface);\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.printf(AtlasCommandConstants.INPUT_PARAMETER_DESCRIPTION);\n        writer.printf(\"-minimum=[scale factor of square meters]\\n\");\n        writer.printf(\"-output=/path/to/output/file\\n\");\n    }\n\n    @Override\n    protected int finish(final CommandMap command)\n    {\n        try\n        {\n            this.counter.close();\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Failure to close\", oops);\n        }\n        return 0;\n    }\n\n    @Override\n    protected void handle(final Atlas atlas, final CommandMap command)\n    {\n        StreamSupport.stream(new ComplexBuildingFinder().find(atlas).spliterator(), false)\n                .map(ComplexBuilding.class::cast).filter(this::tooSmall).forEach(this.counter);\n    }\n\n    @Override\n    protected void start(final CommandMap command)\n    {\n        super.start(command);\n        this.minimumSurface = (Surface) command.get(MINIMUM_BUILDING_SIZE_PARAMETER);\n        this.counter = new TinyBuildingLogger(createStream(command));\n    }\n\n    private PrintStream createStream(final CommandMap command)\n    {\n        try\n        {\n            final Path output = (Path) command.get(OUTPUT_FILE_PARAMETER);\n            try\n            {\n                Files.createDirectories(output.getParent());\n            }\n            catch (final IOException oops)\n            {\n                throw new CoreException(\"Error when creating output directory\", oops);\n            }\n            return new PrintStream(new BufferedOutputStream(new FileOutputStream(output.toFile())));\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Failure to open output\", oops);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteArea.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Independent {@link Area} that contains its own data. At scale, use at your own risk.\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class CompleteArea extends Area implements CompleteEntity<CompleteArea>\n{\n    private static final long serialVersionUID = 309534717673911086L;\n\n    private Rectangle bounds;\n    private long identifier;\n    private Polygon polygon;\n    private Map<String, String> tags;\n    private Set<Long> relationIdentifiers;\n    private Set<Long> geometricRelationIdentifiers;\n\n    private final TagChangeDelegate tagChangeDelegate = TagChangeDelegate.newTagChangeDelegate();\n\n    /**\n     * Create a {@link CompleteArea} from a given {@link Area} reference. The {@link CompleteArea}'s\n     * fields will match the fields of the reference. The returned {@link CompleteArea} will be\n     * full, i.e. all of its associated fields will be non-null.\n     *\n     * @param area\n     *            the {@link Area} to copy\n     * @return the full {@link CompleteArea}\n     */\n    public static CompleteArea from(final Area area)\n    {\n        if (area instanceof CompleteArea && !((CompleteArea) area).isFull())\n        {\n            throw new CoreException(\"Area parameter was a CompleteArea but it was not full: {}\",\n                    area);\n        }\n        return new CompleteArea(area.getIdentifier(), area.asPolygon(), area.getTags(),\n                area.relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet()))\n                .withGeometricRelationIdentifiers(\n                        area.relations().stream().filter(Relation::isGeometric)\n                                .filter(relation -> relation.asMultiPolygon().isPresent()\n                                        && !relation.asMultiPolygon().get().isEmpty())\n                                .map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    /**\n     * Create a shallow {@link CompleteArea} from a given {@link Area} reference. The\n     * {@link CompleteArea}'s identifier will match the identifier of the reference {@link Area}.\n     * The returned {@link CompleteArea} will be shallow, i.e. all of its associated fields will be\n     * null except for the identifier.\n     *\n     * @param area\n     *            the {@link Area} to copy\n     * @return the shallow {@link CompleteArea}\n     */\n    public static CompleteArea shallowFrom(final Area area)\n    {\n        if (area.bounds() == null)\n        {\n            throw new CoreException(\"Area parameter bounds were null\");\n        }\n        return new CompleteArea(area.getIdentifier()).withBoundsExtendedBy(area.bounds());\n    }\n\n    public CompleteArea(final Long identifier, final Polygon polygon,\n            final Map<String, String> tags, final Set<Long> relationIdentifiers)\n    {\n        super(new EmptyAtlas());\n\n        if (identifier == null)\n        {\n            throw new CoreException(\"Identifier can never be null.\");\n        }\n\n        this.bounds = polygon != null ? polygon.bounds() : null;\n\n        this.identifier = identifier;\n        this.polygon = polygon;\n        this.tags = tags;\n        this.relationIdentifiers = relationIdentifiers;\n    }\n\n    protected CompleteArea(final long identifier)\n    {\n        this(identifier, null, null, null);\n    }\n\n    @Override\n    public void addTagChangeListener(final TagChangeListener tagChangeListener)\n    {\n        this.tagChangeDelegate.addTagChangeListener(tagChangeListener);\n    }\n\n    @Override\n    public Polygon asPolygon()\n    {\n        return this.polygon;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bounds;\n    }\n\n    @Override\n    public CompleteItemType completeItemType()\n    {\n        return CompleteItemType.AREA;\n    }\n\n    public CompleteArea copy()\n    {\n        return new CompleteArea(this.identifier, this.polygon, this.tags, this.relationIdentifiers);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof CompleteArea)\n        {\n            final CompleteArea that = (CompleteArea) other;\n            return CompleteEntity.basicEqual(this, that)\n                    && Objects.equals(this.asPolygon(), that.asPolygon());\n        }\n        return false;\n    }\n\n    @Override\n    public void fireTagChangeEvent(final TagChangeEvent tagChangeEvent)\n    {\n        this.tagChangeDelegate.fireTagChangeEvent(tagChangeEvent);\n    }\n\n    public Set<Long> geometricRelationIdentifiers()\n    {\n        return this.geometricRelationIdentifiers;\n    }\n\n    @Override\n    public Iterable<Location> getGeometry()\n    {\n        if (this.polygon != null)\n        {\n            return new ArrayList<>(this.polygon);\n        }\n        return null;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    @Override\n    public boolean isFull()\n    {\n        return this.bounds != null && this.polygon != null && this.tags != null\n                && this.relationIdentifiers != null;\n    }\n\n    @Override\n    public boolean isShallow()\n    {\n        return this.polygon == null && this.tags == null && this.relationIdentifiers == null;\n    }\n\n    @Override\n    public String prettify(final PrettifyStringFormat format, final boolean truncate)\n    {\n        String separator = \"\";\n        if (format == PrettifyStringFormat.MINIMAL_SINGLE_LINE)\n        {\n            separator = \"\";\n        }\n        else if (format == PrettifyStringFormat.MINIMAL_MULTI_LINE)\n        {\n            separator = \"\\n\";\n        }\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName() + \" \");\n        builder.append(\"[\");\n        builder.append(separator);\n        builder.append(\"identifier: \" + this.identifier + \", \");\n        builder.append(separator);\n        if (this.polygon != null)\n        {\n            if (truncate)\n            {\n                builder.append(\"geometry: \" + truncate(this.polygon.toString()) + \", \");\n            }\n            else\n            {\n                builder.append(\"geometry: \" + this.polygon.toString() + \", \");\n            }\n            builder.append(separator);\n        }\n        if (this.tags != null)\n        {\n            builder.append(\"tags: \" + new TreeMap<>(this.tags) + \", \");\n            builder.append(separator);\n        }\n        if (this.relationIdentifiers != null)\n        {\n            builder.append(\"parentRelations: \" + new TreeSet<>(this.relationIdentifiers) + \", \");\n            builder.append(separator);\n        }\n        if (this.bounds != null)\n        {\n            builder.append(\"bounds: \" + this.bounds.toWkt() + \", \");\n            builder.append(separator);\n        }\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    @Override\n    public Set<Long> relationIdentifiers()\n    {\n        return this.relationIdentifiers;\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        /*\n         * Note that the Relations returned by this method will technically break the Located\n         * contract, since they have null bounds.\n         */\n        return this.relationIdentifiers == null ? null\n                : this.relationIdentifiers.stream().map(CompleteRelation::new)\n                        .collect(Collectors.toSet());\n    }\n\n    @Override\n    public void removeTagChangeListeners()\n    {\n        this.tagChangeDelegate.removeTagChangeListeners();\n    }\n\n    @Override\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags != null ? new HashMap<>(tags) : null;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getSimpleName() + \" [identifier=\" + this.identifier + \", polygon=\"\n                + this.polygon + \", tags=\" + this.tags + \", relationIdentifiers=\"\n                + this.relationIdentifiers + \"]\";\n    }\n\n    @Override\n    public String toWkt()\n    {\n        if (this.polygon == null)\n        {\n            return null;\n        }\n        return this.polygon.toWkt();\n    }\n\n    @Override\n    public CompleteArea withAddedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers.add(relationIdentifier);\n        return this;\n    }\n\n    public CompleteArea withBoundsExtendedBy(final Rectangle bounds)\n    {\n        if (this.bounds == null)\n        {\n            this.bounds = bounds;\n            return this;\n        }\n        this.bounds = Rectangle.forLocated(this.bounds, bounds);\n        return this;\n    }\n\n    public CompleteArea withGeometricRelationIdentifiers(\n            final Set<Long> geometricRelationIdentifiers)\n    {\n        this.geometricRelationIdentifiers = geometricRelationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompleteEntity withGeometry(final Iterable<Location> locations)\n    {\n        return this.withPolygon(new Polygon(locations));\n    }\n\n    @Override\n    public CompleteArea withIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n        return this;\n    }\n\n    public CompleteArea withPolygon(final Polygon polygon)\n    {\n        this.polygon = polygon;\n        if (this.polygon != null)\n        {\n            this.bounds = polygon.bounds();\n        }\n        return this;\n    }\n\n    @Override\n    public CompleteArea withRelationIdentifiers(final Set<Long> relationIdentifiers)\n    {\n        this.relationIdentifiers = relationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompleteArea withRelations(final Set<Relation> relations)\n    {\n        this.relationIdentifiers = relations.stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet());\n        this.geometricRelationIdentifiers = relations.stream().filter(Relation::isGeometric)\n                .filter(relation -> relation.asMultiPolygon().isPresent()\n                        && !relation.asMultiPolygon().get().isEmpty())\n                .map(Relation::getIdentifier).collect(Collectors.toSet());\n        return this;\n    }\n\n    @Override\n    public CompleteArea withRemovedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers = this.relationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        this.geometricRelationIdentifiers = this.geometricRelationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Independent {@link Edge} that contains its own data. At scale, use at your own risk.\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class CompleteEdge extends Edge implements CompleteLineItem<CompleteEdge>\n{\n    private static final long serialVersionUID = 309534717673911086L;\n\n    private Rectangle bounds;\n    private long identifier;\n    private PolyLine polyLine;\n    private Map<String, String> tags;\n    private Long startNodeIdentifier;\n    private Long endNodeIdentifier;\n    private Set<Long> relationIdentifiers;\n    private Set<Long> geometricRelationIdentifiers;\n    private final TagChangeDelegate tagChangeDelegate = TagChangeDelegate.newTagChangeDelegate();\n\n    /**\n     * Create a {@link CompleteEdge} from a given {@link Edge} reference. The {@link CompleteEdge}'s\n     * fields will match the fields of the reference. The returned {@link CompleteEdge} will be\n     * full, i.e. all of its associated fields will be non-null.\n     *\n     * @param edge\n     *            the {@link Edge} to copy\n     * @return the full {@link CompleteEdge}\n     */\n    public static CompleteEdge from(final Edge edge)\n    {\n        if (edge instanceof CompleteEdge && !((CompleteEdge) edge).isFull())\n        {\n            throw new CoreException(\"Edge parameter was a CompleteEdge but it was not full: {}\",\n                    edge);\n        }\n        return new CompleteEdge(edge.getIdentifier(), edge.asPolyLine(), edge.getTags(),\n                edge.start().getIdentifier(), edge.end().getIdentifier(),\n                edge.relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet()))\n                .withGeometricRelationIdentifiers(\n                        edge.relations().stream().filter(Relation::isGeometric)\n                                .filter(relation -> relation.asMultiPolygon().isPresent()\n                                        && !relation.asMultiPolygon().get().isEmpty())\n                                .map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    /**\n     * Create a shallow {@link CompleteEdge} from a given {@link Edge} reference. The\n     * {@link CompleteEdge}'s identifier will match the identifier of the reference {@link Edge}.\n     * The returned {@link CompleteEdge} will be shallow, i.e. all of its associated fields will be\n     * null except for the identifier.\n     *\n     * @param edge\n     *            the {@link Edge} to copy\n     * @return the shallow {@link CompleteEdge}\n     */\n    public static CompleteEdge shallowFrom(final Edge edge)\n    {\n        if (edge.bounds() == null)\n        {\n            throw new CoreException(\"Edge parameter bounds were null\");\n        }\n        return new CompleteEdge(edge.getIdentifier()).withBoundsExtendedBy(edge.bounds());\n    }\n\n    public CompleteEdge(final Long identifier, final PolyLine polyLine,\n            final Map<String, String> tags, final Long startNodeIdentifier,\n            final Long endNodeIdentifier, final Set<Long> relationIdentifiers)\n    {\n        super(new EmptyAtlas());\n\n        if (identifier == null)\n        {\n            throw new CoreException(\"Identifier can never be null.\");\n        }\n\n        this.bounds = polyLine != null ? polyLine.bounds() : null;\n\n        this.identifier = identifier;\n        this.polyLine = polyLine;\n        this.tags = tags;\n        this.startNodeIdentifier = startNodeIdentifier;\n        this.endNodeIdentifier = endNodeIdentifier;\n        this.relationIdentifiers = relationIdentifiers;\n    }\n\n    protected CompleteEdge(final long identifier)\n    {\n        this(identifier, null, null, null, null, null);\n    }\n\n    @Override\n    public void addTagChangeListener(final TagChangeListener tagChangeListener)\n    {\n        this.tagChangeDelegate.addTagChangeListener(tagChangeListener);\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return this.polyLine;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bounds;\n    }\n\n    @Override\n    public CompleteItemType completeItemType()\n    {\n        return CompleteItemType.EDGE;\n    }\n\n    public CompleteEdge copy()\n    {\n        return new CompleteEdge(this.identifier, this.polyLine, this.tags, this.startNodeIdentifier,\n                this.endNodeIdentifier, this.relationIdentifiers);\n    }\n\n    @Override\n    public Node end()\n    {\n        /*\n         * Note that the Node returned by this method will technically break the Located contract,\n         * since it has null bounds.\n         */\n        return this.endNodeIdentifier == null ? null : new CompleteNode(this.endNodeIdentifier);\n    }\n\n    public Long endNodeIdentifier()\n    {\n        return this.endNodeIdentifier;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof CompleteEdge)\n        {\n            final CompleteEdge that = (CompleteEdge) other;\n            return CompleteEntity.basicEqual(this, that)\n                    && Objects.equals(this.asPolyLine(), that.asPolyLine())\n                    && CompleteEntity.equalThroughGet(this.start(), that.start(),\n                            Node::getIdentifier)\n                    && CompleteEntity.equalThroughGet(this.end(), that.end(), Node::getIdentifier);\n        }\n        return false;\n    }\n\n    @Override\n    public void fireTagChangeEvent(final TagChangeEvent tagChangeEvent)\n    {\n        this.tagChangeDelegate.fireTagChangeEvent(tagChangeEvent);\n    }\n\n    public Set<Long> geometricRelationIdentifiers()\n    {\n        return this.geometricRelationIdentifiers;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    @Override\n    public boolean isFull()\n    {\n        return this.bounds != null && this.polyLine != null && this.tags != null\n                && this.startNodeIdentifier != null && this.endNodeIdentifier != null\n                && this.relationIdentifiers != null;\n    }\n\n    @Override\n    public boolean isShallow()\n    {\n        return this.polyLine == null && this.startNodeIdentifier == null\n                && this.endNodeIdentifier == null && this.tags == null\n                && this.relationIdentifiers == null;\n    }\n\n    @Override\n    public String prettify(final PrettifyStringFormat format, final boolean truncate)\n    {\n        String separator = \"\";\n        if (format == PrettifyStringFormat.MINIMAL_SINGLE_LINE)\n        {\n            separator = \"\";\n        }\n        else if (format == PrettifyStringFormat.MINIMAL_MULTI_LINE)\n        {\n            separator = \"\\n\";\n        }\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName() + \" \");\n        builder.append(\"[\");\n        builder.append(separator);\n        builder.append(\"identifier: \" + this.identifier + \", \");\n        builder.append(separator);\n        if (this.polyLine != null)\n        {\n            if (truncate)\n            {\n                builder.append(\"geometry: \" + truncate(this.polyLine.toString()) + \", \");\n            }\n            else\n            {\n                builder.append(\"geometry: \" + this.polyLine.toString() + \", \");\n            }\n            builder.append(separator);\n        }\n        if (this.startNodeIdentifier != null)\n        {\n            builder.append(\"startNode: \" + this.startNodeIdentifier + \", \");\n            builder.append(separator);\n        }\n        if (this.endNodeIdentifier != null)\n        {\n            builder.append(\"endNode: \" + this.endNodeIdentifier + \", \");\n            builder.append(separator);\n        }\n        if (this.tags != null)\n        {\n            builder.append(\"tags: \" + new TreeMap<>(this.tags) + \", \");\n            builder.append(separator);\n        }\n        if (this.relationIdentifiers != null)\n        {\n            builder.append(\"parentRelations: \" + new TreeSet<>(this.relationIdentifiers) + \", \");\n            builder.append(separator);\n        }\n        if (this.bounds != null)\n        {\n            builder.append(\"bounds: \" + this.bounds.toWkt() + \", \");\n            builder.append(separator);\n        }\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    @Override\n    public Set<Long> relationIdentifiers()\n    {\n        return this.relationIdentifiers;\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        /*\n         * Note that the Relations returned by this method will technically break the Located\n         * contract, since they have null bounds.\n         */\n        return this.relationIdentifiers == null ? null\n                : this.relationIdentifiers.stream().map(CompleteRelation::new)\n                        .collect(Collectors.toSet());\n    }\n\n    @Override\n    public void removeTagChangeListeners()\n    {\n        this.tagChangeDelegate.removeTagChangeListeners();\n    }\n\n    @Override\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags != null ? new HashMap<>(tags) : null;\n    }\n\n    @Override\n    public Node start()\n    {\n        /*\n         * Note that the Node returned by this method will technically break the Located contract,\n         * since it has null bounds.\n         */\n        return this.startNodeIdentifier == null ? null : new CompleteNode(this.startNodeIdentifier);\n    }\n\n    public Long startNodeIdentifier()\n    {\n        return this.startNodeIdentifier;\n    }\n\n    @Override\n    public JsonObject toJson()\n    {\n        final JsonObject edgeObject = super.toJson();\n\n        edgeObject.addProperty(\"startNode\", this.startNodeIdentifier);\n        edgeObject.addProperty(\"endNode\", this.endNodeIdentifier);\n\n        return edgeObject;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getSimpleName() + \" [identifier=\" + this.identifier\n                + \", startNodeIdentifier=\" + this.startNodeIdentifier + \", endNodeIdentifier=\"\n                + this.endNodeIdentifier + \", polyLine=\" + this.polyLine + \", tags=\" + this.tags\n                + \", relationIdentifiers=\" + this.relationIdentifiers + \"]\";\n    }\n\n    @Override\n    public String toWkt()\n    {\n        if (this.polyLine == null)\n        {\n            return null;\n        }\n        return this.polyLine.toWkt();\n    }\n\n    @Override\n    public CompleteEdge withAddedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers.add(relationIdentifier);\n        return this;\n    }\n\n    public CompleteEdge withBoundsExtendedBy(final Rectangle bounds)\n    {\n        if (this.bounds == null)\n        {\n            this.bounds = bounds;\n            return this;\n        }\n        this.bounds = Rectangle.forLocated(this.bounds, bounds);\n        return this;\n    }\n\n    public CompleteEdge withEndNodeIdentifier(final Long endNodeIdentifier)\n    {\n        this.endNodeIdentifier = endNodeIdentifier;\n        return this;\n    }\n\n    public CompleteEdge withGeometricRelationIdentifiers(\n            final Set<Long> geometricRelationIdentifiers)\n    {\n        this.geometricRelationIdentifiers = geometricRelationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompleteEntity withGeometry(final Iterable<Location> locations)\n    {\n        return this.withPolyLine(new PolyLine(locations));\n    }\n\n    @Override\n    public CompleteEdge withIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n        return this;\n    }\n\n    @Override\n    public CompleteEdge withPolyLine(final PolyLine polyLine)\n    {\n        this.polyLine = polyLine;\n        if (this.polyLine != null)\n        {\n            this.bounds = polyLine.bounds();\n        }\n        return this;\n    }\n\n    @Override\n    public CompleteEdge withRelationIdentifiers(final Set<Long> relationIdentifiers)\n    {\n        this.relationIdentifiers = relationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompleteEdge withRelations(final Set<Relation> relations)\n    {\n        this.relationIdentifiers = relations.stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet());\n        this.geometricRelationIdentifiers = relations.stream().filter(Relation::isGeometric)\n                .filter(relation -> relation.asMultiPolygon().isPresent()\n                        && !relation.asMultiPolygon().get().isEmpty())\n                .map(Relation::getIdentifier).collect(Collectors.toSet());\n        return this;\n    }\n\n    @Override\n    public CompleteEdge withRemovedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers = this.relationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        this.geometricRelationIdentifiers = this.geometricRelationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        return this;\n    }\n\n    public CompleteEdge withStartNodeIdentifier(final Long startNodeIdentifier)\n    {\n        this.startNodeIdentifier = startNodeIdentifier;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteEntity.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listenable.TagChangeListenable;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Simple interface for all the Complete entities. As each one extends its parent class already\n * (Node, Edge, Area, ...) this cannot be an abstract class.\n *\n * @param <C>\n *            the {@link CompleteEntity} implementation\n * @author matthieun\n * @author Yazad Khambata\n */\npublic interface CompleteEntity<C extends CompleteEntity<C>> extends TagChangeListenable\n{\n    static Map<String, String> addNewTag(final Map<String, String> tags, final String key,\n            final String value)\n    {\n        Map<String, String> result = new HashMap<>();\n        if (tags != null)\n        {\n            result = new HashMap<>(tags);\n        }\n        result.put(key, value);\n        return result;\n    }\n\n    /**\n     * A simple equality check that only looks at identifiers, tags, and parent relations.\n     *\n     * @param left\n     *            the left entity\n     * @param right\n     *            the right entity\n     * @return if the left and right entities are related through a simple equality\n     */\n    static boolean basicEqual(final AtlasEntity left, final AtlasEntity right)\n    {\n        return left.getIdentifier() == right.getIdentifier()\n                && Objects.equals(left.getTags(), right.getTags())\n                && Objects.equals(left.relations(), right.relations());\n    }\n\n    static <M, T> boolean equalThroughGet(final M left, final M right, final Function<M, T> getter)\n    {\n        if (left == null && right == null)\n        {\n            return true;\n        }\n        else if (left == null || right == null)\n        {\n            return false;\n        }\n        else\n        {\n            return Objects.equals(getter.apply(left), getter.apply(right));\n        }\n    }\n\n    /**\n     * Create a {@link CompleteEntity} from a given {@link AtlasEntity} reference. The\n     * {@link CompleteEntity}'s fields will match the fields of the reference. The returned\n     * {@link CompleteEntity} will be full, i.e. all of its associated fields will be non-null.\n     *\n     * @param reference\n     *            the reference to copy\n     * @return the full entity\n     */\n    static AtlasEntity from(final AtlasEntity reference)\n    {\n        final ItemType type = reference.getType();\n        switch (type)\n        {\n            case NODE:\n                return CompleteNode.from((Node) reference);\n            case EDGE:\n                return CompleteEdge.from((Edge) reference);\n            case AREA:\n                return CompleteArea.from((Area) reference);\n            case LINE:\n                return CompleteLine.from((Line) reference);\n            case POINT:\n                return CompletePoint.from((Point) reference);\n            case RELATION:\n                return CompleteRelation.from((Relation) reference);\n            default:\n                throw new CoreException(\"Unknown ItemType {}\", type);\n        }\n    }\n\n    static Map<String, String> removeTag(final Map<String, String> tags, final String key)\n    {\n        Map<String, String> result = new HashMap<>();\n        if (tags != null)\n        {\n            result = new HashMap<>(tags);\n        }\n        result.remove(key);\n        return result;\n    }\n\n    /**\n     * Create a shallow {@link CompleteEntity} from a given {@link AtlasEntity} reference. The\n     * {@link CompleteEntity}'s identifier will match the identifier of the reference. The returned\n     * {@link CompleteEntity} will be shallow, i.e. all of its associated fields will be null except\n     * for the identifier.\n     *\n     * @param reference\n     *            the reference to copy\n     * @return the shallow entity\n     */\n    static AtlasEntity shallowFrom(final AtlasEntity reference)\n    {\n        final ItemType type = reference.getType();\n        switch (type)\n        {\n            case NODE:\n                return CompleteNode.shallowFrom((Node) reference);\n            case EDGE:\n                return CompleteEdge.shallowFrom((Edge) reference);\n            case AREA:\n                return CompleteArea.shallowFrom((Area) reference);\n            case LINE:\n                return CompleteLine.shallowFrom((Line) reference);\n            case POINT:\n                return CompletePoint.shallowFrom((Point) reference);\n            case RELATION:\n                return CompleteRelation.shallowFrom((Relation) reference);\n            default:\n                throw new CoreException(\"Unknown ItemType {}\", type);\n        }\n    }\n\n    /**\n     * Create a shallow {@link CompleteEntity} from a given {@link ItemType} and identifier.\n     *\n     * @param type\n     *            the {@link ItemType}\n     * @param identifier\n     *            the identifier\n     * @return a shallow {@link CompleteEntity} that matches the requested parameters\n     */\n    static AtlasEntity shallowFrom(final ItemType type, final Long identifier)\n    {\n        switch (type)\n        {\n            case NODE:\n                return new CompleteNode(identifier);\n            case EDGE:\n                return new CompleteEdge(identifier);\n            case AREA:\n                return new CompleteArea(identifier);\n            case LINE:\n                return new CompleteLine(identifier);\n            case POINT:\n                return new CompletePoint(identifier);\n            case RELATION:\n                return new CompleteRelation(identifier);\n            default:\n                throw new CoreException(\"Unknown ItemType {}\", type);\n        }\n    }\n\n    static <C extends CompleteEntity<C>> C withAddedTag(final C completeEntity, final String key,\n            final String value, final boolean suppressFiringEvent)\n    {\n        CompleteEntity.withTags(completeEntity,\n                CompleteEntity.addNewTag(completeEntity.getTags(), key, value), true);\n\n        if (!suppressFiringEvent)\n        {\n            completeEntity\n                    .fireTagChangeEvent(TagChangeEvent.added(completeEntity.completeItemType(),\n                            completeEntity.getIdentifier(), Pair.of(key, value)));\n        }\n\n        return completeEntity;\n    }\n\n    static <C extends CompleteEntity<C>> C withRemovedTag(final C completeEntity, final String key,\n            final boolean suppressFiringEvent)\n    {\n        CompleteEntity.withTags(completeEntity,\n                CompleteEntity.removeTag(completeEntity.getTags(), key), true);\n\n        if (!suppressFiringEvent)\n        {\n            completeEntity.fireTagChangeEvent(TagChangeEvent.remove(\n                    completeEntity.completeItemType(), completeEntity.getIdentifier(), key));\n        }\n\n        return completeEntity;\n    }\n\n    static <C extends CompleteEntity<C>> C withReplacedTag(final C completeEntity,\n            final String oldKey, final String newKey, final String newValue,\n            final boolean suppressFiringEvent)\n    {\n        CompleteEntity.withRemovedTag(completeEntity, oldKey, true);\n        CompleteEntity.withAddedTag(completeEntity, newKey, newValue, true);\n\n        if (!suppressFiringEvent)\n        {\n            completeEntity\n                    .fireTagChangeEvent(TagChangeEvent.replaced(completeEntity.completeItemType(),\n                            completeEntity.getIdentifier(), Triple.of(oldKey, newKey, newValue)));\n        }\n\n        return completeEntity;\n    }\n\n    static <C extends CompleteEntity<C>> C withTags(final C completeEntity,\n            final Map<String, String> tags, final boolean suppressFiringEvent)\n    {\n        completeEntity.setTags(tags);\n\n        if (!suppressFiringEvent)\n        {\n            completeEntity.fireTagChangeEvent(TagChangeEvent.overwrite(\n                    completeEntity.completeItemType(), completeEntity.getIdentifier(), tags));\n        }\n\n        return completeEntity;\n    }\n\n    CompleteItemType completeItemType();\n\n    Iterable<Location> getGeometry();\n\n    long getIdentifier();\n\n    Map<String, String> getTags();\n\n    ItemType getType();\n\n    /**\n     * A full {@link CompleteEntity} is one one that contains a non-null value for all its fields.\n     *\n     * @return if this entity is full\n     */\n    boolean isFull();\n\n    /**\n     * A shallow {@link CompleteEntity} is one that contains only its identifier as effective data.\n     *\n     * @return if this entity is shallow\n     */\n    boolean isShallow();\n\n    /**\n     * Transform this {@link CompleteEntity} into a pretty string. The pretty string for a\n     * {@link CompleteEntity} can be customized using different available formats.\n     *\n     * @param format\n     *            the format type for the pretty string\n     * @param truncate\n     *            if the string should be truncated\n     * @return the pretty string\n     */\n    String prettify(PrettifyStringFormat format, boolean truncate);\n\n    /**\n     * Transform this {@link CompleteEntity} into a pretty string. The pretty string for a\n     * {@link CompleteEntity} can be customized using different available formats.\n     *\n     * @param format\n     *            the format type for the pretty string\n     * @return the pretty string\n     */\n    default String prettify(final PrettifyStringFormat format)\n    {\n        return prettify(PrettifyStringFormat.MINIMAL_SINGLE_LINE, true);\n    }\n\n    /**\n     * Transform this {@link CompleteEntity} into a pretty string. This method uses the default\n     * format {@link PrettifyStringFormat#MINIMAL_SINGLE_LINE}.\n     *\n     * @return the pretty string\n     */\n    default String prettify()\n    {\n        return prettify(PrettifyStringFormat.MINIMAL_SINGLE_LINE);\n    }\n\n    Set<Long> relationIdentifiers();\n\n    void setTags(Map<String, String> tags);\n\n    JsonObject toJson();\n\n    /**\n     * Get the WKT for this entity's geometry.\n     *\n     * @return the WKT of this entity's geometry, null if the geometry is null\n     */\n    String toWkt();\n\n    default String truncate(final String input)\n    {\n        return input.substring(0, Math.min(input.length(), PrettifyStringFormat.TRUNCATE_LENGTH))\n                + PrettifyStringFormat.TRUNCATE_ELLIPSES;\n    }\n\n    CompleteEntity withAddedRelationIdentifier(Long relationIdentifier);\n\n    default C withAddedTag(final String key, final String value)\n    {\n        return CompleteEntity.withAddedTag((C) this, key, value, false);\n    }\n\n    CompleteEntity withGeometry(Iterable<Location> locations);\n\n    CompleteEntity withIdentifier(long identifier);\n\n    CompleteEntity withRelationIdentifiers(Set<Long> relationIdentifiers);\n\n    CompleteEntity withRelations(Set<Relation> relations);\n\n    CompleteEntity withRemovedRelationIdentifier(Long relationIdentifier);\n\n    default C withRemovedTag(final String key)\n    {\n        return CompleteEntity.withRemovedTag((C) this, key, false);\n    }\n\n    default C withReplacedTag(final String oldKey, final String newKey, final String newValue)\n    {\n        return CompleteEntity.withReplacedTag((C) this, oldKey, newKey, newValue, false);\n    }\n\n    default C withTags(final Map<String, String> tags)\n    {\n        return CompleteEntity.withTags((C) this, tags, false);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteItemType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.Arrays;\n\nimport org.apache.commons.lang3.Validate;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * A mapping of {@link CompleteEntity}-ies to {@link ItemType}.\n *\n * @author Yazad Khambata\n */\npublic enum CompleteItemType\n{\n\n    NODE(CompleteNode.class, ItemType.NODE),\n    EDGE(CompleteEdge.class, ItemType.EDGE),\n    AREA(CompleteArea.class, ItemType.AREA),\n    LINE(CompleteLine.class, ItemType.LINE),\n    POINT(CompletePoint.class, ItemType.POINT),\n    RELATION(CompleteRelation.class, ItemType.RELATION);\n\n    private final Class<? extends CompleteEntity> completeEntityClass;\n\n    private final ItemType itemType;\n\n    public static CompleteItemType from(final ItemType itemType)\n    {\n        return Arrays.stream(CompleteItemType.values())\n                .filter(completeItemType -> completeItemType.itemType == itemType).findFirst()\n                .orElseThrow(IllegalArgumentException::new);\n    }\n\n    public static <C extends CompleteEntity> C shallowFrom(final AtlasEntity reference)\n    {\n        final ItemType itemType = reference.getType();\n        final CompleteItemType completeItemType = CompleteItemType.from(itemType);\n        final C completeEntity = completeItemType.completeEntityShallowFrom(reference);\n        return completeEntity;\n    }\n\n    CompleteItemType(final Class<? extends CompleteEntity> completeEntityClass,\n            final ItemType itemType)\n    {\n        this.completeEntityClass = completeEntityClass;\n        this.itemType = itemType;\n    }\n\n    public <C extends CompleteEntity> C completeEntityFrom(final AtlasEntity reference)\n    {\n        validate(reference);\n        return (C) CompleteEntity.from(reference);\n    }\n\n    public <C extends CompleteEntity> C completeEntityShallowFrom(final AtlasEntity reference)\n    {\n        validate(reference);\n        return (C) CompleteEntity.shallowFrom(reference);\n    }\n\n    public Class<? extends CompleteEntity> getCompleteEntityClass()\n    {\n        return this.completeEntityClass;\n    }\n\n    public ItemType getItemType()\n    {\n        return this.itemType;\n    }\n\n    private void validate(final AtlasEntity reference)\n    {\n        Validate.isTrue(getItemType().getMemberClass().isAssignableFrom(reference.getClass()),\n                \"reference: \" + reference + \"; cannot be converted to completed entity \" + this\n                        + \".\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteLine.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Independent {@link Line} that contains its own data. At scale, use at your own risk.\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class CompleteLine extends Line implements CompleteLineItem<CompleteLine>\n{\n    private static final long serialVersionUID = 309534717673911086L;\n\n    private Rectangle bounds;\n    private long identifier;\n    private PolyLine polyLine;\n    private Map<String, String> tags;\n    private Set<Long> geometricRelationIdentifiers;\n    private Set<Long> relationIdentifiers;\n\n    private final TagChangeDelegate tagChangeDelegate = TagChangeDelegate.newTagChangeDelegate();\n\n    /**\n     * Create a {@link CompleteLine} from a given {@link Line} reference. The {@link CompleteLine}'s\n     * fields will match the fields of the reference. The returned {@link CompleteLine} will be\n     * full, i.e. all of its associated fields will be non-null.\n     *\n     * @param line\n     *            the {@link Line} to copy\n     * @return the full {@link CompleteLine}\n     */\n    public static CompleteLine from(final Line line)\n    {\n        if (line instanceof CompleteLine && !((CompleteLine) line).isFull())\n        {\n            throw new CoreException(\"Line parameter was a CompleteLine but it was not full: {}\",\n                    line);\n        }\n        return new CompleteLine(line.getIdentifier(), line.asPolyLine(), line.getTags(),\n                line.relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet()))\n                .withGeometricRelationIdentifiers(\n                        line.relations().stream().filter(Relation::isGeometric)\n                                .filter(relation -> relation.asMultiPolygon().isPresent()\n                                        && !relation.asMultiPolygon().get().isEmpty())\n                                .map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    /**\n     * Create a shallow {@link CompleteLine} from a given {@link Line} reference. The\n     * {@link CompleteLine}'s identifier will match the identifier of the reference {@link Line}.\n     * The returned {@link CompleteLine} will be shallow, i.e. all of its associated fields will be\n     * null except for the identifier.\n     *\n     * @param line\n     *            the {@link Line} to copy\n     * @return the shallow {@link CompleteLine}\n     */\n    public static CompleteLine shallowFrom(final Line line)\n    {\n        if (line.bounds() == null)\n        {\n            throw new CoreException(\"Line parameter bounds were null\");\n        }\n        return new CompleteLine(line.getIdentifier()).withBoundsExtendedBy(line.bounds());\n    }\n\n    public CompleteLine(final Long identifier, final PolyLine polyLine,\n            final Map<String, String> tags, final Set<Long> relationIdentifiers)\n    {\n        super(new EmptyAtlas());\n\n        if (identifier == null)\n        {\n            throw new CoreException(\"Identifier can never be null.\");\n        }\n\n        this.bounds = polyLine != null ? polyLine.bounds() : null;\n\n        this.identifier = identifier;\n        this.polyLine = polyLine;\n        this.tags = tags;\n        this.relationIdentifiers = relationIdentifiers;\n        this.geometricRelationIdentifiers = new HashSet<>();\n    }\n\n    protected CompleteLine(final long identifier)\n    {\n        this(identifier, null, null, null);\n    }\n\n    @Override\n    public void addTagChangeListener(final TagChangeListener tagChangeListener)\n    {\n        this.tagChangeDelegate.addTagChangeListener(tagChangeListener);\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return this.polyLine;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bounds;\n    }\n\n    @Override\n    public CompleteItemType completeItemType()\n    {\n        return CompleteItemType.LINE;\n    }\n\n    public CompleteLine copy()\n    {\n        return new CompleteLine(this.identifier, this.polyLine, this.tags,\n                this.relationIdentifiers);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof CompleteLine)\n        {\n            final CompleteLine that = (CompleteLine) other;\n            return CompleteEntity.basicEqual(this, that)\n                    && Objects.equals(this.asPolyLine(), that.asPolyLine());\n        }\n        return false;\n    }\n\n    @Override\n    public void fireTagChangeEvent(final TagChangeEvent tagChangeEvent)\n    {\n        this.tagChangeDelegate.fireTagChangeEvent(tagChangeEvent);\n    }\n\n    public Set<Long> geometricRelationIdentifiers()\n    {\n        return this.geometricRelationIdentifiers;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    @Override\n    public boolean isFull()\n    {\n        return this.bounds != null && this.polyLine != null && this.tags != null\n                && this.relationIdentifiers != null;\n    }\n\n    @Override\n    public boolean isShallow()\n    {\n        return this.polyLine == null && this.tags == null && this.relationIdentifiers == null;\n    }\n\n    @Override\n    public String prettify(final PrettifyStringFormat format, final boolean truncate)\n    {\n        String separator = \"\";\n        if (format == PrettifyStringFormat.MINIMAL_SINGLE_LINE)\n        {\n            separator = \"\";\n        }\n        else if (format == PrettifyStringFormat.MINIMAL_MULTI_LINE)\n        {\n            separator = \"\\n\";\n        }\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName() + \" \");\n        builder.append(\"[\");\n        builder.append(separator);\n        builder.append(\"identifier: \" + this.identifier + \", \");\n        builder.append(separator);\n        if (this.polyLine != null)\n        {\n            if (truncate)\n            {\n                builder.append(\"polyLine: \" + truncate(this.polyLine.toString()) + \", \");\n            }\n            else\n            {\n                builder.append(\"polyLine: \" + this.polyLine.toString() + \", \");\n            }\n            builder.append(separator);\n        }\n        if (this.tags != null)\n        {\n            builder.append(\"tags: \" + new TreeMap<>(this.tags) + \", \");\n            builder.append(separator);\n        }\n        if (this.relationIdentifiers != null)\n        {\n            builder.append(\"parentRelations: \" + new TreeSet<>(this.relationIdentifiers) + \", \");\n            builder.append(separator);\n        }\n        if (this.bounds != null)\n        {\n            builder.append(\"bounds: \" + this.bounds.toWkt() + \", \");\n            builder.append(separator);\n        }\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    @Override\n    public Set<Long> relationIdentifiers()\n    {\n        return this.relationIdentifiers;\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        /*\n         * Note that the Relations returned by this method will technically break the Located\n         * contract, since they have null bounds.\n         */\n        return this.relationIdentifiers == null ? null\n                : this.relationIdentifiers.stream().map(CompleteRelation::new)\n                        .collect(Collectors.toSet());\n    }\n\n    @Override\n    public void removeTagChangeListeners()\n    {\n        this.tagChangeDelegate.removeTagChangeListeners();\n    }\n\n    @Override\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags != null ? new HashMap<>(tags) : null;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getSimpleName() + \" [identifier=\" + this.identifier + \", polyLine=\"\n                + this.polyLine + \", tags=\" + this.tags + \", relationIdentifiers=\"\n                + this.relationIdentifiers + \"]\";\n    }\n\n    @Override\n    public String toWkt()\n    {\n        if (this.polyLine == null)\n        {\n            return null;\n        }\n        return this.polyLine.toWkt();\n    }\n\n    @Override\n    public CompleteLine withAddedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers.add(relationIdentifier);\n        return this;\n    }\n\n    public CompleteLine withBoundsExtendedBy(final Rectangle bounds)\n    {\n        if (this.bounds == null)\n        {\n            this.bounds = bounds;\n            return this;\n        }\n        this.bounds = Rectangle.forLocated(this.bounds, bounds);\n        return this;\n    }\n\n    public CompleteLine withGeometricRelationIdentifiers(\n            final Set<Long> geometricRelationIdentifiers)\n    {\n        this.geometricRelationIdentifiers = geometricRelationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompleteEntity withGeometry(final Iterable<Location> locations)\n    {\n        return this.withPolyLine(new PolyLine(locations));\n    }\n\n    @Override\n    public CompleteLine withIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n        return this;\n    }\n\n    @Override\n    public CompleteLine withPolyLine(final PolyLine polyLine)\n    {\n        this.polyLine = polyLine;\n        if (this.polyLine != null)\n        {\n            this.bounds = polyLine.bounds();\n        }\n        return this;\n    }\n\n    @Override\n    public CompleteLine withRelationIdentifiers(final Set<Long> relationIdentifiers)\n    {\n        this.relationIdentifiers = relationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompleteLine withRelations(final Set<Relation> relations)\n    {\n        this.relationIdentifiers = relations.stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet());\n        this.geometricRelationIdentifiers = relations.stream().filter(Relation::isGeometric)\n                .filter(relation -> relation.asMultiPolygon().isPresent()\n                        && !relation.asMultiPolygon().get().isEmpty())\n                .map(Relation::getIdentifier).collect(Collectors.toSet());\n        return this;\n    }\n\n    @Override\n    public CompleteLine withRemovedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers = this.relationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        this.geometricRelationIdentifiers = this.geometricRelationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteLineItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.ArrayList;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\n\n/**\n * Similar to a {@link org.openstreetmap.atlas.geography.atlas.items.LineItem} but for\n * {@link CompleteEntity}-ies.\n *\n * @param <E>\n *            the {@link CompleteEntity} being worked on.\n * @author Yazad Khambata\n */\npublic interface CompleteLineItem<E extends CompleteLineItem<E>> extends CompleteEntity<E>\n{\n    PolyLine asPolyLine();\n\n    @Override\n    default Iterable<Location> getGeometry()\n    {\n        if (asPolyLine() != null)\n        {\n            return new ArrayList<>(asPolyLine());\n        }\n        return null;\n    }\n\n    CompleteLineItem withPolyLine(PolyLine polyLine);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteLocationItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Location;\n\n/**\n * Similar to a {@link org.openstreetmap.atlas.geography.atlas.items.LocationItem} but for\n * {@link CompleteEntity}-ies.\n *\n * @param <E>\n *            - the {@link CompleteEntity} bveing worked on.\n * @author Yazad Khambata\n */\npublic interface CompleteLocationItem<E extends CompleteLocationItem<E>> extends CompleteEntity<E>\n{\n    @Override\n    default Iterable<Location> getGeometry()\n    {\n        if (getLocation() != null)\n        {\n            final List<Location> geometry = new ArrayList<>();\n            geometry.add(getLocation());\n            return geometry;\n        }\n        return null;\n    }\n\n    Location getLocation();\n\n    CompleteLocationItem withLocation(Location location);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteNode.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * Independent {@link Node} that may contain its own altered data. At scale, use at your own risk.\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class CompleteNode extends Node implements CompleteLocationItem<CompleteNode>\n{\n    private static final long serialVersionUID = -8229589987121555419L;\n\n    private final TagChangeDelegate tagChangeDelegate = TagChangeDelegate.newTagChangeDelegate();\n\n    private Rectangle bounds;\n    private long identifier;\n    private Location location;\n    private Map<String, String> tags;\n    private SortedSet<Long> inEdgeIdentifiers;\n    private SortedSet<Long> outEdgeIdentifiers;\n    private Set<Long> explicitlyExcludedInEdgeIdentifiers;\n    private Set<Long> explicitlyExcludedOutEdgeIdentifiers;\n    private Set<Long> relationIdentifiers;\n\n    /**\n     * Create a {@link CompleteNode} from a given {@link Node} reference. The {@link CompleteNode}'s\n     * fields will match the fields of the reference. The returned {@link CompleteNode} will be\n     * full, i.e. all of its associated fields will be non-null.\n     *\n     * @param node\n     *            the {@link Node} to copy\n     * @return the full {@link CompleteNode}\n     */\n    public static CompleteNode from(final Node node)\n    {\n        if (node instanceof CompleteNode && !((CompleteNode) node).isFull())\n        {\n            throw new CoreException(\"Node parameter was a CompleteNode but it was not full: {}\",\n                    node);\n        }\n        return new CompleteNode(node.getIdentifier(), node.getLocation(), node.getTags(),\n                node.inEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toCollection(TreeSet::new)),\n                node.outEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toCollection(TreeSet::new)),\n                node.relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    /**\n     * Create a shallow {@link CompleteNode} from a given {@link Node} reference. The\n     * {@link CompleteNode}'s identifier will match the identifier of the reference {@link Node}.\n     * The returned {@link CompleteNode} will be shallow, i.e. all of its associated fields will be\n     * null except for the identifier.\n     *\n     * @param node\n     *            the {@link Node} to copy\n     * @return the shallow {@link CompleteNode}\n     */\n    public static CompleteNode shallowFrom(final Node node)\n    {\n        if (node.bounds() == null)\n        {\n            throw new CoreException(\"Node parameter bounds were null\");\n        }\n        return new CompleteNode(node.getIdentifier()).withBoundsExtendedBy(node.bounds());\n    }\n\n    public CompleteNode(final Long identifier, final Location location,\n            final Map<String, String> tags, final SortedSet<Long> inEdgeIdentifiers,\n            final SortedSet<Long> outEdgeIdentifiers, final Set<Long> relationIdentifiers)\n    {\n        super(new EmptyAtlas());\n\n        if (identifier == null)\n        {\n            throw new CoreException(\"Identifier can never be null.\");\n        }\n\n        this.bounds = location != null ? location.bounds() : null;\n\n        this.identifier = identifier;\n        this.location = location;\n        this.tags = tags;\n        this.inEdgeIdentifiers = inEdgeIdentifiers;\n        this.outEdgeIdentifiers = outEdgeIdentifiers;\n        this.explicitlyExcludedInEdgeIdentifiers = new HashSet<>();\n        this.explicitlyExcludedOutEdgeIdentifiers = new HashSet<>();\n        this.relationIdentifiers = relationIdentifiers;\n    }\n\n    CompleteNode(final long identifier)\n    {\n        this(identifier, null, null, null, null, null);\n    }\n\n    @Override\n    public void addTagChangeListener(final TagChangeListener tagChangeListener)\n    {\n        this.tagChangeDelegate.addTagChangeListener(tagChangeListener);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bounds;\n    }\n\n    @Override\n    public CompleteItemType completeItemType()\n    {\n        return CompleteItemType.NODE;\n    }\n\n    public CompleteNode copy()\n    {\n        return new CompleteNode(this.identifier, this.location, this.tags, this.inEdgeIdentifiers,\n                this.outEdgeIdentifiers, this.relationIdentifiers);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof CompleteNode)\n        {\n            final CompleteNode that = (CompleteNode) other;\n            return CompleteEntity.basicEqual(this, that)\n                    && Objects.equals(this.getLocation(), that.getLocation())\n                    && Objects.equals(this.inEdges(), that.inEdges())\n                    && Objects.equals(this.outEdges(), that.outEdges());\n        }\n        return false;\n    }\n\n    public Set<Long> explicitlyExcludedInEdgeIdentifiers()\n    {\n        return this.explicitlyExcludedInEdgeIdentifiers;\n    }\n\n    public Set<Long> explicitlyExcludedOutEdgeIdentifiers()\n    {\n        return this.explicitlyExcludedOutEdgeIdentifiers;\n    }\n\n    @Override\n    public void fireTagChangeEvent(final TagChangeEvent tagChangeEvent)\n    {\n        this.tagChangeDelegate.fireTagChangeEvent(tagChangeEvent);\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return this.location;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    public Set<Long> inEdgeIdentifiers()\n    {\n        return this.inEdgeIdentifiers;\n    }\n\n    @Override\n    public SortedSet<Edge> inEdges()\n    {\n        /*\n         * Note that the Edges returned by this method will technically break the Located contract,\n         * since they have null bounds.\n         */\n        return this.inEdgeIdentifiers == null ? null\n                : this.inEdgeIdentifiers.stream().map(CompleteEdge::new)\n                        .collect(Collectors.toCollection(TreeSet::new));\n    }\n\n    @Override\n    public boolean isFull()\n    {\n        return this.bounds != null && this.location != null && this.tags != null\n                && this.inEdgeIdentifiers != null && this.outEdgeIdentifiers != null\n                && this.relationIdentifiers != null;\n    }\n\n    @Override\n    public boolean isShallow()\n    {\n        return this.location == null && this.inEdgeIdentifiers == null\n                && this.outEdgeIdentifiers == null && this.tags == null\n                && this.relationIdentifiers == null;\n    }\n\n    public Set<Long> outEdgeIdentifiers()\n    {\n        return this.outEdgeIdentifiers;\n    }\n\n    @Override\n    public SortedSet<Edge> outEdges()\n    {\n        /*\n         * Note that the Edges returned by this method will technically break the Located contract,\n         * since they have null bounds.\n         */\n        return this.outEdgeIdentifiers == null ? null\n                : this.outEdgeIdentifiers.stream().map(CompleteEdge::new)\n                        .collect(Collectors.toCollection(TreeSet::new));\n    }\n\n    @Override\n    public String prettify(final PrettifyStringFormat format, final boolean truncate)\n    {\n        String separator = \"\";\n        if (format == PrettifyStringFormat.MINIMAL_SINGLE_LINE)\n        {\n            separator = \"\";\n        }\n        else if (format == PrettifyStringFormat.MINIMAL_MULTI_LINE)\n        {\n            separator = \"\\n\";\n        }\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName() + \" \");\n        builder.append(\"[\");\n        builder.append(separator);\n        builder.append(\"identifier: \" + this.identifier + \", \");\n        builder.append(separator);\n        if (this.location != null)\n        {\n            builder.append(\"geometry: \" + this.location + \", \");\n            builder.append(separator);\n        }\n        if (this.tags != null)\n        {\n            builder.append(\"tags: \" + new TreeMap<>(this.tags) + \", \");\n            builder.append(separator);\n        }\n        if (this.inEdgeIdentifiers != null)\n        {\n            builder.append(\"inEdges: \" + this.inEdgeIdentifiers + \", \");\n            builder.append(separator);\n        }\n        if (this.explicitlyExcludedInEdgeIdentifiers != null\n                && !this.explicitlyExcludedInEdgeIdentifiers.isEmpty())\n        {\n            builder.append(\"explicitlyExcludedInEdges: \"\n                    + new TreeSet<>(this.explicitlyExcludedInEdgeIdentifiers) + \", \");\n            builder.append(separator);\n        }\n        if (this.outEdgeIdentifiers != null)\n        {\n            builder.append(\"outEdges: \" + this.outEdgeIdentifiers + \", \");\n            builder.append(separator);\n        }\n        if (this.explicitlyExcludedOutEdgeIdentifiers != null\n                && !this.explicitlyExcludedOutEdgeIdentifiers.isEmpty())\n        {\n            builder.append(\"explicitlyExcludedOutEdges: \"\n                    + new TreeSet<>(this.explicitlyExcludedOutEdgeIdentifiers) + \", \");\n            builder.append(separator);\n        }\n        if (this.relationIdentifiers != null)\n        {\n            builder.append(\"parentRelations: \" + new TreeSet<>(this.relationIdentifiers) + \", \");\n            builder.append(separator);\n        }\n        if (this.bounds != null)\n        {\n            builder.append(\"bounds: \" + this.bounds.toWkt() + \", \");\n            builder.append(separator);\n        }\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    @Override\n    public Set<Long> relationIdentifiers()\n    {\n        return this.relationIdentifiers;\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        /*\n         * Note that the Relations returned by this method will technically break the Located\n         * contract, since they have null bounds.\n         */\n        return this.relationIdentifiers == null ? null\n                : this.relationIdentifiers.stream().map(CompleteRelation::new)\n                        .collect(Collectors.toSet());\n    }\n\n    @Override\n    public void removeTagChangeListeners()\n    {\n        this.tagChangeDelegate.removeTagChangeListeners();\n    }\n\n    public void setExplicitlyExcludedInEdgeIdentifiers(final Set<Long> edges)\n    {\n        this.explicitlyExcludedInEdgeIdentifiers = edges;\n    }\n\n    public void setExplicitlyExcludedOutEdgeIdentifiers(final Set<Long> edges)\n    {\n        this.explicitlyExcludedOutEdgeIdentifiers = edges;\n    }\n\n    @Override\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags != null ? new HashMap<>(tags) : null;\n    }\n\n    @Override\n    public JsonObject toJson()\n    {\n        final JsonObject nodeObject = super.toJson();\n\n        final JsonArray inEdgeIdentifiersArray = new JsonArray();\n        for (final Long inEdgeIdentifier : new TreeSet<>(this.inEdgeIdentifiers))\n        {\n            inEdgeIdentifiersArray.add(new JsonPrimitive(inEdgeIdentifier));\n        }\n        final JsonArray outEdgeIdentifiersArray = new JsonArray();\n        for (final Long outEdgeIdentifier : new TreeSet<>(this.outEdgeIdentifiers))\n        {\n            outEdgeIdentifiersArray.add(new JsonPrimitive(outEdgeIdentifier));\n        }\n        nodeObject.add(\"inEdges\", inEdgeIdentifiersArray);\n        nodeObject.add(\"outEdges\", outEdgeIdentifiersArray);\n\n        return nodeObject;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getSimpleName() + \" [identifier=\" + this.identifier\n                + \", inEdgeIdentifiers=\" + this.inEdgeIdentifiers + \", outEdgeIdentifiers=\"\n                + this.outEdgeIdentifiers + \", location=\" + this.location + \", tags=\" + this.tags\n                + \", relationIdentifiers=\" + this.relationIdentifiers + \"]\";\n    }\n\n    @Override\n    public String toWkt()\n    {\n        if (this.location == null)\n        {\n            return null;\n        }\n        return this.location.toWkt();\n    }\n\n    public CompleteNode withAddedInEdgeIdentifier(final Long inEdgeIdentifier)\n    {\n        this.inEdgeIdentifiers.add(inEdgeIdentifier);\n        return this;\n    }\n\n    public CompleteNode withAddedOutEdgeIdentifier(final Long inEdgeIdentifier)\n    {\n        this.outEdgeIdentifiers.add(inEdgeIdentifier);\n        return this;\n    }\n\n    @Override\n    public CompleteNode withAddedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers.add(relationIdentifier);\n        return this;\n    }\n\n    public CompleteNode withBoundsExtendedBy(final Rectangle bounds)\n    {\n        if (this.bounds == null)\n        {\n            this.bounds = bounds;\n            return this;\n        }\n        this.bounds = Rectangle.forLocated(this.bounds, bounds);\n        return this;\n    }\n\n    @Override\n    public CompleteEntity withGeometry(final Iterable<Location> locations)\n    {\n        if (!locations.iterator().hasNext())\n        {\n            throw new CoreException(\"Cannot interpret empty Iterable as a Location\");\n        }\n        return this.withLocation(locations.iterator().next());\n    }\n\n    @Override\n    public CompleteNode withIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n        return this;\n    }\n\n    public CompleteNode withInEdgeIdentifiers(final SortedSet<Long> inEdgeIdentifiers)\n    {\n        this.inEdgeIdentifiers = inEdgeIdentifiers;\n        return this;\n    }\n\n    public CompleteNode withInEdgeIdentifiersAndSource(final SortedSet<Long> inEdgeIdentifiers,\n            final Node source)\n    {\n        final Set<Long> sourceIdentifiers = source.inEdges().stream().map(Edge::getIdentifier)\n                .collect(Collectors.toSet());\n        final Set<Long> excludedBasedOnSource = com.google.common.collect.Sets\n                .difference(sourceIdentifiers, inEdgeIdentifiers);\n        this.inEdgeIdentifiers = inEdgeIdentifiers;\n        this.explicitlyExcludedInEdgeIdentifiers.addAll(excludedBasedOnSource);\n        return this;\n    }\n\n    public CompleteNode withInEdges(final Set<Edge> inEdges)\n    {\n        this.inEdgeIdentifiers = inEdges.stream().map(Edge::getIdentifier)\n                .collect(Collectors.toCollection(TreeSet::new));\n        return this;\n    }\n\n    public CompleteNode withInEdgesAndSource(final Set<Edge> inEdges, final Node source)\n    {\n        return withInEdgeIdentifiersAndSource(inEdges.stream().map(Edge::getIdentifier)\n                .collect(Collectors.toCollection(TreeSet::new)), source);\n    }\n\n    @Override\n    public CompleteNode withLocation(final Location location)\n    {\n        this.location = location;\n        if (this.location != null)\n        {\n            this.bounds = location.bounds();\n        }\n        return this;\n    }\n\n    public CompleteNode withOutEdgeIdentifiers(final SortedSet<Long> outEdgeIdentifiers)\n    {\n        this.outEdgeIdentifiers = outEdgeIdentifiers;\n        return this;\n    }\n\n    public CompleteNode withOutEdgeIdentifiersAndSource(final SortedSet<Long> outEdgeIdentifiers,\n            final Node source)\n    {\n        final Set<Long> sourceIdentifiers = source.outEdges().stream().map(Edge::getIdentifier)\n                .collect(Collectors.toSet());\n        final Set<Long> excludedBasedOnSource = com.google.common.collect.Sets\n                .difference(sourceIdentifiers, outEdgeIdentifiers);\n        this.outEdgeIdentifiers = outEdgeIdentifiers;\n        this.explicitlyExcludedOutEdgeIdentifiers.addAll(excludedBasedOnSource);\n        return this;\n    }\n\n    public CompleteNode withOutEdges(final Set<Edge> outEdges)\n    {\n        this.outEdgeIdentifiers = outEdges.stream().map(Edge::getIdentifier)\n                .collect(Collectors.toCollection(TreeSet::new));\n        return this;\n    }\n\n    public CompleteNode withOutEdgesAndSource(final Set<Edge> outEdges, final Node source)\n    {\n        return withOutEdgeIdentifiersAndSource(outEdges.stream().map(Edge::getIdentifier)\n                .collect(Collectors.toCollection(TreeSet::new)), source);\n    }\n\n    @Override\n    public CompleteNode withRelationIdentifiers(final Set<Long> relationIdentifiers)\n    {\n        this.relationIdentifiers = relationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompleteNode withRelations(final Set<Relation> relations)\n    {\n        this.relationIdentifiers = relations.stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet());\n        return this;\n    }\n\n    public CompleteNode withRemovedInEdgeIdentifier(final Long inEdgeIdentifier)\n    {\n        this.inEdgeIdentifiers.remove(inEdgeIdentifier);\n        this.explicitlyExcludedInEdgeIdentifiers.add(inEdgeIdentifier);\n        return this;\n    }\n\n    public CompleteNode withRemovedOutEdgeIdentifier(final Long outEdgeIdentifier)\n    {\n        this.outEdgeIdentifiers.remove(outEdgeIdentifier);\n        this.explicitlyExcludedOutEdgeIdentifiers.add(outEdgeIdentifier);\n        return this;\n    }\n\n    @Override\n    public CompleteNode withRemovedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers = this.relationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        return this;\n    }\n\n    public CompleteNode withReplacedInEdgeIdentifier(final Long beforeInEdgeIdentifier,\n            final Long afterInEdgeIdentifier)\n    {\n        return this.withRemovedInEdgeIdentifier(beforeInEdgeIdentifier)\n                .withAddedInEdgeIdentifier(afterInEdgeIdentifier);\n    }\n\n    public CompleteNode withReplacedOutEdgeIdentifier(final Long beforeOutEdgeIdentifier,\n            final Long afterOutEdgeIdentifier)\n    {\n        return this.withRemovedOutEdgeIdentifier(beforeOutEdgeIdentifier)\n                .withAddedOutEdgeIdentifier(afterOutEdgeIdentifier);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompletePoint.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Independent {@link Point} that contains its own data. At scale, use at your own risk.\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class CompletePoint extends Point implements CompleteLocationItem<CompletePoint>\n{\n    private static final long serialVersionUID = 309534717673911086L;\n\n    private Rectangle bounds;\n    private long identifier;\n    private Location location;\n    private Map<String, String> tags;\n    private Set<Long> relationIdentifiers;\n    private final TagChangeDelegate tagChangeDelegate = TagChangeDelegate.newTagChangeDelegate();\n\n    /**\n     * Create a {@link CompletePoint} from a given {@link Point} reference. The\n     * {@link CompletePoint}'s fields will match the fields of the reference. The returned\n     * {@link CompletePoint} will be full, i.e. all of its associated fields will be non-null.\n     *\n     * @param point\n     *            the {@link Point} to copy\n     * @return the full {@link CompletePoint}\n     */\n    public static CompletePoint from(final Point point)\n    {\n        if (point instanceof CompletePoint && !((CompletePoint) point).isFull())\n        {\n            throw new CoreException(\"Point parameter was a CompletePoint but it was not full: {}\",\n                    point);\n        }\n        return new CompletePoint(point.getIdentifier(), point.getLocation(), point.getTags(), point\n                .relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    /**\n     * Create a shallow {@link CompletePoint} from a given {@link Point} reference. The\n     * {@link CompletePoint}'s identifier will match the identifier of the reference {@link Point}.\n     * The returned {@link CompletePoint} will be shallow, i.e. all of its associated fields will be\n     * null except for the identifier.\n     *\n     * @param point\n     *            the {@link Point} to copy\n     * @return the shallow {@link CompletePoint}\n     */\n    public static CompletePoint shallowFrom(final Point point)\n    {\n        if (point.bounds() == null)\n        {\n            throw new CoreException(\"Point parameter bounds were null\");\n        }\n        return new CompletePoint(point.getIdentifier()).withBoundsExtendedBy(point.bounds());\n    }\n\n    public CompletePoint(final Long identifier, final Location location,\n            final Map<String, String> tags, final Set<Long> relationIdentifiers)\n    {\n        super(new EmptyAtlas());\n\n        if (identifier == null)\n        {\n            throw new CoreException(\"Identifier can never be null.\");\n        }\n\n        this.bounds = location != null ? location.bounds() : null;\n\n        this.identifier = identifier;\n        this.location = location;\n        this.tags = tags;\n        this.relationIdentifiers = relationIdentifiers;\n    }\n\n    CompletePoint(final long identifier)\n    {\n        this(identifier, null, null, null);\n    }\n\n    @Override\n    public void addTagChangeListener(final TagChangeListener tagChangeListener)\n    {\n        this.tagChangeDelegate.addTagChangeListener(tagChangeListener);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bounds;\n    }\n\n    @Override\n    public CompleteItemType completeItemType()\n    {\n        return CompleteItemType.POINT;\n    }\n\n    public CompletePoint copy()\n    {\n        return new CompletePoint(this.identifier, this.location, this.tags,\n                this.relationIdentifiers);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof CompletePoint)\n        {\n            final CompletePoint that = (CompletePoint) other;\n            return CompleteEntity.basicEqual(this, that)\n                    && Objects.equals(this.getLocation(), that.getLocation());\n        }\n        return false;\n    }\n\n    @Override\n    public void fireTagChangeEvent(final TagChangeEvent tagChangeEvent)\n    {\n        this.tagChangeDelegate.fireTagChangeEvent(tagChangeEvent);\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return this.location;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    @Override\n    public boolean isFull()\n    {\n        return this.bounds != null && this.location != null && this.tags != null\n                && this.relationIdentifiers != null;\n    }\n\n    @Override\n    public boolean isShallow()\n    {\n        return this.location == null && this.tags == null && this.relationIdentifiers == null;\n    }\n\n    @Override\n    public String prettify(final PrettifyStringFormat format, final boolean truncate)\n    {\n        String separator = \"\";\n        if (format == PrettifyStringFormat.MINIMAL_SINGLE_LINE)\n        {\n            separator = \"\";\n        }\n        else if (format == PrettifyStringFormat.MINIMAL_MULTI_LINE)\n        {\n            separator = \"\\n\";\n        }\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName() + \" \");\n        builder.append(\"[\");\n        builder.append(separator);\n        builder.append(\"identifier: \" + this.identifier + \", \");\n        builder.append(separator);\n        if (this.location != null)\n        {\n            builder.append(\"geometry: \" + this.location + \", \");\n            builder.append(separator);\n        }\n        if (this.tags != null)\n        {\n            builder.append(\"tags: \" + new TreeMap<>(this.tags) + \", \");\n            builder.append(separator);\n        }\n        if (this.relationIdentifiers != null)\n        {\n            builder.append(\"parentRelations: \" + new TreeSet<>(this.relationIdentifiers).toString()\n                    + \", \");\n            builder.append(separator);\n        }\n        if (this.bounds != null)\n        {\n            builder.append(\"bounds: \" + this.bounds.toWkt() + \", \");\n            builder.append(separator);\n        }\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    @Override\n    public Set<Long> relationIdentifiers()\n    {\n        return this.relationIdentifiers;\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        /*\n         * Note that the Relations returned by this method will technically break the Located\n         * contract, since they have null bounds.\n         */\n        return this.relationIdentifiers == null ? null\n                : this.relationIdentifiers.stream().map(CompleteRelation::new)\n                        .collect(Collectors.toSet());\n    }\n\n    @Override\n    public void removeTagChangeListeners()\n    {\n        this.tagChangeDelegate.removeTagChangeListeners();\n    }\n\n    @Override\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags != null ? new HashMap<>(tags) : null;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getSimpleName() + \" [identifier=\" + this.identifier + \", location=\"\n                + this.location + \", tags=\" + this.tags + \", relationIdentifiers=\"\n                + this.relationIdentifiers + \"]\";\n    }\n\n    @Override\n    public String toWkt()\n    {\n        if (this.location == null)\n        {\n            return null;\n        }\n        return this.location.toWkt();\n    }\n\n    @Override\n    public CompletePoint withAddedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers.add(relationIdentifier);\n        return this;\n    }\n\n    public CompletePoint withBoundsExtendedBy(final Rectangle bounds)\n    {\n        if (this.bounds == null)\n        {\n            this.bounds = bounds;\n            return this;\n        }\n        this.bounds = Rectangle.forLocated(this.bounds, bounds);\n        return this;\n    }\n\n    @Override\n    public CompletePoint withGeometry(final Iterable<Location> locations)\n    {\n        if (!locations.iterator().hasNext())\n        {\n            throw new CoreException(\"Cannot interpret empty Iterable as a Location\");\n        }\n        return this.withLocation(locations.iterator().next());\n    }\n\n    @Override\n    public CompletePoint withIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n        return this;\n    }\n\n    @Override\n    public CompletePoint withLocation(final Location location)\n    {\n        this.location = location;\n        if (this.location != null)\n        {\n            this.bounds = location.bounds();\n        }\n        return this;\n    }\n\n    @Override\n    public CompletePoint withRelationIdentifiers(final Set<Long> relationIdentifiers)\n    {\n        this.relationIdentifiers = relationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompletePoint withRelations(final Set<Relation> relations)\n    {\n        this.relationIdentifiers = relations.stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet());\n        return this;\n    }\n\n    @Override\n    public CompletePoint withRemovedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers = this.relationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteRelation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\n/**\n * Independent {@link Relation} that contains its own data. At scale, use at your own risk.\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class CompleteRelation extends Relation implements CompleteEntity<CompleteRelation>\n{\n    private static final long serialVersionUID = -8295865049110084558L;\n    private static final Logger logger = LoggerFactory.getLogger(CompleteRelation.class);\n\n    private Rectangle bounds;\n    private long identifier;\n    private Map<String, String> tags;\n    private RelationBean members;\n    private List<Long> allRelationsWithSameOsmIdentifier;\n    private RelationBean allKnownOsmMembers;\n    private Long osmRelationIdentifier;\n    private Set<Long> relationIdentifiers;\n    private MultiPolygon storedGeometry;\n    private boolean overrideGeometry = false;\n    private final Set<LineString> addedGeometry = new HashSet<>();\n    private final Set<LineString> removedGeometry = new HashSet<>();\n\n    private final TagChangeDelegate tagChangeDelegate = TagChangeDelegate.newTagChangeDelegate();\n\n    /**\n     * Create a {@link CompleteRelation} from a given {@link Relation} reference. The\n     * {@link CompleteRelation}'s fields will match the fields of the reference. The returned\n     * {@link CompleteRelation} will be full, i.e. all of its associated fields will be non-null.\n     *\n     * @param relation\n     *            the {@link Relation} to copy\n     * @return the full {@link CompleteRelation}\n     */\n    public static CompleteRelation from(final Relation relation)\n    {\n        if (relation instanceof CompleteRelation && !((CompleteRelation) relation).isFull())\n        {\n            throw new CoreException(\n                    \"Relation parameter was a CompleteRelation but it was not full: {}\", relation);\n        }\n        return new CompleteRelation(relation.getIdentifier(), relation.getTags(), relation.bounds(),\n                relation.members().asBean(),\n                relation.allRelationsWithSameOsmIdentifier().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toList()),\n                relation.allKnownOsmMembers().asBean(), relation.osmRelationIdentifier(),\n                relation.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                relation.asMultiPolygon().orElse(null));\n    }\n\n    /**\n     * Create a shallow {@link CompleteRelation} from a given {@link Relation} reference. The\n     * {@link CompleteRelation}'s identifier will match the identifier of the reference\n     * {@link Relation}. The returned {@link CompleteRelation} will be shallow, i.e. all of its\n     * associated fields will be null except for the identifier.\n     *\n     * @param relation\n     *            the {@link Relation} to copy\n     * @return the shallow {@link CompleteRelation}\n     */\n    public static CompleteRelation shallowFrom(final Relation relation)\n    {\n        if (relation.bounds() == null)\n        {\n            throw new CoreException(\"Relation parameter bounds were null\");\n        }\n        return new CompleteRelation(relation.getIdentifier())\n                .withBoundsExtendedBy(relation.bounds());\n    }\n\n    public CompleteRelation(final long identifier)\n    {\n        this(identifier, null, null, null, null, null, null, null);\n    }\n\n    public CompleteRelation(final Long identifier, final Map<String, String> tags, // NOSONAR\n            final Rectangle bounds, final RelationBean members,\n            final List<Long> allRelationsWithSameOsmIdentifier,\n            final RelationBean allKnownOsmMembers, final Long osmRelationIdentifier,\n            final Set<Long> relationIdentifiers)\n    {\n        this(identifier, tags, bounds, members, allRelationsWithSameOsmIdentifier,\n                allKnownOsmMembers, osmRelationIdentifier, relationIdentifiers, null);\n    }\n\n    public CompleteRelation(final Long identifier, final Map<String, String> tags, // NOSONAR\n            final Rectangle bounds, final RelationBean members,\n            final List<Long> allRelationsWithSameOsmIdentifier,\n            final RelationBean allKnownOsmMembers, final Long osmRelationIdentifier,\n            final Set<Long> relationIdentifiers, final MultiPolygon jtsGeometry)\n    {\n        super(new EmptyAtlas());\n\n        if (identifier == null)\n        {\n            throw new CoreException(\"Identifier can never be null.\");\n        }\n\n        this.bounds = bounds != null ? bounds : null;\n\n        this.identifier = identifier;\n        this.tags = tags;\n        this.members = members;\n        this.allRelationsWithSameOsmIdentifier = allRelationsWithSameOsmIdentifier;\n        this.allKnownOsmMembers = allKnownOsmMembers;\n        this.osmRelationIdentifier = osmRelationIdentifier;\n        this.relationIdentifiers = relationIdentifiers;\n        this.storedGeometry = jtsGeometry;\n    }\n\n    protected CompleteRelation(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    @Override\n    public void addTagChangeListener(final TagChangeListener tagChangeListener)\n    {\n        this.tagChangeDelegate.addTagChangeListener(tagChangeListener);\n    }\n\n    @Override\n    public RelationMemberList allKnownOsmMembers()\n    {\n        return membersFor(this.allKnownOsmMembers);\n    }\n\n    @Override\n    public List<Relation> allRelationsWithSameOsmIdentifier()\n    {\n        /*\n         * Note that the Relations returned by this method will technically break the Located\n         * contract, since they have null bounds.\n         */\n        return this.allRelationsWithSameOsmIdentifier == null ? null\n                : this.allRelationsWithSameOsmIdentifier.stream().map(CompleteRelation::new)\n                        .collect(Collectors.toList());\n    }\n\n    @Override\n    public Optional<MultiPolygon> asMultiPolygon()\n    {\n        return Optional.ofNullable(this.storedGeometry);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bounds;\n    }\n\n    public CompleteRelation changeMemberRole(final AtlasEntity member, final String role)\n    {\n        final Optional<RelationBeanItem> oldItem = this.members.getItemFor(member.getIdentifier(),\n                member.getType());\n        if (oldItem.isPresent())\n        {\n            if (isGeometric() && this.asMultiPolygon().isPresent())\n            {\n                throw new CoreException(\n                        \"Cannot modify roles directly for relations with existing geometry! Please remove and add members directly instead\");\n            }\n            else\n            {\n                final RelationBeanItem old = oldItem.get();\n                this.members.removeItem(old);\n                final RelationBeanItem newItem = new RelationBeanItem(member.getIdentifier(), role,\n                        member.getType());\n                this.members.addItem(newItem);\n                this.members.addItemExplicitlyExcluded(member.getIdentifier(), old.getRole(),\n                        member.getType());\n            }\n        }\n        return this;\n    }\n\n    @Override\n    public CompleteItemType completeItemType()\n    {\n        return CompleteItemType.RELATION;\n    }\n\n    public CompleteRelation copy()\n    {\n        return new CompleteRelation(this.identifier, this.tags, this.bounds, this.members,\n                this.allRelationsWithSameOsmIdentifier, this.allKnownOsmMembers,\n                this.osmRelationIdentifier, this.relationIdentifiers, this.storedGeometry);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof CompleteRelation)\n        {\n            final CompleteRelation that = (CompleteRelation) other;\n            return CompleteEntity.basicEqual(this, that)\n                    && CompleteEntity.equalThroughGet(this.members(), that.members(),\n                            RelationMemberList::asBean)\n                    && Objects.equals(this.allRelationsWithSameOsmIdentifier(),\n                            that.allRelationsWithSameOsmIdentifier())\n                    && CompleteEntity.equalThroughGet(this.allKnownOsmMembers(),\n                            that.allKnownOsmMembers(), RelationMemberList::asBean)\n                    && Objects.equals(this.osmRelationIdentifier(), that.osmRelationIdentifier())\n                    && Objects.equals(this.asMultiPolygon(), that.asMultiPolygon())\n                    && Objects.equals(this.getAddedGeometry(), that.getAddedGeometry())\n                    && Objects.equals(this.getRemovedGeometry(), that.getRemovedGeometry());\n        }\n        return false;\n    }\n\n    @Override\n    public void fireTagChangeEvent(final TagChangeEvent tagChangeEvent)\n    {\n        this.tagChangeDelegate.fireTagChangeEvent(tagChangeEvent);\n    }\n\n    public Set<LineString> getAddedGeometry()\n    {\n        if (this.overrideGeometry)\n        {\n            return new HashSet<>();\n        }\n        return this.addedGeometry;\n    }\n\n    @Override\n    public Iterable<Location> getGeometry()\n    {\n        // TO DO enable for geometric?\n        throw new UnsupportedOperationException(\"Relations do not have an explicit geometry.\"\n                + \" Please instead use bounds to check the apparent geometry.\");\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public Set<LineString> getRemovedGeometry()\n    {\n        if (this.overrideGeometry)\n        {\n            return new HashSet<>();\n        }\n        return this.removedGeometry;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    @Override\n    public boolean isFull()\n    {\n        return this.bounds != null && this.tags != null && this.members != null\n                && this.allRelationsWithSameOsmIdentifier != null && this.allKnownOsmMembers != null\n                && this.osmRelationIdentifier != null && this.relationIdentifiers != null;\n    }\n\n    @Override\n    public boolean isGeometric()\n    {\n        return this.getTags() != null && super.isGeometric();\n    }\n\n    public boolean isOverrideGeometry()\n    {\n        return this.overrideGeometry;\n    }\n\n    @Override\n    public boolean isShallow()\n    {\n        return this.bounds == null && this.members == null\n                && this.allRelationsWithSameOsmIdentifier == null && this.allKnownOsmMembers == null\n                && this.osmRelationIdentifier == null && this.tags == null\n                && this.relationIdentifiers == null && this.storedGeometry == null;\n    }\n\n    @Override\n    public RelationMemberList members()\n    {\n        return membersFor(this.members);\n    }\n\n    @Override\n    public Long osmRelationIdentifier()\n    {\n        return this.osmRelationIdentifier;\n    }\n\n    @Override\n    public String prettify(final PrettifyStringFormat format, final boolean truncate)\n    {\n        String separator = \"\";\n        if (format == PrettifyStringFormat.MINIMAL_SINGLE_LINE)\n        {\n            separator = \"\";\n        }\n        else if (format == PrettifyStringFormat.MINIMAL_MULTI_LINE)\n        {\n            separator = \"\\n\";\n        }\n        final StringBuilder builder = new StringBuilder();\n\n        builder.append(this.getClass().getSimpleName() + \" \");\n        builder.append(\"[\");\n        builder.append(separator);\n        builder.append(\"identifier: \" + this.identifier + \", \");\n        builder.append(separator);\n        if (this.tags != null)\n        {\n            builder.append(\"tags: \" + new TreeMap<>(this.tags) + \", \");\n            builder.append(separator);\n        }\n        if (this.members != null && !this.members.isEmpty())\n        {\n            builder.append(\"members: \" + this.members + \", \");\n            builder.append(separator);\n        }\n        if (this.relationIdentifiers != null)\n        {\n            builder.append(\"parentRelations: \" + new TreeSet<>(this.relationIdentifiers) + \", \");\n            builder.append(separator);\n        }\n        if (this.bounds != null)\n        {\n            builder.append(\"bounds: \" + this.bounds.toWkt() + \", \");\n            builder.append(separator);\n        }\n        if (this.isGeometric())\n        {\n            builder.append(\"multiPolygonGeometry: \"\n                    + this.asMultiPolygon().map(Geometry::toText).orElse(\"null\"));\n        }\n        builder.append(separator);\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    @Override\n    public Set<Long> relationIdentifiers()\n    {\n        return this.relationIdentifiers;\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        /*\n         * Note that the Relations returned by this method will technically break the Located\n         * contract, since they have null bounds.\n         */\n        return this.relationIdentifiers == null ? null\n                : this.relationIdentifiers.stream().map(CompleteRelation::new)\n                        .collect(Collectors.toSet());\n    }\n\n    @Override\n    public void removeTagChangeListeners()\n    {\n        this.tagChangeDelegate.removeTagChangeListeners();\n    }\n\n    @Override\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags != null ? new HashMap<>(tags) : null;\n    }\n\n    @Override\n    public JsonObject toJson()\n    {\n        final JsonObject relationObject = super.toJson();\n\n        final JsonArray membersArray = new JsonArray();\n        for (final RelationBeanItem item : this.members)\n        {\n            final JsonObject memberObject = new JsonObject();\n            memberObject.addProperty(\"type\", item.getType().toString());\n            memberObject.addProperty(\"identifier\", item.getIdentifier());\n            memberObject.addProperty(\"role\", item.getRole());\n            membersArray.add(memberObject);\n        }\n        relationObject.add(\"members\", membersArray);\n        relationObject.addProperty(\"multiPolygonGeometry\",\n                this.asMultiPolygon().map(Geometry::toText).orElse(\"null\"));\n\n        return relationObject;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getSimpleName() + \" [identifier=\" + this.identifier + \", tags=\"\n                + this.tags + \", members=\" + this.members + \", relationIdentifiers=\"\n                + this.relationIdentifiers + \"]\";\n    }\n\n    @Override\n    public String toWkt()\n    {\n        if (this.isGeometric() && this.storedGeometry != null)\n        {\n            return this.storedGeometry.toText();\n        }\n        if (this.bounds == null)\n        {\n            return null;\n        }\n        return this.bounds.toWkt();\n    }\n\n    public void updateGeometry()\n    {\n        try\n        {\n            this.storedGeometry = new JtsMultiPolygonToMultiPolygonConverter()\n                    .backwardConvert(new RelationOrAreaToMultiPolygonConverter().convert(this));\n        }\n        catch (final Exception exc)\n        {\n            logger.error(\"Couldn't reconstruct geometry for relation {}\", this, exc);\n            this.storedGeometry = null;\n        }\n    }\n\n    public CompleteRelation withAddedMember(final AtlasEntity newMember,\n            final AtlasEntity memberFromWhichToCopyRole)\n    {\n        // TO DO support geometry\n        final Relation parentRelation = Iterables.stream(memberFromWhichToCopyRole.relations())\n                .firstMatching(relation -> relation.getIdentifier() == this.getIdentifier())\n                .orElseThrow(() -> new CoreException(\n                        \"Cannot copy role from {} {} as it does not have relation {} as parent\",\n                        memberFromWhichToCopyRole.getType(),\n                        memberFromWhichToCopyRole.getIdentifier(), this.getIdentifier()));\n        final String role = parentRelation.members().asBean()\n                .getItemFor(memberFromWhichToCopyRole.getIdentifier(),\n                        memberFromWhichToCopyRole.getType())\n                .orElseThrow(() -> new CoreException(\n                        \"Cannot copy role from {} {} as it is not a member of {} {}\",\n                        memberFromWhichToCopyRole.getType(),\n                        memberFromWhichToCopyRole.getIdentifier(), this.getClass().getSimpleName(),\n                        this))\n                .getRole();\n        return withAddedMember(newMember, role);\n    }\n\n    public CompleteRelation withAddedMember(final AtlasEntity newMember, final String role)\n    {\n        if (this.members == null)\n        {\n            final Collection<RelationMember> newMembers = new ArrayList<>();\n            newMembers.add(new RelationMember(role, newMember, this.getIdentifier()));\n            final RelationMemberList memberList = new RelationMemberList(newMembers);\n            return this.withMembers(memberList);\n        }\n        this.members.addItem(\n                new RelationBeanItem(newMember.getIdentifier(), role, newMember.getType()));\n        if (this.isGeometric() && this.asMultiPolygon().isPresent()\n                && (role.equalsIgnoreCase(Ring.INNER.toString())\n                        || role.equalsIgnoreCase(Ring.OUTER.toString())))\n        {\n            switch (newMember.getType())\n            {\n                case LINE:\n                    this.addedGeometry.add(\n                            new JtsPolyLineConverter().convert(((Line) newMember).asPolyLine()));\n                    break;\n                case AREA:\n                    this.addedGeometry.add(\n                            new JtsPolyLineConverter().convert(((Area) newMember).asPolygon()));\n                    break;\n                case EDGE:\n                    this.addedGeometry.add(\n                            new JtsPolyLineConverter().convert(((Edge) newMember).asPolyLine()));\n                    break;\n                case NODE:\n                case POINT:\n                case RELATION:\n                default:\n                    break;\n            }\n        }\n        this.withBoundsExtendedBy(newMember.bounds());\n        return this;\n    }\n\n    @Override\n    public CompleteRelation withAddedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers.add(relationIdentifier);\n        return this;\n    }\n\n    public CompleteRelation withAllKnownOsmMembers(final RelationBean allKnownOsmMembers)\n    {\n        this.allKnownOsmMembers = allKnownOsmMembers;\n        return this;\n    }\n\n    public CompleteRelation withAllRelationsWithSameOsmIdentifier(\n            final List<Long> allRelationsWithSameOsmIdentifier)\n    {\n        this.allRelationsWithSameOsmIdentifier = allRelationsWithSameOsmIdentifier;\n        return this;\n    }\n\n    public CompleteRelation withBounds(final Rectangle bounds)\n    {\n        this.bounds = bounds;\n        return this;\n    }\n\n    public CompleteRelation withBoundsExtendedBy(final Rectangle bounds)\n    {\n        if (this.bounds == null)\n        {\n            this.bounds = bounds;\n            return this;\n        }\n        this.bounds = Rectangle.forLocated(this.bounds, bounds);\n        return this;\n    }\n\n    @Override\n    public CompleteEntity withGeometry(final Iterable<Location> locations)\n    {\n        throw new UnsupportedOperationException(\"Relations cannot have an explicit geometry.\"\n                + \" Please instead use withBounds or withBoundsExtendedBy to adjust the bounds.\");\n    }\n\n    @Override\n    public CompleteRelation withIdentifier(final long identifier)\n    {\n        this.identifier = identifier;\n        return this;\n    }\n\n    /**\n     * Assign this {@link CompleteRelation} with members.\n     * <p>\n     * In case this {@link CompleteRelation} is created from an existing relation, and the new\n     * member list has had some existing members removed, use\n     * {@link #withMembersAndSource(RelationBean, Relation, Rectangle)}\n     *\n     * @param members\n     *            The members of the relation\n     * @param bounds\n     *            The bounds of all the members of the relation.\n     * @return This\n     */\n    public CompleteRelation withMembers(final RelationBean members, final Rectangle bounds)\n    {\n        this.members = members;\n        updateBounds(bounds);\n        return this;\n    }\n\n    /**\n     * Assign this {@link CompleteRelation} with members.\n     * <p>\n     * In case this {@link CompleteRelation} is created from an existing relation, and the new\n     * member list has had some existing members removed, use\n     * {@link #withMembersAndSource(RelationMemberList, Relation)}\n     *\n     * @param members\n     *            The full members of the Relation\n     * @return This\n     */\n    public CompleteRelation withMembers(final RelationMemberList members)\n    {\n        return withMembers(members.asBean(), members.bounds());\n    }\n\n    /**\n     * @param members\n     *            The members of the relation\n     * @param source\n     *            The relation that was used as a base to create that {@link CompleteRelation}, if\n     *            any. Due to the weak nature of relation membership across Atlas(es), this helps\n     *            decide what relation members are forcibly removed if any.\n     * @param bounds\n     *            The bounds of all the members of the relation.\n     * @return This.\n     */\n    public CompleteRelation withMembersAndSource(final RelationBean members, final Relation source,\n            final Rectangle bounds)\n    {\n        if (source == null)\n        {\n            throw new CoreException(\"Source relation must not be null.\");\n        }\n        this.members = members;\n\n        // This has been created from an existing relation, make sure to record the members that\n        // have been intentionally omitted, so as not to add them back in the future when either\n        // merging FeatureChanges or stitching MultiAtlases.\n        for (final RelationMember member : source.members())\n        {\n            if (!members.getItemFor(member.getEntity().getIdentifier(), member.getRole(),\n                    member.getEntity().getType()).isPresent())\n            {\n                this.members.addItemExplicitlyExcluded(member.getEntity().getIdentifier(),\n                        member.getRole(), member.getEntity().getType());\n            }\n        }\n        updateBounds(bounds);\n        return this;\n    }\n\n    /**\n     * Here the members have to be full so the new bounds can be computed from them.\n     *\n     * @param members\n     *            The full members of the Relation\n     * @param source\n     *            The relation that was used as a base to create that {@link CompleteRelation}, if\n     *            any. Due to the weak nature of relation membership across Atlas(es), this helps\n     *            decide what relation members are forcibly removed if any.\n     * @return This.\n     */\n    public CompleteRelation withMembersAndSource(final RelationMemberList members,\n            final Relation source)\n    {\n        if (source instanceof CompleteRelation)\n        {\n            throw new CoreException(\n                    \"This version of withMembersAndSource must use a source Relation that is tied to an atlas, instead found Relation of type {}\",\n                    source.getClass().getName());\n        }\n        return withMembersAndSource(members.asBean(), source, members.bounds());\n    }\n\n    public CompleteRelation withMultiPolygonGeometry(final MultiPolygon geometry)\n    {\n        this.storedGeometry = geometry;\n        this.overrideGeometry = true;\n        if (geometry != null && geometry.getEnvelope() instanceof Polygon)\n        {\n            this.bounds = Rectangle.forLocated(\n                    new JtsPolygonConverter().backwardConvert((Polygon) geometry.getEnvelope()));\n        }\n        return this;\n    }\n\n    public CompleteRelation withOsmRelationIdentifier(final Long osmRelationIdentifier)\n    {\n        this.osmRelationIdentifier = osmRelationIdentifier;\n        return this;\n    }\n\n    @Override\n    public CompleteRelation withRelationIdentifiers(final Set<Long> relationIdentifiers)\n    {\n        this.relationIdentifiers = relationIdentifiers;\n        return this;\n    }\n\n    @Override\n    public CompleteRelation withRelations(final Set<Relation> relations)\n    {\n        this.relationIdentifiers = relations.stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet());\n        return this;\n    }\n\n    public CompleteRelation withRemovedMember(final AtlasEntity memberToRemove)\n    {\n        final List<String> roles = this.members\n                .removeAllMatchingItems(memberToRemove.getIdentifier(), memberToRemove.getType());\n        roles.forEach(role -> this.members.addItemExplicitlyExcluded(memberToRemove.getIdentifier(),\n                role, memberToRemove.getType()));\n        if (this.isGeometric() && this.asMultiPolygon().isPresent()\n                && (roles.contains(Ring.INNER.toString()) || roles.contains(Ring.OUTER.toString())\n                        || roles.contains(Ring.INNER.toString().toLowerCase())\n                        || roles.contains(Ring.OUTER.toString().toLowerCase())))\n        {\n            switch (memberToRemove.getType())\n            {\n                case LINE:\n                    this.removedGeometry.add(new JtsPolyLineConverter()\n                            .convert(((Line) memberToRemove).asPolyLine()));\n                    break;\n                case AREA:\n                    this.removedGeometry.add(new JtsPolyLineConverter()\n                            .convert(((Area) memberToRemove).asPolygon()));\n                    break;\n                case EDGE:\n                    this.removedGeometry.add(new JtsPolyLineConverter()\n                            .convert(((Edge) memberToRemove).asPolyLine()));\n                    break;\n                case NODE:\n                case POINT:\n                case RELATION:\n                default:\n                    break;\n            }\n        }\n        return this;\n    }\n\n    public CompleteRelation withRemovedMember(final AtlasEntity memberToRemove, final String role)\n    {\n        final boolean success = this.members.removeItem(memberToRemove.getIdentifier(), role,\n                memberToRemove.getType());\n        if (success)\n        {\n            this.members.addItemExplicitlyExcluded(memberToRemove.getIdentifier(), role,\n                    memberToRemove.getType());\n            if (this.isGeometric() && this.asMultiPolygon().isPresent()\n                    && (role.equalsIgnoreCase(Ring.INNER.toString())\n                            || role.equalsIgnoreCase(Ring.OUTER.toString())))\n            {\n                switch (memberToRemove.getType())\n                {\n                    case LINE:\n                        this.removedGeometry.add(new JtsPolyLineConverter()\n                                .convert(((Line) memberToRemove).asPolyLine()));\n                        break;\n                    case AREA:\n                        this.removedGeometry.add(new JtsPolyLineConverter()\n                                .convert(((Area) memberToRemove).asPolygon()));\n                        break;\n                    case EDGE:\n                        this.removedGeometry.add(new JtsPolyLineConverter()\n                                .convert(((Edge) memberToRemove).asPolyLine()));\n                        break;\n                    case NODE:\n                    case POINT:\n                    case RELATION:\n                    default:\n                        break;\n                }\n            }\n        }\n        return this;\n    }\n\n    @Override\n    public CompleteRelation withRemovedRelationIdentifier(final Long relationIdentifier)\n    {\n        this.relationIdentifiers = this.relationIdentifiers.stream()\n                .filter(keepId -> keepId != relationIdentifier.longValue())\n                .collect(Collectors.toSet());\n        return this;\n    }\n\n    private RelationMemberList membersFor(final RelationBean bean)\n    {\n        if (bean == null)\n        {\n            return null;\n        }\n        final List<RelationMember> memberList = new ArrayList<>();\n        for (final RelationBeanItem item : bean)\n        {\n            memberList.add(new RelationMember(item.getRole(),\n                    getAtlas().entity(item.getIdentifier(), item.getType()), getIdentifier()));\n        }\n        final RelationMemberList result = new RelationMemberList(memberList);\n        bean.getExplicitlyExcluded().forEach(result::addItemExplicitlyExcluded);\n        return result;\n    }\n\n    private void updateBounds(final Rectangle bounds)\n    {\n        this.bounds = bounds;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/EmptyAtlas.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.function.BiConsumer;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.SnappedEdge;\nimport org.openstreetmap.atlas.geography.atlas.items.SnappedLineItem;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Simple Atlas that supports single temporary entities. It does not do anything by design, as all\n * the {@link CompleteEntity} are self-contained. They just need an Atlas to refer to, so they\n * comply with the Edge, Node, Area etc. definitions.\n *\n * @author matthieun\n */\npublic class EmptyAtlas implements Atlas\n{\n    private static final long serialVersionUID = 5265300513234306056L;\n\n    @Override\n    public Area area(final long identifier)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Area> areas()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Area> areas(final Predicate<Area> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Area> areasCovering(final Location location)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Area> areasCovering(final Location location, final Predicate<Area> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Area> areasIntersecting(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Area> areasIntersecting(final GeometricSurface surface,\n            final Predicate<Area> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Area> areasWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public JsonObject asGeoJson()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public JsonObject asGeoJson(final Predicate<AtlasEntity> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Edge edge(final long identifier)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Edge> edges()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Edge> edges(final Predicate<Edge> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Edge> edgesContaining(final Location location)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Edge> edgesContaining(final Location location, final Predicate<Edge> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Edge> edgesIntersecting(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Edge> edgesIntersecting(final GeometricSurface surface,\n            final Predicate<Edge> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Edge> edgesWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entities()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public <M extends AtlasEntity> Iterable<M> entities(final ItemType type,\n            final Class<M> memberClass)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entities(final Predicate<AtlasEntity> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entitiesIntersecting(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entitiesIntersecting(final GeometricSurface surface,\n            final Predicate<AtlasEntity> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entitiesWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasEntity> entitiesWithin(final GeometricSurface surface,\n            final Predicate<AtlasEntity> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * Note that the {@link AtlasEntity}s returned by this method will technically break the\n     * {@link Located} contract, since they have null bounds.\n     *\n     * @param identifier\n     *            the entity identifier\n     * @param type\n     *            the entity type\n     * @return the matching {@link AtlasEntity}\n     */\n    @Override\n    public AtlasEntity entity(final long identifier, final ItemType type)\n    {\n        switch (type)\n        {\n            case NODE:\n                return new CompleteNode(identifier);\n            case EDGE:\n                return new CompleteEdge(identifier);\n            case AREA:\n                return new CompleteArea(identifier);\n            case LINE:\n                return new CompleteLine(identifier);\n            case POINT:\n                return new CompletePoint(identifier);\n            case RELATION:\n                return new CompleteRelation(identifier);\n            default:\n                throw new CoreException(\"Unknown type {}\", type);\n        }\n    }\n\n    @Override\n    public Iterable<AtlasEntity> getGeoJsonObjects()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public UUID getIdentifier()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"EmptyAtlas\";\n    }\n\n    @Override\n    public Iterable<AtlasItem> items()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasItem> items(final Predicate<AtlasItem> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsContaining(final Location location)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsContaining(final Location location,\n            final Predicate<AtlasItem> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsIntersecting(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsIntersecting(final GeometricSurface surface,\n            final Predicate<AtlasItem> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<AtlasItem> itemsWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterator<AtlasEntity> iterator()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Line line(final long identifier)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LineItem> lineItems()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LineItem> lineItems(final Predicate<LineItem> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsContaining(final Location location)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsContaining(final Location location,\n            final Predicate<LineItem> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsIntersecting(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsIntersecting(final GeometricSurface surface,\n            final Predicate<LineItem> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LineItem> lineItemsWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Line> lines()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Line> lines(final Predicate<Line> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Line> linesContaining(final Location location)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Line> linesContaining(final Location location, final Predicate<Line> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Line> linesIntersecting(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Line> linesIntersecting(final GeometricSurface surface,\n            final Predicate<Line> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Line> linesWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LocationItem> locationItems()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LocationItem> locationItems(final Predicate<LocationItem> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LocationItem> locationItemsWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<LocationItem> locationItemsWithin(final GeometricSurface surface,\n            final Predicate<LocationItem> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public AtlasMetaData metaData()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Node node(final long identifier)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Node> nodes()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Node> nodes(final Predicate<Node> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Node> nodesAt(final Location location)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Node> nodesWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Node> nodesWithin(final GeometricSurface surface, final Predicate<Node> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public long numberOfAreas()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public long numberOfEdges()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public long numberOfLines()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public long numberOfNodes()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public long numberOfPoints()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public long numberOfRelations()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Point point(final long identifier)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Point> points()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Point> points(final Predicate<Point> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Point> pointsAt(final Location location)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Point> pointsWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Point> pointsWithin(final GeometricSurface surface,\n            final Predicate<Point> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Relation relation(final long identifier)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Relation> relations()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Relation> relations(final Predicate<Relation> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Relation> relationsLowerOrderFirst()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesIntersecting(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesIntersecting(final GeometricSurface surface,\n            final Predicate<Relation> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesWithin(final GeometricSurface surface)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void save(final WritableResource writableResource)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void saveAsGeoJson(final WritableResource resource)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void saveAsGeoJson(final WritableResource resource, final Predicate<AtlasEntity> matcher)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void saveAsLineDelimitedGeoJsonFeatures(final WritableResource resource,\n            final BiConsumer<AtlasEntity, JsonObject> jsonMutator)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void saveAsLineDelimitedGeoJsonFeatures(final WritableResource resource,\n            final Predicate<AtlasEntity> matcher,\n            final BiConsumer<AtlasEntity, JsonObject> jsonMutator)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void saveAsList(final WritableResource resource)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void saveAsProto(final WritableResource resource)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void saveAsText(final WritableResource resource)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public SnappedEdge snapped(final Location point, final Distance threshold)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public List<SnappedEdge> snaps(final Location point, final Distance threshold)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public List<SnappedLineItem> snapsLineItem(final Location point, final Distance threshold)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Optional<Atlas> subAtlas(final GeometricSurface boundary, final AtlasCutType cutType)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Optional<Atlas> subAtlas(final Predicate<AtlasEntity> matcher,\n            final AtlasCutType cutType)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public String summary()\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public String toStringDetailed()\n    {\n        throw new UnsupportedOperationException();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/PrettifyStringFormat.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\n/**\n * @author lcram\n */\npublic enum PrettifyStringFormat\n{\n    MINIMAL_SINGLE_LINE,\n    MINIMAL_MULTI_LINE;\n\n    public static final int TRUNCATE_LENGTH = 2000;\n    public static final String TRUNCATE_ELLIPSES = \"...\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/complete/TagChangeDelegate.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang3.Validate;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listenable.TagChangeListenable;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;\n\n/**\n * Consolidates redundant state and behavior that would have otherwise be repeated across all\n * {@link CompleteEntity} implementations.\n *\n * @author Yazad Khambata\n */\nclass TagChangeDelegate implements TagChangeListenable, Serializable\n{\n    private static final long serialVersionUID = -7015756232511317683L;\n\n    private final List<TagChangeListener> tagChangeListeners = new ArrayList<>();\n\n    static TagChangeDelegate newTagChangeDelegate()\n    {\n        return new TagChangeDelegate();\n    }\n\n    protected TagChangeDelegate()\n    {\n        super();\n    }\n\n    @Override\n    public void addTagChangeListener(final TagChangeListener tagChangeListener)\n    {\n        Validate.notNull(tagChangeListener, \"tagChangeListener is NULL.\");\n        synchronized (this.tagChangeListeners)\n        {\n            this.tagChangeListeners.add(tagChangeListener);\n        }\n    }\n\n    @Override\n    public void fireTagChangeEvent(final TagChangeEvent tagChangeEvent)\n    {\n        Validate.notNull(tagChangeEvent, \"tagChangeEvent is EMPTY!\");\n        if (!this.tagChangeListeners.isEmpty())\n        {\n            synchronized (this.tagChangeListeners)\n            {\n                this.tagChangeListeners.stream().forEach(\n                        tagChangeListener -> tagChangeListener.entityChanged(tagChangeEvent));\n            }\n        }\n    }\n\n    @Override\n    public void removeTagChangeListeners()\n    {\n        synchronized (this.tagChangeListeners)\n        {\n            this.tagChangeListeners.removeIf(tagChangeListener -> true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/converters/AtlasDebugTool.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.atlas.routing.AStarRouter;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.boundary.converters.CountryListTwoWayStringConverter;\nimport org.openstreetmap.atlas.geography.converters.MultiPolygonStringConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Convert an Atlas to GeoJson\n *\n * @author matthieun\n */\npublic class AtlasDebugTool extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDebugTool.class);\n\n    private static final Switch<File> PBF = new Switch<>(\"pbf\", \"The protobuf file\",\n            path -> new File(path), Optionality.OPTIONAL);\n    private static final Switch<File> ATLAS = new Switch<>(\"atlas\", \"The atlas file\",\n            path -> new File(path), Optionality.REQUIRED);\n    private static final Switch<File> GEOJSON = new Switch<>(\"geojson\", \"The geojson file\",\n            path -> new File(path), Optionality.OPTIONAL);\n    private static final Switch<File> TEXT = new Switch<>(\"text\", \"The text file\",\n            path -> new File(path), Optionality.OPTIONAL);\n    private static final Switch<java.io.File> BOUNDARY = new Switch<>(\"boundary\",\n            \"The country boundary file\", path -> new java.io.File(path), Optionality.OPTIONAL);\n    private static final Switch<String> COUNTRY = new Switch<>(\"country\",\n            \"The country name which will be loaded\", name -> name, Optionality.OPTIONAL);\n    private static final Switch<List<Location>> ROUTE = new Switch<>(\"route\",\n            \"The lat,lon:lat,lon representing a start and end points to get a route\", value ->\n            {\n                final StringList split = StringList.split(value, \":\");\n                final List<Location> result = new ArrayList<>();\n                result.add(Location.forString(split.get(0)));\n                result.add(Location.forString(split.get(1)));\n                return result;\n            }, Optionality.OPTIONAL);\n    private static final Switch<Rectangle> BOUND = new Switch<>(\"bound\",\n            \"Data will be loaded only in this bounding box\", value -> Rectangle.forString(value));\n    private static final Switch<MultiPolygon> MULTIPOLYGON = new Switch<>(\"multipolygon\",\n            \"Data will be loaded only in this multipolygon\",\n            value -> new MultiPolygonStringConverter().convert(value));\n\n    public static void main(final String[] args)\n    {\n        new AtlasDebugTool().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final File pbf = (File) command.get(PBF);\n        final File atlasFile = (File) command.get(ATLAS);\n        final File geojson = (File) command.get(GEOJSON);\n        final File text = (File) command.get(TEXT);\n        final java.io.File boundaryFile = (java.io.File) command.get(BOUNDARY);\n        final String country = (String) command.get(COUNTRY);\n        final Rectangle bound = (Rectangle) command.get(BOUND);\n        final MultiPolygon inputMultipolygon = (MultiPolygon) command.get(MULTIPOLYGON);\n        @SuppressWarnings(\"unchecked\")\n        final List<Location> startEndRoute = (List<Location>) command.get(ROUTE);\n\n        Atlas atlas;\n        if (pbf != null && pbf.exists())\n        {\n            final AtlasLoadingOption option;\n            MultiPolygon multiPolygon = MultiPolygon.forPolygon(Rectangle.MAXIMUM);\n            if (boundaryFile != null)\n            {\n                final CountryBoundaryMap boundaryMap = CountryBoundaryMap\n                        .fromShapeFile(boundaryFile);\n                option = AtlasLoadingOption.createOptionWithAllEnabled(boundaryMap);\n                if (country != null)\n                {\n                    if (new CountryListTwoWayStringConverter().convert(country).size() == 1)\n                    {\n                        multiPolygon = new JtsPolygonToMultiPolygonConverter()\n                                .convert(boundaryMap.countryBoundary(country).get(0));\n                    }\n                    option.setCountryCode(country);\n                }\n            }\n            else\n            {\n                option = AtlasLoadingOption.createOptionWithNoSlicing();\n            }\n            if (bound != null)\n            {\n                multiPolygon = MultiPolygon.forPolygon(bound);\n            }\n            if (inputMultipolygon != null)\n            {\n                multiPolygon = inputMultipolygon;\n            }\n\n            atlas = new RawAtlasGenerator(pbf, option, multiPolygon).build();\n            if (option.isCountrySlicing())\n            {\n                atlas = new RawAtlasSlicer(option, atlas).slice();\n            }\n            atlas = new AtlasSectionProcessor(atlas, option).run();\n            atlas.save(atlasFile);\n        }\n        else if (atlasFile != null && atlasFile.exists())\n        {\n            atlas = new AtlasResourceLoader().load(atlasFile);\n        }\n        else\n        {\n            logger.error(\"Must have at least one source, -pbf or -atlas\");\n            atlas = null;\n            System.exit(1);\n        }\n        logger.info(\"Loaded {}\", atlas.summary());\n        if (geojson != null)\n        {\n            atlas.saveAsGeoJson(geojson);\n        }\n        if (text != null)\n        {\n            atlas.saveAsText(text);\n        }\n        if (startEndRoute != null)\n        {\n            logger.info(\"Route between {} and {} = {}\", startEndRoute.get(0), startEndRoute.get(1),\n                    AStarRouter.dijkstra(atlas, Distance.TEN_MILES).route(startEndRoute.get(0),\n                            startEndRoute.get(1)));\n        }\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(ATLAS, GEOJSON, TEXT, PBF, BOUNDARY, COUNTRY, ROUTE, BOUND,\n                MULTIPOLYGON);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDelta.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.delta.Diff.DiffReason;\nimport org.openstreetmap.atlas.geography.atlas.delta.Diff.DiffType;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.matching.PolyLineRoute;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CounterWithStatistic;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Difference between two {@link Atlas}.\n *\n * @author matthieun\n */\npublic class AtlasDelta implements Serializable\n{\n    private static final long serialVersionUID = 1189641317938152158L;\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDelta.class);\n    private static final int COUNTER_REPORT = 100_000;\n\n    private final Atlas before;\n    private final Atlas after;\n    private final SortedSet<Diff> differences;\n    private final boolean withGeometryMatching;\n    private final transient CounterWithStatistic counter;\n\n    public AtlasDelta(final Atlas before, final Atlas after)\n    {\n        this(before, after, false);\n    }\n\n    public AtlasDelta(final Atlas before, final Atlas after, final boolean withGeometryMatching)\n    {\n        this.before = before;\n        this.after = after;\n        this.differences = new TreeSet<>();\n        this.counter = new CounterWithStatistic(logger, COUNTER_REPORT, \"Processed\");\n        this.withGeometryMatching = withGeometryMatching;\n    }\n\n    public AtlasDelta generate()\n    {\n        // Check for removed identifiers\n        logger.info(\"Looking for removed items.\");\n        for (final AtlasEntity entity : this.before)\n        {\n            this.counter.increment();\n            if (entity.getType().entityForIdentifier(this.after, entity.getIdentifier()) == null\n                    && (!(entity instanceof Edge) || !hasGoodMatch((Edge) entity, this.after)))\n            {\n                this.differences.add(new Diff(entity.getType(), DiffType.REMOVED,\n                        DiffReason.REMOVED, this.before, this.after, entity.getIdentifier()));\n            }\n        }\n        // Check for added identifiers\n        logger.info(\"Looking for added items.\");\n        for (final AtlasEntity entity : this.after)\n        {\n            this.counter.increment();\n            if (entity.getType().entityForIdentifier(this.before, entity.getIdentifier()) == null\n                    && (!(entity instanceof Edge) || !hasGoodMatch((Edge) entity, this.before)))\n            {\n                this.differences.add(new Diff(entity.getType(), DiffType.ADDED, DiffReason.ADDED,\n                        this.before, this.after, entity.getIdentifier()));\n            }\n        }\n        logger.info(\"Looking for changed items.\");\n        for (final AtlasEntity baseEntity : this.before)\n        {\n            this.counter.increment();\n            final long identifier = baseEntity.getIdentifier();\n            final AtlasEntity alterEntity = baseEntity.getType().entityForIdentifier(this.after,\n                    baseEntity.getIdentifier());\n            // Look only at entities that are in both Atlas.\n            if (alterEntity != null)\n            {\n                // Entity Tags & Entity's Relations first\n                if (!baseEntity.getTags().equals(alterEntity.getTags()))\n                {\n                    this.differences.add(new Diff(baseEntity.getType(), DiffType.CHANGED,\n                            DiffReason.TAGS, this.before, this.after, identifier));\n                }\n                else if (differentInRelation(baseEntity, alterEntity))\n                {\n                    this.differences.add(new Diff(baseEntity.getType(), DiffType.CHANGED,\n                            DiffReason.RELATION_MEMBER, this.before, this.after, identifier));\n                }\n                else if (baseEntity instanceof Node)\n                {\n                    if (differentNodes((Node) baseEntity, (Node) alterEntity))\n                    {\n                        this.differences.add(new Diff(ItemType.NODE, DiffType.CHANGED,\n                                DiffReason.GEOMETRY_OR_TOPOLOGY, this.before, this.after,\n                                identifier));\n                    }\n                }\n                else if (baseEntity instanceof Edge)\n                {\n                    if (differentEdges((Edge) baseEntity, (Edge) alterEntity))\n                    {\n                        this.differences.add(new Diff(ItemType.EDGE, DiffType.CHANGED,\n                                DiffReason.GEOMETRY_OR_TOPOLOGY, this.before, this.after,\n                                identifier));\n                    }\n                }\n                else if (baseEntity instanceof Area)\n                {\n                    if (differentAreas((Area) baseEntity, (Area) alterEntity))\n                    {\n                        this.differences.add(new Diff(ItemType.AREA, DiffType.CHANGED,\n                                DiffReason.GEOMETRY_OR_TOPOLOGY, this.before, this.after,\n                                identifier));\n                    }\n                }\n                else if (baseEntity instanceof Line)\n                {\n                    if (differentLines((Line) baseEntity, (Line) alterEntity))\n                    {\n                        this.differences.add(new Diff(ItemType.LINE, DiffType.CHANGED,\n                                DiffReason.GEOMETRY_OR_TOPOLOGY, this.before, this.after,\n                                identifier));\n                    }\n                }\n                else if (baseEntity instanceof Point)\n                {\n                    if (differentPoints((Point) baseEntity, (Point) alterEntity))\n                    {\n                        this.differences.add(new Diff(ItemType.POINT, DiffType.CHANGED,\n                                DiffReason.GEOMETRY_OR_TOPOLOGY, this.before, this.after,\n                                identifier));\n                    }\n                }\n                else if (baseEntity instanceof Relation)\n                {\n                    if (differentRelations((Relation) baseEntity, (Relation) alterEntity))\n                    {\n                        this.differences.add(new Diff(ItemType.RELATION, DiffType.CHANGED,\n                                DiffReason.RELATION_TOPOLOGY, this.before, this.after, identifier));\n                    }\n                }\n            }\n        }\n        this.counter.summary();\n        return this;\n    }\n\n    public Atlas getAfter()\n    {\n        return this.after;\n    }\n\n    public Atlas getBefore()\n    {\n        return this.before;\n    }\n\n    public SortedSet<Diff> getDifferences()\n    {\n        return this.differences;\n    }\n\n    /**\n     * Similar to the regular {@link AtlasDelta#toString}, but attempts to make the string more\n     * friendly to diff viewing.\n     *\n     * @return the diff string\n     */\n    public String toDiffViewFriendlyString()\n    {\n        return Diff.toDiffViewFriendlyString(this.differences);\n    }\n\n    public String toGeoJson()\n    {\n        return Diff.toGeoJson(this.differences);\n    }\n\n    public String toGeoJson(final Predicate<Diff> filter)\n    {\n        return Diff.toGeoJson(this.differences, filter);\n    }\n\n    public String toRelationsGeoJson()\n    {\n        return Diff.toRelationsGeoJson(this.differences);\n    }\n\n    public String toRelationsGeoJson(final Predicate<Diff> filter)\n    {\n        return Diff.toRelationsGeoJson(this.differences, filter);\n    }\n\n    @Override\n    public String toString()\n    {\n        return Diff.toString(this.differences);\n    }\n\n    private boolean differentAreas(final Area baseArea, final Area alterArea)\n    {\n        try\n        {\n            if (!baseArea.asPolygon().equals(alterArea.asPolygon()))\n            {\n                return true;\n            }\n            return false;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to compare areas {} and {}\", baseArea, alterArea, e);\n        }\n    }\n\n    private boolean differentEdgeSet(final SortedSet<Edge> baseEdges,\n            final SortedSet<Edge> alterEdges)\n    {\n        final boolean differentEdgeSetBasic = differentEdgeSetBasic(baseEdges, alterEdges);\n        final boolean differentEdgeSetWithMatch = differentEdgeSetWithMatch(baseEdges, alterEdges);\n        return differentEdgeSetBasic && differentEdgeSetWithMatch;\n    }\n\n    private boolean differentEdgeSetBasic(final SortedSet<Edge> baseEdges,\n            final SortedSet<Edge> alterEdges)\n    {\n        if (baseEdges.size() != alterEdges.size())\n        {\n            return true;\n        }\n        final Iterator<Edge> baseInEdgeIterator = baseEdges.iterator();\n        final Iterator<Edge> alterInEdgeIterator = alterEdges.iterator();\n        for (int i = 0; i < baseEdges.size(); i++)\n        {\n            final Edge baseInEdge = baseInEdgeIterator.next();\n            final Edge alterInEdge = alterInEdgeIterator.next();\n            if (baseInEdge.getIdentifier() != alterInEdge.getIdentifier())\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private boolean differentEdgeSetWithMatch(final Set<Edge> baseEdges, final Set<Edge> alterEdges)\n    {\n        if (baseEdges.isEmpty() && alterEdges.isEmpty())\n        {\n            return false;\n        }\n        boolean baseToAlterResult = baseEdges.isEmpty();\n        for (final Edge edge : baseEdges)\n        {\n            if (alterEdges.isEmpty() || !hasPerfectMatch(edge, alterEdges))\n            {\n                baseToAlterResult = true;\n                break;\n            }\n        }\n        boolean alterToBaseResult = alterEdges.isEmpty();\n        for (final Edge edge : alterEdges)\n        {\n            if (baseEdges.isEmpty() || !hasPerfectMatch(edge, baseEdges))\n            {\n                alterToBaseResult = true;\n                break;\n            }\n        }\n        return baseToAlterResult && alterToBaseResult;\n    }\n\n    private boolean differentEdges(final Edge baseEdge, final Edge alterEdge)\n    {\n        try\n        {\n            boolean result = false;\n            if (!baseEdge.asPolyLine().equals(alterEdge.asPolyLine()))\n            {\n                result = true;\n            }\n            if (!result && baseEdge.start().getIdentifier() != alterEdge.start().getIdentifier())\n            {\n                result = true;\n            }\n            if (!result && baseEdge.end().getIdentifier() != alterEdge.end().getIdentifier())\n            {\n                result = true;\n            }\n            if (result)\n            {\n                // Make sure that there is not way to find a match with the other polylines\n                result = !hasGoodMatch(baseEdge, alterEdge.getAtlas());\n            }\n            return result;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to compare edges {} and {}\", baseEdge, alterEdge, e);\n        }\n    }\n\n    private boolean differentInRelation(final AtlasEntity baseEntity, final AtlasEntity alterEntity)\n    {\n        try\n        {\n            final Set<Relation> baseRelations = baseEntity.relations();\n            final Set<Relation> alterRelations = alterEntity.relations();\n            if (baseRelations.size() != alterRelations.size())\n            {\n                return true;\n            }\n            for (final Relation baseRelation : baseRelations)\n            {\n                Relation alterRelation = null;\n                for (final Relation alterRelationCandidate : alterRelations)\n                {\n                    if (alterRelationCandidate.getIdentifier() == baseRelation.getIdentifier())\n                    {\n                        alterRelation = alterRelationCandidate;\n                        break;\n                    }\n                }\n                if (alterRelation == null)\n                {\n                    // The two relation sets are different\n                    return true;\n                }\n\n                // Index of the member in the Relation's member list\n                int baseIndex = -1;\n                int alterIndex = -1;\n                final RelationMemberList baseMembers = baseRelation.members();\n                final RelationMemberList alterMembers = alterRelation.members();\n                for (int j = 0; j < baseMembers.size(); j++)\n                {\n                    final RelationMember baseMember = baseMembers.get(j);\n                    if (baseMember.getEntity().getIdentifier() == baseEntity.getIdentifier())\n                    {\n                        baseIndex = j;\n                    }\n                }\n                for (int j = 0; j < alterMembers.size(); j++)\n                {\n                    final RelationMember alterMember = alterMembers.get(j);\n                    if (alterMember.getEntity().getIdentifier() == baseEntity.getIdentifier())\n                    {\n                        alterIndex = j;\n                    }\n                }\n                if (baseIndex < 0 || alterIndex < 0)\n                {\n                    throw new CoreException(\"Corrupted Atlas dataset.\");\n                }\n                if (baseIndex != alterIndex)\n                {\n                    // Order changed\n                    return true;\n                }\n                if (!baseMembers.get(baseIndex).getRole()\n                        .equals(alterMembers.get(alterIndex).getRole()))\n                {\n                    // Role changed\n                    return true;\n                }\n                if (baseMembers.get(baseIndex).getEntity().getType() != alterMembers.get(alterIndex)\n                        .getEntity().getType())\n                {\n                    // Type changed\n                    return true;\n                }\n            }\n            return false;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to compare relations for {} and {}\", baseEntity,\n                    alterEntity, e);\n        }\n    }\n\n    private boolean differentLines(final Line baseLine, final Line alterLine)\n    {\n        try\n        {\n            if (!baseLine.asPolyLine().equals(alterLine.asPolyLine()))\n            {\n                return true;\n            }\n            return false;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to compare line geometries for {} and {}\", baseLine,\n                    alterLine, e);\n        }\n    }\n\n    private boolean differentNodes(final Node baseNode, final Node alterNode)\n    {\n        try\n        {\n            if (!baseNode.getLocation().equals(alterNode.getLocation()))\n            {\n                return true;\n            }\n            if (differentEdgeSet(baseNode.inEdges(), alterNode.inEdges()))\n            {\n                return true;\n            }\n            if (differentEdgeSet(baseNode.outEdges(), alterNode.outEdges()))\n            {\n                return true;\n            }\n            return false;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to compare nodes {} and {}\", baseNode, alterNode, e);\n        }\n    }\n\n    private boolean differentPoints(final Point basePoint, final Point alterPoint)\n    {\n        try\n        {\n            if (!basePoint.getLocation().equals(alterPoint.getLocation()))\n            {\n                return true;\n            }\n            return false;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to compare points {} and {}\", basePoint, alterPoint, e);\n        }\n    }\n\n    private boolean differentRelationMemberListsWithMatch(final RelationMemberList baseMembers,\n            final RelationMemberList alterMembers)\n    {\n        final SortedSet<Edge> baseEdges = Iterables.stream(baseMembers)\n                .map(member -> member.getEntity()).filter(entity -> entity instanceof Edge)\n                .map(entity -> (Edge) entity).collectToSortedSet();\n        final SortedSet<Edge> alterEdges = Iterables.stream(alterMembers)\n                .map(member -> member.getEntity()).filter(entity -> entity instanceof Edge)\n                .map(entity -> (Edge) entity).collectToSortedSet();\n        return differentEdgeSet(baseEdges, alterEdges);\n    }\n\n    private boolean differentRelations(final Relation baseRelation, final Relation alterRelation)\n    {\n        try\n        {\n            final RelationMemberList baseMembers = baseRelation.members();\n            final RelationMemberList alterMembers = alterRelation.members();\n            return !baseMembers.equals(alterMembers)\n                    && !differentRelationMemberListsWithMatch(baseMembers, alterMembers);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to compare relations {} and {}\", baseRelation,\n                    alterRelation, e);\n        }\n    }\n\n    private boolean hasGoodMatch(final Edge edge, final Atlas other)\n    {\n        if (this.withGeometryMatching)\n        {\n            final Rectangle bounds = edge.bounds();\n            return hasPerfectMatch(edge, other.edgesIntersecting(bounds,\n                    otherEdge -> edge.getOsmIdentifier() == otherEdge.getOsmIdentifier()));\n        }\n        return false;\n    }\n\n    private boolean hasPerfectMatch(final Edge edge, final Iterable<Edge> otherEdges)\n    {\n        if (this.withGeometryMatching)\n        {\n            final PolyLine source = edge.asPolyLine();\n            final List<PolyLine> candidates = Iterables.stream(otherEdges).map(Edge::asPolyLine)\n                    .collectToList();\n            final Optional<PolyLineRoute> match = source.costDistanceToOneWay(candidates)\n                    .match(Distance.ZERO);\n            if (match.isPresent() && match.get().getCost().isLessThanOrEqualTo(Distance.ZERO))\n            {\n                // The edge was probably split by way sectioning without changing itself.\n                logger.trace(\"Edge {} from {} has no equal member but found a match with no cost.\",\n                        edge, edge.getAtlas().getName());\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaGenerator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport static org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader.HAS_ATLAS_EXTENSION;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This CLI will allow you to point to two atlases and generate a diff of the two. You can point to\n * single atlas files or directories of atlas shards. If you are diffing shards for both your input\n * and your output, we will process diffs for each shard individually in parallel.\n *\n * @author matthieun\n * @author hallahan\n */\npublic class AtlasDeltaGenerator extends Command\n{\n    private static final int DEFAULT_THREADS = 8;\n    private static final int COMMAND_LINE_USAGE_ERROR_EXIT = 64;\n\n    private static final Switch<Path> BEFORE_SWITCH = new Switch<>(\"before\",\n            \"The before atlas directory or file from which to delta.\", Paths::get,\n            Optionality.REQUIRED);\n\n    private static final Switch<Path> AFTER_SWITCH = new Switch<>(\"after\",\n            \"The after atlas directory or file that the before atlas deltas to.\", Paths::get,\n            Optionality.REQUIRED);\n\n    private static final Switch<Path> OUTPUT_DIRECTORY_SWITCH = new Switch<>(\"outputDirectory\",\n            \"The path of the output directory.\", Paths::get, Optionality.REQUIRED);\n\n    private static final Switch<Integer> THREADS_SWITCH = new Switch<>(\"threads\",\n            \"The number of threads to work on processing atlas shards.\", Integer::valueOf,\n            Optionality.OPTIONAL, String.valueOf(DEFAULT_THREADS));\n    private static final AtlasResourceLoader ATLAS_RESOURCE_LOADER = new AtlasResourceLoader();\n    private final Logger logger;\n    /**\n     * The size of the thread pool for shard-by-shard parallel processing.\n     */\n    private int threads = DEFAULT_THREADS;\n\n    public static void main(final String[] args)\n    {\n        new AtlasDeltaGenerator(LoggerFactory.getLogger(AtlasDeltaGenerator.class)).run(args);\n    }\n\n    private static List<File> fetchAtlasFilesInDirectory(final Path directory)\n    {\n        return new File(directory.toFile()).listFilesRecursively().stream()\n                .filter(HAS_ATLAS_EXTENSION).collect(Collectors.toList());\n    }\n\n    public AtlasDeltaGenerator(final Logger logger)\n    {\n        this.logger = logger;\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Path before = (Path) command.get(\"before\");\n        final Path after = (Path) command.get(\"after\");\n        final Path outputDirectory = (Path) command.get(\"outputDirectory\");\n        this.threads = (Integer) command.get(\"threads\");\n        run(before, after, outputDirectory);\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(BEFORE_SWITCH, AFTER_SWITCH, OUTPUT_DIRECTORY_SWITCH,\n                THREADS_SWITCH);\n    }\n\n    private void compare(final Atlas beforeAtlas, final Atlas afterAtlas,\n            final Path outputDirectory)\n    {\n        final String name = FilenameUtils.removeExtension(beforeAtlas.getName());\n\n        final AtlasDelta delta = new AtlasDelta(beforeAtlas, afterAtlas).generate();\n\n        final String text = delta.toDiffViewFriendlyString();\n        final File textFile = new File(\n                outputDirectory.resolve(name + FileSuffix.TEXT.toString()).toFile());\n        textFile.writeAndClose(text);\n        this.logger.info(\"Saved text file {}\", textFile);\n\n        final String geoJson = delta.toGeoJson();\n        final File geoJsonFile = new File(\n                outputDirectory.resolve(name + FileSuffix.GEO_JSON.toString()).toFile());\n        geoJsonFile.writeAndClose(geoJson);\n        this.logger.info(\"Saved GeoJSON file {}\", geoJsonFile);\n\n        final String relationsGeoJson = delta.toRelationsGeoJson();\n        final String relationsGeoJsonFileName = name + \"_relations\"\n                + FileSuffix.GEO_JSON.toString();\n        final File relationsGeoJsonFile = new File(\n                outputDirectory.resolve(relationsGeoJsonFileName).toFile());\n        relationsGeoJsonFile.writeAndClose(relationsGeoJson);\n        this.logger.info(\"Saved Relations GeoJSON file {}\", relationsGeoJsonFile);\n    }\n\n    private void compareShardByShard(final Path before, final Path after,\n            final Path outputDirectory)\n    {\n        final List<File> afterShardFiles = fetchAtlasFilesInDirectory(after);\n        afterShardFiles.parallelStream().forEach(afterShardFile ->\n        {\n            final Path beforeShardPath = before.resolve(afterShardFile.getName());\n            final Atlas beforeAtlas = load(beforeShardPath);\n            final Atlas afterAtlas = ATLAS_RESOURCE_LOADER.load(afterShardFile);\n            compare(beforeAtlas, afterAtlas, outputDirectory);\n        });\n    }\n\n    /**\n     * Load a multi atlas if directory, otherwise load single atlas.\n     *\n     * @param path\n     *            An atlas shard directory or a single atlas.\n     * @return An atlas object.\n     */\n    private Atlas load(final Path path)\n    {\n        return ATLAS_RESOURCE_LOADER.load(new File(path.toFile()));\n    }\n\n    private void run(final Path before, final Path after, final Path outputDirectory)\n    {\n        final Time time = Time.now();\n\n        this.logger.info(\"Comparing {} and {}\", before, after);\n\n        // If the after is a directory, we want to diff the individual shards in parallel.\n        if (after.toFile().isDirectory())\n        {\n            // You need to have the before dir be a dir of shards too for this to work.\n            if (!before.toFile().isDirectory())\n            {\n                this.logger.error(\n                        \"Your -before parameter must point to a directory of atlas shards if \"\n                                + \"you want to compare shard by shard with an -after directory also of shards!\");\n                System.exit(COMMAND_LINE_USAGE_ERROR_EXIT);\n            }\n\n            // Execute in a pool of threads so we limit how many atlases get loaded in parallel.\n            try (Pool pool = new Pool(this.threads, \"atlas-diff-worker\"))\n            {\n                pool.queue(() -> this.compareShardByShard(before, after, outputDirectory));\n            }\n        }\n        // Otherwise, we can do a normal compare where we look at 2 atlases or input shards with a\n        // single output.\n        else\n        {\n            final Atlas beforeAtlas = load(before);\n            final Atlas afterAtlas = load(after);\n            compare(beforeAtlas, afterAtlas, outputDirectory);\n        }\n\n        this.logger.info(\"AtlasDeltaGenerator complete. Total time: {}.\", time.elapsedSince());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/delta/Diff.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * A single Atlas diff\n *\n * @author matthieun\n */\npublic class Diff implements Comparable<Diff>, Serializable\n{\n    /**\n     * @author matthieun\n     */\n    public enum DiffReason\n    {\n        ADDED,\n        REMOVED,\n        TAGS,\n        GEOMETRY_OR_TOPOLOGY,\n        RELATION_MEMBER,\n        RELATION_TOPOLOGY\n    }\n\n    /**\n     * @author matthieun\n     */\n    public enum DiffType\n    {\n        ADDED,\n        CHANGED,\n        REMOVED\n    }\n\n    private static final long serialVersionUID = -1798331824716201841L;\n\n    private final ItemType itemType;\n    private final DiffType diffType;\n    private final DiffReason diffReason;\n    private final Atlas before;\n    private final Atlas after;\n    private final long identifier;\n\n    /**\n     * Similar to the regular toString, but attempts to make the diff string more friendly to human\n     * readers.\n     *\n     * @param diffs\n     *            An {@link Iterable} of {@link Diff}\n     * @return the human readable diff string\n     */\n    public static String toDiffViewFriendlyString(final Iterable<Diff> diffs)\n    {\n        final String newLine = System.getProperty(\"line.separator\");\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"Diffset {\");\n        final StringList list = new StringList();\n        for (final Diff diff : diffs)\n        {\n            list.add(newLine + diff.toDiffViewFriendlyString());\n        }\n        builder.append(list.join(newLine));\n        builder.append(newLine + \"}\");\n        return builder.toString();\n    }\n\n    /**\n     * @param diffs\n     *            An {@link Iterable} of {@link Diff}\n     * @return A GeoJSON String representation of all the {@link Diff} items in the {@link Iterable}\n     */\n    public static String toGeoJson(final Iterable<Diff> diffs)\n    {\n        return toGeoJson(diffs, val -> true);\n    }\n\n    /**\n     * @param diffs\n     *            An {@link Iterable} of {@link Diff}\n     * @param filter\n     *            The filter to apply to the diff\n     * @return A GeoJSON String representation of all the {@link Diff} items in the {@link Iterable}\n     *         , which match the filter.\n     */\n    public static String toGeoJson(final Iterable<Diff> diffs, final Predicate<Diff> filter)\n    {\n        return new GeoJsonBuilder().create(\n                Iterables.stream(diffs).filter(diff -> diff.getItemType() != ItemType.RELATION)\n                        .filter(filter).flatMap(Diff::processDiff).collect())\n                .jsonObject().toString();\n    }\n\n    /**\n     * @param diffs\n     *            An {@link Iterable} of {@link Diff}\n     * @return A GeoJSON String representation of all the {@link AtlasItem}s that are a\n     *         {@link RelationMember} in a {@link Diff} {@link Relation}\n     */\n    public static String toRelationsGeoJson(final Iterable<Diff> diffs)\n    {\n        return toRelationsGeoJson(diffs, val -> true);\n    }\n\n    /**\n     * @param diffs\n     *            An {@link Iterable} of {@link Diff}\n     * @param filter\n     *            The filter to apply to the diff\n     * @return A GeoJSON String representation of all the {@link AtlasItem}s that are a\n     *         {@link RelationMember} in a {@link Diff} {@link Relation}, which match the filter.\n     */\n    public static String toRelationsGeoJson(final Iterable<Diff> diffs,\n            final Predicate<Diff> filter)\n    {\n        return new GeoJsonBuilder().create(\n                Iterables.stream(diffs).filter(diff -> diff.getItemType() == ItemType.RELATION)\n                        .filter(filter).flatMap(Diff::processDiff).collect())\n                .jsonObject().toString();\n    }\n\n    /**\n     * @param diffs\n     *            An {@link Iterable} of {@link Diff}\n     * @return A String representation of all the {@link Diff} items in the {@link Iterable}\n     */\n    public static String toString(final Iterable<Diff> diffs)\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[Diffs: \");\n        final StringList list = new StringList();\n        for (final Diff diff : diffs)\n        {\n            list.add(\"\\n\\t\" + diff.toString());\n        }\n        builder.append(list.join(\", \"));\n        builder.append(\"\\n]\");\n        return builder.toString();\n    }\n\n    private static List<LocationIterableProperties> processDiff(final Diff diff)\n    {\n        final List<LocationIterableProperties> items = new ArrayList<>();\n\n        // Process the BEFORE entity. Note that it is null if it is ADDED.\n        final AtlasEntity beforeEntity = diff.getBeforeEntity();\n        processDiffEntity(diff, beforeEntity, items, \"BEFORE\");\n\n        // Process the AFTER entity. Note that it is null if it is REMOVED.\n        final AtlasEntity afterEntity = diff.getAfterEntity();\n        processDiffEntity(diff, afterEntity, items, \"AFTER\");\n\n        return items;\n    }\n\n    private static void processDiffEntity(final Diff diff, final AtlasEntity entity,\n            final List<LocationIterableProperties> items, final String diffStage)\n    {\n        // Depending on if it is a before or after for a given diff type, an entity may be null.\n        if (entity != null)\n        {\n            final Map<String, String> tags = entity.getTags();\n            tags.put(\"diff\", diffStage);\n            tags.put(\"diff:type\", diff.getDiffType().name());\n            tags.put(\"diff:reason\", diff.getDiffReason().name());\n            if (diff.getItemType() == ItemType.RELATION)\n            {\n                items.addAll(processRelationForGeoJson((Relation) entity, tags));\n            }\n            else\n            {\n                items.add(new LocationIterableProperties(((AtlasItem) entity).getRawGeometry(),\n                        tags));\n            }\n        }\n    }\n\n    private static List<LocationIterableProperties> processRelationForGeoJson(\n            final Relation relation, final Map<String, String> parentTags)\n    {\n        final Map<String, String> relationTags = relation.getTags();\n        final Map<String, String> modifiedRelationTags = new HashMap<>(parentTags);\n        for (final String key : relationTags.keySet())\n        {\n            modifiedRelationTags.put(\"[REL_ID:\" + relation.getIdentifier() + \"]\" + key,\n                    relationTags.get(key));\n        }\n        final List<LocationIterableProperties> result = new ArrayList<>();\n        for (final RelationMember member : relation.members())\n        {\n            if (member.getEntity() instanceof Relation)\n            {\n                final Relation subRelation = (Relation) member.getEntity();\n                result.addAll(processRelationForGeoJson(subRelation, modifiedRelationTags));\n            }\n            else\n            {\n                final AtlasItem item = (AtlasItem) member.getEntity();\n                final Map<String, String> modifiedTags = item.getTags();\n                modifiedTags.putAll(modifiedRelationTags);\n                result.add(new LocationIterableProperties(item.getRawGeometry(), modifiedTags));\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Construct\n     *\n     * @param itemType\n     *            The type of the entity that this {@link Diff} represents\n     * @param diffType\n     *            The type of this {@link Diff}, among \"ADDED\", \"REMOVED\" and \"CHANGED\"\n     * @param diffReason\n     *            The reason of this {@link Diff}\n     * @param before\n     *            The before {@link Atlas}, i.e. the older one\n     * @param after\n     *            The after {@link Atlas}, i.e. the newer one\n     * @param identifier\n     *            The identifier if the entity that this {@link Diff} represents.\n     */\n    public Diff(final ItemType itemType, final DiffType diffType, final DiffReason diffReason,\n            final Atlas before, final Atlas after, final long identifier)\n    {\n        this.itemType = itemType;\n        this.diffType = diffType;\n        this.diffReason = diffReason;\n        this.before = before;\n        this.after = after;\n        this.identifier = identifier;\n    }\n\n    @Override\n    public int compareTo(final Diff other)\n    {\n        if (this.getDiffType() != other.getDiffType())\n        {\n            return this.getDiffType().compareTo(other.getDiffType());\n        }\n        if (this.getItemType() != other.getItemType())\n        {\n            return this.itemType.compareTo(other.getItemType());\n        }\n        final long deltaIdentifier = this.getIdentifier() - other.getIdentifier();\n        return deltaIdentifier > 0 ? 1 : deltaIdentifier == 0 ? 0 : -1;\n    }\n\n    /**\n     * @return The after {@link Atlas}, i.e. the newer one\n     */\n    public Atlas getAfter()\n    {\n        return this.after;\n    }\n\n    /**\n     * @return The entity this {@link Diff} represents in the newer Atlas. null if this Diff is of\n     *         type \"REMOVED\"\n     */\n    public AtlasEntity getAfterEntity()\n    {\n        return this.itemType.entityForIdentifier(this.after, this.identifier);\n    }\n\n    /**\n     * @return The before {@link Atlas}, i.e. the older one\n     */\n    public Atlas getBefore()\n    {\n        return this.before;\n    }\n\n    /**\n     * @return The entity this {@link Diff} represents in the older Atlas. null if this Diff is of\n     *         type \"ADDED\"\n     */\n    public AtlasEntity getBeforeEntity()\n    {\n        return this.itemType.entityForIdentifier(this.before, this.identifier);\n    }\n\n    /**\n     * @return The reason for this diff\n     */\n    public DiffReason getDiffReason()\n    {\n        return this.diffReason;\n    }\n\n    /**\n     * @return The type of this {@link Diff}, among \"ADDED\", \"REMOVED\" and \"CHANGED\"\n     */\n    public DiffType getDiffType()\n    {\n        return this.diffType;\n    }\n\n    /**\n     * @return The identifier of the entity that this {@link Diff} represents.\n     */\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    /**\n     * @return The type of the entity that this {@link Diff} represents\n     */\n    public ItemType getItemType()\n    {\n        return this.itemType;\n    }\n\n    /**\n     * @return True if this diff is of type ADDED\n     */\n    public boolean isAdded()\n    {\n        return DiffType.ADDED == this.getDiffType();\n    }\n\n    /**\n     * @return True if this diff is of type CHANGED\n     */\n    public boolean isChanged()\n    {\n        return DiffType.CHANGED == this.getDiffType();\n    }\n\n    /**\n     * @return True if this diff is of type REMOVED\n     */\n    public boolean isRemoved()\n    {\n        return DiffType.REMOVED == this.getDiffType();\n    }\n\n    /**\n     * Similar to the regular toString method. However, this version returns the {@link Diff} with\n     * an attempt at being more friendly to diff readouts.\n     *\n     * @return the string\n     */\n    public String toDiffViewFriendlyString()\n    {\n        final String newLine = System.getProperty(\"line.separator\");\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"Diff {\");\n        builder.append(newLine);\n        builder.append(\"diffType: \" + this.diffType);\n        builder.append(newLine);\n        builder.append(\"diffReason: \" + this.diffReason);\n        builder.append(newLine);\n        builder.append(\"Entity = \" + this.itemType);\n        builder.append(newLine);\n        builder.append(\"ID = \" + this.identifier);\n        builder.append(newLine);\n        if (this.getBeforeEntity() != null)\n        {\n            builder.append(this.getBeforeEntity().toDiffViewFriendlyString());\n        }\n        else\n        {\n            builder.append(\"null\");\n        }\n        builder.append(newLine);\n        builder.append(\" -> \");\n        builder.append(newLine);\n        if (this.getAfterEntity() != null)\n        {\n            builder.append(this.getAfterEntity().toDiffViewFriendlyString());\n        }\n        else\n        {\n            builder.append(\"null\");\n        }\n        builder.append(newLine);\n        builder.append(\"}\");\n        return builder.toString();\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[Diff: \");\n        builder.append(this.diffType);\n        builder.append(\", Entity = {\");\n        builder.append(this.itemType);\n        builder.append(\", ID = \");\n        builder.append(this.identifier);\n        builder.append(\", \");\n        builder.append(this.getBeforeEntity());\n        builder.append(\" -> \");\n        builder.append(this.getAfterEntity());\n        builder.append(\"}]\");\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/delta/README.md",
    "content": "# Atlas Delta\n\n## Use Cases\n\n### All\n\n* An Entity's tag is added\n* An Entity's tag is removed\n* An Entity's tag is changed\n\n### Node\n\n* A Node is added (There is a new identifier)\n* A Node is deleted (The identifier has disappeared)\n* A Node is moved (The Location is different for the same identifier)\n* A Node's in-Edge is added\n* A Node's in-Edge is removed\n* A Node's out-Edge is added\n* A Node's out-Edge is removed\n* A Node is added to a Relation\n* A Node is removed from a Relation\n* A Node's order is changed inside a Relation\n\n### Edge\n\n* An Edge is added (There is a new identifier)\n* An Edge is deleted (The identifier has disappeared)\n* An Edge is moved (The PolyLine is different for the same identifier)\n* An Edge's start Node is different\n* An Edge's end Node is different\n* An Edge is added to a Relation\n* An Edge is removed from a Relation\n* An Edge's order is changed inside a Relation\n\n### Area\n\n* An Area is added (There is a new identifier)\n* An Area is deleted (The identifier has disappeared)\n* An Area is moved (The Polygon is different for the same identifier)\n* An Area is added to a Relation\n* An Area is removed from a Relation\n* An Area's order is changed inside a Relation\n\n### Line\n\n* A Line is added (There is a new identifier)\n* A Line is deleted (The identifier has disappeared)\n* A Line is moved (The PolyLine is different for the same identifier)\n* A Line is added to a Relation\n* A Line is removed from a Relation\n* A Line's order is changed inside a Relation\n\n### Point\n\n* A Point is added (There is a new identifier)\n* A Point is deleted (The identifier has disappeared)\n* A Point is moved (The Location is different for the same identifier)\n* A Point is added to a Relation\n* A Point is removed from a Relation\n* A Point's order is changed inside a Relation\n\n### Relation\n\n* A Relation is added (There is a new identifier)\n* A Relation is deleted (The identifier has disappeared)\n* A Relation is changed:\n  * A Relation member is added (There is a new member identifier)\n  * A Relation member is removed (A member identifier is changed)\n  * A Relation member list order is different\n  * A Relation member is changed:\n    * The role changed (The role String is different)\n* A Relation is added to a Relation\n* A Relation is removed from a Relation\n* A Relation's order is changed inside a Relation\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicArea.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * @author matthieun\n */\npublic class DynamicArea extends Area\n{\n    private static final long serialVersionUID = 3402097623330654390L;\n\n    // Not index!\n    private final long identifier;\n\n    protected DynamicArea(final DynamicAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public Polygon asPolygon()\n    {\n        return subArea().asPolygon();\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return subArea().getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return subArea().relations().stream()\n                .map(relation -> new DynamicRelation(dynamicAtlas(), relation.getIdentifier()))\n                .collect(Collectors.toSet());\n    }\n\n    private DynamicAtlas dynamicAtlas()\n    {\n        return (DynamicAtlas) this.getAtlas();\n    }\n\n    private Area subArea()\n    {\n        final Area result = dynamicAtlas().subArea(this.identifier);\n        if (result != null)\n        {\n            return result;\n        }\n        else\n        {\n            throw new CoreException(\"DynamicAtlas {} moved too fast! {} {} is missing now.\",\n                    dynamicAtlas().getName(), this.getClass().getSimpleName(), this.identifier);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlas.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.BareAtlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * This is not thread safe!\n * <p>\n * An Atlas that is dynamically expanding by loading neighboring shards upon request of its\n * features.\n *\n * @author matthieun\n */\n// NOSONAR here as the parent equals is enough\npublic class DynamicAtlas extends BareAtlas // NOSONAR\n{\n    private static final long serialVersionUID = -2858997785405677961L;\n\n    // The current Atlas that will be swapped during expansion.\n    private Atlas current;\n    private final DynamicAtlasExpander expander;\n\n    /**\n     * @param dynamicAtlasExpansionPolicy\n     *            Expansion policy for the dynamic atlas\n     */\n    public DynamicAtlas(final DynamicAtlasPolicy dynamicAtlasExpansionPolicy)\n    {\n        this.setName(\"DynamicAtlas(\" + dynamicAtlasExpansionPolicy.getInitialShards().stream()\n                .map(Shard::getName).collect(Collectors.toSet()) + \")\");\n        this.expander = new DynamicAtlasExpander(this, dynamicAtlasExpansionPolicy);\n    }\n\n    @Override\n    public Area area(final long identifier)\n    {\n        final Iterator<DynamicArea> result = this.expander\n                .expand(() -> Iterables.from(subArea(identifier)), this.expander::areaCovered,\n                        this::newArea)\n                .iterator();\n        return result.hasNext() ? result.next() : null;\n    }\n\n    @Override\n    public Iterable<Area> areas()\n    {\n        return this.expander.expand(() -> this.current.areas(), this.expander::areaCovered,\n                this::newArea);\n    }\n\n    @Override\n    public Iterable<Area> areasCovering(final Location location)\n    {\n        return this.expander.expand(() -> this.current.areasCovering(location),\n                this.expander::areaCovered, this::newArea);\n    }\n\n    @Override\n    public Iterable<Area> areasCovering(final Location location, final Predicate<Area> matcher)\n    {\n        return Iterables.filter(this.expander.expand(() -> this.current.areasCovering(location),\n                this.expander::areaCovered, this::newArea), matcher);\n    }\n\n    @Override\n    public Iterable<Area> areasIntersecting(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.areasIntersecting(surface),\n                this.expander::areaCovered, this::newArea);\n    }\n\n    @Override\n    public Iterable<Area> areasIntersecting(final GeometricSurface surface,\n            final Predicate<Area> matcher)\n    {\n        return Iterables.filter(this.expander.expand(() -> this.current.areasIntersecting(surface),\n                this.expander::areaCovered, this::newArea), matcher);\n    }\n\n    @Override\n    public Iterable<Area> areasWithin(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.areasWithin(surface),\n                this.expander::areaCovered, this::newArea);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.current.bounds();\n    }\n\n    @Override\n    public Edge edge(final long identifier)\n    {\n        final Iterator<DynamicEdge> result = this.expander\n                .expand(() -> Iterables.from(subEdge(identifier)), this.expander::lineItemCovered,\n                        this::newEdge)\n                .iterator();\n        return result.hasNext() ? result.next() : null;\n    }\n\n    @Override\n    public Iterable<Edge> edges()\n    {\n        return this.expander.expand(() -> this.current.edges(), this.expander::lineItemCovered,\n                this::newEdge);\n    }\n\n    @Override\n    public Iterable<Edge> edgesContaining(final Location location)\n    {\n        return this.expander.expand(() -> this.current.edgesContaining(location),\n                this.expander::lineItemCovered, this::newEdge);\n    }\n\n    @Override\n    public Iterable<Edge> edgesContaining(final Location location, final Predicate<Edge> matcher)\n    {\n        return Iterables.filter(this.expander.expand(() -> this.current.edgesContaining(location),\n                this.expander::lineItemCovered, this::newEdge), matcher);\n    }\n\n    @Override\n    public Iterable<Edge> edgesIntersecting(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.edgesIntersecting(surface),\n                this.expander::lineItemCovered, this::newEdge);\n    }\n\n    @Override\n    public Iterable<Edge> edgesIntersecting(final GeometricSurface surface,\n            final Predicate<Edge> matcher)\n    {\n        return Iterables.filter(this.expander.expand(() -> this.current.edgesIntersecting(surface),\n                this.expander::lineItemCovered, this::newEdge), matcher);\n    }\n\n    @Override\n    public Iterable<Edge> edgesWithin(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.edgesWithin(surface),\n                this.expander::lineItemCovered, this::newEdge);\n    }\n\n    /**\n     * @return All the {@link Atlas}es explored by that {@link DynamicAtlas}\n     */\n    public Set<Atlas> getAtlasesLoaded()\n    {\n        return this.expander.getLoadedShards().values().stream().filter(Objects::nonNull)\n                .collect(Collectors.toSet());\n    }\n\n    /**\n     * @return The number of shards loaded by that {@link DynamicAtlas} at any time.\n     */\n    public int getNumberOfShardsLoaded()\n    {\n        return getShardsLoaded().size();\n    }\n\n    public DynamicAtlasPolicy getPolicy()\n    {\n        return this.expander.getPolicy();\n    }\n\n    /**\n     * @return A copy of the {@link Shard} to {@link Atlas} {@link Map} populated by the underlying\n     *         {@link DynamicAtlasExpander}, with any null Atlases filtered out\n     */\n    public Map<Shard, Atlas> getShardToAtlasMap()\n    {\n        return new HashMap<>(this.expander.getLoadedShards().entrySet().stream()\n                .filter(entry -> entry.getValue() != null)\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));\n    }\n\n    /**\n     * @return All the shards explored by this {@link DynamicAtlas} including the ones that yielded\n     *         no Atlas.\n     */\n    public Set<Shard> getShardsExplored()\n    {\n        return this.expander.getLoadedShards().keySet();\n    }\n\n    /**\n     * @return All the shards explored by that {@link DynamicAtlas} which yielded some non null\n     *         Atlas.\n     */\n    public Set<Shard> getShardsLoaded()\n    {\n        return this.expander.getLoadedShards().entrySet().stream()\n                .filter(entry -> entry.getValue() != null).map(Entry::getKey)\n                .collect(Collectors.toSet());\n    }\n\n    /**\n     * @return The number of times that {@link DynamicAtlas} has (re-)built its {@link MultiAtlas}\n     *         underneath.\n     */\n    public int getTimesMultiAtlasWasBuiltUnderneath()\n    {\n        return this.expander.getTimesMultiAtlasWasBuiltUnderneath();\n    }\n\n    @Override\n    public Line line(final long identifier)\n    {\n        final Iterator<DynamicLine> result = this.expander\n                .expand(() -> Iterables.from(subLine(identifier)), this.expander::lineItemCovered,\n                        this::newLine)\n                .iterator();\n        return result.hasNext() ? result.next() : null;\n    }\n\n    @Override\n    public Iterable<Line> lines()\n    {\n        return this.expander.expand(() -> this.current.lines(), this.expander::lineItemCovered,\n                this::newLine);\n    }\n\n    @Override\n    public Iterable<Line> linesContaining(final Location location)\n    {\n        return this.expander.expand(() -> this.current.linesContaining(location),\n                this.expander::lineItemCovered, this::newLine);\n    }\n\n    @Override\n    public Iterable<Line> linesContaining(final Location location, final Predicate<Line> matcher)\n    {\n        return Iterables.filter(this.expander.expand(() -> this.current.linesContaining(location),\n                this.expander::lineItemCovered, this::newLine), matcher);\n    }\n\n    @Override\n    public Iterable<Line> linesIntersecting(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.linesIntersecting(surface),\n                this.expander::lineItemCovered, this::newLine);\n    }\n\n    @Override\n    public Iterable<Line> linesIntersecting(final GeometricSurface surface,\n            final Predicate<Line> matcher)\n    {\n        return Iterables.filter(this.expander.expand(() -> this.current.linesIntersecting(surface),\n                this.expander::lineItemCovered, this::newLine), matcher);\n    }\n\n    @Override\n    public Iterable<Line> linesWithin(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.linesWithin(surface),\n                this.expander::lineItemCovered, this::newLine);\n    }\n\n    @Override\n    public AtlasMetaData metaData()\n    {\n        return this.current.metaData();\n    }\n\n    @Override\n    public Node node(final long identifier)\n    {\n        final Iterator<DynamicNode> result = this.expander\n                .expand(() -> Iterables.from(subNode(identifier)),\n                        this.expander::locationItemCovered, this::newNode)\n                .iterator();\n        return result.hasNext() ? result.next() : null;\n    }\n\n    @Override\n    public Iterable<Node> nodes()\n    {\n        return this.expander.expand(() -> this.current.nodes(), this.expander::locationItemCovered,\n                this::newNode);\n    }\n\n    @Override\n    public Iterable<Node> nodesAt(final Location location)\n    {\n        return this.expander.expand(() -> this.current.nodesAt(location),\n                this.expander::locationItemCovered, this::newNode);\n    }\n\n    @Override\n    public Iterable<Node> nodesWithin(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.nodesWithin(surface),\n                this.expander::locationItemCovered, this::newNode);\n    }\n\n    @Override\n    public Iterable<Node> nodesWithin(final GeometricSurface surface, final Predicate<Node> matcher)\n    {\n        return Iterables.filter(this.expander.expand(() -> this.current.nodesWithin(surface),\n                this.expander::locationItemCovered, this::newNode), matcher);\n    }\n\n    @Override\n    public long numberOfAreas()\n    {\n        return this.current.numberOfAreas();\n    }\n\n    @Override\n    public long numberOfEdges()\n    {\n        return this.current.numberOfEdges();\n    }\n\n    @Override\n    public long numberOfLines()\n    {\n        return this.current.numberOfLines();\n    }\n\n    @Override\n    public long numberOfNodes()\n    {\n        return this.current.numberOfNodes();\n    }\n\n    @Override\n    public long numberOfPoints()\n    {\n        return this.current.numberOfPoints();\n    }\n\n    @Override\n    public long numberOfRelations()\n    {\n        return this.current.numberOfRelations();\n    }\n\n    @Override\n    public Point point(final long identifier)\n    {\n        final Iterator<DynamicPoint> result = this.expander\n                .expand(() -> Iterables.from(subPoint(identifier)),\n                        this.expander::locationItemCovered, this::newPoint)\n                .iterator();\n        return result.hasNext() ? result.next() : null;\n    }\n\n    @Override\n    public Iterable<Point> points()\n    {\n        return this.expander.expand(() -> this.current.points(), this.expander::locationItemCovered,\n                this::newPoint);\n    }\n\n    @Override\n    public Iterable<Point> pointsAt(final Location location)\n    {\n        return this.expander.expand(() -> this.current.pointsAt(location),\n                this.expander::locationItemCovered, this::newPoint);\n    }\n\n    @Override\n    public Iterable<Point> pointsWithin(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.pointsWithin(surface),\n                this.expander::locationItemCovered, this::newPoint);\n    }\n\n    @Override\n    public Iterable<Point> pointsWithin(final GeometricSurface surface,\n            final Predicate<Point> matcher)\n    {\n        return Iterables.filter(this.expander.expand(() -> this.current.pointsWithin(surface),\n                this.expander::locationItemCovered, this::newPoint), matcher);\n    }\n\n    /**\n     * Do a preemptive load of the {@link DynamicAtlas} as far as the {@link DynamicAtlasPolicy}\n     * allows.\n     * <p>\n     * In some very specific cases, where the {@link DynamicAtlasPolicy} allows expansion only if\n     * new shards intersect at least one feature that crosses the initial set of shards, it is\n     * possible that expanding only one time misses out some shard candidates. This happens when\n     * some feature intersects the initial shards but does not have any shape point inside any\n     * initial shard. This way, the initial shards do not contain that feature even though they\n     * intersect it. That feature is discovered as we load the neighboring shards which contain that\n     * feature. If that said feature also intersects a third neighboring shard, then that third\n     * neighboring shard becomes eligible for expansion, as that specific feature crosses it and the\n     * initial shards. To work around that case, the preemptive load will do a multi-staged loading.\n     */\n    public void preemptiveLoad()\n    {\n        this.expander.preemptiveLoad();\n    }\n\n    @Override\n    public Relation relation(final long identifier)\n    {\n        final Iterator<DynamicRelation> result = this.expander\n                .expand(() -> Iterables.from(subRelation(identifier)),\n                        this.expander::relationCovered, this::newRelation)\n                .iterator();\n        return result.hasNext() ? result.next() : null;\n    }\n\n    @Override\n    public Iterable<Relation> relations()\n    {\n        return this.expander.expand(() -> this.current.relations(), this.expander::relationCovered,\n                this::newRelation);\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesIntersecting(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.relationsWithEntitiesIntersecting(surface),\n                this.expander::relationCovered, this::newRelation);\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesIntersecting(final GeometricSurface surface,\n            final Predicate<Relation> matcher)\n    {\n        return Iterables.filter(\n                this.expander.expand(() -> this.current.relationsWithEntitiesIntersecting(surface),\n                        this.expander::relationCovered, this::newRelation),\n                matcher);\n    }\n\n    @Override\n    public Iterable<Relation> relationsWithEntitiesWithin(final GeometricSurface surface)\n    {\n        return this.expander.expand(() -> this.current.relationsWithEntitiesWithin(surface),\n                this.expander::relationCovered, this::newRelation);\n    }\n\n    @Override\n    public void save(final WritableResource writableResource)\n    {\n        throw new CoreException(\"DynamicAtlas cannot be saved\");\n    }\n\n    protected Area subArea(final long identifier)\n    {\n        return this.current.area(identifier);\n    }\n\n    protected Edge subEdge(final long identifier)\n    {\n        return this.current.edge(identifier);\n    }\n\n    protected Line subLine(final long identifier)\n    {\n        return this.current.line(identifier);\n    }\n\n    protected Node subNode(final long identifier)\n    {\n        return this.current.node(identifier);\n    }\n\n    protected Point subPoint(final long identifier)\n    {\n        return this.current.point(identifier);\n    }\n\n    protected Relation subRelation(final long identifier)\n    {\n        return this.current.relation(identifier);\n    }\n\n    synchronized void swapCurrentAtlas(final Atlas current)\n    {\n        this.current = current;\n    }\n\n    private DynamicArea newArea(final Area area)\n    {\n        return new DynamicArea(this, area.getIdentifier());\n    }\n\n    private DynamicEdge newEdge(final Edge edge)\n    {\n        return new DynamicEdge(this, edge.getIdentifier());\n    }\n\n    private DynamicLine newLine(final Line line)\n    {\n        return new DynamicLine(this, line.getIdentifier());\n    }\n\n    private DynamicNode newNode(final Node node)\n    {\n        return new DynamicNode(this, node.getIdentifier());\n    }\n\n    private DynamicPoint newPoint(final Point point)\n    {\n        return new DynamicPoint(this, point.getIdentifier());\n    }\n\n    private DynamicRelation newRelation(final Relation relation)\n    {\n        return new DynamicRelation(this, relation.getIdentifier());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasExpander.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometryPrintable;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.WktPrintable;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StreamIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\nclass DynamicAtlasExpander\n{\n    private static final Logger logger = LoggerFactory.getLogger(DynamicAtlasExpander.class);\n\n    private final DynamicAtlas dynamicAtlas;\n\n    private Set<Shard> shardsUsedForCurrent;\n    private final Map<Shard, Atlas> loadedShards;\n    private final Function<Shard, Optional<Atlas>> atlasFetcher;\n    private final Sharding sharding;\n    private final DynamicAtlasPolicy policy;\n    // This is true when the loading of the initial shard has been completed\n    private final boolean initialized;\n    // This is true when, in case of deferred loading, the loading of the shards has been called\n    // (unlocking further automatic loading later)\n    private boolean isAlreadyLoaded = false;\n    private boolean preemptiveLoadDone = false;\n    // Number of times the udnerlying Multi-Atlas has been built.\n    private int timesMultiAtlasWasBuiltUnderneath;\n    private final Set<Long> areaCoveredCache;\n    private final Set<Long> edgeCoveredCache;\n    private final Set<Long> lineCoveredCache;\n\n    DynamicAtlasExpander(final DynamicAtlas dynamicAtlas, final DynamicAtlasPolicy policy)\n    {\n        this.dynamicAtlas = dynamicAtlas;\n\n        this.timesMultiAtlasWasBuiltUnderneath = 0;\n        this.sharding = policy.getSharding();\n        this.loadedShards = new HashMap<>();\n        this.shardsUsedForCurrent = new HashSet<>();\n        this.atlasFetcher = policy.getAtlasFetcher();\n        // Still keep the policy\n        this.policy = policy;\n        this.addNewShards(policy.getInitialShards());\n        this.initialized = true;\n        // DynamicAtlas always expands, so it is ok to cache the features already covered by shards.\n        this.areaCoveredCache = new HashSet<>();\n        this.edgeCoveredCache = new HashSet<>();\n        this.lineCoveredCache = new HashSet<>();\n    }\n\n    public DynamicAtlasPolicy getPolicy()\n    {\n        return this.policy;\n    }\n\n    boolean areaCovered(final Area area)\n    {\n        if (!entityNotCached(area))\n        {\n            return true;\n        }\n        final Polygon polygon = area.asPolygon();\n        final MultiPolygon initialShardsBounds = this.policy.getInitialShardsBounds();\n        if (!this.policy.isExtendIndefinitely() && !(polygon.overlaps(initialShardsBounds)\n                || initialShardsBounds.overlaps(polygon)))\n        {\n            // If the policy is to not extend indefinitely, then assume that the loading is not\n            // necessary.\n            return true;\n        }\n        cacheEntity(area);\n        final Iterable<? extends Shard> neededShards = this.sharding.shards(polygon);\n        for (final Shard neededShard : neededShards)\n        {\n            if (!this.loadedShards.containsKey(neededShard))\n            {\n                newPolygon(polygon, area);\n                return false;\n            }\n        }\n        return true;\n    }\n\n    void buildUnderlyingMultiAtlas()\n    {\n        final Time buildTime = Time.now();\n        final Set<Shard> nonNullShards = nonNullShards();\n        if (this.shardsUsedForCurrent.equals(nonNullShards))\n        {\n            // Same Multi-Atlas, let's not reload.\n            return;\n        }\n        final List<Atlas> nonNullAtlasShards = getNonNullAtlasShards();\n        if (!nonNullAtlasShards.isEmpty())\n        {\n            this.policy.getShardSetChecker().accept(nonNullShards());\n            if (nonNullAtlasShards.size() == 1)\n            {\n                this.dynamicAtlas.swapCurrentAtlas(nonNullAtlasShards.get(0));\n            }\n            else\n            {\n                if (logger.isDebugEnabled())\n                {\n                    logger.debug(\"{}: Loading MultiAtlas with {}\", this.dynamicAtlas.getName(),\n                            nonNullShards().stream().map(Shard::getName)\n                                    .collect(Collectors.toList()));\n                }\n                this.dynamicAtlas.swapCurrentAtlas(new MultiAtlas(nonNullAtlasShards));\n                this.timesMultiAtlasWasBuiltUnderneath++;\n            }\n            this.shardsUsedForCurrent = nonNullShards;\n            if (this.initialized)\n            {\n                this.isAlreadyLoaded = true;\n            }\n        }\n        else\n        {\n            throw new CoreException(\"Cannot load shards with no data!\");\n        }\n        logger.trace(\"{}: Built underlying MultiAtlas in {}\", this.dynamicAtlas.getName(),\n                buildTime.elapsedSince());\n    }\n\n    /**\n     * Expand the Atlas if needed. This method loops through the provided {@link Iterable}, then\n     * checks if each entity found warrants loading another neighboring {@link Shard}. If it does,\n     * it loads all the necessary {@link Shard}s and retries looping through the new\n     * {@link Iterable}. Once everything is included, then the final {@link Iterable} is returned.\n     *\n     * @param entitiesSupplier\n     *            The {@link Supplier} of the {@link Iterable} of items that will be called as long\n     *            as there are overlaps to new shards. There is a need of a supplier here so that\n     *            the {@link Iterable} is re-built every time with the latest Atlas.\n     * @param entityCoveredPredicate\n     *            The function that decides if an entity is already covered or not.\n     * @param mapper\n     *            What to do with the result. This is to replace the regular items with\n     *            DynamicItems.\n     * @param <T>\n     *            The object type the returned iterable will return\n     * @param <V>\n     *            The original entity type\n     * @return The {@link Iterable} of DynamicItems\n     */\n    <V extends AtlasEntity, T> Iterable<T> expand(final Supplier<Iterable<V>> entitiesSupplier,\n            final Predicate<V> entityCoveredPredicate, final Function<V, T> mapper)\n    {\n        StreamIterable<V> result = Iterables.stream(entitiesSupplier.get())\n                .filter(Objects::nonNull);\n        final boolean shouldStopExploring = this.policy.isDeferLoading() && this.preemptiveLoadDone;\n        while (!shouldStopExploring && !entitiesCovered(result, entityCoveredPredicate))\n        {\n            result = Iterables.stream(entitiesSupplier.get()).filter(Objects::nonNull);\n        }\n        return result.map(mapper).collect();\n    }\n\n    Map<Shard, Atlas> getLoadedShards()\n    {\n        return this.loadedShards;\n    }\n\n    /**\n     * @return The number of times that {@link DynamicAtlas} has (re-)built its {@link MultiAtlas}\n     *         underneath.\n     */\n    int getTimesMultiAtlasWasBuiltUnderneath()\n    {\n        return this.timesMultiAtlasWasBuiltUnderneath;\n    }\n\n    boolean lineItemCovered(final LineItem item)\n    {\n        if (!entityNotCached(item))\n        {\n            return true;\n        }\n        final PolyLine polyLine = item.asPolyLine();\n        final MultiPolygon initialShardsBounds = this.policy.getInitialShardsBounds();\n        if (!this.policy.isExtendIndefinitely() && !initialShardsBounds.overlaps(polyLine)\n                || !this.policy.getAtlasEntitiesToConsiderForExpansion().test(item))\n        {\n            // If the policy is to not extend indefinitely, then assume that the loading is not\n            // necessary.\n            return true;\n        }\n        cacheEntity(item);\n        final Iterable<? extends Shard> neededShards = this.sharding.shardsIntersecting(polyLine);\n        for (final Shard neededShard : neededShards)\n        {\n            if (!this.loadedShards.containsKey(neededShard))\n            {\n                newPolyLine(polyLine, item);\n                return false;\n            }\n        }\n        return true;\n    }\n\n    boolean locationItemCovered(final LocationItem item)\n    {\n        final Location location = item.getLocation();\n        final MultiPolygon initialShardsBounds = this.policy.getInitialShardsBounds();\n        if (!this.policy.isExtendIndefinitely()\n                && !initialShardsBounds.fullyGeometricallyEncloses(location)\n                || !this.policy.getAtlasEntitiesToConsiderForExpansion().test(item))\n        {\n            // If the policy is to not extend indefinitely, then assume that the loading is not\n            // necessary.\n            return true;\n        }\n        final Iterable<? extends Shard> neededShards = this.sharding.shardsCovering(location);\n        for (final Shard neededShard : neededShards)\n        {\n            if (!this.loadedShards.containsKey(neededShard))\n            {\n                newLocation(location, item);\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Do a preemptive load of the {@link DynamicAtlas} as far as the {@link DynamicAtlasPolicy}\n     * allows.\n     * <p>\n     * In some very specific cases, where the {@link DynamicAtlasPolicy} allows expansion only if\n     * new shards intersect at least one feature that crosses the initial set of shards, it is\n     * possible that expanding only one time misses out some shard candidates. This happens when\n     * some feature intersects the initial shards but does not have any shape point inside any\n     * initial shard. This way, the initial shards do not contain that feature even though they\n     * intersect it. That feature is discovered as we load the neighboring shards which contain that\n     * feature. If that said feature also intersects a third neighboring shard, then that third\n     * neighboring shard becomes eligible for expansion, as that specific feature crosses it and the\n     * initial shards. To work around that case, the preemptive load will do a multi-staged loading.\n     */\n    void preemptiveLoad()\n    {\n        if (!this.policy.isDeferLoading())\n        {\n            logger.warn(\n                    \"{}: Skipping preemptive loading as it is useful only when the DynamicAtlasPolicy is deferLoading = true.\",\n                    this.dynamicAtlas.getName());\n            return;\n        }\n        if (this.preemptiveLoadDone)\n        {\n            return;\n        }\n        // Loop through the entities to find potential shards to add\n        browseForPotentialNewShards();\n        // Load all the shards into a multiAtlas\n        buildUnderlyingMultiAtlas();\n        // Record the current list of shards\n        Set<Shard> currentShards = new HashSet<>(this.loadedShards.keySet());\n        // Loop through the entities again to find potential shards to add. This can still happen if\n        // a way intersects the initial shard without shapepoints inside the initial shards, and was\n        // revealed by loading a new neighboring shard. At that point, if that way also intersects a\n        // third shard which was not loaded before, that third shard might become now eligible.\n        browseForPotentialNewShards();\n        browseForPotentialNewShardsFromAggressiveRelations();\n        // Repeat the same process as long as we find some of those third party shards.\n        while (!this.loadedShards.keySet().equals(currentShards))\n        {\n            if (logger.isInfoEnabled())\n            {\n                final Set<Shard> missingShards = new HashSet<>(this.loadedShards.keySet());\n                missingShards.removeAll(currentShards);\n                logger.info(\"{}: Preemptive load found new unexpected 2nd degree shard(s): {}\",\n                        this.dynamicAtlas.getName(),\n                        missingShards.stream().map(Shard::getName).collect(Collectors.toList()));\n            }\n\n            // Load all the shards into a multiAtlas\n            buildUnderlyingMultiAtlas();\n            // Record the current list of shards\n            currentShards = new HashSet<>(this.loadedShards.keySet());\n            // Loop through the entities again to find potential shards to add.\n            browseForPotentialNewShards();\n            browseForPotentialNewShardsFromAggressiveRelations();\n        }\n        this.preemptiveLoadDone = true;\n    }\n\n    boolean relationCovered(final Relation relation)\n    {\n        final Set<Long> parentRelationIdentifierTree = new HashSet<>();\n        parentRelationIdentifierTree.add(relation.getIdentifier());\n        return relationCoveredInternal(relation, parentRelationIdentifierTree);\n    }\n\n    private void addNewShardLog(final Shard shard)\n    {\n        if (logger.isInfoEnabled())\n        {\n            final Atlas loaded = this.loadedShards.get(shard);\n            if (loaded == null)\n            {\n                logger.info(\"{}: Loading new shard {} found no new Atlas.\",\n                        this.dynamicAtlas.getName(), shard.getName());\n            }\n            else\n            {\n                logger.info(\"{}: Loading new shard {} found a new Atlas {} of size {}\",\n                        this.dynamicAtlas.getName(), shard.getName(), loaded.getName(),\n                        loaded.size());\n            }\n        }\n    }\n\n    private void addNewShards(final Iterable<? extends Shard> shards)\n    {\n        final Set<Shard> initialNonEmptyLoadedShards = nonNullShards();\n        for (final Shard shard : shards)\n        {\n            if (!this.loadedShards.containsKey(shard))\n            {\n                this.loadedShards.put(shard, this.atlasFetcher.apply(shard).orElse(null));\n                addNewShardLog(shard);\n            }\n        }\n        final List<Atlas> nonNullAtlasShards = getNonNullAtlasShards();\n        if (!nonNullAtlasShards.isEmpty())\n        {\n            if (shouldBuildUnderlyingMultiAtlasWhenAddingNewShards(initialNonEmptyLoadedShards))\n            {\n                // Load the new current atlas only if it is the first time, or it is not the\n                // first time, and the policy is not to defer loading.\n                buildUnderlyingMultiAtlas();\n            }\n        }\n        else\n        {\n            // There should always be a non-null atlas in that list, coming from the initial Shard.\n            throw new CoreException(\"{}: There is no data to load for initial shard!\",\n                    this.dynamicAtlas.getName());\n        }\n    }\n\n    private boolean areaCoversInitialShardBounds(final Area area)\n    {\n        return this.policy.getInitialShardsBounds().overlaps(area.asPolygon());\n    }\n\n    private void browseForPotentialNewShards()\n    {\n        // Look at regular entities\n        this.dynamicAtlas.entities();\n    }\n\n    private void browseForPotentialNewShardsFromAggressiveRelations()\n    {\n        // In case we want to aggressively explore relations, we constrain it to only when the\n        // policy is to defer loading.\n        if (this.policy.isAggressivelyExploreRelations() && this.policy.isDeferLoading())\n        {\n            // Get all the neighboring shards\n            final Set<Shard> onlyNeighboringShards = new HashSet<>();\n            this.loadedShards.keySet().forEach(\n                    shard -> this.sharding.neighbors(shard).forEach(onlyNeighboringShards::add));\n            onlyNeighboringShards.removeAll(this.loadedShards.keySet());\n            if (logger.isTraceEnabled())\n            {\n                final Set<String> shardNames = onlyNeighboringShards.stream().map(Shard::getName)\n                        .collect(Collectors.toSet());\n                final String wktCollection = WktPrintable.toWktCollection(onlyNeighboringShards);\n                logger.trace(\"{}: Aggressively exploring relations in shards {} - {}\",\n                        this.dynamicAtlas.getName(), shardNames, wktCollection);\n            }\n            // For each of those shards, load the Atlas individually and find the relation and its\n            // members if it is there too.\n            final Set<Shard> neighboringShardsContainingRelation = neighboringShardsContainingInitialRelation(\n                    onlyNeighboringShards);\n            // Add the neighboring shards as new shards to be loaded.\n            if (!neighboringShardsContainingRelation.isEmpty())\n            {\n                addNewShards(neighboringShardsContainingRelation);\n            }\n        }\n    }\n\n    private void cacheEntity(final AtlasEntity atlasEntity)\n    {\n        if (atlasEntity instanceof Area)\n        {\n            this.areaCoveredCache.add(atlasEntity.getIdentifier());\n        }\n        else if (atlasEntity instanceof LineItem)\n        {\n            cacheEntity((LineItem) atlasEntity);\n        }\n    }\n\n    private void cacheEntity(final LineItem lineItem)\n    {\n        if (lineItem instanceof Edge)\n        {\n            this.edgeCoveredCache.add(lineItem.getIdentifier());\n        }\n        else\n        {\n            this.lineCoveredCache.add(lineItem.getIdentifier());\n        }\n    }\n\n    /**\n     * @param entities\n     *            The items to test for full coverage by the current shards\n     * @param entityCoveredPredicate\n     *            The function that decides if an entity is already covered or not.\n     * @return False if any of the items is not fully covered by the current shards\n     */\n    private <V extends AtlasEntity> boolean entitiesCovered(final Iterable<V> entities,\n            final Predicate<V> entityCoveredPredicate)\n    {\n        return Iterables.stream(entities).filter(this::entityNotCached).filter(entity ->\n        {\n            final boolean toConsiderForExpansion = this.policy\n                    .getAtlasEntitiesToConsiderForExpansion().test(entity);\n            if (!toConsiderForExpansion)\n            {\n                cacheEntity(entity);\n            }\n            return toConsiderForExpansion;\n        }).allMatch(entityCoveredPredicate);\n    }\n\n    private boolean entityNotCached(final LineItem lineItem)\n    {\n        if (lineItem instanceof Edge)\n        {\n            return !this.edgeCoveredCache.contains(lineItem.getIdentifier());\n        }\n        else\n        {\n            return !this.lineCoveredCache.contains(lineItem.getIdentifier());\n        }\n    }\n\n    private boolean entityNotCached(final AtlasEntity atlasEntity)\n    {\n        if (atlasEntity instanceof Area)\n        {\n            return !this.areaCoveredCache.contains(atlasEntity.getIdentifier());\n        }\n        else if (atlasEntity instanceof LineItem)\n        {\n            return entityNotCached((LineItem) atlasEntity);\n        }\n        else\n        {\n            return true;\n        }\n    }\n\n    private List<Atlas> getNonNullAtlasShards()\n    {\n        return this.loadedShards.values().stream().filter(Objects::nonNull)\n                .collect(Collectors.toList());\n    }\n\n    private boolean lineItemCoversInitialShardBounds(final LineItem lineItem)\n    {\n        return this.policy.getInitialShardsBounds().overlaps(lineItem.asPolyLine());\n    }\n\n    private boolean loadedShardsfullyGeometricallyEncloseLocation(final Location location)\n    {\n        return Iterables.stream(this.sharding.shardsCovering(location))\n                .allMatch(this.loadedShards::containsKey);\n    }\n\n    private boolean loadedShardsfullyGeometricallyEnclosePolyLine(final PolyLine polyLine)\n    {\n        return Iterables.stream(this.sharding.shardsIntersecting(polyLine))\n                .allMatch(this.loadedShards::containsKey);\n    }\n\n    private boolean loadedShardsfullyGeometricallyEnclosePolygon(final Polygon polygon)\n    {\n        return Iterables.stream(this.sharding.shards(polygon))\n                .allMatch(this.loadedShards::containsKey);\n    }\n\n    private boolean locationItemCoversInitialShardBounds(final LocationItem locationItem)\n    {\n        return this.policy.getInitialShardsBounds()\n                .fullyGeometricallyEncloses(locationItem.getLocation());\n    }\n\n    private boolean neighboringAtlasContainingInitialRelation(final Atlas atlas)\n    {\n        for (final Relation newRelation : atlas.relations())\n        {\n            final Relation currentRelation = this.dynamicAtlas\n                    .subRelation(newRelation.getIdentifier());\n            if (currentRelation != null\n                    && this.policy.getAtlasEntitiesToConsiderForExpansion().test(currentRelation)\n                    && relationCoversInitialShardBounds(currentRelation))\n            {\n                final RelationBean newMembers = newRelation.members().asBean();\n                final RelationBean currentMembers = currentRelation.members().asBean();\n                for (final RelationBeanItem newMember : newMembers)\n                {\n                    if (!currentMembers.contains(newMember))\n                    {\n                        newShapeLog(newRelation, currentRelation);\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    private Set<Shard> neighboringShardsContainingInitialRelation(\n            final Set<Shard> neighboringShardCandidates)\n    {\n        final Set<Shard> neighboringShardsContainingRelation = new HashSet<>();\n        neighboringShardCandidates\n                .forEach(shard -> this.policy.getAtlasFetcher().apply(shard).ifPresent(atlas ->\n                {\n                    if (neighboringAtlasContainingInitialRelation(atlas))\n                    {\n                        neighboringShardsContainingRelation.add(shard);\n                    }\n                }));\n        return neighboringShardsContainingRelation;\n    }\n\n    private void newLocation(final Location location, final LocationItem... source)\n    {\n        if (!loadedShardsfullyGeometricallyEncloseLocation(location))\n        {\n            newShapeLog(location, source);\n            addNewShards(this.sharding.shardsCovering(location));\n        }\n    }\n\n    private void newPolyLine(final PolyLine polyLine, final LineItem... source)\n    {\n        if (!loadedShardsfullyGeometricallyEnclosePolyLine(polyLine))\n        {\n            newShapeLog(polyLine, source);\n            addNewShards(this.sharding.shardsIntersecting(polyLine));\n        }\n    }\n\n    private void newPolygon(final Polygon polygon, final AtlasEntity... source)\n    {\n        if (!loadedShardsfullyGeometricallyEnclosePolygon(polygon))\n        {\n            newShapeLog(polygon, source);\n            addNewShards(this.sharding.shards(polygon));\n        }\n    }\n\n    private void newShapeLog(final GeometryPrintable geometry, final AtlasEntity... source)\n    {\n        if (logger.isDebugEnabled())\n        {\n            logger.debug(\"{}: Triggering new shard load for {}{}\", this.dynamicAtlas.getName(),\n                    source.length > 0\n                            ? \"Atlas \" + new StringList(Iterables.stream(Iterables.asList(source))\n                                    .map(item -> item.getType() + \" \" + item.getIdentifier()))\n                                    .join(\", \") + \" with shape \"\n                            : \"\",\n                    geometry.toWkt());\n        }\n    }\n\n    private Set<Shard> nonNullShards()\n    {\n        return new HashSet<>(this.loadedShards.keySet().stream()\n                .filter(shard -> this.loadedShards.get(shard) != null).collect(Collectors.toSet()));\n    }\n\n    // NOSONAR here as complexity 16 is ok.\n    private boolean relationCoveredInternal(final Relation relation, // NOSONAR\n            final Set<Long> parentRelationIdentifierTree)\n    {\n        final RelationMemberList members = relation.members();\n        boolean result = true;\n        for (final RelationMember member : members)\n        {\n            final AtlasEntity entity = member.getEntity();\n            if (entity instanceof Area)\n            {\n                if (!areaCovered((Area) entity))\n                {\n                    result = false;\n                }\n            }\n            else if (entity instanceof LineItem)\n            {\n                if (!lineItemCovered((LineItem) entity))\n                {\n                    result = false;\n                }\n            }\n            else if (entity instanceof LocationItem)\n            {\n                if (!locationItemCovered((LocationItem) entity))\n                {\n                    result = false;\n                }\n            }\n            else if (entity instanceof Relation)\n            {\n                result = relationMemberCoveredInternal(relation, (Relation) entity,\n                        parentRelationIdentifierTree);\n            }\n            else\n            {\n                throw new CoreException(\"Unknown Relation Member Type: {}\",\n                        entity.getClass().getName());\n            }\n        }\n        return result;\n    }\n\n    private boolean relationCoversInitialShardBounds(final Relation relation)\n    {\n        final Set<Long> parentRelationIdentifierTree = new HashSet<>();\n        parentRelationIdentifierTree.add(relation.getIdentifier());\n        return relationCoversInitialShardBoundsInternal(relation, parentRelationIdentifierTree);\n    }\n\n    // NOSONAR here as complexity 16 is ok.\n    private boolean relationCoversInitialShardBoundsInternal(final Relation relation, // NOSONAR\n            final Set<Long> parentRelationIdentifierTree)\n    {\n        final RelationMemberList members = relation.members();\n        boolean result = false;\n        for (final RelationMember member : members)\n        {\n            final AtlasEntity entity = member.getEntity();\n            if (entity instanceof Area)\n            {\n                if (areaCoversInitialShardBounds((Area) entity))\n                {\n                    result = true;\n                }\n            }\n            else if (entity instanceof LineItem)\n            {\n                if (lineItemCoversInitialShardBounds((LineItem) entity))\n                {\n                    result = true;\n                }\n            }\n            else if (entity instanceof LocationItem)\n            {\n                if (locationItemCoversInitialShardBounds((LocationItem) entity))\n                {\n                    result = true;\n                }\n            }\n            else if (entity instanceof Relation)\n            {\n                result = relationMemberCoversInitialShardBoundsInternal(relation, (Relation) entity,\n                        parentRelationIdentifierTree);\n            }\n            else\n            {\n                throw new CoreException(\"Unknown Relation Member Type: {}\",\n                        entity.getClass().getName());\n            }\n        }\n        return result;\n    }\n\n    private boolean relationMemberCoveredInternal(final Relation parentRelation,\n            final Relation relation, final Set<Long> parentRelationIdentifierTree)\n    {\n        boolean result = true;\n        final long newIdentifier = relation.getIdentifier();\n        if (parentRelationIdentifierTree.contains(newIdentifier))\n        {\n            logger.error(\n                    \"Skipping! Unable to expand on relation which has a loop: {}. Parent tree: {}\",\n                    parentRelation, parentRelationIdentifierTree);\n        }\n        else\n        {\n            final Set<Long> newParentRelationIdentifierTree = new HashSet<>();\n            newParentRelationIdentifierTree.addAll(parentRelationIdentifierTree);\n            newParentRelationIdentifierTree.add(newIdentifier);\n            if (!relationCoveredInternal(relation, newParentRelationIdentifierTree))\n            {\n                result = false;\n            }\n        }\n        return result;\n    }\n\n    private boolean relationMemberCoversInitialShardBoundsInternal(final Relation parentRelation,\n            final Relation relation, final Set<Long> parentRelationIdentifierTree)\n    {\n        boolean result = false;\n        final long newIdentifier = relation.getIdentifier();\n        if (parentRelationIdentifierTree.contains(newIdentifier))\n        {\n            logger.error(\n                    \"Skipping! Unable to expand on relation which has a loop: {}. Parent tree: {}\",\n                    parentRelation, parentRelationIdentifierTree);\n        }\n        else\n        {\n            final Set<Long> newParentRelationIdentifierTree = new HashSet<>();\n            newParentRelationIdentifierTree.addAll(parentRelationIdentifierTree);\n            newParentRelationIdentifierTree.add(newIdentifier);\n            if (relationCoversInitialShardBoundsInternal(relation, newParentRelationIdentifierTree))\n            {\n                result = true;\n            }\n        }\n        return result;\n    }\n\n    private boolean shouldBuildUnderlyingMultiAtlasWhenAddingNewShards(\n            final Set<Shard> initialNonEmptyLoadedShards)\n    {\n        final boolean thereAreNewViableShards = !initialNonEmptyLoadedShards\n                .equals(nonNullShards());\n        // If DynamicAtlas is not initialized yet, it means this call is within the constructor.\n        // We always load the initial shards first.\n        final boolean dynamicAtlasNotInitializedYet = !this.initialized;\n        // This is either:\n        // 1. The opposite waiting for a preemptive load call\n        // OR\n        // 2. The preemptive load call has already happened and we are in a subsequent call.\n        final boolean loadingIsNotDeferredOrItIsAndAlreadyHappened = !this.policy.isDeferLoading()\n                || this.isAlreadyLoaded;\n\n        final boolean shouldBuildUnderlyingMultiAtlas = thereAreNewViableShards // NOSONAR\n                && (dynamicAtlasNotInitializedYet || loadingIsNotDeferredOrItIsAndAlreadyHappened);\n        return shouldBuildUnderlyingMultiAtlas;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * @author matthieun\n */\npublic class DynamicEdge extends Edge\n{\n    private static final long serialVersionUID = -3839789846949424342L;\n\n    // Not index!\n    private final long identifier;\n\n    protected DynamicEdge(final DynamicAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return subEdge().asPolyLine();\n    }\n\n    @Override\n    public Node end()\n    {\n        return new DynamicNode(dynamicAtlas(), subEdge().end().getIdentifier());\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return subEdge().getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return subEdge().relations().stream()\n                .map(relation -> new DynamicRelation(dynamicAtlas(), relation.getIdentifier()))\n                .collect(Collectors.toSet());\n    }\n\n    @Override\n    public Node start()\n    {\n        return new DynamicNode(dynamicAtlas(), subEdge().start().getIdentifier());\n    }\n\n    private DynamicAtlas dynamicAtlas()\n    {\n        return (DynamicAtlas) this.getAtlas();\n    }\n\n    private Edge subEdge()\n    {\n        final Edge result = dynamicAtlas().subEdge(this.identifier);\n        if (result != null)\n        {\n            return result;\n        }\n        else\n        {\n            throw new CoreException(\"DynamicAtlas {} moved too fast! {} {} is missing now.\",\n                    dynamicAtlas().getName(), this.getClass().getSimpleName(), this.identifier);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicLine.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * @author matthieun\n */\npublic class DynamicLine extends Line\n{\n    private static final long serialVersionUID = -7689258557279641445L;\n\n    // Not index!\n    private final long identifier;\n\n    protected DynamicLine(final DynamicAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return subLine().asPolyLine();\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return subLine().getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return subLine().relations().stream()\n                .map(relation -> new DynamicRelation(dynamicAtlas(), relation.getIdentifier()))\n                .collect(Collectors.toSet());\n    }\n\n    private DynamicAtlas dynamicAtlas()\n    {\n        return (DynamicAtlas) this.getAtlas();\n    }\n\n    private Line subLine()\n    {\n        final Line result = dynamicAtlas().subLine(this.identifier);\n        if (result != null)\n        {\n            return result;\n        }\n        else\n        {\n            throw new CoreException(\"DynamicAtlas {} moved too fast! {} {} is missing now.\",\n                    dynamicAtlas().getName(), this.getClass().getSimpleName(), this.identifier);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicNode.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * @author matthieun\n */\npublic class DynamicNode extends Node\n{\n    private static final long serialVersionUID = 7046248083667389625L;\n\n    // Not index!\n    private final long identifier;\n\n    protected DynamicNode(final DynamicAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return subNode().getLocation();\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return subNode().getTags();\n    }\n\n    @Override\n    public SortedSet<Edge> inEdges()\n    {\n        return subNode().inEdges().stream()\n                .map(edge -> new DynamicEdge(dynamicAtlas(), edge.getIdentifier()))\n                .collect(Collectors.toCollection(TreeSet::new));\n    }\n\n    @Override\n    public SortedSet<Edge> outEdges()\n    {\n        return subNode().outEdges().stream()\n                .map(edge -> new DynamicEdge(dynamicAtlas(), edge.getIdentifier()))\n                .collect(Collectors.toCollection(TreeSet::new));\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return subNode().relations().stream()\n                .map(relation -> new DynamicRelation(dynamicAtlas(), relation.getIdentifier()))\n                .collect(Collectors.toSet());\n    }\n\n    private DynamicAtlas dynamicAtlas()\n    {\n        return (DynamicAtlas) this.getAtlas();\n    }\n\n    private Node subNode()\n    {\n        final Node result = dynamicAtlas().subNode(this.identifier);\n        if (result != null)\n        {\n            return result;\n        }\n        else\n        {\n            throw new CoreException(\"DynamicAtlas {} moved too fast! {} {} is missing now.\",\n                    dynamicAtlas().getName(), this.getClass().getSimpleName(), this.identifier);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicPoint.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * @author matthieun\n */\npublic class DynamicPoint extends Point\n{\n    private static final long serialVersionUID = 5290355290550015953L;\n\n    // Not index!\n    private final long identifier;\n\n    protected DynamicPoint(final DynamicAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return subPoint().getLocation();\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return subPoint().getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return subPoint().relations().stream()\n                .map(relation -> new DynamicRelation(dynamicAtlas(), relation.getIdentifier()))\n                .collect(Collectors.toSet());\n    }\n\n    private DynamicAtlas dynamicAtlas()\n    {\n        return (DynamicAtlas) this.getAtlas();\n    }\n\n    private Point subPoint()\n    {\n        final Point result = dynamicAtlas().subPoint(this.identifier);\n        if (result != null)\n        {\n            return result;\n        }\n        else\n        {\n            throw new CoreException(\"DynamicAtlas {} moved too fast! {} {} is missing now.\",\n                    dynamicAtlas().getName(), this.getClass().getSimpleName(), this.identifier);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicRelation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\n\n/**\n * @author matthieun\n */\npublic class DynamicRelation extends Relation\n{\n    private static final long serialVersionUID = 7994622214805021474L;\n\n    // Not index!\n    private final long identifier;\n\n    protected DynamicRelation(final DynamicAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public RelationMemberList allKnownOsmMembers()\n    {\n        return getRelationMembersAsDynamicEntities(subRelation().allKnownOsmMembers());\n    }\n\n    @Override\n    public List<Relation> allRelationsWithSameOsmIdentifier()\n    {\n        return subRelation().allRelationsWithSameOsmIdentifier().stream()\n                .map(relation -> new DynamicRelation(dynamicAtlas(), relation.getIdentifier()))\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public Optional<MultiPolygon> asMultiPolygon()\n    {\n        return subRelation().asMultiPolygon();\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return subRelation().getTags();\n    }\n\n    @Override\n    public RelationMemberList members()\n    {\n        return getRelationMembersAsDynamicEntities(subRelation().members());\n    }\n\n    @Override\n    public Long osmRelationIdentifier()\n    {\n        return subRelation().osmRelationIdentifier();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return subRelation().relations().stream()\n                .map(relation -> new DynamicRelation(dynamicAtlas(), relation.getIdentifier()))\n                .collect(Collectors.toSet());\n    }\n\n    private DynamicAtlas dynamicAtlas()\n    {\n        return (DynamicAtlas) this.getAtlas();\n    }\n\n    private RelationMemberList getRelationMembersAsDynamicEntities(\n            final RelationMemberList memberList)\n    {\n        final List<RelationMember> newMemberList = new ArrayList<>();\n\n        for (final RelationMember member : memberList)\n        {\n            final AtlasEntity entity = member.getEntity();\n            AtlasEntity dynamicEntity = null;\n            switch (entity.getType())\n            {\n                case NODE:\n                    dynamicEntity = new DynamicNode(dynamicAtlas(), entity.getIdentifier());\n                    break;\n                case EDGE:\n                    dynamicEntity = new DynamicEdge(dynamicAtlas(), entity.getIdentifier());\n                    break;\n                case POINT:\n                    dynamicEntity = new DynamicPoint(dynamicAtlas(), entity.getIdentifier());\n                    break;\n                case LINE:\n                    dynamicEntity = new DynamicLine(dynamicAtlas(), entity.getIdentifier());\n                    break;\n                case AREA:\n                    dynamicEntity = new DynamicArea(dynamicAtlas(), entity.getIdentifier());\n                    break;\n                case RELATION:\n                    dynamicEntity = new DynamicRelation(dynamicAtlas(), entity.getIdentifier());\n                    break;\n                default:\n                    throw new CoreException(\"Invalid entity type {}\", entity.getType());\n            }\n            newMemberList.add(new RelationMember(member.getRole(), dynamicEntity,\n                    member.getRelationIdentifier()));\n        }\n\n        return new RelationMemberList(newMemberList);\n    }\n\n    private Relation subRelation()\n    {\n        final Relation result = dynamicAtlas().subRelation(this.identifier);\n        if (result != null)\n        {\n            return result;\n        }\n        else\n        {\n            throw new CoreException(\"DynamicAtlas {} moved too fast! {} {} is missing now.\",\n                    dynamicAtlas().getName(), this.getClass().getSimpleName(), this.identifier);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/README.md",
    "content": "# `DynamicAtlas`\n\n## Locally complete map with shards\n\nWhen splitting the map in many shards for easier distributed processing, the complexity of the tasks working on those shards goes up. The data is local, and not global, there are boundary effects at the shard delimitations, and the data might look different. For example, a road might be in shard A, but its intersection with another road is not visible, as it is inside shard B. One process doing anything related to shard A cannot just load shard A, it would then have to load shard A and B. To alleviate those concerns, and make shard-based map processing easier and more intuitive, `DynamicAtlas` abstracts most of the shard resolution and stitching, to present a simple Atlas API that takes care of expansion, stitching and shard fetching under the cover.\n\n## Creation\n\nAll the user has to provide to create a `DynamicAtlas` is:\n- **Sharding Tree**: A `Sharding` tree, which contains the definition of how the world is split. The currently supported implementations are `SlippyTileSharding` (all shards at the same zoom level) and `DynamicTileSharding` which is a simple quad tree split of the world. Any other implementation of `Sharding` would work too.\n- **Starting Point**: The initial shard(s) of interest, or a `Polygon` covering the intial shard(s) of interest.\n- **Atlas Fetcher**: A function called \"fetcher\" that tells the `DynamicAtlas` how to find the Atlas shards it needs. This function is very generic, and takes a `Shard` in input and returns an `Optional<Atlas>`. The returned Atlas can either not exist (exhausted the map data), or come from memory, disk, remote storage... The user chooses.\n- **Expansion Policy**: An \"expansion policy\" that specifies how the `DynamicAtlas` needs to explore neighboring shards. Several options are described below.\n\n## Atlas Fetcher\n\nThe most simple use case is where the user has a local folder `myFolder` with all the Dominica (DMA) Atlas shards saved using `PackedAtlas`, and the file names are equal to the shard names. For example, a shard at zoom 8, x index 2345 and y index 4567 would correspond to a file called `myFolder/DMA_8-2345-4567.atlas`. Then a simple atlas fetcher function is as follows:\n\n```java\nFunction<Shard, Optional<Atlas>> fetcher = shard -> {\n    File folder = new File(\"myFolder\");\n    File atlasFile = folder.child(\"DMA_\" + shard.getName() + \".atlas\");\n    if (file.exists())\n    {\n        try\n        {\n            Atlas atlas = PackedAtlas.load(atlasFile);\n            return Optional.of(atlas);\n        }\n        catch(Exception e)\n        {\n            logger.info(\"Unable to load shard {}\", shard.getName(), e);\n        }\n    }\n    return Optional.empty();\n}\n```\n\nAs it is in the above example, the user providing the fetcher is also responsible for handling load failures, and missing or nonexistent resources.\n\n### Shard filtering\n\nAs the user controls the fetcher, it also controls the whole loading process from resource to Atlas object. Sometimes, it can be useful to also filter Atlas shards prior to providing them to the DynamicAtlas through the fetcher, when the user knows that only a subset of the map data will be useful.\n\nHere is the same example but with a user interested only in motorways:\n\n```java\nPredicate<AtlasEntity> filter = entity -> entity.getType()\n    HighwayTag.tag(entity).orElse(null) == HighwayTag.MOTORWAY;\n\nFunction<Shard, Optional<Atlas>> fetcher = shard -> {\n    File folder = new File(\"myFolder\");\n    File atlasFile = folder.child(\"DMA_\" + shard.getName() + \".atlas\");\n    if (file.exists())\n    {\n        try\n        {\n            Atlas atlas = PackedAtlas.load(atlasFile);\n            return atlas.subAtlas(filter);\n        }\n        catch(Exception e)\n        {\n            logger.info(\"Unable to load shard {}\", shard.getName(), e);\n        }\n    }\n    return Optional.empty();\n}\n```\n\n### Remote access and caching\n\nWhen loading shards from a remote location (network drive, object storage service like Azure Blob or S3) it is recommended to cache the Atlas files locally on disk to avoid un-necessary bandwidth usage.\n\n\nRemote without caching:\n\n```java\n// For the example, we assume this Hadoop FileSystem is already\n// initialized.\nFileSystem fileSystem;\nFunction<Shard, Optional<Atlas>> fetcher = shard -> {\n    String path = \"myStorage://myFolder/\"\n            + \"DMA_\" + shard.getName() + \".atlas\";\n    if (fileSystem.exists(path))\n    {\n        try\n        {\n            Resource atlasResource = new InputStreamResource(() ->\n                    fileSystem.open(path));\n            Atlas atlas = PackedAtlas.load(atlasResource);\n            return Optional.of(atlas);\n        }\n        catch(Exception e)\n        {\n            logger.info(\"Unable to load shard {}\", shard.getName(), e);\n        }\n    }\n    return Optional.empty();\n}\n```\n\nHere is the same example with caching enabled (Uses [`HadoopAtlasFileCache`](https://github.com/osmlab/atlas-generator/blob/4.0.9/src/main/java/org/openstreetmap/atlas/generator/tools/caching/HadoopAtlasFileCache.java) from the [atlas-generator](https://github.com/osmlab/atlas-generator) project):\n\n```java\n// Here are the Hadoop configuration strings, that can contain the\n// credentials to connect to the remote storage.\nMap<String, String> hadoopConfiguration;\nHadoopAtlasFileCache cache =\n        new HadoopAtlasFileCache(\"myStorage://myFolder/\",\n            hadoopConfiguration);\nFunction<Shard, Optional<Atlas>> fetcher = shard -> {\n    Optional<Resource> atlasResource = cache.get(\"DMA\", shard);\n    if (atlasResource.isPresent())\n    {\n        try\n        {\n            Atlas atlas = PackedAtlas.load(atlasResource.get());\n            return Optional.of(atlas);\n        }\n        catch(Exception e)\n        {\n            logger.info(\"Unable to load shard {}\", shard.getName(), e);\n        }\n    }\n    return Optional.empty();\n}\n```\n\n## Expansion Policy\n\nThe expansion policy ([`DynamicAtlasPolicy`](policy/DynamicAtlasPolicy.java)) determines the behavior of the expansion steps of the `DynamicAtlas`, and can have vast impact on performance and ease of use. It also contains the sharding tree, initial shard(s) and fetcher function.\n\n### Expansion\n\nAs the user queries features from the `DynamicAtlas`, the `DynamicAtlas` intercepts the calls and checks if that feature intersects the initial shard boundaries. If it does, it blocks the call, removes the current stitched set of shards, loads the needed shards using the fetcher, and re-stitches (using `MultiAtlas`). It then returns the proper feature with all the data around it properly loaded.\n\n#### Indefinite Expansion\n\nBy default, the policy is set for indefinite expansion. That means that as long as features intersect loaded shards boundaries, they will trigger a new MultiAtlas load. This is good for debugging and testing, but most production cases need to cap extension.\n\n#### Finite Expansion\n\nFinite expansion is toggled with `DynamicAtlasPolicy.withExtendIndefinitely(false);`. Once this is set, the features that will trigger a new shard load and MultiAtlas load are only the ones that intersect the initial shard(s) of interest, or the initial `Polygon`. That way, all the features intersecting the initial area of interest will always have a locally complete map around them, but the expansion will be capped.\n\n#### Indiscriminate Expansion\n\nBy default, the expansion can be triggered by any feature requested that falls outside of the shard boundaries.\n\n#### Discriminate Expansion\n\nDiscriminate expansion can be enabled by using a `DynamicAtlasPolicy` that contains `final Predicate<AtlasEntity> atlasEntitiesToConsiderForExpansion`. When this is provided, every time the `DynamicAtlas` needs to check if a feature needs to trigger a shard expansion, it will do so only if that feature passes the `atlasEntitiesToConsiderForExpansion` predicate. For example, this is useful when a user is interested only in a certain type of roads, and expanding based on parks or lakes does not provide any more context.\n\n### Deferred and Preemptive Loading\n\nWhen _finite expansion is enabled_ (or indefinite expansion disabled), and deferred loading is true (`DynamicAtlasPolicy.withDeferredLocaing(true);`), there is an option to toggle preemptive loading: `DynamicAtlas.preemptiveLoad();`. No shard load will be triggered until `preemptiveLoad` is called.\n\nThis will ensure that all the shards needed will be pre-loaded once and for all. It also disables the \"interception\" of queries for features by the user, as the `DynamicAtlas` is already sure everything is properly loaded. This is the same as having an \"intelligent\" MultiAtlas that knows what shards the user needs based on each area of interest.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/policy/DynamicAtlasPolicy.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic.policy;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.DynamicAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasPolicy\n{\n    private static final Logger logger = LoggerFactory.getLogger(DynamicAtlasPolicy.class);\n\n    private final Polygon maximumBounds;\n    private final Sharding sharding;\n    private final Function<Shard, Optional<Atlas>> atlasFetcher;\n    private final Set<Shard> initialShards;\n    private boolean extendIndefinitely = true;\n    private boolean deferLoading = false;\n    private Consumer<Set<Shard>> shardSetChecker = set ->\n    {\n    };\n    private Predicate<AtlasEntity> atlasEntitiesToConsiderForExpansion = entity -> true;\n    private boolean aggressivelyExploreRelations = false;\n    // In case the initial shards were found using a Polygon or a MultiPolygon, remember it to\n    // provide the initial shards shape. This will be useful to not over-extend when using\n    // extendIndefinitely=false\n    private Optional<MultiPolygon> shapeCoveringInitialShards = Optional.empty();\n\n    public DynamicAtlasPolicy(final Function<Shard, Optional<Atlas>> atlasFetcher,\n            final Sharding sharding, final MultiPolygon shapeCoveringInitialShards,\n            final Polygon maximumBounds)\n    {\n        this.initialShards = new HashSet<>();\n        this.shapeCoveringInitialShards = Optional.of(shapeCoveringInitialShards);\n        sharding.shards(shapeCoveringInitialShards).forEach(this.initialShards::add);\n        this.atlasFetcher = atlasFetcher;\n        this.maximumBounds = maximumBounds;\n        this.sharding = sharding;\n    }\n\n    public DynamicAtlasPolicy(final Function<Shard, Optional<Atlas>> atlasFetcher,\n            final Sharding sharding, final Polygon shapeCoveringInitialShards,\n            final Polygon maximumBounds)\n    {\n        this.initialShards = new HashSet<>();\n        this.shapeCoveringInitialShards = Optional\n                .of(MultiPolygon.forPolygon(shapeCoveringInitialShards));\n        sharding.shards(shapeCoveringInitialShards).forEach(this.initialShards::add);\n        this.atlasFetcher = atlasFetcher;\n        this.maximumBounds = maximumBounds;\n        this.sharding = sharding;\n    }\n\n    public DynamicAtlasPolicy(final Function<Shard, Optional<Atlas>> atlasFetcher,\n            final Sharding sharding, final Set<Shard> initialShards, final Polygon maximumBounds)\n    {\n        this.initialShards = initialShards;\n        this.atlasFetcher = atlasFetcher;\n        this.maximumBounds = maximumBounds;\n        this.sharding = sharding;\n    }\n\n    public DynamicAtlasPolicy(final Function<Shard, Optional<Atlas>> atlasFetcher,\n            final Sharding sharding, final Shard initialShard, final Polygon maximumBounds)\n    {\n        this.initialShards = new HashSet<>();\n        this.initialShards.add(initialShard);\n        this.atlasFetcher = atlasFetcher;\n        this.maximumBounds = maximumBounds;\n        this.sharding = sharding;\n    }\n\n    public Predicate<AtlasEntity> getAtlasEntitiesToConsiderForExpansion()\n    {\n        return this.atlasEntitiesToConsiderForExpansion;\n    }\n\n    public Function<Shard, Optional<Atlas>> getAtlasFetcher()\n    {\n        // Here, make sure to not load outside the bounds.\n        return shard ->\n        {\n            if (this.maximumBounds.overlaps(shard.bounds()))\n            {\n                return this.atlasFetcher.apply(shard);\n            }\n            else\n            {\n                logger.debug(\n                        \"Skipping atlasFetcher for {} because shard bounds are outside the policy's maximumBounds\",\n                        shard.getName());\n            }\n\n            return Optional.empty();\n        };\n    }\n\n    public Set<Shard> getInitialShards()\n    {\n        return this.initialShards;\n    }\n\n    public MultiPolygon getInitialShardsBounds()\n    {\n        if (this.shapeCoveringInitialShards.isPresent())\n        {\n            return this.shapeCoveringInitialShards.get();\n        }\n        final MultiMap<Polygon, Polygon> outerToInners = new MultiMap<>();\n        this.initialShards.forEach(shard -> outerToInners.put(shard.bounds(), new ArrayList<>()));\n        this.shapeCoveringInitialShards = Optional.of(new MultiPolygon(outerToInners));\n        return this.shapeCoveringInitialShards.get();\n    }\n\n    public Polygon getMaximumBounds()\n    {\n        return this.maximumBounds;\n    }\n\n    public Consumer<Set<Shard>> getShardSetChecker()\n    {\n        return this.shardSetChecker;\n    }\n\n    public Sharding getSharding()\n    {\n        return this.sharding;\n    }\n\n    public boolean isAggressivelyExploreRelations()\n    {\n        return this.aggressivelyExploreRelations;\n    }\n\n    public boolean isDeferLoading()\n    {\n        return this.deferLoading;\n    }\n\n    public boolean isExtendIndefinitely()\n    {\n        return this.extendIndefinitely;\n    }\n\n    /**\n     * This switch tells the {@link DynamicAtlas} to preemptively and temporarily load the\n     * neighboring shards to see if they contain the relation in the current shard and if the member\n     * list is different. In which case it expands to the neighboring shards that are including\n     * those members.\n     *\n     * @param aggressivelyExploreRelations\n     *            True to aggressively explore relations\n     * @return The modified policy\n     */\n    public DynamicAtlasPolicy withAggressivelyExploreRelations(\n            final boolean aggressivelyExploreRelations)\n    {\n        this.aggressivelyExploreRelations = aggressivelyExploreRelations;\n        return this;\n    }\n\n    /**\n     * @param atlasEntitiesToConsiderForExpansion\n     *            A predicate that defines what entities will be considered when deciding to expand\n     *            or not to a neighboring shard.\n     * @return The modified policy\n     */\n    public DynamicAtlasPolicy withAtlasEntitiesToConsiderForExpansion(\n            final Predicate<AtlasEntity> atlasEntitiesToConsiderForExpansion)\n    {\n        this.atlasEntitiesToConsiderForExpansion = atlasEntitiesToConsiderForExpansion;\n        return this;\n    }\n\n    /**\n     * Defer loading until the load command is sent.\n     *\n     * @param deferLoading\n     *            True to defer loading until the load command is sent.\n     * @return The modified policy\n     */\n    public DynamicAtlasPolicy withDeferredLoading(final boolean deferLoading)\n    {\n        this.deferLoading = deferLoading;\n        return this;\n    }\n\n    /**\n     * The extension policy: if this is set to true, then the loading of shards will go as far as it\n     * can within the boundary polygon. If this is set to false, then the loading will extend only\n     * if the feature warranting the extension is included within or intersects the initial shard\n     * boundaries.\n     *\n     * @param extendIndefinitely\n     *            True to extend indefinitely.\n     * @return The modified policy\n     */\n    public DynamicAtlasPolicy withExtendIndefinitely(final boolean extendIndefinitely)\n    {\n        this.extendIndefinitely = extendIndefinitely;\n        return this;\n    }\n\n    /**\n     * @param shardSetChecker\n     *            A function that will inspect the shards prior to loading them in a MultiAtlas. The\n     *            default version just does nothing, but this could for example throw an exception\n     *            if the DynamicAtlas is loading too many neighboring shards, or send shard\n     *            downloading statistics. This is up to the end user.\n     * @return The modified policy\n     */\n    public DynamicAtlasPolicy withShardSetChecker(final Consumer<Set<Shard>> shardSetChecker)\n    {\n        this.shardSetChecker = shardSetChecker;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/dynamic/policy/DynamicAtlasResourcePolicy.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic.policy;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * Policy that uses {@link Resource}s and takes care of converting them to {@link Atlas}\n *\n * @author matthieun\n */\npublic class DynamicAtlasResourcePolicy extends DynamicAtlasPolicy\n{\n    public DynamicAtlasResourcePolicy(final Function<Shard, Optional<Resource>> atlasFetcher,\n            final Sharding sharding, final Shard initialShard, final Polygon maximumBounds)\n    {\n        super(shard ->\n        {\n            final Optional<Resource> resourceOption = atlasFetcher.apply(shard);\n            if (resourceOption.isPresent())\n            {\n                return Optional.ofNullable(new AtlasResourceLoader().load(resourceOption.get()));\n            }\n            else\n            {\n                return Optional.empty();\n            }\n        }, sharding, initialShard, maximumBounds);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/exception/AtlasIntegrityException.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.exception;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic class AtlasIntegrityException extends CoreException\n{\n    private static final long serialVersionUID = -2780280960455310936L;\n\n    public AtlasIntegrityException(final String message)\n    {\n        super(message);\n    }\n\n    public AtlasIntegrityException(final String message, final Object... arguments)\n    {\n        super(message, arguments);\n    }\n\n    public AtlasIntegrityException(final String message, final Throwable cause)\n    {\n        super(message, cause);\n    }\n\n    public AtlasIntegrityException(final String message, final Throwable cause,\n            final Object... arguments)\n    {\n        super(message, cause, arguments);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/geojson/AtlasGeoJsonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.geojson;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasLoadingCommand;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Utility to save an Atlas as GeoJson\n *\n * @author matthieun\n */\npublic class AtlasGeoJsonConverter extends AtlasLoadingCommand\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasGeoJsonConverter.class);\n\n    private static final Switch<File> GEOJSON = new Switch<>(\"geojson\",\n            \"The file where to save as GeoJson\", File::new, Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new AtlasGeoJsonConverter().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Atlas atlas = loadAtlas(command);\n        final File output = (File) command.get(GEOJSON);\n        atlas.saveAsGeoJson(output);\n        logger.info(\"Saved to {}\", output);\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return super.switches().with(GEOJSON);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/geojson/LineDelimitedGeoJsonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.geojson;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.FilenameUtils;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.openstreetmap.atlas.utilities.vectortiles.TippecanoeCommands;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonObject;\n\n/**\n * This CLI takes a directory of atlas files and turns them into line-delimited GeoJSON. If you\n * would also like to convert into MBTiles with tippecanoe, use TippecanoeExporter.\n *\n * @author hallahan\n */\npublic class LineDelimitedGeoJsonConverter extends Command\n{\n    /**\n     * After all of your files are converted to LD GeoJSON, it is then concatenated into\n     * EVERYTHING.geojson\n     */\n    public static final String EVERYTHING = \"EVERYTHING.geojson\";\n    public static final int EXIT_FAILURE = 1;\n    protected static final Switch<Path> GEOJSON_DIRECTORY = new Switch<>(\"geojsonDirectory\",\n            \"The directory to write line-delimited GeoJSON.\", Paths::get, Optionality.REQUIRED);\n    protected static final Switch<Boolean> OVERWRITE = new Switch<>(\"overwrite\",\n            \"Choose to automatically overwrite a GeoJSON file if it exists at the given path.\",\n            Boolean::parseBoolean, Optionality.OPTIONAL, \"false\");\n    // Works great on a MacBook Pro (Retina, 15-inch, Mid 2015)\n    private static final int DEFAULT_THREADS = 8;\n    private static final Logger logger = LoggerFactory\n            .getLogger(LineDelimitedGeoJsonConverter.class);\n    private static final AtlasResourceLoader ATLAS_RESOURCE_LOADER = new AtlasResourceLoader();\n    private static final Switch<Path> ATLAS_DIRECTORY = new Switch<>(\"atlasDirectory\",\n            \"The directory of atlases to convert.\", Paths::get, Optionality.REQUIRED);\n    private static final Switch<Integer> THREADS = new Switch<>(\"threads\",\n            \"The number of threads to work on processing atlas shards.\", Integer::valueOf,\n            Optionality.OPTIONAL, String.valueOf(DEFAULT_THREADS));\n\n    /**\n     * We only want positive (main) edges, because the negative edge can be derived at the\n     * application level, and this encodes extraneous data that can be easily derived by the map\n     * viewer. For relations, we only want multipolygon relations, as the rest can be derived from\n     * their members.\n     */\n    private static final Predicate<AtlasEntity> ENTITY_PREDICATE = entity ->\n    {\n        // We only want positive atlas entities. No negative ids.\n        if (ItemType.EDGE.equals(entity.getType()))\n        {\n            final Edge edge = (Edge) entity;\n            if (!edge.isMainEdge())\n            {\n                return false;\n            }\n        }\n\n        // Because we're writing the multipolygon relations, we don't want to also write the area\n        // components that are pieces of the multipolygon relation.\n        if (ItemType.AREA.equals(entity.getType()))\n        {\n            final Set<Relation> relations = entity.relations();\n            if (!relations.isEmpty())\n            {\n                return relations.stream().noneMatch(relation -> Validators.isOfType(relation,\n                        RelationTypeTag.class, RelationTypeTag.MULTIPOLYGON));\n            }\n        }\n\n        return true;\n    };\n\n    /**\n     * If we are rendering vector tiles, we may want to examine various tags of a given atlas entity\n     * and make decisions for the layer name, min zoom, and max zoom for the feature. Depending on\n     * your vector tile renderer, as well as map data visualization needs, you can override this\n     * BiConsumer to mutate your JSON object as you see fit.\n     */\n    private BiConsumer<AtlasEntity, JsonObject> jsonMutator = (atlasEntity, feature) ->\n    {\n    };\n\n    public static void main(final String[] args)\n    {\n        new LineDelimitedGeoJsonConverter().run(args);\n    }\n\n    private static List<File> fetchAtlasFilesInDirectory(final Path directory)\n    {\n        return new File(directory.toFile()).listFilesRecursively().stream()\n                .filter(AtlasResourceLoader.HAS_ATLAS_EXTENSION).collect(Collectors.toList());\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Time time = Time.now();\n        final Path atlasDirectory = (Path) command.get(ATLAS_DIRECTORY);\n        final Path geojsonDirectory = (Path) command.get(GEOJSON_DIRECTORY);\n        final Boolean overwrite = (Boolean) command.get(OVERWRITE);\n        final int threads = (Integer) command.get(THREADS);\n\n        if (overwrite)\n        {\n            try\n            {\n                FileUtils.deleteDirectory(geojsonDirectory.toFile());\n            }\n            catch (final IOException noDelete)\n            {\n                logger.warn(\n                        \"Tried to delete GeoJSON output directory {} for overwrite, but unable.\",\n                        geojsonDirectory, noDelete);\n            }\n        }\n\n        final List<File> atlases = fetchAtlasFilesInDirectory(atlasDirectory);\n\n        if (atlases.isEmpty())\n        {\n            logger.error(\"There are no atlas files in {}. Exiting...\", atlasDirectory);\n            System.exit(EXIT_FAILURE);\n        }\n\n        logger.info(\"About to convert {} atlas shards into line-delimited GeoJSON...\",\n                atlases.size());\n\n        // Execute in a pool of threads so we limit how many atlases get loaded in parallel.\n        try (Pool pool = new Pool(threads, \"atlas-converter-worker\"))\n        {\n            atlases.forEach(\n                    atlasFile -> pool.queue(() -> this.convertAtlas(atlasFile, geojsonDirectory)));\n        }\n\n        TippecanoeCommands.concatenate(geojsonDirectory);\n\n        logger.info(\n                \"Finished converting directory of atlas shards into line-delimited GeoJSON in {}!\",\n                time.elapsedSince());\n\n        return 0;\n    }\n\n    protected void setJsonMutator(final BiConsumer<AtlasEntity, JsonObject> jsonMutator)\n    {\n        this.jsonMutator = jsonMutator;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(ATLAS_DIRECTORY, GEOJSON_DIRECTORY, OVERWRITE, THREADS);\n    }\n\n    private void convertAtlas(final File atlasFile, final Path geojsonDirectory)\n    {\n        final Time time = Time.now();\n        final Atlas atlas = ATLAS_RESOURCE_LOADER.load(atlasFile);\n        final String name = FilenameUtils.removeExtension(atlasFile.getName())\n                + FileSuffix.GEO_JSON.toString();\n        final File geojsonFile = new File(geojsonDirectory.resolve(name).toFile());\n        atlas.saveAsLineDelimitedGeoJsonFeatures(geojsonFile, ENTITY_PREDICATE, this.jsonMutator);\n        logger.info(\"Saved {} in {}.\", name, time.elapsedSince());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/inspection/EntityClassifier.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.inspection;\n\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * This interface provides default implementations for common types of entity classification. It's\n * unlikely that implementations of this interface would need to override the default behaviour,\n * though still an obvious option.\n *\n * @author brian_l_davis\n */\npublic interface EntityClassifier\n{\n    /**\n     * Checks if the feature is an edge\n     *\n     * @param entity\n     *            The entity to check in\n     * @return True if it's a edge\n     */\n    default boolean isEdge(final AtlasEntity entity)\n    {\n        return entity instanceof Edge;\n    }\n\n    /**\n     * Checks if the feature is a line\n     *\n     * @param entity\n     *            The entity to check in\n     * @return True if it's a line\n     */\n    default boolean isLine(final AtlasEntity entity)\n    {\n        return entity instanceof Line;\n    }\n\n    /**\n     * Checks if the feature is a road\n     *\n     * @param entity\n     *            The entity to check in\n     * @return True if it's a road\n     */\n    default boolean isRoad(final AtlasEntity entity)\n    {\n        return isEdge(entity) || isLine(entity) && Validators.isOfType(entity, HighwayTag.class,\n                HighwayTag.PROPOSED, HighwayTag.CONSTRUCTION);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/Area.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Area from an {@link Atlas}\n *\n * @author matthieun\n */\npublic abstract class Area extends AtlasItem\n{\n    private static final long serialVersionUID = 5244165133018408045L;\n\n    protected Area(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        return asPolygon().asGeoJsonGeometry();\n    }\n\n    /**\n     * @return The {@link PolyLine} that represents this {@link LineItem}\n     */\n    public abstract Polygon asPolygon();\n\n    @Override\n    public Rectangle bounds()\n    {\n        return asPolygon().bounds();\n    }\n\n    /**\n     * @return The closed {@link Polygon}, with the end {@link Location} equal to the start\n     *         {@link Location}.\n     */\n    public Polygon getClosedGeometry()\n    {\n        return new Polygon(asPolygon().closedLoop());\n    }\n\n    /**\n     * @return The underlying {@link PolyLine}, which will not have the end {@link Location} equal\n     *         to the start {@link Location}. i.e. the area will not be closed.\n     */\n    @Override\n    public Iterable<Location> getRawGeometry()\n    {\n        return asPolygon();\n    }\n\n    @Override\n    public ItemType getType()\n    {\n        return ItemType.AREA;\n    }\n\n    @Override\n    public boolean intersects(final GeometricSurface surface)\n    {\n        return surface.overlaps(asPolygon());\n    }\n\n    @Override\n    public String toDiffViewFriendlyString()\n    {\n        final String relationsString = this.parentRelationsAsDiffViewFriendlyString();\n\n        return \"[Area: id=\" + this.getIdentifier() + \", polygon=\" + this.asPolygon()\n                + \", relations=(\" + relationsString + \"), \" + tagString() + \"]\";\n    }\n\n    @Override\n    public LocationIterableProperties toGeoJsonBuildingBlock()\n    {\n        final Map<String, String> tags = getTags();\n        tags.put(\"identifier\", String.valueOf(getIdentifier()));\n        tags.put(\"osmIdentifier\", String.valueOf(getOsmIdentifier()));\n        tags.put(\"itemType\", String.valueOf(getType()));\n\n        final Optional<String> shardName = getAtlas().metaData().getShardName();\n        shardName.ifPresent(shard -> tags.put(\"shard\", shard));\n\n        final StringList parentRelations = new StringList();\n        this.relations().forEach(relation ->\n        {\n            final RelationMember member = relation.members().get(getIdentifier(), getType());\n            parentRelations.add(member.getRelationIdentifier() + \"-\" + member.getRole());\n        });\n\n        if (!parentRelations.isEmpty())\n        {\n            tags.put(\"parentRelations\", parentRelations.join(\", \"));\n        }\n\n        return new GeoJsonBuilder.LocationIterableProperties(getClosedGeometry(), tags);\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[Area: id=\" + this.getIdentifier() + \", polygon=\" + this.asPolygon() + \", \"\n                + tagString() + \"]\";\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return this.asPolygon().toWkb();\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return this.asPolygon().toWkt();\n    }\n\n    @Override\n    public boolean within(final GeometricSurface surface)\n    {\n        return this.asPolygon().within(surface);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/AtlasEntity.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.GeometryPrintable;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.ReverseIdentifierFactory;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonFeature;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.tags.LastEditTimeTag;\nimport org.openstreetmap.atlas.tags.LastEditUserIdentifierTag;\nimport org.openstreetmap.atlas.tags.LastEditUserNameTag;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.time.Time;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * A located entity with tags\n *\n * @author matthieun\n * @author mgostintsev\n * @author Sid\n * @author hallahan\n */\npublic abstract class AtlasEntity\n        implements AtlasObject, DiffViewFriendlyItem, GeometryPrintable, GeoJsonFeature\n{\n    private static final long serialVersionUID = -6072525057489468736L;\n\n    // The atlas this item belongs to\n    private final Atlas atlas;\n\n    protected AtlasEntity(final Atlas atlas)\n    {\n        this.atlas = atlas;\n    }\n\n    /**\n     * Utility function to test if an entity's tags match some given tag keys.\n     *\n     * @param matches\n     *            The given tag keys to match\n     * @return True if at least one tag of the entity matches one of the given keys\n     */\n    public boolean containsKey(final Iterable<String> matches)\n    {\n        final Map<String, String> tags = this.getTags();\n        for (final String candidate : matches)\n        {\n            if (tags.containsKey(candidate))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Utility function to test if an entity's tags start with some given tag keys.\n     *\n     * @param matches\n     *            The given tag keys to match\n     * @return True if at least one tag of the entity starts with one of the given keys\n     */\n    public boolean containsKeyStartsWith(final Iterable<String> matches)\n    {\n        final Map<String, String> tags = this.getTags();\n        for (final String candidate : matches)\n        {\n            for (final String key : tags.keySet())\n            {\n                if (key.startsWith(candidate))\n                {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * For comparison, we require the AtlasEntities to be from the same instance of Atlas. Edges\n     * with same attributes from two instances of Atlas (with same data) are considered different\n     */\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other != null && this.getClass() == other.getClass())\n        {\n            final AtlasEntity that = (AtlasEntity) other;\n            // Do not call atlas.equals() which would browse all the items and create a stack\n            // overflow\n            return this.getAtlas() == that.getAtlas()\n                    && this.getIdentifier() == that.getIdentifier();\n        }\n        return false;\n    }\n\n    @Override\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    /**\n     * A method that creates properties for a GeoJSON Feature from the tags.\n     *\n     * @return A GeoJSON properties object that is to be put in a Feature.\n     */\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        final JsonObject properties = new JsonObject();\n        getTags().forEach(properties::addProperty);\n        properties.addProperty(GeoJsonUtils.IDENTIFIER, getIdentifier());\n        properties.addProperty(GeoJsonUtils.OSM_IDENTIFIER, getOsmIdentifier());\n        properties.addProperty(GeoJsonUtils.ITEM_TYPE, String.valueOf(getType()));\n\n        final Set<Relation> relations = relations();\n        if (!relations.isEmpty())\n        {\n            final JsonArray relationsArray = new JsonArray();\n            properties.add(\"relations\", relationsArray);\n            for (final Relation relation : relations)\n            {\n                relationsArray.add(new JsonPrimitive(relation.getIdentifier()));\n            }\n        }\n\n        return properties;\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.FEATURE;\n    }\n\n    /**\n     * The value in the \"name\" attribute.\n     *\n     * @return an optional string representing the value of the name tag.\n     */\n    public Optional<String> getName()\n    {\n        return this.getTag(NameTag.KEY);\n    }\n\n    @Override\n    public long getOsmIdentifier()\n    {\n        return new ReverseIdentifierFactory().getOsmIdentifier(this.getIdentifier());\n    }\n\n    @Override\n    public Optional<String> getTag(final String key)\n    {\n        return Optional.ofNullable(getTags().get(key));\n    }\n\n    public abstract ItemType getType();\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(getIdentifier()).append(getClass()).hashCode();\n    }\n\n    /**\n     * Return true if the entity intersects the geometricSurface. If it is a {@link LocationItem},\n     * the polygon fully encloses it. For a {@link LineItem} the geometricSurface overlaps it. For\n     * an {@link Area} the geometricSurface overlaps it. For a relation, at least one member of the\n     * relation returns true to this method.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to test\n     * @return True if it intersects\n     */\n    public abstract boolean intersects(GeometricSurface surface);\n\n    /**\n     * @return If available, the {@link Time} at which the entity was last edited.\n     */\n    public Optional<Time> lastEdit()\n    {\n        final String tag = this.tag(LastEditTimeTag.KEY);\n        if (tag == null)\n        {\n            return Optional.empty();\n        }\n        return Optional.of(new Time(Duration.milliseconds(Long.valueOf(tag))));\n    }\n\n    /**\n     * @return If available, the identifier of the last user.\n     */\n    public Optional<String> lastUserIdentifier()\n    {\n        final String tag = this.tag(LastEditUserIdentifierTag.KEY);\n        if (tag == null)\n        {\n            return Optional.empty();\n        }\n        return Optional.of(tag);\n    }\n\n    /**\n     * @return If available, the name of the last user.\n     */\n    public Optional<String> lastUserName()\n    {\n        final String tag = this.tag(LastEditUserNameTag.KEY);\n        if (tag == null)\n        {\n            return Optional.empty();\n        }\n        return Optional.of(tag);\n    }\n\n    /**\n     * @return All the relations this {@link AtlasEntity} is member of.\n     */\n    public abstract Set<Relation> relations();\n\n    @Override\n    public String toDiffViewFriendlyString()\n    {\n        throw new UnsupportedOperationException(\n                \"This operation is not supported for type \" + this.getClass().getName());\n    }\n\n    /**\n     * @return The {@link LocationIterableProperties} for this {@link AtlasEntity}\n     */\n    public abstract LocationIterableProperties toGeoJsonBuildingBlock();\n\n    public JsonObject toJson()\n    {\n        final JsonObject object = new JsonObject();\n        object.addProperty(\"identifier\", this.getIdentifier());\n        object.addProperty(\"type\", this.getType().toString());\n        final String geometry = \"geometry\";\n        if (this instanceof LocationItem)\n        {\n            final LocationItem thisItem = (LocationItem) this;\n            object.addProperty(geometry, thisItem.getLocation().toWkt());\n        }\n        else if (this instanceof LineItem)\n        {\n            final LineItem thisItem = (LineItem) this;\n            object.addProperty(geometry, thisItem.asPolyLine().toWkt());\n        }\n        else if (this instanceof Area)\n        {\n            final Area thisItem = (Area) this;\n            object.addProperty(geometry, thisItem.asPolygon().toWkt());\n        }\n        else if (this instanceof Relation)\n        {\n            // TODO handle relation geometry for certain cases? // NOSONAR\n            object.addProperty(geometry, \"null\");\n        }\n\n        final JsonObject tagsObject = new JsonObject();\n        for (final String tagKey : new TreeSet<>(this.getTags().keySet()))\n        {\n            tagsObject.addProperty(tagKey, this.getTags().get(tagKey));\n        }\n        object.add(\"tags\", tagsObject);\n\n        final JsonArray parentRelationsArray = new JsonArray();\n        for (final Long parentRelationId : new TreeSet<>(\n                this.relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet())))\n        {\n            parentRelationsArray.add(new JsonPrimitive(parentRelationId));\n        }\n        object.add(\"parentRelations\", parentRelationsArray);\n\n        object.addProperty(\"bounds\", this.bounds().toString());\n        return object;\n    }\n\n    protected String tagString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        final Map<String, String> tags = getTags();\n        int index = 0;\n        builder.append(\"[Tags: \");\n        if (tags != null)\n        {\n            for (final String key : tags.keySet())\n            {\n                final String value = tags.get(key);\n                builder.append(\"[\");\n                builder.append(key);\n                builder.append(\" => \");\n                builder.append(value);\n                builder.append(\"]\");\n                if (index < tags.size() - 1)\n                {\n                    builder.append(\", \");\n                }\n                index++;\n            }\n        }\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    String parentRelationsAsDiffViewFriendlyString()\n    {\n        final StringList relationIds = new StringList();\n        if (this.relations() != null)\n        {\n            for (final Relation relation : this.relations())\n            {\n                relationIds.add(relation.getIdentifier());\n            }\n        }\n        return relationIds.join(\",\");\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/AtlasItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.Iterator;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * A flyweight item in an Atlas.\n *\n * @author matthieun\n */\npublic abstract class AtlasItem extends AtlasEntity implements Iterable<Location>\n{\n    private static final long serialVersionUID = -2907538413224236973L;\n\n    protected AtlasItem(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    /**\n     * @return The raw geometry, as {@link Location}, {@link PolyLine} or {@link Polygon}.\n     */\n    public abstract Iterable<Location> getRawGeometry();\n\n    @Override\n    public Iterator<Location> iterator()\n    {\n        return getRawGeometry().iterator();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/AtlasObject.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.tags.AtlasTag;\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * Very loose interface for something that lives off an {@link Atlas}\n *\n * @author matthieun\n */\npublic interface AtlasObject extends Located, Taggable, Serializable\n{\n    /**\n     * @return The {@link Atlas} this object is attached to\n     */\n    Atlas getAtlas();\n\n    /**\n     * @return This object's identifier\n     */\n    long getIdentifier();\n\n    /**\n     * Get this item's OSM identifier\n     *\n     * @return This item's OSM identifier\n     */\n    long getOsmIdentifier();\n\n    /**\n     * Atlas objects contain OSM tags plus tags inserted during Atlas generation. This function will\n     * remove all but the OSM tags.\n     *\n     * @return All the OSM tags for the object\n     */\n    default Map<String, String> getOsmTags()\n    {\n        return this.getTags(tag -> !AtlasTag.TAGS_FROM_OSM.contains(tag)\n                && !AtlasTag.TAGS_FROM_ATLAS.contains(tag));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/ConnectedEdgeType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.SortedSet;\nimport java.util.function.Function;\n\n/**\n * {@link Node}s have in and out edges, this enum enables generalizing logic around this node and\n * edge connection.\n *\n * @author Yazad Khambata\n */\npublic enum ConnectedEdgeType implements ConnectedEntityType<Node, SortedSet<Edge>>\n{\n    IN(\"inEdges\", Node::inEdges),\n\n    OUT(\"outEdges\", Node::outEdges);\n\n    private final String propertyName;\n\n    private final Function<Node, SortedSet<Edge>> accessFunction;\n\n    ConnectedEdgeType(final String propertyName,\n            final Function<Node, SortedSet<Edge>> accessFunction)\n    {\n        this.propertyName = propertyName;\n        this.accessFunction = accessFunction;\n    }\n\n    @Override\n    public Function<Node, SortedSet<Edge>> getAccessFunction()\n    {\n        return this.accessFunction;\n    }\n\n    @Override\n    public String getPropertyName()\n    {\n        return this.propertyName;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/ConnectedEntityType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.function.Function;\n\n/**\n * An interface that generalizes enum based declarative connectivity information.\n *\n * @param <M>\n *            - The current entity.\n * @param <C>\n *            - The connected entity.\n * @author Yazad Khambata\n */\npublic interface ConnectedEntityType<M extends AtlasEntity, C>\n{\n    Function<M, C> getAccessFunction();\n\n    String getPropertyName();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/ConnectedNodeType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.function.Function;\n\n/**\n * @author Yazad Khambata\n */\npublic enum ConnectedNodeType implements ConnectedEntityType<Edge, Node>\n{\n    START(\"startNode\", Edge::start),\n\n    END(\"endNode\", Edge::end);\n\n    private final String propertyName;\n\n    private final Function<Edge, Node> accessFunction;\n\n    ConnectedNodeType(final String propertyName, final Function<Edge, Node> accessFunction)\n    {\n        this.propertyName = propertyName;\n        this.accessFunction = accessFunction;\n    }\n\n    @Override\n    public Function<Edge, Node> getAccessFunction()\n    {\n        return this.accessFunction;\n    }\n\n    @Override\n    public String getPropertyName()\n    {\n        return this.propertyName;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/DiffViewFriendlyItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\n/**\n * Any feature that adheres to this interface promises to provide a method that returns a\n * representation of itself suitable for diff viewing. Diff view friendly strings should break\n * individual logical units onto separate lines. They should also avoid use of tab characters, since\n * the tab context in which the string will be displayed is generally unknown until runtime.\n *\n * @author lcram\n */\npublic interface DiffViewFriendlyItem\n{\n    String toDiffViewFriendlyString();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/DirectionalizedEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.tags.MaxSpeedBackwardTag;\nimport org.openstreetmap.atlas.tags.MaxSpeedForwardTag;\nimport org.openstreetmap.atlas.tags.MaxSpeedTag;\n\n/**\n * A {@link DirectionalizedEdge} is an {@link Edge} but with the tags interpreted with this\n * {@link Edge}'s direction. For example, if this {@link Edge} is backwards from its OSM way, and\n * the way has a maxspeed:backward tag, here it will be translated into a maxspeed tag. Also the\n * maxspeed:forward tag will be filtered out (it will be used by the reverse edge).\n *\n * @author matthieun\n */\npublic class DirectionalizedEdge extends Edge\n{\n    private static final long serialVersionUID = -1165815834787481668L;\n\n    private final Edge source;\n\n    protected DirectionalizedEdge(final Edge source)\n    {\n        super(source.getAtlas());\n        this.source = source;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return this.source.asPolyLine();\n    }\n\n    @Override\n    public Edge directionalized()\n    {\n        return this;\n    }\n\n    @Override\n    public Node end()\n    {\n        return this.source.end();\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.source.getIdentifier();\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        if (MaxSpeedTag.hasExtendedMaxSpeed(this.source))\n        {\n            final Map<String, String> tags = new HashMap<>();\n            this.source.getTags().forEach((key, value) ->\n            {\n                if (MaxSpeedForwardTag.KEY.equals(key))\n                {\n                    if (this.source.isMainEdge())\n                    {\n                        tags.put(MaxSpeedTag.KEY, value);\n                    }\n                }\n                else if (MaxSpeedBackwardTag.KEY.equals(key))\n                {\n                    if (!this.source.isMainEdge())\n                    {\n                        tags.put(MaxSpeedTag.KEY, value);\n                    }\n                }\n                else\n                {\n                    tags.put(key, value);\n                }\n            });\n            return tags;\n        }\n        return this.source.getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return this.source.relations();\n    }\n\n    @Override\n    public Optional<Edge> reversed()\n    {\n        final Optional<Edge> reversed = super.reversed();\n        if (reversed.isPresent())\n        {\n            return Optional.of(reversed.get().directionalized());\n        }\n        else\n        {\n            return reversed;\n        }\n    }\n\n    @Override\n    public Node start()\n    {\n        return this.source.start();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/Edge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.apache.commons.lang3.Validate;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.ReverseIdentifierFactory;\nimport org.openstreetmap.atlas.tags.HighwayTag;\n\nimport com.google.gson.JsonObject;\n\n/**\n * A unidirectional edge that belongs to an Atlas.\n *\n * @author matthieun\n */\npublic abstract class Edge extends LineItem implements Comparable<Edge>\n{\n    private static final long serialVersionUID = -4426003484206550921L;\n    private static final ReverseIdentifierFactory reverseIdentifierFactory = new ReverseIdentifierFactory();\n\n    /**\n     * If the way is bidirectional in OSM, we will put two edges in atlas for two traffic\n     * directions, each of them is one way. The main edge will have positive identifier and same\n     * traffic direction as OSM.\n     *\n     * @param identifier\n     *            Edge identifier\n     * @return True if the edge identifier is positive\n     */\n    public static boolean isMainEdgeIdentifier(final long identifier)\n    {\n        return identifier > 0;\n    }\n\n    /**\n     * @param identifier\n     *            Edge identifier\n     * @return True if the edge identifier is positive\n     * @deprecated Use isMainEdgeIdentifier instead.\n     */\n    @Deprecated(since = \"\")\n    public static boolean isMasterEdgeIdentifier(final long identifier)\n    {\n        return isMainEdgeIdentifier(identifier);\n    }\n\n    protected Edge(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    /**\n     * Compare two edges on their identifier.\n     * <p>\n     * NOSONAR here as the {@link AtlasEntity} equals and hashcode are good enough. \"\"equals(Object\n     * obj)\" should be overridden along with the \"compareTo(T obj)\" method (squid:S1210)\"\n     */\n    @Override\n    public int compareTo(final Edge other) // NOSONAR\n    {\n        return Long.compare(this.getIdentifier(), other.getIdentifier());\n    }\n\n    /**\n     * @return All the {@link Edge}s connected to the end {@link Node}s of this {@link Edge}, except\n     *         self. If this {@link Edge} is a two-way road, then the reversed {@link Edge} will be\n     *         included in the set.\n     */\n    public Set<Edge> connectedEdges()\n    {\n        final Set<Edge> result = new HashSet<>();\n        for (final Edge edge : this.end().connectedEdges())\n        {\n            if (!this.equals(edge))\n            {\n                result.add(edge);\n            }\n        }\n        for (final Edge edge : this.start().connectedEdges())\n        {\n            if (!this.equals(edge))\n            {\n                result.add(edge);\n            }\n        }\n        return result;\n    }\n\n    public Node connectedNode(final ConnectedNodeType connectedNodeType)\n    {\n        Validate.notNull(connectedNodeType);\n        final Node connectedNode = connectedNodeType.getAccessFunction().apply(this);\n        return connectedNode;\n    }\n\n    public Set<Node> connectedNodes()\n    {\n        final Set<Node> result = new HashSet<>();\n        result.add(this.start());\n        result.add(this.end());\n        return result;\n    }\n\n    /**\n     * @return The same {@link Edge} but with the tags interpreted with this {@link Edge}'s\n     *         direction. For example, if this {@link Edge} is backwards from its OSM way, and the\n     *         way has a maxspeed:backward tag, here it will be translated into a maxspeed tag. Also\n     *         the maxspeed:forward tag will be filtered out (it will be used by the reverse edge).\n     */\n    public Edge directionalized()\n    {\n        return new DirectionalizedEdge(this);\n    }\n\n    /**\n     * @return The {@link Node} at the end of this {@link Edge}\n     */\n    public abstract Node end();\n\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        final JsonObject properties = super.getGeoJsonProperties();\n\n        properties.addProperty(ConnectedNodeType.START.getPropertyName(), start().getIdentifier());\n        properties.addProperty(ConnectedNodeType.END.getPropertyName(), end().getIdentifier());\n\n        return properties;\n    }\n\n    /**\n     * @return the main for this {@link Edge}, which may or may not be the main.\n     */\n    public Edge getMainEdge()\n    {\n        return this.isMainEdge() ? this\n                : this.reversed()\n                        .orElseThrow(() -> new CoreException(\n                                \"Reverse edge should be available for edge {}\",\n                                this.getIdentifier()));\n    }\n\n    public long getMainEdgeIdentifier()\n    {\n        return Math.abs(this.getIdentifier());\n    }\n\n    /**\n     * @return the main for this {@link Edge}, which may or may not be the main.\n     * @deprecated Use getMainEdge instead.\n     */\n    @Deprecated(since = \"\")\n    public Edge getMasterEdge()\n    {\n        return getMainEdge();\n    }\n\n    /**\n     * @return The main edge identifier\n     * @deprecated Use getMainEdgeIdentifier instead\n     */\n    @Deprecated(since = \"\")\n    public long getMasterEdgeIdentifier()\n    {\n        return getMainEdgeIdentifier();\n    }\n\n    @Override\n    public ItemType getType()\n    {\n        return ItemType.EDGE;\n    }\n\n    /**\n     * @return {@code true} if there is a reverse edge to this one\n     */\n    public boolean hasReverseEdge()\n    {\n        return this.getAtlas().edge(-this.getIdentifier()) != null;\n    }\n\n    /**\n     * @return The {@link HighwayTag} of the Edge, if it is present. Return HighwayTag.NO if it is\n     *         not.\n     */\n    public HighwayTag highwayTag()\n    {\n        final Optional<HighwayTag> result = HighwayTag.highwayTag(this);\n        if (result.isPresent())\n        {\n            return result.get();\n        }\n        else\n        {\n            return HighwayTag.NO;\n        }\n    }\n\n    /**\n     * @return All the {@link Edge}s connected and pointing to the start {@link Node} of this\n     *         {@link Edge}. If this {@link Edge} is a two-way road, then the reversed {@link Edge}\n     *         will be included in the set.\n     */\n    public Set<Edge> inEdges()\n    {\n        return this.start().inEdges();\n    }\n\n    /**\n     * @param candidates\n     *            Set of edges and nodes to test connectivity to.\n     * @return True if the edge is directly connected at its end to at least one of the candidate\n     *         items\n     */\n    public boolean isConnectedAtEndTo(final Set<? extends AtlasItem> candidates)\n    {\n        for (final AtlasItem item : candidates)\n        {\n            if (item instanceof Node && end().equals(item))\n            {\n                return true;\n            }\n            if (item instanceof Edge && end().equals(((Edge) item).start()))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * @param candidates\n     *            Set of edges and nodes to test connectivity to.\n     * @return True if the edge is directly connected at its start to at least one of the candidate\n     *         items\n     */\n    public boolean isConnectedAtStartTo(final Set<? extends AtlasItem> candidates)\n    {\n        for (final AtlasItem item : candidates)\n        {\n            if (item instanceof Node && start().equals(item))\n            {\n                return true;\n            }\n            if (item instanceof Edge && start().equals(((Edge) item).end()))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Checks if edge is a main edge, by verifying that its identifier is a main edge identifier.\n     *\n     * @return True if the edge's identifier is a main edge identifier\n     */\n    public boolean isMainEdge()\n    {\n        return isMainEdgeIdentifier(this.getIdentifier());\n    }\n\n    /**\n     * @return True if the edge's identifier is a main edge identifier\n     * @deprecated Use isMainEdge instead.\n     */\n    @Deprecated(since = \"\")\n    public boolean isMasterEdge()\n    {\n        return isMainEdge();\n    }\n\n    /**\n     * @param candidate\n     *            candidate for reverseEdge\n     * @return {@code true} if candidate is the reverse of this {@link Edge}\n     */\n    public boolean isReversedEdge(final Edge candidate)\n    {\n        return this.getIdentifier() == -candidate.getIdentifier();\n    }\n\n    /**\n     * @return {@code true} if the {@link Edge} is a way-sectioned road.\n     */\n    public boolean isWaySectioned()\n    {\n        return reverseIdentifierFactory.getWaySectionIndex(this.getIdentifier()) != 0;\n    }\n\n    /**\n     * @return All the {@link Edge}s connected and pointing out of the end {@link Node} of this\n     *         {@link Edge}. If this {@link Edge} is a two-way road, then the reversed {@link Edge}\n     *         will be included in the set.\n     */\n    public Set<Edge> outEdges()\n    {\n        return this.end().outEdges();\n    }\n\n    /**\n     * @return An {@link Edge} that is reversed to this one if it exists, empty otherwise.\n     */\n    public Optional<Edge> reversed()\n    {\n        final Edge edge = this.getAtlas().edge(-this.getIdentifier());\n        if (edge != null)\n        {\n            return Optional.of(edge);\n        }\n        return Optional.empty();\n    }\n\n    public abstract Node start();\n\n    @Override\n    public String toDiffViewFriendlyString()\n    {\n        final String relationsString = this.parentRelationsAsDiffViewFriendlyString();\n\n        final String startNodeString = start() != null ? Long.toString(start().getIdentifier())\n                : \"null\";\n        final String endNodeString = start() != null ? Long.toString(end().getIdentifier())\n                : \"null\";\n        final String polyLineWkt = this.asPolyLine() != null ? this.asPolyLine().toWkt() : \"null\";\n\n        return \"[Edge\" + \": id=\" + this.getIdentifier() + \", startNode=\" + startNodeString\n                + \", endNode=\" + endNodeString + \", polyLine=\" + polyLineWkt + \", relations=(\"\n                + relationsString + \"), \" + tagString() + \"]\";\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[Edge\" + \": id=\" + this.getIdentifier() + \", startNode=\" + start().getIdentifier()\n                + \", endNode=\" + end().getIdentifier() + \", polyLine=\" + this.asPolyLine().toWkt()\n                + \", \" + tagString() + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/ItemType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * Type of item in an {@link Atlas}\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic enum ItemType\n{\n    NODE(0)\n    {\n        @Override\n        public Iterable<Node> entitiesForIdentifiers(final Atlas atlas, final Long... identifiers)\n        {\n            return atlas.nodes(identifiers);\n        }\n\n        @Override\n        public long numberOfEntities(final Atlas atlas)\n        {\n            return atlas.numberOfNodes();\n        }\n    },\n    EDGE(1)\n    {\n        @Override\n        public Iterable<Edge> entitiesForIdentifiers(final Atlas atlas, final Long... identifiers)\n        {\n            return atlas.edges(identifiers);\n        }\n\n        @Override\n        public long numberOfEntities(final Atlas atlas)\n        {\n            return atlas.numberOfEdges();\n        }\n    },\n    AREA(2)\n    {\n        @Override\n        public Iterable<Area> entitiesForIdentifiers(final Atlas atlas, final Long... identifiers)\n        {\n            return atlas.areas(identifiers);\n        }\n\n        @Override\n        public long numberOfEntities(final Atlas atlas)\n        {\n            return atlas.numberOfAreas();\n        }\n    },\n    LINE(3)\n    {\n        @Override\n        public Iterable<Line> entitiesForIdentifiers(final Atlas atlas, final Long... identifiers)\n        {\n            return atlas.lines(identifiers);\n        }\n\n        @Override\n        public long numberOfEntities(final Atlas atlas)\n        {\n            return atlas.numberOfLines();\n        }\n    },\n    POINT(4)\n    {\n        @Override\n        public Iterable<Point> entitiesForIdentifiers(final Atlas atlas, final Long... identifiers)\n        {\n            return atlas.points(identifiers);\n        }\n\n        @Override\n        public long numberOfEntities(final Atlas atlas)\n        {\n            return atlas.numberOfPoints();\n        }\n    },\n    RELATION(5)\n    {\n        @Override\n        public Iterable<Relation> entitiesForIdentifiers(final Atlas atlas,\n                final Long... identifiers)\n        {\n            return atlas.relations(identifiers);\n        }\n\n        @Override\n        public long numberOfEntities(final Atlas atlas)\n        {\n            return atlas.numberOfRelations();\n        }\n    };\n\n    private final int value;\n\n    public static ItemType forEntity(final AtlasEntity entity)\n    {\n        return entity.getType();\n    }\n\n    public static ItemType forValue(final int value)\n    {\n        for (final ItemType type : values())\n        {\n            if (type.getValue() == value)\n            {\n                return type;\n            }\n        }\n        throw new CoreException(\"Invalid value: {}\", value);\n    }\n\n    public static ItemType shortValueOf(final String value)\n    {\n        switch (value)\n        {\n            case \"N\":\n                return NODE;\n            case \"E\":\n                return EDGE;\n            case \"A\":\n                return AREA;\n            case \"L\":\n                return LINE;\n            case \"P\":\n                return POINT;\n            case \"R\":\n                return RELATION;\n            default:\n                throw new CoreException(\"Invalid short value {}\", value);\n        }\n    }\n\n    ItemType(final int value)\n    {\n        this.value = value;\n    }\n\n    public abstract <E extends AtlasEntity> Iterable<E> entitiesForIdentifiers(Atlas atlas,\n            Long... identifiers);\n\n    public AtlasEntity entityForIdentifier(final Atlas atlas, final long identifier)\n    {\n        switch (this)\n        {\n            case NODE:\n                return atlas.node(identifier);\n            case EDGE:\n                return atlas.edge(identifier);\n            case AREA:\n                return atlas.area(identifier);\n            case LINE:\n                return atlas.line(identifier);\n            case POINT:\n                return atlas.point(identifier);\n            case RELATION:\n                return atlas.relation(identifier);\n\n            default:\n                throw new CoreException(\"Invalid type {}\", this);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <M extends AtlasEntity> Class<M> getMemberClass()\n    {\n        switch (this)\n        {\n            case NODE:\n                return (Class<M>) Node.class;\n            case EDGE:\n                return (Class<M>) Edge.class;\n            case AREA:\n                return (Class<M>) Area.class;\n            case LINE:\n                return (Class<M>) Line.class;\n            case POINT:\n                return (Class<M>) Point.class;\n            case RELATION:\n                return (Class<M>) Relation.class;\n\n            default:\n                throw new CoreException(\"Invalid type {}\", this);\n        }\n    }\n\n    public int getValue()\n    {\n        return this.value;\n    }\n\n    public abstract long numberOfEntities(Atlas atlas);\n\n    public String toShortString()\n    {\n        switch (this)\n        {\n            case NODE:\n                return \"N\";\n            case EDGE:\n                return \"E\";\n            case AREA:\n                return \"A\";\n            case LINE:\n                return \"L\";\n            case POINT:\n                return \"P\";\n            case RELATION:\n                return \"R\";\n            default:\n                throw new CoreException(\"Invalid type {}\", this);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/Line.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * A line that is not navigable\n *\n * @author matthieun\n */\npublic abstract class Line extends LineItem\n{\n    private static final long serialVersionUID = 5348604376185677L;\n\n    protected Line(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    @Override\n    public ItemType getType()\n    {\n        return ItemType.LINE;\n    }\n\n    @Override\n    public String toDiffViewFriendlyString()\n    {\n        final String relationsString = this.parentRelationsAsDiffViewFriendlyString();\n\n        return \"[Line: id=\" + this.getIdentifier() + \", polyLine=\" + this.asPolyLine()\n                + \", relations=(\" + relationsString + \"), \" + tagString() + \"]\";\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[Line: id=\" + this.getIdentifier() + \", polyLine=\" + this.asPolyLine() + \", \"\n                + tagString() + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/LineItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Heading;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonObject;\n\n/**\n * {@link AtlasItem} that is in shape of a {@link PolyLine}\n *\n * @author matthieun\n * @author mgostintsev\n */\npublic abstract class LineItem extends AtlasItem\n{\n    private static final long serialVersionUID = -2053566750957119655L;\n    private static final Logger logger = LoggerFactory.getLogger(LineItem.class);\n\n    protected LineItem(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        return asPolyLine().asGeoJsonGeometry();\n    }\n\n    /**\n     * @return The {@link PolyLine} that represents this {@link LineItem}\n     */\n    public abstract PolyLine asPolyLine();\n\n    @Override\n    public Rectangle bounds()\n    {\n        return asPolyLine().bounds();\n    }\n\n    @Override\n    public Iterable<Location> getRawGeometry()\n    {\n        return asPolyLine();\n    }\n\n    @Override\n    public boolean intersects(final GeometricSurface surface)\n    {\n        return surface.overlaps(asPolyLine());\n    }\n\n    /**\n     * Check if this {@link LineItem} is closed. Closed is defined when the first {@link Location}\n     * is the same as the last {@link Location}.\n     *\n     * @return {@code true} if it's closed.\n     */\n    public boolean isClosed()\n    {\n        final PolyLine polyLine = asPolyLine();\n        return polyLine.first().equals(polyLine.last());\n    }\n\n    /**\n     * @return flag to denote zero length line items\n     */\n    public boolean isZeroLength()\n    {\n        return length().equals(Distance.ZERO);\n    }\n\n    /**\n     * @return The length of this item\n     */\n    public Distance length()\n    {\n        return asPolyLine().length();\n    }\n\n    /**\n     * @return the number of shape-points for this item, including start and end points.\n     */\n    public int numberOfShapePoints()\n    {\n        return asPolyLine().size();\n    }\n\n    /**\n     * @return The overall heading of the {@link PolyLine}: the heading between the start point and\n     *         the end point.\n     */\n    public Optional<Heading> overallHeading()\n    {\n        final PolyLine polyLine = this.asPolyLine();\n        if (polyLine.first().equals(polyLine.last()))\n        {\n            if (logger.isWarnEnabled())\n            {\n                logger.warn(\n                        \"Cannot compute ({},{})'s overall heading when the polyline has \"\n                                + \"same start and end locations : {}\",\n                        this.getType(), this.getIdentifier(), polyLine.first().toWkt());\n            }\n            return Optional.empty();\n        }\n        return this.asPolyLine().overallHeading();\n    }\n\n    @Override\n    public LocationIterableProperties toGeoJsonBuildingBlock()\n    {\n        final Map<String, String> tags = getTags();\n        tags.put(\"identifier\", String.valueOf(getIdentifier()));\n        tags.put(\"osmIdentifier\", String.valueOf(getOsmIdentifier()));\n        tags.put(\"itemType\", String.valueOf(getType()));\n\n        final Optional<String> shardName = getAtlas().metaData().getShardName();\n        shardName.ifPresent(shard -> tags.put(\"shard\", shard));\n\n        if (this instanceof Edge)\n        {\n            tags.put(\"startNode\", String.valueOf(((Edge) this).start().getIdentifier()));\n            tags.put(\"endNode\", String.valueOf(((Edge) this).end().getIdentifier()));\n        }\n\n        final StringList parentRelations = new StringList();\n        this.relations().forEach(relation ->\n        {\n            final RelationMember member = relation.members().get(getIdentifier(), getType());\n            parentRelations.add(member.getRelationIdentifier() + \"-\" + member.getRole());\n        });\n\n        if (!parentRelations.isEmpty())\n        {\n            tags.put(\"parentRelations\", parentRelations.join(\", \"));\n        }\n\n        return new GeoJsonBuilder.LocationIterableProperties(getRawGeometry(), tags);\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return this.asPolyLine().toWkb();\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return this.asPolyLine().toWkt();\n    }\n\n    @Override\n    public boolean within(final GeometricSurface surface)\n    {\n        return this.asPolyLine().within(surface);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/LocationItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.Snapper.SnappedLocation;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport com.google.gson.JsonObject;\n\n/**\n * An {@link AtlasItem} that is represented by one single location\n *\n * @author matthieun\n */\npublic abstract class LocationItem extends AtlasItem\n{\n    private static final long serialVersionUID = -2616559591051747286L;\n\n    protected LocationItem(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        return getLocation().asGeoJsonGeometry();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return getLocation().bounds();\n    }\n\n    /**\n     * @return The item's {@link Location}\n     */\n    public abstract Location getLocation();\n\n    @Override\n    public Iterable<Location> getRawGeometry()\n    {\n        return getLocation();\n    }\n\n    @Override\n    public boolean intersects(final GeometricSurface surface)\n    {\n        return surface.fullyGeometricallyEncloses(getLocation());\n    }\n\n    public SnappedLocation snapTo(final Area other)\n    {\n        return this.getLocation().snapTo(other.asPolygon());\n    }\n\n    public SnappedLocation snapTo(final LineItem other)\n    {\n        return this.getLocation().snapTo(other.asPolyLine());\n    }\n\n    @Override\n    public LocationIterableProperties toGeoJsonBuildingBlock()\n    {\n        final Map<String, String> tags = getTags();\n        tags.put(\"identifier\", String.valueOf(getIdentifier()));\n        tags.put(\"osmIdentifier\", String.valueOf(getOsmIdentifier()));\n        tags.put(\"itemType\", String.valueOf(getType()));\n\n        if (this instanceof Node)\n        {\n            final StringList inEdges = new StringList();\n            final StringList outEdges = new StringList();\n            ((Node) this).inEdges()\n                    .forEach(edge -> inEdges.add(edge != null ? edge.getIdentifier() : \"null\"));\n            ((Node) this).outEdges()\n                    .forEach(edge -> outEdges.add(edge != null ? edge.getIdentifier() : \"null\"));\n            tags.put(\"inEdges\", inEdges.join(\", \"));\n            tags.put(\"outEdges\", outEdges.join(\", \"));\n        }\n\n        final Location location = this.getLocation();\n        tags.put(\"latitude\", String.valueOf(location.getLatitude().asDm7()));\n        tags.put(\"longitude\", String.valueOf(location.getLongitude().asDm7()));\n\n        final StringList parentRelations = new StringList();\n        this.relations().forEach(relation ->\n        {\n            final RelationMember member = relation.members().get(getIdentifier(), getType());\n            parentRelations.add(member.getRelationIdentifier() + \"-\" + member.getRole());\n        });\n\n        if (!parentRelations.isEmpty())\n        {\n            tags.put(\"parentRelations\", parentRelations.join(\", \"));\n        }\n\n        return new GeoJsonBuilder.LocationIterableProperties(getRawGeometry(), tags);\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return this.getLocation().toWkb();\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return this.getLocation().toWkt();\n    }\n\n    @Override\n    public boolean within(final GeometricSurface surface)\n    {\n        return this.getLocation().within(surface);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/Node.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.Supplier;\n\nimport org.apache.commons.lang3.Validate;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * Navigable Node\n *\n * @author matthieun\n * @author Yazad Khambata\n */\npublic abstract class Node extends LocationItem\n{\n    private static final long serialVersionUID = 2082593591946379000L;\n\n    protected Node(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    /**\n     * @return The absolute valence, considering all {@link Edge}s, irrespective of\n     *         bi-directionality.\n     */\n    public long absoluteValence()\n    {\n        return this.connectedEdges().size();\n    }\n\n    public SortedSet<Edge> connectedEdges()\n    {\n        final SortedSet<Edge> result = new TreeSet<>();\n        result.addAll(inEdges());\n        result.addAll(outEdges());\n        return result;\n    }\n\n    /**\n     * Get the appropriate set {@link Edge}s of {@link ConnectedEdgeType}.\n     *\n     * @param connectedEdgeType\n     *            - The type of {@link Edge}-{@link Node} connection.\n     * @return - A set of {@link Edge}s connected to the {@link Node} of {@link ConnectedEdgeType}.\n     */\n    public SortedSet<Edge> connectedEdges(final ConnectedEdgeType connectedEdgeType)\n    {\n        Validate.notNull(connectedEdgeType);\n        final SortedSet<Edge> connectedEdges = connectedEdgeType.getAccessFunction().apply(this);\n        return connectedEdges;\n    }\n\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        final JsonObject properties = super.getGeoJsonProperties();\n\n        final JsonArray inEdgesArray = new JsonArray();\n        final JsonArray outEdgesArray = new JsonArray();\n\n        for (final Edge edge : this.inEdges())\n        {\n            inEdgesArray.add(new JsonPrimitive(edge.getIdentifier()));\n        }\n\n        for (final Edge edge : this.outEdges())\n        {\n            outEdgesArray.add(new JsonPrimitive(edge.getIdentifier()));\n        }\n\n        // Adding a JSON array with the edge IDs.\n        // In the RFC spec, nested objects are ok in properties.\n        // https://tools.ietf.org/html/rfc7946#section-1.5\n        properties.add(ConnectedEdgeType.IN.getPropertyName(), inEdgesArray);\n        properties.add(ConnectedEdgeType.OUT.getPropertyName(), outEdgesArray);\n\n        return properties;\n    }\n\n    @Override\n    public ItemType getType()\n    {\n        return ItemType.NODE;\n    }\n\n    /**\n     * @return The {@link Edge}s that end at this node\n     */\n    public abstract SortedSet<Edge> inEdges();\n\n    /**\n     * @return The {@link Edge}s that start at this node\n     */\n    public abstract SortedSet<Edge> outEdges();\n\n    @Override\n    public String toDiffViewFriendlyString()\n    {\n        final String relationsString = this.parentRelationsAsDiffViewFriendlyString();\n\n        return \"[Node: id=\" + this.getIdentifier() + \", location=\" + this.getLocation()\n                + \", inEdges=\" + connectedEdgesIdentifiers(() -> inEdges()) + \", outEdges=\"\n                + connectedEdgesIdentifiers(() -> outEdges()) + \", relations=(\" + relationsString\n                + \"), \" + tagString() + \"]\";\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[Node: id=\" + this.getIdentifier() + \", location=\" + this.getLocation()\n                + \", inEdges=\" + connectedEdgesIdentifiers(() -> inEdges()) + \", outEdges=\"\n                + connectedEdgesIdentifiers(() -> outEdges()) + \", \" + tagString() + \"]\";\n    }\n\n    /**\n     * @return The valence considering only the main {@link Edge}s\n     */\n    public long valence()\n    {\n        return this.connectedEdges().stream().filter(Edge::isMainEdge).count();\n    }\n\n    private SortedSet<Long> connectedEdgesIdentifiers(\n            final Supplier<SortedSet<Edge>> getConnectedEdges)\n    {\n        final SortedSet<Long> result = new TreeSet<>();\n        getConnectedEdges.get().forEach(edge -> result.add(edge.getIdentifier()));\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/Point.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * A Point that is not navigable.\n *\n * @author matthieun\n */\npublic abstract class Point extends LocationItem\n{\n    private static final long serialVersionUID = -7888952319754555424L;\n\n    protected Point(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    @Override\n    public ItemType getType()\n    {\n        return ItemType.POINT;\n    }\n\n    @Override\n    public String toDiffViewFriendlyString()\n    {\n        final String relationsString = this.parentRelationsAsDiffViewFriendlyString();\n\n        return \"[Point: id=\" + this.getIdentifier() + \", location=\" + this.getLocation()\n                + \", relations=(\" + relationsString + \"), \" + tagString() + \"]\";\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[Point: id=\" + this.getIdentifier() + \", location=\" + this.getLocation() + \", \"\n                + tagString() + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/README.md",
    "content": "# Atlas Items\n\n## TurnRestriction\n\nA TurnRestriction is a Route that is restricted based on an [OSM turn restriction](http://wiki.openstreetmap.org/wiki/Relation:restriction) relation.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/Relation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.Deque;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.WktPrintable;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonFeatureCollection;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\n/**\n * An OSM relation\n *\n * @author matthieun\n * @author Sid\n * @author hallahan\n * @author Yazad Khambata\n */\npublic abstract class Relation extends AtlasEntity\n        implements Iterable<RelationMember>, GeoJsonFeatureCollection<RelationMember>\n{\n    /**\n     * The ring type of a {@link MultiPolygon} member.\n     *\n     * @author matthieun\n     */\n    public enum Ring\n    {\n        OUTER,\n        INNER\n    }\n\n    public static final Comparator<Relation> RELATION_ID_COMPARATOR = Comparator\n            .comparingLong(AtlasObject::getIdentifier);\n    private static final Logger logger = LoggerFactory.getLogger(Relation.class);\n    private static final long serialVersionUID = -9013894610780915685L;\n    private static final RelationOrAreaToMultiPolygonConverter MULTI_POLYGON_CONVERTER = new RelationOrAreaToMultiPolygonConverter();\n    private static final JtsMultiPolygonToMultiPolygonConverter JTS_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n    private org.locationtech.jts.geom.MultiPolygon geom;\n    private boolean badGeom = false;\n    private Rectangle bounds = null;\n\n    protected Relation(final Atlas atlas)\n    {\n        super(atlas);\n    }\n\n    /**\n     * @return All the members of this relation's OSM ancestor. If this relation has not been\n     *         sliced, then this will return the same as <code>members()</code>. If this relation is\n     *         sliced, and is part of a pool of other relations that belong to the same OSM\n     *         ancestor, this method will pool together all the members of all those relations in\n     *         its Atlas.\n     */\n    public abstract RelationMemberList allKnownOsmMembers();\n\n    public abstract List<Relation> allRelationsWithSameOsmIdentifier();\n\n    @Override\n    public JsonObject asGeoJson()\n    {\n        return GeoJsonUtils.feature(this);\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        // Can't be final due to catch block may reassign.\n        JsonObject geometry;\n\n        // We should only be writing relations as GeoJSON when they are polygons and multipolygons.\n        // We want multipolygons, but not boundaries, as we can render boundaries' ways by\n        // themselves fine.\n        // The isMultiPolygon() method also includes boundaries, which we do not want.\n        if (this.isGeometric())\n        {\n            try\n            {\n                final MultiPolygon multiPolygon = MULTI_POLYGON_CONVERTER.convert(this);\n                geometry = multiPolygon.asGeoJsonGeometry();\n            }\n            // It seems like we get caught in this exception a lot! We don't ingest coastline\n            // features, so polygons that touch coastlines will fail. It's good to include\n            // the exception in the data, along with the bounding box. That way, we can\n            // notice the problem when browsing the map.\n            catch (final CoreException exception)\n            {\n                final String message = String.format(\"%s - %s\",\n                        exception.getClass().getSimpleName(), exception.getMessage());\n                logger.error(\"Unable to recreate multipolygon for relation {}. {}\", getIdentifier(),\n                        message);\n                geometry = GeoJsonUtils.boundsToPolygonGeometry(bounds());\n            }\n        }\n        // Otherwise, we'll fall back to just providing the properties of the relation with the\n        // bounding box as a polygon geometry.\n        else\n        {\n            geometry = GeoJsonUtils.boundsToPolygonGeometry(bounds());\n        }\n        return geometry;\n    }\n\n    public Optional<org.locationtech.jts.geom.MultiPolygon> asMultiPolygon()\n    {\n        return this.asMultiPolygon(false);\n    }\n\n    public Optional<org.locationtech.jts.geom.MultiPolygon> asMultiPolygon(final boolean assemble)\n    {\n        try\n        {\n            if (assemble && !this.badGeom && this.geom == null && isGeometric())\n            {\n                this.geom = JTS_CONVERTER.backwardConvert(MULTI_POLYGON_CONVERTER.convert(this));\n            }\n        }\n        catch (final Exception exc)\n        {\n            logger.trace(\"Exception making multipolygon geometry for relation {}\",\n                    this.getIdentifier(), exc);\n            this.badGeom = true;\n        }\n        return Optional.ofNullable(this.geom);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        final Optional<org.locationtech.jts.geom.MultiPolygon> geometry = this.asMultiPolygon();\n        if (!this.getBadGeom() && geometry.isPresent())\n        {\n            if (this.bounds == null)\n            {\n                this.bounds = Rectangle.forLocated(new JtsPolygonConverter()\n                        .backwardConvert((org.locationtech.jts.geom.Polygon) new GeometryFactory()\n                                .toGeometry(geometry.get().getEnvelopeInternal())));\n            }\n            return this.bounds;\n        }\n        return boundsInternal(new LinkedHashSet<>());\n    }\n\n    public String configurableString(final String betweenEachMemberAndRelation,\n            final String betweenEachMember)\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[Relation: id=\");\n        builder.append(getIdentifier());\n        builder.append(\", [Members: \\n\\t\\t\\t\\t\");\n        final StringList list = new StringList();\n        for (final RelationMember member : this)\n        {\n            list.add(betweenEachMemberAndRelation + betweenEachMember + member.toString());\n        }\n        builder.append(list.join(\", \\n\\t\\t\\t\\t\"));\n        builder.append(\"\\n\\t\\t\\t\");\n        builder.append(betweenEachMemberAndRelation);\n        builder.append(\"], \");\n        builder.append(tagString());\n        final Optional<org.locationtech.jts.geom.MultiPolygon> multipolygon = this.asMultiPolygon();\n        if (multipolygon.isPresent())\n        {\n            builder.append(\", multipolygon=\");\n            builder.append(multipolygon.get().toText());\n        }\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    /**\n     * \"Flattens\" the relation by returning the set of non-Relation members. Adds any non-Relation\n     * members to the set, then loops on any Relation members to add their non-Relation members as\n     * well. Keeps track of Relations whose identifiers have already been operated on, so that\n     * recursively defined relations don't cause problems.\n     *\n     * @return a Set of AtlasObjects all related to this Relation, with no Relations.\n     */\n    public Set<AtlasObject> flatten()\n    {\n        final Deque<AtlasObject> toProcess = new LinkedList<>();\n        final Set<Long> relationsSeen = new HashSet<>();\n        AtlasObject polledMember;\n        final Set<AtlasObject> relationMembers = new HashSet<>();\n\n        toProcess.add(this);\n        while (!toProcess.isEmpty())\n        {\n            polledMember = toProcess.poll();\n            if (polledMember instanceof Relation)\n            {\n                if (relationsSeen.contains(polledMember.getIdentifier()))\n                {\n                    continue;\n                }\n                ((Relation) polledMember).members()\n                        .forEach(member -> toProcess.add(member.getEntity()));\n                relationsSeen.add(polledMember.getIdentifier());\n            }\n            else\n            {\n                relationMembers.add(polledMember);\n            }\n        }\n        return relationMembers;\n    }\n\n    /**\n     * \"Flattens\" the relation by returning the set of child Relation members, recursively.\n     *\n     * @return a Set of IDs for all sub Relations.\n     */\n    public Set<Long> flattenRelations()\n    {\n        final Deque<AtlasObject> toProcess = new LinkedList<>();\n        final Set<Long> subrelations = new HashSet<>();\n        AtlasObject polledMember;\n\n        toProcess.add(this);\n        while (!toProcess.isEmpty())\n        {\n            polledMember = toProcess.poll();\n            if (polledMember instanceof Relation)\n            {\n                if (subrelations.contains(polledMember.getIdentifier()))\n                {\n                    continue;\n                }\n                ((Relation) polledMember).members()\n                        .forEach(member -> toProcess.add(member.getEntity()));\n                subrelations.add(polledMember.getIdentifier());\n            }\n        }\n        return subrelations;\n    }\n\n    /**\n     * @return the {@link RelationBean} representation of the Relation\n     */\n    public RelationBean getBean()\n    {\n        final RelationBean bean = new RelationBean();\n        for (final RelationMember member : this)\n        {\n            final AtlasEntity entity = member.getEntity();\n            bean.addItem(entity.getIdentifier(), member.getRole(), entity.getType());\n        }\n        return bean;\n    }\n\n    @Override\n    public Iterable<RelationMember> getGeoJsonObjects()\n    {\n        return this;\n    }\n\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        final JsonObject properties = super.getGeoJsonProperties();\n        addMembersToProperties(properties);\n        return properties;\n    }\n\n    public JsonObject getGeoJsonPropertiesWithoutMembers()\n    {\n        return super.getGeoJsonProperties();\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.FEATURE;\n    }\n\n    @Override\n    public ItemType getType()\n    {\n        return ItemType.RELATION;\n    }\n\n    public boolean hasMultiPolygonMembers(final Ring ring)\n    {\n        if (isGeometric())\n        {\n            for (final RelationMember member : members())\n            {\n                switch (ring)\n                {\n                    case OUTER:\n                        if (RelationTypeTag.MULTIPOLYGON_ROLE_OUTER.equals(member.getRole()))\n                        {\n                            return true;\n                        }\n                        break;\n                    case INNER:\n                        if (RelationTypeTag.MULTIPOLYGON_ROLE_INNER.equals(member.getRole()))\n                        {\n                            return true;\n                        }\n                        break;\n                    default:\n                        throw new CoreException(\"Unknown ring type: {}\", ring);\n                }\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean intersects(final GeometricSurface surface)\n    {\n        return intersectsInternal(surface, new LinkedHashSet<>());\n    }\n\n    public boolean isGeometric()\n    {\n        return Validators.isOfType(this, RelationTypeTag.class, RelationTypeTag.MULTIPOLYGON,\n                RelationTypeTag.BOUNDARY, RelationTypeTag.LAND_AREA);\n    }\n\n    public boolean isMultiPolygon()\n    {\n        return Validators.isOfType(this, RelationTypeTag.class, RelationTypeTag.MULTIPOLYGON);\n    }\n\n    @Override\n    public Iterator<RelationMember> iterator()\n    {\n        return members().iterator();\n    }\n\n    /**\n     * @return All the members of this specific (potentially sliced) relation.\n     */\n    public abstract RelationMemberList members();\n\n    /**\n     * Get a subset of {@link #members()} matching the predicate.\n     *\n     * @param predicate\n     *            - the predicate to filter on.\n     * @return - {@link #members()} matching the predicate.\n     */\n    public RelationMemberList membersMatching(final Predicate<RelationMember> predicate)\n    {\n        if (this.members() == null)\n        {\n            return new RelationMemberList(new ArrayList<>());\n        }\n        return members().stream().filter(predicate).collect(RelationMemberList.collect());\n    }\n\n    /**\n     * Get a subset of {@link #members()} matching a certain {@link ItemType}.\n     *\n     * @param itemTypes\n     *            - the types of members to filter.\n     * @return - {@link #members()} of type itemType.\n     */\n    public RelationMemberList membersOfType(final ItemType... itemTypes)\n    {\n        final List<Predicate<RelationMember>> itemTypePredicates = Arrays.stream(itemTypes)\n                .map(itemType ->\n                {\n                    final Predicate<RelationMember> relationMemberPredicate = member -> member\n                            .getEntity().getType() == itemType;\n\n                    return relationMemberPredicate;\n                }).collect(Collectors.toList());\n\n        final RelationMemberList relationMemberList = itemTypePredicates.stream()\n                .map(this::membersMatching).flatMap(RelationMemberList::stream)\n                .collect(RelationMemberList.collect());\n\n        return relationMemberList;\n    }\n\n    /**\n     * In case a {@link Relation} is spanning multiple {@link Atlas}, keep track of the parent OSM\n     * relation identifier to be able to match it back to other sliced relations.\n     *\n     * @return The OSM identifier\n     */\n    public abstract Long osmRelationIdentifier();\n\n    @Override\n    public String toDiffViewFriendlyString()\n    {\n        final String relationsString = this.parentRelationsAsDiffViewFriendlyString();\n\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[Relation: id=\");\n        builder.append(getIdentifier());\n        builder.append(\", [Members: \");\n        final StringList list = new StringList();\n        for (final RelationMember member : this)\n        {\n            list.add(member.toString());\n        }\n        builder.append(list.join(\", \"));\n        builder.append(\"], \");\n        builder.append(\"relations=(\" + relationsString + \"), \");\n        builder.append(tagString());\n        builder.append(\"]\");\n\n        return builder.toString();\n    }\n\n    @Override\n    public LocationIterableProperties toGeoJsonBuildingBlock()\n    {\n        final Map<String, String> tags = getTags();\n        tags.put(\"identifier\", String.valueOf(getIdentifier()));\n        tags.put(\"osmIdentifier\", String.valueOf(getOsmIdentifier()));\n        tags.put(\"itemType\", String.valueOf(getType()));\n        tags.put(\"relation\", this.toSimpleString());\n\n        final Optional<String> shardName = getAtlas().metaData().getShardName();\n        shardName.ifPresent(shard -> tags.put(\"shard\", shard));\n\n        return new GeoJsonBuilder.LocationIterableProperties(bounds().center(), tags);\n    }\n\n    public String toSimpleString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[Relation: id=\");\n        builder.append(getIdentifier());\n        builder.append(\", [Members: \");\n        final StringList list = new StringList();\n        for (final RelationMember member : this)\n        {\n            list.add(member.toString());\n        }\n        builder.append(list.join(\", \"));\n        builder.append(\"], \");\n        builder.append(tagString());\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    @Override\n    public String toString()\n    {\n        return configurableString(\"\", \"\");\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        throw new UnsupportedOperationException(\"Relation.toWkb not implemented yet.\");\n    }\n\n    @Override\n    public String toWkt()\n    {\n        final Optional<org.locationtech.jts.geom.MultiPolygon> geom = this.asMultiPolygon();\n        if (geom.isPresent())\n        {\n            return geom.get().toText();\n        }\n        return WktPrintable.toWktCollection(leafMembers().collect(Collectors.toList()));\n    }\n\n    /**\n     * Return {@code true} if this Relation has all members fully within the supplied\n     * {@link GeometricSurface}.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to check for\n     * @return {@code true} if the relation has all members within the given\n     *         {@link GeometricSurface}\n     */\n    @Override\n    public boolean within(final GeometricSurface surface)\n    {\n        return withinInternal(surface, new LinkedHashSet<>());\n    }\n\n    /**\n     * Avoid stack overflows in case a relation has looping members. This should never happen with a\n     * {@link PackedAtlas} but could happen when two {@link Atlas} are combined into a\n     * {@link MultiAtlas}.\n     *\n     * @param parentRelationIdentifiers\n     *            The identifiers of the parent relations that have already been visited.\n     * @return The bounds\n     */\n    protected Rectangle boundsInternal(final Set<Long> parentRelationIdentifiers)\n    {\n        if (this.members().isEmpty())\n        {\n            return Rectangle.MINIMUM;\n        }\n        final List<Located> itemsToConsider = new ArrayList<>();\n        for (final AtlasEntity member : Iterables.stream(this).map(RelationMember::getEntity)\n                .filter(Objects::nonNull))\n        {\n            if (member instanceof Relation)\n            {\n                final long identifier = member.getIdentifier();\n                if (parentRelationIdentifiers.contains(identifier))\n                {\n                    continue;\n                }\n                else\n                {\n                    parentRelationIdentifiers.add(identifier);\n                    itemsToConsider\n                            .add(((Relation) member).boundsInternal(parentRelationIdentifiers));\n                }\n            }\n            else\n            {\n                final Rectangle memberBounds = member.bounds();\n                if (memberBounds != null)\n                {\n                    itemsToConsider.add(memberBounds);\n                }\n            }\n        }\n        if (Iterables.size(itemsToConsider) == 0)\n        {\n            return Rectangle.MINIMUM;\n        }\n        return Rectangle.forLocated(itemsToConsider);\n    }\n\n    protected boolean getBadGeom()\n    {\n        return this.badGeom;\n    }\n\n    protected org.locationtech.jts.geom.MultiPolygon getGeom()\n    {\n        return this.geom;\n    }\n\n    /**\n     * Avoid stack overflows in case a relation has looping members. This should never happen with a\n     * {@link PackedAtlas} but could happen when two {@link Atlas} are combined into a\n     * {@link MultiAtlas}.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to check for\n     * @param parentRelationIdentifiers\n     *            The identifiers of the parent relations that have already been visited.\n     * @return True if the relation intersects the geometricSurface\n     */\n    protected boolean intersectsInternal(final GeometricSurface surface,\n            final Set<Long> parentRelationIdentifiers)\n    {\n        for (final RelationMember member : this)\n        {\n            final AtlasEntity entity = member.getEntity();\n            if (entity instanceof Relation)\n            {\n                final long identifier = entity.getIdentifier();\n                if (parentRelationIdentifiers.contains(identifier))\n                {\n                    continue;\n                }\n                else\n                {\n                    parentRelationIdentifiers.add(identifier);\n                    if (((Relation) entity).intersectsInternal(surface, parentRelationIdentifiers))\n                    {\n                        return true;\n                    }\n                }\n            }\n            else if (entity.intersects(surface))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    protected void setGeom(final org.locationtech.jts.geom.MultiPolygon geom)\n    {\n        this.geom = geom;\n    }\n\n    /**\n     * Avoid stack overflows in case a relation has looping members. This should never happen with a\n     * {@link PackedAtlas} but could happen when two {@link Atlas} are combined into a\n     * {@link MultiAtlas}.\n     *\n     * @param surface\n     *            The {@link GeometricSurface} to check for\n     * @param parentRelationIdentifiers\n     *            The identifiers of the parent relations that have already been visited.\n     * @return {@code true} if the relation has all members within the given\n     *         {@link GeometricSurface}\n     */\n    protected boolean withinInternal(final GeometricSurface surface,\n            final Set<Long> parentRelationIdentifiers)\n    {\n        for (final RelationMember member : this)\n        {\n            final AtlasEntity entity = member.getEntity();\n            if (entity instanceof Relation)\n            {\n                final long identifier = entity.getIdentifier();\n                if (parentRelationIdentifiers.contains(identifier))\n                {\n                    continue;\n                }\n                else\n                {\n                    parentRelationIdentifiers.add(identifier);\n                    if (!((Relation) entity).withinInternal(surface, parentRelationIdentifiers))\n                    {\n                        return false;\n                    }\n                }\n            }\n            else if (isUnenclosedNonRelationEntity(surface, entity))\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * We explicitly want to add member metadata to the properties of Relations, but only when we\n     * are serializing relation entities. Overriding getGeoJsonProperties() would not work properly,\n     * because that gets called when you are listing metadata about relations a non-relation entity\n     * may be in. Calling this method, only in this class, avoids a recursive call that would list\n     * members of relations in relation metadata for non-relation entities.\n     *\n     * @param properties\n     *            The JsonObject properties object we will add member metadata to.\n     */\n    private void addMembersToProperties(final JsonObject properties)\n    {\n        final RelationMemberList members = members();\n        final JsonArray membersArray = new JsonArray();\n        properties.add(\"members\", membersArray);\n        for (final RelationMember member : members)\n        {\n            final JsonObject memberObject = new JsonObject();\n            membersArray.add(memberObject);\n            final AtlasEntity entity = member.getEntity();\n            if (entity != null)\n            {\n                final long identifier = entity.getIdentifier();\n                memberObject.addProperty(GeoJsonUtils.IDENTIFIER, identifier);\n                memberObject.addProperty(\"itemType\", entity.getType().name());\n            }\n            else\n            {\n                // We shouldn't get here, but if we do, let's know about it in the data...\n                memberObject.addProperty(GeoJsonUtils.IDENTIFIER, \"MISSING\");\n                logger.warn(\"Missing identifier for relation entity: Relation ID: {}\",\n                        getIdentifier());\n            }\n\n            // Sometimes a member doesnt have a role. That's normal.\n            final String role = member.getRole();\n            if (role != null)\n            {\n                // And sometimes the role is \"\", but we should keep it that way...\n                memberObject.addProperty(\"role\", role);\n            }\n        }\n    }\n\n    private boolean isUnenclosedArea(final AtlasEntity entity, final GeometricSurface surface)\n    {\n        return entity instanceof Area\n                && !surface.fullyGeometricallyEncloses(((Area) entity).asPolygon());\n    }\n\n    private boolean isUnenclosedLineItem(final AtlasEntity entity, final GeometricSurface surface)\n    {\n        return entity instanceof LineItem\n                && !surface.fullyGeometricallyEncloses(((LineItem) entity).asPolyLine());\n    }\n\n    private boolean isUnenclosedLocationItem(final AtlasEntity entity,\n            final GeometricSurface surface)\n    {\n        return entity instanceof LocationItem\n                && !surface.fullyGeometricallyEncloses(((LocationItem) entity).getLocation());\n    }\n\n    private boolean isUnenclosedNonRelationEntity(final GeometricSurface surface,\n            final AtlasEntity entity)\n    {\n        switch (entity.getType())\n        {\n            case NODE:\n            case POINT:\n                return isUnenclosedLocationItem(entity, surface);\n            case EDGE:\n            case LINE:\n                return isUnenclosedLineItem(entity, surface);\n            case AREA:\n                return isUnenclosedArea(entity, surface);\n            case RELATION:\n            default:\n                throw new CoreException(\"Relations not supported in this method\");\n        }\n    }\n\n    private Stream<AtlasItem> leafMembers()\n    {\n        final Stream<AtlasItem> nonRelationMembers = members().stream()\n                .map(RelationMember::getEntity).filter(entity -> !(entity instanceof Relation))\n                .map(entity -> (AtlasItem) entity);\n        final Stream<AtlasItem> relationMembers = members().stream().map(RelationMember::getEntity)\n                .filter(entity -> entity instanceof Relation).map(entity -> (Relation) entity)\n                .flatMap(Relation::leafMembers);\n        return Stream.concat(nonRelationMembers, relationMembers);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/RelationMember.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonFeature;\n\nimport com.google.gson.JsonObject;\n\n/**\n * A {@link Relation} member. It has a role and an {@link AtlasEntity}.\n *\n * @author matthieun\n */\npublic class RelationMember implements Comparable<RelationMember>, Located, GeoJsonFeature\n{\n    private final String role;\n    private final AtlasEntity entity;\n    private final long relationIdentifier;\n\n    public RelationMember(final String role, final AtlasEntity entity,\n            final long relationIdentifier)\n    {\n        this.role = role;\n        this.entity = entity;\n        this.relationIdentifier = relationIdentifier;\n    }\n\n    @Override\n    public JsonObject asGeoJsonGeometry()\n    {\n        return this.entity.asGeoJsonGeometry();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.entity.bounds();\n    }\n\n    @Override\n    public int compareTo(final RelationMember other)\n    {\n        // Order by type first, then by identifier, then by role\n        final int itemTypeValue1 = this.getEntity().getType().getValue();\n        final int itemTypeValue2 = other.getEntity().getType().getValue();\n        final int deltaTypeValue = itemTypeValue1 - itemTypeValue2;\n        if (deltaTypeValue > 0)\n        {\n            return 1;\n        }\n        else if (deltaTypeValue < 0)\n        {\n            return -1;\n        }\n        else\n        {\n            final long identifier1 = this.getEntity().getIdentifier();\n            final long identifier2 = other.getEntity().getIdentifier();\n            final int comparisonMarker = Long.compare(identifier1, identifier2);\n            if (comparisonMarker != 0)\n            {\n                return comparisonMarker;\n            }\n            else\n            {\n                final String thisRole = this.getRole();\n                final String otherRole = other.getRole();\n                if (thisRole == null && otherRole == null)\n                {\n                    return 0;\n                }\n                if (thisRole == null)\n                {\n                    return -1;\n                }\n                if (otherRole == null)\n                {\n                    return 1;\n                }\n                return thisRole.compareTo(otherRole);\n            }\n        }\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof RelationMember)\n        {\n            final RelationMember that = (RelationMember) other;\n            return StringUtils.equals(this.getRole(), that.getRole())\n                    && this.getRelationIdentifier() == that.getRelationIdentifier()\n                    && this.entity.getIdentifier() == that.getEntity().getIdentifier();\n        }\n        return false;\n    }\n\n    /**\n     * @return The {@link AtlasEntity} pointed out by this relation. null if the {@link Atlas} that\n     *         created it has a sliced {@link Relation}.\n     */\n    public AtlasEntity getEntity()\n    {\n        return this.entity;\n    }\n\n    @Override\n    public JsonObject getGeoJsonProperties()\n    {\n        final JsonObject properties = this.entity.getGeoJsonProperties();\n        properties.addProperty(\"role\", this.role);\n        return properties;\n    }\n\n    public long getRelationIdentifier()\n    {\n        return this.relationIdentifier;\n    }\n\n    public String getRole()\n    {\n        return this.role;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.role.hashCode() + this.entity.hashCode()\n                + Long.hashCode(this.relationIdentifier);\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"{Member: ID = \");\n        builder.append(this.getEntity().getIdentifier());\n        builder.append(\", Type = \");\n        builder.append(this.getEntity().getType());\n        builder.append(\", Role = \");\n        builder.append(this.getRole());\n        builder.append(\"}\");\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/RelationMemberList.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.AbstractCollection;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Collector;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedRelation;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class RelationMemberList extends AbstractCollection<RelationMember> implements Located\n{\n    private final List<RelationMember> members;\n\n    /**\n     * This set has no concept of how many {@link RelationBeanItem}s of a given value have been\n     * removed. Technically, OSM allows for duplicate {@link RelationBeanItem}s in a given relation.\n     * However, these duplicates are disallowed by {@link PackedAtlas#relationMembers} and by\n     * extension {@link PackedRelation#members}. As a result, we need not worry about that edge case\n     * here.\n     */\n    private final Set<RelationBeanItem> explicitlyExcluded;\n\n    /**\n     * A {@link Collectors#collectingAndThen(Collector, Function)} wrapper for\n     * {@link RelationMember}s.\n     *\n     * @return - the collector.\n     */\n    public static Collector<RelationMember, ? extends Object, RelationMemberList> collect()\n    {\n        return Collectors.collectingAndThen(Collectors.toList(), RelationMemberList::new);\n    }\n\n    public RelationMemberList(final Iterable<RelationMember> members)\n    {\n        if (members instanceof List)\n        {\n            this.members = (List<RelationMember>) members;\n        }\n        else\n        {\n            this.members = new ArrayList<>();\n            members.forEach(this.members::add);\n        }\n        this.explicitlyExcluded = new HashSet<>();\n    }\n\n    @Override\n    public boolean add(final RelationMember item)\n    {\n        return this.members.add(item);\n    }\n\n    public void addItemExplicitlyExcluded(final RelationBeanItem item)\n    {\n        this.explicitlyExcluded.add(item);\n    }\n\n    public RelationBean asBean()\n    {\n        final RelationBean result = new RelationBean();\n        for (final RelationMember member : this.members)\n        {\n            result.addItem(member.getEntity().getIdentifier(), member.getRole(),\n                    member.getEntity().getType());\n        }\n        this.explicitlyExcluded.forEach(result::addItemExplicitlyExcluded);\n        return result;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return Rectangle.forLocated(this.members);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof RelationMemberList)\n        {\n            final RelationMemberList that = (RelationMemberList) other;\n            if (this.getMemberList().size() != that.getMemberList().size())\n            {\n                return false;\n            }\n            int index = 0;\n            for (final RelationMember thisMember : this.members)\n            {\n                final RelationMember thatMember = that.get(index++);\n                if (thisMember == null && thatMember != null\n                        || thisMember != null && thatMember == null)\n                {\n                    return false;\n                }\n                if (thisMember == null && thatMember == null)\n                {\n                    continue;\n                }\n                if (!thisMember.equals(thatMember))\n                {\n                    return false;\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Check if the two {@link RelationMemberList}s are the same, without looking at the List order.\n     * Also, ensure that their explicitlyExcluded sets match.\n     *\n     * @param other\n     *            The other object\n     * @return True if the other object satisfies {@link RelationMemberList#equals(Object)} AND has\n     *         a matching explicitlyExcluded set.\n     */\n    public boolean equalsIncludingExplicitlyExcluded(final Object other)\n    {\n        if (other instanceof RelationMemberList)\n        {\n            final boolean basicEquals = this.equals(other);\n            final RelationMemberList otherBean = (RelationMemberList) other;\n            return basicEquals && this.explicitlyExcluded.equals(otherBean.explicitlyExcluded);\n        }\n        return false;\n    }\n\n    public RelationMember get(final int index)\n    {\n        if (index < 0 || index >= size())\n        {\n            throw new CoreException(\n                    \"No RelationMember with index {}. This list has only {} members.\", index,\n                    size());\n        }\n        return this.members.get(index);\n    }\n\n    public RelationMember get(final long identifier, final ItemType type)\n    {\n        for (final RelationMember member : this)\n        {\n            if (member.getEntity().getIdentifier() == identifier)\n            {\n                switch (type)\n                {\n                    case NODE:\n                        if (member.getEntity() instanceof Node)\n                        {\n                            return member;\n                        }\n                        break;\n                    case EDGE:\n                        if (member.getEntity() instanceof Edge)\n                        {\n                            return member;\n                        }\n                        break;\n                    case AREA:\n                        if (member.getEntity() instanceof Area)\n                        {\n                            return member;\n                        }\n                        break;\n                    case LINE:\n                        if (member.getEntity() instanceof Line)\n                        {\n                            return member;\n                        }\n                        break;\n                    case POINT:\n                        if (member.getEntity() instanceof Point)\n                        {\n                            return member;\n                        }\n                        break;\n                    case RELATION:\n                        if (member.getEntity() instanceof Relation)\n                        {\n                            return member;\n                        }\n                        break;\n                    default:\n                        break;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.members.hashCode();\n    }\n\n    @Override\n    public Iterator<RelationMember> iterator()\n    {\n        return Iterables.filter(this.members, member -> member != null).iterator();\n    }\n\n    @Override\n    public int size()\n    {\n        return this.members.size();\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.members.toString();\n    }\n\n    private List<RelationMember> getMemberList()\n    {\n        return this.members;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/Route.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.apache.commons.lang3.builder.CompareToBuilder;\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Heading;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A route is a set of {@link Edge}s that are connected to each other.\n *\n * @author matthieun\n * @author Sid\n * @author mgostintsev\n */\n@SuppressWarnings(\"serial\")\npublic abstract class Route implements Iterable<Edge>, Located, Serializable\n{\n    /**\n     * A {@link Route} implementation made of many {@link Edge}s.\n     *\n     * @author matthieun\n     */\n    // NOSONAR here to override \"Subclasses that add fields should override \"equals\" (squid:S2160)\"\n    // as the parent equals is good enough.\n    private static final class MultiRoute extends Route // NOSONAR\n    {\n        private static final long serialVersionUID = -4562811506650155750L;\n        private final Route upstream;\n        private final Route downstream;\n\n        private MultiRoute(final Route upstream, final Route downstream)\n        {\n            this.upstream = upstream;\n            this.downstream = downstream;\n        }\n\n        @Override\n        public PolyLine asPolyLine()\n        {\n            final PolyLine one = this.upstream.asPolyLine();\n            final PolyLine two = this.downstream.asPolyLine();\n            final List<Location> points = new ArrayList<>();\n            one.forEach(points::add);\n            for (int i = 1; i < two.size(); i++)\n            {\n                points.add(two.get(i));\n            }\n            return new PolyLine(points);\n        }\n\n        @Override\n        public Rectangle bounds()\n        {\n            return Rectangle.forLocated(this.upstream, this.downstream);\n        }\n\n        @Override\n        public Edge end()\n        {\n            return this.downstream.end();\n        }\n\n        @Override\n        public Edge get(final int index)\n        {\n            final int size = size();\n            if (index < 0 || index >= size)\n            {\n                throw new CoreException(\"Index {} out of Route's bounds: size = {}\", index, size);\n            }\n            else\n            {\n                final int upstreamSize = this.upstream.size();\n                if (index < upstreamSize)\n                {\n                    return this.upstream.get(index);\n                }\n                else\n                {\n                    return this.downstream.get(index - upstreamSize);\n                }\n            }\n        }\n\n        @Override\n        public int indexOf(final Edge edge)\n        {\n            final int indexUp = this.upstream.indexOf(edge);\n            if (indexUp >= 0)\n            {\n                return indexUp;\n            }\n            final int indexDown = this.downstream.indexOf(edge);\n            if (indexDown >= 0)\n            {\n                return this.upstream.size() + indexDown;\n            }\n            return indexDown;\n        }\n\n        @Override\n        public Iterator<Edge> iterator()\n        {\n            return new MultiIterable<>(this.upstream, this.downstream).iterator();\n        }\n\n        @Override\n        public Distance length()\n        {\n            return this.upstream.length().add(this.downstream.length());\n        }\n\n        @Override\n        public List<Node> nodes()\n        {\n            final List<Node> nodes = new ArrayList<>();\n            nodes.addAll(this.upstream.nodes());\n            final List<Node> downNodes = this.downstream.nodes();\n            for (int i = 1; i < downNodes.size(); i++)\n            {\n                nodes.add(downNodes.get(i));\n            }\n            return nodes;\n        }\n\n        @Override\n        public Optional<Route> reverse()\n        {\n            Route reversed = null;\n            final Iterator<Edge> iterator = this.iterator();\n            while (iterator.hasNext())\n            {\n                final Edge edge = iterator.next();\n                if (edge.hasReverseEdge())\n                {\n                    final Edge reverse = edge.reversed().orElseThrow(\n                            () -> new CoreException(\"Edge should have a reverse edge.\"));\n                    if (reversed == null)\n                    {\n                        reversed = Route.forEdge(reverse);\n                    }\n                    else\n                    {\n                        reversed = reversed.prepend(reverse);\n                    }\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }\n            return Optional.ofNullable(reversed);\n        }\n\n        @Override\n        public int size()\n        {\n            return this.upstream.size() + this.downstream.size();\n        }\n\n        @Override\n        public Edge start()\n        {\n            return this.upstream.start();\n        }\n    }\n\n    /**\n     * A {@link Route} made of a single {@link Edge}\n     *\n     * @author matthieun\n     */\n    // NOSONAR here to override \"Subclasses that add fields should override \"equals\" (squid:S2160)\"\n    // as the parent equals is good enough.\n    private static final class SingleRoute extends Route // NOSONAR\n    {\n        private static final long serialVersionUID = -3870416343539125425L;\n        private final Edge edge;\n\n        SingleRoute(final Edge edge)\n        {\n            this.edge = edge;\n        }\n\n        @Override\n        public PolyLine asPolyLine()\n        {\n            return this.edge.asPolyLine();\n        }\n\n        @Override\n        public Rectangle bounds()\n        {\n            return this.edge.bounds();\n        }\n\n        @Override\n        public Edge end()\n        {\n            return this.edge;\n        }\n\n        @Override\n        public Edge get(final int index)\n        {\n            if (index != 0)\n            {\n                throw new CoreException(\"Invalid SingleRoute index: {}. Only 0 is permitted.\",\n                        index);\n            }\n            return this.edge;\n        }\n\n        @Override\n        public int indexOf(final Edge edge)\n        {\n            return this.edge.getIdentifier() == edge.getIdentifier() ? 0 : -1;\n        }\n\n        @Override\n        public Iterator<Edge> iterator()\n        {\n            return Iterables.from(this.edge).iterator();\n        }\n\n        @Override\n        public Distance length()\n        {\n            return this.edge.length();\n        }\n\n        @Override\n        public List<Node> nodes()\n        {\n            final List<Node> result = new ArrayList<>();\n            result.add(this.edge.start());\n            result.add(this.edge.end());\n            return result;\n        }\n\n        @Override\n        public Optional<Route> reverse()\n        {\n            return this.edge.hasReverseEdge()\n                    ? Optional.of(new SingleRoute(this.edge.reversed()\n                            .orElseThrow(() -> new CoreException(\"Edge should have a reverse.\"))))\n                    : Optional.empty();\n        }\n\n        @Override\n        public int size()\n        {\n            return 1;\n        }\n\n        @Override\n        public Edge start()\n        {\n            return this.edge;\n        }\n    }\n\n    /**\n     * Comparator that sorts {@link Route}s from longest to shortest and then by individual\n     * {@link Route} hashCode.\n     */\n    public static final Comparator<Route> ROUTE_COMPARATOR = (final Route route1,\n            final Route route2) -> new CompareToBuilder().append(route2.size(), route1.size())\n                    .append(route1.hashCode(), route2.hashCode()).toComparison();\n\n    private static final Logger logger = LoggerFactory.getLogger(Route.class);\n\n    /**\n     * Given a set of {@link Edge}s, which may or may not have reverse {@link Edge}s, build a\n     * {@link Route} that uses each unique {@link Edge} exactly once. Throws an exception if it\n     * cannot build a {@link Route}.\n     *\n     * @param candidates\n     *            The {@link Edge}s\n     * @param startNode\n     *            Starting {@link Node} of the {@link Route}\n     * @param endNode\n     *            Ending {@link Node} of the {@link Route}\n     * @return The corresponding {@link Route}.\n     */\n    public static Route buildFullRouteIgnoringReverseEdges(final Set<Edge> candidates,\n            final Node startNode, final Node endNode)\n    {\n        Route route = null;\n        int numberOfConsecutiveFailures = 0;\n        final long maxEdgesToAdd = candidates.stream().map(edge -> edge.getMainEdgeIdentifier())\n                .distinct().count();\n        final Set<Long> idsAdded = new HashSet<>();\n        if (maxEdgesToAdd == 0)\n        {\n            throw new CoreException(\"Can't have a route with no members\");\n        }\n        while (route == null || route.size() < maxEdgesToAdd\n                && !(route.start().start().equals(startNode) && route.end().end().equals(endNode)))\n        {\n            if (route == null)\n            {\n                // Find an edge that connects to the startNode\n                for (final Edge edge : candidates)\n                {\n                    if (edge.start().equals(startNode))\n                    {\n                        route = Route.forEdge(edge);\n                        idsAdded.add(edge.getMainEdgeIdentifier());\n                        break;\n                    }\n                }\n                if (route == null)\n                {\n                    throw new CoreException(\n                            \"Can't find an edge that connects to the startNode. StartNode: {} EndNode: {}\",\n                            startNode.getIdentifier(), endNode.getIdentifier());\n                }\n            }\n            else\n            {\n                boolean edgeAdded = false;\n\n                for (final Edge edge : candidates)\n                {\n                    if (idsAdded.contains(edge.getMainEdgeIdentifier()))\n                    {\n                        // this edge or reverseEdge is already used, continue\n                        continue;\n                    }\n                    // Can use equals here, as the items all come from the same atlas.\n                    // Note: Here the order has a great importance. It is edge start to route\n                    // end before edge end to route start, otherwise all the self-intersecting\n                    // osm ways will not be able to create a route. In the case of MultiAtlas\n                    // re-creating ways that have been mis-way-sectioned at borders, this is because\n                    // the edges are sorted in ascending order and processed here in the same order.\n                    if (edge.start().equals(route.end().end()))\n                    {\n                        edgeAdded = true;\n                        numberOfConsecutiveFailures = 0;\n                        route = route.append(edge);\n                        idsAdded.add(edge.getMainEdgeIdentifier());\n                        break;\n                    }\n                }\n\n                // To ensure there's no infinite loop, number of consecutive loops where an edge is\n                // not added cannot exceed the total number of unique edges passed in\n                if (!edgeAdded && ++numberOfConsecutiveFailures >= maxEdgesToAdd)\n                {\n                    throw new CoreException(\n                            \"No edge that connects to the current route. StartNode: {} EndNode: {}\",\n                            startNode.getIdentifier(), endNode.getIdentifier());\n                }\n            }\n        }\n\n        if (route.size() != maxEdgesToAdd)\n        {\n            throw new CoreException(\n                    \"A route was found from start to end, but not every unique edge was used. StartNode: {} EndNode: {}\",\n                    startNode.getIdentifier(), endNode.getIdentifier());\n        }\n\n        return route;\n    }\n\n    /**\n     * Create a {@link Route} from a single {@link Edge}\n     *\n     * @param edge\n     *            The {@link Edge}\n     * @return The single-{@link Edge} {@link Route}\n     */\n    public static Route forEdge(final Edge edge)\n    {\n        if (edge == null)\n        {\n            throw new CoreException(\"Cannot create a Route from a null Edge.\");\n        }\n        return new SingleRoute(edge);\n    }\n\n    /**\n     * Create a {@link Route} from an {@link Iterable} of {@link Edge}s that are already in the\n     * proper order to be connected.\n     *\n     * @param edges\n     *            The {@link Edge}s to link in a {@link Route}\n     * @return The corresponding {@link Route}\n     */\n    public static Route forEdges(final Edge... edges)\n    {\n        return forEdges(Iterables.asList(edges));\n    }\n\n    /**\n     * Create a {@link Route} from an {@link Iterable} of {@link Edge}s that are already in the\n     * proper order to be connected.\n     *\n     * @param edges\n     *            The {@link Edge}s to link in a {@link Route}\n     * @return The corresponding {@link Route}\n     */\n    public static Route forEdges(final Iterable<Edge> edges)\n    {\n        if (!edges.iterator().hasNext())\n        {\n            throw new CoreException(\"Cannot have no edges\");\n        }\n        int counter = 0;\n        Route result = null;\n        for (final Edge edge : edges)\n        {\n            if (counter == 0)\n            {\n                result = Route.forEdge(edge);\n            }\n            else\n            {\n                result = result.append(edge);\n            }\n            counter++;\n        }\n        return result;\n    }\n\n    public static Route forRoutes(final Iterable<Route> routes)\n    {\n        if (!routes.iterator().hasNext())\n        {\n            throw new CoreException(\"Cannot have no edges\");\n        }\n        Route result = null;\n        for (final Route route : routes)\n        {\n            if (result == null)\n            {\n                result = route;\n            }\n            else\n            {\n                result = result.append(route);\n            }\n        }\n        return result;\n    }\n\n    public static Route forRoutes(final Route... routes)\n    {\n        return forRoutes(Iterables.asList(routes));\n    }\n\n    /**\n     * Get a {@link Route} from a set of {@link Edge}s, that we assume are connected. However, this\n     * does not require the {@link Edge}s to be in any order. The order should be inferred by this\n     * method. Throws an exception if it cannot build a {@link Route}.\n     *\n     * @param candidates\n     *            The {@link Edge}s\n     * @param shuffle\n     *            When no {@link Route} is found on the first pass, if this is true, the set of\n     *            {@link Edge}s will be shuffled to find routes that might have been missed. This is\n     *            way slower.\n     * @return The corresponding {@link Route}.\n     */\n    public static Route fromNonArrangedEdgeSet(final Set<Edge> candidates, final boolean shuffle)\n    {\n        Route route = null;\n        int numberFailures = 0;\n        final List<Edge> members = new ArrayList<>();\n        members.addAll(candidates);\n        if (members.isEmpty())\n        {\n            throw new CoreException(\"Cannot have a route with no members\");\n        }\n        while (route == null || route.size() < members.size())\n        {\n            if (route == null)\n            {\n                route = Route.forEdge(members.iterator().next());\n            }\n            else\n            {\n                final int initialSize = route.size();\n                for (final Edge edge : members)\n                {\n                    if (route.includes(edge))\n                    {\n                        // this edge is already used, continue\n                        continue;\n                    }\n                    // Can use equals here, as the items all come from the same atlas.\n                    // Note: Here the order has a great importance. It is edge start to route\n                    // end before edge end to route start, otherwise all the self-intersecting\n                    // osm ways will not be able to create a route. In the case of MultiAtlas\n                    // re-creating ways that have been mis-way-sectioned at borders, this is because\n                    // the edges are sorted in ascending order and processed here in the same order.\n                    if (edge.start().equals(route.end().end()))\n                    {\n                        route = route.append(edge);\n                        break;\n                    }\n                    if (edge.end().equals(route.start().start()))\n                    {\n                        route = route.prepend(edge);\n                        break;\n                    }\n                }\n                if (initialSize + 1 != route.size())\n                {\n                    if (shuffle && ++numberFailures < candidates.size())\n                    {\n                        // The user suggested that the algorithm is sensitive to which edge is the\n                        // first in case of loops\n                        // Try another first edge\n                        final Edge firstMember = members.remove(0);\n                        members.add(firstMember);\n                        // Make the loop restart\n                        route = null;\n                    }\n                    else\n                    {\n                        // Format and throw an exception.\n                        final StringList edges = new StringList();\n                        final StringList debug = new StringList();\n                        candidates.forEach(edge -> edges.add(edge.getIdentifier()));\n                        candidates.forEach(edge -> debug.add(edge.asPolyLine().toWkt()));\n                        throw new CoreException(\n                                \"Unable to build a route from edges {}\\nLocations:\\n{}\",\n                                edges.join(\", \"), debug.join(\"\\n\"));\n                    }\n                }\n            }\n        }\n        return route;\n    }\n\n    protected Route()\n    {\n    }\n\n    public Route append(final Edge edge)\n    {\n        return append(Route.forEdge(edge));\n    }\n\n    public Route append(final Route route)\n    {\n        if (route == null)\n        {\n            throw new CoreException(\n                    \"Cannot append a route that is null to a route {} that ends at {}\", this,\n                    this.end());\n        }\n        if (!end().end().equals(route.start().start()))\n        {\n            throw new CoreException(\n                    \"Cannot append a disconnected route:\\nOne: {}\\nAt: {}\\nTo\\nTwo: {}\\nAt: {}\",\n                    this, this.end(), route, route.start());\n        }\n        return new MultiRoute(this, route);\n    }\n\n    public abstract PolyLine asPolyLine();\n\n    /**\n     * @return All the {@link Edge}s connected to the start/end {@link Node}s of this {@link Route},\n     *         excluding {@link Edge}s in {@link Route}. If this {@link Edge} is a {@link Route}\n     *         with two-way road, then the reversed {@link Edge}s will be included in the set. This\n     *         does not include {@link Edge} connected to the interior {@link Node}s of the\n     *         {@link Route}.\n     */\n    public Set<Edge> connectedEdges()\n    {\n        final Set<Edge> result = new HashSet<>();\n        for (final Edge edge : this.end().end().connectedEdges())\n        {\n            if (!this.includes(edge))\n            {\n                result.add(edge);\n            }\n        }\n        for (final Edge edge : this.start().start().connectedEdges())\n        {\n            if (!this.includes(edge))\n            {\n                result.add(edge);\n            }\n        }\n        return result;\n    }\n\n    public abstract Edge end();\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof Route)\n        {\n            final Route that = (Route) other;\n            if (this.size() == that.size())\n            {\n                return new EqualsBuilder()\n                        .append(this.start().start().getLocation(),\n                                that.start().start().getLocation())\n                        .append(this.end().end().getLocation(), that.end().end().getLocation())\n                        .append(Iterables.asList(this), Iterables.asList(that)).isEquals();\n            }\n        }\n        return false;\n    }\n\n    /**\n     * @param index\n     *            An index in the {@link Route}\n     * @return The {@link Edge} at the specified index in the {@link Route}\n     */\n    public abstract Edge get(int index);\n\n    /**\n     * Note: The start and end {@link Node}s of the {@link Route} are part of the hash code to\n     * reduce the probability of a collision. There are other candidates to add here, like distance\n     * between start/end, but start/end by themselves are the least computationally intensive to\n     * derive.\n     */\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.start().start().getLocation())\n                .append(this.end().end().getLocation()).append(Iterables.asList(this)).hashCode();\n    }\n\n    /**\n     * @param edge\n     *            The {@link Edge} to test for\n     * @return true if the {@link Edge} provided belongs in the {@link Route}\n     */\n    public boolean includes(final Edge edge)\n    {\n        return this.indexOf(edge) >= 0;\n    }\n\n    /**\n     * @param edge\n     *            The {@link Edge} to test for\n     * @return The index of the {@link Edge} in the {@link Route} if it is included, -1 otherwise.\n     */\n    public abstract int indexOf(Edge edge);\n\n    /**\n     * Identifies whether the entire given {@link Route} is overlapping.\n     *\n     * @param route\n     *            The {@link Route} to check\n     * @return true if the given {@link Route} is overlapping\n     */\n    public boolean isOverlapping(final Route route)\n    {\n        return overlapIndex(route) > -1;\n    }\n\n    /**\n     * Identifies whether any of the given {@link Route} is entirely overlapping this one.\n     *\n     * @param routes\n     *            The {@link Iterable} of {@link Route}s to check\n     * @return true if any of the given {@link Route}s is overlapping\n     */\n    public boolean isOverlappingForAtLeastOneOf(final Iterable<Route> routes)\n    {\n        return overlapIndex(routes) > -1;\n    }\n\n    /**\n     * Identifies whether this @{link Route} is a simple U-Turn (route follows along a path to a\n     * point and returns the exact same way it came in).\n     * <p>\n     * NOTE: A route could still be a U-Turn that doesn't follow an identical path out based on\n     * {@link Heading}, but this method won't catch that case.\n     *\n     * @return true if this route is a simple U-Turn\n     */\n    public boolean isSimpleUTurn()\n    {\n        final int numberOfEdges = this.size();\n\n        // A simple UTurn route cannot have an odd number of edges\n        if (numberOfEdges % 2 == 1)\n        {\n            return false;\n        }\n\n        int index = 0;\n\n        // Start by comparing the first and last edge, and incrementally move in from each side.\n        // Stop after we compare the middle two edges\n        while (index < numberOfEdges / 2)\n        {\n            // If any comparison doesn't match, it's not a simple U-Turn\n            if (!this.get(index).isReversedEdge(this.get(numberOfEdges - index - 1)))\n            {\n                return false;\n            }\n\n            index++;\n        }\n\n        // If the comparisons all succeeded, it's a simple U-Turn!\n        return true;\n    }\n\n    /**\n     * Identifies whether the given {@link Route} is a sub-route of this one. If the given\n     * {@link Route} is greater than this {@link Route}, this method will return false.\n     *\n     * @param route\n     *            The {@link Route} to check\n     * @return true if the given {@link Route} is a sub-route\n     */\n    public boolean isSubRoute(final Route route)\n    {\n        return subRouteIndex(route) > -1;\n    }\n\n    /**\n     * Identifies whether any of the given {@link Route} is a sub-route of this one.\n     *\n     * @param routes\n     *            The {@link Iterable} of {@link Route}s to check\n     * @return true if any of the given {@link Route}s is a sub-route.\n     */\n    public boolean isSubRouteForAtLeastOneOf(final Iterable<Route> routes)\n    {\n        return subRouteIndex(routes) > -1;\n    }\n\n    /**\n     * @return true if this {@link Route} contains a Turn Restriction given OSM's definition.\n     */\n    public boolean isTurnRestriction()\n    {\n        return TurnRestriction.isTurnRestriction(this);\n    }\n\n    public abstract Distance length();\n\n    public abstract List<Node> nodes();\n\n    /**\n     * Calculates the first occurring overlapping index from the given {@link Route}s. For details,\n     * see {@link #overlapIndex(Route)}.\n     *\n     * @param routes\n     *            The {@link Route}s to compare with\n     * @return first occurring calculated index\n     */\n    public int overlapIndex(final Iterable<Route> routes)\n    {\n        for (final Route route : routes)\n        {\n            final int overlapIndex = overlapIndex(route);\n            if (overlapIndex > -1)\n            {\n                return overlapIndex;\n            }\n        }\n\n        return -1;\n    }\n\n    /**\n     * Calculates the index of the last {@link Edge} from this {@link Route} that overlaps the given\n     * {@link Route}. If there is no overlap, -1 is returned. Note: The given {@link Route} can be\n     * of any size. Example 1 - this route: [A,B], given route: [A,B,C] will return 1 since the last\n     * overlap occurs at Edge B, index 1 for this route. Example 2 - this route: [A,B,C], given\n     * route: [C] will return 2, since overlap is at C, index 2 for this route.\n     *\n     * @param route\n     *            The {@link Route} to compare with\n     * @return the calculated index\n     */\n    public int overlapIndex(final Route route)\n    {\n        int overlapIndex;\n        boolean givenPathIsLonger = false;\n\n        // Find overlap index relative to this route, but use the longer of the two routes to find\n        // the overlap section\n        if (route.size() > this.size())\n        {\n            givenPathIsLonger = true;\n            overlapIndex = route.subRouteIndex(this);\n        }\n        else\n        {\n            overlapIndex = subRouteIndex(route);\n        }\n\n        // If there is an overlap and the given route was longer, go back and find the index for the\n        // overlapping edge in this route\n        if (givenPathIsLonger && overlapIndex > -1)\n        {\n            final Edge lastOverlap = route.get(overlapIndex);\n            if (this.includes(lastOverlap))\n            {\n                return this.indexOf(lastOverlap);\n            }\n\n            logger.error(\"Detected overlap at edge {}, but unable to find in current route {}\",\n                    lastOverlap.getIdentifier(), this);\n\n            overlapIndex = -1;\n        }\n\n        return overlapIndex;\n    }\n\n    public Route prepend(final Edge edge)\n    {\n        return prepend(Route.forEdge(edge));\n    }\n\n    public Route prepend(final Route route)\n    {\n        if (!start().start().equals(route.end().end()))\n        {\n            throw new CoreException(\"Cannot prepend a disconnected route.\");\n        }\n        return new MultiRoute(route, this);\n    }\n\n    /**\n     * @return The reversed {@link Route}, if all the reversed {@link Edge}s exist.\n     */\n    public abstract Optional<Route> reverse();\n\n    /**\n     * @return The number of {@link Edge}s in this {@link Route}\n     */\n    public abstract int size();\n\n    /**\n     * @return The first {@link Edge} in this route\n     */\n    public abstract Edge start();\n\n    /**\n     * Determine if this route starts with the {@link Route} passed in.\n     *\n     * @param other\n     *            The {@link Route} to compare\n     * @return true if this route starts with the route passed in\n     */\n    public boolean startsWith(final Route other)\n    {\n        // If the other route is longer than this route, return false\n        if (other.size() > this.size())\n        {\n            return false;\n        }\n\n        final Iterator<Edge> otherIterator = other.iterator();\n        final Iterator<Edge> thisIterator = this.iterator();\n\n        // otherIterator has to be shorter than thisIterator, so loop over otherIterator\n        while (otherIterator.hasNext())\n        {\n            final Edge otherEdge = otherIterator.next();\n            final Edge thisEdge = thisIterator.next();\n\n            if (!thisEdge.equals(otherEdge))\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Creates a new {@link Route} from the original route based on the start and end indexes passed\n     * in\n     *\n     * @param startIndex\n     *            The starting index to create the new route from\n     * @param endIndex\n     *            The ending index (exclusive) of the new route\n     * @return a new route based which is a subset of the original route\n     */\n    public Route subRoute(final int startIndex, final int endIndex)\n    {\n        // Create a new ArrayList for safety reasons because subList returns a list backed by the\n        // original list.\n        return Route\n                .forEdges(new ArrayList<>(Iterables.asList(this).subList(startIndex, endIndex)));\n    }\n\n    /**\n     * Calculates the first occurring subRoute index from the given {@link Route}s. For details, see\n     * {@link #subRouteIndex(Route)}.\n     *\n     * @param routes\n     *            The {@link Route}s to compare with\n     * @return first occurring calculated index\n     */\n    public int subRouteIndex(final Iterable<Route> routes)\n    {\n        for (final Route route : routes)\n        {\n            final int overlapIndex = subRouteIndex(route);\n            if (overlapIndex > -1)\n            {\n                return overlapIndex;\n            }\n        }\n\n        return -1;\n    }\n\n    /**\n     * Calculates the index of the last {@link Edge} from this {@link Route} that overlaps the given\n     * {@link Route}. If there is no overlap, -1 is returned. Note: The given {@link Route} must be\n     * shorter than or equal to this {@link Route}. Example: This route: [A,B]. Given route: [A,B,C]\n     * will return -1 since given route goes beyond this route. To avoid the size constraint, and\n     * detect any overlap, use {@link #overlapIndex(Route)} instead.\n     *\n     * @param route\n     *            The {@link Route} to compare with\n     * @return the calculated index\n     */\n    public int subRouteIndex(final Route route)\n    {\n        // Keep track of the last index at which the last Edge was overlapping this route, to avoid\n        // returning false positives in case of routes making a loop.\n        int lastOverlapIndex = -1;\n\n        // Fail-fast optimization\n        if (route == null || route.size() > this.size())\n        {\n            return -1;\n        }\n\n        for (final Edge routeEdge : route)\n        {\n            final int index = this.indexOf(routeEdge);\n            if (index <= lastOverlapIndex)\n            {\n                // The edge does not overlap, or it does but at a smaller index which would indicate\n                // a loop.\n                return -1;\n            }\n            lastOverlapIndex = index;\n        }\n        return lastOverlapIndex;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[Route: \");\n        final StringList edgeIdentifiers = new StringList();\n        this.forEach(edge -> edgeIdentifiers.add(String.valueOf(edge.getIdentifier())));\n        builder.append(edgeIdentifiers.join(\", \"));\n        builder.append(\"]\");\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/SnappedEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.Snapper.SnappedLocation;\n\n/**\n * {@link SnappedLocation} on an {@link Edge}\n *\n * @author matthieun\n */\npublic class SnappedEdge extends SnappedLocation\n{\n    private static final long serialVersionUID = 804405113068154275L;\n\n    private final Edge edge;\n\n    public SnappedEdge(final SnappedLocation snap, final Edge edge)\n    {\n        super(snap.getOrigin(), snap, edge.asPolyLine());\n        this.edge = edge;\n    }\n\n    public Edge getEdge()\n    {\n        return this.edge;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[SnappedEdge: Edge: \");\n        builder.append(this.edge.getIdentifier());\n        builder.append(\", Origin: \");\n        builder.append(getOrigin());\n        builder.append(\", Snap: \");\n        builder.append(super.toString());\n        builder.append(\"]\");\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/SnappedLineItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.geography.Snapper;\n\n/**\n * {@link Snapper.SnappedLocation} on an {@link LineItem}\n *\n * @author chunzc\n */\npublic class SnappedLineItem extends Snapper.SnappedLocation\n{\n    private static final long serialVersionUID = 4935931723612324295L;\n    private final LineItem lineItem;\n\n    public SnappedLineItem(final Snapper.SnappedLocation snap, final LineItem lineItem)\n    {\n        super(snap.getOrigin(), snap, lineItem.asPolyLine());\n        this.lineItem = lineItem;\n    }\n\n    @Override\n    public boolean equals(final Object object)\n    {\n        if (object == null || object.getClass() != this.getClass())\n        {\n            return false;\n        }\n        final SnappedLineItem other = (SnappedLineItem) object;\n        return new EqualsBuilder()\n                .append(this.getLineItem().getIdentifier(), other.getLineItem().getIdentifier())\n                .append(this.getLineItem().asPolyLine(), other.getLineItem().asPolyLine())\n                .append(this.getLineItem().getTags(), other.getLineItem().getTags()).isEquals();\n    }\n\n    public LineItem getLineItem()\n    {\n        return this.lineItem;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.getLineItem().asPolyLine())\n                .append(this.getLineItem().getIdentifier()).append(this.getLineItem().getTags())\n                .toHashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[SnappedLineItem: LineItem: \");\n        builder.append(this.lineItem.getIdentifier());\n        builder.append(\", Origin: \");\n        builder.append(getOrigin());\n        builder.append(\", Snap: \");\n        builder.append(super.toString());\n        builder.append(\"]\");\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/TurnRestriction.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.TurnRestrictionTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * OSM Turn restriction, modeled from an {@link Atlas}.\n *\n * @author matthieun\n * @author sbhalekar\n */\npublic final class TurnRestriction implements Located, Serializable, Taggable\n{\n    /**\n     * The type of a {@link TurnRestriction}\n     *\n     * @author matthieun\n     */\n    public enum TurnRestrictionType\n    {\n        NO,\n        ONLY,\n        OTHER\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(TurnRestriction.class);\n\n    private static final long serialVersionUID = 7043701090121113526L;\n\n    private Route from;\n    private final Relation relation;\n    private Route route;\n    private Route too;\n    private final TurnRestrictionType type;\n    private Route via;\n    private String invalidReason = null;\n\n    /**\n     * Create a {@link TurnRestriction} from a {@link Relation}\n     *\n     * @param relation\n     *            The {@link Relation} to use\n     * @return An option on a {@link TurnRestriction}, which is filled if the {@link Relation} could\n     *         be translated to a {@link TurnRestriction}\n     */\n    public static Optional<TurnRestriction> from(final Relation relation)\n    {\n        final TurnRestriction turnRestriction = new TurnRestriction(relation);\n        if (turnRestriction.isValid())\n        {\n            return Optional.of(turnRestriction);\n        }\n        else\n        {\n            return Optional.empty();\n        }\n    }\n\n    public static TurnRestrictionType getTurnRestrictionType(final Relation relation)\n    {\n        if (TurnRestrictionTag.isNoPathRestriction(relation))\n        {\n            return TurnRestrictionType.NO;\n        }\n        else if (TurnRestrictionTag.isOnlyPathRestriction(relation))\n        {\n            return TurnRestrictionType.ONLY;\n        }\n        else\n        {\n            return TurnRestrictionType.OTHER;\n        }\n    }\n\n    /**\n     * Test if a {@link Route} is restricted.\n     *\n     * @param candidate\n     *            The {@link Route} to test\n     * @return true if the {@link Route} is restricted\n     */\n    public static boolean isTurnRestriction(final Route candidate)\n    {\n        for (final Edge edge : candidate)\n        {\n            final Set<Relation> relations = edge.relations();\n            for (final Relation relation : relations)\n            {\n                if (TurnRestrictionTag.isRestriction(relation))\n                {\n                    final Optional<TurnRestriction> turnRestrictionOption = TurnRestriction\n                            .from(relation);\n                    if (turnRestrictionOption.isPresent())\n                    {\n                        final TurnRestriction turnRestriction = turnRestrictionOption.get();\n                        final Route path = turnRestriction.route();\n                        switch (turnRestriction.getTurnRestrictionType())\n                        {\n                            case NO:\n                                // There is a \"No\" turn restriction on one edge of the path!\n                                if (path == null)\n                                {\n                                    continue;\n                                }\n\n                                if (candidate.isOverlapping(path)\n                                        && routeContainsAllTurnRestrictionParts(turnRestriction,\n                                                candidate))\n                                {\n                                    // All the edges in the turn restriction's path are included in\n                                    // the initial BigNode route and all of the turn restriction's\n                                    // parts (to/via/from) are on the path, so we can flag it as a\n                                    // turn restriction. The reason for the second piece of criteria\n                                    // is to avoid false positives that may overlap or contain\n                                    // pieces of the turn restriction path, but not the entire\n                                    // thing. For example: if there is a BigNode route that overlaps\n                                    // with a turn restriction's from and via, but not the to, then\n                                    // we cannot say it's a turn restriction.\n                                    return true;\n                                }\n                                break;\n                            case ONLY:\n                                // There is an \"Only\" turn restriction on one edge of the path\n                                // This is a tricky one.\n                                if (path == null)\n                                {\n                                    continue;\n                                }\n                                final Route from = turnRestriction.getFrom();\n\n                                /*\n                                 * The path should be a turn restriction if all the following\n                                 * criteria are met: 1. The path includes the from edge of the\n                                 * turnRestriction 2. The path isn't identical to the\n                                 * turnRestriction path (this is the ONLY case) 3. The path includes\n                                 * an edge which is connected to the via Node 4. The edge from the\n                                 * path that matches the from edge comes before the edge which is\n                                 * connected to the via node (this matters for u-turn scenarios)\n                                 */\n\n                                // First make sure the from route is found within the candidate\n                                // route and make sure the specified path is not a\n                                // subRoute of the candidate route\n                                if (candidate.isSubRoute(from) && !candidate.isSubRoute(path))\n                                {\n                                    final int fromSubRouteIndex = candidate.subRouteIndex(from);\n                                    final int routeEndIndex = candidate.size() - 1;\n\n                                    if (fromSubRouteIndex == routeEndIndex)\n                                    {\n                                        // If the from edge is the last edge in the route, the route\n                                        // should not be restricted as the only directive isn't\n                                        // broken, so continue searching.\n                                        continue;\n                                    }\n\n                                    // Create a partial route containing everything after the from\n                                    // edge(s).\n                                    // fromSubRouteIndex + 1 because the subRouteIndex returns the\n                                    // index of the last edge in the route. To create a partial\n                                    // route of everything after the from edge, we need the next\n                                    // index after the last from edge.\n                                    // routeEndIndex + 1 because subRoute's endingIndex is\n                                    // exclusive, so we need the next index to get the desired\n                                    // partialRoute\n                                    final Route partialRoute = candidate\n                                            .subRoute(fromSubRouteIndex + 1, routeEndIndex + 1);\n\n                                    if (turnRestriction.otherToOptions().stream()\n                                            .anyMatch(partialRoute::startsWith))\n                                    {\n                                        // If the partial route starts with any other to options,\n                                        // the only directive has not been followed and this path\n                                        // should be restricted\n                                        return true;\n                                    }\n                                }\n                                // If the path fully overlaps the \"only\" restriction, do not return\n                                // true, as the path might belong to other restrictions\n                                break;\n                            case OTHER:\n                                // There are some other (not in \"No\" or \"Only\") cases that we ignore\n                                logger.trace(\"Not using Other TurnRestrictionType: {}\",\n                                        turnRestriction.getTurnRestrictionType());\n                                return false;\n                            default:\n                                // Not in the NO, ONLY, or OTHER category: None expected unless\n                                // there is an expansion of TurnRestrictionTag enums\n                                throw new CoreException(\"Unknown TurnRestrictionType: {}\",\n                                        turnRestriction.getTurnRestrictionType());\n                        }\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * @param turnRestriction\n     *            The {@link TurnRestriction} to use for comparison\n     * @param route\n     *            The target {@link Route} to examine\n     * @return {@code true} if the given {@link Route} contains all parts - via/from/to edges\n     */\n    private static boolean routeContainsAllTurnRestrictionParts(\n            final TurnRestriction turnRestriction, final Route route)\n    {\n        final Optional<Route> possibleVia = turnRestriction.getVia();\n        boolean viaMatches = true;\n        if (possibleVia.isPresent())\n        {\n            viaMatches = route.isSubRoute(possibleVia.get());\n        }\n        return viaMatches && route.isSubRoute(turnRestriction.getTo())\n                && route.isSubRoute(turnRestriction.getFrom());\n    }\n\n    public TurnRestriction(final Relation relation)\n    {\n        Route fromMember = null;\n        Route viaMember = null;\n        Route toMember = null;\n        this.relation = relation;\n        this.type = getTurnRestrictionType(relation);\n        if (this.type == TurnRestrictionType.OTHER)\n        {\n            this.from = null;\n            this.via = null;\n            this.too = null;\n            return;\n        }\n        try\n        {\n            if (!TurnRestrictionTag.isRestriction(relation))\n            {\n                throw new CoreException(\"Relation {} is not a restriction.\", relation);\n            }\n            // Try to re-build the route, based on the \"from\", \"via\" and \"to\" members\n\n            // Build the via members\n            final Set<AtlasItem> viaMembers = relation.members().stream()\n                    .filter(member -> member.getRole().equals(RelationTypeTag.RESTRICTION_ROLE_VIA))\n                    .filter(member -> member.getEntity() instanceof Node\n                            || member.getEntity() instanceof Edge)\n                    .map(RelationMember::getEntity).map(entity -> (AtlasItem) entity)\n                    .collect(Collectors.toSet());\n\n            // According to OSM Wiki a restriction relation member can not have more than 1 via node\n            // https://wiki.openstreetmap.org/wiki/Relation:restriction#Members\n            // If the relation has more than 1 via node then discard the restriction as it is\n            // incorrectly modeled.\n            // To bring back the turn restriction OSM data needs to be modeled correctly\n            final long viaNodeCount = viaMembers.stream()\n                    .filter(atlasItem -> atlasItem instanceof Node).count();\n            if (viaNodeCount > 1)\n            {\n                throw new CoreException(\n                        \"Restriction relation should not have more than 1 via node. But, {} has {} via nodes\",\n                        relation.getOsmIdentifier(), viaNodeCount);\n            }\n\n            // If there are no via members, create a temporary unfiltered set of \"to\" items, to help\n            // with future filtering of \"from\" items by connectivity.\n            final Set<AtlasItem> temporaryToMembers = new HashSet<>();\n            if (viaMembers.isEmpty())\n            {\n                if (isSameRoadViaAndTo(relation))\n                {\n                    throw new CoreException(\n                            \"Relation {} has same members in from and to, but has no via members to disambiguate.\",\n                            relation.getIdentifier());\n                }\n                relation.members().stream().filter(\n                        member -> member.getRole().equals(RelationTypeTag.RESTRICTION_ROLE_TO))\n                        .forEach(member -> temporaryToMembers.add((AtlasItem) member.getEntity()));\n            }\n\n            // Filter the members to extract only the \"from\" members that are connected at the end\n            // to via members if any, or to the start of \"to\" members.\n            final Set<Edge> fromMembers = new TreeSet<>();\n            relation.members().stream().filter(member -> member.getRole()\n                    .equals(RelationTypeTag.RESTRICTION_ROLE_FROM)\n                    && member.getEntity() instanceof Edge\n                    && (!viaMembers.isEmpty()\n                            && ((Edge) member.getEntity()).isConnectedAtEndTo(viaMembers)\n                            || ((Edge) member.getEntity()).isConnectedAtEndTo(temporaryToMembers)))\n                    .forEach(member -> fromMembers.add((Edge) member.getEntity()));\n            fromMember = Route.fromNonArrangedEdgeSet(fromMembers, false);\n\n            // Filter the members to extract only the \"to\" members that are connected at the\n            // beginning to via members if any, or to the end of \"from\" members.\n            final Set<Edge> toMembers = new TreeSet<>();\n            relation.members().stream().filter(member -> member.getRole()\n                    .equals(RelationTypeTag.RESTRICTION_ROLE_TO)\n                    && member.getEntity() instanceof Edge\n                    && (!viaMembers.isEmpty()\n                            && ((Edge) member.getEntity()).isConnectedAtStartTo(viaMembers)\n                            || ((Edge) member.getEntity()).isConnectedAtStartTo(fromMembers)))\n                    .forEach(member -> toMembers.add((Edge) member.getEntity()));\n            toMember = Route.fromNonArrangedEdgeSet(toMembers, false);\n\n            // Take only the via members that are edges. Build this last to guarantee a route from\n            // from to too.\n            final Set<Edge> viaEdges = viaMembers.stream().filter(member -> member instanceof Edge)\n                    .map(member -> (Edge) member).collect(Collectors.toSet());\n\n            if (!viaEdges.isEmpty())\n            {\n                // It's possible that the via edge is bi-directional. To prevent both edges from\n                // being put into the route, build a route using all unique edges once. This method\n                // attempts to build a route from from the end of from to start of too.\n                viaMember = Route.buildFullRouteIgnoringReverseEdges(viaEdges,\n                        fromMember.end().end(), toMember.start().start());\n            }\n            this.from = fromMember;\n            this.via = viaMember;\n            this.too = toMember;\n            // Make sure that the route can be built\n            route();\n        }\n        catch (final CoreException e)\n        {\n            this.invalidReason = e.getMessage();\n            logger.trace(\"Could not build TurnRestriction from relation {}\", relation, e);\n            this.from = null;\n            this.via = null;\n            this.too = null;\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public LocationIterableProperties asGeoJson()\n    {\n        final Map<String, String> tagsNo = Maps.hashMap(\"highway\", \"primary\", \"oneway\", \"yes\",\n                \"type\", \"NO\");\n        final Map<String, String> tagsOnly = Maps.hashMap(\"highway\", \"primary\", \"oneway\", \"yes\",\n                \"type\", \"ONLY\");\n        return new LocationIterableProperties(this.route().asPolyLine(),\n                this.getTurnRestrictionType() == TurnRestrictionType.NO ? tagsNo : tagsOnly);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        if (!isValid())\n        {\n            throw new CoreException(\"An invalid TurnRestriction cannot be Located.\");\n        }\n        return route().bounds();\n    }\n\n    /**\n     * @return The \"from\" members of this {@link TurnRestriction}\n     */\n    public Route getFrom()\n    {\n        return this.from;\n    }\n\n    public String getInvalidReason()\n    {\n        return this.invalidReason;\n    }\n\n    @Override\n    public Optional<String> getTag(final String key)\n    {\n        return this.relation.getTag(key);\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return new HashMap<>(this.relation.getTags());\n    }\n\n    /**\n     * @return The \"to\" members of this {@link TurnRestriction}\n     */\n    public Route getTo()\n    {\n        return this.too;\n    }\n\n    public TurnRestrictionType getTurnRestrictionType()\n    {\n        return this.type;\n    }\n\n    public Optional<Route> getVia()\n    {\n        return Optional.ofNullable(this.via);\n    }\n\n    public boolean isValid()\n    {\n        return this.from != null && this.too != null && route() != null;\n    }\n\n    /**\n     * @return The {@link TurnRestriction}'s full {@link Route}\n     */\n    public Route route()\n    {\n        if (this.route == null)\n        {\n            final List<Route> routes = new ArrayList<>();\n            if (this.from != null)\n            {\n                routes.add(this.from);\n            }\n            if (this.via != null)\n            {\n                routes.add(this.via);\n            }\n            if (this.too != null)\n            {\n                routes.add(this.too);\n            }\n            try\n            {\n                this.route = Route.forRoutes(routes);\n            }\n            catch (final Exception e)\n            {\n                throw new CoreException(\"Can't build route from {}\", this.relation, e);\n            }\n        }\n        return this.route;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[\");\n        builder.append(this.type.toString());\n        builder.append(\": \");\n        if (this.from != null)\n        {\n            final StringList froms = new StringList();\n            this.from.forEach(edge -> froms.add(String.valueOf(edge.getIdentifier())));\n            builder.append(froms.join(\",\"));\n        }\n        builder.append(\"_\");\n        if (this.via != null)\n        {\n            final StringList vias = new StringList();\n            this.via.forEach(edge -> vias.add(String.valueOf(edge.getIdentifier())));\n            builder.append(vias.join(\",\"));\n        }\n        builder.append(\"_\");\n        if (this.too != null)\n        {\n            final StringList tos = new StringList();\n            this.too.forEach(edge -> tos.add(String.valueOf(edge.getIdentifier())));\n            builder.append(tos.join(\",\"));\n        }\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    /**\n     * @return The set of {@link Route} that are emaning from the Via route, but which are not the\n     *         current To option.\n     */\n    protected Set<Route> otherToOptions()\n    {\n        final Set<Route> result = new HashSet<>();\n        for (final Edge toEdge : this.too.start().start().outEdges())\n        {\n            if (toEdge.equals(this.too.start()))\n            {\n                continue;\n            }\n            result.add(Route.forEdge(toEdge));\n        }\n        return result;\n    }\n\n    private boolean isSameRoadViaAndTo(final Relation relation)\n    {\n        final Set<Long> fromIdentifiers = new TreeSet<>();\n        final Set<Long> toIdentifiers = new TreeSet<>();\n        relation.members().stream().filter(member -> \"to\".equals(member.getRole()))\n                .forEach(member -> toIdentifiers.add(member.getEntity().getIdentifier()));\n        relation.members().stream().filter(member -> \"from\".equals(member.getRole()))\n                .forEach(member -> fromIdentifiers.add(member.getEntity().getIdentifier()));\n        return fromIdentifiers.equals(toIdentifiers);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/ComplexEntity.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.io.Serializable;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasObject;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * Complex entity built on the fly from an existing {@link Atlas}. Examples include buildings with\n * holes (Relation type=multipolygon with inner and outer members, and a building=yes tag), lakes\n * with islands, etc.\n *\n * @author matthieun\n */\npublic abstract class ComplexEntity implements AtlasObject\n{\n    /**\n     * Validation errors are reported through this class to any interested callers\n     *\n     * @author cstaylor\n     */\n    public static final class ComplexEntityError implements Serializable\n    {\n        private static final long serialVersionUID = 3162352792545207168L;\n\n        private final transient ComplexEntity source;\n        private final String reason;\n        private final Throwable oops;\n\n        public ComplexEntityError(final ComplexEntity source, final String reason)\n        {\n            this(source, reason, null);\n        }\n\n        public ComplexEntityError(final ComplexEntity source, final String reason,\n                final Throwable oops)\n        {\n            this.source = source;\n            this.reason = reason;\n            this.oops = oops;\n        }\n\n        public Throwable getException()\n        {\n            return this.oops;\n        }\n\n        public String getReason()\n        {\n            return this.reason;\n        }\n\n        public ComplexEntity getSource()\n        {\n            return this.source;\n        }\n\n        @Override\n        public String toString()\n        {\n            final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            final PrintStream stream = new PrintStream(baos);\n            stream.printf(\"%s\\n\", this.source);\n            stream.printf(\"OSM id: %d\\n\", this.source.getOsmIdentifier());\n            if (this.reason != null)\n            {\n                stream.printf(\"Why: %s\\n\", this.reason);\n            }\n            if (this.oops != null)\n            {\n                this.oops.printStackTrace(stream);\n            }\n            stream.close();\n            return new String(baos.toByteArray(), Charset.forName(\"UTF-8\"));\n        }\n\n    }\n\n    private static final long serialVersionUID = -553026286746440299L;\n\n    private final Atlas atlas;\n\n    private final AtlasEntity source;\n\n    private Tuple<String, Throwable> invalidReason;\n\n    protected ComplexEntity(final AtlasEntity source)\n    {\n        this.source = source;\n        this.atlas = source.getAtlas();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return getSource().bounds();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof ComplexEntity)\n        {\n            return other.getClass().equals(this.getClass())\n                    && this.getSource().equals(((ComplexEntity) other).getSource());\n        }\n        return false;\n    }\n\n    public List<ComplexEntityError> getAllInvalidations()\n    {\n        final List<ComplexEntityError> returnValue = new ArrayList<>();\n        if (!isValid())\n        {\n            getError().ifPresent(returnValue::add);\n        }\n        return returnValue;\n    }\n\n    @Override\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    public Optional<ComplexEntityError> getError()\n    {\n        if (this.invalidReason == null)\n        {\n            return Optional.empty();\n        }\n        return Optional.of(new ComplexEntityError(this, this.invalidReason.getFirst(),\n                this.invalidReason.getSecond()));\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return getSource().getIdentifier();\n    }\n\n    @Override\n    public long getOsmIdentifier()\n    {\n        return this.source.getOsmIdentifier();\n    }\n\n    public AtlasEntity getSource()\n    {\n        return this.source;\n    }\n\n    @Override\n    public Optional<String> getTag(final String tagName)\n    {\n        return getSource().getTag(tagName);\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return getSource().getTags();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.source.hashCode();\n    }\n\n    /**\n     * @return True if there are not any missing data in the Atlas to be able to properly build this\n     *         {@link ComplexEntity}. This is used so any Atlas does not offer any\n     *         {@link ComplexEntity} that would be compromised to an end user.\n     */\n    public boolean isValid()\n    {\n        return this.invalidReason == null;\n    }\n\n    @Override\n    public abstract String toString();\n\n    protected void setInvalidReason(final String reason, final Throwable oops)\n    {\n        this.invalidReason = new Tuple<>(reason, oops);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/Finder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport java.util.function.Consumer;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * @author matthieun\n * @author cstaylor\n * @param <T>\n *            the type of ComplexEntity we'll be searching for\n */\npublic interface Finder<T extends ComplexEntity>\n{\n    /**\n     * Helper method that can be used when searching a Finder so we skip any bad entities\n     *\n     * @param badEntity\n     *            the entity considered bad by the finder implementation\n     * @param <T>\n     *            the type of bad complex entity we want to ignore\n     */\n    static <T> void ignore(final T badEntity)\n    {\n\n    }\n\n    /**\n     * @param atlas\n     *            The {@link Atlas} to browse.\n     * @return The simple entities first, then the complex ones.\n     */\n    Iterable<T> find(Atlas atlas);\n\n    /**\n     * Automatically filters out invalid complex entities and passes them to the badEntityConsumer\n     * for further processing\n     *\n     * @param atlas\n     *            the atlas we're searching\n     * @param badEntityConsumer\n     *            the consumer receiving each invalid complex entity\n     * @return an iterable of only valid complex entities\n     */\n    default Iterable<T> find(final Atlas atlas, final Consumer<T> badEntityConsumer)\n    {\n        return Iterables.filter(find(atlas), entity ->\n        {\n            final boolean wasEntityValid = entity.isValid();\n            if (!wasEntityValid)\n            {\n                badEntityConsumer.accept(entity);\n            }\n            return wasEntityValid;\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/MultiPolygonRelationToMemberConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * @author Sid\n */\npublic class MultiPolygonRelationToMemberConverter\n        implements Converter<Relation, Iterable<AtlasEntity>>\n{\n    /**\n     * @author Sid\n     */\n    public enum Ring\n    {\n        OUTER,\n        INNER\n    }\n\n    private final Ring ring;\n\n    public MultiPolygonRelationToMemberConverter(final Ring ring)\n    {\n        this.ring = ring;\n    }\n\n    @Override\n    public Iterable<AtlasEntity> convert(final Relation relation)\n    {\n        if (!relation.isGeometric())\n        {\n            throw new CoreException(\"Not a MultiPolygon: {}\", relation);\n        }\n\n        final List<AtlasEntity> list = new ArrayList<>();\n        for (final RelationMember member : relation.members())\n        {\n            switch (this.ring)\n            {\n                case OUTER:\n                    if (RelationTypeTag.MULTIPOLYGON_ROLE_OUTER.equals(member.getRole()))\n                    {\n                        list.add(member.getEntity());\n                    }\n                    break;\n                case INNER:\n                    if (RelationTypeTag.MULTIPOLYGON_ROLE_INNER.equals(member.getRole()))\n                    {\n                        list.add(member.getEntity());\n                    }\n                    break;\n                default:\n                    throw new CoreException(\"Unknown ring type: {}\", this.ring);\n            }\n        }\n        return list;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/README.md",
    "content": "# Complex Entities\n\nComplex entities are higher level features made of relationships between Atlas features. Those complex entities are never stored in the Atlas directly, but rather built on demand using Finders.\n\n## Some examples\n\n### ComplexTurnRestriction\n\nOne relation with type=restriction and from, via and to members. This is aggregated to build the restricted route and fails to build if the relation is malformed.\n\n### ComplexBuilding\n\nOne building with multiple parts, or a hole. The Finder reads the relations with the right tags and members, and provides a ComplexBuilding that can return the built-out `MultiPolygon` object.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/RelationOrAreaToMultiPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.locationtech.jts.geom.prep.PreparedGeometryFactory;\nimport org.locationtech.jts.geom.prep.PreparedPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation.Ring;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * Take in a {@link Relation} or an {@link Area} and return the corresponding {@link MultiPolygon}\n * if any.\n *\n * @author matthieun\n * @author bbreithaupt\n */\npublic class RelationOrAreaToMultiPolygonConverter implements Converter<AtlasEntity, MultiPolygon>\n{\n    private final RelationToMultiPolygonMemberConverter outerConverter;\n    private final RelationToMultiPolygonMemberConverter innerConverter;\n\n    public RelationOrAreaToMultiPolygonConverter()\n    {\n        this(false);\n    }\n\n    public RelationOrAreaToMultiPolygonConverter(final boolean usePolygonizer)\n    {\n        this.outerConverter = new RelationToMultiPolygonMemberConverter(Ring.OUTER, usePolygonizer);\n        this.innerConverter = new RelationToMultiPolygonMemberConverter(Ring.INNER, usePolygonizer);\n    }\n\n    @Override\n    public MultiPolygon convert(final AtlasEntity entity)\n    {\n        if (entity instanceof Relation)\n        {\n            final Relation relation = (Relation) entity;\n            if (relation.isGeometric())\n            {\n                // Loop through the relation members, extract the inners and outers, and create the\n                // outline.\n                final MultiMap<Polygon, Polygon> outerToInners = new MultiMap<>();\n                for (final Polygon outer : this.outerConverter.convert(relation))\n                {\n                    outerToInners.put(outer, new ArrayList<>());\n                }\n                if (outerToInners.isEmpty())\n                {\n                    throw new CoreException(\"Unable to find outer polygon.\");\n                }\n                final Map<Polygon, PreparedPolygon> preparedOuters = new HashMap<>();\n                final JtsPolygonConverter converter = new JtsPolygonConverter();\n                outerToInners.keySet().forEach(\n                        outer -> preparedOuters.put(outer, (PreparedPolygon) PreparedGeometryFactory\n                                .prepare(converter.convert(outer))));\n                for (final Polygon inner : this.innerConverter.convert(relation))\n                {\n                    boolean added = false;\n                    for (final Map.Entry<Polygon, PreparedPolygon> entry : preparedOuters\n                            .entrySet())\n                    {\n                        final org.locationtech.jts.geom.Polygon inner2 = converter.convert(inner);\n                        if (entry.getValue().containsProperly(inner2))\n                        {\n                            outerToInners.add(entry.getKey(), inner);\n                            added = true;\n                            break;\n                        }\n                    }\n                    if (!added)\n                    {\n                        throw new CoreException(\n                                \"Malformed MultiPolygon: inner has no outer host: {}\", inner);\n                    }\n                }\n                return new MultiPolygon(outerToInners);\n            }\n            else\n            {\n                throw new CoreException(\"This is not a multipolygon relation\");\n            }\n        }\n        else if (entity instanceof Area)\n        {\n            return MultiPolygon.forPolygon(((Area) entity).asPolygon());\n        }\n        else\n        {\n            throw new CoreException(\"The outline is not an area nor a relation: {}\", entity);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/RelationToMultiPolygonMemberConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation.Ring;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.converters.MultiplePolyLineToPolygonsConverter;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * Get the {@link MultiPolygon} rings of a certain type from a {@link Relation} of type\n * multipolygon.\n *\n * @author matthieun\n */\npublic class RelationToMultiPolygonMemberConverter implements Converter<Relation, Iterable<Polygon>>\n{\n    private final MultiplePolyLineToPolygonsConverter multiplePolyLineToPolygonsConverter;\n    private final Ring ring;\n\n    public RelationToMultiPolygonMemberConverter(final Ring ring)\n    {\n        this(ring, false);\n    }\n\n    public RelationToMultiPolygonMemberConverter(final Ring ring, final boolean usePolygonizer)\n    {\n        this.ring = ring;\n        this.multiplePolyLineToPolygonsConverter = new MultiplePolyLineToPolygonsConverter(\n                usePolygonizer);\n    }\n\n    @Override\n    public Iterable<Polygon> convert(final Relation relation)\n    {\n        final List<PolyLine> candidates = new ArrayList<>();\n        final List<Polygon> alreadyFormed = new ArrayList<>();\n        if (!relation.isGeometric())\n        {\n            throw new CoreException(\"Not a MultiPolygon: {}\", relation);\n        }\n        final ArrayList<RelationMember> members = new ArrayList<>();\n        relation.members().iterator().forEachRemaining(members::add);\n        Collections.sort(members);\n        for (final RelationMember member : members)\n        {\n            final AtlasEntity entity = member.getEntity();\n            switch (this.ring)\n            {\n                case OUTER:\n                    if (RelationTypeTag.MULTIPOLYGON_ROLE_OUTER.equals(member.getRole()))\n                    {\n                        processEntity(entity, candidates, alreadyFormed);\n                    }\n                    break;\n                case INNER:\n                    if (RelationTypeTag.MULTIPOLYGON_ROLE_INNER.equals(member.getRole()))\n                    {\n                        processEntity(entity, candidates, alreadyFormed);\n                    }\n                    break;\n                default:\n                    throw new CoreException(\"Unknown ring type: {}\", this.ring);\n            }\n        }\n        return new MultiIterable<>(alreadyFormed,\n                this.multiplePolyLineToPolygonsConverter.convert(candidates));\n    }\n\n    private void processEntity(final AtlasEntity entity, final List<PolyLine> candidates,\n            final List<Polygon> alreadyFormed)\n    {\n        if (entity instanceof Area)\n        {\n            // Easy\n            alreadyFormed.add(((Area) entity).asPolygon());\n        }\n        else if (entity instanceof LineItem)\n        {\n            // In case an Edge is an outer/inner, make sure to not double count it by looking at the\n            // main edge only.\n            if (!(entity instanceof Edge) || ((Edge) entity).isMainEdge())\n            {\n                candidates.add(((LineItem) entity).asPolyLine());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/WaterIslandConfigurationReader.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.islands.ComplexIsland;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.ComplexWaterEntity;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * Abstract class to define the configuration reader which will read in a json file and provide an\n * api to use that configuration and generate a {@link ComplexEntity} from {@link AtlasEntity}.\n * Currently this is used to generate {@link ComplexWaterEntity} or {@link ComplexIsland}\n *\n * @param <T>\n *            Type of objects which will map {@link AtlasEntity} to any {@link ComplexEntity}\n * @author sbhalekar\n */\npublic abstract class WaterIslandConfigurationReader<T>\n        implements Converter<AtlasEntity, Optional<? extends ComplexEntity>>\n{\n    private final T configurationMapper;\n\n    public WaterIslandConfigurationReader(final Resource configurationResource)\n    {\n        this.configurationMapper = readConfiguration(configurationResource);\n    }\n\n    @Override\n    public Optional<? extends ComplexEntity> convert(final AtlasEntity atlasEntity)\n    {\n        return createComplexEntity(atlasEntity);\n    }\n\n    /**\n     * This method will use the configuration mapper to convert {@link AtlasEntity} into\n     * {@link ComplexEntity}\n     *\n     * @param atlasEntity\n     *            {@link AtlasEntity} which needs to be converted\n     * @return An optional {@link ComplexEntity}\n     */\n    @SuppressWarnings(\"squid:S1452\")\n    protected abstract Optional<? extends ComplexEntity> createComplexEntity(\n            AtlasEntity atlasEntity);\n\n    protected T getConfigurationMapper()\n    {\n        return this.configurationMapper;\n    }\n\n    /**\n     * Implementation should read the resource and generate a mapper which will be used to convert\n     * an {@link AtlasEntity} to {@link ComplexEntity}\n     *\n     * @param configurationResource\n     *            Resource for the configuration file\n     * @return Mapper which would be used for the conversion\n     */\n    protected abstract T readConfiguration(Resource configurationResource);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/aoi/ComplexAreaOfInterest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.aoi;\n\nimport java.io.InputStreamReader;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonParser;\n\n/**\n * Complex Entity for AOI Relations and AOI Areas. AOI relations are those that are\n * {@link MultiPolygon} and have AOI tags in it and AOI Areas are those {@link Area}s that have AOI\n * tags in it. AOI tags are checked against the {@link TaggableFilter} passed as an argument or to\n * the default {@link TaggableFilter} of AOI tags if the tags are not explicitly specified.\n *\n * @author sayas01\n */\npublic final class ComplexAreaOfInterest extends ComplexEntity\n{\n    private static final Logger logger = LoggerFactory.getLogger(ComplexAreaOfInterest.class);\n    private static final RelationOrAreaToMultiPolygonConverter RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER = new RelationOrAreaToMultiPolygonConverter();\n    private static final String AOI_RESOURCE = \"aoi-tag-filter.json\";\n    // The default AreasOfInterest(AOI) tags\n    private static List<TaggableFilter> defaultTaggableFilter;\n    private static final long serialVersionUID = 1191946548857888704L;\n    private final MultiPolygon multiPolygon;\n\n    /**\n     * This method creates a {@link ComplexAreaOfInterest} for the specified {@link AtlasEntity} if\n     * it meets the requirements for Complex AOI relation. The AOI tags are checked against the\n     * default tags.\n     *\n     * @param source\n     *            The {@link AtlasEntity} for which the ComplexEntity is created\n     * @return {@link ComplexAreaOfInterest} if created, else return empty.\n     */\n    public static Optional<ComplexAreaOfInterest> getComplexAOI(final AtlasEntity source)\n    {\n        return getComplexAOI(source, customAoiFilter -> false);\n    }\n\n    /**\n     * This method creates a {@link ComplexAreaOfInterest} for the specified {@link AtlasEntity} and\n     * {@link TaggableFilter}. The AOI tags are checked against the aoiFilter param as well as the\n     * default tags.\n     *\n     * @param source\n     *            The {@link AtlasEntity} for which the ComplexEntity is created\n     * @param aoiFilter\n     *            The {@link TaggableFilter} of AOI tags against which the relation is checked for\n     *            AOI tags\n     * @return {@link ComplexAreaOfInterest} if created, else return empty.\n     */\n    public static Optional<ComplexAreaOfInterest> getComplexAOI(final AtlasEntity source,\n            final Predicate<Taggable> aoiFilter)\n    {\n        try\n        {\n            if (defaultTaggableFilter == null)\n            {\n                computeDefaultFilter();\n            }\n            return (source instanceof Relation || source instanceof Area)\n                    && (hasAOITag(source) || aoiFilter.test(source))\n                            ? Optional.of(new ComplexAreaOfInterest(source))\n                            : Optional.empty();\n        }\n        catch (final Exception exception)\n        {\n            logger.warn(\"Unable to create complex AOI relations from {}\", source, exception);\n            return Optional.empty();\n        }\n    }\n\n    private static void computeDefaultFilter()\n    {\n        try (InputStreamReader reader = new InputStreamReader(\n                ComplexAreaOfInterest.class.getResourceAsStream(AOI_RESOURCE)))\n        {\n            final JsonElement element = new JsonParser().parse(reader);\n            final JsonArray filters = element.getAsJsonObject().get(\"filters\").getAsJsonArray();\n            defaultTaggableFilter = StreamSupport.stream(filters.spliterator(), false)\n                    .map(jsonElement -> TaggableFilter.forDefinition(jsonElement.getAsString()))\n                    .collect(Collectors.toList());\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\n                    \"There was a problem parsing aoi-tag-filter.json. Check if the JSON file has valid structure.\",\n                    exception);\n        }\n    }\n\n    /**\n     * Checks for AOI tags in the object\n     *\n     * @param source\n     *            {@link AtlasEntity} that needs to be checked for AOI tags\n     * @return {@code true} if the source has AOI tags\n     */\n    private static boolean hasAOITag(final AtlasEntity source)\n    {\n        return defaultTaggableFilter.stream()\n                .anyMatch(taggableFilter -> taggableFilter.test(source));\n    }\n\n    /**\n     * Construct a {@link ComplexAreaOfInterest}\n     *\n     * @param source\n     *            the {@link AtlasEntity} to construct the ComplexAoiRelation\n     */\n    private ComplexAreaOfInterest(final AtlasEntity source)\n    {\n        super(source);\n        try\n        {\n            if (source.getType().equals(ItemType.RELATION))\n            {\n                final Optional<org.locationtech.jts.geom.MultiPolygon> geom = ((Relation) source)\n                        .asMultiPolygon();\n                if (geom.isEmpty())\n                {\n                    throw new CoreException(\"No stored geometry for Relation {}\", source);\n                }\n                this.multiPolygon = new JtsMultiPolygonToMultiPolygonConverter()\n                        .convert(geom.get());\n            }\n            else\n            {\n                this.multiPolygon = RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER.convert(source);\n            }\n\n        }\n        catch (final Exception exception)\n        {\n            setInvalidReason(\"Unable to convert the AtlasEntity to MultiPolygon\", exception);\n            throw new CoreException(\"Unable to convert the AtlasEntity to MultiPolygon\", exception);\n        }\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        return other instanceof ComplexAreaOfInterest && super.equals(other);\n    }\n\n    public MultiPolygon getGeometry()\n    {\n        return this.multiPolygon;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getName() + \" \" + getSource();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/aoi/ComplexAreaOfInterestFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.aoi;\n\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\n\n/**\n * {@link ComplexAreaOfInterest} finder.\n *\n * @author sayas01\n */\npublic class ComplexAreaOfInterestFinder implements Finder<ComplexAreaOfInterest>\n{\n    /**\n     * Finds all relations and areas that are candidates for {@link ComplexAreaOfInterest} and\n     * converts them into {@link ComplexAreaOfInterest}.\n     *\n     * @param atlas\n     *            The {@link Atlas} to browse.\n     * @return {@link Iterables} of {@link ComplexAreaOfInterest}.\n     */\n    @Override\n    public Iterable<ComplexAreaOfInterest> find(final Atlas atlas)\n    {\n        final Iterable<ComplexAreaOfInterest> iterableOfComplexAOIRelations = StreamSupport\n                .stream(atlas.relations().spliterator(), true)\n                .map(ComplexAreaOfInterest::getComplexAOI).filter(Optional::isPresent)\n                .map(Optional::get).collect(Collectors.toList());\n        final Iterable<ComplexAreaOfInterest> iterableOfComplexAOIAreas = StreamSupport\n                .stream(atlas.areas().spliterator(), true).map(ComplexAreaOfInterest::getComplexAOI)\n                .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());\n        return new MultiIterable<>(iterableOfComplexAOIRelations, iterableOfComplexAOIAreas);\n    }\n\n    /**\n     * Finds all relations and areas that are candidates for {@link ComplexAreaOfInterest} with\n     * given AOI tags and converts them into {@link ComplexAreaOfInterest}.\n     *\n     * @param atlas\n     *            Atlas to build the ComplexAOiRelation\n     * @param aoiFilter\n     *            {@link TaggableFilter} aoi taggable filter\n     * @return {@link Iterables} of {@link ComplexAreaOfInterest}.\n     */\n    public Iterable<ComplexAreaOfInterest> find(final Atlas atlas, final TaggableFilter aoiFilter)\n    {\n        final Iterable<ComplexAreaOfInterest> iterableOfComplexAOIRelations = StreamSupport\n                .stream(atlas.relations().spliterator(), true)\n                .map(relation -> ComplexAreaOfInterest.getComplexAOI(relation, aoiFilter))\n                .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());\n        final Iterable<ComplexAreaOfInterest> iterableOfComplexAOIAreas = StreamSupport\n                .stream(atlas.areas().spliterator(), true)\n                .map(area -> ComplexAreaOfInterest.getComplexAOI(area, aoiFilter))\n                .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());\n        return new MultiIterable<>(iterableOfComplexAOIRelations, iterableOfComplexAOIAreas);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/BigNode.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.bignode;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.routing.AStarRouter;\nimport org.openstreetmap.atlas.geography.atlas.routing.AllPathsRouter;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * {@link BigNode} is a {@link ComplexEntity} that represents complex intersections, as one single\n * object. This is mostly used to model turn restrictions. A {@link BigNode} also provides the\n * different ways to traverse it as {@link Route} objects.\n *\n * @author matthieun\n * @author mgostintsev\n */\npublic class BigNode extends ComplexEntity\n{\n    /**\n     * This denotes the type path through a {@link BigNode}. Note: other than the start and end\n     * edges, only junction edges are allowed in the path.\n     *\n     * @author mgostintsev\n     */\n    public enum Path\n    {\n        SHORTEST,\n        ALL\n    }\n\n    /**\n     * This denotes the type of {@link BigNode}\n     *\n     * @author Sid\n     */\n    public enum Type\n    {\n        // Intersections represented by a single node\n        SIMPLE,\n        // Dual Carriage Way Intersections represented by a set of nodes\n        DUAL_CARRIAGEWAY\n    }\n\n    // Maximum allowed nodes for a BigNode\n    public static final int MAXIMUM_NODES = 20;\n    private static final long serialVersionUID = 4102278807908010498L;\n    // BigNode type\n    private Type type;\n\n    // All the nodes defining this BigNode\n    private final Set<Node> nodes;\n\n    // Lazily initialized sets of edges making up this BigNode\n    private Set<Edge> edges;\n    private Set<Edge> inEdges;\n    private Set<Edge> outEdges;\n    private Set<Edge> junctionEdges;\n\n    // Outside of the start/end edge, only junction edges are allowed to be part of a path leading\n    // in/out of a BigNode. Because we're building a new atlas to do the routing one, we can't use\n    // contains for the edge in question, but rely on the identifier to make sure it's actually a\n    // junction edge.\n    private final Predicate<Edge> isJunctionEdge = edge -> this.junctionEdges().stream()\n            .filter(junctionEdge -> junctionEdge.getIdentifier() == edge.getIdentifier())\n            .count() > 0;\n\n    /**\n     * Construct a {@link Type#SIMPLE} {@link BigNode}\n     *\n     * @param source\n     *            The source node\n     */\n    public BigNode(final Node source)\n    {\n        super(source);\n\n        final Set<Node> nodes = new HashSet<>();\n        nodes.add(source);\n\n        this.nodes = nodes;\n        this.type = Type.SIMPLE;\n    }\n\n    /**\n     * Construct a {@link BigNode}\n     *\n     * @param source\n     *            The source node\n     * @param nodes\n     *            All the nodes belonging to the {@link BigNode}\n     * @param type\n     *            Type of the {@link BigNode}\n     */\n    public BigNode(final Node source, final Set<Node> nodes, final Type type)\n    {\n        this(source, nodes);\n        this.type = type;\n    }\n\n    /**\n     * Construct a {@link BigNode}\n     *\n     * @param source\n     *            The source {@link Node}\n     * @param nodes\n     *            All the {@link Node}s belonging to the {@link BigNode}\n     */\n    protected BigNode(final Node source, final Set<Node> nodes)\n    {\n        super(source);\n        this.nodes = nodes;\n    }\n\n    /**\n     * @return The set of all possible {@link Route}s in and out of this {@link BigNode}.\n     *         {@link BigNode#shortestPaths} are a subset of this set. Note: this will NOT be cached\n     *         by the {@link BigNode}, so be judicious in how many times this is called.\n     */\n    public Set<Route> allPaths()\n    {\n        return allPaths(AllPathsRouter.MAXIMUM_ALLOWED_PATHS);\n    }\n\n    /**\n     * @param maximumPathCount\n     *            The maximum path count of each in out edge pair\n     * @return The set of all possible {@link Route}s in and out of this {@link BigNode}.\n     *         {@link BigNode#shortestPaths} are a subset of this set. Note: this will NOT be cached\n     *         by the {@link BigNode}, so be judicious in how many times this is called.\n     */\n    public Set<Route> allPaths(final int maximumPathCount)\n    {\n        final Atlas atlas = nodes().iterator().next().getAtlas();\n        final Atlas bigNodeAtlas = buildBigNodeAtlas();\n\n        // Find all the routes from all the in-edges to all the out-edges\n        final Set<Route> allPaths = new HashSet<>();\n        for (final Edge inEdge : inEdges())\n        {\n            for (final Edge outEdge : outEdges())\n            {\n                // Translate edges to bigNodeAtlas edges to get all routes\n                final Set<Route> bigNodeRoutes = AllPathsRouter.allRoutes(\n                        bigNodeAtlas.edge(inEdge.getIdentifier()),\n                        bigNodeAtlas.edge(outEdge.getIdentifier()), this.isJunctionEdge,\n                        maximumPathCount);\n\n                if (bigNodeRoutes.isEmpty())\n                {\n                    continue;\n                }\n\n                // Translate the result routes back to routes made of edges from the original Atlas\n                for (final Route bigNodeRoute : bigNodeRoutes)\n                {\n                    Route route = Route.forEdge(atlas.edge(bigNodeRoute.start().getIdentifier()));\n                    for (int index = 1; index < bigNodeRoute.size(); index++)\n                    {\n                        final long edgeIdentifier = bigNodeRoute.get(index).getIdentifier();\n                        route = route.append(atlas.edge(edgeIdentifier));\n                    }\n                    allPaths.add(route);\n                }\n            }\n        }\n\n        return allPaths;\n    }\n\n    public GeoJsonObject asGeoJson()\n    {\n        return new GeoJsonBuilder().create(Iterables.from(asGeoJsonBigNode()));\n    }\n\n    public LocationIterableProperties asGeoJsonBigNode()\n    {\n        final List<Location> locations = new ArrayList<>();\n        nodes().stream().forEach(node -> locations\n                .addAll(Rectangle.forLocated(node.getLocation()).expand(Distance.meters(2))));\n        return new LocationIterableProperties(new Polygon(locations),\n                Maps.hashMap(\"BigNode\", String.valueOf(getSource().getIdentifier())));\n    }\n\n    public Iterable<LocationIterableProperties> asGeoJsonRestrictedPath()\n    {\n        return this.turnRestrictions().stream()\n                .map(turnRestriction -> new LocationIterableProperties(\n                        turnRestriction.getRoute().asPolyLine(),\n                        Maps.hashMap(\"highway\", \"motorway\", \"oneway\", \"yes\", \"route\",\n                                turnRestriction.getRoute().toString())))\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        // Override the regular bounds() method that only looks at the source\n        return Rectangle.forLocated(nodesAndEdges());\n    }\n\n    /**\n     * @return All the {@link Edge}s linked to this {@link BigNode}\n     */\n    public Set<Edge> edges()\n    {\n        if (this.edges == null)\n        {\n            this.edges = this.nodes.stream().flatMap(node ->\n            {\n                return node.connectedEdges().stream().filter(HighwayTag::isCarNavigableHighway);\n            }).collect(Collectors.toSet());\n        }\n        return this.edges;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        // Override the regular equals(Object) method that only looks at the source\n        if (other instanceof BigNode)\n        {\n            return this.getSource().equals(((BigNode) other).getSource())\n                    && this.nodes().equals(((BigNode) other).nodes());\n        }\n        return false;\n    }\n\n    public Set<Edge> exteriorEdges()\n    {\n        return exteriorEdgesStream().collect(Collectors.toSet());\n    }\n\n    @Override\n    public List<ComplexEntityError> getAllInvalidations()\n    {\n        final List<ComplexEntityError> returnValue = new ArrayList<>();\n        if (!isValid())\n        {\n            returnValue.add(new ComplexEntityError(this,\n                    String.format(\"Too many nodes %d\", nodes().size()), null));\n        }\n        return returnValue;\n    }\n\n    /**\n     * @return the {@link Type} of {@link BigNode} this is. Can be null.\n     */\n    public Type getType()\n    {\n        return this.type;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        // For checkstyle\n        return super.hashCode();\n    }\n\n    /**\n     * @return All the {@link Edge}s that drive into the {@link BigNode}\n     */\n    public Set<Edge> inEdges()\n    {\n        if (this.inEdges == null)\n        {\n            this.inEdges = inEdgesStream().collect(Collectors.toSet());\n        }\n        return this.inEdges;\n    }\n\n    @Override\n    public boolean isValid()\n    {\n        return nodes().size() <= MAXIMUM_NODES;\n    }\n\n    /**\n     * @return All the {@link Edge}s that represent internal junctions to the {@link BigNode}\n     */\n    public Set<Edge> junctionEdges()\n    {\n        if (this.junctionEdges == null)\n        {\n            this.junctionEdges = junctionEdgesStream().collect(Collectors.toSet());\n        }\n        return this.junctionEdges;\n    }\n\n    /**\n     * @return All the {@link Node}s forming this {@link BigNode}\n     */\n    public Set<Node> nodes()\n    {\n        return this.nodes;\n    }\n\n    /**\n     * @return All the {@link Node}s and {@link Edge}s forming this {@link BigNode}\n     */\n    public Iterable<AtlasItem> nodesAndEdges()\n    {\n        return new MultiIterable<>(nodes(), edges());\n    }\n\n    /**\n     * @return All the {@link Edge}s that drive out of the {@link BigNode}.\n     */\n    public Set<Edge> outEdges()\n    {\n        if (this.outEdges == null)\n        {\n            this.outEdges = outEdgesStream().collect(Collectors.toSet());\n        }\n        return this.outEdges;\n    }\n\n    /**\n     * Set the {@link Type} for this {@link BigNode}.\n     *\n     * @param type\n     *            The {@link Type} to set\n     */\n    public void setType(final Type type)\n    {\n        this.type = type;\n    }\n\n    /**\n     * @return The set of shortest {@link Route}s in/out of this {@link BigNode}. Note: this will\n     *         NOT be cached by the {@link BigNode}, so be judicious in how many times this is\n     *         called.\n     */\n    public Set<Route> shortestPaths()\n    {\n        final Atlas atlas = nodes().iterator().next().getAtlas();\n        final Atlas bigNodeAtlas = buildBigNodeAtlas();\n\n        // Find all the routes from all the in-edges to all the out-edges\n        final Set<Route> shortestPaths = new HashSet<>();\n        for (final Edge inEdge : inEdges())\n        {\n            for (final Edge outEdge : outEdges())\n            {\n                // Translate edges to bigNodeAtlas edges to get the route\n                final Route bigNodeRoute = AStarRouter.dijkstra(bigNodeAtlas, Distance.ONE_METER)\n                        .route(bigNodeAtlas.edge(inEdge.getIdentifier()),\n                                bigNodeAtlas.edge(outEdge.getIdentifier()));\n                if (bigNodeRoute == null)\n                {\n                    continue;\n                }\n\n                // Translate the result route back to a route made of edges from the original Atlas\n                Route route = Route.forEdge(atlas.edge(bigNodeRoute.start().getIdentifier()));\n                for (int index = 1; index < bigNodeRoute.size(); index++)\n                {\n                    final long edgeIdentifier = bigNodeRoute.get(index).getIdentifier();\n                    route = route.append(atlas.edge(edgeIdentifier));\n                }\n                shortestPaths.add(route);\n            }\n        }\n\n        return shortestPaths;\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[BigNode: nodes=\"\n                + nodes().stream().map(Node::getIdentifier).collect(Collectors.toSet()) + \"]\";\n    }\n\n    /**\n     * @return All the paths in this {@link BigNode} that overlap at least one restricted path.\n     */\n    public Set<RestrictedPath> turnRestrictions()\n    {\n        return this.allPaths().stream().filter(Route::isTurnRestriction)\n                .map(route -> new RestrictedPath(this, route)).collect(Collectors.toSet());\n    }\n\n    protected Stream<Edge> exteriorEdgesStream()\n    {\n        return edges().stream()\n                .filter(edge -> !nodes().contains(edge.end()) || !nodes().contains(edge.start()));\n    }\n\n    protected Stream<Edge> inEdgesStream()\n    {\n        return edges().stream().filter(edge -> !nodes().contains(edge.start()));\n    }\n\n    protected Stream<Edge> junctionEdgesStream()\n    {\n        return edges().stream()\n                .filter(edge -> nodes().contains(edge.start()) && nodes().contains(edge.end()));\n    }\n\n    protected Stream<Edge> outEdgesStream()\n    {\n        return edges().stream().filter(edge -> !nodes().contains(edge.end()));\n    }\n\n    /**\n     * @return an {@link Atlas} made up of {@link Node}s and {@link Edge}s comprising this\n     *         {@link BigNode}, in order to run routing algorithms on it.\n     */\n    private Atlas buildBigNodeAtlas()\n    {\n        final Set<Edge> edges = edges();\n        final Set<Node> extendedNodes = new HashSet<>();\n        edges.forEach(edge ->\n        {\n            extendedNodes.add(edge.start());\n            extendedNodes.add(edge.end());\n        });\n\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder()\n                .withSizeEstimates(new AtlasSize(edges.size(), extendedNodes.size(), 0, 0, 0, 0));\n        extendedNodes.forEach(\n                node -> builder.addNode(node.getIdentifier(), node.getLocation(), node.getTags()));\n        edges.forEach(\n                edge -> builder.addEdge(edge.getIdentifier(), edge.asPolyLine(), edge.getTags()));\n        return builder.get();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/BigNodeFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.bignode;\n\nimport static org.openstreetmap.atlas.tags.names.NameFinder.STANDARD_TAGS;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.builder.CompareToBuilder;\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Heading;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNode.Type;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.JunctionTag;\nimport org.openstreetmap.atlas.tags.names.NameFinder;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.direction.EdgeDirectionComparator;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.AbstractIterator;\nimport com.google.common.collect.ComparisonChain;\nimport com.google.common.collect.Sets;\n\n/**\n * This finds all the {@link BigNode}s in an {@link Atlas} (including dual carriage way junctions\n * and simple intersections).\n *\n * @author sid\n * @author matthieun\n */\npublic class BigNodeFinder implements Finder<BigNode>\n{\n    /**\n     * This comparator is used to compare atlas node based on their identifier value\n     */\n    public static final class NodeComparator implements Comparator<Node>, Serializable\n    {\n        private static final long serialVersionUID = 816401695743423872L;\n\n        @Override\n        public int compare(final Node node1, final Node node2)\n        {\n            return new CompareToBuilder().append(node1.getIdentifier(), node2.getIdentifier())\n                    .toComparison();\n        }\n    }\n\n    /**\n     * An intermediate {@link BigNode} candidate that is easily merged with other {@link BigNode}s\n     *\n     * @author Sid\n     */\n    public static final class BigNodeCandidate implements Comparable<BigNodeCandidate>, Serializable\n    {\n        private static final long serialVersionUID = 7225634482602225746L;\n        private final Set<Long> nodeIdentifiers;\n\n        public static BigNodeCandidate from(final Set<Node> nodes)\n        {\n            return new BigNodeCandidate(\n                    nodes.stream().map(Node::getIdentifier).collect(Collectors.toSet()));\n        }\n\n        public BigNodeCandidate()\n        {\n            this.nodeIdentifiers = new TreeSet<>();\n        }\n\n        public BigNodeCandidate(final Set<Long> nodeIds)\n        {\n            this.nodeIdentifiers = new TreeSet<>(nodeIds);\n        }\n\n        @Override\n        public int compareTo(final BigNodeCandidate bigNodeCandidate)\n        {\n            final Iterator<Long> iterator2 = bigNodeCandidate.nodeIdentifiers.iterator();\n            for (final Long identifier : this.nodeIdentifiers)\n            {\n                // Shorter sets sort first.\n                if (!iterator2.hasNext())\n                {\n                    return 1;\n                }\n                final int comparison = identifier.compareTo(iterator2.next());\n                if (comparison != 0)\n                {\n                    return comparison;\n                }\n            }\n            // Shorter sets sort first\n            if (iterator2.hasNext())\n            {\n                return -1;\n            }\n            else\n            {\n                return 0;\n            }\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (other instanceof BigNodeCandidate)\n            {\n                final BigNodeCandidate that = (BigNodeCandidate) other;\n                if (this.nodeIdentifiers.size() == that.nodeIdentifiers.size())\n                {\n                    return new EqualsBuilder().append(this.nodeIdentifiers, that.nodeIdentifiers)\n                            .isEquals();\n                }\n            }\n            return false;\n        }\n\n        public Set<Long> getNodeIdentifiers()\n        {\n            return this.nodeIdentifiers;\n        }\n\n        public long getSourceNodeIdentifier()\n        {\n            if (!this.nodeIdentifiers.isEmpty())\n            {\n                return this.nodeIdentifiers.iterator().next();\n            }\n            throw new IllegalArgumentException(\n                    \"Could not get source node identifier since nodesIdentifiers are empty\");\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return new HashCodeBuilder().append(this.nodeIdentifiers).hashCode();\n        }\n\n        public void merge(final BigNodeCandidate mergeBigNodeCandidate)\n        {\n            this.nodeIdentifiers.addAll(mergeBigNodeCandidate.nodeIdentifiers);\n        }\n    }\n\n    /**\n     * @author Sid\n     */\n    public class BigNodeIterable implements Iterable<BigNode>\n    {\n        private final BigNodeIterator iterator;\n\n        public BigNodeIterable(final BigNodeIterator iterator)\n        {\n            this.iterator = iterator;\n        }\n\n        @Override\n        public Iterator<BigNode> iterator()\n        {\n            return this.iterator;\n        }\n    }\n\n    /**\n     * The iterator avoids pre-computation of all the {@link BigNode}s. We maintain only the\n     * dualCarriageWay {@link BigNode}s to reduce memory.\n     *\n     * @author Sid\n     */\n    public class BigNodeIterator extends AbstractIterator<BigNode>\n    {\n        private final Iterator<BigNodeCandidate> bigNodeCandidateIterator;\n        private final Atlas atlas;\n        private final Iterator<Node> nodeIterator;\n\n        // We maintain a set of processed nodeIds to avoid returning duplicates\n        private final Set<Long> bigNodeIdentifiers;\n\n        public BigNodeIterator(final Atlas atlas, final Set<BigNodeCandidate> bigNodeCandidates)\n        {\n            this.atlas = atlas;\n            this.bigNodeCandidateIterator = bigNodeCandidates.iterator();\n            this.nodeIterator = this.atlas.nodes().iterator();\n            this.bigNodeIdentifiers = new HashSet<>();\n        }\n\n        @Override\n        protected BigNode computeNext()\n        {\n            while (this.bigNodeCandidateIterator.hasNext())\n            {\n                final BigNodeCandidate bigNodeCandidate = this.bigNodeCandidateIterator.next();\n\n                // Sorting to ensure deterministic id\n                final Set<Node> nodes = new TreeSet<>(new NodeComparator());\n                bigNodeCandidate.nodeIdentifiers\n                        .forEach(nodeIdentifier -> nodes.add(this.atlas.node(nodeIdentifier)));\n                if (!nodes.isEmpty())\n                {\n                    final Node sourceNode = nodes.iterator().next();\n                    final BigNode bigNode = new BigNode(sourceNode, nodes, Type.DUAL_CARRIAGEWAY);\n                    nodes.stream()\n                            .forEach(node -> this.bigNodeIdentifiers.add(node.getIdentifier()));\n                    return bigNode;\n                }\n            }\n            while (this.nodeIterator.hasNext())\n            {\n                // Next, look for simple intersections\n                final Node candidateNode = this.nodeIterator.next();\n                if (!this.bigNodeIdentifiers.contains(candidateNode.getIdentifier()))\n                {\n                    if (candidateNode.connectedEdges().stream()\n                            .anyMatch(HighwayTag::isCarNavigableHighway))\n                    {\n                        this.bigNodeIdentifiers.add(candidateNode.getIdentifier());\n                        return new BigNode(candidateNode);\n                    }\n                }\n            }\n            // We reached the end of the list\n            return endOfData();\n        }\n    }\n\n    /**\n     * Minimum number of main edges involved in dual carriage way intersection\n     */\n    private static final int MIN_MAIN_EDGE_DUAL_CARRIAGEWAY_INTERSECTION = 2;\n    /**\n     * LEVENSHTEIN limit is used for fuzzy name match\n     */\n    private static final int LEVENSHTEIN_DISTANCE_THRESHOLD = 1;\n    private static final Logger logger = LoggerFactory.getLogger(BigNodeFinder.class);\n    /**\n     * The limits below are used as upper bounds for length of the junction edge. The limits for the\n     * junction edge are based not on road classification of the junction edge but the highest road\n     * class of the connected edges of the junction edge. These limits might need some tuning. In\n     * some cases like TestCase 6, the junction edge is not straight at an angle (hence longer\n     * length). TODO : Experiment with max length of junction Route?\n     */\n    private static final Distance SEARCH_RADIUS_MOTORWAY = Distance.meters(70);\n    private static final Distance SEARCH_RADIUS_TRUNK = Distance.meters(60);\n    private static final Distance SEARCH_RADIUS_PRIMARY = Distance.meters(50);\n    private static final Distance SEARCH_RADIUS_SECONDARY = Distance.meters(40);\n    private static final Distance SEARCH_RADIUS_TERTIARY = Distance.meters(35);\n    private static final Distance SEARCH_RADIUS_RESIDENTIAL = Distance.meters(25);\n    /**\n     * This is maximum number of edges in dual carriage way route and is used as safety threshold to\n     * prevent bad edge cases while constructing the big node\n     */\n    private static final int MAXIMUM_DUAL_CARRIAGEWAY_ROUTE_SIZE = 10;\n    /**\n     * This is a maximum number of possible exploratory routes when searching for dual carriage way\n     * junction routes. This is to prevent exponential slowdowns during rare edge cases, eg. when\n     * OSM ways overlap.\n     */\n    private static final int MAXIMUM_CANDIDATE_JUNCTION_ROUTE_SET_SIZE = 10_000;\n    public static final String LOWEST_JUNCTION_EDGE_CANDIDATE_HIGHWAY_KEY = \"LOWEST_JUNCTION_EDGE_CANDIDATE_HIGHWAY_TAG\";\n    public static final String LONG_JUNCTION_ROUTE_LENGTH_KEY = \"LONG_JUNCTION_ROUTE_LENGTH\";\n    public static final String NON_STRAIGHT_JUNCTION_EDGES_ANGLE_KEY = \"NON_STRAIGHT_JUNCTION_EDGES_ANGLE\";\n    private Map<String, Distance> radiusMap;\n    private Map<String, String> nonJunctionEdgeTagMap;\n    private HighwayTag lowestJunctionEdgeCandidateHighwayTag = HighwayTag.SERVICE;\n    // if junction route is longer than this length, we need to extra check to determine if it is\n    // a straight long junction route which is considered to be invalid junction route\n    private static final int LONG_JUNCTION_ROUTE_LENGTH = 100;\n    private static final int NON_STRAIGHT_JUNCTION_EDGES_ANGLE = 60;\n    private Distance longJunctionRouteLength = Distance.meters(LONG_JUNCTION_ROUTE_LENGTH);\n    private Angle nonStraightJunctionEdgesAngle = Angle.degrees(NON_STRAIGHT_JUNCTION_EDGES_ANGLE);\n    private final EdgeDirectionComparator edgeDirectionComparator = new EdgeDirectionComparator();\n    private final NameFinder nameFinder = new NameFinder().withTags(STANDARD_TAGS);\n\n    public BigNodeFinder()\n    {\n    }\n\n    public BigNodeFinder(final Map<String, Distance> radiusMap,\n            final Map<String, String> nonJunctionEdgeTagMap,\n            final Map<String, String> configurationMap)\n    {\n        this.radiusMap = radiusMap;\n        this.nonJunctionEdgeTagMap = nonJunctionEdgeTagMap;\n        if (configurationMap != null)\n        {\n            configure(configurationMap);\n        }\n    }\n\n    @Override\n    public Iterable<BigNode> find(final Atlas atlas)\n    {\n        /*\n         * Maintain a set of junction edges that are already part of a big Node. Sort the\n         * junctionEdge ids for maintaining order consistency\n         */\n        final Set<Long> junctionEdgeIds = new TreeSet<>();\n\n        /*\n         * For junctionRouteEdges (sequence of edges that for a junctionRoute), maintain the first\n         * edge\n         */\n        final Set<Long> junctionRouteEdgeIds = new TreeSet<>();\n\n        // First pass through edges\n        for (final Edge candidateEdge : atlas.edges(this::isCandidateJunctionEdge))\n        {\n            // Check if the candidate edge is already part of another big Node\n            if (!junctionEdgeIds.contains(candidateEdge.getIdentifier()))\n            {\n                if (isDualCarriageWayJunctionEdge(candidateEdge))\n                {\n                    junctionEdgeIds.add(candidateEdge.getIdentifier());\n                }\n                else\n                {\n                    // Expand Junction Edge to Junction Route before checking for Dual Carriage\n                    // way intersection\n                    final Optional<Route> junctionRoute = isDualCarriageWayJunctionRoute(\n                            Route.forEdge(candidateEdge));\n\n                    junctionRoute.ifPresent(route ->\n                    {\n                        final Edge startEdge = route.start();\n                        junctionRouteEdgeIds.add(startEdge.getIdentifier());\n                        route.forEach(edge -> junctionEdgeIds.add(edge.getIdentifier()));\n                    });\n                }\n            }\n        }\n\n        final Map<Long, BigNodeCandidate> nodeIdToBigNodeCandidateMap = new HashMap<>();\n        /*\n         * There are some cases where a junction edge is bidirectional. We do not explicitly remove\n         * the negative Edge in those cases. This may have to be revisited\n         */\n        while (!junctionEdgeIds.isEmpty())\n        {\n            final Long candidateEdgeId = junctionRouteEdgeIds.isEmpty()\n                    ? junctionEdgeIds.iterator().next()\n                    : junctionRouteEdgeIds.iterator().next();\n            final Edge candidate = atlas.edge(candidateEdgeId);\n            final Route mergedCandidate = mergeJunctionEdges(Route.forEdge(candidate),\n                    junctionEdgeIds);\n            logger.debug(\"Merged bigNode Route : {}. Number of Edges : {}\", mergedCandidate,\n                    mergedCandidate.size());\n            if (!isStraightLongRoute(mergedCandidate))\n            {\n                /*\n                 * mergedRoutes are formed after strict connectivity checks. But mergedRoutes can\n                 * share same node. Each node must have 1-1 mapping with a big node. So we merge\n                 * routes into same big node if they share a node\n                 */\n                final Set<Node> nodes = new HashSet<>();\n                mergedCandidate.forEach(edge -> nodes.addAll(edge.connectedNodes()));\n\n                final Set<BigNodeCandidate> bigNodesMergeCandidates = new HashSet<>();\n                for (final Node node : nodes)\n                {\n                    if (nodeIdToBigNodeCandidateMap.containsKey(node.getIdentifier()))\n                    {\n                        bigNodesMergeCandidates\n                                .add(nodeIdToBigNodeCandidateMap.get(node.getIdentifier()));\n                    }\n                }\n                final BigNodeCandidate bigNodeCandidate = BigNodeCandidate.from(nodes);\n                for (final BigNodeCandidate mergeBigNode : bigNodesMergeCandidates)\n                {\n                    bigNodeCandidate.merge(mergeBigNode);\n                }\n                for (final Long nodeIdentifier : bigNodeCandidate.getNodeIdentifiers())\n                {\n                    nodeIdToBigNodeCandidateMap.put(nodeIdentifier, bigNodeCandidate);\n                }\n            }\n            // Successfully added the bigNodeCandidate, can safely remove the junction edge\n            mergedCandidate.forEach(edge -> junctionEdgeIds.remove(edge.getIdentifier()));\n            mergedCandidate.forEach(edge -> junctionRouteEdgeIds.remove(edge.getIdentifier()));\n        }\n\n        final Set<BigNodeCandidate> bigNodeCandidateSet = new HashSet<>(\n                nodeIdToBigNodeCandidateMap.values());\n        logger.info(\n                \"Atlas has {} DualCarriageWay bigNodes with {} subNodes. Total Number of Big Nodes (including Simple Intersections) : {}\",\n                bigNodeCandidateSet.size(), nodeIdToBigNodeCandidateMap.keySet().size(),\n                atlas.numberOfNodes() - nodeIdToBigNodeCandidateMap.keySet().size()\n                        + bigNodeCandidateSet.size());\n        final BigNodeIterator bigNodeIterator = new BigNodeIterator(atlas, bigNodeCandidateSet);\n        return new BigNodeIterable(bigNodeIterator);\n    }\n\n    /**\n     * Find the big nodes and save them as geojson in a resource\n     *\n     * @param atlas\n     *            The atlas to look at\n     * @param writableResource\n     *            Where to save the geojson\n     */\n    public void findAndSaveBigNodesAsGeoJson(final Atlas atlas,\n            final WritableResource writableResource)\n    {\n        final List<LocationIterableProperties> features = new ArrayList<>();\n        Iterables.stream(this.find(atlas)).map(BigNode::asGeoJsonBigNode).forEach(features::add);\n        new GeoJsonBuilder().create(features).save(writableResource);\n    }\n\n    /**\n     * Find restricted paths of all bignodes and save them as geojson in a resource\n     *\n     * @param atlas\n     *            The atlas to look at\n     * @param writableResource\n     *            Where to save the geojson\n     */\n    public void findAndSaveRestrictedPathsAsGeoJson(final Atlas atlas,\n            final WritableResource writableResource)\n    {\n        final List<LocationIterableProperties> features = new ArrayList<>();\n        Iterables.stream(this.find(atlas)).flatMap(BigNode::asGeoJsonRestrictedPath)\n                .forEach(features::add);\n        new GeoJsonBuilder().create(features).save(writableResource);\n    }\n\n    /*\n     * Check if an edge contains any tags to be excluded as junction edges\n     */\n    protected boolean hasJunctionEdgeTags(final Edge edge)\n    {\n        if (this.nonJunctionEdgeTagMap != null)\n        {\n            return this.nonJunctionEdgeTagMap.entrySet().stream()\n                    .noneMatch(entry -> edge.getTags().containsKey(entry.getKey())\n                            && edge.getTags().get(entry.getKey()).equals(entry.getValue()));\n        }\n        return true;\n    }\n\n    // Check if merged junction candidate is a very long straight route which is normally not a\n    // valid junction route\n    protected boolean isStraightLongRoute(final Route mergedCandidate)\n    {\n        // if a junction route is merged route, it must have equal to or more than two junction\n        // edges\n        if (mergedCandidate.size() < 2)\n        {\n            return false;\n        }\n        // if merged candidate is longer than threshold, we will check if all junctions edges are\n        // from the same long and straight road\n        if (mergedCandidate.length().isGreaterThanOrEqualTo(this.longJunctionRouteLength))\n        {\n            final Iterator<Edge> iterator = mergedCandidate.iterator();\n            Edge currentEdge = iterator.next();\n            while (iterator.hasNext())\n            {\n                final Edge nextEdge = iterator.next();\n                final Optional<Heading> currentHeading = currentEdge.overallHeading();\n                final Optional<Heading> nextHeading = nextEdge.overallHeading();\n                // if the angle of any two edges are large than 60 degree, we think they are valid\n                // junction route\n                if (currentHeading.isPresent() && nextHeading.isPresent()\n                        && currentHeading.get().difference(nextHeading.get()).asPositiveAngle()\n                                .isGreaterThanOrEqualTo(this.nonStraightJunctionEdgesAngle))\n                {\n                    return false;\n                }\n                // if there are any two edges has different name, we think they are valid\n                if (currentEdge.getTag(NameTag.KEY).isPresent()\n                        && nextEdge.getTag(NameTag.KEY).isPresent()\n                        && !currentEdge.getTag(NameTag.KEY).get()\n                                .equalsIgnoreCase(nextEdge.getTag(NameTag.KEY).get()))\n                {\n                    return false;\n                }\n                currentEdge = nextEdge;\n            }\n            if (logger.isInfoEnabled())\n            {\n                if (mergedCandidate != null && mergedCandidate.asPolyLine() != null)\n                {\n                    logger.info(\"Invalid Merged Candidate Route length is {} with WKT {}\",\n                            mergedCandidate.length(), mergedCandidate.asPolyLine().toWkt());\n                }\n            }\n            return true;\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n    /*\n     * Check if the start and end node of an edge connects to the same edge. Example is\n     * https://www.openstreetmap.org/way/798542598 This type of edge should not be considered as\n     * junction edge\n     */\n    protected boolean startAndEndNodesConnectedToSameEdge(final Edge edge)\n    {\n        return edge.start().connectedEdges().stream()\n                .filter(connectedEdge -> Math.abs(connectedEdge.getIdentifier()) != Math\n                        .abs(edge.getIdentifier()))\n                .anyMatch(connectedEdge -> edge.end().connectedEdges().contains(connectedEdge));\n    }\n\n    private void configure(final Map<String, String> configurationMap)\n    {\n        if (configurationMap.get(LOWEST_JUNCTION_EDGE_CANDIDATE_HIGHWAY_KEY) != null)\n        {\n            this.lowestJunctionEdgeCandidateHighwayTag = HighwayTag.valueOf(\n                    configurationMap.get(LOWEST_JUNCTION_EDGE_CANDIDATE_HIGHWAY_KEY).toUpperCase());\n        }\n        if (configurationMap.get(LONG_JUNCTION_ROUTE_LENGTH_KEY) != null)\n        {\n            this.longJunctionRouteLength = Distance.meters(\n                    Double.parseDouble(configurationMap.get(LONG_JUNCTION_ROUTE_LENGTH_KEY)));\n        }\n        if (configurationMap.get(NON_STRAIGHT_JUNCTION_EDGES_ANGLE_KEY) != null)\n        {\n            this.nonStraightJunctionEdgesAngle = Angle.degrees(Double\n                    .parseDouble(configurationMap.get(NON_STRAIGHT_JUNCTION_EDGES_ANGLE_KEY)));\n        }\n    }\n\n    /**\n     * Identify {@link Edge} name matches when both {@link Edge}s have same names. When strict mode\n     * parameter is set to {@code true}, both edge names must be non-empty and must match. Exact\n     * match is recommended for residential roads\n     */\n    private boolean edgeNameExactMatch(final Edge edgeA, final Edge edgeB, final boolean strictMode)\n    {\n        final Optional<String> edgeAName = this.nameFinder.best(edgeA);\n        final Optional<String> edgeBName = this.nameFinder.best(edgeB);\n        if (edgeAName.isPresent() && edgeBName.isPresent())\n        {\n            return nameExactMatch(edgeAName.get(), edgeBName.get());\n        }\n        if (!strictMode && (!edgeAName.isPresent() || !edgeBName.isPresent()))\n        {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Identify {@link Edge} name matches when both {@link Edge}s have same names. When strict mode\n     * parameter is set to {@code true}, both edge names must be non-empty and must match. Instead\n     * of exact match, we allow for fuzzy match. TODO : Improve normalization (remove cardinal\n     * Directions before match?)\n     */\n    private boolean edgeNameFuzzyMatch(final Edge edgeA, final Edge edgeB, final boolean strictMode)\n    {\n        final Optional<String> edgeAName = this.nameFinder.best(edgeA);\n        final Optional<String> edgeBName = this.nameFinder.best(edgeB);\n        if (edgeAName.isPresent() && edgeBName.isPresent())\n        {\n            return nameFuzzyMatch(edgeAName.get(), edgeBName.get());\n        }\n        if (!strictMode && (!edgeAName.isPresent() || !edgeBName.isPresent()))\n        {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Test if an {@link Edge} is a candidate for expanding a {@link BigNode}, potentially becoming\n     * a Junction Edge in the process. This filters off all the {@link Edge}s that are less\n     * important than {@link HighwayTag#RESIDENTIAL}. It also filters off all the edges that are\n     * likely roundabouts and link Roads.\n     *\n     * @param edge\n     *            The candidate {@link Edge}\n     * @return {@code true} if the {@link Edge} is a candidate for expanding a {@link BigNode}\n     */\n    private boolean isCandidateJunctionEdge(final Edge edge)\n    {\n        final HighwayTag highwayTag = edge.highwayTag();\n        return isShortEnough(edge)\n                && highwayTag.isMoreImportantThanOrEqualTo(\n                        this.lowestJunctionEdgeCandidateHighwayTag)\n                && hasJunctionEdgeTags(edge) && !JunctionTag.isRoundabout(edge)\n                && !startAndEndNodesConnectedToSameEdge(edge);\n    }\n\n    private boolean isDualCarriageWayJunctionEdge(final Edge candidateEdge)\n    {\n        return isDualCarriageWayRoute(Route.forEdge(candidateEdge));\n    }\n\n    private Optional<Route> isDualCarriageWayJunctionRoute(final Route candidateJunctionRoute)\n    {\n        final Set<Route> candidateJunctionRoutes = new LinkedHashSet<>();\n        candidateJunctionRoutes.add(candidateJunctionRoute);\n        return isDualCarriageWayJunctionRoute(candidateJunctionRoutes);\n    }\n\n    /**\n     * Look through all the edges around this node and expand if edge is connected to another short\n     * Edge in same direction.\n     */\n    private Optional<Route> isDualCarriageWayJunctionRoute(final Set<Route> candidateJunctionRoutes)\n    {\n        if (candidateJunctionRoutes.size() > MAXIMUM_CANDIDATE_JUNCTION_ROUTE_SET_SIZE)\n        {\n            logger.warn(\n                    \"Aborting isDualCarriageWayJunctionRoute, candidate set size {} exceeded {}\",\n                    candidateJunctionRoutes.size(), MAXIMUM_CANDIDATE_JUNCTION_ROUTE_SET_SIZE);\n            return Optional.empty();\n        }\n        if (candidateJunctionRoutes.isEmpty())\n        {\n            return Optional.empty();\n        }\n        // Maintain a set of expandable routes.\n        final Set<Route> expandableJunctionRoutes = new LinkedHashSet<>();\n        for (final Route candidateJunctionRoute : candidateJunctionRoutes)\n        {\n            final Set<Edge> expandableEdges = new LinkedHashSet<>();\n            candidateJunctionRoute.end().outEdges().stream().filter(this::isCandidateJunctionEdge)\n                    .filter(edge -> edge.getMainEdgeIdentifier() != candidateJunctionRoute.end()\n                            .getMainEdgeIdentifier())\n                    .filter(edge -> this.edgeDirectionComparator\n                            .isSameDirection(candidateJunctionRoute.end(), edge, false))\n                    .forEach(expandableEdges::add);\n\n            for (final Edge expandableEdge : expandableEdges)\n            {\n                Route route = null;\n                try\n                {\n                    route = candidateJunctionRoute.append(expandableEdge);\n                }\n                catch (final CoreException e)\n                {\n                    throw new CoreException(\"Could not append dual carriageway route {} with {}\",\n                            candidateJunctionRoute, expandableEdge.getIdentifier(), e);\n                }\n                // If the routes are DualCarriageWayRoutes, then return\n                if (isDualCarriageWayRoute(route))\n                {\n                    logger.debug(\"Adding Dual Carriageway Junction Route : {}\", route);\n                    return Optional.of(route);\n                }\n                // Upper safety threshold to prevent bad edge cases\n                if (route.size() <= MAXIMUM_DUAL_CARRIAGEWAY_ROUTE_SIZE)\n                {\n                    expandableJunctionRoutes.add(route);\n                }\n                else\n                {\n                    logger.trace(\n                            \"Maximum number of edges in dual carriageway route  ({}) reached. Skipping route : {}\",\n                            MAXIMUM_DUAL_CARRIAGEWAY_ROUTE_SIZE, route);\n                }\n            }\n        }\n        return isDualCarriageWayJunctionRoute(expandableJunctionRoutes);\n    }\n\n    private boolean isDualCarriageWayRoute(final Route candidateRoute)\n    {\n        if (candidateRoute == null)\n        {\n            return false;\n        }\n        /*\n         * A restriction with at least 4 main edges in a dual carriage way intersection, used to\n         * filter out false positives, missed Test case 5 in BigNodeFinderTest. To increase\n         * coverage, this is set to 2.\n         */\n        if (candidateRoute.connectedEdges().stream().filter(Edge::isMainEdge)\n                .count() >= MIN_MAIN_EDGE_DUAL_CARRIAGEWAY_INTERSECTION)\n        {\n            for (final Edge inEdge : candidateRoute.start().inEdges())\n            {\n                // If inEdge is less important than RESIDENTIAL or has the same name as\n                // candidateEdge or has the same Heading as candidateEdge, then skip\n                if (inEdge.highwayTag().isLessImportantThan(HighwayTag.RESIDENTIAL)\n                        || this.edgeDirectionComparator.isSameDirection(candidateRoute.start(),\n                                inEdge, false)\n                        || edgeNameFuzzyMatch(candidateRoute.start(), inEdge, true))\n                {\n                    continue;\n                }\n                // If the candidateRoute has inEdge and outEdge that are in opposite direction\n                for (final Edge outEdge : candidateRoute.end().outEdges())\n                {\n                    /**\n                     * Usually Dual Carriage Way roads are one way. OutEdge and inEdge cannot have\n                     * name mismatch. Including other Car Navigable roads like Service roads\n                     * increases false positive cases.\n                     */\n                    if (outEdge.highwayTag().isMoreImportantThanOrEqualTo(HighwayTag.UNCLASSIFIED)\n                            // if an edge is considered as a junction edge, the dual carriage way\n                            // it connects to can not be link road\n                            && !inEdge.highwayTag().isLink() && !outEdge.highwayTag().isLink()\n                            && this.edgeDirectionComparator.isOppositeDirection(inEdge, outEdge,\n                                    false)\n                            && !outEdge.hasReverseEdge() && !inEdge.hasReverseEdge()\n                            && edgeNameFuzzyMatch(outEdge, inEdge, false))\n                    {\n                        return true;\n                    }\n                    /**\n                     * Enforce stricter name checks for Residential Roads to reduce false positives\n                     * and over merging of big nodes\n                     */\n                    if (outEdge.highwayTag() == HighwayTag.RESIDENTIAL\n                            && this.edgeDirectionComparator.isOppositeDirection(inEdge, outEdge,\n                                    false)\n                            && !outEdge.hasReverseEdge() && !inEdge.hasReverseEdge()\n                            && edgeNameExactMatch(outEdge, inEdge,\n                                    inEdge.highwayTag() != HighwayTag.RESIDENTIAL))\n                    {\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Determines if a candidate {@link Edge} can join the candidate {@link Route} to junction edges\n     * that can be merged to candidate {@link Route} to form a {@link BigNode}\n     */\n    private boolean isMergeCandidateEdge(final Edge candidateEdge, final Route candidateRoute,\n            final Set<Long> junctionEdgeIds)\n    {\n        if (isCandidateJunctionEdge(candidateEdge))\n        {\n            final Set<Edge> candidateRouteEdges = Sets.newHashSet(candidateRoute);\n            final Set<Long> candidateRouteEdgeIds = candidateRouteEdges.stream()\n                    .map(edge -> edge.getIdentifier()).collect(Collectors.toSet());\n\n            final Set<Long> filteredJunctionEdgeIds = Sets.difference(junctionEdgeIds,\n                    candidateRouteEdgeIds);\n            for (final Edge edge : candidateEdge.outEdges())\n            {\n                // Check if edge is connected to a junctionEdge with same name. Unless\n                // there is a name mismatch we merge them.\n                if (filteredJunctionEdgeIds.contains(edge.getIdentifier())\n                        && edgeNameFuzzyMatch(candidateRoute.end(), edge, false))\n                {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    private boolean isShortEnough(final Edge edge)\n    {\n        final Distance length = edge.length();\n        if (SEARCH_RADIUS_MOTORWAY.isLessThanOrEqualTo(length))\n        {\n            return false;\n        }\n        final HighwayTag highwayTag = mostSignificantConnectedHighwayType(edge);\n        return searchRadius(highwayTag).isGreaterThan(length);\n    }\n\n    /**\n     * Merging/coalescing connected junctionEdges together\n     */\n    private Route mergeJunctionEdges(final Route candidateRoute, final Set<Long> junctionEdgeIds)\n    {\n        // If the start Node and end Node are equal, we have a complete Route\n        if (candidateRoute.start().start().equals(candidateRoute.end().end()))\n        {\n            return candidateRoute;\n        }\n\n        final Set<Edge> connectedEdges = candidateRoute.end().outEdges();\n        connectedEdges.addAll(candidateRoute.start().inEdges());\n\n        /*\n         * The perfect case is when we have 4 junction edges that constitute a big node. We check if\n         * there are outgoing or incoming Edges that are connected to other junction edges. Use of a\n         * TreeSet is to consistently order the edge candidates so that we have a stable identifier\n         * for the bigNode\n         */\n        final Set<Edge> mergeCandidates = new TreeSet<>((edge1, edge2) -> ComparisonChain.start()\n                .compare(edge1.getIdentifier(), edge2.getIdentifier()).result());\n\n        final Set<Long> mainEdgeIdentifiers = new HashSet<>();\n        candidateRoute.forEach(edge -> mainEdgeIdentifiers.add(edge.getMainEdgeIdentifier()));\n\n        connectedEdges.stream().filter(edge -> junctionEdgeIds.contains(edge.getIdentifier()))\n                .filter(edge -> !candidateRoute.includes(edge))\n                .filter(edge -> !mainEdgeIdentifiers.contains(edge.getMainEdgeIdentifier()))\n                .forEach(mergeCandidates::add);\n        /*\n         * There are some cases where we have a couple of junction edges (instead of all four) that\n         * are part of big node\n         */\n        if (mergeCandidates.isEmpty())\n        {\n            connectedEdges.stream().filter(edge -> !junctionEdgeIds.contains(edge.getIdentifier()))\n                    .filter(edge -> !mainEdgeIdentifiers.contains(edge.getMainEdgeIdentifier()))\n                    .filter(edge -> isMergeCandidateEdge(edge, candidateRoute, junctionEdgeIds))\n                    .filter(edge -> !candidateRoute.includes(edge)).forEach(mergeCandidates::add);\n        }\n        if (!mergeCandidates.isEmpty())\n        {\n            for (final Edge mergeCandidate : mergeCandidates)\n            {\n                final Set<Edge> connectedEdge = Collections.singleton(mergeCandidate);\n                if (candidateRoute.end().isConnectedAtEndTo(connectedEdge))\n                {\n                    return mergeJunctionEdges(candidateRoute.append(mergeCandidate),\n                            junctionEdgeIds);\n                }\n                else if (candidateRoute.start().isConnectedAtStartTo(connectedEdge))\n                {\n                    return mergeJunctionEdges(Route.forEdge(mergeCandidate).append(candidateRoute),\n                            junctionEdgeIds);\n                }\n            }\n        }\n        return candidateRoute;\n    }\n\n    /**\n     * @param edge\n     *            The {@link Edge} to look at\n     * @return The most significant {@link HighwayTag} directly connected to this {@link Edge}\n     *         (including itself)\n     */\n    private HighwayTag mostSignificantConnectedHighwayType(final Edge edge)\n    {\n        HighwayTag edgeTag = edge.highwayTag();\n        for (final Edge connected : edge.connectedEdges())\n        {\n            final HighwayTag connectedTag = connected.highwayTag();\n            if (connectedTag.isMoreImportantThan(edgeTag))\n            {\n                edgeTag = connectedTag;\n            }\n        }\n        return edgeTag;\n    }\n\n    private boolean nameExactMatch(final String nameA, final String nameB)\n    {\n        return nameA.equalsIgnoreCase(nameB);\n    }\n\n    private boolean nameFuzzyMatch(final String nameA, final String nameB)\n    {\n        return nameA.equalsIgnoreCase(nameB) || StringUtils.getLevenshteinDistance(nameA, nameB,\n                LEVENSHTEIN_DISTANCE_THRESHOLD) != -1;\n    }\n\n    private Distance searchRadius(final HighwayTag highwayTag)\n    {\n        if (this.radiusMap != null && this.radiusMap.containsKey(highwayTag.getTagValue()))\n        {\n            return this.radiusMap.get(highwayTag.getTagValue());\n        }\n\n        if (highwayTag == HighwayTag.MOTORWAY || highwayTag == HighwayTag.MOTORWAY_LINK)\n        {\n            return SEARCH_RADIUS_MOTORWAY;\n        }\n        if (highwayTag == HighwayTag.TRUNK || highwayTag == HighwayTag.TRUNK_LINK)\n        {\n            return SEARCH_RADIUS_TRUNK;\n        }\n        if (highwayTag == HighwayTag.PRIMARY || highwayTag == HighwayTag.PRIMARY_LINK)\n        {\n            return SEARCH_RADIUS_PRIMARY;\n        }\n        if (highwayTag == HighwayTag.SECONDARY || highwayTag == HighwayTag.SECONDARY_LINK)\n        {\n            return SEARCH_RADIUS_SECONDARY;\n        }\n        if (highwayTag == HighwayTag.TERTIARY || highwayTag == HighwayTag.TERTIARY_LINK)\n        {\n            return SEARCH_RADIUS_TERTIARY;\n        }\n        return SEARCH_RADIUS_RESIDENTIAL;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/README.md",
    "content": "# BigNode\n\nBigNode is a concept of \"augmented\" intersection. It should match single decision points for a driver. This is mostly useful when inferring turn restrictions.\n\n## Structure\n\nA BigNode is a group of Nodes, each one Edge away from at least one other, and close to each other.\n\nFor example, two dual carriageways, when intersecting each other, can be modeled by a BigNode made of 4 Nodes at the 4 intersection points. The 4 internal Edges are referred to as \"Junction Edges\", and the edges directly connected to one of the Nodes, but at one end only, are the \"in-Edges\" and \"out-Edges\" to the BigNode.\n\n## Path\n\nA BigNode can provide a set of Route objects called paths. Each path is the shortest way through the BigNode from one in-Edge to one out-Edge.\n\n### RestrictedPath\n\nA RestrictedPath is a BigNode path that is restricted because of some TurnRestriction. It can be direct with a \"NO\\_TURN\" type restriction, or indirect with a \"ONLY\" restriction that the path is close to but not following (For example some BigNode has a right\\_turn\\_only restriction, then the path that goes straight and the one turning left will be restricted).\n\n## Visualization\n\nIt is easy to visualize all the BigNodes and inferred RestrictedPaths by calling the following methods, provided an Atlas:\n\n```\nBigNodeFinder.findAndSaveBigNodesAsGeoJson(Atlas, WritableResource);\n```\n\nand\n\n```\nBigNodeFinder.findAndSaveRestrictedPathsAsGeoJson(Atlas, WritableResource);\n```\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/RestrictedPath.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.bignode;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\n\n/**\n * A turn restriction within a {@link BigNode}. It indicates which {@link BigNode} it belongs to, as\n * well as the {@link Route} path that is forbidden within the {@link BigNode}\n *\n * @author matthieun\n */\npublic class RestrictedPath implements Located, Serializable\n{\n    private static final long serialVersionUID = 8003332683119555591L;\n\n    private final Route route;\n    private final BigNode parent;\n\n    protected RestrictedPath(final BigNode parent, final Route route)\n    {\n        this.route = route;\n        this.parent = parent;\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.route.bounds();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof RestrictedPath)\n        {\n            return this.route.equals(((RestrictedPath) other).getRoute())\n                    && this.parent == ((RestrictedPath) other).getParent();\n        }\n        return false;\n    }\n\n    public BigNode getParent()\n    {\n        return this.parent;\n    }\n\n    public Route getRoute()\n    {\n        return this.route;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.route.hashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[BigNode Restricted Path: \" + this.route + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/converters/AtlasBigNodeRestrictedPathToGeoJsonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.bignode.converters;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNode;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNodeFinder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * @author matthieun\n */\npublic class AtlasBigNodeRestrictedPathToGeoJsonConverter implements Converter<Atlas, GeoJsonObject>\n{\n    private final int logFrequency;\n\n    public AtlasBigNodeRestrictedPathToGeoJsonConverter(final int logFrequency)\n    {\n        this.logFrequency = logFrequency;\n    }\n\n    @Override\n    public GeoJsonObject convert(final Atlas atlas)\n    {\n        return new GeoJsonBuilder(this.logFrequency).create(Iterables\n                .translateMulti(new BigNodeFinder().find(atlas), BigNode::asGeoJsonRestrictedPath));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/converters/AtlasBigNodesToGeoJsonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.bignode.converters;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNode;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNodeFinder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * @author matthieun\n */\npublic class AtlasBigNodesToGeoJsonConverter implements Converter<Atlas, GeoJsonObject>\n{\n    @Override\n    public GeoJsonObject convert(final Atlas atlas)\n    {\n        return new GeoJsonBuilder().create(\n                Iterables.translate(new BigNodeFinder().find(atlas), BigNode::asGeoJsonBigNode));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/boundaries/ComplexBoundary.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.boundaries;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometryPrintable;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.locale.IsoCountry;\nimport org.openstreetmap.atlas.tags.AdministrativeLevelTag;\nimport org.openstreetmap.atlas.tags.BoundaryTag;\nimport org.openstreetmap.atlas.tags.Iso31662CountryTag;\nimport org.openstreetmap.atlas.tags.Iso31663CountryTag;\nimport org.openstreetmap.atlas.tags.Iso3166DefaultCountryTag;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonObject;\n\n/**\n * A representation of the Administrative Boundaries defined in the {@link BoundaryTag}.\n *\n * @author matthieun\n */\npublic class ComplexBoundary extends ComplexEntity implements GeometryPrintable\n{\n    private static final long serialVersionUID = 3836743004772506528L;\n\n    private static final Logger logger = LoggerFactory.getLogger(ComplexBoundary.class);\n    private static final RelationOrAreaToMultiPolygonConverter RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER = new RelationOrAreaToMultiPolygonConverter();\n\n    private MultiPolygon outline;\n    private Integer administrativeLevel;\n    private Set<IsoCountry> countries;\n    // The sub-areas as defined by the relation member role subarea\n    private Set<ComplexBoundary> subAreas = new HashSet<>();\n\n    private final List<ComplexEntityError> invalidations = new ArrayList<>();\n    private final transient Optional<Integer> administrativeLevelFilter;\n    private final boolean withSubAreas;\n\n    protected ComplexBoundary(final AtlasEntity source, final boolean withSubAreas,\n            final Optional<Integer> administrativeLevelFilter)\n    {\n        super(source);\n        this.administrativeLevelFilter = administrativeLevelFilter;\n        this.withSubAreas = withSubAreas;\n        try\n        {\n            this.populateAdministrativeLevelAndOutline();\n            if (withSubAreas)\n            {\n                this.populateSubAreas();\n                for (final ComplexBoundary boundary : this.subAreas)\n                {\n                    if (!boundary.isValid())\n                    {\n                        setInvalidReason(\"Some subAreas are invalid\", new CoreException(\n                                \"Some subArea(s) are invalid: {}\", boundary,\n                                boundary.getError().orElseThrow(\n                                        () -> new CoreException(\"Should have an error here.\"))\n                                        .getException()));\n                    }\n                }\n            }\n        }\n        catch (final Exception e)\n        {\n            setInvalidReason(\"Unable to create complex boundary from \" + source, e);\n            logger.warn(\"Unable to create complex boundary from {}, id {}. Reason: {}\",\n                    source.getType(), source.getIdentifier(), e.getMessage());\n        }\n    }\n\n    @Override\n    public JsonObject asGeoJson()\n    {\n        return this.outline.asGeoJsonGeometry();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof ComplexBoundary)\n        {\n            final ComplexBoundary that = (ComplexBoundary) other;\n            return this.administrativeLevel == that.getAdministrativeLevel()\n                    && this.outline.equals(that.getOutline());\n        }\n        return false;\n    }\n\n    public int getAdministrativeLevel()\n    {\n        return this.administrativeLevel;\n    }\n\n    @Override\n    public List<ComplexEntityError> getAllInvalidations()\n    {\n        return this.invalidations;\n    }\n\n    public Iterable<IsoCountry> getCountries()\n    {\n        return this.countries;\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.forJson(this.asGeoJson());\n    }\n\n    public MultiPolygon getOutline()\n    {\n        return this.outline;\n    }\n\n    public Set<ComplexBoundary> getSubAreas()\n    {\n        return this.subAreas;\n    }\n\n    public boolean hasCountryCode()\n    {\n        return isValid() && Iterables.size(this.countries) > 0;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(this.administrativeLevel, this.outline);\n    }\n\n    public void removeOuter(final Polygon outerToRemove)\n    {\n        final MultiMap<Polygon, Polygon> outersToInners = new MultiMap<>();\n        this.outline.outers().forEach(outer ->\n        {\n            final List<Polygon> innersForThisOuter = this.outline.innersOf(outer);\n            outersToInners.put(outer, innersForThisOuter);\n        });\n        outersToInners.remove(outerToRemove);\n        setOutline(new MultiPolygon(outersToInners));\n    }\n\n    public void setOutline(final MultiPolygon outline)\n    {\n        this.outline = outline;\n    }\n\n    @Override\n    public String toString()\n    {\n        return toString(\"\");\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return this.outline.toWkb();\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return this.outline.toWkt();\n    }\n\n    protected String toString(final String header)\n    {\n        return String.format(\n                header + \"[ComplexBoundary: Source = [%s, ID = %s]\\n\\t\" + header\n                        + \"Administrative Level = %s\\n\\t\" + header + \"Countries = %s\\n\\t\" + header\n                        + \"Name = %s\\n\\t\" + header + \"Outline = %s\\n\\t\" + header\n                        + \"Children = \\n%s\\n\" + header + \"]\",\n                this.getSource().getType(), this.getSource().getIdentifier(),\n                this.administrativeLevel, this.countries, NameTag.getNameOf(getSource()).orElse(\"\"),\n                this.outline == null ? \"MISSING\" : this.outline.toReadableString(),\n                new StringList(\n                        this.subAreas.stream().map(subArea -> subArea.toString(header + \"\\t\"))\n                                .collect(Collectors.toList()))\n                        .join(\"\\n\"));\n    }\n\n    /**\n     * Find the administrative level and the outline of the boundary\n     */\n    private void populateAdministrativeLevelAndOutline()\n    {\n        final AtlasEntity source = getSource();\n        if (source instanceof Relation || source instanceof Area)\n        {\n            final Optional<Integer> administrativeLevelOption = AdministrativeLevelTag\n                    .getAdministrativeLevel(source);\n            this.administrativeLevel = administrativeLevelOption.orElseThrow(\n                    () -> new CoreException(\"Invalid or missing administrative level for {} {}\",\n                            source.getType(), source.getIdentifier()));\n            // Store countries in a set to avoid duplicates.\n            this.countries = Iterables\n                    .stream(new MultiIterable<>(Iso31663CountryTag.all(source),\n                            Iso31662CountryTag.all(source), Iso3166DefaultCountryTag.all(source)))\n                    .collectToSet();\n            // Don't bother if the admin level is not the one expected\n            if (this.administrativeLevelFilter.isPresent()\n                    && !this.administrativeLevel.equals(this.administrativeLevelFilter.get()))\n            {\n                throw new CoreException(\"Administrative Level {} is not being queried.\",\n                        this.administrativeLevel);\n            }\n            this.outline = RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER.convert(source);\n        }\n        else\n        {\n            throw new CoreException(\"Supports only relations and areas.\");\n        }\n    }\n\n    /**\n     * Find the sub-areas if any\n     */\n    private void populateSubAreas()\n    {\n        this.subAreas = new HashSet<>();\n        final AtlasEntity source = getSource();\n        if (source instanceof Relation)\n        {\n            for (final RelationMember member : ((Relation) source).members())\n            {\n                final AtlasEntity childEntity = member.getEntity();\n                if (BoundaryTag.isAdministrative(childEntity)\n                        && RelationTypeTag.ADMINISTRATIVE_BOUNDARY_ROLE_SUB_AREA\n                                .equals(member.getRole()))\n                {\n                    final ComplexBoundary child = new ComplexBoundary(childEntity,\n                            this.withSubAreas, this.administrativeLevelFilter);\n                    this.subAreas.add(child);\n                    if (!child.isValid())\n                    {\n                        this.invalidations.addAll(child.getAllInvalidations());\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/boundaries/ComplexBoundaryFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.boundaries;\n\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.tags.AdministrativeLevelTag;\nimport org.openstreetmap.atlas.tags.BoundaryTag;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\n\n/**\n * Finder for {@link ComplexBoundary}(ies).\n *\n * @author matthieun\n */\npublic class ComplexBoundaryFinder implements Finder<ComplexBoundary>\n{\n    // If set to true, the boundaries will be structured with their sub-areas, and any\n    // sub-area that has a parent area will not be standalone. If set to false, all\n    // boundaries at all levels will be returned independently.\n    private boolean withSubAreas;\n    // If any, the administrative level to focus on only.\n    private Optional<Integer> administrativeLevel;\n\n    /**\n     * Construct the finder. All boundaries at all levels will be returned independently.\n     */\n    public ComplexBoundaryFinder()\n    {\n        this.withSubAreas = false;\n        this.administrativeLevel = Optional.empty();\n    }\n\n    @Override\n    public Iterable<ComplexBoundary> find(final Atlas atlas)\n    {\n        return Iterables.stream(new MultiIterable<>(atlas.relations(), atlas.areas()))\n                .filter(BoundaryTag::isAdministrative)\n                // Filter out the relations that are part of a larger admin boundary, as those will\n                // be brought in as part of the larger boundary.\n                .filter(this::subAreaFilter).map(entity -> new ComplexBoundary(entity,\n                        this.withSubAreas, this.administrativeLevel));\n    }\n\n    /**\n     * @param administrativeLevel\n     *            If any, the administrative level to focus on only.\n     */\n    public void setAdministrativeLevel(final int administrativeLevel)\n    {\n        final long minimum = AdministrativeLevelTag.minimumAdministrativeLevelValue();\n        final long maximum = AdministrativeLevelTag.maximumAdministrativeLevelValue();\n        if (administrativeLevel >= minimum && administrativeLevel <= maximum)\n        {\n            this.administrativeLevel = Optional.of(administrativeLevel);\n        }\n        else\n        {\n            throw new CoreException(\n                    \"Invalid administrative level: {}. Should be between {} and {} included.\",\n                    administrativeLevel, minimum, maximum);\n        }\n    }\n\n    /**\n     * @param withSubAreas\n     *            If set to true, the boundaries will be structured with their sub-areas, and any\n     *            sub-area that has a parent area will not be standalone. If set to false, all\n     *            boundaries at all levels will be returned independently.\n     */\n    public void setWithSubAreas(final boolean withSubAreas)\n    {\n        this.withSubAreas = withSubAreas;\n    }\n\n    /**\n     * @param entity\n     *            An Atlas entity\n     * @return True when the entity is has a role subarea within one of its administrative boundary\n     *         parent relations.\n     */\n    private boolean isSubArea(final AtlasEntity entity)\n    {\n        final Set<Relation> parentRelations = entity.relations();\n        for (final Relation parentRelation : parentRelations)\n        {\n            if (BoundaryTag.isAdministrative(parentRelation))\n            {\n                final RelationMemberList children = parentRelation.members();\n                for (final RelationMember child : children)\n                {\n                    final AtlasEntity childEntity = child.getEntity();\n                    if (childEntity.getClass().equals(entity.getClass())\n                            && childEntity.getIdentifier() == entity.getIdentifier())\n                    {\n                        if (RelationTypeTag.ADMINISTRATIVE_BOUNDARY_ROLE_SUB_AREA\n                                .equals(child.getRole()))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Filter out the sub areas only if the finder is looking for boundaries with sub areas\n     * included.\n     *\n     * @param entity\n     *            The entity to filter\n     * @return True if the entity is not filtered out\n     */\n    private boolean subAreaFilter(final AtlasEntity entity)\n    {\n        return this.withSubAreas ? isSubArea(entity) : true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/boundaries/converters/ComplexBoundaryIterableToGeoJsonWriter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.boundaries.converters;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.atlas.items.complex.boundaries.ComplexBoundary;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Write a set of complex boundaries to a geojson resource.\n *\n * @author matthieun\n */\npublic final class ComplexBoundaryIterableToGeoJsonWriter\n{\n    public static void saveAsGeojson(final Iterable<ComplexBoundary> complexBoundaries,\n            final WritableResource output)\n    {\n        try (JsonWriter writer = new JsonWriter(output))\n        {\n            final Iterable<LocationIterableProperties> geojsonObjects = Iterables\n                    .stream(complexBoundaries).flatMap(boundary ->\n                    {\n                        final Map<String, String> tags = boundary.getSource().getTags();\n                        return Iterables\n                                .stream(boundary.getOutline().asLocationIterableProperties())\n                                .map(locationIterableProperties ->\n                                {\n                                    locationIterableProperties.getProperties().putAll(tags);\n                                    return locationIterableProperties;\n                                });\n                    });\n            writer.write(new GeoJsonBuilder().create(geojsonObjects).jsonObject());\n        }\n    }\n\n    private ComplexBoundaryIterableToGeoJsonWriter()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/BuildingPart.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Part of a {@link ComplexBuilding}. Can be a simple {@link Area}, or a {@link MultiPolygon}\n * {@link Relation} that can be a building part with holes\n *\n * @author matthieun\n */\npublic class BuildingPart extends ComplexEntity\n{\n    private static final long serialVersionUID = 364620404649236692L;\n\n    private static final Logger logger = LoggerFactory.getLogger(BuildingPart.class);\n    private static final RelationOrAreaToMultiPolygonConverter BUILDING_OUTLINE_CONVERTER = new RelationOrAreaToMultiPolygonConverter();\n    private static final HeightConverter HEIGHT_CONVERTER = new HeightConverter();\n\n    private MultiPolygon geometry;\n\n    public BuildingPart(final AtlasEntity source)\n    {\n        super(source);\n        try\n        {\n            this.geometry = BUILDING_OUTLINE_CONVERTER.convert(source);\n        }\n        catch (final Exception e)\n        {\n            logger.warn(\"Unable to create building part from {}\", source, e);\n            setInvalidReason(\"Unable to create building part\", e);\n        }\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof BuildingPart)\n        {\n            return this.getGeometry().equals(((BuildingPart) other).getGeometry());\n        }\n        return false;\n    }\n\n    public MultiPolygon getGeometry()\n    {\n        return this.geometry;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(this.geometry);\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[BuildingPart: Geometry = \" + this.geometry.toReadableString() + \"]\";\n    }\n\n    /**\n     * @return The building part's top height\n     */\n    public Optional<Distance> topHeight()\n    {\n        final Map<String, String> tags = getSource().getTags();\n        final String heightTag = tags.get(\"height\");\n        try\n        {\n\n            if (heightTag != null)\n            {\n                return Optional.of(HEIGHT_CONVERTER.convert(heightTag));\n            }\n        }\n        catch (final Exception e)\n        {\n            logger.warn(\"Invalid height {} for building part id {}\", heightTag,\n                    getSource().getIdentifier());\n        }\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/ComplexBuilding.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.tags.BuildingLevelsTag;\nimport org.openstreetmap.atlas.tags.BuildingMinLevelTag;\nimport org.openstreetmap.atlas.tags.BuildingTag;\nimport org.openstreetmap.atlas.tags.MinHeightTag;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A complex building, that can be made of Parts, and have holes (MultiPolygon). It can also be very\n * simple and be made of only one {@link Area}.\n *\n * @author matthieun\n */\npublic class ComplexBuilding extends ComplexEntity\n{\n    private static final long serialVersionUID = 5351464852316720525L;\n\n    private static final Logger logger = LoggerFactory.getLogger(ComplexBuilding.class);\n    private static final RelationOrAreaToMultiPolygonConverter RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER = new RelationOrAreaToMultiPolygonConverter();\n    private static final JtsMultiPolygonToMultiPolygonConverter MULTIPOLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n    private static final HeightConverter HEIGHT_CONVERTER = new HeightConverter();\n\n    private final Set<BuildingPart> buildingParts;\n    private MultiPolygon outline = null;\n    private AtlasEntity outlineSource;\n\n    private final Set<Long> containedOSMIDs;\n\n    protected ComplexBuilding(final AtlasEntity source)\n    {\n        super(source);\n        this.buildingParts = new HashSet<>();\n        this.containedOSMIDs = new HashSet<>();\n        try\n        {\n            this.populateBuildingPartsAndOutline();\n        }\n        catch (final Exception e)\n        {\n            setInvalidReason(\"Unable to create complex building\", e);\n            logger.warn(\"Unable to create complex building from {}\", source, e);\n            return;\n        }\n    }\n\n    /**\n     * @return The building's base height\n     */\n    public Optional<Altitude> baseHeight()\n    {\n        return MinHeightTag.get(this.getSource());\n    }\n\n    public boolean containsOSMIdentifier(final long identifier)\n    {\n        return this.containedOSMIDs.contains(identifier);\n    }\n\n    @Override\n    public List<ComplexEntityError> getAllInvalidations()\n    {\n        final List<ComplexEntityError> returnValue = new ArrayList<>();\n        if (!isValid())\n        {\n            getError().ifPresent(returnValue::add);\n            this.buildingParts.stream().filter(part -> !part.isValid())\n                    .map(ComplexEntity::getAllInvalidations).flatMap(List::stream)\n                    .forEach(returnValue::add);\n        }\n        return returnValue;\n    }\n\n    public Set<BuildingPart> getBuildingParts()\n    {\n        return this.buildingParts;\n    }\n\n    /**\n     * @return The outline of the building.\n     */\n    public Optional<MultiPolygon> getOutline()\n    {\n        return Optional.ofNullable(this.outline);\n    }\n\n    /**\n     * @return The {@link AtlasEntity} representing the outline of the building\n     */\n    public AtlasEntity getOutlineSource()\n    {\n        return this.outlineSource;\n    }\n\n    @Override\n    public boolean isValid()\n    {\n        if (super.isValid())\n        {\n            for (final BuildingPart part : this.buildingParts)\n            {\n                if (!part.isValid())\n                {\n                    return false;\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    public Optional<Double> levels()\n    {\n        return BuildingLevelsTag.get(this.getSource());\n    }\n\n    public Optional<Double> minimumLevel()\n    {\n        return BuildingMinLevelTag.get(this.getSource());\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder parts = new StringBuilder();\n        for (final BuildingPart part : this.buildingParts)\n        {\n            parts.append(part);\n        }\n        return String.format(\"[ComplexBuilding:\\n\\tOutline = %s,\\n\\tParts = %s]\",\n                this.outline == null ? \"MISSING\" : this.outline.toReadableString(),\n                parts.toString());\n    }\n\n    /**\n     * @return The building's top height\n     */\n    public Optional<Distance> topHeight()\n    {\n        Map<String, String> tags = getSource().getTags();\n        String heightTag = tags.get(\"height\");\n        try\n        {\n            if (heightTag != null)\n            {\n                return Optional.of(HEIGHT_CONVERTER.convert(heightTag));\n            }\n            tags = this.outlineSource.getTags();\n            heightTag = tags.get(\"height\");\n            if (heightTag != null)\n            {\n                return Optional.of(HEIGHT_CONVERTER.convert(heightTag));\n            }\n        }\n        catch (final Exception e)\n        {\n            logger.warn(\"Invalid height {} for building id {}\", heightTag,\n                    getSource().getIdentifier());\n        }\n        return Optional.empty();\n    }\n\n    protected void populateBuildingPartsAndOutline()\n    {\n        this.containedOSMIDs.add(getOsmIdentifier());\n        final AtlasEntity source = getSource();\n        if (source instanceof Area)\n        {\n            // Simple case, yay!\n            this.outline = RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER.convert(source);\n            this.outlineSource = source;\n        }\n        else if (source instanceof Relation)\n        {\n            final Relation relation = (Relation) source;\n            final String type = relation.tag(RelationTypeTag.KEY);\n            // Two cases here. The relation can be a multipolygon (in case there are just holes and\n            // no parts) or a building relation, in case there are building parts.\n            final Optional<org.locationtech.jts.geom.MultiPolygon> geom = relation.asMultiPolygon();\n            if (RelationTypeTag.MULTIPOLYGON_TYPE.equals(type) && geom.isPresent())\n            {\n                // 1. Multipolygon. Relatively easy, there will be no building parts.\n                this.outline = MULTIPOLYGON_CONVERTER.convert(geom.get());\n                this.outlineSource = relation;\n            }\n            else if (BuildingTag.KEY.equals(type))\n            {\n                // 2. This relation is of an OSM 3D building. It should contain a member Area that\n                // is the outline, tagged as a building. It should also contain zero to many\n                // building:part=yes areas.\n                // 2.a. Loop through the roles and find the outline\n                for (final RelationMember member : relation.members())\n                {\n                    this.containedOSMIDs.add(member.getEntity().getOsmIdentifier());\n                    if (RelationTypeTag.BUILDING_ROLE_OUTLINE.equals(member.getRole()))\n                    {\n                        this.outline = RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER\n                                .convert(member.getEntity());\n                        this.outlineSource = member.getEntity();\n                    }\n                }\n                if (this.outline == null)\n                {\n                    throw new CoreException(\n                            \"Building part relation does not contain a building outline member\");\n                }\n                for (final RelationMember member : relation.members())\n                {\n                    this.containedOSMIDs.add(member.getEntity().getOsmIdentifier());\n                    if (RelationTypeTag.BUILDING_ROLE_PART.equals(member.getRole()))\n                    {\n                        this.buildingParts.add(new BuildingPart(member.getEntity()));\n                    }\n                }\n            }\n            else\n            {\n                throw new CoreException(\n                        \"A building relation can only be of type=multipolygon or type=building\");\n            }\n        }\n        else\n        {\n            throw new CoreException(\n                    \"A building can only be made of a Relation or an Area. This was a {}\",\n                    source.getClass().getName());\n        }\n        if (this.outline.outers().isEmpty())\n        {\n            throw new CoreException(\"A building cannot have no geometry.\");\n        }\n\n        try\n        {\n            // By fetching the surface we calculate the area: if that area is negative we throw an\n            // exception\n            this.outline.surface();\n        }\n        catch (final IllegalArgumentException oops)\n        {\n            throw new CoreException(\"Negative surface area\", oops);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/ComplexBuildingFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.tags.BuildingTag;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\n\n/**\n * {@link Finder} for {@link ComplexBuilding}s\n *\n * @author matthieun\n */\npublic class ComplexBuildingFinder implements Finder<ComplexBuilding>\n{\n    @Override\n    public Iterable<ComplexBuilding> find(final Atlas atlas)\n    {\n        // 1. Get all the simple buildings (Areas) that are not outlines.\n        final Iterable<Area> simpleBuildings = atlas\n                .areas(area -> isBuilding(area) && isSimple(area));\n        final Iterable<ComplexBuilding> simpleEntities = Iterables.translate(simpleBuildings,\n                ComplexBuilding::new);\n        // 2. Get all the complex buildings\n        final Iterable<Relation> complexBuildings = atlas\n                .relations(relation -> isBuilding(relation) && isSimple(relation));\n        final Iterable<ComplexBuilding> complexEntities = Iterables.translate(complexBuildings,\n                ComplexBuilding::new);\n        // 3. Combine them in a multi iterable.\n        return new MultiIterable<>(simpleEntities, complexEntities);\n    }\n\n    private boolean hasChildAreaAsBuilding(final Relation relation)\n    {\n        for (final RelationMember member : relation.members())\n        {\n            final AtlasEntity entity = member.getEntity();\n            final String role = member.getRole();\n            if (entity instanceof Area && RelationTypeTag.MULTIPOLYGON_ROLE_OUTER.equals(role)\n                    && isBuilding((Area) entity))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private boolean isBuilding(final Area area)\n    {\n        return BuildingTag.isBuilding(area);\n    }\n\n    private boolean isBuilding(final Relation relation)\n    {\n        final String type = relation.tag(RelationTypeTag.KEY);\n        /*\n         * If we have a multipolygon relation, then it can be a building if it has the building tag\n         * itself, or if one outer member of the relation has a building tag (this one is not\n         * recommended in OSM, but many occurrences happen)\n         */\n        return BuildingTag.KEY.equals(type) || RelationTypeTag.MULTIPOLYGON_TYPE.equals(type)\n                && (BuildingTag.isBuilding(relation) || hasChildAreaAsBuilding(relation));\n    }\n\n    private boolean isSimple(final AtlasEntity entity)\n    {\n        for (final Relation relation : entity.relations())\n        {\n            if (isBuilding(relation))\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/HeightConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * Example use: Building Height\n *\n * @deprecated use {@link org.openstreetmap.atlas.tags.HeightTag} {@code get()} or\n *             {@link org.openstreetmap.atlas.tags.annotations.extraction.AltitudeExtractor}\n *             instead.\n * @author matthieun\n */\n@Deprecated\npublic class HeightConverter implements StringConverter<Distance>\n{\n    private static final String METERS_SUFFIX = \" m\";\n\n    @Override\n    public Distance convert(final String object)\n    {\n        try\n        {\n            if (object.endsWith(METERS_SUFFIX))\n            {\n                return Distance.meters(\n                        Double.valueOf(object.substring(0, object.lastIndexOf(METERS_SUFFIX))));\n            }\n            if (object.contains(\"\\'\") || object.contains(\"\\\"\"))\n            {\n                final StringList split = StringList.split(object, \"\\'\");\n                if (split.size() == 2)\n                {\n                    return Distance.feetAndInches(Double.valueOf(split.get(0)), Double\n                            .valueOf(split.get(1).substring(0, split.get(1).lastIndexOf(\"\\\"\"))));\n                }\n                else if (split.size() == 1)\n                {\n                    return Distance.inches(Double\n                            .valueOf(split.get(1).substring(0, split.get(1).lastIndexOf(\"\\\"\"))));\n                }\n                else\n                {\n                    throw new CoreException(\"Invalid Feet & Inches height value: {}\", object);\n                }\n            }\n            return Distance.meters(Double.valueOf(object));\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Cannot parse height {}\", object, e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/converters/ComplexBuildingToGeojsonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings.converters;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuildingFinder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonSaver;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Get all the building outlines in an Atlas to a GeoJson file.\n *\n * @author matthieun\n */\npublic class ComplexBuildingToGeojsonConverter extends Command\n{\n    public static final Switch<Atlas> ATLAS_FOLDER = new Switch<>(\"atlasFolder\",\n            \"Folder containing the Atlas files to transcribe\",\n            value -> new AtlasResourceLoader().load(new File(value)), Optionality.REQUIRED);\n    public static final Switch<File> OUTPUT = new Switch<>(\"output\", \"The output GeoJson file\",\n            value -> new File(value), Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new ComplexBuildingToGeojsonConverter().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Atlas atlas = (Atlas) command.get(ATLAS_FOLDER);\n        final File output = (File) command.get(OUTPUT);\n        final Iterable<Polygon> shapes = Iterables.translateMulti(\n                new ComplexBuildingFinder().find(atlas),\n                building -> building.getOutline().get().outers());\n        GeoJsonSaver.save(shapes, output);\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(ATLAS_FOLDER, OUTPUT);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/ComplexHighwayArea.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.NavigableSet;\nimport java.util.TreeSet;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.base.MoreObjects;\n\n/**\n * A complex entity of highway areas.\n *\n * @author mgostintsev\n * @author isabellehillberg\n * @author cstaylor\n */\npublic class ComplexHighwayArea extends ComplexEntity\n{\n    private static final long serialVersionUID = 441824709133762710L;\n\n    private static final Logger logger = LoggerFactory.getLogger(ComplexHighwayArea.class);\n\n    private PolyLine boundary;\n\n    private final NavigableSet<Long> visitedEdgeIdentifiers = new TreeSet<>();\n\n    protected ComplexHighwayArea(final ComplexHighwayAreaHelper helper)\n    {\n        super(helper.getSourceEdge());\n        try\n        {\n            if (helper.getException() != null)\n            {\n                throw helper.getException();\n            }\n            this.boundary = helper.getBoundary();\n            this.visitedEdgeIdentifiers.addAll(helper.getVisitedEdgeIdentifiers());\n            if (isSelfIntersecting())\n            {\n                throw new CoreException(\"Self-intersecting geometry\");\n            }\n            if (isZeroSized())\n            {\n                throw new CoreException(\"Zero-sized area\");\n            }\n        }\n        catch (final Exception oops)\n        {\n            logger.warn(\"Unable to create ComplexHighwayArea from {}\", helper.getSourceEdge(),\n                    oops);\n            setInvalidReason(\"Couldn't create ComplexHighwayArea\", oops);\n        }\n    }\n\n    protected ComplexHighwayArea(final Edge edge)\n    {\n        this(new ComplexHighwayAreaHelper(edge));\n    }\n\n    public PolyLine getHighwayAreaBoundary()\n    {\n        return this.boundary;\n    }\n\n    /**\n     * Returns the atlas identifiers of all edges that were navigated when creating this highway\n     * area\n     *\n     * @return a sorted list of atlas edge identifiers that we visited when creating this highway\n     *         area\n     */\n    public NavigableSet<Long> getVisitedEdgeIdentifiers()\n    {\n        return this.visitedEdgeIdentifiers;\n    }\n\n    public boolean isSelfIntersecting()\n    {\n        return boundaryAsPolygon().selfIntersects();\n    }\n\n    public boolean isZeroSized()\n    {\n        return boundaryAsPolygon().surface().equals(Surface.MINIMUM);\n    }\n\n    @Override\n    public String toString()\n    {\n        return MoreObjects.toStringHelper(this).add(\"osm\", getSource().getOsmIdentifier())\n                .add(\"atlas\", getSource().getIdentifier()).add(\"boundary\", this.boundary)\n                .toString();\n    }\n\n    private Polygon boundaryAsPolygon()\n    {\n        if (!isValid())\n        {\n            throw new CoreException(\"Highway Area is invalid {}\", getOsmIdentifier());\n        }\n        final List<Location> locations = new ArrayList<>();\n        locations.addAll(this.boundary);\n        return new Polygon(locations);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/ComplexHighwayAreaFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.tags.AreaTag;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * A simple finder for a {@link ComplexHighwayArea}\n *\n * @author isabellehillberg\n */\npublic class ComplexHighwayAreaFinder implements Finder<ComplexHighwayArea>\n{\n    private static Optional<ComplexHighwayArea> toEntity(final Edge edge)\n    {\n        final ComplexHighwayArea complexHighwayAreaEntity = new ComplexHighwayArea(edge);\n        return Optional.of(complexHighwayAreaEntity);\n    }\n\n    private static boolean validEdge(final Edge edge)\n    {\n        return Validators.isNotOfType(edge, HighwayTag.class, HighwayTag.NO)\n                && Validators.isOfType(edge, AreaTag.class, AreaTag.YES) && edge.isMainEdge();\n    }\n\n    @Override\n    public Iterable<ComplexHighwayArea> find(final Atlas atlas)\n    {\n        final Set<Long> visitedEdgeIdentifiers = new HashSet<>();\n        return Iterables.stream(atlas.edges())\n                .flatMap(edges -> processEntity(edges, visitedEdgeIdentifiers));\n    }\n\n    private List<ComplexHighwayArea> processEntity(final Edge edge,\n            final Set<Long> visitedEdgeIdentifiers)\n    {\n        final List<ComplexHighwayArea> returnValue = new ArrayList<>();\n        if (!visitedEdgeIdentifiers.contains(edge.getIdentifier()))\n        {\n            Stream.of(edge).filter(ComplexHighwayAreaFinder::validEdge)\n                    .map(ComplexHighwayAreaFinder::toEntity).filter(Optional::isPresent)\n                    .map(Optional::get).forEach(area ->\n                    {\n                        visitedEdgeIdentifiers.addAll(area.getVisitedEdgeIdentifiers());\n                        returnValue.add(area);\n                    });\n        }\n        return returnValue;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/ComplexHighwayAreaHelper.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport java.util.NavigableSet;\nimport java.util.Optional;\nimport java.util.TreeSet;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\n\n/**\n * Since the constructor for ComplexHighwayArea must pass the source edge immediately we need to do\n * all of the processing of routes and which edges are the lowest ordered ones outside of that\n * class.\n *\n * @author cstaylor\n */\nclass ComplexHighwayAreaHelper\n{\n    private final NavigableSet<Long> visitedEdgeIdentifiers = new TreeSet<>();\n\n    private PolyLine boundary;\n\n    private Edge sourceEdge;\n\n    private CoreException oops;\n\n    ComplexHighwayAreaHelper(final Edge edge)\n    {\n        this.sourceEdge = edge;\n\n        buildHighwayAreaBoundary(Route.forEdge(edge)).ifPresent(route ->\n        {\n            this.boundary = route.asPolyLine();\n            StreamSupport.stream(route.spliterator(), false).map(Edge::getIdentifier)\n                    .forEach(this.visitedEdgeIdentifiers::add);\n            this.sourceEdge = edge.getAtlas().edge(this.visitedEdgeIdentifiers.first());\n        });\n        if (this.boundary == null)\n        {\n            this.oops = new CoreException(\"Unable to build boundary for edge {}\",\n                    edge.getOsmIdentifier());\n        }\n    }\n\n    PolyLine getBoundary()\n    {\n        return this.boundary;\n    }\n\n    CoreException getException()\n    {\n        return this.oops;\n    }\n\n    Edge getSourceEdge()\n    {\n        return this.sourceEdge;\n    }\n\n    NavigableSet<Long> getVisitedEdgeIdentifiers()\n    {\n        return this.visitedEdgeIdentifiers;\n    }\n\n    private Optional<Route> buildHighwayAreaBoundary(final Route boundary)\n    {\n        for (final Edge edge : boundary.end().end().connectedEdges())\n        {\n            if (canAddEdgeToBoundary(edge, boundary))\n            {\n                final Route extendedBoundary = boundary.append(edge);\n                if (extendedBoundary.end().end().getLocation()\n                        .equals(extendedBoundary.start().start().getLocation()))\n                {\n                    return Optional.of(extendedBoundary);\n                }\n                else\n                {\n                    return buildHighwayAreaBoundary(extendedBoundary);\n                }\n            }\n        }\n        return Optional.empty();\n    }\n\n    // 1. Traversing in one direction, don't add any reverse edges\n    // 2. There are some overlapping areas (bad data) which represent the same entity. To avoid\n    // adding incorrect edges, only add edges with the same OSM identifier.\n    // 3. The end location of the boundary matches the start location of the candidate edge.\n    // 4. No duplicate edges.\n    private boolean canAddEdgeToBoundary(final Edge edge, final Route boundary)\n    {\n        return edge.getIdentifier() != -boundary.end().getIdentifier()\n                && edge.getOsmIdentifier() == boundary.end().getOsmIdentifier()\n                && boundary.end().end().getLocation().equals(edge.start().getLocation())\n                && !boundary.includes(edge);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/islands/ComplexIsland.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.islands;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * In cases of lakes/reservoirs with islands, they are modeled as relations of multi-polygon type.\n * The outer polygons are usually the lakes. The inner polygons are islands. See\n * http://www.openstreetmap.org/relation/2314241\n *\n * @author Sid\n */\npublic class ComplexIsland extends ComplexEntity\n{\n    private static final long serialVersionUID = 7840944233946510730L;\n\n    private static final RelationOrAreaToMultiPolygonConverter RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER = new RelationOrAreaToMultiPolygonConverter();\n    private static final JtsMultiPolygonToMultiPolygonConverter MULTIPOLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n\n    private static final Logger logger = LoggerFactory.getLogger(ComplexIsland.class);\n\n    private MultiPolygon multiPolygon;\n\n    public ComplexIsland(final AtlasEntity source)\n    {\n        super(source);\n        try\n        {\n            populateGeometry();\n        }\n        catch (final Exception e)\n        {\n            logger.warn(\"Unable to create complex islands from {}\", source, e);\n            setInvalidReason(\"Unable to create complex islands\", e);\n        }\n    }\n\n    public MultiPolygon getGeometry()\n    {\n        return this.multiPolygon;\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"Island : \" + getSource();\n    }\n\n    private void populateGeometry()\n    {\n        final AtlasEntity source = getSource();\n        if (source instanceof Relation)\n        {\n            final Relation relation = (Relation) source;\n            final String type = relation.tag(RelationTypeTag.KEY);\n            final Optional<org.locationtech.jts.geom.MultiPolygon> geom = relation.asMultiPolygon();\n            if (RelationTypeTag.MULTIPOLYGON_TYPE.equals(type) && geom.isPresent())\n            {\n                this.multiPolygon = MULTIPOLYGON_CONVERTER.convert(geom.get());\n                return;\n            }\n        }\n        else if (source instanceof Area)\n        {\n            this.multiPolygon = RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER.convert(source);\n            return;\n        }\n        throw new CoreException(\"Geometry is not set for {}\", source);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/islands/ComplexIslandFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.islands;\n\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.WaterIslandConfigurationReader;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author Sid\n * @author sbhalekar\n */\npublic class ComplexIslandFinder implements Finder<ComplexIsland>\n{\n    private final WaterIslandConfigurationReader islandConfigurationReader;\n\n    public ComplexIslandFinder()\n    {\n        this.islandConfigurationReader = new DefaultIslandConfigurationReader(\"islands.json\");\n    }\n\n    public ComplexIslandFinder(final Resource resource)\n    {\n        this.islandConfigurationReader = new DefaultIslandConfigurationReader(resource);\n    }\n\n    /**\n     * Use the configuration reader and convert all the allowed atlas entities into\n     * {@link ComplexIsland}\n     *\n     * @param atlas\n     *            The {@link Atlas} to browse.\n     * @return an {@link Iterable} of the {@link ComplexIsland}s in the given {@link Atlas}\n     */\n    @Override\n    public Iterable<ComplexIsland> find(final Atlas atlas)\n    {\n        return StreamSupport\n                .stream(Iterables\n                        .translate(atlas.entities(), this.islandConfigurationReader::convert)\n                        .spliterator(), false)\n                .filter(Optional::isPresent).map(Optional::get)\n                .filter(object -> object instanceof ComplexIsland)\n                .map(object -> (ComplexIsland) object).collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/islands/DefaultIslandConfigurationReader.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.islands;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.WaterIslandConfigurationReader;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.configuration.ConfiguredFilter;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\n\n/**\n * Class used by {@link ComplexIslandFinder} to read the island configuration and map\n * {@link AtlasEntity} to {@link ComplexIsland}\n *\n * @author sbhalekar\n */\npublic class DefaultIslandConfigurationReader\n        extends WaterIslandConfigurationReader<ConfiguredFilter>\n{\n    public DefaultIslandConfigurationReader(final Resource resource)\n    {\n        super(resource);\n    }\n\n    public DefaultIslandConfigurationReader(final String resourceName)\n    {\n        this(new InputStreamResource(\n                () -> DefaultIslandConfigurationReader.class.getResourceAsStream(resourceName)));\n    }\n\n    @Override\n    protected Optional<ComplexIsland> createComplexEntity(final AtlasEntity atlasEntity)\n    {\n        return this.getConfigurationMapper().test(atlasEntity)\n                ? Optional.of(new ComplexIsland(atlasEntity))\n                : Optional.empty();\n    }\n\n    @Override\n    protected ConfiguredFilter readConfiguration(final Resource configurationResource)\n    {\n        return ConfiguredFilter.getDefaultFilter(new StandardConfiguration(configurationResource));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/landcover/ComplexLandCover.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.landcover;\n\nimport java.io.InputStreamReader;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonParser;\n\n/**\n * @author sbhalekar\n * @author samg\n */\npublic final class ComplexLandCover extends ComplexEntity\n{\n    private static final Logger logger = LoggerFactory.getLogger(ComplexLandCover.class);\n    private static final long serialVersionUID = 220683230343177634L;\n    private static final RelationOrAreaToMultiPolygonConverter MULTIPOLYGON_CONVERTER = new RelationOrAreaToMultiPolygonConverter();\n    private static final String LAND_COVER_RESOURCE = \"land-cover-tag-filter.json\";\n    // The default LandCover tags\n    private static List<TaggableFilter> defaultTaggableFilter;\n    private final MultiPolygon multiPolygon;\n\n    /**\n     * This method creates a {@link ComplexLandCover} for the specified {@link AtlasEntity} if it\n     * meets the requirements for Complex LandCover relation. The LandCover tags are checked against\n     * the default tags.\n     *\n     * @param source\n     *            The {@link AtlasEntity} for which the ComplexEntity is created\n     * @return {@link ComplexLandCover} if created, else return empty.\n     */\n    public static Optional<ComplexLandCover> getComplexLandCover(final AtlasEntity source)\n    {\n        if (defaultTaggableFilter == null)\n        {\n            computeDefaultFilter();\n        }\n        return getComplexLandCover(source, ComplexLandCover::hasLandCoverTag);\n    }\n\n    /**\n     * This method creates a {@link ComplexLandCover} for the specified {@link AtlasEntity} and\n     * {@link TaggableFilter}. The land cover tags are checked against the landCoverFilter param as\n     * well as the default tags.\n     *\n     * @param source\n     *            The {@link AtlasEntity} for which the ComplexEntity is created\n     * @param landCoverFilter\n     *            The {@link TaggableFilter} of land cover tags against which the relation is\n     *            checked for land cover tags\n     * @return {@link ComplexLandCover} if created, else return empty.\n     */\n    public static Optional<ComplexLandCover> getComplexLandCover(final AtlasEntity source,\n            final Predicate<Taggable> landCoverFilter)\n    {\n        try\n        {\n            return ((source instanceof Relation || source instanceof Area)\n                    && landCoverFilter.test(source)) ? Optional.of(new ComplexLandCover(source))\n                            : Optional.empty();\n        }\n        catch (final Exception exception)\n        {\n            logger.warn(\"Unable to create complex land cover relations from {}\", source, exception);\n            return Optional.empty();\n        }\n    }\n\n    private static void computeDefaultFilter()\n    {\n        try (InputStreamReader reader = new InputStreamReader(\n                ComplexLandCover.class.getResourceAsStream(LAND_COVER_RESOURCE)))\n        {\n            final JsonElement element = new JsonParser().parse(reader);\n            final JsonArray filters = element.getAsJsonObject().get(\"filters\").getAsJsonArray();\n            defaultTaggableFilter = StreamSupport.stream(filters.spliterator(), false)\n                    .map(jsonElement -> TaggableFilter.forDefinition(jsonElement.getAsString()))\n                    .collect(Collectors.toList());\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\n                    \"There was a problem parsing aoi-tag-filter.json. Check if the JSON file has valid structure.\",\n                    exception);\n        }\n    }\n\n    /**\n     * Checks for land cover tags in the object\n     *\n     * @param source\n     *            {@link AtlasEntity} that needs to be checked for land cover tags\n     * @return {@code true} if the source has land cover tags\n     */\n    private static boolean hasLandCoverTag(final Taggable source)\n    {\n        return defaultTaggableFilter.stream()\n                .anyMatch(taggableFilter -> taggableFilter.test(source));\n    }\n\n    private ComplexLandCover(final AtlasEntity source)\n    {\n        super(source);\n        try\n        {\n            this.multiPolygon = MULTIPOLYGON_CONVERTER.convert(source);\n        }\n        catch (final Exception exception)\n        {\n            setInvalidReason(\"Unable to convert the AtlasEntity to MultiPolygon\", exception);\n            throw new CoreException(\"Unable to convert the AtlasEntity {} to MultiPolygon\",\n                    source.getOsmIdentifier(), exception);\n        }\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        return other instanceof ComplexLandCover && super.equals(other);\n    }\n\n    public MultiPolygon getGeometry()\n    {\n        return this.multiPolygon;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getName() + \" \" + getSource();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/landcover/ComplexLandCoverFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.landcover;\n\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\n\n/**\n * {@link ComplexLandCover} finder.\n *\n * @author samg\n */\npublic class ComplexLandCoverFinder implements Finder<ComplexLandCover>\n{\n    /**\n     * Finds all relations and areas that are candidates for {@link ComplexLandCover} and converts\n     * them into {@link ComplexLandCover}.\n     *\n     * @param atlas\n     *            The {@link Atlas} to browse.\n     * @return {@link Iterables} of {@link ComplexLandCover}.\n     */\n    @Override\n    public Iterable<ComplexLandCover> find(final Atlas atlas)\n    {\n        final Iterable<ComplexLandCover> iterableOfComplexLandCoverRelations = StreamSupport\n                .stream(atlas.relations().spliterator(), true)\n                .map(ComplexLandCover::getComplexLandCover).filter(Optional::isPresent)\n                .map(Optional::get).collect(Collectors.toList());\n        final Iterable<ComplexLandCover> iterableOfComplexLandCoverAreas = StreamSupport\n                .stream(atlas.areas().spliterator(), true)\n                .map(ComplexLandCover::getComplexLandCover).filter(Optional::isPresent)\n                .map(Optional::get).collect(Collectors.toList());\n        return new MultiIterable<>(iterableOfComplexLandCoverRelations,\n                iterableOfComplexLandCoverAreas);\n    }\n\n    /**\n     * Finds all relations and areas that are candidates for {@link ComplexLandCover} with given\n     * land cover tags and converts them into {@link ComplexLandCover}.\n     *\n     * @param atlas\n     *            Atlas to build the ComplexLandCover\n     * @param landCoverFilter\n     *            {@link TaggableFilter} land cover taggable filter\n     * @return {@link Iterables} of {@link ComplexLandCover}.\n     */\n    public Iterable<ComplexLandCover> find(final Atlas atlas, final TaggableFilter landCoverFilter)\n    {\n        final Iterable<ComplexLandCover> iterableOfComplexLandCoverRelations = StreamSupport\n                .stream(atlas.relations().spliterator(), true)\n                .map(relation -> ComplexLandCover.getComplexLandCover(relation, landCoverFilter))\n                .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());\n        final Iterable<ComplexLandCover> iterableOfComplexLandCoverAreas = StreamSupport\n                .stream(atlas.areas().spliterator(), true)\n                .map(area -> ComplexLandCover.getComplexLandCover(area, landCoverFilter))\n                .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());\n        return new MultiIterable<>(iterableOfComplexLandCoverRelations,\n                iterableOfComplexLandCoverAreas);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/restriction/ComplexTurnRestriction.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.restriction;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.items.TurnRestriction;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A Complex turn restriction from one atlas.\n *\n * @author matthieun\n * @author cstaylor\n */\npublic class ComplexTurnRestriction extends ComplexEntity\n{\n    private static final Logger logger = LoggerFactory.getLogger(ComplexTurnRestriction.class);\n\n    private static final long serialVersionUID = 8558201688502883714L;\n\n    private TurnRestriction turnRestriction;\n\n    protected ComplexTurnRestriction(final AtlasEntity source, final Predicate<Edge> validEdge)\n    {\n        super(source);\n        try\n        {\n            this.turnRestriction = TurnRestriction.from((Relation) source)\n                    .orElseThrow(() -> new CoreException(\n                            \"{} is not a turn restriction according to Atlas\",\n                            source.getIdentifier()));\n\n            final Route route = this.turnRestriction.route();\n            final int routeLength = route.size();\n\n            if (routeLength < 2)\n            {\n                throw new CoreException(\"Must have at least two edges in the route\");\n            }\n\n            final long filteredLength = StreamSupport.stream(route.spliterator(), false)\n                    .filter(validEdge).count();\n            if (filteredLength < routeLength)\n            {\n                throw new CoreException(\"{} invalid edges\", routeLength - filteredLength);\n            }\n        }\n        catch (final Exception oops)\n        {\n            logger.trace(\"Unable to create ComplexTurnRestriction from {}\", source, oops);\n            setInvalidReason(\"Couldn't create ComplexTurnRestriction\", oops);\n        }\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.route().bounds();\n    }\n\n    @Override\n    public List<ComplexEntityError> getAllInvalidations()\n    {\n        final List<ComplexEntityError> returnValue = new ArrayList<>();\n        if (!isValid())\n        {\n            returnValue.add(new ComplexEntityError(this, \"turn restriction is null\"));\n        }\n        return returnValue;\n    }\n\n    public TurnRestriction getTurnRestriction()\n    {\n        return this.turnRestriction;\n    }\n\n    /**\n     * Proxy for TurnRestriction.route()\n     *\n     * @return The Route represented by this {@link ComplexTurnRestriction}\n     */\n    public Route route()\n    {\n        if (this.turnRestriction != null)\n        {\n            return this.turnRestriction.route();\n        }\n        else\n        {\n            return null;\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[ComplexTurnRestriction: \" + this.turnRestriction + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/restriction/ComplexTurnRestrictionFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.restriction;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.tags.TurnRestrictionTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * A simple finder for a {@link ComplexTurnRestriction}\n *\n * @author matthieun\n */\npublic class ComplexTurnRestrictionFinder implements Finder<ComplexTurnRestriction>\n{\n    private final Predicate<Edge> acceptableEdges;\n\n    public ComplexTurnRestrictionFinder()\n    {\n        this(x -> true);\n    }\n\n    public ComplexTurnRestrictionFinder(final Predicate<Edge> acceptableEdges)\n    {\n        this.acceptableEdges = acceptableEdges == null ? x -> true : acceptableEdges;\n    }\n\n    @Override\n    public Iterable<ComplexTurnRestriction> find(final Atlas atlas)\n    {\n        return Iterables.translate(atlas.relations(TurnRestrictionTag::isRestriction),\n                relation -> new ComplexTurnRestriction(relation, this.acceptableEdges));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/restriction/converters/AtlasTurnRestrictionsToGeoJsonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.restriction.converters;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.restriction.ComplexTurnRestrictionFinder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * @author matthieun\n */\npublic class AtlasTurnRestrictionsToGeoJsonConverter implements Converter<Atlas, GeoJsonObject>\n{\n    @Override\n    public GeoJsonObject convert(final Atlas atlas)\n    {\n        return new GeoJsonBuilder()\n                .create(Iterables.translate(new ComplexTurnRestrictionFinder().find(atlas),\n                        turnRestriction -> turnRestriction.getTurnRestriction().asGeoJson()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/roundabout/ComplexRoundabout.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.roundabout;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.walker.SimpleEdgeWalker;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.tags.JunctionTag;\n\n/**\n * A {@link ComplexEntity} representation of a roundabout. To form a valid {@link ComplexRoundabout}\n * the source {@link Edge} and all connected Edges tagged as a roundabout must meet the following\n * criteria: be one way; be car navigable; together form a single closed {@link Route} that has the\n * appropriate directionality based on the country. Adapted from the MalformedRoundaboutCheck in\n * Atlas-Checks.\n *\n * @author bbreithaupt\n * @author savannahostrowski\n */\npublic class ComplexRoundabout extends ComplexEntity\n{\n    /**\n     * An enum of RoundaboutDirections\n     */\n    public enum RoundaboutDirection\n    {\n        CLOCKWISE,\n        COUNTERCLOCKWISE,\n        // Handles the case where we were unable to get any information about the roundabout's\n        // direction.\n        UNKNOWN\n    }\n\n    protected static final String WRONG_WAY_INVALIDATION = \"This roundabout is going the wrong direction, or has been improperly tagged as a roundabout.\";\n    protected static final String INCOMPLETE_ROUTE_INVALIDATION = \"This roundabout does not form a single, one-way, complete, car navigable route.\";\n    private static final String EXCEPTION_MESSAGE = \"Exception thrown while trying to build a ComplexRoundabout\";\n    // Country default source:\n    // https://en.wikipedia.org/wiki/List_of_countries_with_left-hand_traffic\n    private static final List<String> LEFT_DRIVING_COUNTRIES_DEFAULT = Arrays.asList(\"AIA\", \"ATG\",\n            \"AUS\", \"BGD\", \"BHS\", \"BMU\", \"BRB\", \"BRN\", \"BTN\", \"BWA\", \"CCK\", \"COK\", \"CXR\", \"CYM\",\n            \"CYP\", \"DMA\", \"FJI\", \"FLK\", \"GBR\", \"GGY\", \"GRD\", \"GUY\", \"HKG\", \"IDN\", \"IMN\", \"IND\",\n            \"IRL\", \"JAM\", \"JEY\", \"JPN\", \"KEN\", \"KIR\", \"KNA\", \"LCA\", \"LKA\", \"LSO\", \"MAC\", \"MDV\",\n            \"MLT\", \"MOZ\", \"MSR\", \"MUS\", \"MWI\", \"MYS\", \"NAM\", \"NFK\", \"NIU\", \"NPL\", \"NRU\", \"NZL\",\n            \"PAK\", \"PCN\", \"PNG\", \"SGP\", \"SGS\", \"SHN\", \"SLB\", \"SUR\", \"SWZ\", \"SYC\", \"TCA\", \"THA\",\n            \"TKL\", \"TLS\", \"TON\", \"TTO\", \"TUV\", \"TZA\", \"UGA\", \"VCT\", \"VGB\", \"VIR\", \"WSM\", \"ZAF\",\n            \"ZMB\", \"ZWE\");\n    private static final long serialVersionUID = 2512054399729675784L;\n    private final List<ComplexEntityError> invalidationReasons = new ArrayList<>();\n    private Set<Edge> roundaboutEdgeSet;\n    private Route roundaboutRoute;\n\n    /**\n     * This method returns a RoundaboutDirection enum which indicates direction of the flow of\n     * traffic based on the cross product of two adjacent edges. This method leverages the\n     * right-hand rule as it relates to the directionality of two vectors.\n     *\n     * @see \"https://en.wikipedia.org/wiki/Right-hand_rule\"\n     * @param roundaboutEdges\n     *            A list of Edges in a roundabout\n     * @return CLOCKWISE or COUNTERCLOCKWISE if the majority of the edges have positive or negative\n     *         cross products respectively, and UNKNOWN if all edge cross products are 0 or if the\n     *         roundabout's geometry is malformed\n     */\n    private static RoundaboutDirection findRoundaboutDirection(final Route roundaboutEdges)\n    {\n        int clockwiseCount = 0;\n        int counterClockwiseCount = 0;\n\n        for (int index = 0; index < roundaboutEdges.size(); index++)\n        {\n            // Get the Edges to use in the cross product\n            final Edge edge1 = roundaboutEdges.get(index);\n            // We mod the roundabout edges here so that we can get the last pair of edges in the\n            // Roundabout correctly\n            final Edge edge2 = roundaboutEdges.get((index + 1) % roundaboutEdges.size());\n            // Get the cross product and then the direction of the roundabout\n            final double crossProduct = getCrossProduct(edge1, edge2);\n            final RoundaboutDirection direction;\n            if (crossProduct < 0)\n            {\n                direction = RoundaboutDirection.COUNTERCLOCKWISE;\n            }\n            else if (crossProduct > 0)\n            {\n                direction = RoundaboutDirection.CLOCKWISE;\n            }\n            else\n            {\n                direction = RoundaboutDirection.UNKNOWN;\n            }\n\n            // If the direction is UNKNOWN then we continue to the next iteration because we do not\n            // Have any new information about the roundabout's direction\n            if (direction.equals(RoundaboutDirection.UNKNOWN))\n            {\n                continue;\n            }\n            if (direction.equals(RoundaboutDirection.CLOCKWISE))\n            {\n                clockwiseCount += 1;\n            }\n            if (direction.equals(RoundaboutDirection.COUNTERCLOCKWISE))\n            {\n                counterClockwiseCount += 1;\n            }\n        }\n        // Return the Enum for whatever has the highest count\n        if (clockwiseCount > counterClockwiseCount)\n        {\n            return RoundaboutDirection.CLOCKWISE;\n        }\n        else if (clockwiseCount < counterClockwiseCount)\n        {\n            return RoundaboutDirection.COUNTERCLOCKWISE;\n        }\n        else\n        {\n            return RoundaboutDirection.UNKNOWN;\n        }\n    }\n\n    /**\n     * This method returns the cross product between two adjacent edges.\n     *\n     * @see \"https://en.wikipedia.org/wiki/Cross_product\"\n     * @param edge1\n     *            An Edge entity in the roundabout\n     * @param edge2\n     *            An Edge entity in the roundabout adjacent to edge1\n     * @return A double corresponding to the cross product between two edges\n     */\n    private static double getCrossProduct(final Edge edge1, final Edge edge2)\n    {\n        // Get the nodes' latitudes and longitudes to use in deriving the vectors\n        final double node1Y = edge1.start().getLocation().getLatitude().asDegrees();\n        final double node1X = edge1.start().getLocation().getLongitude().asDegrees();\n        final double node2Y = edge1.end().getLocation().getLatitude().asDegrees();\n        final double node2X = edge1.end().getLocation().getLongitude().asDegrees();\n        final double node3Y = edge2.end().getLocation().getLatitude().asDegrees();\n        final double node3X = edge2.end().getLocation().getLongitude().asDegrees();\n\n        // Get the vectors from node 2 to 1, and node 2 to 3\n        final double vector1X = node2X - node1X;\n        final double vector1Y = node2Y - node1Y;\n        final double vector2X = node2X - node3X;\n        final double vector2Y = node2Y - node3Y;\n\n        // The cross product tells us the direction of the orthogonal vector, which is\n        // Directly related to the direction of rotation/traffic\n        return (vector1X * vector2Y) - (vector1Y * vector2X);\n    }\n\n    public ComplexRoundabout(final Edge source)\n    {\n        this(source, LEFT_DRIVING_COUNTRIES_DEFAULT);\n    }\n\n    public ComplexRoundabout(final Edge source, final List leftDrivingCountries)\n    {\n        super(source);\n        try\n        {\n            if (!(\n            // Make sure that the source has an iso_country_code\n            source.getTag(ISOCountryTag.KEY).isPresent()\n                    // Make sure that the source is an instances of a roundabout\n                    && JunctionTag.isRoundabout(source)\n                    // Make sure that we are looking at a main edge\n                    && (source.isMainEdge())))\n            {\n                final ComplexEntityError sourceError = new ComplexEntityError(this,\n                        String.format(\"Invalid source Edge (%s) for a roundabout.\",\n                                source.getIdentifier()),\n                        new CoreException(EXCEPTION_MESSAGE));\n                this.invalidationReasons.add(sourceError);\n                throw sourceError.getException();\n            }\n            final String isoCountryCode = source.tag(ISOCountryTag.KEY).toUpperCase();\n\n            // Get all edges in the roundabout\n            this.roundaboutEdgeSet = new SimpleEdgeWalker(source, this.isRoundaboutEdge())\n                    .collectEdges();\n\n            // Try to build a Route from the edges\n            try\n            {\n                this.roundaboutRoute = Route.fromNonArrangedEdgeSet(this.roundaboutEdgeSet, false);\n            }\n            // If a Route cannot be formed, invalidate the roundabout.\n            catch (final CoreException badRoundabout)\n            {\n                final ComplexEntityError routeError = new ComplexEntityError(this,\n                        INCOMPLETE_ROUTE_INVALIDATION, new CoreException(EXCEPTION_MESSAGE));\n                this.invalidationReasons.add(routeError);\n                throw routeError.getException();\n            }\n\n            // Invalidate the roundabout if any of the edges are not car navigable or main Edges,\n            // or the route does not form a closed loop.\n            if (this.roundaboutEdgeSet.stream()\n                    .anyMatch(roundaboutEdge -> !HighwayTag.isCarNavigableHighway(roundaboutEdge)\n                            || !roundaboutEdge.isMainEdge())\n                    || !this.roundaboutRoute.start().inEdges().contains(this.roundaboutRoute.end()))\n            {\n                this.invalidationReasons.add(new ComplexEntityError(this,\n                        INCOMPLETE_ROUTE_INVALIDATION, new CoreException(EXCEPTION_MESSAGE)));\n            }\n\n            // Get the direction of the roundabout\n            final RoundaboutDirection direction = findRoundaboutDirection(this.roundaboutRoute);\n\n            // Determine if the roundabout is in a left or right driving country\n            final boolean isLeftDriving = leftDrivingCountries.contains(isoCountryCode);\n\n            // If the roundabout traffic is clockwise in a right-driving country, or\n            // If the roundabout traffic is counterclockwise in a left-driving country\n            if (direction.equals(RoundaboutDirection.CLOCKWISE) && !isLeftDriving\n                    || direction.equals(RoundaboutDirection.COUNTERCLOCKWISE) && isLeftDriving)\n            {\n                this.invalidationReasons.add(new ComplexEntityError(this, WRONG_WAY_INVALIDATION,\n                        new CoreException(EXCEPTION_MESSAGE)));\n            }\n\n            // If there is an invalidation logged, invalidate the roundabout\n            if (!this.invalidationReasons.isEmpty())\n            {\n                throw this.invalidationReasons.get(0).getException();\n            }\n        }\n        catch (final Throwable exception)\n        {\n            setInvalidReason(exception.getMessage(), exception);\n        }\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.roundaboutRoute.bounds();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof ComplexRoundabout)\n        {\n            return this.roundaboutEdgeSet\n                    .equals(((ComplexRoundabout) other).getRoundaboutEdgeSet());\n        }\n        return false;\n    }\n\n    @Override\n    public List<ComplexEntityError> getAllInvalidations()\n    {\n        return this.invalidationReasons;\n    }\n\n    public Set<Edge> getRoundaboutEdgeSet()\n    {\n        return this.roundaboutEdgeSet;\n    }\n\n    public Route getRoundaboutRoute()\n    {\n        return this.roundaboutRoute;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return super.hashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.format(\"Roundabout of Edges: %s\", this.roundaboutEdgeSet);\n    }\n\n    /**\n     * Function for {@link SimpleEdgeWalker} that gathers connected edges that are part of a\n     * roundabout.\n     *\n     * @return {@link Function} for {@link SimpleEdgeWalker}\n     */\n    private Function<Edge, Stream<Edge>> isRoundaboutEdge()\n    {\n        return edge -> edge.connectedEdges().stream().filter(JunctionTag::isRoundabout);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/roundabout/ComplexRoundaboutFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.roundabout;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * {@link Finder} for {@link ComplexRoundabout}s.\n *\n * @author bbreithaupt\n */\npublic class ComplexRoundaboutFinder implements Finder<ComplexRoundabout>\n{\n    private final Set<Long> checkedIds = new HashSet<>();\n\n    @Override\n    public Iterable<ComplexRoundabout> find(final Atlas atlas)\n    {\n        final List<ComplexRoundabout> complexRoundabouts = new ArrayList<>();\n        Iterables.stream(atlas.edges()).forEach(edge ->\n        {\n            if (!this.checkedIds.contains(edge.getIdentifier()))\n            {\n                final ComplexRoundabout complexRoundabout = new ComplexRoundabout(edge);\n                if (complexRoundabout.getRoundaboutEdgeSet() != null)\n                {\n                    this.checkedIds.addAll(complexRoundabout.getRoundaboutEdgeSet().stream()\n                            .map(Edge::getIdentifier).collect(Collectors.toSet()));\n                }\n                if (complexRoundabout.isValid())\n                {\n                    complexRoundabouts.add(complexRoundabout);\n                }\n            }\n        });\n        return complexRoundabouts;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/ComplexWaterEntity.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.slf4j.Logger;\n\n/**\n * @author Sid\n */\npublic abstract class ComplexWaterEntity extends ComplexEntity\n{\n    private static final long serialVersionUID = 7835788819725148174L;\n\n    private final WaterType waterType;\n\n    public ComplexWaterEntity(final AtlasEntity source, final WaterType waterType)\n    {\n        super(source);\n        this.waterType = waterType;\n        try\n        {\n            populateGeometry();\n        }\n        catch (final Exception e)\n        {\n            getLogger().warn(\"Unable to create complex water entity from {}\", source, e);\n            setInvalidReason(\"Unable to create complex water entity\", e);\n        }\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof ComplexWaterEntity)\n        {\n            final ComplexWaterEntity that = (ComplexWaterEntity) other;\n            return new EqualsBuilder().append(this.waterType, that.waterType)\n                    .append(this.getSource(), that.getSource()).build();\n        }\n        return false;\n    }\n\n    public WaterType getWaterType()\n    {\n        return this.waterType;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.getSource()).append(this.waterType).build();\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getName() + \" \" + this.getWaterType() + \" \" + getSource();\n    }\n\n    protected abstract Logger getLogger();\n\n    /**\n     * This function populates the geometry\n     */\n    protected abstract void populateGeometry();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/ComplexWaterbody.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport java.util.Optional;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A waterbody usually refers to a area of (standing) water and typically has polygonal geometry.\n * This contrasts with waterways (usually flowing) like streams which typically have linear geometry\n *\n * @author Sid\n */\npublic class ComplexWaterbody extends ComplexWaterEntity\n{\n    private static final long serialVersionUID = -666543090371777011L;\n\n    private static final RelationOrAreaToMultiPolygonConverter RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER = new RelationOrAreaToMultiPolygonConverter(\n            true);\n    private static final JtsMultiPolygonToMultiPolygonConverter MULTIPOLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n\n    private static final Logger logger = LoggerFactory.getLogger(ComplexWaterbody.class);\n\n    private MultiPolygon geometry;\n\n    public ComplexWaterbody(final AtlasEntity source, final WaterType type)\n    {\n        super(source, type);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof ComplexWaterbody)\n        {\n            final ComplexWaterbody that = (ComplexWaterbody) other;\n            return new EqualsBuilder().append(this.getWaterType(), that.getWaterType())\n                    .append(this.getSource(), that.getSource())\n                    .append(this.getGeometry().toWkt(), that.getGeometry().toWkt()).build();\n        }\n        return false;\n    }\n\n    public MultiPolygon getGeometry()\n    {\n        return this.geometry;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.getSource()).append(this.getWaterType())\n                .append(this.getGeometry().toWkb()).build();\n    }\n\n    @Override\n    protected Logger getLogger()\n    {\n        return logger;\n    }\n\n    @Override\n    protected void populateGeometry()\n    {\n        final AtlasEntity source = getSource();\n        if (source instanceof Area)\n        {\n            this.geometry = RELATION_OR_AREA_TO_MULTI_POLYGON_CONVERTER.convert(source);\n            return;\n        }\n        else if (source instanceof Relation)\n        {\n            final Relation relation = (Relation) source;\n            final Optional<org.locationtech.jts.geom.MultiPolygon> geom = relation.asMultiPolygon();\n            final String type = relation.tag(RelationTypeTag.KEY);\n            if (RelationTypeTag.MULTIPOLYGON_TYPE.equals(type) && geom.isPresent())\n            {\n                this.geometry = MULTIPOLYGON_CONVERTER.convert(geom.get());\n                return;\n            }\n        }\n        throw new CoreException(\"Geometry is not set for {}\", source);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/ComplexWaterway.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A waterway (usually flowing ex : rivers, streams) typically has linear geometry. This contrasts\n * with waterbody which usually refers to an area of (standing) water and typically has polygonal\n * geometry\n *\n * @author Sid\n */\npublic class ComplexWaterway extends ComplexWaterEntity\n{\n    private static final long serialVersionUID = -5567739097914423531L;\n\n    private static final Logger logger = LoggerFactory.getLogger(ComplexWaterway.class);\n\n    private PolyLine geometry;\n\n    public ComplexWaterway(final AtlasEntity source, final WaterType type)\n    {\n        super(source, type);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof ComplexWaterbody)\n        {\n            final ComplexWaterbody that = (ComplexWaterbody) other;\n            return new EqualsBuilder().append(this.getWaterType(), that.getWaterType())\n                    .append(this.getSource(), that.getSource())\n                    .append(this.getGeometry().toWkt(), that.getGeometry().toWkt()).build();\n        }\n        return false;\n    }\n\n    public PolyLine getGeometry()\n    {\n        return this.geometry;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.getSource()).append(this.getWaterType())\n                .append(this.getGeometry().toWkb()).build();\n    }\n\n    @Override\n    protected Logger getLogger()\n    {\n        return logger;\n    }\n\n    @Override\n    protected void populateGeometry()\n    {\n        final AtlasEntity source = getSource();\n\n        /*\n         * We currently don't process waterway relations. So if it is a way and it is part of\n         * relation where it is a side stream, main stream or tributary, we process them.\n         */\n        if (source instanceof Line)\n        {\n            final Line line = (Line) source;\n            this.geometry = line.asPolyLine();\n            return;\n        }\n        throw new CoreException(\"Geometry is not set for {}\", source);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/WaterType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Type of supported water bodies\n *\n * @author sbhalekar\n */\npublic enum WaterType\n{\n    LAKE,\n    RIVER,\n    CANAL,\n    CREEK,\n    DITCH,\n    LAGOON,\n    POND,\n    POOL,\n    RESERVOIR,\n    SEA,\n    WETLAND,\n    HARBOUR,\n    UNKNOWN;\n\n    /**\n     * Convert a string to {@link WaterType}\n     *\n     * @param type\n     *            String representation of WaterType\n     * @return {@link WaterType}\n     */\n    public static WaterType from(final String type)\n    {\n        for (final WaterType waterBodyType : WaterType.values())\n        {\n            if (waterBodyType.name().equalsIgnoreCase(type))\n            {\n                return waterBodyType;\n            }\n        }\n\n        throw new CoreException(\"Unknown water type {}\", type);\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.name().toLowerCase();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/ComplexWaterEntityFinder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water.finder;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.ComplexWaterEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.WaterType;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Finder to find all {@link ComplexWaterEntity} from and {@link Atlas} by using multiple\n * configuration readers for each {@link WaterType}\n *\n * @author sbhalekar\n */\npublic class ComplexWaterEntityFinder implements Finder<ComplexWaterEntity>\n{\n    private static final Predicate<Relation> RELATION_FILTER = relation -> Validators.isOfType(\n            relation, RelationTypeTag.class, RelationTypeTag.MULTIPOLYGON, RelationTypeTag.BOUNDARY,\n            RelationTypeTag.WATERWAY);\n\n    private static final Logger logger = LoggerFactory.getLogger(ComplexWaterEntityFinder.class);\n\n    private final List<WaterConfigurationReader> waterConfigurationReaders;\n\n    public ComplexWaterEntityFinder()\n    {\n        this.waterConfigurationReaders = new ArrayList<>();\n\n        // read in the default configuration files with default mappings for each water body type\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"lake.json\", WaterType.LAKE));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"river.json\", WaterType.RIVER));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"lagoon.json\", WaterType.LAGOON));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"wetland.json\", WaterType.WETLAND));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"reservoir.json\", WaterType.RESERVOIR));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"pool.json\", WaterType.POOL));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"pond.json\", WaterType.POND));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"harbour.json\", WaterType.HARBOUR));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"canal.json\", WaterType.CANAL));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"creek.json\", WaterType.CREEK));\n        this.waterConfigurationReaders\n                .add(new DefaultWaterConfigurationReader(\"ditch.json\", WaterType.DITCH));\n    }\n\n    public ComplexWaterEntityFinder(final WaterConfigurationReader... waterConfigurationReaders)\n    {\n        // use the passed in configuration readers for the filters\n        this.waterConfigurationReaders = Arrays.asList(waterConfigurationReaders);\n    }\n\n    @Override\n    public Iterable<ComplexWaterEntity> find(final Atlas atlas)\n    {\n        final Stream<Line> lineStream = StreamSupport.stream(atlas.lines().spliterator(), false);\n        final Stream<Area> areaStream = StreamSupport.stream(atlas.areas().spliterator(), false);\n        final Stream<Relation> relationStream = StreamSupport\n                .stream(atlas.relations(RELATION_FILTER).spliterator(), false);\n\n        return Stream.concat(Stream.concat(lineStream, areaStream), relationStream)\n                .map(this::processEntity).filter(Optional::isPresent).map(Optional::get)\n                .collect(Collectors.toList());\n    }\n\n    public List<WaterConfigurationReader> getWaterConfigurationReaders()\n    {\n        return this.waterConfigurationReaders;\n    }\n\n    /**\n     * Convert {@link AtlasEntity} to an Optional {@link ComplexWaterEntity}. Sometimes an\n     * {@link AtlasEntity} might not pass any of the filters in the configuration files. In that\n     * case return empty optional\n     *\n     * @param atlasEntity\n     *            {@link AtlasEntity} which needs to be converted\n     * @return Optional {@link ComplexWaterEntity}\n     */\n    public Optional<ComplexWaterEntity> processEntity(final AtlasEntity atlasEntity)\n    {\n        // pass the atlas entity through all the filters in the configuration files and try to\n        // create a complex water entity for each passed filter\n        final List<ComplexWaterEntity> complexWaterEntities = this.waterConfigurationReaders\n                .stream()\n                .map(waterConfigurationReader -> waterConfigurationReader.convert(atlasEntity))\n                .filter(Optional::isPresent).map(Optional::get)\n                .filter(object -> object instanceof ComplexWaterEntity)\n                .map(object -> (ComplexWaterEntity) object).collect(Collectors.toList());\n\n        if (complexWaterEntities.isEmpty())\n        {\n            logger.trace(\"AtlasEntity: {} did not match any water type filters\", atlasEntity);\n            return Optional.empty();\n        }\n        else if (complexWaterEntities.size() > 1)\n        {\n            // Each AtlasEntity should pass only one WaterType configuration. If is passes more than\n            // one means the taggable filters specified in the configurations has an overlap. Due to\n            // ambiguous taggable filters specified this method would return empty optional\n            final String matchedWaterBodies = complexWaterEntities.stream()\n                    .map(ComplexWaterEntity::getWaterType).map(WaterType::toString)\n                    .collect(Collectors.joining(\",\"));\n\n            logger.error(\"Skipping AtlasEnity : {} as it got mapped to multiple types: {}\",\n                    atlasEntity, matchedWaterBodies);\n            return Optional.empty();\n        }\n\n        // return the ComplexWaterEntity matched with the only water type configuration\n        return Optional.of(complexWaterEntities.get(0));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/DefaultWaterConfigurationReader.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water.finder;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.ComplexWaterEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.ComplexWaterbody;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.ComplexWaterway;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.WaterType;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.configuration.ConfiguredFilter;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\n\n/**\n * Configuration reader which would read a configuration file and only convert the default\n * configuration filter into a {@link ConfiguredFilter}\n *\n * @author sbhalekar\n */\npublic class DefaultWaterConfigurationReader extends WaterConfigurationReader<ConfiguredFilter>\n{\n    public DefaultWaterConfigurationReader(final Resource resource, final WaterType waterType)\n    {\n        super(resource, waterType);\n    }\n\n    public DefaultWaterConfigurationReader(final String resourceFileName, final WaterType waterType)\n    {\n        this(new InputStreamResource(\n                () -> DefaultWaterConfigurationReader.class.getResourceAsStream(resourceFileName)),\n                waterType);\n    }\n\n    @Override\n    public ConfiguredFilter readConfiguration(final Resource configurationResource)\n    {\n        return ConfiguredFilter.getDefaultFilter(new StandardConfiguration(configurationResource));\n    }\n\n    @Override\n    protected Optional<ComplexWaterEntity> createComplexEntity(final AtlasEntity atlasEntity)\n    {\n        final WaterType waterBodyType = this.getWaterBodyType();\n        Optional<ComplexWaterEntity> complexWaterEntity = Optional.empty();\n\n        if (this.getConfigurationMapper().test(atlasEntity))\n        {\n            if (atlasEntity instanceof Relation || atlasEntity instanceof Area)\n            {\n                complexWaterEntity = Optional.of(new ComplexWaterbody(atlasEntity, waterBodyType));\n            }\n            else if (atlasEntity instanceof Line)\n            {\n                complexWaterEntity = Optional.of(new ComplexWaterway(atlasEntity, waterBodyType));\n            }\n        }\n\n        return complexWaterEntity;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/WaterConfigurationReader.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water.finder;\n\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.WaterIslandConfigurationReader;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.WaterType;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * Class which ties a water configuration reader to {@link WaterType}\n * \n * @param <T>\n *            Type of objects which will map {@link AtlasEntity} to any {@link ComplexEntity}\n * @author sbhalekar\n */\npublic abstract class WaterConfigurationReader<T> extends WaterIslandConfigurationReader<T>\n{\n    private final WaterType waterType;\n\n    public WaterConfigurationReader(final Resource configurationResource, final WaterType waterType)\n    {\n        super(configurationResource);\n        this.waterType = waterType;\n    }\n\n    protected WaterType getWaterBodyType()\n    {\n        return this.waterType;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightArea.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport java.util.Arrays;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.LongStream;\n\nimport javax.annotation.Nullable;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * A lightweight area.\n *\n * @author Taylor Smock\n */\npublic class LightArea extends Area implements LightLineItem<LightArea>\n{\n    private static final byte HASH_BYTE = 31;\n    private final long identifier;\n    private final long[] relationIdentifiers;\n    private final Location[] locations;\n\n    /**\n     * Create a new area from another area\n     *\n     * @param from\n     *            The area to clone\n     * @return A new LightArea\n     */\n    static LightArea from(final Area from)\n    {\n        return new LightArea(from);\n    }\n\n    /**\n     * Create a new LightArea with just an identifier\n     *\n     * @param identifier\n     *            The identifier\n     */\n    LightArea(final long identifier)\n    {\n        this(identifier, EMPTY_LOCATION_ARRAY);\n    }\n\n    /**\n     * Create a new LightArea with just an identifier and points\n     *\n     * @param identifier\n     *            The identifier\n     * @param points\n     *            The points of the area\n     */\n    LightArea(final long identifier, final Location... points)\n    {\n        super(new EmptyAtlas());\n        this.identifier = identifier;\n        this.relationIdentifiers = EMPTY_LONG_ARRAY;\n        this.locations = points.length > 0 ? points.clone() : points;\n    }\n\n    /**\n     * Create a new LightArea from another Area\n     *\n     * @param from\n     *            The area to copy from\n     */\n    LightArea(final Area from)\n    {\n        super(new EmptyAtlas());\n        this.identifier = from.getIdentifier();\n        this.relationIdentifiers = from.relations().stream().mapToLong(Relation::getIdentifier)\n                .toArray();\n        this.locations = from.asPolygon().toArray(EMPTY_LOCATION_ARRAY);\n    }\n\n    @Override\n    @Nullable\n    public PolyLine asPolyLine()\n    {\n        if (this.locations.length > 0)\n        {\n            return new PolyLine(this.locations);\n        }\n        return null;\n    }\n\n    @Override\n    @Nullable\n    public Polygon asPolygon()\n    {\n        if (this.locations.length > 0)\n        {\n            return new Polygon(this.locations);\n        }\n        return null;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other == null || this.getClass() != other.getClass())\n        {\n            return false;\n        }\n        if (!super.equals(other))\n        {\n            return false;\n        }\n        final var lightArea = (LightArea) other;\n        return this.identifier == lightArea.identifier\n                && Arrays.equals(this.relationIdentifiers, lightArea.relationIdentifiers)\n                && Arrays.equals(this.locations, lightArea.locations);\n    }\n\n    @Nullable\n    @Override\n    public Iterable<Location> getGeometry()\n    {\n        if (this.locations.length > 0)\n        {\n            return Iterables.asList(this.locations);\n        }\n        return null;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public long[] getRelationIdentifiers()\n    {\n        return this.relationIdentifiers.clone();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        int result = super.hashCode();\n        result = HASH_BYTE * result + Long.hashCode(this.identifier);\n        result = HASH_BYTE * result + Arrays.hashCode(this.relationIdentifiers);\n        result = HASH_BYTE * result + Arrays.hashCode(this.locations);\n        return result;\n    }\n\n    /**\n     * Please note that the relations returned from this method should *only* be used for\n     * identifiers.\n     *\n     * @see Area#relations()\n     * @return A set of identifier only relations\n     */\n    @Override\n    public Set<Relation> relations()\n    {\n        return LongStream.of(this.relationIdentifiers).mapToObj(LightRelation::new)\n                .collect(Collectors.toSet());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport java.util.Arrays;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.LongStream;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * A lightweight edge with basic information\n *\n * @author Taylor Smock\n */\npublic class LightEdge extends Edge implements LightLineItem<LightEdge>\n{\n    private static final byte HASH_BYTE = 31;\n    private final long identifier;\n    private final long[] relationIdentifiers;\n    private final long startNodeIdentifier;\n    private final long endNodeIdentifier;\n    private final Location[] pointLocations;\n\n    /**\n     * Create a new LightEdge from another Edge\n     *\n     * @param from\n     *            The edge to copy from\n     * @return A new LightEdge\n     */\n    static LightEdge from(final Edge from)\n    {\n        return new LightEdge(from);\n    }\n\n    /**\n     * Create a LightEdge with just an identifier\n     *\n     * @param identifier\n     *            The identifier\n     */\n    LightEdge(final long identifier)\n    {\n        this(identifier, EMPTY_LOCATION_ARRAY);\n    }\n\n    /**\n     * Create a new LightEdge with an identifier and locations\n     *\n     * @param identifier\n     *            The identifier\n     * @param points\n     *            The location points\n     */\n    LightEdge(final long identifier, final Location... points)\n    {\n        super(new EmptyAtlas());\n        this.identifier = identifier;\n        this.relationIdentifiers = EMPTY_LONG_ARRAY;\n        this.startNodeIdentifier = 0;\n        this.endNodeIdentifier = 0;\n        this.pointLocations = points.length > 0 ? points.clone() : points;\n    }\n\n    /**\n     * Create a new LightEdge from another Edge\n     *\n     * @param from\n     *            The edge to copy from\n     */\n    LightEdge(final Edge from)\n    {\n        super(new EmptyAtlas());\n        this.identifier = from.getIdentifier();\n        this.relationIdentifiers = from.relations().stream().mapToLong(Relation::getIdentifier)\n                .toArray();\n        this.startNodeIdentifier = from.start().getIdentifier();\n        this.endNodeIdentifier = from.end().getIdentifier();\n        this.pointLocations = from.asPolyLine().toArray(EMPTY_LOCATION_ARRAY);\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        if (this.pointLocations.length > 0)\n        {\n            return new PolyLine(this.pointLocations);\n        }\n        return null;\n    }\n\n    @Override\n    public Node end()\n    {\n        if (this.pointLocations.length > 0)\n        {\n            return new LightNode(this.endNodeIdentifier,\n                    this.pointLocations[this.pointLocations.length - 1]);\n        }\n        return null;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other == null || this.getClass() != other.getClass())\n        {\n            return false;\n        }\n        if (!super.equals(other))\n        {\n            return false;\n        }\n        final var lightEdge = (LightEdge) other;\n        return this.identifier == lightEdge.identifier\n                && this.startNodeIdentifier == lightEdge.startNodeIdentifier\n                && this.endNodeIdentifier == lightEdge.endNodeIdentifier\n                && Arrays.equals(this.relationIdentifiers, lightEdge.relationIdentifiers)\n                && Arrays.equals(this.pointLocations, lightEdge.pointLocations);\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public long[] getRelationIdentifiers()\n    {\n        return this.relationIdentifiers.clone();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        int result = super.hashCode();\n        result = HASH_BYTE * result + Long.hashCode(this.identifier);\n        result = HASH_BYTE * result + Long.hashCode(this.startNodeIdentifier);\n        result = HASH_BYTE * result + Long.hashCode(this.endNodeIdentifier);\n        result = HASH_BYTE * result + Arrays.hashCode(this.relationIdentifiers);\n        result = HASH_BYTE * result + Arrays.hashCode(this.pointLocations);\n        return result;\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return LongStream.of(this.getRelationIdentifiers()).mapToObj(LightRelation::new)\n                .collect(Collectors.toSet());\n    }\n\n    @Override\n    public Node start()\n    {\n        if (this.pointLocations.length > 0)\n        {\n            return new LightNode(this.startNodeIdentifier, this.pointLocations[0]);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightEntity.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport javax.annotation.Nullable;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * A lightweight Atlas entity. In this case, light weight refers to size in memory. These are\n * primarily useful for raw identifier uses. First generation lightweight entities may also have\n * geometry.\n *\n * @param <C>\n *            The primitive type\n * @author Taylor Smock\n */\npublic interface LightEntity<C extends LightEntity<C>>\n{\n    /** A common empty long array to avoid additional empty arrays (memory) */\n    long[] EMPTY_LONG_ARRAY = {};\n    /** A common empty location array to avoid additional empty arrays (memory) */\n    Location[] EMPTY_LOCATION_ARRAY = {};\n\n    /**\n     * Create a {@link LightEntity} from a given {@link AtlasEntity} reference. The\n     * {@link LightEntity}'s fields will match the fields of the reference. The returned\n     * {@link LightEntity} may or may not be full, i.e. some of its associated fields will be\n     * {@code null}. Currently no tags are saved in light entities. The <i>only</i> items guaranteed\n     * to exist is the id of the entity. First generation entities also have geometry, but any\n     * second generation entities do not. For example, the {@link AtlasEntity#relations()} method\n     * only returns {@link LightRelation}s with an identifier and no geometry. For this reason,\n     * {@link LightEntity}s should only be used with code that expects {@link LightEntity}s.\n     *\n     * @param reference\n     *            the reference to copy\n     * @return the full entity\n     */\n    static AtlasEntity from(final AtlasEntity reference)\n    {\n        final ItemType type = reference.getType();\n        switch (type)\n        {\n            case NODE:\n                return LightNode.from((Node) reference);\n            case EDGE:\n                return LightEdge.from((Edge) reference);\n            case AREA:\n                return LightArea.from((Area) reference);\n            case LINE:\n                return LightLine.from((Line) reference);\n            case POINT:\n                return LightPoint.from((Point) reference);\n            case RELATION:\n                return LightRelation.from((Relation) reference);\n            default:\n                throw new CoreException(\"Unknown ItemType {}\", type);\n        }\n    }\n\n    /**\n     * Get the geometry of the entity\n     *\n     * @return The geometry\n     */\n    @Nullable\n    Iterable<Location> getGeometry();\n\n    /**\n     * Get relation identifiers\n     *\n     * @return The relation identifiers\n     */\n    long[] getRelationIdentifiers();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightLine.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport java.util.Arrays;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.LongStream;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * A lightweight line\n *\n * @author Taylor Smock\n */\npublic class LightLine extends Line implements LightLineItem<LightLine>\n{\n    private static final byte HASH_BYTE = 31;\n    private final long identifier;\n    private final long[] relationIdentifiers;\n    private final Location[] locations;\n\n    /**\n     * Create a new LightLine from another line\n     *\n     * @param from\n     *            The line to copy from\n     * @return A new LightLine\n     */\n    static LightLine from(final Line from)\n    {\n        return new LightLine(from);\n    }\n\n    /**\n     * Create a new LightLine with just an identifier\n     *\n     * @param identifier\n     *            The identifier\n     */\n    LightLine(final long identifier)\n    {\n        this(identifier, EMPTY_LOCATION_ARRAY);\n    }\n\n    /**\n     * Create a new LightLine with the specified points\n     *\n     * @param identifier\n     *            The identifier for the Line\n     * @param points\n     *            The points for the line\n     */\n    LightLine(final long identifier, final Location... points)\n    {\n        super(new EmptyAtlas());\n        this.identifier = identifier;\n        this.relationIdentifiers = EMPTY_LONG_ARRAY;\n        this.locations = points.length > 0 ? points.clone() : points;\n    }\n\n    /**\n     * Create a new LightLine from another line\n     *\n     * @param from\n     *            The line to copy from\n     */\n    LightLine(final Line from)\n    {\n        super(new EmptyAtlas());\n        this.identifier = from.getIdentifier();\n        this.relationIdentifiers = from.relations().stream().mapToLong(Relation::getIdentifier)\n                .toArray();\n        this.locations = Iterables.stream(from.asPolyLine()).collectToList()\n                .toArray(Location[]::new);\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        if (this.locations.length > 0)\n        {\n            return new PolyLine(this.locations);\n        }\n        return null;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other == null || this.getClass() != other.getClass())\n        {\n            return false;\n        }\n        final var lightLine = (LightLine) other;\n        return this.identifier == lightLine.identifier\n                && Arrays.equals(this.relationIdentifiers, lightLine.relationIdentifiers)\n                && Arrays.equals(this.locations, lightLine.locations);\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public long[] getRelationIdentifiers()\n    {\n        return this.relationIdentifiers.clone();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        int result = super.hashCode();\n        result = HASH_BYTE * result + Long.hashCode(this.identifier);\n        result = HASH_BYTE * result + Arrays.hashCode(this.relationIdentifiers);\n        result = HASH_BYTE * result + Arrays.hashCode(this.locations);\n        return result;\n    }\n\n    /**\n     * Please note that the relations returned from this method should *only* be used for\n     * identifiers.\n     *\n     * @see Line#relations()\n     * @return A set of identifier only relations\n     */\n    @Override\n    public Set<Relation> relations()\n    {\n        return LongStream.of(this.getRelationIdentifiers()).mapToObj(LightRelation::new)\n                .collect(Collectors.toSet());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightLineItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport javax.annotation.Nullable;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\n\n/**\n * A common interface for line items\n *\n * @param <E>\n *            The line item type\n * @author Taylor Smock\n */\npublic interface LightLineItem<E extends LightLineItem<E>> extends LightEntity<E>\n{\n    PolyLine asPolyLine();\n\n    @Nullable\n    default Iterable<Location> getGeometry()\n    {\n        return this.asPolyLine();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightLocationItem.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport java.util.Collections;\n\nimport org.openstreetmap.atlas.geography.Location;\n\n/**\n * A light location item\n *\n * @param <E>\n *            The type of location item\n * @author Taylor Smock\n */\npublic interface LightLocationItem<E extends LightLocationItem<E>>\n        extends LightEntity<LightLocationItem<E>>\n{\n    @Override\n    default Iterable<Location> getGeometry()\n    {\n        return Collections.singleton(this.getLocation());\n    }\n\n    Location getLocation();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightNode.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport java.util.Arrays;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\nimport java.util.stream.LongStream;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * A lightweight node with only basic information\n *\n * @author Taylor Smock\n */\npublic class LightNode extends Node implements LightLocationItem<LightNode>\n{\n    private static final byte HASH_BYTE = 31;\n    private final long identifier;\n    private final Location location;\n    private final long[] inEdgeIdentifiers;\n    private final long[] outEdgeIdentifiers;\n    private final long[] relationIdentifiers;\n\n    /**\n     * Create a new LightNode from a Node\n     *\n     * @param node\n     *            The node to copy from\n     * @return A LightNode\n     */\n    static LightNode from(final Node node)\n    {\n        return new LightNode(node);\n    }\n\n    /**\n     * Create a LightNode with just an identifier\n     *\n     * @param identifier\n     *            The identifier\n     */\n    LightNode(final long identifier)\n    {\n        this(identifier, null);\n    }\n\n    /**\n     * Create a LightNode from an identifier and a location\n     *\n     * @param identifier\n     *            The identifier\n     * @param location\n     *            The location\n     */\n    LightNode(final long identifier, final Location location)\n    {\n        super(new EmptyAtlas());\n        this.identifier = identifier;\n        this.location = location;\n        this.inEdgeIdentifiers = EMPTY_LONG_ARRAY;\n        this.outEdgeIdentifiers = EMPTY_LONG_ARRAY;\n        this.relationIdentifiers = EMPTY_LONG_ARRAY;\n    }\n\n    /**\n     * Create a new LightNode from another Node\n     *\n     * @param from\n     *            The ndoe to copy from\n     */\n    LightNode(final Node from)\n    {\n        super(new EmptyAtlas());\n        this.identifier = from.getIdentifier();\n        this.location = from.getLocation();\n        this.inEdgeIdentifiers = from.inEdges().stream().map(Edge::getIdentifier).distinct()\n                .mapToLong(Long::longValue).toArray();\n        this.outEdgeIdentifiers = from.outEdges().stream().map(Edge::getIdentifier).distinct()\n                .mapToLong(Long::longValue).toArray();\n        this.relationIdentifiers = from.relations().stream().map(Relation::getIdentifier).distinct()\n                .mapToLong(Long::longValue).toArray();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other == null || this.getClass() != other.getClass())\n        {\n            return false;\n        }\n        final var lightNode = (LightNode) other;\n        if (this.location != null && lightNode.location != null)\n        {\n            return this.identifier == lightNode.identifier\n                    && this.location.equals(lightNode.location)\n                    && Arrays.equals(this.inEdgeIdentifiers, lightNode.inEdgeIdentifiers)\n                    && Arrays.equals(this.outEdgeIdentifiers, lightNode.outEdgeIdentifiers)\n                    && Arrays.equals(this.relationIdentifiers, lightNode.relationIdentifiers);\n        }\n        else if (this.location == null && lightNode.location == null)\n        {\n            return this.identifier == lightNode.identifier\n                    && Arrays.equals(this.inEdgeIdentifiers, lightNode.inEdgeIdentifiers)\n                    && Arrays.equals(this.outEdgeIdentifiers, lightNode.outEdgeIdentifiers)\n                    && Arrays.equals(this.relationIdentifiers, lightNode.relationIdentifiers);\n        }\n        return false;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    /**\n     * Get the identifiers for in edges\n     *\n     * @return The identifiers for in edges\n     */\n    public long[] getInEdgeIdentifiers()\n    {\n        return this.inEdgeIdentifiers.clone();\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return this.location;\n    }\n\n    /**\n     * Get the identifiers for out edges\n     *\n     * @return The identifiers for out edges\n     */\n    public long[] getOutEdgeIdentifiers()\n    {\n        return this.outEdgeIdentifiers.clone();\n    }\n\n    @Override\n    public long[] getRelationIdentifiers()\n    {\n        return this.relationIdentifiers.clone();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        int result = super.hashCode();\n        result = HASH_BYTE * result + Long.hashCode(this.identifier);\n        if (this.location != null)\n        {\n            result = HASH_BYTE * result + this.location.hashCode();\n        }\n        result = HASH_BYTE * result + Arrays.hashCode(this.inEdgeIdentifiers);\n        result = HASH_BYTE * result + Arrays.hashCode(this.outEdgeIdentifiers);\n        result = HASH_BYTE * result + Arrays.hashCode(this.relationIdentifiers);\n        return result;\n    }\n\n    /**\n     * Please note that the edges returned from this method should *only* be used for identifiers.\n     *\n     * @see Node#inEdges()\n     * @return A set of identifier only edges\n     */\n    @Override\n    public SortedSet<Edge> inEdges()\n    {\n        return LongStream.of(this.inEdgeIdentifiers).mapToObj(LightEdge::new)\n                .collect(Collectors.toCollection(TreeSet::new));\n    }\n\n    /**\n     * Please note that the edges returned from this method should *only* be used for identifiers.\n     *\n     * @see Node#outEdges()\n     * @return A set of identifier only edges\n     */\n    @Override\n    public SortedSet<Edge> outEdges()\n    {\n        return LongStream.of(this.outEdgeIdentifiers).mapToObj(LightEdge::new)\n                .collect(Collectors.toCollection(TreeSet::new));\n    }\n\n    /**\n     * Please note that the relations returned from this method should *only* be used for\n     * identifiers.\n     *\n     * @see Node#relations()\n     * @return A set of identifier only relations\n     */\n    @Override\n    public Set<Relation> relations()\n    {\n        return LongStream.of(this.getRelationIdentifiers()).mapToObj(LightRelation::new)\n                .collect(Collectors.toSet());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightPoint.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.LongStream;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * A lightweight Point. If it doesn't need to be stored in a field, it isn't.\n *\n * @author Taylor Smock\n */\npublic class LightPoint extends Point implements LightLocationItem<LightPoint>\n{\n    private static final byte HASH_BYTE = 31;\n    private final long identifier;\n    private final Location location;\n    private final long[] relationIdentifiers;\n\n    static LightPoint from(final Point point)\n    {\n        return new LightPoint(point);\n    }\n\n    /**\n     * Create a new light point.\n     *\n     * @param identifier\n     *            The identifier for the new point\n     * @param location\n     *            The location of the point\n     * @param relationIdentifiers\n     *            Any relations for the point\n     */\n    public LightPoint(final Long identifier, final Location location,\n            final Set<Long> relationIdentifiers)\n    {\n        super(new EmptyAtlas());\n        this.identifier = identifier;\n        this.location = location;\n        this.relationIdentifiers = relationIdentifiers.stream().mapToLong(Long::longValue)\n                .toArray();\n    }\n\n    /**\n     * A basic point with just an identifier\n     *\n     * @param identifier\n     *            The identifier for the point\n     */\n    LightPoint(final long identifier)\n    {\n        this(identifier, null);\n    }\n\n    /**\n     * Create a new LightPoint with an id and location\n     *\n     * @param identifier\n     *            The identifier\n     * @param location\n     *            The location\n     */\n    LightPoint(final long identifier, final Location location)\n    {\n        this(identifier, location, Collections.emptySet());\n    }\n\n    /**\n     * Create a lightweight point from another point\n     *\n     * @param from\n     *            The point to copy information from\n     */\n    LightPoint(final Point from)\n    {\n        super(new EmptyAtlas());\n        this.identifier = from.getIdentifier();\n        this.location = from.getLocation();\n        this.relationIdentifiers = from.relations().stream().mapToLong(Relation::getIdentifier)\n                .toArray();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other == null || this.getClass() != other.getClass())\n        {\n            return false;\n        }\n        final var lightPoint = (LightPoint) other;\n        if (this.location != null && lightPoint.location != null)\n        {\n            return this.identifier == lightPoint.identifier\n                    && this.location.equals(lightPoint.location)\n                    && Arrays.equals(this.relationIdentifiers, lightPoint.relationIdentifiers);\n        }\n        else if (this.location == null && lightPoint.location == null)\n        {\n            return this.identifier == lightPoint.identifier\n                    && Arrays.equals(this.relationIdentifiers, lightPoint.relationIdentifiers);\n        }\n        return false;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return this.location;\n    }\n\n    @Override\n    public long[] getRelationIdentifiers()\n    {\n        return this.relationIdentifiers.clone();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        int result = super.hashCode();\n        if (this.location != null)\n        {\n            result = HASH_BYTE * result + this.location.hashCode();\n        }\n        result = HASH_BYTE * result + Long.hashCode(this.identifier);\n        result = HASH_BYTE * result + Arrays.hashCode(this.relationIdentifiers);\n        return result;\n    }\n\n    /**\n     * Please note that the relations returned from this method should *only* be used for\n     * identifiers.\n     *\n     * @see Point#relations()\n     * @return A set of identifier only relations\n     */\n    @Override\n    public Set<Relation> relations()\n    {\n        return LongStream.of(this.getRelationIdentifiers()).mapToObj(LightRelation::new)\n                .collect(Collectors.toSet());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightRelation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.LongStream;\nimport java.util.stream.Stream;\n\nimport javax.annotation.Nullable;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * A minimal relation class. Anything that does not need to be stored in memory isn't stored in\n * memory.\n *\n * @author Taylor Smock\n */\npublic class LightRelation extends Relation implements LightEntity<LightRelation>\n{\n    private static final byte HASH_BYTE = 31;\n    private static final String[] EMPTY_STRING_ARRAY = {};\n    private static final ItemType[] EMPTY_ITEM_TYPE_ARRAY = {};\n    private static final Location[][] EMPTY_LOCATIONS_ARRAY = {};\n    private final long identifier;\n    private final long[] relationIdentifiers;\n    private final long[] memberIdentifiers;\n    private final ItemType[] memberTypes;\n    private final String[] memberRoles;\n    private final Location[][] memberLocations;\n\n    /**\n     * Create a new LightRelation from a relation\n     *\n     * @param relation\n     *            The relation to copy\n     * @return The generated light relation\n     */\n    static LightRelation from(final Relation relation)\n    {\n        return new LightRelation(relation);\n    }\n\n    /**\n     * Create a new LightRelation with just an identifier\n     *\n     * @param identifier\n     *            The relation identifier\n     */\n    LightRelation(final long identifier)\n    {\n        super(new EmptyAtlas());\n        this.identifier = identifier;\n        this.relationIdentifiers = EMPTY_LONG_ARRAY;\n        this.memberIdentifiers = EMPTY_LONG_ARRAY;\n        this.memberRoles = EMPTY_STRING_ARRAY;\n        this.memberTypes = EMPTY_ITEM_TYPE_ARRAY;\n        this.memberLocations = EMPTY_LOCATIONS_ARRAY;\n    }\n\n    /**\n     * Create a new LightRelation with basic information from another relation\n     *\n     * @param from\n     *            The relation to copy from\n     */\n    LightRelation(final Relation from)\n    {\n        super(new EmptyAtlas());\n        this.identifier = from.getIdentifier();\n        this.relationIdentifiers = from.relations().stream().mapToLong(Relation::getIdentifier)\n                .toArray();\n        this.memberIdentifiers = from.allKnownOsmMembers().stream()\n                .mapToLong(member -> member.getEntity().getIdentifier()).toArray();\n        this.memberTypes = from.allKnownOsmMembers().stream()\n                .map(member -> member.getEntity().getType()).toArray(ItemType[]::new);\n        this.memberRoles = from.allKnownOsmMembers().stream().map(RelationMember::getRole)\n                .toArray(String[]::new);\n        this.memberLocations = from.allKnownOsmMembers().stream().map(RelationMember::getEntity)\n                .map(entity ->\n                {\n                    if (entity instanceof AtlasItem)\n                    {\n                        return Iterables.toArray(((AtlasItem) entity).getRawGeometry(),\n                                Location.class);\n                    }\n                    else if (entity instanceof Relation)\n                    {\n                        // Don't store sub relation entity at this time -- we aren't storing the\n                        // necessary information for\n                        // sub relations anyway (we would need to store the sub relation member\n                        // information).\n                        return EMPTY_LOCATION_ARRAY;\n                    }\n                    else\n                    {\n                        throw new CoreException(\"{0} type not understood\", entity.getType());\n                    }\n                }).toArray(Location[][]::new);\n    }\n\n    @Override\n    public RelationMemberList allKnownOsmMembers()\n    {\n        return this.members();\n    }\n\n    @Override\n    public List<Relation> allRelationsWithSameOsmIdentifier()\n    {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other == null || this.getClass() != other.getClass())\n        {\n            return false;\n        }\n        final var that = (LightRelation) other;\n        return this.identifier == that.identifier\n                && Arrays.equals(this.relationIdentifiers, that.relationIdentifiers)\n                && Arrays.equals(this.memberIdentifiers, that.memberIdentifiers)\n                && Arrays.equals(this.memberTypes, that.memberTypes)\n                && Arrays.equals(this.memberRoles, that.memberRoles)\n                && Arrays.deepEquals(this.memberLocations, that.memberLocations);\n    }\n\n    @Nullable\n    @Override\n    public Iterable<Location> getGeometry()\n    {\n        return Stream.of(this.memberLocations).flatMap(Stream::of).collect(Collectors.toList());\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public long[] getRelationIdentifiers()\n    {\n        return this.relationIdentifiers.clone();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        int result = super.hashCode();\n        result = HASH_BYTE * result + Long.hashCode(this.identifier);\n        result = HASH_BYTE * result + Arrays.hashCode(this.relationIdentifiers);\n        result = HASH_BYTE * result + Arrays.hashCode(this.memberIdentifiers);\n        result = HASH_BYTE * result + Arrays.hashCode(this.memberTypes);\n        result = HASH_BYTE * result + Arrays.hashCode(this.memberRoles);\n        result = HASH_BYTE * result + Arrays.deepHashCode(this.memberLocations);\n        return result;\n    }\n\n    /**\n     * Please note that the entities returned from this method should *only* be used for\n     * identifiers.\n     *\n     * @see Relation#members()\n     * @return A set of identifier only entities\n     */\n    @Override\n    public RelationMemberList members()\n    {\n        final List<RelationMember> relationMemberList = new ArrayList<>(\n                this.memberIdentifiers.length);\n        for (var index = 0; index < this.memberIdentifiers.length; index++)\n        {\n            final AtlasEntity entity;\n            final long memberIdentifier = this.memberIdentifiers[index];\n            final ItemType type = this.memberTypes[index];\n            if (type == ItemType.RELATION)\n            {\n                entity = new LightRelation(memberIdentifier);\n            }\n            else if (type == ItemType.AREA)\n            {\n                entity = new LightArea(memberIdentifier, this.memberLocations[index]);\n            }\n            else if (type == ItemType.EDGE)\n            {\n                entity = new LightEdge(memberIdentifier, this.memberLocations[index]);\n            }\n            else if (type == ItemType.LINE)\n            {\n                entity = new LightLine(memberIdentifier, this.memberLocations[index]);\n            }\n            else if (type == ItemType.NODE)\n            {\n                entity = new LightNode(memberIdentifier, this.memberLocations[index][0]);\n            }\n            else if (type == ItemType.POINT)\n            {\n                entity = new LightPoint(memberIdentifier, this.memberLocations[index][0]);\n            }\n            else\n            {\n                throw new CoreException(\"{0} is not a known type\", this.memberTypes[index]);\n            }\n            relationMemberList\n                    .add(new RelationMember(this.memberRoles[index], entity, this.getIdentifier()));\n        }\n        return new RelationMemberList(relationMemberList);\n    }\n\n    @Override\n    public Long osmRelationIdentifier()\n    {\n        return this.getOsmIdentifier();\n    }\n\n    /**\n     * Please note that the relations returned from this method should *only* be used for\n     * identifiers.\n     *\n     * @see Relation#relations()\n     * @return A set of identifier only relations\n     */\n    @Override\n    public Set<Relation> relations()\n    {\n        return LongStream.of(this.getRelationIdentifiers()).mapToObj(LightRelation::new)\n                .collect(Collectors.toSet());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiArea.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\nimport com.google.common.collect.Sets;\n\n/**\n * {@link Area} made from a {@link MultiAtlas}.\n *\n * @author matthieun\n */\npublic class MultiArea extends Area\n{\n    private static final long serialVersionUID = 4710025391581335160L;\n\n    // Not index!\n    private final long identifier;\n\n    private SubAreaList subAreaList;\n\n    protected MultiArea(final MultiAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public Polygon asPolygon()\n    {\n        return getRepresentativeSubArea().asPolygon();\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public SubAreaList getSubAreas()\n    {\n        if (this.subAreaList == null)\n        {\n            this.subAreaList = multiAtlas().subAreas(this.identifier);\n        }\n        return this.subAreaList;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.getRepresentativeSubArea().getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        Set<Relation> unionOfAllParentRelations = new HashSet<>();\n        for (final Area subArea : getSubAreas().getSubAreas())\n        {\n            final Set<Relation> currentSubAreaParentRelations = multiAtlas()\n                    .multifyRelations(subArea);\n            unionOfAllParentRelations = Sets.union(unionOfAllParentRelations,\n                    currentSubAreaParentRelations);\n        }\n        return unionOfAllParentRelations;\n    }\n\n    private Area getRepresentativeSubArea()\n    {\n        return getSubAreas().getSubAreas().get(0);\n    }\n\n    private MultiAtlas multiAtlas()\n    {\n        return (MultiAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlas.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.io.ObjectInputStream;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.AbstractAtlas;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.index.RTree;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.maps.LongToIntegerMultiMap;\nimport org.openstreetmap.atlas.utilities.maps.LongToLongMap;\nimport org.openstreetmap.atlas.utilities.maps.LongToLongMultiMap;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.ImmutableList;\n\n/**\n * {@link Atlas} that is backed by multiple {@link Atlas}es stitched on the fly.\n *\n * @author matthieun\n */\npublic class MultiAtlas extends AbstractAtlas\n{\n    private static final long serialVersionUID = -5917117808042670700L;\n    private static final Logger logger = LoggerFactory.getLogger(MultiAtlas.class);\n\n    private static final double ARRAY_SIZE_MULTIPLIER = 1.1;\n\n    private final List<Atlas> atlases;\n\n    private final AtlasMetaData metaData;\n\n    private final long numberOfEdges;\n    private final long numberOfNodes;\n    private final long numberOfAreas;\n    private final long numberOfLines;\n    private final long numberOfPoints;\n    private final long numberOfRelations;\n    private final RTree<Integer> atlasSpatialIndex;\n\n    // The identifiers of the Nodes, and the Atlases we can find them into. Only shared nodes, used\n    // for connectivity will be referenced in more than one Atlas (for real time stitching of the\n    // navigable network).\n    private final LongToIntegerMultiMap nodeIdentifierToAtlasIndices;\n    private final LongToIntegerMultiMap edgeIdentifierToAtlasIndices;\n    private final LongToIntegerMultiMap areaIdentifierToAtlasIndices;\n    private final LongToIntegerMultiMap lineIdentifierToAtlasIndices;\n    private final LongToIntegerMultiMap pointIdentifierToAtlasIndices;\n    private final LongToIntegerMultiMap relationIdentifierToAtlasIndices;\n\n    // Re-build relations with the same OSM identifier\n    private final LongToLongMultiMap relationOsmIdentifierToRelationIdentifiers;\n    private final LongToLongMap relationIdentifierToRelationOsmIdentifier;\n\n    private final int subArraySize;\n    private final long maximumSize;\n\n    private final int nodeMemoryBlockSize;\n    private final int edgeMemoryBlockSize;\n    private final int areaMemoryBlockSize;\n    private final int lineMemoryBlockSize;\n    private final int pointMemoryBlockSize;\n    private final int relationMemoryBlockSize;\n\n    private final int nodeHashSize;\n    private final int edgeHashSize;\n    private final int areaHashSize;\n    private final int lineHashSize;\n    private final int pointHashSize;\n    private final int relationHashSize;\n\n    // Custom fixers for crossing edges and overlapping nodes\n    private final MultiAtlasOverlappingNodesFixer nodesFixer;\n\n    /**\n     * Load a {@link MultiAtlas} from a serialized resource\n     *\n     * @param resource\n     *            The {@link Resource} to read from\n     * @return The deserialized {@link MultiAtlas}\n     */\n    public static MultiAtlas load(final Resource resource)\n    {\n        try (ObjectInputStream input = new ObjectInputStream(resource.read()))\n        {\n            final MultiAtlas result = (MultiAtlas) input.readObject();\n            return result;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not load Atlas from {}\", e, resource);\n        }\n    }\n\n    /**\n     * Load a {@link MultiAtlas} from an {@link Iterable} of {@link PackedAtlas} serialized\n     * resources\n     *\n     * @param resources\n     *            The {@link Resource}s to read from (which each contain a serialized\n     *            {@link PackedAtlas}).\n     * @return The deserialized {@link MultiAtlas}\n     */\n    public static MultiAtlas loadFromPackedAtlas(final Iterable<? extends Resource> resources)\n    {\n        return loadFromPackedAtlas(resources, false);\n    }\n\n    /**\n     * Load a {@link MultiAtlas} from an {@link Iterable} of {@link PackedAtlas} serialized\n     * resources\n     *\n     * @param resources\n     *            The {@link Resource}s to read from (which each contain a serialized\n     *            {@link PackedAtlas}).\n     * @param lotsOfOverlap\n     *            If this is true, then the builder will start with small arrays and re-size a lot,\n     *            but won't waste memory because of the overlap of the sub-{@link Atlas}es. However,\n     *            if this is false, the builder will blindly sum all the items of all the\n     *            {@link Atlas}es regardless of overlap, and will hence allocate potentially more\n     *            memory than necessary. In that case though, the arrays will never resize, and the\n     *            load time will be faster.\n     * @return The deserialized {@link MultiAtlas}\n     */\n    public static MultiAtlas loadFromPackedAtlas(final Iterable<? extends Resource> resources,\n            final boolean lotsOfOverlap)\n    {\n        if (Iterables.size(resources) == 0)\n        {\n            throw new CoreException(\"Can't create an atlas from zero resources\");\n        }\n        return new MultiAtlas(Iterables.translate(resources, resource ->\n        {\n            try\n            {\n                return PackedAtlas.load(resource);\n            }\n            catch (final Exception exception)\n            {\n                throw new CoreException(\"Failed to load an atlas from {} with name {}\",\n                        resource.getClass().getName(), resource.getName(), exception);\n            }\n        }), lotsOfOverlap);\n    }\n\n    /**\n     * Load a {@link MultiAtlas} from an {@link Iterable} of {@link PackedAtlas} serialized\n     * resources\n     *\n     * @param resources\n     *            The {@link Resource}s to read from (which each contain a serialized\n     *            {@link PackedAtlas}).\n     * @param lotsOfOverlap\n     *            If this is true, then the builder will start with small arrays and re-size a lot,\n     *            but won't waste memory because of the overlap of the sub-{@link Atlas}es. However,\n     *            if this is false, the builder will blindly sum all the items of all the\n     *            {@link Atlas}es regardless of overlap, and will hence allocate potentially more\n     *            memory than necessary. In that case though, the arrays will never resize, and the\n     *            load time will be faster.\n     * @param filter\n     *            The {@link Predicate} to use when loading from {@link PackedAtlas}\n     * @return The deserialized {@link MultiAtlas}\n     */\n    public static MultiAtlas loadFromPackedAtlas(final Iterable<? extends Resource> resources,\n            final boolean lotsOfOverlap, final Predicate<AtlasEntity> filter)\n    {\n        if (Iterables.size(resources) == 0)\n        {\n            throw new CoreException(\"Can't create an atlas from zero resources\");\n        }\n        final Iterable<Atlas> validAtlases = Iterables\n                .translate(Iterables.stream(Iterables.translate(resources, resource ->\n                {\n                    final Atlas atlas;\n                    try\n                    {\n                        atlas = PackedAtlas.load(resource);\n                    }\n                    catch (final Exception exception)\n                    {\n                        throw new CoreException(\"Failed to load an atlas from {} with name {}\",\n                                resource.getClass().getName(), resource.getName(), exception);\n                    }\n                    return atlas.subAtlas(filter, AtlasCutType.SOFT_CUT);\n                })).filter(Optional::isPresent), Optional::get);\n        if (Iterables.size(validAtlases) == 0)\n        {\n            throw new CoreException(\"Provided entity filter will result in an empty Atlas\");\n        }\n        return new MultiAtlas(validAtlases, lotsOfOverlap);\n    }\n\n    /**\n     * Load a {@link MultiAtlas} from an {@link Iterable} of {@link PackedAtlas} serialized\n     * resources\n     *\n     * @param resources\n     *            The {@link Resource}s to read from (which each contain a serialized\n     *            {@link PackedAtlas}).\n     * @param filter\n     *            The {@link Predicate} to use when loading from {@link PackedAtlas}\n     * @return The deserialized {@link MultiAtlas}\n     */\n    public static MultiAtlas loadFromPackedAtlas(final Iterable<? extends Resource> resources,\n            final Predicate<AtlasEntity> filter)\n    {\n        return loadFromPackedAtlas(resources, false, filter);\n    }\n\n    /**\n     * Load a {@link MultiAtlas} from {@link PackedAtlas} serialized resources\n     *\n     * @param resources\n     *            The {@link Resource}s to read from (which each contain a serialized\n     *            {@link PackedAtlas}).\n     * @return The deserialized {@link MultiAtlas}\n     */\n    public static MultiAtlas loadFromPackedAtlas(final Resource... resources)\n    {\n        return loadFromPackedAtlas(Iterables.iterable(resources), false);\n    }\n\n    /**\n     * Create an {@link Atlas} from stitching many other {@link Atlas}\n     *\n     * @param atlases\n     *            The {@link Atlas}es to stitch together\n     */\n    public MultiAtlas(final Atlas... atlases)\n    {\n        this(Iterables.iterable(atlases), false);\n    }\n\n    /**\n     * Create an {@link Atlas} from stitching many other {@link Atlas}\n     *\n     * @param atlases\n     *            The {@link Atlas}es to stitch together\n     */\n    public MultiAtlas(final Iterable<Atlas> atlases)\n    {\n        this(atlases, false);\n    }\n\n    /**\n     * Create an {@link Atlas} from stitching many other {@link Atlas}\n     *\n     * @param atlases\n     *            The {@link Atlas}es to stitch together\n     * @param lotsOfOverlap\n     *            If this is true, then the builder will start with small arrays and re-size a lot,\n     *            but won't waste memory because of the overlap of the sub-{@link Atlas}es. However,\n     *            if this is false, the builder will blindly sum all the items of all the\n     *            {@link Atlas}es regardless of overlap, and will hence allocate potentially more\n     *            memory than necessary. In that case though, the arrays will never resize, and the\n     *            load time will be faster.\n     */\n    public MultiAtlas(final Iterable<Atlas> atlases, final boolean lotsOfOverlap)\n    {\n        this(Iterables.asList(atlases), lotsOfOverlap);\n    }\n\n    /**\n     * Create an {@link Atlas} from stitching many other {@link Atlas}\n     *\n     * @param atlases\n     *            The {@link Atlas}es to stitch together\n     */\n    public MultiAtlas(final List<Atlas> atlases)\n    {\n        this(atlases, false);\n    }\n\n    /**\n     * Create an {@link Atlas} from stitching many other {@link Atlas}\n     *\n     * @param atlases\n     *            The {@link Atlas}es to stitch together\n     * @param lotsOfOverlap\n     *            If this is true, then the builder will start with small arrays and re-size a lot,\n     *            but won't waste memory because of the overlap of the sub-{@link Atlas}es. However,\n     *            if this is false, the builder will blindly sum all the items of all the\n     *            {@link Atlas}es regardless of overlap, and will hence allocate potentially more\n     *            memory than necessary. In that case though, the arrays will never resize, and the\n     *            load time will be faster.\n     */\n    public MultiAtlas(final List<Atlas> atlases, final boolean lotsOfOverlap)\n    {\n        this(atlases, lotsOfOverlap, true);\n    }\n\n    public MultiAtlas(final boolean fixNodesOnOppositeAntiMeridians, final Atlas... atlases)\n    {\n        this(Iterables.iterable(atlases), false, fixNodesOnOppositeAntiMeridians);\n    }\n\n    public MultiAtlas(final Iterable<Atlas> atlases, final boolean lotsOfOverlap,\n            final boolean fixNodesOnOppositeAntiMeridians)\n    {\n        this(Iterables.asList(atlases), lotsOfOverlap, fixNodesOnOppositeAntiMeridians);\n    }\n\n    public MultiAtlas(final List<Atlas> atlases, final boolean lotsOfOverlap,\n            final boolean fixNodesOnOppositeAntiMeridians)\n    {\n        if (atlases.isEmpty())\n        {\n            throw new CoreException(\"An Atlas is Located, and therefore cannot be empty.\");\n        }\n        this.atlases = atlases;\n        this.atlasSpatialIndex = newPackedAtlasSpatialIndex();\n        long numberOfNodes;\n        long numberOfEdges;\n        long numberOfAreas;\n        long numberOfLines;\n        long numberOfPoints;\n        long numberOfRelations;\n        if (lotsOfOverlap)\n        {\n            // We do not know in advance how much overlap there will be. We choose to re-size\n            // instead of wasting memory\n            numberOfEdges = DEFAULT_NUMBER_OF_ITEMS;\n            numberOfNodes = DEFAULT_NUMBER_OF_ITEMS;\n            numberOfAreas = DEFAULT_NUMBER_OF_ITEMS;\n            numberOfLines = DEFAULT_NUMBER_OF_ITEMS;\n            numberOfPoints = DEFAULT_NUMBER_OF_ITEMS;\n            numberOfRelations = DEFAULT_NUMBER_OF_ITEMS;\n        }\n        else\n        {\n            // Re-sizing arrays is prohibitive, so even if we do not know how much overlap there\n            // will be, we choose to waste memory instead.\n            numberOfNodes = Iterables.count(this.atlases, atlas -> atlas.numberOfNodes());\n            numberOfEdges = Iterables.count(this.atlases, atlas -> atlas.numberOfEdges());\n            numberOfAreas = Iterables.count(this.atlases, atlas -> atlas.numberOfAreas());\n            numberOfLines = Iterables.count(this.atlases, atlas -> atlas.numberOfLines());\n            numberOfPoints = Iterables.count(this.atlases, atlas -> atlas.numberOfPoints());\n            numberOfRelations = Iterables.count(this.atlases, atlas -> atlas.numberOfRelations());\n        }\n        if (atlases.size() > 1)\n        {\n            numberOfNodes = Math.round(numberOfNodes * ARRAY_SIZE_MULTIPLIER);\n            numberOfEdges = Math.round(numberOfEdges * ARRAY_SIZE_MULTIPLIER);\n            numberOfAreas = Math.round(numberOfAreas * ARRAY_SIZE_MULTIPLIER);\n            numberOfLines = Math.round(numberOfLines * ARRAY_SIZE_MULTIPLIER);\n            numberOfPoints = Math.round(numberOfPoints * ARRAY_SIZE_MULTIPLIER);\n            numberOfRelations = Math.round(numberOfRelations * ARRAY_SIZE_MULTIPLIER);\n        }\n        int index = 0;\n        for (final Atlas atlas : this.atlases)\n        {\n            this.atlasSpatialIndex.add(atlas.bounds(), index);\n            index++;\n        }\n\n        this.subArraySize = Integer.MAX_VALUE;\n        this.maximumSize = Long.MAX_VALUE;\n\n        this.nodeMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                numberOfNodes % Integer.MAX_VALUE);\n        this.edgeMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                numberOfEdges % Integer.MAX_VALUE);\n        this.areaMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                numberOfAreas % Integer.MAX_VALUE);\n        this.lineMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                numberOfLines % Integer.MAX_VALUE);\n        this.pointMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                numberOfPoints % Integer.MAX_VALUE);\n        this.relationMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                numberOfRelations % Integer.MAX_VALUE);\n\n        this.nodeHashSize = (int) Math\n                .max(Math.min(numberOfNodes / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        this.edgeHashSize = (int) Math\n                .max(Math.min(numberOfEdges / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        this.areaHashSize = (int) Math\n                .max(Math.min(numberOfAreas / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        this.lineHashSize = (int) Math\n                .max(Math.min(numberOfLines / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        this.pointHashSize = (int) Math\n                .max(Math.min(numberOfPoints / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        this.relationHashSize = (int) Math\n                .max(Math.min(numberOfRelations / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n\n        this.nodeIdentifierToAtlasIndices = new LongToIntegerMultiMap(\n                \"MultiAtlas - nodeIdentifierToAtlasIndices\", this.maximumSize, this.nodeHashSize,\n                this.nodeMemoryBlockSize, this.subArraySize, this.nodeMemoryBlockSize,\n                this.subArraySize);\n        this.edgeIdentifierToAtlasIndices = new LongToIntegerMultiMap(\n                \"MultiAtlas - edgeIdentifierToAtlasIndex\", this.maximumSize, this.edgeHashSize,\n                this.edgeMemoryBlockSize, this.subArraySize, this.edgeMemoryBlockSize,\n                this.subArraySize);\n        this.areaIdentifierToAtlasIndices = new LongToIntegerMultiMap(\n                \"MultiAtlas - areaIdentifierToAtlasIndex\", this.maximumSize, this.areaHashSize,\n                this.areaMemoryBlockSize, this.subArraySize, this.areaMemoryBlockSize,\n                this.subArraySize);\n        this.lineIdentifierToAtlasIndices = new LongToIntegerMultiMap(\n                \"MultiAtlas - lineIdentifierToAtlasIndex\", this.maximumSize, this.lineHashSize,\n                this.lineMemoryBlockSize, this.subArraySize, this.lineMemoryBlockSize,\n                this.subArraySize);\n        this.pointIdentifierToAtlasIndices = new LongToIntegerMultiMap(\n                \"MultiAtlas - pointIdentifierToAtlasIndex\", this.maximumSize, this.pointHashSize,\n                this.pointMemoryBlockSize, this.subArraySize, this.pointMemoryBlockSize,\n                this.subArraySize);\n        this.relationIdentifierToAtlasIndices = new LongToIntegerMultiMap(\n                \"MultiAtlas - relationIdentifierToAtlasIndices\", this.maximumSize,\n                this.relationHashSize, this.relationMemoryBlockSize, this.subArraySize,\n                this.relationMemoryBlockSize, this.subArraySize);\n        this.relationOsmIdentifierToRelationIdentifiers = new LongToLongMultiMap(\n                \"MultiAtlas - relationOsmIdentifierToRelationIdentifier\", this.maximumSize,\n                this.relationHashSize, this.relationMemoryBlockSize, this.subArraySize,\n                this.relationMemoryBlockSize, this.subArraySize);\n        this.relationIdentifierToRelationOsmIdentifier = new LongToLongMap(\n                \"MultiAtlas - relationIdentifierToRelationOsmIdentifier\", this.maximumSize,\n                this.relationHashSize, this.relationMemoryBlockSize, this.subArraySize,\n                this.relationMemoryBlockSize, this.subArraySize);\n\n        // Populate the pointers\n        int atlasIndex = 0;\n        for (final Atlas atlas : this.atlases)\n        {\n            populateReferences(atlas, atlasIndex);\n            atlasIndex++;\n        }\n\n        // Build the spatial indices\n        this.getAsNewNodeSpatialIndex();\n        this.getAsNewEdgeSpatialIndex();\n        this.getAsNewAreaSpatialIndex();\n        this.getAsNewLineSpatialIndex();\n        this.getAsNewPointSpatialIndex();\n        this.getAsNewRelationSpatialIndex();\n        this.nodeIdentifierToAtlasIndices\n                .forEach(identifier -> this.getNodeSpatialIndex().add(node(identifier)));\n        this.edgeIdentifierToAtlasIndices\n                .forEach(identifier -> this.getEdgeSpatialIndex().add(edge(identifier)));\n        this.areaIdentifierToAtlasIndices\n                .forEach(identifier -> this.getAreaSpatialIndex().add(area(identifier)));\n        this.lineIdentifierToAtlasIndices\n                .forEach(identifier -> this.getLineSpatialIndex().add(line(identifier)));\n        this.pointIdentifierToAtlasIndices\n                .forEach(identifier -> this.getPointSpatialIndex().add(point(identifier)));\n        this.relationIdentifierToAtlasIndices.forEach(identifier ->\n        {\n            final Relation relation = relation(identifier);\n            if (!relation.members().isEmpty() && relation.bounds() != null)\n            {\n                // The relation is not empty, hence it is located\n                this.getRelationSpatialIndex().add(relation);\n            }\n            final long osmIdentifier = relation.osmRelationIdentifier();\n            this.relationOsmIdentifierToRelationIdentifiers.add(osmIdentifier, identifier);\n            this.relationIdentifierToRelationOsmIdentifier.put(identifier, osmIdentifier);\n        });\n\n        // Find the overlapping nodes. Main to alternate has a one to many relationship. A main\n        // cannot be an alternate and vice versa\n        this.nodesFixer = new MultiAtlasOverlappingNodesFixer(this,\n                fixNodesOnOppositeAntiMeridians);\n        this.nodesFixer.aggregateSameLocationNodes();\n\n        // At this point de-duplication has been done already.\n        this.numberOfEdges = this.edgeIdentifierToAtlasIndices.size();\n        this.numberOfNodes = this.nodeIdentifierToAtlasIndices.size();\n        this.numberOfAreas = this.areaIdentifierToAtlasIndices.size();\n        this.numberOfLines = this.lineIdentifierToAtlasIndices.size();\n        this.numberOfPoints = this.pointIdentifierToAtlasIndices.size();\n        this.numberOfRelations = this.relationIdentifierToAtlasIndices.size();\n\n        if (!lotsOfOverlap)\n        {\n            // In case we have small overlap and the arrays are larger than necessary, resize is\n            // worth it if the arrays are more than twice as big as needed.\n            final Ratio trimRatio = Ratio.HALF;\n            this.nodeIdentifierToAtlasIndices.trimIfLessFilledThan(trimRatio);\n            this.edgeIdentifierToAtlasIndices.trimIfLessFilledThan(trimRatio);\n            this.areaIdentifierToAtlasIndices.trimIfLessFilledThan(trimRatio);\n            this.lineIdentifierToAtlasIndices.trimIfLessFilledThan(trimRatio);\n            this.pointIdentifierToAtlasIndices.trimIfLessFilledThan(trimRatio);\n            this.relationIdentifierToAtlasIndices.trimIfLessFilledThan(trimRatio);\n            this.relationOsmIdentifierToRelationIdentifiers.trimIfLessFilledThan(trimRatio);\n            this.relationIdentifierToRelationOsmIdentifier.trimIfLessFilledThan(trimRatio);\n        }\n\n        // Update the meta data.\n        this.metaData = mergeMetaData();\n    }\n\n    @Override\n    public Area area(final long identifier)\n    {\n        if (this.areaIdentifierToAtlasIndices.containsKey(identifier))\n        {\n            return new MultiArea(this, identifier);\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Area> areas()\n    {\n        return Iterables.translate(this.areaIdentifierToAtlasIndices, this::area);\n    }\n\n    /**\n     * Get all the Atlas intersecting some bounds\n     *\n     * @param bounds\n     *            The bounds\n     * @return All the Atlas intersecting the bounds\n     */\n    public Set<Atlas> atlasIntersecting(final Polygon bounds)\n    {\n        if (!(bounds instanceof Rectangle))\n        {\n            throw new UnsupportedOperationException(\"Non-Rectangle Polygons not supported yet.\");\n        }\n        return Iterables.asSet(Iterables.translate(this.atlasSpatialIndex.get(bounds.bounds()),\n                atlasIndex -> this.atlases.get(atlasIndex)));\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return Rectangle.forLocated(this.atlases);\n    }\n\n    @Override\n    public Edge edge(final long identifier)\n    {\n        if (this.edgeIdentifierToAtlasIndices.containsKey(identifier))\n        {\n            return new MultiEdge(this, identifier);\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Edge> edges()\n    {\n        return Iterables.translate(this.edgeIdentifierToAtlasIndices, this::edge);\n    }\n\n    @Override\n    public Line line(final long identifier)\n    {\n        if (this.lineIdentifierToAtlasIndices.containsKey(identifier))\n        {\n            return new MultiLine(this, identifier);\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Line> lines()\n    {\n        return Iterables.translate(this.lineIdentifierToAtlasIndices, this::line);\n    }\n\n    @Override\n    public AtlasMetaData metaData()\n    {\n        return this.metaData;\n    }\n\n    @Override\n    public Node node(final long identifier)\n    {\n        if (this.nodeIdentifierToAtlasIndices.containsKey(identifier))\n        {\n            return new MultiNode(this, identifier);\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Node> nodes()\n    {\n        // Use the identifier here to avoid listing duplicated nodes twice.\n        return Iterables.translate(this.nodeIdentifierToAtlasIndices, this::node);\n    }\n\n    @Override\n    public long numberOfAreas()\n    {\n        return this.numberOfAreas;\n    }\n\n    @Override\n    public long numberOfEdges()\n    {\n        return this.numberOfEdges;\n    }\n\n    @Override\n    public long numberOfLines()\n    {\n        return this.numberOfLines;\n    }\n\n    @Override\n    public long numberOfNodes()\n    {\n        return this.numberOfNodes;\n    }\n\n    @Override\n    public long numberOfPoints()\n    {\n        return this.numberOfPoints;\n    }\n\n    @Override\n    public long numberOfRelations()\n    {\n        return this.numberOfRelations;\n    }\n\n    public int numberOfSubAtlas()\n    {\n        return this.atlases.size();\n    }\n\n    @Override\n    public Point point(final long identifier)\n    {\n        if (this.pointIdentifierToAtlasIndices.containsKey(identifier))\n        {\n            return new MultiPoint(this, identifier);\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Point> points()\n    {\n        return Iterables.translate(this.pointIdentifierToAtlasIndices, this::point);\n    }\n\n    @Override\n    public Relation relation(final long identifier)\n    {\n        if (this.relationIdentifierToAtlasIndices.containsKey(identifier))\n        {\n            return new MultiRelation(this, identifier);\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Relation> relations()\n    {\n        return Iterables.translate(this.relationIdentifierToAtlasIndices, this::relation);\n    }\n\n    @Override\n    public void save(final WritableResource writableResource)\n    {\n        throw new CoreException(\n                \"A MultiAtlas has to be cloned to a {} before it can be saved. Consider using {}\",\n                PackedAtlas.class.getName(), PackedAtlasCloner.class.getName());\n    }\n\n    /**\n     * Get the List of {@link Atlas} that comprise this {@link MultiAtlas}.\n     *\n     * @return The list of {@link Atlas}\n     */\n    public List<Atlas> subAtlases()\n    {\n        return ImmutableList.copyOf(this.atlases);\n    }\n\n    protected List<Atlas> getAtlases()\n    {\n        return this.atlases;\n    }\n\n    protected int getEdgeHashSize()\n    {\n        return this.edgeHashSize;\n    }\n\n    protected LongToIntegerMultiMap getEdgeIdentifierToAtlasIndices()\n    {\n        return this.edgeIdentifierToAtlasIndices;\n    }\n\n    protected int getEdgeMemoryBlockSize()\n    {\n        return this.edgeMemoryBlockSize;\n    }\n\n    protected long getMaximumSize()\n    {\n        return this.maximumSize;\n    }\n\n    protected LongToIntegerMultiMap getNodeIdentifierToAtlasIndices()\n    {\n        return this.nodeIdentifierToAtlasIndices;\n    }\n\n    protected int getSubArraySize()\n    {\n        return this.subArraySize;\n    }\n\n    /**\n     * In case there is a main node overlapping this node, get the main node.\n     *\n     * @param identifier\n     *            The node identifier to query\n     * @return The identifier of the main node that has the exact same location\n     */\n    protected Optional<Long> mainNode(final Long identifier)\n    {\n        return this.nodesFixer.mainNode(identifier);\n    }\n\n    @Deprecated\n    protected Optional<Long> masterNode(final Long identifier)\n    {\n        return mainNode(identifier);\n    }\n\n    /**\n     * Take all the relations an {@link AtlasEntity} belongs to, and replace them with the\n     * corresponding relations linking back to this {@link MultiAtlas}.\n     *\n     * @param entity\n     *            The {@link AtlasEntity}\n     * @return The relations of this entity, as viewed from this multi atlas.\n     */\n    protected Set<Relation> multifyRelations(final AtlasEntity entity)\n    {\n        final Set<Relation> subRelations = entity.relations();\n        final Set<Relation> result = new HashSet<>();\n        for (final Relation relation : subRelations)\n        {\n            result.add(relation(relation.getIdentifier()));\n        }\n        return result;\n    }\n\n    /**\n     * In case this is a main node, get all the overlapping nodes.\n     *\n     * @param identifier\n     *            The node identifier to query\n     * @return The identifiers of the overlapping nodes that has the exact same location\n     */\n    protected Set<Long> overlappingNodes(final Long identifier)\n    {\n        return this.nodesFixer.overlappingNodes(identifier);\n    }\n\n    protected void populateReferences(final Atlas atlas, final int atlasIndex)\n    {\n        for (final Node node : atlas.nodes())\n        {\n            this.nodeIdentifierToAtlasIndices.add(node.getIdentifier(), atlasIndex);\n        }\n        for (final Edge edge : atlas.edges())\n        {\n            this.edgeIdentifierToAtlasIndices.add(edge.getIdentifier(), atlasIndex);\n        }\n        for (final Area area : atlas.areas())\n        {\n            this.areaIdentifierToAtlasIndices.add(area.getIdentifier(), atlasIndex);\n        }\n        for (final Line line : atlas.lines())\n        {\n            this.lineIdentifierToAtlasIndices.add(line.getIdentifier(), atlasIndex);\n        }\n        for (final Point point : atlas.points())\n        {\n            this.pointIdentifierToAtlasIndices.add(point.getIdentifier(), atlasIndex);\n        }\n        for (final Relation relation : atlas.relations())\n        {\n            this.relationIdentifierToAtlasIndices.add(relation.getIdentifier(), atlasIndex);\n        }\n    }\n\n    protected List<Relation> relationAllRelationsWithSameOsmIdentifier(final long identifier)\n    {\n        final List<Relation> result = new ArrayList<>();\n        final long osmIdentifier = this.relationIdentifierToRelationOsmIdentifier.get(identifier);\n        for (final long candidateIdentifier : this.relationOsmIdentifierToRelationIdentifiers\n                .get(osmIdentifier))\n        {\n            result.add(relation(candidateIdentifier));\n        }\n        return result;\n    }\n\n    /**\n     * Get the {@link Area} from the {@link Atlas} that has this identifier.\n     *\n     * @param identifier\n     *            The identifier to query\n     * @return The {@link Area}s that have this identifier\n     */\n    SubAreaList subAreas(final long identifier)\n    {\n        final List<Area> subAreas = new ArrayList<>();\n        for (final int index : this.areaIdentifierToAtlasIndices.get(identifier))\n        {\n            if (index != -1)\n            {\n                subAreas.add(this.atlases.get(index).area(identifier));\n            }\n        }\n        return new SubAreaList(subAreas);\n    }\n\n    /**\n     * Get the {@link Edge}s from the {@link Atlas} that has this identifier.\n     *\n     * @param identifier\n     *            The identifier to query\n     * @return The {@link Edge}s that have this identifier\n     */\n    SubEdgeList subEdge(final long identifier)\n    {\n        final List<Edge> subEdges = new ArrayList<>();\n        for (final int index : this.edgeIdentifierToAtlasIndices.get(identifier))\n        {\n            if (index != -1)\n            {\n                subEdges.add(this.atlases.get(index).edge(identifier));\n            }\n        }\n        return new SubEdgeList(subEdges);\n    }\n\n    /**\n     * Get the {@link Line}s from the {@link Atlas} that has this identifier.\n     *\n     * @param identifier\n     *            The identifier to query\n     * @return The {@link Line}s that have this identifier\n     */\n    SubLineList subLines(final long identifier)\n    {\n        final List<Line> subLines = new ArrayList<>();\n        for (final int index : this.lineIdentifierToAtlasIndices.get(identifier))\n        {\n            if (index != -1)\n            {\n                subLines.add(this.atlases.get(index).line(identifier));\n            }\n        }\n        return new SubLineList(subLines);\n    }\n\n    /**\n     * Get the nodes from different Atlases that have this identifier.\n     *\n     * @param identifier\n     *            The identifier to query\n     * @return The {@link Node}s that have this identifier\n     */\n    SubNodeList subNodes(final long identifier)\n    {\n        final List<Node> subNodes = new ArrayList<>();\n        for (final int index : this.nodeIdentifierToAtlasIndices.get(identifier))\n        {\n            // Add all the sub nodes that come from regular sub atlas\n            if (index != -1)\n            {\n                subNodes.add(this.atlases.get(index).node(identifier));\n            }\n        }\n        return new SubNodeList(subNodes);\n    }\n\n    /**\n     * Get the {@link Point}s from the {@link Atlas} that has this identifier.\n     *\n     * @param identifier\n     *            The identifier to query\n     * @return The {@link Point}s that have this identifier\n     */\n    SubPointList subPoints(final long identifier)\n    {\n        final List<Point> subPoints = new ArrayList<>();\n        for (final int index : this.pointIdentifierToAtlasIndices.get(identifier))\n        {\n            if (index != -1)\n            {\n                subPoints.add(this.atlases.get(index).point(identifier));\n            }\n        }\n        return new SubPointList(subPoints);\n    }\n\n    /**\n     * Get the {@link Relation}s from the {@link Atlas} that have this identifier.\n     *\n     * @param identifier\n     *            The identifier to query\n     * @return The {@link Relation}s that have this identifier\n     */\n    SubRelationList subRelations(final long identifier)\n    {\n        final List<Relation> subRelations = new ArrayList<>();\n        for (final int index : this.relationIdentifierToAtlasIndices.get(identifier))\n        {\n            if (index != -1)\n            {\n                subRelations.add(this.atlases.get(index).relation(identifier));\n            }\n        }\n        return new SubRelationList(subRelations);\n    }\n\n    private AtlasMetaData mergeMetaData()\n    {\n        final AtlasSize size = new AtlasSize(this.numberOfEdges, this.numberOfNodes,\n                this.numberOfAreas, this.numberOfLines, this.numberOfPoints,\n                this.numberOfRelations);\n        String codeVersion = null;\n        String dataVersion = null;\n        String shardName = null;\n        final Map<String, String> tags = Maps.hashMap();\n        // countries\n        final StringList countries = new StringList(this.atlases.stream().map(Atlas::metaData)\n                .map(AtlasMetaData::getCountry).filter(Optional::isPresent).map(Optional::get)\n                .flatMap(value -> StringList.split(value, \",\").stream()).distinct()\n                .collect(Collectors.toList()));\n        // code version\n        final List<String> codeVersions = this.atlases.stream().map(Atlas::metaData)\n                .map(AtlasMetaData::getCodeVersion).filter(Optional::isPresent).map(Optional::get)\n                .distinct().collect(Collectors.toList());\n        if (codeVersions.size() > 1)\n        {\n            logger.warn(\"Two sub atlas files have different code versions: {}\", codeVersions);\n        }\n        if (codeVersions.size() > 0)\n        {\n            codeVersion = codeVersions.get(0);\n        }\n        // data version\n        final List<String> dataVersions = this.atlases.stream().map(Atlas::metaData)\n                .map(AtlasMetaData::getDataVersion).filter(Optional::isPresent).map(Optional::get)\n                .distinct().collect(Collectors.toList());\n        if (dataVersions.size() > 1)\n        {\n            logger.warn(\"Two sub atlas files have different data versions: {}\", dataVersions);\n        }\n        if (dataVersions.size() > 0)\n        {\n            dataVersion = dataVersions.get(0);\n        }\n        // shard name\n        final List<String> shardNames = this.atlases.stream().map(Atlas::metaData)\n                .map(AtlasMetaData::getShardName).filter(Optional::isPresent).map(Optional::get)\n                .distinct().collect(Collectors.toList());\n        if (shardNames.size() > 1)\n        {\n            logger.warn(\"Two sub atlas files have different shard names: {}\", shardNames);\n        }\n        if (shardNames.size() > 0)\n        {\n            shardName = shardNames.get(0);\n        }\n        // tags\n        this.atlases.stream().map(Atlas::metaData).map(AtlasMetaData::getTags)\n                .flatMap(map -> map.entrySet().stream()).forEach(entry ->\n                {\n                    final String key = entry.getKey();\n                    final String value = entry.getValue();\n                    if (tags.containsKey(key))\n                    {\n                        final String overridenValue = tags.get(key);\n                        if (overridenValue != null && !overridenValue.equals(value))\n                        {\n                            logger.trace(\n                                    \"AtlasMetaData has conflicting values for the same key:\"\n                                            + \" key = {}, and values = [{}, {}]. 2nd one is kept.\",\n                                    key, overridenValue, value);\n                        }\n                    }\n                    tags.put(key, value);\n                });\n        return new AtlasMetaData(size, true, codeVersion, dataVersion,\n                countries.isEmpty() ? null : countries.join(\",\"), shardName, tags);\n    }\n\n    private RTree<Integer> newPackedAtlasSpatialIndex()\n    {\n        return new RTree<>();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlasLoaderCommand.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CounterWithStatistic;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class MultiAtlasLoaderCommand extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(MultiAtlasLoaderCommand.class);\n\n    private static final Switch<Atlas> FOLDER = new Switch<>(\"folder\",\n            \"Folder containing the atlas files\",\n            path -> new AtlasResourceLoader().load(new File(path)), Optionality.REQUIRED);\n    private static final Switch<String> OUTPUT = new Switch<>(\"output\", \"output atlas file\",\n            StringConverter.IDENTITY, Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new MultiAtlasLoaderCommand().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final CounterWithStatistic statistics = new CounterWithStatistic(logger);\n        final Atlas multi = (Atlas) command.get(FOLDER);\n        final File output = new File((String) command.get(OUTPUT));\n        statistics.summary();\n        final PackedAtlas packed = PackedAtlas.cloneFrom(multi);\n        packed.save(output);\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(FOLDER, OUTPUT);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlasOverlappingNodesFixer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.exception.AtlasIntegrityException;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.maps.MultiMapWithSet;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Merge nodes that exactly overlap.\n *\n * @author matthieun\n * @author samg\n */\npublic class MultiAtlasOverlappingNodesFixer implements Serializable\n{\n\n    private static final long serialVersionUID = -8926124646524609913L;\n\n    private static final Logger logger = LoggerFactory\n            .getLogger(MultiAtlasOverlappingNodesFixer.class);\n\n    private final MultiAtlas parent;\n    private final boolean fixNodesOnOppositeAntiMeridians;\n\n    // The overlapping nodes... Those maps should be tiny\n    private final Map<Long, Long> overlappingNodeIdentifierToMainNodeIdentifier = new HashMap<>();\n    private final MultiMapWithSet<Long, Long> mainNodeIdentifierToOverlappingNodeIdentifier = new MultiMapWithSet<>();\n\n    protected MultiAtlasOverlappingNodesFixer(final MultiAtlas parent,\n            final boolean fixNodesOnOppositeAntiMeridians)\n    {\n        this.parent = parent;\n        this.fixNodesOnOppositeAntiMeridians = fixNodesOnOppositeAntiMeridians;\n    }\n\n    /**\n     * This is to build maps of nodes that are at the same location, and to pick the main node based\n     * on point identifier\n     */\n    protected void aggregateSameLocationNodes()\n    {\n        this.parent.getNodeIdentifierToAtlasIndices().forEach(identifier ->\n        {\n            // if this identifier is in the map, then skip. otherwise, poll all\n            // overlapping nodes here, pick a main node based on identifier,\n            // and update the map\n            if (!this.overlappingNodeIdentifierToMainNodeIdentifier.containsKey(identifier))\n            {\n                final Node current = this.parent.node(identifier);\n                final SortedSet<Node> overlapping = new TreeSet<>((final Node a, final Node b) ->\n                {\n                    if (a.getIdentifier() < b.getIdentifier())\n                    {\n                        return -1;\n                    }\n                    else if (a.getIdentifier() > b.getIdentifier())\n                    {\n                        return 1;\n                    }\n                    return 0;\n                });\n                overlapping.addAll(nodesOverlapping(current));\n                if (overlapping.size() > 1)\n                {\n                    final Node main = overlapping.first();\n                    final long mainIdentifier = main.getIdentifier();\n                    overlapping.remove(main);\n                    overlapping.forEach(node ->\n                    {\n                        final long overlappingIdentifier = node.getIdentifier();\n                        this.overlappingNodeIdentifierToMainNodeIdentifier\n                                .put(overlappingIdentifier, mainIdentifier);\n                        this.mainNodeIdentifierToOverlappingNodeIdentifier.add(mainIdentifier,\n                                overlappingIdentifier);\n                    });\n                    warnIfNodesHaveDifferentTags(main, overlapping);\n                }\n            }\n        });\n    }\n\n    /**\n     * In case there is a main node overlapping this node, get the main node.\n     *\n     * @param identifier\n     *            The node identifier to query\n     * @return The identifier of the main node that has the exact same location\n     */\n    protected Optional<Long> mainNode(final Long identifier)\n    {\n        return Optional\n                .ofNullable(this.overlappingNodeIdentifierToMainNodeIdentifier.get(identifier));\n    }\n\n    @Deprecated\n    protected Optional<Long> masterNode(final Long identifier)\n    {\n        return mainNode(identifier);\n    }\n\n    /**\n     * In case this node is a main, get all the overlapping nodes.\n     *\n     * @param identifier\n     *            The node identifier to query\n     * @return The identifiers of the overlapping nodes that has the exact same location\n     */\n    protected Set<Long> overlappingNodes(final Long identifier)\n    {\n        if (this.mainNodeIdentifierToOverlappingNodeIdentifier.containsKey(identifier))\n        {\n            return this.mainNodeIdentifierToOverlappingNodeIdentifier.get(identifier);\n        }\n        else\n        {\n            return new HashSet<>();\n        }\n    }\n\n    /**\n     * @param main\n     *            The node to check for\n     * @return all the nodes that have the same location, including itself\n     */\n    private Set<Node> nodesOverlapping(final Node main)\n    {\n        final List<Rectangle> bounds = new ArrayList<>();\n        // Make sure that the AntiMeridian case is taken care of\n        final Location mainLocation = main.getLocation();\n        // This will be true for both the minimum antimeridian and the maximum.\n        if (this.fixNodesOnOppositeAntiMeridians\n                && (Longitude.ANTIMERIDIAN_WEST.equals(mainLocation.getLongitude())\n                        || Longitude.ANTIMERIDIAN_EAST.equals(mainLocation.getLongitude())))\n        {\n            final Location antimeridianMinimum = new Location(mainLocation.getLatitude(),\n                    Longitude.ANTIMERIDIAN_WEST);\n            final Location antimeridianMaximum = new Location(mainLocation.getLatitude(),\n                    Longitude.ANTIMERIDIAN_EAST);\n            bounds.add(Rectangle.forCorners(antimeridianMinimum, antimeridianMinimum));\n            bounds.add(Rectangle.forCorners(antimeridianMaximum, antimeridianMaximum));\n        }\n        else\n        {\n            bounds.add(main.bounds());\n        }\n        final Set<Node> others = new HashSet<>();\n        for (final Rectangle bound : bounds)\n        {\n            others.addAll(Iterables.asSet(this.parent.nodesWithin(bound)));\n        }\n        if (others.isEmpty())\n        {\n            throw new AtlasIntegrityException(\"A node has to overlap itself at least! {}\", main);\n        }\n        return others;\n    }\n\n    /**\n     * Print a warning for nodes that have the same location but different tags.\n     *\n     * @param main\n     *            The main node\n     * @param overlapping\n     *            The overlapping nodes\n     */\n    private void warnIfNodesHaveDifferentTags(final Node main, final Set<Node> overlapping)\n    {\n        final Map<String, String> tags = main.getTags();\n        for (final Node node : overlapping)\n        {\n            if (!tags.equals(node.getTags()))\n            {\n                logger.warn(\"Nodes overlap but have different tags: {} and {}\",\n                        main.getIdentifier(), node.getIdentifier());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\nimport com.google.common.collect.Sets;\n\n/**\n * {@link Edge} made from a {@link MultiAtlas}.\n *\n * @author matthieun\n */\npublic class MultiEdge extends Edge\n{\n    private static final long serialVersionUID = -3986525201031430336L;\n\n    // Not index!\n    private final long identifier;\n\n    private SubEdgeList subEdgeList;\n\n    protected MultiEdge(final MultiAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return this.getRepresentativeSubEdge().asPolyLine();\n    }\n\n    @Override\n    public Node end()\n    {\n        return new MultiNode(multiAtlas(),\n                getMainNodeIdentifier(this.getRepresentativeSubEdge().end().getIdentifier()));\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.getRepresentativeSubEdge().getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        Set<Relation> unionOfAllParentRelations = new HashSet<>();\n        for (final Edge subEdge : getSubEdges().getSubEdges())\n        {\n            final Set<Relation> currentSubEdgeParentRelations = multiAtlas()\n                    .multifyRelations(subEdge);\n            unionOfAllParentRelations = Sets.union(unionOfAllParentRelations,\n                    currentSubEdgeParentRelations);\n        }\n        return unionOfAllParentRelations;\n    }\n\n    @Override\n    public Node start()\n    {\n        return new MultiNode(multiAtlas(),\n                getMainNodeIdentifier(this.getRepresentativeSubEdge().start().getIdentifier()));\n    }\n\n    /**\n     * In case there is another node that overlaps this one, and the other one is the main, get the\n     * other one\n     *\n     * @param identifier\n     *            The node identifier\n     * @return The main node identifier if any, or identity\n     */\n    private Long getMainNodeIdentifier(final long identifier)\n    {\n        final Optional<Long> mainNodeIdentifier = multiAtlas().mainNode(identifier);\n        if (mainNodeIdentifier.isPresent())\n        {\n            return mainNodeIdentifier.get();\n        }\n        else\n        {\n            return identifier;\n        }\n    }\n\n    private Edge getRepresentativeSubEdge()\n    {\n        return getSubEdges().getSubEdges().get(0);\n    }\n\n    private SubEdgeList getSubEdges()\n    {\n        if (this.subEdgeList == null)\n        {\n            this.subEdgeList = this.multiAtlas().subEdge(this.identifier);\n        }\n        return this.subEdgeList;\n    }\n\n    private MultiAtlas multiAtlas()\n    {\n        return (MultiAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiLine.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\nimport com.google.common.collect.Sets;\n\n/**\n * {@link Area} made from a {@link MultiAtlas}.\n *\n * @author matthieun\n */\npublic class MultiLine extends Line\n{\n    private static final long serialVersionUID = 4833193008195471987L;\n\n    // Not index!\n    private final long identifier;\n\n    private SubLineList subLineList;\n\n    protected MultiLine(final MultiAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return getRepresentativeSubLine().asPolyLine();\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public SubLineList getSubLines()\n    {\n        if (this.subLineList == null)\n        {\n            this.subLineList = multiAtlas().subLines(this.identifier);\n        }\n        return this.subLineList;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.getRepresentativeSubLine().getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        Set<Relation> unionOfAllParentRelations = new HashSet<>();\n        for (final Line subLine : getSubLines().getSubLines())\n        {\n            final Set<Relation> currentSubLineParentRelations = multiAtlas()\n                    .multifyRelations(subLine);\n            unionOfAllParentRelations = Sets.union(unionOfAllParentRelations,\n                    currentSubLineParentRelations);\n        }\n        return unionOfAllParentRelations;\n    }\n\n    private Line getRepresentativeSubLine()\n    {\n        return getSubLines().getSubLines().get(0);\n    }\n\n    private MultiAtlas multiAtlas()\n    {\n        return (MultiAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiNode.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Sets;\n\n/**\n * {@link Node} made from a {@link MultiAtlas}\n *\n * @author matthieun\n */\npublic class MultiNode extends Node\n{\n    private static final long serialVersionUID = 4280290265432052817L;\n    private static final Logger logger = LoggerFactory.getLogger(MultiNode.class);\n\n    private final long identifier;\n    private SubNodeList subNodes;\n\n    MultiNode(final MultiAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return getRepresentativeSubNode().getLocation();\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return getRepresentativeSubNode().getTags();\n    }\n\n    @Override\n    public SortedSet<Edge> inEdges()\n    {\n        return attachedEdgesFromOverlappingNodes(Node::inEdges);\n    }\n\n    @Override\n    public SortedSet<Edge> outEdges()\n    {\n        return attachedEdgesFromOverlappingNodes(Node::outEdges);\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        Set<Relation> unionOfAllParentRelations = new HashSet<>();\n        for (final Node subNode : getSubNodes().getSubNodes())\n        {\n            final Set<Relation> currentSubNodeParentRelations = multiAtlas()\n                    .multifyRelations(subNode);\n            unionOfAllParentRelations = Sets.union(unionOfAllParentRelations,\n                    currentSubNodeParentRelations);\n        }\n        return unionOfAllParentRelations;\n    }\n\n    /**\n     * Get all the attached edges from one single node identifier, directly coming from the\n     * sub-Atlases...\n     *\n     * @param getConnectedEdges\n     *            The function that decides what side the edges are to be taken (in or out)\n     * @return All the attached edges from one single node identifier\n     */\n    private SortedSet<Edge> attachedEdges(final Function<Node, Set<Edge>> getConnectedEdges)\n    {\n        final Set<Long> subResult = new HashSet<>();\n        if (getSubNodes().size() == 1)\n        {\n            // The node is in one single sub-atlas only, hence not fixed.\n            getConnectedEdges.apply(getRepresentativeSubNode())\n                    .forEach(edge -> subResult.add(edge.getIdentifier()));\n        }\n        else\n        {\n            // The node is in many sub-atlases. Collect all the edges from the different Atlases.\n            final SubNodeList subNodes = getSubNodes();\n            for (final Node node : subNodes.getSubNodes())\n            {\n                for (final Edge connectedEdge : getConnectedEdges.apply(node))\n                {\n                    subResult.add(connectedEdge.getIdentifier());\n                }\n            }\n        }\n\n        // Return MultiEdges so they still have a reference to the MultiAtlas\n        final SortedSet<Edge> result = new TreeSet<>();\n        for (final Long subEdgeIdentifier : subResult)\n        {\n            final Edge multiEdge = multiAtlas().edge(subEdgeIdentifier);\n            if (multiEdge == null)\n            {\n                // This can happen sometimes when a node has another overlapping node in a sub-atlas\n                // which gets all the connected edges, and that edge has been corrected for way\n                // sectioning issues at the border, but the edge has been marked as \"removed\" from\n                // the other node and not this one. Log it, and do not include the null.\n                final List<String> missingEdgeAtlasNames = new ArrayList<>();\n                final List<Atlas> atlases = ((MultiAtlas) getAtlas()).getAtlases();\n                for (int index = 0; index < atlases.size(); index++)\n                {\n                    final Atlas subAtlas = atlases.get(index);\n                    final Edge subEdge = subAtlas.edge(subEdgeIdentifier);\n                    if (subEdge != null)\n                    {\n                        missingEdgeAtlasNames.add(subAtlas.getName());\n                    }\n                }\n                logger.warn(\"Some edge got lost in translation, and is not in the MultiAtlas. \"\n                        + \"The node below probably has another node at the exact same location!\\n\\t\"\n                        + \"Node: {}\\n\\t\" + \"Edge connected: {}\\n\\t\" + \"From SubAtlas: {}\",\n                        this.identifier, subEdgeIdentifier, missingEdgeAtlasNames);\n            }\n            else\n            {\n                result.add(multiEdge);\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Get all the attached edges from one node, including those coming from overlapping nodes.\n     *\n     * @param getConnectedEdges\n     *            The function that decides what side the edges are to be taken (in or out)\n     * @return All the attached edges from one node, including those coming from overlapping edges\n     */\n    private SortedSet<Edge> attachedEdgesFromOverlappingNodes(\n            final Function<Node, Set<Edge>> getConnectedEdges)\n    {\n        final Set<Long> alternateNodes = multiAtlas().overlappingNodes(getIdentifier());\n        final Optional<Long> mainNode = multiAtlas().mainNode(this.identifier);\n        if (!alternateNodes.isEmpty())\n        {\n            // This Multi-Node is an overlapping main node. Return all the in/out edges of this\n            // node, plus those of the other alternate nodes.\n            final SortedSet<Edge> result = attachedEdges(getConnectedEdges);\n            alternateNodes.forEach(alternateIdentifier -> result\n                    .addAll(((MultiNode) multiAtlas().node(alternateIdentifier))\n                            .attachedEdges(getConnectedEdges)));\n            return result;\n        }\n        else if (mainNode.isPresent())\n        {\n            // This Multi-Node is an overlapping alternate node. Return no edges. All its edges will\n            // be directed to the main node. Alternate nodes are then rendered useless (even though\n            // they are still present) to mimic the behavior of the PackedAtlas.\n            return new TreeSet<>();\n        }\n        else\n        {\n            // This Multi-Node is not an overlapping node. Return the standard set of in/out edges.\n            return attachedEdges(getConnectedEdges);\n        }\n    }\n\n    private Node getRepresentativeSubNode()\n    {\n        return getSubNodes().getSubNodes().get(0);\n    }\n\n    private SubNodeList getSubNodes()\n    {\n        if (this.subNodes == null)\n        {\n            this.subNodes = multiAtlas().subNodes(this.identifier);\n        }\n        return this.subNodes;\n    }\n\n    private MultiAtlas multiAtlas()\n    {\n        return (MultiAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiPoint.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\nimport com.google.common.collect.Sets;\n\n/**\n * {@link Point} made from a {@link MultiAtlas}.\n *\n * @author matthieun\n */\npublic class MultiPoint extends Point\n{\n    private static final long serialVersionUID = 209103872813085178L;\n\n    // Not index!\n    private final long identifier;\n\n    private SubPointList subPoints;\n\n    protected MultiPoint(final MultiAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return getRepresentativeSubPoint().getLocation();\n    }\n\n    public SubPointList getSubPoints()\n    {\n        if (this.subPoints == null)\n        {\n            this.subPoints = multiAtlas().subPoints(this.identifier);\n        }\n        return this.subPoints;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.getRepresentativeSubPoint().getTags();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        Set<Relation> unionOfAllParentRelations = new HashSet<>();\n        for (final Point subPoint : getSubPoints().getSubPoints())\n        {\n            final Set<Relation> currentSubPointParentRelations = multiAtlas()\n                    .multifyRelations(subPoint);\n            unionOfAllParentRelations = Sets.union(unionOfAllParentRelations,\n                    currentSubPointParentRelations);\n        }\n        return unionOfAllParentRelations;\n    }\n\n    private Point getRepresentativeSubPoint()\n    {\n        return getSubPoints().getSubPoints().get(0);\n    }\n\n    private MultiAtlas multiAtlas()\n    {\n        return (MultiAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/MultiRelation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\n\nimport com.google.common.collect.Sets;\n\n/**\n * {@link Relation} made from a {@link MultiAtlas}.\n *\n * @author matthieun\n */\npublic class MultiRelation extends Relation\n{\n    private static final long serialVersionUID = 2377231271257992525L;\n\n    // Not index!\n    private final long identifier;\n\n    private SubRelationList subRelations;\n\n    protected MultiRelation(final MultiAtlas atlas, final long identifier)\n    {\n        super(atlas);\n        this.identifier = identifier;\n    }\n\n    @Override\n    public RelationMemberList allKnownOsmMembers()\n    {\n        final List<RelationMember> members = new ArrayList<>();\n        for (final Relation candidate : multiAtlas()\n                .relationAllRelationsWithSameOsmIdentifier(this.identifier))\n        {\n            candidate.members().forEach(relationMember -> members.add(relationMember));\n        }\n        return new RelationMemberList(members);\n    }\n\n    @Override\n    public List<Relation> allRelationsWithSameOsmIdentifier()\n    {\n        return multiAtlas().relationAllRelationsWithSameOsmIdentifier(this.identifier);\n    }\n\n    @Override\n    public Optional<MultiPolygon> asMultiPolygon()\n    {\n        return getSingleSubRelation().asMultiPolygon();\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        // They all should have the same tags\n        return getSingleSubRelation().getTags();\n    }\n\n    @Override\n    public RelationMemberList members()\n    {\n        // Use a TreeSet to make sure all the members are always in a deterministic order.\n        // RelationMember(s) are always ordered by member identifier.\n        final Set<RelationMember> members = new TreeSet<>();\n        for (final Relation subRelation : getSubRelations().getSubRelations())\n        {\n            final RelationMemberList subMembers = subRelation.members();\n            for (final RelationMember subMember : subMembers)\n            {\n                final AtlasEntity nonMulti = subMember.getEntity();\n                final long nonMultiIdentifier = nonMulti.getIdentifier();\n                AtlasEntity multiEntity = null;\n                if (nonMulti instanceof Node)\n                {\n                    multiEntity = multiAtlas().node(nonMultiIdentifier);\n                }\n                else if (nonMulti instanceof Edge)\n                {\n                    multiEntity = multiAtlas().edge(nonMultiIdentifier);\n                }\n                else if (nonMulti instanceof Area)\n                {\n                    multiEntity = multiAtlas().area(nonMultiIdentifier);\n                }\n                else if (nonMulti instanceof Line)\n                {\n                    multiEntity = multiAtlas().line(nonMultiIdentifier);\n                }\n                else if (nonMulti instanceof Point)\n                {\n                    multiEntity = multiAtlas().point(nonMultiIdentifier);\n                }\n                else if (nonMulti instanceof Relation)\n                {\n                    multiEntity = multiAtlas().relation(nonMultiIdentifier);\n                }\n                else\n                {\n                    throw new CoreException(\"Could not find the proper type for {}\", nonMulti);\n                }\n                if (multiEntity != null)\n                {\n                    members.add(new RelationMember(subMember.getRole(), multiEntity,\n                            subMember.getRelationIdentifier()));\n                }\n            }\n        }\n        if (members.isEmpty())\n        {\n            throw new CoreException(\n                    \"This should not happen: MultiRelation {} has no members. Its sub relations are {}.\",\n                    getIdentifier(), getSubRelations());\n        }\n        return new RelationMemberList(members);\n    }\n\n    @Override\n    public Long osmRelationIdentifier()\n    {\n        return getSingleSubRelation().osmRelationIdentifier();\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        Set<Relation> unionOfAllParentRelations = new HashSet<>();\n        for (final Relation subRelations : getSubRelations().getSubRelations())\n        {\n            final Set<Relation> currentSubRelationParentRelations = multiAtlas()\n                    .multifyRelations(subRelations);\n            unionOfAllParentRelations = Sets.union(unionOfAllParentRelations,\n                    currentSubRelationParentRelations);\n        }\n        return unionOfAllParentRelations;\n    }\n\n    private Relation getSingleSubRelation()\n    {\n        return getSubRelations().getSubRelations().get(0);\n    }\n\n    private SubRelationList getSubRelations()\n    {\n        if (this.subRelations == null)\n        {\n            this.subRelations = this.multiAtlas().subRelations(this.identifier);\n        }\n        return this.subRelations;\n    }\n\n    private MultiAtlas multiAtlas()\n    {\n        return (MultiAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/README.md",
    "content": "# `MultiAtlas`\n\nThis is a stitched `Atlas`. It is made of multiple sub-`Atlas` (which themselves can be `PackedAtlas` or `MultiAtlas`) and takes care of conflating and stitching at the intersection points.\n\n## Flyweight `MultiAtlas`\n\nWhen creating a `MultiAtlas` from multiple other `Atlas` objects, no data is copied. The `MultiAtlas` builds array references to remember for example where each available `Edge` is relative to the sub `Atlas`, but does not copy the `Edge` data. When an `Edge` is requested, it creates a `MultiEdge` that contains only its identifier and the `MultiAtlas` it belongs to. When a user asks for the `Node`s connected to that `Edge` for example, the `MultiEdge` relays the query to its `MultiAtlas` that relays the query to the appropriate sub-`Atlas` which returns the result to be relayed back to the user.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/SubAreaList.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Used by the {@link MultiArea} to hold multiple versions of identical {@link Area}s. This is in\n * case one of the {@link Area}s has a parent {@link Relation} that was not contained in one of the\n * sub-{@link Atlas}es of the containing {@link MultiAtlas}.\n *\n * @author lcram\n */\npublic class SubAreaList implements Iterable<Area>, Serializable\n{\n    private static final long serialVersionUID = -1413359659676228024L;\n\n    private final List<Area> subAreas;\n\n    SubAreaList(final List<Area> subAreas)\n    {\n        if (subAreas == null)\n        {\n            throw new CoreException(\"Cannot have a null list of sub areas.\");\n        }\n        this.subAreas = subAreas;\n    }\n\n    @Override\n    public Iterator<Area> iterator()\n    {\n        return this.subAreas.iterator();\n    }\n\n    public int size()\n    {\n        return this.subAreas.size();\n    }\n\n    List<Area> getSubAreas()\n    {\n        return this.subAreas;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/SubEdgeList.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Used by the {@link MultiEdge} to hold multiple versions of identical {@link Edge}s. This is in\n * case one of the {@link Edge}s has a parent {@link Relation} that was not contained in one of the\n * sub-{@link Atlas}es of the containing {@link MultiAtlas}.\n *\n * @author lcram\n * @author matthieun\n */\npublic class SubEdgeList implements Iterable<Edge>, Serializable\n{\n    private static final long serialVersionUID = 4338093791628259315L;\n\n    private final List<Edge> subEdges;\n\n    SubEdgeList(final List<Edge> subEdges)\n    {\n        if (subEdges == null)\n        {\n            throw new CoreException(\"Cannot have a null list of sub edges.\");\n        }\n        this.subEdges = subEdges;\n    }\n\n    public List<Edge> getSubEdges()\n    {\n        return this.subEdges;\n    }\n\n    @Override\n    public Iterator<Edge> iterator()\n    {\n        return this.subEdges.iterator();\n    }\n\n    public int size()\n    {\n        return this.subEdges.size();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/SubLineList.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Used by the {@link MultiLine} to hold multiple versions of identical {@link Line}s. This is in\n * case one of the {@link Line}s has a parent {@link Relation} that was not contained in one of the\n * sub-{@link Atlas}es of the containing {@link MultiAtlas}.\n *\n * @author lcram\n */\npublic class SubLineList implements Iterable<Line>, Serializable\n{\n    private static final long serialVersionUID = -1413359659676228024L;\n\n    private final List<Line> subLines;\n\n    SubLineList(final List<Line> subLines)\n    {\n        if (subLines == null)\n        {\n            throw new CoreException(\"Cannot have a null list of sub lines.\");\n        }\n        this.subLines = subLines;\n    }\n\n    @Override\n    public Iterator<Line> iterator()\n    {\n        return this.subLines.iterator();\n    }\n\n    public int size()\n    {\n        return this.subLines.size();\n    }\n\n    List<Line> getSubLines()\n    {\n        return this.subLines;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/SubNodeList.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Used by the {@link MultiNode} to hold multiple versions of identical {@link Node}s. This is in\n * case one of the {@link Node}s has a parent {@link Relation} that was not contained in one of the\n * sub-{@link Atlas}es of the containing {@link MultiAtlas}.\n *\n * @author matthieun\n */\npublic class SubNodeList implements Iterable<Node>, Serializable\n{\n    private static final long serialVersionUID = -1413359659676228024L;\n\n    private final List<Node> subNodes;\n\n    SubNodeList(final List<Node> subNodes)\n    {\n        if (subNodes == null)\n        {\n            throw new CoreException(\"Cannot have a null list of sub nodes.\");\n        }\n        this.subNodes = subNodes;\n    }\n\n    @Override\n    public Iterator<Node> iterator()\n    {\n        return this.subNodes.iterator();\n    }\n\n    public int size()\n    {\n        return this.subNodes.size();\n    }\n\n    List<Node> getSubNodes()\n    {\n        return this.subNodes;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/SubPointList.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Used by the {@link MultiPoint} to hold multiple versions of identical {@link Point}s. This is in\n * case one of the {@link Point}s has a parent {@link Relation} that was not contained in one of the\n * sub-{@link Atlas}es of the containing {@link MultiAtlas}.\n *\n * @author lcram\n */\npublic class SubPointList implements Iterable<Point>, Serializable\n{\n    private static final long serialVersionUID = -1413359659676228024L;\n\n    private final List<Point> subPoints;\n\n    SubPointList(final List<Point> subPoints)\n    {\n        if (subPoints == null)\n        {\n            throw new CoreException(\"Cannot have a null list of sub points.\");\n        }\n        this.subPoints = subPoints;\n    }\n\n    @Override\n    public Iterator<Point> iterator()\n    {\n        return this.subPoints.iterator();\n    }\n\n    public int size()\n    {\n        return this.subPoints.size();\n    }\n\n    List<Point> getSubPoints()\n    {\n        return this.subPoints;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/multi/SubRelationList.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * @author matthieun\n */\npublic class SubRelationList implements Iterable<Relation>, Serializable\n{\n    private static final long serialVersionUID = 8408824588171850810L;\n\n    private final List<Relation> subRelations;\n\n    SubRelationList(final List<Relation> subRelations)\n    {\n        if (subRelations == null)\n        {\n            throw new CoreException(\"Cannot have a null list of sub relations.\");\n        }\n        this.subRelations = subRelations;\n    }\n\n    @Override\n    public Iterator<Relation> iterator()\n    {\n        return this.subRelations.iterator();\n    }\n\n    public int size()\n    {\n        return this.subRelations.size();\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        final StringList relations = new StringList();\n        this.subRelations.forEach(relation -> relations.add(relation.toString()));\n        builder.append(\"[SubRelations: \");\n        builder.append(relations.join(System.lineSeparator()));\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    List<Relation> getSubRelations()\n    {\n        return this.subRelations;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedArea.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Area} from a {@link PackedAtlas}\n *\n * @author matthieun\n */\npublic class PackedArea extends Area\n{\n    private static final long serialVersionUID = 4578525310383858728L;\n\n    private final long index;\n\n    protected PackedArea(final PackedAtlas atlas, final long index)\n    {\n        super(atlas);\n        this.index = index;\n    }\n\n    @Override\n    public Polygon asPolygon()\n    {\n        return packedAtlas().areaPolygon(this.index);\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return packedAtlas().areaIdentifier(this.index);\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return packedAtlas().areaTags(this.index);\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return packedAtlas().areaRelations(this.index);\n    }\n\n    private PackedAtlas packedAtlas()\n    {\n        return (PackedAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlas.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.AbstractAtlas;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.exception.AtlasIntegrityException;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.arrays.ByteArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.arrays.LongArray;\nimport org.openstreetmap.atlas.utilities.arrays.LongArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.arrays.PolyLineArray;\nimport org.openstreetmap.atlas.utilities.arrays.PolygonArray;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.compression.IntegerDictionary;\nimport org.openstreetmap.atlas.utilities.maps.LongToLongMap;\nimport org.openstreetmap.atlas.utilities.maps.LongToLongMultiMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Atlas that packs data in large arrays\n *\n * @author matthieun\n */\npublic final class PackedAtlas extends AbstractAtlas\n{\n    /**\n     * Serialization format settings for an {@link Atlas}. While the serialization interface for\n     * saving is well-defined by the {@link Atlas}, the actual serialization mechanics - as well as\n     * the interface for loading - are left up to the discretion of the implementing {@link Atlas}\n     * subclass.\n     *\n     * @author lcram\n     */\n    public enum AtlasSerializationFormat\n    {\n        PROTOBUF,\n        JAVA\n    }\n\n    // Keep track of the field names for reflection code in the Serializer.\n    protected static final String FIELD_PREFIX = \"FIELD_\";\n    protected static final String FIELD_BOUNDS = \"bounds\";\n    protected static final String FIELD_LOGGER = \"logger\";\n    protected static final String FIELD_SERIAL_VERSION_UID = \"serialVersionUID\";\n    protected static final String FIELD_SERIALIZER = \"serializer\";\n    protected static final String FIELD_SAVE_SERIALIZATION_FORMAT = \"saveSerializationFormat\";\n    protected static final String FIELD_LOAD_SERIALIZATION_FORMAT = \"loadSerializationFormat\";\n    protected static final String FIELD_CONTAINS_ENHANCED_RELATION_GEOMETRY = \"containsEnhancedRelationGeometry\";\n    protected static final String FIELD_META_DATA = \"metaData\";\n    protected static final String FIELD_DICTIONARY = \"dictionary\";\n    private transient Object fieldDictionaryLock = new Object();\n    protected static final String FIELD_EDGE_IDENTIFIERS = \"edgeIdentifiers\";\n    private transient Object fieldEdgeIdentifiersLock = new Object();\n    protected static final String FIELD_NODE_IDENTIFIERS = \"nodeIdentifiers\";\n    private transient Object fieldNodeIdentifiersLock = new Object();\n    protected static final String FIELD_AREA_IDENTIFIERS = \"areaIdentifiers\";\n    private transient Object fieldAreaIdentifiersLock = new Object();\n    protected static final String FIELD_LINE_IDENTIFIERS = \"lineIdentifiers\";\n    private transient Object fieldLineIdentifiersLock = new Object();\n    protected static final String FIELD_POINT_IDENTIFIERS = \"pointIdentifiers\";\n    private transient Object fieldPointIdentifiersLock = new Object();\n    protected static final String FIELD_RELATION_IDENTIFIERS = \"relationIdentifiers\";\n    private transient Object fieldRelationIdentifiersLock = new Object();\n    protected static final String FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX = \"edgeIdentifierToEdgeArrayIndex\";\n    private transient Object fieldEdgeIdentifierToEdgeArrayIndexLock = new Object();\n    protected static final String FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX = \"nodeIdentifierToNodeArrayIndex\";\n    private transient Object fieldNodeIdentifierToNodeArrayIndexLock = new Object();\n    protected static final String FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX = \"areaIdentifierToAreaArrayIndex\";\n    private transient Object fieldAreaIdentifierToAreaArrayIndexLock = new Object();\n    protected static final String FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX = \"lineIdentifierToLineArrayIndex\";\n    private transient Object fieldLineIdentifierToLineArrayIndexLock = new Object();\n    protected static final String FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX = \"pointIdentifierToPointArrayIndex\";\n    private transient Object fieldPointIdentifierToPointArrayIndexLock = new Object();\n    protected static final String FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX = \"relationIdentifierToRelationArrayIndex\";\n    private transient Object fieldRelationIdentifierToRelationArrayIndexLock = new Object();\n    protected static final String FIELD_NODE_LOCATIONS = \"nodeLocations\";\n    private transient Object fieldNodeLocationsLock = new Object();\n    protected static final String FIELD_NODE_IN_EDGES_INDICES = \"nodeInEdgesIndices\";\n    private transient Object fieldNodeInEdgesIndicesLock = new Object();\n    protected static final String FIELD_NODE_OUT_EDGES_INDICES = \"nodeOutEdgesIndices\";\n    private transient Object fieldNodeOutEdgesIndicesLock = new Object();\n    protected static final String FIELD_NODE_TAGS = \"nodeTags\";\n    private transient Object fieldNodeTagsLock = new Object();\n    protected static final String FIELD_NODE_INDEX_TO_RELATION_INDICES = \"nodeIndexToRelationIndices\";\n    private transient Object fieldNodeIndexToRelationIndicesLock = new Object();\n    protected static final String FIELD_EDGE_START_NODE_INDEX = \"edgeStartNodeIndex\";\n    private transient Object fieldEdgeStartNodeIndexLock = new Object();\n    protected static final String FIELD_EDGE_END_NODE_INDEX = \"edgeEndNodeIndex\";\n    private transient Object fieldEdgeEndNodeIndexLock = new Object();\n    protected static final String FIELD_EDGE_POLY_LINES = \"edgePolyLines\";\n    private transient Object fieldEdgePolyLinesLock = new Object();\n    protected static final String FIELD_EDGE_TAGS = \"edgeTags\";\n    private transient Object fieldEdgeTagsLock = new Object();\n    protected static final String FIELD_EDGE_INDEX_TO_RELATION_INDICES = \"edgeIndexToRelationIndices\";\n    private transient Object fieldEdgeIndexToRelationIndicesLock = new Object();\n    protected static final String FIELD_AREA_POLYGONS = \"areaPolygons\";\n    private transient Object fieldAreaPolygonsLock = new Object();\n    protected static final String FIELD_AREA_TAGS = \"areaTags\";\n    private transient Object fieldAreaTagsLock = new Object();\n    protected static final String FIELD_AREA_INDEX_TO_RELATION_INDICES = \"areaIndexToRelationIndices\";\n    private transient Object fieldAreaIndexToRelationIndicesLock = new Object();\n    protected static final String FIELD_LINE_POLYLINES = \"linePolyLines\";\n    private transient Object fieldLinePolyLinesLock = new Object();\n    protected static final String FIELD_LINE_TAGS = \"lineTags\";\n    private transient Object fieldLineTagsLock = new Object();\n    protected static final String FIELD_LINE_INDEX_TO_RELATION_INDICES = \"lineIndexToRelationIndices\";\n    private transient Object fieldLindIndexToRelationIndicesLock = new Object();\n    protected static final String FIELD_POINT_LOCATIONS = \"pointLocations\";\n    private transient Object fieldPointLocationsLock = new Object();\n    protected static final String FIELD_POINT_TAGS = \"pointTags\";\n    private transient Object fieldPointTagsLock = new Object();\n    protected static final String FIELD_POINT_INDEX_TO_RELATION_INDICES = \"pointIndexToRelationIndices\";\n    private transient Object fieldPointIndexToRelationIndicesLock = new Object();\n    protected static final String FIELD_RELATION_MEMBERS_INDICES = \"relationMemberIndices\";\n    private transient Object fieldRelationMembersIndicesLock = new Object();\n    protected static final String FIELD_RELATION_MEMBER_TYPES = \"relationMemberTypes\";\n    private transient Object fieldRelationMemberTypesLock = new Object();\n    protected static final String FIELD_RELATION_MEMBER_ROLES = \"relationMemberRoles\";\n    private transient Object fieldRelationMemberRolesLock = new Object();\n    protected static final String FIELD_RELATION_TAGS = \"relationTags\";\n    private transient Object fieldRelationTagsLock = new Object();\n    protected static final String FIELD_RELATION_INDEX_TO_RELATION_INDICES = \"relationIndexToRelationIndices\";\n    private transient Object fieldRelationIndexToRelationIndicesLock = new Object();\n    protected static final String FIELD_RELATION_OSM_IDENTIFIER_TO_RELATION_IDENTIFIERS = \"relationOsmIdentifierToRelationIdentifiers\";\n    private transient Object fieldRelationOsmIdentifierToRelationIdentifiersLock = new Object();\n    protected static final String FIELD_RELATION_OSM_IDENTIFIERS = \"relationOsmIdentifiers\";\n    private transient Object fieldRelationOsmIdentifiersLock = new Object();\n    protected static final String FIELD_RELATION_GEOMETRIES = \"relationGeometries\";\n    private transient Object fieldRelationGeometriesLock = new Object();\n    protected static final String FIELD_BUILT_RELATION_GEOMETRIES = \"builtRelationGeometries\";\n\n    private static final long serialVersionUID = -7582554057580336684L;\n    private static final Logger logger = LoggerFactory.getLogger(PackedAtlas.class);\n\n    // Serializer.\n    private transient PackedAtlasSerializer serializer;\n\n    // Serialization formats for saving/loading this PackedAtlas\n    private AtlasSerializationFormat saveSerializationFormat = AtlasSerializationFormat.PROTOBUF;\n    private AtlasSerializationFormat loadSerializationFormat = AtlasSerializationFormat.PROTOBUF;\n    private boolean containsEnhancedRelationGeometry = false;\n\n    // Meta-Data\n    private AtlasMetaData metaData = new AtlasMetaData();\n\n    // Dictionary\n    private final IntegerDictionary<String> dictionary;\n\n    // The OSM (and way-sectioned) edge and node indices\n    private final LongArray edgeIdentifiers;\n    private final LongArray nodeIdentifiers;\n    private final LongArray areaIdentifiers;\n    private final LongArray lineIdentifiers;\n    private final LongArray pointIdentifiers;\n    private final LongArray relationIdentifiers;\n\n    // The maps from edge index to index in the arrays above, and in the attributes\n    private final LongToLongMap edgeIdentifierToEdgeArrayIndex;\n    private final LongToLongMap nodeIdentifierToNodeArrayIndex;\n    private final LongToLongMap areaIdentifierToAreaArrayIndex;\n    private final LongToLongMap lineIdentifierToLineArrayIndex;\n    private final LongToLongMap pointIdentifierToPointArrayIndex;\n    private final LongToLongMap relationIdentifierToRelationArrayIndex;\n\n    // Node attributes\n    private final LongArray nodeLocations;\n    private final LongArrayOfArrays nodeInEdgesIndices;\n    private final LongArrayOfArrays nodeOutEdgesIndices;\n    private final PackedTagStore nodeTags;\n    private final LongToLongMultiMap nodeIndexToRelationIndices;\n\n    // Edge attributes\n    private final LongArray edgeStartNodeIndex;\n    private final LongArray edgeEndNodeIndex;\n    private final PolyLineArray edgePolyLines;\n    private final PackedTagStore edgeTags;\n    private final LongToLongMultiMap edgeIndexToRelationIndices;\n\n    // Areas attributes\n    private final PolygonArray areaPolygons;\n    private final PackedTagStore areaTags;\n    private final LongToLongMultiMap areaIndexToRelationIndices;\n\n    // Line attributes\n    private final PolyLineArray linePolyLines;\n    private final PackedTagStore lineTags;\n    private final LongToLongMultiMap lineIndexToRelationIndices;\n\n    // Point attributes\n    private final LongArray pointLocations;\n    private final PackedTagStore pointTags;\n    private final LongToLongMultiMap pointIndexToRelationIndices;\n\n    // Relation attributes\n    private final LongArrayOfArrays relationMemberIndices;\n    private final ByteArrayOfArrays relationMemberTypes;\n    private final IntegerArrayOfArrays relationMemberRoles;\n    private final PackedTagStore relationTags;\n    private final LongToLongMultiMap relationIndexToRelationIndices;\n    private final LongToLongMultiMap relationOsmIdentifierToRelationIdentifiers;\n    private final LongArray relationOsmIdentifiers;\n    private ByteArrayOfArrays relationGeometries;\n    private transient Map<Long, MultiPolygon> builtRelationGeometries = new HashMap<>();\n\n    // Bounds of the Atlas\n    private Rectangle bounds;\n\n    /**\n     * Clone an {@link Atlas} into a {@link PackedAtlas}\n     *\n     * @param other\n     *            The {@link Atlas} to clone\n     * @return The cloned {@link PackedAtlas}\n     */\n    public static PackedAtlas cloneFrom(final Atlas other)\n    {\n        return new PackedAtlasCloner().cloneFrom(other);\n    }\n\n    /**\n     * Load a {@link PackedAtlas} from a zip entry resource\n     *\n     * @param resource\n     *            The {@link Resource} to read from\n     * @return The deserialized {@link PackedAtlas}\n     */\n    public static PackedAtlas load(final Resource resource)\n    {\n        final PackedAtlas result = PackedAtlasSerializer.load(resource);\n        result.setName(resource.getName());\n        return result;\n    }\n\n    /**\n     * This constructor is used only by the serializer.\n     */\n    protected PackedAtlas()\n    {\n        this.metaData = null;\n        this.dictionary = null;\n        this.edgeIdentifiers = null;\n        this.nodeIdentifiers = null;\n        this.areaIdentifiers = null;\n        this.lineIdentifiers = null;\n        this.pointIdentifiers = null;\n        this.relationIdentifiers = null;\n        this.edgeIdentifierToEdgeArrayIndex = null;\n        this.nodeIdentifierToNodeArrayIndex = null;\n        this.areaIdentifierToAreaArrayIndex = null;\n        this.lineIdentifierToLineArrayIndex = null;\n        this.pointIdentifierToPointArrayIndex = null;\n        this.relationIdentifierToRelationArrayIndex = null;\n        this.nodeLocations = null;\n        this.nodeInEdgesIndices = null;\n        this.nodeOutEdgesIndices = null;\n        this.nodeTags = null;\n        this.nodeIndexToRelationIndices = null;\n        this.edgeStartNodeIndex = null;\n        this.edgeEndNodeIndex = null;\n        this.edgePolyLines = null;\n        this.edgeTags = null;\n        this.edgeIndexToRelationIndices = null;\n        this.areaPolygons = null;\n        this.areaTags = null;\n        this.areaIndexToRelationIndices = null;\n        this.linePolyLines = null;\n        this.lineTags = null;\n        this.lineIndexToRelationIndices = null;\n        this.pointLocations = null;\n        this.pointTags = null;\n        this.pointIndexToRelationIndices = null;\n        this.relationMemberIndices = null;\n        this.relationMemberTypes = null;\n        this.relationMemberRoles = null;\n        this.relationTags = null;\n        this.relationIndexToRelationIndices = null;\n        this.relationOsmIdentifierToRelationIdentifiers = null;\n        this.relationOsmIdentifiers = null;\n        this.relationGeometries = null;\n    }\n\n    /**\n     * Construct an Atlas\n     *\n     * @param estimates\n     *            The size estimates\n     */\n    protected PackedAtlas(final AtlasSize estimates)\n    {\n        this(estimates, false);\n    }\n\n    protected PackedAtlas(final AtlasSize estimates, final boolean containsEnhancedRelationGeometry)\n    {\n        final long edgeNumberEstimate = estimates.getEdgeNumber();\n        final long nodeNumberEstimate = estimates.getNodeNumber();\n        final long areaNumberEstimate = estimates.getAreaNumber();\n        final long lineNumberEstimate = estimates.getLineNumber();\n        final long pointNumberEstimate = estimates.getPointNumber();\n        final long relationNumberEstimate = estimates.getRelationNumber();\n\n        final int subArraySize = Integer.MAX_VALUE;\n        final long maximumSize = Long.MAX_VALUE;\n\n        final int edgeMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                edgeNumberEstimate % Integer.MAX_VALUE);\n        final int nodeMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                nodeNumberEstimate % Integer.MAX_VALUE);\n        final int areaMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                areaNumberEstimate % Integer.MAX_VALUE);\n        final int lineMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                lineNumberEstimate % Integer.MAX_VALUE);\n        final int pointMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                pointNumberEstimate % Integer.MAX_VALUE);\n        final int relationMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,\n                relationNumberEstimate % Integer.MAX_VALUE);\n\n        final int edgeHashSize = (int) Math\n                .max(Math.min(edgeNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        final int nodeHashSize = (int) Math\n                .max(Math.min(nodeNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        final int areaHashSize = (int) Math\n                .max(Math.min(areaNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        final int lineHashSize = (int) Math\n                .max(Math.min(lineNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        final int pointHashSize = (int) Math\n                .max(Math.min(pointNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n        final int relationHashSize = (int) Math\n                .max(Math.min(relationNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);\n\n        this.dictionary = new IntegerDictionary<>();\n\n        this.edgeIdentifiers = new LongArray(maximumSize, edgeMemoryBlockSize, subArraySize);\n        this.nodeIdentifiers = new LongArray(maximumSize, nodeMemoryBlockSize, subArraySize);\n        this.areaIdentifiers = new LongArray(maximumSize, areaMemoryBlockSize, subArraySize);\n        this.lineIdentifiers = new LongArray(maximumSize, lineMemoryBlockSize, subArraySize);\n        this.pointIdentifiers = new LongArray(maximumSize, pointMemoryBlockSize, subArraySize);\n        this.relationIdentifiers = new LongArray(maximumSize, relationMemoryBlockSize,\n                subArraySize);\n\n        this.edgeIdentifierToEdgeArrayIndex = new LongToLongMap(\n                \"PackedAtlas - edgeIdentifierToEdgeArrayIndex\", maximumSize, edgeHashSize,\n                edgeMemoryBlockSize, subArraySize, edgeMemoryBlockSize, subArraySize);\n        this.nodeIdentifierToNodeArrayIndex = new LongToLongMap(\n                \"PackedAtlas - nodeIdentifierToNodeArrayIndex\", maximumSize, nodeHashSize,\n                nodeMemoryBlockSize, subArraySize, nodeMemoryBlockSize, subArraySize);\n        this.areaIdentifierToAreaArrayIndex = new LongToLongMap(\n                \"PackedAtlas - areaIdentifierToAreaArrayIndex\", maximumSize, areaHashSize,\n                areaMemoryBlockSize, subArraySize, areaMemoryBlockSize, subArraySize);\n        this.lineIdentifierToLineArrayIndex = new LongToLongMap(\n                \"PackedAtlas - lineIdentifierToLineArrayIndex\", maximumSize, lineHashSize,\n                lineMemoryBlockSize, subArraySize, lineMemoryBlockSize, subArraySize);\n        this.pointIdentifierToPointArrayIndex = new LongToLongMap(\n                \"PackedAtlas - pointIdentifierToPointArrayIndex\", maximumSize, pointHashSize,\n                pointMemoryBlockSize, subArraySize, pointMemoryBlockSize, subArraySize);\n        this.relationIdentifierToRelationArrayIndex = new LongToLongMap(\n                \"PackedAtlas - relationIdentifierToRelationArrayIndex\", maximumSize,\n                relationHashSize, relationMemoryBlockSize, subArraySize, relationMemoryBlockSize,\n                subArraySize);\n\n        this.nodeInEdgesIndices = new LongArrayOfArrays(subArraySize, nodeMemoryBlockSize,\n                subArraySize);\n        this.nodeOutEdgesIndices = new LongArrayOfArrays(subArraySize, nodeMemoryBlockSize,\n                subArraySize);\n        this.nodeLocations = new LongArray(maximumSize, nodeMemoryBlockSize, subArraySize);\n        this.nodeTags = new PackedTagStore(maximumSize, nodeMemoryBlockSize, subArraySize,\n                dictionary());\n        this.nodeIndexToRelationIndices = new LongToLongMultiMap(\n                \"PackedAtlas - nodeIndexToRelationIndices\", maximumSize, nodeHashSize,\n                nodeMemoryBlockSize, subArraySize, nodeMemoryBlockSize, nodeHashSize);\n\n        this.edgeStartNodeIndex = new LongArray(maximumSize, edgeMemoryBlockSize, subArraySize);\n        this.edgeEndNodeIndex = new LongArray(maximumSize, edgeMemoryBlockSize, subArraySize);\n        this.edgePolyLines = new PolyLineArray(maximumSize, edgeMemoryBlockSize, subArraySize);\n        this.edgeTags = new PackedTagStore(maximumSize, edgeMemoryBlockSize, subArraySize,\n                dictionary());\n        this.edgeIndexToRelationIndices = new LongToLongMultiMap(\n                \"PackedAtlas - edgeIndexToRelationIndices\", maximumSize, edgeHashSize,\n                edgeMemoryBlockSize, subArraySize, edgeMemoryBlockSize, edgeHashSize);\n\n        this.areaPolygons = new PolygonArray(maximumSize, areaMemoryBlockSize, subArraySize);\n        this.areaTags = new PackedTagStore(maximumSize, areaMemoryBlockSize, subArraySize,\n                dictionary());\n        this.areaIndexToRelationIndices = new LongToLongMultiMap(\n                \"PackedAtlas - areaIndexToRelationIndices\", maximumSize, areaHashSize,\n                areaMemoryBlockSize, subArraySize, areaMemoryBlockSize, areaHashSize);\n\n        this.linePolyLines = new PolyLineArray(maximumSize, lineMemoryBlockSize, subArraySize);\n        this.lineTags = new PackedTagStore(maximumSize, lineMemoryBlockSize, subArraySize,\n                dictionary());\n        this.lineIndexToRelationIndices = new LongToLongMultiMap(\n                \"PackedAtlas - lineIndexToRelationIndices\", maximumSize, lineHashSize,\n                lineMemoryBlockSize, subArraySize, lineMemoryBlockSize, lineHashSize);\n\n        this.pointLocations = new LongArray(maximumSize, pointMemoryBlockSize, subArraySize);\n        this.pointTags = new PackedTagStore(maximumSize, pointMemoryBlockSize, subArraySize,\n                dictionary());\n        this.pointIndexToRelationIndices = new LongToLongMultiMap(\n                \"PackedAtlas - pointIndexToRelationIndices\", maximumSize, pointHashSize,\n                pointMemoryBlockSize, subArraySize, pointMemoryBlockSize, pointHashSize);\n\n        this.relationMemberIndices = new LongArrayOfArrays(maximumSize, relationMemoryBlockSize,\n                subArraySize);\n        this.relationMemberTypes = new ByteArrayOfArrays(maximumSize, relationMemoryBlockSize,\n                subArraySize);\n        this.relationMemberRoles = new IntegerArrayOfArrays(maximumSize, relationMemoryBlockSize,\n                subArraySize);\n        this.relationTags = new PackedTagStore(maximumSize, relationMemoryBlockSize, subArraySize,\n                dictionary());\n        this.relationIndexToRelationIndices = new LongToLongMultiMap(\n                \"PackedAtlas - relationIndexToRelationIndices\", maximumSize, relationHashSize,\n                relationMemoryBlockSize, subArraySize, relationMemoryBlockSize, relationHashSize);\n        this.relationOsmIdentifierToRelationIdentifiers = new LongToLongMultiMap(\n                \"PackedAtlas - relationOsmIdentifierToRelationIdentifier\", maximumSize,\n                relationHashSize, relationMemoryBlockSize, subArraySize, relationMemoryBlockSize,\n                subArraySize);\n        this.relationOsmIdentifiers = new LongArray(maximumSize, relationMemoryBlockSize,\n                subArraySize);\n\n        this.edgeIdentifiers.setName(\"PackedAtlas - edgeIdentifiers\");\n        this.edgeStartNodeIndex.setName(\"PackedAtlas - edgeStartNodeIndex\");\n        this.edgeEndNodeIndex.setName(\"PackedAtlas - edgeEndNodeIndex\");\n        this.edgePolyLines.setName(\"PackedAtlas - edgePolyLines\");\n\n        this.nodeIdentifiers.setName(\"PackedAtlas - nodeIdentifiers\");\n        this.nodeInEdgesIndices.setName(\"PackedAtlas - nodeInEdgesIndices\");\n        this.nodeOutEdgesIndices.setName(\"PackedAtlas - nodeOutEdgesIndices\");\n        this.nodeLocations.setName(\"PackedAtlas - nodeLocations\");\n\n        this.areaIdentifiers.setName(\"PackedAtlas - areaIdentifiers\");\n        this.areaPolygons.setName(\"PackedAtlas - areaPolygons\");\n\n        this.lineIdentifiers.setName(\"PackedAtlas - lineIdentifiers\");\n        this.linePolyLines.setName(\"PackedAtlas - linePolyLines\");\n\n        this.pointIdentifiers.setName(\"PackedAtlas - pointIdentifiers\");\n        this.pointLocations.setName(\"PackedAtlas - pointLocations\");\n\n        this.relationIdentifiers.setName(\"PackedAtlas - relationIdentifiers\");\n        this.relationMemberIndices.setName(\"PackedAtlas - relationMemberIndices\");\n        this.relationMemberTypes.setName(\"PackedAtlas - relationMemberTypes\");\n        this.relationMemberRoles.setName(\"PackedAtlas - relationMemberRoles\");\n        this.relationOsmIdentifiers.setName(\"PackedAtlas - relationOsmIdentifiers\");\n\n        if (containsEnhancedRelationGeometry)\n        {\n            this.containsEnhancedRelationGeometry = containsEnhancedRelationGeometry;\n            this.relationGeometries = new ByteArrayOfArrays(maximumSize, relationMemoryBlockSize,\n                    subArraySize);\n            this.relationGeometries.setName(\"PackedAtlas - relationGeometries\");\n        }\n    }\n\n    @Override\n    public Area area(final long identifier)\n    {\n        if (this.areaIdentifierToAreaArrayIndex().containsKey(identifier))\n        {\n            return new PackedArea(this, this.areaIdentifierToAreaArrayIndex().get(identifier));\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Area> areas()\n    {\n        return Iterables.indexBasedIterable(this.areaIdentifiers().size(),\n                index -> new PackedArea(this, index));\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        if (this.bounds == null)\n        {\n            final Iterable<AtlasEntity> boundedEntities = Iterables.filter(this,\n                    entity -> entity.bounds() != null);\n            this.bounds = Rectangle.forLocated(boundedEntities);\n        }\n        return this.bounds;\n    }\n\n    public boolean containsEnhancedRelationGeometry()\n    {\n        return this.containsEnhancedRelationGeometry;\n    }\n\n    @Override\n    public Edge edge(final long identifier)\n    {\n        if (this.edgeIdentifierToEdgeArrayIndex().containsKey(identifier))\n        {\n            return new PackedEdge(this, this.edgeIdentifierToEdgeArrayIndex().get(identifier));\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Edge> edges()\n    {\n        return Iterables.indexBasedIterable(this.edgeIdentifiers().size(),\n                index -> new PackedEdge(this, index));\n    }\n\n    /**\n     * Get the serialization format used for saving this {@link PackedAtlas}. By default use Java\n     * serialization.\n     *\n     * @return The serialization format setting\n     */\n    public AtlasSerializationFormat getSaveSerializationFormat()\n    {\n        return this.saveSerializationFormat;\n    }\n\n    /**\n     * Get the serialization format with which this atlas was loaded.\n     *\n     * @return the format\n     */\n    public AtlasSerializationFormat getSerializationFormat()\n    {\n        return this.loadSerializationFormat;\n    }\n\n    @Override\n    public Line line(final long identifier)\n    {\n        if (this.lineIdentifierToLineArrayIndex().containsKey(identifier))\n        {\n            return new PackedLine(this, this.lineIdentifierToLineArrayIndex().get(identifier));\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Line> lines()\n    {\n        return Iterables.indexBasedIterable(this.lineIdentifiers().size(),\n                index -> new PackedLine(this, index));\n    }\n\n    @Override\n    public AtlasMetaData metaData()\n    {\n        if (this.metaData == null)\n        {\n            this.serializer.deserializeIfNeeded(FIELD_META_DATA);\n        }\n        return this.metaData;\n    }\n\n    @Override\n    public Node node(final long identifier)\n    {\n        if (this.nodeIdentifierToNodeArrayIndex().containsKey(identifier))\n        {\n            return new PackedNode(this, this.nodeIdentifierToNodeArrayIndex().get(identifier));\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Node> nodes()\n    {\n        return Iterables.indexBasedIterable(this.nodeIdentifiers().size(),\n                index -> new PackedNode(this, index));\n    }\n\n    @Override\n    public long numberOfAreas()\n    {\n        return this.areaIdentifiers().size();\n    }\n\n    @Override\n    public long numberOfEdges()\n    {\n        return this.edgeIdentifiers().size();\n    }\n\n    @Override\n    public long numberOfLines()\n    {\n        return this.lineIdentifiers().size();\n    }\n\n    @Override\n    public long numberOfNodes()\n    {\n        return this.nodeIdentifiers().size();\n    }\n\n    @Override\n    public long numberOfPoints()\n    {\n        return this.pointIdentifiers().size();\n    }\n\n    @Override\n    public long numberOfRelations()\n    {\n        return this.relationIdentifiers().size();\n    }\n\n    @Override\n    public Point point(final long identifier)\n    {\n        if (this.pointIdentifierToPointArrayIndex().containsKey(identifier))\n        {\n            return new PackedPoint(this, this.pointIdentifierToPointArrayIndex().get(identifier));\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Point> points()\n    {\n        return Iterables.indexBasedIterable(this.pointIdentifiers().size(),\n                index -> new PackedPoint(this, index));\n    }\n\n    @Override\n    public Relation relation(final long identifier)\n    {\n        if (this.relationIdentifierToRelationArrayIndex().containsKey(identifier))\n        {\n            return new PackedRelation(this,\n                    this.relationIdentifierToRelationArrayIndex().get(identifier));\n        }\n        return null;\n    }\n\n    @Override\n    public Iterable<Relation> relations()\n    {\n        return Iterables.indexBasedIterable(this.relationIdentifiers().size(),\n                index -> new PackedRelation(this, index));\n    }\n\n    @Override\n    public void save(final WritableResource writableResource)\n    {\n        new PackedAtlasSerializer(this, writableResource).save();\n    }\n\n    /**\n     * Set the serialization format for saving this {@link PackedAtlas}.\n     *\n     * @param format\n     *            The format to use\n     */\n    public void setSaveSerializationFormat(final AtlasSerializationFormat format)\n    {\n        this.saveSerializationFormat = format;\n        if (this.saveSerializationFormat != AtlasSerializationFormat.PROTOBUF)\n        {\n            this.containsEnhancedRelationGeometry = false;\n        }\n    }\n\n    /**\n     * Trim this Atlas' arrays with the proper size. WARNING! This could potentially temporarily\n     * double the amount of memory used by each array.\n     */\n    public void trim()\n    {\n        logger.info(\"Trimming Atlas {} to save on space.\", this.getName());\n        final Time start = Time.now();\n\n        this.edgeIdentifiers.trim();\n        this.nodeIdentifiers.trim();\n        this.areaIdentifiers.trim();\n        this.lineIdentifiers.trim();\n        this.pointIdentifiers.trim();\n        this.relationIdentifiers.trim();\n\n        this.edgeIdentifierToEdgeArrayIndex.trim();\n        this.nodeIdentifierToNodeArrayIndex.trim();\n        this.areaIdentifierToAreaArrayIndex.trim();\n        this.lineIdentifierToLineArrayIndex.trim();\n        this.pointIdentifierToPointArrayIndex.trim();\n        this.relationIdentifierToRelationArrayIndex.trim();\n\n        this.nodeLocations.trim();\n        this.nodeInEdgesIndices.trim();\n        this.nodeOutEdgesIndices.trim();\n        this.nodeTags.trim();\n        this.nodeIndexToRelationIndices.trim();\n\n        this.edgeStartNodeIndex.trim();\n        this.edgeEndNodeIndex.trim();\n        this.edgePolyLines.trim();\n        this.edgeTags.trim();\n        this.edgeIndexToRelationIndices.trim();\n\n        this.areaPolygons.trim();\n        this.areaTags.trim();\n        this.areaIndexToRelationIndices.trim();\n\n        this.linePolyLines.trim();\n        this.lineTags.trim();\n        this.lineIndexToRelationIndices.trim();\n\n        this.pointLocations.trim();\n        this.pointTags.trim();\n        this.pointIndexToRelationIndices.trim();\n\n        this.relationMemberIndices.trim();\n        this.relationMemberTypes.trim();\n        this.relationMemberRoles.trim();\n        this.relationTags.trim();\n        this.relationIndexToRelationIndices.trim();\n        this.relationOsmIdentifierToRelationIdentifiers.trim();\n        this.relationOsmIdentifiers.trim();\n        if (this.containsEnhancedRelationGeometry)\n        {\n            this.relationGeometries.trim();\n        }\n\n        logger.info(\"Trimmed Atlas {} in {}.\", this.getName(), start.elapsedSince());\n    }\n\n    protected void addArea(final long areaIdentifier, final Polygon polygon,\n            final Map<String, String> tags)\n    {\n        synchronized (this.areaIdentifiers)\n        {\n            if (this.areaIdentifierToAreaArrayIndex.containsKey(areaIdentifier))\n            {\n                throw new AtlasIntegrityException(\n                        PackedAtlasLogMessages.ALREADY_EXISTS_EXCEPTION_MESSAGE, ItemType.AREA,\n                        areaIdentifier);\n            }\n            final long index = this.areaIdentifiers.size();\n            this.areaIdentifiers.add(areaIdentifier);\n            this.areaIdentifierToAreaArrayIndex.put(areaIdentifier, index);\n\n            this.areaPolygons.add(polygon);\n\n            this.getAsNewAreaSpatialIndex().add(new PackedArea(this, index));\n\n            // Tags\n            updatePackedTagStore(this.areaTags, index, tags);\n        }\n    }\n\n    protected void addEdge(final long edgeIdentifier, final long startNodeIdentifier,\n            final long endNodeIdentifier, final PolyLine polyline, final Map<String, String> tags)\n    {\n        synchronized (this.edgeIdentifiers)\n        {\n            if (this.edgeIdentifierToEdgeArrayIndex.containsKey(edgeIdentifier))\n            {\n                throw new AtlasIntegrityException(\n                        PackedAtlasLogMessages.ALREADY_EXISTS_EXCEPTION_MESSAGE, ItemType.EDGE,\n                        edgeIdentifier);\n            }\n            final long index = this.edgeIdentifiers.size();\n            this.edgeIdentifiers.add(edgeIdentifier);\n            this.edgeIdentifierToEdgeArrayIndex.put(edgeIdentifier, index);\n\n            this.edgePolyLines.add(polyline);\n\n            // Start Node\n            final long startNodeIndex = this.nodeIdentifierToNodeArrayIndex\n                    .get(startNodeIdentifier);\n            this.edgeStartNodeIndex.add(startNodeIndex);\n\n            // End Node\n            final long endNodeIndex = this.nodeIdentifierToNodeArrayIndex.get(endNodeIdentifier);\n            this.edgeEndNodeIndex.add(endNodeIndex);\n\n            // Node In edges\n            updateNodeEdgesReference(endNodeIndex, this.nodeInEdgesIndices, index);\n\n            // Node Out edges\n            updateNodeEdgesReference(startNodeIndex, this.nodeOutEdgesIndices, index);\n\n            // Spatial Index\n            this.getAsNewEdgeSpatialIndex().add(new PackedEdge(this, index));\n\n            // Tags\n            updatePackedTagStore(this.edgeTags, index, tags);\n        }\n    }\n\n    protected void addLine(final long lineIdentifier, final PolyLine polyline,\n            final Map<String, String> tags)\n    {\n        synchronized (this.lineIdentifiers)\n        {\n            if (this.lineIdentifierToLineArrayIndex.containsKey(lineIdentifier))\n            {\n                throw new AtlasIntegrityException(\n                        PackedAtlasLogMessages.ALREADY_EXISTS_EXCEPTION_MESSAGE, ItemType.LINE,\n                        lineIdentifier);\n            }\n            final long index = this.lineIdentifiers.size();\n            this.lineIdentifiers.add(lineIdentifier);\n            this.lineIdentifierToLineArrayIndex.put(lineIdentifier, index);\n\n            this.linePolyLines.add(polyline);\n\n            this.getAsNewLineSpatialIndex().add(new PackedLine(this, index));\n\n            // Tags\n            updatePackedTagStore(this.lineTags, index, tags);\n        }\n    }\n\n    protected void addNode(final long nodeIdentifier, final Location location,\n            final Map<String, String> tags)\n    {\n        synchronized (this.nodeIdentifiers)\n        {\n            if (this.nodeIdentifierToNodeArrayIndex.containsKey(nodeIdentifier))\n            {\n                throw new AtlasIntegrityException(\n                        PackedAtlasLogMessages.ALREADY_EXISTS_EXCEPTION_MESSAGE, ItemType.NODE,\n                        nodeIdentifier);\n            }\n            final long index = this.nodeIdentifiers.size();\n            this.nodeIdentifiers.add(nodeIdentifier);\n            this.nodeIdentifierToNodeArrayIndex.put(nodeIdentifier, index);\n\n            this.nodeLocations.add(location.asConcatenation());\n\n            // Fill the in/out edges arrays for later\n            this.nodeInEdgesIndices.add(new long[0]);\n            this.nodeOutEdgesIndices.add(new long[0]);\n\n            this.getAsNewNodeSpatialIndex().add(new PackedNode(this, index));\n\n            // Tags\n            updatePackedTagStore(this.nodeTags, index, tags);\n        }\n    }\n\n    protected void addPoint(final long pointIdentifier, final Location location,\n            final Map<String, String> tags)\n    {\n        synchronized (this.pointIdentifiers)\n        {\n            if (this.pointIdentifierToPointArrayIndex.containsKey(pointIdentifier))\n            {\n                throw new AtlasIntegrityException(\n                        PackedAtlasLogMessages.ALREADY_EXISTS_EXCEPTION_MESSAGE, ItemType.POINT,\n                        pointIdentifier);\n            }\n            final long index = this.pointIdentifiers.size();\n            this.pointIdentifiers.add(pointIdentifier);\n            this.pointIdentifierToPointArrayIndex.put(pointIdentifier, index);\n\n            this.pointLocations.add(location.asConcatenation());\n\n            this.getAsNewPointSpatialIndex().add(new PackedPoint(this, index));\n\n            // Tags\n            updatePackedTagStore(this.pointTags, index, tags);\n        }\n    }\n\n    /**\n     * Add a relation to the {@link PackedAtlas}. WARNING: This method will throw\n     * {@link AtlasIntegrityException}s in the following cases:\n     * <ul>\n     * <li>The identifiers list, types list and roles list do not have all the same length\n     * <li>The identifiers list, types list and roles list are empty\n     * <li>Some member identifiers are null\n     * </ul>\n     *\n     * @param relationIdentifier\n     *            The identifier of the relation\n     * @param relationOsmIdentifier\n     *            The original OSM identifier of the relation. Can be the same as the identifier.\n     * @param identifiers\n     *            The member identifiers.\n     * @param types\n     *            The member types in the same order as the member identifiers\n     * @param roles\n     *            The member roles in the same order as the member identifiers\n     * @param tags\n     *            The relation's tags\n     * @param geometry\n     *            the JTS geometry for the relation, if it is a multipolygon\n     */\n    protected void addRelation(final long relationIdentifier, final long relationOsmIdentifier,\n            final List<Long> identifiers, final List<ItemType> types, final List<String> roles,\n            final Map<String, String> tags, final MultiPolygon geometry)\n    {\n        if (identifiers.size() != types.size() || types.size() != roles.size())\n        {\n            throw new AtlasIntegrityException(\n                    \"Different sizes for relation identifiers and types and roles.\");\n        }\n        if (identifiers.isEmpty())\n        {\n            throw new AtlasIntegrityException(\"Cannot add the relation {} with no members\",\n                    relationIdentifier);\n        }\n        // Do not allow relations with some null members.\n        if (identifiers.stream().anyMatch(Objects::isNull))\n        {\n            throw new AtlasIntegrityException(\"Cannot have a relation with null members.\");\n        }\n        synchronized (this.relationIdentifiers)\n        {\n            if (this.relationIdentifierToRelationArrayIndex.containsKey(relationIdentifier))\n            {\n                throw new AtlasIntegrityException(\n                        PackedAtlasLogMessages.ALREADY_EXISTS_EXCEPTION_MESSAGE, ItemType.RELATION,\n                        relationIdentifier);\n            }\n\n            final long index = this.relationIdentifiers.size();\n            this.relationIdentifiers.add(relationIdentifier);\n            this.relationIdentifierToRelationArrayIndex.put(relationIdentifier, index);\n            this.relationOsmIdentifierToRelationIdentifiers.add(relationOsmIdentifier,\n                    relationIdentifier);\n            this.relationOsmIdentifiers.add(relationOsmIdentifier);\n\n            final long[] memberIndices = new long[identifiers.size()];\n            final byte[] typeValues = new byte[types.size()];\n            final int[] roleValues = new int[roles.size()];\n            for (int i = 0; i < identifiers.size(); i++)\n            {\n                final ItemType type = types.get(i);\n                typeValues[i] = (byte) type.getValue();\n                roleValues[i] = this.dictionary.add(roles.get(i));\n                final Long memberIdentifier = identifiers.get(i);\n                switch (type)\n                {\n                    case NODE:\n                        addRelationMember(\"Node\", index, memberIdentifier, i, memberIndices,\n                                this.nodeIdentifierToNodeArrayIndex,\n                                this.nodeIndexToRelationIndices);\n                        break;\n                    case EDGE:\n                        addRelationMember(\"Edge\", index, memberIdentifier, i, memberIndices,\n                                this.edgeIdentifierToEdgeArrayIndex,\n                                this.edgeIndexToRelationIndices);\n                        break;\n                    case AREA:\n                        addRelationMember(\"Area\", index, memberIdentifier, i, memberIndices,\n                                this.areaIdentifierToAreaArrayIndex,\n                                this.areaIndexToRelationIndices);\n                        break;\n                    case LINE:\n                        addRelationMember(\"Line\", index, memberIdentifier, i, memberIndices,\n                                this.lineIdentifierToLineArrayIndex,\n                                this.lineIndexToRelationIndices);\n                        break;\n                    case POINT:\n                        addRelationMember(\"Point\", index, memberIdentifier, i, memberIndices,\n                                this.pointIdentifierToPointArrayIndex,\n                                this.pointIndexToRelationIndices);\n                        break;\n                    case RELATION:\n                        addRelationMember(\"Relation\", index, memberIdentifier, i, memberIndices,\n                                this.relationIdentifierToRelationArrayIndex,\n                                this.relationIndexToRelationIndices);\n                        break;\n\n                    default:\n                        throw new CoreException(\"Cannot recognize ItemType {}\", type);\n                }\n            }\n\n            if (geometry != null)\n            {\n                if (!this.containsEnhancedRelationGeometry)\n                {\n                    throw new AtlasIntegrityException(\n                            \"Could not add Relation {} with enhanced geometry because it was not enabled for this atlas\",\n                            relationIdentifier);\n                }\n                final ByteArrayResource compressedGeom = new ByteArrayResource(\n                        geometry.toText().getBytes().length * (long) Byte.SIZE);\n                compressedGeom.setCompressor(Compressor.GZIP);\n                compressedGeom.writeAndClose(geometry.toText());\n                this.relationGeometries.add(compressedGeom.readBytesAndClose());\n            }\n            else if (this.containsEnhancedRelationGeometry)\n            {\n                final ByteArrayResource compressedGeom = new ByteArrayResource();\n                compressedGeom.setCompressor(Compressor.GZIP);\n                compressedGeom.writeAndClose(\"\");\n                this.relationGeometries.add(compressedGeom.readBytesAndClose());\n            }\n            this.relationMemberTypes.add(typeValues);\n            this.relationMemberIndices.add(memberIndices);\n            this.relationMemberRoles.add(roleValues);\n\n            // Tags\n            updatePackedTagStore(this.relationTags, index, tags);\n        }\n    }\n\n    protected long areaIdentifier(final long index)\n    {\n        return this.areaIdentifiers().get(index);\n    }\n\n    protected Polygon areaPolygon(final long index)\n    {\n        return this.areaPolygons().get(index);\n    }\n\n    protected Set<Relation> areaRelations(final long index)\n    {\n        return itemRelations(this.areaIndexToRelationIndices().get(index));\n    }\n\n    protected Map<String, String> areaTags(final long index)\n    {\n        return this.areaTags().keyValuePairs(index);\n    }\n\n    protected Node edgeEndNode(final long index)\n    {\n        return new PackedNode(this, this.edgeEndNodeIndex().get(index));\n    }\n\n    protected long edgeIdentifier(final long index)\n    {\n        return this.edgeIdentifiers().get(index);\n    }\n\n    protected PolyLine edgePolyLine(final long index)\n    {\n        return this.edgePolyLines().get(index);\n    }\n\n    protected Set<Relation> edgeRelations(final long index)\n    {\n        return itemRelations(this.edgeIndexToRelationIndices().get(index));\n    }\n\n    protected Node edgeStartNode(final long index)\n    {\n        return new PackedNode(this, this.edgeStartNodeIndex().get(index));\n    }\n\n    protected Map<String, String> edgeTags(final long index)\n    {\n        return this.edgeTags().keyValuePairs(index);\n    }\n\n    /**\n     * Get the serialization format used for loading this {@link PackedAtlas}.\n     *\n     * @return The load serialization format setting\n     */\n    protected AtlasSerializationFormat getLoadSerializationFormat()\n    {\n        return this.loadSerializationFormat;\n    }\n\n    protected Optional<PackedAtlasSerializer> getSerializer()\n    {\n        return Optional.ofNullable(this.serializer);\n    }\n\n    protected boolean isEmpty()\n    {\n        return this.nodeIdentifiers().isEmpty() && this.edgeIdentifiers().isEmpty()\n                && this.areaIdentifiers().isEmpty() && this.lineIdentifiers().isEmpty()\n                && this.pointIdentifiers().isEmpty() && this.relationIdentifiers().isEmpty();\n    }\n\n    protected long lineIdentifier(final long index)\n    {\n        return this.lineIdentifiers().get(index);\n    }\n\n    protected PolyLine linePolyLine(final long index)\n    {\n        return this.linePolyLines().get(index);\n    }\n\n    protected Set<Relation> lineRelations(final long index)\n    {\n        return itemRelations(this.lineIndexToRelationIndices().get(index));\n    }\n\n    protected Map<String, String> lineTags(final long index)\n    {\n        return this.lineTags().keyValuePairs(index);\n    }\n\n    protected long nodeIdentifier(final long index)\n    {\n        return this.nodeIdentifiers().get(index);\n    }\n\n    /**\n     * In very rare cases, Way Slicing will return slightly non-deterministic cut locations in\n     * different shards. This tolerance allows the PackedAtlasBuilder to identify very closeby nodes\n     * and use them instead.\n     *\n     * @param location\n     *            The location to target\n     * @param searchDistance\n     *            The distance to search for around the location\n     * @param toleranceDistance\n     *            The maximum distance at which a node is accepted\n     * @return The resulting node identifier\n     */\n    protected Long nodeIdentifierForEnlargedLocation(final Location location,\n            final Distance searchDistance, final Distance toleranceDistance)\n    {\n        final Rectangle locationBounds = location.bounds().expand(searchDistance);\n        buildNodeSpatialIndexIfNecessary();\n        final SortedSet<Node> nodes = new TreeSet<>((node1, node2) ->\n        {\n            final Distance distance1 = location.distanceTo(node1.getLocation());\n            final Distance distance2 = location.distanceTo(node2.getLocation());\n            final double difference = distance2.asMillimeters() - distance1.asMillimeters();\n            if (difference > 0.0)\n            {\n                return 1;\n            }\n            else if (difference < 0.0)\n            {\n                return -1;\n            }\n            else\n            {\n                return 0;\n            }\n        });\n        this.getNodeSpatialIndex().get(locationBounds).forEach(nodes::add);\n        for (final Node candidate : nodes)\n        {\n            final Distance distance = location.distanceTo(candidate.getLocation());\n            if (distance.isLessThanOrEqualTo(toleranceDistance))\n            {\n                return candidate.getIdentifier();\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Return the identifier of the {@link Node}, if any, at a given {@link Location}. If there are\n     * multiple {@link Node}s at the given location, the one with the lowest identifier is returned.\n     *\n     * @param location\n     *            the location to check\n     * @return the {@link Node} if it exists, null otherwise\n     */\n    protected Long nodeIdentifierForLocation(final Location location)\n    {\n        buildNodeSpatialIndexIfNecessary();\n\n        final Rectangle locationBounds = location.bounds();\n        final SortedSet<Node> nodesByAscendingIdentifier = new TreeSet<>((node1, node2) ->\n        {\n            if (node1.getIdentifier() < node2.getIdentifier())\n            {\n                return -1;\n            }\n            else if (node1.getIdentifier() > node2.getIdentifier())\n            {\n                return 1;\n            }\n            else\n            {\n                return 0;\n            }\n        });\n        this.getNodeSpatialIndex().get(locationBounds).forEach(nodesByAscendingIdentifier::add);\n\n        if (!nodesByAscendingIdentifier.isEmpty())\n        {\n            return nodesByAscendingIdentifier.first().getIdentifier();\n        }\n\n        return null;\n    }\n\n    protected SortedSet<Edge> nodeInEdges(final long index)\n    {\n        final SortedSet<Edge> result = new TreeSet<>();\n        for (final long edgeIndex : this.nodeInEdgesIndices().get(index))\n        {\n            result.add(new PackedEdge(this, edgeIndex));\n        }\n        return result;\n    }\n\n    protected Location nodeLocation(final long index)\n    {\n        return new Location(this.nodeLocations().get(index));\n    }\n\n    protected SortedSet<Edge> nodeOutEdges(final long index)\n    {\n        final SortedSet<Edge> result = new TreeSet<>();\n        for (final long edgeIndex : this.nodeOutEdgesIndices().get(index))\n        {\n            result.add(new PackedEdge(this, edgeIndex));\n        }\n        return result;\n    }\n\n    protected Set<Relation> nodeRelations(final long index)\n    {\n        return itemRelations(this.nodeIndexToRelationIndices().get(index));\n    }\n\n    protected Map<String, String> nodeTags(final long index)\n    {\n        return this.nodeTags().keyValuePairs(index);\n    }\n\n    protected long pointIdentifier(final long index)\n    {\n        return this.pointIdentifiers().get(index);\n    }\n\n    protected Location pointLocation(final long index)\n    {\n        return new Location(this.pointLocations().get(index));\n    }\n\n    protected Set<Relation> pointRelations(final long index)\n    {\n        return itemRelations(this.pointIndexToRelationIndices().get(index));\n    }\n\n    protected Map<String, String> pointTags(final long index)\n    {\n        return this.pointTags().keyValuePairs(index);\n    }\n\n    protected RelationMemberList relationAllKnownOsmMembers(final long index)\n    {\n        final List<RelationMember> result = new ArrayList<>();\n        for (final long candidateIdentifier : this.relationOsmIdentifierToRelationIdentifiers()\n                .get(relationOsmIdentifier(index)))\n        {\n            final long candidateIndex = this.relationIdentifierToRelationArrayIndex()\n                    .get(candidateIdentifier);\n            result.addAll(relationMembers(candidateIndex));\n        }\n        return new RelationMemberList(result);\n    }\n\n    protected List<Relation> relationAllRelationsWithSameOsmIdentifier(final long index)\n    {\n        final List<Relation> result = new ArrayList<>();\n        for (final long candidateIdentifier : this.relationOsmIdentifierToRelationIdentifiers()\n                .get(relationOsmIdentifier(index)))\n        {\n            final long candidateIndex = this.relationIdentifierToRelationArrayIndex()\n                    .get(candidateIdentifier);\n            result.add(new PackedRelation(this, candidateIndex));\n        }\n        return result;\n    }\n\n    protected MultiPolygon relationGeometry(final long index)\n    {\n        if (this.builtRelationGeometries == null)\n        {\n            this.builtRelationGeometries = new HashMap<>();\n        }\n        if (this.builtRelationGeometries.containsKey(index))\n        {\n            return this.builtRelationGeometries.get(index);\n        }\n        try\n        {\n            final ByteArrayResource compressed = new ByteArrayResource(\n                    this.relationGeometries().get(index).length * (long) Byte.SIZE);\n            compressed.writeAndClose(this.relationGeometries().get(index));\n            compressed.setDecompressor(Decompressor.GZIP);\n            final MultiPolygon geom = (MultiPolygon) new WKTReader()\n                    .read(new String(compressed.readBytesAndClose()));\n            this.builtRelationGeometries.put(index, geom);\n            return geom;\n        }\n        catch (final ParseException exc)\n        {\n            logger.warn(\"Couldn't deserialized relation geometry for relation {}\", index, exc);\n            return null;\n        }\n    }\n\n    protected long relationIdentifier(final long index)\n    {\n        return this.relationIdentifiers().get(index);\n    }\n\n    /**\n     * Fetch the {@link RelationMemberList} for a given index. Note that while OSM technically\n     * allows duplicate {@link RelationMember}s, this method disallows duplicates. So a valid OSM\n     * relation that looks like {[1L, 'role1', POINT], [1L, 'role1', POINT], [45L, 'area', AREA]}\n     * would become {[1L, 'role1', POINT], [45L, 'area', AREA]}.\n     *\n     * @param index\n     *            the {@link Relation} array index\n     * @return a fully constructed {@link RelationMemberList} for the {@link Relation} at the given\n     *         index\n     */\n    protected RelationMemberList relationMembers(final long index)\n    {\n        final Set<RelationMember> result = new TreeSet<>();\n        int arrayIndex = 0;\n        for (final byte typeValue : this.relationMemberTypes().get(index))\n        {\n            final ItemType type = ItemType.forValue(typeValue);\n            final long memberIndex = this.relationMemberIndices().get(index)[arrayIndex];\n            final String role = this.dictionary()\n                    .word(this.relationMemberRoles().get(index)[arrayIndex]);\n            final AtlasEntity entity;\n            switch (type)\n            {\n                case NODE:\n                    entity = new PackedNode(this, memberIndex);\n                    break;\n                case EDGE:\n                    entity = new PackedEdge(this, memberIndex);\n                    break;\n                case AREA:\n                    entity = new PackedArea(this, memberIndex);\n                    break;\n                case LINE:\n                    entity = new PackedLine(this, memberIndex);\n                    break;\n                case POINT:\n                    entity = new PackedPoint(this, memberIndex);\n                    break;\n                case RELATION:\n                    entity = new PackedRelation(this, memberIndex);\n                    break;\n                default:\n                    throw new CoreException(\"Invalid member type {}\", type);\n            }\n            result.add(new RelationMember(role, entity, relationIdentifier(index)));\n            arrayIndex++;\n        }\n        return new RelationMemberList(result);\n    }\n\n    protected long relationOsmIdentifier(final long index)\n    {\n        return this.relationOsmIdentifiers().get(index);\n    }\n\n    protected Set<Relation> relationRelations(final long index)\n    {\n        return itemRelations(this.relationIndexToRelationIndices().get(index));\n    }\n\n    protected Map<String, String> relationTags(final long index)\n    {\n        return this.relationTags().keyValuePairs(index);\n    }\n\n    /**\n     * Set the serialization format for loading this {@link PackedAtlas}.\n     *\n     * @param loadFormat\n     *            The format to use\n     */\n    protected void setLoadSerializationFormat(final AtlasSerializationFormat loadFormat)\n    {\n        this.loadSerializationFormat = loadFormat;\n    }\n\n    /**\n     * This method is to be used by the {@link PackedAtlasBuilder} only\n     *\n     * @param metaData\n     *            The new MetaData\n     */\n    protected void setMetaData(final AtlasMetaData metaData)\n    {\n        this.metaData = metaData;\n    }\n\n    @Override\n    protected void setName(final String name) // NOSONAR\n    {\n        super.setName(name);\n    }\n\n    ByteArrayOfArrays enhancedRelationGeometries()\n    {\n        return relationGeometries();\n    }\n\n    void setContainsEnhancedRelationGeometry(final boolean flag)\n    {\n        this.containsEnhancedRelationGeometry = flag;\n    }\n\n    /**\n     * Add a {@link RelationMember}\n     *\n     * @param relationIndex\n     *            The index of the {@link Relation} in the {@link Relation} arrays.\n     * @param memberIdentifier\n     *            The identifier of the member to add\n     * @param relationMemberListIndex\n     *            The index of the member in the {@link Relation}'s member arrays.\n     * @param relationMemberIndexArray\n     *            The array of the indices of the relation members.\n     * @param memberIdentifierToArrayIndex\n     *            The member type's identifier to array index map\n     * @param memberIndicesToRelationIndices\n     *            The member type's index to relation indices map.\n     */\n    private void addRelationMember(final String type, final long relationIndex,\n            final Long memberIdentifier, final int relationMemberListIndex,\n            final long[] relationMemberIndexArray, final LongToLongMap memberIdentifierToArrayIndex,\n            final LongToLongMultiMap memberIndicesToRelationIndices)\n    {\n        if (memberIdentifierToArrayIndex.containsKey(memberIdentifier))\n        {\n            relationMemberIndexArray[relationMemberListIndex] = memberIdentifierToArrayIndex\n                    .get(memberIdentifier);\n            memberIndicesToRelationIndices.add(relationMemberIndexArray[relationMemberListIndex],\n                    relationIndex);\n        }\n        else\n        {\n            throw new AtlasIntegrityException(\"The {} {} does not exist for relation {}.\", type,\n                    memberIdentifier, this.relationIdentifiers.get(relationIndex));\n        }\n    }\n\n    private LongToLongMap areaIdentifierToAreaArrayIndex()\n    {\n        return deserializedIfNeeded(() -> this.areaIdentifierToAreaArrayIndex,\n                this.fieldAreaIdentifierToAreaArrayIndexLock,\n                FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX);\n    }\n\n    private LongArray areaIdentifiers()\n    {\n        return deserializedIfNeeded(() -> this.areaIdentifiers, this.fieldAreaIdentifiersLock,\n                FIELD_AREA_IDENTIFIERS);\n    }\n\n    private LongToLongMultiMap areaIndexToRelationIndices()\n    {\n        return deserializedIfNeeded(() -> this.areaIndexToRelationIndices,\n                this.fieldAreaIndexToRelationIndicesLock, FIELD_AREA_INDEX_TO_RELATION_INDICES);\n    }\n\n    private PolygonArray areaPolygons()\n    {\n        return deserializedIfNeeded(() -> this.areaPolygons, this.fieldAreaPolygonsLock,\n                FIELD_AREA_POLYGONS);\n    }\n\n    private PackedTagStore areaTags()\n    {\n        return deserializedIfNeeded(() -> this.areaTags, tags -> tags.setDictionary(dictionary()),\n                this.fieldAreaTagsLock, FIELD_AREA_TAGS);\n    }\n\n    private <T> T deserializedIfNeeded(final Supplier<T> supplier, final Consumer<T> consumer,\n            final Object lock, final String fieldName)\n    {\n        if (supplier.get() == null)\n        {\n            synchronized (lock) // NOSONAR\n            {\n                if (supplier.get() == null)\n                {\n                    this.serializer.deserializeIfNeeded(fieldName);\n                }\n            }\n        }\n        if (consumer != null)\n        {\n            consumer.accept(supplier.get());\n        }\n        return supplier.get();\n    }\n\n    private <T> T deserializedIfNeeded(final Supplier<T> supplier, final Object lock,\n            final String fieldName)\n    {\n        return deserializedIfNeeded(supplier, null, lock, fieldName);\n    }\n\n    private IntegerDictionary<String> dictionary()\n    {\n        return deserializedIfNeeded(() -> this.dictionary, this.fieldDictionaryLock,\n                FIELD_DICTIONARY);\n    }\n\n    private LongArray edgeEndNodeIndex()\n    {\n        return deserializedIfNeeded(() -> this.edgeEndNodeIndex, this.fieldEdgeEndNodeIndexLock,\n                FIELD_EDGE_END_NODE_INDEX);\n    }\n\n    private LongToLongMap edgeIdentifierToEdgeArrayIndex()\n    {\n        return deserializedIfNeeded(() -> this.edgeIdentifierToEdgeArrayIndex,\n                this.fieldEdgeIdentifierToEdgeArrayIndexLock,\n                FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX);\n    }\n\n    private LongArray edgeIdentifiers()\n    {\n        return deserializedIfNeeded(() -> this.edgeIdentifiers, this.fieldEdgeIdentifiersLock,\n                FIELD_EDGE_IDENTIFIERS);\n    }\n\n    private LongToLongMultiMap edgeIndexToRelationIndices()\n    {\n        return deserializedIfNeeded(() -> this.edgeIndexToRelationIndices,\n                this.fieldEdgeIndexToRelationIndicesLock, FIELD_EDGE_INDEX_TO_RELATION_INDICES);\n    }\n\n    private PolyLineArray edgePolyLines()\n    {\n        return deserializedIfNeeded(() -> this.edgePolyLines, this.fieldEdgePolyLinesLock,\n                FIELD_EDGE_POLY_LINES);\n    }\n\n    private LongArray edgeStartNodeIndex()\n    {\n        return deserializedIfNeeded(() -> this.edgeStartNodeIndex, this.fieldEdgeStartNodeIndexLock,\n                FIELD_EDGE_START_NODE_INDEX);\n    }\n\n    private PackedTagStore edgeTags()\n    {\n        return deserializedIfNeeded(() -> this.edgeTags, tags -> tags.setDictionary(dictionary()),\n                this.fieldEdgeTagsLock, FIELD_EDGE_TAGS);\n    }\n\n    private Set<Relation> itemRelations(final long[] relationIndices)\n    {\n        final Set<Relation> result = new LinkedHashSet<>();\n        if (relationIndices == null)\n        {\n            return result;\n        }\n        for (final long relationIndex : relationIndices)\n        {\n            result.add(new PackedRelation(this, relationIndex));\n        }\n        return result;\n    }\n\n    private LongToLongMap lineIdentifierToLineArrayIndex()\n    {\n        return deserializedIfNeeded(() -> this.lineIdentifierToLineArrayIndex,\n                this.fieldLineIdentifierToLineArrayIndexLock,\n                FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX);\n    }\n\n    private LongArray lineIdentifiers()\n    {\n        return deserializedIfNeeded(() -> this.lineIdentifiers, this.fieldLineIdentifiersLock,\n                FIELD_LINE_IDENTIFIERS);\n    }\n\n    private LongToLongMultiMap lineIndexToRelationIndices()\n    {\n        return deserializedIfNeeded(() -> this.lineIndexToRelationIndices,\n                this.fieldLindIndexToRelationIndicesLock, FIELD_LINE_INDEX_TO_RELATION_INDICES);\n    }\n\n    private PolyLineArray linePolyLines()\n    {\n        return deserializedIfNeeded(() -> this.linePolyLines, this.fieldLinePolyLinesLock,\n                FIELD_LINE_POLYLINES);\n    }\n\n    private PackedTagStore lineTags()\n    {\n        return deserializedIfNeeded(() -> this.lineTags, tags -> tags.setDictionary(dictionary()),\n                this.fieldLineTagsLock, FIELD_LINE_TAGS);\n    }\n\n    // Keep this method around so legacy Atlas files can still be deserialized.\n    @SuppressWarnings(\"unused\")\n    private PackedTagStore newPackedTagStore(final long maximumSize, final int memoryBlockSize,\n            final int subArraySize)\n    {\n        return new PackedTagStore(maximumSize, memoryBlockSize, subArraySize, dictionary())\n        {\n            private static final long serialVersionUID = 5959934069025112665L;\n        };\n    }\n\n    private LongToLongMap nodeIdentifierToNodeArrayIndex()\n    {\n        return deserializedIfNeeded(() -> this.nodeIdentifierToNodeArrayIndex,\n                this.fieldNodeIdentifierToNodeArrayIndexLock,\n                FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX);\n    }\n\n    private LongArray nodeIdentifiers()\n    {\n        return deserializedIfNeeded(() -> this.nodeIdentifiers, this.fieldNodeIdentifiersLock,\n                FIELD_NODE_IDENTIFIERS);\n    }\n\n    private LongArrayOfArrays nodeInEdgesIndices()\n    {\n        return deserializedIfNeeded(() -> this.nodeInEdgesIndices, this.fieldNodeInEdgesIndicesLock,\n                FIELD_NODE_IN_EDGES_INDICES);\n    }\n\n    private LongToLongMultiMap nodeIndexToRelationIndices()\n    {\n        return deserializedIfNeeded(() -> this.nodeIndexToRelationIndices,\n                this.fieldNodeIndexToRelationIndicesLock, FIELD_NODE_INDEX_TO_RELATION_INDICES);\n    }\n\n    private LongArray nodeLocations()\n    {\n        return deserializedIfNeeded(() -> this.nodeLocations, this.fieldNodeLocationsLock,\n                FIELD_NODE_LOCATIONS);\n    }\n\n    private LongArrayOfArrays nodeOutEdgesIndices()\n    {\n        return deserializedIfNeeded(() -> this.nodeOutEdgesIndices,\n                this.fieldNodeOutEdgesIndicesLock, FIELD_NODE_OUT_EDGES_INDICES);\n    }\n\n    private PackedTagStore nodeTags()\n    {\n        return deserializedIfNeeded(() -> this.nodeTags, tags -> tags.setDictionary(dictionary()),\n                this.fieldNodeTagsLock, FIELD_NODE_TAGS);\n    }\n\n    private LongToLongMap pointIdentifierToPointArrayIndex()\n    {\n        return deserializedIfNeeded(() -> this.pointIdentifierToPointArrayIndex,\n                this.fieldPointIdentifierToPointArrayIndexLock,\n                FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX);\n    }\n\n    private LongArray pointIdentifiers()\n    {\n        return deserializedIfNeeded(() -> this.pointIdentifiers, this.fieldPointIdentifiersLock,\n                FIELD_POINT_IDENTIFIERS);\n    }\n\n    private LongToLongMultiMap pointIndexToRelationIndices()\n    {\n        return deserializedIfNeeded(() -> this.pointIndexToRelationIndices,\n                this.fieldPointIndexToRelationIndicesLock, FIELD_POINT_INDEX_TO_RELATION_INDICES);\n    }\n\n    private LongArray pointLocations()\n    {\n        return deserializedIfNeeded(() -> this.pointLocations, this.fieldPointLocationsLock,\n                FIELD_POINT_LOCATIONS);\n    }\n\n    private PackedTagStore pointTags()\n    {\n        return deserializedIfNeeded(() -> this.pointTags, tags -> tags.setDictionary(dictionary()),\n                this.fieldPointTagsLock, FIELD_POINT_TAGS);\n    }\n\n    private void readObject(final java.io.ObjectInputStream inFile)\n            throws IOException, ClassNotFoundException\n    {\n        inFile.defaultReadObject();\n        if (this.fieldDictionaryLock == null)\n        {\n            this.fieldDictionaryLock = new Object();\n        }\n        if (this.fieldEdgeIdentifiersLock == null)\n        {\n            this.fieldEdgeIdentifiersLock = new Object();\n        }\n        if (this.fieldNodeIdentifiersLock == null)\n        {\n            this.fieldNodeIdentifiersLock = new Object();\n        }\n        if (this.fieldAreaIdentifiersLock == null)\n        {\n            this.fieldAreaIdentifiersLock = new Object();\n        }\n        if (this.fieldLineIdentifiersLock == null)\n        {\n            this.fieldLineIdentifiersLock = new Object();\n        }\n        if (this.fieldPointIdentifiersLock == null)\n        {\n            this.fieldPointIdentifiersLock = new Object();\n        }\n        if (this.fieldRelationIdentifiersLock == null)\n        {\n            this.fieldRelationIdentifiersLock = new Object();\n        }\n        if (this.fieldEdgeIdentifierToEdgeArrayIndexLock == null)\n        {\n            this.fieldEdgeIdentifierToEdgeArrayIndexLock = new Object();\n        }\n        if (this.fieldNodeIdentifierToNodeArrayIndexLock == null)\n        {\n            this.fieldNodeIdentifierToNodeArrayIndexLock = new Object();\n        }\n        if (this.fieldAreaIdentifierToAreaArrayIndexLock == null)\n        {\n            this.fieldAreaIdentifierToAreaArrayIndexLock = new Object();\n        }\n        if (this.fieldLineIdentifierToLineArrayIndexLock == null)\n        {\n            this.fieldLineIdentifierToLineArrayIndexLock = new Object();\n        }\n        if (this.fieldPointIdentifierToPointArrayIndexLock == null)\n        {\n            this.fieldPointIdentifierToPointArrayIndexLock = new Object();\n        }\n        if (this.fieldRelationIdentifierToRelationArrayIndexLock == null)\n        {\n            this.fieldRelationIdentifierToRelationArrayIndexLock = new Object();\n        }\n        if (this.fieldNodeLocationsLock == null)\n        {\n            this.fieldNodeLocationsLock = new Object();\n        }\n        if (this.fieldNodeInEdgesIndicesLock == null)\n        {\n            this.fieldNodeInEdgesIndicesLock = new Object();\n        }\n        if (this.fieldNodeOutEdgesIndicesLock == null)\n        {\n            this.fieldNodeOutEdgesIndicesLock = new Object();\n        }\n        if (this.fieldNodeTagsLock == null)\n        {\n            this.fieldNodeTagsLock = new Object();\n        }\n        if (this.fieldNodeIndexToRelationIndicesLock == null)\n        {\n            this.fieldNodeIndexToRelationIndicesLock = new Object();\n        }\n        if (this.fieldEdgeStartNodeIndexLock == null)\n        {\n            this.fieldEdgeStartNodeIndexLock = new Object();\n        }\n        if (this.fieldEdgeEndNodeIndexLock == null)\n        {\n            this.fieldEdgeEndNodeIndexLock = new Object();\n        }\n        if (this.fieldEdgePolyLinesLock == null)\n        {\n            this.fieldEdgePolyLinesLock = new Object();\n        }\n        if (this.fieldEdgeTagsLock == null)\n        {\n            this.fieldEdgeTagsLock = new Object();\n        }\n        if (this.fieldEdgeIndexToRelationIndicesLock == null)\n        {\n            this.fieldEdgeIndexToRelationIndicesLock = new Object();\n        }\n        if (this.fieldAreaPolygonsLock == null)\n        {\n            this.fieldAreaPolygonsLock = new Object();\n        }\n        if (this.fieldAreaTagsLock == null)\n        {\n            this.fieldAreaTagsLock = new Object();\n        }\n        if (this.fieldAreaIndexToRelationIndicesLock == null)\n        {\n            this.fieldAreaIndexToRelationIndicesLock = new Object();\n        }\n        if (this.fieldLinePolyLinesLock == null)\n        {\n            this.fieldLinePolyLinesLock = new Object();\n        }\n        if (this.fieldLineTagsLock == null)\n        {\n            this.fieldLineTagsLock = new Object();\n        }\n        if (this.fieldLindIndexToRelationIndicesLock == null)\n        {\n            this.fieldLindIndexToRelationIndicesLock = new Object();\n        }\n        if (this.fieldPointLocationsLock == null)\n        {\n            this.fieldPointLocationsLock = new Object();\n        }\n        if (this.fieldPointTagsLock == null)\n        {\n            this.fieldPointTagsLock = new Object();\n        }\n        if (this.fieldPointIndexToRelationIndicesLock == null)\n        {\n            this.fieldPointIndexToRelationIndicesLock = new Object();\n        }\n        if (this.fieldRelationMembersIndicesLock == null)\n        {\n            this.fieldRelationMembersIndicesLock = new Object();\n        }\n        if (this.fieldRelationMemberTypesLock == null)\n        {\n            this.fieldRelationMemberTypesLock = new Object();\n        }\n        if (this.fieldRelationMemberRolesLock == null)\n        {\n            this.fieldRelationMemberRolesLock = new Object();\n        }\n        if (this.fieldRelationTagsLock == null)\n        {\n            this.fieldRelationTagsLock = new Object();\n        }\n        if (this.fieldRelationIndexToRelationIndicesLock == null)\n        {\n            this.fieldRelationIndexToRelationIndicesLock = new Object();\n        }\n        if (this.fieldRelationOsmIdentifierToRelationIdentifiersLock == null)\n        {\n            this.fieldRelationOsmIdentifierToRelationIdentifiersLock = new Object();\n        }\n        if (this.fieldRelationOsmIdentifiersLock == null)\n        {\n            this.fieldRelationOsmIdentifiersLock = new Object();\n        }\n        if (this.fieldRelationGeometriesLock == null)\n        {\n            this.fieldRelationGeometriesLock = new Object();\n        }\n    }\n\n    private ByteArrayOfArrays relationGeometries()\n    {\n        return deserializedIfNeeded(() -> this.relationGeometries, this.fieldRelationGeometriesLock,\n                FIELD_RELATION_GEOMETRIES);\n    }\n\n    private LongToLongMap relationIdentifierToRelationArrayIndex()\n    {\n        return deserializedIfNeeded(() -> this.relationIdentifierToRelationArrayIndex,\n                this.fieldRelationIdentifierToRelationArrayIndexLock,\n                FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX);\n    }\n\n    private LongArray relationIdentifiers()\n    {\n        return deserializedIfNeeded(() -> this.relationIdentifiers,\n                this.fieldRelationIdentifiersLock, FIELD_RELATION_IDENTIFIERS);\n    }\n\n    private LongToLongMultiMap relationIndexToRelationIndices()\n    {\n        return deserializedIfNeeded(() -> this.relationIndexToRelationIndices,\n                this.fieldRelationIndexToRelationIndicesLock,\n                FIELD_RELATION_INDEX_TO_RELATION_INDICES);\n    }\n\n    private LongArrayOfArrays relationMemberIndices()\n    {\n        return deserializedIfNeeded(() -> this.relationMemberIndices,\n                this.fieldRelationMembersIndicesLock, FIELD_RELATION_MEMBERS_INDICES);\n    }\n\n    private IntegerArrayOfArrays relationMemberRoles()\n    {\n        return deserializedIfNeeded(() -> this.relationMemberRoles,\n                this.fieldRelationMemberRolesLock, FIELD_RELATION_MEMBER_ROLES);\n    }\n\n    private ByteArrayOfArrays relationMemberTypes()\n    {\n        return deserializedIfNeeded(() -> this.relationMemberTypes,\n                this.fieldRelationMemberTypesLock, FIELD_RELATION_MEMBER_TYPES);\n    }\n\n    private LongToLongMultiMap relationOsmIdentifierToRelationIdentifiers()\n    {\n        return deserializedIfNeeded(() -> this.relationOsmIdentifierToRelationIdentifiers,\n                this.fieldRelationOsmIdentifierToRelationIdentifiersLock,\n                FIELD_RELATION_OSM_IDENTIFIER_TO_RELATION_IDENTIFIERS);\n    }\n\n    private LongArray relationOsmIdentifiers()\n    {\n        return deserializedIfNeeded(() -> this.relationOsmIdentifiers,\n                this.fieldRelationOsmIdentifiersLock, FIELD_RELATION_OSM_IDENTIFIERS);\n    }\n\n    private PackedTagStore relationTags()\n    {\n        return deserializedIfNeeded(() -> this.relationTags,\n                tags -> tags.setDictionary(dictionary()), this.fieldRelationTagsLock,\n                FIELD_RELATION_TAGS);\n    }\n\n    /**\n     * Update references for Node in/out edges\n     *\n     * @param nodeEdgesIndices\n     *            Either the nodeInEdges or the nodeOutEdges\n     */\n    private void updateNodeEdgesReference(final long nodeIndex,\n            final LongArrayOfArrays nodeEdgesIndices, final long edgeIndex)\n    {\n        final long[] nodeEdges = nodeEdgesIndices.get(nodeIndex);\n        final long[] newNodeEdges = new long[nodeEdges.length + 1];\n        for (int i = 0; i < nodeEdges.length; i++)\n        {\n            newNodeEdges[i] = nodeEdges[i];\n        }\n        newNodeEdges[newNodeEdges.length - 1] = edgeIndex;\n        nodeEdgesIndices.set(nodeIndex, newNodeEdges);\n    }\n\n    private void updatePackedTagStore(final PackedTagStore packedTagStore, final long index,\n            final Map<String, String> tags)\n    {\n        if (tags.isEmpty())\n        {\n            packedTagStore.add(index, null, null);\n        }\n        else\n        {\n            for (final Map.Entry<String, String> entry : tags.entrySet())\n            {\n                packedTagStore.add(index, entry.getKey(), entry.getValue());\n            }\n        }\n    }\n\n    private void writeObject(final java.io.ObjectOutputStream out) throws IOException\n    {\n        if (this.serializer != null)\n        {\n            this.serializer.deserializeAllFieldsIfNeeded();\n        }\n        out.defaultWriteObject();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasBuilder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.Map;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.exception.AtlasIntegrityException;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link AtlasBuilder} for a {@link PackedAtlas}. This is not thread safe!\n *\n * @author matthieun\n */\npublic final class PackedAtlasBuilder implements AtlasBuilder\n{\n    private static final Logger logger = LoggerFactory.getLogger(PackedAtlasBuilder.class);\n\n    private static final int MAXIMUM_RELATION_MEMBER_DEPTH = 500;\n    // In very rare cases, Way Slicing will return slightly non-deterministic cut locations in\n    // different shards. This tolerance allows the PackedAtlasBuilder to identify very closeby nodes\n    // and use them instead.\n    private static final Distance NODE_SEARCH_DISTANCE = Distance.ONE_METER;\n    private static final Distance NODE_TOLERANCE_DISTANCE = Distance.meters(0.1);\n\n    private PackedAtlas atlas;\n    private AtlasSize sizeEstimates = AtlasSize.DEFAULT;\n    private boolean locked = false;\n    private String name;\n\n    private AtlasMetaData metaData = new AtlasMetaData();\n\n    @Override\n    public void addArea(final long identifier, final Polygon geometry,\n            final Map<String, String> tags)\n    {\n        initialize();\n        try\n        {\n            this.atlas.addArea(identifier, geometry, tags);\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Error adding Area ({}): {}\", identifier, geometry.toWkt(), e);\n        }\n    }\n\n    @Override\n    public void addEdge(final long identifier, final PolyLine geometry,\n            final Map<String, String> tags)\n    {\n        initialize();\n        final Location start = geometry.first();\n        final Location end = geometry.last();\n        Long startNodeIdentifier = this.atlas.nodeIdentifierForLocation(start);\n        Long endNodeIdentifier = this.atlas.nodeIdentifierForLocation(end);\n        if (startNodeIdentifier == null)\n        {\n            startNodeIdentifier = this.atlas.nodeIdentifierForEnlargedLocation(start,\n                    NODE_SEARCH_DISTANCE, NODE_TOLERANCE_DISTANCE);\n            if (startNodeIdentifier == null)\n            {\n                throw new AtlasIntegrityException(\n                        \"Atlas does not contain Node for Location {} for edge {}\", start,\n                        identifier);\n            }\n            logger.warn(\n                    \"Atlas does not contain Node for Location {} for edge {}. \"\n                            + \"Found very close node {} and using it instead.\",\n                    start, identifier, startNodeIdentifier);\n        }\n        if (endNodeIdentifier == null)\n        {\n            endNodeIdentifier = this.atlas.nodeIdentifierForEnlargedLocation(end,\n                    NODE_SEARCH_DISTANCE, NODE_TOLERANCE_DISTANCE);\n            if (endNodeIdentifier == null)\n            {\n                throw new AtlasIntegrityException(\n                        \"Atlas does not contain Node for Location {} for edge {}\", end, identifier);\n            }\n            logger.warn(\n                    \"Atlas does not contain Node for Location {} for edge {}. \"\n                            + \"Found very close node {} and using it instead.\",\n                    end, identifier, endNodeIdentifier);\n        }\n        try\n        {\n            this.atlas.addEdge(identifier, startNodeIdentifier, endNodeIdentifier, geometry, tags);\n        }\n        catch (final AtlasIntegrityException e)\n        {\n            throw e;\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Error adding Edge ({}): {}\", identifier, geometry.toWkt(), e);\n        }\n    }\n\n    @Override\n    public void addLine(final long identifier, final PolyLine geometry,\n            final Map<String, String> tags)\n    {\n        initialize();\n        try\n        {\n            this.atlas.addLine(identifier, geometry, tags);\n        }\n        catch (final AtlasIntegrityException e)\n        {\n            throw e;\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Error adding Line ({}): {}\", identifier, geometry.toWkt(), e);\n        }\n    }\n\n    @Override\n    public void addNode(final long identifier, final Location geometry,\n            final Map<String, String> tags)\n    {\n        initialize();\n        try\n        {\n            this.atlas.addNode(identifier, geometry, tags);\n        }\n        catch (final AtlasIntegrityException e)\n        {\n            throw e;\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Error adding Node ({}): {}\", identifier, geometry.toWkt(), e);\n        }\n    }\n\n    @Override\n    public void addPoint(final long identifier, final Location geometry,\n            final Map<String, String> tags)\n    {\n        initialize();\n        try\n        {\n            this.atlas.addPoint(identifier, geometry, tags);\n        }\n        catch (final AtlasIntegrityException e)\n        {\n            throw e;\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Error adding Point ({}): {}\", identifier, geometry.toWkt(), e);\n        }\n    }\n\n    @Override\n    public void addRelation(final long identifier, final long osmIdentifier,\n            final RelationBean structure, final Map<String, String> tags)\n    {\n        if (structure.isEmpty())\n        {\n            throw new CoreException(\"Cannot add relation {} with an empty member list.\",\n                    identifier);\n        }\n        initialize();\n        try\n        {\n            this.atlas.addRelation(identifier, osmIdentifier, structure.getMemberIdentifiers(),\n                    structure.getMemberTypes(), structure.getMemberRoles(), tags, null);\n        }\n        catch (final AtlasIntegrityException e)\n        {\n            throw e;\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Error adding Relation ({}): {}\", identifier, structure, e);\n        }\n    }\n\n    public void addRelation(final long identifier, final long osmIdentifier,\n            final RelationBean structure, final Map<String, String> tags,\n            final MultiPolygon geometry)\n    {\n        if (structure.isEmpty())\n        {\n            throw new CoreException(\"Cannot add relation {} with an empty member list.\",\n                    identifier);\n        }\n        initialize();\n        try\n        {\n            this.atlas.addRelation(identifier, osmIdentifier, structure.getMemberIdentifiers(),\n                    structure.getMemberTypes(), structure.getMemberRoles(), tags, geometry);\n        }\n        catch (final AtlasIntegrityException e)\n        {\n            throw e;\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Error adding Relation ({}): {}\", identifier, structure.toString(), e);\n        }\n    }\n\n    @Override\n    public Atlas get()\n    {\n        initialize();\n        this.locked = true;\n        if (this.atlas.isEmpty())\n        {\n            logger.warn(\"An Atlas is Located, and therefore cannot be empty.\");\n            return null;\n        }\n        if (Iterables.size(this.atlas) == this.atlas.numberOfRelations())\n        {\n            logger.warn(\n                    \"An Atlas is Located, and therefore cannot be made of only relations (which cannot be located as there are no other features).\");\n            return null;\n        }\n        verifyNegativeEdgesHaveMainEdge();\n        this.atlas.relations().forEach(relation ->\n        {\n            try\n            {\n                // Make sure that the relations are not looping to each other, or just bounds-less.\n                validateRelation(relation, relation.getIdentifier(), 0);\n            }\n            catch (final Exception e)\n            {\n                throw new CoreException(\"Relation {} is corrupted. Invalidating Atlas!\",\n                        relation.getIdentifier(), e);\n            }\n        });\n        // Update the meta data so the Atlas sizes are correct.\n        final AtlasSize updatedAtlasSize = new AtlasSize(this.atlas.numberOfEdges(),\n                this.atlas.numberOfNodes(), this.atlas.numberOfAreas(), this.atlas.numberOfLines(),\n                this.atlas.numberOfPoints(), this.atlas.numberOfRelations());\n        this.atlas.setMetaData(this.metaData.copyWithNewSize(updatedAtlasSize));\n        return this.atlas;\n    }\n\n    public PackedAtlas peek()\n    {\n        initialize();\n        return this.atlas;\n    }\n\n    @Override\n    public void setMetaData(final AtlasMetaData metaData)\n    {\n        this.metaData = metaData;\n    }\n\n    @Override\n    public void setSizeEstimates(final AtlasSize estimates)\n    {\n        this.sizeEstimates = estimates;\n    }\n\n    public PackedAtlasBuilder withEnhancedRelationGeometry()\n    {\n        initialize(true);\n        return this;\n    }\n\n    public PackedAtlasBuilder withMetaData(final AtlasMetaData metaData)\n    {\n        setMetaData(metaData);\n        return this;\n    }\n\n    public PackedAtlasBuilder withName(final String name)\n    {\n        this.name = name;\n        return this;\n    }\n\n    public PackedAtlasBuilder withSizeEstimates(final AtlasSize estimates)\n    {\n        setSizeEstimates(estimates);\n        return this;\n    }\n\n    private void initialize()\n    {\n        initialize(false);\n    }\n\n    private void initialize(final boolean withEnhancedRelationGeometry)\n    {\n        if (this.locked)\n        {\n            throw new CoreException(\"Cannot keep adding items to a locked graph.\");\n        }\n        if (this.atlas == null)\n        {\n            this.atlas = new PackedAtlas(this.sizeEstimates, withEnhancedRelationGeometry);\n            this.atlas.setName(this.name);\n        }\n    }\n\n    /**\n     * Recursive call to make sure that the relations are really bounded and do not loop on each\n     * other.\n     *\n     * @param relation\n     */\n    private void validateRelation(final Relation relation, final long parentIdentifier,\n            final int depth)\n    {\n        if (depth > MAXIMUM_RELATION_MEMBER_DEPTH)\n        {\n            throw new CoreException(\n                    \"Relation {} referencing each other more than {} levels deep, without hitting any bounded feature.\",\n                    parentIdentifier, MAXIMUM_RELATION_MEMBER_DEPTH);\n        }\n        for (final RelationMember member : relation.members())\n        {\n            if (member.getEntity() instanceof AtlasItem)\n            {\n                return;\n            }\n            else\n            {\n                validateRelation((Relation) member.getEntity(), parentIdentifier, depth + 1);\n            }\n        }\n    }\n\n    private void verifyNegativeEdgesHaveMainEdge()\n    {\n        this.atlas.edges().forEach(edge ->\n        {\n            final long edgeIdentifier = edge.getIdentifier();\n            if (edgeIdentifier < 0 && this.atlas.edge(-edgeIdentifier) == null)\n            {\n                throw new AtlasIntegrityException(\n                        \"Cannot build an Atlas with a negative edge without its positive counterpart: {}\",\n                        edgeIdentifier);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasCloner.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\n\n/**\n * Atlas Cloner. Mostly useful to get a {@link MultiAtlas} and clone it into one single\n * {@link PackedAtlas}\n *\n * @author matthieun\n */\npublic class PackedAtlasCloner\n{\n    private String shardName = null;\n    private Optional<Map<String, String>> additionalMetaDataTags = Optional.empty();\n\n    public PackedAtlasCloner()\n    {\n    }\n\n    public PackedAtlasCloner(final String shardName)\n    {\n        this.shardName = shardName;\n    }\n\n    /**\n     * Clone an {@link Atlas}\n     *\n     * @param atlas\n     *            The source {@link Atlas}\n     * @return A cloned {@link PackedAtlas}\n     */\n    public PackedAtlas cloneFrom(final Atlas atlas)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.setSizeEstimates(atlas.metaData().getSize());\n        AtlasMetaData metaData = atlas.metaData();\n        if (this.shardName != null)\n        {\n            metaData.copyWithNewShardName(this.shardName);\n        }\n\n        if (this.additionalMetaDataTags.isPresent())\n        {\n            final Map<String, String> atlasTags = metaData.getTags();\n            atlasTags.putAll(this.additionalMetaDataTags.get());\n\n            metaData = new AtlasMetaData(metaData.getSize(), metaData.isOriginal(),\n                    metaData.getCodeVersion().orElse(null), metaData.getDataVersion().orElse(null),\n                    metaData.getCountry().orElse(null), metaData.getShardName().orElse(null),\n                    atlasTags);\n        }\n\n        builder.setMetaData(metaData);\n        builder.withEnhancedRelationGeometry();\n        atlas.nodes().forEach(\n                node -> builder.addNode(node.getIdentifier(), node.getLocation(), node.getTags()));\n        atlas.edges().forEach(\n                edge -> builder.addEdge(edge.getIdentifier(), edge.asPolyLine(), edge.getTags()));\n        atlas.areas().forEach(\n                area -> builder.addArea(area.getIdentifier(), area.asPolygon(), area.getTags()));\n        atlas.lines().forEach(\n                line -> builder.addLine(line.getIdentifier(), line.asPolyLine(), line.getTags()));\n        atlas.points().forEach(point -> builder.addPoint(point.getIdentifier(), point.getLocation(),\n                point.getTags()));\n        // It's crucial to add relations in lowest order to highest order, to avoid adding a\n        // relation which may contain an un-added sub-relation.\n        atlas.relationsLowerOrderFirst().forEach(relation -> addRelation(builder, relation));\n\n        return (PackedAtlas) builder.get();\n    }\n\n    /**\n     * Adds the passed in extra tags to the {@link AtlasMetaData} when the atlas is cloned.\n     * <p>\n     * CAUTION: This will overwrite current tags if there is already a tag with the same key in the\n     * tag map.\n     *\n     * @param additionalMetaDataTags\n     *            Extra {@link AtlasMetaData} tags to add to the cloned atlas\n     * @return The updated {@link PackedAtlasCloner}\n     */\n    public PackedAtlasCloner withAdditionalMetaDataTags(\n            final Map<String, String> additionalMetaDataTags)\n    {\n        this.additionalMetaDataTags = Optional.ofNullable(additionalMetaDataTags);\n        return this;\n    }\n\n    private void addRelation(final PackedAtlasBuilder builder, final Relation relation)\n    {\n        final RelationBean bean = new RelationBean();\n        relation.members().forEach(member -> bean.addItem(member.getEntity().getIdentifier(),\n                member.getRole(), member.getEntity().getType()));\n        final Optional<MultiPolygon> geom = relation.asMultiPolygon();\n        if (geom.isPresent())\n        {\n            builder.addRelation(relation.getIdentifier(), relation.osmRelationIdentifier(), bean,\n                    relation.getTags(), geom.get());\n        }\n        else\n        {\n            builder.addRelation(relation.getIdentifier(), relation.osmRelationIdentifier(), bean,\n                    relation.getTags());\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasLogMessages.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\n/**\n * @author matthieun\n */\nfinal class PackedAtlasLogMessages\n{\n    static final String ALREADY_EXISTS_EXCEPTION_MESSAGE = \"{} with identifier {} already exists.\";\n\n    private PackedAtlasLogMessages()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasSerializer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.io.BufferedOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.OutputStream;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas.AtlasSerializationFormat;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.streaming.CounterOutputStream;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.resource.zip.ZipFileWritableResource;\nimport org.openstreetmap.atlas.streaming.resource.zip.ZipResource;\nimport org.openstreetmap.atlas.streaming.resource.zip.ZipResource.ZipIterator;\nimport org.openstreetmap.atlas.streaming.resource.zip.ZipWritableResource;\nimport org.openstreetmap.atlas.utilities.arrays.ByteArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.collections.StreamIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Class that serializes and deserializes {@link PackedAtlas}s to a {@link ZipResource}\n *\n * @author matthieun\n * @author lcram\n */\npublic final class PackedAtlasSerializer\n{\n    /**\n     * Exception that is thrown in case a field is in a {@link ZipResource} but the current\n     * implementation of the {@link PackedAtlas} does not recognize it.\n     *\n     * @author matthieun\n     */\n    private static class MissingFieldException extends CoreException\n    {\n        private static final long serialVersionUID = 6780849464228478451L;\n\n        MissingFieldException(final String message)\n        {\n            super(message);\n        }\n\n        MissingFieldException(final String message, final Object... items)\n        {\n            super(message, items);\n        }\n\n        MissingFieldException(final String message, final Throwable cause)\n        {\n            super(message, cause);\n        }\n    }\n\n    public static final String META_DATA_ERROR_MESSAGE = \"MetaData not here!\";\n    private static final Logger logger = LoggerFactory.getLogger(PackedAtlasSerializer.class);\n    // The fields not serialized.\n    private static final StringList EXCLUDED_FIELDS = new StringList(PackedAtlas.FIELD_BOUNDS,\n            PackedAtlas.FIELD_SERIAL_VERSION_UID, PackedAtlas.FIELD_LOGGER, \"$SWITCH_TABLE$\",\n            PackedAtlas.FIELD_SERIALIZER, PackedAtlas.FIELD_SAVE_SERIALIZATION_FORMAT,\n            PackedAtlas.FIELD_LOAD_SERIALIZATION_FORMAT, PackedAtlas.FIELD_PREFIX,\n            PackedAtlas.FIELD_CONTAINS_ENHANCED_RELATION_GEOMETRY,\n            PackedAtlas.FIELD_BUILT_RELATION_GEOMETRIES,\n            /* https://stackoverflow.com/a/39037512/1558687 */\"$jacocoData\");\n    private final PackedAtlas atlas;\n    private final ZipResource source;\n\n    /**\n     * Use reflection to create a {@link PackedAtlas} from a serialized resource.\n     *\n     * @param resource\n     *            The resource\n     * @return The deserialized {@link PackedAtlas}\n     */\n    protected static PackedAtlas load(final Resource resource)\n    {\n        // Create an empty Atlas.\n        final PackedAtlas atlas = new PackedAtlas();\n        // Build the serializer with it\n        final PackedAtlasSerializer serializer = new PackedAtlasSerializer(atlas, resource);\n        // Assign the serializer to the Atlas! Then the Atlas will load all the fields depending on\n        // demand.\n        serializer.assign();\n\n        // This is for backwards compatibility and will slow Atlas loading\n        determineAtlasLoadFormat(atlas);\n\n        return atlas;\n    }\n\n    /*\n     * Try loading the meta data to make sure the data format is appropriate. Keep trying formats\n     * until we find the right one\n     */\n    private static void determineAtlasLoadFormat(final PackedAtlas atlas)\n    {\n        final AtlasSerializationFormat[] possibleFormats = AtlasSerializationFormat.values();\n        for (final AtlasSerializationFormat candidateFormat : possibleFormats)\n        {\n            logger.trace(\"Trying load format {} for atlas {}\", candidateFormat, atlas.getName());\n            atlas.setLoadSerializationFormat(candidateFormat);\n            try\n            {\n                atlas.metaData();\n            }\n            catch (final CoreException exception)\n            {\n                logger.debug(\"Load format {} invalid for atlas {}\", candidateFormat,\n                        atlas.getName(), exception);\n                continue;\n            }\n            // If we make it here, then we found the appropriate format\n            logger.trace(\"Using load format {} for atlas {}\", candidateFormat, atlas.getName());\n\n            /*\n             * Now, if we are PROTOBUF, let's check for the enhanced relation geometry that some\n             * atlases may contain.\n             */\n            if (atlas.getLoadSerializationFormat() == AtlasSerializationFormat.PROTOBUF)\n            {\n                try\n                {\n                    atlas.setContainsEnhancedRelationGeometry(true);\n                    final ByteArrayOfArrays array = atlas.enhancedRelationGeometries();\n                    if (array == null)\n                    {\n                        atlas.setContainsEnhancedRelationGeometry(false);\n                    }\n                }\n                catch (final CoreException exception)\n                {\n                    if (\"Unable to read Atlas field relationGeometries\"\n                            .equals(exception.getMessage()))\n                    {\n                        atlas.setContainsEnhancedRelationGeometry(false);\n                    }\n                    else\n                    {\n                        throw exception;\n                    }\n                }\n            }\n\n            return;\n        }\n\n        throw new CoreException(\"Could not determine a valid load format for atlas {}\",\n                atlas.getName());\n    }\n\n    /**\n     * Construct a new {@link PackedAtlasSerializer}.\n     *\n     * @param atlas\n     *            The {@link Atlas} to be serialized / deserialized\n     * @param resource\n     *            The resource where to serialize / deserialize from.\n     */\n    protected PackedAtlasSerializer(final PackedAtlas atlas, final Resource resource)\n    {\n        this.atlas = atlas;\n        if (resource instanceof File && !resource.isGzipped())\n        {\n            // Make sure to use ZipFileWritableResource to take advantage of the random access.\n            this.source = new ZipFileWritableResource((File) resource);\n        }\n        else if (resource instanceof WritableResource)\n        {\n            this.source = new ZipWritableResource((WritableResource) resource);\n        }\n        else\n        {\n            this.source = new ZipResource(resource);\n        }\n    }\n\n    /**\n     * Go after all the fields that might not have been deserialized and deserialize them\n     */\n    protected void deserializeAllFieldsIfNeeded()\n    {\n        fields().map(Field::getName).forEach(this::deserializeIfNeeded);\n    }\n\n    /**\n     * This method is used by the {@link PackedAtlas} to access its own fields!\n     *\n     * @param name\n     *            The name of the field\n     */\n    protected void deserializeIfNeeded(final String name)\n    {\n        final Object member;\n        try\n        {\n            final Field field = readField(name);\n            member = getField(field);\n            if (member == null)\n            {\n                if (this.source == null)\n                {\n                    throw new CoreException(\n                            \"The PackedAtlasSerializer has not been properly assigned.\");\n                }\n                // If the field is not populated, this will trigger a load (partial or not,\n                // depending on the zip resource)\n                load(name);\n            }\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to read Atlas field {}\", name, e);\n        }\n    }\n\n    /**\n     * Save an Atlas file to a {@link ZipWritableResource}. This method uses reflection to identify\n     * all the fields in the {@link PackedAtlas}, and stores each field into a separate zip entry,\n     * named after the field itself.\n     */\n    protected void save()\n    {\n        if (this.source instanceof ZipWritableResource)\n        {\n            // Load the Atlas completely if it has not been loaded yet\n            this.atlas.getSerializer()\n                    .ifPresent(PackedAtlasSerializer::deserializeAllFieldsIfNeeded);\n            final ZipWritableResource destination = (ZipWritableResource) this.source;\n            // Isolate the metaData field\n            final Field metaData = readField(PackedAtlas.FIELD_META_DATA);\n            final Iterable<Resource> firstResource = Iterables.from(fieldTranslator(metaData));\n            final Iterable<Resource> fieldResources = fields().filter(field ->\n            {\n                final String fieldName = field.getName();\n                /*\n                 * If this atlas does not contain enhanced relation geometries, skip serialization\n                 */\n                if (!this.atlas.containsEnhancedRelationGeometry()\n                        && PackedAtlas.FIELD_RELATION_GEOMETRIES.equals(fieldName))\n                {\n                    return false;\n                }\n                return !PackedAtlas.FIELD_META_DATA.equals(fieldName)\n                        && !EXCLUDED_FIELDS.startsWithContains(fieldName)\n                        && !fieldName.contains(\"Lock\");\n            }).map(this::fieldTranslator).collect();\n            // Put the metaData field first, always.\n            final Iterable<Resource> result = new MultiIterable<>(firstResource, fieldResources);\n            destination.writeAndClose(result);\n        }\n        else\n        {\n            throw new CoreException(\"The ZipResource {} is not writable.\", this.source);\n        }\n    }\n\n    /**\n     * Assign itself as the Atlas' official serializer\n     */\n    private void assign()\n    {\n        setField(readField(PackedAtlas.FIELD_SERIALIZER), this);\n    }\n\n    /**\n     * @return True if the underlying {@link Resource} allows for random access to the serialized\n     *         fields.\n     */\n    private boolean canLoadWithRandomAccess()\n    {\n        return this.source instanceof ZipFileWritableResource;\n    }\n\n    private OutputStream compress(final OutputStream out) throws IOException\n    {\n        return out;\n    }\n\n    private InputStream decompress(final InputStream input) throws IOException\n    {\n        return input;\n    }\n\n    private void deserializeAllFields()\n    {\n        Iterables.stream(this.source.entries()).forEach(resource ->\n        {\n            final String name = resource.getName();\n            try\n            {\n                final Field field = readField(name);\n                final Object value = deserializeResource(resource, name);\n                setField(field, value);\n            }\n            catch (final MissingFieldException e)\n            {\n                // Skipping field, comes from a legacy serialized file. We however have to read it\n                // fully to move to the next one. Here we skip the selection logic of\n                // deserializeResource and just force Java deserialization\n                deserializeJavaResource(resource);\n            }\n        });\n    }\n\n    private Object deserializeJavaResource(final Resource resource)\n    {\n        try (ObjectInputStream input = new ObjectInputStream(decompress(resource.read())))\n        {\n            final Time start = Time.now();\n            final Object result = input.readObject();\n            Streams.close(input);\n            logger.trace(\"Loaded Field {} from {} in {}\", resource.getName(), this.source,\n                    start.elapsedSince());\n            return result;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not load Field {} from {}\", resource.getName(),\n                    this.source, e);\n        }\n    }\n\n    private Object deserializeProtoResource(final Resource resource, final String fieldName)\n    {\n        final Field field = readField(fieldName);\n        final Class<?> fieldClass = field.getType();\n        Constructor<?> fieldClassConstructor = null;\n\n        // We need to obtain a dummy instance of the field we want to deserialize. We then use this\n        // dummy instance as a handle to get the correct {@link ProtoAdapter}.\n        try\n        {\n            fieldClassConstructor = fieldClass.getDeclaredConstructor();\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Class {} does not implement a nullary constructor\",\n                    fieldClass.getName(), exception);\n        }\n        fieldClassConstructor.setAccessible(true);\n\n        Object handle = null;\n        try\n        {\n            handle = fieldClassConstructor.newInstance();\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Failed to create instance of {}\", fieldClass.getName(),\n                    exception);\n        }\n\n        ProtoSerializable protoHandle = null;\n        try\n        {\n            protoHandle = (ProtoSerializable) handle;\n        }\n        catch (final ClassCastException exception)\n        {\n            throw new CoreException(\"{} is not ProtoSerializable\", fieldClass.getName(), exception);\n        }\n\n        return protoHandle.getProtoAdapter().deserialize(resource.readBytesAndClose());\n    }\n\n    private Object deserializeResource(final Resource resource, final String fieldName)\n    {\n        final AtlasSerializationFormat loadFormat = this.atlas.getLoadSerializationFormat();\n        Object result = null;\n        switch (loadFormat)\n        {\n            case JAVA:\n                result = deserializeJavaResource(resource);\n                break;\n            case PROTOBUF:\n                result = deserializeProtoResource(resource, fieldName);\n                break;\n            default:\n                throw new CoreException(\"Unsupported serialization format {}\",\n                        loadFormat.toString());\n        }\n        if (result == null)\n        {\n            throw new CoreException(\"Unable to deserialize field {} from resource {} in {}.\",\n                    fieldName, resource.getName(), this.atlas.getName());\n        }\n        return result;\n    }\n\n    /**\n     * Deserialize a specific field and assign it to the Atlas.\n     *\n     * @param name\n     *            The name of the field.\n     */\n    private void deserializeSingleField(final String name)\n    {\n        final Object result;\n        if (canLoadWithRandomAccess())\n        {\n            if (PackedAtlas.FIELD_RELATION_GEOMETRIES.equals(name)\n                    && !this.atlas.containsEnhancedRelationGeometry())\n            {\n                return;\n            }\n            final Resource resource = ((ZipFileWritableResource) this.source).entryForName(name);\n            result = deserializeResource(resource, name);\n        }\n        else if (PackedAtlas.FIELD_META_DATA.equals(name))\n        {\n            // The metaData field is always the first.\n            final Iterable<Resource> resources = this.source.entries();\n            try (ZipIterator iterator = (ZipIterator) resources.iterator())\n            {\n                final Resource resource = iterator.next();\n                if (resource == null)\n                {\n                    throw new CoreException(META_DATA_ERROR_MESSAGE);\n                }\n                result = deserializeResource(resource, name);\n            }\n        }\n        else\n        {\n            throw new CoreException(\n                    \"Cannot deserialize a specific field without a ZipFileWritableResource\");\n        }\n        setField(readField(name), result);\n    }\n\n    /**\n     * The function that translates a reflection {@link Field} into a {@link Resource}\n     *\n     * @param field\n     *            The field\n     * @return The resource\n     */\n    private Resource fieldTranslator(final Field field)\n    {\n        final AtlasSerializationFormat saveFormat = this.atlas.getSaveSerializationFormat();\n\n        switch (saveFormat)\n        {\n            case JAVA:\n                final Object objectCandidate = getField(field);\n                return makeJavaResource(objectCandidate, field.getName());\n            case PROTOBUF:\n                final ProtoSerializable protoCandidate = (ProtoSerializable) getField(field);\n                return makeProtoResource(protoCandidate, field.getName());\n            default:\n                throw new CoreException(\"Unsupported serialization format {}\",\n                        saveFormat.toString());\n        }\n    }\n\n    private StreamIterable<Field> fields()\n    {\n        return Iterables.stream(Iterables.from(PackedAtlas.class.getDeclaredFields()))\n                .filter(field -> !EXCLUDED_FIELDS.startsWithContains(field.getName())).map(field ->\n                {\n                    field.setAccessible(true);\n                    return field;\n                });\n    }\n\n    private Object getField(final Field field)\n    {\n        try\n        {\n            return field.get(this.atlas);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to access field {} for {}\", field.getName(),\n                    this.atlas.getName(), e);\n        }\n    }\n\n    /**\n     * De-serialize a specific field and set it to the Atlas.\n     *\n     * @param name\n     *            The name of the field.\n     */\n    private void load(final String name)\n    {\n        if (canLoadWithRandomAccess() || PackedAtlas.FIELD_META_DATA.equals(name))\n        {\n            deserializeSingleField(name);\n        }\n        else\n        {\n            deserializeAllFields();\n        }\n    }\n\n    /**\n     * Transform a field of this Atlas into a readable {@link Resource}. The underlying\n     * implementation stores everything in a {@link ByteArrayResource}\n     *\n     * @param field\n     *            The field to translate\n     * @param name\n     *            The name of the resource\n     * @return The resource\n     */\n    private Resource makeJavaResource(final Object field, final String name)\n    {\n        // First pass read, to count the size\n        final CounterOutputStream counterOutputStream = new CounterOutputStream();\n        try (ObjectOutputStream outCounter = new ObjectOutputStream(\n                compress(new BufferedOutputStream(counterOutputStream))))\n        {\n            outCounter.writeObject(field);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not count the size of {}.\", field, e);\n        }\n        final long count = counterOutputStream.getCount();\n\n        // Second pass, write to the memory resource.\n        final ByteArrayResource resource = new ByteArrayResource(count).withName(name);\n        logger.trace(\"Saving field {}\", resource.getName());\n        if (field == null)\n        {\n            logger.warn(\"Field {} is null in atlas {} of size {}\", name, this.atlas.getName(),\n                    this.atlas.size());\n            return resource;\n        }\n        try (ObjectOutputStream out = new ObjectOutputStream(\n                compress(new BufferedOutputStream(resource.write()))))\n        {\n            out.writeObject(field);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not convert {} to a readable resource.\", field, e);\n        }\n        return resource;\n    }\n\n    private Resource makeProtoResource(final ProtoSerializable field, final String name)\n    {\n        // We automatically get the correct adapter for whatever type 'field' happens to be\n        final ProtoAdapter adapter = field.getProtoAdapter();\n        // The adapter handles all the actual serialization using the protobuf classes. Easy!\n        final byte[] byteContents = adapter.serialize(field);\n\n        final ByteArrayResource resource = new ByteArrayResource(byteContents.length)\n                .withName(name);\n\n        try (BufferedOutputStream out = new BufferedOutputStream(resource.write()))\n        {\n            out.write(byteContents);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not convert {} to a readable resource.\", field, e);\n        }\n        return resource;\n    }\n\n    private Field readField(final String name) throws MissingFieldException\n    {\n        try\n        {\n            final Field result = PackedAtlas.class.getDeclaredField(name);\n            result.setAccessible(true);\n            return result;\n        }\n        catch (final NoSuchFieldException e)\n        {\n            logger.warn(\"Unable to access field {}\", name);\n            throw new MissingFieldException(\"Unable to access field {}\", name, e);\n        }\n    }\n\n    /**\n     * Assign a field to the Atlas\n     *\n     * @param field\n     *            The field to assign\n     * @param object\n     *            The object to assign to the field.\n     */\n    private void setField(final Field field, final Object object)\n    {\n        try\n        {\n            field.set(this.atlas, object);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Cannot set field {} for Atlas {}\", field.getName(),\n                    this.atlas.getName(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedEdge.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Edge} from a {@link PackedAtlas}\n *\n * @author matthieun\n */\npublic class PackedEdge extends Edge\n{\n    private static final long serialVersionUID = -7425733302988626570L;\n\n    private final long index;\n\n    protected PackedEdge(final PackedAtlas atlas, final long index)\n    {\n        super(atlas);\n        this.index = index;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return packedAtlas().edgePolyLine(this.index);\n    }\n\n    @Override\n    public Node end()\n    {\n        return packedAtlas().edgeEndNode(this.index);\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return packedAtlas().edgeIdentifier(this.index);\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return packedAtlas().edgeTags(this.index);\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return packedAtlas().edgeRelations(this.index);\n    }\n\n    @Override\n    public Node start()\n    {\n        return packedAtlas().edgeStartNode(this.index);\n    }\n\n    private PackedAtlas packedAtlas()\n    {\n        return (PackedAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedLine.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Edge} from a {@link PackedAtlas}\n *\n * @author matthieun\n */\npublic class PackedLine extends Line\n{\n    private static final long serialVersionUID = 3087755941210424968L;\n\n    private final long index;\n\n    protected PackedLine(final PackedAtlas atlas, final long index)\n    {\n        super(atlas);\n        this.index = index;\n    }\n\n    @Override\n    public PolyLine asPolyLine()\n    {\n        return packedAtlas().linePolyLine(this.index);\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return packedAtlas().lineIdentifier(this.index);\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return packedAtlas().lineTags(this.index);\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return packedAtlas().lineRelations(this.index);\n    }\n\n    private PackedAtlas packedAtlas()\n    {\n        return (PackedAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedNode.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedSet;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Node} built from a {@link PackedAtlas}\n *\n * @author matthieun\n */\npublic class PackedNode extends Node\n{\n    private static final long serialVersionUID = -4505441893548672843L;\n\n    private final long index;\n\n    protected PackedNode(final PackedAtlas atlas, final long index)\n    {\n        super(atlas);\n        this.index = index;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return packedAtlas().nodeIdentifier(this.index);\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return packedAtlas().nodeLocation(this.index);\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return packedAtlas().nodeTags(this.index);\n    }\n\n    @Override\n    public SortedSet<Edge> inEdges()\n    {\n        return packedAtlas().nodeInEdges(this.index);\n    }\n\n    @Override\n    public SortedSet<Edge> outEdges()\n    {\n        return packedAtlas().nodeOutEdges(this.index);\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return packedAtlas().nodeRelations(this.index);\n    }\n\n    private PackedAtlas packedAtlas()\n    {\n        return (PackedAtlas) getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedPoint.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * {@link Edge} from a {@link PackedAtlas}\n *\n * @author matthieun\n */\npublic class PackedPoint extends Point\n{\n    private static final long serialVersionUID = -7143958478767647582L;\n\n    private final long index;\n\n    protected PackedPoint(final PackedAtlas atlas, final long index)\n    {\n        super(atlas);\n        this.index = index;\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return packedAtlas().pointIdentifier(this.index);\n    }\n\n    @Override\n    public Location getLocation()\n    {\n        return packedAtlas().pointLocation(this.index);\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return packedAtlas().pointTags(this.index);\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return packedAtlas().pointRelations(this.index);\n    }\n\n    private PackedAtlas packedAtlas()\n    {\n        return (PackedAtlas) this.getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedRelation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\n\n/**\n * @author matthieun\n */\npublic class PackedRelation extends Relation\n{\n    private static final long serialVersionUID = -1912941368972318403L;\n\n    private final long index;\n\n    protected PackedRelation(final PackedAtlas atlas, final long index)\n    {\n        super(atlas);\n        this.index = index;\n    }\n\n    @Override\n    public RelationMemberList allKnownOsmMembers()\n    {\n        return packedAtlas().relationAllKnownOsmMembers(this.index);\n    }\n\n    @Override\n    public List<Relation> allRelationsWithSameOsmIdentifier()\n    {\n        return packedAtlas().relationAllRelationsWithSameOsmIdentifier(this.index);\n    }\n\n    @Override\n    public Optional<MultiPolygon> asMultiPolygon()\n    {\n        // return the previously stored result\n        if (this.getBadGeom() || this.getGeom() != null)\n        {\n            return Optional.ofNullable(this.getGeom());\n        }\n        MultiPolygon relationGeometry = null;\n        if (packedAtlas().containsEnhancedRelationGeometry())\n        {\n            relationGeometry = packedAtlas().relationGeometry(this.index);\n            this.setGeom(relationGeometry);\n        }\n        if (relationGeometry == null)\n        {\n            return super.asMultiPolygon();\n        }\n        return Optional.ofNullable(relationGeometry);\n    }\n\n    @Override\n    public long getIdentifier()\n    {\n        return packedAtlas().relationIdentifier(this.index);\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return packedAtlas().relationTags(this.index);\n    }\n\n    @Override\n    public RelationMemberList members()\n    {\n        return packedAtlas().relationMembers(this.index);\n    }\n\n    @Override\n    public Long osmRelationIdentifier()\n    {\n        return packedAtlas().relationOsmIdentifier(this.index);\n    }\n\n    @Override\n    public Set<Relation> relations()\n    {\n        return packedAtlas().relationRelations(this.index);\n    }\n\n    private PackedAtlas packedAtlas()\n    {\n        return (PackedAtlas) getAtlas();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedTagStore.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoPackedTagStoreAdapter;\nimport org.openstreetmap.atlas.utilities.arrays.Arrays;\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.compression.IntegerDictionary;\n\n/**\n * Store OSM Key-Value pairs, relying on the sub-class to provide Dictionaries. This allows for\n * sharing dictionaries if necessary. The key/value storage is in arrays to minimize space, which\n * assumes each item will have a reasonably small number of key-value pairs.\n *\n * @author matthieun\n * @author lcram\n */\npublic class PackedTagStore implements Serializable, ProtoSerializable\n{\n    // Keep track of the field names for reflection code in the ProtoAdapter\n    public static final String FIELD_KEYS = \"keys\";\n    public static final String FIELD_VALUES = \"values\";\n    public static final String FIELD_INDEX = \"index\";\n    private static final long serialVersionUID = -5240324410665237846L;\n    private final IntegerArrayOfArrays keys;\n    private final IntegerArrayOfArrays values;\n    private transient IntegerDictionary<String> dictionary;\n\n    private long index = 0L;\n\n    public PackedTagStore()\n    {\n        this.keys = null;\n        this.values = null;\n        this.dictionary = null;\n    }\n\n    public PackedTagStore(final long maximumSize, final int memoryBlockSize, final int subArraySize,\n            final IntegerDictionary<String> dictionary)\n    {\n        this.keys = new IntegerArrayOfArrays(maximumSize, memoryBlockSize, subArraySize);\n        this.values = new IntegerArrayOfArrays(maximumSize, memoryBlockSize, subArraySize);\n        this.dictionary = dictionary;\n    }\n\n    /**\n     * Add a key/value pair at the specified index\n     *\n     * @param index\n     *            The index\n     * @param key\n     *            The key\n     * @param value\n     *            The value\n     */\n    public void add(final long index, final String key, final String value)\n    {\n        if (index > size())\n        {\n            throw new CoreException(\"Cannot add. Invalid index {} is bigger than the size {}\",\n                    index, size());\n        }\n        final int keyIndex = keysDictionary().add(key);\n        final int valueIndex = valuesDictionary().add(value);\n        final int[] keyArray;\n        final int[] valueArray;\n        if (index == this.index)\n        {\n            // We are adding a new row\n            if (key == null || value == null)\n            {\n                keyArray = new int[0];\n                valueArray = new int[0];\n            }\n            else\n            {\n                keyArray = new int[1];\n                valueArray = new int[1];\n                keyArray[0] = keyIndex;\n                valueArray[0] = valueIndex;\n            }\n            this.keys.add(keyArray);\n            this.values.add(valueArray);\n            this.index++;\n        }\n        else\n        {\n            // We are adding a key/value pair to an existing item\n            if (key == null || value == null)\n            {\n                // Do not add anything\n                return;\n            }\n            keyArray = Arrays.addNewItem(this.keys.get(index), keyIndex);\n            valueArray = Arrays.addNewItem(this.values.get(index), valueIndex);\n            this.keys.set(index, keyArray);\n            this.values.set(index, valueArray);\n        }\n    }\n\n    /**\n     * @param index\n     *            The index to check for\n     * @param key\n     *            The key to test the presence of\n     * @return True if the key is present at the specified index\n     */\n    public boolean containsKey(final long index, final String key)\n    {\n        if (key == null)\n        {\n            throw new CoreException(\"Cannot test if a null key is contained\");\n        }\n        final int[] keyArray = this.keys.get(index);\n        for (final int keyIndex : keyArray)\n        {\n            if (key.equals(keysDictionary().word(keyIndex)))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof PackedTagStore)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            final PackedTagStore that = (PackedTagStore) other;\n            if (!this.keys.equals(that.keys))\n            {\n                return false;\n            }\n            if (!this.values.equals(that.values))\n            {\n                return false;\n            }\n            if (!Objects.equals(this.keysDictionary(), that.keysDictionary()))\n            {\n                return false;\n            }\n            if (!Objects.equals(this.valuesDictionary(), that.valuesDictionary()))\n            {\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * @param index\n     *            The index to check for\n     * @param key\n     *            The key to get the value from\n     * @return The value for the specified key at the specified index. Returns null if the key is\n     *         not present.\n     */\n    public String get(final long index, final String key)\n    {\n        if (key == null)\n        {\n            throw new CoreException(\"Cannot get a null key's value\");\n        }\n        final int[] keyArray = this.keys.get(index);\n        for (int i = 0; i < keyArray.length; i++)\n        {\n            final int keyIndex = keyArray[i];\n            if (key.equals(keysDictionary().word(keyIndex)))\n            {\n                final int valueIndex = this.values.get(index)[i];\n                return valuesDictionary().word(valueIndex);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoPackedTagStoreAdapter();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final int initialPrime = 31;\n        final int hashSeed = 37;\n\n        int hash = hashSeed * initialPrime + this.keys.hashCode();\n        hash = hashSeed * hash + this.values.hashCode();\n\n        final int keysDictionaryHash = this.keysDictionary() == null ? 0\n                : this.keysDictionary().hashCode();\n        final int valuesDictionaryHash = this.valuesDictionary() == null ? 0\n                : this.valuesDictionary().hashCode();\n\n        hash = hashSeed * hash + keysDictionaryHash;\n        hash = hashSeed * hash + valuesDictionaryHash;\n\n        return hash;\n    }\n\n    /**\n     * @param index\n     *            The index to look for\n     * @return All the keys at a specified index\n     */\n    public Set<String> keySet(final long index)\n    {\n        final Set<String> result = new HashSet<>();\n        final int[] keyArray = this.keys.get(index);\n        for (final int keyIndex : keyArray)\n        {\n            result.add(keysDictionary().word(keyIndex));\n        }\n        return result;\n    }\n\n    /**\n     * @param index\n     *            The index to look for\n     * @return All the key/value pairs at a specified index\n     */\n    public Map<String, String> keyValuePairs(final long index)\n    {\n        if (null == this.keys || this.keys.isEmpty())\n        {\n            // No tags\n            return new HashMap<>();\n        }\n        final int[] keyArray = this.keys.get(index);\n        final int[] valueArray = this.values.get(index);\n        final Map<String, String> result = new HashMap<>(keyArray.length);\n        for (int i = 0; i < keyArray.length; i++)\n        {\n            final int keyIndex = keyArray[i];\n            final int valueIndex = valueArray[i];\n            result.put(keysDictionary().word(keyIndex), valuesDictionary().word(valueIndex));\n        }\n        return result;\n    }\n\n    /**\n     * @return The dictionary for keys\n     */\n    public IntegerDictionary<String> keysDictionary()\n    {\n        return this.dictionary;\n    }\n\n    public void setDictionary(final IntegerDictionary<String> dictionary)\n    {\n        this.dictionary = dictionary;\n    }\n\n    /**\n     * @return The size of this tag store\n     */\n    public long size()\n    {\n        return this.index;\n    }\n\n    public void trim()\n    {\n        this.keys.trim();\n        this.values.trim();\n    }\n\n    /**\n     * @return The dictionary for values\n     */\n    public IntegerDictionary<String> valuesDictionary()\n    {\n        return this.dictionary;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/packed/README.md",
    "content": "# `PackedAtlas`\n\nThis is the before `Atlas`. It is immutable, and stores all its items in large arrays in memory.\n\n## Array Storage\n\nAll information is kept in large arrays in memory. Each array contains data for a specific type, with each index in the array always representing the same feature. For example, there is an array that contains the `Edge` start nodes indices, another one that contains the `Edge` end node indices. In each of those two arrays, the item at index 3 is always in reference to the same `Edge` with OSM identifier 123. Here are all the most important arrays in the PackedAtlas:\n\n* `dictionary`\n* `edgeIdentifiers`\n* `nodeIdentifiers`\n* `areaIdentifiers`\n* `lineIdentifiers`\n* `pointIdentifiers`\n* `relationIdentifiers`\n* `edgeIdentifierToEdgeArrayIndex`\n* `nodeIdentifierToNodeArrayIndex`\n* `areaIdentifierToAreaArrayIndex`\n* `lineIdentifierToLineArrayIndex`\n* `pointIdentifierToPointArrayIndex`\n* `relationIdentifierToRelationArrayIndex`\n* `nodeLocations`\n* `nodeInEdgesIndices`\n* `nodeOutEdgesIndices`\n* `nodeTags`\n* `nodeIndexToRelationIndices`\n* `edgeStartNodeIndex`\n* `edgeEndNodeIndex`\n* `edgePolyLines`\n* `edgeTags`\n* `edgeIndexToRelationIndices`\n* `areaPolygons`\n* `areaTags`\n* `areaIndexToRelationIndices`\n* `linePolyLines`\n* `lineTags`\n* `lineIndexToRelationIndices`\n* `pointLocations`\n* `pointTags`\n* `pointIndexToRelationIndices`\n* `relationMemberIndices`\n* `relationMemberTypes`\n* `relationMemberRoles`\n* `relationTags`\n* `relationIndexToRelationIndices`\n* `relationOsmIdentifierToRelationIdentifiers`\n* `relationOsmIdentifiers`\n\n## Flyweight Atlas features\n\nAll Atlas features are following the flyweight design pattern. What that means is every [`PackedEdge`](/src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedEdge.java), [`PackedNode`](/src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedNode.java), [`PackedArea`](/src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedArea.java), [`PackedLine`](/src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedLine.java), [`PackedPoint`](/src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedPoint.java) or [`PackedRelation`](/src/main/java/org/openstreetmap/atlas/geography/atlas/packed/PackedRelation.java) contains only two things: a reference to the Atlas object it belongs to, and the index it is positioned at in all the arrays in that Atlas. This makes the feature objects really lightweight and fast to create.\n\nWhen a user asks for the incoming edges to a `PackedNode`, then the `PackedNode` relays the query to its own `PackedAtlas` along with its index, and the `PackedAtlas` returns the result which is relayed to the user by the `PackedNode` object.\n\n## Serialization / De-serialization\n\nA `PackedAtlas` can be serialized to a `WritableResource` using the `PackedAtlasSerializer`. During that process, all the arrays are pushed to a non-compressed zip stream in which each array is a zip entry with the same name. Each array is serialized in its zip entry using either standard Java serialization or protobuf.\n\nIn case the `Resource` is a file, then the user has random access to each zip entry. That means that all the arrays are lazily de-serialized only when needed. This is extremely useful when  opening an Atlas file just to check `Node` connectivity for example. Only the arrays relative with `Node`s and `Edge`s will be loaded.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/AtlasLoadingOption.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.tags.filters.ConfiguredTaggableFilter;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\n\n/**\n * With this {@link AtlasLoadingOption} you can specify which feature you want to load to Atlas\n *\n * @author tony\n */\npublic final class AtlasLoadingOption implements Serializable\n{\n    private static final long serialVersionUID = 1811691207451027561L;\n\n    public static final String ATLAS_EDGE_FILTER_NAME = \"atlas-edge\";\n    public static final String ATLAS_AREA_FILTER_NAME = \"atlas-area\";\n    public static final String ATLAS_RELATION_SLICING_FILTER_NAME = \"atlas-relation-slicing\";\n    public static final String ATLAS_RELATION_SLICING_CONSOLIDATE_FILTER_NAME = \"atlas-relation-slicing-consolidate\";\n    public static final String ATLAS_WAY_SECTION_FILTER_NAME = \"atlas-way-section\";\n\n    private static final BridgeConfiguredFilter DEFAULT_EDGE_FILTER = new BridgeConfiguredFilter(\"\",\n            ATLAS_EDGE_FILTER_NAME,\n            new StandardConfiguration(new InputStreamResource(() -> AtlasLoadingOption.class\n                    .getResourceAsStream(ATLAS_EDGE_FILTER_NAME + FileSuffix.JSON.toString()))));\n    private static final BridgeConfiguredFilter DEFAULT_AREA_FILTER = new BridgeConfiguredFilter(\"\",\n            ATLAS_AREA_FILTER_NAME,\n            new StandardConfiguration(new InputStreamResource(() -> AtlasLoadingOption.class\n                    .getResourceAsStream(ATLAS_AREA_FILTER_NAME + FileSuffix.JSON.toString()))));\n\n    private static final BridgeConfiguredFilter DEFAULT_RELATION_SLICING_FILTER = new BridgeConfiguredFilter(\n            \"\", ATLAS_RELATION_SLICING_FILTER_NAME,\n            new StandardConfiguration(\n                    new InputStreamResource(() -> AtlasLoadingOption.class.getResourceAsStream(\n                            ATLAS_RELATION_SLICING_FILTER_NAME + FileSuffix.JSON.toString()))));\n\n    private static final BridgeConfiguredFilter DEFAULT_RELATION_SLICING_CONSOLIDATE_FILTER = new BridgeConfiguredFilter(\n            \"\", ATLAS_RELATION_SLICING_CONSOLIDATE_FILTER_NAME,\n            new StandardConfiguration(\n                    new InputStreamResource(() -> AtlasLoadingOption.class.getResourceAsStream(\n                            ATLAS_RELATION_SLICING_FILTER_NAME + FileSuffix.JSON.toString()))));\n\n    private static final BridgeConfiguredFilter DEFAULT_WAY_SECTION_FILTER = new BridgeConfiguredFilter(\n            \"\", ATLAS_WAY_SECTION_FILTER_NAME,\n            new StandardConfiguration(\n                    new InputStreamResource(() -> AtlasLoadingOption.class.getResourceAsStream(\n                            ATLAS_WAY_SECTION_FILTER_NAME + FileSuffix.JSON.toString()))));\n    private static final ConfiguredTaggableFilter DEFAULT_OSM_PBF_WAY_FILTER = new ConfiguredTaggableFilter(\n            new StandardConfiguration(new InputStreamResource(\n                    () -> AtlasLoadingOption.class.getResourceAsStream(\"osm-pbf-way.json\"))));\n    private static final ConfiguredTaggableFilter DEFAULT_OSM_PBF_NODE_FILTER = new ConfiguredTaggableFilter(\n            new StandardConfiguration(new InputStreamResource(\n                    () -> AtlasLoadingOption.class.getResourceAsStream(\"osm-pbf-node.json\"))));\n    private static final ConfiguredTaggableFilter DEFAULT_OSM_PBF_RELATION_FILTER = new ConfiguredTaggableFilter(\n            new StandardConfiguration(new InputStreamResource(\n                    () -> AtlasLoadingOption.class.getResourceAsStream(\"osm-pbf-relation.json\"))));\n\n    private boolean loadAtlasPoint;\n    private boolean loadAtlasLine;\n    private boolean loadAtlasArea;\n    private boolean loadAtlasNode;\n    private boolean loadAtlasEdge;\n    private BridgeConfiguredFilter edgeFilter = DEFAULT_EDGE_FILTER;\n    private BridgeConfiguredFilter areaFilter = DEFAULT_AREA_FILTER;\n    private BridgeConfiguredFilter waySectionFilter = DEFAULT_WAY_SECTION_FILTER;\n    private ConfiguredTaggableFilter osmPbfWayFilter = DEFAULT_OSM_PBF_WAY_FILTER;\n    private ConfiguredTaggableFilter osmPbfNodeFilter = DEFAULT_OSM_PBF_NODE_FILTER;\n    private ConfiguredTaggableFilter osmPbfRelationFilter = DEFAULT_OSM_PBF_RELATION_FILTER;\n    private BridgeConfiguredFilter relationSlicingFilter = DEFAULT_RELATION_SLICING_FILTER;\n    private BridgeConfiguredFilter relationSlicingConsolidateFilter = DEFAULT_RELATION_SLICING_CONSOLIDATE_FILTER;\n\n    private boolean loadAtlasRelation;\n    private boolean loadOsmBound;\n    private boolean countrySlicing;\n    /** Used to indicate that all objects should be kept */\n    private boolean keepAll;\n    private boolean waySectioning;\n    private boolean loadWaysSpanningCountryBoundaries;\n    private String countryCode;\n\n    private CountryBoundaryMap countryBoundaryMap;\n\n    public static AtlasLoadingOption createOptionWithAllEnabled(\n            final CountryBoundaryMap countryBoundaryMap)\n    {\n        final AtlasLoadingOption option = new AtlasLoadingOption();\n        option.setCountrySlicing(true);\n        option.setWaySectioning(true);\n        option.setCountryBoundaryMap(countryBoundaryMap);\n        return option;\n    }\n\n    public static AtlasLoadingOption createOptionWithNoSlicing()\n    {\n        return new AtlasLoadingOption();\n    }\n\n    public static AtlasLoadingOption createOptionWithOnlyNodesAndWayNoSlicing()\n    {\n        final AtlasLoadingOption option = new AtlasLoadingOption();\n        option.setLoadAtlasPoint(false);\n        option.setLoadAtlasLine(false);\n        option.setLoadAtlasArea(false);\n        option.setLoadAtlasRelation(false);\n        return option;\n    }\n\n    public static AtlasLoadingOption createOptionWithOnlyNodesAndWaysAndSlicing(\n            final CountryBoundaryMap countryBoundaryMap)\n    {\n        final AtlasLoadingOption option = new AtlasLoadingOption();\n        option.setLoadAtlasPoint(false);\n        option.setLoadAtlasLine(false);\n        option.setLoadAtlasArea(false);\n        option.setLoadAtlasRelation(false);\n        option.setCountrySlicing(true);\n        option.setWaySectioning(true);\n        option.setCountryBoundaryMap(countryBoundaryMap);\n        return option;\n    }\n\n    public static AtlasLoadingOption createOptionWithOnlySectioning()\n    {\n        final AtlasLoadingOption option = new AtlasLoadingOption();\n        option.setCountrySlicing(false);\n        option.setWaySectioning(true);\n        return option;\n    }\n\n    public static AtlasLoadingOption withNoFilter()\n    {\n        final StringResource pbfFilter = new StringResource(\"{\\\"filters\\\":[]}\");\n        final ConfiguredTaggableFilter filter = new ConfiguredTaggableFilter(\n                new StandardConfiguration(pbfFilter));\n        final AtlasLoadingOption atlasLoadingOption = new AtlasLoadingOption();\n        atlasLoadingOption.setOsmPbfWayFilter(filter);\n        atlasLoadingOption.setOsmPbfNodeFilter(filter);\n        atlasLoadingOption.setOsmPbfRelationFilter(filter);\n        atlasLoadingOption.setWaySectioning(true);\n        return atlasLoadingOption;\n    }\n\n    private AtlasLoadingOption()\n    {\n        this.loadAtlasPoint = true;\n        this.loadAtlasNode = true;\n        this.loadAtlasLine = true;\n        this.loadAtlasEdge = true;\n        this.loadAtlasArea = true;\n        this.loadAtlasRelation = true;\n        this.loadOsmBound = true;\n        this.countrySlicing = false;\n        this.keepAll = false;\n        this.waySectioning = false;\n        this.loadWaysSpanningCountryBoundaries = true;\n        this.countryBoundaryMap = null;\n    }\n\n    public BridgeConfiguredFilter getAreaFilter()\n    {\n        return this.areaFilter;\n    }\n\n    public CountryBoundaryMap getCountryBoundaryMap()\n    {\n        return this.countryBoundaryMap;\n    }\n\n    public String getCountryCode()\n    {\n        return this.countryCode;\n    }\n\n    public BridgeConfiguredFilter getEdgeFilter()\n    {\n        return this.edgeFilter;\n    }\n\n    public ConfiguredTaggableFilter getOsmPbfNodeFilter()\n    {\n        return this.osmPbfNodeFilter;\n    }\n\n    public ConfiguredTaggableFilter getOsmPbfRelationFilter()\n    {\n        return this.osmPbfRelationFilter;\n    }\n\n    public ConfiguredTaggableFilter getOsmPbfWayFilter()\n    {\n        return this.osmPbfWayFilter;\n    }\n\n    public BridgeConfiguredFilter getRelationSlicingConsolidateFilter()\n    {\n        return this.relationSlicingConsolidateFilter;\n    }\n\n    public BridgeConfiguredFilter getRelationSlicingFilter()\n    {\n        return this.relationSlicingFilter;\n    }\n\n    public BridgeConfiguredFilter getWaySectionFilter()\n    {\n        return this.waySectionFilter;\n    }\n\n    public boolean isCountrySlicing()\n    {\n        return this.countrySlicing;\n    }\n\n    /**\n     * Check to see if the atlas should not be filtered or deduplicated. This option takes\n     * precedence over all filtering options.\n     *\n     * @return {@code true} if we should not drop any items\n     */\n    public boolean isKeepAll()\n    {\n        return this.keepAll;\n    }\n\n    public boolean isLoadAtlasArea()\n    {\n        return this.loadAtlasArea;\n    }\n\n    public boolean isLoadAtlasEdge()\n    {\n        return this.loadAtlasEdge;\n    }\n\n    public boolean isLoadAtlasLine()\n    {\n        return this.loadAtlasLine;\n    }\n\n    public boolean isLoadAtlasNode()\n    {\n        return this.loadAtlasNode;\n    }\n\n    public boolean isLoadAtlasPoint()\n    {\n        return this.loadAtlasPoint;\n    }\n\n    public boolean isLoadAtlasRelation()\n    {\n        return this.loadAtlasRelation;\n    }\n\n    public boolean isLoadOsmBound()\n    {\n        return this.loadOsmBound;\n    }\n\n    public boolean isLoadOsmNode()\n    {\n        return isLoadAtlasNode() || isLoadAtlasPoint();\n    }\n\n    public boolean isLoadOsmRelation()\n    {\n        return isLoadAtlasRelation();\n    }\n\n    public boolean isLoadOsmWay()\n    {\n        return isLoadAtlasEdge() || isLoadAtlasLine();\n    }\n\n    public boolean isLoadWaysSpanningCountryBoundaries()\n    {\n        return this.loadWaysSpanningCountryBoundaries;\n    }\n\n    public boolean isWaySectioning()\n    {\n        return this.waySectioning;\n    }\n\n    public void setAreaFilter(final BridgeConfiguredFilter areaFilter)\n    {\n        this.areaFilter = areaFilter;\n    }\n\n    public void setCountryBoundaryMap(final CountryBoundaryMap countryBoundaryMap)\n    {\n        this.countryBoundaryMap = countryBoundaryMap;\n    }\n\n    public AtlasLoadingOption setCountryCode(final String countryCode)\n    {\n        this.countryCode = countryCode;\n        return this;\n    }\n\n    public AtlasLoadingOption setCountrySlicing(final boolean isCountrySlicing)\n    {\n        this.countrySlicing = isCountrySlicing;\n        return this;\n    }\n\n    public void setEdgeFilter(final BridgeConfiguredFilter edgeFilter)\n    {\n        this.edgeFilter = edgeFilter;\n    }\n\n    /**\n     * Set whether or not all objects should be kept, regardless of filters.\n     *\n     * @param isKeepAll\n     *            {@code true} to keep all objects\n     * @return {@code this}, for easy chaining\n     */\n    public AtlasLoadingOption setKeepAll(final boolean isKeepAll)\n    {\n        this.keepAll = isKeepAll;\n        return this;\n    }\n\n    public AtlasLoadingOption setLoadAtlasArea(final boolean isLoadAtlasArea)\n    {\n        this.loadAtlasArea = isLoadAtlasArea;\n        return this;\n    }\n\n    public AtlasLoadingOption setLoadAtlasEdge(final boolean isLoadAtlasEdge)\n    {\n        this.loadAtlasEdge = isLoadAtlasEdge;\n        return this;\n    }\n\n    public AtlasLoadingOption setLoadAtlasLine(final boolean isLoadAtlasLine)\n    {\n        this.loadAtlasLine = isLoadAtlasLine;\n        return this;\n    }\n\n    public AtlasLoadingOption setLoadAtlasNode(final boolean isLoadAtlasNode)\n    {\n        this.loadAtlasNode = isLoadAtlasNode;\n        return this;\n    }\n\n    public AtlasLoadingOption setLoadAtlasPoint(final boolean isLoadAtlasPoint)\n    {\n        this.loadAtlasPoint = isLoadAtlasPoint;\n        return this;\n    }\n\n    public AtlasLoadingOption setLoadAtlasRelation(final boolean isLoadAtlasRelation)\n    {\n        this.loadAtlasRelation = isLoadAtlasRelation;\n        return this;\n    }\n\n    public AtlasLoadingOption setLoadOsmBound(final boolean isLoadOsmBound)\n    {\n        this.loadOsmBound = isLoadOsmBound;\n        return this;\n    }\n\n    public AtlasLoadingOption setLoadWaysSpanningCountryBoundaries(\n            final boolean loadWaysSpanningCountryBoundaries)\n    {\n        this.loadWaysSpanningCountryBoundaries = loadWaysSpanningCountryBoundaries;\n        return this;\n    }\n\n    public void setOsmPbfNodeFilter(final ConfiguredTaggableFilter osmPbfNodeFilter)\n    {\n        this.osmPbfNodeFilter = osmPbfNodeFilter;\n    }\n\n    public void setOsmPbfRelationFilter(final ConfiguredTaggableFilter osmPbfRelationFilter)\n    {\n        this.osmPbfRelationFilter = osmPbfRelationFilter;\n    }\n\n    public void setOsmPbfWayFilter(final ConfiguredTaggableFilter osmPbfWayFilter)\n    {\n        this.osmPbfWayFilter = osmPbfWayFilter;\n    }\n\n    public void setRelationSlicingConsolidateFilter(\n            final BridgeConfiguredFilter relationSlicingConsolidateFilter)\n    {\n        this.relationSlicingConsolidateFilter = relationSlicingConsolidateFilter;\n    }\n\n    public void setRelationSlicingFilter(final BridgeConfiguredFilter relationSlicingFilter)\n    {\n        this.relationSlicingFilter = relationSlicingFilter;\n    }\n\n    public void setWaySectionFilter(final BridgeConfiguredFilter waySectionFilter)\n    {\n        this.waySectionFilter = waySectionFilter;\n    }\n\n    public AtlasLoadingOption setWaySectioning(final boolean isWaySectioning)\n    {\n        this.waySectioning = isWaySectioning;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/BridgeConfiguredFilter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.ConfiguredTaggableFilter;\nimport org.openstreetmap.atlas.utilities.configuration.Configuration;\nimport org.openstreetmap.atlas.utilities.configuration.ConfigurationReader;\nimport org.openstreetmap.atlas.utilities.configuration.ConfiguredFilter;\n\nimport com.google.common.collect.Lists;\n\n/**\n * This class is a bridge between {@link ConfiguredTaggableFilter} and {@link ConfiguredFilter} for\n * use in {@link AtlasLoadingOption}, as a backwards-compatible option until\n * {@link ConfiguredFilter} is the only option used.\n *\n * @author matthieun\n */\npublic class BridgeConfiguredFilter implements Predicate<AtlasEntity>, Serializable\n{\n    private static final long serialVersionUID = -1496420126649881929L;\n    private static final String EMPTY_MARKER = \"N/A\";\n\n    private ConfiguredTaggableFilter configuredTaggableFilter;\n    private ConfiguredFilter configuredFilter;\n\n    public BridgeConfiguredFilter(final String root, final String name,\n            final Configuration configuration)\n    {\n        final ConfigurationReader reader = new ConfigurationReader(\"\");\n        final List<String> configuredTaggableFilterFilters = reader.configurationValues(\n                configuration, ConfiguredTaggableFilter.FILTERS_CONFIGURATION_NAME,\n                Lists.newArrayList(EMPTY_MARKER));\n        if (configuredTaggableFilterFilters.size() == 1\n                && EMPTY_MARKER.equals(configuredTaggableFilterFilters.get(0)))\n        {\n            // It is a new ConfiguredFilter\n            this.configuredFilter = ConfiguredFilter.from(root, name, configuration);\n        }\n        else\n        {\n            // It is a legacy ConfiguredTaggableFilter\n            this.configuredTaggableFilter = new ConfiguredTaggableFilter(configuration);\n        }\n    }\n\n    @Override\n    public boolean test(final AtlasEntity atlasEntity)\n    {\n        return this.configuredFilter != null ? this.configuredFilter.test(atlasEntity)\n                : this.configuredTaggableFilter.test(atlasEntity);\n\n    }\n\n    public boolean test(final Taggable taggable)\n    {\n        return this.configuredFilter != null ? this.configuredFilter.test(taggable)\n                : this.configuredTaggableFilter.test(taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/CloseableOsmosisReader.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport crosby.binary.osmosis.OsmosisReader;\n\n/**\n * {@link Closeable} version of an {@link OsmosisReader} that prevents {@link InputStream} leaks.\n *\n * @author matthieun\n */\npublic class CloseableOsmosisReader extends OsmosisReader implements Closeable\n{\n    private final InputStream inputStream;\n\n    public CloseableOsmosisReader(final InputStream input)\n    {\n        super(input);\n        this.inputStream = input;\n    }\n\n    @Override\n    public void close() throws IOException\n    {\n        this.inputStream.close();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/converters/TagMapToTagCollectionConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.converters;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Tag;\n\n/**\n * @author matthieun\n */\npublic class TagMapToTagCollectionConverter\n        implements Converter<Map<String, String>, Collection<Tag>>\n{\n    @Override\n    public Collection<Tag> convert(final Map<String, String> object)\n    {\n        final List<Tag> result = new ArrayList<>();\n        object.forEach((key, value) -> result.add(new Tag(key, value)));\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/slicing/identifier/AbstractIdentifierFactory.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Identifier management like create new way, node and relation identifier\n *\n * @author tony\n */\npublic abstract class AbstractIdentifierFactory\n{\n    public static final long IDENTIFIER_SCALE_DEFAULT = 1000;\n\n    private final long[] referenceIdentifiers;\n    private int index;\n    private long delta;\n    private final long identifierScale;\n\n    public AbstractIdentifierFactory(final long referenceIdentifier)\n    {\n        this(new long[] { referenceIdentifier });\n    }\n\n    public AbstractIdentifierFactory(final long referenceIdentifier, final long identifierScale)\n    {\n        this(new long[] { referenceIdentifier }, identifierScale);\n    }\n\n    public AbstractIdentifierFactory(final long[] referenceIdentifierArray)\n    {\n        this.referenceIdentifiers = referenceIdentifierArray;\n        this.delta = 0;\n        this.index = 0;\n        this.identifierScale = IDENTIFIER_SCALE_DEFAULT;\n    }\n\n    public AbstractIdentifierFactory(final long[] referenceIdentifierArray,\n            final long identifierScale)\n    {\n        this.referenceIdentifiers = referenceIdentifierArray;\n        this.delta = 0;\n        this.index = 0;\n        this.identifierScale = identifierScale;\n    }\n\n    public long getDelta()\n    {\n        return this.delta;\n    }\n\n    public long getIdentifierScale()\n    {\n        return this.identifierScale;\n    }\n\n    public long getReferenceIdentifier()\n    {\n        return this.referenceIdentifiers[this.index];\n    }\n\n    public boolean hasMore()\n    {\n        return this.delta < this.identifierScale - 1\n                || this.index < this.referenceIdentifiers.length - 1;\n    }\n\n    public abstract long nextIdentifier();\n\n    protected void incrementDelta()\n    {\n        this.delta++;\n\n        if (this.delta >= this.identifierScale)\n        {\n            if (this.index < this.referenceIdentifiers.length - 1)\n            {\n                this.index++;\n                this.delta = 1;\n            }\n            else\n            {\n                throw new CoreException(\"Entity {} has been split into more than 999 pieces\",\n                        this.referenceIdentifiers);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/slicing/identifier/CountrySlicingIdentifierFactory.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier;\n\n/**\n * Identifier factory for country slicing\n *\n * @author tony\n */\npublic class CountrySlicingIdentifierFactory extends AbstractIdentifierFactory\n{\n    public CountrySlicingIdentifierFactory(final long referenceIdentifier)\n    {\n        super(referenceIdentifier);\n    }\n\n    public CountrySlicingIdentifierFactory(final long[] referenceIdentifiers)\n    {\n        super(referenceIdentifiers);\n    }\n\n    @Override\n    public long nextIdentifier()\n    {\n        incrementDelta();\n        return (getReferenceIdentifier() / IDENTIFIER_SCALE_DEFAULT + getDelta())\n                * IDENTIFIER_SCALE_DEFAULT;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/slicing/identifier/PaddingIdentifierFactory.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier;\n\n/**\n * IdentifierFactory for padding only\n *\n * @author tony\n */\npublic final class PaddingIdentifierFactory\n{\n    private static final long IDENTIFIER_PADDING = 1_000_000;\n\n    public static long pad(final long identifier)\n    {\n        return identifier * IDENTIFIER_PADDING;\n    }\n\n    private PaddingIdentifierFactory()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/slicing/identifier/PointIdentifierFactory.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier;\n\n/**\n * Identifier factory for points (mainly used for synthetic boundary nodes)\n *\n * @author samg\n */\npublic class PointIdentifierFactory extends AbstractIdentifierFactory\n{\n    private static final long IDENFITIER_SCALE = 1000000;\n\n    public PointIdentifierFactory(final long referenceIdentifier)\n    {\n        super(referenceIdentifier, IDENFITIER_SCALE);\n    }\n\n    @Override\n    public long nextIdentifier()\n    {\n        incrementDelta();\n        return getReferenceIdentifier() + getDelta();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/slicing/identifier/ReverseIdentifierFactory.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier;\n\n/**\n * Common methods to extract information from an Atlas identifier. Atlas identifier is defined as\n * {OSM identifier}{3 digits for country slicing id}{3 digits for way-section id}.\n *\n * @author matthieun\n */\npublic class ReverseIdentifierFactory\n{\n    /**\n     * Returns the country code for the given identifier. Example: 222222001003 returns 1.\n     *\n     * @param countryCodeAndWaySectionedIdentifier\n     *            The full atlas identifier\n     * @return the country code for given identifier\n     */\n    public long getCountryCode(final long countryCodeAndWaySectionedIdentifier)\n    {\n        return countryCodeAndWaySectionedIdentifier\n                / AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT\n                % AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT;\n    }\n\n    /**\n     * Returns the OSM identifier and country portion for the given identifier. This truncates the\n     * identifier to get rid of the way-sectioning piece. Example: 222222001003 returns 222222001.\n     *\n     * @param countryCodeAndWaySectionedIdentifier\n     *            The full atlas identifier\n     * @return the identifier without the way-section portion for given identifier\n     */\n    public long getCountryOsmIdentifier(final long countryCodeAndWaySectionedIdentifier)\n    {\n        return Math.abs(countryCodeAndWaySectionedIdentifier\n                / AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT);\n    }\n\n    /**\n     * Returns the OSM identifier padded by the 6 digits allocated for country-slicing and\n     * way-sectioning. Example: 222222001003 returns 222222000000.\n     *\n     * @param countryCodeAndWaySectionedIdentifier\n     *            The full atlas identifier\n     * @return the OSM identifier padded by the 6 digits allocated for country-slicing and\n     *         way-sectioning\n     */\n    public long getFirstAtlasIdentifier(final long countryCodeAndWaySectionedIdentifier)\n    {\n        return Math.abs(getOsmIdentifier(countryCodeAndWaySectionedIdentifier)\n                * AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT\n                * AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT);\n    }\n\n    /**\n     * Returns the OSM identifier for the given identifier. This truncates the identifier to get rid\n     * of the country slicing and way-sectioning pieces. Example: 222222001003 returns 222222.\n     *\n     * @param countryCodeAndWaySectionedIdentifier\n     *            The full atlas identifier\n     * @return the OSM identifier for the given identifier\n     */\n    public long getOsmIdentifier(final long countryCodeAndWaySectionedIdentifier)\n    {\n        return Math.abs(countryCodeAndWaySectionedIdentifier\n                / (AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT\n                        * AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT));\n    }\n\n    /**\n     * Given the identifier that has the OSM identifier and country slicing portion, this will\n     * return the first complete Atlas identifier, including the way-section index. Example:\n     * 222222001 returns 222222001000.\n     *\n     * @param countryOsmIdentifier\n     *            The identifier with OSM identifier and country-slicing pieces\n     * @return the first complete Atlas identifier, including the way-section index\n     */\n    public long getStartIdentifier(final long countryOsmIdentifier)\n    {\n        return countryOsmIdentifier * AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT;\n    }\n\n    /**\n     * Similar to {@link #getStartIdentifier(long)}, this takes the OSM identifier and the country\n     * code as two separate inputs and returns the first complete Atlas identifier, including the\n     * way-section index. Example: 222222 and 001 returns 222222001000.\n     *\n     * @param osmIdentifier\n     *            The OSM identifier\n     * @param countryCode\n     *            The country-code identifier\n     * @return the first complete Atlas identifier, including the way-section index\n     */\n    public long getStartIdentifier(final long osmIdentifier, final long countryCode)\n    {\n        return (osmIdentifier * AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT + countryCode)\n                * AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT;\n    }\n\n    /**\n     * Returns the way-section index for the given identifier. Example: 222222001003 returns 3.\n     *\n     * @param countryCodeAndWaySectionedIdentifier\n     *            The full atlas identifier\n     * @return the way-section index for given identifier\n     */\n    public long getWaySectionIndex(final long countryCodeAndWaySectionedIdentifier)\n    {\n        return countryCodeAndWaySectionedIdentifier\n                % AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/pbf/slicing/identifier/WaySectionIdentifierFactory.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier;\n\n/**\n * Identifier factory for way section\n *\n * @author tony\n */\npublic class WaySectionIdentifierFactory extends AbstractIdentifierFactory\n{\n    public WaySectionIdentifierFactory(final long referenceIdentifier)\n    {\n        super(referenceIdentifier);\n    }\n\n    @Override\n    public long nextIdentifier()\n    {\n        incrementDelta();\n        return getReferenceIdentifier() + getDelta();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/creation/OsmPbfCounter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.creation;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.TagMap;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.RouteTag;\nimport org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Bound;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Entity;\nimport org.openstreetmap.osmosis.core.domain.v0_6.EntityType;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Node;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Relation;\nimport org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Way;\nimport org.openstreetmap.osmosis.core.domain.v0_6.WayNode;\nimport org.openstreetmap.osmosis.core.task.v0_6.Sink;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * The {@link OsmPbfCounter} is responsible for identifying and counting the number of\n * {@link Point}s, {@link Line}s and {@link org.openstreetmap.atlas.geography.atlas.items.Relation}s\n * that will be brought in from the given OSM PBF file. This information will be used to populate\n * the {@link AtlasSize} field to efficiently construct a Raw {@link Atlas}. The logic for\n * determining whether a feature should be brought in is as follows: *\n * <ul>\n * <li>Look at each OSM Node, if it's inside the given bounding box, bring it in. Keep track of all\n * nodes that were not brought in.\n * <li>Look at each OSM Way, if it has a Node that was brought in, bring in the Way and all other\n * Nodes that are part of this Way (since they may not have been originally brought in).\n * <li>Look at each OSM Relation at a shallow level - if it has a member (Node, Way or Relation)\n * that was brought in, go ahead and bring this Relation in. At this stage, we don't look at nested\n * relations. If we can't find a shallow member, stage this relation.\n * <li>Look at all staged relations and try to bring any in. If we loop through all staged relations\n * without bringing anything in, we can conclude that all remaining relations are either fully\n * outside the given boundary or are looping over each other.\n * </ul>\n *\n * @author mgostintsev\n */\npublic class OsmPbfCounter implements Sink\n{\n    private static final Logger logger = LoggerFactory.getLogger(OsmPbfCounter.class);\n\n    private static final int MAXIMUM_NETWORK_EXTENSION = 2;\n\n    private final AtlasLoadingOption loadingOption;\n    private final GeometricSurface boundingBox;\n\n    // Identifiers to bring in to the raw atlas\n    private final Set<Long> nodeIdentifiersToInclude = new HashSet<>();\n    private final Set<Long> nodeIdentifiersBroughtInByWaysOrRelations = new HashSet<>();\n    private final Set<Long> wayIdentifiersToInclude = new HashSet<>();\n    private final Set<Long> relationIdentifiersToInclude = new HashSet<>();\n\n    // Keep track of included nodes so that they can be used in calculating if a way intersects the\n    // given shard\n    private final Map<Long, Location> nodeIdentifierToLocation = new HashMap<>();\n\n    // Keep track of excluded ways to see if we need to add them later\n    private final Map<Long, Way> waysToExclude = new HashMap<>();\n\n    // Keep track of non-shallow relations to see if we need to add them later\n    private final List<Relation> stagedRelations = new ArrayList<>();\n\n    /**\n     * Default constructor.\n     *\n     * @param loadingOption\n     *            The {@link AtlasLoadingOption} to use\n     * @param boundingBox\n     *            The bounding box to consider when including features in the raw atlas\n     */\n    public OsmPbfCounter(final AtlasLoadingOption loadingOption, final GeometricSurface boundingBox)\n    {\n        this.loadingOption = loadingOption;\n        this.boundingBox = boundingBox;\n    }\n\n    @Override\n    public void close()\n    {\n        // Process all staged Relations\n        processStagedRelations();\n\n        // Grab any bridges, ferries or other ways that may be outside the immediate boundary\n        bringInConnectedOutsideWays();\n\n        // Combine all included nodes into a single collection\n        this.nodeIdentifiersToInclude.addAll(this.nodeIdentifiersBroughtInByWaysOrRelations);\n        logger.info(\"Released OSM PBF Counter\");\n    }\n\n    @Override\n    public void complete()\n    {\n        // No-Op\n    }\n\n    /**\n     * @return the set of all OSM Node identifiers to include in the raw Atlas.\n     */\n    public Set<Long> getIncludedNodeIdentifiers()\n    {\n        return this.nodeIdentifiersToInclude;\n    }\n\n    /**\n     * @return the set of all OSM Way identifiers to include in the raw Atlas.\n     */\n    public Set<Long> getIncludedWayIdentifiers()\n    {\n        return this.wayIdentifiersToInclude;\n    }\n\n    @Override\n    public void initialize(final Map<String, Object> metaData)\n    {\n        logger.info(\"Initialized OSM PBF Counter successfully\");\n    }\n\n    /**\n     * @return the number of {@link Line} objects found\n     */\n    public long lineCount()\n    {\n        return this.wayIdentifiersToInclude.size();\n    }\n\n    /**\n     * @return the number of {@link Point} objects found\n     */\n    public long pointCount()\n    {\n        return this.nodeIdentifiersToInclude.size();\n    }\n\n    @Override\n    public void process(final EntityContainer entityContainer)\n    {\n        final Entity rawEntity = entityContainer.getEntity();\n\n        if (OsmPbfReader.shouldProcessEntity(this.loadingOption, rawEntity))\n        {\n            // store all node locations for calculating way geometry\n            if (rawEntity instanceof Node)\n            {\n                final Node node = (Node) rawEntity;\n                final Location nodeLocation = new Location(Latitude.degrees(node.getLatitude()),\n                        Longitude.degrees(node.getLongitude()));\n                this.nodeIdentifierToLocation.put(rawEntity.getId(), nodeLocation);\n            }\n            if (shouldLoadOsmNode(rawEntity))\n            {\n                // This node is within the boundary or we are using the generated atlas file for QA\n                // purposes, bring it in\n                this.nodeIdentifiersToInclude.add(rawEntity.getId());\n            }\n            else if (shouldLoadOsmWay(rawEntity))\n            {\n                final Way way = (Way) rawEntity;\n\n                if (wayIntersectsBoundary(way))\n                {\n                    // This way contains at least one shape-point within the given bounding box.\n                    // Bring it and all of its nodes in to the atlas.\n                    addWayNodes(this.nodeIdentifiersBroughtInByWaysOrRelations, way);\n                    this.wayIdentifiersToInclude.add(way.getId());\n                }\n                else\n                {\n                    // This way doesn't have any shape-points within the given boundary. Mark it as\n                    // a way to exclude so it can be revisited during relation processing\n                    this.waysToExclude.put(way.getId(), way);\n                }\n            }\n            else if (shouldLoadOsmRelation(rawEntity))\n            {\n                final Relation relation = (Relation) rawEntity;\n                if (relationContainsMemberWithinBoundary(relation))\n                {\n                    // Shallow check showed that this relation has a member that is inside our\n                    // boundary, mark all members and relation as inside\n                    markRelationAndMembersInsideBoundary(relation);\n                }\n                else\n                {\n                    // Stage the relation - it might be added later\n                    this.stagedRelations.add(relation);\n                }\n            }\n            else if (rawEntity instanceof Bound)\n            {\n                logger.trace(\"Encountered PBF Bound {}, skipping over it.\", rawEntity.getId());\n            }\n        }\n    }\n\n    /**\n     * @return the number of {@link org.openstreetmap.atlas.geography.atlas.items.Relation} objects\n     *         found.\n     */\n    public long relationCount()\n    {\n        return this.relationIdentifiersToInclude.size();\n    }\n\n    private void addWayNodes(final Set<Long> set, final Way way)\n    {\n        way.getWayNodes().forEach(wayNode -> set.add(wayNode.getNodeId()));\n    }\n\n    /**\n     * Sometimes there are OSM ways (bridges, ferry routes, etc.) that are connected to the road\n     * network, but are outside the working country boundary. This method will expand up to\n     * {@link #MAXIMUM_NETWORK_EXTENSION} connections past our existing boundary nodes and pull in\n     * any ways that meet the above criteria. The while loop terminates if we've exceeded the\n     * maximum number of extensions or if we haven't added anything during the previous iteration.\n     */\n    private void bringInConnectedOutsideWays()\n    {\n        if (this.loadingOption.isLoadWaysSpanningCountryBoundaries())\n        {\n            int extensionCounter = 0;\n            final Set<Long> alreadyAddedWays = new HashSet<>();\n            final AtomicBoolean addedNewEdge = new AtomicBoolean(true);\n            while (extensionCounter < MAXIMUM_NETWORK_EXTENSION && addedNewEdge.get())\n            {\n                logger.trace(\"Adding connected ways outside boundary pass {}\", extensionCounter);\n                addedNewEdge.set(false);\n                this.waysToExclude.values().stream().filter(this::isHighwayOrFerry)\n                        .filter(way -> !alreadyAddedWays.contains(way.getId())).forEach(way ->\n                        {\n                            final List<WayNode> wayNodes = way.getWayNodes();\n                            for (final WayNode wayNode : wayNodes)\n                            {\n                                final long identifier = wayNode.getNodeId();\n                                if (this.nodeIdentifiersBroughtInByWaysOrRelations\n                                        .contains(identifier))\n                                {\n                                    // Add way and its members\n                                    logger.trace(\"Adding connected way with identifier {}\",\n                                            way.getId());\n                                    this.wayIdentifiersToInclude.add(way.getId());\n                                    addWayNodes(this.nodeIdentifiersBroughtInByWaysOrRelations,\n                                            way);\n                                    addedNewEdge.set(true);\n                                    alreadyAddedWays.add(way.getId());\n                                    break;\n                                }\n                            }\n                        });\n                extensionCounter++;\n            }\n        }\n    }\n\n    private boolean isHighwayOrFerry(final Way way)\n    {\n        final TagMap taggableWay = new TagMap(way.getTags());\n        return HighwayTag.isCoreWay(taggableWay) || RouteTag.isFerry(taggableWay);\n    }\n\n    private void markRelationAndMembersInsideBoundary(final Relation relation)\n    {\n        // Add all the members\n        for (final RelationMember member : relation.getMembers())\n        {\n            final EntityType memberType = member.getMemberType();\n            final Long memberIdentifier = member.getMemberId();\n\n            if (memberType == EntityType.Node)\n            {\n                this.nodeIdentifiersToInclude.add(memberIdentifier);\n            }\n            else if (memberType == EntityType.Way)\n            {\n                this.wayIdentifiersToInclude.add(memberIdentifier);\n\n                // If this line was originally excluded, bring it in now\n                final Way toAdd = this.waysToExclude.get(memberIdentifier);\n                if (toAdd != null)\n                {\n                    addWayNodes(this.nodeIdentifiersBroughtInByWaysOrRelations, toAdd);\n                    this.waysToExclude.remove(memberIdentifier, toAdd);\n                }\n            }\n            else if (memberType == EntityType.Relation)\n            {\n                this.relationIdentifiersToInclude.add(member.getMemberId());\n            }\n        }\n\n        // Add the relation itself\n        this.relationIdentifiersToInclude.add(relation.getId());\n    }\n\n    private boolean nodeWithinTargetBoundary(final Node node)\n    {\n        return this.boundingBox.fullyGeometricallyEncloses(new Location(\n                Latitude.degrees(node.getLatitude()), Longitude.degrees(node.getLongitude())));\n    }\n\n    private void processStagedRelations()\n    {\n        List<Relation> stagedRelations = this.stagedRelations;\n        int currentStagedRelationSize = this.stagedRelations.size();\n        int previousStagedRelationSize = 0;\n\n        // Loop through all staged relations and see if adding any of the staged relations trigger a\n        // new relation to be added. Once we have a full cycle where no new relation has been marked\n        // as inside the boundary, we can safely conclude that all remaining relations have members\n        // outside of the boundary\n        while (!this.stagedRelations.isEmpty()\n                && currentStagedRelationSize != previousStagedRelationSize)\n        {\n            final List<Relation> updatedStagedRelations = new ArrayList<>();\n            for (final Relation relation : stagedRelations)\n            {\n                if (relationContainsMemberWithinBoundary(relation))\n                {\n                    markRelationAndMembersInsideBoundary(relation);\n                }\n                else\n                {\n                    updatedStagedRelations.add(relation);\n                }\n            }\n            stagedRelations = updatedStagedRelations;\n            previousStagedRelationSize = currentStagedRelationSize;\n            currentStagedRelationSize = stagedRelations.size();\n        }\n    }\n\n    private boolean relationContainsMemberWithinBoundary(final Relation relation)\n    {\n        // This relation has already been marked as inside\n        if (this.relationIdentifiersToInclude.contains(relation.getId()))\n        {\n            return true;\n        }\n\n        // Do a shallow check to see if any members hit\n        for (final RelationMember member : relation.getMembers())\n        {\n            final EntityType memberType = member.getMemberType();\n\n            if (memberType == EntityType.Node\n                    && this.nodeIdentifiersToInclude.contains(member.getMemberId()))\n            {\n                return true;\n            }\n            else if (memberType == EntityType.Way\n                    && this.wayIdentifiersToInclude.contains(member.getMemberId()))\n            {\n                return true;\n            }\n            else if (memberType == EntityType.Relation\n                    && this.relationIdentifiersToInclude.contains(member.getMemberId()))\n            {\n                return true;\n            }\n        }\n\n        // We've looped through every member (excluding nested relations) and found no match\n        return false;\n    }\n\n    private boolean shouldLoadOsmNode(final Entity entity)\n    {\n        // For QA purposes, it is necessary to keep nodes that are outside the target boundary.\n        // For example, atlas-checks needs to know all the node ids in order to reverse a way and\n        // then create an\n        // osmChange file for MapRoulette.\n        return this.loadingOption.isLoadOsmNode() && entity instanceof Node\n                && (nodeWithinTargetBoundary((Node) entity) || this.loadingOption.isKeepAll());\n    }\n\n    private boolean shouldLoadOsmRelation(final Entity entity)\n    {\n        return this.loadingOption.isLoadOsmRelation() && entity instanceof Relation;\n    }\n\n    private boolean shouldLoadOsmWay(final Entity entity)\n    {\n        return this.loadingOption.isLoadOsmWay() && entity instanceof Way;\n    }\n\n    private boolean wayIntersectsBoundary(final Way way)\n    {\n        // CASE 1: Line crosses (or is enclosed by) the shard bounds and has at least one shapepoint\n        // within the shard bounds\n        ArrayList<Location> wayNodesLocations = new ArrayList<>();\n        for (final WayNode node : way.getWayNodes())\n        {\n            // nodes are processed first so allNodes will contain all node locations\n            wayNodesLocations.add(this.nodeIdentifierToLocation.get(node.getNodeId()));\n            if (this.nodeIdentifiersToInclude.contains(node.getNodeId()))\n            {\n                this.wayIdentifiersToInclude.add(way.getId());\n                return true;\n            }\n        }\n\n        // CASE 2: Line crossed the shard but has no shapepoints within it, so we must check for\n        // intersections\n        wayNodesLocations = wayNodesLocations.stream().filter(node -> node != null)\n                .collect(Collectors.toCollection(ArrayList::new));\n        if (wayNodesLocations.isEmpty())\n        {\n            return false;\n        }\n        final PolyLine wayGeometry = new PolyLine(wayNodesLocations);\n        if (wayGeometry.isPoint() || wayGeometry.isEmpty() || wayGeometry.bounds() == null)\n        {\n            return false;\n        }\n        // Checking the bounds of the polyline instead of the actual geometry may include some\n        // extraneous lines, but is much more performant. The extra lines will be filtered out after\n        // the slicing process\n        if (this.boundingBox.bounds().overlaps(wayGeometry.bounds()))\n        {\n            this.wayIdentifiersToInclude.add(way.getId());\n            return true;\n        }\n\n        // If we reach here, the way doesn't have a node anywhere inside (or on) the given boundary\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/creation/OsmPbfReader.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.creation;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.PaddingIdentifierFactory;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.TagMap;\nimport org.openstreetmap.atlas.tags.AtlasTag;\nimport org.openstreetmap.atlas.tags.LastEditChangesetTag;\nimport org.openstreetmap.atlas.tags.LastEditTimeTag;\nimport org.openstreetmap.atlas.tags.LastEditUserIdentifierTag;\nimport org.openstreetmap.atlas.tags.LastEditUserNameTag;\nimport org.openstreetmap.atlas.tags.LastEditVersionTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Bound;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Entity;\nimport org.openstreetmap.osmosis.core.domain.v0_6.EntityType;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Node;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Relation;\nimport org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Tag;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Way;\nimport org.openstreetmap.osmosis.core.domain.v0_6.WayNode;\nimport org.openstreetmap.osmosis.core.task.v0_6.Sink;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * The {@link OsmPbfReader} is responsible for reading OSM PBF entities and converting them to Atlas\n * Entities. It will map a PBF {@link Node} to an Atlas {@link Point}, a PBF {@link Way} to an Atlas\n * {@link Line} and a PBF {@link Relation} to an Atlas\n * {@link org.openstreetmap.atlas.geography.atlas.items.Relation}. Any exclusions based on tags are\n * allowed via the passed in {@link AtlasLoadingOption} configuration. There is no support for\n * Atlas @ {@link org.openstreetmap.atlas.geography.atlas.items.Node}s, {@link Edge}s or\n * {@link Area}s. This is also assuming that the Osmosis --completeWays flag is used during PBF\n * creation.\n *\n * @author mgostintsev\n */\npublic class OsmPbfReader implements Sink\n{\n    private static final Logger logger = LoggerFactory.getLogger(OsmPbfReader.class);\n    private static final String MISSING_MEMBER_MESSAGE = \"Relation {} contains {} {} as a member, but it's either filtered out or this PBF shard does not contain it.\";\n    private static final int MINIMUM_CLOSED_WAY_LENGTH = 4;\n\n    private final PackedAtlasBuilder builder;\n    private final AtlasLoadingOption loadingOption;\n    private final Set<Long> nodeIdentifiersToInclude = new HashSet<>();\n    private final Set<Long> wayIdentifiersToInclude = new HashSet<>();\n    private final Set<Long> pointIdentifiersFromFilteredLines = new HashSet<>();\n    private final List<Relation> stagedRelations = new ArrayList<>();\n    private final RawAtlasStatistic statistics = new RawAtlasStatistic(logger);\n\n    /**\n     * Determines if the given {@link Entity} should be brought into the {@link Atlas}. Ideally, all\n     * features will be brought in. However, to make {@link Atlas} generation flexible and fit all\n     * use cases, this is configurable.\n     *\n     * @param loadingOption\n     *            The {@link AtlasLoadingOption} to use for configuration lookup.\n     * @param entity\n     *            The candidate {@link Entity}\n     * @return {@code true} if this {@link Entity} should be brought into the {@link Atlas}.\n     */\n    public static boolean shouldProcessEntity(final AtlasLoadingOption loadingOption,\n            final Entity entity)\n    {\n        // The keepAll option is primarily used for QA purposes. Everything must stay.\n        if (loadingOption.isKeepAll())\n        {\n            return true;\n        }\n        else if (entity instanceof Node)\n        {\n            return loadingOption.getOsmPbfNodeFilter().test(Taggable.with(entity.getTags()));\n        }\n        else if (entity instanceof Way)\n        {\n            return loadingOption.getOsmPbfWayFilter().test(Taggable.with(entity.getTags()));\n        }\n        else if (entity instanceof Relation)\n        {\n            return loadingOption.getOsmPbfRelationFilter().test(Taggable.with(entity.getTags()));\n        }\n        else\n        {\n            // No Bound filtering\n            return true;\n        }\n    }\n\n    /**\n     * Default constructor\n     *\n     * @param loadingOption\n     *            The {@link AtlasLoadingOption} to use\n     * @param builder\n     *            The {@link PackedAtlasBuilder} to construct the raw Atlas\n     */\n    public OsmPbfReader(final AtlasLoadingOption loadingOption, final PackedAtlasBuilder builder)\n    {\n        this.builder = builder;\n        this.loadingOption = loadingOption;\n    }\n\n    @Override\n    public void close()\n    {\n        // We've processed all Nodes, Ways and shallow Relations to this point. Now, we need to\n        // handle Relations that contain Relation members properly.\n        this.processStagedRelations();\n        this.statistics.summary();\n        logger.info(\"Released OSM PBF Reader\");\n    }\n\n    @Override\n    public void complete()\n    {\n        // No-op\n    }\n\n    @Override\n    public void initialize(final Map<String, Object> metaData)\n    {\n        logger.info(\"Initialized OSM PBF Reader successfully\");\n    }\n\n    @Override\n    public void process(final EntityContainer entityContainer)\n    {\n        final Entity rawEntity = entityContainer.getEntity();\n\n        if (shouldProcessEntity(this.loadingOption, rawEntity))\n        {\n            if (rawEntity instanceof Node\n                    && this.nodeIdentifiersToInclude.contains(rawEntity.getId()))\n            {\n                processNode(rawEntity);\n            }\n            else if (rawEntity instanceof Way\n                    && this.wayIdentifiersToInclude.contains(rawEntity.getId()))\n            {\n                processWay(rawEntity);\n            }\n            else if (this.loadingOption.isLoadOsmRelation() && rawEntity instanceof Relation)\n            {\n                processRelation(rawEntity);\n            }\n            else if (rawEntity instanceof Bound)\n            {\n                logger.trace(\"Encountered PBF Bound {}, skipping over it.\", rawEntity.getId());\n            }\n        }\n        else\n        {\n            recordNodeIdentifiersFromFilteredEntity(rawEntity);\n            logFilteredStatistics(rawEntity);\n            logger.trace(\"Filtering out OSM {} {} from Raw Atlas\", rawEntity.getType(),\n                    rawEntity.getId());\n        }\n    }\n\n    /**\n     * Sets all the Node identifiers marked for inclusion.\n     *\n     * @param nodesToInclude\n     *            The set of Node identifiers to include in the atlas\n     */\n    public void setIncludedNodes(final Set<Long> nodesToInclude)\n    {\n        this.nodeIdentifiersToInclude.addAll(nodesToInclude);\n    }\n\n    /**\n     * Sets all the Way identifiers marked for inclusion.\n     *\n     * @param waysToInclude\n     *            The set of Way identifiers to include in the atlas\n     */\n    public void setIncludedWays(final Set<Long> waysToInclude)\n    {\n        this.wayIdentifiersToInclude.addAll(waysToInclude);\n    }\n\n    /**\n     * @return all the {@link Point} identifiers that make up {@link Line}s that were filtered. We\n     *         process all PBF {@link Node}s first and add them as Atlas {@link Point}s. After this,\n     *         we may filter out some PBF {@link Way}s. As a result, our Atlas file may contain\n     *         points which aren't used by any lines. We want to record these and see if we can\n     *         filter them out downstream.\n     */\n    protected Set<Long> getPointIdentifiersFromFilteredLines()\n    {\n        return this.pointIdentifiersFromFilteredLines;\n    }\n\n    /**\n     * Constructs an Atlas {@link org.openstreetmap.atlas.geography.atlas.items.Relation}. In the\n     * process, a relation can be dropped if it doesn't contain any members. The members could be\n     * empty, if they were filtered out by the PBF ingest criteria or if this PBF file doesn't\n     * contain them.\n     *\n     * @param relation\n     *            The {@link Relation} to add\n     */\n    private void addRelation(final Relation relation)\n    {\n        final RelationBean bean = constructRelationBean(relation);\n        if (!bean.isEmpty())\n        {\n            this.builder.addRelation(padIdentifier(relation.getId()), relation.getId(), bean,\n                    populateEntityTags(relation).getTags());\n            this.statistics.recordCreatedRelation();\n        }\n        else\n        {\n            this.statistics.recordDroppedRelation();\n            logger.debug(\"Cannot add empty Relation {} to the Atlas. We're either filtering\"\n                    + \" out the members that make up the Relation or none of the \"\n                    + \"members are present in this PBF shard.\", relation.getId());\n        }\n    }\n\n    /**\n     * Creates a {@link RelationBean} for the given {@link Relation}. Note: The returned bean can be\n     * empty.\n     *\n     * @param relation\n     *            The {@link Relation} for which to create the {@link RelationBean}\n     * @return the created {@link RelationBean}\n     */\n    private RelationBean constructRelationBean(final Relation relation)\n    {\n        final RelationBean bean = new RelationBean();\n        for (final RelationMember member : relation.getMembers())\n        {\n            final long memberIdentifier = padIdentifier(member.getMemberId());\n            final EntityType memberType = member.getMemberType();\n            final String role = member.getMemberRole();\n\n            if (memberType == EntityType.Node)\n            {\n                if (this.builder.peek().point(memberIdentifier) != null)\n                {\n                    bean.addItem(memberIdentifier, role, ItemType.POINT);\n                }\n                else\n                {\n                    logger.trace(MISSING_MEMBER_MESSAGE, relation.getId(), EntityType.Node,\n                            memberIdentifier);\n                }\n            }\n            else if (memberType == EntityType.Way)\n            {\n                if (this.builder.peek().line(memberIdentifier) != null)\n                {\n                    bean.addItem(memberIdentifier, role, ItemType.LINE);\n                }\n                else if (this.builder.peek().area(memberIdentifier) != null)\n                {\n                    bean.addItem(memberIdentifier, role, ItemType.AREA);\n                }\n                else\n                {\n                    logger.trace(MISSING_MEMBER_MESSAGE, relation.getId(), EntityType.Way,\n                            memberIdentifier);\n                }\n            }\n            else if (memberType == EntityType.Relation)\n            {\n                if (this.builder.peek().relation(memberIdentifier) != null)\n                {\n                    bean.addItem(memberIdentifier, role, ItemType.RELATION);\n                }\n                else\n                {\n                    logger.trace(MISSING_MEMBER_MESSAGE, relation.getId(), EntityType.Relation,\n                            memberIdentifier);\n                }\n            }\n            else\n            {\n                // A Bound should never be a Relation member; if this is the case, log and continue\n                logger.error(\"Invalid Bound member {} found for Relation {}\", member.getMemberId(),\n                        relation.getId());\n            }\n        }\n\n        return bean;\n    }\n\n    private Polygon constructWayPolygon(final Way way)\n    {\n        final List<WayNode> wayNodes = way.getWayNodes();\n        final List<Location> wayLocations = new ArrayList<>();\n        wayNodes.forEach(\n                node -> wayLocations.add(getNodeLocation(padIdentifier(node.getNodeId()))));\n        wayLocations.remove(wayLocations.size() - 1);\n        return new Polygon(wayLocations);\n    }\n\n    /**\n     * Constructs a {@link PolyLine} given an OSM PBF {@link Way}. The {@link Way} object doesn't\n     * contain the coordinates of its geometry, only the references to the {@link Node}s\n     * identifiers. We need to look up the {@link Location} of each {@link Node} and re-construct\n     * the {@link PolyLine} manually.\n     *\n     * @param way\n     *            The {@link Way} for which to construct the {@link PolyLine}\n     * @return the constructed {@link PolyLine}\n     */\n    private PolyLine constructWayPolyline(final Way way)\n    {\n        final List<WayNode> wayNodes = way.getWayNodes();\n        final List<Location> wayLocations = new ArrayList<>();\n        wayNodes.forEach(\n                node -> wayLocations.add(getNodeLocation(padIdentifier(node.getNodeId()))));\n        return new PolyLine(wayLocations);\n    }\n\n    /**\n     * Checks if the given {@link Relation} contains an un-indexed member {@link Relation}.\n     *\n     * @param relation\n     *            The {@link Relation} to check\n     * @return {@code true} if the given {@link Relation} contains a member {@link Relation} that\n     *         hasn't yet been indexed.\n     */\n    private boolean containsUnindexedSubRelation(final Relation relation)\n    {\n        return relation.getMembers().stream()\n                .anyMatch(member -> member.getMemberType() == EntityType.Relation && this.builder\n                        .peek().relation(padIdentifier(member.getMemberId())) == null);\n    }\n\n    /**\n     * Looks up the {@link Location} of a {@link Node}, given its identifier. This identifier is\n     * used to look up the corresponding Atlas {@link Point}.\n     *\n     * @param identifier\n     *            The {@link Node} identifier, in whose {@link Location} we're interested in.\n     * @return the extracted {@link Location}\n     */\n    private Location getNodeLocation(final long identifier)\n    {\n        final Point point = this.builder.peek().point(identifier);\n        if (point != null)\n        {\n            return point.getLocation();\n        }\n        else\n        {\n            throw new CoreException(\n                    \"Unable to find Point {} in Atlas. \"\n                            + \"It was either filtered out or the PBF file is malformed.\",\n                    identifier);\n        }\n    }\n\n    /**\n     * A {@link Way} is invalid if it's of size 0 or 1; or if it's of size 2 and has the same start\n     * and end node.\n     *\n     * @param way\n     *            The {@link Way} to validate\n     * @return {@code true} if the given {@link Way} is invalid\n     */\n    private boolean isInvalidWay(final Way way)\n    {\n        final List<WayNode> wayNodes = way.getWayNodes();\n        return wayNodes.size() < 2\n                || wayNodes.size() == 2\n                        && wayNodes.get(0).getNodeId() == wayNodes.get(1).getNodeId()\n                || wayNodes.size() < MINIMUM_CLOSED_WAY_LENGTH && getNodeLocation(\n                        padIdentifier(wayNodes.get(0).getNodeId()))\n                        .equals(getNodeLocation(\n                                padIdentifier(wayNodes.get(wayNodes.size() - 1).getNodeId())));\n    }\n\n    /**\n     * Log any {@link Entity}s that got filtered by ingest configuration.\n     *\n     * @param entity\n     *            The filtered {@link Entity}\n     */\n    private void logFilteredStatistics(final Entity entity)\n    {\n        if (entity instanceof Node)\n        {\n            this.statistics.recordFilteredNode();\n        }\n        else if (entity instanceof Way)\n        {\n            this.statistics.recordFilteredWay();\n        }\n        else if (entity instanceof Relation)\n        {\n            this.statistics.recordFilteredRelation();\n        }\n        else\n        {\n            // No-Op. We don't log bounds.\n        }\n    }\n\n    /**\n     * @return {@code true} if we need to pad identifiers when creating atlas entities.\n     */\n    private boolean needsPadding()\n    {\n        return this.loadingOption.isCountrySlicing() || this.loadingOption.isWaySectioning();\n    }\n\n    /**\n     * Pads the given OSM identifier, by appending 6 digits to it. The first 3 appended digits are\n     * the country code identifier and the last 3 digits are the way-section identifier.\n     *\n     * @param identifier\n     *            The original OSM identifier\n     * @return a padded identifier\n     */\n    private long padIdentifier(final long identifier)\n    {\n        if (needsPadding())\n        {\n            return PaddingIdentifierFactory.pad(identifier);\n        }\n        else\n        {\n            return identifier;\n        }\n    }\n\n    /**\n     * First, creates an {@link Entity} {@link Tag} for specific OSM attributes we're interested in\n     * propagating to the {@link AtlasEntity}. Secondly, converts the given {@link Entity}'s\n     * collection of {@link Tag}s to a {@link Map} of key/value pairs used to build an\n     * {@link AtlasEntity}'s tag set.\n     *\n     * @param entity\n     *            The {@link Entity} being processed\n     * @return a {@link Map} of key/value tags\n     */\n    private TagMap populateEntityTags(final Entity entity)\n    {\n        // Update the entity's tags to contain specific OSM attributes we care about, so that these\n        // get translated to Atlas Entity tags.\n        storeOsmEntityAttributesAsTags(entity);\n        return new TagMap(entity.getTags());\n    }\n\n    /**\n     * Uses the {@link Node} OSM identifier, geometry and tags to create an Atlas {@link Point}.\n     *\n     * @param entity\n     *            The {@link Entity} that will become an Atlas {@link Point}\n     */\n    private void processNode(final Entity entity)\n    {\n        final Node node = (Node) entity;\n        this.builder.addPoint(padIdentifier(node.getId()),\n                new Location(Latitude.degrees(node.getLatitude()),\n                        Longitude.degrees(node.getLongitude())),\n                populateEntityTags(node).getTags());\n        this.statistics.recordCreatedPoint();\n    }\n\n    /**\n     * Tries to create an Atlas {@link org.openstreetmap.atlas.geography.atlas.items.Relation}. If\n     * the {@link Entity} contains a member that's also a relation and that member hasn't been\n     * processed yet, then we add the given {@link Relation} to a Collection of staged relations to\n     * process later (see {@link #close()} method). Otherwise, we add it.\n     *\n     * @param entity\n     *            The {@link Entity} that will become an Atlas\n     *            {@link org.openstreetmap.atlas.geography.atlas.items.Relation}\n     */\n    private void processRelation(final Entity entity)\n    {\n        final Relation relation = (Relation) entity;\n        if (containsUnindexedSubRelation(relation))\n        {\n            // Stage this Relation, it has a member relation that we haven't processed yet\n            this.stagedRelations.add(relation);\n        }\n        else\n        {\n            addRelation(relation);\n        }\n    }\n\n    /**\n     * Processes all non-shallow {@link Relation}s - any {@link Relation} that has another\n     * {@link Relation} as a member. This is called near the end of the processing, once we've\n     * successfully added all {@link Node}s, {@link Way}s and shallow {@link Relation}s. If the\n     * number of staged relations doesn't change from one iteration to the next, then we know that\n     * any future iteration will not un-stage any of the relations. Therefore, we can try to add the\n     * relations in the current state.\n     */\n    private void processStagedRelations()\n    {\n        int previousStagedRelationSize = Integer.MAX_VALUE;\n        List<Relation> stagedRelations = this.stagedRelations;\n        int currentStagedRelationSize = stagedRelations.size();\n\n        while (!stagedRelations.isEmpty() && currentStagedRelationSize < previousStagedRelationSize)\n        {\n            final List<Relation> updatedStagedRelations = new ArrayList<>();\n            for (final Relation relation : stagedRelations)\n            {\n                if (containsUnindexedSubRelation(relation))\n                {\n                    updatedStagedRelations.add(relation);\n                }\n                else\n                {\n                    addRelation(relation);\n                }\n            }\n            stagedRelations = updatedStagedRelations;\n            previousStagedRelationSize = currentStagedRelationSize;\n            currentStagedRelationSize = stagedRelations.size();\n        }\n\n        // If we weren't able to process any of the staged relations and there are still some left,\n        // go ahead and try to add these in their current state. We can potentially add a relation\n        // that has an incomplete member list, however we are relying on the fact that a neighboring\n        // shard will contain this relation and the missing members. When the two are combined, the\n        // relation will become whole again.\n        if (currentStagedRelationSize == previousStagedRelationSize && !stagedRelations.isEmpty())\n        {\n            boolean changesMade = false;\n            while (!stagedRelations.isEmpty())\n            {\n                final List<Relation> staging = new ArrayList<>();\n                staging.addAll(stagedRelations);\n                for (final Relation relation : stagedRelations)\n                {\n                    if (relation.getMembers().stream()\n                            .anyMatch(member -> member.getMemberType().equals(EntityType.Relation)\n                                    && staging.stream().anyMatch(\n                                            staged -> staged.getId() == member.getMemberId())))\n                    {\n                        logger.error(\"Relation {} had staging member {}\", relation.getId(), relation\n                                .getMembers().stream()\n                                .filter(member -> member.getMemberType().equals(EntityType.Relation)\n                                        && staging.stream().anyMatch(\n                                                staged -> staged.getId() == member.getMemberId()))\n                                .collect(Collectors.toSet()));\n                    }\n                    else\n                    {\n                        addRelation(relation);\n                        staging.remove(relation);\n                        changesMade = true;\n                    }\n                }\n                stagedRelations = staging;\n                if (!changesMade)\n                {\n                    logger.error(\n                            \"No changes found for staged relation loop. Staged relations were {}\",\n                            stagedRelations);\n                    logger.error(\"Just in case, first relation was {}\",\n                            stagedRelations.iterator().next());\n                    break;\n                }\n                changesMade = false;\n            }\n            if (!stagedRelations.isEmpty())\n            {\n                final List<Relation> stagedRelationsButFinal = stagedRelations;\n                final SortedSet<Relation> sortedByNumberOfParents = new TreeSet<>((r1, r2) ->\n                {\n                    final int r1Parents = r1.getMembers().stream()\n                            .filter(member1 -> member1.getMemberType().equals(EntityType.Relation)\n                                    && stagedRelationsButFinal.stream().anyMatch(\n                                            staged1 -> staged1.getId() == member1.getMemberId()))\n                            .collect(Collectors.toSet()).size();\n                    final int r2Parents = r2.getMembers().stream()\n                            .filter(member2 -> member2.getMemberType().equals(EntityType.Relation)\n                                    && stagedRelationsButFinal.stream().anyMatch(\n                                            staged2 -> staged2.getId() == member2.getMemberId()))\n                            .collect(Collectors.toSet()).size();\n                    if (r1Parents < r2Parents)\n                    {\n                        return 1;\n                    }\n                    else if (r1Parents > r2Parents)\n                    {\n                        return -1;\n                    }\n                    else\n                    {\n                        return Long.compare(r1.getId(), r2.getId());\n                    }\n\n                });\n                stagedRelations.forEach(sortedByNumberOfParents::add);\n                sortedByNumberOfParents.forEach(this::addRelation);\n            }\n        }\n    }\n\n    /**\n     * Uses the {@link Way} identifier, re-constructed geometry and tags to create an Atlas\n     * {@link Line}.\n     *\n     * @param entity\n     *            The {@link Entity} that will become an Atlas {@link Line}\n     */\n    private void processWay(final Entity entity)\n    {\n        final Way way = (Way) entity;\n        if (isInvalidWay(way))\n        {\n            this.statistics.recordDroppedWay();\n        }\n        else\n        {\n            final PolyLine wayPolyLine = constructWayPolyline(way);\n            final TagMap wayTags = populateEntityTags(way);\n            if (wayPolyLine.first().equals(wayPolyLine.last()))\n            {\n                boolean kept = false;\n                if (this.loadingOption.getAreaFilter().test(wayTags))\n                {\n                    this.builder.addArea(padIdentifier(way.getId()), constructWayPolygon(way),\n                            wayTags.getTags());\n                    this.statistics.recordCreatedLine();\n                    kept = true;\n                }\n                if (this.loadingOption.getEdgeFilter().test(wayTags))\n                {\n                    this.builder.addLine(padIdentifier(way.getId()), wayPolyLine,\n                            wayTags.getTags());\n                    this.statistics.recordCreatedLine();\n                    kept = true;\n                }\n                if (!kept)\n                {\n                    this.statistics.recordDroppedWay();\n                }\n            }\n            else\n            {\n                this.builder.addLine(padIdentifier(way.getId()), wayPolyLine, wayTags.getTags());\n                this.statistics.recordCreatedLine();\n            }\n        }\n    }\n\n    /**\n     * Because of how an Atlas is constructed, we add all PBF {@link Node}s as {@link Point}s.\n     * However, if we filter out a PBF {@link Way}, we want to keep track of all {@link Node}s that\n     * make up that {@link Way}, in case we need to remove them as well. We are going to keep track\n     * of all filtered PBF {@link Node}/Atlas {@link Point} identifiers, and use subAtlas\n     * functionality to filter them out after the Atlas is built. For now, ignore filtering any\n     * Nodes that come from filtered Relations. This will be handled in the way-sectioning code.\n     *\n     * @param entity\n     *            The {@link Entity} whose Node (Atlas Point) identifiers we want to filter out\n     */\n    private void recordNodeIdentifiersFromFilteredEntity(final Entity entity)\n    {\n        if (entity instanceof Way)\n        {\n            final List<WayNode> wayNodes = ((Way) entity).getWayNodes();\n            wayNodes.forEach(node ->\n            {\n                this.pointIdentifiersFromFilteredLines.add(padIdentifier(node.getNodeId()));\n                this.statistics.recordFilteredNode();\n            });\n        }\n    }\n\n    /**\n     * Grabs desired OSM attributes (such as last edited time) from given {@link Entity} and creates\n     * a corresponding {@link Tag} value for each.\n     *\n     * @param entity\n     *            The {@link Entity}, whose attributes we want to save.\n     */\n    private void storeOsmEntityAttributesAsTags(final Entity entity)\n    {\n        final Collection<Tag> tags = entity.getTags();\n        for (final String tag : AtlasTag.TAGS_FROM_OSM)\n        {\n            if (tag.equals(LastEditTimeTag.KEY))\n            {\n                tags.add(new Tag(tag, String.valueOf(entity.getTimestamp().getTime())));\n            }\n            else if (tag.equals(LastEditUserIdentifierTag.KEY))\n            {\n                tags.add(new Tag(tag, String.valueOf(entity.getUser().getId())));\n            }\n            else if (tag.equals(LastEditUserNameTag.KEY))\n            {\n                tags.add(new Tag(tag, entity.getUser().getName()));\n            }\n            else if (tag.equals(LastEditVersionTag.KEY))\n            {\n                tags.add(new Tag(tag, String.valueOf(entity.getVersion())));\n            }\n            else if (tag.equals(LastEditChangesetTag.KEY))\n            {\n                tags.add(new Tag(tag, String.valueOf(entity.getChangesetId())));\n            }\n            else\n            {\n                logger.error(\n                        \"Trying to add mandatory tag {}, but no behavior defined for getting the value.\",\n                        tag);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/creation/README.md",
    "content": "# `Raw Atlas Creation`\n\n## Overview\n\nThis package is responsible for the initial OSM PBF file to Atlas file transformation. The result is an intermediate Atlas file, that is not country-sliced or way-sectioned. The advantage to this is that we get to leverage the Atlas API layer and most importantly, the spatial index to make slicing and sectioning as straight-forward as possible.\n\n## Raw Atlas Terminology\n\n### Stages\n\nThe raw Atlas flow is comprised of three separate stages. They're explained in detail below:\n\n1. Raw Atlas - this is the first Atlas type that is created. It's a direct representation of the PBF data in Atlas format. This Atlas contains only Points, Lines, Areas, and Relations. It does **not** contain country code assignment or way-sectioning. \n\n2. Sliced Atlas - the sliced  Atlas is made up of Points, Lines, Areas, and Relations with each one containing one or more country code for which country it belongs to. For details on this process, see the Slicing package README file.\n\n3. Sectioned Atlas - this is the final sliced and sectioned artifact, which is made up of all Atlas entities - Points, Nodes, Edges, Lines, Areas and Relations and is fully ready for processing.\n\n### General Principles\n\nThere are a few general principles that are observed throughout the ingest process. \n\n1. The desired goal is to achieve parity with OSM and get as accurate a representation as possible - including ingesting bad data. The reasoning for this is to be able to leverage the Atlas and write [atlas-checks](https://github.com/osmlab/atlas-checks) to detect bad data, which will lead to OSM data fixes.\n2. Do all complex processing once. If we have access to all the data during the ingest piece and if we're building MultiAtlases to country-slice and section the road network correctly, bundle all similar processing together to save any downstream users the hassle of having to re-build MutliAtlases or make inferences based on impartial Atlas views.  \n3. Rely on synthetic tags. Instead of silently fixing bad data or making data-altering decisions - rely on synthetic tags where possible to identify specific cases that may require additional attention or custom atlas-checks.\n\n## Raw Atlas Implementation Details\n\nThe PBF ingestion process is configuration driven. This means that the user has full control of what features end up in the final Atlas. The default configuration, found in the [pbf resource package](https://github.com/osmlab/atlas/tree/dev/src/main/resources/org/openstreetmap/atlas/geography/atlas/pbf), attempts full parity with OSM - meaning it ingests almost all features.\n\nThere are a couple of implementation details to call out for the raw Atlas creation. The input protobuf file is created using the [Osmosis library](https://github.com/openstreetmap/osmosis) and it's structured with a distinct order - the file contains the Nodes first, then the Ways and lastly the Relations. Each Way references the Node identifiers that are used to construct itself. This is something problematic, because we have no Node location or tag properties of the individual Nodes when processing each Way. To solve this, we must either create a Node map or make two passes over the PBF file - once to read the ways and a second time to read the Nodes for the Ways we're interested in. It's a lot faster to read the file twice, rather than resize the underlying `PackedAtlas` arrays during build time. In the actual implementation, the `OsmPbfCounter` class is responsible for identifying what to bring in, keeping track of relevant Nodes and counts. The `OsmPbfReader` will then go through the file a second time and build the raw Atlas using the information from the counter.\n\n## Synthetic Tags\n\nThe raw Atlas creation process add a new synthetic tag as part of the final Atlas - `SyntheticDuplicateOsmNodeTag`. This tag signifies that the input OSM data contains two or more stacked Nodes at the same Location. This is almost always a data error, that we are handling graciously and deterministically. The Node with the lowest identifier is kept, while all others are excluded from the final Atlas. There are two caveats to call out here. The first caveat is that if there are Nodes with different `LayerTag` values at the same `Location`, we will keep the lowest occurring Node for each layer in order to preserve proper connectivity. The second caveat is that we can potentially remove a Node that has rich tagging and keep the Node that has no tagging. This is a potential problem, but the only way to ensure deterministic processing. Ideally, the presence of this synthetic tag will prompt the creation of an atlas-check that will result in data fixes for such cases.  \n\n## Raw Atlas Ingestion Logic\n\nThe specific logic for what gets ingested into the raw Atlas is as follows:\n\n1. For each OSM Node, if it's inside the `Shard` boundary that's being processed, bring it in. As a side note, we keep track of all Nodes that were not brought in, in case we have to pull them in later. \n2. For each OSM Way, if the Way has a Node that was brought in, bring in the entire Way and all other Nodes that are part of this Way (since they may not have been originally brought in as they could be outside the target `Shard` boundary).\n3. For each OSM Relation, process Relations in a queue structure - where we only look at Relations that have no member Relations or have had their member Relations already processed. For each Relation, if it contains a member that was brought into the Atlas (Node, Way or Relation) then bring in this Relation in to the Atlas. \n\n## Code Sample\n\nThere are a few ways to create raw Atlas files. Each one is outlined here. \n\nThe first and most simple one is to build a raw Atlas from some PBF resource, irrespective of `Shard` boundary or any kind of `AtlasLoadingOption`:\n\n```java\nfinal String pbfPath = \"/path/to/pbf/resource\";\n\nfinal RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(pbfPath));\nfinal Atlas rawAtlas = rawAtlasGenerator.build();\n```\n\nThe second way is to build a raw Atlas given a PBF resource and a specific boundary of interest:\n\n```java\nfinal String pbfPath = \"/path/to/pbf/resource\";\n\n// The boundary of interest\nfinal MultiPolygon boundary;\n\nfinal RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(pbfPath), boundary);\nfinal Atlas rawAtlas = rawAtlasGenerator.build();\n```\n\nLastly, we can specify all three fields of interest - the PBF resource, the specific `AtlasLoadingOption` and boundary of interest:\n\n```java\nfinal String pbfPath = \"/path/to/pbf/resource\";\n\n// The boundary of interest\nfinal MultiPolygon boundary;\n\n// Country boundary shapes backed by a spatial index\nfinal CountryBoundaryMap countryBoundaryMap;\n\nfinal AtlasLoadingOption loadingOption = AtlasLoadingOption.createOptionWithAllEnabled(countryBoundaryMap);\nfinal RawAtlasGenerator generator = new RawAtlasGenerator(new File(pbfPath), loadingOption, boundary);\n\nfinal Atlas rawAtlas = rawAtlasGenerator.build();\n```\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/creation/RawAtlasGenerator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.creation;\n\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.CloseableOsmosisReader;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.tags.AtlasTag;\nimport org.openstreetmap.atlas.tags.LayerTag;\nimport org.openstreetmap.atlas.tags.SyntheticDuplicateOsmNodeTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.openstreetmap.osmosis.core.task.v0_6.Sink;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * The {@link RawAtlasGenerator} loads an OSM protobuf file and constructs a raw {@link Atlas} from\n * it. A raw {@link Atlas} will only contains Atlas {@link Point}s, {@link Line}s and\n * {@link Relation}s. The protobuf file is structured in a specific way - there is a distinct order:\n * the file first contains Nodes, then Ways and lastly Relations. Each Way only references the\n * identifier, it doesn't contain any location or tag properties of the Nodes used to construct\n * itself. In order to process them - we can either create a Node map or read the file twice. We\n * also want to identify all features that will be part of the Atlas before building it so we can\n * create an accurate {@link AtlasSize}. It is a lot faster to read the file twice than resize the\n * underlying {@link PackedAtlas} arrays during build time. In our implementation, the\n * {@link OsmPbfCounter} is responsible for identifying and counting what to pull in by going\n * through the PBF file once. The {@link OsmPbfReader} will go through it a second time and build\n * the raw Atlas using the information obtained from the counter.\n *\n * @author mgostintsev\n */\npublic class RawAtlasGenerator\n{\n    private static final Logger logger = LoggerFactory.getLogger(RawAtlasGenerator.class);\n\n    // Used to identify and count all entities pulled into the Atlas\n    private final OsmPbfCounter pbfCounter;\n\n    // Used to build the raw Atlas given the information from the OsmPbfCounter\n    private final OsmPbfReader pbfReader;\n\n    // The target bounding box. Anything outside of this will be discarded.\n    private final MultiPolygon boundingBox;\n\n    // Builder to build raw Atlas\n    private final PackedAtlasBuilder builder;\n\n    // Any configurations needed\n    private final AtlasLoadingOption atlasLoadingOption;\n\n    // Osmosis supplier\n    private final Supplier<CloseableOsmosisReader> osmosisReaderSupplier;\n\n    // Raw atlas metadata\n    private AtlasMetaData metaData = new AtlasMetaData();\n\n    /**\n     * Constructor that supplies the maximum bounds possible as the bounding box.\n     *\n     * @param resource\n     *            The OSM PBF {@link Resource} to use\n     */\n    public RawAtlasGenerator(final Resource resource)\n    {\n        this(resource, AtlasLoadingOption.createOptionWithOnlySectioning(), MultiPolygon.MAXIMUM);\n    }\n\n    /**\n     * Default constructor.\n     *\n     * @param resource\n     *            The OSM PBF {@link Resource} to use\n     * @param loadingOption\n     *            The {@link AtlasLoadingOption} to use\n     * @param boundingBox\n     *            The bounding box to consider when including features in the raw atlas\n     */\n    public RawAtlasGenerator(final Resource resource, final AtlasLoadingOption loadingOption,\n            final MultiPolygon boundingBox)\n    {\n        this(() -> new CloseableOsmosisReader(resource.read()), loadingOption, boundingBox);\n    }\n\n    /**\n     * Constructor that uses the default configuration with a given bounding box.\n     *\n     * @param resource\n     *            The OSM PBF {@link Resource} to use\n     * @param boundingBox\n     *            The bounding box to consider when including features in the raw atlas\n     */\n    public RawAtlasGenerator(final Resource resource, final MultiPolygon boundingBox)\n    {\n        this(resource, AtlasLoadingOption.createOptionWithNoSlicing(), boundingBox);\n    }\n\n    public RawAtlasGenerator(final Supplier<CloseableOsmosisReader> osmosisReaderSupplier,\n            final AtlasLoadingOption atlasLoadingOption, final MultiPolygon boundingBox)\n    {\n        this.osmosisReaderSupplier = osmosisReaderSupplier;\n        this.atlasLoadingOption = atlasLoadingOption;\n        this.boundingBox = boundingBox;\n        this.builder = new PackedAtlasBuilder();\n        this.pbfReader = new OsmPbfReader(atlasLoadingOption, this.builder);\n        this.pbfCounter = new OsmPbfCounter(atlasLoadingOption, this.boundingBox);\n    }\n\n    /**\n     * Loops through the PBF file once to gather the entity counts. Updates the\n     * {@link AtlasMetaData} and {@link AtlasSize}, then proceeds to loop through the PBF file a\n     * second time to build the raw {@link Atlas}.\n     *\n     * @return the raw {@link Atlas}, can be {@code null}.\n     */\n    public Atlas build()\n    {\n        prepareBuild();\n\n        // Second pass -- loop through the PBF file again. This time, read the entities and\n        // construct a raw Atlas.\n        return buildRawAtlas();\n    }\n\n    /**\n     * Works the same way as build() above, but doesn't trim duplicate and extraneous points from\n     * the atlas. This is used as a faster way to build the atlas when verifying the validity of PBF\n     * files.\n     *\n     * @return the raw {@link Atlas}, can be {@code null} or filled with duplicate points\n     */\n    public Atlas buildNoTrim()\n    {\n        prepareBuild();\n\n        // Second pass -- loop through the PBF file again. This time, read the entities and\n        // construct a raw Atlas.\n        return buildRawAtlasNoTrim();\n    }\n\n    /**\n     * Save raw {@link Atlas} as geoJson.\n     *\n     * @param resource\n     *            The {@link WritableResource} to save to.\n     */\n    public void saveAsGeojson(final WritableResource resource)\n    {\n        logger.info(\"Saving Raw Atlas as geojson\");\n        build().saveAsGeoJson(resource);\n    }\n\n    /**\n     * Save raw {@link Atlas} as text.\n     *\n     * @param resource\n     *            The {@link WritableResource} to save to.\n     */\n    public void saveAsText(final WritableResource resource)\n    {\n        logger.info(\"Saving Raw Atlas as text\");\n        build().saveAsText(resource);\n    }\n\n    /**\n     * Save raw {@link Atlas}.\n     *\n     * @param resource\n     *            The {@link WritableResource} to save to.\n     */\n    public void saveAtlas(final WritableResource resource)\n    {\n        logger.info(\"Saving Raw Atlas file\");\n        build().save(resource);\n    }\n\n    /**\n     * Use given {@link AtlasMetaData} object\n     *\n     * @param metaData\n     *            {@link AtlasMetaData} to use\n     * @return the updated {@link RawAtlasGenerator}\n     */\n    public RawAtlasGenerator withMetaData(final AtlasMetaData metaData)\n    {\n        this.metaData = metaData;\n        return this;\n    }\n\n    /**\n     * Loops through the given OSM PBF file and builds the raw {@link Atlas}.\n     *\n     * @return the raw {@link Atlas}, possibly {@code null} if no {@link Atlas} was built.\n     */\n    private Atlas buildRawAtlas()\n    {\n        final String shardName = this.metaData.getShardName().orElse(\"unknown\");\n        final Atlas atlas = buildRawAtlasNoTrim();\n\n        if (atlas == null)\n        {\n            logger.info(\"Generated empty raw Atlas for PBF Shard {}\", shardName);\n            return atlas;\n        }\n        else\n        {\n            final Time trimTime = Time.now();\n            final Atlas trimmedAtlas;\n            if (this.atlasLoadingOption.isKeepAll())\n            {\n                trimmedAtlas = atlas;\n            }\n            else\n            {\n                trimmedAtlas = removeDuplicateAndExtraneousPointsFromAtlas(atlas);\n            }\n            logger.info(\"Trimmed Raw Atlas for {} in {}\", shardName, trimTime.elapsedSince());\n\n            if (trimmedAtlas == null)\n            {\n                logger.info(\"Empty raw Atlas after filtering for PBF Shard {}\", shardName);\n            }\n            return trimmedAtlas;\n        }\n    }\n\n    private Atlas buildRawAtlasNoTrim()\n    {\n        final String shardName = this.metaData.getShardName().orElse(\"unknown\");\n        final Time parseTime = Time.now();\n        try (CloseableOsmosisReader reader = connectOsmPbfToPbfConsumer(this.pbfReader))\n        {\n            reader.run();\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Atlas creation error for PBF shard {}\", shardName, e);\n        }\n        logger.info(\"Read PBF for {} in {}\", shardName, parseTime.elapsedSince());\n\n        final Time buildTime = Time.now();\n        final Atlas atlas = this.builder.get();\n        logger.info(\"Built Raw Atlas for {} in {}\", shardName, buildTime.elapsedSince());\n        return atlas;\n    }\n\n    /**\n     * Connects the given {@link Sink} implementation to the PBF File.\n     */\n    private CloseableOsmosisReader connectOsmPbfToPbfConsumer(final Sink consumer)\n    {\n        final CloseableOsmosisReader reader = this.osmosisReaderSupplier.get();\n        reader.setSink(consumer);\n        return reader;\n    }\n\n    /**\n     * Loops through the given OSM PBF file and count the all the {@link Point}s, {@link Line}s and\n     * {@link Relation}s. These will be used to initialize the {@link AtlasSize} to efficiently\n     * build the raw {@link Atlas}.\n     */\n    private void countOsmPbfEntities()\n    {\n        final Time countTime = Time.now();\n        try (CloseableOsmosisReader counter = connectOsmPbfToPbfConsumer(this.pbfCounter))\n        {\n            counter.run();\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Error counting PBF entities\", e);\n        }\n        logger.info(\"Counted PBF Entities in {}\", countTime.elapsedSince());\n    }\n\n    private long getLayerTagValueForPoint(final Atlas atlas, final long identifier)\n    {\n        return LayerTag.getTaggedOrImpliedValue(atlas.point(identifier), 0L);\n    }\n\n    /**\n     * Check if the {@link Point} with the given identifier is a {@link Relation} member in the\n     * given {@link Atlas}.\n     *\n     * @param atlas\n     *            The {@link Atlas} to check\n     * @param pointIdentifier\n     *            The {@link Point} identifier to use\n     * @return {@code true} if the given {@link Point} identifier is a {@link Relation} member in\n     *         the given {@link Atlas}\n     */\n    private boolean isRelationMember(final Atlas atlas, final long pointIdentifier)\n    {\n        return !atlas.point(pointIdentifier).relations().isEmpty();\n    }\n\n    /**\n     * Check if the {@link Point} with the given identifier is a shape point for some {@link Line}\n     * in the given {@link Atlas}.\n     *\n     * @param atlas\n     *            The {@link Atlas} to check\n     * @param pointIdentifier\n     *            The {@link Point} identifier to use\n     * @return {@code true} if the given {@link Point} identifier is a shape point for some\n     *         {@link Line} in the given {@link Atlas}\n     */\n    private boolean isShapePoint(final Atlas atlas, final long pointIdentifier)\n    {\n        return Iterables\n                .size(atlas.linesContaining(atlas.point(pointIdentifier).getLocation())) > 0;\n    }\n\n    /**\n     * A simple point is one that only has the mandatory entity tags. See\n     * {@link AtlasTag#TAGS_FROM_OSM} for the 5 tags. Examples of non-simple points include stop\n     * lights, barriers, etc.\n     *\n     * @param atlas\n     *            The {@link Atlas} to check\n     * @param pointIdentifier\n     *            The {@link Point} identifier to use\n     * @return {@code true} if the given identifier represents a simple {@link Point}\n     */\n    private boolean isSimplePoint(final Atlas atlas, final long pointIdentifier)\n    {\n        return atlas.point(pointIdentifier).getTags().size() == AtlasTag.TAGS_FROM_OSM.size();\n    }\n\n    private boolean locationPartOfMultipleWaysWithDifferentLayerTags(final Atlas atlas,\n            final Location location)\n    {\n        final long distinctLayerTagValues = StreamSupport\n                .stream(atlas.linesContaining(location).spliterator(), false)\n                .map(line -> LayerTag.getTaggedOrImpliedValue(atlas.line(line.getIdentifier()), 0L))\n                .distinct().count();\n\n        return distinctLayerTagValues > 1;\n    }\n\n    /**\n     * Populates the {@link AtlasMetaData} used to build the raw {@link Atlas}. Specifically,\n     * records any {@link Node}, {@link Way} and {@link Relation} filtering that may have been used.\n     * This may also populate other options that were passed in the {@link AtlasLoadingOption}\n     * parameter at construction.\n     */\n    private void populateAtlasMetadata()\n    {\n        // AtlasMetaData returns a new HashMap with every getTags() call.\n        // We therefore want to operate on a \"copy\" of the original tags,\n        // and create a new metadata object.\n        final Map<String, String> originalTags = this.metaData.getTags();\n        originalTags.put(AtlasMetaData.OSM_PBF_NODE_CONFIGURATION,\n                this.atlasLoadingOption.getOsmPbfNodeFilter().toString());\n        originalTags.put(AtlasMetaData.OSM_PBF_WAY_CONFIGURATION,\n                this.atlasLoadingOption.getOsmPbfWayFilter().toString());\n        originalTags.put(AtlasMetaData.OSM_PBF_RELATION_CONFIGURATION,\n                this.atlasLoadingOption.getOsmPbfRelationFilter().toString());\n        originalTags.put(AtlasMetaData.KEEP_ALL_CONFIGURATION,\n                Boolean.toString(this.atlasLoadingOption.isKeepAll()));\n        // While AtlasMetaData returns a new map by default, this accounts for the possibility that\n        // it may change in the future.\n        if (!originalTags.equals(this.metaData.getTags()))\n        {\n            this.metaData = this.metaData.copyWithNewTags(originalTags);\n        }\n        this.builder.setMetaData(this.metaData);\n    }\n\n    /**\n     * Get the set of {@link Point}s that make up all the filtered PBF {@link Way}s and see if we\n     * can remove them from the generated raw Atlas. Criteria for removal are:\n     * <ul>\n     * <li>The {@link Point} has to be simple. This avoids removing non-shape point features.\n     * <li>The {@link Point} cannot be a {@link Relation} member.\n     * <li>The {@link Point} cannot be a shape point for an existing {@link Line}.\n     * </ul>\n     *\n     * @param atlas\n     *            The {@link Atlas} being filtered from\n     * @return the {@link Set} of {@link Point} identifiers that are safe to filter out\n     */\n    private Set<Long> preFilterPointsToRemove(final Atlas atlas)\n    {\n        return this.pbfReader.getPointIdentifiersFromFilteredLines().stream()\n                .filter(identifier -> atlas.point(identifier) != null)\n                .filter(identifier -> isSimplePoint(atlas, identifier))\n                .filter(identifier -> !isRelationMember(atlas, identifier))\n                .filter(identifier -> !isShapePoint(atlas, identifier)).collect(Collectors.toSet());\n    }\n\n    private void prepareBuild()\n    {\n        countOsmPbfEntities();\n\n        // Update the metadata to reflect any configuration that was used and use count results to\n        // set the AtlasSize estimate.\n        populateAtlasMetadata();\n        setAtlasSizeEstimate();\n        this.builder.withEnhancedRelationGeometry();\n\n        // Update the reader to be aware of any included nodes/ways to avoid repeated calculations\n        this.pbfReader.setIncludedNodes(this.pbfCounter.getIncludedNodeIdentifiers());\n        this.pbfReader.setIncludedWays(this.pbfCounter.getIncludedWayIdentifiers());\n    }\n\n    private Atlas rebuildAtlas(final Atlas atlas, final Set<Long> pointsToRemove,\n            final Set<Long> pointsNeedingSyntheticTag)\n    {\n        final PackedAtlasBuilder rebuilder = new PackedAtlasBuilder();\n\n        // Set the metadata and size. Use existing Atlas as estimate.\n        rebuilder.setMetaData(this.metaData);\n        final AtlasSize size = new AtlasSize(0, 0, atlas.numberOfAreas(), atlas.numberOfLines(),\n                atlas.numberOfPoints(), atlas.numberOfRelations());\n        rebuilder.setSizeEstimates(size);\n\n        // Add Points\n        atlas.points().forEach(point ->\n        {\n            final long identifier = point.getIdentifier();\n            // Only add if this point isn't being removed\n            if (!pointsToRemove.contains(identifier))\n            {\n                final Map<String, String> tags = point.getTags();\n                if (pointsNeedingSyntheticTag.contains(identifier))\n                {\n                    // Add the synthetic tag\n                    tags.put(SyntheticDuplicateOsmNodeTag.KEY,\n                            SyntheticDuplicateOsmNodeTag.YES.toString());\n                }\n\n                // Add the Point\n                rebuilder.addPoint(identifier, point.getLocation(), tags);\n            }\n        });\n\n        // Add Lines\n        atlas.lines().forEach(\n                line -> rebuilder.addLine(line.getIdentifier(), line.asPolyLine(), line.getTags()));\n\n        // Add Lines\n        atlas.areas().forEach(\n                area -> rebuilder.addArea(area.getIdentifier(), area.asPolygon(), area.getTags()));\n\n        // Add Relations\n        // Keep a set of all relations that have members that have been removed, so if that member\n        // is the only member, we do not add the parent relation either.\n        final Set<Long> relationsToCheckForRemoval = new HashSet<>();\n        atlas.relationsLowerOrderFirst().forEach(relation ->\n        {\n            final RelationBean bean = new RelationBean();\n            relation.members().forEach(member ->\n            {\n                final AtlasEntity entity = member.getEntity();\n                final long memberIdentifier = entity.getIdentifier();\n                if (entity.getType() == ItemType.POINT && pointsToRemove.contains(memberIdentifier))\n                {\n                    // Make sure not to add any removed points\n                    logger.debug(\n                            \"Excluding point {} from relation {} since point was removed from Atlas\",\n                            memberIdentifier, relation.getIdentifier());\n                }\n                else if (entity.getType() == ItemType.RELATION\n                        && relationsToCheckForRemoval.contains(memberIdentifier))\n                {\n                    // Make sure not to add any removed relations\n                    logger.debug(\n                            \"Excluding relation member {} from parent relation {} since that relation member became empty\",\n                            memberIdentifier, relation.getIdentifier());\n                }\n                else\n                {\n                    bean.addItem(memberIdentifier, member.getRole(), entity.getType());\n                }\n            });\n\n            if (!bean.isEmpty())\n            {\n                rebuilder.addRelation(relation.getIdentifier(), relation.getOsmIdentifier(), bean,\n                        relation.getTags());\n            }\n            else\n            {\n                final long relationIdentifier = relation.getIdentifier();\n                logger.debug(\"Relation {} bean is empty, dropping from Atlas\", relationIdentifier);\n                relationsToCheckForRemoval.add(relationIdentifier);\n            }\n        });\n\n        // Build and return the new Atlas\n        return rebuilder.get();\n    }\n\n    /**\n     * We may need to remove {@link Point}s from the built raw Atlas. There are two scenarios for\n     * removal:\n     * <p>\n     * <ul>\n     * <li>1. A {@link Point} was a shape point for an OSM {@link Way} that was removed. This point\n     * doesn't have any tags, isn't a part of a {@link Relation} and doesn't intersect with any\n     * other features in the Atlas.\n     * <li>2. There are multiple {@link Point}s at a {@link Location}. In this case, we sort all the\n     * points, keep the one with the smallest identifier, add a {@link SyntheticDuplicateOsmNodeTag}\n     * and remove the rest of the duplicate points. Two notes: 1. We keep Nodes if they have\n     * different layer tagging. This way, we aren't creating a false connection between an overpass\n     * and a road beneath it, which happened to have a way node at the identical location. 2. We are\n     * potentially tossing out OSM Nodes with non-empty tags. However, this is the most\n     * deterministic and simple way to handle this. The presence of the synthetic tag will make it\n     * easy to write an Atlas Check to resolve the data error.\n     * </ul>\n     *\n     * @param atlas\n     *            The {@link Atlas} to remove the Points from\n     * @return a new {@link Atlas} without the extra points or the given Atlas if no removal is\n     *         needed\n     */\n    private Atlas removeDuplicateAndExtraneousPointsFromAtlas(final Atlas atlas)\n    {\n        final Set<Long> pointsToRemove = new HashSet<>();\n        final Set<Long> duplicatePointsToKeep = new HashSet<>();\n\n        for (final Point point : atlas.points())\n        {\n            // Don't try to de-duplicate points we've already handled\n            if (!pointsToRemove.contains(point.getIdentifier())\n                    && !duplicatePointsToKeep.contains(point.getIdentifier()))\n            {\n                final Set<Long> duplicatePoints = Iterables\n                        .stream(atlas.pointsAt(point.getLocation())).map(Point::getIdentifier)\n                        .collectToSet();\n                if (!duplicatePoints.isEmpty() && duplicatePoints.size() > 1)\n                {\n                    // Factor in ways that pass through these points. If these points are part of\n                    // ways that have a different layer tag value, then\n                    // keep all of them to avoid merging ways that shouldn't be merged.\n                    if (locationPartOfMultipleWaysWithDifferentLayerTags(atlas,\n                            point.getLocation()))\n                    {\n                        duplicatePointsToKeep.addAll(duplicatePoints);\n                        continue;\n                    }\n\n                    // Sort the points\n                    final Set<Long> sortedDuplicates = Iterables.asSortedSet(duplicatePoints);\n                    final Set<Long> uniqueLayerValues = new HashSet<>();\n\n                    // Keep the point with the smallest identifier (deterministic) for each layer\n                    final Iterator<Long> duplicateIterator = sortedDuplicates.iterator();\n                    final long duplicatePointToKeep = duplicateIterator.next();\n                    final long layerValue = getLayerTagValueForPoint(atlas, duplicatePointToKeep);\n\n                    duplicatePointsToKeep.add(duplicatePointToKeep);\n                    duplicateIterator.remove();\n                    uniqueLayerValues.add(layerValue);\n\n                    while (duplicateIterator.hasNext())\n                    {\n                        final long candidateToKeep = duplicateIterator.next();\n                        final long candidateLayerValue = getLayerTagValueForPoint(atlas,\n                                candidateToKeep);\n\n                        if (!uniqueLayerValues.contains(candidateLayerValue))\n                        {\n                            // Keep the point if it has a unique layer value\n                            duplicatePointsToKeep.add(candidateToKeep);\n                            duplicateIterator.remove();\n                        }\n                    }\n\n                    // Remove all remaining (non-kept) points\n                    pointsToRemove.addAll(sortedDuplicates);\n                }\n            }\n        }\n\n        // Remove any non-used shape points from filtered lines\n        if (!this.pbfReader.getPointIdentifiersFromFilteredLines().isEmpty())\n        {\n            pointsToRemove.addAll(preFilterPointsToRemove(atlas));\n        }\n\n        // Remove points or return the original atlas\n        if (pointsToRemove.isEmpty())\n        {\n            return atlas;\n        }\n        else\n        {\n            // Rebuild the Atlas to add the synthetic tags and get rid of the removed points\n            return rebuildAtlas(atlas, pointsToRemove, duplicatePointsToKeep);\n        }\n    }\n\n    /**\n     * Sets the {@link AtlasSize} to efficiently build the raw {@link Atlas}, using the values\n     * obtained from the {@link OsmPbfCounter}.\n     */\n    private void setAtlasSizeEstimate()\n    {\n        final AtlasSize size = new AtlasSize(0, 0, this.pbfCounter.lineCount(),\n                this.pbfCounter.lineCount(), this.pbfCounter.pointCount(),\n                this.pbfCounter.relationCount());\n        this.builder.setSizeEstimates(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/creation/RawAtlasStatistic.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.creation;\n\nimport java.util.function.Consumer;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CounterWithStatistic;\nimport org.slf4j.Logger;\n\n/**\n * Keeps track of created and filtered (by configuration) {@link Point}s, {@link Line}s and\n * {@link Relation}s during raw {@link Atlas} generation - see {@link OsmPbfReader}. Also, tracks\n * any {@link Relation}s that were dropped.\n *\n * @author mgostintsev\n */\npublic class RawAtlasStatistic\n{\n    private static final long LOG_FREQUENCY = 10_000;\n    private final Logger logger;\n\n    // Added entities\n    private final CounterWithStatistic points;\n    private final CounterWithStatistic lines;\n    private final CounterWithStatistic relations;\n\n    // Filtered entities\n    private final CounterWithStatistic filteredNodes;\n    private final CounterWithStatistic filteredWays;\n    private final CounterWithStatistic filteredRelations;\n\n    // Dropped entities\n    private final CounterWithStatistic droppedWays;\n    private final CounterWithStatistic droppedRelations;\n\n    public RawAtlasStatistic(final Logger logger)\n    {\n        this.logger = logger;\n        final Consumer<String> log = logger::info;\n\n        this.points = new CounterWithStatistic(this.logger, LOG_FREQUENCY, \"Added Point\");\n        this.points.logUsingLevel(log);\n        this.lines = new CounterWithStatistic(this.logger, LOG_FREQUENCY, \"Added Line\");\n        this.lines.logUsingLevel(log);\n        this.relations = new CounterWithStatistic(this.logger, LOG_FREQUENCY, \"Added Relation\");\n        this.relations.logUsingLevel(log);\n\n        this.filteredNodes = new CounterWithStatistic(this.logger, LOG_FREQUENCY, \"Filtered Node\");\n        this.filteredNodes.logUsingLevel(log);\n        this.filteredWays = new CounterWithStatistic(this.logger, LOG_FREQUENCY, \"Filtered Way\");\n        this.filteredWays.logUsingLevel(log);\n        this.filteredRelations = new CounterWithStatistic(this.logger, LOG_FREQUENCY,\n                \"Filtered Relation\");\n        this.filteredRelations.logUsingLevel(log);\n\n        this.droppedWays = new CounterWithStatistic(this.logger, LOG_FREQUENCY, \"Dropped Way\");\n        this.droppedWays.logUsingLevel(log);\n        this.droppedRelations = new CounterWithStatistic(this.logger, LOG_FREQUENCY,\n                \"Dropped Relation\");\n        this.droppedRelations.logUsingLevel(log);\n    }\n\n    public void recordCreatedLine()\n    {\n        this.lines.increment();\n    }\n\n    public void recordCreatedPoint()\n    {\n        this.points.increment();\n    }\n\n    public void recordCreatedRelation()\n    {\n        this.relations.increment();\n    }\n\n    public void recordDroppedRelation()\n    {\n        this.droppedRelations.increment();\n    }\n\n    public void recordDroppedWay()\n    {\n        this.droppedWays.increment();\n    }\n\n    public void recordFilteredNode()\n    {\n        this.filteredNodes.increment();\n    }\n\n    public void recordFilteredRelation()\n    {\n        this.filteredRelations.increment();\n    }\n\n    public void recordFilteredWay()\n    {\n        this.filteredWays.increment();\n    }\n\n    public void summary()\n    {\n        this.logger.trace(\"PBF to Raw Atlas Summary\");\n        this.points.summaryWithoutTimer();\n        this.lines.summaryWithoutTimer();\n        this.relations.summaryWithoutTimer();\n        this.filteredNodes.summaryWithoutTimer();\n        this.filteredWays.summaryWithoutTimer();\n        this.filteredRelations.summaryWithoutTimer();\n        this.droppedWays.summaryWithoutTimer();\n        this.droppedRelations.summaryWithoutTimer();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/sectioning/AtlasSectionProcessor.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.sectioning;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeAtlas;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeBuilder;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.DynamicAtlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.WaySectionIdentifierFactory;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.tags.LayerTag;\nimport org.openstreetmap.atlas.tags.SyntheticInvalidWaySectionTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Node;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Way-section processor that converts Lines to Edges. This will work in two ways: 1. Section a\n * given atlas. 2. Given a shard, sharding and an atlas fetcher policy to - leverage\n * {@link DynamicAtlas} to build an Atlas that contains all edges from the initial shard to their\n * completion as well as any edges that may intersect them. For the second case above, we are\n * guaranteed to have consistent identifiers across shards after way-sectioning, since we are\n * relying on the line shape point order creation and identifying all edge intersections that span\n * shard boundaries.\n *\n * @author mgostintsev\n * @author samg\n */\npublic class AtlasSectionProcessor\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasSectionProcessor.class);\n\n    private static final int MINIMUM_NODES_TO_QUALIFY_AS_A_EDGE = 2;\n\n    // Logging constants\n    private static final String STARTED_SECTIONING = \"Started way-sectioning for Atlas {}\";\n    private static final String FINISHED_SECTIONING = \"Finished way-sectioning for Atlas {} in {}\";\n    private static final String STARTED_EDGE_CREATION = \"Started creating Edges for Atlas {}\";\n    private static final String FINISHED_EDGE_CREATION = \"Finished creating Edges for Atlas {} in {}}\";\n    private static final String STARTED_NODE_CREATION = \"Started creating Nodes for Atlas {}\";\n    private static final String FINISHED_NODE_CREATION = \"Finished creating Nodes for Atlas {} in {}}\";\n    private static final String STARTED_EXCESS_POINT_REMOVAL = \"Started removing excess Points for Atlas {}\";\n    private static final String FINISHED_EXCESS_POINT_REMOVAL = \"Finished removing excess Points for Atlas {} in {}}\";\n    private static final String STARTED_POINT_ADDITION = \"Started adding additional Points for Atlas {}\";\n    private static final String FINISHED_POINT_ADDITION = \"Finished adding additional Points for Atlas {} in {}\";\n\n    // Expand the initial shard boundary to capture any edges that are crossing the shard boundary\n    private static final Distance SHARD_EXPANSION_DISTANCE = Distance.meters(20);\n\n    private final Atlas inputAtlas;\n    private final AtlasLoadingOption loadingOption;\n\n    // Bring in all lines that will become edges\n    private final List<Shard> loadedShards = new ArrayList<>();\n    private final Predicate<AtlasEntity> dynamicAtlasExpansionFilter;\n\n    private final Set<FeatureChange> changes = Collections\n            .newSetFromMap(new ConcurrentHashMap<FeatureChange, Boolean>());\n    private final Map<Location, CompleteNode> nodeMap = new ConcurrentHashMap<>();\n\n    /**\n     * Determines if we should section at the given {@link Location}. Relies on the underlying\n     * {@link AtlasLoadingOption} configuration to make the decision. If true, this implies the\n     * point at this {@link Location} should be a {@link Node}. Sectioning is either based on the\n     * tag values of the underlying points at the location, or the existence of an intersecting Edge\n     * at that location\n     *\n     * @param location\n     *            The {@link Location} to check\n     * @param line\n     *            The {@link Line} this {@link Location} belongs to\n     * @param loading\n     *            The AtlasLoadingOption to use for Edge test\n     * @param inputAtlas\n     *            The source input atlas\n     * @return {@code true} if we should section at the given {@link Location}\n     */\n    public static boolean shouldSectionAtLocation(final Location location, final Line line,\n            final AtlasLoadingOption loading, final Atlas inputAtlas)\n    {\n        final long targetLayerValue = LayerTag.getTaggedOrImpliedValue(line, 0L);\n\n        return Iterables.stream(inputAtlas.pointsAt(location))\n                .anyMatch(point -> loading.getWaySectionFilter().test(point))\n                || Iterables\n                        // Find all intersecting edges\n                        .stream(inputAtlas.linesContaining(location,\n                                target -> target.getIdentifier() != line.getIdentifier()\n                                        && target.asPolyLine().contains(location)))\n                        .filter(loading.getEdgeFilter()::test)\n                        // Check whether that edge has a different layer value as the line we're\n                        // looking at and that our point is its start or end node\n                        .anyMatch(candidate ->\n                        {\n                            final long layerValue = LayerTag.getTaggedOrImpliedValue(candidate, 0L);\n                            if (targetLayerValue == layerValue)\n                            {\n                                return true;\n                            }\n                            final boolean edgesOnDifferentLayers = targetLayerValue != layerValue;\n                            final PolyLine candidatePolyline = candidate.asPolyLine();\n                            final boolean intersectionIsAtEndPoint = candidatePolyline.first()\n                                    .equals(location) || candidatePolyline.last().equals(location);\n\n                            return edgesOnDifferentLayers && intersectionIsAtEndPoint;\n                        });\n    }\n\n    /**\n     * Default constructor. Will section given raw {@link Atlas} file.\n     *\n     * @param inputAtlas\n     *            The {@link Atlas} to section\n     * @param loadingOption\n     *            The {@link AtlasLoadingOption} to use\n     */\n    public AtlasSectionProcessor(final Atlas inputAtlas, final AtlasLoadingOption loadingOption)\n    {\n        this.inputAtlas = inputAtlas;\n        this.loadingOption = loadingOption;\n        this.dynamicAtlasExpansionFilter = entity -> entity instanceof Line\n                && this.loadingOption.getEdgeFilter().test(entity);\n    }\n\n    /**\n     * Takes in a starting {@link Shard} and uses the given sharding and atlas fetcher function to\n     * build a {@link DynamicAtlas}, which is then sectioned. This guarantees consistent identifiers\n     * across the constructed atlas. The sharding and atlas fetcher function must be provided and\n     * the sharding must the same as the one used to generate the input shard. The overall logic for\n     * atlas construction and sectioning is:\n     * <ul>\n     * <li>Grab the atlas for the starting shard in its entirety, expand out if there are any edges\n     * bleeding into neighboring shards.\n     * <li>Once the full atlas is built, way-section it.\n     * <li>After sectioning is completed and atlas is rebuild, cut a sub-atlas representing the\n     * bounds of the original shard being processed. This is a soft cut, so any edges that start in\n     * the shard and end in neighboring shards, will be captured.\n     * </ul>\n     *\n     * @param initialShard\n     *            The initial {@link Shard} to start at\n     * @param loadingOption\n     *            The {@link AtlasLoadingOption} to use\n     * @param sharding\n     *            The {@link Sharding} to use to know which neighboring atlas files to get\n     * @param atlasFetcher\n     *            The fetching policy to use to obtain adjacent atlas files\n     */\n    public AtlasSectionProcessor(final Shard initialShard, final AtlasLoadingOption loadingOption,\n            final Sharding sharding, final Function<Shard, Optional<Atlas>> atlasFetcher)\n    {\n        this.loadingOption = loadingOption;\n        this.dynamicAtlasExpansionFilter = entity -> entity instanceof Line\n                && this.loadingOption.getEdgeFilter().test(entity);\n        if (sharding == null || atlasFetcher == null)\n        {\n            throw new IllegalArgumentException(\n                    \"Must supply a valid sharding and fetcher function for sectioning!\");\n        }\n        this.inputAtlas = buildExpandedAtlas(initialShard, sharding, atlasFetcher);\n    }\n\n    /**\n     * Slices the given {@link Atlas}.\n     *\n     * @return the way-sectioned {@link Atlas}\n     */\n    public Atlas run()\n    {\n        final Time overallTime = Time.now();\n        Time time = Time.now();\n\n        logger.info(STARTED_SECTIONING, this.getShardOrAtlasName());\n\n        logger.info(STARTED_EDGE_CREATION, this.getShardOrAtlasName());\n        this.inputAtlas.lines(this.loadingOption.getEdgeFilter()::test).forEach(this::section);\n        logger.info(FINISHED_EDGE_CREATION, this.getShardOrAtlasName(),\n                time.elapsedSince().asMilliseconds());\n\n        time = Time.now();\n        logger.info(STARTED_NODE_CREATION, this.getShardOrAtlasName());\n        this.nodeMap.values()\n                .forEach(node -> this.changes.add(FeatureChange.add(node, this.inputAtlas)));\n        logger.info(FINISHED_NODE_CREATION, this.getShardOrAtlasName(),\n                time.elapsedSince().asMilliseconds());\n\n        time = Time.now();\n        // If this atlas is supposed to keep everything, add the points that are not also saved as a\n        // node.\n        if (this.loadingOption.isKeepAll())\n        {\n            logger.info(STARTED_POINT_ADDITION, this.getShardOrAtlasName());\n            this.inputAtlas.points().forEach(point ->\n            {\n                final CompleteNode possibleDupe = this.nodeMap.get(point.getLocation());\n                if (possibleDupe == null\n                        || possibleDupe.getOsmIdentifier() != point.getOsmIdentifier())\n                {\n                    this.changes.add(FeatureChange.add(CompletePoint.from(point)));\n                }\n            });\n            logger.info(FINISHED_POINT_ADDITION, this.getShardOrAtlasName(),\n                    time.elapsedSince().asMilliseconds());\n        }\n        else\n        {\n            logger.info(STARTED_EXCESS_POINT_REMOVAL, this.getShardOrAtlasName());\n            this.inputAtlas.points().forEach(point ->\n            {\n                // we care about a point if and only if it has pre-existing OSM tags OR it belongs\n                // to a future edge OR we are doing QA\n                if (!this.loadingOption.isKeepAll() && point.getOsmTags().isEmpty()\n                        && point.relations().isEmpty())\n                {\n                    this.changes.add(FeatureChange.remove(CompletePoint.shallowFrom(point)));\n                }\n            });\n            logger.info(FINISHED_EXCESS_POINT_REMOVAL, this.getShardOrAtlasName(),\n                    time.elapsedSince().asMilliseconds());\n        }\n\n        logger.info(FINISHED_SECTIONING, this.getShardOrAtlasName(),\n                overallTime.elapsedSince().asMilliseconds());\n\n        // return either the unchanged original Atlas, or a cut-down version of the sectioned Atlas\n        if (this.changes.isEmpty())\n        {\n            if (this.loadedShards.isEmpty() || this.loadedShards.size() == 1)\n            {\n                return this.inputAtlas.cloneToPackedAtlas();\n            }\n            return cutSubAtlasForOriginalShard(this.inputAtlas).cloneToPackedAtlas();\n        }\n\n        final String country = this.loadingOption.getCountryCode();\n        final String shardOrAtlasName = this.getShardOrAtlasName();\n        final ChangeAtlas sectionedAtlas = new ChangeAtlas(this.inputAtlas,\n                new ChangeBuilder().addAll(this.changes).get())\n        {\n            private static final long serialVersionUID = -1379576156041355921L;\n\n            @Override\n            public synchronized AtlasMetaData metaData()\n            {\n                // Override meta-data here so the country code is properly included.\n                final AtlasMetaData metaData = super.metaData();\n                final var originalTags = metaData.getTags();\n                // Remove country shards to keep old behavior where they were dropped, but keep\n                // other tags.\n                originalTags.remove(\"countryShards\");\n                return new AtlasMetaData(metaData.getSize(), false,\n                        metaData.getCodeVersion().orElse(null),\n                        metaData.getDataVersion().orElse(null), country, shardOrAtlasName,\n                        originalTags);\n            }\n        };\n        if (this.loadedShards.isEmpty())\n        {\n            return sectionedAtlas.cloneToPackedAtlas();\n        }\n        return cutSubAtlasForOriginalShard(sectionedAtlas).cloneToPackedAtlas();\n    }\n\n    /**\n     * Grabs the atlas for the initial shard, in its entirety. Then proceeds to expand out to\n     * surrounding shards if there are any edges bleeding over the shard bounds plus\n     * {@link #SHARD_EXPANSION_DISTANCE}. Finally, will return the constructed Atlas.\n     *\n     * @param initialShard\n     *            The initial {@link Shard} being processed\n     * @param sharding\n     *            The {@link Sharding} used to identify which shards to fetch\n     * @param fullySlicedAtlasFetcher\n     *            The fetcher policy to retrieve an Atlas file for each shard\n     * @return the expanded {@link Atlas}\n     */\n    private Atlas buildExpandedAtlas(final Shard initialShard, final Sharding sharding,\n            final Function<Shard, Optional<Atlas>> fullySlicedAtlasFetcher)\n    {\n        // Keep track of all loaded shards. This will be used to cut the sub-atlas for the shard\n        // we're processing after all sectioning is completed. Initial shard will always be first!\n        this.loadedShards.add(initialShard);\n\n        final DynamicAtlasPolicy policy = new DynamicAtlasPolicy(fullySlicedAtlasFetcher, sharding,\n                initialShard.bounds().expand(SHARD_EXPANSION_DISTANCE), Rectangle.MAXIMUM)\n                .withDeferredLoading(true).withExtendIndefinitely(false)\n                .withAtlasEntitiesToConsiderForExpansion(this.dynamicAtlasExpansionFilter::test);\n\n        final DynamicAtlas atlas = new DynamicAtlas(policy);\n        atlas.preemptiveLoad();\n        return atlas;\n    }\n\n    /**\n     * Takes a polyline for a new Edge and adds the feature to the ChangeSet\n     *\n     * @param line\n     *            The {@link Line} being converted to an {@link Edge}\n     * @param edgePolyLine\n     *            The polyline defining the geometry of the {@link Edge}\n     * @param edgeIdentifier\n     *            The identifier for the {@link Edge}\n     * @param hasReverseEdge\n     *            Boolean for if a reverse {@link Edge} should be made as well\n     * @param tags\n     *            The tags for the new {@link Edge}\n     */\n    private void createEdge(final Line line, final PolyLine edgePolyLine, final long edgeIdentifier,\n            final boolean hasReverseEdge, final Map<String, String> tags)\n    {\n        // if a node already exists for the start/end locations, use theml otherwise make new ones\n        CompleteNode startNode = this.nodeMap.get(edgePolyLine.first());\n        if (startNode == null)\n        {\n            final SortedSet<Long> inEdges = new TreeSet<>();\n            if (hasReverseEdge)\n            {\n                inEdges.add(-edgeIdentifier);\n            }\n            final SortedSet<Long> outEdges = new TreeSet<>();\n            outEdges.add(edgeIdentifier);\n            startNode = createNode(line, edgePolyLine.first(), inEdges, outEdges);\n        }\n        else\n        {\n            if (hasReverseEdge)\n            {\n                startNode.withAddedInEdgeIdentifier(-edgeIdentifier);\n            }\n            startNode.withAddedOutEdgeIdentifier(edgeIdentifier);\n        }\n\n        CompleteNode endNode = this.nodeMap.get(edgePolyLine.last());\n        if (endNode == null)\n        {\n            final SortedSet<Long> inEdges = new TreeSet<>();\n            inEdges.add(edgeIdentifier);\n            final SortedSet<Long> outEdges = new TreeSet<>();\n            if (hasReverseEdge)\n            {\n                outEdges.add(-edgeIdentifier);\n            }\n            endNode = createNode(line, edgePolyLine.last(), inEdges, outEdges);\n        }\n        else\n        {\n            if (hasReverseEdge)\n            {\n                endNode.withAddedOutEdgeIdentifier(-edgeIdentifier);\n            }\n            endNode.withAddedInEdgeIdentifier(edgeIdentifier);\n        }\n        final Set<Long> relations = new HashSet<>();\n        line.relations().forEach(relation -> relations.add(relation.getIdentifier()));\n\n        final CompleteEdge newEdge = new CompleteEdge(edgeIdentifier, edgePolyLine, tags,\n                startNode.getIdentifier(), endNode.getIdentifier(), relations);\n        final Set<Long> nonGeometricRelations = new HashSet<>();\n        for (final Long relationId : relations)\n        {\n            if (!this.inputAtlas.relation(relationId).isGeometric())\n            {\n                nonGeometricRelations.add(relationId);\n            }\n        }\n        final CompleteEdge newReverseEdge = new CompleteEdge(-edgeIdentifier,\n                edgePolyLine.reversed(), tags, endNode.getIdentifier(), startNode.getIdentifier(),\n                nonGeometricRelations);\n        updateRelations(line, newEdge, newReverseEdge, hasReverseEdge);\n\n        this.changes.add(FeatureChange.add(newEdge, this.inputAtlas));\n        if (hasReverseEdge)\n        {\n            this.changes.add(FeatureChange.add(newReverseEdge, this.inputAtlas));\n        }\n    }\n\n    /**\n     * Helper method to make a new Node for an Edge\n     *\n     * @param line\n     *            The {@link Line} being converted to an {@link Edge}\n     * @param nodeLocation\n     *            The {@link Location} for the {@link Node} being made\n     * @param inEdges\n     *            The identifiers for the {@link Edge}s going into the {@link Node} being made\n     * @param outEdges\n     *            The identifiers for the {@link Edge}s going out of the {@link Node} being made\n     * @return\n     */\n    private CompleteNode createNode(final Line line, final Location nodeLocation,\n            final SortedSet<Long> inEdges, final SortedSet<Long> outEdges)\n    {\n        if (!this.inputAtlas.pointsAt(nodeLocation).iterator().hasNext())\n        {\n            throw new CoreException(\n                    \"Couldn't find node at {} while sectioning Line {} for Atlas {}\",\n                    nodeLocation.toString(), line.toString(), getShardOrAtlasName());\n        }\n        final Point pointForNode = this.inputAtlas.pointsAt(nodeLocation).iterator().next();\n        // Drop nodes that don't have tags when we don't need them for other purposes (e.g., QA)\n        if (!this.loadingOption.isKeepAll() && pointForNode.getOsmTags().isEmpty())\n        {\n            this.changes.add(FeatureChange.remove(CompletePoint.shallowFrom(pointForNode)));\n        }\n        final Set<Long> relationIds = new HashSet<>();\n        pointForNode.relations().forEach(relation -> relationIds.add(relation.getIdentifier()));\n        final CompleteNode node = new CompleteNode(pointForNode.getIdentifier(),\n                pointForNode.getLocation(), pointForNode.getTags(), inEdges, outEdges, relationIds);\n        this.nodeMap.put(node.getLocation(), node);\n\n        pointForNode.relations().forEach(relation -> relation\n                .membersMatching(member -> member.getEntity().getType().equals(ItemType.POINT)\n                        && member.getEntity().getIdentifier() == pointForNode.getIdentifier())\n                .forEach(member -> this.changes.add(FeatureChange.add(CompleteRelation\n                        .shallowFrom(relation).withAddedMember(node, member.getRole())))));\n        return node;\n    }\n\n    /**\n     * Takes a given Line and its Nodes, and turns into Edges starting and ending at each Node.\n     *\n     * @param line\n     *            The {@link Line} being converted to an {@link Edge}\n     * @param nodes\n     *            The identifiers for the {@link Point}s that will be converted into the\n     *            {@link Node} for the {@link Edge}\n     * @param isReversed\n     *            Boolean for if the geometry of the {@link Edge} should reversed\n     * @param hasReverseEdge\n     *            Boolean for if a reverse {@link Edge} should be made as well\n     * @param remainder\n     *            Any remaining linear geometry at the end of the {@link Line} being converted to an\n     *            {@link Edge}-- in some circumstances, this geometry will be converted to its own\n     *            {@link Edge}, but in many cases it will be combined into the last {@link Edge} to\n     *            reduce the number of {@link Edge}s made\n     */\n    private void createSections(final Line line, final List<Integer> nodes,\n            final boolean isReversed, final boolean hasReverseEdge, final PolyLine remainder)\n    {\n        // Prepare the nodes identifiers, identifier factory and one way information\n        final WaySectionIdentifierFactory identifierFactory = new WaySectionIdentifierFactory(\n                line.getIdentifier());\n\n        // if the edge geometry is going to be singular, make that directly and bypass the loops\n        if (remainder != null && remainder.size() == line.asPolyLine().size())\n        {\n            createEdge(line, remainder, identifierFactory.nextIdentifier(), hasReverseEdge,\n                    line.getTags());\n            return;\n        }\n        else if (!line.isClosed() && nodes.size() == 2 && nodes.get(0) == 0\n                && line.asPolyLine().size() - 1 == nodes.get(1))\n        {\n            createEdge(line, isReversed ? line.asPolyLine().reversed() : line.asPolyLine(),\n                    line.getIdentifier(), hasReverseEdge, line.getTags());\n            return;\n        }\n\n        final Iterator<Integer> nodesIterator = nodes.iterator();\n        int startIndex = nodesIterator.next();\n\n        // iterate over all node locations and make edges for each polyline section\n        while (identifierFactory.hasMore() && nodesIterator.hasNext())\n        {\n            int endIndex = nodesIterator.next();\n            final long edgeIdentifier = identifierFactory.nextIdentifier();\n            final Map<String, String> tags = line.getTags();\n\n            // if there are no more identifiers left, fast forward to the end of the line\n            if (!identifierFactory.hasMore())\n            {\n                // Update the tags to indicate this edge wasn't way-sectioned\n                tags.put(SyntheticInvalidWaySectionTag.KEY,\n                        SyntheticInvalidWaySectionTag.YES.toString());\n                endIndex = line.asPolyLine().size() - 1;\n            }\n\n            final PolyLine rawPolyLine = new PolyLine(line.asPolyLine().truncate(startIndex,\n                    line.asPolyLine().size() - endIndex - 1));\n            PolyLine edgePolyLine = isReversed ? rawPolyLine.reversed() : rawPolyLine;\n\n            // if this is the last node, deal with the remainder. if the remainder\n            if (!nodesIterator.hasNext() && remainder != null)\n            {\n                final Location potentialStitchLocation = isReversed ? edgePolyLine.first()\n                        : edgePolyLine.last();\n\n                // if we need a section at the last location, we'll make the remainder its own edge.\n                // otherwise, we'll combine it with the last edge to reduce excess edges\n                if (shouldSectionAtLocation(potentialStitchLocation, line, this.loadingOption,\n                        this.inputAtlas))\n                {\n                    final long remainderIdentifier = identifierFactory.nextIdentifier();\n                    createEdge(line, remainder, remainderIdentifier, hasReverseEdge, tags);\n                }\n                else\n                {\n                    edgePolyLine = isReversed ? remainder.append(edgePolyLine)\n                            : edgePolyLine.append(remainder);\n                }\n            }\n\n            createEdge(line, edgePolyLine, edgeIdentifier, hasReverseEdge, tags);\n            startIndex = endIndex;\n        }\n    }\n\n    /**\n     * Up to this point, we've constructed the {@link DynamicAtlas} and way-sectioned it. Since\n     * we're only responsible for returning an Atlas for the provided shard, we now need to cut a\n     * sub-atlas for the initial shard boundary and return it. We can leverage the loaded shards\n     * parameter, which will always contain the starting shard as the first shard and all other\n     * loaded shards after. If no other shards were loaded, simply return the given Atlas.\n     *\n     * @param atlas\n     *            The {@link Atlas} file we need to trim\n     * @return the {@link Atlas} for the bounds of the input shard\n     */\n    private Atlas cutSubAtlasForOriginalShard(final Atlas atlas)\n    {\n        try\n        {\n            // The first shard is always the initial one. Use its bounds to build the atlas.\n            final Rectangle originalShardBounds = this.loadedShards.get(0).bounds();\n            return atlas.subAtlas(originalShardBounds, AtlasCutType.SOFT_CUT)\n                    .orElseThrow(() -> new CoreException(\n                            \"Cannot have an empty atlas after way sectioning {}\",\n                            this.loadedShards.get(0).getName()));\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Error creating sub-atlas for original shard bounds\", e);\n        }\n    }\n\n    /**\n     * Iterate over a Line's locations to determine which ones qualify as a location to section at\n     *\n     * @param line\n     *            The {@link Line} being converted to an {@link Edge}\n     * @param linePolyLine\n     *            The polyline for the {@link Line} being converted to an {@link Edge}\n     * @return\n     */\n    private List<Integer> findNodesForEdge(final Line line, final PolyLine linePolyLine)\n    {\n        final List<Integer> nodesForEdge = new ArrayList<>();\n        final Set<Location> selfIntersections = linePolyLine.selfIntersections();\n        Location previousLocation = null;\n        for (int i = 0; i < linePolyLine.size(); i++)\n        {\n            final Location location = linePolyLine.get(i);\n            if (i == 0 || i == linePolyLine.size() - 1)\n            {\n                nodesForEdge.add(i);\n            }\n            else if (location.equals(previousLocation))\n            {\n                // NOOP\n            }\n            else if (selfIntersections.contains(location)\n                    || shouldSectionAtLocation(location, line, this.loadingOption, this.inputAtlas))\n            {\n                nodesForEdge.add(i);\n            }\n            previousLocation = location;\n        }\n        return nodesForEdge;\n    }\n\n    /**\n     * Just a helper method to use a consistent naming scheme\n     *\n     * @return A String for either the Shard name or the Atlas name\n     */\n    private String getShardOrAtlasName()\n    {\n        // Default to getting the Shard name, if available, otherwise fall back to atlas name\n        if (!this.loadedShards.isEmpty())\n        {\n            return this.loadedShards.get(0).getName();\n        }\n        else\n        {\n            return this.inputAtlas.getName();\n        }\n    }\n\n    /**\n     * Takes a Line, finds its Nodes, then makes Edges for each section.\n     *\n     * @param line\n     *            The {@link Line} being converted to an {@link Edge}\n     */\n    private void section(final Line line)\n    {\n        final PolyLine polyLine = line.asPolyLine();\n\n        // Determines if we need to reverse the polyline and if a reverse edge is needed\n        final PbfOneWay oneWay = PbfOneWay.forTag(line);\n        final boolean hasReverseEdge = oneWay == PbfOneWay.NO;\n        final boolean isReversed = oneWay == PbfOneWay.REVERSED;\n\n        final List<Integer> nodesForEdge = findNodesForEdge(line, polyLine);\n\n        if (nodesForEdge.size() < MINIMUM_NODES_TO_QUALIFY_AS_A_EDGE)\n        {\n            logger.error(\"Edge {} hass less than {} nodes, cannot be sectioned!\",\n                    line.getIdentifier(), MINIMUM_NODES_TO_QUALIFY_AS_A_EDGE);\n            this.changes\n                    .add(FeatureChange.add(\n                            CompleteLine.shallowFrom(line).withTags(line.getTags()).withAddedTag(\n                                    SyntheticInvalidWaySectionTag.KEY,\n                                    SyntheticInvalidWaySectionTag.YES.toString()),\n                            this.inputAtlas));\n            return;\n        }\n\n        // Initialize start location\n        PolyLine remainder = null;\n\n        // we'll preprocess rings a bit to make them consistently sectioned\n        if (line.isClosed())\n        {\n            if (nodesForEdge.size() == 2)\n            {\n                // the node is just the beginning/end of the Edge, and connects to another Edge,\n                // like a cul-de-sac\n                if (nodesForEdge.get(0) == 0\n                        && nodesForEdge.get(1) == line.numberOfShapePoints() - 1\n                        && shouldSectionAtLocation(polyLine.get(0), line, this.loadingOption,\n                                this.inputAtlas))\n                {\n                    // we just want a single Edge for the whole loop, connecting back to itself\n                    // noop\n                }\n                else\n                {\n                    // corner case-- a ring with no other connected Edges that starts and ends at\n                    // the same node. make an artificial node halfway through to make two Edges\n                    nodesForEdge.remove(1);\n                    nodesForEdge.add(polyLine.size() / 2);\n                    nodesForEdge.add(polyLine.size() - 1);\n                }\n            }\n            else if (polyLine.isSimple())\n            {\n                final int nextIndex = nodesForEdge.get(nodesForEdge.size() - 2);\n                remainder = new PolyLine(polyLine.truncate(nextIndex, 0));\n                nodesForEdge.remove(nodesForEdge.size() - 1);\n                if (!shouldSectionAtLocation(polyLine.get(0), line, this.loadingOption,\n                        this.inputAtlas))\n                {\n                    nodesForEdge.remove(0);\n                    final int startIndex = nodesForEdge.get(0);\n                    remainder = remainder.append(\n                            new PolyLine(polyLine.truncate(0, polyLine.size() - 1 - startIndex)));\n                }\n                if (isReversed)\n                {\n                    remainder = remainder.reversed();\n                }\n            }\n        }\n\n        createSections(line, nodesForEdge, isReversed, hasReverseEdge, remainder);\n        this.changes.add(FeatureChange.remove(CompleteLine.shallowFrom(line), this.inputAtlas));\n    }\n\n    /**\n     * Iterate over Relations to make sure they're synchronized with the changes for new Edges\n     *\n     * @param line\n     *            The {@link Line} being converted to an {@link Edge}\n     * @param newEdge\n     *            The new {@link Edge}\n     * @param newReverseEdge\n     *            The new reverse {@link Edge}\n     * @param hasReverseEdge\n     *            Boolean representing the existence of a reverse {@link Edge}\n     */\n    private void updateRelations(final Line line, final CompleteEdge newEdge,\n            final CompleteEdge newReverseEdge, final boolean hasReverseEdge)\n    {\n        line.relations().forEach(relation -> relation\n                .membersMatching(member -> member.getEntity().getType().equals(ItemType.LINE)\n                        && member.getEntity().getIdentifier() == line.getIdentifier())\n                .forEach(member ->\n                {\n                    this.changes.add(FeatureChange.add(CompleteRelation.shallowFrom(relation)\n                            .withAddedMember(newEdge, member.getRole())));\n                    if (hasReverseEdge && !relation.isGeometric())\n                    {\n                        this.changes.add(FeatureChange.add(CompleteRelation.shallowFrom(relation)\n                                .withAddedMember(newReverseEdge, member.getRole())));\n                    }\n                }));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/sectioning/PbfOneWay.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.sectioning;\n\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.JunctionTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.tags.oneway.OneWayTag;\n\n/**\n * One way attribute of an OSM Way\n *\n * @author tony\n * @author matthieun\n */\npublic enum PbfOneWay\n{\n    YES,\n    NO,\n    REVERSED;\n\n    /**\n     * Determines the whether the given {@link Taggable} is a one-way, non-one-way, reversed or\n     * closed Edge.\n     *\n     * @param taggable\n     *            The {@link Taggable} to look at\n     * @return the {@link PbfOneWay} for the given {@link Taggable}\n     */\n    public static PbfOneWay forTag(final Taggable taggable)\n    {\n        if (OneWayTag.isExplicitlyTwoWay(taggable))\n        {\n            return NO;\n        }\n        else if (OneWayTag.isTwoWay(taggable))\n        {\n            if (JunctionTag.isRoundabout(taggable)\n                    || Validators.isOfType(taggable, HighwayTag.class, HighwayTag.MOTORWAY))\n            {\n                // Override the two-way here, as a roundabout takes precedence as a one way road in\n                // OSM, when no one way tag is specified. Similarly, a motorway tag implies a\n                // one way road. The same does NOT hold true for motorway_link.\n                return YES;\n            }\n            return NO;\n        }\n        else if (OneWayTag.isOneWayForward(taggable))\n        {\n            return YES;\n        }\n        else if (OneWayTag.isOneWayReversed(taggable))\n        {\n            return REVERSED;\n        }\n        else\n        {\n            return NO;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/sectioning/README.md",
    "content": "# `Atlas Sectioning`\n\n## Overview\n\nAtlas Sectioning is the process of converting certain `Lines` and `Points` into navigable `Edges` and `Nodes`. As input, the process takes in an Atlas with no `Edges` or `Nodes` and produces one that has these entities. \n\n## Detailed Steps\n\nThe sectioning process is comprised of the following steps:\n\n1. Assemble a `DynamicAtlas` that will be used for sectioning. The reason for a `DynamicAtlas` is to be able to capture all OSM Ways that go outside the `Shard` bounds being processed and also capture any intersections of the each of these ways. This is the driving force behind the guarantee of consistent `Edge` identifiers. If we can see a way in its entirety as well as all of its intersecting ways, we can section it deterministically and consistently, irrespective of which `Shard` we are looking from and apply this consistent view to the current `Shard` being processed. The logic for building the `DynamicAtlas` is to grab the full Atlas for the initial `Shard`, then proceed to expand out to surrounding Shards if there are any Edges bleeding over the initial `Shard` boundary. One caveat to call out here is that the `Sharding` being passed in, **must** be the same `Sharding` that was used to generate the input raw Atlas. If it's not, there may be inconsistencies in the understanding of the surrounding shards that are available.\n\n\n2. Iterate over all the input `Line`s and identify the ones that become `Edges`, based on the tags for the `Line`. \n    1. If a `Line` is set to become an `Edge`, we identify all location-based `Nodes` for that `Edge`. A `Node` will be created in one of the 4 cases:\n        * A self-intersection - there is a repeated non-consecutive intersection in the `Edge`\n        * Configuration-based sectioning dependent on tagging (ex: section at a barrier)\n        * Intersection with another `Edge` with the same `LayerTag` value\n        * Intersection with another `Edge` with a different `LayerTag` value, that either starts or ends at the point of intersection\n    2. If the `Line` is closed, apply some consistency logic to ensure we get a good sectioning result\n        * If there are only 2 `Node` candidates, then check to see if it's a basic loop with a single `Node` at the beginning/end of the `Edge`\n            * If so, make it into one `Edge`\n            * Otherwise, it's disconnected from the rest of the `Edge` network, so split it artificially at the halfway point and make two `Edge`s for the loop\n        * If it has more than 2 `Nodes`, put the last `Edge` section into a \"remainder\". If the beginning/end of the `Line` isn't connected to anything else, then we'll add the remainder to it so we don't create excess `Edge`s. Otherwise, we'll make an `Edge` from the remainder\n    3. Using the calculated `Node` candidates and remainder geometry, make `Node` and `Edges` for the `Line`\n    4. Remove the old `Line` from the `Atlas`\n3. Iterate over the staged CompleteNode entities and add them as FeatureChanges-- waiting until after all the `Edges` have been processed ensures this process just simplifies things from a consistency standpoint\n4. Iterate over all remaining `Points` in the `Atlas` and remove them if they don't have explicit tags or belong to any `Relations` \n5. If there were changes, make a new `ChangeAtlas` and return it, otherwise return the original `Atlas`\n\n## Synthetic Tags\n\nThe sectioning process add a new synthetic tag as part of the final Atlas - `SyntheticInvalidWaySectionTag`. This tag signifies that we couldn't fully section the given Atlas Line. Specifically, we created 998 Edges and put the rest of the un-sectioned remnant into `Edge` 999. This is usually an indicator of bad data. We have seen this in the case of really long duplicated OSM Ways. If the two Ways are stacked and have over 1000 shape points, then each shape point would become an Atlas `Node` and result in sectioning. This is technically also possible for really long valid OSM Ways that have more than 999 intersections for the duration of the Way.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/sectioning/TagMap.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.sectioning;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.ManMadeTag;\nimport org.openstreetmap.atlas.tags.RailwayTag;\nimport org.openstreetmap.atlas.tags.RouteTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Tag;\n\n/**\n * A wrapper for tags for an OSM entity.\n *\n * @author tony\n * @author matthieun\n */\npublic class TagMap implements Taggable\n{\n    private final Map<String, String> tags;\n\n    public TagMap(final Collection<Tag> tagCollection)\n    {\n        this.tags = new HashMap<>();\n        tagCollection.forEach(tag -> this.tags.put(tag.getKey(), tag.getValue()));\n    }\n\n    public TagMap(final Map<String, String> tags)\n    {\n        this.tags = tags;\n    }\n\n    public PbfOneWay getOneWay()\n    {\n        return PbfOneWay.forTag(this);\n    }\n\n    @Override\n    public Optional<String> getTag(final String key)\n    {\n        return Optional.ofNullable(this.tags.get(key));\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    public boolean hasHighwayTag()\n    {\n        return HighwayTag.highwayTag(this).isPresent();\n    }\n\n    public boolean isEmpty()\n    {\n        return this.tags.size() == 0;\n    }\n\n    public boolean matchFerry()\n    {\n        return RouteTag.isFerry(this);\n    }\n\n    public boolean matchPier()\n    {\n        return ManMadeTag.isPier(this);\n    }\n\n    public boolean matchRailway()\n    {\n        return RailwayTag.isRailway(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/slicing/CountryCodeProperties.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.slicing;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\n\n/**\n * Simple container that tracks country code and nearest neighbor values returned from a JTS\n * {@link Geometry}.\n *\n * @author mgostintsev\n */\npublic class CountryCodeProperties\n{\n    private final String iso3CountryCode;\n\n    public CountryCodeProperties(final String iso3CountryCode)\n    {\n        this.iso3CountryCode = iso3CountryCode;\n    }\n\n    /**\n     * @return a string to represent country code in iso_3 format. If multiple countries, they'll be\n     *         separated by comma. e.g. USA,CAN\n     */\n    public String getIso3CountryCode()\n    {\n        return this.iso3CountryCode;\n    }\n\n    /**\n     * @return {@code true} if the country code field contains more than 1 country\n     */\n    public boolean inMultipleCountries()\n    {\n        return this.iso3CountryCode.contains(ISOCountryTag.COUNTRY_DELIMITER);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/slicing/README.md",
    "content": "# `Raw Atlas Slicing`\n## Overview\nSlicing is the operation of taking an `Atlas` and tagging all of its entities with country codes, as well as ensuring that *only* entities from the loaded country code persist in the output `Atlas`. While this operation may sound straightforward, data complexities frequently result in corner cases and complicated calculations in order to make sure the entities in the output `Atlas` are sensible. The term \"slicing\" describes these more difficult cases because the geometry of an entity frequently spans multiple countries, and thus must be \"sliced\" into portions that either a) are contained entirely in only one country boundary polygon or b) exactly along the shared overlap of multiple country boundary exterior rings and thus have geometry shared by a set of countries.  \n\n### Initialization\nThe slicer is constructed with a minimum input of `Atlas` and `AtlasLoadingOption`\n* The `Atlas` should be raw, i.e. not previously sliced and consisting of only `Line`, `Point`, and `Relation` entities\n* The `AtlasLoadingOption` will use the following paramters: \n    * `CountryBoundaryMap` to determine country boundary definitions\n    * `countryCodes` to determine which entities to keep in the final Atlas-- any entity whose country code is *not* in the `countryCodes` set will be removed from the final tlas!\n    * `relationSlicingFilter` to determine which `Relations` to expand the `DynamicAtlas` on and attempt to slice-- at a minimum, these relations must be `type->multipolygon` or `type->boundary`, but additional tagging critieria are acceptable here\n    * `edgeFilter` to determine which `Line` entities should be considered future `Edge` entities\n\t    * This is important because closed `Line` entities will be sliced as two-dimenstional polygons but closed `Edge` entities (such as a traffic circle) will be sliced as linear features\n\nIf the constructor that takes in a `Sharding` object and `Atlas` fetcher function is used, then additionally the initial `Atlas` will be converted to a `DynamicAtlas` expanded on the `relationSlicingFilter` in the `AtlasLoadingOption`.\nOnce the constructor is called, three maps are initialized for `CompleteEntity` representations of all three `AtlasEntity` types. These objects will be used to track changes made during the slicing operation.\n\n### Slicing Steps\nThe high level operations of slicing are executed in the `slice()` method, and can be summarized as follows:\n1. Slice all `Line` entities in the `Atlas` following a basic logic fork:\n\t* If the `Line` entity is closed (i.e. a loop) and neither a future `Edge` or a multipolygon `Relation` member, then slice it as an `Area` (2d geometry)\n\t* Otherwise, slice it as a linear entity\n2. Slice all multipolygon type `Relation` entities\n3. Slice all `Point` entities\n4. Filter any remaining `Relation` entities-- because we don't expand on all `Relations`, it's impossible to deterministically \"slice\" these `Relations`, so instead we just filter out any members that are outside the country code set being sliced and update the country tag for the `Relation` to be the sum of its remaining members.\n5. Add all `CompleteEntities` in the staged entity maps as `FeatureChange.ADD`-- these are either existing features being updated or new entities being added, so `FeatureChange.ADD` is always appropriate\n6. Build a new `ChangeAtlas` out of these changes, then cut out any entities that lay outside the shard bounds (frequently happens for data loaded in during the `DynamicAtlas`expansion for `Relations`)\n\n### Geometry Slicing\nSlicing of all entities follows the same general approach. \n1. The data for the entity is constructed into a relevant JTS geometry. \n    * For example, for a closed `Line` that meets the criteria for an `Area`, a JTS `Polygon` is created\n2. This internal envelope for this geometry is checked against the spatial index of the boundary map to calculate which country boundary polygons it intersects\n    * Should this geometry exclusively belong to one country, its geometry is left unaltered and the country code tag is updated to contain this country\n3. Next, geometry is checked for validity-- it must be [valid geometry](http://www.ogc.org/docs/is/) and not empty\n    * This is critical because if we filter out occasional invalid geomtery coming out of the slicing operation; inputting invalid geometry is a guarantee of junk output\n    * If it's invalid, then we remove it from the Atlas\n4. The JTS geometry is then divided into the portions intersected by each country boundary polygon, creating a map of country code to geometric pieces.\n    * ALL of these pieces must meet the requirements for validity and significance, because incredibly small lines or polygons are likely junk data or irrelevant\n    * Should the operation return a GeometryCollection, all geometries in the collection are separated and added to the results Set independently after being checked for validity and significance\n5. The results are post-processed based on the type of AtlasEntity\n    * These operations are largely similar but there are a few different expectations based on AtlasEntity type-- for example, a sliced linear `Line` will attempt to join all resulting `LineString` pieces for each country using `LineMerger` because occasionally a few of these `LineStrings` can be merged\n    * Additionally, the map returned here will use `SortedMap` to guarantee deterministic consistency in `Line` slice creation regardless of what country code settings are used, etc.\n6. On the small chance that the slicing operation returned an empty set or all significant geometry was in exclusively one country, the geometry will be unaltered and the country code tag will be updated to have either the country-missing value or the single country only, respectively\n7. Additionally, should the number of slices be greater than the country identifier space (000-999), then the operation will fail out and the entity will be tagged with all country codes its geometry spanned\n8. Finally, the slice geomtries will be converted sequentially based on their SortedMap ordering into new `AtlasEntities` with the same tags as their parent entity, but with the geomtery of the slice and the country code tag of the relevant country, and these are be added to the relevant staged `CompleteEntity` map\n    * At the end of this process, the original parent entity will be removed from the relevant staged `CompleteEntity` map and a `FeatureChange.remove` is added to the `changes` `ChangeSet` to ensure it is removed from the final `Atlas`\n\n### MultiPolygon Relation Slicing\nThis operation has some added complexity that is worth explaining in-depth. While the overall approach follows the approach described above, the changes are as follows:\n\n1. The `Relation` is filtered of [invalid members](https://wiki.openstreetmap.org/wiki/Relation:multipolygon#Members)\n2. The geometry is built using the *raw member line geometry*, not the sliced `Lines` in the staged `CompleteEntity` map\n    * This choice ensures that the geometry will build if the raw `Relation` data builds a valid multipolygon, and avoids any possible small stitching errors resultant from data gaps introduced during `Line` slicing\n3. After the multipolygon is sliced against the intersecting country boundary polygons and new sliced `Relations` are created, an additional step occurs: `createSyntheticRelationMembers()`\n    * This method takes the sliced multipolygon for a country, subtracts out the existing sliced `Line` members for that country from that geometry, then generates new `Line` entities to cover the remaining geometry\n    * This operation preserves the ability to build a valid multipolygon out of the sliced `Relation`-- without it, the sliced `Line` members would have major gaps and fail to build into geometry\n\t* Additionally, in rare circumstances a member that was previously tagged as an `inner` role will now overlap with the exterior ring of the sliced geometry\n\t    * In this case, that member will still be preserved but its role will be switched to an `outer`\n\n## Synthetic Tags\nThere are synthetic tags generated by the country-slicing process:\n1. `SyntheticRelationMemberAdded` - indicates a Relation that had an added member as a result of country-slicing. An example includes closing a water body MultiPolygon relation with a new `Line` member that runs along a country boundary\n2. `SyntheticRelationRoleUpdated` - indicates a Relation member role update, any time that some combination of inner/outer relation members was merged\n3. `SyntheticBoundaryNodeTag` -- indicates a Point/Node along a Line/Edge that has been created due to the slicing of Line/Edge geometry at a country boundary\n4. `SytheticInvalidGeometryTag` -- indicates a geometry went through the slicing process, but could not be sliced due to not meeting OGC compliance for the geometry type\n5. `SyntheticInvalidMultiPolygonRelationMembersRemovedTag` -- indicates any multipolygon Relation members that were removed during slicing due to not meeting the OSM requirements for members (i.e. not a Line with a role of \"inner\" or \"outer\")\n6. `SyntheticSyntheticRelationMemberTag` -- indicates an entity is a synthetic addition to a multipolygon Relation added during slicing\n7. `SyntheticGeometrySlicedTag` -- indicates an entity had its geometry changed during slicing\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/raw/slicing/RawAtlasSlicer.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.slicing;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedMap;\nimport java.util.SortedSet;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryCollection;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.prep.PreparedGeometry;\nimport org.locationtech.jts.geom.prep.PreparedGeometryFactory;\nimport org.locationtech.jts.geom.prep.PreparedPolygon;\nimport org.locationtech.jts.operation.linemerge.LineMerger;\nimport org.locationtech.jts.operation.overlayng.OverlayNG;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeAtlas;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeBuilder;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.MultiPolygonRelationToMemberConverter.Ring;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.AbstractIdentifierFactory;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.CountrySlicingIdentifierFactory;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPrecisionManager;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.SyntheticBoundaryNodeTag;\nimport org.openstreetmap.atlas.tags.SyntheticGeometrySlicedTag;\nimport org.openstreetmap.atlas.tags.SyntheticInvalidGeometryTag;\nimport org.openstreetmap.atlas.tags.SyntheticInvalidMultiPolygonRelationMembersRemovedTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.identifiers.EntityIdentifierGenerator;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Takes a raw Atlas (i.e. only Points, Lines, and Relations, all lacking country code tags) and\n * \"slices\" it\n *\n * @author samg\n */\npublic class RawAtlasSlicer\n{\n    // Buffer values for slicing operation. If the remaining piece turns to be smaller than\n    // buffer, we'll just ignore them.\n    public static final double LINE_BUFFER = 0.000001;\n    public static final double AREA_BUFFER = 0.000000005;\n    public static final double BUFFER_PERCENTAGE = 0.005;\n    public static final double PERCENTAGE = 100;\n\n    // JTS converters\n    private static final JtsPolygonConverter JTS_POLYGON_CONVERTER = new JtsPolygonConverter();\n    private static final JtsPolyLineConverter JTS_POLYLINE_CONVERTER = new JtsPolyLineConverter();\n\n    private static final Logger logger = LoggerFactory.getLogger(RawAtlasSlicer.class);\n    private static final double MINIMUM_CONSOLIDATE_THRESHOLD = 0.8;\n    private static final long SLICING_DURATION_WARN = 10;\n\n    // Logging constants\n    private static final String MULTIPOLYGON_RELATION_INVALID_GEOMETRY = \"Relation {} for Atlas {} had invalid multipolygon geometry {}!\";\n    private static final String MULTIPOLYGON_RELATION_HAD_NO_SLICED_GEOMETRY = \"Relation {} for Atlas {} had no valid sliced geometry!\";\n    private static final String MULTIPOLYGON_RELATION_HAD_EQUIVALENT_SLICED_GEOMETRY = \"Relation {} for Atlas {} had sliced geometry equal to original geometry, not slicing!\";\n    private static final String MULTIPOLYGON_RELATION_SLICING_DURATION_EXCEEDED = \"Relation {} for Atlas {} took {} to slice!\";\n    private static final String MULTIPOLYGON_RELATION_INVALID_MEMBER_REMOVED = \"Purging invalid member {} from relation {}\";\n    private static final String MULTIPOLYGON_RELATION_INVALID_SLICED_GEOMETRY = \"Relation {} sliced for country {} produced invalid geometry {}!\";\n    private static final String MULTIPOLYGON_RELATION_OVERLAPPING_INNERS = \"Relation {} for Atlas {} had overlapping inners, but slicing will continue!\";\n\n    private static final String LINE_HAD_MULTIPOLYGON_SLICE = \"Line {} for Atlas {} had multipolygon slicing result when sliced as polygon, will slice as line instead!\";\n    private static final String LINE_SLICING_DURATION_EXCEEDED = \"Line {} for Atlas {} took {} to slice!\";\n    private static final String LINE_HAD_INVALID_GEOMETRY = \"Line {} for Atlas {} had invalid geometry {}, removing instead of slicing!\";\n    private static final String LINE_EXCEEDED_SLICING_IDENTIFIER_SPACE = \"Country slicing exceeded maximum line identifier name space of {} for line {} for Atlas {}. It will be added as is, with two or more country codes\";\n\n    private static final String STARTED_SLICING = \"Starting slicing for Atlas {}\";\n    private static final String FINISHED_SLICING = \"Finished slicing for Atlas {} in {}\";\n    private static final String STARTED_LINE_SLICING = \"Starting line slicing for Atlas {}\";\n    private static final String FINISHED_LINE_SLICING = \"Finished line slicing for Atlas {} in {}\";\n    private static final String STARTED_RELATION_SLICING = \"Starting relation slicing for Atlas {}\";\n    private static final String FINISHED_RELATION_SLICING = \"Finished relation slicing for Atlas {} in {}\";\n    private static final String STARTED_POINT_SLICING = \"Starting point slicing for Atlas {}\";\n    private static final String FINISHED_POINT_SLICING = \"Finished point slicing for Atlas {} in {}\";\n    private static final String STARTED_RELATION_FILTERING = \"Starting relation filtering for Atlas {}\";\n    private static final String FINISHED_RELATION_FILTERING = \"Finished relation filtering for Atlas {} in {}\";\n\n    private final Atlas inputAtlas;\n    private final Shard initialShard;\n    private final Predicate<AtlasEntity> consolidatePredicate;\n    private final Predicate<AtlasEntity> isInCountry;\n    private final Predicate<AtlasEntity> isAtlasEdge;\n    private final CountryBoundaryMap boundary;\n    private final String shardOrAtlasName;\n    private final String country;\n    /** See {@link AtlasLoadingOption#isKeepAll} */\n    private final boolean keepAll;\n    private final Map<Long, CompleteArea> stagedAreas = new ConcurrentHashMap<>();\n    private final Map<Long, CompleteRelation> stagedRelations = new ConcurrentHashMap<>();\n    private final Map<Long, CompleteLine> stagedLines = new ConcurrentHashMap<>();\n    private final Map<Long, CompletePoint> stagedPoints = new ConcurrentHashMap<>();\n    private final AtlasLoadingOption loadingOption;\n\n    private final Map<Long, Map<String, CompleteRelation>> splitRelations = new ConcurrentHashMap<>();\n    private final Set<FeatureChange> changes = Collections\n            .newSetFromMap(new ConcurrentHashMap<FeatureChange, Boolean>());\n    private final Set<Long> pointsBelongingToEdge = Collections\n            .newSetFromMap(new ConcurrentHashMap<Long, Boolean>());\n    private final PreparedGeometryFactory preparer = new PreparedGeometryFactory();\n\n    /**\n     * This constructor will build a RawAtlasSlicer for use on a single Atlas with no dynamic\n     * expansion on Relations. Primarily for tests, please consider using the alternate constructor\n     * for most use cases!\n     *\n     * @param loadingOption\n     *            An AtlasLoadingOption with a minimum of a CountryBoundaryMap included\n     * @param startingAtlas\n     *            The raw Atlas to slice\n     */\n    public RawAtlasSlicer(final AtlasLoadingOption loadingOption, final Atlas startingAtlas)\n    {\n        this(loadingOption, startingAtlas, SlippyTile.forName(\"1-1-1\"));\n    }\n\n    /**\n     * This constructor will build a RawAtlasSlicer for use on a single Atlas with no dynamic\n     * expansion on Relations. Primarily for tests, please consider using the alternate constructor\n     * for most use cases!\n     *\n     * @param loadingOption\n     *            An AtlasLoadingOption with a minimum of a CountryBoundaryMap included\n     * @param startingAtlas\n     *            The raw Atlas to slice\n     * @param initialShard\n     *            The initial shard to keep points for\n     */\n    public RawAtlasSlicer(final AtlasLoadingOption loadingOption, final Atlas startingAtlas,\n            final Shard initialShard)\n    {\n        this.inputAtlas = startingAtlas;\n        this.initialShard = initialShard;\n        this.loadingOption = loadingOption;\n        this.country = loadingOption.getCountryCode();\n        this.consolidatePredicate = entity -> entity.getType().equals(ItemType.RELATION)\n                && loadingOption.getRelationSlicingConsolidateFilter().test(entity);\n        if (loadingOption.getCountryCode() == null || loadingOption.getCountryCode().isEmpty())\n        {\n            this.isInCountry = entity -> true;\n        }\n        else\n        {\n            this.isInCountry = entity -> ISOCountryTag.isIn(loadingOption.getCountryCode())\n                    .test(entity);\n        }\n        this.isAtlasEdge = entity -> loadingOption.getEdgeFilter().test(entity);\n        this.boundary = loadingOption.getCountryBoundaryMap();\n        this.shardOrAtlasName = startingAtlas.metaData().getShardName()\n                .orElse(startingAtlas.getName());\n        this.inputAtlas.areas().forEach(\n                area -> this.stagedAreas.put(area.getIdentifier(), CompleteArea.from(area)));\n        this.inputAtlas.points().forEach(\n                point -> this.stagedPoints.put(point.getIdentifier(), CompletePoint.from(point)));\n        this.inputAtlas.lines().forEach(\n                line -> this.stagedLines.put(line.getIdentifier(), CompleteLine.from(line)));\n        this.inputAtlas.relations().forEach(relation -> this.stagedRelations\n                .put(relation.getIdentifier(), CompleteRelation.from(relation)));\n\n        this.keepAll = loadingOption.isKeepAll();\n    }\n\n    /**\n     * Calculates the changes needed to slice the Atlas, then builds a ChangeAtlas out of that and\n     * cuts it down to match the boundaries of the initial shard\n     *\n     * @return An Atlas with only entities that lay inside the original Shard bounds and matching\n     *         the country code set provided in the AtlasLoadingOption, all with ISOCountryTags\n     *         properly set\n     */\n    public Atlas slice()\n    {\n        final Time overallTime = Time.now();\n        Time time = Time.now();\n        logger.info(STARTED_SLICING, this.shardOrAtlasName);\n        logger.info(STARTED_LINE_SLICING, this.shardOrAtlasName);\n        this.inputAtlas.areas().forEach(this::sliceArea);\n\n        final Set<CompleteLine> linesToSlice = new HashSet<>();\n        linesToSlice.addAll(this.stagedLines.values());\n        linesToSlice.forEach(this::sliceLine);\n\n        logger.info(FINISHED_LINE_SLICING, this.shardOrAtlasName,\n                time.elapsedSince().asMilliseconds());\n\n        time = Time.now();\n        logger.info(STARTED_RELATION_SLICING, this.shardOrAtlasName);\n        this.inputAtlas.relationsLowerOrderFirst().forEach(relation ->\n        {\n            if (relation.isGeometric())\n            {\n                sliceRelation(this.stagedRelations.get(relation.getIdentifier()));\n            }\n        });\n        logger.info(FINISHED_RELATION_SLICING, this.shardOrAtlasName,\n                time.elapsedSince().asMilliseconds());\n\n        time = Time.now();\n        logger.info(STARTED_POINT_SLICING, this.shardOrAtlasName);\n        this.inputAtlas.points().forEach(this::slicePoint);\n        logger.info(FINISHED_POINT_SLICING, this.shardOrAtlasName,\n                time.elapsedSince().asMilliseconds());\n\n        logger.info(STARTED_RELATION_FILTERING, this.shardOrAtlasName);\n        this.inputAtlas.relationsLowerOrderFirst().forEach(relation ->\n        {\n            if (this.stagedRelations.containsKey(relation.getIdentifier())\n                    && !Validators.hasValuesFor(this.stagedRelations.get(relation.getIdentifier()),\n                            ISOCountryTag.class))\n            {\n                filterRelation(this.stagedRelations.get(relation.getIdentifier()));\n            }\n        });\n        logger.info(FINISHED_RELATION_FILTERING, this.shardOrAtlasName,\n                time.elapsedSince().asMilliseconds());\n\n        this.stagedLines.values()\n                .forEach(line -> this.changes.add(FeatureChange.add(line, this.inputAtlas)));\n        this.stagedPoints.values()\n                .forEach(point -> this.changes.add(FeatureChange.add(point, this.inputAtlas)));\n        this.stagedRelations.values().forEach(\n                relation -> this.changes.add(FeatureChange.add(relation, this.inputAtlas)));\n        this.stagedAreas.values()\n                .forEach(area -> this.changes.add(FeatureChange.add(area, this.inputAtlas)));\n\n        logger.info(FINISHED_SLICING, this.shardOrAtlasName,\n                overallTime.elapsedSince().asMilliseconds());\n\n        return new ChangeAtlas(this.inputAtlas, new ChangeBuilder().addAll(this.changes).get())\n        {\n            private static final long serialVersionUID = -1379576156041355921L;\n\n            @Override\n            public synchronized AtlasMetaData metaData()\n            {\n                // Override meta-data here so the country code is properly included.\n                final AtlasMetaData metaData = super.metaData();\n                final var originalTags = metaData.getTags();\n                // Remove the country shards to keep old behavior where they were dropped, but keep\n                // other tags\n                originalTags.remove(\"countryShards\");\n                return new AtlasMetaData(metaData.getSize(), false,\n                        metaData.getCodeVersion().orElse(null),\n                        metaData.getDataVersion().orElse(null), RawAtlasSlicer.this.country,\n                        RawAtlasSlicer.this.shardOrAtlasName, originalTags);\n            }\n        };\n    }\n\n    /**\n     * Given a new split Relation, filter its original Relation's members by the country code for\n     * the new split Relation and add only them\n     *\n     * @param newRelation\n     *            A new Relation containing a subset of members based on country code\n     * @param oldRelation\n     *            The original Relation for the new split Relation\n     * @param countryMultiPolygon\n     */\n    private void addCountryMembersToSplitRelation(final CompleteRelation newRelation,\n            final CompleteRelation oldRelation, final PreparedPolygon countryMultiPolygon)\n    {\n        oldRelation.membersMatching(member -> member.getEntity().getType().equals(ItemType.LINE)\n                && Validators.isOfSameType(this.stagedLines.get(member.getEntity().getIdentifier()),\n                        newRelation, ISOCountryTag.class))\n                .forEach(member ->\n                {\n                    final CompleteLine lineForMember = this.stagedLines\n                            .get(member.getEntity().getIdentifier());\n                    // quirk of line slicing-- check to see that either we never altered the\n                    // geometry, in which case it definitely belongs in the relation, or that it was\n                    // sliced but intersects the output multipolygon\n                    if (lineForMember.getTag(SyntheticGeometrySlicedTag.KEY).isEmpty()\n                            || countryMultiPolygon.covers(\n                                    JTS_POLYLINE_CONVERTER.convert(lineForMember.asPolyLine())))\n                    {\n                        newRelation.withAddedMember(lineForMember, member.getRole());\n                        lineForMember.withAddedRelationIdentifier(newRelation.getIdentifier());\n                    }\n                    lineForMember.withRemovedRelationIdentifier(oldRelation.getIdentifier());\n                });\n        oldRelation\n                .membersMatching(member -> member.getEntity().getType().equals(ItemType.AREA)\n                        && Validators.isOfSameType(\n                                this.stagedAreas.get(member.getEntity().getIdentifier()),\n                                newRelation, ISOCountryTag.class)\n                        && (member.getRole().equalsIgnoreCase(Ring.OUTER.toString())\n                                || this.stagedAreas.get(member.getEntity().getIdentifier())\n                                        .getTag(SyntheticGeometrySlicedTag.KEY).isEmpty()))\n                .forEach(member ->\n                {\n                    final CompleteArea areaForMember = this.stagedAreas\n                            .get(member.getEntity().getIdentifier());\n                    newRelation.withAddedMember(areaForMember, member.getRole());\n                    areaForMember.withAddedRelationIdentifier(newRelation.getIdentifier());\n                    areaForMember.withRemovedRelationIdentifier(oldRelation.getIdentifier());\n                });\n\n    }\n\n    /**\n     * Given a Line and its slice segment, ensure that any new coordinates where the Line has been\n     * split either have an existing Point that has an updated SyntheticBoundaryNodeTag.EXISTING\n     * value, or a new Point is created with the SyntheticBoundaryNodeTag.NEW value\n     *\n     * @param line\n     *            A Line being sliced that meets the criteria for an Edge (see isAtlasEdge method)\n     * @param slice\n     *            A slice for that Line\n     */\n    private void addSyntheticBoundaryNodesForSlice(final Line line, final PolyLine slice)\n    {\n        if (!slice.first().equals(line.asPolyLine().first()))\n        {\n            final Iterable<Point> pointsAtFirstLocation = this.inputAtlas.pointsAt(slice.first());\n            if (Iterables.isEmpty(pointsAtFirstLocation))\n            {\n                final EntityIdentifierGenerator pointIdentifierGenerator = new EntityIdentifierGenerator();\n                final SortedSet<String> countries = new TreeSet<>();\n                final Map<String, String> tags = new HashMap<>();\n                countries.addAll(Arrays.asList(this.boundary.getCountryCodeISO3(slice.first())\n                        .getIso3CountryCode().split(ISOCountryTag.COUNTRY_DELIMITER)));\n                tags.put(ISOCountryTag.KEY,\n                        String.join(ISOCountryTag.COUNTRY_DELIMITER, countries));\n                tags.put(SyntheticBoundaryNodeTag.KEY, SyntheticBoundaryNodeTag.YES.toString());\n                final CompletePoint syntheticBoundaryNode = new CompletePoint(1L, slice.first(),\n                        tags, new HashSet<>());\n                syntheticBoundaryNode.withIdentifier(\n                        pointIdentifierGenerator.generateIdentifier(syntheticBoundaryNode));\n                this.stagedPoints.put(syntheticBoundaryNode.getIdentifier(), syntheticBoundaryNode);\n            }\n            else\n            {\n                this.stagedPoints.get(pointsAtFirstLocation.iterator().next().getIdentifier())\n                        .withAddedTag(SyntheticBoundaryNodeTag.KEY,\n                                SyntheticBoundaryNodeTag.EXISTING.toString());\n            }\n        }\n\n        if (!slice.last().equals(line.asPolyLine().last()))\n        {\n            final Iterable<Point> pointsAtLastLocation = this.inputAtlas.pointsAt(slice.last());\n            if (Iterables.isEmpty(pointsAtLastLocation))\n            {\n                final EntityIdentifierGenerator pointIdentifierGenerator = new EntityIdentifierGenerator();\n                final SortedSet<String> countries = new TreeSet<>();\n                final Map<String, String> tags = new HashMap<>();\n                countries.addAll(Arrays.asList(this.boundary.getCountryCodeISO3(slice.last())\n                        .getIso3CountryCode().split(ISOCountryTag.COUNTRY_DELIMITER)));\n                tags.put(ISOCountryTag.KEY,\n                        String.join(ISOCountryTag.COUNTRY_DELIMITER, countries));\n                tags.put(SyntheticBoundaryNodeTag.KEY, SyntheticBoundaryNodeTag.YES.toString());\n                final CompletePoint syntheticBoundaryNode = new CompletePoint(1L, slice.last(),\n                        tags, new HashSet<>());\n                syntheticBoundaryNode.withIdentifier(\n                        pointIdentifierGenerator.generateIdentifier(syntheticBoundaryNode));\n                this.stagedPoints.put(syntheticBoundaryNode.getIdentifier(), syntheticBoundaryNode);\n            }\n            else\n            {\n                this.stagedPoints.get(pointsAtLastLocation.iterator().next().getIdentifier())\n                        .withAddedTag(SyntheticBoundaryNodeTag.KEY,\n                                SyntheticBoundaryNodeTag.EXISTING.toString());\n            }\n        }\n    }\n\n    /**\n     * Checks the slices of a Line to see if we can process them succesfully. If there's just one\n     * country, go ahead and tag the line, and if it's too many slices tag the line as well.\n     *\n     * @param slicesKeySet\n     *            The set of country codes for all of the slices\n     * @param totalSlicesCount\n     *            The total number of slices for the line\n     * @return false if the line shouldn't be sliced with these slices, true otherwise\n     */\n    private boolean checkSlices(final Set<String> slicesCountryCodes, final long totalSlicesCount,\n            final AtlasEntity entity)\n    {\n        if (slicesCountryCodes.size() == 1)\n        {\n            final String countryCode = slicesCountryCodes.iterator().next();\n            if (entity instanceof Line)\n            {\n                this.stagedLines.get(entity.getIdentifier()).withAddedTag(ISOCountryTag.KEY,\n                        countryCode);\n                if (!this.isInCountry.test(this.stagedLines.get(entity.getIdentifier())))\n                {\n                    removeLine((Line) entity);\n                }\n            }\n            else if (entity instanceof Area)\n            {\n                this.stagedAreas.get(entity.getIdentifier()).withAddedTag(ISOCountryTag.KEY,\n                        countryCode);\n                if (!this.isInCountry.test(this.stagedAreas.get(entity.getIdentifier())))\n                {\n                    removeArea((Area) entity);\n                }\n            }\n            return false;\n        }\n        else if (totalSlicesCount >= AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT)\n        {\n            // this should be rare, but we can't slice the line if we don't have the identifier\n            // space for the slices\n            logger.error(LINE_EXCEEDED_SLICING_IDENTIFIER_SPACE,\n                    AbstractIdentifierFactory.IDENTIFIER_SCALE_DEFAULT, entity.getOsmIdentifier(),\n                    this.shardOrAtlasName);\n            final String countryString = String.join(\",\", slicesCountryCodes);\n            if (entity instanceof Line)\n            {\n                this.stagedLines.get(entity.getIdentifier()).withTags(entity.getTags())\n                        .withAddedTag(ISOCountryTag.KEY, countryString);\n            }\n            else if (entity instanceof Area)\n            {\n                this.stagedAreas.get(entity.getIdentifier()).withTags(entity.getTags())\n                        .withAddedTag(ISOCountryTag.KEY, countryString);\n            }\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * Given an Area and its slices, create the new Area entities and put them in the stagedAreas\n     * map\n     *\n     * @param area\n     *            The Area being sliced\n     * @param slices\n     *            The map representing the portions of its geometry in each country, mapped to\n     *            country code\n     */\n    private void createNewSlicedAreas(final Area area,\n            final SortedMap<String, Set<org.locationtech.jts.geom.Polygon>> slices)\n    {\n        final CountrySlicingIdentifierFactory areaIdentifierFactory = new CountrySlicingIdentifierFactory(\n                area.getIdentifier());\n        slices.keySet().forEach(countryCode ->\n        {\n            for (final org.locationtech.jts.geom.Polygon slice : slices.get(countryCode))\n            {\n                final CompleteArea newAreaSlice = CompleteArea.from(area)\n                        .withIdentifier(areaIdentifierFactory.nextIdentifier())\n                        .withAddedTag(SyntheticGeometrySlicedTag.KEY,\n                                SyntheticGeometrySlicedTag.YES.toString())\n                        .withAddedTag(ISOCountryTag.KEY, countryCode);\n                if (this.isInCountry.test(newAreaSlice))\n                {\n                    final Polygon newAreaGeometry = new Polygon(processSlice(slice, area));\n                    newAreaSlice.withGeometry(newAreaGeometry);\n                    area.relations().forEach(relation -> newAreaSlice\n                            .withAddedRelationIdentifier(relation.getIdentifier()));\n                    this.stagedAreas.put(newAreaSlice.getIdentifier(), newAreaSlice);\n                    for (final Relation relation : area.relations())\n                    {\n                        if (this.stagedRelations.containsKey(relation.getIdentifier()))\n                        {\n                            this.stagedRelations.get(relation.getIdentifier())\n                                    .withAddedMember(newAreaSlice, area);\n                        }\n                    }\n                }\n            }\n        });\n        removeArea(area);\n    }\n\n    /**\n     * Given a non-Area Line and its slices, create the new Line entities and put them in the\n     * stagedLines map\n     *\n     * @param line\n     *            The Line being sliced\n     * @param slices\n     *            The map representing the portions of its geometry in each country, mapped to\n     *            country code\n     */\n    private void createNewSlicedLines(final Line line, // NOSONAR\n            final SortedMap<String, Set<LineString>> slices)\n    {\n        final CountrySlicingIdentifierFactory lineIdentifierFactory = new CountrySlicingIdentifierFactory(\n                line.getIdentifier());\n        slices.keySet().forEach(countryCode ->\n        {\n            for (final LineString slice : slices.get(countryCode))\n            {\n                final CompleteLine newLineSlice = CompleteLine.from(line)\n                        .withIdentifier(lineIdentifierFactory.nextIdentifier())\n                        .withAddedTag(SyntheticGeometrySlicedTag.KEY,\n                                SyntheticGeometrySlicedTag.YES.toString())\n                        .withAddedTag(ISOCountryTag.KEY, countryCode);\n                if (this.isInCountry.test(newLineSlice))\n                {\n                    newLineSlice.withGeometry(processSlice(slice, line));\n                    if (this.isAtlasEdge.test(line))\n                    {\n                        addSyntheticBoundaryNodesForSlice(line, newLineSlice.asPolyLine());\n                    }\n                    this.stagedLines.put(newLineSlice.getIdentifier(), newLineSlice);\n                    for (final Relation relation : line.relations())\n                    {\n                        if (this.stagedRelations.containsKey(relation.getIdentifier()))\n                        {\n                            final String role = this.stagedRelations.get(relation.getIdentifier())\n                                    .membersMatching(member -> member.getEntity().getType()\n                                            .equals(ItemType.LINE)\n                                            && member.getEntity().getIdentifier() == line\n                                                    .getIdentifier())\n                                    .iterator().next().getRole();\n                            this.stagedRelations.get(relation.getIdentifier())\n                                    .withAddedMember(newLineSlice, role);\n                        }\n                    }\n                }\n            }\n        });\n        removeLine(line);\n    }\n\n    /**\n     * For any Relation that we can't slice (non-multipolygon, didn't meet the Relation predicate\n     * criteria, or bad geometry), instead filter out any members that aren't in the country code\n     * set, and update the ISOCountryTag for the Relation\n     *\n     * @param relation\n     */\n    private void filterRelation(final CompleteRelation relation)\n    {\n        final Set<String> countryList = new HashSet<>();\n        for (final RelationMember member : relation.members())\n        {\n            final AtlasEntity stagedRelationMember = getStagedEntityForMember(member);\n            if (stagedRelationMember == null)\n            {\n                relation.withRemovedMember(member.getEntity());\n                if (member.getEntity().getType().equals(ItemType.RELATION)\n                        && this.splitRelations.containsKey(member.getEntity().getIdentifier()))\n                {\n                    this.splitRelations.get(member.getEntity().getIdentifier()).keySet()\n                            .forEach(countryCode ->\n                            {\n                                final CompleteRelation splitRelation = this.splitRelations\n                                        .get(member.getEntity().getIdentifier()).get(countryCode);\n                                if (this.isInCountry.test(splitRelation))\n                                {\n                                    relation.withAddedMember(splitRelation, member.getRole());\n                                    countryList.add(countryCode);\n                                }\n                            });\n                }\n                continue;\n            }\n            final Optional<String> countryCodeTag = stagedRelationMember.getTag(ISOCountryTag.KEY);\n            if (countryCodeTag.isEmpty())\n            {\n                throw new CoreException(\n                        \"Untagged country value for entity {} for relation {} for Atlas {}\",\n                        stagedRelationMember, relation.getIdentifier(), this.shardOrAtlasName);\n            }\n            Collections.addAll(countryList, countryCodeTag.get().split(\",\"));\n        }\n        if (countryList.isEmpty())\n        {\n            this.stagedRelations.remove(relation.getIdentifier());\n            this.changes.add(FeatureChange.remove(relation));\n            return;\n        }\n\n        // compute the value of the final country tag\n        relation.withAddedTag(ISOCountryTag.KEY, ISOCountryTag.join(countryList));\n        if (relation.isGeometric() && relation.asMultiPolygon().isPresent())\n        {\n            relation.withMultiPolygonGeometry(null);\n        }\n    }\n\n    /**\n     * Given a JTS geometry, return all Polygons from the country boundary map that intersect its\n     * internal envelope\n     *\n     * @param targetGeometry\n     *            The geometry being queried\n     * @return All Polygons from the country boundary map that intersect its internal envelope\n     */\n    private Set<PreparedPolygon> getIntersectingBoundaryPolygons(final Geometry targetGeometry)\n    {\n        return this.boundary.query(targetGeometry.getEnvelopeInternal()).stream().distinct()\n                .filter(preparedPolygon -> preparedPolygon.intersects(targetGeometry))\n                .collect(Collectors.toSet());\n    }\n\n    /**\n     * Given a RelationMember, find its staged CompleteEntity\n     *\n     * @param member\n     *            A RelationMember to find\n     * @return Its staged CompleteEntity\n     */\n    private AtlasEntity getStagedEntityForMember(final RelationMember member)\n    {\n        final long identifier = member.getEntity().getIdentifier();\n        if (member.getEntity() instanceof Point)\n        {\n            if (this.stagedPoints.containsKey(identifier))\n            {\n                return this.stagedPoints.get(identifier);\n            }\n            return null;\n        }\n        else if (member.getEntity() instanceof Line)\n        {\n            if (this.stagedLines.containsKey(identifier))\n            {\n                return this.stagedLines.get(identifier);\n            }\n            return null;\n        }\n        else if (member.getEntity() instanceof Area)\n        {\n            if (this.stagedAreas.containsKey(identifier))\n            {\n                return this.stagedAreas.get(identifier);\n            }\n            return null;\n        }\n        else\n        {\n            if (this.stagedRelations.containsKey(identifier))\n            {\n                return this.stagedRelations.get(identifier);\n            }\n            return null;\n        }\n    }\n\n    /**\n     * Checks two sets of geometries to see if one contains any geometries that are covered by or\n     * equals to any geometries in the second set\n     *\n     * @param geometries\n     *            A Set of Geometries to check\n     * @param geometriesComparison\n     *            A second Set of Geometries to compare to\n     * @return True if any geometry in geometries is equal to or is covered by a geometry in\n     *         geometryComparison, false otherwise\n     */\n    private boolean isCoveredBy(final Set<Geometry> geometries,\n            final Set<PreparedGeometry> geometriesComparison)\n    {\n        for (final PreparedGeometry comparisonGeometry : geometriesComparison)\n        {\n            for (final Geometry geometry : geometries)\n            {\n                if (geometry.equals(comparisonGeometry.getGeometry())\n                        || comparisonGeometry.coveredBy(geometry)\n                        || comparisonGeometry.intersects(geometry) && geometry\n                                .intersection(comparisonGeometry.getGeometry()).getDimension() > 0)\n                {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * A filter to ensure trivial pieces of geometry aren't preserved from the slicing operation\n     *\n     * @param geometry\n     *            The geometry to check\n     * @return True if the geometry is valid and larger than either the\n     *         CountryBoundaryMap.LINE_BUFFER or CountryBoundaryMap.AREA_BUFFER\n     */\n    private boolean isSignificantGeometry(final Geometry original, final Geometry clipped)\n    {\n        if (!clipped.isValid() && logger.isWarnEnabled())\n        {\n            logger.warn(\"Found invalid geometry {} during slicing Atlas {}\", clipped.toText(),\n                    this.shardOrAtlasName);\n            return false;\n        }\n        if (clipped.getDimension() == 1)\n        {\n            return clipped.getLength() > LINE_BUFFER\n                    || clipped.getLength() / original.getLength() > BUFFER_PERCENTAGE;\n        }\n        else if (clipped.getDimension() == 2)\n        {\n            return clipped.getArea() > AREA_BUFFER\n                    || clipped.getArea() / original.getArea() > BUFFER_PERCENTAGE;\n        }\n        return false;\n    }\n\n    /**\n     * Given a slice for a Line entity, construct an appropriate PolyLine for the new sliced Line\n     * member. In the case of a Polygon (i.e. Area), the PolyLine will be constructed such that the\n     * winding represents the winding of the original entity\n     *\n     * @param slice\n     *            The slice for a Line entity\n     * @param entity\n     *            The Line being sliced\n     * @return The best PolyLine to represent that slice for the Line\n     */\n    private PolyLine processSlice(final Geometry slice, final AtlasEntity entity)\n    {\n        PolyLine polylineForSlice;\n        if (slice instanceof LineString)\n        {\n            polylineForSlice = JTS_POLYLINE_CONVERTER.backwardConvert((LineString) slice);\n        }\n        else if (slice instanceof org.locationtech.jts.geom.Polygon)\n        {\n            polylineForSlice = JTS_POLYLINE_CONVERTER\n                    .backwardConvert(((org.locationtech.jts.geom.Polygon) slice).getExteriorRing());\n        }\n        else\n        {\n            throw new CoreException(\"Unexpected geometry when slicing line {} for Atlas {}\",\n                    entity.getOsmIdentifier(), this.shardOrAtlasName);\n        }\n\n        // JTS frequently reverses the winding during slicing-- this checks the winding of the\n        // original geometry, and reverse the winding of the slice if needed\n        if (entity instanceof Area)\n        {\n            final boolean originalClockwise = ((Area) entity).asPolygon().isClockwise();\n            final boolean sliceClockwise = new Polygon(polylineForSlice.truncate(0, 1))\n                    .isClockwise();\n            if (originalClockwise != sliceClockwise)\n            {\n                polylineForSlice = polylineForSlice.reversed();\n            }\n        }\n        return polylineForSlice;\n    }\n\n    /**\n     * Given a geometric Relation, purge any members that don't meet the criteria in the OSM\n     * specification\n     *\n     * @param relation\n     *            The Relation to check\n     */\n    private void purgeInvalidGeometricRelationMembers(final CompleteRelation relation)\n    {\n        final Set<String> memberIdentifiersRemoved = new HashSet<>();\n        relation.membersMatching(member -> member.getEntity().getType() != ItemType.LINE\n                || !(member.getRole().equals(RelationTypeTag.MULTIPOLYGON_ROLE_OUTER)\n                        || member.getRole().equals(RelationTypeTag.MULTIPOLYGON_ROLE_INNER)))\n                .forEach(invalidMember ->\n                {\n                    final long identifier = invalidMember.getEntity().getIdentifier();\n                    logger.warn(MULTIPOLYGON_RELATION_INVALID_MEMBER_REMOVED, invalidMember,\n                            relation.getOsmIdentifier());\n                    if (invalidMember.getEntity().getType().equals(ItemType.LINE))\n                    {\n                        // if it was in the atlas, remove the OG version, else it must have been\n                        // sliced so remove that\n                        if (this.inputAtlas.line(invalidMember.getEntity().getIdentifier()) != null)\n                        {\n                            relation.withRemovedMember(this.inputAtlas\n                                    .line(invalidMember.getEntity().getIdentifier()));\n                        }\n                        else\n                        {\n                            relation.withRemovedMember(this.stagedLines\n                                    .get(invalidMember.getEntity().getIdentifier()));\n                        }\n\n                        this.stagedLines.get(identifier)\n                                .withRemovedRelationIdentifier(relation.getIdentifier());\n                        memberIdentifiersRemoved.add(Long.toString(identifier));\n                    }\n                    else if (invalidMember.getEntity().getType().equals(ItemType.AREA)\n                            && this.stagedAreas.containsKey(identifier)\n                            && this.stagedAreas.get(identifier)\n                                    .getTag(SyntheticGeometrySlicedTag.KEY).isPresent()\n                            && !(invalidMember.getRole()\n                                    .equals(RelationTypeTag.MULTIPOLYGON_ROLE_OUTER)\n                                    || invalidMember.getRole()\n                                            .equals(RelationTypeTag.MULTIPOLYGON_ROLE_INNER)))\n                    {\n                        relation.withRemovedMember(invalidMember.getEntity());\n                        this.stagedAreas.get(identifier)\n                                .withRemovedRelationIdentifier(relation.getIdentifier());\n                    }\n                    else if (invalidMember.getEntity().getType().equals(ItemType.POINT))\n                    {\n                        if (Validators.isOfType(relation, RelationTypeTag.class,\n                                RelationTypeTag.BOUNDARY)\n                                && (invalidMember.getRole().equals(\"admin_centre\")\n                                        || invalidMember.getRole().equals(\"label\")))\n                        {\n                            // keep admin centre or lable nodes\n                        }\n                        else\n                        {\n                            relation.withRemovedMember(invalidMember.getEntity());\n                            this.stagedPoints.get(identifier)\n                                    .withRemovedRelationIdentifier(relation.getIdentifier());\n                            memberIdentifiersRemoved.add(Long.toString(identifier));\n                        }\n                    }\n                    else\n                    {\n                        if (invalidMember.getEntity().getType().equals(ItemType.RELATION)\n                                && this.stagedRelations.containsKey(identifier))\n                        {\n                            relation.withRemovedMember(invalidMember.getEntity());\n                            this.stagedRelations.get(identifier)\n                                    .withRemovedRelationIdentifier(relation.getIdentifier());\n                            memberIdentifiersRemoved.add(Long.toString(identifier));\n                        }\n                        else if (invalidMember.getEntity().getType().equals(ItemType.RELATION)\n                                && this.splitRelations.containsKey(identifier))\n                        {\n                            relation.withRemovedMember(invalidMember.getEntity());\n                            this.splitRelations.get(identifier).values().forEach(\n                                    childRelation -> childRelation.withRemovedRelationIdentifier(\n                                            relation.getIdentifier()));\n                            memberIdentifiersRemoved.add(Long.toString(identifier));\n                        }\n                    }\n                });\n        if (!memberIdentifiersRemoved.isEmpty())\n        {\n            relation.withAddedTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY,\n                    String.join(\n                            SyntheticInvalidMultiPolygonRelationMembersRemovedTag.MEMBER_DELIMITER,\n                            memberIdentifiersRemoved));\n        }\n    }\n\n    /**\n     * Remove an Area from the Atlas, and remove it from any Relations that may contain it\n     *\n     * @param area\n     *            The Line to remove\n     */\n    private void removeArea(final Area area)\n    {\n        final CompleteArea removedArea = this.stagedAreas.remove(area.getIdentifier());\n        this.changes.add(FeatureChange.remove(removedArea, this.inputAtlas));\n        removedArea.relationIdentifiers().forEach(relationIdentifier ->\n        {\n            if (this.stagedRelations.containsKey(relationIdentifier))\n            {\n                this.stagedRelations.get(relationIdentifier).withRemovedMember(removedArea);\n            }\n        });\n    }\n\n    /**\n     * Remove a Line from the Atlas, and remove it from any Relations that may contain it\n     *\n     * @param line\n     *            The Line to remove\n     */\n    private void removeLine(final Line line)\n    {\n        final CompleteLine removedLine = this.stagedLines.remove(line.getIdentifier());\n        if (this.inputAtlas.line(line.getIdentifier()) == null)\n        {\n            // in rare cases, we're slicing a line that technically never existed in the original\n            // atlas, e.g. multipolygon areas now being sliced as lines, or areas that failed\n            // polygonal slicing. in these cases, simple remove the staged line, don't make a\n            // FeatureChange to remove the line from the Atlas\n            removedLine.relationIdentifiers().forEach(relationIdentifier ->\n            {\n                if (this.stagedRelations.containsKey(relationIdentifier))\n                {\n                    this.stagedRelations.get(relationIdentifier).withRemovedMember(removedLine);\n                }\n            });\n            return;\n        }\n        this.changes.add(FeatureChange.remove(removedLine, this.inputAtlas));\n        removedLine.relationIdentifiers().forEach(relationIdentifier ->\n        {\n            if (this.stagedRelations.containsKey(relationIdentifier))\n            {\n                this.stagedRelations.get(relationIdentifier).withRemovedMember(removedLine);\n            }\n        });\n    }\n\n    private org.locationtech.jts.geom.MultiPolygon removeOsmValidOverlappingInners(\n            final Relation relation, final org.locationtech.jts.geom.MultiPolygon multipolygon)\n    {\n        final Set<PreparedGeometry> slicedInnerLines = new HashSet<>();\n        relation.membersMatching(\n                member -> member.getRole().equals(RelationTypeTag.MULTIPOLYGON_ROLE_INNER)\n                        && member.getEntity().getType().equals(ItemType.LINE))\n                .forEach(member ->\n                {\n                    final Line innerLine = this.stagedLines.get(member.getEntity().getIdentifier());\n                    if (innerLine.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n                    {\n                        slicedInnerLines.add(this.preparer\n                                .create(JTS_POLYLINE_CONVERTER.convert(innerLine.asPolyLine())));\n                    }\n                });\n\n        final org.locationtech.jts.geom.Polygon[] modifiedPolygons = new org.locationtech.jts.geom.Polygon[multipolygon\n                .getNumGeometries()];\n        for (int i = 0; i < multipolygon.getNumGeometries(); i++)\n        {\n            final org.locationtech.jts.geom.Polygon currentPolygon = (org.locationtech.jts.geom.Polygon) multipolygon\n                    .getGeometryN(i);\n            final List<LinearRing> holes = new ArrayList<>();\n\n            for (int j = 0; j < currentPolygon.getNumInteriorRing(); j++)\n            {\n                final PreparedGeometry currentInner = this.preparer\n                        .create(currentPolygon.getInteriorRingN(j));\n                boolean remove = false;\n                for (int k = j + 1; k < currentPolygon.getNumInteriorRing(); k++)\n                {\n                    final Geometry comparisonInner = currentPolygon.getInteriorRingN(k);\n                    if (currentInner.intersects(comparisonInner))\n                    {\n                        final Set<Geometry> inners = new HashSet<>();\n                        inners.add(currentInner.getGeometry());\n                        inners.add(comparisonInner);\n                        if (isCoveredBy(inners, slicedInnerLines))\n                        {\n                            remove = false;\n                            break;\n                        }\n                        remove = true;\n                    }\n                }\n                if (!remove)\n                {\n                    holes.add((LinearRing) currentInner.getGeometry());\n                }\n            }\n            modifiedPolygons[i] = new org.locationtech.jts.geom.Polygon(\n                    currentPolygon.getExteriorRing(), holes.toArray(new LinearRing[holes.size()]),\n                    JtsPrecisionManager.getGeometryFactory());\n        }\n        return new org.locationtech.jts.geom.MultiPolygon(modifiedPolygons,\n                JtsPrecisionManager.getGeometryFactory());\n    }\n\n    /**\n     * Slice a Line that qualifies as an Area by converting it to 2d geometry, calculating its\n     * slices, and creating the new sliced Lines. If it belongs to just one country or cannot be\n     * sliced, update the ISOCountryTag appropriately instead\n     *\n     * @param line\n     *            The Line to slice as an Area\n     */\n    private void sliceArea(final Area area)\n    {\n        final Time time = Time.now();\n        final org.locationtech.jts.geom.Polygon jtsPolygon = JTS_POLYGON_CONVERTER\n                .convert(area.asPolygon());\n        final Set<PreparedPolygon> intersectingBoundaryPolygons = getIntersectingBoundaryPolygons(\n                jtsPolygon);\n        if (intersectingBoundaryPolygons.size() == 1\n                || CountryBoundaryMap.isSameCountry(intersectingBoundaryPolygons))\n        {\n            final String countryCode = CountryBoundaryMap.getGeometryProperty(\n                    intersectingBoundaryPolygons.iterator().next().getGeometry(),\n                    ISOCountryTag.KEY);\n            this.stagedAreas.get(area.getIdentifier()).withAddedTag(ISOCountryTag.KEY, countryCode);\n            if (!this.isInCountry.test(this.stagedAreas.get(area.getIdentifier())))\n            {\n                removeArea(area);\n            }\n            return;\n        }\n\n        // only check this once we know it's likely to be sliced\n        if (jtsPolygon.isEmpty() || !jtsPolygon.isValid())\n        {\n            if (logger.isErrorEnabled())\n            {\n                logger.error(LINE_HAD_INVALID_GEOMETRY, area.getOsmIdentifier(),\n                        this.shardOrAtlasName, jtsPolygon.toText());\n            }\n            final SortedSet<String> countries = new TreeSet<>();\n            intersectingBoundaryPolygons.forEach(polygon -> countries.add(CountryBoundaryMap\n                    .getGeometryProperty(polygon.getGeometry(), ISOCountryTag.KEY)));\n            final String countryCodes = String.join(\",\", countries);\n            this.stagedAreas.get(area.getIdentifier()).withAddedTag(ISOCountryTag.KEY,\n                    countryCodes);\n            this.stagedAreas.get(area.getIdentifier()).withAddedTag(SyntheticInvalidGeometryTag.KEY,\n                    SyntheticInvalidGeometryTag.YES.toString());\n            return;\n        }\n        final SortedMap<String, Set<org.locationtech.jts.geom.Polygon>> slices;\n        try\n        {\n            slices = slicePolygonGeometry(area.getOsmIdentifier(), jtsPolygon,\n                    intersectingBoundaryPolygons);\n        }\n        catch (final CoreException exception)\n        {\n            logger.error(LINE_HAD_MULTIPOLYGON_SLICE, area.getOsmIdentifier(),\n                    this.shardOrAtlasName);\n            final Set<Long> relationIds = new HashSet<>();\n            area.relations().forEach(relation -> relationIds.add(relation.getIdentifier()));\n            final CompleteLine lineFromArea = new CompleteLine(area.getIdentifier(),\n                    JTS_POLYLINE_CONVERTER.backwardConvert(\n                            JTS_POLYGON_CONVERTER.convert(area.asPolygon()).getExteriorRing()),\n                    area.getTags(), relationIds);\n            lineFromArea.withGeometricRelationIdentifiers(\n                    CompleteArea.from(area).geometricRelationIdentifiers());\n            area.relations().forEach(relation -> this.stagedRelations.get(relation.getIdentifier())\n                    .withAddedMember(lineFromArea, area));\n            removeArea(area);\n            this.stagedLines.put(lineFromArea.getIdentifier(), lineFromArea);\n            return;\n        }\n\n        long numSlices = 0;\n        for (final Set<org.locationtech.jts.geom.Polygon> sliceSet : slices.values())\n        {\n            numSlices += sliceSet.size();\n        }\n        logger.info(\"Way {} was sliced into {} slices\", area.getOsmIdentifier(), numSlices);\n\n        if (!checkSlices(slices.keySet(), numSlices, area))\n        {\n            return;\n        }\n\n        createNewSlicedAreas(area, slices);\n\n        if (time.elapsedSince().isMoreThan(Duration.minutes(SLICING_DURATION_WARN)))\n        {\n            logger.warn(LINE_SLICING_DURATION_EXCEEDED, area.getOsmIdentifier(),\n                    this.shardOrAtlasName, time.elapsedSince().asMilliseconds());\n        }\n    }\n\n    /**\n     * Given a geometry and a set of intersecting country boundary Polygons, calculate the portion\n     * of that geometry contained by each boundary polygon and return those mapped to country code\n     *\n     * @param geometry\n     *            The JTS geometry to slice\n     * @param countryBoundaryPolygons\n     *            All country boundary polygons intersecting it\n     * @return A map of country codes to the portions of JTS geometry inside those country boundary\n     *         polygons\n     */\n    private Map<String, Set<org.locationtech.jts.geom.Geometry>> sliceGeometry(\n            final Geometry geometry, final Set<PreparedPolygon> countryBoundaryPolygons,\n            final long identifier)\n    {\n        final Set<Geometry> filteredPieces = new HashSet<>();\n        final Map<String, Set<org.locationtech.jts.geom.Geometry>> results = new HashMap<>();\n        for (final PreparedPolygon boundaryPolygon : countryBoundaryPolygons)\n        {\n            final String countryCode = CountryBoundaryMap\n                    .getGeometryProperty(boundaryPolygon.getGeometry(), ISOCountryTag.KEY);\n            // occasionally, the geometry's internal envelope will intersect multiple boundary\n            // polygons but the geometry doesn't. in this case, just tag the geometry entirely\n            if (boundaryPolygon.contains(geometry))\n            {\n                CountryBoundaryMap.setGeometryProperty(geometry, ISOCountryTag.KEY, countryCode);\n                results.clear();\n                results.put(countryCode, new HashSet<>());\n                results.get(countryCode).add(geometry);\n                return results;\n            }\n            else if (boundaryPolygon.intersects(geometry))\n            {\n                if (!results.containsKey(countryCode))\n                {\n                    results.put(countryCode, new HashSet<>());\n                }\n                final Geometry clipped = OverlayNG.overlay(geometry, boundaryPolygon.getGeometry(),\n                        OverlayNG.INTERSECTION, JtsPrecisionManager.getPrecisionModel());\n\n                if (clipped instanceof GeometryCollection)\n                {\n                    CountryBoundaryMap.geometries((GeometryCollection) clipped)\n                            .filter(result -> isSignificantGeometry(geometry, result))\n                            .forEach(result ->\n                            {\n                                CountryBoundaryMap.setGeometryProperty(result, ISOCountryTag.KEY,\n                                        countryCode);\n                                results.get(countryCode).add(result);\n                            });\n                    filteredPieces\n                            .addAll(CountryBoundaryMap.geometries((GeometryCollection) clipped)\n                                    .filter(result -> !isSignificantGeometry(geometry, result))\n                                    .collect(Collectors.toSet()));\n                }\n                else if (isSignificantGeometry(geometry, clipped))\n                {\n                    CountryBoundaryMap.setGeometryProperty(clipped, ISOCountryTag.KEY, countryCode);\n                    results.get(countryCode).add(clipped);\n                }\n                else\n                {\n                    filteredPieces.add(clipped);\n                }\n            }\n        }\n        if (!filteredPieces.isEmpty())\n        {\n            if (geometry.getDimension() == 1)\n            {\n                long length = 0;\n                for (final Geometry filtered : filteredPieces)\n                {\n                    length += filtered.getLength();\n                }\n                logger.warn(\"Removed {} slices from way {} for being trivial, summing to {} length\",\n                        filteredPieces.size(), identifier, length);\n            }\n            else if (geometry.getDimension() == 2)\n            {\n                long area = 0;\n                for (final Geometry filtered : filteredPieces)\n                {\n                    area += filtered.getArea();\n                }\n                logger.warn(\n                        \"Removed {} slices from OSM entity {} for being trivial, summing to {} area\",\n                        filteredPieces.size(), identifier, area);\n            }\n\n        }\n        return results;\n    }\n\n    /**\n     * Slice a Line by converting it to 1d geometry, calculating its slices, and creating the new\n     * sliced Lines. If it belongs to just one country or cannot be sliced, update the ISOCountryTag\n     * appropriately instead\n     *\n     * @param line\n     *            The Line to slice as an Line\n     */\n    private void sliceLine(final Line line)\n    {\n        final Time time = Time.now();\n        final LineString jtsLine = JTS_POLYLINE_CONVERTER.convert(line.asPolyLine());\n\n        if (this.isAtlasEdge.test(line))\n        {\n            final Set<Location> selfIntersections = line.asPolyLine().selfIntersects()\n                    ? line.asPolyLine().selfIntersections()\n                    : new HashSet<>();\n            line.forEach(location ->\n            {\n                if (line.asPolyLine().first().equals(location)\n                        || line.asPolyLine().last().equals(location) || line.isClosed()\n                        || selfIntersections.contains(location)\n                        || !this.initialShard.bounds().fullyGeometricallyEncloses(location)\n                        || AtlasSectionProcessor.shouldSectionAtLocation(location, line,\n                                this.loadingOption, this.inputAtlas))\n                {\n                    this.inputAtlas.pointsAt(location).forEach(\n                            point -> this.pointsBelongingToEdge.add(point.getIdentifier()));\n                }\n            });\n        }\n\n        final Set<PreparedPolygon> intersectingBoundaryPolygons = getIntersectingBoundaryPolygons(\n                jtsLine);\n        if (CountryBoundaryMap.isSameCountry(intersectingBoundaryPolygons))\n        {\n            final String countryCode = CountryBoundaryMap.getGeometryProperty(\n                    intersectingBoundaryPolygons.iterator().next().getGeometry(),\n                    ISOCountryTag.KEY);\n            this.stagedLines.get(line.getIdentifier()).withAddedTag(ISOCountryTag.KEY, countryCode);\n            if (!this.isInCountry.test(this.stagedLines.get(line.getIdentifier())))\n            {\n                removeLine(line);\n            }\n            return;\n        }\n\n        // we only want to do this validation if we're going to slice it\n        if (jtsLine.isEmpty() || !jtsLine.isValid())\n        {\n            if (logger.isErrorEnabled())\n            {\n                logger.error(LINE_HAD_INVALID_GEOMETRY, line.getOsmIdentifier(),\n                        this.shardOrAtlasName, jtsLine.toText());\n            }\n            final SortedSet<String> countries = new TreeSet<>();\n            intersectingBoundaryPolygons.forEach(polygon -> countries.add(CountryBoundaryMap\n                    .getGeometryProperty(polygon.getGeometry(), ISOCountryTag.KEY)));\n            final String countryCodes = String.join(\",\", countries);\n            this.stagedLines.get(line.getIdentifier()).withAddedTag(ISOCountryTag.KEY,\n                    countryCodes);\n            this.stagedLines.get(line.getIdentifier()).withAddedTag(SyntheticInvalidGeometryTag.KEY,\n                    SyntheticInvalidGeometryTag.YES.toString());\n            return;\n        }\n        final SortedMap<String, Set<LineString>> slices = sliceLineStringGeometry(jtsLine,\n                intersectingBoundaryPolygons, line.getOsmIdentifier());\n\n        long numSlices = 0;\n        for (final Set<LineString> sliceSet : slices.values())\n        {\n            numSlices += sliceSet.size();\n        }\n        if (this.isAtlasEdge.test(line))\n        {\n            logger.info(\"Edge {} was sliced into {} slices\", line.getOsmIdentifier(), numSlices);\n        }\n        else\n        {\n            logger.info(\"Way {} was sliced into {} slices\", line.getOsmIdentifier(), numSlices);\n        }\n\n        if (!checkSlices(slices.keySet(), numSlices, line))\n        {\n            return;\n        }\n\n        createNewSlicedLines(line, slices);\n\n        if (time.elapsedSince().isMoreThan(Duration.minutes(SLICING_DURATION_WARN)))\n        {\n            logger.warn(LINE_SLICING_DURATION_EXCEEDED, line.getOsmIdentifier(),\n                    this.shardOrAtlasName, time.elapsedSince().asMilliseconds());\n        }\n    }\n\n    /**\n     * Take a LineString and find its slices, also attempt to merge all LineSlices for a country\n     * together to reduce artifacting\n     *\n     * @param line\n     *            The Line being sliced\n     * @param intersectingBoundaryPolygons\n     *            All intersecting country boundary polygons\n     * @return A SortedMap of country codes to the portions of the Line inside those country\n     *         boundary polygons\n     */\n    @SuppressWarnings(\"unchecked\")\n    private SortedMap<String, Set<LineString>> sliceLineStringGeometry(final LineString line,\n            final Set<PreparedPolygon> intersectingBoundaryPolygons, final long identifier)\n    {\n        final Map<String, Set<Geometry>> currentResults = sliceGeometry(line,\n                intersectingBoundaryPolygons, identifier);\n        final SortedMap<String, Set<org.locationtech.jts.geom.LineString>> results = new TreeMap<>();\n        for (final Map.Entry<String, Set<org.locationtech.jts.geom.Geometry>> entry : currentResults\n                .entrySet())\n        {\n            final Set<org.locationtech.jts.geom.LineString> lineSlices = new HashSet<>();\n            final LineMerger lineMerger = new LineMerger();\n            entry.getValue().stream()\n                    .filter(polygon -> polygon instanceof org.locationtech.jts.geom.LineString)\n                    .forEach(lineMerger::add);\n            final String countryCode = entry.getKey();\n            lineMerger.add(lineSlices);\n            lineMerger.getMergedLineStrings().forEach(mergedLineSlice ->\n            {\n                if (mergedLineSlice instanceof LineString)\n                {\n                    lineSlices.add((LineString) mergedLineSlice);\n                }\n            });\n            results.put(countryCode, lineSlices);\n        }\n        return results;\n    }\n\n    /**\n     * Take a MultiPolygon and find its slices, while also checking for validity and geometric\n     * consistency (i.e. only Polygonal results)\n     *\n     * @param identifier\n     *            The identifier for the Relation being sliced\n     * @param geometry\n     *            The multipolygon being sliced\n     * @param intersectingBoundaryPolygons\n     *            All intersecting country boundary polygons\n     * @return A SortedMap of country codes to the portions of the MultiPolygon inside those country\n     *         boundary polygons\n     */\n    private SortedMap<String, org.locationtech.jts.geom.MultiPolygon> sliceMultiPolygonGeometry(\n            final long identifier, final org.locationtech.jts.geom.MultiPolygon geometry,\n            final Set<PreparedPolygon> intersectingBoundaryPolygons)\n    {\n        final Map<String, Set<org.locationtech.jts.geom.Geometry>> currentResults = sliceGeometry(\n                geometry, intersectingBoundaryPolygons, identifier);\n        final SortedMap<String, org.locationtech.jts.geom.MultiPolygon> results = new TreeMap<>();\n        for (final Map.Entry<String, Set<org.locationtech.jts.geom.Geometry>> entry : currentResults\n                .entrySet())\n        {\n            if (entry.getValue().size() == 1 && entry.getValue().iterator()\n                    .next() instanceof org.locationtech.jts.geom.MultiPolygon)\n            {\n                results.put(entry.getKey(), (org.locationtech.jts.geom.MultiPolygon) entry\n                        .getValue().iterator().next());\n                continue;\n            }\n            final Set<org.locationtech.jts.geom.Polygon> polygonClippings = new HashSet<>();\n            entry.getValue().stream()\n                    .filter(polygon -> polygon instanceof org.locationtech.jts.geom.Polygon)\n                    .forEach(polygon -> polygonClippings\n                            .add((org.locationtech.jts.geom.Polygon) polygon));\n            final String countryCode = entry.getKey();\n            final org.locationtech.jts.geom.MultiPolygon multipolygon = new org.locationtech.jts.geom.MultiPolygon(\n                    polygonClippings.toArray(\n                            new org.locationtech.jts.geom.Polygon[polygonClippings.size()]),\n                    JtsPrecisionManager.getGeometryFactory());\n            if (multipolygon.isEmpty() || !multipolygon.isValid())\n            {\n                if (logger.isErrorEnabled())\n                {\n                    logger.warn(MULTIPOLYGON_RELATION_INVALID_SLICED_GEOMETRY, identifier,\n                            countryCode, multipolygon.toText());\n                }\n            }\n            else\n            {\n                results.put(countryCode, multipolygon);\n            }\n        }\n        return results;\n    }\n\n    /**\n     * Given a point, either remove it from the Atlas if it has no pre-existing tags and doesn't\n     * belong to an Edge, OR update its ISOCountryTag value\n     *\n     * @param point\n     *            The point to slice\n     */\n    private void slicePoint(final Point point)\n    {\n        if (point.getOsmTags().isEmpty()\n                && !this.pointsBelongingToEdge.contains(point.getIdentifier()) && this.stagedPoints\n                        .get(point.getIdentifier()).getTag(SyntheticBoundaryNodeTag.KEY).isEmpty()\n                && point.relations().isEmpty() && !this.keepAll)\n        {\n            // we care about a point if and only if it has pre-existing OSM tags OR it belongs\n            // to a future edge OR we are keeping all points for QC\n            this.stagedPoints.remove(point.getIdentifier());\n            this.changes.add(FeatureChange.remove(CompletePoint.shallowFrom(point)));\n        }\n        else\n        {\n            final CompletePoint updatedPoint = this.stagedPoints.get(point.getIdentifier());\n            final SortedSet<String> countries = new TreeSet<>();\n            countries.addAll(Arrays.asList(this.boundary.getCountryCodeISO3(point.getLocation())\n                    .getIso3CountryCode().split(ISOCountryTag.COUNTRY_DELIMITER)));\n            updatedPoint.withAddedTag(ISOCountryTag.KEY,\n                    String.join(ISOCountryTag.COUNTRY_DELIMITER, countries));\n            if (countries.size() > 1)\n            {\n                updatedPoint.withAddedTag(SyntheticBoundaryNodeTag.KEY,\n                        SyntheticBoundaryNodeTag.EXISTING.toString());\n            }\n            if (!this.isInCountry.test(updatedPoint) && !this.keepAll)\n            {\n                this.stagedPoints.remove(point.getIdentifier());\n                this.changes.add(FeatureChange.remove(updatedPoint, this.inputAtlas));\n            }\n        }\n    }\n\n    /**\n     * Take a polygon and find its slices, while also checking for validity and geometric\n     * consistency (i.e. only Polygonal results)\n     *\n     * @param identifier\n     *            the OSM identifier for the Line being sliced\n     * @param polygon\n     *            The Polygon being sliced\n     * @param intersectingBoundaryPolygons\n     *            All intersecting country boundary polygons\n     * @return A SortedMap of country codes to the portions of the Polygon inside those country\n     *         boundary polygons\n     */\n    private SortedMap<String, Set<org.locationtech.jts.geom.Polygon>> slicePolygonGeometry(\n            final long identifier, final org.locationtech.jts.geom.Polygon polygon,\n            final Set<PreparedPolygon> intersectingBoundaryPolygon)\n    {\n        final Map<String, Set<Geometry>> currentResults = sliceGeometry(polygon,\n                intersectingBoundaryPolygon, identifier);\n\n        final SortedMap<String, Set<org.locationtech.jts.geom.Polygon>> results = new TreeMap<>();\n        for (final Map.Entry<String, Set<Geometry>> entry : currentResults.entrySet())\n        {\n            final String countryCode = entry.getKey();\n            final Set<org.locationtech.jts.geom.Polygon> slicedPolygons = new HashSet<>();\n            entry.getValue().forEach(geometry ->\n            {\n                if (geometry instanceof org.locationtech.jts.geom.Polygon)\n                {\n                    // this can happen when a country boundary introduces a hole into a polygon that\n                    // was previously simple. in this case, we *must* discard the result. we surface\n                    // the log as an error so it should be easy to find\n                    if (((org.locationtech.jts.geom.Polygon) geometry).getNumInteriorRing() > 0)\n                    {\n                        throw new CoreException(\n                                \"Line {} for Atlas {} had multipolygon geometry {}!\", identifier,\n                                this.shardOrAtlasName, geometry.toText());\n                    }\n                    else\n                    {\n                        slicedPolygons.add((org.locationtech.jts.geom.Polygon) geometry);\n                    }\n                }\n            });\n            results.put(countryCode, slicedPolygons);\n        }\n        return results;\n    }\n\n    /**\n     * Slice a geometric Relation by constructing its geometry out of the valid raw Atlas members,\n     * calculating its slices, and creating the new sliced Relations with valid geometry. If it\n     * belongs to just one country or cannot be sliced, update the ISOCountryTag appropriately\n     * instead\n     *\n     * @param Relation\n     *            The Relation to slice geometrically\n     */\n    private void sliceRelation(final CompleteRelation relation)\n    {\n        final Time time = Time.now();\n\n        purgeInvalidGeometricRelationMembers(relation);\n        final Optional<MultiPolygon> geom = relation.asMultiPolygon();\n        if (geom.isEmpty())\n        {\n            return;\n        }\n\n        org.locationtech.jts.geom.MultiPolygon jtsMp = geom.get();\n\n        // Check to see if the relation is one country only, short circuit if it is\n        final Set<PreparedPolygon> polygons = this.boundary.query(jtsMp.getEnvelopeInternal())\n                .stream().distinct().collect(Collectors.toSet());\n\n        if (CountryBoundaryMap.isSameCountry(polygons))\n        {\n            final String country = CountryBoundaryMap.getGeometryProperty(\n                    polygons.iterator().next().getGeometry(), ISOCountryTag.KEY);\n            // just tag with the country code and move on, no slicing needed\n            relation.withAddedTag(ISOCountryTag.KEY, country);\n            relation.withMultiPolygonGeometry(jtsMp);\n            return;\n        }\n\n        if (!jtsMp.isValid())\n        {\n            jtsMp = removeOsmValidOverlappingInners(relation, jtsMp);\n            if (!jtsMp.isValid())\n            {\n                if (logger.isErrorEnabled())\n                {\n                    logger.error(MULTIPOLYGON_RELATION_INVALID_GEOMETRY,\n                            relation.getOsmIdentifier(), this.shardOrAtlasName, jtsMp.toText());\n                }\n                relation.withAddedTag(SyntheticInvalidGeometryTag.KEY,\n                        SyntheticInvalidGeometryTag.YES.toString());\n                return;\n            }\n            else\n            {\n                logger.warn(MULTIPOLYGON_RELATION_OVERLAPPING_INNERS, relation.getOsmIdentifier(),\n                        this.shardOrAtlasName);\n            }\n        }\n\n        final SortedMap<String, org.locationtech.jts.geom.MultiPolygon> clippedMultiPolygons = sliceMultiPolygonGeometry(\n                relation.getIdentifier(), jtsMp, polygons);\n\n        if (clippedMultiPolygons.isEmpty())\n        {\n            logger.error(MULTIPOLYGON_RELATION_HAD_NO_SLICED_GEOMETRY, relation.getOsmIdentifier(),\n                    this.shardOrAtlasName);\n            return;\n        }\n\n        final SortedMap<String, PreparedPolygon> preparedClippedPolygons = new TreeMap<>();\n        for (final Map.Entry<String, org.locationtech.jts.geom.MultiPolygon> entry : clippedMultiPolygons\n                .entrySet())\n        {\n            final String country = entry.getKey();\n            final org.locationtech.jts.geom.MultiPolygon countryMultipolygon = entry.getValue();\n            if (countryMultipolygon.equals(jtsMp))\n            {\n                logger.info(MULTIPOLYGON_RELATION_HAD_EQUIVALENT_SLICED_GEOMETRY,\n                        relation.getOsmIdentifier(), this.shardOrAtlasName);\n                // just tag with the country code and move on, no slicing needed\n                relation.withAddedTag(ISOCountryTag.KEY, country);\n                relation.withMultiPolygonGeometry(jtsMp);\n                return;\n            }\n\n            preparedClippedPolygons.put(country,\n                    (PreparedPolygon) this.preparer.create(countryMultipolygon));\n        }\n\n        if (this.consolidatePredicate.test(relation))\n        {\n            final Set<String> removedCountries = new HashSet<>();\n            double size = 0;\n            double percentSize = 0;\n            org.locationtech.jts.geom.MultiPolygon largest = null;\n            for (final PreparedPolygon polygon : preparedClippedPolygons.values())\n            {\n                if (largest == null || polygon.getGeometry().getArea() > largest.getArea())\n                {\n                    largest = (org.locationtech.jts.geom.MultiPolygon) polygon.getGeometry();\n                }\n            }\n\n            if (largest.getArea() / jtsMp.getArea() > MINIMUM_CONSOLIDATE_THRESHOLD)\n            {\n                final Set<String> countrySlices = new HashSet<>();\n                countrySlices.addAll(preparedClippedPolygons.keySet());\n                for (final String country : countrySlices)\n                {\n                    if (!preparedClippedPolygons.get(country).getGeometry().equals(largest))\n                    {\n                        size += preparedClippedPolygons.get(country).getGeometry().getArea();\n                        percentSize += preparedClippedPolygons.get(country).getGeometry().getArea()\n                                / jtsMp.getArea() * PERCENTAGE;\n                        logger.info(\n                                \"Removing sliced relation {} with size {} and percentage size {}\",\n                                relation.getOsmIdentifier(),\n                                preparedClippedPolygons.get(country).getGeometry().getArea(),\n                                preparedClippedPolygons.get(country).getGeometry().getArea()\n                                        / jtsMp.getArea());\n                        preparedClippedPolygons.remove(country);\n                        removedCountries.add(country);\n                    }\n                }\n                logger.info(\n                        \"Consolidated relation {} to country {}, ignoring countries {} with sliced size {} and percent size {}\",\n                        relation.getOsmIdentifier(),\n                        preparedClippedPolygons.keySet().iterator().next(),\n                        String.join(ISOCountryTag.COUNTRY_DELIMITER, removedCountries), size,\n                        percentSize);\n            }\n            else\n            {\n                logger.info(\n                        \"Relation {} met tagging criteria for consolidation, but largest piece with size {} did not meet percentage threshold {}\",\n                        relation.getOsmIdentifier(), largest.getArea() / jtsMp.getArea(),\n                        MINIMUM_CONSOLIDATE_THRESHOLD);\n            }\n        }\n\n        // because this is a SortedSet, iterating over the keys guarantees that we will split our\n        // relation into a deterministic identifier each time. note that this is why we put all\n        // countries that this relation spans into the key set for the map, even if it's associated\n        // with empty polygons. that way, if a relation spans country A and country B, country A\n        // will get 001000 and country B will get 002000, no matter what the country parameter for\n        // slicing is\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                relation.getIdentifier());\n        final List<Long> newRelationIds = new ArrayList<>();\n        final Map<String, CompleteRelation> newRelations = new HashMap<>();\n        preparedClippedPolygons.keySet().forEach(countryCode ->\n        {\n            final CompleteRelation newRelation = CompleteRelation.shallowFrom(relation)\n                    .withIdentifier(relationIdentifierFactory.nextIdentifier())\n                    .withTags(relation.getTags())\n                    .withAddedTag(SyntheticGeometrySlicedTag.KEY,\n                            SyntheticGeometrySlicedTag.YES.toString())\n                    .withAddedTag(ISOCountryTag.KEY, countryCode)\n                    .withRelationIdentifiers(relation.relationIdentifiers())\n                    .withOsmRelationIdentifier(relation.getOsmIdentifier());\n            newRelationIds.add(newRelation.getIdentifier());\n            if (this.isInCountry.test(newRelation))\n            {\n                final Geometry clipped = preparedClippedPolygons.get(countryCode).getGeometry();\n                final MultiPolygon slicedGeometry;\n                if (clipped instanceof org.locationtech.jts.geom.Polygon)\n                {\n                    slicedGeometry = new MultiPolygon(\n                            new org.locationtech.jts.geom.Polygon[] {\n                                    (org.locationtech.jts.geom.Polygon) preparedClippedPolygons\n                                            .get(countryCode).getGeometry() },\n                            JtsPrecisionManager.getGeometryFactory());\n                }\n                else if (clipped instanceof MultiPolygon)\n                {\n                    slicedGeometry = (MultiPolygon) preparedClippedPolygons.get(countryCode)\n                            .getGeometry();\n                }\n                else\n                {\n                    throw new CoreException(\n                            \"Cannot have non-multipolygon relation geometry {} for relation {}\",\n                            clipped, relation.getIdentifier());\n                }\n                newRelation.withMultiPolygonGeometry(slicedGeometry);\n                addCountryMembersToSplitRelation(newRelation, relation,\n                        preparedClippedPolygons.get(countryCode));\n                if (newRelation.members() != null && !newRelation.members().isEmpty())\n                {\n                    newRelations.put(countryCode, newRelation);\n                }\n            }\n        });\n\n        final HashMap<String, CompleteRelation> relationByCountry = new HashMap<>();\n        newRelations.values().forEach(newRelation ->\n        {\n            // update so each split relation knows about the other split relations\n            newRelation.withAllRelationsWithSameOsmIdentifier(new ArrayList<>());\n            newRelation.withAllKnownOsmMembers(relation.getBean());\n            relationByCountry.put(newRelation.getTag(ISOCountryTag.KEY).get(), newRelation);\n            this.stagedRelations.put(newRelation.getIdentifier(), newRelation);\n        });\n\n        // remove the old relation\n        this.changes.add(FeatureChange.remove(relation, this.inputAtlas));\n        this.stagedRelations.remove(relation.getIdentifier());\n        this.splitRelations.put(relation.getIdentifier(), relationByCountry);\n        if (time.elapsedSince().isMoreThan(Duration.minutes(SLICING_DURATION_WARN)))\n        {\n            logger.warn(MULTIPOLYGON_RELATION_SLICING_DURATION_EXCEEDED,\n                    relation.getOsmIdentifier(), this.shardOrAtlasName, time.elapsedSince());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/routing/AStarRouter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport java.util.HashSet;\nimport java.util.PriorityQueue;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * Router that follows the simple A* algorithm\n *\n * @author matthieun\n */\npublic class AStarRouter extends AbstractRouter\n{\n    /**\n     * A heuristic for an {@link AStarRouter}\n     *\n     * @author matthieun\n     */\n    public interface Heuristic\n    {\n        /**\n         * Compute the cost of a candidate, given the start and end point.\n         *\n         * @param start\n         *            The start of the route\n         * @param candidate\n         *            The candidate point of the route\n         * @param end\n         *            The end of the route\n         * @return The cost\n         */\n        double cost(Node start, Node candidate, Node end);\n    }\n\n    /**\n     * A candidate for an {@link AStarRouter}\n     *\n     * @author matthieun\n     */\n    private static class Candidate implements Comparable<Candidate>\n    {\n        private final Route route;\n        private final double cost;\n\n        Candidate(final Route route, final double cost)\n        {\n            this.route = route;\n            this.cost = cost;\n        }\n\n        @Override\n        public int compareTo(final Candidate other)\n        {\n            return this.getCost() > other.getCost() ? 1\n                    : this.getCost() == other.getCost() ? 0 : -1;\n        }\n\n        public double getCost()\n        {\n            return this.cost;\n        }\n\n        public Route getRoute()\n        {\n            return this.route;\n        }\n\n        public Edge lastEdge()\n        {\n            return getRoute().end();\n        }\n\n        public Node lastNode()\n        {\n            return lastEdge().end();\n        }\n\n        @Override\n        public String toString()\n        {\n            final StringBuilder builder = new StringBuilder();\n            builder.append(\"[Candidate: \");\n            builder.append(this.route.toString());\n            builder.append(\", Cost: \");\n            builder.append(this.cost);\n            builder.append(\"]\");\n            return builder.toString();\n        }\n\n        public Candidate withNewEdge(final Edge edge, final double edgeCost)\n        {\n            return new Candidate(getRoute().append(edge), getCost() + edgeCost);\n        }\n    }\n\n    private final Heuristic heuristic;\n\n    /**\n     * @param atlas\n     *            The {@link Atlas} on which the router works\n     * @param threshold\n     *            The threshold to look for edges in case of routing between locations\n     * @return A balanced A* Router, which gives 75% cost to the distance from the end and 25% cost\n     *         to the distance from the start.\n     */\n    public static AStarRouter balanced(final Atlas atlas, final Distance threshold)\n    {\n        final double distanceFromStartCostRatio = 0.25;\n        return new AStarRouter(atlas, threshold,\n                (start, candidate, end) -> distanceFromStartCostRatio\n                        * start.getLocation().distanceTo(candidate.getLocation()).asMeters()\n                        + (1 - distanceFromStartCostRatio)\n                                * candidate.getLocation().distanceTo(end.getLocation()).asMeters());\n    }\n\n    /**\n     * @param atlas\n     *            The {@link Atlas} on which the router works\n     * @param threshold\n     *            The threshold to look for edges in case of routing between locations\n     * @return A Dijkstra router (the heuristic looks at the distance from the start only)\n     */\n    public static AStarRouter dijkstra(final Atlas atlas, final Distance threshold)\n    {\n        return new AStarRouter(atlas, threshold, (start, candidate, end) -> start.getLocation()\n                .distanceTo(candidate.getLocation()).asMeters());\n    }\n\n    /**\n     * @param atlas\n     *            The {@link Atlas} on which the router works\n     * @param threshold\n     *            The threshold to look for edges in case of routing between locations\n     * @return A fast A* Router, which gives all cost to the distance from the end. The route will\n     *         be found faster, but the result will be non-optimal\n     */\n    public static AStarRouter fastComputationAndSubOptimalRoute(final Atlas atlas,\n            final Distance threshold)\n    {\n        return new AStarRouter(atlas, threshold, (start, candidate, end) -> candidate.getLocation()\n                .distanceTo(end.getLocation()).asMeters());\n    }\n\n    /**\n     * Construct\n     *\n     * @param atlas\n     *            The map\n     * @param threshold\n     *            The threshold to look for edges in case of routing between locations\n     * @param heuristic\n     *            The heuristic of the {@link AStarRouter}\n     */\n    public AStarRouter(final Atlas atlas, final Distance threshold, final Heuristic heuristic)\n    {\n        super(atlas, threshold);\n        this.heuristic = heuristic;\n    }\n\n    @Override\n    public Route route(final Node start, final Node end)\n    {\n        if (start.equals(end))\n        {\n            return null;\n        }\n        if (start.outEdges().isEmpty())\n        {\n            return null;\n        }\n        if (end.inEdges().isEmpty())\n        {\n            return null;\n        }\n\n        // Real Routing\n        final PriorityQueue<Candidate> candidates = new PriorityQueue<>();\n        final Set<Edge> explored = new HashSet<>();\n        // Initialize\n        for (final Edge edge : start.outEdges())\n        {\n            if (end.equals(edge.end()))\n            {\n                return Route.forEdge(edge);\n            }\n            candidates.add(new Candidate(Route.forEdge(edge),\n                    this.heuristic.cost(start, edge.end(), end)));\n        }\n        // Cycle\n        while (!candidates.isEmpty())\n        {\n            final Candidate best = candidates.poll();\n            if (end.equals(best.lastNode()))\n            {\n                return best.getRoute();\n            }\n            for (final Edge edge : best.lastNode().outEdges())\n            {\n                if (!explored.contains(edge))\n                {\n                    candidates.add(\n                            best.withNewEdge(edge, this.heuristic.cost(start, edge.end(), end)));\n                }\n            }\n            explored.add(best.lastEdge());\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/routing/AbstractRouter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.items.SnappedEdge;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Abstract implementation of a {@link Router}.\n *\n * @author matthieun\n */\npublic abstract class AbstractRouter implements Router\n{\n    @SuppressWarnings(\"unused\")\n    private static final Logger logger = LoggerFactory.getLogger(AbstractRouter.class);\n\n    private final Atlas atlas;\n    private final Distance threshold;\n\n    /**\n     * Construct\n     *\n     * @param atlas\n     *            The map\n     * @param threshold\n     *            The threshold to look for edges in case of routing between locations\n     */\n    public AbstractRouter(final Atlas atlas, final Distance threshold)\n    {\n        this.atlas = atlas;\n        this.threshold = threshold;\n    }\n\n    @Override\n    public Route route(final Edge start, final Edge end)\n    {\n        if (start == null || end == null)\n        {\n            throw new CoreException(\n                    \"Cannot compute route on null arguments: start = {} and end = {}\", start, end);\n        }\n        if (start.equals(end))\n        {\n            // Same edge\n            return Route.forEdge(start);\n        }\n        if (start.end().equals(end.start()))\n        {\n            // Directly connected edges\n            return Route.forEdge(start).append(end);\n        }\n        final Route result = route(start.end(), end.start());\n        if (result != null)\n        {\n            // Re-populate the result with the start and end edges\n            return Route.forEdge(start).append(result).append(end);\n        }\n        return null;\n    }\n\n    @Override\n    public Route route(final Location start, final Location end)\n    {\n        if (start == null || end == null)\n        {\n            throw new CoreException(\n                    \"Cannot compute route on null arguments: start = {} and end = {}\", start, end);\n        }\n        final List<SnappedEdge> startEdges = this.atlas.snaps(start, this.threshold);\n        final List<SnappedEdge> endEdges = this.atlas.snaps(end, this.threshold);\n        if (startEdges.isEmpty())\n        {\n            // logger.warn(\"Could not find a snap for start location {}\", start);\n            return null;\n        }\n        if (endEdges.isEmpty())\n        {\n            // logger.warn(\"Could not find a snap for end location {}\", end);\n            return null;\n        }\n        final Iterator<SnappedEdge> startIterator = startEdges.iterator();\n        for (int i = 0; i < startEdges.size(); i++)\n        {\n            final Edge startEdge = startIterator.next().getEdge();\n            final Iterator<SnappedEdge> endIterator = endEdges.iterator();\n            for (int j = 0; j < endEdges.size(); j++)\n            {\n                final Edge endEdge = endIterator.next().getEdge();\n                final Route route = route(startEdge, endEdge);\n                if (route != null)\n                {\n                    return route;\n                }\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/routing/AllPathsRouter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.Stack;\nimport java.util.TreeSet;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * Router that returns all possible paths between two given {@link Edge}s, using DFS. Note: this is\n * very inefficient for larger {@link Atlas} files.\n *\n * @author mgostintsev\n */\npublic final class AllPathsRouter\n{\n    public static final int MAXIMUM_ALLOWED_PATHS = 20000;\n    private static final Logger logger = LoggerFactory.getLogger(AllPathsRouter.class);\n    private static final int MAXIMUM_ALLOWED_EDGES_FOR_TRAVERSAL = 1000;\n    // The default filter to use when we want to include all edges.\n    private static final Predicate<Edge> ALL_EDGES = edge -> true;\n\n    /**\n     * Find all possible routes from the start {@link Edge} to the end {@link Edge}, using DFS.\n     *\n     * @param start\n     *            The {@link Edge} to start from\n     * @param end\n     *            The {@link Edge} to end at\n     * @return all possible {@link Route}s from start to end\n     */\n    public static Set<Route> allRoutes(final Edge start, final Edge end)\n    {\n        if (start.getAtlas() != end.getAtlas())\n        {\n            throw new CoreException(\"Supplied start and end edges must come from the same atlas!\");\n        }\n        if (Iterables.size(start.getAtlas().edges()) > MAXIMUM_ALLOWED_EDGES_FOR_TRAVERSAL)\n        {\n            throw new CoreException(\n                    \"Atlas has too many edges for an efficient traversal - aborting!\");\n        }\n\n        final Stack<Edge> path = new Stack<>();\n        final Set<Long> onPath = new HashSet<>();\n        final Set<Route> routes = new HashSet<>();\n        allRoutes(start, end, path, onPath, routes, ALL_EDGES, MAXIMUM_ALLOWED_PATHS);\n\n        return routes;\n    }\n\n    /**\n     * Find all possible routes from the start {@link Edge} to the end {@link Edge}, using DFS and\n     * return in an order determined by the given {@link Comparator}.\n     *\n     * @param start\n     *            The {@link Edge} to start from\n     * @param end\n     *            The {@link Edge} to end at\n     * @param comparator\n     *            Used to order the found routes\n     * @return all possible {@link Route}s from start to end\n     */\n    public static Set<Route> allRoutes(final Edge start, final Edge end,\n            final Comparator<Route> comparator)\n    {\n        if (start.getAtlas() != end.getAtlas())\n        {\n            throw new CoreException(\"Supplied start and end edges must come from the same atlas!\");\n        }\n        if (Iterables.size(start.getAtlas().edges()) > MAXIMUM_ALLOWED_EDGES_FOR_TRAVERSAL)\n        {\n            throw new CoreException(\n                    \"Atlas has too many edges for an efficient traversal - aborting!\");\n        }\n\n        final Stack<Edge> path = new Stack<>();\n        final Set<Long> onPath = new HashSet<>();\n        final Set<Route> routes = new TreeSet<>(comparator);\n        allRoutes(start, end, path, onPath, routes, ALL_EDGES, MAXIMUM_ALLOWED_PATHS);\n\n        return routes;\n    }\n\n    /**\n     * Find all possible routes from the start {@link Edge} to the end {@link Edge}, using DFS. Only\n     * {@link Edge}s that meet the given filter will be included in the resulting {@link Route}s.\n     *\n     * @param start\n     *            The {@link Edge} to start from\n     * @param end\n     *            The {@link Edge} to end at\n     * @param filter\n     *            The filter to use when including {@link Edge}s that make up the route\n     * @return all possible {@link Route}s from start to end\n     */\n    public static Set<Route> allRoutes(final Edge start, final Edge end,\n            final Predicate<Edge> filter)\n    {\n        if (start.getAtlas() != end.getAtlas())\n        {\n            throw new CoreException(\"Supplied start and end edges must come from the same atlas!\");\n        }\n        if (Iterables.size(start.getAtlas().edges()) > MAXIMUM_ALLOWED_EDGES_FOR_TRAVERSAL)\n        {\n            throw new CoreException(\n                    \"Atlas has too many edges for an efficient traversal - aborting!\");\n        }\n\n        final Stack<Edge> path = new Stack<>();\n        final Set<Long> onPath = new HashSet<>();\n        final Set<Route> routes = new HashSet<>();\n        allRoutes(start, end, path, onPath, routes, filter, MAXIMUM_ALLOWED_PATHS);\n\n        return routes;\n    }\n\n    /**\n     * Find all possible routes from the start {@link Edge} to the end {@link Edge}, using DFS. Only\n     * {@link Edge}s that meet the given filter will be included in the resulting {@link Route}s.\n     * The supplied {@link Comparator} will be used to order the {@link Route}s.\n     *\n     * @param start\n     *            The {@link Edge} to start from\n     * @param end\n     *            The {@link Edge} to end at\n     * @param filter\n     *            The filter to use when including {@link Edge}s that make up the route\n     * @param comparator\n     *            Used to order the found routes\n     * @return all possible {@link Route}s from start to end\n     */\n    public static Set<Route> allRoutes(final Edge start, final Edge end,\n            final Predicate<Edge> filter, final Comparator<Route> comparator)\n    {\n        if (start.getAtlas() != end.getAtlas())\n        {\n            throw new CoreException(\"Supplied start and end edges must come from the same atlas!\");\n        }\n        if (Iterables.size(start.getAtlas().edges()) > MAXIMUM_ALLOWED_EDGES_FOR_TRAVERSAL)\n        {\n            throw new CoreException(\n                    \"Atlas has too many edges for an efficient traversal - aborting!\");\n        }\n\n        final Stack<Edge> path = new Stack<>();\n        final Set<Long> onPath = new HashSet<>();\n        final Set<Route> routes = new TreeSet<>(comparator);\n        allRoutes(start, end, path, onPath, routes, filter, MAXIMUM_ALLOWED_PATHS);\n\n        return routes;\n    }\n\n    public static Set<Route> allRoutes(final Edge start, final Edge end,\n            final Predicate<Edge> filter, final int maximumAllowedPaths)\n    {\n        if (start.getAtlas() != end.getAtlas())\n        {\n            throw new CoreException(\"Supplied start and end edges must come from the same atlas!\");\n        }\n        if (Iterables.size(start.getAtlas().edges()) > MAXIMUM_ALLOWED_EDGES_FOR_TRAVERSAL)\n        {\n            throw new CoreException(\n                    \"Atlas has too many edges for an efficient traversal - aborting!\");\n        }\n\n        final Stack<Edge> path = new Stack<>();\n        final Set<Long> onPath = new HashSet<>();\n        final Set<Route> routes = new HashSet<>();\n        allRoutes(start, end, path, onPath, routes, filter, maximumAllowedPaths);\n\n        return routes;\n    }\n\n    private static void allRoutes(final Edge start, final Edge end, final Stack<Edge> path,\n            final Set<Long> onPath, final Set<Route> routes, final Predicate<Edge> filter,\n            final int maximumAllowedPaths)\n    {\n        if (routes.size() >= maximumAllowedPaths)\n        {\n            return;\n        }\n        // Add start edge to the path\n        path.push(start);\n        // This will avoid adding same edge both in forward and reverse direction\n        onPath.add(start.end().getIdentifier());\n\n        if (start.equals(end))\n        {\n            // Found a path from start to end, save this route\n            routes.add(Route.forEdges(path));\n\n            if (routes.size() > maximumAllowedPaths)\n            {\n                logger.warn(\"Too many paths found - aborting! Path so far: {}\",\n                        path.stream().map(edge -> String.valueOf(edge.getMainEdgeIdentifier()))\n                                .collect(Collectors.toList()));\n            }\n        }\n        else\n        {\n            // Consider all outgoing, non-zero length edges that can continue the current path,\n            // without repeating an edge that's already part of the current path, and that meet the\n            // given filter\n            for (final Edge candidate : start.outEdges())\n            {\n                // Proceed if we have not yet visited the edge in any direction (It would be really\n                // weired\n                // to revisit an edge in a BigNode, both in positive and negative directions.\n                if (!candidate.isZeroLength() && !onPath.contains(candidate.end().getIdentifier())\n                        && (filter.test(candidate) || candidate.equals(end)))\n                {\n                    allRoutes(candidate, end, path, onPath, routes, filter, maximumAllowedPaths);\n                }\n            }\n        }\n\n        // We've explored all paths that go through this edge. Remove it from consideration\n        path.pop();\n        onPath.remove(start.end().getIdentifier());\n    }\n\n    private AllPathsRouter()\n    {\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/routing/README.md",
    "content": "# Routing\n\nThis package contains simple routers mostly used to test the Atlas connectivity features.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/routing/Router.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\n\n/**\n * A router that routes between two points\n *\n * @author matthieun\n */\npublic interface Router\n{\n    /**\n     * Route from a start to an end\n     *\n     * @param start\n     *            The start edge\n     * @param end\n     *            The end edge\n     * @return The route corresponding, null if it can't find any\n     */\n    Route route(Edge start, Edge end);\n\n    /**\n     * Route from a start to an end\n     *\n     * @param start\n     *            The start location\n     * @param end\n     *            The end location\n     * @return The route corresponding, null if it can't find any\n     */\n    Route route(Location start, Location end);\n\n    /**\n     * Route from a start to an end\n     *\n     * @param start\n     *            The start node\n     * @param end\n     *            The end node\n     * @return The route corresponding, null if it can't find any\n     */\n    Route route(Node start, Node end);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/AtlasStatistics.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics;\n\nimport java.io.BufferedWriter;\nimport java.io.OutputStreamWriter;\nimport java.io.Serializable;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.StringEscapeUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * @author matthieun\n */\npublic class AtlasStatistics implements Iterable<AtlasStatistics.StatisticKey>, Serializable\n{\n    /**\n     * @author matthieun\n     */\n    public static class StatisticKey implements Serializable\n    {\n        private static final long serialVersionUID = -4581103889690712256L;\n\n        private final String tag;\n        private final String type;\n        private final String subType;\n\n        public StatisticKey(final String tag, final String type, final String subType)\n        {\n            this.tag = tag;\n            this.type = type;\n            this.subType = subType;\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (other instanceof StatisticKey)\n            {\n                final StatisticKey that = (StatisticKey) other;\n                return this.getTag().equalsIgnoreCase(that.getTag())\n                        && this.getType().equals(that.getType())\n                        && this.getSubType().equals(that.getSubType());\n            }\n            return false;\n        }\n\n        public String getSubType()\n        {\n            return this.subType;\n        }\n\n        public String getTag()\n        {\n            return this.tag;\n        }\n\n        public String getType()\n        {\n            return this.type;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return this.tag.toLowerCase().hashCode() + this.type.hashCode()\n                    + this.subType.hashCode();\n        }\n\n        @Override\n        public String toString()\n        {\n            return this.tag + \",\" + this.type + \",\" + StringEscapeUtils.escapeCsv(this.subType);\n        }\n    }\n\n    /**\n     * @author matthieun\n     */\n    public static class StatisticValue implements Serializable\n    {\n        private static final long serialVersionUID = 1693304125915623196L;\n\n        private final double count;\n        private final double totalCount;\n\n        private static String format(final double value)\n        {\n            return String.format(\"%.2f\", value);\n        }\n\n        private static void validate(final double value)\n        {\n            if (Math.abs(value) > Double.MAX_VALUE / 2.0)\n            {\n                throw new CoreException(\"Invalid count/totalCount value: {}\", format(value));\n            }\n        }\n\n        public StatisticValue(final double count, final double totalCount)\n        {\n            validate(count);\n            validate(totalCount);\n            this.count = count;\n            this.totalCount = totalCount;\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (other instanceof StatisticValue)\n            {\n                final StatisticValue that = (StatisticValue) other;\n                return this.getCount() == that.getCount()\n                        && this.getTotalCount() == that.getTotalCount();\n            }\n            return false;\n        }\n\n        public double getCount()\n        {\n            return this.count;\n        }\n\n        public double getTotalCount()\n        {\n            return this.totalCount;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return Double.hashCode(this.count) + Double.hashCode(this.totalCount);\n        }\n\n        public StatisticValue merge(final StatisticValue other)\n        {\n            return new StatisticValue(getCount() + other.getCount(),\n                    getTotalCount() + other.getTotalCount());\n        }\n\n        @Override\n        public String toString()\n        {\n            return format(this.count) + \",\" + format(this.totalCount);\n        }\n    }\n\n    private static final long serialVersionUID = 1564587872667339612L;\n    private static final String LINE_SEPARATOR = System.getProperty(\"line.separator\");\n\n    private static final int TAG = 0;\n    private static final int TYPE = 1;\n    private static final int SUB_TYPE = 2;\n    private static final int COUNT = 3;\n    private static final int TOTAL = 4;\n\n    private final Map<StatisticKey, StatisticValue> data;\n\n    public static String csvHeader()\n    {\n        final StringList header = new StringList();\n        header.add(\"# tag\");\n        header.add(\"type\");\n        header.add(\"sub_type\");\n        header.add(\"count\");\n        header.add(\"total_count\");\n        return header.join(\",\");\n    }\n\n    public static AtlasStatistics fromResource(final Resource resource)\n    {\n        final AtlasStatistics result = new AtlasStatistics();\n        resource.lines().forEach(line ->\n        {\n            if (line.startsWith(\"#\"))\n            {\n                return;\n            }\n            final StringList split = StringList.split(line, \",\");\n            final StatisticKey key = new StatisticKey(split.get(TAG), split.get(TYPE),\n                    split.get(SUB_TYPE));\n            final StatisticValue value = new StatisticValue(Double.valueOf(split.get(COUNT)),\n                    Double.valueOf(split.get(TOTAL)));\n            result.add(key, value);\n        });\n        return result;\n    }\n\n    public static AtlasStatistics merge(final AtlasStatistics... statistics)\n    {\n        return merge(Iterables.asList(statistics));\n    }\n\n    public static AtlasStatistics merge(final Iterable<AtlasStatistics> statistics)\n    {\n        final Map<StatisticKey, StatisticValue> mergedData = new HashMap<>();\n        for (final AtlasStatistics source : statistics)\n        {\n            for (final StatisticKey key : source)\n            {\n                StatisticValue value = source.get(key);\n                if (mergedData.containsKey(key))\n                {\n                    value = value.merge(mergedData.get(key));\n                    mergedData.remove(key);\n                }\n                mergedData.put(key, value);\n            }\n        }\n        return new AtlasStatistics(mergedData);\n    }\n\n    public AtlasStatistics()\n    {\n        this.data = new HashMap<>();\n    }\n\n    private AtlasStatistics(final Map<StatisticKey, StatisticValue> data)\n    {\n        this.data = data;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof AtlasStatistics)\n        {\n            return ((AtlasStatistics) other).getData().equals(getData());\n        }\n        return false;\n    }\n\n    public StatisticValue get(final StatisticKey key)\n    {\n        return this.data.get(key);\n    }\n\n    public StatisticValue get(final String tag, final String type, final String subType)\n    {\n        return this.data.get(new StatisticKey(tag, type, subType));\n    }\n\n    public Map<StatisticKey, StatisticValue> getData()\n    {\n        return this.data;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.data.hashCode();\n    }\n\n    @Override\n    public Iterator<StatisticKey> iterator()\n    {\n        return this.data.keySet().iterator();\n    }\n\n    public void save(final WritableResource writableResource)\n    {\n        BufferedWriter out = null;\n        try\n        {\n            out = new BufferedWriter(\n                    new OutputStreamWriter(writableResource.write(), StandardCharsets.UTF_8));\n            out.write(toString());\n            Streams.close(out);\n        }\n        catch (final Exception e)\n        {\n            Streams.close(out);\n            throw new CoreException(\"Could not save AtlasStatistics to {}\", e, writableResource);\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringList result = new StringList();\n        result.add(csvHeader());\n        this.data.forEach((key, value) -> result.add(key + \",\" + value));\n        return result.join(LINE_SEPARATOR);\n    }\n\n    protected void add(final StatisticKey key, final StatisticValue value)\n    {\n        this.data.put(key, value);\n    }\n\n    protected void append(final Map<StatisticKey, StatisticValue> statistics)\n    {\n        this.data.putAll(statistics);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/AtlasStatisticsMerger.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * @author matthieun\n */\npublic class AtlasStatisticsMerger extends Command\n{\n    private static final Switch<List<File>> INPUT = new Switch<>(\"input\",\n            \"The input folder containing all the shard stat files\", value ->\n            {\n                final File folder = new File(value);\n                return folder.listFilesRecursively().stream()\n                        .filter(file -> file.getName().endsWith(\".csv.gz\"))\n                        .collect(Collectors.toList());\n            });\n    private static final Switch<File> OUTPUT = new Switch<>(\"output\",\n            \"The output folder for the country stats\", File::new);\n\n    public static void main(final String[] args)\n    {\n        new AtlasStatisticsMerger().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        @SuppressWarnings(\"unchecked\")\n        final List<File> inputs = (List<File>) command.get(INPUT);\n        final File output = (File) command.get(OUTPUT);\n        output.mkdirs();\n        final MultiMap<String, AtlasStatistics> stats = new MultiMap<>();\n        for (final File input : inputs)\n        {\n            final String country = input.getName().split(\"_\")[0];\n            final AtlasStatistics stat = AtlasStatistics.fromResource(input);\n            stats.add(country, stat);\n        }\n        final Map<String, AtlasStatistics> countryStats = stats.reduceByKey(AtlasStatistics::merge);\n        countryStats.forEach((country, stat) -> stat.save(output.child(country + \".csv\")));\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(INPUT, OUTPUT);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/Counter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.Crawler;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.Coverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.area.LakeAreaCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.area.RiverAreaCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.BusRouteLinearCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.AllHighwayTagEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.BridgeEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.FerryEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.FreshnessEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.LanesEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.NameEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.NoNameEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.OneWayEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.PrivateAccessEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.ReferenceEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.SpeedLimitEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.SurfaceEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.TollEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.TunnelEdgeCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.line.RailLineCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.line.RiverLineCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.line.TransitRailLineCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi.EdgesCountCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi.LastUserNameCountCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi.OneWayEdgesCountCoverage;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi.SimpleCoverage;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * @author matthieun\n */\npublic class Counter extends Crawler\n{\n    private static final Logger logger = LoggerFactory.getLogger(Counter.class);\n    private static final long LAST_USER_EDITS_CUTOFF = 1_000L;\n\n    public static final Switch<Resource> POI_COUNTS_DEFINITION = new Switch<>(\"poiCounts\",\n            \"file containing all the poi counts definition\", value ->\n            {\n                final Resource defaultResource = new InputStreamResource(\n                        () -> SimpleCoverage.class.getResourceAsStream(\"counts.txt\"));\n                if (\"\".equals(value))\n                {\n                    return defaultResource;\n                }\n                else\n                {\n                    try\n                    {\n                        return new File(value);\n                    }\n                    catch (final Exception e)\n                    {\n                        return defaultResource;\n                    }\n                }\n            }, Optionality.OPTIONAL, \"\");\n\n    private Resource countsDefinition = POI_COUNTS_DEFINITION.getDefault();\n\n    private Sharding sharding;\n\n    public static void main(final String[] args)\n    {\n        new Counter().run(args);\n    }\n\n    public Counter()\n    {\n        super(logger);\n    }\n\n    public List<Coverage<? extends AtlasEntity>> generateCoverages(final Atlas atlas)\n    {\n        final List<Coverage<? extends AtlasEntity>> coverages = new ArrayList<>();\n\n        // Areas\n        coverages.add(new LakeAreaCoverage(atlas));\n        coverages.add(new RiverAreaCoverage(atlas));\n\n        // Edges\n        coverages.add(new SpeedLimitEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway));\n        coverages.add(new LanesEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway));\n        coverages.add(new SurfaceEdgeCoverage(atlas, HighwayTag::isMetricHighway));\n        coverages.add(new NameEdgeCoverage(atlas, HighwayTag::isMetricHighway, \"length_named\"));\n        coverages.add(new NameEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway,\n                \"length_roads_named\"));\n        coverages.add(new NoNameEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway));\n        coverages.add(new OneWayEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway));\n        coverages.add(\n                new AllHighwayTagEdgeCoverage(atlas, HighwayTag::isMetricHighway, \"length_total\"));\n        coverages.add(new AllHighwayTagEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway,\n                \"length_roads_total\"));\n        coverages.add(new BridgeEdgeCoverage(atlas));\n        coverages.add(new TunnelEdgeCoverage(atlas));\n        coverages.add(new FerryEdgeCoverage(atlas));\n        coverages.add(new AllHighwayTagEdgeCoverage(atlas, HighwayTag::isPedestrianNavigableHighway,\n                \"length_roads_pedestrian\"));\n        coverages.add(new ReferenceEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway));\n        coverages.add(new TollEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway));\n        coverages.add(new PrivateAccessEdgeCoverage(atlas, HighwayTag::isCarNavigableHighway));\n\n        // LineItems\n        coverages.add(new BusRouteLinearCoverage(atlas));\n\n        // Lines\n        coverages.add(new RiverLineCoverage(atlas));\n        coverages.add(new RailLineCoverage(atlas));\n        coverages.add(new TransitRailLineCoverage(atlas));\n\n        // POIs\n        SimpleCoverage.parseSimpleCoverages(atlas, this.countsDefinition.lines())\n                .forEach(coverages::add);\n        coverages.add(new EdgesCountCoverage(atlas, edge -> HighwayTag.isMetricHighway(edge)\n                && edge.length().isGreaterThan(Distance.ZERO) && edge.asPolyLine().size() > 1));\n        coverages.add(new OneWayEdgesCountCoverage(atlas, edge -> HighwayTag.isMetricHighway(edge)\n                && edge.length().isGreaterThan(Distance.ZERO) && edge.asPolyLine().size() > 1));\n        coverages.add(new LastUserNameCountCoverage(atlas, LAST_USER_EDITS_CUTOFF));\n\n        // Freshness\n        coverages.add(new FreshnessEdgeCoverage(atlas));\n\n        // Sharding related adjustments\n        if (this.sharding != null)\n        {\n            coverages.forEach(coverage -> coverage.setShardDivisor(entity ->\n            {\n                if (entity instanceof AtlasItem && !(entity instanceof LocationItem))\n                {\n                    final PolyLine geometry;\n                    if (entity instanceof LineItem)\n                    {\n                        geometry = ((LineItem) entity).asPolyLine();\n                    }\n                    else if (entity instanceof Area)\n                    {\n                        geometry = ((Area) entity).asPolygon();\n                    }\n                    else\n                    {\n                        throw new CoreException(\"Unknown entity type: {}\",\n                                entity.getClass().getCanonicalName());\n                    }\n                    return Iterables.size(this.sharding.shardsIntersecting(geometry));\n                }\n                else\n                {\n                    // Skip relations, points and nodes\n                }\n                return 1;\n            }));\n        }\n        return coverages;\n    }\n\n    public AtlasStatistics processAtlas(final Atlas atlas)\n    {\n        final AtlasStatistics result = new AtlasStatistics();\n        generateCoverages(atlas).forEach(coverage ->\n        {\n            coverage.run();\n            result.append(coverage.getStatistic());\n        });\n        return result;\n    }\n\n    public void setCountsDefinition(final Resource countsDefinition)\n    {\n        this.countsDefinition = countsDefinition;\n    }\n\n    public Counter withSharding(final Sharding sharding)\n    {\n        this.sharding = sharding;\n        return this;\n    }\n\n    @Override\n    protected void initialize(final CommandMap command)\n    {\n        this.countsDefinition = (Resource) command.get(POI_COUNTS_DEFINITION);\n        logger.info(\"Using {} for POI counts\", this.countsDefinition);\n    }\n\n    @Override\n    protected void processAtlas(final String atlasName, final Atlas atlas, final String folder)\n    {\n        final File file = new File(folder).child(atlasName + \"-statistics.csv\");\n        final AtlasStatistics statistics = processAtlas(atlas);\n        file.writeAndClose(statistics.toString());\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return super.switches().with(POI_COUNTS_DEFINITION);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/Coverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage;\n\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.statistics.AtlasStatistics.StatisticKey;\nimport org.openstreetmap.atlas.geography.atlas.statistics.AtlasStatistics.StatisticValue;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CounterWithStatistic;\nimport org.slf4j.Logger;\n\n/**\n * Coverage of an Atlas feature.\n *\n * @param <T>\n *            The type of {@link AtlasEntity}\n * @author matthieun\n */\npublic abstract class Coverage<T extends AtlasEntity>\n{\n    /**\n     * @author matthieun\n     */\n    public enum CoverageType\n    {\n        DISTANCE,\n        SURFACE,\n        COUNT;\n\n        public static CoverageType forName(final String name)\n        {\n            if (DISTANCE.name().equalsIgnoreCase(name))\n            {\n                return DISTANCE;\n            }\n            else if (SURFACE.name().equalsIgnoreCase(name))\n            {\n                return SURFACE;\n            }\n            else if (COUNT.name().equalsIgnoreCase(name))\n            {\n                return COUNT;\n            }\n            else\n            {\n                throw new CoreException(\"Unknown coverage type {}\", name);\n            }\n        }\n    }\n\n    private static final int REPORT_FREQUENCY = 100_000;\n    public static final String AGGREGATE_KEY = \"all\";\n    public static final String NULL_KEY = \"\";\n\n    private final Logger logger;\n    private final Atlas atlas;\n    private final Predicate<T> filter;\n    private Map<String, Double> counted;\n    private Map<String, Double> total;\n    private Map<String, Long> validCount;\n    private Map<String, Long> totalCount;\n    private CounterWithStatistic statistic;\n    // In case an item is spanning multiple count entities (ex. shards), supply a divisor that will\n    // under-count the item.\n    private Function<T, Integer> shardDivisor = null;\n\n    private Comparator<String> keyComparator;\n\n    public Coverage(final Logger logger, final Atlas atlas)\n    {\n        this(logger, atlas, item -> true);\n    }\n\n    /**\n     * Construct.\n     *\n     * @param atlas\n     *            The {@link Atlas} to crawl\n     * @param filter\n     *            The filter to apply to the crawled items.\n     * @param logger\n     *            The logger to use\n     */\n    public Coverage(final Logger logger, final Atlas atlas, final Predicate<T> filter)\n    {\n        this.logger = logger;\n        this.atlas = atlas;\n        this.filter = filter;\n        this.counted = new HashMap<>();\n        this.total = new HashMap<>();\n        this.validCount = new HashMap<>();\n        this.totalCount = new HashMap<>();\n        this.statistic = null;\n        this.keyComparator = null;\n    }\n\n    /**\n     * @param key\n     *            The key to get the ratio from\n     * @return The count {@link Ratio} of the specified key.\n     */\n    public Ratio getCountCoverage(final String key)\n    {\n        if (!this.counted.containsKey(key))\n        {\n            throw new CoreException(\"Key {} is not valid.\");\n        }\n        if (this.validCount.get(key) > this.totalCount.get(key))\n        {\n            throw new CoreException(\"Invalid Ratio: {} / {}\", this.validCount.get(key),\n                    this.totalCount.get(key));\n        }\n        if (this.totalCount.get(key) <= 0)\n        {\n            return Ratio.percentage(0);\n        }\n        return Ratio.ratio((double) this.validCount.get(key) / this.totalCount.get(key));\n    }\n\n    /**\n     * @param key\n     *            The key to get the ratio from\n     * @return The coverage {@link Ratio} of the specified key.\n     */\n    public Ratio getCoverage(final String key)\n    {\n        if (!this.counted.containsKey(key))\n        {\n            throw new CoreException(\"Key {} is not valid.\");\n        }\n        if (this.counted.get(key) > this.total.get(key))\n        {\n            throw new CoreException(\"Invalid Ratio: {} / {}\", this.counted.get(key),\n                    this.total.get(key));\n        }\n        if (this.total.get(key) <= 0.0)\n        {\n            throw new CoreException(\"Invalid Total: {}\", this.total.get(key));\n        }\n        return Ratio.ratio(this.counted.get(key) / this.total.get(key));\n    }\n\n    public Map<StatisticKey, StatisticValue> getStatistic()\n    {\n        final Map<StatisticKey, StatisticValue> result = new HashMap<>();\n        for (final String key : getKeys())\n        {\n            final StatisticKey statisticKey = new StatisticKey(key, type(), subType());\n            final double count;\n            final double totalCount;\n            switch (coverageType())\n            {\n                case DISTANCE:\n                case SURFACE:\n                    count = this.counted.get(key);\n                    totalCount = this.total.get(key);\n                    break;\n                case COUNT:\n                    count = this.validCount.get(key);\n                    totalCount = this.totalCount.get(key);\n                    break;\n                default:\n                    throw new CoreException(\"Unknown coverage type {}\", coverageType());\n            }\n            final StatisticValue statisticValue = new StatisticValue(count, totalCount);\n            result.put(statisticKey, statisticValue);\n        }\n        return result;\n    }\n\n    /**\n     * @param key\n     *            The key to test for\n     * @return True if this {@link Coverage} covers the specified key\n     */\n    public boolean hasKey(final String key)\n    {\n        return this.counted.containsKey(key);\n    }\n\n    /**\n     * Execute this {@link Coverage}\n     */\n    public void run()\n    {\n        this.statistic = new CounterWithStatistic(this.logger, REPORT_FREQUENCY,\n                this.getClass().getSimpleName());\n        items().forEach(item ->\n        {\n            this.statistic.increment();\n            keys(item).forEach(key ->\n            {\n                final double value = getValue(item);\n                // The adjusted value is the value divided by the divisor. The divisor can be the\n                // number of shards the feature crosses. For example, an Edge that spans three\n                // shards will be counted one third. Then when aggregating the counts for all the\n                // shards, this value will not have been counted three times too many.\n                final double adjustedValue = this.shardDivisor == null ? value\n                        : value / this.shardDivisor.apply(item);\n                increment(key, adjustedValue, isCounted(item));\n            });\n        });\n    }\n\n    public void setShardDivisor(final Function<T, Integer> shardDivisor)\n    {\n        this.shardDivisor = shardDivisor;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[\");\n        builder.append(this.getClass().getSimpleName());\n        builder.append(\":\\n\");\n        for (final String key : getKeys())\n        {\n            builder.append(\"\\t\");\n            builder.append(key);\n            builder.append(\" = \\n\\t{\\n\\t\\t\");\n            builder.append(getCoverage(key));\n            builder.append(\" of \");\n            builder.append(String.format(\"%,.2f\", this.total.get(key)));\n            builder.append(\" \");\n            builder.append(getUnit());\n            builder.append(\",\\n\\t\\t\");\n            builder.append(getCountCoverage(key));\n            builder.append(\" of \");\n            builder.append(String.format(\"%,d\", this.totalCount.get(key)));\n            builder.append(\" features\\n\\t},\\n\");\n        }\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    protected abstract CoverageType coverageType();\n\n    protected Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    /**\n     * @return All the wanted {@link AtlasEntity}s\n     */\n    protected abstract Iterable<T> getEntities();\n\n    protected Set<String> getKeys()\n    {\n        final Set<String> keySet;\n        if (this.keyComparator != null)\n        {\n            keySet = new TreeSet<>(this.keyComparator);\n        }\n        else\n        {\n            keySet = new HashSet<>();\n        }\n        keySet.addAll(this.total.keySet());\n        return keySet;\n    }\n\n    /**\n     * Get all the categories (or \"keys\") for which an item can be accounted for. For example, an\n     * Edge that is a highway would be categorized as \"highway\" and a trunk road edge would be in\n     * the different category \"trunk\".\n     *\n     * @param item\n     *            The item to test for\n     * @return All the categories (or \"keys\") for which the item can be accounted for\n     */\n    protected abstract Set<String> getKeys(T item);\n\n    /**\n     * @return The unit (if any, empty {@link String} otherwise) that is used to meter each item.\n     */\n    protected abstract String getUnit();\n\n    /**\n     * The value used to meter an item.\n     *\n     * @param item\n     *            The item to meter\n     * @return The value of the item to meter.\n     */\n    protected abstract double getValue(T item);\n\n    /**\n     * Increment counts for an item with a specific key\n     *\n     * @param key\n     *            One of the item's keys.\n     * @param value\n     *            The item's value\n     * @param valid\n     *            True if the item is valid within the requirements of the coverage metric.\n     */\n    protected void increment(final String key, final double value, final boolean valid)\n    {\n        if (value < 0.0)\n        {\n            throw new CoreException(\"Invalid value {}\", value);\n        }\n        if (!this.total.containsKey(key))\n        {\n            this.total.put(key, 0.0);\n            this.totalCount.put(key, 0L);\n            this.counted.put(key, 0.0);\n            this.validCount.put(key, 0L);\n        }\n        this.total.put(key, this.total.get(key) + value);\n        this.totalCount.put(key, this.totalCount.get(key) + 1);\n        if (valid)\n        {\n            this.counted.put(key, this.counted.get(key) + value);\n            this.validCount.put(key, this.validCount.get(key) + 1);\n        }\n    }\n\n    /**\n     * @param item\n     *            The item to test\n     * @return True if the item is valid within the requirements of the coverage metric.\n     */\n    protected abstract boolean isCounted(T item);\n\n    /**\n     * @param keyComparator\n     *            The String comparator to order the keys when printing\n     */\n    protected void setKeyComparator(final Comparator<String> keyComparator)\n    {\n        this.keyComparator = keyComparator;\n    }\n\n    protected abstract String subType();\n\n    protected abstract String type();\n\n    /**\n     * @return The filtered {@link Iterable} of items to measure.\n     */\n    private Iterable<T> items()\n    {\n        return Iterables.filter(getEntities(), this.filter);\n    }\n\n    /**\n     * The keys for an item. This method wraps the abstract getKeys() method and adds an all, which\n     * will represent an aggregate of all the coverages. If the keys provided by the sub class is\n     * empty, there is no need to add an \"all\" key. Instead a null key is added.\n     *\n     * @param item\n     *            The item to get the keys from.\n     * @return The keys from the sub-class, plus the \"All\" key.\n     */\n    private Set<String> keys(final T item)\n    {\n        final Set<String> result = getKeys(item);\n        if (result.isEmpty())\n        {\n            result.add(NULL_KEY);\n        }\n        else\n        {\n            result.add(AGGREGATE_KEY);\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/area/AreaCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.area;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.Coverage;\nimport org.slf4j.Logger;\n\n/**\n * @author matthieun\n */\npublic abstract class AreaCoverage extends Coverage<Area>\n{\n    public AreaCoverage(final Logger logger, final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public AreaCoverage(final Logger logger, final Atlas atlas, final Predicate<Area> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected CoverageType coverageType()\n    {\n        return CoverageType.DISTANCE;\n    }\n\n    @Override\n    protected Iterable<Area> getEntities()\n    {\n        return getAtlas().areas();\n    }\n\n    @Override\n    protected Set<String> getKeys(final Area area)\n    {\n        return new HashSet<>();\n    }\n\n    @Override\n    protected String getUnit()\n    {\n        return \"kilometer squared\";\n    }\n\n    @Override\n    protected double getValue(final Area area)\n    {\n        return area.asPolygon().surface().asKilometerSquared();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/area/LakeAreaCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.area;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class LakeAreaCoverage extends AreaCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(LakeAreaCoverage.class);\n\n    private static final StringList NATURAL_MATCHES = new StringList(new String[] { \"water\" });\n    private static final StringList WATER_MATCHES = new StringList(\n            new String[] { \"lake\", \"pond\", \"reflecting_pool\", \"reservoir\" });\n    private static final StringList LANDUSE_MATCHES = new StringList(new String[] { \"basin\" });\n\n    public LakeAreaCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public LakeAreaCoverage(final Atlas atlas, final Predicate<Area> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Area item)\n    {\n        return item.containsValue(\"natural\", NATURAL_MATCHES)\n                && (item.containsValue(\"water\", WATER_MATCHES) || item.tag(\"water\") == null)\n                || item.containsValue(\"landuse\", LANDUSE_MATCHES);\n    }\n\n    @Override\n    protected String subType()\n    {\n        return \"true\";\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"lakes_area\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/area/RiverAreaCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.area;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.line.RiverLineCoverage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class RiverAreaCoverage extends AreaCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(RiverAreaCoverage.class);\n\n    public RiverAreaCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public RiverAreaCoverage(final Atlas atlas, final Predicate<Area> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Area item)\n    {\n        return item.containsValue(\"waterway\", RiverLineCoverage.WATERWAY_MATCHES)\n                || item.containsValue(\"natural\", RiverLineCoverage.NATURAL_MATCHES)\n                        && item.containsValue(\"water\", RiverLineCoverage.WATER_MATCHES);\n    }\n\n    @Override\n    protected String subType()\n    {\n        return \"true\";\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"rivers_area\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/BusRouteLinearCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class BusRouteLinearCoverage extends LinearCoverage<LineItem>\n{\n    private static final Logger logger = LoggerFactory.getLogger(BusRouteLinearCoverage.class);\n\n    private static final StringList RELATION_TYPE_MATCHES = new StringList(\n            new String[] { \"route\" });\n    private static final StringList RELATION_ROUTE_MATCHES = new StringList(new String[] { \"bus\" });\n\n    public BusRouteLinearCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public BusRouteLinearCoverage(final Atlas atlas, final Predicate<LineItem> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected Iterable<LineItem> getEntities()\n    {\n        return getAtlas().lineItems();\n    }\n\n    @Override\n    protected Set<String> getKeys(final LineItem item)\n    {\n        return new HashSet<>();\n    }\n\n    @Override\n    protected boolean isCounted(final LineItem item)\n    {\n        for (final Relation relation : item.relations())\n        {\n            if (relation.containsValue(\"type\", RELATION_TYPE_MATCHES)\n                    && relation.containsValue(\"route\", RELATION_ROUTE_MATCHES))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    protected String subType()\n    {\n        return \"true\";\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"transit_bus_length\";\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/LinearCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.Coverage;\nimport org.slf4j.Logger;\n\n/**\n * @param <T>\n *            The type of {@link LineItem}\n * @author matthieun\n */\npublic abstract class LinearCoverage<T extends LineItem> extends Coverage<T>\n{\n    public LinearCoverage(final Logger logger, final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public LinearCoverage(final Logger logger, final Atlas atlas, final Predicate<T> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected CoverageType coverageType()\n    {\n        return CoverageType.DISTANCE;\n    }\n\n    @Override\n    protected String getUnit()\n    {\n        return \"kilometers\";\n    }\n\n    @Override\n    protected double getValue(final T item)\n    {\n        return item.asPolyLine().length().asKilometers();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/AllHighwayTagEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AllHighwayTagEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(AllHighwayTagEdgeCoverage.class);\n\n    private final String type;\n\n    public AllHighwayTagEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter,\n            final String type)\n    {\n        super(logger, atlas, filter);\n        this.type = type;\n    }\n\n    public AllHighwayTagEdgeCoverage(final Atlas atlas, final String type)\n    {\n        super(logger, atlas);\n        this.type = type;\n    }\n\n    @Override\n    protected boolean isCounted(final Edge item)\n    {\n        return true;\n    }\n\n    @Override\n    protected String type()\n    {\n        return this.type;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/BridgeEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.BridgeTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the Bridge tag\n *\n * @author pmi\n */\npublic class BridgeEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(BridgeEdgeCoverage.class);\n\n    public BridgeEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public BridgeEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return BridgeTag.isBridge(edge);\n    }\n\n    @Override\n    protected String type()\n    {\n        return BridgeTag.KEY;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/EdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.LinearCoverage;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.slf4j.Logger;\n\n/**\n * Highway type separated {@link Edge} coverage\n *\n * @author matthieun\n */\npublic abstract class EdgeCoverage extends LinearCoverage<Edge>\n{\n    private static final Comparator<String> KEY_COMPARATOR = (key1, key2) ->\n    {\n        final Taggable taggable1 = Taggable.with(Maps.hashMap(HighwayTag.KEY, key1));\n        final Taggable taggable2 = Taggable.with(Maps.hashMap(HighwayTag.KEY, key2));\n        final Optional<HighwayTag> tag1 = HighwayTag.highwayTag(taggable1);\n        final Optional<HighwayTag> tag2 = HighwayTag.highwayTag(taggable2);\n        if (tag1.isPresent() && tag2.isPresent())\n        {\n            return tag1.get().compareTo(tag2.get());\n        }\n        else\n        {\n            return key1.compareTo(key2);\n        }\n    };\n\n    public EdgeCoverage(final Logger logger, final Atlas atlas)\n    {\n        super(logger, atlas);\n        this.setKeyComparator(KEY_COMPARATOR);\n    }\n\n    public EdgeCoverage(final Logger logger, final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter.and(Edge::isMainEdge));\n        this.setKeyComparator(KEY_COMPARATOR);\n    }\n\n    @Override\n    public String toString()\n    {\n        return super.toString();\n    }\n\n    @Override\n    protected Iterable<Edge> getEntities()\n    {\n        return getAtlas().edges();\n    }\n\n    @Override\n    protected Set<String> getKeys(final Edge edge)\n    {\n        final Set<String> result = new HashSet<>();\n        result.add(edge.highwayTag().getTagValue());\n        return result;\n    }\n\n    @Override\n    protected String subType()\n    {\n        return \"true\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/FerryEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.RouteTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for Route=Ferry tag\n *\n * @author pmi\n */\npublic class FerryEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(FerryEdgeCoverage.class);\n\n    public FerryEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public FerryEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return RouteTag.isFerry(edge);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"ferry\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/FreshnessEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.statistics.AtlasStatistics.StatisticKey;\nimport org.openstreetmap.atlas.geography.atlas.statistics.AtlasStatistics.StatisticValue;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class FreshnessEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(FreshnessEdgeCoverage.class);\n\n    public FreshnessEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public FreshnessEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    public Map<StatisticKey, StatisticValue> getStatistic()\n    {\n        final Map<StatisticKey, StatisticValue> old = super.getStatistic();\n        final Map<String, Double> totals = new HashMap<>();\n        final Map<String, Double> monthAll = new HashMap<>();\n        old.forEach((oldKey, oldValue) ->\n        {\n            final StringList split = StringList.split(oldKey.getTag(), \"_\");\n            if (split.size() == 2)\n            {\n                final String tag = split.get(0);\n                final String month = split.get(1);\n                Double allResult = oldValue.getCount();\n                if (monthAll.containsKey(month))\n                {\n                    allResult += monthAll.get(month);\n                }\n                monthAll.put(month, allResult);\n                Double result = oldValue.getCount();\n                if (totals.containsKey(tag))\n                {\n                    result += totals.get(tag);\n                }\n                totals.put(tag, result);\n            }\n        });\n        final Map<StatisticKey, StatisticValue> result = new HashMap<>();\n        old.forEach((oldKey, oldValue) ->\n        {\n            final StringList split = StringList.split(oldKey.getTag(), \"_\");\n            if (split.size() == 2)\n            {\n                final StatisticKey key = new StatisticKey(split.get(0), oldKey.getType(),\n                        split.get(1));\n                final StatisticValue value = new StatisticValue(oldValue.getCount(),\n                        totals.get(key.getTag()));\n                result.put(key, value);\n            }\n        });\n        monthAll.forEach((month, count) -> result.put(\n                new StatisticKey(AGGREGATE_KEY, type(), month),\n                new StatisticValue(monthAll.get(month),\n                        monthAll.values().stream().reduce((left, right) -> left + right).get())));\n        return result;\n    }\n\n    @Override\n    protected Set<String> getKeys(final Edge edge)\n    {\n        final Optional<Time> lastEditOption = edge.lastEdit();\n        if (!lastEditOption.isPresent())\n        {\n            return new HashSet<>();\n        }\n        final Time lastEdit = lastEditOption.get();\n        final String year = String.valueOf(lastEdit.year());\n        final String month = String.valueOf(lastEdit.month());\n        final Set<String> result = new HashSet<>();\n        result.add(edge.highwayTag().getTagValue() + \"_\" + year + \"-\" + month);\n        return result;\n    }\n\n    @Override\n    protected boolean isCounted(final Edge item)\n    {\n        return true;\n    }\n\n    @Override\n    protected String subType()\n    {\n        return \"dummy\";\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"freshness\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/LanesEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.LanesTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the lanes tag\n *\n * @author matthieun\n */\npublic class LanesEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(LanesEdgeCoverage.class);\n\n    public LanesEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public LanesEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return LanesTag.numberOfLanes(edge).isPresent();\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"length_road_lanes\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/NameEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the name tag\n *\n * @author matthieun\n */\npublic class NameEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(NameEdgeCoverage.class);\n\n    private static final StringList EXACT_MATCHES = new StringList(new String[] { \"name\", \"name_1\",\n            \"alt_name\", \"int_name\", \"loc_name\", \"nat_name\", \"old_name\", \"reg_name\", \"short_name\",\n            \"official_name\", \"name:left\", \"name:right\", \"ref\" });\n    private static final StringList START_WITH_MATCHES = new StringList(\n            new String[] { \"name:\", \"alt_name:\", \"old_name:\", \"alt_name_\" });\n    private static final StringList RELATION_EXACT_MATCHES = new StringList(\n            new String[] { \"name\", \"ref\" });\n    private static final StringList RELATION_START_WITH_MATCHES = new StringList(\n            new String[] { \"name:\", \"ref:\" });\n\n    private final String type;\n\n    public NameEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter, final String type)\n    {\n        super(logger, atlas, filter);\n        this.type = type;\n    }\n\n    public NameEdgeCoverage(final Atlas atlas, final String type)\n    {\n        super(logger, atlas);\n        this.type = type;\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        if (edge.containsKey(EXACT_MATCHES))\n        {\n            return true;\n        }\n        if (edge.containsKeyStartsWith(START_WITH_MATCHES))\n        {\n            return true;\n        }\n        for (final Relation relation : edge.relations())\n        {\n            final Map<String, String> tags = relation.getTags();\n            if (tags.containsKey(\"type\") && \"route\".equals(tags.get(\"type\"))\n                    && tags.containsKey(\"route\") && \"road\".equals(tags.get(\"route\")))\n            {\n                if (relation.containsKey(RELATION_EXACT_MATCHES))\n                {\n                    return true;\n                }\n                if (relation.containsKeyStartsWith(RELATION_START_WITH_MATCHES))\n                {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    @Override\n    protected String type()\n    {\n        return this.type;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/NoNameEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.names.NoNameTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the no_name tag\n *\n * @author matthieun\n */\npublic class NoNameEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(NoNameEdgeCoverage.class);\n\n    public NoNameEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public NoNameEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return NoNameTag.isNoName(edge);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"no_name_roads\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/OneWayEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the lanes tag\n *\n * @author matthieun\n */\npublic class OneWayEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(OneWayEdgeCoverage.class);\n\n    public static final Set<String> ONE_WAYS = Iterables.asSet(new String[] { \"yes\", \"1\", \"-1\" });\n    public static final Set<String> ROUNDABOUT = Iterables.asSet(new String[] { \"roundabout\" });\n    public static final String ONE_WAY_KEY = \"oneway\";\n    public static final String JUNCTION_KEY = \"junction\";\n\n    public OneWayEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public OneWayEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return edge.containsValue(ONE_WAY_KEY, ONE_WAYS)\n                || edge.containsValue(JUNCTION_KEY, ROUNDABOUT);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"one_way\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/PrivateAccessEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.AccessTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for roads with Access = No or Access = Private.Does not include Highway = private\n *\n * @author pmi\n */\n\npublic class PrivateAccessEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(PrivateAccessEdgeCoverage.class);\n\n    public PrivateAccessEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public PrivateAccessEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return AccessTag.isPrivate(edge);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"length_roads_private\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/ReferenceEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Edge Coverage for route shield related tags\n *\n * @author pmi\n */\npublic class ReferenceEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(ReferenceEdgeCoverage.class);\n\n    private static final StringList REF_KEYS = new StringList(new String[] { \"ref\", \"int_ref\" });\n\n    public ReferenceEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public ReferenceEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    // If any of the possible ref keys are present, count the edge\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return edge.containsKey(REF_KEYS);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"length_roads_refs\";\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/SpeedLimitEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.MaxSpeedTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the maxspeed tag\n *\n * @author matthieun\n */\npublic class SpeedLimitEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(SpeedLimitEdgeCoverage.class);\n\n    public SpeedLimitEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public SpeedLimitEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return MaxSpeedTag.hasExtendedMaxSpeed(edge);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"speed_limit\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/SurfaceEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.SurfaceTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the surface tag\n *\n * @author matthieun\n */\npublic class SurfaceEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(SurfaceEdgeCoverage.class);\n\n    public SurfaceEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public SurfaceEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return SurfaceTag.get(edge).isPresent();\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"surface\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/TollEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.TollTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Edge Coverage for roads with toll = yes\n *\n * @author pmi\n */\n\npublic class TollEdgeCoverage extends EdgeCoverage\n{\n\n    private static final Logger logger = LoggerFactory.getLogger(TollEdgeCoverage.class);\n\n    public TollEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public TollEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return TollTag.isYes(edge);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"length_toll_roads\";\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/edge/TunnelEdgeCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.TunnelTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the Tunnel tag\n *\n * @author pmi\n */\npublic class TunnelEdgeCoverage extends EdgeCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(TunnelEdgeCoverage.class);\n\n    public TunnelEdgeCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public TunnelEdgeCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return TunnelTag.isTunnel(edge);\n    }\n\n    @Override\n    protected String type()\n    {\n        return TunnelTag.KEY;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/line/LineCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.line;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.LinearCoverage;\nimport org.slf4j.Logger;\n\n/**\n * @author matthieun\n */\npublic abstract class LineCoverage extends LinearCoverage<Line>\n{\n\n    public LineCoverage(final Logger logger, final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public LineCoverage(final Logger logger, final Atlas atlas, final Predicate<Line> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected Iterable<Line> getEntities()\n    {\n        return getAtlas().lines();\n    }\n\n    @Override\n    protected Set<String> getKeys(final Line item)\n    {\n        return new HashSet<>();\n    }\n\n    @Override\n    protected String subType()\n    {\n        return \"true\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/line/RailLineCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.line;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class RailLineCoverage extends LineCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(RailLineCoverage.class);\n\n    private static final StringList RAILWAY_MATCHES = new StringList(\n            new String[] { \"narrow_gauge\", \"rail\" });\n\n    public RailLineCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public RailLineCoverage(final Atlas atlas, final Predicate<Line> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Line item)\n    {\n        return item.containsValue(\"railway\", RAILWAY_MATCHES);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"rail_length\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/line/RiverLineCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.line;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class RiverLineCoverage extends LineCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(RiverLineCoverage.class);\n\n    public static final StringList WATERWAY_MATCHES = new StringList(\n            new String[] { \"river\", \"stream\", \"wadi\", \"canal\" });\n    public static final StringList NATURAL_MATCHES = new StringList(new String[] { \"water\" });\n    public static final StringList WATER_MATCHES = new StringList(\n            new String[] { \"river\", \"canal\" });\n\n    public RiverLineCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public RiverLineCoverage(final Atlas atlas, final Predicate<Line> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Line item)\n    {\n        return item.containsValue(\"waterway\", WATERWAY_MATCHES)\n                || item.containsValue(\"natural\", NATURAL_MATCHES)\n                        && item.containsValue(\"water\", WATER_MATCHES);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"river_length\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/linear/line/TransitRailLineCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.line;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class TransitRailLineCoverage extends LineCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(TransitRailLineCoverage.class);\n\n    private static final StringList RAILWAY_MATCHES = new StringList(\n            new String[] { \"funicular\", \"light_rail\", \"monorail\", \"subway\", \"tram\" });\n\n    public TransitRailLineCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public TransitRailLineCoverage(final Atlas atlas, final Predicate<Line> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Line item)\n    {\n        return item.containsValue(\"railway\", RAILWAY_MATCHES);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"transit_rail_length\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/poi/EdgesCountCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the name tag\n *\n * @author matthieun\n */\npublic class EdgesCountCoverage extends SimpleCoverage<Edge>\n{\n    private static final Logger logger = LoggerFactory.getLogger(EdgesCountCoverage.class);\n\n    public EdgesCountCoverage(final Atlas atlas)\n    {\n        super(logger, atlas, CoverageType.COUNT);\n    }\n\n    public EdgesCountCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter, CoverageType.COUNT);\n    }\n\n    public EdgesCountCoverage(final Logger logger, final Atlas atlas)\n    {\n        super(logger, atlas, CoverageType.COUNT);\n    }\n\n    public EdgesCountCoverage(final Logger logger, final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter, CoverageType.COUNT);\n    }\n\n    @Override\n    protected Iterable<Edge> getEntities()\n    {\n        return getAtlas().edges();\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return true;\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"edge_count\";\n    }\n\n    @Override\n    protected Predicate<Taggable> validKeyValuePairs()\n    {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/poi/LastUserNameCountCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.statistics.AtlasStatistics.StatisticKey;\nimport org.openstreetmap.atlas.geography.atlas.statistics.AtlasStatistics.StatisticValue;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.Coverage;\nimport org.openstreetmap.atlas.tags.LastEditUserNameTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Count the last user editing features\n *\n * @author matthieun\n */\npublic class LastUserNameCountCoverage extends SimpleCoverage<AtlasEntity>\n{\n    private static final Logger logger = LoggerFactory.getLogger(LastUserNameCountCoverage.class);\n\n    private final long cutoff;\n\n    /**\n     * @param atlas\n     *            The atlas to read from\n     * @param cutoff\n     *            The cutoff number of last edited features under which a user is not counted.\n     */\n    public LastUserNameCountCoverage(final Atlas atlas, final long cutoff)\n    {\n        super(logger, atlas, CoverageType.COUNT);\n        this.cutoff = cutoff;\n    }\n\n    @Override\n    public Map<StatisticKey, StatisticValue> getStatistic()\n    {\n        // Do like in Freshness, re-assign the key as a Sub-Type\n        final Map<StatisticKey, StatisticValue> old = super.getStatistic();\n        double total = 0.0;\n        for (final Entry<StatisticKey, StatisticValue> entry : old.entrySet())\n        {\n            if (!entry.getKey().getTag().equals(AGGREGATE_KEY))\n            {\n                total += entry.getValue().getCount();\n            }\n        }\n        final double totalCount = total;\n        final Map<StatisticKey, StatisticValue> result = new HashMap<>();\n        final List<Double> aggregatedCounts = new ArrayList<>();\n        aggregatedCounts.add(0.0);\n        old.forEach((oldKey, oldValue) ->\n        {\n            if (oldValue.getCount() >= this.cutoff)\n            {\n                if (!oldKey.getTag().equals(AGGREGATE_KEY))\n                {\n                    final StatisticKey key = new StatisticKey(Coverage.NULL_KEY, oldKey.getType(),\n                            oldKey.getTag());\n                    final StatisticValue value = new StatisticValue(oldValue.getCount(),\n                            totalCount);\n                    result.put(key, value);\n                }\n            }\n            else\n            {\n                aggregatedCounts.set(0, aggregatedCounts.get(0) + oldValue.getCount());\n            }\n        });\n        result.put(new StatisticKey(Coverage.NULL_KEY, type(), \"_all_others_below_\" + this.cutoff),\n                new StatisticValue(aggregatedCounts.get(0), totalCount));\n        return result;\n    }\n\n    @Override\n    protected Iterable<AtlasEntity> getEntities()\n    {\n        return getAtlas();\n    }\n\n    @Override\n    protected Set<String> getKeys(final AtlasEntity item)\n    {\n        // In CountCoverage, the key is always \"All\", but in this case, override that to make it the\n        // last user's name.\n        final HashSet<String> result = new HashSet<>();\n        final Optional<String> lastUser = item.lastUserName();\n        if (lastUser.isPresent())\n        {\n            result.add(lastUser.get());\n        }\n        else\n        {\n            result.add(\"unknown_last_user\");\n        }\n        return result;\n    }\n\n    @Override\n    protected String type()\n    {\n        return LastEditUserNameTag.KEY;\n    }\n\n    @Override\n    protected Predicate<Taggable> validKeyValuePairs()\n    {\n        // count all the values.\n        return taggable -> true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/poi/OneWayEdgesCountCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi;\n\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.linear.edge.OneWayEdgeCoverage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Coverage for the name tag\n *\n * @author matthieun\n */\npublic class OneWayEdgesCountCoverage extends EdgesCountCoverage\n{\n    private static final Logger logger = LoggerFactory.getLogger(OneWayEdgesCountCoverage.class);\n\n    public OneWayEdgesCountCoverage(final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public OneWayEdgesCountCoverage(final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected boolean isCounted(final Edge edge)\n    {\n        return edge.containsValue(OneWayEdgeCoverage.ONE_WAY_KEY, OneWayEdgeCoverage.ONE_WAYS)\n                || edge.containsValue(OneWayEdgeCoverage.JUNCTION_KEY,\n                        OneWayEdgeCoverage.ROUNDABOUT);\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"one_way_count\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/poi/SimpleCoverage.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.Coverage;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This class is specifically made for feature counts. It can count shops, lakes, bus stops, etc.\n * This is simpler than other metrics, so all the definitions of counts to run are defined in a\n * resource (text file for example), one per line.\n *\n * @author matthieun\n * @param <T>\n *            The type of {@link AtlasEntity} to count.\n */\npublic abstract class SimpleCoverage<T extends AtlasEntity> extends Coverage<T>\n{\n    private static final Logger logger = LoggerFactory.getLogger(SimpleCoverage.class);\n\n    private static final String TYPE_SEPARATOR = \";\";\n    private static final String VALUES_SEPARATOR = \",\";\n    private static final String COMMENTED_LINE = \"#\";\n    private static final int COVERAGE_TYPE_INDEX = 3;\n\n    private static final String NODES = \"nodes\";\n    private static final String EDGES = \"edges\";\n    private static final String LINES = \"lines\";\n    private static final String AREAS = \"areas\";\n    private static final String POINTS = \"points\";\n    private static final String RELATIONS = \"relations\";\n\n    private final Predicate<Taggable> filter;\n    private final CoverageType coverageType;\n\n    /**\n     * Parse a count configuration file\n     *\n     * @param atlas\n     *            The Atlas to crawl\n     * @param coverages\n     *            The configuration file\n     * @return All the {@link SimpleCoverage}s defined in the file\n     */\n    public static Iterable<SimpleCoverage<AtlasEntity>> parseSimpleCoverages(final Atlas atlas,\n            final Iterable<String> coverages)\n    {\n        final List<SimpleCoverage<AtlasEntity>> result = new ArrayList<>();\n        final Iterable<String> filteredCoverages = Iterables.filter(coverages,\n                line -> !(line.startsWith(COMMENTED_LINE) || \"\".equals(line)));\n        filteredCoverages.forEach(definition ->\n        {\n            try\n            {\n                // Each line\n                final StringList split = StringList.split(definition, TYPE_SEPARATOR);\n                final StringList sources = StringList.split(split.get(1), VALUES_SEPARATOR);\n                final String type = split.get(0);\n                final String coverageTypes = split.size() > COVERAGE_TYPE_INDEX\n                        ? split.get(COVERAGE_TYPE_INDEX)\n                        : CoverageType.COUNT.name();\n                final Set<CoverageType> coverageTypeSet = StringList\n                        .split(coverageTypes, VALUES_SEPARATOR).stream().map(CoverageType::forName)\n                        .collect(Collectors.toSet());\n                final Predicate<Taggable> allowedTags = TaggableFilter.forDefinition(split.get(2));\n                final BiFunction<String, CoverageType, SimpleCoverage<AtlasEntity>> simpleCoverageFunction = (\n                        metricName, sampleCoverageType) -> new SimpleCoverage<AtlasEntity>(\n                                LoggerFactory.getLogger(metricName), atlas, sampleCoverageType)\n                        {\n                            @Override\n                            protected Iterable<AtlasEntity> getEntities()\n                            {\n                                if (sources.contains(\"all\"))\n                                {\n                                    return getAtlas();\n                                }\n                                final List<Iterable<? extends AtlasEntity>> result = new ArrayList<>();\n                                if (sources.contains(NODES))\n                                {\n                                    result.add(getAtlas().nodes());\n                                }\n                                if (sources.contains(EDGES))\n                                {\n                                    result.add(getAtlas().edges());\n                                }\n                                if (sources.contains(AREAS))\n                                {\n                                    result.add(getAtlas().areas());\n                                }\n                                if (sources.contains(LINES))\n                                {\n                                    result.add(getAtlas().lines());\n                                }\n                                if (sources.contains(POINTS))\n                                {\n                                    result.add(getAtlas().points());\n                                }\n                                if (sources.contains(RELATIONS))\n                                {\n                                    result.add(getAtlas().relations());\n                                }\n                                return new MultiIterable<>(result);\n                            }\n\n                            @Override\n                            protected String type()\n                            {\n                                return metricName;\n                            }\n\n                            @Override\n                            protected Predicate<Taggable> validKeyValuePairs()\n                            {\n                                return allowedTags;\n                            }\n                        };\n                coverageTypeSet.forEach(localCoverageType ->\n                {\n                    final String appendix = CoverageType.COUNT.equals(localCoverageType) ? \"\"\n                            : \"_\" + localCoverageType.name().toLowerCase();\n                    final String metricName = type + appendix;\n                    result.add(simpleCoverageFunction.apply(metricName, localCoverageType));\n                });\n            }\n            catch (final Exception e)\n            {\n                throw new CoreException(\"Error parsing {}\", definition, e);\n            }\n        });\n        return result;\n    }\n\n    public SimpleCoverage(final Logger logger, final Atlas atlas, final CoverageType coverageType)\n    {\n        super(logger, atlas);\n        this.coverageType = coverageType;\n        this.filter = validKeyValuePairs();\n    }\n\n    public SimpleCoverage(final Logger logger, final Atlas atlas, final Predicate<T> filter,\n            final CoverageType coverageType)\n    {\n        super(logger, atlas, filter);\n        this.coverageType = coverageType;\n        this.filter = validKeyValuePairs();\n    }\n\n    @Override\n    protected CoverageType coverageType()\n    {\n        return this.coverageType;\n    }\n\n    @Override\n    protected Set<String> getKeys(final AtlasEntity item)\n    {\n        // Only All will be represented here\n        return new HashSet<>();\n    }\n\n    @Override\n    protected String getUnit()\n    {\n        switch (coverageType())\n        {\n            case COUNT:\n                return \"count unit\";\n            case DISTANCE:\n                return \"kilometers\";\n            case SURFACE:\n                return \"square kilometers\";\n            default:\n                throw new CoreException(\"Unknown coverage type: {}\", this.coverageType.name());\n        }\n    }\n\n    @Override\n    protected double getValue(final T item)\n    {\n        switch (coverageType())\n        {\n            case COUNT:\n                return 1;\n            case DISTANCE:\n                return getDistance(item).asKilometers();\n            case SURFACE:\n                return getSurface(item).asKilometerSquared();\n            default:\n                throw new CoreException(\"Unknown coverage type: {}\", this.coverageType.name());\n        }\n    }\n\n    @Override\n    protected boolean isCounted(final T item)\n    {\n        return this.filter.test(item);\n    }\n\n    @Override\n    protected String subType()\n    {\n        return \"true\";\n    }\n\n    /**\n     * @return All the tag key/value pairs that an entity needs to have to be counted. Extending\n     *         this definition, if a TagGroup is empty, then all features are valid and will be\n     *         counted.\n     */\n    protected abstract Predicate<Taggable> validKeyValuePairs();\n\n    /**\n     * Gets the length of the item to count\n     *\n     * @param item\n     *            The item to count\n     * @return The length of the item to count. In case this is a {@link Relation}, the length is\n     *         the sum of all the relation's lowest order {@link LineItem}s.\n     */\n    private Distance getDistance(final AtlasEntity item)\n    {\n        Distance result = Distance.ZERO;\n        if (item instanceof LineItem)\n        {\n            return ((LineItem) item).asPolyLine().length();\n        }\n        else if (item instanceof Relation)\n        {\n            for (final RelationMember member : ((Relation) item).members())\n            {\n                result = result.add(getDistance(member.getEntity()));\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Gets the area of the item to count\n     *\n     * @param item\n     *            The item to count\n     * @return The area of the item to count. In case this is a {@link Relation}, and the\n     *         {@link Relation} is type=multipolygon, the area is the area of the multipolygon. In\n     *         case the relation is of another type, the area is the sum of all the lowest order\n     *         areas that make the relation.\n     */\n    private Surface getSurface(final AtlasEntity item)\n    {\n        Surface result = Surface.MINIMUM;\n        if (item instanceof Relation && ((Relation) item).isGeometric() || item instanceof Area)\n        {\n            try\n            {\n                final RelationOrAreaToMultiPolygonConverter converter = new RelationOrAreaToMultiPolygonConverter();\n                result = result.add(converter.apply(item).surface());\n                return result;\n            }\n            catch (final CoreException e)\n            {\n                // Many features will not be multipolygons.\n            }\n            catch (final IllegalArgumentException e)\n            {\n                logger.error(\"AtlasStatistics cannot compute surface of {}\", item, e);\n            }\n            return result;\n        }\n        if (item instanceof Relation)\n        {\n            // Relation that is not of type multipolygon\n            for (final RelationMember member : ((Relation) item).members())\n            {\n                result = result.add(getSurface(member.getEntity()));\n            }\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/weird/NodesPerLength.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.weird;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.Coverage;\nimport org.slf4j.Logger;\n\n/**\n * @author matthieun\n */\npublic class NodesPerLength extends Coverage<Edge>\n{\n    public NodesPerLength(final Logger logger, final Atlas atlas)\n    {\n        super(logger, atlas);\n    }\n\n    public NodesPerLength(final Logger logger, final Atlas atlas, final Predicate<Edge> filter)\n    {\n        super(logger, atlas, filter);\n    }\n\n    @Override\n    protected CoverageType coverageType()\n    {\n        // This is a hack to not have to re-code the whole system. Here the distance measure will be\n        // the number of points per km, in each edge.\n        return CoverageType.DISTANCE;\n    }\n\n    @Override\n    protected Iterable<Edge> getEntities()\n    {\n        return getAtlas().edges();\n    }\n\n    @Override\n    protected Set<String> getKeys(final Edge item)\n    {\n        // Only \"All\" will show up.\n        return new HashSet<>();\n    }\n\n    @Override\n    protected String getUnit()\n    {\n        return \"Nodes per km\";\n    }\n\n    @Override\n    protected double getValue(final Edge item)\n    {\n        return item.asPolyLine().size() / item.length().asKilometers();\n    }\n\n    @Override\n    protected boolean isCounted(final Edge item)\n    {\n        return true;\n    }\n\n    @Override\n    protected String subType()\n    {\n        return \"true\";\n    }\n\n    @Override\n    protected String type()\n    {\n        return \"nodes_per_km\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/sub/AtlasCutType.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.sub;\n\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Denotes the type of Atlas cut.\n * <ul>\n * <li>Soft-cut: Perform a cut and keep all entities that match the filter or bound. However, bring\n * in additional atlas entities that did not meet the original criteria, in order to satisfy atlas\n * integrity. Example: An Atlas Edge is brought in via boundary or filter match, but one of its\n * Nodes is not. The Node would be brought in, despite not meeting initial criteria, in order to\n * maintain Atlas integrity. Verbose version:\n * <ul>\n * <li>{@link Node}: It is included only if it is matched by the matcher, or if a valid edge (below)\n * has it at one of its ends, or it is pulled in by an {@link Edge} which itself pulled in by a\n * {@link Relation}, matched by the matcher.\n * <li>{@link Edge}: It is included only if it is matched by the matcher or pulled in by a\n * {@link Relation}, matched by the matcher.\n * <li>{@link Area}: It is included only if it is matched by the matcher or pulled in by a\n * {@link Relation}, matched by the matcher.\n * <li>{@link Line}: It is included only if it is matched by the matcher or pulled in by a\n * {@link Relation}, matched by the matcher.\n * <li>{@link Point}: It is included only if it is matched by the matcher or pulled in by a\n * {@link Relation}, matched by the matcher.\n * <li>{@link Relation}: It is included if is matched by matcher or pulled in via another\n * {@link Relation} which was matched by the matcher. To maintain {@link Relation} validity, all of\n * its members will be included in the member list, even if not matched by the given matcher.\n * </ul>\n * </li>\n * <li>Silk-cut: Largely the same as a Soft-cut, however there is one important difference. During\n * soft-cuts, Points are only brought in if they match an Edge. However, this filter is too\n * restrictive if the cut is being performed before way-sectioning, as in this case all Lines are\n * potential Edge candidates and must have Points associated with their coordinates preserved.\n * Silk-cut will perform an extra check so that all Lines being included will have any Points\n * associated with their coordinates preserved as well, allowing way-sectioning to run on these\n * Atlases.\n * <li>Hard-cut-all: Perform a cut, only keep entities that match the bound or filter. If including\n * the item in the final Atlas breaks Atlas integrity, exclude that entity. Example: An Edge is\n * brought in via boundary or filter match, but its start or end Node is omitted. As a result, the\n * Edge is left out of the final Atlas.</li>\n * <li>Hard-cut-relations-only: Perform a soft cut and maintain Atlas integrity for all Atlas Items.\n * For all Relations - perform a hard cut and only include members that satisfy the given predicate\n * or bound. This case will remove Relations altogether if no members satisfy the required\n * conditions.</li>\n * </ul>\n *\n * @author mgostintsev\n */\npublic enum AtlasCutType\n{\n    SOFT_CUT,\n    SILK_CUT,\n    HARD_CUT_ALL,\n    HARD_CUT_RELATIONS_ONLY;\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/sub/SubAtlasCreator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.sub;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasObject;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Responsible for creating sub-atlases given a cut type and Atlas.\n *\n * @author mgostintsev\n * @author samgass\n */\npublic final class SubAtlasCreator\n{\n    private static final Logger logger = LoggerFactory.getLogger(SubAtlasCreator.class);\n\n    private static final String CUT_START_MESSAGE = \"Starting {} of Atlas {} with meta-data {}\";\n    private static final String CUT_STOP_MESSAGE = \"Finished {} of Atlas {} in {}\";\n    private static final String SUB_ATLAS_NAME_POSTFIX = \"_sub\";\n\n    private static final double HARD_CUT_RATIO_BUFFER = 0.5;\n    private static final double SOFT_CUT_RATIO_BUFFER = 1.2;\n\n    public static Optional<Atlas> hardCutAllEntities(final Atlas atlas,\n            final GeometricSurface boundary)\n    {\n        logger.trace(CUT_START_MESSAGE, AtlasCutType.HARD_CUT_ALL, atlas.getName(),\n                atlas.metaData());\n        final Time begin = Time.now();\n\n        final Iterable<Node> nodesWithin = getContainmentCachingSupplier(atlas,\n                atlas.nodesWithin(boundary), ItemType.NODE).get();\n        final Iterable<Edge> edgesWithin = getContainmentCachingSupplier(atlas,\n                atlas.edgesWithin(boundary), ItemType.EDGE).get();\n        final Iterable<Area> areasWithin = getContainmentCachingSupplier(atlas,\n                atlas.areasWithin(boundary), ItemType.AREA).get();\n        final Iterable<Line> linesWithin = getContainmentCachingSupplier(atlas,\n                atlas.linesWithin(boundary), ItemType.LINE).get();\n        final Iterable<Point> pointsWithin = getContainmentCachingSupplier(atlas,\n                atlas.pointsWithin(boundary), ItemType.POINT).get();\n\n        // Generate the size estimates and the builder. There is an edge case we need to\n        // consider for node size estimating. Because of our underlying dependency on awt insideness\n        // definition - we may be excluding some nodes that are exactly on the boundary of the\n        // given polygon. To account for this, instead of doing a count to have an exact number, we\n        // choose here to have an arbitrary 5% buffer on top of the nodes inside the polygon. This\n        // mostly avoids resizing. No other entity needs the buffer since the resulting within calls\n        // give us exact features we want to include.\n        final long nodeEstimate = Math.round(Iterables.size(nodesWithin) * HARD_CUT_RATIO_BUFFER);\n        final AtlasSize sizeEstimates = new AtlasSize(Iterables.size(edgesWithin), nodeEstimate,\n                Iterables.size(areasWithin), Iterables.size(linesWithin),\n                Iterables.size(pointsWithin),\n                Iterables.size(atlas.relationsWithEntitiesIntersecting(boundary)));\n        final PackedAtlasBuilder builder = getPackedAtlasBuilder(atlas, sizeEstimates);\n\n        addNodes(nodesWithin, node -> !hasEntity(node, builder), builder);\n        addEdges(edgesWithin, edge -> !hasEntity(edge, builder), builder);\n        addAreas(areasWithin, builder);\n        addLines(linesWithin, builder);\n        addPoints(pointsWithin, builder);\n\n        // Checks all relations not currently in the subatlas and adds in all that have valid\n        // members (basically filters out empty relations)\n        addRelations(atlas, atlas.relationsLowerOrderFirst(),\n                relation -> !hasEntity(relation, builder),\n                member -> hasEntity(member.getEntity(), builder), builder,\n                AtlasCutType.HARD_CUT_ALL);\n\n        final PackedAtlas result = (PackedAtlas) builder.get();\n        if (result != null)\n        {\n            result.trim();\n        }\n\n        logger.trace(CUT_STOP_MESSAGE, AtlasCutType.HARD_CUT_ALL, atlas.getName(),\n                begin.elapsedSince());\n        return Optional.ofNullable(result);\n    }\n\n    public static Optional<Atlas> hardCutAllEntities(final Atlas atlas,\n            final Predicate<AtlasEntity> matcher)\n    {\n        logger.trace(CUT_START_MESSAGE, AtlasCutType.HARD_CUT_ALL, atlas.getName(),\n                atlas.metaData());\n        final Time begin = Time.now();\n\n        // Using a predicate here can create wild changes in entity counts. For example a predicate\n        // would include only edges, but all the nodes would have to be pulled in. In that case, we\n        // use the same size as the source Atlas, but we trim it at the end.\n        final PackedAtlasBuilder builder = getPackedAtlasBuilder(atlas, atlas.size());\n\n        // Identify all nodes that match the predicate, while avoiding \"floating\" Nodes that have no\n        // connected Edges\n        final Set<Long> matchedEdgeIdentifiers = Iterables.stream(atlas.edges(matcher::test))\n                .map(Edge::getIdentifier).collectToSet();\n        final Predicate<Node> validNodeFilter = node -> !hasEntity(node, builder)\n                && node.connectedEdges().stream().anyMatch(connectedEdge -> matchedEdgeIdentifiers\n                        .contains(connectedEdge.getIdentifier()));\n        addNodes(atlas.nodes(matcher::test), validNodeFilter, builder);\n\n        // Predicate that checks that both the start and end node were added, and that the edge has\n        // not yet been added to the builder\n        final Predicate<Edge> validEdgeFilter = edge -> !hasEntity(edge, builder)\n                && builder.peek().node(edge.start().getIdentifier()) != null\n                && builder.peek().node(edge.end().getIdentifier()) != null;\n        addEdges(atlas.edges(matcher::test), validEdgeFilter, builder);\n\n        addPoints(atlas.points(matcher::test), builder);\n        addAreas(atlas.areas(matcher::test), builder);\n        addLines(atlas.lines(matcher::test), builder);\n\n        // Add all relations that matched, as long as they have at least one member left in the\n        // subatlas (i.e. aren't empty)\n        addRelations(atlas, atlas.relationsLowerOrderFirst(), matcher::test,\n                member -> hasEntity(member.getEntity(), builder), builder,\n                AtlasCutType.HARD_CUT_ALL);\n\n        final PackedAtlas result = (PackedAtlas) builder.get();\n        if (result != null)\n        {\n            result.trim();\n        }\n\n        logger.trace(CUT_STOP_MESSAGE, AtlasCutType.HARD_CUT_ALL, atlas.getName(),\n                begin.elapsedSince());\n        return Optional.ofNullable(result);\n    }\n\n    public static Optional<Atlas> hardCutRelationsOnly(final Atlas atlas,\n            final Predicate<AtlasEntity> matcher)\n    {\n        logger.trace(CUT_START_MESSAGE, AtlasCutType.HARD_CUT_RELATIONS_ONLY, atlas.getName(),\n                atlas.metaData());\n        final Time begin = Time.now();\n\n        // Using a predicate here can create wild changes in entity counts. For example a predicate\n        // would include only edges, but all the nodes would have to be pulled in. In that case, we\n        // use the same size as the source Atlas, but we trim it at the end.\n        final PackedAtlasBuilder builder = getPackedAtlasBuilder(atlas, atlas.size());\n\n        // Identify all nodes that match the predicate. This includes pulling in nodes that did not\n        // match the predicate, but are required by matched edges\n        addNodes(atlas.nodes(matcher::test), node -> !hasEntity(node, builder), builder);\n        addNodesFromEdges(atlas.edges(matcher::test), builder);\n\n        addEdges(atlas.edges(matcher::test), edge -> !hasEntity(edge, builder), builder);\n        addPoints(atlas.points(matcher::test), builder);\n        addAreas(atlas.areas(matcher::test), builder);\n        addLines(atlas.lines(matcher::test), builder);\n\n        // Add all relations that matched, as long as they have at least one member left in the\n        // subatlas (i.e. aren't empty)\n        addRelations(atlas, atlas.relationsLowerOrderFirst(), matcher::test,\n                member -> hasEntity(member.getEntity(), builder), builder,\n                AtlasCutType.HARD_CUT_RELATIONS_ONLY);\n\n        final PackedAtlas result = (PackedAtlas) builder.get();\n        if (result != null)\n        {\n            result.trim();\n        }\n\n        logger.trace(CUT_STOP_MESSAGE, AtlasCutType.HARD_CUT_RELATIONS_ONLY, atlas.getName(),\n                begin.elapsedSince());\n        return Optional.ofNullable(result);\n    }\n\n    public static Optional<Atlas> silkCut(final Atlas atlas, final GeometricSurface boundary)\n    {\n        logger.debug(CUT_START_MESSAGE, AtlasCutType.SILK_CUT, atlas.getName(), atlas.metaData());\n        final Time begin = Time.now();\n\n        final Iterable<Node> nodesWithin = getIntersectingCachingSupplier(atlas,\n                atlas.nodesWithin(boundary), ItemType.NODE).get();\n        final Iterable<Edge> edgesIntersecting = getIntersectingCachingSupplier(atlas,\n                atlas.edgesIntersecting(boundary), ItemType.EDGE).get();\n        final Iterable<Area> areasIntersecting = getIntersectingCachingSupplier(atlas,\n                atlas.areasIntersecting(boundary), ItemType.AREA).get();\n        final Iterable<Line> linesIntersecting = getIntersectingCachingSupplier(atlas,\n                atlas.linesIntersecting(boundary), ItemType.LINE).get();\n        final Iterable<Point> pointsWithin = getIntersectingCachingSupplier(atlas,\n                atlas.pointsWithin(boundary), ItemType.POINT).get();\n\n        // Generate the size estimates, then the builder.\n        // Nodes estimating is a bit tricky. We want to include all the nodes within the polygon,\n        // but we also want to include those attached to edges that span outside the polygon.\n        // Instead of doing a count to have an exact number, we choose here to have an arbitrary 20%\n        // buffer on top of the nodes inside the polygon. This mostly avoids resizing.\n        final long nodeNumber = Math.round(Iterables.size(nodesWithin) * SOFT_CUT_RATIO_BUFFER);\n        final long edgeNumber = Math\n                .round(Iterables.size(edgesIntersecting) * SOFT_CUT_RATIO_BUFFER);\n        final long areaNumber = Math\n                .round(Iterables.size(areasIntersecting) * SOFT_CUT_RATIO_BUFFER);\n        final long lineNumber = Math\n                .round(Iterables.size(linesIntersecting) * SOFT_CUT_RATIO_BUFFER);\n        final long pointNumber = Math.round(Iterables.size(pointsWithin) * SOFT_CUT_RATIO_BUFFER);\n        final long relationNumber = Math\n                .round(Iterables.size(atlas.relationsWithEntitiesIntersecting(boundary))\n                        * SOFT_CUT_RATIO_BUFFER);\n        final AtlasSize sizeEstimates = new AtlasSize(edgeNumber, nodeNumber, areaNumber,\n                lineNumber, pointNumber, relationNumber);\n\n        final PackedAtlasBuilder builder = getPackedAtlasBuilder(atlas, sizeEstimates);\n\n        // Add all the individual nodes and edge start and end nodes coming from the predicate\n        addNodes(nodesWithin, node -> !hasEntity(node, builder), builder);\n        addNodesFromEdges(edgesIntersecting, builder);\n\n        addEdges(edgesIntersecting, edge -> !hasEntity(edge, builder), builder);\n        addAreas(areasIntersecting, builder);\n        addLines(linesIntersecting, builder);\n        addPoints(pointsWithin, builder);\n        addPointsForLines(atlas, linesIntersecting, builder);\n\n        // Check all relations and filter out either hard-cut (filter out members not entirely\n        // enclosed in the boundaries) or soft-cut (allow any members that intersected or were\n        // within the boundaries)\n        addRelations(atlas, atlas.relationsLowerOrderFirst(),\n                relation -> !hasEntity(relation, builder),\n                member -> hasEntity(member.getEntity(), builder), builder, AtlasCutType.SILK_CUT);\n\n        final PackedAtlas result = (PackedAtlas) builder.get();\n        if (result != null)\n        {\n            result.trim();\n        }\n\n        logger.trace(CUT_STOP_MESSAGE, AtlasCutType.SILK_CUT, atlas.getName(),\n                begin.elapsedSince());\n        return Optional.ofNullable(result);\n    }\n\n    /**\n     * @param matcher\n     *            The matcher to consider\n     * @param atlas\n     *            The {@link Atlas} to cut\n     * @return a sub-atlas from this Atlas.\n     */\n    public static Optional<Atlas> silkCut(final Atlas atlas, final Predicate<AtlasEntity> matcher)\n    {\n        logger.trace(CUT_START_MESSAGE, AtlasCutType.SOFT_CUT, atlas.getName(), atlas.metaData());\n        final Time begin = Time.now();\n\n        // Using a predicate here can create wild changes in entity counts. For example a predicate\n        // would include only edges, but all the nodes would have to be pulled in. In that case, we\n        // use the same size as the source Atlas, but we trim it at the end.\n        final PackedAtlasBuilder builder = getPackedAtlasBuilder(atlas, atlas.size());\n\n        // First, add all the nodes contained by relations and all start and end nodes from edges\n        // contained by relations\n        atlas.relations(matcher::test).forEach(relation -> addNodesFromRelation(relation, builder));\n\n        // Next, add all the individual nodes and edge start and end nodes coming from the predicate\n        addNodes(atlas.nodes(matcher::test), node -> !hasEntity(node, builder), builder);\n        addNodesFromEdges(atlas.edges(matcher::test), builder);\n\n        // Next, add the Lines, Points, Areas and Edges. Edges are a little trickier - 1) They rely\n        // on their start/end nodes to have already been added 2) They can potentially pull in nodes\n        // that weren't matched by the given predicate. These two cases are handled above.\n        // Similarly, Relations depend on all other entities to have been added, since they make up\n        // the member list. For this pass, add all entities, except Relations, that match the given\n        // Predicate to the builder.\n        addEdges(atlas.edges(matcher::test), edge -> !hasEntity(edge, builder), builder);\n        addPoints(atlas.points(matcher::test), builder);\n        addAreas(atlas.areas(matcher::test), builder);\n        addLines(atlas.lines(matcher::test), builder);\n        addPointsForLines(atlas, atlas.lines(matcher::test), builder);\n\n        // It's now safe to add Relations. There are two caveats: 1. A Relation member may not\n        // have been pulled in by the given predicate. In order to maintain Relation validity, we\n        // need to pull in those members. 2. The member may be a Relation that hasn't been\n        // added yet. We check if any of the members are unadded relations and if so, we add\n        // them.\n        Iterables.filter(atlas.relationsLowerOrderFirst(), matcher::test)\n                .forEach(relation -> addRelationMembers(relation, builder));\n        addRelations(atlas, atlas.relationsLowerOrderFirst(), matcher::test, member -> true,\n                builder, AtlasCutType.SOFT_CUT);\n\n        final PackedAtlas result = (PackedAtlas) builder.get();\n        if (result != null)\n        {\n            result.trim();\n        }\n\n        logger.trace(CUT_STOP_MESSAGE, AtlasCutType.SOFT_CUT, atlas.getName(),\n                begin.elapsedSince());\n        return Optional.ofNullable(result);\n    }\n\n    public static Optional<Atlas> softCut(final Atlas atlas, final GeometricSurface boundary,\n            final boolean hardCutRelations)\n    {\n        logger.trace(CUT_START_MESSAGE,\n                !hardCutRelations ? AtlasCutType.SOFT_CUT : AtlasCutType.HARD_CUT_RELATIONS_ONLY,\n                atlas.getName(), atlas.metaData());\n        final Time begin = Time.now();\n\n        final Iterable<Node> nodesWithin = getIntersectingCachingSupplier(atlas,\n                atlas.nodesWithin(boundary), ItemType.NODE).get();\n        final Iterable<Edge> edgesIntersecting = getIntersectingCachingSupplier(atlas,\n                atlas.edgesIntersecting(boundary), ItemType.EDGE).get();\n        final Iterable<Area> areasIntersecting = getIntersectingCachingSupplier(atlas,\n                atlas.areasIntersecting(boundary), ItemType.AREA).get();\n        final Iterable<Line> linesIntersecting = getIntersectingCachingSupplier(atlas,\n                atlas.linesIntersecting(boundary), ItemType.LINE).get();\n        final Iterable<Point> pointsWithin = getIntersectingCachingSupplier(atlas,\n                atlas.pointsWithin(boundary), ItemType.POINT).get();\n\n        // Generate the size estimates, then the builder.\n        // Nodes estimating is a bit tricky. We want to include all the nodes within the polygon,\n        // but we also want to include those attached to edges that span outside the polygon.\n        // Instead of doing a count to have an exact number, we choose here to have an arbitrary 20%\n        // buffer on top of the nodes inside the polygon. This mostly avoids resizing.\n        final long nodeNumber = Math.round(Iterables.size(nodesWithin) * SOFT_CUT_RATIO_BUFFER);\n        final long edgeNumber = Math\n                .round(Iterables.size(edgesIntersecting) * SOFT_CUT_RATIO_BUFFER);\n        final long areaNumber = Math\n                .round(Iterables.size(areasIntersecting) * SOFT_CUT_RATIO_BUFFER);\n        final long lineNumber = Math\n                .round(Iterables.size(linesIntersecting) * SOFT_CUT_RATIO_BUFFER);\n        final long pointNumber = Math.round(Iterables.size(pointsWithin) * SOFT_CUT_RATIO_BUFFER);\n        final long relationNumber = Math\n                .round(Iterables.size(atlas.relationsWithEntitiesIntersecting(boundary))\n                        * SOFT_CUT_RATIO_BUFFER);\n        final AtlasSize sizeEstimates = new AtlasSize(edgeNumber, nodeNumber, areaNumber,\n                lineNumber, pointNumber, relationNumber);\n\n        final PackedAtlasBuilder builder = getPackedAtlasBuilder(atlas, sizeEstimates);\n\n        // Add all the individual nodes and edge start and end nodes coming from the predicate\n        addNodes(nodesWithin, node -> !hasEntity(node, builder), builder);\n        addNodesFromEdges(edgesIntersecting, builder);\n\n        addEdges(edgesIntersecting, edge -> !hasEntity(edge, builder), builder);\n        addAreas(areasIntersecting, builder);\n        addLines(linesIntersecting, builder);\n        addPoints(pointsWithin, builder);\n\n        final Predicate<RelationMember> validMemberTest;\n        if (hardCutRelations)\n        {\n            validMemberTest = member ->\n            {\n                switch (member.getEntity().getType())\n                {\n                    case NODE:\n                    case POINT:\n                        return boundary.fullyGeometricallyEncloses(\n                                ((LocationItem) member.getEntity()).getLocation());\n                    case LINE:\n                    case EDGE:\n                        return boundary.fullyGeometricallyEncloses(\n                                ((LineItem) member.getEntity()).asPolyLine());\n                    case AREA:\n                        return boundary.fullyGeometricallyEncloses(\n                                ((Area) member.getEntity()).asPolygon());\n                    case RELATION:\n                        return hasEntity(member.getEntity(), builder);\n                    default:\n                        return false;\n                }\n            };\n        }\n        else\n        {\n            validMemberTest = member -> hasEntity(member.getEntity(), builder);\n        }\n\n        // Check all relations and filter out either hard-cut (filter out members not entirely\n        // enclosed in the boundaries) or soft-cut (allow any members that intersected or were\n        // within the boundaries)\n        addRelations(atlas, atlas.relationsLowerOrderFirst(),\n                relation -> !hasEntity(relation, builder), validMemberTest, builder,\n                AtlasCutType.SOFT_CUT);\n\n        final PackedAtlas result = (PackedAtlas) builder.get();\n        if (result != null)\n        {\n            result.trim();\n        }\n\n        logger.trace(CUT_STOP_MESSAGE,\n                !hardCutRelations ? AtlasCutType.SOFT_CUT : AtlasCutType.HARD_CUT_RELATIONS_ONLY,\n                atlas.getName(), begin.elapsedSince());\n        return Optional.ofNullable(result);\n    }\n\n    /**\n     * @param matcher\n     *            The matcher to consider\n     * @param atlas\n     *            The {@link Atlas} to cut\n     * @return a sub-atlas from this Atlas.\n     */\n    public static Optional<Atlas> softCut(final Atlas atlas, final Predicate<AtlasEntity> matcher)\n    {\n        logger.trace(CUT_START_MESSAGE, AtlasCutType.SOFT_CUT, atlas.getName(), atlas.metaData());\n        final Time begin = Time.now();\n\n        // Using a predicate here can create wild changes in entity counts. For example a predicate\n        // would include only edges, but all the nodes would have to be pulled in. In that case, we\n        // use the same size as the source Atlas, but we trim it at the end.\n        final PackedAtlasBuilder builder = getPackedAtlasBuilder(atlas, atlas.size());\n\n        // First, add all the nodes contained by relations and all start and end nodes from edges\n        // contained by relations\n        atlas.relations(matcher::test).forEach(relation -> addNodesFromRelation(relation, builder));\n\n        // Next, add all the individual nodes and edge start and end nodes coming from the predicate\n        addNodes(atlas.nodes(matcher::test), node -> !hasEntity(node, builder), builder);\n        addNodesFromEdges(atlas.edges(matcher::test), builder);\n\n        // Next, add the Lines, Points, Areas and Edges. Edges are a little trickier - 1) They rely\n        // on their start/end nodes to have already been added 2) They can potentially pull in nodes\n        // that weren't matched by the given predicate. These two cases are handled above.\n        // Similarly, Relations depend on all other entities to have been added, since they make up\n        // the member list. For this pass, add all entities, except Relations, that match the given\n        // Predicate to the builder.\n        addEdges(atlas.edges(matcher::test), edge -> !hasEntity(edge, builder), builder);\n        addPoints(atlas.points(matcher::test), builder);\n        addAreas(atlas.areas(matcher::test), builder);\n        addLines(atlas.lines(matcher::test), builder);\n\n        // It's now safe to add Relations. There are two caveats: 1. A Relation member may not\n        // have been pulled in by the given predicate. In order to maintain Relation validity, we\n        // need to pull in those members. 2. The member may be a Relation that hasn't been\n        // added yet. We check if any of the members are unadded relations and if so, we add\n        // them.\n        Iterables.filter(atlas.relationsLowerOrderFirst(), matcher::test)\n                .forEach(relation -> addRelationMembers(relation, builder));\n        addRelations(atlas, atlas.relationsLowerOrderFirst(), matcher::test, member -> true,\n                builder, AtlasCutType.SOFT_CUT);\n\n        final PackedAtlas result = (PackedAtlas) builder.get();\n        if (result != null)\n        {\n            result.trim();\n        }\n\n        logger.trace(CUT_STOP_MESSAGE, AtlasCutType.SOFT_CUT, atlas.getName(),\n                begin.elapsedSince());\n        return Optional.ofNullable(result);\n    }\n\n    private static void addAllSubRelations(final Atlas atlas, final Relation parentRelation,\n            final PackedAtlasBuilder builder)\n    {\n        final Set<Long> subrelations = parentRelation.flattenRelations();\n        for (final Relation relation : atlas.relationsLowerOrderFirst())\n        {\n            final Long relationId = relation.getIdentifier();\n            if (subrelations.contains(relationId) && !hasEntity(relation, builder))\n            {\n                builder.addRelation(relationId, relation.getOsmIdentifier(), relation.getBean(),\n                        relation.getTags());\n            }\n            // we shouldn't ever have lower order subrelations after the parent relation!\n            // returning here prevents infintite loops and unecessary computations\n            if (relationId == parentRelation.getIdentifier())\n            {\n                return;\n            }\n        }\n    }\n\n    /**\n     * Adds all {@link Area}s from the {@link Iterable} to the {@link PackedAtlasBuilder}\n     *\n     * @param areas\n     *            An {@link Iterable} of {@link Area}s to add to the {@link PackedAtlasBuilder}\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     */\n    private static void addAreas(final Iterable<Area> areas, final PackedAtlasBuilder builder)\n    {\n        areas.forEach(\n                area -> builder.addArea(area.getIdentifier(), area.asPolygon(), area.getTags()));\n    }\n\n    /**\n     * Adds all {@link Edge}s from the {@link Iterable} that pass the filter to the\n     * {@link PackedAtlasBuilder}. Uses a special consumer to make sure reverse {@link Edge}s get\n     * added as well, for consistency.\n     *\n     * @param edges\n     *            An {@link Iterable} of {@link Edge}s to add to the {@link PackedAtlasBuilder}\n     * @param validEdgeFilter\n     *            A {@link Predicate} to filter out invalid {@link Edge}s\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     */\n    private static void addEdges(final Iterable<Edge> edges, final Predicate<Edge> validEdges,\n            final PackedAtlasBuilder builder)\n    {\n        final Consumer<Edge> edgeAdder = edge ->\n        {\n            // Here, making sure that edge identifiers are not 0 to work around an issue in\n            // unit tests: https://github.com/osmlab/atlas/issues/252\n            if (edge.getIdentifier() != 0 && edge.hasReverseEdge())\n            {\n                final Edge reverse = edge.reversed().get();\n                if (!hasEntity(reverse, builder))\n                {\n                    builder.addEdge(reverse.getIdentifier(), reverse.asPolyLine(),\n                            reverse.getTags());\n                }\n            }\n            builder.addEdge(edge.getIdentifier(), edge.asPolyLine(), edge.getTags());\n        };\n        Iterables.stream(edges).filter(validEdges).forEach(edgeAdder::accept);\n    }\n\n    /**\n     * Adds all {@link Line}s from the {@link Iterable} to the {@link PackedAtlasBuilder}\n     *\n     * @param lines\n     *            An {@link Iterable} of {@link Line}s to add to the {@link PackedAtlasBuilder}\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     */\n    private static void addLines(final Iterable<Line> lines, final PackedAtlasBuilder builder)\n    {\n        lines.forEach(\n                line -> builder.addLine(line.getIdentifier(), line.asPolyLine(), line.getTags()));\n    }\n\n    /**\n     * Adds all {@link Node}s from the {@link Iterable} that pass the filter to the\n     * {@link PackedAtlasBuilder}\n     *\n     * @param nodes\n     *            An {@link Iterable} of {@link Node}s to add to the {@link PackedAtlasBuilder}\n     * @param validNodeFilter\n     *            A {@link Predicate} to filter out invalid {@link Node}s\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     */\n    private static void addNodes(final Iterable<Node> nodes, final Predicate<Node> validNodeFilter,\n            final PackedAtlasBuilder builder)\n    {\n        Iterables.stream(nodes).filter(validNodeFilter).forEach(\n                node -> builder.addNode(node.getIdentifier(), node.getLocation(), node.getTags()));\n    }\n\n    /**\n     * Adds any start or end {@link Node}s from the {@link Iterable} of {@link Edge}s that weren't\n     * already in the {@link PackedAtlasBuilder}\n     *\n     * @param edges\n     *            An {@link Iterable} of {@link Edge}s to add {@link Node}s from to the\n     *            {@link PackedAtlasBuilder}\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     */\n    private static void addNodesFromEdges(final Iterable<Edge> edges,\n            final PackedAtlasBuilder builder)\n    {\n        edges.forEach(edge ->\n        {\n            final Node start = edge.start();\n            final Node end = edge.end();\n\n            if (!hasEntity(start, builder))\n            {\n                builder.addNode(start.getIdentifier(), start.getLocation(), start.getTags());\n            }\n            if (!hasEntity(end, builder))\n            {\n                builder.addNode(end.getIdentifier(), end.getLocation(), end.getTags());\n            }\n        });\n    }\n\n    /**\n     * Uses {@link Relation#flatten()} to get the list of all non-{@link Relation} members\n     * recursively, then adds all {@link Node} members, then finally adds all {@link Node} members\n     * from {@link Edge}s that may not have been members directly\n     *\n     * @param relation\n     *            The {@link Relation} which we're exploring\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     */\n    @SuppressWarnings(\"unchecked\")\n    private static void addNodesFromRelation(final Relation relation,\n            final PackedAtlasBuilder builder)\n    {\n        final Set<AtlasObject> relationMembers = relation.flatten();\n\n        final Set<Edge> edgeMembers = (Set<Edge>) (Set<?>) relationMembers.stream()\n                .filter(member -> member instanceof Edge).collect(Collectors.toSet());\n        final Set<Node> nodeMembers = (Set<Node>) (Set<?>) relationMembers.stream()\n                .filter(member -> member instanceof Node).collect(Collectors.toSet());\n\n        addNodes(nodeMembers, node -> !hasEntity(node, builder), builder);\n        addNodesFromEdges(edgeMembers, builder);\n    }\n\n    /**\n     * Adds all points from the {@link Iterable} to the {@link PackedAtlasBuilder}\n     *\n     * @param points\n     *            An {@link Iterable} of {@link Point}s to add to the {@link PackedAtlasBuilder}\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     */\n    private static void addPoints(final Iterable<Point> points, final PackedAtlasBuilder builder)\n    {\n        points.forEach(point -> builder.addPoint(point.getIdentifier(), point.getLocation(),\n                point.getTags()));\n    }\n\n    private static void addPointsForLines(final Atlas atlas, final Iterable<Line> lines,\n            final PackedAtlasBuilder builder)\n    {\n        lines.forEach(line -> line.getRawGeometry()\n                .forEach(location -> atlas.pointsAt(location).forEach(point ->\n                {\n                    if (!hasEntity(point, builder))\n                    {\n                        builder.addPoint(point.getIdentifier(), point.getLocation(),\n                                point.getTags());\n                    }\n                })));\n    }\n\n    /**\n     * Uses {@link Relation#flatten()} to get the list of all non-{@link Relation} members\n     * recursively, then adds all {@link Edge}, {@link Area}, {@link Line}, and {@link Point}\n     * members\n     *\n     * @param relation\n     *            The {@link Relation} to add members from\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     */\n    @SuppressWarnings(\"unchecked\")\n    private static void addRelationMembers(final Relation relation,\n            final PackedAtlasBuilder builder)\n    {\n        final Set<AtlasObject> relationMembers = relation.flatten();\n        addEdges(\n                (Set<Edge>) (Set<?>) relationMembers.stream()\n                        .filter(member -> member instanceof Edge).collect(Collectors.toSet()),\n                edge -> !hasEntity(edge, builder), builder);\n\n        addAreas((Set<Area>) (Set<?>) relationMembers.stream()\n                .filter(member -> member instanceof Area && !hasEntity((Area) member, builder))\n                .collect(Collectors.toSet()), builder);\n        addLines((Set<Line>) (Set<?>) relationMembers.stream()\n                .filter(member -> member instanceof Line && !hasEntity((Line) member, builder))\n                .collect(Collectors.toSet()), builder);\n        addPoints((Set<Point>) (Set<?>) relationMembers.stream()\n                .filter(member -> member instanceof Point && !hasEntity((Point) member, builder))\n                .collect(Collectors.toSet()), builder);\n\n    }\n\n    /**\n     * Adds all relations from the {@link Iterable} that pass the {@link Relation} filter test and\n     * have at least one member that was added to the sub{@link Atlas}. If the cut type is a\n     * {@link AtlasCutType.SOFT_CUT}, then any {@link Relation} that passed the filter will have all\n     * its members that pass the valid member filter added to the sub{@link Atlas} if they don't\n     * already exist.\n     *\n     * @param relations\n     *            An {@link Iterable} of Relations to add to the builder\n     * @param validRelationTest\n     *            A filter for the {@link Relation}s\n     * @param validMemberTest\n     *            A filter for the members of any valid {@link Relation}s\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     * @param cutType\n     *            The {@link AtlasCutType} representing the kind of cut being performed\n     */\n    private static void addRelations(final Atlas atlas, final Iterable<Relation> relations,\n            final Predicate<Relation> validRelationTest,\n            final Predicate<RelationMember> validMemberTest, final PackedAtlasBuilder builder,\n            final AtlasCutType cutType)\n    {\n        Iterables.stream(relations).filter(validRelationTest).forEach(relation ->\n        {\n            final List<RelationMember> validMembers = relation.members().stream()\n                    .filter(validMemberTest).collect(Collectors.toList());\n            if (!validMembers.isEmpty())\n            {\n                if (AtlasCutType.SOFT_CUT.equals(cutType) || AtlasCutType.SILK_CUT.equals(cutType))\n                {\n                    validMembers.forEach(member ->\n                    {\n                        if (member.getEntity() instanceof Relation\n                                && !hasEntity(member.getEntity(), builder))\n                        {\n                            addAllSubRelations(atlas, (Relation) member.getEntity(), builder);\n                        }\n                    });\n                }\n                final RelationBean structure = new RelationBean();\n                validMembers.forEach(validMember -> structure.addItem(\n                        validMember.getEntity().getIdentifier(), validMember.getRole(),\n                        ItemType.forEntity(validMember.getEntity())));\n                if (relation.asMultiPolygon().isPresent())\n                {\n                    builder.addRelation(relation.getIdentifier(), relation.getOsmIdentifier(),\n                            structure, relation.getTags(), relation.asMultiPolygon().get());\n                }\n                else\n                {\n                    builder.addRelation(relation.getIdentifier(), relation.getOsmIdentifier(),\n                            structure, relation.getTags());\n                }\n            }\n            else\n            {\n                logger.trace(\n                        \"Excluding relation {} from sub-atlas since none of its members pass the {} cut.\",\n                        relation.getIdentifier(), AtlasCutType.HARD_CUT_ALL);\n            }\n        });\n    }\n\n    private static <M extends AtlasEntity> Supplier<Iterable<M>> getContainmentCachingSupplier(\n            final Atlas atlas, final Iterable<M> source, final ItemType type)\n    {\n        final Set<Long> memberIdentifiersWithin = new HashSet<>();\n        // A supplier that will effectively cache all the members contained by that polygon. This is\n        // thread safe because the cache is internal to the supplier's scope.\n        @SuppressWarnings(\"unchecked\")\n        final Supplier<Iterable<M>> result = () ->\n        {\n            if (memberIdentifiersWithin.isEmpty())\n            {\n                // Here using a map instead of forEach to pipe the edge identifiers to the cache\n                // list without having to re-open the iterable.\n                return Iterables.stream(source).map(member ->\n                {\n                    memberIdentifiersWithin.add(member.getIdentifier());\n                    return member;\n                }).collect();\n            }\n            else\n            {\n                return (Iterable<M>) Iterables.stream(memberIdentifiersWithin)\n                        .map(entityIdentifier -> atlas.entity(entityIdentifier, type)).collect();\n            }\n        };\n        return result;\n    }\n\n    private static <M extends AtlasEntity> Supplier<Iterable<M>> getIntersectingCachingSupplier(\n            final Atlas atlas, final Iterable<M> source, final ItemType type)\n    {\n        final Set<Long> memberIdentifiersIntersecting = new HashSet<>();\n        // A supplier that will effectively cache all the members intersecting that polygon. This is\n        // thread safe because the cache is internal to the supplier's scope.\n        @SuppressWarnings(\"unchecked\")\n        final Supplier<Iterable<M>> result = () ->\n        {\n            if (memberIdentifiersIntersecting.isEmpty())\n            {\n                // Here using a map instead of forEach to pipe the edge identifiers to the cache\n                // list without having to re-open the iterable.\n                return Iterables.stream(source).map(member ->\n                {\n                    memberIdentifiersIntersecting.add(member.getIdentifier());\n                    return member;\n                }).collect();\n            }\n            else\n            {\n                return (Iterable<M>) Iterables.stream(memberIdentifiersIntersecting)\n                        .map(entityIdentifier -> atlas.entity(entityIdentifier, type)).collect();\n            }\n        };\n        return result;\n    }\n\n    /**\n     * Instantiates a {@link PackedAtlasBuilder}, used in all sub{@link Atlas} types. Ensures all\n     * sub{@link Atlas} cut types are consistent wrt naming, metadata, etc of returned Atlas\n     *\n     * @param sizeEstimates\n     *            The {@link AtlasSize} to use for the {@link PackedAtlasBuilder} -- this changes\n     *            depending on {@link AtlasCutType}, some will use the base {@link Atlas} size,\n     *            others make their own estimates\n     * @return {@link PackedAtlasBuilder} instantiated with size estimates and name/metadata from\n     *         base Atlas\n     */\n    private static PackedAtlasBuilder getPackedAtlasBuilder(final Atlas atlas,\n            final AtlasSize sizeEstimates)\n    {\n        return new PackedAtlasBuilder().withSizeEstimates(sizeEstimates)\n                .withMetaData(atlas.metaData())\n                .withName(String.format(\"%s%s\", atlas.getName(), SUB_ATLAS_NAME_POSTFIX))\n                .withEnhancedRelationGeometry();\n    }\n\n    /**\n     * Checks the {@link PackedAtlasBuilder} to see if the {@link AtlasEntity} has been added or not\n     *\n     * @param entity\n     *            {@link AtlasEntity} to check existence of\n     * @param builder\n     *            The {@link PackedAtlasBuilder} used to build the sub{@link Atlas}\n     * @return True if an {@link AtlasEntity} with the same {@link ItemType} and identifier exists\n     *         in the {@link PackedAtlasBuilder}, false otherwise\n     */\n    private static boolean hasEntity(final AtlasEntity entity, final PackedAtlasBuilder builder)\n    {\n        switch (entity.getType())\n        {\n            case POINT:\n                return builder.peek().point(entity.getIdentifier()) != null;\n            case NODE:\n                return builder.peek().node(entity.getIdentifier()) != null;\n            case EDGE:\n                return builder.peek().edge(entity.getIdentifier()) != null;\n            case LINE:\n                return builder.peek().line(entity.getIdentifier()) != null;\n            case AREA:\n                return builder.peek().area(entity.getIdentifier()) != null;\n            case RELATION:\n                return builder.peek().relation(entity.getIdentifier()) != null;\n            default:\n                return false;\n        }\n    }\n\n    private SubAtlasCreator()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasEdgeValidator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasEdgeValidator\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasEdgeValidator.class);\n\n    private final Atlas atlas;\n\n    public AtlasEdgeValidator(final Atlas atlas)\n    {\n        this.atlas = atlas;\n    }\n\n    public void validate()\n    {\n        logger.trace(\"Starting Edge validation of Atlas {}\", this.atlas.getName());\n        final Time start = Time.now();\n        validateEdgeToNodeConnectivity();\n        validateEdgeToNodeLocationAccuracy();\n        validateReverseEdgePolyLineUpdated();\n        logger.trace(\"Finished Edge validation of Atlas {} in {}\", this.atlas.getName(),\n                start.elapsedSince());\n    }\n\n    protected void validateEdgeToNodeConnectivity()\n    {\n        for (final Edge edge : this.atlas.edges())\n        {\n            final Node start = edge.start();\n            if (start == null)\n            {\n                throw new CoreException(\n                        \"Edge {} is logically disconnected at its start. Referenced Node does not exist.\",\n                        edge.getIdentifier());\n            }\n            if (start.outEdges().stream()\n                    .noneMatch(edgeAtNode -> edgeAtNode.getIdentifier() == edge.getIdentifier()))\n            {\n                throw new CoreException(\"Edge {} references start Node {}. It is not reciprocal.\",\n                        edge.getIdentifier(), start.getIdentifier());\n            }\n            final Node end = edge.end();\n            if (end == null)\n            {\n                throw new CoreException(\n                        \"Edge {} is logically disconnected at its end. Referenced Node does not exist.\",\n                        edge.getIdentifier());\n            }\n            if (end.inEdges().stream()\n                    .noneMatch(edgeAtNode -> edgeAtNode.getIdentifier() == edge.getIdentifier()))\n            {\n                throw new CoreException(\"Edge {} references end Node {}. It is not reciprocal.\",\n                        edge.getIdentifier(), end.getIdentifier());\n            }\n        }\n    }\n\n    protected void validateEdgeToNodeLocationAccuracy()\n    {\n        for (final Edge edge : this.atlas.edges())\n        {\n            final Location startNodeLocation = edge.start().getLocation();\n            final Location edgeStartLocation = edge.asPolyLine().first();\n            if (!startNodeLocation.equals(edgeStartLocation))\n            {\n                throw new CoreException(\n                        \"Edge {} with start location {} does not match with its start Node {} at location: {}\",\n                        edge.getIdentifier(), edgeStartLocation, edge.start().getIdentifier(),\n                        startNodeLocation);\n            }\n            final Location endNodeLocation = edge.end().getLocation();\n            final Location edgeEndLocation = edge.asPolyLine().last();\n            if (!endNodeLocation.equals(edgeEndLocation))\n            {\n                throw new CoreException(\n                        \"Edge {} with end location {} does not match with its end Node {} at location: {}\",\n                        edge.getIdentifier(), edgeEndLocation, edge.end().getIdentifier(),\n                        endNodeLocation);\n            }\n        }\n    }\n\n    protected void validateReverseEdgePolyLineUpdated()\n    {\n        for (final Edge edge : this.atlas.edges(edge -> edge.hasReverseEdge() && edge.isMainEdge()))\n        {\n            final Edge reversed = edge.reversed().orElseThrow(() -> new CoreException(\n                    \"Edge {} should have a reverse, but does not.\", edge.getIdentifier()));\n            final PolyLine forward = edge.asPolyLine();\n            final PolyLine backward = reversed.asPolyLine();\n            if (!forward.equals(backward.reversed()))\n            {\n                throw new CoreException(\n                        \"Edge {} and its reverse {} have mismatching PolyLines: Forward = {}, Backward = {}\",\n                        edge.getIdentifier(), reversed.getIdentifier(), forward, backward);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasLineItemValidator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasLineItemValidator\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasLineItemValidator.class);\n\n    private final Atlas atlas;\n\n    public AtlasLineItemValidator(final Atlas atlas)\n    {\n        this.atlas = atlas;\n    }\n\n    public void validate()\n    {\n        logger.trace(\"Starting LineItem validation of Atlas {}\", this.atlas.getName());\n        final Time start = Time.now();\n        validatePolyLinePresent();\n        logger.trace(\"Finished LineItem validation of Atlas {} in {}\", this.atlas.getName(),\n                start.elapsedSince());\n    }\n\n    private void validatePolyLinePresent()\n    {\n        for (final LineItem lineItem : this.atlas.lineItems())\n        {\n            if (lineItem.asPolyLine() == null)\n            {\n                throw new CoreException(\"{} {} is missing its PolyLine.\", lineItem.getType(),\n                        lineItem.getIdentifier());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasLocationItemValidator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasLocationItemValidator\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasLocationItemValidator.class);\n\n    private final Atlas atlas;\n\n    public AtlasLocationItemValidator(final Atlas atlas)\n    {\n        this.atlas = atlas;\n    }\n\n    public void validate()\n    {\n        logger.trace(\"Starting LocationItem validation of Atlas {}\", this.atlas.getName());\n        final Time start = Time.now();\n        validateLocationPresent();\n        logger.trace(\"Finished LocationItem validation of Atlas {} in {}\", this.atlas.getName(),\n                start.elapsedSince());\n    }\n\n    protected void validateLocationPresent()\n    {\n        for (final LocationItem locationItem : this.atlas.locationItems())\n        {\n            if (locationItem.getLocation() == null)\n            {\n                throw new CoreException(\"{} {} is missing a Location.\", locationItem.getType(),\n                        locationItem.getIdentifier());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasNodeValidator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasNodeValidator\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasNodeValidator.class);\n\n    private final Atlas atlas;\n\n    public AtlasNodeValidator(final Atlas atlas)\n    {\n        this.atlas = atlas;\n    }\n\n    public void validate()\n    {\n        logger.trace(\"Starting Node validation of Atlas {}\", this.atlas.getName());\n        final Time start = Time.now();\n        validateNodeToEdgeConnectivity();\n        validateNodeToEdgeLocationAccuracy();\n        logger.trace(\"Finished Node validation of Atlas {} in {}\", this.atlas.getName(),\n                start.elapsedSince());\n    }\n\n    protected void validateNodeToEdgeConnectivity()\n    {\n        for (final Node node : this.atlas.nodes())\n        {\n            for (final Edge edge : node.inEdges())\n            {\n                if (edge == null)\n                {\n                    throw new CoreException(\n                            \"Node {} is logically disconnected from some referenced in edge.\",\n                            node.getIdentifier());\n                }\n            }\n            for (final Edge edge : node.outEdges())\n            {\n                if (edge == null)\n                {\n                    throw new CoreException(\"Node {} is logically disconnected from some out edge.\",\n                            node.getIdentifier());\n                }\n            }\n        }\n    }\n\n    protected void validateNodeToEdgeLocationAccuracy()\n    {\n        for (final Node node : this.atlas.nodes())\n        {\n            final Location nodeLocation = node.getLocation();\n            for (final Edge edge : node.outEdges())\n            {\n                final Location edgeStartLocation = edge.asPolyLine().first();\n                if (!nodeLocation.equals(edgeStartLocation))\n                {\n                    throw new CoreException(\n                            \"Node {} at location {} references outEdge {} which starts at a different location {}\",\n                            node.getIdentifier(), nodeLocation, edge.getIdentifier(),\n                            edgeStartLocation);\n                }\n            }\n            for (final Edge edge : node.inEdges())\n            {\n                final Location edgeEndLocation = edge.asPolyLine().last();\n                if (!nodeLocation.equals(edgeEndLocation))\n                {\n                    throw new CoreException(\n                            \"Node {} at location {} references inEdge {} which ends at a different location {}\",\n                            node.getIdentifier(), nodeLocation, edge.getIdentifier(),\n                            edgeEndLocation);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasRelationValidator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasRelationValidator\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasRelationValidator.class);\n\n    private final Atlas atlas;\n\n    public AtlasRelationValidator(final Atlas atlas)\n    {\n        this.atlas = atlas;\n    }\n\n    public void validate()\n    {\n        logger.trace(\"Starting Relation validation of Atlas {}\", this.atlas.getName());\n        final Time start = Time.now();\n        validateRelationMembersPresent();\n        logger.trace(\"Finished Reltaion validation of Atlas {} in {}\", this.atlas.getName(),\n                start.elapsedSince());\n    }\n\n    private void validateRelationMembersPresent()\n    {\n        for (final Relation relation : this.atlas.relations())\n        {\n            for (final RelationMember relationMember : relation.members())\n            {\n                if (relationMember.getEntity() == null\n                        || this.atlas.entity(relationMember.getEntity().getIdentifier(),\n                                relationMember.getEntity().getType()) == null)\n                {\n                    throw new CoreException(\n                            \"Relation {} specifies a member with role \\\"{}\\\" that is not present in the Atlas.\",\n                            relation.getIdentifier(), relationMember.getRole());\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasValidator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasValidator\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasValidator.class);\n\n    private final Atlas atlas;\n\n    public AtlasValidator(final Atlas atlas)\n    {\n        this.atlas = atlas;\n    }\n\n    public void validate()\n    {\n        logger.info(\"Starting validation of Atlas {}\", this.atlas.getName());\n        final Time start = Time.now();\n        logger.trace(\"Starting relation validation of Atlas {}\", this.atlas.getName());\n        final Time startRelations = Time.now();\n        validateRelationsPresentAndLinked();\n        validateGeometricRelations();\n        logger.trace(\"Finished relation validation of Atlas {} in {}\", this.atlas.getName(),\n                startRelations.elapsedSince());\n        logger.trace(\"Starting tags validation of Atlas {}\", this.atlas.getName());\n        final Time startTags = Time.now();\n        validateTagsPresent();\n        logger.trace(\"Finished tags validation of Atlas {} in {}\", this.atlas.getName(),\n                startTags.elapsedSince());\n        new AtlasLocationItemValidator(this.atlas).validate();\n        new AtlasLineItemValidator(this.atlas).validate();\n        new AtlasEdgeValidator(this.atlas).validate();\n        new AtlasNodeValidator(this.atlas).validate();\n        logger.info(\"Finished validation of Atlas {} in {}\", this.atlas.getName(),\n                start.elapsedSince());\n    }\n\n    protected void validateGeometricRelations()\n    {\n        for (final Relation relation : this.atlas.relations())\n        {\n            if (relation instanceof ChangeRelation && relation.isGeometric()\n                    && !((ChangeRelation) relation).preservedValidGeometry())\n            {\n                throw new CoreException(\n                        \"Relation {} had valid source geometry but invalid change geometry!\",\n                        relation.getIdentifier());\n\n            }\n        }\n    }\n\n    protected void validateRelationsPresentAndLinked()\n    {\n        for (final AtlasEntity entity : this.atlas.entities())\n        {\n            for (final Relation relation : entity.relations())\n            {\n                if (relation == null)\n                {\n                    throw new CoreException(\n                            \"Entity {} {} lists some parent relation that is not present: {}\",\n                            entity.getType(), entity.getIdentifier(),\n                            entity.relations().stream()\n                                    .map(parent -> parent == null ? \"null\"\n                                            : String.valueOf(parent.getIdentifier()))\n                                    .collect(Collectors.toSet()));\n                }\n                if (!relation.members().asBean()\n                        .getItemFor(entity.getIdentifier(), entity.getType()).isPresent())\n                {\n                    throw new CoreException(\n                            \"Entity {} {} lists parent relation {} which does not have it as a member.\",\n                            entity.getType(), entity.getIdentifier(), relation.getIdentifier());\n                }\n            }\n        }\n    }\n\n    protected void validateTagsPresent()\n    {\n        for (final AtlasEntity entity : this.atlas.entities())\n        {\n            final Map<String, String> tags = entity.getTags();\n            if (tags == null)\n            {\n                throw new CoreException(\"Entity {} {} is missing tags.\", entity.getType(),\n                        entity.getIdentifier());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/validators/FeatureChangeUsefulnessValidator.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeType;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\n\n/**\n * Validate that a {@link FeatureChange} actually introduces changes. This will throw an exception\n * if the afterView is identical to the beforeView (which is computed from a provided atlas\n * context).\n *\n * @author lcram\n */\npublic class FeatureChangeUsefulnessValidator\n{\n    private final FeatureChange featureChange;\n\n    public FeatureChangeUsefulnessValidator(final FeatureChange featureChange)\n    {\n        this.featureChange = featureChange;\n    }\n\n    public void validate()\n    {\n        validateFeatureChange();\n    }\n\n    private void validateFeatureChange()\n    {\n        final ChangeType changeType = this.featureChange.getChangeType();\n        final AtlasEntity beforeView = this.featureChange.getBeforeView();\n        final AtlasEntity afterView = this.featureChange.getAfterView();\n\n        /*\n         * No need to validate a REMOVE. This will always be useful.\n         */\n        if (changeType == ChangeType.REMOVE)\n        {\n            return;\n        }\n\n        /*\n         * beforeView will be null in the case of an ADD that is creating a brand new feature. In\n         * that case, there is no need to validate since a brand new ADD will always be useful.\n         */\n        if (beforeView == null)\n        {\n            return;\n        }\n\n        if (beforeView.getType() != afterView.getType())\n        {\n            throw new CoreException(\"beforeView type {} did not match afterView type {} in {}\",\n                    beforeView, afterView, this.featureChange.toString());\n        }\n\n        if (beforeView.getTags() != null && afterView.getTags() != null\n                && !beforeView.getTags().equals(afterView.getTags()))\n        {\n            return;\n        }\n\n        if (beforeView.relations() != null && afterView.relations() != null\n                && !beforeView.relations().equals(afterView.relations()))\n        {\n            return;\n        }\n\n        switch (afterView.getType())\n        {\n            case AREA:\n                final Area beforeArea = (Area) beforeView;\n                final Area afterArea = (Area) afterView;\n                if (beforeArea.asPolygon() != null && afterArea.asPolygon() != null\n                        && !beforeArea.asPolygon().equals(afterArea.asPolygon()))\n                {\n                    return;\n                }\n                break;\n            case EDGE:\n                final Edge beforeEdge = (Edge) beforeView;\n                final Edge afterEdge = (Edge) afterView;\n                if (beforeEdge.asPolyLine() != null && afterEdge.asPolyLine() != null\n                        && !beforeEdge.asPolyLine().equals(afterEdge.asPolyLine()))\n                {\n                    return;\n                }\n                if (beforeEdge.start() != null && afterEdge.start() != null\n                        && !beforeEdge.start().equals(afterEdge.start()))\n                {\n                    return;\n                }\n                if (beforeEdge.end() != null && afterEdge.end() != null\n                        && !beforeEdge.end().equals(afterEdge.end()))\n                {\n                    return;\n                }\n                break;\n            case LINE:\n                final Line beforeLine = (Line) beforeView;\n                final Line afterLine = (Line) afterView;\n                if (beforeLine.asPolyLine() != null && afterLine.asPolyLine() != null\n                        && !beforeLine.asPolyLine().equals(afterLine.asPolyLine()))\n                {\n                    return;\n                }\n                break;\n            case NODE:\n                final Node beforeNode = (Node) beforeView;\n                final Node afterNode = (Node) afterView;\n                if (beforeNode.getLocation() != null && afterNode.getLocation() != null\n                        && !beforeNode.getLocation().equals(afterNode.getLocation()))\n                {\n                    return;\n                }\n                if (beforeNode.inEdges() != null && afterNode.inEdges() != null\n                        && !beforeNode.inEdges().equals(afterNode.inEdges()))\n                {\n                    return;\n                }\n                if (beforeNode.outEdges() != null && afterNode.outEdges() != null\n                        && !beforeNode.outEdges().equals(afterNode.outEdges()))\n                {\n                    return;\n                }\n                break;\n            case POINT:\n                final Point beforePoint = (Point) beforeView;\n                final Point afterPoint = (Point) afterView;\n                if (beforePoint.getLocation() != null && afterPoint.getLocation() != null\n                        && !beforePoint.getLocation().equals(afterPoint.getLocation()))\n                {\n                    return;\n                }\n                break;\n            case RELATION:\n                final Relation beforeRelation = (Relation) beforeView;\n                final Relation afterRelation = (Relation) afterView;\n                if (beforeRelation.members() != null && afterRelation.members() != null\n                        && !beforeRelation.members().equals(afterRelation.members()))\n                {\n                    return;\n                }\n                if (beforeRelation.allRelationsWithSameOsmIdentifier() != null\n                        && afterRelation.allRelationsWithSameOsmIdentifier() != null\n                        && !beforeRelation.allRelationsWithSameOsmIdentifier()\n                                .equals(afterRelation.allRelationsWithSameOsmIdentifier()))\n                {\n                    return;\n                }\n                if (beforeRelation.allKnownOsmMembers() != null\n                        && afterRelation.allKnownOsmMembers() != null && !beforeRelation\n                                .allKnownOsmMembers().equals(afterRelation.allKnownOsmMembers()))\n                {\n                    return;\n                }\n                if (beforeRelation.osmRelationIdentifier() != null\n                        && afterRelation.osmRelationIdentifier() != null\n                        && !beforeRelation.osmRelationIdentifier()\n                                .equals(afterRelation.osmRelationIdentifier()))\n                {\n                    return;\n                }\n                break;\n            default:\n                throw new CoreException(\"Unknown ItemType {}\", afterView.getType());\n        }\n\n        /*\n         * If we made it all the way here, then we know this FeatureChange is not useful.\n         */\n        throw new CoreException(\n                \"FeatureChange is not useful: beforeView perfectly matched afterView: {} vs {}\",\n                beforeView, afterView);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/walker/EdgeWalker.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.walker;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * A base class for collecting {@link Edge}s by walking the graph. Graph traversal is done by taking\n * steps along the path defined by the nextCandidates {@link Function} taking and {@link Edge} and\n * returning a stream of more candidate {@link Edge}s to traverse. Candidate {@link Edge}s are\n * filtered by the candidateFilter {@link Predicate} before becoming part of the collected path.\n * Order is maintained by either the order Edges are traversed or by the {@link Edge}\n * {@link Comparator} provided. Use the {@link EdgeHandler} interface to keep track of state gleaned\n * while walking the road network.\n *\n * @author brian_l_davis\n */\npublic abstract class EdgeWalker\n{\n    /**\n     * Implementations of an {@link EdgeHandler} will be called during the collections of\n     * {@link Edge}s walked.\n     */\n    public interface EdgeHandler\n    {\n        /**\n         * Called before {@link EdgeHandler#handleEdge(Edge)} for every {@link Edge} that results in\n         * no more candidate {@link Edge}s\n         *\n         * @param edge\n         *            the current {@link Edge}\n         */\n        default void handleBoundaryEdge(final Edge edge)\n        {\n            // no-op\n        }\n\n        /**\n         * Called for every {@link Edge} collected along the path\n         *\n         * @param edge\n         *            the current {@link Edge}\n         */\n        default void handleEdge(final Edge edge)\n        {\n            // no-op\n        }\n    }\n\n    public static final Comparator<Edge> DEFAULT_TRAVERSAL_ORDER = null;\n    public static final Predicate<Edge> DEFAULT_CANDIDATE_FILTER = edge -> true;\n\n    public static final EdgeHandler DEFAULT_EDGE_HANDLER = new EdgeHandler()\n    {\n    };\n    private final Edge startingEdge;\n    private final Comparator<Edge> edgeOrder;\n    private final Predicate<Edge> candidateFilter;\n    private final Function<Edge, Stream<Edge>> nextCandidates;\n\n    private final EdgeHandler edgeHandler;\n\n    /**\n     * Custom {@link Edge} walker\n     *\n     * @param startingEdge\n     *            {@link Edge} used to start walking the graph\n     * @param edgeOrder\n     *            {@link Comparator} used to order the resulting collection\n     * @param candidateFilter\n     *            {@link Predicate} filter to restrict candidate {@link Edge}s\n     * @param nextCandidates\n     *            {@link Function} supplier provides the next step to walk\n     */\n    protected EdgeWalker(final Edge startingEdge, final Comparator<Edge> edgeOrder,\n            final Predicate<Edge> candidateFilter,\n            final Function<Edge, Stream<Edge>> nextCandidates)\n    {\n        this(startingEdge, edgeOrder, candidateFilter, nextCandidates, DEFAULT_EDGE_HANDLER);\n    }\n\n    /**\n     * Custom {@link Edge} walker with {@link Edge} handling\n     *\n     * @param startingEdge\n     *            {@link Edge} used to start walking the graph\n     * @param edgeOrder\n     *            {@link Comparator} used to order the resulting collection\n     * @param candidateFilter\n     *            {@link Predicate} filter to restrict candidate {@link Edge}s\n     * @param nextCandidates\n     *            {@link Function} supplier provides the next step to walk\n     * @param edgeHandler\n     *            {@link EdgeHandler} handler that will be called during walk\n     */\n    protected EdgeWalker(final Edge startingEdge, final Comparator<Edge> edgeOrder,\n            final Predicate<Edge> candidateFilter,\n            final Function<Edge, Stream<Edge>> nextCandidates, final EdgeHandler edgeHandler)\n    {\n        this.startingEdge = startingEdge;\n        this.edgeOrder = edgeOrder;\n        this.candidateFilter = candidateFilter;\n        this.nextCandidates = nextCandidates;\n        this.edgeHandler = edgeHandler;\n    }\n\n    /**\n     * Basic traversal ordered {@link Edge} walker\n     *\n     * @param startingEdge\n     *            {@link Edge} used to start walking the graph\n     * @param nextCandidates\n     *            {@link Function} supplier provides the next step to walk to walk\n     */\n    protected EdgeWalker(final Edge startingEdge, final Function<Edge, Stream<Edge>> nextCandidates)\n    {\n        this(startingEdge, DEFAULT_TRAVERSAL_ORDER, DEFAULT_CANDIDATE_FILTER, nextCandidates,\n                DEFAULT_EDGE_HANDLER);\n    }\n\n    /**\n     * Basic traversal ordered {@link Edge} walker with {@link Edge} handling\n     *\n     * @param startingEdge\n     *            {@link Edge} used to start walking the graph\n     * @param nextCandidates\n     *            {@link Function} supplier provides the next step to walk\n     * @param edgeHandler\n     *            {@link EdgeHandler} handler that will be called during walk\n     */\n    protected EdgeWalker(final Edge startingEdge, final Function<Edge, Stream<Edge>> nextCandidates,\n            final EdgeHandler edgeHandler)\n    {\n        this(startingEdge, DEFAULT_TRAVERSAL_ORDER, DEFAULT_CANDIDATE_FILTER, nextCandidates,\n                edgeHandler);\n    }\n\n    /**\n     * Filtered, traversal ordered {@link Edge} walker\n     *\n     * @param startingEdge\n     *            {@link Edge} used to start walking the graph\n     * @param candidateFilter\n     *            {@link Predicate} filter to restrict candidate {@link Edge}s\n     * @param nextCandidates\n     *            {@link Function} supplier provides the next step to walk to walk\n     */\n    protected EdgeWalker(final Edge startingEdge, final Predicate<Edge> candidateFilter,\n            final Function<Edge, Stream<Edge>> nextCandidates)\n    {\n        this(startingEdge, DEFAULT_TRAVERSAL_ORDER, candidateFilter, nextCandidates,\n                DEFAULT_EDGE_HANDLER);\n    }\n\n    /**\n     * Filtered, traversal ordered {@link Edge} walker with {@link Edge} handling\n     *\n     * @param startingEdge\n     *            {@link Edge} used to start walking the graph\n     * @param candidateFilter\n     *            {@link Predicate} filter to restrict candidate {@link Edge}s\n     * @param nextCandidates\n     *            {@link Function} supplier provides the next step to walk to walk\n     * @param edgeHandler\n     *            {@link EdgeHandler} handler that will be called during walk\n     */\n    protected EdgeWalker(final Edge startingEdge, final Predicate<Edge> candidateFilter,\n            final Function<Edge, Stream<Edge>> nextCandidates, final EdgeHandler edgeHandler)\n    {\n        this(startingEdge, DEFAULT_TRAVERSAL_ORDER, candidateFilter, nextCandidates, edgeHandler);\n    }\n\n    /**\n     * Gets an Set ({@link TreeSet}) of {@link Edge}s, optionally ordered by the {@link Edge}\n     * {@link Comparator}, that's constructed by iteratively adding Edges returned from the\n     * {@link Edge} supplier {@link Function}, and filtered by the {@link Predicate}.\n     *\n     * @return the set of edges walked\n     */\n    public Set<Edge> collectEdges()\n    {\n        final Set<Edge> edges = this.edgeOrder == null ? new LinkedHashSet<>()\n                : new TreeSet<>(this.edgeOrder);\n        final Predicate<Edge> unvisited = edge -> !edges.contains(edge);\n        Set<Edge> nextBoundaryEdges = Collections.singleton(this.startingEdge);\n        do\n        {\n            edges.addAll(nextBoundaryEdges);\n            nextBoundaryEdges = nextBoundaryEdges.stream().flatMap(edge ->\n            {\n                final Set<Edge> candidates = this.nextCandidates.apply(edge)\n                        .filter(unvisited.and(this.candidateFilter))\n                        .collect(Collectors.toCollection(LinkedHashSet::new));\n                if (candidates.isEmpty())\n                {\n                    this.edgeHandler.handleBoundaryEdge(edge);\n                }\n                if (edge.equals(this.startingEdge))\n                {\n                    // Check if the starting Edge is a boundary\n                    if (!edge.isConnectedAtStartTo(Iterables.asSet(candidates))\n                            || !edge.isConnectedAtEndTo(Iterables.asSet(candidates)))\n                    {\n                        this.edgeHandler.handleBoundaryEdge(edge);\n                    }\n                }\n                this.edgeHandler.handleEdge(edge);\n                return candidates.stream();\n            }).collect(Collectors.toCollection(LinkedHashSet::new));\n        }\n        while (nextBoundaryEdges.size() > 0);\n        return edges;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/walker/OsmWayWalker.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.walker;\n\nimport java.util.Comparator;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.ReverseIdentifierFactory;\n\n/**\n * An {@link EdgeWalker} that builds a Way, represented by a {@link Set} of {@link Edge}s ordered by\n * WaySectionIndex, by walking outward in both directions from the starting {@link Edge}.\n *\n * @author brian_l_davis\n */\npublic class OsmWayWalker extends EdgeWalker\n{\n    /**\n     * Filters in main Edges with the same OSM Identifier\n     *\n     * @author brian_l_davis\n     */\n    private static class MainEdgeByOsmIdentifierFilter implements Predicate<Edge>\n    {\n        private final long osmIdentifier;\n\n        MainEdgeByOsmIdentifierFilter(final long osmIdentifier)\n        {\n            this.osmIdentifier = osmIdentifier;\n        }\n\n        @Override\n        public boolean test(final Edge edge)\n        {\n            return edge.isMainEdge() && edge.getOsmIdentifier() == this.osmIdentifier;\n        }\n    }\n\n    /**\n     * Compares WaySectionIndex of two Edges\n     *\n     * @author brian_l_davis\n     */\n    private static class WaySectionComparator implements Comparator<Edge>\n    {\n        private final ReverseIdentifierFactory reverseIdentifierFactory = new ReverseIdentifierFactory();\n\n        @Override\n        public int compare(final Edge left, final Edge right)\n        {\n            return Long.compare(\n                    this.reverseIdentifierFactory.getWaySectionIndex(left.getIdentifier()),\n                    this.reverseIdentifierFactory.getWaySectionIndex(right.getIdentifier()));\n        }\n    }\n\n    /**\n     * Grows the {@link Edge} path by all edges connected to an {@link Edge}\n     */\n    private static final Function<Edge, Stream<Edge>> CONNECTED_EDGES = edge -> edge\n            .connectedEdges().stream();\n\n    /**\n     * Constructs an {@link EdgeWalker} that collects all the {@link Edge}s that form a complete Way\n     *\n     * @param edge\n     *            any {@link Edge} section of the Way\n     */\n    public OsmWayWalker(final Edge edge)\n    {\n        super(edge.getMainEdge(), new WaySectionComparator(),\n                new MainEdgeByOsmIdentifierFilter(edge.getOsmIdentifier()), CONNECTED_EDGES);\n    }\n\n    /**\n     * Constructs an {@link EdgeWalker} that collects all the {@link Edge}s that form a complete Way\n     *\n     * @param edge\n     *            any {@link Edge} section of the Way\n     * @param edgeHandler\n     *            an EdgeHandler to collect statistics\n     */\n    public OsmWayWalker(final Edge edge, final EdgeHandler edgeHandler)\n    {\n        super(edge.getMainEdge(), new WaySectionComparator(),\n                new MainEdgeByOsmIdentifierFilter(edge.getOsmIdentifier()), CONNECTED_EDGES,\n                edgeHandler);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/atlas/walker/SimpleEdgeWalker.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.walker;\n\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\n\n/**\n * This class provides some constructors for simple {@link EdgeWalker}s. These constructors mirror\n * constructors from the abstract parent class, making them public and instantiable. These walkers\n * get all filtering logic from their parameters, and only return a single {@link java.util.Set} of\n * unsorted {@link Edge}s from the {@link #collectEdges()} method.\n *\n * @author bbreithaupt\n */\npublic class SimpleEdgeWalker extends EdgeWalker\n{\n    /**\n     * An {@link EdgeWalker} where all the filtering is done by gathering the candidates.\n     *\n     * @param startingEdge\n     *            {@link Edge} used to start walking the graph\n     * @param nextCandidates\n     *            {@link Function} supplier provides that gathers the next set of candidates\n     */\n    public SimpleEdgeWalker(final Edge startingEdge,\n            final Function<Edge, Stream<Edge>> nextCandidates)\n    {\n        super(startingEdge, nextCandidates);\n    }\n\n    /**\n     * An {@link EdgeWalker} where all the filtering is done by gathering the candidates.\n     *\n     * @param startingEdge\n     *            {@link Edge} used to start walking the graph\n     * @param candidateFilter\n     *            {@link Predicate} filter to restrict candidate {@link Edge}s\n     * @param nextCandidates\n     *            {@link Function} supplier provides that gathers the next set of candidates\n     */\n    public SimpleEdgeWalker(final Edge startingEdge, final Predicate<Edge> candidateFilter,\n            final Function<Edge, Stream<Edge>> nextCandidates)\n    {\n        super(startingEdge, candidateFilter, nextCandidates);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryBoundaryMap.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.io.Serializable;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.StringTokenizer;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.geotools.data.FileDataStore;\nimport org.geotools.data.FileDataStoreFinder;\nimport org.geotools.feature.FeatureIterator;\nimport org.locationtech.jts.geom.Envelope;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryCollection;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.geom.Polygon;\nimport org.locationtech.jts.geom.prep.PreparedGeometry;\nimport org.locationtech.jts.geom.prep.PreparedGeometryFactory;\nimport org.locationtech.jts.geom.prep.PreparedPolygon;\nimport org.locationtech.jts.index.strtree.STRtree;\nimport org.locationtech.jts.io.WKTReader;\nimport org.locationtech.jts.io.WKTWriter;\nimport org.locationtech.jts.precision.GeometryPrecisionReducer;\nimport org.opengis.feature.Feature;\nimport org.opengis.feature.Property;\nimport org.opengis.feature.simple.SimpleFeature;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.boundaries.ComplexBoundary;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.boundaries.ComplexBoundaryFinder;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.CountryCodeProperties;\nimport org.openstreetmap.atlas.geography.boundary.converters.CountryBoundaryMapGeoJsonConverter;\nimport org.openstreetmap.atlas.geography.boundary.converters.CountryListTwoWayStringConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPrecisionManager;\nimport org.openstreetmap.atlas.geography.geojson.GeoJson;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Node;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Way;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Lists;\nimport com.google.gson.JsonObject;\n\n/**\n * This {@link CountryBoundaryMap} loads boundaries from given country boundary shape file into\n * spatial index, and then supports {@link Node} and {@link Way} queries.\n *\n * @author Tony Ma\n * @author Yiqing Jin\n * @author mgostintsev\n * @author mkalender\n * @author samg\n */\npublic class CountryBoundaryMap implements Serializable, GeoJson\n{\n    // Buffer values for slicing operation. If the remaining piece turns to be smaller than\n    // buffer, we'll just ignore them.\n    public static final double LINE_BUFFER = 0.000001;\n    public static final double AREA_BUFFER = 0.0000000001;\n\n    // Boundary file constants\n    static final String COUNTRY_BOUNDARY_DELIMITER = \"||\";\n    private static final long serialVersionUID = -1714710346834527699L;\n    private static final Logger logger = LoggerFactory.getLogger(CountryBoundaryMap.class);\n    // Old country code field\n    private static final String ISO_COUNTRY = \"ISO_COUNTR\";\n    // New country code field\n    private static final String COUNTRY_CODE = \"cntry_code\";\n    private static final List<String> COUNTRY_CODE_FIELDS = Arrays.asList(ISO_COUNTRY,\n            COUNTRY_CODE);\n\n    // WKT Helpers\n    private static final WKTWriter WKT_WRITER = new WKTWriter();\n\n    private static final String GEOMETRY_FIELD = \"the_geom\";\n    private static final String LIST_SEPARATOR = \"#\";\n    private static final String POLYGON_ID_KEY = \"pid\";\n\n    private static final int DEFAULT_MAXIMUM_POLYGONS_TO_SLICE_WITH = 2000;\n    private static final int EXPANDED_MAXIMUM_POLYGONS_TO_SLICE_WITH = 25000;\n    // Converters\n    private static final JtsPolyLineConverter JTS_POLYLINE_CONVERTER = new JtsPolyLineConverter();\n    private static final JtsMultiPolygonToMultiPolygonConverter JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n    private static final JtsPointConverter JTS_POINT_CONVERTER = new JtsPointConverter();\n\n    // Maps the ISO-3 country code to country boundary.\n    private final MultiMap<String, Polygon> countryNameToBoundaryMap;\n    private transient MultiMap<String, PreparedPolygon> countryNameToPreparedBoundaryPolyonMap;\n\n    private boolean useExpandedPolygonLimit = true;\n    private final transient GeometryPrecisionReducer reducer;\n    private final CountryListTwoWayStringConverter countryListConverter = new CountryListTwoWayStringConverter();\n\n    private transient STRtree spatialIndex;\n\n    /**\n     * @param countryGeometries\n     *            A list of {@link Geometry}s to check\n     * @return The set of country codes represented\n     */\n    public static Set<String> countryCodesIn(final Collection<?> countryGeometries)\n    {\n        return countryGeometries.stream().map(geometry ->\n        {\n            if (geometry instanceof Geometry)\n            {\n                return getGeometryProperty((Geometry) geometry, ISOCountryTag.KEY);\n            }\n            else if (geometry instanceof PreparedGeometry)\n            {\n                return getGeometryProperty(((PreparedGeometry) geometry).getGeometry(),\n                        ISOCountryTag.KEY);\n            }\n            return StringUtils.EMPTY;\n        }).filter(StringUtils::isNotEmpty).collect(Collectors.toSet());\n    }\n\n    /**\n     * @param atlas\n     *            {@link Atlas} to read boundaries\n     * @return {@link CountryBoundaryMap} created from {@link Atlas}\n     */\n    public static CountryBoundaryMap fromAtlas(final Atlas atlas)\n    {\n        final CountryBoundaryMap map = new CountryBoundaryMap();\n        map.readFromAtlas(atlas);\n        return map;\n    }\n\n    /**\n     * @param boundaries\n     *            A {@link Map} from country names to country boundaries in {@link MultiPolygon}\n     *            format\n     * @return {@link CountryBoundaryMap} created from existing boundaries\n     */\n    public static CountryBoundaryMap fromBoundaryMap(final Map<String, List<Polygon>> boundaries)\n    {\n        final CountryBoundaryMap map = new CountryBoundaryMap(Rectangle.MAXIMUM);\n        boundaries.forEach(\n                (name, polygons) -> polygons.forEach(polygon -> map.addCountry(name, polygon)));\n        return map;\n\n    }\n\n    /**\n     * @param resource\n     *            Text {@link Resource} to read boundaries\n     * @return {@link CountryBoundaryMap} created from {@link Resource}\n     */\n    public static CountryBoundaryMap fromPlainText(final Resource resource)\n    {\n        final CountryBoundaryMap map = new CountryBoundaryMap();\n        map.readFromPlainText(resource);\n        return map;\n    }\n\n    /**\n     * @param file\n     *            Shape {@link File} to read boundaries\n     * @return {@link CountryBoundaryMap} created from {@link File}\n     */\n    public static CountryBoundaryMap fromShapeFile(final File file)\n    {\n        final CountryBoundaryMap map = new CountryBoundaryMap();\n        map.readFromShapeFile(file);\n        return map;\n    }\n\n    public static Stream<Geometry> geometries(final GeometryCollection collection)\n    {\n        return IntStream.range(0, collection.getNumGeometries())\n                .mapToObj(index -> collection.getGeometryN(index));\n    }\n\n    /**\n     * Follows the same concept as {@link #setGeometryProperty(Geometry, String, String)}. Because\n     * we're working with JTS {@link Polygon}s instead of {@link AtlasEntity}s, we don't have access\n     * to a tag map and can't explicitly set tags. This wraps the {@link Polygon#getUserData} call\n     * and is the single entry point that should be used for setting {@link Geometry} properties.\n     *\n     * @param geometry\n     *            The {@link Geometry} whose userData we're interested in\n     * @return The property map for the geometry, {@code null} if it doesn't exist\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static Map<String, String> getGeometryProperties(final Geometry geometry)\n    {\n        final Map<String, String> result = new HashMap<>();\n        // Grab the existing key/value map from the object\n        final Map<String, String> propertyMap = (Map<String, String>) geometry.getUserData();\n\n        if (propertyMap != null)\n        {\n            result.putAll(propertyMap);\n        }\n        return result;\n    }\n\n    /**\n     * Follows the same concept as {@link #setGeometryProperty(Geometry, String, String)}. Because\n     * we're working with JTS {@link Polygon}s instead of {@link AtlasEntity}s, we don't have access\n     * to a tag map and can't explicitly set tags. This wraps the {@link Polygon#getUserData} call\n     * and is the single entry point that should be used for setting {@link Geometry} properties.\n     *\n     * @param geometry\n     *            The {@link Geometry} whose userData we're interested in\n     * @param key\n     *            The metadata lookup key to use\n     * @return the string value of the given key, {@code null} if it doesn't exist\n     */\n    public static String getGeometryProperty(final Geometry geometry, final String key)\n    {\n        return getGeometryProperties(geometry).get(key);\n    }\n\n    /**\n     * @param countryGeometries\n     *            A list of {@link Geometry}s to check\n     * @return {@code true} if all given {@link Geometry}s belong to the same country\n     */\n    public static boolean isSameCountry(final Collection<?> countryGeometries)\n    {\n        return numberCountries(countryGeometries) == 1;\n    }\n\n    /**\n     * @param countryGeometries\n     *            A list of {@link Geometry}s to check\n     * @return The number of distinct countries represented\n     */\n    public static long numberCountries(final Collection<?> countryGeometries)\n    {\n        if (countryGeometries.isEmpty())\n        {\n            return 0;\n        }\n\n        if (countryGeometries.size() == 1)\n        {\n            return 1;\n        }\n        return countryCodesIn(countryGeometries).size();\n    }\n\n    /**\n     * Because we're working with JTS {@link Polygon}s instead of {@link AtlasEntity}s, we don't\n     * have access to a tag map and can't explicitly set tags. Instead, we use the\n     * {@link Polygon#setUserData} call to store a {@link Map} of properties that we derived during\n     * country slicing and country code assignment. This wraps the {@link Polygon#setUserData} call\n     * and is the single entry point that should be used for setting {@link Geometry} properties.\n     *\n     * @param geometry\n     *            The {@link Geometry} for which to save the key/value pair\n     * @param key\n     *            The key to store for this {@link Geometry}\n     * @param value\n     *            The value to store for the {@link Geometry}\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static void setGeometryProperty(final Geometry geometry, final String key,\n            final String value)\n    {\n        // Grab the existing key/value map from the object\n        final Map<String, String> propertyMap = (Map<String, String>) geometry.getUserData();\n\n        if (propertyMap == null)\n        {\n            // No user data exists - create one and store the data.\n            final Map<String, String> newPropertyMap = new HashMap<>();\n            newPropertyMap.put(key, value);\n            geometry.setUserData(newPropertyMap);\n        }\n        else\n        {\n            final String existingValue = propertyMap.get(key);\n\n            // Check for key existence\n            if (existingValue == null)\n            {\n                // New key/value pair, store and update\n                propertyMap.put(key, value);\n                geometry.setUserData(propertyMap);\n            }\n            else\n            {\n                // Trying to override an existing value - this shouldn't happen!\n                if (!Objects.equals(existingValue, value) && logger.isDebugEnabled())\n                {\n                    logger.debug(\n                            \"Trying to override existing '{}' key's value of '{}' with '{}' for geometry with values {}\",\n                            key, existingValue, value, propertyMap);\n                }\n            }\n        }\n    }\n\n    /**\n     * Default constructor\n     */\n    public CountryBoundaryMap()\n    {\n        this(Rectangle.MAXIMUM);\n    }\n\n    /**\n     * Constructor with limited bounds\n     *\n     * @param bounds\n     *            {@link Rectangle} bounds for boundary map calculation\n     */\n    public CountryBoundaryMap(final Rectangle bounds)\n    {\n        this.countryNameToBoundaryMap = new MultiMap<>();\n        this.countryNameToPreparedBoundaryPolyonMap = new MultiMap<>();\n        this.reducer = new GeometryPrecisionReducer(JtsPrecisionManager.getPrecisionModel());\n        this.reducer.setPointwise(true);\n        this.reducer.setChangePrecisionModel(true);\n        this.spatialIndex = new STRtree();\n    }\n\n    public void addCountry(final String country, final Polygon polygon)\n    {\n        this.countryNameToBoundaryMap.add(country, polygon);\n        final PreparedPolygon prepared = (PreparedPolygon) PreparedGeometryFactory.prepare(polygon);\n        this.countryNameToPreparedBoundaryPolyonMap.add(country, prepared);\n        setGeometryProperty(prepared.getGeometry(), ISOCountryTag.KEY, country);\n        setGeometryProperty(prepared.getGeometry(), POLYGON_ID_KEY, \"0\");\n        this.spatialIndex.insert(prepared.getGeometry().getEnvelopeInternal(), prepared);\n    }\n\n    public void addCountryWithoutPolygonIdKey(final String country, final Polygon polygon)\n    {\n        final PreparedPolygon prepared = (PreparedPolygon) PreparedGeometryFactory.prepare(polygon);\n        this.countryNameToBoundaryMap.add(country, polygon);\n        this.countryNameToPreparedBoundaryPolyonMap.add(country, prepared);\n        setGeometryProperty(prepared.getGeometry(), ISOCountryTag.KEY, country);\n        this.spatialIndex.insert(prepared.getGeometry().getEnvelopeInternal(), prepared);\n    }\n\n    /**\n     * @return {@link List} of all country names\n     */\n    public List<String> allCountryNames()\n    {\n        final List<String> countries = new ArrayList<>();\n        countries.addAll(this.countryNameToBoundaryMap.keySet());\n        return countries;\n    }\n\n    /**\n     * Get a GeoJSON representation of this {@link CountryBoundaryMap}.\n     *\n     * @return a GeoJSON representation of this {@link CountryBoundaryMap}\n     */\n    @Override\n    public JsonObject asGeoJson()\n    {\n        return new CountryBoundaryMapGeoJsonConverter().prettyPrint(false).usePolygons(false)\n                .convert(this);\n    }\n\n    /**\n     * Query country boundaries which cover given {@link Location}\n     *\n     * @param location\n     *            Any {@link Location}\n     * @return a MultiMap of Polygons per country boundary\n     */\n    public MultiMap<String, Polygon> boundaries(final Location location)\n    {\n        final Point jtsPoint = JTS_POINT_CONVERTER.convert(location);\n        return this.boundariesHelper(() -> this.query(jtsPoint.getEnvelopeInternal()),\n                preparedGeom -> preparedGeom.covers(jtsPoint));\n    }\n\n    /**\n     * Query country boundaries which cover given {@link Location}, with an extension square box\n     *\n     * @param location\n     *            Any {@link Location}\n     * @param extension\n     *            Extension {@link Distance}\n     * @return a MultiMap of Polygons per country boundary\n     */\n    public MultiMap<String, Polygon> boundaries(final Location location, final Distance extension)\n    {\n        return this.boundaries(location.boxAround(extension));\n    }\n\n    /**\n     * Query country boundaries which cover/partially cover given {@link PolyLine}\n     *\n     * @param polyLine\n     *            Any {@link PolyLine} or {@link Polygon}\n     * @return a MultiMap of Polygons per country boundary\n     */\n    public MultiMap<String, Polygon> boundaries(final PolyLine polyLine)\n    {\n        final LineString jtsLine = JTS_POLYLINE_CONVERTER.convert(polyLine);\n        return this.boundariesHelper(() -> this.query(jtsLine.getEnvelopeInternal()),\n                preparedGeom -> preparedGeom.intersects(jtsLine));\n    }\n\n    /**\n     * Query country boundaries, which intersect the given bound\n     *\n     * @param bound\n     *            Bounding box\n     * @return a MultiMap of Polygons per country boundary\n     */\n    public MultiMap<String, Polygon> boundaries(final Rectangle bound)\n    {\n        final GeometryFactory factory = new GeometryFactory();\n        return this.boundariesHelper(() -> this.query(bound.asEnvelope()),\n                polygon -> polygon.intersects(factory.toGeometry(bound.asEnvelope())));\n    }\n\n    /**\n     * Return a list of country boundaries.\n     *\n     * @param countryName\n     *            Country name in iso3\n     * @return a MultiMap of Polygons per country boundary\n     */\n    public List<Polygon> countryBoundary(final String countryName)\n    {\n        final List<PreparedPolygon> geometries = this.countryNameToPreparedBoundaryPolyonMap\n                .get(countryName);\n        if (geometries == null || geometries.isEmpty())\n        {\n            return null;\n        }\n\n        final List<Polygon> boundaries = new ArrayList<>();\n        for (final PreparedGeometry prep : geometries)\n        {\n            boundaries.add((Polygon) prep.getGeometry());\n        }\n        return boundaries;\n    }\n\n    /**\n     * Query country names which intersect the given bound\n     *\n     * @param bound\n     *            Bounding box\n     * @return A {@link StringList} of country names\n     */\n    public StringList countryCodesOverlappingWith(final Rectangle bound)\n    {\n        return new StringList(query(bound.asEnvelope()).stream()\n                .map(polygon -> getGeometryProperty(polygon.getGeometry(), ISOCountryTag.KEY))\n                .collect(Collectors.toList()));\n    }\n\n    /**\n     * Allows you to increase/decrease the number of polygons a way can be sliced with.\n     *\n     * @param value\n     *            {@code true} to expand the slice limit, {@code false} to keep it at the default\n     *            limit\n     */\n    public void expandPolygonSliceLimit(final boolean value)\n    {\n        this.useExpandedPolygonLimit = value;\n    }\n\n    /**\n     * This defaults to slow mode, which tries to match all countries.\n     *\n     * @param geometry\n     *            A JTS {@link Geometry}\n     * @return the resulting {@link CountryCodeProperties}\n     */\n    public CountryCodeProperties getCountryCodeISO3(final Geometry geometry)\n    {\n        return this.getCountryCodeISO3(geometry, 0.0);\n    }\n\n    /**\n     * @param geometry\n     *            The {@link Geometry} which we're assigning a country code to\n     * @param buffer\n     *            The buffer distance of geometry, please note that if the geometry is 0 dimension\n     *            then a envelope expend is used instead of a rounding buffer\n     * @return the resulting {@link CountryCodeProperties}\n     */\n    public CountryCodeProperties getCountryCodeISO3(final Geometry geometry, final double buffer)\n    {\n        final StringList countryList = new StringList();\n        final Geometry target;\n\n        if (buffer > 0.0)\n        {\n            if (geometry.getDimension() == 0)\n            {\n                final Envelope envelope = geometry.getEnvelopeInternal();\n                envelope.expandBy(buffer);\n                target = geometry.getFactory().toGeometry(envelope);\n            }\n            else\n            {\n                target = geometry.buffer(buffer);\n            }\n        }\n        else\n        {\n            target = geometry;\n        }\n\n        final List<PreparedPolygon> polygons = this.query(target);\n        if (polygons.size() == 1 || isSameCountry(polygons))\n        {\n            countryList.add(getGeometryProperty(polygons.get(0).getGeometry(), ISOCountryTag.KEY));\n        }\n        else\n        {\n            countryList.addAll(polygons.stream()\n                    .map(polygon -> getGeometryProperty(polygon.getGeometry(), ISOCountryTag.KEY))\n                    .collect(Collectors.toList()));\n        }\n\n        return new CountryCodeProperties(this.countryListConverter.backwardConvert(countryList));\n    }\n\n    /**\n     * @param location\n     *            The {@link Location} to check\n     * @return the resulting {@link CountryCodeProperties}\n     * @see #getCountryCodeISO3(Geometry)\n     */\n    public CountryCodeProperties getCountryCodeISO3(final Location location)\n    {\n        return this.getCountryCodeISO3(JTS_POINT_CONVERTER.convert(location));\n    }\n\n    public MultiMap<String, PreparedPolygon> getCountryNameToBoundaryMap()\n    {\n        return this.countryNameToPreparedBoundaryPolyonMap;\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.FEATURE_COLLECTION;\n    }\n\n    /**\n     * @return all the countries represented by this {@link CountryBoundaryMap}\n     */\n    public Set<String> getLoadedCountries()\n    {\n        return this.countryNameToPreparedBoundaryPolyonMap.keySet();\n    }\n\n    public int getPolygonSliceLimit()\n    {\n        if (this.useExpandedPolygonLimit)\n        {\n            return EXPANDED_MAXIMUM_POLYGONS_TO_SLICE_WITH;\n        }\n        else\n        {\n            return DEFAULT_MAXIMUM_POLYGONS_TO_SLICE_WITH;\n        }\n    }\n\n    public List<PreparedPolygon> query(final Envelope envelope)\n    {\n        return this.query(JtsPrecisionManager.getGeometryFactory().toGeometry(envelope));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<PreparedPolygon> query(final Geometry geometry)\n    {\n        final Geometry target;\n        if (geometry.getDimension() == 0)\n        {\n            final Envelope expanded = geometry.getEnvelopeInternal();\n            expanded.expandBy(LINE_BUFFER);\n            target = JtsPrecisionManager.getGeometryFactory().toGeometry(expanded);\n        }\n        else\n        {\n            target = geometry;\n        }\n        final List<PreparedPolygon> result = new ArrayList<>();\n        this.spatialIndex.query(target.getEnvelopeInternal()).forEach(boundaryPolygon ->\n        {\n            final PreparedPolygon boundary = (PreparedPolygon) boundaryPolygon;\n            if (boundary.intersects(target))\n            {\n                result.add(boundary);\n            }\n        });\n        return result;\n    }\n\n    /**\n     * Read a {@link CountryBoundaryMap} from the {@link ComplexBoundary}(ies) inside an\n     * {@link Atlas}\n     *\n     * @param atlas\n     *            The {@link Atlas} to read from.\n     */\n    public void readFromAtlas(final Atlas atlas)\n    {\n        final Iterable<ComplexBoundary> deduplicatedComplexBoundaries = resolveOverlappingBorders(\n                new ComplexBoundaryFinder().find(atlas));\n        for (final ComplexBoundary complexBoundary : deduplicatedComplexBoundaries)\n        {\n            if (complexBoundary.hasCountryCode())\n            {\n                final List<String> countryCodes = new ArrayList<>();\n                try\n                {\n                    final MultiPolygon outline = complexBoundary.getOutline();\n                    final org.locationtech.jts.geom.MultiPolygon multiPolygon = JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER\n                            .backwardConvert(outline);\n                    for (int i = 0; i < multiPolygon.getNumGeometries(); i++)\n                    {\n                        final Polygon polygon = (Polygon) multiPolygon.getGeometryN(i);\n                        complexBoundary.getCountries().forEach(isoCountry -> this\n                                .addCountry(isoCountry.getIso3CountryCode(), polygon));\n                    }\n                }\n                catch (final IllegalArgumentException e)\n                {\n                    throw new CoreException(\"Unable to read country boundary for country codes {}\",\n                            countryCodes, e);\n                }\n            }\n        }\n        this.spatialIndex.build();\n    }\n\n    /**\n     * Read a {@link CountryBoundaryMap} from a {@link Resource} in plain text format\n     *\n     * @param resource\n     *            {@link Resource} containing {@link CountryBoundaryMap} in plain text format\n     */\n    public void readFromPlainText(final Resource resource)\n    {\n        final Map<String, Integer> countryIdentifierMap = new HashMap<>();\n        final WKTReader reader = new WKTReader(JtsPrecisionManager.getGeometryFactory());\n\n        if (resource == null)\n        {\n            throw new CoreException(\"Boundary map resource is null and could not be read.\");\n        }\n\n        for (final String line : resource.lines())\n        {\n            // Ignore empty lines\n            if (line.isEmpty())\n            {\n                continue;\n            }\n\n            // Read the country boundaries\n            final StringTokenizer boundaryTokenizer = new StringTokenizer(line,\n                    COUNTRY_BOUNDARY_DELIMITER);\n            final String country = boundaryTokenizer.nextToken();\n            final String geometryString = boundaryTokenizer.nextToken();\n            try\n            {\n                final StringTokenizer geometryTokenizer = new StringTokenizer(geometryString,\n                        LIST_SEPARATOR);\n                while (geometryTokenizer.hasMoreTokens())\n                {\n                    final String polygonString = geometryTokenizer.nextToken();\n                    final Geometry geometry = reader.read(polygonString);\n                    setGeometryProperty(geometry, ISOCountryTag.KEY, country);\n                    final Integer identifier = countryIdentifierMap.get(country);\n                    if (identifier == null)\n                    {\n                        countryIdentifierMap.put(country, 0);\n                        setGeometryProperty(geometry, POLYGON_ID_KEY, String.valueOf(0));\n                    }\n                    else\n                    {\n                        countryIdentifierMap.put(country, identifier + 1);\n                        setGeometryProperty(geometry, POLYGON_ID_KEY,\n                                String.valueOf(identifier + 1));\n                    }\n                    this.addCountryWithoutPolygonIdKey(country, (Polygon) geometry);\n                }\n            }\n            catch (final Exception e)\n            {\n                logger.error(\n                        \"Error while reading malformed file. Please ensure all lines are Polygons, and there is no GridIndex attached!\");\n                throw new CoreException(\"Invalid country boundary text file format.\", e);\n            }\n        }\n        this.spatialIndex.build();\n    }\n\n    /**\n     * Read a {@link CountryBoundaryMap} from a shape {@link File}\n     *\n     * @param file\n     *            Shape {@link File}\n     */\n    public void readFromShapeFile(final File file)\n    {\n        FileDataStore store = null;\n        FeatureIterator<SimpleFeature> iterator = null;\n        try\n        {\n            store = FileDataStoreFinder.getDataStore(file);\n            iterator = store.getFeatureSource().getFeatures().features();\n            while (iterator.hasNext())\n            {\n                final Feature feature = iterator.next();\n                final Optional<Property> name = findCountryName(feature, COUNTRY_CODE_FIELDS);\n                final Property geometry = feature.getProperty(GEOMETRY_FIELD);\n                final String nameValue = (String) name.orElseThrow(() -> new CoreException(\n                        \"Can't read country code attribute from shape file\")).getValue();\n                final org.locationtech.jts.geom.MultiPolygon multiPolygon = (org.locationtech.jts.geom.MultiPolygon) geometry\n                        .getValue();\n                for (int i = 0; i < multiPolygon.getNumGeometries(); i++)\n                {\n                    this.addCountry(nameValue, (Polygon) multiPolygon.getGeometryN(i));\n                }\n            }\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Error reading country boundary from file\", e);\n        }\n        finally\n        {\n            if (iterator != null)\n            {\n                iterator.close();\n            }\n            if (store != null)\n            {\n                store.dispose();\n            }\n        }\n        this.spatialIndex.build();\n    }\n\n    /**\n     * Filters the given iterable of {@link ComplexBoundary}s to only allow a single country to\n     * claim an outer boundary polygon.\n     *\n     * @param complexBoundaries\n     *            The {@link ComplexBoundary}s to filter duplicate polygons from\n     * @return An iterable of {@link ComplexBoundary} with no duplicate outer polygons\n     */\n    public Iterable<ComplexBoundary> resolveOverlappingBorders(\n            final Iterable<ComplexBoundary> complexBoundaries)\n    {\n        final List<ComplexBoundary> deduplicatedComplexBoundaries = new ArrayList<>();\n        final Set<org.openstreetmap.atlas.geography.Polygon> processedOuters = new HashSet<>();\n        final List<ComplexBoundary> complexBoundaryList = Lists.newArrayList(complexBoundaries);\n        Collections.sort(complexBoundaryList, (firstBoundary, secondBoundary) ->\n        {\n            if (firstBoundary.getIdentifier() > secondBoundary.getIdentifier())\n            {\n                return 1;\n            }\n            else if (secondBoundary.getIdentifier() > firstBoundary.getIdentifier())\n            {\n                return -1;\n            }\n            return 0;\n        });\n        for (final ComplexBoundary currentComplexBoundary : complexBoundaryList)\n        {\n            final MultiPolygon outline = currentComplexBoundary.getOutline();\n            if (outline != null)\n            {\n                for (final org.openstreetmap.atlas.geography.Polygon outer : outline.outers())\n                {\n                    if (processedOuters.contains(outer))\n                    {\n                        currentComplexBoundary.removeOuter(outer);\n                    }\n                    // ensures that the outer is only claimed once it has been assigned to a country\n                    else if (Lists.newArrayList(currentComplexBoundary.getCountries()).size() > 0)\n                    {\n                        processedOuters.add(outer);\n                    }\n                }\n                deduplicatedComplexBoundaries.add(currentComplexBoundary);\n            }\n        }\n        return deduplicatedComplexBoundaries;\n    }\n\n    /**\n     * @return the number of countries represented by this {@link CountryBoundaryMap}\n     */\n    public int size()\n    {\n        return this.countryNameToBoundaryMap.size();\n    }\n\n    /**\n     * <pre>\n     * Write country boundary map into a text file using WKT format.\n     * Output will have the format below, where each country will be on a new line:\n     *\n     * [ISO-Country-code]||[Country boundary Multi-Polygon]#\n     * </pre>\n     *\n     * @param resource\n     *            The output {@link WritableResource}\n     * @throws IOException\n     *             {@link IOException}\n     */\n    public void writeToFile(final WritableResource resource) throws IOException\n    {\n        try (BufferedWriter output = new BufferedWriter(\n                new OutputStreamWriter(resource.write(), StandardCharsets.UTF_8)))\n        {\n            // Write country boundaries\n            this.writeCountryBoundaries(output);\n        }\n    }\n\n    /**\n     * Given a {@link Supplier}, retrieves {@link List} of {@link Polygon}s and applies given\n     * {@link Predicate}, and returns results as MultiMap\n     *\n     * @param supplier\n     *            {@link Supplier} providing {@link List} of {@link Polygon}s\n     * @param filter\n     *            {@link Polygon} {@link Predicate} to be used as filter\n     * @return a MultiMap of Polygons per country boundary\n     */\n    private MultiMap<String, Polygon> boundariesHelper(\n            final Supplier<List<PreparedPolygon>> supplier, final Predicate<PreparedPolygon> filter)\n    {\n        final MultiMap<String, Polygon> map = new MultiMap<>();\n        final List<PreparedPolygon> geometry = supplier.get();\n        geometry.stream().filter(filter).forEach(polygon ->\n        {\n            final String countryCode = getGeometryProperty(polygon.getGeometry(),\n                    ISOCountryTag.KEY);\n            map.add(countryCode, (Polygon) polygon.getGeometry());\n        });\n        return map;\n    }\n\n    private Optional<Property> findCountryName(final Feature feature,\n            final List<String> alternateNames)\n    {\n        final List<String> lowerCaseAlternateNames = alternateNames.stream()\n                .map(String::toLowerCase).collect(Collectors.toList());\n        return feature.getProperties().stream().filter(property -> lowerCaseAlternateNames\n                .contains(property.getName().getURI().toLowerCase())).findFirst();\n    }\n\n    private void readObject(final java.io.ObjectInputStream inFile)\n            throws IOException, ClassNotFoundException\n    {\n        inFile.defaultReadObject();\n        if (this.countryNameToPreparedBoundaryPolyonMap == null)\n        {\n            this.countryNameToPreparedBoundaryPolyonMap = new MultiMap<>();\n        }\n        if (this.spatialIndex == null)\n        {\n            this.spatialIndex = new STRtree();\n        }\n        this.countryNameToBoundaryMap.entrySet()\n                .forEach(entry -> entry.getValue().forEach(polygon ->\n                {\n                    final PreparedPolygon prepared = (PreparedPolygon) PreparedGeometryFactory\n                            .prepare(polygon);\n                    this.countryNameToPreparedBoundaryPolyonMap.add(entry.getKey(), prepared);\n                    this.spatialIndex.insert(prepared.getGeometry().getEnvelopeInternal(),\n                            prepared);\n                }));\n        this.spatialIndex.build();\n    }\n\n    private void writeCountryBoundaries(final BufferedWriter output) throws IOException\n    {\n        logger.info(\"Writing country boundaries to output\");\n        this.countryNameToPreparedBoundaryPolyonMap.forEach((country, polygons) ->\n\n        polygons.forEach(polygon ->\n        {\n            try\n            {\n                output.write(country);\n                output.write(COUNTRY_BOUNDARY_DELIMITER);\n                output.write(WKT_WRITER.write(polygon.getGeometry()));\n                output.write(LIST_SEPARATOR);\n                output.write(System.lineSeparator());\n            }\n            catch (final IOException e)\n            {\n                throw new CoreException(\"Failed to write country boundaries.\", e);\n            }\n        }));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryBoundaryMapArchiver.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.io.IOException;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.command.subcommands.CountryBoundaryMapPrinterCommand;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n * @author Yiqing Jin\n * @author james-gage\n */\npublic class CountryBoundaryMapArchiver extends Command\n{\n    // Inputs\n    protected static final Switch<File> SHAPE_FILE = new Switch<>(\"shp\", \"path to the shape file\",\n            File::new, Optionality.OPTIONAL);\n    protected static final Switch<Atlas> ATLAS = new Switch<>(\"atlas\",\n            \"path to the atlas file containing boundaries\",\n            path -> PackedAtlas.load(new File(path)), Optionality.OPTIONAL);\n    protected static final Switch<File> BOUNDARY_FILE = new Switch<>(\"boundaries\",\n            \"path to the pre-existing boundary file\", File::new, Optionality.OPTIONAL);\n    // Outputs\n    protected static final Switch<File> OUTPUT = new Switch<>(\"out\", \"The output file format\",\n            File::new, Optionality.REQUIRED);\n    // Options\n    protected static final Switch<Rectangle> BOUNDS = new Switch<>(\"bounds\", \"The bounds\",\n            Rectangle::forString, Optionality.OPTIONAL, Rectangle.MAXIMUM.toCompactString());\n    protected static final Switch<Boolean> CREATE_SPATIAL_INDEX = new Switch<>(\"createSpatialIndex\",\n            \"Indicator whether to create a spatial grid index and include that in the output.\",\n            Boolean::parseBoolean, Optionality.OPTIONAL, Boolean.FALSE.toString());\n    protected static final Switch<Integer> OCEAN_BOUNDARY_ZOOM_LEVEL = new Switch<>(\n            \"oceanBoundaryZoomLevel\",\n            \"The zoom level at which to create ocean tiles to fill in potential voids. Recommended value: 3\",\n            Integer::parseInt, Optionality.OPTIONAL);\n    protected static final Switch<Boolean> SAVE_GEOJSON_WKT = new Switch<>(\"saveGeojsonWkt\",\n            \"Save the country boundaries to Geojson and WKT\", Boolean::parseBoolean,\n            Optionality.OPTIONAL, Boolean.FALSE.toString());\n    private static final Logger logger = LoggerFactory.getLogger(CountryBoundaryMapArchiver.class);\n\n    public static void main(final String[] args)\n    {\n        new CountryBoundaryMapArchiver().run(args);\n    }\n\n    /**\n     * @param resource\n     *            The {@link Resource} to read the {@link CountryBoundaryMap} from\n     * @return the created {@link CountryBoundaryMap}\n     */\n    public CountryBoundaryMap read(final Resource resource)\n    {\n        return CountryBoundaryMap.fromPlainText(resource);\n    }\n\n    protected CountryBoundaryMap generateOceanBoundaryMap(final CountryBoundaryMap boundaryMap,\n            final Iterable<SlippyTile> allTiles)\n    {\n        final CountryBoundaryMap finalBoundaryMap = new CountryBoundaryMap();\n        int oceanCountryCount = 0;\n        // add all ocean boundaries to the new boundary map\n        logger.info(\"Calculating ocean boundaries...\");\n        for (final SlippyTile tile : allTiles)\n        {\n            final Time start = Time.now();\n            final String countryCode = String.format(\"O%02d\", oceanCountryCount);\n            final Geometry countryGeometry = geometryForShard(tile.bounds(), boundaryMap);\n            if (!countryGeometry.isEmpty())\n            {\n                if (countryGeometry instanceof Polygon)\n                {\n                    finalBoundaryMap.addCountry(countryCode, (Polygon) countryGeometry);\n                }\n                if (countryGeometry instanceof MultiPolygon)\n                {\n                    for (int i = 0; i < countryGeometry.getNumGeometries(); i++)\n                    {\n                        finalBoundaryMap.addCountry(countryCode,\n                                (Polygon) countryGeometry.getGeometryN(i));\n                    }\n                }\n                logger.info(\"Added Ocean Country {} in {}\", countryCode, start.elapsedSince());\n                oceanCountryCount++;\n            }\n            else\n            {\n                logger.info(\"Skipped Ocean Country {} in {}. It is land covered.\", tile.getName(),\n                        start.elapsedSince());\n            }\n        }\n\n        // add all countries from the input boundary map to the new boundary map\n        logger.info(\"Adding back country boundaries to the new ocean boundary map\");\n        for (final String country : boundaryMap.allCountryNames())\n        {\n            for (final Polygon countryBoundary : boundaryMap.countryBoundary(country))\n            {\n                finalBoundaryMap.addCountry(country, countryBoundary);\n            }\n        }\n        return finalBoundaryMap;\n    }\n\n    protected Geometry geometryForShard(final Rectangle shardBounds,\n            final CountryBoundaryMap boundaryMap)\n    {\n        final JtsPolygonConverter polyConverter = new JtsPolygonConverter();\n        // jts version of the initial shard bounds\n        org.locationtech.jts.geom.Geometry shardPolyJts = polyConverter.convert(shardBounds);\n        // remove country boundaries from the ocean tile one by one\n        for (final Polygon boundary : boundaryMap.boundaries(shardBounds).allValues())\n        {\n            shardPolyJts = shardPolyJts.difference(boundary);\n        }\n        return shardPolyJts;\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        // Read inputs\n        final File shapeFile = (File) command.get(SHAPE_FILE);\n        final Atlas atlas = (Atlas) command.get(ATLAS);\n        final File boundaries = (File) command.get(BOUNDARY_FILE);\n        final File output = (File) command.get(OUTPUT);\n        output.setCompressor(Compressor.GZIP);\n        final Rectangle bounds = (Rectangle) command.get(BOUNDS);\n        final Integer oceanBoundaryZoomLevel = (Integer) command.get(OCEAN_BOUNDARY_ZOOM_LEVEL);\n        final boolean saveGeojsonWkt = (Boolean) command.get(SAVE_GEOJSON_WKT);\n\n        // Create boundary map\n        final Time timer = Time.now();\n        CountryBoundaryMap map = new CountryBoundaryMap(bounds);\n        if (atlas != null)\n        {\n            map.readFromAtlas(atlas);\n        }\n        else if (shapeFile != null)\n        {\n            map.readFromShapeFile(shapeFile.getFile());\n        }\n        else if (boundaries != null)\n        {\n            map.readFromPlainText(boundaries);\n        }\n        else\n        {\n            throw new CoreException(\"No input data was specified to build a Country Boundary Map\");\n        }\n\n        // Add oceans\n        if (oceanBoundaryZoomLevel != null)\n        {\n            final Iterable<SlippyTile> allTiles = SlippyTile.allTiles(oceanBoundaryZoomLevel);\n            map = generateOceanBoundaryMap(map, allTiles);\n        }\n\n        // Save\n        try\n        {\n            logger.info(\"Saving CountryBoundaryMap to {}.\", output);\n            map.writeToFile(output);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Could not write CountryBoundaryMap.\", e);\n        }\n\n        // Use printer for Geojson and WKT\n        if (saveGeojsonWkt)\n        {\n            new CountryBoundaryMapPrinterCommand().runSubcommand(\n                    String.join(\"\", \"--country-boundary=\", output.getAbsolutePathString()));\n        }\n\n        logger.info(\"CountryBoundaryMap creation took {}.\", timer.elapsedSince());\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(SHAPE_FILE, ATLAS, BOUNDARY_FILE, OUTPUT, BOUNDS,\n                CREATE_SPATIAL_INDEX, OCEAN_BOUNDARY_ZOOM_LEVEL, SAVE_GEOJSON_WKT);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryBoundaryMapCompareCommand.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Compares given {@link CountryBoundaryMap}s to find if there are any differences between them.\n *\n * @author mkalender\n */\npublic class CountryBoundaryMapCompareCommand extends Command\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(CountryBoundaryMapCompareCommand.class);\n\n    // Parameters\n    private static final Switch<CountryBoundaryMap> BASELINE_MAP = new Switch<>(\"baseline\",\n            \"Path to file generated by CountryBoundaryMap to use as a baseline\",\n            path -> CountryBoundaryMap.fromPlainText(new File(path)), Optionality.REQUIRED);\n    private static final Switch<CountryBoundaryMap> OTHER_MAP = new Switch<>(\"new\",\n            \"Path to file generated by CountryBoundaryMap to compare against baseline\",\n            path -> CountryBoundaryMap.fromPlainText(new File(path)), Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new CountryBoundaryMapCompareCommand().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        // Read inputs\n        final CountryBoundaryMap baselineMap = (CountryBoundaryMap) command.get(BASELINE_MAP);\n        final CountryBoundaryMap newMap = (CountryBoundaryMap) command.get(OTHER_MAP);\n\n        // Compare loaded countries\n        final Set<String> baselineCountries = baselineMap.getLoadedCountries();\n        final Set<String> newCountries = newMap.getLoadedCountries();\n        baselineCountries.forEach(country -> Assert.assertTrue(\n                String.format(\"New map is missing country %s.\", country),\n                newCountries.contains(country)));\n        newCountries.forEach(country -> Assert.assertTrue(\n                String.format(\"New map has additional country %s.\", country),\n                baselineCountries.contains(country)));\n        Assert.assertEquals(\"Map sizes are not equal.\", baselineMap.size(), newMap.size());\n\n        // Compare country boundaries\n        baselineMap.getLoadedCountries().forEach(country -> baselineMap.countryBoundary(country)\n                .forEach(countryBoundary -> Assert.assertTrue(\n                        String.format(\"New map is missing a boundary for %s.\", country),\n                        newMap.countryBoundary(country).contains(countryBoundary.getBoundary()))));\n\n        newMap.getLoadedCountries()\n                .forEach(country -> newMap.countryBoundary(country)\n                        .forEach(countryBoundary -> Assert.assertTrue(\n                                String.format(\"New map is missing a boundary for %s.\", country),\n                                baselineMap.countryBoundary(country)\n                                        .contains(countryBoundary.getBoundary()))));\n        logger.info(\"Compared given maps and found no difference.\");\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(BASELINE_MAP, OTHER_MAP);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryBoundaryMapFiller.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.clipping.Clip;\nimport org.openstreetmap.atlas.geography.clipping.Clip.ClipType;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Take a country boundary map and fill it with a Polygon with all the spaces that do not have any\n * country assigned\n *\n * @author matthieun\n */\npublic class CountryBoundaryMapFiller extends Command\n{\n    public static final Switch<File> INPUT = new Switch<>(\"input\", \"The input boundary file\",\n            File::new, Optionality.REQUIRED);\n    public static final Switch<File> OUTPUT = new Switch<>(\"output\", \"The output boundary file\",\n            File::new, Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new CountryBoundaryMapFiller().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final File input = (File) command.get(INPUT);\n        final File output = (File) command.get(OUTPUT);\n        final CountryBoundaryMap map = CountryBoundaryMap.fromPlainText(input);\n        final MultiMap<Polygon, Polygon> outerToInners = new MultiMap<>();\n        final MultiMap<String, org.locationtech.jts.geom.Polygon> resultMap = new MultiMap<>();\n        for (final String countryName : map.allCountryNames())\n        {\n            for (final org.locationtech.jts.geom.Polygon boundary : map\n                    .countryBoundary(countryName))\n            {\n                resultMap.add(countryName, boundary);\n                final MultiPolygon multiPolygon = new JtsPolygonToMultiPolygonConverter()\n                        .convert(boundary);\n                for (final Polygon outer : multiPolygon.outers())\n                {\n                    outerToInners.put(outer, new ArrayList<>());\n                    for (final Polygon inner : multiPolygon.innersOf(outer))\n                    {\n                        outerToInners.add(outer, inner);\n                    }\n                }\n            }\n        }\n        final MultiPolygon allCountries = new MultiPolygon(outerToInners);\n        final Clip clip = MultiPolygon.forPolygon(Rectangle.MAXIMUM).clip(allCountries,\n                ClipType.NOT);\n        final MultiPolygon result = clip.getClipMultiPolygon();\n        final List<org.locationtech.jts.geom.Polygon> results = new ArrayList<>();\n        results.addAll(new JtsMultiPolygonConverter().convert(result));\n        resultMap.put(ISOCountryTag.COUNTRY_MISSING, results);\n        try\n        {\n            CountryBoundaryMap.fromBoundaryMap(resultMap).writeToFile(output);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Could not write new boundaries file to {}\", output, e);\n        }\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(INPUT, OUTPUT);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryCodeGenerator.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Generate complete country code list and excluded list based on the given boundary file and\n * country code list\n *\n * @author tony\n */\npublic class CountryCodeGenerator extends Command\n{\n    private static final String SEPARATOR = \",\";\n    private static final Switch<java.io.File> BOUNDARY = new Switch<>(\"boundary\",\n            \"The country boundary file\", path -> new java.io.File(path), Optionality.REQUIRED);\n    private static final Switch<Set<String>> COUNTRIES_TO_EXCLUDE = new Switch<>(\n            \"countriesToExclude\", \"The comma separated country name list to exclude\",\n            countries -> new HashSet<>(Arrays.asList(countries.split(SEPARATOR))),\n            Optionality.OPTIONAL);\n\n    public static void main(final String[] args)\n    {\n        new CountryCodeGenerator().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final java.io.File boundaryFile = (java.io.File) command.get(BOUNDARY);\n        final CountryBoundaryMap boundaryMap = CountryBoundaryMap.fromShapeFile(boundaryFile);\n        final StringList allCountries = boundaryMap.countryCodesOverlappingWith(Rectangle.MAXIMUM);\n        System.out.println(\"The number of all countries: \" + allCountries.size());\n        System.out.println(allCountries.join(SEPARATOR));\n\n        @SuppressWarnings(\"unchecked\")\n        final Set<String> countriesToExclude = (Set<String>) command.get(COUNTRIES_TO_EXCLUDE);\n        if (countriesToExclude != null)\n        {\n            final StringList filtered = new StringList();\n            for (final String country : allCountries)\n            {\n                if (!countriesToExclude.contains(country))\n                {\n                    filtered.add(country);\n                }\n            }\n            System.out.println(\"The number of all filtered countries: \" + filtered.size());\n            System.out.println(filtered.join(SEPARATOR));\n        }\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(BOUNDARY, COUNTRIES_TO_EXCLUDE);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryShardListing.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.stream.IntStream;\n\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.utilities.maps.MultiMapWithSet;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Utility class that transforms a {@link CountryBoundaryMap}, {@link Sharding} and returns all the\n * {@link Shard}s that are covered by each country in a provided list.\n *\n * @author mkalender\n * @author matthieun\n */\npublic final class CountryShardListing\n{\n    private static final Logger logger = LoggerFactory.getLogger(CountryShardListing.class);\n\n    public static MultiMapWithSet<String, Shard> countryToShardList(\n            final Iterable<String> countries, final CountryBoundaryMap boundaries,\n            final Sharding sharding)\n    {\n        // Extract country boundaries and queue them\n        final BlockingQueue<Polygon> queue = new LinkedBlockingQueue<>();\n        final MultiMapWithSet<String, Shard> countryToShardMap = new MultiMapWithSet<>();\n        countries.forEach(country ->\n        {\n            // Initialize country-shard map\n            countryToShardMap.put(country, new HashSet<>());\n\n            // Fetch boundaries\n            final List<Polygon> countryBoundaries = boundaries.countryBoundary(country);\n            if (countryBoundaries == null)\n            {\n                logger.error(\"No boundaries found for {}!\", country);\n                return;\n            }\n\n            // Queue boundary for processing\n            countryBoundaries.forEach(queue::add);\n        });\n\n        // Use all available processors except one (used by main thread)\n        final int threadCount = Runtime.getRuntime().availableProcessors() - 1;\n        logger.info(\"Generating tasks with {} processors (threads).\", threadCount);\n\n        // Start the execution pool to generate tasks\n        try (Pool processPool = new Pool(threadCount, \"CountryShardListing\"))\n        {\n            // Generate processors\n            IntStream.range(0, threadCount)\n                    .forEach(index -> processPool.queue(new CountryShardListingProcessor(queue,\n                            sharding, boundaries, countryToShardMap)));\n        }\n        return countryToShardMap;\n    }\n\n    private CountryShardListing()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryShardListingProcessor.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.BlockingQueue;\n\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.utilities.maps.MultiMapWithSet;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Worker that acts like both producer and consumer. Takes a Polygon from work queue and processes\n * it.\n *\n * @author mkalender\n */\npublic class CountryShardListingProcessor implements Runnable\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(CountryShardListingProcessor.class);\n    private final BlockingQueue<Polygon> queue;\n    private final Sharding sharding;\n    private final CountryBoundaryMap boundaryMap;\n    private final MultiMapWithSet<String, Shard> countryToShardMap;\n\n    /**\n     * Default constructor.\n     *\n     * @param queue\n     *            {@link BlockingQueue} holding {@link CountryBoundary}s to process\n     * @param sharding\n     *            {@link Sharding} strategy to extract {@link Shard}s\n     * @param boundaryMap\n     *            {@link CountryBoundaryMap} to eliminate false positives\n     * @param countryToShardMap\n     *            {@link Map} of country names to {@link Set} of {@link Shard}s\n     */\n    CountryShardListingProcessor(final BlockingQueue<Polygon> queue, final Sharding sharding,\n            final CountryBoundaryMap boundaryMap,\n            final MultiMapWithSet<String, Shard> countryToShardMap)\n    {\n        this.queue = queue;\n        this.sharding = sharding;\n        this.boundaryMap = boundaryMap;\n        this.countryToShardMap = countryToShardMap;\n    }\n\n    @Override\n    public void run()\n    {\n        try\n        {\n            while (!this.queue.isEmpty())\n            {\n                final Polygon itemToProcess = this.queue.poll();\n\n                // Queue might have been emptied by another thread. That gives an null item.\n                // Check for null item to avoid an exception.\n                if (itemToProcess != null)\n                {\n                    this.process(itemToProcess);\n                }\n            }\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Processor failed to process.\", e);\n        }\n    }\n\n    private void process(final Polygon item)\n    {\n        final JtsPolygonConverter converter = new JtsPolygonConverter();\n        final String country = CountryBoundaryMap.getGeometryProperty(item, ISOCountryTag.KEY);\n        this.sharding.shards(converter.backwardConvert(item).bounds()).forEach(shard ->\n        {\n\n            final Rectangle shardBounds = shard.bounds();\n            if (this.boundaryMap.boundaries(shardBounds).containsKey(country))\n            {\n                final Set<Shard> shards = this.countryToShardMap.get(country);\n                synchronized (shards)\n                {\n                    shards.add(shard);\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryToShardListCache.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.converters.StringToShardConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.writers.SafeBufferedWriter;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Class that stores country and intersecting shards in a {@link MultiMap}, and can be constructed\n * from scratch or from file. Allows saving this mapping to file, which is useful when sharding is\n * dense or borders are complex.\n *\n * @author james-gage\n */\npublic final class CountryToShardListCache extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(CountryToShardListCache.class);\n    private static final Switch<CountryBoundaryMap> BOUNDARIES = new Switch<>(\"boundaries\",\n            \"The country boundaries.\", value -> initializeCountryBoundaryMap(value),\n            Optionality.REQUIRED);\n    private static final Switch<StringList> COUNTRIES = new Switch<>(\"countries\",\n            \"CSV list of the country iso3 codes\", value -> StringList.split(value, \",\"),\n            Optionality.OPTIONAL);\n    private static final Switch<Sharding> SHARDING = new Switch<>(\"sharding\",\n            \"File containing the sharding definition (Works with files only)\", Sharding::forString,\n            Optionality.REQUIRED);\n    private static final Switch<File> OUTPUT = new Switch<>(\"output\", \"The output file\", File::new,\n            Optionality.REQUIRED);\n    private static final String DELIMITER = \"||\";\n    private static final StringToShardConverter CONVERTER = new StringToShardConverter();\n    private final MultiMap<String, Shard> countryToShards = new MultiMap<>();\n\n    public static void main(final String[] args)\n    {\n        new CountryToShardListCache().run(args);\n    }\n\n    private static CountryBoundaryMap initializeCountryBoundaryMap(final String value)\n    {\n        final Time start = Time.now();\n        logger.info(\"Loading boundaries\");\n        final CountryBoundaryMap result = CountryBoundaryMap.fromPlainText(new File(value));\n        logger.info(\"Loaded boundaries in {}\", start.elapsedSince());\n        return result;\n    }\n\n    public CountryToShardListCache(final CountryBoundaryMap boundaries, final Sharding sharding)\n    {\n        this(boundaries, new StringList(boundaries.allCountryNames()), sharding);\n    }\n\n    public CountryToShardListCache(final CountryBoundaryMap boundaries, final StringList countries,\n            final Sharding sharding)\n    {\n        CountryShardListing.countryToShardList(countries, boundaries, sharding)\n                .forEach((country, shardSet) ->\n                {\n                    shardSet.forEach(shard -> this.countryToShards.add(country,\n                            CONVERTER.convert(shard.getName())));\n                });\n    }\n\n    public CountryToShardListCache(final Resource resource)\n    {\n        try\n        {\n            resource.lines().forEach(line ->\n            {\n                final String[] countryAndShardList = line.split(Pattern.quote(DELIMITER));\n                final String country = countryAndShardList[0];\n                final String shardList = countryAndShardList[1];\n                Arrays.asList(shardList.split(\"\\\\s*,\\\\s*\")).stream().map(CONVERTER::convert)\n                        .forEach(shard -> this.countryToShards.add(country, shard));\n            });\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Error while reading CountryToShardListCache resource\", e);\n        }\n    }\n\n    private CountryToShardListCache()\n    {\n\n    }\n\n    /**\n     * Takes a country code, and returns a List of all {@link SlippyTile}s that cover the country\n     * boundary. If an invalid country code is passed, an empty list is returned.\n     *\n     * @param country\n     *            The three digit country code\n     * @return A List of {@link SlippyTile}s\n     */\n    public List<Shard> getShardsForCountry(final String country)\n    {\n        return this.countryToShards.getOrDefault(country, Collections.emptyList());\n    }\n\n    /**\n     * Writes to a {@link WritableResource} the CountryShardListCache. The resulting resource can be\n     * used to initialize another CountryShardListCache.\n     *\n     * @param output\n     *            The {@link WritableResource} where the CountryToShardListCache will be written.\n     */\n    public void save(final WritableResource output)\n    {\n        try (SafeBufferedWriter writer = output.writer())\n        {\n            this.countryToShards.forEach((country, shardList) ->\n            {\n                final List<String> shardNames = shardList.stream()\n                        .map(slippyTile -> slippyTile.getName()).collect(Collectors.toList());\n                writer.writeLine(String.format(\"%s%s%s\", country, DELIMITER, shardNames)\n                        .replace(\"[\", \"\").replace(\"]\", \"\"));\n            });\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Error while writing CountryToShardListCache to file!\", e);\n        }\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final StringList countries = (StringList) command.get(COUNTRIES);\n        final CountryBoundaryMap boundaries = (CountryBoundaryMap) command.get(BOUNDARIES);\n        final File output = (File) command.get(OUTPUT);\n        final Sharding sharding = (Sharding) command.get(SHARDING);\n        CountryToShardListCache cache = null;\n        if (countries != null)\n        {\n            cache = new CountryToShardListCache(boundaries, countries, sharding);\n        }\n        else\n        {\n            cache = new CountryToShardListCache(boundaries, sharding);\n        }\n        logger.info(\"Saving file to {}\", output);\n        cache.save(output);\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(COUNTRIES, BOUNDARIES, OUTPUT, SHARDING);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/CountryToShardListing.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.sharding.CountryShard;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.writers.SafeBufferedWriter;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * List all the {@link Shard}s for each country, wrapped in a command to print the result to a\n * Resource.\n *\n * @author matthieun\n */\npublic class CountryToShardListing extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(CountryToShardListing.class);\n\n    private static final Switch<CountryBoundaryMap> BOUNDARIES = new Switch<>(\"boundaries\",\n            \"The country boundaries.\", value ->\n            {\n                final Time start = Time.now();\n                logger.info(\"Loading boundaries\");\n                final CountryBoundaryMap result = CountryBoundaryMap.fromPlainText(new File(value));\n                logger.info(\"Loaded boundaries in {}\", start.elapsedSince());\n                return result;\n            }, Optionality.REQUIRED);\n    private static final Switch<StringList> COUNTRIES = new Switch<>(\"countries\",\n            \"CSV list of the country iso3 codes\", value -> StringList.split(value, \",\"),\n            Optionality.REQUIRED);\n    private static final Switch<Sharding> SHARDING = new Switch<>(\"sharding\",\n            \"File containing the sharding definition (Works with files only)\", Sharding::forString,\n            Optionality.REQUIRED);\n    private static final Switch<File> OUTPUT = new Switch<>(\"output\", \"The output file\", File::new,\n            Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new CountryToShardListing().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Time overall = Time.now();\n        final StringList countries = (StringList) command.get(COUNTRIES);\n        logger.info(\"Listing for countries: {}\", countries);\n        final CountryBoundaryMap boundaries = (CountryBoundaryMap) command.get(BOUNDARIES);\n        final File output = (File) command.get(OUTPUT);\n        final Sharding sharding = (Sharding) command.get(SHARDING);\n        try (SafeBufferedWriter writer = output.writer())\n        {\n            CountryShardListing.countryToShardList(countries, boundaries, sharding)\n                    .forEach((country, shardSet) -> shardSet.forEach(\n                            shard -> writer.writeLine(new CountryShard(country, shard).getName())));\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not match shards to boundaries\", e);\n        }\n        logger.info(\"Finished in {}\", overall.elapsedSince());\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(BOUNDARIES, COUNTRIES, SHARDING, OUTPUT);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/README.md",
    "content": "# Boundary\n\nThis package contains all the classes that help with defining administrative boundaries, and associating them with three digit country codes.\n\n## Grid Index\n\nThe grid index is an optimization for efficiently identifying which country a feature belongs to. The basic concept is to first construct a Quad Tree. The quad tree is a spatial index, which allows you to query if something is contained in a 2D rectangle. In our case, we have two ways of building the Quad Tree - see `DynamicGridIndexBuilder` and `FixedGridIndexBuilder` section below. Usually, we go with the `DynamicGridIndexBuilder` approach and build the quad tree where each cell in the tree will only contain features from a single country. Because of this property, the border between countries will have many small cells, whereas the inner portion of a country will have less cells and they will typically be larger rectangles. This principle generally holds true, unless the country is really small or has many boundary enclaves. Once the Quad Tree is constructed, the individual Quad Tree cells are stored into a R-Tree. The R-Tree is a spatial index used to query two-dimensional spatial data (the rectangles making up our Quad Tree). This gives us the advantage of quickly, O(log N), looking up the exact rectangle a feature is contained/intersects and identifying which country/region it belongs to. Both the R-Tree and Quad Tree are backed by the JTS implementation.\n\n## Implementation Details\n\n### `CountryBoundaryMap`\n\nLoads boundaries from an input country boundary shape file/text file/resource into a spatial index. Supports clipping, slicing and `Node` and `Way` queries.\n\n### `CountryBoundaryMapArchiver`\n\nCreates the country boundary text file given some shape file and/or boundary.\n\n### `DynamicGridIndexBuilder` and `FixedGridIndexBuilder`\n\nBuilders that create a spatial index using the JTS R-tree. The fixed builder will use an equally divided grid index, whereas the dynamic one will use a 2D K-D Tree to generate variable size cells. Smaller cells will be generated at the boundaries to improve search performance and larger cells will be found near the center of a country, where there is less chance to overlap with a neighboring country.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/converters/CountryBoundaryMapGeoJsonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.boundary.converters;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.locationtech.jts.geom.Polygon;\nimport org.locationtech.jts.geom.prep.PreparedPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\n/**\n * @author lcram\n */\npublic class CountryBoundaryMapGeoJsonConverter implements Converter<CountryBoundaryMap, JsonObject>\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(CountryBoundaryMapGeoJsonConverter.class);\n\n    private boolean usePolygons;\n    private boolean prettyPrint;\n    private Set<String> countryAllowList;\n    private Set<String> countryDenyList;\n\n    public CountryBoundaryMapGeoJsonConverter()\n    {\n        this.usePolygons = false;\n        this.prettyPrint = false;\n        this.countryAllowList = null;\n        this.countryDenyList = null;\n    }\n\n    @Override\n    public JsonObject convert(final CountryBoundaryMap map)\n    {\n        final MultiMap<String, PreparedPolygon> countryNameToBoundaryMap = map\n                .getCountryNameToBoundaryMap();\n        final JsonObject featureCollectionObject = new JsonObject();\n        featureCollectionObject.addProperty(\"type\", \"FeatureCollection\");\n        final JsonArray features = new JsonArray();\n        for (final Map.Entry<String, List<PreparedPolygon>> entry : countryNameToBoundaryMap\n                .entrySet())\n        {\n            final String countryCode = entry.getKey();\n            final List<PreparedPolygon> polygons = entry.getValue();\n            if (this.countryDenyList != null && this.countryDenyList.contains(countryCode)\n                    || this.countryAllowList != null\n                            && !this.countryAllowList.contains(countryCode))\n            {\n                continue;\n            }\n            polygons.forEach(polygon ->\n            {\n                final JsonObject featureObject = new JsonObject();\n                featureObject.addProperty(\"type\", \"Feature\");\n                if (this.usePolygons)\n                {\n                    final org.openstreetmap.atlas.geography.Polygon atlasPolygon = new JtsPolygonConverter()\n                            .backwardConvert((Polygon) polygon.getGeometry());\n                    featureObject.add(\"geometry\", atlasPolygon.asGeoJsonGeometry());\n                }\n                else\n                {\n                    final org.openstreetmap.atlas.geography.Polygon atlasPolygon = new JtsPolygonConverter()\n                            .backwardConvert((Polygon) polygon.getGeometry());\n                    featureObject.add(\"geometry\",\n                            new PolyLine(atlasPolygon.closedLoop()).asGeoJsonGeometry());\n                }\n                final JsonObject propertiesObject = new JsonObject();\n                propertiesObject.addProperty(\"iso_country_code\", countryCode);\n                featureObject.add(\"properties\", propertiesObject);\n                features.add(featureObject);\n            });\n            logger.trace(\"Finished processing polygons for {}\", countryCode);\n        }\n        featureCollectionObject.add(\"features\", features);\n\n        return featureCollectionObject;\n    }\n\n    /**\n     * Convert a {@link CountryBoundaryMap} directly to a GeoJSON string. This method will respect\n     * the 'prettyPrint' parameter.\n     *\n     * @param map\n     *            the {@link CountryBoundaryMap}\n     * @return the GeoJSON string form of the {@link CountryBoundaryMap}\n     */\n    public String convertToString(final CountryBoundaryMap map)\n    {\n        if (this.prettyPrint)\n        {\n            return new GsonBuilder().setPrettyPrinting().create().toJson(this.convert(map));\n        }\n        return this.convert(map).toString();\n    }\n\n    /**\n     * Specify if the GeoJSON should be pretty printed. Otherwise, it will all be on a single line.\n     * This parameter only affects the\n     * {@link CountryBoundaryMapGeoJsonConverter#convertToString(CountryBoundaryMap)} method.\n     *\n     * @param prettyPrint\n     *            pretty print the GeoJSON\n     * @return a modified instance of {@link CountryBoundaryMapGeoJsonConverter}\n     */\n    public CountryBoundaryMapGeoJsonConverter prettyPrint(final boolean prettyPrint)\n    {\n        this.prettyPrint = prettyPrint;\n        return this;\n    }\n\n    /**\n     * Use polygons instead of linestrings in the GeoJSON representation of the\n     * {@link CountryBoundaryMap}. This may be useful if the GeoJSON is being used in visualization\n     * software.\n     *\n     * @param usePolygons\n     *            use polygons\n     * @return a modified instance of {@link CountryBoundaryMapGeoJsonConverter}\n     */\n    public CountryBoundaryMapGeoJsonConverter usePolygons(final boolean usePolygons)\n    {\n        this.usePolygons = usePolygons;\n        return this;\n    }\n\n    /**\n     * Specify an allowlist for countries to include. If this set is empty, then no countries will\n     * be included.\n     *\n     * @param countryAllowList\n     *            the allowlist\n     * @return a modified instance of {@link CountryBoundaryMapGeoJsonConverter}\n     */\n    public CountryBoundaryMapGeoJsonConverter withCountryAllowList(\n            final Set<String> countryAllowList)\n    {\n        this.countryAllowList = countryAllowList;\n        return this;\n    }\n\n    /**\n     * Specify a denylist for countries to exclude. If this set is empty, then no countries will be\n     * excluded.\n     *\n     * @param countryDenyList\n     *            the denylist\n     * @return a modified instance of {@link CountryBoundaryMapGeoJsonConverter}\n     */\n    public CountryBoundaryMapGeoJsonConverter withCountryDenyList(final Set<String> countryDenyList)\n    {\n        this.countryDenyList = countryDenyList;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/boundary/converters/CountryListTwoWayStringConverter.java",
    "content": "package org.openstreetmap.atlas.geography.boundary.converters;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayStringConverter;\n\n/**\n * @author tony\n */\npublic class CountryListTwoWayStringConverter\n        implements TwoWayStringConverter<StringList>, Serializable\n{\n    private static final long serialVersionUID = -9019352938025359414L;\n    private static final String COUNTRY_LIST_SEPARATOR = \",\";\n\n    @Override\n    public String backwardConvert(final StringList countryList)\n    {\n        return countryList.join(COUNTRY_LIST_SEPARATOR);\n    }\n\n    @Override\n    public StringList convert(final String countries)\n    {\n        return StringList.split(countries, COUNTRY_LIST_SEPARATOR);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/clipping/Clip.java",
    "content": "package org.openstreetmap.atlas.geography.clipping;\n\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Utility class to compute geometry clips\n *\n * @author matthieun\n * @deprecated Use {@link GeometryOperation} instead.\n */\n@Deprecated\npublic class Clip\n{\n    /**\n     * Type of clip\n     *\n     * @author matthieun\n     */\n    public enum ClipType\n    {\n        AND,\n        OR,\n        NOT,\n        XOR\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(Clip.class);\n\n    private final ClipType clipType;\n    private final List<? extends PolyLine> clip;\n    private final MultiPolygon clipMulti;\n\n    public Clip(final ClipType clipType, final MultiPolygon subject, final MultiPolygon clipping)\n    {\n        this.clipType = clipType;\n        this.clip = null;\n        this.clipMulti = clip(subject, clipping);\n    }\n\n    public Clip(final ClipType clipType, final PolyLine subject, final MultiPolygon clipping)\n    {\n        this.clipType = clipType;\n        if (subject instanceof Polygon)\n        {\n            this.clipMulti = clip((Polygon) subject, clipping);\n            this.clip = null;\n        }\n        else\n        {\n            this.clipMulti = null;\n            this.clip = clip(subject, clipping);\n        }\n    }\n\n    public Clip(final ClipType clipType, final PolyLine subject, final Polygon clipping)\n    {\n        this.clipType = clipType;\n        if (subject instanceof Polygon)\n        {\n            this.clip = clip((Polygon) subject, clipping);\n        }\n        else\n        {\n            this.clip = clip(subject, clipping);\n        }\n        this.clipMulti = null;\n    }\n\n    public List<? extends PolyLine> getClip()\n    {\n        return this.clip;\n    }\n\n    public MultiPolygon getClipMultiPolygon()\n    {\n        return this.clipMulti;\n    }\n\n    public boolean returnsMultiPolygon()\n    {\n        return this.clipMulti != null;\n    }\n\n    public boolean returnsPolygonOrPolyLineList()\n    {\n        return this.clip != null;\n    }\n\n    private MultiPolygon clip(final MultiPolygon subject, final MultiPolygon clipping)\n    {\n        switch (this.clipType)\n        {\n            case AND:\n                return new MultiPolygonClipper(clipping).and(subject);\n            case OR:\n                return new MultiPolygonClipper(clipping).union(subject);\n            case NOT:\n                return new MultiPolygonClipper(clipping).not(subject);\n            case XOR:\n                return new MultiPolygonClipper(clipping).xor(subject);\n            default:\n                throw new CoreException(\"Invalid Clip Type.\");\n        }\n    }\n\n    private MultiPolygon clip(final Polygon subject, final MultiPolygon clipping)\n    {\n        switch (this.clipType)\n        {\n            case AND:\n                return new MultiPolygonClipper(clipping).and(subject);\n            case OR:\n                return new MultiPolygonClipper(clipping).union(subject);\n            case NOT:\n                return new MultiPolygonClipper(clipping).not(subject);\n            case XOR:\n                return new MultiPolygonClipper(clipping).xor(subject);\n            default:\n                throw new CoreException(\"Invalid Clip Type.\");\n        }\n    }\n\n    private List<? extends PolyLine> clip(final Polygon subject, final Polygon clipping)\n    {\n        switch (this.clipType)\n        {\n            case AND:\n                return toPolygons(new PolygonClipper(clipping).and(subject));\n            case OR:\n                return toPolygons(new PolygonClipper(clipping).union(subject));\n            case NOT:\n                return toPolygons(new PolygonClipper(clipping).not(subject));\n            case XOR:\n                return toPolygons(new PolygonClipper(clipping).xor(subject));\n            default:\n                throw new CoreException(\"Invalid Clip Type.\");\n        }\n    }\n\n    private List<? extends PolyLine> clip(final PolyLine subject, final MultiPolygon clipping)\n    {\n        switch (this.clipType)\n        {\n            case AND:\n                return new MultiPolygonClipper(clipping).and(subject);\n            case OR:\n                return new MultiPolygonClipper(clipping).union(subject);\n            case NOT:\n                return new MultiPolygonClipper(clipping).not(subject);\n            case XOR:\n                return new MultiPolygonClipper(clipping).xor(subject);\n            default:\n                throw new CoreException(\"Invalid Clip Type.\");\n        }\n    }\n\n    private List<? extends PolyLine> clip(final PolyLine subject, final Polygon clipping)\n    {\n        switch (this.clipType)\n        {\n            case AND:\n                return new PolygonClipper(clipping).and(subject);\n            case OR:\n                return new PolygonClipper(clipping).union(subject);\n            case NOT:\n                return new PolygonClipper(clipping).not(subject);\n            case XOR:\n                return new PolygonClipper(clipping).xor(subject);\n            default:\n                throw new CoreException(\"Invalid Clip Type.\");\n        }\n    }\n\n    private List<? extends PolyLine> toPolygons(final Iterable<? extends PolyLine> input)\n    {\n        final Iterable<PolyLine> iterables = Iterables.translate(input, polyLine ->\n        {\n            if (polyLine != null && !(polyLine instanceof Polygon))\n            {\n                logger.warn(\"Something is not a Polygon {} : {}\", polyLine.getClass(), polyLine);\n                // throw new CoreException(\"Something is not a Polygon...\");\n            }\n            return polyLine instanceof Polygon ? polyLine : null;\n        });\n        // We filter off anything thats not a polygon\n        return Iterables.asList(Iterables.filter(iterables, p -> p != null));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/clipping/GeometryOperation.java",
    "content": "package org.openstreetmap.atlas.geography.clipping;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.operation.union.UnaryUnionOp;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricObject;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsLocationConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Wrapper around JTS for geometry operations.\n *\n * @author matthieun\n */\npublic final class GeometryOperation\n{\n    private static final JtsMultiPolygonToMultiPolygonConverter JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n    private static final JtsMultiPolygonConverter JTS_MULTI_POLYGON_CONVERTER = new JtsMultiPolygonConverter();\n    private static final JtsPolygonConverter JTS_POLYGON_CONVERTER = new JtsPolygonConverter();\n    private static final JtsMultiPolyLineConverter JTS_MULTI_POLY_LINE_CONVERTER = new JtsMultiPolyLineConverter();\n    private static final JtsPolyLineConverter JTS_POLY_LINE_CONVERTER = new JtsPolyLineConverter();\n    private static final JtsLocationConverter JTS_LOCATION_CONVERTER = new JtsLocationConverter();\n\n    public static Optional<GeometricObject> intersection(final Iterable<Polygon> polygons)\n    {\n        final List<Geometry> toIntersect = new ArrayList<>();\n        for (final Polygon polygon : polygons)\n        {\n            toIntersect.add(JTS_POLYGON_CONVERTER.convert(polygon));\n        }\n        try\n        {\n            return intersection(toIntersect);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Error computing intersection of {}!\",\n                    Iterables.asList(polygons), e);\n        }\n    }\n\n    public static Optional<GeometricSurface> union(final Iterable<MultiPolygon> multiPolygons)\n    {\n        final List<Geometry> toUnion = new ArrayList<>();\n        for (final MultiPolygon multiPolygon : multiPolygons)\n        {\n            toUnion.add(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(multiPolygon));\n        }\n        try\n        {\n            return union(toUnion);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Error computing union of {}!\", Iterables.asList(multiPolygons),\n                    e);\n        }\n    }\n\n    public static Optional<GeometricSurface> union(final MultiPolygon... multiPolygons)\n    {\n        return union(Iterables.asList(multiPolygons));\n    }\n\n    private static Optional<GeometricObject> handleGeometricObject(final Geometry result)\n    {\n        if (result.isEmpty())\n        {\n            return Optional.empty();\n        }\n        else if (result instanceof org.locationtech.jts.geom.MultiLineString)\n        {\n            return handleMultiLineString((org.locationtech.jts.geom.MultiLineString) result);\n        }\n        else if (result instanceof org.locationtech.jts.geom.LineString)\n        {\n            return handleLineString((org.locationtech.jts.geom.LineString) result);\n        }\n        else if (result instanceof org.locationtech.jts.geom.Point)\n        {\n            return handlePoint((org.locationtech.jts.geom.Point) result);\n        }\n        else\n        {\n            return handleGeometricSurface(result)\n                    .map(geometricSurface -> (GeometricObject) geometricSurface);\n        }\n    }\n\n    private static Optional<GeometricSurface> handleGeometricSurface(final Geometry result)\n    {\n        if (result.isEmpty())\n        {\n            return Optional.empty();\n        }\n        else if (result instanceof org.locationtech.jts.geom.MultiPolygon)\n        {\n            return handleMultiPolygon((org.locationtech.jts.geom.MultiPolygon) result);\n        }\n        else if (result instanceof org.locationtech.jts.geom.Polygon)\n        {\n            return handlePolygon((org.locationtech.jts.geom.Polygon) result);\n        }\n        else\n        {\n            throw new CoreException(\"Result is not recognized.\");\n        }\n    }\n\n    private static Optional<GeometricObject> handleLineString(\n            final org.locationtech.jts.geom.LineString result)\n    {\n        return Optional.of(JTS_POLY_LINE_CONVERTER.backwardConvert(result))\n                .map(polyLine -> (GeometricObject) polyLine);\n    }\n\n    private static Optional<GeometricObject> handleMultiLineString(\n            final org.locationtech.jts.geom.MultiLineString result)\n    {\n        return Optional.of(JTS_MULTI_POLY_LINE_CONVERTER.backwardConvert(result))\n                .map(polyLines -> (GeometricObject) polyLines);\n    }\n\n    private static Optional<GeometricSurface> handleMultiPolygon(\n            final org.locationtech.jts.geom.MultiPolygon result)\n    {\n        return Optional.of(JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.convert(result));\n    }\n\n    private static Optional<GeometricObject> handlePoint(\n            final org.locationtech.jts.geom.Point result)\n    {\n        return Optional.of(JTS_LOCATION_CONVERTER.backwardConvert(result.getCoordinate()))\n                .map(location -> (GeometricObject) location);\n    }\n\n    private static Optional<GeometricSurface> handlePolygon(\n            final org.locationtech.jts.geom.Polygon result)\n    {\n        final Set<org.locationtech.jts.geom.Polygon> resultSet = new HashSet<>();\n        resultSet.add(result);\n        final MultiPolygon resultMultiPolygon = JTS_MULTI_POLYGON_CONVERTER\n                .backwardConvert(resultSet);\n        if (resultMultiPolygon.inners().isEmpty() && resultMultiPolygon.outers().size() == 1)\n        {\n            return Optional.of(resultMultiPolygon.outers().iterator().next());\n        }\n        else\n        {\n            return Optional.of(resultMultiPolygon);\n        }\n    }\n\n    private static Optional<GeometricObject> intersection(final List<Geometry> toIntersect)\n    {\n        Geometry result = null;\n        for (final Geometry geometry : toIntersect)\n        {\n            if (result == null)\n            {\n                result = geometry;\n            }\n            else\n            {\n                result = result.intersection(geometry);\n            }\n        }\n        if (result == null || result.isEmpty())\n        {\n            return Optional.empty();\n        }\n        else\n        {\n            return handleGeometricObject(result);\n        }\n    }\n\n    private static Optional<GeometricSurface> union(final List<Geometry> toUnion)\n    {\n        final Geometry result = UnaryUnionOp.union(toUnion);\n        return handleGeometricSurface(result);\n    }\n\n    private GeometryOperation()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/clipping/MultiPolygonClipper.java",
    "content": "package org.openstreetmap.atlas.geography.clipping;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.BiFunction;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryCollection;\nimport org.locationtech.jts.geom.LineString;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * Wrapper around the JTS library for {@link Polygon} and {@link PolyLine} clipping with\n * {@link Polygon}s and {@link MultiPolygon}s.\n *\n * @author matthieun\n * @deprecated Use {@link GeometryOperation} instead.\n */\n@Deprecated\npublic class MultiPolygonClipper\n{\n    private final Set<org.locationtech.jts.geom.Polygon> jtsClippings;\n\n    protected MultiPolygonClipper(final MultiPolygon clipping)\n    {\n        this.jtsClippings = new JtsMultiPolygonConverter().convert(clipping);\n    }\n\n    protected MultiPolygon and(final MultiPolygon subject)\n    {\n        return runMultiPolygonClipping(subject, (sub, clipping) -> sub.intersection(clipping));\n    }\n\n    protected MultiPolygon and(final Polygon subject)\n    {\n        return runPolygonClipping(subject, (sub, clipping) -> sub.intersection(clipping));\n    }\n\n    protected List<PolyLine> and(final PolyLine subject)\n    {\n        return runPolyLineClipping(subject, (sub, clipping) -> sub.intersection(clipping));\n    }\n\n    protected MultiPolygon not(final MultiPolygon subject)\n    {\n        return runMultiPolygonClipping(subject, (sub, clipping) -> sub.difference(clipping));\n    }\n\n    protected MultiPolygon not(final Polygon subject)\n    {\n        return runPolygonClipping(subject, (sub, clipping) -> sub.difference(clipping));\n    }\n\n    protected List<PolyLine> not(final PolyLine subject)\n    {\n        return runPolyLineClipping(subject, (sub, clipping) -> sub.difference(clipping));\n    }\n\n    protected MultiPolygon union(final MultiPolygon subject)\n    {\n        return runMultiPolygonClipping(subject, (sub, clipping) -> sub.union(clipping));\n    }\n\n    protected MultiPolygon union(final Polygon subject)\n    {\n        return runPolygonClipping(subject, (sub, clipping) -> sub.union(clipping));\n    }\n\n    protected List<PolyLine> union(final PolyLine subject)\n    {\n        return runPolyLineClipping(subject, (sub, clipping) -> sub.union(clipping));\n    }\n\n    protected MultiPolygon xor(final MultiPolygon subject)\n    {\n        return runMultiPolygonClipping(subject, (sub, clipping) -> sub.symDifference(clipping));\n    }\n\n    protected MultiPolygon xor(final Polygon subject)\n    {\n        return runPolygonClipping(subject, (sub, clipping) -> sub.symDifference(clipping));\n    }\n\n    protected List<PolyLine> xor(final PolyLine subject)\n    {\n        return runPolyLineClipping(subject, (sub, clipping) -> sub.symDifference(clipping));\n    }\n\n    private MultiPolygon processMultiPolygon(final Geometry intersections)\n    {\n        MultiPolygon result = new MultiPolygon(new MultiMap<>());\n        if (intersections instanceof GeometryCollection)\n        {\n            final GeometryCollection collection = (GeometryCollection) intersections;\n            final int numGeometries = collection.getNumGeometries();\n            for (int n = 0; n < numGeometries; n++)\n            {\n                final Geometry geometry = collection.getGeometryN(n);\n                result = result.merge(processMultiPolygon(geometry));\n            }\n        }\n        else if (intersections instanceof org.locationtech.jts.geom.Polygon)\n        {\n            final Set<org.locationtech.jts.geom.Polygon> set = new HashSet<>();\n            set.add((org.locationtech.jts.geom.Polygon) intersections);\n            result = result.merge(new JtsMultiPolygonConverter().backwardConvert(set));\n        }\n        return result;\n    }\n\n    private List<PolyLine> processPolyLine(final Geometry intersections)\n    {\n        final List<PolyLine> result = new ArrayList<>();\n        if (intersections instanceof GeometryCollection)\n        {\n            final GeometryCollection collection = (GeometryCollection) intersections;\n            final int numGeometries = collection.getNumGeometries();\n            for (int n = 0; n < numGeometries; n++)\n            {\n                final Geometry geometry = collection.getGeometryN(n);\n                result.addAll(processPolyLine(geometry));\n            }\n        }\n        else if (intersections instanceof LineString)\n        {\n            result.add(new JtsPolyLineConverter().backwardConvert((LineString) intersections));\n        }\n        return result;\n    }\n\n    private MultiPolygon runMultiPolygonClipping(final MultiPolygon subject,\n            final BiFunction<Geometry, Geometry, Geometry> application)\n    {\n        MultiPolygon result = new MultiPolygon(new MultiMap<>());\n        final Set<org.locationtech.jts.geom.Polygon> jtsSubjects = new JtsMultiPolygonConverter()\n                .convert(subject);\n        for (final org.locationtech.jts.geom.Polygon jtsClipping : this.jtsClippings)\n        {\n            for (final org.locationtech.jts.geom.Polygon jtsSubject : jtsSubjects)\n            {\n                result = result\n                        .merge(processMultiPolygon(application.apply(jtsSubject, jtsClipping)));\n            }\n        }\n        return result;\n    }\n\n    private List<PolyLine> runPolyLineClipping(final PolyLine subject,\n            final BiFunction<Geometry, Geometry, Geometry> application)\n    {\n        final List<PolyLine> result = new ArrayList<>();\n        for (final org.locationtech.jts.geom.Polygon jtsClipping : this.jtsClippings)\n        {\n            result.addAll(processPolyLine(\n                    application.apply(PolygonClipper.getJts(subject), jtsClipping)));\n        }\n        return result;\n    }\n\n    private MultiPolygon runPolygonClipping(final Polygon subject,\n            final BiFunction<Geometry, Geometry, Geometry> application)\n    {\n        MultiPolygon result = new MultiPolygon(new MultiMap<>());\n        for (final org.locationtech.jts.geom.Polygon jtsClipping : this.jtsClippings)\n        {\n            result = result.merge(processMultiPolygon(\n                    application.apply(PolygonClipper.getJts(subject), jtsClipping)));\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/clipping/PolygonClipper.java",
    "content": "package org.openstreetmap.atlas.geography.clipping;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryCollection;\nimport org.locationtech.jts.geom.LineString;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\n\n/**\n * Clip {@link Polygon}s using the JTS library\n *\n * @author matthieun\n * @deprecated Use {@link GeometryOperation} instead.\n */\n@Deprecated\npublic class PolygonClipper\n{\n    private final Geometry jtsClipping;\n\n    public static Geometry getJts(final PolyLine polyLine)\n    {\n        if (polyLine instanceof Polygon)\n        {\n            return new JtsPolygonConverter().convert((Polygon) polyLine);\n        }\n        return new JtsPolyLineConverter().convert(polyLine);\n    }\n\n    /**\n     * Construct\n     *\n     * @param clipping\n     *            The clipping {@link Polygon}\n     */\n    public PolygonClipper(final Polygon clipping)\n    {\n        this.jtsClipping = getJts(clipping);\n    }\n\n    public List<? extends PolyLine> and(final PolyLine subject)\n    {\n        return processResult(getJts(subject).intersection(this.jtsClipping));\n    }\n\n    public List<? extends PolyLine> not(final PolyLine subject)\n    {\n        return processResult(getJts(subject).difference(this.jtsClipping));\n    }\n\n    public List<? extends PolyLine> union(final PolyLine subject)\n    {\n        return processResult(getJts(subject).union(this.jtsClipping));\n    }\n\n    public List<? extends PolyLine> xor(final PolyLine subject)\n    {\n        return processResult(getJts(subject).symDifference(this.jtsClipping));\n    }\n\n    private List<? extends PolyLine> processResult(final Geometry intersections)\n    {\n        final List<PolyLine> result = new ArrayList<>();\n        if (intersections instanceof GeometryCollection)\n        {\n            final GeometryCollection collection = (GeometryCollection) intersections;\n            final int numGeometries = collection.getNumGeometries();\n            for (int n = 0; n < numGeometries; n++)\n            {\n                final Geometry geometry = collection.getGeometryN(n);\n                result.addAll(processResult(geometry));\n            }\n        }\n        else if (intersections instanceof org.locationtech.jts.geom.Polygon)\n        {\n            result.add(new JtsPolygonConverter()\n                    .backwardConvert((org.locationtech.jts.geom.Polygon) intersections));\n        }\n        else if (intersections instanceof LineString)\n        {\n            result.add(new JtsPolyLineConverter().backwardConvert((LineString) intersections));\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/clipping/README.md",
    "content": "# Geometry Clipping\n\nRules below are deprecated\n\nSubject|Clipping|Result\n-------|-------|-------\nPolyLine|Polygon|List of PolyLines\nPolyLine|MultiPolygon|List of PolyLines\nPolygon|Polygon|List of Polygons\nPolyLine|MultiPolygon|One MultiPolygon\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/constants/WorldGeodeticStandardConstants.java",
    "content": "package org.openstreetmap.atlas.geography.constants;\n\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * World Geodetic Standard 1984 ellipsoid constants.\n *\n * @author mgostintsev\n */\npublic final class WorldGeodeticStandardConstants\n{\n    /**\n     * Equatorial radius, typically denoted as 'a' in standard convention.\n     */\n    public static final Distance SEMI_MAJOR_AXIS = Distance.meters(6378137.0);\n\n    /**\n     * {@link #SEMI_MAJOR_AXIS} squared, for convenience.\n     */\n    public static final double SEMI_MAJOR_AXIS_SQUARED = Math.pow(SEMI_MAJOR_AXIS.asMeters(), 2);\n\n    /**\n     * Flattening is the measure of compression of a sphere to form a spheroid. Typically denoted as\n     * 'f' in standard convention.\n     */\n    public static final double FLATTENING = 298.257223563;\n\n    /**\n     * {@link #FLATTENING} inverse, for convenience.\n     */\n    public static final double INVERSE_FLATTENING = 1 / FLATTENING;\n\n    /**\n     * The measure of how much a conic section deviates from being circular. Typically denoted as\n     * 'e' in standard convention. Eccentricity does not have units. Ex: the eccentricity of a\n     * circle is 0 and that of a line is infinite.\n     */\n    public static final double ECCENTRICITY = Math.sqrt(1 - Math.pow(1 - INVERSE_FLATTENING, 2));\n\n    /**\n     * {@link #ECCENTRICITY} squared, for convenience.\n     */\n    public static final double ECCENTRICITY_SQUARED = Math.pow(ECCENTRICITY, 2);\n\n    private WorldGeodeticStandardConstants()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/GeodeticEarthCenteredEarthFixedConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.constants.WorldGeodeticStandardConstants;\nimport org.openstreetmap.atlas.geography.coordinates.EarthCenteredEarthFixedCoordinate;\nimport org.openstreetmap.atlas.geography.coordinates.GeodeticCoordinate;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Conversion from a {@link GeodeticCoordinate} to an {@link EarthCenteredEarthFixedCoordinate}. For\n * reference, images and formulas used are found\n * <a href= \"https://microem.ru/files/2012/08/GPS.G1-X-00006.pdf\"> here</a>.\n *\n * @author mgostintsev\n */\npublic class GeodeticEarthCenteredEarthFixedConverter\n        implements TwoWayConverter<GeodeticCoordinate, EarthCenteredEarthFixedCoordinate>\n{\n    @Override\n    public GeodeticCoordinate backwardConvert(final EarthCenteredEarthFixedCoordinate coordinate)\n    {\n        final double semiMinor = Math.sqrt(WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS_SQUARED\n                * (1 - WorldGeodeticStandardConstants.ECCENTRICITY_SQUARED));\n        final double semiMinorSquared = Math.pow(semiMinor, 2);\n\n        final double secondEccentricity = Math\n                .sqrt((WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS_SQUARED - semiMinorSquared)\n                        / semiMinorSquared);\n        final double auxiliaryP = Math\n                .sqrt(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2));\n        final double theta = Math.atan2(\n                coordinate.getZ() * WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS.asMeters(),\n                auxiliaryP * semiMinor);\n\n        final double longitude = Math.atan2(coordinate.getY(), coordinate.getX());\n        final double latitude = Math.atan2(\n                coordinate.getZ() + Math.pow(secondEccentricity, 2) * semiMinor\n                        * Math.pow(Math.sin(theta), 3),\n                auxiliaryP - WorldGeodeticStandardConstants.ECCENTRICITY_SQUARED\n                        * WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS.asMeters()\n                        * Math.pow(Math.cos(theta), 3));\n\n        final double radiusOfCurviture = WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS.asMeters()\n                / Math.sqrt(1 - WorldGeodeticStandardConstants.ECCENTRICITY_SQUARED\n                        * Math.pow(Math.sin(latitude), 2));\n\n        final double altitude = auxiliaryP / Math.cos(latitude) - radiusOfCurviture;\n\n        return new GeodeticCoordinate(Latitude.radians(latitude), Longitude.radians(longitude),\n                Altitude.meters(altitude));\n    }\n\n    @Override\n    public EarthCenteredEarthFixedCoordinate convert(final GeodeticCoordinate coordinate)\n    {\n        final double radiusOfCurviture = WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS.asMeters()\n                / Math.sqrt(1 - WorldGeodeticStandardConstants.ECCENTRICITY_SQUARED\n                        * Math.pow(Math.sin(coordinate.getLatitude().asPositiveRadians()), 2));\n\n        final double height = coordinate.getAltitude().asMeters();\n\n        // getting positive angles for latitude/longitude is okay as the angles would be the same.\n        // For example, cos(-30) == cos(330) and sin(-30) == sin(330)\n        final double xValue = (radiusOfCurviture + height)\n                * Math.cos(coordinate.getLatitude().asPositiveRadians())\n                * Math.cos(coordinate.getLongitude().asPositiveRadians());\n\n        final double yValue = (radiusOfCurviture + height)\n                * Math.cos(coordinate.getLatitude().asPositiveRadians())\n                * Math.sin(coordinate.getLongitude().asPositiveRadians());\n\n        final double zValue = ((1 - WorldGeodeticStandardConstants.ECCENTRICITY_SQUARED)\n                * radiusOfCurviture + height)\n                * Math.sin(coordinate.getLatitude().asPositiveRadians());\n\n        return new EarthCenteredEarthFixedCoordinate(xValue, yValue, zValue);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/MultiPolygonStringConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\n\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayStringConverter;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * Convert a {@link MultiPolygon} back and forth to a {@link String}\n *\n * @author matthieun\n */\npublic class MultiPolygonStringConverter implements TwoWayStringConverter<MultiPolygon>\n{\n    // For some reason, splitting the String fails when OUTER_SEPARATOR is \"|\"\n    public static final String OUTER_SEPARATOR = \"&\";\n    public static final String OUTER_INNERS_SEPARATOR = \"#\";\n    public static final String INNER_SEPARATOR = \"+\";\n\n    private static final PolygonStringConverter POLYGON_STRING_CONVERTER = new PolygonStringConverter();\n\n    @Override\n    public String backwardConvert(final MultiPolygon object)\n    {\n        final StringList outers = new StringList();\n        for (final Polygon outer : object.outers())\n        {\n            final StringList inners = new StringList();\n            for (final Polygon inner : object.innersOf(outer))\n            {\n                inners.add(inner.toCompactString());\n            }\n            outers.add(outer.toCompactString() + OUTER_INNERS_SEPARATOR\n                    + inners.join(INNER_SEPARATOR));\n        }\n        return outers.join(OUTER_SEPARATOR);\n    }\n\n    @Override\n    public MultiPolygon convert(final String object)\n    {\n        final MultiMap<Polygon, Polygon> result = new MultiMap<>();\n        final StringList outers = StringList.split(object, OUTER_SEPARATOR);\n        for (final String outerString : outers)\n        {\n            final StringList outerInners = StringList.split(outerString, OUTER_INNERS_SEPARATOR);\n            final Polygon outer = POLYGON_STRING_CONVERTER.convert(outerInners.get(0));\n            if (outerInners.size() > 1)\n            {\n                final StringList inners = StringList.split(outerInners.get(1), INNER_SEPARATOR);\n\n                for (final String innerString : inners)\n                {\n                    result.add(outer, POLYGON_STRING_CONVERTER.convert(innerString));\n                }\n            }\n            else\n            {\n                result.put(outer, new ArrayList<>());\n            }\n        }\n        return new MultiPolygon(result);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToMultiPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.locationtech.jts.geom.prep.PreparedGeometryFactory;\nimport org.locationtech.jts.geom.prep.PreparedPolygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation.Ring;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * From a map of {@link PolyLine}s for {@link Ring} types, try to stitch all the {@link PolyLine}s\n * together to form a single {@link MultiPolygon}.\n *\n * @author samg\n **/\npublic class MultiplePolyLineToMultiPolygonConverter\n        implements Converter<Map<Ring, Iterable<PolyLine>>, MultiPolygon>\n{\n\n    private static final MultiplePolyLineToPolygonsConverter MULTIPLE_POLY_LINE_TO_POLYGONS_CONVERTER = new MultiplePolyLineToPolygonsConverter();\n\n    private static List<Polygon> buildOuters(final Iterable<PolyLine> outers)\n    {\n        final List<Polygon> outerPolygons = new ArrayList<>();\n        Iterables.stream(outers).filter(line -> line instanceof Polygon)\n                .forEach(polygon -> outerPolygons.add((Polygon) polygon));\n        final List<PolyLine> outerPolyLines = new ArrayList<>();\n        Iterables.stream(outers).filter(line -> !(line instanceof Polygon))\n                .forEach(outerPolyLines::add);\n        MULTIPLE_POLY_LINE_TO_POLYGONS_CONVERTER.convert(outerPolyLines)\n                .forEach(outerPolygons::add);\n        return outerPolygons;\n    }\n\n    private static MultiMap<Polygon, Polygon> buildOutersToInnersMap(final List<Polygon> outers,\n            final Iterable<PolyLine> inners)\n    {\n        final MultiMap<Polygon, Polygon> outersToInners = new MultiMap<>();\n        outers.forEach(outer -> outersToInners.put(outer, new ArrayList<>()));\n        final List<Polygon> innerPolygons = new ArrayList<>();\n        final List<PolyLine> innerPolyLines = new ArrayList<>();\n        Iterables.stream(inners).filter(line -> line instanceof Polygon)\n                .forEach(polygon -> innerPolygons.add((Polygon) polygon));\n        Iterables.stream(inners).filter(line -> !(line instanceof Polygon))\n                .forEach(innerPolyLines::add);\n        MULTIPLE_POLY_LINE_TO_POLYGONS_CONVERTER.convert(innerPolyLines)\n                .forEach(innerPolygons::add);\n        final JtsPolygonConverter converter = new JtsPolygonConverter();\n        final Map<Polygon, PreparedPolygon> preparedOuters = new HashMap<>();\n        outersToInners.keySet().forEach(outer -> preparedOuters.put(outer,\n                (PreparedPolygon) PreparedGeometryFactory.prepare(converter.convert(outer))));\n        innerPolygons.forEach(inner ->\n        {\n            boolean added = false;\n            final org.locationtech.jts.geom.Polygon inner2 = converter.convert(inner);\n            for (final Map.Entry<Polygon, PreparedPolygon> entry : preparedOuters.entrySet())\n            {\n                if (entry.getValue().containsProperly(inner2))\n                {\n                    outersToInners.add(entry.getKey(), inner);\n                    added = true;\n                    break;\n                }\n            }\n            if (!added)\n            {\n                throw new CoreException(\"Malformed MultiPolygon: inner has no outer host: {}\",\n                        inner);\n            }\n        });\n        return outersToInners;\n    }\n\n    @Override\n    public MultiPolygon convert(final Map<Ring, Iterable<PolyLine>> outersAndInners)\n    {\n        final List<Polygon> outers = buildOuters(outersAndInners.get(Ring.OUTER));\n        if (outers.isEmpty())\n        {\n            throw new CoreException(\"Unable to find outer polygon.\");\n        }\n        final MultiMap<Polygon, Polygon> outersToInners = buildOutersToInnersMap(outers,\n                outersAndInners.get(Ring.INNER));\n        return new MultiPolygon(outersToInners);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.operation.polygonize.Polygonizer;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * From a set of {@link PolyLine}s, try to stitch all the {@link PolyLine}s together to form\n * {@link Polygon}s.\n *\n * @author matthieun\n * @author Sid\n */\npublic class MultiplePolyLineToPolygonsConverter\n        implements Converter<Iterable<PolyLine>, Iterable<Polygon>>\n{\n    /**\n     * @author Sid\n     */\n    public static class OpenPolygonException extends CoreException\n    {\n        private static final long serialVersionUID = -278028096455310936L;\n\n        public static final String OPEN_LOCATIONS_ARE = \" Open Locations are: \";\n\n        private final List<Location> openLocations;\n\n        public OpenPolygonException(final String message, final List<Location> openLocations)\n        {\n            super(message + OPEN_LOCATIONS_ARE + openLocations.toString());\n            this.openLocations = openLocations;\n        }\n\n        public OpenPolygonException(final String message, final List<Location> openLocations,\n                final Object... arguments)\n        {\n            super(message + OPEN_LOCATIONS_ARE + openLocations.toString(), arguments);\n            this.openLocations = openLocations;\n        }\n\n        public OpenPolygonException(final String message, final List<Location> openLocations,\n                final Throwable cause, final Object... arguments)\n        {\n            super(message + OPEN_LOCATIONS_ARE + openLocations.toString(), cause, arguments);\n            this.openLocations = openLocations;\n        }\n\n        public List<Location> getOpenLocations()\n        {\n            return this.openLocations;\n        }\n    }\n\n    /**\n     * Simple object containing connectivity information about two {@link PolyLine}s. \"connected\"\n     * set to \"true\" means the two {@link PolyLine}s connect at some end. \"reversed\" set to true\n     * means that one of the two {@link PolyLine}s had to be reversed to connect.\n     *\n     * @author matthieun\n     */\n    private static class ConnectResult\n    {\n        private final boolean connected;\n        private final boolean reversed;\n\n        ConnectResult(final boolean connected, final boolean reversed)\n        {\n            this.connected = connected;\n            this.reversed = reversed;\n        }\n\n        public boolean isConnected()\n        {\n            return this.connected;\n        }\n\n        public boolean isReversed()\n        {\n            return this.reversed;\n        }\n\n        @Override\n        public String toString()\n        {\n            final String isReversed = this.reversed ? \" and reversed\" : \"\";\n            return this.connected ? \"Connected\" + isReversed : \"Not connected\";\n        }\n    }\n\n    /**\n     * A {@link Polygon} in construction, with many other {@link PolyLine}s\n     *\n     * @author matthieun\n     */\n    private static class PossiblePolygon\n    {\n        private boolean completed;\n        // An ordered list of polylines, based on connectivity\n        private final List<PolyLine> polyLines = new ArrayList<>();\n\n        PossiblePolygon(final PolyLine first)\n        {\n            this.completed = first instanceof Polygon || first.first().equals(first.last());\n            this.polyLines.add(first);\n        }\n\n        /**\n         * @param candidate\n         *            A polyLine to attach to that possible polygon\n         * @return True if the polyLine was successfully attached.\n         */\n        public boolean attach(final PolyLine candidate)\n        {\n            boolean result = false;\n            final ConnectResult canAppendCandidateToLine = canAppendSecondToFirst(lastPolyLine(),\n                    candidate);\n            final ConnectResult canPrependCandidateToLine = canPrependFirstToSecond(candidate,\n                    firstPolyLine());\n            PolyLine toAdd = candidate;\n            if (canAppendCandidateToLine.isConnected())\n            {\n                if (canAppendCandidateToLine.isReversed())\n                {\n                    toAdd = toAdd.reversed();\n                }\n                if (toAdd.size() > 1)\n                {\n                    toAdd = trimFirst(toAdd);\n                }\n                else\n                {\n                    if (canPrependCandidateToLine.isConnected())\n                    {\n                        this.completed = true;\n                    }\n                    return true;\n                }\n            }\n            if (canPrependCandidateToLine.isConnected())\n            {\n                if (canPrependCandidateToLine.isReversed())\n                {\n                    if (canAppendCandidateToLine.isConnected())\n                    {\n                        // Already reversed previously\n                    }\n                    else\n                    {\n                        toAdd = toAdd.reversed();\n                    }\n                }\n                if (toAdd.size() > 1)\n                {\n                    toAdd = trimLast(toAdd);\n                }\n                else\n                {\n                    if (canAppendCandidateToLine.isConnected())\n                    {\n                        this.completed = true;\n                    }\n                    return true;\n                }\n            }\n            if (canAppendCandidateToLine.isConnected())\n            {\n                this.polyLines.add(toAdd);\n                result = true;\n            }\n            else if (canPrependCandidateToLine.isConnected())\n            {\n                this.polyLines.add(0, toAdd);\n                result = true;\n            }\n            if (canPrependCandidateToLine.isConnected() && canAppendCandidateToLine.isConnected())\n            {\n                this.completed = true;\n            }\n            return result;\n        }\n\n        public Location firstLocation()\n        {\n            return this.polyLines.get(0).first();\n        }\n\n        public boolean isCompleted()\n        {\n            return this.completed;\n        }\n\n        public Location lastLocation()\n        {\n            return this.polyLines.get(this.polyLines.size() - 1).last();\n        }\n\n        public int size()\n        {\n            return this.polyLines.size();\n        }\n\n        public Polygon toPolygon()\n        {\n            if (!this.isCompleted() && this.size() >= 1)\n            {\n                // If that method is called and the PossiblePolygon is not closed (i.e. completed)\n                // we gather the first and end point of the partially completed polyline and throw\n                // an exception.\n                final List<Location> openLocations = new ArrayList<>();\n                final Location firstLocation = this.polyLines.get(0).first();\n                final Location lastLocation = this.polyLines.get(this.size() - 1).last();\n                if (firstLocation != null && lastLocation != null)\n                {\n                    openLocations.add(firstLocation);\n                    openLocations.add(lastLocation);\n                    throw new OpenPolygonException(\n                            \"Cannot build polygon with multiple polylines. Loop is not closed.\",\n                            openLocations);\n                }\n            }\n            return new Polygon(new MultiIterable<>(this.polyLines));\n        }\n\n        @Override\n        public String toString()\n        {\n            final StringList list = new StringList();\n            this.polyLines.forEach(polyLine -> list.add(polyLine.first() + \" -> \"));\n            list.add(this.lastLocation());\n            return list.join(\"\");\n        }\n\n        /**\n         * Test if two {@link PolyLine}s connect by appending the second {@link PolyLine} (straight\n         * or reversed) to the first one (unchanged).\n         *\n         * @param one\n         *            The {@link PolyLine} from which the end will be considered\n         * @param two\n         *            The {@link PolyLine} from which the start will be considered\n         * @return ConnectResult: connected = true if the end of one is the same as the start of two\n         *         and reversed = true if the {@link PolyLine} two had to be reversed to be able to\n         *         connect the end of one to the beginning of two.\n         */\n        private ConnectResult canAppendSecondToFirst(final PolyLine one, final PolyLine two)\n        {\n            if (one.last().equals(two.first()))\n            {\n                return new ConnectResult(true, false);\n            }\n            else if (one.last().equals(two.last()))\n            {\n                return new ConnectResult(true, true);\n            }\n            else\n            {\n                return new ConnectResult(false, false);\n            }\n        }\n\n        /**\n         * Test if two {@link PolyLine}s connect by prepending the first {@link PolyLine} (straight\n         * or reversed) to the second one (unchanged).\n         *\n         * @param one\n         *            The {@link PolyLine} from which the end will be considered\n         * @param two\n         *            The {@link PolyLine} from which the start will be considered\n         * @return ConnectResult: connected = true if the end of one is the same as the start of two\n         *         and reversed = true if the {@link PolyLine} one had to be reversed to be able to\n         *         connect the end of one to the beginning of two.\n         */\n        private ConnectResult canPrependFirstToSecond(final PolyLine one, final PolyLine two)\n        {\n            if (one.last().equals(two.first()))\n            {\n                return new ConnectResult(true, false);\n            }\n            else if (one.first().equals(two.first()))\n            {\n                return new ConnectResult(true, true);\n            }\n            else\n            {\n                return new ConnectResult(false, false);\n            }\n        }\n\n        private PolyLine firstPolyLine()\n        {\n            return this.polyLines.get(0);\n        }\n\n        private PolyLine lastPolyLine()\n        {\n            return this.polyLines.get(this.polyLines.size() - 1);\n        }\n\n        /**\n         * Remove the first point of this {@link PolyLine} to append it to another {@link PolyLine}\n         *\n         * @param current\n         *            The {@link PolyLine} to trim\n         * @return The {@link PolyLine} trimmed of its first point.\n         */\n        private PolyLine trimFirst(final PolyLine current)\n        {\n            final List<Location> result = new ArrayList<>();\n            for (final Location location : current)\n            {\n                result.add(location);\n            }\n            result.remove(0);\n            return new PolyLine(result);\n        }\n\n        /**\n         * Remove the last point of this {@link PolyLine} to prepend it to another {@link PolyLine}\n         *\n         * @param current\n         *            The {@link PolyLine} to trim\n         * @return The {@link PolyLine} trimmed of its last point.\n         */\n        private PolyLine trimLast(final PolyLine current)\n        {\n            final List<Location> result = new ArrayList<>();\n            for (final Location location : current)\n            {\n                result.add(location);\n            }\n            result.remove(result.size() - 1);\n            return new PolyLine(result);\n        }\n    }\n\n    private static final JtsPolyLineConverter JTS_POLY_LINE_CONVERTER = new JtsPolyLineConverter();\n    private static final JtsPolygonConverter JTS_POLYGON_CONVERTER = new JtsPolygonConverter();\n\n    private final boolean usePolygonizer;\n\n    public MultiplePolyLineToPolygonsConverter()\n    {\n        this.usePolygonizer = false;\n    }\n\n    public MultiplePolyLineToPolygonsConverter(final boolean usePolygonizer)\n    {\n        this.usePolygonizer = usePolygonizer;\n    }\n\n    @Override\n    public Iterable<Polygon> convert(final Iterable<PolyLine> candidates)\n    {\n        if (this.usePolygonizer)\n        {\n            return convertAttemptPolygonizer(candidates);\n        }\n        else\n        {\n            return convertLegacy(candidates);\n        }\n    }\n\n    public Iterable<Polygon> convertAttemptPolygonizer(final Iterable<PolyLine> candidates)\n    {\n        final Polygonizer polygonizer = new Polygonizer();\n        candidates.forEach(polyLine -> polygonizer.add(JTS_POLY_LINE_CONVERTER.convert(polyLine)));\n\n        // Check for missing parts\n        final List<LineString> errors = new ArrayList<>();\n        Exception potentialException = null;\n        try\n        {\n            errors.addAll(polygonizer.getDangles());\n            errors.addAll(polygonizer.getCutEdges());\n            errors.addAll(polygonizer.getInvalidRingLines());\n        }\n        catch (final Exception e)\n        {\n            potentialException = e;\n        }\n        if (errors.isEmpty() && potentialException == null)\n        {\n            // Get results\n            final List<org.locationtech.jts.geom.Polygon> result = (List<org.locationtech.jts.geom.Polygon>) polygonizer\n                    .getPolygons();\n            return result.stream().map(polygon ->\n            {\n                polygon.normalize();\n                return JTS_POLYGON_CONVERTER.backwardConvert(polygon);\n            }).collect(Collectors.toList());\n        }\n        else\n        {\n            final List<Location> locations = errors.stream()\n                    .map(JTS_POLY_LINE_CONVERTER::backwardConvert)\n                    .flatMap(dangle -> Iterables\n                            .asList(Iterables.from(dangle.first(), dangle.last())).stream())\n                    .collect(Collectors.toList());\n            final OpenPolygonException jtsException;\n            final String errorMessage = \"Unable to close all the polygons!\";\n            if (potentialException != null)\n            {\n                jtsException = new OpenPolygonException(errorMessage, locations,\n                        potentialException);\n            }\n            else\n            {\n                jtsException = new OpenPolygonException(errorMessage, locations);\n            }\n            // Try the legacy convert\n            try\n            {\n                return convertLegacy(candidates);\n            }\n            catch (final Exception e)\n            {\n                throw new OpenPolygonException(\n                        \"Failed second legacy attempt. JTS Exception was: \\\"{}\\\"\",\n                        jtsException.getOpenLocations(), jtsException.getMessage(), e);\n            }\n        }\n    }\n\n    public Iterable<Polygon> convertLegacy(final Iterable<PolyLine> candidates) // NOSONAR\n    {\n        // The complete polygons\n        final List<PossiblePolygon> completes = new ArrayList<>();\n        // The polygons that have been started, but that are incomplete.\n        final List<PossiblePolygon> incompletes = new ArrayList<>();\n        // The polyLines that have not found a match yet\n        final LinkedList<PolyLine> remainingPolyLines = new LinkedList<>();\n        candidates.forEach(remainingPolyLines::add);\n        int iterationsSinceLastPolyLineTaken = 0;\n        while (!remainingPolyLines.isEmpty()\n                && iterationsSinceLastPolyLineTaken <= remainingPolyLines.size())\n        {\n            final PolyLine candidate = remainingPolyLines.removeFirst();\n            boolean added = false;\n            if (!incompletes.isEmpty())\n            {\n                // There are some incompletes. Always try to fill the incompletes to the end until\n                // they are complete before creating new incomplete polygons.\n                boolean completed = false;\n                int index = -1;\n                // Try the candidate polyline with all the incomplete polygons\n                for (final PossiblePolygon incomplete : incompletes)\n                {\n                    index++;\n                    if (incomplete.attach(candidate))\n                    {\n                        added = true;\n                        completed = incomplete.isCompleted();\n                        break;\n                    }\n                }\n                if (completed)\n                {\n                    final PossiblePolygon increased = incompletes.get(index);\n                    incompletes.remove(index);\n                    completes.add(increased);\n                }\n            }\n            else\n            {\n                // There are no incomplete polygons, just create one.\n                final PossiblePolygon incompleteCandidate = new PossiblePolygon(candidate);\n                if (incompleteCandidate.isCompleted())\n                {\n                    completes.add(incompleteCandidate);\n                }\n                else\n                {\n                    incompletes.add(incompleteCandidate);\n                }\n                added = true;\n            }\n\n            if (!added)\n            {\n                // Could not add the polyline to any incomplete polygon, so adding it back to the\n                // end of the list. It might get better luck once those incomplete polygons have\n                // grown a bit more.\n                remainingPolyLines.addLast(candidate);\n                iterationsSinceLastPolyLineTaken++;\n            }\n            else\n            {\n                iterationsSinceLastPolyLineTaken = 0;\n            }\n        }\n        if (!incompletes.isEmpty())\n        {\n            throw new OpenPolygonException(\"Unable to close all the polygons!\",\n                    Iterables\n                            .stream(incompletes).flatMap(incomplete -> Iterables\n                                    .from(incomplete.firstLocation(), incomplete.lastLocation()))\n                            .collectToList());\n        }\n        return completes.stream().map(PossiblePolygon::toPolygon).collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterCommand.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.io.OutputStreamWriter;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.writers.SafeBufferedWriter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * This command reads a file with delimited lists of {@link PolyLine}s in WKT format, and applies\n * logic to stitch those {@link PolyLine}s together to form one or more {@link Polygon}s.\n *\n * @author matthieun\n */\npublic class MultiplePolyLineToPolygonsConverterCommand extends Command\n{\n    private static final Switch<File> POLYLINES = new Switch<>(\"polylines\",\n            \"File containing lines of semicolon separated list of WKT polylines\", File::new,\n            Optionality.REQUIRED);\n    private static final Switch<File> POLYGONS = new Switch<>(\"polygons\",\n            \"Output file that will contain lines of semicolon separated list of reconstructed polygons\",\n            File::new, Optionality.OPTIONAL);\n    private static final Switch<String> DELIMITER = new Switch<>(\"delimiter\",\n            \"The string delimiter between groups of polylines, and polygons in the output.\",\n            StringConverter.IDENTITY, Optionality.OPTIONAL, \";\");\n\n    private static final WktPolyLineConverter WKT_POLY_LINE_CONVERTER = new WktPolyLineConverter();\n    private static final WktMultiPolyLineConverter WKT_MULTI_POLY_LINE_CONVERTER = new WktMultiPolyLineConverter();\n    private static final MultiplePolyLineToPolygonsConverter MULTIPLE_POLY_LINE_TO_POLYGONS_CONVERTER = new MultiplePolyLineToPolygonsConverter();\n\n    public static void main(final String[] args)\n    {\n        new MultiplePolyLineToPolygonsConverterCommand().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final String delimiter = (String) command.get(DELIMITER);\n        final File input = (File) command.get(POLYLINES);\n        final File output = (File) command.get(POLYGONS);\n        translate(input, output, delimiter);\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(POLYLINES, POLYGONS, DELIMITER);\n    }\n\n    protected void translate(final Resource input, final WritableResource output,\n            final String delimiter)\n    {\n        final Iterable<List<PolyLine>> inputs = Iterables.stream(input.lines())\n                .map(line -> StringList.split(line, delimiter).stream()\n                        .filter(wkt -> wkt != null && !wkt.isEmpty()).flatMap(wkt ->\n                        {\n                            final List<PolyLine> result = new ArrayList<>();\n                            if (wkt.toLowerCase().contains(\"multi\"))\n                            {\n                                WKT_MULTI_POLY_LINE_CONVERTER.backwardConvert(wkt)\n                                        .forEach(result::add);\n                            }\n                            else\n                            {\n                                result.add(WKT_POLY_LINE_CONVERTER.backwardConvert(wkt));\n                            }\n                            return result.stream();\n                        }).collect(Collectors.toList()));\n        try (SafeBufferedWriter writer = writer(output))\n        {\n            for (final List<PolyLine> inputPolyLines : inputs)\n            {\n                writer.writeLine(new StringList(Iterables\n                        .stream(MULTIPLE_POLY_LINE_TO_POLYGONS_CONVERTER.convert(inputPolyLines))\n                        .map(Polygon::toWkt)).join(delimiter));\n            }\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to convert polylines from {}\", input.getName(), e);\n        }\n    }\n\n    private SafeBufferedWriter writer(final WritableResource output)\n    {\n        return output != null ? output.writer()\n                : new SafeBufferedWriter(new OutputStreamWriter(System.out));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/PolyLineStringConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayStringConverter;\n\n/**\n * @author matthieun\n */\npublic class PolyLineStringConverter implements TwoWayStringConverter<PolyLine>\n{\n    @Override\n    public String backwardConvert(final PolyLine object)\n    {\n        return object.toString();\n    }\n\n    @Override\n    public PolyLine convert(final String object)\n    {\n        final StringList split = StringList.split(object, PolyLine.SEPARATOR);\n        final List<Location> locations = new ArrayList<>();\n        for (final String location : split)\n        {\n            locations.add(Location.forString(location));\n        }\n        return new PolyLine(locations);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/PolygonStringConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayStringConverter;\n\n/**\n * @author matthieun\n */\npublic class PolygonStringConverter implements TwoWayStringConverter<Polygon>\n{\n    @Override\n    public String backwardConvert(final Polygon object)\n    {\n        return object.toCompactString();\n    }\n\n    @Override\n    public Polygon convert(final String object)\n    {\n        final StringList split = StringList.split(object, PolyLine.SEPARATOR);\n        final List<Location> locations = new ArrayList<>();\n        for (final String location : split)\n        {\n            locations.add(Location.forString(location));\n        }\n        return new Polygon(locations);\n    }\n\n    public Polygon convertLongitudeLatitude(final String object)\n    {\n        final StringList split = StringList.split(object, PolyLine.SEPARATOR);\n        final List<Location> locations = new ArrayList<>();\n        for (final String location : split)\n        {\n            locations.add(Location.forStringLongitudeLatitude(location));\n        }\n        return new Polygon(locations);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/PolygonStringFormat.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.locationtech.jts.io.WKBReader;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.streaming.readers.GeoJsonReader;\nimport org.openstreetmap.atlas.streaming.readers.json.serializers.PropertiesLocated;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * The enum for supported Polygon and Multipolygon string formats. Contains functions to return\n * {@link Polygon} and {@link MultiPolygon} {@link StringConverter}s.\n *\n * @author jklamer\n */\npublic enum PolygonStringFormat\n{\n    ATLAS(\"atlas\"),\n    GEOJSON(\"geojson\"),\n    WKT(\"wkt\"),\n    WKB(\"wkb\"),\n    UNSUPPORTED(\"UNSUPPORTED\");\n\n    private static final Logger logger = LoggerFactory.getLogger(PolygonStringFormat.class);\n    private final String format;\n\n    public static PolygonStringFormat getEnumForFormat(final String format)\n    {\n        for (final PolygonStringFormat polygonStringFormat : values())\n        {\n            if (polygonStringFormat.getFormat().equalsIgnoreCase(format))\n            {\n                return polygonStringFormat;\n            }\n        }\n        return UNSUPPORTED;\n    }\n\n    PolygonStringFormat(final String format)\n    {\n        this.format = format;\n    }\n\n    public String getFormat()\n    {\n        return this.format;\n    }\n\n    public StringConverter<Optional<List<MultiPolygon>>> getMultiPolygonConverter()\n    {\n        switch (this)\n        {\n            case ATLAS:\n                return string -> Optional.of(Collections\n                        .singletonList(new MultiPolygonStringConverter().convert(string)));\n            case GEOJSON:\n                return string ->\n                {\n                    final List<MultiPolygon> multiPolygons = new ArrayList<>();\n                    final GeoJsonReader reader = new GeoJsonReader(new StringResource(string));\n                    while (reader.hasNext())\n                    {\n                        final PropertiesLocated propertiesLocated = reader.next();\n                        if (propertiesLocated.getItem() instanceof MultiPolygon)\n                        {\n                            multiPolygons.add((MultiPolygon) propertiesLocated.getItem());\n                        }\n                        else\n                        {\n                            logger.warn(\"MultiPolygon Filter does not support item {}\",\n                                    propertiesLocated.toString());\n                        }\n                    }\n                    return Optional.of(multiPolygons).filter(list -> !list.isEmpty());\n                };\n            case WKB:\n                return string -> Optional\n                        .of(Collections.singletonList(new WkbMultiPolygonConverter()\n                                .backwardConvert(WKBReader.hexToBytes(string))));\n            case WKT:\n                return string -> Optional.of(Collections\n                        .singletonList(new WktMultiPolygonConverter().backwardConvert(string)));\n            case UNSUPPORTED:\n            default:\n                logger.warn(\"No converter set up for {} format. Supported formats are {}\",\n                        this.format, Arrays.copyOf(values(), values().length - 1));\n                return string -> Optional.empty();\n        }\n    }\n\n    public StringConverter<Optional<List<Polygon>>> getPolygonConverter()\n    {\n        switch (this)\n        {\n            case ATLAS:\n                return string -> Optional.of(\n                        Collections.singletonList(new PolygonStringConverter().convert(string)));\n            case GEOJSON:\n                return string ->\n                {\n                    final List<Polygon> polygons = new ArrayList<>();\n                    final GeoJsonReader reader = new GeoJsonReader(new StringResource(string));\n                    while (reader.hasNext())\n                    {\n                        final PropertiesLocated propertiesLocated = reader.next();\n                        if (propertiesLocated.getItem() instanceof Polygon)\n                        {\n                            polygons.add((Polygon) propertiesLocated.getItem());\n                        }\n                        else\n                        {\n                            logger.warn(\"Polygon Filter does not support item {}\",\n                                    propertiesLocated.toString());\n                        }\n                    }\n                    return Optional.of(polygons).filter(list -> !list.isEmpty());\n                };\n            case WKT:\n                return string -> Optional.of(Collections\n                        .singletonList(new WktPolygonConverter().backwardConvert(string)));\n            case WKB:\n                return string -> Optional.of(Collections.singletonList(\n                        new WkbPolygonConverter().backwardConvert(WKBReader.hexToBytes(string))));\n            case UNSUPPORTED:\n            default:\n                logger.warn(\"No converter set up for {} format. Supported formats are {}\",\n                        this.format, Arrays.copyOf(values(), values().length - 1));\n                return string -> Optional.empty();\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return getFormat();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WkMultiPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Abstract converter that underpins {@link WktMultiPolygonConverter} and\n * {@link WkbMultiPolygonConverter} to reduce code duplication.\n *\n * @author matthieun\n * @param <T>\n *            The type to convert to: {@link String} or byte[]\n */\npublic abstract class WkMultiPolygonConverter<T> implements TwoWayConverter<MultiPolygon, T>\n{\n    private static final JtsPolygonToMultiPolygonConverter POLYGON_TO_MULTI_POLYGON_CONVERTER = new JtsPolygonToMultiPolygonConverter();\n    private static final JtsMultiPolygonToMultiPolygonConverter MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n\n    @Override\n    public MultiPolygon backwardConvert(final T wkt)\n    {\n        try\n        {\n            final Geometry result = getGeometryConverter().convert(wkt);\n            if (result instanceof org.locationtech.jts.geom.Polygon)\n            {\n                final org.locationtech.jts.geom.Polygon jtsPolygon = (org.locationtech.jts.geom.Polygon) result;\n                return POLYGON_TO_MULTI_POLYGON_CONVERTER.convert(jtsPolygon);\n            }\n            else if (result instanceof org.locationtech.jts.geom.MultiPolygon)\n            {\n                return MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER\n                        .convert((org.locationtech.jts.geom.MultiPolygon) result);\n            }\n            else\n            {\n                throw new CoreException(\"Unknown type: {}\", result.getClass().getCanonicalName());\n            }\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Cannot parse wkt : {}\", wkt, e);\n        }\n    }\n\n    @Override\n    public T convert(final MultiPolygon multiPolygon)\n    {\n        final org.locationtech.jts.geom.Geometry geometry;\n        if (multiPolygon.outers().size() > 1)\n        {\n            geometry = MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(multiPolygon);\n        }\n        else\n        {\n            // JTS Polygons can have one outer and some holes\n            geometry = POLYGON_TO_MULTI_POLYGON_CONVERTER.backwardConvert(multiPolygon);\n        }\n        return getGeometryConverter().backwardConvert(geometry);\n    }\n\n    abstract TwoWayConverter<T, Geometry> getGeometryConverter();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WkbLocationConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKBReader;\nimport org.locationtech.jts.io.WKBWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a {@link Location} to/from Well Known Binary (WKB).\n *\n * @author matthieun\n */\npublic class WkbLocationConverter implements TwoWayConverter<Location, byte[]>\n{\n    @Override\n    public Location backwardConvert(final byte[] wkb)\n    {\n        Point geometry = null;\n        final WKBReader myReader = new WKBReader();\n        try\n        {\n            geometry = (Point) myReader.read(wkb);\n        }\n        catch (final ParseException | ClassCastException e)\n        {\n            throw new CoreException(\"Cannot parse wkb : {}\", WKBWriter.toHex(wkb), e);\n        }\n        return new JtsPointConverter().backwardConvert(geometry);\n    }\n\n    @Override\n    public byte[] convert(final Location location)\n    {\n        final Geometry geometry = new JtsPointConverter().convert(location);\n        final byte[] wkb = new WKBWriter().write(geometry);\n        return wkb;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WkbMultiPolyLineConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.MultiLineString;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKBReader;\nimport org.locationtech.jts.io.WKBWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolyLine;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolyLineConverter;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * A class for converting between MultiLineStrings in WKB format and {@link MultiPolyLine}\n *\n * @author jklamer\n */\npublic class WkbMultiPolyLineConverter implements TwoWayConverter<MultiPolyLine, byte[]>\n{\n    private static final WKBReader WKB_READER = new WKBReader();\n\n    @Override\n    public MultiPolyLine backwardConvert(final byte[] wkb)\n    {\n        MultiLineString geometry = null;\n        try\n        {\n            geometry = (MultiLineString) WKB_READER.read(wkb);\n        }\n        catch (final ParseException | ClassCastException e)\n        {\n            throw new CoreException(\"Cannot parse wkb : {}\", WKBWriter.toHex(wkb), e);\n        }\n        return new JtsMultiPolyLineConverter().backwardConvert(geometry);\n    }\n\n    @Override\n    public byte[] convert(final MultiPolyLine multiPolyLine)\n    {\n        final Geometry geometry = new JtsMultiPolyLineConverter().convert(multiPolyLine);\n        return new WKBWriter().write(geometry);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WkbMultiPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKBReader;\nimport org.locationtech.jts.io.WKBWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Converter class for conversion between Wkb byte array and {@link MultiPolygon}\n *\n * @author jklamer\n * @author matthieun\n */\npublic class WkbMultiPolygonConverter extends WkMultiPolygonConverter<byte[]>\n{\n    private static final TwoWayConverter<byte[], Geometry> CONVERTER = new TwoWayConverter<byte[], Geometry>()\n    {\n        @Override\n        public byte[] backwardConvert(final Geometry geometry)\n        {\n            return new WKBWriter().write(geometry);\n        }\n\n        @Override\n        public Geometry convert(final byte[] kyte)\n        {\n            try\n            {\n                return new WKBReader().read(kyte);\n            }\n            catch (final ParseException e)\n            {\n                throw new CoreException(\"Unable to parse WKB\", e);\n            }\n        }\n    };\n\n    @Override\n    TwoWayConverter<byte[], Geometry> getGeometryConverter()\n    {\n        return CONVERTER;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WkbPolyLineConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKBReader;\nimport org.locationtech.jts.io.WKBWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Conversion from a {@link PolyLine} into a Well Known Binary (WKB) byte array. Uses JTS for\n * Coordinate creation and WKB creation and writing.\n *\n * @author robert_stack\n */\npublic class WkbPolyLineConverter implements TwoWayConverter<PolyLine, byte[]>\n{\n    @Override\n    public PolyLine backwardConvert(final byte[] wkb)\n    {\n        PolyLine polyLine = null;\n        Geometry geometry = null;\n        final WKBReader myReader = new WKBReader();\n        try\n        {\n            geometry = myReader.read(wkb);\n        }\n        catch (final ParseException e)\n        {\n            throw new CoreException(\"Cannot parse wkb : {}\", WKBWriter.toHex(wkb), e);\n        }\n\n        final Coordinate[] coordinates = geometry.getCoordinates();\n        final List<Location> locations = new ArrayList<>();\n        for (int i = 0; i < coordinates.length; i++)\n        {\n            // y = latitude, x = longitude from JTS Coordinate format\n            locations.add(new Location(Latitude.degrees(coordinates[i].y),\n                    Longitude.degrees(coordinates[i].x)));\n        }\n        polyLine = new PolyLine(locations);\n\n        return polyLine;\n    }\n\n    @Override\n    public byte[] convert(final PolyLine polyLine)\n    {\n        final Geometry geometry;\n        final List<Coordinate> cooordinates = new ArrayList<>();\n        for (final Location location : polyLine)\n        {\n            // swap latitude/longitude for JTS Coordinate format\n            cooordinates.add(new Coordinate(location.getLongitude().asDegrees(),\n                    location.getLatitude().asDegrees()));\n        }\n        final Coordinate[] coordinateArray = cooordinates\n                .toArray(new Coordinate[cooordinates.size()]);\n        if (coordinateArray.length == 1)\n        {\n            geometry = new GeometryFactory().createPoint(coordinateArray[0]);\n        }\n        else\n        {\n            geometry = new GeometryFactory().createLineString(coordinateArray);\n        }\n\n        final byte[] wkb = new WKBWriter().write(geometry);\n\n        return wkb;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WkbPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKBReader;\nimport org.locationtech.jts.io.WKBWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert Polygons to a Well Known Binary (WKB) byte array. Polygons retain only one of first\n * versus last point (which are the same for a closed loop).\n *\n * @author Sid\n * @author cstaylor\n */\npublic class WkbPolygonConverter implements TwoWayConverter<Polygon, byte[]>\n{\n    @Override\n    public Polygon backwardConvert(final byte[] wkb)\n    {\n        org.locationtech.jts.geom.Polygon geometry = null;\n        final WKBReader myReader = new WKBReader();\n        try\n        {\n            geometry = (org.locationtech.jts.geom.Polygon) myReader.read(wkb);\n        }\n        catch (final ParseException | ClassCastException e)\n        {\n            throw new CoreException(\"Cannot parse wkb : {}\", WKBWriter.toHex(wkb), e);\n        }\n        return new JtsPolygonConverter().backwardConvert(geometry);\n    }\n\n    @Override\n    public byte[] convert(final Polygon polygon)\n    {\n        final Geometry geometry = new JtsPolygonConverter().convert(polygon);\n        final byte[] wkb = new WKBWriter().write(geometry);\n        return wkb;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WktLocationConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.locationtech.jts.io.WKTWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a {@link Location} to/from Well Known Text (WKT).\n *\n * @author matthieun\n */\npublic class WktLocationConverter implements TwoWayConverter<Location, String>\n{\n    @Override\n    public Location backwardConvert(final String wkt)\n    {\n        Point geometry = null;\n        final WKTReader myReader = new WKTReader();\n        try\n        {\n            geometry = (Point) myReader.read(wkt);\n        }\n        catch (final ParseException | ClassCastException e)\n        {\n            throw new CoreException(\"Cannot parse wkt : {}\", wkt, e);\n        }\n        return new JtsPointConverter().backwardConvert(geometry);\n    }\n\n    @Override\n    public String convert(final Location location)\n    {\n        final Geometry geometry = new JtsPointConverter().convert(location);\n        final String wkt = new WKTWriter().write(geometry);\n        return wkt;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WktMultiPolyLineConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.MultiLineString;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.locationtech.jts.io.WKTWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolyLine;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolyLineConverter;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Given an WKT string generate a {@link MultiLineString} and vice-versa\n *\n * @author yalimu\n */\npublic class WktMultiPolyLineConverter implements TwoWayConverter<MultiPolyLine, String>\n{\n    @Override\n    public MultiPolyLine backwardConvert(final String wkt)\n    {\n        MultiLineString geometry = null;\n        final WKTReader myReader = new WKTReader();\n        try\n        {\n            geometry = (MultiLineString) myReader.read(wkt);\n        }\n        catch (final ParseException | ClassCastException e)\n        {\n            throw new CoreException(\"Cannot parse wkt : {}\", wkt, e);\n        }\n        return new JtsMultiPolyLineConverter().backwardConvert(geometry);\n    }\n\n    @Override\n    public String convert(final MultiPolyLine multiPolyLine)\n    {\n        final MultiLineString geometry = new JtsMultiPolyLineConverter().convert(multiPolyLine);\n        return new WKTWriter().write(geometry);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WktMultiPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.locationtech.jts.io.WKTWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Given an WKT string generate a {@link MultiPolygon} and vice-versa\n *\n * @author matthieun\n */\npublic class WktMultiPolygonConverter extends WkMultiPolygonConverter<String>\n{\n    private static final TwoWayConverter<String, Geometry> CONVERTER = new TwoWayConverter<String, Geometry>()\n    {\n        @Override\n        public String backwardConvert(final Geometry geometry)\n        {\n            return new WKTWriter().write(geometry);\n        }\n\n        @Override\n        public Geometry convert(final String wkt)\n        {\n            try\n            {\n                return new WKTReader().read(wkt);\n            }\n            catch (final ParseException e)\n            {\n                throw new CoreException(\"Unable to parse WKT\", e);\n            }\n        }\n    };\n\n    @Override\n    TwoWayConverter<String, Geometry> getGeometryConverter()\n    {\n        return CONVERTER;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WktPolyLineConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.locationtech.jts.io.WKTWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Given an WKT string generate a {@link PolyLine} and vice-versa\n *\n * @author matthieun\n */\npublic class WktPolyLineConverter implements TwoWayConverter<PolyLine, String>\n{\n    @Override\n    public PolyLine backwardConvert(final String wkt)\n    {\n        LineString geometry = null;\n        final WKTReader myReader = new WKTReader();\n        try\n        {\n            geometry = (LineString) myReader.read(wkt);\n        }\n        catch (final ParseException | ClassCastException e)\n        {\n            throw new CoreException(\"Cannot parse wkt : {}\", wkt, e);\n        }\n        return new JtsPolyLineConverter().backwardConvert(geometry);\n    }\n\n    @Override\n    public String convert(final PolyLine polyLine)\n    {\n        final Geometry geometry = new JtsPolyLineConverter().convert(polyLine);\n        return new WKTWriter().write(geometry);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/WktPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.locationtech.jts.io.WKTWriter;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Given an WKT string generate a Polygon and vice-versa\n *\n * @author cstaylor\n * @author matthieun\n */\npublic class WktPolygonConverter implements TwoWayConverter<Polygon, String>\n{\n    private static final JtsMultiPolygonToMultiPolygonConverter JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER = new JtsMultiPolygonToMultiPolygonConverter();\n\n    @Override\n    public Polygon backwardConvert(final String wkt)\n    {\n        final WKTReader myReader = new WKTReader();\n        try\n        {\n            final Geometry result = myReader.read(wkt);\n            if (result instanceof org.locationtech.jts.geom.Polygon)\n            {\n                return new JtsPolygonConverter()\n                        .backwardConvert((org.locationtech.jts.geom.Polygon) result);\n            }\n            else if (result instanceof org.locationtech.jts.geom.MultiPolygon)\n            {\n                final MultiPolygon castResult = JTS_MULTI_POLYGON_TO_MULTI_POLYGON_CONVERTER\n                        .convert((org.locationtech.jts.geom.MultiPolygon) result);\n                if (castResult.outers().size() == 1 && castResult.inners().isEmpty())\n                {\n                    return castResult.outers().iterator().next();\n                }\n            }\n            throw new CoreException(\n                    \"Cannot convert wkt which is not a Polygon or single-outer MultiPolygon: {}\",\n                    wkt);\n        }\n        catch (final ParseException e)\n        {\n            throw new CoreException(\"Cannot parse wkt: {}\", wkt, e);\n        }\n    }\n\n    @Override\n    public String convert(final Polygon polygon)\n    {\n        final Geometry geometry = new JtsPolygonConverter().convert(polygon);\n        return new WKTWriter().write(geometry);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/GeometryStreamer.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryCollection;\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Transform a JTS {@link GeometryCollection} into a real {@link Iterable}.\n *\n * @author matthieun\n */\npublic final class GeometryStreamer\n{\n    /**\n     * @param collection\n     *            The collection to stream\n     * @return The JTS {@link Geometry} {@link Iterable}\n     */\n    public static Iterable<Geometry> stream(final GeometryCollection collection)\n    {\n        final int size = collection.getNumGeometries();\n        return Iterables.indexBasedIterable(size, index -> collection.getGeometryN((int) index));\n    }\n\n    /**\n     * @param collection\n     *            The collection to stream\n     * @return The JTS {@link Geometry} {@link Iterable} as JTS {@link Polygon}s\n     */\n    public static Iterable<Polygon> streamPolygons(final GeometryCollection collection)\n    {\n        return Iterables.translate(stream(collection), geometry -> (Polygon) geometry);\n    }\n\n    private GeometryStreamer()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsCoordinateArrayConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.CoordinateSequence;\nimport org.locationtech.jts.geom.impl.CoordinateArraySequence;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert an {@link Iterable} of {@link Location} to a {@link CoordinateSequence} from the JTS\n * library.\n *\n * @author matthieun\n */\npublic class JtsCoordinateArrayConverter\n        implements TwoWayConverter<Iterable<Location>, CoordinateSequence>\n{\n    private static final JtsLocationConverter LOCATION_CONVERTER = new JtsLocationConverter();\n\n    public static CoordinateSequence empty()\n    {\n        final Coordinate[] emptyCoordinateArray = new Coordinate[0];\n        return new CoordinateArraySequence(emptyCoordinateArray);\n    }\n\n    @Override\n    public Iterable<Location> backwardConvert(final CoordinateSequence coordinateSequence)\n    {\n        final List<Location> result = new ArrayList<>();\n        for (final Coordinate coordinate : coordinateSequence.toCoordinateArray())\n        {\n            result.add(LOCATION_CONVERTER.backwardConvert(coordinate));\n        }\n        return result;\n    }\n\n    @Override\n    public CoordinateSequence convert(final Iterable<Location> locations)\n    {\n        final int size;\n        if (locations instanceof Collection)\n        {\n            size = ((Collection<Location>) locations).size();\n        }\n        else\n        {\n            size = (int) Iterables.size(locations);\n        }\n        final Coordinate[] result = new Coordinate[size];\n        int index = 0;\n        for (final Location location : locations)\n        {\n            result[index++] = LOCATION_CONVERTER.convert(location);\n        }\n        return new CoordinateArraySequence(result);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsLinearRingConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.CoordinateSequence;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.locationtech.jts.geom.impl.CoordinateArraySequence;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a {@link Polygon} to a {@link LinearRing} from the JTS library. It is worth noting that a\n * {@link Polygon} assumes that the last point is connected to the first point, and hence does not\n * supply the first point twice. However the JTS {@link LinearRing} implementation assumes that the\n * ring has to be closed, i.e. that the last point and the first point have to be the same. If this\n * is not the case, creating the {@link LinearRing} will fail. This converter accounts for that both\n * ways.\n *\n * @author matthieun\n */\npublic class JtsLinearRingConverter implements TwoWayConverter<Polygon, LinearRing>\n{\n    private static final JtsCoordinateArrayConverter COORDINATE_ARRAY_CONVERTER = new JtsCoordinateArrayConverter();\n    private static final GeometryFactory FACTORY = JtsPrecisionManager.getGeometryFactory();\n    // Protect from: java.lang.IllegalArgumentException: Invalid number of points in LinearRing\n    // (found x - must be 0 or >= 4)\n    private static final int MINIMUM_LINEAR_RING_SIZE = 4;\n\n    public static LinearRing empty()\n    {\n        return new LinearRing(JtsCoordinateArrayConverter.empty(), FACTORY);\n    }\n\n    @Override\n    public Polygon backwardConvert(final LinearRing object)\n    {\n        final CoordinateSequence sequence = object.getCoordinateSequence();\n        if (sequence.size() <= 0)\n        {\n            // Cannot have an empty polygon.\n            return null;\n        }\n        final Coordinate[] newArray = new Coordinate[sequence.size() - 1];\n        for (int i = 0; i < newArray.length; i++)\n        {\n            newArray[i] = sequence.getCoordinate(i);\n        }\n        return new Polygon(\n                COORDINATE_ARRAY_CONVERTER.backwardConvert(new CoordinateArraySequence(newArray)));\n    }\n\n    @Override\n    public LinearRing convert(final Polygon object)\n    {\n        final List<Location> locations = new ArrayList<>(object);\n        // Hack to close the loop, as JTS expects it...\n        locations.add(locations.get(0));\n        while (locations.size() < MINIMUM_LINEAR_RING_SIZE)\n        {\n            locations.add(locations.get(0));\n        }\n        return new LinearRing(COORDINATE_ARRAY_CONVERTER.convert(locations), FACTORY);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsLocationConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport org.locationtech.jts.geom.Coordinate;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a {@link Location} to a {@link Coordinate} from the JTS library. The {@link Location}'s\n * {@link Latitude} dm7 value becomes the {@link Coordinate}'s y value, and the {@link Location}'s\n * {@link Longitude} dm7 value becomes the the {@link Coordinate}'s x value.\n *\n * @author matthieun\n */\npublic class JtsLocationConverter implements TwoWayConverter<Location, Coordinate>\n{\n    @Override\n    public Location backwardConvert(final Coordinate coordinate)\n    {\n        return new Location(Latitude.degrees(coordinate.y), Longitude.degrees(coordinate.x));\n    }\n\n    @Override\n    public Coordinate convert(final Location location)\n    {\n        return new Coordinate(location.getLongitude().asDegrees(),\n                location.getLatitude().asDegrees());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsMultiPolyLineConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.MultiLineString;\nimport org.openstreetmap.atlas.geography.MultiPolyLine;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a {@link MultiPolyLine} to a JTS {@link MultiLineString}.\n *\n * @author yalimu\n */\npublic class JtsMultiPolyLineConverter implements TwoWayConverter<MultiPolyLine, MultiLineString>\n{\n    private static final JtsCoordinateArrayConverter COORDINATE_ARRAY_CONVERTER = new JtsCoordinateArrayConverter();\n    private static final JtsPolyLineConverter POLYLINE_CONVERTER = new JtsPolyLineConverter();\n    private static final GeometryFactory FACTORY = JtsPrecisionManager.getGeometryFactory();\n\n    @Override\n    public MultiPolyLine backwardConvert(final MultiLineString multiLineString)\n    {\n        final List<PolyLine> polyLineList = new ArrayList<>();\n        for (int i = 0; i < multiLineString.getNumGeometries(); i++)\n        {\n            final LineString lineString = (LineString) multiLineString.getGeometryN(i);\n            final PolyLine polyLine = new PolyLine(\n                    COORDINATE_ARRAY_CONVERTER.backwardConvert(lineString.getCoordinateSequence()));\n            // No duplicated polyline is allowed to add.\n            if (!polyLineList.contains(polyLine))\n            {\n                polyLineList.add(polyLine);\n            }\n        }\n        return new MultiPolyLine(polyLineList);\n    }\n\n    @Override\n    public MultiLineString convert(final MultiPolyLine multiPolyLine)\n    {\n        final List<LineString> lineStringList = Iterables.stream(multiPolyLine)\n                .map(POLYLINE_CONVERTER::convert).collectToList();\n        return new MultiLineString(lineStringList.toArray(new LineString[lineStringList.size()]),\n                FACTORY);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsMultiPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * Convert a {@link MultiPolygon} to a {@link Set} of {@link org.locationtech.jts.geom.Polygon} form\n * the JTS library. As a {@link MultiPolygon} can contain many outer bounds, each outer bound is\n * translated to a {@link org.locationtech.jts.geom.Polygon}. A JTS\n * {@link org.locationtech.jts.geom.Polygon} is one single outer bound and many inner bounds.\n *\n * @author matthieun\n */\npublic class JtsMultiPolygonConverter\n        implements TwoWayConverter<MultiPolygon, Set<org.locationtech.jts.geom.Polygon>>\n{\n    private static final JtsLinearRingConverter LINEAR_RING_CONVERTER = new JtsLinearRingConverter();\n    private static final JtsPolygonConverter POLYGON_CONVERTER = new JtsPolygonConverter();\n    private static final GeometryFactory FACTORY = JtsPrecisionManager.getGeometryFactory();\n\n    @Override\n    public MultiPolygon backwardConvert(final Set<org.locationtech.jts.geom.Polygon> object)\n    {\n        final MultiMap<Polygon, Polygon> result = new MultiMap<>();\n        for (final org.locationtech.jts.geom.Polygon polygon : object)\n        {\n            final Polygon outer = POLYGON_CONVERTER.backwardConvert(polygon);\n            if (outer == null)\n            {\n                continue;\n            }\n            for (int n = 0; n < polygon.getNumInteriorRing(); n++)\n            {\n                final LinearRing ring = new LinearRing(\n                        polygon.getInteriorRingN(n).getCoordinateSequence(), FACTORY);\n                final Polygon inner = LINEAR_RING_CONVERTER.backwardConvert(ring);\n                result.add(outer, inner);\n            }\n            if (polygon.getNumInteriorRing() == 0)\n            {\n                // Make sure the outer still exists if the inners are not there.\n                result.put(outer, new ArrayList<>());\n            }\n        }\n        return new MultiPolygon(result);\n    }\n\n    @Override\n    public Set<org.locationtech.jts.geom.Polygon> convert(final MultiPolygon object)\n    {\n        final Set<org.locationtech.jts.geom.Polygon> result = new HashSet<>();\n        for (final Polygon outer : object.outers())\n        {\n            final List<Polygon> inners = object.innersOf(outer);\n            final LinearRing[] holes = new LinearRing[inners.size()];\n            int index = 0;\n            for (final Polygon inner : inners)\n            {\n                holes[index++] = LINEAR_RING_CONVERTER.convert(inner);\n            }\n            result.add(new org.locationtech.jts.geom.Polygon(LINEAR_RING_CONVERTER.convert(outer),\n                    holes, FACTORY));\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsMultiPolygonToMultiLineStringConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryCollection;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeRelation;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * this is a somewhat specific use case converter used by {@link ChangeRelation}\n *\n * @author samuelgass\n */\npublic class JtsMultiPolygonToMultiLineStringConverter\n        implements TwoWayConverter<MultiPolygon, GeometryCollection>\n{\n\n    @Override\n    public MultiPolygon backwardConvert(final GeometryCollection object)\n    {\n        final Polygon[] polygons = new Polygon[object.getNumGeometries()];\n        for (int i = 0; i < polygons.length; i++)\n        {\n            polygons[i] = (Polygon) object.getGeometryN(i);\n        }\n        return new MultiPolygon(polygons, JtsPrecisionManager.getGeometryFactory());\n    }\n\n    @Override\n    public GeometryCollection convert(final MultiPolygon object)\n    {\n        final List<LineString> linestrings = new ArrayList<>();\n        for (int i = 0; i < object.getNumGeometries(); i++)\n        {\n            final Polygon part = (Polygon) object.getGeometryN(i);\n            linestrings.add(part.getExteriorRing());\n            for (int j = 0; j < part.getNumInteriorRing(); j++)\n            {\n                linestrings.add(part.getInteriorRingN(j));\n            }\n        }\n        Geometry[] geometries = new Geometry[linestrings.size()];\n        geometries = linestrings.toArray(geometries);\n        return new GeometryCollection(geometries, JtsPrecisionManager.getGeometryFactory());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsMultiPolygonToMultiPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * @author matthieun\n */\npublic class JtsMultiPolygonToMultiPolygonConverter\n        implements TwoWayConverter<org.locationtech.jts.geom.MultiPolygon, MultiPolygon>\n{\n    private static final JtsMultiPolygonConverter JTS_MULTI_POLYGON_CONVERTER = new JtsMultiPolygonConverter();\n    private static final GeometryFactory FACTORY = JtsPrecisionManager.getGeometryFactory();\n\n    @Override\n    public org.locationtech.jts.geom.MultiPolygon backwardConvert(final MultiPolygon object)\n    {\n        final Polygon[] polygons = JTS_MULTI_POLYGON_CONVERTER.convert(object)\n                .toArray(new Polygon[0]);\n        return new org.locationtech.jts.geom.MultiPolygon(polygons, FACTORY);\n    }\n\n    @Override\n    public MultiPolygon convert(final org.locationtech.jts.geom.MultiPolygon object)\n    {\n        final int numberGeometries = object.getNumGeometries();\n        final Set<Polygon> polygons = new HashSet<>();\n        for (int index = 0; index < numberGeometries; index++)\n        {\n            final Polygon polygon = (Polygon) object.getGeometryN(index);\n            polygons.add(polygon);\n        }\n        return JTS_MULTI_POLYGON_CONVERTER.backwardConvert(polygons);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsPointConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.geom.impl.CoordinateArraySequence;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a {@link Location} to a {@link Point} from the JTS library. The {@link Location}'s\n * {@link Latitude} dm7 value becomes the {@link Point}'s y value, and the {@link Location}'s\n * {@link Longitude} dm7 value becomes the the {@link Point}'s x value.\n *\n * @author mgostintsev\n */\npublic class JtsPointConverter implements TwoWayConverter<Location, Point>\n{\n    public static final GeometryFactory GEOMETRY_FACTORY = JtsPrecisionManager.getGeometryFactory();\n    private static final JtsLocationConverter LOCATION_CONVERTER = new JtsLocationConverter();\n\n    @Override\n    public Location backwardConvert(final Point point)\n    {\n        return new Location(Latitude.degrees(point.getY()), Longitude.degrees(point.getX()));\n    }\n\n    @Override\n    public Point convert(final Location location)\n    {\n        final Coordinate coordinate = LOCATION_CONVERTER.convert(location);\n        final CoordinateArraySequence sequence = new CoordinateArraySequence(\n                new Coordinate[] { coordinate });\n        return new Point(sequence, GEOMETRY_FACTORY);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsPolyLineConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a {@link PolyLine} to a JTS {@link LineString}.\n *\n * @author matthieun\n */\npublic class JtsPolyLineConverter implements TwoWayConverter<PolyLine, LineString>\n{\n    private static final JtsCoordinateArrayConverter COORDINATE_ARRAY_CONVERTER = new JtsCoordinateArrayConverter();\n    private static final GeometryFactory FACTORY = JtsPrecisionManager.getGeometryFactory();\n\n    @Override\n    public PolyLine backwardConvert(final LineString lineString)\n    {\n        return new PolyLine(\n                COORDINATE_ARRAY_CONVERTER.backwardConvert(lineString.getCoordinateSequence()));\n    }\n\n    @Override\n    public LineString convert(final PolyLine polyLine)\n    {\n        /*\n         * Occasionally this method may be called with a PolyLine that is actually a Polygon. In\n         * those cases, we want to properly return a LinearRing so as not to break downstream code\n         * that expects Polygon-like closed-ness.\n         */\n        if (polyLine instanceof Polygon)\n        {\n            final Polygon polygon = (Polygon) polyLine;\n            return new LinearRing(\n                    COORDINATE_ARRAY_CONVERTER.convert(new PolyLine(polygon.closedLoop())),\n                    FACTORY);\n        }\n        return new LineString(COORDINATE_ARRAY_CONVERTER.convert(polyLine), FACTORY);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a {@link Polygon} to a JTS {@link org.locationtech.jts.geom.Polygon}. Here the inner\n * bounds are left empty. When converting backwards, if there is an inner bound in the\n * {@link org.locationtech.jts.geom.Polygon}, it will be ignored.\n *\n * @author matthieun\n */\npublic class JtsPolygonConverter\n        implements TwoWayConverter<Polygon, org.locationtech.jts.geom.Polygon>\n{\n    private static final JtsLinearRingConverter LINEAR_RING_CONVERTER = new JtsLinearRingConverter();\n    private static final GeometryFactory FACTORY = JtsPrecisionManager.getGeometryFactory();\n\n    @Override\n    public Polygon backwardConvert(final org.locationtech.jts.geom.Polygon object)\n    {\n        return LINEAR_RING_CONVERTER.backwardConvert((LinearRing) object.getExteriorRing());\n    }\n\n    @Override\n    public org.locationtech.jts.geom.Polygon convert(final Polygon object)\n    {\n        return new org.locationtech.jts.geom.Polygon(LINEAR_RING_CONVERTER.convert(object),\n                new LinearRing[0], FACTORY);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsPolygonToMultiPolygonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * @author matthieun\n */\npublic class JtsPolygonToMultiPolygonConverter\n        implements TwoWayConverter<org.locationtech.jts.geom.Polygon, MultiPolygon>\n{\n    private static final JtsLinearRingConverter LINEAR_RING_CONVERTER = new JtsLinearRingConverter();\n    private static final GeometryFactory FACTORY = JtsPrecisionManager.getGeometryFactory();\n\n    @Override\n    public org.locationtech.jts.geom.Polygon backwardConvert(final MultiPolygon object)\n    {\n        if (object.getOuterToInners().isEmpty())\n        {\n            final LinearRing[] emptyInners = new LinearRing[0];\n            return new org.locationtech.jts.geom.Polygon(JtsLinearRingConverter.empty(),\n                    emptyInners, FACTORY);\n        }\n        if (object.getOuterToInners().keySet().size() != 1)\n        {\n            throw new CoreException(\n                    \"A MultiPolygon can be converted to JTS Polygon only if it has no more than one outer ring.\");\n        }\n        final Polygon outer = object.outers().iterator().next();\n        final LinearRing linearRingOuter = LINEAR_RING_CONVERTER.convert(outer);\n        final List<Polygon> inners = object.getOuterToInners().get(outer);\n        final LinearRing[] linearRingInners = new LinearRing[inners.size()];\n        for (int index = 0; index < inners.size(); index++)\n        {\n            linearRingInners[index] = LINEAR_RING_CONVERTER.convert(inners.get(index));\n        }\n        return new org.locationtech.jts.geom.Polygon(linearRingOuter, linearRingInners, FACTORY);\n    }\n\n    @Override\n    public MultiPolygon convert(final org.locationtech.jts.geom.Polygon object)\n    {\n        final MultiMap<Polygon, Polygon> outersToInners = new MultiMap<>();\n        // Here we cannot use the reverse JtsLinearRingConverter because\n        // jts.Polygon.getExteriorRing() returns a LineString instead of a LinearRing :(\n        final List<Location> locationsOuter = (List<Location>) new JtsCoordinateArrayConverter()\n                .backwardConvert(object.getExteriorRing().getCoordinateSequence());\n        final Polygon outer = new Polygon(locationsOuter.subList(0, locationsOuter.size() - 1));\n        outersToInners.put(outer, new ArrayList<>());\n        for (int index = 0; index < object.getNumInteriorRing(); index++)\n        {\n            final List<Location> locationsInner = (List<Location>) new JtsCoordinateArrayConverter()\n                    .backwardConvert(object.getInteriorRingN(index).getCoordinateSequence());\n            final Polygon inner = new Polygon(locationsInner.subList(0, locationsInner.size() - 1));\n            outersToInners.add(outer, inner);\n        }\n        return new MultiPolygon(outersToInners);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsPrecisionManager.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.PrecisionModel;\n\n/**\n * JTS Precision utility class.\n *\n * @author Yiqing Jin\n */\npublic final class JtsPrecisionManager\n{\n    private static final int PRECISION_SCALE = 10_000_000;\n    private static PrecisionModel precisionModel;\n    private static GeometryFactory geometryFactory;\n\n    static\n    {\n        precisionModel = new PrecisionModel(PRECISION_SCALE);\n        geometryFactory = new GeometryFactory(precisionModel);\n    }\n\n    public static GeometryFactory getGeometryFactory()\n    {\n        return geometryFactory;\n    }\n\n    public static PrecisionModel getPrecisionModel()\n    {\n        return precisionModel;\n    }\n\n    private JtsPrecisionManager()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/converters/jts/JtsUtility.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport java.util.List;\n\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.CoordinateSequence;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.locationtech.jts.geom.Polygon;\nimport org.locationtech.jts.geom.impl.CoordinateArraySequence;\n\n/**\n * A simple utility for working with JTS objects.\n *\n * @author Yiqing Jin\n */\npublic final class JtsUtility\n{\n    public static final GeometryFactory GEOMETRY_FACTORY = JtsPrecisionManager.getGeometryFactory();\n    private static final int MININMUM_NUMBER_OF_POLYGON_POINTS = 4;\n\n    public static LineString buildLineString(final Coordinate[] coordinates)\n    {\n        final CoordinateArraySequence sequence = new CoordinateArraySequence(coordinates);\n        return new LineString(sequence, GEOMETRY_FACTORY);\n    }\n\n    public static LinearRing buildLinearRing(final List<Coordinate> coordinates)\n    {\n        final Coordinate[] coordinateArray = coordinates\n                .toArray(new Coordinate[coordinates.size()]);\n        final CoordinateArraySequence sequence = new CoordinateArraySequence(coordinateArray);\n        return new LinearRing(sequence, GEOMETRY_FACTORY);\n    }\n\n    public static LinearRing buildLinearRing(final CoordinateSequence sequence)\n    {\n        return new LinearRing(sequence, GEOMETRY_FACTORY);\n    }\n\n    public static Polygon toPolygon(final Coordinate[] coordinates)\n    {\n        if (coordinates.length < MININMUM_NUMBER_OF_POLYGON_POINTS && coordinates.length != 0)\n        {\n            // An invalid polygon. one example A->B->A\n            return null;\n        }\n\n        final CoordinateArraySequence sequence = new CoordinateArraySequence(coordinates);\n\n        final LinearRing shell = new LinearRing(sequence, GEOMETRY_FACTORY);\n        return new Polygon(shell, new LinearRing[] {}, GEOMETRY_FACTORY);\n    }\n\n    public static Polygon toPolygon(final List<Coordinate> coordinates)\n    {\n        return toPolygon(coordinates.toArray(new Coordinate[coordinates.size()]));\n    }\n\n    private JtsUtility()\n    {\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/coordinates/EarthCenteredEarthFixedCoordinate.java",
    "content": "package org.openstreetmap.atlas.geography.coordinates;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.converters.GeodeticEarthCenteredEarthFixedConverter;\n\n/**\n * Earth-Fixed, Earth-Centered (ECEF) coordinate system representation. This is a right-handed\n * Cartesian coordinate system (think X, Y and Z) with the origin at the Earth's center. Note: Units\n * are generally expressed in meters.\n *\n * @author mgostintsev\n */\npublic class EarthCenteredEarthFixedCoordinate implements Serializable\n{\n    private static final long serialVersionUID = -3091871010423428109L;\n\n    private static final GeodeticEarthCenteredEarthFixedConverter COORDINATE_CONVERTER = new GeodeticEarthCenteredEarthFixedConverter();\n\n    private final double xValue;\n    private final double yValue;\n    private final double zValue;\n\n    /**\n     * Constructs an {@link EarthCenteredEarthFixedCoordinate} at (0,0,0).\n     */\n    public EarthCenteredEarthFixedCoordinate()\n    {\n        this.xValue = 0;\n        this.yValue = 0;\n        this.zValue = 0;\n    }\n\n    /**\n     * Constructs an {@link EarthCenteredEarthFixedCoordinate} at given (x,y,z).\n     *\n     * @param xValue\n     *            x-value\n     * @param yValue\n     *            y-value\n     * @param zValue\n     *            z-value\n     */\n    public EarthCenteredEarthFixedCoordinate(final double xValue, final double yValue,\n            final double zValue)\n    {\n        this.xValue = xValue;\n        this.yValue = yValue;\n        this.zValue = zValue;\n    }\n\n    /**\n     * Constructs an {@link EarthCenteredEarthFixedCoordinate} at given {@link Location}.\n     *\n     * @param location\n     *            The {@link Location} of the coordinate\n     */\n    public EarthCenteredEarthFixedCoordinate(final Location location)\n    {\n        final EarthCenteredEarthFixedCoordinate coordinate = COORDINATE_CONVERTER\n                .apply(location.toGeodeticCoordinate());\n        this.xValue = coordinate.getX();\n        this.yValue = coordinate.getY();\n        this.zValue = coordinate.getZ();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof EarthCenteredEarthFixedCoordinate)\n        {\n            final EarthCenteredEarthFixedCoordinate that = (EarthCenteredEarthFixedCoordinate) other;\n            return this.getX() == that.getX() && this.getY() == that.getY()\n                    && this.getZ() == that.getZ();\n        }\n        return false;\n    }\n\n    public double getX()\n    {\n        return this.xValue;\n    }\n\n    public double getY()\n    {\n        return this.yValue;\n    }\n\n    public double getZ()\n    {\n        return this.zValue;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.getX()).append(this.getY()).append(this.getZ())\n                .hashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"(\" + this.getX() + \", \" + this.getY() + \", \" + this.getZ() + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/coordinates/GeodeticCoordinate.java",
    "content": "package org.openstreetmap.atlas.geography.coordinates;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\n\n/**\n * Geodetic coordinate system representation, consisting of a {@link Latitude}, {@link Longitude}\n * and {@link Altitude} - commonly referred to as LLA. Note: Units are generally expressed in polar\n * coordinates and meters (for {@link Altitude}}.\n *\n * @author mgostintsev\n */\npublic class GeodeticCoordinate implements Serializable\n{\n    private static final long serialVersionUID = 4614378421580938085L;\n\n    private final Latitude latitude;\n    private final Longitude longitude;\n    private final Altitude altitude;\n\n    /**\n     * Default constructor.\n     *\n     * @param latitude\n     *            latitude\n     * @param longitude\n     *            longitude\n     * @param altitude\n     *            altitude\n     */\n    public GeodeticCoordinate(final Latitude latitude, final Longitude longitude,\n            final Altitude altitude)\n    {\n        this.latitude = latitude;\n        this.longitude = longitude;\n        this.altitude = altitude;\n    }\n\n    /**\n     * Creates a {@link GeodeticCoordinate} at the given {@link Location}, at mean sea level.\n     *\n     * @param location\n     *            The {@link Location} of the coordinate.\n     */\n    public GeodeticCoordinate(final Location location)\n    {\n        this.latitude = location.getLatitude();\n        this.longitude = location.getLongitude();\n        this.altitude = Altitude.MEAN_SEA_LEVEL;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof GeodeticCoordinate)\n        {\n            final GeodeticCoordinate that = (GeodeticCoordinate) other;\n            return this.getLatitude().equals(that.getLatitude())\n                    && this.getLongitude().equals(that.getLongitude())\n                    && this.getAltitude().equals(that.getAltitude());\n        }\n        return false;\n    }\n\n    public Altitude getAltitude()\n    {\n        return this.altitude;\n    }\n\n    public Latitude getLatitude()\n    {\n        return this.latitude;\n    }\n\n    public Longitude getLongitude()\n    {\n        return this.longitude;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.getLatitude()).append(this.getLongitude())\n                .append(this.getAltitude()).hashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"(\" + this.getLatitude() + \", \" + this.getLongitude() + \", \" + this.getAltitude()\n                + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/ConcatenateGeoJsonCommand.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.readers.GeoJsonReader;\nimport org.openstreetmap.atlas.streaming.readers.json.serializers.PropertiesLocated;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Utility class to concatenate GeoJson objects.\n *\n * @author matthieun\n * @author sid\n */\npublic class ConcatenateGeoJsonCommand extends Command\n{\n    /**\n     * Mode specifies the format of the input geoJSON objects. Each file can be a GeoJSON object or\n     * file can contain multiple geoJSON objects (one per line)\n     *\n     * @author Sid\n     */\n    public enum Mode\n    {\n        // Each file is a geoJSON object\n        FILE,\n        // Each line of the file is a geoJSON object\n        LINE;\n    }\n\n    public static final Switch<File> PATH = new Switch<>(\"path\",\n            \"The folder containing the geojson files to concatenate\", File::new,\n            Optionality.REQUIRED);\n    public static final Switch<File> OUTPUT = new Switch<>(\"output\",\n            \"The file to write the concatenated geojson to\", File::new, Optionality.REQUIRED);\n    public static final Switch<Mode> MODE = new Switch<>(\"mode\",\n            \"The mode of the input geoJSON objects (FILE or LINE)\", value -> Mode.valueOf(value),\n            Optionality.REQUIRED);\n    public static final Switch<String> FILE_PREFIX = new Switch<>(\"filePrefix\",\n            \"The prefix of the input geoJSON file in LINE mode\", StringConverter.IDENTITY,\n            Optionality.OPTIONAL, \"part-\");\n\n    public static void main(final String[] args)\n    {\n        new ConcatenateGeoJsonCommand().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final File folder = (File) command.get(PATH);\n        final File output = (File) command.get(OUTPUT);\n        final Mode mode = (Mode) command.get(MODE);\n        final String filePrefix = (String) command.get(FILE_PREFIX);\n\n        // processing the files in sorted order makes testing easier\n        final List<File> files = folder.listFilesRecursively();\n        Collections.sort(files);\n        final Iterable<PropertiesLocated> jsonItems = readGeoJsonItems(mode, files, filePrefix);\n        final GeoJsonObject result = new GeoJsonBuilder()\n                .createFeatureCollectionFromPropertiesLocated(jsonItems);\n        final JsonWriter writer = new JsonWriter(output);\n        writer.write(result.jsonObject());\n        writer.close();\n        return 0;\n    }\n\n    protected Iterable<PropertiesLocated> readGeoJsonItems(final Mode mode,\n            final Iterable<File> files, final String filePrefix)\n    {\n        switch (mode)\n        {\n            case FILE:\n                return Iterables.stream(files)\n                        .filter(file -> file.getName().endsWith(FileSuffix.GEO_JSON.toString()))\n                        .flatMap(this::readGeoJsonItems);\n            case LINE:\n                return Iterables.stream(files).filter(file -> file.getName().startsWith(filePrefix))\n                        .flatMap(file -> file.lines()).map(line -> line.trim())\n                        .filter(line -> !line.isEmpty()).flatMap(this::readGeoJsonItems);\n            default:\n                throw new CoreException(\"Invalid Mode\");\n        }\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(PATH, OUTPUT, MODE, FILE_PREFIX);\n    }\n\n    private Iterable<PropertiesLocated> readGeoJsonItems(final Resource resource)\n    {\n        final Iterable<PropertiesLocated> iterableOfPropertiesLocated = () -> new GeoJsonReader(\n                resource);\n        return iterableOfPropertiesLocated;\n    }\n\n    private Iterable<PropertiesLocated> readGeoJsonItems(final String line)\n    {\n        return readGeoJsonItems(new StringResource(line));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJson.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport com.google.gson.JsonObject;\n\n/**\n * For all classes with a GeoJson representation. From the spec\n * https://tools.ietf.org/html/rfc7946#section-3\n * \n * <pre>\n *      A GeoJSON object represents a Geometry, Feature, or collection of\n *    Features.\n *\n *    o  A GeoJSON object is a JSON object.\n *\n *    o  A GeoJSON object has a member with the name \"type\".  The value of\n *       the member MUST be one of the GeoJSON types.\n *\n *    o  A GeoJSON object MAY have a \"bbox\" member, the value of which MUST\n *       be a bounding box array (see Section 5).\n *\n *    o  A GeoJSON object MAY have other members (see Section 6).\n * </pre>\n * \n * This interface is for all classes with a geojson object representation.\n *\n * @author jklamer\n */\npublic interface GeoJson\n{\n    JsonObject asGeoJson();\n\n    GeoJsonType getGeoJsonType();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonBuilder.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolyLine;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.streaming.readers.json.serializers.PropertiesLocated;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * @author matthieun\n * @author cuthbertm\n * @author mgostintsev\n * @author rmegraw\n * @author chunzhu\n */\npublic class GeoJsonBuilder\n{\n    /**\n     * Java bean to store geometry (as an {@link Iterable} of {@link Location}s) and properties as a\n     * {@link String} to {@link Object} {@link Map}\n     *\n     * @author matthieun\n     * @author rmegraw\n     */\n    public static class GeometryWithProperties\n    {\n        private final Iterable<Location> geometry;\n        private final Map<String, Object> properties;\n\n        public GeometryWithProperties(final Iterable<Location> geometry,\n                final Map<String, Object> properties)\n        {\n            this.geometry = geometry;\n            this.properties = properties;\n        }\n\n        public Iterable<Location> getGeometry()\n        {\n            return this.geometry;\n        }\n\n        public Map<String, Object> getProperties()\n        {\n            return this.properties;\n        }\n    }\n\n    /**\n     * Java bean to store the geometry (as an {@link Iterable} of {@link Location}s) and all the\n     * tags as a {@link String} to {@link String} {@link Map}\n     *\n     * @deprecated instead use {@link GeometryWithProperties}\n     * @author matthieun\n     */\n    @Deprecated\n    public static class LocationIterableProperties\n    {\n        private final Iterable<Location> locations;\n        private final Map<String, String> properties;\n\n        public LocationIterableProperties(final Iterable<Location> locations,\n                final Map<String, String> properties)\n        {\n            this.locations = locations;\n            this.properties = properties;\n        }\n\n        public Iterable<Location> getLocations()\n        {\n            return this.locations;\n        }\n\n        public Map<String, String> getProperties()\n        {\n            return this.properties;\n        }\n    }\n\n    public static final String COORDINATES = \"coordinates\";\n    public static final String FEATURE = \"Feature\";\n    public static final String FEATURES = \"features\";\n    public static final String FEATURE_COLLECTION = \"FeatureCollection\";\n    public static final String GEOMETRIES = \"geometries\";\n    public static final String GEOMETRY = \"geometry\";\n    public static final String GEOMETRY_COLLECTION = \"GeometryCollection\";\n    public static final String PROPERTIES = \"properties\";\n    public static final String TYPE = \"type\";\n    private static final Logger logger = LoggerFactory.getLogger(GeoJsonBuilder.class);\n    private final int logFrequency;\n\n    /**\n     * Converts iterable of deprecated LocationIterableProperties to iterable of\n     * GeometryWithProperties.\n     *\n     * @param objects\n     *            iterable of LocationIterableProperties\n     * @return iterable of GeometryWithProperties\n     */\n    protected static final Iterable<GeometryWithProperties> toGeometriesWithProperties(\n            final Iterable<LocationIterableProperties> objects)\n    {\n        final Iterable<GeometryWithProperties> geometriesWithProperties = Iterables\n                .translate(objects, locationIterableProperties ->\n                {\n                    return toGeometryWithProperties(locationIterableProperties);\n                });\n        return geometriesWithProperties;\n    }\n\n    /**\n     * Converts deprecated LocationIterableProperties to GeometryWithProperties.\n     *\n     * @param locationIterableProperties\n     *            LocationIterableProperties object\n     * @return GeometryWithProperties object\n     */\n    protected static final GeometryWithProperties toGeometryWithProperties(\n            final LocationIterableProperties locationIterableProperties)\n    {\n        final Map<String, Object> propertiesObjects = new HashMap<>();\n        propertiesObjects.putAll(locationIterableProperties.getProperties());\n        return new GeometryWithProperties(locationIterableProperties.getLocations(),\n                propertiesObjects);\n    }\n\n    public GeoJsonBuilder()\n    {\n        this.logFrequency = -1;\n    }\n\n    public GeoJsonBuilder(final int logFrequency)\n    {\n        this.logFrequency = logFrequency;\n    }\n\n    public GeoJsonObject create(final GeoJsonType type, final Location... locations)\n    {\n        return this.create(Iterables.iterable(locations), type);\n    }\n\n    /**\n     * Creates a Json Feature from a {@link GeometryWithProperties}\n     *\n     * @param geometryWithProperties\n     *            {@link GeometryWithProperties}\n     * @return a GeoJson Feature\n     */\n    public JsonObject create(final GeometryWithProperties geometryWithProperties)\n    {\n        final Iterable<Location> geometry = geometryWithProperties.getGeometry();\n        final Map<String, Object> properties = geometryWithProperties.getProperties();\n\n        if (geometry instanceof Location)\n        {\n            return create((Location) geometry).withNewProperties(properties).jsonObject();\n        }\n        else if (geometry instanceof Polygon)\n        {\n            return create((Polygon) geometry).withNewProperties(properties).jsonObject();\n        }\n        else if (geometry instanceof PolyLine)\n        {\n            return create((PolyLine) geometry).withNewProperties(properties).jsonObject();\n        }\n        else\n        {\n            throw new CoreException(\"Unrecognized object type {}\",\n                    geometry.getClass().getSimpleName());\n        }\n    }\n\n    /**\n     * Creates a GeoJson Feature containing a Geometry\n     *\n     * @param locations\n     *            geometry coordinates\n     * @param type\n     *            geometry type\n     * @return a GeoJson Feature\n     */\n    public GeoJsonObject create(final Iterable<Location> locations, final GeoJsonType type)\n    {\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE);\n        final JsonArray coordinates;\n        switch (type)\n        {\n            case POINT:\n            {\n                final Location location = locations.iterator().next();\n                coordinates = new JsonArray();\n                coordinates.add(new JsonPrimitive(location.getLongitude().asDegrees()));\n                coordinates.add(new JsonPrimitive(location.getLatitude().asDegrees()));\n                break;\n            }\n            case LINESTRING:\n            case MULTI_POINT:\n            case MULTI_LINESTRING:\n            case MULTI_POLYGON:\n            {\n                coordinates = GeoJsonUtils.locationsToCoordinates(locations);\n                break;\n            }\n            case POLYGON:\n            {\n                coordinates = new JsonArray();\n                final JsonArray locationArray = GeoJsonUtils.locationsToCoordinates(locations);\n                coordinates.add(locationArray);\n                break;\n            }\n            default:\n                throw new CoreException(\"Unrecognized object type {}\", type);\n        }\n\n        final JsonObject geometry = new JsonObject();\n        geometry.addProperty(TYPE, type.getTypeString());\n        geometry.add(COORDINATES, coordinates);\n        result.add(GEOMETRY, geometry);\n        return new GeoJsonObject(result);\n    }\n\n    /**\n     * Creates a GeoJson FeatureCollection containing a list of Features\n     *\n     * @param objects\n     *            used to build each Feature\n     * @return a GeoJson FeatureCollection\n     * @deprecated use {@link #createFromGeometriesWithProperties(Iterable)} instead\n     */\n    @Deprecated\n    public GeoJsonObject create(final Iterable<LocationIterableProperties> objects)\n    {\n        return createFromGeometriesWithProperties(toGeometriesWithProperties(objects));\n    }\n\n    /**\n     * Creates a Point type GeoJson Feature\n     *\n     * @param location\n     *            geometry\n     * @return a Feature\n     */\n    public GeoJsonObject create(final Location location)\n    {\n        return this.create(location, GeoJsonType.POINT);\n    }\n\n    /**\n     * Creates a Json Feature from a {@link LocationIterableProperties}\n     *\n     * @deprecated use {@link #create(GeometryWithProperties)} instead\n     * @param object\n     *            {@link LocationIterableProperties}\n     * @return a GeoJson Feature\n     */\n    @Deprecated\n    public JsonObject create(final LocationIterableProperties object)\n    {\n        final GeometryWithProperties geometryWithProperties = toGeometryWithProperties(object);\n        return create(geometryWithProperties);\n    }\n\n    /**\n     * Creates a Polygon type GeoJson Feature\n     *\n     * @param polygon\n     *            geometry\n     * @return a GeoJson Feature\n     */\n    public GeoJsonObject create(final Polygon polygon)\n    {\n        return this.create(polygon.closedLoop(), GeoJsonType.POLYGON);\n    }\n\n    /**\n     * Creates a LineString type GeoJson Feature\n     *\n     * @param polyLine\n     *            geometry\n     * @return a GeoJson Feature\n     */\n    public GeoJsonObject create(final PolyLine polyLine)\n    {\n        return this.create(polyLine, GeoJsonType.LINESTRING);\n    }\n\n    /**\n     * Creates a GeoJson FeatureCollection containing a list of GeoJsonObject Features\n     *\n     * @param objects\n     *            the features\n     * @return a GeoJson FeatureCollection\n     */\n    public GeoJsonObject createFeatureCollection(final Iterable<GeoJsonObject> objects)\n    {\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE_COLLECTION);\n        final JsonArray features = new JsonArray();\n        int counter = 0;\n        for (final GeoJsonObject object : objects)\n        {\n            if (this.logFrequency > 0 && ++counter % this.logFrequency == 0)\n            {\n                logger.info(\"Processed {} features.\", counter);\n            }\n            if (!Optional.ofNullable(object.jsonObject().get(TYPE))\n                    .filter(jsonObject -> jsonObject.getAsString().equals(FEATURE)).isPresent())\n            {\n                throw new CoreException(\"Illegal GeoJson Type for Feature collection\");\n            }\n            features.add(object.jsonObject());\n        }\n        result.add(FEATURES, features);\n        return new GeoJsonObject(result);\n    }\n\n    /**\n     * Creates a GeoJson FeatureCollection containing a list of Features from an iterable of\n     * PropertiesLocated.\n     *\n     * @param iterableOfPropertiesLocated\n     *            iterable of PropertiesLocated\n     * @return a GeoJson FeatureCollection\n     */\n    public GeoJsonObject createFeatureCollectionFromPropertiesLocated(\n            final Iterable<PropertiesLocated> iterableOfPropertiesLocated)\n    {\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE_COLLECTION);\n        final JsonArray features = new JsonArray();\n        int counter = 0;\n        for (final PropertiesLocated propertiesLocated : iterableOfPropertiesLocated)\n        {\n            if (this.logFrequency > 0 && ++counter % this.logFrequency == 0)\n            {\n                logger.info(\"Processed {} features.\", counter);\n            }\n            final GeoJsonObject feature;\n            final Located located = propertiesLocated.getItem();\n            if (located instanceof Location)\n            {\n                feature = create((Location) located);\n            }\n            else if (located instanceof PolyLine)\n            {\n                feature = create((PolyLine) located);\n            }\n            else if (located instanceof Polygon)\n            {\n                feature = create((Polygon) located);\n            }\n            else if (located instanceof MultiPolyLine)\n            {\n                feature = createMultiLineStrings((MultiPolyLine) located);\n            }\n            else\n            {\n                throw new CoreException(\"Unrecognized object type {}\",\n                        located.getClass().getName());\n            }\n            final JsonObject featureJsonObj = feature.jsonObject();\n            featureJsonObj.add(PROPERTIES, propertiesLocated.getProperties());\n            features.add(feature.jsonObject());\n        }\n        result.add(FEATURES, features);\n        return new GeoJsonObject(result);\n    }\n\n    /**\n     * Creates a GeoJson FeatureCollection from an iterable of GeoJsonObject\n     *\n     * @param geoJsonObjects\n     *            a iterable of GeoJsonObject\n     * @return a GeoJson FeatureCollection\n     */\n    public GeoJsonObject createFromGeoJson(final Iterable<GeoJsonObject> geoJsonObjects)\n    {\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE_COLLECTION);\n        final JsonArray features = new JsonArray();\n        int counter = 0;\n        for (final GeoJsonObject object : geoJsonObjects)\n        {\n            if (this.logFrequency > 0 && ++counter % this.logFrequency == 0)\n            {\n                logger.info(\"Processed {} features.\", counter);\n            }\n            features.add(object.jsonObject());\n        }\n        result.add(FEATURES, features);\n        return new GeoJsonObject(result);\n    }\n\n    /**\n     * Creates a GeoJson FeatureCollection containing a list of Features\n     *\n     * @param geometriesWithProperties\n     *            associated geometries and properties used to build each Feature\n     * @return a GeoJson FeatureCollection\n     */\n    public GeoJsonObject createFromGeometriesWithProperties(\n            final Iterable<GeometryWithProperties> geometriesWithProperties)\n    {\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE_COLLECTION);\n        final JsonArray features = new JsonArray();\n        int counter = 0;\n        for (final GeometryWithProperties geometryWithProperties : geometriesWithProperties)\n        {\n            if (this.logFrequency > 0 && ++counter % this.logFrequency == 0)\n            {\n                logger.info(\"Processed {} features.\", counter);\n            }\n            features.add(create(geometryWithProperties));\n        }\n        result.add(FEATURES, features);\n        return new GeoJsonObject(result);\n    }\n\n    /**\n     * Creates a GeometryCollection type Feature containing geometries derived from a collection of\n     * {@link LocationIterableProperties}. <strong>Note:</strong> feature parameters are not present\n     * in the resulting GeometryCollection and must be handled separately to avoid data loss.\n     *\n     * @deprecated use {@link #createGeometryCollectionFeature(Iterable)} instead\n     * @param objects\n     *            used to build each geometry\n     * @return a GeoJson Feature\n     */\n    @Deprecated\n    public GeoJsonObject createGeometryCollection(\n            final Iterable<LocationIterableProperties> objects)\n    {\n        return createGeometryCollectionFeature(toGeometriesWithProperties(objects));\n    }\n\n    /**\n     * Creates a GeometryCollection type Feature containing geometries derived from a collection of\n     * {@link GeometryWithProperties}. <strong>Note:</strong> feature parameters are not present in\n     * the resulting GeometryCollection and must be handled separately to avoid data loss.\n     *\n     * @param geometriesWithProperties\n     *            used to build each geometry\n     * @return a GeoJson Feature\n     */\n    public GeoJsonObject createGeometryCollectionFeature(\n            final Iterable<GeometryWithProperties> geometriesWithProperties)\n    {\n        final JsonObject geometryCollection = new JsonObject();\n        geometryCollection.addProperty(TYPE, GEOMETRY_COLLECTION);\n\n        final Map<GeoJsonType, List<Iterable<Location>>> geometryMap = new HashMap<>();\n        int counter = 0;\n        for (final GeometryWithProperties geometryWithProperties : geometriesWithProperties)\n        {\n            if (this.logFrequency > 0 && ++counter % this.logFrequency == 0)\n            {\n                logger.info(\"Processed {} geometries.\", counter);\n            }\n\n            final Iterable<Location> geometry = geometryWithProperties.getGeometry();\n            final GeoJsonType geoJsonType;\n            if (geometry instanceof Location)\n            {\n                geoJsonType = GeoJsonType.POINT;\n            }\n            else if (geometry instanceof Polygon)\n            {\n                geoJsonType = GeoJsonType.POLYGON;\n            }\n            else if (geometry instanceof PolyLine)\n            {\n                geoJsonType = GeoJsonType.LINESTRING;\n            }\n            else\n            {\n                throw new CoreException(\"Unrecognized object type {}\",\n                        geometry.getClass().getSimpleName());\n            }\n            geometryMap.computeIfAbsent(geoJsonType, key -> new ArrayList<>()).add(geometry);\n        }\n\n        final JsonArray geometries = new JsonArray();\n        // Point vs MultiPoint\n        if (geometryMap.containsKey(GeoJsonType.POINT))\n        {\n            final List<Iterable<Location>> points = geometryMap.get(GeoJsonType.POINT);\n            if (points.size() > 1)\n            {\n                geometries.add(create(Iterables.translate(points, Iterables::head),\n                        GeoJsonType.MULTI_POINT).jsonObject().getAsJsonObject(GEOMETRY));\n            }\n            else if (points.size() == 1)\n            {\n                geometries.add(create(Iterables.head(points), GeoJsonType.POINT).jsonObject()\n                        .getAsJsonObject(GEOMETRY));\n            }\n        }\n\n        // Polygon vs MultiPolygon\n        if (geometryMap.containsKey(GeoJsonType.POLYGON))\n        {\n            final List<Iterable<Location>> polygons = geometryMap.get(GeoJsonType.POLYGON);\n            if (polygons.size() > 1)\n            {\n                geometries.add(createMultiPolygons(Iterables.stream(polygons).map(Polygon::new))\n                        .jsonObject().getAsJsonObject(GEOMETRY));\n            }\n            else if (polygons.size() == 1)\n            {\n                geometries.add(create(Iterables.head(polygons), GeoJsonType.POLYGON).jsonObject()\n                        .getAsJsonObject(GEOMETRY));\n            }\n        }\n\n        // LineString vs MultLineString\n        if (geometryMap.containsKey(GeoJsonType.LINESTRING))\n        {\n            final List<Iterable<Location>> multiPolylines = geometryMap.get(GeoJsonType.LINESTRING);\n            if (multiPolylines.size() > 1)\n            {\n                geometries.add(\n                        createMultiLineStrings(Iterables.stream(multiPolylines).map(PolyLine::new))\n                                .jsonObject().getAsJsonObject(GEOMETRY));\n            }\n            else if (multiPolylines.size() == 1)\n            {\n                geometries.add(create(Iterables.head(multiPolylines), GeoJsonType.LINESTRING)\n                        .jsonObject().getAsJsonObject(GEOMETRY));\n            }\n        }\n\n        geometryCollection.add(GEOMETRIES, geometries);\n\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE);\n        result.add(GEOMETRY, geometryCollection);\n\n        return new GeoJsonObject(result);\n    }\n\n    /**\n     * Creates a MultiLineString type GeoJson Feature\n     *\n     * @param polyLines\n     *            geometry\n     * @return a GeoJson Feature\n     */\n    public GeoJsonObject createMultiLineStrings(final Iterable<PolyLine> polyLines)\n    {\n        // Create the coordinates for each polyline\n        final List<GeoJsonObject> objects = new ArrayList<>();\n        for (final PolyLine polygon : polyLines)\n        {\n            objects.add(this.create(polygon, GeoJsonType.MULTI_LINESTRING));\n        }\n\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE);\n        final JsonArray coordinates = new JsonArray();\n\n        // Add the coordinates back for the entire object\n        for (final GeoJsonObject object : objects)\n        {\n            coordinates\n                    .add(object.jsonObject().getAsJsonObject(GEOMETRY).getAsJsonArray(COORDINATES));\n        }\n\n        final JsonObject geometry = new JsonObject();\n        geometry.addProperty(TYPE, GeoJsonType.MULTI_LINESTRING.getTypeString());\n        geometry.add(COORDINATES, coordinates);\n        result.add(GEOMETRY, geometry);\n        return new GeoJsonObject(result);\n    }\n\n    /**\n     * Creates a MultiPolygon type GeoJson Feature\n     *\n     * @param polygons\n     *            geometries\n     * @return a GeoJson Feature\n     */\n    public GeoJsonObject createMultiPolygons(final Iterable<Polygon> polygons)\n    {\n        // Create the coordinates for each polygon\n        final List<GeoJsonObject> objects = new ArrayList<>();\n        for (final Polygon polygon : polygons)\n        {\n            objects.add(this.create(polygon.closedLoop(), GeoJsonType.MULTI_POLYGON));\n        }\n\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE);\n        final JsonArray coordinates = new JsonArray();\n\n        // Add the coordinates back for the entire object\n        for (final GeoJsonObject object : objects)\n        {\n            final JsonArray subCoordinates = new JsonArray();\n            subCoordinates\n                    .add(object.jsonObject().getAsJsonObject(GEOMETRY).getAsJsonArray(COORDINATES));\n            coordinates.add(subCoordinates);\n        }\n\n        final JsonObject geometry = new JsonObject();\n        geometry.addProperty(TYPE, GeoJsonType.MULTI_POLYGON.getTypeString());\n        geometry.add(COORDINATES, coordinates);\n        result.add(GEOMETRY, geometry);\n        return new GeoJsonObject(result);\n    }\n\n    /**\n     * Creates multipolygon from {@link Iterable} of {@link Polygon}s where first polygon is assumed\n     * to be the outer ring and the rest are inner.\n     *\n     * @param polygons\n     *            an iterable of polygons where the first is assumed to be the outer polygon in a\n     *            multipolygon\n     * @return a MultiPolygon geojson feature with one polygon that geometrically represents a\n     *         single outer Atlas Multipolygon\n     */\n    public GeoJsonObject createOneOuterMultiPolygon(final Iterable<Polygon> polygons)\n    {\n        // Create the coordinates for each polygon\n        final List<GeoJsonObject> objects = new ArrayList<>();\n        for (final Polygon polygon : polygons)\n        {\n            objects.add(this.create(polygon.closedLoop(), GeoJsonType.MULTI_POLYGON));\n        }\n\n        final JsonObject result = new JsonObject();\n        result.addProperty(TYPE, FEATURE);\n        final JsonArray coordinates = new JsonArray();\n\n        // Add the coordinates back for the entire object\n        for (final GeoJsonObject object : objects)\n        {\n            coordinates\n                    .add(object.jsonObject().getAsJsonObject(GEOMETRY).getAsJsonArray(COORDINATES));\n        }\n\n        final JsonArray newCoordinates = new JsonArray();\n        newCoordinates.add(coordinates);\n        final JsonObject geometry = new JsonObject();\n        geometry.addProperty(TYPE, GeoJsonType.MULTI_POLYGON.getTypeString());\n        geometry.add(COORDINATES, newCoordinates);\n        result.add(GEOMETRY, geometry);\n        result.add(PROPERTIES, new JsonObject());\n        return new GeoJsonObject(result);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonCollection.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\n/**\n * Interface for all GeoJson collection types {@link GeojsonGeometryCollection} and\n * {@link GeoJsonFeatureCollection}\n * \n * @param <T>\n *            The type of GeoJsonGeometry in the collection\n * @author jklamer\n */\npublic interface GeoJsonCollection<T extends GeoJsonGeometry> extends GeoJson\n{\n    Iterable<T> getGeoJsonObjects();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonConstants.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\n/**\n * Utility class for all Geojson Constants\n *\n * @author jklamer\n */\npublic final class GeoJsonConstants\n{\n    public static final String TYPE = \"type\";\n    public static final String COORDINATES = \"coordinates\";\n    public static final String PROPERTIES = \"properties\";\n    public static final String GEOMETRY = \"geometry\";\n    public static final String GEOMETRIES = \"geometries\";\n    public static final String FEATURES = \"features\";\n\n    private GeoJsonConstants()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonFeature.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport com.google.gson.JsonObject;\n\n/**\n * From the spec https://tools.ietf.org/html/rfc7946#section-3.2\n * \n * <pre>\n *  A Feature object represents a spatially bounded thing.  Every Feature\n *    object is a GeoJSON object no matter where it occurs in a GeoJSON\n *    text.\n *\n *    o  A Feature object has a \"type\" member with the value \"Feature\".\n *\n *    o  A Feature object has a member with the name \"geometry\".  The value\n *       of the geometry member SHALL be either a Geometry object as\n *       defined above or, in the case that the Feature is unlocated, a\n *       JSON null value.\n *\n *    o  A Feature object has a member with the name \"properties\".  The\n *       value of the properties member is an object (any JSON object or a\n *       JSON null value).\n\n *    o  If a Feature has a commonly used identifier, that identifier\n *       SHOULD be included as a member of the Feature object with the name\n *       \"id\", and the value of this member is either a JSON string or\n *       number.\n * </pre>\n * \n * This interface is for all classes with a geojson feature representation. Because a fully formed\n * geojson geometry is a part of a geojosn feature this interface extends from that.\n *\n * @author jklamer\n */\npublic interface GeoJsonFeature extends GeoJsonGeometry, GeoJsonProperties\n{\n    @Override\n    default JsonObject asGeoJson()\n    {\n        return GeoJsonUtils.feature(this);\n    }\n\n    @Override\n    default GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.FEATURE;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonFeatureCollection.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport com.google.gson.JsonObject;\n\n/**\n * From the spec https://tools.ietf.org/html/rfc7946#section-3.3\n * \n * <pre>\n *     A GeoJSON object with the type \"FeatureCollection\" is a\n *    FeatureCollection object.  A FeatureCollection object has a member\n *    with the name \"features\".  The value of \"features\" is a JSON array.\n *    Each element of the array is a Feature object as defined above.  It\n *    is possible for this array to be empty.\n * </pre>\n *\n * @param <T>\n *            The Type of object that implements the {@link GeoJsonFeature} interface that is\n *            returned by this implementation\n * @author jklamer\n */\npublic interface GeoJsonFeatureCollection<T extends GeoJsonFeature>\n        extends GeoJsonCollection<T>, GeoJsonProperties\n{\n    @Override\n    default JsonObject asGeoJson()\n    {\n        return GeoJsonUtils.featureCollection(this);\n    }\n\n    @Override\n    default GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.FEATURE_COLLECTION;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonGeometry.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport com.google.gson.JsonObject;\n\n/**\n * From the spec https://tools.ietf.org/html/rfc7946#section-3.1\n * \n * <pre>\n *      A Geometry object represents points, curves, and surfaces in\n *    coordinate space.  Every Geometry object is a GeoJSON object no\n *    matter where it occurs in a GeoJSON text.\n * </pre>\n * \n * This interface is for all objects with a geojson Geometry object representation. This encompasses\n * all the Geojson Geometry types in {@link GeoJsonType#isGeometryType(GeoJsonType)}.\n *\n * @author jklamer\n */\npublic interface GeoJsonGeometry extends GeoJson\n{\n    @Override\n    default JsonObject asGeoJson()\n    {\n        return this.asGeoJsonGeometry();\n    }\n\n    /**\n     * This returns a Geojson object that is one of the Geometry types\n     * (https://tools.ietf.org/html/rfc7946#section-3.1)\n     * \n     * @return Geojson geometry representation.\n     */\n    JsonObject asGeoJsonGeometry();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonObject.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport java.io.BufferedWriter;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * A Geo {@link JsonObject} with properties.\n *\n * @author matthieun\n */\npublic class GeoJsonObject\n{\n    private JsonObject jsonObject;\n\n    protected GeoJsonObject(final JsonObject jsonObject)\n    {\n        this.jsonObject = jsonObject;\n    }\n\n    public Map<String, Object> getProperties()\n    {\n        final Map<String, Object> result = new HashMap<>();\n        if (this.jsonObject.get(\"properties\") != null)\n        {\n            final JsonObject propertiesObject = (JsonObject) this.jsonObject.get(\"properties\");\n            for (final Map.Entry<String, JsonElement> entry : propertiesObject.entrySet())\n            {\n                result.put(entry.getKey(), entry.getValue().toString());\n            }\n        }\n        return result;\n    }\n\n    public JsonObject jsonObject()\n    {\n        return this.jsonObject;\n    }\n\n    public void makeFeatureCollection()\n    {\n        if (!\"FeatureCollection\".equals(this.jsonObject.get(\"type\").getAsString()))\n        {\n            final JsonObject result = new JsonObject();\n            result.addProperty(\"type\", \"FeatureCollection\");\n            final JsonArray features = new JsonArray();\n            features.add(this.jsonObject);\n            result.add(\"features\", features);\n            this.jsonObject = result;\n        }\n    }\n\n    public void save(final WritableResource output)\n    {\n        final BufferedWriter writer = new BufferedWriter(\n                new OutputStreamWriter(output.write(), StandardCharsets.UTF_8));\n        try\n        {\n            writer.write(this.jsonObject.toString());\n            Streams.close(writer);\n        }\n        catch (final Exception e)\n        {\n            Streams.close(writer);\n            throw new CoreException(\"Could not save geojson object\", e);\n        }\n    }\n\n    public String toPrettyString()\n    {\n        return new GsonBuilder().setPrettyPrinting().create().toJson(this.jsonObject);\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.jsonObject.toString();\n    }\n\n    /***\n     * Adds a parent member to a FeatureCollection object. This Member will be on the same level as\n     * the \"type\" and \"features\" members.\n     *\n     * @param key\n     *            member key\n     * @param value\n     *            member value\n     * @return GeoJsonObject\n     */\n    public GeoJsonObject withNewParentMember(final String key, final Object value)\n    {\n        // Check if jsonObject is a FeatureCollection\n        final Map<String, Object> properties = new HashMap<>();\n        properties.put(key, value);\n        return this.withNewParentMembers(properties);\n    }\n\n    /***\n     * Adds multiple members to the FeatureCollection object.\n     *\n     * @param properties\n     *            Map of member key and properties\n     * @return GeoJsonObject\n     */\n    public GeoJsonObject withNewParentMembers(final Map<String, ? extends Object> properties)\n    {\n        // Check if jsonObject is a FeatureCollection\n        if (this.jsonObject.get(GeoJsonBuilder.TYPE).getAsString()\n                .equals(GeoJsonBuilder.FEATURE_COLLECTION))\n        {\n            final Gson gson = new Gson();\n            properties.forEach((key, value) -> this.jsonObject.add(key, gson.toJsonTree(value)));\n        }\n        return this;\n    }\n\n    public GeoJsonObject withNewProperties(final Map<String, ? extends Object> properties)\n    {\n        final JsonObject propertiesObject;\n        if (this.jsonObject.get(\"properties\") != null)\n        {\n            propertiesObject = (JsonObject) this.jsonObject.get(\"properties\");\n            this.jsonObject.remove(\"properties\");\n        }\n        else\n        {\n            propertiesObject = new JsonObject();\n        }\n        final Gson gson = new Gson();\n        properties.forEach((key, value) -> propertiesObject.add(key, gson.toJsonTree(value)));\n        this.jsonObject.add(\"properties\", propertiesObject);\n        return this;\n    }\n\n    public GeoJsonObject withNewProperty(final String key, final Object value)\n    {\n        final Map<String, Object> properties = new HashMap<>();\n        properties.put(key, value);\n        return this.withNewProperties(properties);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonProperties.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Interface for all interfaces that have geojson properties. Namely {@link GeoJsonFeature} and\n * {@link GeoJsonFeatureCollection} From the spec : https://tools.ietf.org/html/rfc7946#section-3.2\n * \n * <pre>\n *      o  A Feature object has a member with the name \"properties\".  The\n *       value of the properties member is an object (any JSON object or a\n *       JSON null value).\n * </pre>\n *\n * @author jklamer\n */\npublic interface GeoJsonProperties\n{\n    /**\n     * This returns the geojson properties associate with the\n     * \n     * @return a JsonObject for the \"properties\" field\n     */\n    JsonObject getGeoJsonProperties();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonSaver.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\n\n/**\n * Save some geometry items to a resource, as GeoJson\n *\n * @author matthieun\n */\npublic final class GeoJsonSaver\n{\n    public static void save(final Iterable<? extends Iterable<Location>> geometries,\n            final WritableResource destination)\n    {\n        final GeoJsonObject object = new GeoJsonBuilder().create(Iterables.translate(geometries,\n                polyLine -> new GeoJsonBuilder.LocationIterableProperties(polyLine,\n                        Maps.hashMap())));\n        save(object, destination);\n    }\n\n    public static void saveMultipolygon(final Iterable<MultiPolygon> geometries,\n            final WritableResource destination)\n    {\n        final Iterable<Polygon> outers = Iterables.translateMulti(geometries,\n                multiPolygon -> multiPolygon.outers());\n        final Iterable<Polygon> inners = Iterables.translateMulti(geometries,\n                multiPolygon -> multiPolygon.inners());\n        final Iterable<Polygon> multi = new MultiIterable<>(outers, inners);\n        save(multi, destination);\n    }\n\n    private static void save(final GeoJsonObject object, final WritableResource destination)\n    {\n        final JsonWriter writer = new JsonWriter(destination);\n        writer.write(object.jsonObject());\n        writer.close();\n    }\n\n    private GeoJsonSaver()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonType.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport java.util.EnumSet;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Geojson types case sensitive from the specification definition section 1.4:\n * https://tools.ietf.org/html/rfc7946#section-1.4\n * \n * <pre>\n *o  Inside this document, the term \"geometry type\" refers to seven\n *       case-sensitive strings: \"Point\", \"MultiPoint\", \"LineString\",\n *       \"MultiLineString\", \"Polygon\", \"MultiPolygon\", and\n *       \"GeometryCollection\".\n *o  As another shorthand notation, the term \"GeoJSON types\" refers to\n *       nine case-sensitive strings: \"Feature\", \"FeatureCollection\", and\n *       the geometry types listed above.\n * </pre>\n *\n * The reasoning for this being an enum also comes from the spec section 7:\n * https://tools.ietf.org/html/rfc7946#section-7\n * \n * <pre>\n *      Implementations MUST NOT extend the fixed set of GeoJSON types:\n *    FeatureCollection, Feature, Point, LineString, MultiPoint, Polygon,\n *    MultiLineString, MultiPolygon, and GeometryCollection.\n * </pre>\n *\n * @author matthieun\n * @author mgostintsev\n * @author jklamer\n */\npublic enum GeoJsonType\n{\n    FEATURE(\"Feature\"),\n    FEATURE_COLLECTION(\"FeatureCollection\"),\n    POINT(\"Point\"),\n    MULTI_POINT(\"MultiPoint\"),\n    LINESTRING(\"LineString\"),\n    MULTI_LINESTRING(\"MultiLineString\"),\n    POLYGON(\"Polygon\"),\n    MULTI_POLYGON(\"MultiPolygon\"),\n    GEOMETRY_COLLECTION(\"GeometryCollection\");\n\n    private static final EnumSet GEOMETRY_TYPES = EnumSet.of(POINT, MULTI_POINT, LINESTRING,\n            MULTI_LINESTRING, POLYGON, MULTI_POLYGON, GEOMETRY_COLLECTION);\n    private static final EnumSet FEATURE_TYPES = EnumSet.of(FEATURE, FEATURE_COLLECTION);\n    private final String typeString;\n\n    public static GeoJsonType forJson(final JsonObject object)\n    {\n        final String typeString;\n        try\n        {\n            typeString = object.get(GeoJsonConstants.TYPE).getAsString();\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Invalid geoJson type: {}\", object.get(GeoJsonConstants.TYPE));\n        }\n        return forString(typeString);\n    }\n\n    public static GeoJsonType forString(final String type)\n    {\n        for (final GeoJsonType value : values())\n        {\n            if (value.getTypeString().equals(type))\n            {\n                return value;\n            }\n        }\n        throw new CoreException(\"Invalid geoJson type: {}\", type);\n    }\n\n    public static boolean isFeatureType(final GeoJsonType type)\n    {\n        return FEATURE_TYPES.contains(type);\n    }\n\n    public static boolean isGeometryType(final GeoJsonType type)\n    {\n        return GEOMETRY_TYPES.contains(type);\n    }\n\n    GeoJsonType(final String typeString)\n    {\n        this.typeString = typeString;\n    }\n\n    public String getTypeString()\n    {\n        return this.typeString;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.typeString;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeoJsonUtils.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport static org.openstreetmap.atlas.geography.geojson.GeoJsonConstants.COORDINATES;\nimport static org.openstreetmap.atlas.geography.geojson.GeoJsonConstants.FEATURES;\nimport static org.openstreetmap.atlas.geography.geojson.GeoJsonConstants.GEOMETRIES;\nimport static org.openstreetmap.atlas.geography.geojson.GeoJsonConstants.GEOMETRY;\nimport static org.openstreetmap.atlas.geography.geojson.GeoJsonConstants.PROPERTIES;\nimport static org.openstreetmap.atlas.geography.geojson.GeoJsonConstants.TYPE;\nimport static org.openstreetmap.atlas.geography.geojson.GeoJsonType.POLYGON;\n\nimport java.util.Optional;\n\nimport org.apache.commons.lang3.Validate;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * These are utility functions that well help you create GeoJSON!\n *\n * @author hallahan\n */\npublic final class GeoJsonUtils\n{\n    public static final String IDENTIFIER = \"identifier\";\n    public static final String OSM_IDENTIFIER = \"osmIdentifier\";\n    public static final String ITEM_TYPE = \"itemType\";\n    private static final Logger logger = LoggerFactory.getLogger(GeoJsonUtils.class);\n\n    /**\n     * Creates a GeoJSON Polygon geometry from a bounds.\n     *\n     * @param bounds\n     *            A bounds.\n     * @return A GeoJSON Polygon geometry JsonObject.\n     */\n    public static JsonObject boundsToPolygonGeometry(final Rectangle bounds)\n    {\n        final JsonArray outerRing = new JsonArray();\n        final Iterable<Location> locations = bounds.closedLoop();\n        for (final Location location : locations)\n        {\n            outerRing.add(coordinate(location));\n        }\n\n        final JsonArray coordinates = new JsonArray();\n        coordinates.add(outerRing);\n\n        return geometry(POLYGON, coordinates);\n    }\n\n    /**\n     * From a location, we get a Latitude / Longitude Json Array [ latitude, longitude ]\n     *\n     * @param location\n     *            An atlas location\n     * @return JsonArray [ longitude, latitude ] coordinate.\n     */\n    public static JsonArray coordinate(final Location location)\n    {\n        return coordinate(location.getLongitude().asDegrees(), location.getLatitude().asDegrees());\n    }\n\n    /**\n     * Slightly more explicit, you provide a double longitude and latitude.\n     *\n     * @param longitude\n     *            The longitude (x).\n     * @param latitude\n     *            The latitude (y).\n     * @return JsonArray [ longitude, latitude ] coordinate.\n     */\n    public static JsonArray coordinate(final double longitude, final double latitude)\n    {\n        final JsonArray coordinate = new JsonArray();\n        coordinate.add(new JsonPrimitive(longitude));\n        coordinate.add(new JsonPrimitive(latitude));\n        return coordinate;\n    }\n\n    public static JsonObject feature(final GeoJsonFeature geoJsonFeature)\n    {\n        if (!geoJsonFeature.getGeoJsonType().equals(GeoJsonType.FEATURE))\n        {\n            logger.warn(\n                    \"Constructing GeoJson Feature Json for something with incorrect Geojson type: object {} with type {}\",\n                    geoJsonFeature, geoJsonFeature.getGeoJsonType());\n        }\n        return GeoJsonUtils.feature(geoJsonFeature.asGeoJsonGeometry(),\n                geoJsonFeature.getGeoJsonProperties());\n    }\n\n    /**\n     * Creates a GeoJSON Feature with a geometry and properties object.\n     *\n     * @param geometry\n     *            JsonObject that is the geometry.\n     * @param properties\n     *            JsonObject that is the properties.\n     * @return GeoJSON Feature as JsonObject.\n     */\n    public static JsonObject feature(final JsonObject geometry, final JsonObject properties)\n    {\n        final JsonObject feature = new JsonObject();\n        feature.addProperty(TYPE, GeoJsonType.FEATURE.getTypeString());\n        feature.add(GEOMETRY, geometry);\n        feature.add(PROPERTIES, properties);\n        return feature;\n    }\n\n    public static JsonObject featureCollection(\n            final GeoJsonFeatureCollection<? extends GeoJsonFeature> featureCollection)\n    {\n        if (!featureCollection.getGeoJsonType().equals(GeoJsonType.FEATURE_COLLECTION))\n        {\n            logger.warn(\n                    \"Constructing GeoJson Feature Json for something with incorrect Geojson type: object {} with type {}\",\n                    featureCollection, featureCollection.getGeoJsonType());\n        }\n        return GeoJsonUtils.featureCollection(featureCollection.getGeoJsonObjects(),\n                featureCollection.getGeoJsonProperties());\n    }\n\n    public static JsonObject featureCollection(\n            final Iterable<? extends GeoJsonFeature> featureObjects, final JsonObject properties)\n    {\n        final JsonObject featureCollection = new JsonObject();\n        featureCollection.addProperty(TYPE, GeoJsonType.FEATURE_COLLECTION.getTypeString());\n        final JsonArray features = new JsonArray();\n        Iterables.stream(featureObjects).map(GeoJsonUtils::feature).forEach(features::add);\n        featureCollection.add(FEATURES, features);\n        featureCollection.add(PROPERTIES, properties);\n        return featureCollection;\n    }\n\n    public static JsonObject geometry(\n            final GeojsonGeometryCollection<? extends GeoJsonGeometry> geojsonGeometryCollection)\n    {\n        if (!geojsonGeometryCollection.getGeoJsonType().equals(GeoJsonType.GEOMETRY_COLLECTION))\n        {\n            logger.warn(\n                    \"Constructing GeoJson Geometry Collection Json for something with incorrect Geojson type: object {} with type {}\",\n                    geojsonGeometryCollection, geojsonGeometryCollection.getGeoJsonType());\n        }\n        final JsonObject geometry = new JsonObject();\n        final JsonArray geometries = new JsonArray();\n        geojsonGeometryCollection.getGeoJsonObjects()\n                .forEach(geoJsonGeometry -> geometries.add(geoJsonGeometry.asGeoJsonGeometry()));\n        geometry.addProperty(TYPE, GeoJsonType.GEOMETRY_COLLECTION.getTypeString());\n        geometry.add(GEOMETRIES, geometries);\n        return geometry;\n    }\n\n    public static JsonObject geometry(final GeoJsonType type, final JsonArray coordinates)\n    {\n        Validate.isTrue(GeoJsonType.isGeometryType(type), \"Type is not geometry type. \");\n        Validate.isTrue(!type.equals(GeoJsonType.GEOMETRY_COLLECTION),\n                \"Geometry Collection cannot be represented by coordinate array\");\n        final JsonObject geometry = new JsonObject();\n        geometry.addProperty(TYPE, type.getTypeString());\n        geometry.add(COORDINATES, coordinates);\n        return geometry;\n    }\n\n    /**\n     * An iterable of locations will turn into a JsonArray of Longitude, Latitude coordinates.\n     *\n     * @param locations\n     *            An iterable of locations\n     * @return A JsonArray of Longitude, Latitude coordinates.\n     */\n    public static JsonArray locationsToCoordinates(final Iterable<Location> locations)\n    {\n        final JsonArray coordinates = new JsonArray();\n        for (final Location point : locations)\n        {\n            coordinates.add(coordinate(point));\n        }\n        return coordinates;\n    }\n\n    /**\n     * Convert an atlas {@link MultiPolygon} into it's geojson coordinate representation\n     *\n     * @param multiPolygon\n     *            the multiPolygon\n     * @return the coordinate array\n     */\n    public static JsonArray multiPolygonToCoordinates(final MultiPolygon multiPolygon)\n    {\n        final JsonArray polygons = new JsonArray();\n        multiPolygon.getOuterToInners().forEach((outer, inners) -> polygons\n                .add(GeoJsonUtils.polygonToCoordinates(outer, Optional.of(inners))));\n        return polygons;\n    }\n\n    /**\n     * Convert an atlas {@link Polygon} into it's geojson coordinate representation\n     *\n     * @param polygon\n     *            the polygon\n     * @return the coordinate array\n     */\n    public static JsonArray polygonToCoordinates(final Polygon polygon)\n    {\n        return GeoJsonUtils.polygonToCoordinates(polygon, Optional.empty());\n    }\n\n    private static JsonArray polygonToCoordinates(final Polygon outer,\n            final Optional<Iterable<Polygon>> inners)\n    {\n        final JsonArray polygon = new JsonArray();\n        polygon.add(GeoJsonUtils.locationsToCoordinates(outer.closedLoop()));\n        inners.ifPresent(innerPolygons -> innerPolygons.forEach(innerPolygon -> polygon\n                .add(GeoJsonUtils.locationsToCoordinates(innerPolygon.closedLoop()))));\n        return polygon;\n    }\n\n    private GeoJsonUtils()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/GeojsonGeometryCollection.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport com.google.gson.JsonObject;\n\n/**\n * From the spec https://tools.ietf.org/html/rfc7946#section-3.1.8\n * \n * <pre>\n *      A GeoJSON object with type \"GeometryCollection\" is a Geometry object.\n *    A GeometryCollection has a member with the name \"geometries\".  The\n *    value of \"geometries\" is an array.  Each element of this array is a\n *    GeoJSON Geometry object.  It is possible for this array to be empty.\n *\n *    Unlike the other geometry types described above, a GeometryCollection\n *    can be a heterogeneous composition of smaller Geometry objects.  For\n *    example, a Geometry object in the shape of a lowercase roman \"i\" can\n *    be composed of one point and one LineString.\n *\n *    GeometryCollections have a different syntax from single type Geometry\n *    objects (Point, LineString, and Polygon) and homogeneously typed\n *    multipart Geometry objects (MultiPoint, MultiLineString, and\n *    MultiPolygon) but have no different semantics.  Although a\n *    GeometryCollection object has no \"coordinates\" member, it does have\n *    coordinates: the coordinates of all its parts belong to the\n *    collection.  The \"geometries\" member of a GeometryCollection\n *    describes the parts of this composition.  Implementations SHOULD NOT\n *    apply any additional semantics to the \"geometries\" array.\n *\n *    To maximize interoperability, implementations SHOULD avoid nested\n *    GeometryCollections.  Furthermore, GeometryCollections composed of a\n *    single part or a number of parts of a single type SHOULD be avoided\n *    when that single part or a single object of multipart type\n *    (MultiPoint, MultiLineString, or MultiPolygon) could be used instead.\n * </pre>\n *\n * This interface is for all classes with a GeoJsonGeometryCollection representation.\n *\n * @param <T>\n *            The Type of object that implements the {@link GeoJsonGeometry} interface that is\n *            returned by this implementation\n * @author jklamer\n */\npublic interface GeojsonGeometryCollection<T extends GeoJsonGeometry>\n        extends GeoJsonCollection<T>, GeoJsonGeometry\n{\n\n    @Override\n    default JsonObject asGeoJson()\n    {\n        return this.asGeoJsonGeometry();\n    }\n\n    @Override\n    default JsonObject asGeoJsonGeometry()\n    {\n        return GeoJsonUtils.geometry(this);\n    }\n\n    @Override\n    default GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.GEOMETRY_COLLECTION;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/GeoJsonParser.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.GeoJsonItem;\n\n/**\n * @author Yazad Khambata\n */\npublic interface GeoJsonParser extends Serializable\n{\n    GeoJsonItem deserialize(String geoJson);\n\n    GeoJsonItem deserialize(Map<String, Object> map);\n\n    <T> T deserializeExtension(String json, Class<T> targetClass);\n\n    <T> T deserializeExtension(Map<String, Object> map, Class<T> targetClass);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/README.md",
    "content": "# GeoJSON Parsing\n\n## Why do we need a `GeoJson` parser? Won't a regular `JSON` parser do?\n\nWhile the `GeoJSON` spec is well defined [RFC](https://tools.ietf.org/html/rfc7946), the specification is none the less very loose. This presents 2 challenges that are not common in `JSON` parsing where the schema is strictly defined.\n\n1. Polymorphic nature of fields (`coordinates`),\n\n**A Point**\n```json\n     {\n         \"type\": \"Point\",\n         \"coordinates\": [40, 10]\n      }\n```\n\n**A Polygon**\n```json\n     {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],\n            [100.0, 1.0], [100.0, 0.0]\n          ]\n        ]\n      }\n```\nNotice the structure in terms of fields is completely the same, but the value type is very different. For `Point` `coordinates` are an array of exactly 2 numbers of decimal type and for the `Polygon` it is an array-of-array of such point coordinates (itself represented as arrays).\n\nThis presents a unique challenge for mapping Java objects in a way that preserves type safety while still has room to accommodate different types.\n\n2. Polymorphic nature of fields (`geometry`, `geometries` and `features`).\n\na. A `GeometryWithCoordinates` (all geometries except `GeometryCollection`) have coordinates. \nb. A `Feature` has one associated `geometry`, while a GeometryCollection has several assocated nested `geometries`.\nc. A FeatureCollection could have several associated `features`.\nd. `GeometryCollection`s can nest `GeometryCollection`s recursively.\n\nExample,\n\n**A Feature containing a LineString**\n```json\n{\n  \"type\": \"Feature\",\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [-122.009566, 37.33531],[-122.031007,37.390535],[-122.028932,37.332451]\n    ]\n  }\n}\n```\n\n**A recursively nested GeometryCollection**\n```json\n{\n    \"type\": \"GeometryCollection\",\n    \"geometries\": [\n        ...,\n        {\n            \"type\": \"GeometryCollection\",\n            \"geometries\": [\n                ...,\n                {\n                    \"type\": \"GeometryCollection\",\n                    \"geometries\": [\n                        ...,\n                        {\n                            \"type\": \"GeometryCollection\",\n                            \"geometries\": [\n                                ...\n                            ]\n                        }\n                    ]\n                }\n            ]\n        }\n    ]\n}\n\n```\n\n\nSince the building block of a `Geometry` is itself highly dynamic, this dynamism \"leaks\" into all other major structures like `Feature`s, `FeatureCollection`s and `GeometryCollection`s. A challenge that is not suited for regular JSON parsing.\n\n3. Very liberal specification\n\nThe RFC supports the concept of [foreign fields](https://tools.ietf.org/html/rfc7946#section-6.1) and fields like `properties` are entirely foreign. This means that implementations that rely heavily in foreign fields would have to map the properties section to a map-of-map-of-map... or it's like. This makes coding against it difficult and also introduces bugs the the compiler cannot protect against due to loss of type-safety.\n\nExample,\n\n```json\n{\n  \"type\": \"Feature\",\n  \"bbox\": [ ... ],\n  \"geometry\": { ... },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"somekey1\": \"some value 1\",\n      \"somekey2\": \"some value 2\"\n    },\n    \"description\": {\n      \"type\": \"UPDATE\",\n      \"descriptors\": [\n        {\n          \"name\": \"TAG\",\n          \"type\": \"ADD\",\n          \"key\": \"c\",\n          \"value\": \"3\"\n        },\n        {\n          \"name\": \"TAG\",\n          \"type\": \"UPDATE\",\n          \"key\": \"b\",\n          \"value\": \"2a\",\n          \"originalValue\": \"2\"\n        },\n        {\n          \"name\": \"TAG\",\n          \"type\": \"REMOVE\",\n          \"key\": \"a\",\n          \"value\": \"1\"\n        },\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"ADD\",\n          \"position\": \"5/5\",\n          \"afterView\": \"LINESTRING (-122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\"\n        },\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"REMOVE\",\n          \"position\": \"0/5\",\n          \"beforeView\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451)\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"ADD\",\n          \"afterView\": \"3\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"REMOVE\",\n          \"beforeView\": \"1\"\n        },\n        {\n          \"name\": \"START_NODE\",\n          \"type\": \"UPDATE\",\n          \"beforeView\": \"1\",\n          \"afterView\": \"10\"\n        },\n        {\n          \"name\": \"END_NODE\",\n          \"type\": \"UPDATE\",\n          \"beforeView\": \"2\",\n          \"afterView\": \"20\"\n        }\n      ]\n    },\n    \"entityType\": \"EDGE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"b\": \"2a\",\n      \"c\": \"3\"\n    },\n    \" relations\": [\n      2,\n      3\n    ],\n    \"startNode\": 10,\n    \"endNode\": 20,\n    \"WKT\": \"LINESTRING (-122.009566 37.33531, -122.031007 37.390535, -122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n\n```\n\nNotice how difficult it would be to access the originalValue inside properties#decription#descriptor[index] without type information.\n\nIn my exploration I didn't come across a framework that solves these problems to my satisfaction. In fact tools that deal with JsonSchema and JsonSchema-to-Java mapping seems to break due to the complexity of the geojson schema.\n\n## What does this GeoJSON parser support?\n\n1. Map GeoJSON to all standard types defined in the specification, including Feature, `FeatureCollection`, `Point`, `MultiPoint`, `LineString`, `MultiLineString`, `Polygon`, `MultiPolygon`.\n2. Support complex `coordinates` structures.\n3. Support for 2D and 3D `Bbox`es.\n4. Full support for Foreign Fields.\n5. Highly functional \"auto\"-`mapper` for properties, that automatically maps properties to your user-defined bean (subject to some restrictions).\n\n## How do I use it?\n\nIf you are just interested in the standard types and do not rely heavily on `Foreign Fields` or `properties` you just need one line of code.\n\n```java\n        final GeoJsonItem geoJsonItem = GeoJsonParserJacksonImpl.instance.deserialize(json);\n```\n\nIf you do wish to map your properties to a deep nested `bean` or `pojo`, you will need an additional line while accessing properties (assume MyClass is your deep-nested POJO),\n\n```java\n        final MyClass myObj = geoJsonItem.getProperties().asType(MyClass.class);\n```\n\n## What is (will) not (be) supported?\n\nVery detailed validations to ensure validation of the geometries. Example, [southwardly to northwardly](https://tools.ietf.org/html/rfc7946#section-5), [Anti-meridian](https://tools.ietf.org/html/rfc7946#section-3.1.9). These feature are available in `AtlasEntity`-ies. Only basic structural validations are added here.\n\n## A note on Mutability\n\nGeoJSONs i.e all `Geometry`-ies, `Feature`s and `FeatureCollection`s, and their constituents like `Coordinates`, `Bbox`, `Position`, etc are immutable. However your custom class which will be auto-mapped from the `properties` requires setter methods.  \n\n## A note of `properties` auto-mapping\n\n`Properties` in the `GeoJSONItem` can be automatically mapped to a user-defined POJO or bean. This doesn't require the user to explicitly call setters/getters. Under the hood the auto-mapper is a custom recursive `BeanUtilsBean#copyProperties` which is collection and nexted structure aware.\n\nThis means that for the auto-mapper to work you need to follow the `JavaBean` standards and some additional restrictions.\n\n### `Properties` auto-mapping support and restrictions\n\n#### Data-Types supported\n\n* Scalar Values\n    - String\n    - [Java Wrapper Types](https://en.wikipedia.org/wiki/Primitive_wrapper_class)\n* 1D Arrays of Scalar Values.\n* A Java Bean that contains the above.\n* 1D arrays of Java Beans.\n* A Map of scalar values in it's `key` and `value`.\n\n#### Parsed Geo JSON Geometry to Atlas Geometry mapping\n\n| Geo Json Geometry        | Atlas Geometry                                                                        |\n| -------------------------|---------------------------------------------------------------------------------------|\n| Point                    | `Location`                                                                            |\n| MultiPoint               | `List<Location>`                                                                      |\n| LineString               | `PolyLine`                                                                            |\n| MultiLineString          | `List<PolyLine>`                                                                      |\n| Polygon                  | `Polygon`                                                                             |\n| MultiPolygon             | `List<Polygon>`                                                                       |\n| GeometryCollection       | *N/A* (Non-GeometryCollection children can be converted to the above.)                | \n\n#### Restrictions\n\n| Restriction                                    | Reason                                                                                                      | Workaround                                                                                                                         |\n|------------------------------------------------|-------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|\n| Public Constructor and setters                 | Needed for reflectively constructing and setting data in the bean                                           | NA                                                                                                                                 |\n| Arrays instead of `List`s or `Set`s            | This makes auto mapping of Nested structures easier, since generic info is lost at runtime. *               | Expose a getter method that converts the array to your `Collection`                                                                |\n| Maps of scalars only                           | Keys in JSON cannot cantain objects. Values may contain objects but the generic info is lost in Java. *     | May be supported in the future, if the use case presents. In the mean while you can construct an array of `Pair`s as a workaround. | \n\n\\* The loss of generic info at runtime in Java is due to [Type Erasure](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html). While there are workarounds available to get that at runtime, they are awkward and would make the API complicated.\n\n "
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/annotation/Foreign.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author Yazad Khambata\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.FIELD })\npublic @interface Foreign\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/base/AbstractGeoJsonItem.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.base;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang3.Validate;\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.bbox.Bbox;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.bbox.Dimensions;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.ForeignFields;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.properties.Properties;\n\n/**\n * @author Yazad Khambata\n */\npublic abstract class AbstractGeoJsonItem implements GeoJsonItem\n{\n    private Bbox bbox;\n    private Properties properties;\n    private ForeignFields foreignFields;\n\n    public static Object extractBbox(final Map<String, Object> map)\n    {\n        final List<Double> list = (List<Double>) map.get(\"bbox\");\n\n        if (CollectionUtils.isEmpty(list))\n        {\n            return null;\n        }\n\n        final Object rawBbox = list.toArray(new Double[list.size()]);\n        return rawBbox;\n    }\n\n    public static Map<String, Object> extractPropertiesMap(final Map<String, Object> map)\n    {\n        final Map<String, Object> properties = (Map<String, Object>) map.get(\"properties\");\n\n        return properties;\n    }\n\n    protected static Map<String, Object> extractForeignFields(final Map<String, Object> map,\n            final HashSet<String> exclude)\n    {\n        return new HashMap<>(map).entrySet().stream()\n                .map(entry -> Pair.of(entry.getKey(), entry.getValue()))\n                .filter(pair -> !exclude.contains(pair.getKey()))\n                .collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n    }\n\n    private static Bbox toBbox(final Map<String, Object> map)\n    {\n        final Double[] coordinates = (Double[]) extractBbox(map);\n\n        if (coordinates == null)\n        {\n            return null;\n        }\n\n        return Dimensions.toBbox(coordinates);\n    }\n\n    public AbstractGeoJsonItem(final Bbox bbox, final Properties properties,\n            final ForeignFields foreignFields)\n    {\n        this.bbox = bbox;\n        this.properties = properties;\n        this.foreignFields = foreignFields;\n    }\n\n    public AbstractGeoJsonItem(final Map<String, Object> map, final ForeignFields foreignFields)\n    {\n        this(toBbox(map), new Properties(extractPropertiesMap(map)), foreignFields);\n        Validate.notEmpty(map, \"input map is empty.\");\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    @Override\n    public Bbox getBbox()\n    {\n        return this.bbox;\n    }\n\n    @Override\n    public ForeignFields getForeignFields()\n    {\n        return this.foreignFields;\n    }\n\n    @Override\n    public Properties getProperties()\n    {\n        return this.properties;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/base/GeoJsonItem.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.base;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.Type;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.bbox.Bbox;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.SupportsForeigners;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.properties.Properties;\n\n/**\n * @author Yazad Khambata\n */\npublic interface GeoJsonItem extends SupportsForeigners, Serializable\n{\n    Bbox getBbox();\n\n    Properties getProperties();\n\n    Type getType();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/base/type/FeatureType.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.base.type;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang3.reflect.ConstructorUtils;\nimport org.openstreetmap.atlas.geography.geojson.parser.GeoJsonParser;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.feature.AbstractFeature;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.feature.Feature;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.feature.FeatureCollection;\n\n/**\n * @author Yazad Khambata\n */\npublic enum FeatureType implements Type\n{\n    FEATURE(\"Feature\", Feature.class),\n    FEATURE_COLLECTION(\"FeatureCollection\", FeatureCollection.class, true);\n\n    private String typeValue;\n    private Class<? extends AbstractFeature> concreteClass;\n    private boolean collection;\n\n    public static AbstractFeature construct(final FeatureType geometryType,\n            final GeoJsonParser goeJsonParser, final Map<String, Object> map)\n    {\n        try\n        {\n            final Class<? extends AbstractFeature> concreteClass = geometryType.getConcreteClass();\n\n            return ConstructorUtils.invokeConstructor(concreteClass, goeJsonParser, map);\n        }\n        catch (final Exception e)\n        {\n            throw new RuntimeException(e);\n        }\n    }\n\n    FeatureType(final String typeValue, final Class<? extends AbstractFeature> concreteClass)\n    {\n        this(typeValue, concreteClass, false);\n    }\n\n    FeatureType(final String typeValue, final Class<? extends AbstractFeature> concreteClass,\n            final boolean collection)\n    {\n        this.typeValue = typeValue;\n        this.concreteClass = concreteClass;\n        this.collection = collection;\n    }\n\n    @Override\n    public AbstractFeature construct(final GeoJsonParser goeJsonParser,\n            final Map<String, Object> map)\n    {\n        return FeatureType.construct(this, goeJsonParser, map);\n    }\n\n    @Override\n    public Class<? extends AbstractFeature> getConcreteClass()\n    {\n        return this.concreteClass;\n    }\n\n    @Override\n    public String getTypeValue()\n    {\n        return this.typeValue;\n    }\n\n    @Override\n    public boolean isCollection()\n    {\n        return this.collection;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/base/type/GeometryType.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.base.type;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.apache.commons.lang3.Validate;\nimport org.apache.commons.lang3.reflect.ConstructorUtils;\nimport org.openstreetmap.atlas.geography.geojson.parser.GeoJsonParser;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.Geometry;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.GeometryCollection;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.LineString;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.MultiLineString;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.MultiPoint;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.MultiPolygon;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.Point;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.Polygon;\n\n/**\n * @author Yazad Khambata\n */\npublic enum GeometryType implements Type\n{\n    POINT(\"Point\", Point.class),\n    MULTI_POINT(\"MultiPoint\", MultiPoint.class),\n    LINE_STRING(\"LineString\", LineString.class),\n    MULTI_LINE_STRING(\"MultiLineString\", MultiLineString.class),\n    POLYGON(\"Polygon\", Polygon.class),\n    MULTI_POLYGON(\"MultiPolygon\", MultiPolygon.class),\n    GEOMETRY_COLLECTION(\"GeometryCollection\", GeometryCollection.class, true);\n\n    private String typeValue;\n    private Class<? extends Geometry> concreteClass;\n    private boolean collection;\n\n    public static Geometry construct(final GeometryType geometryType,\n            final GeoJsonParser goeJsonParser, final Map<String, Object> map)\n    {\n        try\n        {\n            final Class<? extends Geometry> concreteClass = geometryType.getConcreteClass();\n\n            if (geometryType.isCollection())\n            {\n                return ConstructorUtils.invokeConstructor(concreteClass, goeJsonParser, map);\n            }\n\n            return ConstructorUtils.invokeConstructor(concreteClass, map);\n        }\n        catch (final Exception e)\n        {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static GeometryType fromConcreteClass(final Class<? extends Geometry> concreteClass)\n    {\n        Validate.notNull(concreteClass);\n\n        final Predicate<GeometryType> filterFunction = geometryType -> geometryType\n                .getConcreteClass().equals(concreteClass);\n        return associatedGeometryType(concreteClass.toString(), filterFunction);\n    }\n\n    public static GeometryType fromTypeValue(final String typeValue)\n    {\n        Validate.notEmpty(typeValue);\n\n        final Predicate<GeometryType> filterFunction = geometryType -> geometryType.getTypeValue()\n                .equals(typeValue);\n        return associatedGeometryType(typeValue, filterFunction);\n    }\n\n    private static GeometryType associatedGeometryType(final String typeValue,\n            final Predicate<GeometryType> filterFunction)\n    {\n        return Arrays.stream(GeometryType.values()).filter(filterFunction).findFirst()\n                .orElseThrow(() -> new IllegalArgumentException(typeValue));\n    }\n\n    GeometryType(final String typeValue, final Class<? extends Geometry> concreteClass)\n    {\n        this(typeValue, concreteClass, false);\n    }\n\n    GeometryType(final String typeValue, final Class<? extends Geometry> concreteClass,\n            final boolean collection)\n    {\n        this.typeValue = typeValue;\n        this.concreteClass = concreteClass;\n        this.collection = collection;\n    }\n\n    @Override\n    public Geometry construct(final GeoJsonParser goeJsonParser, final Map<String, Object> map)\n    {\n        return GeometryType.construct(this, goeJsonParser, map);\n    }\n\n    @Override\n    public Class<? extends Geometry> getConcreteClass()\n    {\n        return this.concreteClass;\n    }\n\n    @Override\n    public String getTypeValue()\n    {\n        return this.typeValue;\n    }\n\n    @Override\n    public boolean isCollection()\n    {\n        return this.collection;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/base/type/Type.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.base.type;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang3.EnumUtils;\nimport org.openstreetmap.atlas.geography.geojson.parser.GeoJsonParser;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.GeoJsonItem;\n\n/**\n * @author Yazad Khambata\n */\npublic interface Type\n{\n    static <E extends Enum<E>> Type fromName(Class<? extends Type> subTypeClass, String typeValue)\n    {\n        return EnumUtils.getEnumList((Class<E>) subTypeClass).stream().map(item -> (Type) item)\n                .filter(item -> item.getTypeValue().equals(typeValue)).findFirst()\n                .orElseThrow(() -> new IllegalArgumentException(typeValue));\n    }\n\n    GeoJsonItem construct(GeoJsonParser goeJsonParser, Map<String, Object> map);\n\n    Class<? extends GeoJsonItem> getConcreteClass();\n\n    String getTypeValue();\n\n    boolean isCollection();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/base/type/TypeUtil.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.base.type;\n\nimport java.util.Arrays;\n\nimport org.apache.commons.lang3.Validate;\n\nimport com.google.common.collect.Streams;\n\n/**\n * @author Yazad Khambata\n */\npublic final class TypeUtil\n{\n    public static Type identifyStandardType(final String typeAsStr)\n    {\n        Validate.notEmpty(typeAsStr, \"typeAsStr is EMPTY!\");\n\n        final Type identifiedType = Streams\n                .concat(Arrays.stream(FeatureType.values()), Arrays.stream(GeometryType.values()))\n                .map(type -> (Type) type).filter(type -> type.getTypeValue().equals(typeAsStr))\n                .findFirst().orElseThrow(() -> new IllegalArgumentException(typeAsStr));\n\n        return identifiedType;\n    }\n\n    private TypeUtil()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/bbox/AbstractBbox.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.bbox;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\n\n/**\n * @author Yazad Khambata\n */\npublic abstract class AbstractBbox implements Bbox\n{\n    private Dimensions dimensions;\n\n    public AbstractBbox(final Dimensions dimensions)\n    {\n        this.dimensions = dimensions;\n    }\n\n    @Override\n    public Dimensions applicableDimensions()\n    {\n        return this.dimensions;\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/bbox/Bbox.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.bbox;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * @author Yazad Khambata\n */\npublic interface Bbox extends Serializable\n{\n\n    Dimensions applicableDimensions();\n\n    List<Double> toList();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/bbox/Bbox2D.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.bbox;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author Yazad Khambata\n */\npublic class Bbox2D extends AbstractBbox\n{\n    private final Double coordinate1;\n    private final Double coordinate2;\n    private final Double coordinate3;\n    private final Double coordinate4;\n\n    private static final int ZERO = 0;\n    private static final int ONE = 1;\n    private static final int TWO = 2;\n    private static final int THREE = 3;\n\n    public Bbox2D(final Double... coordinates)\n    {\n        this(Dimensions.TWO_DIMENSIONAL, coordinates);\n    }\n\n    Bbox2D(final Dimensions dimensions, final Double... coordinates)\n    {\n        super(dimensions);\n\n        dimensions.validate(coordinates);\n        this.coordinate1 = coordinates[ZERO];\n        this.coordinate2 = coordinates[ONE];\n        this.coordinate3 = coordinates[TWO];\n        this.coordinate4 = coordinates[THREE];\n    }\n\n    public Double getCoordinate1()\n    {\n        return this.coordinate1;\n    }\n\n    public Double getCoordinate2()\n    {\n        return this.coordinate2;\n    }\n\n    public Double getCoordinate3()\n    {\n        return this.coordinate3;\n    }\n\n    public Double getCoordinate4()\n    {\n        return this.coordinate4;\n    }\n\n    @Override\n    public List<Double> toList()\n    {\n        return Arrays.asList(this.coordinate1, this.coordinate2, this.coordinate3,\n                this.coordinate4);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/bbox/Bbox3D.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.bbox;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author Yazad Khambata\n */\npublic class Bbox3D extends Bbox2D\n{\n    private final Double coordinate5;\n    private final Double coordinate6;\n\n    private static final int FOUR = 4;\n    private static final int FIVE = 4;\n\n    public Bbox3D(final Double... coordinates)\n    {\n        super(Dimensions.THREE_DIMENSIONAL, coordinates);\n\n        this.coordinate5 = coordinates[FOUR];\n        this.coordinate6 = coordinates[FIVE];\n    }\n\n    public Double getCoordinate5()\n    {\n        return this.coordinate5;\n    }\n\n    public Double getCoordinate6()\n    {\n        return this.coordinate6;\n    }\n\n    @Override\n    public List<Double> toList()\n    {\n        final List<Double> list = new ArrayList<>(super.toList());\n        list.addAll(Arrays.asList(this.coordinate5, this.coordinate6));\n\n        return list;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/bbox/Dimensions.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.bbox;\n\nimport java.util.Arrays;\n\nimport org.apache.commons.lang3.Validate;\nimport org.apache.commons.lang3.reflect.ConstructorUtils;\n\n/**\n * @author Yazad Khambata\n */\npublic enum Dimensions\n{\n    TWO_DIMENSIONAL(2, Bbox2D.class),\n\n    THREE_DIMENSIONAL(3, Bbox3D.class);\n\n    private int numberOfDimensions;\n\n    private Class<? extends Bbox> bboxClass;\n\n    public static Bbox toBbox(final Double... coordinates)\n    {\n        Validate.notEmpty(coordinates);\n        final int length = coordinates.length;\n        final int minCoordinates = 4;\n        Validate.isTrue(length >= minCoordinates, \"length: %s.\", length);\n\n        final Dimensions dimensions = Arrays.stream(Dimensions.values())\n                .filter(theseDimensions -> theseDimensions.getNumberOfCoordinates() == length)\n                .findFirst()\n                .orElseThrow(() -> new IllegalArgumentException(Arrays.toString(coordinates)));\n\n        try\n        {\n            return ConstructorUtils.invokeConstructor(dimensions.getBboxClass(), coordinates);\n        }\n        catch (final Exception e)\n        {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    Dimensions(final int numberOfDimensions, final Class<? extends Bbox> bboxClass)\n    {\n        this.numberOfDimensions = numberOfDimensions;\n        this.bboxClass = bboxClass;\n    }\n\n    public Class<? extends Bbox> getBboxClass()\n    {\n        return this.bboxClass;\n    }\n\n    /**\n     * Per https://tools.ietf.org/html/rfc7946#section-5\n     *\n     * <pre>\n     *     The value of the bbox member MUST be an array of\n     *    length 2*n where n is the number of dimensions represented in the\n     *    contained geometries, with all axes of the most southwesterly point\n     *    followed by all axes of the more northeasterly point.\n     * </pre>\n     *\n     * @return the number of coordinates.\n     */\n    public int getNumberOfCoordinates()\n    {\n        final int multiply = 2;\n        return this.getNumberOfDimensions() * multiply;\n    }\n\n    public int getNumberOfDimensions()\n    {\n        return this.numberOfDimensions;\n    }\n\n    public void validate(final Double... coordinates)\n    {\n        Validate.notEmpty(coordinates, \"coordinates is EMPTY for %s.\", this);\n        final int actual = coordinates.length;\n        final int expected = getNumberOfCoordinates();\n        Validate.isTrue(actual == expected, \"coordinates.length actual {}; expected: {}.\", actual,\n                expected);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/feature/AbstractFeature.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.feature;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.AbstractGeoJsonItem;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.ForeignFields;\n\n/**\n * @author Yazad Khambata\n */\npublic abstract class AbstractFeature extends AbstractGeoJsonItem\n{\n    AbstractFeature(final Map<String, Object> map, final ForeignFields foreignFields)\n    {\n        super(map, foreignFields);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/feature/Feature.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.feature;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.GeoJsonParser;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.FeatureType;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.Type;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.DefaultForeignFieldsImpl;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.Geometry;\n\n/**\n * @author Yazad Khambata\n */\npublic class Feature extends AbstractFeature\n{\n    private Geometry geometry;\n\n    public Feature(final GeoJsonParser goeJsonParser, final Map<String, Object> map)\n    {\n        super(map, new DefaultForeignFieldsImpl(extractForeignFields(map,\n                new HashSet<>(Arrays.asList(\"type\", \"bbox\", \"geometry\", \"properties\")))));\n        this.geometry = (Geometry) goeJsonParser\n                .deserialize((Map<String, Object>) map.get(\"geometry\"));\n    }\n\n    public Geometry getGeometry()\n    {\n        return this.geometry;\n    }\n\n    @Override\n    public Type getType()\n    {\n        return FeatureType.FEATURE;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/feature/FeatureCollection.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.feature;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.GeoJsonParser;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.FeatureType;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.Type;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.DefaultForeignFieldsImpl;\n\n/**\n * @author Yazad Khambata\n */\npublic class FeatureCollection extends AbstractFeature\n{\n    private List<Feature> features;\n\n    public FeatureCollection(final GeoJsonParser goeJsonParser, final Map<String, Object> map)\n    {\n        super(map, new DefaultForeignFieldsImpl(extractForeignFields(map,\n                new HashSet<>(Arrays.asList(\"type\", \"bbox\", \"features\", \"properties\")))));\n        this.features = ((List<Map<String, Object>>) map.get(\"features\")).stream()\n                .map(goeJsonParser::deserialize).map(item -> (Feature) item)\n                .collect(Collectors.toList());\n    }\n\n    public List<Feature> getFeatures()\n    {\n        return this.features;\n    }\n\n    @Override\n    public Type getType()\n    {\n        return FeatureType.FEATURE_COLLECTION;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/foreign/DefaultForeignFieldsImpl.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.foreign;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\n\n/**\n * @author Yazad Khambata\n */\npublic class DefaultForeignFieldsImpl implements ForeignFields\n{\n    private Map<String, Object> valuesAsMap;\n\n    public DefaultForeignFieldsImpl(final Map<String, Object> valuesAsMap)\n    {\n        this.valuesAsMap = valuesAsMap;\n    }\n\n    @Override\n    public Map<String, Object> asMap()\n    {\n        if (this.valuesAsMap == null)\n        {\n            return Collections.EMPTY_MAP;\n        }\n\n        return Collections.unmodifiableMap(this.valuesAsMap);\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    @Override\n    public Object get(final String key)\n    {\n        return this.valuesAsMap.get(key);\n    }\n\n    @Override\n    public <T> T get(final String key, final Class<T> valueClass)\n    {\n        return (T) this.valuesAsMap.get(key);\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/foreign/ForeignFields.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.foreign;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * @author Yazad Khambata\n */\npublic interface ForeignFields extends Serializable\n{\n    Map<String, Object> asMap();\n\n    <T> T get(String key, Class<T> valueClass);\n\n    Object get(String key);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/foreign/SupportsForeigners.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.foreign;\n\n/**\n * @author Yazad Khambata\n */\npublic interface SupportsForeigners\n{\n    ForeignFields getForeignFields();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/AbstractGeometry.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.AbstractGeoJsonItem;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.GeometryType;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.Type;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.ForeignFields;\n\n/**\n * @author Yazad Khambata\n */\npublic abstract class AbstractGeometry extends AbstractGeoJsonItem implements Geometry\n{\n    AbstractGeometry(final Map<String, Object> map, final ForeignFields foreignFields)\n    {\n        super(map, foreignFields);\n    }\n\n    @Override\n    public GeometryType getGeometryType()\n    {\n        return GeometryType.fromConcreteClass(this.getClass());\n    }\n\n    @Override\n    public Type getType()\n    {\n        return getGeometryType();\n    }\n\n    @Override\n    public String getTypeValue()\n    {\n        return getGeometryType().getTypeValue();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/AbstractGeometryWithCoordinateSupport.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.DefaultForeignFieldsImpl;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.ForeignFields;\n\n/**\n * An abstraction of geometries with coordinates.\n *\n * @param <C>\n *            - coordinate data type.\n * @param <G>\n *            - Atlas Geometry.\n * @author Yazad Khambata\n */\npublic abstract class AbstractGeometryWithCoordinateSupport<C, G> extends AbstractGeometry\n        implements GeometryWithCoordinates<C, G>\n{\n    public static Object extractRawCoordinates(final Map<String, Object> map)\n    {\n        return map.get(\"coordinates\");\n    }\n\n    public AbstractGeometryWithCoordinateSupport(final Map<String, Object> map)\n    {\n        super(map, new DefaultForeignFieldsImpl(extractForeignFields(map,\n                new HashSet<>(Arrays.asList(\"type\", \"bbox\", \"coordinates\", \"properties\")))));\n    }\n\n    public AbstractGeometryWithCoordinateSupport(final Map<String, Object> map,\n            final ForeignFields foreignFields)\n    {\n        super(map, foreignFields);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/Geometry.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.GeoJsonItem;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.GeometryType;\n\n/**\n * @author Yazad Khambata\n */\npublic interface Geometry extends GeoJsonItem\n{\n    GeometryType getGeometryType();\n\n    String getTypeValue();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/GeometryCollection.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.GeoJsonParser;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.DefaultForeignFieldsImpl;\n\n/**\n * {@link GeometryCollection} nesting inside other {@link GeometryCollection}(s) is NOT allowed.\n *\n * @author Yazad Khambata\n */\npublic class GeometryCollection extends AbstractGeometry\n{\n    private List<Geometry> geometries;\n\n    public GeometryCollection(final GeoJsonParser goeJsonParser, final Map<String, Object> map)\n    {\n        super(map, new DefaultForeignFieldsImpl(extractForeignFields(map,\n                new HashSet<>(Arrays.asList(\"type\", \"bbox\", \"geometries\", \"properties\")))));\n        this.geometries = ((List<Map<String, Object>>) map.get(\"geometries\")).stream()\n                .map(goeJsonParser::deserialize).map(item -> (Geometry) item)\n                .collect(Collectors.toList());\n    }\n\n    public List<Geometry> getGeometries()\n    {\n        return this.geometries;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/GeometryWithCoordinates.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Coordinates;\n\n/**\n * An abstraction of a geometry with coordinates.\n *\n * @param <C>\n *            - value of the coordinates.\n * @param <G>\n *            - The compatible Atlas Geometry that this geojson can be converted to.\n * @author Yazad Khambata\n */\npublic interface GeometryWithCoordinates<C, G> extends Geometry\n{\n    Coordinates<C> getCoordinates();\n\n    G toAtlasGeometry();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/LineString.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.bbox.Bbox;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.ForeignFields;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Coordinates;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Position;\n\n/**\n * @author Yazad Khambata\n */\n@SuppressWarnings(\"squid:S2160\")\npublic class LineString extends AbstractGeometryWithCoordinateSupport<List<Position>, PolyLine>\n{\n    private MultiPoint value;\n\n    public LineString(final Map<String, Object> map)\n    {\n        super(map, null);\n        this.value = new MultiPoint(map);\n    }\n\n    @Override\n    public Bbox getBbox()\n    {\n        return this.value.getBbox();\n    }\n\n    @Override\n    public Coordinates<List<Position>> getCoordinates()\n    {\n        return this.value.getCoordinates();\n    }\n\n    @Override\n    public ForeignFields getForeignFields()\n    {\n        return this.value.getForeignFields();\n    }\n\n    @Override\n    public PolyLine toAtlasGeometry()\n    {\n        final List<Location> locations = this.value.toAtlasGeometry();\n        return new PolyLine(locations);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/MultiLineString.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Coordinates;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Position;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Positions;\n\n/**\n * @author Yazad Khambata\n */\n@SuppressWarnings(\"squid:S2160\")\npublic class MultiLineString\n        extends AbstractGeometryWithCoordinateSupport<List<List<Position>>, List<PolyLine>>\n{\n    private List<List<Position>> coordinates;\n\n    public MultiLineString(final Map<String, Object> map)\n    {\n        super(map, null);\n        this.coordinates = Coordinates\n                .forMultiLineString((List<List<List<Number>>>) extractRawCoordinates(map))\n                .getValue();\n    }\n\n    @Override\n    public Coordinates<List<List<Position>>> getCoordinates()\n    {\n        return new Coordinates<>(this.coordinates);\n    }\n\n    @Override\n    public List<PolyLine> toAtlasGeometry()\n    {\n        final List<List<Location>> listsOfLocations = Positions\n                .toCollectionsOfLocations(this.coordinates);\n        return listsOfLocations.stream().map(PolyLine::new).collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/MultiPoint.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Coordinates;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Position;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Positions;\n\n/**\n * @author Yazad Khambata\n */\n@SuppressWarnings(\"squid:S2160\")\npublic class MultiPoint\n        extends AbstractGeometryWithCoordinateSupport<List<Position>, List<Location>>\n{\n    private List<Position> coordinates;\n\n    public MultiPoint(final Map<String, Object> map)\n    {\n        super(map, null);\n        this.coordinates = Coordinates\n                .forMultiPoint((List<List<Number>>) extractRawCoordinates(map)).getValue();\n    }\n\n    @Override\n    public Coordinates<List<Position>> getCoordinates()\n    {\n        return new Coordinates<>(this.coordinates);\n    }\n\n    @Override\n    public List<Location> toAtlasGeometry()\n    {\n        return Positions.toLocations(this.coordinates);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/MultiPolygon.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Coordinates;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Position;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Positions;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * @author Yazad Khambata\n */\n@SuppressWarnings(\"squid:S2160\")\npublic class MultiPolygon extends\n        AbstractGeometryWithCoordinateSupport<List<List<List<Position>>>, org.openstreetmap.atlas.geography.MultiPolygon>\n{\n    private List<List<List<Position>>> coordinates;\n\n    public MultiPolygon(final Map<String, Object> map)\n    {\n        super(map, null);\n        this.coordinates = Coordinates\n                .forMultiPolygon((List<List<List<List<Number>>>>) extractRawCoordinates(map))\n                .getValue();\n    }\n\n    @Override\n    public Coordinates<List<List<List<Position>>>> getCoordinates()\n    {\n        return new Coordinates<>(this.coordinates);\n    }\n\n    @Override\n    public org.openstreetmap.atlas.geography.MultiPolygon toAtlasGeometry()\n    {\n        final MultiMap<Polygon, Polygon> outersToIneers = new MultiMap<>();\n        this.coordinates.stream()\n                .map(geojsonPolygon -> geojsonPolygon.stream().map(\n                        geojsonLinearRing -> new Polygon(Positions.toLocations(geojsonLinearRing)))\n                        .collect(Collectors.toList()))\n                .forEach(polygonList -> outersToIneers.put(polygonList.get(0),\n                        polygonList.size() > 1 ? polygonList.subList(1, polygonList.size())\n                                : Collections.emptyList()));\n        return new org.openstreetmap.atlas.geography.MultiPolygon(outersToIneers);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/Point.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Coordinates;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Position;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Positions;\n\n/**\n * @author Yazad Khambata\n */\n@SuppressWarnings(\"squid:S2160\")\npublic class Point extends AbstractGeometryWithCoordinateSupport<Position, Location>\n{\n    private Position coordinates;\n\n    public Point(final Map<String, Object> map)\n    {\n        super(map);\n        this.coordinates = Coordinates.forPoint((List<Number>) extractRawCoordinates(map))\n                .getValue();\n    }\n\n    @Override\n    public Coordinates<Position> getCoordinates()\n    {\n        return new Coordinates<>(this.coordinates);\n    }\n\n    @Override\n    public Location toAtlasGeometry()\n    {\n        return Positions.toLocation(this.coordinates);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/Polygon.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.bbox.Bbox;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.ForeignFields;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Coordinates;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Position;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate.Positions;\n\n/**\n * @author Yazad Khambata\n */\n@SuppressWarnings(\"squid:S2160\")\npublic class Polygon extends\n        AbstractGeometryWithCoordinateSupport<List<List<Position>>, org.openstreetmap.atlas.geography.Polygon>\n{\n    private MultiLineString value;\n\n    public Polygon(final Map<String, Object> map)\n    {\n        super(map, null);\n        this.value = new MultiLineString(map);\n    }\n\n    @Override\n    public Bbox getBbox()\n    {\n        return this.value.getBbox();\n    }\n\n    public Coordinates<List<List<Position>>> getCoordinates()\n    {\n        return this.value.getCoordinates();\n    }\n\n    @Override\n    public ForeignFields getForeignFields()\n    {\n        return this.value.getForeignFields();\n    }\n\n    @Override\n    public org.openstreetmap.atlas.geography.Polygon toAtlasGeometry()\n    {\n        return Positions.toAtlasPolygonFromMultiLineString(this.value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/coordinate/Coordinates.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.Validate;\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\n\n/**\n * The coordinates of the geometry.\n *\n * @param <V>\n *            - value of the coordinates.\n * @author Yazad Khambata\n */\npublic final class Coordinates<V>\n{\n\n    private V value;\n\n    public static Coordinates<List<Position>> forLineString(final List<List<Number>> data)\n    {\n        return forMultiPoint(data);\n    }\n\n    public static Coordinates<List<List<Position>>> forMultiLineString(\n            final List<List<List<Number>>> data)\n    {\n        return new Coordinates<>(toListOfPositionList(data));\n    }\n\n    public static Coordinates<List<Position>> forMultiPoint(final List<List<Number>> data)\n    {\n        return new Coordinates<>(toPositionList(data));\n    }\n\n    public static Coordinates<List<List<List<Position>>>> forMultiPolygon(\n            final List<List<List<List<Number>>>> data)\n    {\n        return new Coordinates<>(\n                data.stream().map(Coordinates::toListOfPositionList).collect(Collectors.toList()));\n    }\n\n    public static Coordinates<Position> forPoint(final List<Number> data)\n    {\n        return new Coordinates<>(toPosition(data));\n    }\n\n    public static Coordinates<List<List<Position>>> forPolygon(final List<List<List<Number>>> data)\n    {\n        // Designed in the specification as a MultiPolygon of size 1.\n        return forMultiLineString(data);\n    }\n\n    private static List<List<Position>> toListOfPositionList(final List<List<List<Number>>> data)\n    {\n        Validate.notEmpty(data, \"list containing the lists of coordinates is EMPTY.\");\n        Validate.isTrue(data.size() >= 1, \"multi point coordinates must be at least 1: %s.\", data);\n\n        return data.stream().map(listOfCoords -> toPositionList(listOfCoords))\n                .collect(Collectors.toList());\n    }\n\n    private static Position toPosition(final List<Number> data)\n    {\n        Validate.notEmpty(data, \"coordinates is EMPTY.\");\n        Validate.isTrue(data.size() == 2, \"point coordinates is NOT 2: %s.\", data);\n\n        return new Position(data.get(0).doubleValue(), data.get(1).doubleValue());\n    }\n\n    private static List<Position> toPositionList(final List<List<Number>> data)\n    {\n        Validate.notEmpty(data, \"list of coordinates is EMPTY.\");\n        Validate.isTrue(data.size() >= 1, \"multi point coordinates must be at least 1: %s.\", data);\n\n        return data.stream().map(coords -> toPosition(coords)).collect(Collectors.toList());\n    }\n\n    public Coordinates(final V value)\n    {\n        this.value = value;\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    public V getValue()\n    {\n        return this.value;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/coordinate/Position.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\n\n/**\n * @author Yazad Khambata\n */\npublic class Position implements Serializable\n{\n    private Double coordinate1;\n    private Double coordinate2;\n\n    public Position(final Double coordinate1, final Double coordinate2)\n    {\n        this.coordinate1 = coordinate1;\n        this.coordinate2 = coordinate2;\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    public Double getCoordinate1()\n    {\n        return this.coordinate1;\n    }\n\n    public Double getCoordinate2()\n    {\n        return this.coordinate2;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/coordinate/Positions.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.Validate;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.MultiLineString;\n\n/**\n * A utility class to help with conversions of geo json geometry instances with positions to atlas\n * geometry.\n *\n * @author Yazad Khambata\n */\npublic final class Positions\n{\n    public static Polygon toAtlasPolygonFromMultiLineString(final MultiLineString multiLineString)\n    {\n        final List<Polygon> atlasPolygons = toListOfAtlasPolygonsFromMultiLineString(\n                multiLineString, 1);\n        Validate.isTrue(atlasPolygons.size() == 1);\n        return atlasPolygons.get(0);\n    }\n\n    public static List<List<Location>> toCollectionsOfLocations(\n            final List<List<Position>> collectionOfPositions)\n    {\n        return collectionOfPositions.stream().map(Positions::toLocations)\n                .collect(Collectors.toList());\n    }\n\n    public static List<Polygon> toListOfAtlasPolygonsFromMultiLineString(\n            final MultiLineString multiLineString, final int expectedSize)\n    {\n        Validate.notNull(multiLineString);\n        Validate.notNull(multiLineString.getCoordinates());\n        Validate.notEmpty(multiLineString.getCoordinates().getValue());\n\n        if (expectedSize > 0)\n        {\n            Validate.isTrue(expectedSize == multiLineString.getCoordinates().getValue().size());\n        }\n\n        return multiLineString.getCoordinates().getValue().stream()\n                .map(positions -> new Polygon(Positions.toLocations(positions)))\n                .collect(Collectors.toList());\n    }\n\n    public static List<Polygon> toListOfAtlasPolygonsFromMultiLineString(\n            final MultiLineString multiLineString)\n    {\n        return toListOfAtlasPolygonsFromMultiLineString(multiLineString, -1);\n    }\n\n    /**\n     * The order of longitude and latitude in GeoJson as per the RFC is [lon, lat, alt].\n     * <p>\n     * However the order longitude and latitude is not shared in various atlas constructors, hence\n     * the flip in the order.\n     *\n     * @param position\n     *            - the {@link Position} to convert to {@link Location}.\n     * @return - the {@link Location} represented by the {@link Position}.\n     */\n    public static Location toLocation(final Position position)\n    {\n        return new Location(Latitude.degrees(position.getCoordinate2()),\n                Longitude.degrees(position.getCoordinate1()));\n    }\n\n    public static List<Location> toLocations(final List<Position> positions)\n    {\n        return positions.stream().map(Positions::toLocation).collect(Collectors.toList());\n    }\n\n    private Positions()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/properties/Properties.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.properties;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.DefaultForeignFieldsImpl;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.foreign.ForeignFields;\nimport org.openstreetmap.atlas.geography.geojson.parser.mapper.Mapper;\nimport org.openstreetmap.atlas.geography.geojson.parser.mapper.impl.DefaultBeanUtilsBasedMapperImpl;\n\n/**\n * @author Yazad Khambata\n */\npublic class Properties implements ForeignFields\n{\n    private ForeignFields values;\n\n    public Properties(final Map<String, Object> valuesAsMap)\n    {\n        this.values = new DefaultForeignFieldsImpl(valuesAsMap);\n    }\n\n    @Override\n    public Map<String, Object> asMap()\n    {\n        final Map<String, Object> foreignMap = this.values.asMap();\n        if (foreignMap == null)\n        {\n            return Collections.EMPTY_MAP;\n        }\n\n        return Collections.unmodifiableMap(foreignMap);\n    }\n\n    public <T> T asType(final Class<T> type, final Mapper mapper)\n    {\n        return mapper.map(this.asMap(), type);\n    }\n\n    public <T> T asType(final Class<T> type)\n    {\n        return asType(type, DefaultBeanUtilsBasedMapperImpl.instance);\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    @Override\n    public Object get(final String key)\n    {\n        return this.values.get(key);\n    }\n\n    @Override\n    public <T> T get(final String key, final Class<T> valueClass)\n    {\n        return this.values.get(key, valueClass);\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/properties/ext/change/Description.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.properties.ext.change;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.annotation.Foreign;\n\n/**\n * @author Yazad Khambata\n */\n@Foreign\npublic class Description implements Serializable\n{\n    private String type;\n    private Descriptor[] descriptors;\n\n    public Description()\n    {\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    public Descriptor[] getDescriptors()\n    {\n        return this.descriptors;\n    }\n\n    public String getType()\n    {\n        return this.type;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    public void setDescriptors(final Descriptor[] descriptors)\n    {\n        this.descriptors = descriptors;\n    }\n\n    public void setType(final String type)\n    {\n        this.type = type;\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/properties/ext/change/Descriptor.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.properties.ext.change;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.annotation.Foreign;\n\n/**\n * The Descriptor is a flattened version of subclasses of\n * {@link org.openstreetmap.atlas.geography.atlas.change.description.descriptors.ChangeDescriptor}.\n *\n * @author Yazad Khambata\n */\n@Foreign\npublic class Descriptor implements Serializable\n{\n    private Long id;\n    private String name;\n    private String type;\n    private String itemType;\n    private String role;\n    private String key;\n    private String value;\n    private String originalValue;\n    private String position;\n    private String beforeView;\n    private String afterView;\n    private Long beforeElement;\n    private Long afterElement;\n\n    public Descriptor()\n    {\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    public Long getAfterElement()\n    {\n        return this.afterElement;\n    }\n\n    public String getAfterView()\n    {\n        return this.afterView;\n    }\n\n    public Long getBeforeElement()\n    {\n        return this.beforeElement;\n    }\n\n    public String getBeforeView()\n    {\n        return this.beforeView;\n    }\n\n    public Long getId()\n    {\n        return this.id;\n    }\n\n    public String getItemType()\n    {\n        return this.itemType;\n    }\n\n    public String getKey()\n    {\n        return this.key;\n    }\n\n    public String getName()\n    {\n        return this.name;\n    }\n\n    public String getOriginalValue()\n    {\n        return this.originalValue;\n    }\n\n    public String getPosition()\n    {\n        return this.position;\n    }\n\n    public String getRole()\n    {\n        return this.role;\n    }\n\n    public String getType()\n    {\n        return this.type;\n    }\n\n    public String getValue()\n    {\n        return this.value;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    public void setAfterElement(final Long afterElement)\n    {\n        this.afterElement = afterElement;\n    }\n\n    public void setAfterView(final String afterView)\n    {\n        this.afterView = afterView;\n    }\n\n    public void setBeforeElement(final Long beforeElement)\n    {\n        this.beforeElement = beforeElement;\n    }\n\n    public void setBeforeView(final String beforeView)\n    {\n        this.beforeView = beforeView;\n    }\n\n    public void setId(final Long id)\n    {\n        this.id = id;\n    }\n\n    public void setItemType(final String itemType)\n    {\n        this.itemType = itemType;\n    }\n\n    public void setKey(final String key)\n    {\n        this.key = key;\n    }\n\n    public void setName(final String name)\n    {\n        this.name = name;\n    }\n\n    public void setOriginalValue(final String originalValue)\n    {\n        this.originalValue = originalValue;\n    }\n\n    public void setPosition(final String position)\n    {\n        this.position = position;\n    }\n\n    public void setRole(final String role)\n    {\n        this.role = role;\n    }\n\n    public void setType(final String type)\n    {\n        this.type = type;\n    }\n\n    public void setValue(final String value)\n    {\n        this.value = value;\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/domain/properties/ext/change/FeatureChangeProperties.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.properties.ext.change;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.annotation.Foreign;\n\n/**\n * @author Yazad Khambata\n */\n@Foreign\npublic class FeatureChangeProperties implements Serializable\n{\n    private String featureChangeType;\n    private Map<String, String> metadata;\n    private Description description;\n    private String entityType;\n    private String completeEntityClass;\n    private Long identifier;\n    private Map<String, String> tags;\n    private Long[] relations;\n    private Long startNode;\n    private Long endNode;\n    private String WKT;\n    private String bboxWKT;\n\n    public FeatureChangeProperties()\n    {\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    public String getBboxWKT()\n    {\n        return this.bboxWKT;\n    }\n\n    public String getCompleteEntityClass()\n    {\n        return this.completeEntityClass;\n    }\n\n    public Description getDescription()\n    {\n        return this.description;\n    }\n\n    public Long getEndNode()\n    {\n        return this.endNode;\n    }\n\n    public String getEntityType()\n    {\n        return this.entityType;\n    }\n\n    public String getFeatureChangeType()\n    {\n        return this.featureChangeType;\n    }\n\n    public Long getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public Map<String, String> getMetadata()\n    {\n        return this.metadata;\n    }\n\n    public Long[] getRelations()\n    {\n        return this.relations;\n    }\n\n    public Long getStartNode()\n    {\n        return this.startNode;\n    }\n\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    public String getWKT()\n    {\n        return this.WKT;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    public void setBboxWKT(final String bboxWKT)\n    {\n        this.bboxWKT = bboxWKT;\n    }\n\n    public void setCompleteEntityClass(final String completeEntityClass)\n    {\n        this.completeEntityClass = completeEntityClass;\n    }\n\n    public void setDescription(final Description description)\n    {\n        this.description = description;\n    }\n\n    public void setEndNode(final Long endNode)\n    {\n        this.endNode = endNode;\n    }\n\n    public void setEntityType(final String entityType)\n    {\n        this.entityType = entityType;\n    }\n\n    public void setFeatureChangeType(final String featureChangeType)\n    {\n        this.featureChangeType = featureChangeType;\n    }\n\n    public void setIdentifier(final Long identifier)\n    {\n        this.identifier = identifier;\n    }\n\n    public void setMetadata(final Map<String, String> metadata)\n    {\n        this.metadata = metadata;\n    }\n\n    public void setRelations(final Long[] relations)\n    {\n        this.relations = relations;\n    }\n\n    public void setStartNode(final Long startNode)\n    {\n        this.startNode = startNode;\n    }\n\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags;\n    }\n\n    public void setWKT(final String WKT)\n    {\n        this.WKT = WKT;\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/impl/jackson/GeoJsonParserJacksonImpl.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.impl.jackson;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang3.Validate;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.geojson.parser.GeoJsonParser;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.GeoJsonItem;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.Type;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.type.TypeUtil;\nimport org.openstreetmap.atlas.geography.geojson.parser.mapper.Mapper;\nimport org.openstreetmap.atlas.geography.geojson.parser.mapper.impl.DefaultBeanUtilsBasedMapperImpl;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n/**\n * @author Yazad Khambata\n * @author seancoulter\n */\npublic enum GeoJsonParserJacksonImpl implements GeoJsonParser\n{\n\n    INSTANCE;\n\n    private static final Logger log = LoggerFactory.getLogger(GeoJsonParserJacksonImpl.class);\n\n    @Override\n    public GeoJsonItem deserialize(final String geoJson)\n    {\n        log.trace(\"geoJson:: {}.\", geoJson);\n\n        final Map<String, Object> map = toMap(geoJson);\n\n        return deserialize(map);\n    }\n\n    @Override\n    public GeoJsonItem deserialize(final Map<String, Object> map)\n    {\n        log.trace(\"map:: {}.\", map);\n\n        final Type type = TypeUtil.identifyStandardType(getType(map));\n\n        return type.construct(GeoJsonParserJacksonImpl.INSTANCE, map);\n    }\n\n    @Override\n    public <T> T deserializeExtension(final String json, final Class<T> targetClass)\n    {\n        final Map<String, Object> map = toMap(json);\n        return deserializeExtension(map, targetClass);\n    }\n\n    @Override\n    public <T> T deserializeExtension(final Map<String, Object> map, final Class<T> targetClass)\n    {\n        final Mapper mapper = DefaultBeanUtilsBasedMapperImpl.instance;\n        return mapper.map(map, targetClass);\n    }\n\n    private String getType(final Map<String, Object> map)\n    {\n        final Object type = map.get(\"type\");\n        Validate.isTrue(type instanceof String, \"type: %s.\", type);\n        return (String) type;\n    }\n\n    private Map<String, Object> toMap(final String geoJson)\n    {\n        try\n        {\n            final ObjectMapper mapper = new ObjectMapper();\n            return (Map<String, Object>) mapper.readValue(geoJson, Object.class);\n        }\n        catch (final JsonProcessingException exception1)\n        {\n            throw new CoreException(exception1.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/mapper/Mapper.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.mapper;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * @author Yazad Khambata\n */\npublic interface Mapper extends Serializable\n{\n    <T> T map(Map<String, Object> map, Class<T> targetClass);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/geojson/parser/mapper/impl/DefaultBeanUtilsBasedMapperImpl.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.mapper.impl;\n\nimport java.beans.PropertyDescriptor;\nimport java.lang.reflect.Array;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.commons.beanutils.BeanUtilsBean;\nimport org.apache.commons.lang3.Validate;\nimport org.openstreetmap.atlas.geography.geojson.parser.mapper.Mapper;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Yazad Khambata\n */\npublic enum DefaultBeanUtilsBasedMapperImpl implements Mapper\n{\n    instance;\n\n    private static final Logger log = LoggerFactory\n            .getLogger(DefaultBeanUtilsBasedMapperImpl.class);\n    private static final Set<Class<?>> scalarTypes = new HashSet<>(\n            Arrays.asList(String.class, Integer.class, Long.class, Float.class, Double.class,\n                    Short.class, Boolean.class, Byte.class));\n\n    @Override\n    public <T> T map(final Map<String, Object> map, final Class<T> targetClass)\n    {\n        final T bean = create(targetClass);\n\n        populate(map, bean);\n\n        return bean;\n    }\n\n    private <T> void copyProperty(final BeanUtilsBean beanUtilsBean, final T bean,\n            final String name, final Object value)\n    {\n        try\n        {\n            beanUtilsBean.copyProperty(bean, name, value);\n        }\n        catch (final Exception e)\n        {\n            throw new IllegalStateException(\n                    \"Failed to copy \" + value + \" in \" + bean.getClass() + \"#\" + name + \".\", e);\n        }\n    }\n\n    private <T> T create(final Class<T> targetClass)\n    {\n        Validate.notNull(targetClass, \"null class cannot be instantiated.\");\n\n        try\n        {\n            return targetClass.getConstructor().newInstance();\n        }\n        catch (final ReflectiveOperationException reflectiveOperationException)\n        {\n            throw new IllegalStateException(\"Failed to construct instance of class: \" + targetClass\n                    + \"; isArray: \" + targetClass.isArray(), reflectiveOperationException);\n        }\n    }\n\n    private <C> boolean isScalarType(final Class<C> clazz)\n    {\n        return scalarTypes.contains(clazz) || clazz.isPrimitive();\n    }\n\n    private <T> void populate(final Map<String, Object> map, final T bean)\n    {\n        try\n        {\n            Validate.notNull(map, \"input map is NULL.\");\n            Validate.notNull(bean, \"bean is NULL\");\n\n            final BeanUtilsBean beanUtilsBean = new BeanUtilsBean();\n\n            final PropertyDescriptor[] propertyDescriptors = beanUtilsBean.getPropertyUtils()\n                    .getPropertyDescriptors(bean);\n\n            // Start with the concrete object\n            for (final PropertyDescriptor propertyDescriptor : propertyDescriptors)\n            {\n                try\n                {\n                    final String name = propertyDescriptor.getName();\n                    final Class<?> propertyType = propertyDescriptor.getPropertyType();\n\n                    final Object value = map.get(name);\n\n                    if (value == null)\n                    {\n                        continue;\n                    }\n\n                    if (isScalarType(propertyType) || Map.class.isAssignableFrom(propertyType))\n                    {\n                        // Scalar types or value is a Map in concrete class.\n                        // Map values can be scalar or nested maps of scalars.\n                        copyProperty(beanUtilsBean, bean, name, value);\n                    }\n                    else if (!propertyType.isArray())\n                    {\n                        // User-defined concrete classes.\n                        final T child = (T) create(propertyType);\n                        populate((Map<String, Object>) value, child);\n\n                        copyProperty(beanUtilsBean, bean, name, child);\n                    }\n                    else\n                    {\n                        // Array case.\n                        final List<Object> values = (List<Object>) value;\n                        if (values == null || values.isEmpty() || values.get(0) == null)\n                        {\n                            continue;\n                        }\n\n                        if (isScalarType(values.get(0).getClass()))\n                        {\n                            copyProperty(beanUtilsBean, bean, name, values.toArray());\n                        }\n                        else\n                        {\n                            log.info(\"values: {}.\", values);\n                            final Class<?> componentType = propertyType.getComponentType();\n                            final Object valuesAsObjects = values.stream().map(item ->\n                            {\n                                Validate.notNull(item,\n                                        \"item is NULL, do you have a trailing comma in the JSON?\");\n\n                                final T child = (T) create(componentType);\n                                populate((Map<String, Object>) item, child);\n                                return child;\n                            }).toArray(Propersize -> (Object[]) Array.newInstance(componentType,\n                                    values.size()));\n\n                            copyProperty(beanUtilsBean, bean, name, valuesAsObjects);\n                        }\n                    }\n                }\n                catch (final Exception e)\n                {\n                    throw new IllegalStateException(\"Population failed. propertyDescriptor name: \"\n                            + propertyDescriptor.getName() + \"; map: \" + map + \"; bean: \" + bean\n                            + \".\", e);\n                }\n            }\n        }\n        catch (final Exception e)\n        {\n            throw new IllegalStateException(\n                    \"Population failed. map: \" + map + \"; bean: \" + bean + \".\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/index/JtsSpatialIndex.java",
    "content": "package org.openstreetmap.atlas.geography.index;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\n\n/**\n * Define some common methods for spatial indices\n *\n * @param <T>\n *            The type to be indexed\n * @author tony\n */\npublic interface JtsSpatialIndex<T> extends Located, Serializable\n{\n    /**\n     * Inserts an spatial item with an extent specified by the given {@link Rectangle} to the index\n     *\n     * @param bound\n     *            The bound the object belongs to\n     * @param item\n     *            The item to insert\n     */\n    void add(Rectangle bound, T item);\n\n    /**\n     * Queries the index for all items whose bounds intersect the given {@link Rectangle}\n     *\n     * @param bound\n     *            The bound to query\n     * @return An {@link List} of features within or intersecting the bound.\n     */\n    List<T> get(Rectangle bound);\n\n    /**\n     * Queries the index for all items whose bounds intersect the given {@link Rectangle} and match\n     * the given predicate\n     *\n     * @param bound\n     *            The bound to query\n     * @param predicate\n     *            a predicate to apply to each item to determine if it should be included\n     * @return An {@link List} of features within or intersecting the bound.\n     */\n    List<T> get(Rectangle bound, Predicate<T> predicate);\n\n    /**\n     * Removes a single item from the tree.\n     *\n     * @param bound\n     *            The bound the object belongs to\n     * @param item\n     *            The item to remove\n     * @return <code>true</code> if the item was found\n     */\n    boolean remove(Rectangle bound, T item);\n\n    /**\n     * @return the number of items in the index\n     */\n    int size();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/index/PackedSpatialIndex.java",
    "content": "package org.openstreetmap.atlas.geography.index;\n\nimport java.util.ArrayList;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\n\n/**\n * This {@link PackedSpatialIndex} accepts a {@link Located} object (e.g. an {@link Edge}), but only\n * stores the packed information (identifier) into the real index. Spatial index can be an R-Tree or\n * Quad-Tree.\n *\n * @param <L>\n *            The type of {@link Located} item\n * @param <Packed>\n *            The type of packed item\n * @author tony\n */\npublic abstract class PackedSpatialIndex<L extends Located, Packed> implements SpatialIndex<L>\n{\n    private static final long serialVersionUID = 1747435801359663115L;\n    private final JtsSpatialIndex<Packed> index;\n\n    public PackedSpatialIndex(final JtsSpatialIndex<Packed> index)\n    {\n        this.index = index;\n    }\n\n    @Override\n    public void add(final L located)\n    {\n        final Rectangle bounds = located.bounds();\n        if (bounds != null)\n        {\n            this.index.add(bounds, compress(located));\n        }\n        else\n        {\n            throw new CoreException(\n                    \"Unable to get bounds for located item when building spatial index: {}\",\n                    located);\n        }\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.index.bounds();\n    }\n\n    @Override\n    public Iterable<L> get(final Rectangle bound)\n    {\n        return ((ArrayList<Packed>) this.index.get(bound)).stream().map(this::restore)\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public Iterable<L> get(final Rectangle bound, final Predicate<L> predicate)\n    {\n        return ((ArrayList<Packed>) this.index.get(bound)).stream().map(this::restore)\n                .filter(predicate).collect(Collectors.toList());\n    }\n\n    /**\n     * Extract a packed object from the given located object\n     *\n     * @param located\n     *            The {@link Located} item to extract\n     * @return The packed object\n     */\n    protected abstract Packed compress(L located);\n\n    protected abstract boolean isValid(L located, Rectangle bounds);\n\n    /**\n     * Restore the located object from packed one\n     *\n     * @param packed\n     *            The packed object to restore\n     * @return The restored object\n     */\n    protected abstract L restore(Packed packed);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/index/QuadTree.java",
    "content": "package org.openstreetmap.atlas.geography.index;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.index.quadtree.Quadtree;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\n\n/**\n * A JTS Quadtree wrapper.\n * <p>\n * Quadtree is a spatial index structure for efficient range querying of items bounded by 2D\n * rectangles. This Quadtree index provides a primary filter for range rectangle queries. The\n * various query methods return a list of all items which may intersect the query rectangle.\n * <p>\n * <b>Note that it may thus return items which do not in fact intersect the query rectangle</b>.\n * </p>\n * A secondary filter is required to test for actual intersection between the query rectangle and\n * the envelope of each candidate item. The secondary filter may be performed explicitly, or it may\n * be provided implicitly by subsequent operations executed on the items (for instance, if the index\n * query is followed by computing a spatial predicate between the query geometry and tree items, the\n * envelope intersection check is performed automatically.\n *\n * @param <T>\n *            The type to be indexed\n * @author tony\n */\npublic class QuadTree<T> implements JtsSpatialIndex<T>\n{\n    private static final long serialVersionUID = 7515245245282264428L;\n    private final Quadtree tree = new Quadtree();\n    private Rectangle bound;\n\n    public static <K> QuadTree<K> forCollection(final Iterable<K> iterable,\n            final Function<K, Rectangle> transform)\n    {\n        final QuadTree<K> toReturn = new QuadTree<>();\n        iterable.forEach(item -> toReturn.add(transform.apply(item), item));\n        return toReturn;\n    }\n\n    public static <K extends Located> QuadTree<K> forLocated(final Iterable<K> locatedIterable)\n    {\n        final QuadTree<K> toReturn = new QuadTree<>();\n        locatedIterable.forEach(located -> toReturn.add(located.bounds(), located));\n        return toReturn;\n    }\n\n    @Override\n    public void add(final Rectangle bound, final T item)\n    {\n        this.tree.insert(bound.asEnvelope(), item);\n        if (this.bound != null)\n        {\n            this.bound = this.bound.combine(bound);\n        }\n        else\n        {\n            this.bound = bound;\n        }\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bound;\n    }\n\n    public int depth()\n    {\n        return this.tree.depth();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public List<T> get(final Rectangle bound)\n    {\n        return this.tree.query(bound.asEnvelope());\n    }\n\n    @Override\n    public List<T> get(final Rectangle bound, final Predicate<T> predicate)\n    {\n        return get(bound).stream().filter(predicate).collect(Collectors.toList());\n    }\n\n    public boolean isEmpty()\n    {\n        return this.tree.isEmpty();\n    }\n\n    @Override\n    /**\n     * Note that the remove operation won't adjust this.bound accordingly\n     */\n    public boolean remove(final Rectangle bound, final T item)\n    {\n        return this.tree.remove(bound.asEnvelope(), item);\n    }\n\n    @Override\n    public int size()\n    {\n        return this.tree.size();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/index/RTree.java",
    "content": "package org.openstreetmap.atlas.geography.index;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.index.strtree.STRtree;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\n\n/**\n * A wrapper of JTS STRtree.\n * <p>\n * A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm. For two-dimensional\n * spatial data. The STR packed R-tree is simple to implement and maximizes space utilization; that\n * is, as many leaves as possible are filled to capacity. Overlap between nodes is far less than in\n * a basic R-tree. However, once the tree has been built (explicitly or on the first call to\n * #query), items may not be added or removed.\n * </p>\n *\n * @param <T>\n *            The type to be indexed\n * @author tony\n */\npublic class RTree<T> implements JtsSpatialIndex<T>\n{\n    private static final long serialVersionUID = -3672714932238885163L;\n    private final STRtree tree;\n    private Rectangle bound;\n\n    public static <K> RTree<K> forCollection(final Iterable<K> iterable,\n            final Function<K, Rectangle> transform)\n    {\n        final RTree<K> toReturn = new RTree<>();\n        iterable.forEach(item -> toReturn.add(transform.apply(item), item));\n        return toReturn;\n    }\n\n    public static <K extends Located> RTree<K> forLocated(final Iterable<K> locatedIterable)\n    {\n        final RTree<K> toReturn = new RTree<>();\n        locatedIterable.forEach(located -> toReturn.add(located.bounds(), located));\n        return toReturn;\n    }\n\n    public RTree()\n    {\n        this.tree = new STRtree();\n    }\n\n    public RTree(final int nodeCapacity)\n    {\n        this.tree = new STRtree(nodeCapacity);\n    }\n\n    @Override\n    public void add(final Rectangle bound, final T item)\n    {\n        this.tree.insert(bound.asEnvelope(), item);\n        if (this.bound != null)\n        {\n            this.bound = this.bound.combine(bound);\n        }\n        else\n        {\n            this.bound = bound;\n        }\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bound;\n    }\n\n    public void build()\n    {\n        this.tree.build();\n    }\n\n    public int depth()\n    {\n        return this.tree.depth();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public List<T> get(final Rectangle bound)\n    {\n        return this.tree.query(bound.asEnvelope());\n    }\n\n    @Override\n    public List<T> get(final Rectangle bound, final Predicate<T> predicate)\n    {\n        return get(bound).stream().filter(predicate).collect(Collectors.toList());\n    }\n\n    public boolean isEmpty()\n    {\n        return this.tree.isEmpty();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<T> itemsTree()\n    {\n        return this.tree.itemsTree();\n    }\n\n    @Override\n    /**\n     * Note that the remove operation won't adjust this.bound accordingly\n     */\n    public boolean remove(final Rectangle bound, final T item)\n    {\n        return this.tree.remove(bound.asEnvelope(), item);\n    }\n\n    @Override\n    public int size()\n    {\n        return this.tree.size();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/index/SpatialIndex.java",
    "content": "package org.openstreetmap.atlas.geography.index;\n\nimport java.io.Serializable;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\n\n/**\n * @author matthieun\n * @param <T>\n *            The type to be indexed\n */\npublic interface SpatialIndex<T extends Located> extends Located, Serializable\n{\n    /**\n     * Populate the spatial index\n     *\n     * @param feature\n     *            The feature to add\n     */\n    void add(T feature);\n\n    /**\n     * Get an {@link Iterable} over the features that are within or intersecting some bounds.\n     *\n     * @param bounds\n     *            The bounds to query\n     * @return An {@link Iterable} of features within or intersecting the bounds.\n     */\n    Iterable<T> get(Rectangle bounds);\n\n    /**\n     * Queries the index for all items whose bounds intersect the given {@link Rectangle} and match\n     * the given predicate\n     *\n     * @param bound\n     *            The bound to query\n     * @param predicate\n     *            a predicate to apply to each item to determine if it should be included\n     * @return An {@link Iterable} of features within or intersecting the bound.\n     */\n    Iterable<T> get(Rectangle bound, Predicate<T> predicate);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/matching/PolyLineMatch.java",
    "content": "package org.openstreetmap.atlas.geography.matching;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.concurrent.TimeoutException;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\nimport org.openstreetmap.atlas.utilities.threads.Result;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Best match between a {@link PolyLine} and some other set of {@link PolyLine}s.\n *\n * @author matthieun\n */\npublic class PolyLineMatch\n{\n    private static final Logger logger = LoggerFactory.getLogger(PolyLineMatch.class);\n\n    private final PolyLine source;\n    private final List<PolyLine> candidates;\n\n    /**\n     * Constructor\n     *\n     * @param source\n     *            The source {@link PolyLine} to try to match.\n     * @param candidates\n     *            The candidate {@link PolyLine}s that will provide the segments for the match\n     */\n    public PolyLineMatch(final PolyLine source, final List<PolyLine> candidates)\n    {\n        this.source = source;\n        this.candidates = candidates;\n    }\n\n    /**\n     * Find the best set of {@link Segment}s that form a {@link PolyLineRoute} that matches the\n     * source {@link PolyLine} with a costDistance that is less than the threshold provided.\n     *\n     * @param threshold\n     *            The threshold provided\n     * @return The best re-constructed route. Empty if none.\n     */\n    public Optional<PolyLineRoute> match(final Distance threshold)\n    {\n        if (this.candidates.isEmpty())\n        {\n            return Optional.empty();\n        }\n        PolyLineRoute best = null;\n        final SortedSet<PolyLineRoute> candidateRoutes = new TreeSet<>();\n        final Set<Location> visitedStitchingLocations = new HashSet<>();\n        int priorNumberOfCandidateRoutes = -1;\n\n        boolean candidateRoutesEmpty = candidateRoutes.isEmpty();\n        boolean bestNonNull = best != null;\n        boolean bestCostTooHigh = false;\n        boolean candidateRoutesIncreased = false;\n\n        while (candidateRoutesEmpty || bestNonNull && bestCostTooHigh && candidateRoutesIncreased)\n        {\n            priorNumberOfCandidateRoutes = candidateRoutes.size();\n            final Set<PolyLineRoute> toAdd = new HashSet<>();\n            for (int polyLineIndex = 0; polyLineIndex < this.candidates.size(); polyLineIndex++)\n            {\n                final PolyLine candidate = this.candidates.get(polyLineIndex);\n                final List<Segment> segments = candidate.segments();\n                for (int segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++)\n                {\n                    if (candidateRoutes.isEmpty())\n                    {\n                        toAdd.add(PolyLineRoute.startFrom(this.source, this.candidates,\n                                polyLineIndex, segmentIndex));\n                    }\n                    else\n                    {\n                        for (final PolyLineRoute existing : candidateRoutes)\n                        {\n                            final Optional<Location> stitchingLocation = existing\n                                    .canAppend(polyLineIndex, segmentIndex);\n                            if (stitchingLocation.isPresent()\n                                    && !visitedStitchingLocations.contains(stitchingLocation.get()))\n                            {\n                                visitedStitchingLocations.add(stitchingLocation.get());\n                                final PolyLineRoute elected = existing.copyAndAppend(polyLineIndex,\n                                        segmentIndex);\n                                if (!candidateRoutes.contains(elected))\n                                {\n                                    toAdd.add(elected);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            candidateRoutes.addAll(toAdd);\n            best = candidateRoutes.first();\n\n            candidateRoutesEmpty = candidateRoutes.isEmpty();\n            bestNonNull = best != null;\n            bestCostTooHigh = best.getCost().isGreaterThan(threshold);\n            candidateRoutesIncreased = candidateRoutes.size() > priorNumberOfCandidateRoutes;\n        }\n        return Optional.ofNullable(best);\n    }\n\n    /**\n     * Find the best set of {@link Segment}s that form a {@link PolyLineRoute} that matches the\n     * source {@link PolyLine} with a costDistance that is less than the threshold provided.\n     *\n     * @param threshold\n     *            The threshold provided\n     * @param maximum\n     *            The maximum {@link Duration} of the computation.\n     * @return The best re-constructed route. Empty if none, or if the computation took longer than\n     *         the maximum {@link Duration}\n     */\n    public Optional<PolyLineRoute> match(final Distance threshold, final Duration maximum)\n    {\n        try (Pool polyLineMatchPool = new Pool(1, \"PolyLine Match\"))\n        {\n            final Result<Optional<PolyLineRoute>> result = polyLineMatchPool\n                    .queue(() -> match(threshold));\n            return result.get(maximum);\n        }\n        catch (final TimeoutException e)\n        {\n            logger.warn(\"Was not able to compute PolyLineMatch in {} for {}\", maximum, this.source);\n            return Optional.empty();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/matching/PolyLineRoute.java",
    "content": "package org.openstreetmap.atlas.geography.matching;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * A candidate {@link PolyLine} route matching the source {@link PolyLine}.\n * <p>\n * This is a re-constructed {@link PolyLine} from {@link Segment}s coming from a list of candidate\n * {@link PolyLine}s. The algorithm tries to find the best set of connected {@link Segment}s that\n * belong to many other {@link PolyLine}s and that matches the source {@link PolyLine}\n *\n * @author matthieun\n */\npublic final class PolyLineRoute implements Comparable<PolyLineRoute>\n{\n    private final List<Segment> candidateSegments;\n    private final Distance cost;\n    private final PolyLine source;\n    private final List<PolyLine> candidates;\n    private PolyLine polyLine;\n\n    protected static PolyLineRoute startFrom(final PolyLine source, final List<PolyLine> candidates,\n            final int polyLineIndex, final int segmentIndex)\n    {\n        final List<Segment> candidateSegments = new ArrayList<>();\n        candidateSegments.add(candidateSegment(candidates, polyLineIndex, segmentIndex));\n        return new PolyLineRoute(source, candidates, candidateSegments);\n    }\n\n    private static Segment candidateSegment(final List<PolyLine> candidates,\n            final int polyLineIndex, final int segmentIndex)\n    {\n        return new Segment(candidates.get(polyLineIndex).get(segmentIndex),\n                segmentIndex < candidates.get(polyLineIndex).size() - 1\n                        ? candidates.get(polyLineIndex).get(segmentIndex + 1)\n                        : candidates.get(polyLineIndex).first());\n    }\n\n    private PolyLineRoute(final PolyLine source, final List<PolyLine> candidates,\n            final List<Segment> candidateSegments)\n    {\n        this.candidateSegments = candidateSegments;\n        this.source = source;\n        this.candidates = candidates;\n        this.cost = this.source.averageOneWayDistanceTo(this.asPolyLine())\n                .add(this.asPolyLine().size() > 2 ? new PolyLine(this.asPolyLine().innerLocations())\n                        .averageOneWayDistanceTo(this.source) : Distance.ZERO);\n    }\n\n    public PolyLine asPolyLine()\n    {\n        if (this.polyLine == null)\n        {\n            final List<Location> locations = new ArrayList<>();\n            this.candidateSegments.forEach(segment -> locations.add(segment.first()));\n            if (this.source instanceof Polygon)\n            {\n                return new Polygon(locations);\n            }\n            else\n            {\n                locations.add(this.candidateSegments.get(this.candidateSegments.size() - 1).last());\n                return new PolyLine(locations);\n            }\n        }\n        return this.polyLine;\n    }\n\n    @Override\n    public int compareTo(final PolyLineRoute other)\n    {\n        return this.cost.isGreaterThan(other.getCost()) ? 1\n                : this.cost.isLessThan(other.getCost()) ? -1 : 0;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof PolyLineRoute)\n        {\n            return ((PolyLineRoute) other).asPolyLine().equals(this.asPolyLine());\n        }\n        return false;\n    }\n\n    public Distance getCost()\n    {\n        return this.cost;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return asPolyLine().hashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[PolyLineRoute: \" + asPolyLine() + \"]\";\n    }\n\n    protected Optional<Location> canAppend(final int polyLineIndex, final int segmentIndex)\n    {\n        // The segment index and the index of the first point of the segment in the polyline are\n        // the same!\n        final Location end = this.candidateSegments.get(this.candidateSegments.size() - 1).last();\n        final Location proposed = this.candidates.get(polyLineIndex).get(segmentIndex);\n        final Segment candidateSegment = candidateSegment(this.candidates, polyLineIndex,\n                segmentIndex);\n        final boolean matchingLocations = end.equals(proposed);\n        final boolean candidateSegmentAlreadyContained = this.candidateSegments\n                .contains(candidateSegment);\n        if (matchingLocations && !candidateSegmentAlreadyContained)\n        {\n            return Optional.of(proposed);\n        }\n        else\n        {\n            return Optional.empty();\n        }\n    }\n\n    protected PolyLineRoute copyAndAppend(final int polyLineIndex, final int segmentIndex)\n    {\n        if (canAppend(polyLineIndex, segmentIndex).isPresent())\n        {\n            final List<Segment> polyLineIndexAndSegmentIndex = new ArrayList<>(\n                    this.candidateSegments);\n            polyLineIndexAndSegmentIndex\n                    .add(candidateSegment(this.candidates, polyLineIndex, segmentIndex));\n            return new PolyLineRoute(this.source, this.candidates, polyLineIndexAndSegmentIndex);\n        }\n        else\n        {\n            throw new CoreException(\"Unable to append {} and {}\", asPolyLine(),\n                    getSegment(polyLineIndex, segmentIndex));\n        }\n    }\n\n    private Segment getSegment(final int polyLineIndex, final int segmentIndex)\n    {\n        return this.candidates.get(polyLineIndex).segments().get(segmentIndex);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/CountryShard.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.sharding.converters.StringToShardConverter;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Shard and country pair\n *\n * @author matthieun\n */\npublic class CountryShard implements Shard\n{\n    private static final long serialVersionUID = -4158215940506552768L;\n\n    private final Shard shard;\n    private final String country;\n\n    public static CountryShard forName(final String name)\n    {\n        final StringList split = StringList.split(name, Shard.SHARD_DATA_SEPARATOR, 2);\n        return new CountryShard(split.get(0), new StringToShardConverter().convert(split.get(1)));\n    }\n\n    public CountryShard(final String country, final Shard shard)\n    {\n        if (shard == null || country == null)\n        {\n            throw new CoreException(\"Cannot have null parameters: Country = {} and Shard = {}\",\n                    country, shard);\n        }\n        this.shard = shard;\n        this.country = country;\n    }\n\n    public CountryShard(final String country, final String shardString)\n    {\n        if (shardString == null || country == null)\n        {\n            throw new CoreException(\"Cannot have null parameters: Country = {} and Shard = {}\",\n                    country, shardString);\n        }\n        this.country = country;\n        this.shard = new StringToShardConverter().convert(shardString);\n    }\n\n    @Override\n    public JsonObject asGeoJson()\n    {\n        return this.shard.asGeoJson();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.shard.bounds();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof CountryShard)\n        {\n            final CountryShard that = (CountryShard) other;\n            return this.getCountry().equals(that.getCountry())\n                    && this.getShard().equals(that.getShard());\n        }\n        return false;\n    }\n\n    public String getCountry()\n    {\n        return this.country;\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return this.shard.getGeoJsonType();\n    }\n\n    @Override\n    public String getName()\n    {\n        return this.country + Shard.SHARD_DATA_SEPARATOR + this.shard.getName();\n    }\n\n    public Shard getShard()\n    {\n        return this.shard;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return new HashCodeBuilder().append(this.shard).append(this.country).hashCode();\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[CountryShard: country = \" + this.country + \", shard = \" + this.shard.toString()\n                + \"]\";\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return this.shard.toWkb();\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return this.shard.toWkt();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/DynamicTileSharding.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.io.BufferedWriter;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonObject;\nimport org.openstreetmap.atlas.geography.sharding.preparation.TilePrinter;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Quad Tree sharding. The name does not reflect QuadTree to not confuse with the quad tree spatial\n * index.\n * <p>\n * The {@link Command} portion of this class is to read a csv file containing feature counts\n * (usually ways) for each of the maxZoom - 1 tiles. This is generated in the OSM database snapshot,\n * using the {@link TilePrinter} class to create the list of tiles needed. The csv file that is read\n * helps generate a tree, which is then serialized.\n *\n * @author matthieun\n * @author mgostintsev\n */\npublic class DynamicTileSharding extends Command implements Sharding\n{\n    /**\n     * Node of the quad tree. Implementation is recursive.\n     *\n     * @author matthieun\n     */\n    private static class Node implements Located, Serializable\n    {\n        private static final int MAXIMUM_CHILDREN = 4;\n        private static final long serialVersionUID = -7789058745501080439L;\n        private final List<Node> children;\n        private final SlippyTile tile;\n\n        protected static Node read(final Resource resource)\n        {\n            return read(resource.lines().iterator());\n        }\n\n        private static Node read(final Iterator<String> lineIterator)\n        {\n            final String line = lineIterator.next();\n            final List<Node> children = new ArrayList<>();\n            String tileName = line;\n            if (line.endsWith(\"+\"))\n            {\n                for (int i = 0; i < MAXIMUM_CHILDREN; i++)\n                {\n                    children.add(read(lineIterator));\n                }\n                tileName = line.substring(0, line.length() - 1);\n            }\n            return new Node(SlippyTile.forName(tileName), children);\n        }\n\n        protected Node()\n        {\n            this(SlippyTile.ROOT);\n        }\n\n        private Node(final SlippyTile tile)\n        {\n            this.tile = tile;\n            this.children = new ArrayList<>();\n        }\n\n        private Node(final SlippyTile tile, final List<Node> children)\n        {\n            this.tile = tile;\n            this.children = children;\n        }\n\n        @Override\n        public Rectangle bounds()\n        {\n            return this.tile.bounds();\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (other instanceof Node)\n            {\n                return ((Node) other).getTile().equals(this.tile);\n            }\n            return false;\n        }\n\n        public SlippyTile getTile()\n        {\n            return this.tile;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return this.tile.hashCode();\n        }\n\n        public Set<Node> leafNodes(final GeometricSurface surface)\n        {\n            final Set<Node> result = new HashSet<>();\n            final Rectangle polygonBounds = surface.bounds();\n            if (polygonBounds.overlaps(bounds()))\n            {\n                if (isFinal() && surface.overlaps(bounds()))\n                {\n                    result.add(this);\n                }\n                else\n                {\n                    for (final Node child : this.children)\n                    {\n                        result.addAll(child.leafNodes(surface));\n                    }\n                }\n            }\n            return result;\n        }\n\n        public Set<Node> leafNodesCovering(final Location location)\n        {\n            final Set<Node> result = new HashSet<>();\n            if (bounds().fullyGeometricallyEncloses(location))\n            {\n                if (isFinal())\n                {\n                    result.add(this);\n                }\n                else\n                {\n                    for (final Node child : this.children)\n                    {\n                        result.addAll(child.leafNodesCovering(location));\n                    }\n                }\n            }\n            return result;\n        }\n\n        public Set<Node> leafNodesIntersecting(final PolyLine polyLine)\n        {\n            final Rectangle polyLineBounds = polyLine.bounds();\n            return leafNodesIntersecting(polyLine, polyLineBounds);\n        }\n\n        public Set<Node> leafNodesIntersecting(final PolyLine polyLine,\n                final Rectangle polyLineBounds)\n        {\n            final Set<Node> result = new HashSet<>();\n            if (polyLineBounds.overlaps(bounds()))\n            {\n                if (isFinal() && (polyLine.intersects(bounds())\n                        || bounds().fullyGeometricallyEncloses(polyLine)))\n                {\n                    result.add(this);\n                }\n                else\n                {\n                    for (final Node child : this.children)\n                    {\n                        result.addAll(child.leafNodesIntersecting(polyLine, polyLineBounds));\n                    }\n                }\n            }\n            return result;\n        }\n\n        public LocationIterableProperties toGeoJsonBuildingBlock()\n        {\n            final Map<String, String> tags = new HashMap<>();\n            tags.put(\"tile\", this.name());\n            return new GeoJsonBuilder.LocationIterableProperties(bounds(), tags);\n        }\n\n        protected void build(final Predicate<SlippyTile> shouldSplit)\n        {\n            if (this.zoom() < SlippyTile.MAX_ZOOM && shouldSplit.test(this.tile))\n            {\n                this.split();\n                for (final Node child : this.children)\n                {\n                    child.build(shouldSplit);\n                }\n            }\n        }\n\n        protected boolean isFinal()\n        {\n            return this.children.isEmpty();\n        }\n\n        protected String name()\n        {\n            return this.tile.getName();\n        }\n\n        protected Set<Node> neighbors(final SlippyTile targetTile)\n        {\n            final Set<Node> neighboringNodes = new HashSet<>();\n\n            for (final Node leafNode : this.leafNodes(targetTile.bounds()))\n            {\n                final Rectangle expandedBoundary = leafNode.bounds()\n                        .expand(SlippyTile.calculateExpansionDistance(leafNode.bounds()));\n                if (targetTile.bounds().overlaps(expandedBoundary)\n                        && !leafNode.bounds().equals(targetTile.bounds()))\n                {\n                    neighboringNodes.add(leafNode);\n                }\n            }\n\n            return neighboringNodes;\n        }\n\n        protected void save(final WritableResource resource)\n        {\n            final BufferedWriter writer = resource.writer();\n            try\n            {\n                this.save(writer);\n            }\n            catch (final Exception e)\n            {\n                Streams.close(writer);\n                throw e;\n            }\n            Streams.close(writer);\n        }\n\n        protected void split()\n        {\n            this.children.addAll(this.tile.split(this.zoom() + 1).stream().map(Node::new)\n                    .collect(Collectors.toList()));\n        }\n\n        protected int zoom()\n        {\n            return this.tile.getZoom();\n        }\n\n        /**\n         * Does a deep equals with the other node\n         *\n         * @param other\n         *            other Node\n         * @return true if entire structure is equal, false if not\n         */\n        private boolean deepEquals(final Node other)\n        {\n            final Comparator<Node> nodeCompare = Comparator.comparing(Node::getTile);\n            // BFS through both trees to get equality\n            final Queue<Node> queue = new LinkedList<>();\n            queue.offer(this);\n            queue.offer(other);\n            while (!queue.isEmpty())\n            {\n                // We always offer two at a time, so we can poll two at a time.\n                final Node node1 = queue.poll();\n                final Node node2 = queue.poll();\n                if (node1.equals(node2) && node1.getChildren().size() == node2.getChildren().size())\n                {\n                    final List<Node> children1 = node1.getChildren();\n                    final List<Node> children2 = node2.getChildren();\n                    children1.sort(nodeCompare);\n                    children2.sort(nodeCompare);\n                    for (int index = 0; index < children1.size(); index++)\n                    {\n                        queue.offer(children1.get(index));\n                        queue.offer(children2.get(index));\n                    }\n                }\n                else\n                {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        private List<Node> getChildren()\n        {\n            return this.children;\n        }\n\n        private void save(final BufferedWriter writer)\n        {\n            try\n            {\n                writer.write(this.tile.getName());\n                if (!isFinal())\n                {\n                    writer.write(\"+\");\n                }\n                writer.write(\"\\n\");\n            }\n            catch (final Exception e)\n            {\n                throw new CoreException(\"Unable to write slippy tile {}\", this.tile, e);\n            }\n            for (final Node child : this.children)\n            {\n                child.save(writer);\n            }\n        }\n    }\n\n    public static final Switch<Resource> DEFINITION = new Switch<>(\"definition\",\n            \"Resource containing the maxZoom - 1 tile to feature count mapping.\", File::new,\n            Optionality.REQUIRED);\n    public static final Switch<WritableResource> GEOJSON = new Switch<>(\"geoJson\",\n            \"The resource where to save the geojson tree for debugging\", File::new,\n            Optionality.OPTIONAL);\n    public static final Switch<Integer> MAXIMUM_COUNT = new Switch<>(\"maxCount\",\n            \"The maximum feature count. Any cell with a larger feature count will be split, up to maxZoom\",\n            Integer::valueOf, Optionality.OPTIONAL, \"200000\");\n    public static final Switch<Integer> MAXIMUM_ZOOM = new Switch<>(\"maxZoom\", \"The maximum zoom\",\n            Integer::valueOf, Optionality.OPTIONAL, \"10\");\n    public static final Switch<Integer> MINIMUM_ZOOM = new Switch<>(\"minZoom\", \"The minimum zoom\",\n            Integer::valueOf, Optionality.OPTIONAL, \"5\");\n    public static final Switch<WritableResource> OUTPUT = new Switch<>(\"output\",\n            \"The resource where to save the serialized tree.\", File::new, Optionality.REQUIRED);\n    private static final int MINIMUM_TO_SPLIT = 1_000;\n    private static final int READER_REPORT_FREQUENCY = 10_000_000;\n    private static final Logger logger = LoggerFactory.getLogger(DynamicTileSharding.class);\n    private static final long serialVersionUID = 229952569300405488L;\n    // The root of the tree for this dynamic sharding\n    private final Node root;\n    private final String resourceName;\n\n    public static void main(final String[] args)\n    {\n        new DynamicTileSharding().run(args);\n    }\n\n    /**\n     * Construct.\n     *\n     * @param resource\n     *            The resource containing the serialized tree definition.\n     */\n    public DynamicTileSharding(final Resource resource)\n    {\n        this.root = Node.read(resource);\n        this.resourceName = resource.getName();\n    }\n\n    /**\n     * Construct, with a root that covers the whole world.\n     */\n    private DynamicTileSharding()\n    {\n        this.root = new Node();\n        this.resourceName = \"N/A\";\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n\n        if (other == null || getClass() != other.getClass())\n        {\n            return false;\n        }\n\n        final DynamicTileSharding that = (DynamicTileSharding) other;\n        return this.root.deepEquals(that.root);\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"dynamic@\" + this.resourceName;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(this.root);\n    }\n\n    @Override\n    public Iterable<Shard> neighbors(final Shard shard)\n    {\n        return this.root.neighbors(SlippyTile.forName(shard.getName())).stream().map(Node::getTile)\n                .collect(Collectors.toList());\n    }\n\n    /**\n     * Save the tree to a {@link WritableResource}\n     *\n     * @param resource\n     *            The {@link WritableResource} to serialize the tree definition to.\n     */\n    public void save(final WritableResource resource)\n    {\n        this.root.save(resource);\n    }\n\n    public void saveAsGeoJson(final WritableResource resource)\n    {\n        final JsonWriter writer = new JsonWriter(resource);\n        final GeoJsonObject geoJson = new GeoJsonBuilder().create(Iterables\n                .translate(this.root.leafNodes(Rectangle.MAXIMUM), Node::toGeoJsonBuildingBlock));\n        writer.write(geoJson.jsonObject());\n        writer.close();\n    }\n\n    @Override\n    public Shard shardForName(final String name)\n    {\n        final SlippyTile result = SlippyTile.forName(name);\n        if (!this.root.leafNodesCovering(result.bounds().center()).contains(new Node(result)))\n        {\n            throw new CoreException(\"This tree does not contain tile {}\", name);\n        }\n        return result;\n    }\n\n    @Override\n    public Iterable<Shard> shards(final GeometricSurface surface)\n    {\n        return Iterables.stream(this.root.leafNodes(surface)).map(Node::getTile);\n    }\n\n    @Override\n    public Iterable<Shard> shardsCovering(final Location location)\n    {\n        return Iterables.stream(this.root.leafNodesCovering(location)).map(Node::getTile);\n    }\n\n    @Override\n    public Iterable<Shard> shardsIntersecting(final PolyLine polyLine)\n    {\n        return Iterables.stream(this.root.leafNodesIntersecting(polyLine)).map(Node::getTile);\n    }\n\n    /**\n     * Calculates and saves the counts for each zoom layer lower than firstZoomLayerToGenerate.\n     *\n     * @param firstZoomLayerToGenerate\n     *            the first zoom layer for which to generate counts\n     * @param counts\n     *            Map containing counts for all {@link SlippyTile}s in firstZoomLayerToGenerate+1\n     * @return a Map containing counts for all (@link SlippyTile}s in zoomLayerToGenerate and below\n     */\n    protected Map<SlippyTile, Long> calculateTileCountsForAllZoom(\n            final int firstZoomLayerToGenerate, final Map<SlippyTile, Long> counts)\n    {\n        for (int currentZoom = firstZoomLayerToGenerate; currentZoom >= 0; currentZoom--)\n        {\n            long count = 0;\n            long tilesCalculated = 0;\n            for (int x = 0; x < Math.pow(2, currentZoom + 1.0); x += 2)\n            {\n                for (int y = 0; y < Math.pow(2, currentZoom + 1.0); y += 2)\n                {\n                    count = 0;\n                    // top left\n                    count += counts.getOrDefault(new SlippyTile(x, y, currentZoom + 1), (long) 0);\n                    // top right\n                    count += counts.getOrDefault(new SlippyTile(x + 1, y, currentZoom + 1),\n                            (long) 0);\n                    // bottom left\n                    count += counts.getOrDefault(new SlippyTile(x, y + 1, currentZoom + 1),\n                            (long) 0);\n                    // bottom right\n                    count += counts.getOrDefault(new SlippyTile(x + 1, y + 1, currentZoom + 1),\n                            (long) 0);\n                    if (count != 0)\n                    {\n                        counts.put(new SlippyTile(x / 2, y / 2, currentZoom), count);\n                    }\n                    if (++tilesCalculated % READER_REPORT_FREQUENCY == 0)\n                    {\n                        logger.info(\"Calculated {} zoom level {} tiles.\", tilesCalculated,\n                                currentZoom);\n                    }\n                }\n            }\n        }\n        return counts;\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Resource definition = (Resource) command.get(DEFINITION);\n        final int numberLines = (int) Iterables.size(definition.lines());\n        logger.info(\"There are {} tiles.\", numberLines);\n        final Map<SlippyTile, Long> counts = new HashMap<>(numberLines);\n        final WritableResource output = (WritableResource) command.get(OUTPUT);\n        final int maximum = (int) command.get(MAXIMUM_COUNT);\n        final int minimumZoom = (int) command.get(MINIMUM_ZOOM);\n        final int maximumZoom = (int) command.get(MAXIMUM_ZOOM);\n        final WritableResource geoJson = (WritableResource) command.get(GEOJSON);\n        int zoom = 0;\n        int counter = 0;\n        for (final String line : definition.lines())\n        {\n            final StringList split = StringList.split(line, \",\");\n            final SlippyTile tile = SlippyTile.forName(split.get(0));\n            counts.put(tile, Long.valueOf(split.get(1)));\n            zoom = tile.getZoom();\n            if (++counter % READER_REPORT_FREQUENCY == 0)\n            {\n                logger.info(\"Read counts for {} zoom level {} tiles.\", counter, zoom);\n            }\n        }\n        // maximumZoom is decremented by 2 because it represents the highest zoom that the sharding\n        // tree will split to. The input CSV (definition) has count information at the\n        // (maximumZoom-1) level, which is already put into the HashMap \"counts\" by the code above.\n        // Therefore we want to start calculating counts one level below, which is (maximumZoom-2)\n        final Map<SlippyTile, Long> allCounts = calculateTileCountsForAllZoom(maximumZoom - 2,\n                counts);\n        if (zoom == 0)\n        {\n            throw new CoreException(\"No tiles in definition\");\n        }\n        final int finalZoom = zoom;\n        if (maximumZoom > finalZoom + 1)\n        {\n            throw new CoreException(\n                    \"Cannot go over the resolution of the counts definition. \"\n                            + \"MaxZoom = {} has to be at most equal to definition zoom + 1 = {}\",\n                    maximumZoom, finalZoom);\n        }\n        this.root.build(tile ->\n        {\n            final long count = allCounts.getOrDefault(tile, (long) 0);\n            if (count <= MINIMUM_TO_SPLIT)\n            {\n                return false;\n            }\n            if (tile.getZoom() < minimumZoom)\n            {\n                return true;\n            }\n            if (tile.getZoom() >= maximumZoom)\n            {\n                return false;\n            }\n            return count > maximum;\n        });\n        this.save(output);\n        final String outputLocation = lastRawCommand(OUTPUT);\n        logger.info(\"Printed tree to {}. Loading for verification...\", outputLocation);\n        new DynamicTileSharding(new File(outputLocation));\n        logger.info(\"Successfully loaded tree from {}\", outputLocation);\n        if (geoJson != null)\n        {\n            if (logger.isInfoEnabled())\n            {\n                logger.info(\"Saving geojson to {}...\", lastRawCommand(GEOJSON));\n            }\n            this.saveAsGeoJson(geoJson);\n        }\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(DEFINITION, OUTPUT, MINIMUM_ZOOM, MAXIMUM_ZOOM, MAXIMUM_COUNT,\n                GEOJSON);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/GeoHashSharding.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class GeoHashSharding implements Sharding\n{\n    private static final long serialVersionUID = -7355746343440111174L;\n\n    private final int precision;\n\n    public GeoHashSharding(final int precision)\n    {\n        GeoHashTile.validatePrecision(precision);\n        this.precision = precision;\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"geohash@\" + this.precision;\n    }\n\n    public int getPrecision()\n    {\n        return this.precision;\n    }\n\n    @Override\n    public Iterable<Shard> neighbors(final Shard shard)\n    {\n        if (shard instanceof GeoHashTile)\n        {\n            return ((GeoHashTile) shard).neighbors();\n        }\n        else\n        {\n            throw new CoreException(\"Shard parameter was of invalid type {}\",\n                    shard.getClass().getName());\n        }\n    }\n\n    @Override\n    public Shard shardForName(final String name)\n    {\n        if (name.length() != this.precision)\n        {\n            throw new CoreException(\n                    \"This geohash sharding is of precision {}. \\\"{}\\\" is not the correct length.\",\n                    this.precision, name);\n        }\n        return GeoHashTile.forName(name);\n    }\n\n    @Override\n    public Iterable<Shard> shards(final GeometricSurface surface)\n    {\n        return Iterables.stream(GeoHashTile.allTiles(this.precision, surface))\n                .map(tile -> (Shard) tile);\n    }\n\n    @Override\n    public Iterable<Shard> shardsCovering(final Location location)\n    {\n        return Iterables.stream(Iterables.from(GeoHashTile.covering(location, this.precision)))\n                .map(tile -> (Shard) tile);\n    }\n\n    @Override\n    public Iterable<Shard> shardsIntersecting(final PolyLine polyLine)\n    {\n        return Iterables.stream(GeoHashTile.allTiles(this.precision, polyLine.bounds()))\n                .filter(tile -> tile.bounds().intersects(polyLine)).map(tile -> (Shard) tile);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/GeoHashTile.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport org.locationtech.spatial4j.context.SpatialContext;\nimport org.locationtech.spatial4j.io.GeohashUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.sharding.converters.RectangleToSpatial4JRectangleConverter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\nimport com.google.common.collect.BiMap;\nimport com.google.common.collect.HashBiMap;\nimport com.google.gson.JsonObject;\n\n/**\n * @author matthieun\n */\npublic class GeoHashTile implements Shard\n{\n    public static final int MAXIMUM_PRECISION = 12;\n    public static final GeoHashTile ROOT = new GeoHashTile(\"\");\n\n    static final char[] GEOHASH_CHARACTERS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n            'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u',\n            'v', 'w', 'x', 'y', 'z' };\n    static final BiMap<Integer, Character> GEOHASH_CHARACTER_MAP = HashBiMap.create();\n\n    private static final long serialVersionUID = 525101912087621541L;\n    private static final RectangleToSpatial4JRectangleConverter RECTANGLE_TO_SPATIAL_4_J_RECTANGLE_CONVERTER = new RectangleToSpatial4JRectangleConverter();\n\n    static\n    {\n        for (int index = 0; index < GEOHASH_CHARACTERS.length; index++)\n        {\n            GEOHASH_CHARACTER_MAP.put(index, GEOHASH_CHARACTERS[index]);\n        }\n    }\n\n    private final String value;\n    private Rectangle bounds;\n\n    public static Iterable<GeoHashTile> allTiles(final int precision)\n    {\n        validatePrecision(precision);\n        if (precision == 0)\n        {\n            return Iterables.iterable(GeoHashTile.ROOT);\n        }\n        return new GeoHashTileIterable(precision);\n    }\n\n    public static Iterable<GeoHashTile> allTiles(final int precision,\n            final GeometricSurface surface)\n    {\n        validatePrecision(precision);\n        if (precision == 0)\n        {\n            return Iterables.iterable(GeoHashTile.ROOT);\n        }\n        return new GeoHashTileIterable(precision, surface);\n    }\n\n    public static GeoHashTile covering(final Location location, final int precision)\n    {\n        validatePrecision(precision);\n        return new GeoHashTile(GeohashUtils.encodeLatLon(location.getLatitude().asDegrees(),\n                location.getLongitude().asDegrees(), precision));\n    }\n\n    public static GeoHashTile forName(final String value)\n    {\n        return new GeoHashTile(value);\n    }\n\n    public static long numberTilesAtPrecision(final int precision)\n    {\n        if (precision == 0)\n        {\n            return 1L;\n        }\n        return (long) Math.pow((double) GEOHASH_CHARACTERS.length, (double) precision);\n    }\n\n    public static void validatePrecision(final int precision)\n    {\n        if (precision > MAXIMUM_PRECISION)\n        {\n            throw new CoreException(\"Cannot have precision {} > {}\", precision, MAXIMUM_PRECISION);\n        }\n        if (precision < 0)\n        {\n            throw new CoreException(\"Cannot have precision {} < 0\", precision);\n        }\n    }\n\n    public GeoHashTile(final String value)\n    {\n        this.value = value;\n    }\n\n    @Override\n    public JsonObject asGeoJson()\n    {\n        return bounds().asGeoJson();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        if (this.bounds == null)\n        {\n            if (this.value.isEmpty())\n            {\n                this.bounds = Rectangle.MAXIMUM;\n            }\n            else\n            {\n                this.bounds = RECTANGLE_TO_SPATIAL_4_J_RECTANGLE_CONVERTER\n                        .convert(GeohashUtils.decodeBoundary(this.value, SpatialContext.GEO));\n            }\n        }\n        return this.bounds;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof GeoHashTile)\n        {\n            return this.value.equals(((GeoHashTile) other).getName());\n        }\n        return false;\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return bounds().getGeoJsonType();\n    }\n\n    @Override\n    public String getName()\n    {\n        return this.value;\n    }\n\n    public int getPrecision()\n    {\n        return this.value.length();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.value.hashCode();\n    }\n\n    public Iterable<Shard> neighbors()\n    {\n        return Iterables.stream(GeoHashTile.allTiles(this.getPrecision(), bounds()))\n                .filter(tile -> !this.equals(tile)).map(tile -> (Shard) tile).collect();\n    }\n\n    public char[] toCharArray()\n    {\n        return this.value.toCharArray();\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[GeoHashTile: value = \" + this.value + \"]\";\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return bounds().toWkb();\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return bounds().toWkt();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/GeoHashTileIterable.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.NoSuchElementException;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\n\n/**\n * @author matthieun\n */\nclass GeoHashTileIterable implements Iterable<GeoHashTile>\n{\n    /**\n     * @author matthieun\n     */\n    private class GeoHashTileIterator implements Iterator<GeoHashTile>\n    {\n        private final char[] current = Arrays.copyOf(GeoHashTileIterable.this.starting,\n                GeoHashTileIterable.this.precision);\n        private boolean done = false;\n        private boolean firstCall = true;\n        private int backIndex = GeoHashTileIterable.this.precision - 1;\n\n        @Override\n        public boolean hasNext()\n        {\n            return !this.done;\n        }\n\n        @Override\n        public GeoHashTile next()\n        {\n            if (!hasNext())\n            {\n                throw new NoSuchElementException();\n            }\n            GeoHashTile result = new GeoHashTile(String.valueOf(this.current));\n            if (!isMaximum() && this.firstCall)\n            {\n                while (!this.done && !overlaps(result))\n                {\n                    upgrade();\n                    result = new GeoHashTile(String.valueOf(this.current));\n                }\n            }\n            upgrade();\n            while (!isMaximum() && !this.done\n                    && !overlaps(new GeoHashTile(String.valueOf(this.current))))\n            {\n                upgrade();\n            }\n            this.firstCall = false;\n            return result;\n        }\n\n        private boolean overlaps(final GeoHashTile tile)\n        {\n            return GeoHashTileIterable.this.surface.overlaps(tile.bounds());\n        }\n\n        private void tick(final int index)\n        {\n            final char currentChar = this.current[index];\n            final int currentCharIndex = GeoHashTile.GEOHASH_CHARACTER_MAP.inverse()\n                    .get(currentChar);\n            this.current[index] = GeoHashTile.GEOHASH_CHARACTER_MAP.get(currentCharIndex + 1);\n        }\n\n        private void upgrade()\n        {\n            final char lastCharacter = GeoHashTile.GEOHASH_CHARACTERS[GeoHashTile.GEOHASH_CHARACTERS.length\n                    - 1];\n            while (this.backIndex >= GeoHashTileIterable.this.prefix.length\n                    && this.current[this.backIndex] == lastCharacter)\n            {\n                this.backIndex--;\n            }\n            if (this.backIndex < GeoHashTileIterable.this.prefix.length)\n            {\n                this.done = true;\n                return;\n            }\n            tick(this.backIndex);\n            if (this.backIndex < GeoHashTileIterable.this.precision - 1)\n            {\n                zero(this.backIndex + 1);\n                this.backIndex = GeoHashTileIterable.this.precision - 1;\n            }\n        }\n\n        private void zero(final int index)\n        {\n            for (int subIndex = index; subIndex < GeoHashTileIterable.this.precision; subIndex++)\n            {\n                this.current[subIndex] = GeoHashTile.GEOHASH_CHARACTERS[0];\n            }\n        }\n    }\n\n    private final char[] starting;\n    private final char[] prefix;\n    private final int precision;\n    private final GeometricSurface surface;\n    private final boolean isMaximum;\n\n    GeoHashTileIterable(final int precision)\n    {\n        this(precision, Rectangle.MAXIMUM);\n    }\n\n    GeoHashTileIterable(final int precision, final GeometricSurface surface)\n    {\n        this.precision = precision;\n        this.surface = surface;\n        this.starting = new char[precision];\n        this.prefix = prefix();\n        this.isMaximum = surface instanceof Rectangle && Rectangle.MAXIMUM.equals(surface);\n        for (int index = 0; index < this.prefix.length; index++)\n        {\n            this.starting[index] = this.prefix[index];\n        }\n        for (int index = this.prefix.length; index < precision; index++)\n        {\n            this.starting[index] = GeoHashTile.GEOHASH_CHARACTERS[0];\n        }\n    }\n\n    @Override\n    public Iterator<GeoHashTile> iterator()\n    {\n        return new GeoHashTileIterator();\n    }\n\n    private boolean isMaximum()\n    {\n        return this.isMaximum;\n    }\n\n    /**\n     * @return The prefix of geohash that totally covers the bounds of the surface\n     */\n    private char[] prefix() // NOSONAR\n    {\n        if (isMaximum())\n        {\n            return new char[0];\n        }\n        final List<char[]> geoHashes = new ArrayList<>();\n        for (final Location corner : this.surface.bounds())\n        {\n            geoHashes.add(GeoHashTile.covering(corner, this.precision).toCharArray());\n        }\n        final List<Character> prefixAsList = new ArrayList<>();\n        for (int index = 0; index < this.precision; index++)\n        {\n            Character candidate = null;\n            boolean valid = true;\n            for (int cornerIndex = 0; cornerIndex < geoHashes.size(); cornerIndex++)\n            {\n                final char cornerCharacter = geoHashes.get(cornerIndex)[index];\n                if (candidate == null)\n                {\n                    candidate = cornerCharacter;\n                }\n                else if (!candidate.equals(cornerCharacter))\n                {\n                    valid = false;\n                    break;\n                }\n\n            }\n            if (valid)\n            {\n                prefixAsList.add(candidate);\n            }\n            else\n            {\n                break;\n            }\n        }\n        final char[] result = new char[prefixAsList.size()];\n        for (int index = 0; index < prefixAsList.size(); index++)\n        {\n            result[index] = prefixAsList.get(index);\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/LocationToShardCommand.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * @author matthieun\n */\npublic class LocationToShardCommand extends Command\n{\n    private static final Switch<Sharding> SHARDING = new Switch<>(\"sharding\", \"The sharding tree\",\n            Sharding::forString, Optionality.REQUIRED);\n    private static final Switch<Location> LOCATION = new Switch<>(\"location\",\n            \"The location to check as \\\"latitude,longitude\\\"\", Location::forString,\n            Optionality.OPTIONAL);\n    private static final Switch<Location> WKT_POINT = new Switch<>(\"wktPoint\",\n            \"The location to check as a WKT point\", Location::forWkt, Optionality.OPTIONAL);\n\n    public static void main(final String[] args)\n    {\n        new LocationToShardCommand().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Sharding sharding = (Sharding) command.get(SHARDING);\n        Location location = (Location) command.get(LOCATION);\n        if (location == null)\n        {\n            location = (Location) command.get(WKT_POINT);\n        }\n        if (location == null)\n        {\n            System.err.println(\"No location found! Make sure to use either the -\"\n                    + LOCATION.getName() + \" or -\" + WKT_POINT.getName() + \" switch.\");\n            return 0;\n        }\n        final Set<? extends Shard> shards = Iterables.asSet(sharding.shardsCovering(location));\n        if (shards.size() > 0)\n        {\n            System.out.println(\"Shard(s) covering \" + location.toCompactString() + \":\");\n            shards.forEach(\n                    shard -> System.out.println(shard.getName() + \"; \" + shard.bounds().toWkt()));\n        }\n        else\n        {\n            System.err.println(\"No shard found!\");\n            return 1;\n        }\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(SHARDING, LOCATION, WKT_POINT);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/README.md",
    "content": "# Sharding\n\nSharding is a way to split the world in small units of work, with the goal of having each unit be roughly the same effort.\n\n## SlippyTileSharding\n\nEach shard is a bounding box referenced with a zoom-level, x and y coordinates. All shards have the same zoom-level and the same size.\n\nExample: Invoke with `Sharding.forString(\"slippy@10\");`\n\n## DynamicTreeSharding\n\nEach shard is a bounding box referenced with a zoom-level, x and y coordinates. All shards are leaf nodes in a quad tree, the root of which is `Rectangle.MAXIMUM`. The quad tree can be serialized to a simple text file.\n\nExample: Invoke with `Sharding.forString(\"dynamic@file:///path/to/tree.txt\");`\n\n## GeohashSharding\n\nEach shard is a bounding box referenced with a geohash encoded string. All shards have the same precision and the same size.\n\nExample: Invoke with `Sharding.forString(\"geohash@4\");`\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/Shard.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.geography.GeometryPrintable;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.sharding.converters.StringToShardConverter;\n\n/**\n * A {@link Sharding} shard.\n *\n * @author matthieun\n */\npublic interface Shard extends Located, Serializable, GeometryPrintable // NOSONAR\n{\n    /**\n     * The separator character for data within a shard string. For example, this character should be\n     * used to separate the country code from the shard name: USA_1-2-3. It should also be used to\n     * separate metadata from the shard name: USA_4-5-6_zz/xx/yy\n     */\n    String SHARD_DATA_SEPARATOR = \"_\";\n\n    /**\n     * Get the name of this {@link Shard}. The result of this method should be parsable by\n     * {@link StringToShardConverter}.\n     * \n     * @return the parsable name of this {@link Shard}\n     */\n    String getName();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/Sharding.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.io.Serializable;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.geojson.GeoJson;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\n/**\n * Sharding strategy\n *\n * @author matthieun\n */\npublic interface Sharding extends Serializable, GeoJson\n{\n    int SHARDING_STRING_SPLIT = 2;\n    int SLIPPY_ZOOM_MAXIMUM = 18;\n\n    /**\n     * Parse a sharding definition string and use the given {@link FileSystem} if the {@link String}\n     * references a {@link DynamicTileSharding}.\n     *\n     * @param sharding\n     *            The definition string\n     * @param fileSystem\n     *            the {@link FileSystem} to use for a {@link DynamicTileSharding}\n     * @return The corresponding {@link Sharding} instance.\n     */\n    static Sharding forString(final String sharding, final FileSystem fileSystem)\n    {\n        final StringList split;\n        split = StringList.split(sharding, \"@\");\n        if (split.size() != SHARDING_STRING_SPLIT)\n        {\n            throw new CoreException(\n                    \"Invalid sharding string: {} (correct e.g. dynamic@/path/to/tree, slippy@9, etc.)\",\n                    sharding);\n        }\n        if (\"slippy\".equals(split.get(0)))\n        {\n            final int zoom;\n            zoom = Integer.valueOf(split.get(1));\n            if (zoom > SLIPPY_ZOOM_MAXIMUM)\n            {\n                throw new CoreException(\"Slippy Sharding zoom too high : {}, max is {}\", zoom,\n                        SLIPPY_ZOOM_MAXIMUM);\n            }\n            return new SlippyTileSharding(zoom);\n        }\n        if (\"geohash\".equals(split.get(0)))\n        {\n            final int precision;\n            precision = Integer.valueOf(split.get(1));\n            return new GeoHashSharding(precision);\n        }\n        if (\"dynamic\".equals(split.get(0)))\n        {\n            final String definition = split.get(1);\n            return new DynamicTileSharding(new File(definition, fileSystem));\n        }\n        throw new CoreException(\"Sharding type {} is not recognized.\", split.get(0));\n    }\n\n    /**\n     * Parse a sharding definition string and use the default {@link FileSystem} if the\n     * {@link String} references a {@link DynamicTileSharding}.\n     *\n     * @param sharding\n     *            The definition string\n     * @return The corresponding {@link Sharding} instance.\n     */\n    static Sharding forString(final String sharding)\n    {\n        return Sharding.forString(sharding, FileSystems.getDefault());\n    }\n\n    @Override\n    default JsonObject asGeoJson()\n    {\n        final JsonObject featureCollectionObject = new JsonObject();\n        featureCollectionObject.addProperty(\"type\", \"FeatureCollection\");\n        final JsonArray features = new JsonArray();\n        for (final Shard shard : this.shards(Rectangle.MAXIMUM))\n        {\n            final JsonObject featureObject = new JsonObject();\n            featureObject.addProperty(\"type\", \"Feature\");\n            featureObject.add(\"geometry\",\n                    new PolyLine(shard.bounds().closedLoop()).asGeoJsonGeometry());\n            final JsonObject propertiesObject = new JsonObject();\n            propertiesObject.addProperty(\"shard\", shard.getName());\n            featureObject.add(\"properties\", propertiesObject);\n            features.add(featureObject);\n        }\n        featureCollectionObject.add(\"features\", features);\n        return featureCollectionObject;\n    }\n\n    @Override\n    default GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.FEATURE_COLLECTION;\n    }\n\n    /**\n     * Get the name of this {@link Sharding}. The name should succinctly describe this\n     * {@link Sharding}'s parameters.\n     *\n     * @return the name of this {@link Sharding}\n     */\n    String getName();\n\n    /**\n     * Get the neighboring shards for a given shard.\n     *\n     * @param shard\n     *            The shard for which to get neighbors\n     * @return The shards {@link Iterable}, neighboring the supplied shard\n     */\n    Iterable<Shard> neighbors(Shard shard);\n\n    /**\n     * Get a shard given its name\n     *\n     * @param name\n     *            The name of the shard\n     * @return The corresponding shard\n     */\n    Shard shardForName(String name);\n\n    /**\n     * Generate shards. This needs to be deterministic!\n     *\n     * @param surface\n     *            The bounds to limit the shards.\n     * @return The shards {@link Iterable}.\n     */\n    Iterable<Shard> shards(GeometricSurface surface);\n\n    /**\n     * Generate shards for the whole planet. This needs to be deterministic!\n     *\n     * @return The shards {@link Iterable}, covering the whole planet.\n     */\n    default Iterable<Shard> shards()\n    {\n        return shards(Rectangle.MAXIMUM);\n    }\n\n    /**\n     * Generate shards. This needs to be deterministic!\n     *\n     * @param location\n     *            The location to find\n     * @return The shards {@link Iterable} (In case the location falls right at the boundary between\n     *         shards)\n     */\n    Iterable<Shard> shardsCovering(Location location);\n\n    /**\n     * Generate shards. This needs to be deterministic!\n     *\n     * @param polyLine\n     *            The line intersecting the shards\n     * @return The shards {@link Iterable}.\n     */\n    Iterable<Shard> shardsIntersecting(PolyLine polyLine);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/SlippyTile.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.NoSuchElementException;\nimport java.util.Queue;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.geography.sharding.converters.SlippyTileConverter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\nimport com.google.gson.JsonObject;\n\n/**\n * OSM Slippy tile\n *\n * @see \"http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames\"\n * @author matthieun\n * @author mgostintsev\n */\npublic class SlippyTile implements Shard, Comparable<SlippyTile>\n{\n    public static final SlippyTile ROOT = new SlippyTile(0, 0, 0);\n    public static final int MAX_ZOOM = 30;\n    public static final String COORDINATE_SEPARATOR = \"-\";\n\n    private static final long serialVersionUID = -3752920878013084039L;\n    private static final SlippyTileConverter CONVERTER = new SlippyTileConverter();\n    private static final double CIRCULAR_MULTIPLIER = 2.0;\n    private static final double ZOOM_LEVEL_POWER = 2.0;\n    private static final int BIT_SHIFT = 2;\n    private static final double FULL_ROTATION_DEGREES = 360.0;\n    private static final double HALF_ROTATION_DEGREES = 180.0;\n    private static final double LONGITUDE_BOUNDARY = 180;\n    private static final double NEIGHBOR_EXPANSION_SCALE = 0.10;\n\n    private Rectangle bounds;\n    private final int xAxis;\n    private final int yAxis;\n    private final int zoom;\n\n    /**\n     * All tiles at some zoom level\n     *\n     * @param zoom\n     *            The zoom to consider\n     * @return All tiles at some zoom level\n     */\n    public static Iterable<SlippyTile> allTiles(final int zoom)\n    {\n        return allTiles(zoom, Rectangle.MAXIMUM);\n    }\n\n    /**\n     * All tiles within some bounds\n     *\n     * @param zoom\n     *            The zoom to consider\n     * @param bounds\n     *            The bounds to consider\n     * @return All tiles within some bounds\n     */\n    public static Iterable<SlippyTile> allTiles(final int zoom, final Rectangle bounds)\n    {\n        if (zoom > MAX_ZOOM)\n        {\n            throw new CoreException(\"Zoom too large.\");\n        }\n        final Iterable<SlippyTile> result = () -> allTilesIterator(zoom, bounds);\n        final List<SlippyTile> list = Iterables.asList(result);\n        if (list.isEmpty())\n        {\n            throw new CoreException(\"List cannot be empty\");\n        }\n        return list;\n    }\n\n    /**\n     * Iterator for all tiles within some bounds\n     *\n     * @param zoom\n     *            The zoom to consider\n     * @param bounds\n     *            The bounds to consider\n     * @return Iterator for all tiles within some bounds\n     */\n    public static Iterator<SlippyTile> allTilesIterator(final int zoom, final Rectangle bounds)\n    {\n        if (zoom > MAX_ZOOM)\n        {\n            throw new CoreException(\"Zoom too large.\");\n        }\n        final SlippyTile lowerLeft = new SlippyTile(bounds.lowerLeft(), zoom);\n        final SlippyTile upperRight = new SlippyTile(bounds.upperRight(), zoom);\n        final int minX = lowerLeft.getX();\n        final int maxX = upperRight.getX();\n        final int minY = upperRight.getY();\n        final int maxY = lowerLeft.getY();\n        return new Iterator<SlippyTile>()\n        {\n            private int xAxis = minX;\n            private int yAxis = minY;\n\n            @Override\n            public boolean hasNext()\n            {\n                return this.yAxis <= maxY && this.xAxis <= maxX;\n            }\n\n            @Override\n            public SlippyTile next()\n            {\n                if (!hasNext())\n                {\n                    throw new NoSuchElementException();\n                }\n                final SlippyTile result = new SlippyTile(this.xAxis, this.yAxis, zoom);\n                this.xAxis++;\n                if (this.xAxis > maxX)\n                {\n                    this.yAxis++;\n                    this.xAxis = minX;\n                }\n                return result;\n            }\n        };\n    }\n\n    /**\n     * Expansion distance will be the smaller of the height/width scaled by 1/4.\n     *\n     * @param bounds\n     *            The bounds\n     * @return The smaller of the height/width scaled by 1/4\n     */\n    public static Distance calculateExpansionDistance(final Rectangle bounds)\n    {\n        final Distance shorterSide = bounds.width().onEarth().isLessThanOrEqualTo(\n                bounds.height().onEarth()) ? bounds.width().onEarth() : bounds.height().onEarth();\n        return shorterSide.scaleBy(NEIGHBOR_EXPANSION_SCALE);\n    }\n\n    public static SlippyTile forName(final String name)\n    {\n        return CONVERTER.backwardConvert(name);\n    }\n\n    /**\n     * Construct\n     *\n     * @param xAxis\n     *            The x index (along north-south)\n     * @param yAxis\n     *            The y index (along west-east)\n     * @param zoom\n     *            The zoom level\n     */\n    public SlippyTile(final int xAxis, final int yAxis, final int zoom)\n    {\n        if (zoom > MAX_ZOOM)\n        {\n            throw new CoreException(\"Zoom {} is too large.\", zoom);\n        }\n        this.zoom = zoom;\n        this.xAxis = xAxis;\n        this.yAxis = yAxis;\n    }\n\n    /**\n     * Construct\n     *\n     * @param location\n     *            The location to get the overlapping {@link SlippyTile}\n     * @param zoom\n     *            The zoom level\n     */\n    public SlippyTile(final Location location, final int zoom)\n    {\n        if (zoom > MAX_ZOOM)\n        {\n            throw new CoreException(\"Zoom {} is too large.\", zoom);\n        }\n        final List<Integer> tileNumbers = this.getTileNumbers(location, zoom);\n        this.zoom = zoom;\n        this.xAxis = tileNumbers.get(0);\n        this.yAxis = tileNumbers.get(1);\n    }\n\n    @Override\n    public JsonObject asGeoJson()\n    {\n        return bounds().asGeoJsonGeometry();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        if (this.bounds == null)\n        {\n            this.bounds = tile2boundingBox(this.xAxis, this.yAxis, this.zoom);\n        }\n        return this.bounds;\n    }\n\n    @Override\n    public int compareTo(final SlippyTile other)\n    {\n        // Order by z-level, x-value and then y-value\n        final int zoomLevelDelta = this.getZoom() - other.getZoom();\n        if (zoomLevelDelta > 0)\n        {\n            return 1;\n        }\n        else if (zoomLevelDelta < 0)\n        {\n            return -1;\n        }\n        else\n        {\n            final int xDelta = this.getX() - other.getX();\n            if (xDelta > 0)\n            {\n                return 1;\n            }\n            else if (xDelta < 0)\n            {\n                return -1;\n            }\n            else\n            {\n                final int yDelta = this.getY() - other.getY();\n                if (yDelta > 0)\n                {\n                    return 1;\n                }\n                else if (yDelta < 0)\n                {\n                    return -1;\n                }\n                else\n                {\n                    return 0;\n                }\n            }\n        }\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof SlippyTile)\n        {\n            final SlippyTile that = (SlippyTile) other;\n            return this.getZoom() == that.getZoom() && this.getX() == that.getX()\n                    && this.getY() == that.getY();\n        }\n        return false;\n    }\n\n    @Override\n    public GeoJsonType getGeoJsonType()\n    {\n        return GeoJsonType.POLYGON;\n    }\n\n    @Override\n    public String getName()\n    {\n        return CONVERTER.convert(this);\n    }\n\n    /**\n     * @return The tile's X index.\n     */\n    public int getX()\n    {\n        return this.xAxis;\n    }\n\n    /**\n     * @return The tile's Y index.\n     */\n    public int getY()\n    {\n        return this.yAxis;\n    }\n\n    /**\n     * @return The tile's zoom.\n     */\n    public int getZoom()\n    {\n        return this.zoom;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final long result = (long) Math.pow(getMaxXorY(this.zoom - 1), 2)\n                + (long) Math.pow(this.xAxis, 2) + this.yAxis;\n        return (int) result % Integer.MAX_VALUE;\n    }\n\n    /**\n     * @return All neighbors for the current tile, including diagonal tiles that may share a single\n     *         vertex on the corner they meet.\n     */\n    public Set<SlippyTile> neighbors()\n    {\n        return Iterables\n                .stream(new SlippyTileSharding(this.zoom)\n                        .shards(bounds().expand(calculateExpansionDistance(bounds()))))\n                .map(shard -> (SlippyTile) shard).filter(tile -> !this.equals(tile)).collectToSet();\n    }\n\n    /**\n     * @return The {@link SlippyTile} that has one less zoom level, and that covers that\n     *         {@link SlippyTile}. If the zoom is already 0, return itself.\n     */\n    public SlippyTile parent()\n    {\n        if (this.zoom == 0)\n        {\n            return this;\n        }\n        final int parentZoom = this.zoom - 1;\n        final int parentX = this.xAxis / 2;\n        final int parentY = this.yAxis / 2;\n        return new SlippyTile(parentX, parentY, parentZoom);\n    }\n\n    /**\n     * @return The 4 {@link SlippyTile} that represent the current {@link SlippyTile} at one more\n     *         zoom level.\n     */\n    public List<SlippyTile> split()\n    {\n        if (this.zoom == MAX_ZOOM)\n        {\n            throw new CoreException(\"Cannot split further than zoom {}\", MAX_ZOOM);\n        }\n        final List<SlippyTile> result = new ArrayList<>();\n        for (int i = 2 * this.xAxis; i <= 2 * this.xAxis + 1; i++)\n        {\n            for (int j = 2 * this.yAxis; j <= 2 * this.yAxis + 1; j++)\n            {\n                result.add(new SlippyTile(i, j, this.zoom + 1));\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Split a {@link SlippyTile} up to the given zoom level. If the zoom is smaller than the\n     * current tile's, then the method will return the only parent tile at the specified zoom in the\n     * result list.\n     *\n     * @param newZoom\n     *            The zoom to split to\n     * @return the split tiles\n     */\n    public List<SlippyTile> split(final int newZoom)\n    {\n        if (newZoom < 0 || newZoom > MAX_ZOOM)\n        {\n            throw new CoreException(\n                    \"Cannot split to a zoom {} which is not between 0 and {} included.\", newZoom,\n                    MAX_ZOOM);\n        }\n        List<SlippyTile> result = new ArrayList<>();\n        if (newZoom == this.zoom)\n        {\n            result.add(this);\n        }\n        if (newZoom > this.zoom)\n        {\n            List<SlippyTile> temporary = new ArrayList<>();\n            temporary.add(this);\n            int temporaryZoom = this.zoom + 1;\n            while (temporaryZoom <= newZoom)\n            {\n                final List<SlippyTile> newTemporary = new ArrayList<>();\n                for (final SlippyTile temporaryTile : temporary)\n                {\n                    newTemporary.addAll(temporaryTile.split());\n                }\n                temporary = newTemporary;\n                temporaryZoom++;\n            }\n            result = temporary;\n        }\n        if (newZoom < this.zoom)\n        {\n            SlippyTile temporary = this;\n            int temporaryZoom = temporary.getZoom() - 1;\n            while (temporaryZoom >= newZoom)\n            {\n                temporary = temporary.parent();\n                temporaryZoom--;\n            }\n            result.add(temporary);\n        }\n        return result;\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[SlippyTile: zoom = \" + this.zoom + \", x = \" + this.xAxis + \", y = \" + this.yAxis\n                + \"]\";\n    }\n\n    @Override\n    public byte[] toWkb()\n    {\n        return bounds().toWkb();\n    }\n\n    @Override\n    public String toWkt()\n    {\n        return bounds().toWkt();\n    }\n\n    /**\n     * Add the siblings and parent.\n     *\n     * @param candidates\n     *            The candidate tiles\n     * @param visitedTiles\n     *            The tiles already visited\n     * @param targetTile\n     *            The target tile\n     */\n    protected void getNeighborsForAllZoomLevels(final Queue<SlippyTile> candidates,\n            final Set<String> visitedTiles, final SlippyTile targetTile)\n    {\n        final SlippyTile parent = targetTile.parent();\n        for (final SlippyTile child : parent.split())\n        {\n            if (!visitedTiles.contains(child.getName()) && !child.equals(targetTile))\n            {\n                candidates.add(child);\n            }\n        }\n        candidates.add(parent);\n    }\n\n    /**\n     * @param zoom\n     *            The provided zoom level\n     * @return The maximum slippy tile index (x or y) for that zoom level\n     */\n    private int getMaxXorY(final int zoom)\n    {\n        if (zoom <= 0)\n        {\n            return 1;\n        }\n        final int result = getTileNumbers(new Location(Latitude.MINIMUM, Longitude.ZERO), zoom)\n                .get(0);\n        return result;\n    }\n\n    /**\n     * @param location\n     *            The location to consider\n     * @param zoom\n     *            The zoom level\n     * @return The list made of the corresponding x and y indices.\n     */\n    private List<Integer> getTileNumbers(final Location location, final int zoom)\n    {\n        final double latitude = location.getLatitude().asDegrees();\n        final double longitude = location.getLongitude().asDegrees();\n        int xAxis = (int) Math\n                .floor((longitude + HALF_ROTATION_DEGREES) / FULL_ROTATION_DEGREES * (1 << zoom));\n        int yAxis = (int) Math.floor((1 - Math\n                .log(Math.tan(Math.toRadians(latitude)) + 1 / Math.cos(Math.toRadians(latitude)))\n                / Math.PI) / BIT_SHIFT * (1 << zoom));\n        if (xAxis < 0)\n        {\n            xAxis = 0;\n        }\n        if (xAxis >= 1 << zoom)\n        {\n            xAxis = (1 << zoom) - 1;\n        }\n        if (yAxis < 0)\n        {\n            yAxis = 0;\n        }\n        if (yAxis >= 1 << zoom)\n        {\n            yAxis = (1 << zoom) - 1;\n        }\n        final List<Integer> result = new ArrayList<>();\n        result.add(xAxis);\n        result.add(yAxis);\n        return result;\n    }\n\n    /**\n     * @param xAxis\n     *            The x index\n     * @param yAxis\n     *            The y index\n     * @param zoom\n     *            The zoom level\n     * @return The corresponding bounding box {@link Rectangle}\n     */\n    private Rectangle tile2boundingBox(final int xAxis, final int yAxis, final int zoom)\n    {\n        final Latitude minLat = tile2lat(yAxis + 1, zoom);\n        final Latitude maxLat = tile2lat(yAxis, zoom);\n        final Longitude minLon = tile2lon(xAxis, zoom);\n        final Longitude maxLon = tile2lon(xAxis + 1, zoom);\n        return Rectangle.forCorners(new Location(minLat, minLon), new Location(maxLat, maxLon));\n    }\n\n    /**\n     * @param yAxis\n     *            The tile's y index\n     * @param zoom\n     *            The zoom level\n     * @return The corresponding latitude\n     */\n    private Latitude tile2lat(final int yAxis, final int zoom)\n    {\n        final double pivot = Math.PI\n                - CIRCULAR_MULTIPLIER * Math.PI * yAxis / Math.pow(ZOOM_LEVEL_POWER, zoom);\n        return Latitude.degrees(Math.toDegrees(Math.atan(Math.sinh(pivot))));\n    }\n\n    /**\n     * @param xAxis\n     *            The tile's x index\n     * @param zoom\n     *            The zoom level\n     * @return The corresponding longitude\n     */\n    private Longitude tile2lon(final int xAxis, final int zoom)\n    {\n        final double longitude = xAxis / Math.pow(ZOOM_LEVEL_POWER, zoom) * FULL_ROTATION_DEGREES\n                - HALF_ROTATION_DEGREES;\n        return longitude >= LONGITUDE_BOUNDARY ? Longitude.MAXIMUM\n                : longitude < -LONGITUDE_BOUNDARY ? Longitude.MINIMUM\n                        : Longitude.degrees(longitude);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/SlippyTileSharding.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Atlas Sharding with {@link SlippyTile}s.\n *\n * @author matthieun\n */\npublic class SlippyTileSharding implements Sharding\n{\n    private static final long serialVersionUID = -6727830583309410676L;\n\n    private final int zoom;\n\n    public SlippyTileSharding(final int zoom)\n    {\n        this.zoom = zoom;\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"slippy@\" + this.zoom;\n    }\n\n    @Override\n    public Iterable<Shard> neighbors(final Shard shard)\n    {\n        if (shard instanceof SlippyTile)\n        {\n            return ((SlippyTile) shard).neighbors().stream().map(neighbor -> (Shard) neighbor)\n                    .collect(Collectors.toSet());\n        }\n        else\n        {\n            throw new CoreException(\"Cannot have neighbors from another type of shard.\");\n        }\n    }\n\n    @Override\n    public Shard shardForName(final String name)\n    {\n        final SlippyTile result = SlippyTile.forName(name);\n        if (result.getZoom() != this.zoom)\n        {\n            throw new CoreException(\"This sharding is of zoom {}, and \\\"{}\\\" is not.\", this.zoom,\n                    name);\n        }\n        return result;\n    }\n\n    @Override\n    public Iterable<Shard> shards(final GeometricSurface surface)\n    {\n        return Iterables.stream(SlippyTile.allTiles(this.zoom, surface.bounds()))\n                .filter(slippyTile -> surface.overlaps(slippyTile.bounds()))\n                .map(shard -> (Shard) shard);\n    }\n\n    @Override\n    public Iterable<Shard> shardsCovering(final Location location)\n    {\n        return Iterables.stream(SlippyTile.allTiles(this.zoom, location.bounds()))\n                .filter(slippyTile -> slippyTile.bounds().fullyGeometricallyEncloses(location))\n                .map(shard -> (Shard) shard);\n    }\n\n    @Override\n    public Iterable<Shard> shardsIntersecting(final PolyLine polyLine)\n    {\n        return Iterables.stream(SlippyTile.allTiles(this.zoom, polyLine.bounds()))\n                .filter(slippyTile -> polyLine.intersects(slippyTile.bounds())\n                        || slippyTile.bounds().fullyGeometricallyEncloses(polyLine))\n                .map(shard -> (Shard) shard);\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"slippy:\" + this.zoom;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/converters/DynamicTileShardingGeoJsonConverter.java",
    "content": "package org.openstreetmap.atlas.geography.sharding.converters;\n\nimport org.openstreetmap.atlas.geography.sharding.DynamicTileSharding;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Save an existing serialized tree into a Geojson file.\n *\n * @author matthieun\n */\npublic class DynamicTileShardingGeoJsonConverter extends Command\n{\n    public static final Switch<WritableResource> GEOJSON = new Switch<>(\"geojson\",\n            \"The resource where to save the geojson tree for debugging\", File::new,\n            Optionality.REQUIRED);\n    public static final Switch<Resource> INPUT = new Switch<>(\"input\",\n            \"The resource where to read the serialized tree\", File::new, Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new DynamicTileShardingGeoJsonConverter().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Resource input = (Resource) command.get(INPUT);\n        final WritableResource geojson = (WritableResource) command.get(GEOJSON);\n        new DynamicTileSharding(input).saveAsGeoJson(geojson);\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(GEOJSON, INPUT);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/converters/RectangleToSpatial4JRectangleConverter.java",
    "content": "package org.openstreetmap.atlas.geography.sharding.converters;\n\nimport org.locationtech.spatial4j.context.SpatialContext;\nimport org.locationtech.spatial4j.shape.impl.RectangleImpl;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * @author matthieun\n */\npublic class RectangleToSpatial4JRectangleConverter\n        implements TwoWayConverter<org.locationtech.spatial4j.shape.Rectangle, Rectangle>\n{\n    @Override\n    public org.locationtech.spatial4j.shape.Rectangle backwardConvert(final Rectangle other)\n    {\n        final Location lowerLeft = other.lowerLeft();\n        final Location upperRight = other.upperRight();\n        return new RectangleImpl(lowerLeft.getLongitude().asDegrees(),\n                upperRight.getLongitude().asDegrees(), lowerLeft.getLatitude().asDegrees(),\n                upperRight.getLatitude().asDegrees(), SpatialContext.GEO);\n    }\n\n    @Override\n    public Rectangle convert(final org.locationtech.spatial4j.shape.Rectangle other)\n    {\n        return Rectangle.forCorners(\n                new Location(Latitude.degrees(other.getMinY()), Longitude.degrees(other.getMinX())),\n                new Location(Latitude.degrees(other.getMaxY()),\n                        Longitude.degrees(other.getMaxX())));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/converters/SlippyTileConverter.java",
    "content": "package org.openstreetmap.atlas.geography.sharding.converters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Convert a SlippyTile to its string value\n *\n * @author tony\n */\npublic class SlippyTileConverter implements TwoWayConverter<SlippyTile, String>\n{\n    private static final int TILE_DIMENSIONS = 3;\n\n    @Override\n    public SlippyTile backwardConvert(final String slippyTileParameters)\n    {\n        final StringList splits = StringList.split(slippyTileParameters,\n                SlippyTile.COORDINATE_SEPARATOR);\n        if (splits.size() != TILE_DIMENSIONS)\n        {\n            throw new CoreException(\"Wrong format of input string {}\", slippyTileParameters);\n        }\n        final int zoom = Integer.parseInt(splits.get(0));\n        final int xAxis = Integer.parseInt(splits.get(1));\n        final int yAxis = Integer.parseInt(splits.get(2));\n        return new SlippyTile(xAxis, yAxis, zoom);\n    }\n\n    @Override\n    public String convert(final SlippyTile slippyTile)\n    {\n        return slippyTile.getZoom() + SlippyTile.COORDINATE_SEPARATOR + slippyTile.getX()\n                + SlippyTile.COORDINATE_SEPARATOR + slippyTile.getY();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/converters/StringToShardConverter.java",
    "content": "package org.openstreetmap.atlas.geography.sharding.converters;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.sharding.CountryShard;\nimport org.openstreetmap.atlas.geography.sharding.GeoHashTile;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * Convert a string representation of a {@link Shard} to a concrete {@link Shard} object. Any time a\n * new {@link Shard} implementation is added, this class needs to be updated. See the Javadoc for\n * the {@link StringToShardConverter#convert(String)} method for more information.\n *\n * @author lcram\n */\npublic class StringToShardConverter implements Converter<String, Shard>\n{\n    private static final String COUNTRY_CODE_REGEX = \"^[A-Z][A-Z0-9][A-Z0-9]$\";\n    private static final String SLIPPY_TILE_REGEX = \"^[0-9]+\\\\-[0-9]+\\\\-[0-9]+$\";\n    private static final String GEOHASH_TILE_REGEX = \"^(?:(?![ailo])[0-9a-z])+$\";\n\n    /**\n     * Convert a string into a concrete {@link Shard} object. This method attempts to handle all\n     * possible shard strings that exist in the wild. Note that this method will correctly handle\n     * country shards in the form \"ABC_1-2-3\", where \"_\" is the declared country-shard separator. To\n     * extract metadata from the shard string (e.g. ABC_1-2-3_xx/yy/zz), see\n     * {@link StringToShardConverter#convertWithMetadata}.\n     * \n     * @param shardString\n     *            the shard in string format\n     * @return the constructed {@link Shard}\n     */\n    @Override\n    public Shard convert(final String shardString)\n    {\n        return convertWithMetadata(shardString).getFirst();\n    }\n\n    /**\n     * Convert the shard string, and also get any metadata appended to the end of the string.\n     * \n     * @param shardString\n     *            the shard in string format\n     * @return a {@link Tuple} containing the constructed {@link Shard} as well as the metadata.\n     */\n    public Tuple<Shard, Optional<String>> convertWithMetadata(final String shardString)\n    {\n        final StringList shardSplit = StringList.split(shardString, Shard.SHARD_DATA_SEPARATOR, 2);\n\n        try\n        {\n            return convertHelper(shardSplit);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Could not parse shard string: {}\", shardString, exception);\n        }\n    }\n\n    private Tuple<Shard, Optional<String>> convertHelper(final StringList shardSplit)\n    {\n        /*\n         * In this case, the shardSplit should contain just the shard string. For e.g. \"1-2-3\" for\n         * SlippyTile or \"bb2r5\" for GeoHashTile.\n         */\n        if (shardSplit.size() == 1)\n        {\n            return shardFromSplitExcludingCountryShard(shardSplit);\n        }\n        /*\n         * In this case, the shardSplit should contain a country code, a shard string, and\n         * optionally some additional metadata introduced by downstream client code. For e.g. we may\n         * have \"ABC_1-2-2\" or \"DEF_bb2r5\" or \"GHI_1-2-3_additionalmetadata\". For the purposes of\n         * shard parsing, we ignore the additional metadata. Note that we use a recursive call to\n         * recover the subshard, in the case of a nested CountryShard (e.g.\n         * ABC_DEF_1-2-3_somemetadata). It is also possible the the shardSplit is just a shard\n         * string and some metadata (e.g. 1-2-3_somemetadata). In that case, we ignore country code\n         * parsing.\n         */\n        else if (shardSplit.size() > 1)\n        {\n            if (shardSplit.get(0).matches(COUNTRY_CODE_REGEX))\n            {\n                final StringList newListWithoutLeadingCountryCode = StringList\n                        .split(shardSplit.get(1), Shard.SHARD_DATA_SEPARATOR, 2);\n                final Tuple<Shard, Optional<String>> conversionResult = convertHelper(\n                        newListWithoutLeadingCountryCode);\n                return new Tuple<>(new CountryShard(shardSplit.get(0), conversionResult.getFirst()),\n                        conversionResult.getSecond());\n            }\n            return shardFromSplitExcludingCountryShard(shardSplit);\n        }\n        /*\n         * Otherwise we have to fail.\n         */\n        else\n        {\n            throw new CoreException(\"Split list {} had invalid size {}, must be at least 1\",\n                    shardSplit, shardSplit.size());\n        }\n    }\n\n    /**\n     * A helper to convert a {@link StringList} into a {@link Shard} object, but excluding the\n     * {@link CountryShard} implementation. This method will ignore any trailing metadata.\n     * \n     * @param shardSplit\n     *            the {@link StringList} containing the shard info\n     * @return the constructed {@link Shard}\n     */\n    private Tuple<Shard, Optional<String>> shardFromSplitExcludingCountryShard(\n            final StringList shardSplit)\n    {\n        if (shardSplit.size() < 1)\n        {\n            throw new CoreException(\"Split list {} had invalid size {}, must be at least 1\",\n                    shardSplit, shardSplit.size());\n        }\n\n        final Shard shard;\n        final String shardString = shardSplit.get(0);\n        if (shardString.matches(SLIPPY_TILE_REGEX))\n        {\n            shard = SlippyTile.forName(shardString);\n        }\n        else if (shardString.matches(GEOHASH_TILE_REGEX))\n        {\n            shard = GeoHashTile.forName(shardString);\n        }\n        else\n        {\n            throw new CoreException(\"Unrecognized shard component: {}\", shardString);\n        }\n\n        if (shardSplit.size() > 1)\n        {\n            return new Tuple<>(shard, Optional.of(shardSplit.get(1)));\n        }\n        return new Tuple<>(shard, Optional.empty());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/preparation/TilePrinter.java",
    "content": "package org.openstreetmap.atlas.geography.sharding.preparation;\n\nimport java.util.Iterator;\n\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.writers.SafeBufferedWriter;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Print all the rectangles of all {@link SlippyTile}s at a specified zoom level in a {@link File}.\n * This class creates a SQL file that can be executed towards an OSM database, and will return the\n * way counts for each slippy tile.\n *\n * @author matthieun\n */\npublic class TilePrinter extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(TilePrinter.class);\n    private static final int MAX_SHARDS_PER_FILE = 4_000_000;\n\n    private static final Switch<File> OUTPUT_FOLDER = new Switch<>(\"output\", \"The output folder\",\n            value -> new File(value));\n    private static final Switch<Integer> ZOOM_SWITCH = new Switch<>(\"zoom\", \"The zoom\",\n            value -> Integer.valueOf(value));\n    private static final Switch<String> USER = new Switch<>(\"user\", \"The user for the db\",\n            value -> new String(value));\n\n    private int index;\n    private File folder;\n    private int zoom;\n\n    public static void main(final String[] args)\n    {\n        new TilePrinter().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        this.index = 0;\n        this.zoom = (int) command.get(ZOOM_SWITCH);\n        this.folder = (File) command.get(OUTPUT_FOLDER);\n        final String user = (String) command.get(USER);\n        this.folder.mkdirs();\n        SafeBufferedWriter writer = getNextFile().writer();\n        writer.writeLine(\"CREATE SCHEMA sharding AUTHORIZATION \" + user + \";\");\n        writer.writeLine(\"DROP TABLE sharding.tiles;\");\n        writer.writeLine(\n                \"CREATE TABLE sharding.tiles(tile text, bounds geometry) WITH ( OIDS=FALSE );\");\n        writer.writeLine(\"ALTER TABLE sharding.tiles OWNER TO \" + user + \";\");\n        writer.writeLine(\"DROP TABLE sharding.counts;\");\n        writer.writeLine(\n                \"CREATE TABLE sharding.counts(tile text, count integer) WITH ( OIDS=FALSE );\");\n        writer.writeLine(\"ALTER TABLE sharding.counts OWNER TO \" + user + \";\");\n\n        final Iterator<SlippyTile> tileIterator = SlippyTile.allTilesIterator(this.zoom,\n                Rectangle.MAXIMUM);\n        while (tileIterator.hasNext())\n        {\n            Streams.close(writer);\n            writer = getNextFile().writer();\n            writer.writeLine(\"INSERT INTO sharding.tiles(tile, bounds) VALUES \");\n            SlippyTile tile = null;\n            int counter = 0;\n            while (tileIterator.hasNext() && counter < MAX_SHARDS_PER_FILE)\n            {\n                tile = tileIterator.next();\n                final Rectangle bounds = tile.bounds();\n                writer.write(\"(\\'\" + tile.getName() + \"\\', ST_MakeEnvelope(\"\n                        + bounds.lowerLeft().getLongitude().asDegrees() + \",\"\n                        + bounds.lowerLeft().getLatitude().asDegrees() + \",\"\n                        + bounds.upperRight().getLongitude().asDegrees() + \",\"\n                        + bounds.upperRight().getLatitude().asDegrees() + \",4326))\");\n                counter++;\n                if (tileIterator.hasNext() && counter < MAX_SHARDS_PER_FILE)\n                {\n                    writer.write(\",\");\n                }\n                else\n                {\n                    writer.write(\";\");\n                }\n                writer.write(\"\\n\");\n            }\n        }\n        Streams.close(writer);\n        writer = getNextFile().writer();\n        writer.writeLine(\"CREATE OR REPLACE FUNCTION countForTiles() RETURNS void AS $$\");\n        writer.writeLine(\"DECLARE\");\n        writer.writeLine(\"s_tile text;\");\n        writer.writeLine(\"s_bounds geometry;\");\n        writer.writeLine(\"s_count integer;\");\n        writer.writeLine(\"n_count integer;\");\n        writer.writeLine(\"BEGIN\");\n        writer.writeLine(\"    FOR s_tile, s_bounds IN SELECT tile, bounds FROM sharding.tiles\");\n        writer.writeLine(\"    LOOP\");\n        writer.writeLine(\n                \"        SELECT count(*) INTO s_count FROM public.ways WHERE ST_Intersects(s_bounds, linestring);\");\n        writer.writeLine(\n                \"        SELECT count(*) INTO n_count FROM public.nodes WHERE ST_Intersects(s_bounds, geom);\");\n        writer.writeLine(\n                \"        INSERT INTO sharding.counts(tile,count) VALUES (s_tile, s_count + n_count);\");\n        writer.writeLine(\"    END LOOP;\");\n        writer.writeLine(\"    RETURN;\");\n        writer.writeLine(\"END\");\n        writer.writeLine(\"$$ LANGUAGE 'plpgsql' ;\");\n        Streams.close(writer);\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(ZOOM_SWITCH, OUTPUT_FOLDER, USER);\n    }\n\n    private File getNextFile()\n    {\n        final File result = this.folder.child(\"tiles-\" + this.zoom + \"_\" + this.index++ + \".sql\");\n        logger.info(\"Generating file {}\", result);\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/preparation/tileDownload.sh",
    "content": "#!/bin/bash\n\n# Once the tiles have been counted in the database, use this to download them.\n\n: ${1:?\"Make sure to pass the database user name to the script\"}\n\npsql -U $1 -d osm_planet -c \"\\copy sharding.counts TO ./counts.csv WITH DELIMITER ',' CSV\"\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/preparation/tileExecute.sh",
    "content": "#!/bin/bash\n\n# Run the function that will count the tiles members in the database.\n\n: ${1:?\"Make sure to pass the database user name to the script\"}\n\npsql -U $1 -d osm_planet -c \"SELECT * FROM countForTiles()\"\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/geography/sharding/preparation/tilePrinter.sh",
    "content": "#!/bin/bash\n\n# Once the tiles sql files are generated, run this script to have the database do the counting.\n\n: ${1:?\"Make sure to pass the database user name to the script\"}\n\nfor sqlscript in ./*.sql\ndo\n\techo \"processing $sqlscript\"\n\tpsql -U $1 -d osm_planet -f $sqlscript\ndone\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/locale/IsoCountry.java",
    "content": "package org.openstreetmap.atlas.locale;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Java Locale based countries, including ISO2, ISO3, and descriptive country name representations.\n *\n * @author robert_stack\n * @author lcram\n */\npublic final class IsoCountry implements Serializable\n{\n    // Package private fields used by other classes in the locale package\n    static final Set<String> ALL_COUNTRY_CODES;\n    static final Set<String> ALL_DISPLAY_COUNTRIES;\n    static final Map<String, String> ISO2_TO_DISPLAY_COUNTRY;\n    static final Map<String, String> DISPLAY_COUNTRY_TO_ISO2;\n    static final Map<String, String> ISO2_TO_ISO3;\n    static final Map<String, String> ISO3_TO_ISO2;\n    // private static final BiMap<String, String> ISO2_ISO3_MAP;\n    static final Map<String, IsoCountry> ISO_COUNTRIES;\n    private static final long serialVersionUID = 8686298246454085812L;\n    private static final Logger logger = LoggerFactory.getLogger(IsoCountry.class);\n    // Use United States fixed Locale for display use cases\n    private static final String LOCALE_LANGUAGE = Locale.ENGLISH.getLanguage();\n    private static final int ISO2_LENGTH = 2;\n    private static final int ISO3_LENGTH = 3;\n\n    static\n    {\n        // All Locale based 2 letter country codes\n        final String[] countries = Locale.getISOCountries();\n\n        // Set of language codes--exposed publically through allLanguageCodes()\n        ALL_COUNTRY_CODES = Collections.unmodifiableSet(\n                Arrays.stream(countries).map(String::intern).collect(Collectors.toSet()));\n\n        // Map from ISO2 to full country name\n        ISO2_TO_DISPLAY_COUNTRY = Collections.unmodifiableMap(\n                Arrays.stream(countries).collect(Collectors.toMap(iso2 -> iso2.intern(),\n                        iso2 -> new Locale(LOCALE_LANGUAGE, iso2).getDisplayCountry().intern())));\n\n        /*\n         * Check that country names are actually unique, and log an error if not. NOTE that this\n         * relies on English country names. If you are updating this code to handle\n         * internationalization, these assumptions may not hold.\n         */\n        final Map<String, String> countriesSeen = new HashMap<>();\n        for (final String iso2Country : countries)\n        {\n            final String countryName = new Locale(LOCALE_LANGUAGE, iso2Country).getDisplayCountry()\n                    .intern();\n            if (countriesSeen.containsKey(countryName))\n            {\n                logger.error(\"Detected duplicate country name {} -> {} AND {}\", countryName,\n                        iso2Country, countriesSeen.get(countryName));\n            }\n            countriesSeen.put(countryName, iso2Country);\n        }\n\n        // Map from full country name to ISO2\n        DISPLAY_COUNTRY_TO_ISO2 = Collections.unmodifiableMap(Arrays.stream(countries)\n                .collect(Collectors.toMap(\n                        iso2 -> new Locale(LOCALE_LANGUAGE, iso2).getDisplayCountry().intern(),\n                        iso2 -> iso2.intern())));\n\n        // Map from ISO2 to ISO3\n        ISO2_TO_ISO3 = Collections.unmodifiableMap(\n                Arrays.stream(countries).collect(Collectors.toMap(iso2 -> iso2.intern(),\n                        iso2 -> new Locale(LOCALE_LANGUAGE, iso2).getISO3Country().intern())));\n\n        // Map from ISO3 to ISO2\n        // Have verified a 1:1 between ISO2 to ISO3, otherwise there would be overwrites of ISO3\n        // keys\n        ISO3_TO_ISO2 = Collections\n                .unmodifiableMap(ISO2_TO_ISO3.entrySet().stream().collect(Collectors\n                        .toMap(iso3 -> iso3.getValue().intern(), iso3 -> iso3.getKey().intern())));\n\n        // TODO Use Guava BiMap\n        // ISO2_ISO3_MAP = Collections.unmodifiableMap(Arrays.stream(countries).collect(\n        // Collectors.toMap(x -> x.get(0), x -> x.get(1), (a, b) -> b, HashBiMap::create)));\n\n        // Map from ISO2 to IsoCountry\n        ISO_COUNTRIES = Collections.unmodifiableMap(Arrays.stream(countries)\n                .collect(Collectors.toMap(iso2 -> iso2.intern(), iso2 -> new IsoCountry(iso2))));\n\n        // Set of display country names, do this one last since it relies on the other maps\n        ALL_DISPLAY_COUNTRIES = Collections.unmodifiableSet(ALL_COUNTRY_CODES.stream()\n                .map(IsoCountry::displayCountry).map(Optional::get).collect(Collectors.toSet()));\n    }\n\n    // This validated country code\n    private final String iso2CountryCode;\n    private final String iso3CountryCode;\n    private final String displayCountry;\n\n    /**\n     * Provides a set of all Locale based country codes; supports convenience methods that use all\n     * country codes\n     *\n     * @return Set of country codes\n     */\n    public static Set<String> allCountryCodes()\n    {\n        return ALL_COUNTRY_CODES;\n    }\n\n    /**\n     * Provides a set of all Locale based country long names.\n     *\n     * @return Set of country long names\n     */\n    public static Set<String> allDisplayCountries()\n    {\n        return ALL_DISPLAY_COUNTRIES;\n    }\n\n    /**\n     * Provides long name country for country code\n     *\n     * @param countryCode\n     *            2 or 3 character country code, case sensitive (examples \"US\", \"USA\")\n     * @return Optional of display country string (example \"United States\")\n     */\n    public static Optional<String> displayCountry(final String countryCode)\n    {\n        if (countryCode != null)\n        {\n            if (countryCode.length() == ISO2_LENGTH && ISO2_TO_ISO3.keySet().contains(countryCode))\n            {\n                final String displayCountry = ISO2_TO_DISPLAY_COUNTRY.get(countryCode);\n                return Optional.ofNullable(displayCountry);\n            }\n            if (countryCode.length() == ISO3_LENGTH && ISO3_TO_ISO2.keySet().contains(countryCode))\n            {\n                final String iso2 = ISO3_TO_ISO2.get(countryCode);\n                if (iso2 != null)\n                {\n                    final String displayCountry = ISO2_TO_DISPLAY_COUNTRY.get(iso2);\n                    return Optional.ofNullable(displayCountry);\n                }\n            }\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Provides IsoCountry for valid country code\n     *\n     * @param countryCode\n     *            2 or 3 character country code, case sensitive (examples \"US\", \"USA\")\n     * @return Optional of valid country code, or Optional.empty if not recognized\n     */\n    public static Optional<IsoCountry> forCountryCode(final String countryCode)\n    {\n        if (countryCode != null)\n        {\n            if (countryCode.length() == ISO2_LENGTH && ISO2_TO_ISO3.keySet().contains(countryCode))\n            {\n                return Optional.ofNullable(ISO_COUNTRIES.get(countryCode));\n            }\n            else if (countryCode.length() == ISO3_LENGTH\n                    && ISO3_TO_ISO2.keySet().contains(countryCode))\n            {\n                return Optional.ofNullable(ISO_COUNTRIES.get(ISO3_TO_ISO2.get(countryCode)));\n            }\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Provides IsoCountry for a valid country display name. Ignores capitalization (e.g. \"united\n     * stAtes\" and \"United States\" are the same)\n     *\n     * @param displayCountry\n     *            the display country name, e.g. \"United States\"\n     * @return an Optional containing the IsoCountry if present\n     */\n    public static Optional<IsoCountry> forDisplayCountry(final String displayCountry)\n    {\n        if (displayCountry != null)\n        {\n            /*\n             * We want to allow the displayCountry parameter to have inconsistent case. E.g.\n             * displayCountry=\"united States\" should match IsoCountry<\"United States\">\n             */\n            String foundKey = null;\n            for (final String key : DISPLAY_COUNTRY_TO_ISO2.keySet())\n            {\n                if (displayCountry.equalsIgnoreCase(key))\n                {\n                    foundKey = key;\n                    break;\n                }\n            }\n            return Optional.ofNullable(foundKey).map(DISPLAY_COUNTRY_TO_ISO2::get)\n                    .map(ISO_COUNTRIES::get);\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Indicates whether the ISO2 or ISO3 country code is valid\n     *\n     * @param isoCountry\n     *            2 or 3 character country code, case sensitive (examples \"US\", \"USA\")\n     * @return Whether this is a valid ISO2 or ISO3 country code\n     */\n    public static boolean isValidCountryCode(final String isoCountry)\n    {\n        if (isoCountry != null)\n        {\n            if (isoCountry.length() == ISO2_LENGTH && ISO2_TO_ISO3.keySet().contains(isoCountry))\n            {\n                return true;\n            }\n            else if (isoCountry.length() == ISO3_LENGTH\n                    && ISO3_TO_ISO2.keySet().contains(isoCountry))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Provides ISO2 string for ISO3\n     *\n     * @param iso3\n     *            3 character country code, case sensitive (example \"USA\")\n     * @return Optional of ISO2 country code, or Optional.empty if not recognized\n     */\n    public static Optional<String> iso2ForIso3(final String iso3)\n    {\n        String iso2 = null;\n        if (iso3 != null)\n        {\n            iso2 = ISO3_TO_ISO2.get(iso3);\n        }\n        return Optional.ofNullable(iso2);\n    }\n\n    /**\n     * Provides ISO3 string for ISO2\n     *\n     * @param iso2\n     *            2 character country code, case sensitive (example \"US\")\n     * @return Optional of ISO3 country code, or Optional.empty if not recognized\n     */\n    public static Optional<String> iso3ForIso2(final String iso2)\n    {\n        String iso3 = null;\n        if (iso2 != null)\n        {\n            iso3 = ISO2_TO_ISO3.get(iso2);\n        }\n        return Optional.ofNullable(iso3);\n    }\n\n    private IsoCountry(final String iso2)\n    {\n        this.iso2CountryCode = iso2;\n        this.iso3CountryCode = ISO2_TO_ISO3.get(this.iso2CountryCode);\n        this.displayCountry = ISO2_TO_DISPLAY_COUNTRY.get(this.iso2CountryCode);\n    }\n\n    /**\n     * Provides the ISO2 country code for this IsoCountry\n     *\n     * @return 2 character (ISO2) language code\n     */\n    public String getCountryCode()\n    {\n        return this.iso2CountryCode;\n    }\n\n    /**\n     * Provides the display name for this IsoCountry\n     *\n     * @return Display country string\n     */\n    public String getDisplayCountry()\n    {\n        return this.displayCountry;\n    }\n\n    /**\n     * Provides the ISO3 country code for this IsoCountry\n     *\n     * @return 3 character (ISO3) language code\n     */\n    public String getIso3CountryCode()\n    {\n        return this.iso3CountryCode;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getDisplayCountry();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/locale/IsoCountryFuzzyMatcher.java",
    "content": "package org.openstreetmap.atlas.locale;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.text.similarity.LevenshteinDistance;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Extends {@link IsoCountry} functionality with fuzzy matching for display names. Note that this\n * functionality is very simple, and assumes dependence on English country name representations.\n *\n * @author lcram\n */\npublic final class IsoCountryFuzzyMatcher\n{\n    private static final Logger logger = LoggerFactory.getLogger(IsoCountryFuzzyMatcher.class);\n\n    /**\n     * Provides closest IsoCountries for a country display name. If the given display name does not\n     * perfectly match a valid IsoCountry, this will return the closest string match up to number of\n     * matches.\n     *\n     * @param number\n     *            the number of matches to show\n     * @param displayCountry\n     *            the display country name, e.g. \"united stats\" (which does not match any display\n     *            country)\n     * @return an Optional containing the IsoCountry if present\n     */\n    public static List<IsoCountry> forDisplayCountryTopMatches(final int number,\n            final String displayCountry)\n    {\n        if (displayCountry != null)\n        {\n            final List<IsoCountry> results = new ArrayList<>();\n            if (IsoCountry.DISPLAY_COUNTRY_TO_ISO2.containsKey(displayCountry))\n            {\n\n                results.add(IsoCountry.ISO_COUNTRIES\n                        .get(IsoCountry.DISPLAY_COUNTRY_TO_ISO2.get(displayCountry)));\n            }\n            else\n            {\n                final List<String> closestCountries = closestIsoCountries(number, displayCountry);\n                if (!closestCountries.isEmpty())\n                {\n                    logger.debug(\n                            \"Exact match for {} was not found, returning closest {} matches {}\",\n                            displayCountry, number, closestCountries);\n                    results.addAll(closestCountries.stream()\n                            .map(countryString -> IsoCountry.ISO_COUNTRIES\n                                    .get(IsoCountry.DISPLAY_COUNTRY_TO_ISO2.get(countryString)))\n                            .collect(Collectors.toList()));\n                }\n            }\n            return results;\n        }\n        return new ArrayList<>();\n    }\n\n    /*\n     * This algorithm could definitely be improved for cases with multi-word country matching.\n     */\n    private static List<String> closestIsoCountries(final int number, final String displayCountry)\n    {\n        if (number <= 0 || number > IsoCountry.ALL_DISPLAY_COUNTRIES.size())\n        {\n            throw new CoreException(\"number \" + number + \" out of range (0, \"\n                    + IsoCountry.ALL_DISPLAY_COUNTRIES.size() + \"]\");\n        }\n        final Map<String, Integer> countryRankings = new HashMap<>();\n        for (final String countryName : IsoCountry.ALL_DISPLAY_COUNTRIES)\n        {\n            final String countryNameLowerCase = countryName.toLowerCase();\n            final String[] nameComponents = countryNameLowerCase.split(\"\\\\s+\");\n            if (nameComponents.length > 1)\n            {\n                int bestInterCountryDistance = Integer.MAX_VALUE;\n                for (final String nameComponent : nameComponents)\n                {\n                    final int distance = LevenshteinDistance.getDefaultInstance()\n                            .apply(displayCountry, nameComponent);\n                    if (distance < bestInterCountryDistance)\n                    {\n                        bestInterCountryDistance = distance;\n                    }\n                    countryRankings.put(countryName, bestInterCountryDistance);\n                }\n            }\n            else\n            {\n                final int distance = LevenshteinDistance.getDefaultInstance().apply(displayCountry,\n                        countryNameLowerCase);\n                countryRankings.put(countryName, distance);\n            }\n        }\n        final List<Entry<String, Integer>> entries = new ArrayList<>(countryRankings.entrySet());\n        entries.sort(Entry.comparingByValue());\n\n        return entries.subList(0, number).stream().map(Entry::getKey).collect(Collectors.toList());\n    }\n\n    private IsoCountryFuzzyMatcher()\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/locale/IsoLanguage.java",
    "content": "package org.openstreetmap.atlas.locale;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.utilities.collections.EnhancedCollectors;\n\n/**\n * Languages derived from Locale\n *\n * @author robert_stack\n */\npublic final class IsoLanguage implements Comparable<IsoLanguage>, Serializable\n{\n    // Use United States fixed Locale for display use cases\n    private static final Locale LANGUAGE_LOCALE = Locale.US;\n\n    private static Set<String> ALL_LANGUAGE_CODES;\n    private static Set<String> DISPLAY_LANGUAGES_SET;\n    private static Map<String, IsoLanguage> ISO_LANGUAGES;\n\n    static\n    {\n        // All 2 letter language codes\n        final String[] languages = Locale.getISOLanguages();\n\n        // Set of language codes--exposed publically through allLanguageCodes()\n        ALL_LANGUAGE_CODES = Arrays.stream(languages)\n                .collect(EnhancedCollectors.toUnmodifiableSortedSet());\n\n        // Set of display languages; here only to support performant validation of display language.\n        // Using a specific fixed Locale rather than system dependent Locale\n        DISPLAY_LANGUAGES_SET = Arrays.stream(languages)\n                .map(language -> new Locale(language).getDisplayLanguage(LANGUAGE_LOCALE))\n                .collect(EnhancedCollectors.toUnmodifiableSortedSet());\n\n        // Map from language codes to IsoLanguages\n        ISO_LANGUAGES = Arrays.stream(languages).collect(EnhancedCollectors.toUnmodifiableSortedMap(\n                languageCode -> languageCode, languageCode -> new IsoLanguage(languageCode)));\n    }\n\n    private final String languageCode;\n    private final String displayLanguage;\n\n    /**\n     * Convenience method for getting all of the IsoLanguage objects\n     *\n     * @return a stream of IsoLanguage objects\n     */\n    public static Stream<IsoLanguage> all()\n    {\n        return ISO_LANGUAGES.values().stream();\n    }\n\n    /**\n     * Provides a set of all Locale based language codes; supports convenience methods that use all\n     * language codes\n     *\n     * @return Set of language codes\n     */\n    public static Set<String> allLanguageCodes()\n    {\n        return ALL_LANGUAGE_CODES;\n    }\n\n    /**\n     * Provide the display language per Locale\n     *\n     * @param languageCode\n     *            Locale based language code\n     * @return The display language\n     */\n    public static Optional<String> displayLanguageForLanguageCode(final String languageCode)\n    {\n        String displayLanguage = null;\n        if (languageCode != null)\n        {\n            final IsoLanguage isoLanguage = ISO_LANGUAGES.get(languageCode);\n            if (isoLanguage != null)\n            {\n                displayLanguage = isoLanguage.getDisplayLanguage();\n            }\n        }\n        return Optional.ofNullable(displayLanguage);\n    }\n\n    /**\n     * Provides IsoLanguage for valid language code\n     *\n     * @param languageCode\n     *            2 character language code, case sensitive (examples \"en\", \"es\")\n     * @return Optional of valid language code, or empty if not recognized\n     */\n    public static Optional<IsoLanguage> forLanguageCode(final String languageCode)\n    {\n        return Optional.ofNullable(ISO_LANGUAGES.get(languageCode));\n    }\n\n    /**\n     * Check if display language is valid\n     *\n     * @param displayLanguage\n     *            Display language (example \"United States\")\n     * @return whether this display language is valid\n     */\n    public static boolean isValidDisplayLanguage(final String displayLanguage)\n    {\n        return displayLanguage != null && DISPLAY_LANGUAGES_SET.contains(displayLanguage);\n    }\n\n    /**\n     * Check if language code is valid\n     *\n     * @param languageCode\n     *            2 letter language code\n     * @return whether this language code is valid\n     */\n    public static boolean isValidLanguageCode(final String languageCode)\n    {\n        return languageCode != null && ISO_LANGUAGES.containsKey(languageCode);\n    }\n\n    private IsoLanguage(final String languageCode)\n    {\n        this.languageCode = languageCode;\n        // Using a specific fixed Locale rather than system dependent Locale\n        this.displayLanguage = new Locale(languageCode).getDisplayLanguage(LANGUAGE_LOCALE);\n    }\n\n    @Override\n    public int compareTo(final IsoLanguage other)\n    {\n        if (this == other)\n        {\n            return 0;\n        }\n        return this.languageCode.compareTo(other.getLanguageCode());\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        return other instanceof IsoLanguage && this.compareTo((IsoLanguage) other) == 0;\n    }\n\n    /**\n     * Provides the display language for this IsoLanguage\n     *\n     * @return Display language string\n     */\n    public String getDisplayLanguage()\n    {\n        return this.displayLanguage;\n    }\n\n    /**\n     * Provides the language code for this IsoLanguage\n     *\n     * @return 2 character language code\n     */\n    public String getLanguageCode()\n    {\n        return this.languageCode;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.getLanguageCode().hashCode();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/ProtoSerializable.java",
    "content": "package org.openstreetmap.atlas.proto;\n\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\n\n/**\n * {@link ProtoSerializable} is a contract for types that would like to be serialized in protocol\n * buffer format. A type that implements this interface must be able to provide a valid adapter to\n * its owner.\n *\n * @author lcram\n */\npublic interface ProtoSerializable\n{\n    /**\n     * @return The adapter associated with this {@link ProtoSerializable}\n     */\n    ProtoAdapter getProtoAdapter();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\n\n/**\n * Any protobuf adapter class must conform to this interface.\n *\n * @author lcram\n */\npublic interface ProtoAdapter\n{\n    /**\n     * @param byteArray\n     *            The raw byte representation of the {@link ProtoSerializable} in protocol buffer\n     *            format\n     * @return The object represented by the byte stream.\n     */\n    ProtoSerializable deserialize(byte[] byteArray);\n\n    /**\n     * @param serializable\n     *            The object to serialize\n     * @return The raw byte representation of the {@link ProtoSerializable} in protocol buffer\n     *         format.\n     */\n    byte[] serialize(ProtoSerializable serializable);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoAtlasMetaDataAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.proto.ProtoAtlasMetaData;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.converters.ProtoTagListConverter;\n\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link AtlasMetaData} and\n * {@link ProtoAtlasMetaData}.\n *\n * @author lcram\n */\npublic class ProtoAtlasMetaDataAdapter implements ProtoAdapter\n{\n    private static final ProtoTagListConverter PROTOTAG_LIST_CONVERTER = new ProtoTagListConverter();\n\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoAtlasMetaData protoAtlasMetaData = null;\n        try\n        {\n            protoAtlasMetaData = ProtoAtlasMetaData.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n\n        AtlasSize atlasSize = null;\n        final boolean hasAllAtlasSizeFeatures = protoAtlasMetaData.hasEdgeNumber()\n                && protoAtlasMetaData.hasNodeNumber() && protoAtlasMetaData.hasAreaNumber()\n                && protoAtlasMetaData.hasLineNumber() && protoAtlasMetaData.hasPointNumber()\n                && protoAtlasMetaData.hasRelationNumber();\n        if (hasAllAtlasSizeFeatures)\n        {\n            atlasSize = new AtlasSize(protoAtlasMetaData.getEdgeNumber(),\n                    protoAtlasMetaData.getNodeNumber(), protoAtlasMetaData.getAreaNumber(),\n                    protoAtlasMetaData.getLineNumber(), protoAtlasMetaData.getPointNumber(),\n                    protoAtlasMetaData.getRelationNumber());\n        }\n\n        final boolean original = protoAtlasMetaData.getOriginal();\n\n        String codeVersion = null;\n        if (protoAtlasMetaData.hasCodeVersion())\n        {\n            codeVersion = protoAtlasMetaData.getCodeVersion();\n        }\n        String dataVersion = null;\n        if (protoAtlasMetaData.hasDataVersion())\n        {\n            dataVersion = protoAtlasMetaData.getDataVersion();\n        }\n        String country = null;\n        if (protoAtlasMetaData.hasCountry())\n        {\n            country = protoAtlasMetaData.getCountry();\n        }\n        String shardName = null;\n        if (protoAtlasMetaData.hasShardName())\n        {\n            shardName = protoAtlasMetaData.getShardName();\n        }\n        final Map<String, String> tags = PROTOTAG_LIST_CONVERTER\n                .convert(protoAtlasMetaData.getTagsList());\n\n        final AtlasMetaData atlasMetaData = new AtlasMetaData(atlasSize, original, codeVersion,\n                dataVersion, country, shardName, tags);\n\n        return atlasMetaData;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof AtlasMetaData))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final AtlasMetaData atlasMetaData = (AtlasMetaData) serializable;\n\n        final ProtoAtlasMetaData.Builder protoMetaDataBuilder = ProtoAtlasMetaData.newBuilder();\n\n        if (atlasMetaData.getSize() != null)\n        {\n            protoMetaDataBuilder.setEdgeNumber(atlasMetaData.getSize().getEdgeNumber());\n            protoMetaDataBuilder.setNodeNumber(atlasMetaData.getSize().getNodeNumber());\n            protoMetaDataBuilder.setAreaNumber(atlasMetaData.getSize().getAreaNumber());\n            protoMetaDataBuilder.setLineNumber(atlasMetaData.getSize().getLineNumber());\n            protoMetaDataBuilder.setPointNumber(atlasMetaData.getSize().getPointNumber());\n            protoMetaDataBuilder.setRelationNumber(atlasMetaData.getSize().getRelationNumber());\n        }\n\n        protoMetaDataBuilder.setOriginal(atlasMetaData.isOriginal());\n\n        atlasMetaData.getCodeVersion().ifPresent(value ->\n        {\n            protoMetaDataBuilder.setCodeVersion(value);\n        });\n\n        atlasMetaData.getDataVersion().ifPresent(value ->\n        {\n            protoMetaDataBuilder.setDataVersion(value);\n        });\n\n        atlasMetaData.getCountry().ifPresent(value ->\n        {\n            protoMetaDataBuilder.setCountry(value);\n        });\n\n        atlasMetaData.getShardName().ifPresent(value ->\n        {\n            protoMetaDataBuilder.setShardName(value);\n        });\n\n        if (atlasMetaData.getTags() != null)\n        {\n            protoMetaDataBuilder.addAllTags(ProtoAtlasMetaDataAdapter.PROTOTAG_LIST_CONVERTER\n                    .backwardConvert(atlasMetaData.getTags()));\n        }\n\n        return protoMetaDataBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoByteArrayOfArraysAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoByteArray;\nimport org.openstreetmap.atlas.proto.ProtoByteArrayOfArrays;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.utilities.arrays.ByteArrayOfArrays;\n\nimport com.google.protobuf.ByteString;\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link ByteArrayOfArrays} and\n * {@link ProtoByteArrayOfArrays}.\n *\n * @author lcram\n */\npublic class ProtoByteArrayOfArraysAdapter implements ProtoAdapter\n{\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoByteArrayOfArrays protoByteArrayOfArrays = null;\n        try\n        {\n            protoByteArrayOfArrays = ProtoByteArrayOfArrays.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n\n        final ByteArrayOfArrays byteArrayOfArrays = new ByteArrayOfArrays(\n                protoByteArrayOfArrays.getArraysCount(), protoByteArrayOfArrays.getArraysCount(),\n                protoByteArrayOfArrays.getArraysCount());\n        if (protoByteArrayOfArrays.hasName())\n        {\n            byteArrayOfArrays.setName(protoByteArrayOfArrays.getName());\n        }\n        protoByteArrayOfArrays.getArraysList().stream().forEach(array ->\n        {\n            final byte[] items = array.getElements().toByteArray();\n            byteArrayOfArrays.add(items);\n        });\n\n        return byteArrayOfArrays;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof ByteArrayOfArrays))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final ByteArrayOfArrays byteArrayOfArrays = (ByteArrayOfArrays) serializable;\n\n        if (byteArrayOfArrays.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize {}, size too large ({})\",\n                    byteArrayOfArrays.getClass().getName(), byteArrayOfArrays.size());\n        }\n\n        final ProtoByteArrayOfArrays.Builder protoArraysBuilder = ProtoByteArrayOfArrays\n                .newBuilder();\n\n        for (final byte[] elementArray : byteArrayOfArrays)\n        {\n            final ProtoByteArray.Builder elementArrayBuilder = ProtoByteArray.newBuilder();\n            if (elementArray == null)\n            {\n                throw new CoreException(\"{} cannot serialize arrays with null elements\",\n                        this.getClass().getName());\n            }\n            elementArrayBuilder.setElements(ByteString.copyFrom(elementArray));\n            protoArraysBuilder.addArrays(elementArrayBuilder);\n        }\n        if (byteArrayOfArrays.getName() != null)\n        {\n            protoArraysBuilder.setName(byteArrayOfArrays.getName());\n        }\n\n        return protoArraysBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoIntegerArrayOfArraysAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoIntegerArrayOfArrays;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.converters.ProtoIntegerArrayOfArraysConverter;\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;\n\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link IntegerArrayOfArrays} and\n * {@link ProtoIntegerArrayOfArrays}.\n *\n * @author lcram\n */\npublic class ProtoIntegerArrayOfArraysAdapter implements ProtoAdapter\n{\n    private static final ProtoIntegerArrayOfArraysConverter CONVERTER = new ProtoIntegerArrayOfArraysConverter();\n\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoIntegerArrayOfArrays protoIntegerArrayOfArrays = null;\n        try\n        {\n            protoIntegerArrayOfArrays = ProtoIntegerArrayOfArrays.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n\n        final IntegerArrayOfArrays integerArrayOfArrays = CONVERTER\n                .convert(protoIntegerArrayOfArrays);\n\n        return integerArrayOfArrays;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof IntegerArrayOfArrays))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final IntegerArrayOfArrays integerArrayOfArrays = (IntegerArrayOfArrays) serializable;\n\n        ProtoIntegerArrayOfArrays protoArrays = null;\n        try\n        {\n            protoArrays = CONVERTER.backwardConvert(integerArrayOfArrays);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Failed to serialize {}\",\n                    integerArrayOfArrays.getClass().getName(), exception);\n        }\n\n        return protoArrays.toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoIntegerStringDictionaryAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoIntegerStringDictionary;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.utilities.compression.IntegerDictionary;\n\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link IntegerDictionary} and\n * {@link ProtoIntegerStringDictionary}. This adapter will fail when attempting to serialize an\n * {@link IntegerDictionary} that is not type parameterized using {@link String}. Also note that\n * {@link ProtoIntegerStringDictionaryAdapter#deserialize(byte[])} will give back an\n * {@link IntegerDictionary} parameterized with {@link String}, no matter what type parameterization\n * was used by the parent {@link IntegerDictionary} the adapter belongs to.\n *\n * @author lcram\n */\npublic class ProtoIntegerStringDictionaryAdapter implements ProtoAdapter\n{\n    /*\n     * IntegerDictionary does not provide an interface for setting its subfields directly. This\n     * class's implementation uses reflection to side-step the issue.\n     */\n\n    /*\n     * IntegerDictionary relies on null entries. Since protobuf cannot serialize Java's 'null'\n     * value, we must represent 'null' in a non-null way. Note that if there are any tags that have\n     * this sentinel as an actual key or value, the adapter will drop them when deserializing.\n     */\n    private static final String NULL_SENTINEL_VALUE = \"_+_NuLl{681FCC7E5213&E39443D7A0DE607A557|385D422B6092F_727517603F69880B5648}_||__\";\n\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoIntegerStringDictionary protoIntegerStringDictionary = null;\n        try\n        {\n            protoIntegerStringDictionary = ProtoIntegerStringDictionary.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n        final IntegerDictionary<String> dictionary = new IntegerDictionary<>();\n\n        final Integer currentIndex = protoIntegerStringDictionary.getCurrentIndex();\n        final List<Integer> indexes = protoIntegerStringDictionary.getIndexesList();\n        final List<String> words = protoIntegerStringDictionary.getWordsList();\n\n        final Map<String, Integer> wordToIndex = new HashMap<>();\n        final Map<Integer, String> indexToWord = new HashMap<>();\n\n        for (int index = 0; index < words.size(); index++)\n        {\n            String word = words.get(index);\n            if (word.equals(NULL_SENTINEL_VALUE))\n            {\n                word = null;\n            }\n            final Integer theIndex = indexes.get(index);\n            wordToIndex.put(word, theIndex);\n            indexToWord.put(theIndex, word);\n        }\n\n        Field wordToIndexField = null;\n        Field indexToWordField = null;\n        Field indexField = null;\n\n        try\n        {\n            wordToIndexField = dictionary.getClass()\n                    .getDeclaredField(IntegerDictionary.FIELD_WORD_TO_INDEX);\n            wordToIndexField.setAccessible(true);\n            wordToIndexField.set(dictionary, wordToIndex);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to set field \\\"{}\\\" in {}\",\n                    IntegerDictionary.FIELD_WORD_TO_INDEX, dictionary.getClass().getName(),\n                    exception);\n        }\n\n        try\n        {\n            indexToWordField = dictionary.getClass()\n                    .getDeclaredField(IntegerDictionary.FIELD_INDEX_TO_WORD);\n            indexToWordField.setAccessible(true);\n            indexToWordField.set(dictionary, indexToWord);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to set field \\\"{}\\\" in {}\",\n                    IntegerDictionary.FIELD_INDEX_TO_WORD, dictionary.getClass().getName(),\n                    exception);\n        }\n\n        try\n        {\n            indexField = dictionary.getClass().getDeclaredField(IntegerDictionary.FIELD_INDEX);\n            indexField.setAccessible(true);\n            indexField.set(dictionary, currentIndex);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to set field \\\"{}\\\" in {}\",\n                    IntegerDictionary.FIELD_INDEX, dictionary.getClass().getName(), exception);\n        }\n\n        return dictionary;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof IntegerDictionary))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final IntegerDictionary<String> integerDictionary = (IntegerDictionary<String>) serializable;\n\n        if (integerDictionary.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize {}, size too large ({})\",\n                    integerDictionary.getClass().getName(), integerDictionary.size());\n        }\n\n        final ProtoIntegerStringDictionary.Builder protoDictionaryBuilder = ProtoIntegerStringDictionary\n                .newBuilder();\n\n        Field indexToWordField = null;\n        Map<Integer, String> indexToWord = null;\n        Field indexField = null;\n        Integer index = -1;\n\n        try\n        {\n            indexToWordField = integerDictionary.getClass()\n                    .getDeclaredField(IntegerDictionary.FIELD_INDEX_TO_WORD);\n            indexToWordField.setAccessible(true);\n            indexToWord = (Map<Integer, String>) indexToWordField.get(integerDictionary);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to read field \\\"{}\\\" from {}\",\n                    IntegerDictionary.FIELD_INDEX_TO_WORD, integerDictionary.getClass().getName(),\n                    exception);\n        }\n\n        /*\n         * Wondering why we don't read the field wordToIndex? We don't need to, since it is\n         * symmetric with the indexToWord field! We can populate the underlying proto arrays by\n         * simply grabbing the keys and values from the indexToWord map.\n         */\n\n        try\n        {\n            indexField = integerDictionary.getClass()\n                    .getDeclaredField(IntegerDictionary.FIELD_INDEX);\n            indexField.setAccessible(true);\n            index = (Integer) indexField.get(integerDictionary);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to read field \\\"{}\\\" from {}\",\n                    IntegerDictionary.FIELD_INDEX, integerDictionary.getClass().getName(),\n                    exception);\n        }\n\n        try\n        {\n            for (final Integer key : indexToWord.keySet())\n            {\n                String word = indexToWord.get(key);\n                if (word == null)\n                {\n                    word = NULL_SENTINEL_VALUE;\n                }\n                protoDictionaryBuilder.addIndexes(key);\n                protoDictionaryBuilder.addWords(word);\n            }\n        }\n        catch (final ClassCastException exception)\n        {\n            throw new CoreException(\n                    \"This adapter is incompatible with type parametrization of the owning {}. Must be java.lang.String\",\n                    serializable.getClass().getName(), exception);\n        }\n        protoDictionaryBuilder.setCurrentIndex(index);\n\n        return protoDictionaryBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoLongArrayAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoLongArray;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.utilities.arrays.LongArray;\n\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link LongArray} and\n * {@link ProtoLongArray}.\n *\n * @author lcram\n */\npublic class ProtoLongArrayAdapter implements ProtoAdapter\n{\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoLongArray protoLongArray = null;\n        try\n        {\n            protoLongArray = ProtoLongArray.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n\n        final int elementCount = protoLongArray.getElementsCount();\n        final LongArray longArray = new LongArray(elementCount, elementCount, elementCount);\n        protoLongArray.getElementsList().stream().forEach(element ->\n        {\n            longArray.add(element);\n        });\n\n        if (protoLongArray.hasName())\n        {\n            longArray.setName(protoLongArray.getName());\n        }\n\n        return longArray;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof LongArray))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final LongArray longArray = (LongArray) serializable;\n\n        if (longArray.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize {}, size too large ({})\",\n                    longArray.getClass().getName(), longArray.size());\n        }\n\n        final ProtoLongArray.Builder protoLongArrayBuilder = ProtoLongArray.newBuilder();\n        for (final long element : longArray)\n        {\n            protoLongArrayBuilder.addElements(element);\n        }\n\n        if (longArray.getName() != null)\n        {\n            protoLongArrayBuilder.setName(longArray.getName());\n        }\n\n        return protoLongArrayBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoLongArrayOfArraysAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoLongArray;\nimport org.openstreetmap.atlas.proto.ProtoLongArrayOfArrays;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.utilities.arrays.LongArrayOfArrays;\n\nimport com.google.common.primitives.Longs;\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link LongArrayOfArrays} and\n * {@link ProtoLongArrayOfArrays}.\n *\n * @author lcram\n */\npublic class ProtoLongArrayOfArraysAdapter implements ProtoAdapter\n{\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoLongArrayOfArrays protoLongArrayOfArrays = null;\n        try\n        {\n            protoLongArrayOfArrays = ProtoLongArrayOfArrays.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n\n        final LongArrayOfArrays longArrayOfArrays = new LongArrayOfArrays(\n                protoLongArrayOfArrays.getArraysCount(), protoLongArrayOfArrays.getArraysCount(),\n                protoLongArrayOfArrays.getArraysCount());\n        if (protoLongArrayOfArrays.hasName())\n        {\n            longArrayOfArrays.setName(protoLongArrayOfArrays.getName());\n        }\n        protoLongArrayOfArrays.getArraysList().stream().forEach(array ->\n        {\n            final long[] items = Longs.toArray(array.getElementsList());\n            longArrayOfArrays.add(items);\n        });\n\n        return longArrayOfArrays;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof LongArrayOfArrays))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final LongArrayOfArrays longArrayOfArrays = (LongArrayOfArrays) serializable;\n\n        if (longArrayOfArrays.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize {}, size too large ({})\",\n                    longArrayOfArrays.getClass().getName(), longArrayOfArrays.size());\n        }\n\n        final ProtoLongArrayOfArrays.Builder protoArraysBuilder = ProtoLongArrayOfArrays\n                .newBuilder();\n        for (final long[] elementArray : longArrayOfArrays)\n        {\n            final ProtoLongArray.Builder elementArrayBuilder = ProtoLongArray.newBuilder();\n            if (elementArray == null)\n            {\n                throw new CoreException(\"{} cannot serialize arrays with null elements\",\n                        this.getClass().getName());\n            }\n            for (int subIndex = 0; subIndex < elementArray.length; subIndex++)\n            {\n                elementArrayBuilder.addElements(elementArray[subIndex]);\n            }\n            protoArraysBuilder.addArrays(elementArrayBuilder);\n        }\n\n        if (longArrayOfArrays.getName() != null)\n        {\n            protoArraysBuilder.setName(longArrayOfArrays.getName());\n        }\n\n        return protoArraysBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoLongToLongMapAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoLongArray;\nimport org.openstreetmap.atlas.proto.ProtoLongToLongMap;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.utilities.maps.LongToLongMap;\n\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link LongToLongMap} and\n * {@link ProtoLongToLongMap}.\n *\n * @author lcram\n */\npublic class ProtoLongToLongMapAdapter implements ProtoAdapter\n{\n    /*\n     * If the maximum size of the LongToLongMap we are reading is less than this value, just use\n     * this value instead of the actual max size. This ensures that calculations in LargeMap using\n     * DEFAULT_HASH_MODULO_RATIO do not fail do to integer division truncation.\n     */\n    private static final int DEFAULT_MAX_SIZE = 1024;\n\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoLongToLongMap protoLongToLongMap = null;\n        try\n        {\n            protoLongToLongMap = ProtoLongToLongMap.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n\n        String deserializedName = null;\n        if (protoLongToLongMap.hasName())\n        {\n            deserializedName = protoLongToLongMap.getName();\n        }\n\n        final int size = protoLongToLongMap.getKeys().getElementsCount() <= DEFAULT_MAX_SIZE\n                ? DEFAULT_MAX_SIZE\n                : protoLongToLongMap.getKeys().getElementsCount();\n        final LongToLongMap longToLongMap = new LongToLongMap(deserializedName, size, size, size,\n                size, size, size);\n\n        for (int index = 0; index < protoLongToLongMap.getKeys().getElementsCount(); index++)\n        {\n            longToLongMap.put(protoLongToLongMap.getKeys().getElements(index),\n                    protoLongToLongMap.getValues().getElements(index));\n        }\n\n        return longToLongMap;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof LongToLongMap))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final LongToLongMap longMap = (LongToLongMap) serializable;\n\n        if (longMap.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize {}, size too large ({})\",\n                    longMap.getClass().getName(), longMap.size());\n        }\n\n        final ProtoLongToLongMap.Builder protoMapBuilder = ProtoLongToLongMap.newBuilder();\n        final ProtoLongArray.Builder keysBuilder = ProtoLongArray.newBuilder();\n        final ProtoLongArray.Builder valuesBuilder = ProtoLongArray.newBuilder();\n\n        final Iterable<Long> iterable = () -> longMap.iterator();\n        for (final Long key : iterable)\n        {\n            final Long value = longMap.get(key);\n            keysBuilder.addElements(key);\n            valuesBuilder.addElements(value);\n        }\n        protoMapBuilder.setKeys(keysBuilder);\n        protoMapBuilder.setValues(valuesBuilder);\n        if (longMap.getName() != null)\n        {\n            protoMapBuilder.setName(longMap.getName());\n        }\n\n        return protoMapBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoLongToLongMultiMapAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoLongArray;\nimport org.openstreetmap.atlas.proto.ProtoLongToLongMultiMap;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.utilities.maps.LongToLongMultiMap;\n\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link LongToLongMultiMap} and\n * {@link ProtoLongToLongMultiMap}.\n *\n * @author lcram\n */\npublic class ProtoLongToLongMultiMapAdapter implements ProtoAdapter\n{\n    /*\n     * If the maximum size of the LongToLongMap we are reading is less than this value, just use\n     * this value instead of the actual max size. This ensures that calculations in LargeMap using\n     * DEFAULT_HASH_MODULO_RATIO do not fail do to integer division truncation.\n     */\n    private static final int DEFAULT_MAX_SIZE = 1024;\n\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoLongToLongMultiMap protoLongToLongMultiMap = null;\n        try\n        {\n            protoLongToLongMultiMap = ProtoLongToLongMultiMap.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n\n        String deserializedName = null;\n        if (protoLongToLongMultiMap.hasName())\n        {\n            deserializedName = protoLongToLongMultiMap.getName();\n        }\n\n        final int size = protoLongToLongMultiMap.getKeys().getElementsCount() <= DEFAULT_MAX_SIZE\n                ? DEFAULT_MAX_SIZE\n                : protoLongToLongMultiMap.getKeys().getElementsCount();\n        final LongToLongMultiMap longToLongMultiMap = new LongToLongMultiMap(deserializedName, size,\n                size, size, size, size, size);\n\n        for (int index = 0; index < protoLongToLongMultiMap.getKeys().getElementsCount(); index++)\n        {\n            // First we get the proto format array associated with this key\n            final ProtoLongArray protoLongArray = protoLongToLongMultiMap.getValuesList()\n                    .get(index);\n            // Now we convert this proto format array to a List<Long> and then to a primitive long[]\n            final long[] values = protoLongArray.getElementsList().stream()\n                    .mapToLong(Long::longValue).toArray();\n            longToLongMultiMap.put(protoLongToLongMultiMap.getKeys().getElements(index), values);\n        }\n\n        return longToLongMultiMap;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof LongToLongMultiMap))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final LongToLongMultiMap longMultiMap = (LongToLongMultiMap) serializable;\n\n        if (longMultiMap.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize {}, size too large ({})\",\n                    longMultiMap.getClass().getName(), longMultiMap.size());\n        }\n\n        if (longMultiMap.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize provided {}, size {} too large\",\n                    serializable.getClass().getName(), longMultiMap.size());\n        }\n\n        final ProtoLongToLongMultiMap.Builder protoMapBuilder = ProtoLongToLongMultiMap\n                .newBuilder();\n        final ProtoLongArray.Builder keysBuilder = ProtoLongArray.newBuilder();\n\n        final Iterable<Long> iterable = () -> longMultiMap.iterator();\n        for (final Long key : iterable)\n        {\n            final ProtoLongArray.Builder valuesBuilder = ProtoLongArray.newBuilder();\n            final long[] value = longMultiMap.get(key);\n            if (value == null)\n            {\n                throw new CoreException(\"{} cannot serialize arrays with null elements\",\n                        this.getClass().getName());\n            }\n            keysBuilder.addElements(key);\n            for (int index = 0; index < value.length; index++)\n            {\n                valuesBuilder.addElements(value[index]);\n            }\n            protoMapBuilder.addValues(valuesBuilder);\n        }\n        protoMapBuilder.setKeys(keysBuilder);\n        if (longMultiMap.getName() != null)\n        {\n            protoMapBuilder.setName(longMultiMap.getName());\n        }\n\n        return protoMapBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoPackedTagStoreAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport java.lang.reflect.Field;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedTagStore;\nimport org.openstreetmap.atlas.proto.ProtoIntegerArrayOfArrays;\nimport org.openstreetmap.atlas.proto.ProtoPackedTagStore;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.converters.ProtoIntegerArrayOfArraysConverter;\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;\n\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link PackedTagStore} and\n * {@link ProtoPackedTagStore}.\n *\n * @author lcram\n */\npublic class ProtoPackedTagStoreAdapter implements ProtoAdapter\n{\n    /*\n     * PackedTagStore does not provide an interface for setting its arrays directly. This class's\n     * implementation uses reflection to side-step the issue. Unlike the other PackedAtlas field\n     * classes, there is no nice converter for the ProtoPackedTagStore (since it cannot be rebuilt\n     * through its public API).\n     */\n\n    private static final ProtoIntegerArrayOfArraysConverter CONVERTER = new ProtoIntegerArrayOfArraysConverter();\n\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoPackedTagStore protoStore = null;\n        try\n        {\n            protoStore = ProtoPackedTagStore.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n        final PackedTagStore store = new PackedTagStore();\n\n        final ProtoIntegerArrayOfArrays protoKeyArray = protoStore.getKeys();\n        final ProtoIntegerArrayOfArrays protoValueArray = protoStore.getValues();\n\n        final IntegerArrayOfArrays keyArray = CONVERTER.convert(protoKeyArray);\n        final IntegerArrayOfArrays valueArray = CONVERTER.convert(protoValueArray);\n        final Long index = protoStore.getIndex();\n\n        Field keyArrayField = null;\n        Field valueArrayField = null;\n        Field indexField = null;\n\n        try\n        {\n            keyArrayField = store.getClass().getDeclaredField(PackedTagStore.FIELD_KEYS);\n            keyArrayField.setAccessible(true);\n            keyArrayField.set(store, keyArray);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to set field \\\"{}\\\" in {}\", PackedTagStore.FIELD_KEYS,\n                    store.getClass().getName(), exception);\n        }\n\n        try\n        {\n            valueArrayField = store.getClass().getDeclaredField(PackedTagStore.FIELD_VALUES);\n            valueArrayField.setAccessible(true);\n            valueArrayField.set(store, valueArray);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to set field \\\"{}\\\" in {}\", PackedTagStore.FIELD_VALUES,\n                    store.getClass().getName(), exception);\n        }\n\n        try\n        {\n            indexField = store.getClass().getDeclaredField(PackedTagStore.FIELD_INDEX);\n            indexField.setAccessible(true);\n            indexField.set(store, index);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to set field \\\"{}\\\" in {}\", PackedTagStore.FIELD_INDEX,\n                    store.getClass().getName(), exception);\n        }\n\n        return store;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof PackedTagStore))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final PackedTagStore store = (PackedTagStore) serializable;\n\n        final ProtoPackedTagStore.Builder protoTagStoreBuilder = ProtoPackedTagStore.newBuilder();\n\n        Field keyArrayField = null;\n        IntegerArrayOfArrays keyArray = null;\n        Field valueArrayField = null;\n        IntegerArrayOfArrays valueArray = null;\n        Field indexField = null;\n        Long index = -1L;\n\n        try\n        {\n            keyArrayField = store.getClass().getDeclaredField(PackedTagStore.FIELD_KEYS);\n            keyArrayField.setAccessible(true);\n            keyArray = (IntegerArrayOfArrays) keyArrayField.get(store);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to read field \\\"{}\\\" from {}\",\n                    PackedTagStore.FIELD_KEYS, store.getClass().getName(), exception);\n        }\n\n        try\n        {\n            valueArrayField = store.getClass().getDeclaredField(PackedTagStore.FIELD_VALUES);\n            valueArrayField.setAccessible(true);\n            valueArray = (IntegerArrayOfArrays) valueArrayField.get(store);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to read field \\\"{}\\\" from {}\",\n                    PackedTagStore.FIELD_VALUES, store.getClass().getName(), exception);\n        }\n\n        try\n        {\n            indexField = store.getClass().getDeclaredField(PackedTagStore.FIELD_INDEX);\n            indexField.setAccessible(true);\n            index = (Long) indexField.get(store);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to read field \\\"{}\\\" from {}\",\n                    PackedTagStore.FIELD_INDEX, store.getClass().getName(), exception);\n        }\n\n        try\n        {\n            protoTagStoreBuilder.setKeys(CONVERTER.backwardConvert(keyArray));\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Failed to serialize {}\", keyArray.getClass().getName(),\n                    exception);\n        }\n        try\n        {\n            protoTagStoreBuilder.setValues(CONVERTER.backwardConvert(valueArray));\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Failed to serialize {}\", valueArray.getClass().getName(),\n                    exception);\n        }\n        protoTagStoreBuilder.setIndex(index);\n\n        return protoTagStoreBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoPolyLineArrayAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.StringCompressedPolyLine;\nimport org.openstreetmap.atlas.proto.ProtoPolyLineArray;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.utilities.arrays.PolyLineArray;\n\nimport com.google.protobuf.ByteString;\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link PolyLineArray} and\n * {@link ProtoPolyLineArray}.\n *\n * @author lcram\n */\npublic class ProtoPolyLineArrayAdapter implements ProtoAdapter\n{\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoPolyLineArray protoPolyLineArray = null;\n        try\n        {\n            protoPolyLineArray = ProtoPolyLineArray.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n        final int encodingsCount = protoPolyLineArray.getEncodingsCount();\n        final PolyLineArray polyLineArray = new PolyLineArray(encodingsCount, encodingsCount,\n                encodingsCount);\n        for (final ByteString encoding : protoPolyLineArray.getEncodingsList())\n        {\n            polyLineArray.add(new StringCompressedPolyLine(encoding.toByteArray()).asPolyLine());\n        }\n\n        if (protoPolyLineArray.hasName())\n        {\n            polyLineArray.setName(protoPolyLineArray.getName());\n        }\n\n        return polyLineArray;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof PolyLineArray))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final PolyLineArray polyLineArray = (PolyLineArray) serializable;\n\n        if (polyLineArray.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize {}, size too large ({})\",\n                    polyLineArray.getClass().getName(), polyLineArray.size());\n        }\n\n        final ProtoPolyLineArray.Builder protoPolyLineArrayBuilder = ProtoPolyLineArray\n                .newBuilder();\n        for (final PolyLine polyLine : polyLineArray)\n        {\n            if (polyLine == null)\n            {\n                throw new CoreException(\"{} cannot serialize arrays with null elements\",\n                        this.getClass().getName());\n            }\n            protoPolyLineArrayBuilder.addEncodings(\n                    ByteString.copyFrom(new StringCompressedPolyLine(polyLine).getEncoding()));\n        }\n\n        if (polyLineArray.getName() != null)\n        {\n            protoPolyLineArrayBuilder.setName(polyLineArray.getName());\n        }\n\n        return protoPolyLineArrayBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/adapters/ProtoPolygonArrayAdapter.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.StringCompressedPolyLine;\nimport org.openstreetmap.atlas.proto.ProtoPolygonArray;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.utilities.arrays.PolygonArray;\n\nimport com.google.protobuf.ByteString;\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Implements the {@link ProtoAdapter} interface to connect {@link PolygonArray} and\n * {@link ProtoPolygonArray}.\n *\n * @author lcram\n */\npublic class ProtoPolygonArrayAdapter implements ProtoAdapter\n{\n    @Override\n    public ProtoSerializable deserialize(final byte[] byteArray)\n    {\n        ProtoPolygonArray protoPolygonArray = null;\n        try\n        {\n            protoPolygonArray = ProtoPolygonArray.parseFrom(byteArray);\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error encountered while parsing protobuf bytestream\",\n                    exception);\n        }\n        final int encodingsCount = protoPolygonArray.getEncodingsCount();\n        final PolygonArray polygonArray = new PolygonArray(encodingsCount, encodingsCount,\n                encodingsCount);\n        for (final ByteString encoding : protoPolygonArray.getEncodingsList())\n        {\n            polygonArray.add(\n                    new Polygon(new StringCompressedPolyLine(encoding.toByteArray()).asPolyLine()));\n        }\n\n        if (protoPolygonArray.hasName())\n        {\n            polygonArray.setName(protoPolygonArray.getName());\n        }\n\n        return polygonArray;\n    }\n\n    @Override\n    public byte[] serialize(final ProtoSerializable serializable)\n    {\n        if (!(serializable instanceof PolygonArray))\n        {\n            throw new CoreException(\n                    \"Invalid ProtoSerializable type was provided to {}: cannot serialize {}\",\n                    this.getClass().getName(), serializable.getClass().getName());\n        }\n        final PolygonArray polygonArray = (PolygonArray) serializable;\n\n        if (polygonArray.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot serialize {}, size too large ({})\",\n                    polygonArray.getClass().getName(), polygonArray.size());\n        }\n\n        final ProtoPolygonArray.Builder protoPolygonArrayBuilder = ProtoPolygonArray.newBuilder();\n        for (final Polygon polygon : polygonArray)\n        {\n            if (polygon == null)\n            {\n                throw new CoreException(\"{} cannot serialize arrays with null elements\",\n                        this.getClass().getName());\n            }\n            protoPolygonArrayBuilder.addEncodings(\n                    ByteString.copyFrom(new StringCompressedPolyLine(polygon).getEncoding()));\n        }\n\n        if (polygonArray.getName() != null)\n        {\n            protoPolygonArrayBuilder.setName(polygonArray.getName());\n        }\n\n        return protoPolygonArrayBuilder.build().toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/builder/ProtoAtlasBuilder.java",
    "content": "package org.openstreetmap.atlas.proto.builder;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.ReverseIdentifierFactory;\nimport org.openstreetmap.atlas.proto.ProtoArea;\nimport org.openstreetmap.atlas.proto.ProtoAtlas;\nimport org.openstreetmap.atlas.proto.ProtoAtlasMetaData;\nimport org.openstreetmap.atlas.proto.ProtoEdge;\nimport org.openstreetmap.atlas.proto.ProtoLine;\nimport org.openstreetmap.atlas.proto.ProtoLocation;\nimport org.openstreetmap.atlas.proto.ProtoNode;\nimport org.openstreetmap.atlas.proto.ProtoPoint;\nimport org.openstreetmap.atlas.proto.ProtoRelation;\nimport org.openstreetmap.atlas.proto.converters.ProtoLocationConverter;\nimport org.openstreetmap.atlas.proto.converters.ProtoTagListConverter;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * Build an {@link Atlas} from a ProtoAtlas formatted file, or write an {@link Atlas} to a\n * ProtoAtlas formatted file. ProtoAtlas is a naive encoding for {@link Atlas}es using protocol\n * buffers. A more compact and performant encoding can be obtained by using\n * {@link PackedAtlasSerializer}.\n *\n * @author lcram\n */\npublic class ProtoAtlasBuilder\n{\n    private static final ProtoLocationConverter PROTOLOCATION_CONVERTER = new ProtoLocationConverter();\n    private static final ProtoTagListConverter PROTOTAG_LIST_CONVERTER = new ProtoTagListConverter();\n    private static final ReverseIdentifierFactory REVERSE_IDENTIFIER_FACTORY = new ReverseIdentifierFactory();\n    private static final Logger logger = LoggerFactory.getLogger(ProtoAtlasBuilder.class);\n\n    /*\n     * When performing serialization, metadata fields with 'null' values will be serialized as\n     * \"unknown\". When deserializing, fields not present in the proto object will be interpreted\n     * with this value.\n     */\n    private static final String NULL_SENTINEL = \"unknown\";\n\n    /*\n     * String that describes the data format of the atlas. This is used by the AtlasMetaData class\n     * to record this version.\n     */\n    public static final String PROTOATLAS_DATA_VERSION = \"ProtoAtlas\";\n\n    /**\n     * Read a resource in naive ProtoAtlas format into a PackedAtlas.\n     *\n     * @param resource\n     *            the resource in naive ProtoAtlas format\n     * @return the constructed PackedAtlas\n     */\n    public PackedAtlas read(final Resource resource)\n    {\n        ProtoAtlas protoAtlas = null;\n\n        // First, we need to construct the container object from the proto binary\n        try\n        {\n            protoAtlas = ProtoAtlas.parseFrom(resource.readBytesAndClose());\n        }\n        catch (final InvalidProtocolBufferException exception)\n        {\n            throw new CoreException(\"Error deserializing the ProtoAtlasContainer from {}\",\n                    resource.getName(), exception);\n        }\n\n        // TODO make sure metadata read is consistent with what is written\n        final ProtoAtlasMetaData protoAtlasMetaData = protoAtlas.getMetaData();\n\n        AtlasSize atlasSize = null;\n        final boolean hasAllAtlasSizeFeatures = protoAtlasMetaData.hasEdgeNumber()\n                && protoAtlasMetaData.hasNodeNumber() && protoAtlasMetaData.hasAreaNumber()\n                && protoAtlasMetaData.hasLineNumber() && protoAtlasMetaData.hasPointNumber()\n                && protoAtlasMetaData.hasRelationNumber();\n        if (hasAllAtlasSizeFeatures)\n        {\n            atlasSize = new AtlasSize(protoAtlasMetaData.getEdgeNumber(),\n                    protoAtlasMetaData.getNodeNumber(), protoAtlasMetaData.getAreaNumber(),\n                    protoAtlasMetaData.getLineNumber(), protoAtlasMetaData.getPointNumber(),\n                    protoAtlasMetaData.getRelationNumber());\n        }\n        else\n        {\n            logger.warn(\"Could not deserialize AtlasSize, using defaults\");\n            atlasSize = AtlasSize.DEFAULT;\n        }\n\n        final String codeVersion = protoAtlasMetaData.hasCodeVersion()\n                ? protoAtlasMetaData.getCodeVersion()\n                : NULL_SENTINEL;\n        final String dataVersion = protoAtlasMetaData.hasDataVersion()\n                ? protoAtlasMetaData.getDataVersion()\n                : NULL_SENTINEL;\n        final String country = protoAtlasMetaData.hasCountry() ? protoAtlasMetaData.getCountry()\n                : NULL_SENTINEL;\n        final String shardName = protoAtlasMetaData.hasShardName()\n                ? protoAtlasMetaData.getShardName()\n                : NULL_SENTINEL;\n\n        final Map<String, String> tags = PROTOTAG_LIST_CONVERTER\n                .convert(protoAtlasMetaData.getTagsList());\n\n        final AtlasMetaData atlasMetaData = new AtlasMetaData(atlasSize,\n                protoAtlasMetaData.getOriginal(), codeVersion, dataVersion, country, shardName,\n                tags);\n\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder().withSizeEstimates(atlasSize)\n                .withMetaData(atlasMetaData).withName(resource.getName());\n\n        // build the atlas features\n        parsePoints(builder, protoAtlas.getPointsList());\n        parseLines(builder, protoAtlas.getLinesList());\n        parseAreas(builder, protoAtlas.getAreasList());\n        parseNodes(builder, protoAtlas.getNodesList());\n        parseEdges(builder, protoAtlas.getEdgesList());\n        parseRelations(builder, protoAtlas.getRelationsList());\n\n        return (PackedAtlas) builder.get();\n    }\n\n    /**\n     * Write an Atlas to a resource in the naive ProtoAtlas format.\n     *\n     * @param atlas\n     *            the Atlas to be written\n     * @param resource\n     *            the resource to write into\n     */\n    public void write(final Atlas atlas, final WritableResource resource)\n    {\n        final ProtoAtlas.Builder protoAtlasBuilder = ProtoAtlas.newBuilder();\n\n        // put the Atlas features into the ProtoAtlasBuilder\n        writePointsToBuilder(atlas, protoAtlasBuilder);\n        writeLinesToBuilder(atlas, protoAtlasBuilder);\n        writeAreasToBuilder(atlas, protoAtlasBuilder);\n        writeNodesToBuilder(atlas, protoAtlasBuilder);\n        writeEdgesToBuilder(atlas, protoAtlasBuilder);\n        writeRelationsToBuilder(atlas, protoAtlasBuilder);\n\n        final AtlasMetaData atlasMetaData = atlas.metaData();\n        final ProtoAtlasMetaData.Builder protoMetaDataBuilder = ProtoAtlasMetaData.newBuilder();\n        if (atlasMetaData.getSize() != null)\n        {\n            protoMetaDataBuilder.setEdgeNumber(atlasMetaData.getSize().getEdgeNumber());\n            protoMetaDataBuilder.setNodeNumber(atlasMetaData.getSize().getNodeNumber());\n            protoMetaDataBuilder.setAreaNumber(atlasMetaData.getSize().getAreaNumber());\n            protoMetaDataBuilder.setLineNumber(atlasMetaData.getSize().getLineNumber());\n            protoMetaDataBuilder.setPointNumber(atlasMetaData.getSize().getPointNumber());\n            protoMetaDataBuilder.setRelationNumber(atlasMetaData.getSize().getRelationNumber());\n        }\n        protoMetaDataBuilder.setOriginal(atlasMetaData.isOriginal());\n\n        atlasMetaData.getCodeVersion().ifPresent(protoMetaDataBuilder::setCodeVersion);\n\n        atlasMetaData.getDataVersion().ifPresent(protoMetaDataBuilder::setDataVersion);\n\n        atlasMetaData.getCountry().ifPresent(protoMetaDataBuilder::setCountry);\n\n        atlasMetaData.getShardName().ifPresent(protoMetaDataBuilder::setShardName);\n\n        if (atlasMetaData.getTags() != null)\n        {\n            protoMetaDataBuilder\n                    .addAllTags(PROTOTAG_LIST_CONVERTER.backwardConvert(atlasMetaData.getTags()));\n        }\n\n        protoAtlasBuilder.setMetaData(protoMetaDataBuilder);\n\n        final ProtoAtlas protoAtlas = protoAtlasBuilder.build();\n        resource.writeAndClose(protoAtlas.toByteArray());\n    }\n\n    private void parseAreas(final PackedAtlasBuilder builder, final List<ProtoArea> areas)\n    {\n        areas.forEach(protoArea ->\n        {\n            final long identifier = protoArea.getId();\n            final List<Location> shapePoints = protoArea.getShapePointsList().stream()\n                    .map(ProtoAtlasBuilder.PROTOLOCATION_CONVERTER::convert)\n                    .collect(Collectors.toList());\n            final Polygon geometry = new Polygon(shapePoints);\n            final Map<String, String> tags = ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER\n                    .convert(protoArea.getTagsList());\n            builder.addArea(identifier, geometry, tags);\n        });\n    }\n\n    private void parseEdges(final PackedAtlasBuilder builder, final List<ProtoEdge> edges)\n    {\n        edges.forEach(protoEdge ->\n        {\n            final long identifier = protoEdge.getId();\n            final List<Location> shapePoints = protoEdge.getShapePointsList().stream()\n                    .map(ProtoAtlasBuilder.PROTOLOCATION_CONVERTER::convert)\n                    .collect(Collectors.toList());\n            final PolyLine geometry = new PolyLine(shapePoints);\n            final Map<String, String> tags = ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER\n                    .convert(protoEdge.getTagsList());\n            builder.addEdge(identifier, geometry, tags);\n        });\n    }\n\n    private void parseLines(final PackedAtlasBuilder builder, final List<ProtoLine> lines)\n    {\n        lines.forEach(protoLine ->\n        {\n            final long identifier = protoLine.getId();\n            final List<Location> shapePoints = protoLine.getShapePointsList().stream()\n                    .map(ProtoAtlasBuilder.PROTOLOCATION_CONVERTER::convert)\n                    .collect(Collectors.toList());\n            final PolyLine geometry = new PolyLine(shapePoints);\n            final Map<String, String> tags = ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER\n                    .convert(protoLine.getTagsList());\n            builder.addLine(identifier, geometry, tags);\n        });\n    }\n\n    private void parseNodes(final PackedAtlasBuilder builder, final List<ProtoNode> nodes)\n    {\n        nodes.forEach(protoNode ->\n        {\n            final long identifier = protoNode.getId();\n            final Longitude longitude = Longitude.dm7(protoNode.getLocation().getLongitude());\n            final Latitude latitude = Latitude.dm7(protoNode.getLocation().getLatitude());\n            final Location geometry = new Location(latitude, longitude);\n            final Map<String, String> tags = ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER\n                    .convert(protoNode.getTagsList());\n            builder.addNode(identifier, geometry, tags);\n        });\n    }\n\n    private void parsePoints(final PackedAtlasBuilder builder, final List<ProtoPoint> points)\n    {\n        points.forEach(protoPoint ->\n        {\n            final long identifier = protoPoint.getId();\n            final Longitude longitude = Longitude.dm7(protoPoint.getLocation().getLongitude());\n            final Latitude latitude = Latitude.dm7(protoPoint.getLocation().getLatitude());\n            final Location geometry = new Location(latitude, longitude);\n            final Map<String, String> tags = ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER\n                    .convert(protoPoint.getTagsList());\n            builder.addPoint(identifier, geometry, tags);\n        });\n    }\n\n    private RelationBean parseRelationBean(final ProtoRelation protoRelation)\n    {\n        final RelationBean bean = new RelationBean();\n\n        protoRelation.getBeansList().forEach(protoRelationBean ->\n        {\n            final long memberId = protoRelationBean.getMemberId();\n            final String memberRole = protoRelationBean.getMemberRole();\n            final ItemType memberType = ItemType\n                    .forValue(protoRelationBean.getMemberType().getNumber());\n            bean.addItem(memberId, memberRole, memberType);\n        });\n\n        return bean;\n    }\n\n    private void parseRelations(final PackedAtlasBuilder builder,\n            final List<ProtoRelation> relations)\n    {\n        relations.forEach(protoRelation ->\n        {\n            final long identifier = protoRelation.getId();\n            final RelationBean bean = parseRelationBean(protoRelation);\n            final Map<String, String> tags = ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER\n                    .convert(protoRelation.getTagsList());\n            builder.addRelation(identifier,\n                    ProtoAtlasBuilder.REVERSE_IDENTIFIER_FACTORY.getOsmIdentifier(identifier), bean,\n                    tags);\n        });\n    }\n\n    private void writeAreasToBuilder(final Atlas atlas, final ProtoAtlas.Builder protoAtlasBuilder)\n    {\n        long numberOfAreas = 0;\n\n        for (final Area area : atlas.areas())\n        {\n            final ProtoArea.Builder protoAreaBuilder = ProtoArea.newBuilder();\n            protoAreaBuilder.setId(area.getIdentifier());\n\n            final List<ProtoLocation> protoLocations = area.asPolygon().stream()\n                    .map(ProtoAtlasBuilder.PROTOLOCATION_CONVERTER::backwardConvert)\n                    .collect(Collectors.toList());\n            protoAreaBuilder.addAllShapePoints(protoLocations);\n\n            final Map<String, String> tags = area.getTags();\n            protoAreaBuilder\n                    .addAllTags(ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER.backwardConvert(tags));\n\n            numberOfAreas++;\n            protoAtlasBuilder.addAreas(protoAreaBuilder.build());\n        }\n        protoAtlasBuilder.setNumberOfAreas(numberOfAreas);\n    }\n\n    private void writeEdgesToBuilder(final Atlas atlas, final ProtoAtlas.Builder protoAtlasBuilder)\n    {\n        long numberOfEdges = 0;\n\n        for (final Edge edge : atlas.edges())\n        {\n            final ProtoEdge.Builder protoEdgeBuilder = ProtoEdge.newBuilder();\n            protoEdgeBuilder.setId(edge.getIdentifier());\n\n            final List<ProtoLocation> protoLocations = edge.asPolyLine().stream()\n                    .map(ProtoAtlasBuilder.PROTOLOCATION_CONVERTER::backwardConvert)\n                    .collect(Collectors.toList());\n            protoEdgeBuilder.addAllShapePoints(protoLocations);\n\n            final Map<String, String> tags = edge.getTags();\n            protoEdgeBuilder\n                    .addAllTags(ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER.backwardConvert(tags));\n\n            numberOfEdges++;\n            protoAtlasBuilder.addEdges(protoEdgeBuilder.build());\n        }\n        protoAtlasBuilder.setNumberOfEdges(numberOfEdges);\n    }\n\n    private void writeLinesToBuilder(final Atlas atlas, final ProtoAtlas.Builder protoAtlasBuilder)\n    {\n        long numberOfLines = 0;\n\n        for (final Line line : atlas.lines())\n        {\n            final ProtoLine.Builder protoLineBuilder = ProtoLine.newBuilder();\n            protoLineBuilder.setId(line.getIdentifier());\n\n            final List<ProtoLocation> protoLocations = line.asPolyLine().stream()\n                    .map(ProtoAtlasBuilder.PROTOLOCATION_CONVERTER::backwardConvert)\n                    .collect(Collectors.toList());\n            protoLineBuilder.addAllShapePoints(protoLocations);\n\n            final Map<String, String> tags = line.getTags();\n            protoLineBuilder\n                    .addAllTags(ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER.backwardConvert(tags));\n\n            numberOfLines++;\n            protoAtlasBuilder.addLines(protoLineBuilder.build());\n        }\n        protoAtlasBuilder.setNumberOfLines(numberOfLines);\n    }\n\n    private void writeNodesToBuilder(final Atlas atlas, final ProtoAtlas.Builder protoAtlasBuilder)\n    {\n        long numberOfNodes = 0;\n\n        for (final Node node : atlas.nodes())\n        {\n            final ProtoNode.Builder protoNodeBuilder = ProtoNode.newBuilder();\n\n            protoNodeBuilder.setId(node.getIdentifier());\n            protoNodeBuilder.setLocation(\n                    ProtoAtlasBuilder.PROTOLOCATION_CONVERTER.backwardConvert(node.getLocation()));\n\n            final Map<String, String> tags = node.getTags();\n            protoNodeBuilder\n                    .addAllTags(ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER.backwardConvert(tags));\n\n            numberOfNodes++;\n            protoAtlasBuilder.addNodes(protoNodeBuilder.build());\n        }\n        protoAtlasBuilder.setNumberOfNodes(numberOfNodes);\n    }\n\n    private void writePointsToBuilder(final Atlas atlas, final ProtoAtlas.Builder protoAtlasBuilder)\n    {\n        long numberOfPoints = 0;\n\n        for (final Point point : atlas.points())\n        {\n            final ProtoPoint.Builder protoPointBuilder = ProtoPoint.newBuilder();\n\n            protoPointBuilder.setId(point.getIdentifier());\n            protoPointBuilder.setLocation(\n                    ProtoAtlasBuilder.PROTOLOCATION_CONVERTER.backwardConvert(point.getLocation()));\n\n            final Map<String, String> tags = point.getTags();\n            protoPointBuilder\n                    .addAllTags(ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER.backwardConvert(tags));\n\n            numberOfPoints++;\n            protoAtlasBuilder.addPoints(protoPointBuilder.build());\n        }\n        protoAtlasBuilder.setNumberOfPoints(numberOfPoints);\n    }\n\n    private void writeRelationsToBuilder(final Atlas atlas,\n            final ProtoAtlas.Builder protoAtlasBuilder)\n    {\n        long numberOfRelations = 0;\n\n        for (final Relation relation : atlas.relations())\n        {\n            final ProtoRelation.Builder protoRelationBuilder = ProtoRelation.newBuilder();\n            protoRelationBuilder.setId(relation.getIdentifier());\n            for (final RelationMember member : relation.members())\n            {\n                final ProtoRelation.RelationBean.Builder beanBuilder = ProtoRelation.RelationBean\n                        .newBuilder();\n                beanBuilder.setMemberId(member.getEntity().getIdentifier());\n                beanBuilder.setMemberRole(member.getRole());\n                final ItemType type = ItemType.forEntity(member.getEntity());\n                beanBuilder.setMemberType(ProtoRelation.ProtoItemType.valueOf(type.getValue()));\n                protoRelationBuilder.addBeans(beanBuilder.build());\n            }\n            final Map<String, String> tags = relation.getTags();\n            protoRelationBuilder\n                    .addAllTags(ProtoAtlasBuilder.PROTOTAG_LIST_CONVERTER.backwardConvert(tags));\n\n            numberOfRelations++;\n            protoAtlasBuilder.addRelations(protoRelationBuilder.build());\n        }\n        protoAtlasBuilder.setNumberOfRelations(numberOfRelations);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/command/PackedToProtoAtlasSubCommand.java",
    "content": "package org.openstreetmap.atlas.proto.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleSubCommand;\n\n/**\n * Command for converting a serialized {@link PackedAtlas} to a serialized ProtoAtlas\n *\n * @author lcram\n */\npublic class PackedToProtoAtlasSubCommand implements FlexibleSubCommand\n{\n    private static final String NAME = \"packed-to-proto\";\n    private static final String DESCRIPTION = \"converts a packed atlas to a naive proto-based atlas\";\n    private static final String PACKED_SWITCH_TEXT = \"packed-atlas\";\n    private static final String PROTO_SWITCH_TEXT = \"proto-atlas\";\n\n    private static final Switch<Path> INPUT_PARAMETER = new Switch<>(PACKED_SWITCH_TEXT,\n            \"Input atlas data in text atlas format\", Paths::get, Optionality.REQUIRED);\n\n    private static final Switch<Path> OUTPUT_PARAMETER = new Switch<>(PROTO_SWITCH_TEXT,\n            \"Output atlas data path\", Paths::get, Optionality.REQUIRED);\n\n    private Path inputPath;\n    private Path outputPath;\n\n    @Override\n    public int execute(final CommandMap map)\n    {\n        this.inputPath = (Path) map.get(INPUT_PARAMETER);\n        this.outputPath = (Path) map.get(OUTPUT_PARAMETER);\n        verifyArguments();\n        PackedAtlas.load(new File(this.inputPath.toFile()))\n                .saveAsProto(new File(this.outputPath.toFile()));\n\n        return 0;\n    }\n\n    @Override\n    public String getDescription()\n    {\n        return DESCRIPTION;\n    }\n\n    @Override\n    public String getName()\n    {\n        return NAME;\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return new SwitchList().with(INPUT_PARAMETER, OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.println(\"-\" + PACKED_SWITCH_TEXT + \"=/input/path/to/packed/atlas\");\n        writer.println(\"-\" + PROTO_SWITCH_TEXT + \"=/output/path/to/proto/atlas\");\n    }\n\n    private void verifyArguments()\n    {\n        if (!Files.isRegularFile(this.inputPath))\n        {\n            throw new CoreException(\"{} is not a readable file\", this.inputPath);\n        }\n\n        try\n        {\n            if (Files.isDirectory(this.outputPath))\n            {\n                throw new CoreException(\"{} is a directory.  Aborting\", this.outputPath);\n            }\n            Files.createDirectories(this.outputPath.getParent());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Error when creating directories {}\",\n                    this.outputPath.getParent(), exception);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/command/ProtoToPackedAtlasSubCommand.java",
    "content": "package org.openstreetmap.atlas.proto.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.proto.builder.ProtoAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.runtime.FlexibleSubCommand;\n\n/**\n * Command for converting a serialized ProtoAtlas to a serialized {@link PackedAtlas}\n *\n * @author lcram\n */\npublic class ProtoToPackedAtlasSubCommand implements FlexibleSubCommand\n{\n    private static final String NAME = \"proto-to-packed\";\n    private static final String DESCRIPTION = \"converts a naive proto-based atlas to a packed atlas\";\n    private static final String PROTO_SWITCH_TEXT = \"proto-atlas\";\n    private static final String PACKED_SWITCH_TEXT = \"packed-atlas\";\n\n    private static final Switch<Path> INPUT_PARAMETER = new Switch<>(PROTO_SWITCH_TEXT,\n            \"Input atlas data in text atlas format\", Paths::get, Optionality.REQUIRED);\n\n    private static final Switch<Path> OUTPUT_PARAMETER = new Switch<>(PACKED_SWITCH_TEXT,\n            \"Output atlas data path\", Paths::get, Optionality.REQUIRED);\n\n    private Path inputPath;\n    private Path outputPath;\n\n    @Override\n    public int execute(final CommandMap map)\n    {\n        this.inputPath = (Path) map.get(INPUT_PARAMETER);\n        this.outputPath = (Path) map.get(OUTPUT_PARAMETER);\n        verifyArguments();\n        new ProtoAtlasBuilder().read(new File(this.inputPath.toFile()))\n                .save(new File(this.outputPath.toFile()));\n\n        return 0;\n    }\n\n    @Override\n    public String getDescription()\n    {\n        return DESCRIPTION;\n    }\n\n    @Override\n    public String getName()\n    {\n        return NAME;\n    }\n\n    @Override\n    public SwitchList switches()\n    {\n        return new SwitchList().with(INPUT_PARAMETER, OUTPUT_PARAMETER);\n    }\n\n    @Override\n    public void usage(final PrintStream writer)\n    {\n        writer.println(\"-\" + PROTO_SWITCH_TEXT + \"=/input/path/to/proto/atlas\");\n        writer.println(\"-\" + PACKED_SWITCH_TEXT + \"=/output/path/to/packed/atlas\");\n    }\n\n    private void verifyArguments()\n    {\n        if (!Files.isRegularFile(this.inputPath))\n        {\n            throw new CoreException(\"{} is not a readable file\", this.inputPath);\n        }\n\n        try\n        {\n            if (Files.isDirectory(this.outputPath))\n            {\n                throw new CoreException(\"{} is a directory.  Aborting\", this.outputPath);\n            }\n            Files.createDirectories(this.outputPath.getParent());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Error when creating directories {}\",\n                    this.outputPath.getParent(), exception);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/converters/ProtoIntegerArrayOfArraysConverter.java",
    "content": "package org.openstreetmap.atlas.proto.converters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoIntegerArray;\nimport org.openstreetmap.atlas.proto.ProtoIntegerArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\nimport com.google.common.primitives.Ints;\n\n/**\n * Converts between the {@link IntegerArrayOfArrays} and its autogenerated protobuf counterpart.\n *\n * @author lcram\n */\npublic class ProtoIntegerArrayOfArraysConverter\n        implements TwoWayConverter<ProtoIntegerArrayOfArrays, IntegerArrayOfArrays>\n{\n    @Override\n    public ProtoIntegerArrayOfArrays backwardConvert(final IntegerArrayOfArrays array)\n    {\n        if (array.size() > Integer.MAX_VALUE)\n        {\n            throw new CoreException(\"Cannot convert {}, size too large ({})\",\n                    array.getClass().getName(), array.size());\n        }\n\n        final ProtoIntegerArrayOfArrays.Builder arraysBuilder = ProtoIntegerArrayOfArrays\n                .newBuilder();\n\n        for (final int[] elementArray : array)\n        {\n            final ProtoIntegerArray.Builder subArrayBuilder = ProtoIntegerArray.newBuilder();\n            if (elementArray == null)\n            {\n                throw new CoreException(\"{} cannot convert arrays with null elements\",\n                        this.getClass().getName());\n            }\n            for (final int element : elementArray)\n            {\n                subArrayBuilder.addElements(element);\n            }\n            arraysBuilder.addArrays(subArrayBuilder);\n        }\n\n        if (array.getName() != null)\n        {\n            arraysBuilder.setName(array.getName());\n        }\n\n        return arraysBuilder.build();\n    }\n\n    @Override\n    public IntegerArrayOfArrays convert(final ProtoIntegerArrayOfArrays protoArray)\n    {\n        final IntegerArrayOfArrays integerArrayOfArrays = new IntegerArrayOfArrays(\n                protoArray.getArraysCount(), protoArray.getArraysCount(),\n                protoArray.getArraysCount());\n        protoArray.getArraysList().stream().forEach(array ->\n        {\n            final int[] items = Ints.toArray(array.getElementsList());\n            integerArrayOfArrays.add(items);\n        });\n\n        if (protoArray.hasName())\n        {\n            integerArrayOfArrays.setName(protoArray.getName());\n        }\n\n        return integerArrayOfArrays;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/converters/ProtoLocationConverter.java",
    "content": "package org.openstreetmap.atlas.proto.converters;\n\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.proto.ProtoLocation;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Converts back and forth between ProtoLocation and Location\n *\n * @author lcram\n */\npublic class ProtoLocationConverter implements TwoWayConverter<ProtoLocation, Location>\n{\n    @Override\n    public ProtoLocation backwardConvert(final Location location)\n    {\n        final ProtoLocation.Builder protoLocationBuilder = ProtoLocation.newBuilder();\n        protoLocationBuilder.setLatitude(Math.toIntExact(location.getLatitude().asDm7()));\n        protoLocationBuilder.setLongitude(Math.toIntExact(location.getLongitude().asDm7()));\n        return protoLocationBuilder.build();\n    }\n\n    @Override\n    public Location convert(final ProtoLocation protoLocation)\n    {\n        final Longitude longitude = Longitude.dm7(protoLocation.getLongitude());\n        final Latitude latitude = Latitude.dm7(protoLocation.getLatitude());\n        return new Location(latitude, longitude);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/proto/converters/ProtoTagListConverter.java",
    "content": "package org.openstreetmap.atlas.proto.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.proto.ProtoTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Converts back and forth between a List of ProtoTags and an OSM tag Map.\n *\n * @author lcram\n */\npublic class ProtoTagListConverter implements TwoWayConverter<List<ProtoTag>, Map<String, String>>\n{\n    private static final Logger logger = LoggerFactory.getLogger(ProtoTagListConverter.class);\n\n    @Override\n    public List<ProtoTag> backwardConvert(final Map<String, String> osmTagMap)\n    {\n        final List<ProtoTag> protoTags = new ArrayList<>();\n        for (final Map.Entry<String, String> entry : osmTagMap.entrySet())\n        {\n            final ProtoTag.Builder tagBuilder = ProtoTag.newBuilder();\n            final String keyText;\n            final String valueText;\n\n            if (entry.getKey() == null)\n            {\n                logger.warn(\"Conversion from OSM tagmap found null key, skipping...\");\n                continue;\n            }\n            else\n            {\n                keyText = entry.getKey();\n            }\n\n            if (entry.getValue() == null)\n            {\n                logger.warn(\"Conversion from OSM tagmap found null value for key {}\", keyText);\n                valueText = \"\";\n            }\n            else\n            {\n                valueText = entry.getValue();\n            }\n\n            tagBuilder.setKey(keyText);\n            tagBuilder.setValue(valueText);\n            protoTags.add(tagBuilder.build());\n\n        }\n        return protoTags;\n    }\n\n    @Override\n    public Map<String, String> convert(final List<ProtoTag> protoTagList)\n    {\n        try\n        {\n            final Map<String, String> result = Maps.hashMap();\n            for (final ProtoTag tag : protoTagList)\n            {\n                result.put(tag.getKey(), tag.getValue());\n            }\n            return result;\n        }\n        catch (final Throwable error)\n        {\n            throw new CoreException(\"Unable to parse proto tags\", error);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/CounterOutputStream.java",
    "content": "package org.openstreetmap.atlas.streaming;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic class CounterOutputStream extends OutputStream\n{\n    private long count = 0;\n    private boolean closed = false;\n\n    @Override\n    public void close()\n    {\n        this.closed = true;\n    }\n\n    public long getCount()\n    {\n        if (!this.closed)\n        {\n            throw new CoreException(\"Cannot get the counts when the stream has not been closed.\");\n        }\n        return this.count;\n    }\n\n    @Override\n    public void write(final byte[] bite, final int offset, final int length) throws IOException\n    {\n        this.count += length - offset;\n    }\n\n    @Override\n    public void write(final int value) throws IOException\n    {\n        this.count++;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/NotifyingIOUtils.java",
    "content": "package org.openstreetmap.atlas.streaming;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.EventListener;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Helpful class for notifying a caller on status of copying data from one stream to anonther\n *\n * @author cstaylor\n */\npublic class NotifyingIOUtils\n{\n    /**\n     * Notified on IO lifecycle events:\n     * <ul>\n     * <li>[1] started</li>\n     * <li>[N] statusUpdate</li>\n     * <li>[1] completed | failed</li>\n     * </ul>\n     *\n     * @author cstaylor\n     */\n    public interface IOProgressListener extends EventListener\n    {\n        void completed();\n\n        void failed(IOException oops);\n\n        void started();\n\n        void statusUpdate(long read);\n    }\n\n    private static final int EOF = -1;\n\n    private static final int DEFAULT_BUFFER_SIZE = 4096;\n\n    private Optional<IOProgressListener> progessListener;\n\n    private int bufferSize;\n\n    public static long copy(final InputStream input, final OutputStream output,\n            final int bufferSize, final IOProgressListener listener) throws IOException\n    {\n        return new NotifyingIOUtils().withListener(listener).withBufferSize(bufferSize).copy(input,\n                output);\n    }\n\n    public static long copy(final InputStream input, final OutputStream output,\n            final IOProgressListener listener) throws IOException\n    {\n        return new NotifyingIOUtils().withListener(listener).copy(input, output);\n    }\n\n    public NotifyingIOUtils()\n    {\n        this.progessListener = Optional.empty();\n        this.bufferSize = DEFAULT_BUFFER_SIZE;\n    }\n\n    public long copy(final InputStream input, final OutputStream output) throws IOException\n    {\n        final byte[] buffer = new byte[this.bufferSize];\n        long count = 0;\n        int bufferReadCount = 0;\n        try\n        {\n            this.progessListener.ifPresent(IOProgressListener::started);\n            while (EOF != (bufferReadCount = input.read(buffer)))\n            {\n                output.write(buffer, 0, bufferReadCount);\n                count += bufferReadCount;\n                final long temporaryCount = count;\n                this.progessListener.ifPresent(listener -> listener.statusUpdate(temporaryCount));\n            }\n            this.progessListener.ifPresent(IOProgressListener::completed);\n        }\n        catch (final IOException oops)\n        {\n            this.progessListener.ifPresent(listener -> listener.failed(oops));\n        }\n        return count;\n    }\n\n    public NotifyingIOUtils withBufferSize(final int bufferSize)\n    {\n        if (bufferSize <= 0)\n        {\n            throw new CoreException(\"Buffer size must be larger than zero: {}\", bufferSize);\n        }\n        this.bufferSize = bufferSize;\n        return this;\n    }\n\n    public NotifyingIOUtils withListener(final IOProgressListener listener)\n    {\n        this.progessListener = Optional.ofNullable(listener);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/SplittableInputStream.java",
    "content": "package org.openstreetmap.atlas.streaming;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * From <a href=\n * \"http://stackoverflow.com/questions/5034311/multiple-readers-for-inputstream-in-java/30262036#30262036\"\n * ></a>\n * <p>\n * IMPORTANT! Make sure to read from the original stream as well, and not just the split ones,\n * otherwise the buffer will blow up.\n * <p>\n * Additionally, this class has been made thread safe.\n *\n * @author matthieun\n */\npublic class SplittableInputStream extends InputStream\n{\n    /**\n     * Almost an input stream: The read-method takes an id.\n     *\n     * @author matthieun\n     */\n    public static class MultiplexedSource\n    {\n        public static final int MINIMUM_BUFFER = 512;\n        public static final int MAXIMUM_BUFFER = 10 * 512;\n\n        // Underlying source\n        private final InputStream source;\n\n        // Read positions of each SplittableInputStream\n        private final List<Integer> readPositions = new ArrayList<>();\n\n        // Data to be read by the SplittableInputStreams\n        private int[] buffer = new int[MINIMUM_BUFFER];\n\n        // Last valid position in buffer\n        private int writePosition = 0;\n\n        public MultiplexedSource(final InputStream source)\n        {\n            this.source = source;\n        }\n\n        /**\n         * Read and advance position for given reader\n         *\n         * @param readerIdentifier\n         *            The reader identifier\n         * @return The byte read\n         * @throws IOException\n         *             In case the source read failed.\n         */\n        public synchronized int read(final int readerIdentifier) throws IOException\n        {\n\n            // Enough data in buffer?\n            if (this.readPositions.get(readerIdentifier) >= this.writePosition)\n            {\n                readJustBuffer();\n                this.buffer[this.writePosition++] = this.source.read();\n            }\n\n            final int position = this.readPositions.get(readerIdentifier);\n            final int byteValue = this.buffer[position];\n            if (byteValue != -1)\n            {\n                this.readPositions.set(readerIdentifier, position + 1);\n            }\n            return byteValue;\n        }\n\n        /**\n         * Add a multiplexed reader\n         *\n         * @param splitIdentifier\n         *            The split identifier\n         * @return The new reader identifier.\n         */\n        protected int addSource(final int splitIdentifier)\n        {\n            this.readPositions\n                    .add(splitIdentifier == -1 ? 0 : this.readPositions.get(splitIdentifier));\n            return this.readPositions.size() - 1;\n        }\n\n        /**\n         * Make room for more data (and drop data that has been read by all readers)\n         */\n        private void readJustBuffer()\n        {\n            final int from = Collections.min(this.readPositions);\n            final int whereTo = Collections.max(this.readPositions);\n            final int newLength = Math.max((whereTo - from) * 2, MINIMUM_BUFFER);\n            // System.out.println(\"New Length: \" + newLength);\n            if (newLength > MAXIMUM_BUFFER)\n            {\n                throw new CoreException(\"The SplittableInputStream buffer is blowing up. \"\n                        + \"Make sure all the split streams (including the original one \"\n                        + \"from which the splits originate!) are read at a similar pace.\");\n            }\n            final int[] newBuf = new int[newLength];\n            System.arraycopy(this.buffer, from, newBuf, 0, whereTo - from);\n            for (int i = 0; i < this.readPositions.size(); i++)\n            {\n                this.readPositions.set(i, this.readPositions.get(i) - from);\n            }\n            this.writePosition -= from;\n            this.buffer = newBuf;\n        }\n    }\n\n    // Non-root fields\n    private final MultiplexedSource multiSource;\n    private final int myId;\n\n    /**\n     * Public constructor: Used for first SplittableInputStream\n     *\n     * @param source\n     *            the source {@link InputStream}\n     */\n    public SplittableInputStream(final InputStream source)\n    {\n        this.multiSource = new MultiplexedSource(source);\n        this.myId = this.multiSource.addSource(-1);\n    }\n\n    /**\n     * Private constructor: Used in split()\n     *\n     * @param multiSource\n     *            The multiplexed source\n     * @param splitId\n     *            The split identifier\n     */\n    private SplittableInputStream(final MultiplexedSource multiSource, final int splitId)\n    {\n        this.multiSource = multiSource;\n        this.myId = multiSource.addSource(splitId);\n    }\n\n    @Override\n    public int read() throws IOException\n    {\n        return this.multiSource.read(this.myId);\n    }\n\n    /**\n     * @return a new InputStream that will read bytes from this position onwards.\n     */\n    public SplittableInputStream split()\n    {\n        return new SplittableInputStream(this.multiSource, this.myId);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/Streams.java",
    "content": "package org.openstreetmap.atlas.streaming;\n\nimport java.io.Closeable;\nimport java.io.Flushable;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Stream utility\n *\n * @author matthieun\n */\npublic final class Streams\n{\n    /**\n     * Safe close of a {@link Closeable} item.\n     *\n     * @param stream\n     *            The stream to close.\n     */\n    public static void close(final Closeable stream)\n    {\n        if (stream == null)\n        {\n            return;\n        }\n        try\n        {\n            stream.close();\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not close stream\", e);\n        }\n    }\n\n    /**\n     * Safe flush of a {@link Flushable} item.\n     *\n     * @param stream\n     *            The stream to flush.\n     */\n    public static void flush(final Flushable stream)\n    {\n        if (stream == null)\n        {\n            return;\n        }\n        try\n        {\n            stream.flush();\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not flush stream\", e);\n        }\n    }\n\n    private Streams()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/StringInputStream.java",
    "content": "package org.openstreetmap.atlas.streaming;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * An {@link InputStream} that reads from a {@link String} for convenience\n *\n * @author matthieun\n */\npublic class StringInputStream extends InputStream\n{\n    private final String source;\n    private int index;\n\n    public StringInputStream(final String source)\n    {\n        this.source = source;\n        this.index = 0;\n    }\n\n    @Override\n    public int read() throws IOException\n    {\n        if (this.index < this.source.length())\n        {\n            final int result = this.source.charAt(this.index);\n            this.index++;\n            return result;\n        }\n        return -1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/StringOutputStream.java",
    "content": "package org.openstreetmap.atlas.streaming;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\n\n/**\n * {@link OutputStream} backed by a {@link StringBuilder}\n *\n * @author matthieun\n */\npublic class StringOutputStream extends OutputStream\n{\n    private final StringBuilder builder = new StringBuilder();\n\n    @Override\n    public String toString()\n    {\n        return this.builder.toString();\n    }\n\n    @Override\n    public void write(final int byteValue) throws IOException\n    {\n        this.builder.append(String.valueOf((char) byteValue));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/compression/Compressor.java",
    "content": "package org.openstreetmap.atlas.streaming.compression;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.zip.GZIPOutputStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Compressor for an {@link OutputStream}\n *\n * @author matthieun\n */\npublic interface Compressor\n{\n    Compressor NONE = new Compressor()\n    {\n        @Override\n        public OutputStream compress(final OutputStream out)\n        {\n            return out;\n        }\n\n        @Override\n        public String toString()\n        {\n            return \"NONE\";\n        }\n    };\n    Compressor GZIP = out ->\n    {\n        try\n        {\n            return new GZIPOutputStream(out);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Cannot create compressor.\", e);\n        }\n    };\n\n    /**\n     * @param out\n     *            The {@link OutputStream} to compress\n     * @return The compressed {@link OutputStream}\n     */\n    OutputStream compress(OutputStream out);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/compression/Decompressor.java",
    "content": "package org.openstreetmap.atlas.streaming.compression;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.zip.GZIPInputStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Decompressor for an {@link InputStream}\n *\n * @author matthieun\n */\npublic interface Decompressor\n{\n    Decompressor NONE = new Decompressor()\n    {\n        @Override\n        public InputStream decompress(final InputStream input)\n        {\n            return input;\n        }\n\n        @Override\n        public String toString()\n        {\n            return \"NONE\";\n        }\n    };\n    Decompressor GZIP = input ->\n    {\n        try\n        {\n            return new GZIPInputStream(input);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Cannot create decompressor.\", e);\n        }\n    };\n\n    /**\n     * @param input\n     *            The {@link InputStream} to decompress\n     * @return The decompressed {@link InputStream}\n     */\n    InputStream decompress(InputStream input);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/CsvLine.java",
    "content": "package org.openstreetmap.atlas.streaming.readers;\n\nimport java.io.IOException;\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport au.com.bytecode.opencsv.CSVParser;\n\n/**\n * A Csv line\n *\n * @author matthieun\n */\npublic final class CsvLine implements Iterable<Object>\n{\n    private final CsvSchema schema;\n    private final String[] items;\n\n    public static CsvLine build(final CsvSchema schema, final String line)\n    {\n        final String[] items;\n        try\n        {\n            items = new CSVParser().parseLine(line);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Could not parse line \" + line, e);\n        }\n        if (items != null && schema != null)\n        {\n            if (items.length == schema.size())\n            {\n                return new CsvLine(schema, items);\n            }\n            throw new CoreException(\"Line -- \" + line + \" -- has \" + items.length\n                    + \" arguments instead of \" + schema.size() + \" expected in the schema.\");\n        }\n        throw new CoreException(\"line or schema was null.\");\n    }\n\n    /**\n     * Force use of factory method\n     */\n    private CsvLine(final CsvSchema schema, final String[] line)\n    {\n        this.schema = schema;\n        this.items = line;\n    }\n\n    /**\n     * Get an item in this line\n     *\n     * @param index\n     *            The index at which the item is in the line\n     * @return The item.\n     */\n    public Object get(final int index)\n    {\n        verifyIndex(index);\n        return this.schema.get(this, index);\n    }\n\n    @Override\n    public Iterator<Object> iterator()\n    {\n        return new Iterator<Object>()\n        {\n            private int index = 0;\n\n            @Override\n            public boolean hasNext()\n            {\n                return this.index < CsvLine.this.items.length;\n            }\n\n            @Override\n            public Object next()\n            {\n                if (!hasNext())\n                {\n                    throw new NoSuchElementException();\n                }\n                return get(this.index++);\n            }\n        };\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringList list = new StringList(() -> new Iterator<String>()\n        {\n            private final Iterator<Object> objects = CsvLine.this.iterator();\n\n            @Override\n            public boolean hasNext()\n            {\n                return this.objects.hasNext();\n            }\n\n            @Override\n            public String next()\n            {\n                final Object next = this.objects.next();\n                return \"\\\"\" + next.toString() + \"\\\"\";\n            }\n        });\n        return list.join(\",\");\n    }\n\n    protected String getValue(final int index)\n    {\n        verifyIndex(index);\n        return this.items[index];\n    }\n\n    private void verifyIndex(final int index)\n    {\n        if (index < 0 || index >= this.items.length)\n        {\n            throw new CoreException(\n                    \"Item index \" + index + \" is out of range: 0 -> \" + this.items.length);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/CsvReader.java",
    "content": "package org.openstreetmap.atlas.streaming.readers;\n\nimport java.util.Iterator;\n\nimport org.openstreetmap.atlas.streaming.resource.AbstractResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Reader for a Csv {@link Resource}. This is using openCsv\n *\n * @author tony\n * @author matthieun\n */\npublic class CsvReader implements Iterator<CsvLine>\n{\n    public static final Logger logger = LoggerFactory.getLogger(CsvReader.class);\n\n    private final String comment;\n    private final Iterator<String> lineIterator;\n    private final CsvSchema schema;\n\n    public CsvReader(final CsvSchema schema, final AbstractResource resource)\n    {\n        this.lineIterator = resource.lines().iterator();\n        this.schema = schema;\n        this.comment = \"#\";\n    }\n\n    /**\n     * @param resource\n     *            The resource to read\n     * @param schema\n     *            The Csv schema to use\n     * @param comment\n     *            The lines starting with this will be ignored.\n     */\n    public CsvReader(final CsvSchema schema, final AbstractResource resource, final String comment)\n    {\n        this.lineIterator = resource.lines().iterator();\n        this.schema = schema;\n        this.comment = comment;\n    }\n\n    @Override\n    public boolean hasNext()\n    {\n        return this.lineIterator.hasNext();\n    }\n\n    @Override\n    public CsvLine next()\n    {\n        CsvLine result = null;\n        String candidate;\n\n        do\n        {\n            candidate = this.lineIterator.next();\n            if (candidate == null)\n            {\n                return null;\n            }\n            result = candidate.startsWith(this.comment) ? null : parse(candidate);\n        }\n        while (this.lineIterator.hasNext() && result == null);\n\n        return result;\n    }\n\n    private CsvLine parse(final String candidate)\n    {\n        try\n        {\n            return CsvLine.build(this.schema, candidate);\n        }\n        catch (final Exception e)\n        {\n            logger.warn(\"Ignoring malformed line: -- {} --. Reason: {}\", candidate, e.getMessage(),\n                    e);\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/CsvSchema.java",
    "content": "package org.openstreetmap.atlas.streaming.readers;\n\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\n\n/**\n * Schema for a Csv {@link Resource}. Each item in a {@link CsvLine} is represented by its\n * {@link StringConverter}. All the {@link StringConverter}s must be supplied in proper order.\n *\n * @author matthieun\n */\npublic class CsvSchema\n{\n    private final List<StringConverter<? extends Object>> converters;\n\n    public CsvSchema(final Iterable<StringConverter<? extends Object>> converters)\n    {\n        this.converters = Iterables.asList(converters);\n    }\n\n    @SafeVarargs\n    public CsvSchema(final StringConverter<? extends Object>... converters)\n    {\n        this.converters = Iterables.asList(Iterables.iterable(converters));\n    }\n\n    /**\n     * Get an item\n     *\n     * @param line\n     *            The line to extract the item from\n     * @param index\n     *            The index at which the item is in the line\n     * @return The item\n     */\n    protected Object get(final CsvLine line, final int index)\n    {\n        verifyIndex(index);\n        return this.converters.get(index).convert(line.getValue(index));\n    }\n\n    /**\n     * The number of columns in this schema\n     *\n     * @return The number of columns in this schema\n     */\n    protected int size()\n    {\n        return this.converters.size();\n    }\n\n    private void verifyIndex(final int index)\n    {\n        if (index < 0 || index >= size())\n        {\n            throw new CoreException(\n                    \"Index \" + index + \" out of CsvSchema bounds of 0 -> \" + size());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/GeoJsonReader.java",
    "content": "package org.openstreetmap.atlas.streaming.readers;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.readers.json.deserializers.LocatedDeserializer;\nimport org.openstreetmap.atlas.streaming.readers.json.serializers.PropertiesLocated;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonObject;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\n\n/**\n * Basic GeoJson stream reader. It reads only Point, LineString and Polygon. It ignores the rest,\n * including CRS.\n *\n * @author matthieun\n */\npublic class GeoJsonReader implements Iterator<PropertiesLocated>\n{\n    private final InputStream input;\n    private final JsonReader reader;\n    private final Gson gson;\n\n    public GeoJsonReader(final Resource source)\n    {\n        this.input = source.read();\n        this.reader = new JsonReader(new InputStreamReader(this.input));\n        final GsonBuilder builder = new GsonBuilder();\n        builder.registerTypeAdapter(Located.class, new LocatedDeserializer());\n        this.gson = builder.create();\n\n        try\n        {\n            this.reader.beginObject();\n            if (!\"type\".equals(this.reader.nextName())\n                    || !\"FeatureCollection\".equals(this.reader.nextString()))\n            {\n                throw new CoreException(\"Malformed feature collection\");\n            }\n            String features = this.reader.nextName();\n            while (!\"features\".equals(features))\n            {\n                // Read and skip the object (crs for example)\n                this.reader.skipValue();\n                features = this.reader.nextName();\n            }\n            this.reader.beginArray();\n        }\n        catch (final Exception e)\n        {\n            Streams.close(this.input);\n            throw new CoreException(\"Error reading GeoJson stream\", e);\n        }\n    }\n\n    @Override\n    public boolean hasNext()\n    {\n        try\n        {\n            final boolean hasNext = this.reader.hasNext()\n                    && !this.reader.peek().equals(JsonToken.END_ARRAY);\n            if (!hasNext)\n            {\n                Streams.close(this.input);\n            }\n            return hasNext;\n        }\n        catch (final IOException e)\n        {\n            Streams.close(this.input);\n            throw new CoreException(\"Error reading GeoJson stream\", e);\n        }\n    }\n\n    @Override\n    public PropertiesLocated next()\n    {\n        if (!hasNext())\n        {\n            throw new NoSuchElementException();\n        }\n        try\n        {\n            Located geometry = null;\n            JsonObject properties = null;\n            this.reader.beginObject();\n            while (this.reader.hasNext())\n            {\n                final String name = this.reader.nextName();\n                if (\"properties\".equals(name))\n                {\n                    // Populate the properties\n                    properties = this.gson.fromJson(this.reader, JsonObject.class);\n                }\n                else if (\"geometry\".equals(name))\n                {\n                    geometry = this.gson.fromJson(this.reader, Located.class);\n                }\n                else\n                {\n                    this.reader.skipValue();\n                }\n            }\n            this.reader.endObject();\n            if (geometry == null || properties == null)\n            {\n                throw new CoreException(\"Geometry or properties were null.\");\n            }\n            return new PropertiesLocated(geometry, properties);\n        }\n        catch (final IOException e)\n        {\n            Streams.close(this.input);\n            throw new CoreException(\"Error reading GeoJson stream\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/csv/converters/CsvLineConverter.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.csv.converters;\n\nimport org.openstreetmap.atlas.streaming.readers.CsvLine;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * Converter from a {@link CsvLine}\n *\n * @param <T>\n *            The type to be converted\n * @author matthieun\n */\npublic interface CsvLineConverter<T> extends Converter<CsvLine, T>\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/converters/MultiPolyLineCoordinateConverter.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.converters;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolyLine;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\n\n/**\n * Used for {@link MultiPolyLine}s\n * \n * @author chunzhu\n */\npublic class MultiPolyLineCoordinateConverter implements Converter<MultiPolyLine, JsonArray>\n{\n    private final PointCoordinateConverter coordinateConverter = new PointCoordinateConverter();\n\n    @Override\n    public JsonArray convert(final MultiPolyLine object)\n    {\n        final JsonArray result = new JsonArray();\n\n        object.forEach(polyline ->\n        {\n            final JsonArray locations = new JsonArray();\n            polyline.forEach(location -> locations.add(this.coordinateConverter.convert(location)));\n            result.add(locations);\n        });\n        return result;\n    }\n\n    public Converter<JsonArray, MultiPolyLine> revert()\n    {\n        return jsonArray ->\n        {\n            final List<PolyLine> polyLines = new ArrayList<>();\n            jsonArray.forEach(polyline ->\n            {\n                final JsonArray points = (JsonArray) polyline;\n                final List<Location> locations = new ArrayList<>();\n                final Iterator<JsonElement> locationIterator = points.iterator();\n                while (locationIterator.hasNext())\n                {\n                    final JsonArray coordinate = (JsonArray) locationIterator.next();\n                    locations.add(this.coordinateConverter.revert().convert(coordinate));\n                }\n                final PolyLine convertedPolyLine = new PolyLine(locations);\n                polyLines.add(convertedPolyLine);\n            });\n            if (polyLines.isEmpty())\n            {\n                throw new CoreException(\"Cannot have an empty polyLines.\");\n            }\n            return new MultiPolyLine(polyLines);\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/converters/MultiPolygonCoordinateConverter.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.converters;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\n\n/**\n * Used for MultiPolygons\n *\n * @author brian_l_davis\n */\npublic class MultiPolygonCoordinateConverter implements Converter<MultiPolygon, JsonArray>\n{\n    private final PointCoordinateConverter coordinateConverter = new PointCoordinateConverter();\n\n    @Override\n    public JsonArray convert(final MultiPolygon object)\n    {\n        final JsonArray result = new JsonArray();\n\n        object.outers().forEach(outerPolygon ->\n        {\n            final JsonArray outerLocations = new JsonArray();\n            outerPolygon.forEach(\n                    location -> outerLocations.add(this.coordinateConverter.convert(location)));\n            result.add(outerLocations);\n            object.innersOf(outerPolygon).forEach(innerPolygon ->\n            {\n                final JsonArray innerLocations = new JsonArray();\n                innerPolygon.forEach(\n                        location -> innerLocations.add(this.coordinateConverter.convert(location)));\n                result.add(innerLocations);\n            });\n        });\n        return result;\n    }\n\n    public Converter<JsonArray, MultiPolygon> revert()\n    {\n        return jsonArray ->\n        {\n            final MultiMap<Polygon, Polygon> outerToInners = new MultiMap<>();\n            jsonArray.forEach(polygon ->\n            {\n\n                final JsonArray linearRings = (JsonArray) polygon;\n                final Iterator<JsonElement> linearRingsIterator = linearRings.iterator();\n                if (linearRingsIterator.hasNext())\n                {\n\n                    final List<Polygon> polygons = new ArrayList<>();\n                    while (linearRingsIterator.hasNext())\n                    {\n                        final JsonArray coordinates = (JsonArray) linearRingsIterator.next();\n                        final List<Location> locations = new ArrayList<>();\n                        coordinates.forEach(coordinate ->\n                        {\n                            final JsonArray points = (JsonArray) coordinate;\n                            locations.add(this.coordinateConverter.revert().convert(points));\n                        });\n                        if (locations.isEmpty()\n                                || !locations.get(0).equals(locations.get(locations.size() - 1)))\n                        {\n                            throw new CoreException(\n                                    \"Invalidly formatted Geojson Polygon within Multipolygon\");\n                        }\n                        // in valid geojson the first point is repeated at the end and does not need\n                        // to be for our polygons\n                        locations.remove(locations.size() - 1);\n                        polygons.add(new Polygon(locations));\n                    }\n                    if (polygons.isEmpty())\n                    {\n                        throw new CoreException(\"Cannot have an empty MultiPolygon.\");\n                    }\n                    final Polygon outer = polygons.remove(0);\n                    outerToInners.put(outer, new ArrayList<>());\n                    polygons.forEach(inner ->\n                    {\n                        if (!outer.fullyGeometricallyEncloses(inner))\n                        {\n                            throw new CoreException(\n                                    \"MultiPolygon inner ring not enclosed by outer ring.\");\n                        }\n                        outerToInners.add(outer, inner);\n                    });\n                }\n            });\n            if (outerToInners.isEmpty())\n            {\n                throw new CoreException(\"Cannot have an empty MultiPolygon.\");\n            }\n            return new MultiPolygon(outerToInners);\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/converters/PointCoordinateConverter.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.converters;\n\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * Converter that converts a {@link Location} object into its coordinate representation in the Geo\n * Json model: \"coordinates\" : [longitude, latitude]\n *\n * @author matthieun\n */\npublic class PointCoordinateConverter implements Converter<Location, JsonArray>\n{\n    private static final double DATE_LINE_LONGITUDE_WEST = -180.0;\n    private static final double DATE_LINE_LONGITUDE_EAST = 180.0;\n\n    @Override\n    public JsonArray convert(final Location location)\n    {\n        final JsonArray coordinates = new JsonArray();\n        coordinates.add(new JsonPrimitive(location.getLongitude().asDegrees()));\n        coordinates.add(new JsonPrimitive(location.getLatitude().asDegrees()));\n        return coordinates;\n    }\n\n    public Converter<JsonArray, Location> revert()\n    {\n        return jsonArray ->\n        {\n            final double latitude = jsonArray.get(1).getAsDouble();\n            double longitude = jsonArray.get(0).getAsDouble();\n            if (longitude == DATE_LINE_LONGITUDE_EAST)\n            {\n                longitude = DATE_LINE_LONGITUDE_WEST;\n            }\n            return new Location(Latitude.degrees(latitude), Longitude.degrees(longitude));\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/converters/PolyLineCoordinateConverter.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\nimport com.google.gson.JsonArray;\n\n/**\n * Used for {@link PolyLine}s\n *\n * @author matthieun\n */\npublic class PolyLineCoordinateConverter implements Converter<Iterable<Location>, JsonArray>\n{\n    private final PointCoordinateConverter coordinateConverter = new PointCoordinateConverter();\n\n    @Override\n    public JsonArray convert(final Iterable<Location> object)\n    {\n        final JsonArray result = new JsonArray();\n        object.forEach(location -> result.add(this.coordinateConverter.convert(location)));\n        return result;\n    }\n\n    public Converter<JsonArray, List<Location>> revert()\n    {\n        return jsonArray ->\n        {\n            final List<Location> result = new ArrayList<>();\n            jsonArray.forEach(jsonElement ->\n            {\n                final JsonArray array = (JsonArray) jsonElement;\n                result.add(this.coordinateConverter.revert().convert(array));\n            });\n            return result;\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/converters/PolygonCoordinateConverter.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\nimport com.google.gson.JsonArray;\n\n/**\n * Used for {@link Polygon}s\n *\n * @author matthieun\n */\npublic class PolygonCoordinateConverter implements Converter<Iterable<Location>, JsonArray>\n{\n    private final PointCoordinateConverter coordinateConverter = new PointCoordinateConverter();\n\n    @Override\n    public JsonArray convert(final Iterable<Location> object)\n    {\n        final JsonArray result = new JsonArray();\n        final JsonArray inner = new JsonArray();\n        object.forEach(location -> inner.add(this.coordinateConverter.convert(location)));\n        result.add(inner);\n        return result;\n    }\n\n    public Converter<JsonArray, List<Location>> revert()\n    {\n        return jsonArray ->\n        {\n            final List<Location> result = new ArrayList<>();\n            jsonArray.forEach(jsonElement ->\n            {\n                final JsonArray array = (JsonArray) jsonElement;\n                array.forEach(element ->\n                {\n                    final JsonArray array2 = (JsonArray) element;\n                    result.add(this.coordinateConverter.revert().convert(array2));\n                });\n            });\n            if (result.isEmpty() || !result.get(0).equals(result.get(result.size() - 1)))\n            {\n                throw new CoreException(\"Invalidly formatted Geojson Polygon\");\n            }\n            // in valid Geojson the first point is repeated at the end where it does not need to be\n            // for our polygons\n            result.remove(result.size() - 1);\n            return result;\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/deserializers/LocatedDeserializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.deserializers;\n\nimport java.lang.reflect.Type;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\n\nimport com.google.gson.JsonDeserializationContext;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonParseException;\n\n/**\n * Deserializer that is clever about re-directing to the proper Located type.\n *\n * @author matthieun\n */\npublic class LocatedDeserializer implements JsonDeserializer<Located>\n{\n    @Override\n    public Located deserialize(final JsonElement json, final Type typeOfT,\n            final JsonDeserializationContext context) throws JsonParseException\n    {\n        final GeoJsonType type = GeoJsonType.forJson(json.getAsJsonObject());\n        if (GeoJsonType.POINT == type)\n        {\n            return new LocationDeserializer().deserialize(json, typeOfT, context);\n        }\n        else if (GeoJsonType.LINESTRING == type)\n        {\n            return new PolyLineDeserializer().deserialize(json, typeOfT, context);\n        }\n        else if (GeoJsonType.POLYGON == type)\n        {\n            return new PolygonDeserializer().deserialize(json, typeOfT, context);\n        }\n        else if (GeoJsonType.MULTI_POLYGON == type)\n        {\n            return new MultiPolygonDeserializer().deserialize(json, typeOfT, context);\n        }\n        else if (GeoJsonType.MULTI_LINESTRING == type)\n        {\n            return new MultiPolyLineDeserializer().deserialize(json, typeOfT, context);\n        }\n        throw new CoreException(\"Unknown/unsupported geometry type: \" + type);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/deserializers/LocationDeserializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.deserializers;\n\nimport java.lang.reflect.Type;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.PointCoordinateConverter;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonDeserializationContext;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParseException;\n\n/**\n * @author matthieun\n */\npublic class LocationDeserializer implements JsonDeserializer<Location>\n{\n    private final PointCoordinateConverter coordinateConverter = new PointCoordinateConverter();\n\n    @Override\n    public Location deserialize(final JsonElement json, final Type typeOfT,\n            final JsonDeserializationContext context) throws JsonParseException\n    {\n        final JsonArray coordinates = (JsonArray) ((JsonObject) json).get(\"coordinates\");\n        return this.coordinateConverter.revert().convert(coordinates);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/deserializers/MultiPolyLineDeserializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.deserializers;\n\nimport java.lang.reflect.Type;\n\nimport org.openstreetmap.atlas.geography.MultiPolyLine;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.MultiPolyLineCoordinateConverter;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonDeserializationContext;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParseException;\n\n/**\n * @author chunzhu\n */\npublic class MultiPolyLineDeserializer implements JsonDeserializer<MultiPolyLine>\n{\n    private final MultiPolyLineCoordinateConverter multiMultiCoordinateConverter = new MultiPolyLineCoordinateConverter();\n\n    @Override\n    public MultiPolyLine deserialize(final JsonElement json, final Type typeOfT,\n            final JsonDeserializationContext context) throws JsonParseException\n    {\n        final JsonArray coordinates = (JsonArray) ((JsonObject) json).get(\"coordinates\");\n        return this.multiMultiCoordinateConverter.revert().convert(coordinates);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/deserializers/MultiPolygonDeserializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.deserializers;\n\nimport java.lang.reflect.Type;\n\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.MultiPolygonCoordinateConverter;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonDeserializationContext;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParseException;\n\n/**\n * @author brian_l_davis\n */\npublic class MultiPolygonDeserializer implements JsonDeserializer<MultiPolygon>\n{\n    private final MultiPolygonCoordinateConverter multiMultiCoordinateConverter = new MultiPolygonCoordinateConverter();\n\n    @Override\n    public MultiPolygon deserialize(final JsonElement json, final Type typeOfT,\n            final JsonDeserializationContext context) throws JsonParseException\n    {\n        final JsonArray coordinates = (JsonArray) ((JsonObject) json).get(\"coordinates\");\n        return this.multiMultiCoordinateConverter.revert().convert(coordinates);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/deserializers/PolyLineDeserializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.deserializers;\n\nimport java.lang.reflect.Type;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.PolyLineCoordinateConverter;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonDeserializationContext;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParseException;\n\n/**\n * @author matthieun\n */\npublic class PolyLineDeserializer implements JsonDeserializer<PolyLine>\n{\n    private final PolyLineCoordinateConverter multiCoordinateConverter = new PolyLineCoordinateConverter();\n\n    @Override\n    public PolyLine deserialize(final JsonElement json, final Type typeOfT,\n            final JsonDeserializationContext context) throws JsonParseException\n    {\n        final JsonArray coordinates = (JsonArray) ((JsonObject) json).get(\"coordinates\");\n        return new PolyLine(this.multiCoordinateConverter.revert().convert(coordinates));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/deserializers/PolygonDeserializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.deserializers;\n\nimport java.lang.reflect.Type;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.PolygonCoordinateConverter;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonDeserializationContext;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParseException;\n\n/**\n * @author matthieun\n */\npublic class PolygonDeserializer implements JsonDeserializer<Polygon>\n{\n    private final PolygonCoordinateConverter multiMultiCoordinateConverter = new PolygonCoordinateConverter();\n\n    /**\n     * @deprecated Currently doesn't return accurate geometric representations for Geojson polygons\n     *             with inner rings use {@link MultiPolygonDeserializer} and check to see if the\n     *             returned multipolygon is a simple polygon\n     **/\n    @Deprecated\n    @Override\n    public Polygon deserialize(final JsonElement json, final Type typeOfT,\n            final JsonDeserializationContext context) throws JsonParseException\n    {\n        final JsonArray coordinates = (JsonArray) ((JsonObject) json).get(\"coordinates\");\n        return new Polygon(this.multiMultiCoordinateConverter.revert().convert(coordinates));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/serializers/LocationSerializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.serializers;\n\nimport java.lang.reflect.Type;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.PointCoordinateConverter;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\nimport com.google.gson.JsonSerializationContext;\nimport com.google.gson.JsonSerializer;\n\n/**\n * {@link JsonSerializer} for a {@link Location}\n *\n * @author matthieun\n */\npublic class LocationSerializer implements JsonSerializer<Location>\n{\n    @Override\n    public JsonElement serialize(final Location location, final Type typeOfSrc,\n            final JsonSerializationContext context)\n    {\n        final JsonObject result = new JsonObject();\n        result.add(\"type\", new JsonPrimitive(\"Point\"));\n        result.add(\"coordinates\", new PointCoordinateConverter().convert(location));\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/serializers/MultiLocationSerializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.serializers;\n\nimport java.lang.reflect.Type;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.PolyLineCoordinateConverter;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\nimport com.google.gson.JsonSerializationContext;\nimport com.google.gson.JsonSerializer;\n\n/**\n * @param <T>\n *            The type of {@link PolyLine} to serialize.\n * @author matthieun\n */\npublic abstract class MultiLocationSerializer<T extends PolyLine> implements JsonSerializer<T>\n{\n    @Override\n    public JsonElement serialize(final T polyLine, final Type typeOfSrc,\n            final JsonSerializationContext context)\n    {\n        final JsonObject result = new JsonObject();\n        result.add(\"type\", new JsonPrimitive(getType()));\n        polyLine.forEach(location ->\n        {\n        });\n        result.add(\"coordinates\", new PolyLineCoordinateConverter().convert(polyLine));\n        return result;\n    }\n\n    /**\n     * @return The type of multi-location item\n     */\n    protected abstract String getType();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/serializers/PolyLineSerializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.serializers;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\n\nimport com.google.gson.JsonSerializer;\n\n/**\n * {@link JsonSerializer} for a {@link PolyLine}\n *\n * @author matthieun\n */\npublic class PolyLineSerializer extends MultiLocationSerializer<PolyLine>\n{\n    @Override\n    protected String getType()\n    {\n        return \"LineString\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/serializers/PolygonSerializer.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.serializers;\n\nimport org.openstreetmap.atlas.geography.Polygon;\n\nimport com.google.gson.JsonSerializer;\n\n/**\n * {@link JsonSerializer} for a {@link Polygon}\n *\n * @author matthieun\n */\npublic class PolygonSerializer extends MultiLocationSerializer<Polygon>\n{\n    @Override\n    protected String getType()\n    {\n        return \"Polygon\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/readers/json/serializers/PropertiesLocated.java",
    "content": "package org.openstreetmap.atlas.streaming.readers.json.serializers;\n\nimport org.openstreetmap.atlas.geography.Located;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Java bean containing a Located object and its properties from a GeoJson file\n *\n * @author matthieun\n */\npublic class PropertiesLocated\n{\n    private final Located item;\n    private final JsonObject properties;\n\n    public PropertiesLocated(final Located item, final JsonObject properties)\n    {\n        this.item = item;\n        this.properties = properties;\n    }\n\n    public Located getItem()\n    {\n        return this.item;\n    }\n\n    public JsonObject getProperties()\n    {\n        return this.properties;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder result = new StringBuilder();\n        result.append(this.item.getClass().getSimpleName());\n        result.append(\": \");\n        result.append(this.item.toString());\n        result.append(\" -- Properties: \");\n        result.append(this.properties);\n        return result.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/AbstractResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.BufferedInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\n\n/**\n * Base implementation for a {@link Resource}\n *\n * @author matthieun\n */\npublic abstract class AbstractResource implements Resource\n{\n    private Decompressor decompressor = Decompressor.NONE;\n    private String name = null;\n\n    public Decompressor getDecompressor()\n    {\n        return this.decompressor;\n    }\n\n    @Override\n    public String getName()\n    {\n        if (this.name == null)\n        {\n            return Resource.super.getName();\n        }\n        return this.name;\n    }\n\n    @Override\n    public long length()\n    {\n        try (InputStream input = new BufferedInputStream(read()))\n        {\n            long length = 0;\n            while (input.read() >= 0)\n            {\n                length++;\n            }\n            return length;\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Resource Length can't be obtained.\", e);\n        }\n    }\n\n    @Override\n    public final InputStream read()\n    {\n        if (this.decompressor == null)\n        {\n            return this.onRead();\n        }\n        return this.decompressor.decompress(this.onRead());\n    }\n\n    public void setDecompressor(final Decompressor decompressor)\n    {\n        this.decompressor = decompressor;\n    }\n\n    public void setName(final String name)\n    {\n        this.name = name;\n    }\n\n    @Override\n    public String toString()\n    {\n        if (getName() != null)\n        {\n            return getName();\n        }\n        return super.toString();\n    }\n\n    /**\n     * @return The raw stream from the resource\n     */\n    protected abstract InputStream onRead();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/AbstractWritableResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.OutputStream;\n\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\n\n/**\n * Implementation for a {@link WritableResource}\n *\n * @author matthieun\n */\npublic abstract class AbstractWritableResource extends AbstractResource implements WritableResource\n{\n    private Compressor compressor = Compressor.NONE;\n\n    public Compressor getCompressor()\n    {\n        return this.compressor;\n    }\n\n    public void setCompressor(final Compressor compressor)\n    {\n        this.compressor = compressor;\n    }\n\n    @Override\n    public final OutputStream write()\n    {\n        if (this.compressor == null)\n        {\n            return this.onWrite();\n        }\n        return this.compressor.compress(this.onWrite());\n    }\n\n    /**\n     * @return The stream where to write raw bytes to the resource\n     */\n    protected abstract OutputStream onWrite();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/ByteArrayResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.arrays.ByteArray;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link WritableResource} backed by a large {@link ByteArray}.\n *\n * @author matthieun\n */\npublic class ByteArrayResource extends AbstractWritableResource\n{\n    private static final Logger logger = LoggerFactory.getLogger(ByteArrayResource.class);\n\n    private static final int BYTE_MASK = 0xFF;\n\n    private final ByteArray array;\n\n    public ByteArrayResource()\n    {\n        this.array = new ByteArray(Long.MAX_VALUE);\n        this.array.setName(\"ByteArrayResource\");\n    }\n\n    /**\n     * @param initialSize\n     *            An initial size to help avoiding resizings.\n     */\n    public ByteArrayResource(final long initialSize)\n    {\n        final int blockSize = (int) (initialSize <= Integer.MAX_VALUE ? initialSize\n                : Integer.MAX_VALUE);\n        this.array = new ByteArray(Long.MAX_VALUE, blockSize, Integer.MAX_VALUE);\n        this.array.setName(\"ByteArrayResource\");\n    }\n\n    @Override\n    public long length()\n    {\n        return this.array.size();\n    }\n\n    public ByteArrayResource withName(final String name)\n    {\n        setName(name);\n        this.array.setName(name);\n        return this;\n    }\n\n    @Override\n    protected InputStream onRead()\n    {\n        return new InputStream()\n        {\n            private long index = 0L;\n            private boolean readOpen = true;\n\n            @Override\n            public void close()\n            {\n                this.readOpen = false;\n            }\n\n            @Override\n            public int read() throws IOException\n            {\n                if (!this.readOpen)\n                {\n                    throw new CoreException(\"Cannot read a closed stream\");\n                }\n                if (this.index >= ByteArrayResource.this.array.size())\n                {\n                    return -1;\n                }\n                return ByteArrayResource.this.array.get(this.index++) & BYTE_MASK;\n            }\n        };\n    }\n\n    @Override\n    protected OutputStream onWrite()\n    {\n        return new OutputStream()\n        {\n            private boolean writeOpen = true;\n\n            @Override\n            public void close()\n            {\n                this.writeOpen = false;\n                logger.trace(\"Closed writer after {} bytes.\", ByteArrayResource.this.array.size());\n            }\n\n            @Override\n            public void write(final int byteValue) throws IOException\n            {\n                if (!this.writeOpen)\n                {\n                    throw new CoreException(\"Cannot write to a closed stream\");\n                }\n                ByteArrayResource.this.array.add((byte) (byteValue & BYTE_MASK));\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/ClassResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.lang.reflect.Type;\nimport java.util.Properties;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.Streams;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonIOException;\nimport com.google.gson.JsonSyntaxException;\nimport com.google.gson.stream.JsonReader;\n\n/**\n * @author cuthbertm\n */\npublic class ClassResource extends AbstractResource\n{\n    private final String resource;\n\n    public ClassResource(final String resource)\n    {\n        this.resource = resource;\n    }\n\n    /**\n     * Converts the resource into an object. It is expected that the format of the resource would be\n     * JSON\n     *\n     * @param gson\n     *            A Gson object to convert the json resource to the specified type. This could have\n     *            special type adapters registered with it so that you can deserialize the resource\n     *            correctly\n     * @param classType\n     *            The type of object to convert the resource json into\n     * @param <T>\n     *            The type T of the object being returned\n     * @return The instantiated object\n     */\n    public <T> T getJSONResourceObject(final Gson gson, final Type classType)\n    {\n        InputStream input = null;\n        try\n        {\n            input = onRead();\n            final JsonReader reader = new JsonReader(new InputStreamReader(onRead()));\n            return gson.fromJson(reader, classType);\n        }\n        catch (final JsonIOException | JsonSyntaxException e)\n        {\n            throw new CoreException(\"Failed to load json file from resource file {}\", this.resource,\n                    e);\n        }\n        finally\n        {\n            Streams.close(input);\n        }\n    }\n\n    /**\n     * Converts the resource into an object. It is expected that the format of the resource would be\n     * JSON\n     *\n     * @param classType\n     *            The type of object to convert the resource json into\n     * @param <T>\n     *            The type T of the object being returned\n     * @return The instantiated objecte\n     */\n    public <T> T getJSONResourceObject(final Type classType)\n    {\n        return this.getJSONResourceObject(new Gson(), classType);\n    }\n\n    /**\n     * Given a resource file location will load from jar/classpath and return a Properties file.\n     * Resource file should follow pattern for Properties file\n     *\n     * @return A properties file\n     */\n    public Properties getResourceAsPropertyFile()\n    {\n        InputStream input = null;\n        try\n        {\n            input = onRead();\n            final Properties props = new Properties();\n            props.load(input);\n            return props;\n        }\n        catch (final IOException ioe)\n        {\n            throw new CoreException(\"Failed to load properties from resource file {}\",\n                    this.resource, ioe);\n        }\n        finally\n        {\n            Streams.close(input);\n        }\n    }\n\n    @Override\n    protected InputStream onRead()\n    {\n        final ClassLoader classloader = Thread.currentThread().getContextClassLoader();\n        if (classloader == null)\n        {\n            throw new CoreException(\"Context Class loader could not be initialized.\");\n        }\n        final InputStream input = classloader.getResourceAsStream(this.resource);\n        if (input == null)\n        {\n            throw new CoreException(\"Resource, {}, not found.\", this.resource);\n        }\n        return input;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/File.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.LinkOption;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.nio.file.attribute.FileAttribute;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * File from a local file system as an {@link AbstractWritableResource}.\n *\n * @author matthieun\n * @author lcram\n */\npublic class File extends AbstractWritableResource implements Comparable<File>\n{\n    private static final Logger logger = LoggerFactory.getLogger(File.class);\n    private static final String COULD_NOT_CREATE_DIRECTORIES_FOR_PATH = \"Could not create directories for path {}\";\n\n    private static final String JAVA_TEMPORARY_DIRECTORY;\n\n    static\n    {\n        final String property = System.getProperty(\"java.io.tmpdir\");\n        if (property == null)\n        {\n            throw new CoreException(\"Could not get System property java.io.tmpdir\");\n        }\n        else\n        {\n            JAVA_TEMPORARY_DIRECTORY = property;\n        }\n    }\n\n    private final Path path;\n    private String name = null;\n\n    /**\n     * Get a {@link TemporaryFile} in the default location using the default {@link FileSystem}.\n     * This method is deprecated in favor of {@link File#temporary(FileSystem)}, which should allow\n     * test cases much more control and flexibility over their environments. If your code needs to\n     * use the default filesystem, then call {@link File#temporary(FileSystem)} with\n     * {@link FileSystems#getDefault()}. However, your code would be more flexible if it were\n     * file-system-agnostic and received the {@link FileSystem} as an input from calling code. See\n     * the unit tests for the {@link File} class for details on how to use jimfs as an alternative\n     * {@link FileSystem} in testing cases.\n     *\n     * @return the temporary file\n     * @deprecated please use {@link File#temporary(FileSystem)} instead.\n     */\n    @Deprecated\n    public static TemporaryFile temporary()\n    {\n        return temporary(FileSystems.getDefault());\n    }\n\n    /**\n     * Get a {@link TemporaryFile} in the default location using the default {@link FileSystem}, but\n     * with a specified prefix and suffix. This method is deprecated in favor of\n     * {@link File#temporary(FileSystem)}, which should allow test cases much more control and\n     * flexibility over their environments. If your code needs to use the default filesystem, then\n     * call {@link File#temporary(FileSystem)} with {@link FileSystems#getDefault()}. However, your\n     * code would be more flexible if it were file-system-agnostic and received the\n     * {@link FileSystem} as an input from calling code. See the unit tests for the {@link File}\n     * class for details on how to use jimfs as an alternative {@link FileSystem} in testing cases.\n     *\n     * @param prefix\n     *            the prefix to use\n     * @param suffix\n     *            the suffix to use\n     * @return the temporary file\n     * @deprecated please use {@link File#temporary(FileSystem, String, String)} instead.\n     */\n    @Deprecated\n    public static TemporaryFile temporary(final String prefix, final String suffix)\n    {\n        return temporary(FileSystems.getDefault(), prefix, suffix);\n    }\n\n    /**\n     * Create a temporary file at the system default temporary location. The name of the file will\n     * be generated randomly and will have the suffix given by {@link FileSuffix#TEMPORARY}.\n     *\n     * @param fileSystem\n     *            the {@link FileSystem} to use for this {@link TemporaryFile}, use\n     *            {@link FileSystems#getDefault()} for the default local {@link FileSystem}\n     * @return the file's {@link TemporaryFile}\n     */\n    public static TemporaryFile temporary(final FileSystem fileSystem)\n    {\n        return temporary(fileSystem, null, FileSuffix.TEMPORARY.toString());\n    }\n\n    /**\n     * Create a temporary file with a given prefix and suffix at the system default temporary\n     * location. The name of the file will be generated randomly and will be prefixed by the given\n     * prefix. The file will have a suffix given by the suffix parameter.\n     *\n     * @param fileSystem\n     *            the {@link FileSystem} to use for this {@link TemporaryFile}, use\n     *            {@link FileSystems#getDefault()} for the default local {@link FileSystem}\n     * @param prefix\n     *            a string prefix to use for the temporary file\n     * @param suffix\n     *            a string suffix to use for the temporary file\n     * @return the file's {@link TemporaryFile}\n     */\n    public static TemporaryFile temporary(final FileSystem fileSystem, final String prefix,\n            final String suffix)\n    {\n        final Path directory = fileSystem.getPath(JAVA_TEMPORARY_DIRECTORY);\n        return temporary(directory, prefix, suffix);\n    }\n\n    /**\n     * Create a temporary file with a given prefix and suffix at a given directory. The name of the\n     * file will be generated randomly and will be prefixed by the given prefix. The file will have\n     * a suffix given by the suffix parameter.\n     *\n     * @param directory\n     *            the parent directory for this temporary file\n     * @param prefix\n     *            a string prefix to use for the temporary file\n     * @param suffix\n     *            a string suffix to use for the temporary file\n     * @return the file's {@link TemporaryFile}\n     */\n    public static TemporaryFile temporary(final Path directory, final String prefix,\n            final String suffix)\n    {\n        /*\n         * Create the directory and all parents if it/they do not exist. Since Files.createTempFile\n         * will not actually create parent directories, this step is necessary in some cases.\n         */\n        new File(directory).mkdirs();\n        try\n        {\n            return new TemporaryFile(Files.createTempFile(directory, prefix, suffix));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\n                    \"Unable to create a temporary file with prefix '{}' and suffix '{}' at {}\",\n                    prefix, suffix, directory.toAbsolutePath(), exception);\n        }\n    }\n\n    /**\n     * Create a temporary folder at the system default temporary location. The name of the folder\n     * will be generated randomly.\n     *\n     * @param fileSystem\n     *            the {@link FileSystem} to use for this {@link TemporaryFile}, use\n     *            {@link FileSystems#getDefault()} for the default local {@link FileSystem}\n     * @return the folder's {@link TemporaryFile}\n     */\n    public static TemporaryFile temporaryFolder(final FileSystem fileSystem)\n    {\n        return temporaryFolder(fileSystem, null);\n    }\n\n    /**\n     * Create a temporary folder with a given prefix at the system default temporary location. The\n     * name of the folder will be generated randomly and will be prefixed by the given prefix.\n     *\n     * @param fileSystem\n     *            the {@link FileSystem} to use for this {@link TemporaryFile}, use *\n     *            {@link FileSystems#getDefault()} for the default local {@link FileSystem}\n     * @param prefix\n     *            a string prefix to use for the temporary folder\n     * @return the folder's {@link TemporaryFile}\n     */\n    public static TemporaryFile temporaryFolder(final FileSystem fileSystem, final String prefix)\n    {\n        final Path directory = fileSystem.getPath(JAVA_TEMPORARY_DIRECTORY);\n        return temporaryFolder(directory, prefix);\n    }\n\n    /**\n     * Get a {@link TemporaryFile} folder in the default location using the default\n     * {@link FileSystem}. This method is deprecated in favor of\n     * {@link File#temporaryFolder(FileSystem)}, which should allow test cases much more control and\n     * flexibility over their environments. If your code needs to use the default filesystem, then\n     * call {@link File#temporaryFolder(FileSystem)} with {@link FileSystems#getDefault()}. However,\n     * your code would be more flexible if it were file-system-agnostic and received the\n     * {@link FileSystem} as an input from calling code. See the unit tests for the {@link File}\n     * class for details on how to use jimfs as an alternative {@link FileSystem} in testing cases.\n     *\n     * @return the temporary folder\n     * @deprecated please use {@link File#temporaryFolder(FileSystem)} instead.\n     */\n    @Deprecated\n    public static TemporaryFile temporaryFolder()\n    {\n        return temporaryFolder(FileSystems.getDefault());\n    }\n\n    /**\n     * Create a temporary folder with a given prefix at a given directory. The name of the folder\n     * will be generated randomly and will be prefixed by the given prefix.\n     *\n     * @param directory\n     *            the parent directory for this temporary folder\n     * @param prefix\n     *            a string prefix to use for the temporary folder\n     * @return the folder's {@link TemporaryFile}\n     */\n    public static TemporaryFile temporaryFolder(final Path directory, final String prefix)\n    {\n        /*\n         * Create the directory and all parents if it/they do not exist. Since Files.createTempFile\n         * will not actually create parent directories, this step is necessary in some cases.\n         */\n        new File(directory).mkdirs();\n        try\n        {\n            return new TemporaryFile(Files.createTempDirectory(directory, prefix));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Unable to create a temporary folder with prefix '{}' at {}\",\n                    prefix, directory.toAbsolutePath(), exception);\n        }\n    }\n\n    /**\n     * Create a new {@link File} from a {@link java.io.File}, creating all necessary parent\n     * directories.\n     *\n     * @param file\n     *            the {@link java.io.File} to use\n     * @deprecated please use {@link File#File(Path)}\n     */\n    @Deprecated\n    public File(final java.io.File file)\n    {\n        this(file, true);\n    }\n\n    /**\n     * Create a new {@link File} from a given {@link Path}, creating all necessary parent\n     * directories.\n     *\n     * @param path\n     *            the {@link Path} to use\n     */\n    public File(final Path path)\n    {\n        this(path, true);\n    }\n\n    /**\n     * Create a new {@link File} from a given {@link Path}, optionally creating all necessary parent\n     * directories.\n     *\n     * @param path\n     *            the {@link Path} to use\n     * @param createParentDirectories\n     *            whether or not to create necessary parent directories\n     */\n    public File(final Path path, final boolean createParentDirectories)\n    {\n        this.path = path;\n        if (path.toAbsolutePath().toString().endsWith(FileSuffix.GZIP.toString()))\n        {\n            this.setCompressor(Compressor.GZIP);\n            this.setDecompressor(Decompressor.GZIP);\n        }\n        if (this.path.getParent() != null && createParentDirectories)\n        {\n            /*\n             * We must explicitly check the case where the direct parent already exists and is a\n             * symbolic link. This is due to the fact that Files#createDirectories does not treat\n             * symlinks to directories as directories themselves. So attempting to create parent\n             * directories for the path \"foo/bar/baz/bat\" where baz is a symlink results in a\n             * FileAlreadyExistsException on baz, even if baz points to a directory such that\n             * \"foo/bar/baz/bat\" is still a valid path. This exception is not behaviour we want,\n             * since all subsequent file operations will function normally. Rather, in this case, we\n             * can simply refrain from even attempting to create the parent directories.\n             */\n            if (Files.isSymbolicLink(this.path.getParent()))\n            {\n                logger.debug(\n                        \"{} already existed and was a symbolic link, skipping parent directory creation\",\n                        this.path.getParent());\n            }\n            else\n            {\n                createDirectoriesForPath(this.path.getParent());\n            }\n        }\n    }\n\n    /**\n     * Create a new {@link File} from a {@link java.io.File}, optionally creating all necessary\n     * parent directories.\n     *\n     * @param file\n     *            the {@link java.io.File} to use\n     * @param createParentDirectories\n     *            whether or not to create necessary parent directories\n     * @deprecated please use {@link File#File(Path, boolean)}\n     */\n    @Deprecated\n    public File(final java.io.File file, final boolean createParentDirectories)\n    {\n        this(file.toPath(), createParentDirectories);\n    }\n\n    /**\n     * Create a new {@link File} from a given path string, using the given {@link FileSystem} to\n     * resolve the path string into an actual {@link Path}. Automatically create all necessary\n     * parent directories.\n     *\n     * @param pathString\n     *            the path string to the file\n     * @param fileSystem\n     *            the {@link FileSystem} to use for resolution\n     */\n    public File(final String pathString, final FileSystem fileSystem)\n    {\n        this(pathString, fileSystem, true);\n    }\n\n    /**\n     * Create a new {@link File} from a given path string, using the given {@link FileSystem} to\n     * resolve the path string into an actual {@link Path}. Optionally create all necessary parent\n     * directories.\n     *\n     * @param pathString\n     *            the path string to the file\n     * @param fileSystem\n     *            the {@link FileSystem} to use for resolution\n     * @param createParentDirectories\n     *            whether or not to create necessary parent directories\n     */\n    public File(final String pathString, final FileSystem fileSystem,\n            final boolean createParentDirectories)\n    {\n        this(fileSystem.getPath(pathString), createParentDirectories);\n    }\n\n    /**\n     * Create a new {@link File} from a given path string, using the default {@link FileSystem}\n     * (i.e. {@link FileSystems#getDefault()}) to resolve the path string into an actual\n     * {@link Path}. Automatically create all necessary parent directories.\n     *\n     * @param pathString\n     *            the path string to the file\n     * @deprecated please use {@link File#File(String, FileSystem)} instead\n     */\n    @Deprecated\n    public File(final String pathString)\n    {\n        this(pathString, FileSystems.getDefault(), true);\n    }\n\n    /**\n     * Create a new {@link File} from a given path string, using the default {@link FileSystem}\n     * (i.e. {@link FileSystems#getDefault()}) to resolve the path string into an actual\n     * {@link Path}. Optionally create all necessary parent directories.\n     *\n     * @param pathString\n     *            the path string to the file\n     * @param createParentDirectories\n     *            whether or not to create necessary parent directories\n     * @deprecated please use {@link File#File(String, FileSystem, boolean)} instead\n     */\n    @Deprecated\n    public File(final String pathString, final boolean createParentDirectories)\n    {\n        this(pathString, FileSystems.getDefault(), createParentDirectories);\n    }\n\n    /**\n     * Get the basename of this {@link File} object as given by the underlying {@link FileSystem}.\n     *\n     * @return the basename of this {@link File}.\n     */\n    public String basename()\n    {\n        return this.path.getFileName().toString();\n    }\n\n    /**\n     * Get a child {@link File} object for this file with a given name. Note this method will not\n     * actually create the child file in the underlying {@link FileSystem}. However, it will attempt\n     * to create all necessary directories such that \"child\" can resolve successfully when actually\n     * created, whether that be through {@link File#writeAndClose(String)}, {@link File#mkdirs()},\n     * or something else. This method will fail if this {@link File} object is not resolvable to a\n     * directory.\n     *\n     * @param name\n     *            the name of the desired child\n     * @return the child {@link File} object\n     */\n    public File child(final String name)\n    {\n        /*\n         * We must explicitly check the case that this.path is a symbolic link. If it is, then we\n         * want to skip the directory creation step. Why? Because the Files#createDirectories call\n         * used in createDirectoriesForPath will fail when this.path is a symlink, even if it points\n         * to a directory. However in this case, our Files.isDirectory() and Path#resolve() calls\n         * later on will follow symlinks for us by default. So in the end, things resolve properly\n         * in all cases.\n         */\n        if (Files.isSymbolicLink(this.path))\n        {\n            logger.debug(\"{} already existed and was a symbolic link, skipping directory creation\",\n                    this.path);\n        }\n        else\n        {\n            createDirectoriesForPath(this.path);\n        }\n\n        if (!Files.isDirectory(this.path))\n        {\n            throw new CoreException(\n                    \"Cannot create the child of file {} since it did not resolve to a directory\",\n                    this.path);\n        }\n        return new File(this.path.resolve(name));\n    }\n\n    /**\n     * This comparison uses the underlying {@link Path#compareTo(Path)} (Object)} implementation for\n     * each {@link File} object. This means that the file contents are not necessarily considered.\n     * If you are looking to compare files strictly using contents, you may consider implementing\n     * your own {@link Comparator} that uses something like {@link File#all()}.\n     *\n     * @param other\n     *            the other {@link File}\n     * @return the comparison value between the two {@link File}s\n     */\n    @Override\n    public int compareTo(final File other)\n    {\n        return this.path.compareTo(other.toPath());\n    }\n\n    /**\n     * Delete this {@link File} from the underlying {@link FileSystem}. This will fail to delete\n     * non-empty directories. If you need that behaviour, see {@link File#deleteRecursively()}.\n     */\n    public void delete()\n    {\n        try\n        {\n            Files.delete(this.path);\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Cannot delete file {}\", this.path, exception);\n        }\n    }\n\n    /**\n     * Delete this {@link File} from the underlying {@link FileSystem}. If this {@link File} is a\n     * non-empty directory, recursively delete all contents before deleting this {@link File}.\n     */\n    public void deleteRecursively()\n    {\n        try\n        {\n            Files.walkFileTree(this.path, new SimpleFileVisitor<Path>()\n            {\n                @Override\n                public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)\n                        throws IOException\n                {\n                    Files.delete(dir);\n                    return FileVisitResult.CONTINUE;\n                }\n\n                @Override\n                public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)\n                        throws IOException\n                {\n                    Files.delete(file);\n                    return FileVisitResult.CONTINUE;\n                }\n            });\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Cannot delete folder {} recursively\", this.path, exception);\n        }\n    }\n\n    /**\n     * This equals check uses the underlying {@link Path#equals(Object)} implementation for each\n     * {@link File} object. This means that the file contents are not necessarily considered. If you\n     * are looking to compare files strictly using contents, you may consider comparing the values\n     * of {@link File#all()}.\n     *\n     * @param other\n     *            the other {@link File}\n     * @return if the two {@link File} objects are equal as specified by\n     *         {@link Path#equals(Object)}.\n     */\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof File)\n        {\n            return this.path.equals(((File) other).toPath());\n        }\n        return false;\n    }\n\n    /**\n     * Check if this {@link File} actually exists in the underlying {@link FileSystem}.\n     *\n     * @return true if this {@link File} exists\n     */\n    public boolean exists()\n    {\n        return Files.exists(this.path);\n    }\n\n    /**\n     * Return a string version of this {@link File}'s absolute path.\n     *\n     * @return the absolute path of this {@link File} as a string\n     */\n    public String getAbsolutePathString()\n    {\n        return this.toAbsolutePath().toString();\n    }\n\n    /**\n     * Get a {@link java.io.File} version of this {@link File} resource. Note that this will force\n     * the file to be resolved against the default filesystem. Please avoid using this method, it is\n     * here for backwards-compatibility purposes.\n     *\n     * @return a {@link java.io.File} version of this {@link File} resource\n     * @deprecated please move away from this method, and consider using either this class or\n     *             {@link Files} for file operations\n     */\n    @Deprecated\n    public java.io.File getFile()\n    {\n        return new java.io.File(this.path.toAbsolutePath().toString());\n    }\n\n    /**\n     * Get the name of this {@link File} {@link Resource}, as specified by the {@link Resource}\n     * interface. By default, this implementation defers to the name of the {@link File} as given by\n     * the underlying {@link FileSystem}. However, through the use of {@link File#withName(String)},\n     * it is possible this name may deviate from this {@link File}'s true basename. For the\n     * ground-truth file system basename, try {@link File#basename()}. We recommend you use\n     * {@link File#basename()} in all cases where the actual {@link File} name is what you care\n     * about.\n     *\n     * @return the name of this {@link File} {@link Resource}\n     */\n    @Override\n    public String getName()\n    {\n        if (this.name != null)\n        {\n            return this.name;\n        }\n        return this.basename();\n    }\n\n    /**\n     * Return a string version of this {@link File}'s parent path. This may be absolute or relative,\n     * depending on the status of the underlying {@link Path}. For the example file\n     * File(\"foo/bar/baz\"), this method will return \"foo/bar\".\n     *\n     * @return the parent path of this {@link File} as a string\n     */\n    public String getParentPathString()\n    {\n        return this.toParentPath().toString();\n    }\n\n    /**\n     * Return a string version of this {@link File}'s path. This may be absolute or relative,\n     * depending on the status of the underlying {@link Path}.\n     *\n     * @return the path of this {@link File} as a string\n     */\n    public String getPathString()\n    {\n        return this.toPath().toString();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.path.hashCode();\n    }\n\n    /**\n     * Determine if this {@link File} is a directory. See\n     * {@link Files#isDirectory(Path, LinkOption...)} for more details.\n     *\n     * @return if this {@link File} is a directory\n     */\n    public boolean isDirectory()\n    {\n        return Files.isDirectory(this.path);\n    }\n\n    /**\n     * Get the size in bytes of this {@link File}. This method defers to the implementation of the\n     * underlying {@link FileSystem} to compute the file size. See {@link Files#size(Path)} for\n     * details.\n     *\n     * @return the size in bytes of this {@link File}\n     */\n    @Override\n    public long length()\n    {\n        try\n        {\n            return Files.size(this.path);\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Could not get length of file {}\", this.path, exception);\n        }\n    }\n\n    /**\n     * Get a {@link List} of all {@link File} objects at this {@link File}'s path, excluding any\n     * directories. If this {@link File} is a regular file, then return a {@link List} containing\n     * only itself. If this {@link File} does not exist, return an empty {@link List}. For example,\n     * suppose this {@link File} is a directory called \"foo/\" containing \"bar\", \"baz/\", \"bat/\", and\n     * \"fred\". This method would return the following {@link List}: [bar, fred].\n     *\n     * @return a {@link List} of all file-only {@link File}s (not directories) at this\n     *         {@link File}'s path\n     */\n    public List<File> listFiles()\n    {\n        return listFiles(false);\n    }\n\n    /**\n     * Get a {@link List} of all {@link File} objects at this {@link File}'s path, optionally\n     * including or excluding any directories. If this {@link File} is a regular file, then return a\n     * {@link List} containing only itself. If this {@link File} does not exist, return an empty\n     * {@link List}. For example, suppose this {@link File} is a directory called \"foo/\" containing\n     * \"bar\", \"baz/\", \"bat/\", and \"fred\". When includeDirectories is specified \"true\", this method\n     * would return the following {@link List}: [bar, baz, bat, fred]. When includeDirectories is\n     * specified \"false\", this method would instead return the following {@link List}: [bar, fred].\n     *\n     * @param includeDirectories\n     *            whether or not to include directories in the list\n     * @return a {@link List} of all {@link File}s at this {@link File}'s path\n     */\n    public List<File> listFiles(final boolean includeDirectories)\n    {\n        final List<File> result = new ArrayList<>();\n        if (!this.exists())\n        {\n            return result;\n        }\n        if (!this.isDirectory())\n        {\n            result.add(this);\n            return result;\n        }\n        try (Stream<Path> pathStream = Files.list(this.path))\n        {\n            pathStream.forEach(path0 ->\n            {\n                final File file = new File(path0, false);\n                if (file.isDirectory())\n                {\n                    if (includeDirectories)\n                    {\n                        result.add(file);\n                    }\n                }\n                else\n                {\n                    result.add(file);\n                }\n            });\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Could not list files at {}\", this.path);\n        }\n\n        return result;\n    }\n\n    /**\n     * If this {@link File} is a directory, recursively list all {@link File}s contained by this\n     * {@link File}. Subdirectories will themselves be listed recursively. If this {@link File} is a\n     * file, simply return a singleton list containing this {@link File}. If this {@link File} does\n     * not exist, return an empty {@link List}. The 'includeDirectories' parameter will only control\n     * whether directories are included in the final list, not whether subdirectories are expanded.\n     * All subdirectories are always expanded.\n     *\n     * @param includeDirectories\n     *            whether or not to include directories in the list\n     * @return the {@link List} of all {@link File}s contained in this {@link File}\n     */\n    public List<File> listFilesRecursively(final boolean includeDirectories)\n    {\n        final List<File> result = new ArrayList<>();\n        if (!this.exists())\n        {\n            return result;\n        }\n        if (!this.isDirectory())\n        {\n            result.add(this);\n            return result;\n        }\n\n        for (final File file : this.listFiles(true))\n        {\n            final File listedFile = new File(file.toAbsolutePath());\n            if (listedFile.isDirectory())\n            {\n                if (includeDirectories)\n                {\n                    result.add(listedFile);\n                }\n                // We need to carry through the value of includeDirectories\n                result.addAll(listedFile.listFilesRecursively(includeDirectories));\n            }\n            else\n            {\n                result.add(listedFile);\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * If this {@link File} is a directory, recursively list all {@link File}s contained by this\n     * {@link File}. Subdirectories will themselves be listed recursively. The final result will\n     * contain only leaf {@link File} objects. If this {@link File} is a file, simply return a\n     * singleton list containing this {@link File}. If this {@link File} does not exist, return an\n     * empty {@link List}.\n     *\n     * @return the {@link List} of all {@link File}s contained in this {@link File}\n     */\n    public List<File> listFilesRecursively()\n    {\n        return listFilesRecursively(false);\n    }\n\n    /**\n     * Create a directory for this {@link File}'s {@link Path} by creating all non-existent parent\n     * directories first. See {@link Files#createDirectories(Path, FileAttribute[])}. For example,\n     * if this {@link File} is specified by the {@link Path} \"/foo/bar/baz\", then this method will\n     * first create \"foo\" if necessary, followed by \"bar\", and then finally \"baz\".\n     */\n    public void mkdirs()\n    {\n        createDirectoriesForPath(this.path);\n    }\n\n    /**\n     * Get a {@link File} object representing the parent directory of this {@link File}.\n     *\n     * @return a {@link File} object of the parent directory\n     */\n    public File parent()\n    {\n        return new File(this.toAbsolutePath().getParent());\n    }\n\n    /**\n     * Get the absolute {@link Path} object associated with this {@link File}.\n     *\n     * @return the absolute {@link Path} for this {@link File}\n     */\n    public Path toAbsolutePath()\n    {\n        if (this.path.isAbsolute())\n        {\n            return this.path;\n        }\n        return this.path.toAbsolutePath();\n    }\n\n    /**\n     * Get the parent {@link Path} of the {@link Path} object associated with this {@link File}.\n     *\n     * @return the parent {@link Path} of this {@link File}.\n     */\n    public Path toParentPath()\n    {\n        return this.parent().toPath();\n    }\n\n    /**\n     * Get the {@link Path} object associated with this {@link File}.\n     *\n     * @return the {@link Path} for this {@link File}\n     */\n    public Path toPath()\n    {\n        return this.path;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getAbsolutePathString();\n    }\n\n    /**\n     * Utilize a given {@link Compressor} when writing to this {@link File}.\n     *\n     * @param compressor\n     *            the {@link Compressor} to use\n     * @return this {@link File} for chaining\n     */\n    public File withCompressor(final Compressor compressor)\n    {\n        this.setCompressor(compressor);\n        return this;\n    }\n\n    /**\n     * Utilize a given {@link Decompressor} when reading from this {@link File}.\n     *\n     * @param decompressor\n     *            the {@link Decompressor} to use\n     * @return this {@link File} for chaining\n     */\n    public File withDecompressor(final Decompressor decompressor)\n    {\n        this.setDecompressor(decompressor);\n        return this;\n    }\n\n    /**\n     * Update the name of this {@link File} {@link Resource}. Note that this simply changes the name\n     * metadata in the {@link Resource} object, it *will not* change the actual name of the file in\n     * the filesystem. Generally, users should avoid this method as it may cause confusion in\n     * downstream code.\n     *\n     * @param name\n     *            the new name\n     * @return this {@link File} for chaining\n     */\n    public File withName(final String name)\n    {\n        this.name = name;\n        return this;\n    }\n\n    @Override\n    protected InputStream onRead()\n    {\n        try\n        {\n            return new BufferedInputStream(Files.newInputStream(this.path));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Cannot read file {}\", this.path, exception);\n        }\n    }\n\n    @Override\n    protected OutputStream onWrite()\n    {\n        try\n        {\n            return new BufferedOutputStream(Files.newOutputStream(this.path));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Cannot write to file {}\", this.path, exception);\n        }\n    }\n\n    /**\n     * Create a directory for a given {@link Path} by creating all non-existent parent directories\n     * first. See documentation for {@link Files#createDirectories(Path, FileAttribute[])}.\n     *\n     * @param path\n     *            the given {@link Path}\n     */\n    private void createDirectoriesForPath(final Path path)\n    {\n        try\n        {\n            Files.createDirectories(path);\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(COULD_NOT_CREATE_DIRECTORIES_FOR_PATH, path, exception);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/FileSuffix.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.nio.file.Path;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport com.google.common.base.Joiner;\n\n/**\n * @author mgostintsev\n */\npublic enum FileSuffix\n{\n    ATLAS(\".atlas\"),\n    CHANGESET(\".cs\"),\n    CSV(\".csv\"),\n    GEO_JSON(\".geojson\"),\n    GZIP(\".gz\"),\n    // extended csv\n    EXTENDED(\".ext\"),\n    JSON(\".json\"),\n    NONE(\"\"),\n    OSMPBF(\".osm.pbf\"),\n    OSM(\".osm\"),\n    PBF(\".pbf\"),\n    PROTOATLAS(\".patlas\"),\n    TEMPORARY(\".tmp\"),\n    TEXT(\".txt\"),\n    ZIP(\".zip\"),\n    WKT(\".wkt\"),\n    WKB(\".wkb\");\n\n    private final String suffix;\n\n    public static FileSuffix getEnum(final String value)\n    {\n        return suffixFor(value).orElseThrow(\n                () -> new IllegalArgumentException(\"No file suffix found for \" + value));\n    }\n\n    public static Predicate<Path> pathFilter(final FileSuffix... listOfSuffixes)\n    {\n        final String suffix = Joiner.on(\"\").join(listOfSuffixes);\n        return path -> path.getFileName().toString().toLowerCase().endsWith(suffix);\n    }\n\n    public static Predicate<Resource> resourceFilter(final FileSuffix... listOfSuffixes)\n    {\n        final String suffix = Joiner.on(\"\").join(listOfSuffixes);\n        return resource -> resource.getName().endsWith(suffix);\n    }\n\n    public static Optional<FileSuffix> suffixFor(final String value)\n    {\n        final String compareMe = value.toLowerCase();\n        return Stream.of(FileSuffix.values())\n                .filter(suffix -> compareMe.endsWith(suffix.toString())).findFirst();\n    }\n\n    FileSuffix(final String suffix)\n    {\n        this.suffix = suffix;\n    }\n\n    public boolean matches(final Resource resource)\n    {\n        final Optional<FileSuffix> foundSuffix = suffixFor(resource.getName());\n        return foundSuffix.isPresent() && foundSuffix.get() == this;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.suffix;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/GeoJsonFile.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.util.Iterator;\n\nimport org.openstreetmap.atlas.streaming.readers.GeoJsonReader;\nimport org.openstreetmap.atlas.streaming.readers.json.serializers.PropertiesLocated;\n\n/**\n * File that contains GeoJson data.\n *\n * @author matthieun\n */\npublic class GeoJsonFile extends File implements Iterable<PropertiesLocated>\n{\n    public GeoJsonFile(final String path)\n    {\n        super(path);\n    }\n\n    @Override\n    public Iterator<PropertiesLocated> iterator()\n    {\n        return new GeoJsonReader(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/InputStreamResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.InputStream;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\n\n/**\n * Readable resource from an {@link InputStream}. This is readable once only when using the\n * {@link InputStream} constructor, and readable as many times as needed when using the\n * {@link Supplier} constructor.\n *\n * @author matthieun\n */\npublic class InputStreamResource extends AbstractResource\n{\n    private final Supplier<InputStream> inputStreamSupplier;\n\n    /**\n     * The supplier given to this constructor should return a new input stream at each invocation to\n     * avoid any read-once gotchas. E.g. of a good supplier: () -&gt; new FileInputStream(\"foo.txt\")\n     *\n     * @param inputStreamSupplier\n     *            the stream supplier\n     */\n    public InputStreamResource(final Supplier<InputStream> inputStreamSupplier)\n    {\n        this.inputStreamSupplier = inputStreamSupplier;\n    }\n\n    public InputStreamResource withDecompressor(final Decompressor decompressor)\n    {\n        this.setDecompressor(decompressor);\n        return this;\n    }\n\n    public InputStreamResource withName(final String name)\n    {\n        this.setName(name);\n        return this;\n    }\n\n    @Override\n    protected InputStream onRead()\n    {\n        return this.inputStreamSupplier.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/InputStreamResourceCloseable.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.InputStream;\nimport java.util.Objects;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\n/**\n * Create an InputStream resource that depends upon other resources that should not be closed until\n * the caller is done with the InputStream\n * \n * @author Taylor Smock\n */\npublic class InputStreamResourceCloseable extends InputStreamResource implements ResourceCloseable\n{\n    private final AutoCloseable[] dependencies;\n\n    /**\n     * Create a new InputStreamWritableResource\n     * \n     * @param inputStreamSupplier\n     *            The InputStream to write to\n     * @param dependencies\n     *            The dependencies that should be closed on finish\n     */\n    @SafeVarargs\n    public InputStreamResourceCloseable(final Supplier<InputStream> inputStreamSupplier,\n            final AutoCloseable... dependencies)\n    {\n        super(inputStreamSupplier);\n        this.dependencies = dependencies != null\n                ? Stream.of(dependencies).filter(Objects::nonNull).toArray(AutoCloseable[]::new)\n                : new AutoCloseable[0];\n    }\n\n    @Override\n    public AutoCloseable[] getDependencies()\n    {\n        return this.dependencies;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/LineFilteredResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.InputStream;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * A resource that has a built-in filter for the lines method.\n *\n * @author matthieun\n */\npublic class LineFilteredResource implements Resource\n{\n    private final Resource source;\n    private final Predicate<String> lineFilter;\n\n    public LineFilteredResource(final Resource source, final Predicate<String> lineFilter)\n    {\n        this.source = source;\n        this.lineFilter = lineFilter;\n    }\n\n    @Override\n    public long length()\n    {\n        return this.source.length();\n    }\n\n    @Override\n    public Iterable<String> lines()\n    {\n        return Iterables.stream(this.source.lines()).filter(this.lineFilter).collect();\n    }\n\n    @Override\n    public InputStream read()\n    {\n        return this.source.read();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/LineWriter.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.Charset;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Write lines to a {@link WritableResource}\n *\n * @author matthieun\n */\npublic class LineWriter extends BufferedWriter\n{\n    private static final Charset CHARSET = Charset.forName(\"UTF-8\");\n    private static final String LINE_SEPARATOR = System.getProperty(\"line.separator\");\n\n    private final WritableResource writableResource;\n\n    public LineWriter(final WritableResource writableResource)\n    {\n        super(new OutputStreamWriter(writableResource.write(), CHARSET));\n        this.writableResource = writableResource;\n    }\n\n    public void writeLine(final String line)\n    {\n        try\n        {\n            write(line);\n            write(LINE_SEPARATOR);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Unable to write line to {}\", this.writableResource, e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/OutputStreamWritableResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Writable resource from an {@link OutputStream}\n *\n * @author matthieun\n */\npublic class OutputStreamWritableResource extends AbstractWritableResource\n{\n    private final OutputStream out;\n\n    public OutputStreamWritableResource(final OutputStream out)\n    {\n        this.out = out;\n    }\n\n    @Override\n    protected InputStream onRead()\n    {\n        throw new CoreException(\"This resource cannot be read.\");\n    }\n\n    @Override\n    protected OutputStream onWrite()\n    {\n        return this.out;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/OutputStreamWritableResourceCloseable.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.OutputStream;\nimport java.util.Objects;\nimport java.util.stream.Stream;\n\n/**\n * Create an OutputStream resource that depends upon other resources that should not be closed until\n * the caller is done with the OutputStream\n * \n * @author Taylor Smock\n */\npublic class OutputStreamWritableResourceCloseable extends OutputStreamWritableResource\n        implements WritableResourceCloseable\n{\n    private final AutoCloseable[] dependencies;\n\n    /**\n     * Create a new OutputStreamWritableResource\n     * \n     * @param out\n     *            The OutputStream to write to\n     * @param dependencies\n     *            The dependencies that should be closed on finish\n     */\n    public OutputStreamWritableResourceCloseable(final OutputStream out,\n            final AutoCloseable... dependencies)\n    {\n        super(out);\n        this.dependencies = dependencies != null\n                ? Stream.of(dependencies).filter(Objects::nonNull).toArray(AutoCloseable[]::new)\n                : new AutoCloseable[0];\n    }\n\n    @Override\n    public AutoCloseable[] getDependencies()\n    {\n        return this.dependencies;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/README.md",
    "content": "# FAQ/Notes on the recent [File](https://github.com/osmlab/atlas/blob/dev/src/main/java/org/openstreetmap/atlas/streaming/resource/File.java) refactor\n\n---\n### FAQ\n\n1. **What has been updated?**\n\nOur [`org.openstreetmap.atlas.streaming.resource.File`](https://github.com/osmlab/atlas/blob/dev/src/main/java/org/openstreetmap/atlas/streaming/resource/File.java)\nimplementation (hereafter referred to simply as `File`) has been refactored to entirely\nuse [`FileSystem`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/FileSystem.html)\nand [`java.nio.file`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/package-summary.html) operations.\nIt no longer depends on [`java.io.File`](https://docs.oracle.com/javase/8/docs/api/java/io/File.html) for\nany functionality. Additionally, the class is now\n[heavily tested](https://github.com/osmlab/atlas/blob/dev/src/test/java/org/openstreetmap/atlas/streaming/resource/FileTest.java)\nusing [`jimfs`](https://github.com/google/jimfs).\n\nFor the curious, here are a few links discussing the differences between `java.io.File`\nand the file manipulation code in `java.nio.file`:\n\nhttps://docs.oracle.com/javase/tutorial/essential/io/legacy.html\nhttps://docs.oracle.com/javase/8/docs/api/java/nio/file/package-summary.html#package.description\n\n\n2. **Why were these updates made?**\n\nBefore these updates, any `File` object (through its dependence on `java.io.File`) was\nat the mercy of the default `FileSystem`. Code components which used `File` were\ndifficult to test because they automatically relied on file system state external to\nthe test environment. Even modular code that operated on caller-provided paths still had\nto be grounded in the default `FileSystem`. Because of this, unit/integration tests often\nrelied on creating temporary files and directories at the system default temporary location.\nThis approach is brittle, since testing code ultimately has no control over the state of\nthe system default temporary location. This is especially true when running local tests,\nsince a poorly crafted test could inadvertently be affected by file system state left from previous test runs.\n\nThese changes make it much easier to decouple file manipulation code from a concrete\nfile system. Unit and integration tests can now easily utilize a mock file system\n(like [`jimfs`](https://github.com/google/jimfs)) for the code they are testing.\n\n3. **My code uses a method that is now deprecated. What should I do?**\n\nYour initial approach should be to decouple the file manipulation parts of your code from any\nspecific `FileSystem` implementation. This allows clients of your code to provide whatever implementation\nthey would like. See the below samples.\n\nCoupled code using the old `File` implementation:\n```java\npublic class MyOldCoupledComponent\n{\n    /*\n     * This \"computeSomething\" uses the old deprecated methods in File.\n     * Thus, MyOldCoupledComponent automatically uses the default FileSystem,\n     * and there is nothing MyOldClient can do to change this.\n     */\n\n    public void computeSomething(String pathstring)\n    {\n        int i = 2 * 2;\n\n        // Store result at user's home folder\n        if (pathstring == null)\n        {\n            String userHome = System.getProperty(\"user.home\");\n            File result = new File(userHome).child(\"result\");\n            result.writeAndClose(\"result: \" + i);\n        }\n        // Store result at alternate location\n        else\n        {\n            File result = new File(pathstring).child(\"result\");\n            result.writeAndClose(\"result: \" + i);\n        }\n    }\n}\n\npublic class MyOldClient\n{\n    public void useComponent()\n    {\n        MyOldCoupledComponent c = new MyOldCoupledComponent();\n\n        // Store the result in the home folder\n        c.computeSomething(null);\n\n        // Also store it somewhere else\n        c.computeSomething(\"/foo/bar\")\n    }\n}\n```\n\nDecoupled code using the new `File` implementation:\n```java\npublic class MyNewDecoupledComponent\n{\n    /*\n     * Below are two different ways you might refactor \"computeSomething\"\n     * to utilize the new File implementation in a FileSystem agnostic way.\n     */\n\n    // This version uses the pathstring like before, but also receives a\n    // FileSystem argument to contextualize the pathstring\n    public void computeSomethingString(FileSystem filesystem, String pathstring)\n    {\n        int i = 2 * 2;\n\n        // Store result at user's home folder in the provided FileSystem\n        if (pathstring == null)\n        {\n            String userHome = System.getProperty(\"user.home\");\n            File result = new File(userHome, filesystem).child(\"result\");\n            result.writeAndClose(\"result: \" + i);\n        }\n        // Store result at alternate location\n        else\n        {\n            File result = new File(pathstring, filesystem).child(\"result\");\n            result.writeAndClose(\"result: \" + i);\n        }\n    }\n\n    // This version takes a Path, which is already implicitly tied to\n    // a specific FileSystem. In this case, it would be the client's\n    // responsibility to ensure the Path is tied to the FileSystem they want\n    public void computeSomethingPath(Path path)\n    {\n        int i = 2 * 2;\n\n        // Store result at user's home folder\n        if (path == null)\n        {\n            // We cannot infer a FileSystem when no Path is given,\n            // so just assume default\n            String userHome = System.getProperty(\"user.home\");\n            File result = new File(userHome, FileSystems.getDefault()).child(\"result\");\n            result.writeAndClose(\"result: \" + i);\n        }\n        // Store result at alternate location\n        else\n        {\n            // No need to specify a FileSystem here, since it is already part of\n            // the Path object\n            File result = new File(path).child(\"result\");\n            result.writeAndClose(\"result: \" + i);\n        }\n    }\n}\n\npublic class MyNewClient\n{\n    /*\n     * Notice that MyNewClient has complete control over the FileSystem used by\n     * the File class in MyNewDecoupledComponent. This means that no matter how\n     * complex the component is, we can always easily test it. It also means that\n     * we can construct arbitrarily complex file system states in our unit tests,\n     * making them that much more effective at identifying edge cases and brittle code.\n     */\n\n    public void useComponent()\n    {\n        MyNewDecoupledComponent c = new MyNewDecoupledComponent();\n\n        // Store the result in the home folder on the default FileSystem\n        c.computeSomethingString(FileSystems.getDefault(), null)\n        c.computeSomethingPath(null);\n\n        // Some ways to store it at \"/foo/bar\" in the default FileSystem\n        c.computeSomethingString(FileSystems.getDefault(), \"/foo/bar\");\n        c.computeSomethingPath(Paths.get(\"/foo/bar\"));\n        c.computeSomethingPath(Path.of(\"/foo\", \"bar\"));\n        c.computeSomethingPath(FileSystems.getDefault().getPath(\"/foo/bar\"));\n\n        // Store it at \"/baz/bat\" in an alternate filesystem (jimfs)\n        try (FileSystem jimfsFileSystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            c.computeSomethingString(jimfsFileSystem, \"/baz/bat\");\n            c.computeSomethingPath(jimfsFileSystem.getPath(\"/baz/bat\"))\n        }\n        catch (final IOException exception)\n        {\n            // ...\n        }\n    }\n}\n```\n\n4. **I don't want to refactor my code. What can I do to remove the deprecated warnings?**\n\nIf you really want to avoid refactoring your code like in the above sample, then you can just use\nthe new version of each deprecated method that takes a `FileSystem`, and simply provide\n`FileSystems.getDefault()` as an argument.\n\nSo `new File(\"/foo\")` becomes `new File(\"/foo\", FileSystems.getDefault())`,\n`File.temporary()` becomes `File.temporary(FileSystems.getDefault())`, etc.\n\nFor code that uses the deprecated method `getFile`, you *will* have to change your code to\nperform file operations using the `java.nio.file` package. For more information on this,\n[see the documentation here.](https://docs.oracle.com/javase/tutorial/essential/io/legacy.html)\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/Resource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.NoSuchElementException;\n\nimport org.apache.commons.io.IOUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.NotifyingIOUtils;\nimport org.openstreetmap.atlas.streaming.NotifyingIOUtils.IOProgressListener;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * A resource that contains data and is readable by default.\n *\n * @author matthieun\n */\npublic interface Resource\n{\n    int BYTE_MASK = 0x00FF;\n\n    /**\n     * @return The full contents of the {@link Resource} as a {@link String}\n     */\n    default String all()\n    {\n        return new StringList(lines()).join(\"\\n\");\n    }\n\n    /**\n     * Copy all the contents of this {@link Resource} to a {@link WritableResource}\n     *\n     * @param output\n     *            The output {@link WritableResource}\n     */\n    default void copyTo(final WritableResource output)\n    {\n        try (InputStream inputStream = read(); OutputStream outputStream = output.write())\n        {\n            IOUtils.copy(inputStream, outputStream);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to copy {} to {}.\", this, output, e);\n        }\n    }\n\n    /**\n     * Copy all of the contents of {@link Resource} to a {@link WritableResource} while notifying a\n     * progress listener\n     *\n     * @param output\n     *            The output {@link WritableResource}\n     * @param listener\n     *            The notification {@link IOProgressListener} called as data is being copied\n     */\n    default void copyTo(final WritableResource output,\n            final NotifyingIOUtils.IOProgressListener listener)\n    {\n        try (InputStream inputStream = read(); OutputStream outputStream = output.write())\n        {\n            NotifyingIOUtils.copy(inputStream, outputStream, listener);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to copy {} to {}.\", this, output, e);\n        }\n    }\n\n    /**\n     * @return The first line in this resource\n     */\n    default String firstLine()\n    {\n        try (BufferedReader reader = reader())\n        {\n            return reader.readLine();\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Unable to read first line of {}\", this, e);\n        }\n    }\n\n    /**\n     * @return The optional name of the resource.\n     */\n    default String getName()\n    {\n        return getClass().getName() + \"@\" + Integer.toHexString(hashCode());\n    }\n\n    /**\n     * @return True if the name of this resource lets believe that the resource contains Gzipped\n     *         contents.\n     */\n    default boolean isGzipped()\n    {\n        return FileSuffix.GZIP.matches(this);\n    }\n\n    /**\n     * @return The raw length on the {@link Resource} (as stored, regardless of compression).\n     *         Depending on the type of the {@link Resource}, it can be really slow if there is no\n     *         direct length meta data.\n     */\n    long length();\n\n    /**\n     * @return A String {@link Iterable} of all the lines in this resource.\n     */\n    default Iterable<String> lines()\n    {\n        final BufferedReader reader = reader();\n        return () ->\n        {\n            try\n            {\n                return new Iterator<String>()\n                {\n                    private String line = reader.readLine();\n\n                    @Override\n                    public boolean hasNext()\n                    {\n                        return this.line != null;\n                    }\n\n                    @Override\n                    public String next()\n                    {\n                        if (!hasNext())\n                        {\n                            throw new NoSuchElementException();\n                        }\n                        final String result = this.line;\n                        populateNextLine();\n                        return result;\n                    }\n\n                    private void populateNextLine()\n                    {\n                        try\n                        {\n                            this.line = reader.readLine();\n                        }\n                        catch (final IOException e)\n                        {\n                            Streams.close(reader);\n                            throw new CoreException(\"Could not read resource line\", e);\n                        }\n                        if (this.line == null)\n                        {\n                            Streams.close(reader);\n                        }\n                    }\n                };\n            }\n            catch (final IOException e)\n            {\n                Streams.close(reader);\n                throw new CoreException(\"Could not read resource line\", e);\n            }\n        };\n    }\n\n    /**\n     * @return A {@link StringList} of all the lines in this resource.\n     */\n    default StringList linesList()\n    {\n        return new StringList(lines());\n    }\n\n    /**\n     * @return An {@link InputStream} streaming the contents of the resource\n     */\n    InputStream read();\n\n    /**\n     * @return The contents of the resource as a String\n     */\n    default String readAndClose()\n    {\n        final StringList builder = new StringList();\n        lines().forEach(builder::add);\n        return builder.join(System.lineSeparator());\n    }\n\n    /**\n     * @return The contents of the resource as a byte[]\n     */\n    default byte[] readBytesAndClose()\n    {\n        final List<Byte> byteContents = new ArrayList<>();\n        int kyte;\n        try (InputStream input = new BufferedInputStream(read()))\n        {\n            while ((kyte = input.read()) >= 0)\n            {\n                byteContents.add((byte) (kyte & BYTE_MASK));\n            }\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Unable to read the bytes from {}.\", this, e);\n        }\n        final byte[] contents = new byte[byteContents.size()];\n        for (int index = 0; index < byteContents.size(); index++)\n        {\n            contents[index] = byteContents.get(index);\n        }\n        return contents;\n    }\n\n    /**\n     * @return A {@link BufferedReader} on this resource.\n     */\n    default BufferedReader reader()\n    {\n        return new BufferedReader(new InputStreamReader(this.read(), StandardCharsets.UTF_8));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/ResourceCloseable.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Use when the returned resource has some items that cannot be closed prior to finishing with this\n * resource.\n *\n * @author Taylor Smock\n */\npublic interface ResourceCloseable extends Resource, AutoCloseable\n{\n    @Override\n    default void close() throws Exception\n    {\n        final Collection<Exception> exceptions = new ArrayList<>();\n        for (final AutoCloseable closeable : Stream.of(getDependencies()).filter(Objects::nonNull)\n                .collect(Collectors.toList()))\n        {\n            try\n            {\n                closeable.close();\n            }\n            catch (final Exception exception)\n            {\n                exceptions.add(exception);\n            }\n        }\n        if (!exceptions.isEmpty())\n        {\n            throw new CoreException(\n                    \"{} exceptions thrown while closing {}, only showing the first thrown exception\",\n                    exceptions.size(), this.getName(), exceptions.iterator().next());\n        }\n    }\n\n    /**\n     * Get the {@link AutoCloseable} resources that this resource depends upon.\n     *\n     * @return An array of {@link AutoCloseable} dependencies\n     */\n    default AutoCloseable[] getDependencies()\n    {\n        return new AutoCloseable[0];\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/StreamOfResourceStreams.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.SequenceInputStream;\nimport java.util.Collections;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Piggybacking on that classic IO class for concatenating the contents of several streams into one\n *\n * @author cstaylor\n */\npublic class StreamOfResourceStreams extends SequenceInputStream\n{\n    public StreamOfResourceStreams(final Resource... resources)\n    {\n        super(Collections.enumeration(Stream.of(resources).map(resource -> resource.read())\n                .collect(Collectors.toList())));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/StringResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.streaming.StringInputStream;\nimport org.openstreetmap.atlas.streaming.StringOutputStream;\n\n/**\n * A {@link Resource} that relies on a {@link String} for convenience.\n *\n * @author matthieun\n */\npublic class StringResource extends AbstractWritableResource\n{\n    private String source;\n    private StringOutputStream out;\n\n    /**\n     * A {@link StringResource} for Writing, then Reading\n     */\n    public StringResource()\n    {\n        this.out = new StringOutputStream();\n    }\n\n    public StringResource(final AbstractResource source)\n    {\n        final StringBuilder builder = new StringBuilder();\n        source.lines().forEach(line ->\n        {\n            builder.append(line);\n            builder.append(\"\\n\");\n        });\n        this.source = builder.toString();\n    }\n\n    public StringResource(final Supplier<InputStream> sourceSupplier)\n    {\n        this(new InputStreamResource(sourceSupplier));\n    }\n\n    public StringResource(final String source)\n    {\n        this.source = source;\n    }\n\n    @Override\n    public long length()\n    {\n        if (this.source != null)\n        {\n            return this.source.length();\n        }\n        return super.length();\n    }\n\n    public StringResource withName(final String name)\n    {\n        this.setName(name);\n        return this;\n    }\n\n    public String writtenString()\n    {\n        return this.out.toString();\n    }\n\n    @Override\n    protected InputStream onRead()\n    {\n        return new StringInputStream(this.source != null ? this.source : writtenString());\n    }\n\n    @Override\n    protected OutputStream onWrite()\n    {\n        if (this.out == null)\n        {\n            this.out = new StringOutputStream();\n        }\n        return this.out;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/TemporaryFile.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.Closeable;\nimport java.nio.file.FileSystem;\nimport java.nio.file.Path;\n\n/**\n * A special type of {@link File} that automatically cleans up after itself. Please use\n * {@link File#temporary(FileSystem)}, {@link File#temporaryFolder(FileSystem)}, and the related\n * methods to obtain {@link TemporaryFile}s. You may take advantage of the automatic cleanup by\n * using it in a try-with-resources like so:\n *\n * <pre>\n * try (TemporaryFile directory = File.temporaryFolder(fileSystem))\n * {\n *     File foo = directory.child(\"foo\");\n *     // do something with foo\n * }\n * // the temporary directory is automatically deleted here\n * </pre>\n *\n * @author matthieun\n * @author lcram\n */\npublic class TemporaryFile extends File implements Closeable\n{\n    /**\n     * Construct a new {@link TemporaryFile} with the given {@link Path}. All parent directories\n     * will be automatically created.\n     *\n     * @param path\n     *            the path\n     */\n    TemporaryFile(final Path path)\n    {\n        super(path);\n    }\n\n    /**\n     * Clean up this {@link TemporaryFile}. If it is a regular file, simply delete it. If it is a\n     * directory, we will delete it and all its contents recursively.\n     */\n    @Override\n    public void close()\n    {\n        if (this.isDirectory())\n        {\n            this.deleteRecursively();\n        }\n        else\n        {\n            this.delete();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/WritableResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.BufferedOutputStream;\nimport java.io.BufferedWriter;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.StandardCharsets;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.NotifyingIOUtils.IOProgressListener;\nimport org.openstreetmap.atlas.streaming.writers.SafeBufferedWriter;\n\n/**\n * A resource that can be written to\n *\n * @author matthieun\n */\npublic interface WritableResource extends Resource\n{\n    /**\n     * Copy all the contents of another {@link Resource} to this {@link WritableResource}\n     *\n     * @param input\n     *            The input {@link Resource}\n     */\n    default void copyFrom(final Resource input)\n    {\n        input.copyTo(this);\n    }\n\n    /**\n     * Copy all the contents of another {@link Resource} to this {@link WritableResource} while\n     * notifying a progress listener\n     *\n     * @param input\n     *            The input {@link Resource}\n     * @param listener\n     *            The notification {@link IOProgressListener} called as data is being copied\n     */\n    default void copyFrom(final Resource input, final IOProgressListener listener)\n    {\n        input.copyTo(this, listener);\n    }\n\n    /**\n     * @return An {@link OutputStream} that streams data to this resource\n     */\n    OutputStream write();\n\n    /**\n     * Write to this resource and close it.\n     *\n     * @param value\n     *            The value to write.\n     */\n    default void writeAndClose(final byte[] value)\n    {\n        try (BufferedOutputStream output = new BufferedOutputStream(write()))\n        {\n            output.write(value);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not write to {}\", this, e);\n        }\n    }\n\n    /**\n     * Write to this resource and close it.\n     *\n     * @param value\n     *            The value to write.\n     */\n    default void writeAndClose(final String value)\n    {\n        try (BufferedWriter writer = writer())\n        {\n            writer.write(value);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not write to {}\", this, e);\n        }\n    }\n\n    /**\n     * @return A {@link BufferedWriter} on this resource.\n     */\n    default SafeBufferedWriter writer()\n    {\n        return new SafeBufferedWriter(new OutputStreamWriter(write(), StandardCharsets.UTF_8));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/WritableResourceCloseable.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\n/**\n * Use for WritableResources that have dependent AutoCloseable resources\n * \n * @author Taylor Smock\n */\npublic interface WritableResourceCloseable extends WritableResource, ResourceCloseable\n{\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/http/DeleteResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.http;\n\nimport java.net.URI;\n\nimport org.apache.http.client.methods.HttpDelete;\n\n/**\n * Same usage as found in {@link HttpResource}\n *\n * @author cuthbertm\n */\npublic class DeleteResource extends HttpResource\n{\n    public DeleteResource(final String uri)\n    {\n        this(URI.create(uri));\n    }\n\n    public DeleteResource(final URI uri)\n    {\n        super(uri);\n        setRequest(new HttpDelete(uri));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/http/GetResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.http;\n\nimport java.net.URI;\n\nimport org.apache.http.client.methods.HttpGet;\n\n/**\n * Same usage as found in {@link HttpResource}\n *\n * @author cuthbertm\n */\npublic class GetResource extends HttpResource\n{\n    public GetResource(final String uri)\n    {\n        this(URI.create(uri));\n    }\n\n    public GetResource(final URI uri)\n    {\n        super(uri);\n        setRequest(new HttpGet(uri));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/http/HttpResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.http;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.util.Optional;\n\nimport org.apache.http.Header;\nimport org.apache.http.HttpHost;\nimport org.apache.http.auth.AuthScope;\nimport org.apache.http.auth.UsernamePasswordCredentials;\nimport org.apache.http.client.AuthCache;\nimport org.apache.http.client.CredentialsProvider;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.HttpRequestBase;\nimport org.apache.http.client.protocol.HttpClientContext;\nimport org.apache.http.client.utils.HttpClientUtils;\nimport org.apache.http.impl.auth.BasicScheme;\nimport org.apache.http.impl.client.BasicAuthCache;\nimport org.apache.http.impl.client.BasicCredentialsProvider;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.HttpClientBuilder;\nimport org.apache.http.impl.client.HttpClients;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.StringInputStream;\nimport org.openstreetmap.atlas.streaming.resource.AbstractResource;\nimport org.openstreetmap.atlas.streaming.resource.ResourceCloseable;\n\n/**\n * Base Http resource object that will handle most of the http request information. Sub classes\n * generally will set the type of request and possibly a couple of request-specific parameters. For\n * instance POST will require to post body data in the request. Example Usage: URI uri = new\n * URIBuilder(\"http://localhost:2020/path/to/location\").build(); HttpResource post = new GetResource\n * // get t(uri, body); //read the response post.lines().foreach(System.out.println(x)); //get\n * status code int code = post.getStatusCode();\n *\n * @author cuthbertm\n */\npublic abstract class HttpResource extends AbstractResource implements ResourceCloseable\n{\n    private HttpRequestBase request;\n    private final URI uri;\n    private CloseableHttpResponse response = null;\n    private Optional<UsernamePasswordCredentials> creds = Optional.empty();\n    private Optional<HttpHost> proxy = Optional.empty();\n\n    private static HttpClientContext createBasicAuthCache(final HttpHost target,\n            final HttpClientContext context)\n    {\n        // Create AuthCache instance\n        final AuthCache authCache = new BasicAuthCache();\n        // Generate BASIC scheme object and add it to the local\n        // auth cache\n        final BasicScheme basicAuth = new BasicScheme();\n        authCache.put(target, basicAuth);\n        // Add AuthCache to the execution context\n        context.setAuthCache(authCache);\n        return context;\n    }\n\n    public HttpResource(final String uri)\n    {\n        this(URI.create(uri));\n    }\n\n    public HttpResource(final URI uri)\n    {\n        this.uri = uri;\n    }\n\n    @Override\n    public void close()\n    {\n        HttpClientUtils.closeQuietly(this.response);\n    }\n\n    /**\n     * If you want to execute the request, call this. All other attempts in an HttpResource will\n     * first check to see if the response object has been retrieved. This will null out the response\n     * object and execute it again.\n     */\n    public void execute()\n    {\n        this.response = null;\n        onRead();\n    }\n\n    public Header[] getHeader(final String headerKey)\n    {\n        // make sure that a connection attempt has been made\n        onRead();\n        return this.response.getHeaders(headerKey);\n    }\n\n    public HttpRequestBase getRequest()\n    {\n        return this.request;\n    }\n\n    public String getRequestBodyAsString()\n    {\n        // make sure that a connection attempt has been made\n        final StringBuilder builder = new StringBuilder();\n        lines().forEach(builder::append);\n        return builder.toString();\n    }\n    // ------------------------------------//\n\n    public CloseableHttpResponse getResponse()\n    {\n        // make sure that a connection attempt has been made\n        onRead();\n        return this.response;\n    }\n\n    // ---- HTTP Helper Functions ---------//\n    public int getStatusCode()\n    {\n        // make sure that a connection attempt has been made\n        onRead();\n        return this.response.getStatusLine().getStatusCode();\n    }\n\n    public URI getURI()\n    {\n        return this.uri;\n    }\n\n    public void setAuth(final String user, final String pass)\n    {\n        this.creds = Optional.of(new UsernamePasswordCredentials(user, pass));\n    }\n\n    public void setHeader(final String name, final String value)\n    {\n        this.request.setHeader(name, value);\n    }\n\n    public void setProxy(final HttpHost proxy)\n    {\n        this.proxy = Optional.ofNullable(proxy);\n    }\n\n    public void setRequest(final HttpRequestBase request)\n    {\n        this.request = request;\n    }\n\n    @Override\n    protected InputStream onRead()\n    {\n        try\n        {\n            if (this.response == null)\n            {\n                final HttpHost target = new HttpHost(this.uri.getHost(), this.uri.getPort(),\n                        this.uri.getScheme());\n                HttpClientContext context = HttpClientContext.create();\n                HttpClientBuilder clientBuilder = HttpClients.custom();\n                if (this.creds.isPresent())\n                {\n                    final CredentialsProvider credsProvider = new BasicCredentialsProvider();\n                    credsProvider.setCredentials(\n                            new AuthScope(target.getHostName(), target.getPort()),\n                            this.creds.get());\n                    clientBuilder = clientBuilder.setDefaultCredentialsProvider(credsProvider);\n                }\n                if (this.proxy.isPresent())\n                {\n                    clientBuilder = clientBuilder.setProxy(this.proxy.get());\n                }\n                final CloseableHttpClient client = clientBuilder.build();\n                context = createBasicAuthCache(target, context);\n                this.response = client.execute(target, this.request, context);\n            }\n            if (this.response.getEntity() == null)\n            {\n                return new StringInputStream(\"\");\n            }\n            return this.response.getEntity().getContent();\n        }\n        catch (final IOException ioe)\n        {\n            throw new CoreException(ioe.getMessage(), ioe);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/http/PostResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.http;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URI;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpHeaders;\nimport org.apache.http.client.methods.HttpEntityEnclosingRequestBase;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.entity.StringEntity;\n\n/**\n * Usage: byte[] body = \"{\\\"test\\\":\\\"test\\\"}\".getBytes(); URI uri = new\n * URIBuilder(\"http://localhost:2020/path/to/location\").build(); HttpResource post = new\n * PostResource(uri, body); //read the response post.lines().foreach(System.out.println(x)); // get\n * the status code int code = post.getStatusCode();\n *\n * @author cuthbertm\n */\npublic class PostResource extends HttpResource\n{\n    public PostResource(final String uri)\n    {\n        this(URI.create(uri));\n    }\n\n    public PostResource(final URI uri)\n    {\n        super(uri);\n        setRequest(new HttpPost(uri));\n    }\n\n    public void setEntity(final HttpEntity entity)\n    {\n        ((HttpEntityEnclosingRequestBase) getRequest()).setEntity(entity);\n    }\n\n    public void setStringBody(final String body, final ContentType contentType)\n            throws UnsupportedEncodingException\n    {\n        // using HttpEntityEnclosingRequestBase, so that resources like Put and Path can\n        // extend from it.\n        final HttpEntityEnclosingRequestBase base = (HttpEntityEnclosingRequestBase) getRequest();\n        base.addHeader(HttpHeaders.CONTENT_TYPE, contentType.getMimeType());\n        final StringEntity entity = new StringEntity(body, contentType);\n        base.setEntity(entity);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/http/PutResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.http;\n\nimport java.net.URI;\n\nimport org.apache.http.client.methods.HttpPut;\n\n/**\n * Same usage as {@link PostResource}\n *\n * @author cuthbertm\n */\npublic class PutResource extends PostResource\n{\n    public PutResource(final String uri)\n    {\n        this(URI.create(uri));\n    }\n\n    public PutResource(final URI uri)\n    {\n        super(uri);\n        setRequest(new HttpPut(uri));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/zip/ZipFileWritableResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.zip;\n\nimport java.io.BufferedInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * Zip wrapper for a {@link File} resource. This enables random lookups which are not available in\n * the case of a {@link ZipResource}\n *\n * @author matthieun\n */\npublic class ZipFileWritableResource extends ZipWritableResource\n{\n    public ZipFileWritableResource(final File source)\n    {\n        super(source);\n    }\n\n    @Override\n    public Iterable<Resource> entries()\n    {\n        try\n        {\n            final ZipInputStream iteratorStream = getZipInputStream();\n            ZipEntry currentZipEntry;\n            final List<Resource> resources = new ArrayList<>();\n            while ((currentZipEntry = iteratorStream.getNextEntry()) != null)\n            {\n                final String entryName = currentZipEntry.getName();\n                resources.add(new InputStreamResource(() ->\n                {\n                    final ZipInputStream zipStream = getZipInputStream();\n                    seekStreamAheadToEntry(zipStream, entryName);\n                    return zipStream;\n                }).withName(entryName));\n            }\n            return resources;\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Could not read entries for {}\",\n                    getFileSource().getAbsolutePathString(), exception);\n        }\n    }\n\n    public Resource entryForName(final String name)\n    {\n        return new InputStreamResource(() ->\n        {\n            final ZipInputStream zipStream = getZipInputStream();\n            seekStreamAheadToEntry(zipStream, name);\n            return zipStream;\n        }).withName(name);\n    }\n\n    @Override\n    public ZipFileWritableResource withWriteCompression(final boolean compression)\n    {\n        setWriteCompression(compression);\n        return this;\n    }\n\n    protected File getFileSource()\n    {\n        return (File) getWritableSource();\n    }\n\n    protected ZipInputStream getZipInputStream()\n    {\n        return new ZipInputStream(new BufferedInputStream(getFileSource().read()));\n    }\n\n    private void seekStreamAheadToEntry(final ZipInputStream streamToSeekThrough,\n            final String entryNameToSeek)\n    {\n        if (entryNameToSeek == null)\n        {\n            throw new CoreException(\"Cannot seek for null entry name\");\n        }\n\n        String currentEntryName = null;\n        while (!Objects.equals(currentEntryName, entryNameToSeek))\n        {\n            try\n            {\n                final ZipEntry currentEntry = streamToSeekThrough.getNextEntry();\n                if (currentEntry == null)\n                {\n                    break;\n                }\n                currentEntryName = currentEntry.getName();\n            }\n            catch (final IOException exception)\n            {\n                throw new CoreException(\"IOException while getting next entry\", exception);\n            }\n        }\n\n        /*\n         * If we make it here, then we didn't find the entry we were looking for - these aren't the\n         * entries you're looking for.\n         */\n        if (!Objects.equals(currentEntryName, entryNameToSeek))\n        {\n            throw new CoreException(\"No such entry {} found in stream\", entryNameToSeek);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/zip/ZipResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.zip;\n\nimport java.io.BufferedInputStream;\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.AbstractResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * Zipped {@link Resource} flavored wrapper using {@link ZipInputStream}\n *\n * @author matthieun\n */\npublic class ZipResource\n{\n    /**\n     * @author matthieun\n     */\n    public static class ZipIterator implements Iterator<Resource>, Closeable\n    {\n        private final Resource source;\n        private final ZipInputStream input;\n        private ZipEntry nextEntry = null;\n        private boolean doneReading = true;\n\n        public ZipIterator(final Resource source)\n        {\n            this.source = source;\n            this.input = new ZipInputStream(new BufferedInputStream(this.source.read()));\n        }\n\n        @Override\n        public void close()\n        {\n            Streams.close(this.input);\n        }\n\n        @Override\n        public boolean hasNext()\n        {\n            try\n            {\n                if (this.nextEntry == null)\n                {\n                    this.nextEntry = this.input.getNextEntry();\n                }\n                if (this.nextEntry == null)\n                {\n                    close();\n                }\n                return this.nextEntry != null;\n            }\n            catch (final IOException e)\n            {\n                throw new CoreException(\"Unable to go to next Zip Entry!\", e);\n            }\n        }\n\n        @Override\n        public Resource next()\n        {\n            if (!this.doneReading)\n            {\n                throw new CoreException(PREMATURE_READ_ERROR_MESSAGE);\n            }\n            if (hasNext())\n            {\n                this.doneReading = false;\n                final Resource result = new AbstractResource()\n                {\n                    private final String name = ZipIterator.this.nextEntry.getName();\n\n                    @Override\n                    public String getName()\n                    {\n                        return this.name;\n                    }\n\n                    @Override\n                    protected InputStream onRead()\n                    {\n                        return new InputStream()\n                        {\n                            @Override\n                            public void close()\n                            {\n                                // Trick to make sure the resource is read fully before moving\n                                // to the next one.\n                                ZipIterator.this.doneReading = true;\n                            }\n\n                            @Override\n                            public int read() throws IOException\n                            {\n                                return ZipIterator.this.input.read();\n                            }\n\n                            @Override\n                            public int read(final byte[] buffer, final int offset, final int length)\n                                    throws IOException\n                            {\n                                return ZipIterator.this.input.read(buffer, offset, length);\n                            }\n                        };\n                    }\n                };\n                this.nextEntry = null;\n                return result;\n            }\n            else\n            {\n                throw new NoSuchElementException();\n            }\n        }\n    }\n\n    public static final String PREMATURE_READ_ERROR_MESSAGE = \"Cannot go to the next ZipEntry before the previous one has been fully read.\";\n\n    private final Resource source;\n\n    public ZipResource(final Resource source)\n    {\n        this.source = source;\n    }\n\n    /**\n     * @return The entries of the file as an {@link Iterable} of {@link Resource}s. This works only\n     *         if each resource is sequentially read.\n     */\n    public Iterable<Resource> entries()\n    {\n        return () -> new ZipIterator(getSource());\n    }\n\n    public String getName()\n    {\n        return this.source.getName();\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getName();\n    }\n\n    protected Resource getSource()\n    {\n        return this.source;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/resource/zip/ZipWritableResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.zip;\n\nimport java.io.BufferedOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.zip.Deflater;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport org.apache.commons.compress.utils.IOUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Zipped {@link WritableResource} flavored wrapper using {@link ZipOutputStream}\n *\n * @author matthieun\n */\npublic class ZipWritableResource extends ZipResource\n{\n    private static final int ZIP_MAXIMUM_COMPRESSION_LEVEL = 9;\n\n    private boolean compression = true;\n\n    public ZipWritableResource(final WritableResource source)\n    {\n        super(source);\n    }\n\n    /**\n     * @param compression\n     *            True to compress the zip archive when writing.\n     */\n    public void setWriteCompression(final boolean compression)\n    {\n        this.compression = compression;\n    }\n\n    /**\n     * @param compression\n     *            True to compress the zip archive when writing.\n     * @return The same object\n     */\n    public ZipWritableResource withWriteCompression(final boolean compression)\n    {\n        setWriteCompression(compression);\n        return this;\n    }\n\n    /**\n     * Write a set of {@link Resource}s to the zip resource and close the stream.\n     *\n     * @param entries\n     *            The {@link Resource}s to write to the zip resource.\n     */\n    public void writeAndClose(final Iterable<? extends Resource> entries)\n    {\n        try (ZipOutputStream output = new ZipOutputStream(\n                new BufferedOutputStream(getWritableSource().write())))\n        {\n            output.setLevel(\n                    this.compression ? ZIP_MAXIMUM_COMPRESSION_LEVEL : Deflater.NO_COMPRESSION);\n            int counter = 0;\n            for (final Resource resource : entries)\n            {\n                String name = resource.getName();\n                if (name == null)\n                {\n                    name = \"Entry \" + counter;\n                }\n                final ZipEntry entry = new ZipEntry(name);\n                output.putNextEntry(entry);\n                try (InputStream input = resource.read())\n                {\n                    IOUtils.copy(input, output);\n                    counter++;\n                }\n                catch (final Exception e)\n                {\n                    throw new CoreException(\"Unable to read resource {}\", resource, e);\n                }\n            }\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Unable to write next ZipEntry!\", e);\n        }\n    }\n\n    /**\n     * Write a set of {@link Resource}s to the zip resource and close the stream.\n     *\n     * @param entries\n     *            The {@link Resource}s to write to the zip resource.\n     */\n    public void writeAndClose(final Resource... entries)\n    {\n        writeAndClose(Iterables.asList(entries));\n    }\n\n    protected WritableResource getWritableSource()\n    {\n        return (WritableResource) getSource();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/writers/JsonWriter.java",
    "content": "package org.openstreetmap.atlas.streaming.writers;\n\nimport java.io.BufferedWriter;\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.StandardCharsets;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Write Json objects\n *\n * @author matthieun\n */\npublic class JsonWriter implements Closeable\n{\n    private final BufferedWriter writer;\n\n    public JsonWriter(final WritableResource resource)\n    {\n        this.writer = new BufferedWriter(\n                new OutputStreamWriter(resource.write(), StandardCharsets.UTF_8));\n    }\n\n    @Override\n    public void close()\n    {\n        try\n        {\n            this.writer.close();\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Cannot close JsonWriter\", e);\n        }\n    }\n\n    public void flush()\n    {\n        try\n        {\n            this.writer.flush();\n        }\n        catch (final IOException e)\n        {\n            close();\n            throw new CoreException(\"Cannot flush JsonWriter\", e);\n        }\n    }\n\n    public void write(final JsonObject object)\n    {\n        final String value = object.toString();\n        try\n        {\n            this.writer.write(value);\n        }\n        catch (final IOException e)\n        {\n            close();\n            throw new CoreException(\"Could not write String to JsonWriter\", e);\n        }\n    }\n\n    public void writeLine(final JsonObject object)\n    {\n        final String value = object.toString();\n        writeLine(value);\n    }\n\n    private void writeLine(final String stringValue)\n    {\n        try\n        {\n            this.writer.write(stringValue);\n            this.writer.newLine();\n        }\n        catch (final IOException e)\n        {\n            close();\n            throw new CoreException(\"Could not write String to JsonWriter\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/streaming/writers/SafeBufferedWriter.java",
    "content": "package org.openstreetmap.atlas.streaming.writers;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.io.Writer;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic class SafeBufferedWriter extends BufferedWriter\n{\n\n    public SafeBufferedWriter(final Writer out)\n    {\n        super(out);\n    }\n\n    @Override\n    public void write(final String string)\n    {\n        try\n        {\n            super.write(string);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Could not write.\", e);\n        }\n    }\n\n    public void writeLine(final String line)\n    {\n        write(line + \"\\n\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AbandonedAerowayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM abandoned:aeroway tag\n *\n * @author cstaylor\n */\n@Tag(with = {\n        AerowayTag.class }, taginfo = \"http://taginfo.openstreetmap.org/keys/abandoned%3Aaeroway#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:abandoned:\")\npublic interface AbandonedAerowayTag\n{\n    @TagKey\n    String KEY = \"abandoned:aeroway\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AbandonedAmenityTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM abandoned:amenity tag\n *\n * @author cstaylor\n */\n@Tag(with = {\n        AmenityTag.class }, taginfo = \"http://taginfo.openstreetmap.org/keys/abandoned%3Aamenity#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:abandoned:\")\npublic interface AbandonedAmenityTag\n{\n    @TagKey\n    String KEY = \"abandoned:amenity\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AbandonedArtworkTypeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM abandoned:artwork_type tag\n *\n * @author cstaylor\n */\n@Tag(with = {\n        ArtworkTypeTag.class }, taginfo = \"http://taginfo.openstreetmap.org/keys/abandoned%3Aartwork_type#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:abandoned:\")\npublic interface AbandonedArtworkTypeTag\n{\n    @TagKey\n    String KEY = \"abandoned:artwork_type\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AccessTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM access tag\n *\n * @author robert_stack\n * @author matthieun\n * @author pmi\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/access#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:access\")\npublic enum AccessTag\n{\n    YES,\n    PRIVATE,\n    NO,\n    PERMISSIVE,\n    AGRICULTURAL,\n    USE_SIDEPATH,\n    DELIVERY,\n    DESIGNATED,\n    DISMOUNT,\n    DISCOURAGED,\n    FORESTRY,\n    DESTINATION,\n    CUSTOMERS,\n    PROHIBITED,\n    PUBLIC,\n    RESTRICTED,\n    UNKNOWN;\n\n    @TagKey\n    public static final String KEY = \"access\";\n\n    private static final EnumSet<AccessTag> PRIVATE_ACCESS = EnumSet.of(CUSTOMERS, NO, PRIVATE,\n            RESTRICTED, PROHIBITED);\n\n    public static boolean isNo(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, AccessTag.class, AccessTag.NO);\n    }\n\n    public static boolean isPrivate(final Taggable taggable)\n    {\n        final Optional<AccessTag> access = Validators.from(AccessTag.class, taggable);\n        return access.isPresent() && PRIVATE_ACCESS.contains(access.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressCityTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:city tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Acity#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressCityTag\n{\n    @TagKey\n    String KEY = \"addr:city\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressCountryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:country tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.ISO2_COUNTRY, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Acountry#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressCountryTag\n{\n    @TagKey\n    String KEY = \"addr:country\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressFlatsTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:flats tag\n *\n * @author mgostintsev\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/search?q=addr%3Aflats\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr:flats\")\npublic interface AddressFlatsTag\n{\n    @TagKey\n    String KEY = \"addr:flats\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressFullTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:full tag\n *\n * @author cstaylor\n */\n\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Afull#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressFullTag\n{\n    @TagKey\n    String KEY = \"addr:full\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressHousenameTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM addr:housename tag\n *\n * @author cstaylor\n */\n\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Ahousename#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressHousenameTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"addr:housename\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressHousenumberTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:housenumber tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Ahousenumber#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressHousenumberTag\n{\n    @TagKey\n    String KEY = \"addr:housenumber\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressInterpolationTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:interpolation tag\n *\n * @author pmi\n */\n\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Ainterpolation#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressInterpolationTag\n{\n    @TagKey\n    String KEY = \"addr:interpolation\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressPlaceTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:place tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Aplace#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressPlaceTag\n{\n    @TagKey\n    String KEY = \"addr:place\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressPostcodeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:postcode tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Apostcode#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressPostcodeTag\n{\n    @TagKey\n    String KEY = \"addr:postcode\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressProvinceTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:province tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Aprovince#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressProvinceTag\n{\n    @TagKey\n    String KEY = \"addr:province\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressStateTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM addr:state tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Astate#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressStateTag\n{\n    @TagKey\n    String KEY = \"addr:state\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AddressStreetTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM addr:street tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/addr%3Astreet#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:addr\")\npublic interface AddressStreetTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"addr:street\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AdministrativeLevelTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.OrdinalExtractor;\n\n/**\n * OSM admin_level tag. Works in conjunction with the {@link BoundaryTag} administrative value.\n *\n * @author matthieun\n */\n@Tag(value = Validation.ORDINAL, range = @Range(min = 1, max = 11), taginfo = \"https://taginfo.openstreetmap.org/keys/admin_level#values\", osm = \"http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#10_admin_level_values_for_specific_countries\")\npublic interface AdministrativeLevelTag\n{\n    @TagKey\n    String KEY = \"admin_level\";\n\n    /**\n     * Validate the tag and return the Administrative Level tag as an integer between 1 and 11.\n     *\n     * @param taggable\n     *            The object to parse\n     * @return the Administrative Level tag as an integer between 1 and 11, if validated.\n     */\n    static Optional<Integer> getAdministrativeLevel(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            final OrdinalExtractor extractor = new OrdinalExtractor();\n            return extractor.validateAndExtract(tagValue.get(),\n                    AdministrativeLevelTag.class.getDeclaredAnnotation(Tag.class));\n        }\n\n        return Optional.empty();\n    }\n\n    static long maximumAdministrativeLevelValue()\n    {\n        final Tag tag = AdministrativeLevelTag.class.getDeclaredAnnotation(Tag.class);\n        final Range range = tag.range();\n        return range.max();\n    }\n\n    static long minimumAdministrativeLevelValue()\n    {\n        final Tag tag = AdministrativeLevelTag.class.getDeclaredAnnotation(Tag.class);\n        final Range range = tag.range();\n        return range.min();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AerialWayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM aerialway tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/aerialway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:aerialway\")\npublic enum AerialWayTag\n{\n    CABLE_CAR,\n    GONDOLA,\n    CHAIR_LIFT,\n    MIXED_LIFT,\n    DRAG_LIFT,\n    T_BAR,\n    J_BAR,\n    PLATTER,\n    ROPE_TOW,\n    MAGIC_CARPET,\n    ZIP_LINE,\n    PYLON,\n    STATION;\n\n    @TagKey\n    public static final String KEY = \"aerialway\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AerowayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM aeroway tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/aeroway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Aeroways\")\npublic enum AerowayTag\n{\n    TAXIWAY,\n    AERODROME,\n    RUNWAY,\n    HELIPAD,\n    APRON,\n    HANGAR,\n    GATE,\n    PARKING_POSITION,\n    TERMINAL,\n    HOLDING_POSITION,\n    WINDSOCK,\n    NAVIGATIONAID,\n    MARKING;\n\n    @TagKey\n    public static final String KEY = \"aeroway\";\n\n    public static Optional<AerowayTag> get(final Taggable taggable)\n    {\n        return Validators.from(AerowayTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AmenityTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM amenity tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/amenity#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:amenity\")\npublic enum AmenityTag\n{\n    PARKING,\n    PARKING_ENTRANCE,\n    MOTORCYCLE_PARKING,\n    PLACE_OF_WORSHIP,\n    SCHOOL,\n    BENCH,\n    RESTAURANT,\n    FUEL,\n    CAFE,\n    FAST_FOOD,\n    BANK,\n    POST_BOX,\n    GRAVE_YARD,\n    KINDERGARTEN,\n    RECYCLING,\n    PHARMACY,\n    WASTE_BASKET,\n    BICYCLE_PARKING,\n    TOILETS,\n    HOSPITAL,\n    SHELTER,\n    POST_OFFICE,\n    PUB,\n    DRINKING_WATER,\n    PUBLIC_BUILDING,\n    TELEPHONE,\n    ATM,\n    BAR,\n    POLICE,\n    FIRE_STATION,\n    TOWNHALL,\n    HUNTING_STAND,\n    PARKING_SPACE,\n    VENDING_MACHINE,\n    FOUNTAIN,\n    LIBRARY,\n    DOCTORS,\n    SWIMMING_POOL,\n    SOCIAL_FACILITY,\n    UNIVERSITY,\n    BICYCLE_RENTAL,\n    EMERGENCY_PHONE,\n    WASTE_DISPOSAL,\n    FESTIVAL_GROUNDS,\n    COLLEGE,\n    COMMUNITY_CENTRE,\n    COMMUNITY_CENTER,\n    MARKETPLACE,\n    FERRY_TERMINAL;\n\n    @TagKey\n    public static final String KEY = \"amenity\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AreaTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM area tag\n *\n * @author ihillberg\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/area#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:area\")\npublic enum AreaTag\n{\n    YES,\n    NO;\n\n    @TagKey\n    public static final String KEY = \"area\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ArtworkTypeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM artwork_type tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/artwork_type#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:artwork_type\")\npublic enum ArtworkTypeTag\n{\n    SCULPTURE,\n    STATUE,\n    MURAL,\n    ARCHITECTURE,\n    STONE,\n    PAINTING,\n    BUST,\n    INSTALLATION,\n    MOSAIC;\n\n    @TagKey\n    public static final String KEY = \"artwork_type\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/AtlasTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Atlas created tags\n *\n * @author tony\n */\npublic final class AtlasTag\n{\n    public static final Set<String> TAGS_FROM_OSM = Collections.unmodifiableSet(\n            new HashSet<>(Arrays.asList(LastEditTimeTag.KEY, LastEditUserIdentifierTag.KEY,\n                    LastEditUserNameTag.KEY, LastEditVersionTag.KEY, LastEditChangesetTag.KEY)));\n\n    public static final Set<String> TAGS_FROM_ATLAS = Collections.unmodifiableSet(\n            new HashSet<>(Arrays.asList(ISOCountryTag.KEY, SyntheticBoundaryNodeTag.KEY,\n                    SyntheticDuplicateOsmNodeTag.KEY, SyntheticGeometrySlicedTag.KEY,\n                    SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY,\n                    SyntheticInvalidWaySectionTag.KEY, SyntheticRelationMemberAdded.KEY,\n                    SyntheticRelationRoleUpdated.KEY, SyntheticSyntheticRelationMemberTag.KEY,\n                    SyntheticInvalidGeometryTag.KEY)));\n\n    private AtlasTag()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BarrierTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM barrier tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/barrier#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:barrier\")\npublic enum BarrierTag\n{\n    FENCE,\n    WALL,\n    GATE,\n    HEDGE,\n    BOLLARD,\n    LIFT_GATE,\n    RETAINING_WALL,\n    STILE,\n    CYCLE_BARRIER,\n    KERB,\n    YES,\n    ENTRANCE,\n    BLOCK,\n    TOLL_BOOTH,\n    CATTLE_GRID,\n    DITCH,\n    KISSING_GATE,\n    CITY_WALL,\n    GUARD_RAIL,\n    HEDGE_BANK,\n    WIRE_FENCE,\n    LINE,\n    SWING_GATE,\n    CHAIN,\n    TURNSTILE,\n    EMBANKMENT,\n    FIELD_BOUNDARY,\n    BORDER_CONTROL,\n    SALLY_PORT,\n    DOOR,\n    HAMPSHIRE_GATE,\n    WOOD_FENCE,\n    BUMP_GATE,\n    BUS_TRAP,\n    JERSEY_BARRIER,\n    SLIDING_GATE,\n    HEIGHT_RESTRICTOR,\n    HANDRAIL,\n    LOG,\n    AVALANCHE_PROTECTION,\n    MEDIAN_STRIP,\n    DEBRIS,\n    TRAFFIC_ISLAND,\n    WICKET_GATE,\n    ROPE,\n    @TagValueAs(\"full-height_turnstile\")\n    FULL_HEIGHT_TURNSTILE,\n    PLANTER,\n    PROPERTY_BOUNDARY,\n    NO,\n    CABLE_BARRIER,\n    RAILING,\n    SUMP_BUSTER,\n    @TagValueAs(\"sump-buster\")\n    SUMPBUSTER;\n\n    @TagKey\n    public static final String KEY = \"barrier\";\n\n    private static final EnumSet<BarrierTag> BARRIERS = EnumSet.of(FENCE, WALL, GATE, HEDGE,\n            BOLLARD, LIFT_GATE, RETAINING_WALL, STILE, CYCLE_BARRIER, KERB, YES, ENTRANCE, BLOCK,\n            TOLL_BOOTH, CATTLE_GRID, DITCH, KISSING_GATE, CITY_WALL, GUARD_RAIL, HEDGE_BANK,\n            WIRE_FENCE, LINE, SWING_GATE, CHAIN, TURNSTILE, EMBANKMENT, FIELD_BOUNDARY,\n            BORDER_CONTROL, SALLY_PORT, DOOR, HAMPSHIRE_GATE, WOOD_FENCE, BUMP_GATE, BUS_TRAP,\n            JERSEY_BARRIER, SLIDING_GATE, HEIGHT_RESTRICTOR, HANDRAIL, LOG, AVALANCHE_PROTECTION,\n            MEDIAN_STRIP, DEBRIS, TRAFFIC_ISLAND, WICKET_GATE, ROPE, FULL_HEIGHT_TURNSTILE, PLANTER,\n            PROPERTY_BOUNDARY, NO, CABLE_BARRIER, RAILING, SUMP_BUSTER, SUMPBUSTER);\n\n    public static boolean isBarrier(final Taggable taggable)\n    {\n        final Optional<BarrierTag> barrier = Validators.from(BarrierTag.class, taggable);\n        return barrier.isPresent() && BARRIERS.contains(barrier.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BicycleTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM bicycle tag. Also see http://wiki.openstreetmap.org/wiki/Bicycle#Bicycle_Restrictions for\n * further tagging detail.\n *\n * @author robert_stack\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/bicycle#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:bicycle\")\npublic enum BicycleTag\n{\n    YES,\n    DESIGNATED,\n    USE_SIDEPATH,\n    NO,\n    PERMISSIVE,\n    DESTINATION,\n    DISMOUNT,\n    PRIVATE,\n    OFFICIAL,\n    UNKNOWN;\n\n    @TagKey\n    public static final String KEY = \"bicycle\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BoundaryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM Boundary Tag. Also works with the {@link AdministrativeLevelTag}.\n *\n * @author matthieun\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/boundary#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:boundary\")\npublic enum BoundaryTag\n{\n    ADMINISTRATIVE,\n    HISTORIC,\n    MARITIME,\n    NATIONAL_PARK,\n    POLITICAL,\n    POSTAL_CODE,\n    RELIGIOUS_ADMINISTRATION,\n    PROTECTED_AREA;\n\n    @TagKey\n    public static final String KEY = \"boundary\";\n\n    public static boolean isAdministrative(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, BoundaryTag.class, ADMINISTRATIVE);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BrandTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM brand tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/brand#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:brand\")\npublic interface BrandTag\n{\n    @TagKey\n    String KEY = \"brand\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BreakfastTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM breakfast tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/breakfast#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:breakfast\")\npublic enum BreakfastTag\n{\n    YES,\n    BUFFET,\n    NO,\n    FREE;\n\n    @TagKey\n    public static final String KEY = \"breakfast\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BridgeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM bridge tag\n *\n * @author cstaylor\n * @author pmi\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/bridge#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:bridge\")\npublic enum BridgeTag\n{\n    YES,\n    VIADUCT,\n    NO,\n    AQUEDUCT,\n    BOARDWALK,\n    MOVABLE,\n    SUSPENSION,\n    CULVERT,\n    ABANDONED,\n    LOW_WATER_CROSSING,\n    SIMPLE_BRUNNEL,\n    COVERED;\n\n    // Left out bridge=no and bridge=simple_brunnel\n    private static final EnumSet<BridgeTag> BRIDGE_WAYS = EnumSet.of(YES, VIADUCT, AQUEDUCT,\n            BOARDWALK, MOVABLE, SUSPENSION, CULVERT, ABANDONED, LOW_WATER_CROSSING, SIMPLE_BRUNNEL,\n            COVERED);\n\n    @TagKey\n    public static final String KEY = \"bridge\";\n\n    public static Optional<BridgeTag> get(final Taggable taggable)\n    {\n        return Validators.from(BridgeTag.class, taggable);\n    }\n\n    public static boolean isBridge(final Taggable taggable)\n    {\n        final Optional<BridgeTag> bridge = get(taggable);\n        return bridge.isPresent() && BRIDGE_WAYS.contains(bridge.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BuildingHeightTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.AltitudeExtractor;\n\n/**\n * OSM building:height tag: https://taginfo.openstreetmap.org/keys/building%3Aheight#values. OSM\n * Wiki indicates that height is the standard key, but taginfo usage suggest prevalent use of\n * building:height as well\n *\n * @author isabellehillberg\n * @author bbreithaupt\n */\n@Tag(value = Validation.LENGTH, taginfo = \"https://taginfo.openstreetmap.org/keys/building%3Aheight#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:height\")\npublic interface BuildingHeightTag\n{\n    @TagKey\n    String KEY = \"building:height\";\n\n    static Optional<Altitude> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return AltitudeExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BuildingLevelsTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.DoubleValidator;\n\n/**\n * OSM building:levels tag\n *\n * @author robert_stack\n */\n@Tag(value = Validation.ORDINAL, taginfo = \"http://taginfo.openstreetmap.org/keys/building:levels#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:building:levels\")\npublic interface BuildingLevelsTag\n{\n    @TagKey\n    String KEY = \"building:levels\";\n\n    DoubleValidator validator = new DoubleValidator();\n\n    static Optional<Double> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent() && validator.isValid(tagValue.get()))\n        {\n            return Optional.of(Double.valueOf(tagValue.get()));\n        }\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BuildingMinLevelTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.DoubleValidator;\n\n/**\n * OSM building:min_level tag\n *\n * @author ajayaswal\n */\n@Tag(value = Validation.ORDINAL, taginfo = \"http://taginfo.openstreetmap.org/keys/building:min_level#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:building:min_level\")\npublic interface BuildingMinLevelTag\n{\n    @TagKey\n    String KEY = \"building:min_level\";\n\n    DoubleValidator validator = new DoubleValidator();\n\n    static Optional<Double> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n\n        if (tagValue.isPresent() && validator.isValid(tagValue.get()))\n        {\n            return Optional.of(Double.valueOf(tagValue.get()));\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BuildingPartTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM building:part tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/building%3Apart#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:building:part\")\npublic enum BuildingPartTag\n{\n    YES,\n    ROOF,\n    APARTMENTS,\n    COLUMN,\n    NO,\n    COMMERCIAL,\n    BASE,\n    STEPS,\n    RESIDENTIAL,\n    GARAGE,\n    HOUSE,\n    RETAIL,\n    SCHOOL,\n    ROOT,\n    INDUSTRIAL,\n    DEFAULT,\n    STILOBATE,\n    OFFICE,\n    SUPERSTRUCTURE,\n    RUINS,\n    OUTLINE,\n    UNIVERSITY;\n\n    @TagKey\n    public static final String KEY = \"building:part\";\n\n    public String getTagValue()\n    {\n        return name().toLowerCase().intern();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BuildingRoofTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\n\n/**\n * OSM building:roof tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/building%3Aroof#values\", osm = \"http://wiki.openstreetmap.org/wiki/Proposed_features/Building_attributes\")\npublic enum BuildingRoofTag\n{\n    TILE,\n    TRADITIONAL,\n    FLAT,\n    CONCRETE,\n    PERMANENT,\n    TIN,\n    METAL,\n    TILES,\n    SLATE,\n    @TagValueAs(\"semi-permanent\")\n    SEMI_PERMANENT,\n    ASBESTOS,\n    PITCHED,\n    IRONSHEETS,\n    HIPPED,\n    GABLED;\n\n    @TagKey\n    public static final String KEY = \"building:roof\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/BuildingTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM building tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/building#values\", osm = \"http://wiki.openstreetmap.org/wiki/Buildings\")\npublic enum BuildingTag\n{\n    YES,\n    RESIDENTIAL,\n    COMMERCIAL,\n    SHOP,\n    HOUSE,\n    GARAGE,\n    APARTMENTS,\n    HUT,\n    INDUSTRIAL,\n    DETACHED,\n    ROOF,\n    SHED,\n    TERRACE,\n    SCHOOL,\n    RETAIL,\n    FARM_AUXILIARY,\n    CHURCH,\n    BARN,\n    CONSTRUCTION,\n    GREENHOUSE,\n    SERVICE,\n    MANUFACTURE,\n    CABIN,\n    FARM,\n    WAREHOUSE,\n    CIVIC,\n    COLLAPSED,\n    OFFICE,\n    NO,\n    UNIVERSITY,\n    HOTEL,\n    DORMITORY,\n    BUNGALOW,\n    CHAPEL,\n    MOSQUE,\n    KINDERGARTEN,\n    HOSPITAL,\n    STADIUM,\n    TRAIN_STATION,\n    TRANSPORTATION,\n    PUBLIC,\n    BUNKER,\n    GARAGES,\n    HANGAR,\n    STABLE,\n    TRANSFORMER_TOWER,\n    RUINS,\n    ENTRANCE,\n    FACTORY,\n    STORAGE_TANK,\n    PAVILION,\n    STORE,\n    KIOSK,\n    COWSHED,\n    COLLEGE,\n    SUPERMARKET,\n    TANK,\n    ADMINISTRATIVE,\n    ABANDONED,\n    SEMIDETACHED_HOUSE,\n    CATHEDRAL,\n    TEMPLE,\n    SHELTER,\n    POLICLINIC,\n    GREENHOUSE_HORTICULTURE,\n    STANDS,\n    TOWER,\n    KITCHEN,\n    SILO,\n    PARKING,\n    OFFICES,\n    STATION,\n    SHACK,\n    UNCLASSIFIED,\n    GLASSHOUSE,\n    GAZEBO,\n    POLICE,\n    COTTAGE,\n    CASTLE_WALL,\n    COVER,\n    CELLAR,\n    HEAT_STATION,\n    CLINIC,\n    PART,\n    MILITARY,\n    GRANDSTAND,\n    UNDEFINED,\n    CASTLE_TOWER,\n    SHEDS,\n    SPORT,\n    HOME,\n    SAUNA,\n    DISUSED,\n    TRIBUNE,\n    POWER,\n    BANK,\n    ELEVATOR,\n    PUBLIC_BUILDING,\n    WATER_TOWER,\n    MALL,\n    MUSEUM,\n    FIRE_STATION,\n    MODEL,\n    TOILETS,\n    RAILWAY_STATION,\n    FUEL,\n    BRIDGE,\n    THEATRE,\n    CAFE,\n    DAMAGED,\n    STAND,\n    ALLOTMENT_HOUSE,\n    GOVERNMENT,\n    SPORTS_CENTRE;\n\n    private static final EnumSet<BuildingTag> VALID_BUILDINGS = EnumSet.of(YES, RESIDENTIAL,\n            COMMERCIAL, SHOP, HOUSE, GARAGE, APARTMENTS, HUT, INDUSTRIAL, DETACHED, ROOF, SHED,\n            TERRACE, SCHOOL, RETAIL, FARM_AUXILIARY, CHURCH, BARN, CONSTRUCTION, GREENHOUSE,\n            SERVICE, MANUFACTURE, CABIN, FARM, WAREHOUSE, CIVIC, COLLAPSED, OFFICE, UNIVERSITY,\n            HOTEL, DORMITORY, BUNGALOW, CHAPEL, MOSQUE, KINDERGARTEN, HOSPITAL, STADIUM,\n            TRAIN_STATION, TRANSPORTATION, PUBLIC, BUNKER, GARAGES, HANGAR, STABLE,\n            TRANSFORMER_TOWER, RUINS, FACTORY, STORAGE_TANK, PAVILION, STORE, KIOSK, COWSHED,\n            COLLEGE, SUPERMARKET, TANK, ADMINISTRATIVE, ABANDONED, SEMIDETACHED_HOUSE, CATHEDRAL,\n            TEMPLE, SHELTER, POLICLINIC, GREENHOUSE_HORTICULTURE, STANDS, TOWER, KITCHEN, SILO,\n            PARKING, OFFICES, STATION, SHACK, UNCLASSIFIED, GLASSHOUSE, GAZEBO, POLICE, COTTAGE,\n            CASTLE_WALL, COVER, CELLAR, HEAT_STATION, CLINIC, PART, MILITARY, GRANDSTAND, UNDEFINED,\n            CASTLE_TOWER, SHEDS, SPORT, HOME, SAUNA, DISUSED, TRIBUNE, POWER, BANK, ELEVATOR,\n            PUBLIC_BUILDING, WATER_TOWER, MALL, MUSEUM, FIRE_STATION, MODEL, TOILETS,\n            RAILWAY_STATION, FUEL, BRIDGE, THEATRE, CAFE, DAMAGED, STAND, ALLOTMENT_HOUSE,\n            GOVERNMENT, SPORTS_CENTRE);\n\n    @TagKey\n    public static final String KEY = \"building\";\n\n    public static final String BUILDING_ROLE_OUTLINE = \"outline\";\n\n    public static final String BUILDING_ROLE_PART = \"part\";\n\n    public static boolean isBuilding(final String value)\n    {\n        return isBuilding(taggable -> Optional.of(value));\n    }\n\n    public static boolean isBuilding(final Taggable taggable)\n    {\n        final Optional<BuildingTag> building = Validators.from(BuildingTag.class, taggable);\n        return building.isPresent() && VALID_BUILDINGS.contains(building.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/CheckDateTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM check_date tag\n *\n * @author brianjor\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/check_date#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:check_date\")\npublic interface CheckDateTag\n{\n    @TagKey\n    String KEY = \"check_date\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ConstructionDateTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM construction:date tag\n *\n * @author brianjor\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/construction:date#values\", osm = \"https://wiki.openstreetmap.org/wiki/Item:Q9553\")\npublic interface ConstructionDateTag\n{\n    @TagKey\n    String KEY = \"construction:date\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ConstructionTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM Construction tag. Inherits values from Bridge, Highway, LandUse and Railway tags.\n *\n * @author bbreithaupt\n */\n@Tag(with = { BuildingTag.class, HighwayTag.class, LandUseTag.class,\n        RailwayTag.class }, taginfo = \"https://taginfo.openstreetmap.org/keys/construction\", osm = \"https://wiki.openstreetmap.org/wiki/Key:construction\")\n@SuppressWarnings(\"squid:S1214\")\npublic interface ConstructionTag\n{\n    @TagKey\n    String KEY = \"construction\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactDiasporaTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:diaspora tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Adiaspora#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactDiasporaTag\n{\n    @TagKey\n    String KEY = \"contact:diaspora\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactEmailTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:email tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Aemail#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactEmailTag\n{\n    @TagKey\n    String KEY = \"contact:email\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactFacebookTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:facebook tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Afacebook#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactFacebookTag\n{\n    @TagKey\n    String KEY = \"contact:facebook\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactFaxTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:fax tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Afax#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactFaxTag\n{\n    @TagKey\n    String KEY = \"contact:fax\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactGooglePlusTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:google_plug tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Agoogle_plus#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactGooglePlusTag\n{\n    @TagKey\n    String KEY = \"contact:google_plus\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactInstagramTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:instagram tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Ainstagram#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactInstagramTag\n{\n    @TagKey\n    String KEY = \"contact:instagram\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactLinkedInTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:linkedin tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Alinkedin#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactLinkedInTag\n{\n    @TagKey\n    String KEY = \"contact:linkedin\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactMobileTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:mobile tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Amobile#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactMobileTag\n{\n    @TagKey\n    String KEY = \"contact:mobile\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactPhoneTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:phone tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Aphone#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactPhoneTag\n{\n    @TagKey\n    String KEY = \"contact:phone\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactTwitterTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:twitter tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Atwitter#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactTwitterTag\n{\n    @TagKey\n    String KEY = \"contact:twitter\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactWebsiteTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:website tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.URI, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Awebsite#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactWebsiteTag\n{\n    @TagKey\n    String KEY = \"contact:website\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ContactXingTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM contact:xing tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/contact%3Axing#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:contact\")\npublic interface ContactXingTag\n{\n    @TagKey\n    String KEY = \"contact:xing\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/CoveredTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM Covered Tag\n *\n * @author cuthbertm\n */\n@Tag(value = Tag.Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/covered\", osm = \"http://wiki.openstreetmap.org/wiki/Key:covered\")\npublic enum CoveredTag\n{\n    YES,\n    NO,\n    ARCADE,\n    COLONNADE;\n\n    @TagKey\n    public static final String KEY = \"covered\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/CuisineTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM cuisine tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/cuisine#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:cuisine\")\npublic interface CuisineTag\n{\n    @TagKey\n    String KEY = \"cuisine\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/CyclewayLaneTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM cycleway lane tag\n *\n * @author james_gage\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/cycleway%3Alane#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:cycleway:lane\")\npublic enum CyclewayLaneTag\n{\n    ADVISORY,\n    EXCLUSIVE;\n\n    @TagKey\n    public static final String KEY = \"cycleway:lane\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/CyclewayLeftTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM cycleway left tag\n *\n * @author james_gage\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/cycleway%3Aleft#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:cycleway:left\")\npublic enum CyclewayLeftTag\n{\n    LANE,\n    TRACK,\n    OPPOSITE_LANE,\n    OPPOSITE_SHARE_BUSWAY,\n    SHARED_LANE,\n    SHARED,\n    SHARED_BUSWAY;\n\n    @TagKey\n    public static final String KEY = \"cycleway:left\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/CyclewayRightTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM cycleway right tag\n *\n * @author james_gage\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/cycleway%3Aright#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:cycleway:right\")\npublic enum CyclewayRightTag\n{\n    LANE,\n    TRACK,\n    OPPOSITE_LANE,\n    OPPOSITE_SHARE_BUSWAY,\n    SHARED_LANE,\n    SHARED,\n    SHARED_BUSWAY;\n\n    @TagKey\n    public static final String KEY = \"cycleway:right\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/CyclewayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM cycleway tag\n *\n * @author robert_stack\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/cycleway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:cycleway\")\npublic enum CyclewayTag\n{\n    LANE,\n    OPPOSITE_LANE,\n    OPPOSITE,\n    SHARED_LANE,\n    SHARE_BUSWAY,\n    SHARED,\n    TRACK,\n    OPPOSITE_TRACK,\n    ASL,\n    SHOULDER,\n    NO,\n    YES,\n    LEFT,\n    RIGHT,\n    OPPOSITE_SHARE_BUSWAY,\n    SEGREGATED,\n    NONE,\n    SEPARATE;\n\n    @TagKey\n    public static final String KEY = \"cycleway\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DestinationForwardTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM destination:forward tag\n *\n * @author alexhsieh\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/destination%3Aforward\")\npublic interface DestinationForwardTag\n{\n    @TagKey\n    String KEY = \"destination:forward\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DestinationIntRefTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM destination:int_ref tag\n *\n * @author sbhalekar\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/destination%3Aint_ref\")\npublic interface DestinationIntRefTag\n{\n    @TagKey\n    String KEY = \"destination:int_ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DestinationRefTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM destination:ref tag\n *\n * @author alexhsieh\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/destination%3Aref\")\npublic interface DestinationRefTag\n{\n    @TagKey\n    String KEY = \"destination:ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DestinationRefToTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM destination:ref:to tag\n *\n * @author sbhalekar\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/destination%3Aref%3Ato\")\npublic interface DestinationRefToTag\n{\n    @TagKey\n    String KEY = \"destination:ref:to\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DestinationStreetTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM destination:street tag\n *\n * @author sbhalekar\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/destination%3Astreet\")\npublic interface DestinationStreetTag\n{\n    @TagKey\n    String KEY = \"destination:street\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DestinationTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM destination tag\n *\n * @author alexhsieh\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/destination\")\npublic interface DestinationTag\n{\n    @TagKey\n    String KEY = \"destination\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DirectionTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM direction tag\n *\n * @author MonicaBrandeis\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/direction\", osm = \"https://wiki.openstreetmap.org/wiki/Key:direction\")\npublic enum DirectionTag\n{\n    FORWARD,\n    BACKWARD,\n    CLOCKWISE,\n    ANTICLOCKWISE,\n    BOTH;\n\n    @TagKey\n    public static final String KEY = \"direction\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DisusedRailwayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM disused: Railway tag\n *\n * @author Vladimir Lemberg\n */\n@Tag(with = {\n        ShopTag.class }, taginfo = \"https://taginfo.openstreetmap.org/keys/disused%3Arailway#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:disused:railway\")\npublic enum DisusedRailwayTag\n{\n    YES,\n    RAIL,\n    LIGHT_RAIL,\n    CROSSING,\n    LEVEL_CROSSING,\n    STATION,\n    HALT,\n    PLATFORM,\n    TRAM;\n\n    @TagKey\n    public static final String KEY = \"disused:railway\";\n\n    private static final EnumSet<DisusedRailwayTag> DISUSED_RAILWAY_CROSSINGS = EnumSet.of(CROSSING,\n            LEVEL_CROSSING);\n\n    public static Optional<DisusedRailwayTag> get(final Taggable taggable)\n    {\n        return Validators.from(DisusedRailwayTag.class, taggable);\n    }\n\n    public static boolean isDisusedRailwayCrossing(final Taggable taggable)\n    {\n        final Optional<DisusedRailwayTag> disusedRailway = get(taggable);\n        return disusedRailway.isPresent()\n                && DISUSED_RAILWAY_CROSSINGS.contains(disusedRailway.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/DisusedShopTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM disused:shop tag\n * <p>\n * Note how we're using the with optional parameter to Tag. This lets us bring in all of the values\n * defined in the ShopTag enum\n *\n * @author cstaylor\n */\n@Tag(with = {\n        ShopTag.class }, taginfo = \"http://taginfo.openstreetmap.org/keys/disused%3Ashop#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:disused:\")\npublic interface DisusedShopTag\n{\n    @TagKey\n    String KEY = \"disused:shop\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ElevationTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM elevation tag. This tag is used to express height above sea level of a point in meters.\n *\n * @author mgostintsev\n */\n@Tag(value = Validation.DOUBLE, taginfo = \"https://taginfo.openstreetmap.org/keys/ele#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:ele\")\npublic interface ElevationTag\n{\n    @TagKey\n    String KEY = \"ele\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/EmbankmentTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM embankment tag\n *\n * @author mkalender\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/embankment#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:embankment\")\npublic enum EmbankmentTag\n{\n    YES,\n    NO;\n\n    @TagKey\n    public static final String KEY = \"embankment\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/EntranceTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM entrance tag\n *\n * @author savannahostrowski\n */\n@Tag(taginfo = \"https://wiki.openstreetmap.org/wiki/Key:entrance#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:entrance\")\npublic enum EntranceTag\n{\n    EMERGENCY,\n    EXIT,\n    HOME,\n    MAIN,\n    SERVICE,\n    STAIRCASE,\n    YES;\n\n    @TagKey\n    public static final String KEY = \"entrance\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/EstimatedWidthTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.LengthExtractor;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * OSM estimated width tag\n *\n * @author bbreithaupt\n */\n@Tag(value = Validation.DOUBLE, range = @Range(min = 0, max = Integer.MAX_VALUE), taginfo = \"https://taginfo.openstreetmap.org/keys/est_width\", osm = \"https://wiki.openstreetmap.org/wiki/Key:est_width\")\npublic interface EstimatedWidthTag\n{\n    @TagKey\n    String KEY = \"est_width\";\n\n    static Optional<Distance> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return LengthExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ExitToLeftTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM exit_to:left tag\n *\n * @author alexhsieh\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/exit_to%3Aleft\")\npublic interface ExitToLeftTag\n{\n    @TagKey\n    String KEY = \"exit_to:left\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ExitToRightTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM exit_to:right tag\n *\n * @author alexhsieh\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/exit_to%3Aright\")\npublic interface ExitToRightTag\n{\n    @TagKey\n    String KEY = \"exit_to:right\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ExitToTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM exit_to tag\n *\n * @author alexhsieh\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/exit_to\")\npublic interface ExitToTag\n{\n    @TagKey\n    String KEY = \"exit_to\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/FaxTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM fax tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/fax#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:phone\")\npublic interface FaxTag\n{\n    @TagKey\n    String KEY = \"fax\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/FerryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM Ferry Tag\n *\n * @author isabellehillberg\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/ferry#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:ferry\")\npublic enum FerryTag\n{\n    YES,\n    NO,\n    MOTORWAY,\n    TRUNK,\n    PRIMARY,\n    SECONDARY,\n    TERTIARY,\n    UNCLASSIFIED,\n    RESIDENTIAL,\n    SERVICE,\n    FOOTWAY,\n    PEDESTRIAN,\n    TRACK;\n\n    @TagKey\n    public static final String KEY = \"ferry\";\n\n    private static final EnumSet<FerryTag> CAR_NAVIGABLE = EnumSet.of(MOTORWAY, TRUNK, PRIMARY,\n            SECONDARY, TERTIARY, UNCLASSIFIED, RESIDENTIAL, SERVICE);\n    private static final EnumSet<FerryTag> PEDESTRIAN_NAVIGABLE = EnumSet.of(FOOTWAY, TRACK,\n            PEDESTRIAN);\n\n    public static boolean isCarNavigableFerry(final Taggable taggable)\n    {\n        final Optional<FerryTag> ferry = Validators.from(FerryTag.class, taggable);\n        return ferry.isPresent() && CAR_NAVIGABLE.contains(ferry.get());\n    }\n\n    public static boolean isPedestrianNavigableFerry(final Taggable taggable)\n    {\n        final Optional<FerryTag> ferry = Validators.from(FerryTag.class, taggable);\n        return ferry.isPresent() && PEDESTRIAN_NAVIGABLE.contains(ferry.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/FixMeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM fix me tag. Check OSM Wiki below for more information.\n *\n * @author v-garei\n */\n@Tag(value = Tag.Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/fixme#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:fixme\")\npublic interface FixMeTag\n{\n    @TagKey\n    String KEY = \"fixme\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/FootTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM foot tag\n *\n * @author robert_stack\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/foot#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:foot\")\npublic enum FootTag\n{\n    YES,\n    NO,\n    DESIGNATED,\n    OFFICIAL,\n    PRIVATE,\n    PERMISSIVE,\n    DESTINATION,\n    USE_SIDEPATH,\n    CUSTOMERS,\n    UNKNOWN;\n\n    private static final EnumSet<FootTag> PRIVATE_ACCESS = EnumSet.of(NO, PRIVATE);\n    private static final EnumSet<FootTag> PEDESTRIAN_ACCESS = EnumSet.of(YES, DESIGNATED, OFFICIAL,\n            PERMISSIVE, DESTINATION);\n\n    @TagKey\n    public static final String KEY = \"foot\";\n\n    public static boolean isPedestrianAccessible(final Taggable taggable)\n    {\n        final Optional<FootTag> foot = Validators.from(FootTag.class, taggable);\n        return foot.isPresent() && PEDESTRIAN_ACCESS.contains(foot.get());\n    }\n\n    public static boolean isPrivate(final Taggable taggable)\n    {\n        final Optional<FootTag> foot = Validators.from(FootTag.class, taggable);\n        return foot.isPresent() && PRIVATE_ACCESS.contains(foot.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/FootwayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM footway tag\n *\n * @author robert_stack\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/footway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:footway\")\npublic enum FootwayTag\n{\n    SIDEWALK,\n    CROSSING;\n\n    @TagKey\n    public static final String KEY = \"footway\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/FordTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM Ford Tag.\n *\n * @author sayas01\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/ford#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:ford\")\npublic enum FordTag\n{\n    YES,\n    STEPPING_STONES,\n    SEASONAL,\n    NO,\n    STREAM,\n    LEVEL_CROSSING,\n    BOAT;\n\n    @TagKey\n    public static final String KEY = \"ford\";\n\n    public static boolean isYes(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, FordTag.class, FordTag.YES);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/FourWheelDriveOnlyTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * This tag denotes roads that are only navigable by vehicles with Four wheel drive\n * \n * @author kkonishi2\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/?key=4wd_only\", osm = \"https://wiki.openstreetmap.org/wiki/Key:4wd_only\")\npublic enum FourWheelDriveOnlyTag\n{\n    YES,\n    NO;\n\n    @TagKey\n    public static final String KEY = \"4wd_only\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/HarbourTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM Harbour tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/harbour#values\", osm = \"http://wiki.openstreetmap.org/wiki/Harbour\")\npublic enum HarbourTag\n{\n    YES;\n\n    @TagKey\n    public static final String KEY = \"harbour\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/HeightTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.AltitudeExtractor;\n\n/**\n * OSM height tag: http://taginfo.openstreetmap.org/keys/height#values\n *\n * @author cstaylor\n * @author bbreithaupt\n */\n@Tag(value = Validation.LENGTH, taginfo = \"http://taginfo.openstreetmap.org/keys/height#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:height\")\npublic interface HeightTag\n{\n    @TagKey\n    String KEY = \"height\";\n\n    static Optional<Altitude> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return AltitudeExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/HighResolutionTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM hires tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/hires#values\")\npublic enum HighResolutionTag\n{\n    YES,\n    NO,\n    MAPBOX,\n    BING;\n\n    @TagKey\n    public static final String KEY = \"hires\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/HighwayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\nimport com.google.common.collect.EnumBiMap;\n\n/**\n * OSM highway tag\n *\n * @author cstaylor\n * @author matthieun\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/highway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:highway\")\npublic enum HighwayTag\n{\n    MOTORWAY,\n    MOTORWAY_LINK,\n    TRUNK,\n    TRUNK_LINK,\n    PRIMARY,\n    PRIMARY_LINK,\n    SECONDARY,\n    SECONDARY_LINK,\n    TERTIARY,\n    TERTIARY_LINK,\n    UNCLASSIFIED,\n    RESIDENTIAL,\n    SERVICE,\n    TRACK,\n    LIVING_STREET,\n    PEDESTRIAN,\n    BUS_GUIDEWAY,\n    RACEWAY,\n    ROAD,\n    CYCLEWAY,\n    FOOTWAY,\n    BRIDLEWAY,\n    STEPS,\n    PATH,\n    PROPOSED,\n    CONSTRUCTION,\n    ESCAPE,\n    BUS_STOP,\n    CROSSING,\n    ELEVATOR,\n    EMERGENCY_ACCESS_POINT,\n    EMERGENCY_BAY,\n    GIVE_WAY,\n    MINI_ROUNDABOUT,\n    MOTORWAY_JUNCTION,\n    PASSING_PLACE,\n    REST_AREA,\n    SPEED_CAMERA,\n    STREET_LAMP,\n    SERVICES,\n    STOP,\n    TRAILHEAD,\n    TRAFFIC_MIRROR,\n    TRAFFIC_SIGNALS,\n    TURNING_CIRCLE,\n    PLATFORM,\n    MILESTONE,\n    TURNING_LOOP,\n    CORRIDOR,\n    NO,\n    TOLL_GANTRY;\n\n    @TagKey\n    public static final String KEY = \"highway\";\n\n    private static final EnumSet<HighwayTag> CORE_WAYS = EnumSet.of(MOTORWAY, TRUNK, PRIMARY,\n            SECONDARY, TERTIARY, UNCLASSIFIED, RESIDENTIAL, SERVICE, MOTORWAY_LINK, TRUNK_LINK,\n            PRIMARY_LINK, SECONDARY_LINK, TERTIARY_LINK, LIVING_STREET, PEDESTRIAN, TRACK,\n            BUS_GUIDEWAY, RACEWAY, ROAD, FOOTWAY, BRIDLEWAY, STEPS, PATH, CYCLEWAY, ESCAPE);\n\n    private static final EnumSet<HighwayTag> METRICS_HIGHWAYS = EnumSet.of(MOTORWAY, TRUNK, PRIMARY,\n            SECONDARY, TERTIARY, UNCLASSIFIED, RESIDENTIAL, SERVICE, MOTORWAY_LINK, TRUNK_LINK,\n            PRIMARY_LINK, SECONDARY_LINK, TERTIARY_LINK, LIVING_STREET, PEDESTRIAN, TRACK,\n            BUS_GUIDEWAY, FOOTWAY, BRIDLEWAY, STEPS, PATH, CYCLEWAY, ESCAPE);\n\n    private static final EnumSet<HighwayTag> CAR_NAVIGABLE_HIGHWAYS = EnumSet.of(MOTORWAY, TRUNK,\n            PRIMARY, SECONDARY, TERTIARY, UNCLASSIFIED, RESIDENTIAL, SERVICE, MOTORWAY_LINK,\n            TRUNK_LINK, PRIMARY_LINK, SECONDARY_LINK, TERTIARY_LINK, LIVING_STREET, TRACK, ROAD);\n\n    private static final EnumSet<HighwayTag> PEDESTRIAN_NAVIGABLE_HIGHWAYS = EnumSet.of(PEDESTRIAN,\n            FOOTWAY, STEPS, PATH, CROSSING, PLATFORM, ELEVATOR, CORRIDOR);\n\n    private static final EnumSet<HighwayTag> NODE_ONLY_HIGHWAYTAGS = EnumSet.of(BUS_STOP, CROSSING,\n            EMERGENCY_ACCESS_POINT, GIVE_WAY, MILESTONE, MINI_ROUNDABOUT, MOTORWAY_JUNCTION,\n            PASSING_PLACE, SPEED_CAMERA, STREET_LAMP, STOP, TRAFFIC_MIRROR, TRAFFIC_SIGNALS,\n            TRAILHEAD, TURNING_LOOP, TOLL_GANTRY);\n\n    private static final EnumSet<HighwayTag> WAY_ONLY_HIGHWAYTAGS = EnumSet.of(MOTORWAY, TRUNK,\n            PRIMARY, SECONDARY, TERTIARY, UNCLASSIFIED, RESIDENTIAL, MOTORWAY_LINK, TRUNK_LINK,\n            PRIMARY_LINK, SECONDARY_LINK, TERTIARY_LINK, LIVING_STREET, SERVICE, PEDESTRIAN, TRACK,\n            BUS_GUIDEWAY, ESCAPE, RACEWAY, ROAD, FOOTWAY, BRIDLEWAY, STEPS, CORRIDOR, PATH,\n            CYCLEWAY, PROPOSED, CONSTRUCTION);\n\n    private static final EnumBiMap<HighwayTag, HighwayTag> HIGHWAY_LINKS = EnumBiMap\n            .create(HighwayTag.class, HighwayTag.class);\n\n    static\n    {\n        HIGHWAY_LINKS.put(MOTORWAY, MOTORWAY_LINK);\n        HIGHWAY_LINKS.put(TRUNK, TRUNK_LINK);\n        HIGHWAY_LINKS.put(PRIMARY, PRIMARY_LINK);\n        HIGHWAY_LINKS.put(SECONDARY, SECONDARY_LINK);\n        HIGHWAY_LINKS.put(TERTIARY, TERTIARY_LINK);\n    }\n\n    public static Optional<HighwayTag> highwayTag(final Taggable taggable)\n    {\n        return Validators.from(HighwayTag.class, taggable);\n    }\n\n    public static boolean isCarNavigableHighway(final HighwayTag tag)\n    {\n        return CAR_NAVIGABLE_HIGHWAYS.contains(tag);\n    }\n\n    public static boolean isCarNavigableHighway(final Taggable taggable)\n    {\n        final Optional<HighwayTag> highway = highwayTag(taggable);\n        return highway.isPresent() && CAR_NAVIGABLE_HIGHWAYS.contains(highway.get());\n    }\n\n    public static boolean isCoreWay(final Taggable taggable)\n    {\n        final Optional<HighwayTag> highway = highwayTag(taggable);\n        return highway.isPresent() && CORE_WAYS.contains(highway.get());\n    }\n\n    /**\n     * Looking for (highway=pedestrian or highway=footway) and (building = * or area=yes) tag\n     * combination. These are pedestrian plazas and can contain valid road intersections.\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return Whether the taggable object is a highway area or not\n     */\n    public static boolean isHighwayArea(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, HighwayTag.class, HighwayTag.PEDESTRIAN,\n                HighwayTag.FOOTWAY)\n                && (Validators.isOfType(taggable, AreaTag.class, AreaTag.YES)\n                        || Validators.hasValuesFor(taggable, BuildingTag.class));\n    }\n\n    public static boolean isHighwayWithLink(final Taggable taggable)\n    {\n        final Optional<HighwayTag> highway = highwayTag(taggable);\n        return highway.isPresent() && HIGHWAY_LINKS.containsKey(highway.get());\n    }\n\n    public static boolean isLinkHighway(final HighwayTag tag)\n    {\n        return HIGHWAY_LINKS.containsValue(tag);\n    }\n\n    public static boolean isLinkHighway(final Taggable taggable)\n    {\n        final Optional<HighwayTag> highway = highwayTag(taggable);\n        return highway.isPresent() && HIGHWAY_LINKS.containsValue(highway.get());\n    }\n\n    public static boolean isMetricHighway(final Taggable taggable)\n    {\n        final Optional<HighwayTag> highway = highwayTag(taggable);\n        return highway.isPresent() && METRICS_HIGHWAYS.contains(highway.get());\n    }\n\n    public static boolean isNodeOnlyTag(final Taggable taggable)\n    {\n        final Optional<HighwayTag> highway = highwayTag(taggable);\n        return highway.isPresent() && NODE_ONLY_HIGHWAYTAGS.contains(highway.get());\n    }\n\n    public static boolean isPedestrianCrossing(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, HighwayTag.class, CROSSING);\n    }\n\n    public static boolean isPedestrianNavigableHighway(final Taggable taggable)\n    {\n        final Optional<HighwayTag> highway = highwayTag(taggable);\n        return highway.isPresent() && PEDESTRIAN_NAVIGABLE_HIGHWAYS.contains(highway.get());\n    }\n\n    public static boolean isWayOnlyTag(final Taggable taggable)\n    {\n        final Optional<HighwayTag> highway = highwayTag(taggable);\n        return highway.isPresent() && WAY_ONLY_HIGHWAYTAGS.contains(highway.get());\n    }\n\n    public static Optional<HighwayTag> tag(final Taggable taggable)\n    {\n        return Validators.from(HighwayTag.class, taggable);\n    }\n\n    /**\n     * Checks if the current highway type has a complementary link type\n     *\n     * @return true if has a link type for highway\n     */\n    public boolean canHaveLink()\n    {\n        return HIGHWAY_LINKS.containsKey(this);\n    }\n\n    /**\n     * Gets the highway type from a link type. So PRIMARY_LINK will return PRIMARY\n     *\n     * @return an Optional {@link HighwayTag}, if highway type does not have a link will return\n     *         Optional.empty\n     */\n    public Optional<HighwayTag> getHighwayFromLink()\n    {\n        return Optional.ofNullable(HIGHWAY_LINKS.inverse().get(this));\n    }\n\n    /**\n     * Gets the highway_link type from the highway type. So PRIMARY will return PRIMARY_LINK\n     *\n     * @return an Optional {@link HighwayTag}, if no link for highway available will return\n     *         Optional.empty\n     */\n    public Optional<HighwayTag> getLinkFromHighway()\n    {\n        return Optional.ofNullable(HIGHWAY_LINKS.get(this));\n    }\n\n    public String getTagValue()\n    {\n        return name().toLowerCase().intern();\n    }\n\n    /**\n     * Checks to see if one highway type has the identical classification as another.\n     *\n     * @param tag\n     *            The {@link HighwayTag} that you are comparing this to\n     * @return {@code true} if class is the same\n     */\n    public boolean isIdenticalClassification(final HighwayTag tag)\n    {\n        return this == tag;\n    }\n\n    public boolean isLessImportantThan(final HighwayTag other)\n    {\n        return this.compareTo(other) > 0;\n    }\n\n    public boolean isLessImportantThanOrEqualTo(final HighwayTag other)\n    {\n        return this.compareTo(other) >= 0;\n    }\n\n    /**\n     * Checks to see if the highway type is a link type\n     *\n     * @return true if it is link type\n     */\n    public boolean isLink()\n    {\n        return HIGHWAY_LINKS.containsValue(this);\n    }\n\n    public boolean isMoreImportantThan(final HighwayTag other)\n    {\n        return this.compareTo(other) < 0;\n    }\n\n    public boolean isMoreImportantThanOrEqualTo(final HighwayTag other)\n    {\n        return this.compareTo(other) <= 0;\n    }\n\n    /**\n     * Checks to see if one highway type has an equal classification as another. This either\n     * indicates that it is the exact same type, or that one is a link and the other the same\n     * non-link type. e.g. TRUNK and TRUNK_LINK.\n     *\n     * @param tag\n     *            The {@link HighwayTag} that you are comparing this to\n     * @return {@code true} if class the same\n     */\n    public boolean isOfEqualClassification(final HighwayTag tag)\n    {\n        return this == tag || HIGHWAY_LINKS.get(this) == tag\n                || HIGHWAY_LINKS.inverse().get(this) == tag;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/HistoricTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM historic tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/historic#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:historic\")\npublic enum HistoricTag\n{\n    AIRCRAFT,\n    ARCHAEOLOGICAL_SITE,\n    BATTLEFIELD,\n    BOUNDARY_STONE,\n    BUILDING,\n    CANNON,\n    CASTLE,\n    CITY_GATE,\n    CITYWALLS,\n    FARM,\n    FORT,\n    GALLOWS,\n    LOCOMOTIVE,\n    MANOR,\n    MEMORIAL,\n    MILESTONE,\n    MONASTERY,\n    MONUMENT,\n    OPTICAL_TELEGRAPH,\n    PILLORY,\n    RUINS,\n    RUNE_STONE,\n    SHIP,\n    TOMB,\n    WAYSIDE_CROSS,\n    WAYSIDE_SHRINE,\n    WRECK;\n\n    @TagKey\n    public static final String KEY = \"historic\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ISOCountryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\nimport com.google.common.collect.ImmutableList;\n\n/**\n * Annotated class representing the iso_country_code tag in Atlas data\n *\n * @author cstaylor\n * @author ahsieh\n * @author bbreithaupt\n */\n@Tag(Validation.ISO3_COUNTRY)\npublic interface ISOCountryTag\n{\n    @TagKey\n    String KEY = \"iso_country_code\";\n\n    String COUNTRY_MISSING = \"N/A\";\n    String COUNTRY_DELIMITER = \",\";\n\n    static Iterable<String> all(final Taggable taggable)\n    {\n        final Optional<String> countryCode = taggable.getTag(ISOCountryTag.class, Optional.empty());\n        if (countryCode.isPresent())\n        {\n            return Arrays.asList(countryCode.get().split(COUNTRY_DELIMITER));\n        }\n        return ImmutableList.of();\n    }\n\n    /**\n     * Tests if all of the item's iso_country_codes can be found in a list of countries\n     *\n     * @param countries\n     *            A set of countries we want to check in\n     * @return Predicate used to test if all of the item's iso_country_codes can be found in the\n     *         list of countries\n     */\n    static Predicate<Taggable> allIn(final Collection<String> countries)\n    {\n        if (countries.isEmpty())\n        {\n            return taggable -> false;\n        }\n\n        return taggable ->\n        {\n            for (final String country : all(taggable))\n            {\n                // if any don't match, return false\n                if (!countries.contains(country))\n                {\n                    return false;\n                }\n            }\n            return true;\n        };\n    }\n\n    /**\n     * Returns the first country code from the Taggable's iso_country_code tag. BEWARE: This method\n     * may be non-deterministic as it depends on the ordering of the country codes\n     *\n     * @param taggable\n     *            The {@link Taggable} to get the first country code from\n     * @return The first country code for the item if it exists\n     */\n    static Optional<String> first(final Taggable taggable)\n    {\n        return Iterables.first(all(taggable));\n    }\n\n    static Predicate<Taggable> isIn(final Set<String> countries)\n    {\n        if (countries.isEmpty())\n        {\n            return taggable -> false;\n        }\n\n        return taggable ->\n        {\n            for (final String country : all(taggable))\n            {\n                // if any one matches, return true\n                if (countries.contains(country))\n                {\n                    return true;\n                }\n            }\n            return false;\n        };\n    }\n\n    static Predicate<Taggable> isIn(final String countryToMatch)\n    {\n        if (countryToMatch == null || countryToMatch.isEmpty())\n        {\n            return taggable -> false;\n        }\n\n        return taggable ->\n        {\n            for (final String country : all(taggable))\n            {\n                // if any one matches, return true\n                if (countryToMatch.equals(country))\n                {\n                    return true;\n                }\n            }\n            return false;\n        };\n    }\n\n    static String join(final Collection<String> countries)\n    {\n        return String.join(COUNTRY_DELIMITER, countries);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/IceRoadTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM ice_road tag.\n *\n * @author jacker\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/ice_road#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:ice_road\")\npublic enum IceRoadTag\n{\n    YES,\n    SNOWMOBILE,\n    NO;\n\n    @TagKey\n    public static final String KEY = \"ice_road\";\n\n    public static boolean isYes(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, IceRoadTag.class, IceRoadTag.YES,\n                IceRoadTag.SNOWMOBILE);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/IndustrialTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM Industrial Tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/industrial#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:industrial\")\npublic enum IndustrialTag\n{\n    ALUMINUM_SMELTING,\n    BAKERY,\n    BREWERY,\n    BRICKYARD,\n    DEPOT,\n    DISTRIBUTOR,\n    FACTORY,\n    FURNITURE,\n    GAS,\n    GRINDING_MILL,\n    HEATING_STATION,\n    ICE_FACTORY,\n    MACHINE_SHOP,\n    MINE,\n    MOBILE_EQUIPMENT,\n    OIL_MILL,\n    OIL,\n    PORT,\n    SALT_POND,\n    SAWMILL,\n    SCRAP_YARD,\n    SHIPYARD,\n    SLAUGHTERHOUSE,\n    STEELMAKING,\n    WAREHOUSE,\n    WELL_CLUSTER,\n    WELLSITE;\n\n    @TagKey\n    public static final String KEY = \"industrial\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/IntermittentTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM's intermittent tags\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/intermittent#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:intermittent\")\npublic enum IntermittentTag\n{\n    YES,\n    NO,\n    DRY;\n\n    @TagKey\n    public static final String KEY = \"intermittent\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/InternetAccessFeeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM internet_access:fee tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/internet_access:fee#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:internet_access\")\npublic enum InternetAccessFeeTag\n{\n    NO,\n    YES;\n\n    @TagKey\n    public static final String KEY = \"internet_access:fee\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/InternetAccessTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM internet_access tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/internet_access#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:internet_access\")\npublic enum InternetAccessTag\n{\n    WLAN,\n    NO,\n    YES,\n    TERMINAL;\n\n    @TagKey\n    public static final String KEY = \"internet_access\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/Iso31662CountryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.locale.IsoCountry;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.IsoCountryExtractor;\n\n/**\n * OSM's 2 digit ISO Country tag\n *\n * @author matthieun\n */\n@Tag(Validation.ISO2_COUNTRY)\npublic interface Iso31662CountryTag\n{\n    @TagKey\n    String KEY = \"ISO3166-1:alpha2\";\n\n    static Iterable<IsoCountry> all(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            final IsoCountryExtractor extractor = new IsoCountryExtractor();\n            final Optional<List<IsoCountry>> countries = extractor.validateAndExtract(\n                    tagValue.get(), Iso31662CountryTag.class.getDeclaredAnnotation(Tag.class));\n            return countries.isPresent() ? countries.get() : new ArrayList<>();\n        }\n\n        return new ArrayList<>();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/Iso31663CountryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.locale.IsoCountry;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.IsoCountryExtractor;\n\n/**\n * OSM's 3 digit ISO Country tag\n *\n * @author matthieun\n */\n@Tag(Validation.ISO3_COUNTRY)\npublic interface Iso31663CountryTag\n{\n    @TagKey\n    String KEY = \"ISO3166-1:alpha3\";\n\n    static Iterable<IsoCountry> all(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            final IsoCountryExtractor extractor = new IsoCountryExtractor();\n            final Optional<List<IsoCountry>> countries = extractor.validateAndExtract(\n                    tagValue.get(), Iso31663CountryTag.class.getDeclaredAnnotation(Tag.class));\n            return countries.isPresent() ? countries.get() : new ArrayList<>();\n        }\n\n        return new ArrayList<>();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/Iso3166DefaultCountryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.locale.IsoCountry;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.IsoCountryExtractor;\n\n/**\n * OSM's default 2 digit ISO Country tag\n *\n * @author matthieun\n */\n@Tag(Validation.ISO2_COUNTRY)\npublic interface Iso3166DefaultCountryTag\n{\n    @TagKey\n    String KEY = \"ISO3166-1\";\n\n    static Iterable<IsoCountry> all(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            final IsoCountryExtractor extractor = new IsoCountryExtractor();\n            final Optional<List<IsoCountry>> countries = extractor.validateAndExtract(\n                    tagValue.get(),\n                    Iso3166DefaultCountryTag.class.getDeclaredAnnotation(Tag.class));\n            return countries.isPresent() ? countries.get() : new ArrayList<>();\n        }\n\n        return new ArrayList<>();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/JunctionTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM junction tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/junction#values\", osm = \"http://wiki.openstreetmap.org/wiki/Junctions\")\npublic enum JunctionTag\n{\n    ROUNDABOUT,\n    CIRCULAR;\n\n    @TagKey\n    public static final String KEY = \"junction\";\n\n    public static boolean isCircular(final Taggable taggable)\n    {\n        final Optional<JunctionTag> junction = Validators.from(JunctionTag.class, taggable);\n        return junction.isPresent() && CIRCULAR == junction.get();\n    }\n\n    public static boolean isRoundabout(final Taggable taggable)\n    {\n        final Optional<JunctionTag> junction = Validators.from(JunctionTag.class, taggable);\n        return junction.isPresent() && ROUNDABOUT == junction.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LandUseTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM landuse tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/landuse#values\", osm = \"http://wiki.openstreetmap.org/wiki/Landuse\")\npublic enum LandUseTag\n{\n    ALLOTMENTS,\n    AQUACULTURE,\n    BASIN,\n    BROWNFIELD,\n    CEMETERY,\n    COMMERCIAL,\n    CONSTRUCTION,\n    FARMLAND,\n    FARMYARD,\n    FOREST,\n    GARAGES,\n    GRASS,\n    GREENFIELD,\n    GREENHOUSE_HORTICULTURE,\n    INDUSTRIAL,\n    LANDFILL,\n    MEADOW,\n    MILITARY,\n    ORCHARD,\n    PEAT_CUTTING,\n    PLANT_NURSERY,\n    PORT,\n    QUARRY,\n    RAILWAY,\n    RECREATION_GROUND,\n    RESERVOIR,\n    RESIDENTIAL,\n    RETAIL,\n    SALT_POND,\n    VILLAGE_GREEN,\n    VINEYARD,\n    POND;\n\n    @TagKey\n    public static final String KEY = \"landuse\";\n\n    public static Optional<LandUseTag> get(final Taggable taggable)\n    {\n        return Validators.from(LandUseTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LandcoverTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM landcover tag\n *\n * @author stephencerqueira\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/landcover#values\", osm = \"https://wiki.openstreetmap.org/wiki/Proposed_features/landcover\")\npublic enum LandcoverTag\n{\n    GRASS,\n    GRAVEL,\n    TREES;\n\n    @TagKey\n    public static final String KEY = \"landcover\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LanesTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.OrdinalExtractor;\n\n/**\n * OSM lanes tag\n *\n * @author robert_stack\n */\n@Tag(value = Validation.ORDINAL, range = @Range(min = 1, max = 50), taginfo = \"http://taginfo.openstreetmap.org/keys/lanes#values\", osm = \"http://wiki.openstreetmap.org/wiki/Lanes\")\npublic interface LanesTag\n{\n    @TagKey\n    String KEY = \"lanes\";\n\n    static Optional<Integer> numberOfLanes(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            final OrdinalExtractor extractor = new OrdinalExtractor();\n            return extractor.validateAndExtract(tagValue.get(),\n                    LanesTag.class.getDeclaredAnnotation(Tag.class));\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LastEditChangesetTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Annotated class representing the last_edit_changeset tag in Atlas data. Though a tag in an atlas,\n * this property does not come from a tag in an OSM Entity. It comes from the OSM Entity attribute:\n * changeset. Every atlas entity will have this tag.\n *\n * @author nhallahan\n */\n@Tag(Validation.LONG)\npublic interface LastEditChangesetTag\n{\n    @TagKey\n    String KEY = \"last_edit_changeset\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LastEditTimeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Annotated class representing the last_edit_time tag in Atlas data. Though a tag in an atlas, this\n * property does not come from a tag in an OSM Entity. It comes from the OSM Entity attribute:\n * timestamp. Every atlas entity will have this tag.\n *\n * @author cstaylor\n */\n@Tag(Validation.TIMESTAMP)\npublic interface LastEditTimeTag\n{\n    @TagKey\n    String KEY = \"last_edit_time\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LastEditUserIdentifierTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Annotated class representing the last_edit_user_id tag in Atlas data. Though a tag in an atlas,\n * this property does not come from a tag in an OSM Entity. It comes from the OSM Entity attribute:\n * uid. Every atlas entity will have this tag.\n *\n * @author tony\n */\n@Tag(Validation.LONG)\npublic interface LastEditUserIdentifierTag\n{\n    @TagKey\n    String KEY = \"last_edit_user_id\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LastEditUserNameTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Annotated class representing the last_edit_user_name tag in Atlas data. Though a tag in an atlas,\n * this property does not come from a tag in an OSM Entity. It comes from the OSM Entity attribute:\n * user. Every atlas entity will have this tag.\n *\n * @author cstaylor\n */\n@Tag(Validation.NON_EMPTY_STRING)\npublic interface LastEditUserNameTag\n{\n    @TagKey\n    String KEY = \"last_edit_user_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LastEditVersionTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Annotated class representing the last_edit_version tag in Atlas data. Though a tag in an atlas,\n * this property does not come from a tag in an OSM Entity. It comes from the OSM Entity attribute:\n * version. Every atlas entity will have this tag.\n *\n * @author nhallahan\n */\n@Tag(Validation.LONG)\npublic interface LastEditVersionTag\n{\n    @TagKey\n    String KEY = \"last_edit_version\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LayerTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.LongExtractor;\n\n/**\n * OSM layer tag\n *\n * @author cstaylor\n * @author brian_l_davis\n * @author bbreithaupt\n */\n@Tag(value = Validation.LONG, range = @Range(min = -5, max = 5, exclude = {\n        0 }), taginfo = \"http://taginfo.openstreetmap.org/keys/layer#values\", osm = \"http://wiki.openstreetmap.org/wiki/Layer\")\npublic interface LayerTag\n{\n    Long ZERO = 0L;\n    @TagKey\n    String KEY = \"layer\";\n\n    /**\n     * Checks if two Taggable objects are on the same layer or not. According to OSM wiki, objects\n     * with no explicit LayerTag are assumed to have layer 0.\n     *\n     * @param taggableOne\n     *            first object to compare\n     * @param taggableTwo\n     *            second object to compare\n     * @return true if the two objects have same layer tag, false otherwise\n     */\n    static boolean areOnSameLayer(final Taggable taggableOne, final Taggable taggableTwo)\n    {\n        return LayerTag.getTaggedOrImpliedValue(taggableOne, ZERO)\n                .equals(LayerTag.getTaggedOrImpliedValue(taggableTwo, ZERO));\n    }\n\n    static long getMaxValue()\n    {\n        return LayerTag.class.getDeclaredAnnotation(Tag.class).range().max();\n    }\n\n    static long getMinValue()\n    {\n        return LayerTag.class.getDeclaredAnnotation(Tag.class).range().min();\n    }\n\n    static Long getTaggedOrImpliedValue(final Taggable taggable, final Long impliedValue)\n    {\n        final Optional<Long> taggedValue = getTaggedValue(taggable);\n        // Return the layer tag if there is one\n        if (taggedValue.isPresent())\n        {\n            return taggedValue.get();\n        }\n        // Else return 1 if taggable is a bridge\n        if (BridgeTag.isBridge(taggable))\n        {\n            return 1L;\n        }\n        // Else return -1 if taggable is a tunnel\n        if (TunnelTag.isTunnel(taggable))\n        {\n            return -1L;\n        }\n        // Else return the implied value\n        return impliedValue;\n    }\n\n    static Optional<Long> getTaggedValue(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            final LongExtractor extractor = new LongExtractor();\n            return extractor.validateAndExtract(tagValue.get(),\n                    LayerTag.class.getDeclaredAnnotation(Tag.class));\n        }\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LeisureTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM leisure tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/leisure#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:leisure\")\npublic enum LeisureTag\n{\n    PITCH,\n    SWIMMING_POOL,\n    PARK,\n    PLAYGROUND,\n    GARDEN,\n    SPORTS_CENTRE,\n    NATURE_RESERVE,\n    COMMON,\n    TRACK,\n    STADIUM,\n    GOLF_COURSE,\n    RECREATION_GROUND,\n    SLIPWAY,\n    PICNIC_TABLE,\n    MARINA,\n    WATER_PARK,\n    DOG_PARK,\n    FIREPIT,\n    SAUNA,\n    MINIATURE_GOLF,\n    FISHING,\n    HORSE_RIDING,\n    FITNESS_STATION,\n    ICE_RINK,\n    BEACH_RESORT,\n    YES,\n    BIRD_HIDE,\n    RESORT,\n    DANCE,\n    ADULT_GAMING_CENTRE,\n    CLUB,\n    CLIMBING,\n    BLEACHERS,\n    OUTDOOR_SEATING,\n    TANNING_SALON,\n    BANDSTAND,\n    SOCIAL_CLUB,\n    FESTIVAL_GROUNDS;\n\n    @TagKey\n    public static final String KEY = \"leisure\";\n\n    public static Optional<LeisureTag> get(final Taggable taggable)\n    {\n        return Validators.from(LeisureTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LevelTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM level tag\n *\n * @author sayas01\n */\n@Tag(value = Tag.Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/level#values\", osm = \"http://wiki.openstreetmap.org/wiki/Level\")\n@SuppressWarnings(\"squid:S1214\")\npublic interface LevelTag\n{\n    @TagKey\n    String KEY = \"level\";\n\n    /**\n     * Checks if two Taggable objects are on the same level or not. As per\n     * https://wiki.openstreetmap.org/wiki/Key:level, level=0 is not always at street level and so\n     * unlike LayerTag, if the LevelTag is not explicitly given, we cannot imply that the object is\n     * at level 0.\n     *\n     * @param taggableOne\n     *            first object to compare\n     * @param taggableTwo\n     *            second object to compare\n     * @return true if object one and object two are on the same level\n     */\n    static boolean areOnSameLevel(final Taggable taggableOne, final Taggable taggableTwo)\n    {\n        final Optional<String> levelTagEdgeOne = LevelTag.getTaggedValue(taggableOne);\n        final Optional<String> levelTagEdgeTwo = LevelTag.getTaggedValue(taggableTwo);\n        if (levelTagEdgeOne.isPresent() && levelTagEdgeTwo.isPresent())\n        {\n            return levelTagEdgeOne.get().equals(levelTagEdgeTwo.get());\n        }\n        return !levelTagEdgeOne.isPresent() && !levelTagEdgeTwo.isPresent();\n    }\n\n    static String getTaggedOrImpliedValue(final Taggable taggable, final String impliedValue)\n    {\n        final Optional<String> taggedValue = getTaggedValue(taggable);\n        return taggedValue.isPresent() ? taggedValue.get() : impliedValue;\n    }\n\n    static Optional<String> getTaggedValue(final Taggable taggable)\n    {\n        return taggable.getTag(KEY);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LivingStreetTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM's living_street tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/living_street#values\", osm = \"http://wiki.openstreetmap.org/wiki/Proposed_features/Tag:living_street%3Dyes\")\npublic enum LivingStreetTag\n{\n    YES,\n    NO,\n    RESIDENTIAL;\n\n    @TagKey\n    public static final String KEY = \"living_street\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LocalizedTagNameWithOptionalDate.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport com.google.common.base.Joiner;\n\n/**\n * Some tags in OSM have optional languages and even date ranges.\n * <p>\n * This class is immutable, so it's threadsafe\n *\n * @author cstaylor\n */\npublic class LocalizedTagNameWithOptionalDate\n{\n    private final String name;\n\n    private Optional<IsoLanguage> language = Optional.empty();\n\n    // For now, we just leave this be. In the future we may parse and use this portion of the name\n    private Optional<String> untouchedDateRange = Optional.empty();\n\n    public LocalizedTagNameWithOptionalDate(final String key)\n    {\n        if (key == null)\n        {\n            throw new IllegalArgumentException(\"key can't be null\");\n        }\n\n        final StringList list = StringList.split(key, \":\");\n        // This is the count of items in the string list that are part of the name and not optional\n        // language or date range fields\n        int nameBlocks = list.size();\n        if (nameBlocks > 1)\n        {\n            // Check if we have a '-' character in the last item\n            // This would make it a date range\n            if (list.get(nameBlocks - 1).contains(\"-\"))\n            {\n                // We don't need the date range in the name\n                nameBlocks--;\n                this.untouchedDateRange = Optional.of(list.get(nameBlocks));\n            }\n\n            // Check if we have the language portion at the end: it's possible nameBlocks is now 1,\n            // so we need to check it again\n            if (nameBlocks > 1)\n            {\n                this.language = IsoLanguage.forLanguageCode(list.get(nameBlocks - 1));\n                if (this.language.isPresent())\n                {\n                    nameBlocks--;\n                }\n            }\n        }\n        // Thank you streams!\n        this.name = Joiner.on(\":\")\n                .join(list.stream().limit(nameBlocks).collect(Collectors.toList()));\n    }\n\n    public Optional<String> getDateRange()\n    {\n        return this.untouchedDateRange;\n    }\n\n    public Optional<IsoLanguage> getLanguage()\n    {\n        return this.language;\n    }\n\n    public String getName()\n    {\n        return this.name;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/LocationTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM location tag\n *\n * @author mkalender\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/location#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:location\")\npublic enum LocationTag\n{\n    INDOOR,\n    KIOSK,\n    OUTDOOR,\n    OVERGROUND,\n    OVERWATER,\n    PLATFORM,\n    ROOF,\n    ROOFTOP,\n    UNDERGROUND,\n    UNDERWATER;\n\n    @TagKey\n    public static final String KEY = \"location\";\n\n    public static Optional<LocationTag> get(final Taggable taggable)\n    {\n        return Validators.from(LocationTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ManMadeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM man_made tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/man_made#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:man_made\")\npublic enum ManMadeTag\n{\n    ADIT,\n    BEACON,\n    BREAKWATER,\n    BRIDGE,\n    BUNKER_SILO,\n    CAMPANILE,\n    CHIMNEY,\n    COMMUNICATIONS_TOWER,\n    CRANE,\n    CROSS,\n    CUTLINE,\n    CLEARCUT,\n    EMBANKMENT,\n    DYKE,\n    FLAGPOLE,\n    GASOMETER,\n    GROYNE,\n    HOT_WATER_TANK,\n    KILN,\n    LIGHTHOUSE,\n    MAST,\n    MINESHAFT,\n    MONITORING_STATION,\n    OBSERVATORY,\n    OFFSHORE_PLATFORM,\n    PETROLEUM_WELL,\n    PIER,\n    PIPELINE,\n    PUMPING_STATION,\n    RESERVOIR_COVERED,\n    SILO,\n    SNOW_FENCE,\n    SNOW_NET,\n    STORAGE_TANK,\n    STREET_CABINET,\n    SURVEILLANCE,\n    SURVEY_POINT,\n    TELESCOPE,\n    TOWER,\n    WASTEWATER_PLANT,\n    WATERMILL,\n    WATER_TOWER,\n    WATER_WELL,\n    WATER_TAP,\n    WATER_WORKS,\n    WILDLIFE_CROSSING,\n    WINDMILL,\n    WORKS;\n\n    @TagKey\n    public static final String KEY = \"man_made\";\n\n    public static boolean isBridge(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, ManMadeTag.class, BRIDGE);\n    }\n\n    public static boolean isPier(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, ManMadeTag.class, PIER);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MaxHeightTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValue;\nimport org.openstreetmap.atlas.tags.annotations.TagValue.ValueType;\nimport org.openstreetmap.atlas.tags.annotations.extraction.AltitudeExtractor;\n\n/**\n * OSM maxheight tag: http://taginfo.openstreetmap.org/keys/maxheight#values\n *\n * @author cstaylor\n * @author bbreithaupt\n */\n@Tag(value = Validation.DOUBLE, taginfo = \"http://taginfo.openstreetmap.org/keys/maxheight#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:maxheight\")\npublic interface MaxHeightTag\n{\n    @TagKey\n    String KEY = \"maxheight\";\n\n    @TagValue\n    String DEFAULT = \"default\";\n\n    @TagValue\n    String NONE = \"none\";\n\n    @TagValue(ValueType.REGEX)\n    String METERS = \"(\\\\d+(\\\\.\\\\d+)?|\\\\.\\\\d+)(\\\\sm)?\";\n\n    @TagValue(ValueType.REGEX)\n    String FEET = \"\\\\d'\\\\d\\\"\";\n\n    static Optional<Altitude> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return AltitudeExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MaxSpeedBackwardTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValue;\nimport org.openstreetmap.atlas.tags.annotations.extraction.SpeedExtractor;\nimport org.openstreetmap.atlas.utilities.scalars.Speed;\n\n/**\n * OSM maxspeed:backward tag\n *\n * @author matthieun\n */\n@Tag(value = Validation.SPEED, taginfo = \"http://taginfo.openstreetmap.org/keys/maxspeed#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:maxspeed\")\npublic interface MaxSpeedBackwardTag\n{\n    @TagKey\n    String KEY = \"maxspeed:backward\";\n\n    @TagValue\n    String NONE = \"none\";\n\n    static Optional<Speed> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return SpeedExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n\n    static boolean hasMaxSpeedBackward(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MaxSpeedForwardTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValue;\nimport org.openstreetmap.atlas.tags.annotations.extraction.SpeedExtractor;\nimport org.openstreetmap.atlas.utilities.scalars.Speed;\n\n/**\n * OSM maxspeed:forward tag\n *\n * @author matthieun\n */\n@Tag(value = Validation.SPEED, taginfo = \"http://taginfo.openstreetmap.org/keys/maxspeed#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:maxspeed\")\npublic interface MaxSpeedForwardTag\n{\n    @TagKey\n    String KEY = \"maxspeed:forward\";\n\n    @TagValue\n    String NONE = \"none\";\n\n    static Optional<Speed> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return SpeedExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n\n    static boolean hasMaxSpeedForward(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MaxSpeedTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValue;\nimport org.openstreetmap.atlas.tags.annotations.extraction.SpeedExtractor;\nimport org.openstreetmap.atlas.utilities.scalars.Speed;\n\n/**\n * OSM maxspeed tag\n *\n * @author cstaylor\n * @author matthieun\n */\n@Tag(value = Validation.SPEED, taginfo = \"http://taginfo.openstreetmap.org/keys/maxspeed#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:maxspeed\")\npublic interface MaxSpeedTag\n{\n    @TagKey\n    String KEY = \"maxspeed\";\n\n    @TagValue\n    String NONE = \"none\";\n\n    static Optional<Speed> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return SpeedExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n\n    static boolean hasExtendedMaxSpeed(final Taggable taggable)\n    {\n        return hasMaxSpeed(taggable) || MaxSpeedForwardTag.hasMaxSpeedForward(taggable)\n                || MaxSpeedBackwardTag.hasMaxSpeedBackward(taggable);\n    }\n\n    static boolean hasMaxSpeed(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MaxWidthTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValue;\nimport org.openstreetmap.atlas.tags.annotations.TagValue.ValueType;\nimport org.openstreetmap.atlas.tags.annotations.extraction.LengthExtractor;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * OSM maxwidth tag\n *\n * @author cstaylor\n * @author bbreithaupt\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/maxwidth#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:maxwidth\")\npublic interface MaxWidthTag\n{\n    @TagKey\n    String KEY = \"maxwidth\";\n\n    @TagValue(ValueType.REGEX)\n    String METERS = \"(\\\\d+(\\\\.\\\\d+)?|\\\\.\\\\d+)(\\\\sm)?\";\n\n    @TagValue(ValueType.REGEX)\n    String FEET = \"\\\\d'\\\\d\\\"\";\n\n    static Optional<Distance> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return LengthExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MilitaryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM military tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/military#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key%3Amilitary\")\npublic enum MilitaryTag\n{\n    BUNKER,\n    BARRACKS,\n    NUCLEAR_EXPLOSION_SITE,\n    RANGE,\n    TRENCH,\n    AIRFIELD,\n    YES,\n    CHECKPOINT,\n    DANGER_AREA,\n    NAVAL_BASE,\n    TRAINING_AREA;\n\n    @TagKey\n    public static final String KEY = \"military\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MinHeightTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.AltitudeExtractor;\nimport org.openstreetmap.atlas.tags.annotations.validation.DoubleValidator;\n\n/**\n * OSM min_height tag\n *\n * @author ajayaswal\n * @author bbreithaupt\n */\n@Tag(value = Validation.DOUBLE, taginfo = \"https://taginfo.openstreetmap.org/keys/min_height#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:min_height\")\npublic interface MinHeightTag\n{\n    @TagKey\n    String KEY = \"min_height\";\n\n    DoubleValidator validator = new DoubleValidator();\n\n    static Optional<Altitude> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return AltitudeExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MinSpeedTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM minspeed tag\n *\n * @author mgostintsev\n */\n@Tag(value = Validation.SPEED, taginfo = \"http://taginfo.openstreetmap.org/keys/minspeed#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:minspeed\")\npublic interface MinSpeedTag\n{\n    @TagKey\n    String KEY = \"minspeed\";\n\n    static boolean hasMinSpeed(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MotorVehicleTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM motor vehicle tag\n *\n * @author pmi\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/motor_vehicle#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:motor_vehicle\")\npublic enum MotorVehicleTag\n{\n    YES,\n    NO,\n    PRIVATE,\n    AGRICULTURAL,\n    PERMISSIVE,\n    DESTINATION,\n    DESIGNATED,\n    DELIVERY,\n    FORESTRY,\n    OFFICIAL,\n    CUSTOMERS,\n    EMERGENCY,\n    BUS,\n    SERVICE,\n    UNKNOWN;\n\n    @TagKey\n    public static final String KEY = \"motor_vehicle\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MotorcarTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM motorcar tag\n *\n * @author mkalender\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/motorcar#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:motorcar\")\npublic enum MotorcarTag\n{\n    YES,\n    NO,\n    PRIVATE,\n    AGRICULTURAL,\n    PERMISSIVE,\n    DESTINATION,\n    DESIGNATED,\n    DELIVERY,\n    FORESTRY,\n    OFFICIAL,\n    CUSTOMERS,\n    EMERGENCY,\n    BUS,\n    SERVICE,\n    UNKNOWN;\n\n    @TagKey\n    public static final String KEY = \"motorcar\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/MotorcycleTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM motorcyle tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/motorcycle#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:motorcycle\")\npublic enum MotorcycleTag\n{\n    NO,\n    YES,\n    AGRICULTURAL,\n    DESIGNATED,\n    FORESTRY,\n    PRIVATE,\n    PERMISSIVE,\n    UNKNOWN;\n\n    @TagKey\n    public static final String KEY = \"motorcycle\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/NaturalTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM natural tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/natural#values\", osm = \"http://wiki.openstreetmap.org/wiki/Natural\")\npublic enum NaturalTag\n{\n    WATER,\n    TREE,\n    WOOD,\n    WETLAND,\n    SCRUB,\n    COASTLINE,\n    PEAK,\n    CLIFF,\n    GRASSLAND,\n    TREE_ROW,\n    HEATH,\n    ROCK,\n    BARE_ROCK,\n    BEACH,\n    SAND,\n    SPRING,\n    HOT_SPRING,\n    GEYSER,\n    LAND,\n    BAY,\n    SCREE,\n    RIDGE,\n    GLACIER,\n    CAVE_ENTRANCE,\n    SADDLE,\n    MARSH,\n    FELL,\n    REEF,\n    MUD,\n    STONE,\n    LANDFORM,\n    SHINGLE,\n    VALLEY,\n    CAPE,\n    VOLCANO,\n    CREVASSE,\n    SINKHOLE;\n\n    @TagKey\n    public static final String KEY = \"natural\";\n\n    public static Optional<NaturalTag> get(final Taggable taggable)\n    {\n        return Validators.from(NaturalTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/NetworkTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM network tag: While there are some conventions indicated on the OSM wiki, there is a wide\n * variety of possible values based on taginfo so this is subject to validation as any string.\n *\n * @author robert_stack\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/network#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:network\")\npublic interface NetworkTag\n{\n    @TagKey\n    String KEY = \"network\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/NoExitTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM noexit tag\n *\n * @author matthieun\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/noexit#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:noexit\")\npublic enum NoExitTag\n{\n    YES;\n\n    @TagKey\n    public static final String KEY = \"noexit\";\n\n    public static Optional<NoExitTag> get(final Taggable taggable)\n    {\n        return Validators.from(NoExitTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/NotesTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM notes tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/notes#values\", osm = \"http://wiki.openstreetmap.org/wiki/Notes\")\npublic interface NotesTag\n{\n    @TagKey\n    String KEY = \"notes\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/OpenDateTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM open_date tag\n *\n * @author brianjor\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/open_date#values\")\npublic interface OpenDateTag\n{\n    @TagKey\n    String KEY = \"open_date\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/OpeningDateTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM opening_date tag\n *\n * @author brianjor\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/opening_date#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:opening_date\")\npublic interface OpeningDateTag\n{\n    @TagKey\n    String KEY = \"opening_date\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/OpeningHoursTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValue;\nimport org.openstreetmap.atlas.tags.annotations.TagValue.ValueType;\n\n/**\n * OSM opening_hours tag\n *\n * @author mcuthbert\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/opening_hours#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:opening_hours\")\npublic interface OpeningHoursTag\n{\n    @TagKey\n    String KEY = \"opening_hours\";\n\n    @TagValue\n    String ALL = \"24/7\";\n\n    @TagValue(ValueType.REGEX)\n    String TWENTY_FOUR_SEVEN = \"24[/x]7\";\n\n    @TagValue(ValueType.REGEX)\n    String DATE_AND_TIME = \"(((Mo|Tu|We|Th|Fr|Sa|Su)(-?|,{0,6})){1,2}[_ ]?(([0-9]{1,2}:[0-9]{2}_?-_?[0-9]{1,2}:[0-9]{2}),? ?)+,?)+\";\n\n    @TagValue(ValueType.REGEX)\n    String TIME = \"(([0-9]{1,2}:[0-9]{2}_?-_?[0-9]{1,2}:[0-9]{2}),? ?)+\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/OrganicTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM organic tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/organic#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:organic\")\npublic enum OrganicTag\n{\n    ONLY,\n    YES,\n    NO,\n    LIMITED;\n\n    @TagKey\n    public static final String KEY = \"organic\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ParkingTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\n\n/**\n * OSM parking tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/parking#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:parking\")\npublic enum ParkingTag\n{\n    SURFACE,\n    UNDERGROUND,\n    @TagValueAs(\"multi-storey\")\n    MULTI_STOREY;\n\n    @TagKey\n    public static final String KEY = \"parking\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/PhoneTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM phone tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/phone#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:phone\")\npublic interface PhoneTag\n{\n    @TagKey\n    String KEY = \"phone\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/PlaceTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM place tag\n *\n * @author robert_stack\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/place#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:place\")\npublic enum PlaceTag\n{\n    COUNTRY,\n    STATE,\n    REGION,\n    PROVINCE,\n    DISTRICT,\n    COUNTY,\n    MUNICIPALITY,\n    CITY,\n    BOROUGH,\n    SUBURB,\n    QUARTER,\n    NEIGHBOURHOOD,\n    CITY_BLOCK,\n    PLOT,\n    TOWN,\n    VILLAGE,\n    HAMLET,\n    ISOLATED_DWELLING,\n    FARM,\n    ALLOTMENTS,\n    CONTINENT,\n    ARCHIPELAGO,\n    ISLAND,\n    ISLET,\n    LOCALITY,\n    SQUARE,\n    OCEAN,\n    SEA;\n\n    @TagKey\n    public static final String KEY = \"place\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/PowerTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM power tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/power#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:power\")\npublic enum PowerTag\n{\n    TOWER,\n    POLE,\n    LINE,\n    GENERATOR,\n    MINOR_LINE,\n    SUBSTATION,\n    SUB_STATION,\n    TRANSFORMER,\n    STATION,\n    SWITCH,\n    CABLE_DISTRIBUTION_CABINET,\n    BUSBAR,\n    PORTAL,\n    CABLE,\n    HELIOSTAT,\n    CATENARY_MAST,\n    PLANT,\n    INSULATOR,\n    SWITCHGEAR,\n    COMPENSATOR,\n    TERMINAL;\n\n    @TagKey\n    public static final String KEY = \"power\";\n\n    public static Optional<PowerTag> get(final Taggable taggable)\n    {\n        return Validators.from(PowerTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ProtectClassTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.OrdinalExtractor;\n\n/**\n * OSM protect_class tag\n *\n * @author bbreithaupt\n */\n@Tag(value = Validation.ORDINAL, range = @Range(min = 1, max = 99), taginfo = \"https://taginfo.openstreetmap.org/keys/protect_class\", osm = \"https://wiki.openstreetmap.org/wiki/Key:protect_class\")\npublic interface ProtectClassTag\n{\n    @TagKey\n    String KEY = \"protect_class\";\n\n    static Optional<Integer> getValue(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            final OrdinalExtractor extractor = new OrdinalExtractor();\n            return extractor.validateAndExtract(tagValue.get(),\n                    ProtectClassTag.class.getDeclaredAnnotation(Tag.class));\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/PublicServiceVehiclesTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM Public Service Vehicles (psv) tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/psv#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:psv\")\npublic enum PublicServiceVehiclesTag\n{\n    YES,\n    NO,\n    BUS,\n    OFFICIAL,\n    DESIGNATED;\n\n    @TagKey\n    public static final String KEY = \"psv\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/README.md",
    "content": "# Tags\n\nThis package helps with OSM tag parsing and validation.\n\n## Enum or Interface\n\nEach recognized tag has a java instance named after itself. When the tag values are set in the OSM wiki, and well defined, we use enums (for example, [HighwayTag](HighwayTag.java)). When the values are more loosely defined, like using ranges, we use interfaces and validation annotations (for example, [LanesTag](LanesTag.java)).\n\n## Annotations and Validation\n\nThe [annotations](annotations) and [validation](annotations/validation) packages define the annotation values used to identify the key and type of tags. For example, the [MaxSpeedTag](MaxSpeedTag.java) defines its Validation as a Speed, and annotates the tag's key as \"maxspeed\".\n\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/RailwayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM's railway tag\n *\n * @author cstaylor\n * @author matthieun\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/railway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:railway\")\npublic enum RailwayTag\n{\n    RAIL,\n    LEVEL_CROSSING,\n    ABANDONED,\n    SWITCH,\n    TRAVERSER,\n    BUFFER_STOP,\n    STATION,\n    PLATFORM,\n    TRAM,\n    DISUSED,\n    CROSSING,\n    SIGNAL,\n    TRAM_STOP,\n    SUBWAY,\n    HALT,\n    NARROW_GAUGE,\n    MILESTONE,\n    SUBWAY_ENTRANCE,\n    TRAM_LEVEL_CROSSING,\n    LIGHT_RAIL,\n    STOP,\n    PRESERVED,\n    RAZED,\n    TRAM_CROSSING,\n    CONSTRUCTION,\n    RAILWAY_CROSSING,\n    DISMANTLED,\n    PROPOSED,\n    DERAIL,\n    MINIATURE,\n    TURNTABLE,\n    MONORAIL,\n    FUNICULAR;\n\n    @TagKey\n    public static final String KEY = \"railway\";\n\n    private static final EnumSet<RailwayTag> RAILWAY_CROSSINGS = EnumSet.of(CROSSING,\n            LEVEL_CROSSING, TRAM_LEVEL_CROSSING, TRAM_CROSSING);\n\n    public static Optional<RailwayTag> get(final Taggable taggable)\n    {\n        return Validators.from(RailwayTag.class, taggable);\n    }\n\n    public static boolean isRailway(final Taggable taggable)\n    {\n        return get(taggable).isPresent();\n    }\n\n    public static boolean isRailwayCrossing(final Taggable taggable)\n    {\n        final Optional<RailwayTag> railway = get(taggable);\n        return railway.isPresent() && RAILWAY_CROSSINGS.contains(railway.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/RampBicycleTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM ramp bicycle tag\n *\n * @author james_gage\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/ramp:bicycle#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:ramp:bicycle\")\npublic enum RampBicycleTag\n{\n    YES,\n    NO;\n\n    @TagKey\n    public static final String KEY = \"ramp:bicycle\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/RelationTypeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Used internally to Atlas for determining the type of a relation\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/type#values\", osm = \"http://wiki.openstreetmap.org/wiki/Types_of_relation\")\npublic enum RelationTypeTag\n{\n    MULTIPOLYGON,\n    BUILDING,\n    RESTRICTION,\n    ROUTE,\n    ROUTE_MASTER,\n    BOUNDARY,\n    LAND_AREA,\n    SITE,\n    ASSOCIATEDSTREET,\n    PUBLIC_TRANSPORT,\n    STREET,\n    DESTINATION_SIGN,\n    WATERWAY,\n    ENFORCEMENT,\n    BRIDGE,\n    TUNNEL;\n\n    @TagKey\n    public static final String KEY = \"type\";\n\n    public static final String MULTIPOLYGON_TYPE = \"multipolygon\";\n    public static final String MULTIPOLYGON_ROLE_INNER = \"inner\";\n    public static final String MULTIPOLYGON_ROLE_OUTER = \"outer\";\n\n    public static final String BUILDING_ROLE_OUTLINE = \"outline\";\n    public static final String BUILDING_ROLE_PART = \"part\";\n\n    public static final String RESTRICTION_ROLE_FROM = \"from\";\n    public static final String RESTRICTION_ROLE_VIA = \"via\";\n    public static final String RESTRICTION_ROLE_TO = \"to\";\n\n    public static final String ADMINISTRATIVE_BOUNDARY_ROLE_SUB_AREA = \"subarea\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/RouteTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM route tag\n *\n * @author robert_stack\n * @author matthieun\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/route#values\", osm = \"http://wiki.openstreetmap.org/wiki/Relation:route\")\npublic enum RouteTag\n{\n    BICYCLE,\n    BUS,\n    CANOE,\n    DETOUR,\n    FERRY,\n    FITNESS_TRAIL,\n    HIKING,\n    HORSE,\n    INLINE_SKATES,\n    LIGHT_RAIL,\n    MTB,\n    NORDIC_WALKING,\n    PIPELINE,\n    PISTE,\n    POWER,\n    RAILWAY,\n    ROAD,\n    RUNNING,\n    SKI,\n    TRAIN,\n    TRAM;\n\n    @TagKey\n    public static final String KEY = \"route\";\n\n    public static boolean isFerry(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, RouteTag.class, FERRY);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SaltTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM salt tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/salt#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:salt\")\npublic enum SaltTag\n{\n    YES,\n    NO,\n    UNKNOWN;\n\n    @TagKey\n    public static final String KEY = \"salt\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SeasonalTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM seasonal tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/seasonal#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:seasonal\")\npublic enum SeasonalTag\n{\n    NO,\n    YES,\n    DRY_SEASON,\n    WET_SEASON,\n    SPRING,\n    SUMMER,\n    AUTUMN,\n    WINTER;\n\n    @TagKey\n    public static final String KEY = \"seasonal\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ServiceTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\n\n/**\n * OSM service tag. Does not include the documented but rarely used waterway related service tags.\n *\n * @author robert_stack\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/service#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:service\")\npublic enum ServiceTag\n{\n    PARKING_AISLE,\n    DRIVEWAY,\n    ALLEY,\n    EMERGENCY_ACCESS,\n    @TagValueAs(\"drive-through\")\n    DRIVE_THROUGH,\n    SPUR,\n    YARD,\n    SIDING,\n    CROSSOVER;\n\n    @TagKey\n    public static final String KEY = \"service\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ShopTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM shop tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/shop#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:shop\")\npublic enum ShopTag\n{\n    CONVENIENCE,\n    SUPERMARKET,\n    CLOTHES,\n    HAIRDRESSER,\n    BAKERY,\n    CAR_REPAIR,\n    CAR,\n    YES,\n    KIOSK,\n    DOITYOURSELF,\n    BUTCHER,\n    FLORIST,\n    MALL,\n    FURNITURE,\n    SHOES,\n    BICYCLE,\n    ALCOHOL,\n    ELECTRONICS,\n    HARDWARE,\n    BOOKS,\n    BEAUTY,\n    MOBILE_PHONE,\n    JEWELRY,\n    DEPARTMENT_STORE,\n    OPTICIAN,\n    GIFT,\n    GREENGROCER,\n    CAR_PARTS,\n    CHEMIST,\n    VARIETY_STORE,\n    SPORTS,\n    GARDEN_CENTRE,\n    COMPUTER,\n    STATIONERY,\n    TRAVEL_AGENCY,\n    LAUNDRY,\n    CONFECTIONERY,\n    BEVERAGES,\n    DRY_CLEANING,\n    TOYS,\n    TAILOR,\n    ART,\n    BABY_GOODS,\n    BATHROOM_FURNISHING,\n    BOUTIQUE,\n    CARPET,\n    CHEESE,\n    CHOCOLATE,\n    COPY_SHOP,\n    COSMETICS,\n    DAIRY,\n    DELI,\n    FABRIC,\n    FARM,\n    FASHION,\n    VACANT;\n\n    @TagKey\n    public static final String KEY = \"shop\";\n\n    public String getTagValue()\n    {\n        return name().toLowerCase().intern();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SidewalkLeftTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM sidewalk left tag\n *\n * @author Vladimir Lemberg\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/sidewalk:left#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:sidewalk:left\")\npublic enum SidewalkLeftTag\n{\n    NO,\n    YES,\n    SEPARATE;\n\n    @TagKey\n    public static final String KEY = \"sidewalk:left\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SidewalkRightTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM sidewalk right tag\n *\n * @author Vladimir Lemberg\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/sidewalk:right#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:sidewalk:right\")\npublic enum SidewalkRightTag\n{\n    NO,\n    YES,\n    SEPARATE;\n\n    @TagKey\n    public static final String KEY = \"sidewalk:right\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SidewalkTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM sidewalk tag\n *\n * @author isabellehillberg\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/sidewalk#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:sidewalk\")\npublic enum SidewalkTag\n{\n    BOTH,\n    NONE,\n    NO,\n    RIGHT,\n    LEFT,\n    SEPARATE,\n    YES;\n\n    @TagKey\n    public static final String KEY = \"sidewalk\";\n\n    public static boolean isNo(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, SidewalkTag.class, SidewalkTag.NO, SidewalkTag.NONE);\n    }\n\n    public static boolean isYes(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, SidewalkTag.class, SidewalkTag.BOTH, SidewalkTag.RIGHT,\n                SidewalkTag.LEFT, SidewalkTag.SEPARATE, SidewalkTag.YES);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SkiTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM Ski Tag.\n *\n * @author sayas01\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/ski#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:ski\")\npublic enum SkiTag\n{\n    NO,\n    YES,\n    DESIGNATED,\n    OFFICIAL,\n    PERMISSIVE,\n    CROSSING,\n    PRIVATE,\n    DOWNHILL,\n    CUSTOMERS,\n    NORDIC;\n\n    @TagKey\n    public static final String KEY = \"ski\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SmokingTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM smoking tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/smoking#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:smoking\")\npublic enum SmokingTag\n{\n    NO,\n    OUTSIDE,\n    YES,\n    SEPARATED,\n    ISOLATED,\n    DEDICATED,\n    UNKNOWN;\n\n    @TagKey\n    public static final String KEY = \"smoking\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SmoothnessTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM smoothness tag\n *\n * @author bbreithaupt\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/smoothness\", osm = \"https://wiki.openstreetmap.org/wiki/Key:smoothness\")\npublic enum SmoothnessTag\n{\n    EXCELLENT,\n    GOOD,\n    INTERMEDIATE,\n    BAD,\n    VERY_BAD,\n    HORRIBLE,\n    VERY_HORRIBLE,\n    IMPASSABLE;\n\n    @TagKey\n    public static final String KEY = \"smoothness\";\n\n    public boolean isLessImportantThan(final SmoothnessTag other)\n    {\n        return this.compareTo(other) > 0;\n    }\n\n    public boolean isLessImportantThanOrEqualTo(final SmoothnessTag other)\n    {\n        return this.compareTo(other) >= 0;\n    }\n\n    public boolean isMoreImportantThan(final SmoothnessTag other)\n    {\n        return this.compareTo(other) < 0;\n    }\n\n    public boolean isMoreImportantThanOrEqualTo(final SmoothnessTag other)\n    {\n        return this.compareTo(other) <= 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SnowmobileTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM Snowmobile Tag.\n *\n * @author sayas01\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/snowmobile#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:snowmobile\")\npublic enum SnowmobileTag\n{\n    NO,\n    DESIGNATED,\n    YES,\n    PERMISSIVE,\n    PRIVATE;\n\n    @TagKey\n    public static final String KEY = \"snowmobile\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SourceTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM source tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/source#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:source\")\npublic interface SourceTag\n{\n    @TagKey\n    String KEY = \"source\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SourceTypeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM source:type tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/source%3Atype#values\")\npublic interface SourceTypeTag\n{\n    @TagKey\n    String KEY = \"source:type\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SourceURLTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM source_url tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.URI, taginfo = \"http://taginfo.openstreetmap.org/keys/source%3Aurl#values\")\npublic interface SourceURLTag\n{\n    @TagKey\n    String KEY = \"source:url\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SportTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM sports tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/sport#values\")\npublic enum SportTag\n{\n    TENNIS,\n    GOLF,\n    SKI,\n    SOCCER,\n    BILLIARD,\n    MULTI,\n    BADMINTON,\n    EQUESTRIAN,\n    RUNNING,\n    ATHLETICS,\n    MODEL_AERODROME,\n    MOTOR,\n    FISHING,\n    WINGSTUN,\n    HOCKEY,\n    SWIMMING;\n\n    @TagKey\n    public static final String KEY = \"sport\";\n\n    public static Optional<SportTag> get(final Taggable taggable)\n    {\n        return Validators.from(SportTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SurfaceTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM's surface tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/surface#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:surface\")\npublic enum SurfaceTag\n{\n    ASPHALT,\n    UNPAVED,\n    PAVED,\n    GRAVEL,\n    GROUND,\n    DIRT,\n    GRASS,\n    CONCRETE,\n    PAVING_STONES,\n    SAND,\n    COMPACTED,\n    COBBLESTONE,\n    WOOD,\n    FINE_GRAVEL,\n    EARTH,\n    PEBBLESTONE,\n    SETT,\n    MUD,\n    GRASS_PAVER,\n    METAL,\n    GRAVEL_TURF,\n    ICE,\n    SALT,\n    SNOW,\n    WOODCHIPS,\n    TARTAN,\n    ARTIFICIAL_TURF,\n    DECOTURF,\n    CLAY,\n    METAL_GRID,\n    @TagValueAs(\"cobblestone:flattened\")\n    COBBLESTONE_FLATTENED,\n    @TagValueAs(\"concrete:lanes\")\n    CONCRETE_LANES,\n    @TagValueAs(\"concrete:plates\")\n    CONCRETE_PLATES;\n\n    @TagKey\n    public static final String KEY = \"surface\";\n\n    public static Optional<SurfaceTag> get(final Taggable taggable)\n    {\n        return Validators.from(SurfaceTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SwimmingPoolTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM swimming_pool tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/swimming_pool#values\")\npublic enum SwimmingPoolTag\n{\n    NO,\n    YES,\n    ABOVE,\n    WADING,\n    ENTRANCE,\n    PLUNGE;\n\n    @TagKey\n    public static final String KEY = \"swimming_pool\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticBoundaryNodeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Tag for boundary nodes that are created at boundaries.\n * <p>\n * This is not an OSM tag.\n *\n * @author matthieun\n */\n@Tag(synthetic = true)\npublic enum SyntheticBoundaryNodeTag\n{\n    YES,\n    EXISTING;\n\n    @TagKey\n    public static final String KEY = \"synthetic_boundary_node\";\n\n    private static EnumSet<SyntheticBoundaryNodeTag> ALL_BOUNDARY_NODES = EnumSet.of(YES, EXISTING);\n    private static EnumSet<SyntheticBoundaryNodeTag> SYNTHETIC_BOUNDARY_NODES = EnumSet.of(YES);\n    private static EnumSet<SyntheticBoundaryNodeTag> EXISTING_BOUNDARY_NODES = EnumSet.of(EXISTING);\n\n    public static boolean isBoundaryNode(final Taggable taggable)\n    {\n        final Optional<SyntheticBoundaryNodeTag> boundary = Validators\n                .from(SyntheticBoundaryNodeTag.class, taggable);\n        return boundary.isPresent() && ALL_BOUNDARY_NODES.contains(boundary.get());\n    }\n\n    public static boolean isExistingBoundaryNode(final Taggable taggable)\n    {\n        final Optional<SyntheticBoundaryNodeTag> boundary = Validators\n                .from(SyntheticBoundaryNodeTag.class, taggable);\n        return boundary.isPresent() && EXISTING_BOUNDARY_NODES.contains(boundary.get());\n    }\n\n    public static boolean isSyntheticBoundaryNode(final Taggable taggable)\n    {\n        final Optional<SyntheticBoundaryNodeTag> boundary = Validators\n                .from(SyntheticBoundaryNodeTag.class, taggable);\n        return boundary.isPresent() && SYNTHETIC_BOUNDARY_NODES.contains(boundary.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticDuplicateOsmNodeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Tag identifying an OSM Node that contains duplicate Node on top of it. This usually signifies a\n * data error. This is NOT an OSM tag.\n *\n * @author mgostintsev\n */\n@Tag(synthetic = true)\npublic enum SyntheticDuplicateOsmNodeTag\n{\n    YES;\n\n    @TagKey\n    public static final String KEY = \"synthetic_duplicate_osm_node\";\n\n    public static boolean isYes(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, SyntheticDuplicateOsmNodeTag.class, YES);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticGeometrySlicedTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Tag indicating an entity had its geometry sliced during country slicing\n * <p>\n * This is not an OSM tag.\n *\n * @author samg\n */\n\n@Tag(synthetic = true)\npublic enum SyntheticGeometrySlicedTag\n{\n    YES;\n\n    @TagKey\n    public static final String KEY = \"synthetic_geometry_sliced\";\n\n    public static boolean isGeometrySliced(final Taggable taggable)\n    {\n        return Validators.from(SyntheticGeometrySlicedTag.class, taggable).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticInvalidGeometryTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Tag indicating an entity does not conform to the OGC geometry specification.\n * <p>\n * This is not an OSM tag.\n *\n * @author samg\n */\n\n@Tag(synthetic = true)\npublic enum SyntheticInvalidGeometryTag\n{\n    YES;\n\n    @TagKey\n    public static final String KEY = \"synthetic_invalid_geometry\";\n\n    public static boolean isInvalidGeometry(final Taggable taggable)\n    {\n        return Validators.from(SyntheticInvalidGeometryTag.class, taggable).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticInvalidMultiPolygonRelationMembersRemovedTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Tag that tracks any invalid multipolygon members that were removed during country slicing\n * <p>\n * This is not an OSM tag.\n *\n * @author samg\n */\n@Tag(synthetic = true, value = Validation.NON_EMPTY_STRING)\npublic interface SyntheticInvalidMultiPolygonRelationMembersRemovedTag\n{\n    @TagKey\n    String KEY = \"synthetic_invalid_multipolygon_relation_members_removed\";\n\n    String MEMBER_DELIMITER = \",\";\n\n    /**\n     * The list of all added member identifiers\n     *\n     * @param taggable\n     *            The {@link Taggable} whose members we're interested in\n     * @return Iterable of all the added member identifiers for this item\n     */\n    static Optional<Iterable<Long>> all(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).map(tagValue -> Arrays.stream(tagValue.split(MEMBER_DELIMITER))\n                .map(Long::valueOf).filter(Objects::nonNull).collect(Collectors.toList()));\n    }\n\n    /**\n     * @param taggable\n     *            The {@link Taggable} we're looking at\n     * @return {@code true} if this item has removed invalid relation members\n     */\n    static boolean hasAddedRelationMember(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n\n    static String join(final Collection<String> ids)\n    {\n        return String.join(MEMBER_DELIMITER, ids);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticInvalidWaySectionTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Tag identifying an Atlas Edge that was the remnant of way-sectioning that exceeded the maximum\n * 999 slices. As a result, this edge contains the rest of the un-sectioned OSM Way. This usually\n * indicates a data error and is NOT an OSM tag.\n *\n * @author mgostintsev\n */\n@Tag(synthetic = true)\npublic enum SyntheticInvalidWaySectionTag\n{\n    YES;\n\n    @TagKey\n    public static final String KEY = \"synthetic_invalid_way_section\";\n\n    public static boolean isYes(final Taggable taggable)\n    {\n        return Validators.isOfType(taggable, SyntheticInvalidWaySectionTag.class, YES);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticRelationMemberAdded.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Tag that tracks any {@link Relation} member identifiers that were added during country-slicing.\n * <p>\n * This is not an OSM tag.\n *\n * @author mgostintsev\n */\n@Tag(synthetic = true, value = Validation.NON_EMPTY_STRING)\npublic interface SyntheticRelationMemberAdded\n{\n    @TagKey\n    String KEY = \"synthetic_relation_member_added\";\n\n    String MEMBER_DELIMITER = \",\";\n\n    /**\n     * The list of all added member identifiers\n     *\n     * @param taggable\n     *            The {@link Taggable} whose members we're interested in\n     * @return Iterable of all the added member identifiers for this item\n     */\n    static Optional<Iterable<Long>> all(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).map(tagValue -> Arrays.stream(tagValue.split(MEMBER_DELIMITER))\n                .map(Long::valueOf).filter(Objects::nonNull).collect(Collectors.toList()));\n    }\n\n    /**\n     * @param taggable\n     *            The {@link Taggable} we're looking at\n     * @return {@code true} if this item has added relation members\n     */\n    static boolean hasAddedRelationMember(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n\n    static String join(final Collection<String> ids)\n    {\n        return String.join(MEMBER_DELIMITER, ids);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticRelationRoleUpdated.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * Tag that tracks any {@link Relation} member role update. An example is an inner role changed to\n * an outer role. The format will be {relation-member-identifier}|{RoleChange}.\n * <p>\n * This is not an OSM tag.\n *\n * @author mgostintsev\n */\n@Tag(synthetic = true, value = Validation.NON_EMPTY_STRING)\npublic interface SyntheticRelationRoleUpdated\n{\n    /**\n     * @author mgostintsev\n     */\n    enum RoleChange\n    {\n        INNER_TO_OUTER,\n        OUTER_TO_INNER;\n\n        static RoleChange safeValueOf(final String value)\n        {\n            try\n            {\n                return valueOf(value.toUpperCase());\n            }\n            catch (final IllegalArgumentException ignored)\n            {\n                return null;\n            }\n        }\n    }\n\n    @TagKey\n    String KEY = \"synthetic_relation_role_updated\";\n\n    String ROLE_CHANGE_IDENTIFIER_DELIMITER = \"|\";\n\n    /**\n     * The optional Tuple of the member identifier and type of {@link RoleChange}\n     *\n     * @param taggable\n     *            The {@link Taggable} we're interested in\n     * @return the optional tuple of the member and type of {@link RoleChange}\n     */\n    static Optional<Tuple<Long, RoleChange>> all(final Taggable taggable)\n    {\n        final Optional<String> possibleUpdatedRole = taggable\n                .getTag(SyntheticRelationRoleUpdated.class, Optional.empty());\n\n        if (possibleUpdatedRole.isPresent())\n        {\n            final List<String> updatedRole = Arrays\n                    .asList(possibleUpdatedRole.get().split(ROLE_CHANGE_IDENTIFIER_DELIMITER));\n\n            return Optional.of(Tuple.createTuple(Long.valueOf(updatedRole.get(0)),\n                    RoleChange.safeValueOf(updatedRole.get(1))));\n        }\n\n        return Optional.empty();\n    }\n\n    /**\n     * @param taggable\n     *            The {@link Taggable} we're looking at\n     * @return {@code true} if this item has added relation members\n     */\n    static boolean hasUpdatedRelationMemberRole(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/SyntheticSyntheticRelationMemberTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Tag to indicate an entity is a synthetic relation member added during country slicing.\n * <p>\n * This is not an OSM tag.\n *\n * @author samg\n */\n@Tag(synthetic = true)\npublic enum SyntheticSyntheticRelationMemberTag\n{\n    YES;\n\n    @TagKey\n    public static final String KEY = \"synthetic_relation_member\";\n\n    public static boolean isSyntheticRelationMember(final Taggable taggable)\n    {\n        return Validators.from(SyntheticSyntheticRelationMemberTag.class, taggable).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/Taggable.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Tag;\n\n/**\n * Any class that should expose OSM tags can implement this interface for more complex interactions\n * with tag validation.\n *\n * @author cstaylor\n * @author matthieun\n */\npublic interface Taggable\n{\n    /**\n     * Options for the localized tag search:\n     * <ul>\n     * <li>DEFAULT - return the non-localized value of a tag if the localized value for a given\n     * language is not found</li>\n     * <li>LOCALIZED_ONLY - only return the localized value if the tag is a localized tag for a\n     * given language; empty otherwise</li>\n     * <li>FORCE_ALL_LOCALIZED_ONLY - same as LOCALIZED_ONLY but treat non-localized tags as\n     * localizable</li>\n     * </ul>\n     *\n     * @author cstaylor\n     */\n    enum TagSearchOption\n    {\n        DEFAULT,\n        LOCALIZED_ONLY,\n        FORCE_ALL_LOCALIZED_ONLY\n    }\n\n    static Taggable with(final Collection<Tag> tagCollection)\n    {\n        final Map<String, String> tags = new HashMap<>();\n        tagCollection.forEach(tag -> tags.put(tag.getKey(), tag.getValue()));\n        return with(tags);\n    }\n\n    static Taggable with(final Map<String, String> tags)\n    {\n        return new Taggable()\n        {\n            @Override\n            public Optional<String> getTag(final String key)\n            {\n                return Optional.ofNullable(tags.get(key));\n            }\n\n            @Override\n            public Map<String, String> getTags()\n            {\n                return tags;\n            }\n\n            @Override\n            public String toString()\n            {\n                return tags.toString();\n            }\n        };\n    }\n\n    static Taggable with(final String... tags)\n    {\n        return with(Maps.hashMap(tags));\n    }\n\n    /**\n     * Utility function to test if an entity's tag value starts with some given values.\n     *\n     * @param key\n     *            The tag key\n     * @param matches\n     *            The matching values\n     * @return True if the tag's value matches at least one of the matching values.\n     */\n    default boolean containsValue(final String key, final Iterable<String> matches)\n    {\n        final Optional<String> valueOption = getTag(key);\n        if (valueOption.isPresent())\n        {\n            final String value = valueOption.get();\n            for (final String candidate : matches)\n            {\n                if (candidate.startsWith(\"!\"))\n                {\n                    if (candidate.length() > 1)\n                    {\n                        final String forbiddenValue = candidate.substring(1);\n                        if (!value.equalsIgnoreCase(forbiddenValue))\n                        {\n                            return true;\n                        }\n                    }\n                    else\n                    {\n                        return false;\n                    }\n                }\n                if (\"*\".equals(candidate) || value.equalsIgnoreCase(candidate))\n                {\n                    return true;\n                }\n                if (candidate != null && candidate.startsWith(\"*\")\n                        && value.endsWith(candidate.substring(1)))\n                {\n                    return true;\n                }\n                if (candidate != null && candidate.endsWith(\"*\")\n                        && value.startsWith(candidate.substring(0, candidate.length() - 1)))\n                {\n                    return true;\n                }\n            }\n        }\n        else\n        {\n            for (final String candidate : matches)\n            {\n                if (!candidate.startsWith(\"!\"))\n                {\n                    return false;\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Neat single place where we can get tags by their language if they exist, otherwise return the\n     * default without the localized name\n     *\n     * @param key\n     *            the base key we want to find\n     * @param language\n     *            the optional language's version of the tag we want to find\n     * @param searchOptions\n     *            search options for finding the value of a key. Sometimes we only want to check the\n     *            localized level and not bring in the non-localized value\n     * @return the value of the localized version of the tag if it exists, the value of the base key\n     *         if the localized one is missing, or an empty optional if neither are available\n     */\n    default Optional<String> getTag(final Class<?> key, final Optional<IsoLanguage> language,\n            final TagSearchOption... searchOptions)\n    {\n        final EnumSet<TagSearchOption> searchOptionSet = searchOptions.length > 0\n                ? EnumSet.copyOf(Arrays.asList(searchOptions))\n                : EnumSet.noneOf(TagSearchOption.class);\n\n        final Optional<String> localizedKeyName = Validators.localizeKeyName(key, language,\n                searchOptions);\n        if (localizedKeyName.isPresent())\n        {\n            final Optional<String> localizedValue = getTag(localizedKeyName.get());\n            if (localizedValue.isPresent()\n                    || searchOptionSet.contains(TagSearchOption.LOCALIZED_ONLY)\n                    || searchOptionSet.contains(TagSearchOption.FORCE_ALL_LOCALIZED_ONLY))\n            {\n                return localizedValue;\n            }\n            final Optional<String> optionalKey = Validators.localizeKeyName(key, Optional.empty());\n            if (optionalKey.isPresent())\n            {\n                return getTag(optionalKey.get());\n            }\n        }\n        return Optional.empty();\n    }\n\n    Optional<String> getTag(String key);\n\n    /**\n     * Some taggables support fetching all keys, some don't\n     *\n     * @return all of the tag keys and their values\n     */\n    default Map<String, String> getTags()\n    {\n        return new HashMap<>();\n    }\n\n    /**\n     * Will retrieve tags based on a filter\n     *\n     * @param filter\n     *            The predicate to test each tag by\n     * @return The map of filtered tags.\n     */\n    default Map<String, String> getTags(final Predicate<String> filter)\n    {\n        return this.getTags().entrySet().stream().filter(item -> filter.test(item.getKey()))\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n    }\n\n    /**\n     * @param tags\n     *            A tag map to compare to\n     * @return True if this contains at least one tag specified in the tag map\n     */\n    default boolean hasAtLeastOneOf(final Map<String, String> tags)\n    {\n        for (final Map.Entry<String, String> entry : tags.entrySet())\n        {\n            final String key = entry.getKey();\n            final String value = entry.getValue();\n            final Optional<String> myValue = getTag(key);\n\n            if (myValue.isPresent())\n            {\n                if (\"*\".equals(value))\n                {\n                    return true;\n                }\n                if (value.equals(myValue.get()))\n                {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return the set of languages explicitly set on the tag provided.\n     *\n     * @param tag\n     *            check this tag on this Taggable for explicitly defined languages\n     * @param searchOptions\n     *            optional list of flags that alter the behavior of the underlying tag value search\n     * @return the optional set of explicitly listed languages found\n     */\n    default Optional<Set<IsoLanguage>> languagesFor(final Class<?> tag,\n            final TagSearchOption... searchOptions)\n    {\n        final TreeSet<IsoLanguage> returnValue = new TreeSet<>();\n\n        final EnumSet<TagSearchOption> searchOptionSet = searchOptions.length > 0\n                ? EnumSet.copyOf(Arrays.asList(searchOptions))\n                : EnumSet.noneOf(TagSearchOption.class);\n\n        if (!searchOptionSet.contains(TagSearchOption.FORCE_ALL_LOCALIZED_ONLY)\n                && !Validators.hasLocalizedTagKey(tag))\n        {\n            throw new CoreException(\"{} isn't a localizable tag\", tag.getName());\n        }\n\n        final String prefix = Validators.TagKeySearch.findTagKeyIn(tag)\n                .orElseThrow(\n                        () -> new CoreException(\"Could not find key for tag {}\", tag.getName()))\n                .getKeyName();\n        final int prefixLength = prefix.length();\n        for (final String key : this.getTags().keySet())\n        {\n            if (key.startsWith(prefix) && key.length() > prefixLength)\n            {\n                final LocalizedTagNameWithOptionalDate parser = new LocalizedTagNameWithOptionalDate(\n                        key);\n                parser.getLanguage().ifPresent(returnValue::add);\n            }\n        }\n\n        return Optional.of(returnValue);\n    }\n\n    /**\n     * Get the value for this tag key\n     *\n     * @param key\n     *            The tag key\n     * @return The value. null if the value does not exist\n     */\n    default String tag(final String key)\n    {\n        return getTag(key).orElse(null);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TemporaryDateOnTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM temporary:date_on tag\n *\n * @author brianjor\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/temporary:date_on#values\", osm = \"https://wiki.openstreetmap.org/wiki/Item:Q15233\")\npublic interface TemporaryDateOnTag\n{\n    @TagKey\n    String KEY = \"temporary:date_on\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TollTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM toll tag\n *\n * @author robert_stack\n * @author pmi\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/?key=toll#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:toll\")\npublic enum TollTag\n{\n    YES,\n    NO,\n    SNOWMOBILE;\n\n    @TagKey\n    public static final String KEY = \"toll\";\n\n    public static boolean isYes(final Taggable taggable)\n    {\n        final Taggable checkMe = taggable;\n        return Validators.isOfType(checkMe, TollTag.class, TollTag.YES);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TourismTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM tourism tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/tourism#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:tourism\")\npublic enum TourismTag\n{\n    INFORMATION,\n    HOTEL,\n    ATTRACTION,\n    VIEWPOINT,\n    PICNIC_SITE,\n    CAMP_SITE,\n    GUEST_HOUSE,\n    MUSEUM,\n    ARTWORK,\n    CHALET,\n    MOTEL,\n    HOSTEL,\n    CARAVAN_SITE,\n    ALPINE_HUT,\n    THEME_PARK,\n    ZOO,\n    YES,\n    APARTMENT,\n    WILDERNESS_HUT,\n    GALLERY,\n    BED_AND_BREAKFAST,\n    WINE_CELLAR,\n    RESORT,\n    AQUARIUM;\n\n    @TagKey\n    public static final String KEY = \"tourism\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TracktypeTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM tracktype tag\n *\n * @author robert_stack\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/tracktype#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:tracktype\")\npublic enum TracktypeTag\n{\n    GRADE1,\n    GRADE2,\n    GRADE3,\n    GRADE4,\n    GRADE5;\n\n    @TagKey\n    public static final String KEY = \"tracktype\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TrafficCalmingTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM traffic_calming tag\n *\n * @author mgostintsev\n */\n@Tag(taginfo = \"http://wiki.openstreetmap.org/wiki/Key:traffic_calming#Common_values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:traffic_calming\")\npublic enum TrafficCalmingTag\n{\n    BUMP,\n    HUMP,\n    TABLE,\n    CUSHION,\n    RUMBLE_STRIP,\n    CHICANE,\n    CHOKER,\n    ISLAND;\n\n    @TagKey\n    public static final String KEY = \"traffic_calming\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TunnelTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM tunnel tag\n *\n * @author robert_stack\n * @author pmi\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/tunnel#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:tunnel\")\npublic enum TunnelTag\n{\n    YES,\n    CULVERT,\n    BUILDING_PASSAGE,\n    FLOODED,\n    NO;\n\n    private static final EnumSet<TunnelTag> TUNNEL_WAYS = EnumSet.of(YES, CULVERT, BUILDING_PASSAGE,\n            FLOODED);\n\n    @TagKey\n    public static final String KEY = \"tunnel\";\n\n    public static boolean isTunnel(final Taggable taggable)\n    {\n        final Optional<TunnelTag> tunnel = Validators.from(TunnelTag.class, taggable);\n        return tunnel.isPresent() && TUNNEL_WAYS.contains(tunnel.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TurnLanesBackwardTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM turn:lanes:backward tag indicating the lane types for a two-way\n *\n * @author brian_l_davis\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/turn%3Alanes%3Abackward#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:turn\")\npublic interface TurnLanesBackwardTag extends TurnLanesTag\n{\n    @TagKey\n    String KEY = \"turn:lanes:backward\";\n\n    /**\n     * The list of turn types specified in the turn:lanes:backward tag\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return The list of turn types when tagged\n     */\n    static Optional<List<Set<TurnType>>> getBackwardTurnLanes(final Taggable taggable)\n    {\n        return taggable.getTag(KEY)\n                .map(tagValue -> Arrays.stream(tagValue.split(TURN_LANE_DELIMITER))\n                        .map(lane -> Arrays.stream(lane.split(TURN_TYPE_DELIMITER))\n                                .map(TurnType::safeValueOf).filter(Objects::nonNull)\n                                .collect(Collectors.toSet()))\n                        .collect(Collectors.toList()));\n    }\n\n    /**\n     * Checks if the {@link Taggable} has the {@link TurnType} in the {@code turn:lanes:backward}\n     * tags.\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @param turnType\n     *            A turn type to test for\n     * @return {@code true} if tagged with the turn type, otherwise {@code false}\n     */\n    static boolean hasBackwardTurnLane(final Taggable taggable, final TurnType turnType)\n    {\n        return getBackwardTurnLanes(taggable)\n                .map(lanes -> lanes.stream().anyMatch(turnLane -> turnLane.contains(turnType)))\n                .orElse(false);\n    }\n\n    /**\n     * Checks if the {@link Taggable} has a {@code turn:lanes:backward} tag value.\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return {@code true} if tagged, otherwise {@code false}\n     */\n    static boolean hasBackwardTurnLane(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TurnLanesForwardTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM turn:lanes:forward tag indicating the lane types for a two-way\n *\n * @author brian_l_davis\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/turn%3Alanes%3Aforward#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:turn\")\npublic interface TurnLanesForwardTag extends TurnLanesTag\n{\n    @TagKey\n    String KEY = \"turn:lanes:forward\";\n\n    /**\n     * The list of turn types specified in the turn:lanes:forward tag\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return The list of turn types when tagged\n     */\n    static Optional<List<Set<TurnType>>> getForwardTurnLanes(final Taggable taggable)\n    {\n        return taggable.getTag(KEY)\n                .map(tagValue -> Arrays.stream(tagValue.split(TURN_LANE_DELIMITER))\n                        .map(lane -> Arrays.stream(lane.split(TURN_TYPE_DELIMITER))\n                                .map(TurnType::safeValueOf).filter(Objects::nonNull)\n                                .collect(Collectors.toSet()))\n                        .collect(Collectors.toList()));\n    }\n\n    /**\n     * Checks if the {@link Taggable} has the {@link TurnType} in the {@code turn:lanes:forward}\n     * tags.\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @param turnType\n     *            A turn type to test for\n     * @return {@code true} if tagged with the turn type, otherwise {@code false}\n     */\n    static boolean hasForwardTurnLane(final Taggable taggable, final TurnType turnType)\n    {\n        return getForwardTurnLanes(taggable)\n                .map(lanes -> lanes.stream().anyMatch(turnLane -> turnLane.contains(turnType)))\n                .orElse(false);\n    }\n\n    /**\n     * Checks if the {@link Taggable} has a {@code turn:lanes:forward} tag value.\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return {@code true} if tagged, otherwise {@code false}\n     */\n    static boolean hasForwardTurnLane(final Taggable taggable)\n    {\n        return taggable.getTag(KEY).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TurnLanesTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.OptionalIterable;\n\n/**\n * OSM turn:lanes tag indicating the lane types for a one-way road\n *\n * @author brian_l_davis\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/turn%3Alanes#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:turn\")\npublic interface TurnLanesTag extends TurnTag\n{\n    @TagKey\n    String KEY = \"turn:lanes\";\n\n    /**\n     * The list of turn types specified in the turn:lanes tag\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return The list of turn types when tagged\n     */\n    static Optional<List<Set<TurnType>>> getTurnLanes(final Taggable taggable)\n    {\n        return taggable.getTag(KEY)\n                .map(tagValue -> Arrays.stream(tagValue.split(TURN_LANE_DELIMITER))\n                        .map(lane -> Arrays.stream(lane.split(TURN_TYPE_DELIMITER))\n                                .map(TurnType::safeValueOf).filter(Objects::nonNull)\n                                .collect(Collectors.toSet()))\n                        .collect(Collectors.toList()));\n    }\n\n    /**\n     * Checks if the {@link Taggable} has the {@link TurnType} in any of the\n     * {@code turn:lanes[:forward|:backward]} tags.\n     * \n     * @param taggable\n     *            The taggable object being test\n     * @param turnType\n     *            A turn type to test for\n     * @return {@code true} if tagged with the turn type, otherwise {@code false}\n     */\n    @SuppressWarnings(\"unchecked\")\n    static boolean hasTurnLane(final Taggable taggable, final TurnType turnType)\n    {\n        final OptionalIterable<List<Set<TurnType>>> turnLanes = new OptionalIterable<>(Iterables\n                .iterable(getTurnLanes(taggable), TurnLanesForwardTag.getForwardTurnLanes(taggable),\n                        TurnLanesBackwardTag.getBackwardTurnLanes(taggable)));\n        return Iterables.asList(turnLanes).stream().anyMatch(\n                lanes -> lanes.stream().anyMatch(turnLane -> turnLane.contains(turnType)));\n    }\n\n    /**\n     * Checks if the {@link Taggable} has a {@code turn[:lanes[:forward|:backward]]} tag value.\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return {@code true} if tagged, otherwise {@code false}\n     */\n    static boolean hasTurnLane(final Taggable taggable)\n    {\n        return taggable.getTag(TurnLanesTag.KEY).isPresent()\n                || taggable.getTag(TurnLanesForwardTag.KEY).isPresent()\n                || taggable.getTag(TurnLanesBackwardTag.KEY).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TurnRestrictionTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Tag for a turn restriction relation\n *\n * @author matthieun\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/restriction#values\", osm = \"http://wiki.openstreetmap.org/wiki/Relation:restriction\")\npublic enum TurnRestrictionTag\n{\n    NO_RIGHT_TURN,\n    NO_LEFT_TURN,\n    NO_U_TURN,\n    NO_STRAIGHT_ON,\n    ONLY_RIGHT_TURN,\n    ONLY_LEFT_TURN,\n    ONLY_STRAIGHT_ON,\n    NO_ENTRY,\n    NO_EXIT;\n\n    @TagKey\n    public static final String KEY = \"restriction\";\n\n    private static final EnumSet<TurnRestrictionTag> NO_PATH_RESTRICTIONS = EnumSet\n            .of(NO_RIGHT_TURN, NO_LEFT_TURN, NO_U_TURN, NO_STRAIGHT_ON, NO_ENTRY, NO_EXIT);\n    private static final EnumSet<TurnRestrictionTag> ONLY_PATH_RESTRICTIONS = EnumSet\n            .of(ONLY_RIGHT_TURN, ONLY_LEFT_TURN, ONLY_STRAIGHT_ON);\n\n    public static boolean isNoPathRestriction(final Taggable taggable)\n    {\n        final Optional<TurnRestrictionTag> turnRestriction = Validators\n                .from(TurnRestrictionTag.class, taggable);\n        return turnRestriction.isPresent() && NO_PATH_RESTRICTIONS.contains(turnRestriction.get());\n    }\n\n    public static boolean isOnlyPathRestriction(final Taggable taggable)\n    {\n        final Optional<TurnRestrictionTag> turnRestriction = Validators\n                .from(TurnRestrictionTag.class, taggable);\n        return turnRestriction.isPresent()\n                && ONLY_PATH_RESTRICTIONS.contains(turnRestriction.get());\n    }\n\n    public static boolean isRestriction(final Taggable taggable)\n    {\n        final Optional<TurnRestrictionTag> turnRestriction = Validators\n                .from(TurnRestrictionTag.class, taggable);\n        return turnRestriction.isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/TurnTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.OptionalIterable;\n\n/**\n * Base OSM turn tag indicating a turn direction\n *\n * @author brian_l_davis\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/turn#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:turn\")\npublic interface TurnTag\n{\n    /**\n     * @author brian_l_davis\n     */\n    enum TurnType\n    {\n        LEFT,\n        SHARP_LEFT,\n        SLIGHT_LEFT,\n        THROUGH,\n        RIGHT,\n        SHARP_RIGHT,\n        SLIGHT_RIGHT,\n        REVERSE,\n        MERGE_TO_LEFT,\n        MERGE_TO_RIGHT,\n        NONE;\n\n        static final EnumSet<TurnType> leftTurn = EnumSet.of(LEFT, SHARP_LEFT, SLIGHT_LEFT,\n                MERGE_TO_LEFT);\n        static final EnumSet<TurnType> rightTurn = EnumSet.of(RIGHT, SHARP_RIGHT, SLIGHT_RIGHT,\n                MERGE_TO_RIGHT);\n\n        static TurnType safeValueOf(final String value)\n        {\n            try\n            {\n                return valueOf(value.toUpperCase());\n            }\n            catch (final IllegalArgumentException ignored)\n            {\n                return null;\n            }\n        }\n    }\n\n    @TagKey\n    String KEY = \"turn\";\n    String TURN_LANE_DELIMITER = \"\\\\|\";\n    String TURN_TYPE_DELIMITER = \";\";\n\n    /**\n     * The list of turn types specified in the turn tag\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return The list of turn types when tagged\n     */\n    static Optional<List<Set<TurnType>>> getTurns(final Taggable taggable)\n    {\n        return taggable.getTag(KEY)\n                .map(tagValue -> Arrays.stream(tagValue.split(TURN_LANE_DELIMITER))\n                        .map(lane -> Arrays.stream(lane.split(TURN_TYPE_DELIMITER))\n                                .map(TurnType::safeValueOf).filter(Objects::nonNull)\n                                .collect(Collectors.toSet()))\n                        .collect(Collectors.toList()));\n    }\n\n    /**\n     * Checks if the {@link Taggable} has the {@link TurnType} in any of the\n     * {@code turn[:lanes[:forward|:backward]]} tags.\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @param turnType\n     *            A turn type to test for\n     * @return {@code true} if tagged with the turn type, otherwise {@code false}\n     */\n    @SuppressWarnings(\"unchecked\")\n    static boolean hasTurn(final Taggable taggable, final TurnType turnType)\n    {\n        final OptionalIterable<List<Set<TurnType>>> turnLanes = new OptionalIterable<>(\n                Iterables.iterable(getTurns(taggable), TurnLanesTag.getTurnLanes(taggable),\n                        TurnLanesForwardTag.getForwardTurnLanes(taggable),\n                        TurnLanesBackwardTag.getBackwardTurnLanes(taggable)));\n        return Iterables.asList(turnLanes).stream().anyMatch(\n                lanes -> lanes.stream().anyMatch(turnLane -> turnLane.contains(turnType)));\n    }\n\n    /**\n     * Checks if the {@link Taggable} has a {@code turn[:lanes[:forward|:backward]]} tag value.\n     *\n     * @param taggable\n     *            The taggable object being test\n     * @return {@code true} if tagged, otherwise {@code false}\n     */\n    static boolean hasTurn(final Taggable taggable)\n    {\n        return taggable.getTag(TurnTag.KEY).isPresent()\n                || taggable.getTag(TurnLanesTag.KEY).isPresent()\n                || taggable.getTag(TurnLanesForwardTag.KEY).isPresent()\n                || taggable.getTag(TurnLanesBackwardTag.KEY).isPresent();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/URLTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM url tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.URI, taginfo = \"http://taginfo.openstreetmap.org/keys/url#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:url\")\npublic interface URLTag\n{\n    @TagKey\n    String KEY = \"url\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/UsageTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM usage tag\n *\n * @author jacker\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/usage#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:usage\")\npublic enum UsageTag\n{\n    MAIN,\n    BRANCH,\n    INDUSTRIAL,\n    FREIGHT,\n    TOURISM,\n    MILITARY,\n    YARD,\n    STOCK,\n    DISTRIBUTION,\n    FACILITY,\n    TRANSMISSION,\n    TEST;\n\n    @TagKey\n    public static final String KEY = \"usage\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/VacantTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM vacant tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/vacant#values\")\npublic enum VacantTag\n{\n    YES,\n    SELL,\n    RENT,\n    RESTAURANT,\n    RENTAL,\n    NO,\n    PUB,\n    CONSTRUCTION,\n    SHOP;\n\n    @TagKey\n    public static final String KEY = \"vacant\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/VehicleTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM vehicle tag\n *\n * @author mkalender\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/vehicle#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:vehicle\")\npublic enum VehicleTag\n{\n    YES,\n    NO,\n    PRIVATE,\n    AGRICULTURAL,\n    PERMISSIVE,\n    DESTINATION,\n    DESIGNATED,\n    DELIVERY,\n    FORESTRY,\n    OFFICIAL,\n    CUSTOMERS,\n    EMERGENCY,\n    BUS,\n    SERVICE,\n    UNKNOWN;\n\n    @TagKey\n    public static final String KEY = \"vehicle\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/VendingTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM's vending tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/vending#values\", osm = \"http://wiki.openstreetmap.org/wiki/Tag:amenity%3Dvending_machine\")\npublic enum VendingTag\n{\n    PARKING_TICKETS,\n    CIGARETTES,\n    EXCREMENT_BAGS,\n    PUBLIC_TRANSPORT_TICKETS,\n    DRINKS,\n    SWEETS,\n    PARCEL_PICKUP,\n    PARCEL_MAIL_IN,\n    CONDOMS,\n    NEWS_PAPERS,\n    STAMPS,\n    BICYCLE_TUBE,\n    FUEL,\n    GAS;\n\n    @TagKey\n    public static final String KEY = \"vending\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WaterTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM water tag\n *\n * @author Sid\n * @author cstaylor\n * @author mgostintsev\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/water#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:water\")\npublic enum WaterTag\n{\n    INTERMITTENT,\n    LAKE,\n    LAGOON,\n    POND,\n    REFLECTING_POOL,\n    RESERVOIR,\n    BASIN,\n    CANAL,\n    RIVER,\n    FISH_PASS,\n    OXBOW,\n    LOCK,\n    MOAT,\n    WASTEWATER,\n    STREAM_POOL,\n    SEA,\n    TIDAL,\n    SALT_POOL,\n    POOL;\n\n    @TagKey\n    public static final String KEY = \"water\";\n\n    public static Optional<WaterTag> get(final Taggable taggable)\n    {\n        return Validators.from(WaterTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WaterwayTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM waterway tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/waterway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:waterway\")\npublic enum WaterwayTag\n{\n    STREAM,\n    DITCH,\n    RIVER,\n    DRAIN,\n    RIVERBANK,\n    CANAL,\n    DAM,\n    WEIR,\n    RAPIDS,\n    WATERFALL,\n    LOCK_GATE,\n    WADI,\n    DRYSTREAM,\n    DOCK,\n    BOATYARD,\n    DERELICT_CANAL,\n    MILESTONE,\n    BROOK,\n    TURNING_POINT,\n    FUEL,\n    FISH_PASS,\n    WATER_POINT;\n\n    @TagKey\n    public static final String KEY = \"waterway\";\n\n    public static Optional<WaterwayTag> get(final Taggable taggable)\n    {\n        return Validators.from(WaterwayTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WebsiteTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM website tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.URI, taginfo = \"http://taginfo.openstreetmap.org/keys/website#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:website\")\npublic interface WebsiteTag\n{\n    @TagKey\n    String KEY = \"website\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WetlandTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM wetland tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/wetland#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:wetland\")\npublic enum WetlandTag\n{\n    BOG,\n    MARSH,\n    SWAMP,\n    REEDBED,\n    TIDALFLAT,\n    MANGROVE,\n    WET_MEADOW,\n    SALTMARSH,\n    STRING_BOG,\n    SALTERN,\n    FEN;\n\n    @TagKey\n    public static final String KEY = \"wetland\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WheelchairDescriptionTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM wheelchair:description tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/wheelchair:description#values\")\npublic interface WheelchairDescriptionTag\n{\n    @TagKey\n    String KEY = \"wheelchair:description\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WheelchairTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM wheelchair tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/wheelchair#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key%3Awheelchair\")\npublic enum WheelchairTag\n{\n    YES,\n    NO,\n    LIMITED;\n\n    @TagKey\n    public static final String KEY = \"wheelchair\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WidthTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.extraction.LengthExtractor;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * OSM width tag\n *\n * @author cstaylor\n * @author bbreithaupt\n */\n@Tag(value = Validation.DOUBLE, range = @Range(min = 0, max = Integer.MAX_VALUE), taginfo = \"http://taginfo.openstreetmap.org/keys/width#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:width\")\npublic interface WidthTag\n{\n    @TagKey\n    String KEY = \"width\";\n\n    static Optional<Distance> get(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return LengthExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WifiTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM wifi tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/wifi#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:internet_access\")\npublic interface WifiTag\n{\n    @TagKey\n    String KEY = \"wifi\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WikidataTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\nimport org.openstreetmap.atlas.tags.annotations.TagValue;\nimport org.openstreetmap.atlas.tags.annotations.TagValue.ValueType;\n\n/**\n * OSM wikidata tag\n *\n * @author cstaylor\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/wikidata#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:wikidata\")\npublic interface WikidataTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"wikidata\";\n\n    @TagValue(ValueType.REGEX)\n    String WIKI_DATA = \"Q[1-9]\\\\d*\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WikipediaTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM wikipedia tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/wikipedia#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:wikipedia\")\npublic interface WikipediaTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"wikipedia\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/WinterRoadTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM Winter Tag.\n *\n * @author sayas01\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/winter_road#values\", osm = \"https://wiki.openstreetmap.org/wiki/Key:winter_road\")\npublic enum WinterRoadTag\n{\n    YES,\n    NO;\n\n    @TagKey\n    public static final String KEY = \"winter_road\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/ZooTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM zoo tag\n *\n * @author stephencerqueira\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/zoo#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key%zoo\")\npublic enum ZooTag\n{\n    AVIARY,\n    BIRDS,\n    ENCLOSURE,\n    FALCONRY,\n    PETTING_ZOO,\n    REPTILE,\n    SAFARI_PARK,\n    WILDLIFE_PARK;\n\n    @TagKey\n    public static final String KEY = \"zoo\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/Tag.java",
    "content": "package org.openstreetmap.atlas.tags.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Java annotation for Atlas tags including how we should validate their values at runtime\n *\n * @author cstaylor\n */\n@Target(value = ElementType.TYPE_USE)\n@Retention(value = RetentionPolicy.RUNTIME)\npublic @interface Tag\n{\n    /**\n     * This is an optional range of values that can be used in numeric validators\n     *\n     * @author cstaylor\n     */\n    public @interface Range\n    {\n        // These are values that shouldn't be allowed, even if they fall inside the range\n        long[] exclude() default {};\n\n        // Values aren't mixed up: these are sentinel values that disable the range\n        long max() default Long.MIN_VALUE;\n\n        long min() default Long.MAX_VALUE;\n    }\n\n    /**\n     * Validation rules for the values of a tag:\n     * <ul>\n     * <li>MATCH - an exact match against a set of values</li>\n     * <li>ORDINAL - a positive integer. Also implies MATCH for any defined TagValues in the Tag\n     * </li>\n     * <li>LONG - a long of any value constrained by the optional range. Also implies MATCH for any\n     * defined TagValues in the Tag</li>\n     * <li>DOUBLE - parse the value as a double. Also implies MATCH for any defined TagValues in the\n     * Tag</li>\n     * <li>TIMESTAMP - parse the value as a long and create a Date from that value. Also implies\n     * MATCH for any defined TagValues in the Tag</li>\n     * <li>NON_EMPTY_STRING - valid if the value has at least one non-whitespace character. Does NOT\n     * imply MATCH</li>\n     * <li>ISO_COUNTRY - valid if the value matches an entry in the ISO country code list. Does NOT\n     * imply MATCH</li>\n     * <li>NONE - no validation performed</li>\n     * <li>URI - check if the value if a well-formed URI</li>\n     * <li>SPEED - check if the value is a well-formed speed</li>\n     * <li>LENGTH - check if the value is a well-formed length</li>\n     * </ul>\n     *\n     * @author cstaylor\n     */\n    enum Validation\n    {\n        MATCH,\n        ORDINAL,\n        LONG,\n        DOUBLE,\n        TIMESTAMP,\n        NON_EMPTY_STRING,\n        ISO3_COUNTRY,\n        ISO2_COUNTRY,\n        NONE,\n        URI,\n        SPEED,\n        LENGTH;\n    }\n\n    /**\n     * Optional URL to OSM wiki page for this tag\n     *\n     * @return the URL or an empty string if no tag is defined\n     */\n    String osm() default \"\";\n\n    Range range() default @Range();\n\n    /**\n     * If true, this tag is an artifact of processing and does not exist in OSM itself.\n     *\n     * @return true if this tag has been marked as synthetic, false otherwise\n     */\n    boolean synthetic() default false;\n\n    /**\n     * Optional URL to taginfo site for this tag\n     *\n     * @return the URL or an empty string if no tag is defined\n     */\n    String taginfo() default \"\";\n\n    /**\n     * The validation rule for this particular Atlas Tag\n     *\n     * @return the defined Validation rule: defaults to MATCH\n     */\n    Validation value() default Validation.MATCH;\n\n    /**\n     * This lets tags use shared enum values. For example shop and disused:shop\n     *\n     * @return the optional array of enums to copy their names for exact matches\n     */\n    Class<? extends Enum<?>>[] with() default {};\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/TagKey.java",
    "content": "package org.openstreetmap.atlas.tags.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Java annotation for an Atlas tag's key field. We can find this value at runtime using java\n * introspection without resorting to idioms like 'KEY_' or '_TAG'\n *\n * @author cstaylor\n */\n@Retention(value = RetentionPolicy.RUNTIME)\n@Target(value = ElementType.FIELD)\npublic @interface TagKey\n{\n    /**\n     * Some keys in OSM can be localized (for example, wikipedia:[language ISO2 code])\n     *\n     * @author cstaylor\n     */\n    enum KeyType\n    {\n        EXACT,\n        LOCALIZED\n    }\n\n    KeyType value() default KeyType.EXACT;\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/TagValue.java",
    "content": "package org.openstreetmap.atlas.tags.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Java annotation for constants in an Atlas tag definition for Tag.Validation.MATCH rules\n *\n * @author cstaylor\n */\n@Retention(value = RetentionPolicy.RUNTIME)\n@Target(value = ElementType.FIELD)\npublic @interface TagValue\n{\n    /**\n     * We can support exact string match values or regular expression patterns. The default is exact\n     * string matches\n     *\n     * @author cstaylor\n     */\n    enum ValueType\n    {\n        EXACT,\n        REGEX;\n    }\n\n    ValueType value() default ValueType.EXACT;\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/TagValueAs.java",
    "content": "package org.openstreetmap.atlas.tags.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for changing how Validators will interpret a tag's value. Only applies for enums\n * constants since their values are usually directly lower-cased.\n *\n * @author cstaylor\n */\n@Retention(value = RetentionPolicy.RUNTIME)\n@Target(value = ElementType.FIELD)\npublic @interface TagValueAs\n{\n    boolean deprecated() default false;\n\n    String value();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/TagValueDeprecated.java",
    "content": "package org.openstreetmap.atlas.tags.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * If a particular TagValue shouldn't be used anymore, we can mark it with this annotation. In the\n * future the tag integrity check can use this information to flag tag values as obsolete\n *\n * @author cstaylor\n */\n@Retention(value = RetentionPolicy.RUNTIME)\n@Target(value = ElementType.FIELD)\npublic @interface TagValueDeprecated\n{\n    boolean value() default true;\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/extraction/AltitudeExtractor.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * Extracts a {@link Altitude} from a value. Can be used on tag values such as\n * {@link org.openstreetmap.atlas.tags.HeightTag}.\n *\n * @author bbreithaupt\n */\npublic class AltitudeExtractor implements TagExtractor\n{\n    /**\n     * Validates and converts a value to a {@link Altitude}.\n     *\n     * @param value\n     *            {@link String} value.\n     * @return {@link Optional} of a {@link Altitude}\n     */\n    public static Optional<Altitude> validateAndExtract(final String value)\n    {\n        if (value.startsWith(\"-\"))\n        {\n            final Optional<Distance> distance = LengthExtractor\n                    .validateAndExtract(value.substring(1));\n            return distance.map(distance1 -> Altitude.meters(distance1.asMeters() * -1));\n        }\n        final Optional<Distance> distance = LengthExtractor.validateAndExtract(value);\n        return distance.map(distance1 -> Altitude.meters(distance1.asMeters()));\n    }\n\n    @Override\n    public Optional<Altitude> validateAndExtract(final String value, final Tag tag)\n    {\n        return AltitudeExtractor.validateAndExtract(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/extraction/IsoCountryExtractor.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.locale.IsoCountry;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * Extracts ISO2 and ISO3 country code values.\n *\n * @author mgostintsev\n */\npublic class IsoCountryExtractor implements TagExtractor<List<IsoCountry>>\n{\n    @Override\n    public Optional<List<IsoCountry>> validateAndExtract(final String value, final Tag tag)\n    {\n        final List<IsoCountry> countries = StringList.split(value, ISOCountryTag.COUNTRY_DELIMITER)\n                .stream().map(IsoCountry::forCountryCode).filter(Optional::isPresent)\n                .map(Optional::get).collect(Collectors.toList());\n        return Optional.of(countries);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/extraction/LengthExtractor.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.validation.LengthValidator;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * Extracts a {@link Distance} from a value. Can be used on tag values such as\n * {@link org.openstreetmap.atlas.tags.WidthTag}.\n *\n * @author bbreithaupt\n */\npublic class LengthExtractor implements TagExtractor\n{\n    private static final LengthValidator VALIDATOR = new LengthValidator();\n    private static final String SINGLE_SPACE = \" \";\n    private static final String COMMA = \",\";\n    private static final String DECIMAL_POINT = \".\";\n\n    /**\n     * Validates and converts a value to a {@link Distance}.\n     *\n     * @param value\n     *            {@link String} value.\n     * @return {@link Optional} of a {@link Distance}\n     */\n    public static Optional<Distance> validateAndExtract(final String value)\n    {\n        // Some countries use comma as decimal separator\n        // (https://en.wikipedia.org/wiki/Decimal_separator#Hindu-Arabic_numerals). Replace comma\n        // with decimal point for further processing.\n        final String uppercaseValue = value.replace(COMMA, DECIMAL_POINT).toUpperCase();\n        if (VALIDATOR.isValid(uppercaseValue))\n        {\n            if (uppercaseValue.endsWith(SINGLE_SPACE + Distance.UnitAbbreviations.M))\n            {\n                return Optional.of(Distance.meters(Double.valueOf(uppercaseValue.substring(0,\n                        uppercaseValue.lastIndexOf(SINGLE_SPACE + Distance.UnitAbbreviations.M)))));\n            }\n            else if (uppercaseValue.endsWith(SINGLE_SPACE + Distance.UnitAbbreviations.KM))\n            {\n                return Optional.of(Distance\n                        .kilometers(Double.valueOf(uppercaseValue.substring(0, uppercaseValue\n                                .lastIndexOf(SINGLE_SPACE + Distance.UnitAbbreviations.KM)))));\n            }\n            else if (uppercaseValue.endsWith(SINGLE_SPACE + Distance.UnitAbbreviations.MI))\n            {\n                return Optional\n                        .of(Distance.miles(Double.valueOf(uppercaseValue.substring(0, uppercaseValue\n                                .lastIndexOf(SINGLE_SPACE + Distance.UnitAbbreviations.MI)))));\n            }\n            else if (uppercaseValue.endsWith(SINGLE_SPACE + Distance.UnitAbbreviations.NMI))\n            {\n                return Optional.of(Distance\n                        .nauticalMiles(Double.valueOf(uppercaseValue.substring(0, uppercaseValue\n                                .lastIndexOf(SINGLE_SPACE + Distance.UnitAbbreviations.NMI)))));\n            }\n            else if (uppercaseValue.contains(Distance.INCHES_NOTATION))\n            {\n                final StringList split = StringList.split(uppercaseValue, Distance.FEET_NOTATION);\n                if (split.size() == 2)\n                {\n                    return Optional.of(Distance.feetAndInches(Double.valueOf(split.get(0)),\n                            Double.valueOf(split.get(1).substring(0,\n                                    split.get(1).lastIndexOf(Distance.INCHES_NOTATION)))));\n                }\n                else if (split.size() == 1)\n                {\n                    return Optional.of(Distance.inches(Double.valueOf(split.get(0).substring(0,\n                            split.get(0).lastIndexOf(Distance.INCHES_NOTATION)))));\n                }\n            }\n            else if (uppercaseValue.contains(Distance.FEET_NOTATION))\n            {\n                return Optional.of(Distance.feet(Double.valueOf(uppercaseValue.substring(0,\n                        uppercaseValue.lastIndexOf(Distance.FEET_NOTATION)))));\n            }\n            else\n            {\n                return Optional.of(Distance.meters(Double.valueOf(uppercaseValue)));\n            }\n        }\n        return Optional.empty();\n    }\n\n    @Override\n    public Optional<Distance> validateAndExtract(final String value, final Tag tag)\n    {\n        return LengthExtractor.validateAndExtract(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/extraction/LongExtractor.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.validation.LongValidator;\n\n/**\n * Extracts long {@link Number} values.\n *\n * @author mgostintsev\n */\npublic class LongExtractor implements TagExtractor<Long>\n{\n    @Override\n    public Optional<Long> validateAndExtract(final String value, final Tag tag)\n    {\n        final LongValidator validator = new LongValidator();\n        final Range range = tag.range();\n        if (range != null)\n        {\n            validator.setRange(range.min(), range.max());\n\n            for (final long exclusion : range.exclude())\n            {\n                validator.excludeValue(exclusion);\n            }\n        }\n\n        if (validator.isValid(value))\n        {\n            return Optional.of(Long.parseLong(value));\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/extraction/NonEmptyStringExtractor.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.validation.NonEmptyStringValidator;\n\n/**\n * Extracts non-empty String values\n *\n * @author mgostintsev\n */\npublic final class NonEmptyStringExtractor\n{\n    private static final NonEmptyStringValidator VALIDATOR = new NonEmptyStringValidator();\n\n    public static Optional<String> validateAndExtract(final String value)\n    {\n        if (VALIDATOR.isValid(value))\n        {\n            return Optional.of(value);\n        }\n\n        return Optional.empty();\n    }\n\n    private NonEmptyStringExtractor()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/extraction/OrdinalExtractor.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.validation.OrdinalValidator;\n\n/**\n * Extracts integer {@link Number} values.\n *\n * @author mgostintsev\n */\npublic final class OrdinalExtractor implements TagExtractor<Integer>\n{\n    @Override\n    public Optional<Integer> validateAndExtract(final String value, final Tag tag)\n    {\n        final OrdinalValidator validator = new OrdinalValidator();\n        final Range range = tag.range();\n        if (range != null)\n        {\n            validator.setRange(range.min(), range.max());\n\n            for (final long exclusion : range.exclude())\n            {\n                validator.excludeValue(exclusion);\n            }\n        }\n\n        if (validator.isValid(value))\n        {\n            return Optional.of(Integer.parseInt(value));\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/extraction/SpeedExtractor.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.io.InputStreamReader;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.validation.SpeedValidator;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.scalars.Speed;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonObject;\nimport com.google.gson.stream.JsonReader;\n\n/**\n * Extracts {@link Speed} values.\n *\n * @author mgostintsev\n */\npublic final class SpeedExtractor\n{\n    private static final SpeedValidator VALIDATOR = new SpeedValidator();\n    private static final String SINGLE_SPACE = \" \";\n    private static final Logger logger = LoggerFactory.getLogger(SpeedExtractor.class);\n    private static final JsonObject IMPLICIT_SPEED_MAP = new Gson().fromJson(\n            new JsonReader(new InputStreamReader(\n                    Tag.class.getResourceAsStream(\"implicit-speed-values.json\"))),\n            JsonObject.class);\n\n    public static Optional<Speed> validateAndExtract(final String value)\n    {\n        if (VALIDATOR.isValid(value))\n        {\n            try\n            {\n                final String valueOrImplicit = IMPLICIT_SPEED_MAP.has(value.toLowerCase())\n                        ? IMPLICIT_SPEED_MAP.get(value.toLowerCase()).getAsString()\n                        : value;\n                if (valueOrImplicit.endsWith(Speed.MILES_PER_HOUR))\n                {\n                    return Optional.of(Speed.milesPerHour(Double.valueOf(\n                            StringList.split(valueOrImplicit, SINGLE_SPACE).iterator().next())));\n                }\n                if (valueOrImplicit.endsWith(Speed.NAUTICAL_MILES_PER_HOUR))\n                {\n                    return Optional.of(Speed.knots(Double.valueOf(\n                            StringList.split(valueOrImplicit, SINGLE_SPACE).iterator().next())));\n                }\n                if (valueOrImplicit.endsWith(Speed.KILOMETERS_PER_HOUR))\n                {\n                    return Optional.of(Speed.kilometersPerHour(Double.valueOf(\n                            StringList.split(valueOrImplicit, SINGLE_SPACE).iterator().next())));\n                }\n                if (\"none\".equals(valueOrImplicit))\n                {\n                    return Optional.empty();\n                }\n                return Optional.of(Speed.kilometersPerHour(Double.valueOf(valueOrImplicit)));\n            }\n            catch (final NumberFormatException e)\n            {\n                logger.warn(\"Unable to read speed from {}\", value);\n                return Optional.empty();\n            }\n        }\n\n        return Optional.empty();\n    }\n\n    private SpeedExtractor()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/extraction/TagExtractor.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\n\n/**\n * Extracts the tag value from the textual representation and associated {@link Tag}.\n *\n * @param <T>\n *            The extracted value type\n * @author mgostintsev\n */\npublic interface TagExtractor<T>\n{\n    /**\n     * Validates and extracts the value from given textual representation\n     *\n     * @param value\n     *            The textual representation of this tag's value\n     * @param tag\n     *            The associated {@link Tag}\n     * @return the {@link Optional} containing the extracted value\n     */\n    Optional<T> validateAndExtract(String value, Tag tag);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/DoubleValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\n/**\n * Checks if the value of a tag is either an exact value or can be coerced into a java double and\n * within an optional range of accepted values\n *\n * @author cstaylor\n */\npublic class DoubleValidator extends NumericValidator\n{\n    @Override\n    protected Number parse(final String value)\n    {\n        return Double.parseDouble(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/ExactMatchValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\n/**\n * Checks if the value of the tag matches a list of known good values\n *\n * @author cstaylor\n */\npublic class ExactMatchValidator implements TagValidator\n{\n    private final Set<String> values;\n    private final Set<Pattern> regexes;\n\n    public ExactMatchValidator()\n    {\n        this.values = new HashSet<>();\n        this.regexes = new HashSet<>();\n    }\n\n    @Override\n    public boolean isValid(final String value)\n    {\n        if (this.values.contains(value))\n        {\n            return true;\n        }\n\n        for (final Pattern pattern : this.regexes)\n        {\n            if (pattern.matcher(value).matches())\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Add regular expression patterns to the validator\n     *\n     * @param regexes\n     *            regular expression encoded in a String\n     * @return fluent interface means we return this\n     */\n    public ExactMatchValidator withRegularExpressions(final String... regexes)\n    {\n        this.regexes.addAll(\n                Arrays.asList(regexes).stream().map(Pattern::compile).collect(Collectors.toSet()));\n        return this;\n    }\n\n    /**\n     * Add exact strings to the validator\n     *\n     * @param values\n     *            exact values we want to match\n     * @return fluent interface means we return this\n     */\n    public ExactMatchValidator withValues(final String... values)\n    {\n        this.values.addAll(Arrays.asList(values));\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/ISO2CountryValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.Arrays;\nimport java.util.Locale;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * Checks if the value of a tag matches one of the supported ISO3 country names in the Java Locale\n * classes\n *\n * @author cstaylor\n */\npublic class ISO2CountryValidator implements TagValidator\n{\n    private static Set<String> validISOCountries;\n\n    static\n    {\n        validISOCountries = Arrays.asList(Locale.getAvailableLocales()).stream()\n                .filter(ISO2CountryValidator::hasISO2Country).map(Locale::getCountry)\n                .collect(Collectors.toSet());\n    }\n\n    private static boolean hasISO2Country(final Locale locale)\n    {\n        final String country = locale.getCountry();\n        return StringUtils.isNotBlank(country) && country.length() == 2;\n    }\n\n    @Override\n    public boolean isValid(final String value)\n    {\n        return validISOCountries.contains(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/ISO3CountryValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.Arrays;\nimport java.util.Locale;\nimport java.util.MissingResourceException;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * Checks if the value of a tag matches one of the supported ISO3 country names in the Java Locale\n * classes\n *\n * @author cstaylor\n */\npublic class ISO3CountryValidator implements TagValidator\n{\n    private static Set<String> validISOCountries;\n\n    static\n    {\n        validISOCountries = Arrays.asList(Locale.getAvailableLocales()).stream()\n                .filter(ISO3CountryValidator::hasISO3Country).map(Locale::getISO3Country)\n                .collect(Collectors.toSet());\n    }\n\n    private static boolean hasISO3Country(final Locale locale)\n    {\n        try\n        {\n            locale.getISO3Country();\n            return true;\n        }\n        catch (final MissingResourceException oops)\n        {\n            return false;\n        }\n    }\n\n    @Override\n    public boolean isValid(final String value)\n    {\n        return validISOCountries.contains(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/ISOCountryValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.openstreetmap.atlas.locale.IsoCountry;\n\n/**\n * Checks if the value of a tag matches an ISO2 or ISO3 country code\n *\n * @author cstaylor\n */\npublic class ISOCountryValidator implements TagValidator\n{\n    @Override\n    public boolean isValid(final String value)\n    {\n        return IsoCountry.isValidCountryCode(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/LengthValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.Arrays;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.HeightConverter;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Distance.UnitAbbreviations;\n\n/**\n * Based on mnahoum's {@link HeightConverter} class.\n * <p>\n * Note that values like width and height are still considered lengths in the OSM Units definition\n * http://wiki.openstreetmap.org/wiki/Map_Features/Units\n *\n * @author cstaylor\n * @author gpogulsky\n */\npublic class LengthValidator implements TagValidator\n{\n    private static final DoubleValidator DOUBLE_VALIDATOR;\n\n    static\n    {\n        DOUBLE_VALIDATOR = new DoubleValidator();\n    }\n\n    /**\n     * Validates if the give value is a proper length value.\n     * <p>\n     * The expected values are \"12.5 m\", \"12.5 km\", \"12.5 mi\", \"12.5 nmi\", \"12.5\",\n     * \"12'5\\\"\".Incomplete values like \"12'\"or \"5\\\"\" are also recognized. The method will properly\n     * handle malformed values like \"Estacion de Servicio \\\"Los Arrayanes\\\"\" (contains \\ \", but is\n     * not a number), \"12'err\", etc.\n     * </p>\n     */\n    @Override\n    public boolean isValid(final String value)\n    {\n        final boolean result;\n\n        final Matcher suffixMatcher = Pattern\n                .compile(String.format(\"(\\\\d+.?\\\\d*) (%s)\",\n                        String.join(\"|\",\n                                Arrays.stream(UnitAbbreviations.values()).map(Enum::toString)\n                                        .collect(Collectors.toList()))))\n                .matcher(value.toUpperCase());\n\n        if (suffixMatcher.matches())\n        {\n            result = DOUBLE_VALIDATOR.isValid(suffixMatcher.group(1));\n        }\n        else\n        {\n            final int feetIndex = value.indexOf(Distance.FEET_NOTATION);\n            if (feetIndex > -1)\n            {\n                if (DOUBLE_VALIDATOR.isValid(value.substring(0, feetIndex)))\n                {\n                    // Tail?\n                    if (feetIndex + 1 < value.length())\n                    {\n                        final int inchesIndex = value.indexOf(Distance.INCHES_NOTATION,\n                                feetIndex + 1);\n                        if (inchesIndex > -1)\n                        {\n                            result = this.validateInchesAndTail(value, feetIndex + 1, inchesIndex);\n                        }\n                        else\n                        {\n                            result = StringUtils\n                                    .isBlank(value.substring(feetIndex + 1, value.length()));\n                        }\n                    }\n                    else\n                    {\n                        result = true;\n                    }\n                }\n                else\n                {\n                    result = false;\n                }\n            }\n            else\n            {\n                final int inchesIndex = value.indexOf(Distance.INCHES_NOTATION);\n                if (inchesIndex > -1)\n                {\n                    result = this.validateInchesAndTail(value, 0, inchesIndex);\n                }\n                else\n                {\n                    result = DOUBLE_VALIDATOR.isValid(value);\n                }\n            }\n        }\n\n        return result;\n    }\n\n    private boolean validateInchesAndTail(final String value, final int start, final int index)\n    {\n        return DOUBLE_VALIDATOR.isValid(value.substring(start, index))\n                && (index + 1 == value.length()\n                        || StringUtils.isBlank(value.substring(index + 1, value.length())));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/LongValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\n/**\n * Checks if the value of a tag is either an exact value or can be coerced into a java long and\n * within an optional range of accepted values\n *\n * @author cstaylor\n */\npublic class LongValidator extends NumericValidator\n{\n    @Override\n    protected Number parse(final String value)\n    {\n        return Long.parseLong(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/NonEmptyStringValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\n/**\n * Checks if the value of a tag has at least one non-whitespace character\n *\n * @author cstaylor\n */\npublic class NonEmptyStringValidator implements TagValidator\n{\n    @Override\n    public boolean isValid(final String value)\n    {\n        return value.trim().length() > 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/NoneValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\n/**\n * Does no checking: only used for permitting any kind of values in a tag\n *\n * @author cstaylor\n */\npublic class NoneValidator implements TagValidator\n{\n    @Override\n    public boolean isValid(final String value)\n    {\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/NumericValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Numeric validators can check ranges of valid data or even exclude certain values regardless of\n * the range\n *\n * @author cstaylor\n */\npublic abstract class NumericValidator extends ExactMatchValidator\n{\n    private static final Logger logger = LoggerFactory.getLogger(NumericValidator.class);\n\n    private Long minimum;\n    private Long maximum;\n    private final Set<Double> exclusions;\n\n    public NumericValidator()\n    {\n        this.exclusions = new HashSet<>();\n    }\n\n    public void excludeValue(final long value)\n    {\n        this.exclusions.add(new Double(value));\n    }\n\n    /**\n     * Since all NumericValidators are ExactMatchValidators, we first check if the value matches any\n     * exact match, and then proceed with the numeric conversion and range check\n     */\n    @Override\n    public final boolean isValid(final String value)\n    {\n        try\n        {\n            return super.isValid(value) || withinRange(parse(value));\n        }\n        catch (final NumberFormatException oops)\n        {\n            return false;\n        }\n    }\n\n    public void setMaximum(final long maximum)\n    {\n        if (this.minimum == null)\n        {\n            this.maximum = maximum;\n        }\n        else\n        {\n            if (maximum > this.minimum)\n            {\n                this.maximum = maximum;\n            }\n            else\n            {\n                logger.debug(\n                        \"Cannot set maximum less than or equal to minimum {}. Consider using setRange instead.\",\n                        this.minimum);\n            }\n        }\n    }\n\n    public void setMinimum(final long minimum)\n    {\n        if (this.maximum == null)\n        {\n            this.minimum = minimum;\n        }\n        else\n        {\n            if (minimum < this.maximum)\n            {\n                this.minimum = minimum;\n            }\n            else\n            {\n                logger.debug(\n                        \"Cannot set minimum greater than or equal to maximum {}. Consider using setRange instead.\",\n                        this.maximum);\n            }\n        }\n    }\n\n    public void setRange(final long minimum, final long maximum)\n    {\n        if (minimum < maximum)\n        {\n            this.minimum = minimum;\n            this.maximum = maximum;\n        }\n        else if (minimum == Long.MAX_VALUE || maximum == Long.MIN_VALUE)\n        {\n            /*\n             * Special case where at least one of the range endpoints is a default value. In this\n             * case, we don't want to send out a log message - instead just do nothing.\n             */\n        }\n        else\n        {\n            logger.debug(\"Invalid range supplied, minimum: {} cannot be greater than maximum: {}.\",\n                    minimum, maximum);\n        }\n    }\n\n    protected boolean checkExclusions(final Number checkMe)\n    {\n        return this.exclusions.contains(checkMe.doubleValue());\n    }\n\n    protected abstract Number parse(String value);\n\n    protected boolean withinRange(final Number checkMe)\n    {\n        if (checkExclusions(checkMe))\n        {\n            return false;\n        }\n        final double theValue = checkMe.doubleValue();\n        if (this.minimum != null && theValue < this.minimum)\n        {\n            return false;\n        }\n        if (this.maximum != null && theValue > this.maximum)\n        {\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/OrdinalValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\n/**\n * Ordinals must be positive integers\n *\n * @author cstaylor\n */\npublic class OrdinalValidator extends NumericValidator\n{\n    @Override\n    protected Number parse(final String value)\n    {\n        return Long.parseLong(value);\n    }\n\n    @Override\n    protected boolean withinRange(final Number checkMe)\n    {\n        return super.withinRange(checkMe) && checkMe.longValue() > 0L;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/SpeedValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.io.InputStreamReader;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.utilities.scalars.Speed;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonObject;\nimport com.google.gson.stream.JsonReader;\n\n/**\n * Checks if the value of the tag matches the OSM conventions for denoting speed. See more on the\n * <a href=\"http://wiki.openstreetmap.org/wiki/Speed_limits\">speed limits wiki</a> and\n * <a href=\"http://wiki.openstreetmap.org/wiki/Key:maxspeed\">max speed wiki</a>. Note: the\n * validation has been loosened slightly from OSM standards to handle more valid limits.\n * Specifically, OSM dictates if km/h are the unit, no unit should be specified. The validation\n * below identifies 'kph' as a valid suffix.\n *\n * @author mgostintsev\n */\npublic class SpeedValidator implements TagValidator\n{\n    private static final DoubleValidator DOUBLE_VALIDATOR;\n    private static final JsonObject IMPLICIT_SPEED_MAP = new Gson().fromJson(\n            new JsonReader(new InputStreamReader(\n                    Tag.class.getResourceAsStream(\"implicit-speed-values.json\"))),\n            JsonObject.class);\n\n    static\n    {\n        DOUBLE_VALIDATOR = new DoubleValidator();\n        DOUBLE_VALIDATOR.setMinimum(0);\n    }\n\n    @Override\n    public boolean isValid(final String value)\n    {\n        if (value.endsWith(Speed.MILES_PER_HOUR))\n        {\n            return DOUBLE_VALIDATOR.isValid(\n                    value.substring(0, value.length() - Speed.MILES_PER_HOUR.length()).trim());\n        }\n        else if (value.endsWith(Speed.NAUTICAL_MILES_PER_HOUR))\n        {\n            return DOUBLE_VALIDATOR.isValid(value\n                    .substring(0, value.length() - Speed.NAUTICAL_MILES_PER_HOUR.length()).trim());\n        }\n        else if (value.endsWith(Speed.KILOMETERS_PER_HOUR))\n        {\n            return DOUBLE_VALIDATOR.isValid(\n                    value.substring(0, value.length() - Speed.KILOMETERS_PER_HOUR.length()).trim());\n        }\n        else if (\"none\".equals(value))\n        {\n            return true;\n        }\n        else if (IMPLICIT_SPEED_MAP.has(value.toLowerCase()))\n        {\n            return true;\n        }\n        else\n        {\n            return DOUBLE_VALIDATOR.isValid(value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/TagDocumenter.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.net.URI;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators.TagKeySearch;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfoList;\nimport io.github.classgraph.ScanResult;\n\n/**\n * Class that walks across all Tags and generates metadata about them that can be converted into\n * HTML or any other desired documentation format as needed.\n *\n * @author cstaylor\n */\npublic class TagDocumenter\n{\n    /**\n     * Callback that receives metadata about a Tag\n     *\n     * @author cstaylor\n     */\n    public interface Callback\n    {\n        void tagFound(CallbackData data);\n    }\n\n    /**\n     * Metadata container class for Tag information\n     *\n     * @author cstaylor\n     */\n    public static final class CallbackData implements Comparable<CallbackData>\n    {\n        private String tagClassName;\n        private String tagKey;\n        private final SortedSet<String> validTagValues;\n        private URI tagInfoLink;\n        private URI osmWikiLink;\n        private String validationType;\n        private boolean localized;\n        private boolean synthetic;\n\n        CallbackData()\n        {\n            this.validTagValues = new TreeSet<>();\n        }\n\n        @Override\n        public int compareTo(final CallbackData other)\n        {\n            return this.tagKey.compareTo(other.tagKey);\n        }\n\n        @Override\n        public boolean equals(final Object obj)\n        {\n            if (obj == this)\n            {\n                return true;\n            }\n            if (obj instanceof CallbackData)\n            {\n                final CallbackData other = (CallbackData) obj;\n                boolean returnValue = Objects.equals(this.tagClassName, other.tagClassName);\n                returnValue = returnValue && Objects.equals(this.tagKey, other.tagKey);\n                returnValue = returnValue\n                        && Objects.equals(this.validTagValues, other.validTagValues);\n                returnValue = returnValue && Objects.equals(this.tagInfoLink, other.tagInfoLink);\n                returnValue = returnValue && Objects.equals(this.osmWikiLink, other.osmWikiLink);\n                returnValue = returnValue\n                        && Objects.equals(this.validationType, other.validationType);\n                returnValue = returnValue && Objects.equals(this.localized, other.localized);\n                returnValue = returnValue && Objects.equals(this.synthetic, other.synthetic);\n                return returnValue;\n            }\n            return false;\n        }\n\n        public Optional<URI> getOsmWikiLink()\n        {\n            return Optional.ofNullable(this.osmWikiLink);\n        }\n\n        public String getTagClassName()\n        {\n            return this.tagClassName;\n        }\n\n        public Optional<URI> getTagInfoLink()\n        {\n            return Optional.ofNullable(this.tagInfoLink);\n        }\n\n        public String getTagKey()\n        {\n            return this.tagKey;\n        }\n\n        public Iterable<String> getValidTagValues()\n        {\n            return this.validTagValues;\n        }\n\n        public String getValidationType()\n        {\n            return this.validationType;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return Objects.hash(this.tagKey, this.tagClassName);\n        }\n\n        public boolean isLocalized()\n        {\n            return this.localized;\n        }\n\n        public boolean isSynthetic()\n        {\n            return this.synthetic;\n        }\n    }\n\n    private final Set<CallbackData> tagData;\n\n    /**\n     * Calls the other constructor with the default package name\n     */\n    public TagDocumenter()\n    {\n        this(\"org.openstreetmap.atlas\");\n    }\n\n    /**\n     * Find all of the tags in packageName and create metadata for all of them\n     *\n     * @param packageName\n     *            the base package to search the current classloader for Tags\n     */\n    public TagDocumenter(final String packageName)\n    {\n        this.tagData = new TreeSet<>();\n        /*\n         * We definitely don't want tags in classes named TestCase. When running this from the\n         * command line we shouldn't get any TestCase tags anyways, but when running this in\n         * development mode under Eclipse with core in the classpath they will be picked up.\n         */\n\n        // Scan the given package\n        try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages(packageName)\n                .scan())\n        {\n            // Look at annotated classes\n            final ClassInfoList tagClassInfoList = scanResult\n                    .getClassesWithAnnotation(\"org.openstreetmap.atlas.tags.annotations.Tag\");\n\n            // Ignore any TestCase classes\n            tagClassInfoList.loadClasses().forEach(klass ->\n            {\n                if (!klass.getName().contains(\"TestCase\"))\n                {\n                    this.tagData.add(createCallbackDataFromClass(klass));\n                }\n            });\n        }\n    }\n\n    public void walk(final Callback callback)\n    {\n        this.tagData.stream().forEach(callback::tagFound);\n    }\n\n    private CallbackData createCallbackDataFromClass(final Class<?> tagClass)\n    {\n        final CallbackData returnValue = new CallbackData();\n        TagKeySearch.findTagKeyIn(tagClass).ifPresent(results ->\n        {\n            final String tagName = results.getKeyName();\n            final TagKey tagKey = results.getTagKey();\n            returnValue.tagKey = tagName;\n            returnValue.tagClassName = tagClass.getName();\n            returnValue.osmWikiLink = results.getTag().osm().length() > 0\n                    ? URI.create(results.getTag().osm())\n                    : null;\n            returnValue.tagInfoLink = results.getTag().taginfo().length() > 0\n                    ? URI.create(results.getTag().taginfo())\n                    : null;\n            returnValue.localized = tagKey.value() == TagKey.KeyType.LOCALIZED;\n            returnValue.validationType = results.getTag().value().name();\n            returnValue.synthetic = results.getTag().synthetic();\n        });\n        return returnValue;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/TagValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\n/**\n * TagValidators verify a Tag's value. Some may be complex checks like data conversions, while\n * others may compare against a simple set of values\n *\n * @author cstaylor\n */\npublic interface TagValidator\n{\n    /**\n     * Checks if value is valid for this kind of tag\n     *\n     * @param value\n     *            the textual representation of this tag's value\n     * @return true if the value is valid, false otherwise\n     */\n    boolean isValid(String value);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/TimestampValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.Date;\n\n/**\n * Checks if the value of a tag is either an exact value or can be coerced into a java Date object\n *\n * @author cstaylor\n */\npublic class TimestampValidator extends ExactMatchValidator\n{\n    @Override\n    public boolean isValid(final String value)\n    {\n        if (super.isValid(value))\n        {\n            return true;\n        }\n\n        try\n        {\n            new Date(Long.parseLong(value));\n            return true;\n        }\n        catch (final NumberFormatException oops)\n        {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/URIValidator.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\n/**\n * Validator for verifying a String follows the proper URI syntax\n *\n * @author cstaylor\n */\npublic class URIValidator extends ExactMatchValidator\n{\n    @Override\n    public boolean isValid(final String value)\n    {\n        if (super.isValid(value))\n        {\n            return true;\n        }\n        try\n        {\n            new URI(value);\n            return true;\n        }\n        catch (final URISyntaxException oops)\n        {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/annotations/validation/Validators.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.EnumMap;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.LocalizedTagNameWithOptionalDate;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.Taggable.TagSearchOption;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\nimport org.openstreetmap.atlas.tags.annotations.TagValue;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.cache.CachingValidator;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfoList;\nimport io.github.classgraph.ScanResult;\n\n/**\n * Builds a table of {@link TagValidator}s using Java annotations and introspection.\n *\n * @author cstaylor\n */\npublic class Validators\n{\n    /**\n     * Extracted the code for finding the tag key into its own public inner class\n     *\n     * @author cstaylor\n     */\n    public static final class TagKeySearch\n    {\n        public static Optional<TagKeySearchResults> findTagKeyIn(final Class<?> tagClass)\n        {\n            final Tag tag = tagClass.getDeclaredAnnotation(Tag.class);\n            for (final Field field : tagClass.getDeclaredFields())\n            {\n                final TagKey tagKey = field.getAnnotation(TagKey.class);\n                if (tagKey != null && field.getType().isAssignableFrom(String.class))\n                {\n                    try\n                    {\n                        final String returnValue = (String) field.get(null);\n                        if (returnValue == null || returnValue.trim().length() == 0)\n                        {\n                            throw new IllegalArgumentException(\n                                    String.format(\"%s is missing a key\", tagClass.getName()));\n                        }\n                        return Optional.of(new TagKeySearchResults(tag, tagKey, returnValue));\n                    }\n                    catch (final IllegalAccessException oops)\n                    {\n                        throw new IllegalArgumentException(String.format(\n                                \"Check the source code for %s: the @TagKey is probably not a public static final String constant\",\n                                tagClass.getName()), oops);\n                    }\n                }\n            }\n            return Optional.empty();\n        }\n    }\n\n    /**\n     * Immutable object capturing the results of a tag key search\n     *\n     * @author cstaylor\n     */\n    public static final class TagKeySearchResults\n    {\n        private final Tag tag;\n        private final TagKey key;\n        private final String keyName;\n\n        private TagKeySearchResults(final Tag tag, final TagKey key, final String keyName)\n        {\n            this.tag = tag;\n            this.key = key;\n            this.keyName = keyName;\n        }\n\n        public String getKeyName()\n        {\n            return this.keyName;\n        }\n\n        public Tag getTag()\n        {\n            return this.tag;\n        }\n\n        public TagKey getTagKey()\n        {\n            return this.key;\n        }\n    }\n\n    /**\n     * Single point for handling both standard and localizable tags, simplifying the main Validators\n     * source code below\n     *\n     * @author cstaylor\n     */\n    private static final class ValidatorMap\n    {\n        private final Map<String, TagValidator> validators;\n        private final Map<String, TagValidator> localizedValidators;\n        private final Map<String, Class<?>> origins;\n\n        ValidatorMap()\n        {\n            this.validators = new HashMap<>();\n            this.localizedValidators = new HashMap<>();\n            this.origins = new HashMap<>();\n        }\n\n        boolean canValidate(final String name)\n        {\n            return validatorFor(name) != null;\n        }\n\n        Class<?> classFor(final String name)\n        {\n            return this.origins.get(name);\n        }\n\n        void put(final Class<?> tagClass, final String name, final TagKey tagKey,\n                final TagValidator validator)\n        {\n            if (tagKey.value() == KeyType.EXACT)\n            {\n                this.validators.put(name, validator);\n            }\n            else\n            {\n                // This is for localized tags\n                this.localizedValidators.put(name, validator);\n            }\n            this.origins.put(name, tagClass);\n        }\n\n        TagValidator validatorFor(final String name)\n        {\n            // Step 1: Check standard exact name match validators\n            TagValidator validator = this.validators.get(name);\n            if (validator == null)\n            {\n                final LocalizedTagNameWithOptionalDate localizedName = new LocalizedTagNameWithOptionalDate(\n                        name);\n                validator = this.localizedValidators.get(localizedName.getName());\n            }\n            return validator;\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(Validators.class);\n\n    private final ValidatorMap validators;\n\n    private final EnumMap<Validation, Class<? extends TagValidator>> validatorTypes;\n\n    public static String findTagNameIn(final Class<?> tagClass)\n    {\n        final Optional<TagKeySearchResults> tagName = TagKeySearch.findTagKeyIn(tagClass);\n        if (tagName.isPresent())\n        {\n            return tagName.get().getKeyName();\n        }\n        throw new IllegalArgumentException(\n                String.format(\"key must be declared in class: %s\", tagClass.getName()));\n    }\n\n    /**\n     * Helpful method for swizzling an interface Tag that includes the contents of an enum tag\n     * through the [with] annotation feature into its possible value if that value is found in the\n     * passed in Taggable parameter and the enumType provided is actually listed in the [with]\n     * attribute.\n     * <p>\n     * See the FromEnumTestCase class for an example of how to use this method\n     *\n     * @param <T>\n     *            the type of enum tag we're parsing\n     * @param tagType\n     *            the interface tag with a [with] attribute that we want a possible value from\n     * @param enumType\n     *            the return value type we want a value from\n     * @param taggable\n     *            the source of tags and their values\n     * @return an empty optional if the tagType isn't a tag, doesn't have a key, enumType is not\n     *         included in tagType's [with] list, the value isn't found in taggable, or no enum\n     *         value in enumType matches (ignoring case) the tag's value\n     */\n    public static <T extends Enum<T>> Optional<T> from(final Class<?> tagType,\n            final Class<T> enumType, final Taggable taggable)\n    {\n        final Tag tag = tagType.getDeclaredAnnotation(Tag.class);\n        if (tag != null && Stream.of(tag.with()).anyMatch(possible -> possible == enumType))\n        {\n            return fromHelper(findTagNameIn(tagType), enumType, taggable);\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Caching version - use in generic applications.\n     * <p>\n     * Helpful method for swizzling an Enum Tag into its possible value if that value is found in\n     * the passed in Taggable parameter. This cuts down on a lot of duplicate code that we had in\n     * each enum-type Tag.\n     * <p>\n     * See the FromEnumTestCase class for an example of how to use this method\n     *\n     * @param <T>\n     *            the type of enum tag we're parsing\n     * @param tagType\n     *            the enum style tag that we want a possible value from\n     * @param taggable\n     *            the source of tags and their values\n     * @return an empty optional if the enum isn't a tag, doesn't have a key, the value isn't found\n     *         in taggable, or no enum value matches (ignoring case) the tag's value\n     */\n    public static <T extends Enum<T>> Optional<T> from(final Class<T> tagType,\n            final Taggable taggable)\n    {\n        return CachingValidator.getInstance().from(tagType, taggable);\n    }\n\n    /**\n     * Reflection version - use when you need to get a few tags, and no caching is necessary. This\n     * method is used by the caching version to populate the cache.\n     * <p>\n     * Helpful method for swizzling an Enum Tag into its possible value if that value is found in\n     * the passed in Taggable parameter. This cuts down on a lot of duplicate code that we had in\n     * each enum-type Tag.\n     * <p>\n     *\n     * @param <T>\n     *            the type of enum tag we're parsing\n     * @param tagType\n     *            the enum style tag that we want a possible value from\n     * @param taggable\n     *            the source of tags and their values\n     * @return an empty optional if the enum isn't a tag, doesn't have a key, the value isn't found\n     *         in taggable, or no enum value matches (ignoring case) the tag's value\n     */\n    public static <T extends Enum<T>> Optional<T> fromAnnotation(final Class<T> tagType,\n            final Taggable taggable)\n    {\n        if (tagType.getDeclaredAnnotation(Tag.class) != null)\n        {\n            return fromHelper(findTagNameIn(tagType), tagType, taggable);\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Convenience method for checking if a class is actually a tag and that its key is localizable\n     *\n     * @param tagType\n     *            the tag class we want to check\n     * @return true if tagType is actually a tag and its key is localizable\n     */\n    public static boolean hasLocalizedTagKey(final Class<?> tagType)\n    {\n        /*\n         * First, sanity check the key\n         */\n        if (tagType == null)\n        {\n            throw new IllegalArgumentException(\"tagType can't be null\");\n        }\n        /*\n         * Next, check if the key is actually a key\n         */\n        final Optional<TagKeySearchResults> tagKey = Validators.TagKeySearch.findTagKeyIn(tagType);\n        if (!tagKey.isPresent())\n        {\n            throw new IllegalArgumentException(\n                    String.format(\"%s isn't a known key\", tagType.getName()));\n        }\n        return tagKey.get().getTagKey().value() == KeyType.LOCALIZED;\n    }\n\n    /**\n     * Convenience method that returns a filter for a {@link Taggable} that evaluates if all passed\n     * in tag types are present\n     *\n     * @param tagTypes\n     *            the type of tags to check\n     * @return a filter for a Taggable entity that evaluates if if contains all specified tag types\n     */\n    public static Predicate<Taggable> hasValuesFor(final Class<?>... tagTypes)\n    {\n        if (tagTypes.length == 0)\n        {\n            return taggable -> false;\n        }\n\n        return taggable -> hasValuesFor(taggable, tagTypes);\n    }\n\n    /**\n     * Convenience method for checking if we have defined values for all tags passed into the method\n     *\n     * @param taggable\n     *            where we look up the tags\n     * @param tagTypes\n     *            the type of tags to check\n     * @return true if all of the tags have values, false if at least one is missing\n     */\n    public static boolean hasValuesFor(final Taggable taggable, final Class<?>... tagTypes)\n    {\n        for (final Class<?> tagType : tagTypes)\n        {\n            if (!taggable.getTag(findTagNameIn(tagType)).isPresent())\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Use this method to check if a tag exists in a Taggable object _and_ is _not_ one of several\n     * provided values. While we could do the same with an EnumSet, this makes calling code cleaner\n     * since they only need a single line to check for the existence of at least one item in a set\n     * of tag values\n     *\n     * @param <T>\n     *            the enum-type tag's class object\n     * @param taggable\n     *            where the tags should be read from\n     * @param type\n     *            the class of the enum-type tag we are looking for?\n     * @param values\n     *            which values do we want to check against?\n     * @return true if the tag exists in taggable and if the value is not any of the specified\n     *         values (like an enumset)\n     */\n    public static <T extends Enum<T>> boolean isNotOfType(final Taggable taggable,\n            final Class<T> type, @SuppressWarnings(\"unchecked\") final T... values)\n    {\n        final Optional<T> possibleRealValue = Validators.from(type, taggable);\n        if (!possibleRealValue.isPresent())\n        {\n            return false;\n        }\n\n        final T realValue = possibleRealValue.get();\n        for (final T searching : values)\n        {\n            if (realValue == searching)\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Use this method to check if a tag exists in two {@link Taggable} objects, and those values\n     * are the same.\n     *\n     * @param <T>\n     *            the enum-type tag's class object\n     * @param firstTaggable\n     *            one taggable we are comparing\n     * @param secondTaggable\n     *            the other taggable we are comparing against\n     * @param type\n     *            the class of the enum-type tag we are looking for\n     * @return true if the tag exists in firstTaggable AND secondTaggable, AND the value of\n     *         firstTaggable is equal to the value of secondTaggable.\n     */\n    public static <T> boolean isOfSameType(final Taggable firstTaggable,\n            final Taggable secondTaggable, final Class<T> type)\n    {\n        final String key = findTagNameIn(type);\n\n        return firstTaggable.getTag(key)\n                .flatMap(oneTag -> secondTaggable.getTag(key).map(oneTag::equals)).orElse(false);\n    }\n\n    /**\n     * Use this method to check if a tag exists in a {@link Taggable} object _and_ is one of several\n     * expected values. While we could do the same with an EnumSet, this makes calling code cleaner\n     * since they only need a single line to check for the existence of at least one item in a set\n     * of tag values\n     *\n     * @param <T>\n     *            the enum-type tag's class object\n     * @param taggable\n     *            where the tags should be read from\n     * @param type\n     *            the class of the enum-type tag we are looking for?\n     * @param values\n     *            which values do we want to check against?\n     * @return true if the tag exists in taggable and if the value matches any of the specified\n     *         values (like an enumset)\n     */\n    public static <T extends Enum<T>> boolean isOfType(final Taggable taggable, final Class<T> type,\n            @SuppressWarnings(\"unchecked\") final T... values)\n    {\n        final Optional<T> possibleRealValue = Validators.from(type, taggable);\n        if (!possibleRealValue.isPresent())\n        {\n            return false;\n        }\n\n        final T realValue = possibleRealValue.get();\n        for (final T searching : values)\n        {\n            if (realValue == searching)\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Convenience method for creating the localized name of a tag if and only if tagType is a Tag\n     * and the TagKey is localizable\n     *\n     * @param tagType\n     *            check this class if it's a localizable tag and return the localized name\n     * @param language\n     *            the optional language to localize\n     * @param searchOptions\n     *            optional arguments that change how we interpret tags\n     * @return an optional string of the localized tag name\n     */\n    public static Optional<String> localizeKeyName(final Class<?> tagType,\n            final Optional<IsoLanguage> language, final TagSearchOption... searchOptions)\n    {\n        /*\n         * First, sanity check the key\n         */\n        if (tagType == null)\n        {\n            throw new IllegalArgumentException(\"tagType can't be null\");\n        }\n        /*\n         * Next, check if the key is actually a key\n         */\n        final Optional<TagKeySearchResults> tagKey = Validators.TagKeySearch.findTagKeyIn(tagType);\n        if (!tagKey.isPresent())\n        {\n            throw new IllegalArgumentException(\n                    String.format(\"%s isn't a known key\", tagType.getName()));\n        }\n        final EnumSet<TagSearchOption> searchOptionSet = searchOptions.length > 0\n                ? EnumSet.copyOf(Arrays.asList(searchOptions))\n                : EnumSet.noneOf(TagSearchOption.class);\n\n        final TagKeySearchResults data = tagKey.get();\n        Optional<String> value = Optional.empty();\n        if (language.isPresent() && (data.getTagKey().value() == KeyType.LOCALIZED\n                || searchOptionSet.contains(TagSearchOption.FORCE_ALL_LOCALIZED_ONLY)))\n        {\n            value = Optional.of(\n                    String.format(\"%s:%s\", data.getKeyName(), language.get().getLanguageCode()));\n        }\n        if (!value.isPresent())\n        {\n            value = Optional.of(data.getKeyName());\n        }\n        return value;\n    }\n\n    /**\n     * Simple utility method for creating a hashmap out of a set of enum-type tags.\n     * <p>\n     * The key portion of each map entry is extracted from the tag type itself.\n     *\n     * @param values\n     *            the enum-type tags we want to convert into a hashmap\n     * @return the completed hashmap\n     */\n    public static Map<String, String> toMap(final Enum<?>... values)\n    {\n        return Arrays.asList(values).stream().collect(Collectors.toMap(\n                value -> findTagNameIn(value.getClass()), value -> value.name().toLowerCase()));\n    }\n\n    /**\n     * Shamelessly taken from SO:\n     * http://stackoverflow.com/questions/7254126/get-annotations-for-enum-type-variable\n     *\n     * @param constant\n     *            the particular enum constant we're interested in converting\n     * @return the value of the constant or the overriden value from the TagValueAs annotation\n     */\n    private static String enumConstantToValue(final Enum<?> constant)\n    {\n        try\n        {\n            final Field field = constant.getDeclaringClass().getField(constant.name());\n            final TagValueAs substitutedValue = field.getAnnotation(TagValueAs.class);\n            return substitutedValue == null ? ((Enum<?>) field.get(null)).name().toLowerCase()\n                    : substitutedValue.value();\n        }\n        catch (final IllegalAccessException | NoSuchFieldException oops)\n        {\n            throw new CoreException(\"{} can't access field value\", constant, oops);\n        }\n    }\n\n    private static <T extends Enum<T>> Optional<T> fromHelper(final String tagName,\n            final Class<T> enumType, final Taggable taggable)\n    {\n        // First try simple match\n        // If it fails, try matching based on annotations\n\n        if (tagName != null)\n        {\n            final Optional<String> tagValue = taggable.getTag(tagName);\n            if (tagValue.isPresent())\n            {\n                final String internedValue = tagValue.get().toUpperCase().intern();\n                try\n                {\n                    final T enumValue = Enum.valueOf(enumType, internedValue);\n                    return Optional.of(enumValue);\n                }\n                catch (final IllegalArgumentException badArgument)\n                {\n                    // There is no direct name match\n                    return fromMatchingHelper(tagName, enumType, internedValue);\n                }\n            }\n        }\n\n        return Optional.empty();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private static <T extends Enum<T>> Optional<T> fromMatchingHelper(final String tagName,\n            final Class<T> enumType, final String internedValue)\n    {\n        try\n        {\n            for (final T enumValue : (T[]) enumType.getMethod(\"values\").invoke(null))\n            {\n                if (matches(enumValue, internedValue))\n                {\n                    return Optional.of(enumValue);\n                }\n            }\n        }\n        catch (final NoSuchMethodException | IllegalAccessException\n                | InvocationTargetException anImpossibleError)\n        {\n            logger.error(\"{} doesn't have a values method, or it couldn't be called: impossible\",\n                    tagName, anImpossibleError);\n        }\n\n        return Optional.empty();\n    }\n\n    private static boolean matches(final Enum<?> constant, final String value)\n    {\n        try\n        {\n            final Field field = constant.getDeclaringClass().getField(constant.name());\n            final TagValueAs substitutedValue = field.getAnnotation(TagValueAs.class);\n            final String comparisonString = substitutedValue != null ? substitutedValue.value()\n                    : constant.name();\n            return comparisonString.toUpperCase().intern() == value.intern();\n        }\n        catch (final NoSuchFieldException oops)\n        {\n            throw new CoreException(\"{} can't access field value\", constant, oops);\n        }\n    }\n\n    public Validators(final Class<?> childrenOf)\n    {\n        this(childrenOf.getPackage().getName());\n    }\n\n    public Validators(final String packageName)\n    {\n        this.validatorTypes = new EnumMap<>(Validation.class);\n        this.validators = new ValidatorMap();\n        fillValidatorTypes(this.validatorTypes);\n        final List<Class<?>> klasses = new ArrayList<>();\n\n        // Scan all classes in the given package with the Tag annotation\n        try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages(packageName)\n                .scan())\n        {\n            final ClassInfoList tagClassInfoList = scanResult\n                    .getClassesWithAnnotation(\"org.openstreetmap.atlas.tags.annotations.Tag\");\n            tagClassInfoList.loadClasses().forEach(klasses::add);\n        }\n        klasses.stream().forEach(this::processClass);\n    }\n\n    /**\n     * Is key a known tag?\n     *\n     * @param key\n     *            the type of tag we want to verify\n     * @return true if we can handle [key] tags, false otherwise\n     */\n    public boolean canValidate(final String key)\n    {\n        return this.validators.canValidate(key);\n    }\n\n    /**\n     * Sometimes we want to know where a tag found by Validators was defined.\n     *\n     * @param tag\n     *            the name of the tag we're searching for\n     * @return an Optional containing the class reference if it exists, an empty optional otherwise\n     */\n    public Optional<Class<?>> findClassDefining(final String tag)\n    {\n        return Optional.ofNullable(this.validators.classFor(tag));\n    }\n\n    public Optional<String> getTagInfo(final String tagName)\n    {\n        final Class<?> tagClass = this.validators.classFor(tagName);\n\n        if (tagClass != null)\n        {\n            final Tag tag = tagClass.getAnnotation(Tag.class);\n            if (tag.taginfo().equals(\"\"))\n            {\n                return Optional.empty();\n            }\n            else\n            {\n                return Optional.of(tag.taginfo());\n            }\n        }\n\n        return Optional.empty();\n    }\n\n    /**\n     * Get the validator used for verifying tags named tagName\n     *\n     * @param key\n     *            the name of the tag we want to verify\n     * @return the validator if we support it, null otherwise\n     */\n    public TagValidator getValidatorFor(final String key)\n    {\n        return this.validators.validatorFor(key);\n    }\n\n    /**\n     * Convenience method for checking and verifying a value without having to chain calls\n     *\n     * @param key\n     *            the key we want to verify\n     * @param value\n     *            the value we want to verify against the allowed values for key\n     * @return true if the value is valid for key, false otherwise\n     */\n    public boolean isValidFor(final String key, final String value)\n    {\n        return canValidate(key) && getValidatorFor(key).isValid(value);\n    }\n\n    protected void fillValidatorTypes(\n            final EnumMap<Validation, Class<? extends TagValidator>> validatorTypes)\n    {\n        validatorTypes.put(Validation.DOUBLE, DoubleValidator.class);\n        validatorTypes.put(Validation.LONG, LongValidator.class);\n        validatorTypes.put(Validation.MATCH, ExactMatchValidator.class);\n        validatorTypes.put(Validation.TIMESTAMP, TimestampValidator.class);\n        validatorTypes.put(Validation.NON_EMPTY_STRING, NonEmptyStringValidator.class);\n        validatorTypes.put(Validation.NONE, NoneValidator.class);\n        validatorTypes.put(Validation.ISO3_COUNTRY, ISO3CountryValidator.class);\n        validatorTypes.put(Validation.ISO2_COUNTRY, ISO2CountryValidator.class);\n        validatorTypes.put(Validation.ORDINAL, OrdinalValidator.class);\n        validatorTypes.put(Validation.URI, URIValidator.class);\n        validatorTypes.put(Validation.SPEED, SpeedValidator.class);\n        validatorTypes.put(Validation.LENGTH, LengthValidator.class);\n    }\n\n    private TagValidator createValidatorFor(final Validation validation)\n    {\n        final Class<? extends TagValidator> validatorClass = this.validatorTypes.get(validation);\n        if (validatorClass == null)\n        {\n            throw new IllegalArgumentException(\n                    String.format(\"%s is an unsupported validator\", validation));\n        }\n        try\n        {\n            return validatorClass.newInstance();\n        }\n        catch (final IllegalAccessException | InstantiationException oops)\n        {\n            throw new IllegalArgumentException(\n                    String.format(\"%s is an unsupported validator\", validation), oops);\n        }\n    }\n\n    private TagValidator fillEnumerationValues(final ExactMatchValidator validator,\n            final Class<?> tagClass)\n    {\n        for (final Enum<?> enumValue : (Enum<?>[]) tagClass.getEnumConstants())\n        {\n            validator.withValues(enumConstantToValue(enumValue));\n        }\n        return validator;\n    }\n\n    private void fillExactMatches(final ExactMatchValidator validator, final Field[] fields)\n    {\n        for (final Field field : fields)\n        {\n            final TagValue tagValue = field.getAnnotation(TagValue.class);\n            if (tagValue != null)\n            {\n                if (field.getType().isAssignableFrom(String.class))\n                {\n                    try\n                    {\n                        final String returnValue = (String) field.get(null);\n                        if (returnValue == null || returnValue.trim().length() == 0)\n                        {\n                            throw new IllegalArgumentException(\"key can't be empty\");\n                        }\n                        switch (tagValue.value())\n                        {\n                            case REGEX:\n                                validator.withRegularExpressions(returnValue);\n                                break;\n                            case EXACT:\n                                validator.withValues(returnValue);\n                                break;\n                            default:\n                                throw new IllegalStateException(String.format(\n                                        \"%s is an unsupported value type\", tagValue.value()));\n                        }\n                    }\n                    catch (final IllegalAccessException oops)\n                    {\n                        throw new IllegalArgumentException(oops);\n                    }\n                }\n            }\n        }\n    }\n\n    private void processClass(final Class<?> tagClass)\n    {\n        final Tag tag = tagClass.getAnnotation(Tag.class);\n        if (tag != null)\n        {\n            // I've only seen tags being null under eclipse when they mark\n            // enums. I think this is an Eclipse bug.\n            // This shouldn't be affected by our standard build\n\n            final TagValidator validator = createValidatorFor(tag.value());\n            TagKeySearch.findTagKeyIn(tagClass).ifPresent(results ->\n            {\n                if (validator instanceof ExactMatchValidator)\n                {\n                    final ExactMatchValidator exactMatch = (ExactMatchValidator) validator;\n                    /**\n                     * If our validator also supports direct lookup of valid values, fill them here\n                     */\n                    fillExactMatches(exactMatch, tagClass.getDeclaredFields());\n                    if (tagClass.isEnum())\n                    {\n                        fillEnumerationValues(exactMatch, tagClass);\n                    }\n                    /**\n                     * With classes let us share enum values\n                     */\n                    for (final Class<? extends Enum<?>> withClass : tag.with())\n                    {\n                        fillEnumerationValues(exactMatch, withClass);\n                    }\n                }\n                if (validator instanceof NumericValidator)\n                {\n                    final NumericValidator numeric = (NumericValidator) validator;\n                    numeric.setRange(tag.range().min(), tag.range().max());\n                    for (final long excludeMe : tag.range().exclude())\n                    {\n                        numeric.excludeValue(excludeMe);\n                    }\n                }\n                this.validators.put(tagClass, results.getKeyName(), results.getTagKey(), validator);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/cache/CachingValidator.java",
    "content": "package org.openstreetmap.atlas.tags.cache;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * Implementation of cache for Validators.\n * <P>\n * Used by {@link org.openstreetmap.atlas.tags.annotations.validation.Validators Validators}\n * implicitly. Can be used on its own.\n * <p>\n * CachingValidator uses {@link Tagger} for caching actual values of given Tag.\n *\n * @author gpogulsky\n */\npublic class CachingValidator\n{\n    private static CachingValidator INSTANCE = new CachingValidator();\n\n    @SuppressWarnings(\"rawtypes\")\n    private final Map<Class, Tagger> map;\n\n    public static CachingValidator getInstance()\n    {\n        return INSTANCE;\n    }\n\n    public CachingValidator()\n    {\n        this.map = new HashMap<>();\n    }\n\n    /**\n     * Provides Enum value associated with the given Tag type for an object, if it exists.\n     * <p>\n     * {@link org.openstreetmap.atlas.tags.annotations.validation.Validators#from(Class, Taggable)\n     * Validators.from} is using this method implicitly. This method could be used on its own in\n     * place of Validators.from.\n     *\n     * @param <T>\n     *            the type of enum tag we're parsing\n     * @param tagType\n     *            the enum style tag that we want a possible value from\n     * @param taggable\n     *            the source of tags and their values\n     * @return an empty optional if the enum isn't a tag, doesn't have a key, the value isn't found\n     *         in taggable, or no enum value matches (ignoring case) the tag's value\n     */\n    public <T extends Enum<T>> Optional<T> from(final Class<T> tagType, final Taggable taggable)\n    {\n        final Tagger<T> tagger = this.getTagger(tagType);\n        return tagger.getTag(taggable);\n    }\n\n    private synchronized <T extends Enum<T>> Tagger<T> addTagger(final Class<T> tagType)\n    {\n        @SuppressWarnings(\"unchecked\")\n        Tagger<T> tagger = this.map.get(tagType);\n\n        if (tagger == null)\n        {\n            tagger = new Tagger<T>(tagType);\n            this.map.put(tagType, tagger);\n        }\n\n        return tagger;\n    }\n\n    private <T extends Enum<T>> Tagger<T> getTagger(final Class<T> tagType)\n    {\n        @SuppressWarnings(\"unchecked\")\n        Tagger<T> tagger = this.map.get(tagType);\n\n        if (tagger == null)\n        {\n            tagger = this.addTagger(tagType);\n        }\n\n        return tagger;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/cache/Tagger.java",
    "content": "package org.openstreetmap.atlas.tags.cache;\n\nimport java.io.Serializable;\nimport java.util.Optional;\nimport java.util.concurrent.ExecutionException;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\n\n/**\n * Cache for Tags of certain type. For applications that check tags on big numbers of objects, it\n * would save time to cache associations between tag names and their representative Enum values.\n *\n * @author gpogulsky\n * @author sbhalekar\n * @param <T>\n *            - type of tag Enum class\n */\npublic class Tagger<T extends Enum<T>> implements Serializable\n{\n    private static final long serialVersionUID = -9170158494924659179L;\n\n    private final Class<T> type;\n    private final String tagName;\n    private final Cache<String, Optional<T>> cache;\n\n    public Tagger(final Class<T> type)\n    {\n        // This would not work properly with localized Tags.\n        // So far we don't have any Enum-based tags that are localized.\n        // But if they appear, this code should prevent those (throw).\n\n        this.type = type;\n        this.tagName = Validators.findTagNameIn(type);\n        this.cache = CacheBuilder.newBuilder().build();\n    }\n\n    public Optional<T> getTag(final Taggable taggable)\n    {\n        final Optional<String> possibleTagValue = taggable.getTag(this.tagName);\n        if (possibleTagValue.isPresent())\n        {\n            final String tagValue = possibleTagValue.get();\n            try\n            {\n                // Referenced from\n                // https://github.com/google/guava/wiki/CachesExplained#from-a-callable\n                // If tagValue is present in the cache then return the value; otherwise execute\n                // function add the value to cache and return it\n                return this.cache.get(tagValue,\n                        () -> Validators.fromAnnotation(Tagger.this.type, taggable));\n            }\n            // this exception is thrown by the Callable in the get method of the cache. Ideally\n            // we should never hit this exception\n            catch (final ExecutionException e)\n            {\n                throw new CoreException(\"Error getting tag value from the cache\", e);\n            }\n        }\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/ConfiguredTaggableFilter.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.configuration.Configuration;\n\n/**\n * List of filters defined in a configuration object.\n *\n * @author matthieun\n */\npublic class ConfiguredTaggableFilter implements Predicate<Taggable>, Serializable\n{\n    private static final long serialVersionUID = -3849791821180104953L;\n    public static final String FILTERS_CONFIGURATION_NAME = \"filters\";\n\n    private final List<TaggableFilter> filters;\n\n    @SuppressWarnings(\"unchecked\")\n    public ConfiguredTaggableFilter(final Configuration configuration)\n    {\n        this.filters = ((List<String>) configuration.get(FILTERS_CONFIGURATION_NAME).valueOption()\n                .orElseThrow(() -> new CoreException(\"No filters defined in configuration {}\",\n                        configuration)))\n                .stream().map(TaggableFilter::forDefinition).collect(Collectors.toList());\n    }\n\n    @Override\n    public boolean test(final Taggable taggable)\n    {\n        for (final TaggableFilter filter : this.filters)\n        {\n            if (!filter.test(taggable))\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.filters.toString();\n    }\n\n    protected List<TaggableFilter> getFilters()\n    {\n        return this.filters;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/LineFilterConverter.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter.TreeBoolean;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\n\n/**\n * Parses a {@link TaggableFilter}'s line.\n *\n * @author matthieun\n */\npublic class LineFilterConverter implements TwoWayConverter<String, TaggableFilter>\n{\n    private static final String VALUES_SEPARATOR = \",\";\n    private static final String KEY_VALUE_SEPARATOR = \"->\";\n    private static final Predicate<Taggable> ALL_VALID = (Predicate<Taggable> & Serializable) taggable -> true;\n\n    @Override\n    public String backwardConvert(final TaggableFilter object)\n    {\n        return backwardConvert(object, 0);\n    }\n\n    @Override\n    public TaggableFilter convert(final String object)\n    {\n        return convert(object, TreeBoolean.OR, 0);\n    }\n\n    private String backwardConvert(final TaggableFilter object, final int numberOfOccurrences)\n    {\n        final Optional<String> definition = object.getDefinition();\n        if (definition.isPresent())\n        {\n            return definition.get();\n        }\n        else\n        {\n            final int numberOfSeparators = numberOfSeparators(numberOfOccurrences);\n            final String separatorCharacter = object.getTreeBoolean().separator();\n            final StringList separatorList = new StringList();\n            for (int i = 0; i < numberOfSeparators; i++)\n            {\n                separatorList.add(separatorCharacter);\n            }\n            final String separator = separatorList.join(\"\");\n            final StringList result = new StringList();\n            object.getChildren()\n                    .forEach(child -> result.add(backwardConvert(child, numberOfOccurrences + 1)));\n            return result.join(separator);\n        }\n    }\n\n    private TaggableFilter convert(final String object, final TreeBoolean treeBoolean,\n            final int numberOfOccurrences)\n    {\n        final String regex = regex(treeBoolean.separator(),\n                numberOfSeparators(numberOfOccurrences));\n        final StringList split = StringList.splitByRegex(object, regex);\n        final List<TaggableFilter> children = new ArrayList<>();\n        if (split.size() > 1)\n        {\n            // There is a split on the initial operation\n            for (final String value : split)\n            {\n                children.add(convert(value, treeBoolean.other(), numberOfOccurrences + 1));\n            }\n            return new TaggableFilter(children, treeBoolean);\n        }\n        else if (object.contains(treeBoolean.other().separator()))\n        {\n            // We need to keep splitting\n            children.add(convert(object, treeBoolean.other(), numberOfOccurrences + 1));\n            return new TaggableFilter(children, treeBoolean);\n        }\n        else\n        {\n            final String definition = split.get(0);\n            final Predicate<Taggable> simple = simple(definition);\n            return new TaggableFilter(simple, definition);\n        }\n    }\n\n    private String escaped(final String value)\n    {\n        if (TreeBoolean.OR.separator().equals(value))\n        {\n            return \"\\\\\" + TreeBoolean.OR.separator();\n        }\n        return value;\n    }\n\n    private int numberOfSeparators(final int numberOfOccurrences)\n    {\n        return numberOfOccurrences / 2 + 1;\n    }\n\n    private String regex(final String character, final int numberOfOccurrences)\n    {\n        if (numberOfOccurrences < 1)\n        {\n            throw new CoreException(\"Invalid number of occurences for pattern with {}: {}\",\n                    character, numberOfOccurrences);\n        }\n        // For || an example is \"(?<!\\|)\\|{2}(?!\\|)\"\n        // One look before and one lookahead\n        final String escapedCharacter = escaped(character);\n        final String negativeLookBefore = \"(?<!\" + escapedCharacter + \")\";\n        String characterTimesNumber = escapedCharacter + \"{\" + numberOfOccurrences + \"}\";\n        if (\"\\\\|{2}\".equals(characterTimesNumber))\n        {\n            // For backwards compatibility, use the ^ as a replacement option for ||\n            characterTimesNumber = \"(\" + characterTimesNumber + \"|\\\\^)\";\n        }\n        final String negativeLookAhead = \"(?!\" + escapedCharacter + \")\";\n        return negativeLookBefore + characterTimesNumber + negativeLookAhead;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Predicate<Taggable> simple(final String simple)\n    {\n        if (\"\".equals(simple))\n        {\n            return ALL_VALID;\n        }\n        final StringList split = StringList.split(simple, KEY_VALUE_SEPARATOR);\n        if (split.size() != 2)\n        {\n            throw new CoreException(\"Taggable filter definition \\\"{}\\\" is invalid.\", simple);\n        }\n        final String key = split.get(0);\n        final StringList values = StringList.split(split.get(1), VALUES_SEPARATOR);\n        return (Serializable & Predicate<Taggable>) taggable -> taggable.containsValue(key, values);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/README.md",
    "content": "# Tag filtering\n\n## TaggableFilter\n\n`TaggableFilter` is an extension of `Predicate<Taggable>` that allows for easy string definitions. With a String containing a `TaggableFilter` definition, one can be created easily:\n\n```java\nString definition;\nPredicate<Taggable> filter = TaggableFilter.forDefinition(definition);\n```\n\n## String definition\n\n`TaggableFilter` is built with a decision tree that alternates the OR and AND operands.\n\n```\n                OR\n              /    \\\n            /        \\\n         AND         Test\n       /     \\\n     /         \\\n  Test         Test\n```\n\nEach leaf node is a \"simple\" filter, based on tags. The taggable filter is a collection of simple filters with AND and OR operands.\n\n### Simple filters\n\n#### One tag\n\n```\nwater->pond\n```\n\nOnly features with water=pond in their tags will pass this test.\n\n#### Multiple tag values\n\n```\nwater->pond,lake,canal\n```\n\nOnly features with water=pond or water=lake or water=canal in their tags will pass this test.\n\n#### Negation\n\n```\nwater->!pond\n```\n\nAnything without water=pond\n\n```\nwater->!pond,!lake\n```\n\nAnything without water=pond or without water=lake. That one is not very useful as it is always true because one of the two will always be true (The water tag can have only one value).\n\n```\nwater->!pond&water->!lake\n```\n\nAnything without water=pond and without water=lake.\n\n```\nwater->!\n```\n\nAnything without water tag.\n\n#### Wildcard\n\n```\nwater->*\n```\n\nAnything with a water tag.\n\n### Tree logic\n\nAND and OR operands are represented by `&` and `|` respectively. The same operands one level down are `&&` and `||`. Two levels down are `&&&` and `|||` and so on.\n\n**All trees always start with OR!** This means that `a&b|c` translates to `(a AND b) OR c`. To achieve `a AND (b OR c)` one needs to write `a&b||c`\n\nFor example:\n\n```\n                      OR\n                    /    \\\n                  /        \\\n               AND        highway=motorway\n             /     \\\n           /         \\\nhighway=service    service=parking_aisle\n```\n\nIs defined by:\n\n```\nhighway->service&service->parking_aisle|highway->motorway\n```\n\nAnd\n\n```\n                      OR\n                    /    \\\n                  /        \\\n               AND        highway=motorway\n             /     \\\n           /         \\\nhighway=service       OR\n                    /    \\\n                  /        \\\n       cycleway=lane       AND\n                         /     \\\n                       /         \\\n           cycleway:lane=*      No cycleway tag       \n```\n\nIs defined by:\n\n```\nhighway->service&cycleway->lane||cycleway:lane->*&&cycleway->!|highway->motorway\n```\n\n#### Backwards compatibility\n\nAn older version of `TaggableFilter` had only two OR levels possible, and the lower OR level was represented by `^`. This is still allowed and is interpreted by the parser as `||`.\n\n## ConfiguredTaggableFilter\n\n`ConfiguredTaggableFilter` is a `TaggableFilter` that can be created from a JSON file that contains multiple of those above filters:\n\n```javascript\n{\n    \"filters\": [\n        \"access->!no|motor_vehicle->yes|motorcar->yes|vehicle->yes\",\n        \"oneway->!reversible\",\n        \"route->ferry|man_made->pier|junction->roundabout|highway->motorway\"\n    ]\n}\n```\n\nEach line in that filter must pass for the `TaggableFilter` to pass a `Taggable`. It could be translated to an AND of each filter.\n\nHere it is equivalent to:\n\n```javascript\n{\n    \"filters\": [\n        \"access->!no||motor_vehicle->yes||motorcar->yes||vehicle->yes&oneway->!reversible&route->ferry||man_made->pier||junction->roundabout||highway->motorway\"\n    ]\n}\n```\n\n## RegexTaggableFilter\n\n`RegexTaggableFilter` is an extension of `Predicate<Taggable>` that allows filtering certain tag values based on regex patterns. \nThe filter also accepts a map of values that are excepted from the regex patterns. \n\nIn order to create `RegexTaggableFilter` one must provide:\n- a set of String values representing the tag names that need to be checked\n- a set of String values representing the regex used to match the values\n- optionally, a map of tag names and sets of excepted values.\n\nFor example: \n\n```java\nfinal Set<String> tagNames = new HashSet<>(Arrays.asList(\"source\", \"highway\"));\nfinal Set<String> regex = new HashSet<>(Arrays.asList(\".*(?i)\\\\bmap\\\\b.*\", \".*(?i)\\\\bsecondary\\\\b.*\"));\nfinal HashMap<String, Set<String>> exceptions = new HashMap<>(Map.of(\n        \"source\", Set.of(\"personal map\", \"public map\")\n));\n```\n\nFor a simple filter there is the option to create a `RegexTaggableFilter` from a `String`. This constructor does not support\npassing the exception map which defaults in an empty `HashMap`. Example of `String` for this case:\n```\n\"source,highway|.*illegal.*,.*secondary.*\"\n```\nThe first part before the `|` character represents the list of tag names separated by commas. The second part after the \n`|` represents the list of regex patterns, also separated by commas. Do take into account that commas in the regex pattern\nmight cause an incorrect construction since the creation of the list of regex from string uses commas for splitting.\n\nIf the `Taggable` object contains a key that is part of the tagNames set, matches at least one of the given regex patterns\nand the tag-value combination is not part of the exceptions map, the test method will return a positive response.\nAlso, if the list of tag names is empty, the test method will always return true.\n\nThe `RegexTaggableFilter` also offers a `getMatchedTags` method that returns a joined String of all the tag names that passed\nthe test.\n\n##ConfiguredFilter\n\nThe `ConfiguredFilter` reads a json configuration files that follows a certain schema and creates filters based on it,\nincluding `TaggableFilter` and `RegexTaggableFilter`.\n\nExample of configuration json:\n```\n{\n    \"my\":\n    {\n        \"conf\":\n        {\n            \"filter\":\n            {\n                \"predicate\": \"....\",\n                \"geometry.wkb\":\n                [\n                    \"...\", \"...\"\n                ],\n                \"taggableFilter\": \"...\",\n                \"regexTaggableFilter\": \"...\"\n            }\n        }\n    }\n}\n```\nThe filter can be accessed using \"my.conf\" as root, and \"filter\" as name. The `ConfiguredFilter` predicate returns true\nif all the composing filter predicates are true."
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/RegexTaggableFilter.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * This {@link Taggable} filter relies on regex patterns to verify specific tag values.\n * \n * @author mm-ciub on 11/09/2020.\n */\npublic class RegexTaggableFilter implements Predicate<Taggable>, Serializable\n{\n    private static final String FILTER_DELIMITER = \"\\\\|\";\n    private static final String COMMA = \",\";\n\n    private final Set<String> tagNames;\n    private final Set<Pattern> regexPatterns;\n    private final Map<String, Set<String>> exceptions;\n\n    /**\n     * @param tagNames\n     *            - the set of tag names who's value will be tested\n     * @param regex\n     *            - a set of regex strings that will validate the value for each tag\n     * @param exceptions\n     *            - a map of tag names and values that are valid regardless if they match the regex\n     *            pattern\n     */\n    public RegexTaggableFilter(final Set<String> tagNames, final Set<String> regex,\n            final Map<String, Set<String>> exceptions)\n    {\n        this.tagNames = tagNames;\n        this.regexPatterns = regex.stream().map(Pattern::compile).collect(Collectors.toSet());\n        this.exceptions = exceptions != null ? exceptions : new HashMap<>();\n    }\n\n    /**\n     * Useful constructor for inline configuration. This option does not support passing exceptions.\n     *\n     * @param definition\n     *            - The {@link String} definition of the filter example:\n     *            \"tagName1,tagName2|regex1,regex2,regex3\"\n     */\n    public RegexTaggableFilter(final String definition)\n    {\n        this.exceptions = new HashMap<>();\n        final String[] filter = definition.split(FILTER_DELIMITER);\n        if (filter.length == 2)\n        {\n            this.tagNames = Set.of(filter[0].split(COMMA));\n            this.regexPatterns = Stream.of(filter[1].split(COMMA)).map(Pattern::compile)\n                    .collect(Collectors.toSet());\n        }\n        else\n        {\n            this.tagNames = new HashSet<>();\n            this.regexPatterns = new HashSet<>();\n        }\n    }\n\n    /**\n     * Returns a joined String containing the names of the tags that match at least one of the regex\n     * patterns and are not an exception\n     * \n     * @param taggable\n     *            - the element containing the tags to be checked\n     * @return String - example: source,barrier,boundary\n     */\n    public String getMatchedTags(final Taggable taggable)\n    {\n        final Set<String> matchedTags = findMatches(taggable);\n        return String.join(\",\", matchedTags);\n    }\n\n    @Override\n    public boolean test(final Taggable taggable)\n    {\n        if (this.tagNames.isEmpty())\n        {\n            return true;\n        }\n        final Set<String> matchedTags = findMatches(taggable);\n        return !matchedTags.isEmpty();\n    }\n\n    private Set<String> findMatches(final Taggable taggable)\n    {\n        final Set<String> matchedTags = new HashSet<>();\n        for (final String tagName : this.tagNames)\n        {\n            final Optional<String> tagValue = taggable.getTag(tagName);\n            if (tagValue.isPresent())\n            {\n                final Optional<Matcher> match = this.regexPatterns.stream()\n                        .map(pattern -> pattern.matcher(tagValue.get())).filter(Matcher::find)\n                        .findAny();\n                if (match.isPresent() && !(this.exceptions.containsKey(tagName)\n                        && this.exceptions.get(tagName).contains(tagValue.get())))\n                {\n                    matchedTags.add(tagName);\n                }\n            }\n        }\n        return matchedTags;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/TaggableFilter.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\n\n/**\n * {@link Taggable} filter that relies on a String definition\n * <p>\n * Examples of String definition:\n * <p>\n * highway=motorway AND name=[not empty] <br>\n * highway-&gt;motorway&amp;name-&gt;*\n * <p>\n * highway=motorway OR oneway=[not]yes <br>\n * highway-&gt;motorway|oneway-&gt;!yes\n * <p>\n * highway=motorway OR [no \"name\" tag] <br>\n * highway-&gt;motorway|name-&gt;!\n * <p>\n * amenity=bus_station OR highway=bus_stop OR ( (bus=* OR trolleybus=*) AND\n * public_transport=[stop_position OR platform OR station] ) <br>\n * amenity-&gt;bus_station|highway-&gt;bus_stop|bus-&gt;*||trolleybus-&gt;*&amp;public_transport-&gt;\n * stop_position, platform,station\n *\n * @author matthieun\n */\npublic class TaggableFilter implements Predicate<Taggable>, Serializable\n{\n    /**\n     * @author matthieun\n     */\n    protected enum TreeBoolean\n    {\n        AND,\n        OR;\n\n        public TreeBoolean other()\n        {\n            switch (this)\n            {\n                case AND:\n                    return OR;\n                case OR:\n                    return AND;\n                default:\n                    throw new CoreException(ERROR_MESSAGE, this);\n            }\n        }\n\n        public String separator()\n        {\n            switch (this)\n            {\n                case AND:\n                    return \"&\";\n                case OR:\n                    return \"|\";\n                default:\n                    throw new CoreException(ERROR_MESSAGE, this);\n            }\n        }\n    }\n\n    private static final long serialVersionUID = 5697377487014951158L;\n    private static final String ERROR_MESSAGE = \"Unknown TreeBoolean {}\";\n\n    private final List<TaggableFilter> children;\n    private final TreeBoolean treeBoolean;\n    private final Predicate<Taggable> simple;\n    private final String definition;\n\n    public static TaggableFilter forDefinition(final String definition)\n    {\n        return new LineFilterConverter().convert(definition);\n    }\n\n    /**\n     * @param definition\n     *            The {@link String} definition of the filter\n     * @deprecated Use {@code TaggableFilter.forDefinition(definition)} instead.\n     */\n    @Deprecated()\n    public TaggableFilter(final String definition)\n    {\n        this(TaggableFilter.forDefinition(definition));\n    }\n\n    protected TaggableFilter(final List<TaggableFilter> children, final TreeBoolean treeBoolean)\n    {\n        this.children = children;\n        this.treeBoolean = treeBoolean;\n        this.simple = null;\n        this.definition = null;\n    }\n\n    protected TaggableFilter(final Predicate<Taggable> simple, final String definition)\n    {\n        this.children = new ArrayList<>();\n        this.treeBoolean = TreeBoolean.OR;\n        this.simple = simple;\n        this.definition = definition;\n    }\n\n    private TaggableFilter(final TaggableFilter other)\n    {\n        this.children = other.children;\n        this.treeBoolean = other.treeBoolean;\n        this.simple = other.simple;\n        this.definition = other.definition;\n    }\n\n    public TaggableMatcher convertToTaggableMatcher()\n    {\n        return new TaggableFilterToMatcherConverter().convert(this);\n    }\n\n    @Override\n    public boolean test(final Taggable taggable)\n    {\n        if (this.simple != null)\n        {\n            return this.simple.test(taggable);\n        }\n        if (this.children.isEmpty())\n        {\n            throw new CoreException(\"Malformed predicate {}\", this);\n        }\n        switch (this.treeBoolean)\n        {\n            case AND:\n                return this.children.stream().allMatch(tree -> tree.test(taggable));\n            case OR:\n                return this.children.stream().anyMatch(tree -> tree.test(taggable));\n            default:\n                throw new CoreException(ERROR_MESSAGE, this);\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return new LineFilterConverter().backwardConvert(this);\n    }\n\n    protected List<TaggableFilter> getChildren()\n    {\n        return this.children;\n    }\n\n    protected Optional<String> getDefinition()\n    {\n        return Optional.ofNullable(this.definition);\n    }\n\n    protected Predicate<Taggable> getSimple()\n    {\n        return this.simple;\n    }\n\n    protected TreeBoolean getTreeBoolean()\n    {\n        return this.treeBoolean;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/TaggableFilterToMatcherConverter.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Token;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\n\n/**\n * @author lcram\n */\npublic class TaggableFilterToMatcherConverter implements Converter<TaggableFilter, TaggableMatcher>\n{\n    protected static String toTaggableMatcherDefinition(final TaggableFilter filter)\n    {\n        if (filter.getSimple() != null)\n        {\n            final String definition = filter.getDefinition().orElseThrow(\n                    () -> new CoreException(\"Simple filter definition was not present\"));\n            final String[] split = definition.split(\"->\");\n            if (split.length != 2)\n            {\n                throw new CoreException(\"Array length was not 2 for split('->') on definition `{}'\",\n                        definition);\n            }\n            final String key = split[0];\n            final String value = split[1];\n\n            final String[] commaSeparatedValues = value.split(\",\");\n            if (commaSeparatedValues.length == 1)\n            {\n                return getKeyValueForSingleValue(key, value);\n            }\n            return getKeyValueForMultiValue(key, commaSeparatedValues);\n        }\n        return Token.TokenType.PAREN_OPEN.getLiteralValue()\n                + new StringList(filter.getChildren().stream()\n                        .map(TaggableFilterToMatcherConverter::toTaggableMatcherDefinition)\n                        .collect(Collectors.toList()))\n                        .join(\" \" + filter.getTreeBoolean().separator() + \" \")\n                + Token.TokenType.PAREN_CLOSE.getLiteralValue();\n    }\n\n    private static String getKeyValueForMultiValue(final String key,\n            final String[] commaSeparatedValues)\n    {\n        final StringBuilder keyValue = new StringBuilder();\n        keyValue.append(key);\n        keyValue.append(Token.TokenType.EQUAL.getLiteralValue());\n        keyValue.append(Token.TokenType.PAREN_OPEN.getLiteralValue());\n\n        // check for illegal values\n        final String commaSeparatedValuesString = new StringList(commaSeparatedValues).join(\",\");\n        for (final String value : commaSeparatedValues)\n        {\n            if (Token.TokenType.BANG.getLiteralValue().equals(value))\n            {\n                throw new CoreException(\n                        \"Cannot transpile `{}->{}' since composite value `{}' contains a lone `{}' operator.\"\n                                + \"\\n\"\n                                + \"Expression `{}->{}' is ambiguous and order dependent, please rewrite your TaggableFilter to remove it.\",\n                        key, commaSeparatedValuesString, commaSeparatedValuesString,\n                        Token.TokenType.BANG.getLiteralValue(), key, commaSeparatedValuesString);\n            }\n            if (\"*\".equals(value))\n            {\n                throw new CoreException(\n                        \"Cannot transpile `{}->{}' since composite value `{}' contains a lone `*' operator.\"\n                                + \"\\n\"\n                                + \"Expression `{}->{}' is ambiguous and order dependent, please rewrite your TaggableFilter to remove it.\",\n                        key, commaSeparatedValuesString, commaSeparatedValuesString, key,\n                        commaSeparatedValuesString);\n            }\n        }\n\n        keyValue.append(new StringList(commaSeparatedValues)\n                .join(\" \" + Token.TokenType.OR.getLiteralValue() + \" \"));\n        keyValue.append(Token.TokenType.PAREN_CLOSE.getLiteralValue());\n        return keyValue.toString();\n\n    }\n\n    private static String getKeyValueForSingleValue(final String key, final String value)\n    {\n        if (value.charAt(0) == Token.TokenType.BANG.getLiteralValue().charAt(0)\n                && value.length() == 1)\n        {\n            // case foo->!\n            // return !foo\n            return Token.TokenType.BANG.getLiteralValue() + key;\n        }\n        else if (value.charAt(0) == Token.TokenType.BANG.getLiteralValue().charAt(0)\n                && value.length() > 1)\n        {\n            // case foo->!bar\n            final String valueWithNoLeadingBang = value.substring(1);\n            // return (foo!=bar | !foo)\n            return Token.TokenType.PAREN_OPEN.getLiteralValue() + key\n                    + Token.TokenType.BANG_EQUAL.getLiteralValue() + valueWithNoLeadingBang + \" \"\n                    + Token.TokenType.OR.getLiteralValue() + \" \"\n                    + Token.TokenType.BANG.getLiteralValue() + key\n                    + Token.TokenType.PAREN_CLOSE.getLiteralValue();\n        }\n        else if (value.charAt(0) == '*' && value.length() == 1)\n        {\n            // case foo->*\n            // return foo\n            return key;\n        }\n        else if (value.charAt(0) == '*' && value.length() > 1)\n        {\n            // case foo->*bar\n            final String newValue = value.substring(1);\n            if (hasRegexCharacter(newValue))\n            {\n                throw new CoreException(\n                        \"Cannot transpile `{}->{}' since new value `{}' contains a regex control character.\",\n                        key, value, newValue);\n            }\n            // return foo=/.*bar/\n            return key + Token.TokenType.EQUAL.getLiteralValue()\n                    + Token.TokenType.REGEX.getLiteralValue() + \".*\" + newValue\n                    + Token.TokenType.REGEX.getLiteralValue();\n        }\n        else if (value.charAt(value.length() - 1) == '*' && value.length() > 1)\n        {\n            // case foo->bar*\n            final String newValue = value.substring(0, value.length() - 1);\n            if (hasRegexCharacter(newValue))\n            {\n                throw new CoreException(\n                        \"Cannot transpile `{}->{}' since new value `{}' contains a regex control character.\",\n                        key, value, newValue);\n            }\n            // return foo=/bar.*/\n            return key + Token.TokenType.EQUAL.getLiteralValue()\n                    + Token.TokenType.REGEX.getLiteralValue() + newValue + \".*\"\n                    + Token.TokenType.REGEX.getLiteralValue();\n        }\n        else\n        {\n            // case foo->bar\n            // return foo=bar\n            return key + Token.TokenType.EQUAL.getLiteralValue() + value;\n        }\n    }\n\n    private static boolean hasRegexCharacter(final String string)\n    {\n        return string.matches(\"^.*[^a-zA-Z0-9_ ].*$\");\n    }\n\n    @Override\n    public TaggableMatcher convert(final TaggableFilter filter)\n    {\n        String taggableMatcherDefinition = toTaggableMatcherDefinition(filter);\n        /*\n         * Remove leading `(' and trailing `)' if present, since they are redundant. For compound\n         * expressions, the converter always adds a redundant pair of parentheses (due to the\n         * implementation), so we know it is safe to remove them. There may be additional redundant\n         * parentheses that we miss. For example, `foo->bar&baz->bat' will become `((foo=bar &\n         * baz=bat))', so we'll remove the first pair of redundant parentheses but miss the inner\n         * pair. Ultimately we should find a better way to handle this.\n         */\n        if (taggableMatcherDefinition.charAt(0) == Token.TokenType.PAREN_OPEN.getLiteralValue()\n                .charAt(0)\n                && taggableMatcherDefinition.charAt(taggableMatcherDefinition.length()\n                        - 1) == Token.TokenType.PAREN_CLOSE.getLiteralValue().charAt(0))\n        {\n            taggableMatcherDefinition = taggableMatcherDefinition.substring(1,\n                    taggableMatcherDefinition.length() - 1);\n        }\n        return TaggableMatcher.from(taggableMatcherDefinition);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/ConfiguredTaggableMatcher.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.configuration.Configuration;\n\n/**\n * List of {@link TaggableMatcher}s defined in a configuration object.\n *\n * @author lcram\n */\npublic class ConfiguredTaggableMatcher implements Predicate<Taggable>, Serializable\n{\n    public static final String MATCHERS_CONFIGURATION_NAME = \"matchers\";\n\n    private static final long serialVersionUID = -1870768831799297979L;\n\n    private final List<TaggableMatcher> matchers;\n\n    @SuppressWarnings(\"unchecked\")\n    public ConfiguredTaggableMatcher(final Configuration configuration)\n    {\n        this.matchers = ((List<String>) configuration.get(MATCHERS_CONFIGURATION_NAME).valueOption()\n                .orElseThrow(() -> new CoreException(\"No matchers defined in configuration {}\",\n                        configuration)))\n                .stream().map(TaggableMatcher::from).collect(Collectors.toList());\n    }\n\n    @Override\n    public boolean test(final Taggable taggable)\n    {\n        for (final TaggableMatcher matcher : this.matchers)\n        {\n            if (!matcher.test(taggable))\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.matchers.toString();\n    }\n\n    protected List<TaggableMatcher> getMatchers()\n    {\n        return this.matchers;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/README.md",
    "content": "# TaggableMatcher\n\n#### Table of Contents\n1. [Quick Intro and Examples](#quick-intro-and-examples)\n   * [Some sample matchers with explanations](#some-sample-matchers-with-explanations)\n2. [Basic Semantics](#basic-semantics)\n3. [Syntax Rules](#syntax-rules)\n   * [Table of Operators](#table-of-operators)\n   * [Precedence](#precedence)\n   * [Escaping and Whitespace](#escaping-and-whitespace)\n   * [More On Quoting](#more-on-quoting)\n   * [Regex](#regex)\n   * [Tree Representation](#tree-representation)\n4. [Converting Your Old TaggableFilters](#converting-your-old-taggableFilters)\n\n## Quick Intro and Examples\n`TaggableMatcher` is an extension of `Predicate<Taggable>` that supports intuitive string definitions.\nYou can create a new `TaggableMatcher` like:\n\n```java\n// Create a simple predicate:\nString definition = \"highway=primary\";\nPredicate<Taggable> predicate = TaggableMatcher.from(definition);\n\n// You could also do this to access the extra TaggableMatcher methods:\nTaggableMatcher matcher = TaggableMatcher.from(definition + \" & name=I280\");\n```\n\n### Some sample matchers with explanations\n\nMatch any `Taggable` containing a \"name\" tag with value \"John's Coffee Shop\":\n```\nname=\"John's Coffee Shop\"\n```\n\nMatch any `Taggable` that is a \"water=pond\" or is a \"water=lake\" that is *not* named \"Lake Michigan\":\n```\nwater = pond | water = lake & name != Lake\\ Michigan\n```\n\nMatch any tertiary or residential highway whose \"name\" tag contains the word \"street\" or \"Street\":\n```\n(highway=tertiary | highway=residential) & name=/.*\\b[s|S]treet\\b.*/\n```\n\nMatch all non-highway features, but also include primary and secondary highways:\n```\n!highway | highway=(primary | secondary)\n```\n\nMatch any `Taggable` that is a \"natural=lake\" or \"water=lake\":\n```\n(natural | water) = lake\n```\n\nAnd that's really all there is to it! Enough to get you started. Read on to get more details about\nthe syntax rules and various features.\n\n## Basic Semantics\nConsider the following definition:\n```\nfoo = bar\n```\nThis will create a `TaggableMatcher` with a single `key=value` pair constraint, \"foo=bar\". `TaggableMatcher`\nconstraints are case sensitive, but they are also *inclusive*, meaning that they automatically match anything\ncontaining *at least* the specified constraint. So the above `TaggableMatcher` would match both\n`Taggable(foo=bar)` as well as `Taggable(baz=bat, foo=bar)`. If we wanted to exclude the `Taggable`\ncontaining the \"baz=bat\" `key=value` pair, we would need to explicitly specify that in a compound constraint.\nOne way to do this is by combining the original `key=value` constraint and a negated `key-only` constraint with an\n`&` (AND) operator, like:\n```\nfoo=bar & !baz\n```\nIn the above example, `!baz` is the `key-only` constraint. Any constraint that does not include an `=` or\n`!=` operator will become a `key-only` constraint and will match against the *key only*, as the name suggests.\nSo something like:\n```\nwater & !name\n```\nis equivalent to the old `TaggableFilter` syntax:\n```\nwater->*&name->!\n```\nwhich will match any `Taggable` that both *has* a \"water\" key and does *not have* a \"name\" key, with no constraint on\nthe associated values.\n\n## Syntax Rules\n`TaggableMatcher` syntax follows basic boolean expression syntax, with the standard boolean `==`/`!=`\noperators replaced by `=`/`!=` to denote `key=value` pair constraints. Additionally, like boolean expressions,\nchained `=`/`!=` operators are forbidden by the semantic checker since these would be nonsense in\nthe context of tag matching (more on that in the `Tree Representation` section). For example,\nthis `TaggableMatcher` would generate the following error:\n```\nfoo = (bar = baz)\norg.openstreetmap.atlas.exception.CoreException: semantic error: invalid nested equality operators\n```\n\n### Table of Operators\n| Operator | Description |\n| -------- | ----------- |\n| `( .. )` | Group a subexpression to increase its precedence |\n| `!` | Negate a subexpression |\n| `=` | Specify a `key=value` pair constraint that must be **included** in a given `Taggable` |\n| `!=` | Specify `key=value` pair constraint that must be **excluded** from a given `Taggable` |\n| `&` | Specify an AND relationship between constraints or between specific keys/values within a constraint |\n| `^` | Specify an XOR relationship between constraints or between specific keys/values within a constraint |\n| `\\|` | Specify an OR relationship between constraints or between specific keys/values within a constraint |\n\n### Precedence\n`TaggableMatcher` operator precedence follows that of standard boolean expressions, but again with the `=`/`!=` operators\ntaking the place of the standard boolean `==`/`!=` operators. The following snippet lists operators in descending order,\nfrom highest precedence to lowest precedence. `TaggableMatcher` will evaluate higher precedence operators first and will\nfall back on left-to-right evaluation.\n```\n( .. )\n!\n=, != (these have equivalent precedence)\n&\n^\n|\n```\n\n### Escaping and Whitespace\nFor `TaggableMatcher` definitions, whitespace is not meaningful by default - the lexer will simply ignore it.\nThis means that, for example:\n```\nfoo=bar|baz=bat\n```\nand\n```\nfoo = bar | baz = bat\n```\nare semantically equivalent.\n\nIn order to include significant whitespace in a constraint, you must either escape the whitespace or wrap\nthe whitespace-containing literal in quotes (`TaggableMatcher` supports single ' or double \" quoted literals). For example,\nthe following matcher will fail with a syntax error:\n```\nname = Lake Michigan & water = lake\n\norg.openstreetmap.atlas.exception.CoreException: syntax error: unexpected token LITERAL(Michigan)\nname = Lake Michigan & water = lake\n~~~~~~~~~~~~^\n```\nInstead, you must do either:\n```\nname = Lake\\ Michigan & water = lake\n```\nor\n```\nname = \"Lake Michigan\" & water = lake\n```\nor\n```\nname = 'Lake Michigan' & water = lake\n```\n\nYou may also use `\\`, `\"`, `'` to escape operator characters. For example, to match a tag that contains\na literal \"=\" character you could do:\n```\nmath = 2+2\\=4\n```\nor\n```\nmath=\"2+2=4\"\n```\n\n### More On Quoting\nAs shown above, `TaggableMatcher` supports both single and double quoting. There are not many differences\nbetween the two, other than their escaping rules. Specifically, a single quoted string may contain\nunescaped double quote characters but must escape all inner single quote characters. And vice versa\nfor double quoted strings. A few examples of this:\n```\n// The ' does not need escaping, but the \" do\nname=\"John's \\\"Coffee\\\" Shop\"\n```\n\n```\n// Here we must escape the ', but we can skip escaping the \"\nname='John\\'s \"Coffee\" Shop'\n```\n\nFinally note that unlike many shell languages, a quoted string constitutes a complete literal, and the lexer will\n**not** coalesce multiple consecutive literals together. So something like:\n```\nfoo = bar\" and baz\"\n```\nwill generate the following syntax error:\n```\norg.openstreetmap.atlas.exception.CoreException: syntax error: unexpected token LITERAL( and baz)\nfoo = bar\" and baz\"\n~~~~~~~~~^\n```\n\n### Regex\n`TaggableMatcher` supports regex matching for keys and values with the following syntax:\n```\nname = /[l|L]ake.*/\n```\nAnything between the `/` symbols will be treated as a regex operand.\n\nRegexes are evaluated using\nJava's `String#matches(String)` method, which means that in order to get a match, the regex must match the\n**entire** key or value string. So for example, the above regex would match `Taggable(name=lake michigan)`,\nbut it would **not** match `Taggable(name=Arrow Lake)`. In order to match this second `Taggable` as well as\nthe first, the regex would need to be:\n```\nname = /.*[l|L]ake.*/\n```\n\nYou can escape a closing `/` symbol using the escape symbol `\\`, like so: `\\/`. This will pass the `\\/` directly\ninto the regex. For example:\n```\nname=/foo\\/bar/\n```\nwould result in a matcher regex `foo\\/bar`, which would match the string \"foo/bar\".\n\nhttps://regex101.com is a nice tool for testing regex and matching. Just note that unlike `TaggableMatcher` regex,\nit will match substrings of the input.\n\n### Tree Representation\nIt can be helpful to think about `TaggableMatchers` as syntax trees with the various operators as internal nodes.\nYou can print out pretty Unicode trees for your `TaggableMatchers` using the `TaggableMatcherPrinterCommand`, which\nyou can call from the command line as `print-matcher` using [Atlas Shell Tools](https://github.com/osmlab/atlas/tree/dev/atlas-shell-tools).\n\nFor example, we could print the following `TaggableMatcher` as this tree:\n```\na = b & c = d | e != f\n```\n```\n                        |\n            ┌───────────┴───────────┐\n            &                      !=\n      ┌─────┴─────┐           ┌─────┴─────┐\n      =           =           e           f\n   ┌──┴──┐     ┌──┴──┐\n   a     b     c     d\n```\nThe `TaggableMatcher` is evaluated by walking the tree in post-order (LRN).\n\nAs mentioned in an earlier section, chained \"=\"/\"!=\" operators are forbidden since expressions\ncontaining them are nonsensical in the context of tag matching. Expressions with chained equality\noperators become trees in which an equality operator is present in the subtree of another equality\noperator. For example, the matcher definition:\n```\na = b = c\n```\nbecomes this tree:\n```\n            =\n      ┌─────┴─────┐\n      a           =\n               ┌──┴──┐\n               b     c\n```\nThe `TaggableMatcher` semantic checker is able to detect subtrees like this and reject the matcher\ndefinition as invalid. In fact, the `TaggableMatcherPrinterCommand` will not even allow you to print\nthis tree since it fails the semantic check.\n\n## Converting Your Old TaggableFilters\nThe `TaggableMatcherPrinterCommand` mentioned earlier can also help you convert old `TaggableFilter`\ndefinitions into the new `TaggableMatcher` syntax. All you need to do is specify the `--reverse` option\nat the command line, and then pass as many `TaggableFilter` definitions as you would like to convert.\nFor example:\n```\n$ atlas print-matcher --reverse 'foo->bar|baz->bat' 'cat->mat&hat->zat,hello'\nfoo=bar | baz=bat\n(cat=mat & hat=(zat | hello))\n```\nNote that occasionally, `print-matcher` will include unnecessary parentheses its generated\n`TaggableMatcher` definitions (like the outermost parentheses in the above example).\nYou can safely remove those.\n\nAlso note that `print-matcher` will fail to convert certain kinds of valid `TaggableFilters`,\nspecifically those that make use of ambiguous combinations of operators. For example:\n```\n$ atlas print-matcher --reverse 'foo->bar,!,bat'\nprint-matcher: error: Cannot transpile `foo->bar,!,bat' since composite value `bar,!,bat' contains a lone `!' operator.\nExpression `foo->bar,!,bat' is ambiguous and order dependent, please rewrite your TaggableFilter to remove it.\n```\n\nYou can also use the `print-matcher` command to easily print a tree for an old-style `TaggableFilter`, should\nyou want help debugging one. Make use of your shell's command substitution feature like so:\n```\natlas print-matcher \"$(atlas print-matcher --reverse 'foo->bar||baz->bat&cat->hat')\"\n                                &\n                ┌───────────────┴───────────────┐\n                |                               =\n        ┌───────┴───────┐               ┌───────┴───────┐\n        =               =              cat             hat\n    ┌───┴───┐       ┌───┴───┐\n   foo     bar     baz     bat\n```\n\nFinally, as mentioned above, you can obtain `print-matcher` by installing [Atlas Shell Tools](https://github.com/osmlab/atlas/tree/dev/atlas-shell-tools).\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/TaggableMatcher.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Lexer;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Parser;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.SemanticChecker;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Token;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.ASTNode;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.TreePrinter;\n\n/**\n * @author lcram\n */\npublic final class TaggableMatcher implements Predicate<Taggable>, Serializable\n{\n    private static final long serialVersionUID = -3505184622005535575L;\n\n    private final ASTNode rootNode;\n    private final String definition;\n\n    public static TaggableMatcher from(final String definition)\n    {\n        if (definition.isEmpty())\n        {\n            return new TaggableMatcher(null, definition);\n        }\n        final List<Token> tokens = new Lexer().lex(definition);\n        final ASTNode rootNode = new Parser(tokens, definition).parse();\n        new SemanticChecker().check(rootNode);\n        return new TaggableMatcher(rootNode, definition);\n    }\n\n    private TaggableMatcher(final ASTNode rootNode, final String definition)\n    {\n        this.rootNode = rootNode;\n        this.definition = definition;\n    }\n\n    public String getDefinition()\n    {\n        return this.definition;\n    }\n\n    /**\n     * Get the length of the longest line for the printed tree returned by\n     * {@link TaggableMatcher#prettyPrintTree()}.\n     *\n     * @return the length of the longest line of the printed tree\n     */\n    public long lengthOfLongestLineForPrintedTree()\n    {\n        if (this.rootNode == null)\n        {\n            return 0L;\n        }\n        return TreePrinter.lengthOfLongestLineForTree(this.rootNode);\n    }\n\n    /**\n     * Print this {@link TaggableMatcher} in syntax tree form.\n     *\n     * @return this {@link TaggableMatcher} as a tree\n     */\n    public String prettyPrintTree()\n    {\n        if (this.rootNode == null)\n        {\n            return \"\";\n        }\n        return TreePrinter.print(this.rootNode);\n    }\n\n    @Override\n    public boolean test(final Taggable taggable)\n    {\n        if (this.rootNode == null)\n        {\n            return true;\n        }\n\n        final Map<String, String> tags = taggable.getTags();\n        final List<String> keys = new ArrayList<>();\n        final List<String> values = new ArrayList<>();\n        for (final Map.Entry<String, String> entry : tags.entrySet())\n        {\n            keys.add(entry.getKey());\n            values.add(entry.getValue());\n        }\n\n        return this.rootNode.match(keys, values);\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.getClass().getSimpleName() + \"(\" + this.definition + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/Lexer.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * This class can transform an input {@link String} into a sequence of {@link Token}s recognizable\n * by the {@link Parser}.\n * \n * @author lcram\n */\npublic class Lexer\n{\n    /**\n     * @author lcram\n     */\n    private static class InputBuffer\n    {\n        static final int EOF = -1;\n\n        private final String string;\n        private int position;\n\n        InputBuffer(final String string)\n        {\n            this.string = string;\n            this.position = 0;\n        }\n\n        int consumeCharacter()\n        {\n            if (this.position >= this.string.length())\n            {\n                return EOF;\n            }\n            return this.string.charAt(this.position++);\n        }\n\n        int peek()\n        {\n            if (this.position >= this.string.length())\n            {\n                return EOF;\n            }\n            return this.string.charAt(this.position);\n        }\n\n        void unconsume()\n        {\n            if (this.position > 0)\n            {\n                this.position--;\n            }\n        }\n    }\n\n    /**\n     * @author lcram\n     */\n    private static class LexemeBuffer\n    {\n        private final List<Character> characters;\n\n        LexemeBuffer()\n        {\n            this.characters = new ArrayList<>();\n        }\n\n        @Override\n        public String toString()\n        {\n            final StringBuilder builder = new StringBuilder();\n            for (final Character character : this.characters)\n            {\n                builder.append(character);\n            }\n            return builder.toString();\n        }\n\n        void addCharacter(final char character)\n        {\n            this.characters.add(character);\n        }\n\n        void clear()\n        {\n            this.characters.clear();\n        }\n\n        LexemeBuffer stripLeading()\n        {\n            this.characters.remove(0);\n            return this;\n        }\n\n        LexemeBuffer stripTrailing()\n        {\n            this.characters.remove(this.characters.size() - 1);\n            return this;\n        }\n    }\n\n    public static String debugString(final List<Token> lexedTokens)\n    {\n        final StringBuilder builder = new StringBuilder();\n        for (final Token token : lexedTokens)\n        {\n            builder.append(token.toString());\n            builder.append(\", \");\n        }\n        return builder.toString();\n    }\n\n    /**\n     * Lex a given input line.\n     *\n     * @param inputLine\n     *            the input line\n     * @return a {@link List} of the processed {@link Token}s\n     */\n    public List<Token> lex(final String inputLine) // NOSONAR\n    {\n        final List<Token> lexedTokens = new ArrayList<>();\n        final LexemeBuffer lexemeBuffer = new LexemeBuffer();\n        final InputBuffer inputBuffer = new InputBuffer(inputLine);\n        while (inputBuffer.peek() != InputBuffer.EOF)\n        {\n            if (isKeyValueCharacter(inputBuffer.peek())\n                    || inputBuffer.peek() == Token.TokenType.ESCAPE.getLiteralValue().charAt(0))\n            {\n                literal(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (isWhitespaceCharacter(inputBuffer.peek()))\n            {\n                whitespace(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.EQUAL.getLiteralValue().charAt(0))\n            {\n                equal(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.AND.getLiteralValue().charAt(0))\n            {\n                and(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.OR.getLiteralValue().charAt(0))\n            {\n                or(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.XOR.getLiteralValue().charAt(0))\n            {\n                xor(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.PAREN_OPEN.getLiteralValue().charAt(0))\n            {\n                parenOpen(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.PAREN_CLOSE.getLiteralValue().charAt(0))\n            {\n                parenClose(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.BANG.getLiteralValue().charAt(0))\n            {\n                bangOrBangEqual(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.REGEX.getLiteralValue().charAt(0))\n            {\n                regex(inputBuffer, lexemeBuffer, lexedTokens);\n            }\n            else if (inputBuffer.peek() == Token.TokenType.DOUBLE_QUOTE.getLiteralValue().charAt(0))\n            {\n                quote(inputBuffer, lexemeBuffer, lexedTokens,\n                        Token.TokenType.DOUBLE_QUOTE.getLiteralValue().charAt(0));\n            }\n            else if (inputBuffer.peek() == Token.TokenType.SINGLE_QUOTE.getLiteralValue().charAt(0))\n            {\n                quote(inputBuffer, lexemeBuffer, lexedTokens,\n                        Token.TokenType.SINGLE_QUOTE.getLiteralValue().charAt(0));\n            }\n            else\n            {\n                throw new CoreException(\"unknown char {}\", (char) inputBuffer.peek());\n            }\n\n            lexemeBuffer.clear();\n        }\n\n        // Remove all whitespace from token stream\n        return lexedTokens.stream().filter(token -> token.getType() != Token.TokenType.WHITESPACE)\n                .collect(Collectors.toList());\n    }\n\n    private void and(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n        lexedTokens\n                .add(new Token(Token.TokenType.AND, lexemeBuffer.toString(), inputBuffer.position));\n    }\n\n    private void bangOrBangEqual(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n        if (inputBuffer.peek() == Token.TokenType.EQUAL.getLiteralValue().charAt(0))\n        {\n            lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n            lexedTokens.add(new Token(Token.TokenType.BANG_EQUAL, lexemeBuffer.toString(),\n                    inputBuffer.position));\n        }\n        else\n        {\n            lexedTokens.add(\n                    new Token(Token.TokenType.BANG, lexemeBuffer.toString(), inputBuffer.position));\n        }\n    }\n\n    private void equal(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer, // NOSONAR\n            final List<Token> lexedTokens)\n    {\n        lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n        lexedTokens.add(\n                new Token(Token.TokenType.EQUAL, lexemeBuffer.toString(), inputBuffer.position));\n    }\n\n    private boolean isKeyValueCharacter(final int character)\n    {\n        /*\n         * Anything not in this list counts as a key/value character. To use characters on this list\n         * in a key/value literal, users must use escapes '\\' or double quotes '\"'.\n         */\n        return ((char) character) != Token.TokenType.AND.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.OR.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.XOR.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.EQUAL.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.PAREN_OPEN.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.PAREN_CLOSE.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.REGEX.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.BANG.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.DOUBLE_QUOTE.getLiteralValue().charAt(0)\n                && ((char) character) != Token.TokenType.SINGLE_QUOTE.getLiteralValue().charAt(0)\n                && !isWhitespaceCharacter((char) character);\n    }\n\n    private boolean isWhitespaceCharacter(final int character)\n    {\n        return Character.isWhitespace((char) character);\n    }\n\n    private void literal(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        int character;\n        do\n        {\n            character = inputBuffer.consumeCharacter();\n            if (character == InputBuffer.EOF)\n            {\n                break;\n            }\n            if (character == Token.TokenType.ESCAPE.getLiteralValue().charAt(0))\n            {\n                /*\n                 * If we see an ESCAPE, consume the immediate next character and place it in the\n                 * lexeme buffer. We throw the escape character '\\' out. If the escape character\n                 * comes just before the EOF, fail.\n                 */\n                final int escaped = inputBuffer.consumeCharacter();\n                if (escaped == InputBuffer.EOF)\n                {\n                    throwSyntaxError(\"EOF after '\\\\'\", inputBuffer, inputBuffer.string);\n                }\n                lexemeBuffer.addCharacter((char) escaped);\n            }\n            else\n            {\n                lexemeBuffer.addCharacter((char) character);\n            }\n        }\n        while (isKeyValueCharacter(character));\n\n        if (character != InputBuffer.EOF)\n        {\n            /*\n             * We reached the end of the putative LITERAL token, so give back the non-literal\n             * character to the input buffer for the main loop to re-process.\n             */\n            inputBuffer.unconsume();\n            lexemeBuffer.stripTrailing();\n        }\n\n        lexedTokens.add(\n                new Token(Token.TokenType.LITERAL, lexemeBuffer.toString(), inputBuffer.position));\n    }\n\n    private void or(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n        lexedTokens\n                .add(new Token(Token.TokenType.OR, lexemeBuffer.toString(), inputBuffer.position));\n    }\n\n    private void parenClose(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n        lexedTokens.add(new Token(Token.TokenType.PAREN_CLOSE, lexemeBuffer.toString(),\n                inputBuffer.position));\n    }\n\n    private void parenOpen(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n        lexedTokens.add(new Token(Token.TokenType.PAREN_OPEN, lexemeBuffer.toString(),\n                inputBuffer.position));\n    }\n\n    private void quote(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens, final char quoteType)\n    {\n        int character;\n        do\n        {\n            character = inputBuffer.consumeCharacter();\n            if (character == InputBuffer.EOF)\n            {\n                throwSyntaxError(\"EOF after `\" + quoteType + \"'\", inputBuffer, inputBuffer.string);\n            }\n            if (character == Token.TokenType.ESCAPE.getLiteralValue().charAt(0))\n            {\n                final int escaped = inputBuffer.consumeCharacter();\n                lexemeBuffer.addCharacter((char) escaped);\n            }\n            else\n            {\n                lexemeBuffer.addCharacter((char) character);\n            }\n        }\n        while (inputBuffer.peek() != quoteType);\n        // consume the trailing \"/'\n        inputBuffer.consumeCharacter();\n\n        // Strip leading \"/' character\n        final String lexeme = lexemeBuffer.stripLeading().toString();\n        if (quoteType == Token.TokenType.DOUBLE_QUOTE.getLiteralValue().charAt(0))\n        {\n            lexedTokens.add(new Token(Token.TokenType.DOUBLE_QUOTE, lexeme, inputBuffer.position));\n        }\n        else if (quoteType == Token.TokenType.SINGLE_QUOTE.getLiteralValue().charAt(0))\n        {\n            lexedTokens.add(new Token(Token.TokenType.SINGLE_QUOTE, lexeme, inputBuffer.position));\n        }\n        else\n        {\n            throw new CoreException(\"Unknown quote type `{}'\", quoteType);\n        }\n    }\n\n    private void regex(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        int character;\n        do\n        {\n            character = inputBuffer.consumeCharacter();\n            if (character == InputBuffer.EOF)\n            {\n                throwSyntaxError(\"EOF after '/'\", inputBuffer, inputBuffer.string);\n            }\n            if (character == Token.TokenType.ESCAPE.getLiteralValue().charAt(0))\n            {\n                /*\n                 * If the user is specifically escaping a '/', we need to make sure that '\\/' gets\n                 * into the regex and the '/' will not be interpreted as an end to the regex.\n                 */\n                if (inputBuffer.peek() == Token.TokenType.REGEX.getLiteralValue().charAt(0))\n                {\n                    lexemeBuffer.addCharacter((char) character);\n                    final int escapedForwardSlash = inputBuffer.consumeCharacter();\n                    lexemeBuffer.addCharacter((char) escapedForwardSlash);\n                }\n                // Otherwise, pass the '\\' forward into the regex normally\n                else\n                {\n                    lexemeBuffer.addCharacter((char) character);\n                }\n            }\n            else\n            {\n                lexemeBuffer.addCharacter((char) character);\n            }\n        }\n        while (inputBuffer.peek() != Token.TokenType.REGEX.getLiteralValue().charAt(0));\n        // consume the trailing '/'\n        inputBuffer.consumeCharacter();\n\n        // Strip leftover leading '/' character\n        final String lexeme = lexemeBuffer.stripLeading().toString();\n        lexedTokens.add(new Token(Token.TokenType.REGEX, lexeme, inputBuffer.position));\n    }\n\n    private void throwSyntaxError(final String unexpected, final InputBuffer inputBuffer,\n            final String inputLine)\n    {\n        final String arrow = \"~\".repeat(Math.max(0, inputBuffer.position)) + \"^\";\n        if (unexpected != null)\n        {\n            throw new CoreException(\"syntax error: unexpected {}\\n{}\\n{}\", unexpected, inputLine,\n                    arrow);\n        }\n        throw new CoreException(\"syntax error: unexpected input\\n{}\\n{}\", inputLine, arrow);\n    }\n\n    private void whitespace(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n        lexedTokens.add(new Token(Token.TokenType.WHITESPACE, lexemeBuffer.toString(),\n                inputBuffer.position));\n    }\n\n    private void xor(final InputBuffer inputBuffer, final LexemeBuffer lexemeBuffer,\n            final List<Token> lexedTokens)\n    {\n        lexemeBuffer.addCharacter((char) inputBuffer.consumeCharacter());\n        lexedTokens\n                .add(new Token(Token.TokenType.XOR, lexemeBuffer.toString(), inputBuffer.position));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/Parser.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing;\n\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.ASTNode;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.AndOperator;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.BangOperator;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.BinaryOperator;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.EqualsOperator;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.LiteralOperand;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.Operand;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.OrOperator;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.RegexOperand;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.UnaryOperator;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.XorOperator;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This class can transform a sequence of {@link Token}s into a syntactically valid abstract syntax\n * tree (AST) that can then be checked by the {@link SemanticChecker}. Finally, a\n * {@link TaggableMatcher} may walk this AST to determine if a {@link Taggable}'s tag map\n * corresponds to the matcher. This {@link Parser} implements an LL(1) {@link TaggableMatcher}\n * expression grammar using recursive descent. The grammar can be found below in comment form.\n *\n * @author lcram\n */\npublic class Parser\n{\n    /*\n     * The Grammar. Operator precedence is handled in the standard way. '=' and '!=' are treated as\n     * extremely \"sticky\" (i.e. high precedence) operators. The grammar is not capable of detecting\n     * \"nested\" '=' and '!=' operators (e.g. foo=(bar=baz)), which are syntactically valid but\n     * semantically invalid. Syntax trees containing nested equality operators must be dealt with at\n     * a later stage.\n     */\n\n    // OR -> XOR OR'\n    // OR' -> | XOR OR'\n    // OR' -> ''\n    // XOR -> AND XOR'\n    // XOR' -> | AND XOR'\n    // XOR' -> ''\n    // AND -> EQ AND'\n    // AND' -> & EQ AND'\n    // AND' -> ''\n    // EQ -> VALUE EQ'\n    // EQ' -> = VALUE EQ'\n    // EQ' -> != VALUE EQ'\n    // EQ' -> ''\n    // VALUE -> ( OR )\n    // VALUE -> ! VALUE\n    // VALUE -> literal\n    // VALUE -> /regex/\n\n    /**\n     * @author lcram\n     */\n    private static class TokenBuffer\n    {\n        private final List<Token> tokens;\n        private final String inputLine;\n        private int position;\n\n        TokenBuffer(final List<Token> tokens, final String inputLine)\n        {\n            this.tokens = tokens;\n            this.inputLine = inputLine;\n            this.position = 0;\n        }\n\n        void nextToken()\n        {\n            if (this.position < this.tokens.size())\n            {\n                this.position++;\n            }\n        }\n\n        Token peek()\n        {\n            if (this.position >= this.tokens.size())\n            {\n                return new Token(Token.TokenType.EOF, null, this.inputLine.length());\n            }\n            return this.tokens.get(this.position);\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(Parser.class);\n\n    private final TokenBuffer tokenBuffer;\n    private final String inputLine;\n\n    public Parser(final List<Token> tokens, final String inputLine)\n    {\n        this.tokenBuffer = new TokenBuffer(tokens, inputLine);\n        this.inputLine = inputLine;\n    }\n\n    public ASTNode parse()\n    {\n        BinaryOperator.clearIdentifierCounter();\n        UnaryOperator.clearIdentifierCounter();\n        Operand.clearIdentifierCounter();\n        return or();\n    }\n\n    private void accept(final Token.TokenType tokenType)\n    {\n        if (this.tokenBuffer.peek().getType() == tokenType)\n        {\n            logger.debug(\"ACCEPT: accepted {}({})\", this.tokenBuffer.peek().getType(),\n                    this.tokenBuffer.peek().getLexeme());\n            this.tokenBuffer.nextToken();\n        }\n        else\n        {\n            throwSyntaxError(tokenType, this.tokenBuffer.peek(), this.inputLine);\n        }\n    }\n\n    // AND -> EQ AND'\n    private ASTNode and()\n    {\n        ASTNode node = null;\n\n        logger.debug(\"AND: peek: {}({})\", this.tokenBuffer.peek().getType(),\n                this.tokenBuffer.peek().getLexeme());\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG\n                || this.tokenBuffer.peek().getType() == Token.TokenType.LITERAL\n                || this.tokenBuffer.peek().getType() == Token.TokenType.REGEX\n                || this.tokenBuffer.peek().getType() == Token.TokenType.PAREN_OPEN)\n        {\n            node = eq();\n            final ASTNode rightResult = andPrime();\n            if (rightResult != null)\n            {\n                node = new AndOperator(node, rightResult);\n            }\n        }\n        else\n        {\n            throwSyntaxError(null, this.tokenBuffer.peek(), this.inputLine);\n        }\n\n        return node;\n    }\n\n    // AND' -> & EQ AND'\n    // AND' -> ''\n    private ASTNode andPrime()\n    {\n        ASTNode node;\n\n        logger.debug(\"AND_PRIME: peek: {}({})\", this.tokenBuffer.peek().getType(),\n                this.tokenBuffer.peek().getLexeme());\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.AND)\n        {\n            logger.debug(\"AND_PRIME: try accepting: {}\", Token.TokenType.AND);\n            accept(Token.TokenType.AND);\n            node = eq();\n            final ASTNode rightResult = andPrime();\n            if (rightResult != null)\n            {\n                node = new AndOperator(node, rightResult);\n            }\n        }\n        else\n        {\n            // epsilon transition\n            logger.debug(\"AND_PRIME: taking epsilon\");\n            return null;\n        }\n\n        return node;\n    }\n\n    // EQ -> VALUE EQ'\n    private ASTNode eq()\n    {\n        ASTNode node = null;\n\n        logger.debug(\"EQ: peek: {}({})\", this.tokenBuffer.peek().getType(),\n                this.tokenBuffer.peek().getLexeme());\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG\n                || this.tokenBuffer.peek().getType() == Token.TokenType.LITERAL\n                || this.tokenBuffer.peek().getType() == Token.TokenType.REGEX\n                || this.tokenBuffer.peek().getType() == Token.TokenType.PAREN_OPEN)\n        {\n            node = value();\n            if (this.tokenBuffer.peek().getType() == Token.TokenType.EQUAL)\n            {\n                final ASTNode rightResult = eqPrime();\n                if (rightResult != null)\n                {\n                    node = new EqualsOperator(node, rightResult, false);\n                }\n            }\n            else if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG_EQUAL)\n            {\n                final ASTNode rightResult = eqPrime();\n                if (rightResult != null)\n                {\n                    node = new EqualsOperator(node, rightResult, true);\n                }\n            }\n        }\n        else\n        {\n            throwSyntaxError(null, this.tokenBuffer.peek(), this.inputLine);\n        }\n\n        return node;\n    }\n\n    // EQ' -> = VALUE EQ'\n    // EQ' -> != VALUE EQ'\n    // EQ' -> ''\n    private ASTNode eqPrime() // NOSONAR\n    {\n        ASTNode node;\n\n        logger.debug(\"EQ_PRIME: peek: {}({})\", this.tokenBuffer.peek().getType(),\n                this.tokenBuffer.peek().getLexeme());\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.EQUAL)\n        {\n            logger.debug(\"EQ_PRIME: try accepting: {}\", Token.TokenType.EQUAL);\n            accept(Token.TokenType.EQUAL);\n            node = value();\n            if (this.tokenBuffer.peek().getType() == Token.TokenType.EQUAL)\n            {\n                final ASTNode rightResult = eqPrime();\n                if (rightResult != null)\n                {\n                    node = new EqualsOperator(node, rightResult, false);\n                }\n            }\n            else if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG_EQUAL)\n            {\n                final ASTNode rightResult = eqPrime();\n                if (rightResult != null)\n                {\n                    node = new EqualsOperator(node, rightResult, true);\n                }\n            }\n        }\n        else if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG_EQUAL)\n        {\n            logger.debug(\"EQ_PRIME: try accepting: {}\", Token.TokenType.BANG_EQUAL);\n            accept(Token.TokenType.BANG_EQUAL);\n            node = value();\n            if (this.tokenBuffer.peek().getType() == Token.TokenType.EQUAL)\n            {\n                final ASTNode rightResult = eqPrime();\n                if (rightResult != null)\n                {\n                    node = new EqualsOperator(node, rightResult, false);\n                }\n            }\n            else if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG_EQUAL)\n            {\n                final ASTNode rightResult = eqPrime();\n                if (rightResult != null)\n                {\n                    node = new EqualsOperator(node, rightResult, true);\n                }\n            }\n        }\n        else\n        {\n            // epsilon transition\n            logger.error(\"EQ_PRIME: taking epsilon\");\n            return null;\n        }\n\n        return node;\n    }\n\n    // OR -> XOR OR'\n    private ASTNode or()\n    {\n        ASTNode node = null;\n\n        logger.debug(\"OR: peek: {}({})\", this.tokenBuffer.peek().getType(),\n                this.tokenBuffer.peek().getLexeme());\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG\n                || this.tokenBuffer.peek().getType() == Token.TokenType.LITERAL\n                || this.tokenBuffer.peek().getType() == Token.TokenType.REGEX\n                || this.tokenBuffer.peek().getType() == Token.TokenType.PAREN_OPEN)\n        {\n            node = xor();\n            final ASTNode rightResult = orPrime();\n            if (rightResult != null)\n            {\n                node = new OrOperator(node, rightResult);\n            }\n        }\n        else\n        {\n            throwSyntaxError(null, this.tokenBuffer.peek(), this.inputLine);\n        }\n\n        return node;\n    }\n\n    // OR' -> | XOR OR'\n    // OR' -> ''\n    private ASTNode orPrime()\n    {\n        ASTNode node = null;\n\n        logger.debug(\"OR_PRIME: peek: {}({})\", this.tokenBuffer.peek().getType(),\n                this.tokenBuffer.peek().getLexeme());\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.OR)\n        {\n            logger.debug(\"OR_PRIME: try accepting: {}\", Token.TokenType.OR);\n            accept(Token.TokenType.OR);\n            node = xor();\n            final ASTNode rightResult = orPrime();\n            if (rightResult != null)\n            {\n                node = new OrOperator(node, rightResult);\n            }\n        }\n        else if (this.tokenBuffer.peek().getType() == Token.TokenType.EOF)\n        {\n            // epsilon transition\n            logger.debug(\"OR_PRIME: taking epsilon\");\n            return null;\n        }\n        else if (this.tokenBuffer.peek().getType() == Token.TokenType.PAREN_CLOSE)\n        {\n            // epsilon transition\n            logger.debug(\"OR_PRIME: taking epsilon due to FOLLOW )\");\n            return null;\n        }\n        else\n        {\n            throwSyntaxError(null, this.tokenBuffer.peek(), this.inputLine);\n        }\n\n        return node;\n    }\n\n    private void throwSyntaxError(final Token.TokenType expectedTokenType, final Token currentToken,\n            final String inputLine)\n    {\n        final String arrow = \"~\".repeat(Math.max(0, currentToken.getIndexInLine())) + \"^\";\n        if (expectedTokenType == null)\n        {\n            throw new CoreException(\"syntax error: unexpected token {}({})\\n{}\\n{}\",\n                    currentToken.getType(), currentToken.getLexeme(), inputLine, arrow);\n        }\n        throw new CoreException(\"syntax error: expected {}, but saw {}({})\\n{}\\n{}\",\n                expectedTokenType, currentToken.getType(), currentToken.getLexeme(), inputLine,\n                arrow);\n    }\n\n    // VALUE -> ( OR )\n    // VALUE -> ! VALUE\n    // VALUE -> literal\n    // VALUE -> /regex/\n    private ASTNode value()\n    {\n        final String valueAcceptMessage = \"VALUE: try accepting: {}\";\n        ASTNode node = null;\n\n        logger.debug(\"VALUE: peek: {}({})\", this.tokenBuffer.peek().getType(),\n                this.tokenBuffer.peek().getLexeme());\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.PAREN_OPEN)\n        {\n            logger.debug(valueAcceptMessage, Token.TokenType.PAREN_OPEN);\n            accept(Token.TokenType.PAREN_OPEN);\n            node = or();\n            logger.debug(valueAcceptMessage, Token.TokenType.PAREN_CLOSE);\n            accept(Token.TokenType.PAREN_CLOSE);\n        }\n        else if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG)\n        {\n            logger.debug(valueAcceptMessage, Token.TokenType.BANG);\n            // accept the BANG first, and then parse the remaining token buffer\n            accept(Token.TokenType.BANG);\n            node = new BangOperator(value());\n        }\n        else if (this.tokenBuffer.peek().getType() == Token.TokenType.LITERAL)\n        {\n            logger.debug(valueAcceptMessage, Token.TokenType.LITERAL);\n            // Create the AST node first, since accepting will advance the token buffer\n            node = new LiteralOperand(this.tokenBuffer.peek());\n            accept(Token.TokenType.LITERAL);\n        }\n        else if (this.tokenBuffer.peek().getType() == Token.TokenType.REGEX)\n        {\n            logger.debug(valueAcceptMessage, Token.TokenType.REGEX);\n            // Create the AST node first, since accepting will advance the token buffer\n            node = new RegexOperand(this.tokenBuffer.peek());\n            accept(Token.TokenType.REGEX);\n        }\n        else\n        {\n            throwSyntaxError(null, this.tokenBuffer.peek(), this.inputLine);\n        }\n\n        return node;\n    }\n\n    // XOR -> AND XOR'\n    private ASTNode xor()\n    {\n        ASTNode node = null;\n\n        logger.debug(\"XOR: peek: {}({})\", this.tokenBuffer.peek().getType(),\n                this.tokenBuffer.peek().getLexeme());\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.BANG\n                || this.tokenBuffer.peek().getType() == Token.TokenType.LITERAL\n                || this.tokenBuffer.peek().getType() == Token.TokenType.REGEX\n                || this.tokenBuffer.peek().getType() == Token.TokenType.PAREN_OPEN)\n        {\n            node = and();\n            final ASTNode rightResult = xorPrime();\n            if (rightResult != null)\n            {\n                node = new XorOperator(node, rightResult);\n            }\n        }\n        else\n        {\n            throwSyntaxError(null, this.tokenBuffer.peek(), this.inputLine);\n        }\n\n        return node;\n    }\n\n    // XOR' -> | AND XOR'\n    // XOR' -> ''\n    private ASTNode xorPrime()\n    {\n        ASTNode node;\n\n        if (this.tokenBuffer.peek().getType() == Token.TokenType.XOR)\n        {\n            logger.debug(\"XOR_PRIME: try accepting: {}\", Token.TokenType.XOR);\n            accept(Token.TokenType.XOR);\n            node = and();\n            final ASTNode rightResult = xorPrime();\n            if (rightResult != null)\n            {\n                node = new XorOperator(node, rightResult);\n            }\n        }\n        else\n        {\n            // epsilon transition\n            logger.debug(\"XOR_PRIME: taking epsilon\");\n            return null;\n        }\n\n        return node;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/SemanticChecker.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.ASTNode;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.tree.EqualsOperator;\n\n/**\n * Semantic checker for a {@link TaggableMatcher} abstract syntax tree (AST). This checker will make\n * sure the supplied matcher does not contain invalid \"=\" or \"!=\" semantics. For e.g. \"foo = (bar !=\n * baz)\" is valid syntactically per the {@link TaggableMatcher} expression grammar, but not\n * semantically since it makes no sense for the purposes of tag matching. We will need to catch that\n * with this checker. Basically, we can walk the AST, and if the left or right subtree of a \"=\"/\"!=\"\n * operator contains another \"=\"/\"!=\" operator, then we fail.\n *\n * @author lcram\n */\npublic class SemanticChecker\n{\n    public void check(final ASTNode root)\n    {\n        if (root == null)\n        {\n            return;\n        }\n\n        if (root instanceof EqualsOperator && this.subtreeContainsEquals(root))\n        {\n            throw new CoreException(\"semantic error: invalid nested equality operators\");\n        }\n        check(root.getLeftChild());\n        check(root.getRightChild());\n        check(root.getCenterChild());\n    }\n\n    private boolean subtreeContainsEquals(final ASTNode root)\n    {\n        if (root == null)\n        {\n            return false;\n        }\n\n        final ASTNode leftRoot = root.getLeftChild();\n        final ASTNode rightRoot = root.getRightChild();\n        final ASTNode centerRoot = root.getCenterChild();\n        if (leftRoot instanceof EqualsOperator || rightRoot instanceof EqualsOperator\n                || centerRoot instanceof EqualsOperator)\n        {\n            return true;\n        }\n        return subtreeContainsEquals(leftRoot) || subtreeContainsEquals(rightRoot)\n                || subtreeContainsEquals(centerRoot);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/Token.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\n/**\n * @author lcram\n */\npublic class Token implements Serializable\n{\n    /**\n     * @author lcram\n     */\n    public enum TokenType\n    {\n        AND(\"&\"),\n\n        BANG(\"!\"),\n\n        BANG_EQUAL(\"!=\"),\n\n        DOUBLE_QUOTE(\"\\\"\"),\n\n        ESCAPE(\"\\\\\"),\n\n        EOF(null),\n\n        EQUAL(\"=\"),\n\n        LITERAL(null),\n\n        OR(\"|\"),\n\n        PAREN_OPEN(\"(\"),\n\n        PAREN_CLOSE(\")\"),\n\n        REGEX(\"/\"),\n\n        SINGLE_QUOTE(\"'\"),\n\n        WHITESPACE(null),\n\n        XOR(\"^\");\n\n        private final String literalValue;\n\n        TokenType(final String literalValue)\n        {\n            this.literalValue = literalValue;\n        }\n\n        public String getLiteralValue()\n        {\n            return this.literalValue;\n        }\n    }\n\n    private static final long serialVersionUID = -8498419139066512731L;\n\n    private final TokenType type;\n    private final String lexeme;\n    private final int indexInLine;\n\n    public Token(final TokenType type, final String lexeme, final int indexInLine)\n    {\n        this.lexeme = lexeme;\n        if (type == TokenType.DOUBLE_QUOTE || type == TokenType.SINGLE_QUOTE)\n        {\n            /*\n             * Override DOUBLE_QUOTE/SINGLE_QUOTE with regular LITERAL, since after lexing no other\n             * component cares about this distinction. Using LITERAL everywhere will simplify\n             * following code.\n             */\n            this.type = TokenType.LITERAL;\n            /*\n             * We need to add 2 back to the lexeme length to account for the \"/' characters we\n             * removed.\n             */\n            final int addBack = 2;\n            this.indexInLine = indexInLine - (lexeme != null ? lexeme.length() + addBack : 0);\n        }\n        else\n        {\n            this.type = type;\n            this.indexInLine = indexInLine - (lexeme != null ? lexeme.length() : 0);\n        }\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other == null || getClass() != other.getClass())\n        {\n            return false;\n        }\n        final Token token = (Token) other;\n        return this.type == token.type && Objects.equals(this.getLexeme(), token.getLexeme())\n                && this.indexInLine == token.indexInLine;\n    }\n\n    public int getIndexInLine()\n    {\n        return this.indexInLine;\n    }\n\n    public String getLexeme()\n    {\n        return this.lexeme;\n    }\n\n    public TokenType getType()\n    {\n        return this.type;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(this.type, this.getLexeme(), this.indexInLine);\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"(\" + this.type + \", \" + this.lexeme + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/ASTNode.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\n\n/**\n * A generic abstract syntax tree (AST) node. Any node must be able to print itself and its subtree,\n * as well as have some kind of name for debug purposes.\n *\n * @author lcram\n */\npublic abstract class ASTNode implements Serializable\n{\n    private static final long serialVersionUID = -2085619833468362629L;\n\n    /**\n     * Construct a debug printout of the entire tree. For a prettified version of this tree, try\n     * {@link ASTNode#getPrettyPrintText()}.\n     *\n     * @return the debug printout\n     */\n    public abstract String debugPrintTree();\n\n    /**\n     * Get the center child of this {@link ASTNode}. {@link UnaryOperator}s are the only type of\n     * node that have center children.\n     *\n     * @return the center child {@link ASTNode}\n     */\n    public abstract ASTNode getCenterChild();\n\n    public abstract int getIdentifier();\n\n    /**\n     * Get the left child of this {@link ASTNode}. {@link BinaryOperator}s are the only type of node\n     * that have left children.\n     *\n     * @return the left child {@link ASTNode}\n     */\n    public abstract ASTNode getLeftChild();\n\n    public abstract String getName();\n\n    /**\n     * Get the representation of this node for the purposes of {@link TaggableMatcher}'s pretty tree\n     * print functionality.\n     *\n     * @return the pretty tree\n     */\n    public abstract String getPrettyPrintText();\n\n    /**\n     * Get the right child of this {@link ASTNode}. {@link BinaryOperator}s are the only type of\n     * node that have right children.\n     *\n     * @return the right child {@link ASTNode}\n     */\n    public abstract ASTNode getRightChild();\n\n    public abstract boolean match(List<String> keys, List<String> values);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/AndOperator.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.util.List;\n\n/**\n * @author lcram\n */\npublic class AndOperator extends BinaryOperator\n{\n    public AndOperator(final ASTNode left, final ASTNode right)\n    {\n        super(left, right);\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"AND_\" + getIdentifier();\n    }\n\n    @Override\n    public String getPrettyPrintText()\n    {\n        return \"&\";\n    }\n\n    @Override\n    public boolean match(final List<String> keys, final List<String> values)\n    {\n        return getLeftChild().match(keys, values) && getRightChild().match(keys, values);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/BangOperator.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.util.List;\n\n/**\n * @author lcram\n */\npublic class BangOperator extends UnaryOperator\n{\n    public BangOperator(final ASTNode child)\n    {\n        super(child);\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"BANG_\" + getIdentifier();\n    }\n\n    @Override\n    public String getPrettyPrintText()\n    {\n        return \"!\";\n    }\n\n    @Override\n    public boolean match(final List<String> keys, final List<String> values)\n    {\n        return !getCenterChild().match(keys, values);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/BinaryOperator.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\n/**\n * @author lcram\n */\npublic abstract class BinaryOperator extends ASTNode\n{\n    private static int counter = 0;\n    private static final long serialVersionUID = -8367179322128266687L;\n\n    private final ASTNode left;\n    private final ASTNode right;\n    private final int identifier;\n\n    public static void clearIdentifierCounter()\n    {\n        counter = 0;\n    }\n\n    protected BinaryOperator(final ASTNode left, final ASTNode right)\n    {\n        this.left = left;\n        this.right = right;\n        this.identifier = counter++;\n    }\n\n    @Override\n    public String debugPrintTree()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(this.getName() + \"\\n\");\n        builder.append(this.getName() + \" left: \" + this.left.getName() + \"\\n\");\n        builder.append(this.getName() + \" right: \" + this.right.getName() + \"\\n\");\n        builder.append(this.left.debugPrintTree());\n        builder.append(this.right.debugPrintTree());\n        return builder.toString();\n    }\n\n    @Override\n    public ASTNode getCenterChild()\n    {\n        return null;\n    }\n\n    @Override\n    public int getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public ASTNode getLeftChild()\n    {\n        return this.left;\n    }\n\n    @Override\n    public ASTNode getRightChild()\n    {\n        return this.right;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/EqualsOperator.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author lcram\n */\npublic class EqualsOperator extends BinaryOperator\n{\n    private static final long serialVersionUID = 4853555543879394794L;\n    private final boolean bang;\n\n    public EqualsOperator(final ASTNode left, final ASTNode right, final boolean bang)\n    {\n        super(left, right);\n        this.bang = bang;\n    }\n\n    @Override\n    public String getName()\n    {\n        if (this.bang)\n        {\n            return \"BANGEQ_\" + getIdentifier();\n        }\n        else\n        {\n            return \"EQ_\" + getIdentifier();\n        }\n    }\n\n    @Override\n    public String getPrettyPrintText()\n    {\n        if (this.bang)\n        {\n            return \"!=\";\n        }\n        else\n        {\n            return \"=\";\n        }\n    }\n\n    @Override\n    public boolean match(final List<String> keys, final List<String> values)\n    {\n        if (keys.size() != values.size())\n        {\n            throw new CoreException(\"`keys' and `values' sizes did not match, {} vs {}\",\n                    keys.size(), values.size());\n        }\n\n        for (int i = 0; i < keys.size(); i++)\n        {\n            final boolean leftSide = getLeftChild().match(Collections.singletonList(keys.get(i)),\n                    null);\n            boolean rightSide = getRightChild().match(null,\n                    Collections.singletonList(values.get(i)));\n\n            /*\n             * For BANG_EQUALS, flip the boolean value of the right side to mimic the logic of `!='.\n             */\n            if (this.bang)\n            {\n                rightSide = !rightSide;\n            }\n\n            if (leftSide && rightSide)\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/LiteralOperand.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Token;\n\n/**\n * @author lcram\n */\npublic class LiteralOperand extends Operand\n{\n    public LiteralOperand(final Token token)\n    {\n        super(token);\n    }\n\n    @Override\n    public String getName()\n    {\n        return getToken().getLexeme() + \"_\" + getIdentifier();\n    }\n\n    @Override\n    public String getPrettyPrintText()\n    {\n        return getToken().getLexeme();\n    }\n\n    @Override\n    public boolean match(final List<String> keys, final List<String> values)\n    {\n        if (keys == null && values == null)\n        {\n            throw new CoreException(\"keys and values were null\");\n        }\n        return Objects.requireNonNullElse(keys, values).contains(getToken().getLexeme());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/Operand.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Token;\n\n/**\n * @author lcram\n */\npublic abstract class Operand extends ASTNode\n{\n    private static int counter = 0;\n    private static final long serialVersionUID = 4045177960157269200L;\n\n    private final Token token;\n    private final int identifier;\n\n    public static void clearIdentifierCounter()\n    {\n        counter = 0;\n    }\n\n    protected Operand(final Token token)\n    {\n        this.token = token;\n        this.identifier = counter++;\n    }\n\n    @Override\n    public String debugPrintTree()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(this.getName() + \"\\n\");\n        return builder.toString();\n    }\n\n    @Override\n    public ASTNode getCenterChild()\n    {\n        return null;\n    }\n\n    @Override\n    public int getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public ASTNode getLeftChild()\n    {\n        return null;\n    }\n\n    @Override\n    public ASTNode getRightChild()\n    {\n        return null;\n    }\n\n    public Token getToken()\n    {\n        return this.token;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/OrOperator.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.util.List;\n\n/**\n * @author lcram\n */\npublic class OrOperator extends BinaryOperator\n{\n    public OrOperator(final ASTNode left, final ASTNode right)\n    {\n        super(left, right);\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"OR_\" + getIdentifier();\n    }\n\n    @Override\n    public String getPrettyPrintText()\n    {\n        return \"|\";\n    }\n\n    @Override\n    public boolean match(final List<String> keys, final List<String> values)\n    {\n        return getLeftChild().match(keys, values) || getRightChild().match(keys, values);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/RegexOperand.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Token;\n\n/**\n * @author lcram\n */\npublic class RegexOperand extends Operand\n{\n    public RegexOperand(final Token token)\n    {\n        super(token);\n    }\n\n    @Override\n    public String getName()\n    {\n        return getToken().getLexeme() + \"_\" + getIdentifier();\n    }\n\n    @Override\n    public String getPrettyPrintText()\n    {\n        return \"/\" + getToken().getLexeme() + \"/\";\n    }\n\n    @Override\n    public boolean match(final List<String> keys, final List<String> values)\n    {\n        if (keys == null && values == null)\n        {\n            throw new CoreException(\"keys and values were null\");\n        }\n        return Objects.requireNonNullElse(keys, values).stream()\n                .anyMatch(string -> string.matches(getToken().getLexeme()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/TreePrinter.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * Inspired by MightyPork's algorithm here: https://stackoverflow.com/a/29704252\n *\n * @author lcram\n */\npublic final class TreePrinter\n{\n    public static long lengthOfLongestLineForTree(final ASTNode root)\n    {\n        return Arrays.stream(print(root).split(\"\\n\")).mapToLong(String::length).max()\n                .orElseThrow(() -> new CoreException(\"Failed to caluclate longest line length!\"));\n    }\n\n    /**\n     * Get a {@link TaggableMatcher} tree as a string.\n     *\n     * @param root\n     *            the root {@link ASTNode} of the tree\n     * @return the string tree\n     */\n    public static String print(final ASTNode root) // NOSONAR\n    {\n        final StringBuilder treeString = new StringBuilder();\n        final Tuple<List<List<String>>, Integer> tuple = discoverAllTreeNodes(root);\n        final List<List<String>> lines = tuple.getFirst();\n        final int widestNodeWidth = tuple.getSecond();\n\n        /*\n         * How are we calculating this? The last line returned by the breadth first search will be\n         * the longest, since it will contain a lot of nulls for every dead branch in the tree. So\n         * we use the last line as a baseline for line length. Then, we multiply by the widest width\n         * of any node plus 4 (4 gives some nice padding for readability). As the loop walks down\n         * the tree, this value will be continually halved, since each level there are approx. twice\n         * as many tree pieces.\n         */\n        final int widthPadding = 4;\n        int lengthOfTreePiece = lines.get(lines.size() - 1).size()\n                * (widestNodeWidth + widthPadding);\n        boolean firstIteration = true;\n        for (final List<String> line : lines)\n        {\n            final int nodeLeftRightPadding = (int) Math.floor(lengthOfTreePiece / 2f) - 1;\n\n            /*\n             * This section prints the Unicode box-drawing characters above each line of actual\n             * elements. It does not run on the first iteration of the loop, since there is no line\n             * containing Unicode box-drawing characters to print above the root node.\n             */\n            if (!firstIteration)\n            {\n                for (int lineElementIndex = 0; lineElementIndex < line.size(); lineElementIndex++)\n                {\n                    /*\n                     * Decide which Unicode box-drawing character to print below the nodes *ABOVE*\n                     * the current line. Only print for odd elements within the line. Since the tree\n                     * is binary, there is only one node \"between\" each of the nodes in the current\n                     * line.\n                     */\n                    char boxCharacter = ' ';\n                    if (isOdd(lineElementIndex))\n                    {\n                        if (line.get(lineElementIndex - 1) != null)\n                        {\n                            boxCharacter = (line.get(lineElementIndex) != null) ? '┴' : '┘';\n                        }\n                        else if (line.get(lineElementIndex) != null)\n                        {\n                            boxCharacter = '└';\n                        }\n                    }\n                    treeString.append(boxCharacter);\n\n                    /*\n                     * Print whitespace above null line elements, since nothing is there.\n                     */\n                    if (line.get(lineElementIndex) == null)\n                    {\n                        treeString.append(\" \".repeat(Math.max(0, lengthOfTreePiece - 1)));\n                    }\n                    /*\n                     * Here we decide which box-drawing character to print above the nodes *BELOW*\n                     * the current line.\n                     */\n                    else\n                    {\n                        treeString.append((isEven(lineElementIndex) ? \" \" : \"─\")\n                                .repeat(Math.max(0, nodeLeftRightPadding)));\n                        treeString.append(isEven(lineElementIndex) ? \"┌\" : \"┐\");\n                        treeString.append((isEven(lineElementIndex) ? \"─\" : \" \")\n                                .repeat(Math.max(0, nodeLeftRightPadding)));\n                    }\n                }\n                treeString.append(\"\\n\");\n            }\n\n            /*\n             * This section prints the actual line of elements.\n             */\n            for (final String element : line)\n            {\n                String element2 = element;\n                if (element2 == null)\n                {\n                    element2 = \"\";\n                }\n                final double padding = (lengthOfTreePiece / 2f) - (element2.length() / 2f);\n                final int paddingLeft = (int) Math.ceil(padding);\n                final int paddingRight = (int) Math.floor(padding);\n\n                treeString.append(\" \".repeat(Math.max(0, paddingLeft)));\n                treeString.append(element2);\n                treeString.append(\" \".repeat(Math.max(0, paddingRight)));\n            }\n            treeString.append(\"\\n\");\n\n            lengthOfTreePiece /= 2;\n            firstIteration = false;\n        }\n\n        return treeString.toString();\n    }\n\n    private static Tuple<List<List<String>>, Integer> discoverAllTreeNodes(final ASTNode root) // NOSONAR\n    {\n        final List<List<String>> lines = new ArrayList<>();\n\n        List<ASTNode> nodesThisLevel = new ArrayList<>();\n        List<ASTNode> nodesNextLevel = new ArrayList<>();\n\n        nodesThisLevel.add(root);\n        int numberOfNodesRemaining = 1;\n        int widestNodeWidth = 0;\n\n        while (numberOfNodesRemaining != 0)\n        {\n            final List<String> line = new ArrayList<>();\n            numberOfNodesRemaining = 0;\n            for (final ASTNode node : nodesThisLevel)\n            {\n                if (node == null)\n                {\n                    line.add(null);\n                    nodesNextLevel.add(null);\n                    nodesNextLevel.add(null);\n                }\n                else\n                {\n                    final String nodeText = node.getPrettyPrintText();\n                    line.add(nodeText);\n                    if (nodeText.length() > widestNodeWidth)\n                    {\n                        widestNodeWidth = nodeText.length();\n                    }\n\n                    if (node.getCenterChild() != null)\n                    {\n                        nodesNextLevel.add(node.getCenterChild());\n                    }\n                    else\n                    {\n                        nodesNextLevel.add(node.getLeftChild());\n                    }\n                    nodesNextLevel.add(node.getRightChild());\n\n                    if (node.getCenterChild() != null)\n                    {\n                        numberOfNodesRemaining++;\n                    }\n                    if (node.getLeftChild() != null)\n                    {\n                        numberOfNodesRemaining++;\n                    }\n                    if (node.getRightChild() != null)\n                    {\n                        numberOfNodesRemaining++;\n                    }\n                }\n            }\n\n            if (isOdd(widestNodeWidth))\n            {\n                widestNodeWidth++;\n            }\n\n            lines.add(line);\n\n            final List<ASTNode> tmp = nodesThisLevel;\n            nodesThisLevel = nodesNextLevel;\n            nodesNextLevel = tmp;\n            nodesNextLevel.clear();\n        }\n\n        return new Tuple<>(lines, widestNodeWidth);\n    }\n\n    private static boolean isEven(final int integer)\n    {\n        return integer % 2 == 0;\n    }\n\n    private static boolean isOdd(final int integer)\n    {\n        return integer % 2 != 0;\n    }\n\n    private TreePrinter()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/UnaryOperator.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\n/**\n * @author lcram\n */\npublic abstract class UnaryOperator extends ASTNode\n{\n    private static int counter = 0;\n    private static final long serialVersionUID = -6551792893893585221L;\n\n    private final ASTNode child;\n    private final int identifier;\n\n    public static void clearIdentifierCounter()\n    {\n        counter = 0;\n    }\n\n    protected UnaryOperator(final ASTNode child)\n    {\n        this.child = child;\n        this.identifier = counter++;\n    }\n\n    @Override\n    public String debugPrintTree()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(this.getName() + \"\\n\");\n        builder.append(this.getName() + \" child: \" + this.child.getName() + \"\\n\");\n        builder.append(this.child.debugPrintTree());\n        return builder.toString();\n    }\n\n    @Override\n    public ASTNode getCenterChild()\n    {\n        return this.child;\n    }\n\n    @Override\n    public int getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    @Override\n    public ASTNode getLeftChild()\n    {\n        return null;\n    }\n\n    @Override\n    public ASTNode getRightChild()\n    {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/XorOperator.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport java.util.List;\n\n/**\n * @author lcram\n */\npublic class XorOperator extends BinaryOperator\n{\n    public XorOperator(final ASTNode left, final ASTNode right)\n    {\n        super(left, right);\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"XOR_\" + getIdentifier();\n    }\n\n    @Override\n    public String getPrettyPrintText()\n    {\n        return \"^\";\n    }\n\n    @Override\n    public boolean match(final List<String> keys, final List<String> values)\n    {\n        final boolean left = getLeftChild().match(keys, values);\n        final boolean right = getRightChild().match(keys, values);\n        return (left || right) && !(left && right);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/AlternativeNameTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM alt_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/alt_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:alt_name\")\npublic interface AlternativeNameTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"alt_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/BridgeNameTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM bridge:name tag\n *\n * @author alexhsieh\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/search?q=bridge%3Aname\", osm = \"http://wiki.openstreetmap.org/wiki/Key:bridge:name\")\npublic interface BridgeNameTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"bridge:name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/BulkNameFinder.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.Taggable.TagSearchOption;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\nimport com.google.common.collect.ImmutableMap;\n\n/**\n * When we need results across multiple languages at the same time this hides a lot of the\n * boilerplate code that would be required if we used the NameFinder class directly.\n *\n * @author ihillberg\n * @author cstaylor\n */\npublic class BulkNameFinder implements Serializable\n{\n\n    /**\n     * BulkFind's findIn method will return an implementation of this interface.\n     *\n     * @author cstaylor\n     */\n    public interface BulkFindResults\n    {\n        /**\n         * Returns an optional map of name/value pairs for the requested name tags in the supplied\n         * language. If the language was not requested during the initial search, the Optional will\n         * be empty.\n         *\n         * @param language\n         *            we want the tags for this language only\n         * @return a map of the name/value pairs for the name tags\n         */\n        Optional<Map<Class<?>, String>> allValuesFor(Optional<IsoLanguage> language);\n\n        /**\n         * This method will create a map of basekeyname:language to value. This is good for bulk\n         * editing a set all at once. Note: The keys have been transformed into strings\n         *\n         * @return the flattened list of name/value pairs\n         */\n        Map<String, String> flatten();\n\n        /**\n         * Returns an iterable containing all of the languages encountered when creating these\n         * results\n         *\n         * @return an iterable list of IsoLanguages\n         */\n        Iterable<IsoLanguage> languagesFound();\n\n        /**\n         * Returns a single value for the language and name tag. If the request didn't find a tag by\n         * the type, Optional.empty() will be returned.\n         *\n         * @param language\n         *            we want the tag value for this language\n         * @param tagClass\n         *            the name tag we want\n         * @return an optional containing the value if it exists or empty if it doesn't\n         */\n        Optional<String> valueFor(Optional<IsoLanguage> language, Class<?> tagClass);\n    }\n\n    /**\n     * Internal implementation of the BulkFindResults interface.\n     *\n     * @author cstaylor\n     */\n    private static final class DefaultBulkFindResults implements BulkFindResults\n    {\n        private final Map<IsoLanguage, Map<Class<?>, String>> localizedResults = new HashMap<>();\n\n        private final Map<Class<?>, String> nonLocalizedResults = new LinkedHashMap<>();\n\n        private Map<String, String> flattenedMap = new HashMap<>();\n\n        @Override\n        public Optional<Map<Class<?>, String>> allValuesFor(final Optional<IsoLanguage> language)\n        {\n            return Optional\n                    .ofNullable(language.isPresent() ? this.localizedResults.get(language.get())\n                            : this.nonLocalizedResults);\n        }\n\n        @Override\n        public Map<String, String> flatten()\n        {\n            return this.flattenedMap;\n        }\n\n        @Override\n        public Iterable<IsoLanguage> languagesFound()\n        {\n            return this.localizedResults.keySet();\n        }\n\n        @Override\n        public Optional<String> valueFor(final Optional<IsoLanguage> language,\n                final Class<?> tagClass)\n        {\n            final Map<Class<?>, String> results = language.isPresent()\n                    ? this.localizedResults.get(language.get())\n                    : this.nonLocalizedResults;\n\n            return Optional.ofNullable(results == null ? null : results.get(tagClass));\n        }\n\n        private void completed()\n        {\n            final Map<String, String> temporaryMap = new HashMap<>();\n            for (final Entry<IsoLanguage, Map<Class<?>, String>> entry : this.localizedResults\n                    .entrySet())\n            {\n                final Optional<IsoLanguage> currentLanguage = Optional.of(entry.getKey());\n                for (final Entry<Class<?>, String> itemEntry : entry.getValue().entrySet())\n                {\n                    Validators.localizeKeyName(itemEntry.getKey(), currentLanguage)\n                            .ifPresent(localizedKeyName ->\n                            {\n                                temporaryMap.put(localizedKeyName, itemEntry.getValue());\n                            });\n                }\n            }\n\n            for (final Entry<Class<?>, String> entry : this.nonLocalizedResults.entrySet())\n            {\n                Validators.localizeKeyName(entry.getKey(), Optional.empty())\n                        .ifPresent(nonLocalizedKeyName ->\n                        {\n                            temporaryMap.put(nonLocalizedKeyName, entry.getValue());\n                        });\n            }\n            this.flattenedMap = new ImmutableMap.Builder<String, String>().putAll(temporaryMap)\n                    .build();\n        }\n\n        private void put(final IsoLanguage language, final Class<?> tag, final String value)\n        {\n            Map<Class<?>, String> mapping = this.localizedResults.get(language);\n            if (mapping == null)\n            {\n                mapping = new HashMap<>();\n                this.localizedResults.put(language, mapping);\n            }\n            mapping.put(tag, value);\n        }\n\n        private void put(final IsoLanguage language, final Map<Class<?>, String> results)\n        {\n            this.localizedResults.put(language, results);\n        }\n\n        private void put(final Map<Class<?>, String> results)\n        {\n            this.nonLocalizedResults.putAll(results);\n        }\n    }\n\n    private static final long serialVersionUID = -7709121230794406053L;\n\n    private final LinkedHashSet<IsoLanguage> requestedLanguages = new LinkedHashSet<>();\n\n    private final NameFinder finder = new NameFinder();\n\n    private boolean forceLocalized;\n\n    /**\n     * Convenience method that configures the underlying NameFinder for its standard set of tags to\n     * search\n     *\n     * @return fluent interface returns this\n     */\n    public static BulkNameFinder createStandardSet()\n    {\n        final BulkNameFinder returnValue = new BulkNameFinder();\n        returnValue.finder.withTags(NameFinder.STANDARD_TAGS);\n        return returnValue;\n    }\n\n    /**\n     * Convenience method that adds all languages core is configured to use\n     *\n     * @return fluent interface returns this\n     */\n    public BulkNameFinder allLanguages()\n    {\n        this.requestedLanguages.addAll(IsoLanguage.allLanguageCodes().stream()\n                .map(languageCode -> IsoLanguage.forLanguageCode(languageCode).get())\n                .collect(Collectors.toSet()));\n\n        return this;\n    }\n\n    /**\n     * Based on the current settings of BulkNameFinder, search taggable for the localized values of\n     * the keys in question and return a BulkFindResults with the resulting tags and values.\n     *\n     * @param taggable\n     *            what we're searching for\n     * @return the results of the search\n     */\n    public BulkFindResults findIn(final Taggable taggable)\n    {\n        /*\n         * We don't want the name finder to outsmart us and pull out the non-localized value if the\n         * localized one doesn't exist\n         */\n        if (this.forceLocalized)\n        {\n            this.finder.forceLocalized();\n        }\n        else\n        {\n            this.finder.localizedOnly();\n        }\n        final DefaultBulkFindResults results = new DefaultBulkFindResults();\n        for (final IsoLanguage language : this.requestedLanguages)\n        {\n            results.put(language, this.finder.inLanguage(language).all(taggable));\n        }\n        /*\n         * And the non-localized values\n         */\n        results.put(this.finder.inLanguage(null).all(taggable));\n        /*\n         * When we call completed, DefaultBulkFindResults will run through all of the data and build\n         * an immutable flattened map of the name/value pairs so calls to flatten will return the\n         * same map object. This is just an optimization so we don't recalculate the same results on\n         * multiple calls to flatten on the same BulkFindResults object\n         */\n        results.completed();\n        return results;\n    }\n\n    /**\n     * Based on the current tag class settings of BulkNameFinder, search taggable for the localized\n     * values of the keys in question using the language of the values present and return a\n     * BulkFindResults with the resulting tags and values.\n     *\n     * @param taggable\n     *            what we're searching for\n     * @param searchOptions\n     *            optional list of flags that alter the behavior of the underlying tag value search\n     * @return the results of the search\n     */\n    public BulkFindResults findInWithMyLanguages(final Taggable taggable,\n            final TagSearchOption... searchOptions)\n    {\n        final EnumSet<TagSearchOption> searchOptionSet = searchOptions.length > 0\n                ? EnumSet.copyOf(Arrays.asList(searchOptions))\n                : EnumSet.noneOf(TagSearchOption.class);\n        final TagSearchOption localizationOption = searchOptionSet.contains(\n                TagSearchOption.FORCE_ALL_LOCALIZED_ONLY) ? TagSearchOption.FORCE_ALL_LOCALIZED_ONLY\n                        : TagSearchOption.LOCALIZED_ONLY;\n\n        /*\n         * We don't want the name finder to outsmart us and pull out the non-localized value if the\n         * localized one doesn't exist\n         */\n        final DefaultBulkFindResults results = new DefaultBulkFindResults();\n\n        for (final Class<?> currentTag : this.finder.getTagNames())\n        {\n            if (Validators.hasLocalizedTagKey(currentTag)\n                    || localizationOption == TagSearchOption.FORCE_ALL_LOCALIZED_ONLY)\n            {\n                taggable.languagesFor(currentTag, searchOptions).ifPresent(languages ->\n                {\n                    for (final IsoLanguage language : languages)\n                    {\n                        taggable.getTag(currentTag, Optional.of(language), localizationOption)\n                                .ifPresent(value ->\n                                {\n                                    results.put(language, currentTag, value);\n                                });\n                    }\n                });\n            }\n        }\n\n        /*\n         * And the non-localized values\n         */\n        results.put(this.finder.inLanguage(null).all(taggable));\n        /*\n         * When we call completed, DefaultBulkFindResults will run through all of the data and build\n         * an immutable flattened map of the name/value pairs so calls to flatten will return the\n         * same map object. This is just an optimization so we don't recalculate the same results on\n         * multiple calls to flatten on the same BulkFindResults object\n         */\n        results.completed();\n        return results;\n    }\n\n    public BulkNameFinder forceLocalized()\n    {\n        this.forceLocalized = true;\n        return this;\n    }\n\n    /**\n     * Which language of localized tags are we looking for in the name finder?\n     *\n     * @param languages\n     *            we'll want the values of localizable tags in these languages\n     * @return fluent interface returns this\n     */\n    public BulkNameFinder withLanguage(final IsoLanguage... languages)\n    {\n        this.requestedLanguages.addAll(Arrays.asList(languages));\n        return this;\n    }\n\n    /**\n     * Which tags are we looking for in the underlying name finder?\n     *\n     * @param tags\n     *            the list of tags we'd like to search entities for\n     * @return fluent interface returns this\n     */\n    public BulkNameFinder withTags(final Class<?>... tags)\n    {\n        this.finder.withTags(tags);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/HistoricallyKnownAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM old_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/old_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name\")\npublic interface HistoricallyKnownAsTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"old_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/HistoricallyReferencedAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM old_ref tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/old_ref#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:ref\")\npublic interface HistoricallyReferencedAsTag\n{\n    @TagKey\n    String KEY = \"old_ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/InternationallyKnownAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM int_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/int_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name\")\npublic interface InternationallyKnownAsTag\n{\n    @TagKey\n    String KEY = \"int_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/InternationallyReferencedAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM int_ref tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/int_ref#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:int_ref\")\npublic interface InternationallyReferencedAsTag\n{\n    @TagKey\n    String KEY = \"int_ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/LocallyKnownAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM loc_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/loc_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name\")\npublic interface LocallyKnownAsTag\n{\n    @TagKey\n    String KEY = \"loc_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/LocallyReferencedAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM loc_ref tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/loc_ref#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:ref\")\npublic interface LocallyReferencedAsTag\n{\n    @TagKey\n    String KEY = \"loc_ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/Name1Tag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM name_1 tag, not official per OSM tagging wiki, but is very prevalent especially in the USA\n *\n * @author brian_l_davis\n */\n@Tag(value = Tag.Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/name_1#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name_1\")\npublic interface Name1Tag\n{\n    @TagKey\n    String KEY = \"name_1\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/NameFinder.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport static org.openstreetmap.atlas.geography.atlas.items.Relation.RELATION_ID_COMPARATOR;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.Taggable.TagSearchOption;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.EnhancedCollectors;\n\nimport com.google.common.collect.ImmutableCollection;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Iterables;\n\n/**\n * Responsible for finding names in AtlasEntities\n *\n * @author cstaylor\n * @author Sid\n */\npublic class NameFinder implements Serializable\n{\n    /**\n     * Standard set of name tags excluding the reference tags\n     */\n    public static final ImmutableList<Class<?>> STANDARD_TAGS_NON_REFERENCE = new ImmutableList.Builder<Class<?>>()\n            .add(NameTag.class, InternationallyKnownAsTag.class, NationallyKnownAsTag.class,\n                    RegionallyKnownAsTag.class, LocallyKnownAsTag.class,\n                    HistoricallyKnownAsTag.class, AlternativeNameTag.class, ShortNameTag.class,\n                    OfficialNameTag.class)\n            .build();\n\n    /**\n     * Standard set of reference tags\n     */\n    public static final ImmutableList<Class<?>> STANDARD_TAGS_REFERENCE = new ImmutableList.Builder<Class<?>>()\n            .add(ReferenceTag.class, InternationallyReferencedAsTag.class,\n                    NationallyReferencedAsTag.class, RegionallyReferencedAsTag.class,\n                    LocallyReferencedAsTag.class, HistoricallyReferencedAsTag.class)\n            .build();\n\n    /**\n     * Standard set of name tags in order of priority per mcuthbert's NameTag class\n     */\n    public static final ImmutableList<Class<?>> STANDARD_TAGS = new ImmutableList.Builder<Class<?>>()\n            .addAll(STANDARD_TAGS_NON_REFERENCE).addAll(STANDARD_TAGS_REFERENCE).build();\n\n    public static final ImmutableList<String> STANDARD_TAG_KEYS;\n    private static final long serialVersionUID = -7268140468931884651L;\n    static\n    {\n        STANDARD_TAG_KEYS = STANDARD_TAGS.stream().map(Validators::findTagNameIn)\n                .collect(EnhancedCollectors.toImmutableList());\n    }\n\n    private transient IsoLanguage language;\n\n    private final LinkedHashSet<Class<?>> priorityOrderOfTagNames;\n\n    private TagSearchOption searchOption = TagSearchOption.DEFAULT;\n\n    /**\n     * Returns a new NameFinder initialized with the following tags in priority order:\n     * <ol>\n     * <li>NameTag</li>\n     * <li>InternationallyKnownAsTag</li>\n     * <li>NationallyKnownAsTag</li>\n     * <li>RegionallyKnownAsTag</li>\n     * <li>LocallyKnownAsTag</li>\n     * <li>HistoricallyKnownAsTag</li>\n     * <li>AlternativeNameTag</li>\n     * <li>ShortNameTag</li>\n     * <li>OfficialNameTag</li>\n     * <li>ReferenceTag</li>\n     * <li>InternationallyReferencedAsTag</li>\n     * <li>NationallyReferencedAsTag</li>\n     * <li>RegionallyReferencedAsTag</li>\n     * <li>LocallyReferencedAsTag</li>\n     * <li>HistoricallyReferencedAsTag</li>\n     * </ol>\n     * <p>\n     * Note: This order was originally written by mcuthbert for his NameTag class.\n     *\n     * @param language\n     *            the language we should use for localizable tags when finding their values\n     * @return the initialized NameFinder instance\n     */\n    public static NameFinder createStandardSet(final IsoLanguage language)\n    {\n        return new NameFinder().withTags(STANDARD_TAGS).inLanguage(language);\n    }\n\n    private static List<Taggable> children(final AtlasEntity entity)\n    {\n        final List<Taggable> taggables = new ArrayList<>();\n        taggables.add(entity);\n        final List<Relation> relations = new ArrayList<>(entity.relations());\n        relations.sort(RELATION_ID_COMPARATOR);\n        Iterables.addAll(taggables, relations);\n        return taggables;\n    }\n\n    public NameFinder()\n    {\n        this.priorityOrderOfTagNames = new LinkedHashSet<>();\n        this.language = null;\n    }\n\n    public Map<Class<?>, String> all(final Taggable taggable)\n    {\n        final Map<Class<?>, String> returnValue = new HashMap<>();\n        for (final Class<?> tagClass : this.priorityOrderOfTagNames)\n        {\n            taggable.getTag(tagClass, Optional.ofNullable(this.language), this.searchOption)\n                    .ifPresent(tagValue ->\n                    {\n                        returnValue.put(tagClass, tagValue);\n                    });\n        }\n        return returnValue;\n    }\n\n    public Optional<String> best(final AtlasEntity entity)\n    {\n        return children(entity).stream().map(this::best).filter(Optional::isPresent)\n                .map(Optional::get).findFirst();\n    }\n\n    public Optional<String> best(final Taggable taggable)\n    {\n        return this.priorityOrderOfTagNames.stream()\n                .map(tagClass -> taggable.getTag(tagClass, Optional.ofNullable(this.language),\n                        this.searchOption))\n                .filter(Optional::isPresent).map(Optional::get).findFirst();\n    }\n\n    public NameFinder forceLocalized()\n    {\n        this.searchOption = TagSearchOption.FORCE_ALL_LOCALIZED_ONLY;\n        return this;\n    }\n\n    public ImmutableCollection<Class<?>> getTagNames()\n    {\n        return ImmutableList.<Class<?>> builder().addAll(this.priorityOrderOfTagNames).build();\n    }\n\n    public NameFinder inLanguage(final IsoLanguage language)\n    {\n        this.language = language;\n        return this;\n    }\n\n    public NameFinder localizedOnly()\n    {\n        this.searchOption = TagSearchOption.LOCALIZED_ONLY;\n        return this;\n    }\n\n    public NameFinder withTags(final Class<?>... tagClasses)\n    {\n        for (final Class<?> tagClass : tagClasses)\n        {\n            this.priorityOrderOfTagNames.add(tagClass);\n        }\n        return this;\n    }\n\n    public NameFinder withTags(final Iterable<Class<?>> tagClasses)\n    {\n        for (final Class<?> tagClass : tagClasses)\n        {\n            this.priorityOrderOfTagNames.add(tagClass);\n        }\n        return this;\n    }\n\n    private void readObject(final ObjectInputStream stream)\n            throws IOException, ClassNotFoundException\n    {\n        stream.defaultReadObject();\n        final String iso2 = (String) stream.readObject();\n        if (iso2 != null)\n        {\n            this.language = IsoLanguage.forLanguageCode(iso2).orElse(null);\n        }\n    }\n\n    private void writeObject(final ObjectOutputStream stream) throws IOException\n    {\n        stream.defaultWriteObject();\n        final String iso2 = this.language == null ? null : this.language.getLanguageCode();\n        stream.writeObject(iso2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/NameLeftTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM name:left tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/name%3Aleft#values\", osm = \"http://wiki.openstreetmap.org/wiki/Names#Left_and_right_names\")\npublic interface NameLeftTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"name:left\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/NameRightTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM name:right tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/name%3Aright#values\", osm = \"http://wiki.openstreetmap.org/wiki/Names#Left_and_right_names\")\npublic interface NameRightTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"name:right\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/NameTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\nimport org.openstreetmap.atlas.tags.annotations.extraction.NonEmptyStringExtractor;\n\n/**\n * OSM name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name\")\npublic interface NameTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"name\";\n\n    static Optional<String> getNameOf(final Taggable taggable)\n    {\n        final Optional<String> tagValue = taggable.getTag(KEY);\n        if (tagValue.isPresent())\n        {\n            return NonEmptyStringExtractor.validateAndExtract(tagValue.get());\n        }\n\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/NationallyKnownAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM nat_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/nat_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name\")\npublic interface NationallyKnownAsTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"nat_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/NationallyReferencedAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM nat_ref tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/nat_ref#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:ref#Examples_on_ways\")\npublic interface NationallyReferencedAsTag\n{\n    @TagKey\n    String KEY = \"nat_ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/NoNameTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * OSM No Name Tag.\n *\n * @author matthieun\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/noname#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:noname\")\npublic enum NoNameTag\n{\n    YES,\n    NO;\n\n    @TagKey\n    public static final String KEY = \"noname\";\n\n    public static boolean isNoName(final Taggable taggable)\n    {\n        final Optional<NoNameTag> noName = Validators.from(NoNameTag.class, taggable);\n        return noName.isPresent() && YES == noName.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/OfficialNameTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM official_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/official_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name\")\npublic interface OfficialNameTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"official_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/OldReferenceTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM old_ref tag\n *\n * @author kkonishi2\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/keys/old_ref\", osm = \"http://wiki.openstreetmap.org/wiki/Key:ref\")\npublic interface OldReferenceTag\n{\n    @TagKey\n    String KEY = \"old_ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/ReferenceTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM ref tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/ref#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:ref\")\npublic interface ReferenceTag\n{\n    @TagKey\n    String KEY = \"ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/RegionallyKnownAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM reg_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/reg_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name\")\npublic interface RegionallyKnownAsTag\n{\n    @TagKey\n    String KEY = \"reg_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/RegionallyReferencedAsTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM ref_ref tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/reg_ref#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:ref\")\npublic interface RegionallyReferencedAsTag\n{\n    @TagKey\n    String KEY = \"reg_ref\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/ShortNameTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM short_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/short_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:name\")\npublic interface ShortNameTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"short_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/SortingNameTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * OSM sorting_name tag\n *\n * @author cstaylor\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"http://taginfo.openstreetmap.org/keys/sorting_name#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:sorting_name\")\npublic interface SortingNameTag\n{\n    @TagKey\n    String KEY = \"sorting_name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/names/TunnelNameTag.java",
    "content": "package org.openstreetmap.atlas.tags.names;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagKey.KeyType;\n\n/**\n * OSM tunnel:name tag\n *\n * @author alexhsieh\n */\n@Tag(value = Validation.NON_EMPTY_STRING, taginfo = \"https://taginfo.openstreetmap.org/search?q=tunnel%3Aname\", osm = \"http://wiki.openstreetmap.org/wiki/Key:tunnel\")\npublic interface TunnelNameTag\n{\n    @TagKey(KeyType.LOCALIZED)\n    String KEY = \"tunnel:name\";\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/OneWayTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.TagValueDeprecated;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.BicycleOneWayTag;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.CyclewayLeftOneWayTag;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.CyclewayOneWayTag;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.CyclewayRightOneWayTag;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.OneWayBicycleTag;\nimport org.openstreetmap.atlas.tags.oneway.motor.OneWayMotorVehicleTag;\nimport org.openstreetmap.atlas.tags.oneway.motor.OneWayMotorcarTag;\nimport org.openstreetmap.atlas.tags.oneway.motor.OneWayVehicleTag;\n\n/**\n * OSM's oneway tag\n *\n * @author cstaylor\n * @author matthieun\n */\n@Tag(taginfo = \"http://taginfo.openstreetmap.org/keys/oneway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum OneWayTag\n{\n    YES,\n    NO,\n    REVERSIBLE,\n    @TagValueDeprecated\n    TRUE,\n    @TagValueDeprecated\n    FALSE,\n    @TagValueDeprecated\n    @TagValueAs(\"1\")\n    ONE,\n    @TagValueDeprecated\n    @TagValueAs(value = \"0\")\n    ZERO,\n    @TagValueAs(\"-1\")\n    MINUS_1,\n    @TagValueDeprecated\n    REVERSE;\n\n    @TagKey\n    public static final String KEY = \"oneway\";\n\n    protected static final Set<OneWayTag> ONE_WAYS_FORWARD = EnumSet.of(YES, TRUE, ONE);\n    // Note here that REVERSIBLE is not reversed.\n    protected static final Set<OneWayTag> ONE_WAYS_REVERSED = EnumSet.of(MINUS_1, REVERSE);\n    protected static final Set<OneWayTag> TWO_WAYS = EnumSet.of(NO, FALSE, ZERO);\n\n    /**\n     * @param taggable\n     *            The taggable\n     * @return True if the feature is oneway forward, OR bicycle oneway forward.\n     */\n    public static boolean isBicycleOneWayForward(final Taggable taggable)\n    {\n        return isOneWayForward(taggable) || isBicycleTagSpecificallyOneWayForward(taggable);\n    }\n\n    /**\n     * @param taggable\n     *            The taggable\n     * @return True if the feature is oneway reverse, OR bicycle oneway reverse.\n     */\n    public static boolean isBicycleOneWayReversed(final Taggable taggable)\n    {\n        return isOneWayReversed(taggable) || isBicycleTagSpecificallyOneWayReversed(taggable);\n    }\n\n    /**\n     * @param taggable\n     *            The taggable\n     * @return True if the feature is two way, AND bicycle two way.\n     */\n    public static boolean isBicycleTwoWay(final Taggable taggable)\n    {\n        return OneWayTag.isTwoWay(taggable) && isBicycleTagSpecificallyTwoWay(taggable);\n    }\n\n    /**\n     * This is a subset of two way roads, in which the two way status has been tagged with oneway=no\n     * and not just assumed because of the absence of a oneway tag.\n     *\n     * @param taggable\n     *            The object to test\n     * @return True if the object is explicitly two way\n     */\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isMotorVehicleOneWayForward(final Taggable taggable)\n    {\n        return isOneWayForward(taggable) || OneWayMotorcarTag.isOneWayForward(taggable)\n                || OneWayMotorVehicleTag.isOneWayForward(taggable)\n                || OneWayVehicleTag.isOneWayForward(taggable);\n    }\n\n    public static boolean isMotorVehicleOneWayReversed(final Taggable taggable)\n    {\n        return isOneWayReversed(taggable) || OneWayMotorcarTag.isOneWayReversed(taggable)\n                || OneWayMotorVehicleTag.isOneWayReversed(taggable)\n                || OneWayVehicleTag.isOneWayReversed(taggable);\n    }\n\n    public static boolean isMotorVehicleTwoWay(final Taggable taggable)\n    {\n        // All the motor related tags need to be two way (including not set)\n        return OneWayTag.isTwoWay(taggable) && OneWayMotorcarTag.isTwoWay(taggable)\n                && OneWayMotorVehicleTag.isTwoWay(taggable) && OneWayVehicleTag.isTwoWay(taggable);\n    }\n\n    public static boolean isOneWayForward(final OneWayTag tag)\n    {\n        return ONE_WAYS_FORWARD.contains(tag);\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<OneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final OneWayTag tag)\n    {\n        return ONE_WAYS_REVERSED.contains(tag);\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<OneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversible(final OneWayTag tag)\n    {\n        return REVERSIBLE == tag;\n    }\n\n    public static boolean isOneWayReversible(final Taggable taggable)\n    {\n        final Optional<OneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && REVERSIBLE == oneWay.get();\n    }\n\n    public static boolean isTwoWay(final OneWayTag tag)\n    {\n        return TWO_WAYS.contains(tag);\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayTag> oneWay = tag(taggable);\n        return !oneWay.isPresent() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<OneWayTag> tag(final Taggable taggable)\n    {\n        return Validators.from(OneWayTag.class, taggable);\n    }\n\n    private static boolean isBicycleTagSpecificallyOneWayForward(final Taggable taggable)\n    {\n        return BicycleOneWayTag.isOneWayForward(taggable)\n                || CyclewayOneWayTag.isOneWayForward(taggable)\n                || OneWayBicycleTag.isOneWayForward(taggable)\n                || CyclewayRightOneWayTag.isOneWayForward(taggable)\n                || CyclewayLeftOneWayTag.isOneWayForward(taggable);\n    }\n\n    private static boolean isBicycleTagSpecificallyOneWayReversed(final Taggable taggable)\n    {\n        return BicycleOneWayTag.isOneWayReversed(taggable)\n                || CyclewayOneWayTag.isOneWayReversed(taggable)\n                || OneWayBicycleTag.isOneWayReversed(taggable)\n                || CyclewayRightOneWayTag.isOneWayReversed(taggable)\n                || CyclewayLeftOneWayTag.isOneWayReversed(taggable);\n    }\n\n    private static boolean isBicycleTagSpecificallyTwoWay(final Taggable taggable)\n    {\n        return BicycleOneWayTag.isTwoWay(taggable) && CyclewayOneWayTag.isTwoWay(taggable)\n                && OneWayBicycleTag.isTwoWay(taggable) && CyclewayRightOneWayTag.isTwoWay(taggable)\n                && CyclewayLeftOneWayTag.isTwoWay(taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/bicycle/BicycleOneWayTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway.bicycle;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/bicycle%3Aoneway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum BicycleOneWayTag\n{\n    YES,\n    NO,\n    @TagValueAs(\"-1\")\n    MINUS_1;\n\n    @TagKey\n    public static final String KEY = \"bicycle:oneway\";\n\n    protected static final Set<BicycleOneWayTag> ONE_WAYS_FORWARD = EnumSet.of(YES);\n    protected static final Set<BicycleOneWayTag> ONE_WAYS_REVERSED = EnumSet.of(MINUS_1);\n    protected static final Set<BicycleOneWayTag> TWO_WAYS = EnumSet.of(NO);\n\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<BicycleOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<BicycleOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<BicycleOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<BicycleOneWayTag> oneWay = tag(taggable);\n        return oneWay.isEmpty() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<BicycleOneWayTag> tag(final Taggable taggable)\n    {\n        return Validators.from(BicycleOneWayTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/bicycle/CyclewayLeftOneWayTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway.bicycle;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/cycleway%3Aleft%3Aoneway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum CyclewayLeftOneWayTag\n{\n    YES,\n    NO,\n    @TagValueAs(\"1\")\n    ONE,\n    @TagValueAs(\"-1\")\n    MINUS_1,\n    LANE,\n    OPPOSITE,\n    FALSE;\n\n    @TagKey\n    public static final String KEY = \"cycleway:left:oneway\";\n\n    protected static final Set<CyclewayLeftOneWayTag> ONE_WAYS_FORWARD = EnumSet.of(YES, ONE, LANE);\n    protected static final Set<CyclewayLeftOneWayTag> ONE_WAYS_REVERSED = EnumSet.of(MINUS_1,\n            OPPOSITE);\n    protected static final Set<CyclewayLeftOneWayTag> TWO_WAYS = EnumSet.of(NO, FALSE);\n\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<CyclewayLeftOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<CyclewayLeftOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<CyclewayLeftOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<CyclewayLeftOneWayTag> oneWay = tag(taggable);\n        return oneWay.isEmpty() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<CyclewayLeftOneWayTag> tag(final Taggable taggable)\n    {\n        return Validators.from(CyclewayLeftOneWayTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/bicycle/CyclewayOneWayTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway.bicycle;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/cycleway%3Aoneway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum CyclewayOneWayTag\n{\n    YES,\n    NO,\n    @TagValueAs(\"-1\")\n    MINUS_1;\n\n    @TagKey\n    public static final String KEY = \"cycleway:oneway\";\n\n    protected static final Set<CyclewayOneWayTag> ONE_WAYS_FORWARD = EnumSet.of(YES);\n    protected static final Set<CyclewayOneWayTag> ONE_WAYS_REVERSED = EnumSet.of(MINUS_1);\n    protected static final Set<CyclewayOneWayTag> TWO_WAYS = EnumSet.of(NO);\n\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<CyclewayOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<CyclewayOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<CyclewayOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<CyclewayOneWayTag> oneWay = tag(taggable);\n        return oneWay.isEmpty() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<CyclewayOneWayTag> tag(final Taggable taggable)\n    {\n        return Validators.from(CyclewayOneWayTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/bicycle/CyclewayRightOneWayTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway.bicycle;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/cycleway%3Aright%3Aoneway#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum CyclewayRightOneWayTag\n{\n    YES,\n    NO,\n    @TagValueAs(\"1\")\n    ONE,\n    @TagValueAs(\"-1\")\n    MINUS_1,\n    LANE,\n    DESIGNATED;\n\n    @TagKey\n    public static final String KEY = \"cycleway:right:oneway\";\n\n    protected static final Set<CyclewayRightOneWayTag> ONE_WAYS_FORWARD = EnumSet.of(YES, ONE, LANE,\n            DESIGNATED);\n    protected static final Set<CyclewayRightOneWayTag> ONE_WAYS_REVERSED = EnumSet.of(MINUS_1);\n    protected static final Set<CyclewayRightOneWayTag> TWO_WAYS = EnumSet.of(NO);\n\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<CyclewayRightOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<CyclewayRightOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<CyclewayRightOneWayTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<CyclewayRightOneWayTag> oneWay = tag(taggable);\n        return oneWay.isEmpty() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<CyclewayRightOneWayTag> tag(final Taggable taggable)\n    {\n        return Validators.from(CyclewayRightOneWayTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/bicycle/OneWayBicycleTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway.bicycle;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/oneway%3Abicycle#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum OneWayBicycleTag\n{\n    YES,\n    NO,\n    @TagValueAs(\"1\")\n    ONE,\n    @TagValueAs(\"-1\")\n    MINUS_1,\n    OPPOSITE;\n\n    @TagKey\n    public static final String KEY = \"oneway:bicycle\";\n\n    protected static final Set<OneWayBicycleTag> ONE_WAYS_FORWARD = EnumSet.of(YES, ONE);\n    protected static final Set<OneWayBicycleTag> ONE_WAYS_REVERSED = EnumSet.of(OPPOSITE, MINUS_1);\n    protected static final Set<OneWayBicycleTag> TWO_WAYS = EnumSet.of(NO);\n\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayBicycleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<OneWayBicycleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<OneWayBicycleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayBicycleTag> oneWay = tag(taggable);\n        return oneWay.isEmpty() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<OneWayBicycleTag> tag(final Taggable taggable)\n    {\n        return Validators.from(OneWayBicycleTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/motor/OneWayMotorVehicleTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway.motor;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/oneway%3Amotor_vehicle#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum OneWayMotorVehicleTag\n{\n    YES,\n    NO,\n    @TagValueAs(\"-1\")\n    MINUS_1;\n\n    @TagKey\n    public static final String KEY = \"oneway:motor_vehicle\";\n\n    protected static final Set<OneWayMotorVehicleTag> ONE_WAYS_FORWARD = EnumSet.of(YES);\n    protected static final Set<OneWayMotorVehicleTag> ONE_WAYS_REVERSED = EnumSet.of(MINUS_1);\n    protected static final Set<OneWayMotorVehicleTag> TWO_WAYS = EnumSet.of(NO);\n\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayMotorVehicleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<OneWayMotorVehicleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<OneWayMotorVehicleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayMotorVehicleTag> oneWay = tag(taggable);\n        return oneWay.isEmpty() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<OneWayMotorVehicleTag> tag(final Taggable taggable)\n    {\n        return Validators.from(OneWayMotorVehicleTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/motor/OneWayMotorcarTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway.motor;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/oneway%3Amotorcar#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum OneWayMotorcarTag\n{\n    YES,\n    NO,\n    @TagValueAs(\"-1\")\n    MINUS_1;\n\n    @TagKey\n    public static final String KEY = \"oneway:motorcar\";\n\n    protected static final Set<OneWayMotorcarTag> ONE_WAYS_FORWARD = EnumSet.of(YES);\n    protected static final Set<OneWayMotorcarTag> ONE_WAYS_REVERSED = EnumSet.of(MINUS_1);\n    protected static final Set<OneWayMotorcarTag> TWO_WAYS = EnumSet.of(NO);\n\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayMotorcarTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<OneWayMotorcarTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<OneWayMotorcarTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayMotorcarTag> oneWay = tag(taggable);\n        return oneWay.isEmpty() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<OneWayMotorcarTag> tag(final Taggable taggable)\n    {\n        return Validators.from(OneWayMotorcarTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/tags/oneway/motor/OneWayVehicleTag.java",
    "content": "package org.openstreetmap.atlas.tags.oneway.motor;\n\nimport java.util.EnumSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.tags.annotations.TagValueAs;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\n@Tag(taginfo = \"https://taginfo.openstreetmap.org/keys/oneway%3Avehicle#values\", osm = \"http://wiki.openstreetmap.org/wiki/Key:oneway\")\npublic enum OneWayVehicleTag\n{\n    YES,\n    NO,\n    @TagValueAs(\"-1\")\n    MINUS_1;\n\n    @TagKey\n    public static final String KEY = \"oneway:vehicle\";\n\n    protected static final Set<OneWayVehicleTag> ONE_WAYS_FORWARD = EnumSet.of(YES);\n    protected static final Set<OneWayVehicleTag> ONE_WAYS_REVERSED = EnumSet.of(MINUS_1);\n    protected static final Set<OneWayVehicleTag> TWO_WAYS = EnumSet.of(NO);\n\n    public static boolean isExplicitlyTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayVehicleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayForward(final Taggable taggable)\n    {\n        final Optional<OneWayVehicleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_FORWARD.contains(oneWay.get());\n    }\n\n    public static boolean isOneWayReversed(final Taggable taggable)\n    {\n        final Optional<OneWayVehicleTag> oneWay = tag(taggable);\n        return oneWay.isPresent() && ONE_WAYS_REVERSED.contains(oneWay.get());\n    }\n\n    public static boolean isTwoWay(final Taggable taggable)\n    {\n        final Optional<OneWayVehicleTag> oneWay = tag(taggable);\n        return oneWay.isEmpty() || TWO_WAYS.contains(oneWay.get());\n    }\n\n    public static Optional<OneWayVehicleTag> tag(final Taggable taggable)\n    {\n        return Validators.from(OneWayVehicleTag.class, taggable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/README.md",
    "content": "# Utilities package\n\nThis package contains utilities that help with collections, [scalars](scalars), threading, running java commands, converting, counting, and many others. \n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/AbstractArchiverOrExtractor.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\n/**\n * Abstract superclass for the Archiver and Extractor classes; handles all of the event listener\n * subscriptions and provides helper methods for sending events\n *\n * @author cstaylor\n * @param <T>\n *            either the Archiver or Extractor class\n */\nabstract class AbstractArchiverOrExtractor<T>\n{\n    private final Collection<ArchiverEventListener<T>> listeners;\n\n    private final Class<T> klass;\n\n    private ArchiveVetoDelegate<T> delegate;\n\n    protected AbstractArchiverOrExtractor(final Class<T> klass)\n    {\n        this.klass = klass;\n        this.listeners = new CopyOnWriteArraySet<>();\n    }\n\n    public AbstractArchiverOrExtractor<T> addArchiverEventListener(\n            final ArchiverEventListener<T> listener)\n    {\n        this.listeners.add(listener);\n        return this;\n    }\n\n    public AbstractArchiverOrExtractor<T> removeArchiverEventListener(\n            final ArchiverEventListener<T> listener)\n    {\n        this.listeners.remove(listener);\n        return this;\n    }\n\n    public AbstractArchiverOrExtractor<T> setVetoDelegate(final ArchiveVetoDelegate<T> delegate)\n    {\n        this.delegate = delegate;\n        return this;\n    }\n\n    protected void fireArchiveCompleted()\n    {\n        for (final ArchiverEventListener<T> listener : this.listeners)\n        {\n            listener.archiveCompleted(this.klass.cast(this));\n        }\n    }\n\n    protected void fireArchiveFailed()\n    {\n        for (final ArchiverEventListener<T> listener : this.listeners)\n        {\n            listener.archiveFailed(this.klass.cast(this));\n        }\n    }\n\n    protected void fireArchiveStarted()\n    {\n        for (final ArchiverEventListener<T> listener : this.listeners)\n        {\n            listener.archiveStarted(this.klass.cast(this));\n        }\n    }\n\n    protected void fireItemCompleted(final File file)\n    {\n        for (final ArchiverEventListener<T> listener : this.listeners)\n        {\n            listener.itemCompleted(this.klass.cast(this), file);\n        }\n    }\n\n    protected void fireItemFailed(final File file, final IOException oops)\n    {\n        for (final ArchiverEventListener<T> listener : this.listeners)\n        {\n            listener.itemFailed(this.klass.cast(this), file, oops);\n        }\n    }\n\n    protected void fireItemInProgress(final File file, final long count, final long length)\n    {\n        for (final ArchiverEventListener<T> listener : this.listeners)\n        {\n            listener.itemInProgress(this.klass.cast(this), file, count, length);\n        }\n    }\n\n    protected void fireItemSkipped(final File file)\n    {\n        for (final ArchiverEventListener<T> listener : this.listeners)\n        {\n            listener.itemSkipped(this.klass.cast(this), file);\n        }\n    }\n\n    protected void fireItemStarted(final File file)\n    {\n        for (final ArchiverEventListener<T> listener : this.listeners)\n        {\n            listener.itemStarted(this.klass.cast(this), file);\n        }\n    }\n\n    protected boolean shouldSkip(final File file)\n    {\n        return this.delegate != null && this.delegate.shouldSkip(this.klass.cast(this), file);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/ArchiveStorageProfileDelegate.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.File;\n\n/**\n * Callback that informs an archiver for a given file if it should be compressed or not. Some file\n * formats are already compressed, so compressing them again is pointless and may use extra storage.\n *\n * @author cstaylor\n */\npublic interface ArchiveStorageProfileDelegate\n{\n    /**\n     * Determines if a given file should be compressed by the archiver\n     *\n     * @param item\n     *            the file in question\n     * @return true if we should compress, false otherwise\n     */\n    boolean shouldCompress(File item);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/ArchiveVetoDelegate.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.File;\n\n/**\n * Callback that informs an object of type T for a given file if it should be processed or not. Some\n * files should be skipped (for example, .DS_Store on the OS X), so this lets us configure the\n * Archiver to skip certain files.\n *\n * @param <T>\n *            the owner of item (in this case, an Archiver or Extractor)\n * @author cstaylor\n */\npublic interface ArchiveVetoDelegate<T>\n{\n    /**\n     * For a given file item, should we skip it?\n     *\n     * @param source\n     *            the potential owner of the file\n     * @param item\n     *            the file to possibly be skipped\n     * @return true if this item should be skipped, false otherwise\n     */\n    boolean shouldSkip(T source, File item);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/Archiver.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport org.apache.commons.compress.archivers.ArchiveException;\nimport org.apache.commons.compress.archivers.ArchiveOutputStream;\nimport org.apache.commons.compress.archivers.zip.ZipArchiveEntry;\nimport org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;\nimport org.apache.commons.io.DirectoryWalker;\nimport org.apache.commons.io.IOUtils;\nimport org.openstreetmap.atlas.streaming.NotifyingIOUtils;\nimport org.openstreetmap.atlas.streaming.NotifyingIOUtils.IOProgressListener;\n\n/**\n * Class for archiving the contents of a directory into a ZIP archive\n *\n * @author cstaylor\n */\npublic final class Archiver extends AbstractArchiverOrExtractor<Archiver>\n{\n    /**\n     * Sets all files to the default mode of compression: no inspection of the type of file is done\n     *\n     * @author cstaylor\n     */\n    public class DefaultArchiveStorageProfileDelegate implements ArchiveStorageProfileDelegate\n    {\n        private final boolean defaultMode;\n\n        public DefaultArchiveStorageProfileDelegate(final boolean mode)\n        {\n            this.defaultMode = mode;\n        }\n\n        @Override\n        public boolean shouldCompress(final File item)\n        {\n            return this.defaultMode;\n        }\n    }\n\n    /**\n     * Responsible for tracking the process of a file being archived\n     *\n     * @author cstaylor\n     */\n    class Progress implements IOProgressListener\n    {\n        private final File file;\n\n        private final long length;\n\n        Progress(final File file)\n        {\n            this.file = file;\n            this.length = file.length();\n        }\n\n        @Override\n        public void completed()\n        {\n            fireItemCompleted(this.file);\n        }\n\n        @Override\n        public void failed(final IOException oops)\n        {\n            Archiver.this.errorCount++;\n            fireItemFailed(this.file, oops);\n        }\n\n        @Override\n        public void started()\n        {\n            fireItemStarted(this.file);\n        }\n\n        @Override\n        public void statusUpdate(final long count)\n        {\n            fireItemInProgress(this.file, count, this.length);\n        }\n    }\n\n    /**\n     * The guts of the archival algorithm: - On start, initialize the base path to the file's\n     * absolute path and save the length of the name - On handleFile, copy the file contents into\n     * the zip archive - on end, close the zip archive stream\n     *\n     * @author cstaylor\n     */\n    private class MyDirectoryWalker extends DirectoryWalker<File>\n    {\n        private String path;\n\n        private int length;\n\n        public void walk(final File file) throws IOException, ArchiveException\n        {\n            walk(file, new ArrayList<File>());\n        }\n\n        @Override\n        protected void handleEnd(final Collection<File> items) throws IOException\n        {\n            Archiver.this.archiveOutputStream.finish();\n            Archiver.this.archiveOutputStream.close();\n            super.handleEnd(items);\n        }\n\n        @Override\n        protected void handleFile(final File file, final int depth, final Collection<File> items)\n                throws IOException\n        {\n            if (shouldSkip(file))\n            {\n                fireItemSkipped(file);\n            }\n            else\n            {\n                try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(file)))\n                {\n                    final String path = file.getAbsolutePath().substring(this.length);\n                    final ZipArchiveEntry entry = new ZipArchiveEntry(path);\n                    if (Archiver.this.storageDelegate != null)\n                    {\n                        final int level = Archiver.this.storageDelegate.shouldCompress(file)\n                                ? ZipArchiveEntry.DEFLATED\n                                : ZipArchiveEntry.STORED;\n                        entry.setMethod(level);\n                    }\n                    Archiver.this.archiveOutputStream.putArchiveEntry(entry);\n                    NotifyingIOUtils.copy(input, Archiver.this.archiveOutputStream,\n                            new Progress(file));\n                    IOUtils.closeQuietly(input);\n                    Archiver.this.archiveOutputStream.closeArchiveEntry();\n                }\n                catch (final FileNotFoundException oops)\n                {\n                    // This can happen on Linux if the filename is corrupt\n                    // and we try to open the file\n                    fireItemFailed(file, oops);\n                }\n            }\n            super.handleFile(file, depth, items);\n        }\n\n        @Override\n        protected void handleStart(final File file, final Collection<File> items) throws IOException\n        {\n            this.path = file.getAbsolutePath();\n            this.length = this.path.length() + 1;\n            super.handleStart(file, items);\n        }\n    }\n\n    /**\n     * Where we are compressing to\n     */\n    private final ArchiveOutputStream archiveOutputStream;\n\n    /**\n     * The number of errors that occurred during archival\n     */\n    private int errorCount;\n\n    private ArchiveStorageProfileDelegate storageDelegate;\n\n    /**\n     * Static factory method for creating a new Archiver with the ZIP archive format; currently the\n     * only format supported\n     *\n     * @param outputFile\n     *            the destination zip file\n     * @return the Archiver instance; useful for chaining method calls\n     * @throws ArchiveException\n     *             if something compression related failed\n     * @throws IOException\n     *             if something I/O related failed\n     */\n    public static Archiver createZipArchiver(final File outputFile)\n            throws ArchiveException, IOException\n    {\n        return Archiver.createZipArchiver(outputFile, false);\n    }\n\n    /**\n     * Static factory method for creating a new Archiver with the ZIP archive format; currently the\n     * only format supported\n     *\n     * @param outputFile\n     *            the destination zip file\n     * @param compress\n     *            true if we should compress all entries by default, false will just stored them\n     *            uncompressed\n     * @return the Archiver instance; useful for chaining method calls\n     * @throws ArchiveException\n     *             if something compression related failed\n     * @throws IOException\n     *             if something I/O related failed\n     */\n    public static Archiver createZipArchiver(final File outputFile, final boolean compress)\n            throws ArchiveException, IOException\n    {\n        final ZipArchiveOutputStream zout = new ZipArchiveOutputStream(outputFile);\n        zout.setEncoding(\"UTF-8\");\n        zout.setFallbackToUTF8(true);\n        zout.setUseLanguageEncodingFlag(true);\n        if (compress)\n        {\n            zout.setMethod(ZipArchiveOutputStream.DEFLATED);\n        }\n        else\n        {\n            zout.setMethod(ZipArchiveOutputStream.STORED);\n        }\n        zout.setCreateUnicodeExtraFields(\n                ZipArchiveOutputStream.UnicodeExtraFieldPolicy.NOT_ENCODEABLE);\n        return new Archiver(zout);\n    }\n\n    public static Archiver createZipArchiver(final Path outputPath)\n            throws ArchiveException, IOException\n    {\n        if (outputPath == null)\n        {\n            throw new IllegalArgumentException(\"outputPath can't be null\");\n        }\n        return createZipArchiver(outputPath.toFile());\n    }\n\n    public static Archiver createZipArchiver(final Path outputPath, final boolean compress)\n            throws ArchiveException, IOException\n    {\n        if (outputPath == null)\n        {\n            throw new IllegalArgumentException(\"outputPath can't be null\");\n        }\n        return createZipArchiver(outputPath.toFile(), compress);\n    }\n\n    /**\n     * Creates a new Archiver specifying where the directory contents will be archived to.\n     *\n     * @param archiveOutputStream\n     *            the destination archive output stream (ZIP)\n     */\n    private Archiver(final ArchiveOutputStream archiveOutputStream) throws IOException\n    {\n        super(Archiver.class);\n        this.archiveOutputStream = archiveOutputStream;\n        setVetoDelegate(new DefaultZipVetoDelegate<Archiver>());\n    }\n\n    @Override\n    public Archiver addArchiverEventListener(final ArchiverEventListener<Archiver> listener)\n    {\n        super.addArchiverEventListener(listener);\n        return this;\n    }\n\n    /**\n     * Archives the contents of inputFile to the previously set zip archive stream. Algorithm: -\n     * Sanity check the current state of the Extractor and the inputFile argument - Fire the archive\n     * started event - Walk the contents of the inputFile; this will delegate all compression calls\n     * - If at least one error occurred, fire the archive failed event - Otherwise, fire the archive\n     * completed event\n     *\n     * @param inputFile\n     *            the directory we want to extract; must not be null\n     * @return the Archiver instance; useful for chaining method calls\n     * @throws ArchiveException\n     *             if something compression related failed\n     * @throws IOException\n     *             if something I/O related failed\n     */\n    public Archiver compress(final File inputFile) throws ArchiveException, IOException\n    {\n        if (inputFile == null)\n        {\n            throw new IllegalArgumentException(\"inputFile can't be null\");\n        }\n        if (this.archiveOutputStream == null)\n        {\n            throw new IllegalStateException(\"os can't be null\");\n        }\n\n        fireArchiveStarted();\n        new MyDirectoryWalker().walk(inputFile);\n        if (this.errorCount > 0)\n        {\n            fireArchiveFailed();\n        }\n        else\n        {\n            fireArchiveCompleted();\n        }\n        return this;\n    }\n\n    public Archiver compress(final Path inputFile) throws ArchiveException, IOException\n    {\n        if (inputFile == null)\n        {\n            throw new IllegalArgumentException(\"inputFile can't be null\");\n        }\n        return compress(inputFile.toFile());\n    }\n\n    @Override\n    public Archiver removeArchiverEventListener(final ArchiverEventListener<Archiver> listener)\n    {\n        super.removeArchiverEventListener(listener);\n        return this;\n    }\n\n    public Archiver setStorageDelegate(final ArchiveStorageProfileDelegate storageDelegate)\n    {\n        this.storageDelegate = storageDelegate;\n        return this;\n    }\n\n    @Override\n    public Archiver setVetoDelegate(final ArchiveVetoDelegate<Archiver> delegate)\n    {\n        super.setVetoDelegate(delegate);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/ArchiverEventListener.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Callback methods for status updates during an extraction (Extractor) or archival (Archiver)\n * operation.\n *\n * @author cstaylor\n * @param <T>\n *            either the Archiver or Extractor class\n */\npublic interface ArchiverEventListener<T>\n{\n    /**\n     * Called once after the operation completes successfully\n     *\n     * @param source\n     *            the source Archiver or Extractor object\n     */\n    void archiveCompleted(T source);\n\n    /**\n     * Called once after the operation completes with errors\n     *\n     * @param source\n     *            the source Archiver or Extractor object\n     */\n    void archiveFailed(T source);\n\n    /**\n     * Called once before the operation begins\n     *\n     * @param source\n     *            the source Archiver or Extractor object\n     */\n    void archiveStarted(T source);\n\n    /**\n     * Called once for every item that has been extracted or archived successfully\n     *\n     * @param source\n     *            the source Archiver or Extractor object\n     * @param item\n     *            the item that will be extracted or archived\n     */\n    void itemCompleted(T source, File item);\n\n    /**\n     * Called once for every item that has been extracted or archived with errors\n     *\n     * @param source\n     *            the source Archiver or Extractor object\n     * @param item\n     *            the item that will be extracted or archived\n     * @param oops\n     *            the IOException that caused the extraction or archival to fail\n     */\n    void itemFailed(T source, File item, IOException oops);\n\n    /**\n     * Called after each partial copy of an item with the number of bytes read and the total number\n     * of bytes that will be read\n     *\n     * @param source\n     *            the source Archiver or Extractor object\n     * @param item\n     *            the item that is being extracted or archived\n     * @param bytesRead\n     *            the number of bytes that have already been read\n     * @param bytesTotal\n     *            the total number of bytes in item\n     */\n    void itemInProgress(T source, File item, long bytesRead, long bytesTotal);\n\n    /**\n     * Called once for every item that was not processed because the implementation decided to skip\n     * it\n     *\n     * @param source\n     *            the source Archiver or Extractor object\n     * @param item\n     *            the item that was skipped\n     */\n    void itemSkipped(T source, File item);\n\n    /**\n     * Called once for every item that will be extracted or archived\n     *\n     * @param source\n     *            the source Archiver or Extractor object\n     * @param item\n     *            the item that will be extracted or archived\n     */\n    void itemStarted(T source, File item);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/DefaultZipVetoDelegate.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.File;\n\n/**\n * Sample veto delegate that skips files we don't want on OS X\n *\n * @author cstaylor\n * @param <T>\n *            the archiver or extractor for this file\n */\npublic class DefaultZipVetoDelegate<T> implements ArchiveVetoDelegate<T>\n{\n    @Override\n    public boolean shouldSkip(final T source, final File item)\n    {\n        final String name = item.getAbsolutePath();\n        return name.contains(\".DS_Store\") || name.contains(\"__MACOSX\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/Extractor.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Path;\nimport java.util.Collections;\n\nimport org.apache.commons.compress.archivers.zip.ZipArchiveEntry;\nimport org.apache.commons.compress.archivers.zip.ZipFile;\nimport org.apache.commons.io.FileUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.NotifyingIOUtils;\nimport org.openstreetmap.atlas.streaming.NotifyingIOUtils.IOProgressListener;\n\n/**\n * Class for extracting the contents of a ZIP archive to a specified directory\n *\n * @author cstaylor\n */\npublic final class Extractor extends AbstractArchiverOrExtractor<Extractor>\n{\n    /**\n     * Responsible for tracking the process of a file being extracted from an archive\n     *\n     * @author cstaylor\n     */\n    class Progress implements IOProgressListener\n    {\n        private final File file;\n\n        private final long length;\n\n        Progress(final File file, final long length)\n        {\n            this.file = file;\n            this.length = length;\n        }\n\n        @Override\n        public void completed()\n        {\n            fireItemCompleted(this.file);\n        }\n\n        @Override\n        public void failed(final IOException oops)\n        {\n            Extractor.this.errorCount++;\n            fireItemFailed(this.file, oops);\n        }\n\n        @Override\n        public void started()\n        {\n            fireItemStarted(this.file);\n        }\n\n        @Override\n        public void statusUpdate(final long count)\n        {\n            fireItemInProgress(this.file, count, this.length);\n        }\n    }\n\n    /**\n     * Where we are extracting to\n     */\n    private final File outputDirectory;\n\n    /**\n     * The number of errors that occurred during extraction\n     */\n    private int errorCount;\n\n    /**\n     * Skip existing?\n     */\n    private boolean skipExisting;\n\n    /**\n     * Static factory method for creating a new Extractor with the ZIP archive format; currently the\n     * only format supported\n     *\n     * @param outputDirectory\n     *            the destination directory for any extraction operations\n     * @return the Extractor instance; useful for chaining method calls\n     */\n    public static Extractor extractZipArchive(final File outputDirectory)\n    {\n        if (outputDirectory == null)\n        {\n            throw new IllegalArgumentException(\"outputDirectory is null\");\n        }\n        return new Extractor(outputDirectory);\n    }\n\n    public static Extractor extractZipArchive(final Path outputDirectory)\n    {\n        if (outputDirectory == null)\n        {\n            throw new IllegalArgumentException(\"outputDirectory is null\");\n        }\n        return new Extractor(outputDirectory.toFile());\n    }\n\n    /**\n     * Creates a new Extractor specifying where the contents of the zip file will be extracted to.\n     *\n     * @param output\n     *            the destination directory\n     */\n    private Extractor(final File output)\n    {\n        super(Extractor.class);\n        this.outputDirectory = output;\n        setVetoDelegate(new DefaultZipVetoDelegate<>());\n    }\n\n    @Override\n    public Extractor addArchiverEventListener(final ArchiverEventListener<Extractor> listener)\n    {\n        super.addArchiverEventListener(listener);\n        return this;\n    }\n\n    /**\n     * Extracts the contents of inputFile to the previously set outputDirectory.\n     * <p>\n     * Algorithm:<br>\n     * <ul>\n     * <li>Sanity check the current state of the Extractor and the inputFile argument</li>\n     * <li>Fire the archive started event</li>\n     * <li>Walk the contents of the inputFile\n     * <ul>\n     * <li>Check if the output file's parent directory is created; if not, create it</li>\n     * <li>Copy the contents of the archive entry to the output file</li>\n     * <li>Close both the current archive entry input stream and the output file's output stream\n     * </li>\n     * </ul>\n     * </li>\n     * <li>If at least one error occurred, fire the archive failed event</li>\n     * <li>Otherwise, fire the archive completed event</li>\n     * <li>Close the zip file</li>\n     * </ul>\n     *\n     * @param inputFile\n     *            the zip file we want to extract; must not be null\n     * @return the Extractor instance; useful for chaining method calls\n     * @throws IOException\n     *             if something I/O related failed\n     */\n    public Extractor extract(final File inputFile) throws IOException\n    {\n        if (inputFile == null)\n        {\n            throw new IllegalArgumentException(\"inputFile can't be null\");\n        }\n        if (this.outputDirectory == null)\n        {\n            throw new IllegalStateException(\"outputDirectory can't be null\");\n        }\n        if (this.outputDirectory.exists())\n        {\n            FileUtils.deleteQuietly(this.outputDirectory);\n        }\n        if (!this.outputDirectory.mkdirs())\n        {\n            throw new IOException(\n                    String.format(\"%s can't be created\", this.outputDirectory.getAbsolutePath()));\n        }\n\n        fireArchiveStarted();\n        try (ZipFile file = new ZipFile(inputFile))\n        {\n            for (final ZipArchiveEntry current : Collections.list(file.getEntries()))\n            {\n                final File outputFile = new File(this.outputDirectory, current.getName());\n\n                if (current.getName().contains(\"..\"))\n                {\n                    throw new CoreException(\"Using parent directory not allowed: {}\",\n                            current.getName());\n                }\n\n                if (shouldSkip(outputFile))\n                {\n                    fireItemSkipped(outputFile);\n                }\n                else if (outputFile.exists() && this.skipExisting)\n                {\n                    fireItemSkipped(outputFile);\n                }\n                else if (current.isDirectory())\n                {\n                    // Continue\n                }\n                else\n                {\n                    try (BufferedOutputStream bos = new BufferedOutputStream(\n                            new FileOutputStream(outputFile));\n                            InputStream inputStream = file.getInputStream(current))\n                    {\n                        outputFile.getParentFile().mkdirs();\n                        NotifyingIOUtils.copy(inputStream, bos,\n                                new Progress(outputFile, current.getSize()));\n                    }\n                }\n\n            }\n            if (this.errorCount > 0)\n            {\n                fireArchiveFailed();\n            }\n            else\n            {\n                fireArchiveCompleted();\n            }\n        }\n        return this;\n    }\n\n    public Extractor extract(final Path inputPath) throws IOException\n    {\n        if (inputPath == null)\n        {\n            throw new IllegalArgumentException(\"inputFile can't be null\");\n        }\n        return extract(inputPath.toFile());\n    }\n\n    public Extractor overwriteExisting()\n    {\n        this.skipExisting = false;\n        return this;\n    }\n\n    @Override\n    public Extractor removeArchiverEventListener(final ArchiverEventListener<Extractor> listener)\n    {\n        super.removeArchiverEventListener(listener);\n        return this;\n    }\n\n    @Override\n    public Extractor setVetoDelegate(final ArchiveVetoDelegate<Extractor> delegate)\n    {\n        super.setVetoDelegate(delegate);\n        return this;\n    }\n\n    public Extractor skipExisting()\n    {\n        this.skipExisting = true;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/UnzipperCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Demonstrates the Extractor class\n *\n * @author cstaylor\n */\npublic class UnzipperCommand extends Command\n{\n    private static final Switch<Path> OUTPUT_FILE_PARAMETER = new Switch<>(\"output\",\n            \"Output directory to store zip file entries\", Paths::get, Optionality.REQUIRED);\n\n    private static final Switch<Path> INPUT_ZIP_FILE_PARAMETER = new Switch<>(\"zip\",\n            \"Zip file to extract all of the data\", Paths::get, Optionality.REQUIRED);\n\n    public static void main(final String... args)\n    {\n        new UnzipperCommand().runWithoutQuitting(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Path inputPath = prepareInput((Path) command.get(INPUT_ZIP_FILE_PARAMETER));\n        final Path outputFile = prepareOutput((Path) command.get(OUTPUT_FILE_PARAMETER));\n        try\n        {\n            Extractor.extractZipArchive(outputFile).extract(inputPath);\n        }\n        catch (final Exception oops)\n        {\n            throw new CoreException(\"Error when extracting: {} -> {}\", inputPath, outputFile, oops);\n        }\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(OUTPUT_FILE_PARAMETER, INPUT_ZIP_FILE_PARAMETER);\n\n    }\n\n    private Path prepareInput(final Path inputPath)\n    {\n        if (!Files.isReadable(inputPath))\n        {\n            throw new CoreException(\"Can't read {} or it doesn't exist\", inputPath);\n        }\n        return inputPath;\n    }\n\n    private Path prepareOutput(final Path outputFile)\n    {\n        if (Files.exists(outputFile) && !Files.isDirectory(outputFile))\n        {\n            throw new CoreException(\"{} already exists and is not a directory\", outputFile);\n        }\n        try\n        {\n            Files.createDirectories(outputFile);\n        }\n        catch (final Exception oops)\n        {\n            throw new CoreException(\"Can't create {}\", outputFile, oops);\n        }\n        return outputFile;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/archive/ZipperCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.apache.commons.compress.archivers.ArchiveException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\n\n/**\n * Demonstrates the Archiver class\n *\n * @author cstaylor\n */\npublic class ZipperCommand extends Command\n{\n    private static final Switch<Path> INPUT_FILE_PARAMETER = new Switch<>(\"input\",\n            \"Input files to store in a zip file\", Paths::get, Optionality.REQUIRED);\n\n    private static final Switch<Path> OUTPUT_ZIP_FILE_PARAMETER = new Switch<>(\"zip\",\n            \"Zip file to store all of the data\", Paths::get, Optionality.REQUIRED);\n\n    private static final Flag COMPRESSION_FLAG = new Flag(\"compress\",\n            \"Enable compression of all files\");\n\n    public static void main(final String... args)\n    {\n        new ZipperCommand().runWithoutQuitting(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Path inputPath = prepareInput((Path) command.get(INPUT_FILE_PARAMETER));\n        final Path outputFile = prepareOutput((Path) command.get(OUTPUT_ZIP_FILE_PARAMETER));\n        try\n        {\n            Archiver.createZipArchiver(outputFile).compress(inputPath);\n        }\n        catch (final IOException | ArchiveException oops)\n        {\n            throw new CoreException(\"Error when archiving: {} -> {}\", inputPath, outputFile, oops);\n        }\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(INPUT_FILE_PARAMETER, OUTPUT_ZIP_FILE_PARAMETER,\n                COMPRESSION_FLAG);\n\n    }\n\n    private Path prepareInput(final Path inputPath)\n    {\n        if (!Files.isReadable(inputPath))\n        {\n            throw new CoreException(\"Can't read {} or it doesn't exist\", inputPath);\n        }\n        return inputPath;\n    }\n\n    private Path prepareOutput(final Path outputFile)\n    {\n        if (Files.exists(outputFile))\n        {\n            throw new CoreException(\"{} already exists. Aborting\", outputFile);\n        }\n        try\n        {\n            Files.createDirectories(outputFile.getParent());\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Can't create parent directories for {}\", outputFile, oops);\n        }\n        return outputFile;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/Arrays.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport java.util.Collection;\n\n/**\n * Utility method for arrays\n *\n * @author matthieun\n */\npublic final class Arrays\n{\n    public static int[] addNewItem(final int[] existing, final int newValue)\n    {\n        final int[] result = new int[existing.length + 1];\n        for (int i = 0; i < existing.length; i++)\n        {\n            result[i] = existing[i];\n        }\n        result[result.length - 1] = newValue;\n        return result;\n    }\n\n    public static long[] addNewItem(final long[] existing, final long newValue)\n    {\n        final long[] result = new long[existing.length + 1];\n        for (int i = 0; i < existing.length; i++)\n        {\n            result[i] = existing[i];\n        }\n        result[result.length - 1] = newValue;\n        return result;\n    }\n\n    public static long[] addNewItemAndResizeOnlyIfNecessary(final long[] existing,\n            final long newValue, final int index)\n    {\n        long[] result = existing;\n        if (index >= existing.length)\n        {\n            // Needs resizing. Double the size...\n            result = java.util.Arrays.copyOf(existing, existing.length * 2);\n        }\n        result[index] = newValue;\n        return result;\n    }\n\n    public static long[] toArray(final Collection<Long> list)\n    {\n        final long[] result = new long[list.size()];\n        int index = 0;\n        for (final Long value : list)\n        {\n            result[index++] = value;\n        }\n        return result;\n    }\n\n    public static long[] trimToSize(final long[] existing, final int size)\n    {\n        if (size <= 0)\n        {\n            return new long[0];\n        }\n        return java.util.Arrays.copyOfRange(existing, 0, size - 1);\n    }\n\n    private Arrays()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/BitArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\n/**\n * {@link LargeArray} for type {@link Boolean}\n *\n * @author matthieun\n */\npublic class BitArray extends LargeArray<Boolean>\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Boolean}\n     *\n     * @author matthieun\n     */\n    public static class BitPrimitiveArray extends PrimitiveArray<Boolean>\n    {\n        private static final long serialVersionUID = 5686729947785133814L;\n        private final boolean[] array = new boolean[size()];\n\n        public BitPrimitiveArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public Boolean get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<Boolean> getNewArray(final int size)\n        {\n            return new BitPrimitiveArray(size);\n        }\n\n        @Override\n        public void set(final int index, final Boolean item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = -342493023854989301L;\n\n    public BitArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public BitArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    @Override\n    protected PrimitiveArray<Boolean> getNewArray(final int size)\n    {\n        return new BitPrimitiveArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/BooleanArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\n/**\n * {@link LargeArray} for type {@link Boolean}\n *\n * @author matthieun\n */\npublic class BooleanArray extends LargeArray<Boolean>\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Boolean}\n     *\n     * @author matthieun\n     */\n    public static class BooleanPrimitiveArray extends PrimitiveArray<Boolean>\n    {\n        private static final long serialVersionUID = -3932998946137654750L;\n        private final boolean[] array = new boolean[size()];\n\n        public BooleanPrimitiveArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public Boolean get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<Boolean> getNewArray(final int size)\n        {\n            return new BooleanPrimitiveArray(size);\n        }\n\n        @Override\n        public void set(final int index, final Boolean item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = -6015640040842668449L;\n\n    public BooleanArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public BooleanArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    @Override\n    protected PrimitiveArray<Boolean> getNewArray(final int size)\n    {\n        return new BooleanPrimitiveArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/ByteArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\n/**\n * {@link LargeArray} for type {@link Byte}\n *\n * @author matthieun\n */\npublic class ByteArray extends LargeArray<Byte>\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Byte}\n     *\n     * @author matthieun\n     */\n    public static class BytePrimitiveArray extends PrimitiveArray<Byte>\n    {\n        private static final long serialVersionUID = 6928093441524751750L;\n        private final byte[] array = new byte[size()];\n\n        public BytePrimitiveArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public Byte get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<Byte> getNewArray(final int size)\n        {\n            return new BytePrimitiveArray(size);\n        }\n\n        @Override\n        public void set(final int index, final Byte item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = 6401198662134364211L;\n\n    public ByteArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public ByteArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    @Override\n    protected PrimitiveArray<Byte> getNewArray(final int size)\n    {\n        return new BytePrimitiveArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/ByteArrayOfArrays.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoByteArrayOfArraysAdapter;\n\n/**\n * {@link LargeArray} of arrays of byte (byte[])\n *\n * @author matthieun\n * @author lcram\n */\npublic class ByteArrayOfArrays extends LargeArray<byte[]> implements ProtoSerializable\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Byte}\n     *\n     * @author matthieun\n     */\n    public static class PrimitiveByteArrayArray extends PrimitiveArray<byte[]>\n    {\n        private static final long serialVersionUID = -89500356133572470L;\n\n        private final byte[][] array = new byte[size()][];\n\n        public PrimitiveByteArrayArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public byte[] get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<byte[]> getNewArray(final int size)\n        {\n            return new PrimitiveByteArrayArray(size);\n        }\n\n        @Override\n        public void set(final int index, final byte[] item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = 9114627155973700091L;\n\n    public ByteArrayOfArrays(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public ByteArrayOfArrays(final long maximumSize, final int memoryBlockSize,\n            final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    /**\n     * This nullary constructor is solely for use by the {@link PackedAtlasSerializer}, which calls\n     * it using reflection. It allows the serializer code to obtain a handle on a\n     * {@link ByteArrayOfArrays} that it can use to grab the correct {@link ProtoAdapter}. The\n     * object initialized with this constructor will be corrupted for general use and should be\n     * discarded.\n     */\n    @SuppressWarnings(\"unused\")\n    private ByteArrayOfArrays()\n    {\n        super();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof ByteArrayOfArrays)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            final ByteArrayOfArrays that = (ByteArrayOfArrays) other;\n            if (!Objects.equals(this.getName(), that.getName()))\n            {\n                return false;\n            }\n            if (this.size() != that.size())\n            {\n                return false;\n            }\n            for (long index = 0; index < this.size(); index++)\n            {\n                final byte[] thisSubArray = this.get(index);\n                final byte[] thatSubArray = that.get(index);\n                if (thisSubArray.length != thatSubArray.length)\n                {\n                    return false;\n                }\n                for (int subIndex = 0; subIndex < thisSubArray.length; subIndex++)\n                {\n                    if (thisSubArray[subIndex] != thatSubArray[subIndex])\n                    {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoByteArrayOfArraysAdapter();\n    }\n\n    /*\n     * NOTE: This hashCode implementation uses the array object references and not the actual array\n     * values. Keep this in mind before using.\n     */\n    @Override\n    public int hashCode()\n    {\n        final int initialPrime = 31;\n        final int hashSeed = 37;\n\n        int hash = initialPrime;\n        for (int index = 0; index < this.size(); index++)\n        {\n            hash = hashSeed * hash + Arrays.hashCode(this.get(index));\n        }\n\n        return hash;\n    }\n\n    @Override\n    protected PrimitiveArray<byte[]> getNewArray(final int size)\n    {\n        return new PrimitiveByteArrayArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/IntegerArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\n/**\n * {@link LargeArray} for type {@link Integer}\n *\n * @author matthieun\n */\npublic class IntegerArray extends LargeArray<Integer>\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Integer}\n     *\n     * @author matthieun\n     */\n    public static class IntegerPrimitiveArray extends PrimitiveArray<Integer>\n    {\n        private static final long serialVersionUID = -3932998946137654750L;\n        private final int[] array = new int[size()];\n\n        public IntegerPrimitiveArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public Integer get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<Integer> getNewArray(final int size)\n        {\n            return new IntegerPrimitiveArray(size);\n        }\n\n        @Override\n        public void set(final int index, final Integer item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = -6015640040842668449L;\n\n    public IntegerArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public IntegerArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    @Override\n    protected PrimitiveArray<Integer> getNewArray(final int size)\n    {\n        return new IntegerPrimitiveArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/IntegerArrayOfArrays.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoIntegerArrayOfArraysAdapter;\n\n/**\n * {@link LargeArray} of arrays of int (int[])\n *\n * @author matthieun\n * @author lcram\n */\npublic class IntegerArrayOfArrays extends LargeArray<int[]> implements ProtoSerializable\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Long}\n     *\n     * @author matthieun\n     */\n    public static class PrimitiveIntegerArrayArray extends PrimitiveArray<int[]>\n    {\n        private static final long serialVersionUID = -4397674333836117261L;\n        private final int[][] array = new int[size()][];\n\n        public PrimitiveIntegerArrayArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public int[] get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<int[]> getNewArray(final int size)\n        {\n            return new PrimitiveIntegerArrayArray(size);\n        }\n\n        @Override\n        public void set(final int index, final int[] item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = 6793499910834785994L;\n\n    public IntegerArrayOfArrays(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public IntegerArrayOfArrays(final long maximumSize, final int memoryBlockSize,\n            final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    /**\n     * This nullary constructor is solely for use by the {@link PackedAtlasSerializer}, which calls\n     * it using reflection. It allows the serializer code to obtain a handle on a\n     * {@link IntegerArrayOfArrays} that it can use to grab the correct {@link ProtoAdapter}. The\n     * object initialized with this constructor will be corrupted for general use and should be\n     * discarded.\n     */\n    @SuppressWarnings(\"unused\")\n    private IntegerArrayOfArrays()\n    {\n        super();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof IntegerArrayOfArrays)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            final IntegerArrayOfArrays that = (IntegerArrayOfArrays) other;\n            if (!Objects.equals(this.getName(), that.getName()))\n            {\n                return false;\n            }\n            if (this.size() != that.size())\n            {\n                return false;\n            }\n            for (long index = 0; index < this.size(); index++)\n            {\n                final int[] thisSubArray = this.get(index);\n                final int[] thatSubArray = that.get(index);\n                if (thisSubArray.length != thatSubArray.length)\n                {\n                    return false;\n                }\n                for (int subIndex = 0; subIndex < thisSubArray.length; subIndex++)\n                {\n                    if (thisSubArray[subIndex] != thatSubArray[subIndex])\n                    {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoIntegerArrayOfArraysAdapter();\n    }\n\n    /*\n     * NOTE: This hashCode implementation uses the array object references and not the actual array\n     * values. Keep this in mind before using.\n     */\n    @Override\n    public int hashCode()\n    {\n        final int initialPrime = 31;\n        final int hashSeed = 37;\n\n        final int nameHash = this.getName() == null ? 0 : this.getName().hashCode();\n        int hash = hashSeed * initialPrime + nameHash;\n        hash = hashSeed * hash + Long.valueOf(this.size()).hashCode();\n        for (long index = 0; index < this.size(); index++)\n        {\n            hash = hashSeed * hash + Arrays.hashCode(this.get(index));\n        }\n\n        return hash;\n    }\n\n    @Override\n    protected PrimitiveArray<int[]> getNewArray(final int size)\n    {\n        return new PrimitiveIntegerArrayArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/LargeArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A LargeArray that can have more than {@link Integer}.MAX_VALUE items. It is backed by multiple\n * T[] arrays. Items can be added and gotten, but not removed. The get operations take long indices.\n *\n * @param <T>\n *            The type of items in the array\n * @author matthieun\n * @author lcram\n */\npublic abstract class LargeArray<T> implements Iterable<T>, Serializable\n{\n    private static final long serialVersionUID = -8827093729953143426L;\n    private static final Logger logger = LoggerFactory.getLogger(LargeArray.class);\n\n    private static final int DEFAULT_MEMORY_BLOCK_SIZE = 1024;\n\n    private final List<PrimitiveArray<T>> arrays;\n    private final long maximumSize;\n    private long nextIndex = 0;\n    private final int memoryBlockSize;\n    private final int subArraySize;\n    private String name = null;\n\n    /**\n     * Create a {@link LargeArray}\n     *\n     * @param maximumSize\n     *            The maximum size of the array\n     */\n    public LargeArray(final long maximumSize)\n    {\n        this(maximumSize, DEFAULT_MEMORY_BLOCK_SIZE, Integer.MAX_VALUE);\n    }\n\n    /**\n     * Create a {@link LargeArray}\n     *\n     * @param maximumSize\n     *            The maximum size of the array\n     * @param memoryBlockSize\n     *            The initial block size for sub-array creation. If small, it might resize often, if\n     *            large, there might be unused space.\n     * @param subArraySize\n     *            The maximum size of the sub arrays\n     */\n    public LargeArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        if (memoryBlockSize < 0)\n        {\n            throw new CoreException(\"memoryBlockSize ({}) cannot be negative\", memoryBlockSize);\n        }\n        if (subArraySize < 0)\n        {\n            throw new CoreException(\"subArraySize ({}) cannot be negative\", subArraySize);\n        }\n\n        this.maximumSize = maximumSize;\n        this.arrays = new ArrayList<>();\n        this.memoryBlockSize = memoryBlockSize;\n        this.subArraySize = subArraySize;\n    }\n\n    /**\n     * This nullary constructor exists solely for subclasses of {@link LargeArray} that wish to\n     * implement their own nullary constructor. These nullary constructors should only be used by\n     * serialization code in {@link PackedAtlasSerializer} that needs to obtain\n     * {@link ProtoAdapter}s. The objects they initialize are corrupted for general use and should\n     * be discarded.\n     */\n    protected LargeArray()\n    {\n        this.arrays = null;\n        this.maximumSize = 0;\n        this.memoryBlockSize = 0;\n        this.subArraySize = 0;\n    }\n\n    /**\n     * Add an item to the array\n     *\n     * @param item\n     *            The item to add\n     * @throws CoreException\n     *             if the array is full.\n     */\n    public void add(final T item)\n    {\n        if (this.nextIndex >= this.maximumSize)\n        {\n            throw new CoreException(\"The array is full. Cannot add \" + item);\n        }\n        final int arrayIndex = arrayIndex(this.nextIndex);\n        final int indexInside = indexInside(this.nextIndex);\n        if (this.arrays.size() <= arrayIndex)\n        {\n            // Set an array size\n            this.arrays.add(getNewArray(this.memoryBlockSize));\n        }\n        if (indexInside >= this.arrays.get(arrayIndex).size())\n        {\n            final PrimitiveArray<T> old = this.arrays.get(arrayIndex);\n            final int maximumSizeFromDoubling = Math.min(2 * old.size(), this.subArraySize);\n            final long filledArraysSize = filledArraysSize();\n            final int maximumSizeFromTotal = (int) Math.min(this.maximumSize - filledArraysSize,\n                    this.subArraySize);\n            final int newSize = Math.min(maximumSizeFromDoubling, maximumSizeFromTotal);\n            logger.warn(\"Resizing array {} of {} ({}), from {} to {}.\", arrayIndex,\n                    getName() == null ? super.toString() : getName(),\n                    this.getClass().getSimpleName(), old.size(), newSize);\n            this.arrays.set(arrayIndex, old.withNewSize(newSize));\n        }\n        this.arrays.get(arrayIndex(this.nextIndex)).set(indexInside(this.nextIndex), item);\n        this.nextIndex++;\n    }\n\n    /**\n     * A basic equals() implementation. Note that if this class is parameterized with an array type,\n     * this method may not work as expected (due to array equals() performing a reference\n     * comparison). Child classes of LargeArray may want to override this method to improve its\n     * behavior in special cases.\n     */\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof LargeArray)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            @SuppressWarnings(\"unchecked\")\n            final LargeArray<T> that = (LargeArray<T>) other;\n            if (!Objects.equals(this.getName(), that.getName()))\n            {\n                return false;\n            }\n            if (this.size() != that.size())\n            {\n                return false;\n            }\n            for (long index = 0; index < this.size(); index++)\n            {\n                if (!this.get(index).equals(that.get(index)))\n                {\n                    return false;\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Get an item from the array\n     *\n     * @param index\n     *            The index to get\n     * @return The item at the specified index\n     * @throws CoreException\n     *             If the index is out of bounds.\n     */\n    public T get(final long index)\n    {\n        if (index >= this.nextIndex)\n        {\n            throw new CoreException(index + \" is out of bounds (size = \" + size() + \")\");\n        }\n        return this.arrays.get(arrayIndex(index)).get(indexInside(index));\n    }\n\n    /**\n     * @return The name of this array\n     */\n    public String getName()\n    {\n        return this.name;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final int initialPrime = 31;\n        final int hashSeed = 37;\n\n        final int nameHash = this.getName() == null ? 0 : this.getName().hashCode();\n        int hash = hashSeed * initialPrime + nameHash;\n        hash = hashSeed * hash + Long.valueOf(this.size()).hashCode();\n\n        for (long index = 0; index < this.size(); index++)\n        {\n            hash = hashSeed * hash + this.get(index).hashCode();\n        }\n\n        return hash;\n    }\n\n    public boolean isEmpty()\n    {\n        return size() == 0;\n    }\n\n    @Override\n    public Iterator<T> iterator()\n    {\n        return Iterables.indexBasedIterable(size(), this::get).iterator();\n    }\n\n    /**\n     * Replace an already existing value\n     *\n     * @param index\n     *            The index to replace the value at\n     * @param item\n     *            The new value to put\n     */\n    public void set(final long index, final T item)\n    {\n        if (index >= size())\n        {\n            throw new CoreException(\"Cannot replace an element that is not there. Index: \" + index\n                    + \", size: \" + size());\n        }\n        this.arrays.get(arrayIndex(index)).set(indexInside(index), item);\n    }\n\n    public void setName(final String name)\n    {\n        this.name = name;\n    }\n\n    /**\n     * @return The used size (which will always be smaller or equal to the size allocated in memory)\n     */\n    public long size()\n    {\n        return this.nextIndex;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[\");\n        builder.append(this.getClass().getSimpleName());\n        builder.append(\" \");\n        this.forEach(t ->\n        {\n            if (builder.length() > 0)\n            {\n                builder.append(\", \");\n            }\n            builder.append(t);\n        });\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    /**\n     * Trim this {@link LargeArray}. WARNING: As much as this might save memory on arrays filled a\n     * small amount compared to the memoryBlockSize, it will resize the last {@link PrimitiveArray}\n     * of this {@link LargeArray} which might take a lot of time, and (temporarily) a lot of memory.\n     */\n    public void trim()\n    {\n        trimIfLessFilledThan(Ratio.MAXIMUM);\n    }\n\n    /**\n     * Trim this {@link LargeArray} if and only if the fill {@link Ratio} of the last\n     * {@link PrimitiveArray} of this {@link LargeArray} is less than the provided {@link Ratio}\n     *\n     * @param ratio\n     *            The provided reference {@link Ratio}\n     */\n    public void trimIfLessFilledThan(final Ratio ratio)\n    {\n        logger.trace(\"Trimming {} with Ratio {}\", getName(), ratio);\n        if (this.arrays.isEmpty())\n        {\n            return;\n        }\n        final int arrayIndex = this.arrays.size() - 1;\n        final PrimitiveArray<T> rightmost = this.arrays.get(arrayIndex);\n\n        /*\n         * Exit early in the case that the rightmost subarray is full - there is nothing to trim. In\n         * fact, the trim logic below breaks in this case, and will wipe out the rightmost subarray\n         * instead of trimming it.\n         */\n        if (rightmostSubarrayIsFull())\n        {\n            return;\n        }\n\n        // Here nextIndex is actually the size, and not size-1\n        final int indexInside = indexInside(this.nextIndex);\n        if (Ratio.ratio((double) indexInside / rightmost.size()).isLessThan(ratio))\n        {\n            this.arrays.set(arrayIndex, rightmost.trimmed(indexInside));\n        }\n    }\n\n    public LargeArray<T> withName(final String name)\n    {\n        setName(name);\n        return this;\n    }\n\n    /**\n     * @return the arrays, for testing\n     */\n    protected List<PrimitiveArray<T>> getArrays()\n    {\n        return this.arrays;\n    }\n\n    /**\n     * Create a new primitive array of the Item.\n     *\n     * @param size\n     *            The size of the array\n     * @return The new primitive array.\n     */\n    protected abstract PrimitiveArray<T> getNewArray(int size);\n\n    private int arrayIndex(final long index)\n    {\n        return (int) (index / this.subArraySize);\n    }\n\n    private long filledArraysSize()\n    {\n        if (this.arrays.size() <= 1)\n        {\n            return 0L;\n        }\n        long result = 0L;\n        for (int i = 0; i < this.arrays.size() - 2; i++)\n        {\n            result += this.arrays.get(i).size();\n        }\n        return result;\n    }\n\n    private int indexInside(final long index)\n    {\n        return (int) (index % this.subArraySize);\n    }\n\n    private boolean rightmostSubarrayIsFull()\n    {\n        return this.nextIndex > 0 && indexInside(this.nextIndex) == 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/LongArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoLongArrayAdapter;\n\n/**\n * {@link LargeArray} for type {@link Long}\n *\n * @author matthieun\n */\npublic class LongArray extends LargeArray<Long> implements ProtoSerializable\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Long}\n     *\n     * @author matthieun\n     */\n    public static class PrimitiveLongArray extends PrimitiveArray<Long>\n    {\n        private static final long serialVersionUID = 8325024756132919495L;\n        private final long[] array = new long[size()];\n\n        public PrimitiveLongArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public Long get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<Long> getNewArray(final int size)\n        {\n            return new PrimitiveLongArray(size);\n        }\n\n        @Override\n        public void set(final int index, final Long item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = -6368556371326217582L;\n\n    public LongArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public LongArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    /**\n     * This nullary constructor is solely for use by the {@link PackedAtlasSerializer}, which calls\n     * it using reflection. It allows the serializer code to obtain a handle on a {@link LongArray}\n     * that it can use to grab the correct {@link ProtoAdapter}. The object initialized with this\n     * constructor will be corrupted for general use and should be discarded.\n     */\n    @SuppressWarnings(\"unused\")\n    private LongArray()\n    {\n        super();\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoLongArrayAdapter();\n    }\n\n    @Override\n    protected PrimitiveArray<Long> getNewArray(final int size)\n    {\n        return new PrimitiveLongArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/LongArrayOfArrays.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoLongArrayOfArraysAdapter;\n\n/**\n * {@link LargeArray} of arrays of long (long[])\n *\n * @author matthieun\n * @author lcram\n */\npublic class LongArrayOfArrays extends LargeArray<long[]> implements ProtoSerializable\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Long}\n     *\n     * @author matthieun\n     */\n    public static class PrimitiveLongArrayArray extends PrimitiveArray<long[]>\n    {\n        private static final long serialVersionUID = 8377199081867533638L;\n        private final long[][] array = new long[size()][];\n\n        public PrimitiveLongArrayArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public long[] get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<long[]> getNewArray(final int size)\n        {\n            return new PrimitiveLongArrayArray(size);\n        }\n\n        @Override\n        public void set(final int index, final long[] item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = -1847060797258075365L;\n\n    public LongArrayOfArrays(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public LongArrayOfArrays(final long maximumSize, final int memoryBlockSize,\n            final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    /**\n     * This nullary constructor is solely for use by the {@link PackedAtlasSerializer}, which calls\n     * it using reflection. It allows the serializer code to obtain a handle on a\n     * {@link LongArrayOfArrays} that it can use to grab the correct {@link ProtoAdapter}. The\n     * object initialized with this constructor will be corrupted for general use and should be\n     * discarded.\n     */\n    @SuppressWarnings(\"unused\")\n    private LongArrayOfArrays()\n    {\n        super();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof LongArrayOfArrays)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            final LongArrayOfArrays that = (LongArrayOfArrays) other;\n            if (!Objects.equals(this.getName(), that.getName()))\n            {\n                return false;\n            }\n            if (this.size() != that.size())\n            {\n                return false;\n            }\n            for (long index = 0; index < this.size(); index++)\n            {\n                final long[] thisSubArray = this.get(index);\n                final long[] thatSubArray = that.get(index);\n                if (thisSubArray.length != thatSubArray.length)\n                {\n                    return false;\n                }\n                for (int subIndex = 0; subIndex < thisSubArray.length; subIndex++)\n                {\n                    if (thisSubArray[subIndex] != thatSubArray[subIndex])\n                    {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoLongArrayOfArraysAdapter();\n    }\n\n    /*\n     * NOTE: This hashCode implementation uses the array object references and not the actual array\n     * values. Keep this in mind before using.\n     */\n    @Override\n    public int hashCode()\n    {\n        final int initialPrime = 31;\n        final int hashSeed = 37;\n\n        final int nameHash = this.getName() == null ? 0 : this.getName().hashCode();\n        int hash = hashSeed * initialPrime + nameHash;\n        hash = hashSeed * hash + Long.valueOf(this.size()).hashCode();\n        for (long index = 0; index < this.size(); index++)\n        {\n            hash = hashSeed * hash + this.get(index).hashCode();\n        }\n\n        return hash;\n    }\n\n    @Override\n    protected PrimitiveArray<long[]> getNewArray(final int size)\n    {\n        return new PrimitiveLongArrayArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/PolyLineArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.StringCompressedPolyLine;\nimport org.openstreetmap.atlas.geography.StringCompressedPolyLine.PolyLineCompressionException;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoPolyLineArrayAdapter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link LargeArray} of {@link StringCompressedPolyLine}s, with an interface using {@link PolyLine}\n * s.\n *\n * @author matthieun\n */\npublic class PolyLineArray extends LargeArray<PolyLine> implements ProtoSerializable\n{\n    /**\n     * @author matthieun\n     * @param <Poly>\n     *            An implementation of {@link PolyLine}\n     */\n    public abstract static class PrimitivePointsArray<Poly extends PolyLine>\n            extends PrimitiveArray<Poly>\n    {\n        private static final long serialVersionUID = 3532399057462343784L;\n\n        private final byte[][] encodings;\n\n        public PrimitivePointsArray(final int size)\n        {\n            super(size);\n            this.encodings = new byte[size][];\n        }\n\n        @Override\n        public void set(final int index, final Poly item)\n        {\n            // Here, whether it is a polyline or a polygon does not matter, the encodings will be\n            // the same\n            StringCompressedPolyLine compressed;\n            try\n            {\n                compressed = new StringCompressedPolyLine(item);\n            }\n            catch (final PolyLineCompressionException e)\n            {\n                logger.error(\"Unable to compress polyLine {} at index {}. Sending to Null Island.\",\n                        item, index, e);\n                compressed = new StringCompressedPolyLine(new PolyLine(Location.CENTER));\n            }\n            this.encodings[index] = compressed.getEncoding();\n        }\n\n        protected byte[][] getEncodings()\n        {\n            return this.encodings;\n        }\n    }\n\n    /**\n     * A primitive array specifically for {@link PolyLine}\n     *\n     * @author matthieun\n     */\n    public static class PrimitivePolyLineArray extends PrimitivePointsArray<PolyLine>\n    {\n        private static final long serialVersionUID = -9008848366079793820L;\n\n        public PrimitivePolyLineArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public PolyLine get(final int index)\n        {\n            return new StringCompressedPolyLine(getEncodings()[index]).asPolyLine();\n        }\n\n        @Override\n        public PrimitiveArray<PolyLine> getNewArray(final int size)\n        {\n            return new PrimitivePolyLineArray(size);\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(PolyLineArray.class);\n\n    private static final long serialVersionUID = -4475168018638543482L;\n\n    public PolyLineArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public PolyLineArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    /**\n     * This nullary constructor is solely for use by the {@link PackedAtlasSerializer}, which calls\n     * it using reflection. It allows the serializer code to obtain a handle on a\n     * {@link PolyLineArray} that it can use to grab the correct {@link ProtoAdapter}. The object\n     * initialized with this constructor will be corrupted for general use and should be discarded.\n     */\n    @SuppressWarnings(\"unused\")\n    private PolyLineArray()\n    {\n        super();\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoPolyLineArrayAdapter();\n    }\n\n    @Override\n    protected PrimitiveArray<PolyLine> getNewArray(final int size)\n    {\n        return new PrimitivePolyLineArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/PolygonArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.StringCompressedPolygon;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoPolygonArrayAdapter;\nimport org.openstreetmap.atlas.utilities.arrays.PolyLineArray.PrimitivePointsArray;\n\n/**\n * {@link LargeArray} of {@link StringCompressedPolygon}s, with an interface of {@link Polygon}s.\n *\n * @author matthieun\n */\npublic class PolygonArray extends LargeArray<Polygon> implements ProtoSerializable\n{\n    /**\n     * Primitive array for polygons\n     *\n     * @author matthieun\n     */\n    public static class PrimitivePolygonArray extends PrimitivePointsArray<Polygon>\n    {\n        private static final long serialVersionUID = 1115133908622542632L;\n\n        public PrimitivePolygonArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public Polygon get(final int index)\n        {\n            return new StringCompressedPolygon(getEncodings()[index]).asPolygon();\n        }\n\n        @Override\n        public PrimitiveArray<Polygon> getNewArray(final int size)\n        {\n            return new PrimitivePolygonArray(size);\n        }\n    }\n\n    private static final long serialVersionUID = -2337695414673604456L;\n\n    public PolygonArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public PolygonArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    /**\n     * This nullary constructor is solely for use by the {@link PackedAtlasSerializer}, which calls\n     * it using reflection. It allows the serializer code to obtain a handle on a\n     * {@link PolygonArray} that it can use to grab the correct {@link ProtoAdapter}. The object\n     * initialized with this constructor will be corrupted for general use and should be discarded.\n     */\n    @SuppressWarnings(\"unused\")\n    private PolygonArray()\n    {\n        super();\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoPolygonArrayAdapter();\n    }\n\n    @Override\n    protected PrimitiveArray<Polygon> getNewArray(final int size)\n    {\n        return new PrimitivePolygonArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/PrimitiveArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport java.io.Serializable;\nimport java.util.stream.IntStream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * Wrapper for a primitive array corresponding to a given type.\n *\n * @author matthieun\n * @param <T>\n *            The non-primitive type of the primitive type this array is wrapping.\n */\npublic abstract class PrimitiveArray<T> implements Serializable\n{\n    private static final long serialVersionUID = 7815464773512459667L;\n\n    private final int size;\n\n    /**\n     * @param size\n     *            The size of the primitive array\n     */\n    public PrimitiveArray(final int size)\n    {\n        this.size = size;\n    }\n\n    /**\n     * @param index\n     *            The index wanted\n     * @return The item at the index\n     */\n    public abstract T get(int index);\n\n    /**\n     * Build a new empty primitive array of the specified size.\n     *\n     * @param size\n     *            the specified size\n     * @return a new empty primitive array of the specified size\n     */\n    public abstract PrimitiveArray<T> getNewArray(int size);\n\n    /**\n     * Set an item at the specified index in the array.\n     *\n     * @param index\n     *            The specified index\n     * @param item\n     *            The item to set at the specified index\n     */\n    public abstract void set(int index, T item);\n\n    /**\n     * @return The size of this primitive array\n     */\n    public int size()\n    {\n        return this.size;\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[\");\n        final StringList list = new StringList();\n        for (int i = 0; i < size(); i++)\n        {\n            list.add(this.get(i).toString());\n        }\n        builder.append(list.join(\", \"));\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    /**\n     * Trim this array\n     *\n     * @param size\n     *            The size to trim to\n     * @return The trimmed array with data originating from this array up to the size\n     */\n    public PrimitiveArray<T> trimmed(final int size)\n    {\n        if (size >= this.size)\n        {\n            return this;\n        }\n        final PrimitiveArray<T> result = getNewArray(size);\n        // Equivalent of for (int i = 0; i < size; i++)\n        IntStream.range(0, size).forEach(i -> result.set(i, get(i)));\n        return result;\n    }\n\n    /**\n     * Copy this primitive array into another primitive array of bigger size.\n     *\n     * @param newSize\n     *            The bigger size\n     * @return The new bigger array, copied from this one.\n     */\n    public PrimitiveArray<T> withNewSize(final int newSize)\n    {\n        if (newSize < size())\n        {\n            throw new CoreException(\"Cannot copy into a smaller array. This is \" + size()\n                    + \" and the new size asked is \" + newSize);\n        }\n        final PrimitiveArray<T> young = getNewArray(newSize);\n        for (int i = 0; i < size(); i++)\n        {\n            young.set(i, get(i));\n        }\n        return young;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/ShortArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\n/**\n * {@link LargeArray} for type {@link Short}\n *\n * @author matthieun\n */\npublic class ShortArray extends LargeArray<Short>\n{\n    /**\n     * {@link PrimitiveArray} for type {@link Short}\n     *\n     * @author matthieun\n     */\n    public static class ShortPrimitiveArray extends PrimitiveArray<Short>\n    {\n        private static final long serialVersionUID = 3177690048477030833L;\n        private final short[] array = new short[size()];\n\n        public ShortPrimitiveArray(final int size)\n        {\n            super(size);\n        }\n\n        @Override\n        public Short get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<Short> getNewArray(final int size)\n        {\n            return new ShortPrimitiveArray(size);\n        }\n\n        @Override\n        public void set(final int index, final Short item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = 6867216948199207925L;\n\n    public ShortArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public ShortArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    @Override\n    protected PrimitiveArray<Short> getNewArray(final int size)\n    {\n        return new ShortPrimitiveArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/arrays/StringArray.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\n/**\n * {@link LargeArray} of Strings\n *\n * @author matthieun\n */\npublic class StringArray extends LargeArray<String>\n{\n    /**\n     * {@link PrimitiveArray} of Strings.\n     *\n     * @author matthieun\n     */\n    public static class StringPrimitiveArray extends PrimitiveArray<String>\n    {\n        private static final long serialVersionUID = 6050182547243598715L;\n        private final String[] array;\n\n        public StringPrimitiveArray(final int size)\n        {\n            super(size);\n            this.array = new String[size];\n        }\n\n        @Override\n        public String get(final int index)\n        {\n            return this.array[index];\n        }\n\n        @Override\n        public PrimitiveArray<String> getNewArray(final int size)\n        {\n            return new StringPrimitiveArray(size);\n        }\n\n        @Override\n        public void set(final int index, final String item)\n        {\n            this.array[index] = item;\n        }\n    }\n\n    private static final long serialVersionUID = 5462179570391723788L;\n\n    public StringArray(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    public StringArray(final long maximumSize, final int memoryBlockSize, final int subArraySize)\n    {\n        super(maximumSize, memoryBlockSize, subArraySize);\n    }\n\n    @Override\n    protected PrimitiveArray<String> getNewArray(final int size)\n    {\n        return new StringPrimitiveArray(size);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/ConcurrentResourceCache.java",
    "content": "package org.openstreetmap.atlas.utilities.caching;\n\nimport java.net.URI;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.caching.strategies.CachingStrategy;\nimport org.openstreetmap.atlas.utilities.caching.strategies.NamespaceCachingStrategy;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * <p>\n * The threadsafe {@link ResourceCache} implementation. There is a caveat, related to the fact that\n * some caching strategies utilize system-wide global state e.g. {@link NamespaceCachingStrategy}\n * uses the tmp filesystem. In doing so it becomes impossible to guarantee concurrency safety from\n * within the {@link ConcurrentResourceCache} alone.<br>\n * {@link ConcurrentResourceCache} needs a specified {@link CachingStrategy} and default fetching\n * {@link Function} at creation time. The cache then loads a resource using a given {@link URI}.\n * Since using {@link URI} objects can often be cumbersome, users of this class are encouraged to\n * extend it and overload the {@link ConcurrentResourceCache#get} method to take more convenient\n * parameters.\n * </p>\n *\n * @author lcram\n */\npublic class ConcurrentResourceCache implements ResourceCache\n{\n    private static final Logger logger = LoggerFactory.getLogger(ConcurrentResourceCache.class);\n\n    private final CachingStrategy cachingStrategy;\n    private final Function<URI, Optional<Resource>> fetcher;\n    private final UUID cacheID;\n\n    /**\n     * Create a new {@link ConcurrentResourceCache} with the given fetcher and strategy.\n     *\n     * @param cachingStrategy\n     *            the caching strategy\n     * @param fetcher\n     *            the default fetcher\n     */\n    public ConcurrentResourceCache(final CachingStrategy cachingStrategy,\n            final Function<URI, Optional<Resource>> fetcher)\n    {\n        this.cachingStrategy = cachingStrategy;\n        this.fetcher = fetcher;\n        this.cacheID = UUID.randomUUID();\n        logger.info(\"Initialized cache {} with ID {}\", this.getClass().getName(), this.cacheID);\n    }\n\n    @Override\n    public Optional<Resource> get(final URI resourceURI)\n    {\n        Optional<Resource> cachedResource;\n\n        // We must synchronize the application of the caching strategy since we cannot guarantee\n        // that the strategy does not utilize internal global state.\n        synchronized (this)\n        {\n            cachedResource = this.cachingStrategy.attemptFetch(resourceURI, this.fetcher);\n        }\n\n        if (cachedResource.isEmpty())\n        {\n            logger.warn(\"CacheID {}: cache fetch of {} failed, falling back to default fetcher...\",\n                    this.cacheID, resourceURI);\n\n            // We must also synchronize the application of the fetcher, since it may rely on state\n            // shared by the calling threads.\n            synchronized (this)\n            {\n                cachedResource = this.fetcher.apply(resourceURI);\n            }\n        }\n\n        return cachedResource;\n    }\n\n    /**\n     * Get the name of the backing {@link CachingStrategy}.\n     *\n     * @return the name\n     */\n    public String getStrategyName()\n    {\n        return this.cachingStrategy.getName();\n    }\n\n    @Override\n    public void invalidate()\n    {\n        logger.info(\"CacheID {}: invalidating cache\", this.cacheID);\n        // Synchronize invalidation with the same lock used to fetch and cache. This prevents\n        // invalidation corruption.\n        synchronized (this)\n        {\n            this.cachingStrategy.invalidate();\n        }\n    }\n\n    @Override\n    public void invalidate(final URI resourceURI)\n    {\n        logger.info(\"CacheID {}: invalidating resource {}\", this.cacheID, resourceURI);\n        // Synchronize invalidation with the same lock used to fetch and cache. This prevents\n        // invalidation corruption.\n        synchronized (this)\n        {\n            this.cachingStrategy.invalidate(resourceURI);\n        }\n    }\n\n    /**\n     * Get a {@link UUID} for this cache instance. This is useful for logging.\n     *\n     * @return The cache instance {@link UUID}\n     */\n    protected UUID getCacheID()\n    {\n        return this.cacheID;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/LocalFileInMemoryCache.java",
    "content": "package org.openstreetmap.atlas.utilities.caching;\n\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Paths;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.caching.strategies.ByteArrayCachingStrategy;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * An example of how to extend the {@link ConcurrentResourceCache} to enhance functionality. This\n * class caches local files in memory, and abstracts the the messiness of file URIs behind a cleaner\n * interface. The {@link LocalFileInMemoryCache} forces the caching strategy to be\n * {@link ByteArrayCachingStrategy}, and forces the default fetcher to simply load a local file.\n *\n * @author lcram\n */\npublic class LocalFileInMemoryCache extends ConcurrentResourceCache\n{\n    private static final Logger logger = LoggerFactory.getLogger(LocalFileInMemoryCache.class);\n\n    /**\n     * Create a new {@link LocalFileInMemoryCache} with the default {@link FileSystem}. See\n     * {@link FileSystems#getDefault()} for more information.\n     */\n    public LocalFileInMemoryCache()\n    {\n        this(FileSystems.getDefault());\n    }\n\n    /**\n     * Create a new {@link LocalFileInMemoryCache} with the given {@link FileSystem}.\n     * \n     * @param fileSystem\n     *            the {@link FileSystem} to use for file loading\n     */\n    public LocalFileInMemoryCache(final FileSystem fileSystem)\n    {\n        super(new ByteArrayCachingStrategy(), uri ->\n        {\n            final File file = new File(uri.getPath(), fileSystem);\n            if (!file.exists())\n            {\n                logger.warn(\"File {} does not exist!\", file);\n                return Optional.empty();\n            }\n            return Optional.of(file);\n        });\n    }\n\n    /**\n     * Attempt to get the resource specified by the given path.\n     *\n     * @param path\n     *            the path to the desired resource\n     * @return an {@link Optional} wrapping the {@link Resource}\n     */\n    @Override\n    public Optional<Resource> get(final String path)\n    {\n        return this.get(Paths.get(path).toAbsolutePath().toUri());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/README.md",
    "content": "# utilities.caching package\n\n---\n\nThis package adds extensible `Resource` caching capabilities to `Atlas`.\n\nThe basic idea is that users can create a cache with a default population mechanism (fetcher), choose a caching strategy, and then fetch desired resources (keyed by `URI`s) using the strategy. A very simple use case might look like:\n\n```java\n// Let's read a resource at an arbitrary URI into a Resource.\n// Notice the fetcher function provided to constructor conforms to the\n// Function<URI, Resource> functional interface.\n\nfinal URI LOCAL_TEST_FILE_URI = URI.create(\"file:///path/to/some/file.txt\");\n\n// cache file contents into memory (a byte array)\nfinal ConcurrentResourceCache resourceCache = \n        new ConcurrentResourceCache(new ByteArrayCachingStrategy(), uri -> Optional.of(new File(uri.getPath())));\n\n// this will cache miss the first time and populate the cache using the provided fetcher\nResource r1 = resourceCache.get(LOCAL_TEST_FILE_URI).get();\n\n// this time we hit the cache, and read the bytes from memory instead of with the fetcher (from disk)\nResource r2 = resourceCache.get(LOCAL_TEST_FILE_URI).get();\n\n\n// Manually setting fetchers and CachingStrategies can be a pain. You can abstract\n// all this away by creating case-specific subclasses. This subclass cache uses\n// ByteArrayCachingStrategy by default and abstracts the get URI parameter by just\n// taking a path string as a parameter instead.\nfinal LocalFileInMemoryCache fileCache = new LocalFileInMemoryCache();\nResource r3 = fileCache.get(\"/path/to/another/file.txt\").get();\n```\nSee the `CachingTests` class for more usage examples, and the `LocalFileInMemoryCache` class for an example of how to extend `ConcurrentResourceCache`.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/ResourceCache.java",
    "content": "package org.openstreetmap.atlas.utilities.caching;\n\nimport java.net.URI;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.caching.strategies.CachingStrategy;\n\n/**\n * All {@link ResourceCache}s must conform to this interface. Of particular interest is the thread\n * safe implementation of this interface, the {@link ConcurrentResourceCache}.\n *\n * @author lcram\n */\npublic interface ResourceCache\n{\n    /**\n     * Attempt to get the resource specified by the given string URI.\n     *\n     * @param resourceURIString\n     *            the resource {@link URI} as a {@link String}\n     * @return an {@link Optional} wrapping the {@link Resource}\n     */\n    default Optional<Resource> get(final String resourceURIString)\n    {\n        return this.get(URI.create(resourceURIString));\n    }\n\n    /**\n     * Attempt to get the resource specified by the given URI.\n     *\n     * @param resourceURI\n     *            the resource {@link URI}\n     * @return an {@link Optional} wrapping the {@link Resource}\n     */\n    Optional<Resource> get(URI resourceURI);\n\n    /**\n     * Invalidate the contents of this cache. Generally, this method should rely on the\n     * {@link CachingStrategy#invalidate} implementation of the underlying strategy. However this is\n     * not enforced by the interface. since some implementations may need to do extra housekeeping\n     * to perform an invalidation. See {@link ConcurrentResourceCache} for an example.\n     */\n    void invalidate();\n\n    /**\n     * Invalidate the cached {@link Resource} for a given {@link URI}, if it exists.\n     *\n     * @param resourceURI\n     *            The {@link URI} of the {@link Resource} to invalidate\n     */\n    void invalidate(URI resourceURI);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/strategies/AbstractCachingStrategy.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.net.URI;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * An incomplete implementation of the {@link CachingStrategy} interface. Provides some additional\n * functionality for subclasses to leverage.\n *\n * @author lcram\n */\npublic abstract class AbstractCachingStrategy implements CachingStrategy\n{\n    private static final Logger logger = LoggerFactory.getLogger(AbstractCachingStrategy.class);\n\n    /*\n     * Cache the UUIDs for each URI so we only have to compute them once. Caching them in a\n     * comparatively small map is significantly faster than recomputing them every time. Subclasses\n     * may want to use this cache to associate a UUID with a given URI.\n     */\n    private final Map<String, UUID> uriStringToUUIDCache;\n\n    private final UUID strategyID;\n\n    public AbstractCachingStrategy()\n    {\n        this.uriStringToUUIDCache = new ConcurrentHashMap<>();\n        this.strategyID = UUID.randomUUID();\n        logger.info(\"Initialized strategy {} with ID {}\", this.getClass().getName(),\n                this.strategyID);\n    }\n\n    /**\n     * Get a {@link UUID} for this strategy instance. This is useful for logging.\n     *\n     * @return The strategy instance {@link UUID}\n     */\n    protected UUID getStrategyID()\n    {\n        return this.strategyID;\n    }\n\n    /**\n     * Given a URI, compute a universally unique identifier ({@link UUID}) for that URI. This method\n     * uses the {@link String} representation of a URI to compute the UUID. It will also cache\n     * computed UUIDs, so subsequent fetches will not incur a re-computation performance penalty.\n     *\n     * @param resourceURI\n     *            the {@link URI}\n     * @return the {@link UUID} of the given {@link URI}\n     */\n    protected UUID getUUIDForResourceURI(final URI resourceURI)\n    {\n        final String uriString = resourceURI.toString();\n\n        if (!this.uriStringToUUIDCache.containsKey(uriString))\n        {\n            // As of Java 8, this method computes the MD5 sum of the URI string.\n            // This can be relatively slow (10 digests per 1 ms), so we will cache the result in\n            // memory for subsequent requests.\n            final UUID newUUID = UUID.nameUUIDFromBytes(uriString.getBytes());\n            this.uriStringToUUIDCache.put(uriString, newUUID);\n            return newUUID;\n        }\n        else\n        {\n            return this.uriStringToUUIDCache.get(uriString);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/strategies/ByteArrayCachingStrategy.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Caching strategy that attempts to cache a {@link Resource} in a byte array, in memory.\n *\n * @author lcram\n */\npublic class ByteArrayCachingStrategy extends AbstractCachingStrategy\n{\n    /*\n     * Default size is arbitrarily set to 2 MiB\n     */\n    private static final long DEFAULT_BYTE_ARRAY_SIZE = 1024L * 1024 * 2;\n    private static final Logger logger = LoggerFactory.getLogger(ByteArrayCachingStrategy.class);\n\n    private final Map<UUID, ByteArrayResource> resourceCache;\n    private long initialArraySize;\n    private boolean useExactResourceSize;\n\n    public ByteArrayCachingStrategy()\n    {\n        this.resourceCache = new HashMap<>();\n        this.initialArraySize = DEFAULT_BYTE_ARRAY_SIZE;\n        this.useExactResourceSize = false;\n    }\n\n    @Override\n    public Optional<Resource> attemptFetch(final URI resourceURI,\n            final Function<URI, Optional<Resource>> defaultFetcher)\n    {\n        final UUID resourceUUID = this.getUUIDForResourceURI(resourceURI);\n\n        if (!this.resourceCache.containsKey(resourceUUID))\n        {\n            logger.trace(\n                    \"StrategyID {}: attempting to cache resource {} in byte array keyed on UUID {}\",\n                    this.getStrategyID(), resourceURI, resourceUUID);\n\n            final Optional<Resource> resource = defaultFetcher.apply(resourceURI);\n            if (resource.isEmpty())\n            {\n                logger.warn(\n                        \"StrategyID {}: application of default fetcher for {} returned empty Optional!\",\n                        this.getStrategyID(), resourceURI);\n                return Optional.empty();\n            }\n\n            final ByteArrayResource resourceBytes;\n            if (this.useExactResourceSize)\n            {\n                final long resourceLength = resource.get().length();\n                logger.trace(\"StrategyID {}: using exact resource length {}\", this.getStrategyID(),\n                        resourceLength);\n                resourceBytes = new ByteArrayResource(resourceLength);\n            }\n            else\n            {\n                logger.trace(\"StrategyID {}: using initial array size {}\", this.getStrategyID(),\n                        this.initialArraySize);\n                resourceBytes = new ByteArrayResource(this.initialArraySize);\n            }\n            resourceBytes.writeAndClose(resource.get().readBytesAndClose());\n            this.resourceCache.put(resourceUUID, resourceBytes);\n        }\n        logger.trace(\"StrategyID {}: returning cached resource {} from byte array keyed on UUID {}\",\n                this.getStrategyID(), resourceURI, resourceUUID);\n\n        return Optional.of(this.resourceCache.get(resourceUUID));\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"ByteArrayCachingStrategy\";\n    }\n\n    @Override\n    public void invalidate()\n    {\n        this.resourceCache.clear();\n    }\n\n    @Override\n    public void invalidate(final URI resourceURI)\n    {\n        final UUID resourceUUID = this.getUUIDForResourceURI(resourceURI);\n        this.resourceCache.remove(resourceUUID);\n    }\n\n    /**\n     * Use the exact resource size of the byte arrays of the cache. This may cause performance\n     * degradation on cache misses, since some resources do not store their length as metadata.\n     *\n     * @return the configured {@link ByteArrayCachingStrategy}\n     */\n    public ByteArrayCachingStrategy useExactResourceSize()\n    {\n        this.useExactResourceSize = true;\n        return this;\n    }\n\n    /**\n     * Set an initial array size for the byte arrays of the cache.\n     *\n     * @param initialSize\n     *            the initial size\n     * @return the configured {@link ByteArrayCachingStrategy}\n     */\n    public ByteArrayCachingStrategy withInitialArraySize(final long initialSize)\n    {\n        this.initialArraySize = initialSize;\n        return this;\n    }\n\n    Map<UUID, ByteArrayResource> getResourceCache()\n    {\n        return this.resourceCache;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/strategies/CachingStrategy.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.net.URI;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * Interface definition for a caching strategy. A caching strategy must provide a method for\n * obtaining a resource based on a {@link URI}.\n *\n * @author lcram\n */\npublic interface CachingStrategy\n{\n    /**\n     * Attempt to fetch the resource located at the given URI.\n     *\n     * @param resourceURI\n     *            the {@link URI} if the desired {@link Resource}\n     * @param defaultFetcher\n     *            the initial {@link Function} used to populate the cache\n     * @return the {@link Resource} wrapped in an {@link Optional}\n     */\n    Optional<Resource> attemptFetch(URI resourceURI,\n            Function<URI, Optional<Resource>> defaultFetcher);\n\n    /**\n     * Get a strategy name for logging purposes.\n     *\n     * @return the strategy name\n     */\n    String getName();\n\n    /**\n     * Invalidate the {@link Resource} given by the {@link URI}. The contract of this method is the\n     * same as {@link CachingStrategy#invalidate()}, but only for the given {@link URI}.\n     *\n     * @param resourceURI\n     *            The {@link URI} of the {@link Resource} to invalidate\n     */\n    default void invalidate(final URI resourceURI)\n    {\n\n    }\n\n    /**\n     * Invalidate the contents of this strategy. The contract of this method is the following: a\n     * {@link URI} that produces a cache hit on an {@link CachingStrategy#attemptFetch} before an\n     * {@link CachingStrategy#invalidate} call must produce a cache miss on the first\n     * {@link CachingStrategy#attemptFetch} after an {@link CachingStrategy#invalidate} call.\n     */\n    default void invalidate()\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/strategies/GlobalNamespaceCachingStrategy.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.net.URI;\nimport java.nio.file.FileSystem;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * A special case of {@link NamespaceCachingStrategy} that uses a predefined, global namespace. This\n * means that all instances of {@link GlobalNamespaceCachingStrategy} will share the same underlying\n * contents. From this it follows that fetches and invalidates will manifest across instances. To\n * prevent concurrency issues, the global namespace is locked using a class lock. It is worth noting\n * that this still does not protect the namespace's integrity from multiple JVMs running concurrent\n * {@link GlobalNamespaceCachingStrategy} objects.\n *\n * @author lcram\n */\npublic class GlobalNamespaceCachingStrategy extends NamespaceCachingStrategy\n{\n    /*\n     * This is a random SHA256 hash. Collisions with this namespace are astronomically unlikely (due\n     * to the 256 bits of entropy).\n     */\n    private static final String GLOBAL_NAMESPACE = \"3707740A818531237051A0F1E086CF701E2C38483675FCD1AAD8F5C5C33F19BC\";\n\n    public GlobalNamespaceCachingStrategy(final FileSystem fileSystem)\n    {\n        super(GLOBAL_NAMESPACE, fileSystem);\n    }\n\n    public GlobalNamespaceCachingStrategy()\n    {\n        super(GLOBAL_NAMESPACE);\n    }\n\n    @Override\n    public Optional<Resource> attemptFetch(final URI resourceURI,\n            final Function<URI, Optional<Resource>> defaultFetcher)\n    {\n        synchronized (GlobalNamespaceCachingStrategy.class)\n        {\n            return super.attemptFetch(resourceURI, defaultFetcher);\n        }\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"GlobalNamespaceCachingStrategy\";\n    }\n\n    @Override\n    public void invalidate()\n    {\n        synchronized (GlobalNamespaceCachingStrategy.class)\n        {\n            super.invalidate();\n        }\n    }\n\n    @Override\n    public void invalidate(final URI resourceURI)\n    {\n        synchronized (GlobalNamespaceCachingStrategy.class)\n        {\n            super.invalidate(resourceURI);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/strategies/NamespaceCachingStrategy.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.net.URI;\nimport java.nio.file.FileAlreadyExistsException;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.AbstractResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.caching.ConcurrentResourceCache;\nimport org.openstreetmap.atlas.utilities.runtime.Retry;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Caching strategy that attempts to cache a {@link Resource} within a user-defined namespace at the\n * standard system temporary location. It should be noted that this strategy has no inherent\n * concurrency safety. Since the namespaces are implemented as directories in the underlying\n * filesystem, two {@link NamespaceCachingStrategy} objects with the same namespace can possibly\n * step on each other's toes if used improperly. It is up to the users of the strategy to prevent\n * concurrent access to {@link NamespaceCachingStrategy} objects that share a namespace. One way to\n * ensure concurrency safety is to carefully associate a given namespace (and its\n * {@link NamespaceCachingStrategy}) with exactly one {@link ConcurrentResourceCache} object\n * throughout your code, and stick to this restriction consistently.\n *\n * @author lcram\n */\npublic class NamespaceCachingStrategy extends AbstractCachingStrategy\n{\n    private static final Logger logger = LoggerFactory.getLogger(NamespaceCachingStrategy.class);\n    private static final String FILE_EXTENSION_DOT = \".\";\n    private static final String PROPERTY_LOCAL_TEMPORARY_DIRECTORY = \"java.io.tmpdir\";\n    private static final String TEMPORARY_DIRECTORY_STRING = System\n            .getProperty(PROPERTY_LOCAL_TEMPORARY_DIRECTORY);\n    private static final int RETRY_NUMBER = 5;\n    private static final Retry RETRY = new Retry(RETRY_NUMBER, Duration.ONE_SECOND)\n            .withQuadratic(true);\n\n    private final String namespace;\n    private boolean preserveFileExtension;\n    private final FileSystem fileSystem;\n\n    public NamespaceCachingStrategy(final String namespace)\n    {\n        this(namespace, FileSystems.getDefault());\n    }\n\n    public NamespaceCachingStrategy(final String namespace, final FileSystem fileSystem)\n    {\n        super();\n        if (namespace.contains(\"/\") || namespace.contains(\"\\\\\"))\n        {\n            throw new IllegalArgumentException(\n                    \"The namespace cannot contain characters '\\\\' or '/'\");\n        }\n        this.namespace = this.getName() + \"_\" + namespace + \"_\"\n                + UUID.nameUUIDFromBytes(namespace.getBytes()).toString();\n        this.preserveFileExtension = true;\n        this.fileSystem = fileSystem;\n    }\n\n    @Override\n    public Optional<Resource> attemptFetch(final URI resourceURI,\n            final Function<URI, Optional<Resource>> defaultFetcher)\n    {\n        if (TEMPORARY_DIRECTORY_STRING == null)\n        {\n            logger.error(\"StrategyID {}: failed to read property {}, skipping cache fetch...\",\n                    this.getStrategyID(), PROPERTY_LOCAL_TEMPORARY_DIRECTORY);\n            return Optional.empty();\n        }\n\n        if (resourceURI == null)\n        {\n            logger.warn(\"StrategyID {}: resourceURI was null, skipping cache fetch...\",\n                    this.getStrategyID());\n            return Optional.empty();\n        }\n\n        final File cachedFile = getCachedFile(resourceURI);\n        attemptToCacheFileLocally(cachedFile, defaultFetcher, resourceURI);\n\n        if (cachedFile.exists())\n        {\n            logger.trace(\"StrategyID {}: returning local copy of resource {}\", this.getStrategyID(),\n                    resourceURI);\n            return Optional.of(cachedFile);\n        }\n\n        // If we got here, something went wrong in attemptToCacheFileLocally().\n        logger.warn(\"StrategyID {}: could not find local copy of resource {}\", this.getStrategyID(),\n                resourceURI);\n        return Optional.empty();\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"NamespaceCachingStrategy\";\n    }\n\n    @Override\n    public void invalidate()\n    {\n        final Path storageDirectory = this.getStorageDirectory();\n        try\n        {\n            new File(storageDirectory.toString(), this.fileSystem).deleteRecursively();\n        }\n        catch (final Exception exception)\n        {\n            logger.warn(\"StrategyID {}: invalidate failed due to {}\", this.getStrategyID(),\n                    exception.getClass().getName(), exception);\n        }\n    }\n\n    @Override\n    public void invalidate(final URI resourceURI)\n    {\n        try\n        {\n            getCachedFile(resourceURI).delete();\n        }\n        catch (final Exception exception)\n        {\n            logger.warn(\"StrategyID {}: invalidate of resource {} failed due to {}\",\n                    this.getStrategyID(), resourceURI, exception.getClass().getName(), exception);\n        }\n    }\n\n    /**\n     * Preserve the file extension of the cached URI when saving it as a file to the temporary\n     * location. For example, if the URI of the resource was \"hdfs://foo/bar/baz.txt\", then after\n     * computing the hash of the URI, {@link NamespaceCachingStrategy} will append a '.txt'\n     * extension to the filename. This is useful for e.g. in cases where resource loading code may\n     * be looking for specific file extensions in order to decide between various load strategies.\n     * \n     * @param preserveFileExtension\n     *            if true, preserve the original extension\n     * @return this instance for chaining\n     */\n    public NamespaceCachingStrategy withFileExtensionPreservation(\n            final boolean preserveFileExtension)\n    {\n        this.preserveFileExtension = preserveFileExtension;\n        return this;\n    }\n\n    protected void validateLocalFile(final File localFile)\n    {\n        // Do nothing here, leave to extensions to decide.\n    }\n\n    /*\n     * Package-private for unit testing\n     */\n    Path getStorageDirectory()\n    {\n        return this.fileSystem.getPath(TEMPORARY_DIRECTORY_STRING, this.namespace);\n    }\n\n    private void attemptToCacheFileLocally(final File cachedFile,\n            final Function<URI, Optional<Resource>> defaultFetcher, final URI resourceURI)\n    {\n        if (!cachedFile.exists())\n        {\n            logger.trace(\"StrategyID {}: attempting to cache resource {} in temporary file {}\",\n                    this.getStrategyID(), resourceURI, cachedFile);\n\n            final Optional<Resource> resourceFromDefaultFetcher = defaultFetcher.apply(resourceURI);\n            if (resourceFromDefaultFetcher.isEmpty())\n            {\n                logger.warn(\n                        \"StrategyID {}: application of default fetcher for {} returned empty Optional!\",\n                        this.getStrategyID(), resourceURI);\n                return;\n            }\n\n            final File temporaryLocalFile = File.temporary(this.fileSystem);\n            RETRY.run(() ->\n            {\n                try\n                {\n                    /*\n                     * We have to explicitly set the decompressor here. Why? Because if the resource\n                     * ends with a '.gz' extension, the 'copyTo' method will apply GZIP\n                     * decompression to it. The problem? When the user goes to fetch the contents of\n                     * the cached copy, it will still have the '.gz' extension but it will now be\n                     * decompressed. So our automatic decompression code will run on an uncompressed\n                     * file! This will cause the contents fetch to fail since Java's GZIPInputStream\n                     * won't be able to find the GZIP magic number!\n                     */\n                    final AbstractResource abstractResource = (AbstractResource) resourceFromDefaultFetcher\n                            .get();\n                    abstractResource.setDecompressor(Decompressor.NONE);\n                    abstractResource.copyTo(temporaryLocalFile);\n                    validateLocalFile(temporaryLocalFile);\n                }\n                catch (final Exception exception)\n                {\n                    throw new CoreException(\n                            \"StrategyID {}: something went wrong copying {} to temporary local file {}\",\n                            this.getStrategyID(), resourceFromDefaultFetcher, temporaryLocalFile,\n                            exception);\n                }\n            });\n\n            // now that we have pulled down the file to a unique temporary location, attempt to\n            // atomically move it to the cache after re-checking for existence\n            if (!cachedFile.exists())\n            {\n                try\n                {\n                    final Path temporaryLocalFilePath = this.fileSystem\n                            .getPath(temporaryLocalFile.getPathString());\n                    final Path cachedFilePath = this.fileSystem.getPath(cachedFile.getPathString());\n                    Files.move(temporaryLocalFilePath, cachedFilePath,\n                            StandardCopyOption.ATOMIC_MOVE);\n                    validateLocalFile(cachedFile);\n                }\n                catch (final FileAlreadyExistsException exception)\n                {\n                    logger.trace(\"StrategyID {}: file {} is already cached\", this.getStrategyID(),\n                            cachedFile);\n                }\n                catch (final Exception exception)\n                {\n                    throw new CoreException(\"StrategyID {}: something went wrong moving {} to {}\",\n                            this.getStrategyID(), temporaryLocalFile, cachedFile, exception);\n                }\n            }\n        }\n    }\n\n    private File getCachedFile(final URI resourceURI)\n    {\n        final Path storageDirectory = getStorageDirectory();\n        final Optional<String> resourceExtensionOptional = getFileExtensionFromURI(resourceURI);\n        final String cachedFileName;\n        cachedFileName = resourceExtensionOptional\n                .map(extension -> this.getUUIDForResourceURI(resourceURI).toString()\n                        + FILE_EXTENSION_DOT + extension)\n                .orElseGet(() -> this.getUUIDForResourceURI(resourceURI).toString());\n        final Path cachedFilePath = this.fileSystem.getPath(storageDirectory.toString(),\n                cachedFileName);\n\n        return new File(cachedFilePath.toString(), this.fileSystem);\n    }\n\n    private Optional<String> getFileExtensionFromURI(final URI resourceURI)\n    {\n        if (!this.preserveFileExtension)\n        {\n            return Optional.empty();\n        }\n\n        final String asciiString = resourceURI.toASCIIString();\n        final int lastIndexOfDot = asciiString.lastIndexOf(FILE_EXTENSION_DOT);\n\n        if (lastIndexOfDot < 0)\n        {\n            return Optional.empty();\n        }\n\n        final String extension = asciiString.substring(lastIndexOfDot + 1);\n        if (extension.isEmpty())\n        {\n            return Optional.empty();\n        }\n        else\n        {\n            return Optional.of(extension);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/caching/strategies/NoCachingStrategy.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.net.URI;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * Caching strategy that always produces a cache miss.\n *\n * @author lcram\n */\npublic class NoCachingStrategy extends AbstractCachingStrategy\n{\n    @Override\n    public Optional<Resource> attemptFetch(final URI resourceURI,\n            final Function<URI, Optional<Resource>> defaultFetcher)\n    {\n        return Optional.empty();\n    }\n\n    @Override\n    public String getName()\n    {\n        return \"NoCachingStrategy\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheck.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\nimport com.puppycrawl.tools.checkstyle.api.AbstractCheck;\nimport com.puppycrawl.tools.checkstyle.api.DetailAST;\nimport com.puppycrawl.tools.checkstyle.api.TokenTypes;\n\n/**\n * Check for specified ordering of elements in a source file.\n * \n * @author matthieun\n */\npublic class ArrangementCheck extends AbstractCheck\n{\n    /**\n     * @author matthieun\n     */\n    private enum Type\n    {\n        INTERFACE(TokenTypes.INTERFACE_DEF),\n        ENUM(TokenTypes.ENUM_DEF),\n        CLASS(TokenTypes.CLASS_DEF),\n        FIELD(TokenTypes.VARIABLE_DEF),\n        STATIC_INITIALIZER_BLOCK(TokenTypes.STATIC_INIT),\n        INITIALIZER_BLOCK(TokenTypes.INSTANCE_INIT),\n        METHOD(TokenTypes.METHOD_DEF),\n        CONSTRUCTOR(TokenTypes.CTOR_DEF);\n\n        private final int tokenType;\n\n        public static Type forName(final String name)\n        {\n            for (final Type type : Type.values())\n            {\n                if (type.name().equalsIgnoreCase(name))\n                {\n                    return type;\n                }\n            }\n            throw new CoreException(\"Invalid name {}\", name);\n        }\n\n        public static Type forType(final int tokenType)\n        {\n            for (final Type type : Type.values())\n            {\n                if (type.tokenType == tokenType)\n                {\n                    return type;\n                }\n            }\n            throw new CoreException(\"Invalid token type {}\", tokenType);\n        }\n\n        Type(final int tokenType)\n        {\n            this.tokenType = tokenType;\n        }\n    }\n\n    /**\n     * @author matthieun\n     */\n    private enum Visibility\n    {\n        PUBLIC,\n        PROTECTED,\n        PACKAGE_PRIVATE,\n        PRIVATE;\n\n        public static Visibility forName(final String name)\n        {\n            for (final Visibility visibility : Visibility.values())\n            {\n                if (visibility.name().equalsIgnoreCase(name))\n                {\n                    return visibility;\n                }\n            }\n            if (name.isEmpty())\n            {\n                return Visibility.PACKAGE_PRIVATE;\n            }\n            throw new CoreException(\"Invalid name \\\"{}\\\"\", name);\n        }\n    }\n\n    /**\n     * @author matthieun\n     */\n    private class ObjectRawType\n    {\n        private final Visibility visibility;\n        private final Type type;\n        private final boolean isStatic;\n\n        ObjectRawType(final Visibility visibility, final Type type, final boolean isStatic)\n        {\n            this.visibility = visibility;\n            this.type = type;\n            this.isStatic = isStatic;\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            if (other == null || getClass() != other.getClass())\n            {\n                return false;\n            }\n            final ObjectRawType that = (ObjectRawType) other;\n            return isStatic() == that.isStatic() && getVisibility() == that.getVisibility()\n                    && getType() == that.getType();\n        }\n\n        public Type getType()\n        {\n            return this.type;\n        }\n\n        public Visibility getVisibility()\n        {\n            return this.visibility;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return Objects.hash(getVisibility(), getType(), isStatic());\n        }\n\n        public boolean isStatic()\n        {\n            return this.isStatic;\n        }\n    }\n\n    /**\n     * @author matthieun\n     */\n    private class ObjectType implements Comparable<ObjectType>\n    {\n        private final ObjectRawType objectRawType;\n        private final String name;\n\n        ObjectType(final DetailAST object)\n        {\n            final Type type = Type.forType(object.getType());\n            final Visibility visibility;\n            final boolean isStatic;\n            final Optional<DetailAST> ident = findFirstToken(object, TokenTypes.IDENT);\n            if (ident.isPresent())\n            {\n                this.name = ident.get().getText();\n            }\n            else if (Type.INITIALIZER_BLOCK == type || Type.STATIC_INITIALIZER_BLOCK == type)\n            {\n                this.name = type.name().toLowerCase();\n            }\n            else\n            {\n                this.name = \"\";\n            }\n            final Optional<DetailAST> modifiers = findFirstToken(object, TokenTypes.MODIFIERS);\n            if (modifiers.isPresent())\n            {\n                if (findFirstToken(modifiers.get(), TokenTypes.LITERAL_PRIVATE).isPresent())\n                {\n                    visibility = Visibility.PRIVATE;\n                }\n                else if (findFirstToken(modifiers.get(), TokenTypes.LITERAL_PROTECTED).isPresent())\n                {\n                    visibility = Visibility.PROTECTED;\n                }\n                else if (findFirstToken(modifiers.get(), TokenTypes.LITERAL_PUBLIC).isPresent())\n                {\n                    visibility = Visibility.PUBLIC;\n                }\n                else\n                {\n                    visibility = Visibility.PACKAGE_PRIVATE;\n                }\n                isStatic = findFirstToken(modifiers.get(), TokenTypes.LITERAL_STATIC).isPresent();\n            }\n            else\n            {\n                visibility = Visibility.PACKAGE_PRIVATE;\n                isStatic = false;\n            }\n            this.objectRawType = new ObjectRawType(visibility, type, isStatic);\n        }\n\n        @Override\n        public int compareTo(final ObjectType that)\n        {\n            return ArrangementCheck.this.getObjectTypeComparator().compare(this, that);\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            if (other == null || getClass() != other.getClass())\n            {\n                return false;\n            }\n            final ObjectType that = (ObjectType) other;\n            return getObjectRawType().equals(that.getObjectRawType())\n                    && getName().equals(that.getName());\n        }\n\n        public String getName()\n        {\n            return this.name;\n        }\n\n        public ObjectRawType getObjectRawType()\n        {\n            return this.objectRawType;\n        }\n\n        public Type getType()\n        {\n            return this.objectRawType.getType();\n        }\n\n        public Visibility getVisibility()\n        {\n            return this.objectRawType.getVisibility();\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return Objects.hash(getVisibility(), getType(), isStatic(), getName());\n        }\n\n        public boolean isStatic()\n        {\n            return this.objectRawType.isStatic();\n        }\n\n        @Override\n        public String toString()\n        {\n            return \"ObjectType{\" + \"visibility=\" + this.getVisibility() + \", type=\" + this.getType()\n                    + \", isStatic=\" + this.isStatic() + \", name='\" + this.name + '\\'' + '}';\n        }\n    }\n\n    /**\n     * @author matthieun\n     */\n    private class ObjectTypeComparator implements Comparator<ObjectType>\n    {\n        private static final int ARRANGEMENT_LINE_SIZE = 3;\n        private static final String ARRANGEMENT_FILE = \"arrangement.txt\";\n        private final Map<ObjectRawType, Integer> rawTypeToOrderIndex;\n\n        ObjectTypeComparator()\n        {\n            this(new InputStreamResource(\n                    () -> ArrangementCheck.class.getResourceAsStream(ARRANGEMENT_FILE)));\n        }\n\n        ObjectTypeComparator(final Resource ordering)\n        {\n            int index = 0;\n            try\n            {\n                this.rawTypeToOrderIndex = new HashMap<>();\n                for (final String line : ordering.lines())\n                {\n                    final StringList split = StringList.split(line, \",\");\n                    if (line.isEmpty() || line.startsWith(\"#\"))\n                    {\n                        index++;\n                        continue;\n                    }\n                    if (split.size() != ARRANGEMENT_LINE_SIZE)\n                    {\n                        throw new CoreException(\"Malformed line: \\\"{}\\\"\", line);\n                    }\n                    final Type type = Type.forName(split.get(0));\n                    final Visibility visibility = Visibility.forName(split.get(1));\n                    final boolean isStatic = \"static\".equalsIgnoreCase(split.get(2));\n                    this.rawTypeToOrderIndex.put(new ObjectRawType(visibility, type, isStatic),\n                            index);\n                    index++;\n                }\n            }\n            catch (final Exception e)\n            {\n                throw new CoreException(\n                        \"Unable to parse file defining arrangement (was at line {}): {}\", index + 1,\n                        ArrangementCheck.class.getResource(ARRANGEMENT_FILE).getPath(), e);\n            }\n        }\n\n        @Override\n        public int compare(final ObjectType left, final ObjectType right)\n        {\n            final int difference = this.rawTypeToOrderIndex.get(left.getObjectRawType())\n                    - this.rawTypeToOrderIndex.get(right.getObjectRawType());\n            if (difference == 0 && Type.FIELD != left.getObjectRawType().getType())\n            {\n                final String leftName = left.getName();\n                final String rightName = right.getName();\n                return leftName.compareTo(rightName);\n            }\n            else\n            {\n                return difference;\n            }\n        }\n\n        public boolean isComparable(final ObjectType object)\n        {\n            return this.rawTypeToOrderIndex.containsKey(object.getObjectRawType())\n                    && !\"serialVersionUID\".equals(object.getName());\n        }\n    }\n\n    private ObjectTypeComparator objectTypeComparator;\n    private String arrangementDefinition = \"\";\n\n    public static Optional<DetailAST> findFirstToken(final DetailAST source, final int tokenType)\n    {\n        return Optional.ofNullable(source.findFirstToken(tokenType));\n    }\n\n    @Override\n    public int[] getAcceptableTokens()\n    {\n        return getDefaultTokens();\n    }\n\n    @Override\n    public int[] getDefaultTokens()\n    {\n        return new int[] { TokenTypes.CLASS_DEF, TokenTypes.ENUM_DEF, TokenTypes.METHOD_DEF,\n                TokenTypes.VARIABLE_DEF, TokenTypes.CTOR_DEF, TokenTypes.INTERFACE_DEF,\n                TokenTypes.STATIC_INIT, TokenTypes.INSTANCE_INIT };\n    }\n\n    @Override\n    public int[] getRequiredTokens()\n    {\n        return getDefaultTokens();\n    }\n\n    public void setArrangementDefinition(final String arrangementDefinition)\n    {\n        this.arrangementDefinition = arrangementDefinition;\n    }\n\n    @Override\n    public void visitToken(final DetailAST object)\n    {\n        final ObjectType left = new ObjectType(object);\n        if (!acceptedTokens().contains(object.getType())\n                || !this.getObjectTypeComparator().isComparable(left))\n        {\n            return;\n        }\n        Optional<DetailAST> nextSibling = Optional.ofNullable(object.getNextSibling());\n        while (nextSibling.isPresent() && (!acceptedTokens().contains(nextSibling.get().getType())\n                || !this.getObjectTypeComparator().isComparable(new ObjectType(nextSibling.get()))))\n        {\n            nextSibling = Optional.ofNullable(nextSibling.get().getNextSibling());\n        }\n        if (nextSibling.isPresent())\n        {\n            final DetailAST nextSiblingGet = nextSibling.get();\n            final ObjectType right = new ObjectType(nextSiblingGet);\n\n            if (left.compareTo(right) > 0)\n            {\n                String moreInfo = \"\";\n                if (!left.getName().isEmpty())\n                {\n                    moreInfo = moreInfo + \", \" + left.getName();\n                }\n                if (!right.getName().isEmpty())\n                {\n                    moreInfo = moreInfo + \", \" + right.getName();\n                }\n                log(nextSiblingGet.getLineNo(), \"Invalid order\" + moreInfo);\n            }\n        }\n    }\n\n    private Set<Integer> acceptedTokens()\n    {\n        final HashSet<Integer> result = new HashSet<>();\n        for (final int value : getAcceptableTokens())\n        {\n            result.add(value);\n        }\n        return result;\n    }\n\n    private ObjectTypeComparator getObjectTypeComparator()\n    {\n        if (this.objectTypeComparator == null)\n        {\n            if (this.arrangementDefinition.isEmpty())\n            {\n                this.objectTypeComparator = new ObjectTypeComparator();\n            }\n            else if (this.arrangementDefinition.startsWith(\"/\"))\n            {\n                this.objectTypeComparator = new ObjectTypeComparator(\n                        new File(this.arrangementDefinition));\n            }\n            else\n            {\n                throw new CoreException(\"Invalid configuration for ArrangementCheck: {}\",\n                        this.arrangementDefinition);\n            }\n        }\n        return this.objectTypeComparator;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/checkstyle/README.md",
    "content": "# ArrangementCheck\n\nThis is a Checkstyle plugin that checks for member ordering in Java source files.\n\n## Setup\n\nThe default setup takes the [default ordering definition](/src/main/resources/org/openstreetmap/atlas/utilities/checkstyle/arrangement.txt).\n\n```xml\n<module name=\"org.openstreetmap.atlas.utilities.checkstyle.ArrangementCheck\"/>\n```\n\nor with a specified ordering definition:\n\n```xml\n<module name=\"org.openstreetmap.atlas.utilities.checkstyle.ArrangementCheck\">\n    <property name=\"arrangementDefinition\" value=\"${config_loc}/arrangement.txt\" />\n</module>\n```\n\nTo use this with gradle, the project needs this dependency:\n\n```groovy\ndependencies\n{\n    checkstyle \"com.puppycrawl.tools:checkstyle:<checkstyle_version>\"\n    checkstyle \"org.openstreetmap.atlas:atlas:<atlas_version>\"\n}\n```\n\n**With Atlas 5.6.9+, the versions of checkstyle that are supported are up to `8.20`.**\n\nCheckstyle `8.21` and `8.22`+ contain [a breaking change](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.20...checkstyle-8.21#diff-2ecb5f79d7dcce6cfd4fa27ef2ec99d1R29) that is not yet supported by `ArrangementCheck`.\n\n## Ordering definition\n\nThe ordering can be defined in a text file which has the following format:\n\n```\ntype,visibility,static/non_static\n```\n\nwith each line in order of importance.\n\nAll the combinations that are not matched in that file will be ignored during processing. For example, a file like this:\n\n```\nmethod,public,non_static\nmethod,protected,non_static\n```\n\nwill only check that non-static public methods come before non-static protected methods, and it will ignore everything else.\n\n### Alphabetical order\n\nBy default all types that are comparable according to the ordering file will also have to be alphabetically ordered. This is mandatory, and not configurable yet.\n\n## Examples\n\nThe following examples are based on the [default ordering definition](/src/main/resources/org/openstreetmap/atlas/utilities/checkstyle/arrangement.txt).\n\n### Error: Field vs. Method\n\n```java\npublic class MyClass\n{\n    public void method()\n    {\n    }\n\n    private boolean field;\n}\n```\n\n### Error: Visibility\n\n```java\npublic class MyClass\n{\n    void methodA()\n    {\n    }\n\n    public void methodB()\n    {\n    }\n}\n```\n\n### Error: Name\n\n```java\npublic class MyClass\n{\n    public void methodB()\n    {\n    }\n\n    public void methodA()\n    {\n    }\n}\n```\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/AbstractHDFSOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperation;\n\n/**\n * Brings all of the common argument values for remote HDFS operations into a single superclass\n * along with permitting custom hadoop configuration settings\n *\n * @author cstaylor\n */\npublic abstract class AbstractHDFSOperation extends AbstractOperation\n{\n    private Optional<String> customConfiguration;\n\n    private Optional<String> customHostname;\n\n    protected AbstractHDFSOperation()\n    {\n        this.customConfiguration = Optional.empty();\n        this.customHostname = Optional.empty();\n    }\n\n    public AbstractHDFSOperation withConfiguration(final String configuration)\n    {\n        this.customConfiguration = Optional.ofNullable(configuration);\n        return this;\n    }\n\n    public AbstractHDFSOperation withCustomHostname(final String customHostname)\n    {\n        this.customHostname = Optional.ofNullable(customHostname);\n        return this;\n    }\n\n    protected String preparePath(final String input)\n    {\n        if (input == null || input.startsWith(\"hdfs://\"))\n        {\n            return input;\n        }\n        if (this.customHostname.isPresent())\n        {\n            return String.format(\"hdfs://%s%s\", this.customHostname.get(), input);\n        }\n        return input;\n    }\n\n    protected SSHOperation prepareSSH()\n    {\n        final SSHOperation returnValue = this.ssh();\n        returnValue.addArgs(\"hdfs\");\n        this.customConfiguration.ifPresent(configuration ->\n        {\n            returnValue.addArgs(\"--config\", configuration);\n        });\n        returnValue.addArgs(\"dfs\");\n        return returnValue;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/AbstractOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperation;\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Helpful base class containing some shared methods used by the various remote ssh commands\n *\n * @author cstaylor\n */\npublic abstract class AbstractOperation implements Operation\n{\n    private final SSHOperation ssh;\n\n    protected AbstractOperation()\n    {\n        this.ssh = new SSHOperation();\n    }\n\n    @Override\n    public AbstractOperation asUser(final String username)\n    {\n        this.ssh.asUser(username);\n        return this;\n    }\n\n    @Override\n    public AbstractOperation onHost(final String hostname)\n    {\n        this.ssh.onHost(hostname);\n        return this;\n    }\n\n    @Override\n    public AbstractOperation onPort(final int portNumber)\n    {\n        this.ssh.onPort(portNumber);\n        return this;\n    }\n\n    protected String getHost()\n    {\n        return this.ssh.getHost();\n    }\n\n    protected String getUser()\n    {\n        return this.ssh.getUser();\n    }\n\n    protected SSHOperationResults handleResults(final SSHOperationResults results)\n    {\n        // We can do logging here in a central place for all of our commands\n        return results;\n    }\n\n    protected SSHOperation ssh()\n    {\n        return this.ssh;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/CheckIfFileExistsOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * This command checks if a file exists on a remote server via SSH\n *\n * @author cstaylor\n */\npublic class CheckIfFileExistsOperation extends AbstractOperation\n{\n    @Override\n    public CheckIfFileExistsOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public boolean exists(final Path remotePath) throws InterruptedException, IOException\n    {\n        if (remotePath == null)\n        {\n            throw new IllegalArgumentException(\"remotePath can't be null\");\n        }\n        this.ssh().addArgs(\"stat\", remotePath.toString());\n        final SSHOperationResults results = handleResults(this.ssh().execute());\n        return !results.getOutput().contains(\"No such file or directory\");\n    }\n\n    @Override\n    public CheckIfFileExistsOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public CheckIfFileExistsOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/DeepLSOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Uses find on a remote system to find all files from a search path and returns them as a list of\n * Path objects\n *\n * @author cstaylor\n */\npublic class DeepLSOperation extends AbstractOperation\n{\n    @Override\n    public DeepLSOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public List<Path> list(final Path remotePath) throws InterruptedException, IOException\n    {\n        if (remotePath == null)\n        {\n            throw new IllegalArgumentException(\"remotePath can't be null\");\n        }\n        this.ssh().addArgs(\"find\", remotePath.toString(), \"-type\", \"f\");\n        final SSHOperationResults results = handleResults(this.ssh().execute());\n        if (results.getReturnValue() == 1)\n        {\n            throw new CoreException(\"Error: {}@{}:{} doesn't exist\", getUser(), getHost(),\n                    remotePath);\n        }\n        return Arrays.asList(results.getOutput().split(\"\\n\")).stream()\n                .map(child -> remotePath.resolve(child)).collect(Collectors.toList());\n    }\n\n    @Override\n    public DeepLSOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public DeepLSOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/HDFSCatOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Implements the HDFS cat command (dump contents of files in HDFS to stdout) over SSH\n *\n * @author cstaylor\n */\npublic class HDFSCatOperation extends AbstractHDFSOperation\n{\n    @Override\n    public HDFSCatOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public SSHOperationResults cat(final String... paths) throws InterruptedException, IOException\n    {\n        if (paths.length == 0)\n        {\n            throw new IllegalArgumentException(\"source can't be null\");\n        }\n\n        final String pathsAsString = Arrays.asList(paths).stream().map(this::preparePath)\n                .collect(Collectors.joining(\" \"));\n\n        prepareSSH().addArgs(\"-cat\", pathsAsString);\n        return handleResults(this.ssh().execute());\n    }\n\n    @Override\n    public HDFSCatOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public HDFSCatOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n\n    @Override\n    public HDFSCatOperation withConfiguration(final String configuration)\n    {\n        super.withConfiguration(configuration);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/HDFSCheckIfFileExistsOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Implements the HDFS stat command so we can check if a file exists in a remote HDFS cluster\n *\n * @author cstaylor\n */\npublic class HDFSCheckIfFileExistsOperation extends AbstractHDFSOperation\n{\n    @Override\n    public HDFSCheckIfFileExistsOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public boolean exists(final Path remotePath) throws InterruptedException, IOException\n    {\n        if (remotePath == null)\n        {\n            throw new IllegalArgumentException(\"remotePath can't be null\");\n        }\n        prepareSSH().addArgs(\"-stat\", preparePath(remotePath.toString()));\n        final SSHOperationResults results = handleResults(this.ssh().execute());\n        return results.getReturnValue() == STANDARD_SUCCESS_CODE;\n    }\n\n    @Override\n    public HDFSCheckIfFileExistsOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public HDFSCheckIfFileExistsOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n\n    @Override\n    public HDFSCheckIfFileExistsOperation withConfiguration(final String configuration)\n    {\n        super.withConfiguration(configuration);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/HDFSCopyOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Implements the HDFS copy command (copy files from one area of HDFS to another) over SSH\n *\n * @author cstaylor\n */\npublic class HDFSCopyOperation extends AbstractHDFSOperation\n{\n    @Override\n    public HDFSCopyOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public SSHOperationResults copy(final Path source, final Path destination)\n            throws InterruptedException, IOException\n    {\n        if (source == null)\n        {\n            throw new IllegalArgumentException(\"source can't be null\");\n        }\n        if (destination == null)\n        {\n            throw new IllegalArgumentException(\"destination can't be null\");\n        }\n        prepareSSH().addArgs(\"-cp\", preparePath(source.toString()),\n                preparePath(destination.toString()));\n\n        return handleResults(this.ssh().execute());\n    }\n\n    @Override\n    public HDFSCopyOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public HDFSCopyOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n\n    @Override\n    public HDFSCopyOperation withConfiguration(final String configuration)\n    {\n        super.withConfiguration(configuration);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/HDFSLSOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Implements the HDFS ls command over SSH and returns the file names, not the absolute paths\n *\n * @author cstaylor\n */\npublic class HDFSLSOperation extends AbstractHDFSOperation\n{\n    @Override\n    public HDFSLSOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public Stream<String> list(final Path remotePath) throws InterruptedException, IOException\n    {\n        if (remotePath == null)\n        {\n            throw new IllegalArgumentException(\"remotePath can't be null\");\n        }\n        prepareSSH().addArgs(\"-ls\", preparePath(remotePath.toString()));\n        final SSHOperationResults results = handleResults(this.ssh().execute());\n        return Stream.of(results.getOutput().split(\"\\n\")).filter(i -> i.indexOf('/') != -1)\n                .map(line ->\n                {\n                    final String[] pieces = line.split(\" \");\n                    return pieces[pieces.length - 1];\n                });\n    }\n\n    @Override\n    public HDFSLSOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public HDFSLSOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n\n    @Override\n    public HDFSLSOperation withConfiguration(final String configuration)\n    {\n        super.withConfiguration(configuration);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/HDFSMkdirOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperation;\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Implements the HDFS mkdir command over SSH\n *\n * @author cstaylor\n */\npublic class HDFSMkdirOperation extends AbstractHDFSOperation\n{\n    @Override\n    public HDFSMkdirOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public boolean mkdir(final Path... remotePaths) throws InterruptedException, IOException\n    {\n        if (remotePaths.length == 0)\n        {\n            throw new IllegalArgumentException(\"Need at least one remote path\");\n        }\n        final SSHOperation operation = prepareSSH().addArgs(\"-mkdir\", \"-p\");\n\n        Stream.of(remotePaths).map(Path::toString).map(this::preparePath)\n                .forEach(operation::addArgs);\n\n        final SSHOperationResults results = handleResults(this.ssh().execute());\n        return results.getReturnValue() == STANDARD_SUCCESS_CODE;\n    }\n\n    @Override\n    public HDFSMkdirOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public HDFSMkdirOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n\n    @Override\n    public HDFSMkdirOperation withConfiguration(final String configuration)\n    {\n        super.withConfiguration(configuration);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/HDFSPutOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Implements the HDFS put command (copy a file from the local filesystem into HDFS) over SSH\n *\n * @author cstaylor\n */\npublic class HDFSPutOperation extends AbstractHDFSOperation\n{\n    @Override\n    public HDFSPutOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    @Override\n    public HDFSPutOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public HDFSPutOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n\n    public SSHOperationResults put(final Path source, final Path destination)\n            throws InterruptedException, IOException\n    {\n        if (source == null)\n        {\n            throw new IllegalArgumentException(\"source can't be null\");\n        }\n        if (destination == null)\n        {\n            throw new IllegalArgumentException(\"destination can't be null\");\n        }\n        prepareSSH().addArgs(\"-put\", preparePath(source.toString()),\n                preparePath(destination.toString()));\n        return handleResults(this.ssh().execute());\n    }\n\n    @Override\n    public HDFSPutOperation withConfiguration(final String configuration)\n    {\n        super.withConfiguration(configuration);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/LSOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Returns the list of filenames from a directory on a remote server over SSH\n *\n * @author cstaylor\n */\npublic class LSOperation extends AbstractOperation\n{\n    @Override\n    public LSOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public List<String> list(final Path remotePath) throws InterruptedException, IOException\n    {\n        if (remotePath == null)\n        {\n            throw new IllegalArgumentException(\"remotePath can't be null\");\n        }\n        this.ssh().addArgs(\"ls\", \"-1\", remotePath.toString());\n        final SSHOperationResults results = handleResults(this.ssh().execute());\n        return Arrays.asList(results.getOutput().split(\"\\n\"));\n    }\n\n    @Override\n    public LSOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public LSOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/MkdirOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * Creates one or more directories on a remote system over SSH\n *\n * @author cstaylor\n */\npublic class MkdirOperation extends AbstractOperation\n{\n    private Optional<Consumer<Tuple<Path, String>>> errorHandler = Optional.empty();\n\n    @Override\n    public MkdirOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    public boolean mkdir(final Path... remotePaths) throws InterruptedException, IOException\n    {\n        if (remotePaths.length == 0)\n        {\n            throw new IllegalArgumentException(\"Need at least one remote path\");\n        }\n        this.ssh().addArgs(\"mkdir\", \"-p\");\n        Stream.of(remotePaths).map(Path::toString).forEach(item -> this.ssh().addArgs(item));\n        final SSHOperationResults results = handleResults(this.ssh().execute());\n        if (results.getReturnValue() != STANDARD_SUCCESS_CODE)\n        {\n            this.errorHandler.ifPresent(handler ->\n            {\n                Stream.of(results.getOutput().split(\"\\n\")).map(line -> StringList.split(line, \":\"))\n                        .forEach(stringList ->\n                        {\n                            handler.accept(\n                                    new Tuple<>(Paths.get(stringList.get(1)), stringList.get(2)));\n                        });\n            });\n        }\n        return results.getReturnValue() == STANDARD_SUCCESS_CODE;\n    }\n\n    @Override\n    public MkdirOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public MkdirOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n\n    public MkdirOperation withErrorHandler(final Consumer<Tuple<Path, String>> errorHandler)\n    {\n        this.errorHandler = Optional.ofNullable(errorHandler);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/Operation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\n/**\n * All commands must have a remote user on a particular host\n *\n * @author cstaylor\n */\npublic interface Operation\n{\n    int STANDARD_SUCCESS_CODE = 0;\n\n    /**\n     * @param username\n     *            the SSH username for connecting with the remote server\n     * @return fluent interface returns this\n     */\n    Operation asUser(String username);\n\n    /**\n     * @param hostname\n     *            the hostname or IP of the remote server\n     * @return fluent interface returns this\n     */\n    Operation onHost(String hostname);\n\n    /**\n     * @param portNumber\n     *            the non-standard SSH port we should use\n     * @return fluent interface returns this\n     */\n    Operation onPort(int portNumber);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/RMDirOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.openstreetmap.atlas.utilities.cli.operations.base.SSHOperationResults;\n\n/**\n * Removes a directory from a remote server over SSH.\n * <p>\n * See that rm -rf? Be very careful...\n *\n * @author cstaylor\n */\npublic class RMDirOperation extends AbstractOperation\n{\n    @Override\n    public RMDirOperation asUser(final String username)\n    {\n        super.asUser(username);\n        return this;\n    }\n\n    @Override\n    public RMDirOperation onHost(final String host)\n    {\n        super.onHost(host);\n        return this;\n    }\n\n    @Override\n    public RMDirOperation onPort(final int portNumber)\n    {\n        super.onPort(portNumber);\n        return this;\n    }\n\n    public boolean rmdir(final Path remotePath) throws InterruptedException, IOException\n    {\n        if (remotePath == null)\n        {\n            throw new IllegalArgumentException(\"remotePath can't be null\");\n        }\n\n        /**\n         * Not the best sanity check in the world.\n         */\n        if (remotePath.toString().contains(\"*\"))\n        {\n            throw new IllegalArgumentException(\"Please, be careful with the asterisks\");\n        }\n        this.ssh().addArgs(\"rm\", \"-rf\", remotePath.toString());\n        final SSHOperationResults results = handleResults(this.ssh().execute());\n        return results.getReturnValue() == STANDARD_SUCCESS_CODE;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/base/AvailableSocketFinder.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations.base;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Simple utility class that will try and find a socket to bind to and return that port number after\n * the socket has been closed\n *\n * @author cstaylor\n */\nfinal class AvailableSocketFinder\n{\n    static int takePort()\n    {\n        Integer returnValue = null;\n        try\n        {\n            final ServerSocket socket = new ServerSocket(0);\n            returnValue = socket.getLocalPort();\n            socket.close();\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Error when trying to reserve a port\", oops);\n        }\n        return returnValue;\n    }\n\n    private AvailableSocketFinder()\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/base/OperationResults.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations.base;\n\nimport java.time.Duration;\n\n/**\n * Shared methods of both SSH and SCP operation results.\n *\n * @author cstaylor\n */\npublic interface OperationResults\n{\n    /**\n     * @return how long did it take to run?\n     */\n    Duration getElapsedTime();\n\n    /**\n     * @return Was there any output on stderr or stdout from the remote command?\n     */\n    String getOutput();\n\n    /**\n     * @return The exit code returned by the remote command\n     */\n    int getReturnValue();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/base/RemoteObject.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations.base;\n\nimport java.nio.file.Path;\n\n/**\n * When making a transfer call via SCP, a RemoteObject is something to read or write from a remote\n * machine.\n *\n * @author cstaylor\n */\npublic class RemoteObject\n{\n    /**\n     * SSH connect string\n     */\n    private final String connectString;\n\n    public RemoteObject(final String username, final String hostname, final Path path)\n    {\n        this.connectString = String.format(\"%s@%s:%s\", username, hostname, path.toString());\n    }\n\n    /**\n     * @return a valid parameter for a remote file resource (username@hostname:path) to be used with\n     *         SCP\n     */\n    @Override\n    public String toString()\n    {\n        return this.connectString;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/base/SCPOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations.base;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apache.commons.io.IOUtils;\n\n/**\n * Wrapper for making a remote call via the command-line scp utility\n *\n * @author cstaylor\n */\npublic class SCPOperation\n{\n    private static final String QUIET_ARG = \"-q\";\n    private static final String SCP_COMMAND = \"scp\";\n    private static final String PORT_OVERRIDE = \"-P\";\n    private Optional<Integer> possiblePortNumber = Optional.empty();\n\n    /**\n     * Send locally to remote with scp\n     *\n     * @param fromLocal\n     *            the local file to send\n     * @param toRemote\n     *            the remote file saved\n     * @return the results (error code, stdout/stderr output, timing)\n     * @throws IOException\n     *             if there's a network problem or the local file can't be read\n     * @throws InterruptedException\n     *             if our thread is interrupted while waiting for the remote command to finish\n     */\n    public SCPOperationResults copy(final Path fromLocal, final RemoteObject toRemote)\n            throws IOException, InterruptedException\n    {\n        return copy(fromLocal.toString(), toRemote.toString());\n    }\n\n    /**\n     * Read remote file and save it locally\n     *\n     * @param fromRemote\n     *            the remote file to read\n     * @param toLocal\n     *            the local file saved\n     * @return the results (error code, stdout/stderr output, timing)\n     * @throws IOException\n     *             if there's a network problem\n     * @throws InterruptedException\n     *             if our thread is interrupted while waiting for the remote command to finish\n     */\n    public SCPOperationResults copy(final RemoteObject fromRemote, final Path toLocal)\n            throws IOException, InterruptedException\n    {\n        return copy(fromRemote.toString(), toLocal.toString());\n    }\n\n    /**\n     * Send remote file to another remote file\n     *\n     * @param fromRemote\n     *            the remote file to read\n     * @param toRemote\n     *            the remote file saved\n     * @return the results (error code, stdout/stderr output, timing)\n     * @throws IOException\n     *             if there's a network problem\n     * @throws InterruptedException\n     *             if our thread is interrupted while waiting for the remote command to finish\n     */\n    public SCPOperationResults copy(final RemoteObject fromRemote, final RemoteObject toRemote)\n            throws IOException, InterruptedException\n    {\n        return copy(fromRemote.toString(), toRemote.toString());\n    }\n\n    public SCPOperation onPort(final int portNumber)\n    {\n        this.possiblePortNumber = Optional.of(portNumber);\n        return this;\n    }\n\n    /**\n     * The helper method that does the actual call to SCP\n     *\n     * @param fromResource\n     *            the local or remote file to be read\n     * @param toResource\n     *            the local or remote file that will be saved\n     * @return the results (error code, stdout/stderr output, timing)\n     * @throws IOException\n     *             if there's a network problem or the local file can't be read or saved\n     * @throws InterruptedException\n     *             if our thread is interrupted while waiting for the remote command to finish\n     */\n    private SCPOperationResults copy(final String fromResource, final String toResource)\n            throws IOException, InterruptedException\n    {\n        final List<String> args = new ArrayList<>();\n        args.add(SCP_COMMAND);\n        args.add(QUIET_ARG);\n        this.possiblePortNumber.ifPresent(portNumber ->\n        {\n            args.add(PORT_OVERRIDE);\n            args.add(String.valueOf(portNumber));\n        });\n        args.add(fromResource);\n        args.add(toResource);\n        final ProcessBuilder builder = new ProcessBuilder(args);\n        builder.redirectErrorStream(true);\n        final SCPOperationResults results = new SCPOperationResults(fromResource, toResource);\n        final Process process = builder.start();\n        final String remoteOutput = new String(IOUtils.toByteArray(process.getInputStream()));\n        final int returnCode = process.waitFor();\n        return results.finish(remoteOutput, returnCode);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/base/SCPOperationResults.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations.base;\n\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\n\n/**\n * When an scp command is run, the output from the command is captured by this class, including:\n * <ul>\n * <li>stdout/stderr output</li>\n * <li>duration of call</li>\n * <li>source file (local or remote) sent</li>\n * <li>destination file (local or remote) written</li>\n * </ul>\n *\n * @see SCPOperation\n * @see OperationResults\n * @author cstaylor\n */\npublic class SCPOperationResults implements OperationResults\n{\n    private final String destination;\n    private long end = -1;\n    private String output;\n    private int returnValue;\n    private final String source;\n    private long start = -1;\n\n    /**\n     * @param source\n     *            the local or remote file being read\n     * @param destination\n     *            the local or remote file being written\n     */\n    SCPOperationResults(final String source, final String destination)\n    {\n        this.start = System.currentTimeMillis();\n        this.source = source;\n        this.destination = destination;\n    }\n\n    /**\n     * @return the local or remote file being written\n     */\n    public String getDestination()\n    {\n        return this.destination;\n    }\n\n    @Override\n    public Duration getElapsedTime()\n    {\n        if (this.end == -1)\n        {\n            this.end = System.currentTimeMillis();\n        }\n        return Duration.of(this.end - this.start, ChronoUnit.MILLIS);\n    }\n\n    @Override\n    public String getOutput()\n    {\n        return this.output;\n    }\n\n    @Override\n    public int getReturnValue()\n    {\n        return this.returnValue;\n    }\n\n    /**\n     * @return the local or remote file being read\n     */\n    public String getSource()\n    {\n        return this.source;\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.format(\"%d\\n%s\\n\", getReturnValue(), getOutput());\n    }\n\n    /**\n     * When the scp operation is completed, this method should be called so the duration is\n     * recorded, the stdout/stderr output captured, and the remote return code saved\n     *\n     * @param output\n     *            possible stdout/stderr from the scp process\n     * @param returnCode\n     *            standard unix exit code. See man scp for details\n     * @return fluent interface returns this\n     */\n    SCPOperationResults finish(final String output, final int returnCode)\n    {\n        this.output = output;\n        this.returnValue = returnCode;\n        getElapsedTime();\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/base/SSHForwarder.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations.base;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.io.IOUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * This class spawns a child process that will connect via SSH to a remote host that will act as a\n * packet forwarder for us.\n * <p>\n * We need this to bypass some of the firewall restrictions.\n *\n * @author cstaylor\n */\npublic class SSHForwarder\n{\n    private static final int DEFAULT_SSH_PORT = 22;\n    private static final String LOGIN_FORMAT = \"%s@%s\";\n    private static final String PROXY_FORMAT = \"%d:%s:%d\";\n    private static final String FORWARD_CREDENTIALS = \"-A\";\n    private static final String PORT_MAPPING = \"-L\";\n    private static final String DISABLE_STRICT_HOST_CHECKING = \"-oStrictHostKeyChecking=no\";\n    private static final String SSH_COMMAND = \"ssh\";\n    private static final String CAT_COMMAND = \"cat\";\n    private static final String READ_FROM_STDIN = \"-\";\n    private static final int SSH_OPERATION_FAILURE_CODE = 255;\n    private String hostname;\n    private String username;\n    private int forwardingLocalPort = -1;\n    private int forwardingRemotePort = DEFAULT_SSH_PORT;\n    private String forwardingToHostname;\n    private Process remoteConnection;\n\n    public SSHForwarder()\n    {\n    }\n\n    /**\n     * @param username\n     *            the SSH username for connecting with the remote server\n     * @return fluent interface returns this\n     */\n    public SSHForwarder asUser(final String username)\n    {\n        this.username = username;\n        return this;\n    }\n\n    public String getHost()\n    {\n        return this.hostname;\n    }\n\n    public String getUser()\n    {\n        return this.username;\n    }\n\n    public SSHForwarder onHost(final String hostname)\n    {\n        this.hostname = hostname;\n        return this;\n    }\n\n    /**\n     * Connects via ssh to the remote server and holds the connection open\n     *\n     * @return the results (error code, stdout/stderr output, timing)\n     * @throws IOException\n     *             if there's a network problem\n     * @throws InterruptedException\n     *             if our thread is interrupted while waiting for the remote command to finish\n     */\n    public int startProxy() throws IOException, InterruptedException\n    {\n        if (this.hostname == null)\n        {\n            throw new IllegalStateException(\"Hostname must be defined\");\n        }\n        if (this.username == null)\n        {\n            this.username = System.getProperty(\"user.name\");\n        }\n        if (this.forwardingToHostname == null)\n        {\n            throw new IllegalStateException(\"forwardingToHostname must be defined\");\n        }\n        if (this.forwardingLocalPort < 0)\n        {\n            this.forwardingLocalPort = AvailableSocketFinder.takePort();\n        }\n        final List<String> arguments = new ArrayList<>();\n        arguments.add(SSH_COMMAND);\n        arguments.add(FORWARD_CREDENTIALS);\n        arguments.add(DISABLE_STRICT_HOST_CHECKING);\n        arguments.add(PORT_MAPPING);\n        arguments.add(String.format(PROXY_FORMAT, this.forwardingLocalPort,\n                this.forwardingToHostname, this.forwardingRemotePort));\n        arguments.add(String.format(LOGIN_FORMAT, this.username, this.hostname));\n        arguments.add(CAT_COMMAND);\n        arguments.add(READ_FROM_STDIN);\n        final ProcessBuilder builder = new ProcessBuilder(arguments);\n        builder.redirectErrorStream(true);\n        // We need to check for errors\n        this.remoteConnection = builder.start();\n        if (this.remoteConnection.waitFor(1L, TimeUnit.SECONDS)\n                && this.remoteConnection.exitValue() == SSH_OPERATION_FAILURE_CODE)\n        {\n            final String remoteOutput = new String(\n                    IOUtils.toByteArray(this.remoteConnection.getInputStream()));\n            throw new CoreException(\"Error when connecting to proxy: {}\", remoteOutput);\n        }\n        return this.forwardingLocalPort;\n    }\n\n    public void stopProxy() throws IOException, InterruptedException\n    {\n        this.remoteConnection.destroyForcibly();\n        this.remoteConnection.waitFor();\n    }\n\n    public SSHForwarder withForwardingHostname(final String hostname)\n    {\n        this.forwardingToHostname = hostname;\n        return this;\n    }\n\n    public SSHForwarder withForwardingLocalPort(final int port)\n    {\n        this.forwardingLocalPort = port;\n        return this;\n    }\n\n    public SSHForwarder withForwardingRemotePort(final int port)\n    {\n        this.forwardingRemotePort = port;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/base/SSHOperation.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations.base;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apache.commons.io.IOUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.base.Joiner;\n\n/**\n * Wrapper for running a remote program with the local ssh utility\n *\n * @author cstaylor\n */\npublic class SSHOperation\n{\n    private static final Logger logger = LoggerFactory.getLogger(SSHOperation.class);\n\n    private static final String LOGIN_FORMAT = \"%s@%s\";\n    private static final String SSH_COMMAND = \"ssh\";\n    private static final String DISABLE_STRICT_HOST_CHECKING = \"-oStrictHostKeyChecking=no\";\n    private static final String QUIET_MODE = \"-q\";\n    private static final String PORT_OVERRIDE = \"-p\";\n    private final List<String> args;\n    private String hostname;\n    private String username;\n    private Optional<Integer> possiblePort;\n    private boolean debug;\n\n    public SSHOperation()\n    {\n        this.args = new ArrayList<>();\n        this.possiblePort = Optional.empty();\n    }\n\n    /**\n     * Add arguments that will be sent to the remote server via SSH\n     *\n     * @param args\n     *            remote linux commands and their arguments\n     * @return fluent interface returns this\n     */\n    public SSHOperation addArgs(final String... args)\n    {\n        if (args == null || args.length == 0)\n        {\n            return this;\n        }\n        this.args.addAll(Arrays.asList(args));\n        return this;\n    }\n\n    /**\n     * @param username\n     *            the SSH username for connecting with the remote server\n     * @return fluent interface returns this\n     */\n    public SSHOperation asUser(final String username)\n    {\n        this.username = username;\n        return this;\n    }\n\n    public SSHOperation enableDebug()\n    {\n        this.debug = true;\n        return this;\n    }\n\n    /**\n     * Connects via ssh to the remote server and executes the linux command\n     *\n     * @return the results (error code, stdout/stderr output, timing)\n     * @throws IOException\n     *             if there's a network problem\n     * @throws InterruptedException\n     *             if our thread is interrupted while waiting for the remote command to finish\n     */\n    public SSHOperationResults execute() throws IOException, InterruptedException\n    {\n        if (this.hostname == null)\n        {\n            throw new IllegalStateException(\"Hostname must be defined\");\n        }\n        if (this.username == null)\n        {\n            this.username = System.getProperty(\"user.name\");\n        }\n        final List<String> arguments = buildArguments();\n        final ProcessBuilder builder = new ProcessBuilder(arguments);\n        if (this.debug)\n        {\n            logger.debug(Joiner.on(\" \").join(arguments));\n        }\n        builder.redirectErrorStream(true);\n        final SSHOperationResults results = new SSHOperationResults();\n        final Process process = builder.start();\n        final String remoteOutput = new String(IOUtils.toByteArray(process.getInputStream()));\n        final int returnCode = process.waitFor();\n        if (this.debug)\n        {\n            logger.debug(\"[{}] with output:\\n{}\", returnCode, remoteOutput);\n        }\n        return results.finish(remoteOutput, returnCode);\n    }\n\n    public String getHost()\n    {\n        return this.hostname;\n    }\n\n    public String getUser()\n    {\n        return this.username;\n    }\n\n    public SSHOperation onHost(final String hostname)\n    {\n        this.hostname = hostname;\n        return this;\n    }\n\n    public SSHOperation onPort(final int port)\n    {\n        this.possiblePort = Optional.of(port);\n        return this;\n    }\n\n    private List<String> buildArguments()\n    {\n        if (this.args.size() == 0)\n        {\n            throw new IllegalStateException(\"You must have at least one argument\");\n        }\n        final List<String> arguments = new ArrayList<>();\n        arguments.add(SSH_COMMAND);\n        arguments.add(DISABLE_STRICT_HOST_CHECKING);\n        arguments.add(QUIET_MODE);\n        this.possiblePort.ifPresent(port ->\n        {\n            arguments.add(PORT_OVERRIDE);\n            arguments.add(String.valueOf(port));\n        });\n        arguments.add(String.format(LOGIN_FORMAT, this.username, this.hostname));\n        arguments.addAll(this.args);\n        return arguments;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/cli/operations/base/SSHOperationResults.java",
    "content": "package org.openstreetmap.atlas.utilities.cli.operations.base;\n\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\n\n/**\n * When an ssh command is run, the output from the command is captured by this class, including:\n * <ul>\n * <li>stdout/stderr output</li>\n * <li>duration of call</li>\n * </ul>\n *\n * @see SSHOperation\n * @see OperationResults\n * @author cstaylor\n */\npublic class SSHOperationResults implements OperationResults\n{\n    private long end = -1;\n    private String output;\n    private int returnValue;\n    private long start = -1;\n\n    public SSHOperationResults()\n    {\n        this.start = System.currentTimeMillis();\n    }\n\n    @Override\n    public Duration getElapsedTime()\n    {\n        if (this.end == -1)\n        {\n            this.end = System.currentTimeMillis();\n        }\n        return Duration.of(this.end - this.start, ChronoUnit.MILLIS);\n    }\n\n    @Override\n    public String getOutput()\n    {\n        return this.output;\n    }\n\n    @Override\n    public int getReturnValue()\n    {\n        return this.returnValue;\n    }\n\n    /**\n     * When the ssh operation is completed, this method should be called so the duration is\n     * recorded, the stdout/stderr output captured, and the remote return code saved\n     *\n     * @param output\n     *            possible stdout/stderr from the scp process\n     * @param returnValue\n     *            standard unix exit code. See man scp for details\n     * @return fluent interface returns this\n     */\n    protected SSHOperationResults finish(final String output, final int returnValue)\n    {\n        this.output = output;\n        this.returnValue = returnValue;\n        getElapsedTime();\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/EnhancedCollectors.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.SortedMap;\nimport java.util.SortedSet;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\nimport java.util.stream.Collector;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport com.google.common.collect.ImmutableList;\n\n/**\n * Shamelessly stolen from:\n * http://stackoverflow.com/questions/29090277/java-8-streams-collections-tomap-from-list-how-to-\n * keep-the-order and from:\n * https://stackoverflow.com/questions/39130122/java-8-nested-multi-level-group-by/39131049#39131049\n *\n * @author cstaylor\n * @author mgostintsev\n */\npublic final class EnhancedCollectors\n{\n    public static <T, K, A, R> Collector<T, ?, R> flatMapping(\n            final Function<? super T, ? extends Stream<? extends K>> mapper,\n            final Collector<? super K, A, R> downstream)\n    {\n        final BiConsumer<A, ? super K> accumulator = downstream.accumulator();\n        return Collector.of(downstream.supplier(), (itemA, itemT) ->\n        {\n            try (Stream<? extends K> s = mapper.apply(itemT))\n            {\n                if (s != null)\n                {\n                    s.forEachOrdered(u -> accumulator.accept(itemA, u));\n                }\n            }\n        }, downstream.combiner(), downstream.finisher(),\n                downstream.characteristics().stream().toArray(Collector.Characteristics[]::new));\n    }\n\n    public static <T extends Comparable<T>> Collector<T, ?, ImmutableList<T>> toImmutableList()\n    {\n        return new ImmutableListCollector<>();\n    }\n\n    /**\n     * I wanted a way of quickly converting lists to linked hashmaps, so I found this little block\n     * of code in Stack Overflow\n     *\n     * @param <T>\n     *            the type in the incoming list\n     * @param <K>\n     *            the key type of the outgoing map\n     * @param <U>\n     *            the value type of the outgoing map\n     * @param keyMapper\n     *            how we get the key into the map\n     * @param valueMapper\n     *            how we get the value into the map\n     * @return the linked hashmap\n     */\n    public static <T, K, U> Collector<T, ?, Map<K, U>> toLinkedMap(\n            final Function<? super T, ? extends K> keyMapper,\n            final Function<? super T, ? extends U> valueMapper)\n    {\n        return Collectors.toMap(keyMapper, valueMapper, (key, value) ->\n        {\n            throw new IllegalStateException(String.format(\"Duplicate key %s\", key));\n        }, LinkedHashMap::new);\n    }\n\n    public static <T, K extends Comparable<K>, U> Collector<T, ?, SortedMap<K, U>> toUnmodifiableSortedMap(\n            final Function<? super T, ? extends K> keyMapper,\n            final Function<? super T, ? extends U> valueMapper)\n    {\n        return new UnmodifiableSortedMapCollector<>(keyMapper, valueMapper);\n    }\n\n    public static <T extends Comparable<T>> Collector<T, ?, SortedSet<T>> toUnmodifiableSortedSet()\n    {\n        return new UnmodifiableSortedSetCollector<>();\n    }\n\n    private EnhancedCollectors()\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/EnumSetCollector.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.TypeVariable;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.BinaryOperator;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collector;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\nimport com.google.common.reflect.TypeToken;\n\n/**\n * A way to build an EnumSet from a stream of strings, where the case of the strings don't matter\n * and ALL will automatically add all declared enum constants to the EnumSet. Since we're using\n * generic types, we have some issues where the parameterized type information is only contained in\n * concrete subclasses of this collector. That means for each Enum we want to collect, we'll need an\n * empty subclass so we can recover the type information. For a testcase showing how to make it\n * work, check out EnumSetCollectionTestCase\n *\n * @author cstaylor\n * @param <T>\n *            The Java enum to store in an EnumSet that can be queried for values\n */\npublic abstract class EnumSetCollector<T extends Enum<T>>\n        implements Collector<String, Set<String>, EnumSet<T>>\n{\n    @SuppressWarnings(\"rawtypes\")\n    private Class enumClass;\n\n    private Method valueOfMethod;\n\n    @SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n    /**\n     * We need to know the enum's class, but we can't get at it directly because we're a generic\n     * interface. Following directions listed out in this article:\n     * http://stackoverflow.com/questions/3609799/how-to-get-type-parameter-values-using-java-\n     * reflection\n     */\n    protected EnumSetCollector()\n    {\n        final TypeToken<?> resolver = TypeToken.of(getClass());\n\n        for (final TypeVariable<Class<EnumSetCollector>> typeVariable : EnumSetCollector.class\n                .getTypeParameters())\n        {\n            final TypeToken<?> currentToken = resolver.resolveType(typeVariable);\n            this.enumClass = currentToken.getRawType();\n            try\n            {\n                this.valueOfMethod = this.enumClass.getMethod(\"valueOf\", String.class);\n            }\n            catch (final NoSuchMethodException oops)\n            {\n                throw new CoreException(\n                        String.format(\"%s isn't a Java enum\", this.enumClass.getName()));\n            }\n        }\n    }\n\n    @Override\n    public BiConsumer<Set<String>, String> accumulator()\n    {\n        return (enumset, value) -> enumset.add(value.toUpperCase());\n    }\n\n    @Override\n    public Set<java.util.stream.Collector.Characteristics> characteristics()\n    {\n        return EnumSet.of(Characteristics.UNORDERED);\n    }\n\n    @Override\n    public BinaryOperator<Set<String>> combiner()\n    {\n        return (left, right) ->\n        {\n            left.addAll(right);\n            return left;\n        };\n    }\n\n    @SuppressWarnings({ \"unchecked\" })\n    @Override\n    public Function<Set<String>, EnumSet<T>> finisher()\n    {\n        return working ->\n        {\n            if (working.contains(\"ALL\"))\n            {\n                return EnumSet.allOf(this.enumClass);\n            }\n            try\n            {\n                final EnumSet<T> returnValue = EnumSet.noneOf(this.enumClass);\n                for (final String constant : working)\n                {\n                    returnValue.add((T) this.valueOfMethod.invoke(null, constant));\n                }\n                return returnValue;\n            }\n            catch (final InvocationTargetException | IllegalAccessException oops)\n            {\n                throw new CoreException(\"Can't find enum value for: {}\", working);\n            }\n        };\n    }\n\n    @Override\n    public Supplier<Set<String>> supplier()\n    {\n        return this.enumClass == null ? null : () -> new HashSet<>();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/FilteredIterable.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Iterator;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * Takes an iterable and adds in a set of elements to skip over. Useful for when an iterable will be\n * iterated over multiple times, but some elements can be skipped for efficiency.\n *\n * @author samuelgass\n * @param <Type>\n *            the type of the {@link Iterable}\n * @param <IdentifierType>\n *            the type for the identifier used by the elements of Type for the {@link Iterable}\n */\npublic class FilteredIterable<Type, IdentifierType> implements Iterable<Type>\n{\n    private final Iterable<Type> source;\n    private final Set<IdentifierType> filterSet;\n    private final Function<Type, IdentifierType> identifier;\n\n    /**\n     * Constructor for FilteredIterable.\n     *\n     * @param source\n     *            A source iterable to translate to FilteredIterable\n     * @param filterSet\n     *            A set of identifiers for elements to skip (can be empty or have members)\n     * @param identifier\n     *            A function that takes an element of Type for the {@link Iterable} and returns the\n     *            identifier for that element\n     */\n    public FilteredIterable(final Iterable<Type> source, final Set<IdentifierType> filterSet,\n            final Function<Type, IdentifierType> identifier)\n    {\n        this.filterSet = filterSet;\n        this.source = source;\n        this.identifier = identifier;\n    }\n\n    /**\n     * Takes an element and uses the identifier function to add its identifier to the filter set.\n     *\n     * @param type\n     *            The element to add to the filter set\n     * @return True if an element was added to the filter set, false if it wasn't (likely in the\n     *         case it was already present in the set)\n     */\n    public boolean addToFilteredSet(final Type type)\n    {\n        return this.filterSet.add(this.identifier.apply(type));\n    }\n\n    @Override\n    public Iterator<Type> iterator()\n    {\n        return new Iterator<Type>()\n        {\n            private final Iterator<Type> sourceIterator = FilteredIterable.this.source.iterator();\n            private Type next = null;\n            private Type current = this.next();\n\n            @Override\n            public boolean hasNext()\n            {\n                return this.next != null && !FilteredIterable.this.filterSet\n                        .contains(FilteredIterable.this.identifier.apply(this.next));\n            }\n\n            @Override\n            public Type next()\n            {\n                this.current = this.next;\n                this.next = null;\n                while (this.sourceIterator.hasNext())\n                {\n                    final Type nextCandidate = this.sourceIterator.next();\n                    if (FilteredIterable.this.filterSet\n                            .contains(FilteredIterable.this.identifier.apply(nextCandidate)))\n                    {\n                        continue;\n                    }\n                    else\n                    {\n                        this.next = nextCandidate;\n                        break;\n                    }\n                }\n                return this.current;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/FixedSizePriorityQueue.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.AbstractQueue;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.PriorityQueue;\n\n/**\n * A fixed size priority queue (binary heap) for use case like getting top n, implemented based on\n * {@link PriorityQueue}.\n * <p>\n * After reaching the given maximum size, any additional element offered to the queue will be added\n * into the queue first and then remove the new head, so the size of the queue will remain the same\n *\n * @param <E>\n *            The type of element\n * @author tony\n */\npublic class FixedSizePriorityQueue<E> extends AbstractQueue<E>\n{\n    private final int maximumSize;\n    private final PriorityQueue<E> priorityQueue;\n\n    public FixedSizePriorityQueue(final int maximumSize)\n    {\n        this.maximumSize = maximumSize;\n        this.priorityQueue = new PriorityQueue<>(maximumSize + 1);\n    }\n\n    public FixedSizePriorityQueue(final int maximumSize, final Comparator<? super E> comparator)\n    {\n        this.maximumSize = maximumSize;\n        this.priorityQueue = new PriorityQueue<>(maximumSize + 1, comparator);\n    }\n\n    @Override\n    public Iterator<E> iterator()\n    {\n        return this.priorityQueue.iterator();\n    }\n\n    @Override\n    public boolean offer(final E e)\n    {\n        final boolean flag = this.priorityQueue.offer(e);\n        if (this.priorityQueue.size() > this.maximumSize)\n        {\n            poll();\n        }\n        return flag;\n    }\n\n    @Override\n    public E peek()\n    {\n        return this.priorityQueue.peek();\n    }\n\n    @Override\n    public E poll()\n    {\n        return this.priorityQueue.poll();\n    }\n\n    @Override\n    public int size()\n    {\n        return this.priorityQueue.size();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/ImmutableListCollector.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.BinaryOperator;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collector;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableList.Builder;\nimport com.google.common.collect.ImmutableSet;\n\n/**\n * Converts a stream of objects into an {@link ImmutableList}\n *\n * @author mgostintsev\n * @param <T>\n *            the type of incoming objects we want in the {@link ImmutableList}\n */\npublic class ImmutableListCollector<T extends Comparable<T>>\n        implements Collector<T, ImmutableList.Builder<T>, ImmutableList<T>>\n{\n\n    @Override\n    public BiConsumer<Builder<T>, T> accumulator()\n    {\n        return (builder, item) -> builder.add(item);\n    }\n\n    @Override\n    public Set<java.util.stream.Collector.Characteristics> characteristics()\n    {\n        return ImmutableSet.of(Characteristics.UNORDERED);\n    }\n\n    @Override\n    public BinaryOperator<Builder<T>> combiner()\n    {\n        return (builder1, builder2) ->\n        {\n            builder1.addAll(builder2.build());\n            return builder1;\n        };\n    }\n\n    @Override\n    public Function<Builder<T>, ImmutableList<T>> finisher()\n    {\n        return builder -> builder.build();\n    }\n\n    @Override\n    public Supplier<Builder<T>> supplier()\n    {\n        return ImmutableList::builder;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/Iterables.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.NoSuchElementException;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.Function;\nimport java.util.function.LongFunction;\nimport java.util.function.Predicate;\nimport java.util.function.ToLongFunction;\nimport java.util.stream.StreamSupport;\n\n/**\n * Iterable utility methods\n *\n * @author matthieun\n */\npublic final class Iterables\n{\n    /**\n     * Adds the contents of the from Iterable to the addHere collection, and returns true if items\n     * were added. It's possible that nothing has changed if addHere is a set\n     *\n     * @param addHere\n     *            where to add the items\n     * @param from\n     *            where to get the items\n     * @param <T>\n     *            what kind of objects we're copying\n     * @return true if addHere has changed, false otherwise\n     */\n    public static <T> boolean addAll(final Collection<T> addHere, final Iterable<T> from)\n    {\n        final int oldSize = addHere.size();\n        StreamSupport.stream(from.spliterator(), false).forEach(addHere::add);\n        return oldSize < addHere.size();\n    }\n\n    /**\n     * Strip down any {@link Iterable} into .. just an {@link Iterable}.\n     *\n     * @param types\n     *            The {@link Iterable} to strip down\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T> Iterable<T> asIterable(final Iterable<T> types)\n    {\n        return types::iterator;\n    }\n\n    /**\n     * Translate an {@link Iterable} into a {@link List}\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T> List<T> asList(final Iterable<T> types)\n    {\n        if (types instanceof List)\n        {\n            return (List<T>) types;\n        }\n        final int initialSize;\n        if (types instanceof Collection)\n        {\n            initialSize = ((Collection) types).size();\n        }\n        else\n        {\n            initialSize = 0;\n        }\n        final List<T> result = new ArrayList<>(initialSize);\n        types.forEach(result::add);\n        return result;\n    }\n\n    /**\n     * Translate an array to an {@link List}. Unlike {@link java.util.Arrays#asList}, this method\n     * does not use the given array as the backing array.\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T> List<T> asList(final T[] types)\n    {\n        // This avoids several grow calls, and ensures that we aren't using the backing array.\n        return new ArrayList<>(Arrays.asList(types));\n    }\n\n    /**\n     * Translate an iterable list of Map entries to a map\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param <K>\n     *            The type of key of the entry\n     * @param <V>\n     *            The type of value of the entry\n     * @return The translated {@link Iterable}\n     */\n    public static <K, V> Map<K, V> asMap(final Iterable<Map.Entry<K, V>> types)\n    {\n        final Map<K, V> result = new HashMap<>();\n        types.forEach(entry -> result.put(entry.getKey(), entry.getValue()));\n        return result;\n    }\n\n    /**\n     * Translate an {@link Iterable} into a {@link Queue}\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T> Queue<T> asQueue(final Iterable<T> types)\n    {\n        final Queue<T> result = new LinkedList<>();\n        types.forEach(result::add);\n        return result;\n    }\n\n    /**\n     * Translate an {@link Iterable} into a {@link Set}\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T> Set<T> asSet(final Iterable<T> types)\n    {\n        final Set<T> result = new HashSet<>();\n        types.forEach(result::add);\n        return result;\n    }\n\n    /**\n     * Translate an array to an {@link Set}\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T> Set<T> asSet(final T[] types)\n    {\n        final Set<T> result = new HashSet<>();\n        for (final T type : types)\n        {\n            result.add(type);\n        }\n        return result;\n    }\n\n    /**\n     * Translate an {@link Iterable} of items into a {@link SortedSet}\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T> SortedSet<T> asSortedSet(final Iterable<T> types)\n    {\n        final SortedSet<T> result = new TreeSet<>();\n        types.forEach(result::add);\n        return result;\n    }\n\n    /**\n     * Test if an {@link Iterable} iterates at some point on an item.\n     *\n     * @param types\n     *            The {@link Iterable} to test\n     * @param type\n     *            The item to test\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return True if the {@link Iterable} iterates at some point on the item.\n     */\n    public static <T> boolean contains(final Iterable<T> types, final T type)\n    {\n        if (types instanceof Collection)\n        {\n            return ((Collection<T>) types).contains(type);\n        }\n        for (final T candidate : types)\n        {\n            if (candidate.equals(type))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Count a set of values\n     *\n     * @param types\n     *            The {@link Iterable} of input type\n     * @param typeCounter\n     *            The function from type to count\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The total count\n     */\n    public static <T> long count(final Iterable<T> types, final ToLongFunction<T> typeCounter)\n    {\n        long result = 0;\n        for (final T type : types)\n        {\n            result += typeCounter.applyAsLong(type);\n        }\n        return result;\n    }\n\n    /**\n     * @param example\n     *            A random object to specify the type\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return An empty {@link Iterable} of the right type\n     */\n    public static <T> Iterable<T> emptyIterable(final T example) // NOSONAR\n    {\n        return () -> new Iterator<T>()\n        {\n            @Override\n            public boolean hasNext()\n            {\n                return false;\n            }\n\n            @Override\n            public T next()\n            {\n                throw new NoSuchElementException();\n            }\n        };\n    }\n\n    /**\n     * Test if two {@link Iterable}s iterate on the same items.\n     *\n     * @param that\n     *            The first {@link Iterable}\n     * @param other\n     *            The second iterable\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return True if the two {@link Iterable}s iterate on the same items.\n     */\n    public static <T> boolean equals(final Iterable<T> that, final Iterable<T> other)\n    {\n        // Handle null iterables\n        // If they are both null, then equal\n        // If only one of them is null, then NOT equal\n        final boolean thatIsNull = that == null;\n        final boolean otherIsNull = other == null;\n        if (thatIsNull || otherIsNull)\n        {\n            return thatIsNull && otherIsNull;\n        }\n\n        // Iterables are not null, let's check for size first\n        // Then the values\n        final long thatSize = Iterables.size(that);\n        if (thatSize != Iterables.size(other))\n        {\n            return false;\n        }\n        final Iterator<T> thatIterator = that.iterator();\n        final Iterator<T> otherIterator = other.iterator();\n        while (thatIterator.hasNext())\n        {\n            if (!thatIterator.next().equals(otherIterator.next()))\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Filter an {@link Iterable}\n     *\n     * @param input\n     *            The {@link Iterable} to filter\n     * @param matcher\n     *            The {@link Predicate} used to filter\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The filtered {@link Iterable}\n     */\n    public static <T> Iterable<T> filter(final Iterable<T> input, final Predicate<T> matcher)\n    {\n        return filterTranslate(input, item -> item, matcher);\n    }\n\n    /**\n     * Translate an {@link Iterable} of items into a {@link FilteredIterable}\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param filterSet\n     *            A set of identifiers for elements to skip (can be empty or have members)\n     * @param identifier\n     *            A function that takes an element of T for the {@link Iterable} and returns the\n     *            identifier for that element\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @param <I>\n     *            The type of the Identifier object for the elements in the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T, I> FilteredIterable<T, I> filter(final Iterable<T> types,\n            final Set<I> filterSet, final Function<T, I> identifier)\n    {\n        return new FilteredIterable<>(types, filterSet, identifier);\n    }\n\n    /**\n     * Translate an {@link Iterable} of type I to an {@link Iterable} of type O.\n     *\n     * @param input\n     *            The input {@link Iterable}\n     * @param converter\n     *            The converter from I to O\n     * @param matcher\n     *            A {@link Predicate} on I that filters only the items to match\n     * @param <I>\n     *            The type of the input {@link Iterable}\n     * @param <O>\n     *            The type of the output {@link Iterable}\n     * @return The {@link Iterable} of O\n     */\n    public static <I, O> Iterable<O> filterTranslate(final Iterable<I> input,\n            final Function<I, O> converter, final Predicate<I> matcher)\n    {\n        return new Iterable<O>()\n        {\n            @Override\n            public Iterator<O> iterator()\n            {\n                return new Iterator<O>()\n                {\n                    private boolean consumed = true;\n                    private final Iterator<I> iterator = input.iterator();\n                    private I next = null;\n                    private boolean valid = false;\n\n                    @Override\n                    public boolean hasNext()\n                    {\n                        if (this.consumed)\n                        {\n                            this.next = null;\n                            this.valid = false;\n                            while (this.iterator.hasNext() && !this.valid)\n                            {\n                                this.next = this.iterator.next();\n                                this.valid = matcher.test(this.next);\n                            }\n                            this.consumed = false;\n                        }\n                        return this.valid;\n                    }\n\n                    @Override\n                    public O next()\n                    {\n                        if (hasNext())\n                        {\n                            this.consumed = true;\n                            return converter.apply(this.next);\n                        }\n                        throw new NoSuchElementException();\n                    }\n                };\n            }\n\n            @SuppressWarnings(\"unused\")\n            public void useless()\n            {\n                // Unused\n            }\n        };\n    }\n\n    /**\n     * Get the first element of an {@link Iterable}\n     *\n     * @param types\n     *            The items\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The first element in the {@link Iterable}, or empty if none.\n     */\n    public static <T> Optional<T> first(final Iterable<T> types)\n    {\n        return nth(types, 0);\n    }\n\n    public static <T> Optional<T> firstMatching(final Iterable<T> types, final Predicate<T> matcher)\n    {\n        return first(filter(types, matcher));\n    }\n\n    /**\n     * Create an {@link Iterable} from an {@link Enumeration}\n     *\n     * @param types\n     *            The {@link Enumeration}\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    public static <T> Iterable<T> from(final Enumeration<T> types)\n    {\n        return () -> new Iterator<T>()\n        {\n            @Override\n            public boolean hasNext()\n            {\n                return types.hasMoreElements();\n            }\n\n            @Override\n            public T next()\n            {\n                if (!hasNext())\n                {\n                    throw new NoSuchElementException();\n                }\n                return types.nextElement();\n            }\n        };\n    }\n\n    /**\n     * Create an {@link Iterable} from 0 to many items of the provided type\n     *\n     * @param types\n     *            The 0 to many array of items to include\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    @SafeVarargs\n    public static <T> Iterable<T> from(final T... types)\n    {\n        return asList(types);\n    }\n\n    /**\n     * Get the head (first) element of an {@link Iterable}\n     *\n     * @param types\n     *            The items\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The head element in the {@link Iterable}, or null if none.\n     */\n    public static <T> T head(final Iterable<T> types)\n    {\n        final Iterator<T> iterator = types.iterator();\n        return iterator.hasNext() ? iterator.next() : null;\n    }\n\n    /**\n     * Get an {@link Iterable} based on something that can return the value at a specific index.\n     *\n     * @param size\n     *            The total size of the collection\n     * @param supplier\n     *            The provider of the value based on the index\n     * @param <T>\n     *            The type to return within the {@link Iterable}\n     * @return The index based {@link Iterable}\n     */\n    public static <T> Iterable<T> indexBasedIterable(final long size,\n            final LongFunction<T> supplier)\n    {\n        return () -> new Iterator<T>()\n        {\n            private long index = 0L;\n\n            @Override\n            public boolean hasNext()\n            {\n                return this.index < size;\n            }\n\n            @Override\n            public T next()\n            {\n                if (!hasNext())\n                {\n                    throw new NoSuchElementException();\n                }\n                return supplier.apply(this.index++);\n            }\n        };\n    }\n\n    /**\n     * Determines if the given iterable is empty\n     *\n     * @param types\n     *            The iterable to check\n     * @return {@code true} if the iterable contains no elements\n     */\n    public static boolean isEmpty(final Iterable<?> types)\n    {\n        if (types instanceof Collection)\n        {\n            return ((Collection<?>) types).isEmpty();\n        }\n        return !types.iterator().hasNext();\n    }\n\n    /**\n     * Translate a passed array of Items to an {@link Iterable} of Items\n     *\n     * @param types\n     *            The items\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return An {@link Iterable} of items.\n     */\n    public static <T> Iterable<T> iterable(@SuppressWarnings(\"unchecked\") final T... types)\n    {\n        return indexBasedIterable(types.length, index -> types[(int) index]);\n    }\n\n    /**\n     * Build an new Iterable by prepending the head element to the tail iterable.\n     *\n     * @param head\n     *            The item to place in the head position\n     * @param tail\n     *            The items positioned after the head\n     * @param <T>\n     *            The type of the head and tail {@link Iterable}\n     * @return An {@link Iterable}\n     */\n    public static <T> Iterable<T> join(final T head, final Iterable<T> tail)\n    {\n        return () -> new Iterator<T>()\n        {\n            private final Iterator<T> tailIterator = tail.iterator();\n            private boolean headConsumed = false;\n\n            @Override\n            public boolean hasNext()\n            {\n                return !this.headConsumed || this.tailIterator.hasNext();\n            }\n\n            @Override\n            public T next()\n            {\n                if (this.headConsumed)\n                {\n                    return this.tailIterator.next();\n                }\n                this.headConsumed = true;\n                return head;\n            }\n        };\n    }\n\n    /**\n     * Get the last element of an {@link Iterable}\n     *\n     * @param types\n     *            The items\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The last element in the {@link Iterable}\n     */\n    public static <T> Optional<T> last(final Iterable<T> types)\n    {\n        T result = null;\n        if (types instanceof List)\n        {\n            final List<T> list = (List<T>) types;\n            if (!list.isEmpty())\n            {\n                result = list.get(list.size() - 1);\n            }\n        }\n        else\n        {\n            for (final T type : types)\n            {\n                result = type;\n            }\n        }\n        return Optional.ofNullable(result);\n    }\n\n    public static <T> Optional<T> lastMatching(final Iterable<T> types, final Predicate<T> matcher)\n    {\n        return last(filter(types, matcher));\n    }\n\n    /**\n     * Get the nth element of an {@link Iterable}\n     *\n     * @param types\n     *            The items\n     * @param index\n     *            The index at which to pick\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The first element in the {@link Iterable}, or empty if the iterable has no element at\n     *         this index.\n     */\n    public static <T> Optional<T> nth(final Iterable<T> types, final long index)\n    {\n        long counter = 0L;\n        final Iterator<T> iterator = types.iterator();\n        T result = iterator.hasNext() ? iterator.next() : null;\n        while (counter++ < index)\n        {\n            if (iterator.hasNext())\n            {\n                result = iterator.next();\n            }\n            else\n            {\n                result = null;\n                break;\n            }\n        }\n        return Optional.ofNullable(result);\n    }\n\n    /**\n     * Create a {@link StreamIterable} that uses parallelization\n     *\n     * @param source\n     *            The {@link Iterable} to use as source\n     * @param <T>\n     *            The type of the source {@link Iterable}\n     * @return The corresponding {@link StreamIterable}\n     */\n    public static <T> StreamIterable<T> parallelStream(final Iterable<T> source)\n    {\n        return new StreamIterable<>(source, true);\n    }\n\n    public static <T> void print(final Iterable<T> input, final String name)\n    {\n        System.out.println(toString(input, name)); // NOSONAR\n    }\n\n    /**\n     * Iterate over an {@link Iterable} to get its size. If the {@link Iterable} is a sub instance\n     * of {@link Collection}, then it reads the size from it directly; it will not iterate\n     * unnecessarily.\n     *\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @param types\n     *            The input {@link Iterable}\n     * @return The size of the {@link Iterable}\n     */\n    public static <T> long size(final Iterable<T> types)\n    {\n        if (types instanceof Collection)\n        {\n            return ((Collection<T>) types).size();\n        }\n        return count(types, type -> 1L);\n    }\n\n    /**\n     * Create a {@link StreamIterable}\n     *\n     * @param source\n     *            The {@link Iterable} to use as source\n     * @param <T>\n     *            The type of the source {@link Iterable}\n     * @return The corresponding {@link StreamIterable}\n     */\n    public static <T> StreamIterable<T> stream(final Iterable<T> source)\n    {\n        return new StreamIterable<>(source);\n    }\n\n    /**\n     * Get an {@link Iterable} of all elements beyond the head.\n     *\n     * @param types\n     *            The items\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return An {@link Iterable}\n     */\n    public static <T> Iterable<T> tail(final Iterable<T> types)\n    {\n        final Iterator<T> iterator = types.iterator();\n        if (iterator.hasNext())\n        {\n            iterator.next();\n        }\n        return () -> iterator;\n    }\n\n    /**\n     * Translate an array to an {@link List}\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The translated {@link Iterable}\n     */\n    @SafeVarargs\n    public static <T> List<T> toList(final T... types)\n    {\n        return asList(types);\n    }\n\n    /**\n     * Translate an {@link Iterable} to a {@link String}\n     *\n     * @param input\n     *            The input {@link Iterable}\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @param name\n     *            The name of the input {@link Iterable}\n     * @return A {@link String} representation of the {@link Iterable}\n     */\n    public static <T> String toString(final Iterable<T> input, final String name)\n    {\n        return toString(input, name, \", \");\n    }\n\n    /**\n     * Translate an {@link Iterable} to a {@link String}\n     *\n     * @param input\n     *            The input {@link Iterable}\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @param name\n     *            The name of the input {@link Iterable}\n     * @param separator\n     *            The separator to use between each item in the input {@link Iterable}\n     * @return A {@link String} representation of the {@link Iterable}\n     */\n    public static <T> String toString(final Iterable<T> input, final String name,\n            final String separator)\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[\");\n        builder.append(name);\n        builder.append(\": \");\n        long index = 0;\n        for (final T type : input)\n        {\n            if (index > 0)\n            {\n                builder.append(separator);\n            }\n            builder.append(type.toString());\n            index++;\n        }\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    /**\n     * Translate an {@link Iterable} of type I to an {@link Iterable} of type O.\n     *\n     * @param input\n     *            The input {@link Iterable}\n     * @param converter\n     *            The converter from I to O\n     * @param <I>\n     *            The type of the input {@link Iterable}\n     * @param <O>\n     *            The type of the output {@link Iterable}\n     * @return The {@link Iterable} of O\n     */\n    public static <I, O> Iterable<O> translate(final Iterable<I> input,\n            final Function<I, O> converter)\n    {\n        return filterTranslate(input, converter, item -> true);\n    }\n\n    /**\n     * Translate an {@link Iterable} of type I to an {@link Iterable} of type O.\n     *\n     * @param input\n     *            The input {@link Iterable}\n     * @param converter\n     *            The converter from I to O\n     * @param <I>\n     *            The type of the input {@link Iterable}\n     * @param <O>\n     *            The type of the output {@link Iterable}\n     * @param matcher\n     *            A {@link Predicate} on O that filters only the items to match\n     * @return The {@link Iterable} of O\n     */\n    public static <I, O> Iterable<O> translateFilter(final Iterable<I> input,\n            final Function<I, O> converter, final Predicate<O> matcher)\n    {\n        return Iterables.filter(Iterables.translate(input, converter), matcher);\n    }\n\n    /**\n     * Translate an {@link Iterable} of type I to an {@link Iterable} of O where each converter\n     * yields multiple O for each I.\n     *\n     * @param iterableIn\n     *            The input {@link Iterable}\n     * @param converter\n     *            The converter from I to multiple O\n     * @param <I>\n     *            The type of the input {@link Iterable}\n     * @param <O>\n     *            The type of the output {@link Iterable}\n     * @return The {@link Iterable} of O\n     */\n    public static <I, O> Iterable<O> translateMulti(final Iterable<I> iterableIn,\n            final Function<I, Iterable<? extends O>> converter)\n    {\n        return new MultiIterable<>(Iterables.translate(iterableIn, converter));\n    }\n\n    /**\n     * Truncate an {@link Iterable}.\n     *\n     * @param types\n     *            The {@link Iterable} to translate\n     * @param startIndex\n     *            The index before which to truncate from the start\n     * @param indexFromEnd\n     *            The index after which to truncate from the end\n     * @param <T>\n     *            The type of the {@link Iterable}\n     * @return The truncated {@link Iterable}\n     */\n    public static <T> Iterable<T> truncate(final Iterable<T> types, final int startIndex,\n            final int indexFromEnd)\n    {\n        return new SubIterable<>(types, startIndex, indexFromEnd);\n    }\n\n    private Iterables()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/JoinedCollection.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * A Joined Collection is simply an array of elements that have been joined together from a\n * ParallelIterator. This object is some what specific to the ParallelIterable object which uses it\n * to join single elements from multiple iterable lists.\n *\n * @author cuthbertm\n */\npublic class JoinedCollection\n{\n    private final Object[] elements;\n\n    public JoinedCollection(final int originalSize)\n    {\n        this.elements = new Object[originalSize];\n        for (int index = 0; index < this.elements.length; index++)\n        {\n            this.elements[index] = null;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <Type> Type get(final int index) throws ClassCastException, CoreException\n    {\n        if (index >= 0 && index < this.elements.length)\n        {\n            return (Type) this.elements[index];\n        }\n        throw new CoreException(\"Invalid index {}, needs to be value between -1 and {}\", index,\n                this.elements.length);\n    }\n\n    public <Type> Optional<Type> getOption(final int index) throws ClassCastException\n    {\n        final Type returnType = get(index);\n        if (returnType == null)\n        {\n            return Optional.empty();\n        }\n        return Optional.of(returnType);\n    }\n\n    public <Type> void set(final int index, final Type value) throws CoreException\n    {\n        if (index >= 0 && index < this.elements.length)\n        {\n            this.elements[index] = value;\n        }\n        else\n        {\n            throw new CoreException(\"Invalid index {}, needs to be value between -1 and {}\", index,\n                    this.elements.length);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/Maps.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic final class Maps\n{\n    /**\n     * Return a {@link HashMap} from a even number of keys and values of the same type\n     *\n     * @param items\n     *            a even number of keys and values of the same type\n     * @param <T>\n     *            The type for the map\n     * @return A {@link HashMap} translated from the keys and values in items\n     */\n    @SafeVarargs\n    public static <T> Map<T, T> hashMap(final T... items)\n    {\n        if (items.length % 2 != 0)\n        {\n            throw new CoreException(\"Needs to have an even number of arguments\");\n        }\n        final Map<T, T> result = new HashMap<>();\n        for (int i = 0; i < items.length; i += 2)\n        {\n            result.put(items[i], items[i + 1]);\n        }\n        return result;\n    }\n\n    public static Map<String, String> stringMap(final String... items)\n    {\n        return hashMap(items);\n    }\n\n    @SafeVarargs\n    public static <K, V> Map<K, V> withMaps(final boolean rejectCollisions,\n            final Map<K, V>... items)\n    {\n        if (items.length == 0)\n        {\n            return new HashMap<>();\n        }\n        if (items.length == 1)\n        {\n            return items[0];\n        }\n        final Map<K, V> result = new HashMap<>();\n        for (final Map<K, V> item : items)\n        {\n            for (final Map.Entry<K, V> entry : item.entrySet())\n            {\n                if (rejectCollisions && result.containsKey(entry.getKey()))\n                {\n                    throw new CoreException(\"Cannot merge maps! Collision on key.\");\n                }\n                result.put(entry.getKey(), entry.getValue());\n            }\n        }\n        return result;\n    }\n\n    @SafeVarargs\n    public static <K, V> Map<K, V> withMaps(final Map<K, V>... items)\n    {\n        return withMaps(true, items);\n    }\n\n    private Maps()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/MultiIterable.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Iterator;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Iterator made of multiple sub-iterators\n *\n * @author matthieun\n * @param <T>\n *            The type of all the iterators\n */\npublic class MultiIterable<T> implements Iterable<T>\n{\n    private final Iterable<? extends Iterable<? extends T>> iterables;\n\n    public MultiIterable(final Iterable<? extends Iterable<? extends T>> iterables)\n    {\n        this.iterables = iterables;\n    }\n\n    @SafeVarargs\n    public MultiIterable(final Iterable<? extends T>... iterables)\n    {\n        if (iterables.length == 0)\n        {\n            throw new CoreException(\"Cannot have an empty set of Iterables.\");\n        }\n        this.iterables = Iterables.asList(iterables);\n    }\n\n    @Override\n    public Iterator<T> iterator()\n    {\n        return new Iterator<T>()\n        {\n            private final Iterator<? extends Iterable<? extends T>> iterablesIterator = MultiIterable.this.iterables\n                    .iterator();\n            private Iterator<? extends T> currentIterator = this.iterablesIterator.hasNext()\n                    ? this.iterablesIterator.next().iterator()\n                    : null;\n\n            @Override\n            public boolean hasNext()\n            {\n                if (this.currentIterator == null)\n                {\n                    return false;\n                }\n                if (this.currentIterator != null && this.currentIterator.hasNext())\n                {\n                    return true;\n                }\n                while (this.iterablesIterator.hasNext())\n                {\n                    this.currentIterator = this.iterablesIterator.next().iterator();\n                    if (this.currentIterator != null && this.currentIterator.hasNext())\n                    {\n                        return true;\n                    }\n                }\n                return false;\n            }\n\n            @Override\n            public T next()\n            {\n                if (hasNext())\n                {\n                    return this.currentIterator.next();\n                }\n                return null;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/OptionalIterable.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Iterator;\nimport java.util.Optional;\n\n/**\n * Iterator that will skip over the empty {@link Optional}s\n *\n * @param <T>\n *            The type of the {@link Optional} iterator\n * @author cuthbertm\n * @author jklamer\n */\npublic class OptionalIterable<T> implements Iterable<T>\n{\n    private final Iterable<Optional<T>> iterable;\n\n    public OptionalIterable(final Iterable<Optional<T>> iterable)\n    {\n        this.iterable = iterable;\n    }\n\n    @Override\n    public Iterator<T> iterator()\n    {\n        return new Iterator<T>()\n        {\n            private final Iterator<Optional<T>> iterator = OptionalIterable.this.iterable\n                    .iterator();\n            private Optional<T> previousElement = Optional.empty();\n\n            @Override\n            public boolean hasNext()\n            {\n                if (this.previousElement.isPresent())\n                {\n                    return true;\n                }\n                else\n                {\n                    while (this.iterator.hasNext())\n                    {\n                        final Optional<T> current = this.iterator.next();\n                        if (current.isPresent())\n                        {\n                            this.previousElement = current;\n                            return true;\n                        }\n                    }\n                    return false;\n                }\n            }\n\n            @Override\n            public T next()\n            {\n                if (this.previousElement.isPresent())\n                {\n                    final T returnElement = this.previousElement.get();\n                    this.previousElement = Optional.empty();\n                    return returnElement;\n                }\n                else\n                {\n                    while (this.iterator.hasNext())\n                    {\n                        final Optional<T> item = this.iterator.next();\n                        if (item.isPresent())\n                        {\n                            return item.get();\n                        }\n                    }\n                    return null;\n                }\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/ParallelIterable.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * This allows you iterator in parallel over multiple iterators. So simplest example would be 2 int\n * iterators {1, 2} and {3, 4}. First iteration would return {1, 3}, second iteration would return\n * {2, 4}. The Iterable can handle different list types (eg. {1, 2} and {\"one\", \"two\"}) and can\n * handle varying lengths (eg. {1, 2} and {1, 2, 3}). In the example of varying lengths when\n * retrieving the third iterations elements it will return null or Optional.empty(), depending on\n * how you request it, for the 1st list.\n *\n * @author cuthbertm\n */\n@SuppressWarnings(\"rawtypes\")\npublic class ParallelIterable implements Iterable<JoinedCollection>\n{\n    private final List<Iterable> iterables;\n\n    public ParallelIterable(final Iterable... iterables)\n    {\n        this.iterables = Arrays.asList(iterables);\n    }\n\n    public List<Iterator> getIteratorList()\n    {\n        final List<Iterator> iteratorList = new ArrayList<>();\n        this.iterables.forEach(iterator -> iteratorList.add(iterator.iterator()));\n        return iteratorList;\n    }\n\n    @Override\n    public Iterator<JoinedCollection> iterator()\n    {\n        return new Iterator<JoinedCollection>()\n        {\n            private final List<Iterator> iterators = getIteratorList();\n\n            @Override\n            public boolean hasNext()\n            {\n                // this strange line will filter out any of the iterables that do not have any more\n                // elements, and then we will check to see if the result has any more elements.\n                return Iterables.filter(this.iterators, Iterator::hasNext).iterator().hasNext();\n            }\n\n            @Override\n            public JoinedCollection next()\n            {\n                final JoinedCollection joined = new JoinedCollection(this.iterators.size());\n                for (int index = 0; index < this.iterators.size(); index++)\n                {\n                    final Iterator iterator = this.iterators.get(index);\n                    if (iterator.hasNext())\n                    {\n                        joined.set(index, iterator.next());\n                    }\n                }\n                return joined;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/Sets.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic final class Sets\n{\n    @SafeVarargs\n    public static <T> Set<T> hashSet(final T... elements)\n    {\n        final Set<T> result = new HashSet<>();\n        for (final T element : elements)\n        {\n            result.add(element);\n        }\n        return result;\n    }\n\n    @SafeVarargs\n    public static <T extends Comparable<T>> SortedSet<T> treeSet(final T... elements)\n    {\n        final SortedSet<T> result = new TreeSet<>();\n        for (final T element : elements)\n        {\n            result.add(element);\n        }\n        return result;\n    }\n\n    @SafeVarargs\n    public static <V> Set<V> withSets(final boolean rejectCollisions, final Set<V>... items)\n    {\n        if (items.length == 0)\n        {\n            return new HashSet<>();\n        }\n        if (items.length == 1)\n        {\n            return items[0];\n        }\n        final Set<V> result = new HashSet<>();\n        for (final Set<V> item : items)\n        {\n            for (final V entry : item)\n            {\n                if (rejectCollisions && result.contains(entry))\n                {\n                    throw new CoreException(\"Cannot merge sets! Collision on element.\");\n                }\n                result.add(entry);\n            }\n        }\n        return result;\n    }\n\n    @SafeVarargs\n    public static <V> Set<V> withSets(final Set<V>... items)\n    {\n        return withSets(true, items);\n    }\n\n    @SafeVarargs\n    public static <V> SortedSet<V> withSortedSets(final boolean rejectCollisions,\n            final SortedSet<V>... items)\n    {\n        if (items.length == 0)\n        {\n            return new TreeSet<>();\n        }\n        if (items.length == 1)\n        {\n            return items[0];\n        }\n        final SortedSet<V> result = new TreeSet<>();\n        for (final SortedSet<V> item : items)\n        {\n            for (final V entry : item)\n            {\n                if (rejectCollisions && result.contains(entry))\n                {\n                    throw new CoreException(\"Cannot merge sets! Collision on element.\");\n                }\n                result.add(entry);\n            }\n        }\n        return result;\n    }\n\n    @SafeVarargs\n    public static <V> SortedSet<V> withSortedSets(final SortedSet<V>... items)\n    {\n        return withSortedSets(true, items);\n    }\n\n    private Sets()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/ShardBucketCollection.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Array;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.index.RTree;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.utilities.scalars.Counter;\n\n/**\n * A collection wrapper for a set of collections associated with shards containing located items,\n * such as CheckFlags or AtlasEntities. Supports concurrent add, contains, remove. The term bucket\n * is used because of the usability of the collections mostly as storage for various location based\n * sorting tasks. Does not support safe iteration while modifying.\n *\n * @param <LocatedType>\n *            Located item type\n * @param <CollectionType>\n *            Type of the Collection of LocatedType associated with shards\n * @author jklamer\n */\npublic abstract class ShardBucketCollection<LocatedType extends Located & Serializable, CollectionType extends Collection<LocatedType> & Serializable>\n        implements Collection<LocatedType>, Serializable\n{\n\n    /**\n     * A helper class that associates a shard with the index that its collection is at in the\n     * collectionBuckets array.\n     */\n    private static class ShardToCollectionIndex implements Located, Serializable\n    {\n        private static final long serialVersionUID = 4050100671815503794L;\n        private final int index;\n        private final Shard shard;\n\n        ShardToCollectionIndex(final int index, final Shard shard)\n        {\n            this.index = index;\n            this.shard = shard;\n        }\n\n        @Override\n        public Rectangle bounds()\n        {\n            return this.getShard().bounds();\n        }\n\n        public int getIndex()\n        {\n            return this.index;\n        }\n\n        public Shard getShard()\n        {\n            return this.shard;\n        }\n    }\n\n    private static final long serialVersionUID = -7892704554302160820L;\n\n    private final CollectionType[] collectionBuckets;\n    private final RTree<ShardToCollectionIndex> collectionIndex;\n    private final HashMap<Shard, ShardToCollectionIndex> initializedShards = new HashMap<>();\n    private final Rectangle maximumBounds;\n\n    public ShardBucketCollection(final Rectangle maximumBounds, final Integer zoomLevel)\n    {\n        this(maximumBounds, SlippyTile.allTiles(zoomLevel, maximumBounds));\n    }\n\n    public ShardBucketCollection(final Rectangle maximumBounds, final Sharding sharding)\n    {\n        this(maximumBounds, sharding.shards(maximumBounds));\n    }\n\n    /**\n     * Construct the collection by allocating space for a collection for each of the shards and\n     * assigning the index back.\n     * \n     * @param maximumBounds\n     *            maximum bound of the collection\n     * @param shards\n     *            shards to use\n     */\n    @SuppressWarnings(\"unchecked\")\n    private ShardBucketCollection(final Rectangle maximumBounds,\n            final Iterable<? extends Shard> shards)\n    {\n        this.maximumBounds = maximumBounds;\n        this.collectionIndex = new RTree<>();\n        final Counter counter = new Counter();\n        shards.forEach(shardBucket ->\n        {\n            final ShardToCollectionIndex shardToCollectionIndex = new ShardToCollectionIndex(\n                    (int) counter.getValueAndIncrement(), shardBucket);\n            this.collectionIndex.add(shardToCollectionIndex.bounds(), shardToCollectionIndex);\n        });\n        this.collectionBuckets = (CollectionType[]) Array.newInstance(\n                this.initializeBucketCollection().getClass(), (int) counter.getValue());\n    }\n\n    @Override\n    public final boolean add(final LocatedType item)\n    {\n        if (Objects.nonNull(item) && item.bounds().overlaps(this.maximumBounds))\n        {\n            final List<ShardToCollectionIndex> indexes = this.collectionIndex.get(item.bounds());\n            if (indexes.size() == 1)\n            {\n                return this.addFunction(item, this.getOrCreateBucketCollectionAt(indexes.get(0)),\n                        indexes.get(0).getShard());\n            }\n            else if (this.allowMultipleBucketInsertion())\n            {\n                final long addedAmount = indexes.stream()\n                        .filter(index -> this.addFunction(item,\n                                this.getOrCreateBucketCollectionAt(index), index.getShard()))\n                        .count();\n                return addedAmount > 0;\n            }\n            else\n            {\n                final Shard toInsertAt = this.resolveShard(item, indexes.stream()\n                        .map(ShardToCollectionIndex::getShard).collect(Collectors.toList()));\n                final Optional<ShardToCollectionIndex> toAddTo = indexes.stream()\n                        .filter(index -> toInsertAt.equals(index.getShard())).findFirst();\n                return toAddTo\n                        .map(index -> this.addFunction(item,\n                                this.getOrCreateBucketCollectionAt(index), index.getShard()))\n                        .orElse(false);\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean addAll(final Collection<? extends LocatedType> collection)\n    {\n        if (Objects.nonNull(collection))\n        {\n            final long addCount = collection.stream().filter(this::add).count();\n            return addCount > 0;\n        }\n        return false;\n    }\n\n    @Override\n    public void clear()\n    {\n        synchronized (this.collectionBuckets)\n        {\n            for (int i = 0; i < this.collectionBuckets.length; i++)\n            {\n                this.collectionBuckets[i] = null;\n            }\n            this.initializedShards.clear();\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public boolean contains(final Object object)\n    {\n        final Optional<LocatedType> typedItem = this.castToLocatedType(object);\n        if (typedItem.isPresent())\n        {\n            final LocatedType item = typedItem.get();\n            return this.getBucketCollectionsForBounds(item.bounds())\n                    .anyMatch(collection -> collection.contains(item));\n        }\n        return false;\n    }\n\n    @Override\n    public boolean containsAll(final Collection<?> collection)\n    {\n        if (Objects.nonNull(collection))\n        {\n            return collection.stream().allMatch(this::contains);\n        }\n        return false;\n    }\n\n    /**\n     * A stream of only distinct elements in the collection. Useful if allowing multiple bucket\n     * insertion\n     *\n     * @return A disticnt stream of the collection\n     */\n    public Stream<LocatedType> distinctStream()\n    {\n        return this.stream().distinct();\n    }\n\n    /**\n     * @return a stream of all initialized bucket collections\n     */\n    public Stream<CollectionType> getAllBucketCollections()\n    {\n        return Arrays.stream(this.collectionBuckets).filter(Objects::nonNull);\n    }\n\n    /**\n     * Get a map of all Shards to their corresponding collection. This will only return shard\n     * collection entries with initialized collections.\n     *\n     * @return Map of shard to collection\n     */\n    public Map<Shard, CollectionType> getAllShardBucketCollectionPairs()\n    {\n        return this.initializedShards.values().stream()\n                .collect(Collectors.toMap(ShardToCollectionIndex::getShard, this::getCollectionAt));\n    }\n\n    /**\n     * Get the collection for a given shard. It will return optional empty if the shard's collection\n     * isn't initialized\n     *\n     * @param shard\n     *            shard whose collection you want.\n     * @return Optional of the initialized Collection\n     */\n    public Optional<CollectionType> getBucketCollectionForShard(final Shard shard)\n    {\n        return Optional.ofNullable(this.initializedShards.get(shard)).map(this::getCollectionAt)\n                .filter(Objects::nonNull);\n    }\n\n    /**\n     * A stream of all the bucket collects whose associated shard overlaps with the bounds. Note it\n     * will return more than one collection if given a shard bound because the edges of shards\n     * overlap\n     *\n     * @param bounds\n     *            to use\n     * @return stream of a subset of bucket collections\n     */\n    public Stream<CollectionType> getBucketCollectionsForBounds(final Rectangle bounds)\n    {\n        return this.collectionIndex.get(bounds).stream().map(this::getCollectionAt)\n                .filter(Objects::nonNull);\n    }\n\n    /**\n     * Get the max bounds of what located can be in the collection.\n     *\n     * @return rectangle bounds\n     */\n    public Rectangle getMaximumBounds()\n    {\n        return this.maximumBounds;\n    }\n\n    @Override\n    public boolean isEmpty()\n    {\n        return this.size() == 0;\n    }\n\n    @Override\n    public Iterator<LocatedType> iterator()\n    {\n        return this.stream().iterator();\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public boolean remove(final Object object)\n    {\n        final Optional<LocatedType> typedItem = this.castToLocatedType(object);\n        if (typedItem.isPresent())\n        {\n            final LocatedType item = typedItem.get();\n            if (item.bounds().overlaps(this.maximumBounds))\n            {\n                final long removeCount = this.getBucketCollectionsForBounds(item.bounds())\n                        .filter(collection -> collection.remove(item)).count();\n                return removeCount > 0;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean removeAll(final Collection<?> collection)\n    {\n        if (Objects.nonNull(collection))\n        {\n            final long removeCount = collection.stream().filter(this::remove).count();\n            return removeCount > 0;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean retainAll(final Collection<?> collection)\n    {\n        final List<LocatedType> toRemove = this.stream().filter(item -> !collection.contains(item))\n                .collect(Collectors.toList());\n        return this.removeAll(toRemove);\n    }\n\n    @Override\n    public int size()\n    {\n        synchronized (this.collectionBuckets)\n        {\n            return this.getAllBucketCollections().mapToInt(CollectionType::size).sum();\n        }\n    }\n\n    @Override\n    public Stream<LocatedType> stream()\n    {\n        return this.getAllBucketCollections().flatMap(CollectionType::stream);\n    }\n\n    @Override\n    public Object[] toArray()\n    {\n        Object[] toReturn = new Object[0];\n        synchronized (this.collectionBuckets)\n        {\n            final Iterator<CollectionType> bucketIterator = this.getAllBucketCollections()\n                    .iterator();\n            while (bucketIterator.hasNext())\n            {\n                toReturn = ArrayUtils.addAll(toReturn, bucketIterator.next().toArray());\n            }\n        }\n        return toReturn;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T1> T1[] toArray(final T1[] otherArray)\n    {\n        return ArrayUtils.addAll(otherArray, (T1[]) this.toArray());\n    }\n\n    /**\n     * To add the item into the collection in a special way or dependent on the shard, this function\n     * can be overridden. The return contract is the same of {@link Collection}'s add. This function\n     * must be deterministic.\n     *\n     * @param item\n     *            to add\n     * @param collection\n     *            to add to\n     * @param shard\n     *            shard associated with the collection\n     * @return true if the collection has been added to successfully and changed as a result, false\n     *         otherwise\n     */\n    protected boolean addFunction(final LocatedType item, final CollectionType collection,\n            final Shard shard)\n    {\n        return collection.add(item);\n    }\n\n    /**\n     * @return true if items are allowed to be in multiple buckets. False otherwise\n     */\n    protected abstract boolean allowMultipleBucketInsertion();\n\n    /**\n     * The collection should be agnostic to the shard. Deciding the collection to insert into and\n     * how to insert into based on the shard should be handled by resolveShard and addFunction\n     * respectively.\n     * \n     * @return an intialized empty bucket collection.\n     */\n    protected abstract CollectionType initializeBucketCollection();\n\n    /**\n     * Resolve which of multiple overlapping shards. Note these shards overlap due to a bounds call,\n     * agnostic of the items geometry.\n     * \n     * @param item\n     *            located item to insert\n     * @param possibleBuckets\n     *            possible buckets for the item to work into\n     * @return Shard whose collection you should add to.\n     */\n    protected Shard resolveShard(final LocatedType item,\n            final List<? extends Shard> possibleBuckets)\n    {\n        throw new UnsupportedOperationException(\n                \"Implement this method when not allowing multiple bucket insertion\");\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Optional<LocatedType> castToLocatedType(final Object object)\n    {\n        try\n        {\n            return Optional.ofNullable(object).map(cast -> (LocatedType) cast);\n        }\n        catch (final ClassCastException e)\n        {\n            return Optional.empty();\n        }\n    }\n\n    private void createBucketCollectionAt(final ShardToCollectionIndex index)\n    {\n        synchronized (this.collectionBuckets)\n        {\n            if (Objects.isNull(this.collectionBuckets[index.getIndex()]))\n            {\n                this.collectionBuckets[index.getIndex()] = this.initializeBucketCollection();\n                this.initializedShards.put(index.getShard(), index);\n            }\n        }\n    }\n\n    private CollectionType getCollectionAt(final ShardToCollectionIndex index)\n    {\n        synchronized (this.collectionBuckets)\n        {\n            return this.collectionBuckets[index.getIndex()];\n        }\n    }\n\n    private CollectionType getOrCreateBucketCollectionAt(final ShardToCollectionIndex index)\n    {\n        final CollectionType collection = this.getCollectionAt(index);\n        if (Objects.isNull(collection))\n        {\n            this.createBucketCollectionAt(index);\n            return this.getCollectionAt(index);\n        }\n        else\n        {\n            return collection;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/StreamIterable.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\n/**\n * An {@link Iterable} that offers similar methods as the {@link Stream} API. To construct one, use\n * {@link Iterables}:\n * <p>\n * <code>\n * Iterables.stream(someIterable).map(...).filter(...).collect();\n * </code> Note: StreamIterable is not thread safe with parallelization usage.\n *\n * @author matthieun\n * @param <T>\n *            The type of the {@link Iterable}\n */\npublic class StreamIterable<T> implements Iterable<T>\n{\n    private final Iterable<T> source;\n    private boolean parallel = false;\n\n    protected StreamIterable(final Iterable<T> source)\n    {\n        this.source = source;\n    }\n\n    /**\n     * Construct a new StreamIterable.\n     *\n     * @param source\n     *            The source iterable to construct the StreamIterable from\n     * @param parallel\n     *            Controls whether to use parallelization or not when streaming\n     */\n    protected StreamIterable(final Iterable<T> source, final boolean parallel)\n    {\n        this.source = source;\n        this.parallel = parallel;\n    }\n\n    /**\n     * Test whether all elements from iterable match the given predicate.\n     *\n     * @param predicate\n     *            Predicate to test\n     * @return {@code true} when given predicate is true for all entities in iterable, else false\n     */\n    public boolean allMatch(final Predicate<T> predicate)\n    {\n        return StreamSupport.stream(this.source.spliterator(), this.parallel).allMatch(predicate);\n    }\n\n    /**\n     * Test whether any of the elements from iterable matches the given predicate\n     *\n     * @param predicate\n     *            Predicate to test\n     * @return {@code true} when given predicate is true for any one entity in iterable, else false\n     */\n    public boolean anyMatch(final Predicate<T> predicate)\n    {\n        return StreamSupport.stream(this.source.spliterator(), this.parallel).anyMatch(predicate);\n    }\n\n    /**\n     * @return The original {@link Iterable} from this {@link StreamIterable}\n     */\n    public Iterable<T> collect()\n    {\n        return this.source;\n    }\n\n    /**\n     * @return The original {@link Iterable} from this {@link StreamIterable}, collected into a\n     *         {@link List}\n     */\n    public List<T> collectToList()\n    {\n        return Iterables.asList(this.source);\n    }\n\n    /**\n     * @return The original {@link Iterable} from this {@link StreamIterable}, collected into a\n     *         {@link Set}\n     */\n    public Set<T> collectToSet()\n    {\n        return Iterables.asSet(this.source);\n    }\n\n    /**\n     * @return The original {@link Iterable} from this {@link StreamIterable}, collected into a\n     *         {@link SortedSet}\n     */\n    public SortedSet<T> collectToSortedSet()\n    {\n        return Iterables.asSortedSet(this.source);\n    }\n\n    /**\n     * Disable parallelization in streams from this StreamIterator\n     *\n     * @return The StreamIterator with parallelization disabled\n     */\n    public StreamIterable<T> disableParallelization()\n    {\n        this.parallel = false;\n        return this;\n    }\n\n    /**\n     * Enable parallelization in streams from this StreamIterator\n     *\n     * @return The StreamIterator with parallelization enabled\n     */\n    public StreamIterable<T> enableParallelization()\n    {\n        this.parallel = true;\n        return this;\n    }\n\n    /**\n     * Filter an {@link Iterable}\n     *\n     * @param filter\n     *            The filter function\n     * @return The filtered {@link Iterable} as a {@link StreamIterable}\n     */\n    public StreamIterable<T> filter(final Predicate<T> filter)\n    {\n        return new StreamIterable<>(Iterables.filter(this.source, filter), this.parallel);\n    }\n\n    /**\n     * Filter an {@link Iterable} using a set of known elements to filter and an idenfitier function\n     *\n     * @param filterSet\n     *            The set of IdentifierTypes to filter\n     * @param identifier\n     *            The function mapping an element of type T to its identifier of type IdentifierType\n     * @param <IdentifierType>\n     *            The type for the object identifier for elements of the {@link Iterable}\n     * @return The filtered {@link Iterable} as a {@link StreamIterable}\n     */\n    public <IdentifierType> StreamIterable<T> filter(final Set<IdentifierType> filterSet,\n            final Function<T, IdentifierType> identifier)\n    {\n        return new StreamIterable<>(new FilteredIterable<>(this.source, filterSet, identifier),\n                this.parallel);\n    }\n\n    public Optional<T> firstMatching(final Predicate<T> filter)\n    {\n        return Iterables.firstMatching(this.source, filter);\n    }\n\n    /**\n     * Flat Map an {@link Iterable}. This means each input item can return one to many results.\n     *\n     * @param <V>\n     *            The type of the output {@link Iterable}\n     * @param flatMap\n     *            The function to flat map\n     * @return The flat mapped {@link Iterable}\n     */\n    public <V> StreamIterable<V> flatMap(final Function<T, Iterable<? extends V>> flatMap)\n    {\n        return new StreamIterable<>(Iterables.translateMulti(this.source, flatMap), this.parallel);\n    }\n\n    @Override\n    public Iterator<T> iterator()\n    {\n        return this.source.iterator();\n    }\n\n    public Optional<T> lastMatching(final Predicate<T> filter)\n    {\n        return Iterables.lastMatching(this.source, filter);\n    }\n\n    /**\n     * Map an {@link Iterable} to a value\n     *\n     * @param <V>\n     *            The new type of the output {@link Iterable}\n     * @param map\n     *            The map function\n     * @return The mapped {@link Iterable}\n     */\n    public <V> StreamIterable<V> map(final Function<T, V> map)\n    {\n        return new StreamIterable<>(Iterables.translate(this.source, map), this.parallel);\n    }\n\n    /**\n     * Truncate an {@link Iterable} from start and end\n     *\n     * @param startIndex\n     *            The index before which to truncate from the start\n     * @param indexFromEnd\n     *            The index after which to truncate from the end\n     * @return The truncated {@link Iterable}\n     */\n    public StreamIterable<T> truncate(final int startIndex, final int indexFromEnd)\n    {\n        return new StreamIterable<>(Iterables.truncate(this.source, startIndex, indexFromEnd),\n                this.parallel);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/StringList.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.regex.RegexUtils;\n\n/**\n * {@link List} of {@link String}s with convenience methods\n *\n * @author matthieun\n */\npublic class StringList implements Iterable<String>, Serializable\n{\n    private static final long serialVersionUID = -7923796535827613632L;\n    private final List<String> list;\n\n    public static StringList split(final String item, final String separator)\n    {\n        return split(item, separator, 0);\n    }\n\n    /**\n     * Split the string item up to limit pieces. Because the separator is wrapped in regex quotes\n     * (\\Q and \\E), this method does not support regex.\n     *\n     * @param item\n     *            The string to split\n     * @param separator\n     *            A string used to separate the input item\n     * @param limit\n     *            The limit parameter controls the number of times the pattern is applied and\n     *            therefore affects the length of the resulting array. If the limit n is greater\n     *            than zero then the pattern will be applied at most n - 1 times, the array's length\n     *            will be no greater than n, and the array's last entry will contain all input\n     *            beyond the last matched delimiter.\n     * @return A StringList object\n     */\n    public static StringList split(final String item, final String separator, final int limit)\n    {\n        final Pattern compiledPattern = RegexUtils.getCompiledPattern(Pattern.quote(separator));\n        return new StringList(Iterables.asList(compiledPattern.split(item, limit)));\n    }\n\n    public static StringList splitByRegex(final String item, final String separator)\n    {\n        return splitByRegex(item, separator, 0);\n    }\n\n    /**\n     * Split the string item up to limit pieces, supports regex.\n     *\n     * @param item\n     *            The string to split\n     * @param regex\n     *            A string used to separate the input item\n     * @param limit\n     *            The limit parameter controls the number of times the pattern is applied and\n     *            therefore affects the length of the resulting array. If the limit n is greater\n     *            than zero then the pattern will be applied at most n - 1 times, the array's length\n     *            will be no greater than n, and the array's last entry will contain all input\n     *            beyond the last matched delimiter.\n     * @return A StringList object\n     */\n    public static StringList splitByRegex(final String item, final String regex, final int limit)\n    {\n        final Pattern compiledPattern = RegexUtils.getCompiledPattern(regex);\n        return new StringList(Iterables.asList(compiledPattern.split(item, limit)));\n    }\n\n    public StringList()\n    {\n        this.list = new ArrayList<>();\n    }\n\n    public StringList(final Iterable<String> list)\n    {\n        this.list = Iterables.asList(list);\n    }\n\n    public StringList(final List<String> list)\n    {\n        this.list = list;\n    }\n\n    public StringList(final String... array)\n    {\n        this.list = Iterables.asList(array);\n    }\n\n    public void add(final Object string)\n    {\n        this.list.add(String.valueOf(string));\n    }\n\n    public void add(final String string)\n    {\n        this.list.add(string);\n    }\n\n    public void addAll(final Iterable<String> split)\n    {\n        split.forEach(this::add);\n    }\n\n    public boolean contains(final String item)\n    {\n        for (final String candidate : this)\n        {\n            if (candidate.equals(item))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public Optional<String> first()\n    {\n        if (this.size() <= 0)\n        {\n            return Optional.empty();\n        }\n        return Optional.of(this.get(0));\n    }\n\n    public synchronized String get(final int index)\n    {\n        if (index >= size())\n        {\n            throw new CoreException(\"Cannot get item out of bounds: {} in size = {}\", index,\n                    size());\n        }\n        return this.list.get(index);\n    }\n\n    public List<String> getUnderlyingList()\n    {\n        return this.list;\n    }\n\n    public boolean isEmpty()\n    {\n        return this.size() == 0;\n    }\n\n    @Override\n    public Iterator<String> iterator()\n    {\n        return this.list.iterator();\n    }\n\n    public String join(final String separator)\n    {\n        final StringBuilder result = new StringBuilder();\n        int index = 0;\n        for (final String string : this)\n        {\n            result.append(string);\n            index++;\n            if (index < size())\n            {\n                result.append(separator);\n            }\n        }\n        return result.toString();\n    }\n\n    public Optional<String> last()\n    {\n        if (this.size() <= 0)\n        {\n            return Optional.empty();\n        }\n        return Optional.of(this.get(size() - 1));\n    }\n\n    public void remove(final int index)\n    {\n        this.list.remove(index);\n    }\n\n    public int size()\n    {\n        return this.list.size();\n    }\n\n    /**\n     * @param item\n     *            The item to test for\n     * @return True if the item starts with some element in this list.\n     */\n    public boolean startsWithContains(final String item)\n    {\n        for (final String candidate : this)\n        {\n            if (item.startsWith(candidate))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public Stream<String> stream()\n    {\n        return this.list.stream();\n    }\n\n    public String[] toArray()\n    {\n        final String[] result = new String[this.size()];\n        for (int index = 0; index < this.size(); index++)\n        {\n            result[index] = this.get(index);\n        }\n        return result;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.list.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/SubIterable.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.Queue;\n\n/**\n * Takes an {@link Iterable} and truncates some items at the beginning and some items at the end.\n *\n * @author matthieun\n * @param <Type>\n *            The type of the {@link Iterable}\n */\npublic class SubIterable<Type> implements Iterable<Type>\n{\n    private final Iterable<Type> source;\n    private final int startIndex;\n    private final int indexFromEnd;\n\n    public SubIterable(final Iterable<Type> source, final int startIndex, final int indexFromEnd)\n    {\n        this.source = source;\n        this.startIndex = startIndex > 0 ? startIndex : 0;\n        this.indexFromEnd = indexFromEnd > 0 ? indexFromEnd : 0;\n    }\n\n    @Override\n    public Iterator<Type> iterator()\n    {\n        return new Iterator<Type>()\n        {\n            private int index = 0;\n            private final Iterator<Type> sourceIterator = SubIterable.this.source.iterator();\n            private final Queue<Type> lookAheadStore = new LinkedList<>();\n\n            @Override\n            public boolean hasNext()\n            {\n                while (this.index < SubIterable.this.startIndex)\n                {\n                    if (this.sourceIterator.hasNext())\n                    {\n                        this.sourceIterator.next();\n                    }\n                    this.index++;\n                }\n                while (this.sourceIterator.hasNext()\n                        && this.lookAheadStore.size() < SubIterable.this.indexFromEnd)\n                {\n                    this.lookAheadStore.add(this.sourceIterator.next());\n                }\n                if (this.lookAheadStore.size() == SubIterable.this.indexFromEnd)\n                {\n                    return this.sourceIterator.hasNext();\n                }\n                else\n                {\n                    return false;\n                }\n            }\n\n            @Override\n            public Type next()\n            {\n                if (hasNext())\n                {\n                    final Type result = this.lookAheadStore.isEmpty() ? this.sourceIterator.next()\n                            : this.lookAheadStore.poll();\n                    this.index++;\n                    return result;\n                }\n                else\n                {\n                    return null;\n                }\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/UnmodifiableSortedMapCollector.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.Set;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\nimport java.util.function.BiConsumer;\nimport java.util.function.BinaryOperator;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collector;\n\nimport com.google.common.collect.Ordering;\n\n/**\n * Converts a stream of objects into an immutable map\n *\n * @author cstaylor\n * @param <T>\n *            the type of incoming objects we want to map\n * @param <K>\n *            the type of keys stored in the map\n * @param <U>\n *            the values stored in the map\n */\npublic class UnmodifiableSortedMapCollector<T, K extends Comparable<K>, U>\n        implements Collector<T, SortedMap<K, U>, SortedMap<K, U>>\n{\n    private final Function<? super T, ? extends K> keyMapper;\n    private final Function<? super T, ? extends U> valueMapper;\n\n    public UnmodifiableSortedMapCollector(final Function<? super T, ? extends K> keyMapper,\n            final Function<? super T, ? extends U> valueMapper)\n    {\n        this.keyMapper = keyMapper;\n        this.valueMapper = valueMapper;\n    }\n\n    @Override\n    public BiConsumer<SortedMap<K, U>, T> accumulator()\n    {\n        return (builder, item) -> builder.put(this.keyMapper.apply(item),\n                this.valueMapper.apply(item));\n    }\n\n    @Override\n    public Set<Characteristics> characteristics()\n    {\n        return EnumSet.of(Characteristics.UNORDERED);\n    }\n\n    @Override\n    public BinaryOperator<SortedMap<K, U>> combiner()\n    {\n        return (builder1, builder2) ->\n        {\n            builder1.putAll(builder2);\n            return builder1;\n        };\n    }\n\n    @Override\n    public Function<SortedMap<K, U>, SortedMap<K, U>> finisher()\n    {\n        return original -> Collections.unmodifiableSortedMap(original);\n    }\n\n    @Override\n    public Supplier<SortedMap<K, U>> supplier()\n    {\n        return () -> new TreeMap<>(Ordering.natural());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/collections/UnmodifiableSortedSetCollector.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.BiConsumer;\nimport java.util.function.BinaryOperator;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collector;\n\nimport com.google.common.collect.Ordering;\n\n/**\n * Converts a stream of objects into an immutable set\n *\n * @author cstaylor\n * @param <U>\n *            the values stored in the map\n */\npublic class UnmodifiableSortedSetCollector<U extends Comparable<U>>\n        implements Collector<U, SortedSet<U>, SortedSet<U>>\n{\n    @Override\n    public BiConsumer<SortedSet<U>, U> accumulator()\n    {\n        return (builder, item) -> builder.add(item);\n    }\n\n    @Override\n    public Set<Characteristics> characteristics()\n    {\n        return EnumSet.of(Characteristics.UNORDERED);\n    }\n\n    @Override\n    public BinaryOperator<SortedSet<U>> combiner()\n    {\n        return (builder1, builder2) ->\n        {\n            builder1.addAll(builder2);\n            return builder1;\n        };\n    }\n\n    @Override\n    public Function<SortedSet<U>, SortedSet<U>> finisher()\n    {\n        return set -> Collections.unmodifiableSortedSet(set);\n    }\n\n    @Override\n    public Supplier<SortedSet<U>> supplier()\n    {\n        return () -> new TreeSet<>(Ordering.natural());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/ActiveModuleIndexWriter.java",
    "content": "package org.openstreetmap.atlas.utilities.command;\n\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\n\nimport com.google.common.base.Objects;\n\n/**\n * @author lcram\n */\npublic class ActiveModuleIndexWriter\n{\n    private static final String VERBOSE = \"--verbose\";\n\n    // Use ASCII record separator as delimiter\n    private static final String DELIMITER = Character.toString((char) 0x1E);\n\n    private final boolean useVerbose;\n    private final String outputPath;\n\n    public static void main(final String[] args)\n    {\n        String outputPath = null;\n        String verboseFlag = null;\n        if (args.length < 1)\n        {\n            throw new CoreException(\"Missing required output path argument\");\n        }\n        else if (args.length == 1)\n        {\n            outputPath = args[0];\n        }\n        else\n        {\n            outputPath = args[0];\n            verboseFlag = args[1];\n        }\n        if (Objects.equal(VERBOSE, verboseFlag))\n        {\n            new ActiveModuleIndexWriter(outputPath, true).printLookupTable();\n        }\n        else\n        {\n            new ActiveModuleIndexWriter(outputPath, false).printLookupTable();\n        }\n\n    }\n\n    public ActiveModuleIndexWriter(final String outputPath, final boolean useVerbose)\n    {\n        this.outputPath = outputPath;\n        this.useVerbose = useVerbose;\n    }\n\n    private void diagnosticIfVerbose(final String message)\n    {\n        if (this.useVerbose)\n        {\n            System.out.println(message); // NOSONAR\n        }\n    }\n\n    private void printLookupTable()\n    {\n        final Set<AbstractAtlasShellToolsCommand> commands = ReflectionUtilities\n                .getSubcommandInstances();\n        final Set<String> namesWeHaveAlreadySeen = new HashSet<>();\n\n        try (PrintWriter printWriter = new PrintWriter(new FileWriter(this.outputPath)))\n        {\n            // print a line break\n            diagnosticIfVerbose(\"\");\n\n            for (final AbstractAtlasShellToolsCommand command : commands)\n            {\n                diagnosticIfVerbose(\"Found command definition in \" + command.getClass().getName());\n                diagnosticIfVerbose(\"Validating command definition...\");\n\n                // validate the command name and description\n                command.throwIfInvalidNameOrDescription();\n\n                // Validate the command options/args/manpage - will throw if something is awry\n                command.registerOptionsAndArguments();\n                command.registerManualPageSections();\n\n                diagnosticIfVerbose(\"Generating index entry...\");\n                final StringBuilder builder = new StringBuilder();\n                String name = command.getCommandName();\n                String nameWithSuffix = name;\n                int uniqueSuffix = 2;\n\n                while (namesWeHaveAlreadySeen.contains(nameWithSuffix))\n                {\n                    nameWithSuffix = name + uniqueSuffix;\n                    uniqueSuffix++;\n                }\n                name = nameWithSuffix;\n\n                builder.append(name);\n                namesWeHaveAlreadySeen.add(name);\n                builder.append(DELIMITER);\n                builder.append(command.getClass().getName());\n                builder.append(DELIMITER);\n                builder.append(command.getSimpleDescription());\n                printWriter.println(builder.toString());\n                diagnosticIfVerbose(\"Command \" + command.getCommandName() + \" registered OK.\");\n\n                // print a line break\n                diagnosticIfVerbose(\"\");\n            }\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"Could not write index\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/AtlasShellToolsException.java",
    "content": "package org.openstreetmap.atlas.utilities.command;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * A special core exception for cases that should never happen. If users see this, it's a bug!\n *\n * @author lcram\n */\npublic class AtlasShellToolsException extends CoreException\n{\n    private static final long serialVersionUID = -2538051525989047548L;\n\n    public AtlasShellToolsException()\n    {\n        super(\"This should never happen - you found a bug! Please report this stack trace and the command line that caused it.\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/ReflectionUtilities.java",
    "content": "package org.openstreetmap.atlas.utilities.command;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AtlasShellToolsMarkerInterface;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfoList;\nimport io.github.classgraph.ScanResult;\n\n/**\n * @author lcram\n */\npublic final class ReflectionUtilities\n{\n    @SuppressWarnings(\"unchecked\")\n    public static Set<AbstractAtlasShellToolsCommand> getSubcommandInstances()\n    {\n        final List<Class<? extends AtlasShellToolsMarkerInterface>> subcommandClasses = new ArrayList<>();\n        final Set<AbstractAtlasShellToolsCommand> instantiatedCommands = new HashSet<>();\n        try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan())\n        {\n            final ClassInfoList classInfoList = scanResult\n                    .getClassesImplementing(AtlasShellToolsMarkerInterface.class.getName());\n            classInfoList.loadClasses().forEach(klass -> subcommandClasses\n                    .add((Class<? extends AtlasShellToolsMarkerInterface>) klass));\n        }\n        subcommandClasses.stream().forEach(klass -> instantiateSubcommand(klass.getName())\n                .ifPresent(instantiatedCommands::add));\n        return instantiatedCommands;\n    }\n\n    private static Optional<AbstractAtlasShellToolsCommand> instantiateSubcommand(\n            final String classname)\n    {\n        final Class<?> subcommandClass;\n        try\n        {\n            subcommandClass = Class.forName(classname);\n        }\n        catch (final ClassNotFoundException exception)\n        {\n            throw new CoreException(\"Class {} was not found\", classname, exception);\n        }\n\n        if (Modifier.isAbstract(subcommandClass.getModifiers()))\n        {\n            return Optional.empty();\n        }\n\n        final Constructor<?> constructor;\n        try\n        {\n            constructor = subcommandClass.getConstructor();\n        }\n        catch (final NoSuchMethodException exception)\n        {\n            throw new CoreException(\"Class {} does not have a matching constructor\", classname,\n                    exception);\n        }\n        catch (final SecurityException exception)\n        {\n            throw new CoreException(\"Error instantiating class {}\", classname, exception);\n        }\n\n        final AbstractAtlasShellToolsCommand subcommand;\n        try\n        {\n            subcommand = (AbstractAtlasShellToolsCommand) constructor.newInstance(new Object[] {}); // NOSONAR\n        }\n        catch (final ClassCastException exception)\n        {\n            throw new CoreException(\"Class {} not a subtype of {}\", classname,\n                    AbstractAtlasShellToolsCommand.class.getName(), exception);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Error instantiating class {}\", classname, exception);\n        }\n\n        return Optional.of(subcommand);\n    }\n\n    private ReflectionUtilities()\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/abstractcommand/AbstractAtlasShellToolsCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.abstractcommand;\n\nimport java.io.InputStream;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.command.documentation.DocumentationFormatter;\nimport org.openstreetmap.atlas.utilities.command.documentation.DocumentationRegistrar;\nimport org.openstreetmap.atlas.utilities.command.documentation.PagerHelper;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.SimpleOptionAndArgumentParser;\nimport org.openstreetmap.atlas.utilities.command.parsing.SimpleOptionAndArgumentParser.SimpleOption;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.AmbiguousAbbreviationException;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.UnknownOptionException;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.UnparsableContextException;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYStringBuilder;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A partial implementation of an Atlas Shell Tools command. Contains significant functionality to\n * aid in command development, including some builtin options.\n *\n * @author lcram\n * @author bbreithaupt\n */\npublic abstract class AbstractAtlasShellToolsCommand implements AtlasShellToolsMarkerInterface\n{\n    /**\n     * DEFAULT_CONTEXT is the integer value of a command's default context. Subclasses may register\n     * additional option parser contexts. They should use this value when performing contextual\n     * operations on the default context. See {@link SimpleOptionAndArgumentParser} for more\n     * information about contexts.\n     */\n    public static final int DEFAULT_CONTEXT = 3;\n\n    private static final String LINE_SEPARATOR = \"line.separator\";\n    private static final Logger logger = LoggerFactory\n            .getLogger(AbstractAtlasShellToolsCommand.class);\n\n    /**\n     * Until Java supports the ability to do granular TTY configuration checking thru an interface\n     * like isatty(3), we must rely on special tail arguments. An external wrapper (bash, perl,\n     * etc.) can do the necessary TTY config check, and then pass these sentinels to tell the\n     * subcommand if it should use special formatting. A ticket to support better TTY in Java\n     * checking has been open for years, with no avail:\n     * https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4099017. In addition to the tail\n     * arguments declared here, this command also expects a TTY maximum column value (a single\n     * integer). See {@link AbstractAtlasShellToolsCommand#runSubcommandAndExit(String...)} for the\n     * code that unpacks the tail arguments.\n     */\n    private static final String JAVA_COLOR_STDOUT = \"___atlas-shell-tools_color_stdout_SPECIALARGUMENT___\";\n    private static final String JAVA_NO_COLOR_STDOUT = \"___atlas-shell-tools_nocolor_stdout_SPECIALARGUMENT___\";\n    private static final String JAVA_COLOR_STDERR = \"___atlas-shell-tools_color_stderr_SPECIALARGUMENT___\";\n    private static final String JAVA_NO_COLOR_STDERR = \"___atlas-shell-tools_nocolor_stderr_SPECIALARGUMENT___\";\n    private static final String JAVA_USE_PAGER = \"___atlas-shell-tools_use_pager_SPECIALARGUMENT___\";\n    private static final String JAVA_NO_USE_PAGER = \"___atlas-shell-tools_no_use_pager_SPECIALARGUMENT___\";\n    private static final String JAVA_MARKER_SENTINEL = \"___atlas-shell-tools_LAST_ARG_MARKER_SENTINEL___\";\n    private static final int NUMBER_SENTINELS = 5;\n    private static final int STDOUT_COLOR_OFFSET = 5;\n    private static final int STDERR_COLOR_OFFSET = 4;\n    private static final int PAGER_OFFSET = 3;\n    private static final int TERMINAL_COLUMN_OFFSET = 2;\n\n    /*\n     * The following options are default-registered.\n     */\n    private static final String VERBOSE_OPTION_LONG = \"verbose\";\n    private static final Character VERBOSE_OPTION_SHORT = 'v';\n    private static final String VERBOSE_OPTION_DESCRIPTION = \"Show verbose output messages.\";\n\n    private static final String HELP_OPTION_LONG = \"help\";\n    private static final Character HELP_OPTION_SHORT = 'h';\n    private static final String HELP_OPTION_DESCRIPTION = \"Show this help menu.\";\n\n    private static final String VERSION_OPTION_LONG = \"version\";\n    private static final Character VERSION_OPTION_SHORT = 'V';\n    private static final String VERSION_OPTION_DESCRIPTION = \"Print the command version and exit.\";\n\n    private static final int HELP_OPTION_CONTEXT = 1;\n    private static final int VERSION_OPTION_CONTEXT = 2;\n\n    /*\n     * This is a hack option that can be supplied by callers to override the option parser's default\n     * behaviour. If present, it is stripped from ARGS before reaching the option parser.\n     */\n    private static final String IGNORE_UNKNOWN_OPTIONS = \"--ignore-unknown-options\";\n\n    /*\n     * Maximum allowed column width. If the user's terminal is very wide, we don't want to display\n     * documentation all the way to the max column, since it may become hard to read.\n     */\n    private static final int MAXIMUM_ALLOWED_COLUMN = 225;\n\n    private final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n    private final DocumentationRegistrar registrar = new DocumentationRegistrar();\n\n    /*\n     * These variables control terminal-related parameters. Their values can be set by the caller\n     * from the command line using sentinal arguments. Try running the atlas-shell-tools \"atlas\"\n     * wrapper perl program with the '--debug' option to see how this works. Also, see the method\n     * \"unpackSentinelArguments\".\n     */\n    private boolean useColorStdout = false;\n    private boolean useColorStderr = false;\n    private boolean usePager = false;\n    private int maximumColumn = DocumentationFormatter.DEFAULT_MAXIMUM_COLUMN;\n    private String version = \"default_version_value\";\n    private boolean ignoreUnknownOptions = false;\n\n    /*\n     * Why are we using PrintStreams instead of a logger? Well, we aren't totally. We still\n     * encourage the use of a logger with various levels for diagnostic messages (see the many\n     * examples of this in subclass implementations). However, for simplicity's sake we use\n     * System.out/err for direct messages intended for the user to read on the command line. This\n     * makes it easier for users to adjust their log configuration to show the desired level of\n     * diagnostics without affecting the actual command outputs. We are declaring explicit stream\n     * variables here so that client classes (i.e. unit tests) can override the streams for testing\n     * purposes.\n     */\n    /**\n     * See {@link AbstractAtlasShellToolsCommand#setNewOutStream(PrintStream)}.\n     */\n    private PrintStream outStream = System.out; // NOSONAR\n    /**\n     * See {@link AbstractAtlasShellToolsCommand#setNewErrStream(PrintStream)}.\n     */\n    private PrintStream errStream = System.err; // NOSONAR\n    /**\n     * See {@link AbstractAtlasShellToolsCommand#setNewInStream(InputStream)}.\n     */\n    private InputStream inStream = System.in;\n    /**\n     * The default value here is {@link FileSystems#getDefault()}. See\n     * {@link AbstractAtlasShellToolsCommand#setNewFileSystem(FileSystem)} for more information.\n     */\n    private FileSystem fileSystem = FileSystems.getDefault();\n    /**\n     * By default the command environment will be given by {@link System#getenv()}. See\n     * {@link AbstractAtlasShellToolsCommand#setNewEnvironment(Map)} for more information.\n     */\n    private Map<String, String> environment = null;\n\n    /**\n     * Add a section to this command's manual page. The section name will be made all capitalized.\n     * Also, use the supplied input stream to read contents into the section.\n     *\n     * @param section\n     *            the name of the section\n     * @param sectionResourceFileStream\n     *            an input stream to the section resource file (easily specified like\n     *            CommandName.class.getResourceAsStream(\"resourcefile.txt\"))\n     */\n    public void addManualPageSection(final String section,\n            final InputStream sectionResourceFileStream)\n    {\n        this.registrar.addManualPageSection(section, sectionResourceFileStream);\n    }\n\n    /**\n     * Execute the command logic. Subclasses of {@link AbstractAtlasShellToolsCommand} must\n     * implement this method, but in general it should not be called directly. See\n     * {@link AbstractAtlasShellToolsCommand#runSubcommandAndExit(String...)}.\n     *\n     * @return the return code of the command\n     */\n    public abstract int execute();\n\n    /**\n     * Force the current command to exit with a given exit code.\n     *\n     * @param exitCode\n     *            the exit code\n     */\n    public void forceExit(final int exitCode)\n    {\n        System.exit(exitCode);\n    }\n\n    /**\n     * The simple name of the command. This should be easy to type for ease of command line use.\n     *\n     * @return the simple name of the command\n     */\n    public abstract String getCommandName();\n\n    /**\n     * Get a {@link CommandOutputDelegate} bound to this {@link AbstractAtlasShellToolsCommand}.\n     *\n     * @return a delegate bound to this command\n     */\n    public CommandOutputDelegate getCommandOutputDelegate()\n    {\n        return new CommandOutputDelegate(this);\n    }\n\n    /**\n     * Get the value of an environment variable. Command implementations should always defer to this\n     * method rather than {@link System#getenv()}, since unit tests may be utilizing\n     * {@link AbstractAtlasShellToolsCommand#setNewEnvironment(Map)} to inject a custom environment\n     * for testing purposes.\n     *\n     * @param name\n     *            the name of the environment variable\n     * @return the string value of the variable, or {@code null} if the variable is not defined in\n     *         the environment\n     */\n    public String getEnvironmentValue(final String name)\n    {\n        if (this.environment == null)\n        {\n            return System.getenv(name);\n        }\n        return this.environment.getOrDefault(name, null);\n    }\n\n    /**\n     * Get the {@link PrintStream} for this command's err stream.\n     *\n     * @return the {@link PrintStream} for this command's err stream.\n     */\n    public PrintStream getErrStream()\n    {\n        return this.errStream;\n    }\n\n    /**\n     * Get the {@link FileSystem} for this command.\n     *\n     * @return the {@link FileSystem} for this command.\n     */\n    public FileSystem getFileSystem()\n    {\n        return this.fileSystem;\n    }\n\n    /**\n     * Get the {@link InputStream} for this command.\n     *\n     * @return the {@link InputStream} for this command.\n     */\n    public InputStream getInStream()\n    {\n        return this.inStream;\n    }\n\n    /**\n     * Get the maximum column of the current terminal.\n     *\n     * @return the maximum column\n     */\n    public int getMaximumColumn()\n    {\n        return this.maximumColumn;\n    }\n\n    /**\n     * Get an {@link OptionAndArgumentDelegate} bound to this\n     * {@link AbstractAtlasShellToolsCommand}.\n     *\n     * @return a fetcher bound to this command\n     */\n    public OptionAndArgumentDelegate getOptionAndArgumentDelegate()\n    {\n        return new OptionAndArgumentDelegate(this);\n    }\n\n    /**\n     * Get the {@link PrintStream} for this command's out stream.\n     *\n     * @return the {@link PrintStream} for this command's out stream.\n     */\n    public PrintStream getOutStream()\n    {\n        return this.outStream;\n    }\n\n    /**\n     * A simple description of the command. It should be brief - see the NAME section of any man\n     * page for an example.\n     *\n     * @return the description\n     */\n    public abstract String getSimpleDescription();\n\n    /**\n     * Get a {@link TTYStringBuilder} that is configured to respect the color settings of stderr.\n     *\n     * @return the configured {@link TTYStringBuilder}\n     */\n    public TTYStringBuilder getTTYStringBuilderForStderr()\n    {\n        return new TTYStringBuilder(this.useColorStderr);\n    }\n\n    /**\n     * Get a {@link TTYStringBuilder} that is configured to respect the color settings of stdout.\n     *\n     * @return the configured {@link TTYStringBuilder}\n     */\n    public TTYStringBuilder getTTYStringBuilderForStdout()\n    {\n        return new TTYStringBuilder(this.useColorStdout);\n    }\n\n    /**\n     * Register an argument with a given arity. The argument hint is used as a key to retrieve the\n     * argument value(s) later. Additionally, documentation can use the hint to specify what the\n     * argument should be for.\n     *\n     * @param argumentHint\n     *            the hint for the argument\n     * @param arity\n     *            the argument arity\n     * @param optionality\n     *            whether the argument is optional or required\n     * @param contexts\n     *            the contexts\n     * @throws CoreException\n     *             if the argument could not be registered\n     */\n    public void registerArgument(final String argumentHint, final ArgumentArity arity,\n            final ArgumentOptionality optionality, final Integer... contexts)\n    {\n        if (contexts.length == 0)\n        {\n            this.parser.registerArgument(argumentHint, arity, optionality, DEFAULT_CONTEXT);\n        }\n        else\n        {\n            this.parser.registerArgument(argumentHint, arity, optionality, contexts);\n        }\n    }\n\n    /**\n     * Register an empty context. This is useful if you want to have a defined usage case where no\n     * options or arguments are passed.\n     *\n     * @param context\n     *            the context id\n     */\n    public void registerEmptyContext(final int context)\n    {\n        this.parser.registerEmptyContext(context);\n    }\n\n    /**\n     * Register any desired manual page sections. An OPTIONS section will be automatically\n     * generated, so it is recommended that you register at least a DESCRIPTION and EXAMPLES section\n     * with some appropriate documentation. See other {@link AbstractAtlasShellToolsCommand}\n     * implementations for how this is done. For clarification on best practices and/or other\n     * sections to include, see any system man-page (git(1), curl(1), and less(1) are good places to\n     * start).\n     */\n    public abstract void registerManualPageSections();\n\n    /**\n     * Register a {@link AtlasShellToolsCommandTemplate}'s manual pages for this command.\n     *\n     * @param template\n     *            the {@link AtlasShellToolsCommandTemplate} whose man pages sections you want to\n     *            register\n     */\n    public void registerManualPageSectionsFromTemplate(\n            final AtlasShellToolsCommandTemplate template)\n    {\n        template.registerManualPageSections(this);\n    }\n\n    /**\n     * Register an option with a given long and short form. The option will be a flag option, ie. it\n     * can take no arguments.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param shortForm\n     *            the short form of the option, eg. -o\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param contexts\n     *            the contexts\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOption(final String longForm, final Character shortForm,\n            final String description, final OptionOptionality optionality,\n            final Integer... contexts)\n    {\n        if (contexts.length == 0)\n        {\n            this.parser.registerOption(longForm, shortForm, description, optionality,\n                    DEFAULT_CONTEXT);\n        }\n        else\n        {\n            this.parser.registerOption(longForm, shortForm, description, optionality, contexts);\n        }\n    }\n\n    /**\n     * Register an option with a given long form. The option will be a flag option, ie. it can take\n     * no arguments.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param contexts\n     *            the contexts\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOption(final String longForm, final String description,\n            final OptionOptionality optionality, final Integer... contexts)\n    {\n        if (contexts.length == 0)\n        {\n            this.parser.registerOption(longForm, description, optionality, DEFAULT_CONTEXT);\n        }\n        else\n        {\n            this.parser.registerOption(longForm, description, optionality, contexts);\n        }\n    }\n\n    /**\n     * Register an option with a given long and short form that takes an optional argument. The\n     * provided argument hint can be used for generated documentation, and should be a single word\n     * describing the argument. The parser will throw an exception at parse-time if the argument is\n     * not supplied.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param shortForm\n     *            the short form of the option, eg. -o\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param argumentHint\n     *            the hint for the argument\n     * @param contexts\n     *            the contexts\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOptionWithOptionalArgument(final String longForm, final Character shortForm,\n            final String description, final OptionOptionality optionality,\n            final String argumentHint, final Integer... contexts)\n    {\n        if (contexts.length == 0)\n        {\n            this.parser.registerOptionWithOptionalArgument(longForm, shortForm, description,\n                    optionality, argumentHint, DEFAULT_CONTEXT);\n        }\n        else\n        {\n            this.parser.registerOptionWithOptionalArgument(longForm, shortForm, description,\n                    optionality, argumentHint, contexts);\n        }\n    }\n\n    /**\n     * Register an option with a given long form that takes an optional argument. The provided\n     * argument hint can be used for generated documentation, and should be a single word describing\n     * the argument.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param argumentHint\n     *            the hint for the argument\n     * @param contexts\n     *            the contexts\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOptionWithOptionalArgument(final String longForm, final String description,\n            final OptionOptionality optionality, final String argumentHint,\n            final Integer... contexts)\n    {\n        if (contexts.length == 0)\n        {\n            this.parser.registerOptionWithOptionalArgument(longForm, description, optionality,\n                    argumentHint, DEFAULT_CONTEXT);\n        }\n        else\n        {\n            this.parser.registerOptionWithOptionalArgument(longForm, description, optionality,\n                    argumentHint, contexts);\n        }\n    }\n\n    /**\n     * Register an option with a given long form that takes a required argument. The provided\n     * argument hint can be used for generated documentation, and should be a single word describing\n     * the argument. The parser will throw an exception at parse-time if the argument is not\n     * supplied.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param shortForm\n     *            the short form of the option, eg. -o\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param argumentHint\n     *            the hint for the argument\n     * @param contexts\n     *            the contexts\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOptionWithRequiredArgument(final String longForm, final Character shortForm,\n            final String description, final OptionOptionality optionality,\n            final String argumentHint, final Integer... contexts)\n    {\n        if (contexts.length == 0)\n        {\n            this.parser.registerOptionWithRequiredArgument(longForm, shortForm, description,\n                    optionality, argumentHint, DEFAULT_CONTEXT);\n        }\n        else\n        {\n            this.parser.registerOptionWithRequiredArgument(longForm, shortForm, description,\n                    optionality, argumentHint, contexts);\n        }\n    }\n\n    /**\n     * Register an option with a given long form that takes a required argument. The provided\n     * argument hint can be used for generated documentation, and should be a single word describing\n     * the argument. The parser will throw an exception if a required argument option is not\n     * supplied an argument at parse-time.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param argumentHint\n     *            the hint for the argument\n     * @param contexts\n     *            the contexts\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOptionWithRequiredArgument(final String longForm, final String description,\n            final OptionOptionality optionality, final String argumentHint,\n            final Integer... contexts)\n    {\n        if (contexts.length == 0)\n        {\n            this.parser.registerOptionWithRequiredArgument(longForm, description, optionality,\n                    argumentHint, DEFAULT_CONTEXT);\n        }\n        else\n        {\n            this.parser.registerOptionWithRequiredArgument(longForm, description, optionality,\n                    argumentHint, contexts);\n        }\n    }\n\n    /**\n     * Register any necessary options and arguments for the command. Subclasses should override this\n     * method, but call super.registerOptionsAndArguments last in order to pick up super class\n     * options/args.\n     */\n    public void registerOptionsAndArguments()\n    {\n        // register --help and --version to contexts 1 and 2, respectively\n        registerOption(HELP_OPTION_LONG, HELP_OPTION_SHORT, HELP_OPTION_DESCRIPTION,\n                OptionOptionality.REQUIRED, HELP_OPTION_CONTEXT);\n        registerOption(VERSION_OPTION_LONG, VERSION_OPTION_SHORT, VERSION_OPTION_DESCRIPTION,\n                OptionOptionality.REQUIRED, VERSION_OPTION_CONTEXT);\n        registerEmptyContext(DEFAULT_CONTEXT);\n\n        // register a default '--verbose' option in all contexts (except the --help and --version)\n        final Integer[] contexts = this.getFilteredRegisteredContexts().toArray(new Integer[0]);\n        registerOption(VERBOSE_OPTION_LONG, VERBOSE_OPTION_SHORT, VERBOSE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, contexts);\n    }\n\n    /**\n     * Register a {@link AtlasShellToolsCommandTemplate}'s options and arguments for this command.\n     *\n     * @param template\n     *            the {@link AtlasShellToolsCommandTemplate} whose options and arguments you want to\n     *            register\n     */\n    public void registerOptionsAndArgumentsFromTemplate(\n            final AtlasShellToolsCommandTemplate template)\n    {\n        template.registerOptionsAndArguments(this);\n    }\n\n    /**\n     * Run this subcommand using all the special setup and teardown semantics provided by\n     * {@link AbstractAtlasShellToolsCommand}. It automatically registers some default standard\n     * arguments: (help,h) and (verbose,v).\n     *\n     * @param args\n     *            the command arguments\n     * @return an {@code int} that can be used as a system return code\n     */\n    public int runSubcommand(final String... args)\n    {\n        throwIfInvalidNameOrDescription();\n\n        final String[] argsCopy = unpackSentinelArguments(args);\n        this.parser.ignoreUnknownOptions(this.ignoreUnknownOptions);\n\n        // fill out appropriate data structures so the execute() implementation can query\n        registerOptionsAndArguments();\n        registerManualPageSections();\n\n        // parse the options and arguments, throwing exceptions on bad input\n        try\n        {\n            this.parser.parse(Arrays.asList(argsCopy));\n        }\n        catch (final AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException exception)\n        {\n            printlnErrorMessage(exception.getMessage());\n            printSimpleUsageMenu();\n            printStderr(\"Try the \\'\");\n            printStderr(\"--help\", TTYAttribute.BOLD);\n            printStderr(\"\\' option (e.g. \");\n            printStderr(\"atlas \" + this.getCommandName() + \" --help\", TTYAttribute.BOLD);\n            printlnStderr(\") for more info\");\n            return 1;\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"unhandled exception\", exception);\n        }\n\n        logger.debug(\"Command using context {}\", this.parser.getContext());\n\n        // handle the hardcoded --help and --version options\n        if (this.parser.hasOption(HELP_OPTION_LONG))\n        {\n            if (this.usePager)\n            {\n                final PagerHelper helper = new PagerHelper();\n                helper.pageString(this.getHelpMenu());\n            }\n            else\n            {\n                printlnStdout(this.getHelpMenu());\n            }\n            return 0;\n        }\n\n        if (this.parser.hasOption(VERSION_OPTION_LONG))\n        {\n            printlnStdout(String.format(\"%s version %s\", getCommandName(), this.version));\n            return 0;\n        }\n\n        // run the command\n        return execute();\n    }\n\n    /**\n     * Run this subcommand and exit the JVM. An example of how this method should be called to make\n     * the command functional with an external wrapper:\n     *\n     * <pre>\n     * public static void main(final String[] args)\n     * {\n     *     new MySubclassSubcommand().runSubcommandAndExit(args);\n     * }\n     * </pre>\n     *\n     * @param args\n     *            the command arguments\n     */\n    public void runSubcommandAndExit(final String... args)\n    {\n        System.exit(this.runSubcommand(args));\n    }\n\n    /**\n     * Set a new {@link Map} to act as the system environment for this command. This may be useful\n     * for various unit tests which want to inject alternate values into a command's environment\n     * variables (for example, changing the location of user.home to be independent of the machine\n     * context).\n     *\n     * @param newEnvironment\n     *            the new environment {@link Map}\n     */\n    public void setNewEnvironment(final Map<String, String> newEnvironment)\n    {\n        this.environment = newEnvironment;\n    }\n\n    /**\n     * Set a new {@link PrintStream} for the stderr writer methods. This may be useful for various\n     * unit tests which want to intercept command error output to check it against some expected\n     * result.\n     *\n     * @param newErrStream\n     *            the new err {@link PrintStream}\n     */\n    public void setNewErrStream(final PrintStream newErrStream)\n    {\n        this.errStream = newErrStream;\n    }\n\n    /**\n     * Set a new {@link FileSystem} for this command. Implementations should respect the set\n     * {@link FileSystem} when performing file operations. This is particularly useful for\n     * unit-testing, where we may want to use an alternate file system (e.g. in-memory).\n     *\n     * @param newFileSystem\n     *            the new {@link FileSystem} to use.\n     */\n    public void setNewFileSystem(final FileSystem newFileSystem)\n    {\n        this.fileSystem = newFileSystem;\n    }\n\n    /**\n     * Set a new {@link InputStream} for this command. Implementations should respect the set\n     * {@link InputStream} when reading input from the user. This is particularly useful for\n     * unit-testing, where we may want to inject arbitrary input for testing purposes.\n     *\n     * @param inStream\n     *            the new {@link InputStream} to use\n     */\n    public void setNewInStream(final InputStream inStream)\n    {\n        this.inStream = inStream;\n    }\n\n    /**\n     * Set a new {@link PrintStream} for the stdout writer methods. This may be useful for various\n     * unit tests which want to intercept command output to check it against some expected result.\n     *\n     * @param newOutStream\n     *            the new out {@link PrintStream}\n     */\n    public void setNewOutStream(final PrintStream newOutStream)\n    {\n        this.outStream = newOutStream;\n    }\n\n    /**\n     * Check that the command name and description are valid. This should be called before relying\n     * on the return values of getCommandName and getDescription.\n     */\n    public void throwIfInvalidNameOrDescription()\n    {\n        final String name = this.getCommandName();\n        if (name == null || name.isEmpty())\n        {\n            throw new CoreException(\"{} command name must not be null or empty\",\n                    this.getClass().getName());\n        }\n        final String[] split = name.split(\"\\\\s+\");\n        if (split.length > 1)\n        {\n            throw new CoreException(\"{} command name must not contain whitespace\",\n                    this.getClass().getName());\n        }\n        for (int index = 0; index < name.length(); index++)\n        {\n            final char currentCharacter = name.charAt(index);\n            if (!Character.isLetterOrDigit(currentCharacter) && currentCharacter != '-'\n                    && currentCharacter != '_')\n            {\n                throw new CoreException(\n                        \"{} command name must only contain letters, digits, hyphens, or underscores\",\n                        this.getClass().getName());\n            }\n        }\n\n        final String simpleDescription = this.getSimpleDescription();\n        if (simpleDescription == null || simpleDescription.isEmpty())\n        {\n            throw new CoreException(\"{} simple description must not be null or empty\",\n                    this.getClass().getName());\n        }\n    }\n\n    /**\n     * Add a given code line to a given manual page section. Code lines are given additional\n     * indentation and are excluded from line-wrap formatting. If a code line contains a newline,\n     * the formatting will not automatically indent after the line break.\n     *\n     * @param section\n     *            the section to add to\n     * @param codeLine\n     *            the code line\n     * @throws CoreException\n     *             if the section does not exist\n     */\n    protected void addCodeLineToSection(final String section, final String codeLine)\n    {\n        this.registrar.addCodeLineToSection(section, codeLine);\n    }\n\n    /**\n     * Add a section to this command's manual page. The section name will be made all capitalized.\n     *\n     * @param section\n     *            the name of the section\n     */\n    protected void addManualPageSection(final String section)\n    {\n        this.registrar.addManualPageSection(section);\n    }\n\n    /**\n     * Add a given paragraph to a given manual page section.\n     *\n     * @param section\n     *            the section to add to\n     * @param paragraph\n     *            the paragraph\n     * @throws CoreException\n     *             if the section does not exist\n     */\n    protected void addParagraphToSection(final String section, final String paragraph)\n    {\n        this.registrar.addParagraphToSection(section, paragraph);\n    }\n\n    /**\n     * Set the version of this command.\n     *\n     * @param version\n     *            the version string to use (eg. 1.0.0)\n     */\n    protected void setVersion(final String version)\n    {\n        this.version = version;\n    }\n\n    SortedSet<Integer> getFilteredRegisteredContexts()\n    {\n        // filter out the default, hardcoded '--help' and '--version' contexts\n        final Set<Integer> set = this.parser.getRegisteredContexts().stream().filter(\n                context -> context != HELP_OPTION_CONTEXT && context != VERSION_OPTION_CONTEXT)\n                .collect(Collectors.toSet());\n\n        return new TreeSet<>(set);\n    }\n\n    Optional<String> getOptionArgument(final String longForm)\n    {\n        return this.parser.getOptionArgument(longForm);\n    }\n\n    <T> Optional<T> getOptionArgument(final String longForm, final StringConverter<T> converter)\n    {\n        return this.parser.getOptionArgument(longForm, converter);\n    }\n\n    int getParserContext()\n    {\n        return this.parser.getContext();\n    }\n\n    Optional<String> getUnaryArgument(final String hint)\n    {\n        return this.parser.getUnaryArgument(hint);\n    }\n\n    List<String> getVariadicArgument(final String hint)\n    {\n        return this.parser.getVariadicArgument(hint);\n    }\n\n    boolean hasOption(final String longForm)\n    {\n        return this.parser.hasOption(longForm);\n    }\n\n    boolean hasVerboseOption()\n    {\n        return this.parser.hasOption(VERBOSE_OPTION_LONG);\n    }\n\n    void printStderr(final String string, final TTYAttribute... attributes)\n    {\n        final TTYStringBuilder builder = this.getTTYStringBuilderForStderr();\n        builder.append(string, attributes);\n        this.errStream.print(builder.toString()); // NOSONAR\n    }\n\n    void printStdout(final String string, final TTYAttribute... attributes)\n    {\n        final TTYStringBuilder builder = this.getTTYStringBuilderForStdout();\n        builder.append(string, attributes);\n        this.outStream.print(builder.toString()); // NOSONAR\n    }\n\n    void printlnCommandMessage(final String message)\n    {\n        printStderr(this.getCommandName() + \": \");\n        printStderr(message + System.getProperty(LINE_SEPARATOR));\n    }\n\n    void printlnErrorMessage(final String message)\n    {\n        printStderr(this.getCommandName() + \": \");\n        printStderr(\"error: \", TTYAttribute.BOLD, TTYAttribute.RED);\n        printStderr(message + System.getProperty(LINE_SEPARATOR));\n    }\n\n    void printlnStderr(final String string, final TTYAttribute... attributes)\n    {\n        final TTYStringBuilder builder = this.getTTYStringBuilderForStderr();\n        builder.append(string, attributes);\n        this.errStream.println(builder.toString()); // NOSONAR\n    }\n\n    void printlnStdout(final String string, final TTYAttribute... attributes)\n    {\n        final TTYStringBuilder builder = this.getTTYStringBuilderForStdout();\n        builder.append(string, attributes);\n        this.outStream.println(builder.toString()); // NOSONAR\n    }\n\n    void printlnWarnMessage(final String message)\n    {\n        printStderr(this.getCommandName() + \": \");\n        printStderr(\"warn: \", TTYAttribute.BOLD, TTYAttribute.MAGENTA);\n        printStderr(message + System.getProperty(LINE_SEPARATOR));\n    }\n\n    private String getHelpMenu()\n    {\n        final String name = this.getCommandName();\n        final String simpleDescription = this.getSimpleDescription();\n        final Map<Integer, Set<SimpleOption>> optionsWithContext = this.parser\n                .getContextToRegisteredOptions();\n        final Set<SimpleOption> allOptions = this.parser.getRegisteredOptions();\n        final TTYStringBuilder builder = getTTYStringBuilderForStdout();\n\n        builder.newline();\n\n        DocumentationFormatter.generateTextForNameSection(name, simpleDescription, builder);\n        builder.newline();\n\n        DocumentationFormatter.generateTextForSynopsisSection(name, this.maximumColumn,\n                optionsWithContext, this.parser.getRegisteredContexts(),\n                this.parser.getArgumentHintToArity(), this.parser.getArgumentHintToOptionality(),\n                builder);\n        builder.newline();\n\n        // Let's manually insert the DESCRIPTION section first, if it exists.\n        // This is typical for manpages, DESCRIPTION always comes before OPTIONS.\n        if (this.registrar.hasDescriptionSection())\n        {\n            DocumentationFormatter.generateTextForGenericSection(\n                    this.registrar.getDescriptionHeader(), this.maximumColumn, builder,\n                    this.registrar);\n            builder.newline();\n        }\n\n        DocumentationFormatter.generateTextForOptionsSection(this.maximumColumn, allOptions,\n                builder);\n        builder.newline();\n\n        // Insert the rest of the user designed sections\n        for (final String section : this.registrar.getSections())\n        {\n            // Skip DESCRIPTION header, since we already inserted it before OPTIONS\n            if (this.registrar.getDescriptionHeader().equals(section))\n            {\n                continue;\n            }\n            DocumentationFormatter.generateTextForGenericSection(section, this.maximumColumn,\n                    builder, this.registrar);\n            builder.newline();\n        }\n\n        return builder.toString();\n    }\n\n    private void printSimpleUsageMenu()\n    {\n        final String name = this.getCommandName();\n        final Map<Integer, Set<SimpleOption>> optionsWithContext = this.parser\n                .getContextToRegisteredOptions();\n        final TTYStringBuilder builder = getTTYStringBuilderForStderr();\n\n        DocumentationFormatter.generateTextForSynopsisSection(name, this.maximumColumn,\n                optionsWithContext, this.parser.getRegisteredContexts(),\n                this.parser.getArgumentHintToArity(), this.parser.getArgumentHintToOptionality(),\n                builder);\n\n        printlnStderr(builder.toString());\n    }\n\n    private String[] unpackSentinelArguments(final String[] args)\n    {\n        List<String> argsAsList = Arrays.asList(args);\n        argsAsList = new ArrayList<>(argsAsList);\n        if (!argsAsList.isEmpty() && argsAsList.contains(IGNORE_UNKNOWN_OPTIONS))\n        {\n            this.ignoreUnknownOptions = true;\n            argsAsList.remove(IGNORE_UNKNOWN_OPTIONS);\n        }\n        if (!argsAsList.isEmpty()\n                && JAVA_MARKER_SENTINEL.equals(argsAsList.get(argsAsList.size() - 1)))\n        {\n            final String stdoutColorArg = argsAsList.get(argsAsList.size() - STDOUT_COLOR_OFFSET);\n            final String stderrColorArg = argsAsList.get(argsAsList.size() - STDERR_COLOR_OFFSET);\n            final String usePagerArg = argsAsList.get(argsAsList.size() - PAGER_OFFSET);\n            final String terminalColumnArg = argsAsList\n                    .get(argsAsList.size() - TERMINAL_COLUMN_OFFSET);\n            if (JAVA_COLOR_STDOUT.equals(stdoutColorArg))\n            {\n                this.useColorStdout = true;\n            }\n            else if (JAVA_NO_COLOR_STDOUT.equals(stdoutColorArg))\n            {\n                this.useColorStdout = false;\n            }\n            if (JAVA_COLOR_STDERR.equals(stderrColorArg))\n            {\n                this.useColorStderr = true;\n            }\n            else if (JAVA_NO_COLOR_STDERR.equals(stderrColorArg))\n            {\n                this.useColorStderr = false;\n            }\n            if (JAVA_USE_PAGER.equals(usePagerArg))\n            {\n                this.usePager = true;\n            }\n            else if (JAVA_NO_USE_PAGER.equals(usePagerArg))\n            {\n                this.usePager = false;\n            }\n            this.maximumColumn = Integer.parseInt(terminalColumnArg);\n            if (this.maximumColumn > MAXIMUM_ALLOWED_COLUMN)\n            {\n                this.maximumColumn = MAXIMUM_ALLOWED_COLUMN;\n            }\n            argsAsList = argsAsList.subList(0, argsAsList.size() - NUMBER_SENTINELS);\n        }\n        return argsAsList.toArray(new String[0]);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/abstractcommand/AtlasShellToolsCommandTemplate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.abstractcommand;\n\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.ListOfNumbersTemplate;\n\n/**\n * An {@link AtlasShellToolsCommandTemplate} provides an easy way for implementations of\n * {@link AbstractAtlasShellToolsCommand} to share options, arguments, man page sections, and common\n * functionality. For example, by using a template implementation, command authors will not need to\n * re-declare a common option (with the accompanying duplicated option parsing code) across many\n * different commands. See {@link ListOfNumbersTemplate} for an example implementation.\n *\n * @author lcram\n */\npublic interface AtlasShellToolsCommandTemplate\n{\n    /**\n     * Register some manual page sections associated with this template.\n     *\n     * @param parentCommand\n     *            the parent {@link AbstractAtlasShellToolsCommand} for this template\n     */\n    void registerManualPageSections(AbstractAtlasShellToolsCommand parentCommand);\n\n    /**\n     * Register some options and arguments associated with this template.\n     *\n     * @param parentCommand\n     *            the parent {@link AbstractAtlasShellToolsCommand} for this template\n     */\n    void registerOptionsAndArguments(AbstractAtlasShellToolsCommand parentCommand);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/abstractcommand/AtlasShellToolsMarkerInterface.java",
    "content": "package org.openstreetmap.atlas.utilities.command.abstractcommand;\n\n/**\n * A marker interface for the classpath scanner. No subcommands should directly implement this\n * interface. They instead should extend {@link AbstractAtlasShellToolsCommand}.\n *\n * @author lcram\n */\npublic interface AtlasShellToolsMarkerInterface\n{\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/abstractcommand/CommandOutputDelegate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.abstractcommand;\n\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYStringBuilder;\n\n/**\n * @author lcram\n */\npublic class CommandOutputDelegate\n{\n    private final AbstractAtlasShellToolsCommand parentCommand;\n\n    public CommandOutputDelegate(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        this.parentCommand = parentCommand;\n    }\n\n    /**\n     * Get a {@link TTYStringBuilder} with the correct formatting settings for stderr.\n     * Implementations of {@link AbstractAtlasShellToolsCommand} should use this method instead of\n     * instantiating their own string builders.\n     *\n     * @return the string builder\n     */\n    public TTYStringBuilder getTTYStringBuilderForStderr()\n    {\n        return this.parentCommand.getTTYStringBuilderForStderr();\n    }\n\n    /**\n     * Get a {@link TTYStringBuilder} with the correct formatting settings for stdout.\n     * Implementations of {@link AbstractAtlasShellToolsCommand} should use this method instead of\n     * instantiating their own string builders.\n     *\n     * @return the string builder\n     */\n    public TTYStringBuilder getTTYStringBuilderForStdout()\n    {\n        return this.parentCommand.getTTYStringBuilderForStdout();\n    }\n\n    /**\n     * Print a message (with no ending newline) to STDERR with the supplied attributes.\n     *\n     * @param string\n     *            the string to print\n     * @param attributes\n     *            the attributes\n     */\n    public void printStderr(final String string, final TTYAttribute... attributes)\n    {\n        this.parentCommand.printStderr(string, attributes);\n    }\n\n    /**\n     * Print a message (with no ending newline) to STDOUT with the supplied attributes.\n     *\n     * @param string\n     *            the string to print\n     * @param attributes\n     *            the attributes\n     */\n    public void printStdout(final String string, final TTYAttribute... attributes)\n    {\n        this.parentCommand.printStdout(string, attributes);\n    }\n\n    /**\n     * Prints the supplied message like \"commandName: message\" to stderr. Automatically appends a\n     * newline to the output.\n     *\n     * @param message\n     *            the message\n     */\n    public void printlnCommandMessage(final String message)\n    {\n        this.parentCommand.printlnCommandMessage(message);\n    }\n\n    /**\n     * Prints the supplied message like \"commandName: error: message\" with automatic coloring to\n     * stderr. Automatically appends a newline to the output.\n     *\n     * @param message\n     *            the error message\n     */\n    public void printlnErrorMessage(final String message)\n    {\n        this.parentCommand.printlnErrorMessage(message);\n    }\n\n    /**\n     * Print a message to STDERR with the supplied attributes. Terminates the message with a\n     * newline.\n     *\n     * @param string\n     *            the string to print\n     * @param attributes\n     *            the attributes\n     */\n    public void printlnStderr(final String string, final TTYAttribute... attributes)\n    {\n        this.parentCommand.printlnStderr(string, attributes);\n    }\n\n    /**\n     * Print a message to STDOUT with the supplied attributes. Terminates the message with a\n     * newline.\n     *\n     * @param string\n     *            the string to print\n     * @param attributes\n     *            the attributes\n     */\n    public void printlnStdout(final String string, final TTYAttribute... attributes)\n    {\n        this.parentCommand.printlnStdout(string, attributes);\n    }\n\n    /**\n     * Prints the supplied message like \"commandName: warn: message\" with automatic coloring to\n     * stderr. Automatically appends a newline to the output.\n     *\n     * @param message\n     *            the warn message\n     */\n    public void printlnWarnMessage(final String message)\n    {\n        this.parentCommand.printlnWarnMessage(message);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/abstractcommand/OptionAndArgumentDelegate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.abstractcommand;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.SortedSet;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\n\n/**\n * @author lcram\n */\npublic class OptionAndArgumentDelegate\n{\n    private final AbstractAtlasShellToolsCommand parentCommand;\n\n    public OptionAndArgumentDelegate(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        this.parentCommand = parentCommand;\n    }\n\n    /**\n     * Get all registered contexts for this command.\n     *\n     * @return the set of registered contexts\n     */\n    public SortedSet<Integer> getFilteredRegisteredContexts()\n    {\n        return this.parentCommand.getFilteredRegisteredContexts();\n    }\n\n    /**\n     * Get the argument of a given option, if present.\n     *\n     * @param longForm\n     *            the long form of the option\n     * @return an {@link Optional} wrapping the argument\n     * @throws CoreException\n     *             if longForm does not refer to a registered option\n     */\n    public Optional<String> getOptionArgument(final String longForm)\n    {\n        return this.parentCommand.getOptionArgument(longForm);\n    }\n\n    /**\n     * Get the argument of a given option, if present. Also, convert it using the supplied\n     * converter. If the converter function returns null, then this method will return\n     * {@link Optional#empty()}.\n     *\n     * @param <T>\n     *            the type to convert to\n     * @param longForm\n     *            the long form of the option\n     * @param converter\n     *            the conversion function\n     * @return an {@link Optional} wrapping the argument\n     * @throws CoreException\n     *             if longForm does not refer to a registered option\n     */\n    public <T> Optional<T> getOptionArgument(final String longForm,\n            final StringConverter<T> converter)\n    {\n        return this.parentCommand.getOptionArgument(longForm, converter);\n    }\n\n    /**\n     * Get the current context ID of the command's option parser.\n     *\n     * @return the context ID\n     */\n    public int getParserContext()\n    {\n        return this.parentCommand.getParserContext();\n    }\n\n    /**\n     * Given a hint registered as a unary argument, return an optional wrapping the argument value\n     * associated with that hint.\n     *\n     * @param hint\n     *            the hint to check\n     * @return an {@link Optional} wrapping the value\n     * @throws CoreException\n     *             if the argument hint was not registered or is not unary\n     */\n    public Optional<String> getUnaryArgument(final String hint)\n    {\n        return this.parentCommand.getUnaryArgument(hint);\n    }\n\n    /**\n     * Given a hint registered as a variadic argument, return the argument values associated with\n     * that hint.\n     *\n     * @param hint\n     *            the hint to check\n     * @return a list of the values\n     * @throws CoreException\n     *             if the argument hint was not registered or is not variadic\n     */\n    public List<String> getVariadicArgument(final String hint)\n    {\n        return this.parentCommand.getVariadicArgument(hint);\n    }\n\n    /**\n     * Check if a given option was supplied. This will return true even if only the short form was\n     * actually present on the command line.\n     *\n     * @param longForm\n     *            the option\n     * @return if the option was supplied\n     * @throws CoreException\n     *             if longForm does not refer to a registered option\n     */\n    public boolean hasOption(final String longForm)\n    {\n        return this.parentCommand.hasOption(longForm);\n    }\n\n    /**\n     * Check if the user supplied the '--verbose' or '-v' option. This is a default option inherited\n     * by all commands.\n     *\n     * @return if --verbose was set\n     */\n    public boolean hasVerboseOption()\n    {\n        return this.parentCommand.hasVerboseOption();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/documentation/DocumentationFormatType.java",
    "content": "package org.openstreetmap.atlas.utilities.command.documentation;\n\n/**\n * @author lcram\n */\npublic enum DocumentationFormatType\n{\n    PARAGRAPH,\n    CODE\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/documentation/DocumentationFormatter.java",
    "content": "package org.openstreetmap.atlas.utilities.command.documentation;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionArgumentType;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.SimpleOptionAndArgumentParser;\nimport org.openstreetmap.atlas.utilities.command.parsing.SimpleOptionAndArgumentParser.SimpleOption;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYStringBuilder;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * @author lcram\n */\npublic final class DocumentationFormatter\n{\n    public static final int DEFAULT_MAXIMUM_COLUMN = 80;\n\n    public static final int DEFAULT_CODE_INDENT_LEVEL = 2;\n    public static final int DEFAULT_CODE_INDENT_WIDTH = 4;\n\n    public static final int DEFAULT_PARAGRAPH_INDENT_LEVEL = 1;\n    public static final int DEFAULT_INNER_PARAGRAPH_INDENT_LEVEL = 2;\n    public static final int DEFAULT_PARAGRAPH_INDENT_WIDTH = 4;\n\n    /**\n     * Call\n     * {@link DocumentationFormatter#addCodeLineAtExactIndentation(int, String, TTYStringBuilder)},\n     * but compute the exact indentation width by multiplying the supplied indentationLevel with the\n     * default INDENTATION_WIDTH.\n     *\n     * @param indentationLevel\n     *            the indentation level\n     * @param string\n     *            the code block string\n     * @param builder\n     *            the builder to be modified\n     */\n    public static void addCodeLine(final int indentationLevel, final String string,\n            final TTYStringBuilder builder)\n    {\n        DocumentationFormatter.addCodeLineAtExactIndentation(\n                indentationLevel * DEFAULT_CODE_INDENT_WIDTH, string, builder);\n    }\n\n    /**\n     * Add a string to the builder with a given number of indentation spaces and a given maximum\n     * column width. The string will be treated as a code block, ie. it will not have any special\n     * formatting applied to it.\n     *\n     * @param exactIndentation\n     *            the exact number of indentation spaces\n     * @param string\n     *            the string to display\n     * @param builder\n     *            the builder to be modified\n     */\n    public static void addCodeLineAtExactIndentation(final int exactIndentation,\n            final String string, final TTYStringBuilder builder)\n    {\n        builder.pushExactIndentWidth(exactIndentation).append(string).popIndentation();\n    }\n\n    /**\n     * Call\n     * {@link DocumentationFormatter#addParagraphWithLineWrappingAtExactIndentation(int, int, String, TTYStringBuilder, boolean)},\n     * but compute the exact indentation width by multiplying the supplied indentationLevel with the\n     * default INDENTATION_WIDTH.\n     *\n     * @param indentationLevel\n     *            the indentation level\n     * @param maximumColumn\n     *            the max column to wrap at\n     * @param string\n     *            the code block string\n     * @param builder\n     *            the builder to be modified\n     * @param indentFirstLine\n     *            decide to indent the first line\n     */\n    public static void addParagraphWithLineWrapping(final int indentationLevel,\n            final int maximumColumn, final String string, final TTYStringBuilder builder,\n            final boolean indentFirstLine)\n    {\n        DocumentationFormatter.addParagraphWithLineWrappingAtExactIndentation(\n                indentationLevel * DEFAULT_PARAGRAPH_INDENT_WIDTH, maximumColumn, string, builder,\n                indentFirstLine);\n    }\n\n    public static void addParagraphWithLineWrappingAtExactIndentation(final int exactIndentation,\n            final int maximumColumn, final String string, final TTYStringBuilder builder,\n            final boolean indentFirstLine)\n    {\n        final int lineWidth = maximumColumn - exactIndentation;\n        int spaceLeft = lineWidth;\n        final String[] words = string.split(\"\\\\s+\");\n        boolean firstIteration = true;\n\n        if (indentFirstLine)\n        {\n            builder.pushExactIndentWidth(exactIndentation);\n        }\n        else\n        {\n            builder.pushExactIndentWidth(0);\n        }\n        for (final String word : words)\n        {\n            // Word fits exactly in the remaining space\n            if (word.length() == spaceLeft)\n            {\n                builder.append(word).pushExactIndentWidth(0).newline()\n                        .pushExactIndentWidth(exactIndentation);\n                spaceLeft = lineWidth;\n            }\n            // Word plus a whitespace is longer than the remaining space\n            else if (word.length() + \" \".length() > spaceLeft)\n            {\n                /*\n                 * This is a special edge case that can occur if the first word of the documentation\n                 * is longer than the line length: if we are on the first iteration, we already\n                 * new-lined and indented so just skip these steps.\n                 */\n                if (!firstIteration)\n                {\n                    builder.newline();\n                    builder.pushExactIndentWidth(exactIndentation);\n                }\n                builder.append(word + \" \").pushExactIndentWidth(0);\n                spaceLeft = lineWidth - (word.length() + \" \".length());\n            }\n            // Word plus a whitespace fits in the remaining space\n            else\n            {\n                builder.append(word + \" \").pushExactIndentWidth(0);\n                spaceLeft = spaceLeft - (word.length() + \" \".length());\n            }\n            firstIteration = false;\n        }\n    }\n\n    public static void generateTextForGenericSection(final String sectionName,\n            final int maximumColumn, final TTYStringBuilder builder,\n            final DocumentationRegistrar registrar)\n    {\n        final List<Tuple<DocumentationFormatType, String>> sectionContents = registrar\n                .getSectionContents(sectionName);\n        final List<Tuple<DocumentationFormatType, String>> sectionContentsFiltered = new ArrayList<>();\n        // Filter out any empty sections\n        for (final Tuple<DocumentationFormatType, String> contents : sectionContents)\n        {\n            if (!contents.getSecond().isEmpty())\n            {\n                sectionContentsFiltered.add(contents);\n            }\n        }\n\n        builder.clearIndentationStack();\n        builder.append(sectionName, TTYAttribute.BOLD).newline();\n        for (int index = 0; index < sectionContentsFiltered.size(); index++)\n        {\n            final Tuple<DocumentationFormatType, String> contents = sectionContentsFiltered\n                    .get(index);\n            final DocumentationFormatType type = contents.getFirst();\n            final String text = contents.getSecond();\n            if (type == DocumentationFormatType.CODE)\n            {\n                DocumentationFormatter.addCodeLine(DocumentationFormatter.DEFAULT_CODE_INDENT_LEVEL,\n                        text, builder);\n                builder.newline();\n            }\n            else if (type == DocumentationFormatType.PARAGRAPH)\n            {\n                DocumentationFormatter.addParagraphWithLineWrapping(\n                        DocumentationFormatter.DEFAULT_PARAGRAPH_INDENT_LEVEL, maximumColumn, text,\n                        builder, true);\n                builder.newline();\n            }\n            // Add an extra newline unless we are on the last element\n            if (index < sectionContentsFiltered.size() - 1)\n            {\n                builder.newline();\n            }\n        }\n    }\n\n    public static void generateTextForNameSection(final String name, final String simpleDescription,\n            final TTYStringBuilder builder)\n    {\n        builder.append(\"NAME\", TTYAttribute.BOLD).newline();\n        builder.clearIndentationStack();\n        builder.withLevelWidth(DEFAULT_PARAGRAPH_INDENT_WIDTH);\n        builder.pushIndentLevel(DEFAULT_PARAGRAPH_INDENT_LEVEL)\n                .append(name + \" -- \" + simpleDescription).popIndentation();\n        builder.newline();\n    }\n\n    public static String generateTextForOptionsSection(final int maximumColumn,\n            final Set<SimpleOption> options, final TTYStringBuilder builder)\n    {\n        final List<SimpleOption> sortedOptions = new ArrayList<>(options);\n        Collections.sort(sortedOptions);\n        builder.clearIndentationStack();\n        builder.withLevelWidth(DEFAULT_PARAGRAPH_INDENT_WIDTH);\n        builder.append(\"OPTIONS\", TTYAttribute.BOLD).newline();\n        for (int index = 0; index < sortedOptions.size(); index++)\n        {\n            final SimpleOption option = sortedOptions.get(index);\n            builder.pushIndentLevel(DEFAULT_PARAGRAPH_INDENT_LEVEL)\n                    .append(SimpleOptionAndArgumentParser.LONG_FORM_PREFIX + option.getLongForm(),\n                            TTYAttribute.BOLD)\n                    .popIndentation();\n            final OptionArgumentType argumentType = option.getArgumentType();\n            if (argumentType == OptionArgumentType.OPTIONAL)\n            {\n                builder.append(\"[\" + SimpleOptionAndArgumentParser.OPTION_ARGUMENT_DELIMITER\n                        + option.getArgumentHint().orElseThrow(AtlasShellToolsException::new)\n                        + \"]\");\n            }\n            else if (argumentType == OptionArgumentType.REQUIRED)\n            {\n                builder.append(SimpleOptionAndArgumentParser.OPTION_ARGUMENT_DELIMITER + \"<\"\n                        + option.getArgumentHint().orElseThrow(AtlasShellToolsException::new)\n                        + \">\");\n            }\n            if (option.getShortForm().isPresent())\n            {\n                builder.append(\", \");\n                builder.append(\n                        SimpleOptionAndArgumentParser.SHORT_FORM_PREFIX + option.getShortForm()\n                                .orElseThrow(AtlasShellToolsException::new).toString(),\n                        TTYAttribute.BOLD);\n                if (argumentType == OptionArgumentType.OPTIONAL)\n                {\n                    builder.append(\"[\"\n                            + option.getArgumentHint().orElseThrow(AtlasShellToolsException::new)\n                            + \"]\");\n                }\n                else if (argumentType == OptionArgumentType.REQUIRED)\n                {\n                    builder.append(\"<\"\n                            + option.getArgumentHint().orElseThrow(AtlasShellToolsException::new)\n                            + \">\");\n                }\n            }\n            builder.newline();\n            addParagraphWithLineWrapping(DEFAULT_INNER_PARAGRAPH_INDENT_LEVEL, maximumColumn,\n                    option.getDescription(), builder, true);\n            builder.newline();\n            // Add an extra newline when we are not on the last element\n            if (index < sortedOptions.size() - 1)\n            {\n                builder.newline();\n            }\n        }\n\n        return builder.toString();\n    }\n\n    public static void generateTextForSynopsisSection(final String programName, // NOSONAR\n            final int maximumColumn, final Map<Integer, Set<SimpleOption>> optionsWithContext,\n            final Set<Integer> contexts,\n            final Map<Integer, Map<String, ArgumentArity>> argumentArities,\n            final Map<Integer, Map<String, ArgumentOptionality>> argumentOptionalities,\n            final TTYStringBuilder builder)\n    {\n        builder.append(\"SYNOPSIS\", TTYAttribute.BOLD).newline();\n        builder.clearIndentationStack();\n        builder.withLevelWidth(DEFAULT_PARAGRAPH_INDENT_WIDTH);\n        for (final Integer context : contexts)\n        {\n            builder.pushIndentLevel(DEFAULT_PARAGRAPH_INDENT_LEVEL)\n                    .append(programName, TTYAttribute.UNDERLINE).popIndentation().append(\" \");\n            final StringBuilder paragraph = new StringBuilder();\n\n            // add all the options\n            final List<SimpleOption> sortedOptions = new ArrayList<>(\n                    optionsWithContext.getOrDefault(context, new HashSet<>()));\n            Collections.sort(sortedOptions);\n            for (final SimpleOption option : sortedOptions)\n            {\n                if (option.getOptionality() == OptionOptionality.OPTIONAL)\n                {\n                    paragraph.append(\"[\");\n                }\n                paragraph.append(\n                        SimpleOptionAndArgumentParser.LONG_FORM_PREFIX + option.getLongForm());\n                final OptionArgumentType argumentType = option.getArgumentType();\n                if (argumentType == OptionArgumentType.OPTIONAL)\n                {\n                    paragraph.append(\"[\" + SimpleOptionAndArgumentParser.OPTION_ARGUMENT_DELIMITER\n                            + option.getArgumentHint().orElseThrow(AtlasShellToolsException::new)\n                            + \"]\");\n                }\n                else if (argumentType == OptionArgumentType.REQUIRED)\n                {\n                    paragraph.append(SimpleOptionAndArgumentParser.OPTION_ARGUMENT_DELIMITER + \"<\"\n                            + option.getArgumentHint().orElseThrow(AtlasShellToolsException::new)\n                            + \">\");\n                }\n                if (option.getOptionality() == OptionOptionality.OPTIONAL)\n                {\n                    paragraph.append(\"]\");\n                }\n                paragraph.append(\" \");\n            }\n\n            // now add all the arguments\n            for (final String hint : argumentArities.getOrDefault(context, new HashMap<>())\n                    .keySet())\n            {\n                if (argumentOptionalities.get(context).get(hint) == ArgumentOptionality.OPTIONAL)\n                {\n                    paragraph.append(\"[\");\n                }\n                else if (argumentOptionalities.get(context)\n                        .get(hint) == ArgumentOptionality.REQUIRED)\n                {\n                    paragraph.append(\"<\");\n                }\n\n                paragraph.append(hint);\n                if (argumentArities.get(context).get(hint) == ArgumentArity.VARIADIC)\n                {\n                    paragraph.append(\"...\");\n                }\n\n                if (argumentOptionalities.get(context).get(hint) == ArgumentOptionality.OPTIONAL)\n                {\n                    paragraph.append(\"] \");\n                }\n                else if (argumentOptionalities.get(context)\n                        .get(hint) == ArgumentOptionality.REQUIRED)\n                {\n                    paragraph.append(\"> \");\n                }\n            }\n\n            final int exactIndentation = DEFAULT_PARAGRAPH_INDENT_LEVEL\n                    * DEFAULT_PARAGRAPH_INDENT_WIDTH + programName.length() + \" \".length();\n            addParagraphWithLineWrappingAtExactIndentation(exactIndentation, maximumColumn,\n                    paragraph.toString(), builder, false);\n            builder.pushIndentLevel(0).newline();\n        }\n    }\n\n    private DocumentationFormatter()\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/documentation/DocumentationRegistrar.java",
    "content": "package org.openstreetmap.atlas.utilities.command.documentation;\n\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * @author lcram\n */\npublic class DocumentationRegistrar\n{\n    private static final String DESCRIPTION_HEADER = \"DESCRIPTION\";\n\n    private final Map<String, List<Tuple<DocumentationFormatType, String>>> sections;\n\n    public DocumentationRegistrar()\n    {\n        this.sections = new LinkedHashMap<>();\n    }\n\n    public void addCodeLineToSection(final String section, final String codeLine)\n    {\n        final String capsSection = section.toUpperCase();\n        if (!this.sections.containsKey(capsSection))\n        {\n            throw new CoreException(\"Section {} has not been added\", capsSection);\n        }\n        final List<Tuple<DocumentationFormatType, String>> list = this.sections.get(capsSection);\n        list.add(new Tuple<>(DocumentationFormatType.CODE, codeLine));\n    }\n\n    public void addManualPageSection(final String section)\n    {\n        final String capsSection = section.toUpperCase();\n        if (this.sections.containsKey(capsSection))\n        {\n            throw new CoreException(\"Manpage section {} was already added\", capsSection);\n        }\n        this.sections.put(capsSection, new ArrayList<>());\n    }\n\n    public void addManualPageSection(final String section,\n            final InputStream sectionResourceFileStream)\n    {\n        final String capsSection = section.toUpperCase();\n        if (this.sections.containsKey(capsSection))\n        {\n            throw new CoreException(\"Manpage section {} was already added\", capsSection);\n        }\n        final StringResource resource = new StringResource();\n        resource.copyFrom(new InputStreamResource(() -> sectionResourceFileStream));\n        final String rawText = resource.all();\n        final List<Tuple<DocumentationFormatType, String>> sectionContents = new ArrayList<>();\n\n        StringBuilder paragraphBuilder = new StringBuilder();\n        final String[] split = rawText.split(System.getProperty(\"line.separator\"));\n        for (final String line : split)\n        {\n            if (line.isEmpty())\n            {\n                sectionContents.add(new Tuple<>(DocumentationFormatType.PARAGRAPH,\n                        paragraphBuilder.toString()));\n                paragraphBuilder = new StringBuilder();\n            }\n            else if (line.startsWith(\"#\"))\n            {\n                /*\n                 * Close and add any in-progress paragraph. If the user was not in the middle of\n                 * creating a paragraph, this is fine. Empty blocks will be filtered by downstream\n                 * code.\n                 */\n                sectionContents.add(new Tuple<>(DocumentationFormatType.PARAGRAPH,\n                        paragraphBuilder.toString()));\n                paragraphBuilder = new StringBuilder();\n\n                // scrub the '#' off the line\n                final String scrubbedLine = line.substring(1);\n                sectionContents.add(new Tuple<>(DocumentationFormatType.CODE, scrubbedLine));\n            }\n            else\n            {\n                paragraphBuilder.append(line + \" \");\n            }\n        }\n        // add last paragraph if one is left over\n        sectionContents\n                .add(new Tuple<>(DocumentationFormatType.PARAGRAPH, paragraphBuilder.toString()));\n\n        this.sections.put(capsSection, sectionContents);\n    }\n\n    public void addParagraphToSection(final String section, final String paragraph)\n    {\n        final String capsSection = section.toUpperCase();\n        if (!this.sections.containsKey(capsSection))\n        {\n            throw new CoreException(\"Section {} has not been added\", capsSection);\n        }\n        final List<Tuple<DocumentationFormatType, String>> list = this.sections.get(capsSection);\n        list.add(new Tuple<>(DocumentationFormatType.PARAGRAPH, paragraph));\n    }\n\n    public String getDescriptionHeader()\n    {\n        return DESCRIPTION_HEADER;\n    }\n\n    public List<Tuple<DocumentationFormatType, String>> getSectionContents(final String section)\n    {\n        return this.sections.get(section);\n    }\n\n    public Set<String> getSections()\n    {\n        return this.sections.keySet();\n    }\n\n    public boolean hasDescriptionSection()\n    {\n        return this.sections.keySet().contains(DESCRIPTION_HEADER);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/documentation/PagerHelper.java",
    "content": "package org.openstreetmap.atlas.utilities.command.documentation;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.util.Arrays;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\n\n/**\n * @author lcram\n */\npublic class PagerHelper\n{\n    private static final String PAGER_ENVIRONMENT_VARIABLE = \"ATLAS_SHELL_TOOLS_PAGER\";\n    private static final String PAGER_FALLBACK_VARIABLE = \"PAGER\";\n    private static final String DEFAULT_PAGER = \"less -cSRMis\";\n\n    /**\n     * Page a given string using the system paging program. Will check PAGER environment variable\n     * first and use that if possible.\n     *\n     * @param string\n     *            the string to page\n     */\n    public void pageString(final String string)\n    {\n        final String pagerVariable = System.getenv(PAGER_ENVIRONMENT_VARIABLE);\n        final String pagerFallbackVariable = System.getenv(PAGER_FALLBACK_VARIABLE);\n        final Optional<String> pagerProgram;\n        final String[] pagerFlags;\n\n        if (pagerVariable != null && !pagerVariable.isEmpty())\n        {\n            pagerProgram = callWhichOnPager(pagerVariable.split(\"\\\\s+\")[0]);\n            pagerFlags = extractFlagsFromVariable(pagerVariable);\n        }\n        else if (pagerFallbackVariable != null && !pagerFallbackVariable.isEmpty())\n        {\n            pagerProgram = callWhichOnPager(pagerFallbackVariable.split(\"\\\\s+\")[0]);\n            pagerFlags = extractFlagsFromVariable(pagerFallbackVariable);\n        }\n        else\n        {\n            pagerProgram = callWhichOnPager(DEFAULT_PAGER.split(\"\\\\s+\")[0]);\n            pagerFlags = extractFlagsFromVariable(DEFAULT_PAGER);\n        }\n\n        File temporaryFile = null;\n        try\n        {\n            temporaryFile = File.temporary();\n        }\n        catch (final Exception exception)\n        {\n            System.out.println(string); // NOSONAR\n            return;\n        }\n\n        if (temporaryFile == null)\n        {\n            System.out.println(string); // NOSONAR\n            return;\n        }\n\n        temporaryFile.writeAndClose(string);\n\n        try\n        {\n            final String[] processBuilderArguments = new String[1 + pagerFlags.length + 1];\n            processBuilderArguments[0] = pagerProgram.orElseThrow(AtlasShellToolsException::new);\n            if (pagerFlags.length > 0)\n            {\n                System.arraycopy(pagerFlags, 0, processBuilderArguments, 1, pagerFlags.length);\n            }\n            processBuilderArguments[processBuilderArguments.length - 1] = temporaryFile\n                    .getAbsolutePathString();\n            final ProcessBuilder processBuilder = new ProcessBuilder(processBuilderArguments);\n            processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);\n            processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);\n            processBuilder.redirectInput(ProcessBuilder.Redirect.INHERIT);\n            final Process process = processBuilder.start();\n            process.waitFor();\n        }\n        catch (final Exception exception)\n        {\n            System.out.println(string); // NOSONAR\n        }\n        finally\n        {\n            temporaryFile.delete();\n        }\n    }\n\n    private Optional<String> callWhichOnPager(final String pager)\n    {\n        final String whichProgram = \"which\";\n        final Process process;\n        try\n        {\n            process = new ProcessBuilder(whichProgram, pager).start();\n        }\n        catch (final IOException exception)\n        {\n            return Optional.empty();\n        }\n\n        final InputStream stream = process.getInputStream();\n        final BufferedReader reader = new BufferedReader(new InputStreamReader(stream));\n        String line = null;\n\n        try\n        {\n            line = reader.readLine();\n        }\n        catch (final IOException exception)\n        {\n            return Optional.empty();\n        }\n\n        if (line == null || line.isEmpty())\n        {\n            return Optional.empty();\n        }\n        return Optional.of(line);\n    }\n\n    private String[] extractFlagsFromVariable(final String variable)\n    {\n        final String[] flags = variable.split(\"\\\\s+\");\n        return Arrays.copyOfRange(flags, 1, flags.length);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/ArgumentArity.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing;\n\n/**\n * The arity of an argument.\n *\n * @see \"https://en.wikipedia.org/wiki/Arity\"\n * @author lcram\n */\npublic enum ArgumentArity\n{\n    UNARY,\n    VARIADIC\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/ArgumentOptionality.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing;\n\n/**\n * The optionality of a program argument.\n * \n * @author lcram\n */\npublic enum ArgumentOptionality\n{\n    OPTIONAL,\n    REQUIRED\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/OptionArgumentType.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing;\n\n/**\n * The optionality of an option's argument.\n *\n * @author lcram\n */\npublic enum OptionArgumentType\n{\n    NONE,\n    OPTIONAL,\n    REQUIRED\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/OptionOptionality.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing;\n\n/**\n * The optionality of an option.\n *\n * @author lcram\n */\npublic enum OptionOptionality\n{\n    OPTIONAL,\n    REQUIRED\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/SimpleOptionAndArgumentParser.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.AmbiguousAbbreviationException;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.ArgumentException;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.OptionParseException;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.UnknownOptionException;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.UnparsableContextException;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * A simple option and argument parser, designed specifically to impose constraints on the format of\n * the arguments and options. Non-ambiguity is enforced at registration time. Once you have\n * successfully registered the parser, you can be sure it will parse any input command line as\n * expected, throwing errors where appropriate. Nothing about this class is thread safe, should you\n * decide to parse in one thread and read results in another.\n * <p>\n * Supports multiple types of arguments:<br>\n * OPTIONAL vs REQUIRED: if an argument marked REQUIRED is not supplied, the parser will throw an\n * error<br>\n * UNARY vs VARIADIC: a VARIADIC argument is one that can consist of an arbitrary number of\n * values<br>\n * <br>\n * Supports long and short options:<br>\n * --opt : a long option<br>\n * --opt-arg=my_argument : a long option with argument, supports optional or required arguments<br>\n * --opt-arg my_argument : alternate syntax for required long option arguments<br>\n * -a : a short option<br>\n * -abc : bundled short options (-a, -b, -c)<br>\n * -o arg : a short option (-o) that takes a required arg<br>\n * -oarg : alternate syntax, a short option (-o) that takes a required or optional arg<br>\n * <br>\n * If an option is specified multiple times with different arguments, the parser will use the\n * version in the highest ARGV position (ie. the furthest right on the command line).\n * </p>\n * This class supports both the POSIX short option spec as well as the GNU long option spec. See\n * included links for details.<br>\n * <br>\n * This class supports long option prefix abbreviations. This means that a long option \"--option\"\n * can be abbreviated on the command line as \"--o\" or \"--op\" or any non-ambiguous prefix. If an\n * abbreviation results in ambiguity, the parser will throw an error at parse-time.<br>\n * <br>\n * Note that this class also supports multiple parsing contexts, if desired. A parsing context\n * corresponds to certain usage case. For example, you can register a context with ID 3 that takes a\n * single argument and the option \"--opt1\". Then you can also define a context ID 4 that takes 2\n * arguments and an option \"--opt2\". The parser will automatically figure out which context is\n * implied from the supplied command line. If more than one context matches, the context with the\n * lowest numerical ID is selected. If no matching contexts can be found, the parser throws an error\n * with a diagnostic message explaining what happened.<br>\n * <br>\n *\n * @see \"https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html\"\n * @see \"http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html\"\n * @see \"http://pubs.opengroup.org/onlinepubs/7908799/xbd/utilconv.html\"\n * @author lcram\n */\npublic class SimpleOptionAndArgumentParser\n{\n    /**\n     * A simple option representation. Store the option long/short form as well as metadata about\n     * the option.\n     *\n     * @author lcram\n     */\n    public class SimpleOption implements Comparable<SimpleOption>\n    {\n        private final String longForm;\n        private final Optional<Character> shortForm;\n        private final String description;\n        private final OptionOptionality optionality;\n\n        // Default values for option argument fields\n        private OptionArgumentType argumentType = OptionArgumentType.NONE;\n        private Optional<String> argumentHint = Optional.empty();\n\n        SimpleOption(final String longForm, final Character shortForm, final String description,\n                final OptionOptionality optionality, final OptionArgumentType argumentType,\n                final String argumentHint)\n        {\n            if (longForm == null || longForm.isEmpty())\n            {\n                throw new CoreException(\"Long option form cannot be null or empty\");\n            }\n            if (shortForm != null && !Character.isLetterOrDigit(shortForm))\n            {\n                throw new CoreException(\"Invalid short option form {}: must be letter or digit\",\n                        shortForm);\n            }\n            if (description == null || description.isEmpty())\n            {\n                throw new CoreException(\"Description cannot be null or empty\");\n            }\n            this.longForm = longForm;\n            this.shortForm = Optional.ofNullable(shortForm);\n            this.description = description;\n            this.optionality = optionality;\n\n            this.argumentType = argumentType;\n            if (this.argumentType != OptionArgumentType.NONE)\n            {\n                if (argumentHint != null && !argumentHint.isEmpty())\n                {\n                    final String[] split = argumentHint.split(\"\\\\s+\");\n                    if (split.length > 1)\n                    {\n                        throw new CoreException(\"Option argument hint cannot contain whitespace\");\n                    }\n                    this.argumentHint = Optional.of(argumentHint);\n                }\n                else\n                {\n                    throw new CoreException(\"Option argument hint cannot be null or empty\");\n                }\n            }\n        }\n\n        @Override\n        public int compareTo(final SimpleOption other)\n        {\n            final String otherCaps = other.longForm.toUpperCase();\n            final String thisCaps = this.longForm.toUpperCase();\n            return thisCaps.compareTo(otherCaps);\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (other instanceof SimpleOption)\n            {\n                if (this == other)\n                {\n                    return true;\n                }\n                final SimpleOption that = (SimpleOption) other;\n                return Objects.equals(this.longForm, that.longForm);\n            }\n            return false;\n        }\n\n        public Optional<String> getArgumentHint()\n        {\n            return this.argumentHint;\n        }\n\n        public OptionArgumentType getArgumentType()\n        {\n            return this.argumentType;\n        }\n\n        public String getDescription()\n        {\n            return this.description;\n        }\n\n        public String getLongForm()\n        {\n            return this.longForm;\n        }\n\n        public OptionOptionality getOptionality()\n        {\n            return this.optionality;\n        }\n\n        public Optional<Character> getShortForm()\n        {\n            return this.shortForm;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            final int initialPrime = 31;\n            final int hashSeed = 37;\n\n            return hashSeed * initialPrime + Objects.hashCode(this.longForm);\n        }\n\n        @Override\n        public String toString()\n        {\n            final StringBuilder builder = new StringBuilder();\n            builder.append(this.longForm);\n            if (this.shortForm.isPresent())\n            {\n                builder.append(\", \" + this.shortForm.get());\n            }\n            return builder.toString();\n        }\n    }\n\n    public static final String LONG_FORM_PREFIX = \"--\";\n    public static final String SHORT_FORM_PREFIX = \"-\";\n    public static final String OPTION_ARGUMENT_DELIMITER = \"=\";\n    public static final String END_OPTIONS_OPERATOR = \"--\";\n\n    public static final int NO_CONTEXT = 0;\n\n    private static final String MUST_REGISTER_AT_LEAST_ONE_CONTEXT = \"Must register at least one context.\";\n    private static final String PROVIDED_OPTION_LONG_FORM_WAS_AMBIGUOUS = \"provided option long form {} was ambiguous\";\n    private static final String CANNOT_GET_OPTIONS_BEFORE_PARSING = \"Cannot get options before parsing!\";\n\n    private static final Logger logger = LoggerFactory\n            .getLogger(SimpleOptionAndArgumentParser.class);\n\n    private final Map<Integer, Set<SimpleOption>> contextToRegisteredOptions;\n    private final Map<Integer, Map<String, ArgumentArity>> contextToArgumentHintToArity;\n    private final Map<Integer, Map<String, ArgumentOptionality>> contextToArgumentHintToOptionality;\n    private final Map<Integer, Boolean> contextToRegisteredVariadicArgument;\n    private final Map<Integer, Boolean> contextToRegisteredOptionalArgument;\n    private final SortedSet<Integer> registeredContexts;\n\n    private final Set<String> longFormsSeen;\n    private final Set<Character> shortFormsSeen;\n    private final Set<String> argumentHintsSeen;\n\n    private final Map<SimpleOption, Optional<String>> parsedOptions;\n    private final Map<String, List<String>> parsedArguments;\n    private int currentContext;\n    private boolean parseStepRanAtLeastOnce;\n    private boolean ignoreUnknownOptions;\n\n    public SimpleOptionAndArgumentParser()\n    {\n        this.contextToRegisteredOptions = new HashMap<>();\n        this.contextToArgumentHintToArity = new HashMap<>();\n        this.contextToArgumentHintToOptionality = new HashMap<>();\n        this.contextToRegisteredVariadicArgument = new HashMap<>();\n        this.contextToRegisteredOptionalArgument = new HashMap<>();\n        this.registeredContexts = new TreeSet<>();\n\n        this.longFormsSeen = new HashSet<>();\n        this.shortFormsSeen = new HashSet<>();\n        this.argumentHintsSeen = new HashSet<>();\n\n        this.parsedOptions = new LinkedHashMap<>();\n        this.parsedArguments = new LinkedHashMap<>();\n        this.currentContext = NO_CONTEXT;\n        this.parseStepRanAtLeastOnce = false;\n        this.ignoreUnknownOptions = false;\n    }\n\n    /**\n     * Get the mapping of registered argument hints to their arities.\n     *\n     * @return the mapping\n     */\n    public Map<Integer, Map<String, ArgumentArity>> getArgumentHintToArity()\n    {\n        return this.contextToArgumentHintToArity;\n    }\n\n    /**\n     * Get the mapping of registered argument hints to their optionalities\n     *\n     * @return the mapping\n     */\n    public Map<Integer, Map<String, ArgumentOptionality>> getArgumentHintToOptionality()\n    {\n        return this.contextToArgumentHintToOptionality;\n    }\n\n    public int getContext()\n    {\n        return this.currentContext;\n    }\n\n    public Map<Integer, Set<SimpleOption>> getContextToRegisteredOptions()\n    {\n        return this.contextToRegisteredOptions;\n    }\n\n    /**\n     * Get the argument of a given option, if present. If the option is not a registered option,\n     * this will throw an exception.\n     *\n     * @param longForm\n     *            the long form of the option\n     * @return an {@link Optional} wrapping the argument\n     * @throws CoreException\n     *             if longForm does not refer to a registered option\n     */\n    public Optional<String> getOptionArgument(final String longForm)\n    {\n        final Optional<SimpleOption> option;\n        try\n        {\n            option = getParsedOptionFromLongForm(longForm);\n        }\n        catch (final UnknownOptionException exception)\n        {\n            throw new CoreException(\"{} not a registered option\", longForm);\n        }\n        if (option.isPresent())\n        {\n            return this.parsedOptions.get(option.get());\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Get the argument of a given option, if present. Also, convert it using the supplied\n     * converter. If the converter function returns null, then this method will return\n     * {@link Optional#empty()}. If the option is not a registered option, this will throw an\n     * exception.\n     *\n     * @param <T>\n     *            the type to convert to\n     * @param longForm\n     *            the long form of the option\n     * @param converter\n     *            the conversion function\n     * @return an {@link Optional} wrapping the argument\n     * @throws CoreException\n     *             if longForm does not refer to a registered option\n     */\n    public <T> Optional<T> getOptionArgument(final String longForm,\n            final StringConverter<T> converter)\n    {\n        final Optional<SimpleOption> option;\n        try\n        {\n            option = getParsedOptionFromLongForm(longForm);\n        }\n        catch (final UnknownOptionException exception)\n        {\n            throw new CoreException(\"{} not a registered option\", longForm);\n        }\n        if (option.isPresent())\n        {\n            final Optional<String> argument = this.parsedOptions.get(option.get());\n            if (argument.isPresent())\n            {\n                final String argumentValue = argument.get();\n                return Optional.ofNullable(converter.convert(argumentValue));\n            }\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Get a mapping from option names to {@link SimpleOption}s.\n     *\n     * @return the mapping\n     */\n    public Map<String, SimpleOption> getOptionNameToRegisteredOption()\n    {\n        final Set<SimpleOption> allOptions = getRegisteredOptions();\n        final Map<String, SimpleOption> map = new HashMap<>();\n\n        for (final SimpleOption option : allOptions)\n        {\n            map.put(option.getLongForm(), option);\n        }\n\n        return map;\n    }\n\n    /**\n     * Get the registered contexts for this parser.\n     *\n     * @return the set\n     */\n    public SortedSet<Integer> getRegisteredContexts()\n    {\n        return this.registeredContexts;\n    }\n\n    /**\n     * Get the set of registered {@link SimpleOption}s.\n     *\n     * @return the set\n     */\n    public Set<SimpleOption> getRegisteredOptions()\n    {\n        final Set<SimpleOption> allOptions = new HashSet<>();\n        for (final Integer context : this.registeredContexts)\n        {\n            allOptions.addAll(this.contextToRegisteredOptions.get(context));\n        }\n        return allOptions;\n    }\n\n    /**\n     * Given a hint registered as a unary argument, return an optional wrapping the argument value\n     * associated with that hint.\n     *\n     * @param hint\n     *            the hint to check\n     * @return an optional wrapping the value\n     * @throws CoreException\n     *             if the argument hint was not registered or is not unary\n     */\n    public Optional<String> getUnaryArgument(final String hint)\n    {\n        if (!this.parseStepRanAtLeastOnce)\n        {\n            throw new CoreException(\"Cannot get arguments before parsing!\");\n        }\n        if (!this.contextToArgumentHintToArity.get(this.currentContext).containsKey(hint))\n        {\n            return Optional.empty();\n        }\n        if (this.contextToArgumentHintToArity.get(this.currentContext)\n                .get(hint) != ArgumentArity.UNARY)\n        {\n            throw new CoreException(\"hint \\'{}\\' does not correspond to a unary argument\", hint);\n        }\n        final List<String> arguments = this.parsedArguments.get(hint);\n        if (arguments != null && arguments.size() == 1)\n        {\n            return Optional.of(arguments.get(0));\n        }\n\n        logger.debug(\"No value found for unary argument {}, returning empty Optional\", hint);\n        return Optional.empty();\n    }\n\n    /**\n     * Given a hint registered as a variadic argument, return the argument values associated with\n     * that hint.\n     *\n     * @param hint\n     *            the hint to check\n     * @return a list of the values\n     * @throws CoreException\n     *             if the argument hint was not registered or is not variadic\n     */\n    public List<String> getVariadicArgument(final String hint)\n    {\n        if (!this.parseStepRanAtLeastOnce)\n        {\n            throw new CoreException(\"Cannot get arguments before parsing!\");\n        }\n        if (!this.contextToArgumentHintToArity.containsKey(this.currentContext)\n                || !this.contextToArgumentHintToArity.get(this.currentContext).containsKey(hint))\n        {\n            throw new CoreException(\n                    \"hint \\'{}\\' does not correspond to a registered argument in context {}\", hint,\n                    this.currentContext);\n        }\n        if (this.contextToArgumentHintToArity.get(this.currentContext)\n                .get(hint) != ArgumentArity.VARIADIC)\n        {\n            throw new CoreException(\"hint \\'{}\\' does not correspond to a variadic argument\", hint);\n        }\n        final List<String> arguments = this.parsedArguments.get(hint);\n        if (arguments != null)\n        {\n            return arguments;\n        }\n\n        logger.debug(\"No value found for variadic argument {}, returning empty List\", hint);\n        return new ArrayList<>();\n    }\n\n    /**\n     * Check if a given long form option was supplied. This will return true even if only the short\n     * form was actually present on the command line. If the option is not a registered option, this\n     * will return false.\n     *\n     * @param longForm\n     *            the long form option\n     * @return if the option was supplied\n     */\n    public boolean hasOption(final String longForm)\n    {\n        Optional<SimpleOption> option;\n        try\n        {\n            option = getParsedOptionFromLongForm(longForm);\n        }\n        catch (final UnknownOptionException exception)\n        {\n            option = Optional.empty();\n        }\n        return option.isPresent();\n    }\n\n    /**\n     * Set this parser to ignore unknown options.\n     *\n     * @param ignore\n     *            true to ignore unknown option\n     * @return this modified instance\n     */\n    public SimpleOptionAndArgumentParser ignoreUnknownOptions(final boolean ignore)\n    {\n        this.ignoreUnknownOptions = ignore;\n        return this;\n    }\n\n    public boolean isEmpty()\n    {\n        return this.parsedOptions.isEmpty() && this.parsedArguments.isEmpty();\n    }\n\n    public void parse(final List<String> allArguments) throws AmbiguousAbbreviationException, // NOSONAR\n            UnknownOptionException, UnparsableContextException\n    {\n        this.parsedArguments.clear();\n        this.parsedOptions.clear();\n        this.currentContext = NO_CONTEXT;\n        boolean seenEndOptionsOperator = false;\n        final List<String> modifiedArguments = new ArrayList<>();\n\n        /*\n         * First, we pre-parse arguments to see if there are any ambiguous or unknown long options.\n         * This will help generate better error message for the end user. This check must happen\n         * independent of any parsing context, since you need to be able to disambiguate option\n         * prefix abbreviations before a context is selected. Consider the following example:\n         */\n        // Parser Context ID 3 has option --opt1\n        // Parser Context ID 4 has option --opt2\n        // User supplies option --opt\n        /*\n         * In this case we want to throw an error early, warning that the option is ambiguous. If we\n         * didn't, the parser context selection code would choose context 3 (since it picks the\n         * first context that does not throw a parse error). This is not intuitive behaviour for end\n         * users, who need not know about the mechanics of parser contexts.\n         */\n        for (final String argument : allArguments)\n        {\n            boolean addBackArg = true;\n            if (END_OPTIONS_OPERATOR.equals(argument))\n            {\n                if (!seenEndOptionsOperator)\n                {\n                    seenEndOptionsOperator = true;\n                }\n            }\n            else if (SHORT_FORM_PREFIX.equals(argument))\n            {\n                continue; // NOSONAR\n            }\n            else if (argument.startsWith(LONG_FORM_PREFIX) && !seenEndOptionsOperator)\n            {\n                final String[] split = argument.substring(LONG_FORM_PREFIX.length())\n                        .split(OPTION_ARGUMENT_DELIMITER, 2);\n                final String optionName = split[0];\n                final Optional<SimpleOption> option = checkForLongOption(optionName,\n                        getRegisteredOptions(), true);\n                if (!option.isPresent())\n                {\n                    if (this.ignoreUnknownOptions)\n                    {\n                        addBackArg = false;\n                    }\n                    else\n                    {\n                        throw new UnknownOptionException(optionName, getRegisteredOptions());\n                    }\n                }\n            }\n            else if (argument.startsWith(SHORT_FORM_PREFIX) && !seenEndOptionsOperator)\n            {\n                final Optional<SimpleOption> option = checkForShortOption(argument.charAt(1),\n                        getRegisteredOptions());\n                if (!option.isPresent())\n                {\n                    if (this.ignoreUnknownOptions)\n                    {\n                        addBackArg = false;\n                    }\n                    else\n                    {\n                        throw new UnknownOptionException(argument.charAt(1));\n                    }\n                }\n            }\n            if (addBackArg)\n            {\n                modifiedArguments.add(argument);\n            }\n        }\n\n        final SortedSet<String> exceptionMessagesWeSaw = new TreeSet<>();\n        // Now we actually parse the arguments, assigning a context.\n        for (final Integer context : this.registeredContexts) // NOSONAR\n        {\n            try\n            {\n                this.parseOptionsAndArguments(modifiedArguments, context);\n            }\n            catch (final Exception exception)\n            {\n                exceptionMessagesWeSaw.add(String.format(\"%d: %s (context %d)\", context,\n                        exception.getMessage(), context));\n                continue;\n            }\n\n            this.currentContext = context;\n            break;\n        }\n\n        if (this.currentContext == NO_CONTEXT)\n        {\n            throw new UnparsableContextException(exceptionMessagesWeSaw);\n        }\n\n        this.parseStepRanAtLeastOnce = true;\n    }\n\n    /**\n     * Register an argument with a given arity. The argument hint is used as a key to retrieve the\n     * argument value(s) later. Additionally, documentation generators can use the hint to create\n     * more accurate doc pages.\n     *\n     * @param argumentHint\n     *            the hint for the argument\n     * @param arity\n     *            the argument arity\n     * @param optionality\n     *            whether the argument is optional or required\n     * @param contexts\n     *            the contexts for this argument, if not provided then uses a default context\n     * @throws CoreException\n     *             if the argument could not be registered\n     */\n    public void registerArgument(final String argumentHint, final ArgumentArity arity,\n            final ArgumentOptionality optionality, final Integer... contexts)\n    {\n        throwIfArgumentHintSeen(argumentHint);\n        this.argumentHintsSeen.add(argumentHint);\n\n        if (argumentHint == null || argumentHint.isEmpty())\n        {\n            throw new CoreException(\"Argument hint cannot be null or empty\");\n        }\n\n        final String[] split = argumentHint.split(\"\\\\s+\");\n        if (split.length > 1)\n        {\n            throw new CoreException(\"Option argument hint cannot contain whitespace\");\n        }\n\n        if (contexts.length == 0)\n        {\n            throw new CoreException(\"Must provide at least one context.\");\n        }\n        for (int i = 0; i < contexts.length; i++)\n        {\n            registerArgumentHelper(contexts[i], argumentHint, arity, optionality);\n        }\n    }\n\n    /**\n     * Register a given context with no options or arguments. If the context already exists, this\n     * will noop.\n     *\n     * @param context\n     *            the context to register\n     */\n    public void registerEmptyContext(final int context)\n    {\n        if (this.registeredContexts.contains(context))\n        {\n            logger.info(\"Tried to register empty context {}, but {} is already registered\", context,\n                    context);\n            return;\n        }\n        this.registeredContexts.add(context);\n        this.contextToRegisteredOptions.put(context, new HashSet<>());\n        this.contextToRegisteredOptionalArgument.put(context, false);\n        this.contextToArgumentHintToArity.put(context, new HashMap<>());\n        this.contextToArgumentHintToOptionality.put(context, new HashMap<>());\n        this.contextToRegisteredVariadicArgument.put(context, false);\n    }\n\n    /**\n     * Register an option with a given long and short form. The option will be a flag option, ie. it\n     * can take no arguments.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param shortForm\n     *            the short form of the option, eg. -o\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param contexts\n     *            the contexts for this option, if not provided then uses a default context\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOption(final String longForm, final Character shortForm,\n            final String description, final OptionOptionality optionality,\n            final Integer... contexts)\n    {\n        if (longForm != null)\n        {\n            throwIfDuplicateLongForm(longForm);\n            this.longFormsSeen.add(longForm);\n        }\n        if (contexts.length == 0)\n        {\n            throw new CoreException(MUST_REGISTER_AT_LEAST_ONE_CONTEXT);\n        }\n        for (int i = 0; i < contexts.length; i++)\n        {\n            registerOptionHelper(contexts[i], longForm, shortForm, description, optionality,\n                    OptionArgumentType.NONE, null);\n        }\n    }\n\n    /**\n     * Register an option with a given long form. The option will be a flag option, ie. it can take\n     * no arguments.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param contexts\n     *            the contexts for this option, if not provided then uses a default context\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOption(final String longForm, final String description,\n            final OptionOptionality optionality, final Integer... contexts)\n    {\n        this.registerOption(longForm, null, description, optionality, contexts);\n    }\n\n    /**\n     * Register an option with a given long and short form that takes an optional argument. The\n     * provided argument hint can be used for generated documentation, and should be a single word\n     * describing the argument. The parser will throw an exception at parse-time if the argument is\n     * not supplied.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param shortForm\n     *            the short form of the option, eg. -o\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param argumentHint\n     *            the hint for the argument\n     * @param contexts\n     *            the contexts for this option, if not provided then uses a default context\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOptionWithOptionalArgument(final String longForm, final Character shortForm,\n            final String description, final OptionOptionality optionality,\n            final String argumentHint, final Integer... contexts)\n    {\n        if (longForm != null)\n        {\n            throwIfDuplicateLongForm(longForm);\n            this.longFormsSeen.add(longForm);\n        }\n        if (shortForm != null)\n        {\n            throwIfDuplicateShortForm(shortForm);\n            this.shortFormsSeen.add(shortForm);\n        }\n        if (contexts.length == 0)\n        {\n            throw new CoreException(MUST_REGISTER_AT_LEAST_ONE_CONTEXT);\n        }\n        for (int i = 0; i < contexts.length; i++)\n        {\n            registerOptionHelper(contexts[i], longForm, shortForm, description, optionality,\n                    OptionArgumentType.OPTIONAL, argumentHint);\n        }\n    }\n\n    /**\n     * Register an option with a given long form that takes an optional argument. The provided\n     * argument hint can be used for generated documentation, and should be a single word describing\n     * the argument.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param argumentHint\n     *            the hint for the argument\n     * @param contexts\n     *            the contexts for this option, if not provided then uses a default context\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOptionWithOptionalArgument(final String longForm, final String description,\n            final OptionOptionality optionality, final String argumentHint,\n            final Integer... contexts)\n    {\n        if (longForm != null)\n        {\n            throwIfDuplicateLongForm(longForm);\n            this.longFormsSeen.add(longForm);\n        }\n        if (contexts.length == 0)\n        {\n            throw new CoreException(MUST_REGISTER_AT_LEAST_ONE_CONTEXT);\n        }\n        for (int i = 0; i < contexts.length; i++)\n        {\n            registerOptionHelper(contexts[i], longForm, null, description, optionality,\n                    OptionArgumentType.OPTIONAL, argumentHint);\n        }\n    }\n\n    /**\n     * Register an option with a given long and short form that takes a required argument. The\n     * provided argument hint can be used for generated documentation, and should be a single word\n     * describing the argument. The parser will throw an exception at parse-time if the argument is\n     * not supplied.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param shortForm\n     *            the short form of the option, eg. -o\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param argumentHint\n     *            the hint for the argument\n     * @param contexts\n     *            the contexts for this option, if not provided then uses a default context\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOptionWithRequiredArgument(final String longForm, final Character shortForm,\n            final String description, final OptionOptionality optionality,\n            final String argumentHint, final Integer... contexts)\n    {\n        if (longForm != null)\n        {\n            throwIfDuplicateLongForm(longForm);\n            this.longFormsSeen.add(longForm);\n        }\n        if (shortForm != null)\n        {\n            throwIfDuplicateShortForm(shortForm);\n            this.shortFormsSeen.add(shortForm);\n        }\n        if (contexts.length == 0)\n        {\n            throw new CoreException(MUST_REGISTER_AT_LEAST_ONE_CONTEXT);\n        }\n        for (int i = 0; i < contexts.length; i++)\n        {\n            registerOptionHelper(contexts[i], longForm, shortForm, description, optionality,\n                    OptionArgumentType.REQUIRED, argumentHint);\n        }\n    }\n\n    /**\n     * Register an option with a given long form that takes a required argument. The provided\n     * argument hint can be used for generated documentation, and should be a single word describing\n     * the argument. The parser will throw an exception at parse-time if the argument is not\n     * supplied.\n     *\n     * @param longForm\n     *            the long form of the option, eg. --option\n     * @param description\n     *            a simple description\n     * @param optionality\n     *            the optionality\n     * @param argumentHint\n     *            the hint for the argument\n     * @param contexts\n     *            the contexts for this option, if not provided then uses a default context\n     * @throws CoreException\n     *             if the option could not be registered\n     */\n    public void registerOptionWithRequiredArgument(final String longForm, final String description,\n            final OptionOptionality optionality, final String argumentHint,\n            final Integer... contexts)\n    {\n        if (longForm != null)\n        {\n            throwIfDuplicateLongForm(longForm);\n            this.longFormsSeen.add(longForm);\n        }\n        if (contexts.length == 0)\n        {\n            throw new CoreException(MUST_REGISTER_AT_LEAST_ONE_CONTEXT);\n        }\n        for (int i = 0; i < contexts.length; i++)\n        {\n            registerOptionHelper(contexts[i], longForm, null, description, optionality,\n                    OptionArgumentType.REQUIRED, argumentHint);\n        }\n    }\n\n    private Optional<SimpleOption> checkForLongOption(final String longForm,\n            final Set<SimpleOption> setToCheck, final boolean usePrefixMatching)\n            throws AmbiguousAbbreviationException\n    {\n        final Set<SimpleOption> matchedOptions = new HashSet<>();\n        for (final SimpleOption option : setToCheck)\n        {\n            if (option.getLongForm().startsWith(longForm))\n            {\n                /*\n                 * Break out if we find an exact match. This handles the edge case where you have\n                 * two options like \"--option\" and \"--optionSuffix\". In this case, if \"--option\" is\n                 * supplied, we want to return the exact match instead of throwing an ambiguity\n                 * error.\n                 */\n                if (option.getLongForm().equals(longForm))\n                {\n                    return Optional.of(option);\n                }\n                if (usePrefixMatching)\n                {\n                    matchedOptions.add(option);\n                }\n            }\n        }\n        if (matchedOptions.size() > 1)\n        {\n            final List<String> ambiguousOptions = matchedOptions.stream()\n                    .map(SimpleOption::getLongForm).collect(Collectors.toList());\n            throw new AmbiguousAbbreviationException(longForm,\n                    new StringList(ambiguousOptions).join(\", \"));\n        }\n        else if (matchedOptions.size() == 1)\n        {\n            final SimpleOption matchedOption = matchedOptions.toArray(new SimpleOption[0])[0];\n            return Optional.of(matchedOption);\n        }\n        return Optional.empty();\n    }\n\n    private Optional<SimpleOption> checkForShortOption(final Character shortForm,\n            final Set<SimpleOption> setToCheck)\n    {\n        for (final SimpleOption option : setToCheck)\n        {\n            final Optional<Character> optionalForm = option.getShortForm();\n            if (optionalForm.isPresent() && optionalForm.get().equals(shortForm))\n            {\n                return Optional.of(option);\n            }\n        }\n        return Optional.empty();\n    }\n\n    private Optional<SimpleOption> getParsedOptionFromLongForm(final String longForm)\n            throws UnknownOptionException\n    {\n        if (!this.parseStepRanAtLeastOnce)\n        {\n            throw new CoreException(CANNOT_GET_OPTIONS_BEFORE_PARSING);\n        }\n        final Optional<SimpleOption> option;\n        try\n        {\n            if (!registeredOptionForLongForm(this.currentContext, longForm).isPresent())\n            {\n                throw new UnknownOptionException(longForm);\n            }\n            option = checkForLongOption(longForm, this.parsedOptions.keySet(), false);\n        }\n        catch (final AmbiguousAbbreviationException exception)\n        {\n            throw new CoreException(PROVIDED_OPTION_LONG_FORM_WAS_AMBIGUOUS, longForm);\n        }\n        return option;\n    }\n\n    /*\n     * This function returns a boolean value specifying whether or not it consumed the lookahead\n     * value.\n     */\n    private boolean parseLongFormOption(final int tryContext, final String argument, // NOSONAR\n            final Optional<String> lookahead)\n            throws UnknownOptionException, OptionParseException, AmbiguousAbbreviationException\n    {\n        final String scrubbedPrefix = argument.substring(LONG_FORM_PREFIX.length());\n        final String[] split = scrubbedPrefix.split(OPTION_ARGUMENT_DELIMITER, 2);\n        final String optionName = split[0];\n\n        final Optional<SimpleOption> option = registeredOptionForLongForm(tryContext, optionName);\n\n        if (option.isPresent())\n        {\n            // Split length is 1 if command line looks like \"... --option anotherThing ...\"\n            // Split length is > 1 if command line looks like \"... --option=arg anotherThing ...\"\n            // Split length will never be < 1\n            if (split.length == 1)\n            {\n                // Cases to handle here regarding the lookahead\n                // 1) The option takes no argument or an optional argument -> do not use lookahead\n                // 2) The option takes a required argument -> attempt to use lookahead\n                // Once done, we return whether or not we used the lookahead\n                switch (option.get().getArgumentType())\n                {\n                    case NONE:\n                        // fallthru intended\n                    case OPTIONAL:\n                        this.parsedOptions.put(option.get(), Optional.empty());\n                        return false;\n                    case REQUIRED:\n                        if (lookahead.isPresent())\n                        {\n                            this.parsedOptions.put(option.get(), lookahead);\n                            return true;\n                        }\n                        else\n                        {\n                            throw new OptionParseException(\"option \\'\" + option.get().getLongForm() // NOSONAR\n                                    + \"\\' needs an argument\");\n                        }\n                    default:\n                        throw new CoreException(\"Unrecognized OptionArgumentType {}\",\n                                option.get().getArgumentType());\n                }\n            }\n            else\n            {\n                // Cases to handle here\n                // 1) The option takes no argument -> throw an error\n                // 2) The option takes an optional or required argument -> use the split\n                final String optionArgument = split[1];\n                switch (option.get().getArgumentType())\n                {\n                    case NONE:\n                        throw new OptionParseException(\n                                \"option \\'\" + option.get().getLongForm() + \"\\' takes no argument\");\n                    case OPTIONAL:\n                        // fallthru intended\n                    case REQUIRED:\n                        this.parsedOptions.put(option.get(), Optional.ofNullable(optionArgument));\n                        return false;\n                    default:\n                        throw new CoreException(\"Unrecognized OptionArgumentType {}\",\n                                option.get().getArgumentType());\n                }\n            }\n        }\n        else\n        {\n            throw new UnknownOptionException(optionName);\n        }\n    }\n\n    /**\n     * Perform a full scan and parse of the provided arguments list. This method will populate the\n     * parser's internal data structures so they are ready to be queried for results. This method\n     * tries to parse the arguments within a supplied context.\n     *\n     * @param allArguments\n     *            The provided arguments list\n     * @param tryContext\n     *            the context to try\n     * @throws UnknownOptionException\n     *             If an unknown option is detected\n     * @throws OptionParseException\n     *             If another parsing error occurs\n     * @throws ArgumentException\n     *             If supplied arguments do not match the registered argument hints\n     * @throws AmbiguousAbbreviationException\n     *             If an ambiguous long option abbreviation was used\n     */\n    private void parseOptionsAndArguments(final List<String> allArguments, final int tryContext) // NOSONAR\n            throws UnknownOptionException, OptionParseException, ArgumentException,\n            AmbiguousAbbreviationException\n    {\n        final List<String> regularArguments = new ArrayList<>();\n        boolean seenEndOptionsOperator = false;\n        this.parsedArguments.clear();\n        this.parsedOptions.clear();\n        int regularArgumentCounter = 0;\n\n        boolean skipNextArgument = false;\n\n        for (int index = 0; index < allArguments.size(); index++)\n        {\n            if (skipNextArgument)\n            {\n                skipNextArgument = false;\n                continue;\n            }\n            skipNextArgument = false;\n\n            final String argument = allArguments.get(index);\n\n            // We store a lookahead to use in case of an option with the argument specified like\n            // \"--option optarg\". In this case we will need the lookahead value.\n            Optional<String> lookahead = Optional.empty();\n            if (index + 1 < allArguments.size())\n            {\n                lookahead = Optional.ofNullable(allArguments.get(index + 1));\n            }\n\n            // Five cases:\n            // Argument is \"--\" -> stop parsing arguments as options\n            // Argument is \"-\" -> treat as a regular argument\n            // Argument starts with \"--\" -> long form option\n            // Argument starts with \"-\" -> short form option\n            // Anything else -> regular argument\n            if (END_OPTIONS_OPERATOR.equals(argument))\n            {\n                if (seenEndOptionsOperator)\n                {\n                    regularArguments.add(argument);\n                }\n                else\n                {\n                    seenEndOptionsOperator = true;\n                }\n            }\n            else if (SHORT_FORM_PREFIX.equals(argument))\n            {\n                regularArguments.add(argument);\n            }\n            else if (argument.startsWith(LONG_FORM_PREFIX) && !seenEndOptionsOperator)\n            {\n                final boolean consumedLookahead = parseLongFormOption(tryContext, argument,\n                        lookahead);\n                if (consumedLookahead)\n                {\n                    skipNextArgument = true;\n                }\n            }\n            else if (argument.startsWith(SHORT_FORM_PREFIX) && !seenEndOptionsOperator)\n            {\n                final boolean consumedLookahead = parseShortFormOption(tryContext, argument,\n                        lookahead);\n                if (consumedLookahead)\n                {\n                    skipNextArgument = true;\n                }\n            }\n            else\n            {\n                regularArguments.add(argument);\n            }\n        }\n\n        // Check that any option registered as required is actually present. If not, throw an error.\n        final Set<SimpleOption> registeredOptions = this.contextToRegisteredOptions.get(tryContext);\n        if (registeredOptions != null)\n        {\n            for (final SimpleOption registeredOption : registeredOptions)\n            {\n                if (registeredOption.getOptionality() == OptionOptionality.REQUIRED\n                        && !this.parsedOptions.keySet().contains(registeredOption))\n                {\n                    throw new OptionParseException(\n                            \"missing required option \" + registeredOption.longForm);\n                }\n            }\n        }\n\n        if (this.contextToRegisteredOptionalArgument.getOrDefault(tryContext, false))\n        {\n            if (this.contextToArgumentHintToArity.containsKey(tryContext) && regularArguments\n                    .size() < this.contextToArgumentHintToArity.get(tryContext).size() - 1)\n            {\n                throw new ArgumentException(\"missing required argument(s)\");\n            }\n        }\n        else\n        {\n            if (this.contextToArgumentHintToArity.containsKey(tryContext) && regularArguments\n                    .size() < this.contextToArgumentHintToArity.get(tryContext).size())\n            {\n                throw new ArgumentException(\"missing required argument(s)\");\n            }\n        }\n\n        // Now handle the regular arguments\n        for (final String regularArgument : regularArguments)\n        {\n            regularArgumentCounter = parseRegularArgument(tryContext, regularArgument,\n                    regularArguments.size(), regularArgumentCounter);\n        }\n\n        this.parseStepRanAtLeastOnce = true;\n    }\n\n    private int parseRegularArgument(final int context, final String argument,\n            final int regularArgumentSize, final int regularArgumentCounter)\n            throws ArgumentException\n    {\n        int argumentCounter = regularArgumentCounter;\n\n        if (!this.contextToArgumentHintToArity.containsKey(context))\n        {\n            throw new ArgumentException(\"too many arguments\");\n        }\n\n        if (this.contextToArgumentHintToArity.containsKey(context)\n                && argumentCounter >= this.contextToArgumentHintToArity.get(context).size())\n        {\n            throw new ArgumentException(\"too many arguments\");\n        }\n\n        final String argumentHint = (String) this.contextToArgumentHintToArity.get(context).keySet()\n                .toArray()[argumentCounter];\n        final ArgumentArity currentArity = this.contextToArgumentHintToArity.get(context)\n                .get(argumentHint);\n        switch (currentArity)\n        {\n            case UNARY:\n                logger.debug(\"parsed unary argument hint => {} : value => {}\", argumentHint,\n                        argument);\n                this.parsedArguments.put(argumentHint, Arrays.asList(argument));\n                argumentCounter++;\n                break;\n            case VARIADIC:\n                List<String> multiArgumentList = this.parsedArguments.get(argumentHint);\n                multiArgumentList = multiArgumentList == null ? new ArrayList<>()\n                        : multiArgumentList;\n                multiArgumentList.add(argument);\n                logger.debug(\"parsed variadic argument hint => {} : value => {}\", argumentHint,\n                        argument);\n                this.parsedArguments.put(argumentHint, multiArgumentList);\n\n                // Two cases:\n                // Case 1 -> [UNARY...] VARIADIC\n                if (argumentCounter == this.contextToArgumentHintToArity.get(context).size() - 1)\n                {\n                    // do nothing, we can consume the rest of the arguments\n                }\n                // Case 2 -> [UNARY...] VARIADIC UNARY [UNARY...]\n                else\n                {\n                    // cutoff point, be sure to save arguments for consumption by subsequent hints\n                    if (multiArgumentList.size() == regularArgumentSize\n                            - this.contextToArgumentHintToArity.get(context).size() + 1)\n                    {\n                        argumentCounter++;\n                        break;\n                    }\n                }\n                break;\n            default:\n                throw new CoreException(\"Unrecognized ArgumentArity {}\", currentArity);\n        }\n        return argumentCounter;\n    }\n\n    /*\n     * This function returns a boolean value specifying whether or not it consumed the lookahead\n     * value.\n     */\n    private boolean parseShortFormOption(final int context, final String argument, // NOSONAR\n            final Optional<String> lookahead) throws OptionParseException, UnknownOptionException\n    {\n        final String scrubbedPrefix = argument.substring(SHORT_FORM_PREFIX.length());\n\n        // Two cases\n        // 1) command line looks like \"... -o arg ...\"\n        // 2) command line looks like \"... -oarg ...\"\n        // scrubbedPrefix length will never be < 1\n\n        // Case 1) \"... -o arg ...\"\n        if (scrubbedPrefix.length() == 1)\n        {\n            final Optional<SimpleOption> option = registeredOptionForShortForm(context,\n                    scrubbedPrefix.charAt(0));\n\n            if (!option.isPresent())\n            {\n                throw new UnknownOptionException(scrubbedPrefix.charAt(0));\n            }\n\n            // 3 cases to handle here regarding the option argument type\n            // a) The option takes no argument -> do not use lookahead\n            // b) The option takes an optional argument -> do not use lookahead\n            // c) The option takes a required argument -> attempt to use lookahead\n            // Once done, we return whether or not we used the lookahead\n            switch (option.get().getArgumentType())\n            {\n                case NONE:\n                    // fallthru intended\n                case OPTIONAL:\n                    this.parsedOptions.put(option.get(), Optional.empty());\n                    return false;\n                case REQUIRED:\n                    if (lookahead.isPresent())\n                    {\n                        this.parsedOptions.put(option.get(), lookahead);\n                        return true;\n                    }\n                    else\n                    {\n                        throw new OptionParseException(\"option \\'\"\n                                + option.get().getShortForm().get() + \"\\' needs an argument\"); // NOSONAR\n                    }\n                default:\n                    throw new CoreException(\"Bad OptionArgumentType {}\",\n                            option.get().getArgumentType());\n            }\n        }\n        // Case 2) \"... -oarg ...\"\n        else\n        {\n            // Cases to handle here\n            // a) The option is using bundling, ie. (\"-oarg\" meaning \"-o -a -r -g\")\n            // b) The option is using an argument, ie. (\"-oarg\" where \"arg\" is an argument to \"-o\")\n\n            // Check for case a) determine if valid bundle\n            boolean isValidBundle = true;\n            for (int index = 0; index < scrubbedPrefix.length(); index++) // NOSONAR\n            {\n                final char optionCharacter = scrubbedPrefix.charAt(index);\n                final Optional<SimpleOption> option = registeredOptionForShortForm(context,\n                        optionCharacter);\n                if (option.isPresent())\n                {\n                    if (option.get().getArgumentType() != OptionArgumentType.NONE)\n                    {\n                        isValidBundle = false;\n                        break;\n                    }\n                }\n                else\n                {\n                    isValidBundle = false;\n                    break;\n                }\n            }\n\n            if (isValidBundle)\n            {\n                // Bundle was valid, so loop over again and add all options\n                for (int index = 0; index < scrubbedPrefix.length(); index++)\n                {\n                    final char optionCharacter = scrubbedPrefix.charAt(index);\n                    final Optional<SimpleOption> option = registeredOptionForShortForm(context,\n                            optionCharacter);\n                    this.parsedOptions.put(option.get(), Optional.empty()); // NOSONAR\n                }\n            }\n            else\n            {\n                // Bundle was not valid, so treat remaining chars as an option arg\n                final char optionCharacter = scrubbedPrefix.charAt(0);\n                final Optional<SimpleOption> option = registeredOptionForShortForm(context,\n                        optionCharacter);\n                if (!option.isPresent())\n                {\n                    throw new UnknownOptionException(String.valueOf(optionCharacter).charAt(0));\n                }\n                if (option.get().getArgumentType() == OptionArgumentType.NONE)\n                {\n                    throw new OptionParseException(\"option \\'\" + option.get().getShortForm().get() // NOSONAR\n                            + \"\\' takes no argument\");\n                }\n                final String optionArgument = scrubbedPrefix.substring(1);\n                this.parsedOptions.put(option.get(), Optional.ofNullable(optionArgument));\n            }\n\n            return false;\n        }\n    }\n\n    private void registerArgumentHelper(final int context, final String argumentHint,\n            final ArgumentArity arity, final ArgumentOptionality optionality)\n    {\n        if (context < 0)\n        {\n            throw new CoreException(\"Context ID must be a positive integer\");\n        }\n        if (this.contextToRegisteredOptionalArgument.getOrDefault(context, false))\n        {\n            throw new CoreException(\"Optional argument must be the last registered argument\");\n        }\n\n        if (arity == ArgumentArity.VARIADIC\n                && this.contextToRegisteredVariadicArgument.getOrDefault(context, false))\n        {\n            throw new CoreException(\"Cannot register more than one variadic argument\");\n        }\n        if (optionality == ArgumentOptionality.OPTIONAL)\n        {\n            if (this.contextToRegisteredOptionalArgument.getOrDefault(context, false))\n            {\n                throw new CoreException(\"Cannot register more than one optional argument\");\n            }\n            if (this.contextToRegisteredVariadicArgument.getOrDefault(context, false))\n            {\n                throw new CoreException(\n                        \"Cannot register both an optional argument and a variadic argument\");\n            }\n            this.contextToRegisteredOptionalArgument.put(context, true);\n        }\n\n        if (arity == ArgumentArity.VARIADIC)\n        {\n            this.contextToRegisteredVariadicArgument.put(context, true);\n        }\n\n        final Map<String, ArgumentArity> argumentHintToArity = this.contextToArgumentHintToArity\n                .get(context) == null ? new LinkedHashMap<>()\n                        : this.contextToArgumentHintToArity.get(context);\n        argumentHintToArity.put(argumentHint, arity);\n        this.contextToArgumentHintToArity.put(context, argumentHintToArity);\n\n        final Map<String, ArgumentOptionality> argumentHintToOptionality = this.contextToArgumentHintToOptionality\n                .get(context) == null ? new LinkedHashMap<>()\n                        : this.contextToArgumentHintToOptionality.get(context);\n        argumentHintToOptionality.put(argumentHint, optionality);\n        this.contextToArgumentHintToOptionality.put(context, argumentHintToOptionality);\n\n        this.registeredContexts.add(context);\n    }\n\n    private void registerOptionHelper(final int context, final String longForm,\n            final Character shortForm, final String description,\n            final OptionOptionality optionality, final OptionArgumentType type,\n            final String argumentHint)\n    {\n        if (context <= 0)\n        {\n            throw new CoreException(\"Context ID must be a positive integer (>= 1)\");\n        }\n        final Set<SimpleOption> registeredOptionsForContext = this.contextToRegisteredOptions\n                .get(context) == null ? new HashSet<>()\n                        : this.contextToRegisteredOptions.get(context);\n        registeredOptionsForContext.add(new SimpleOption(longForm, shortForm, description,\n                optionality, type, argumentHint));\n        this.contextToRegisteredOptions.put(context, registeredOptionsForContext);\n\n        this.registeredContexts.add(context);\n    }\n\n    private Optional<SimpleOption> registeredOptionForLongForm(final int context,\n            final String longForm) throws AmbiguousAbbreviationException\n    {\n        return checkForLongOption(longForm, this.contextToRegisteredOptions.get(context), true);\n    }\n\n    private Optional<SimpleOption> registeredOptionForShortForm(final int context,\n            final Character shortForm)\n    {\n        return checkForShortOption(shortForm, this.contextToRegisteredOptions.get(context));\n    }\n\n    private void throwIfArgumentHintSeen(final String hint)\n    {\n        if (this.argumentHintsSeen.contains(hint))\n        {\n            throw new CoreException(\"Cannot register argument hint {} more than once!\", hint);\n        }\n    }\n\n    private void throwIfDuplicateLongForm(final String longForm)\n    {\n        if (this.longFormsSeen.contains(longForm))\n        {\n            throw new CoreException(\"Cannot register option {} more than once!\", longForm);\n        }\n    }\n\n    private void throwIfDuplicateShortForm(final Character shortForm)\n    {\n        if (this.shortFormsSeen.contains(shortForm))\n        {\n            throw new CoreException(\"Cannot register option {} more than once!\", shortForm);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/exceptions/AmbiguousAbbreviationException.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing.exceptions;\n\n/**\n * @author lcram\n */\npublic class AmbiguousAbbreviationException extends Exception\n{\n    private static final long serialVersionUID = 8506034533362610699L;\n\n    public AmbiguousAbbreviationException(final String option, final String ambiguousOptions)\n    {\n        super(\"long option \\'\" + option + \"\\' is ambiguous (\" + ambiguousOptions + \")\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/exceptions/ArgumentException.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing.exceptions;\n\n/**\n * @author lcram\n */\npublic class ArgumentException extends Exception\n{\n    private static final long serialVersionUID = 8506034533362610699L;\n\n    public ArgumentException(final String message)\n    {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/exceptions/OptionParseException.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing.exceptions;\n\n/**\n * @author lcram\n */\npublic class OptionParseException extends Exception\n{\n    private static final long serialVersionUID = 2471393426772482019L;\n\n    public OptionParseException(final String message)\n    {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/exceptions/UnknownOptionException.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing.exceptions;\n\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.openstreetmap.atlas.utilities.command.parsing.SimpleOptionAndArgumentParser.SimpleOption;\n\n/**\n * @author lcram\n */\npublic class UnknownOptionException extends Exception\n{\n    private static final long serialVersionUID = 8506034533362610699L;\n\n    private static Optional<String> closestMatchMessage(final String option,\n            final Set<SimpleOption> validOptions)\n    {\n        final Set<String> optionNames = validOptions.stream().map(SimpleOption::getLongForm)\n                .collect(Collectors.toSet());\n\n        String closestOption = null;\n        int minimumDistance = Integer.MAX_VALUE;\n        for (final String optionName : optionNames)\n        {\n            final int distance = StringUtils.getLevenshteinDistance(option, optionName);\n            if (distance < minimumDistance)\n            {\n                closestOption = optionName;\n                minimumDistance = distance;\n            }\n        }\n\n        if (closestOption == null)\n        {\n            return Optional.empty();\n        }\n        return Optional.of(\", did you mean \\'\" + closestOption + \"\\'?\");\n    }\n\n    public UnknownOptionException(final Character option)\n    {\n        super(\"unknown short option \\'\" + option + \"\\'\");\n    }\n\n    public UnknownOptionException(final String option)\n    {\n        super(\"unknown long option \\'\" + option + \"\\'\");\n    }\n\n    public UnknownOptionException(final String option, final Set<SimpleOption> validOptions)\n    {\n        super(\"unknown long option \\'\" + option + \"\\'\"\n                + closestMatchMessage(option, validOptions).orElse(\"\"));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/parsing/exceptions/UnparsableContextException.java",
    "content": "package org.openstreetmap.atlas.utilities.command.parsing.exceptions;\n\nimport java.util.SortedSet;\n\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * @author lcram\n */\npublic class UnparsableContextException extends Exception\n{\n    private static final long serialVersionUID = 8204676424116770097L;\n\n    public UnparsableContextException(final SortedSet<String> exceptionMessagesWeSaw)\n    {\n        super(\"could not match command line to a usage context: \"\n                + System.getProperty(\"line.separator\") + new StringList(exceptionMessagesWeSaw)\n                        .join(System.getProperty(\"line.separator\")));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/AnyToGeoJsonCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.boundary.converters.CountryBoundaryMapGeoJsonConverter;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.CountryBoundaryMapTemplate;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.OutputDirectoryTemplate;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.ShardingTemplate;\n\nimport com.google.gson.GsonBuilder;\n\n/**\n * This command converts our many different file formats to a GeoJSON representation. This may be\n * useful for various visualization software.\n * \n * @author lcram\n */\npublic class AnyToGeoJsonCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final String ATLAS_OPTION_LONG = \"atlas\";\n    private static final String ATLAS_OPTION_DESCRIPTION = \"The path to an atlas file to be converted.\";\n    private static final String ATLAS_OPTION_HINT = \"atlas-file\";\n\n    private static final String COUNTRIES_OPTION_LONG = \"countries\";\n    private static final Character COUNTRIES_OPTION_SHORT = 'c';\n    private static final String COUNTRIES_OPTION_DESCRIPTION = \"A comma separated list of allowlist country codes to exclusively include. Defaults to all.\";\n    private static final String COUNTRIES_OPTION_HINT = \"included-countries\";\n\n    private static final String COUNTRIES_DENY_LIST_OPTION_LONG = \"countries-denylist\";\n    private static final Character COUNTRIES_DENY_LIST_OPTION_SHORT = 'C';\n    private static final String COUNTRIES_DENY_LIST_OPTION_DESCRIPTION = \"A comma separated denylist of country codes to explicitly exclude. Defaults to none.\";\n    private static final String COUNTRIES_DENY_LIST_OPTION_HINT = \"excluded-countries\";\n\n    private static final String POLYGONS_OPTION_LONG = \"use-polygons\";\n    private static final Character POLYGONS_OPTION_SHORT = 'p';\n    private static final String POLYGONS_OPTION_DESCRIPTION = \"Use polygons instead of linestrings for the boundary GeoJSON. This may be better for certain visualization software.\";\n\n    private static final Integer ATLAS_CONTEXT = 3;\n    private static final Integer SHARDING_CONTEXT = 4;\n    private static final Integer BOUNDARY_CONTEXT = 5;\n\n    private static final String OUTPUT_FILE = \"output\";\n    private static final String ATLAS_FILE = OUTPUT_FILE + \"-\" + ATLAS_OPTION_LONG\n            + FileSuffix.GEO_JSON;\n    private static final String SHARDING_FILE = OUTPUT_FILE + \"-sharding\" + FileSuffix.GEO_JSON;\n    private static final String BOUNDARY_FILE = OUTPUT_FILE + \"-country-boundary\"\n            + FileSuffix.GEO_JSON;\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new AnyToGeoJsonCommand().runSubcommandAndExit(args);\n    }\n\n    public AnyToGeoJsonCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        if (this.optionAndArgumentDelegate.getParserContext() == ATLAS_CONTEXT)\n        {\n            return executeAtlasContext();\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == SHARDING_CONTEXT)\n        {\n            return executeShardingContext();\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == BOUNDARY_CONTEXT)\n        {\n            return executeBoundaryContext();\n        }\n        else\n        {\n            throw new AtlasShellToolsException();\n        }\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"any2geojson\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"convert a custom file format to GeoJSON\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", AtlasSearchCommand.class\n                .getResourceAsStream(\"AnyToGeoJsonCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", AtlasSearchCommand.class\n                .getResourceAsStream(\"AnyToGeoJsonCommandExamplesSection.txt\"));\n        registerManualPageSectionsFromTemplate(new ShardingTemplate());\n        registerManualPageSectionsFromTemplate(new CountryBoundaryMapTemplate());\n        registerManualPageSectionsFromTemplate(new OutputDirectoryTemplate());\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOptionWithRequiredArgument(ATLAS_OPTION_LONG, ATLAS_OPTION_DESCRIPTION,\n                OptionOptionality.REQUIRED, ATLAS_OPTION_HINT, ATLAS_CONTEXT);\n        registerOptionWithRequiredArgument(COUNTRIES_OPTION_LONG, COUNTRIES_OPTION_SHORT,\n                COUNTRIES_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL, COUNTRIES_OPTION_HINT,\n                BOUNDARY_CONTEXT);\n        registerOptionWithRequiredArgument(COUNTRIES_DENY_LIST_OPTION_LONG,\n                COUNTRIES_DENY_LIST_OPTION_SHORT, COUNTRIES_DENY_LIST_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, COUNTRIES_DENY_LIST_OPTION_HINT, BOUNDARY_CONTEXT);\n        registerOption(POLYGONS_OPTION_LONG, POLYGONS_OPTION_SHORT, POLYGONS_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, BOUNDARY_CONTEXT);\n\n        registerOptionsAndArgumentsFromTemplate(new ShardingTemplate(SHARDING_CONTEXT));\n        registerOptionsAndArgumentsFromTemplate(new CountryBoundaryMapTemplate(BOUNDARY_CONTEXT));\n        registerOptionsAndArgumentsFromTemplate(\n                new OutputDirectoryTemplate(ATLAS_CONTEXT, SHARDING_CONTEXT, BOUNDARY_CONTEXT));\n\n        super.registerOptionsAndArguments();\n    }\n\n    private int executeAtlasContext()\n    {\n        final File atlasFile = new File(this.optionAndArgumentDelegate\n                .getOptionArgument(ATLAS_OPTION_LONG).orElseThrow(AtlasShellToolsException::new),\n                this.getFileSystem());\n        if (!atlasFile.exists())\n        {\n            this.outputDelegate\n                    .printlnErrorMessage(\"file not found: \" + atlasFile.getAbsolutePathString());\n            return 1;\n        }\n        final Atlas atlas = new AtlasResourceLoader().load(atlasFile);\n        final Optional<Path> pathOptional = OutputDirectoryTemplate.getOutputPath(this);\n        if (pathOptional.isEmpty())\n        {\n            this.outputDelegate.printlnErrorMessage(\"could not save atlas file\");\n            return 1;\n        }\n        final Path concatenatedPath = Paths.get(pathOptional.get().toAbsolutePath().toString(),\n                ATLAS_FILE);\n        final File outputFile = new File(concatenatedPath.toAbsolutePath().toString(),\n                this.getFileSystem());\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate.printlnCommandMessage(\n                    \"writing the atlas geojson file to \" + outputFile.toAbsolutePath().toString());\n        }\n        atlas.saveAsLineDelimitedGeoJsonFeatures(outputFile, (entity, json) ->\n        {\n            // Dummy consumer, we don't need to mutate the JSON\n        });\n        return 0;\n    }\n\n    private int executeBoundaryContext()\n    {\n        Set<String> countries = new HashSet<>();\n        if (this.optionAndArgumentDelegate.hasOption(COUNTRIES_OPTION_LONG))\n        {\n            countries = this.optionAndArgumentDelegate\n                    .getOptionArgument(COUNTRIES_OPTION_LONG, this::parseCommaSeparatedCountries)\n                    .orElse(new HashSet<>());\n        }\n        final boolean usePolygons = this.optionAndArgumentDelegate.hasOption(POLYGONS_OPTION_LONG);\n        Set<String> countriesDenyList = new HashSet<>();\n        if (this.optionAndArgumentDelegate.hasOption(COUNTRIES_DENY_LIST_OPTION_LONG))\n        {\n            countriesDenyList = this.optionAndArgumentDelegate\n                    .getOptionArgument(COUNTRIES_DENY_LIST_OPTION_LONG,\n                            this::parseCommaSeparatedCountries)\n                    .orElse(new HashSet<>());\n        }\n\n        CountryBoundaryMap map = null;\n        final Optional<CountryBoundaryMap> mapOptional = CountryBoundaryMapTemplate\n                .getCountryBoundaryMap(this);\n        if (mapOptional.isEmpty())\n        {\n            this.outputDelegate.printlnErrorMessage(\"failed to load country boundary\");\n            return 1;\n        }\n        map = mapOptional.get();\n\n        final String boundaryJson;\n        if (countries.isEmpty())\n        {\n            boundaryJson = new CountryBoundaryMapGeoJsonConverter().prettyPrint(true)\n                    .withCountryDenyList(countriesDenyList).usePolygons(usePolygons)\n                    .convertToString(map);\n        }\n        else\n        {\n            boundaryJson = new CountryBoundaryMapGeoJsonConverter().withCountryAllowList(countries)\n                    .withCountryDenyList(countriesDenyList).prettyPrint(true)\n                    .usePolygons(usePolygons).convertToString(map);\n        }\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate.printlnCommandMessage(\"converting boundary file to GeoJSON...\");\n        }\n        final Optional<Path> pathOptional = OutputDirectoryTemplate.getOutputPath(this);\n        if (pathOptional.isEmpty())\n        {\n            this.outputDelegate.printlnErrorMessage(\"could not save boundary file\");\n            return 1;\n        }\n        final Path concatenatedPath = Paths.get(pathOptional.get().toAbsolutePath().toString(),\n                BOUNDARY_FILE);\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate.printlnCommandMessage(\"writing the boundary geojson file to \"\n                    + concatenatedPath.toAbsolutePath().toString());\n        }\n        new File(concatenatedPath.toAbsolutePath().toString(), this.getFileSystem())\n                .writeAndClose(boundaryJson);\n\n        return 0;\n    }\n\n    private int executeShardingContext()\n    {\n        final Sharding sharding = ShardingTemplate.getSharding(this);\n        final String shardingJson = new GsonBuilder().setPrettyPrinting().create()\n                .toJson(sharding.asGeoJson());\n        final Optional<Path> pathOptional = OutputDirectoryTemplate.getOutputPath(this);\n        if (pathOptional.isEmpty())\n        {\n            this.outputDelegate.printlnErrorMessage(\"could not save sharding tree\");\n            return 1;\n        }\n        final Path concatenatedPath = Paths.get(pathOptional.get().toAbsolutePath().toString(),\n                SHARDING_FILE);\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate.printlnCommandMessage(\"writing the sharding geojson file to \"\n                    + concatenatedPath.toAbsolutePath().toString());\n        }\n        new File(concatenatedPath.toAbsolutePath().toString(), this.getFileSystem())\n                .writeAndClose(shardingJson);\n        return 0;\n    }\n\n    private Set<String> parseCommaSeparatedCountries(final String countryString)\n    {\n        final Set<String> countrySet = new HashSet<>();\n\n        if (countryString.isEmpty())\n        {\n            return countrySet;\n        }\n\n        countrySet.addAll(Arrays.stream(countryString.split(\",\")).collect(Collectors.toSet()));\n        return countrySet;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.change.Change;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeBuilder;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.change.diff.AtlasDiff;\nimport org.openstreetmap.atlas.geography.atlas.change.serializer.ChangeGeoJsonSerializer;\nimport org.openstreetmap.atlas.geography.atlas.complete.PrettifyStringFormat;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\n\nimport com.google.common.collect.Sets;\n\n/**\n * @author lcram\n */\npublic class AtlasDiffCommand extends AbstractAtlasShellToolsCommand\n{\n    /**\n     * @author matthieun\n     */\n    private static class AtlasDiffCommandContext\n    {\n        private final File beforeAtlasFile;\n        private final File afterAtlasFile;\n        private final boolean useGeoJson;\n        private final boolean useLdGeoJson;\n        private final boolean fullText;\n        private final Long selectedIdentifier;\n        private final ItemType selectedType;\n        private final boolean recursive;\n\n        AtlasDiffCommandContext(final File beforeAtlasFile, final File afterAtlasFile, // NOSONAR\n                final boolean useGeoJson, final boolean useLdGeoJson, final boolean fullText,\n                final Long selectedIdentifier, final ItemType selectedType, final boolean recursive)\n        {\n            this.beforeAtlasFile = beforeAtlasFile;\n            this.afterAtlasFile = afterAtlasFile;\n            this.useGeoJson = useGeoJson;\n            this.useLdGeoJson = useLdGeoJson;\n            this.fullText = fullText;\n            this.selectedIdentifier = selectedIdentifier;\n            this.selectedType = selectedType;\n            this.recursive = recursive;\n        }\n\n        public File getAfterAtlasFile()\n        {\n            return this.afterAtlasFile;\n        }\n\n        public File getBeforeAtlasFile()\n        {\n            return this.beforeAtlasFile;\n        }\n\n        public Long getSelectedIdentifier()\n        {\n            return this.selectedIdentifier;\n        }\n\n        public ItemType getSelectedType()\n        {\n            return this.selectedType;\n        }\n\n        public boolean isFullText()\n        {\n            return this.fullText;\n        }\n\n        public boolean isRecursive()\n        {\n            return this.recursive;\n        }\n\n        public boolean isUseGeoJson()\n        {\n            return this.useGeoJson;\n        }\n\n        public boolean isUseLdGeoJson()\n        {\n            return this.useLdGeoJson;\n        }\n    }\n\n    static final String NO_CHANGE = \"atlases are effectively identical\";\n\n    private static final String BEFORE_ATLAS_ARGUMENT = \"before-atlas(es)\";\n    private static final String AFTER_ATLAS_ARGUMENT = \"after-atlas(es)\";\n\n    private static final List<String> FORMAT_TYPE_STRINGS = Arrays\n            .stream(PrettifyStringFormat.values()).map(PrettifyStringFormat::toString)\n            .collect(Collectors.toList());\n    private static final PrettifyStringFormat DEFAULT_PRETTY_FEATURE_CHANGE_FORMAT = PrettifyStringFormat.MINIMAL_MULTI_LINE;\n    private static final PrettifyStringFormat DEFAULT_PRETTY_COMPLETE_ENTITY_FORMAT = PrettifyStringFormat.MINIMAL_SINGLE_LINE;\n\n    private static final String FEATURE_CHANGE_FORMAT_OPTION_LONG = \"feature-change-format\";\n    private static final String COMPLETE_ENTITY_FORMAT_OPTION_LONG = \"complete-entity-format\";\n    private static final String FEATURE_CHANGE_FORMAT_OPTION_HINT = \"format\";\n    private static final String COMPLETE_ENTITY_FORMAT_OPTION_HINT = \"format\";\n    private static final String FEATURE_CHANGE_FORMAT_OPTION_DESCRIPTION = \"The format type for the constituent FeatureChanges. Valid settings are: \"\n            + new StringList(FORMAT_TYPE_STRINGS).join(\", \") + \". Defaults to \"\n            + DEFAULT_PRETTY_FEATURE_CHANGE_FORMAT.toString() + \".\";\n    private static final String COMPLETE_ENTITY_FORMAT_OPTION_DESCRIPTION = \"The format type for the CompleteEntities within the constituent FeatureChanges. Valid settings are: \"\n            + new StringList(FORMAT_TYPE_STRINGS).join(\", \") + \". Defaults to \"\n            + DEFAULT_PRETTY_COMPLETE_ENTITY_FORMAT.toString() + \".\";\n\n    private static final String LDGEOJSON_OPTION_LONG = \"ldgeojson\";\n    private static final String LDGEOJSON_OPTION_DESCRIPTION = \"Use the line-delimited geoJSON format for output.\";\n    private static final String GEOJSON_OPTION_LONG = \"geojson\";\n    private static final String GEOJSON_OPTION_DESCRIPTION = \"Use the pretty geoJSON format for output.\";\n    private static final String FULL_OPTION_LONG = \"full\";\n    private static final String FULL_OPTION_DESCRIPTION = \"Show the full FeatureChange instead of just the ChangeDescription.\";\n    private static final String FOLDER_SEARCH_RECURSIVE_OPTION_LONG = \"recursive\";\n    private static final String FOLDER_SEARCH_RECURSIVE_OPTION_DESCRIPTION = \"When comparing Atlas folders, search sub-folders too.\";\n\n    private static final List<String> ITEM_TYPE_STRINGS = Arrays.stream(ItemType.values())\n            .map(ItemType::toString).collect(Collectors.toList());\n    private static final String TYPE_OPTION_LONG = \"type\";\n    private static final String TYPE_OPTION_DESCRIPTION = \"The ItemType of the desired feature. Valid types are: \"\n            + new StringList(ITEM_TYPE_STRINGS).join(\", \") + \".\";\n    private static final String TYPE_OPTION_HINT = \"type\";\n\n    private static final String ID_OPTION_LONG = \"id\";\n    private static final String ID_OPTION_DESCRIPTION = \"The identifier of the desired feature.\";\n    private static final String ID_OPTION_HINT = \"id\";\n\n    private static final Integer LDGEOJSON_CONTEXT = 4;\n    private static final Integer GEOJSON_CONTEXT = 5;\n    private static final Integer FULL_CONTEXT = 6;\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new AtlasDiffCommand().runSubcommandAndExit(args);\n    }\n\n    public AtlasDiffCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute() // NOSONAR\n    {\n        final String beforeAtlasPath = this.optionAndArgumentDelegate\n                .getUnaryArgument(BEFORE_ATLAS_ARGUMENT).orElseThrow(AtlasShellToolsException::new);\n        final String afterAtlasPath = this.optionAndArgumentDelegate\n                .getUnaryArgument(AFTER_ATLAS_ARGUMENT).orElseThrow(AtlasShellToolsException::new);\n        final File beforeAtlasFile = new File(beforeAtlasPath, this.getFileSystem());\n        final File afterAtlasFile = new File(afterAtlasPath, this.getFileSystem());\n        boolean useGeoJson = false;\n        boolean useLdGeoJson = false;\n        boolean fullText = false;\n        Long selectedIdentifier = null;\n        ItemType selectedType = null;\n        boolean recursive = false;\n\n        if (this.optionAndArgumentDelegate.hasOption(\n                ID_OPTION_LONG) != this.optionAndArgumentDelegate.hasOption(TYPE_OPTION_LONG))\n        {\n            this.outputDelegate.printlnErrorMessage(\"options --\" + ID_OPTION_LONG + \" and --\"\n                    + TYPE_OPTION_LONG + \" must be supplied together or not at all\");\n            return 2;\n        }\n        if (this.optionAndArgumentDelegate.hasOption(ID_OPTION_LONG))\n        {\n            final String idString = this.optionAndArgumentDelegate.getOptionArgument(ID_OPTION_LONG)\n                    .orElseThrow(AtlasShellToolsException::new);\n            try\n            {\n                selectedIdentifier = Long.parseLong(idString);\n            }\n            catch (final Exception exception)\n            {\n                this.outputDelegate.printlnErrorMessage(\"could not parse id \" + idString);\n                return 2;\n            }\n        }\n        if (this.optionAndArgumentDelegate.hasOption(TYPE_OPTION_LONG))\n        {\n            final String typeString = this.optionAndArgumentDelegate\n                    .getOptionArgument(TYPE_OPTION_LONG).orElseThrow(AtlasShellToolsException::new)\n                    .toUpperCase();\n            try\n            {\n                selectedType = ItemType.valueOf(typeString);\n            }\n            catch (final Exception exception)\n            {\n                this.outputDelegate.printlnErrorMessage(\"could not parse id \" + typeString);\n                return 2;\n            }\n        }\n\n        if (this.optionAndArgumentDelegate.getParserContext() == GEOJSON_CONTEXT)\n        {\n            useGeoJson = true;\n        }\n\n        if (this.optionAndArgumentDelegate.getParserContext() == LDGEOJSON_CONTEXT)\n        {\n            useLdGeoJson = true;\n        }\n\n        if (this.optionAndArgumentDelegate.getParserContext() == FULL_CONTEXT)\n        {\n            fullText = true;\n        }\n\n        if (this.optionAndArgumentDelegate.hasOption(FOLDER_SEARCH_RECURSIVE_OPTION_LONG))\n        {\n            recursive = true;\n        }\n\n        if (!beforeAtlasFile.exists())\n        {\n            this.outputDelegate.printlnWarnMessage(\"file not found: \" + beforeAtlasPath);\n            return 2;\n        }\n        if (!afterAtlasFile.exists())\n        {\n            this.outputDelegate.printlnWarnMessage(\"file not found: \" + afterAtlasPath);\n            return 2;\n        }\n\n        final AtlasDiffCommandContext context = new AtlasDiffCommandContext(beforeAtlasFile,\n                afterAtlasFile, useGeoJson, useLdGeoJson, fullText, selectedIdentifier,\n                selectedType, recursive);\n\n        if (beforeAtlasFile.isDirectory() && afterAtlasFile.isDirectory())\n        {\n            final int result = this.compute(context, beforeAtlasFile, afterAtlasFile);\n            return result > 0 ? 1 : 0;\n        }\n        else if (!beforeAtlasFile.isDirectory() && !afterAtlasFile.isDirectory())\n        {\n            final Atlas beforeAtlas = load(beforeAtlasFile);\n            final Atlas afterAtlas = load(afterAtlasFile);\n\n            final int result = this.compute(context, beforeAtlas, afterAtlas);\n            return result > 0 ? 1 : 0;\n        }\n        else\n        {\n            this.outputDelegate.printlnErrorMessage(\"Cannot compare a file and a directory.\");\n            return 1;\n        }\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"atlas-diff\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"compare two atlas files\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", AtlasDiffCommand.class\n                .getResourceAsStream(\"AtlasDiffCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\",\n                AtlasDiffCommand.class.getResourceAsStream(\"AtlasDiffCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOptionWithRequiredArgument(TYPE_OPTION_LONG, TYPE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, TYPE_OPTION_HINT, DEFAULT_CONTEXT, GEOJSON_CONTEXT,\n                LDGEOJSON_CONTEXT, FULL_CONTEXT);\n        registerOptionWithRequiredArgument(ID_OPTION_LONG, ID_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, ID_OPTION_HINT, DEFAULT_CONTEXT, GEOJSON_CONTEXT,\n                LDGEOJSON_CONTEXT, FULL_CONTEXT);\n        registerOptionWithRequiredArgument(FEATURE_CHANGE_FORMAT_OPTION_LONG,\n                FEATURE_CHANGE_FORMAT_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                FEATURE_CHANGE_FORMAT_OPTION_HINT, FULL_CONTEXT);\n        registerOptionWithRequiredArgument(COMPLETE_ENTITY_FORMAT_OPTION_LONG,\n                COMPLETE_ENTITY_FORMAT_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                COMPLETE_ENTITY_FORMAT_OPTION_HINT, FULL_CONTEXT);\n        registerOption(LDGEOJSON_OPTION_LONG, LDGEOJSON_OPTION_DESCRIPTION,\n                OptionOptionality.REQUIRED, LDGEOJSON_CONTEXT);\n        registerOption(GEOJSON_OPTION_LONG, GEOJSON_OPTION_DESCRIPTION, OptionOptionality.REQUIRED,\n                GEOJSON_CONTEXT);\n        registerOption(FULL_OPTION_LONG, FULL_OPTION_DESCRIPTION, OptionOptionality.REQUIRED,\n                FULL_CONTEXT);\n        registerOption(FOLDER_SEARCH_RECURSIVE_OPTION_LONG,\n                FOLDER_SEARCH_RECURSIVE_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                DEFAULT_CONTEXT, FULL_CONTEXT, GEOJSON_CONTEXT, LDGEOJSON_CONTEXT);\n        registerArgument(BEFORE_ATLAS_ARGUMENT, ArgumentArity.UNARY, ArgumentOptionality.REQUIRED,\n                DEFAULT_CONTEXT, LDGEOJSON_CONTEXT, GEOJSON_CONTEXT, FULL_CONTEXT);\n        registerArgument(AFTER_ATLAS_ARGUMENT, ArgumentArity.UNARY, ArgumentOptionality.REQUIRED,\n                DEFAULT_CONTEXT, LDGEOJSON_CONTEXT, GEOJSON_CONTEXT, FULL_CONTEXT);\n        super.registerOptionsAndArguments();\n    }\n\n    int compute(final AtlasDiffCommandContext context, final File beforeAtlasFile,\n            final File afterAtlasFile)\n    {\n        int result = 0;\n        final Map<String, File> beforeNamesToFiles = new HashMap<>();\n        final Map<String, File> afterNamesToFiles = new HashMap<>();\n        final List<File> beforeFilesToConsider = context.isRecursive()\n                ? beforeAtlasFile.listFilesRecursively(false)\n                : beforeAtlasFile.listFiles(false);\n        final List<File> afterFilesToConsider = context.isRecursive()\n                ? afterAtlasFile.listFilesRecursively(false)\n                : afterAtlasFile.listFiles(false);\n        beforeFilesToConsider.stream().filter(this::checkAtlas).forEach(\n                file -> beforeNamesToFiles.put(getRelativeFileName(beforeAtlasFile, file), file));\n        afterFilesToConsider.stream().filter(this::checkAtlas).forEach(\n                file -> afterNamesToFiles.put(getRelativeFileName(afterAtlasFile, file), file));\n        final Set<String> filesOnlyInBefore = Sets.difference(beforeNamesToFiles.keySet(),\n                afterNamesToFiles.keySet());\n        final Set<String> filesOnlyInAfter = Sets.difference(afterNamesToFiles.keySet(),\n                beforeNamesToFiles.keySet());\n        final Set<String> filesInBoth = Sets.intersection(beforeNamesToFiles.keySet(),\n                afterNamesToFiles.keySet());\n        if (!filesOnlyInBefore.isEmpty())\n        {\n            final String warnMessage = \"Files only in Before Atlas folder:\";\n            this.outputDelegate.printlnWarnMessage(warnMessage);\n            filesOnlyInBefore.stream().sorted().forEach(this.outputDelegate::printlnWarnMessage);\n            result += filesOnlyInBefore.size();\n        }\n        if (!filesOnlyInAfter.isEmpty())\n        {\n            final String warnMessage = \"Files only in After Atlas folder:\";\n            this.outputDelegate.printlnWarnMessage(warnMessage);\n            filesOnlyInAfter.stream().sorted().forEach(this.outputDelegate::printlnWarnMessage);\n            result += filesOnlyInAfter.size();\n        }\n        for (final String name : filesInBoth.stream().sorted().collect(Collectors.toList()))\n        {\n            final Atlas beforeAtlas = load(beforeNamesToFiles.get(name));\n            final Atlas afterAtlas = load(afterNamesToFiles.get(name));\n            this.outputDelegate.printlnStdout(name, TTYAttribute.BOLD, TTYAttribute.GREEN);\n            result += compute(context, beforeAtlas, afterAtlas);\n        }\n        return result > 0 ? 1 : 0;\n    }\n\n    /**\n     * @param context\n     *            The context to apply\n     * @param beforeAtlas\n     *            The before Atlas to compare\n     * @param afterAtlas\n     *            The after Atlas to compare\n     * @return 1 if there are differences\n     */\n    int compute(final AtlasDiffCommandContext context, final Atlas beforeAtlas,\n            final Atlas afterAtlas)\n    {\n        final AtlasDiff diff = new AtlasDiff(beforeAtlas, afterAtlas).saveAllGeometries(false);\n        final Optional<Change> changeOptional = diff.generateChange();\n\n        if (changeOptional.isPresent())\n        {\n            final Optional<Change> trimmedChangeOption = trimChange(context, changeOptional.get());\n            final Change change;\n            if (trimmedChangeOption.isPresent())\n            {\n                change = trimmedChangeOption.get();\n            }\n            else\n            {\n                return 0;\n            }\n\n            final String serializedString;\n            if (context.isUseGeoJson())\n            {\n                serializedString = new ChangeGeoJsonSerializer().convert(change);\n            }\n            else if (context.isUseLdGeoJson())\n            {\n                serializedString = change.toLineDelimitedFeatureChanges(true);\n            }\n            else if (context.isFullText())\n            {\n                final PrettifyStringFormat featureChangeFormat = this.optionAndArgumentDelegate\n                        .getOptionArgument(FEATURE_CHANGE_FORMAT_OPTION_LONG,\n                                PrettifyStringFormat::valueOf)\n                        .orElse(DEFAULT_PRETTY_FEATURE_CHANGE_FORMAT);\n                final PrettifyStringFormat completeEntityFormat = this.optionAndArgumentDelegate\n                        .getOptionArgument(COMPLETE_ENTITY_FORMAT_OPTION_LONG,\n                                PrettifyStringFormat::valueOf)\n                        .orElse(DEFAULT_PRETTY_COMPLETE_ENTITY_FORMAT);\n                serializedString = change.prettify(featureChangeFormat, completeEntityFormat, false)\n                        + \"\\n\";\n            }\n            else\n            {\n                final StringBuilder builder = new StringBuilder();\n                change.changes().forEach(featureChange ->\n                {\n                    builder.append(featureChange.explain());\n                    builder.append(\"\\n\");\n                });\n                serializedString = builder.toString();\n            }\n            this.outputDelegate.printlnStdout(serializedString);\n            return change.changeCount() > 0 ? 1 : 0;\n        }\n        else\n        {\n            if (this.optionAndArgumentDelegate.hasVerboseOption())\n            {\n                this.outputDelegate.printlnCommandMessage(NO_CHANGE);\n            }\n            return 0;\n        }\n    }\n\n    private boolean checkAtlas(final Resource resource)\n    {\n        return AtlasResourceLoader.HAS_ATLAS_EXTENSION.test(resource)\n                || AtlasResourceLoader.HAS_TEXT_ATLAS_EXTENSION.test(resource);\n    }\n\n    private String getRelativeFileName(final File parent, final File file)\n    {\n        return file.getAbsolutePathString().substring(parent.getAbsolutePathString().length() + 1);\n    }\n\n    private Atlas load(final File file)\n    {\n        return new AtlasResourceLoader()\n                .load(new InputStreamResource(file::read).withName(file.getAbsolutePathString()));\n    }\n\n    private Optional<Change> trimChange(final AtlasDiffCommandContext context, final Change change)\n    {\n        if (this.optionAndArgumentDelegate.hasOption(ID_OPTION_LONG)\n                && this.optionAndArgumentDelegate.hasOption(TYPE_OPTION_LONG))\n        {\n            final Optional<FeatureChange> featureChangeOptional = change\n                    .changeFor(context.getSelectedType(), context.getSelectedIdentifier());\n            if (featureChangeOptional.isPresent())\n            {\n                return Optional.of(new ChangeBuilder().add(featureChangeOptional.get()).get());\n            }\n            else\n            {\n                final String stdoutMessage = \"No change found for \" + context.getSelectedType()\n                        + \" \" + context.getSelectedIdentifier();\n                this.outputDelegate.printlnWarnMessage(stdoutMessage);\n                return Optional.empty();\n            }\n        }\n        return Optional.of(change);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasMetadataReaderCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.AtlasLoaderCommand;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\n\n/**\n * @author lcram\n */\npublic class AtlasMetadataReaderCommand extends AtlasLoaderCommand\n{\n    private static final String SIZE_OPTION_LONG = \"size\";\n    private static final String SIZE_OPTION_DESCRIPTION = \"Show feature array sizes.\";\n\n    private static final String ORIGINAL_OPTION_LONG = \"original\";\n    private static final String ORIGINAL_OPTION_DESCRIPTION = \"Show value of 'original' field.\";\n\n    private static final String CODE_VERSION_OPTION_LONG = \"code-version\";\n    private static final String CODE_VERSION_OPTION_DESCRIPTION = \"Show the code version.\";\n\n    private static final String DATA_VERSION_OPTION_LONG = \"data-version\";\n    private static final String DATA_VERSION_OPTION_DESCRIPTION = \"Show the data version.\";\n\n    private static final String COUNTRY_OPTION_LONG = \"country\";\n    private static final String COUNTRY_OPTION_DESCRIPTION = \"Show country(s).\";\n\n    private static final String SHARD_OPTION_LONG = \"shard\";\n    private static final String SHARD_OPTION_DESCRIPTION = \"Show shard(s).\";\n\n    private static final String TAGS_OPTION_LONG = \"tags\";\n    private static final String TAGS_OPTION_DESCRIPTION = \"Show metadata tags.\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new AtlasMetadataReaderCommand().runSubcommandAndExit(args);\n    }\n\n    public AtlasMetadataReaderCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"atlas-metadata-reader\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"read selected fields from the atlas metadata\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", AtlasMetadataReaderCommand.class\n                .getResourceAsStream(\"AtlasMetadataReaderCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", AtlasMetadataReaderCommand.class\n                .getResourceAsStream(\"AtlasMetadataReaderCommandExamplesSection.txt\"));\n        super.registerManualPageSections();\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOption(SIZE_OPTION_LONG, SIZE_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL);\n        registerOption(ORIGINAL_OPTION_LONG, ORIGINAL_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL);\n        registerOption(CODE_VERSION_OPTION_LONG, CODE_VERSION_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL);\n        registerOption(DATA_VERSION_OPTION_LONG, DATA_VERSION_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL);\n        registerOption(COUNTRY_OPTION_LONG, COUNTRY_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL);\n        registerOption(SHARD_OPTION_LONG, SHARD_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL);\n        registerOption(TAGS_OPTION_LONG, TAGS_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL);\n        super.registerOptionsAndArguments();\n    }\n\n    @Override\n    protected void processAtlas(final Atlas atlas, final String atlasFileName,\n            final File atlasResource)\n    {\n        this.outputDelegate.printlnStdout(atlasResource.getPathString() + \" metadata:\",\n                TTYAttribute.BOLD);\n        if (this.optionAndArgumentDelegate.hasOption(SIZE_OPTION_LONG))\n        {\n            final StringBuilder builder = new StringBuilder();\n            builder.append(\"Size: \");\n            builder.append(\"\\n\\tNodes: \");\n            builder.append(atlas.metaData().getSize().getNodeNumber());\n            builder.append(\"\\n\\tEdges: \");\n            builder.append(atlas.metaData().getSize().getEdgeNumber());\n            builder.append(\"\\n\\tAreas: \");\n            builder.append(atlas.metaData().getSize().getAreaNumber());\n            builder.append(\"\\n\\tLines: \");\n            builder.append(atlas.metaData().getSize().getLineNumber());\n            builder.append(\"\\n\\tPoints: \");\n            builder.append(atlas.metaData().getSize().getPointNumber());\n            builder.append(\"\\n\\tRelations: \");\n            builder.append(atlas.metaData().getSize().getRelationNumber());\n            this.outputDelegate.printlnStdout(builder.toString(), TTYAttribute.GREEN);\n        }\n        if (this.optionAndArgumentDelegate.hasOption(ORIGINAL_OPTION_LONG))\n        {\n            this.outputDelegate.printlnStdout(\"Original: \" + atlas.metaData().isOriginal(),\n                    TTYAttribute.GREEN);\n        }\n        if (this.optionAndArgumentDelegate.hasOption(CODE_VERSION_OPTION_LONG))\n        {\n            this.outputDelegate.printlnStdout(\n                    \"Code Version: \" + atlas.metaData().getCodeVersion().orElse(\"null\"),\n                    TTYAttribute.GREEN);\n        }\n        if (this.optionAndArgumentDelegate.hasOption(DATA_VERSION_OPTION_LONG))\n        {\n            this.outputDelegate.printlnStdout(\n                    \"Data Version: \" + atlas.metaData().getDataVersion().orElse(\"null\"),\n                    TTYAttribute.GREEN);\n        }\n        if (this.optionAndArgumentDelegate.hasOption(COUNTRY_OPTION_LONG))\n        {\n            this.outputDelegate.printlnStdout(\n                    \"Country: \" + atlas.metaData().getCountry().orElse(\"null\"), TTYAttribute.GREEN);\n        }\n        if (this.optionAndArgumentDelegate.hasOption(SHARD_OPTION_LONG))\n        {\n            this.outputDelegate.printlnStdout(\n                    \"Shard: \" + atlas.metaData().getShardName().orElse(\"null\"), TTYAttribute.GREEN);\n        }\n        if (this.optionAndArgumentDelegate.hasOption(TAGS_OPTION_LONG))\n        {\n            final StringBuilder builder = new StringBuilder();\n            final SortedSet<String> sortedTags = atlas.metaData().getTags().entrySet().stream()\n                    .map(entry -> entry.getKey() + \" -> \" + entry.getValue())\n                    .collect(Collectors.toCollection(TreeSet::new));\n            builder.append(new StringList(sortedTags).join(\"\\n\\t\"));\n            this.outputDelegate.printlnStdout(\"Tags:\\n\\t\" + builder.toString(), TTYAttribute.GREEN);\n        }\n        // If none of the specific options are supplied, print everything\n        if (!this.optionAndArgumentDelegate.hasOption(SIZE_OPTION_LONG)\n                && !this.optionAndArgumentDelegate.hasOption(ORIGINAL_OPTION_LONG)\n                && !this.optionAndArgumentDelegate.hasOption(CODE_VERSION_OPTION_LONG)\n                && !this.optionAndArgumentDelegate.hasOption(DATA_VERSION_OPTION_LONG)\n                && !this.optionAndArgumentDelegate.hasOption(COUNTRY_OPTION_LONG)\n                && !this.optionAndArgumentDelegate.hasOption(SHARD_OPTION_LONG)\n                && !this.optionAndArgumentDelegate.hasOption(TAGS_OPTION_LONG))\n        {\n            this.outputDelegate.printlnStdout(atlas.metaData().toReadableString(),\n                    TTYAttribute.GREEN);\n        }\n        else\n        {\n            this.outputDelegate.printlnStdout(\"\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasSearchCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.PrettifyStringFormat;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.AtlasLoaderCommand;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.PredicateTemplate;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonObject;\n\n/**\n * Search atlases for some given feature identifiers or properties, with various options and\n * restrictions. Draws some inspiration from similar identifier locator commands by cstaylor and\n * bbreithaupt.\n *\n * @author lcram\n */\n// Some future improvements\n// + fix RelationMember delimiting so that roles with ';' won't break everything?\npublic class AtlasSearchCommand extends AtlasLoaderCommand\n{\n    /**\n     * @author lcram\n     */\n    private static class RelationMemberSearchConstraint\n    {\n        private ItemType type = null;\n        private Long identifier = null;\n        private String role = null;\n\n        RelationMemberSearchConstraint()\n        {\n        }\n\n        /**\n         * Check if this constraint matches at least one member of a given {@link Relation}'s member\n         * list.\n         *\n         * @param relation\n         *            the {@link Relation} against which to check\n         * @return if there is a match\n         */\n        boolean matches(final Relation relation)\n        {\n            boolean constraintMatchedAnItem = false;\n            for (final RelationBean.RelationBeanItem item : relation.getBean())\n            {\n                boolean constraintMatchedType = true;\n                if (this.type != null && !this.type.equals(item.getType()))\n                {\n                    constraintMatchedType = false;\n                }\n                boolean constraintMatchedId = true;\n                if (this.identifier != null && !this.identifier.equals(item.getIdentifier()))\n                {\n                    constraintMatchedId = false;\n                }\n                boolean constraintMatchedRole = true;\n                if (this.role != null && !this.role.equals(item.getRole()))\n                {\n                    constraintMatchedRole = false;\n                }\n\n                constraintMatchedAnItem = constraintMatchedType && constraintMatchedId\n                        && constraintMatchedRole;\n\n                /*\n                 * Break early if we match an item, since we don't need to check any more.\n                 */\n                if (constraintMatchedAnItem)\n                {\n                    break;\n                }\n            }\n\n            return constraintMatchedAnItem;\n        }\n\n        RelationMemberSearchConstraint withId(final Long identifier)\n        {\n            this.identifier = identifier;\n            return this;\n        }\n\n        RelationMemberSearchConstraint withRole(final String role)\n        {\n            this.role = role;\n            return this;\n        }\n\n        RelationMemberSearchConstraint withType(final ItemType type)\n        {\n            this.type = type;\n            return this;\n        }\n    }\n\n    private static final List<String> ITEM_TYPE_STRINGS = Arrays.stream(ItemType.values())\n            .map(ItemType::toString).collect(Collectors.toList());\n    private static final String TYPES_OPTION_LONG = \"type\";\n    private static final String TYPES_OPTION_DESCRIPTION = \"A comma separated list of ItemTypes by which to narrow the search. Valid types are: \"\n            + new StringList(ITEM_TYPE_STRINGS).join(\", \")\n            + \". Defaults to including all values, unless another option (e.g. --startNode) automatically narrows the search space.\";\n    private static final String TYPES_OPTION_HINT = \"types\";\n\n    private static final String BOUNDING_POLYGON_OPTION_LONG = \"bounding-polygons\";\n    private static final String BOUNDING_POLYGON_OPTION_DESCRIPTION = \"Match all features within at least one member of a given colon separated list of bounding polygons.\";\n    private static final String BOUNDING_POLYGON_OPTION_HINT = \"wkt-polygons\";\n\n    private static final String GEOMETRY_OPTION_LONG = \"geometry\";\n    private static final String GEOMETRY_OPTION_DESCRIPTION = \"A colon separated list of exact geometry WKTs for which to search.\";\n    private static final String GEOMETRY_OPTION_HINT = \"wkt-geometry\";\n\n    private static final String SUB_GEOMETRY_OPTION_LONG = \"sub-geometry\";\n    private static final String SUB_GEOMETRY_OPTION_DESCRIPTION = \"Like --geometry, but can match against contained geometry. E.g. POINT(2 2) would match LINESTRING(1 1, 2 2, 3 3).\";\n    private static final String SUB_GEOMETRY_OPTION_HINT = \"wkt-geometry\";\n\n    private static final String TAGGABLEFILTER_OPTION_LONG = \"tag-filter\";\n    private static final String TAGGABLEFILTER_OPTION_DESCRIPTION = \"A TaggableFilter by which to filter the search space.\";\n    private static final String TAGGABLEFILTER_OPTION_HINT = \"filter\";\n\n    private static final String TAGGABLEMATCHER_OPTION_LONG = \"tag-matcher\";\n    private static final String TAGGABLEMATCHER_OPTION_DESCRIPTION = \"A TaggableMatcher by which to filter the search space.\";\n    private static final String TAGGABLEMATCHER_OPTION_HINT = \"matcher\";\n\n    private static final String STARTNODE_OPTION_LONG = \"start-nodes\";\n    private static final String STARTNODE_OPTION_DESCRIPTION = \"A comma separated list of start node identifiers for which to search.\";\n    private static final String STARTNODE_OPTION_HINT = \"ids\";\n\n    private static final String ENDNODE_OPTION_LONG = \"end-nodes\";\n    private static final String ENDNODE_OPTION_DESCRIPTION = \"A comma separated list of end node identifiers for which to search.\";\n    private static final String ENDNODE_OPTION_HINT = \"ids\";\n\n    private static final String INEDGE_OPTION_LONG = \"in-edges\";\n    private static final String INEDGE_OPTION_DESCRIPTION = \"A comma separated list of in edge identifiers for which to search.\";\n    private static final String INEDGE_OPTION_HINT = \"ids\";\n\n    private static final String OUTEDGE_OPTION_LONG = \"out-edges\";\n    private static final String OUTEDGE_OPTION_DESCRIPTION = \"A comma separated list of out edge identifiers for which to search.\";\n    private static final String OUTEDGE_OPTION_HINT = \"ids\";\n\n    private static final String PARENT_RELATIONS_OPTION_LONG = \"parent-relations\";\n    private static final String PARENT_RELATIONS_OPTION_DESCRIPTION = \"A comma separated list of parent relation identifiers for which to search.\";\n    private static final String PARENT_RELATIONS_OPTION_HINT = \"ids\";\n\n    private static final String RELATION_MEMBERS_OR_OPTION_LONG = \"relation-members\";\n    private static final String RELATION_MEMBERS_OR_OPTION_DESCRIPTION = \"Filter to relations that contain at least one of the given semicolon separated members.\"\n            + \" Members can be specified like e.g. `AREA,1234,myrole;EDGE,4567,*;*,9012,*'.\"\n            + \" Here you can see you may optionally supply a `*' wildcard to broaden the search constraints.\"\n            + \" This example string filters for relations that contain an Area with ID 1234 and role `myrole', OR an Edge with ID 4567 and any role, OR any feature type with ID 9012 and any role.\";\n    private static final String RELATION_MEMBERS_OR_OPTION_HINT = \"member[;member]...\";\n\n    private static final String RELATION_MEMBERS_AND_OPTION_LONG = \"and-relation-members\";\n    private static final String RELATION_MEMBERS_AND_OPTION_DESCRIPTION = \"Filter to relations that contain all of the given semicolon separated members.\"\n            + \" Members can be specified like e.g. `EDGE,*,to;EDGE,*,from;NODE,1234,via'.\"\n            + \" Here you can see you may optionally supply a `*' wildcard to broaden the search constraints.\"\n            + \" This example string filters for relations that contain a `to' AND `from' Edge with any ID, as well as a Node 1234 with role `via'.\";\n    private static final String RELATION_MEMBERS_AND_OPTION_HINT = \"member[;member]...\";\n\n    private static final String ID_OPTION_LONG = \"id\";\n    private static final String ID_OPTION_DESCRIPTION = \"A comma separated list of Atlas ids for which to search.\";\n    private static final String ID_OPTION_HINT = \"ids\";\n\n    private static final String OSMID_OPTION_LONG = \"osmid\";\n    private static final String OSMID_OPTION_DESCRIPTION = \"A comma separated list of OSM ids for which to search.\";\n    private static final String OSMID_OPTION_HINT = \"osmids\";\n\n    private static final String ALL_OPTION_LONG = \"all\";\n    private static final String ALL_OPTION_DESCRIPTION = \"Ignore all other criteria and just print all entities.\";\n\n    private static final String JSON_OPTION_LONG = \"json\";\n    private static final String JSON_OPTION_DESCRIPTION = \"Print matches in a parsable JSON format. For e.g., try chaining output into `jq' for more flexibility.\";\n\n    private static final String OUTPUT_ATLAS = \"collected-multi.atlas\";\n    private static final String COLLECT_OPTION_LONG = \"collect-matching\";\n    private static final String COLLECT_OPTION_DESCRIPTION = \"Collect all matching atlas files and save to a file using the MultiAtlas.\";\n\n    private static final Integer ALL_TYPES_CONTEXT = 3;\n    private static final Integer EDGE_ONLY_CONTEXT = 4;\n    private static final Integer NODE_ONLY_CONTEXT = 5;\n    private static final Integer RELATION_ONLY_CONTEXT = 6;\n    private static final Integer SHOW_ALL_CONTEXT = 7;\n\n    private static final String COULD_NOT_PARSE = \"could not parse %s '%s'\";\n\n    private static final List<String> IMPORTS_ALLOW_LIST = Arrays.asList(\n            \"org.openstreetmap.atlas.geography.atlas.items\",\n            \"org.openstreetmap.atlas.tags.annotations\",\n            \"org.openstreetmap.atlas.tags.annotations.validation\",\n            \"org.openstreetmap.atlas.tags.annotations.extraction\", \"org.openstreetmap.atlas.tags\",\n            \"org.openstreetmap.atlas.tags.names\", \"org.openstreetmap.atlas.geography\",\n            \"org.openstreetmap.atlas.utilities.collections\");\n    private static final String WILDCARD = \"*\";\n\n    private Set<String> geometryWkts;\n    private Set<String> subGeometryWkts;\n    private Set<String> boundingWkts;\n    private TaggableFilter taggableFilter;\n    private TaggableMatcher taggableMatcher;\n    private Set<Long> startNodeIds;\n    private Set<Long> endNodeIds;\n    private Set<Long> inEdgeIds;\n    private Set<Long> outEdgeIds;\n    private Set<Long> parentRelations;\n    private Set<RelationMemberSearchConstraint> relationMemberConstraintsOR;\n    private Set<RelationMemberSearchConstraint> relationMemberConstraintsAND;\n    private Predicate<AtlasEntity> predicate;\n\n    private Set<Long> ids;\n    private Set<Long> osmIds;\n    private Set<ItemType> typesToCheckFromOption;\n    private final Set<ItemType> impliedTypesToCheck;\n\n    private final Set<Atlas> matchingAtlases;\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new AtlasSearchCommand().runSubcommandAndExit(args);\n    }\n\n    public AtlasSearchCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n        this.geometryWkts = new HashSet<>();\n        this.subGeometryWkts = new HashSet<>();\n        this.boundingWkts = new HashSet<>();\n        this.startNodeIds = new HashSet<>();\n        this.endNodeIds = new HashSet<>();\n        this.inEdgeIds = new HashSet<>();\n        this.outEdgeIds = new HashSet<>();\n        this.parentRelations = new HashSet<>();\n        this.relationMemberConstraintsOR = new HashSet<>();\n        this.relationMemberConstraintsAND = new HashSet<>();\n\n        this.ids = new HashSet<>();\n        this.osmIds = new HashSet<>();\n        this.typesToCheckFromOption = new HashSet<>();\n        this.impliedTypesToCheck = new HashSet<>();\n\n        this.matchingAtlases = new HashSet<>();\n    }\n\n    @Override\n    public int finish()\n    {\n        if (this.optionAndArgumentDelegate.hasOption(COLLECT_OPTION_LONG)\n                && !this.matchingAtlases.isEmpty())\n        {\n            final Path concatenatedPath = this.getFileSystem()\n                    .getPath(getOutputPath().toAbsolutePath().toString(), OUTPUT_ATLAS);\n            final File outputFile = new File(concatenatedPath.toAbsolutePath().toString(),\n                    this.getFileSystem());\n            final Atlas outputAtlas;\n            if (this.matchingAtlases.size() == 1)\n            {\n                outputAtlas = new ArrayList<>(this.matchingAtlases).get(0);\n                outputAtlas.save(outputFile);\n            }\n            else\n            {\n                outputAtlas = new MultiAtlas(this.matchingAtlases);\n                new PackedAtlasCloner().cloneFrom(outputAtlas).save(outputFile);\n            }\n\n            if (this.optionAndArgumentDelegate.hasVerboseOption())\n            {\n                this.outputDelegate\n                        .printlnCommandMessage(\"saved to \" + concatenatedPath.toString());\n            }\n        }\n\n        if (this.matchingAtlases.isEmpty())\n        {\n            return 1;\n        }\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"find\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"find features with given identifiers or properties in given atlas(es)\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", AtlasSearchCommand.class\n                .getResourceAsStream(\"AtlasSearchCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", AtlasSearchCommand.class\n                .getResourceAsStream(\"AtlasSearchCommandExamplesSection.txt\"));\n        registerManualPageSectionsFromTemplate(new PredicateTemplate());\n        super.registerManualPageSections();\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOptionWithRequiredArgument(TYPES_OPTION_LONG, TYPES_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, TYPES_OPTION_HINT, ALL_TYPES_CONTEXT);\n\n        registerOptionWithRequiredArgument(BOUNDING_POLYGON_OPTION_LONG,\n                BOUNDING_POLYGON_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                BOUNDING_POLYGON_OPTION_HINT, ALL_TYPES_CONTEXT, EDGE_ONLY_CONTEXT,\n                NODE_ONLY_CONTEXT, RELATION_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(GEOMETRY_OPTION_LONG, GEOMETRY_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, GEOMETRY_OPTION_HINT, ALL_TYPES_CONTEXT,\n                EDGE_ONLY_CONTEXT, NODE_ONLY_CONTEXT, RELATION_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(SUB_GEOMETRY_OPTION_LONG,\n                SUB_GEOMETRY_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                SUB_GEOMETRY_OPTION_HINT, ALL_TYPES_CONTEXT, EDGE_ONLY_CONTEXT, NODE_ONLY_CONTEXT,\n                RELATION_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(TAGGABLEFILTER_OPTION_LONG,\n                TAGGABLEFILTER_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                TAGGABLEFILTER_OPTION_HINT, ALL_TYPES_CONTEXT, EDGE_ONLY_CONTEXT, NODE_ONLY_CONTEXT,\n                RELATION_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(TAGGABLEMATCHER_OPTION_LONG,\n                TAGGABLEMATCHER_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                TAGGABLEMATCHER_OPTION_HINT, ALL_TYPES_CONTEXT, EDGE_ONLY_CONTEXT,\n                NODE_ONLY_CONTEXT, RELATION_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(STARTNODE_OPTION_LONG, STARTNODE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, STARTNODE_OPTION_HINT, EDGE_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(ENDNODE_OPTION_LONG, ENDNODE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, ENDNODE_OPTION_HINT, EDGE_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(INEDGE_OPTION_LONG, INEDGE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, INEDGE_OPTION_HINT, NODE_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(OUTEDGE_OPTION_LONG, OUTEDGE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, OUTEDGE_OPTION_HINT, NODE_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(PARENT_RELATIONS_OPTION_LONG,\n                PARENT_RELATIONS_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                PARENT_RELATIONS_OPTION_HINT, ALL_TYPES_CONTEXT, EDGE_ONLY_CONTEXT,\n                NODE_ONLY_CONTEXT, RELATION_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(RELATION_MEMBERS_OR_OPTION_LONG,\n                RELATION_MEMBERS_OR_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                RELATION_MEMBERS_OR_OPTION_HINT, RELATION_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(RELATION_MEMBERS_AND_OPTION_LONG,\n                RELATION_MEMBERS_AND_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                RELATION_MEMBERS_AND_OPTION_HINT, RELATION_ONLY_CONTEXT);\n        registerOptionsAndArgumentsFromTemplate(new PredicateTemplate(ALL_TYPES_CONTEXT,\n                EDGE_ONLY_CONTEXT, NODE_ONLY_CONTEXT, RELATION_ONLY_CONTEXT));\n\n        registerOptionWithRequiredArgument(ID_OPTION_LONG, ID_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, ID_OPTION_HINT, ALL_TYPES_CONTEXT, EDGE_ONLY_CONTEXT,\n                NODE_ONLY_CONTEXT, RELATION_ONLY_CONTEXT);\n        registerOptionWithRequiredArgument(OSMID_OPTION_LONG, OSMID_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, OSMID_OPTION_HINT, ALL_TYPES_CONTEXT, EDGE_ONLY_CONTEXT,\n                NODE_ONLY_CONTEXT, RELATION_ONLY_CONTEXT);\n\n        registerOption(ALL_OPTION_LONG, ALL_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                SHOW_ALL_CONTEXT);\n        registerOption(JSON_OPTION_LONG, JSON_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                ALL_TYPES_CONTEXT, EDGE_ONLY_CONTEXT, NODE_ONLY_CONTEXT, RELATION_ONLY_CONTEXT,\n                SHOW_ALL_CONTEXT);\n\n        registerOption(COLLECT_OPTION_LONG, COLLECT_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL);\n        super.registerOptionsAndArguments();\n    }\n\n    @Override\n    public int start()\n    {\n        /*\n         * Get the types we will need to search for, implied by the option context. So for example,\n         * if the user supplied an option that indicates they are only interested in Relations (e.g.\n         * --relation-members), then we can set the impliedTypesToCheck to contain only RELATION.\n         */\n        if (this.optionAndArgumentDelegate.getParserContext() == ALL_TYPES_CONTEXT)\n        {\n            this.impliedTypesToCheck.addAll(Sets.hashSet(ItemType.values()));\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == NODE_ONLY_CONTEXT)\n        {\n            this.impliedTypesToCheck.add(ItemType.NODE);\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == EDGE_ONLY_CONTEXT)\n        {\n            this.impliedTypesToCheck.add(ItemType.EDGE);\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == RELATION_ONLY_CONTEXT)\n        {\n            this.impliedTypesToCheck.add(ItemType.RELATION);\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == SHOW_ALL_CONTEXT)\n        {\n            this.impliedTypesToCheck.addAll(Sets.hashSet(ItemType.values()));\n            /*\n             * If our context is from --all, we don't need to do anything else.\n             */\n            return 0;\n        }\n\n        /*\n         * Handle the various search properties.\n         */\n        if (this.optionAndArgumentDelegate.getParserContext() == ALL_TYPES_CONTEXT)\n        {\n            this.typesToCheckFromOption = this.optionAndArgumentDelegate\n                    .getOptionArgument(TYPES_OPTION_LONG, this::parseCommaSeparatedItemTypes)\n                    .orElse(new HashSet<>());\n        }\n        this.boundingWkts = this.optionAndArgumentDelegate\n                .getOptionArgument(BOUNDING_POLYGON_OPTION_LONG, this::parseColonSeparatedWkts)\n                .orElse(new HashSet<>());\n        this.geometryWkts = this.optionAndArgumentDelegate\n                .getOptionArgument(GEOMETRY_OPTION_LONG, this::parseColonSeparatedWkts)\n                .orElse(new HashSet<>());\n        this.subGeometryWkts = this.optionAndArgumentDelegate\n                .getOptionArgument(SUB_GEOMETRY_OPTION_LONG, this::parseColonSeparatedWkts)\n                .orElse(new HashSet<>());\n        this.taggableFilter = this.optionAndArgumentDelegate\n                .getOptionArgument(TAGGABLEFILTER_OPTION_LONG, TaggableFilter::forDefinition)\n                .orElse(null);\n        this.taggableMatcher = this.optionAndArgumentDelegate\n                .getOptionArgument(TAGGABLEMATCHER_OPTION_LONG, TaggableMatcher::from).orElse(null);\n        if (this.optionAndArgumentDelegate.getParserContext() == EDGE_ONLY_CONTEXT)\n        {\n            this.startNodeIds = this.optionAndArgumentDelegate\n                    .getOptionArgument(STARTNODE_OPTION_LONG, this::parseCommaSeparatedLongs)\n                    .orElse(new HashSet<>());\n            this.endNodeIds = this.optionAndArgumentDelegate\n                    .getOptionArgument(ENDNODE_OPTION_LONG, this::parseCommaSeparatedLongs)\n                    .orElse(new HashSet<>());\n        }\n        if (this.optionAndArgumentDelegate.getParserContext() == NODE_ONLY_CONTEXT)\n        {\n            this.inEdgeIds = this.optionAndArgumentDelegate\n                    .getOptionArgument(INEDGE_OPTION_LONG, this::parseCommaSeparatedLongs)\n                    .orElse(new HashSet<>());\n            this.outEdgeIds = this.optionAndArgumentDelegate\n                    .getOptionArgument(OUTEDGE_OPTION_LONG, this::parseCommaSeparatedLongs)\n                    .orElse(new HashSet<>());\n        }\n        if (this.optionAndArgumentDelegate.getParserContext() == RELATION_ONLY_CONTEXT)\n        {\n            this.relationMemberConstraintsOR = this.optionAndArgumentDelegate\n                    .getOptionArgument(RELATION_MEMBERS_OR_OPTION_LONG,\n                            this::parseSemicolonSeparatedRelationMembers)\n                    .orElse(new HashSet<>());\n            this.relationMemberConstraintsAND = this.optionAndArgumentDelegate\n                    .getOptionArgument(RELATION_MEMBERS_AND_OPTION_LONG,\n                            this::parseSemicolonSeparatedRelationMembers)\n                    .orElse(new HashSet<>());\n        }\n        this.parentRelations = this.optionAndArgumentDelegate\n                .getOptionArgument(PARENT_RELATIONS_OPTION_LONG, this::parseCommaSeparatedLongs)\n                .orElse(new HashSet<>());\n        this.predicate = PredicateTemplate.getPredicate(AtlasEntity.class, IMPORTS_ALLOW_LIST, this)\n                .orElse(null);\n\n        /*\n         * Handle identifier searches.\n         */\n        this.ids = this.optionAndArgumentDelegate\n                .getOptionArgument(ID_OPTION_LONG, this::parseCommaSeparatedLongs)\n                .orElse(new HashSet<>());\n        this.osmIds = this.optionAndArgumentDelegate\n                .getOptionArgument(OSMID_OPTION_LONG, this::parseCommaSeparatedLongs)\n                .orElse(new HashSet<>());\n\n        if (this.typesToCheckFromOption.isEmpty() && this.boundingWkts.isEmpty()\n                && this.geometryWkts.isEmpty() && this.subGeometryWkts.isEmpty()\n                && this.taggableFilter == null && this.taggableMatcher == null\n                && this.startNodeIds.isEmpty() && this.endNodeIds.isEmpty()\n                && this.inEdgeIds.isEmpty() && this.outEdgeIds.isEmpty()\n                && this.relationMemberConstraintsOR.isEmpty()\n                && this.relationMemberConstraintsAND.isEmpty() && this.parentRelations.isEmpty()\n                && this.predicate == null && this.ids.isEmpty() && this.osmIds.isEmpty())\n        {\n            this.outputDelegate\n                    .printlnErrorMessage(\"no filtering objects were successfully constructed\");\n            return 1;\n        }\n\n        return 0;\n    }\n\n    @Override\n    protected void processAtlas(final Atlas atlas, final String atlasFileName, // NOSONAR\n            final File atlasResource)\n    {\n        List<AtlasEntity> boundedEntities = null;\n        if (!this.boundingWkts.isEmpty())\n        {\n            boundedEntities = entitiesBoundedByWktGeometry(this.boundingWkts, atlas);\n        }\n\n        Iterable<AtlasEntity> entitiesWeAreChecking = atlas.entities();\n        if (boundedEntities != null)\n        {\n            entitiesWeAreChecking = boundedEntities;\n        }\n\n        /*\n         * This loop is O(N) (where N is the number of atlas entities), assuming the lists of\n         * provided evaluation properties are much smaller than the size of the entity set. We try\n         * every condition to see if we can falsify this entity as a match candidate.\n         */\n        for (final AtlasEntity entity : entitiesWeAreChecking) // NOSONAR\n        {\n            boolean entityMatchesAllCriteriaSoFar = true;\n            if (!this.impliedTypesToCheck.contains(entity.getType()))\n            {\n                entityMatchesAllCriteriaSoFar = false;\n            }\n            if (entityMatchesAllCriteriaSoFar && !this.typesToCheckFromOption.isEmpty()\n                    && !this.typesToCheckFromOption.contains(entity.getType()))\n            {\n                entityMatchesAllCriteriaSoFar = false;\n            }\n            if (entityMatchesAllCriteriaSoFar && this.taggableFilter != null\n                    && !this.taggableFilter.test(entity))\n            {\n                entityMatchesAllCriteriaSoFar = false;\n            }\n            if (entityMatchesAllCriteriaSoFar && this.taggableMatcher != null\n                    && !this.taggableMatcher.test(entity))\n            {\n                entityMatchesAllCriteriaSoFar = false;\n            }\n\n            if (entityMatchesAllCriteriaSoFar && !this.ids.isEmpty()\n                    && !this.ids.contains(entity.getIdentifier()))\n            {\n                entityMatchesAllCriteriaSoFar = false;\n            }\n            if (entityMatchesAllCriteriaSoFar && !this.osmIds.isEmpty()\n                    && !this.osmIds.contains(entity.getOsmIdentifier()))\n            {\n                entityMatchesAllCriteriaSoFar = false;\n            }\n\n            if (entityMatchesAllCriteriaSoFar && !this.geometryWkts.isEmpty())\n            {\n                boolean matchedAtLeastOneWktGeometry = false;\n                for (final String wkt : this.geometryWkts)\n                {\n                    if (entityMatchesWktGeometry(entity, wkt))\n                    {\n                        matchedAtLeastOneWktGeometry = true;\n                    }\n                }\n                if (!matchedAtLeastOneWktGeometry)\n                {\n                    entityMatchesAllCriteriaSoFar = false;\n                }\n            }\n            if (entityMatchesAllCriteriaSoFar && !this.subGeometryWkts.isEmpty())\n            {\n                boolean containedAtLeastOneWktGeometry = false;\n                for (final String wkt : this.subGeometryWkts)\n                {\n                    if (entityContainsWktGeometry(entity, wkt))\n                    {\n                        containedAtLeastOneWktGeometry = true;\n                    }\n                }\n                if (!containedAtLeastOneWktGeometry)\n                {\n                    entityMatchesAllCriteriaSoFar = false;\n                }\n            }\n            if (entityMatchesAllCriteriaSoFar && this.predicate != null\n                    && !this.predicate.test(entity))\n            {\n                entityMatchesAllCriteriaSoFar = false;\n            }\n            if (entityMatchesAllCriteriaSoFar\n                    && this.optionAndArgumentDelegate.getParserContext() == NODE_ONLY_CONTEXT)\n            {\n                final Node node = (Node) entity;\n                final Set<Long> intersectingInEdgeIdentifiers = com.google.common.collect.Sets\n                        .intersection(node.inEdges().stream().map(Edge::getIdentifier)\n                                .collect(Collectors.toSet()), this.inEdgeIds);\n                final Set<Long> intersectingOutEdgeIdentifiers = com.google.common.collect.Sets\n                        .intersection(node.outEdges().stream().map(Edge::getIdentifier)\n                                .collect(Collectors.toSet()), this.outEdgeIds);\n                if (!this.inEdgeIds.isEmpty() && intersectingInEdgeIdentifiers.isEmpty())\n                {\n                    entityMatchesAllCriteriaSoFar = false;\n                }\n                if (!this.outEdgeIds.isEmpty() && intersectingOutEdgeIdentifiers.isEmpty())\n                {\n                    entityMatchesAllCriteriaSoFar = false;\n                }\n            }\n            if (entityMatchesAllCriteriaSoFar\n                    && this.optionAndArgumentDelegate.getParserContext() == EDGE_ONLY_CONTEXT)\n            {\n                final Edge edge = (Edge) entity;\n                if (!this.startNodeIds.isEmpty()\n                        && !this.startNodeIds.contains(edge.start().getIdentifier()))\n                {\n                    entityMatchesAllCriteriaSoFar = false;\n                }\n                if (!this.endNodeIds.isEmpty()\n                        && !this.endNodeIds.contains(edge.end().getIdentifier()))\n                {\n                    entityMatchesAllCriteriaSoFar = false;\n                }\n            }\n            if (entityMatchesAllCriteriaSoFar\n                    && this.optionAndArgumentDelegate.getParserContext() == RELATION_ONLY_CONTEXT\n                    && !this.relationMemberConstraintsAND.isEmpty())\n            {\n                final Relation relation = (Relation) entity;\n                for (final RelationMemberSearchConstraint constraint : this.relationMemberConstraintsAND)\n                {\n                    if (!constraint.matches(relation))\n                    {\n                        entityMatchesAllCriteriaSoFar = false;\n                        break;\n                    }\n                }\n            }\n            if (entityMatchesAllCriteriaSoFar\n                    && this.optionAndArgumentDelegate.getParserContext() == RELATION_ONLY_CONTEXT\n                    && !this.relationMemberConstraintsOR.isEmpty())\n            {\n                final Relation relation = (Relation) entity;\n                boolean foundMemberMatch = false;\n                for (final RelationMemberSearchConstraint constraint : this.relationMemberConstraintsOR)\n                {\n                    if (constraint.matches(relation))\n                    {\n                        foundMemberMatch = true;\n                        break;\n                    }\n                }\n                if (!foundMemberMatch)\n                {\n                    entityMatchesAllCriteriaSoFar = false;\n                }\n            }\n            if (entityMatchesAllCriteriaSoFar && !this.parentRelations.isEmpty())\n            {\n                final Set<Long> intersectingParentRelationIdentifiers = com.google.common.collect.Sets\n                        .intersection(entity.relations().stream().map(Relation::getIdentifier)\n                                .collect(Collectors.toSet()), this.parentRelations);\n                if (intersectingParentRelationIdentifiers.isEmpty())\n                {\n                    entityMatchesAllCriteriaSoFar = false;\n                }\n            }\n\n            /*\n             * If we made it here while matching all criteria, then we can print a diagnostic\n             * detailing the find.\n             */\n            if (entityMatchesAllCriteriaSoFar)\n            {\n                this.matchingAtlases.add(atlas);\n                if (this.optionAndArgumentDelegate.hasOption(JSON_OPTION_LONG))\n                {\n                    printEntityWithJSONFormat(entity, atlasResource, atlas);\n                }\n                else\n                {\n                    printEntityWithHumanReadableFormat(entity, atlasResource);\n                }\n            }\n        }\n    }\n\n    private List<AtlasEntity> entitiesBoundedByWktGeometry(final Iterable<String> wkts,\n            final Atlas atlas) // NOSONAR\n    {\n        final List<AtlasEntity> entities = new ArrayList<>();\n\n        for (final String wkt : wkts) // NOSONAR\n        {\n            final Geometry geometry = parseWkt(wkt);\n            if (geometry == null)\n            {\n                continue;\n            }\n\n            Polygon inputPolygon = null;\n            if (geometry instanceof org.locationtech.jts.geom.Polygon)\n            {\n                inputPolygon = new JtsPolygonConverter()\n                        .backwardConvert((org.locationtech.jts.geom.Polygon) geometry);\n            }\n            else\n            {\n                this.outputDelegate.printlnErrorMessage(\"--\" + BOUNDING_POLYGON_OPTION_LONG\n                        + \" only supports POLYGON, found \" + geometry.getClass().getName());\n                continue;\n            }\n\n            for (final AtlasEntity withinEntity : atlas.entitiesWithin(inputPolygon))\n            {\n                entities.add(withinEntity);\n            }\n        }\n        return entities;\n    }\n\n    private boolean entityContainsWktGeometry(final AtlasEntity entity, final String wkt) // NOSONAR\n    {\n        if (entity.getType() == ItemType.RELATION)\n        {\n            return false;\n        }\n\n        final Geometry geometry = parseWkt(wkt);\n        if (geometry == null)\n        {\n            return false;\n        }\n\n        Location inputLocation = null;\n        PolyLine inputPolyline = null;\n        if (geometry instanceof Point)\n        {\n            inputLocation = new JtsPointConverter().backwardConvert((Point) geometry);\n        }\n        else if (geometry instanceof LineString)\n        {\n            inputPolyline = new JtsPolyLineConverter().backwardConvert((LineString) geometry);\n        }\n        else\n        {\n            this.outputDelegate.printlnErrorMessage(\n                    \"--\" + SUB_GEOMETRY_OPTION_LONG + \" only supports POINT and LINESTRING, found \"\n                            + geometry.getClass().getName());\n            return false;\n        }\n\n        boolean matchedSomething;\n        if (entity.getType() == ItemType.POINT || entity.getType() == ItemType.NODE)\n        {\n            final Location location = ((LocationItem) entity).getLocation();\n            if (inputLocation != null)\n            {\n                matchedSomething = location.equals(inputLocation);\n                return matchedSomething;\n            }\n        }\n        else if (entity.getType() == ItemType.LINE || entity.getType() == ItemType.EDGE)\n        {\n            final PolyLine line = ((LineItem) entity).asPolyLine();\n            if (inputLocation != null)\n            {\n                matchedSomething = line.contains(inputLocation);\n                if (matchedSomething)\n                {\n                    return true;\n                }\n            }\n            if (inputPolyline != null)\n            {\n                matchedSomething = line.overlapsShapeOf(inputPolyline);\n                return matchedSomething;\n            }\n        }\n        else if (entity.getType() == ItemType.AREA)\n        {\n            final Polygon polygon = ((Area) entity).asPolygon();\n            if (inputLocation != null)\n            {\n                matchedSomething = polygon.contains(inputLocation);\n                if (matchedSomething)\n                {\n                    return true;\n                }\n            }\n            if (inputPolyline != null)\n            {\n                matchedSomething = polygon.overlapsShapeOf(inputPolyline);\n                return matchedSomething;\n            }\n        }\n        return false;\n    }\n\n    private boolean entityMatchesWktGeometry(final AtlasEntity entity, final String wkt) // NOSONAR\n    {\n        if (entity.getType() == ItemType.RELATION)\n        {\n            return false;\n        }\n\n        final Geometry geometry = parseWkt(wkt);\n        if (geometry == null)\n        {\n            return false;\n        }\n\n        Location inputLocation = null;\n        PolyLine inputPolyLine = null;\n        Polygon inputPolygon = null;\n        if (geometry instanceof Point)\n        {\n            inputLocation = new JtsPointConverter().backwardConvert((Point) geometry);\n        }\n        else if (geometry instanceof LineString)\n        {\n            inputPolyLine = new JtsPolyLineConverter().backwardConvert((LineString) geometry);\n        }\n        else if (geometry instanceof org.locationtech.jts.geom.Polygon)\n        {\n            inputPolygon = new JtsPolygonConverter()\n                    .backwardConvert((org.locationtech.jts.geom.Polygon) geometry);\n        }\n        else\n        {\n            this.outputDelegate.printlnErrorMessage(\"--\" + GEOMETRY_OPTION_LONG\n                    + \" only supports POINT, LINESTRING, and POLYGON, found \"\n                    + geometry.getClass().getName());\n            return false;\n        }\n\n        final boolean matchedSomething;\n        if (entity.getType() == ItemType.POINT || entity.getType() == ItemType.NODE)\n        {\n            final Location location = ((LocationItem) entity).getLocation();\n            if (inputLocation != null)\n            {\n                matchedSomething = location.equals(inputLocation);\n                return matchedSomething;\n            }\n        }\n        else if (entity.getType() == ItemType.LINE || entity.getType() == ItemType.EDGE)\n        {\n            final PolyLine line = ((LineItem) entity).asPolyLine();\n            if (inputPolyLine != null)\n            {\n                matchedSomething = line.equals(inputPolyLine);\n                return matchedSomething;\n            }\n        }\n        else if (entity.getType() == ItemType.AREA)\n        {\n            final Polygon polygon = ((Area) entity).asPolygon();\n            if (inputPolygon != null)\n            {\n                matchedSomething = polygon.equals(inputPolygon);\n                return matchedSomething;\n            }\n        }\n        return false;\n    }\n\n    private Optional<Tuple<String, Long>> extractIdFromMemberElement(final String element,\n            final String member)\n    {\n        if (WILDCARD.equals(element))\n        {\n            return Optional.of(new Tuple<>(WILDCARD, null));\n        }\n\n        final long identifier;\n        try\n        {\n            identifier = Long.parseLong(element);\n            return Optional.of(new Tuple<>(element, identifier));\n        }\n        catch (final NumberFormatException exception)\n        {\n            this.outputDelegate.printlnErrorMessage(\n                    \"could not parse ID `\" + element + \"' from member `\" + member + \"'\");\n            return Optional.empty();\n        }\n    }\n\n    private Optional<Tuple<String, ItemType>> extractItemTypeFromMemberElement(final String element,\n            final String member)\n    {\n        if (WILDCARD.equals(element))\n        {\n            return Optional.of(new Tuple<>(WILDCARD, null));\n        }\n\n        final ItemType type;\n        try\n        {\n            type = ItemType.valueOf(element.toUpperCase());\n            return Optional.of(new Tuple<>(element, type));\n        }\n        catch (final IllegalArgumentException exception)\n        {\n            this.outputDelegate.printlnErrorMessage(\n                    \"could not parse ItemType `\" + element + \"' from member `\" + member + \"'\");\n            return Optional.empty();\n        }\n    }\n\n    private Optional<Tuple<String, String>> extractRoleFromMemberElement(final String element)\n    {\n        if (WILDCARD.equals(element))\n        {\n            return Optional.of(new Tuple<>(WILDCARD, null));\n        }\n\n        /*\n         * Allow users to specify escape sequences. This way, they may escape the '*', or escape the\n         * '\\' if they want to include these characters literally.\n         */\n        final StringBuilder builder = new StringBuilder();\n        for (final int codePoint : element.codePoints().toArray())\n        {\n            /*\n             * Only check for backslash when we see a BMP code point. This way, our role string will\n             * support emojis and other non-BMP characters.\n             */\n            if (Character.isBmpCodePoint(codePoint) && ((char) codePoint) == '\\\\')\n            {\n                continue;\n            }\n\n            builder.appendCodePoint(codePoint);\n        }\n\n        return Optional.of(new Tuple<>(element, builder.toString()));\n    }\n\n    private Set<String> parseColonSeparatedWkts(final String wktString)\n    {\n        final Set<String> wktSet = new HashSet<>();\n\n        if (wktString.isEmpty())\n        {\n            return wktSet;\n        }\n\n        final WKTReader reader = new WKTReader();\n        final String[] wktStringSplit = wktString.split(\":\");\n        for (final String wkt : wktStringSplit)\n        {\n            try\n            {\n                reader.read(wkt);\n                wktSet.add(wkt);\n            }\n            catch (final ParseException exception)\n            {\n                this.outputDelegate.printlnErrorMessage(String.format(COULD_NOT_PARSE, \"wkt\", wkt));\n                return new HashSet<>();\n            }\n        }\n\n        return wktSet;\n    }\n\n    private Set<ItemType> parseCommaSeparatedItemTypes(final String typeString)\n    {\n        final Set<ItemType> typeSet = new HashSet<>();\n\n        if (typeString.isEmpty())\n        {\n            return typeSet;\n        }\n\n        final String[] typeStringSplit = typeString.split(\",\");\n        for (final String typeElement : typeStringSplit)\n        {\n            final ItemType type;\n            try\n            {\n                type = ItemType.valueOf(typeElement.toUpperCase());\n                typeSet.add(type);\n            }\n            catch (final IllegalArgumentException exception)\n            {\n                this.outputDelegate.printlnErrorMessage(\n                        String.format(COULD_NOT_PARSE, \"ItemType\", typeElement));\n                return new HashSet<>();\n            }\n        }\n        return typeSet;\n    }\n\n    private Set<Long> parseCommaSeparatedLongs(final String idString)\n    {\n        final Set<Long> idSet = new HashSet<>();\n\n        if (idString.isEmpty())\n        {\n            return idSet;\n        }\n\n        final String[] idStringSplit = idString.split(\",\");\n        for (final String idElement : idStringSplit)\n        {\n            final long identifier;\n            try\n            {\n                identifier = Long.parseLong(idElement);\n                idSet.add(identifier);\n            }\n            catch (final NumberFormatException exception)\n            {\n                this.outputDelegate\n                        .printlnErrorMessage(String.format(COULD_NOT_PARSE, \"id\", idElement));\n                return new HashSet<>();\n            }\n        }\n        return idSet;\n    }\n\n    private Set<RelationMemberSearchConstraint> parseSemicolonSeparatedRelationMembers(\n            final String memberString)\n    {\n        final Set<RelationMemberSearchConstraint> constraints = new HashSet<>();\n\n        if (memberString.isEmpty())\n        {\n            return constraints;\n        }\n\n        final String[] memberStringSplit = memberString.split(\";\");\n        for (final String member : memberStringSplit)\n        {\n            final int expectedElementLength = 3;\n            final String[] memberElements = member.split(\",\");\n            if (memberElements.length != expectedElementLength)\n            {\n                this.outputDelegate.printlnErrorMessage(\n                        \"invalid syntax for member string `\" + memberString + \"'\");\n                return new HashSet<>();\n            }\n\n            final Optional<Tuple<String, ItemType>> itemType = extractItemTypeFromMemberElement(\n                    memberElements[0], member);\n            final Optional<Tuple<String, Long>> identifier = extractIdFromMemberElement(\n                    memberElements[1], member);\n            final Optional<Tuple<String, String>> role = extractRoleFromMemberElement(\n                    memberElements[2]);\n\n            if (itemType.isEmpty() || identifier.isEmpty() || role.isEmpty())\n            {\n                return new HashSet<>();\n            }\n\n            final RelationMemberSearchConstraint constraint = new RelationMemberSearchConstraint();\n            constraint.withType(itemType.get().getSecond()).withId(identifier.get().getSecond())\n                    .withRole(role.get().getSecond());\n            constraints.add(constraint);\n        }\n\n        return constraints;\n    }\n\n    private Geometry parseWkt(final String wkt)\n    {\n        final WKTReader reader = new WKTReader();\n        final Geometry geometry;\n        try\n        {\n            geometry = reader.read(wkt);\n            return geometry;\n        }\n        catch (final ParseException exception)\n        {\n            this.outputDelegate.printlnErrorMessage(\"unable to parse `\" + wkt + \"' as WKT\");\n            return null;\n        }\n    }\n\n    private void printEntityWithHumanReadableFormat(final AtlasEntity entity,\n            final File atlasResource)\n    {\n        this.outputDelegate.printlnStdout(\n                \"Found entity matching criteria in \" + atlasResource.getPathString() + \":\",\n                TTYAttribute.BOLD);\n        this.outputDelegate.printlnStdout(((CompleteEntity) CompleteEntity.from(entity))\n                .prettify(PrettifyStringFormat.MINIMAL_MULTI_LINE, false), TTYAttribute.GREEN);\n        this.outputDelegate.printlnStdout(\"\");\n    }\n\n    private void printEntityWithJSONFormat(final AtlasEntity entity, final File atlasResource,\n            final Atlas atlas)\n    {\n        final JsonObject outputObject = new JsonObject();\n        outputObject.addProperty(\"shard\", atlas.metaData().getCountry().orElse(\"XUK\") + \"_\"\n                + atlas.metaData().getShardName().orElse(\"0-0-0\"));\n        outputObject.addProperty(\"path\", atlasResource.getPathString());\n        outputObject.add(\"entity\", ((CompleteEntity) CompleteEntity.from(entity)).toJson());\n        this.outputDelegate.printlnStdout(\n                new GsonBuilder().disableHtmlEscaping().create().toJson(outputObject),\n                TTYAttribute.GREEN);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShardingConverterCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\n\n/**\n * This command provides an easy way to change the sharding in which a folder of atlas files is\n * described.\n * \n * @author matthieun\n */\npublic class AtlasShardingConverterCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final String INPUT = \"input\";\n    private static final String INPUT_DESCRIPTION = \"The input folder containing XXX_<old_shard_name>.atlas files\";\n    private static final String OUTPUT = \"output\";\n    private static final String OUTPUT_DESCRIPTION = \"The output folder where XXX_<new_shard_name>.atlas files will be saved\";\n    private static final String INPUT_SHARDING = \"inputSharding\";\n    private static final String INPUT_SHARDING_DESCRIPTION = \"The input sharding\";\n    private static final String OUTPUT_SHARDING = \"outputSharding\";\n    private static final String OUTPUT_SHARDING_DESCRIPTION = \"The output sharding\";\n\n    private static final Pattern FILE_MATCHER = Pattern\n            .compile(\"^[A-Za-z0-9]+_{1}([A-Za-z0-9]|-)+\\\\.atlas$\");\n    private static final String EXCEPTION_MESSAGE = \"{} needs to be specified.\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new AtlasShardingConverterCommand().runSubcommandAndExit(args);\n    }\n\n    public AtlasShardingConverterCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        final File inputFolder = new File(this.optionAndArgumentDelegate.getOptionArgument(INPUT)\n                .orElseThrow(() -> new CoreException(EXCEPTION_MESSAGE, INPUT)),\n                this.getFileSystem());\n        final File outputFolder = new File(this.optionAndArgumentDelegate.getOptionArgument(OUTPUT)\n                .orElseThrow(() -> new CoreException(EXCEPTION_MESSAGE, OUTPUT)),\n                this.getFileSystem());\n        if (!inputFolder.exists())\n        {\n            throw new CoreException(\"{} does not exist.\", inputFolder);\n        }\n        if (outputFolder.exists())\n        {\n            throw new CoreException(\"{} already exists.\", outputFolder);\n        }\n        final Sharding inputSharding = Sharding\n                .forString(this.optionAndArgumentDelegate.getOptionArgument(INPUT_SHARDING)\n                        .orElseThrow(() -> new CoreException(EXCEPTION_MESSAGE, INPUT_SHARDING)));\n        final Sharding outputSharding = Sharding\n                .forString(this.optionAndArgumentDelegate.getOptionArgument(OUTPUT_SHARDING)\n                        .orElseThrow(() -> new CoreException(EXCEPTION_MESSAGE, OUTPUT_SHARDING)));\n        final List<File> inputFiles = inputFolder.listFilesRecursively().stream()\n                .filter(file -> FILE_MATCHER.matcher(file.getName()).matches())\n                .collect(Collectors.toList());\n        this.outputDelegate.printlnCommandMessage(\"Found input files: \" + inputFiles);\n        final Map<Shard, File> inputShardToAtlas = new HashMap<>();\n        final Set<String> countries = new HashSet<>();\n        final Set<Shard> inputShards = inputFiles.stream().map(file ->\n        {\n            final StringList split = StringList.split(file.getName(), \"_\");\n            String shardName = split.get(1);\n            shardName = shardName.substring(0, shardName.indexOf(FileSuffix.ATLAS.toString()));\n            final Shard inputShard = inputSharding.shardForName(shardName);\n            inputShardToAtlas.put(inputShard, file);\n            countries.add(split.get(0));\n            return inputShard;\n        }).collect(Collectors.toSet());\n        if (countries.size() > 1)\n        {\n            throw new CoreException(\"Found more than one country in the folder: {}\", countries);\n        }\n        this.outputDelegate.printlnCommandMessage(\n                \"Found \" + inputShards.size() + \" input shards: \" + inputShards);\n        this.outputDelegate.printlnCommandMessage(\"Found country: \" + countries.iterator().next());\n        final Set<Shard> outputShards = inputShards.stream().flatMap(\n                inputShard -> Iterables.asList(outputSharding.shards(inputShard.bounds())).stream())\n                .collect(Collectors.toSet());\n        if (outputShards.isEmpty())\n        {\n            throw new CoreException(\"There are no resulting output shards.\");\n        }\n        else\n        {\n            outputFolder.mkdirs();\n        }\n        this.outputDelegate.printlnCommandMessage(\n                \"Found \" + outputShards.size() + \" output shards: \" + outputShards);\n        for (final Shard outputShard : outputShards)\n        {\n            this.outputDelegate.printlnCommandMessage(\"Processing output shard \" + outputShard);\n            final List<File> inputAtlases = new ArrayList<>();\n            Iterables.stream(inputSharding.shards(outputShard.bounds()))\n                    .filter(inputShardToAtlas::containsKey)\n                    .forEach(inputShard -> inputAtlases.add(inputShardToAtlas.get(inputShard)));\n            this.outputDelegate.printlnCommandMessage(\"Loading Atlas with \" + inputAtlases);\n            final Atlas combined = new AtlasResourceLoader().load(inputAtlases);\n            final Optional<Atlas> result = combined.subAtlas(outputShard.bounds(),\n                    AtlasCutType.SOFT_CUT);\n            final File outputFile = outputFolder.child(\n                    countries.iterator().next() + \"_\" + outputShard.getName() + FileSuffix.ATLAS);\n            this.outputDelegate.printlnCommandMessage(\"Saving Atlas to \" + outputFile);\n            result.ifPresent(atlas -> atlas.save(outputFile));\n        }\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"sharding-converter\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"translate Atlas files from one Sharding to another\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", AtlasShardingConverterCommand.class\n                .getResourceAsStream(\"AtlasShardingConverterCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", AtlasShardingConverterCommand.class\n                .getResourceAsStream(\"AtlasShardingConverterCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOptionWithRequiredArgument(INPUT, INPUT_DESCRIPTION, OptionOptionality.REQUIRED,\n                \"/path/to/atlases\");\n        registerOptionWithRequiredArgument(OUTPUT, OUTPUT_DESCRIPTION, OptionOptionality.REQUIRED,\n                \"/path/to/output\");\n        registerOptionWithRequiredArgument(INPUT_SHARDING, INPUT_SHARDING_DESCRIPTION,\n                OptionOptionality.REQUIRED, \"type@parameter\");\n        registerOptionWithRequiredArgument(OUTPUT_SHARDING, OUTPUT_SHARDING_DESCRIPTION,\n                OptionOptionality.REQUIRED, \"type@parameter\");\n        super.registerOptionsAndArguments();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShellToolsDemoCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.util.List;\nimport java.util.Scanner;\n\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\n\n/**\n * @author lcram\n */\npublic class AtlasShellToolsDemoCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final int BREAKFAST_CONTEXT = 4;\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new AtlasShellToolsDemoCommand().runSubcommandAndExit(args);\n    }\n\n    public AtlasShellToolsDemoCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        // Check if the parser context detected the breakfast usage\n        if (this.optionAndArgumentDelegate.getParserContext() == BREAKFAST_CONTEXT)\n        {\n            executeBreakfastContext();\n        }\n        else\n        {\n            executeLunchDinnerContext();\n        }\n\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"ast-demo\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"a demo of the Atlas Shell Tools subcommand API and features\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", AtlasShellToolsDemoCommand.class\n                .getResourceAsStream(\"AtlasShellToolsDemoCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", AtlasShellToolsDemoCommand.class\n                .getResourceAsStream(\"AtlasShellToolsDemoCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        /*\n         * Generally, it's better practice to declare option forms, descriptions, and hints in\n         * static final Strings at the top of your class. However, this demo command declares them\n         * using literals just for ease of tutorial.\n         */\n        final String beerDescription = \"Brand of your favorite beer. \"\n                + \"Currently making this option description really long in\"\n                + \" order to test out the autoformatting capabilities of\"\n                + \" the DocumentationFormatter class.\";\n\n        setVersion(\"0.0.1\");\n\n        // Register options/arguments for default lunch/dinner context\n        registerOption(\"capitalize\", 'c', \"Capitalize the foods list.\", OptionOptionality.OPTIONAL);\n        registerOptionWithRequiredArgument(\"beer\", beerDescription, OptionOptionality.OPTIONAL,\n                \"brand\");\n        registerOptionWithOptionalArgument(\"cheese\", 'C', // NOSONAR\n                \"Use cheese. Defaults to cheddar, but will accept a supplied alternative.\",\n                OptionOptionality.OPTIONAL, \"type\");\n        registerOptionWithRequiredArgument(\"repeat\", 'R', \"Repeat the food list N times.\",\n                OptionOptionality.OPTIONAL, \"N\");\n        registerArgument(\"favoriteMeal\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED);\n        registerArgument(\"favoriteFoods\", ArgumentArity.VARIADIC, ArgumentOptionality.OPTIONAL);\n\n        // Register options/arguments for an alternate breakfast use case\n        registerOption(\"breakfast\", 'b', \"Use breakfast mode\", OptionOptionality.REQUIRED,\n                BREAKFAST_CONTEXT);\n        registerArgument(\"favoriteBreakfastFood\", ArgumentArity.UNARY, ArgumentOptionality.OPTIONAL,\n                BREAKFAST_CONTEXT);\n\n        /*\n         * Always call super.registerOptionsAndArguments last. Some superclasses will attempt to\n         * register options to all available parser contexts. You want to ensure that each super\n         * class has access to the full set of parser contexts within its class hierarchy. For\n         * example, the global superclass AbstractAtlasShellToolsCommand registers a '--verbose'\n         * option to all parser contexts. By calling super.register... last, you ensure that the\n         * '--verbose' registry code runs for every context you registered above. Had you called\n         * super.register... first, the '--verbose' registry code would have missed any additional\n         * contexts you registered here.\n         */\n        super.registerOptionsAndArguments();\n    }\n\n    private void executeBreakfastContext()\n    {\n        this.outputDelegate.printlnCommandMessage(\n                \"value of HOME environment variable: \" + this.getEnvironmentValue(\"user.home\"));\n        final String breakfast = this.optionAndArgumentDelegate\n                .getUnaryArgument(\"favoriteBreakfastFood\").orElse(\"Default waffles :(\");\n        this.outputDelegate.printlnStdout(\"Using special breakfast mode:\");\n        this.outputDelegate.printlnStdout(breakfast, TTYAttribute.BOLD);\n        this.outputDelegate.printlnStdout(\"Now say something!\");\n        this.outputDelegate.printStdout(\"> \");\n        try (Scanner scanner = new Scanner(this.getInStream()))\n        {\n            final String input = scanner.nextLine();\n            this.outputDelegate.printlnStdout(\"You said: \" + input);\n        }\n    }\n\n    private void executeLunchDinnerContext()\n    {\n        // We registered favoriteFoods as variadic so it comes back as a List.\n        final List<String> foods = this.optionAndArgumentDelegate\n                .getVariadicArgument(\"favoriteFoods\");\n\n        // We registered favoriteMeal as REQUIRED so it is safe to unwrap the Optional.\n        // The orElseThrow is just there to stop Sonar from complaining.\n        final String meal = this.optionAndArgumentDelegate.getUnaryArgument(\"favoriteMeal\")\n                .orElseThrow(AtlasShellToolsException::new);\n\n        this.outputDelegate.printStdout(\"I like meal \");\n        this.outputDelegate.printStdout(meal, TTYAttribute.MAGENTA, TTYAttribute.BOLD,\n                TTYAttribute.BLINK);\n        this.outputDelegate.printlnStdout(\" the best\");\n\n        final int repeatDefault = 1;\n        final int repeat = this.optionAndArgumentDelegate.getOptionArgument(\"repeat\", value ->\n        {\n            final int parsed;\n            try\n            {\n                parsed = Integer.parseInt(value);\n            }\n            catch (final Exception exception)\n            {\n                this.outputDelegate\n                        .printlnWarnMessage(\"failed to parse repeat argument, using default\");\n                return null;\n            }\n            return parsed;\n        }).orElse(repeatDefault);\n\n        this.outputDelegate.printlnStdout(\"Favorite foods are:\");\n        for (int index = 0; index < repeat; index++)\n        {\n            for (final String food : foods)\n            {\n                String mutableFood = food;\n                if (this.optionAndArgumentDelegate.hasOption(\"capitalize\"))\n                {\n                    mutableFood = mutableFood.toUpperCase();\n                }\n                this.outputDelegate.printlnStdout(mutableFood, TTYAttribute.BOLD);\n            }\n        }\n\n        if (this.optionAndArgumentDelegate.hasOption(\"cheese\"))\n        {\n            this.outputDelegate.printlnStdout(\"Using \"\n                    + this.optionAndArgumentDelegate.getOptionArgument(\"cheese\").orElse(\"cheddar\")\n                    + \" cheese\");\n        }\n\n        if (this.optionAndArgumentDelegate.hasOption(\"beer\"))\n        {\n            this.outputDelegate\n                    .printlnStdout(\"Also ordering a beer, \" + this.optionAndArgumentDelegate\n                            .getOptionArgument(\"beer\").orElseThrow(AtlasShellToolsException::new));\n        }\n        else\n        {\n            this.outputDelegate.printlnWarnMessage(\"beer skipped\");\n        }\n\n        this.outputDelegate.printStderr(\"Here is a closing stderr message\\n\",\n                TTYAttribute.UNDERLINE);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/ConcatenateAtlasCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.AtlasLoaderCommand;\n\n/**\n * @author lcram\n */\npublic class ConcatenateAtlasCommand extends AtlasLoaderCommand\n{\n    private static final String OUTPUT_ATLAS = \"output.atlas\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    private final List<Atlas> atlases = new ArrayList<>();\n\n    public static void main(final String[] args)\n    {\n        new ConcatenateAtlasCommand().runSubcommandAndExit(args);\n    }\n\n    public ConcatenateAtlasCommand()\n    {\n        super();\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"fatlas\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"create and save a fatlas using the MultiAtlas\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", ConcatenateAtlasCommand.class\n                .getResourceAsStream(\"ConcatenateAtlasCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", ConcatenateAtlasCommand.class\n                .getResourceAsStream(\"ConcatenateAtlasCommandExamplesSection.txt\"));\n        super.registerManualPageSections();\n    }\n\n    @Override\n    protected int finish()\n    {\n        if (this.atlases.isEmpty())\n        {\n            this.outputDelegate.printlnErrorMessage(\"could not load atlas(es)\");\n            return 1;\n        }\n\n        final Atlas atlas = new MultiAtlas(this.atlases);\n\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate.printlnCommandMessage(\"cloning...\");\n        }\n        final PackedAtlas outputAtlas = new PackedAtlasCloner().cloneFrom(atlas);\n        final Path concatenatedPath = Paths.get(getOutputPath().toAbsolutePath().toString(),\n                OUTPUT_ATLAS);\n        final File outputFile = new File(concatenatedPath.toAbsolutePath().toString(),\n                this.getFileSystem());\n        outputAtlas.save(outputFile);\n\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate.printlnCommandMessage(\"saved to \" + concatenatedPath.toString());\n        }\n\n        return 0;\n    }\n\n    @Override\n    protected void processAtlas(final Atlas atlas, final String atlasFileName,\n            final File atlasResource)\n    {\n        this.atlases.add(atlas);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/CountryBoundaryMapPrinterCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.BufferedWriter;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Polygon;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.CountryBoundaryMapTemplate;\nimport org.openstreetmap.atlas.utilities.time.Time;\n\n/**\n * @author matthieun\n */\npublic class CountryBoundaryMapPrinterCommand extends AbstractAtlasShellToolsCommand\n{\n    public static final String BOUNDARY_OPTION_LONG = \"country-boundary\";\n\n    public static void main(final String[] args)\n    {\n        new CountryBoundaryMapPrinterCommand().runSubcommandAndExit(args);\n    }\n\n    @Override\n    public int execute()\n    {\n        final File boundaryFile = getBoundaryFile();\n        String boundaryFileName = boundaryFile.getName();\n        boundaryFileName = boundaryFileName.substring(0, boundaryFileName.indexOf('.'));\n        final Optional<CountryBoundaryMap> boundariesOption = CountryBoundaryMapTemplate\n                .getCountryBoundaryMap(this);\n        final File outputFolder = boundaryFile.parent();\n        final File geojson = outputFolder.child(boundaryFileName + \"-geojson\");\n        geojson.mkdirs();\n        final File wkt = outputFolder.child(boundaryFileName + \"-wkt\");\n        wkt.mkdirs();\n        if (boundariesOption.isEmpty())\n        {\n            getCommandOutputDelegate().printlnErrorMessage(\"Could not read boundary file!\");\n            return 1;\n        }\n        final CountryBoundaryMap map = boundariesOption.get();\n        final Set<String> countrySet = map.countryCodesOverlappingWith(Rectangle.MAXIMUM).stream()\n                .collect(Collectors.toSet());\n        final GeometryFactory geometryFactory = new GeometryFactory();\n        for (final String country : countrySet)\n        {\n            final Time start = Time.now();\n            final Polygon[] polygons = map.countryBoundary(country).toArray(new Polygon[0]);\n            final MultiPolygon multiPolygon = new MultiPolygon(polygons, geometryFactory);\n            saveGeometry(wkt, geojson, country, multiPolygon);\n            if (getOptionAndArgumentDelegate().hasVerboseOption())\n            {\n                getCommandOutputDelegate()\n                        .printlnCommandMessage(\"Saved \" + country + \" in \" + start.elapsedSince());\n            }\n        }\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"boundary-itemizer\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"Read a CountryBoundaryMap file and print each country to geojson and wkt\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        registerManualPageSectionsFromTemplate(new CountryBoundaryMapTemplate());\n        addManualPageSection(\"DESCRIPTION\", CountryBoundaryMapPrinterCommand.class\n                .getResourceAsStream(\"CountryBoundaryMapPrinterCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", CountryBoundaryMapPrinterCommand.class\n                .getResourceAsStream(\"CountryBoundaryMapPrinterCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOptionsAndArgumentsFromTemplate(new CountryBoundaryMapTemplate());\n        super.registerOptionsAndArguments();\n    }\n\n    private File getBoundaryFile()\n    {\n        return new File(getOptionAndArgumentDelegate()\n                .getOptionArgument(CountryBoundaryMapTemplate.COUNTRY_BOUNDARY_OPTION_LONG)\n                .orElseThrow(AtlasShellToolsException::new), this.getFileSystem());\n    }\n\n    private void save(final WritableResource output, final String string)\n    {\n        try (BufferedWriter writer = new BufferedWriter(\n                new OutputStreamWriter(output.write(), StandardCharsets.UTF_8)))\n        {\n            writer.write(string);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not save file {}\", output.getName(), e);\n        }\n    }\n\n    private void saveGeometry(final File wkt, final File geojson, final String name,\n            final MultiPolygon multiPolygon)\n    {\n        save(wkt.child(name + FileSuffix.WKT), multiPolygon.toText());\n        final File countryFile = geojson.child(name + FileSuffix.GEO_JSON);\n        new JtsMultiPolygonToMultiPolygonConverter().convert(multiPolygon)\n                .saveAsGeoJson(countryFile);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/CountryShardToBoundsCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.WktPolygonConverter;\nimport org.openstreetmap.atlas.geography.sharding.GeoHashSharding;\nimport org.openstreetmap.atlas.geography.sharding.GeoHashTile;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\nimport org.openstreetmap.atlas.geography.sharding.converters.StringToShardConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class CountryShardToBoundsCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final Logger logger = LoggerFactory.getLogger(CountryShardToBoundsCommand.class);\n\n    private static final String REVERSE_OPTION_LONG = \"reverse\";\n    private static final String REVERSE_OPTION_DESCRIPTION = \"Convert given WKT bound(s) to SlippyTile/GeoHashTile shard(s) if possible. Supports up to slippy zoom level \"\n            + Sharding.SLIPPY_ZOOM_MAXIMUM + \" and geohash precision \"\n            + GeoHashTile.MAXIMUM_PRECISION + \".\";\n\n    private static final String COUNTRY_BOUNDARY_OPTION_LONG = \"country-boundary\";\n    private static final String COUNTRY_BOUNDARY_OPTION_DESCRIPTION = \"A boundary file to use as a source. See DESCRIPTION section for details.\";\n    private static final String COUNTRY_BOUNDARY_OPTION_HINT = \"boundary-file\";\n\n    private static final String SHARD = \"shard\";\n    private static final String COUNTRY = \"ISO3-country-code\";\n\n    private static final Integer SHARD_CONTEXT = 3;\n    private static final Integer COUNTRY_CONTEXT = 4;\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new CountryShardToBoundsCommand().runSubcommandAndExit(args);\n    }\n\n    public CountryShardToBoundsCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        if (this.optionAndArgumentDelegate.getParserContext() == SHARD_CONTEXT)\n        {\n            return executeShardContext();\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == COUNTRY_CONTEXT)\n        {\n            return executeCountryContext();\n        }\n        else\n        {\n            throw new AtlasShellToolsException();\n        }\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"country-shard-bounds\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"get the WKT bounds of given shards or countries\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", CountryShardToBoundsCommand.class\n                .getResourceAsStream(\"CountryShardToBoundsCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", CountryShardToBoundsCommand.class\n                .getResourceAsStream(\"CountryShardToBoundsCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOption(REVERSE_OPTION_LONG, REVERSE_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL);\n        registerOptionWithRequiredArgument(COUNTRY_BOUNDARY_OPTION_LONG,\n                COUNTRY_BOUNDARY_OPTION_DESCRIPTION, OptionOptionality.REQUIRED,\n                COUNTRY_BOUNDARY_OPTION_HINT, COUNTRY_CONTEXT);\n        registerArgument(SHARD, ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED,\n                SHARD_CONTEXT);\n        registerArgument(COUNTRY, ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED,\n                COUNTRY_CONTEXT);\n        super.registerOptionsAndArguments();\n    }\n\n    private int executeCountryContext()\n    {\n        final CountryBoundaryMap countryBoundaryMap;\n        final File boundaryMapFile = new File(\n                this.optionAndArgumentDelegate.getOptionArgument(COUNTRY_BOUNDARY_OPTION_LONG)\n                        .orElseThrow(AtlasShellToolsException::new),\n                this.getFileSystem());\n        if (!boundaryMapFile.exists())\n        {\n            this.outputDelegate.printlnErrorMessage(\n                    \"boundary file \" + boundaryMapFile.getAbsolutePathString() + \" does not exist\");\n            return 1;\n        }\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate.printlnCommandMessage(\"loading country boundary map...\");\n        }\n        countryBoundaryMap = CountryBoundaryMap.fromPlainText(boundaryMapFile);\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate.printlnCommandMessage(\"loaded boundary map\");\n        }\n\n        final List<String> countryCodes = this.optionAndArgumentDelegate\n                .getVariadicArgument(COUNTRY);\n\n        for (int i = 0; i < countryCodes.size(); i++)\n        {\n            final String countryCode = countryCodes.get(i).toUpperCase();\n            this.outputDelegate.printlnStdout(countryCode + \" boundary:\", TTYAttribute.BOLD);\n            final List<org.locationtech.jts.geom.Polygon> boundaries = countryBoundaryMap\n                    .countryBoundary(countryCode);\n            if (boundaries == null || boundaries.isEmpty())\n            {\n                this.outputDelegate.printlnWarnMessage(\"no boundaries found for \" + countryCode);\n            }\n            else\n            {\n                for (final org.locationtech.jts.geom.Polygon boundary : boundaries)\n                {\n                    this.outputDelegate.printlnStdout(boundary.toText(), TTYAttribute.GREEN);\n                }\n            }\n\n            if (i < countryCodes.size() - 1)\n            {\n                this.outputDelegate.printlnStdout(\"\");\n            }\n        }\n\n        return 0;\n    }\n\n    private int executeShardContext()\n    {\n        if (this.optionAndArgumentDelegate.hasOption(REVERSE_OPTION_LONG))\n        {\n            final List<String> wkts = this.optionAndArgumentDelegate.getVariadicArgument(SHARD);\n\n            for (int i = 0; i < wkts.size(); i++)\n            {\n                final String wkt = wkts.get(i);\n                parseWktAndPrintOutput(wkt);\n\n                // Only print a separating newline if there were multiple entries\n                if (i < wkts.size() - 1)\n                {\n                    this.outputDelegate.printlnStdout(\"\");\n                }\n            }\n        }\n        else\n        {\n            final List<String> shards = this.optionAndArgumentDelegate.getVariadicArgument(SHARD);\n\n            for (int i = 0; i < shards.size(); i++)\n            {\n                final String shard = shards.get(i);\n                parseShardAndPrintOutput(shard);\n\n                // Only print a separating newline if there were multiple entries\n                if (i < shards.size() - 1)\n                {\n                    this.outputDelegate.printlnStdout(\"\");\n                }\n            }\n        }\n\n        return 0;\n    }\n\n    private void parseShardAndPrintOutput(final String shardName)\n    {\n        this.outputDelegate.printlnStdout(shardName + \" bounds:\", TTYAttribute.BOLD);\n        final Shard shard;\n        try\n        {\n            shard = new StringToShardConverter().convert(shardName);\n        }\n        catch (final Exception exception)\n        {\n            logger.error(\"unable to parse {}\", shardName, exception);\n            return;\n        }\n        this.outputDelegate.printlnStdout(shard.bounds().toWkt(), TTYAttribute.GREEN);\n    }\n\n    private void parseWktAndPrintOutput(final String wkt)\n    {\n        final Polygon polygon;\n        try\n        {\n            polygon = new WktPolygonConverter().backwardConvert(wkt);\n        }\n        catch (final Exception exception)\n        {\n            logger.error(\"unable to parse WKT polygon {}\", wkt, exception);\n            return;\n        }\n        for (int zoom = 1; zoom <= Sharding.SLIPPY_ZOOM_MAXIMUM; zoom++)\n        {\n            final SlippyTileSharding sharding = new SlippyTileSharding(zoom);\n            for (final Shard shard : sharding.shardsIntersecting(polygon))\n            {\n                if (shard.toWkt().equals(wkt))\n                {\n                    this.outputDelegate.printlnStdout(wkt + \" exactly matched shard:\",\n                            TTYAttribute.BOLD);\n                    this.outputDelegate.printlnStdout(shard.toString(), TTYAttribute.GREEN);\n                    return;\n                }\n            }\n        }\n        for (int precision = 1; precision <= GeoHashTile.MAXIMUM_PRECISION; precision++)\n        {\n            final GeoHashSharding sharding = new GeoHashSharding(precision);\n            for (final Shard shard : sharding.shardsIntersecting(polygon))\n            {\n                if (shard.toWkt().equals(wkt))\n                {\n                    this.outputDelegate.printlnStdout(wkt + \" exactly matched shard:\",\n                            TTYAttribute.BOLD);\n                    this.outputDelegate.printlnStdout(shard.toString(), TTYAttribute.GREEN);\n                    return;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/HelloWorldCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\n\n/**\n * @author lcram\n */\npublic class HelloWorldCommand extends AbstractAtlasShellToolsCommand\n{\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new HelloWorldCommand().runSubcommandAndExit(args);\n    }\n\n    public HelloWorldCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        this.outputDelegate.printStdout(\"Hello, \"\n                + this.optionAndArgumentDelegate.getOptionArgument(\"name\").orElse(\"world\") + \"!\\n\");\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"hello-world\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"a simple subcommand that prints \\\"Hello, world!\\\" and exits\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", HelloWorldCommand.class\n                .getResourceAsStream(\"HelloWorldCommandDescriptionSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOptionWithRequiredArgument(\"name\", \"Your name for the greeting.\",\n                OptionOptionality.OPTIONAL, \"name\");\n        super.registerOptionsAndArguments();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/IsoCountryCodeCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.locale.IsoCountry;\nimport org.openstreetmap.atlas.locale.IsoCountryFuzzyMatcher;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\n\n/**\n * @author lcram\n */\npublic class IsoCountryCodeCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final int DEFAULT_MATCH_NUMBER = 10;\n\n    private static final char NUMBER_OPTION_SHORT = 'n';\n    private static final String NUMBER_OPTION_LONG = \"number\";\n    private static final String NUMBER_OPTION_DESCRIPTION = \"The number of matches to display. Defaults to \"\n            + DEFAULT_MATCH_NUMBER + \".\";\n    private static final String NUMBER_OPTION_HINT = \"n\";\n\n    private static final char ALL_OPTION_SHORT = 'a';\n    private static final String ALL_OPTION_LONG = \"all\";\n    private static final String ALL_OPTION_DESCRIPTION = \"Show the entire ISO country listing.\";\n    private static final Integer ALL_OPTION_CONTEXT = 4;\n\n    private static final String QUERY_HINT = \"query\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new IsoCountryCodeCommand().runSubcommandAndExit(args);\n    }\n\n    public IsoCountryCodeCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        if (this.optionAndArgumentDelegate.getParserContext() == ALL_OPTION_CONTEXT)\n        {\n            return allExecute();\n        }\n\n        final List<String> queries = this.optionAndArgumentDelegate.getVariadicArgument(QUERY_HINT);\n\n        for (int i = 0; i < queries.size(); i++)\n        {\n            final String query = queries.get(i);\n            final Optional<IsoCountry> forIsoCode = IsoCountry.forCountryCode(query);\n            final Optional<IsoCountry> forDisplayNameExact = IsoCountry.forDisplayCountry(query);\n            final List<IsoCountry> forDisplayNameTopMatches = IsoCountryFuzzyMatcher\n                    .forDisplayCountryTopMatches(this.optionAndArgumentDelegate\n                            .getOptionArgument(NUMBER_OPTION_LONG, Integer::parseInt)\n                            .orElse(DEFAULT_MATCH_NUMBER), query.toLowerCase());\n\n            if (forIsoCode.isEmpty() && IsoCountry.forCountryCode(query.toUpperCase()).isPresent())\n            {\n                this.outputDelegate.printlnWarnMessage(\n                        \"did you mean case-sensitive ISO code '\" + query.toUpperCase() + \"'?\");\n            }\n\n            // check for exact country code first\n            if (forIsoCode.isPresent())\n            {\n                this.outputDelegate.printlnStdout(\"ISO code '\" + query + \"' matched: \",\n                        TTYAttribute.BOLD);\n                printCountry(forIsoCode.get());\n            }\n            else if (forDisplayNameExact.isPresent())\n            {\n                this.outputDelegate.printlnStdout(\"Display country name '\" + query + \"' matched: \",\n                        TTYAttribute.BOLD);\n                printCountry(forDisplayNameExact.get());\n            }\n            else if (!forDisplayNameTopMatches.isEmpty())\n            {\n                this.outputDelegate.printlnStdout(\n                        \"Display country name '\" + query + \"' had no exact matches. \"\n                                + forDisplayNameTopMatches.size() + \" closest matches are:\",\n                        TTYAttribute.BOLD);\n                for (final IsoCountry country : forDisplayNameTopMatches)\n                {\n                    printCountry(country);\n                }\n            }\n            else\n            {\n                this.outputDelegate.printlnErrorMessage(\"unmatchable query \" + query);\n            }\n\n            if (i < queries.size() - 1)\n            {\n                this.outputDelegate.printlnStdout(\"\");\n            }\n        }\n\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"iso-country-code\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"convert ISO country codes to countries and back again\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", IsoCountryCodeCommand.class\n                .getResourceAsStream(\"IsoCountryCodeCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", IsoCountryCodeCommand.class\n                .getResourceAsStream(\"IsoCountryCodeCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerArgument(QUERY_HINT, ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED,\n                AbstractAtlasShellToolsCommand.DEFAULT_CONTEXT);\n        registerOptionWithRequiredArgument(NUMBER_OPTION_LONG, NUMBER_OPTION_SHORT,\n                NUMBER_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL, NUMBER_OPTION_HINT,\n                AbstractAtlasShellToolsCommand.DEFAULT_CONTEXT);\n        registerOption(ALL_OPTION_LONG, ALL_OPTION_SHORT, ALL_OPTION_DESCRIPTION,\n                OptionOptionality.REQUIRED, ALL_OPTION_CONTEXT);\n        super.registerOptionsAndArguments();\n    }\n\n    private int allExecute()\n    {\n        final List<String> countries = new ArrayList<>(IsoCountry.allCountryCodes());\n        Collections.sort(countries);\n\n        this.outputDelegate.printlnStdout(\"Displaying all countries:\", TTYAttribute.BOLD);\n        for (final String country : countries)\n        {\n            final Optional<IsoCountry> forCode = IsoCountry.forCountryCode(country);\n            if (forCode.isEmpty())\n            {\n                throw new AtlasShellToolsException();\n            }\n            printCountry(forCode.get());\n        }\n        return 0;\n    }\n\n    private void printCountry(final IsoCountry country)\n    {\n        this.outputDelegate.printlnStdout(country.getCountryCode() + \"   \"\n                + country.getIso3CountryCode() + \"   \" + country.toString(), TTYAttribute.GREEN);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/JavaToProtoSerializationCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas.AtlasSerializationFormat;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.AtlasLoaderTemplate;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.OutputDirectoryTemplate;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\n\n/**\n * @author lcram\n */\npublic class JavaToProtoSerializationCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final String CHECK_OPTION_LONG = \"check\";\n    private static final Character CHECK_OPTION_SHORT = 'c';\n    private static final String CHECK_OPTION_DESCRIPTION = \"Check the serialization format of the atlas(es) without converting.\";\n\n    private static final String REVERSE_OPTION_LONG = \"reverse\";\n    private static final Character REVERSE_OPTION_SHORT = 'R';\n    private static final String REVERSE_OPTION_DESCRIPTION = \"Convert Protocol Buffers atlas(es) back to Java serialization.\";\n\n    private static final Integer CHECK_CONTEXT = 4;\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new JavaToProtoSerializationCommand().runSubcommandAndExit(args);\n    }\n\n    public JavaToProtoSerializationCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        return AtlasLoaderTemplate.execute(this, null, this::processAtlas, null);\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"java2proto\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"convert Java-serialized atlases to Protocol Buffers format\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", JavaToProtoSerializationCommand.class\n                .getResourceAsStream(\"JavaToProtoSerializationCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", JavaToProtoSerializationCommand.class\n                .getResourceAsStream(\"JavaToProtoSerializationCommandExamplesSection.txt\"));\n        registerManualPageSectionsFromTemplate(new AtlasLoaderTemplate());\n        registerManualPageSectionsFromTemplate(new OutputDirectoryTemplate());\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOption(REVERSE_OPTION_LONG, REVERSE_OPTION_SHORT, REVERSE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL);\n        registerOption(CHECK_OPTION_LONG, CHECK_OPTION_SHORT, CHECK_OPTION_DESCRIPTION,\n                OptionOptionality.REQUIRED, CHECK_CONTEXT);\n        registerOptionsAndArgumentsFromTemplate(new AtlasLoaderTemplate(\n                AbstractAtlasShellToolsCommand.DEFAULT_CONTEXT, CHECK_CONTEXT));\n        registerOptionsAndArgumentsFromTemplate(\n                new OutputDirectoryTemplate(AbstractAtlasShellToolsCommand.DEFAULT_CONTEXT));\n        super.registerOptionsAndArguments();\n    }\n\n    private void processAtlas(final Atlas atlas, final String atlasFileName,\n            final File atlasResource)\n    {\n        PackedAtlas outputAtlas;\n        try\n        {\n            outputAtlas = (PackedAtlas) atlas;\n        }\n        catch (final ClassCastException exception)\n        {\n            outputAtlas = new PackedAtlasCloner().cloneFrom(atlas);\n        }\n\n        if (this.optionAndArgumentDelegate.getParserContext() == CHECK_CONTEXT)\n        {\n            this.outputDelegate.printStdout(\"atlas \");\n            this.outputDelegate.printStdout(atlasResource.getPathString(), TTYAttribute.BOLD);\n            this.outputDelegate.printStdout(\" format: \");\n            this.outputDelegate.printlnStdout(outputAtlas.getSerializationFormat().toString(),\n                    TTYAttribute.BOLD);\n        }\n        else\n        {\n            if (this.optionAndArgumentDelegate.hasOption(REVERSE_OPTION_LONG))\n            {\n                outputAtlas.setSaveSerializationFormat(AtlasSerializationFormat.JAVA);\n            }\n            else\n            {\n                outputAtlas.setSaveSerializationFormat(AtlasSerializationFormat.PROTOBUF);\n            }\n\n            final Optional<Path> outputPathOptional = OutputDirectoryTemplate.getOutputPath(this);\n            if (outputPathOptional.isEmpty())\n            {\n                this.outputDelegate\n                        .printlnWarnMessage(\"could not save \" + atlasFileName + \", skipping...\");\n                return;\n            }\n            final Path concatenatedPath = outputPathOptional\n                    .map(path -> Paths.get(path.toAbsolutePath().toString(), atlasFileName))\n                    .orElseThrow(AtlasShellToolsException::new);\n\n            final File outputFile = new File(concatenatedPath.toAbsolutePath().toString(),\n                    this.getFileSystem());\n            outputAtlas.save(outputFile);\n\n            if (this.optionAndArgumentDelegate.hasVerboseOption())\n            {\n                this.outputDelegate.printlnStdout(\"Saved to \" + concatenatedPath.toString());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/OsmFileParserCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileParser;\n\n/**\n * @author matthieun\n */\npublic class OsmFileParserCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final String JOSM_OSM_FILE = \"josm\";\n    private static final String OSM_FILE = \"osm\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n\n    public static void main(final String[] args)\n    {\n        new OsmFileParserCommand().runSubcommandAndExit(args);\n    }\n\n    public OsmFileParserCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        final String josmOsmFile = this.optionAndArgumentDelegate.getUnaryArgument(JOSM_OSM_FILE)\n                .orElseThrow(AtlasShellToolsException::new);\n        final String osmFile = this.optionAndArgumentDelegate.getUnaryArgument(OSM_FILE)\n                .orElseThrow(AtlasShellToolsException::new);\n        new OsmFileParser().update(new File(josmOsmFile, this.getFileSystem()),\n                new File(osmFile, this.getFileSystem()));\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"josm2osm\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"transform a JOSM OSM file into a real OSM file.\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", OsmFileParserCommand.class\n                .getResourceAsStream(\"OsmFileParserCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", OsmFileParserCommand.class\n                .getResourceAsStream(\"OsmFileParserCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerArgument(JOSM_OSM_FILE, ArgumentArity.UNARY, ArgumentOptionality.REQUIRED);\n        registerArgument(OSM_FILE, ArgumentArity.UNARY, ArgumentOptionality.REQUIRED);\n        super.registerOptionsAndArguments();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/OsmToAtlasCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlasHandler;\n\n/**\n * Convert an .osm file of various types to an atlas.\n *\n * @author jklamer\n */\npublic class OsmToAtlasCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final String INPUT_OSM_FILE_ARGUMENT = \"input-osm-file\";\n    private static final String OUTPUT_ATLAS_FILE_ARGUMENT = \"output-atlas-file\";\n\n    private static final String JOSM_OPTION_LONG = \"josm\";\n    private static final String JOSM_OPTION_DESCRIPTION = \"Specify if the OSM file is in JOSM format.\";\n\n    private static final String COUNTRY_OPTION_LONG = \"country\";\n    private static final String COUNTRY_OPTION_DESCRIPTION = \"Specify an ISO3 country code to use for slicing.\";\n    private static final String COUNTRY_OPTION_HINT = \"ISO3\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n\n    public static void main(final String[] args)\n    {\n        new OsmToAtlasCommand().runSubcommandAndExit(args);\n    }\n\n    public OsmToAtlasCommand()\n    {\n        super();\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        final File osmFile = new File(\n                this.optionAndArgumentDelegate.getUnaryArgument(INPUT_OSM_FILE_ARGUMENT)\n                        .orElseThrow(AtlasShellToolsException::new),\n                this.getFileSystem());\n        final File atlasFile = new File(\n                this.optionAndArgumentDelegate.getUnaryArgument(OUTPUT_ATLAS_FILE_ARGUMENT)\n                        .orElseThrow(AtlasShellToolsException::new),\n                this.getFileSystem());\n        final boolean useJosmFormat = this.optionAndArgumentDelegate.hasOption(JOSM_OPTION_LONG);\n\n        final Atlas atlas = TestAtlasHandler.getAtlasFromJosmOsmResource(useJosmFormat,\n                new InputStreamResource(osmFile::read), osmFile.getName(),\n                this.optionAndArgumentDelegate.getOptionArgument(COUNTRY_OPTION_LONG));\n        atlas.save(atlasFile);\n\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"osm2atlas\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"convert a .osm file into an Atlas file\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", OsmToAtlasCommand.class\n                .getResourceAsStream(\"OsmToAtlasCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", OsmToAtlasCommand.class\n                .getResourceAsStream(\"OsmToAtlasCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerArgument(INPUT_OSM_FILE_ARGUMENT, ArgumentArity.UNARY,\n                ArgumentOptionality.REQUIRED);\n        registerArgument(OUTPUT_ATLAS_FILE_ARGUMENT, ArgumentArity.UNARY,\n                ArgumentOptionality.REQUIRED);\n        registerOption(JOSM_OPTION_LONG, JOSM_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL);\n        registerOptionWithRequiredArgument(COUNTRY_OPTION_LONG, COUNTRY_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, COUNTRY_OPTION_HINT);\n        super.registerOptionsAndArguments();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/PackedToTextAtlasCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.AtlasLoaderCommand;\n\n/**\n * @author lcram\n */\npublic class PackedToTextAtlasCommand extends AtlasLoaderCommand\n{\n    private static final String SAVED_TO = \"saved to \";\n\n    private static final String REVERSE_OPTION_LONG = \"reverse\";\n    private static final Character REVERSE_OPTION_SHORT = 'R';\n    private static final String REVERSE_OPTION_DESCRIPTION = \"Convert a text atlas in TextAtlas format (i.e. not GeoJSON) back into a PackedAtlas.\";\n\n    private static final String GEOJSON_OPTION_LONG = \"geojson\";\n    private static final Character GEOJSON_OPTION_SHORT = 'g';\n    private static final String GEOJSON_OPTION_DESCRIPTION = \"Save atlas as GeoJSON.\";\n\n    private static final String LDGEOJSON_OPTION_LONG = \"ldgeojson\";\n    private static final Character LDGEOJSON_OPTION_SHORT = 'l';\n    private static final String LDGEOJSON_OPTION_DESCRIPTION = \"Save atlas as line-delimited GeoJSON.\";\n\n    private static final Integer GEOJSON_CONTEXT = 4;\n    private static final Integer LDGEOJSON_CONTEXT = 5;\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new PackedToTextAtlasCommand().runSubcommandAndExit(args);\n    }\n\n    public PackedToTextAtlasCommand()\n    {\n        super();\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"packed2text\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"transform a PackedAtlas into a human-readable format\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", PackedToTextAtlasCommand.class\n                .getResourceAsStream(\"PackedToTextAtlasCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", PackedToTextAtlasCommand.class\n                .getResourceAsStream(\"PackedToTextAtlasCommandExamplesSection.txt\"));\n        super.registerManualPageSections();\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOption(REVERSE_OPTION_LONG, REVERSE_OPTION_SHORT, REVERSE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL);\n        registerOption(GEOJSON_OPTION_LONG, GEOJSON_OPTION_SHORT, GEOJSON_OPTION_DESCRIPTION,\n                OptionOptionality.REQUIRED, GEOJSON_CONTEXT);\n        registerOption(LDGEOJSON_OPTION_LONG, LDGEOJSON_OPTION_SHORT, LDGEOJSON_OPTION_DESCRIPTION,\n                OptionOptionality.REQUIRED, LDGEOJSON_CONTEXT);\n        super.registerOptionsAndArguments();\n    }\n\n    @Override\n    protected void processAtlas(final Atlas atlas, final String atlasFileName,\n            final File atlasResource)\n    {\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate\n                    .printlnCommandMessage(\"converting \" + atlasResource.getPathString() + \"...\");\n        }\n        try\n        {\n            writeOutput(atlasFileName, atlas);\n        }\n        catch (final Exception exception)\n        {\n            this.outputDelegate.printlnErrorMessage(\"failed to save file for \"\n                    + atlasResource.getPathString() + \": \" + exception.getMessage());\n        }\n    }\n\n    private void writeOutput(final String atlasFileName, final Atlas outputAtlas)\n    {\n        final String fileName = AtlasLoaderCommand.removeSuffixFromFileName(atlasFileName);\n        final Path concatenatedPath = Paths.get(getOutputPath().toAbsolutePath().toString(),\n                fileName);\n        File outputFile = null;\n\n        if (this.optionAndArgumentDelegate\n                .getParserContext() == AbstractAtlasShellToolsCommand.DEFAULT_CONTEXT)\n        {\n            if (this.optionAndArgumentDelegate.hasOption(REVERSE_OPTION_LONG))\n            {\n                outputFile = new File(\n                        concatenatedPath.toAbsolutePath().toString() + FileSuffix.ATLAS,\n                        this.getFileSystem());\n                outputAtlas.save(outputFile);\n            }\n            else\n            {\n                outputFile = new File(concatenatedPath.toAbsolutePath().toString()\n                        + FileSuffix.ATLAS + FileSuffix.TEXT, this.getFileSystem());\n                outputAtlas.saveAsText(outputFile);\n            }\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == GEOJSON_CONTEXT)\n        {\n            outputFile = new File(\n                    concatenatedPath.toAbsolutePath().toString() + FileSuffix.GEO_JSON,\n                    this.getFileSystem());\n            outputAtlas.saveAsGeoJson(outputFile);\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == LDGEOJSON_CONTEXT)\n        {\n            outputFile = new File(\n                    concatenatedPath.toAbsolutePath().toString() + FileSuffix.GEO_JSON,\n                    this.getFileSystem());\n            outputAtlas.saveAsLineDelimitedGeoJsonFeatures(outputFile, (entity, json) ->\n            {\n                // Dummy consumer, we don't need to mutate the JSON\n            });\n\n        }\n        else\n        {\n            throw new AtlasShellToolsException();\n        }\n\n        if (this.optionAndArgumentDelegate.hasVerboseOption())\n        {\n            this.outputDelegate\n                    .printlnCommandMessage(SAVED_TO + outputFile.toAbsolutePath().toString());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/PbfToAtlasCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas.AtlasSerializationFormat;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.MultipleOutputCommand;\n\n/**\n * @author samgass\n * @author matthieun\n */\npublic class PbfToAtlasCommand extends MultipleOutputCommand\n{\n    // The hint for the input path for the PBF file(s) to convert\n    private static final String PBF_PATH_HINT = \"pbf\";\n\n    // The country name for the country shards Atlas file(s) to output\n    private static final String COUNTRY_NAME = \"countryName\";\n\n    // The file containing the WKT polygon to constrain the loading\n    private static final String BOUNDS = \"bounds\";\n\n    // Whether or not to stop at the raw atlas\n    private static final String RAW = \"raw\";\n\n    private static final String COUNTRY_NAME_DESCRIPTION = \"The country for the shard to build\";\n    private static final String BOUNDS_DESCRIPTION = \"The file containing WKT bounds to restrain the loading.\";\n    private static final String RAW_DESCRIPTION = \"Whether or not to stop at the raw atlas. If this is enabled, way-sectioning will not happen\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    private List<File> pbfs;\n\n    public static void main(final String[] args)\n    {\n        new PbfToAtlasCommand().runSubcommandAndExit(args);\n    }\n\n    public PbfToAtlasCommand()\n    {\n        super();\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        // set up the output path from the parent class\n        final int code = super.execute();\n        if (code != 0)\n        {\n            return code;\n        }\n\n        getInputPBFs();\n        final String countryName = this.optionAndArgumentDelegate.getOptionArgument(COUNTRY_NAME)\n                .orElseThrow(AtlasShellToolsException::new);\n        this.pbfs.forEach(pbf ->\n        {\n            PackedAtlas atlas = (PackedAtlas) new RawAtlasGenerator(pbf,\n                    AtlasLoadingOption.createOptionWithOnlySectioning(), getBounds()).build();\n            final String pbfName = pbf.getName().replace(FileSuffix.PBF.toString(), \"\");\n            final String rawAtlasFilename = String.format(\"%s%s%s%s\", countryName,\n                    Shard.SHARD_DATA_SEPARATOR, pbfName, FileSuffix.ATLAS);\n            if (!stopAtRaw())\n            {\n                final AtlasSectionProcessor waySectionProcessor = new AtlasSectionProcessor(atlas,\n                        AtlasLoadingOption.createOptionWithNoSlicing());\n                atlas = (PackedAtlas) waySectionProcessor.run();\n            }\n            atlas.setSaveSerializationFormat(AtlasSerializationFormat.PROTOBUF);\n            final Path concatenatedPath;\n            if (this.optionAndArgumentDelegate\n                    .hasOption(MultipleOutputCommand.OUTPUT_DIRECTORY_OPTION_LONG))\n            {\n                // save atlas to user specified output directory\n                concatenatedPath = Paths.get(getOutputPath().toAbsolutePath().toString(),\n                        rawAtlasFilename);\n            }\n            else\n            {\n                // save atlas in place\n                concatenatedPath = Paths.get(Paths.get(pbf.getAbsolutePathString()).getParent()\n                        .toAbsolutePath().toString(), rawAtlasFilename);\n            }\n            this.outputDelegate.printlnStdout(concatenatedPath.toAbsolutePath().toString());\n            final File outputFile = new File(concatenatedPath.toAbsolutePath().toString(),\n                    this.getFileSystem());\n            atlas.save(outputFile);\n        });\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"pbf2atlas\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"generate way-sectioned Atlas file(s) from the given PBF shard(s)\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\",\n                PbfToAtlasCommand.class.getResourceAsStream(\"PbfToAtlasDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", PbfToAtlasCommand.class\n                .getResourceAsStream(\"PbfToAtlasCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        final Integer[] contexts = this.optionAndArgumentDelegate.getFilteredRegisteredContexts()\n                .toArray(new Integer[0]);\n        this.registerArgument(PBF_PATH_HINT, ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED,\n                contexts);\n        this.registerOptionWithRequiredArgument(COUNTRY_NAME, COUNTRY_NAME_DESCRIPTION,\n                OptionOptionality.REQUIRED, COUNTRY_NAME);\n        this.registerOptionWithRequiredArgument(BOUNDS, BOUNDS_DESCRIPTION,\n                OptionOptionality.OPTIONAL, BOUNDS);\n        this.registerOption(RAW, RAW_DESCRIPTION, OptionOptionality.OPTIONAL);\n        super.registerOptionsAndArguments();\n    }\n\n    private MultiPolygon getBounds()\n    {\n        final Optional<String> boundsFilePathOption = this.optionAndArgumentDelegate\n                .getOptionArgument(BOUNDS);\n        if (boundsFilePathOption.isPresent())\n        {\n            final String wktFileName = boundsFilePathOption.get();\n            final File wktFile = new File(wktFileName, this.getFileSystem());\n            if (wktFileName.endsWith(FileSuffix.GZIP.toString()))\n            {\n                wktFile.setDecompressor(Decompressor.GZIP);\n            }\n            final String wkt = wktFile.firstLine();\n            return MultiPolygon.wkt(wkt);\n        }\n        else\n        {\n            return MultiPolygon.MAXIMUM;\n        }\n    }\n\n    /**\n     * Get a list of input PBF resources from the input switch\n     */\n    private void getInputPBFs()\n    {\n        if (this.pbfs == null)\n        {\n            this.pbfs = new ArrayList<>();\n        }\n        else\n        {\n            return;\n        }\n\n        final List<String> inputPbfPaths = this.optionAndArgumentDelegate\n                .getVariadicArgument(PBF_PATH_HINT);\n\n        inputPbfPaths.forEach(path ->\n        {\n            final File file = new File(path, this.getFileSystem(), false);\n            if (!file.exists())\n            {\n                this.outputDelegate.printlnWarnMessage(\"file not found: \" + path);\n            }\n            else if (file.isDirectory())\n            {\n                this.outputDelegate.printlnWarnMessage(\"skipping directory: \" + path);\n            }\n            else\n            {\n                if (this.optionAndArgumentDelegate.hasVerboseOption())\n                {\n                    this.outputDelegate.printlnCommandMessage(\"loading \" + path);\n                }\n\n                this.pbfs.add(file);\n            }\n        });\n    }\n\n    private boolean stopAtRaw()\n    {\n        return this.optionAndArgumentDelegate.hasOption(RAW);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/SubAtlasCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.AtlasLoaderCommand;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.PredicateTemplate;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class SubAtlasCommand extends AtlasLoaderCommand\n{\n    private static final Logger logger = LoggerFactory.getLogger(SubAtlasCommand.class);\n\n    private static final String POLYGON_OPTION_LONG = \"polygon\";\n    private static final String POLYGON_OPTION_DESCRIPTION = \"The WKT of the polygon with which to cut.\";\n    private static final String POLYGON_OPTION_HINT = \"wkt\";\n\n    private static final List<String> CUT_TYPE_STRINGS = Arrays.stream(AtlasCutType.values())\n            .map(AtlasCutType::toString).collect(Collectors.toList());\n    private static final String CUT_TYPE_OPTION_LONG = \"cut-type\";\n    private static final String CUT_TYPE_OPTION_DESCRIPTION = \"The cut-type of this subatlas. Valid settings are: \"\n            + new StringList(CUT_TYPE_STRINGS).join(\", \") + \". Defaults to SOFT_CUT.\";\n    private static final String CUT_TYPE_OPTION_HINT = \"type\";\n\n    private static final String SLICE_FIRST_OPTION_LONG = \"slice-first\";\n    private static final String SLICE_FIRST_OPTION_DESCRIPTION = \"Cut with supplied geometry before applying the predicate.\";\n\n    private static final List<String> IMPORTS_ALLOW_LIST = Arrays.asList(\n            \"org.openstreetmap.atlas.geography.atlas.items\",\n            \"org.openstreetmap.atlas.tags.annotations\",\n            \"org.openstreetmap.atlas.tags.annotations.validation\",\n            \"org.openstreetmap.atlas.tags.annotations.extraction\", \"org.openstreetmap.atlas.tags\",\n            \"org.openstreetmap.atlas.tags.names\", \"org.openstreetmap.atlas.geography\",\n            \"org.openstreetmap.atlas.utilities.collections\");\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    private AtlasCutType cutType;\n    private Polygon polygon;\n    private Predicate<AtlasEntity> matcher;\n\n    public static void main(final String[] args)\n    {\n        new SubAtlasCommand().runSubcommandAndExit(args);\n    }\n\n    public SubAtlasCommand()\n    {\n        super();\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n        this.cutType = AtlasCutType.SOFT_CUT;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"subatlas\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"cut subatlases according to given parameters\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\",\n                SubAtlasCommand.class.getResourceAsStream(\"SubAtlasCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\",\n                SubAtlasCommand.class.getResourceAsStream(\"SubAtlasCommandExamplesSection.txt\"));\n        registerManualPageSectionsFromTemplate(new PredicateTemplate());\n        super.registerManualPageSections();\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        this.registerOptionWithRequiredArgument(POLYGON_OPTION_LONG, POLYGON_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, POLYGON_OPTION_HINT);\n        this.registerOptionsAndArgumentsFromTemplate(new PredicateTemplate());\n        this.registerOptionWithRequiredArgument(CUT_TYPE_OPTION_LONG, CUT_TYPE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, CUT_TYPE_OPTION_HINT);\n        this.registerOption(SLICE_FIRST_OPTION_LONG, SLICE_FIRST_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL);\n        super.registerOptionsAndArguments();\n    }\n\n    @Override\n    protected void processAtlas(final Atlas atlas, final String atlasFileName,\n            final File atlasResource)\n    {\n        Optional<Atlas> subbedAtlas = Optional.empty();\n\n        if (this.optionAndArgumentDelegate.hasOption(SLICE_FIRST_OPTION_LONG))\n        {\n            if (this.polygon != null)\n            {\n                subbedAtlas = atlas.subAtlas(this.polygon, this.cutType);\n            }\n\n            if (this.matcher != null)\n            {\n                subbedAtlas = subbedAtlas.orElse(atlas).subAtlas(this.matcher, this.cutType);\n            }\n        }\n        else\n        {\n            if (this.matcher != null)\n            {\n                subbedAtlas = atlas.subAtlas(this.matcher, this.cutType);\n            }\n\n            if (this.polygon != null)\n            {\n                subbedAtlas = subbedAtlas.orElse(atlas).subAtlas(this.polygon, this.cutType);\n            }\n        }\n\n        if (subbedAtlas.isPresent())\n        {\n            final String fileName = \"sub_\"\n                    + AtlasLoaderCommand.removeSuffixFromFileName(atlasFileName);\n            final Path concatenatedPath = Paths.get(getOutputPath().toAbsolutePath().toString(),\n                    fileName);\n            final File outputFile = new File(\n                    concatenatedPath.toAbsolutePath().toString() + FileSuffix.ATLAS,\n                    this.getFileSystem());\n            subbedAtlas.get().save(outputFile);\n            if (this.optionAndArgumentDelegate.hasVerboseOption())\n            {\n                this.outputDelegate\n                        .printlnCommandMessage(\"saved to \" + outputFile.getAbsolutePathString());\n            }\n        }\n        else\n        {\n            this.outputDelegate.printlnWarnMessage(\n                    \"skipping save of empty subatlas cut from \" + atlasResource.getPathString());\n        }\n    }\n\n    @Override\n    protected int start()\n    {\n        final Optional<String> cutTypeParameter = this.optionAndArgumentDelegate\n                .getOptionArgument(CUT_TYPE_OPTION_LONG);\n        final Optional<String> wktParameter = this.optionAndArgumentDelegate\n                .getOptionArgument(POLYGON_OPTION_LONG);\n\n        if (cutTypeParameter.isPresent())\n        {\n            try\n            {\n                this.cutType = AtlasCutType.valueOf(cutTypeParameter.get().toUpperCase());\n            }\n            catch (final IllegalArgumentException exception)\n            {\n                this.outputDelegate\n                        .printlnErrorMessage(\"invalid cut type \" + cutTypeParameter.get());\n                this.outputDelegate\n                        .printlnStderr(\"Try \" + new StringList(CUT_TYPE_STRINGS).join(\", \"));\n                return 1;\n            }\n        }\n\n        if (wktParameter.isPresent())\n        {\n            final WKTReader reader = new WKTReader();\n            Geometry geometry = null;\n            try\n            {\n                geometry = reader.read(wktParameter.get());\n            }\n            catch (final ParseException exception)\n            {\n                logger.error(\"unable to parse {}\", wktParameter.get(), exception);\n                return 1;\n            }\n\n            if (geometry instanceof org.locationtech.jts.geom.Polygon)\n            {\n                this.polygon = new JtsPolygonConverter()\n                        .backwardConvert((org.locationtech.jts.geom.Polygon) geometry);\n            }\n            else\n            {\n                this.outputDelegate\n                        .printlnErrorMessage(\"unsupported geometry type \" + wktParameter.get());\n                return 1;\n            }\n        }\n        this.matcher = PredicateTemplate.getPredicate(AtlasEntity.class, IMPORTS_ALLOW_LIST, this)\n                .orElse(null);\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/TaggableMatcherPrinterCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.util.List;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\n\n/**\n * @author lcram\n */\npublic class TaggableMatcherPrinterCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final String REVERSE_OPTION_LONG = \"reverse\";\n    private static final String REVERSE_OPTION_DESCRIPTION = \"Convert an old-style TaggableFilter into a TaggableMatcher.\";\n\n    private static final String FILTERS_ARGUMENT = \"filters\";\n    private static final String MATCHERS_ARGUMENT = \"matchers\";\n\n    private static final int REVERSE_CONTEXT = 4;\n\n    public static void main(final String[] args)\n    {\n        new TaggableMatcherPrinterCommand().runSubcommandAndExit(args);\n    }\n\n    @Override\n    public int execute()\n    {\n        if (this.getOptionAndArgumentDelegate().getParserContext() == REVERSE_CONTEXT)\n        {\n            executeReverseContext();\n        }\n        else\n        {\n            executeDefaultContext();\n        }\n\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"print-matcher\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"print a TaggableMatcher as a tree\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\", AtlasSearchCommand.class\n                .getResourceAsStream(\"TaggableMatcherPrinterCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\", AtlasSearchCommand.class\n                .getResourceAsStream(\"TaggableMatcherPrinterCommandExamplesSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerArgument(MATCHERS_ARGUMENT, ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED);\n        registerArgument(FILTERS_ARGUMENT, ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED,\n                REVERSE_CONTEXT);\n        registerOption(REVERSE_OPTION_LONG, REVERSE_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                REVERSE_CONTEXT);\n        super.registerOptionsAndArguments();\n    }\n\n    private void executeDefaultContext()\n    {\n        final List<String> definitions = this.getOptionAndArgumentDelegate()\n                .getVariadicArgument(MATCHERS_ARGUMENT);\n        for (int i = 0; i < definitions.size(); i++)\n        {\n            final String definition = definitions.get(i);\n            try\n            {\n                final TaggableMatcher matcher = TaggableMatcher.from(definition);\n                this.getCommandOutputDelegate().printStdout(matcher.prettyPrintTree());\n                if (matcher.lengthOfLongestLineForPrintedTree() > this.getMaximumColumn())\n                {\n                    this.getCommandOutputDelegate()\n                            .printlnWarnMessage(\"tree was too big for detected terminal width\");\n                    this.getCommandOutputDelegate().printlnCommandMessage(\n                            \"try piping into `less -S' to disable line-wrapping\");\n                }\n            }\n            catch (final CoreException exception)\n            {\n                if (exception.getMessage().contains(\"invalid nested equality operators\"))\n                {\n                    this.getCommandOutputDelegate().printlnErrorMessage(\n                            \"definition `\" + definition + \"' contained nested equality operators\");\n                }\n                else\n                {\n                    throw exception;\n                }\n            }\n\n            // Print an extra newline between trees, but not for the last tree\n            if (i < definitions.size() - 1)\n            {\n                this.getCommandOutputDelegate().printlnStdout(\"\");\n            }\n        }\n    }\n\n    private void executeReverseContext()\n    {\n        final List<String> definitions = this.getOptionAndArgumentDelegate()\n                .getVariadicArgument(FILTERS_ARGUMENT);\n        for (final String definition : definitions)\n        {\n            try\n            {\n                this.getCommandOutputDelegate().printlnStdout(TaggableFilter\n                        .forDefinition(definition).convertToTaggableMatcher().getDefinition());\n            }\n            catch (final Exception exception)\n            {\n                this.getCommandOutputDelegate().printlnErrorMessage(exception.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/TemplateTestCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.util.List;\n\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AtlasShellToolsCommandTemplate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.ListOfNumbersTemplate;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\n\n/**\n * A command to test the {@link AtlasShellToolsCommandTemplate} feature.\n * \n * @author lcram\n */\npublic class TemplateTestCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final int REVERSE_CONTEXT = 4;\n\n    public static void main(final String[] args)\n    {\n        new TemplateTestCommand().runSubcommandAndExit(args);\n    }\n\n    @Override\n    public int execute()\n    {\n        if (this.getOptionAndArgumentDelegate()\n                .getParserContext() == AbstractAtlasShellToolsCommand.DEFAULT_CONTEXT)\n        {\n            final List<Integer> listOfNumbers = ListOfNumbersTemplate.getListOfNumbers(this);\n            if (listOfNumbers.isEmpty())\n            {\n                this.getCommandOutputDelegate().printlnErrorMessage(\"failed to parse number list!\");\n                return 1;\n            }\n            this.getCommandOutputDelegate().printlnStdout(listOfNumbers.toString(),\n                    TTYAttribute.GREEN);\n        }\n        else\n        {\n            this.getCommandOutputDelegate().printlnStdout(\"Using reverse context!\",\n                    TTYAttribute.GREEN);\n        }\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"template-test\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"test the Atlas Shell Tools template feature\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        registerManualPageSectionsFromTemplate(new ListOfNumbersTemplate());\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerOptionsAndArgumentsFromTemplate(\n                new ListOfNumbersTemplate(AbstractAtlasShellToolsCommand.DEFAULT_CONTEXT));\n        registerOption(\"reverse\", \"Perform this operation in reverse.\", OptionOptionality.REQUIRED,\n                REVERSE_CONTEXT);\n        super.registerOptionsAndArguments();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/WKTShardCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.geom.Polygon;\nimport org.locationtech.jts.geom.prep.PreparedPolygon;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.geography.sharding.converters.StringToShardConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.CountryBoundaryMapTemplate;\nimport org.openstreetmap.atlas.utilities.command.subcommands.templates.ShardingTemplate;\nimport org.openstreetmap.atlas.utilities.command.terminal.TTYAttribute;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class WKTShardCommand extends AbstractAtlasShellToolsCommand\n{\n    private static final Logger logger = LoggerFactory.getLogger(WKTShardCommand.class);\n\n    private static final String INPUT_FILE_OPTION_LONG = \"input\";\n    private static final String INPUT_FILE_OPTION_DESCRIPTION = \"An input file from which to source the WKT entities. See DESCRIPTION section for details.\";\n    private static final String INPUT_FILE_OPTION_HINT = \"file\";\n\n    private static final Integer SHARDING_CONTEXT = 3;\n    private static final Integer COUNTRY_BOUNDARY_CONTEXT = 4;\n\n    private static final String INPUT_WKT_SHARD = \"wkt|shard\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    public static void main(final String[] args)\n    {\n        new WKTShardCommand().runSubcommandAndExit(args);\n    }\n\n    public WKTShardCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n    }\n\n    @Override\n    public int execute()\n    {\n        final List<String> inputWktOrShard = new ArrayList<>();\n        if (this.optionAndArgumentDelegate.hasOption(INPUT_FILE_OPTION_LONG))\n        {\n            inputWktOrShard.addAll(readInputsFromFile(this.optionAndArgumentDelegate\n                    .getOptionArgument(INPUT_FILE_OPTION_LONG).orElse(null)));\n        }\n        inputWktOrShard.addAll(this.optionAndArgumentDelegate.getVariadicArgument(INPUT_WKT_SHARD));\n\n        if (inputWktOrShard.isEmpty())\n        {\n            this.outputDelegate.printlnWarnMessage(\"no input WKTs were found\");\n            return 0;\n        }\n\n        Sharding sharding = null;\n        CountryBoundaryMap countryBoundaryMap = null;\n        if (this.optionAndArgumentDelegate.getParserContext() == SHARDING_CONTEXT)\n        {\n            sharding = ShardingTemplate.getSharding(this);\n        }\n        else if (this.optionAndArgumentDelegate.getParserContext() == COUNTRY_BOUNDARY_CONTEXT)\n        {\n            final Optional<CountryBoundaryMap> mapOptional = CountryBoundaryMapTemplate\n                    .getCountryBoundaryMap(this);\n            if (mapOptional.isEmpty())\n            {\n                this.outputDelegate.printlnErrorMessage(\"failed to load country boundary\");\n                return 1;\n            }\n            countryBoundaryMap = mapOptional.get();\n        }\n        else\n        {\n            throw new AtlasShellToolsException();\n        }\n\n        for (int i = 0; i < inputWktOrShard.size(); i++)\n        {\n            final String wktOrShard = inputWktOrShard.get(i);\n            parseWktOrShardAndPrintOutput(wktOrShard, sharding, countryBoundaryMap);\n\n            // Only print a separating newline if there were multiple entries\n            if (i < inputWktOrShard.size() - 1)\n            {\n                this.outputDelegate.printlnStdout(\"\");\n            }\n        }\n\n        return 0;\n    }\n\n    @Override\n    public String getCommandName()\n    {\n        return \"wkt-shard\";\n    }\n\n    @Override\n    public String getSimpleDescription()\n    {\n        return \"perform various intersection lookups\";\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"DESCRIPTION\",\n                WKTShardCommand.class.getResourceAsStream(\"WKTShardCommandDescriptionSection.txt\"));\n        addManualPageSection(\"EXAMPLES\",\n                WKTShardCommand.class.getResourceAsStream(\"WKTShardCommandExamplesSection.txt\"));\n        registerManualPageSectionsFromTemplate(new ShardingTemplate());\n        registerManualPageSectionsFromTemplate(new CountryBoundaryMapTemplate());\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        registerArgument(INPUT_WKT_SHARD, ArgumentArity.VARIADIC, ArgumentOptionality.OPTIONAL,\n                SHARDING_CONTEXT, COUNTRY_BOUNDARY_CONTEXT);\n        registerOptionWithRequiredArgument(INPUT_FILE_OPTION_LONG, INPUT_FILE_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, INPUT_FILE_OPTION_HINT, SHARDING_CONTEXT,\n                COUNTRY_BOUNDARY_CONTEXT);\n        registerOptionsAndArgumentsFromTemplate(new ShardingTemplate(SHARDING_CONTEXT));\n        registerOptionsAndArgumentsFromTemplate(\n                new CountryBoundaryMapTemplate(COUNTRY_BOUNDARY_CONTEXT));\n        super.registerOptionsAndArguments();\n    }\n\n    private void parseWktOrShardAndPrintOutput(final String wktOrShard, final Sharding sharding,\n            final CountryBoundaryMap countryBoundaryMap)\n    {\n        final Optional<Geometry> geometryOptional = parseWktOrShardString(wktOrShard);\n\n        if (geometryOptional.isEmpty())\n        {\n            this.outputDelegate.printlnErrorMessage(\n                    \"unable to parse '\" + wktOrShard + \"' as WKT or shard string\");\n            return;\n        }\n        final Geometry geometry = geometryOptional.get();\n\n        if (geometry instanceof Point)\n        {\n            printPointOutput(wktOrShard, geometry, sharding, countryBoundaryMap);\n        }\n        else if (geometry instanceof LineString)\n        {\n            printLineStringOutput(wktOrShard, geometry, sharding, countryBoundaryMap);\n        }\n        else if (geometry instanceof Polygon)\n        {\n            printPolygonOutput(wktOrShard, geometry, sharding, countryBoundaryMap);\n        }\n        /*\n         * TODO handle more geometry types? e.g. MultiPoint, MultiLineString, and MultiPolygon?\n         */\n        else\n        {\n            this.outputDelegate.printlnErrorMessage(\"unsupported geometry type \" + wktOrShard);\n        }\n    }\n\n    private Optional<Geometry> parseWktOrShardString(final String wktOrShard)\n    {\n        final WKTReader reader = new WKTReader();\n        try\n        {\n            return Optional.of(reader.read(wktOrShard));\n        }\n        catch (final ParseException exception)\n        {\n            logger.warn(\"unable to parse {} as wkt\", wktOrShard, exception);\n            // input String was not a WKT, so try parsing it as a shard string\n            try\n            {\n                final StringToShardConverter converter = new StringToShardConverter();\n                final Shard shard = converter.convert(wktOrShard);\n                return Optional.of(new WKTReader().read(shard.toWkt()));\n            }\n            catch (final Exception exception2)\n            {\n                logger.warn(\"unable to parse {} as shard\", wktOrShard, exception2);\n            }\n        }\n        return Optional.empty();\n    }\n\n    private void printLineStringOutput(final String wktOrShard, final Geometry geometry,\n            final Sharding sharding, final CountryBoundaryMap countryBoundaryMap)\n    {\n        this.outputDelegate.printlnStdout(wktOrShard + \" intersects:\", TTYAttribute.BOLD);\n        final PolyLine polyline = new JtsPolyLineConverter().backwardConvert((LineString) geometry);\n        if (sharding != null)\n        {\n            final Iterable<? extends Shard> shards = sharding.shardsIntersecting(polyline);\n            for (final Shard shard : shards)\n            {\n                this.outputDelegate.printlnStdout(shard.toString(), TTYAttribute.GREEN);\n            }\n        }\n\n        if (countryBoundaryMap != null)\n        {\n            final MultiMap<String, Polygon> boundaries = countryBoundaryMap.boundaries(polyline);\n            for (final String country : boundaries.keySet())\n            {\n                this.outputDelegate.printlnStdout(country, TTYAttribute.GREEN);\n            }\n        }\n    }\n\n    private void printPointOutput(final String wktOrShard, final Geometry geometry,\n            final Sharding sharding, final CountryBoundaryMap countryBoundaryMap)\n    {\n        this.outputDelegate.printlnStdout(wktOrShard + \" covered by:\", TTYAttribute.BOLD);\n        final Location location = new JtsPointConverter().backwardConvert((Point) geometry);\n        if (sharding != null)\n        {\n            final Iterable<? extends Shard> shards = sharding.shardsCovering(location);\n            for (final Shard shard : shards)\n            {\n                this.outputDelegate.printlnStdout(shard.toString(), TTYAttribute.GREEN);\n            }\n        }\n        if (countryBoundaryMap != null)\n        {\n            final MultiMap<String, Polygon> boundaries = countryBoundaryMap.boundaries(location);\n            for (final String country : boundaries.keySet())\n            {\n                this.outputDelegate.printlnStdout(country, TTYAttribute.GREEN);\n            }\n        }\n    }\n\n    private void printPolygonOutput(final String wktOrShard, final Geometry geometry,\n            final Sharding sharding, final CountryBoundaryMap countryBoundaryMap)\n    {\n        this.outputDelegate.printlnStdout(wktOrShard + \" contains or intersects:\",\n                TTYAttribute.BOLD);\n        final org.openstreetmap.atlas.geography.Polygon polygon = new JtsPolygonConverter()\n                .backwardConvert((Polygon) geometry);\n        if (sharding != null)\n        {\n            final Iterable<? extends Shard> shards = sharding.shards(polygon);\n            for (final Shard shard : shards)\n            {\n                this.outputDelegate.printlnStdout(shard.toString(), TTYAttribute.GREEN);\n            }\n        }\n\n        if (countryBoundaryMap != null)\n        {\n            /*\n             * This is handled a little differently here than in the other printXOutput methods.\n             * This is because the CountryBoundaryMap#boundaries method does not handle certain\n             * cases the way we want it to, e.g. like when a shard boundary or other large polygon\n             * completely encloses a country boundary (think large ocean shard totally containing a\n             * small island country). We still want to report those enclosed boundaries in this\n             * case. In the future, we may want to fix CountryBoundaryMap to handle this case in\n             * some way.\n             */\n            final List<PreparedPolygon> polygons = countryBoundaryMap\n                    .query(geometry.getEnvelopeInternal()).stream().distinct()\n                    .collect(Collectors.toList());\n            final Set<String> countries = new HashSet<>();\n            polygons.forEach(polygon2 -> countries.add(CountryBoundaryMap\n                    .getGeometryProperty(polygon2.getGeometry(), ISOCountryTag.KEY)));\n            for (final String country : countries)\n            {\n                this.outputDelegate.printlnStdout(country, TTYAttribute.GREEN);\n            }\n        }\n    }\n\n    private List<String> readInputsFromFile(final String path)\n    {\n        if (path == null)\n        {\n            throw new AtlasShellToolsException();\n        }\n        final Path inputPath = this.getFileSystem().getPath(path);\n        if (inputPath.toString().startsWith(\"~\"))\n        {\n            this.outputDelegate.printlnWarnMessage(\"the '~' was not expanded by your shell\");\n        }\n        if (!Files.isReadable(inputPath) || !Files.isRegularFile(inputPath))\n        {\n            this.outputDelegate.printlnErrorMessage(\n                    inputPath.toAbsolutePath().toString() + \" is not a readable file\");\n            return new ArrayList<>();\n        }\n        final List<String> wktOrShardList = new ArrayList<>();\n        final StringResource resource = new StringResource();\n        resource.copyFrom(new File(inputPath.toAbsolutePath().toString(), this.getFileSystem()));\n        final String rawText = resource.all();\n\n        final String[] split = rawText.split(System.getProperty(\"line.separator\"));\n        for (final String line : split)\n        {\n            if (!line.isEmpty())\n            {\n                wktOrShardList.add(line);\n            }\n        }\n\n        return wktOrShardList;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/AtlasLoaderCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.command.AbstractAtlasSubCommand;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.JavaToProtoSerializationCommand;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * A helper super class for any command that wants to load atlas files from disk. Provides a builtin\n * variadic input argument, as well as automatic conversion from paths to resources with various\n * options for increased flexibility. Subclasses can override the start(), processAtlas(), and\n * finish() methods, which provide a way to operate on the input atlases without having to deal with\n * resource loading and iterating.<br>\n * This class is based off the {@link AbstractAtlasSubCommand} by cstaylor.\n *\n * @author lcram\n * @author cstaylor\n * @deprecated Please use {@link AtlasLoaderTemplate} instead. See\n *             {@link JavaToProtoSerializationCommand } for an example.\n */\n@Deprecated\npublic abstract class AtlasLoaderCommand extends MultipleOutputCommand\n{\n    private static final String COMBINED_ATLAS_NAME = \"combined.atlas\";\n\n    private static final String INPUT_HINT = \"input-atlases\";\n\n    private static final String COMBINE_OPTION_LONG = \"combine\";\n    private static final String COMBINE_OPTION_DESCRIPTION = \"Combine all input atlases into a MultiAtlas before processing.\";\n\n    private static final String STRICT_OPTION_LONG = \"strict\";\n    private static final String STRICT_OPTION_DESCRIPTION = \"Fail fast if any input atlases are missing.\";\n\n    private static final String PARALLEL_OPTION_LONG = \"parallel\";\n    private static final Character PARALLEL_OPTION_SHORT = 'p';\n    private static final String PARALLEL_OPTION_DESCRIPTION = \"Process the atlases in parallel.\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n\n    private List<Tuple<File, Atlas>> atlases;\n\n    public static String removeSuffixFromFileName(final String fileName)\n    {\n        final String[] split = fileName.split(\"\\\\.\");\n        return split[0];\n    }\n\n    public AtlasLoaderCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n        this.atlases = null;\n    }\n\n    @Override\n    public int execute()\n    {\n        // set up the output path from the parent class\n        int code = super.execute();\n        if (code != 0)\n        {\n            return code;\n        }\n\n        // call the user start implementation\n        code = start();\n        if (code != 0)\n        {\n            return code;\n        }\n\n        final List<Tuple<File, Atlas>> atlasTuples = getInputAtlases();\n        if (atlasTuples.isEmpty())\n        {\n            this.outputDelegate.printlnErrorMessage(\"no atlas files were loaded\");\n            return 1;\n        }\n\n        Stream<Tuple<File, Atlas>> atlasTupleStream = atlasTuples.stream();\n        if (this.optionAndArgumentDelegate.hasOption(PARALLEL_OPTION_LONG))\n        {\n            atlasTupleStream = atlasTupleStream.parallel();\n        }\n\n        if (this.optionAndArgumentDelegate.hasOption(COMBINE_OPTION_LONG))\n        {\n            if (this.optionAndArgumentDelegate.hasVerboseOption())\n            {\n                this.outputDelegate\n                        .printlnCommandMessage(\"processing all atlases as one multiatlas...\");\n            }\n            processAtlas(\n                    new MultiAtlas(\n                            atlasTupleStream.map(Tuple::getSecond).collect(Collectors.toList())),\n                    COMBINED_ATLAS_NAME, new File(COMBINED_ATLAS_NAME, this.getFileSystem()));\n        }\n        else\n        {\n            final int size = atlasTuples.size();\n            final int[] count = new int[1];\n            count[0] = 1;\n            atlasTupleStream.forEach(atlasTuple ->\n            {\n                if (this.optionAndArgumentDelegate.hasVerboseOption())\n                {\n                    this.outputDelegate.printlnCommandMessage(\n                            \"processing atlas \" + atlasTuple.getFirst().getAbsolutePathString()\n                                    + \" (\" + count[0] + \"/\" + size + \")\");\n                }\n                processAtlas(atlasTuple.getSecond(), atlasTuple.getFirst().getName(),\n                        atlasTuple.getFirst());\n                count[0]++;\n            });\n        }\n\n        // return the exit code from the user's finish implementation\n        return finish();\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"ATLAS LOADER\",\n                AtlasLoaderCommand.class.getResourceAsStream(\"AtlasLoaderCommandSection.txt\"));\n        super.registerManualPageSections();\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        final Integer[] contexts = this.optionAndArgumentDelegate.getFilteredRegisteredContexts()\n                .toArray(new Integer[0]);\n        registerOption(STRICT_OPTION_LONG, STRICT_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                contexts);\n        registerOption(PARALLEL_OPTION_LONG, PARALLEL_OPTION_SHORT, PARALLEL_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, contexts);\n        registerOption(COMBINE_OPTION_LONG, COMBINE_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL,\n                contexts);\n        registerArgument(INPUT_HINT, ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED,\n                contexts);\n        super.registerOptionsAndArguments();\n    }\n\n    /**\n     * After all atlas files have been handled, the subclass can override this method for final\n     * notification and processing. The return value is sent back to the caller through System.exit\n     *\n     * @return a status value returned through System.exit\n     */\n    protected int finish()\n    {\n        return 0; // NOSONAR\n    }\n\n    /**\n     * Subclasses can implement this method for processing each atlas object as it is loaded.\n     *\n     * @param atlas\n     *            the atlas to process\n     * @param atlasFileName\n     *            name of the atlas file resource\n     * @param atlasResource\n     *            the {@link File} resource from which the atlas was loaded\n     */\n    protected abstract void processAtlas(Atlas atlas, String atlasFileName, File atlasResource);\n\n    /**\n     * Subclasses can override this method if they want to do something once before processing the\n     * atlases. The start method can return a status to indicate if the start-up operations were\n     * successful. On return 0, the command will continue execution. On any non-zero exit code, the\n     * command will terminate early and return the code through System.exit.\n     *\n     * @return a status value returned through System.exit\n     */\n    protected int start()\n    {\n        return 0; // NOSONAR\n    }\n\n    /**\n     * Get a list of input atlas resources with their associated atlases, one for each atlas loaded\n     * from the input-atlases parameter. This method can be used when overriding the execute method,\n     * in place of the standard start(), handle(), finish() semantics.\n     *\n     * @return the list of atlases\n     */\n    private List<Tuple<File, Atlas>> getInputAtlases()\n    {\n        if (this.atlases == null)\n        {\n            this.atlases = new ArrayList<>();\n        }\n        else\n        {\n            return this.atlases;\n        }\n\n        final List<String> inputAtlasPaths = this.optionAndArgumentDelegate\n                .getVariadicArgument(INPUT_HINT);\n\n        final AtlasResourceLoader loader = new AtlasResourceLoader();\n        inputAtlasPaths.forEach(path ->\n        {\n            final File file = new File(path, this.getFileSystem(), false);\n            if (!file.exists())\n            {\n                this.outputDelegate.printlnWarnMessage(\"file not found: \" + path);\n            }\n            else if (file.isDirectory())\n            {\n                this.outputDelegate.printlnWarnMessage(\"skipping directory: \" + path);\n            }\n            else\n            {\n                if (this.optionAndArgumentDelegate.hasVerboseOption())\n                {\n                    this.outputDelegate.printlnCommandMessage(\"loading \" + path);\n                }\n                final Optional<Atlas> atlas = loader.safeLoad(file);\n                if (atlas.isPresent())\n                {\n                    this.atlases.add(new Tuple<>(file, atlas.get()));\n                }\n                else\n                {\n                    this.outputDelegate.printlnWarnMessage(\"could not load: \" + file);\n                }\n            }\n        });\n\n        if (this.optionAndArgumentDelegate.hasOption(STRICT_OPTION_LONG)\n                && this.atlases.size() != inputAtlasPaths.size())\n        {\n            this.outputDelegate.printlnErrorMessage(\"strict load is missing some atlas(es)\");\n            this.atlases = new ArrayList<>();\n        }\n\n        return this.atlases;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/AtlasLoaderTemplate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.IntSupplier;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AtlasShellToolsCommandTemplate;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.function.TernaryConsumer;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * An {@link AtlasShellToolsCommandTemplate} for commands that want to read and process some input\n * {@link Atlas}es.\n *\n * @author lcram\n */\npublic class AtlasLoaderTemplate implements AtlasShellToolsCommandTemplate\n{\n    private static final String COMBINED_ATLAS_NAME = \"combined.atlas\";\n\n    private static final String INPUT_HINT = \"input-atlases\";\n    private static final String COMBINE_OPTION_LONG = \"combine\";\n    private static final String STRICT_OPTION_LONG = \"strict\";\n    private static final String PARALLEL_OPTION_LONG = \"parallel\";\n    private static final Character PARALLEL_OPTION_SHORT = 'p';\n\n    private final Integer[] contexts;\n\n    /**\n     * Execute a command using the {@link AtlasLoaderTemplate} in a structured way. This is entirely\n     * optional, but highly recommended as it handles boilerplate functionality for you\n     * automatically.\n     *\n     * @param parentCommand\n     *            the parent command that controls this template\n     * @param startUpFunction\n     *            Provide this function if you want to do something once before processing the\n     *            atlases. The start function can return a status to indicate if the start-up\n     *            operations were successful. On return 0, {@code execute} will continue execution.\n     *            On any non-zero exit code, the {@code execute} will return this function's exit\n     *            value. Pass {@code null} to skip the startup step.\n     * @param processAtlasFunction\n     *            This function processes each atlas object as it is loaded. It is not optional, you\n     *            may not pass {@code null} for this function. The processAtlasFunction receives as\n     *            arguments an Atlas object, a String name of the Atlas file resource, and the File\n     *            resource object from which the Atlas was loaded.\n     * @param finishUpFunction\n     *            Provide this method to run after all atlas files have been handled for final\n     *            notification and processing. The exit value of this function will be returned to\n     *            the caller of {@code execute}. Pass {@code null} to skip the finish up step.\n     * @return An exit value for the command. Callers can simply return this from their execute\n     *         methods.\n     */\n    public static int execute(final AbstractAtlasShellToolsCommand parentCommand,\n            final IntSupplier startUpFunction,\n            final TernaryConsumer<Atlas, String, File> processAtlasFunction,\n            final IntSupplier finishUpFunction)\n    {\n        Objects.requireNonNull(processAtlasFunction);\n\n        /*\n         * Run the user's optionally supplied start-up function.\n         */\n        if (startUpFunction != null)\n        {\n            final int returnCode = startUpFunction.getAsInt();\n            if (returnCode != 0)\n            {\n                return returnCode;\n            }\n        }\n\n        /*\n         * Get the input atlases and run the process code on each. We optionally collapse into a\n         * MultiAtlas or stream parallel, depending on user options.\n         */\n        final List<Tuple<File, Atlas>> atlasTuples = getInputAtlases(parentCommand);\n        if (atlasTuples.isEmpty())\n        {\n            parentCommand.getCommandOutputDelegate()\n                    .printlnErrorMessage(\"no atlas files were loaded\");\n            return 1;\n        }\n\n        Stream<Tuple<File, Atlas>> atlasTupleStream = atlasTuples.stream();\n        if (parentCommand.getOptionAndArgumentDelegate().hasOption(PARALLEL_OPTION_LONG))\n        {\n            atlasTupleStream = atlasTupleStream.parallel();\n        }\n\n        if (parentCommand.getOptionAndArgumentDelegate().hasOption(COMBINE_OPTION_LONG))\n        {\n            if (parentCommand.getOptionAndArgumentDelegate().hasVerboseOption())\n            {\n                parentCommand.getCommandOutputDelegate()\n                        .printlnCommandMessage(\"processing all atlases as one multiatlas...\");\n            }\n            processAtlasFunction.accept(\n                    new MultiAtlas(\n                            atlasTupleStream.map(Tuple::getSecond).collect(Collectors.toList())),\n                    COMBINED_ATLAS_NAME,\n                    new File(COMBINED_ATLAS_NAME, parentCommand.getFileSystem()));\n        }\n        else\n        {\n            final int size = atlasTuples.size();\n            final int[] count = { 1 };\n            atlasTupleStream.forEach(atlasTuple ->\n            {\n                if (parentCommand.getOptionAndArgumentDelegate().hasVerboseOption())\n                {\n                    parentCommand.getCommandOutputDelegate()\n                            .printlnCommandMessage(\"processing atlas \"\n                                    + atlasTuple.getFirst().getAbsolutePathString() + \" (\"\n                                    + count[0] + \"/\" + size + \")\");\n                }\n                processAtlasFunction.accept(atlasTuple.getSecond(), atlasTuple.getFirst().getName(),\n                        atlasTuple.getFirst());\n                count[0]++;\n            });\n        }\n\n        /*\n         * Run the user's optionally supplied finish-up function.\n         */\n        if (finishUpFunction != null)\n        {\n            return finishUpFunction.getAsInt();\n        }\n\n        return 0;\n    }\n\n    /**\n     * Get a list of input atlas resources with their associated atlases, one for each atlas loaded\n     * from the input-atlases parameter.\n     *\n     * @return the list of atlases\n     */\n    private static List<Tuple<File, Atlas>> getInputAtlases(\n            final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        final List<Tuple<File, Atlas>> atlases = new ArrayList<>();\n\n        final List<String> inputAtlasPaths = parentCommand.getOptionAndArgumentDelegate()\n                .getVariadicArgument(INPUT_HINT);\n\n        final AtlasResourceLoader loader = new AtlasResourceLoader();\n        inputAtlasPaths.forEach(path ->\n        {\n            final File file = new File(path, parentCommand.getFileSystem(), false);\n            if (!file.exists())\n            {\n                parentCommand.getCommandOutputDelegate()\n                        .printlnWarnMessage(\"file not found: \" + path);\n            }\n            else if (file.isDirectory())\n            {\n                parentCommand.getCommandOutputDelegate()\n                        .printlnWarnMessage(\"skipping directory: \" + path);\n            }\n            else\n            {\n                if (parentCommand.getOptionAndArgumentDelegate().hasVerboseOption())\n                {\n                    parentCommand.getCommandOutputDelegate()\n                            .printlnCommandMessage(\"loading \" + path);\n                }\n                final Optional<Atlas> atlas = loader.safeLoad(file);\n                if (atlas.isPresent())\n                {\n                    atlases.add(new Tuple<>(file, atlas.get()));\n                }\n                else\n                {\n                    parentCommand.getCommandOutputDelegate()\n                            .printlnWarnMessage(\"could not load: \" + file);\n                }\n            }\n        });\n\n        if (parentCommand.getOptionAndArgumentDelegate().hasOption(STRICT_OPTION_LONG)\n                && atlases.size() != inputAtlasPaths.size())\n        {\n            parentCommand.getCommandOutputDelegate()\n                    .printlnErrorMessage(\"strict load is missing some atlas(es)\");\n            atlases.clear();\n        }\n\n        return atlases;\n    }\n\n    /**\n     * This constructor allows callers to specify under which contexts they want the options\n     * provided by this template to appear. If left blank, this template will only be applied to the\n     * default context.\n     *\n     * @param contexts\n     *            the parse contexts under which you want the options provided by this template to\n     *            appear\n     */\n    public AtlasLoaderTemplate(final Integer... contexts)\n    {\n        this.contexts = contexts;\n    }\n\n    @Override\n    public void registerManualPageSections(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.addManualPageSection(\"ATLAS LOADER\",\n                AtlasLoaderTemplate.class.getResourceAsStream(\"AtlasLoaderTemplateSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.registerOption(STRICT_OPTION_LONG,\n                \"Fail fast if any input atlases are missing.\", OptionOptionality.OPTIONAL,\n                this.contexts);\n        parentCommand.registerOption(PARALLEL_OPTION_LONG, PARALLEL_OPTION_SHORT,\n                \"Process the atlases in parallel.\", OptionOptionality.OPTIONAL, this.contexts);\n        parentCommand.registerOption(COMBINE_OPTION_LONG,\n                \"Combine all input atlases into a MultiAtlas before processing.\",\n                OptionOptionality.OPTIONAL, this.contexts);\n        parentCommand.registerArgument(INPUT_HINT, ArgumentArity.VARIADIC,\n                ArgumentOptionality.REQUIRED, this.contexts);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/CountryBoundaryMapTemplate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AtlasShellToolsCommandTemplate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\n\n/**\n * An {@link AtlasShellToolsCommandTemplate} for commands that want to read an input\n * {@link CountryBoundaryMap}.\n *\n * @author lcram\n */\npublic class CountryBoundaryMapTemplate implements AtlasShellToolsCommandTemplate\n{\n    public static final String COUNTRY_BOUNDARY_OPTION_LONG = \"country-boundary\";\n\n    private final Integer[] contexts;\n\n    /**\n     * Get a {@link CountryBoundaryMap} object from the user's input option.\n     *\n     * @param parentCommand\n     *            the parent command that controls this template\n     * @return the {@link CountryBoundaryMap} object built from the file specified by the user\n     */\n    public static Optional<CountryBoundaryMap> getCountryBoundaryMap(\n            final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        final Optional<CountryBoundaryMap> countryBoundaryMap;\n        final File boundaryMapFile = new File(parentCommand.getOptionAndArgumentDelegate()\n                .getOptionArgument(COUNTRY_BOUNDARY_OPTION_LONG)\n                .orElseThrow(AtlasShellToolsException::new), parentCommand.getFileSystem());\n        if (!boundaryMapFile.exists())\n        {\n            parentCommand.getCommandOutputDelegate().printlnErrorMessage(\n                    \"boundary file \" + boundaryMapFile.getAbsolutePathString() + \" does not exist\");\n            return Optional.empty();\n        }\n        if (parentCommand.getOptionAndArgumentDelegate().hasVerboseOption())\n        {\n            parentCommand.getCommandOutputDelegate()\n                    .printlnCommandMessage(\"loading country boundary map...\");\n        }\n        countryBoundaryMap = Optional.of(CountryBoundaryMap.fromPlainText(boundaryMapFile));\n        if (parentCommand.getOptionAndArgumentDelegate().hasVerboseOption())\n        {\n            parentCommand.getCommandOutputDelegate().printlnCommandMessage(\"loaded boundary map\");\n        }\n        return countryBoundaryMap;\n    }\n\n    /**\n     * This constructor allows callers to specify under which contexts they want the options\n     * provided by this template to appear. If left blank, this template will only be applied to the\n     * default context.\n     *\n     * @param contexts\n     *            the parse contexts under which you want the options provided by this template to\n     *            appear\n     */\n    public CountryBoundaryMapTemplate(final Integer... contexts)\n    {\n        this.contexts = contexts;\n    }\n\n    @Override\n    public void registerManualPageSections(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.addManualPageSection(\"INPUT COUNTRY BOUNDARY MAP\", ShardingTemplate.class\n                .getResourceAsStream(\"CountryBoundaryMapTemplateSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.registerOptionWithRequiredArgument(COUNTRY_BOUNDARY_OPTION_LONG,\n                \"A boundary file to use for intersection checks. See INPUT COUNTRY BOUNDARY MAP section for details.\",\n                OptionOptionality.REQUIRED, \"boundary-file\", this.contexts);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/ListOfNumbersTemplate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.io.ByteArrayInputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AtlasShellToolsCommandTemplate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.TemplateTestCommand;\n\n/**\n * An example of how to implement an {@link AtlasShellToolsCommandTemplate}. This template simply\n * provides an option that accepts a comma separated list of numbers. Note that the code to parse\n * the option can be contained within the template itself! Check {@link TemplateTestCommand} to see\n * how to use the template in a command implementation.\n * \n * @author lcram\n */\npublic class ListOfNumbersTemplate implements AtlasShellToolsCommandTemplate\n{\n    private static final String LIST_OF_NUMBERS_OPTION_LONG = \"list-of-numbers\";\n    private static final String COULD_NOT_PARSE = \"could not parse %s '%s'\";\n\n    /**\n     * The parse contexts under which we want the options provided by this template to appear. Leave\n     * empty to use the default context.\n     */\n    private final Integer[] contexts;\n\n    public static List<Integer> getListOfNumbers(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        final String listString = parentCommand.getOptionAndArgumentDelegate()\n                .getOptionArgument(LIST_OF_NUMBERS_OPTION_LONG)\n                .orElseThrow(AtlasShellToolsException::new);\n\n        if (listString.isEmpty())\n        {\n            return new ArrayList<>();\n        }\n\n        final List<Integer> numberList = new ArrayList<>();\n        final String[] listStringSplit = listString.split(\",\");\n        for (final String numberElement : listStringSplit)\n        {\n            final int number;\n            try\n            {\n                number = Integer.parseInt(numberElement);\n                numberList.add(number);\n            }\n            catch (final NumberFormatException exception)\n            {\n                parentCommand.getCommandOutputDelegate().printlnErrorMessage(\n                        String.format(COULD_NOT_PARSE, \"number\", numberElement));\n                return new ArrayList<>();\n            }\n        }\n        return numberList;\n    }\n\n    /**\n     * This constructor allows callers to specify under which contexts they want the options\n     * provided by this template to appear. If left blank, this template will only be applied to the\n     * default context.\n     * \n     * @param contexts\n     *            the parse contexts under which you want the options provided by this template to\n     *            appear\n     */\n    public ListOfNumbersTemplate(final Integer... contexts)\n    {\n        this.contexts = contexts;\n    }\n\n    @Override\n    public void registerManualPageSections(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.addManualPageSection(\"LIST OF NUMBERS TEMPLATE\",\n                new ByteArrayInputStream(\n                        (\"This is an example man page section for the ListOfNumbersTemplate! \"\n                                + \"This template adds an option that reads a list of numbers.\")\n                                .getBytes(StandardCharsets.UTF_8)));\n    }\n\n    @Override\n    public void registerOptionsAndArguments(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.registerOptionWithRequiredArgument(LIST_OF_NUMBERS_OPTION_LONG,\n                \"Specify a comma separated list of numbers.\", OptionOptionality.REQUIRED, \"numbers\",\n                this.contexts);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/MultipleOutputCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.subcommands.AnyToGeoJsonCommand;\n\n/**\n * A {@link MultipleOutputCommand} is any command which may produce multiple output files, and wants\n * to provide users with a way to specify the desired location of those files. This command template\n * registers a '--output' option, the argument to which will be used as an output directory.\n *\n * @author lcram\n * @deprecated Please use {@link OutputDirectoryTemplate} instead. Check {@link AnyToGeoJsonCommand}\n *             for an example.\n */\n@Deprecated\npublic abstract class MultipleOutputCommand extends AbstractAtlasShellToolsCommand\n{\n    public static final String OUTPUT_DIRECTORY_OPTION_LONG = \"output\";\n    private static final Character OUTPUT_DIRECTORY_OPTION_SHORT = 'o';\n    private static final String OUTPUT_DIRECTORY_OPTION_DESCRIPTION = \"Specify an alternate output directory for any output files. If the directory \"\n            + \"does not exist, it will be created.\";\n    private static final String OUTPUT_DIRECTORY_OPTION_HINT = \"dir\";\n\n    private final OptionAndArgumentDelegate optionAndArgumentDelegate;\n    private final CommandOutputDelegate outputDelegate;\n    private Path outputPath;\n\n    public MultipleOutputCommand()\n    {\n        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();\n        this.outputDelegate = this.getCommandOutputDelegate();\n        this.outputPath = null;\n    }\n\n    /**\n     * Populate the output path field. Subclasses should override this method, but invoke it with\n     * super.execute to populate the outputPath field. The subclass can check the return code of\n     * this method to see if the output path was parsed successfully.\n     *\n     * @return the exit status, 0 indicates success while 1 indicates that the output path was\n     *         invalid\n     */\n    @Override\n    public int execute()\n    {\n        final Optional<Path> outputPathOptional = parseOutputPath();\n        if (outputPathOptional.isEmpty())\n        {\n            this.outputDelegate.printlnErrorMessage(\"invalid output path\");\n            return 1;\n        }\n        else\n        {\n            this.outputPath = outputPathOptional.get();\n        }\n        return 0;\n    }\n\n    public Path getOutputPath()\n    {\n        return this.outputPath;\n    }\n\n    @Override\n    public void registerManualPageSections()\n    {\n        addManualPageSection(\"MULTIPLE OUTPUT\", MultipleOutputCommand.class\n                .getResourceAsStream(\"MultipleOutputCommandSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments()\n    {\n        final Integer[] contexts = this.optionAndArgumentDelegate.getFilteredRegisteredContexts()\n                .toArray(new Integer[0]);\n        registerOptionWithRequiredArgument(OUTPUT_DIRECTORY_OPTION_LONG,\n                OUTPUT_DIRECTORY_OPTION_SHORT, OUTPUT_DIRECTORY_OPTION_DESCRIPTION,\n                OptionOptionality.OPTIONAL, OUTPUT_DIRECTORY_OPTION_HINT, contexts);\n        super.registerOptionsAndArguments();\n    }\n\n    private Optional<Path> parseOutputPath()\n    {\n        final Path outputParentPath = this.getFileSystem().getPath(this.optionAndArgumentDelegate\n                .getOptionArgument(OUTPUT_DIRECTORY_OPTION_LONG).orElse(\"\"));\n\n        // If output path already exists and is a file, then fail\n        if (Files.isRegularFile(outputParentPath))\n        {\n            this.outputDelegate.printlnErrorMessage(\n                    outputParentPath.toString() + \" already exists and is a file\");\n            return Optional.empty();\n        }\n\n        // If output path does not exist, create it using 'mkdir -p' behaviour\n        if (!Files.exists(outputParentPath))\n        {\n            try\n            {\n                new File(outputParentPath.toAbsolutePath().toString(), this.getFileSystem())\n                        .mkdirs();\n            }\n            catch (final Exception exception)\n            {\n                this.outputDelegate.printlnErrorMessage(\n                        \"failed to create output directory \" + outputParentPath.toString());\n                return Optional.empty();\n            }\n        }\n\n        // If output path is not writable, fail\n        if (!Files.isWritable(outputParentPath))\n        {\n            this.outputDelegate\n                    .printlnErrorMessage(outputParentPath.toString() + \" is not writable\");\n            return Optional.empty();\n        }\n\n        return Optional.of(outputParentPath);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/OutputDirectoryTemplate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AtlasShellToolsCommandTemplate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * The {@link OutputDirectoryTemplate} provides a template for any command which may produce\n * multiple output files and wants to provide users with a way to specify the desired location of\n * those files. This command template registers an '--output-directory' option, the argument to\n * which will be possibly created and used as an output directory.\n *\n * @author lcram\n */\npublic class OutputDirectoryTemplate implements AtlasShellToolsCommandTemplate\n{\n    private static final Logger logger = LoggerFactory.getLogger(OutputDirectoryTemplate.class);\n\n    private static final String OUTPUT_DIRECTORY_OPTION_LONG = \"output-directory\";\n    private static final Character OUTPUT_DIRECTORY_OPTION_SHORT = 'o';\n\n    private final Integer[] contexts;\n\n    /**\n     * Get the output path specified by the user. If the returned {@link Optional} is empty, then\n     * the output path could not be parsed and it is recommended that you exit with an error.\n     * \n     * @param parentCommand\n     *            the parent command that controls this template\n     * @return an {@link Optional} containing the output path for this command\n     */\n    public static Optional<Path> getOutputPath(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        /*\n         * Grab output path from --output-directory option if present. If that option is not\n         * present, return the current working directory.\n         */\n        final Path outputParentPath = parentCommand.getFileSystem()\n                .getPath(parentCommand.getOptionAndArgumentDelegate()\n                        .getOptionArgument(OUTPUT_DIRECTORY_OPTION_LONG).orElse(\"\"));\n\n        // If output path already exists and is a file, then fail\n        if (Files.isRegularFile(outputParentPath))\n        {\n            parentCommand.getCommandOutputDelegate().printlnErrorMessage(\n                    outputParentPath.toString() + \" already exists and is a file\");\n            return Optional.empty();\n        }\n\n        // If output path does not exist, create it using 'mkdir -p' behaviour\n        if (!Files.exists(outputParentPath))\n        {\n            try\n            {\n                new File(outputParentPath.toAbsolutePath().toString(),\n                        parentCommand.getFileSystem()).mkdirs();\n            }\n            catch (final Exception exception)\n            {\n                parentCommand.getCommandOutputDelegate().printlnErrorMessage(\n                        \"failed to create output directory \" + outputParentPath.toString());\n                logger.error(\"Failed to create output directory\", exception);\n                return Optional.empty();\n            }\n        }\n\n        // If output path is not writable, fail\n        if (!Files.isWritable(outputParentPath))\n        {\n            parentCommand.getCommandOutputDelegate()\n                    .printlnErrorMessage(outputParentPath.toString() + \" is not writable\");\n            return Optional.empty();\n        }\n\n        return Optional.of(outputParentPath);\n    }\n\n    /**\n     * This constructor allows callers to specify under which contexts they want the options\n     * provided by this template to appear. If left blank, this template will only be applied to the\n     * default context.\n     *\n     * @param contexts\n     *            the parse contexts under which you want the options provided by this template to\n     *            appear\n     */\n    public OutputDirectoryTemplate(final Integer... contexts)\n    {\n        this.contexts = contexts;\n    }\n\n    @Override\n    public void registerManualPageSections(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.addManualPageSection(\"OUTPUT DIRECTORY\", OutputDirectoryTemplate.class\n                .getResourceAsStream(\"OutputDirectoryTemplateSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.registerOptionWithRequiredArgument(OUTPUT_DIRECTORY_OPTION_LONG,\n                OUTPUT_DIRECTORY_OPTION_SHORT,\n                \"Specify an alternate output directory for any output files. If the directory \"\n                        + \"does not exist, it will be created.\",\n                OptionOptionality.OPTIONAL, \"dir\", this.contexts);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/PredicateTemplate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AtlasShellToolsCommandTemplate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.conversion.StringToPredicateConverter;\n\n/**\n * An {@link AtlasShellToolsCommandTemplate} for commands that want to read an input groovy\n * predicate.\n *\n * @author lcram\n */\npublic class PredicateTemplate implements AtlasShellToolsCommandTemplate\n{\n    private static final String PREDICATE_OPTION_LONG = \"predicate\";\n    private static final String PREDICATE_IMPORTS_OPTION_LONG = \"imports\";\n\n    private final Integer[] contexts;\n\n    public static <T> Optional<Predicate<T>> getPredicate(final Class<T> clazz,\n            final List<String> importsAllowList, final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        if (parentCommand.getOptionAndArgumentDelegate().hasOption(PREDICATE_OPTION_LONG))\n        {\n            final String predicateString = parentCommand.getOptionAndArgumentDelegate()\n                    .getOptionArgument(PREDICATE_OPTION_LONG)\n                    .orElseThrow(AtlasShellToolsException::new);\n            List<String> userImports = new ArrayList<>();\n            if (parentCommand.getOptionAndArgumentDelegate()\n                    .hasOption(PREDICATE_IMPORTS_OPTION_LONG))\n            {\n                userImports = StringList\n                        .split(parentCommand.getOptionAndArgumentDelegate()\n                                .getOptionArgument(PREDICATE_IMPORTS_OPTION_LONG)\n                                .orElseThrow(AtlasShellToolsException::new), \",\")\n                        .getUnderlyingList();\n            }\n            final List<String> allImports = new ArrayList<>();\n            allImports.addAll(userImports);\n            allImports.addAll(importsAllowList);\n            return Optional.of(new StringToPredicateConverter<T>()\n                    .withAddedStarImportPackages(allImports).convert(predicateString));\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * This constructor allows callers to specify under which contexts they want the options\n     * provided by this template to appear. If left blank, this template will only be applied to the\n     * default context.\n     *\n     * @param contexts\n     *            the parse contexts under which you want the options provided by this template to\n     *            appear\n     */\n    public PredicateTemplate(final Integer... contexts)\n    {\n        this.contexts = contexts;\n    }\n\n    @Override\n    public void registerManualPageSections(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.addManualPageSection(\"PREDICATE\",\n                ShardingTemplate.class.getResourceAsStream(\"PredicateTemplateSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.registerOptionWithRequiredArgument(PREDICATE_OPTION_LONG,\n                \"A flexible groovy predicate supplied at the command line. See DESCRIPTION and PREDICATE sections for details.\",\n                OptionOptionality.OPTIONAL, \"groovy-code\", this.contexts);\n        parentCommand.registerOptionWithRequiredArgument(PREDICATE_IMPORTS_OPTION_LONG,\n                \"A comma separated list of some additional package imports to include for the predicate option, if present.\",\n                OptionOptionality.OPTIONAL, \"packages\", this.contexts);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/ShardingTemplate.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AtlasShellToolsCommandTemplate;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\n\n/**\n * An {@link AtlasShellToolsCommandTemplate} for commands that want to read an input\n * {@link Sharding}.\n * \n * @author lcram\n */\npublic class ShardingTemplate implements AtlasShellToolsCommandTemplate\n{\n    private static final String SHARDING_OPTION_LONG = \"sharding\";\n\n    private final Integer[] contexts;\n\n    /**\n     * Get a {@link Sharding} object from the user's input option.\n     * \n     * @param parentCommand\n     *            the parent command that controls this template\n     * @return the {@link Sharding} object specified by the user\n     */\n    public static Sharding getSharding(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        return Sharding.forString(parentCommand.getOptionAndArgumentDelegate()\n                .getOptionArgument(SHARDING_OPTION_LONG).orElseThrow(AtlasShellToolsException::new),\n                parentCommand.getFileSystem());\n    }\n\n    /**\n     * This constructor allows callers to specify under which contexts they want the options\n     * provided by this template to appear. If left blank, this template will only be applied to the\n     * default context.\n     *\n     * @param contexts\n     *            the parse contexts under which you want the options provided by this template to\n     *            appear\n     */\n    public ShardingTemplate(final Integer... contexts)\n    {\n        this.contexts = contexts;\n    }\n\n    @Override\n    public void registerManualPageSections(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.addManualPageSection(\"INPUT SHARDING\",\n                ShardingTemplate.class.getResourceAsStream(\"ShardingTemplateSection.txt\"));\n    }\n\n    @Override\n    public void registerOptionsAndArguments(final AbstractAtlasShellToolsCommand parentCommand)\n    {\n        parentCommand.registerOptionWithRequiredArgument(SHARDING_OPTION_LONG,\n                \"The sharding to use, e.g. slippy@9, dynamic@/Users/foo/my-tree.txt, geohash@4, etc.\",\n                OptionOptionality.REQUIRED, \"type@parameter\", this.contexts);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/terminal/TTYAttribute.java",
    "content": "package org.openstreetmap.atlas.utilities.command.terminal;\n\n/**\n * Easy mnemonics for TTY display attributes (ANSI control codes) and their Unicode encodings.\n *\n * @see \"https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters\"\n * @author lcram\n */\npublic enum TTYAttribute\n{\n    BOLD(\"\\u001B[1m\"),\n    FAINT(\"\\u001B[2m\"),\n    ITALIC(\"\\u001B[3m\"),\n    UNDERLINE(\"\\u001B[4m\"),\n    BLINK(\"\\u001B[5m\"),\n    RAPID_BLINK(\"\\u001B[6m\"),\n    REVERSE_VIDEO(\"\\u001B[7m\"),\n    BLACK(\"\\u001B[30m\"),\n    RED(\"\\u001B[31m\"),\n    GREEN(\"\\u001B[32m\"),\n    YELLOW(\"\\u001B[33m\"),\n    BLUE(\"\\u001B[34m\"),\n    MAGENTA(\"\\u001B[35m\"),\n    CYAN(\"\\u001B[36m\"),\n    WHITE(\"\\u001B[037m\"),\n    BACKGROUND_BLACK(\"\\u001B[40m\"),\n    BACKGROUND_RED(\"\\u001B[41m\"),\n    BACKGROUND_GREEN(\"\\u001B[42m\"),\n    BACKGROUND_YELLOW(\"\\u001B[43m\"),\n    BACKGROUND_BLUE(\"\\u001B[44m\"),\n    BACKGROUND_MAGENTA(\"\\u001B[45m\"),\n    BACKGROUND_CYAN(\"\\u001B[46m\"),\n    BACKGROUND_WHITE(\"\\u001B[047m\"),\n    RESET(\"\\u001B[0m\");\n\n    private final String ansiSequence;\n\n    TTYAttribute(final String ansiSequence)\n    {\n        this.ansiSequence = ansiSequence;\n    }\n\n    public String getANSISequence()\n    {\n        return this.ansiSequence;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/command/terminal/TTYStringBuilder.java",
    "content": "package org.openstreetmap.atlas.utilities.command.terminal;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * A simple string building class that allows for optional TTY formatting and output\n * pretty-fication.\n *\n * @author lcram\n */\npublic class TTYStringBuilder\n{\n    public static final int DEFAULT_LEVEL_WIDTH = 4;\n\n    private final StringBuilder builder;\n    private final boolean useColors;\n    private final Deque<Integer> exactIndentWidthStack;\n    private int levelWidth;\n\n    public TTYStringBuilder(final boolean useColors)\n    {\n        this.builder = new StringBuilder();\n        this.useColors = useColors;\n        this.exactIndentWidthStack = new ArrayDeque<>();\n        this.exactIndentWidthStack.push(0);\n        this.levelWidth = DEFAULT_LEVEL_WIDTH;\n    }\n\n    public TTYStringBuilder append(final Object object, final TTYAttribute... attributes)\n    {\n        // Append whitespace for the indent setting\n        for (int i = 0; i < this.exactIndentWidthStack.peek(); i++)\n        {\n            this.builder.append(\" \");\n        }\n\n        if (this.useColors)\n        {\n            for (final TTYAttribute attribute : attributes)\n            {\n                this.builder.append(attribute.getANSISequence());\n            }\n        }\n\n        this.builder.append(String.valueOf(object));\n\n        // If an attribute was supplied, we need to reset the TTY\n        if (this.useColors && attributes.length > 0)\n        {\n            this.builder.append(TTYAttribute.RESET.getANSISequence());\n        }\n\n        return this;\n    }\n\n    public TTYStringBuilder clearIndentationStack()\n    {\n        this.exactIndentWidthStack.clear();\n        this.exactIndentWidthStack.push(0);\n        return this;\n    }\n\n    /**\n     * Append a newline to this builder.\n     *\n     * @return the updated builder\n     */\n    public TTYStringBuilder newline()\n    {\n        this.builder.append(System.getProperty(\"line.separator\"));\n        return this;\n    }\n\n    public TTYStringBuilder popIndentation()\n    {\n        if (this.exactIndentWidthStack.size() == 1)\n        {\n            throw new CoreException(\"Cannot pop default indention off the stack\");\n        }\n        this.exactIndentWidthStack.pop();\n        return this;\n    }\n\n    public TTYStringBuilder pushExactIndentWidth(final int width)\n    {\n        if (width < 0)\n        {\n            throw new CoreException(\"Indent width ({}) must be >= 0\", width);\n        }\n        this.exactIndentWidthStack.push(width);\n        return this;\n    }\n\n    public TTYStringBuilder pushIndentLevel(final int level)\n    {\n        if (level < 0)\n        {\n            throw new CoreException(\"Indent level ({}) must be >= 0\", level);\n        }\n        this.exactIndentWidthStack.push(level * this.levelWidth);\n        return this;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.builder.toString();\n    }\n\n    public TTYStringBuilder withLevelWidth(final int newLevelWidth)\n    {\n        if (newLevelWidth < 0)\n        {\n            throw new CoreException(\"Level width ({}) must be >= 0\", newLevelWidth);\n        }\n        this.levelWidth = newLevelWidth;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/compression/IntegerDictionary.java",
    "content": "package org.openstreetmap.atlas.utilities.compression;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoIntegerStringDictionaryAdapter;\n\n/**\n * Simple dictionary encoding for {@link String}s\n *\n * @author matthieun\n * @author lcram\n * @param <Type>\n *            The type to encode. Typically called word\n */\npublic class IntegerDictionary<Type> implements Serializable, ProtoSerializable\n{\n    private static final long serialVersionUID = -1781411097803512149L;\n\n    public static final String FIELD_WORD_TO_INDEX = \"wordToIndex\";\n    public static final String FIELD_INDEX_TO_WORD = \"indexToWord\";\n    public static final String FIELD_INDEX = \"index\";\n\n    private final Map<Type, Integer> wordToIndex;\n    private final Map<Integer, Type> indexToWord;\n\n    private int index = 0;\n\n    public IntegerDictionary()\n    {\n        this.wordToIndex = new HashMap<>();\n        this.indexToWord = new HashMap<>();\n    }\n\n    public synchronized int add(final Type word)\n    {\n        if (this.wordToIndex.containsKey(word))\n        {\n            return this.wordToIndex.get(word);\n        }\n        this.wordToIndex.put(word, this.index);\n        this.indexToWord.put(this.index, word);\n        return this.index++;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof IntegerDictionary)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            @SuppressWarnings(\"unchecked\")\n            final IntegerDictionary<Type> that = (IntegerDictionary<Type>) other;\n            if (this.size() != that.size())\n            {\n                return false;\n            }\n            if (!this.wordToIndex.equals(that.wordToIndex))\n            {\n                return false;\n            }\n            if (!this.indexToWord.equals(that.indexToWord))\n            {\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /*\n     * TODO the problem here is that if someone tries to get an adapter for an\n     * IntegerDictionary<SomeTypeThatIsNotAString>, this method will return the wrong adapter.\n     * Currently, the ProtoIntegerStringDictionaryAdapter class's serialize() method handles this by\n     * catching a ClassCastException and rethrowing a CoreException with a better message.\n     */\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoIntegerStringDictionaryAdapter();\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final int initialPrime = 31;\n        final int hashSeed = 37;\n\n        int hash = hashSeed * initialPrime + Integer.valueOf(this.size()).hashCode();\n        for (final Type key : this.wordToIndex.keySet())\n        {\n            final Integer value = this.wordToIndex.get(key);\n            final int keyHash = key == null ? 0 : key.hashCode();\n            final int valueHash = value == null ? 0 : value.hashCode();\n            hash = hashSeed * hash + keyHash;\n            hash = hashSeed * hash + valueHash;\n        }\n        for (final Integer key : this.indexToWord.keySet())\n        {\n            final Type value = this.indexToWord.get(key);\n            final int keyHash = key == null ? 0 : key.hashCode();\n            final int valueHash = value == null ? 0 : value.hashCode();\n            hash = hashSeed * hash + keyHash;\n            hash = hashSeed * hash + valueHash;\n        }\n\n        return hash;\n    }\n\n    public int size()\n    {\n        return this.index;\n    }\n\n    public Type word(final int index)\n    {\n        return this.indexToWord.get(index);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/compression/LongDictionary.java",
    "content": "package org.openstreetmap.atlas.utilities.compression;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Simple dictionary encoding for {@link String}s\n *\n * @author matthieun\n * @param <Type>\n *            The type to encode. Typically called word\n */\npublic class LongDictionary<Type> implements Serializable\n{\n    private static final long serialVersionUID = 6060400113166584385L;\n    private final Map<Type, Long> wordToIndex;\n    private final Map<Long, Type> indexToWord;\n\n    private long index = 0;\n\n    public LongDictionary()\n    {\n        this.wordToIndex = new HashMap<>();\n        this.indexToWord = new HashMap<>();\n    }\n\n    public synchronized long add(final Type word)\n    {\n        if (this.wordToIndex.containsKey(word))\n        {\n            return this.wordToIndex.get(word);\n        }\n        this.wordToIndex.put(word, this.index);\n        this.indexToWord.put(this.index, word);\n        return this.index++;\n    }\n\n    public Type word(final long index)\n    {\n        return this.indexToWord.get(index);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/configuration/Configurable.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport java.util.Optional;\n\n/**\n * Configurable wrapper\n *\n * @author brian_l_davis\n */\npublic interface Configurable\n{\n    /**\n     * @param <V>\n     *            property type\n     * @return the current value\n     */\n    <V> V value();\n\n    /**\n     * @param <V>\n     *            property type\n     * @return Optional of the current value, wrapping {@code null}\n     */\n    <V> Optional<V> valueOption();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/configuration/Configuration.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * Configuration interface providing key-value look with support for defaults and transformations.\n *\n * @author brian_l_davis\n */\npublic interface Configuration\n{\n    /**\n     * Returns a set view of all the top level keys in this {@link Configuration}.\n     *\n     * @return the set of top level keys\n     */\n    Set<String> configurationDataKeySet();\n\n    /**\n     * Returns a returns a copy Configuration specific to a keyword with overwritten values\n     *\n     * @param keyword\n     *            keyword string\n     * @return Configuration\n     */\n    Configuration configurationForKeyword(String keyword);\n\n    /**\n     * Returns a {@link Configurable} wrapper around the configured property.\n     *\n     * @param key\n     *            property key\n     * @return a {@link Configurable} wrapper\n     */\n    Configurable get(String key);\n\n    /**\n     * Returns a {@link Configurable} wrapper around the configured property.\n     *\n     * @param key\n     *            property key\n     * @param transform\n     *            applied to the configured property\n     * @param <R>\n     *            configured type\n     * @param <T>\n     *            transformed type\n     * @return a {@link Configurable} wrapper\n     */\n    <R, T> Configurable get(String key, Function<R, T> transform);\n\n    /**\n     * Returns a {@link Configurable} wrapper around the configured property.\n     *\n     * @param key\n     *            property key\n     * @param defaultValue\n     *            value returned if not found in the configuration\n     * @param transform\n     *            applied to the configured property\n     * @param <R>\n     *            configured type\n     * @param <T>\n     *            transformed type\n     * @return a {@link Configurable} wrapper\n     */\n    <R, T> Configurable get(String key, R defaultValue, Function<R, T> transform);\n\n    /**\n     * Returns a {@link Configurable} wrapper around the configured property.\n     *\n     * @param key\n     *            property key\n     * @param defaultValue\n     *            value returned if not found in the configuration\n     * @param <T>\n     *            configured type\n     * @return a {@link Configurable} wrapper\n     */\n    <T> Configurable get(String key, T defaultValue);\n\n    /**\n     * Returns a new configuration with contents starting at the provided key.\n     * <p>\n     * Assuming the initial configuration is:\n     *\n     * <pre>\n     * {@code\n     * {\n     *     \"a\" :\n     *     {\n     *         \"b\" : \"c\"\n     *     }\n     *\n     * }\n     * }\n     * </pre>\n     *\n     * With a key provided as \"a\", the new sub configuration looks like:\n     *\n     * <pre>\n     * {@code\n     * {\n     *     \"b\" : \"c\"\n     * }\n     * }\n     * </pre>\n     *\n     * @param key\n     *            The provided key\n     * @return The sub Configuration if it exists under the key, Optional.empty otherwise.\n     */\n    Optional<Configuration> subConfiguration(String key);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/configuration/ConfigurationDeserializer.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.core.JsonToken;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonMappingException;\n\n/**\n * Basic {@link JsonDeserializer} the builds a key-value maps for quick lookup\n *\n * @author cstaylor\n * @author brian_l_davis\n */\nclass ConfigurationDeserializer extends JsonDeserializer<Map<String, Object>>\n{\n    private static final String INVALID_JSON = \"Invalid JSON format.\";\n    private static final String DOT = \".\";\n\n    @Override\n    public Map<String, Object> deserialize(final JsonParser jacksonParser,\n            final DeserializationContext context) throws IOException\n    {\n        return deserializeObject(jacksonParser, context);\n    }\n\n    private ArrayList<Object> deserializeArray(final JsonParser jacksonParser,\n            final DeserializationContext context) throws IOException\n    {\n        if (jacksonParser.getCurrentToken() != JsonToken.START_ARRAY)\n        {\n            throw new JsonMappingException(jacksonParser, INVALID_JSON);\n        }\n\n        jacksonParser.nextToken();\n\n        final ArrayList<Object> list = new ArrayList<>();\n\n        while (jacksonParser.getCurrentToken() != JsonToken.END_ARRAY)\n        {\n            list.add(deserializeValue(jacksonParser, context));\n            jacksonParser.nextToken();\n        }\n\n        return list;\n    }\n\n    private Map<String, Object> deserializeObject(final JsonParser jacksonParser,\n            final DeserializationContext context) throws IOException\n    {\n        if (jacksonParser.getCurrentToken() != JsonToken.START_OBJECT)\n        {\n            throw new JsonMappingException(jacksonParser, INVALID_JSON);\n        }\n\n        final Map<String, Object> map = new LinkedHashMap<>();\n        while (jacksonParser.nextToken() != JsonToken.END_OBJECT)\n        {\n            final String dotKey = jacksonParser.getCurrentName();\n            final Map<String, Object> locatedMap = locateMap(dotKey, map, jacksonParser);\n\n            final String key;\n            final int lastDot = dotKey.lastIndexOf(DOT);\n            if (lastDot > 0 && lastDot < dotKey.length())\n            {\n                key = dotKey.substring(lastDot + 1);\n            }\n            else\n            {\n                key = dotKey;\n            }\n            // skip key, go to value\n            jacksonParser.nextToken();\n            final Object deserializedValue = deserializeValue(jacksonParser, context);\n            locatedMap.put(key, deserializedValue);\n        }\n\n        return map;\n    }\n\n    private Object deserializeValue(final JsonParser jacksonParser,\n            final DeserializationContext context) throws IOException\n    {\n        switch (jacksonParser.getCurrentToken())\n        {\n            case START_OBJECT:\n                return deserializeObject(jacksonParser, context);\n            case START_ARRAY:\n                return deserializeArray(jacksonParser, context);\n            case VALUE_STRING:\n                return jacksonParser.getText();\n            case VALUE_NUMBER_INT:\n                return jacksonParser.getLongValue();\n            case VALUE_NUMBER_FLOAT:\n                return jacksonParser.getDoubleValue();\n            case VALUE_TRUE:\n                return Boolean.TRUE;\n            case VALUE_FALSE:\n                return Boolean.FALSE;\n            case VALUE_NULL:\n                return null;\n            default:\n                return context.handleUnexpectedToken(Object.class, jacksonParser);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Map<String, Object> locateMap(final String dotKey, final Map<String, Object> map,\n            final JsonParser jacksonParser) throws IOException\n    {\n        final int dotIndex = dotKey.indexOf(DOT);\n        if (dotIndex > 0 && dotIndex < dotKey.length())\n        {\n            final String part = dotKey.substring(0, dotIndex);\n            final String remaining = dotKey.substring(dotIndex + 1);\n            final Object nextMap = map.getOrDefault(part, new LinkedHashMap<>());\n            if (!(nextMap instanceof Map))\n            {\n                throw new JsonMappingException(jacksonParser, INVALID_JSON);\n            }\n            map.put(part, nextMap);\n            return locateMap(remaining, (Map<String, Object>) nextMap, jacksonParser);\n        }\n        return map;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/configuration/ConfigurationReader.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\nimport com.google.common.collect.Lists;\n\n/**\n * Helper class to read a {@link Configuration}\n * \n * @author matthieun\n */\npublic class ConfigurationReader\n{\n    private static final String CONFIGURATION_PATH_NAME_DEFAULT = \"N/A\";\n\n    private final String root;\n\n    public ConfigurationReader(final String root)\n    {\n        this.root = root;\n    }\n\n    public final String configurationKey(final String key)\n    {\n        return this.root.isEmpty() ? key : this.root + \".\" + key;\n    }\n\n    public String configurationValue(final Configuration configuration, final String key)\n    {\n        final String result = configuration\n                .get(configurationKey(key), CONFIGURATION_PATH_NAME_DEFAULT).value();\n        if (CONFIGURATION_PATH_NAME_DEFAULT.equals(result))\n        {\n            throw new CoreException(\"Malformed configuration for {}\", configurationKey(key));\n        }\n        return result;\n    }\n\n    public <U> U configurationValue(final Configuration configuration, final String key,\n            final U defaultValue)\n    {\n        return configuration.get(configurationKey(key), defaultValue).value();\n    }\n\n    public <R, T> T configurationValue(final Configuration configuration,\n            final Function<R, T> defaultValue)\n    {\n        return configuration.get(this.root, defaultValue).value();\n    }\n\n    public List<String> configurationValues(final Configuration configuration, final String key,\n            final List<String> defaultValue)\n    {\n        return configuration.get(configurationKey(key), defaultValue).value();\n    }\n\n    public List<String> configurationValues(final Configuration configuration, final String key)\n    {\n        final List<String> defaults = Lists.newArrayList(CONFIGURATION_PATH_NAME_DEFAULT);\n        final List<String> result = configurationValues(configuration, key, defaults);\n        if (defaults.equals(result))\n        {\n            throw new CoreException(\"Malformed configuration for {}\", configurationKey(key));\n        }\n        return result;\n    }\n\n    public boolean isPresent(final Configuration configuration, final String key)\n    {\n        final Object result = configuration\n                .get(configurationKey(key), CONFIGURATION_PATH_NAME_DEFAULT).value();\n        return !CONFIGURATION_PATH_NAME_DEFAULT.equals(result);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/configuration/ConfiguredFilter.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.converters.WkbMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.WktMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.RegexTaggableFilter;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\nimport org.openstreetmap.atlas.utilities.conversion.HexStringByteArrayConverter;\nimport org.openstreetmap.atlas.utilities.conversion.StringToPredicateConverter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Lists;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\n/**\n * This class reads in a configuration file with a specific schema and creates filters based on the\n * predicates and taggable filter specified in the file. Take a look at water-handlers.json for\n * reference\n *\n * @author matthieun\n */\npublic final class ConfiguredFilter implements Predicate<AtlasEntity>, Serializable\n{\n    public static final String CONFIGURATION_GLOBAL = \"global\";\n    public static final String DEFAULT = \"default\";\n    public static final ConfiguredFilter NO_FILTER = new ConfiguredFilter();\n    public static final String CONFIGURATION_ROOT = CONFIGURATION_GLOBAL + \".filters\";\n\n    /*\n     * JSON constants for the toJson method. We should probably handle this better so that we do not\n     * duplicate String literals.\n     */\n    public static final String TYPE_JSON_PROPERTY_VALUE = \"_filter\";\n    public static final String NAME_JSON_PROPERTY = \"name\";\n    public static final String PREDICATE_JSON_PROPERTY = \"predicate\";\n    public static final String UNSAFE_PREDICATE_JSON_PROPERTY = \"unsafePredicate\";\n    public static final String IMPORTS_JSON_PROPERTY = \"imports\";\n    public static final String TAGGABLE_FILTER_JSON_PROPERTY = \"taggableFilter\";\n    public static final String REGEX_TAGGABLE_FILTER_JSON_PROPERTY = \"regexTaggableFilter\";\n    public static final String TAGGABLE_MATCHER_JSON_PROPERTY = \"taggableMatcher\";\n    public static final String NO_EXPANSION_JSON_PROPERTY = \"noExpansion\";\n\n    private static final long serialVersionUID = 7503301238426719144L;\n    private static final Logger logger = LoggerFactory.getLogger(ConfiguredFilter.class);\n    private static final String CONFIGURATION_PREDICATE_COMMAND = \"predicate.command\";\n    private static final String CONFIGURATION_PREDICATE_UNSAFE_COMMAND = \"predicate.unsafeCommand\";\n    private static final String CONFIGURATION_PREDICATE_IMPORTS = \"predicate.imports\";\n    private static final String CONFIGURATION_TAGGABLE_FILTER = \"taggableFilter\";\n    private static final String CONFIGURATION_REGEX_TAGGABLE_FILTER = \"regexTaggableFilter\";\n    private static final String CONFIGURATION_TAGGABLE_MATCHER = \"taggableMatcher\";\n    private static final String CONFIGURATION_WKT_FILTER = \"geometry.wkt\";\n    private static final String CONFIGURATION_WKB_FILTER = \"geometry.wkb\";\n    private static final String CONFIGURATION_HINT_NO_EXPANSION = \"hint.noExpansion\";\n    private static final WktMultiPolygonConverter WKT_MULTI_POLYGON_CONVERTER = new WktMultiPolygonConverter();\n    private static final WkbMultiPolygonConverter WKB_MULTI_POLYGON_CONVERTER = new WkbMultiPolygonConverter();\n    private static final HexStringByteArrayConverter HEX_STRING_BYTE_ARRAY_CONVERTER = new HexStringByteArrayConverter();\n\n    private final String name;\n    private final String predicate;\n    private final String unsafePredicate;\n    private transient Predicate<AtlasEntity> filter;\n    private final List<String> imports;\n    private final String taggableFilter;\n    private final String regexTaggableFilter;\n    private final String taggableMatcher;\n    private final boolean noExpansion;\n    private final List<MultiPolygon> geometryBasedFilters;\n\n    public static ConfiguredFilter from(final String name, final Configuration configuration)\n    {\n        return from(CONFIGURATION_ROOT, name, configuration);\n    }\n\n    /**\n     * Create a new {@link ConfiguredFilter}.\n     * <p>\n     * For example, in the following json configuration:\n     *\n     * <pre>\n     * {@code\n     * {\n     *     \"my\":\n     *     {\n     *         \"conf\":\n     *         {\n     *             \"filter\":\n     *             {\n     *                 \"predicate\": \"....\",\n     *                 \"geometry.wkb\":\n     *                 [\n     *                     \"...\", \"...\"\n     *                 ],\n     *                 \"taggableFilter\": \"...\",\n     *                 \"regexTaggableFilter\": \"...\",\n     *                 \"taggableMatcher\": \"...\"\n     *             }\n     *         }\n     *     }\n     * }\n     * }\n     * </pre>\n     *\n     * the filter can be accessed using \"my.conf\" as root, and \"filter\" as name.\n     *\n     * @param root\n     *            The root of the configuration hierarchy, where to search for the name of the\n     *            filter.\n     * @param name\n     *            The name of the filter, which is right under the root in the configuration\n     * @param configuration\n     *            The {@link Configuration} containing the configured filter\n     * @return The constructed {@link ConfiguredFilter}\n     */\n    public static ConfiguredFilter from(final String root, final String name,\n            final Configuration configuration)\n    {\n        if (DEFAULT.equals(name))\n        {\n            return getDefaultFilter(root, configuration);\n        }\n        if (!isPresent(root, name, configuration))\n        {\n            logger.warn(\n                    \"Attempted to create ConfiguredFilter called \\\"{}\\\" but it was not found. It will be swapped with default passthrough filter.\",\n                    name);\n            return getDefaultFilter(root, configuration);\n        }\n        return new ConfiguredFilter(root, name, configuration);\n    }\n\n    public static ConfiguredFilter getDefaultFilter(final Configuration configuration)\n    {\n        return getDefaultFilter(CONFIGURATION_ROOT, configuration);\n    }\n\n    public static ConfiguredFilter getDefaultFilter(final String root,\n            final Configuration configuration)\n    {\n        if (ConfiguredFilter.isPresent(root, DEFAULT, configuration))\n        {\n            return new ConfiguredFilter(root, DEFAULT, configuration);\n        }\n        return NO_FILTER;\n    }\n\n    public static boolean isPresent(final String name, final Configuration configuration)\n    {\n        return isPresent(CONFIGURATION_ROOT, name, configuration);\n    }\n\n    public static boolean isPresent(final String root, final String name,\n            final Configuration configuration)\n    {\n        return new ConfigurationReader(root).isPresent(configuration, name);\n    }\n\n    private ConfiguredFilter()\n    {\n        this(CONFIGURATION_ROOT, \"NO_FILTER\", new StandardConfiguration(new StringResource(\"{}\")));\n    }\n\n    private ConfiguredFilter(final String root, final String name,\n            final Configuration configuration)\n    {\n        this.name = name;\n        String readerRoot = \"\";\n        if (root != null && !root.isEmpty())\n        {\n            readerRoot = root + \".\";\n        }\n        final ConfigurationReader reader = new ConfigurationReader(readerRoot + name);\n        this.predicate = reader.configurationValue(configuration, CONFIGURATION_PREDICATE_COMMAND,\n                \"\");\n        this.unsafePredicate = reader.configurationValue(configuration,\n                CONFIGURATION_PREDICATE_UNSAFE_COMMAND, \"\");\n        this.imports = reader.configurationValue(configuration, CONFIGURATION_PREDICATE_IMPORTS,\n                Lists.newArrayList());\n        this.taggableFilter = reader.configurationValue(configuration,\n                CONFIGURATION_TAGGABLE_FILTER, \"\");\n        this.regexTaggableFilter = reader.configurationValue(configuration,\n                CONFIGURATION_REGEX_TAGGABLE_FILTER, \"\");\n        this.taggableMatcher = reader.configurationValue(configuration,\n                CONFIGURATION_TAGGABLE_MATCHER, \"\");\n        this.noExpansion = readBoolean(configuration, reader, CONFIGURATION_HINT_NO_EXPANSION,\n                false);\n        this.geometryBasedFilters = readGeometries(configuration, reader);\n    }\n\n    public List<MultiPolygon> getGeometryBasedFilters()\n    {\n        return new ArrayList<>(this.geometryBasedFilters);\n    }\n\n    public String getName()\n    {\n        return this.name;\n    }\n\n    public boolean isNoExpansion()\n    {\n        return this.noExpansion;\n    }\n\n    @Override\n    public boolean test(final AtlasEntity atlasEntity)\n    {\n        return getFilter().test(atlasEntity);\n    }\n\n    public boolean test(final Taggable taggable)\n    {\n        return TaggableFilter.forDefinition(this.taggableFilter).test(taggable);\n    }\n\n    public JsonObject toJson()\n    {\n        final JsonObject filterObject = new JsonObject();\n        filterObject.addProperty(\"type\", TYPE_JSON_PROPERTY_VALUE);\n        filterObject.addProperty(NAME_JSON_PROPERTY, this.name);\n        if (!this.predicate.isEmpty())\n        {\n            filterObject.addProperty(PREDICATE_JSON_PROPERTY, this.predicate);\n        }\n        if (!this.unsafePredicate.isEmpty())\n        {\n            filterObject.addProperty(UNSAFE_PREDICATE_JSON_PROPERTY, this.unsafePredicate);\n        }\n        final JsonArray importsArray = new JsonArray();\n        if (!this.imports.isEmpty())\n        {\n            for (final String importString : this.imports)\n            {\n                importsArray.add(new JsonPrimitive(importString));\n            }\n            filterObject.add(IMPORTS_JSON_PROPERTY, importsArray);\n        }\n        if (!this.taggableFilter.isEmpty())\n        {\n            filterObject.addProperty(TAGGABLE_FILTER_JSON_PROPERTY, this.taggableFilter); // NOSONAR\n        }\n        if (!this.regexTaggableFilter.isEmpty())\n        {\n            filterObject.addProperty(REGEX_TAGGABLE_FILTER_JSON_PROPERTY, this.regexTaggableFilter); // NOSONAR\n        }\n        if (!this.taggableMatcher.isEmpty())\n        {\n            filterObject.addProperty(TAGGABLE_MATCHER_JSON_PROPERTY, this.taggableMatcher); // NOSONAR\n        }\n        filterObject.addProperty(NO_EXPANSION_JSON_PROPERTY, this.noExpansion);\n\n        return filterObject;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.name;\n    }\n\n    private Predicate<AtlasEntity> geometryPredicate()\n    {\n        if (this.geometryBasedFilters.isEmpty())\n        {\n            return atlasEntity -> true;\n        }\n        else\n        {\n            return atlasEntity ->\n            {\n                for (final MultiPolygon multiPolygon : this.geometryBasedFilters)\n                {\n                    if (atlasEntity.intersects(multiPolygon))\n                    {\n                        return true;\n                    }\n                }\n                return false;\n            };\n        }\n    }\n\n    private Predicate<AtlasEntity> getFilter()\n    {\n        if (this.filter == null)\n        {\n            Predicate<AtlasEntity> localTemporaryPredicate = atlasEntity -> true;\n            final StringToPredicateConverter<AtlasEntity> predicateReader = new StringToPredicateConverter<>();\n            predicateReader.withAddedStarImportPackages(this.imports);\n            if (!this.predicate.isEmpty() && !this.unsafePredicate.isEmpty())\n            {\n                throw new CoreException(\"Cannot specify both 'command' and 'unsafeCommand'\");\n            }\n            if (!this.predicate.isEmpty())\n            {\n                localTemporaryPredicate = predicateReader.convert(this.predicate);\n            }\n            if (!this.unsafePredicate.isEmpty())\n            {\n                localTemporaryPredicate = predicateReader.convertUnsafe(this.unsafePredicate);\n            }\n            final Predicate<AtlasEntity> localPredicate = localTemporaryPredicate;\n            final TaggableFilter localTaggablefilter = TaggableFilter\n                    .forDefinition(this.taggableFilter);\n            final RegexTaggableFilter localRegexTaggableFilter = new RegexTaggableFilter(\n                    this.regexTaggableFilter);\n            final TaggableMatcher localTaggableMatcher = TaggableMatcher.from(this.taggableMatcher);\n            final Predicate<AtlasEntity> geometryPredicate = geometryPredicate();\n            this.filter = atlasEntity -> localPredicate.test(atlasEntity)\n                    && localTaggablefilter.test(atlasEntity) && geometryPredicate.test(atlasEntity)\n                    && localRegexTaggableFilter.test(atlasEntity)\n                    && localTaggableMatcher.test(atlasEntity);\n        }\n        return this.filter;\n    }\n\n    private boolean readBoolean(final Configuration configuration, final ConfigurationReader reader,\n            final String booleanName, final boolean defaultValue)\n    {\n        try\n        {\n            return reader.configurationValue(configuration, booleanName, defaultValue);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Unable to read \\\"{}\\\"\", booleanName, e);\n        }\n    }\n\n    private List<MultiPolygon> readGeometries(final Configuration configuration,\n            final ConfigurationReader reader)\n    {\n        final List<MultiPolygon> result = new ArrayList<>();\n        final String defaultValue = \"N/A\";\n        try\n        {\n            final List<String> values = reader.configurationValues(configuration,\n                    CONFIGURATION_WKT_FILTER, new ArrayList<>());\n            if (!values.isEmpty())\n            {\n                result.addAll(values.stream().map(WKT_MULTI_POLYGON_CONVERTER::backwardConvert)\n                        .collect(Collectors.toList()));\n            }\n        }\n        catch (final Exception e)\n        {\n            final String wktString = reader.configurationValue(configuration,\n                    CONFIGURATION_WKT_FILTER, defaultValue);\n            if (!defaultValue.equals(wktString))\n            {\n                result.add(WKT_MULTI_POLYGON_CONVERTER.backwardConvert(wktString));\n            }\n        }\n        try\n        {\n            final List<String> values = reader.configurationValues(configuration,\n                    CONFIGURATION_WKB_FILTER, new ArrayList<>());\n            if (!values.isEmpty())\n            {\n                result.addAll(values.stream().map(HEX_STRING_BYTE_ARRAY_CONVERTER::convert)\n                        .map(WKB_MULTI_POLYGON_CONVERTER::backwardConvert)\n                        .collect(Collectors.toList()));\n            }\n        }\n        catch (final Exception e)\n        {\n            final String wkbString = reader.configurationValue(configuration,\n                    CONFIGURATION_WKB_FILTER, defaultValue);\n            if (!defaultValue.equals(wkbString))\n            {\n                result.add(WKB_MULTI_POLYGON_CONVERTER\n                        .backwardConvert(HEX_STRING_BYTE_ARRAY_CONVERTER.convert(wkbString)));\n            }\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/configuration/MergedConfiguration.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport static org.openstreetmap.atlas.utilities.collections.Iterables.join;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Utility class used when reading from multiple underlying configurations. Property collisions are\n * handled using a last one wins policy. This enables both layered and partial configuration\n * organization schemes.\n *\n * @author cstaylor\n * @author brian_l_davis\n * @author jklamer\n */\npublic class MergedConfiguration implements Configuration\n{\n    /**\n     * Configurable that calls out to the underlying configuration's Configurables\n     *\n     * @param <R>\n     *            configured type\n     * @param <T>\n     *            transformed type\n     * @author cstaylor\n     */\n    private class MergedConfigurable<R, T> implements Configurable\n    {\n        private final R defaultValue;\n        private final String key;\n        private final Function<R, T> transform;\n\n        MergedConfigurable(final String key, final R defaultValue, final Function<R, T> transform)\n        {\n            this.key = key;\n            this.transform = transform;\n            this.defaultValue = defaultValue;\n        }\n\n        @SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n        @Override\n        public <V> V value()\n        {\n            Object value = MergedConfiguration.this.configurations.stream()\n                    .map(config -> config.get(this.key)).map(Configurable::value)\n                    .filter(Objects::nonNull).findFirst().orElse(this.defaultValue);\n\n            if (value instanceof Map)\n            {\n                final Map mergeMap = new HashMap();\n                MergedConfiguration.this.configurations.stream()\n                        .map(config -> config.get(this.key).value())\n                        .filter(found -> found instanceof Map)\n                        .collect(Collectors.toCollection(LinkedList::new)).descendingIterator()\n                        .forEachRemaining(found -> mergeMap.putAll((Map) found));\n                value = mergeMap;\n            }\n\n            return (V) this.transform.apply((R) value);\n        }\n\n        @Override\n        public <V> Optional<V> valueOption()\n        {\n            return Optional.ofNullable(value());\n        }\n    }\n\n    private final List<Configuration> configurations;\n\n    public MergedConfiguration(final Configuration... configurations)\n    {\n        this(Arrays.asList(configurations));\n    }\n\n    public MergedConfiguration(final List<Configuration> configurations)\n    {\n        this.configurations = Collections.unmodifiableList(configurations);\n    }\n\n    public MergedConfiguration(final Resource first, final Iterable<Resource> configurations)\n    {\n        final LinkedList<Configuration> mergedConfigurations = new LinkedList<>();\n        Iterables.stream(join(first, configurations)).map(StandardConfiguration::new)\n                .forEach(mergedConfigurations::addFirst);\n        this.configurations = Collections.unmodifiableList(mergedConfigurations);\n    }\n\n    public MergedConfiguration(final Resource first, final Resource... configurations)\n    {\n        this(first, Iterables.iterable(configurations));\n    }\n\n    /**\n     * Note that the implementation of {@link Configuration#configurationDataKeySet()} for\n     * {@link MergedConfiguration} will perform a set merge operation on the keysets of the\n     * underlying {@link StandardConfiguration}s. Keep this in mind when using this method.\n     */\n    @Override\n    public Set<String> configurationDataKeySet()\n    {\n        // merge the keysets of the underlying StandardConfigurations\n        final Set<String> keySet = new HashSet<>();\n        this.configurations\n                .forEach(configuration -> keySet.addAll(configuration.configurationDataKeySet()));\n        return keySet;\n    }\n\n    @Override\n    public Configuration configurationForKeyword(final String keyword)\n    {\n        final List<Configuration> configurationsByKeyword = this.configurations.stream()\n                .map(configuration -> configuration.configurationForKeyword(keyword))\n                .collect(Collectors.toList());\n        return Iterables.equals(this.configurations, configurationsByKeyword) ? this\n                : new MergedConfiguration(configurationsByKeyword);\n    }\n\n    @Override\n    public Configurable get(final String key)\n    {\n        return new MergedConfigurable<>(key, null, Function.identity());\n    }\n\n    @Override\n    public <R, T> Configurable get(final String key, final Function<R, T> transform)\n    {\n        return new MergedConfigurable<>(key, null, transform);\n    }\n\n    @Override\n    public <R, T> Configurable get(final String key, final R defaultValue,\n            final Function<R, T> transform)\n    {\n        return new MergedConfigurable<>(key, defaultValue, transform);\n    }\n\n    @Override\n    public <T> Configurable get(final String key, final T defaultValue)\n    {\n        return new MergedConfigurable<>(key, defaultValue, Function.identity());\n    }\n\n    @Override\n    public Optional<Configuration> subConfiguration(final String key)\n    {\n        final Object all = this.get(\"\").value();\n        if (all == null)\n        {\n            return Optional.empty();\n        }\n        final Map<String, Object> map;\n        if (all instanceof Map)\n        {\n            map = (Map<String, Object>) all;\n        }\n        else\n        {\n            map = new HashMap<>();\n            map.put(\"\", all);\n        }\n        final StandardConfiguration standardConfiguration = new StandardConfiguration(\"\", map);\n        return standardConfiguration.subConfiguration(key);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/configuration/StandardConfiguration.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport java.io.ByteArrayInputStream;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.fasterxml.jackson.core.JsonFactory;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport com.fasterxml.jackson.dataformat.yaml.YAMLFactory;\nimport com.fasterxml.jackson.dataformat.yaml.YAMLParser;\n\n/**\n * Standard implementation of the Configuration interface supporting dot-notation key-value lookup.\n *\n * @author cstaylor\n * @author brian_l_davis\n * @author jklamer\n */\npublic class StandardConfiguration implements Configuration\n{\n    /**\n     * Enum for the supported configuration file formats\n     */\n    public enum ConfigurationFormat\n    {\n        JSON,\n        YAML,\n        UNKNOWN\n    }\n\n    /**\n     * Configurable implementation that pulls from the outer class's data table\n     *\n     * @param <R>\n     *            configured type\n     * @param <T>\n     *            transformed type\n     * @author cstaylor\n     * @author brian_l_davis\n     * @author cameron_frenette\n     */\n    private final class StandardConfigurable<R, T> implements Configurable\n    {\n        private final T defaultValue;\n        private final String key;\n        private final Function<R, T> transform;\n\n        private StandardConfigurable(final String key, final R defaultValue,\n                final Function<R, T> transform)\n        {\n            this.key = key;\n            this.transform = transform;\n            this.defaultValue = Optional.ofNullable(defaultValue).map(transform).orElse(null);\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        @Override\n        public <V> V value()\n        {\n            try\n            {\n                final R found = (R) resolve(this.key, StandardConfiguration.this.configurationData);\n                return (V) Optional.ofNullable(found).map(this.transform).orElse(this.defaultValue);\n            }\n            catch (final ClassCastException e)\n            {\n                logger.error(String.format(\"Invalid configuration type for %s\", this.key), e);\n            }\n            return null;\n        }\n\n        @Override\n        public <V> Optional<V> valueOption()\n        {\n            return Optional.ofNullable(value());\n        }\n    }\n\n    // \"override\" is no longer available to use as a configuration key\n    private static final String OVERRIDE_STRING = \"override\";\n    private static final String DOT = \".\";\n    private static final Logger logger = LoggerFactory.getLogger(StandardConfiguration.class);\n    private final Map<String, Object> configurationData;\n    private final String name;\n\n    public StandardConfiguration(final Resource resource)\n    {\n        this(resource, ConfigurationFormat.UNKNOWN);\n    }\n\n    public StandardConfiguration(final Resource resource, final ConfigurationFormat configFormat)\n    {\n        this.name = resource.getName();\n        final byte[] configBytes = resource.readBytesAndClose();\n\n        switch (configFormat)\n        {\n            case JSON:\n                this.configurationData = this.readConfigurationMapFromJSON(configBytes)\n                        .orElseThrow(() -> new CoreException(\"Unable to load JSON configuration.\"));\n                return;\n            case YAML:\n                this.configurationData = this.readConfigurationMapFromYAML(configBytes)\n                        .orElseThrow(() -> new CoreException(\"Unable to load YAML configuration.\"));\n                return;\n            case UNKNOWN:\n            default:\n                // If the config format is unknown, attempt to load the config with each format\n                // until one finds some data\n                final Optional<Map<String, Object>> loadedConfigMap = Stream\n                        .<Supplier<Optional<Map<String, Object>>>> of(\n                                () -> this.readConfigurationMapFromJSON(configBytes),\n                                () -> this.readConfigurationMapFromYAML(configBytes))\n                        .map(Supplier::get).filter(Optional::isPresent).map(Optional::get)\n                        .findFirst();\n\n                this.configurationData = loadedConfigMap.orElseThrow(\n                        () -> new CoreException(\"Unable to load UNKNOWN configuration.\"));\n        }\n    }\n\n    public StandardConfiguration(final String name, final Map<String, Object> configurationData)\n    {\n        this.name = name;\n        this.configurationData = configurationData;\n    }\n\n    @Override\n    public Set<String> configurationDataKeySet()\n    {\n        return new HashSet<>(this.configurationData.keySet());\n    }\n\n    @Override\n    public Configuration configurationForKeyword(final String keyword)\n    {\n        final Optional<Map<String, Object>> overrideDataForKeyword = this\n                .getOverrideDataForKeyword(keyword, this.configurationData);\n        if (overrideDataForKeyword.isPresent())\n        {\n            return new MergedConfiguration(\n                    new StandardConfiguration(this.name, overrideDataForKeyword.get()), this);\n        }\n        return this;\n    }\n\n    @Override\n    public Configurable get(final String key)\n    {\n        return new StandardConfigurable<>(key, null, Function.identity());\n    }\n\n    @Override\n    public <R, T> Configurable get(final String key, final Function<R, T> transform)\n    {\n        return new StandardConfigurable<>(key, null, transform);\n    }\n\n    @Override\n    public Configurable get(final String key, final Object defaultValue)\n    {\n        return new StandardConfigurable<>(key, defaultValue, Function.identity());\n    }\n\n    @Override\n    public <R, T> Configurable get(final String key, final R defaultValue,\n            final Function<R, T> transform)\n    {\n        return new StandardConfigurable<>(key, defaultValue, transform);\n    }\n\n    @Override\n    public Optional<Configuration> subConfiguration(final String key)\n    {\n        if (StringUtils.isEmpty(key))\n        {\n            return Optional.of(this);\n        }\n        final Object result = this.resolve(key, this.configurationData);\n        if (result != null)\n        {\n            final Map<String, Object> subConfigurationData;\n            if (result instanceof Map)\n            {\n                subConfigurationData = (Map<String, Object>) result;\n            }\n            else\n            {\n                subConfigurationData = new HashMap<>();\n                subConfigurationData.put(\"\", result);\n            }\n            return Optional.of(new StandardConfiguration(this.name, subConfigurationData));\n        }\n        else\n        {\n            return Optional.empty();\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.name != null ? this.name : super.toString();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Optional<Map<String, Object>> getOverrideDataForKeyword(final String keyword,\n            final Map<String, Object> currentContext)\n    {\n        final List<String> overrideKeyPrefixList = Arrays.asList(OVERRIDE_STRING, keyword);\n        final String overrideKeyPrefixString = String.join(DOT, overrideKeyPrefixList);\n        final Map<String, Object> overrideData = new HashMap<>();\n        for (final Entry<String, Object> entry : currentContext.entrySet())\n        {\n            final String key = entry.getKey();\n            if (!key.equals(OVERRIDE_STRING))\n            {\n                final String overrideKey = String.join(DOT, overrideKeyPrefixString, key);\n                final Optional<Object> specificOverrideData = Optional\n                        .ofNullable(this.resolve(overrideKey, currentContext));\n                if (specificOverrideData.isPresent())\n                {\n                    overrideData.put(key, specificOverrideData.get());\n                }\n                else\n                {\n                    final Object nextContext = entry.getValue();\n                    if (nextContext instanceof Map)\n                    {\n                        this.getOverrideDataForKeyword(keyword, (Map<String, Object>) nextContext)\n                                .ifPresent(moreOverrideData -> overrideData.put(key,\n                                        moreOverrideData));\n                    }\n                }\n            }\n        }\n        return Optional.of(overrideData).filter(data -> !data.isEmpty());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Optional<Map<String, Object>> readConfigurationMapFromJSON(final byte[] readBytes)\n    {\n        logger.trace(\"Attempting to load configuration as JSON\");\n        try (ByteArrayInputStream read = new ByteArrayInputStream(readBytes))\n        {\n            final ObjectMapper objectMapper = new ObjectMapper();\n            final SimpleModule simpleModule = new SimpleModule();\n            simpleModule.addDeserializer(Map.class, new ConfigurationDeserializer());\n            objectMapper.registerModule(simpleModule);\n            final JsonParser parser = new JsonFactory().createParser(read);\n            final Map<String, Object> readConfig = objectMapper.readValue(parser, Map.class);\n            logger.trace(\"Success! Loaded JSON configuration\");\n            return Optional.of(readConfig);\n        }\n        catch (final Exception jsonReadException)\n        {\n            logger.error(\"Unable to parse config file as JSON\");\n            return Optional.empty();\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Optional<Map<String, Object>> readConfigurationMapFromYAML(final byte[] readBytes)\n    {\n        final ByteArrayInputStream read = new ByteArrayInputStream(readBytes);\n        logger.info(\"Attempting to load configuration as YAML.\");\n        try\n        {\n            final ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());\n            final SimpleModule simpleModule = new SimpleModule();\n            simpleModule.addDeserializer(Map.class, new ConfigurationDeserializer());\n            objectMapper.registerModule(simpleModule);\n            final YAMLParser parser = new YAMLFactory().createParser(read);\n            final Map<String, Object> readConfig = objectMapper.readValue(parser, Map.class);\n            logger.trace(\"Success! Loaded YAML configuration.\");\n            return Optional.of(readConfig);\n        }\n        catch (final Exception yamlReadException)\n        {\n            logger.error(\"Unable to parse config file as YAML\");\n            return Optional.empty();\n        }\n        finally\n        {\n            IOUtils.closeQuietly(read);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Object resolve(final String key, final Map<String, Object> currentContext)\n    {\n        if (StringUtils.isEmpty(key))\n        {\n            return currentContext;\n        }\n        final LinkedList<String> rootParts = new LinkedList<>(Arrays.asList(key.split(\"\\\\.\")));\n        final LinkedList<String> childParts = new LinkedList<>();\n        while (!rootParts.isEmpty())\n        {\n            final String currentKey = String.join(DOT, rootParts);\n            final Object nextItem = currentContext.get(currentKey);\n            if (nextItem instanceof Map)\n            {\n                final String nextKey = String.join(DOT, childParts);\n                return resolve(nextKey, (Map<String, Object>) nextItem);\n            }\n            if (nextItem != null)\n            {\n                return nextItem;\n            }\n            childParts.addFirst(rootParts.removeLast());\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/conversion/Converter.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\nimport java.util.function.Function;\n\n/**\n * Convert from A type to B type\n *\n * @param <A>\n *            The source type\n * @param <B>\n *            The target type\n * @author tony\n */\npublic interface Converter<A, B> extends Function<A, B>\n{\n    @Override\n    default B apply(final A other)\n    {\n        return convert(other);\n    }\n\n    B convert(A object);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/conversion/HexStringByteArrayConverter.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\nimport java.nio.charset.StandardCharsets;\n\n/**\n * Inspired from <a href=\n * \"https://stackoverflow.com/a/140861/1558687\">https://stackoverflow.com/a/140861/1558687</a> and\n * <a href=\"https://stackoverflow.com/a/9855338/1558687\"\n * >https://stackoverflow.com/a/9855338/1558687</a>\n * \n * @author matthieun\n */\npublic class HexStringByteArrayConverter implements TwoWayConverter<String, byte[]>\n{\n    private static final int SHIFT_4 = 4;\n    private static final int SHIFT_16 = 16;\n    private static final int SHIFT_FF = 0xFF;\n    private static final int SHIFT_0F = 0x0F;\n    private static final byte[] HEX_ARRAY = \"0123456789ABCDEF\".getBytes();\n\n    @Override\n    public String backwardConvert(final byte[] bytes)\n    {\n        final byte[] hexChars = new byte[bytes.length * 2];\n        for (int j = 0; j < bytes.length; j++)\n        {\n            final int value = bytes[j] & SHIFT_FF;\n            hexChars[j * 2] = HEX_ARRAY[value >>> SHIFT_4];\n            hexChars[j * 2 + 1] = HEX_ARRAY[value & SHIFT_0F];\n        }\n        return new String(hexChars, StandardCharsets.UTF_8);\n    }\n\n    @Override\n    public byte[] convert(final String value)\n    {\n        final int length = value.length();\n        final byte[] result = new byte[length / 2];\n        for (int i = 0; i < length; i += 2)\n        {\n            result[i / 2] = (byte) ((Character.digit(value.charAt(i), SHIFT_16) << SHIFT_4)\n                    + Character.digit(value.charAt(i + 1), SHIFT_16));\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/conversion/StringConverter.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\n/**\n * Converter from a String to a specified type\n *\n * @author matthieun\n * @param <B>\n *            The target conversion type\n */\npublic interface StringConverter<B> extends Converter<String, B>\n{\n    StringConverter<String> IDENTITY = string -> string;\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/conversion/StringToPredicateConverter.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.codehaus.groovy.control.CompilerConfiguration;\nimport org.codehaus.groovy.control.customizers.ImportCustomizer;\nimport org.codehaus.groovy.control.customizers.SecureASTCustomizer;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport groovy.lang.Binding;\nimport groovy.lang.GroovyClassLoader;\nimport groovy.lang.GroovyCodeSource;\nimport groovy.lang.GroovyShell;\nimport groovy.lang.Script;\n\n/**\n * Convert a boolean expression string to a {@link Predicate}. The converter uses the Groovy\n * interpreter to create a {@link Predicate} from the boolean input expression. The type T is bound\n * to a variable called 'e', and so the expression string should use 'e'. E.g. \"e.getType() ==\n * ItemType.POINT\" (if T is {@link AtlasEntity}) or 'e.equals(\"foo\")' (if T is {@link String}).\n *\n * @author lcram\n * @param <T>\n *            the type of the predicate\n */\npublic class StringToPredicateConverter<T> implements Converter<String, Predicate<T>>\n{\n    private static final Logger logger = LoggerFactory.getLogger(StringToPredicateConverter.class);\n    private static final String SCRIPT = \"%s Predicate predicate = { e -> return (%s); }; return predicate;\";\n    private static final List<String> DEFAULT_IMPORTS = Arrays.asList(\"java.lang\", \"groovy.lang\",\n            \"java.util.function\");\n\n    private final List<String> additionalAllowListPackages;\n\n    public StringToPredicateConverter()\n    {\n        this.additionalAllowListPackages = new ArrayList<>();\n    }\n\n    /**\n     * Convert a {@link String} representing a boolean condition into a {@link Predicate} that\n     * checks for the condition's validity. The binding assumes the variable under consideration is\n     * named 'e'. Multi-statement expressions are supported. For example, to get a predicate that\n     * checks if a given string has the contents \"foo\", one could supply \"e.equals(\\\"foo\\\")\" as an\n     * argument. This would generate a predicate that looks approximately like:\n     * <p>\n     * <code>Predicate predicate = e -&gt; { return (e.equals(\"foo\")); };</code>\n     * </p>\n     *\n     * @param booleanExpressionString\n     *            a boolean expression involving the object 'e' under consideration\n     * @return the {@link Predicate} object\n     */\n    @Override\n    public Predicate<T> convert(final String booleanExpressionString)\n    {\n        final Class<Script> scriptClass = checkExpressionSafety(booleanExpressionString);\n        return element ->\n        {\n            try\n            {\n                final Binding binding = new Binding();\n                binding.setProperty(\"e\", element);\n                final Script script = scriptClass.getDeclaredConstructor(Binding.class)\n                        .newInstance(binding);\n                return (boolean) script.run();\n            }\n            catch (final Exception exception)\n            {\n                throw new CoreException(\"Something went wrong with this predicate \", exception);\n            }\n        };\n    }\n\n    /**\n     * Similar to {@link StringToPredicateConverter#convert(String)}, but uses some hacks to improve\n     * runtime in certain cases. This version is EXTREMELY INSECURE and should never be used. Please\n     * use {@link StringToPredicateConverter#convert(String)} unless you are sure you want unsafe\n     * conversion and understand what security features you are sacrificing. Note also that due to\n     * the limitations of the improvement hacks, this conversion only supports single statement\n     * expressions.\n     *\n     * @param booleanExpressionString\n     *            a boolean expression involving the object 'e' under consideration\n     * @return the {@link Predicate} object\n     */\n    @SuppressWarnings(\"unchecked\")\n    public Predicate<T> convertUnsafe(final String booleanExpressionString)\n    {\n        checkExpressionSafety(booleanExpressionString);\n\n        final Binding binding = new Binding();\n        final GroovyShell shell = new GroovyShell(binding);\n\n        final StringBuilder importsBuilder = new StringBuilder();\n        final List<String> importsAllowList = new ArrayList<>(DEFAULT_IMPORTS);\n        importsAllowList.addAll(this.additionalAllowListPackages);\n        for (final String importPackage : importsAllowList)\n        {\n            importsBuilder.append(\"import \");\n            importsBuilder.append(importPackage);\n            importsBuilder.append(\".*; \");\n        }\n        final String fullScript = String.format(SCRIPT, importsBuilder.toString(),\n                booleanExpressionString);\n        logger.warn(\"Acquiring predicate with unsafe script: {}\", fullScript);\n\n        return (Predicate<T>) shell.evaluate(fullScript);\n    }\n\n    /**\n     * Add some imports to execute before the predicate.\n     *\n     * @param allowList\n     *            the packages to star import.\n     * @return the updated converter\n     */\n    public StringToPredicateConverter<T> withAddedStarImportPackages(final List<String> allowList)\n    {\n        for (final String importString : allowList)\n        {\n            checkImportStringFormat(importString);\n        }\n        this.additionalAllowListPackages.addAll(allowList);\n        return this;\n    }\n\n    /**\n     * Add some imports to execute before the predicate.\n     *\n     * @param allowList\n     *            the packages to star import.\n     * @return the updated converter\n     */\n    public StringToPredicateConverter<T> withAddedStarImportPackages(final String... allowList)\n    {\n        for (final String importString : allowList)\n        {\n            checkImportStringFormat(importString);\n        }\n        this.additionalAllowListPackages.addAll(Arrays.asList(allowList));\n        return this;\n    }\n\n    /**\n     * Clear any previously added imports.\n     *\n     * @return the updated converter\n     */\n    public StringToPredicateConverter<T> withClearedStarImportPackages()\n    {\n        this.additionalAllowListPackages.clear();\n        return this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Class<Script> checkExpressionSafety(final String booleanExpressionString)\n    {\n        final SecureASTCustomizer securityCustomizer = new SecureASTCustomizer();\n        final List<String> importsAllowList = new ArrayList<>(DEFAULT_IMPORTS);\n        importsAllowList.addAll(this.additionalAllowListPackages);\n\n        securityCustomizer.setStarImportsWhitelist(importsAllowList);\n        securityCustomizer.setPackageAllowed(false);\n        securityCustomizer.setMethodDefinitionAllowed(false);\n        securityCustomizer.setIndirectImportCheckEnabled(true);\n\n        final ImportCustomizer importCustomizer = new ImportCustomizer();\n        importCustomizer.addStarImports(importsAllowList.toArray(new String[0]));\n\n        final CompilerConfiguration compilerConfiguration = new CompilerConfiguration();\n        compilerConfiguration.addCompilationCustomizers(securityCustomizer);\n        compilerConfiguration.addCompilationCustomizers(importCustomizer);\n\n        final GroovyCodeSource groovyCodeSource = new GroovyCodeSource(booleanExpressionString,\n                \"ThePredicate\", GroovyShell.DEFAULT_CODE_BASE);\n        groovyCodeSource.setCachable(true);\n        try (GroovyClassLoader groovyClassLoader = new GroovyClassLoader(\n                this.getClass().getClassLoader(), compilerConfiguration))\n        {\n            return groovyClassLoader.parseClass(groovyCodeSource);\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\"Unable to parse {} into a predicate.\", booleanExpressionString,\n                    exception);\n        }\n    }\n\n    /*\n     * Attempt some basic checks to make sure imports are not trying to sneak in extra code.\n     */\n    private void checkImportStringFormat(final String importString)\n    {\n        if (!importString.matches(\"^[a-zA-Z0-9\\\\\\\\.]+$\"))\n        {\n            throw new CoreException(\"Invalid import '{}'\", importString);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/conversion/TagConverter.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * OSM tag conversion. Relies upon splitting upon the following exact string (within brackets here):\n * [\", \"].\n *\n * @author tony\n */\npublic class TagConverter implements StringConverter<Map<String, String>>\n{\n    private static final Logger logger = LoggerFactory.getLogger(TagConverter.class);\n\n    @Override\n    public Map<String, String> convert(final String tagsString)\n    {\n        // A typical string a = \"name\"=>\"St. Thomas Street\", \"oneway\"=>\"yes\", \"highway\"=>\"secondary\"\n        final Map<String, String> tags = new HashMap<>();\n        if (tagsString.length() > 1)\n        {\n            final String[] pairs = tagsString.substring(1, tagsString.length() - 1)\n                    .split(\"\\\", \\\\\\\"\");\n            for (final String pair : pairs)\n            {\n                final String[] values = pair.split(\"=>\");\n\n                final String key = values[0].substring(0, values[0].length() - 1);\n                final String value = values[1].substring(1, values[1].length());\n\n                if (tags.containsKey(key))\n                {\n                    logger.warn(\"Duplicate tags: {}\", tagsString);\n                }\n                else\n                {\n                    tags.put(key, value);\n                }\n            }\n        }\n        return tags;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/conversion/TwoWayConverter.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\n/**\n * Not only can convert from A type to B type, this interface supports backward convert from B to A\n * as well\n *\n * @param <A>\n *            The source type\n * @param <B>\n *            The target type\n * @author tony\n */\npublic interface TwoWayConverter<A, B> extends Converter<A, B>\n{\n    A backwardConvert(B object);\n\n    default Converter<B, A> revert()\n    {\n        return object -> backwardConvert(object);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/conversion/TwoWayStringConverter.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\n/**\n * @author matthieun\n * @param <B>\n *            The type to convert to and from String\n */\npublic interface TwoWayStringConverter<B> extends StringConverter<B>, TwoWayConverter<String, B>\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/direction/EdgeDirectionComparator.java",
    "content": "package org.openstreetmap.atlas.utilities.direction;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.geography.Heading;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveEdge;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\n\n/**\n * A set of utilities that work with {@link Edge} direction and {@link Heading}.\n *\n * @author Sid\n */\npublic final class EdgeDirectionComparator\n{\n    /**\n     * Default {@link Angle} limits are used for {@link Heading} comparison. To keep it simple we\n     * keep the limits contiguous.\n     */\n    public static final Angle DEFAULT_OPPOSITE_DIRECTION_LOWER_LIMIT = Angle.degrees(171);\n    public static final Angle DEFAULT_OPPOSITE_DIRECTION_UPPER_LIMIT = Angle.degrees(-171);\n\n    public static final Angle DEFAULT_SAME_DIRECTION_LOWER_LIMIT = Angle.degrees(-9);\n    public static final Angle DEFAULT_SAME_DIRECTION_UPPER_LIMIT = Angle.degrees(9);\n\n    /*\n     * Limit for the heading difference used for detecting if edges are in opposite direction.\n     */\n    private final Angle oppositeDirectionLowerLimit;\n    private final Angle oppositeDirectionUpperLimit;\n\n    /*\n     * Limit for the heading difference used for detecting if edges are in same direction.\n     */\n    private final Angle sameDirectionLowerLimit;\n    private final Angle sameDirectionUpperLimit;\n\n    public EdgeDirectionComparator()\n    {\n        this(DEFAULT_OPPOSITE_DIRECTION_LOWER_LIMIT, DEFAULT_OPPOSITE_DIRECTION_UPPER_LIMIT,\n                DEFAULT_SAME_DIRECTION_LOWER_LIMIT, DEFAULT_SAME_DIRECTION_UPPER_LIMIT);\n    }\n\n    public EdgeDirectionComparator(final Angle oppositeDirectionLowerLimit,\n            final Angle oppositeDirectionUpperLimit, final Angle sameDirectionLowerLimit,\n            final Angle sameDirectionUpperLimit)\n    {\n        this.oppositeDirectionLowerLimit = oppositeDirectionLowerLimit;\n        this.oppositeDirectionUpperLimit = oppositeDirectionUpperLimit;\n        this.sameDirectionLowerLimit = sameDirectionLowerLimit;\n        this.sameDirectionUpperLimit = sameDirectionUpperLimit;\n    }\n\n    public boolean isOppositeDirection(final AtlasPrimitiveEdge incomingEdge,\n            final AtlasPrimitiveEdge outgoingEdge, final boolean useOverallHeading)\n    {\n        if (incomingEdge.isReversedEdge(outgoingEdge))\n        {\n            return true;\n        }\n        return isOppositeDirection(incomingEdge.getPolyLine(), outgoingEdge.getPolyLine(),\n                useOverallHeading);\n    }\n\n    /*\n     * Detects if given edges are in opposite direction\n     */\n    public boolean isOppositeDirection(final Edge incomingEdge, final Edge outgoingEdge,\n            final boolean useOverallHeading)\n    {\n        if (incomingEdge.isReversedEdge(outgoingEdge))\n        {\n            return true;\n        }\n        return isOppositeDirection(incomingEdge.asPolyLine(), outgoingEdge.asPolyLine(),\n                useOverallHeading);\n    }\n\n    public boolean isOppositeDirection(final Heading incomingEdgeHeading,\n            final Heading outgoingEdgeHeading)\n    {\n        final Angle headingDifference = incomingEdgeHeading.subtract(outgoingEdgeHeading);\n        return headingDifference.isGreaterThanOrEqualTo(this.oppositeDirectionLowerLimit)\n                || headingDifference.isLessThan(this.oppositeDirectionUpperLimit);\n    }\n\n    public boolean isOppositeDirection(final PolyLine incomingEdgePolyline,\n            final PolyLine outgoingEdgePolyline, final boolean useOverallHeading)\n    {\n        return useOverallHeading\n                ? isOppositeDirectionUsingOverallHeading(incomingEdgePolyline, outgoingEdgePolyline)\n                : isOppositeDirectionUsingSegmentHeading(incomingEdgePolyline,\n                        outgoingEdgePolyline);\n    }\n\n    public boolean isOppositeDirectionUsingOverallHeading(final PolyLine incomingEdgePolyline,\n            final PolyLine outgoingEdgePolyline)\n    {\n        boolean oppositeDirection = false;\n        if (Math.min(incomingEdgePolyline.size(), outgoingEdgePolyline.size()) > 1)\n        {\n            final Optional<Heading> incomingSegmentHeading = incomingEdgePolyline.overallHeading();\n            final Optional<Heading> outgoingSegmentHeading = outgoingEdgePolyline.overallHeading();\n            if (incomingSegmentHeading.isPresent() && outgoingSegmentHeading.isPresent())\n            {\n                oppositeDirection = isOppositeDirection(incomingSegmentHeading.get(),\n                        outgoingSegmentHeading.get());\n            }\n        }\n        return oppositeDirection;\n    }\n\n    /*\n     * Detects if given polylines are in opposite direction. Comparing the overall edge Heading\n     * might not work in case of long curvy edges. The last segment of incomingEdgePolyline and\n     * first segment of outgoingEdgePolyline are used to compute the heading difference. Order of\n     * the input polyLine matters in computation of heading.\n     */\n    public boolean isOppositeDirectionUsingSegmentHeading(final PolyLine incomingEdgePolyline,\n            final PolyLine outgoingEdgePolyline)\n    {\n        boolean oppositeDirection = false;\n        if (Math.min(incomingEdgePolyline.size(), outgoingEdgePolyline.size()) > 1)\n        {\n            final List<Segment> incomingSegments = incomingEdgePolyline.segments();\n            final Optional<Heading> incomingSegmentHeading = incomingSegments\n                    .get(incomingSegments.size() - 1).heading();\n            final Optional<Heading> outgoingSegmentHeading = outgoingEdgePolyline.segments().get(0)\n                    .heading();\n            if (incomingSegmentHeading.isPresent() && outgoingSegmentHeading.isPresent())\n            {\n                oppositeDirection = isOppositeDirection(incomingSegmentHeading.get(),\n                        outgoingSegmentHeading.get());\n            }\n        }\n        return oppositeDirection;\n    }\n\n    /*\n     * Detects if edgeA is parallel to any of the given edges\n     */\n    public boolean isParallel(final Edge edgeA, final Collection<Edge> edges,\n            final boolean useOverallHeading)\n    {\n        return edges.stream().anyMatch(edge -> isParallel(edgeA, edge, useOverallHeading));\n    }\n\n    /*\n     * Detects if edgeA is parallel to edgeB. Direction doesn't matter\n     */\n    public boolean isParallel(final Edge edgeA, final Edge edgeB, final boolean useOverallHeading)\n    {\n        return isSameDirection(edgeA, edgeB, useOverallHeading)\n                || isOppositeDirection(edgeA, edgeB, useOverallHeading);\n    }\n\n    /*\n     * Detects if given Atlas Primitive edges are in same direction\n     */\n    public boolean isSameDirection(final AtlasPrimitiveEdge edgeA, final AtlasPrimitiveEdge edgeB,\n            final boolean useOverallHeading)\n    {\n        // If they are reversed edges, then they are in opposite directions\n        if (!edgeA.isReversedEdge(edgeB))\n        {\n            return isSameDirection(edgeA.getPolyLine(), edgeB.getPolyLine(), useOverallHeading);\n        }\n        return false;\n    }\n\n    /*\n     * Detects if given edges are in same direction\n     */\n    public boolean isSameDirection(final Edge edgeA, final Edge edgeB,\n            final boolean useOverallHeading)\n    {\n        // If they are reversed edges, then they are in opposite directions\n        if (!edgeA.isReversedEdge(edgeB))\n        {\n            return isSameDirection(edgeA.asPolyLine(), edgeB.asPolyLine(), useOverallHeading);\n        }\n        return false;\n    }\n\n    /*\n     * Detects if given headings of polylines are in same direction\n     */\n    public boolean isSameDirection(final Heading headingA, final Heading headingB)\n    {\n        final Angle headingDifference = headingB.subtract(headingA);\n        final boolean sameDirection = headingDifference\n                .isGreaterThanOrEqualTo(this.sameDirectionLowerLimit)\n                && headingDifference.isLessThan(this.sameDirectionUpperLimit);\n        return sameDirection;\n    }\n\n    /**\n     * Detects if given {@link PolyLine} are in same direction\n     *\n     * @param polyLineA\n     *            First {@link PolyLine}\n     * @param polyLineB\n     *            Second {@link PolyLine}\n     * @param useOverallHeading\n     *            flag to indicate whether to use overall {@link Heading} or segment {@link Heading}\n     * @return true if {@link PolyLine}s are in same direction\n     */\n    public boolean isSameDirection(final PolyLine polyLineA, final PolyLine polyLineB,\n            final boolean useOverallHeading)\n    {\n        return useOverallHeading ? isSameDirectionUsingOverallHeading(polyLineA, polyLineB)\n                : isSameDirectionUsingSegmentHeading(polyLineA, polyLineB);\n    }\n\n    /*\n     * Detects if given polylines are in same direction using overall heading\n     */\n    public boolean isSameDirectionUsingOverallHeading(final PolyLine polyLineA,\n            final PolyLine polyLineB)\n    {\n        boolean sameDirection = false;\n        if (!polyLineA.isPoint() && !polyLineB.isPoint())\n        {\n            final Optional<Heading> headingA = polyLineA.overallHeading();\n            final Optional<Heading> headingB = polyLineB.overallHeading();\n            if (headingA.isPresent() && headingB.isPresent())\n            {\n                sameDirection = isSameDirection(headingA.get(), headingB.get());\n            }\n        }\n        return sameDirection;\n    }\n\n    /*\n     * Detects if given polylines are in same direction using segment heading\n     */\n    public boolean isSameDirectionUsingSegmentHeading(final PolyLine incomingEdgePolyline,\n            final PolyLine outgoingEdgePolyline)\n    {\n        boolean sameDirection = false;\n        if (!incomingEdgePolyline.isPoint() && !outgoingEdgePolyline.isPoint())\n        {\n            final Optional<Heading> incomingSegmentHeading = incomingEdgePolyline.segments()\n                    .get(incomingEdgePolyline.segments().size() - 1).heading();\n            final Optional<Heading> outgoingSegmentHeading = outgoingEdgePolyline.segments().get(0)\n                    .heading();\n            if (incomingSegmentHeading.isPresent() && outgoingSegmentHeading.isPresent())\n            {\n                sameDirection = isSameDirection(incomingSegmentHeading.get(),\n                        outgoingSegmentHeading.get());\n            }\n        }\n        return sameDirection;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/filters/AtlasEntityPolygonsFilter.java",
    "content": "package org.openstreetmap.atlas.utilities.filters;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.converters.PolygonStringFormat;\nimport org.openstreetmap.atlas.geography.index.RTree;\nimport org.openstreetmap.atlas.utilities.configuration.Configuration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Configurable {@link AtlasEntity} filter that passes through anything that intersects with\n * includePolygons polygons or anything that doesn't intersect with exclude polygons. Exclude\n * polygons are looked at only if no includePolygons polygons exist. Include takes precedence and\n * polygons intersecting with previously read polygons are dropped with a warning.\n *\n * @author jklamer\n */\npublic final class AtlasEntityPolygonsFilter implements Predicate<AtlasEntity>, Serializable\n{\n    /**\n     * The filter Type, either {@link Type#INCLUDE} or {@link Type#EXCLUDE}. Used for filter\n     * construction.\n     */\n    public enum Type\n    {\n        INCLUDE,\n        EXCLUDE;\n\n        public AtlasEntityPolygonsFilter geometricSurfaces(\n                final Collection<GeometricSurface> geometricSurfaces)\n        {\n            return new AtlasEntityPolygonsFilter(this, geometricSurfaces);\n        }\n\n        public AtlasEntityPolygonsFilter geometricSurfaces(\n                final IntersectionPolicy intersectionPolicy,\n                final Collection<GeometricSurface> geometricSurfaces)\n        {\n            return new AtlasEntityPolygonsFilter(this, intersectionPolicy, geometricSurfaces);\n        }\n\n        public AtlasEntityPolygonsFilter multiPolygons(final Collection<MultiPolygon> multiPolygons)\n        {\n            return new AtlasEntityPolygonsFilter(this, multiPolygons);\n        }\n\n        public AtlasEntityPolygonsFilter multiPolygons(final IntersectionPolicy intersectionPolicy,\n                final Collection<MultiPolygon> multiPolygons)\n        {\n            return new AtlasEntityPolygonsFilter(this, intersectionPolicy, multiPolygons);\n        }\n\n        public AtlasEntityPolygonsFilter polygons(final Collection<Polygon> polygons)\n        {\n            return new AtlasEntityPolygonsFilter(this, polygons);\n        }\n\n        public AtlasEntityPolygonsFilter polygons(final IntersectionPolicy intersectionPolicy,\n                final Collection<Polygon> polygons)\n        {\n            return new AtlasEntityPolygonsFilter(this, intersectionPolicy, polygons);\n        }\n\n        public AtlasEntityPolygonsFilter polygonsAndMultiPolygons(\n                final Collection<Polygon> polygons, final Collection<MultiPolygon> multiPolygons)\n        {\n            return new AtlasEntityPolygonsFilter(this,\n                    createSurfaceCollection(polygons, multiPolygons));\n        }\n\n        public AtlasEntityPolygonsFilter polygonsAndMultiPolygons(\n                final IntersectionPolicy intersectionPolicy, final Collection<Polygon> polygons,\n                final Collection<MultiPolygon> multiPolygons)\n        {\n            return new AtlasEntityPolygonsFilter(this, intersectionPolicy,\n                    createSurfaceCollection(polygons, multiPolygons));\n        }\n    }\n\n    public static final String EXCLUDED_MULTIPOLYGONS_KEY = \"filter.multipolygons.exclude\";\n    public static final String EXCLUDED_POLYGONS_KEY = \"filter.polygons.exclude\";\n    public static final String INCLUDED_MULTIPOLYGONS_KEY = \"filter.multipolygons.include\";\n    public static final String INCLUDED_POLYGONS_KEY = \"filter.polygons.include\";\n    private static final Logger logger = LoggerFactory.getLogger(AtlasEntityPolygonsFilter.class);\n    private static final long serialVersionUID = -3474748398986569205L;\n    private final Type filterType;\n    private RTree<GeometricSurface> geometricSurfaces = new RTree<>();\n\n    private final IntersectionPolicy intersectionPolicy;\n\n    public static Collection<GeometricSurface> createSurfaceCollection(\n            final Collection<? extends GeometricSurface> collection1,\n            final Collection<? extends GeometricSurface> collection2)\n    {\n        final ArrayList<GeometricSurface> returnCollection = new ArrayList<>();\n        returnCollection.addAll(collection1);\n        returnCollection.addAll(collection2);\n        return returnCollection;\n    }\n\n    public static AtlasEntityPolygonsFilter forConfiguration(final Configuration configuration)\n    {\n        return forConfiguration(configuration, IntersectionPolicy.DEFAULT_INTERSECTION_POLICY);\n    }\n\n    public static AtlasEntityPolygonsFilter forConfiguration(final Configuration configuration,\n            final IntersectionPolicy intersectionPolicy)\n    {\n        return forConfigurationValues(configuration.get(INCLUDED_POLYGONS_KEY).value(),\n                configuration.get(INCLUDED_MULTIPOLYGONS_KEY).value(),\n                configuration.get(EXCLUDED_POLYGONS_KEY).value(),\n                configuration.get(EXCLUDED_MULTIPOLYGONS_KEY).value(), intersectionPolicy);\n    }\n\n    public static AtlasEntityPolygonsFilter forConfigurationValues(\n            final Map<String, List<String>> includePolygonMap,\n            final Map<String, List<String>> includeMultiPolygonMap,\n            final Map<String, List<String>> excludePolygonMap,\n            final Map<String, List<String>> excludeMultiPolygonMap)\n    {\n        return forConfigurationValues(includePolygonMap, includeMultiPolygonMap, excludePolygonMap,\n                excludeMultiPolygonMap, IntersectionPolicy.DEFAULT_INTERSECTION_POLICY);\n    }\n\n    public static AtlasEntityPolygonsFilter forConfigurationValues(\n            final Map<String, List<String>> includePolygonMap,\n            final Map<String, List<String>> includeMultiPolygonMap,\n            final Map<String, List<String>> excludePolygonMap,\n            final Map<String, List<String>> excludeMultiPolygonMap,\n            final IntersectionPolicy intersectionPolicy)\n    {\n        final List<Polygon> includePolygons;\n        final List<Polygon> excludePolygons;\n        final List<MultiPolygon> includeMultiPolygons;\n        final List<MultiPolygon> excludeMultiPolygons;\n\n        includePolygons = includePolygonMap != null ? getPolygonsFromFormatMap(includePolygonMap)\n                : Collections.emptyList();\n        includeMultiPolygons = includeMultiPolygonMap != null\n                ? getMultiPolygonsFromFormatMap(includeMultiPolygonMap)\n                : Collections.emptyList();\n        excludePolygons = excludePolygonMap != null ? getPolygonsFromFormatMap(excludePolygonMap)\n                : Collections.emptyList();\n        excludeMultiPolygons = excludeMultiPolygonMap != null\n                ? getMultiPolygonsFromFormatMap(excludeMultiPolygonMap)\n                : Collections.emptyList();\n\n        if (!includePolygons.isEmpty() || !includeMultiPolygons.isEmpty())\n        {\n            if (!excludePolygons.isEmpty() || !excludeMultiPolygons.isEmpty())\n            {\n                logger.warn(\n                        \"Ignoring exclude polygons and multipolygons passed through configuration\");\n            }\n            return Type.INCLUDE.polygonsAndMultiPolygons(intersectionPolicy, includePolygons,\n                    includeMultiPolygons);\n        }\n        else\n        {\n            return Type.EXCLUDE.polygonsAndMultiPolygons(intersectionPolicy, excludePolygons,\n                    excludeMultiPolygons);\n        }\n    }\n\n    private static List<MultiPolygon> getMultiPolygonsFromFormatMap(\n            final Map<String, List<String>> multiPolygonLists)\n    {\n        if (multiPolygonLists.isEmpty())\n        {\n            return Collections.emptyList();\n        }\n        return multiPolygonLists.entrySet().stream()\n                .flatMap(formatAndMultiPolygonStrings -> formatAndMultiPolygonStrings.getValue()\n                        .stream()\n                        .map(PolygonStringFormat\n                                .getEnumForFormat(formatAndMultiPolygonStrings.getKey())\n                                .getMultiPolygonConverter())\n                        .filter(Optional::isPresent).map(Optional::get).flatMap(List::stream))\n                .collect(Collectors.toList());\n    }\n\n    private static List<Polygon> getPolygonsFromFormatMap(\n            final Map<String, List<String>> polygonLists)\n    {\n        if (polygonLists.isEmpty())\n        {\n            return Collections.emptyList();\n        }\n        return polygonLists.entrySet().stream()\n                .flatMap(formatAndPolygonStrings -> formatAndPolygonStrings.getValue().stream()\n                        .map(PolygonStringFormat.getEnumForFormat(formatAndPolygonStrings.getKey())\n                                .getPolygonConverter())\n                        .filter(Optional::isPresent).map(Optional::get).flatMap(List::stream))\n                .collect(Collectors.toList());\n    }\n\n    private AtlasEntityPolygonsFilter(final Type filterType,\n            final Collection<? extends GeometricSurface> geometricSurfaces)\n    {\n        this(filterType, IntersectionPolicy.DEFAULT_INTERSECTION_POLICY, geometricSurfaces);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private AtlasEntityPolygonsFilter(final Type filterType,\n            final IntersectionPolicy intersectionPolicy,\n            final Collection<? extends GeometricSurface> geometricSurfaces)\n    {\n        this.filterType = filterType;\n        this.intersectionPolicy = intersectionPolicy;\n        this.geometricSurfaces = RTree\n                .forLocated(geometricSurfaces == null ? Collections.EMPTY_SET : geometricSurfaces);\n        // initialize underlying STR tree\n        this.geometricSurfaces.get(Rectangle.MAXIMUM);\n    }\n\n    @Override\n    public boolean test(final AtlasEntity object)\n    {\n        return noSurfaces().or(isIncluded()).or(isNotExcluded()).test(object);\n    }\n\n    private Predicate<AtlasEntity> isIncluded()\n    {\n        return entity -> this.filterType == Type.INCLUDE && this.geometricSurfaces\n                .get(entity.bounds()).stream().anyMatch(geometricSurface -> this.intersectionPolicy\n                        .geometricSurfaceEntityIntersecting(geometricSurface, entity));\n    }\n\n    private Predicate<AtlasEntity> isNotExcluded()\n    {\n        return entity -> this.filterType == Type.EXCLUDE && this.geometricSurfaces\n                .get(entity.bounds()).stream().noneMatch(geometricSurface -> this.intersectionPolicy\n                        .geometricSurfaceEntityIntersecting(geometricSurface, entity));\n    }\n\n    private Predicate<AtlasEntity> noSurfaces()\n    {\n        return entity -> this.geometricSurfaces.isEmpty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/filters/IntersectionPolicy.java",
    "content": "package org.openstreetmap.atlas.utilities.filters;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\n\n/**\n * Interface to be implemented if custom intersection logic is required.\n *\n * @author jklamer\n */\npublic interface IntersectionPolicy extends Serializable\n{\n    /**\n     * A case by case common sense implementation.\n     */\n    IntersectionPolicy DEFAULT_INTERSECTION_POLICY = new IntersectionPolicy()\n    {\n        private static final long serialVersionUID = -7852178753369272322L;\n\n        @Override\n        public boolean geometricSurfaceEntityIntersecting(final GeometricSurface geometricSurface,\n                final AtlasEntity entity)\n        {\n            return entity.intersects(geometricSurface);\n        }\n\n        @Override\n        public boolean multiPolygonEntityIntersecting(final MultiPolygon multiPolygon,\n                final AtlasEntity entity)\n        {\n            if (entity instanceof LineItem)\n            {\n                return multiPolygon.overlaps(((LineItem) entity).asPolyLine());\n            }\n            if (entity instanceof LocationItem)\n            {\n                return multiPolygon\n                        .fullyGeometricallyEncloses(((LocationItem) entity).getLocation());\n            }\n            if (entity instanceof Area)\n            {\n                return multiPolygon.overlaps(((Area) entity).asPolygon());\n            }\n            if (entity instanceof Relation)\n            {\n                return ((Relation) entity).members().stream().map(RelationMember::getEntity)\n                        .anyMatch(relationEntity -> this\n                                .multiPolygonEntityIntersecting(multiPolygon, relationEntity));\n            }\n            else\n            {\n                return false;\n            }\n        }\n\n        @Override\n        public boolean polygonEntityIntersecting(final Polygon polygon, final AtlasEntity entity)\n        {\n            return entity.intersects(polygon);\n        }\n    };\n\n    default boolean geometricSurfaceEntityIntersecting(final GeometricSurface geometricSurface,\n            final AtlasEntity entity)\n    {\n        return false;\n    }\n\n    default boolean multiPolygonEntityIntersecting(final MultiPolygon multiPolygon,\n            final AtlasEntity entity)\n    {\n        return false;\n    }\n\n    default boolean polygonEntityIntersecting(final Polygon polygon, final AtlasEntity entity)\n    {\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/function/QuaternaryFunction.java",
    "content": "package org.openstreetmap.atlas.utilities.function;\n\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * Represents a function that accepts four arguments and produces a result. This is the four-arity\n * specialization of {@link Function}.\n *\n * @param <S>\n *            the type of the first argument to the function\n * @param <T>\n *            the type of the second argument to the function\n * @param <U>\n *            the type of the third argument to the function\n * @param <V>\n *            the type of the fourth argument to the function\n * @param <R>\n *            the type of the result of the function\n * @author lcram\n */\n@FunctionalInterface\npublic interface QuaternaryFunction<S, T, U, V, R>\n{\n    /**\n     * Returns a composed function that first applies this function to its input, and then applies\n     * the {@code after} function to the result. If evaluation of either function throws an\n     * exception, it is relayed to the caller of the composed function.\n     *\n     * @param <W>\n     *            the type of output of the {@code after} function, and of the composed function\n     * @param after\n     *            the function to apply after this function is applied\n     * @return a composed function that first applies this function and then applies the\n     *         {@code after} function\n     * @throws NullPointerException\n     *             if after is null\n     */\n    default <W> QuaternaryFunction<S, T, U, V, W> andThen(\n            final Function<? super R, ? extends W> after)\n    {\n        Objects.requireNonNull(after);\n        return (final S s, final T t, final U u, final V v) -> after.apply(apply(s, t, u, v));\n    }\n\n    /**\n     * Applies this function to the given arguments.\n     *\n     * @param s\n     *            the first function argument\n     * @param t\n     *            the second function argument\n     * @param u\n     *            the third function argument\n     * @param v\n     *            the fourth function argument\n     * @return the function result\n     */\n    R apply(S s, T t, U u, V v);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/function/QuaternaryOperator.java",
    "content": "package org.openstreetmap.atlas.utilities.function;\n\n/**\n * Represents an operation upon four operands of the same type, producing a result of the same type\n * as the operands. This is a specialization of {@link QuaternaryFunction} for the case where the\n * operands and the result are all of the same type.\n *\n * @param <T>\n *            the type of the operands and result of the operator\n * @author lcram\n */\n@FunctionalInterface\npublic interface QuaternaryOperator<T> extends QuaternaryFunction<T, T, T, T, T>\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/function/SenaryFunction.java",
    "content": "package org.openstreetmap.atlas.utilities.function;\n\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * Represents a function that accepts six arguments and produces a result. This is the six-arity\n * specialization of {@link Function}.\n *\n * @param <P>\n *            the type of the first argument to the function\n * @param <Q>\n *            the type of the second argument to the function\n * @param <S>\n *            the type of the third argument to the function\n * @param <T>\n *            the type of the fourth argument to the function\n * @param <U>\n *            the type of the fifth argument to the function\n * @param <V>\n *            the type of the sixth argument to the function\n * @param <R>\n *            the type of the result of the function\n * @author lcram\n */\n@FunctionalInterface\npublic interface SenaryFunction<P, Q, S, T, U, V, R>\n{\n    /**\n     * Returns a composed function that first applies this function to its input, and then applies\n     * the {@code after} function to the result. If evaluation of either function throws an\n     * exception, it is relayed to the caller of the composed function.\n     *\n     * @param <W>\n     *            the type of output of the {@code after} function, and of the composed function\n     * @param after\n     *            the function to apply after this function is applied\n     * @return a composed function that first applies this function and then applies the\n     *         {@code after} function\n     * @throws NullPointerException\n     *             if after is null\n     */\n    default <W> SenaryFunction<P, Q, S, T, U, V, W> andThen(\n            final Function<? super R, ? extends W> after)\n    {\n        Objects.requireNonNull(after);\n        return (final P p, final Q q, final S s, final T t, final U u, final V v) -> after\n                .apply(apply(p, q, s, t, u, v));\n    }\n\n    /**\n     * Applies this function to the given arguments.\n     *\n     * @param p\n     *            the first function argument\n     * @param q\n     *            the second function argument\n     * @param s\n     *            the third function argument\n     * @param t\n     *            the fourth function argument\n     * @param u\n     *            the fifth function argument\n     * @param v\n     *            the sixth function argument\n     * @return the function result\n     */\n    R apply(P p, Q q, S s, T t, U u, V v);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/function/SenaryOperator.java",
    "content": "package org.openstreetmap.atlas.utilities.function;\n\n/**\n * Represents an operation upon six operands of the same type, producing a result of the same type\n * as the operands. This is a specialization of {@link SenaryFunction} for the case where the\n * operands and the result are all of the same type.\n *\n * @param <T>\n *            the type of the operands and result of the operator\n * @author lcram\n */\n@FunctionalInterface\npublic interface SenaryOperator<T> extends SenaryFunction<T, T, T, T, T, T, T>\n{\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/function/TernaryConsumer.java",
    "content": "package org.openstreetmap.atlas.utilities.function;\n\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * Represents an operation that accepts three input arguments and returns no result. This is the\n * three-arity specialization of {@link Consumer}. Unlike most other functional interfaces,\n * {@link TernaryConsumer} is expected to operate via side-effects.\n *\n * @param <T>\n *            the type of the first argument to the operation\n * @param <U>\n *            the type of the second argument to the operation\n * @param <V>\n *            the type of the third argument to the operation\n * @author lcram\n */\n@FunctionalInterface\npublic interface TernaryConsumer<T, U, V>\n{\n\n    /**\n     * Performs this operation on the given arguments.\n     *\n     * @param t\n     *            the first input argument\n     * @param u\n     *            the second input argument\n     * @param v\n     *            the third input argument\n     */\n    void accept(T t, U u, V v);\n\n    /**\n     * Returns a composed {@link TernaryConsumer} that performs, in sequence, this operation\n     * followed by the {@code after} operation. If performing either operation throws an exception,\n     * it is relayed to the caller of the composed operation. If performing this operation throws an\n     * exception, the {@code after} operation will not be performed.\n     *\n     * @param after\n     *            the operation to perform after this operation\n     * @return a composed {@link TernaryConsumer} that performs in sequence this operation followed\n     *         by the {@code after} operation\n     * @throws NullPointerException\n     *             if {@code after} is null\n     */\n    default TernaryConsumer<T, U, V> andThen(\n            final TernaryConsumer<? super T, ? super U, ? super V> after)\n    {\n        Objects.requireNonNull(after);\n        return (l, c, r) ->\n        {\n            accept(l, c, r);\n            after.accept(l, c, r);\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/function/TernaryFunction.java",
    "content": "package org.openstreetmap.atlas.utilities.function;\n\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * Represents a function that accepts three arguments and produces a result. This is the three-arity\n * specialization of {@link Function}.\n *\n * @param <S>\n *            the type of the first argument to the function\n * @param <T>\n *            the type of the second argument to the function\n * @param <U>\n *            the type of the third argument to the function\n * @param <R>\n *            the type of the result of the function\n * @author lcram\n */\n@FunctionalInterface\npublic interface TernaryFunction<S, T, U, R>\n{\n    /**\n     * Returns a composed function that first applies this function to its input, and then applies\n     * the {@code after} function to the result. If evaluation of either function throws an\n     * exception, it is relayed to the caller of the composed function.\n     *\n     * @param <W>\n     *            the type of output of the {@code after} function, and of the composed function\n     * @param after\n     *            the function to apply after this function is applied\n     * @return a composed function that first applies this function and then applies the\n     *         {@code after} function\n     * @throws NullPointerException\n     *             if after is null\n     */\n    default <W> TernaryFunction<S, T, U, W> andThen(final Function<? super R, ? extends W> after)\n    {\n        Objects.requireNonNull(after);\n        return (final S s, final T t, final U u) -> after.apply(apply(s, t, u));\n    }\n\n    /**\n     * Applies this function to the given arguments.\n     *\n     * @param s\n     *            the first function argument\n     * @param t\n     *            the second function argument\n     * @param u\n     *            the third function argument\n     * @return the function result\n     */\n    R apply(S s, T t, U u);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/function/TernaryOperator.java",
    "content": "package org.openstreetmap.atlas.utilities.function;\n\n/**\n * Represents an operation upon three operands of the same type, producing a result of the same type\n * as the operands. This is a specialization of {@link TernaryFunction} for the case where the\n * operands and the result are all of the same type.\n *\n * @param <T>\n *            the type of the operands and result of the operator\n * @author lcram\n */\n@FunctionalInterface\npublic interface TernaryOperator<T> extends TernaryFunction<T, T, T, T>\n{\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/graphs/DirectedAcyclicGraph.java",
    "content": "package org.openstreetmap.atlas.utilities.graphs;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Deque;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.Stack;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.maps.LinkedMultiMap;\n\n/**\n * A directed acyclic graph. See http://en.wikipedia.org/wiki/Directed_acyclic_graph\n *\n * @param <V>\n *            The objects used in the graph\n * @author cuthbertm\n * @author mgostintsev\n */\npublic class DirectedAcyclicGraph<V> implements Serializable\n{\n    private static final long serialVersionUID = -1980678458375645486L;\n\n    private final LinkedMultiMap<V, V> inMap = new LinkedMultiMap<>();\n    private final LinkedMultiMap<V, V> outMap = new LinkedMultiMap<>();\n\n    public boolean addEdge(final V origin, final V target)\n    {\n        if (origin == null || target == null)\n        {\n            throw new CoreException(\n                    \"Origin and Target for directed Acyclic graph must not be null\");\n        }\n\n        if (hasPath(target, origin))\n        {\n            return false;\n        }\n\n        this.outMap.add(origin, target);\n        this.outMap.add(target, null);\n        this.inMap.add(target, origin);\n        this.inMap.add(origin, null);\n        return true;\n    }\n\n    public void addVertex(final V vertex)\n    {\n        if (vertex == null)\n        {\n            throw new CoreException(\"Cannot add a null vertex to the Directed Acyclic Graph.\");\n        }\n\n        this.outMap.put(vertex, null);\n        this.inMap.put(vertex, null);\n    }\n\n    public boolean contains(final V vertex)\n    {\n        return this.outMap.containsKey(vertex) || this.inMap.containsKey(vertex);\n    }\n\n    public Set<V> getChildren(final V parent)\n    {\n        return Collections.unmodifiableSet(this.outMap.get(parent));\n    }\n\n    public int getDeepestLevel(final V vertex)\n    {\n        return getDeepestLevel(vertex, 1);\n    }\n\n    public Set<V> getParents(final V child)\n    {\n        return Collections.unmodifiableSet(this.inMap.get(child));\n    }\n\n    public Set<V> getSinks()\n    {\n        return getZeroEdgeVertices(this.outMap);\n    }\n\n    public Set<V> getSources()\n    {\n        return getZeroEdgeVertices(this.inMap);\n    }\n\n    /**\n     * A DFS topological sort\n     *\n     * @return a topologically sorted stack\n     */\n    public Stack<V> getTopologicalSortedList()\n    {\n        final Stack<V> stack = new Stack<>();\n        final Set<V> visited = new HashSet<>();\n        this.inMap.forEach((inVertex, outVertex) ->\n        {\n            if (!visited.contains(inVertex))\n            {\n                topologicalSort(inVertex, visited, stack);\n            }\n        });\n\n        return stack;\n    }\n\n    public boolean hasPath(final V start, final V end)\n    {\n        if (start == end)\n        {\n            return true;\n        }\n\n        final Set<V> children = this.outMap.get(start);\n        final Iterator<V> iterator = children.iterator();\n        while (iterator.hasNext())\n        {\n            if (hasPath(iterator.next(), end))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public boolean isSink(final V vertex)\n    {\n        return this.outMap.get(vertex).isEmpty();\n    }\n\n    public boolean isSource(final V vertex)\n    {\n        return this.inMap.get(vertex).isEmpty();\n    }\n\n    /**\n     * @return The ordered groups of vertices that all belong to the same priority level within the\n     *         DAG\n     */\n    // NOSONAR: Cognitive complexity 16 is ok.\n    public List<Set<V>> processGroups() // NOSONAR\n    {\n        final Deque<Set<V>> stack = new LinkedList<>();\n        stack.push(new HashSet<>(getSinks()));\n        final Set<V> added = new HashSet<>(getSinks());\n        final Set<V> sourcesNotAdded = getSources().stream().filter(value -> !added.contains(value))\n                .collect(Collectors.toSet());\n        while (!sourcesNotAdded.isEmpty())\n        {\n            final Set<V> potentialCandidates = new HashSet<>();\n            for (final V alreadyAdded : added)\n            {\n                for (final V parent : getParents(alreadyAdded))\n                {\n                    if (!added.contains(parent))\n                    {\n                        potentialCandidates.add(parent);\n                    }\n                }\n            }\n            final Set<V> candidates = new HashSet<>();\n            for (final V candidate : potentialCandidates)\n            {\n                if (added.containsAll(getChildren(candidate)))\n                {\n                    candidates.add(candidate);\n                    // Hit or miss, hit only at the end of the processing\n                    sourcesNotAdded.remove(candidate);\n                }\n            }\n            stack.push(candidates);\n            added.addAll(candidates);\n        }\n        final List<Set<V>> result = new ArrayList<>();\n        while (!stack.isEmpty())\n        {\n            result.add(stack.pop());\n        }\n        return result;\n    }\n\n    public void removeVertex(final V vertex)\n    {\n        final Set<V> targets = this.outMap.remove(vertex);\n        if (targets != null)\n        {\n            targets.forEach(target -> this.outMap.remove(target, vertex));\n        }\n        final Set<V> origins = this.inMap.remove(vertex);\n        if (origins != null)\n        {\n            origins.forEach(origin -> this.inMap.remove(origin, vertex));\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[Out: \" + this.outMap.toString() + \", In: \" + this.inMap.toString() + \"]\";\n    }\n\n    private int getDeepestLevel(final V vertex, final int level)\n    {\n        if (isSource(vertex))\n        {\n            return level;\n        }\n        final Set<V> parents = getParents(vertex);\n        int nextLevel = level;\n        for (final V parent : parents)\n        {\n            nextLevel = Math.max(nextLevel, getDeepestLevel(parent, level) + 1);\n        }\n        return nextLevel;\n    }\n\n    private Set<V> getZeroEdgeVertices(final LinkedMultiMap<V, V> map)\n    {\n        final Set<V> mapKeys = map.keySet();\n        final Set<V> zeroEdges = new LinkedHashSet<>(mapKeys.size());\n        mapKeys.stream().filter(key -> map.get(key).isEmpty()).forEach(key -> zeroEdges.add(key));\n        return zeroEdges;\n    }\n\n    private void topologicalSort(final V current, final Set<V> visited, final Stack<V> stack)\n    {\n        visited.add(current);\n        getChildren(current).iterator().forEachRemaining(child ->\n        {\n            if (!visited.contains(child))\n            {\n                topologicalSort(child, visited, stack);\n            }\n        });\n        stack.push(current);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/http/rest/DislikedResponseCodeException.java",
    "content": "package org.openstreetmap.atlas.utilities.http.rest;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Thrown when an HTTP Request returns a status code we don't like\n *\n * @author cstaylor\n */\npublic class DislikedResponseCodeException extends CoreException\n{\n    private static final long serialVersionUID = -172869039301922865L;\n\n    private final int statusCode;\n\n    public DislikedResponseCodeException(final int statusCode, final String message,\n            final Object... arguments)\n    {\n        super(message, arguments);\n        this.statusCode = statusCode;\n    }\n\n    public int getStatusCode()\n    {\n        return this.statusCode;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/http/rest/HttpResultHandler.java",
    "content": "package org.openstreetmap.atlas.utilities.http.rest;\n\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.fasterxml.jackson.core.JsonFactory;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n/**\n * This class handles all responses from an HTTP server, and the caller can configure a behavior\n * based on the HTTP status code. There are five kinds of actions that can be taken:\n * <ul>\n * <li>log - just write the response code and body contents to SLF4J</li>\n * <li>ignore - don't do anything at all with the response</li>\n * <li>throw - wrap up the status code and body into a DislikedResponseCodeException and throw it\n * </li>\n * <li>actOn - given a consumer for a tuple of response code and String, wrap up the status code and\n * body into a Tuple and pass it to the consumer</li>\n * <li>parseJson - given a Java class the represents the incoming JSON data and a consumer, using\n * Jackson's ObjectMapper and convert it, then pass a Tuple containing the response code and the\n * converted Java object to the consumer</li>\n * </ul>\n * Note: All of these methods expect smallish bodies since these are really API calls into JSON\n * using a REST interface\n *\n * @author cstaylor\n */\npublic class HttpResultHandler\n{\n    private static final Logger logger = LoggerFactory.getLogger(HttpResultHandler.class);\n\n    private static final Consumer<Tuple<Integer, String>> LOGGING_HANDLER = tuple ->\n    {\n        logger.debug(\"[{}] -> {}\", tuple.getFirst(), tuple.getSecond());\n    };\n\n    private static final Consumer<Tuple<Integer, String>> IGNORE_HANDLER = tuple ->\n    {\n        // Does nothing\n    };\n\n    private static final Consumer<Tuple<Integer, String>> THROW_HANDLER = tuple ->\n    {\n        throw new DislikedResponseCodeException(tuple.getFirst(), tuple.getSecond());\n    };\n\n    private final Map<Integer, Consumer<Tuple<Integer, String>>> responseHandlers = new HashMap<>();\n\n    private Consumer<Tuple<Integer, String>> defaultHandler = IGNORE_HANDLER;\n\n    public HttpResultHandler abort()\n    {\n        this.defaultHandler = THROW_HANDLER;\n        return this;\n    }\n\n    public HttpResultHandler abort(final int statusCode)\n    {\n        return actOn(statusCode, THROW_HANDLER);\n    }\n\n    public HttpResultHandler actOn(final int statusCode,\n            final Consumer<Tuple<Integer, String>> data)\n    {\n        this.responseHandlers.put(statusCode, data);\n        return this;\n    }\n\n    public HttpResultHandler ignore()\n    {\n        this.defaultHandler = IGNORE_HANDLER;\n        return this;\n    }\n\n    public HttpResultHandler ignore(final int statusCode)\n    {\n        return actOn(statusCode, IGNORE_HANDLER);\n    }\n\n    public HttpResultHandler log()\n    {\n        this.defaultHandler = LOGGING_HANDLER;\n        return this;\n    }\n\n    public HttpResultHandler log(final int statusCode)\n    {\n        return actOn(statusCode, LOGGING_HANDLER);\n    }\n\n    public void parse(final CloseableHttpResponse response)\n    {\n        try (StringWriter stringWriter = new StringWriter())\n        {\n            IOUtils.copy(response.getEntity().getContent(), stringWriter, StandardCharsets.UTF_8);\n            final int statusCode = response.getStatusLine().getStatusCode();\n            stringWriter.flush();\n            this.responseHandlers.getOrDefault(statusCode, this.defaultHandler)\n                    .accept(new Tuple<>(statusCode, stringWriter.toString()));\n        }\n        catch (final IOException oops)\n        {\n            throw new CoreException(\"Error when parsing HTTP response body\", oops);\n        }\n    }\n\n    public <T> HttpResultHandler parseJSON(final int statusCode, final Class<T> conversionClass,\n            final Consumer<Tuple<Integer, T>> consumer)\n    {\n        return actOn(statusCode, item ->\n        {\n            try\n            {\n                final JsonParser parser = new JsonFactory().createParser(item.getSecond());\n                parser.setCodec(new ObjectMapper());\n                consumer.accept(new Tuple<>(statusCode, parser.readValueAs(conversionClass)));\n            }\n            catch (final Exception oops)\n            {\n                throw new CoreException(\"Error when parsing JSON\", oops);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/identifiers/EntityIdentifierGenerator.java",
    "content": "package org.openstreetmap.atlas.utilities.identifiers;\n\nimport java.util.Map;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * Generate unique 64 bit (Java long) identifiers for {@link CompleteEntity}s. The identifiers are\n * generated using a hash of the entity's properties, including its geometry and tags. While the\n * identifiers are advertised as unique, 64 bits may not be enough to prevent collisions when used\n * at world-scale.\n * \n * @author lcram\n */\npublic class EntityIdentifierGenerator\n{\n    /**\n     * A config class for {@link EntityIdentifierGenerator} to set various configuration parameters.\n     * \n     * @author lcram\n     */\n    public static class Configuration\n    {\n        private boolean useGeometry;\n        private boolean useTags;\n        private boolean useRelationMembers;\n\n        public Configuration()\n        {\n            this.useGeometry = false;\n            this.useTags = false;\n            this.useRelationMembers = false;\n        }\n\n        public Configuration excludeGeometry()\n        {\n            this.useGeometry = false;\n            return this;\n        }\n\n        public Configuration excludeRelationMembers()\n        {\n            this.useRelationMembers = false;\n            return this;\n        }\n\n        public Configuration excludeTags()\n        {\n            this.useTags = false;\n            return this;\n        }\n\n        /**\n         * Get an {@link EntityIdentifierGenerator} built with this {@link Configuration}.\n         * \n         * @return a configured {@link EntityIdentifierGenerator}\n         */\n        public EntityIdentifierGenerator getGenerator()\n        {\n            return new EntityIdentifierGenerator(this);\n        }\n\n        /**\n         * This {@link Configuration} is empty if all fields are false. Empty {@link Configuration}s\n         * are generally not valid.\n         *\n         * @return if this {@link Configuration} is empty\n         */\n        public boolean isEmpty()\n        {\n            return !this.useGeometry && !this.useTags && !this.useRelationMembers;\n        }\n\n        /**\n         * Check if this {@link Configuration} is non-relation invariant. A non-relation invariant\n         * {@link Configuration} is one that will generate the same ID for any non-relation type\n         * entity.\n         *\n         * @return if this {@link Configuration} is non-relation invariant.\n         */\n        public boolean isNonRelationInvariant()\n        {\n            return !this.useGeometry && !this.useTags;\n        }\n\n        public boolean isUsingGeometry()\n        {\n            return this.useGeometry;\n        }\n\n        public boolean isUsingRelationMembers()\n        {\n            return this.useRelationMembers;\n        }\n\n        public boolean isUsingTags()\n        {\n            return this.useTags;\n        }\n\n        /**\n         * Set all fields to true. This imitates the behaviour of the default\n         * {@link EntityIdentifierGenerator} constructor without any configuration.\n         *\n         * @return a {@link Configuration} set with the defaults\n         */\n        public Configuration useDefaults()\n        {\n            this.useGeometry = true;\n            this.useTags = true;\n            this.useRelationMembers = true;\n            return this;\n        }\n\n        public Configuration useGeometry()\n        {\n            this.useGeometry = true;\n            return this;\n        }\n\n        public Configuration useRelationMembers()\n        {\n            this.useRelationMembers = true;\n            return this;\n        }\n\n        public Configuration useTags()\n        {\n            this.useTags = true;\n            return this;\n        }\n    }\n\n    private static final long HIGHEST_ATLAS_ID = 9999999999999999L;\n    private static final long LOWEST_ATLAS_ID = -9999999999999999L;\n\n    private final Configuration configuration;\n\n    public EntityIdentifierGenerator()\n    {\n        this(new Configuration().useDefaults());\n    }\n\n    public EntityIdentifierGenerator(final Configuration configuration)\n    {\n        this.configuration = configuration;\n    }\n\n    /**\n     * Generate a 64 bit hash for a given non-{@link Edge} {@link CompleteEntity}. The entity must\n     * contain enough information for it to be created from scratch.\n     * \n     * @param entity\n     *            the entity\n     * @return the hash\n     */\n    public long generateIdentifier(final CompleteEntity<?> entity)\n    {\n        if (entity.getType() == ItemType.EDGE)\n        {\n            throw new IllegalArgumentException(\n                    \"For type EDGE, please use generatePositiveIdentifierForEdge\");\n        }\n        return generate(entity, true);\n    }\n\n    /**\n     * Generate a 64 bit hash for a given {@link CompleteEdge}. The edge must contain enough\n     * information for it to be created from scratch. The ID generated from this method will always\n     * be positive.\n     *\n     * @param edge\n     *            the edge\n     * @return the hash\n     */\n    public long generatePositiveIdentifierForEdge(final CompleteEdge edge)\n    {\n        return generate(edge, false);\n    }\n\n    /**\n     * Given some {@link CompleteEntity}, compute a string made up of the concatenated basic entity\n     * properties (i.e. the geometry WKT and the tags).\n     *\n     * @param entity\n     *            the {@link CompleteEntity} to string-ify\n     * @return the property string\n     */\n    String getBasicPropertyString(final CompleteEntity<?> entity)\n    {\n        final StringBuilder builder = new StringBuilder();\n\n        if (this.configuration.isUsingGeometry())\n        {\n            final String wkt = entity.toWkt();\n            if (wkt == null && !(entity instanceof CompleteRelation))\n            {\n                throw new CoreException(\"Geometry must be set for entity {}\", entity.prettify());\n            }\n            builder.append(wkt);\n        }\n        if (this.configuration.isUsingTags())\n        {\n            final Map<String, String> tags = entity.getTags();\n            if (tags == null)\n            {\n                throw new CoreException(\"Tags must be set for entity {}\", entity.prettify());\n            }\n            final SortedSet<String> sortedTags = tags.entrySet().stream()\n                    .map(entry -> entry.getKey() + \"=\" + entry.getValue())\n                    .collect(Collectors.toCollection(TreeSet::new));\n            final String tagString = new StringList(sortedTags).join(\",\");\n            builder.append(\";\");\n            builder.append(tagString);\n        }\n\n        return builder.toString();\n    }\n\n    /**\n     * Given some {@link CompleteEntity}, compute a string made up of concatenated type specific\n     * entity properties. Currently this is only relevant for {@link Relation}s.\n     *\n     * @param entity\n     *            the {@link CompleteEntity} to string-ify\n     * @return the property string\n     */\n    String getTypeSpecificPropertyString(final CompleteEntity<?> entity)\n    {\n        if (entity.getType() != ItemType.RELATION)\n        {\n            return \"\";\n        }\n\n        if (this.configuration.isUsingRelationMembers())\n        {\n            final StringBuilder builder = new StringBuilder();\n            builder.append(\";\");\n\n            final CompleteRelation relation = (CompleteRelation) entity;\n\n            if (relation.members() == null)\n            {\n                throw new CoreException(\"Relation members must be set for entity {}\",\n                        entity.prettify());\n            }\n\n            final RelationBean bean = relation.members().asBean();\n            builder.append(\"RelationBean[\");\n            // Here use sorted list to ensure determinism\n            for (final RelationBean.RelationBeanItem beanItem : bean.asSortedList())\n            {\n                builder.append(\"(\");\n                builder.append(beanItem.getType());\n                builder.append(\",\");\n                builder.append(beanItem.getIdentifier());\n                builder.append(\",\");\n                builder.append(beanItem.getRole());\n                builder.append(\")\");\n            }\n            builder.append(\"]\");\n            return builder.toString();\n        }\n\n        return \"\";\n    }\n\n    /**\n     * A helper method that generates a deterministic 64 bit identifier for a given\n     * {@link CompleteEntity}. Additionally, this method can be tweaked to prevent negative\n     * identifiers.\n     * \n     * @param entity\n     *            the entity\n     * @param allowNegativeIdentifiers\n     *            if we want to allow the algorithm to generate negative identifiers\n     * @return the identifier\n     */\n    private long generate(final CompleteEntity<?> entity, final boolean allowNegativeIdentifiers)\n    {\n        if (this.configuration.isEmpty())\n        {\n            throw new CoreException(\n                    \"EntityIdentifierGenerator.Configuration was empty! Please set at least one of geometry, tags, or relation members.\");\n        }\n\n        if (entity.getType() != ItemType.RELATION && this.configuration.isNonRelationInvariant())\n        {\n            throw new CoreException(\n                    \"EntityIdentifierGenerator.Configuration was non-relation invariant! Please set at least one of geometry or tags to generate IDs for non-relation type entities.\");\n        }\n\n        int iterations = 0;\n        final int maximumIterations = 1000;\n\n        final String entityString = getBasicPropertyString(entity)\n                + getTypeSpecificPropertyString(entity);\n        UUID entityHash = UUID.nameUUIDFromBytes(entityString.getBytes());\n\n        long shortHash = entityHash.getMostSignificantBits();\n        while (!this.isHashSafeToUse(shortHash) || shortHash < 0 && !allowNegativeIdentifiers)\n        {\n            entityHash = UUID.nameUUIDFromBytes(entityHash.toString().getBytes());\n            shortHash = entityHash.getMostSignificantBits();\n\n            if (iterations > maximumIterations)\n            {\n                /*\n                 * If this happens, we have a problem. It means that, within 1000 tries, we could\n                 * not generate an ID that was both safe to use (outside of OSM ID boundary) and\n                 * within the sign constraints (positive vs. negative allowed IDs). Realistically,\n                 * this should never happen. If for some reason it did, a possible solution would be\n                 * to simply bump up the iteration threshold.\n                 */\n                throw new CoreException(\n                        \"Exceeded maximum iterations ({}) when attempting to generate hash for {}\",\n                        maximumIterations, entity.prettify());\n            }\n            iterations++;\n        }\n\n        return shortHash;\n    }\n\n    /**\n     * Check if the hash falls outside of the unsafe range of possible OSM identifiers.\n     *\n     * @param hash\n     *            the hash to check\n     * @return if the hash is in range\n     */\n    private boolean isHashSafeToUse(final long hash)\n    {\n        return hash > HIGHEST_ATLAS_ID || hash < LOWEST_ATLAS_ID;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/jsoncompare/RegularExpressionJSONComparator.java",
    "content": "package org.openstreetmap.atlas.utilities.jsoncompare;\n\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.allJSONObjects;\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.allSimpleValues;\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.arrayOfJsonObjectToMap;\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.findUniqueKey;\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.formatUniqueKey;\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.getKeys;\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.isUsableAsUniqueKey;\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.jsonArrayToList;\nimport static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.qualify;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.skyscreamer.jsonassert.JSONCompareMode;\nimport org.skyscreamer.jsonassert.JSONCompareResult;\nimport org.skyscreamer.jsonassert.comparator.JSONComparator;\nimport org.skyscreamer.jsonassert.comparator.JSONCompareUtil;\n\n/**\n * Code adapted from the AbstractJSONComparator and DefaultJSONComparator classes in the JSONAssert\n * project found at https://github.com/skyscreamer/JSONassert\n *\n * @author cstaylor\n */\npublic class RegularExpressionJSONComparator implements JSONComparator\n{\n    /**\n     * List of feature and values we should ignore when comparing values\n     */\n    private final List<Pattern> searchPatterns;\n\n    /**\n     * What comparison mode should we use?\n     */\n    private final JSONCompareMode mode;\n\n    public RegularExpressionJSONComparator(final JSONCompareMode mode)\n    {\n        this.searchPatterns = new ArrayList<>();\n        this.mode = mode;\n    }\n\n    /**\n     * Compares JSONArray provided to the expected JSONArray, and returns the results of the\n     * comparison.\n     *\n     * @param expected\n     *            Expected JSONArray\n     * @param actual\n     *            JSONArray to compare\n     * @throws JSONException\n     *             something goes wrong when reading JSON values\n     */\n    @Override\n    public final JSONCompareResult compareJSON(final JSONArray expected, final JSONArray actual)\n            throws JSONException\n    {\n        final JSONCompareResult result = createResult();\n        compareJSONArray(\"\", expected, actual, result);\n        return result;\n    }\n\n    /**\n     * Compares JSONObject provided to the expected JSONObject, and returns the results of the\n     * comparison.\n     *\n     * @param expected\n     *            Expected JSONObject\n     * @param actual\n     *            JSONObject to compare\n     * @throws JSONException\n     *             something goes wrong when reading JSON values\n     */\n    @Override\n    public final JSONCompareResult compareJSON(final JSONObject expected, final JSONObject actual)\n            throws JSONException\n    {\n        final JSONCompareResult result = createResult();\n        compareJSON(\"\", expected, actual, result);\n        return result;\n    }\n\n    @Override\n    public void compareJSON(final String prefix, final JSONObject expected, final JSONObject actual,\n            final JSONCompareResult result) throws JSONException\n    {\n        // Check that actual contains all the expected values\n        checkJsonObjectKeysExpectedInActual(prefix, expected, actual, result);\n\n        // If strict, check for vice-versa\n        if (!this.mode.isExtensible())\n        {\n            checkJsonObjectKeysActualInExpected(prefix, expected, actual, result);\n        }\n    }\n\n    @Override\n    public void compareJSONArray(final String prefix, final JSONArray expected,\n            final JSONArray actual, final JSONCompareResult result) throws JSONException\n    {\n        if (expected.length() > actual.length())\n        {\n            final int end = Math.max(expected.length(), actual.length());\n            for (int loop = Math.min(expected.length(), actual.length()); loop < end; loop++)\n            {\n                result.missing(String.format(\"%s[%d]\", prefix, loop), expected.get(loop));\n            }\n            return;\n        }\n        else if (expected.length() < actual.length())\n        {\n            final int end = Math.max(expected.length(), actual.length());\n            for (int loop = Math.min(expected.length(), actual.length()); loop < end; loop++)\n            {\n                result.unexpected(String.format(\"%s[%d]\", prefix, loop), actual.get(loop));\n            }\n            return;\n        }\n        else if (expected.length() == 0)\n        {\n            // Nothing to compare\n            return;\n        }\n\n        if (this.mode.hasStrictOrder())\n        {\n            compareJSONArrayWithStrictOrder(prefix, expected, actual, result);\n        }\n        else if (allSimpleValues(expected))\n        {\n            compareJSONArrayOfSimpleValues(prefix, expected, actual, result);\n        }\n        else if (allJSONObjects(expected))\n        {\n            compareJSONArrayOfJsonObjects(prefix, expected, actual, result);\n        }\n        else\n        {\n            // An expensive last resort\n            recursivelyCompareJSONArray(prefix, expected, actual, result);\n        }\n    }\n\n    @Override\n    public void compareValues(final String prefix, final Object expectedValue,\n            final Object actualValue, final JSONCompareResult result) throws JSONException\n    {\n        if (expectedValue instanceof Number && actualValue instanceof Number)\n        {\n            if (((Number) expectedValue).doubleValue() != ((Number) actualValue).doubleValue())\n            {\n                result.fail(prefix, expectedValue, actualValue);\n            }\n        }\n        else if (expectedValue.getClass().isAssignableFrom(actualValue.getClass()))\n        {\n            if (expectedValue instanceof JSONArray)\n            {\n                compareJSONArray(prefix, (JSONArray) expectedValue, (JSONArray) actualValue,\n                        result);\n            }\n            else if (expectedValue instanceof JSONObject)\n            {\n                compareJSON(prefix, (JSONObject) expectedValue, (JSONObject) actualValue, result);\n            }\n            else if (!expectedValue.equals(actualValue))\n            {\n                result.fail(prefix, expectedValue, actualValue);\n            }\n        }\n        else\n        {\n            result.fail(prefix, expectedValue, actualValue);\n        }\n    }\n\n    /**\n     * Automatically adds a regex section to each regex in regexes that anchors the regular\n     * expression to the end of the line and allows for extra information before the beginning of\n     * the regex.\n     *\n     * @param regexes\n     *            the suffix match regexes we should add to our ignore list\n     * @return Fluent API means we return this\n     */\n    public RegularExpressionJSONComparator endsWith(final String... regexes)\n    {\n        return exact(Stream.of(regexes).map(regex -> String.format(\"(\\\\W)*(\\\\S)*%s$\", regex))\n                .collect(Collectors.toList()).toArray(new String[0]));\n    }\n\n    /**\n     * Adds a specific string as a regex pattern to the list of values that should be ignored from\n     * processing\n     *\n     * @param regexes\n     *            the exact match regexes we should add to our ignore list\n     * @return Fluent API means we return this\n     */\n    public RegularExpressionJSONComparator exact(final String... regexes)\n    {\n        Stream.of(regexes).map(Pattern::compile).forEach(this.searchPatterns::add);\n        return this;\n    }\n\n    /**\n     * Automatically adds a regex section to each regex in regexes that anchors the regular\n     * expression to the beginning of the line and allows for extra information after the end of the\n     * regex.\n     *\n     * @param regexes\n     *            the prefix match regexes we should add to our ignore list\n     * @return Fluent API means we return this\n     */\n    public RegularExpressionJSONComparator startsWith(final String... regexes)\n    {\n        return exact(Stream.of(regexes).map(regex -> String.format(\"^%s(\\\\W)*(\\\\S)*\", regex))\n                .collect(Collectors.toList()).toArray(new String[0]));\n    }\n\n    protected void checkJsonObjectKeysActualInExpected(final String prefix,\n            final JSONObject expected, final JSONObject actual, final JSONCompareResult result)\n    {\n        getKeys(actual).stream().filter(key -> !expected.has(key)).forEach(key ->\n        {\n            result.unexpected(prefix, key);\n        });\n    }\n\n    protected void checkJsonObjectKeysExpectedInActual(final String prefix,\n            final JSONObject expected, final JSONObject actual, final JSONCompareResult result)\n            throws JSONException\n    {\n        for (final String key : getKeys(expected))\n        {\n            if (actual.has(key))\n            {\n                compareValues(qualify(prefix, key), expected.get(key), actual.get(key), result);\n            }\n            else\n            {\n                result.missing(prefix, key);\n            }\n        }\n    }\n\n    protected void compareJSONArrayOfJsonObjects(final String key, final JSONArray expected,\n            final JSONArray actual, final JSONCompareResult result) throws JSONException\n    {\n        final String uniqueKey = findUniqueKey(expected);\n        if (uniqueKey == null || !isUsableAsUniqueKey(uniqueKey, actual))\n        {\n            // An expensive last resort\n            recursivelyCompareJSONArray(key, expected, actual, result);\n            return;\n        }\n        final Map<Object, JSONObject> expectedValueMap = arrayOfJsonObjectToMap(expected,\n                uniqueKey);\n        final Map<Object, JSONObject> actualValueMap = arrayOfJsonObjectToMap(actual, uniqueKey);\n        for (final Object identifier : expectedValueMap.keySet())\n        {\n            if (!actualValueMap.containsKey(identifier))\n            {\n                result.missing(formatUniqueKey(key, uniqueKey, identifier),\n                        expectedValueMap.get(identifier));\n                continue;\n            }\n            final JSONObject expectedValue = expectedValueMap.get(identifier);\n            final JSONObject actualValue = actualValueMap.get(identifier);\n            compareValues(formatUniqueKey(key, uniqueKey, identifier), expectedValue, actualValue,\n                    result);\n        }\n        for (final Object identifier : actualValueMap.keySet())\n        {\n            if (!expectedValueMap.containsKey(identifier))\n            {\n                result.unexpected(formatUniqueKey(key, uniqueKey, identifier),\n                        actualValueMap.get(identifier));\n            }\n        }\n    }\n\n    protected void compareJSONArrayOfSimpleValues(final String key, final JSONArray expected,\n            final JSONArray actual, final JSONCompareResult result) throws JSONException\n    {\n        final Map<Object, Integer> expectedCount = JSONCompareUtil\n                .getCardinalityMap(jsonArrayToList(expected));\n        final Map<Object, Integer> actualCount = JSONCompareUtil\n                .getCardinalityMap(jsonArrayToList(actual));\n        for (final Object foundKey : expectedCount.keySet())\n        {\n            if (!actualCount.containsKey(foundKey))\n            {\n                result.missing(key + \"[]\", foundKey);\n            }\n            else if (!actualCount.get(foundKey).equals(expectedCount.get(foundKey)))\n            {\n                result.fail(key + \"[]: Expected \" + expectedCount.get(foundKey)\n                        + \" occurrence(s) of \" + foundKey + \" but got \" + actualCount.get(foundKey)\n                        + \" occurrence(s)\");\n            }\n        }\n        for (final Object foundKey : actualCount.keySet())\n        {\n            if (!expectedCount.containsKey(foundKey))\n            {\n                result.unexpected(key + \"[]\", foundKey);\n            }\n        }\n    }\n\n    protected void compareJSONArrayWithStrictOrder(final String key, final JSONArray expected,\n            final JSONArray actual, final JSONCompareResult result) throws JSONException\n    {\n        for (int i = 0; i < expected.length(); ++i)\n        {\n            final Object expectedValue = expected.get(i);\n            final Object actualValue = actual.get(i);\n            compareValues(key + \"[\" + i + \"]\", expectedValue, actualValue, result);\n        }\n    }\n\n    protected JSONCompareResult createResult()\n    {\n        return new RegularExpressionJSONCompareResult(this.searchPatterns);\n    }\n\n    /**\n     * This is expensive (O(n^2) -- yuck), but may be the only resort for some cases with loose\n     * array ordering, and no\n     *\n     * @param key\n     *            the named key where expected and actual came from\n     * @param expected\n     *            the value we expect actual to match\n     * @param actual\n     *            the value we're trying to compare\n     * @param result\n     *            where we keep our failure list\n     * @throws JSONException\n     *             if something goes wrong when retrieving values from the JSON objects\n     */\n    protected void recursivelyCompareJSONArray(final String key, final JSONArray expected,\n            final JSONArray actual, final JSONCompareResult result) throws JSONException\n    {\n        final Set<Integer> matched = new HashSet<>();\n        for (int i = 0; i < expected.length(); ++i)\n        {\n            final Object expectedElement = expected.get(i);\n            boolean matchFound = false;\n            for (int j = 0; j < actual.length(); ++j)\n            {\n                final Object actualElement = actual.get(j);\n                if (matched.contains(j)\n                        || !actualElement.getClass().equals(expectedElement.getClass()))\n                {\n                    continue;\n                }\n                if (expectedElement instanceof JSONObject)\n                {\n                    if (compareJSON((JSONObject) expectedElement, (JSONObject) actualElement)\n                            .passed())\n                    {\n                        matched.add(j);\n                        matchFound = true;\n                        break;\n                    }\n                }\n                else if (expectedElement instanceof JSONArray)\n                {\n                    if (compareJSON((JSONArray) expectedElement, (JSONArray) actualElement)\n                            .passed())\n                    {\n                        matched.add(j);\n                        matchFound = true;\n                        break;\n                    }\n                }\n                else if (expectedElement.equals(actualElement))\n                {\n                    matched.add(j);\n                    matchFound = true;\n                    break;\n                }\n            }\n            if (!matchFound)\n            {\n                result.fail(\n                        key + \"[\" + i + \"] Could not find match for element \" + expectedElement);\n                return;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/jsoncompare/RegularExpressionJSONCompareResult.java",
    "content": "package org.openstreetmap.atlas.utilities.jsoncompare;\n\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.skyscreamer.jsonassert.JSONCompareResult;\n\n/**\n * Uses the searchPatterns to selectively ignore certain failures based on the feature and values in\n * question.\n *\n * @author cstaylor\n */\nclass RegularExpressionJSONCompareResult extends JSONCompareResult\n{\n    private final List<Pattern> searchPatterns;\n\n    RegularExpressionJSONCompareResult(final List<Pattern> searchPatterns)\n    {\n        this.searchPatterns = searchPatterns;\n    }\n\n    @Override\n    public JSONCompareResult fail(final String field, final Object expected, final Object actual)\n    {\n        if (!matches(field))\n        {\n            super.fail(field, expected, actual);\n        }\n        return this;\n    }\n\n    @Override\n    public JSONCompareResult missing(final String field, final Object expected)\n    {\n        if (!matches(String.format(\"%s.%s\", field, expected)))\n        {\n            super.missing(field, expected);\n        }\n        return this;\n    }\n\n    @Override\n    public JSONCompareResult unexpected(final String field, final Object value)\n    {\n        if (!matches(String.format(\"%s.%s\", field, value)))\n        {\n            super.unexpected(field, value);\n        }\n        return this;\n    }\n\n    private boolean matches(final String compareMe)\n    {\n        return this.searchPatterns.stream().map(pattern -> pattern.matcher(compareMe))\n                .anyMatch(Matcher::matches);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/IntegerToIntegerMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArray;\nimport org.openstreetmap.atlas.utilities.arrays.LargeArray;\n\n/**\n * @author matthieun\n */\npublic class IntegerToIntegerMap extends LargeMap<Integer, Integer>\n{\n    private static final long serialVersionUID = -3202467180188514944L;\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     */\n    public IntegerToIntegerMap(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     */\n    public IntegerToIntegerMap(final long maximumSize, final int hashSize)\n    {\n        super(maximumSize, hashSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     * @param keyMemoryBlockSize\n     *            The initial memory allocation size for the keys sub arrays\n     * @param keySubArraySize\n     *            The maximum size of a keys sub array\n     * @param valueMemoryBlockSize\n     *            The initial memory allocation size for the values sub arrays\n     * @param valueSubArraySize\n     *            The maximum size of a values sub array\n     */\n    public IntegerToIntegerMap(final String name, final long maximumSize, final int hashSize,\n            final int keyMemoryBlockSize, final int keySubArraySize, final int valueMemoryBlockSize,\n            final int valueSubArraySize)\n    {\n        super(name, maximumSize, hashSize, keyMemoryBlockSize, keySubArraySize,\n                valueMemoryBlockSize, valueSubArraySize);\n    }\n\n    @Override\n    protected LargeArray<Integer> createKeys(final int memoryBlockSize, final int subArraySize)\n    {\n        final IntegerArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new IntegerArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new IntegerArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Keys\");\n        return result;\n    }\n\n    @Override\n    protected LargeArray<Integer> createValues(final int memoryBlockSize, final int subArraySize)\n    {\n        final IntegerArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new IntegerArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new IntegerArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Values\");\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/LargeMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.utilities.arrays.Arrays;\nimport org.openstreetmap.atlas.utilities.arrays.LargeArray;\nimport org.openstreetmap.atlas.utilities.arrays.LongArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.arrays.PrimitiveArray;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\n\n/**\n * Large map that is based on {@link LargeArray}s.\n *\n * @author matthieun\n * @author lcram\n * @param <K>\n *            The key type\n * @param <V>\n *            The value type\n */\npublic abstract class LargeMap<K, V> implements Iterable<K>, Serializable\n{\n    private static final long serialVersionUID = -6051549542042379037L;\n\n    protected static final int DEFAULT_HASH_MODULO_RATIO = 10;\n\n    private final LargeArray<V> values;\n    private final LargeArray<K> keys;\n    private final int hashSize;\n    private final long maximumSize;\n    private final String name;\n\n    // Each index of this list is a hash value (modulo-ed)\n    // Each Value is an array of indices to look up the items in the key/value arrays\n    private final LongArrayOfArrays hashes;\n\n    /**\n     * This nullary constructor exists solely for subclasses of {@link LargeMap} that wish to\n     * implement their own nullary constructor. These nullary constructors should only be used by\n     * serialization code in {@link PackedAtlasSerializer} that needs to obtain\n     * {@link ProtoAdapter}s. The objects they initialize are corrupted for general use and should\n     * be discarded.\n     */\n    protected LargeMap()\n    {\n        this.values = null;\n        this.keys = null;\n        this.hashSize = 0;\n        this.maximumSize = 0;\n        this.name = null;\n        this.hashes = null;\n    }\n\n    /**\n     * Construct a large map\n     *\n     * @param maximumSize\n     *            Estimate of the number of keys.\n     */\n    protected LargeMap(final long maximumSize)\n    {\n        this(\"LargeMap\", maximumSize);\n    }\n\n    /**\n     * Construct a large map\n     *\n     * @param maximumSize\n     *            Estimate of the number of keys.\n     * @param hashSize\n     *            The size of the hash space. The hash used is modulo. If this is too small, there\n     *            will be lots of collisions, and the map will be really slow. If this is too big,\n     *            the hash space will be sparse, and use up a lot of memory for nothing.\n     */\n    protected LargeMap(final long maximumSize, final int hashSize)\n    {\n        this(\"LargeMap\", maximumSize, hashSize);\n    }\n\n    /**\n     * Construct a large map\n     *\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            Estimate of the number of keys.\n     */\n    protected LargeMap(final String name, final long maximumSize)\n    {\n        this(name, maximumSize,\n                (int) Math.min(maximumSize / DEFAULT_HASH_MODULO_RATIO, Integer.MAX_VALUE));\n    }\n\n    /**\n     * Construct a large map\n     *\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            Estimate of the number of keys.\n     * @param hashSize\n     *            The size of the hash space. The hash used is modulo. If this is too small, there\n     *            will be lots of collisions, and the map will be really slow. If this is too big,\n     *            the hash space will be sparse, and use up a lot of memory for nothing.\n     */\n    protected LargeMap(final String name, final long maximumSize, final int hashSize)\n    {\n        this(name, maximumSize, hashSize, -1, -1, -1, -1);\n    }\n\n    /**\n     * Construct a large map\n     *\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            Estimate of the number of keys.\n     * @param hashSize\n     *            The size of the hash space. The hash used is modulo. If this is too small, there\n     *            will be lots of collisions, and the map will be really slow. If this is too big,\n     *            the hash space will be sparse, and use up a lot of memory for nothing.\n     * @param keyMemoryBlockSize\n     *            The initial memory allocation size for the keys sub arrays\n     * @param keySubArraySize\n     *            The maximum size of a keys sub array\n     * @param valueMemoryBlockSize\n     *            The initial memory allocation size for the values sub arrays\n     * @param valueSubArraySize\n     *            The maximum size of a values sub array\n     */\n    protected LargeMap(final String name, final long maximumSize, final int hashSize,\n            final int keyMemoryBlockSize, final int keySubArraySize, final int valueMemoryBlockSize,\n            final int valueSubArraySize)\n    {\n        if (maximumSize < 0 || hashSize <= 0)\n        {\n            throw new CoreException(\"maximumSize(\" + maximumSize + \") has to be >=0 and hashSize(\"\n                    + hashSize + \") has to be > 0\");\n        }\n        this.name = name;\n        this.hashes = new LongArrayOfArrays(hashSize, hashSize, hashSize);\n        for (int i = 0; i < hashSize; i++)\n        {\n            // All the hashes are empty-populated\n            this.hashes.add(new long[0]);\n        }\n        this.hashSize = hashSize;\n        this.maximumSize = maximumSize;\n\n        // Those 2 depend on the above values being set first.\n        this.keys = createKeys(keyMemoryBlockSize, keySubArraySize);\n        this.values = createValues(valueMemoryBlockSize, valueSubArraySize);\n    }\n\n    /**\n     * @param key\n     *            The key to test\n     * @return true if the value is contained at the specified key\n     */\n    public boolean containsKey(final Object key)\n    {\n        final long[] possibleIndices = this.hashes.get(modulo(key.hashCode()));\n        for (final long index : possibleIndices)\n        {\n            if (this.keys.get(index).equals(key))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * A basic equals() implementation. Note that if this class is parameterized with an array type,\n     * this method may not work as expected (due to array equals() performing a reference\n     * comparison). Child classes of {@link LargeMap} may want to override this method to improve\n     * its behavior in special cases.\n     */\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof LargeMap)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            @SuppressWarnings(\"unchecked\")\n            final LargeMap<K, V> that = (LargeMap<K, V>) other;\n            if (!Objects.equals(this.getName(), that.getName()))\n            {\n                return false;\n            }\n            if (this.size() != that.size())\n            {\n                return false;\n            }\n            final Iterable<K> iterable = () -> this.iterator();\n            for (final K key : iterable)\n            {\n                if (!that.containsKey(key))\n                {\n                    return false;\n                }\n                final V thisValue = this.get(key);\n                final V thatValue = that.get(key);\n                if (!thisValue.equals(thatValue))\n                {\n                    return false;\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * @param key\n     *            The key to get the value at\n     * @return The value at the specified key\n     */\n    public V get(final Object key)\n    {\n        final long[] possibleIndices = this.hashes.get(modulo(key.hashCode()));\n        for (final long index : possibleIndices)\n        {\n            if (this.keys.get(index).equals(key))\n            {\n                return this.values.get(index);\n            }\n        }\n        return null;\n    }\n\n    public long getMaximumSize()\n    {\n        return this.maximumSize;\n    }\n\n    /**\n     * @return The name of this map\n     */\n    public String getName()\n    {\n        return this.name;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final int initialPrime = 31;\n        final int hashSeed = 37;\n\n        final int nameHash = this.getName() == null ? 0 : this.getName().hashCode();\n        int hash = hashSeed * initialPrime + nameHash;\n        hash = hashSeed * hash + Long.valueOf(this.size()).hashCode();\n\n        final Iterable<K> iterable = () -> this.iterator();\n        for (final K key : iterable)\n        {\n            final V value = this.get(key);\n            hash = hashSeed * hash + key.hashCode();\n            hash = hashSeed * hash + value.hashCode();\n        }\n\n        return hash;\n    }\n\n    public boolean isEmpty()\n    {\n        return this.keys.size() <= 0;\n    }\n\n    @Override\n    public Iterator<K> iterator()\n    {\n        return this.keys.iterator();\n    }\n\n    public synchronized void put(final K key, final V value)\n    {\n        // Find already all the possible collisions\n        long[] possibleIndices;\n        final long hashIndex = modulo(key.hashCode());\n        possibleIndices = this.hashes.get(hashIndex);\n        for (final long index : possibleIndices)\n        {\n            if (this.keys.get(index).equals(key))\n            {\n                // We have the same key already in, override it!\n                this.values.set(index, value);\n                return;\n            }\n        }\n        if (size() >= this.maximumSize)\n        {\n            throw new CoreException(\"The map is full.\");\n        }\n        // The given key does not exist yet\n        final long index = this.keys.size();\n        this.keys.add(key);\n        this.values.add(value);\n        possibleIndices = Arrays.addNewItem(possibleIndices, index);\n        this.hashes.set(hashIndex, possibleIndices);\n    }\n\n    public long size()\n    {\n        return this.keys.size();\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[\");\n        builder.append(this.getClass().getSimpleName());\n        builder.append(\" \");\n        this.forEach(key ->\n        {\n            if (builder.length() > 0)\n            {\n                builder.append(\", \");\n            }\n            builder.append(asKeyString(key));\n            builder.append(\" -> \");\n            builder.append(asValueString(get(key)));\n        });\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    /**\n     * Trim this {@link LargeMap}. WARNING: As much as this might save memory on arrays filled a\n     * small amount compared to the memoryBlockSize, it will resize the last {@link PrimitiveArray}\n     * of this {@link LargeMap}'s keys' and values' {@link LargeArray} which might take a lot of\n     * time, and (temporarily) a lot of memory.\n     */\n    public void trim()\n    {\n        this.keys.trim();\n        this.values.trim();\n    }\n\n    /**\n     * Trim this {@link LargeMap} if and only if the fill {@link Ratio} of the last\n     * {@link PrimitiveArray} of this {@link LargeMap}'s keys' and values' {@link LargeArray} is\n     * less than the provided {@link Ratio}\n     *\n     * @param ratio\n     *            The provided reference {@link Ratio}\n     */\n    public void trimIfLessFilledThan(final Ratio ratio)\n    {\n        this.keys.trimIfLessFilledThan(ratio);\n        this.values.trimIfLessFilledThan(ratio);\n    }\n\n    /**\n     * Override to change how the key is printed\n     *\n     * @param key\n     *            A key to print\n     * @return The String representation of the key\n     */\n    protected String asKeyString(final K key)\n    {\n        return key.toString();\n    }\n\n    /**\n     * Override to change how the value is printed\n     *\n     * @param value\n     *            A value to print\n     * @return The String representation of the value\n     */\n    protected String asValueString(final V value)\n    {\n        return value.toString();\n    }\n\n    /**\n     * @param memoryBlockSize\n     *            The initial memory allocation size for the keys sub arrays\n     * @param subArraySize\n     *            The maximum size of a keys sub array\n     * @return An empty array of keys\n     */\n    protected abstract LargeArray<K> createKeys(int memoryBlockSize, int subArraySize);\n\n    /**\n     * @param memoryBlockSize\n     *            The initial memory allocation size for the values sub arrays\n     * @param subArraySize\n     *            The maximum size of a values sub array\n     * @return An empty array of values\n     */\n    protected abstract LargeArray<V> createValues(int memoryBlockSize, int subArraySize);\n\n    private int modulo(final int hashValue)\n    {\n        final boolean negative = hashValue < 0;\n        long value = hashValue;\n        if (negative)\n        {\n            value = -value;\n        }\n        return (int) (value % this.hashSize);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/LinkedMultiMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport com.google.common.base.Joiner;\n\n/**\n * A multimap using LinkedHashMap and LinkedHashSet\n *\n * @author cuthbertm\n * @param <K>\n *            The key type for the map\n * @param <V>\n *            The value type for the map\n */\npublic class LinkedMultiMap<K, V> implements Map<K, Set<V>>, Serializable\n{\n    private static final long serialVersionUID = 5668281262269816871L;\n\n    private final Map<K, Set<V>> linkedMap = new LinkedHashMap<>();\n\n    public void add(final K key, final V value)\n    {\n        Set<V> values = this.linkedMap.get(key);\n        if (values == null)\n        {\n            values = new LinkedHashSet<>();\n            this.linkedMap.put(key, values);\n        }\n        if (value != null)\n        {\n            values.add(value);\n        }\n    }\n\n    @Override\n    public void clear()\n    {\n        this.linkedMap.clear();\n    }\n\n    @Override\n    public boolean containsKey(final Object key)\n    {\n        return this.linkedMap.containsKey(key);\n    }\n\n    @Override\n    public boolean containsValue(final Object value)\n    {\n        return this.linkedMap.entrySet().stream().filter(entry -> entry.getValue().contains(value))\n                .collect(Collectors.counting()) > 0;\n    }\n\n    @Override\n    public Set<Entry<K, Set<V>>> entrySet()\n    {\n        return this.linkedMap.entrySet();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public Set<V> get(final Object key)\n    {\n        final Set<V> values = this.linkedMap.get(key);\n        return values == null ? Collections.EMPTY_SET : values;\n    }\n\n    @Override\n    public boolean isEmpty()\n    {\n        return this.linkedMap.isEmpty();\n    }\n\n    @Override\n    public Set<K> keySet()\n    {\n        return this.linkedMap.keySet();\n    }\n\n    @Override\n    public Set<V> put(final K key, final Set<V> value)\n    {\n        return this.linkedMap.put(key, value);\n    }\n\n    @Override\n    public void putAll(final Map<? extends K, ? extends Set<V>> map)\n    {\n        this.linkedMap.putAll(map);\n    }\n\n    @Override\n    public Set<V> remove(final Object key)\n    {\n        return this.linkedMap.remove(key);\n    }\n\n    @Override\n    public boolean remove(final Object key, final Object value)\n    {\n        final Set<V> values = this.linkedMap.get(key);\n        if (values != null && values.contains(value))\n        {\n            values.remove(value);\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public int size()\n    {\n        return this.linkedMap.size();\n    }\n\n    @Override\n    public String toString()\n    {\n        final StringBuilder builder = new StringBuilder();\n        String entryDelimiter = \"\";\n        builder.append(\"[\");\n        for (final Entry<K, Set<V>> entry : this.linkedMap.entrySet())\n        {\n            final K key = entry.getKey();\n            final Set<V> values = entry.getValue();\n\n            builder.append(entryDelimiter);\n            builder.append(key.toString());\n            builder.append(\" -> \");\n\n            if (values != null && !values.isEmpty())\n            {\n                Joiner.on(\", \").skipNulls().appendTo(builder, values);\n            }\n            else\n            {\n                builder.append(\"Empty values\");\n            }\n            entryDelimiter = \", \";\n        }\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    @Override\n    public Collection<Set<V>> values()\n    {\n        return this.linkedMap.values();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/LongToBooleanMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport org.openstreetmap.atlas.utilities.arrays.BooleanArray;\nimport org.openstreetmap.atlas.utilities.arrays.LargeArray;\nimport org.openstreetmap.atlas.utilities.arrays.LongArray;\n\n/**\n * Large Map from Long to Boolean\n *\n * @author matthieun\n */\npublic class LongToBooleanMap extends LargeMap<Long, Boolean>\n{\n    private static final long serialVersionUID = 346161750484489066L;\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     */\n    public LongToBooleanMap(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     */\n    public LongToBooleanMap(final long maximumSize, final int hashSize)\n    {\n        super(maximumSize, hashSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     * @param keyMemoryBlockSize\n     *            The initial memory allocation size for the keys sub arrays\n     * @param keySubArraySize\n     *            The maximum size of a keys sub array\n     * @param valueMemoryBlockSize\n     *            The initial memory allocation size for the values sub arrays\n     * @param valueSubArraySize\n     *            The maximum size of a values sub array\n     */\n    public LongToBooleanMap(final String name, final long maximumSize, final int hashSize,\n            final int keyMemoryBlockSize, final int keySubArraySize, final int valueMemoryBlockSize,\n            final int valueSubArraySize)\n    {\n        super(name, maximumSize, hashSize, keyMemoryBlockSize, keySubArraySize,\n                valueMemoryBlockSize, valueSubArraySize);\n    }\n\n    @Override\n    protected LargeArray<Long> createKeys(final int memoryBlockSize, final int subArraySize)\n    {\n        final LongArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new LongArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new LongArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Keys\");\n        return result;\n    }\n\n    @Override\n    protected LargeArray<Boolean> createValues(final int memoryBlockSize, final int subArraySize)\n    {\n        final BooleanArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new BooleanArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new BooleanArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Values\");\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/LongToIntegerMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArray;\nimport org.openstreetmap.atlas.utilities.arrays.LargeArray;\nimport org.openstreetmap.atlas.utilities.arrays.LongArray;\n\n/**\n * Large Map from Long to Integer\n *\n * @author matthieun\n */\npublic class LongToIntegerMap extends LargeMap<Long, Integer>\n{\n    private static final long serialVersionUID = 346161750484489066L;\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     */\n    public LongToIntegerMap(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     */\n    public LongToIntegerMap(final long maximumSize, final int hashSize)\n    {\n        super(maximumSize, hashSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     * @param keyMemoryBlockSize\n     *            The initial memory allocation size for the keys sub arrays\n     * @param keySubArraySize\n     *            The maximum size of a keys sub array\n     * @param valueMemoryBlockSize\n     *            The initial memory allocation size for the values sub arrays\n     * @param valueSubArraySize\n     *            The maximum size of a values sub array\n     */\n    public LongToIntegerMap(final String name, final long maximumSize, final int hashSize,\n            final int keyMemoryBlockSize, final int keySubArraySize, final int valueMemoryBlockSize,\n            final int valueSubArraySize)\n    {\n        super(name, maximumSize, hashSize, keyMemoryBlockSize, keySubArraySize,\n                valueMemoryBlockSize, valueSubArraySize);\n    }\n\n    @Override\n    protected LargeArray<Long> createKeys(final int memoryBlockSize, final int subArraySize)\n    {\n        final LongArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new LongArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new LongArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Keys\");\n        return result;\n    }\n\n    @Override\n    protected LargeArray<Integer> createValues(final int memoryBlockSize, final int subArraySize)\n    {\n        final IntegerArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new IntegerArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new IntegerArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Values\");\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/LongToIntegerMultiMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport org.openstreetmap.atlas.utilities.arrays.Arrays;\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.arrays.LargeArray;\nimport org.openstreetmap.atlas.utilities.arrays.LongArray;\n\n/**\n * {@link LargeMap} long to int[]\n *\n * @author matthieun\n */\npublic class LongToIntegerMultiMap extends LargeMap<Long, int[]>\n{\n    private static final long serialVersionUID = 6741833447370296269L;\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     */\n    public LongToIntegerMultiMap(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     */\n    public LongToIntegerMultiMap(final long maximumSize, final int hashSize)\n    {\n        super(maximumSize, hashSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     * @param keyMemoryBlockSize\n     *            The initial memory allocation size for the keys sub arrays\n     * @param keySubArraySize\n     *            The maximum size of a keys sub array\n     * @param valueMemoryBlockSize\n     *            The initial memory allocation size for the values sub arrays\n     * @param valueSubArraySize\n     *            The maximum size of a values sub array\n     */\n    public LongToIntegerMultiMap(final String name, final long maximumSize, final int hashSize,\n            final int keyMemoryBlockSize, final int keySubArraySize, final int valueMemoryBlockSize,\n            final int valueSubArraySize)\n    {\n        super(name, maximumSize, hashSize, keyMemoryBlockSize, keySubArraySize,\n                valueMemoryBlockSize, valueSubArraySize);\n    }\n\n    public void add(final Long key, final int value)\n    {\n        final int[] result;\n        if (this.containsKey(key))\n        {\n            result = Arrays.addNewItem(this.get(key), value);\n        }\n        else\n        {\n            result = new int[1];\n            result[0] = value;\n        }\n        this.put(key, result);\n    }\n\n    @Override\n    protected LargeArray<Long> createKeys(final int memoryBlockSize, final int subArraySize)\n    {\n        final LongArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new LongArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new LongArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Keys\");\n        return result;\n    }\n\n    @Override\n    protected LargeArray<int[]> createValues(final int memoryBlockSize, final int subArraySize)\n    {\n        final IntegerArrayOfArrays result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new IntegerArrayOfArrays(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new IntegerArrayOfArrays(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Values\");\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/LongToLongMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoLongToLongMapAdapter;\nimport org.openstreetmap.atlas.utilities.arrays.LargeArray;\nimport org.openstreetmap.atlas.utilities.arrays.LongArray;\n\n/**\n * {@link LargeMap} from {@link Long} to {@link Long}\n *\n * @author matthieun\n * @author lcram\n */\npublic class LongToLongMap extends LargeMap<Long, Long> implements ProtoSerializable\n{\n    private static final long serialVersionUID = -3488197516341341480L;\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     */\n    public LongToLongMap(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     */\n    public LongToLongMap(final long maximumSize, final int hashSize)\n    {\n        super(maximumSize, hashSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     */\n    public LongToLongMap(final String name, final long maximumSize)\n    {\n        super(name, maximumSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     */\n    public LongToLongMap(final String name, final long maximumSize, final int hashSize)\n    {\n        super(name, maximumSize, hashSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     * @param keyMemoryBlockSize\n     *            The initial memory allocation size for the keys sub arrays\n     * @param keySubArraySize\n     *            The maximum size of a keys sub array\n     * @param valueMemoryBlockSize\n     *            The initial memory allocation size for the values sub arrays\n     * @param valueSubArraySize\n     *            The maximum size of a values sub array\n     */\n    public LongToLongMap(final String name, final long maximumSize, final int hashSize,\n            final int keyMemoryBlockSize, final int keySubArraySize, final int valueMemoryBlockSize,\n            final int valueSubArraySize)\n    {\n        super(name, maximumSize, hashSize, keyMemoryBlockSize, keySubArraySize,\n                valueMemoryBlockSize, valueSubArraySize);\n    }\n\n    /**\n     * This nullary constructor is solely for use by the {@link PackedAtlasSerializer}, which calls\n     * it using reflection. It allows the serializer code to obtain a handle on a\n     * {@link LongToLongMap} that it can use to grab the correct {@link ProtoAdapter}. The object\n     * initialized with this constructor will be corrupted for general use and should be discarded.\n     */\n    @SuppressWarnings(\"unused\")\n    private LongToLongMap()\n    {\n        super();\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoLongToLongMapAdapter();\n    }\n\n    @Override\n    protected LargeArray<Long> createKeys(final int memoryBlockSize, final int subArraySize)\n    {\n        final LongArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new LongArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new LongArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Keys\");\n        return result;\n    }\n\n    @Override\n    protected LargeArray<Long> createValues(final int memoryBlockSize, final int subArraySize)\n    {\n        final LongArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new LongArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new LongArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Values\");\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/LongToLongMultiMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport java.util.Objects;\n\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasSerializer;\nimport org.openstreetmap.atlas.proto.ProtoSerializable;\nimport org.openstreetmap.atlas.proto.adapters.ProtoAdapter;\nimport org.openstreetmap.atlas.proto.adapters.ProtoLongToLongMultiMapAdapter;\nimport org.openstreetmap.atlas.utilities.arrays.Arrays;\nimport org.openstreetmap.atlas.utilities.arrays.LargeArray;\nimport org.openstreetmap.atlas.utilities.arrays.LongArray;\nimport org.openstreetmap.atlas.utilities.arrays.LongArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * {@link LargeMap} long to long[]\n *\n * @author matthieun\n * @author lcram\n */\npublic class LongToLongMultiMap extends LargeMap<Long, long[]> implements ProtoSerializable\n{\n    private static final long serialVersionUID = 6741833447370296269L;\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     */\n    public LongToLongMultiMap(final long maximumSize)\n    {\n        super(maximumSize);\n    }\n\n    /**\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     */\n    public LongToLongMultiMap(final long maximumSize, final int hashSize)\n    {\n        super(maximumSize, hashSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     */\n    public LongToLongMultiMap(final String name, final long maximumSize)\n    {\n        super(name, maximumSize);\n    }\n\n    /**\n     * @param name\n     *            The name of the map\n     * @param maximumSize\n     *            The maximum number of keys in the map\n     * @param hashSize\n     *            The size of the hash domain\n     * @param keyMemoryBlockSize\n     *            The initial memory allocation size for the keys sub arrays\n     * @param keySubArraySize\n     *            The maximum size of a keys sub array\n     * @param valueMemoryBlockSize\n     *            The initial memory allocation size for the values sub arrays\n     * @param valueSubArraySize\n     *            The maximum size of a values sub array\n     */\n    public LongToLongMultiMap(final String name, final long maximumSize, final int hashSize,\n            final int keyMemoryBlockSize, final int keySubArraySize, final int valueMemoryBlockSize,\n            final int valueSubArraySize)\n    {\n        super(name, maximumSize, hashSize, keyMemoryBlockSize, keySubArraySize,\n                valueMemoryBlockSize, valueSubArraySize);\n    }\n\n    /**\n     * This nullary constructor is solely for use by the {@link PackedAtlasSerializer}, which calls\n     * it using reflection. It allows the serializer code to obtain a handle on a\n     * {@link LongToLongMultiMap} that it can use to grab the correct {@link ProtoAdapter}. The\n     * object initialized with this constructor will be corrupted for general use and should be\n     * discarded.\n     */\n    @SuppressWarnings(\"unused\")\n    private LongToLongMultiMap()\n    {\n        super();\n    }\n\n    public void add(final Long key, final long value)\n    {\n        final long[] result;\n        if (this.containsKey(key))\n        {\n            result = Arrays.addNewItem(this.get(key), value);\n        }\n        else\n        {\n            result = new long[1];\n            result[0] = value;\n        }\n        this.put(key, result);\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof LongToLongMultiMap)\n        {\n            if (this == other)\n            {\n                return true;\n            }\n            final LongToLongMultiMap that = (LongToLongMultiMap) other;\n            if (!Objects.equals(this.getName(), that.getName()))\n            {\n                return false;\n            }\n            if (this.size() != that.size())\n            {\n                return false;\n            }\n            final Iterable<Long> iterable = () -> this.iterator();\n            for (final Long key : iterable)\n            {\n                if (!that.containsKey(key))\n                {\n                    return false;\n                }\n                final long[] thisValue = this.get(key);\n                final long[] thatValue = that.get(key);\n                if (thisValue.length != thatValue.length)\n                {\n                    return false;\n                }\n                for (int index = 0; index < thisValue.length; index++)\n                {\n                    if (thisValue[index] != thatValue[index])\n                    {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public ProtoAdapter getProtoAdapter()\n    {\n        return new ProtoLongToLongMultiMapAdapter();\n    }\n\n    /*\n     * This hashcode implementation bases the hash on a deeply computed value from the long[]\n     * contents. This is almost certainly undesirable for performance purposes, but it does maintain\n     * the standard relationship between equals() and hashCode() on given objects (namely that two\n     * objects which satisfy equals() will share a hashCode()). Note that this method will likely\n     * never be used and only exists because Checkstyle forces it to be here.\n     */\n    @Override\n    public int hashCode()\n    {\n        final int initialPrime = 31;\n        final int hashSeed = 37;\n\n        final int nameHash = this.getName() == null ? 0 : this.getName().hashCode();\n        int hash = hashSeed * initialPrime + nameHash;\n        hash = hashSeed * hash + Long.valueOf(this.size()).hashCode();\n\n        final Iterable<Long> iterable = () -> this.iterator();\n        for (final Long key : iterable)\n        {\n            final long[] value = this.get(key);\n            for (int index = 0; index < value.length; index++)\n            {\n                hash = hashSeed * hash + Long.valueOf(value[index]).hashCode();\n            }\n        }\n\n        return hash;\n    }\n\n    @Override\n    protected String asValueString(final long[] value)\n    {\n        final StringList list = new StringList();\n        for (final long val : value)\n        {\n            list.add(val);\n        }\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"[\");\n        builder.append(list.join(\", \"));\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    @Override\n    protected LargeArray<Long> createKeys(final int memoryBlockSize, final int subArraySize)\n    {\n        final LongArray result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new LongArray(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new LongArray(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Keys\");\n        return result;\n    }\n\n    @Override\n    protected LargeArray<long[]> createValues(final int memoryBlockSize, final int subArraySize)\n    {\n        final LongArrayOfArrays result;\n        if (memoryBlockSize > 0 && subArraySize > 0)\n        {\n            result = new LongArrayOfArrays(this.getMaximumSize(), memoryBlockSize, subArraySize);\n        }\n        else\n        {\n            result = new LongArrayOfArrays(this.getMaximumSize());\n        }\n        result.withName(getName() + \" - Values\");\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/MultiMap.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.BiFunction;\n\n/**\n * Simple MultiMap backed by a {@link HashMap} and {@link ArrayList}s\n *\n * @author matthieun\n * @param <K>\n *            The Key type\n * @param <V>\n *            The Value type\n */\npublic class MultiMap<K, V> implements Map<K, List<V>>, Serializable\n{\n    private static final long serialVersionUID = -8408806495092086637L;\n\n    private final Map<K, List<V>> map;\n\n    public MultiMap()\n    {\n        this.map = new HashMap<>();\n    }\n\n    /**\n     * If it does not already contain this key, add the key. Then add the value to the key.\n     *\n     * @param key\n     *            The key to add\n     * @param value\n     *            The value to add to the key\n     */\n    public void add(final K key, final V value)\n    {\n        if (!this.map.containsKey(key))\n        {\n            this.map.put(key, new ArrayList<V>());\n        }\n        this.map.get(key).add(value);\n    }\n\n    /**\n     * Merge all the values of another {@link MultiMap}\n     *\n     * @param other\n     *            The other {@link MultiMap} to merge\n     */\n    public void addAll(final MultiMap<K, V> other)\n    {\n        for (final K key : other.keySet())\n        {\n            this.put(key, new ArrayList<>());\n            for (final V value : other.get(key))\n            {\n                this.add(key, value);\n            }\n        }\n    }\n\n    public List<V> allValues()\n    {\n        final List<V> result = new ArrayList<>();\n        for (final List<V> valueList : values())\n        {\n            result.addAll(valueList);\n        }\n        return result;\n    }\n\n    @Override\n    public void clear()\n    {\n        this.map.clear();\n    }\n\n    @Override\n    public boolean containsKey(final Object key)\n    {\n        return this.map.containsKey(key);\n    }\n\n    @Override\n    public boolean containsValue(final Object value)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Set<java.util.Map.Entry<K, List<V>>> entrySet()\n    {\n        return this.map.entrySet();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof MultiMap)\n        {\n            return this.map.equals(((MultiMap<?, ?>) other).map);\n        }\n        return false;\n    }\n\n    @Override\n    public List<V> get(final Object key)\n    {\n        return this.map.get(key);\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.map.hashCode();\n    }\n\n    @Override\n    public boolean isEmpty()\n    {\n        return this.map.isEmpty();\n    }\n\n    @Override\n    public Set<K> keySet()\n    {\n        return this.map.keySet();\n    }\n\n    @Override\n    public List<V> put(final K key, final List<V> value)\n    {\n        return this.map.put(key, value);\n    }\n\n    @Override\n    public void putAll(final Map<? extends K, ? extends List<V>> map)\n    {\n        this.map.putAll(map);\n    }\n\n    public void putAll(final MultiMap<K, V> map)\n    {\n        putAll(map.getMap());\n    }\n\n    public Map<K, V> reduceByKey(final BiFunction<V, V, V> reducer)\n    {\n        final Map<K, V> result = new HashMap<>();\n        for (final K key : keySet())\n        {\n            final List<V> values = get(key);\n            if (values.isEmpty())\n            {\n                continue;\n            }\n            V token = null;\n            for (final V value : values)\n            {\n                if (token == null)\n                {\n                    token = value;\n                }\n                else\n                {\n                    token = reducer.apply(token, value);\n                }\n            }\n            result.put(key, token);\n        }\n        return result;\n    }\n\n    @Override\n    public List<V> remove(final Object key)\n    {\n        return this.map.remove(key);\n    }\n\n    @Override\n    public int size()\n    {\n        return this.map.size();\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.map.toString();\n    }\n\n    @Override\n    public Collection<List<V>> values()\n    {\n        return this.map.values();\n    }\n\n    private Map<K, List<V>> getMap()\n    {\n        return this.map;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/maps/MultiMapWithSet.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Simple SetMultiMap backed by a {@link HashMap} and {@link HashSet}s\n *\n * @author matthieun\n * @param <K>\n *            The Key type\n * @param <V>\n *            The Value type\n */\npublic class MultiMapWithSet<K, V> implements Map<K, Set<V>>, Serializable\n{\n    private static final long serialVersionUID = -8408806495092086637L;\n\n    private final Map<K, Set<V>> map;\n\n    public MultiMapWithSet()\n    {\n        this.map = new HashMap<>();\n    }\n\n    /**\n     * If it does not already contain this key, add the key. Then add the value to the key.\n     *\n     * @param key\n     *            The key to add\n     * @param value\n     *            The value to add to the key\n     */\n    public void add(final K key, final V value)\n    {\n        if (!this.map.containsKey(key))\n        {\n            this.map.put(key, new HashSet<V>());\n        }\n        this.map.get(key).add(value);\n    }\n\n    public List<V> allValues()\n    {\n        final List<V> result = new ArrayList<>();\n        for (final Set<V> valueList : values())\n        {\n            result.addAll(valueList);\n        }\n        return result;\n    }\n\n    @Override\n    public void clear()\n    {\n        this.map.clear();\n    }\n\n    @Override\n    public boolean containsKey(final Object key)\n    {\n        return this.map.containsKey(key);\n    }\n\n    @Override\n    public boolean containsValue(final Object value)\n    {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Set<java.util.Map.Entry<K, Set<V>>> entrySet()\n    {\n        return this.map.entrySet();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof MultiMapWithSet)\n        {\n            return this.map.equals(((MultiMapWithSet<?, ?>) other).map);\n        }\n        return false;\n    }\n\n    @Override\n    public Set<V> get(final Object key)\n    {\n        return this.map.get(key);\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return this.map.hashCode();\n    }\n\n    @Override\n    public boolean isEmpty()\n    {\n        return this.map.isEmpty();\n    }\n\n    @Override\n    public Set<K> keySet()\n    {\n        return this.map.keySet();\n    }\n\n    @Override\n    public Set<V> put(final K key, final Set<V> value)\n    {\n        return this.map.put(key, value);\n    }\n\n    @Override\n    public void putAll(final Map<? extends K, ? extends Set<V>> map)\n    {\n        this.map.putAll(map);\n    }\n\n    public void putAll(final MultiMapWithSet<K, V> map)\n    {\n        putAll(map.getMap());\n    }\n\n    @Override\n    public Set<V> remove(final Object key)\n    {\n        return this.map.remove(key);\n    }\n\n    @Override\n    public int size()\n    {\n        return this.map.size();\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.map.toString();\n    }\n\n    @Override\n    public Collection<Set<V>> values()\n    {\n        return this.map.values();\n    }\n\n    private Map<K, Set<V>> getMap()\n    {\n        return this.map;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/matching/NameMatcher.java",
    "content": "package org.openstreetmap.atlas.utilities.matching;\n\nimport java.io.Serializable;\nimport java.util.function.Predicate;\n\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * A {@code Predicate<String>} utility to find matching names\n *\n * @author brian_l_davis\n */\npublic class NameMatcher implements Serializable, Predicate<String>\n{\n    private static final long serialVersionUID = 5446378702031541204L;\n\n    private static final int DEFAULT_LEVENSHTEIN_DISTANCE_THRESHOLD = 1;\n\n    private final String sourceName;\n\n    // Uses Levenstein Distance to compare strings when true\n    private boolean fuzzyMatch = false;\n    // The minimum number of single-character differences allow to still match (see\n    // https://en.wikipedia.org/wiki/Levenshtein_distance)\n    private int lavenshteinDistanceThreshold = DEFAULT_LEVENSHTEIN_DISTANCE_THRESHOLD;\n    // Null values are considered a match when true\n    private boolean matchNull = false;\n    // Requires strings have the same characters and casing\n    private boolean exactMatch = false;\n\n    public NameMatcher(final String sourceName)\n    {\n        this.sourceName = sourceName;\n    }\n\n    public NameMatcher matchExactly()\n    {\n        this.exactMatch = true;\n        this.fuzzyMatch = false;\n        return this;\n    }\n\n    public NameMatcher matchNulls()\n    {\n        this.matchNull = true;\n        return this;\n    }\n\n    public NameMatcher matchSimilar()\n    {\n        this.exactMatch = false;\n        this.fuzzyMatch = true;\n        return this;\n    }\n\n    public NameMatcher matchSimilar(final int lavenshteinDistanceThreshold)\n    {\n        this.exactMatch = false;\n        this.fuzzyMatch = true;\n        this.lavenshteinDistanceThreshold = lavenshteinDistanceThreshold;\n        return this;\n    }\n\n    @Override\n    public boolean test(final String candidateName)\n    {\n        if (candidateName == null)\n        {\n            return this.matchNull;\n        }\n        if (this.exactMatch)\n        {\n            return candidateName.equals(this.sourceName);\n        }\n        if (this.fuzzyMatch)\n        {\n            return nameFuzzyMatch(this.sourceName, candidateName);\n        }\n        return candidateName.equalsIgnoreCase(this.sourceName);\n    }\n\n    private boolean nameFuzzyMatch(final String nameA, final String nameB)\n    {\n        return nameA.equalsIgnoreCase(nameB) || StringUtils.getLevenshteinDistance(nameA, nameB,\n                this.lavenshteinDistanceThreshold) != -1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/random/RandomTagsSupplier.java",
    "content": "package org.openstreetmap.atlas.utilities.random;\n\nimport java.security.SecureRandom;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Random;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * Supplier for generating random name/value pairs for tags\n *\n * @author cstaylor\n */\npublic class RandomTagsSupplier implements Supplier<Tuple<String, String>>\n{\n    private final Optional<String> fixedName;\n\n    private final Optional<String[]> fixedValues;\n\n    private final Random random;\n\n    private final RandomTextGenerator textGenerator;\n\n    /**\n     * Convenience method that wraps everything up into a Map of name/value pairs\n     *\n     * @param count\n     *            the number of name/value pairs we want to generate\n     * @return the map containing the generated name value pairs\n     */\n    public static Map<String, String> randomTags(final int count)\n    {\n        return Stream.generate(new RandomTagsSupplier()).limit(count).collect(\n                Collectors.toMap(Tuple::getFirst, Tuple::getSecond, (first, second) -> first));\n    }\n\n    /**\n     * Convenience method that wraps everything up into a Map of name/value pairs\n     *\n     * @param count\n     *            the number of name/value pairs we want to generate\n     * @param fixedKey\n     *            the tag key\n     * @param fixedValues\n     *            optional list of values to use instead of completely random text\n     * @return the map containing the generated name value pairs\n     */\n    public static Map<String, String> randomTags(final int count, final String fixedKey,\n            final String... fixedValues)\n    {\n        return Stream.generate(new RandomTagsSupplier(fixedKey, fixedValues)).limit(count).collect(\n                Collectors.toMap(Tuple::getFirst, Tuple::getSecond, (first, second) -> first));\n    }\n\n    /**\n     * Convenience method that wraps everything up into a Map of name/value pairs\n     *\n     * @param count\n     *            the number of name/value pairs we want to generate\n     * @param keyToExclude\n     *            the key we don't want as part of our output\n     * @return the map containing the generated name value pairs\n     */\n    public static Map<String, String> randomTagsExcluding(final int count,\n            final String keyToExclude)\n    {\n        return Stream.generate(new RandomTagsSupplier())\n                .filter(entry -> !entry.getFirst().equals(keyToExclude)).limit(count)\n                .collect(Collectors.toMap(Tuple::getFirst, Tuple::getSecond,\n                        (first, second) -> first));\n    }\n\n    public RandomTagsSupplier()\n    {\n        this(null, null, null);\n    }\n\n    public RandomTagsSupplier(final String fixedName)\n    {\n        this(fixedName, null, null);\n    }\n\n    public RandomTagsSupplier(final String fixedName, final String[] fixedValues)\n    {\n        this(fixedName, fixedValues, null);\n    }\n\n    public RandomTagsSupplier(final String fixedName, final String[] fixedValues,\n            final Random random)\n    {\n        this.fixedName = Optional.ofNullable(fixedName);\n        this.fixedValues = Optional.ofNullable(fixedValues);\n        this.random = random == null ? new SecureRandom() : random;\n        this.textGenerator = new RandomTextGenerator(this.random);\n    }\n\n    @Override\n    public Tuple<String, String> get()\n    {\n        final String key = this.fixedName.orElse(this.textGenerator.newWord());\n        String value = null;\n        if (this.fixedValues.isPresent())\n        {\n            final String[] fixedValueTable = this.fixedValues.get();\n            value = fixedValueTable[this.random.nextInt(fixedValueTable.length)];\n        }\n        else\n        {\n            value = this.textGenerator.newWord();\n        }\n        return new Tuple<>(key, value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/random/RandomTextGenerator.java",
    "content": "package org.openstreetmap.atlas.utilities.random;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.Set;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Generate random text (all English words) for stress tests.\n *\n * @author matthieun\n */\npublic class RandomTextGenerator implements Serializable\n{\n    private static final long serialVersionUID = 1874838119269724332L;\n\n    private static final int MAXIMUM_WORDS_PER_LINE = 10;\n\n    private static final Logger logger = LoggerFactory.getLogger(RandomTextGenerator.class);\n\n    private static final List<String> DICTIONARY;\n\n    static\n    {\n        try\n        {\n            final BufferedReader reader = new BufferedReader(new InputStreamReader(\n                    RandomTextGenerator.class.getResourceAsStream(\"dictionary.txt\")));\n            String line;\n            final Set<String> dictionarySet = new HashSet<>();\n            while ((line = reader.readLine()) != null)\n            {\n                dictionarySet.add(line);\n            }\n            DICTIONARY = new ArrayList<>(dictionarySet);\n        }\n        catch (final IOException e)\n        {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static final int WORD_REPORTING_FREQUENCY = 1_000_000;\n\n    private final Random random;\n\n    private long count = 0;\n\n    public RandomTextGenerator()\n    {\n        this(new Random());\n    }\n\n    public RandomTextGenerator(final Random random)\n    {\n        this.random = random;\n    }\n\n    public String generate(final long sizeInBytes)\n    {\n        final StringBuilder builder = new StringBuilder();\n        int counter = 0;\n        while (builder.length() < sizeInBytes)\n        {\n            if (builder.length() > 0)\n            {\n                if (counter % MAXIMUM_WORDS_PER_LINE == 0)\n                {\n                    builder.append(\"\\n\");\n                }\n                else\n                {\n                    builder.append(\" \");\n                }\n            }\n            builder.append(newWord());\n            counter++;\n        }\n        builder.append(\"\\n\");\n        logger.info(\"Generated text with \" + counter + \" words.\");\n        return builder.toString();\n    }\n\n    public BufferedReader infiniteReader(final String separator)\n    {\n        return new BufferedReader(new Reader()\n        {\n            private boolean closed = false;\n            private String currentWord = newWord();\n            private int index = 0;\n\n            @Override\n            public void close() throws IOException\n            {\n                this.closed = true;\n            }\n\n            @Override\n            public int read(final char[] cbuf, final int off, final int len) throws IOException\n            {\n                if (this.closed)\n                {\n                    throw new RuntimeException(\"Cannot read a closed stream.\");\n                }\n                if (off + len > cbuf.length)\n                {\n                    throw new RuntimeException(\n                            \"Buffer offset + length are larger than buffer size.\");\n                }\n                for (int i = off; i < len; i++)\n                {\n                    cbuf[i] = nextChar();\n                }\n                return len - off;\n            }\n\n            private char nextChar()\n            {\n                if (this.index >= this.currentWord.length())\n                {\n                    nextWord();\n                }\n                return this.currentWord.charAt(this.index++);\n            }\n\n            private void nextWord()\n            {\n                this.index = 0;\n                this.currentWord = separator + newWord();\n            }\n        });\n    }\n\n    public InputStream infiniteStream()\n    {\n        return new InputStream()\n        {\n            private boolean closed = false;\n            private int index = 0;\n            private final BufferedReader reader = infiniteReader(\"\\n\");\n            private String currentLine = newLine();\n\n            @Override\n            public void close() throws IOException\n            {\n                this.closed = true;\n            }\n\n            @Override\n            public int read() throws IOException\n            {\n                if (this.closed)\n                {\n                    throw new RuntimeException(\"Cannot read a closed stream.\");\n                }\n                if (this.index >= this.currentLine.length())\n                {\n                    this.index = 0;\n                    this.currentLine = newLine();\n                    return \"\\n\".charAt(0);\n                }\n                return this.currentLine.charAt(this.index++);\n            }\n\n            private String newLine()\n            {\n                try\n                {\n                    return this.reader.readLine();\n                }\n                catch (final IOException e)\n                {\n                    throw new CoreException(\"Unable to get line.\", e);\n                }\n            }\n        };\n    }\n\n    public String newWord()\n    {\n        if (++this.count % WORD_REPORTING_FREQUENCY == 0)\n        {\n            logger.trace(\"Generated {} random words.\", this.count);\n        }\n        return DICTIONARY.get(this.random.nextInt(DICTIONARY.size()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/regex/RegexUtils.java",
    "content": "package org.openstreetmap.atlas.utilities.regex;\n\nimport java.time.Duration;\nimport java.util.regex.Pattern;\n\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\n\n/**\n * Utility class to avoid compiling patterns all the time.\n *\n * @author Taylor Smock\n */\npublic final class RegexUtils\n{\n    private static final int EXPIRE_DURATION = 120;\n    private static final int MAXIMUM_CACHE = 1000;\n    private static final LoadingCache<String, Pattern> COMPILED_REGEXES = CacheBuilder.newBuilder()\n            .maximumSize(MAXIMUM_CACHE).expireAfterAccess(Duration.ofSeconds(EXPIRE_DURATION))\n            .build(new CacheLoader<>()\n            {\n                @Override\n                public Pattern load(final String key) throws Exception\n                {\n                    return Pattern.compile(key);\n                }\n            });\n\n    /**\n     * Get a specified pattern\n     *\n     * @param pattern\n     *            The pattern that would otherwise be compiled\n     * @return A compiled pattern\n     */\n    public static Pattern getCompiledPattern(final String pattern)\n    {\n        return COMPILED_REGEXES.getUnchecked(pattern);\n    }\n\n    private RegexUtils()\n    {\n        // Don't allow instantiation\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/BoundedPipeBuffer.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.BlockingQueue;\n\n/**\n * Bounded {@link PipeBuffer} based on an {@link ArrayBlockingQueue}\n *\n * @author matthieun\n */\npublic class BoundedPipeBuffer extends PipeBuffer\n{\n    private final int capacity;\n\n    public BoundedPipeBuffer(final int capacity)\n    {\n        this.capacity = capacity;\n    }\n\n    @Override\n    protected BlockingQueue<Byte> createBlockingQueue()\n    {\n        return new ArrayBlockingQueue<>(this.capacity);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/ClassPathTree.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Print the origin of a class in the class loader.\n *\n * @author cstaylor\n * @author matthieun\n */\npublic final class ClassPathTree\n{\n    private static final Logger logger = LoggerFactory.getLogger(ClassPathTree.class);\n\n    public static void print(final Class<?> target)\n    {\n        try\n        {\n            final ClassLoader loader = target.getClassLoader();\n            print(loader);\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Could not print ClassPathTree for {}\", target, e);\n        }\n    }\n\n    public static void print(final String className)\n    {\n        try\n        {\n            print(Class.forName(className));\n        }\n        catch (final ClassNotFoundException e)\n        {\n            logger.error(\"Could not print ClassPathTree for {}\", className, e);\n        }\n    }\n\n    private static void print(final ClassLoader loader)\n    {\n        logger.debug(\"### Classloader Class: {} [{}] ###\\n\\n\", loader.getClass().getName(),\n                System.identityHashCode(loader));\n\n        if (loader instanceof URLClassLoader)\n        {\n            final URL[] urls = ((URLClassLoader) loader).getURLs();\n            for (final URL url : urls)\n            {\n                logger.debug(\"* {}\\n\", url);\n            }\n        }\n        else\n        {\n            logger.debug(\"[Not Using URLClassLoader]\");\n        }\n        logger.debug(\"\");\n        final ClassLoader parent = loader.getParent();\n        if (parent != null)\n        {\n            print(parent);\n        }\n    }\n\n    private ClassPathTree()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/Command.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.EnhancedCollectors;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Run a Command\n *\n * @author matthieun\n * @author tony\n */\npublic abstract class Command\n{\n    /**\n     * A command line flag switch. Example -v\n     */\n    public static class Flag extends Switch<Boolean>\n    {\n        public Flag(final String name, final String description)\n        {\n            super(name, description, value -> Boolean.valueOf(value), Optionality.OPTIONAL,\n                    Boolean.FALSE.toString());\n        }\n\n        public Flag(final String name, final String description, final Optionality optionality)\n        {\n            super(name, description, value -> Boolean.valueOf(value), optionality,\n                    Boolean.FALSE.toString());\n        }\n\n        public Flag(final String name, final String description, final Optionality optionality,\n                final String defaultValue)\n        {\n            super(name, description, value -> Boolean.valueOf(value), optionality, defaultValue);\n        }\n    }\n\n    /**\n     * Optionality for Switch\n     *\n     * @author tony\n     */\n    public enum Optionality\n    {\n        REQUIRED,\n        OPTIONAL\n    }\n\n    /**\n     * A command line switch. Example: -file=~/blah.txt\n     *\n     * @param <T>\n     *            The type returned by the Switch\n     * @author matthieun\n     */\n    public static class Switch<T>\n    {\n        private final String name;\n        private final String description;\n        private final StringConverter<T> converter;\n        private final Optionality optionality;\n        private final String defaultResult;\n\n        public Switch(final String name, final String description,\n                final StringConverter<T> converter)\n        {\n            this(name, description, converter, Optionality.OPTIONAL);\n        }\n\n        public Switch(final String name, final String description,\n                final StringConverter<T> converter, final Optionality optionality)\n        {\n            this.name = name;\n            this.description = description;\n            this.converter = converter;\n            this.optionality = optionality;\n            this.defaultResult = null;\n        }\n\n        public Switch(final String name, final String description,\n                final StringConverter<T> converter, final Optionality optionality,\n                final String defaultResult)\n        {\n            this.name = name;\n            this.description = description;\n            this.converter = converter;\n            this.optionality = optionality;\n            this.defaultResult = defaultResult;\n        }\n\n        @Override\n        public boolean equals(final Object other)\n        {\n            if (other instanceof Switch)\n            {\n                final Switch<?> that = (Switch<?>) other;\n                return this.getName().equals(that.getName())\n                        && this.getDescription().equals(that.getDescription());\n            }\n            return false;\n        }\n\n        public T get(final String value)\n        {\n            if (value == null)\n            {\n                return null;\n            }\n            return this.converter.convert(value);\n        }\n\n        public T getDefault()\n        {\n            return get(this.defaultResult);\n        }\n\n        public String getDescription()\n        {\n            String defaultDescription = \"\";\n            if (this.defaultResult != null)\n            {\n                defaultDescription = \" Default is: \" + this.defaultResult.toString();\n            }\n            return this.description + defaultDescription;\n        }\n\n        public String getName()\n        {\n            return this.name;\n        }\n\n        public Optionality getOptionality()\n        {\n            return this.optionality;\n        }\n\n        @Override\n        public int hashCode()\n        {\n            return this.name.hashCode() + this.description.hashCode() + this.converter.hashCode();\n        }\n\n        @Override\n        public String toString()\n        {\n            return \"[Switch: name = \" + this.getName() + \", description = \" + this.getDescription()\n                    + \"]\";\n        }\n\n        protected String getDefaultResult()\n        {\n            return this.defaultResult;\n        }\n\n        protected boolean hasDefaultResult()\n        {\n            return this.defaultResult != null;\n        }\n    }\n\n    /**\n     * {@link ArrayList} of {@link Switch}es\n     *\n     * @author matthieun\n     */\n    public static class SwitchList extends ArrayList<Switch<?>>\n    {\n        private static final long serialVersionUID = 6310935016772212513L;\n\n        public SwitchList with(final Switch<?>... switches)\n        {\n            Iterables.asList(switches).forEach(aSwitch -> add(aSwitch));\n            return this;\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(Command.class);\n\n    private CommandMap command = null;\n    private Map<Switch<?>, String> lastRawCommand;\n\n    public CommandMap getCommandMap(final String[] arguments)\n    {\n        if (this.command == null)\n        {\n            final Map<String, Switch<?>> switches = switches().stream()\n                    .collect(EnhancedCollectors.toLinkedMap(Switch::getName, Function.identity()));\n            final Map<Switch<?>, String> inputValues = new HashMap<>();\n            final Map<String, String> unknownValues = new HashMap<>();\n            for (int i = 0; i < arguments.length; i++)\n            {\n                final String currentSwitch = arguments[i];\n                if (currentSwitch.startsWith(\"-\"))\n                {\n                    try\n                    {\n                        final String switchString = currentSwitch.substring(1);\n                        final StringList switchStringParts = StringList.split(switchString, \"=\", 2);\n                        final String switchName = switchStringParts.get(0);\n                        // If this switch is a flag, we have special handling\n                        final Switch<?> foundSwitch = switches.get(switchName);\n                        if (foundSwitch instanceof Flag)\n                        {\n                            logger.info(\"Found flag {} with value {}\", switchName, Boolean.TRUE);\n                            inputValues.put(foundSwitch, Boolean.TRUE.toString());\n                        }\n                        else\n                        {\n                            // just exclude switch if split size not equal to 2, it means empty\n                            // string basically. It the switch is required it will fail down the\n                            // line, but if it is optional it will basically just ignore the empty\n                            // string\n                            if (switchStringParts.size() != 2)\n                            {\n                                logger.warn(\"Switch [{}] contains empty string\", switchString);\n                            }\n                            else\n                            {\n                                final String switchValue = switchStringParts.get(1);\n                                if (foundSwitch != null)\n                                {\n                                    inputValues.put(foundSwitch, switchValue);\n                                    logger.info(\"Parsing switch {} -> {}\", switchName, switchValue);\n                                }\n                                else\n                                {\n                                    unknownValues.put(switchName, switchValue);\n                                    logger.warn(\"Unknown switch {} -> {}\", switchName, switchValue);\n                                }\n                            }\n                        }\n                    }\n                    catch (final Exception e)\n                    {\n                        throw new CoreException(\"Problem parsing switch {}\", currentSwitch, e);\n                    }\n                }\n            }\n\n            for (final Switch<?> switchObject : switches.values())\n            {\n                if (!inputValues.containsKey(switchObject))\n                {\n                    if (switchObject.getOptionality() == Optionality.REQUIRED)\n                    {\n                        throw new CoreException(\"Missing Required Switch: {}\", switchObject);\n                    }\n                    if (switchObject.getDefault() != null)\n                    {\n                        inputValues.put(switchObject, switchObject.getDefaultResult());\n                    }\n                    logger.warn(\"Running without switch {}\", switchObject);\n                }\n            }\n\n            this.command = new CommandMap();\n            for (final Switch<?> switchObject : inputValues.keySet())\n            {\n                this.command.put(switchObject.getName(),\n                        switchObject.get(inputValues.get(switchObject)));\n            }\n            this.lastRawCommand = inputValues;\n            this.command.putAll(unknownValues);\n        }\n        return this.command;\n    }\n\n    public void run(final String... arguments)\n    {\n        try\n        {\n            System.exit(execute(arguments));\n        }\n        catch (final Throwable throwable)\n        {\n            logger.error(\"Command execution failed.\", throwable);\n            System.exit(1);\n        }\n    }\n\n    public void runWithoutQuitting(final String... arguments)\n    {\n        try\n        {\n            execute(arguments);\n        }\n        catch (final Throwable throwable)\n        {\n            throw new CoreException(\"Command execution failed.\", throwable);\n        }\n    }\n\n    protected String commandSummary()\n    {\n        final StringList list = new StringList();\n        this.command.forEach((key, value) ->\n        {\n            final StringBuilder builder = new StringBuilder();\n            builder.append(key);\n            builder.append(\" -> \");\n            builder.append(value);\n            list.add(builder.toString());\n        });\n        return list.join(\"\\n\");\n    }\n\n    protected String lastRawCommand(final Switch<?> sswitch)\n    {\n        return this.lastRawCommand.get(sswitch);\n    }\n\n    /**\n     * Run the command\n     *\n     * @param command\n     *            The map of Switch name to converted object from the command line\n     * @return the exit status to return to the caller\n     */\n    protected abstract int onRun(CommandMap command);\n\n    /**\n     * @return The list of expected switches on the command line\n     */\n    protected abstract SwitchList switches();\n\n    private int execute(final String[] arguments)\n    {\n        return onRun(getCommandMap(arguments));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/CommandMap.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.util.HashMap;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\n\n/**\n * Get directly from a Switch.\n *\n * @author matthieun\n */\npublic class CommandMap extends HashMap<String, Object>\n{\n    private static final long serialVersionUID = 670017778010945895L;\n\n    public Object get(final Switch<?> aSwitch)\n    {\n        return get(aSwitch.getName());\n    }\n\n    public Optional<?> getOption(final Switch<?> aSwitch)\n    {\n        final Object object = get(aSwitch);\n        return Optional.ofNullable(object);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/FlexibleCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfoList;\nimport io.github.classgraph.ScanResult;\n\n/**\n * Shell for running subcommands. Reflections is used to find commands and their switchlists are\n * automatically added to the command at runtime. This implementation will search the entire\n * classloader for all FlexibleSubCommand implementations. If you want to restrict the set of\n * commands to support, override the getSupportedCommands method\n *\n * @author cstaylor\n */\npublic class FlexibleCommand extends Command\n{\n    private FlexibleSubCommand subcommand;\n\n    private Map<String, ? extends FlexibleSubCommand> commandMap;\n\n    public static void main(final String... args)\n    {\n        if (args.length == 0)\n        {\n            new FlexibleCommand(\"FAILED\").printUsageAndExit();\n        }\n        new FlexibleCommand(args[0]).run(args);\n    }\n\n    protected static boolean noAbstract(final Class<? extends FlexibleSubCommand> klass)\n    {\n        return !Modifier.isAbstract(klass.getModifiers());\n    }\n\n    private static FlexibleSubCommand create(final Class<? extends FlexibleSubCommand> klass)\n    {\n        try\n        {\n            return klass.newInstance();\n        }\n        catch (final Exception oops)\n        {\n            throw new CoreException(\"Error when creating new instance of {}\",\n                    klass.getCanonicalName(), oops);\n        }\n    }\n\n    public FlexibleCommand(final String... args)\n    {\n        initializeCommands();\n        if (args.length == 0)\n        {\n            printUsageAndExit();\n        }\n    }\n\n    @Override\n    public void run(final String... arguments)\n    {\n        final String[] commandArgs = prepare(arguments);\n        super.run(commandArgs);\n    }\n\n    @Override\n    public void runWithoutQuitting(final String... arguments)\n    {\n        final String[] commandArgs = prepare(arguments);\n        super.runWithoutQuitting(commandArgs);\n    }\n\n    /**\n     * Default behavior finds all FlexibleSubCommands in the classpath. Override to restrict the set\n     * of classes returned\n     *\n     * @return a stream containing all of the subcommands we want to support\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected Stream<Class<? extends FlexibleSubCommand>> getSupportedCommands()\n    {\n        final List<Class<? extends FlexibleSubCommand>> returnValue = new ArrayList<>();\n        try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan())\n        {\n            final ClassInfoList classInfoList = scanResult\n                    .getClassesImplementing(FlexibleSubCommand.class.getName());\n            classInfoList.loadClasses()\n                    .forEach(klass -> returnValue.add((Class<? extends FlexibleSubCommand>) klass));\n        }\n        return returnValue.stream();\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        return this.subcommand.execute(command);\n    }\n\n    protected void printUsageAndExit()\n    {\n        printUsageAndExit(-1);\n    }\n\n    protected void printUsageAndExit(final int errorCode)\n    {\n        this.commandMap.entrySet().stream().forEach(item ->\n        {\n            System.err.printf(\"%s - %s\\n\", item.getKey(), item.getValue().getDescription());\n            item.getValue().usage(System.err);\n            System.err.printf(\"\\n\");\n        });\n        System.exit(errorCode);\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return this.subcommand.switches();\n    }\n\n    private void initializeCommands()\n    {\n        this.commandMap = getSupportedCommands().filter(FlexibleCommand::noAbstract)\n                .map(FlexibleCommand::create)\n                .collect(Collectors.toMap(FlexibleSubCommand::getName, x -> x));\n    }\n\n    private String[] prepare(final String... arguments)\n    {\n        this.subcommand = this.commandMap.get(arguments[0]);\n        if (this.subcommand == null)\n        {\n            printUsageAndExit();\n        }\n        /**\n         * With FlexibleCommands, the first argument is the name of the subcommand. We're peeling\n         * that argument off the args array and passing that value to the FlexibleCommand\n         * constructor. If no subcommand matches that name, we immediately terminate with a usage\n         * error, otherwise we continue to run using the remaining arguments copied heres\n         */\n        final String[] commandArgs = new String[arguments.length - 1];\n        System.arraycopy(arguments, 1, commandArgs, 0, commandArgs.length);\n        return commandArgs;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/FlexibleSubCommand.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.io.PrintStream;\n\nimport org.openstreetmap.atlas.utilities.runtime.Command.SwitchList;\n\n/**\n * All subcommands implement this interface\n *\n * @author cstaylor\n */\npublic interface FlexibleSubCommand\n{\n    /**\n     * Subcommands will override this method to do whatever they want (display metadata, join\n     * multiple atlas files, etc...)\n     *\n     * @param map\n     *            the command line parameters passed to the reader command\n     * @return the exit status returned to the operating system\n     */\n    int execute(CommandMap map);\n\n    /**\n     * Explains what this subcommand does\n     *\n     * @return the text description of the command\n     */\n    String getDescription();\n\n    /**\n     * The name of the command. I suggest you make this all lowercase characters. This will become\n     * the first parameter to the command, so it must be unique within the group of all subcommands\n     *\n     * @return the name of the command\n     */\n    String getName();\n\n    /**\n     * Each subcommand can have its own unique set of command-line switches. If no command-line\n     * switches are needed, return an empty SwitchList\n     *\n     * @return the list of switches supported by this subcommand\n     */\n    SwitchList switches();\n\n    /**\n     * Output usage information for this command to the stream\n     *\n     * @param writer\n     *            where we should write the parameter usage for this subcommand\n     */\n    void usage(PrintStream writer);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/OpenPipeBuffer.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\n\n/**\n * An unbounded {@link PipeBuffer} (still limited at Integer.MAX_VALUE) using an underlying\n * {@link LinkedBlockingQueue}\n *\n * @author matthieun\n */\npublic class OpenPipeBuffer extends PipeBuffer\n{\n\n    @Override\n    protected BlockingQueue<Byte> createBlockingQueue()\n    {\n        // Unbounded, dies at Integer.MAX_VALUE\n        return new LinkedBlockingQueue<>();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/PipeBuffer.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\n\n/**\n * Create an {@link InputStream} from an {@link OutputStream} and handle buffering using a\n * predefined size FIFO. Both can be read by different threads\n *\n * @author matthieun\n */\npublic abstract class PipeBuffer\n{\n    private static final Duration AVOID_DEADLOCK_DELAY = Duration.milliseconds(10);\n\n    private BlockingQueue<Byte> queue;\n    private InputStream input;\n    private OutputStream output;\n    private boolean outClosed = false;\n    private boolean inClosed = false;\n    private Supplier<Boolean> outClosedAction = () -> true;\n    private Supplier<Boolean> inClosedAction = () -> true;\n\n    /**\n     * @return The {@link InputStream} to read from from a thread\n     */\n    public InputStream input()\n    {\n        if (this.input != null)\n        {\n            throw new IllegalAccessError(\"Cannot create a new pipe from the same PipeBuffer.\"\n                    + \"There needs to be a new PipeBuffer object created for this.\");\n        }\n        if (this.output == null)\n        {\n            initialize();\n        }\n        this.input = new InputStream()\n        {\n            @Override\n            public void close()\n            {\n                PipeBuffer.this.inClosed = true;\n                PipeBuffer.this.inClosedAction.get();\n            }\n\n            @Override\n            public int read() throws IOException\n            {\n                Byte result = PipeBuffer.this.queue.poll();\n                while (result == null && !PipeBuffer.this.outClosed)\n                {\n                    // Using this instead of the blocking take() avoids deadlocks when the out is\n                    // closed after the take() was entered\n                    AVOID_DEADLOCK_DELAY.sleep();\n                    result = PipeBuffer.this.queue.poll();\n                }\n                if (result == null)\n                {\n                    return -1;\n                }\n                else\n                {\n                    return result;\n                }\n            }\n        };\n        return this.input;\n    }\n\n    /**\n     * @return The {@link OutputStream} to write to from a thread\n     */\n    public OutputStream out()\n    {\n        if (this.output != null)\n        {\n            throw new IllegalAccessError(\"Cannot create a new pipe from the same PipeBuffer.\"\n                    + \"There needs to be a new PipeBuffer object created for this.\");\n        }\n        if (this.input == null)\n        {\n            initialize();\n        }\n        this.output = new OutputStream()\n        {\n            @Override\n            public void close()\n            {\n                PipeBuffer.this.outClosed = true;\n                PipeBuffer.this.outClosedAction.get();\n            }\n\n            @Override\n            public void write(final int byteValue) throws IOException\n            {\n                final boolean added = add(byteValue);\n                while (!added && !PipeBuffer.this.inClosed)\n                {\n                    AVOID_DEADLOCK_DELAY.sleep();\n                    add(byteValue);\n                }\n                if (!added)\n                {\n                    throw new IOException(\"Consuming InputStream has been closed.\");\n                }\n            }\n\n            private boolean add(final int byteValue)\n            {\n                try\n                {\n                    PipeBuffer.this.queue.add((byte) byteValue);\n                    return true;\n                }\n                catch (final IllegalStateException e)\n                {\n                    return false;\n                }\n            }\n        };\n        return this.output;\n    }\n\n    /**\n     * @return the size of the pipe buffer\n     */\n    public int size()\n    {\n        return this.queue.size();\n    }\n\n    public PipeBuffer withInClosedAction(final Supplier<Boolean> inClosedAction)\n    {\n        this.inClosedAction = inClosedAction;\n        return this;\n    }\n\n    public PipeBuffer withOutClosedAction(final Supplier<Boolean> outClosedAction)\n    {\n        this.outClosedAction = outClosedAction;\n        return this;\n    }\n\n    /**\n     * @return The queue used to cache the contents of this pipe\n     */\n    protected abstract BlockingQueue<Byte> createBlockingQueue();\n\n    private void initialize()\n    {\n        if (this.queue != null)\n        {\n            throw new IllegalAccessError(\"Cannot create a new pipe from the same PipeBuffer.\"\n                    + \"There needs to be a new PipeBuffer object created for this.\");\n        }\n        this.queue = createBlockingQueue();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/Retry.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Simple retry of a job\n *\n * @author matthieun\n */\npublic class Retry\n{\n    private static final Logger logger = LoggerFactory.getLogger(Retry.class);\n    private static final Predicate<Throwable> FILTER_NONE = throwable -> false;\n\n    private final int retries;\n    private final Duration waitBeforeRetry;\n    private boolean quiet = false;\n    private boolean quadratic = false;\n\n    /**\n     * @param retries\n     *            The number of allowed retries\n     * @param waitBeforeRetry\n     *            The {@link Duration} to wait for before each retry\n     */\n    public Retry(final int retries, final Duration waitBeforeRetry)\n    {\n        this.retries = Math.max(retries, 0);\n        this.waitBeforeRetry = waitBeforeRetry;\n    }\n\n    /**\n     * @return True if the Retry is quadratic\n     */\n    public boolean isQuadratic()\n    {\n        return this.quadratic;\n    }\n\n    /**\n     * @return True if the Retry is quiet\n     */\n    public boolean isQuiet()\n    {\n        return this.quiet;\n    }\n\n    /**\n     * Run a {@link Runnable} task with some retries\n     *\n     * @param runnable\n     *            The task\n     */\n    public void run(final Runnable runnable)\n    {\n        run(runnable, null, FILTER_NONE);\n    }\n\n    /**\n     * @param runnable\n     *            The task\n     * @param exceptionsWhichShouldBreakDirectly\n     *            A filter that chooses what exceptions are to break directly, without triggering a\n     *            retry\n     */\n    public void run(final Runnable runnable,\n            final Predicate<Throwable> exceptionsWhichShouldBreakDirectly)\n    {\n        run(runnable, null, exceptionsWhichShouldBreakDirectly);\n    }\n\n    /**\n     * Run a {@link Runnable} task with some retries\n     *\n     * @param runnable\n     *            The task\n     * @param runBeforeRetry\n     *            What to do before retrying\n     */\n    public void run(final Runnable runnable, final Runnable runBeforeRetry)\n    {\n        run(runnable, runBeforeRetry, FILTER_NONE);\n    }\n\n    /**\n     * @param runnable\n     *            The task\n     * @param runBeforeRetry\n     *            What to do before retrying\n     * @param exceptionsWhichShouldBreakDirectly\n     *            A filter that chooses what exceptions are to break directly, without triggering a\n     *            retry\n     */\n    public void run(final Runnable runnable, final Runnable runBeforeRetry,\n            final Predicate<Throwable> exceptionsWhichShouldBreakDirectly)\n    {\n        // Use the Callable implementation\n        run(() ->\n        {\n            runnable.run();\n            return \"\";\n        }, runBeforeRetry, exceptionsWhichShouldBreakDirectly);\n    }\n\n    /**\n     * Run a {@link Supplier} task with some retries\n     *\n     * @param callable\n     *            The task\n     * @param <V>\n     *            The return type of the callable\n     * @return The result of the first successful call\n     */\n    public <V> V run(final Supplier<V> callable)\n    {\n        return run(callable, null, FILTER_NONE);\n    }\n\n    /**\n     * Run a {@link Supplier} task with some retries\n     *\n     * @param callable\n     *            The task\n     * @param <V>\n     *            The return type of the callable\n     * @param exceptionsWhichShouldBreakDirectly\n     *            A filter that chooses what exceptions are to break directly, without triggering a\n     *            retry\n     * @return The result of the first successful call\n     */\n    public <V> V run(final Supplier<V> callable,\n            final Predicate<Throwable> exceptionsWhichShouldBreakDirectly)\n    {\n        return run(callable, null, exceptionsWhichShouldBreakDirectly);\n    }\n\n    /**\n     * Run a {@link Supplier} task with some retries\n     *\n     * @param callable\n     *            The task\n     * @param <V>\n     *            The return type of the callable\n     * @param runBeforeRetry\n     *            What to do before retrying\n     * @return The result of the first successful call\n     */\n    public <V> V run(final Supplier<V> callable, final Runnable runBeforeRetry)\n    {\n        return run(callable, runBeforeRetry, FILTER_NONE);\n    }\n\n    /**\n     * Run a {@link Supplier} task with some retries\n     *\n     * @param callable\n     *            The task\n     * @param <V>\n     *            The return type of the callable\n     * @param runBeforeRetry\n     *            What to do before retrying\n     * @param exceptionsWhichShouldBreakDirectly\n     *            A filter that chooses what exceptions are to break directly, without triggering a\n     *            retry\n     * @return The result of the first successful call\n     */\n    public <V> V run(final Supplier<V> callable, final Runnable runBeforeRetry, // NOSONAR\n            final Predicate<Throwable> exceptionsWhichShouldBreakDirectly)\n    {\n        int retry = 0;\n        V result = null;\n        boolean success = false;\n        Throwable lastError = null;\n        while (!success && retry <= this.retries)\n        {\n            try\n            {\n                if (retry > 0 && runBeforeRetry != null)\n                {\n                    runBeforeRetry.run();\n                }\n                result = callable.get();\n                success = true;\n            }\n            catch (final Throwable throwable) // NOSONAR\n            {\n                if (exceptionsWhichShouldBreakDirectly.test(throwable))\n                {\n                    throw throwable;\n                }\n                if (!this.quiet && logger.isErrorEnabled())\n                {\n                    logger.error(\"Failed retry number {}\", retry, throwable);\n                }\n                lastError = throwable;\n                retry++;\n                wait(retry);\n            }\n        }\n        if (success)\n        {\n            return result;\n        }\n        else\n        {\n            throw new CoreException(\"Failed execution after {} retries.\", retry, lastError);\n        }\n    }\n\n    /**\n     * @param quadratic\n     *            True to wait twice as long as the previous retry\n     */\n    public void setQuadratic(final boolean quadratic)\n    {\n        this.quadratic = quadratic;\n    }\n\n    /**\n     * @param quiet\n     *            True to suppress logging of errors when a retry is happening.\n     */\n    public void setQuiet(final boolean quiet)\n    {\n        this.quiet = quiet;\n    }\n\n    /**\n     * @param quadratic\n     *            True to wait twice as long as the previous retry\n     * @return This\n     */\n    public Retry withQuadratic(final boolean quadratic)\n    {\n        this.setQuadratic(quadratic);\n        return this;\n    }\n\n    /**\n     * @param quiet\n     *            True to suppress logging of errors when a retry is happening.\n     * @return This\n     */\n    public Retry withQuiet(final boolean quiet)\n    {\n        this.setQuiet(quiet);\n        return this;\n    }\n\n    private void wait(final int retry)\n    {\n        if (retry > 1 && this.quadratic)\n        {\n            final Duration quadraticWait = this.waitBeforeRetry.times(Math.pow(2.0, retry - 1.0));\n            if (logger.isInfoEnabled())\n            {\n                logger.info(\"Wait is quadratic. Waiting {}\", quadraticWait);\n            }\n            quadraticWait.sleep();\n        }\n        else\n        {\n            this.waitBeforeRetry.sleep();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/RunScript.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.SplittableInputStream;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.utilities.runtime.RunScriptMonitor.PrinterMonitor;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic final class RunScript\n{\n    private static final Logger logger = LoggerFactory.getLogger(RunScript.class);\n\n    public static void run(final String command)\n    {\n        run(command.split(\"\\\\s+\"));\n    }\n\n    public static void run(final String command, final List<RunScriptMonitor> monitors)\n    {\n        run(command.split(\"\\\\s+\"), monitors);\n    }\n\n    public static void run(final String[] commandArray)\n    {\n        run(commandArray, new ArrayList<>());\n    }\n\n    public static void run(final String[] commandArray, final List<RunScriptMonitor> monitors)\n    {\n        int returnValue = 0;\n        try\n        {\n            final String[] env = System.getenv().entrySet().stream()\n                    .map(entry -> entry.getKey() + \"=\" + entry.getValue())\n                    .collect(Collectors.toList()).toArray(new String[0]);\n            final Process process = Runtime.getRuntime().exec(commandArray, env);\n            final PrinterMonitor printer = new PrinterMonitor(logger);\n            SplittableInputStream standardOut = null;\n            SplittableInputStream standardErr = null;\n\n            if (monitors != null && !monitors.isEmpty())\n            {\n                standardOut = new SplittableInputStream(process.getInputStream());\n                standardErr = new SplittableInputStream(process.getErrorStream());\n\n                final List<InputStream> otherStandardOuts = new ArrayList<>();\n                final List<InputStream> otherStandardErrs = new ArrayList<>();\n                for (@SuppressWarnings(\"unused\")\n                final RunScriptMonitor monitor : monitors)\n                {\n                    otherStandardOuts.add(standardOut.split());\n                    otherStandardErrs.add(standardErr.split());\n                }\n\n                // Launch the output monitors\n                printer.parse(standardOut, standardErr);\n                for (int index = 0; index < monitors.size(); index++)\n                {\n                    final RunScriptMonitor monitor = monitors.get(index);\n                    final InputStream otherStandardOut = otherStandardOuts.get(index);\n                    final InputStream otherStandardErr = otherStandardErrs.get(index);\n                    monitor.parse(otherStandardOut, otherStandardErr);\n                }\n            }\n            else\n            {\n                printer.parse(process.getInputStream(), process.getErrorStream());\n            }\n\n            returnValue = process.waitFor();\n\n            // Wait for the monitors\n            printer.waitForCompletion(Duration.ONE_SECOND);\n\n            if (monitors != null && !monitors.isEmpty())\n            {\n                for (final RunScriptMonitor monitor : monitors)\n                {\n                    monitor.waitForCompletion(Duration.ONE_SECOND);\n                }\n\n                Streams.close(standardOut);\n                Streams.close(standardErr);\n            }\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not launch script \\\"{}\\\"\", commandArray, e);\n        }\n        if (returnValue != 0)\n        {\n            throw new CoreException(\"Non-Zero return value {} when running script \\\"{}\\\".\",\n                    returnValue, commandArray);\n        }\n    }\n\n    private RunScript()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/RunScriptMonitor.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.io.InputStream;\nimport java.util.concurrent.TimeoutException;\n\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\nimport org.openstreetmap.atlas.utilities.threads.Result;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Monitor for the output of a {@link Process} started with {@link RunScript}.\n *\n * @author matthieun\n */\npublic abstract class RunScriptMonitor\n{\n    /**\n     * Print the logs of a {@link Process} using a {@link Logger}\n     *\n     * @author matthieun\n     */\n    public static class PrinterMonitor extends RunScriptMonitor\n    {\n        private final Logger logger;\n\n        public PrinterMonitor(final Logger logger)\n        {\n            this.logger = logger;\n        }\n\n        @Override\n        protected void parseStandardError(final Iterable<String> lines)\n        {\n            parseStream(lines);\n        }\n\n        @Override\n        protected void parseStandardOutput(final Iterable<String> lines)\n        {\n            parseStream(lines);\n        }\n\n        private void parseStream(final Iterable<String> lines)\n        {\n            lines.forEach(line -> this.logger.info(line));\n        }\n    }\n\n    private static final Duration REFRESH_DURATION = Duration.milliseconds(100);\n\n    private static final Logger logger = LoggerFactory.getLogger(RunScriptMonitor.class);\n\n    private Thread out;\n    private Thread err;\n\n    /**\n     * Parse the output logs of a {@link Process}.\n     *\n     * @param standardOut\n     *            The output stream for standard output\n     * @param standardErr\n     *            The output stream for standard error\n     */\n    protected void parse(final InputStream standardOut, final InputStream standardErr)\n    {\n        this.out = new Thread(\n                () -> parseStandardOutput(new InputStreamResource(() -> standardOut).lines()));\n        this.err = new Thread(\n                () -> parseStandardError(new InputStreamResource(() -> standardErr).lines()));\n        this.out.setPriority(Thread.MAX_PRIORITY);\n        this.err.setPriority(Thread.MAX_PRIORITY);\n        this.out.start();\n        this.err.start();\n    }\n\n    /**\n     * Parse the standard error stream\n     *\n     * @param lines\n     *            The stream\n     */\n    protected abstract void parseStandardError(Iterable<String> lines);\n\n    /**\n     * Parse the standard output stream\n     *\n     * @param lines\n     *            The stream\n     */\n    protected abstract void parseStandardOutput(Iterable<String> lines);\n\n    /**\n     * Make sure that the logs are parsed before the end.\n     *\n     * @param maximum\n     *            The maximum duration to wait for.\n     */\n    protected void waitForCompletion(final Duration maximum)\n    {\n        try (Pool waiter = new Pool(1, \"waiter\", Duration.ONE_SECOND))\n        {\n            final Result<Boolean> result = waiter.queue(() ->\n            {\n                while (this.out.isAlive() || this.err.isAlive())\n                {\n                    REFRESH_DURATION.sleep();\n                }\n                return true;\n            });\n            result.get(maximum);\n        }\n        catch (final TimeoutException e)\n        {\n            logger.warn(\"RunScript logs monitor did not finish in {}.\", maximum);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/SingleLineMonitor.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.util.Optional;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Monitor with single lines to extract some result value\n *\n * @author matthieun\n */\npublic abstract class SingleLineMonitor extends RunScriptMonitor\n{\n    private static final Logger logger = LoggerFactory.getLogger(SingleLineMonitor.class);\n\n    private Optional<String> result = Optional.empty();\n\n    public Optional<String> getResult()\n    {\n        return this.result;\n    }\n\n    protected abstract Optional<String> parseResult(String line);\n\n    @Override\n    protected void parseStandardError(final Iterable<String> lines)\n    {\n        parseStandardStream(lines);\n    }\n\n    @Override\n    protected void parseStandardOutput(final Iterable<String> lines)\n    {\n        parseStandardStream(lines);\n    }\n\n    private void parseStandardStream(final Iterable<String> lines)\n    {\n        for (final String line : lines)\n        {\n            // Use the double-if check around the \"synchronized\" trick, to avoid synchronizing\n            // on every line.\n            if (!this.result.isPresent())\n            {\n                final Optional<String> resultOption = parseResult(line);\n                if (resultOption.isPresent())\n                {\n                    synchronized (SingleLineMonitor.class)\n                    {\n                        if (!this.result.isPresent())\n                        {\n                            this.result = resultOption;\n                            logger.trace(\"Found result: {}\", resultOption.get());\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/TimedRetry.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.util.concurrent.TimeoutException;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\n\n/**\n * @author matthieun\n */\npublic class TimedRetry\n{\n    private static final String TIMEOUT_MESSAGE = \"Timeout Error: \";\n\n    private final int retries;\n\n    public TimedRetry(final int retries)\n    {\n        this.retries = retries;\n    }\n\n    public <Value> Value run(final Supplier<Value> callable, final Duration timeBeforeRetry)\n    {\n        final Retry retry = new Retry(this.retries, Duration.ZERO);\n        final Predicate<Throwable> exceptionsWhichShouldBreakDirectly = error -> !error.getMessage()\n                .startsWith(TIMEOUT_MESSAGE);\n        return retry.run(() ->\n        {\n            try (Pool pool = new Pool(1, Thread.currentThread().getName() + \" # TimedRetry\",\n                    timeBeforeRetry.add(Duration.ONE_SECOND)))\n            {\n                return pool.queue(() -> callable.get()).get(timeBeforeRetry);\n            }\n            catch (final TimeoutException e)\n            {\n                throw new CoreException(TIMEOUT_MESSAGE + \"Timeout in TimedRetry call\", e);\n            }\n        }, exceptionsWhichShouldBreakDirectly);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/system/SystemInfo.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime.system;\n\nimport java.io.Serializable;\nimport java.lang.management.ClassLoadingMXBean;\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.MemoryMXBean;\nimport java.lang.management.MemoryPoolMXBean;\nimport java.lang.management.MemoryUsage;\nimport java.lang.management.OperatingSystemMXBean;\nimport java.lang.management.RuntimeMXBean;\nimport java.text.DecimalFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.utilities.runtime.system.memory.Memory;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author cstaylor\n * @author matthieun\n */\npublic final class SystemInfo\n{\n    /**\n     * @author cstaylor\n     */\n    public static class SystemInfoBean implements Serializable\n    {\n        /**\n         * @author cstaylor\n         */\n        public static class MemoryPoolBean\n        {\n            private final String name;\n            private final String managerNames;\n            private final Memory currentInitialized;\n            private final Memory currentUsed;\n            private final Memory currentCommitted;\n            private final Memory currentMaximum;\n            private final Memory peakInitialized;\n            private final Memory peakUsed;\n            private final Memory peakCommitted;\n            private final Memory peakMaximum;\n            private Memory collectionInitialized;\n            private Memory collectionUsed;\n            private Memory collectionCommitted;\n            private Memory collectionMaximum;\n            private final boolean collection;\n\n            public MemoryPoolBean(final MemoryPoolMXBean pool)\n            {\n                final MemoryUsage currentUsage = pool.getUsage();\n                final MemoryUsage peakUsage = pool.getPeakUsage();\n                final MemoryUsage collectionUsage = pool.getCollectionUsage();\n                this.collection = collectionUsage != null;\n                this.name = pool.getName();\n                this.managerNames = Arrays.toString(pool.getMemoryManagerNames());\n\n                this.currentInitialized = Memory.bytes(currentUsage.getInit());\n                this.currentUsed = Memory.bytes(currentUsage.getUsed());\n                this.currentCommitted = Memory.bytes(currentUsage.getCommitted());\n                this.currentMaximum = Memory.bytes(currentUsage.getMax());\n\n                this.peakInitialized = Memory.bytes(peakUsage.getInit());\n                this.peakUsed = Memory.bytes(peakUsage.getUsed());\n                this.peakCommitted = Memory.bytes(peakUsage.getCommitted());\n                this.peakMaximum = Memory.bytes(peakUsage.getMax());\n\n                if (this.collection)\n                {\n                    this.collectionInitialized = Memory.bytes(collectionUsage.getInit());\n                    this.collectionUsed = Memory.bytes(collectionUsage.getUsed());\n                    this.collectionCommitted = Memory.bytes(collectionUsage.getCommitted());\n                    this.collectionMaximum = Memory.bytes(collectionUsage.getMax());\n                }\n            }\n\n            public String getCollectionCommitted()\n            {\n                return this.collectionCommitted.toString();\n            }\n\n            public String getCollectionInitialized()\n            {\n                return this.collectionInitialized.toString();\n            }\n\n            public String getCollectionMaximum()\n            {\n                return this.collectionMaximum.toString();\n            }\n\n            public String getCollectionUsed()\n            {\n                return this.collectionUsed.toString();\n            }\n\n            public String getCurrentCommitted()\n            {\n                return this.currentCommitted.toString();\n            }\n\n            public String getCurrentInitialized()\n            {\n                return this.currentInitialized.toString();\n            }\n\n            public String getCurrentMaximum()\n            {\n                return this.currentMaximum.toString();\n            }\n\n            public String getCurrentUsed()\n            {\n                return this.currentUsed.toString();\n            }\n\n            public String getManagerNames()\n            {\n                return this.managerNames;\n            }\n\n            public String getName()\n            {\n                return this.name;\n            }\n\n            public String getPeakCommitted()\n            {\n                return this.peakCommitted.toString();\n            }\n\n            public String getPeakInitialized()\n            {\n                return this.peakInitialized.toString();\n            }\n\n            public String getPeakMaximum()\n            {\n                return this.peakMaximum.toString();\n            }\n\n            public String getPeakUsed()\n            {\n                return this.peakUsed.toString();\n            }\n\n            public boolean isCollection()\n            {\n                return this.collection;\n            }\n\n            @Override\n            public String toString()\n            {\n                return toString(\"\");\n            }\n\n            public String toString(final String header)\n            {\n                final StringBuilder builder = new StringBuilder();\n                builder.append(header);\n                builder.append(\"Name: \");\n                builder.append(this.name);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Manager Names: \");\n                builder.append(this.managerNames);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Current Init: \");\n                builder.append(this.currentInitialized);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Current USed: \");\n                builder.append(this.currentUsed);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Current Committed: \");\n                builder.append(this.currentCommitted);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Current Maximum: \");\n                builder.append(this.currentMaximum);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Peak Init: \");\n                builder.append(this.peakInitialized);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Peak USed: \");\n                builder.append(this.peakUsed);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Peak Committed: \");\n                builder.append(this.peakCommitted);\n                builder.append(System.lineSeparator());\n                builder.append(header);\n                builder.append(\"Peak Maximum: \");\n                builder.append(this.peakMaximum);\n                if (this.collection)\n                {\n                    builder.append(System.lineSeparator());\n                    builder.append(header);\n                    builder.append(\"Collection Initialized: \");\n                    builder.append(this.collectionInitialized);\n                    builder.append(System.lineSeparator());\n                    builder.append(header);\n                    builder.append(\"Collection USed: \");\n                    builder.append(this.collectionUsed);\n                    builder.append(System.lineSeparator());\n                    builder.append(header);\n                    builder.append(\"Collection Committed: \");\n                    builder.append(this.collectionCommitted);\n                    builder.append(System.lineSeparator());\n                    builder.append(header);\n                    builder.append(\"Collection Maximum: \");\n                    builder.append(this.collectionMaximum);\n                }\n                return builder.toString();\n            }\n\n        }\n\n        private static final long serialVersionUID = 8527234032101389715L;\n\n        private String vmName;\n        private String vmVendor;\n        private String vmVersion;\n        private Date startTime;\n        private Duration upTime;\n        private List<String> vmArgs;\n        private int currentlyLoadedClasses;\n        private long totalLoadedClasses;\n        private long totalUnloadedClasses;\n        private String nativeArchitecture;\n        private int cpus;\n        private String osName;\n        private String osVersion;\n        private Memory heapInitialized;\n        private Memory heapCommitted;\n        private Memory heapUsed;\n        private Memory heapMaximum;\n        private Memory nonHeapInitialized;\n        private Memory nonHeapCommitted;\n        private Memory nonHeapUsed;\n        private Memory nonHeapMaximum;\n\n        private final List<MemoryPoolMXBean> memoryPools = new ArrayList<>();\n        private String vmSpecVersion;\n\n        public void add(final Collection<MemoryPoolMXBean> beans)\n        {\n            this.memoryPools.addAll(beans);\n        }\n\n        public void add(final MemoryPoolMXBean currentBean)\n        {\n            this.memoryPools.add(currentBean);\n        }\n\n        public int getCpus()\n        {\n            return this.cpus;\n        }\n\n        public String getCurrentlyLoadedClasses()\n        {\n            return DecimalFormat.getIntegerInstance().format(this.currentlyLoadedClasses);\n        }\n\n        public String getHeapCommitted()\n        {\n            return this.heapCommitted.toString();\n        }\n\n        public String getHeapInitialized()\n        {\n            return this.heapInitialized.toString();\n        }\n\n        public String getHeapMaximum()\n        {\n            return this.heapMaximum.toString();\n        }\n\n        public String getHeapUsed()\n        {\n            return this.heapUsed.toString();\n        }\n\n        public List<MemoryPoolBean> getMemoryPools()\n        {\n            final ArrayList<MemoryPoolBean> result = new ArrayList<>();\n            for (final MemoryPoolMXBean pool : this.memoryPools)\n            {\n                result.add(new MemoryPoolBean(pool));\n            }\n            return result;\n        }\n\n        public String getNativeArchitecture()\n        {\n            return this.nativeArchitecture;\n        }\n\n        public String getNonHeapCommitted()\n        {\n            return this.nonHeapCommitted.toString();\n        }\n\n        public String getNonHeapInitialized()\n        {\n            return this.nonHeapInitialized.toString();\n        }\n\n        public String getNonHeapMaximum()\n        {\n            return this.nonHeapMaximum.toString();\n        }\n\n        public String getNonHeapUsed()\n        {\n            return this.nonHeapUsed.toString();\n        }\n\n        public String getOsName()\n        {\n            return this.osName;\n        }\n\n        public String getOsVersion()\n        {\n            return this.osVersion;\n        }\n\n        public String getStartTime()\n        {\n            return this.startTime == null ? \"\"\n                    : new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(this.startTime);\n        }\n\n        public String getTotalLoadedClasses()\n        {\n            return DecimalFormat.getIntegerInstance().format(this.totalLoadedClasses);\n        }\n\n        public String getTotalUnloadedClasses()\n        {\n            return DecimalFormat.getIntegerInstance().format(this.totalUnloadedClasses);\n        }\n\n        public String getUpTime()\n        {\n            return this.upTime == null ? \"\" : this.upTime.toString();\n        }\n\n        public List<String> getVmArgs()\n        {\n            return this.vmArgs;\n        }\n\n        public String getVmName()\n        {\n            return this.vmName;\n        }\n\n        public String getVmSpecVersion()\n        {\n            return this.vmSpecVersion;\n        }\n\n        public String getVmVendor()\n        {\n            return this.vmVendor;\n        }\n\n        public String getVmVersion()\n        {\n            return this.vmVersion;\n        }\n\n        public void setCpus(final int cpus)\n        {\n            this.cpus = cpus;\n        }\n\n        public void setCurrentlyLoadedClasses(final int currentlyLoadedClasses)\n        {\n            this.currentlyLoadedClasses = currentlyLoadedClasses;\n        }\n\n        public void setHeapCommitted(final Memory heapCommitted)\n        {\n            this.heapCommitted = heapCommitted;\n        }\n\n        public void setHeapInitialized(final Memory heapInit)\n        {\n            this.heapInitialized = heapInit;\n        }\n\n        public void setHeapMaximum(final Memory heapMax)\n        {\n            this.heapMaximum = heapMax;\n        }\n\n        public void setHeapUsed(final Memory heapUsed)\n        {\n            this.heapUsed = heapUsed;\n        }\n\n        public void setNativeArchitecture(final String nativeArchitecture)\n        {\n            this.nativeArchitecture = nativeArchitecture;\n        }\n\n        public void setNonHeapCommitted(final Memory nonHeapCommitted)\n        {\n            this.nonHeapCommitted = nonHeapCommitted;\n        }\n\n        public void setNonHeapInitialized(final Memory nonHeapInit)\n        {\n            this.nonHeapInitialized = nonHeapInit;\n        }\n\n        public void setNonHeapMaximum(final Memory nonHeapMax)\n        {\n            this.nonHeapMaximum = nonHeapMax;\n        }\n\n        public void setNonHeapUsed(final Memory nonHeapUsed)\n        {\n            this.nonHeapUsed = nonHeapUsed;\n        }\n\n        public void setOsName(final String osName)\n        {\n            this.osName = osName;\n        }\n\n        public void setOsVersion(final String osVersion)\n        {\n            this.osVersion = osVersion;\n        }\n\n        public void setStartTime(final Date startTime)\n        {\n            this.startTime = startTime;\n        }\n\n        public void setTotalLoadedClasses(final long totalLoadedClasses)\n        {\n            this.totalLoadedClasses = totalLoadedClasses;\n        }\n\n        public void setTotalUnloadedClasses(final long totalUnloadedClasses)\n        {\n            this.totalUnloadedClasses = totalUnloadedClasses;\n        }\n\n        public void setUpTime(final Duration upTime)\n        {\n            this.upTime = upTime;\n        }\n\n        public void setVmArgs(final List<String> vmArgs)\n        {\n            this.vmArgs = vmArgs;\n        }\n\n        public void setVmName(final String vmName)\n        {\n            this.vmName = vmName;\n        }\n\n        public void setVmSpecVersion(final String vmSpecVersion)\n        {\n            this.vmSpecVersion = vmSpecVersion;\n        }\n\n        public void setVmVendor(final String vmVendor)\n        {\n            this.vmVendor = vmVendor;\n        }\n\n        public void setVmVersion(final String vmVersion)\n        {\n            this.vmVersion = vmVersion;\n        }\n\n        @Override\n        public String toString()\n        {\n            final StringBuilder builder = new StringBuilder();\n            builder.append(\"\\nVM Name: \");\n            builder.append(this.vmName);\n            builder.append(\"\\nVM Vendor: \");\n            builder.append(this.vmVendor);\n            builder.append(\"\\nVM Version: \");\n            builder.append(this.vmVersion);\n            builder.append(\"\\nStart Time: \");\n            builder.append(this.startTime);\n            builder.append(\"\\nUp Time: \");\n            builder.append(this.upTime);\n            builder.append(\"\\nVM Args: \");\n            builder.append(this.vmArgs);\n            builder.append(\"\\nCurrently Loaded Classes: \");\n            builder.append(this.currentlyLoadedClasses);\n            builder.append(\"\\nTotal Loaded Classes: \");\n            builder.append(this.totalLoadedClasses);\n            builder.append(\"\\nTotal Unloaded Classes: \");\n            builder.append(this.totalUnloadedClasses);\n            builder.append(\"\\nNative Architecture: \");\n            builder.append(this.nativeArchitecture);\n            builder.append(\"\\nCPUs: \");\n            builder.append(this.cpus);\n            builder.append(\"\\nOS Name: \");\n            builder.append(this.osName);\n            builder.append(\"\\nOS Version: \");\n            builder.append(this.osVersion);\n            builder.append(\"\\nHeap Initialized: \");\n            builder.append(this.heapInitialized);\n            builder.append(\"\\nHeap Committed: \");\n            builder.append(this.heapCommitted);\n            builder.append(\"\\nHeap Used: \");\n            builder.append(this.heapUsed);\n            builder.append(\"\\nHeap Maximum: \");\n            builder.append(this.heapMaximum);\n            builder.append(\"\\nNon Heap Initialized: \");\n            builder.append(this.nonHeapInitialized);\n            builder.append(\"\\nNon Heap Committed: \");\n            builder.append(this.nonHeapCommitted);\n            builder.append(\"\\nNon Heap Used: \");\n            builder.append(this.nonHeapUsed);\n            builder.append(\"\\nNon Heap Maximum: \");\n            builder.append(this.nonHeapMaximum);\n            builder.append(\"\\nMemory Pools: \");\n            this.getMemoryPools().forEach(pool ->\n            {\n                builder.append(System.lineSeparator());\n                builder.append(pool.toString(\"\\t\"));\n                builder.append(System.lineSeparator());\n            });\n            builder.append(\"\\nVM Spec Version: \");\n            builder.append(this.vmSpecVersion);\n            return builder.toString();\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(SystemInfo.class);\n\n    public static SystemInfoBean buildSystemInfo()\n    {\n        final SystemInfoBean sib = new SystemInfoBean();\n        systemInfo(sib);\n        classLoadInfo(sib);\n        operatingSystem(sib);\n        memoryUse(sib);\n        return sib;\n    }\n\n    public static void printSystemInfo()\n    {\n        logger.info(buildSystemInfo().toString());\n    }\n\n    protected static void classLoadInfo(final SystemInfoBean systemInfoBean)\n    {\n        final ClassLoadingMXBean bean = ManagementFactory.getClassLoadingMXBean();\n        systemInfoBean.setCurrentlyLoadedClasses(bean.getLoadedClassCount());\n        systemInfoBean.setTotalLoadedClasses(bean.getTotalLoadedClassCount());\n        systemInfoBean.setTotalUnloadedClasses(bean.getUnloadedClassCount());\n    }\n\n    protected static void memoryUse(final SystemInfoBean systemInfoBean)\n    {\n        final MemoryMXBean bean = ManagementFactory.getMemoryMXBean();\n        final MemoryUsage heapUsage = bean.getHeapMemoryUsage();\n        final MemoryUsage nonHeapUsage = bean.getNonHeapMemoryUsage();\n        systemInfoBean.setHeapCommitted(Memory.bytes(heapUsage.getCommitted()));\n        systemInfoBean.setHeapInitialized(Memory.bytes(heapUsage.getInit()));\n        systemInfoBean.setHeapMaximum(Memory.bytes(heapUsage.getMax()));\n        systemInfoBean.setHeapUsed(Memory.bytes(heapUsage.getUsed()));\n        systemInfoBean.setNonHeapCommitted(Memory.bytes(nonHeapUsage.getCommitted()));\n        systemInfoBean.setNonHeapInitialized(Memory.bytes(nonHeapUsage.getInit()));\n        systemInfoBean.setNonHeapMaximum(Memory.bytes(nonHeapUsage.getMax()));\n        systemInfoBean.setNonHeapUsed(Memory.bytes(nonHeapUsage.getUsed()));\n        systemInfoBean.add(ManagementFactory.getMemoryPoolMXBeans());\n    }\n\n    protected static void operatingSystem(final SystemInfoBean systemInfoBean)\n    {\n        final OperatingSystemMXBean bean = ManagementFactory.getOperatingSystemMXBean();\n        systemInfoBean.setCpus(bean.getAvailableProcessors());\n        systemInfoBean.setNativeArchitecture(bean.getArch());\n        systemInfoBean.setOsName(bean.getName());\n        systemInfoBean.setOsVersion(bean.getVersion());\n    }\n\n    protected static void systemInfo(final SystemInfoBean systemInfoBean)\n    {\n        final RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();\n        systemInfoBean.setStartTime(new Date(bean.getStartTime()));\n        systemInfoBean.setUpTime(Duration.milliseconds(bean.getUptime()));\n        systemInfoBean.setVmArgs(bean.getInputArguments());\n        systemInfoBean.setVmName(bean.getVmName());\n        systemInfoBean.setVmVendor(bean.getVmVendor());\n        systemInfoBean.setVmVersion(bean.getVmVersion());\n        systemInfoBean.setVmSpecVersion(bean.getSpecVersion());\n    }\n\n    private SystemInfo()\n    {\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/runtime/system/memory/Memory.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime.system.memory;\n\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Class holding an amount of memory\n *\n * @author matthieun\n */\npublic final class Memory\n{\n    private static final Logger logger = LoggerFactory.getLogger(Memory.class);\n    private static final long BYTES_PER_KILO_BYTE = 1024;\n    private static final long BYTES_PER_MEGA_BYTE = BYTES_PER_KILO_BYTE * BYTES_PER_KILO_BYTE;\n    private static final long BYTES_PER_GIGA_BYTE = BYTES_PER_KILO_BYTE * BYTES_PER_MEGA_BYTE;\n    private static final long BYTES_PER_TERA_BYTE = BYTES_PER_KILO_BYTE * BYTES_PER_GIGA_BYTE;\n    public static final Memory ZERO = Memory.bytes(0);\n\n    private final long bytes;\n\n    /**\n     * @param bytes\n     *            Some amount of memory in bytes\n     * @return The equivalent memory object\n     */\n    public static Memory bytes(final long bytes)\n    {\n        return new Memory(bytes);\n    }\n\n    /**\n     * @return The free memory, as reported by Runtime.getRuntime().freeMemory()\n     */\n    public static Memory free()\n    {\n        return Memory.bytes(Runtime.getRuntime().freeMemory());\n    }\n\n    /**\n     * @param gigaBytes\n     *            Some amount of memory in gigaBytes\n     * @return The equivalent memory object\n     */\n    public static Memory gigaBytes(final double gigaBytes)\n    {\n        return bytes(Math.round(gigaBytes * BYTES_PER_GIGA_BYTE));\n    }\n\n    /**\n     * @param kiloBytes\n     *            Some amount of memory in kiloBytes\n     * @return The equivalent memory object\n     */\n    public static Memory kiloBytes(final double kiloBytes)\n    {\n        return bytes(Math.round(kiloBytes * BYTES_PER_KILO_BYTE));\n    }\n\n    /**\n     * @return The maximum memory, as reported by Runtime.getRuntime().maxMemory()\n     */\n    public static Memory maximum()\n    {\n        return Memory.bytes(Runtime.getRuntime().maxMemory());\n    }\n\n    /**\n     * @param megaBytes\n     *            Some amount of memory in megaBytes\n     * @return The equivalent memory object\n     */\n    public static Memory megaBytes(final double megaBytes)\n    {\n        return bytes(Math.round(megaBytes * BYTES_PER_MEGA_BYTE));\n    }\n\n    /**\n     * Print a summary of the JVM's current memory usage, using Runtime.getRuntime()\n     */\n    public static void printCurrentMemory()\n    {\n        logger.info(\"########## Memory utilization statistics ##########\");\n        logger.info(\"Ratio Used / Maximum: {}\",\n                Ratio.ratio(Memory.used().asKiloBytes() / Memory.maximum().asKiloBytes()));\n        logger.info(\"Used Memory: {}\", Memory.used());\n        logger.info(\"Free Memory: {}\", Memory.free());\n        logger.info(\"Total Memory: {}\", Memory.total());\n        logger.info(\"Maximum Memory: {}\", Memory.maximum());\n        logger.info(\"###################################################\");\n    }\n\n    /**\n     * @param teraBytes\n     *            Some amount of memory in teraBytes\n     * @return The equivalent memory object\n     */\n    public static Memory teraBytes(final double teraBytes)\n    {\n        return bytes(Math.round(teraBytes * BYTES_PER_TERA_BYTE));\n    }\n\n    /**\n     * @return The total memory, as reported by Runtime.getRuntime().totalMemory()\n     */\n    public static Memory total()\n    {\n        return Memory.bytes(Runtime.getRuntime().totalMemory());\n    }\n\n    /**\n     * @return The used memory, as reported by Runtime.getRuntime().totalMemory() minus\n     *         Runtime.getRuntime().freeMemory()\n     */\n    public static Memory used()\n    {\n        final Runtime runtime = Runtime.getRuntime();\n        final long bytes = runtime.totalMemory() - runtime.freeMemory();\n        if (bytes < 0)\n        {\n            return ZERO;\n        }\n        return Memory.bytes(bytes);\n    }\n\n    /**\n     * construct a memory object\n     *\n     * @param bytes\n     *            The amount of bytes\n     */\n    private Memory(final long bytes)\n    {\n        this.bytes = bytes;\n    }\n\n    /**\n     * @return This memory amount in bytes\n     */\n    public long asBytes()\n    {\n        return this.bytes;\n    }\n\n    /**\n     * @return This memory amount in giga bytes\n     */\n    public double asGigaBytes()\n    {\n        return (double) this.asBytes() / BYTES_PER_GIGA_BYTE;\n    }\n\n    /**\n     * @return This memory amount in kilo bytes\n     */\n    public double asKiloBytes()\n    {\n        return (double) this.asBytes() / BYTES_PER_KILO_BYTE;\n    }\n\n    /**\n     * @return This memory amount in mega bytes\n     */\n    public double asMegaBytes()\n    {\n        return (double) this.asBytes() / BYTES_PER_MEGA_BYTE;\n    }\n\n    /**\n     * @return This memory amount in tera bytes\n     */\n    public double asTeraBytes()\n    {\n        return (double) this.asBytes() / BYTES_PER_TERA_BYTE;\n    }\n\n    @Override\n    public String toString()\n    {\n        if (this.bytes < BYTES_PER_KILO_BYTE)\n        {\n            return this.asBytes() + \" bytes\";\n        }\n        else if (this.bytes < BYTES_PER_MEGA_BYTE)\n        {\n            return format(this.asKiloBytes()) + \" Kb\";\n        }\n        else if (this.bytes < BYTES_PER_GIGA_BYTE)\n        {\n            return format(this.asMegaBytes()) + \" Mb\";\n        }\n        else if (this.bytes < BYTES_PER_TERA_BYTE)\n        {\n            return format(this.asGigaBytes()) + \" Gb\";\n        }\n        else\n        {\n            return format(this.asTeraBytes()) + \" Tb\";\n        }\n    }\n\n    private String format(final double value)\n    {\n        return String.format(\"%.2f\", value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/Angle.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.geography.Heading;\n\n/**\n * Angle, between -180 degrees (included) and 180 degrees (excluded). Precision is 10 microdegrees,\n * or one ten millionth of a degree. The term dm7 denotes a unit of one ten millionth of a degree.\n * Angle's degree circle starts from -180 and increases in the clock-wise direction towards 0 and\n * then it goes to 180 (excluded).\n *\n * @author matthieun\n * @author mkalender\n */\npublic class Angle implements Serializable\n{\n    // There are ten million microdegrees per degree\n    public static final int DM7_PER_DEGREE = 10_000_000;\n    // An angle is >= -180 degrees\n    public static final int MINIMUM_DM7 = -1_800_000_000;\n    // An angle is < 180 degrees\n    public static final int MAXIMUM_DM7 = 1_800_000_000;\n    // There are approximately 57 degrees per radian\n    public static final int DM7_PER_RADIAN = 572_957_795;\n    // When precision is needed\n    public static final double DM7_PER_RADIAN_DOUBLE = Double.valueOf(MAXIMUM_DM7) / Math.PI;\n    // This difference does not fit in an int!\n    public static final long REVOLUTION_DM7 = (long) MAXIMUM_DM7 - (long) MINIMUM_DM7;\n    // Useful Angle constants\n    public static final Angle MINIMUM = Angle.dm7(MINIMUM_DM7);\n    public static final Angle NONE = Angle.dm7(0L);\n    public static final Angle MAXIMUM = Angle.dm7(MAXIMUM_DM7 - 1L);\n    // dm7 unit per microdegree\n    public static final int DM7_PER_MICRODEGREE = 10;\n    // Threshold to print a dm7 value\n    public static final int DM7_PRINT_THRESHOLD = 10_000;\n\n    private static final long serialVersionUID = -5120437813288084229L;\n\n    // The primitive store. It will always fit between MINIMUM_DM7 and MAXIMUM_DM7, so int is enough\n    private final int dm7;\n\n    /**\n     * Create an Angle from an angle value. Any value outside of [-180,180[ degrees will be\n     * translated to its equivalent within this range\n     *\n     * @param degrees\n     *            The angle value in degrees\n     * @return The Angle object corresponding to this value in degrees.\n     */\n    public static Angle degrees(final double degrees)\n    {\n        return dm7(Math.round(degrees * DM7_PER_DEGREE));\n    }\n\n    /**\n     * Create an Angle from an angle value. Any value outside of [-180,180[ degrees will be\n     * translated to its equivalent within this range\n     *\n     * @param dm7\n     *            The angle value in dm7\n     * @return The Angle object corresponding to this value in dm7.\n     */\n    public static Angle dm7(final long dm7)\n    {\n        long rollingMicroDegrees = dm7 % REVOLUTION_DM7;\n        // Add a full 360 degrees until the number is within [-180,180[.\n        if (rollingMicroDegrees < MINIMUM_DM7)\n        {\n            rollingMicroDegrees += REVOLUTION_DM7;\n        }\n        // Subtract a full 360 degrees until the number is within [-180,180[.\n        if (rollingMicroDegrees >= MAXIMUM_DM7)\n        {\n            rollingMicroDegrees -= REVOLUTION_DM7;\n        }\n        // Store the angle as modulo 360 degrees\n        return new Angle((int) rollingMicroDegrees);\n    }\n\n    /**\n     * Create an Angle from an angle value. Any value outside of [-Pi,Pi[ degrees will be translated\n     * to its equivalent within this range\n     *\n     * @param radians\n     *            The angle value in radians\n     * @return The Angle object corresponding to this value in radians.\n     */\n    public static Angle radians(final double radians)\n    {\n        return dm7(Math.round(radians * DM7_PER_RADIAN));\n    }\n\n    /**\n     * Constructor.\n     *\n     * @param dm7\n     *            An angle value in dm7, between MINIMUM_DM7 and MAXIMUM_DM7\n     */\n    protected Angle(final int dm7)\n    {\n        this.dm7 = assertDm7(dm7);\n    }\n\n    /**\n     * Add another angle to this angle\n     *\n     * @param that\n     *            The other {@link Angle} to add\n     * @return The angle representing the sum\n     */\n    public final Angle add(final Angle that)\n    {\n        // The dm7 function takes care of maintaining the value inside the bounds.\n        return Angle.dm7(this.getDm7() + that.getDm7());\n    }\n\n    /**\n     * @return The value of this {@link Angle} in degrees.\n     */\n    public double asDegrees()\n    {\n        return (double) this.asDm7() / DM7_PER_DEGREE;\n    }\n\n    /**\n     * @return The value of this {@link Angle} in one tenth of a microdegree. This returns a long\n     *         instead of an int, because {@link Heading} (a sub-class of {@link Angle}) is based on\n     *         0-360 degrees angles that might not fit within int at the dm7 level.\n     */\n    public long asDm7()\n    {\n        return this.dm7;\n    }\n\n    /**\n     * @return the {@link Angle} object with positive values. E.g. -100 degree will be 100 degree.\n     *         Note -180 degree will return 179.9999999\n     */\n    public Angle asPositiveAngle()\n    {\n        if (this.dm7 == MINIMUM_DM7)\n        {\n            return MAXIMUM;\n        }\n        return Angle.dm7(Math.abs(this.dm7));\n    }\n\n    /**\n     * @return The value of this {@link Angle} in positive radians.\n     */\n    public double asPositiveRadians()\n    {\n        return asRadians() < 0 ? asRadians() + 2 * Math.PI : asRadians();\n    }\n\n    /**\n     * @return The value of this {@link Angle} in radians. Can be negative or positive.\n     */\n    public double asRadians()\n    {\n        return this.asDm7() / DM7_PER_RADIAN_DOUBLE;\n    }\n\n    /**\n     * Returns the difference between two {@link Angle}s. Returned {@link Angle} will never be\n     * negative.\n     *\n     * @param that\n     *            {@link Angle} to compare against\n     * @return Positive difference value between given {@link Angle}s\n     */\n    public final Angle difference(final Angle that)\n    {\n        return this.subtract(that).asPositiveAngle();\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof Angle)\n        {\n            final Angle that = (Angle) other;\n            // This is specifically to make sure that the two angles are compared on their actual\n            // dm7 value, rather than what the asDm7() method returns. This is to allow angle\n            // sub-classes to have multiple ways to return the value of one single angle: example is\n            // Longitude, with two ways of representing the Angle -180 degrees, with -180 and +180.\n            // Another example is Heading, which is an Angle, but displays its values in [0, 360[\n            // degrees.\n            return this.getDm7() == that.getDm7();\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Long.hashCode(this.dm7);\n    }\n\n    /**\n     * Compares this {@link Angle} with another {@link Angle}.\n     *\n     * @param other\n     *            Another {@link Angle} to compare against\n     * @return true if this {@link Angle} is greater than other {@link Angle}\n     */\n    public final boolean isGreaterThan(final Angle other)\n    {\n        if (other.getClass() == this.getClass())\n        {\n            return this.asDm7() > other.asDm7();\n        }\n        return this.getDm7() > other.getDm7();\n    }\n\n    /**\n     * Compares this {@link Angle} with another {@link Angle}.\n     *\n     * @param other\n     *            Another {@link Angle} to compare against\n     * @return true if this {@link Angle} is greater than or equal to other {@link Angle}\n     */\n    public final boolean isGreaterThanOrEqualTo(final Angle other)\n    {\n        if (other.getClass() == this.getClass())\n        {\n            return this.asDm7() >= other.asDm7();\n        }\n        return this.getDm7() >= other.getDm7();\n    }\n\n    /**\n     * Compares this {@link Angle} with another {@link Angle}.\n     *\n     * @param other\n     *            Another {@link Angle} to compare against\n     * @return true if this {@link Angle} is less than other {@link Angle}\n     */\n    public final boolean isLessThan(final Angle other)\n    {\n        if (other.getClass() == this.getClass())\n        {\n            return this.asDm7() < other.asDm7();\n        }\n        return this.getDm7() < other.getDm7();\n    }\n\n    /**\n     * Compares this {@link Angle} with another {@link Angle}.\n     *\n     * @param other\n     *            Another {@link Angle} to compare against\n     * @return true if this {@link Angle} is less than or equal to other {@link Angle}\n     */\n    public final boolean isLessThanOrEqualTo(final Angle other)\n    {\n        if (other.getClass() == this.getClass())\n        {\n            return this.asDm7() <= other.asDm7();\n        }\n        return this.getDm7() <= other.getDm7();\n    }\n\n    /**\n     * @return The average distance on Earth's surface of this angle taken from the center of Earth.\n     */\n    public Distance onEarth()\n    {\n        return Distance.AVERAGE_EARTH_RADIUS.scaleBy(this.asPositiveRadians());\n    }\n\n    public final Angle reverse()\n    {\n        return Angle.dm7(this.getDm7() - Angle.REVOLUTION_DM7 / 2);\n    }\n\n    /**\n     * Subtracts an {@link Angle} from this.\n     *\n     * @param that\n     *            The angle to subtract\n     * @return The angle resulting from the subtraction\n     */\n    public final Angle subtract(final Angle that)\n    {\n        // The dm7 function takes care of maintaining the value inside the bounds.\n        return Angle.dm7(this.getDm7() - that.getDm7());\n    }\n\n    @Override\n    public String toString()\n    {\n        if (Math.abs(this.getDm7()) >= DM7_PRINT_THRESHOLD)\n        {\n            return this.asDegrees() + \" degrees\";\n        }\n        return this.asDm7() + \" tenths of microdegrees\";\n    }\n\n    /**\n     * Resolving strategy for any invalid or out of bounds angle value. Sub-classes need to override\n     * this method to change the resolution strategy. The default strategy is throwing an exception\n     * if the dm7 value is smaller than -180 degrees or larger than or equal to 180 degrees.\n     *\n     * @param dm7\n     *            The proposed dm7 value\n     * @return The corrected dm7 value\n     */\n    protected int assertDm7(final int dm7)\n    {\n        if (dm7 < MINIMUM_DM7 || dm7 >= MAXIMUM_DM7)\n        {\n            throw new IllegalArgumentException(\"Angle dm7 value \" + dm7 + \" is invalid.\");\n        }\n        return dm7;\n    }\n\n    /**\n     * @return dm7 value as is. Classes that extend {@link Angle} might override\n     *         {@link Angle#asDm7()}, but {@link Angle#getDm7()}. Therefore, this method is used for\n     *         comparisons and calculations.\n     */\n    private long getDm7()\n    {\n        return this.dm7;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/Counter.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\n/**\n * Counter wrapper.\n *\n * @author matthieun\n */\npublic class Counter\n{\n    private long value;\n\n    public Counter()\n    {\n        this.value = 0;\n    }\n\n    public Counter(final long start)\n    {\n        this.value = start;\n    }\n\n    public void add(final long value)\n    {\n        this.value += value;\n    }\n\n    public long getValue()\n    {\n        return this.value;\n    }\n\n    public long getValueAndIncrement()\n    {\n        final long toReturn = this.value;\n        this.increment();\n        return toReturn;\n    }\n\n    public void increment()\n    {\n        this.add(1L);\n    }\n\n    public void reset()\n    {\n        this.value = 0;\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.valueOf(this.value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/Distance.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\n\n/**\n * A class providing conversion between different length units like feet, kilometers, miles etc.\n *\n * @author tony\n */\npublic final class Distance implements Serializable\n{\n    /**\n     * An enum for distance unit abbreviations.\n     */\n    public enum UnitAbbreviations\n    {\n        M,\n        KM,\n        MI,\n        NMI\n    }\n\n    public static final String FEET_NOTATION = \"'\";\n    public static final String INCHES_NOTATION = \"\\\"\";\n    public static final long INCHES_PER_FOOT = 12L;\n    public static final long FEET_PER_MILE = 5280;\n    public static final double METERS_PER_FOOT = 0.3048;\n    public static final double METERS_PER_KILOMETER = 1000;\n    public static final long MILLIMETERS_PER_METER = 1000;\n    public static final long METERS_PER_NAUTICAL_MILE = 1852;\n\n    public static final Distance ZERO = Distance.millimeters(0);\n    public static final Distance MAXIMUM = Distance.millimeters(Long.MAX_VALUE);\n    public static final Distance AVERAGE_EARTH_RADIUS = Distance.kilometers(6371);\n    public static final Distance TEN_MILES = Distance.miles(10);\n    public static final Distance FIFTEEN_HUNDRED_FEET = Distance.feet(1500);\n    public static final Distance ONE_METER = Distance.meters(1);\n    /**\n     * This value approximates the distance for 1 degree of latitude or longitude near the equator.\n     * The distance per degree latitude should not change anywhere on the earth, barring slight\n     * variations due to earth's true oblate spheroid shape. To calculate the distance per degree\n     * longitude far from the equator, please see\n     * {@link Distance#distancePerDegreeLongitudeAt(Location)}.\n     */\n    public static final Distance APPROXIMATE_DISTANCE_PER_DEGREE_AT_EQUATOR = Angle.degrees(1)\n            .onEarth();\n    /**\n     * @see \"https://en.wikipedia.org/wiki/Territorial_waters\"\n     */\n    public static final Distance SEA_TERRITORY_ZONE = Distance.nauticalMiles(12);\n\n    private static final long serialVersionUID = 3728783948477892064L;\n    private static final int DISTANCE_PRINTING_METERS_THRESHOLD = 1000;\n\n    private final double millimeters;\n\n    public static Distance distancePerDegreeLongitudeAt(final Location location)\n    {\n        return Distance.meters(APPROXIMATE_DISTANCE_PER_DEGREE_AT_EQUATOR.asMeters()\n                * Math.cos(location.getLatitude().asRadians()));\n    }\n\n    public static Distance feet(final double feet)\n    {\n        return Distance.meters(feet * METERS_PER_FOOT);\n    }\n\n    public static Distance feetAndInches(final double feet, final double inches)\n    {\n        return Distance.feet(feet).add(Distance.inches(inches));\n    }\n\n    public static Distance inches(final double inches)\n    {\n        return Distance.feet(inches / INCHES_PER_FOOT);\n    }\n\n    public static Distance kilometers(final double kilometers)\n    {\n        return Distance.meters(kilometers * METERS_PER_KILOMETER);\n    }\n\n    public static Distance meters(final double meters)\n    {\n        return Distance.millimeters(meters * MILLIMETERS_PER_METER);\n    }\n\n    public static Distance miles(final double miles)\n    {\n        return Distance.feet(miles * FEET_PER_MILE);\n    }\n\n    public static Distance millimeters(final double millimeters)\n    {\n        return new Distance(millimeters);\n    }\n\n    public static Distance nauticalMiles(final double nauticalMiles)\n    {\n        return Distance.meters(nauticalMiles * METERS_PER_NAUTICAL_MILE);\n    }\n\n    private Distance(final double millimeters)\n    {\n        if (millimeters < 0)\n        {\n            throw new CoreException(\"Cannot have a negative distance.\");\n        }\n        this.millimeters = millimeters;\n    }\n\n    public Distance add(final Distance that)\n    {\n        return Distance.millimeters(this.asMillimeters() + that.asMillimeters());\n    }\n\n    public double asFeet()\n    {\n        return asMeters() / METERS_PER_FOOT;\n    }\n\n    public double asKilometers()\n    {\n        return asMeters() / METERS_PER_KILOMETER;\n    }\n\n    public double asMeters()\n    {\n        return asMillimeters() / MILLIMETERS_PER_METER;\n    }\n\n    public double asMiles()\n    {\n        return asFeet() / FEET_PER_MILE;\n    }\n\n    public double asMillimeters()\n    {\n        return this.millimeters;\n    }\n\n    public double asNauticalMiles()\n    {\n        return asMeters() / METERS_PER_NAUTICAL_MILE;\n    }\n\n    public Distance difference(final Distance that)\n    {\n        return Distance.millimeters(Math.abs(this.asMillimeters() - that.asMillimeters()));\n    }\n\n    @Override\n    public boolean equals(final Object obj)\n    {\n        if (obj instanceof Distance)\n        {\n            return this.asMillimeters() == ((Distance) obj).asMillimeters();\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Double.hashCode(this.millimeters);\n    }\n\n    public boolean isGreaterThan(final Distance that)\n    {\n        return this.asMillimeters() > that.asMillimeters();\n    }\n\n    public boolean isGreaterThanOrEqualTo(final Distance that)\n    {\n        return this.asMillimeters() >= that.asMillimeters();\n    }\n\n    public boolean isLessThan(final Distance that)\n    {\n        return this.asMillimeters() < that.asMillimeters();\n    }\n\n    public boolean isLessThanOrEqualTo(final Distance that)\n    {\n        return this.asMillimeters() <= that.asMillimeters();\n    }\n\n    public Distance scaleBy(final double multiplier)\n    {\n        if (multiplier < 0)\n        {\n            throw new IllegalArgumentException(\n                    \"Cannot scale a distance by a negative multiplier: \" + multiplier);\n        }\n        return Distance.millimeters(Math.round(this.asMillimeters() * multiplier));\n    }\n\n    public Distance scaleBy(final Ratio ratio)\n    {\n        return scaleBy(ratio.asRatio());\n    }\n\n    public Distance substract(final Distance that)\n    {\n        final double delta = this.asMillimeters() - that.asMillimeters();\n        return Distance.millimeters(Math.max(delta, 0));\n    }\n\n    @Override\n    public String toString()\n    {\n        if (asMeters() < DISTANCE_PRINTING_METERS_THRESHOLD)\n        {\n            return String.format(\"%.1f meters (%.1f feet)\", asMeters(), asFeet());\n        }\n        return String.format(\"%.1f km (%.1f miles)\", asKilometers(), asMiles());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/DoubleCounter.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\n/**\n * Double counter Wrapper\n *\n * @author jklamer\n */\npublic class DoubleCounter\n{\n    private double value;\n\n    public DoubleCounter()\n    {\n        this.value = 0.0;\n    }\n\n    public DoubleCounter(final double start)\n    {\n        this.value = start;\n\n    }\n\n    public void add(final double delta)\n    {\n        this.value += delta;\n    }\n\n    public double getValue()\n    {\n        return this.value;\n    }\n\n    public void reset()\n    {\n        this.value = 0.0;\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.valueOf(this.value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/Duration.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport java.io.Serializable;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Time duration\n *\n * @author matthieun\n */\npublic final class Duration implements Serializable, Comparable<Duration>\n{\n    private static final long serialVersionUID = 8306012362496627267L;\n\n    public static final Duration ONE_DAY = hours(24);\n    public static final Duration ONE_HOUR = hours(1);\n    public static final Duration ONE_MINUTE = minutes(1);\n    public static final Duration ONE_SECOND = seconds(1);\n    public static final Duration ZERO = milliseconds(0);\n    public static final Duration MAXIMUM = milliseconds(Long.MAX_VALUE);\n\n    private static final long NANOSECONDS_PER_MILLISECONDS = 1_000_000;\n    private static final long MILLISECONDS_PER_SECOND = 1000;\n    private static final long SECONDS_PER_MINUTE = 60;\n    private static final long MINUTES_PER_HOUR = 60;\n\n    private final long milliseconds;\n\n    public static Duration hours(final double hours)\n    {\n        return minutes(hours * MINUTES_PER_HOUR);\n    }\n\n    public static Duration milliseconds(final long milliseconds)\n    {\n        return new Duration(milliseconds);\n    }\n\n    public static Duration minutes(final double minutes)\n    {\n        return seconds(minutes * SECONDS_PER_MINUTE);\n    }\n\n    public static Duration seconds(final double seconds)\n    {\n        return milliseconds(Math.round(seconds * MILLISECONDS_PER_SECOND));\n    }\n\n    private Duration(final long milliseconds)\n    {\n        if (milliseconds < 0)\n        {\n            throw new CoreException(\"Cannot have a negative duration\");\n        }\n        this.milliseconds = milliseconds;\n    }\n\n    public Duration add(final Duration that)\n    {\n        return new Duration(this.milliseconds + that.milliseconds);\n    }\n\n    public double asHours()\n    {\n        return this.asMinutes() / MINUTES_PER_HOUR;\n    }\n\n    public long asMilliseconds()\n    {\n        return this.milliseconds;\n    }\n\n    public double asMinutes()\n    {\n        return this.asSeconds() / SECONDS_PER_MINUTE;\n    }\n\n    public double asSeconds()\n    {\n        return (double) this.asMilliseconds() / MILLISECONDS_PER_SECOND;\n    }\n\n    @Override\n    public int compareTo(final Duration other)\n    {\n        if (this.milliseconds > other.milliseconds)\n        {\n            return 1;\n        }\n        else if (this.milliseconds == other.milliseconds)\n        {\n            return 0;\n        }\n        else\n        {\n            return -1;\n        }\n    }\n\n    public Duration difference(final Duration that)\n    {\n        return new Duration(Math.abs(that.milliseconds - this.milliseconds));\n    }\n\n    @Override\n    public boolean equals(final Object obj)\n    {\n        if (obj instanceof Duration)\n        {\n            return this.asMilliseconds() == ((Duration) obj).asMilliseconds();\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Long.hashCode(this.milliseconds);\n    }\n\n    public Duration highest(final Duration other)\n    {\n        if (other == null || this.isMoreThanOrEqualsTo(other))\n        {\n            return this;\n        }\n        return other;\n    }\n\n    public boolean isCloseTo(final Duration that, final Duration safe)\n    {\n        return difference(that).isLessThanOrEqualsTo(safe);\n    }\n\n    public boolean isLessThan(final Duration that)\n    {\n        return this.milliseconds < that.milliseconds;\n    }\n\n    public boolean isLessThanOrEqualsTo(final Duration that)\n    {\n        return this.milliseconds <= that.milliseconds;\n    }\n\n    public boolean isMoreThan(final Duration that)\n    {\n        return this.milliseconds > that.milliseconds;\n    }\n\n    public boolean isMoreThanOrEqualsTo(final Duration that)\n    {\n        return this.milliseconds >= that.milliseconds;\n    }\n\n    public Duration lowest(final Duration other)\n    {\n        if (other == null || this.isLessThanOrEqualsTo(other))\n        {\n            return this;\n        }\n        return other;\n    }\n\n    public long millisecondsOfSecond()\n    {\n        return this.asMilliseconds() % MILLISECONDS_PER_SECOND;\n    }\n\n    public long nanosecondsOfSecond()\n    {\n        return this.millisecondsOfSecond() * NANOSECONDS_PER_MILLISECONDS;\n    }\n\n    public void sleep()\n    {\n        try\n        {\n            Thread.sleep(this.milliseconds);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not sleep {}\", this, e);\n        }\n    }\n\n    public Duration times(final double multiplier)\n    {\n        if (multiplier < 0)\n        {\n            throw new CoreException(\"Duration multiplier cannot be negative. Was {}\", multiplier);\n        }\n        return Duration.seconds(this.asSeconds() * multiplier);\n    }\n\n    @Override\n    public String toString()\n    {\n        if (this.milliseconds < MILLISECONDS_PER_SECOND)\n        {\n            return this.asMilliseconds() + \" milliseconds\";\n        }\n        if (this.milliseconds < MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE)\n        {\n            return String.format(\"%.3f seconds\", this.asSeconds());\n        }\n        return String.format(\"%.3f minutes\", this.asMinutes());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/README.md",
    "content": "# Scalars Package\n\nAll the classes in here wrap scalar values, in a way that shields the developer from conversion errors. Each class stores a core primitive, as well as fixed constants to do the conversions, and offers construction and extraction methods that take care of the conversion.\n\n## `Angle`\n\nAn `Angle` contains a degree of magnitude 7 (dm7, degree with 7 precision) angle value. It can read degrees, radians, dm7 and return any other.\n\n## `Distance`\n\nA `Distance` contains millimeters. It can read millimeters, meters, kilometers, feet and inches, among others, and return any other.\n\n## `Duration`\n\nA time duration. It contains milliseconds, and can read milliseconds, seconds, minutes and hours, and return any other.\n\n## `Speed`\n\nA `Speed` does not contain any primitive, but a combination of `Distance` and `Duration`. It can read miles per hour, kilometers per hour, meters per second among others, and return any other.\n\n## `Surface`\n\nA `Surface` is an `Angle` squared. It will read square dm7, and return the same, or using the earth's radius, square meters, square kilometers, etc.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/Ratio.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport java.io.Serializable;\nimport java.text.DecimalFormat;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * A ratio between 0 and 1\n *\n * @author tony\n * @author matthieun\n */\npublic final class Ratio implements Serializable\n{\n    private static final long serialVersionUID = 8339825003035330715L;\n\n    private static final double PERCENTAGE_PER_RATIO = 100.0;\n    private static final double MAXIMUM_RATIO = 1.0;\n\n    public static final Ratio MAXIMUM = ratio(MAXIMUM_RATIO);\n    public static final Ratio MINIMUM = ratio(0.0);\n    public static final Ratio HALF = ratio(MAXIMUM_RATIO / 2.0);\n\n    private final double ratio;\n\n    public static Ratio percentage(final double percentage)\n    {\n        if (percentage < 0 || percentage > PERCENTAGE_PER_RATIO)\n        {\n            throw new CoreException(\"percentage {} is not between 0.0 and {} inclusive.\",\n                    percentage, PERCENTAGE_PER_RATIO);\n        }\n        return new Ratio(percentage / PERCENTAGE_PER_RATIO);\n    }\n\n    public static Ratio ratio(final double ratio)\n    {\n        if (ratio < 0 || ratio > 1)\n        {\n            throw new CoreException(\"ratio {} is not between 0.0 and 1.0 inclusive.\", ratio);\n        }\n        return new Ratio(ratio);\n    }\n\n    private Ratio(final double ratio)\n    {\n        this.ratio = ratio;\n    }\n\n    public double asPercentage()\n    {\n        return this.ratio * PERCENTAGE_PER_RATIO;\n    }\n\n    public double asRatio()\n    {\n        return this.ratio;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof Ratio)\n        {\n            final Ratio that = (Ratio) other;\n            return this.asRatio() == that.asRatio();\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Double.valueOf(this.ratio).hashCode();\n    }\n\n    public boolean isGreaterThan(final Ratio that)\n    {\n        return this.asRatio() > that.asRatio();\n    }\n\n    public boolean isGreaterThanOrEqualTo(final Ratio that)\n    {\n        return this.asRatio() >= that.asRatio();\n    }\n\n    public boolean isLessThan(final Ratio that)\n    {\n        return this.asRatio() < that.asRatio();\n    }\n\n    public boolean isLessThanOrEqualTo(final Ratio that)\n    {\n        return this.asRatio() <= that.asRatio();\n    }\n\n    @Override\n    public String toString()\n    {\n        return new DecimalFormat(\"#.##\").format(this.asPercentage()) + \" %\";\n    }\n\n    public String toString(final int decimalPlaces)\n    {\n        if (decimalPlaces > 0)\n        {\n            final StringBuilder builder = new StringBuilder();\n            builder.append(\"#.\");\n            for (int i = 0; i < decimalPlaces; i++)\n            {\n                builder.append(\"#\");\n            }\n            return new DecimalFormat(builder.toString()).format(this.asPercentage()) + \" %\";\n        }\n        return String.valueOf(this.asPercentage());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/Speed.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport java.io.Serializable;\n\n/**\n * A class provides the conversion between different speed units like kph and mph\n *\n * @author tony\n * @author Sid\n */\npublic final class Speed implements Serializable\n{\n    public static final String MILES_PER_HOUR = \"mph\";\n    public static final String KILOMETERS_PER_HOUR = \"kph\";\n    public static final String NAUTICAL_MILES_PER_HOUR = \"knots\";\n    private static final long serialVersionUID = 3268649594426387264L;\n    private final Distance distance;\n    private final Duration duration;\n\n    public static Speed distancePerDuration(final Distance distance, final Duration duration)\n    {\n        return new Speed(distance, duration);\n    }\n\n    public static Speed kilometersPerHour(final double kph)\n    {\n        return new Speed(Distance.kilometers(kph), Duration.ONE_HOUR);\n    }\n\n    public static Speed knots(final double knots)\n    {\n        return new Speed(Distance.nauticalMiles(knots), Duration.ONE_HOUR);\n    }\n\n    public static Speed metersPerSecond(final double mps)\n    {\n        return new Speed(Distance.meters(mps), Duration.ONE_SECOND);\n    }\n\n    public static Speed milesPerHour(final double mph)\n    {\n        return new Speed(Distance.miles(mph), Duration.ONE_HOUR);\n    }\n\n    private Speed(final Distance distance, final Duration duration)\n    {\n        this.distance = distance;\n        this.duration = duration;\n    }\n\n    public Speed add(final Speed that)\n    {\n        return Speed.kilometersPerHour(this.asKilometersPerHour() + that.asKilometersPerHour());\n    }\n\n    public Duration asDuration(final Distance distance)\n    {\n        return Duration.seconds(distance.asMeters() / this.asMetersPerSecond());\n    }\n\n    public double asKilometersPerHour()\n    {\n        return this.distance.asKilometers() / this.duration.asHours();\n    }\n\n    public double asKnots()\n    {\n        return this.distance.asNauticalMiles() / this.duration.asHours();\n    }\n\n    public double asMetersPerSecond()\n    {\n        return this.distance.asMeters() / this.duration.asSeconds();\n    }\n\n    public double asMilesPerHour()\n    {\n        return this.distance.asMiles() / this.duration.asHours();\n    }\n\n    public Speed difference(final Speed that)\n    {\n        return Speed.kilometersPerHour(\n                Math.abs(this.asKilometersPerHour() - that.asKilometersPerHour()));\n    }\n\n    @Override\n    public boolean equals(final Object object)\n    {\n        if (object instanceof Speed)\n        {\n            final Speed that = (Speed) object;\n            return this.asKilometersPerHour() == that.asKilometersPerHour();\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Double.hashCode(asKilometersPerHour());\n    }\n\n    public boolean isFasterThan(final Speed that)\n    {\n        return this.asKilometersPerHour() > that.asKilometersPerHour();\n    }\n\n    /**\n     * @param that\n     *            The other {@link Speed} to compare\n     * @return True if this speed is faster than or equals to another speed, note this comparison\n     *         may only track a few digits after decimal depends on the initialization methods you\n     *         choose\n     *         <p>\n     *         e.g.\n     *         Speed.kilometersPerHour(1.000008).isFasterThanOrEqualTo(Speed.kilometersPerHour(1.\n     *         000009)) will return true\n     */\n    public boolean isFasterThanOrEqualTo(final Speed that)\n    {\n        return this.asKilometersPerHour() >= that.asKilometersPerHour();\n    }\n\n    public boolean isSlowerThan(final Speed that)\n    {\n        return this.asKilometersPerHour() < that.asKilometersPerHour();\n    }\n\n    /**\n     * @param that\n     *            The other {@link Speed} to compare\n     * @return True if this speed is slower than or equals to another speed, note this comparison\n     *         will only track a few digits after decimal depends on the initialization methods you\n     *         choose\n     *         <p>\n     *         e.g.\n     *         Speed.kilometersPerHour(1.000009).isSlowerThanOrEqualTo(Speed.kilometersPerHour(1.\n     *         000008)) will return true\n     */\n    public boolean isSlowerThanOrEqualTo(final Speed that)\n    {\n        return this.asKilometersPerHour() <= that.asKilometersPerHour();\n    }\n\n    public Speed subtract(final Speed that)\n    {\n        final double delta = this.asKilometersPerHour() - that.asKilometersPerHour();\n        return Speed.kilometersPerHour(Math.max(delta, 0));\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.format(\"%.1f kph (%.1f mph)\", asKilometersPerHour(), asMilesPerHour());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/scalars/Surface.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport java.io.Serializable;\n\n/**\n * Product of two {@link Angle}s\n *\n * @author matthieun\n * @author jgage\n * @author cstaylor\n * @author rstack\n */\npublic final class Surface implements Serializable\n{\n    public static final Surface MINIMUM = Surface.forDm7Squared(0);\n    public static final Surface MAXIMUM = Surface.forDm7Squared(Long.MAX_VALUE);\n\n    /**\n     * <pre>\n     * dm7 = 10^-7 degrees\n     * d = Angle in Radians * Radius of the Earth\n     * Angle in Radians = d / Radius of the Earth\n     * 1m / Radius of the Earth in meters = 1 / 6371000\n     * 8.99 * 10^-6 degrees\n     * 89.9 dm7 = 1m\n     * 89.9^2 = 8082.01dm7^2\n     * </pre>\n     */\n    public static final Surface UNIT_METER_SQUARED_ON_EARTH_SURFACE = Surface.forDm7Squared(8082L);\n    private static final long serialVersionUID = 9085129200745439319L;\n\n    private final long dm7Squared;\n\n    public static Surface forAngles(final Angle angle1, final Angle angle2)\n    {\n        long dm71 = angle1.asDm7();\n        if (dm71 < 0)\n        {\n            dm71 += Angle.REVOLUTION_DM7;\n        }\n\n        long dm72 = angle2.asDm7();\n        if (dm72 < 0)\n        {\n            dm72 += Angle.REVOLUTION_DM7;\n        }\n\n        return new Surface(dm71 * dm72);\n    }\n\n    public static Surface forDm7Squared(final long dm7Squared)\n    {\n        return new Surface(dm7Squared);\n    }\n\n    private Surface(final long dm7Squared)\n    {\n        this.dm7Squared = dm7Squared;\n    }\n\n    public Surface add(final Surface other)\n    {\n        return Surface.forDm7Squared(this.asDm7Squared() + other.asDm7Squared());\n    }\n\n    public long asDm7Squared()\n    {\n        return this.dm7Squared;\n    }\n\n    public double asKilometerSquared()\n    {\n        final double result = asDm7Squared()\n                / ((double) Angle.DM7_PER_RADIAN * (double) Angle.DM7_PER_RADIAN)\n                * (Distance.AVERAGE_EARTH_RADIUS.asKilometers()\n                        * Distance.AVERAGE_EARTH_RADIUS.asKilometers());\n        return result;\n    }\n\n    public double asMeterSquared()\n    {\n        final double result = asDm7Squared()\n                / ((double) Angle.DM7_PER_RADIAN * (double) Angle.DM7_PER_RADIAN)\n                * (Distance.AVERAGE_EARTH_RADIUS.asMeters()\n                        * Distance.AVERAGE_EARTH_RADIUS.asMeters());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (other instanceof Surface)\n        {\n            final Surface that = (Surface) other;\n            return that.asDm7Squared() == this.asDm7Squared();\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Long.hashCode(this.dm7Squared);\n    }\n\n    public boolean isLargerThan(final Surface other)\n    {\n        return this.asDm7Squared() > other.asDm7Squared();\n    }\n\n    public boolean isLargerThanOrEqualTo(final Surface other)\n    {\n        return this.asDm7Squared() >= other.asDm7Squared();\n    }\n\n    public boolean isLessThan(final Surface other)\n    {\n        return this.asDm7Squared() < other.asDm7Squared();\n    }\n\n    public boolean isLessThanOrEqualTo(final Surface other)\n    {\n        return this.asDm7Squared() <= other.asDm7Squared();\n    }\n\n    public Surface scaleBy(final double factor)\n    {\n        if (factor < 0)\n        {\n            throw new IllegalArgumentException(\n                    \"Scale factor must not be a negative number. Was: \" + factor);\n        }\n        return Surface.forDm7Squared((long) (this.asDm7Squared() * factor));\n    }\n\n    public Surface subtract(final Surface other)\n    {\n        if (isLargerThanOrEqualTo(other))\n        {\n            return Surface.forDm7Squared(this.asDm7Squared() - other.asDm7Squared());\n        }\n        throw new IllegalArgumentException(\n                \"Invalid surfaces for performing subtraction: \" + this + \" minus \" + other);\n    }\n\n    @Override\n    public String toString()\n    {\n        return asDm7Squared() + \" dm7^2\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/statistic/AbstractStatistic.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic;\n\nimport java.text.NumberFormat;\nimport java.util.function.Consumer;\n\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\n\n/**\n * This class holds a counter and a timer, and will print out information with logPrintFrequency.\n *\n * @author tony\n */\npublic abstract class AbstractStatistic implements Statistic\n{\n    private static final long DEFAULT_LOG_PRINT_FREQUENCY = 1_000_000;\n    private static final String DEFAULT_NAME = \"\";\n\n    private final NumberFormat numberFormat;\n    private Time startTime;\n    private long logPrintFrequency;\n    private String name;\n    private long count;\n    private Consumer<String> log;\n\n    protected AbstractStatistic(final Logger logger)\n    {\n        this(logger, DEFAULT_LOG_PRINT_FREQUENCY, DEFAULT_NAME);\n    }\n\n    protected AbstractStatistic(final Logger logger, final long logPrintFrequency)\n    {\n        this(logger, logPrintFrequency, DEFAULT_NAME);\n    }\n\n    protected AbstractStatistic(final Logger logger, final long logPrintFrequency,\n            final String name)\n    {\n        this.log = logger::info;\n        this.logPrintFrequency = logPrintFrequency;\n        this.name = name;\n        this.startTime = Time.now();\n        this.numberFormat = NumberFormat.getInstance();\n        this.numberFormat.setGroupingUsed(true);\n    }\n\n    protected AbstractStatistic(final Logger logger, final String name)\n    {\n        this(logger, DEFAULT_LOG_PRINT_FREQUENCY, name);\n    }\n\n    public void clear()\n    {\n        this.count = 0;\n        this.startTime = Time.now();\n    }\n\n    public void clearCounter()\n    {\n        this.count = 0;\n    }\n\n    public long count()\n    {\n        return this.count;\n    }\n\n    public Consumer<String> getLog()\n    {\n        return this.log;\n    }\n\n    public long getLogPrintFrequency()\n    {\n        return this.logPrintFrequency;\n    }\n\n    public String getName()\n    {\n        return this.name;\n    }\n\n    public Time getStartTime()\n    {\n        return this.startTime;\n    }\n\n    public void increment()\n    {\n        this.count++;\n        if (this.count % this.logPrintFrequency == 0)\n        {\n            this.log.accept(toString());\n        }\n        onIncrement();\n    }\n\n    @Override\n    public void increment(final double value)\n    {\n        this.increment();\n        onIncrement(value);\n    }\n\n    public void logUsingLevel(final Consumer<String> log)\n    {\n        this.log = log;\n    }\n\n    public void setLogPrintFrequency(final long logPrintFrequency)\n    {\n        this.logPrintFrequency = logPrintFrequency;\n    }\n\n    public void setName(final String name)\n    {\n        this.name = name;\n    }\n\n    public Duration sinceStart()\n    {\n        return this.startTime.untilNow();\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.name + \" Count: \" + this.numberFormat.format(this.count) + \", Time spent: \"\n                + sinceStart();\n    }\n\n    public String toStringWithoutTimer()\n    {\n        return this.name + \" Count: \" + this.numberFormat.format(this.count);\n    }\n\n    protected NumberFormat getNumberFormat()\n    {\n        return this.numberFormat;\n    }\n\n    protected abstract void onIncrement();\n\n    protected abstract void onIncrement(double value);\n\n    protected void setCount(final long count)\n    {\n        this.count = count;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/statistic/Statistic.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic;\n\n/**\n * @author tony\n */\npublic interface Statistic\n{\n    /**\n     * Updates the internal state of the statistic to reflect the addition of the new value\n     *\n     * @param value\n     *            the new value\n     */\n    void increment(double value);\n\n    /**\n     * Print current statistic to logger\n     */\n    void summary();\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/statistic/StatisticUtils.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic;\n\nimport java.util.Collection;\nimport java.util.DoubleSummaryStatistics;\nimport java.util.IntSummaryStatistics;\nimport java.util.LongSummaryStatistics;\nimport java.util.Optional;\nimport java.util.function.BinaryOperator;\nimport java.util.function.ToDoubleFunction;\nimport java.util.function.ToIntFunction;\nimport java.util.function.ToLongFunction;\n\nimport org.apache.commons.math3.stat.StatUtils;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\n\n/**\n * Some useful tools can consume a Number / Object collection, then return univariate statistic.\n * Most implementation is based on Java 8 Statistic objects and Stream function, except for\n * percentile method which is based on apache StatUtils class\n *\n * @author tony\n */\npublic final class StatisticUtils\n{\n    /**\n     * @param collection\n     *            The collection of items\n     * @param ratio\n     *            The percentage\n     * @return Returns an estimate of the <code>p</code>th percentile of the values in collection\n     */\n    public static double percentile(final Collection<Double> collection, final Ratio ratio)\n    {\n        final double[] array = toDoubleArray(collection);\n        return StatUtils.percentile(array, ratio.asPercentage());\n    }\n\n    /**\n     * Get statistic by using a customized binary operator. An example of sum of square of a list of\n     * Double would be:\n     * <p>\n     * <code> {@literal Optional<Double> sumOfSquare = StatisticUtils.summarizing(list, (a,b) -> a*a + b*b)};\n     * </code>\n     * </p>\n     *\n     * @param collection\n     *            The collection of items\n     * @param accumulator\n     *            The binary operator that accumulates values\n     * @param <T>\n     *            The type of the statistic\n     * @return the result value of corresponding accumulator operation\n     */\n    public static <T> Optional<T> summarizing(final Collection<T> collection,\n            final BinaryOperator<T> accumulator)\n    {\n        return collection.stream().reduce(accumulator);\n    }\n\n    /**\n     * @param collection\n     *            The collection of items\n     * @return A state object for collecting statistics such as count, min, max, sum, and average.\n     */\n    public static DoubleSummaryStatistics summarizingDouble(final Collection<Double> collection)\n    {\n        return collection.stream().mapToDouble(value -> value).summaryStatistics();\n    }\n\n    /**\n     * Get summary statistic from an object collection. An example to get summary of bank account\n     * balance from a list of Person would be\n     * <p>\n     * <code> {@literal DoubleSummaryStatistics stat =\n     * StatisticUtils.summarizingDouble(list, x->x.getBankAccountBalance());} </code>\n     * </p>\n     *\n     * @param collection\n     *            The collection of items\n     * @param function\n     *            You need to specify the function to get double value for each object T\n     * @param <T>\n     *            The type of the statistic\n     * @return A state object for collecting statistics such as count, min, max, sum, and average.\n     */\n    public static <T> DoubleSummaryStatistics summarizingDouble(final Collection<T> collection,\n            final ToDoubleFunction<? super T> function)\n    {\n        return collection.stream().mapToDouble(function).summaryStatistics();\n    }\n\n    /**\n     * @param collection\n     *            The collection of items\n     * @return A state object for collecting statistics such as count, min, max, sum, and average.\n     */\n    public static IntSummaryStatistics summarizingInt(final Collection<Integer> collection)\n    {\n        return collection.stream().mapToInt(value -> value).summaryStatistics();\n    }\n\n    /**\n     * Get summary statistic from an object collection. An example to get summary of age from a\n     * Person list would be\n     * <p>\n     * <code> {@literal IntSummaryStatistics stat = StatisticUtils.summarizingInt(list, x->x.getAge());} </code>\n     * </p>\n     *\n     * @param collection\n     *            The collection of items\n     * @param function\n     *            You need to specify the function to get int value for each object T\n     * @param <T>\n     *            The type of the statistic\n     * @return A state object for collecting statistics such as count, min, max, sum, and average.\n     */\n    public static <T> IntSummaryStatistics summarizingInt(final Collection<T> collection,\n            final ToIntFunction<? super T> function)\n    {\n        return collection.stream().mapToInt(function).summaryStatistics();\n    }\n\n    /**\n     * @param collection\n     *            The collection of items\n     * @return A state object for collecting statistics such as count, min, max, sum, and average.\n     */\n    public static LongSummaryStatistics summarizingLong(final Collection<Long> collection)\n    {\n        return collection.stream().mapToLong(value -> value).summaryStatistics();\n    }\n\n    /**\n     * Get summary statistic from an object collection. An example to get summary of age from a\n     * Person list would be (suppose a person can live longer than 2^31 - 1)\n     * <p>\n     * <code> {@literal LongSummaryStatistics stat = StatisticUtils.summarizingLong(list, x->x.getAge());} </code>\n     * </p>\n     *\n     * @param collection\n     *            The collection of items\n     * @param function\n     *            You need to specify the function to get long value for each object T\n     * @param <T>\n     *            The type of the statistic\n     * @return A state object for collecting statistics such as count, min, max, sum, and average.\n     */\n    public static <T> LongSummaryStatistics summarizingLong(final Collection<T> collection,\n            final ToLongFunction<? super T> function)\n    {\n        return collection.stream().mapToLong(function).summaryStatistics();\n    }\n\n    private static double[] toDoubleArray(final Collection<Double> collection)\n    {\n        final double[] array = new double[collection.size()];\n        int index = 0;\n        for (final Double element : collection)\n        {\n            array[index++] = element;\n        }\n        return array;\n    }\n\n    private StatisticUtils()\n    {\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/statistic/storeless/CounterWithStatistic.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic.storeless;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.statistic.AbstractStatistic;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\n\n/**\n * A simple class with a counter and a timer which is able to pause in the middle, and can print log\n * every a period of time.\n *\n * @author tony\n */\npublic class CounterWithStatistic extends AbstractStatistic\n{\n    private boolean paused = false;\n    private Duration duration = Duration.ZERO;\n    private Time temporaryTime;\n\n    public CounterWithStatistic(final Logger logger)\n    {\n        super(logger);\n        this.temporaryTime = getStartTime();\n    }\n\n    public CounterWithStatistic(final Logger logger, final long logPrintFrequency,\n            final String name)\n    {\n        super(logger, logPrintFrequency, name);\n        this.temporaryTime = getStartTime();\n    }\n\n    public CounterWithStatistic(final Logger logger, final String name)\n    {\n        super(logger, name);\n        this.temporaryTime = getStartTime();\n    }\n\n    /**\n     * @return Time duration without pause\n     */\n    public Duration accurateTimeSpent()\n    {\n        if (!this.paused)\n        {\n            this.duration = this.duration.add(this.temporaryTime.untilNow());\n            resetTemporaryTime();\n        }\n        return this.duration;\n    }\n\n    public void incrementCount(final long count)\n    {\n        unPause();\n        for (long i = 0; i < count; i++)\n        {\n            long countLocal = count();\n            countLocal++;\n            this.setCount(countLocal);\n            if (countLocal % getLogPrintFrequency() == 0)\n            {\n                getLog().accept(toString());\n            }\n        }\n    }\n\n    public boolean isPaused()\n    {\n        return this.paused;\n    }\n\n    @Override\n    public void onIncrement(final double value)\n    {\n        throw new CoreException(\"Counter doesn't support increment double value {}\", value);\n    }\n\n    public void pause()\n    {\n        if (!this.paused)\n        {\n            this.paused = true;\n            this.duration = this.duration.add(this.temporaryTime.untilNow());\n        }\n    }\n\n    @Override\n    public void summary()\n    {\n        getLog().accept(toString());\n    }\n\n    public void summaryWithAccurateTimeSpent()\n    {\n        getLog().accept(toStringWithAccurateTimeSpent());\n    }\n\n    public void summaryWithoutTimer()\n    {\n        getLog().accept(toStringWithoutTimer());\n    }\n\n    public String toStringWithAccurateTimeSpent()\n    {\n        return getName() + \" Count: \" + getNumberFormat().format(count()) + \", Time spent: \"\n                + accurateTimeSpent();\n    }\n\n    public void unPause()\n    {\n        if (this.paused)\n        {\n            this.paused = false;\n            resetTemporaryTime();\n        }\n    }\n\n    @Override\n    protected void onIncrement()\n    {\n        unPause();\n    }\n\n    private void resetTemporaryTime()\n    {\n        this.temporaryTime = Time.now();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/statistic/storeless/CustomizedStatistic.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic.storeless;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.commons.math3.stat.descriptive.StorelessUnivariateStatistic;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.statistic.AbstractStatistic;\nimport org.slf4j.Logger;\n\n/**\n * This class provide arbitrary combination statistic of {@link StatisticType}, while no need to\n * store any value inside\n *\n * @author tony\n */\npublic class CustomizedStatistic extends AbstractStatistic\n{\n    private final Map<StatisticType, StorelessUnivariateStatistic> statistics = new HashMap<>();\n\n    public CustomizedStatistic(final Logger logger, final long logFrequency,\n            final StatisticType... types)\n    {\n        super(logger, logFrequency);\n        configure(types);\n    }\n\n    public CustomizedStatistic(final Logger logger, final StatisticType... types)\n    {\n        super(logger);\n        configure(types);\n    }\n\n    public double getGeometricMean()\n    {\n        if (this.statistics.containsKey(StatisticType.GeometricMean))\n        {\n            return this.statistics.get(StatisticType.GeometricMean).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type GeometricMean\");\n    }\n\n    public double getKurtosis()\n    {\n        if (this.statistics.containsKey(StatisticType.Kurtosis))\n        {\n            return this.statistics.get(StatisticType.Kurtosis).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type Kurtosis\");\n    }\n\n    public double getMax()\n    {\n        if (this.statistics.containsKey(StatisticType.Max))\n        {\n            return this.statistics.get(StatisticType.Max).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type Max\");\n    }\n\n    public double getMean()\n    {\n        if (this.statistics.containsKey(StatisticType.Mean))\n        {\n            return this.statistics.get(StatisticType.Mean).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type Mean\");\n    }\n\n    public double getMin()\n    {\n        if (this.statistics.containsKey(StatisticType.Min))\n        {\n            return this.statistics.get(StatisticType.Min).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type Min\");\n    }\n\n    public double getProduct()\n    {\n        if (this.statistics.containsKey(StatisticType.Product))\n        {\n            return this.statistics.get(StatisticType.Product).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type Product\");\n    }\n\n    public double getSecondMoment()\n    {\n        if (this.statistics.containsKey(StatisticType.SecondMoment))\n        {\n            return this.statistics.get(StatisticType.SecondMoment).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type SecondMoment\");\n    }\n\n    public double getSkewness()\n    {\n        if (this.statistics.containsKey(StatisticType.Skewness))\n        {\n            return this.statistics.get(StatisticType.Skewness).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type Skewness\");\n    }\n\n    public double getStandardDeviation()\n    {\n        if (this.statistics.containsKey(StatisticType.StandardDeviation))\n        {\n            return this.statistics.get(StatisticType.StandardDeviation).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type StandardDeviation\");\n    }\n\n    public double getSum()\n    {\n        if (this.statistics.containsKey(StatisticType.Sum))\n        {\n            return this.statistics.get(StatisticType.Sum).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type Sum\");\n    }\n\n    public double getSumOfLogs()\n    {\n        if (this.statistics.containsKey(StatisticType.SumOfLogs))\n        {\n            return this.statistics.get(StatisticType.SumOfLogs).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type SumOfLogs\");\n    }\n\n    public double getSumOfSquares()\n    {\n        if (this.statistics.containsKey(StatisticType.SumOfSquares))\n        {\n            return this.statistics.get(StatisticType.SumOfSquares).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type SumOfSquares\");\n    }\n\n    /**\n     * Each statistic will have the same amount of total values associated with it so we really just\n     * need to find the first one and get the N value from it.\n     *\n     * @return The number of times increment was called, or 0 if never called.\n     */\n    public long getTotal()\n    {\n        if (this.statistics.size() > 0)\n        {\n            return this.statistics.entrySet().iterator().next().getValue().getN();\n        }\n        return 0;\n    }\n\n    public double getVariance()\n    {\n        if (this.statistics.containsKey(StatisticType.Variance))\n        {\n            return this.statistics.get(StatisticType.Variance).getResult();\n        }\n        throw new CoreException(\"You didn't choose the statistic type Variance\");\n    }\n\n    @Override\n    public void onIncrement(final double value)\n    {\n        for (final StorelessUnivariateStatistic stat : this.statistics.values())\n        {\n            stat.increment(value);\n        }\n    }\n\n    @Override\n    public void summary()\n    {\n        getLog().accept(toString());\n        this.statistics.forEach(\n                (type, statistic) -> getLog().accept(type.name() + \": \" + statistic.getResult()));\n    }\n\n    @Override\n    protected void onIncrement()\n    {\n    }\n\n    private void configure(final StatisticType... types)\n    {\n        for (final StatisticType type : types)\n        {\n            this.statistics.put(type, type.toStatistic());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/statistic/storeless/StatisticType.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic.storeless;\n\nimport org.apache.commons.math3.stat.descriptive.StorelessUnivariateStatistic;\nimport org.apache.commons.math3.stat.descriptive.moment.GeometricMean;\nimport org.apache.commons.math3.stat.descriptive.moment.Kurtosis;\nimport org.apache.commons.math3.stat.descriptive.moment.Mean;\nimport org.apache.commons.math3.stat.descriptive.moment.SecondMoment;\nimport org.apache.commons.math3.stat.descriptive.moment.Skewness;\nimport org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;\nimport org.apache.commons.math3.stat.descriptive.moment.Variance;\nimport org.apache.commons.math3.stat.descriptive.rank.Max;\nimport org.apache.commons.math3.stat.descriptive.rank.Min;\nimport org.apache.commons.math3.stat.descriptive.summary.Product;\nimport org.apache.commons.math3.stat.descriptive.summary.Sum;\nimport org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;\nimport org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;\n\n/**\n * A wrapper for all univariate storeless statistic from apache\n *\n * @author tony\n */\npublic enum StatisticType\n{\n    SecondMoment(0),\n    GeometricMean(1),\n    Kurtosis(2),\n    Max(3),\n    Mean(4),\n    Min(5),\n    Product(6),\n    Skewness(7),\n    StandardDeviation(8),\n    Sum(9),\n    SumOfLogs(10),\n    SumOfSquares(11),\n    Variance(12);\n\n    private final int identifier;\n\n    StatisticType(final int identifier)\n    {\n        this.identifier = identifier;\n    }\n\n    public int getIdentifier()\n    {\n        return this.identifier;\n    }\n\n    public StorelessUnivariateStatistic toStatistic()\n    {\n        switch (this)\n        {\n            case SecondMoment:\n                return new SecondMoment();\n            case GeometricMean:\n                return new GeometricMean();\n            case Kurtosis:\n                return new Kurtosis();\n            case Max:\n                return new Max();\n            case Mean:\n                return new Mean();\n            case Min:\n                return new Min();\n            case Product:\n                return new Product();\n            case Skewness:\n                return new Skewness();\n            case StandardDeviation:\n                return new StandardDeviation();\n            case Sum:\n                return new Sum();\n            case SumOfLogs:\n                return new SumOfLogs();\n            case SumOfSquares:\n                return new SumOfSquares();\n            case Variance:\n                return new Variance();\n            default:\n                return null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/Bean.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\n/**\n * Uses introspection to set String values. There are plenty of frameworks out there that do this\n * already (Apache BeanUtils, minimalcode), but I didn't want to bring in yet another framework\n * unless we absolutely needed to.\n *\n * @author cstaylor\n */\n@Retention(value = RUNTIME)\n@Target(value = FIELD)\npublic @interface Bean\n{\n    String[] value() default {};\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/BeanHandler.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Very simple Java reflection code used when parsing an array of Strings set on a bean in key=value\n * pairs. It looks for a method named set[key], where the first character of key is upper-cased. The\n * reflected method must take a single String as the parameter.\n *\n * @author cstaylor\n */\npublic class BeanHandler implements FieldHandler\n{\n    private static final Logger logger = LoggerFactory.getLogger(BeanHandler.class);\n\n    @Override\n    public void create(final Field field, final CoreTestRule rule, final CreationContext context)\n    {\n        try\n        {\n            final Class<?> fieldClass = field.getType();\n            final Optional<Constructor<?>> constructor = findConstructorIn(fieldClass);\n            if (constructor.isPresent())\n            {\n                final Object object = constructor.get().newInstance();\n                final Bean bean = field.getAnnotation(Bean.class);\n                Arrays.asList(bean.value()).stream().map(value -> StringList.split(value, \"=\"))\n                        .filter(list -> list.size() == 2).forEach(stringList ->\n                        {\n                            String key = stringList.get(0);\n                            key = \"set\" + key.substring(0, 1).toUpperCase() + key.substring(1);\n                            try\n                            {\n                                final Method method = fieldClass.getDeclaredMethod(key,\n                                        String.class);\n                                method.invoke(object, stringList.get(1));\n                            }\n                            catch (final NoSuchMethodException | InvocationTargetException\n                                    | IllegalAccessException oops)\n                            {\n                                throw new CoreException(\"Couldn't call {} on {}\", key, fieldClass,\n                                        oops);\n                            }\n                        });\n                field.set(rule, object);\n            }\n        }\n        catch (final InstantiationException | IllegalAccessException | IllegalArgumentException\n                | InvocationTargetException e)\n        {\n            throw new CoreException(\"Unable to create rule\", e);\n        }\n    }\n\n    @Override\n    public boolean handles(final Field field)\n    {\n        return findConstructorIn(field.getType()).isPresent();\n    }\n\n    private Optional<Constructor<?>> findConstructorIn(final Class<?> klass)\n    {\n        try\n        {\n            return Optional.of(klass.getDeclaredConstructor());\n        }\n        catch (final Exception oops)\n        {\n            logger.warn(\"Couldn't find default constructor\", oops);\n            return Optional.empty();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/CoreTestRule.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\n\nimport org.junit.rules.TestRule;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\nimport org.openstreetmap.atlas.exception.CoreException;\n\nimport com.google.common.collect.HashBasedTable;\nimport com.google.common.collect.Table;\n\n/**\n * Any JUnit rules that subclass from CoreTestRule can have their values set through annotation\n * instead of direct manipulation. <br >\n * If JUnit 5 support is desired, see {@code CoreTestExtension} (only available in test code).\n *\n * @author cstaylor\n */\npublic class CoreTestRule implements TestRule\n{\n    /**\n     * This is how we compare the sort order of two Annotation classes; used by AnnotationComparator\n     *\n     * @author cstaylor\n     */\n    static class AnnotationClassComparator implements Comparator<Class<? extends Annotation>>\n    {\n        public static final int sortValueFor(final Class<? extends Annotation> klass)\n        {\n            final int returnValue = annotationLookup.indexOf(klass);\n            return returnValue > -1 ? returnValue : Integer.MAX_VALUE;\n        }\n\n        @Override\n        public int compare(final Class<? extends Annotation> first,\n                final Class<? extends Annotation> second)\n        {\n            return sortValueFor(first) - sortValueFor(second);\n        }\n    }\n\n    /**\n     * This is how we compare the sort order of two Annotation objects; used by FieldComparator\n     *\n     * @author cstaylor\n     */\n    static class AnnotationComparator implements Comparator<Annotation>\n    {\n        public static final int sortValueFor(final Annotation annotation)\n        {\n            return AnnotationClassComparator.sortValueFor(annotation.annotationType());\n        }\n\n        @Override\n        public int compare(final Annotation first, final Annotation second)\n        {\n            return sortValueFor(first) - sortValueFor(second);\n        }\n    }\n\n    /**\n     * A wrapper over a guava table for setting and retrieving objects by name and type. You can\n     * think of this as a way for us to bind together references during annotation processing\n     *\n     * @author cstaylor\n     */\n    static class CreationContextImpl implements CreationContext\n    {\n        private final Table<String, Class<?>, Object> values;\n\n        CreationContextImpl()\n        {\n            this.values = HashBasedTable.create();\n        }\n\n        /**\n         * Get a saved object by name and its class\n         */\n        @Override\n        public <T> T get(final String name, final Class<T> klass)\n        {\n            final Object val = this.values.get(name, klass);\n            if (val != null)\n            {\n                return klass.cast(val);\n            }\n            return null;\n        }\n\n        /**\n         * Set an object by name and its class; the passed class doesn't need to be the\n         * implementation class, but it must be possible to cast object as a reference of that type\n         * (for example, klass = java.awt.Window.class, object = java.awt.Frame instance)\n         */\n        @Override\n        public <T> void set(final String name, final Class<T> klass, final T object)\n        {\n            this.values.put(name, klass, object);\n        }\n    }\n\n    /**\n     * This is how we sort the fields in the evaluate() method\n     *\n     * @author cstaylor\n     */\n    static class FieldComparator implements Comparator<Field>\n    {\n        public static final int sortValueFor(final Field field)\n        {\n            int currentValue = Integer.MAX_VALUE;\n            for (final Annotation current : field.getAnnotations())\n            {\n                currentValue = Math.min(currentValue, AnnotationComparator.sortValueFor(current));\n            }\n            return currentValue;\n        }\n\n        @Override\n        public int compare(final Field first, final Field second)\n        {\n            return sortValueFor(first) - sortValueFor(second);\n        }\n    }\n\n    /**\n     * Static global map of Java Annotation classes to FieldHandler instance\n     */\n    private static final SortedMap<Class<? extends Annotation>, FieldHandler> supportedAnnotations;\n\n    /**\n     * The priority order for the annotations we'll process\n     */\n    private static final List<Class<? extends Annotation>> annotationLookup;\n\n    /**\n     * Initialize both static tables\n     */\n    static\n    {\n        final List<Class<? extends Annotation>> temporaryLookupTable = Arrays\n                .asList(TestAtlas.class, Bean.class);\n        annotationLookup = Collections.unmodifiableList(temporaryLookupTable);\n\n        final TreeMap<Class<? extends Annotation>, FieldHandler> temp = new TreeMap<>(\n                new AnnotationClassComparator());\n        temp.put(TestAtlas.class, new TestAtlasHandler());\n        temp.put(Bean.class, new BeanHandler());\n        supportedAnnotations = Collections.unmodifiableSortedMap(temp);\n    }\n\n    @Override\n    public Statement apply(final Statement base, final Description description)\n    {\n        return new Statement()\n        {\n            /**\n             * Algorithm for automatically generating test fixture objects from the annotations\n             * declared in our subclass: - Gather all of the public fields together - Filter out any\n             * fields we don't know to support - Sort the remaining fields into dependency order;\n             * basic objects first, complex objects in order of their dependency; this means we can\n             * declare variables in any order in our subclasses - For each field, get the\n             * annotations on that field - If we support the annotation, ask its handler to create\n             * the object and store the new object into the field's slot using reflection - After\n             * we've finished populating the fields in our subclass, call the super version of\n             * evaluate()\n             */\n            @Override\n            public void evaluate() throws Throwable\n            {\n                /* Step 1: Get our fields */\n                final List<Field> fields = findAllFieldsIn(CoreTestRule.this.getClass(),\n                        new ArrayList<>());\n\n                /* Step 2: Sort the remaining fields */\n                Collections.sort(fields, new FieldComparator());\n\n                /* Step 3: Iterate the fields and execute each */\n                try\n                {\n                    final CreationContextImpl context = new CreationContextImpl();\n                    for (final Field field : fields)\n                    {\n                        for (final Annotation annotation : field.getAnnotations())\n                        {\n                            final FieldHandler handler = supportedAnnotations\n                                    .get(annotation.annotationType());\n                            if (handler != null && handler.handles(field))\n                            {\n                                handler.create(field, CoreTestRule.this, context);\n                            }\n                        }\n                    }\n                }\n                catch (final Throwable oops)\n                {\n                    throw new CoreException(\"Error when processing fields in test code annotations\",\n                            oops);\n                }\n                base.evaluate();\n            }\n        };\n    }\n\n    /**\n     * Do we handle this Field?\n     *\n     * @param field\n     *            the field to interrogate\n     * @return true if we can handle it, false otherwise\n     */\n    private boolean filter(final Field field)\n    {\n        return FieldComparator.sortValueFor(field) < Integer.MAX_VALUE;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private List<Field> findAllFieldsIn(final Class<? extends CoreTestRule> klass,\n            final List<Field> fields)\n    {\n        /*\n         * Shamelessly stolen from SO:\n         * http://stackoverflow.com/questions/1196192/how-do-i-read-a-private-field-in-java\n         */\n        for (final Field field : klass.getDeclaredFields())\n        {\n            if (filter(field))\n            {\n                field.setAccessible(true);\n                fields.add(field);\n            }\n        }\n        if (CoreTestRule.class.isAssignableFrom(klass.getSuperclass()))\n        {\n            findAllFieldsIn((Class<? extends CoreTestRule>) klass.getSuperclass(), fields);\n        }\n        return fields;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/CreationContext.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\n/**\n * Lookup table based on name of the field, the data type of what can be stored in the field, and\n * the object that corresponds to the mapping of name and implementation class.\n *\n * @author cstaylor\n */\npublic interface CreationContext\n{\n    /**\n     * Based on a name and implementation class pair, return an instance of type T, which should be\n     * of the same type as klass\n     *\n     * @param name\n     *            the name of the object to retrieve\n     * @param klass\n     *            the type of the object to retrieve\n     * @param <T>\n     *            the type of object we're looking for within this context\n     * @return T if it exists within this context, null otherwise\n     */\n    <T> T get(String name, Class<T> klass);\n\n    /**\n     * Set a mapping between the class pair of klass and name to an object\n     *\n     * @param name\n     *            the name of the object\n     * @param klass\n     *            the type of the object\n     * @param object\n     *            the instance of klass that should be retained\n     * @param <T>\n     *            the type of object we're setting within this context\n     */\n    <T> void set(String name, Class<T> klass, T object);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/FeatureIDGenerator.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.util.NavigableSet;\nimport java.util.TreeSet;\n\n/**\n * Non-threadsafe class for tracking and assigning feature ids\n *\n * @author cstaylor\n */\nclass FeatureIDGenerator\n{\n    private final NavigableSet<Long> usedIds;\n\n    FeatureIDGenerator()\n    {\n        this.usedIds = new TreeSet<>();\n    }\n\n    public long nextId(final String value)\n    {\n        if (null == value)\n        {\n            throw new IllegalArgumentException(\"value can't be null\");\n        }\n        Long currentValue = null;\n        if (value.equalsIgnoreCase(TestAtlas.AUTO_GENERATED))\n        {\n            currentValue = this.usedIds.floor(Long.MAX_VALUE);\n            currentValue = currentValue == null ? 1L : currentValue + 1;\n        }\n        else\n        {\n            currentValue = Long.parseLong(value);\n        }\n        if (this.usedIds.contains(currentValue))\n        {\n            throw new IllegalStateException(\n                    String.format(\"%d has already been assigned\", currentValue));\n        }\n        if (currentValue == Long.MAX_VALUE)\n        {\n            throw new IllegalStateException(\"No more IDs available\");\n        }\n\n        this.usedIds.add(currentValue);\n        return currentValue;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/FieldHandler.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.lang.reflect.Field;\n\n/**\n * A FieldHandler inspects JUnit rule class fields for annotations, interprets the annotations, and\n * then sets the field's value based on that interpretation.\n *\n * @author cstaylor\n */\npublic interface FieldHandler\n{\n    /**\n     * Based on the annotations set on field, create an object compatible with Field based on any\n     * annotations, other values in rule, and existing data within context\n     *\n     * @param field\n     *            the field and associated annotations in question\n     * @param rule\n     *            the containing rule where field exists\n     * @param context\n     *            a cache of previously made objects that may be reused depending on the test\n     *            implementation\n     */\n    void create(Field field, CoreTestRule rule, CreationContext context);\n\n    /**\n     * Is this the kind of field we can handle? Implementations may check the type of the field or\n     * the annotations on it to determine if we should handle setting the value of the field\n     *\n     * @param field\n     *            the field in question\n     * @return true if this field is suitable for a call to create, false otherwise\n     */\n    boolean handles(Field field);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/FreezeDryFunction.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.util.function.Function;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Function that does a serialize and deserialize pair of operations in memory so we can test if an\n * object will serialize correctly.\n *\n * @author brian_l_davis\n * @author cstaylor\n * @param <T>\n *            the type of object being tested\n */\npublic class FreezeDryFunction<T extends Serializable> implements Function<T, T>\n{\n    @Override\n    public T apply(final T source) throws CoreException\n    {\n        try\n        {\n            final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n            final ObjectOutputStream objectOutputStream = new ObjectOutputStream(\n                    byteArrayOutputStream);\n            objectOutputStream.writeObject(source);\n            objectOutputStream.close();\n            final ObjectInputStream objectInputStream = new ObjectInputStream(\n                    new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));\n            @SuppressWarnings(\"unchecked\")\n            final T result = (T) objectInputStream.readObject();\n            return result;\n        }\n        catch (final Exception oops)\n        {\n            throw new CoreException(\"Failure during serialization/deserialization pair\", oops);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/OsmFileParser.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * Parses an OSM file created by JOSM and makes it look like real OSM data\n *\n * @author matthieun\n */\npublic class OsmFileParser\n{\n    // List of regexes being replaced. The first item in the tuple is the regex, and the second is\n    // the replacement.\n    private final List<Tuple<String, String>> replacements;\n\n    public OsmFileParser()\n    {\n        this.replacements = new ArrayList<>();\n        this.replacements.add(new Tuple<>(\"id=\\\\'-\", \"id=\\\\'\"));\n        this.replacements.add(new Tuple<>(\"ref=\\\\'-\", \"ref=\\\\'\"));\n        this.replacements.add(new Tuple<>(\"action=\\\\'modify\\\\'\",\n                \"uid=\\\\'1\\\\' version=\\\\'1\\\\' changeset=\\\\'1\\\\' user=\\\\'myself\\\\'\"\n                        // Here the timestamp is meaningless, just there so osmosis can read the XML\n                        // file.\n                        + \" timestamp=\\\\'2017-12-19T21:43:02Z\\\\' action=\\\\'modify\\\\'\"));\n        this.replacements.add(new Tuple<>(\"generator=\\\\'JOSM\\\\'\",\n                \"generator=\\\\'JOSM\\\\' timestamp=\\\\'2017-12-19T21:43:02Z\\\\'\"));\n    }\n\n    public void update(final Resource josmOsmFile, final WritableResource osmFile)\n    {\n        final StringList result = new StringList();\n        for (final String line : josmOsmFile.lines())\n        {\n            String replaced = line;\n            for (final Tuple<String, String> replacement : this.replacements)\n            {\n                replaced = replaced.replaceAll(replacement.getFirst(), replacement.getSecond());\n            }\n            result.add(replaced);\n        }\n        osmFile.writeAndClose(result.join(System.lineSeparator()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/OsmFileToPbf.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.osmosis.osmbinary.file.BlockOutputStream;\nimport org.openstreetmap.osmosis.xml.common.CompressionMethod;\n\nimport crosby.binary.osmosis.OsmosisSerializer;\n\n/**\n * @author matthieun\n */\npublic class OsmFileToPbf\n{\n    public void update(final Resource osmFile, final WritableResource pbfFile)\n    {\n        final OsmosisXmlReaderFromResource osmReader = new OsmosisXmlReaderFromResource(osmFile,\n                true, CompressionMethod.None);\n        final OsmosisSerializer pbfWriter = new OsmosisSerializer(\n                new BlockOutputStream(pbfFile.write()));\n        osmReader.setSink(pbfWriter);\n        osmReader.run();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/OsmosisXmlReaderFromResource.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Collections;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.parsers.SAXParser;\nimport javax.xml.parsers.SAXParserFactory;\n\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.osmosis.core.OsmosisRuntimeException;\nimport org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;\nimport org.openstreetmap.osmosis.core.task.v0_6.Sink;\nimport org.openstreetmap.osmosis.xml.common.CompressionActivator;\nimport org.openstreetmap.osmosis.xml.common.CompressionMethod;\nimport org.openstreetmap.osmosis.xml.v0_6.XmlReader;\nimport org.openstreetmap.osmosis.xml.v0_6.impl.OsmHandler;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.SAXParseException;\n\n/**\n * From {@link XmlReader}. Almost everything here is copied from osmosis-xml version 0.44.1. The\n * only change is to allow reading from a {@link Resource} instead from a {@link File}.\n *\n * @author Brett Henderson\n * @author matthieun\n */\npublic class OsmosisXmlReaderFromResource implements RunnableSource\n{\n    private static final Logger log = Logger\n            .getLogger(OsmosisXmlReaderFromResource.class.getName());\n\n    private Sink sink;\n\n    private final Resource resource;\n    private final boolean enableDateParsing;\n    private final CompressionMethod compressionMethod;\n\n    /**\n     * Creates a new SAX parser.\n     *\n     * @return The newly created SAX parser.\n     */\n    private static SAXParser createParser()\n    {\n        try\n        {\n            final SAXParserFactory factory = SAXParserFactory.newInstance();\n            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);\n            return factory.newSAXParser();\n        }\n        catch (final ParserConfigurationException | SAXException e)\n        {\n            throw new OsmosisRuntimeException(\"Unable to create SAX Parser.\", e);\n        }\n    }\n\n    /**\n     * Creates a new instance.\n     *\n     * @param resource\n     *            The resource to read.\n     * @param enableDateParsing\n     *            If true, dates will be parsed from xml data, else the current date will be used\n     *            thus saving parsing time.\n     * @param compressionMethod\n     *            Specifies the compression method to employ.\n     */\n    public OsmosisXmlReaderFromResource(final Resource resource, final boolean enableDateParsing,\n            final CompressionMethod compressionMethod)\n    {\n        this.resource = resource;\n        this.enableDateParsing = enableDateParsing;\n        this.compressionMethod = compressionMethod;\n    }\n\n    /**\n     * Reads all data from the file and send it to the sink.\n     */\n    @Override\n    public void run()\n    {\n        final InputStream inputStream = this.getInputStream();\n        try (Sink temporarySink = this.sink)\n        {\n            final SAXParser parser;\n            temporarySink.initialize(Collections.emptyMap());\n\n            parser = createParser();\n            parser.parse(inputStream, new OsmHandler(this.sink, this.enableDateParsing));\n            temporarySink.complete();\n        }\n        catch (final SAXParseException e)\n        {\n            throw new OsmosisRuntimeException(\n                    \"Unable to parse xml resource \" + this.resource.getName() + \".  publicId=(\"\n                            + e.getPublicId() + \"), systemId=(\" + e.getSystemId() + \"), lineNumber=\"\n                            + e.getLineNumber() + \", columnNumber=\" + e.getColumnNumber() + \".\",\n                    e);\n        }\n        catch (final SAXException e)\n        {\n            throw new OsmosisRuntimeException(\"Unable to parse XML.\", e);\n        }\n        catch (final IOException e)\n        {\n            throw new OsmosisRuntimeException(\n                    \"Unable to read XML file \" + this.resource.getName() + \".\", e);\n        }\n        finally\n        {\n            try\n            {\n                inputStream.close();\n            }\n            catch (final IOException e)\n            {\n                log.log(Level.SEVERE, \"Unable to close input stream.\", e);\n            }\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setSink(final Sink sink)\n    {\n        this.sink = sink;\n    }\n\n    /**\n     * Get the input stream\n     *\n     * @return The input stream for the resource (non-null), but may throw a\n     *         {@link OsmosisRuntimeException}.\n     */\n    private InputStream getInputStream()\n    {\n        final InputStream temporaryInputStream;\n        // make \"-\" an alias for /dev/stdin\n        if (this.resource.getName() != null && \"-\".equals(this.resource.getName()))\n        {\n            temporaryInputStream = System.in;\n        }\n        else\n        {\n            temporaryInputStream = this.resource.read();\n        }\n        return new CompressionActivator(this.compressionMethod)\n                .createCompressionInputStream(temporaryInputStream);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/TestAtlas.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area.Known;\n\n/**\n * Describes an Atlas in test code. Beneath the covers we use a {@link PackedAtlasBuilder} to create\n * the Atlas object.\n *\n * @author cstaylor\n */\n@Retention(value = RUNTIME)\n@Target(value = FIELD)\npublic @interface TestAtlas\n{\n    /**\n     * Adds an area to an Atlas\n     *\n     * @author cstaylor\n     */\n    public @interface Area\n    {\n        /**\n         * Known (constant) areas that we can use in testing when we aren't trying to verify\n         * geometry. if USE_COORDINATES is defined, the field handler should use the coordinates\n         * value to build an area\n         *\n         * @author cstaylor\n         */\n        enum Known\n        {\n            SILICON_VALLEY,\n            USE_COORDINATES,\n            BUILDING_1,\n            BUILDING_2;\n        }\n\n        Loc[] coordinates() default {};\n\n        String id() default AUTO_GENERATED;\n\n        /**\n         * We can also specify a known Area using the known enum above\n         *\n         * @return should we be using specified coordinates or a well-known set of coordinates?\n         */\n        Known known() default Known.USE_COORDINATES;\n\n        /**\n         * Tags we want on the Area\n         *\n         * @return array of tag annotations\n         */\n        String[] tags() default {};\n    }\n\n    /**\n     * While this could be accomplished through areas, tags, and relations, it would make the test\n     * fixture code shorter to have a specialized annotation just for buildings\n     *\n     * @author cstaylor\n     */\n    public @interface Building\n    {\n        String id() default AUTO_GENERATED;\n\n        /**\n         * The optional inner areas of a building\n         *\n         * @return an array of areas that mark the inside of buildings\n         */\n        Area[] inners() default {};\n\n        /**\n         * The required outer area of a building\n         *\n         * @return an area representing the outer structure of the building: defaults to our Silicon\n         *         Valley-sized building\n         */\n        Area outer() default @Area(known = Known.SILICON_VALLEY);\n\n        /**\n         * Tags we want on the outline of the Building\n         *\n         * @return array of tag annotations specific for the outline relation\n         */\n        String[] outlineTags() default {};\n\n        /**\n         * The optional building parts of a building\n         *\n         * @return an array of areas representing parts of a larger building structure\n         */\n        Area[] parts() default {};\n\n        /**\n         * Tags we want on the Building\n         *\n         * @return array of tag annotations\n         */\n        String[] tags() default {};\n    }\n\n    /**\n     * Adds an edge to an Atlas\n     *\n     * @author cstaylor\n     */\n    public @interface Edge\n    {\n        Loc[] coordinates() default {};\n\n        String id() default AUTO_GENERATED;\n\n        String[] tags() default {};\n    }\n\n    /**\n     * Adds a line to an Atlas\n     *\n     * @author cstaylor\n     */\n    public @interface Line\n    {\n        Loc[] coordinates() default {};\n\n        String id() default AUTO_GENERATED;\n\n        String[] tags() default {};\n    }\n\n    /**\n     * Represents a single Location (lat,lon) coordinate. You can also use a String instead of\n     * individual coordinates. We support the following formats for value:\n     * <ul>\n     * <li>USE_LATLON - the values should come from the lat and lon parameters</li>\n     * <li>TEST_1 - Value of Location.TEST_1</li>\n     * <li>lat string,lon string - parse the latitude and longitude from the string</li>\n     * </ul>\n     *\n     * @author cstaylor\n     */\n    public @interface Loc\n    {\n        String USE_LATLON = \"<novalue>\";\n\n        String TEST_1 = \"<test1>\";\n\n        double BAD_VALUE = Long.MIN_VALUE;\n\n        double lat() default BAD_VALUE;\n\n        double lon() default BAD_VALUE;\n\n        String value() default TEST_1;\n    }\n\n    /**\n     * Adds a node to an Atlas\n     *\n     * @author cstaylor\n     */\n    public @interface Node\n    {\n        Loc coordinates() default @Loc;\n\n        String id() default AUTO_GENERATED;\n\n        /**\n         * Tags we want on the Area\n         *\n         * @return array of tag annotations\n         */\n        String[] tags() default {};\n    }\n\n    /**\n     * Adds a point to an Atlas\n     *\n     * @author cstaylor\n     */\n    public @interface Point\n    {\n        Loc coordinates() default @Loc;\n\n        String id() default AUTO_GENERATED;\n\n        /**\n         * Tags we want on the Area\n         *\n         * @return array of tag annotations\n         */\n        String[] tags() default {};\n    }\n\n    /**\n     * Adds a Relation to an Atlas\n     *\n     * @author matthieun\n     */\n    public @interface Relation\n    {\n        /**\n         * Adds a Member to a Relation\n         *\n         * @author matthieun\n         */\n        public @interface Member\n        {\n            String id();\n\n            String role();\n\n            String type();\n        }\n\n        String id() default AUTO_GENERATED;\n\n        Member[] members() default {};\n\n        String osmId() default DEFAULT_OSM_ID;\n\n        /**\n         * Tags we want on the Relation\n         *\n         * @return array of tag annotations\n         */\n        String[] tags() default {};\n\n        String wkt() default \"\";\n    }\n\n    /**\n     * Optional size estimate for the Atlas\n     *\n     * @author cstaylor\n     */\n    public @interface SizeEstimate\n    {\n        long NO_VALUE = 0;\n\n        long areas() default NO_VALUE;\n\n        long edges()\n\n        default NO_VALUE;\n\n        long lines()\n\n        default NO_VALUE;\n\n        long nodes()\n\n        default NO_VALUE;\n\n        long point()\n\n        default NO_VALUE;\n\n        long relations()\n\n        default NO_VALUE;\n    }\n\n    // This is for identifiers: by default they will be auto-generated\n    String AUTO_GENERATED = \"<auto>\";\n\n    String DEFAULT_OSM_ID = \"<default>\";\n\n    // This is for the atlas metadata: we only add the ISO country code if it is something besides\n    // unknown\n    String UNKNOWN_ISO_COUNTRY = \"UNKNOWN\";\n\n    /**\n     * Areas we want added to the atlas\n     *\n     * @return the array of areas we want added to the atlas\n     */\n    Area[] areas() default {};\n\n    /**\n     * While we could build buildings from areas and relations, this makes it easier to read in test\n     * fixture code.\n     *\n     * @return an array of building annotations\n     */\n    Building[] buildings() default {};\n\n    /**\n     * Edges we want added to the atlas\n     *\n     * @return the array of edges we want added to the atlas\n     */\n    Edge[] edges() default {};\n\n    /**\n     * ISO Country Code of the Atlas\n     *\n     * @return a string containing the ISO3 character code of the country or UNK if not set\n     */\n    String iso() default UNKNOWN_ISO_COUNTRY;\n\n    /**\n     * Lines we want added to the atlas\n     *\n     * @return the array of lines we want added to the atlas\n     */\n    Line[] lines() default {};\n\n    /**\n     * Sometimes we want to load the Atlas directly from an OSM file, that was generated by JOSM.\n     * Note that this setting takes precedence over directly defining values\n     *\n     * @return the resource path to the osm file\n     */\n    String loadFromJosmOsmResource() default \"\";\n\n    /**\n     * Sometimes we want to load the Atlas directly from an OSM file. Note that this setting takes\n     * precedence over directly defining values\n     *\n     * @return the resource path to the osm file\n     */\n    String loadFromOsmResource() default \"\";\n\n    /**\n     * Sometimes we want to load the Atlas directly from a file. Note that this setting takes\n     * precedence over directly defining values\n     *\n     * @return the resource path to the text file\n     */\n    String loadFromTextResource() default \"\";\n\n    /**\n     * Nodes we want added to the atlas\n     *\n     * @return the array of nodes we want added to the atlas\n     */\n    Node[] nodes() default {};\n\n    /**\n     * Points we want added to the atlas\n     *\n     * @return the array of points we want added to the atlas\n     */\n    Point[] points() default {};\n\n    /**\n     * Relations we want added to the atlas\n     *\n     * @return the array of relations we want added to the atlas\n     */\n    Relation[] relations() default {};\n\n    /**\n     * Size estimate for the atlas\n     *\n     * @return the size estimate of the atlas\n     */\n    SizeEstimate size() default @SizeEstimate;\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/TestAtlasHandler.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.lang.reflect.Field;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.TreeSet;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.AbstractResource;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.ClassResource;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.tags.BuildingPartTag;\nimport org.openstreetmap.atlas.tags.BuildingTag;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area.Known;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Building;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.SizeEstimate;\n\n/**\n * Handler implementation for Atlas fields annotated with the TestAtlas annotation\n *\n * @author cstaylor\n * @author bbreithaupt\n */\npublic class TestAtlasHandler implements FieldHandler\n{\n    public static Atlas getAtlasFromJosmOsmResource(final boolean josmFormat,\n            final AbstractResource resource, final String fileName)\n    {\n        return getAtlasFromJosmOsmResource(josmFormat, resource, fileName, Optional.empty());\n    }\n\n    public static Atlas getAtlasFromJosmOsmResource(final boolean josmFormat,\n            final AbstractResource resource, final String fileName, final Optional<String> iso)\n    {\n        FileSuffix.suffixFor(fileName).ifPresent(suffix ->\n        {\n            if (suffix == FileSuffix.GZIP)\n            {\n                resource.setDecompressor(Decompressor.GZIP);\n            }\n        });\n        final ByteArrayResource pbfFile = new ByteArrayResource();\n        if (josmFormat)\n        {\n            // If the XML file is in JOSM format, fix it to look like an OSM file\n            final StringResource osmFile = new StringResource();\n            new OsmFileParser().update(resource, osmFile);\n            new OsmFileToPbf().update(osmFile, pbfFile);\n        }\n        else\n        {\n            new OsmFileToPbf().update(resource, pbfFile);\n        }\n        return buildAtlasFromPbf(pbfFile, iso);\n    }\n\n    /**\n     * Builds an {@link Atlas} from the given pbf resource, using the raw atlas flow. This does NOT\n     * country-slice the pbf resource since no corresponding boundary file is supplied and the flow\n     * is not meant to test slicing logic.\n     *\n     * @param pbfResource\n     *            The pbf input resource to use\n     * @param iso\n     *            ISO code to be applied to all features\n     * @return the resulting Atlas\n     */\n    private static Atlas buildAtlasFromPbf(final Resource pbfResource, final Optional<String> iso)\n    {\n        // Create raw Atlas\n        final AtlasLoadingOption loadingOption = AtlasLoadingOption.withNoFilter();\n        if (iso.isPresent())\n        {\n            final List<org.locationtech.jts.geom.Polygon> boundaries = new ArrayList<>();\n            MultiPolygon.MAXIMUM.outers()\n                    .forEach(outer -> boundaries.add(new JtsPolygonConverter().convert(outer)));\n            loadingOption.setCountrySlicing(true);\n            loadingOption.setCountryBoundaryMap(CountryBoundaryMap\n                    .fromBoundaryMap(Collections.singletonMap(iso.get(), boundaries)));\n        }\n        final Atlas rawAtlas = new RawAtlasGenerator(pbfResource, loadingOption,\n                MultiPolygon.MAXIMUM).build();\n\n        // Country Slice and Way-Section\n        return new AtlasSectionProcessor(\n                iso.isPresent() ? new RawAtlasSlicer(loadingOption, rawAtlas).slice() : rawAtlas,\n                loadingOption).run();\n    }\n\n    private static Map<String, String> mergeTags(final Map<String, String> firstTags,\n            final Map<String, String> secondTags)\n    {\n        final Map<String, String> returnValue = new HashMap<>();\n        returnValue.putAll(firstTags);\n        returnValue.putAll(secondTags);\n        return returnValue;\n    }\n\n    private static String[] mergeTags(final String[] firstTags, final String[] secondTags)\n    {\n        final List<String> allTags = new ArrayList<>(Arrays.asList(firstTags));\n        allTags.addAll(Arrays.asList(secondTags));\n        return allTags.toArray(new String[0]);\n    }\n\n    private static Map<String, String> parseTags(final Optional<String> iso, final String... tags)\n    {\n        final Map<String, String> tagmap = new HashMap<>();\n        for (final String tagAndValue : tags)\n        {\n            final StringList fullySplit = StringList.split(tagAndValue, \"=\");\n            if (fullySplit.size() == 2)\n            {\n                // Standard key=value case\n                tagmap.put(fullySplit.get(0), fullySplit.get(1));\n            }\n            else if (fullySplit.size() == 1)\n            {\n                // Case of a key without a value. The value becomes an empty string\n                tagmap.put(fullySplit.get(0), \"\");\n            }\n            else\n            {\n                // Erroneous case\n                throw new CoreException(\"{} isn't a valid tag description\", tagAndValue);\n            }\n        }\n        // Add a country code if one does not already exist\n        if (iso.isPresent() && !tagmap.containsKey(ISOCountryTag.KEY.toLowerCase()))\n        {\n            tagmap.put(ISOCountryTag.KEY.toLowerCase(), iso.get());\n        }\n        return tagmap;\n    }\n\n    @Override\n    public void create(final Field field, final CoreTestRule rule, final CreationContext context)\n    {\n        final TestAtlas testAtlas = field.getAnnotation(TestAtlas.class);\n        if (StringUtils.isNotEmpty(testAtlas.loadFromTextResource()))\n        {\n            try\n            {\n                loadFromTextResource(field, rule, context, testAtlas.loadFromTextResource());\n            }\n            catch (final Throwable e)\n            {\n                throw new CoreException(\"Error creating field {}\", field, e);\n            }\n        }\n        else if (StringUtils.isNotEmpty(testAtlas.loadFromJosmOsmResource()))\n        {\n            try\n            {\n                loadFromJosmOsmResource(field, rule, context, testAtlas.loadFromJosmOsmResource(),\n                        true);\n            }\n            catch (final Throwable e)\n            {\n                throw new CoreException(\"Error creating field {}\", field, e);\n            }\n        }\n        else if (StringUtils.isNotEmpty(testAtlas.loadFromOsmResource()))\n        {\n            try\n            {\n                loadFromJosmOsmResource(field, rule, context, testAtlas.loadFromOsmResource(),\n                        false);\n            }\n            catch (final Throwable e)\n            {\n                throw new CoreException(\"Error creating field {}\", field, e);\n            }\n        }\n        else\n        {\n            createDirectly(testAtlas, field, rule, context);\n        }\n    }\n\n    @Override\n    public boolean handles(final Field field)\n    {\n        final Class<?> fieldClass = field.getType();\n        return Atlas.class.isAssignableFrom(fieldClass);\n    }\n\n    private long addArea(final PackedAtlasBuilder builder, final FeatureIDGenerator areaIDGenerator,\n            final Area area, final Optional<String> iso, final String... additionalTags)\n    {\n        final long areaId = areaIDGenerator.nextId(area.id());\n        builder.addArea(areaId, buildAreaPolygon(area),\n                parseTags(iso, mergeTags(area.tags(), additionalTags)));\n        return areaId;\n    }\n\n    private Polygon buildAreaPolygon(final Area area)\n    {\n        if (area.known() == Known.USE_COORDINATES)\n        {\n            if (area.coordinates().length == 0)\n            {\n                return new Polygon(Location.TEST_1);\n            }\n            return convertPolygon(area.coordinates());\n        }\n        if (area.known() == Known.BUILDING_1)\n        {\n            return Polygon.TEST_BUILDING;\n        }\n        if (area.known() == Known.BUILDING_2)\n        {\n            return Polygon.TEST_BUILDING_PART;\n        }\n        // Right now we only have a single constant for areas\n        return Polygon.SILICON_VALLEY;\n    }\n\n    private Location convertLoc(final Loc point)\n    {\n        if (point.value().equalsIgnoreCase(Loc.USE_LATLON)\n                && (point.lat() == Loc.BAD_VALUE || point.lon() == Loc.BAD_VALUE))\n        {\n            throw new CoreException(\"Loc doesn't have a valid string value or lat/lon values\");\n        }\n\n        if (point.value().equalsIgnoreCase(Loc.USE_LATLON))\n        {\n            return new Location(Latitude.degrees(point.lat()), Longitude.degrees(point.lon()));\n        }\n\n        if (point.value().equalsIgnoreCase(Loc.TEST_1))\n        {\n            return Location.TEST_1;\n        }\n        return Location.forString(point.value());\n    }\n\n    private PolyLine convertPolyLine(final Loc[] points)\n    {\n        return new PolyLine(pointsToLocations(points));\n    }\n\n    private Polygon convertPolygon(final Loc[] points)\n    {\n        return new Polygon(pointsToLocations(points));\n    }\n\n    private AtlasSize convertSizeEstimates(final SizeEstimate estimate)\n    {\n        return new AtlasSize(estimate.edges(), estimate.nodes(), estimate.areas(), estimate.lines(),\n                estimate.point(), estimate.relations());\n    }\n\n    private void createDirectly(final TestAtlas testAtlas, final Field field,\n            final CoreTestRule rule, final CreationContext context)\n    {\n\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder().withEnhancedRelationGeometry();\n        final AtlasSize size = convertSizeEstimates(testAtlas.size());\n        final Optional<String> iso = testAtlas.iso().equals(TestAtlas.UNKNOWN_ISO_COUNTRY)\n                ? Optional.empty()\n                : Optional.of(testAtlas.iso());\n        if (iso.isPresent())\n        {\n            final AtlasMetaData metaData = new AtlasMetaData(size, true, null, null, iso.get(),\n                    null, Maps.hashMap());\n            builder.withMetaData(metaData);\n        }\n        else\n        {\n            builder.setSizeEstimates(size);\n        }\n        handle(builder, iso, testAtlas.nodes());\n        handle(builder, iso, testAtlas.edges());\n        handle(builder, iso, testAtlas.areas());\n        handle(builder, iso, testAtlas.lines());\n        handle(builder, iso, testAtlas.points());\n        handle(builder, iso, testAtlas.relations());\n        handle(builder, iso, testAtlas.buildings());\n\n        try\n        {\n            field.set(rule, builder.get());\n        }\n        catch (IllegalArgumentException | IllegalAccessException e)\n        {\n            throw new CoreException(\"Error creating test atlas\", e);\n        }\n    }\n\n    private void handle(final PackedAtlasBuilder builder, final Optional<String> iso,\n            final Area... areas)\n    {\n        final FeatureIDGenerator areaIDGenerator = new FeatureIDGenerator();\n        for (final Area area : areas)\n        {\n            addArea(builder, areaIDGenerator, area, iso);\n        }\n    }\n\n    private void handle(final PackedAtlasBuilder builder, final Optional<String> iso,\n            final Building... buildings)\n    {\n        final FeatureIDGenerator buildingGenerator = new FeatureIDGenerator();\n        for (final Building building : buildings)\n        {\n            final TreeSet<Long> outerIds = new TreeSet<>();\n            final TreeSet<Long> innerIds = new TreeSet<>();\n            final TreeSet<Long> partIds = new TreeSet<>();\n\n            outerIds.add(addArea(builder, buildingGenerator, building.outer(), iso));\n            for (final Area inner : building.inners())\n            {\n                innerIds.add(addArea(builder, buildingGenerator, inner, iso));\n            }\n            for (final Area part : building.parts())\n            {\n                partIds.add(addArea(builder, buildingGenerator, part, iso, BuildingPartTag.KEY,\n                        BuildingPartTag.YES.getTagValue()));\n            }\n\n            final RelationBean outline = new RelationBean();\n            outline.addItem(outerIds.first(), RelationTypeTag.MULTIPOLYGON_ROLE_OUTER,\n                    ItemType.AREA);\n            for (final Long innerId : innerIds)\n            {\n                outline.addItem(innerId, RelationTypeTag.MULTIPOLYGON_ROLE_INNER, ItemType.AREA);\n            }\n\n            final long outlineId = buildingGenerator.nextId(TestAtlas.AUTO_GENERATED);\n            builder.addRelation(outlineId, outlineId, outline,\n                    mergeTags(parseTags(iso, building.outlineTags()),\n                            Validators.toMap(RelationTypeTag.MULTIPOLYGON)));\n\n            final RelationBean multipart = new RelationBean();\n            multipart.addItem(outlineId, BuildingTag.BUILDING_ROLE_OUTLINE, ItemType.RELATION);\n            for (final Long partId : partIds)\n            {\n                multipart.addItem(partId, BuildingTag.BUILDING_ROLE_PART, ItemType.AREA);\n            }\n\n            final long buildingId = buildingGenerator.nextId(TestAtlas.AUTO_GENERATED);\n            builder.addRelation(buildingId, buildingId, multipart, mergeTags(\n                    parseTags(iso, building.tags()), Validators.toMap(RelationTypeTag.BUILDING)));\n        }\n    }\n\n    private void handle(final PackedAtlasBuilder builder, final Optional<String> iso,\n            final Edge... edges)\n    {\n        final FeatureIDGenerator edgeIDGenerator = new FeatureIDGenerator();\n        for (final Edge edge : edges)\n        {\n            builder.addEdge(edgeIDGenerator.nextId(edge.id()), convertPolyLine(edge.coordinates()),\n                    parseTags(iso, edge.tags()));\n        }\n    }\n\n    private void handle(final PackedAtlasBuilder builder, final Optional<String> iso,\n            final Line... lines)\n    {\n        final FeatureIDGenerator lineIDGenerator = new FeatureIDGenerator();\n        for (final Line line : lines)\n        {\n            builder.addLine(lineIDGenerator.nextId(line.id()), convertPolyLine(line.coordinates()),\n                    parseTags(iso, line.tags()));\n        }\n    }\n\n    private void handle(final PackedAtlasBuilder builder, final Optional<String> iso,\n            final Node... nodes)\n    {\n        final FeatureIDGenerator nodeIDGenerator = new FeatureIDGenerator();\n        for (final Node node : nodes)\n        {\n            builder.addNode(nodeIDGenerator.nextId(node.id()), convertLoc(node.coordinates()),\n                    parseTags(iso, node.tags()));\n        }\n    }\n\n    private void handle(final PackedAtlasBuilder builder, final Optional<String> iso,\n            final Point... points)\n    {\n        final FeatureIDGenerator pointIDGenerator = new FeatureIDGenerator();\n        for (final Point point : points)\n        {\n            builder.addPoint(pointIDGenerator.nextId(point.id()), convertLoc(point.coordinates()),\n                    parseTags(iso, point.tags()));\n        }\n    }\n\n    private void handle(final PackedAtlasBuilder builder, final Optional<String> iso,\n            final Relation... relations)\n    {\n        final FeatureIDGenerator relationIDGenerator = new FeatureIDGenerator();\n        final WKTReader wktReader = new WKTReader();\n        for (final Relation relation : relations)\n        {\n            final RelationBean bean = new RelationBean();\n            for (final Member member : relation.members())\n            {\n                bean.addItem(Long.parseLong(member.id()), member.role(),\n                        Enum.valueOf(ItemType.class, member.type().toUpperCase()));\n            }\n            final long identifier = relationIDGenerator.nextId(relation.id());\n            final long osmIdentifier = relation.osmId().equals(TestAtlas.DEFAULT_OSM_ID)\n                    ? identifier\n                    : relationIDGenerator.nextId(relation.osmId());\n            try\n            {\n                builder.addRelation(identifier, osmIdentifier, bean,\n                        parseTags(iso, relation.tags()),\n                        (org.locationtech.jts.geom.MultiPolygon) wktReader.read(relation.wkt()));\n            }\n            catch (final ParseException e)\n            {\n                throw new CoreException(\"Couldn't parse geometry for relation {}\", relation);\n            }\n        }\n    }\n\n    private void loadFromJosmOsmResource(final Field field, final CoreTestRule rule,\n            final CreationContext context, final String resourcePath, final boolean josmFormat)\n    {\n        final String packageName = rule.getClass().getPackage().getName().replaceAll(\"\\\\.\", \"/\");\n        final String completeName = String.format(\"%s/%s\", packageName, resourcePath);\n        try\n        {\n            field.set(rule, getAtlasFromJosmOsmResource(josmFormat, new ClassResource(completeName),\n                    Paths.get(completeName).getFileName().toString(),\n                    field.getAnnotation(TestAtlas.class).iso().equals(TestAtlas.UNKNOWN_ISO_COUNTRY)\n                            ? Optional.empty()\n                            : Optional.of(field.getAnnotation(TestAtlas.class).iso())));\n        }\n        catch (IllegalArgumentException | IllegalAccessException e)\n        {\n            throw new CoreException(\"Error loading from JOSM osm resource {}\", resourcePath, e);\n        }\n\n    }\n\n    private void loadFromTextResource(final Field field, final CoreTestRule rule,\n            final CreationContext context, final String resourcePath)\n    {\n        final String packageName = rule.getClass().getPackage().getName().replaceAll(\"\\\\.\", \"/\");\n        final String completeName = String.format(\"%s/%s\", packageName, resourcePath);\n        final ClassResource resource = new ClassResource(completeName);\n        FileSuffix.suffixFor(completeName).ifPresent(suffix ->\n        {\n            if (suffix == FileSuffix.GZIP)\n            {\n                resource.setDecompressor(Decompressor.GZIP);\n            }\n        });\n        try\n        {\n            field.set(rule, new TextAtlasBuilder().read(resource));\n        }\n        catch (IllegalArgumentException | IllegalAccessException e)\n        {\n            throw new CoreException(\"Error loading from text resource {}\", resourcePath, e);\n        }\n    }\n\n    private List<Location> pointsToLocations(final Loc[] points)\n    {\n        final List<Location> locations = new ArrayList<>();\n        for (final Loc point : points)\n        {\n            locations.add(convertLoc(point));\n        }\n        return locations;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/testing/TestTaggable.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * Simple taggable interface used for test cases\n *\n * @author cstaylor\n */\npublic class TestTaggable implements Taggable\n{\n    private final Map<String, String> tags;\n\n    public TestTaggable(final Enum<?>... enums)\n    {\n        this(Validators.toMap(enums));\n    }\n\n    public TestTaggable(final Map<String, String> tags)\n    {\n        this.tags = tags;\n    }\n\n    public TestTaggable(final String key, final String value)\n    {\n        this(Maps.hashMap(key, value));\n    }\n\n    @Override\n    public Optional<String> getTag(final String key)\n    {\n        return Optional.ofNullable(this.tags.get(key));\n    }\n\n    @Override\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/threads/CustomNamesThreadPoolFactory.java",
    "content": "package org.openstreetmap.atlas.utilities.threads;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Give our threads pretty names\n *\n * @author cstaylor\n * @author matthieun\n */\nclass CustomNamesThreadPoolFactory implements ThreadFactory\n{\n    private final AtomicInteger poolNumber = new AtomicInteger(1);\n    private final ThreadGroup group;\n    private final AtomicInteger threadNumber = new AtomicInteger(1);\n    private final String namePrefix;\n\n    protected CustomNamesThreadPoolFactory(final String poolName)\n    {\n        final SecurityManager securityManager = System.getSecurityManager();\n        this.group = securityManager != null ? securityManager.getThreadGroup()\n                : Thread.currentThread().getThreadGroup();\n        this.namePrefix = poolName + \"(\" + this.poolNumber.getAndIncrement() + \")-thread-\";\n    }\n\n    @Override\n    public Thread newThread(final Runnable runme)\n    {\n        /**\n         * 0 isn't magic: it's what the javadocs say we should pass if we want the JVM to decide the\n         * appropriate stack depth\n         */\n        final String newThreadName = this.namePrefix + this.threadNumber.getAndIncrement();\n        return new Thread(this.group, runme, newThreadName, 0);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/threads/LogTicker.java",
    "content": "package org.openstreetmap.atlas.utilities.threads;\n\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class LogTicker extends Ticker\n{\n    private static final Logger logger = LoggerFactory.getLogger(LogTicker.class);\n\n    public LogTicker(final String name, final Duration tickerTime)\n    {\n        super(name, tickerTime);\n    }\n\n    @Override\n    protected void tickAction(final Duration sinceStart)\n    {\n        logger.info(\"{}: {}\", getName(), sinceStart);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/threads/Pool.java",
    "content": "package org.openstreetmap.atlas.utilities.threads;\n\nimport java.io.Closeable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Wrapper for a pool of threads.\n *\n * @author matthieun\n */\npublic class Pool implements Closeable\n{\n    /**\n     * Executor that will save all unhandled exceptions thrown by failed tasks to be thrown out\n     * later in a call to close.\n     *\n     * @author cstaylor\n     */\n    private class FixedThreadPoolExecutor extends ThreadPoolExecutor\n    {\n        FixedThreadPoolExecutor()\n        {\n            super(Pool.this.numberOfThreads, Pool.this.numberOfThreads, 0L, TimeUnit.MILLISECONDS,\n                    new LinkedBlockingQueue<Runnable>(),\n                    new CustomNamesThreadPoolFactory(Pool.this.name));\n        }\n\n        @Override\n        protected void afterExecute(final Runnable runnable, final Throwable oops)\n        {\n            if (oops != null)\n            {\n                Pool.this.errors.add(oops);\n            }\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(Pool.class);\n    private final ExecutorService pool;\n    private final String name;\n    private final int numberOfThreads;\n    private final Duration endTimeout;\n    private final List<Throwable> errors;\n\n    public Pool(final int numberOfThreads, final String name)\n    {\n        this(numberOfThreads, name, Duration.ONE_DAY);\n    }\n\n    public Pool(final int numberOfThreads, final String name, final Duration endTimeout)\n    {\n        this.numberOfThreads = Math.max(numberOfThreads, 1);\n        this.name = name;\n        this.endTimeout = endTimeout;\n        this.errors = Collections.synchronizedList(new ArrayList<>());\n        this.pool = new FixedThreadPoolExecutor();\n    }\n\n    @Override\n    public void close()\n    {\n        this.end(this.endTimeout);\n    }\n\n    /**\n     * This method checks that a thread pool is really finished, i.e. all the tasks have been\n     * completed.\n     *\n     * @param maxDuration\n     *            The maximum duration to wait before returning.\n     */\n    public void end(final Duration maxDuration)\n    {\n        if (!stop(maxDuration))\n        {\n            logger.warn(\"Thread pool {} has ended before it was terminated (maxDuration = {}).\",\n                    this.getName(), maxDuration);\n        }\n        if (!this.errors.isEmpty())\n        {\n            this.errors.forEach(error -> logger.error(\"Unhandled error in {}!\", this.name, error));\n            throw new CoreException(\n                    \"{} tasks in {} had uncaught errors! Attaching one of those as cause.\",\n                    this.errors.size(), this.name, this.errors.iterator().next());\n        }\n    }\n\n    public String getName()\n    {\n        return this.name;\n    }\n\n    public boolean isDead()\n    {\n        return this.pool.isTerminated() || this.pool.isShutdown();\n    }\n\n    public <T> Result<T> queue(final Callable<T> task)\n    {\n        return new Result<>(this.pool.submit(task), this);\n    }\n\n    public <T, V> V queue(final Callable<T> task, final Function<T, V> doWithTheOutput,\n            final Duration timeout) throws TimeoutException\n    {\n        final Result<T> result = queue(task);\n        final T item = result.get(timeout);\n        return doWithTheOutput.apply(item);\n    }\n\n    public <T> Result<T> queue(final Callable<T> task, final Ticker ticker)\n    {\n        final Callable<T> taskWrapper = () ->\n        {\n            try\n            {\n                return task.call();\n            }\n            finally\n            {\n                ticker.close();\n            }\n        };\n        this.queue(ticker);\n        return new Result<>(this.pool.submit(taskWrapper), this, ticker);\n    }\n\n    public void queue(final Runnable command)\n    {\n        this.pool.execute(command);\n    }\n\n    public void queue(final Runnable command, final Ticker ticker)\n    {\n        final Runnable commandWrapper = () ->\n        {\n            try\n            {\n                command.run();\n            }\n            finally\n            {\n                ticker.close();\n            }\n        };\n        this.pool.execute(ticker);\n        this.pool.execute(commandWrapper);\n    }\n\n    public <T> List<Result<T>> queueAll(final Iterable<Callable<T>> tasks)\n    {\n        try\n        {\n            final List<Future<T>> results = this.pool.invokeAll(Iterables.asList(tasks));\n            return results.stream().flatMap(future -> Stream.of(new Result<>(future, this)))\n                    .collect(Collectors.toList());\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not submit multiple Callables to {}\", e, this.name);\n        }\n    }\n\n    public <T, V> List<V> queueAll(final Iterable<Callable<T>> tasks,\n            final Function<T, V> doWithTheOutput, final Duration timeoutForEach)\n    {\n        final List<V> result = new ArrayList<>();\n        final List<Result<T>> output = queueAll(tasks);\n        output.forEach(futureResult ->\n        {\n            try\n            {\n                final T input = futureResult.get(timeoutForEach);\n                final V out = doWithTheOutput.apply(input);\n                result.add(out);\n            }\n            catch (final TimeoutException e)\n            {\n                logger.warn(\"Timed out on {}\", futureResult);\n            }\n        });\n        return result;\n    }\n\n    public void queueCommands(final Iterable<Runnable> commands)\n    {\n        commands.forEach(this::queue);\n    }\n\n    public boolean stop(final Duration waitBeforeKill)\n    {\n        this.pool.shutdown();\n        try\n        {\n            return this.pool.awaitTermination(waitBeforeKill.asMilliseconds(),\n                    TimeUnit.MILLISECONDS);\n        }\n        catch (final Exception e)\n        {\n            logger.warn(\"Was interrupted. Could not stop {} within {}.\", this.name, waitBeforeKill,\n                    e);\n            return false;\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"[Pool: \" + getName() + \", \" + this.numberOfThreads + \" threads]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/threads/Result.java",
    "content": "package org.openstreetmap.atlas.utilities.threads;\n\nimport java.util.Optional;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\n\n/**\n * Wrapper for a {@link Future}\n *\n * @param <T>\n *            The type returned in the result\n * @author matthieun\n */\npublic class Result<T>\n{\n    private final Future<T> future;\n    private final Pool pool;\n    private final Optional<Ticker> ticker;\n\n    public Result(final Future<T> future, final Pool pool)\n    {\n        this.pool = pool;\n        this.future = future;\n        this.ticker = Optional.empty();\n    }\n\n    public Result(final Future<T> future, final Pool pool, final Ticker ticker)\n    {\n        this.pool = pool;\n        this.future = future;\n        this.ticker = Optional.of(ticker);\n    }\n\n    public boolean cancel(final boolean mayInterruptIfRunning)\n    {\n        this.ticker.ifPresent(Ticker::close);\n        return this.future.cancel(mayInterruptIfRunning);\n    }\n\n    public T get()\n    {\n        try\n        {\n            return this.future.get();\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not get value from Future in {}\", this.pool, e);\n        }\n        finally\n        {\n            this.ticker.ifPresent(Ticker::close);\n        }\n    }\n\n    public T get(final Duration timeout) throws TimeoutException\n    {\n        try\n        {\n            return this.future.get(timeout.asMilliseconds(), TimeUnit.MILLISECONDS);\n        }\n        catch (final TimeoutException tex)\n        {\n            throw tex;\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\n                    \"Interrupted before {} elapsed. Could not get value from Future in {}\", timeout,\n                    this.pool, e);\n        }\n        finally\n        {\n            this.ticker.ifPresent(Ticker::close);\n        }\n    }\n\n    public boolean isCancelled()\n    {\n        return this.future.isCancelled();\n    }\n\n    public boolean isDone()\n    {\n        return this.future.isDone();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/threads/Ticker.java",
    "content": "package org.openstreetmap.atlas.utilities.threads;\n\nimport java.io.Closeable;\n\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Ticker companion.\n *\n * @author matthieun\n */\npublic abstract class Ticker implements Runnable, Closeable\n{\n    private static final Logger logger = LoggerFactory.getLogger(Ticker.class);\n    private static final Duration CHECK_TIME = Duration.milliseconds(500);\n\n    private final String name;\n    private final Duration tickerTime;\n\n    // This tells the ticker it is time to stop ticking.\n    private volatile boolean stop;\n\n    /**\n     * @param name\n     *            The name of the ticker companion\n     * @param tickerTime\n     *            The duration between each tick. It is indicative only.\n     */\n    public Ticker(final String name, final Duration tickerTime)\n    {\n        this.name = name;\n        this.tickerTime = tickerTime;\n        this.stop = false;\n    }\n\n    @Override\n    public void close()\n    {\n        this.stop = true;\n    }\n\n    public String getName()\n    {\n        return this.name;\n    }\n\n    @Override\n    public void run()\n    {\n        final Time start = Time.now();\n        Time lastCheck = Time.now();\n        while (!this.stop)\n        {\n            // Sleep small, to check regularly. If the thread is \"closed\" it will then die fairly\n            // soon even if the ticker time is really long.\n            CHECK_TIME.lowest(this.tickerTime).sleep();\n            if (lastCheck.elapsedSince().isMoreThan(this.tickerTime))\n            {\n                try\n                {\n                    tickAction(start.elapsedSince());\n                }\n                catch (final Exception e)\n                {\n                    // In case of any error in tickAction, kill the ticker silently, with a nice\n                    // error message.\n                    logger.error(\"{} tick action failed! Associated job should not be affected.\",\n                            getName(), e);\n                }\n                lastCheck = Time.now();\n            }\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"Ticker [name=\" + this.name + \", tickerTime=\" + this.tickerTime + \"]\";\n    }\n\n    /**\n     * Act upon a tick event.\n     *\n     * @param sinceStart\n     *            The duration elapsed since the start of the ticker.\n     */\n    protected abstract void tickAction(Duration sinceStart);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/time/LocalTime.java",
    "content": "package org.openstreetmap.atlas.utilities.time;\n\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\n\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\n\n/**\n * Time class that uses the java.time package in 1.8+\n *\n * @author matthieun\n */\npublic class LocalTime\n{\n    private static final int NANO_PER_MILLI = 1_000_000;\n    private static final String DEFAULT_FORMAT = \"yyyyMMdd-HHmmss-zzz\";\n\n    // Duration from start of UNIX time\n    private final Duration epoch;\n    private final ZoneId timeZone;\n\n    // This is lazily populated!\n    private LocalDateTime dateTime = null;\n\n    public static LocalTime now(final ZoneId offset)\n    {\n        return new LocalTime(Duration.milliseconds(System.currentTimeMillis()), offset);\n    }\n\n    public LocalTime(final Duration epoch, final ZoneId timeZone)\n    {\n        this.epoch = epoch;\n        this.timeZone = timeZone;\n    }\n\n    public int day()\n    {\n        return getDateTime().getDayOfMonth();\n    }\n\n    public Duration elapsedBetween(final LocalTime time)\n    {\n        return Duration\n                .milliseconds(Math.abs(time.epoch.asMilliseconds() - this.epoch.asMilliseconds()));\n    }\n\n    public Duration elapsedSince()\n    {\n        return Duration.milliseconds(\n                now(this.timeZone).epoch.asMilliseconds() - this.epoch.asMilliseconds());\n    }\n\n    public String format(final DateTimeFormatter formatter)\n    {\n        return formatter.format(getDateTime());\n    }\n\n    public String format(final String pattern)\n    {\n        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);\n        return getDateTime().format(formatter);\n    }\n\n    public Duration getEpoch()\n    {\n        return this.epoch;\n    }\n\n    public int hour()\n    {\n        return getDateTime().getHour();\n    }\n\n    public int millisecond()\n    {\n        return (int) Math.round((double) getDateTime().getNano() / NANO_PER_MILLI);\n    }\n\n    public int minute()\n    {\n        return getDateTime().getMinute();\n    }\n\n    public int month()\n    {\n        return getDateTime().getMonthValue();\n    }\n\n    public int second()\n    {\n        return getDateTime().getSecond();\n    }\n\n    @Override\n    public String toString()\n    {\n        return format(DEFAULT_FORMAT);\n    }\n\n    public Duration untilNow()\n    {\n        return Duration.milliseconds(System.currentTimeMillis()).difference(this.epoch);\n    }\n\n    public int year()\n    {\n        return getDateTime().getYear();\n    }\n\n    private LocalDateTime getDateTime()\n    {\n        if (this.dateTime == null)\n        {\n            this.dateTime = LocalDateTime\n                    .ofInstant(Instant.ofEpochMilli(this.epoch.asMilliseconds()), this.timeZone);\n        }\n        return this.dateTime;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/time/Time.java",
    "content": "package org.openstreetmap.atlas.utilities.time;\n\nimport java.time.ZoneOffset;\n\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\n\n/**\n * UTC Time\n *\n * @author matthieun\n */\npublic class Time extends LocalTime\n{\n    public static Time now()\n    {\n        return new Time(Duration.milliseconds(System.currentTimeMillis()));\n    }\n\n    public Time(final Duration epoch)\n    {\n        super(epoch, ZoneOffset.UTC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/timezone/TimeZoneBoundary.java",
    "content": "package org.openstreetmap.atlas.utilities.timezone;\n\nimport java.util.TimeZone;\n\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\n\n/**\n * This {@link TimeZoneBoundary} holds {@link TimeZone} and {@link Polygon} object, and will be\n * stored in spatial index directly for best time zone query performance\n *\n * @author tony\n */\npublic class TimeZoneBoundary implements Located\n{\n    private final TimeZone timeZone;\n    private final Polygon polygon;\n\n    public TimeZoneBoundary(final TimeZone timeZone, final Polygon polygon)\n    {\n        this.timeZone = timeZone;\n        this.polygon = polygon;\n    }\n\n    /**\n     * @return the area as degrees\n     */\n    public Surface area()\n    {\n        return this.polygon.bounds().surface();\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.polygon.bounds();\n    }\n\n    public Polygon getPolygon()\n    {\n        return this.polygon;\n    }\n\n    public TimeZone getTimeZone()\n    {\n        return this.timeZone;\n    }\n\n    @Override\n    public String toString()\n    {\n        return this.timeZone.getID() + \" \" + this.bounds();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/timezone/TimeZoneMap.java",
    "content": "package org.openstreetmap.atlas.utilities.timezone;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.time.ZoneOffset;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.TimeZone;\nimport java.util.regex.Pattern;\n\nimport org.geotools.data.FileDataStore;\nimport org.geotools.data.FileDataStoreFinder;\nimport org.geotools.feature.FeatureIterator;\nimport org.opengis.feature.Feature;\nimport org.opengis.feature.Property;\nimport org.opengis.feature.simple.SimpleFeature;\nimport org.opengis.geometry.BoundingBox;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.index.JtsSpatialIndex;\nimport org.openstreetmap.atlas.geography.index.RTree;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CounterWithStatistic;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This {@link TimeZoneMap} can load partial time zone shape files provided by\n * <a href= \"http://efele.net/maps/tz/world/\" >efele.net</a> into spatial index, and then supports\n * thread-safe {@link TimeZone} queries with {@link Location}.\n *\n * @author tony\n */\npublic class TimeZoneMap implements Located\n{\n    /**\n     * Convert from {@link Feature} (a record of time zone shape file) to a {@link TimeZoneBoundary}\n     * object. If the {@link Feature} is a multipolygon, just store the outer one, there will be\n     * another {@link Feature} for inner polygon\n     */\n    private static class TimeZoneBoundaryConverter implements Converter<Feature, TimeZoneBoundary>\n    {\n        private static final int PREFIX_LENGTH = \"MULTIPOLYGON (((\".length();\n        private static final int SUFFIX_LENGTH = \")))\".length();\n        private static final Pattern MULTI_POLYGON_SEPARATOR = Pattern.compile(\"\\\\), \\\\(\");\n        private static final Pattern POINT_SEPARATOR = Pattern.compile(\", \");\n        private static final Pattern LATITUDE_LONGITUDE_SEPARATOR = Pattern.compile(\" \");\n\n        @Override\n        public TimeZoneBoundary convert(final Feature feature)\n        {\n            // convert time zone\n            final Collection<Property> timeZoneIdentifiers = feature.getProperties(\"TZID\");\n            if (timeZoneIdentifiers.isEmpty() || timeZoneIdentifiers.size() != 1)\n            {\n                logger.error(\"feature {} has more than two time zone identifiers\", feature);\n            }\n            final TimeZone timeZone = TimeZone\n                    .getTimeZone(timeZoneIdentifiers.iterator().next().getValue().toString());\n\n            // convert geometry\n            final Collection<Property> geometries = feature.getProperties(\"the_geom\");\n            if (geometries.isEmpty() || geometries.size() != 1)\n            {\n                logger.error(\"feature {} has more than two geometries\", feature);\n            }\n            final String geometry = geometries.iterator().next().getValue().toString();\n\n            // there could be multipolygons in one record, only store the outer polygon (first one)\n            final String[] multiPolygons = MULTI_POLYGON_SEPARATOR\n                    .split(geometry.substring(PREFIX_LENGTH, geometry.length() - SUFFIX_LENGTH));\n            final String[] points = POINT_SEPARATOR.split(multiPolygons[0]);\n            final List<Location> locations = new ArrayList<>(points.length);\n            for (int j = 0; j < points.length; j++)\n            {\n                final String[] coordinate = LATITUDE_LONGITUDE_SEPARATOR.split(points[j]);\n                locations.add(new Location(Latitude.degrees(Double.parseDouble(coordinate[1])),\n                        Longitude.degrees(Double.parseDouble(coordinate[0]))));\n            }\n            return new TimeZoneBoundary(timeZone, new Polygon(locations));\n        }\n    }\n\n    /**\n     * Use two times sea territory distance as a buffer for extra long bridges, like the one near\n     * Shanghai\n     */\n    private static final Distance SEA_TERRITORY_ZONE_WITH_BUFFER = Distance.SEA_TERRITORY_ZONE\n            .scaleBy(2);\n    private static final Logger logger = LoggerFactory.getLogger(TimeZoneMap.class);\n    private static final int DEGREES_PER_TIME_ZONE = 15;\n    private static final int LOG_PRINT_FREQUENCY = 1_000;\n\n    private final JtsSpatialIndex<TimeZoneBoundary> index = new RTree<>();\n    private final Rectangle bound;\n\n    public TimeZoneMap()\n    {\n        this(Rectangle.MAXIMUM);\n    }\n\n    public TimeZoneMap(final Rectangle bound)\n    {\n        this.bound = bound;\n        try\n        {\n            loadTimeZoneBoundaries();\n        }\n        catch (final IOException e)\n        {\n            logger.error(\"Errors happened when loading timezone\", e);\n        }\n    }\n\n    /**\n     * @return All available time zone boundaries, note this doesn't include ocean strips\n     */\n    public List<TimeZoneBoundary> allTimeZoneBoundaries()\n    {\n        return this.index.get(Rectangle.MAXIMUM);\n    }\n\n    @Override\n    public Rectangle bounds()\n    {\n        return this.bound;\n    }\n\n    /**\n     * Main API for time zone query\n     *\n     * @param location\n     *            The location to query\n     * @return the specified TimeZone, or the GMT zone if in the middle of the sea\n     */\n    public synchronized TimeZone timeZone(final Location location)\n    {\n        List<TimeZoneBoundary> boundaries = this.index.get(location.bounds(),\n                boundary -> boundary.getPolygon().fullyGeometricallyEncloses(location));\n\n        // if find polygons contain location\n        if (!boundaries.isEmpty())\n        {\n            final TimeZoneBoundary smallestBoundary = smallest(boundaries);\n            if (smallestBoundary != null)\n            {\n                return smallestBoundary.getTimeZone();\n            }\n            else\n            {\n                throw new CoreException(\"Could not find smallest time zone boundary.\");\n            }\n        }\n\n        // expand bounding box by 12 * 2 nautical miles and search again\n        final Rectangle larger = location.bounds().expand(SEA_TERRITORY_ZONE_WITH_BUFFER);\n        boundaries = this.index.get(larger);\n\n        // return any found one\n        if (!boundaries.isEmpty())\n        {\n            return boundaries.get(0).getTimeZone();\n        }\n\n        // the location is in the middle of the sea, use normalized time zone (15 degree per zone)\n        final int offset = (int) location.getLongitude().asDegrees() / DEGREES_PER_TIME_ZONE;\n        return TimeZone.getTimeZone(ZoneOffset.of(offset > 0 ? \"+\" + offset : \"\" + offset));\n    }\n\n    /**\n     * Partial loading time zone boundaries\n     *\n     * @throws IOException\n     */\n    private void loadTimeZoneBoundaries() throws IOException\n    {\n        final CounterWithStatistic counter = new CounterWithStatistic(logger);\n        counter.setLogPrintFrequency(LOG_PRINT_FREQUENCY);\n        final URL url = TimeZoneMap.class.getResource(\"tz_world.shp\");\n        final FileDataStore store = FileDataStoreFinder.getDataStore(url);\n        final TimeZoneBoundaryConverter converter = new TimeZoneBoundaryConverter();\n        final FeatureIterator<SimpleFeature> iterator = store.getFeatureSource().getFeatures()\n                .features();\n        try\n        {\n            while (iterator.hasNext())\n            {\n                final Feature feature = iterator.next();\n                final BoundingBox boundingBox = feature.getBounds();\n                final Rectangle featureBound = Rectangle.forLocations(\n                        new Location(Latitude.degrees(boundingBox.getMinY()),\n                                Longitude.degrees(boundingBox.getMinX())),\n                        new Location(Latitude.degrees(boundingBox.getMaxY()),\n                                Longitude.degrees(boundingBox.getMaxX())));\n                // only load overlapped boundaries\n                if (this.bound.overlaps(featureBound))\n                {\n                    final TimeZoneBoundary boundary = converter.convert(feature);\n                    this.index.add(boundary.bounds(), boundary);\n                    counter.increment();\n                }\n            }\n        }\n        finally\n        {\n            iterator.close();\n            store.dispose();\n        }\n\n        counter.summary();\n        logger.info(\"index size is {}\", this.index.size());\n    }\n\n    /**\n     * @return the boundary with minimum area (here we suppose the boundary with minimum area is the\n     *         smallest boundary )\n     */\n    private TimeZoneBoundary smallest(final List<TimeZoneBoundary> boundaries)\n    {\n        if (boundaries.size() == 1)\n        {\n            return boundaries.get(0);\n        }\n        else\n        {\n            TimeZoneBoundary target = null;\n            Surface minimum = Surface.MAXIMUM;\n            for (final TimeZoneBoundary boundary : boundaries)\n            {\n                final Surface area = boundary.getPolygon().bounds().surface();\n                if (area.isLessThan(minimum))\n                {\n                    minimum = area;\n                    target = boundary;\n                }\n            }\n            return target;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/tuples/Either.java",
    "content": "package org.openstreetmap.atlas.utilities.tuples;\n\nimport java.io.Serializable;\nimport java.util.function.Consumer;\n\n/**\n * Class that allows results to be one of two types. You can use the function like so: Either\n * &lt;String, Int&gt;.left(\"Test\").apply( left -&gt; {System.out.println(\"String:\" + left);}, right\n * -&gt; {System.out.println(\"Integer:\" + right);} );\n *\n * @param <L>\n *            The type for the left value\n * @param <R>\n *            The type for the right value\n * @author cuthbertm\n */\npublic final class Either<L, R> implements Serializable\n{\n    private static final long serialVersionUID = 158343315469036806L;\n\n    private final L left;\n    private final R right;\n\n    public static <L, R> Either<L, R> left(final L value)\n    {\n        return new Either<>(value, null);\n    }\n\n    public static <L, R> Either<L, R> right(final R value)\n    {\n        return new Either<>(null, value);\n    }\n\n    private Either(final L left, final R right)\n    {\n        this.left = left;\n        this.right = right;\n    }\n\n    /**\n     * Apply function that allows you to execute against the left or right values\n     *\n     * @param leftFunction\n     *            The function to execute against the left value\n     * @param rightFunction\n     *            The function to execute against the right value\n     */\n    public void apply(final Consumer<? super L> leftFunction,\n            final Consumer<? super R> rightFunction)\n    {\n        if (this.left != null)\n        {\n            leftFunction.accept(this.left);\n        }\n        else if (this.right != null)\n        {\n            rightFunction.accept(this.right);\n        }\n    }\n\n    public L getLeft()\n    {\n        return this.left;\n    }\n\n    public R getRight()\n    {\n        return this.right;\n    }\n\n    public boolean isLeft()\n    {\n        return this.left != null;\n    }\n\n    public boolean isRight()\n    {\n        return this.right != null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/tuples/Tuple.java",
    "content": "package org.openstreetmap.atlas.utilities.tuples;\n\nimport java.io.Serializable;\n\n/**\n * A Generic Tuple implementation\n *\n * @author mgostintsev\n * @param <A>\n *            First object's type\n * @param <B>\n *            Second object's type\n */\npublic class Tuple<A, B> implements Serializable\n{\n    private static final long serialVersionUID = 7745080808877729817L;\n    private final A first;\n    private final B second;\n\n    @SuppressWarnings(\"unchecked\")\n    public static <A, B> Tuple<A, B> cast(final Tuple<?, ?> tuple, final Class<A> aClass,\n            final Class<B> bClass)\n    {\n        if (tuple.isInstanceOf(aClass, bClass))\n        {\n            return (Tuple<A, B>) tuple;\n        }\n        throw new ClassCastException(\"Unable to cast, class mismatch\");\n    }\n\n    public static <A, B> Tuple<A, B> createTuple(final A first, final B second)\n    {\n        return new Tuple<>(first, second);\n    }\n\n    public Tuple(final A first, final B second)\n    {\n        this.first = first;\n        this.second = second;\n    }\n\n    @Override\n    public boolean equals(final Object other)\n    {\n        if (this == other)\n        {\n            return true;\n        }\n        if (other == null || getClass() != other.getClass())\n        {\n            return false;\n        }\n\n        final Tuple<?, ?> tuple = (Tuple<?, ?>) other;\n        if (this.first == null)\n        {\n            if (tuple.first != null)\n            {\n                return false;\n            }\n        }\n        else if (!this.first.equals(tuple.first))\n        {\n            return false;\n        }\n        if (this.second == null)\n        {\n            if (tuple.second != null)\n            {\n                return false;\n            }\n        }\n        else if (!this.second.equals(tuple.second))\n        {\n            return false;\n        }\n        return true;\n    }\n\n    public A getFirst()\n    {\n        return this.first;\n    }\n\n    public B getSecond()\n    {\n        return this.second;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (this.first == null ? 0 : this.first.hashCode());\n        result = prime * result + (this.second == null ? 0 : this.second.hashCode());\n        return result;\n    }\n\n    public boolean isInstanceOf(final Class<?> classA, final Class<?> classB)\n    {\n        return classA.isInstance(this.first) && classB.isInstance(this.second);\n    }\n\n    @Override\n    public String toString()\n    {\n        return String.format(\"(%s,%s)\", this.first, this.second);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/unicode/AbstractClassifier.java",
    "content": "package org.openstreetmap.atlas.utilities.unicode;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.IntPredicate;\nimport java.util.stream.Collectors;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.unicode.Classification.CodeBlock;\n\nimport com.google.common.collect.AbstractIterator;\nimport com.google.common.collect.Range;\nimport com.google.common.collect.RangeMap;\nimport com.google.common.collect.TreeRangeMap;\n\n/**\n * Convenience superclass for Classifier implementations to use that handles everything but the\n * population of the classification lookup and ignore tables\n *\n * @author cstaylor\n */\npublic abstract class AbstractClassifier implements Classifier\n{\n    /**\n     * Simple implementation of the Classification interface where we are passed the data structures\n     * we need (bitset and char collection) and work with those to satisfy our contract methods.\n     *\n     * @author cstaylor\n     */\n    private static final class DefaultClassification implements Classification\n    {\n        private final BitSet classifications;\n        private final Collection<Character> unknownCharacters;\n\n        DefaultClassification(final BitSet classifications,\n                final Collection<Character> unknownCharacters)\n        {\n            this.classifications = classifications;\n            this.unknownCharacters = Collections.unmodifiableCollection(unknownCharacters);\n        }\n\n        @Override\n        public int getClassificationCount()\n        {\n            return this.classifications.cardinality();\n        }\n\n        @Override\n        public Iterable<Character> getUnclassifiedCharacters()\n        {\n            return this.unknownCharacters;\n        }\n\n        @Override\n        public boolean has(final CodeBlock classification)\n        {\n            return this.classifications.get(classification.ordinal());\n        }\n\n        @Override\n        public Iterator<CodeBlock> iterator()\n        {\n            return new AbstractIterator<CodeBlock>()\n            {\n                private int currentIndex = DefaultClassification.this.classifications.nextSetBit(0);\n\n                @Override\n                protected CodeBlock computeNext()\n                {\n                    if (this.currentIndex < 0 || this.currentIndex == Integer.MAX_VALUE)\n                    {\n                        return endOfData();\n                    }\n                    final CodeBlock returnValue = CodeBlock.values()[this.currentIndex];\n                    this.currentIndex = DefaultClassification.this.classifications\n                            .nextSetBit(this.currentIndex + 1);\n                    return returnValue;\n                }\n            };\n        }\n    }\n\n    private final RangeMap<Integer, CodeBlock> classificationTable;\n\n    private final BitSet ignoreTable;\n\n    protected AbstractClassifier()\n    {\n        this.classificationTable = TreeRangeMap.create();\n        this.ignoreTable = new BitSet();\n    }\n\n    @Override\n    public Classification classify(final CharSequence sequence)\n    {\n        if (sequence == null)\n        {\n            throw new CoreException(\"value can't be null\");\n        }\n        final BitSet classifications = new BitSet();\n        final List<Character> unknowns = new ArrayList<>();\n        sequence.chars().filter(createIgnorePredicate()).forEach(character ->\n        {\n            final CodeBlock classification = this.classificationTable.get(character);\n            if (classification == null)\n            {\n                unknowns.add((char) character);\n            }\n            else\n            {\n                classifications.set(classification.ordinal());\n            }\n        });\n        return new DefaultClassification(classifications, unknowns);\n    }\n\n    @Override\n    public List<Optional<CodeBlock>> transform(final CharSequence sequence)\n    {\n        return sequence.chars()\n                .mapToObj(character -> Optional.ofNullable(this.classificationTable.get(character)))\n                .collect(Collectors.toList());\n    }\n\n    protected final void add(final String description, final int start, final int end,\n            final CodeBlock classification)\n    {\n        add(description, Range.closed(start, end), classification);\n    }\n\n    protected final void add(final String description, final Range<Integer> range,\n            final CodeBlock classification)\n    {\n        this.classificationTable.put(range, classification);\n    }\n\n    protected IntPredicate createIgnorePredicate()\n    {\n        return item -> !this.ignoreTable.get(item);\n    }\n\n    protected final void ignore(final String description, final int index)\n    {\n        this.ignoreTable.set(index);\n    }\n\n    protected final void ignore(final String description, final int start, final int end)\n    {\n        // Why +1? BitSet treats the last index as exclusive, but we want to keep\n        // the values passed as the exact values as shown in the Unicode Character tables\n        this.ignoreTable.set(start, end + 1);\n    }\n\n    protected abstract AbstractClassifier initialize();\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/unicode/Classification.java",
    "content": "package org.openstreetmap.atlas.utilities.unicode;\n\nimport org.openstreetmap.atlas.utilities.unicode.Classification.CodeBlock;\n\n/**\n * How many different Unicode CodeBlocks are found in a CharSequence?\n *\n * @author cstaylor\n */\npublic interface Classification extends Iterable<CodeBlock>\n{\n    /**\n     * Code blocks as enum constants, pulled from Character.UnicodeBlock and some simplified names\n     * we use for core.\n     *\n     * @author cstaylor\n     */\n    enum CodeBlock\n    {\n        /**\n         * These codes are from Character.UnicodeBlock, and they follow the Unicode Standard\n         */\n        AEGEAN_NUMBERS,\n        ALCHEMICAL_SYMBOLS,\n        ALPHABETIC_PRESENTATION_FORMS,\n        ANCIENT_GREEK_MUSICAL_NOTATION,\n        ANCIENT_GREEK_NUMBERS,\n        ANCIENT_SYMBOLS,\n        ARABIC,\n        ARABIC_EXTENDED_A,\n        ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS,\n        ARABIC_PRESENTATION_FORMS_A,\n        ARABIC_PRESENTATION_FORMS_B,\n        ARABIC_SUPPLEMENT,\n        ARMENIAN,\n        ARROWS,\n        AVESTAN,\n        BALINESE,\n        BAMUM,\n        BAMUM_SUPPLEMENT,\n        BASIC_LATIN,\n        BATAK,\n        BENGALI,\n        BLOCK_ELEMENTS,\n        BOPOMOFO,\n        BOPOMOFO_EXTENDED,\n        BOX_DRAWING,\n        BRAHMI,\n        BRAILLE_PATTERNS,\n        BUGINESE,\n        BUHID,\n        BYZANTINE_MUSICAL_SYMBOLS,\n        CARIAN,\n        CHAKMA,\n        CHAM,\n        CHEROKEE,\n        CJK_COMPATIBILITY,\n        CJK_COMPATIBILITY_FORMS,\n        CJK_COMPATIBILITY_IDEOGRAPHS,\n        CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT,\n        CJK_RADICALS_SUPPLEMENT,\n        CJK_STROKES,\n        CJK_SYMBOLS_AND_PUNCTUATION,\n        CJK_UNIFIED_IDEOGRAPHS,\n        CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A,\n        CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B,\n        CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C,\n        CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D,\n        COMBINING_DIACRITICAL_MARKS,\n        COMBINING_DIACRITICAL_MARKS_SUPPLEMENT,\n        COMBINING_HALF_MARKS,\n        COMBINING_MARKS_FOR_SYMBOLS,\n        COMMON_INDIC_NUMBER_FORMS,\n        CONTROL_PICTURES,\n        COPTIC,\n        COUNTING_ROD_NUMERALS,\n        CUNEIFORM,\n        CUNEIFORM_NUMBERS_AND_PUNCTUATION,\n        CURRENCY_SYMBOLS,\n        CYPRIOT_SYLLABARY,\n        CYRILLIC,\n        CYRILLIC_EXTENDED_A,\n        CYRILLIC_EXTENDED_B,\n        CYRILLIC_SUPPLEMENTARY,\n        DESERET,\n        DEVANAGARI,\n        DEVANAGARI_EXTENDED,\n        DINGBATS,\n        DOMINO_TILES,\n        EGYPTIAN_HIEROGLYPHS,\n        EMOTICONS,\n        ENCLOSED_ALPHANUMERICS,\n        ENCLOSED_ALPHANUMERIC_SUPPLEMENT,\n        ENCLOSED_CJK_LETTERS_AND_MONTHS,\n        ENCLOSED_IDEOGRAPHIC_SUPPLEMENT,\n        ETHIOPIC,\n        ETHIOPIC_EXTENDED,\n        ETHIOPIC_EXTENDED_A,\n        ETHIOPIC_SUPPLEMENT,\n        GENERAL_PUNCTUATION,\n        GEOMETRIC_SHAPES,\n        GEORGIAN,\n        GEORGIAN_SUPPLEMENT,\n        GLAGOLITIC,\n        GOTHIC,\n        GREEK,\n        GREEK_EXTENDED,\n        GUJARATI,\n        GURMUKHI,\n        HALFWIDTH_AND_FULLWIDTH_FORMS,\n        HANGUL_COMPATIBILITY_JAMO,\n        HANGUL_JAMO,\n        HANGUL_JAMO_EXTENDED_A,\n        HANGUL_JAMO_EXTENDED_B,\n        HANGUL_SYLLABLES,\n        HANUNOO,\n        HEBREW,\n        HIGH_PRIVATE_USE_SURROGATES,\n        HIGH_SURROGATES,\n        HIRAGANA,\n        IDEOGRAPHIC_DESCRIPTION_CHARACTERS,\n        IMPERIAL_ARAMAIC,\n        INSCRIPTIONAL_PAHLAVI,\n        INSCRIPTIONAL_PARTHIAN,\n        IPA_EXTENSIONS,\n        JAVANESE,\n        KAITHI,\n        KANA_SUPPLEMENT,\n        KANBUN,\n        KANGXI_RADICALS,\n        KANNADA,\n        KATAKANA,\n        KATAKANA_PHONETIC_EXTENSIONS,\n        KAYAH_LI,\n        KHAROSHTHI,\n        KHMER,\n        KHMER_SYMBOLS,\n        LAO,\n        LATIN_1_SUPPLEMENT,\n        LATIN_EXTENDED_A,\n        LATIN_EXTENDED_ADDITIONAL,\n        LATIN_EXTENDED_B,\n        LATIN_EXTENDED_C,\n        LATIN_EXTENDED_D,\n        LEPCHA,\n        LETTERLIKE_SYMBOLS,\n        LIMBU,\n        LINEAR_B_IDEOGRAMS,\n        LINEAR_B_SYLLABARY,\n        LISU,\n        LOW_SURROGATES,\n        LYCIAN,\n        LYDIAN,\n        MAHJONG_TILES,\n        MALAYALAM,\n        MANDAIC,\n        MATHEMATICAL_ALPHANUMERIC_SYMBOLS,\n        MATHEMATICAL_OPERATORS,\n        MEETEI_MAYEK,\n        MEETEI_MAYEK_EXTENSIONS,\n        MEROITIC_CURSIVE,\n        MEROITIC_HIEROGLYPHS,\n        MIAO,\n        MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A,\n        MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B,\n        MISCELLANEOUS_SYMBOLS,\n        MISCELLANEOUS_SYMBOLS_AND_ARROWS,\n        MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS,\n        MISCELLANEOUS_TECHNICAL,\n        MODIFIER_TONE_LETTERS,\n        MONGOLIAN,\n        MUSICAL_SYMBOLS,\n        MYANMAR,\n        MYANMAR_EXTENDED_A,\n        NEW_TAI_LUE,\n        NKO,\n        NUMBER_FORMS,\n        OGHAM,\n        OLD_ITALIC,\n        OLD_PERSIAN,\n        OLD_SOUTH_ARABIAN,\n        OLD_TURKIC,\n        OL_CHIKI,\n        OPTICAL_CHARACTER_RECOGNITION,\n        ORIYA,\n        OSMANYA,\n        PHAGS_PA,\n        PHAISTOS_DISC,\n        PHOENICIAN,\n        PHONETIC_EXTENSIONS,\n        PHONETIC_EXTENSIONS_SUPPLEMENT,\n        PLAYING_CARDS,\n        PRIVATE_USE_AREA,\n        REJANG,\n        RUMI_NUMERAL_SYMBOLS,\n        RUNIC,\n        SAMARITAN,\n        SAURASHTRA,\n        SHARADA,\n        SHAVIAN,\n        SINHALA,\n        SMALL_FORM_VARIANTS,\n        SORA_SOMPENG,\n        SPACING_MODIFIER_LETTERS,\n        SPECIALS,\n        SUNDANESE,\n        SUNDANESE_SUPPLEMENT,\n        SUPERSCRIPTS_AND_SUBSCRIPTS,\n        SUPPLEMENTAL_ARROWS_A,\n        SUPPLEMENTAL_ARROWS_B,\n        SUPPLEMENTAL_MATHEMATICAL_OPERATORS,\n        SUPPLEMENTAL_PUNCTUATION,\n        SUPPLEMENTARY_PRIVATE_USE_AREA_A,\n        SUPPLEMENTARY_PRIVATE_USE_AREA_B,\n        SURROGATES_AREA,\n        SYLOTI_NAGRI,\n        SYRIAC,\n        TAGALOG,\n        TAGBANWA,\n        TAGS,\n        TAI_LE,\n        TAI_THAM,\n        TAI_VIET,\n        TAI_XUAN_JING_SYMBOLS,\n        TAKRI,\n        TAMIL,\n        TELUGU,\n        THAANA,\n        THAI,\n        TIBETAN,\n        TIFINAGH,\n        TRANSPORT_AND_MAP_SYMBOLS,\n        UGARITIC,\n        UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS,\n        UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED,\n        VAI,\n        VARIATION_SELECTORS,\n        VARIATION_SELECTORS_SUPPLEMENT,\n        VEDIC_EXTENSIONS,\n        VERTICAL_FORMS,\n        YIJING_HEXAGRAM_SYMBOLS,\n        YI_RADICALS,\n        YI_SYLLABLES,\n\n        /**\n         * And these are the settings we use currently within core and are not defined by the\n         * Unicode standard\n         */\n        LATIN,\n        CJK,\n        ARROW_SYMBOLS,\n        HANGUL,\n        OPERATING_SYSTEM_CONTROL_CHARACTERS,\n        ARABIC_PRESENTATION_FORMS\n    }\n\n    /**\n     * Get the number of CodeBlocks that this Classification covers\n     *\n     * @return the number of classifications\n     */\n    int getClassificationCount();\n\n    /**\n     * Return the set of characters that didn't match a classification\n     *\n     * @return the set of characters that were unclassified\n     */\n    Iterable<Character> getUnclassifiedCharacters();\n\n    /**\n     * Did this classification have this CodeBlock?\n     *\n     * @param classification\n     *            the codeblock in question\n     * @return true if we have it, false otherwise\n     */\n    boolean has(CodeBlock classification);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/unicode/Classifier.java",
    "content": "package org.openstreetmap.atlas.utilities.unicode;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.openstreetmap.atlas.utilities.unicode.Classification.CodeBlock;\n\n/**\n * Responsible for reducing a CharSequence into a Classification object and transforming a\n * CharSequence into an equivalent number of Optional CodeBlock objects. If an Optional is not\n * present, that character could not be classified\n *\n * @author cstaylor\n */\npublic interface Classifier\n{\n    Classification classify(CharSequence sequence);\n\n    List<Optional<CodeBlock>> transform(CharSequence sequence);\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/unicode/LoadingClassifier.java",
    "content": "package org.openstreetmap.atlas.utilities.unicode;\n\nimport java.nio.file.Path;\n\nimport org.openstreetmap.atlas.streaming.resource.ClassResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.LineFilteredResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.unicode.Classification.CodeBlock;\n\n/**\n * This will load both classifications and ignores from a CSV file in the format:\n * <p>\n * START HEX CODE, END HEX CODE, CLASSIFICATION, DESCRIPTION\n * <p>\n * Note: IGNORE is treated as a special CLASSIFICATION value that will add the value to the ignore\n * list\n *\n * @author cstaylor\n */\npublic class LoadingClassifier extends AbstractClassifier\n{\n    private static final int START_INDEX = 0;\n\n    private static final int END_INDEX = 1;\n\n    private static final int CLASSIFICATION_INDEX = 2;\n\n    private static final int DESCRIPTION_INDEX = 3;\n\n    private final Resource resource;\n\n    public LoadingClassifier()\n    {\n        this(\"org/openstreetmap/atlas/utilities/unicode/unicode.defaults\");\n    }\n\n    public LoadingClassifier(final Path path)\n    {\n        this(new File(path.toFile()));\n    }\n\n    public LoadingClassifier(final Resource resource)\n    {\n        this.resource = resource;\n        initialize();\n    }\n\n    public LoadingClassifier(final String classResource)\n    {\n        this(new ClassResource(classResource));\n    }\n\n    @Override\n    protected LoadingClassifier initialize()\n    {\n        if (this.resource != null)\n        {\n            /*\n             * These are the codeblock mappings\n             */\n            Iterables\n                    .stream(new LineFilteredResource(this.resource,\n                            line -> !line.contains(\"IGNORE\")).lines())\n                    .map(line -> StringList.split(line, \",\")).forEach(list ->\n                    {\n                        add(list.get(DESCRIPTION_INDEX), Integer.decode(list.get(START_INDEX)),\n                                Integer.decode(list.get(END_INDEX)),\n                                CodeBlock.valueOf(list.get(CLASSIFICATION_INDEX).toUpperCase()));\n                    });\n\n            /*\n             * These are the ignores\n             */\n            Iterables\n                    .stream(new LineFilteredResource(this.resource, line -> line.contains(\"IGNORE\"))\n                            .lines())\n                    .map(line -> StringList.split(line, \",\")).forEach(list ->\n                    {\n                        ignore(list.get(DESCRIPTION_INDEX), Integer.decode(list.get(START_INDEX)),\n                                Integer.decode(list.get(END_INDEX)));\n                    });\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/vectortiles/MinimumZoom.java",
    "content": "package org.openstreetmap.atlas.utilities.vectortiles;\n\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.exception.CoreException;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\n/**\n * This singleton class is where you get the minimum zoom for features' tags based on a JSON\n * configuration. The values you see in the minimum-zoom.json resource file is inspired for what you\n * see in the standard OpenStreetMap carto style. This is very loosely based off of the minimum\n * zooms you see for various types of features. See the README.md in this package for more\n * details...\n *\n * @author hallahan\n */\npublic enum MinimumZoom\n{\n    INSTANCE;\n\n    private static final String CONFIG_RESOURCE = \"minimum-zooms.json\";\n    private static final int DEFAULT_ZOOM = 14;\n\n    private String[] keys;\n    private int[] defaults;\n    private final List<Map<String, Integer>> valuesList = new ArrayList<>();\n\n    MinimumZoom()\n    {\n        processConfig(parseConfig());\n    }\n\n    public int get(final Map<String, String> tags)\n    {\n        for (int index = 0; index < this.keys.length; ++index)\n        {\n            final String key = this.keys[index];\n            final String value = tags.get(key);\n            if (value == null)\n            {\n                continue;\n            }\n            final Map<String, Integer> valuesMap = this.valuesList.get(index);\n            final Integer valueMinimumZoom = valuesMap.get(value);\n            if (valueMinimumZoom != null)\n            {\n                return valueMinimumZoom;\n            }\n            else\n            {\n                return this.defaults[index];\n            }\n        }\n        return DEFAULT_ZOOM;\n    }\n\n    private JsonArray parseConfig()\n    {\n        try\n        {\n            final InputStream inputStream = MinimumZoom.class.getResourceAsStream(CONFIG_RESOURCE);\n            final InputStreamReader reader = new InputStreamReader(inputStream);\n            final JsonParser parser = new JsonParser();\n            final JsonElement element = parser.parse(reader);\n            return element.getAsJsonArray();\n        }\n        catch (final Exception exception)\n        {\n            throw new CoreException(\n                    \"There was a problem parsing minimum-zooms.json. Check if the JSON file has valid structure.\",\n                    exception);\n        }\n    }\n\n    private void processConfig(final JsonArray config)\n    {\n        final int length = config.size();\n        this.keys = new String[length];\n        this.defaults = new int[length];\n        for (int index = 0; index < length; ++index)\n        {\n            try\n            {\n                final JsonObject object = config.get(index).getAsJsonObject();\n                this.keys[index] = object.get(\"key\").getAsString();\n                this.defaults[index] = object.get(\"default\").getAsInt();\n\n                final Map<String, Integer> valuesMap = new HashMap<>();\n                this.valuesList.add(index, valuesMap);\n\n                // values is optional\n                final JsonElement valuesElement = object.get(\"values\");\n                if (valuesElement != null)\n                {\n                    valuesElement.getAsJsonObject().entrySet().forEach(\n                            entry -> valuesMap.put(entry.getKey(), entry.getValue().getAsInt()));\n                }\n            }\n            catch (final Exception exception)\n            {\n                throw new CoreException(\n                        \"There is a problem with one of the rule objects in the JSON configuration.\",\n                        exception);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/vectortiles/README.md",
    "content": "# Creating Vector Tiles\n\n`TippecanoeExporter.java` is a CLI that converts a directory of atlas files into\nline-delimited GeoJSON, and then that gets converted into an MBTiles file full\nof MapboxVectorTiles by tippecanoe.\n\nUsage:\n\n``` \njava -Xmx12G -cp ./atlas.jar org.openstreetmap.atlas.utilities.vectortiles.TippecanoeExporter \\\n-atlasDirectory=<directory of atlas files> \\\n-geojsonDirectory=<directory to write GeoJSON> \\\n-mbtiles=<where to write MBTiles file> \\\n-threads=8 \\\n-overwrite=true\n```\n\nNote: The `-mbtiles` argument should be the path to the specific file you'd like to write to--not a directory.\n\nOn a beefy server, you might do something like this:\n\n``` \njava -Xmx240G -cp ./atlas-njh.jar org.openstreetmap.atlas.utilities.vectortiles.TippecanoeExporter \\\n-atlasDirectory=/opt/data/tippecanoe/atlas \\\n-geojsonDirectory=/opt/data/tippecanoe/geojson \\\n-mbtiles=/opt/data/tippecanoe/WORLD.mbtiles \\\n-threads=16 \\\n-overwrite=true\n```\n\nOnce you've created your MBTiles file, you can serve and view it with mbview.\n\n``` \nmbview <mbtiles file>\n```\n\nThen a browser should pop up at http://localhost:3000\n\n\n# Dependencies\n\n## tippecanoe\n\nInstall tippecanoe:\n\n``` \nbrew install tippecanoe\n```\n\nor compile it...\n\nhttps://github.com/mapbox/tippecanoe\n\n## mbview\n\nMake sure you have installed [NodeJS](https://nodejs.org/).\n\n``` \nnpm install -g mbview\n```\n\nhttps://github.com/mapbox/mbview\n\n\n# Troubleshooting\n\nSometimes mbview's dependency, `node-sqlite` doesn't play well wit the latest version of NodeJS. In that case, you need to pin your NodeJS to a specific version.\n\nInstall nvm (NodeJS Version Manager):\n\nhttps://github.com/creationix/nvm\n\nThen run:\n\n``` \nnvm install 8.11.1\n```\n\nThen, reinstall mbview. You should be up and running!\n\n\n# Feature Minimum Zoom Level Configuration\n\nThe resource [minimum-zooms.json](https://github.com/hallahan/atlas/blob/tippecanoe/src/main/resources/org/openstreetmap/atlas/utilities/vectortiles/minimum-zooms.json) \nallows you to configure the minimum zoom for a given feature based on its tags.\n\nThe values you see in the minimum-zoom.json resourse file is inspired for what you\nsee in the standard OpenStreetMap carto style. This is very loosely based off of the minimum\nzooms you see for various types of features. Note that there is definitely more work that needs\nto be done to refine our min zooms.\n\nhttps://github.com/gravitystorm/openstreetmap-carto\n\nThe config is a JSON array of rule objects. Each rule looks like this:\n\n```json\n{\n  \"key\": \"landuse\",\n  \"default\": 12,\n  \"values\": {\n    \"basin\": 7,\n    \"forest\": 8\n  }\n}\n```\n\nThe rule must have a key for the tag key. It must have an integer for the default minimum zoom.\nvalues is optional, and this is an object with a given OSM tag value and a minimum zoom that will\napply to it. The way that the JSON config evaluates is that the first rules in the array take\npriority. If a given key matches, we use that rule, and all other rules will not be evaluated for\nfinding the minimum zoom for that given atlas element's tags.\n\nSo, the order of your rule in minimum-zooms.json that applies first takes precedence.\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/vectortiles/TippecanoeCommands.java",
    "content": "package org.openstreetmap.atlas.utilities.vectortiles;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.maven.artifact.versioning.DefaultArtifactVersion;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.geojson.LineDelimitedGeoJsonConverter;\nimport org.openstreetmap.atlas.utilities.runtime.RunScript;\nimport org.openstreetmap.atlas.utilities.runtime.SingleLineMonitor;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This utility class allows you to execute the tippecanoe command line tool ultimately via Java's\n * runtime exec.\n *\n * @author hallahan\n */\npublic final class TippecanoeCommands\n{\n    private static final Logger logger = LoggerFactory.getLogger(TippecanoeCommands.class);\n    private static final SingleLineMonitor MONITOR = new SingleLineMonitor()\n    {\n        @Override\n        protected Optional<String> parseResult(final String line)\n        {\n            return Optional.of(line);\n        }\n    };\n\n    /**\n     * Concatenates all of the GeoJSON files in a directory into a single GeoJSON file. This find\n     * command is fairly sophisticated. What you see here is that it is finding everything in a\n     * certain directory with the extension of *.geojson but is not EVERYTHING.geojosn. It then\n     * pipes those files to cat which in turn outputs to EVERYTHING.geojson.\n     *\n     * @param geojsonDirectory\n     *            The directory of GeoJSON files to concatenate.\n     */\n    public static void concatenate(final Path geojsonDirectory)\n    {\n        final Time time = Time.now();\n        logger.info(\"Concatenating GeoJSON...\");\n        final String directory = geojsonDirectory.toString();\n        final String cat = String.format(\n                \"find '%s' -type f -name '*.geojson' \\\\! -path '%s'/%s -exec cat \\\\{\\\\} + > '%s/'%s\",\n                directory, directory, LineDelimitedGeoJsonConverter.EVERYTHING, directory,\n                LineDelimitedGeoJsonConverter.EVERYTHING);\n        logger.info(cat);\n        final String[] bashCommandArray = new String[] { \"bash\", \"-c\", cat };\n        RunScript.run(bashCommandArray);\n        logger.info(\"Concatenated to {} in {}\", LineDelimitedGeoJsonConverter.EVERYTHING,\n                time.elapsedSince());\n    }\n\n    /**\n     * Decompresses a directory of *.geojson.gz files recursively into *.geojson files.\n     *\n     * @param geojsonDirectory\n     *            The directory to decompress.\n     */\n    public static void decompress(final Path geojsonDirectory)\n    {\n        final Time time = Time.now();\n        final String directory = geojsonDirectory.toString();\n        final String cat = String.format(\n                \"find '%s' -type f -name '*.geojson.gz' -exec gzip -dr \\\\{\\\\} +\", directory);\n        final String[] bashCommandArray = new String[] { \"bash\", \"-c\", cat };\n        try\n        {\n            RunScript.run(bashCommandArray);\n            logger.info(\"Decompressed line-delimited GeoJSON in {}\", time.elapsedSince());\n        }\n        catch (final CoreException exception)\n        {\n            logger.warn(\"Not finding any .geojson.gz to decompress. Continuing...\", exception);\n        }\n    }\n\n    /**\n     * Allows you to check if you have installed tippecanoe on your system and if it satisfies the\n     * minimum version that we need.\n     *\n     * @return Whether you have a valid tippecanoe ready to execute in your environment.\n     */\n    public static boolean hasValidTippecanoe()\n    {\n        final String[] commandArray = new String[] { \"tippecanoe\", \"--version\" };\n        try\n        {\n            RunScript.run(commandArray, Collections.singletonList(MONITOR));\n        }\n        // When you look up the version, tippecanoe exits with 1, so getting here is normal.\n        // We want to get into this catch.\n        // https://github.com/osmlab/atlas/pull/275#discussion_r235430727\n        catch (final CoreException exception)\n        {\n            logger.warn(\n                    \"Note that tippecanoe exited with 1 when checking the version; this is known and does not cause a problem for us.\",\n                    exception);\n            return checkVersion();\n        }\n        // Exception or not, we can check the version and proceed either way. If tippecanoe stops\n        // exiting with 1, then we will handle the same logic outside of the exception.\n        return checkVersion();\n    }\n\n    /**\n     * Runs the tippecanoe CLI with the default arguments found in TippecanoeSettings.java\n     *\n     * @param geojson\n     *            The path to a single line-delimited GeoJSON file.\n     * @param mbtiles\n     *            The path to write the MBTiles file.\n     * @param overwrite\n     *            Whether we should be able to overwrite an existing MBTiles file.\n     */\n    public static void runTippecanoe(final Path geojson, final Path mbtiles,\n            final boolean overwrite)\n    {\n        runTippecanoe(geojson, mbtiles, overwrite, TippecanoeSettings.ARGS);\n    }\n\n    /**\n     * Runs the tippecanoe CLI with the provided arguments.\n     *\n     * @param geojson\n     *            The path to a single line-delimited GeoJSON file.\n     * @param mbtiles\n     *            The path to write the MBTiles file.\n     * @param overwrite\n     *            Whether we should be able to overwrite an existing MBTiles file.\n     * @param args\n     *            tippecanoe CLI arguments as documented in\n     *            https://github.com/mapbox/tippecanoe/blob/master/README.md\n     */\n    public static void runTippecanoe(final Path geojson, final Path mbtiles,\n            final boolean overwrite, final String[] args)\n    {\n        final Time time = Time.now();\n\n        final List<String> commandList = new ArrayList<>();\n\n        commandList.add(\"tippecanoe\");\n        commandList.add(\"-o\");\n        commandList.add(mbtiles.toString());\n\n        commandList.addAll(Arrays.asList(args));\n\n        if (overwrite)\n        {\n            commandList.add(\"--force\");\n        }\n\n        commandList.add(geojson.toString());\n\n        final String[] commandArray = commandList.toArray(new String[0]);\n\n        logger.info(\"Running tippecanoe...\");\n\n        logger.info(StringUtils.join(commandArray, \" \"));\n\n        RunScript.run(commandArray);\n\n        logger.info(\"tippecanoe has successfully generated vector tiles in {}\", mbtiles);\n        logger.info(\"tippecanoe took {}\", time.elapsedSince());\n    }\n\n    private static boolean checkVersion()\n    {\n        final Optional<String> result = MONITOR.getResult();\n        if (result.isPresent())\n        {\n            final String outputString = result.get();\n            final String[] versionArray = outputString.split(\"\\n\")[0].split(\"tippecanoe v\");\n            // Here we extract the version.\n            if (versionArray.length == 2)\n            {\n                final String versionString = versionArray[1];\n                final DefaultArtifactVersion version = new DefaultArtifactVersion(versionString);\n                if (TippecanoeSettings.MIN_VERSION.compareTo(version) <= 0)\n                {\n                    return true;\n                }\n                else\n                {\n                    logger.error(\"Your version of tippecanoe is too old! The minimum version is {}\",\n                            TippecanoeSettings.MIN_VERSION);\n                }\n            }\n        }\n        return false;\n    }\n\n    private TippecanoeCommands()\n    {\n        // Utility class\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/vectortiles/TippecanoeConverter.java",
    "content": "package org.openstreetmap.atlas.utilities.vectortiles;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.openstreetmap.atlas.geography.atlas.geojson.LineDelimitedGeoJsonConverter;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Typically you will use this for atlas-checks or any other tool that already has line-delimited\n * GeoJSON that you would like to turn into vector tiles. This is a simple CLI that will take\n * tippecanoe line-delimited GeoJSON output and convert it into vector tiles with tippecanoe. If you\n * would like the full end-to-end conversion of atlas files into an MBTiles file, use\n * TippecanoeExporter. If you would like to just convert atlas files into line-delimited GeoJSON,\n * use LineDelimitedGeoJsonConverter.\n *\n * @author hallahan\n */\npublic class TippecanoeConverter extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(TippecanoeConverter.class);\n\n    private static final Command.Switch<Path> GEOJSON_DIRECTORY = new Command.Switch<>(\n            \"geojsonDirectory\", \"The directory to read line-delimited GeoJSON.\", Paths::get,\n            Command.Optionality.REQUIRED);\n\n    private static final Switch<Path> MBTILES = new Switch<>(\"mbtiles\",\n            \"The MBTiles file to which tippecanoe will write vector tiles.\", Paths::get,\n            Optionality.REQUIRED);\n\n    private static final Command.Switch<Boolean> OVERWRITE = new Command.Switch<>(\"overwrite\",\n            \"Choose to automatically overwrite an MBTiles file if it exists at the given path.\",\n            Boolean::new, Command.Optionality.OPTIONAL, \"false\");\n\n    public static void main(final String[] args)\n    {\n        new TippecanoeConverter().run(args);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        if (!TippecanoeCommands.hasValidTippecanoe())\n        {\n            logger.error(\n                    \"Your system does not have a valid installation of tippecanoe installed in its path.\");\n            logger.error(\"https://github.com/mapbox/tippecanoe\");\n\n            System.exit(LineDelimitedGeoJsonConverter.EXIT_FAILURE);\n        }\n\n        final Path mbtiles = (Path) command.get(MBTILES);\n        final Path geojsonDirectory = (Path) command.get(GEOJSON_DIRECTORY);\n        final Path geojson = geojsonDirectory.resolve(LineDelimitedGeoJsonConverter.EVERYTHING);\n        final Boolean overwrite = (Boolean) command.get(OVERWRITE);\n\n        TippecanoeCommands.decompress(geojsonDirectory);\n        TippecanoeCommands.concatenate(geojsonDirectory);\n\n        TippecanoeCommands.runTippecanoe(geojson, mbtiles, overwrite, TippecanoeSettings.ARGS);\n\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(GEOJSON_DIRECTORY, MBTILES, OVERWRITE);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/vectortiles/TippecanoeExporter.java",
    "content": "package org.openstreetmap.atlas.utilities.vectortiles;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.openstreetmap.atlas.geography.atlas.geojson.LineDelimitedGeoJsonConverter;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This CLI will take a directory full of atlases and export it into an MBTiles file full of Mapbox\n * vector tiles. It creates intermediary line-delimited GeoJSON, and then it ultimately drives\n * tippecanoe to do the vector tile creation. This CLI is to do the full end-to-end conversion of\n * atlas files into an MBTiles file. If you would like to just convert line-delimited GeoJSON into\n * MBTiles, use TippecanoeConverter. If you would like to just convert atlas files into\n * line-delimited GeoJSON, use LineDelimitedGeoJsonConverter.\n *\n * @author hallahan\n */\npublic final class TippecanoeExporter extends LineDelimitedGeoJsonConverter\n{\n    private static final int EXIT_FAILURE = 1;\n\n    private static final Logger logger = LoggerFactory.getLogger(TippecanoeExporter.class);\n\n    private static final Switch<Path> MBTILES = new Switch<>(\"mbtiles\",\n            \"The MBTiles file to which tippecanoe will write vector tiles.\", Paths::get,\n            Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new TippecanoeExporter().run(args);\n    }\n\n    private TippecanoeExporter()\n    {\n        this.setJsonMutator(TippecanoeSettings.JSON_MUTATOR);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        super.onRun(command);\n\n        if (!TippecanoeCommands.hasValidTippecanoe())\n        {\n            logger.error(\n                    \"Your system does not have a valid installation of tippecanoe installed in its path.\");\n            logger.error(\"https://github.com/mapbox/tippecanoe\");\n\n            System.exit(EXIT_FAILURE);\n        }\n\n        final Path mbtiles = (Path) command.get(MBTILES);\n\n        final Path geojsonDirectory = (Path) command.get(GEOJSON_DIRECTORY);\n        final Path geojson = geojsonDirectory.resolve(EVERYTHING);\n\n        final Boolean overwrite = (Boolean) command.get(OVERWRITE);\n\n        TippecanoeCommands.runTippecanoe(geojson, mbtiles, overwrite);\n\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return super.switches().with(MBTILES);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/vectortiles/TippecanoeGeoJsonExtension.java",
    "content": "package org.openstreetmap.atlas.utilities.vectortiles;\n\nimport com.google.gson.JsonObject;\n\n/**\n * TippecanoeGeoJsonExtension fills in a JsonObject called \"tippecanoe\" to your GeoJSON feature.\n * Here you can specify things like minzoom and layer name for the given feature.\n * https://github.com/mapbox/tippecanoe/blob/739445cb6cd9654bdbc95046ee5e4a6b55cd2ff9/README.md#geojson-extension\n *\n * @author hallahan\n */\npublic class TippecanoeGeoJsonExtension\n{\n    private static final int DEFAULT_MINIMUM_ZOOM = 14;\n\n    private final JsonObject json = new JsonObject();\n\n    public TippecanoeGeoJsonExtension()\n    {\n        minimumZoom(DEFAULT_MINIMUM_ZOOM);\n    }\n\n    public TippecanoeGeoJsonExtension addTo(final JsonObject feature)\n    {\n        feature.add(\"tippecanoe\", this.json);\n        return this;\n    }\n\n    public void layer(final String layer)\n    {\n        this.json.addProperty(\"layer\", layer);\n    }\n\n    public void minimumZoom(final int minimumZoom)\n    {\n        this.json.addProperty(\"minzoom\", minimumZoom);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/openstreetmap/atlas/utilities/vectortiles/TippecanoeSettings.java",
    "content": "package org.openstreetmap.atlas.utilities.vectortiles;\n\nimport java.util.function.BiConsumer;\n\nimport org.apache.maven.artifact.versioning.DefaultArtifactVersion;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\nimport com.google.gson.JsonObject;\n\n/**\n * This is where you configure the tippecanoe properties that will be in the generated\n * line-delimited GeoJSON. You also set the arguments to drive tippecanoe here.\n *\n * @author hallahan\n */\npublic final class TippecanoeSettings\n{\n    private TippecanoeSettings()\n    {\n        // Utility Class\n    }\n\n    public static final DefaultArtifactVersion MIN_VERSION = new DefaultArtifactVersion(\"1.32.1\");\n\n    public static final String[] ARGS = new String[] { \"-Z6\", \"-z14\", \"--generate-ids\",\n            \"--read-parallel\", \"--no-tile-size-limit\", \"--no-feature-limit\" };\n\n    public static final BiConsumer<AtlasEntity, JsonObject> JSON_MUTATOR = (atlasEntity, feature) ->\n    {\n        final TippecanoeGeoJsonExtension tippecanoe = new TippecanoeGeoJsonExtension()\n                .addTo(feature);\n\n        final ItemType type = atlasEntity.getType();\n        final String typeName = type.name();\n\n        // It's good to have multipolygon relations in their own layer, because all other relations\n        // are just bounding boxes. Multipolygons are multipolygons. So, we'll probably want to\n        // render the multipolygons, but we can turn off rendering other relations, that really are\n        // represented as a bunch of bounding boxes...\n        if (ItemType.RELATION.equals(type) && Validators.isOfType(atlasEntity,\n                RelationTypeTag.class, RelationTypeTag.MULTIPOLYGON))\n        {\n            tippecanoe.layer(\"MULTIPOLYGON\");\n        }\n        else\n        {\n            tippecanoe.layer(typeName);\n        }\n\n        tippecanoe.minimumZoom(MinimumZoom.INSTANCE.get(atlasEntity.getTags()));\n    };\n\n}\n"
  },
  {
    "path": "src/main/proto/Area.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoAreaWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Location.proto\";\nimport \"Tag.proto\";\n\nmessage ProtoArea {\n    optional int64 id = 1;\n    repeated ProtoLocation shapePoints = 2;\n\n    repeated ProtoTag tags = 3;\n}\n"
  },
  {
    "path": "src/main/proto/Edge.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoEdgeWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Location.proto\";\nimport \"Tag.proto\";\n\nmessage ProtoEdge {\n    optional int64 id = 1;\n    repeated ProtoLocation shapePoints = 2;\n    repeated ProtoTag tags = 5;\n}\n"
  },
  {
    "path": "src/main/proto/Line.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoLineWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Location.proto\";\nimport \"Tag.proto\";\n\nmessage ProtoLine {\n    optional int64 id = 1;\n    repeated ProtoLocation shapePoints = 2;\n\n    repeated ProtoTag tags = 3;\n}\n"
  },
  {
    "path": "src/main/proto/Location.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoLocationWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Tag.proto\";\n\nmessage ProtoLocation {\n    optional int32 latitude = 1;\n    optional int32 longitude = 2;\n}\n"
  },
  {
    "path": "src/main/proto/Node.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoNodeWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Location.proto\";\nimport \"Tag.proto\";\n\nmessage ProtoNode {\n    optional int64 id = 1;\n    optional ProtoLocation location = 2;\n    repeated ProtoTag tags = 5;\n}\n"
  },
  {
    "path": "src/main/proto/Point.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoPointWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Location.proto\";\nimport \"Tag.proto\";\n\nmessage ProtoPoint {\n    optional int64 id = 1;\n    optional ProtoLocation location = 2;\n\n    repeated ProtoTag tags = 3;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoAtlas.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoAtlasWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Point.proto\";\nimport \"Line.proto\";\nimport \"Area.proto\";\nimport \"Node.proto\";\nimport \"Edge.proto\";\nimport \"Relation.proto\";\nimport \"ProtoAtlasMetaData.proto\";\n\n/*\n * NOTE: the 'repeated' directive is cpmpiled into Java as an ArrayList.\n * The ArrayList implementation has a 32bit signed integer size limit.\n * This could be a potential problem for atlases with an absurd number\n * of a specific feature.\n */\nmessage ProtoAtlas {\n    optional int64 numberOfPoints = 1;\n    repeated ProtoPoint points = 2;\n\n    optional int64 numberOfLines = 3;\n    repeated ProtoLine lines = 4;\n\n    optional int64 numberOfAreas = 5;\n    repeated ProtoArea areas = 6;\n\n    optional int64 numberOfNodes = 7;\n    repeated ProtoNode nodes = 8;\n\n    optional int64 numberOfEdges = 9;\n    repeated ProtoEdge edges = 10;\n\n    optional int64 numberOfRelations = 11;\n    repeated ProtoRelation relations = 12;\n\n    optional ProtoAtlasMetaData metaData = 13;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoAtlasMetaData.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoAtlasMetaDataWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Tag.proto\";\n\nmessage ProtoAtlasMetaData {\n    optional int64 edgeNumber = 1;\n    optional int64 nodeNumber = 2;\n    optional int64 areaNumber = 3;\n    optional int64 lineNumber = 4;\n    optional int64 pointNumber = 5;\n    optional int64 relationNumber = 6;\n\n    optional bool original = 7;\n    optional string codeVersion = 8;\n    optional string dataVersion = 9;\n    optional string country = 10;\n    optional string shardName = 11;\n\n    repeated ProtoTag tags = 12;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoByteArray.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoByteArrayWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nmessage ProtoByteArray {\n    optional string name = 1;\n    optional bytes elements = 2;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoByteArrayOfArrays.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoByteArrayOfArraysWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"ProtoByteArray.proto\";\n\nmessage ProtoByteArrayOfArrays {\n    optional string name = 1;\n    repeated ProtoByteArray arrays = 2;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoIntegerArray.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoIntegerArrayWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nmessage ProtoIntegerArray {\n    optional string name = 1;\n    repeated int32 elements = 2;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoIntegerArrayOfArrays.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoIntegerArrayOfArraysWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"ProtoIntegerArray.proto\";\n\nmessage ProtoIntegerArrayOfArrays {\n    optional string name = 1;\n    repeated ProtoIntegerArray arrays = 2;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoIntegerStringDictionary.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoIntegerStringDictionaryWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nmessage ProtoIntegerStringDictionary {\n    optional int32 currentIndex = 1;\n    repeated int32 indexes = 2;\n    repeated string words = 3;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoLongArray.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoLongArrayWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nmessage ProtoLongArray {\n    optional string name = 1;\n    repeated int64 elements = 2;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoLongArrayOfArrays.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoLongArrayOfArraysWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"ProtoLongArray.proto\";\n\nmessage ProtoLongArrayOfArrays {\n    optional string name = 1;\n    repeated ProtoLongArray arrays = 2;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoLongToLongMap.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoLongToLongMapWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"ProtoLongArray.proto\";\n\nmessage ProtoLongToLongMap {\n    optional string name = 1;\n    optional ProtoLongArray keys = 2;\n    optional ProtoLongArray values = 3;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoLongToLongMultiMap.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoLongToLongMultiMapWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"ProtoLongArray.proto\";\n\nmessage ProtoLongToLongMultiMap {\n    optional string name = 1;\n    optional ProtoLongArray keys = 2;\n    repeated ProtoLongArray values = 3;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoPackedTagStore.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoPackedTagStoreWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"ProtoIntegerArrayOfArrays.proto\";\n\nmessage ProtoPackedTagStore {\n    optional int64 index = 1;\n    optional ProtoIntegerArrayOfArrays keys = 2;\n    optional ProtoIntegerArrayOfArrays values = 3;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoPolyLineArray.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoPolyLineArrayWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nmessage ProtoPolyLineArray {\n    optional string name = 1;\n    repeated bytes encodings = 2;\n}\n"
  },
  {
    "path": "src/main/proto/ProtoPolygonArray.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoPolygonArrayWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nmessage ProtoPolygonArray {\n    optional string name = 1;\n    repeated bytes encodings = 2;\n}\n"
  },
  {
    "path": "src/main/proto/Relation.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoRelationWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nimport \"Tag.proto\";\n\nmessage ProtoRelation {\n    optional int64 id = 1;\n    repeated ProtoTag tags = 2;\n\n    enum ProtoItemType {\n        NODE = 0;\n        EDGE = 1;\n        AREA = 2;\n        LINE = 3;\n        POINT = 4;\n        RELATION = 5;\n    }\n\n    message RelationBean {\n        optional int64 memberId = 1;\n        optional string memberRole = 2;\n        optional ProtoItemType memberType = 3;\n    }\n\n    repeated RelationBean beans = 3;\n}\n"
  },
  {
    "path": "src/main/proto/Tag.proto",
    "content": "syntax = \"proto2\";\n\noption java_multiple_files = true;\noption java_outer_classname = \"ProtoTagWrapper\";\n\npackage org.openstreetmap.atlas.proto;\n\nmessage ProtoTag {\n    optional string key = 1;\n    optional string value = 2;\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/atlas.json",
    "content": "{\n\t\"type\": \"FeatureCollection\",\n\t\"features\": \n\t[\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.033948,\n\t\t\t\t\t37.32544\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"highway\": \"turning_circle\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.031007,\n\t\t\t\t\t37.390535\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"highway\": \"turning_circle\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.028464,\n\t\t\t\t\t37.321628\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"highway\": \"turning_circle\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.033948,\n\t\t\t\t\t\t37.32544\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.031007,\n\t\t\t\t\t\t37.390535\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"surface\": \"concrete\",\n\t\t\t\t\"name\": \"edge9\",\n\t\t\t\t\"lanes\": \"3\",\n\t\t\t\t\"highway\": \"primary\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.031007,\n\t\t\t\t\t\t37.390535\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.033948,\n\t\t\t\t\t\t37.32544\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"surface\": \"fine_gravel\",\n\t\t\t\t\"name\": \"edge_9\",\n\t\t\t\t\"highway\": \"primary\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.031007,\n\t\t\t\t\t\t37.390535\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.028464,\n\t\t\t\t\t\t37.321628\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"name\": \"edge98\",\n\t\t\t\t\"maxspeed\": \"100\",\n\t\t\t\t\"bridge\": \"cantilever\",\n\t\t\t\t\"highway\": \"secondary\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.028464,\n\t\t\t\t\t\t37.321628\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.033948,\n\t\t\t\t\t\t37.32544\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"name\": \"edge987\",\n\t\t\t\t\"maxspeed\": \"50 knots\",\n\t\t\t\t\"highway\": \"residential\",\n\t\t\t\t\"tunnel\": \"culvert\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Polygon\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t-122.033948,\n\t\t\t\t\t\t\t37.32544\n\t\t\t\t\t\t],\n\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t-122.052138,\n\t\t\t\t\t\t\t37.317585\n\t\t\t\t\t\t],\n\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t-122.028464,\n\t\t\t\t\t\t\t37.321628\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"natural\": \"grassland\",\n\t\t\t\t\"leisure\": \"golf_course\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Polygon\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t-122.031007,\n\t\t\t\t\t\t\t37.390535\n\t\t\t\t\t\t],\n\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t-122.009566,\n\t\t\t\t\t\t\t37.33531\n\t\t\t\t\t\t],\n\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t-122.028932,\n\t\t\t\t\t\t\t37.332451\n\t\t\t\t\t\t],\n\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t-122.033948,\n\t\t\t\t\t\t\t37.32544\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"leisure\": \"swimming_pool\",\n\t\t\t\t\"sport\": \"swimming\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.031007,\n\t\t\t\t\t\t37.390535\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.009566,\n\t\t\t\t\t\t37.33531\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"power\": \"line\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.052138,\n\t\t\t\t\t\t37.317585\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.028464,\n\t\t\t\t\t\t37.321628\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.028932,\n\t\t\t\t\t\t37.332451\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"aeroway\": \"runway\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.0304871,\n\t\t\t\t\t\t37.3314171\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.028932,\n\t\t\t\t\t\t37.332451\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.033948,\n\t\t\t\t\t\t37.32544\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-122.028464,\n\t\t\t\t\t\t37.321628\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"natural\": \"coastline\",\n\t\t\t\t\"waterway\": \"canal\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.052138,\n\t\t\t\t\t37.317585\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"addr:city\": \"Cupertino\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.028464,\n\t\t\t\t\t37.321628\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"addr:city\": \"Cupertino\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.033948,\n\t\t\t\t\t37.32544\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"addr:city\": \"Cupertino\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.0304871,\n\t\t\t\t\t37.3314171\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"addr:city\": \"Cupertino\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.028932,\n\t\t\t\t\t37.332451\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"addr:city\": \"Cupertino\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.009566,\n\t\t\t\t\t37.33531\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"addr:city\": \"Cupertino\"\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-122.031007,\n\t\t\t\t\t37.390535\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"addr:city\": \"Cupertino\"\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/aoi/aoi-tag-filter.json",
    "content": "{\n  \"filters\": [\n    \"aerialway->STATION\",\n    \"aeroway->TAXIWAY,AERODROME,RUNWAY,HELIPAD,HELIPORT,APRON,HANGAR,TERMINAL\",\n    \"amenity->PARKING,MOTORCYCLE_PARKING,PLACE_OF_WORSHIP,SCHOOL,RESTAURANT,FUEL,CAFE,FAST_FOOD,BANK,GRAVE_YARD,KINDERGARTEN,RECYCLING,PHARMACY,BICYCLE_PARKING,TOILETS,HOSPITAL,SHELTER,POST_OFFICE,BAR,POLICE,FIRE_STATION,TOWNHALL,PARKING_SPACE,FOUNTAIN,LIBRARY,DOCTORS,SOCIAL_FACILITY,UNIVERSITY,BICYCLE_RENTAL,COLLEGE,DIVE_CENTRE,CASINO,GAMBLING,COMMUNITY_CENTRE,EMBASSY,CONFERENCE_CENTRE,PRISON,COURTHOUSE,ARTS_CENTRE,PUBLIC_BATH,NURSING_HOME,MARKETPLACE,CLINIC,DENTIST,THEATRE,PLANETARIUM,SOCIAL_CENTRE,EXHIBITION_CENTRE\",\n    \"barrier->WALL,HEDGE,KERB,TOLL_BOOTH,CITY_WALL\",\n    \"historic->AIRCRAFT,ARCHAEOLOGICAL_SITE,BATTLEFIELD,CASTLE,CITY_GATE,CITYWALLS,FARM,FORT,GALLOWS,LOCOMOTIVE,MANOR,MEMORIAL,MONASTERY,MONUMENT,OPTICAL_TELEGRAPH,RUINS,SHIP,TOMB,WAYSIDE_SHRINE,WRECK\",\n    \"landuse->ALLOTMENTS,BASIN,BROWNFIELD,CEMETERY,COMMERCIAL,CONSTRUCTION,FARMLAND,FARMYARD,FOREST,GARAGES,GRASS,GREENFIELD,GREENHOUSE_HORTICULTURE,INDUSTRIAL,LANDFILL,MEADOW,MILITARY,ORCHARD,PLANT_NURSERY,PORT,QUARRY,RAILWAY,RECREATION_GROUND,RESIDENTIAL,RETAIL,SALT_POND,VILLAGE_GREEN,VINEYARD,RELIGIOUS,EDUCATION,WINTER_SPORTS\",\n    \"leisure->PITCH,SWIMMING_POOL,PARK,PLAYGROUND,GARDEN,SPORTS_CENTRE,NATURE_RESERVE,COMMON,TRACK,STADIUM,GOLF_COURSE,MARINA,WATER_PARK,DOG_PARK,FIREPIT,MINIATURE_GOLF,FISHING,HORSE_RIDING,FITNESS_STATION,ICE_RINK,BEACH_RESORT,BIRD_HIDE,RESORT,DANCE,ADULT_GAMING_CENTRE,AMUSEMENT_ARCADE,SUMMER_CAMP,FITNESS_CENTRE\",\n    \"man_made->ADIT,BEACON,BREAKWATER,BRIDGE,BUNKER_SILO,CAMPANILE,CHIMNEY,COMMUNICATIONS_TOWER,CRANE,CLEARCUT,GASOMETER,GROYNE,KILN,LIGHTHOUSE,MINESHAFT,MONITORING_STATION,OBSERVATORY,PETROLEUM_WELL,PIER,PUMPING_STATION,RESERVOIR_COVERED,SILO,SNOW_FENCE,SNOW_NET,STORAGE_TANK,STREET_CABINET,TOWER,WASTEWATER_PLANT,WATERMILL,WATER_TOWER,WATER_WELL,WATER_WORKS,WILDLIFE_CROSSING,WINDMILL,WORKS\",\n    \"military->BUNKER,BARRACKS,NUCLEAR_EXPLOSION_SITE,RANGE,AIRFIELD,DANGER_AREA,NAVAL_BASE,TRAINING_AREA\",\n    \"boundary->NATIONAL_PARK,PROTECTED_AREA\",\n    \"natural->WOOD,SCRUB,PEAK,CLIFF,GRASSLAND,HEATH,ROCK,BARE_ROCK,BEACH,SAND,HOT_SPRING,GEYSER,SCREE,GLACIER,CAVE_ENTRANCE,FELL,MUD,STONE,VALLEY,VOLCANO,SINKHOLE,CANYON,CRATER,DESERT,MOUNTAIN_RANGE\",\n    \"sport->TENNIS,GOLF,SOCCER,BILLIARD,MULTI,BADMINTON,EQUESTRIAN,RUNNING,ATHLETICS,MODEL_AERODROME,MOTOR,SWIMMING,BASEBALL,BASKETBALL,CYCLING,AMERICAN_FOOTBALL,VOLLEYBALL\",\n    \"tourism->INFORMATION,HOTEL,ATTRACTION,VIEWPOINT,CAMP_SITE,GUEST_HOUSE,ARTWORK,CHALET,MOTEL,HOSTEL,CARAVAN_SITE,ALPINE_HUT,THEME_PARK,ZOO,APARTMENT,WILDERNESS_HUT,GALLERY,AQUARIUM,MUSEUM\",\n    \"power->GENERATOR,SUB_STATION,PLANT,COMPENSATOR\",\n    \"shop->CONVENIENCE,SUPERMARKET,CLOTHES,HAIRDRESSER,BAKERY,CAR_REPAIR,CAR,KIOSK,DOITYOURSELF,BUTCHER,FLORIST,MALL,FURNITURE,SHOES,BICYCLE,ALCOHOL,ELECTRONICS,HARDWARE,BOOKS,BEAUTY,MOBILE_PHONE,JEWELRY,DEPARTMENT_STORE,OPTICIAN,GIFT,GREENGROCER,CAR_PARTS,CHEMIST,VARIETY_STORE,SPORTS,GARDEN_CENTRE,COMPUTER,STATIONERY,TRAVEL_AGENCY,LAUNDRY,CONFECTIONERY,BEVERAGES,DRY_CLEANING,TOYS,TAILOR,ART,BABY_GOODS,BATHROOM_FURNISHING,BOUTIQUE,CARPET,CHEESE,CHOCOLATE,COSMETICS,DAIRY,DELI,FABRIC,FARM,FASHION,VACANT\",\n    \"highway->RACEWAY,PEDESTRIAN,STEPS\",\n    \"office->GOVERNMENT\",\n    \"geological->VOLCANIC_CALDERA_RIM,PALAEONTOLOGICAL_SITE\",\n    \"waterway->DAM\",\n    \"healthcare->*\",\n    \"place->SQUARE\",\n    \"craft->WINERY\"\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/islands/islands.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Area || e instanceof Relation\"\n        },\n        \"taggableFilter\": \"natural->island|place->islet\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/landcover/land-cover-tag-filter.json",
    "content": "{\n    \"filters\": [\n      \"landuse->*\",\n      \"natural->*\",\n      \"surface->*\",\n      \"landcover->*\"\n      ]\n  }"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/canal.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Line || e instanceof Relation\"\n        },\n        \"taggableFilter\": \"natural->water&water->canal|waterway->canal\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/creek.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Line || e instanceof Area\"\n        },\n        \"taggableFilter\": \"waterway->stream\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/ditch.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Line || e instanceof Area\"\n        },\n        \"taggableFilter\": \"waterway->ditch,drain\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/harbour.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Area || e instanceof Relation\"\n        },\n        \"taggableFilter\": \"harbour->yes&landuse->!industrial,!port&industrial->!port,!shipyard\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/lagoon.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Area || e instanceof Relation\"\n        },\n        \"taggableFilter\": \"natural->water&water->lagoon\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/lake.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Area || e instanceof Relation\"\n        },\n        \"taggableFilter\": \"natural->water&water->!||water->lake,oxbow&waterway->!&landuse->!&harbour->!yes&landuse->!industrial,!port&industrial->!port,!shipyard\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/pond.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Area\"\n        },\n        \"taggableFilter\": \"natural->water&water->pond|landuse->pond\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/pool.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Area\"\n        },\n        \"taggableFilter\": \"natural->water&water->pool\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/reservoir.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Area || e instanceof Relation\"\n        },\n        \"taggableFilter\": \"natural->water&water->reservoir|landuse->reservoir\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/river.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Line || e instanceof Area || e instanceof Relation\"\n        },\n        \"taggableFilter\": \"natural->water&water->river|waterway->river,riverbank\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/finder/wetland.json",
    "content": "{\n  \"global\": {\n    \"filters\": {\n      \"default\": {\n        \"predicate\": {\n          \"imports\": [\n            \"org.openstreetmap.atlas.geography.atlas.items\"\n          ],\n          \"command\": \"e instanceof Area || e instanceof Relation\"\n        },\n        \"taggableFilter\": \"natural->wetland&wetland->swamp,marsh,reedbed\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/pbf/atlas-area.json",
    "content": "{\n    \"atlas-area\":\n    {\n        \"taggableFilter\": \"highway->platform,bus_stop,rest_area||highway->pedestrian,footway&&area->yes||highway->!&railway->!||railway->platform,traverser,station&route->!ferry&man_made->!pier||man_made->pier&&area->yes\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/pbf/atlas-edge.json",
    "content": "{\n    \"atlas-edge\":\n    {\n        \"taggableFilter\": \"access->!no||motor_vehicle->yes||motorcar->yes||vehicle->yes&oneway->!reversible&route->ferry||man_made->pier||junction->roundabout||highway->MOTORWAY,TRUNK,PRIMARY,SECONDARY,TERTIARY,UNCLASSIFIED,RESIDENTIAL,SERVICE,MOTORWAY_LINK,TRUNK_LINK,PRIMARY_LINK,SECONDARY_LINK,TERTIARY_LINK,LIVING_STREET,PEDESTRIAN,TRACK,BUS_GUIDEWAY,RACEWAY,ROAD,FOOTWAY,BRIDLEWAY,STEPS,PATH,CYCLEWAY,ESCAPE\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/pbf/atlas-relation-slicing.json",
    "content": "{\n    \"atlas-relation-slicing\":\n    {\n        \"taggableFilter\": \"type->multipolygon,boundary\"\n    },\n    \"atlas-relation-slicing-consolidate\":\n    {\n        \"taggableFilter\": \"type->boundary\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/pbf/atlas-way-section.json",
    "content": "{\n    \"atlas-way-section\":\n    {\n        \"taggableFilter\": \"highway->crossing,motorway_junction|railway->crossing,level_crossing|barrier->FENCE,WALL,GATE,HEDGE,BOLLARD,LIFT_GATE,RETAINING_WALL,STILE,CYCLE_BARRIER,KERB,YES,ENTRANCE,BLOCK,TOLL_BOOTH,CATTLE_GRID,DITCH,KISSING_GATE,CITY_WALL,GUARD_RAIL,HEDGE_BANK,WIRE_FENCE,LINE,SWING_GATE,CHAIN,TURNSTILE,EMBANKMENT,FIELD_BOUNDARY,BORDER_CONTROL,SALLY_PORT,DOOR,HAMPSHIRE_GATE,WOOD_FENCE,BUMP_GATE\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/pbf/osm-pbf-node.json",
    "content": "{\n    \"filters\": []\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/pbf/osm-pbf-relation.json",
    "content": "{\n    \"filters\": []\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/pbf/osm-pbf-way.json",
    "content": "{\n    \"filters\": [\n        \"hires->!yes\"\n    ]\n}\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/statistics/coverage/poi/counts.txt",
    "content": "#<Features>;<Type>;<possible key 1>-><possible value 1 for key 1>,<possible value 2 for key 1>|<possible key 2>-><possible value 1 for key 2>,<possible value 2 for key 2>|<possible key 3>->*\nturn_restrictions;relations;type->restriction&restriction->no_right_turn,no_left_turn,no_u_turn,no_straight_on,only_right_turn,only_left_turn,only_straight_on\n# split out turn restriction types prohibitory(no) and mandatory(only) \nprohibited_restrictions;relations;type->restriction&restriction->no_right_turn,no_left_turn,no_u_turn,no_straight_on\nmandate_restrictions;relations;type->restriction&restriction->only_right_turn,only_left_turn,only_straight_on\n\n# sign posts for highway exits\nsignposts;points;highway->motorway_junction\nsignpost_ways;edges;destination->*\n\n# all barrier values included\nbarriers;points;barrier->*\nbarriers_whitelist;points;barrier->toll_booth,sally_port,cattle_grid,entrance,border_control\n\n# Stop signs on nodes will be duplicated as a point because it has a tag. Count points only\nstop_signs;points;highway->stop\n#Stop lights on nodes will be duplicated as a point because it has a tag. Count points only\nstop_lights;points;highway->traffic_signals\nfixme;all;FIXME->*|fixme->*\nparks;areas,relations;boundary->national_park,protected_area|leisure->park,garden,common,nature_reserve|landuse->recreation_ground,village_green\n\nbuildings;areas;building->*\n# named buildings\nbuildings_named;areas;building->*&name->*\n\nairports;points,areas;aeroway->aerodrome\nhospitals;areas,points,relations;amenity->hospital|building->hospital\n\n# railway=[station OR halt] OR ( train=yes AND public_transport=[stop_position OR platform OR station] )\nrail_stops;all;railway->station,halt|train->yes&public_transport->stop_position,platform,station\n\n# railway=[station OR halt] OR ( (subway=* OR monorail=* OR tram=*) AND public_transport=[stop_position OR platform OR station] )\ntransit_rail_stops;all;railway->station,halt|subway->*^monorail->*^tram->*&public_transport->stop_position,platform,station\n\n# amenity=bus_station OR highway=bus_stop OR ( (bus=* OR trolleybus=*) AND public_transport=[stop_position OR platform OR station] )\nbus_stops;all;amenity->bus_station|highway->bus_stop|bus->*^trolleybus->*&public_transport->stop_position,platform,station\n\n# natural=water AND water=[lake OR pond OR reflecting_pool OR reservoir] OR natural=water AND (NOT water=*) OR landuse=basin\nlakes;areas,points;natural->water&water->lake,pond,reflecting_pool,reservoir|natural->water&water->!|landuse->basin\n\n# Water queries\nrivers;areas,lines,relations;natural->stream||water->canal,river,lock,moat,creek,riverbank,stream,stream_pool||waterway->river,riverbank,brook,ditch,stream,canal,derelict_canal,creek||stream->*||waterway->drain&&name->*||water->drain&&name->*&tunnel->!culvert&covered->!yes;count,distance\nwetland;areas,relations;wetland->swamp,mangrove,bog,fen,string_bog,saltern,saltmash,wet_meadow,marsh&water->!tidalflat,!reedbed||seasonal->!yes&natural->!tidalflat,!reedbed||seasonal->!yes&covered->!yes&landuse->!basin||natural->water&waterway->!floodway,!spillway&water->!floodway,!spillway;surface,count\nlakes2;areas,relations;natural->spring,hot_spring&&name->*||natural->lake,pond||water:type->lake||landuse->pond||water->lake,pond,oxbow&covered->!yes&landuse->!basin||natural->water&water->!wastewater,!waterhole,!pool,!reflecting_pool,!swimming_pool,!salt_pool,!fountain,!tank;surface,count\nreservoir;areas,relations;water->reservoir||water->dam&&natural->water||landuse->reservoir||natural->reservoir||seamark:type->dam&&natural->water&covered->!yes&water->!waterhole,!pool,!reflecting_pool,!swimming_pool,!salt_pool,!fountain,!tank;surface,count\nlagoon;areas,relations;natural->lagoon|water->lagoon|waterway->lagoon;count,surface\npool;areas,relations;water->reflecting_pool||leisure->swimming_pool,water_park,swimming_area||amenity->swimming_pool,public_bath||sport->swimming&covered->!yes;count,surface\ncoastline;edges,lines,relations;natural->coastline;distance\nharbour;all;water->harbour|seamark:type->harbour|water->cove&salt->yes|harbour->yes;count,distance,surface\nbay;areas,relations;natural->bay;count,surface\nbeach;areas,relations;natural->beach;count,surface\nisland;areas,relations;place->island&natural->*&natural->!coastline|place->island&natural->!;count,surface\npier;edges,lines,areas,relations;man_made->pier;count,distance,surface\nunknown_water;all;natural->water||waterway->water||water->water,perennial||landuse->water&covered->!yes&waterway->!drain||tags->!||name->*&water->!drain||tags->!||name->*&landuse->!basin||natural->water;count,distance,surface\nunnamed_intermittent_water;all;natural->intermittent&name->!|waterway->intermittent&name->!|water->intermittent&name->!|intermittent->*&name->!|stream->intermittent,ephemeral&name->!;count,distance,surface\n\namenity;points,areas;amenity->*\nphone;points,areas;contact:phone->*|phone->*\ncraft;points,areas;craft->*\nhistoric;points,areas;historic->*\nlanduse;points,areas;landuse->*\nleisure;points,areas;leisure->*\nmilitary;points,areas;military->*\nnatural;points,areas;natural->*\noffice;points,areas;office->*\nplace;points,areas;place->*\nshop;points,areas;shop->*\nsport;points,areas;sport->*\ntourism;points,areas;tourism->*\nPOI rollup;points,areas;amenity->*|craft->*|historic->*|landuse->*|leisure->*|military->*|natural->*|office->*|place->*|shop->*|sport->*|tourism->*\n\n# http://wiki.openstreetmap.org/wiki/Key:place#Values\ncity_center;points,areas,relations;place->municipality,city,town,village,hamlet\n\n# Details with addresses\nassociated_street;relations;type->associatedstreet\naddress_ranges;lines;addr:interpolation->*;count,distance\naddress_housenumber;areas,points;addr:housenumber->*\naddress_housename;areas,points;addr:housename->*\naddress_street;areas,points;addr:street->*\naddress_housenumber_and_street;areas,points;addr:street->*&addr:housenumber->*\naddress_housename_and_street;areas,points;addr:street->*&addr:housename->*\naddress_blocknumber;areas,points;addr:blocknumber->*\n\n# Ferries\nferry_route;edges,lines,relations;route->ferry;count,distance\n\n# Ref/Lanes\nref;relations;ref->*;count,distance\nref_no_relation;nodes,edges,lines;ref->*;count,distance\nint_ref;relations;int_ref->*;count,distance\nint_ref_no_relation;nodes,edges,lines;int_ref->*;count,distance\nlane_direction;edges;turn:lanes->*;count,distance\ntoll_booths;points;barrier->toll_booth\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/geography/atlas/statistics/coverage/poi/countsOne.txt",
    "content": "transit_rail_stops;railway->station,halt|subway->*^monorail->*^tram->*&public_transport->stop_position,platform,station"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/tags/annotations/implicit-speed-values.json",
    "content": "{\n  \"as:urban\": \"40\",\n  \"as:urban:primary\": \"60\",\n  \"as:urban:secondary\": \"60\",\n  \"as:rural\": \"110\",\n  \"at:urban\": \"50\",\n  \"at:rural\": \"100\",\n  \"at:trunk\": \"100\",\n  \"at:motorway\": \"130\",\n  \"be:living_street\": \"20\",\n  \"be:bicycle_road\": \"30\",\n  \"be:urban\": \"50\",\n  \"be-vlg:rural\": \"70\",\n  \"be-wal:rural\": \"90\",\n  \"be:trunk\": \"120\",\n  \"be:motorway\": \"120\",\n  \"ch:urban\": \"50\",\n  \"ch:rural\": \"80\",\n  \"ch:trunk\": \"100\",\n  \"ch:motorway\": \"120\",\n  \"cz:pedestrian_zone\": \"20\",\n  \"cz:living_street\": \"20\",\n  \"cz:urban\": \"50\",\n  \"cz:urban_trunk\": \"80\",\n  \"cz:urban_motorway\": \"80\",\n  \"cz:rural\": \"90\",\n  \"cz:trunk\": \"110\",\n  \"cz:motorway\": \"130\",\n  \"dk:urban\": \"50\",\n  \"dk:rural\": \"80\",\n  \"dk:motorway\": \"130\",\n  \"de:living_street\": \"7\",\n  \"de:urban\": \"50\",\n  \"de:rural\": \"100\",\n  \"de:trunk\": \"none\",\n  \"de:motorway\": \"none\",\n  \"fi:urban\": \"50\",\n  \"fi:rural\": \"80\",\n  \"fi:trunk\": \"100\",\n  \"fi:motorway\": \"120\",\n  \"fr:urban\": \"50\",\n  \"fr:rural\": \"80\",\n  \"fr:trunk\": \"110\",\n  \"fr:motorway\": \"130\",\n  \"gr:urban\": \"50\",\n  \"gr:rural\": \"90\",\n  \"gr:trunk\": \"110\",\n  \"gr:motorway\": \"130\",\n  \"hu:urban\": \"50\",\n  \"hu:rural\": \"90\",\n  \"hu:trunk\": \"110\",\n  \"hu:motorway\": \"130\",\n  \"it:urban\": \"50\",\n  \"it:rural\": \"90\",\n  \"it:trunk\": \"110\",\n  \"it:motorway\": \"130\",\n  \"jp:national\": \"60\",\n  \"jp:motorway\": \"100\",\n  \"lt:living_street\": \"20\",\n  \"lt:urban\": \"50\",\n  \"lt:rural\": \"90\",\n  \"lt:trunk\": \"120\",\n  \"lt:motorway\": \"130\",\n  \"pl:living_street\": \"20\",\n  \"pl:urban\": \"50\",\n  \"pl:rural\": \"90\",\n  \"pl:trunk\": \"100\",\n  \"pl:motorway\": \"140\",\n  \"ro:urban\": \"50\",\n  \"ro:rural\": \"90\",\n  \"ro:trunk\": \"100\",\n  \"ro:motorway\": \"130\",\n  \"ru:living_street\": \"20\",\n  \"ru:urban\": \"60\",\n  \"ru:rural\": \"90\",\n  \"ru:motorway\": \"110\",\n  \"sk:urban\": \"50\",\n  \"sk:rural\": \"90\",\n  \"sk:trunk\": \"90\",\n  \"sk:motorway\": \"90\",\n  \"si:urban\": \"50\",\n  \"si:rural\": \"90\",\n  \"si:trunk\": \"110\",\n  \"si:motorway\": \"130\",\n  \"es:living_street\": \"20\",\n  \"es:urban\": \"50\",\n  \"es:rural\": \"50\",\n  \"es:trunk\": \"90\",\n  \"es:motorway\": \"120\",\n  \"se:urban\": \"50\",\n  \"se:rural\": \"70\",\n  \"se:trunk\": \"90\",\n  \"se:motorway\": \"110\",\n  \"gb:nsl_restricted\": \"30 mph\",\n  \"gb:nsl_single\": \"60 mph\",\n  \"gb:nsl_dual\": \"70 mph\",\n  \"gb:motorway\": \"70 mph\",\n  \"ua:urban\": \"50\",\n  \"ua:rural\": \"90\",\n  \"ua:trunk\": \"110\",\n  \"ua:motorway\": \"130\",\n  \"uz:living_street\": \"30\",\n  \"uz:urban\": \"70\",\n  \"uz:rural\": \"100\",\n  \"uz:motorway\": \"110\"\n}"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/checkstyle/arrangement.txt",
    "content": "interface,public,non_static\ninterface,protected,non_static\ninterface,package_private,non_static\ninterface,private,non_static\n\nenum,public,non_static\nenum,protected,non_static\nenum,package_private,non_static\nenum,private,non_static\n\nclass,public,non_static\nclass,protected,non_static\nclass,package_private,non_static\nclass,private,non_static\n\nfield,public,static\nfield,protected,static\nfield,package_private,static\nfield,private,static\n\nstatic_initializer_block,,non_static\n\nfield,public,non_static\nfield,protected,non_static\nfield,package_private,non_static\nfield,private,non_static\n\nmethod,public,static\nmethod,protected,static\nmethod,package_private,static\nmethod,private,static\n\ninitializer_block,,non_static\n\nconstructor,public,non_static\nconstructor,protected,non_static\nconstructor,package_private,non_static\nconstructor,private,non_static\n\nmethod,public,non_static\nmethod,protected,non_static\nmethod,package_private,non_static\nmethod,private,non_static\n\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AnyToGeoJsonCommandDescriptionSection.txt",
    "content": "Convert one of the many atlas-related custom file types to GeoJSON. This is very useful when trying to view\nthese file types in visualization software. Currently, this command supports country boundary files,\nany type of sharding, as well as atlases. See the provided options and examples for various customizations.\nIf you are interested in atlas to text conversion and this command does not provide what you need, try the 'packed2text' command."
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AnyToGeoJsonCommandExamplesSection.txt",
    "content": "Convert an atlas to GeoJSON\n#$ any2geojson --atlas ~/Desktop/myatlas.atlas\nConvert a boundary file to GeoJSON:\n#$ any2geojson --country-boundary ~/boundary.txt\nConvert a boundary file to GeoJSON and include only specific countries:\n#$ any2geojson --country-boundary ~/boundary.txt --countries DMA,MTQ,GLP\nConvert a dynamic sharding tree textfile to GeoJSON:\n#$ any2geojson --sharding dynamic@/Users/you/tree.txt\nGet the GeoJSON for a precision 3 geohash sharding tree:\n#$ any2geojson --sharding geohash@3"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommandDescriptionSection.txt",
    "content": "Compute a diff between two atlases or two atlas directories using the ChangeAtlas API.\n The command will write a pretty print string serialization of the diff to STDOUT.\nThe pretty print options include: ChangeDescription (default),\nthe full FeatureChange text, as well as different types of GeoJSON serialization.\n\nThe command will return 0 when there is no diff, 1 when there is a diff, and 2\nfor any other error.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommandExamplesSection.txt",
    "content": "Compute a diff between two atlases:\n#$ atlas-diff folder/before-atlas.atlas after-atlas.atlas\nCompute a diff between two atlas folders:\n#$ atlas-diff folder/before/ folder/after/\nSet output to use line delimited GeoJSON:\n#$ atlas-diff before-atlas.atlas after-atlas.atlas --ldgeojson\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasMetadataReaderCommandDescriptionSection.txt",
    "content": "Read selected fields from the metadata of some given input atlases. By default, the reader\nwill read all fields and dump them to stdout. The user may supply additional options to filter\nfor specific fields. For example, if the user only cares about the 'size' field, they may\nsupply the '--size' option to filter for that field. Or if the user would only like to see the\n'code-version' and 'data-version' fields, they may supply '--code-version' and '--data-version'.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasMetadataReaderCommandExamplesSection.txt",
    "content": "Dump metadata to stdout for all atlases in the current directory:\n#$ atlas-metadata-reader *.atlas\nDisplay only the tag metadata for atlases in a given directory:\n#$ atlas-metadata-reader dir/*.atlas --tags\nDisplay the code and data versions of some atlases:\n#$ atlas-metadata-reader 1.atlas 2.atlas --code-version --data-version"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasSearchCommandDescriptionSection.txt",
    "content": "Search a collection of atlases for entities that match given criteria, including\nidentifiers (both Atlas and OSM), tags, geometry, and more.\n\nSpecifying multiple criteria will result in an AND-like operation, where only features that match\nall provided criteria are reported. (For example, specifying --id=3 AND --taggable-matcher='highway=*'\nwill show only those features that have an ID 3 and some kind of highway tag). However, specifying\nmultiple options within a given criterion will result in an OR-like operation within that criterion.\nFor example, specifying --id=1,2 will show features that have either ID 1 or ID 2. Note that the\n`--and-relation-members' option is a special exception to this rule. See more details in the OPTIONS section.\n\nThe final feature report will display matching entities in string form, along with the\natlas file that contained them. You can narrow the search to specific ItemTypes\nby specifying the `--types' option. The `--collect-matching' option will use\nthe MultiAtlas to save all matching atlases to a file called `collected-multi.atlas'. See the OPTIONS\nand EXAMPLES section for more information on the various search criteria.\n\nThe '--json' option will output all matches line-by-line in a JSON format. This is ideal for use\nwith JSON parsing tools like `jq'. For example, users could perform advanced operations like:\n#$ find foo/*.atlas --relation-member 'AREA,*,outer' --json | jq -r '[.path, .entity.identifier] | @tsv' | xargs -n2 sh -c 'atlas relation2multipolygon --id=$2 $1' sh\nThis command would find all relations with an `outer' AREA member in the `foo' directory of atlases,\nthen pipe those relation IDs into the `relation2multipolygon' command to create a separate atlas\nand WKT text file for each of the multipolygon relations.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasSearchCommandExamplesSection.txt",
    "content": "Display all features in an atlas:\n#$ find foo.atlas --all\nDisplay all edges in an atlas:\n#$ find foo.atlas --type EDGE\nFind features with Atlas ID 123 or 456 in an atlas:\n#$ find foo.atlas --id 123,456\nFind all coastline features in a group of atlases on your desktop:\n#$ find ~/Desktop/*.atlas --taggableFilter='natural->coastline'\nIn a directory of atlases, find all entities that are members of relation with ID 1:\n#$ find ~/my-atlases/*.atlas --predicate 'e.relations().collect { it.getIdentifier() }.contains(1L)'\nFind all features whose geometry matches some WKTs and also contain a 'population' tag\n#$ find foo.atlas --geometry='POINT (0 0):POINT (1 1)' --taggableFilter='population->*'\nFind features with OSM ID 123, then collect all containing atlases and save with MultiAtlas:\n#$ find ~/Desktop/*.atlas --osmid 123 --collect-matching\nFind all roads that lie withing a given bounding box:\n#$ find foo.atlas --bounding-polygon='POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))' --taggableFilter='highway->*'\nFind all relations that contain all of some given members:\n#$ find foo.atlas --and-relation-members 'LINE,1234,*;AREA,456,role1'\nFind all relations that contain a member node or edge with role 'via':\n#$ find foo.atlas --relation-members 'NODE,*,via;EDGE,*,via'\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShardingConverterCommandDescriptionSection.txt",
    "content": "This command provides an easy way to change the sharding in which a folder\nof atlas files is described.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShardingConverterCommandExamplesSection.txt",
    "content": "Convert a folder from geohash precision 4 to slippy tile zoom 7\n#$ shardingConverter --input=/path/to/in --output=/path/to/out --inputSharding=geohash@4 --outputSharding=slippy@7\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShellToolsDemoCommandDescriptionSection.txt",
    "content": "This command serves as a demo of the subcommand API and capabilities.\nThis example section is filled out to demonstrate the behavior of the\nautomatic documentation formatting code.\n\nUnfortunately, Java does not provide a way to declare multi-line string literals. Due to this\nshortcoming, declaring these paragraphs in code can be a pain. Thankfully,\nan API is provided that allows you to read in doc page sections from a resource\nfile. The paragraph you are currently reading was generated in this manner.\n\nThis command also shows off a more advanced feature of the command API, namely contexts.\nContexts are just a way to provide multiple, mutually exclusive usage styles for a command.\nIn this demo command, notice that the SYNOPSIS section has a command line for specifying a\nfavorite meal along with some foods and cheese/beer options. But it also contains a command line\nfor a --breakfast usage, which changes the expected number of arguments. The option parser will\ntake the current command line and attempt to match it to each parse context, in the order displayed\nby SYNOPSIS.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShellToolsDemoCommandExamplesSection.txt",
    "content": "Run a dinner command with pizza and wings. Use 805 beer and the default cheese:\n#$ demo dinner pizza wings --beer=805 --cheese\nRun a breakfast command with some waffles and pancakes:\n#$ demo --breakfast waffles pancakes\nRun a lunch command with a salad. Use some tasty parmesan cheese:\n#$ demo lunch salad --cheese=parmesan\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/ConcatenateAtlasCommandDescriptionSection.txt",
    "content": "The fatlas command provides an easy way to concatenate atlas files together\nusing the MultiAtlas class. The command takes an arbitrary number of input\natlas files, reads them into a MultiAtlas, and then saves that MultiAtlas to\na file called 'output.atlas' in the current working directory. The '--output'\noption can be used to change this directory. See the ATLAS LOADER section\nfor more info.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/ConcatenateAtlasCommandExamplesSection.txt",
    "content": "Concatenate all atlas files on your desktop and write them to\nan output atlas in your home directory:\n#$ fatlas ~/Desktop/*.atlas --output ~\nConcatenate two atlases, but fail fast if either cannot be found:\n#$ fatlas --strict ~/file1.atlas ~/file2.atlas\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/CountryBoundaryMapPrinterCommandDescriptionSection.txt",
    "content": "Read a country boundary map and print all the country boundaries in it to geojson and wkt.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/CountryBoundaryMapPrinterCommandExamplesSection.txt",
    "content": "#$ boundary-itemizer --country-boundary=/Users/example/path/to/boundary.txt.gz\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/CountryShardToBoundsCommandDescriptionSection.txt",
    "content": "Given a shard (or multiple shards) in any format, output the WKT of the shard's boundary. This\nboundary will always be a rectangle. The shards should be specified using their getName() string\nformat, e.g. the SlippyTile shard for [Zoom: 9, X: 168, Y: 233] would be specified as '9-168-233'.\n\nThis command also supports a reverse match for SlippyTile and GeoHashTile shards using the\n'--reverse' option. The reverse match expects WKT polygon(s) as input, and will spit out any shards\nwhose bounds exactly match a given WKT. If no matches are found, the command will print nothing and\nexit.\n\nUse the '--country-boundary' option to instead show the WKT boundary for given country code(s). The\ncode(s) should be provided in ISO3 format. If you are unsure of the ISO code for your desired\ncountry, try using the 'iso-country-code' command."
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/CountryShardToBoundsCommandExamplesSection.txt",
    "content": "Get the boundaries of some SlippyTile shards:\n#$ country-shard-bounds 9-168-233 9-168-234\nGet the boundary of a GeoHashTile shard:\n#$ country-shard-bounds dd4fb\nGet the boundaries of an assortment of shards:\n#$ country-shard-bounds DMA_9-168-233 1-2-3 f45tkp8n\nGet the shard, if it exists, with the given bounds:\n#$ country-shard-bounds --reverse 'POLYGON ((0 -85.0511288, 0 0, 180 0, 180 -85.0511288, 0 -85.0511288))'\nGet the boundaries for some countries:\n#$ country-shard-bounds --country-boundary ~/boundaries.txt DMA USA"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/HelloWorldCommandDescriptionSection.txt",
    "content": "Prints a simple greeting. The greeting can be personalized with the\n'--name' option.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/IsoCountryCodeCommandDescriptionSection.txt",
    "content": "Query the ISO country specification using a country code or a country display name.\nThe command will automatically differentiate between the ISO2/3 code and the display name.\nAdditionally, the command is able to understand mild misspellings in display names. Use\nthe '--all' option to display all available ISO countries."
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/IsoCountryCodeCommandExamplesSection.txt",
    "content": "Get the ISO country associated with code 'USA':\n#$ iso-country-code USA\nGet the ISO country associated with display name 'United Kingdom':\n#$ iso-country-code 'United Kingdom'\nGet the ISO country associated with misspelled display name 'gernamy', showing top 5 matches:\n#$ iso-country-code -n 5 'gernamy'\nMake multiple queries:\n#$ iso-country-code 'USA' 'GB' 'gernamy' 'France'\nShow all countries:\n#$ iso-country-code --all"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/JavaToProtoSerializationCommandDescriptionSection.txt",
    "content": "Convert Java-serialized atlas(es) to Protocol Buffers format in place or to a specified output\ndirectory. If desired, the reverse conversion can be performed using the '--reverse' option.\nAdditionally, the format can be checked using the '--check' option. When checking the format, no\nconversion will take place."
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/JavaToProtoSerializationCommandExamplesSection.txt",
    "content": "Convert all atlases on your desktop to Protocol Buffers format:\n#$ java2proto ~/Desktop/*.atlas\nConvert an atlas from Protocol Buffers format back to Java format:\n#$ java2proto java.atlas --reverse\nCheck the serialization format of some atlases in some folder:\n#$ java2proto --check some-folder/*.atlas"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/OsmFileParserCommandDescriptionSection.txt",
    "content": "Take an osm file created by JOSM (with temporary identifiers) and transform it into a mock osm file that looks like it is fully committed.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/OsmFileParserCommandExamplesSection.txt",
    "content": "Translate a file josm file into an osm file:\n#$ josm2osm /path/to/file.josm.osm /path/to/file.osm\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/OsmToAtlasCommandDescriptionSection.txt",
    "content": "This command takes an '.osm' or a '.josm.osm' file and converts its contents into a PackedAtlas.\nUse the '--country' option to enable a simple default slicing that applies the given country code\nwith a maximum bounding box.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/OsmToAtlasCommandExamplesSection.txt",
    "content": "Convert foo.osm to foo.atlas:\n#osm2atlas foo.osm foo.atlas\nConvert the JOSM formatted foo.josm.osm to foo.atlas, and apply a DMA country code:\n#osm2atlas --josm foo.josm.osm foo.atlas --country DMA\nConvert foo.osm on your desktop to foo.atlas and save it in your home folder:\n#osm2atlas ~/Desktop/foo.osm ~/foo.atlas\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/PackedToTextAtlasCommandDescriptionSection.txt",
    "content": "Transform a PackedAtlas (or multiple PackedAtlases) into a TextAtlas (or multiple TextAtlases).\nBy default, the created text atlas files will be written to the current working directory.\nThe names of the new text atlas files will be the same as the input files, but with a '.txt'\nextension instead of the usual '.atlas' extension.\n\nTo save as GeoJSON instead of using the TextAtlas format, use the '--geojson' option.\nYou can use line-delimited GeoJSON instead with '--ldgeojson'. Line-delimited GeoJSON\nis much easier to read, since the regular GeoJSON format is all on one line. When using\nGeoJSON, the new text atlas files will be created with the extension '.geojson'.\n\nAdditionally, by using the '--reverse' option this command can transform text-based atlases\nin TextAtlas format back into PackedAtlases (GeoJSON format conversion is not supported). The\nnames of the new binary atlas files are created by taking the extension-less names of the TextAtlas\nfiles and appending '.atlas' extensions. So if a TextAtlas was named 'myatlas.atlas.txt', the\nresulting binary PackedAtlas will be called 'myatlas.atlas'.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/PackedToTextAtlasCommandExamplesSection.txt",
    "content": "Transform 'file.atlas' into a text atlas:\n#$ packed2text file.atlas\nTransform all atlases on your desktop into text atlases, in parallel:\n#$ packed2text ~/Desktop/*.atlas --parallel\nTransform all atlases in your home folder to text atlases, and save\nthem to a folder on your desktop\n#$ packed2text ~/*.atlas -o ~/Desktop/my-folder\nTransform an atlas to GeoJSON:\n#$ packed2text file.atlas --geojson\nTransform an atlas to line-delimited GeoJSON, and fail fast if \nit does not exist:\n#$ packed2text file.atlas -l --strict\nTransform a TextAtlas back into a PackedAtlas:\n#$ packed2text --reverse file.atlas.txt\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/PbfToAtlasCommandExamplesSection.txt",
    "content": "Convert a PBF to an Atlas:\n#$ pbf2atlas 6-42-24.pbf --countryName KAZ\nConvert a PBF to an Atlas with a custom output directory:\n#$ pbf2atlas 6-42-24.pbf --countryName KAZ --output=atlas\nConvert a directory of PBFs to Atlases in a custom output directory:\n#$ pbf2atlas pbf/* --countryName KAZ --output=atlas\nConvert a PBF to an Atlas with a custom boundary:\n#$ pbf2atlas 6-42-24.pbf --countryName KAZ --bounds=bounds.txt\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/PbfToAtlasDescriptionSection.txt",
    "content": "Convert PBF file(s) into sectioned Atlas file(s). The default behavior\nwill output the files to the same directory, but a custom output directory can be supplied. \nBy default, the created Atlas files will be written to the current working directory. The \nnames of the new Atlas files will prepend the country name, use the same name as the PBF file,\nand use the filetype \".atlas\".\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/SubAtlasCommandDescriptionSection.txt",
    "content": "Cut a subatlas using some given parameters. The atlas can be cut to a\npolygon boundary given by a supplied WKT, and/or cut using a predicate.\nIn cases where both a predicate and a polygon are supplied, the predicate\ncut will be applied before the polygon cut. This can be changed with the\n'--slice-first' option. The predicate variable will be interpreted as an AtlasEntity.\nFor more information about predicates in general, see the PREDICATE section.\n\nIn addition to polygons and predicates, the type of cut can be specified using the '--cut-type' option.\nThis utilizes the AtlasCutType enum with available cut types:\n\nSOFT_CUT: Perform a cut and keep all entities that match the filter or bound. However, bring\nin additional atlas entities that did not meet the original criteria, in order to satisfy atlas\nintegrity. Example: An Atlas Edge is brought in via boundary or filter match, but one of its\nNodes is not. The Node would be brought in, despite not meeting initial criteria, in order to\nmaintain Atlas integrity.\n\nSILK_CUT: Largely the same as a soft-cut, however there is one important difference. During\nsoft-cuts, Points are only brought in if they match an Edge. However, this filter is too\nrestrictive if the cut is being performed before way-sectioning, as in this case all Lines are\npotential Edge candidates and must have Points associated with their coordinates preserved.\nSilk-cut will perform an extra check so that all Lines being included will have any Points\nassociated with their coordinates preserved as well, allowing way-sectioning to run on these\nAtlases. On a country sliced and way sectioned atlas, silk-cut and soft-cut should be equivalent.\n\nHARD_CUT_ALL: Perform a cut, only keep entities that match the bound or filter. If including\nthe item in the final Atlas breaks Atlas integrity, exclude that entity. Example: An Edge is\nbrought in via boundary or filter match, but its start or end Node is omitted. As a result, the\nEdge is left out of the final Atlas.\n\nHARD_CUT_RELATIONS_ONLY: Perform a soft cut and maintain Atlas integrity for all Atlas Items.\nFor all Relations - perform a hard cut and only include members that satisfy the given predicate\nor bound. This case will remove Relations altogether if no members satisfy the required\nconditions.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/SubAtlasCommandExamplesSection.txt",
    "content": "Soft cut an atlas along a polygon:\n#$ subatlas --polygon='POLYGON((-61 15,-61 16,-60 16,-60 15,-61 15))' file.atlas \nHard cut a directory of atlases along a polygon, in parallel:\n#$ subatlas dir/*.atlas --polygon='POLYGON((-61 15,-61 16,-60 16,-60 15,-61 15))' --parallel --cut-type=HARD_CUT\nCut an atlas containing only points within a given polygon:\n#$ subatlas file.atlas --predicate 'e.getType() == ItemType.POINT' --polygon 'POLYGON((-61 15,-61 16,-60 16,-60 15,-61 15))'"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/TaggableMatcherPrinterCommandDescriptionSection.txt",
    "content": "Print out the tree views for a set of given TaggableMatcher description strings.\nThe strings will run through the TaggableMatcher parsing system.\n\nIf a tree is too long to fit in your terminal window, try piping it into less\nwith linewrap disabled using less's -S option.\n\nAlternatively, you may supply the '--reverse' flag in order to convert old-style\nTaggableFilters into TaggableMatchers.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/TaggableMatcherPrinterCommandExamplesSection.txt",
    "content": "Print the trees for a few TaggableMatchers:\n#$ print-matcher 'foo=bar' 'baz=bat | cat = mat'\nPrint the tree for a large matcher and make it easier to read:\n#$ print-matcher '!(foo=bar | baz=bat) & cat=mat' | less -S\nConvert some old-style TaggableFilters into TaggableMatchers:\n#$ print-matcher --reverse 'foo->bar|baz->bat' 'cat->hat,mat'\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/WKTShardCommandDescriptionSection.txt",
    "content": "Look for WKT or shard intersections with a given sharding or a given country boundary\nmap. Read on for more information on these use-cases.\n\n1) Given a sharding tree and some supplied WKT or shard strings, spit out the shards that intersect\neach geometry string. The '--sharding' option should specify a string that is parsable by\nSharding#forString (e.g. dynamic@/path/to/tree.txt, slippy@10, geohash@5, etc.).\n\n2) Given a country boundary map and some WKT or shard strings, spit out the countries that intersect\nthe given geometry strings. The '--country-boundary' option should point to a valid\ncountry boundary file as expected by the CountryBoundaryMap class.\n\nUse the '--input' option to specify a file from which to read WKT or shard strings,\nline-by-line. This will supplement any input provided on the\ncommand line. Each line in the input file will be interpreted as\na single WKT entity or shard string.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/WKTShardCommandExamplesSection.txt",
    "content": "See the dynamic shard (defined in tree.txt) that covers POINT (10 30):\n#$ wkt-shard --sharding=dynamic@/Users/example/path/to/tree.txt \"POINT (10 30)\"\nSee all dynamic shards (defined in tree.txt) that intersect some given lines:\n#$ wkt-shard --sharding=dynamic@/Users/example/path/to/tree.txt \"LINESTRING (30 10, 10 30, 40 40)\" \"LINESTRING (40 15, 15 30, 50 50)\"\nSee all slippy tile shards that intersect a point, using a slippy tile sharding at zoom 10:\n#$ wkt-shard --sharding=slippy@10 \"POINT (10 30)\"\nSee all precision 5 geohash tile shards that intersect some WKTs, taken from a file:\n#$ wkt-shard --sharding=geohash@5 --input /Users/example/Desktop/wkt-input.txt\nSee countries that intersect some given shards and WKTs:\n#$ wkt-shard --country-boundary /Users/example/world_boundaries.txt 1-2-3 7zzz 'POINT (10 30)'\nSee which dynamic shards from tree 'tree.txt' intersect with geohash tile 7zzz\n#$ wkt-shard --sharding dynamic@/Users/example/tree.txt 7zzz\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/AtlasLoaderCommandSection.txt",
    "content": "This man section is generated for any command which inherits atlas loader behaviour.\nAn atlas loader command is any command that receives an arbitrary number of input\natlas files on the command line, through the <input-atlases...> argument. A few quick\nexamples:\n#$ example-loader-command myatlas1.atlas myatlas2.atlas --opt=optarg\n#$ example-loader-command ~/*.atlas --opt=optarg\n#$ example-loader-command atlas1.atlas atlas2.atlas atlas3.atlas --strict\nIf an atlas loader command cannot find one of the specified atlas files, it will\nsimply skip over loading the file. This is unless the '--strict' option is applied,\nin which case the load will fail fast.\n\nThe '--combine' option forces the atlas loader command to process its input atlases as a single MultiAtlas.\nThe MultiAtlas construction step will occur before the process step, and then the processing will be applied to\nthe newly combined MultiAtlas. The combined MultiAtlas will always have the name 'combined.atlas'.\n\nThe '--parallel' option forces the atlas loader command to process the input atlases in parallel.\n\nNote that any atlas loader command automatically inherits multiple output behaviour.\nIn this final example, we strictly process all atlases in your home folder in parallel, and output the results\nto your desktop.\n#$ example-loader-command --strict ~/*.atlas --parallel --output=/Users/$(whoami)/Desktop\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/AtlasLoaderTemplateSection.txt",
    "content": "This command is an atlas loader command, or one that makes use of the atlas loader template to load\natlases from the filesystem. An atlas loader command receives an arbitrary number of input\natlas files on the command line, through the <input-atlases...> argument. A few quick\nexamples:\n#$ example-loader-command myatlas1.atlas myatlas2.atlas --opt=optarg\n#$ example-loader-command ~/*.atlas --opt=optarg\n#$ example-loader-command atlas1.atlas atlas2.atlas atlas3.atlas --strict\nIf an atlas loader command cannot find one of the specified atlas files, it will\nsimply skip over loading the file. This is unless the '--strict' option is applied,\nin which case the load will fail fast.\n\nThe '--combine' option forces the atlas loader command to process its input atlases as a single MultiAtlas.\nThe MultiAtlas construction step will occur before the process step, and then the processing will be applied to\nthe newly combined MultiAtlas. The combined MultiAtlas will always have the name 'combined.atlas'.\n\nThe '--parallel' option forces the atlas loader command to process the input atlases in parallel.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/CountryBoundaryMapTemplateSection.txt",
    "content": "The '--country-boundary' option should point to a valid country boundary file in the format\nexpected by the CountryBoundaryMap class. The file will be loaded using CountryBoundaryMap's\nfromPlainText method, which can handle gzipped or uncompressed files.\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/MultipleOutputCommandSection.txt",
    "content": "This man section is generated for any command which inherits multiple output behaviour. \nThis behaviour provides an '--output' option, which can be used to change the default\nlocation of any output files. This example changes the output location of an example\ncommand to your desktop:\n#$ example-output-command myatlas1.atlas --output ~/Desktop"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/OutputDirectoryTemplateSection.txt",
    "content": "This command provides an '--output-directory' option, which can be used to change the default\nlocation of any output files. This example changes the output location of an example\ncommand to your desktop:\n#$ example-output-command myatlas1.atlas --output-directory ~/Desktop"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/PredicateTemplateSection.txt",
    "content": "The '--predicate' option supports predicate input using Groovy integration. When you supply\ncode to the '--predicate' option, you are supplying a Groovy boolean expression which will be applied\nas a Predicate<E>, where E is a type defined by the command. See the command's main documentation\nfor more information about the typing of E.\n\nYour expression will be wrapped in some Groovy code that handles imports for you, as well as\nconversion from the expression to the Java Predicate<E>. If you need to import additional packages\nbeyond the ones included by default, specify a comma separated list of package names to the\n'--imports' option like so: '--imports=com.hello.world,com.foo.bar'\n\nNote that the E against which you are testing is explicitly bound to a variable called\n'e'. See the following examples, assuming E is an AtlasEntity:\n\nMatch all entities that are members of at least 3 relations:\n#--predicate 'e.relations().size() > 3'\nMatch all edges that are connected to edge with ID 1:\n#--predicate 'e instanceof Edge && e.connectedEdges().collect { it.getIdentifier() }.contains(1L)'"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/ShardingTemplateSection.txt",
    "content": "The '--sharding' option should specify a string that is parsable by Sharding's forString method.\nExamples include dynamic@/path/to/tree.txt, slippy@10, geohash@5, etc."
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/random/dictionary.txt",
    "content": "a\na-horizon\na-ok\naardvark\naardwolf\nab\naba\nabaca\nabacist\naback\nabactinal\nabacus\nabaddon\nabaft\nabalienate\nabalienation\nabalone\nabampere\nabandon\nabandoned\nabandonment\nabarticulation\nabase\nabased\nabasement\nabash\nabashed\nabashment\nabasia\nabasic\nabate\nabatement\nabating\nabatis\nabatjour\nabattis\nabattoir\nabaxial\nabba\nabbacy\nabbatial\nabbatical\nabbatis\nabbe\nabbess\nabbey\nabbot\nabbreviate\nabbreviated\nabbreviation\nabbreviature\nabc\nabcoulomb\nabdal\nabderite\nabdicable\nabdicant\nabdicate\nabdication\nabdicator\nabditory\nabditos\nabdomen\nabdominal\nabdominocentesis\nabdominoscope\nabdominoscopy\nabdominous\nabdominousness\nabdominovesical\nabduce\nabducent\nabduct\nabduction\nabductive\nabductor\nabeam\nabecedarian\nabecedarius\nabecedary\nabed\nabel\nabelia\nabelmoschus\nabelmosk\nabends\naber\naberdeen\naberdevine\naberrance\naberrant\naberration\nabest\nabet\nabetalipoproteinemia\nabetment\nabettor\nabeunt\nabeyance\nabeyant\nabfarad\nabhenry\nabhor\nabhorrence\nabhorrent\nabhorrer\nabibis\nabidance\nabide\nabiding\nabidjan\nabience\nabient\nabies\nabigail\nabiit\nabilities\nability\nabiogenesis\nabiogenetic\nabiogenist\nabiotrophy\nabito\nabject\nabjection\nabjectly\nabjectness\nabjunction\nabjuration\nabjurationabjurement\nabjure\nabkari\nablactation\nablated\nablation\nablative\nablaut\nablaze\nablaze(p)\nable\nablebodied\nablegate\nableism\nableness\nablepharia\nablepsia\nablepsy\nabloom\nablude\nablution\nablutionary\nabnaki\nabnegation\nabnegator\nabnormal\nabnormality\nabnormalize\nabnormally\nabnormis\nabnormity\nabnormous\naboard\nabocclusion\nabode\nabodement\naboding\nabohm\naboideau\nabois\naboiteau\nabolengo\nabolish\nabolishable\nabolishment\nabolition\nabolitionary\nabolitionism\nabolitionist\nabolitionize\nabomasal\nabomasum\nabominable\nabominate\nabomination\nabominator\naborad\naboral\nabord\naboriginal\naborigine\naborigines\naborning\nabort\naborticide\nabortifacient\nabortion\nabortionist\nabortive\nabortively\nabortus\nabound\nabounding\nabout\nabout(p)\nabout-face\nabouts\nabove\nabove-mentioned\naboveboard\naboveground\nabovementioned\nabovesaid\nabovestairs\nabra\nabracadabra\nabrachia\nabrade\nabraded\nabrader\nabraham\nabramis\nabranchiate\nabrasion\nabrasive\nabreast\nabrege\nabreption\nabridge\nabridged\nabridger\nabridgment\nabroach\nabroad\nabrocoma\nabrocome\nabrogate\nabrogated\nabrogation\nabronia\nabrupt\nabruption\nabruptly\nabruptness\nabruzzi\nabscess\nabscessed\nabscind\nabscision\nabscissa\nabscission\nabscond\nabsconder\nabscondment\nabsence\nabsens\nabsent\nabsentee\nabsenteeism\nabsently\nabsentminded\nabsentmindedness\nabsento\nabsents\nabsinth\nabsinthe\nabsolute\nabsolutely\nabsoluteness\nabsolution\nabsolutism\nabsolutist\nabsolve\nabsolved\nabsolver\nabsolvitory\nabsolvitur\nabsonant\nabsonous\nabsorb\nabsorbable\nabsorbate\nabsorbed\nabsorbefacient\nabsorbency\nabsorbent\nabsorber\nabsorbing\nabsorption\nabsorptivity\nabsquatulate\nabstain\nabstainer\nabstemious\nabstemiously\nabstemiousness\nabstention\nabsterge\nabstergent\nabstersion\nabstersive\nabstinence\nabstinent\nabstract\nabstracted\nabstractedly\nabstractedness\nabstraction\nabstractionism\nabstractionist\nabstractive\nabstractly\nabstractness\nabstractor\nabstruse\nabstrusely\nabsurd\nabsurdity\nabsurdly\nabsurdness\nabsurdum\nabudefduf\nabulia\nabulic\nabuna\nabundance\nabundant\nabundanti\nabundantly\nabuse\nabused\nabuser\nabusive\nabusively\nabut\nabutilon\nabutment\nabuttal\nabutter\nabutting\nabuzz\nabvolt\nabwatt\naby\nabysm\nabysmal\nabyss\nabyssal\nabyssinian\nac\nacacia\nacademia\nacademic\nacademical\nacademically\nacademician\nacademicianship\nacademist\nacademy\nacadia\nacadian\nacalypha\nacanthaceae\nacanthisitta\nacanthocephala\nacanthocephalan\nacanthocereus\nacanthocybium\nacanthocyte\nacanthocytosis\nacanthoid\nacantholysis\nacanthoma\nacanthophis\nacanthopterygii\nacanthoscelides\nacanthosis\nacanthotic\nacanthuridae\nacanthurus\nacanthus\nacapnic\nacapulco\nacardia\nacariasis\nacariatre\nacaricide\nacarid\nacaridae\nacarina\nacarine\nacaritre\nacarophobia\nacarpelous\nacarpous\nacarus\nacatalectic\nacataphasia\nacathexia\nacathexis\nacaudate\nacaulescent\naccedas\naccede\naccelerando\naccelerate\naccelerated\naccelerating\nacceleration\naccelerative\naccelerator\naccelerometer\naccension\naccent\naccented\naccentor\naccents\naccentual\naccentuate\naccentuation\naccept\naccepta\nacceptability\nacceptable\nacceptably\nacceptance\nacceptation\naccepted\naccepting\nacception\nacceptive\nacceptor\naccess\naccessible\naccession\naccessional\naccessorial\naccessory\nacciaccatura\naccidence\naccident\naccident-prone\naccidental\naccidentally\naccidentalness\naccidents\naccipere\naccipient\naccipiter\naccipitres\naccipitridae\naccipitriformes\naccipitrine\nacclaim\nacclamate\nacclamation\nacclimatization\nacclimatize\nacclivitous\nacclivity\nacclivous\naccloy\naccolade\naccommodate\naccommodating\naccommodation\naccommodational\naccommodative\naccomodation\naccompanied\naccompaniment\naccompanist\naccompany\naccompanying\naccompli\naccomplice\naccomplish\naccomplishable\naccomplished\naccomplishment\naccomplishments\naccompts\naccord\naccordance\naccordant\naccordian\naccording\naccordingly\naccordion\naccordionist\naccost\naccouchement\naccoucheur\naccoucheuse\naccount\naccountability\naccountable\naccountable(p)\naccountableness\naccountancy\naccountant\naccountantship\naccounter\naccounting\naccounts\naccouple\naccouplement\naccousente\naccouter\naccoutered\naccouterment\naccouterments\naccoy\naccra\naccredit\naccreditation\naccredited\naccretion\naccretionary\naccretive\naccrimination\naccroach\naccrue\naccrued\naccrust\naccubation\naccueil\naccultural\nacculturation\nacculturational\naccumbent\naccumulate\naccumulated\naccumulation\naccumulative\naccuracy\naccurate\naccurately\naccurse\naccursed\naccusable\naccusation\naccusative\naccusatorial\naccusatory\naccuse\naccused\naccuser\naccusing\naccusingly\naccustom\naccustomary\naccustomed\nace\nacebutolol\naceite\naceldama\nacentric\nacephalia\nacephalous\nacequia\nacequiador\nacequiamadre\nacer\naceraceae\nacerate\naceration\nacerb\nacerbate\nacerbic\nacerbity\nacerola\nacervate\nacervatim\nacervation\nacervulus\nacervus\nacescent\nacetabular\nacetabulum\nacetal\nacetaldehyde\nacetamide\nacetaminophen\nacetanilide\nacetate\nacetic\nacetone\nacetonic\nacetophenetidin\nacetose\nacetous\nacetyl\nacetylcholine\nacetylene\nacetylenic\nacetylic\nachaean\nachar\nacharn\nacharne\nacharnement\nachates\nache\nachene\nachenial\nacheron\nacheronian\nacherontia\nacherontis\nacheta\nachievability\nachievable\nachieve\nachievement\nachiever\nachillea\nachillean\nachilles\nachimenes\naching\nachira\nachivi\nachlamydeous\nachlorhydria\nachlorhydric\nachoerodus\nacholia\nachomawi\nachondrite\nachondritic\nachondroplasia\nachondroplastic\nachras\nachromatic\nachromatin\nachromatinic\nachromatism\nachromatize\nachromatous\nachromia\nachromic\nachylia\nacicula\nacicular\naciculate\nacid\nacid-fast\nacid-forming\nacid-loving\nacidemia\nacidic\nacidification\nacidify\nacidimetric\nacidimetry\nacidity\nacidophil\nacidophilic\nacidosis\nacidotic\nacidulate\nacidulated\nacidulous\nacierta\naciform\nacinaform\nacinar\naciniform\nacinonyx\nacinos\nacinus\nacipenser\nacipenseridae\nackee\nacknowledge\nacknowledgeable\nacknowledged\nacknowledgement\nacknowledgment\nacme\nacne\nacned\nacneiform\nacnidosporidia\nacocanthera\nacold\nacology\nacolothyst\nacolyte\nacolyth\nacomia\naconcagua\naconite\naconitum\nacoraceae\nacorea\nacorn\nacorus\nacousma\nacoustic\nacoustically\nacoustician\nacoustics\nacquaint\nacquaintance\nacquainted\nacquainted(p)\nacquaintenace\nacquaintend\nacquainting\nacquest\nacquiesce\nacquiescence\nacquiescent\nacquirable\nacquire\nacquired\nacquirement\nacquirements\nacquirer\nacquiring\nacquirit\nacquisition\nacquisitions\nacquisitive\nacquisitiveness\nacquit\nacquitment\nacquittal\nacquittance\nacquitted\nacrasiomycetes\nacre\nacre-foot\nacreage\nacres\nacrid\nacrididae\nacridity\nacridotheres\nacrilan\nacrimonious\nacrimony\nacris\nacritical\nacritude\nacroama\nacroamatic\nacroamatical\nacroamatics\nacroanesthesia\nacroatic\nacrobat\nacrobates\nacrobatic\nacrobatics\nacrocarp\nacrocarpous\nacrocarpus\nacrocentric\nacrocephalus\nacroclinium\nacrocomia\nacrocyanosis\nacrodont\nacrogen\nacrogenic\nacromatic\nacromegalic\nacromegaly\nacromicria\nacromion\nacromphalus\nacromyotonia\nacronym\nacronymic\nacropetal\nacrophobia\nacrophobic\nacropolis\nacropora\nacrosome\nacrospire\nacross\nacross-the-board\nacrostic\nacrostichum\nacrylic\nact\nacta\nactable\nactaea\nacted\nacti\nactias\nactifed\nactin\nactinal\nacting\nacting(a)\nactinia\nactiniaria\nactinic\nactinidia\nactinidiaceae\nactiniopteris\nactinism\nactinium\nactinoid\nactinolite\nactinomeris\nactinometer\nactinometric\nactinometry\nactinomorphic\nactinomyces\nactinomycetacaea\nactinomycetal\nactinomycetales\nactinomycete\nactinomycin\nactinomycosis\nactinomycotic\nactinomyxidia\nactinomyxidian\nactinopod\nactinopoda\naction\nactionable\nactions\nactitis\nactium\nactivated\nactivating(a)\nactivation\nactivator\nactive\nactively\nactiveness\nactivism\nactivist\nactivity\nactomyosin\nactor\nactress\nacts\nactu\nactual\nactuality\nactualized\nactually\nactuarial\nactuary\nactuateact\nactuated\nactuator\nactum\nactus\nacu\nacuate\nacuity\naculea\naculeate\naculeated\naculeus\nacumen\nacuminate\nacuminated\nacumination\nacun\nacupressure\nacupuncture\nacute\nacutely\nacuteness\nacyclic\nacyclovir\nad\nad-lib\nadactylia\nadactylism\nadactylous\nadad\nadaga\nadage\nadagio\nadalia\nadam\nadamance\nadamant\nadamantean\nadamantine\nadamantly\nadams\nadansonia\nadapa\nadapid\nadapt\nadaptability\nadaptable\nadaptation\nadaptational\nadapted\nadapter\nadaption\nadaptive\nadar\nadaxial\nadd\naddable\naddax\nadde\nadded\naddend\naddendum\nadder\naddere\naddesse\naddict\naddicted\naddiction\naddictive\nadding\naddison\nadditament\naddition\nadditional\nadditionally\nadditive\nadditum\naddle\naddle-head\naddlebrained\naddled\naddlehead\naddlepated\naddress\naddressable\naddressed\naddressee\naddresses\nadduce\nadducent\nadducing\nadduction\nadductive\nadductor\nade\nadeem\nadel\nadelaide\nadelges\nadelgid\nadelgidae\nadelie\nadelig\nadelomorphous\nademption\nademptum\naden\nadenanthera\nadenine\nadenitis\nadenium\nadenocarcinoma\nadenocarcinomatous\nadenography\nadenoid\nadenoidal\nadenoidectomy\nadenoma\nadenomegaly\nadenopathy\nadenosine\nadenota\nadenovirus\nadeo\nadeology\nadept\nadeptness\nadequacy\nadequate\nadequately\nadespotic\nadhere\nadherence\nadherent\nadhering\nadhesion\nadhesive\nadhesiveness\nadhibenda\nadhibit\nadhibition\nadhortation\nadiabatic\nadiantaceae\nadiantum\nadiaphanous\nadiathermancy\nadience\nadient\nadieu\nadige\nadipocere\nadipose\nadiposity\nadirondacks\nadit\naditi\naditya\nadj\nadjacency\nadjacent\nadjection\nadjectitious\nadjectival\nadjectivally\nadjective\nadjectively\nadjectives\nadjoin\nadjoining\nadjourn\nadjournment\nadjuc\nadjudge\nadjudicate\nadjudication\nadjudicative\nadjudicator\nadjunct\nadjunctive\nadjuration\nadjuratory\nadjure\nadjust\nadjustable\nadjusted\nadjuster\nadjustive\nadjustment\nadjutage\nadjutant\nadjuvant\nadjuvat\nadlumia\nadmass\nadmeasurement\nadminister\nadministrable\nadministration\nadministrative\nadministratively\nadministrator\nadministrators\nadmirability\nadmirable\nadmirably\nadmiral\nadmiralty\nadmirari\nadmiration\nadmire\nadmired\nadmirer\nadmiring\nadmiringly\nadmissable\nadmissibility\nadmissible\nadmission\nadmissive\nadmit\nadmittable\nadmittance\nadmitted\nadmitted(a)\nadmitting\nadmixture\nadmlration\nadmonish\nadmonished\nadmonisher\nadmonition\nadmonitive\nadmonitory\nadnate\nadnexa\nadnexal\nadnoun\nado\nadobe\nadobo\nadolescence\nadolescent\nadonic\nadonis\nadonize\nadopt\nadoptable\nadopted\nadoption\nadoptive\nadorability\nadorable\nadorably\nadoration\nadore\nadored\nadorer\nadoring\nadoringly\nadorn\nadorned\nadorned(p)\nadornment\nadown\nadrenal\nadrenalectomy\nadrenaline\nadrenarche\nadrenergic\nadrenocortical\nadrenocorticotropic\nadrenosterone\nadrenotrophin\nadriatic\nadrift\nadrift(p)\nadroit\nadroitly\nadroitness\nadrolepsy\nadscititious\nadscript\nadscriptus\nadsorbable\nadsorbate\nadsorbed\nadsorbent\nadsorption\nadulation\nadulator\nadulatory\nadullam\nadult\nadulterant\nadulterate\nadulterated\nadulterating\nadulteration\nadulterer\nadulteress\nadulterine\nadulterous\nadulterously\nadultery\nadulthood\nadultism\nadultness\nadultress\nadumbrate\nadumbration\nadumbrative\naduncated\naduncity\naduncous\nadust\nadustion\nadv\nadvance\nadvance(a)\nadvanced\nadvanced(a)\nadvancement\nadvances\nadvancing\nadvantage\nadvantaged\nadvantageous\nadvection\nadvective\nadvene\nadvent\nadventism\nadventist\nadventitial\nadventitious\nadventive\nadventure\nadventurer\nadventures\nadventuress\nadventurism\nadventuristic\nadventurous\nadventurousness\nadverb\nadverbial\nadverbially\nadverbs\nadversaria\nadversary\nadversarys\nadversative\nadverse\nadversely\nadversis\nadversity\nadversitys\nadversum\nadvert\nadvertence\nadvertency\nadvertent\nadvertise\nadvertised\nadvertisement\nadvertiser\nadvertising\nadvice\nadvisability\nadvisable\nadvise\nadvised\nadvisedly\nadvisee\nadvisemement\nadviser\nadvisory\nadvocacy\nadvocate\nadvocation\nadvoutress\nadvoutry\nadvowson\nadynamia\nadynamic\nadynamy\nadytum\nadz\nadze\nadzooks\naecial\naeciospore\naecium\naedes\naedile\naedipus\naeequa\naegean\naegiceras\naegilops\naegina\naegis\naegospotami\naegri\naegypiidae\naegypius\naegyptopithecus\naeneas\naeneid\naeneus\naeolian\naeolic\naeolis\naeolotropic\naeolus\naeon\naeonium\naepyceros\naepyornidae\naepyorniformes\naequa\naequam\naequat\naequis\naequo\naerated\naeration\naerator\naere\naerial\naerialist\naerially\naerides\naerie\naeriferous\naerifiction\naeriform\naerobacter\naerobe\naerobic\naerobics\naerobiosis\naerobiotic\naerodontalgia\naerodrome\naerodynamic\naerodynamics\naerography\naerolite\naerological\naerology\naerolytic\naeromancy\naeromechanic\naeromechanics\naeromedical\naeromedicine\naerometer\naerometry\naeronatics\naeronaut\naeronautic\naeronautical\naeronautics\naerophagia\naerophilatelic\naerophilately\naeroplane\naeroplanist\naeroscope\naeroscopy\naerosol\naerosolized\naerospace\naerosphere\naerostat\naerostatic\naerostatics\naerostation\naertex\naes\naeschylean\naeschylus\naeschynanthus\naesculapian\naesculapius\naesculus\naesir\naesop\naestas\naesthetic\naesthetically\naesthetics\naestival\naetas\naeterna\naeternum\naether\naethionema\naethusa\naetiology\naetobatus\naevi\nafar\nafeard\nafeard(p)\nafebrile\naffability\naffable\naffably\naffair\naffaire\naffaires\naffairs\naffect\naffectation\naffected\naffected(p)\naffectedly\naffectedness\naffectibility\naffecting\naffectingly\naffection\naffectional\naffectionate\naffectionateness\naffectioned\naffections\naffector\naffects\naffenpinscher\nafferent\naffettuoso\naffiance\naffianced\naffiche\nafficher\naffidation\naffidavit\naffiliated\naffiliation\naffinal\naffined\naffinity\naffirm\naffirmable\naffirmance\naffirmation\naffirmative\naffirmatively\naffirmativeness\naffix\naffixal\naffixation\naffixed\nafflation\nafflatus\nafflict\nafflicted\nafflicting\naffliction\nafflictions\nafflictive\naffluence\naffluent\nafflux\naffluxion\nafford\nafforestation\naffraid\naffranchise\naffranchisement\naffray\naffrayment\naffricate\naffrication\naffriction\naffright\naffrightment\naffront\naffronted\naffronterai\naffuse\naffusion\nafghan\nafghani\nafghanistan\nafibrinogenemia\nafield\nafire\naflak\naflame(p)\naflare\nafloat\nafloat(p)\naflutter\nafoot\nafoot(p)\nafore\naforehand\naforementioned\naforenamed\naforesaid\naforesaid(a)\naforethought\naforethought(ip)\nafoul\nafoul(ip)\nafraid\nafraid(p)\naframomum\nafreet\nafresh\nafric\nafrica\nafrican\nafrican-american\nafricander\nafrikaans\nafrikaner\nafro\nafro-asian\nafro-wig\nafroasiatic\nafrocarpus\nafropavo\naft\naft(a)\nafter\nafter(a)\nafter-hours\nafter-school(a)\nafter-shave\nafterage\nafterbirth\nafterburden\nafterburner\naftercare\nafterclap\naftercome\naftercourse\naftercrop\nafterdamp\nafterdeck\nafterdinner\naftereffect\naftergame\nafterglow\naftergrowth\nafterimage\nafterlife\naftermath\naftermost\nafternoon\nafternoon(a)\nafterpains\nafterpart\nafterpiece\naftershaft\naftershafted\naftershock\naftertaste\nafterthought\nafterwards\nafterworld\naga\nagacerie\nagain\nagains\nagainst\nagalactia\nagalloch\nagallochium\nagama\nagamemnon\nagamic\nagamid\nagamidae\nagamist\nagammaglobulinemia\nagapanthus\nagape\nagape(p)\nagapemone\nagapornis\nagar\nagaric\nagaricaceae\nagaricales\nagaricus\nagas\nagastache\nagate\nagateware\nagathis\nagavaceae\nagave\nagaze\nagdistis\nage\nage-old\naged\naged(a)\nagedness\nageism\nagelaius\nageless\nagelessness\nagelong\nagency\nagenda\nagendum\nagenesis\nagent\nagential\nagentive\nagents\nagentship\nagerasia\nageratina\nageratum\nages\nagglomerate\nagglomeration\nagglutinate\nagglutination\nagglutinative\nagglutinin\nagglutinogen\naggrandize\naggrandizement\naggravable\naggravate\naggravated\naggravating\naggravatingly\naggravation\naggregate\naggregation\naggression\naggressive\naggressively\naggressiveness\naggressor\naggrieve\naggrieved\naggro\naggroup\naghan\naghast\naghast(p)\nagianst\nagile\nagilely\nagility\nagincourt\naging\nagio\nagiotage\nagir\nagis\nagitate\nagitated\nagitation\nagitative\nagitator\nagitur\nagjus\nagkistrodon\naglaomorpha\naglaonema\nagleam\naglet\naglitter(p)\naglow\naglow(p)\nagnate\nagnatha\nagnation\nagni\nagnition\nagnize\nagnomen\nagnosia\nagnostic\nagnosticism\nagnus\nago\nagog\nagoing\nagonadal\nagonal\nagonidae\nagonies\nagonism\nagonist\nagonistic\nagonize\nagonized\nagonizing\nagonizingly\nagonus\nagony\nagora\nagoraphobia\nagoraphobic\nagostadero\nagouti\nagranulocytic\nagranulocytosis\nagrapha\nagraphia\nagraphic\nagrarian\nagree\nagreeable\nagreeableness\nagreeably\nagreed\nagreeing\nagreeing(a)\nagreement\nagrescit\nagrestic\nagribusiness\nagricultor\nagricultural\nagriculture\nagriculturist\nagrimonia\nagriocharis\nagrippa\nagrobacterium\nagrobiologic\nagrobiology\nagrologic\nagrology\nagromania\nagronomic\nagronomist\nagronomy\nagropyron\nagrostemma\nagrostis\naground\naground(p)\nagrypnia\nagrypnotic\nagua\naguardiente\nague\naguets\nagueweed\naguish\nagural\nagurial\nah\naha\nahab\nahariolation\nahead\nahead(p)\nahorse\nahorse(p)\nahriman\nahuehuete\nahura\naid\naidance\naide\naide-memoire\naidedecamp\naidetoi\naiding\naidless\naids\naigrette\naiguille\naigulet\naikido\nail\nailanthus\naile\naileron\nailing\nailment\nailurophobia\nailuropoda\nailuropodidae\nailurus\naim\naimer\naimless\naimlessly\naimlessness\naingenium\naioli\nair\nair(a)\nair-conditioned\nair-conditioner\nair-cooled\nair-intake\nair-to-air\nair-to-surface\nairborne\nairbrake\nairbrush\nairbubble\nairbuilt\nairbus\naircraft\naircraftsman\naircrew\naircrewman\nairdock\naire\naired\nairedale\nairfield\nairflow\nairfoil\nairframe\nairheaded\nairhole\nairiness\nairing\nairless\nairlift\nairline\nairliner\nairlock\nairmail\nairman\nairmanship\nairpipe\nairplane\nairport\nairs\nairship\nairsick\nairspace\nairspeed\nairstream\nairstrip\nairtight\nairwind\nairworthiness\nairworthy\nairy\naise\naisle\nait\naitch\naitchbone\naiunt\naix\naizoaceae\najaia\najar\najar(p)\najax\najee\najuga\najutage\nakan\nakaryocyte\nakee\nakeridae\nakimbo\nakimbo(ip)\nakin\nakin(p)\nakinesis\nakkadian\nakron\nakwa'ala\nal\nala\nalabama\nalabaman\nalabaster\nalack\nalacran\nalacritous\nalacrity\nalacritywant\naladdin\nalalia\nalameda\nalamo\nalanine\nalar\nalarm\nalarmed\nalarming\nalarmingly\nalarmism\nalarmist\nalarum\nalas\nalaska\nalaskan\nalate\nalated\nalauda\nalaudidae\nalaw\nalb\nalba\nalbacore\nalbania\nalbanian\nalbany\nalbata\nalbatrellus\nalbatross\nalbedo\nalbeit\nalberca\nalbert\nalberta\nalbetur\nalbification\nalbinal\nalbinism\nalbino\nalbite\nalbitic\nalbizzia\nalbuca\nalbuginaceae\nalbugo\nalbula\nalbulidae\nalbum\nalbumen\nalbumin\nalbuminous\nalbuminuria\nalbuminuric\nalbuquerque\nalbuterol\nalca\nalcaeus\nalcaic\nalcaid\nalcalde\nalcazar\nalcea\nalcedinidae\nalcedo\nalcelaphus\nalces\nalchemic\nalchemist\nalchemistic\nalchemy\nalcidae\nalcohol\nalcoholic\nalcoholism\nalcoran\nalcove\nalcyonacea\nalcyonaria\naldebaran\naldehyde\naldehydic\nalder\nalderfly\nalderman\naldermanic\naldol\naldose\naldosterone\naldosteronism\naldrovanda\nale\nalea\naleatory\nalectis\nalecto\nalectoria\nalectoris\nalectoromancy\nalectryomancy\nalectura\nalee\nalehouse\nalembic\nalentours\naleph\naleph-null\nalepisaurus\naleppo\nalert\nalertly\nalertness\nalerts\naletris\naleurites\naleuromancy\naleurone\naleuronic\naleut\nalewife\nalex\nalexander\nalexandria\nalexandrian\nalexandrine\nalexandrite\nalexic\nalexipharmic\nalexiteric\naleyrodes\naleyrodidae\nalfalfa\nalfardaws\nalfilaria\nalfresco\nalga\nalgal\nalgarroba\nalgebra\nalgebraic\nalgebraically\nalgebraist\nalgebraize\nalgeria\nalgerian\nalgeripithecus\nalget\nalgid\nalgiers\nalgin\nalgoid\nalgol\nalgolagnic\nalgology\nalgometer\nalgometric\nalgometry\nalgonkian\nalgonquian\nalgophobia\nalgophobic\nalgorism\nalgorithm\nalgorithmic\nalguazil\nalia\nalias\nalibi\nalid\nalien\nalienable\nalienage\nalienate\nalienated\nalienating\nalienation\naliene\nalieni\nalienism\nalienist\nalieno\nalienum\nalight\nalign\naligned\naligning\nalignment\naliis\nalike\nalike(p)\naliment\nalimentary\nalimentation\nalimentative\nalimony\naliner\naliped\naliphatic\naliquant\naliquid\naliquis\naliquot\nalisma\nalismataceae\nalismatidae\naliter\naliterate\nalitur\nalive\nalive(p)\nalizarin\naljibar\nalka-seltzer\nalkahest\nalkahestic\nalkalemia\nalkalescent\nalkali\nalkalimetry\nalkaline\nalkaline-loving\nalkalinity\nalkalinuria\nalkaloid\nalkaloidal\nalkalosis\nalkalotic\nalkene\nalkyd\nalkyl\nalkylbenzenesulfonate\nalkylic\nall\nall(a)\nall-around(a)\nall-devouring(a)\nall-fired\nall-important(a)\nall-knowing\nall-mains(a)\nall-out\nall-rounder\nall-time\nall-victorious\nall-weather\nalla\nallabsorbing\nallah\nallamanda\nallantoic\nallantois\nallargando\nallars\nallay\nalldestroying\nalldevouring\nallectation\nallective\nallegation\nallege\nallegeable\nalleged\nalleged(a)\nallegedly\nalleghenies\nallegiance\nallegiant\nallegorical\nallegorically\nallegorize\nallegory\nallegresse\nallegretto\nallegro\nallele\nallelic\nallelujah\nallemande\nallengulfing\naller\nallergen\nallergenic\nallergic\nallergist\nallergology\nallergy\nalleviate\nalleviated\nalleviation\nalleviative\nalley\nallezvousen\nallfours\nallhallowmas\nallhallows\nallhallowtide\nallholy\nalliaceae\nalliaceous\nalliance\nalliaria\nallied\nallies\nalligation\nalligator\nalligatored\nalligatorfish\nalligatoridae\nallign\nalligned\nallignment\nallionia\nalliteration\nalliterative\nalliteratively\nallium\nallknowing\nallmerciful\nallness\nallo\nallocable\nallocation\nallochronic\nallochthonous\nallocution\nallodial\nallodium\nallogamous\nallogamy\nallograph\nallographic\nallomerism\nallomerous\nallometric\nallometry\nallomorph\nallomorphic\nallones\nallopathic\nallopathy\nallopatric\nallophone\nallophonic\nallopurinol\nalloquy\nallosaur\nallot\nallotment\nallotrope\nallotropic\nallotropy\nallotted\nallover\nallow\nallowable\nallowance\nallowances\nallowed\nalloy\nalloyage\nalloyed\nallpowerful\nalls\nallseeing\nallspice\nallude\nallure\nallurement\nalluring\nallusion\nallusive\nallusiveness\nalluvial\nalluvion\nalluvium\nallwise\nally\nallyl\nallylic\nalma\nalma-ata\nalmanac\nalmanach\nalmanack\nalmandine\nalmandite\nalmightiness\nalmighty\nalmond\nalmond-eyed\nalmond-shaped\nalmoner\nalmost\nalms\nalmsgiver\nalmsgiving\nalmshouse\nalmsman\nalnaschar\nalnashar\nalnico\nalnus\nalocasia\naloe\naloeaceae\naloes\naloft\nalogy\naloha\nalone\nalone(p)\naloneness\nalong\nalongside\naloof\naloofness\nalopecia\nalopecic\nalopecurus\nalopex\nalopiidae\nalopius\nalosa\nalouatta\naloud\nalp\nalpaca\nalpenstock\nalpestrine\nalpha\nalphabet\nalphabetarian\nalphabetic\nalphabetical\nalphabetically\nalphabetization\nalphabetized\nalphanumeric\nalphanumerics\nalphitomancy\nalpine\nalpinia\nalpinist\nalprazolam\nalps\nalready\nalright\nals\nalsace\nalsatia\nalsatian\nalso\nalsobia\nalsophila\nalsphaltite\nalstonia\nalstroemeria\nalstroemeriaceae\nalt\nalta\naltaic\naltar\naltarpiece\naltarstairs\naltazimuth\nalter\naltera\nalterability\nalterable\nalteram\nalterant\nalteration\nalterative\naltercation\nalterd\naltered\naltergerman\nalteri\nalterius\nalternanthera\nalternate\nalternate(a)\nalternately\nalternateness\nalternating\nalternation\nalternative\nalternatively\nalternativeness\nalternator\nalternity\nalterum\nalthea\nalthorn\nalthough\naltiloquence\naltiloquent\naltimeter\naltimetry\naltissimo\naltitude\naltitudinal\naltitudinous\nalto\nalto-relievo\naltocumulus\naltogeher\naltogether\naltorilievo\naltorivievo\naltostratus\naltricial\naltruism\naltruist\naltruistic\naltruistically\nalula\nalular\nalum\nalumina\naluminous\naluminum\nalumnus\nalumroot\nalundum\nalveary\nalveolar\nalveolate\nalveolitis\nalveolus\nalvine\nalways\nalyssum\nalytes\nam\nama\namability\namadou\namaethon\namah\namain\namalgam\namalgamate\namalgamated\namalgamation\namalgamative\namalhaea\namanita\namantes\namantium\namanuensis\namaranth\namaranthaceae\namaranthine\namaranthus\namarelle\namari\namaritude\namaryllidaceae\namaryllis\namasius\namass\namassed\namastia\namaterasu\namateur\namateurish\namateurishly\namateurishness\namateurism\namative\namatory\namauropelta\namaurosis\namaurotic\namaze\namazed\namazedness\namazement\namazing\namazingly\namazon\namazona\nambages\nambagious\nambassador\nambassadorial\nambassadorship\nambassadress\namber\namberboa\nambercolored\nambergris\namberjack\nambiance\nambidexter\nambidexterity\nambidextral\nambidextrous\nambidextrousness\nambient\nambigu\nambiguas\nambiguity\nambiguous\nambiguously\nambilogy\nambiloquy\nambit\nambition\nambitious\nambitiously\nambivalence\nambivalent\nambiversion\nambiversive\namble\nambloplites\namblygonite\namblyopia\namblyopic\namblyrhynchus\nambo\namboyna\nambrosia\nambrosiaceae\nambrosial\nambulacral\nambulacrum\nambulance\nambulant\nambulation\nambulatory\nambuscade\nambush\nambustion\nambystoma\nambystomatidae\nambystomid\name\nameba\namebiasis\nameboid\namedeboue\nameer\nameiuridae\nameiurus\namelanchier\namelia\nameliorate\nameliorating(a)\namelioration\nameloblast\namelogenesis\namen\namen-ra\namenability\namenable\namend\namendable\namendatory\namende\namended\namendment\namends\namenity\namenorrhea\namenorrheic\namentes\namentia\namentiferae\namentiferous\namerce\namercement\namerciable\namerge\namerica\namericaine\namerican\namericana\namericanism\namericanization\namericanize\namericium\namerind\namerindian\nametabolic\namethyst\namethystine\nametria\nametropia\nametropic\namhara\namharic\namia\namiability\namiable\namianth\namianthum\namianthus\namicability\namicable\namicably\namical\namice\namici\namicitia\namicitias\namicos\namicus\namid\namide\namidship\namidships\namidst\namie\namigo\namigos\namiidae\namine\namino\naminoaciduria\naminomethane\naminophylline\naminopyrine\namiodarone\namis\namish\namiss\namiss(p)\namitosis\namitotic\namitriptyline\namity\namman\nammeter\nammine\nammino\nammobium\nammodytes\nammodytidae\nammonia\nammoniac\nammoniacal\nammoniated\nammonification\nammonite\nammonitic\nammonium\nammoniuria\nammotragus\nammunition\namnesia\namnesic\namnestic\namnesty\namniocentesis\namnion\namniota\namniote\namniotic\namnis\namobarbital\namoebean\namoebeaum\namoebeeic\namoebic\namoebida\namok\namole\namong\namongst\namontillado\namor\namoral\namoralism\namoralist\namore\namoret\namorette\namorist\namoristic\namoroso\namorous\namorously\namorousness\namorpha\namorphism\namorphophallus\namorphous\namort\namortization\namotion\namoto\namount\namounts\namour\namourette\namoxicillin\namperage\nampere\nampere-hour\nampere-minute\nampere-turn\nampersand\namphetamine\namphibia\namphibian\namphibiotic\namphibious\namphibole\namphibolips\namphibolite\namphibology\namphibolous\namphiboly\namphibrach\namphicarpaea\namphictyonic\namphictyony\namphidiploid\namphigory\namphigouri\namphimixis\namphineura\namphioxidae\namphipod\namphipoda\namphiprion\namphiprostylar\namphisbaena\namphisbaenidae\namphistylar\namphitheater\namphitheatric\namphitropous\namphitryon\namphiuma\namphiumidae\namphora\namphoric\namphoteric\namphotericin\nampicillin\nample\nampleness\nampliation\namplification\namplifier\namplify\namplitude\namplitudinous\namply\nampulla\nampullar\namputate\namputation\namputee\namrinone\namsinckia\namsonia\namsterdam\namtusement\namuck\namulet\namur\namusare\namuse\namused\namusement\namusing\namusingly\namussim\namygdala\namygdalaceae\namygdaline\namygdalus\namyl\namylaceous\namylase\namyloid\namyloidosis\namylolysis\namylolytic\namyotrophia\namyxia\nan\nana\nanabaptism\nanabaptist\nanabatic\nanabiosis\nanabiotic\nanabolic\nanabolism\nanabrus\nanacanthini\nanacardiaceae\nanacardium\nanachronic\nanachronism\nanachronistically\nanachronize\nanaclinal\nanaclisis\nanaclitic\nanacoluthia\nanacoluthic\nanacoluthon\nanaconda\nanacreontic\nanacrusis\nanacyclus\nanadenanthera\nanadiplosis\nanadromous\nanaemia\nanaerobe\nanaerobic\nanaesthesia\nanaesthetic\nanaesthetize\nanagallis\nanagasta\nanaglyph\nanaglyphic\nanaglyphy\nanaglyptic\nanagoge\nanagogic\nanagogical\nanagram\nanagrammatic\nanagrammatism\nanagrams\nanagyris\nanal\nanalbuminemia\nanalecta\nanalectic\nanalects\nanaleptic\nanalgesia\nanalgesic\nanalog\nanalogical\nanalogicalness\nanalogist\nanalogous\nanalogously\nanalogue\nanalogy\nanalphabetic\nanalysand\nanalysis\nanalyst\nanalytic\nanalytical\nanalytically\nanalyticity\nanalyzable\nanalyze\nanalyzed\nanalyzer\nanamnestic\nanamorphic\nanamorphism\nanamorphosis\nananas\nananias\nanapest\nanapestic\nanaphalis\nanaphase\nanaphasic\nanaphor\nanaphora\nanaphoric\nanaphrodisia\nanaphrodisiac\nanaphylactic\nanaphylaxis\nanaplasia\nanaplasmosis\nanaplastic\nanapsid\nanapsida\nanarchic\nanarchical\nanarchically\nanarchism\nanarchist\nanarchistic\nanarchy\nanarhichadidae\nanarhichas\nanas\nanasa\nanasarca\nanasarcous\nanaspid\nanaspida\nanastalsis\nanastatic\nanastatica\nanastigmat\nanastigmatic\nanastomose\nanastomosis\nanastomotic\nanastomus\nanastrophe\nanastrophy\nanathema\nanathematize\nanatidae\nanatolian\nanatomic\nanatomical\nanatomically\nanatomist\nanatomize\nanatomy\nanatotitan\nanatriptic\nanatropous\nance\nancestor\nancestors\nancestral\nancestress\nancestry\nanchor\nanchora\nanchorage\nanchored\nanchoret\nanchorite\nanchoritic\nanchovy\nanchusa\nancien\nancient\nanciently\nancientness\nancillary\nancohuma\nancora\nancylidae\nancylostomatidae\nancylus\nand\nandalusia\nandalusian\nandante\nandantino\nandean\nandersen\nandes\nandesite\nandheaven\nandira\nandiron\nandorra\nandorran\nandosite\nandradite\nandreaea\nandreaeales\nandrena\nandrenidae\nandrew\nandricus\nandrogen\nandrogenesis\nandrogenetic\nandrogenic\nandrogenous\nandroglossia\nandrogynal\nandrogynous\nandrogyny\nandroid\nandromeda\nandronicus\nandrophobia\nandropogon\nandrosterone\nandryala\nanecdotal\nanecdote\nanecdotic\nanecdotist\nanechoic\naneides\nanele\nanemia\nanemic\nanemographic\nanemography\nanemometer\nanemometric\nanemometry\nanemone\nanemonella\nanemophilous\nanemopsis\nanemoscope\nanencephalic\nanencephaly\nanent\nanergy\naneroid\nanerythmon\nanesthesia\nanesthesiologist\nanesthesiology\nanesthetic\nanesthetic(a)\nanesthetized\nanesthyl\nanestrous\nanestrus\nanethum\naneuploid\naneuploidy\naneurysm\naneurysmal\nanew\nanfang\nanfractuosity\nanfractuous\nangas\nangel\nangelfish\nangelic\nangelica\nangelically\nangelicanism\nangelim\nangels\nangelus\nanger\nangered\nangevin\nangiitis\nangina\nanginal\nangiocardiogram\nangiocarp\nangiocarpic\nangiogram\nangiography\nangiohemophilia\nangiologist\nangiology\nangioma\nangiomatous\nangiopathy\nangioplasty\nangiopteris\nangiosperm\nangiospermae\nangiospermous\nangiotelectasia\nangiotensin\nanglais\nanglaise\nangle\nangled\nangledozer\nangler\nangles\nanglewing\nangliae\nanglian\nanglican\nanglicanism\nanglice\nanglicism\nanglicize\nangling\nanglo-american\nanglo-catholic\nanglo-catholicism\nanglo-french\nanglo-indian\nanglo-jewish\nanglo-saxon\nanglomania\nanglophile\nanglophilia\nanglophilic\nanglophobe\nanglophobia\nanglophobic\nangola\nangolan\nangolese\nangora\nangrecum\nangrily\nangry\nangst\nangstrom\nanguidae\nanguill\nanguilla\nanguillan\nanguillidae\nanguilliform\nanguilliformes\nanguillula\nanguine\nanguis\nanguish\nanguished\nangular\nangularity\nangularness\nangulate\nangulation\nangusta\nangustation\nangwantibo\nanhedonia\nanhelation\nanhelose\nanhidrosis\nanhima\nanhimidae\nanhingidae\nanhydride\nanhydrous\nani\nanicon\nanicteric\nanigozanthus\nanil\nanile\naniline\nanility\nanima\nanimadversion\nanimadvert\nanimal\nanimal(a)\nanimalcule\nanimalia\nanimalism\nanimalistic\nanimality\nanimalization\nanimallike\nanimalness\nanimals\nanimam\nanimastic\nanimate\nanimated\nanimatedly\nanimateness\nanimating\nanimation\nanimatism\nanimatistic\nanimative\nanimatronics\nanime\nanimi\nanimis\nanimism\nanimist\nanimo\nanimos\nanimosity\nanimus\nanion\nanionic\nanise\naniseikonia\naniseikonic\nanisette\nanisogamete\nanisogametic\nanisogamic\nanisogamy\nanisometric\nanisometropia\nanisometropic\nanisoptera\nanisotremus\nanisotropic\nanjou\nankara\nankel\nankle\nankle-deep\nanklebone\nanklet\nankus\nankylosaur\nankylosis\nankylotic\nanna\nannalist\nannalistic\nannals\nannapolis\nannapurna\nannatto\nanneal\nannealed\nannealing\nannelid\nannelida\nannex\nannexation\nannexational\nannexationist\nannexe\nannexion\nannexment\nanni\nanniellidae\nannihilate\nannihilated\nannihilating\nannihilation\nannihilative\nannihilator\nannis\nanniversary\nanno\nannona\nannonaceae\nannotate\nannotation\nannotator\nannotto\nannounce\nannounced\nannouncement\nannouncer\nannoy\nannoyance\nannoyed\nannoying\nannoyingly\nannual\nannually\nannuitant\nannuity\nannul\nannular\nannulet\nannulment\nannulus\nannum\nannunciate\nannunciation\nannunciator\nannunciatory\nannus\nannwfn\nanoa\nanobiidae\nanode\nanodic\nanodonta\nanodyne\nanoectochilus\nanogramma\nanoint\nanointed\nanointing\nanointment\nanolis\nanomala\nanomalist\nanomalistic\nanomalopidae\nanomalops\nanomalopteryx\nanomalous\nanomalously\nanomalousness\nanomaly\nanomia\nanomie\nanomiidae\nanon\nanonymity\nanonymous\nanonymously\nanoperineal\nanopheles\nanopheline\nanopia\nanoplura\nanorchism\nanorectal\nanorectic\nanorexia\nanorexic\nanorexy\nanorgasmia\nanorthite\nanorthitic\nanorthopia\nanosmia\nanosmic\nanostraca\nanother\nanother(a)\nanothers\nanounce\nanovulation\nanoxemia\nanoxemic\nanoxia\nanoxic\nanquis\nanser\nanseres\nanseriformes\nanserinae\nanserine\nanshar\nanswer\nanswerable\nanswering\nanswers\nant\nantacid\nantaeus\nantagonism\nantagonist\nantagonistic\nantagonistically\nantagonize\nantagonizing\nantananarivo\nantapex\nantaphrodisiac\nantarctic\nantarctica\nantares\nantbird\nante\nanteater\nantebellum\nantecedence\nantecedency\nantecedent\nantecubital\nantedate\nantediluvian\nantedon\nantedonidae\nantefix\nantelope\nantemeridian\nantemortem\nantemundane\nantenna\nantennal\nantennaria\nantennariidae\nantepartum\nantepast\nantepenult\nantepenultimate\nanteposition\nanterior\nanteriority\nanteriorly\nanterograde\nanteroom\nantevert\nanthelion\nanthelmintic\nanthem\nanthemion\nanthemis\nanther\nantheraea\nantheral\nanthericum\nantheridiophore\nantheridium\nantheropeas\nantherozoid\nanthidium\nanthill\nanthoceropsida\nanthoceros\nanthocerotaceae\nanthocerotales\nanthologist\nanthology\nanthonomus\nanthonys\nanthophagous\nanthophyllite\nanthozoa\nanthozoan\nanthracite\nanthracitic\nanthracosis\nanthrax\nanthriscus\nanthropic\nanthropocentric\nanthropocentrism\nanthropogenesis\nanthropogenetic\nanthropogeny\nanthropography\nanthropoid\nanthropoidea\nanthropolatry\nanthropological\nanthropologist\nanthropology\nanthropomancy\nanthropometric\nanthropometry\nanthropomorphic\nanthropomorphism\nanthropophagist\nanthropophagous\nanthropophagus\nanthroposcopy\nanthroposophy\nanthurium\nanthus\nanthyllis\nanti\nanti-catholicism\nanti-inflammatory\nanti-intellectual\nanti-semite\nanti-semitic\nanti-semitism\nantiadrenergic\nantiaircraft\nantiarrhythmic\nantiauthoritarian\nantibacterial\nantibaryon\nantibiotic\nantibody\nantic\nanticancer\nanticatalyst\nantichambre\nanticholinergic\nantichrist\nantichristian\nantichristianity\nantichronism\nanticipant\nanticipate\nanticipated\nanticipation\nanticipatory\nanticlimactic\nanticlimax\nanticlinal\nanticoagulant\nanticoagulation\nanticoagulative\nanticonvulsant\nanticyclone\nanticyclonic\nantidepressant\nantidiabetic\nantidiarrheal\nantidiuretic\nantido\nantidorcas\nantidotal\nantidote\nantidromic\nantiemetic\nantifebile\nantifebrile\nantifeminist\nantiferromagnetic\nantiferromagnetism\nantiflatulent\nantifouling\nantifreeze\nantifungal\nantigen\nantigenic\nantigone\nantigonia\nantigonus\nantigram\nantigropelos\nantigua\nantiguan\nantihero\nantihistamine\nantihypertensive\nantiinflammatory\nantiknock\nantilepton\nantilles\nantilocapra\nantilocapridae\nantilogarithm\nantilogy\nantilope\nantiluetic\nantimacassar\nantimagnetic\nantimalarial\nantimeson\nantimetabolite\nantimicrobial\nantimonial\nantimonic\nantimonopoly\nantimony\nantimuon\nantimycin\nantineoplastic\nantineutrino\nantineutron\nantinomasia\nantinomian\nantinomianism\nantinomy\nantionous\nantiorgastic\nantioxidant\nantiparallel\nantiparticle\nantipasto\nantipathetic\nantipathy\nantipersonnel\nantiperspirant\nantiphlogistic\nantiphon\nantiphonary\nantiphony\nantiphrasis\nantipodal\nantipode\nantipodean\nantipodes\nantipoison\nantipollution\nantiproton\nantipruritic\nantipsychotic\nantipyretic\nantiquarian\nantiquarianism\nantiquark\nantiquary\nantiquas\nantiquated\nantique\nantiqueness\nantiquity\nantiredeposition\nantirrhinum\nantiscriptural\nantiseptic\nantiserum\nantisocial\nantispasmodic\nantispast\nantistrophe\nantistrophic\nantisubmarine\nantisyphilitic\nantitank\nantitauon\nantithesis\nantithetic\nantithetical\nantithetically\nantithyroid\nantitoxic\nantitoxin\nantitrades\nantitussive\nantitype\nantitypic\nantivenin\nantiviral\nantler\nantlered\nantofagasta\nantonomasia\nantony\nantonym\nantonymous\nantonymy\nantrorse\nantrozous\nantrum\nantum\nantwerp\nanu\nanubis\nanunnaki\nanuran\nanuresis\nanuretic\nanurous\nanus\nanvil\nanxiety\nanxiolytic\nanxious\nanxious(p)\nanxiousbench\nanxiously\nanxiousness\nanxiousseat\nany\nany(a)\nanybody\nanyhow\nanymore\nanything\nanywhere\naorist\naoristic\naoritis\naorta\naortal\naotus\naoudad\napace\napache\napadana\napalachicola\napar\naparejo\napart\napart(p)\napartheid\napartment\napartments\napathetic\napathetically\napathy\napatite\napatosaur\napatura\napaulette\napc\nape\nape-man\napella\napelles\napennines\napercu\naperea\napergu\naperient\naperiodic\naperit\naperitif\napertion\napertness\naperture\napery\napes\napetalous\napex\naphaeresis\naphaeretic\naphagia\naphakia\naphakic\naphanite\naphanitic\naphasia\naphasic\naphasmidia\naphelion\napheresis\naphesis\naphetic\naphid\naphididae\naphidoidea\naphis\naphonia\naphonic\naphonous\naphony\naphorism\naphorist\naphoristic\naphotic\naphriza\naphrodisia\naphrodisiac\naphrodite\naphrophora\naphyllanthaceae\naphyllanthes\naphyllophorales\naphyllous\napia\napian\napiarian\napiarist\napiary\napical\napicius\napiculate\napiculated\napicultural\napidae\napie\napiece\naping\napios\napis\napish\napishamore\napium\napivorous\naplacental\naplanatic\naplasia\naplectrum\naplite\naplitic\naplodontia\naplodontiidae\naplomb\naplysia\naplysiidae\napnea\napneic\napoapsis\napocalypse\napocalyptic\napocarpous\napochromatic\napocope\napocrine\napocrypha\napocryphal\napocynaceae\napocynaceous\napocynum\napodal\napodeictic\napodeictical\napodeixis\napodeme\napodemus\napodictic\napodidae\napodiformes\napodixis\napoenzyme\napogamic\napogamy\napogean\napogee\napogon\napogonidae\napograph\napoidea\napojove\napolemia\napolitical\napollo\napollyon\napologetic\napologetically\napologetics\napologist\napologize\napologue\napology\napomict\napomictic\napomixis\napomorphine\naponeurosis\naponeurotic\napopemptic\napophasis\napophthegm\napophyseal\napophysis\napoplectic\napoplexy\naporocactus\naposelene\naposiopesis\naposiopetic\napostacy\napostasy\napostate\napostatize\napostle\napostles\napostleship\napostolic\napostolical\napostrophe\napostrophic\napostrophize\napothecaries\napothecary\napothecial\napothecium\napothegm\napothegmatic\napotheosis\napozem\nappal\nappalachia\nappalachian\nappalachians\nappall\nappalling\nappallingly\nappaloosa\nappanage\napparatus\napparel\nappareled\napparent\napparent(a)\napparently\napparentness\napparition\napparitional\napparitor\nappeach\nappeachment\nappeal\nappealable\nappealing\nappealingly\nappear\nappearance\nappearances\nappeasable\nappease\nappeasement\nappeaser\nappeasing(a)\nappelation\nappelative\nappelidage\nappellant\nappellate\nappellation\nappellative\nappend\nappendage\nappendaged\nappendant\nappendectomy\nappendicitis\nappendicle\nappendicular\nappendicularia\nappendix\napperception\napperceptive\nappertain\nappetence\nappetency\nappetens\nappetent\nappetible\nappetite\nappetition\nappetitive\nappetitus\nappetize\nappetizer\nappetizing\nappetizingness\nappingit\napplaud\napplaudable\napplause\napple\napplecart\nappleinpie\napplejack\napples\napplesauce\napplet\napplewood\nappliance\nappliances\napplicability\napplicable\napplicant\napplication\napplicator\napplied\napplique\napply\nappoggiato\nappoggiatura\nappoint\nappointed\nappointee\nappointive\nappointment\nappointments\napportion\napportioned\napportioning\napportionment\napposite\napposition\nappositional\nappositively\nappprove\nappraisal\nappraise\nappraisement\nappraiser\nappraising(a)\nappreciable\nappreciably\nappreciate\nappreciated\nappreciation\nappreciative\nappreciatively\napprehend\napprehensible\napprehension\napprehensive\napprehensiveness\napprentice\napprentice(a)\napprenticed\napprenticeship\nappressed\nappris\napprise\napprized\nappro\napproach\napproachability\napproachable\napproaching\napprobate\napprobation\nappropinquate\nappropinquation\nappropriable\nappropriate\nappropriated\nappropriately\nappropriateness\nappropriation\nappropriative\napproval\napprove\napproved\napprovement\napproving\napprovingly\napproximate\napproximately\napproximating\napproximation\napproximative\napproximatively\nappulse\nappurtenance\nappurtenances\nappurtenant\napractic\napraxia\naprehensions\napres\napres-ski\napricot\napricotcolored\napril\napron\napropos\naprotype\naprum\napse\napsidal\napsu\napt\napt(p)\napte\naptenodytes\napteral\napterous\napterygidae\napterygiformes\naptitude\naptitudinal\naptness\napus\naqaba\naqatic\naqua\naqualung\naquamarine\naquaphobia\naquaplane\naquarium\naquarius\naquatic\naquatics\naquating\naquatint\naquatinta\naquavit\naqueduct\naqueous\naquicultural\naquifer\naquiferous\naquifoliaceae\naquila\naquiline\naquinas\naquitaine\nar\nara\narab\naraba\narabesque\narabia\narabian\narabic\narability\narabis\narabist\narable\naraceae\narachis\narachnid\narachnida\narachnoid\narado\naragon\narales\naralia\naraliaceae\naramaic\naramus\naranea\naraneae\naraneidal\narapaho\nararat\narare\narariba\nararoba\naras\naraucaria\naraucariaceae\naraujia\narawak\narawn\narbeit\narbiter\narbitrable\narbitrage\narbitrageur\narbitral\narbitrament\narbitrariness\narbitrary\narbitrate\narbitration\narbitrative\narbitrator\narbitrement\narbitrium\narbor\narboraceous\narborary\narboreal\narboreous\narborescence\narborescent\narboretum\narborical\narboriculture\narboriform\narborolatry\narborvitae\narbutus\narc\narca\narcade\narcades\narcadia\narcadian\narcadic\narcane\narcanum\narced\narcella\narcellidae\narceo\narceuthobium\narch\narch(a)\narchaebacteria\narchaeological\narchaeologist\narchaeology\narchaeopteryx\narchaeornis\narchaeornithes\narchaic\narchaism\narchaistic\narchangel\narchangelic\narchbishop\narchbishopric\narchdeacon\narchdeaconry\narchdiocesan\narchdiocese\narchducal\narchduchess\narchduchy\narchduke\narchdukedom\narchean\narchebiosis\narched\narchegenesis\narchegonial\narchegonium\narchenemy\narchenteron\narcheologist\narcheology\narcheozoic\narcher\narcherfish\narchery\narches\narchespore\narchesporial\narchetypal\narchetype\narcheus\narchiannelid\narchiannelida\narchiater\narchidiaconal\narchidiaconate\narchidiskidon\narchiepiscopacy\narchiepiscopal\narchil\narchilochus\narchine\narchipallium\narchipelagic\narchipelago\narchitect\narchitectonic\narchitectonics\narchitectural\narchitecturally\narchitecture\narchiteuthis\narchitrave\narchival\narchive\narchives\narchivist\narchlute\narchly\narchmologist\narchon\narchosargus\narchosaur\narchosauria\narchosaurian\narchpriest\narchtraitor\narcidae\narclike\narctic\narctictis\narctiid\narctiidae\narctium\narctocebus\narctocephalus\narctonyx\narctostaphylos\narctotis\narcturus\narcuate\narcuation\narcus\nardea\nardeb\nardeidae\nardennes\nardent\nardently\nardet\nardisia\nardor\narduis\narduous\narduously\narduousness\nare\narea\nareal\nareas\nareaway\nareca\narecidae\narefaction\nareflexia\narena\narenaceous\narenaria\narenarious\narenga\narenicolous\narenose\nareola\nareolar\nareometer\nareopagus\nares\narescent\narete\narethusa\naretology\nargali\nargand\nargasidae\nargent\nargentic\nargentiferous\nargentina\nargentine\nargentinian\nargentinidae\nargentinosaur\nargentite\nargentous\nargil\nargillaceous\nargillite\narginine\nargiope\nargiopidae\nargive\nargol\nargon\nargonaut\nargonauta\nargonautidae\nargos\nargosy\nargot\narguable\narguably\nargue\narguebus\narguementum\narguer\nargues\nargufy\narguing\narguit\nargument\nargumentation\nargumentative\narguments\nargumentum\nargus\nargus-eyed\narguseyed\nargusianus\nargute\nargyle\nargynnis\nargyranthemum\nargyreia\nargyrodite\nargyrotaenia\nargyroxiphium\narhat\naria\nariadne\narianism\narianist\narianrhod\narid\naridity\nariel\naries\narietation\nariete\narietta\naright\nariidae\narikara\naril\nariled\narilus\narins\nariocarpus\nariomma\nariose\narioso\naris\narisaema\narisarum\narise\narista\naristarchus\naristarchy\naristate\naristides\naristocracy\naristocrat\naristocratic\naristocratically\naristolochia\naristolochiaceae\naristolochiales\nariston\naristophanes\naristotelean\naristotelia\naristotelian\naristotelianism\naristotle\narithmancy\narithmetic\narithmetical\narithmetically\narithmetician\narius\narizona\narizonan\nark\narkansan\narkansas\narm\narma\narmada\narmadillidiidae\narmadillidium\narmadillo\narmageddon\narmagnac\narmament\narmamentarium\narmaments\narmature\narmband\narmchair\narmchair(a)\narmed\narmenia\narmenian\narmeria\narmet\narmful\narmhole\narmiger\narmigerent\narmigerous\narmillaria\narmillariella\narmillary\narming\narminian\narminianism\narminius\narmis\narmistice\narmless\narmlet\narmlike\narmoire\narmor\narmor-clad\narmoracia\narmored\narmorer\narmorial\narmory\narmpit\narmrest\narms\narmstrong\narmy\narmyworm\narnica\narno\narnoseris\naroid\naroma\naromatic\narose\naround\naround-the-clock\narousal\narouse\naroused\naroynt\narpeggio\narpent\narpents\narquebus\narquebusade\narrack\narraign\narraignment\narrange\narranged\narrangement\narranger\narrant\narrant(a)\narras\narrastra\narray\narrayed\narrear\narrears\narrectis\narrest\narrestation\narrested\narrester\narresting\narrhenatherum\narrhythmia\narrhythmic\narrier\narriere\narriero\narrish\narrival\narrive\narriving\narriving(a)\narroba\narrogance\narrogant\narrogantly\narrogate\narrondissement\narrosion\narrow\narrowhead\narrowheaded\narrowroot\narrows\narrowsmith\narrowworm\narrowy\narroyo\narrrange\nars\narsenal\narsenate\narsenic\narsenical\narsenious\narsenopyrite\narsine\narson\narsonist\nart\nartamidae\nartamus\nartem\nartemia\nartemis\nartemisia\narterial\narteriectasis\narteries\narteriogram\narteriography\narteriolar\narteriole\narteriolosclerosis\narteriosclerosis\narteriosclerotic\narteriovenous\narteritis\nartery\nartes\nartesian\nartful\nartfully\nartfulness\narthralgia\narthralgic\narthritic\narthritis\narthrocentesis\narthrogram\narthrography\narthromere\narthromeric\narthroplasty\narthropod\narthropoda\narthropodal\narthropteris\narthroscope\narthroscopy\narthrospore\narthrosporic\narthur\narthurian\nartichoke\narticle\narticled\narticles\nartics\narticular\narticulate\narticulated\narticulately\narticulation\narticulatory\narticulo\nartifact\nartifacts\nartifactual\nartifice\nartificer\nartificial\nartificiality\nartificially\nartillery\nartilleryman\nartiodactyl\nartiodactyla\nartisan\nartist\nartiste\nartistic\nartistical\nartistically\nartists\nartium\nartless\nartlessly\nartlessness\nartocarpus\nartois\narts\nartsy-craftsy\nartwork\narty\naruba\narulo\narum\narundinaceous\narundinaria\narundo\naruru\naruspex\naruspice\naruspicy\narvicola\naryan\narytenoid\nas\nasafetida\nasana\nasarabacca\nasarh\nasarum\nasbestos\nasbestosis\nascaphidae\nascaphus\nascariasis\nascaridae\nascaridia\nascaris\nascend\nascendable\nascendancy\nascendant\nascendency\nascending\nascending(a)\nascension\nascensional\nascent\nascertain\nascertainable\nascertained\nascertainment\nascetic\nascetically\nasceticism\nascidiaceae\nascidian\nascii\nascites\nascitic\nascititious\nasclepiad\nasclepiadaceae\nasclepiadaceous\nasclepias\nascocarp\nascocarpous\nascolichen\nascoma\nascomycete\nascomycetes\nascomycetous\nascomycota\nascophyllum\nascospore\nascosporic\nascot\nascribable(p)\nascribe\nascription\nascus\nasepsis\naseptic\nasexual\nasexuality\nasexually\nash\nash-blonde\nash-gray\nash-key\nash-pan\nashame\nashamed\nashamed(p)\nashamedly\nashcake\nashcan\nashcolored\nashen\nashes\nashkhabad\nashlar\nashore\nashtoreth\nashtray\nashur\nashy\nasia\nasian\nasiatic\naside\nasilidae\nasimina\nasin\nasinine\nasinorum\nasinus\nasio\nask\naskance\naskant\nasked\naskew\nasking\nasl\naslant\nasleep\nasleep(p)\naslope\nasmodeus\nasocial\nasomatous\nasp\naspalathus\nasparagaceae\nasparagine\nasparagus\naspartame\naspasia\naspect\naspects\naspectual\naspen\nasper\naspera\nasperges\naspergill\naspergillaceae\naspergillosis\naspergillus\nasperity\nasperous\nasperse\naspersion\naspersions\nasperula\nasphalt\nasphaltic\nasphaltum\naspheric\nasphodel\nasphodelaceae\nasphodeline\nasphodelus\nasphyxia\nasphyxiate\nasphyxiated\nasphyxiating\naspic\naspick\naspidelaps\naspidiotus\naspidistra\naspidophoroides\naspirant\naspirate\naspiration\naspirator\naspire\naspirin\naspiring\naspleniaceae\nasplenium\nasportation\nasquint\nass\nassafoetida\nassagai\nassai\nassail\nassailability\nassailable\nassailant\nassam\nassama\nassamese\nassassin\nassassinate\nassassinated\nassassination\nassault\nassault(a)\nassaulted\nassaultive\nassay\nassegai\nassemablage\nassemblage\nassemble\nassembled\nassembler\nassembly\nassemblyman\nassemblyroom\nassemblywoman\nassent\nassenting\nassentment\nassert\nasserted\nasserting\nassertion\nassertive\nassertively\nassertiveness\nasses\nassess\nassessable\nassessment\nassessor\nasset\nassets\nasseverate\nasseveration\nasshole\nassiduity\nassiduous\nassiduously\nassiduousness\nassify\nassign\nassignable\nassignat\nassignation\nassigned\nassignee\nassignment\nassigns\nassimilable\nassimilate\nassimilating\nassimilation\nassist\nassistance\nassistant\nassisted\nassister\nassistive\nassize\nassizes\nassociability\nassociable\nassociate\nassociate(a)\nassociated\nassociation\nassociational\nassociationism\nassociative\nassoil\nassonance\nassonant\nassort\nassorted\nassortment\nassuage\nassuagement\nassuaging\nassuasive\nassuefaction\nassuetude\nassume\nassumed\nassumiing\nassumption\nassumptive\nassur\nassurance\nassure\nassured\nassuredly\nassurgent\nassuring\nassyria\nassyrian\nassyriology\nast\nastacidae\nastacus\nastarte\nastasia\nastatic\nastatine\nasteism\naster\nasteriated\nasteridae\nasterisk\nasterisked\nasterism\nasterismal\nastern\nasternal\nasteroid\nasteroidal\nasteroidea\nasteroids\nasteroth\nasthenia\nasthenic\nasthenosphere\nasthma\nasthmatic\nastigmatic\nastigmatism\nastilbe\nastir\nastir(p)\nastomatal\nastomatous\nastonish\nastonished\nastonishing\nastonishingly\nastonishment\nastound\nastounding\nastra\nastraea\nastragal\nastragalar\nastragalus\nastrakhan\nastral\nastrantia\nastraphobia\nastray\nastreus\nastriction\nastride\nastringency\nastringent\nastringents\nastrocyte\nastrocytic\nastrodome\nastrodynamics\nastroglia\nastrolabe\nastrolatry\nastrologer\nastrological\nastrology\nastroloma\nastrometry\nastronaut\nastronautic\nastronium\nastronomer\nastronomic\nastronomically\nastronomy\nastrophysical\nastrophysicist\nastrophysics\nastrophyton\nastropogon\nastute\nastutely\nastuteness\nastylar\nasuncion\nasunder\nasunder(p)\nasura\nasvins\naswan\nasylum\nasymmetrical\nasymmetrically\nasymmetry\nasymptomatic\nasymptoptic\nasymptoptical\nasymptote\nasymptotic\nasymptotically\nasynchronism\nasynchronous\nasynclitism\nasyndetic\nasynergic\nasynergy\nasystole\nat\nat-large(ip)\nataghan\natajo\natakapa\nataractic\nataraxia\natavism\natavist\natavistic\nataxia\nataxic\nate\natelectasis\nateleiosis\nateleiotic\nateles\natelier\natenolol\natgloss\nathanasia\nathanasian\nathanasianism\nathanor\nathapaskan\natharva-veda\natheism\natheist\natheistic\natheling\nathelstan\nathena\nathenaeum\nathene\nathenian\nathens\natherinidae\natherinopsis\natherogenesis\natheroma\natheromatous\natherosclerosis\natherosclerotic\natherurus\nathetosis\nathiorhodaceae\nathirst\nathirst(p)\nathlete\nathletic\nathleticism\nathletics\nathrotaxis\nathwart\nathyrium\natilt\natkins\natlanta\natlantean\natlantes\natlantic\natlantis\natlas\natmometer\natmosphere\natmospheric\natole\natoll\natom\natomic\natomistic\natomization\natomizer\natoms\natomy\natonal\natonalistic\natonality\natonally\natone\natonement\natonic\natonicity\natony\natop\natopognosia\natque\natqui\natrabilious\natramentous\natresia\natrial\natrichornis\natrichornithidae\natrioventricular\natriplex\natrium\natrocious\natrocity\natropa\natrophic\natrophied\natrophy\natropidae\natropine\natropos\natsugewi\nattaboy\nattach\nattachable\nattache\nattached\nattachment\nattack\nattacker\nattacking\nattaghan\nattain\nattainable\nattainder\nattained\nattainment\nattainments\nattaint\nattaintment\nattainture\nattalea\nattar\nattemper\nattempered\nattempt\nattemptable\nattempted\nattend\nattendance\nattendant\nattended\nattending\nattendre\nattention\nattention-getting\nattentional\nattentions\nattentive\nattentively\nattentiveness\nattentuate\nattenuate\nattenuated\nattenuation\nattest\nattestation\nattested\nattic\nattica\natticism\natticus\nattila\nattire\nattitude\nattitudes\nattitudinarian\nattitudinize\natto\nattogram\nattollent\nattorney\nattorneyship\nattosecond\nattract\nattractability\nattractable\nattracting\nattraction\nattractive\nattractive(a)\nattractiveness\nattractivity\nattrahent\nattributable\nattribute\nattributed\nattributes\nattribution\nattributive\nattributively\nattrite\nattrited\nattrition\nattritional\nattroupement\nattune\nattuned\natypical\natypicality\natypically\nau\nauburn\nauc\nauction\nauctioneer\nauctor\naudacious\naudaciously\naudacity\naudacter\naude\nauden\naudenesque\naudibility\naudible\naudibly\naudience\naudile\naudio\naudio-lingual\naudiocassette\naudiogram\naudiology\naudiometer\naudiometric\naudiometry\naudiotape\naudiovisual\naudire\naudit\naudita\naudition\nauditor\nauditorium\nauditory\nauf\naufgehoben\naufgeschoben\naugean\naugend\nauger\naugescent\naught\naugite\naugitic\naugment\naugmentation\naugmentative\naugmented\naugmenting\naugur\naugurate\nauguration\naugure\naugurous\naugury\naugust\naugusta\naugustan\naugustine\naugustinian\naugustus\naujordhui\nauk\naukland\nauklet\naulacorhyncus\nauld\naulostomidae\naulostomus\naum\naumbry\naunt\naura\naurae\naural\naurally\naurar\naurea\naureate\naurelia\naureola\naureolaria\naureole\naureolin\naures\nauri\nauribus\nauricle\nauricomous\nauricula\nauricular\nauricularia\nauriculariaceae\nauriculariales\nauriculate\nauriferous\nauriform\nauriga\nauriparus\naurist\nauro\naurochs\naurora\nauroral\nauroroa\naurous\nauscultation\nauscultatory\nauspice\nauspices\nauspicial\nauspicious\nauspiciously\nauspiciousness\nauspicium\naussilot\naust\naustenite\naustenitic\naustere\nausterely\naustereness\nausterity\nausterlitz\naustin\naustral\naustralasia\naustralasian\naustralia\naustralian\naustralis\naustralopithecine\naustria\naustrian\naustro-asiatic\naustrocedrus\naustromancy\naustronesia\naustronesian\naustrotaxus\naut\nautacoid\nautacoidal\nautarchic\nautarkic\nautarky\nauteur\nauthentic\nauthentically\nauthenticate\nauthentication\nauthenticity\nauthor\nauthoress\nauthorial\nauthoritarian\nauthoritative\nauthoritatively\nauthoritativeness\nauthorities\nauthority\nauthorization\nauthorize\nauthorized\nauthors\nauthorship\nautism\nautistic\nauto\nauto-da-fe\nautobahn\nautobiograpby\nautobiographer\nautobiographical\nautobiography\nautobus\nautocatalysis\nautocatalytic\nautochthonal\nautochthones\nautochthonous\nautoclave\nautocracy\nautocrat\nautocratic\nautocratical\nautocratically\nautodafe\nautodidact\nautodidactic\nautoecious\nautoerotic\nautoeroticism\nautofluorescence\nautogamous\nautogamy\nautogenetic\nautogenous\nautogiro\nautograft\nautograph\nautographed\nautographic\nautography\nautoimmune\nautoimmunity\nautoloader\nautoloading(a)\nautologous\nautolycus\nautolysis\nautolytic\nautomaniac\nautomat\nautomated\nautomatic\nautomatically\nautomation\nautomatism\nautomaton\nautomeris\nautomobile\nautomotive\nautonomasia\nautonomic\nautonomous\nautonomy\nautophyte\nautopilot\nautoplagiarism\nautoplastic\nautoplasty\nautopsy\nautoptical\nautoradiograph\nautoradiographic\nautoradiography\nautoregulation\nautosexing\nautosomal\nautosome\nautostrada\nautotelic\nautotelism\nautotomic\nautotomy\nautotrophic\nautotype\nautotypic\nautre\nautumal\nautumn\nautumn(a)\nautumnal\nauvergne\naux\nauxesis\nauxetic\nauxilia\nauxiliary\nauxilio\nauxilium\nauxin\nauxinic\navadavat\navahi\navail\navailable\navails\navalanche\navaler\navalokitesvara\navant\navant-garde\navantcoureur\navantcourier\navantcourrier\navantgarde\navantpropos\navaram\navarice\navaricious\navariciously\navaritiae\navarus\navascular\navast\navatar\navaunt\nave\navec\navellan\navena\navenge\navengeance\navengement\navenger\navenging\navens\navenue\naver\naverage\naverageness\naverment\naverni\navernus\naverrhoa\naverruncate\naversation\naverse\naverseness\naversion\naversive\navert\naverti\naverting\naveruncate\naves\navesta\navestan\navi\navian\naviary\naviate\naviation\naviator\naviatrix\navibus\navicennia\navicenniaceae\navid\navidity\navidly\navifauna\navifaunal\navile\navionic\navionics\navirulent\navis\naviso\navitaminosis\navitaminotic\navo\navocado\navocation\navocational\navocet\navoid\navoidance\navoiding\navoidless\navoir\navoirdupois\navolation\navons\navorum\navouch\navouchment\navow\navowal\navowed(a)\navowedly\navulsion\navulso\navuncular\navunt\nawait\nawake\nawake(p)\nawaken\nawakened\nawakening\naward\naware\naware(p)\nawareness\naway\naway(a)\naway(p)\nawayness\nawe\naweary\nawed\naweigh\naweinspiring\naweless\nawestricken\nawestruck\nawful\nawfully\nawfulness\nawheel\nawhile\nawkward\nawkwardly\nawkwardness\nawl\nawlshaped\nawlwort\nawn\nawned\nawning\nawninged\nawnless\nawny\nawol\nawry\nax\naxenic\naxial\naxially\naxil\naxile\naxillary\naxinomancy\naxiological\naxiology\naxiom\naxiomatic\naxiomatically\naxis\naxle\naxletree\naxolemma\naxolotl\naxon\naxonal\naxseed\nay\nayah\nayapana\naye\naye-aye\nayin\nayr\nayrshire\naythya\nayudante\nazadirachta\nazadirachtin\nazalea\nazathioprine\nazerbaijan\nazerbaijani\nazide\nazido\nazidothymidine\nazimuth\nazimuthal\nazo\nazoic\nazolla\nazollaceae\nazonal\nazonic\nazores\nazote\nazotemic\nazotic\nazoturia\naztec\naztecan\naztreonam\nazure\nazygous\nazymia\nb\bte\nb\nb-girl\nb-horizon\nb-meson\nba\nbaa\nbaa-lamb\nbaal\nbaas\nbaba\nbabar\nbabassu\nbabbitting\nbabble\nbabblement\nbabbler\nbabbling\nbabe\nbabeddin\nbabel\nbabelike\nbabesiidae\nbabies\nbabinski\nbabirusa\nbabish\nbabism\nbabist\nbaboon\nbaboonish\nbabu\nbaby\nbaby(a)\nbaby-faced\nbaby-wise\nbabyhood\nbabyish\nbabylon\nbabylonia\nbabylonian\nbabyminder\nbabyrousa\nbabysitter\nbabysitting\nbaccalaureate\nbaccalaureus\nbaccarat\nbaccate\nbacchal\nbacchanal\nbacchanalia\nbacchanalian\nbacchanals\nbacchant\nbacchante\nbacchantic\nbaccharis\nbacchus\nbaccilar\nbaccivorous\nbach\nbachelor\nbachelorhood\nbachelorship\nbacillaceae\nbacillar\nbacillariophyceae\nbacillus\nbacitracin\nback\nback(a)\nback-formation\nback-geared\nback-to-back\nbackache\nbackband\nbackbench\nbackbencher\nbackbend\nbackbite\nbackbiter\nbackbiting\nbackblast\nbackboard\nbackbone\nbackcap\nbackdate\nbackdown\nbackdrop\nbacked\nbacker\nbackfield\nbackfire\nbackflow\nbackgammon\nbackground\nbackhand\nbackhand(a)\nbackhanded\nbackhoe\nbacking\nbacklash\nbackless\nbacklog\nbackmost\nbackpack\nbackplate\nbackroom\nbacksaw\nbackscratcher\nbackseat\nbackset\nbacksettler\nbackside\nbackslider\nbacksliding\nbackspin\nbackstage\nbackstair\nbackstairs\nbackstay\nbackstitch\nbackstop\nbackstroke\nbackswept\nbackswimmer\nbacksword\nbackup\nbackward\nbackwardation\nbackwardness\nbackwards\nbackwater\nbackwoods\nbackwoods(a)\nbackwoodsman\nbackyard\nbacon\nbaconian\nbacteremia\nbacteremic\nbacteria\nbacterial\nbacterially\nbactericidal\nbactericide\nbacteriochlorophyll\nbacteriological\nbacteriologist\nbacteriology\nbacteriolysis\nbacteriolytic\nbacteriophage\nbacteriophagic\nbacteriostasis\nbacteriostat\nbacteriostatic\nbacteroid\nbacteroidaceae\nbacteroidal\nbacteroides\nbaculinum\nbad\nbadaga\nbadaud\nbaddeleyite\nbade\nbadge\nbadger\nbadgering\nbadinage\nbadlands\nbadli\nbadly\nbadminton\nbadness\nbadtempered\nbadv\nbaedeker\nbaeotian\nbaeotic\nbaffle\nbaffled\nbaffling\nbag\nbagasse\nbagassosis\nbagatelle\nbagel\nbaggage\nbaggageman\nbaggala\nbaggy\nbaghdad\nbagman\nbagnio\nbagpipe\nbagpipes\nbaguet\nbah\nbahamas\nbahamian\nbahrain\nbahraini\nbaht\nbai\nbail\nbailable\nbailee\nbailey\nbailiff\nbailiffship\nbailiwick\nbailment\nbailor\nbaiomys\nbairam\nbairava\nbairdiella\nbairn\nbaisakh\nbaissee\nbait\nbaited\nbaiting\nbaiza\nbaize\nbake\nbaked\nbakehouse\nbakelite\nbaker\nbakers\nbakery\nbaking\nbaklava\nbakshish\nbaku\nbal\nbalaclava\nbalaena\nbalaeniceps\nbalaenicipitidae\nbalaenidae\nbalaenoptera\nbalaenopteridae\nbalagan\nbalais\nbalalaika\nbalance\nbalanced\nbalanidae\nbalanitis\nbalanoposthitis\nbalanus\nbalarama\nbalas\nbalata\nbalboa\nbalbriggan\nbalbucinate\nbalbutiate\nbalconied\nbalcony\nbalcostume\nbald\nbaldacchino\nbaldachin\nbaldachino\nbaldaquin\nbalder\nbalderdash\nbaldhead\nbalding\nbaldly\nbaldness\nbaldric\nbaldwin\nbale\nbaleen\nbalefire\nbaleful\nbalefully\nbali\nbalinese\nbalista\nbalister\nbalistes\nbalistidae\nbalistraria\nbalize\nbalk\nbalkans\nbalkiness\nbalking\nbalkline\nbalky\nball\nball-hawking\nball-shaped\nballad\nballade\nballast\nballcock\nballdress\nballe\nballed(a)\nballerina\nballet\nballetic\nballetomane\nballista\nballistic\nballistics\nballoon\nballoonery\nballoonfish\nballooning\nballoonist\nballot\nballota\nballotbox\nballpark\nballplayer\nballpoint\nballproof\nballroom\nballs\nballup\nbally(a)\nballyhoo\nbalm\nbalmasque\nbalmoral\nbalmy\nbalneal\nbalneation\nbalochi\nbaloney\nbalourdise\nbalsa\nbalsam\nbalsamic\nbalsaminaceae\nbalsamorhiza\nbalsamroot\nbaltic\nbaltic-finnic\nbaltimore\nbalto-slavic\nbaluster\nbalustrade\nbalzac\nbalzacian\nbam\nbamako\nbambino\nbamboo\nbamboozle\nbambusa\nbambuseae\nban\nbanal\nbanana\nbanch\nbanco\nband\nbandage\nbandaged\nbandanna\nbandbox\nbanded\nbanderilla\nbanderillero\nbanderole\nbandicoot\nbandit\nbandleader\nbandmaster\nbandog\nbandoleer\nbandolier\nbandrol\nbands\nbandsaw\nbandsman\nbandstand\nbandung\nbandurria\nbandwagon\nbandwidth\nbandy\nbane\nbaneberry\nbaneful\nbanefully\nbanewort\nbanff\nbang\nbang-up\nbanger\nbangiaceae\nbanging\nbangkok\nbangladesh\nbangladeshi\nbangle\nbangui\nbanish\nbanished\nbanishment\nbanister\nbanjapanese\nbanjo\nbank\nbankable\nbankbook\nbanker\nbankia\nbanking\nbankroll\nbankrup\nbankrupt\nbankruptcy\nbanksia\nbanlieue\nbanned\nbanner\nbanneret\nbannerlike\nbannerol\nbanners\nbanning-order\nbannister\nbannock\nbannockburn\nbanns\nbanquet\nbanquette\nbanshee\nbantam\nbantamcock\nbantamweight\nbanteng\nbanter\nbantering\nbanteringly\nbantling\nbantoid\nbantu\nbanyan\nbanzai\nbanzaijapanese\nbaobab\nbap\nbaphia\nbaptisia\nbaptism\nbaptismal\nbaptist\nbaptistery\nbaptize\nbar\nbara\nbarachois\nbaragouin\nbarb\nbarba\nbarbacan\nbarbadian\nbarbados\nbarbarea\nbarbaresque\nbarbarian\nbarbaric\nbarbarism\nbarbarity\nbarbarization\nbarbarous\nbarbarously\nbarbarousness\nbarbarus\nbarbary\nbarbasco\nbarbe\nbarbecue\nbarbecued\nbarbecuing\nbarbed\nbarbel\nbarbell\nbarber\nbarberry\nbarbershop\nbarbet\nbarbette\nbarbican\nbarbital\nbarbiturate\nbarbouillage\nbarbu\nbarbuda\nbarcarole\nbarcelona\nbard\nbarde\nbarded\nbardic\nbardolatry\nbare\nbare(a)\nbare-assed\nbare-breasted\nbareback\nbarebacked\nbarebone\nbarefaced\nbarefoot\nbarefooted\nbarehanded\nbareheaded\nbarelegged\nbarely\nbareness\nbarf\nbargain\nbargain-priced\nbargaining\nbarge\nbargee\nbargello\nbargeman\nbarghest\nbari\nbaric\nbarilla\nbaritone\nbarium\nbark\nbarkantine\nbarkbound\nbarkeeper\nbarker\nbarklouse\nbarky\nbarley\nbarley-sugar\nbarleycorn\nbarlycorn\nbarm\nbarmaid\nbarmaster\nbarmbrack\nbarmecide\nbarmote\nbarn\nbarnacle\nbarnacled\nbarnacles\nbarnburner\nbarndoor\nbarney\nbarnful\nbarnstormer\nbarnyard\nbarograph\nbarographic\nbarometer\nbarometric\nbaron\nbaronduki\nbaroness\nbaronet\nbaronetcy\nbarong\nbaronial\nbarony\nbaroque\nbaroreceptor\nbarosaur\nbaroscope\nbarouche\nbarque\nbarrack\nbarracker\nbarracoon\nbarracuda\nbarrage\nbarramunda\nbarranca\nbarranco\nbarrater\nbarrator\nbarratrous\nbarratry\nbarred\nbarrel\nbarreled\nbarrelfish\nbarrelhouse\nbarrels\nbarren\nbarrenness\nbarrenwort\nbarrette\nbarricade\nbarricaded\nbarrie\nbarrier\nbarriers\nbarring\nbarrister\nbarroom\nbarrow\nbarry\nbars\nbartender\nbarter\nbarterer\nbartlett\nbartonia\nbartramia\nbarway\nbarycenter\nbarye\nbaryon\nbaryta\nbarytic\nbarytone\nbas\nbasal\nbasalt\nbasaltic\nbasbleu\nbascule\nbase\nbase-forming\nbaseball\nbaseboard\nbaseborn\nbased\nbased(p)\nbasel\nbaselard\nbaseless\nbaseline\nbasement\nbaseminded\nbaseness\nbasenji\nbash\nbashaw\nbashbazouk\nbashful\nbashfulness\nbasic\nbasically\nbasics\nbasidial\nbasidiocarp\nbasidiolichen\nbasidiomycete\nbasidiomycetes\nbasidiomycetous\nbasidiomycota\nbasidiospore\nbasidiosporous\nbasidium\nbasifixed\nbasil\nbasilar\nbasileus\nbasilica\nbasilican\nbasilicata\nbasiliscus\nbasilisk\nbasin\nbasinal\nbasined\nbasinet\nbasipetal\nbasis\nbask\nbasket\nbasketball\nbasketry\nbasketweaver\nbasking\nbasophil\nbasophilia\nbasophilic\nbasophobia\nbasotho\nbasque\nbass\nbassariscidae\nbassariscus\nbassarisk\nbasse-normandie\nbasset\nbasseterre\nbassetting\nbassetto\nbassia\nbassine\nbassinet\nbasso\nbassoon\nbassoonist\nbassorilievo\nbasswood\nbast\nbasta\nbastard\nbastardization\nbastardized\nbastardy\nbaste\nbastille\nbastinado\nbasting\nbastion\nbastioned\nbastnasite\nbat\nbata\nbataan\nbataille\nbatch\nbatching\nbate\nbateau\nbated\nbatfish\nbath\nbathala\nbathe\nbathed\nbathetic\nbathhouse\nbathing\nbatholith\nbatholithic\nbathometer\nbathos\nbathrobe\nbathroom\nbathtub\nbathurst\nbathyal\nbathybic\nbathycolpian\nbathyergidae\nbathyergus\nbathymetric\nbathymetry\nbathyscaphe\nbathysphere\nbatidaceae\nbatik\nbating\nbatis\nbatiste\nbatman\nbaton\nbatophobia\nbatrachoididae\nbatrachomyomachia\nbatrachoseps\nbatta\nbattalia\nbattalion\nbatten\nbatter\nbattered\nbatterie\nbattering\nbattery\nbatting\nbattle\nbattle-ax\nbattle-scarred\nbattleax\nbattled\nbattledore\nbattlefield\nbattlefront\nbattleful\nbattleground\nbattlement\nbattlemented\nbattles\nbattleship\nbattlesight\nbattling\nbattologize\nbattology\nbattre\nbattue\nbatwing\nbauble\nbaud\nbaudelaire\nbauhaus\nbauhinia\nbauxite\nbauxitic\nbavardage\nbavaria\nbavarian\nbaverdage\nbavin\nbawarchikhana\nbawbee\nbawcock\nbawd\nbawdily\nbawdry\nbawdy\nbawdyhouse\nbawl\nbawling\nbawn\nbay\nbaya\nbayadere\nbayard\nbayberry\nbaygall\nbayley\nbayonet\nbayonets\nbayonne\nbayou\nbays\nbazaar\nbazein\nbazein/gr\nbazooka\nbb\nbc\nbd\nbdellium\nbe\nbeach\nbeachcomber\nbeached\nbeachhead\nbeachwear\nbeachy\nbeacon\nbeaconfire\nbead\nbeaded\nbeading\nbeadle\nbeadledom\nbeadlike\nbeadroll\nbeads\nbeadsman\nbeady\nbeady-eyed\nbeagle\nbeagling\nbeak\nbeaked\nbeaker\nbeakless\nbeaklike\nbeam\nbeam-ends\nbeaming\nbeamish\nbeams\nbeamy\nbean\nbeanbag\nbeanball\nbeanfeast\nbeanie\nbeanstalk\nbear\nbearable\nbearance\nbearberry\nbeard\nbearded\nbeardless\nbeardown(a)\nbearer\nbearing\nbearing(a)\nbearings\nbearish\nbearnaise\nbearskin\nbeast\nbeastliness\nbeastly\nbeasts\nbeat\nbeatable\nbeatae\nbeaten\nbeaten(a)\nbeati\nbeatic\nbeatific\nbeatification\nbeatified\nbeatify\nbeating\nbeatitide\nbeatitude\nbeatnik\nbeatrice\nbeats\nbeattie\nbeatus\nbeau\nbeaucatcher\nbeaucoup\nbeaugregory\nbeaujolais\nbeaumont\nbeaumonth\nbeaumontia\nbeaut\nbeauteous\nbeautician\nbeautification\nbeautified\nbeautiful\nbeautifully\nbeautify\nbeautifying\nbeautiless\nbeauty\nbeaver\nbebas\nbebop\nbec\nbecalm\nbecalmed\nbecame\nbecause\nbechance\nbecharm\nbeche\nbeck\nbecket\nbeckett\nbeckley\nbeckon\nbecks\nbecloud\nbecome\nbecoming\nbecomingly\nbecomingness\nbecripple\nbed\nbed-wetting\nbedarken\nbedaub\nbedaubed\nbedazzle\nbedbug\nbedclothes\nbedded\nbedder\nbedding\nbedeck\nbedel\nbedesman\nbedevil\nbedevilment\nbedew\nbedewed\nbedfast\nbedfellow\nbedfellows\nbedgown\nbedground\nbedight\nbedim\nbedimmed\nbedizen\nbedizened\nbedlam\nbedlamite\nbedless\nbedlight\nbedmaker\nbedmate\nbedouin\nbedpan\nbedpost\nbedraggled\nbedridden\nbedrock\nbedroll\nbedroom\nbedside\nbedsore\nbedspread\nbedspring\nbedstead\nbedstraw\nbedtime\nbedwarf\nbee\nbeebread\nbeech\nbeechen\nbeechnut\nbeef\nbeefeater\nbeefed-up\nbeefsteak\nbeefwood\nbeefy\nbeehive\nbeekeeper\nbeekeeping\nbeeline\nbeelzebub\nbeen\nbeep\nbeer\nbeersheba\nbeery\nbees\nbeeswax\nbeet\nbeethoven\nbeethovenian\nbeetle\nbeetle-browed\nbeetlehead\nbeetling\nbeetroot\nbefall\nbefated\nbefit\nbefitting\nbefog\nbefogged\nbefool\nbefooled\nbefore\nbeforehand\nbeforementioned\nbefoul\nbefouled\nbefoulment\nbefriend\nbefringed\nbefuddle\nbeg\nbegawd\nbeget\nbeggar\nbeggard\nbeggared\nbeggarly\nbeggarman\nbeggarmyneighbor\nbeggarred\nbeggars\nbeggarweed\nbeggarwoman\nbeggary\nbegging\nbegilt\nbegin\nbeginner\nbeginning\nbeginning(a)\nbeginnings\nbegins\nbegird\nbegirt\nbeglerbeg\nbegone\nbegonia\nbegoniaceae\nbegotten\nbegreasedabble\nbegrime\nbegrimed\nbegrudge\nbegrudging\nbeguile\nbeguiled\nbeguilement\nbeguiling\nbegum\nbegun\nbehalf\nbehave\nbehaved\nbehavior\nbehavioral\nbehaviorism\nbehaviorist\nbehavioristic\nbehead\nbeheaded\nbehemoth\nbehest\nbehind\nbehind(p)\nbehindhand\nbehold\nbeholden\nbeholden(p)\nbeholder\nbehoof\nbehoove\nbehooving\nbeige\nbeijing\nbeing\nbeings\nbeirut\nbel\nbelabor\nbelamcanda\nbelarus\nbelarusian\nbelated\nbelaud\nbelay\nbelch\nbelching\nbeldam\nbeldame\nbelduque\nbeleaguer\nbelemnite\nbelemnitic\nbelemnitidae\nbelemnoidea\nbelfast\nbelfry\nbelgian\nbelgium\nbelgrade\nbelial\nbelie\nbelief\nbelievably\nbelieve\nbelieved\nbeliever\nbelieving\nbelike\nbelittle\nbelittled\nbelittling\nbelize\nbell\nbell-bottomed\nbella\nbelladonna\nbellarmine\nbellbird\nbellboy\nbelle\nbellerophon\nbelles\nbelles-lettres\nbelletristic\nbelli\nbellicose\nbellied\nbelligerant\nbelligerence\nbelligerency\nbelligerent\nbelligerently\nbelling\nbellis\nbellman\nbellmare\nbello\nbellona\nbellow\nbellows\nbellpull\nbells\nbellum\nbellwether\nbellwort\nbelly\nbellyband\nbellyful\nbellyless\nbelomancy\nbelong\nbelonging\nbelongings\nbelonidae\nbelostomatidae\nbeloved\nbelow\nbelowground\nbelshazzar\nbelt\nbelted\nbelting\nbeluga\nbelus\nbelvedere\nbemingle\nbemire\nbemisia\nbemoan\nbemused\nben\nbench\nbencher\nbenchmark\nbend\nbendability\nbendable\nbended\nbender\nbending\nbene\nbeneath\nbenedick\nbenedict\nbenedictine\nbenediction\nbenedictory\nbenefaction\nbenefactor\nbenefactress\nbenefic\nbenefice\nbeneficed\nbeneficence\nbeneficent\nbeneficial\nbeneficially\nbeneficiary\nbeneficient\nbeneficium\nbenefit\nbenefits\nbenelux\nbeneplacito\nbenevolence\nbenevolent\nbenevolently\nbengal\nbengali\nbenghazi\nbenighted\nbenign\nbenignant\nbenignity\nbenignly\nbenin\nbeninese\nbenison\nbenjamin\nbenjamins\nbennet\nbennettitaceae\nbennettitales\nbennettitis\nbennington\nbenolin\nbenschen\nbenshie\nbent\nbentfollow\nbenthal\nbenthamite\nbenthic\nbenthopelagic\nbenthos\nbentley\nbentonite\nbentonitic\nbenumb\nbenumbed\nbenzedrine\nbenzene\nbenzenoid\nbenzine\nbenzoate\nbenzocaine\nbenzodiazepine\nbenzofuran\nbenzoic\nbenzoin\nbenzol\nbenzyl\nbenzylic\nbeplaster\nbeplastered\nbepraise\nbequeath\nbequest\nberate\nberating\nberber\nberberidaceae\nberberis\nbercy\nbereave\nbereaved\nbereavement\nbereft\nberet\nberg\nbergamot\nbergen\nbergenia\nberiberi\nberith\nberkeley\nberkelium\nberkshire\nberkshires\nberlin\nberliner\nberloque\nbermuda\nbermudan\nbern\nbernardine\nberoe\nberried\nberry\nbersagliere\nberserk\nberserker\nberteroa\nberth\nbertholletia\nberycomorphi\nberyl\nberyllium\nbeseech\nbeseeching\nbeseechingly\nbeseem\nbeset\nbesetting\nbeshrew\nbeside\nbesides\nbesiege\nbesieged\nbesieger\nbesique\nbeslaver\nbeslime\nbeslubber\nbesmear\nbesmirched\nbesom\nbesot\nbesotted\nbespangle\nbespatter\nbespattered\nbespeak\nbespeckle\nbespectacled\nbespoke\nbespot\nbesprent\nbesprinkle\nbess\nbessera\nbesseya\nbest\nbest-known\nbest-selling(p)\nbeste\nbestead\nbested\nbesteht\nbestial\nbestiality\nbestially\nbestiary\nbestir\nbestow\nbestowal\nbestowed\nbestowment\nbestraddle\nbestrew\nbestride\nbestubbled\nbet\nbeta\nbetacism\nbetake\nbetatron\nbete\nbetel\nbetelgeuse\nbeth\nbethel\nbethelem\nbethink\nbethrall\nbetide\nbetimes\nbetise\nbetoken\nbetongue\nbetray\nbetrayal\nbetrayer\nbetraying\nbetrim\nbetroth\nbetrothal\nbetrothed\nbetrothment\nbetter\nbetter(p)\nbetter-known(a)\nbettering\nbetterment\nbetting\nbettong\nbettongia\nbettor\nbetty\nbetula\nbetulaceae\nbetulaceous\nbetween\nbetwixt\nbevatron\nbevel\nbever\nbeverage\nbevue\nbevy\nbewail\nbeware\nbewilder\nbewildered\nbewilderedly\nbewildering\nbewilderingly\nbewilderment\nbewitch\nbewitched\nbewitchery\nbewitching\nbewitchingly\nbewitchment\nbey\nbeyond\nbezant\nbezel\nbezonian\nbf\nbg\nbh\nbhadon\nbhaga\nbhang\nbhisti\nbhutan\nbhutanese\nbi\nbialy\nbiannually\nbias\nbiased\nbiauricular\nbiaxial\nbib\nbib-and-tucker\nbibacious\nbibacity\nbibbed\nbibber\nbibblebabble\nbibendi\nbibendum\nbibere\nbible\nbibless\nbiblical\nbiblioclasm\nbiblioclast\nbibliographer\nbibliographic\nbibliography\nbibliolatrous\nbibliolatry\nbibliomania\nbibliomaniac\nbibliomaniacal\nbibliophile\nbibliophilic\nbibliopole\nbibliopolic\nbibliopolist\nbibliotheca\nbibliothecal\nbibliotic\nbibliotics\nbibliotist\nbibos\nbibulous\nbicameral\nbicapsular\nbicarbonate\nbice\nbicentennial\nbicentric\nbicephalous\nbiceps\nbichloride\nbichona\nbichromate\nbichromated\nbicipital\nbicker\nbickering\nbickerstaff\nbicolor\nbiconcave\nbiconjugate\nbiconvex\nbicorn\nbicornuous\nbicornute\nbicuspid\nbicycle\nbicycle-built-for-two\nbicyclic\nbicycling\nbicylindrical\nbid\nbidder\nbidding\nbiddy\nbide\nbidens\nbidental\nbidentate\nbidet\nbidirectional\nbiduous\nbien\nbiennial\nbiennially\nbienseance\nbier\nbiface\nbifacial\nbifarious\nbiff\nbifid\nbifilar\nbiflagellate\nbifocal\nbifold\nbifoliate\nbiform\nbiformity\nbifurcate\nbifurcated\nbifurcation\nbifurcous\nbig\nbig(a)\nbig(p)\nbig-bellied\nbig-boned\nbig-chested\nbig-shouldered\nbig-ticket(a)\nbigamist\nbigamous\nbigamy\nbigeminal\nbigeneric\nbigeye\nbigfoot\nbigger\nbiggest\nbiggin\nbiggish\nbighead\nbigheaded\nbigheartedness\nbighorn\nbight\nbigmouthed\nbigness\nbignonia\nbignoniaceae\nbignoniaceous\nbignoniad\nbigot\nbigoted\nbigotry\nbigram\nbigsounding\nbigswoln\nbigwig\nbihar\nbihari\nbijou\nbijouterie\nbijoutry\nbike\nbikini\nbilabial\nbilabiate\nbilander\nbilateral\nbilaterality\nbilaterally\nbilberry\nbilbo\nbilboes\nbildet\nbile\nbilge\nbilges\nbilgewater\nbilgy\nbiliary\nbilimbi\nbilinear\nbilingual\nbilingually\nbiliomancy\nbilious\nbiliousness\nbilirubin\nbilk\nbill\nbillboard\nbilled\nbillet\nbilletdoux\nbillfish\nbillhook\nbilliard\nbilliards\nbilling\nbillings\nbillingsgate\nbillion\nbillionth\nbillow\nbillowing\nbillows\nbillowy\nbills\nbilly\nbillycock\nbillyo\nbilobate\nbilocular\nbiloxi\nbiltong\nbimbo\nbimester\nbimestrial\nbimetal\nbimetallism\nbimetallist\nbimetallistic\nbimillenial\nbimillennium\nbimodal\nbimolecular\nbimonthly\nbimorphemic\nbimotored\nbin\nbina\nbinary\nbinate\nbinaural\nbinaurally\nbind\nbindable\nbinder\nbindery\nbinding\nbindweed\nbine\nbing\nbinge\nbinghamton\nbingo\nbinnacle\nbinocular\nbinoculars\nbinomial\nbinturong\nbinucleate\nbioassay\nbiocatalyst\nbiocatalytic\nbiochemical\nbiochemically\nbiochemist\nbiochemistry\nbioclimatic\nbioclimatology\nbiodegradable\nbioelectricity\nbiofeedback\nbiogenesis\nbiogenetic\nbiogenic\nbiogenous\nbiogeny\nbiogeographic\nbiogeography\nbiograph\nbiographer\nbiographic\nbiography\nbioko\nbiol\nbiolets\nbiological\nbiologically\nbiologism\nbiologist\nbiologistic\nbiology\nbioluminescence\nbioluminescent\nbiomass\nbiomedical\nbiomedicine\nbionic\nbionics\nbiont\nbiophysicist\nbiophysics\nbioplasm\nbiopsy\nbioremediation\nbioscope\nbiosynthesis\nbiosynthetic\nbiosystematic\nbiosystematics\nbiota\nbiotaxy\nbiotechnology\nbiotic\nbiotin\nbiotite\nbiotitic\nbiotype\nbiotypic\nbiparous\nbipartisan\nbipartite\nbipartition\nbiped\nbipedal\nbipinnate\nbipinnatifid\nbiplane\nbiplicity\nbipolar\nbiprism\nbiquadrate\nbiquadratic\nbiracial\nbiradial\nbiradially\nbirch\nbird\nbird's-eye\nbirdbath\nbirdcage\nbirdcall\nbirdhouse\nbirdie\nbirdlime\nbirdman\nbirdnesting\nbirds\nbirdseye\nbirdwitted\nbirefringent\nbiretta\nbirl\nbirling\nbirmingham\nbirr\nbirth\nbirthday\nbirthing\nbirthmark\nbirthplace\nbirthrate\nbirthright\nbirththroe\nbirthwort\nbis\nbiscuit\nbiscuits\nbiscutella\nbise\nbisect\nbisected\nbisection\nbisectional\nbiserrate\nbisexual\nbisexuality\nbishop\nbishopdom\nbishopric\nbishopry\nbishops\nbiskek\nbismarck\nbismarckian\nbismuth\nbismuthal\nbismuthic\nbison\nbisontine\nbisque\nbisquit\nbissau\nbissextile\nbister\nbistered\nbistoury\nbistro\nbistroic\nbisulcate\nbisulcated\nbisulcous\nbit\nbit-by-bit\nbitartrate\nbitch\nbitchery\nbitchy\nbite\nbiteplate\nbiter\nbites\nbitewing\nbiting\nbitis\nbitmap\nbito\nbitplayer\nbits\nbitt\nbitten\nbitter\nbittercress\nbitterish\nbitterly\nbittern\nbitterness\nbitternut\nbitterroot\nbitters\nbittersweet\nbitthead\nbitty\nbitumastic\nbitumen\nbituminoid\nbituminous\nbiu-mandara\nbivalent\nbivalve\nbivalvia\nbivalvular\nbivariate\nbivouac\nbiweekly\nbizarre\nbizarrerie\nbizonal\nbj\nbk\nbl\nblab\nblabber\nblabbermouthed\nblaberus\nblack\nblack-and-blue\nblack-and-white\nblackamoor\nblackandwhite\nblackball\nblackberries\nblackberry\nblackberry-lily\nblackbird\nblackboard\nblackbody\nblackbrowed\nblackbuck\nblackburn\nblackcap\nblackcock\nblacken\nblackened\nblackening\nblackface\nblackfish\nblackfly\nblackfoot\nblackguard\nblackguardism\nblackhead\nblackheart\nblackhearted\nblackish\nblackjack\nblackleg\nblackletter\nblacklist\nblackmail\nblackmailer\nblackmouthed\nblackness\nblackout\nblackpoll\nblackpool\nblackquarter\nblackshirt\nblacksmith\nblacksnake\nblackthorn\nblacktop\nblacktopped\nblackwater\nblackwood\nbladder\nbladderpod\nbladderwort\nbladderwrack\nbladdery\nblade\nbladed\nblae\nblague\nblah\nblahs\nblain\nblame\nblameless\nblamelessness\nblameworthiness\nblameworthy\nblanc\nblancbec\nblanch\nblanche\nblanched\nblancmange\nbland\nblandae\nblandfordia\nblandiloquence\nblandiloquent\nblandiment\nblandishment\nblandly\nblandness\nblank\nblanket\nblankly\nblankness\nblanquillo\nblare\nblarina\nblaring\nblarney\nblas\nblase\nblaser\nblaspheme\nblasphemer\nblasphemous\nblasphemously\nblasphemy\nblast\nblasted\nblastema\nblastemal\nblasting\nblastocladia\nblastocladiales\nblastocoel\nblastocoelic\nblastocyte\nblastoderm\nblastodermatic\nblastodiaceae\nblastoff\nblastogenesis\nblastogenetic\nblastomere\nblastomeric\nblastomyces\nblastomycete\nblastomycosis\nblastomycotic\nblastoporal\nblastopore\nblastospheric\nblastula\nblatancy\nblatant\nblatantly\nblather\nblathering\nblatherskite\nblatta\nblattella\nblatter\nblattidae\nblattodea\nblaze\nblazed\nblazer\nblazing\nblazon\nble\nbleach\nbleached\nbleachers\nbleaching\nbleak\nbleakly\nbleakness\nblear\nblearedness\nbleareyed\nbleary\nbleat\nbleb\nblebbed\nblebby\nblechnaceae\nblechnum\nbleed\nbleeding\nbleeding(a)\nbleep\nblemish\nblemished\nblench\nblend\nblended\nblender\nblending\nblenheim\nblenniidae\nblennioidea\nblennius\nblennorrhagia\nblennorrhoea\nblenny\nblepharitis\nblephilia\nbless\nblessed\nblessedly\nblessedness\nblessing\nblessings\nblest\nblether\nbletia\nbletilla\nbletonism\nbleu\nblewits\nbligh\nblighia\nblight\nblighted\nblighty\nblimp\nblimpish\nblind\nblinded\nblindfold\nblindfolded\nblindly\nblindmans\nblindness\nblinds\nblindworm\nblini\nblink\nblinkard\nblinker\nblinking\nblinks\nblintz\nblip\nbliss\nblissful\nblissfully\nblissus\nblister\nblistered\nblistering\nblithe\nblithesome\nblitz\nblizzard\nbloat\nbloated\nbloater\nblob\nblobber\nblobberlipped\nbloc\nblock\nblockade\nblockade-runner\nblockading\nblockage\nblockbuster\nblocked\nblocker\nblockhead\nblockheaded\nblockhouse\nblocking\nblockish\nblocks\nblodd\nbloemfontein\nbloke\nblolly\nblond\nblood\nblood-and-guts\nblood-filled\nbloodbath\nbloodberry\nbloodcurdling\nblooded\nbloodguilt\nbloodguilty\nbloodhound\nbloodied\nbloodily\nbloodleaf\nbloodless\nbloodlessly\nbloodlessness\nbloodletting\nbloodlust\nbloodmobile\nbloodroot\nbloodshed\nbloodshot\nbloodsport\nbloodstain\nbloodstained\nbloodstock\nbloodstone\nbloodstream\nbloodstroke\nbloodsucker\nbloodsucking\nbloodthirsty\nbloodworm\nbloodwort\nbloody\nbloody-minded\nbloodyminded\nbloom\nbloomer\nbloomeria\nbloomers\nblooming\nblooper\nblossom\nblossomed\nblossoming\nblossoms\nblot\nblotch\nblotched\nblotches\nblotchy\nblotter\nblouse\nblow\nblowback\nblowed\nblower\nblowfish\nblowfly\nblowgun\nblowhole\nblowing\nblown\nblown-up\nblown-up(a)\nblowoff\nblowout\nblowpipe\nblows\nblowsy\nblowth\nblowtorch\nblowtube\nblowy\nblowzy\nblubber\nblucher\nbludgeon\nblue\nblue(a)\nblue-black\nblue-collar\nblue-eyed\nblue-eyed(a)\nblue-ribbon(a)\nblueback\nbluebeard\nblueberry\nbluebird\nblueblack\nbluebonnet\nbluebottle\nbluefin\nbluefish\nbluegill\nbluehead\nbluejacket\nblueness\nbluepoint\nblueprint\nblues\nbluestem\nbluestocking\nbluestone\nbluethroat\nbluetick\nblueweed\nbluewing\nbluff\nbluffer\nbluffly\nbluffness\nbluing\nbluish\nbluishness\nblunder\nblunderbuss\nblunderer\nblunderhead\nblundt\nblundwitted\nblunt\nblunted\nbluntness\nblur\nblurb\nblurred\nblurt\nblush\nblushful\nblushing\nblushingly\nbluster\nblusterer\nblustering\nblustering(a)\nblustery\nblut\nbo\nboa\nboann\nboar\nboard\nboarder\nboarding\nboardroom\nboards\nboardwalk\nboarfish\nboarhound\nboast\nboaster\nboastful\nboastfully\nboastfulness\nboasting\nboaston\nboat\nboatbill\nboatbuilder\nboater\nboathouse\nboating\nboatload\nboatman\nboatmanship\nboats\nboatswain\nbob\nbobadil\nbobbery\nbobbin\nbobbing\nbobbish\nbobble\nbobby\nbobbysoxer\nbobcat\nbobolink\nbobsled\nbobsledding\nbobsleigh\nbobtail\nbobwhite\nbocage\nbocca\nbocce\nbocconia\nbock\nbodacious\nbode\nbodega\nbodement\nbodhisattva\nbodice\nbodied\nbodies\nbodiless\nbodily\nbodkin\nbodo-garo\nbody\nbodybuilder\nbodybuilding\nbodyguard\nbodywork\nboehme\nboehmenism\nboehmeria\nboeotia\nboeotian\nboffin\nbog\nbogart\nbogartian\nbogey\nbogeyman\nboggart\nboggle\nboggy\nbogie\nbogle\nbogota\nbogtrotter\nbogus\nbogy\nbohemian\nboidae\nboil\nboiled\nboiled-down\nboiler\nboilerplate\nboiling\nboire\nboise\nboisterous\nboisterousness\nbokmal\nbola\nbolbitis\nbold\nboldface\nboldfaced\nboldly\nboldness\nboldspirited\nbole\nbolero\nboletaceae\nbolete\nboletellus\nboletus\nboleyn\nbolide\nbolivar\nbolivia\nbolivian\nboliviano\nboll\nbollard\nbollock\nbollworm\nbolo\nbologna\nbologram\nbolographic\nbolometer\nbolometric\nbolshevik\nbolshevism\nbolshy\nbolster\nbolt\nbolt-hole\nbolthead\nbolti\nboltonia\nbolus\nbomarea\nbomb\nbombacaceae\nbombard\nbombardier\nbombardment\nbombardon\nbombast\nbombastes\nbombastic\nbombastically\nbombax\nbombay\nbomber\nbombilation\nbombina\nbombinate\nbombination\nbombing\nbombproof\nbombshell\nbombsight\nbombus\nbombycid\nbombycidae\nbombycilla\nbombycillidae\nbombyliidae\nbombyx\nbon\nbona\nbonaire\nbonanza\nbonasa\nbonbon\nbond\nbondable\nbondage\nbonded\nbondholder\nbonding\nbondman\nbonds\nbondslave\nbondsman\nbondswoman\nbonduc\nbondwoman\nbone\nbone(a)\nbone-covered\nbone-dry(a)\nbone-idle\nboned\nbonefish\nboneless\nbonelike\nbonemeal\nboner\nbones\nboneset\nbonesetter\nboneshaker\nbonete\nbonfire\nbong\nbongo\nbonheur\nbonhomie\nbonhomme\nboniface\nboniness\nbonis\nbonito\nbonk\nbonmotjeu\nbonn\nbonne\nbonnebouche\nbonnet\nbonnily\nbonny\nbono\nbonos\nbonsai\nbonum\nbonus\nbony\nbonze\nbonzer\nboo\nboob\nbooboo\nbooby\nboodle\nboof\nbooger\nboogie\nboohoo\nbook\nbookable\nbookbinder\nbookbinding\nbookcase\nbookclub\nbookdealer\nbooked\nbookend\nbooker\nbookful\nbooking\nbookish\nbookishness\nbookkeeper\nbookkeeping\nbookless\nbooklet\nbooklouse\nbookmaker\nbookmaking\nbookmark\nbookmobile\nbookplate\nbooks\nbookseller\nbooksellers\nbookshelf\nbookshop\nbookstore\nbookworm\nboole\nboolean\nbooly\nboom\nboomer\nboomerang\nbooming\nboon\nboone\nboor\nboorish\nboorishly\nboorishness\nboost\nbooster\nboosters\nboot\nbootblack\nbooted\nbootee\nbootes\nbooth\nboothose\nbootikin\nbootjack\nbootlace\nbootleg\nbootlegger\nbootlegging\nbootless\nbootlicking\nbootmaker\nboots\nbootstrap\nbooty\nbooze\nboozy\nbop\nbopeep\nborage\nboraginaceae\nborago\nborassus\nborate\nborated\nborax\nbordeaux\nbordel\nbordelaise\nbordello\nborder\nbordered\nborderer\nbordering\nborderland\nborderline\nborders\nbore\nboreal\nborealis\nboreas\nbored\nboredom\nborer\nboric\nboring\nboringly\nboringness\nborn\nborn(a)\nborn-again\nborne\nbornean\nborneo\nbornite\nborns\nborodino\nboron\nboronic\nborosilicate\nborough\nborrelia\nborrow\nborrowed\nborrower\nborrowing\nborrows\nborsch\nborscht\nborstal\nborzoi\nbos\nbosc\nboscage\nboselaphus\nbosh\nbosk\nboskopoid\nbosky\nbosnia-herzegovina\nbosnian\nbosom\nbosom(a)\nbosomed\nbosomy\nboson\nbosporus\nboss\nboss-eyed\nbossed\nbossism\nbossy\nboston\nboswellia\nbot\nbota\nbotanic\nbotanical\nbotanist\nbotanize\nbotanomancy\nbotany\nbotaurus\nbotch\nbotchery\nbotchy\nbotfly\nboth\nboth(a)\nbother\nbotheration\nbothered\nbothering\nbothidae\nbothrops\nbothy\nbotonee\nbotrychium\nbotryoid\nbotswana\nbotte\nbottes\nbottle\nbottle-fed\nbottle-green\nbottlebrush\nbottlecap\nbottleholder\nbottles\nbottletree\nbottom\nbottom(a)\nbottom-up\nbottomed\nbottomland\nbottomless\nbottomlessness\nbottommost\nbottop\nbotttomless\nbotuliform\nbotulin\nbotulinal\nbotulinus\nbotulism\nbouche\nboucle\nbouderie\nboudoir\nbouffant\nbouffe\nbougainville\nbougainvillea\nbouge\nbough\nboughed\nboughless\nbought\nboughten\nbougie\nbouillabaisse\nbouilli\nbouillon\nboulder\nboulebards\nbouleverse\nbouleversement\nbouleverser\nboulle\nbounce\nbouncer\nbounces\nbouncing\nbouncy\nbound\nbound(p)\nboundaries\nboundary\nbounded\nbounden\nbounding\nboundless\nboundlessly\nboundlessness\nbounds\nboundshave\nbounteous\nbounteousness\nbountied\nbountiful\nbountifully\nbountifulness\nbounty\nbouquet\nbourbon\nbourdon\nbourgeois\nbourgeoisie\nbourgeon\nbourgogne\nbourguignon\nbourn\nbourse\nbourtree\nbouse\nboustrophedon\nboustrophedonic\nbout\nboutade\nbouteloua\nboutez\nboutique\nboutonniere\nbouvines\nbouyei\nbove\nbovi\nbovid\nbovidae\nbovinae\nbovine\nbovini\nbow\nbow(a)\nbow-wow\nbowdlerization\nbowed\nbowel\nbowelless\nbowels\nbower\nbowerbird\nbowers\nbowery\nbowfin\nbowfront\nbowhead\nbowie\nbowiea\nbowing\nbowl\nbowlder\nbowleg\nbowlegged\nbowler\nbowline\nbowling\nbowls\nbowman\nbowmann\nbowsprit\nbowstring\nbowwow\nbox\nbox-number\nboxcar\nboxcars\nboxed\nboxer\nboxes\nboxfish\nboxing\nboxlike\nboxwood\nboy\nboyar\nboycott\nboyfriend\nboyhood\nboyish\nboyishly\nboyishness\nboykinia\nboyne\nboysenberry\nbozo\nbp\nbra\nbrabble\nbrabbler\nbrace\nbraced\nbracelet\nbracer\nbrachial\nbrachiate\nbrachiation\nbrachinus\nbrachiopod\nbrachiopoda\nbrachium\nbrachycephalic\nbrachychiton\nbrachycome\nbrachydactylic\nbrachygraphy\nbrachypterous\nbrachystegia\nbrachytactyly\nbrachyura\nbrachyuran\nbrachyurous\nbracing\nbracken\nbracket\nbrackish\nbrackishness\nbract\nbracteal\nbracteate\nbracteolate\nbracteole\nbrad\nbradawl\nbradshaw\nbradycardia\nbradypodidae\nbradypus\nbrae\nbrag\nbragg\nbraggadocio\nbraggardism\nbraggart\nbragi\nbrahimism\nbrahm\nbrahma\nbrahman\nbrahmana\nbrahmanism\nbrahmaputra\nbrahmi\nbrahmin\nbrahminic\nbrahminical\nbrahms\nbrahui\nbraid\nbraided\nbraille\nbrailletype\nbrain\nbrained\nbrainless\nbrainpan\nbrains\nbrainsick\nbrainstem\nbrainwashed\nbrainwashing\nbrainwave\nbrainworker\nbrainy\nbraise\nbraised\nbraising\nbrake\nbrakeman\nbraky\nbrama\nbramble\nbrambling\nbramidae\nbran\nbrancard\nbranch\nbranched\nbranchiae\nbranchial\nbranchiate\nbranching\nbranchiobdella\nbranchiobdellidae\nbranchiopod\nbranchiopoda\nbranchiostegidae\nbranchiura\nbranchless\nbranchlet\nbranchout\nbranchy\nbrand\nbrand-new\nbrand-newness\nbranded\nbrandish\nbrandnew\nbrandy\nbrandyball\nbrandysnap\nbrangle\nbrangler\nbrank\nbranny\nbrant\nbranta\nbras\nbrasenia\nbrash\nbrashness\nbrasier\nbrasilia\nbrass\nbrassard\nbrassavola\nbrassband\nbrassbound\nbrasscolored\nbrasserie\nbrassia\nbrassica\nbrassie\nbrassiere\nbrassy\nbrat\nbratling\nbrattle\nbratty\nbratwurst\nbraunschweig\nbravado\nbrave\nbravely\nbravery\nbraving\nbravissimo\nbravo\nbravura\nbrawl\nbrawler\nbrawling\nbrawn\nbrawny\nbray\nbrayed\nbraze\nbrazen\nbrazenfaced\nbrazenly\nbrazier\nbrazil\nbrazilian\nbrazilwood\nbrazos\nbrazzaville\nbreach\nbreached\nbreachloader\nbreachy\nbread\nbread-bin\nbreadbasket\nbreadboard\nbreadcrumb\nbreadfruit\nbreadline\nbreadroot\nbreadstick\nbreadstuff\nbreadth\nbreadthwise\nbreadwinner\nbreak\nbreakable\nbreakableness\nbreakage\nbreakaway\nbreakax\nbreakbone\nbreakdown\nbreaker\nbreakers\nbreakfast\nbreaking\nbreakneck\nbreaks\nbreakthrough\nbreakwater\nbream\nbreast\nbreast-deep\nbreast-fed\nbreasted\nbreastless\nbreastplate\nbreasts\nbreaststroke\nbreastwork\nbreath\nbreathalyzer\nbreathe\nbreathed\nbreathing\nbreathinghole\nbreathless\nbreathlessly\nbreatless\nbreccia\nbred\nbreech\nbreechblock\nbreechcloth\nbreeched\nbreeches\nbreechesmaker\nbreed\nbreeder\nbreeding\nbreeze\nbreezily\nbreeziness\nbreezy\nbregma\nbregmatic\nbren\nbretagne\nbrethren\nbreton\nbrett\nbreve\nbrevem\nbrevet\nbreviary\nbrevier\nbrevipennate\nbrevis\nbrevity\nbrevoortia\nbrew\nbrewer\nbrewery\nbrewing\nbrian\nbriar\nbriard\nbriarean\nbriarroot\nbriarwood\nbriary\nbribe\nbriber\nbribery\nbric-a-brac\nbrick\nbrickbat\nbrickcolored\nbrickellia\nbrickkiln\nbricklayer\nbricklaying\nbricks\nbrickwork\nbrickyard\nbridal\nbride\nbride-gift\nbridegroom\nbridesmaid\nbridesman\nbridewell\nbridge\nbridgeable\nbridged-t\nbridgehead\nbridgeport\nbridget\nbridle\nbridoon\nbrie\nbrief\nbriefcase\nbriefing\nbriefless\nbriefly\nbriefness\nbrier\nbrig\nbrigade\nbrigadier\nbrigand\nbrigandage\nbrigandine\nbrigantine\nbright\nbrighten\nbrightness\nbrighton\nbrigit\nbrigue\nbrihaspati\nbrill\nbriller\nbrilliance\nbrilliancy\nbrilliant\nbrilliantine\nbrilliantly\nbrim\nbrimful\nbrimless\nbrimmer\nbrimming\nbrimstone\nbrinded\nbrindle\nbrindled\nbrine\nbring\nbringing\nbringword\nbrininess\nbrink\nbrinkmanship\nbriny\nbrio\nbrioche\nbrioschi\nbriquet\nbriquette\nbrisance\nbrisant\nbrisbane\nbrisk\nbrisket\nbriskly\nbriskness\nbrisling\nbristle\nbristlegrass\nbristlelike\nbristletail\nbristling\nbristly\nbristol\nbrit\nbritannia\nbritannic\nbritches\nbriticism\nbritish\nbritisher\nbriton\nbrittle\nbrittlebush\nbrittleness\nbritzka\nbroach\nbroad\nbroad(a)\nbroad-headed\nbroad-minded\nbroad-mindedly\nbroad-mindedness\nbroadax\nbroadband\nbroadbill\nbroadcast\nbroadcaster\nbroadcasting\nbroadcloth\nbroadening\nbroadhorn\nbroadleaf\nbroadloom\nbroadly\nbroadsheet\nbroadshouldered\nbroadside\nbroadsword\nbroadtail\nbroadway\nbrobdingnagian\nbrocade\nbrocaded\nbrocatelle\nbroccoli\nbrochure\nbrocken\nbrocket\nbroder\nbrodiaea\nbrogan\nbrogue\nbroil\nbroiled\nbroiler\nbroiling\nbroke\nbroken\nbroken-backed\nbroken-down\nbroken-field\nbrokenhearted\nbrokenwinded\nbroker\nbroker-dealer\nbrokerage\nbrokery\nbrome\nbromelia\nbromeliaceae\nbromic\nbromide\nbromidic\nbromine\nbromo-seltzer\nbromus\nbronchia\nbronchial\nbronchiolar\nbronchiole\nbronchiolitis\nbronchitic\nbronchitis\nbroncho\nbronchocele\nbronchodilator\nbronchoscope\nbronchoscopic\nbronchospasm\nbronchus\nbronco\nbronx\nbronze\nbronzed\nbrooch\nbrood\nbrooding\nbroodmare\nbroody\nbrook\nbrooklet\nbrooklime\nbrooklyn\nbrookweed\nbroom\nbroomcorn\nbrooms\nbroomstick\nbroomweed\nbrosmius\nbroth\nbrothel\nbrother\nbrother-in-law\nbrotherhood\nbrotherly\nbrotula\nbrotulidae\nbrougham\nbrought\nbrouhaha\nbrouillerie\nbrouillon\nbroussonetia\nbrow\nbrowbeat\nbrowbeaten\nbrown\nbrownian\nbrownie\nbrowning\nbrownstone\nbrowntail\nbrows\nbrowse\nbrowser\nbrrok\nbruce\nbrucellosis\nbruchidae\nbruchus\nbrucine\nbrucke\nbruckenthalia\nbruder\nbruges\nbrugmansia\nbruin\nbruise\nbruised\nbruiser\nbruising\nbruit\nbrumaire\nbrumal\nbrummagem\nbrumous\nbrunanburh\nbrunch\nbrunei\nbruneian\nbrunet\nbrunette\nbrunfelsia\nbrunnhilde\nbrunt\nbrush\nbrush-off\nbrushed\nbrushwood\nbrushwork\nbrushy\nbrusque\nbrusquerie\nbrussels\nbrustle\nbrut\nbrutal\nbrutality\nbrutalization\nbrutalize\nbrute\nbrutify\nbrutish\nbrutum\nbrutus\nbrya\nbryaceae\nbryales\nbryanite\nbryanthus\nbrydges\nbryon\nbryony\nbryophyta\nbryophyte\nbryophytic\nbryopsida\nbryozoa\nbryozoan\nbrythonic\nbryum\nbubaline\nbubalus\nbubble\nbubbliness\nbubbling\nbubbly\nbubo\nbubonic\nbubulcus\nbuccal\nbuccaneer\nbuccaneering\nbuccanier\nbuccinidae\nbucconidae\nbucephala\nbucephalus\nbuceros\nbucerotidae\nbuchanan\nbucharest\nbuchergerman\nbuchloe\nbuck\nbuck(a)\nbuck-and-wing\nbuckbasket\nbuckboard\nbucket\nbuckets\nbuckeye\nbuckjumper\nbuckle\nbuckled\nbuckler\nbuckleya\nbuckram\nbuckshee\nbuckskin\nbuckskins\nbuckthorn\nbucktooth\nbuckwheat\nbucolic\nbud\nbudapest\nbuddha\nbuddhism\nbuddhist\nbudding\nbuddy\nbudge\nbudgerigar\nbudget\nbudgetary\nbudmash\nbudorcas\nbuds\nbuen\nbueno\nbuff\nbuffalo\nbuffalofish\nbuffer\nbuffet\nbuffeted\nbuffle\nbufflehead\nbuffo\nbuffon\nbuffoon\nbuffoonery\nbuffoonish\nbufo\nbufonidae\nbug\nbugaboo\nbuganda\nbugbane\nbugbear\nbugged\nbugginess\nbuggy\nbugle\nbuglehorn\nbugler\nbugleweed\nbugloss\nbuild\nbuilder\nbuilding\nbuilding(a)\nbuildings\nbuildup\nbuilt\nbuilt(p)\nbuilt-in\nbuilt-up\nbujumbura\nbulb\nbulbaceous\nbulbar\nbulbed\nbulbil\nbulbous\nbulbul\nbulgaria\nbulgarian\nbulge\nbulging\nbulgur\nbulk\nbulk(a)\nbulkhead\nbulkiness\nbulky\nbull\nbull(a)\nbulla\nbullace\nbullate\nbullbrier\nbulldog\nbulldoze\nbulldozer\nbullet\nbullet-headed\nbullethead\nbulletin\nbulletproof\nbullfight\nbullfighter\nbullfighting\nbullfinch\nbullfrog\nbullhead\nbullheaded\nbullhorn\nbullion\nbullish\nbullnecked\nbullnose\nbullock\nbullocky\nbullpen\nbullring\nbulls\nbullseye\nbullshit\nbullshot\nbullterrier\nbullwhack\nbully\nbullyboy\nbullying\nbullyrag\nbulnesia\nbulrush\nbulwark\nbulwarks\nbulwer\nbum\nbumbailiff\nbumble\nbumblebee\nbumbledom\nbumbling\nbumboat\nbumcombe\nbumelia\nbummer\nbump\nbumper\nbumper-to-bumper\nbumpiness\nbumpkin\nbumpkinly\nbumps\nbumptious\nbumptiously\nbumptiousness\nbumpy\nbun\nbuna\nbunas\nbunch\nbunchberry\nbunched\nbunchgrass\nbunchy\nbunco\nbuncombe\nbund\nbunder\nbundesbank\nbundle\nbundled-up\nbundles\nbundling\nbundobast\nbung\nbungaloid\nbungalow\nbungarus\nbungee\nbunghole\nbungle\nbungled\nbungler\nbungling\nbunion\nbunji-bunji\nbunk\nbunker\nbunkmate\nbunko\nbunkum\nbunny\nbunt\nbuntal\nbunter\nbunting\nbunyan\nbuon\nbuoy\nbuoyancy\nbuoyant\nbuoyantly\nbuoyed\nbuphthalmum\nbur\nbura\nburberry\nburble\nburbling\nburbot\nburden\nburdened\nburdensome\nburdensomeness\nburdock\nburdonless\nbureau\nbureaucracy\nbureaucrat\nbureaucratic\nbureaucratically\nburette\nburg\nburgee\nburgeon\nburgess\nburgh\nburgher\nburghmote\nburglar\nburglarious\nburglarproof\nburglary\nburgle\nburgomaster\nburgoo\nburgoyne\nburgrass\nburgrave\nburgundy\nburhinidae\nburhinus\nburial\nburied\nburin\nburke\nburked(p)\nburl\nburlap\nburled\nburlesque\nburletta\nburlington\nburly\nburmannia\nburmanniaceae\nburmeisteria\nburmese\nburn\nburnable\nburned\nburned-out\nburner\nburning\nburning(a)\nburningshame\nburnish\nburnished\nburnisher\nburnoose\nburnous\nburnout\nburnt\nburnup\nburp\nburr\nburr-headed\nburrawong\nburrfish\nburried\nburrito\nburrlike\nburro\nburrock\nburrow\nbursa\nbursal\nbursar\nbursary\nbursera\nburseraceae\nbursiform\nbursitis\nburst\nbursting\nburtful\nburthen\nburton\nburundi\nburundian\nbury\nburying\nbus\nbusbar\nbusbiness\nbusboy\nbusby\nbusch\nbush\nbushbuck\nbushed\nbushel\nbushes\nbushfighting\nbushing\nbushman\nbushranger\nbushtit\nbushwhacker\nbushwhacking\nbushy\nbusily\nbusiness\nbusinesslike\nbusinessman\nbusinessmen\nbusinessperson\nbusinesswoman\nbusker\nbuskin\nbuskined\nbusman\nbusquen\nbuss\nbust\nbust-up\nbustard\nbusted\nbustle\nbustling\nbusy\nbusybody\nbusyness\nbusywork\nbut\nbutacaine\nbutadiene\nbutane\nbutch\nbutcha\nbutcher\nbutcherbird\nbutchery\nbutea\nbuteo\nbuteonine\nbutler\nbutryaceous\nbutt\nbutte\nbutter\nbutterbur\nbuttercrunch\nbuttercup\nbuttered\nbutterfat\nbutterfingers\nbutterfish\nbutterfly\nbuttermilk\nbutternut\nbutterscotch\nbutterweed\nbutterwort\nbuttery\nbuttinsky\nbuttock\nbuttocks\nbutton\nbutton-down\nbuttoned\nbuttoned-up\nbuttonhole\nbuttonholer\nbuttonhook\nbuttony\nbuttress\nbuttter\nbutuminous\nbutut\nbutyl\nbutylene\nbutyraceous\nbutyric\nbutyrin\nbuxaceae\nbuxom\nbuxomness\nbuxus\nbuy\nbuyer\nbuying\nbuyout\nbuzz\nbuzzard\nbuzzsaw\nbuzzword\nbvd's\nby\nby-and-by\nby-line\nby-product\nbye\nbyelaw\nbyelorussian\nbygone\nbygones\nbylaw\nbyname\nbypass\nbypath\nbypaths\nbyplay\nbypnotism\nbyre\nbyroad\nbyron\nbyroom\nbyssa\nbyssus\nbystander\nbyte\nbyway\nbyways\nbyword\nbyzantine\nbyzantium\nc\nc-clamp\nc-horizon\nc-ration\nc.o.d.\nca\ncab\ncabal\ncabala\ncabalistic\ncabana\ncabaret\ncabaset\ncabbage\ncabbageworm\ncabdriver\ncabello\ncaber\ncabernet\ncabestro\ncabin\ncabined\ncabinet\ncabinetmaker\ncabinetmaking\ncabinetwork\ncable\ncabman\ncabochon\ncabomba\ncabombaceae\ncaboodle\ncaboose\ncabotage\ncabriolet\ncabstand\ncacajao\ncacalia\ncacao\ncacation\ncachalot\ncache\ncachectic\ncacher\ncachet\ncachexia\ncachexy\ncachi\ncachinnation\ncachou\ncacicus\ncacique\ncackle\ncackler\ncackly\ncacodemon\ncacodemonic\ncacodyl\ncacodylic\ncacoepy\ncacoethes\ncacogenesis\ncacography\ncaconym\ncacoopy\ncacophonous\ncacophony\ncactaceae\ncactus\ncacuminal\ncacus\ncad\ncada\ncadaster\ncadastral\ncadastre\ncadaver\ncadaveric\ncadaverine\ncadaverous\ncaddie\ncaddish\ncaddisworm\ncaddo\ncaddy\ncade\ncadeau\ncadence\ncadenced\ncadenza\ncader\ncadere\ncadet\ncadetship\ncadge\ncadger\ncadi\ncadit\ncadiz\ncadj\ncadmium\ncadmiumyellow\ncadra\ncadre\ncaducean\ncaduceus\ncaducity\ncaducous\ncaeca\ncaecal\ncaecilian\ncaeciliidae\ncaecum\ncaelo\ncaenolestes\ncaenolestidae\ncaesalpinia\ncaesalpiniaceae\ncaesalpinioideae\ncaesar\ncaesarem\ncaesarian\ncaesarl\ncaespitose\ncaesura\ncaesural\ncaetera\ncaeteris\ncaeur\ncaf\ncafe\ncafeteria\ncaff\ncaffeine\ncaffeinic\ncaffeinism\ncaftan\ncafuzo\ncag\ncage\ncagey\ncagily\ncagliostro\ncagoule\ncahita\ncahoot\ncahoots\ncahot\ncahotage\ncain\ncaique\ncairina\ncairn\ncairned\ncairngorm\ncairo\ncaisson\ncaitiff\ncajanus\ncajole\ncajolery\ncajun\ncakchiquel\ncake\ncakes\ncakewalk\ncakile\ncalaba\ncalabash\ncalaboose\ncalabria\ncaladenia\ncaladium\ncalamagrostis\ncalambac\ncalambour\ncalamiform\ncalamint\ncalamintha\ncalamitous\ncalamity\ncalamo\ncalamus\ncalando\ncalandrinia\ncalanthe\ncalash\ncalathiform\ncalbe\ncalcaneal\ncalcar\ncalcareous\ncalced\ncalcem\ncalceolaria\ncalceolate\ncalceus\ncalcibus\ncalcic\ncalcicolous\ncalciferous\ncalcific\ncalcification\ncalcimine\ncalcination\ncalcine\ncalcite\ncalcitic\ncalcitrate\ncalcitration\ncalcium\ncalcium-cyanamide\ncalculable\ncalculate\ncalculated\ncalculating\ncalculatingly\ncalculation\ncalculator\ncalculous\ncalculus\ncalcutta\ncalcuttan\ncaldron\ncaleche\ncaledonian\ncalefacient\ncalefaction\ncalefactory\ncalembour\ncalendar\ncalendas\ncalender\ncalendric\ncalendula\ncalenture\ncalf\ncalgary\ncaliban\ncaliber\ncalibrate\ncalibration\ncaliche\ncaliche-topped\ncalico\ncalidity\ncalidris\ncalifornia\ncalifornian\ncalifornium\ncaligation\ncaliginous\ncaliper\ncalipers\ncaliph\ncaliphate\ncalisaya\ncalisthenic\ncalisthenics\ncaliver\ncalk\ncall\ncall-back\ncall-board\ncall-out\ncalla\ncallable\ncallaesthetics\ncallant\ncalled\ncaller\ncalliandra\ncallicebus\ncallidity\ncalligrapher\ncalligraphic\ncalligraphy\ncallimorpha\ncallinectes\ncalling\ncallionymidae\ncalliope\ncalliophis\ncalliopsis\ncalliphora\ncalliphoridae\ncallirhoe\ncallisaurus\ncallistephus\ncallisto\ncallithricidae\ncallithrix\ncallithump\ncallithumpian\ncallitrichaceae\ncallitriche\ncallitris\ncallorhinus\ncallosity\ncallous\ncalloused\ncallously\ncallousness\ncallow\ncallowness\ncalls\ncalluna\ncallus\ncalm\ncalming\ncalmly\ncalmness\ncalms\ncalocarpum\ncalocedrus\ncalochortus\ncalomel\ncalophyllum\ncalopogon\ncaloric\ncalorie\ncalorifacient\ncalorific\ncalorimeter\ncalorimetric\ncalorimetry\ncalosoma\ncalostomataceae\ncalote\ncalotte\ncalotype\ncaloyer\ncalque\ncaltha\ncaltrop\ncaludicate\ncalumet\ncaluminiator\ncalumniate\ncalumniatory\ncalumnious\ncalumny\ncalva\ncalvados\ncalvaria\ncalvary\ncalvatia\ncalve\ncalved\ncalvin\ncalving\ncalvinism\ncalvinist\ncalycanthaceae\ncalycanthus\ncalyceal\ncalycophyllum\ncalycular\ncalyculate\ncalyculus\ncalypso\ncalyptra\ncalyptrate\ncalystegia\ncalyx\ncam\ncamail\ncamarade\ncamaraderie\ncamarilla\ncamarista\ncamas\ncamassia\ncambarus\ncamber\ncambial\ncambio\ncambist\ncambium\ncambodia\ncambodian\ncamboose\ncambrian\ncambric\ncambridge\ncamden\ncame\ncamel\ncamelidae\ncamelina\ncamellia\ncamelopard\ncamelot\ncamels\ncamelus\ncamembert\ncameo\ncamera\ncameraman\ncamerated\ncameroon\ncameroonian\ncamilla\ncamino\ncamisade\ncamisole\ncamorra\ncamouflage\ncamouflaged\ncamp\ncamp-made\ncampagna\ncampagne\ncampaign\ncampaigner\ncampaigning\ncampania\ncampaniform\ncampanile\ncampaniliform\ncampanula\ncampanulaceae\ncampanulales\ncampanulate\ncampbell\ncampephilus\ncamper\ncampestral\ncampestrial\ncampestrian\ncampestrine\ncampfire\ncamphor\ncamphoraceous\ncamphorated\ncamphoric\ncamping\ncampmate\ncamponotus\ncampsite\ncampstool\ncamptosorus\ncampus\ncampyloneurum\ncampylorhynchus\ncampylotropous\ncamshaft\ncamwood\ncan\ncan-do\ncanaanite\ncanaanitic\ncanachites\ncanada\ncanadian\ncanaille\ncanakin\ncanal\ncanalicular\ncanaliculate\ncanaliculated\ncanaliculus\ncanaliferous\ncanalization\ncananga\ncanape\ncanard\ncanary\ncanas\ncanasta\ncanavalia\ncanavanine\ncanberra\ncancan\ncancel\ncanceling\ncancellate\ncancellated\ncancellation\ncancelli\ncancer\ncancerous\ncancerweed\ncancridae\ncancroid\ncancun\ncandelabrum\ncandelia\ncandelilla\ncandent\ncandescent\ncandid\ncandida\ncandidate\ncandidature\ncandidiasis\ncandied\ncandle\ncandleholder\ncandlelight\ncandlelighting\ncandlemaker\ncandlemas\ncandlenut\ncandlepins\ncandlepower\ncandlestick\ncandlewick\ncandlewood\ncandor\ncandy\ncandytuft\ncane\ncanebrake\ncanella\ncanellaceae\ncanescent\ncanicula\ncanicular\ncanidae\ncanine\ncanis\ncanistel\ncanister\ncanker\ncankered\ncankerous\ncankerworm\ncanna\ncannabidaceae\ncannabin\ncannabis\ncannaceae\ncannae\ncanned\ncannelloni\ncannery\ncannes\ncannibal\ncannibalic\ncannibalism\ncannibalistic\ncannibalize\ncannikin\ncanning\ncannon\ncannonade\ncannonball\ncannoneer\ncannonical\ncannons\ncannot\ncannula\ncannular\ncannulation\ncanny\ncanoe\ncanoeist\ncanon\ncanoness\ncanonic\ncanonical\ncanonicals\ncanonicate\ncanonist\ncanonization\ncanonize\ncanonized\ncanonry\ncanons\ncanopied\ncanopy\ncanorae\ncanorous\ncanst\ncant\ncantabile\ncantabrigian\ncantala\ncantaloup\ncantaloupe\ncantankerous\ncantankerously\ncantata\ncantatrice\ncanteen\ncanter\ncanterbury\ncantering\ncantharellus\ncantharides\ncanthus\ncantibus\ncanticle\ncantilenam\ncantilever\ncanting\ncantle\ncantlet\ncanto\ncanton\ncantonal\ncantonment\ncantor\ncantrap\ncanty\ncanuck\ncanvas\ncanvasback\ncanvass\ncanvasser\ncanyon\ncanyonside\ncanzonet\ncaoutchouc\ncap\ncap-a-pie\ncapability\ncapable\ncapacious\ncapaciousness\ncapacitance\ncapacitive\ncapacitor\ncapacity\ncapapie\ncaparison\ncaparisoned\ncape\ncapelin\ncapella\ncaper\ncapercaillie\ncapers\ncapful\ncapillament\ncapillarity\ncapillary\ncapillata\ncapilliform\ncapit\ncapital\ncapitalism\ncapitalist\ncapitalistic\ncapitalization\ncapitals\ncapitalsprint\ncapitate\ncapitation\ncapite\ncapiti\ncapitol\ncapitonidae\ncapitular\ncapitulate\ncapitulation\ncapitulum\ncapnomancy\ncapo\ncapon\ncaporal\ncaporetto\ncapote\ncapouch\ncapparidaceae\ncapparis\ncapped\ncapper\ncapping\ncapra\ncaprella\ncapreolus\ncapri\ncapriccio\ncapriccioso\ncaprice\ncapricious\ncapriciously\ncapriciousness\ncapricorn\ncapricornis\ncapricornus\ncaprifig\ncaprifoliaceae\ncaprimulgidae\ncaprimulgiformes\ncaprimulgus\ncaprina\ncaprine\ncapriole\ncaproidae\ncapromyidae\ncapros\ncaps\ncapsaicin\ncapsella\ncapsheaf\ncapsicum\ncapsid\ncapsize\ncapsized\ncapsizing\ncapstan\ncapstone\ncapsular\ncapsulate\ncapsule\ncaptain\ncaptainship\ncaptandum\ncaptation\ncaption\ncaptious\ncaptiously\ncaptiousness\ncaptivate\ncaptivated\ncaptivating\ncaptivation\ncaptive\ncaptivity\ncaptopril\ncaptor\ncapture\ncapuccino\ncapuchin\ncapulets\ncapulin\ncaput\ncapybara\ncaquet\ncaquetterie\ncar\ncar-ferry\ncara\ncarabao\ncarabidae\ncarabineer\ncarabiner\ncaracal\ncaracara\ncaracas\ncarack\ncaracole\ncaracoler\ncaracolito\ncarafe\ncaraffe\ncarambola\ncarambole\ncaramel\ncarancha\ncaranday\ncarangid\ncarangidae\ncaranx\ncarapace\ncarapidae\ncarassius\ncarat\ncaravan\ncaravanning\ncaravansary\ncaravel\ncaraway\ncarbamate\ncarbide\ncarbine\ncarbocyclic\ncarbohydrate\ncarbolated\ncarboloy\ncarbomycin\ncarbon\ncarbonaceous\ncarbonado\ncarbonara\ncarbonaro\ncarbonate\ncarbonated\ncarbonation\ncarbonic\ncarboniferous\ncarbonization\ncarbonyl\ncarborundum\ncarboxyl\ncarboy\ncarbuncle\ncarbuncled\ncarburetor\ncarcanet\ncarcase\ncarcass\ncarcelage\ncarcharhinidae\ncarcharhinus\ncarcharias\ncarchariidae\ncarcharodon\ncarcinogen\ncarcinogenic\ncarcinoid\ncarcinoma\ncarcinomatous\ncard\ncardamine\ncardamom\ncardboard\ncardcase\ncardia\ncardiac\ncardiacal\ncardialgia\ncardiff\ncardigan\ncardiidae\ncardinal\ncardinalate\ncardinalfish\ncardinals\ncardinalship\ncardiograph\ncardiographic\ncardiography\ncardioid\ncardiologic\ncardiologist\ncardiology\ncardiomegaly\ncardiomyopathy\ncardiopulmonary\ncardiospasm\ncardiospermum\ncardiovascular\ncarditis\ncardium\ncardoon\ncardroom\ncards\ncardsharp\ncarduelinae\ncarduelis\ncarduus\ncare\ncare-laden\ncared-for\ncareen\ncareer\ncareerism\ncareerist\ncarefree\ncarefreeness\ncareful\ncarefully\ncarefulness\ncaregiver\ncareless\ncareless(p)\ncarelessly\ncarelessness\ncares\ncaress\ncaressd\ncaressed\ncaressing\ncaressingly\ncaret\ncaretaker\ncaretta\ncareworn\ncarex\ncarful\ncargador\ncargo\ncarhop\ncariama\ncariamidae\ncarib\ncaribbean\ncaribou\ncarica\ncaricaceae\ncaricatura\ncaricature\ncaricaturist\ncaries\ncarillon\ncarina\ncarinal\ncarinate\ncaring\ncarious\ncarissa\ncark\ncarking\ncarle\ncarlina\ncarload\ncarlock\ncarlyle\ncarman\ncarmelite\ncarminative\ncarmine\ncarnage\ncarnal\ncarnality\ncarnallite\ncarnally\ncarnassial\ncarnation\ncarnauba\ncarnegie\ncarnegiea\ncarnelian\ncarnival\ncarnivora\ncarnivore\ncarnivorous\ncarnosaur\ncarnosaura\ncarnotite\ncaro\ncarob\ncarol\ncaroler\ncaroling\ncarolinian\ncarom\ncarotene\ncarotenemia\ncarotenoid\ncarotid\ncarousal\ncarouse\ncarousel\ncarouser\ncarp\ncarpal\ncarpe\ncarped\ncarpel\ncarpellary\ncarpellate\ncarpenter\ncarpenteria\ncarpentry\ncarper\ncarpet\ncarpetbag\ncarpetbagger\ncarpeted\ncarpetweed\ncarphology\ncarphophis\ncarpinaceae\ncarping\ncarpinus\ncarpobrotus\ncarpocapsa\ncarpodacus\ncarpophagous\ncarport\ncarpospore\ncarposporic\ncarposporous\ncarrack\ncarrageenin\ncarre\ncarrefour\ncarrel\ncarriage\ncarriageway\ncarried\ncarrier\ncarriole\ncarrion\ncarroll\ncarrom\ncarronade\ncarrot\ncarroty\ncarry\ncarry-over\ncarryall\ncarrycot\ncarrying\ncart\ncarta\ncartage\ncartagena\ncarte\ncartel\ncarter\ncartes\ncartesian\ncarthage\ncarthaginian\ncarthago\ncarthamus\ncarthorse\ncarthusian\ncartilage\ncartilaginification\ncartilaginous\ncarting\ncartload\ncartographer\ncartographic\ncartography\ncarton\ncartoon\ncartoonist\ncartouche\ncartridge\ncartulary\ncartwheel\ncartwright\ncarum\ncaruncle\ncaruncular\ncarunculate\ncarve\ncarved\ncarvel\ncarvel-built\ncarver\ncarving\ncarya\ncaryatid\ncaryatides\ncaryocar\ncaryocaraceae\ncaryophyllaceae\ncaryophyllaceous\ncaryophyllales\ncaryophyllidae\ncaryota\ncarys\ncasa\ncasaba\ncasablanca\ncasanova\ncasaque\ncascade\ncascades\ncascara\ncascarilla\ncase\ncase-hardened\ncaseation\ncasebook\ncased\ncaseharden\ncasehardened\ncasein\ncasemate\ncasemated\ncasement\ncaseous\ncasern\ncasework\ncaseworm\ncash\ncashable\ncashbook\ncashbox\ncashed\ncashew\ncashier\ncashmere\ncasing\ncasino\ncask\ncasket\ncasmerodius\ncasper\ncasque\ncasquet\ncasquetel\ncassandra\ncassareep\ncassava\ncasserole\ncassette\ncassia\ncassino\ncassiope\ncassiopeas\ncassiopeia\ncassiri\ncassiterite\ncassius\ncassock\ncassocked\ncassowary\ncast\ncast-iron\ncast-off(a)\ncastanea\ncastaneous\ncastanet\ncastanopsis\ncastanospermum\ncastaway\ncastaway(a)\ncaste\ncastellan\ncastellated\ncaster\ncastigate\ncastigation\ncastigator\ncastigatory\ncastile\ncastilleja\ncasting\ncastingweight\ncastle\ncastlebuildier\ncastlebuilding\ncastles\ncastling\ncastoff\ncastor\ncastoridae\ncastoroides\ncastrametation\ncastrate\ncastrated\ncastration\ncastrato\ncastries\ncastroism\ncasual\ncasuality\ncasually\ncasualness\ncasualty\ncasuaridae\ncasuariiformes\ncasuarina\ncasuarinaceae\ncasuarinales\ncasuarius\ncasuist\ncasuistic\ncasuistical\ncasuistry\ncasus\ncasuses\ncasuss\ncat\ncat's-claw\ncat's-ear\ncat's-paw\ncat's-tail\ncat-o'-nine-tails\ncatabiosis\ncatabolic\ncatabolism\ncatacala\ncatachresis\ncatachrestic\ncatachrestical\ncataclasm\ncataclinal\ncataclysm\ncataclysmal\ncatacomb\ncatacorner\ncatadromous\ncatadupe\ncatafalque\ncatahedra\ncatalan\ncatalase\ncatalatic\ncatalectic\ncatalectin\ncatalepsy\ncataleptic\ncatalo\ncatalog\ncataloger\ncatalogue\ncatalogued\ncatalonia\ncatalpa\ncatalufa\ncatalysis\ncatalyst\ncatalytic\ncatamaran\ncatamenia\ncatamenial\ncatamount\ncatananche\ncatanddog\ncataphasia\ncataplasia\ncataplasm\ncataplastic\ncatapult\ncatapultic\ncataract\ncatarrh\ncatarrhal\ncatasetum\ncatastrophe\ncatastrophic\ncatastrophically\ncatatonia\ncatatonic\ncatawba\ncatbird\ncatboat\ncatcall\ncatch\ncatcher\ncatches\ncatching\ncatchment\ncatchpenny\ncatchpenny(a)\ncatchpoll\ncatchweed\ncatchword\ncatchy\ncatechesis\ncatechetical\ncatechism\ncatechismal\ncatechist\ncatechistic\ncatechize\ncatecholamine\ncatechu\ncatechumen\ncategorema\ncategorial\ncategoric\ncategorical\ncategorically\ncategories\ncategorization\ncategorized\ncategory\ncatenary\ncatenation\ncatenulate\ncater\ncaterer\ncatering\ncaterpillar\ncaterwaul\ncaterwauling\ncates\ncatfish\ncatgut\ncatharacta\ncatharanthus\ncatharsis\ncathartes\ncathartic\ncathartidae\ncathay\ncathaya\ncathectic\ncathedra\ncathedral\ncatheretic\ncatherine\ncatheter\ncathexis\ncathode\ncathodic\ncatholic\ncatholical\ncatholicism\ncatholicity\ncatholicon\ncatiline\ncation\ncationic\ncatkin\ncatkinate\ncatlike\ncatling\ncatmint\ncatnip\ncato\ncatopsis\ncatoptric\ncatoptrics\ncatoptromancy\ncatoptrophorus\ncatostomid\ncatostomidae\ncatostomus\ncats\ncatskills\ncatspaw\ncatsup\ncattail\ncattalo\ncattiness\ncattle\ncattleman\ncattleship\ncattleya\ncatty\ncatwalk\ncaucasia\ncaucasian\ncaucasus\ncaucus\ncauda\ncaudal\ncaudally\ncaudate\ncaudex\ncauf\ncaught\ncaul\ncaulescent\ncauliflower\ncauline\ncaulk\ncaulked\ncaulophyllum\ncausa\ncausal\ncausalgia\ncausality\ncausally\ncausans\ncausas\ncausation\ncausative\ncausatives\ncause\ncaused\ncauseless\ncauserie\ncauses\ncauseway\ncausidical\ncausing\ncausiticity\ncaustic\ncaustically\ncautel\ncautela\ncautelous\ncauterization\ncauterize\ncauterizer\ncautery\ncaution\ncautionary\ncautious\ncautiously\ncautiousness\ncavalcade\ncavalier\ncavaliere\ncavalry\ncavalryman\ncavatina\ncave\ncaveat\ncaveman\ncavendish\ncavendo\ncavern\ncavernous\ncavesson\ncavia\ncaviar\ncaviare\ncaviidae\ncavil\ncaviler\ncaviling\ncavilling\ncavity\ncavort\ncavy\ncaw\ncayenne\ncayman\ncaymon\ncayuga\ncayuse\ncazique\ncb\ncc\ncd-r\ncd-rom\ncdeception\nce\nceasar\ncease\nceaseless\nceaselessness\nceasing\ncebidae\ncebu\ncebuan\ncebuella\ncebus\ncecal\ncecidomyidae\ncecity\ncecropia\ncecropiaceae\ncecum\ncedar\ncedarn\ncede\ncedendo\ncedere\ncedi\ncedilla\ncedrela\ncedrus\ncefoperazone\ncefotaxime\nceftazidime\nceftriaxone\ncefuroxime\nceiba\nceibo\nceiling\nceilinged\nceja\ncela\ncelandine\ncelare\ncelastraceae\ncelastrus\ncelebes\ncelebrant\ncelebrate\ncelebrated\ncelebrating\ncelebration\ncelebratory\ncelebrity\nceleriac\ncelerity\ncelery\ncelesta\ncelestial\ncelestite\nceliac\ncelibacy\ncelibate\nceliocentesis\ncelioma\ncelioscopy\ncell\ncell-free\ncell-like\ncellar\ncellarage\ncellaret\ncellarway\ncellblock\ncellist\ncello\ncellophane\ncellular\ncellularity\ncellule\ncellulite\ncellulitis\ncelluloid\ncellulose\ncellulosic\ncellulosid\ncelom\ncelosia\ncelsius\ncelt\nceltic\nceltis\nceltuce\ncelui\nceluila\ncement\ncementation\ncemented\ncementite\ncementitious\ncementum\ncemetery\ncen\ncenchrus\ncenobite\ncenobitic\ncenogenesis\ncenogenetic\ncenotaph\ncenozoic\ncenser\ncensor\ncensored\ncensorial\ncensoring\ncensorious\ncensoriousness\ncensurable\ncensure\ncensured\ncensurer\ncensus\ncent\ncentaur\ncentaurea\ncentaurium\ncentaurus\ncentaury\ncentavo\ncentenarian\ncentenary\ncentennial\ncentennially\ncenter\ncenter(a)\ncenter-fire\ncenterboard\ncentered\ncenterfield\ncentering\ncenterline\ncenterpiece\ncentesimal\ncentesimo\ncentesis\ncentigram\ncentiliter\ncentime\ncentimeter\ncentimo\ncentipede\ncentner\ncento\ncentral\ncentral-fire\ncentralist\ncentrality\ncentralization\ncentralize\ncentralized\ncentralizing(a)\ncentrally\ncentranthus\ncentrarchidae\ncentre\ncentrex\ncentric\ncentrical\ncentricalness\ncentrifugal\ncentrifugation\ncentrifuge\ncentriole\ncentripetal\ncentriscidae\ncentrist\ncentrocercus\ncentroid\ncentroidal\ncentrolobium\ncentromere\ncentromeric\ncentropomidae\ncentropomus\ncentropristis\ncentropus\ncentrosema\ncentrosome\ncentrosomic\ncentrospermae\ncentrum\ncentunculus\ncentuple\ncentuplicate\ncenturial\ncenturiate\ncenturion\ncentury\ncephalalgia\ncephalanthera\ncephalexin\ncephalhematoma\ncephalic\ncephalobidae\ncephalochordata\ncephalochordate\ncephalopod\ncephalopoda\ncephalopterus\ncephaloridine\ncephalosporin\ncephalotaceae\ncephalotaxaceae\ncephalotaxus\ncephalothin\ncephalotus\ncepheus\ncepphus\ncerambycidae\nceramic\nceramics\ncerapteryx\nceras\ncerastium\ncerate\nceratitis\nceratodontidae\nceratodus\nceratonia\nceratopetalum\nceratophyllaceae\nceratophyllum\nceratopogon\nceratopogonidae\nceratopsia\nceratopsian\nceratopsidae\nceratopteris\nceratosaur\nceratostomataceae\nceratostomella\nceratotherium\nceratozamia\ncerberus\ncercaria\ncercarial\ncercidiphyllaceae\ncercidiphyllum\ncercidium\ncercis\ncercocebus\ncercopidae\ncercopithecidae\ncercopithecus\ncercospora\ncercosporella\ncereal\ncerealia\ncereals\ncerebellar\ncerebellum\ncerebral\ncerebrally\ncerebration\ncerebrospinal\ncerebrovascular\ncerebrum\ncerecloth\ncerement\nceremonial\nceremonialism\nceremonially\nceremonie\nceremonies\nceremonious\nceremoniously\nceremoniousness\nceremoniuous\nceremony\nceres\nceresin\ncereus\nceric\nceriman\ncerise\ncerium\ncermonie\ncerni\ncernit\ncernuous\ncero\ncerograph\ncerography\nceromancy\nceroplastic\ncerous\nceroxylon\ncerrado\ncert\ncerta\ncertain\ncertain(a)\ncertain(p)\ncertainly\ncertainty\ncertes\ncerthia\ncerthiidae\ncertifiable\ncertificate\ncertificated\ncertification\ncertificatory\ncertified\ncertify\ncertiorari\ncertitude\ncerulean\ncerulescent\ncerumen\nceruminous\nceruse\ncerussite\ncervantes\ncervical\ncervicem\ncervicitis\ncervidae\ncervine\ncervix\ncervus\nceryle\ncesarean\ncesium\ncespitose\ncess\ncessation\ncession\ncesspool\ncest\ncestida\ncestidae\ncestoda\ncestrum\ncestuiquetrust\ncestum\ncestus\ncetacea\ncetacean\ncetera\nceterach\ncetonia\ncetoniidae\ncetorhinidae\ncetorhinus\ncetraria\ncetrimide\nceux\nceylon\nceylonite\ncf.\ncfallacy\ncforgive\ncg\ncgs\nch\ncha-cha\nchablis\nchachalaca\nchacma\nchacun\nchad\nchadian\nchaenactis\nchaenomeles\nchaenopsis\nchaepau\nchaeronea\nchaeta\nchaetal\nchaetodipterus\nchaetodon\nchaetodontidae\nchaetognatha\nchaetognathan\nchafe\nchafed\nchafeweed\nchaff\nchaffe\nchaffer\nchaffinch\nchaffweed\nchaffy\nchafing\nchafingdish\nchagatai\nchagrin\nchagrined\nchain\nchain-smoker\nchained\nchains\nchair\nchairlift\nchairman\nchairmanship\nchaise\nchait\nchaja\nchalaza\nchalazion\nchalcedony\nchalcididae\nchalcis\nchalcocite\nchalcography\nchalcopyrite\nchalcostigma\nchaldron\nchalet\nchalice\nchalk\nchalkpit\nchalks\nchalky\nchallah\nchallenge\nchallengeable\nchallenging\nchallis\nchalons\nchalybeate\nchamaea\nchamaecrista\nchamaecyparis\nchamaecytisus\nchamaedaphne\nchamaeleo\nchamaeleontidae\nchamaemelum\nchamber\nchambered\nchamberlain\nchambermaid\nchamberpot\nchambers\nchambray\nchambre\nchameleon\nchamfer\nchamois\nchamomile\nchamosite\nchamp\nchampagne\nchampaign\nchampain\nchampak\nchampetre\nchampion\nchampionship\nchampleve\nchanar\nchance\nchance-medley\nchancel\nchancellery\nchancellor\nchancellors\nchancellorship\nchancellorsville\nchancery\nchances\nchancre\nchancroid\nchancroidal\nchancrous\nchancy\nchandelier\nchandelle\nchandellefrench\nchandi\nchandler\nchandlery\nchanfron\nchange\nchange-up\nchangeable\nchangeableness\nchanged\nchangeful\nchangeless\nchangelessness\nchangeling\nchanger\nchanges\nchanging\nchangtzu\nchannel\nchannelization\nchannels\nchant\nchanted\nchanter\nchanterelle\nchantey\nchanticleer\nchanting\nchantlike\nchantry\nchaomancy\nchaos\nchaotic\nchaotically\nchap\nchaparajos\nchaparal\nchapatti\nchapel\nchaperon\nchapfallen\nchaplain\nchaplaincy\nchaplainship\nchaplet\nchapleted\nchapman\nchapped\nchaps\nchapter\nchapterhouse\nchapultepec\nchaque\nchaqueta\nchar\nchara\ncharabancs\ncharaceae\ncharacidae\ncharacin\ncharacinidae\ncharacter\ncharacteristic\ncharacteristically\ncharacterize\ncharacterized\ncharacterless\ncharade\ncharades\ncharadrii\ncharadriidae\ncharadriiformes\ncharadrius\ncharakter\ncharales\ncharcoal\ncharcuterie\nchard\nchardonnay\ncharge\nchargeable\ncharged\ncharger\nchargeship\nchari-nile\ncharily\ncharina\ncharing\nchariot\ncharioteer\ncharisma\ncharismatic\ncharitable\ncharitableness\ncharitably\ncharity\ncharivari\ncharlatan\ncharlatanism\ncharlatanry\ncharlatism\ncharlemagne\ncharles\ncharless\ncharleston\ncharley\ncharleyhorse\ncharlotte\ncharlottetown\ncharm\ncharmed\ncharmer\ncharming\ncharmingly\ncharms\ncharnel\ncharolais\ncharon\ncharophyceae\ncharronia\nchart\ncharta\nchartaceous\ncharter\nchartered\nchartism\nchartist\nchartless\nchartreuse\ncharwoman\nchary\ncharybdim\ncharybdis\nchase\nchaser\nchaserbalancer\nchasm\nchasse\nchassemaree\nchassepot\nchasser\nchassis\nchaste\nchastely\nchasten\nchastened\nchasteness\nchastening\nchastise\nchastised\nchastisement\nchastity\nchasuble\nchat\nchateau\nchateaubriand\nchateaux\nchatelaine\nchateura\nchatham\nchatoyant\nchattanooga\nchattel\nchattels\nchatter\nchatterbox\nchatterer\nchattering\nchatti\nchattily\nchatty\nchaucer\nchauffeur\nchauffeuse\nchauki\nchaulmoogra\nchauna\nchaunt\nchaunter\nchauntress\nchausse\nchaussee\nchautauqua\nchauvinism\nchauvinist\nchauvinistic\nchavar\nchawbacon\nche\ncheap\ncheapen\ncheapest\ncheapjack\ncheaply\ncheapness\ncheapskate\ncheat\ncheating(a)\ncheck\ncheckbook\nchecked\nchecker\ncheckerbloom\ncheckered\ncheckers\nchecklist\ncheckmake\ncheckmate\ncheckout\ncheckpoint\ncheckroom\nchecks\ncheckup\nchecquers\ncheddar\ncheek\ncheekbone\ncheeked\ncheeker\ncheekily\ncheekless\ncheekpiece\ncheeks\ncheep\ncheer\ncheerful\ncheerfully\ncheerfulness\ncheering\ncheerleader\ncheerless\ncheerlessly\ncheerlessness\ncheerly\ncheers\ncheery\ncheese\ncheeseboard\ncheeseburger\ncheesecake\ncheesecloth\ncheeselike\ncheesepairings\ncheeseparing\ncheetah\nchef\ncheilanthes\ncheilitis\ncheilosis\ncheiranthus\nchekhov\nchela\nchelate\nchelation\nchelicera\ncheliceral\nchelicerata\nchelicerous\nchelidonium\nchelifer\ncheliferous\nchelone\nchelonethida\nchelonia\nchelonian\ncheloniidae\nchelyabinsk\nchelydra\nchelydridae\nchemakuan\nchemakum\nchemical\nchemically\nchemiluminescence\nchemiluminescent\nchemin\nchemise\nchemisorption\nchemisorptive\nchemist\nchemistry\nchemoreceptive\nchemoreceptor\nchemosis\nchemosurgery\nchemosynthesis\nchemotaxis\nchemotherapeutic\nchemotherapy\nchen\nchenille\nchenopodiaceae\nchenopodium\ncheoplasty\ncheque\nchequer\nchequers\nchercher\nchere\ncheremis\ncherfrench\ncherimoya\ncherish\ncherished\nchernobyl\ncherokee\ncheroot\ncherry\ncherrycolored\ncherrystone\nchersonese\nchert\ncherty\ncherub\ncherubim\nchervil\ncheshire\nchess\nchessboard\nchessman\nchest\nchester\nchesterfield\nchestnut\nchetrum\ncheval\ncheval-de-frise\nchevalglass\nchevalier\nchevaux\ncheviot\nchevron\nchevronne\nchevrotain\nchew\nchewa\nchewable\nchewing\nchewink\nchewy\ncheyenne\nchi\nchian\nchianti\nchiaroscuro\nchiasma\nchiasmal\nchiasmus\nchic\nchicago\nchicane\nchicanery\nchichewa\nchichi\nchichipe\nchick\nchickadee\nchickamauga\nchickaree\nchickasaw\nchicken\nchickenhearted\nchickenpox\nchickens\nchickenshit\nchickeree\nchickpea\nchickweed\nchicle\nchicory\nchicote\nchid\nchide\nchiding\nchief\nchief(a)\nchiefdom\nchiefly\nchieftain\nchieftaincy\nchien\nchiffon\nchiffonier\nchiffonnier\nchiffonniere\nchigetai\nchignon\nchigoe\nchihuahua\nchilblain\nchilblained\nchild\nchildbearing\nchildbirth\nchildcare\nchildhood\nchildish\nchildishly\nchildishness\nchildless\nchildlessness\nchildlike\nchildren\nchilds\nchile\nchilean\nchili\nchiliad\nchill\nchilled\nchilliness\nchilling\nchilly\nchiloe\nchilomastix\nchilomeniscus\nchilomycterus\nchilopoda\nchilopsis\nchiltern\nchimaera\nchimaeridae\nchimakum\nchimaphila\nchimariko\nchimborazo\nchime\nchimera\nchimeras\nchimeric\nchimerical\nchimes\nchiming\nchimney\nchimneypot\nchimneystack\nchimneysweeper\nchimonanthus\nchimpanzee\nchimwini\nchin\nchin-up\nchina\nchinaberry\nchinaman\nchinaware\nchincapin\nchinch\nchincherinchee\nchinchilla\nchinchillidae\nchine\nchinese\nchink\nchinked\nchinking\nchinks\nchinless\nchino\nchinoiserie\nchinoises\nchinook\nchinookan\nchinos\nchintz\nchiococca\nchionanthus\nchios\nchip\nchipboard\nchipewyan\nchipmunk\nchipped\nchippendale\nchipper\nchipping\nchippy\nchips\nchiralgia\nchirography\nchirology\nchiromancy\nchiromantic\nchironomidae\nchironomus\nchiropodist\nchiropractic\nchiropractor\nchiroptera\nchirp\nchirpiness\nchirr\nchirrup\nchirurgery\nchirurgical\nchirurgy\nchisel\nchiseled\nchiseling\nchishona\nchit\nchitchat\nchitin\nchitinous\nchiton\nchitterings\nchitterlings\nchitty\nchivalric\nchivalrous\nchivalry\nchivarras\nchivarros\nchives\nchiwere\nchlamydeous\nchlamydera\nchlamydia\nchlamydiaceae\nchlamydomonadaceae\nchlamydomonas\nchlamydosaurus\nchlamyphorus\nchlamys\nchloasma\nchloe\nchloral\nchlorambucil\nchloramine\nchloramphenicol\nchloranthaceae\nchloranthus\nchlorate\nchlordiazepoxide\nchlorella\nchlorhexidine\nchloride\nchlorination\nchlorine\nchloris\nchlorococcales\nchlorococcum\nchlorofluorocarbon\nchloroform\nchloroformed\nchlorophis\nchlorophoneus\nchlorophthalmidae\nchlorophyceae\nchlorophyll\nchlorophyllose\nchlorophyta\nchloroplast\nchloroprene\nchloroquine\nchlorosis\nchlorothiazide\nchlorotic\nchloroxylon\nchlorpromazine\nchlortetracycline\nchlorura\nchoanocyte\nchoc\nchoc-ice\nchock\nchockablock(p)\nchocolate\nchoctaw\nchoeronycteris\nchoice\nchoir\nchoirboy\nchoirmaster\nchoix\nchoke\nchokecherry\nchoked\nchokedamp\nchokefull\nchoker\nchokey\nchokidar\nchoking\nchokra\nchoky\ncholangiography\ncholangitis\ncholecystectomy\ncholecystitis\ncholelithiasis\ncholelithotomy\ncholer\ncholera\ncholeraic\ncholeric\ncholesterol\ncholine\ncholinergic\ncholla\ncholoepus\nchomp\nchomping\nchon\nchondrichthyes\nchondrite\nchondritic\nchondroma\nchondrosarcoma\nchondrule\nchondrus\nchoo-choo\nchoose\nchooses\nchoosing\nchoosy\nchop\nchopfallen\nchophouse\nchopin\nchopine\nchopped\nchopper\nchoppiness\nchopping\nchoppy\nchops\nchopstick\nchor\nchoragic\nchoragus\nchoral\nchorale\nchorally\nchord\nchordal\nchordamesoderm\nchordata\nchordate\nchordeiles\nchorditis\nchordophone\nchordospartium\nchords\nchore\nchorea\nchoregus\nchoreographer\nchoreographic\nchoreography\nchoric\nchorioallantois\nchorion\nchorionic\nchorioretinitis\nchoriotis\nchorister\nchorizagrotis\nchorizema\nchorography\nchoroid\nchortle\nchorus\nchose\nchosen\nchotahazri\nchough\nchouse\nchoux\nchow\nchowchow\nchowder\nchowderhead\nchpter\nchrestomathy\nchrism\nchrist\nchrist's-thorn\nchristcrossrow\nchristella\nchristen\nchristendom\nchristened\nchristening\nchristian\nchristianism\nchristianity\nchristianly\nchristians\nchristless\nchristlike\nchristmas\nchristmasberry\nchristopher\nchristum\nchromate\nchromatic\nchromatically\nchromatics\nchromatid\nchromatin\nchromatinic\nchromatism\nchromatogenous\nchromatogram\nchromatographic\nchromatographically\nchromatography\nchromatology\nchromatopseudoblepsis\nchromatrope\nchrome\nchromesthesia\nchromeyellow\nchromite\nchromium\nchromoblastomycosis\nchromolithograph\nchromolithography\nchromoplast\nchromosomal\nchromosome\nchromosphere\nchronic\nchronically\nchronicle\nchronicleannals\nchronicler\nchronique\nchronogram\nchronogrammatical\nchronograph\nchronographer\nchronography\nchronologer\nchronological\nchronologically\nchronologist\nchronology\nchronometer\nchronometrical\nchronometry\nchrononhotonthologos\nchronoperates\nchronoscope\nchruchwarden\nchrysalis\nchrysanthemum\nchrysaora\nchrysemys\nchrysobalanus\nchrysoberyl\nchrysochloridae\nchrysochloris\nchrysolepis\nchrysolite\nchrysology\nchrysolophus\nchrysomelidae\nchrysophrys\nchrysophyceae\nchrysophyllum\nchrysophyta\nchrysopidae\nchrysoprase\nchrysopsis\nchrysosplenium\nchrysothamnus\nchrysotherapy\nchrysotile\nchthonian\nchuang-tzu\nchub\nchubbiness\nchubby\nchuck\nchuck-will's-widow\nchuckaluck\nchuckerout\nchuckle\nchucklehead\nchuckwalla\nchudder\nchufa\nchuff\nchug\nchukker\nchum\nchumminess\nchummy\nchump\nchunga\nchungking\nchunk\nchunks\nchunky\nchunnel\nchup\nchupatty\nchurch\nchurch-state\nchurchdom\nchurchdoor\nchurchgoer\nchurchgoing\nchurchill\nchurchillian\nchurchlike\nchurchman\nchurchwarden\nchurchyard\nchurl\nchurlish\nchurlishly\nchurlishness\nchurn\nchurning\nchurr\nchut\nchute\nchutney\nchuvash\nchylaceous\nchyle\nchyliferous\nchylific\nchyme\nchytridiaceae\nchytridiales\nchytridiomycetes\nciao\ncibarious\ncibotium\ncic\ncicada\ncicadellidae\ncicadidae\ncicatrix\ncicatrization\ncicatrize\ncicer\ncicero\ncicerone\nciceronian\ncichlid\ncichlidae\ncichorium\ncicindelidae\ncicisbeo\nciconia\nciconiidae\nciconiiformes\ncicuta\ncider\nciderpress\ncidevant\ncieceronian\nciel\ncienaga\ncigar\ncigarette\ncigarillo\ncigit\ncilia\nciliary\nciliata\nciliate\nciliated\ncilium\ncimabue\ncimarron\ncimeter\ncimetidine\ncimex\ncimicidae\ncimicifuga\ncimmerian\ncinch\ncinchona\ncincinnati\ncinclidae\ncinclus\ncincture\ncinder\ncinderella\ncinders\ncinderwench\ncinema\ncinematic\ncinematograph\ncinematographer\ncinematography\ncineraria\ncinerary\ncineration\ncinereous\ncineri\ncineritious\ncingle\ncingulum\ncinnabar\ncinnamomum\ncinnamon\ncinque\ncinquecento\ncinquefoil\ncinterchange\ncipher\nciprofloxacin\nciratrix\ncirca\ncircadian\ncircaea\ncircaetus\ncircassian\ncirce\ncircean\ncircination\ncircle\ncircles\ncirclet\ncircling\ncircuit\ncircuition\ncircuitous\ncircuitously\ncircuitry\ncircular\ncircular-knit\ncircularity\ncircularization\ncircularly\ncirculate\ncirculating\ncirculating(a)\ncirculation\ncirculative\ncirculatory\ncircumambience\ncircumambient\ncircumambulate\ncircumambulation\ncircumbendibus\ncircumbest\ncircumcise\ncircumcision\ncircumduction\ncircumference\ncircumferential\ncircumflex\ncircumfluent\ncircumforanean\ncircumforaneous\ncircumfuse\ncircumfusion\ncircumgyration\ncircumjacence\ncircumjacent\ncircumlocution\ncircumlocutious\ncircumlocutory\ncircumnavigate\ncircumnavigation\ncircumpolar\ncircumrotation\ncircumrotatory\ncircumscribe\ncircumscribed\ncircumscription\ncircumspect\ncircumspection\ncircumstance\ncircumstances\ncircumstanees\ncircumstantial\ncircumstantially\ncircumvallation\ncircumvent\ncircumvention\ncircumvolution\ncircumvolve\ncircuration\ncircus\ncirque\ncirrhosis\ncirripedia\ncirrocumulus\ncirrostratus\ncirrup\ncirrus\ncirsium\ncisalpine\ncisco\ncismontane\ncistaceae\ncistercian\ncistern\ncisterna\ncistothorus\ncistus\ncit\ncitadel\ncitation\ncite\ncitellus\ncitharichthys\ncither\ncithern\ncities\ncitified\ncitizen\ncitizenry\ncitizenship\ncitlaltepetl\ncito\ncitrange\ncitrine\ncitron\ncitroncirus\ncitroncolored\ncitronwood\ncitrulline\ncitrullus\ncitrus\ncity\ncitywide\ncivet\ncivic\ncivics\ncivies\ncivil\ncivil-libertarian\ncivile\ncivilian\ncivilisan\ncivility\ncivilization\ncivilize\ncivilized\ncivilly\ncivis\ncivism\ncivitas\ncl\nclabber\nclack\nclad\nclade\ncladistics\ncladode\ncladogram\ncladonia\ncladoniaceae\ncladorhyncus\ncladrastis\nclaim\nclaimant\nclaiming\nclairobscur\nclairvoyance\nclairvoyant\nclam\nclamant\nclamatores\nclamatorial\nclambake\nclamber\nclammily\nclammy\nclammyweed\nclamor\nclamorous\nclamoruous\nclamp\nclampdown\nclamshell\nclamydospore\nclan\nclandestine\nclang\nclanger\nclangor\nclangorous\nclangula\nclank\nclanking\nclannish\nclannishly\nclannishness\nclanship\nclansman\nclap\nclapboard\nclapper\nclapperboard\nclapperclaw\nclapping\nclaptrap\nclaque\nclaquer\nclaqueur\nclarence\nclaret\nclaretcolored\nclarichord\nclarification\nclarified\nclarify\nclarifying\nclarinet\nclarinetist\nclarion\nclarity\nclarksburg\nclaro\nclaronet\nclary\nclash\nclashing\nclasp\nclass\nclass-conscious\nclasses\nclassfellow\nclassic\nclassical\nclassicalism\nclassically\nclassicism\nclassicist\nclassicistic\nclassics\nclassifiable\nclassification\nclassificatory\nclassified\nclassifier\nclassifiers\nclassify\nclassis\nclassless\nclassman\nclassmate\nclassroom\nclassy\nclathraceae\nclathrus\nclatter\nclattering\nclaude\nclaudianus\nclaudication\nclaudius\nclausal\nclause\nclauses\nclausiss\nclaustral\nclaustrophobia\nclaustrophobic\nclaustrum\nclavariaceae\nclavate\nclavated\nclaviceps\nclavichord\nclavicipitaceae\nclavicle\nclavier\nclaviform\nclavis\nclaw\nclawback\nclawed\nclawfoot\nclawlike\nclaws\nclay\nclaycold\nclayey\nclaymore\nclaystone\nclaytonia\nclean\nclean-cut\nclean-limbed\nclean-shaven\ncleanable\ncleancut\ncleaned\ncleaner\ncleaners\ncleaning\ncleanliness\ncleanly\ncleanness\ncleanse\ncleansing\ncleanup\nclear\nclear(p)\nclear-cut\nclear-eyed\nclear-sighted\nclearage\nclearance\nclearcut\ncleared\ncleareyedsighted\nclearheaded\nclearing\nclearly\nclearness\nclearway\ncleat\ncleavable\ncleavage\ncleave\ncleaver\ncleavers\ncledge\nclef\ncleft\ncleistes\ncleistothecium\nclem\nclematis\nclemency\nclement\nclementine\nclench\nclenched\ncleopatra\nclepe\nclepsydra\nclerestory\nclergy\nclergyman\ncleric\nclerical\nclericalism\nclericals\ncleridae\nclerihew\nclerk\nclerkship\ncleromancy\nclethra\nclethraceae\nclethrionomys\ncleveland\nclever\ncleverly\ncleverness\nclew\nclich\ncliched\nclick\nclick-clack\nclickety-clack\nclient\nclientage\nclientele\nclientship\ncliff\ncliff-hanging\ncliffhanger\ncliffy\ncliftonia\nclimacteric\nclimactic\nclimate\nclimatic\nclimatically\nclimatology\nclimax\nclimb\nclimbable\nclimber\nclimbing\nclimbing(a)\nclime\nclinal\nclinch\nclincher\ncline\ncling\nclingfish\nclinic\nclinical\nclinically\nclinician\nclinid\nclinidae\nclink\nclinker\nclinker-built\nclinking\nclinocephaly\nclinodactyly\nclinometer\nclinopodium\nclinquant\nclinton\nclintonia\nclio\nclip\nclip-on\nclipboard\nclipped\nclipper\nclipping\nclique\nclitellae\nclitocybe\nclitoral\nclitoria\nclitoris\nclivers\ncloaca\ncloacina\ncloak\ncloaked\ncloakmaker\ncloakroom\nclobber\ncloche\nclock\nclock-watching\nclocking\nclocksmith\nclockwise\nclockwork\nclod\ncloddish\nclodhopper\nclodpated\nclodpoll\nclofibrate\nclog\nclogged\nclogging\ncloisonne\ncloister\ncloistered\ncloisters\nclomiphene\nclomipramine\nclonal\nclone\nclonic\nclonidine\nclonus\nclop\nclorox\nclos\nclosable\nclose\nclose-cropped\nclose-grained\nclose-hauled\nclose-knit\nclose-minded\nclose-packed\nclose-set(a)\nclosed\nclosed(a)\nclosed-captioned\nclosed-chain\nclosed-circuit\nclosed-door\nclosefisted\nclosely\nclosely-held\ncloseness\ncloseout\ncloser\ncloses\nclosest\ncloset\ncloset(a)\ncloseted\nclosetongued\ncloseup\nclosing\nclostridium\nclosure\nclot\ncloth\nclothe\nclothed\nclothes\nclothesbrush\nclotheshorse\nclothesless\nclothesline\nclothespin\nclothespress\nclothier\nclothing\nclotho\nclotpate\nclotpoll\nclotted\ncloture\ncloud\ncloud-covered\ncloud-cuckoo-land\ncloudberry\ncloudburst\ncloudcapt\ncloudcompeller\nclouded\ncloudiness\nclouding\ncloudland\ncloudless\ncloudlessness\ncloudlike\ncloudliness\nclouds\ncloudtopt\ncloudtouching\ncloudy\nclough\nclout\nclove\ncloven\nclover\ncloverleaf\nclovis\nclowder\nclown\nclownish\ncloy\ncloying\ncloyingly\ncloyment\nclub\nclubbable\nclubbing\nclubbish\nclubbism\nclubfoot\nclubfooted\nclubhouse\nclubroom\ncluck\ncluded\ncludless\nclue\nclumber\nclump\nclumsily\nclumsy\nclunch\ncluniac\nclupea\nclupeidae\ncluricaune\nclusia\ncluster\nclustered\nclustering\nclutch\nclutches\nclutter\ncluttered\nclydesdale\nclypeate\nclypeiform\nclypeus\nclyster\ncmoney\ncn\ncnemidophorus\ncnicus\ncnidaria\ncnidoscolus\ncnidosporidia\ncnsiliis\nco\nco-ed\nco-option\nco-star\ncoacervate\ncoacervation\ncoach\ncoachbuilder\ncoaching\ncoachman\ncoachwhip\ncoaction\ncoactive\ncoadjutancy\ncoadjutant\ncoadjutor\ncoadjutrix\ncoadjuvancy\ncoadjuvant\ncoagency\ncoagmentation\ncoagulable\ncoagulase\ncoagulate\ncoagulated\ncoagulation\ncoagulum\ncoaid\ncoal\ncoal-black\ncoalbin\ncoalblack\ncoalesce\ncoalescence\ncoalescent\ncoalescing\ncoalface\ncoalfield\ncoalition\ncoalman\ncoalmine\ncoals\ncoaming\ncoaptation\ncoarctate\ncoarctation\ncoarse\ncoarse-grained\ncoarsely\ncoarsened\ncoarseness\ncoast\ncoastal\ncoaster\ncoastguard\ncoastguardsman\ncoasting\ncoastland\ncoastline\ncoastward\ncoastwise\ncoat\ncoatdress\ncoated\ncoatee\ncoati\ncoating\ncoatrack\ncoats\ncoattail\ncoauthor\ncoax\ncoaxial\ncoaxing\ncoaxingly\ncob\ncobalt\ncobaltite\ncobber\ncobble\ncobbler\ncobblestone\ncobia\ncobitidae\ncoble\ncobnut\ncobol\ncobra\ncobweb\ncobwebs\ncoca\ncocaine\ncoccal\ncoccidae\ncoccidia\ncoccidioidomycosis\ncoccidiosis\ncoccidium\ncoccinellidae\ncoccoid\ncoccoidea\ncoccothraustes\ncocculus\ncoccus\ncoccyx\ncoccyzus\ncoceive\ncocentric\ncocheleate\ncochelous\ncochimi\ncochin\ncochineal\ncochlea\ncochlear\ncochlearia\ncochlearius\ncock\ncock-a-doodle-doo\ncock-a-leekie\ncocka\ncockade\ncockahoop\ncockamamie\ncockarouse\ncockateel\ncockatoo\ncockatrice\ncockchafer\ncockcrow\ncockcrowing\ncocker\ncockerel\ncockeyed\ncockfight\ncockfighting\ncockle\ncocklebur\ncockles\ncockleshell\ncockloft\ncockney\ncockpit\ncockroach\ncockscomb\ncockshut\ncocksparrow\ncockspur\ncocksucker\ncockswain\ncocktail\ncocky\ncocoa\ncocobolo\ncoconut\ncocoon\ncocopa\ncocos\ncocotte\ncocozelle\ncoction\ncocus\ncocuswood\ncocytus\ncod\ncodariocalyx\ncoddle\ncoddled\ncode\ncodefendant\ncodeine\ncodem\ncodex\ncodger\ncodiaeum\ncodicil\ncodification\ncodified\ncodify\ncoding\ncodlin\ncodling\ncodon\ncodpiece\ncoeducation\ncoefficiency\ncoefficient\ncoelacanth\ncoelebs\ncoelenterate\ncoelenteron\ncoelestibus\ncoeliac\ncoeliacpassion\ncoelitus\ncoelo\ncoeloglossum\ncoelogyne\ncoelophysis\ncoelostat\ncoelum\ncoemption\ncoenzyme\ncoepit\ncoequal\ncoerce\ncoercion\ncoercive\ncoereba\ncoerebidae\ncoetaneous\ncoetanian\ncoetera\ncoeternal\ncoeur\ncoeval\ncoevals\ncoevous\ncoexist\ncoexistence\ncoexistent\ncoexisting\ncoextension\ncoextensive\ncofactor\ncoffea\ncoffee\ncoffeeberry\ncoffeecake\ncoffeepot\ncoffer\ncofferdam\ncoffin\ncofounder\ncog\ncogency\ncogent\ncogged\ncoggery\ncogitable\ncogitare\ncogitate\ncogitation\ncogitative\ncogito\ncognac\ncognate\ncognation\ncognee\ncognition\ncognitive\ncognitively\ncognizable\ncognizance\ncognizant\ncognomen\ncognominal\ncognomination\ncognosce\ncognoscence\ncognoscere\ncognoscible\ncohabilition\ncohabitation\ncoheir\ncoheirship\ncohere\ncoherence\ncoherent\ncoherently\ncohering\ncohesion\ncohesive\ncohesiveness\ncohibit\ncohibition\ncohibitive\ncoho\ncohobate\ncohort\ncohue\ncoif\ncoiffeur\ncoiffeuse\ncoiffure\ncoign\ncoigue\ncoil\ncoiled\ncoiling\ncoin\ncoinage\ncoincide\ncoincidence\ncoincident\ncoincidentally\ncoiner\ncoins\ncoinsurance\ncoir\ncoistril\ncoital\ncoition\ncojugation\ncojuror\ncoke\ncol\ncola\ncolander\ncolaptes\ncolature\ncolbert\ncolchicaceae\ncolchicum\ncolchine\ncolchis\ncold\ncold-blooded\ncold-bloodedly\ncoldblooded\ncoldhearted\ncoldly\ncoldness\ncoleonyx\ncoleoptera\ncoleridge\ncoleridgian\ncoles\ncoleslaw\ncoleus\ncolic\ncolicky\ncolicroot\ncolima\ncolinus\ncoliseum\ncolitis\ncoll\ncollaboration\ncollaborator\ncollage\ncollagen\ncollapse\ncollapsed\ncollapsible\ncollar\ncollard\ncollards\ncollarless\ncollate\ncollateral\ncollation\ncolleague\ncolleagueship\ncollect\ncollectanea\ncollected\ncollectedly\ncollectible\ncollecting\ncollection\ncollective\ncollectively\ncollectiveness\ncollectivism\ncollectivist\ncollectivization\ncollectivized\ncollector\ncolleen\ncollege\ncollegial\ncollegian\ncollegiate\ncollembola\ncollembolan\ncollet\ncollide\ncollider\ncollie\ncollied\ncollier\ncolliery\ncolligate\ncolligation\ncollimation\ncollimator\ncollinear\ncollins\ncollinsia\ncollinsonia\ncolliquation\ncolliquative\ncolliquefaction\ncollision\ncollocalia\ncollocate\ncollocation\ncollocution\ncollogue\ncolloid\ncollop\ncolloquial\ncolloquialism\ncolloquially\ncolloquium\ncolloquy\ncollotype\ncolluctation\ncollude\ncollusion\ncollusive\ncollusory\ncolluvies\ncollyrium\ncolobus\ncolocasia\ncologne\ncolombia\ncolombian\ncolombo\ncolon\ncolonel\ncolonial\ncolonialism\ncolonialist\ncolonic\ncolonist\ncolonization\ncolonize\ncolonized\ncolonizer\ncolonnade\ncolonnaded\ncolony\ncolophon\ncolophony\ncolor\ncolor-blind\ncolorable\ncoloradan\ncolorado\ncoloration\ncoloratura\ncolored\ncolorful\ncolori\ncolorific\ncolorimeter\ncolorimetric\ncolorimetry\ncoloring\ncolorist\ncolorless\ncolorlessness\ncolors\ncolossal\ncolosseum\ncolossus\ncolostomy\ncolostrum\ncolpitis\ncolpocele\ncolpocystitis\ncolporteur\ncolpoxerosis\ncolt\ncolter\ncoltish\ncolton\ncoltsfoot\ncoluber\ncolubridae\ncolubrina\ncolumba\ncolumbararium\ncolumbary\ncolumbia\ncolumbian\ncolumbidae\ncolumbiformes\ncolumbine\ncolumbium\ncolumbo\ncolumbus\ncolumella\ncolumn\ncolumnar\ncolumnea\ncolumned\ncolumniation\ncolumniform\ncolumnist\ncolures\ncolussus\ncolutea\ncolza\ncoma\ncomae\ncomanche\ncomandra\ncomate\ncomatose\ncomb\ncomb-out\ncombat\ncombatant\ncombatants\ncombative\ncombatively\ncombativeness\ncombe\ncombed\ncomber\ncombinable\ncombination\ncombinations\ncombinative\ncombinatorial\ncombine\ncombined\ncomble\ncombo\ncombretaceae\ncombretum\ncomburent\ncombustibility\ncombustible\ncombustion\ncome\ncome-at-able\ncomeabout\ncomedian\ncomedie\ncomedienne\ncomedietta\ncomedown\ncomedy\ncomeliness\ncomely\ncomer\ncomes\ncomestible\ncomestibles\ncomet\ncometary\ncomfit\ncomfort\ncomfortable\ncomfortableness\ncomfortably\ncomforted\ncomforter\ncomforting\ncomfortingly\ncomfortless\ncomforts\ncomfrey\ncomic\ncomica\ncomical\ncomicality\ncomically\ncomidie\ncoming\ncoming(a)\ncomitatus\ncomitia\ncomity\ncomma\ncommand\ncommandant\ncommandeer\ncommander\ncommandership\ncommanding\ncommandment\ncommando\ncomme\ncommedian\ncommedy\ncommelina\ncommelinaceae\ncommelinidae\ncommemorate\ncommemoration\ncommemorative\ncommence\ncommencement\ncommend\ncommendable\ncommendat\ncommendatio\ncommendation\ncommendatory\ncommensal\ncommensalism\ncommensally\ncommensurability\ncommensurable\ncommensurate\ncommensurateness\ncomment\ncommentary\ncommentator\ncommerce\ncommercial\ncommercialization\ncommercialized\ncommercially\ncommination\ncomminatory\ncommingle\ncomminute\ncomminution\ncommiphora\ncommiserate\ncommiseration\ncommiserative\ncommissar\ncommissariat\ncommissary\ncommission\ncommissionaire\ncommissioned\ncommissioner\ncommissriat\ncommissure\ncommisvoyageur\ncommit\ncommitedness\ncommitment\ncommittal\ncommitted\ncommittee\ncommitteeman\ncommitteewoman\ncommix\ncommixion\ncommixtion\ncommixture\ncommodatus\ncommode\ncommodious\ncommodity\ncommodore\ncommon\ncommon-law(p)\ncommonage\ncommonality\ncommonalty\ncommoner\ncommoners\ncommonly\ncommonness\ncommonplace\ncommons\ncommonsense\ncommonweal\ncommonwealth\ncommorant\ncommotion\ncommunal\ncommunally\ncommune\ncommunibus\ncommunicable\ncommunicant\ncommunicate\ncommunicated\ncommunicating\ncommunication\ncommunicational\ncommunicative\ncommunicativeness\ncommunicator\ncommunicatory\ncommunion\ncommunique\ncommunism\ncommunist\ncommunistic\ncommunity\ncommunity(a)\ncommunization\ncommutability\ncommutable\ncommutate\ncommutation\ncommutative\ncommutator\ncommute\ncommuter\ncommutual\ncomon\ncomoros\ncompact\ncompaction\ncompactly\ncompactness\ncompages\ncompagination\ncompanion\ncompanionability\ncompanionable\ncompanionate\ncompanionship\ncompanionway\ncompany\ncomparable\ncomparably\ncomparative\ncomparatively\ncompare\ncompared\ncomparison\ncomparisons\ncompartition\ncompartment\ncompartmental\ncompartmented\ncompartments\ncompass\ncompassion\ncompassionate\ncompatibility\ncompatible\ncompatibly\ncompatriot\ncompeer\ncompel\ncompellation\ncompelled\ncompelling\ncompend\ncompendious\ncompendium\ncompensable\ncompensate\ncompensated\ncompensating\ncompensation\ncompensatory\ncompense\ncompere\ncompete\ncompetence\ncompetency\ncompetent\ncompetently\ncompeting(a)\ncompetition\ncompetitive\ncompetitively\ncompetitiveness\ncompetitor\ncompilation\ncompile\ncompiler\ncomplacency\ncomplacent\ncomplacently\ncomplain\ncomplainer\ncomplaining(a)\ncomplainingly\ncomplaint\ncomplaisance\ncomplaisant\ncomplement\ncomplemental\ncomplementarity\ncomplementary\ncomplementation\ncomplete\ncompleted\ncompletely\ncompleteness\ncompleting\ncompletion\ncomplex\ncomplexed\ncomplexifier\ncomplexion\ncomplexity\ncomplexly\ncomplexness\ncomplexus\ncompliance\ncompliant\ncomplicate\ncomplicated\ncomplicatedness\ncomplication\ncomplice\ncomplicity\ncompliment\ncomplimentary\ncompliments\ncompline\ncomplot\ncomply\ncomplying\ncompo\ncomponent\ncomponent(a)\ncomponere\ncomport\ncomportment\ncompos\ncompose\ncomposed\ncomposer\ncomposing\ncomposingframe\ncompositae\ncomposite\ncomposition\ncompositional\ncompositor\ncompost\ncomposure\ncompote\ncompound\ncompounded\ncomprador\ncomprehend\ncomprehensibility\ncomprehensible\ncomprehension\ncomprehensive\ncomprehensively\ncomprehensiveness\ncomprendre\ncompress\ncompressed\ncompressibility\ncompressible\ncompression\ncompressor\ncomprise\ncomprised\ncomprobation\ncompromise\ncompromised\ncompromising\ncompsognathus\ncompt\ncomptant\ncompte\ncompter\ncomptes\ncomptonia\ncomptroller\ncomptrollership\ncompulsatory\ncompulsion\ncompulsive\ncompulsively\ncompulsiveness\ncompulsorily\ncompulsory\ncompunction\ncompunctious\ncompurgation\ncomputable\ncomputation\ncomputational\ncomputationally\ncompute\ncomputer\ncomputerized\ncomrade\ncomradely\ncomradeship\ncomtation\ncon\nconacaste\nconakry\nconation\nconatu\nconatus\nconbergent\nconcamerate\nconcameration\nconcatenation\nconcave\nconcavely\nconcavity\nconcavo-convex\nconceal\nconcealed\nconcealing\nconcealment\nconcede\nconceit\nconceited\nconceitedly\nconceitedness\nconceivable\nconceivableness\nconceivably\nconceive\nconceived\nconcentrate\nconcentrated\nconcentratin\nconcentration\nconcentric\nconcentricity\nconcentual\nconcept\nconception\nconceptional\nconceptions\nconceptive\nconceptual\nconceptualism\nconceptualistic\nconceptualization\nconceptually\nconcern\nconcerned\nconcernedly\nconcerning\nconcert\nconcert-goer\nconcerted\nconcertina\nconcerto\nconcession\nconcessionaire\nconcessional\nconcessive\nconcesso\nconcetto\nconch\nconcha\nconchfish\nconchoid\nconchoidal\nconchologist\nconchology\nconcierge\nconciliate\nconciliating\nconciliation\nconciliatory\nconciliatrix\nconcinnity\nconcious\nconciousness\nconcise\nconcisely\nconciseness\nconcision\nconclave\nconclliatory\nconclude\nconcluding\nconclusion\nconclusions\nconclusive\nconclusively\nconclusiveness\nconcoct\nconcoction\nconcomitance\nconcomitant\nconcord\nconcordance\nconcordant\nconcordat\nconcordia\nconcordiam\nconcords\nconcours\nconcourse\nconcremation\nconcrete\nconcretely\nconcreteness\nconcretion\nconcretism\nconcretistic\nconcubinage\nconcubine\nconcupiscence\nconcupiscent\nconcur\nconcurrence\nconcurrent\nconcurrently\nconcurring\nconcussion\ncondemn\ncondemnable\ncondemnation\ncondemnatory\ncondemned\ncondensation\ncondense\ncondensed\ncondensed(a)\ncondenser\ncondensing\ncondescend\ncondescending\ncondescendingly\ncondescension\ncondign\ncondiment\ncondisciple\nconditae\ncondition\nconditional\nconditionality\nconditionally\nconditioned\nconditioner\nconditioning\nconditions\ncondole\ncondolence\ncondolenec\ncondom\ncondominium\ncondonation\ncondone\ncondor\ncondottiere\nconduce\nconducement\nconducive\nconduciveness\nconduct\nconductance\nconducted\nconducting\nconduction\nconductive\nconductivity\nconductor\nconductress\nconduit\nconduplicate\ncondylar\ncondyle\ncondylura\ncone\nconeflower\nconenose\nconepatus\nconeshaped\nconestoga\nconey\nconfabulate\nconfabulation\nconfection\nconfectionary\nconfectioner\nconfectionery\nconfederacy\nconfederate\nconfederated\nconfederates\nconfederation\nconfer\nconferee\nconference\nconferva\nconfess\nconfesses\nconfession\nconfessional\nconfessions\nconfessor\nconfetti\nconfidant\nconfidante\nconfide\nconfidence\nconfident\nconfidente\nconfidential\nconfidentiality\nconfidentially\nconfidently\nconfiding\nconfiguration\nconfigurational\nconfigured\nconfine\nconfined\nconfinement\nconfines\nconfining\nconfirm\nconfirmable\nconfirmation\nconfirmatory\nconfirmed\nconfiscate\nconfiscation\nconfiture\nconflagration\nconflexure\nconflict\nconflicting\nconfluence\nconfluenee\nconfluent\nconflux\nconfluxible\nconform\nconformable\nconformably\nconformance\nconformation\nconforming\nconformist\nconformity\nconfound\nconfounded\nconfoundedly\nconfounding\nconfraternity\nconfrere\nconfrication\nconfront\nconfrontation\nconfrontational\nconfucian\nconfucianism\nconfucius\nconfusable\nconfuse\nconfused\nconfusedly\nconfusedness\nconfusing\nconfusion\nconfutable\nconfutation\nconfute\nconfuted\nconfuting\nconga\nconge\ncongeal\ncongealed\ncongelation\ncongener\ncongeneric\ncongenial\ncongeniality\ncongenially\ncongenialness\ncongenital\ncongenite\nconger\ncongeries\ncongested\ncongestion\ncongestive\nconglaciation\nconglobation\nconglomerate\nconglomeration\nconglutinate\nconglutination\ncongo\ncongolese\ncongou\ncongratulate\ncongratulation\ncongratulations\ncongratulatory\ncongregate\ncongregation\ncongregational\ncongregationalism\ncongregationalist\ncongress\ncongressional\ncongressman\ncongresssexual\ncongreve\ncongridae\ncongruence\ncongruent\ncongruity\ncongruous\nconic\nconical\nconically\nconidiophore\nconidium\nconifer\nconiferales\nconiferopsida\nconiferous\nconiform\nconilurus\nconima\nconiogramme\nconium\nconjectural\nconjecturality\nconjecture\nconjoin\nconjoined\nconjoint\nconjointly\nconjugal\nconjugally\nconjugat\nconjugate\nconjugation\nconjunct\nconjunction\nconjunctions\nconjunctiva\nconjunctive\nconjunctivitis\nconjuncture\nconjuration\nconjure\nconjurer\nconjuriation\nconjuring\nconjuror\nconk\nconnaitre\nconnaraceae\nconnarus\nconnate\nconnatural\nconnaturality\nconnaturalize\nconnaturalness\nconnect\nconnected\nconnecticut\nconnecticuter\nconnecting\nconnection\nconnections\nconnective\nconned\nconnivance\nconnive\nconnochaetes\nconnoisseur\nconnotate\nconnotation\nconnotational\nconnotative\nconnote\nconnu\nconnubial\nconocarpus\nconocidos\nconoclinium\nconodont\nconodonta\nconoid\nconopodium\nconoscent\nconospermum\nconover\nconoy\nconquer\nconquerable\nconquering\nconquering(a)\nconqueror\nconquest\nconquistador\nconradina\ncons\nconsanguineous\nconsanguinity\nconscia\nconscience\nconscience-smitten\nconscienceless\nconsciencestricken\nconscientia\nconscientiae\nconscientious\nconscientiousness\nconscionable\nconscious\nconscious(p)\nconsciously\nconsciousness\nconscire\nconscript\nconscription\nconsecate\nconsecrate\nconsecrated\nconsecration\nconsectary\nconsecutio\nconsecution\nconsecutive\nconsecutively\nconsecutiveness\nconsensual\nconsensus\nconsent\nconsentaneous\nconsentaneousness\nconsenting\nconsequence\nconsequences\nconsequent\nconsequential\nconsequentially\nconsequently\nconservancy\nconservation\nconservatism\nconservative\nconservatively\nconservatives\nconservator\nconservatory\nconservatrix\nconserve\nconserved\nconserving\nconsider\nconsiderable\nconsiderate\nconsiderately\nconsideration\nconsidered\nconsiderer\nconsidering\nconsign\nconsignee\nconsigner\nconsignificative\nconsignment\nconsilience\nconsist\nconsistence\nconsistency\nconsistent\nconsistently\nconsistorial\nconsistory\nconsociation\nconsolable\nconsolation\nconsolatory\nconsole\nconsolida\nconsolidate\nconsolidated\nconsolidation\nconsolidative\nconsols\nconsomme\nconsonance\nconsonant\nconsonantal\nconsort\nconsortium\nconsortship\nconspecific\nconspection\nconspectuity\nconspectus\nconspicious\nconspicuity\nconspicuous\nconspicuously\nconspicuousness\nconspiracy\nconspirator\nconspiratorial\nconspire\nconstable\nconstabulary\nconstancy\nconstant\nconstantan\nconstantly\nconstat\nconstellation\nconsternation\nconstipate\nconstipated\nconstipation\nconstituency\nconstituent\nconstituent(a)\nconstitute\nconstituted\nconstitutes\nconstituting\nconstitution\nconstitutional\nconstitutionalism\nconstitutionalist\nconstitutionality\nconstitutionally\nconstrach\nconstrain\nconstrained\nconstrainedly\nconstraint\nconstrict\nconstricted\nconstricting\nconstriction\nconstrictor\nconstringe\nconstruable\nconstruct\nconstruction\nconstructive\nconstructive-metabolic(a)\nconstructively\nconstructiveness\nconstructivism\nconstructivist\nconstrue\nconsubstantial\nconsubstantiation\nconsuecere\nconsuescere\nconsuetude\nconsuetudedustoor\nconsuetudinary\nconsuetudinis\nconsuetudo\nconsul\nconsular\nconsulate\nconsulship\nconsult\nconsultation\nconsultum\nconsumable\nconsume\nconsumed\nconsumer\nconsumere\nconsuming\nconsummate\nconsummated\nconsummation\nconsummatum\nconsumption\nconsumptive\ncontact\ncontadino\ncontagion\ncontagious\ncontagiously\ncontain\ncontained\ncontainer\ncontainerful\ncontainerized\ncontainers\ncontaining\ncontainment\ncontaminant\ncontaminate\ncontaminated\ncontamination\ncontaminative\ncontango\nconte\ncontemn\ncontemper\ncontemplate\ncontemplation\ncontemplative\ncontemporaneity\ncontemporaneous\ncontemporaneously\ncontemporary\ncontemporation\ncontempt\ncontemptible\ncontemptibly\ncontemptin\ncontemptuous\ncontemptuously\ncontemptuousness\ncontend\ncontending\ncontent\ncontent(p)\ncontented\ncontentedly\ncontentedness\ncontention\ncontentious\ncontentiousness\ncontentless\ncontentment\ncontents\nconterminable\nconterminate\nconterminous\ncontesseration\ncontest\ncontestable\ncontestant\ncontestation\ncontext\ncontextual\ncontextually\ncontexture\ncontiguity\ncontiguous\ncontinence\ncontinent\ncontinent-wide\ncontinental\ncontinentals\ncontingence\ncontingency\ncontingens\ncontingent\ncontingents\ncontinual\ncontinually\ncontinuance\ncontinuation\ncontinue\ncontinued\ncontinuing\ncontinuity\ncontinuous\ncontinuously\ncontinuousness\ncontinuum\nconto\ncontopus\ncontort\ncontorted\ncontortion\ncontortionist\ncontour\ncontra\ncontraband\ncontrabandist\ncontrabass\ncontrabasso\ncontrabassoon\ncontraception\ncontraceptive\ncontract\ncontracted\ncontractile\ncontractility\ncontracting\ncontraction\ncontractor\ncontractual\ncontractually\ncontracture\ncontradicente\ncontradict\ncontradiction\ncontradictorily\ncontradictoriness\ncontradictory\ncontradistinction\ncontrafagotto\ncontrail\ncontraindicate\ncontraindication\ncontraire\ncontralateral\ncontralto\ncontraposition\ncontrapuntal\ncontrapuntist\ncontraria\ncontrarian\ncontrariant\ncontraries\ncontrariety\ncontrarily\ncontrariness\ncontrarious\ncontrariwise\ncontrary\ncontrast\ncontrasted\ncontrasting\ncontrastingly\ncontrastive\ncontrasty\ncontrate\ncontravallation\ncontravene\ncontravention\ncontre\ncontrecoup\ncontrectation\ncontretemps\ncontribute\ncontribution\ncontributor\ncontrite\ncontrition\ncontrivance\ncontrive\ncontrived\ncontriving\ncontrol\ncontrollable\ncontrolled\ncontrollership\ncontrolling\ncontroversial\ncontroversialist\ncontroversially\ncontroversy\ncontrovert\ncontrovertible\ncontrovertist\ncontumacious\ncontumacy\ncontumelious\ncontumely\ncontund\ncontuse\ncontusion\nconundrum\nconurbation\nconuropsis\nconvalescence\nconvalescent\nconvallaria\nconvallariaceae\nconvection\nconvector\nconvenance\nconvene\nconvener\nconvenience\nconveniences\nconvenient\nconveniently\nconvent\nconventicle\nconvention\nconventional\nconventionalism\nconventionality\nconventionalized\nconventionally\nconventioneer\nconventions\nconventual\nconverge\nconvergence\nconvergency\nconvergent\nconverging\nconverging(a)\nconversable\nconversant\nconversant(p)\nconversation\nconversational\nconversationalist\nconversationist\nconversazione\nconverse\nconversely\nconversing\nconversion\nconvert\nconverted\nconverter\nconvertibility\nconvertible\nconvex\nconvexity\nconvexly\nconvexo-concave\nconvey\nconveyance\nconveyancer\nconveyancing\nconveyed\nconveyer\nconvict\nconviction\nconvince\nconvinced\nconvinced(p)\nconvincement\nconvincible\nconvincing\nconvincingly\nconvincingness\nconvivial\nconviviality\nconvivially\nconvocate\nconvocation\nconvoke\nconvolute\nconvoluted\nconvolution\nconvolvulaceae\nconvolvulus\nconvoy\nconvulse\nconvulsed\nconvulsion\nconvulsions\nconvulsive\nconvulsively\nconyza\ncoo\ncooing\ncook\ncookbook\ncooked\ncooker\ncookery\ncookfire\ncookhouse\ncookie\ncooking\ncookoo\ncookout\ncooks\ncookshop\ncookstove\ncooky\ncool\ncoolant\ncoold\ncooled\ncooler\ncoolheaded\ncoolidge\ncoolie\ncooling\ncoolly\ncoolness\ncooly\ncoon\ncoonciseness\ncoondog\ncoonhound\ncoons\ncoontie\ncoop\ncooper\ncooperate\ncooperating\ncooperation\ncooperative\ncooperator\ncooptation\ncoordinate\ncoordinated\ncoordinately\ncoordinates\ncoordinating(a)\ncoordination\ncoordinator\ncoot\ncooter\ncop\ncopacetic\ncopaiba\ncopal\ncopalite\ncoparcener\ncoparceny\ncopartner\ncopartnership\ncope\ncopehan\ncopenhagen\ncopepod\ncopepoda\ncoper\ncopernican\ncopernicia\ncopernicus\ncopetitive\ncopia\ncopied\ncopilot\ncoping\ncopingstone\ncopious\ncopiousness\ncoplanar\ncopolymer\ncoportion\ncopout\ncopper\ncopper-bottomed\ncoppercolored\ncopperhead\ncopperplate\ncoppersmith\ncopperware\ncoppery\ncoppice\ncopra\ncoprinaceae\ncoprinus\ncoprolalia\ncoprolite\ncopse\ncopt\ncopter\ncoptic\ncoptis\ncopula\ncopular\ncopulationsex\ncopy\ncopybook\ncopycat\ncopyhold\ncopyholder\ncopying\ncopyist\ncopyright\ncopywriter\ncoquet\ncoquetry\ncoquette\ncoquetting\ncoquettish\ncoquettishly\ncoquillage\ncoquille\ncor\ncoracias\ncoraciidae\ncoraciiformes\ncoracle\ncoragyps\ncoral\ncoralbells\ncoralberry\ncorallorhiza\ncoralwood\ncoram\ncorbeille\ncorbel\ncorbelled\ncorbina\ncorchorus\ncord\ncorda\ncordage\ncordaitaceae\ncordaitales\ncordaites\ncordate\ncordated\ncorded\ncordgrass\ncordia\ncordial\ncordiale\ncordiality\ncordierite\ncordiform\ncordite\ncorditis\ncordless\ncordoba\ncordon\ncordovan\ncords\ncorduroy\ncorduroy(a)\ncordwain\ncordwainer\ncordwood\ncordylidae\ncordyline\ncordylus\ncore\ncoreference\ncoreferential\ncoregonidae\ncoregonus\ncoreidae\ncoreligionist\ncoreopsis\ncorespondent\ncorgi\ncoriaceous\ncoriander\ncoriandrum\ncorinth\ncorinthian\ncorinthians\ncoriolanis\ncoriolanus\ncorixa\ncorixidae\ncork\ncorkage\ncorked\ncorker\ncorking\ncorkscreq\ncorkscrew\ncorkwood\ncorm\ncormorant\ncormous\ncorn\ncorn-fed\ncornaceae\ncornaro\ncornbread\ncorncob\ncorncrake\ncornea\ncorneal\ncorned\ncorneous\ncorner\ncorners\ncornerstone\ncornet\ncornetapistons\ncornetfish\ncornfield\ncornflower\ncornhusk\ncornhusker\ncornhusking\ncornice\ncornicultate\ncornish\ncornishman\ncornishwoman\ncornmeal\ncorno\ncornopean\ncornpone\ncornshucking\ncornsmut\ncornstarch\ncornu\ncornucopia\ncornus\ncornute\ncornuted\ncornwall\ncornwallis\ncorolla\ncorollary\ncorona\ncoronach\ncoronary\ncoronat\ncoronation\ncoroner\ncoronet\ncoroneted\ncoronets\ncoronilla\ncoropuna\ncorozo\ncorpora\ncorporal\ncorporality\ncorporate\ncorporation\ncorpore\ncorporeal\ncorporeity\ncorps\ncorpse\ncorpselike\ncorpulence\ncorpulent\ncorpus\ncorpuscle\ncorpuscular\ncorradiation\ncorral\ncorrect\ncorrectable\ncorrected\ncorrection\ncorrectional\ncorrectionsmake\ncorrectitude\ncorrective\ncorrectly\ncorrectness\ncorregidor\ncorrelate\ncorrelation\ncorrelational\ncorrelative\ncorrepondence\ncorrespond\ncorrespondence\ncorrespondent\ncorresponding\ncorrespondingly\ncorridor\ncorridors\ncorrigenda\ncorrigendum\ncorrigible\ncorrival\ncorrivalry\ncorrivalship\ncorrivation\ncorroborant\ncorroborate\ncorroborated\ncorroboration\ncorroborative\ncorrode\ncorroded\ncorroding\ncorrosion\ncorrosive\ncorrugate\ncorrugated\ncorrugation\ncorrupt\ncorrupted\ncorruptibility\ncorruptible\ncorrupting\ncorruption\ncorruptissima\ncorruptive\ncorruptly\ncorruptness\ncorsage\ncorsair\ncorse\ncorselet\ncorset\ncorsican\ncorso\ncortaderia\ncortas\ncortege\ncortes\ncortex\ncortical\ncortically\ncorticium\ncortico-hypothalamic\ncorticoafferent\ncorticoefferent\ncorticosteroid\ncorticosterone\ncortina\ncortinariaceae\ncortinarius\ncortisone\ncortland\ncorto\ncorundom\ncoruscate\ncoruscation\ncorvee\ncorvette\ncorvidae\ncorvine\ncorvus\ncoryanthes\ncorybantic\ncorydalidae\ncorydalis\ncorydalus\ncorylaceae\ncorylopsis\ncorylus\ncorymb\ncorymbose\ncorypha\ncoryphaenidae\ncoryphantha\ncoryphee\ncorypheus\ncorythosaur\ncos\ncosa\ncoscinomancy\ncoscoroba\ncosecant\ncoseismic\ncosey\ncosignatory\ncosigner\ncosine\ncosm\ncosmetic\ncosmetically\ncosmetician\ncosmetics\ncosmetologist\ncosmic\ncosmical\ncosmocampus\ncosmogony\ncosmographer\ncosmography\ncosmolatry\ncosmologic\ncosmologist\ncosmology\ncosmoplast\ncosmopolitan\ncosmopolitanism\ncosmopolite\ncosmorama\ncosmos\ncosmotron\ncossack\ncosset\ncost\ncost-plus\ncosta\ncostal\ncostanoan\ncostate\ncosterman\ncostermonger\ncostia\ncostiasis\ncosting\ncostive\ncostiveness\ncostless\ncostliness\ncostly\ncostmary\ncostochondritis\ncosts\ncostume\ncostumed\ncostumier\ncostusroot\ncosy\ncot\ncotacachi\ncotangent\ncote\ncotenancy\ncotenant\ncoterie\ncothurnus\ncotidal\ncotillion\ncotillon\ncotinga\ncotingidae\ncotinus\ncotoneaster\ncotopaxi\ncotquean\ncotswold\ncotswolds\ncotta\ncottage\ncottager\ncotter\ncottidae\ncottier\ncotton\ncottonseed\ncottonweed\ncottonwick\ncottonwood\ncottony\ncottus\ncotula\ncoturnix\ncotyledon\ncoucal\ncouch\ncouchant\ncouchant(ip)\ncouchette\ncoucicouci\ncougar\ncough\ncould\ncoulee\ncouleur\ncouleuvres\ncoulisse\ncoulisses\ncoulomb\ncoumarouna\ncouncil\ncouncillorship\ncouncilman\ncouncilor\ncouncilwoman\ncounsel\ncounsellor\ncounselor\ncounselorship\ncount\ncountable\ncountdown\ncounted\ncountenance\ncounteous\ncounter\ncounter-sabotage\ncounteract\ncounteracting\ncounteraction\ncounteractive\ncounterattack\ncounterattraction\ncounterbalance\ncounterbalanced\ncounterbalancing\ncounterblast\ncounterblow\ncounterbombardment\ncounterbore\ncounterchange\ncountercharm\ncountercheck\ncounterclaim\ncounterclockwise\ncounterculture\ncountercurrent\ncounterespionage\ncounterexample\ncounterfactual\ncounterfactuality\ncounterfeit\ncounterfire\ncounterfoil\ncounterglow\ncounterinsurgency\ncounterintelligence\ncounterirritant\ncounterjumper\ncounterman\ncountermand\ncountermarch\ncountermarching\ncountermark\ncountermeasure\ncountermine\ncounteroffensive\ncounteroffer\ncounterpane\ncounterpart\ncounterplot\ncounterpoint\ncounterpoise\ncounterpoison\ncounterproductive\ncounterproject\ncounterproposal\ncounterpunch\ncounterrevolution\ncounterrevolutionary\ncounterrevolutionist\ncounterscarp\ncountershot\ncountersign\ncountersignature\ncounterspy\ncountersubversion\ncountertenor\ncountervail\ncountervailing\ncountervall\ncounterweight\ncounterwork\ncountess\ncounting\ncountinghouse\ncountless\ncountrified\ncountry\ncountry(a)\ncountry-dance\ncountry-style\ncountryman\ncountryseat\ncountryside\ncountrywide\ncountrywoman\ncounts\ncounty\ncountywide\ncoup\ncoupe\ncouple\ncoupled\ncouples\ncouplet\ncoupling\ncoupon\ncour\ncourage\ncourageous\ncourant\ncourbaril\ncourier\ncourlan\ncourroux\ncourse\ncourseness\ncourser\ncourses\ncoursing\ncourt\ncourt-martial\ncourtbaron\ncourtelle\ncourteous\ncourteously\ncourtesan\ncourtesv\ncourtesy\ncourthouse\ncourtier\ncourtierlike\ncourtierly\ncourtleet\ncourtliness\ncourtly\ncourts\ncourtship\ncourtyard\ncousin\ncousingerman\ncousinhood\ncousinly\ncout\ncoute\ncouth\ncouthie\ncouture\ncouturier\ncouvade\ncouvert\ncovalence\ncovalent\ncovariance\ncove\ncoven\ncovenant\ncoventry\ncover\ncover-up\ncoverage\ncoverall\ncovercle\ncovered\ncovering\ncoverlet\ncoverley\ncovert\ncoverte\ncovertly\ncoverture\ncovet\ncoveted\ncoveting\ncovetous\ncovetousness\ncovey\ncovin\ncovinous\ncow\ncowage\ncoward\ncowardice\ncowardliness\ncowardly\ncowbarn\ncowbell\ncowberry\ncowbird\ncowboy\ncowcatcher\ncower\ncowering(a)\ncowerskulk\ncowfish\ncowgirl\ncowherb\ncowherd\ncowhide\ncowkeeper\ncowl\ncowled\ncowlick\ncowlstaff\ncoworker\ncowpea\ncowpens\ncowper\ncowpox\ncowrie\ncowslip\ncoxcomb\ncoxcombery\ncoxcombry\ncoxcomical\ncoxsackievirus\ncoxswain\ncoy\ncoydog\ncoyly\ncoyness\ncoyol\ncoyote\ncoypu\ncozen\ncozenage\ncozily\ncoziness\ncozy\ncpayment\ncpoetry\ncproperty\ncquadroon\ncr\bme\ncr\ncrab\ncrabbe\ncrabbed\ncrabbedness\ncrabbiness\ncrabgrass\ncrablike\ncrabs\ncrabwise\ncracidae\ncrack\ncrackajack\ncrackbrained\ncrackdown\ncracked\ncracker\ncracker-barrel\ncracking\ncrackle\ncrackling\ncracklings\ncrackloo\ncrackpot\ncracks\ncracksman\ncracow\ncracticidae\ncracticus\ncradle\ncraft\ncraftily\ncraftiness\ncraftmanship\ncraftsman\ncrafty\ncrag\ncragged\ncraggy\ncraichy\ncraig\ncraignez\ncrake\ncram\ncrambe\ncrambo\ncrammed\ncrammer\ncramp\ncramped\ncrampon\ncran\ncranberry\ncranch\ncrane\ncranes\ncranesbill\ncrangon\ncrangonidae\ncranial\ncraniology\ncraniometer\ncranioscopy\ncraniotomy\ncranium\ncrank\ncrankcase\ncrankiness\ncrankle\ncrankling\ncranks\ncrankshaft\ncranky\ncrannied\ncranny\ncrap\ncrapaud\ncrape\ncrapek\ncrappie\ncraps\ncrapulence\ncrapulent\ncrapulous\ncras\ncrash\ncrash(a)\ncrasis\ncraspedia\ncrass\ncrassamentum\ncrasse\ncrassitude\ncrassness\ncrassostrea\ncrassula\ncrassulaceae\ncrataegus\ncrate\ncrater\ncraunch\ncravat\ncrave\ncraved\ncraven\ncravenness\ncraving\ncraw\ncrawfish\ncrawl\ncrawling\ncrax\ncrayfish\ncrayon\ncrayons\ncraze\ncrazed\ncraziness\ncrazy\ncreak\ncreakily\ncreaking\ncreaky\ncream\ncreamcolored\ncreamcups\ncreamery\ncreaminess\ncreamy\ncreance\ncrease\ncreaseless\ncreat\ncreate\ncreated\ncreatine\ncreation\ncreationism\ncreative\ncreatively\ncreativeness\ncreativity\ncreator\ncreature\ncreceipts\ncreceptacle\ncreche\ncrecy\ncredat\ncrede\ncredence\ncredenda\ncredential\ncredentials\ncredenza\ncredibility\ncredible\ncredibleness\ncredibly\ncredit\ncreditable\ncredited\ncredited(p)\ncrediting\ncreditor\ncreditworthiness\ncreditworthy\ncredo\ncreduility\ncredula\ncredulity\ncredulous\ncredulously\ncredulousness\ncredundance\ncree\ncreed\ncreedal\ncreek\ncreeks\ncreel\ncreep\ncreeper\ncreeping\ncreeps\ncreepy\ncreese\ncremains\ncremation\ncrematorium\ncrematory\ncreme\ncremona\ncrenate\ncrenated\ncrenelle\ncrenulate\ncreole\ncreole-fish\ncreosote\ncrepe\ncrepidam\ncrepis\ncrepitate\ncrepitation\ncrepuscular\ncrepuscule\ncrescendo\ncrescent\ncrescentia\ncrescentic\ncresco\ncresistance\ncresol\ncress\ncresset\ncressida\ncrest\ncrestate\ncrested\ncrestfallen\ncretaceous\ncretan\ncrete\ncretin\ncretinism\ncretinous\ncretonne\ncrevasse\ncrevasses\ncreversion\ncrevice\ncrew\ncreward\ncrewel\ncrewelwork\ncrewman\ncrex\ncrib\ncribbage\ncribbed\ncribble\ncribriform\ncricetidae\ncricetus\ncrichton\ncrick\ncricket\ncricketer\ncricketground\ncrier\ncrim\ncrime\ncrimea\ncrimen\ncrimes\ncriminal\ncriminalism\ncriminality\ncriminally\ncriminate\ncrimination\ncriminative\ncriminatory\ncriminis\ncriminological\ncriminologist\ncriminology\ncriminousness\ncrimp\ncrimple\ncrimson\ncringe\ncringing\ncringle\ncrinite\ncrinked\ncrinkle\ncrinkled\ncrinkleroot\ncrinkles\ncrinkly\ncrinoid\ncrinoidea\ncrinoline\ncrinose\ncriollo\ncripple\ncrippled\ncrippling\ncrisis\ncrisp\ncrispate\ncrispinus\ncrispness\ncrisscross\ncristobalite\ncriterial\ncriterion\ncrith\ncrithomancy\ncritic\ncritical\ncriticality\ncritically\ncriticise\ncriticism\ncriticize\ncriticorum\ncritique\ncritter\ncrius\ncro-magnon\ncroak\ncroaker\ncroaking\ncroat\ncroatia\ncroatian\ncrocethia\ncrochet\ncrocheting\ncrock\ncrockery\ncrocket\ncrocketed\ncrocodile\ncrocodylia\ncrocodylidae\ncrocodylus\ncrocus\ncrocuta\ncroesus\ncroft\ncrofter\ncroiser\ncroisis\ncrokscrew\ncrolling\ncromlech\ncromwell\ncromwellian\ncronartium\ncrone\ncronus\ncrony\ncrook\ncrookback\ncrooked\ncrookedness\ncrookneck\ncroon\ncrooner\ncrooning\ncrop\ncrop-dusting\ncropout\ncropped\ncropper\ncroquet\ncroquette\ncrore\ncrosier\ncrospin\ncross\ncross(a)\ncross-classification\ncross-country\ncross-cultural\ncross-examination\ncross-examiner\ncross-eye\ncross-eyed\ncross-fertilization\ncross-grained\ncross-legged\ncross-linguistic\ncross-linguistically\ncross-link\ncross-modal\ncross-pollination\ncross-purpose\ncross-question\ncross-reference\ncross-section(a)\ncross-sectional\ncross-sentential\ncross-stitch\ncrossbar\ncrossbarred\ncrossbench\ncrossbencher\ncrossbill\ncrossbones\ncrossbow\ncrossbred\ncrossbreed\ncrosscheck\ncrosscut\ncrossdebt\ncrossdemand\ncrosse\ncrossed\ncrossexamination\ncrossexamine\ncrossfire\ncrossgrained\ncrosshead\ncrossheading\ncrossing\ncrossjack\ncrossly\ncrossness\ncrossopterygian\ncrossopterygii\ncrosspatch\ncrosspiece\ncrosspurposes\ncrossquestion\ncrossreading\ncrossroad\ncrossroads\ncrosstalk\ncrosswind\ncrosswise\ncrotalaria\ncrotalidae\ncrotalus\ncrotaphytus\ncrotch\ncrotchet\ncrotchety\ncroton\ncrotophaga\ncrottle\ncrouch\ncrouched\ncrouching\ncroup\ncroupier\ncroupy\ncrouton\ncrow\ncrowbait\ncrowbar\ncrowberry\ncrowd\ncrowded\ncrowding\ncrowds\ncrown\ncrownbeard\ncrowned\ncrowning\ncrowning(a)\ncrownwork\ncrows\ncrucial\ncrucially\ncruciate\ncruciation\ncrucible\ncrucifer\ncruciferae\ncruciferous\ncrucifix\ncrucifixion\ncruciform\ncrucify\ncrucis\ncrud\ncrude\ncrudely\ncrudeness\ncrudites\ncrudity\ncruel\ncruelly\ncruelness\ncruelty\ncruet\ncruet-stand\ncruise\ncruiser\ncruiserweight\ncruller\ncrumb\ncrumble\ncrumbled\ncrumbles\ncrumbliness\ncrumbling\ncrumbly\ncrumenal\ncrumenam\ncrump\ncrumple\ncrumpled\ncrumply\ncrunch\ncrunched\ncrupper\ncrural\ncrus\ncrusade\ncruse\ncrush\ncrushed\ncrusher\ncrushing\ncrushingly\ncrust\ncrustacea\ncrustacean\ncrustaceous\ncrusted\ncrustose\ncrusty\ncrutch\ncrutched\ncrux\ncruzeiro\ncry\ncryesthesia\ncrying\ncrying(a)\ncryoanesthesia\ncryocautery\ncryogen\ncryogenic\ncryogenics\ncryolite\ncryometer\ncryophobia\ncryoscope\ncryostat\ncryosurgery\ncrypt\ncryptacanthodes\ncryptic\ncryptical\ncryptically\ncryptobiosis\ncryptobiotic\ncryptobranchidae\ncryptobranchus\ncryptocercidae\ncryptocercus\ncryptococcosis\ncryptocoryne\ncryptogam\ncryptogamia\ncryptogamic\ncryptogram\ncryptogramma\ncryptogrammataceae\ncryptograph\ncryptography\ncryptomeria\ncryptomonad\ncryptophyceae\ncryptophyta\ncryptoprocta\ncryptorchidy\ncryptotermes\ncryptotis\ncrystal\ncrystalline\ncrystallite\ncrystallizable\ncrystallization\ncrystallize\ncrystallized\ncrystallography\ncrystallomancy\ncsako\ncsecurity\ncselfish\ncshrillness\ncsociality\ncsorcery\ncst\ncstern\ncsubstitute\nctene\nctenidium\nctenizidae\nctenocephalides\nctenophora\nctenophore\ncthoroughly\ncub\ncuba\ncuban\ncubby\ncubbyhole\ncube\ncubeb\ncubelike\ncubic\ncubical\ncubicity\ncubicle\ncubism\ncubist\ncubit\ncubital\ncubitiere\ncubitus\ncuboid\ncucking\ncuckold\ncuckoldom\ncuckoo\ncuckoo-bumblebee\ncuckoopint\ncuculidae\ncuculiformes\ncucullate\ncuculus\ncucumber\ncucumbers\ncucumis\ncucurbit\ncucurbita\ncucurbitaceae\ncucurbitaceous\ncud\ncuddle\ncuddlesome\ncuddy\ncudgel\ncudgels\ncudweed\ncue\ncuff\ncuffed\ncufflink\ncui\ncuique\ncuirass\ncuirassier\ncuisine\ncuisinecordon\ncuisse\ncul\nculbute\nculbuter\nculcita\nculdelampe\nculdesac\nculdoscope\nculdoscopy\nculex\nculicidae\nculinary\ncull\ncullender\ncullibility\ncullion\ncullis\ncully\nculm\nculminate\nculminating\nculmination\nculotte\nculpa\nculpability\nculpable\nculpam\nculpan\nculprit\ncult\ncultist\ncultivar\ncultivate\ncultivated\ncultivation\ncultivator\ncultural\nculturally\nculture\ncululative\nculverin\nculvert\ncum\ncumber\ncumberland\ncumbersome\ncumbrous\ncumin\ncuminum\ncummerbund\ncumque\ncumulation\ncumulative\ncumulatively\ncumulonimbus\ncumulostratus\ncumulus\ncunaxa\ncuncta\ncunctando\ncunctation\ncuneate\ncuneiform\ncuniculus\ncunner\ncunnilingus\ncunning\ncunningly\ncunningman\ncunningness\ncunnint\ncunoniaceae\ncunproductiveness\ncunt\ncuon\ncup\ncupbearer\ncupboard\ncupcake\ncupellation\ncupflower\ncupid\ncupidity\ncupido\ncupola\ncuppa\ncupping\ncupressaceae\ncupressus\ncupric\ncuprite\ncupronickel\ncups\ncupshaped\ncupular\ncupule\ncuquenan\ncur\ncur(a)\ncura\ncurability\ncurable\ncurableness\ncuracao\ncuracy\ncurassow\ncurate\ncurative\ncurator\ncuratorial\ncuratorship\ncurb\ncurbside\ncurbstone\ncurcuitous\ncurcular\ncurculation\ncurculionidae\ncurcuma\ncurd\ncurdle\ncurdled\ncurdling\ncurduroy\ncure\ncureall\ncured\ncureless\ncurer\ncurettage\ncurette\ncurfew\ncuria\ncuriae\ncuriam\ncurie\ncurio\ncuriosa\ncuriosity\ncurious\ncuriously\ncuriousness\ncurist\ncurium\ncurl\ncurled\ncurler\ncurlew\ncurliness\ncurling\ncurly\ncurly-heads\ncurmudgeon\ncurrant\ncurrawong\ncurrency\ncurrent\ncurrente\ncurrently\ncurrentness\ncurrents\ncurricle\ncurricular\ncurriculum\ncurrish\ncurrishly\ncurrunt\ncurry\ncurrycomb\ncursd\ncurse\ncursed\ncurses\ncursing\ncursitor\ncursive\ncursively\ncursor\ncursorial\ncursorily\ncursorius\ncursory\ncurt\ncurtae\ncurtail\ncurtailed\ncurtailment\ncurtain\ncurtained\ncurtainless\ncurtal\ncurtly\ncurtness\ncurtsy\ncurule\ncurvaceously\ncurvaceousness\ncurvation\ncurvature\ncurve\ncurved\ncurvet\ncurviform\ncurvilineal\ncurvilinear\ncurvity\ncurvy\ncuscus\ncuscuta\ncush-cush\ncushat\ncushaw\ncushion\ncushioned\ncushitic\ncushy\ncusk\ncusk-eel\ncusp\ncuspate\ncusped\ncuspidate\ncuspidated\ncuspidation\ncuspidor\ncuss\ncussed\ncussedness\ncustacean\ncustard\ncustodes\ncustodial\ncustodian\ncustodianship\ncustodiet\ncustody\ncustom\ncustom-built\ncustom-made\ncustomarily\ncustomary\ncustomer\ncustomfall\ncustomhouse\ncustoms\ncustos\ncustum\ncut\ncut-in\ncutaneous\ncutaway\ncutback\ncutch\ncutcherry\ncute\ncuteness\ncuterebra\ncuterebridae\ncuticle\ncuticula\ncuticular\ncutlas\ncutlass\ncutlassfish\ncutler\ncutlery\ncutlet\ncutoff\ncutout\ncutter\ncutters\ncutthroat\ncutting\ncuttingly\ncuttings\ncuttlefish\ncuttystool\ncutwork\ncutworm\ncuum\ncv\ncwater\ncwt\ncyamopsis\ncyamus\ncyanamide\ncyanide\ncyanobacteria\ncyanobacterial\ncyanocitta\ncyanogen\ncyanohydrin\ncyanophyta\ncyanosis\ncyathea\ncyatheaceae\ncybele\ncyberart\ncybernetic\ncybernetics\ncyborg\ncycad\ncycadaceae\ncycadales\ncycadofilicales\ncycadopsida\ncycas\ncyclades\ncyclamen\ncycle\ncyclic\ncyclical\ncyclicity\ncycling\ncycliophora\ncyclist\ncycloid\ncycloidal\ncycloloma\ncyclone\ncyclonic\ncyclopean\ncyclopedia\ncyclopes\ncyclophorus\ncyclopia\ncyclopropane\ncyclops\ncyclopteridae\ncyclopterus\ncycloserine\ncyclosis\ncyclosorus\ncyclosporeae\ncyclostomata\ncyclostome\ncyclostyle\ncyclothymia\ncyclothymic\ncyclotron\ncycnoches\ncydippida\ncydonia\ncygne\ncygnet\ncygnus\ncylinder\ncylindric\ncylindrical\ncylindricality\ncylindricity\ncylindroid\ncyma\ncymatiidae\ncymbal\ncymbeline\ncymbelinel\ncymbid\ncymbiform\ncyme\ncymling\ncymophanous\ncymose\ncynanche\ncynancum\ncynara\ncynic\ncynical\ncynically\ncynicism\ncynipidae\ncynips\ncynocephalidae\ncynocephalus\ncynodon\ncynodont\ncynodontia\ncynoglossidae\ncynoglossum\ncynomys\ncynophobia\ncynopterus\ncynoscephalae\ncynoscion\ncynosure\ncynthia\ncyperaceae\ncyperus\ncyphomandra\ncypraea\ncypraeidae\ncypress\ncyprian\ncyprinid\ncyprinidae\ncypriniformes\ncyprinodont\ncyprinodontidae\ncyprinus\ncypriot\ncypripedia\ncypripedium\ncyproheptadine\ncyprus\ncyrilla\ncyrilliaceae\ncyrillic\ncyrtomium\ncyst\ncysteine\ncystic\ncystine\ncystitis\ncystocele\ncystophora\ncystoplegia\ncystopteris\ncytisus\ncytoarchitectural\ncytoarchitecture\ncytogenesis\ncytogenetic\ncytological\ncytology\ncytolysis\ncytomegalovirus\ncytoplasm\ncytosine\ncytostome\ncytotoxic\ncytotoxin\nczar\nczarevna\nczarina\nczarist\nczarita\nczarowitz\nczech\nczechoslovakia\nczechoslovakian\nczheeseparings\nd\nd-day\nd-layer\nda\ndab\ndaba\ndabble\ndabbled\ndabbler\ndabbling\ndabchick\ndaboecia\ndabri\ndabster\ndacca\ndace\ndacelo\ndacha\ndachau\ndachshund\ndacker\ndacoit\ndacoity\ndacron\ndacrycarpus\ndacrydium\ndacrymyces\ndacrymycetaceae\ndactyl\ndactylic\ndactyliomancy\ndactylis\ndactyloctenium\ndactylology\ndactylomancy\ndactylonomy\ndactylopiidae\ndactylopius\ndactylopteridae\ndactylopterus\ndactylorhiza\ndactyloscopidae\ndad\ndada\ndaddle\ndaddy\ndado\ndaedal\ndaedalian\ndaedalus\ndaemon\ndaeva\ndafe\ndaffaire\ndaffaires\ndaffodil\ndaffy\ndaft\ndaftly\ndagame\ndagan\ndagda\ndagga\ndagger\ndaggerboard\ndaggers\ndaggle\ndago\ndagon\ndaguerreotype\ndahabeah\ndahlia\ndaikon\ndaily\ndaimio\ndaimyo\ndaintily\ndaintiness\ndainty\ndaiquiri\ndairy\ndairying\ndairymaid\ndairyman\ndais\ndaisies\ndaisy\ndaisybush\ndaisylike\ndak\ndakar\ndakota\ndal\ndalal\ndalasi\ndalbergia\ndale\ndalea\ndalesman\ndaleth\ndallas\ndalle\ndalliance\ndallier\ndallisgrass\ndally\ndalmatia\ndalmatian\ndalmatic\ndalton\ndaltonism\ndam\ndama\ndamage\ndamaged\ndamages\ndamaging\ndamaliscus\ndamascene\ndamascus\ndamask\ndame\ndames\ndamkina\ndammar\ndamn\ndamnable\ndamnation\ndamnatory\ndamned\ndamnee\ndamnify\ndamnosa\ndamocles\ndamon\ndamore\ndamour\ndamourite\ndamp\ndampen\ndamper\ndamply\ndamsel\ndamselfish\ndamselfly\ndamson\ndamusque\ndan\ndanae\ndanaea\ndanaid\ndanaidae\ndanaos\ndanaus\ndance\ndanceable\ndancer\ndancing\ndandelion\ndander\ndandi\ndandie\ndandified\ndandily\ndandin\ndandiprat\ndandle\ndandruff\ndandy\ndandyism\ndane\ndanger\ndangerous\ndangerousness\ndangla\ndangle\ndangleberry\ndangler\ndangling(a)\ndaniel\ndanish\ndank\ndankness\ndans\ndanseur\ndanseuse\ndante\ndantescan\ndanu\ndanube\ndap\ndaphne\ndaphnia\ndapper\ndapperling\ndapple\ndapple-gray\ndappled\ndappui\ndaraf\ndarby\ndard\ndardanelles\ndare\ndaredevil\ndaredevilry\ndargent\ndari\ndaring\ndaringly\ndark\ndark-haired\ndark-skinned\ndarken\ndarkened\ndarkening\ndarkie\ndarkish\ndarkle\ndarkling\ndarkly\ndarkness\ndarkroom\ndarksome\ndarky\ndarling\ndarlingtonia\ndarmee\ndarmera\ndarmes\ndarn\ndarnel\ndarning\ndart\ndartboard\ndarting\ndartle\ndartre\ndarts\ndarwin\ndarwinian\ndarwinism\ndas\ndash\ndash-pot\ndashboard\ndashed\ndasher\ndashing\ndashingf\ndashingly\ndastard\ndastard(a)\ndastardliness\ndastardly\ndastardness\ndastardy\ndasturi\ndasyatidae\ndasyatis\ndasymeter\ndasypodidae\ndasyprocta\ndasyproctidae\ndasypus\ndasyure\ndasyuridae\ndasyurus\ndat\ndata\ndatabase\ndatable\ndate\ndated\ndateless\ndateline\ndative\ndatum\ndatura\ndaub\ndaube\ndaubed\ndaubentonia\ndaubentoniidae\ndauber\ndaubing\ndaucus\ndaughter\ndaughter-in-law\ndaughterly\ndaunt\ndaunting\ndauntingly\ndauntless\ndauntlessness\ndauphin\ndavallia\ndavalliaceae\ndavarice\ndavenport\ndavid\ndavids\ndaviesia\ndavit\ndavus\ndavy\ndawdle\ndawdler\ndawdling\ndawn\ndawns\ndawplucker\ndaws\nday\nday-old\ndaybed\ndaybook\ndayboy\ndaybreak\ndaycare\ndaydreamer\ndaygirl\ndaylight\ndaylong\ndays\ndayspring\ndaytime\ndaze\ndazed\ndazedly\ndazzle\ndazzled\ndazzlement\ndazzling\ndazzlingly\ndb\ndc\ndciived\ndd\nde\nde-escalation\nde-iodinase\nde-iodinating\nde-iodination\ndeacon\ndeaconess\ndeaconry\ndeaconship\ndeactivate\ndeactivation\ndead\ndead(a)\ndead(p)\ndead-man's-fingers\ndead-on(a)\ndeaden\ndeadened\ndeadhead\ndeadlight\ndeadline\ndeadliness\ndeadlock\ndeadlocked\ndeadly\ndeadness\ndeadpan\ndeads\ndeaf\ndeaf(p)\ndeaf-and-dumb\ndeaf-mutism\ndeafen\ndeafened\ndeafening\ndeafmute\ndeafness\ndeal\ndealer\ndealfish\ndealignment\ndealing\ndealings\ndean\ndeanery\ndeanship\ndear\ndearborn\ndearest\ndearly\ndearly-won\ndearness\ndearth\ndeat\ndeath\ndeath-roll\ndeathbed\ndeathblow\ndeathless\ndeathlike\ndeathly\ndeathrate\ndeaths\ndeathtrap\ndeau\ndebacle\ndebar\ndebark\ndebarkation\ndebarment\ndebase\ndebased\ndebasement\ndebasing\ndebatable\ndebate\ndebater\ndebates\ndebauch\ndebauched\ndebauchee\ndebauchery\ndebenture\ndebile\ndebilitate\ndebilitated\ndebilitating\ndebilitation\ndebilitative\ndebility\ndebit\ndebitor\ndebitorem\ndebole\ndebonair\ndebonnaire\ndebouch\ndebouche\ndebout\ndebriefing\ndebris\ndebt\ndebtor\ndebts\ndebugger\ndebut\ndebutant\ndebutante\ndecade\ndecadence\ndecadency\ndecadent\ndecagon\ndecahedron\ndecal\ndecalogue\ndecameter\ndecamp\ndecampment\ndecant\ndecanter\ndecapitate\ndecapitation\ndecapod\ndecapoda\ndecapterus\ndecarbonized\ndecasyllabic\ndecasyllable\ndecathlon\ndecay\ndecayable\ndecayed\ndecease\ndeceased\ndeceit\ndeceitful\ndeceive\ndeceived\ndeceiver\ndeceiving\ndeceleration\ndecember\ndecency\ndecenniumm\ndecent\ndecently\ndecentralization\ndecentralize\ndecentralized\ndecentralizing(a)\ndeceptio\ndeception\ndeceptious\ndeceptive\ndeceptively\ndeceptiveness\ndecession\ndecet\ndechristianize\ndecibel\ndecide\ndecided\ndecidedly\ndeciduous\ndecies\ndecigram\ndeciliter\ndecimal\ndecimalization\ndecimate\ndecimation\ndecimeter\ndecipher\ndeciphered\ndecipimur\ndecision\ndecisis\ndecisive\ndecisively\ndecisiveness\ndecison\ndecit\ndeck\ndeck-house\ndecker\ndeckhand\ndeckle\ndeckled\ndeckleedged\ndecks\ndeclaim\ndeclamation\ndeclamatory\ndeclarable\ndeclaration\ndeclarative\ndeclaratory\ndeclare\ndeclared\ndeclassification\ndeclassified\ndeclension\ndeclensions\ndeclination\ndeclinature\ndecline\ndeclining\ndeclining(a)\ndeclinometer\ndeclivitous\ndeclivity\ndeclivous\ndecoction\ndecoder\ndecoding\ndecollate\ndecollation\ndecolletage\ndecollete\ndecolonization\ndecoloration\ndecolorize\ndecompose\ndecomposed\ndecomposing\ndecomposition\ndecompositional\ndecompound\ndecompression\ndecongestant\ndeconsecrate\ndeconsecrated\ndeconsecration\ndeconstruction\ndeconstructionist\ndecontamination\ndecorate\ndecoration\ndecorative\ndecorativeness\ndecorator\ndecorous\ndecorously\ndecorticate\ndecortication\ndecorum\ndecoy\ndecoyduck\ndecrassify\ndecrease\ndecreased\ndecreasing\ndecree\ndecrement\ndecrepid\ndecrepit\ndecrepitation\ndecrepitude\ndecrescendo\ndecretal\ndecretive\ndecretory\ndecry\ndecuma\ndecumaria\ndecumary\ndecumbence\ndecumbency\ndecumbent\ndecuple\ndecuration\ndecurrent\ndecursive\ndecurtate\ndecurved\ndecussate\ndecussation\nded\ndedecoration\ndedecorous\ndedicate\ndedicated\ndedication\ndedifferentiated\ndedifferentiation\ndedit\ndeduce\ndeducible\ndeduct\ndeducted\ndeductible\ndeduction\ndeductive\ndee\ndeed\ndeeds\ndeem\ndeep\ndeep-eyed\ndeep-laid\ndeep-mined\ndeep-rooted\ndeep-sea\ndeep-set\ndeep-water\ndeepcolored\ndeepdyed\ndeepen\ndeepening\ndeepening(a)\ndeepest\ndeepfreeze\ndeeplaid\ndeeply\ndeepmouthed\ndeepness\ndeepread\ndeeprooted\ndeepsounding\ndeeptoned\ndeer\ndeerberry\ndeerskin\ndeerstalker\ndeerstalking\ndeev\ndeface\ndefaced\ndefacement\ndefalcation\ndefamation\ndefamatory\ndefame\ndefamer\ndefatigation\ndefault\ndefaulter\ndefeasance\ndefeasible\ndefeat\ndefeated\ndefeatism\ndefeatist\ndefecate\ndefecation\ndefect\ndefection\ndefective\ndefectively\ndefectiveness\ndefend\ndefendable\ndefendant\ndefended\ndefender\ndefending\ndefense\ndefenseless\ndefenselessness\ndefensible\ndefensive\ndefensively\ndefensor\ndefer\ndeference\ndeferent\ndeferential\ndeferentially\ndeferor\ndeferral\ndeferred\ndeferring\ndeffle\ndefiance\ndefiant\ndefibrillation\ndefibrillator\ndeficiencies\ndeficiency\ndeficient\ndeficit\ndefie\ndefigure\ndefilade\ndefile\ndefiled\ndefilement\ndefinable\ndefine\ndefined\ndefinite\ndefiniteness\ndefinition\ndefinitive\ndeflagration\ndeflated\ndeflation\ndeflationary\ndeflator\ndeflect\ndeflection\ndeflective\ndeflector\ndeflendus\ndeflexion\ndeflexure\ndefloration\ndeflower\ndefluat\ndefluxion\ndefoedation\ndefoliant\ndefoliate\ndefoliation\ndefoliator\ndeforestation\ndeform\ndeformation\ndeformational\ndeformed\ndeforming\ndeformity\ndefraud\ndefray\ndefrayment\ndefroster\ndeft\ndeftly\ndefunct\ndefy\ndefying\ndegage\ndegaussing\ndegeneracy\ndegenerate\ndegenerateness\ndegeneration\ndegenerative\ndegeneres\ndeglutition\ndegradation\ndegrade\ndegrading\ndegree\ndegrees\ndegressive\ndegustation\ndehiscence\ndehiscent\ndehort\ndehortation\ndehortatory\ndehumanization\ndehumanized\ndehydrated\ndehydration\ndei\ndeictic\ndeictics\ndeific\ndeification\ndeify\ndeign\ndeiist\ndeinocheirus\ndeinonychus\ndeipnosophist\ndeism\ndeist\ndeistical\ndeities\ndeity\ndeixis\ndejaniras\ndeject\ndejected\ndejectedly\ndejectedness\ndejection\ndejeuner\ndekagram\ndekaliter\ndeker\ndekko\ndel\ndelabrement\ndelaceration\ndelairea\ndelaminate\ndelation\ndelator\ndelaware\ndelawarean\ndelay\ndelayed\ndelayed-action\ndele\ndelectability\ndelectable\ndelection\ndelectus\ndelegacy\ndelegate\ndelegating\ndelegation\ndelenda\ndeleterious\ndeletion\ndeletory\ndelettanteism\ndelf\ndelfower\ndelft\ndelhi\ndeliberando\ndeliberat\ndeliberate\ndeliberately\ndeliberation\ndeliberative\ndelible\ndelicacy\ndelicat\ndelicate\ndelicatessen\ndelice\ndelichon\ndeliciae\ndelicious\ndeliciously\ndelicti\ndelicto\ndelictum\ndelight\ndelighted\ndelightedly\ndelightful\ndelightfully\ndelilah\ndelineate\ndelineated\ndelineation\ndelineative\ndelineavit\ndelinquency\ndelinquent\ndeliquation\ndeliquesce\ndeliquescence\ndeliquescent\ndeliquium\ndelirant\ndelire\ndelirious\ndeliriously\ndelirium\ndelitescence\ndelitescent\ndeliver\ndeliverance\ndelivered\ndeliverer\ndelivery\ndeliveryman\ndelivre\ndell\ndelonix\ndelphi\ndelphian\ndelphic\ndelphinapterus\ndelphinidae\ndelphinium\ndelphinus\ndelta\ndeltoid\ndelude\ndeluge\ndelusion\ndelusional\ndelusive\ndelusively\ndelusory\ndeluxe\ndelve\ndem\ndemagnetization\ndemagogic\ndemagogue\ndemagoguery\ndemagogy\ndemain\ndemand\ndemander\ndemanding\ndemandingly\ndemands\ndemantoid\ndemarcation\ndemarche\ndematiaceae\ndemavend\ndemean\ndemeaning\ndemeanor\ndemel\ndemele\ndemency\ndementat\ndementate\ndementation\ndemented\ndementi\ndementia\ndementiae\ndemerara\ndemerit\ndemesne\ndemeter\ndemetrius\ndemi\ndemiglace\ndemigod\ndemigration\ndemijohn\ndemijour\ndemimondaine\ndemimonde\ndemineralization\ndemirep\ndemise\ndemised\ndemisemiquaver\ndemission\ndemister\ndemitasse\ndemiurge\ndemiurgus\ndemivolt\ndemobilization\ndemochelys\ndemocracy\ndemocrat\ndemocratic\ndemocratically\ndemocratization\ndemocrats\ndemocritus\ndemodulation\ndemographer\ndemographic\ndemography\ndemoiselle\ndemolish\ndemolished\ndemolishment\ndemolition\ndemon\ndemon-ridden\ndemonetization\ndemonetize\ndemoniac\ndemoniacal\ndemoniacally\ndemonic\ndemoninational\ndemonism\ndemonlacal\ndemonolatry\ndemonology\ndemonomy\ndemonophobia\ndemonry\ndemonship\ndemonstrability\ndemonstrable\ndemonstrably\ndemonstrate\ndemonstrated\ndemonstrating\ndemonstration\ndemonstrative\ndemonstratively\ndemonstrativeness\ndemonstrator\ndemonworship\ndemoralization\ndemoralize\ndemoralized\ndemoralizing\ndemos\ndemosthenes\ndemosthenic\ndemotic\ndemotion\ndemulcent\ndemur\ndemure\ndemurely\ndemureness\ndemurrage\ndemurrer\ndemurring\ndemythologization\ndemythologized\nden\ndenary\ndenate\ndenated\ndenationalization\ndenaturalize\ndenaturalized\ndenaturant\ndenature\ndenatured\ndendranthema\ndendriform\ndendrite\ndendritic\ndendroaspis\ndendrobium\ndendrocalamus\ndendrocolaptes\ndendrocolaptidae\ndendroctonus\ndendroica\ndendroid\ndendrolagus\ndendromecon\ndenfer\ndengue\ndeniable\ndenial\ndenied\ndenier\ndenigme\ndenigrate\ndenigration\ndenim\ndenisonia\ndenization\ndenizen\ndenizens\ndenker\ndenman\ndenmark\ndennstaedtia\ndennstaedtiaceae\ndenominate\ndenomination\ndenominational\ndenominationally\ndenominator\ndenotable\ndenotative\ndenotatum\ndenote\ndenoted\ndenouement\ndenounce\ndenouncement\ndense\ndensely\ndenseness\ndensimeter\ndensitometer\ndensitometry\ndensity\ndent\ndental\ndentaria\ndentate\ndentes\ndenticle\ndenticulate\ndenticulated\ndentiform\ndentifrice\ndentine\ndentist\ndentistry\ndents\ndenture\ndenudation\ndenude\ndenuded\ndenunciation\ndenunciatory\ndenver\ndeny\ndenying\ndeo\ndeobstruct\ndeobstruent\ndeodand\ndeodar\ndeodorant\ndeodorization\ndeodorize\ndeodorized\ndeodorizer\ndeodorizing\ndeontology\ndeoppilate\ndeoppilation\ndeorganization\ndeorganize\ndeos\ndeosculation\ndeoxidization\ndeoxyribose\ndeparia\ndepart\ndeparted\ndeparter\ndeparting\ndeparting(a)\ndepartment\ndepartmental\ndepartmentally\ndeparture\ndepee\ndepend\ndependability\ndependable\ndependant\ndepended\ndependence\ndependency\ndependent\ndepending\ndeperdition\ndephlegmation\ndepict\ndepicted\ndepicting\ndepiction\ndepictment\ndepicture\ndepilation\ndepilatory\ndepilous\ndepletable\ndepleted\ndepletion\ndeplorable\ndeplorably\ndeplore\ndeploy\ndeployment\ndepond\ndepone\ndeponent\ndepopulate\ndepopulated\ndepopulation\ndeport\ndeportation\ndeportment\ndeposal\ndepose\ndeposed\ndeposit\ndepositary\ndeposition\ndepositor\ndepository\ndepot\ndepravation\ndeprave\ndepraved\ndepravement\ndepravity\ndeprecate\ndeprecated\ndeprecation\ndeprecatory\ndepreciate\ndepreciated\ndepreciating\ndepreciation\ndepredation\ndepredator\ndeprehension\ndepress\ndepressant\ndepressed\ndepressing\ndepressingly\ndepression\ndepressive\ndepressor\ndeprivation\ndeprive\ndeprived\ndeprivement\ndepth\ndepurate\ndepurative\ndepuratory\ndeputation\ndepute\ndeputies\ndeputy\ndeputy(a)\ndequantitate\ndequet\nder\nderail\nderailment\nderange\nderanged\nderangement\nderby\nderegulation\nderelict\ndereliction\nderide\nderision\nderisive\nderisively\nderisory\nderivable\nderivation\nderivational\nderivative\nderive\nderived\nderiving\nderm\ndermacentor\ndermal\ndermaptera\ndermatitis\ndermatobia\ndermatoid\ndermatologic\ndermatologist\ndermatology\ndermatome\ndermestidae\ndermis\ndermochelyidae\ndermoptera\ndernier\nderobee\nderogate\nderogation\nderogative\nderogatory\nderr\nderri\nderrick\nderring-do\nderringdo\nderringer\nderris\nderry\nderv\ndervish\ndes\ndesaeuvre\ndesagrement\ndesalination\ndescant\ndescartes\ndescend\ndescendant\ndescendants\ndescendent\ndescending\ndescending(a)\ndescension\ndescensus\ndescent\ndescribable\ndescribe\ndescribed\ndescription\ndescriptive\ndescriptively\ndescry\ndescurainia\ndesecrate\ndesecrated\ndesecrating\ndesecration\ndesegrated\ndesensitization\ndesensitized\ndesensitizing\ndesert\ndeserted\ndeserter\ndesertful\ndeserting\ndesertion\ndesertless\ndeserve\ndeserved\ndeservedly\ndeserving\ndeserving(p)\ndeservingness\ndesespoic\ndesespoir\ndeshabille\ndesiccant\ndesiccate\ndesiccated\ndesiccation\ndesiccative\ndesiderate\ndesideratum\ndesign\ndesignate\ndesignate(ip)\ndesignated\ndesignation\ndesignative\ndesignatum\ndesigned\ndesigned(a)\ndesignedly\ndesigner\ndesigner(a)\ndesigning\ndesignless\ndesillusionner\ndesinence\ndesipere\ndesirability\ndesirable\ndesirableness\ndesire\ndesired\ndesiring\ndesirous\ndesist\ndesistance\ndesk\ndesktop\ndesmanthus\ndesmid\ndesmidiaceae\ndesmidium\ndesmodium\ndesmodontidae\ndesmodus\ndesmograthus\ndesobligeant\ndesolate\ndesolately\ndesolating\ndesolation\ndesole\ndesorient\u0002\ndesorption\ndesous\ndespair\ndespairing\ndespairingly\ndespatch\ndespect\ndesperado\ndesperandum\ndesperate\ndesperately\ndesperation\ndespicable\ndespicably\ndespiciency\ndespisal\ndespise\ndespised\ndespisedness\ndespisement\ndespite\ndespiteful\ndespitefully\ndespoil\ndespoiled\ndespond\ndespondency\ndespondent\ndesponding\ndespot\ndespotic\ndespotical\ndespotism\ndesprit\ndespritepigram\ndespumate\ndesquamation\ndess\ndessai\ndessert\ndessertspoon\ndessiatine\ndessicator\ndessous\ndessus\ndestime\ndestinate\ndestination\ndestine\ndestined\ndestiny\ndestitute\ndestitution\ndesto\ndestrier\ndestroy\ndestroyable\ndestroyed\ndestroyer\ndestroying\ndestructibility\ndestructible\ndestruction\ndestructive\ndestructive-metabolic(a)\ndestructively\ndestructiveness\ndesuetude\ndesultory\ndesume\ndesunt\ndetach\ndetachable\ndetached\ndetachment\ndetail\ndetailed\ndetails\ndetain\ndetainee\ndetat\ndetect\ndetectable\ndetected\ndetection\ndetective\ndetector\ndetent\ndetente\ndetention\ndetenu\ndeter\ndeterge\ndetergency\ndetergent\ndeteriorate\ndeteriorated\ndeterioration\ndeterment\ndeterminable\ndeterminant\ndeterminate\ndeterminateness\ndetermination\ndetermine\ndetermined\ndeterminedly\ndeterminer\ndeterminism\ndeterration\ndeterrence\ndeterrent\ndetersion\ndetersive\ndetest\ndetestable\ndetestably\ndetestation\ndethrone\ndethronement\ndetonate\ndetonating\ndetonation\ndetonative\ndetonator\ndetonize\ndetort\ndetortion\ndetour\ndetox\ndetoxification\ndetract\ndetracting\ndetraction\ndetractive\ndetractor\ndetractory\ndetrain\ndetre\ndetribalization\ndetriment\ndetrimental\ndetrimentally\ndetrition\ndetritus\ndetroit\ndetrude\ndetruncate\ndetruncation\ndetrusion\ndetrusive\ndeuce\ndeuced\ndeucedly\ndeum\ndeus\ndeuteranopia\ndeuteranopic\ndeuterium\ndeuterogamy\ndeuteromycetes\ndeuteromycota\ndeuteron\ndeuteronomy\ndeutzia\ndeuvre\ndeux\ndeva\ndevaluation\ndevanagari\ndevastate\ndevastation\ndevel\ndevelop\ndeveloped\ndeveloper\ndeveloping\ndevelopment\ndevelopmental\ndevelopmentally\ndevex\ndevexity\ndevi\ndeviate\ndeviating\ndeviation\ndeviationism\ndeviationist\ndevice\ndevices\ndevil\ndevil-may-care\ndevilish\ndevilishly\ndevilism\ndevilmay\ndevilmaycare\ndevils\ndevilship\ndeviltry\ndevilwood\ndevilworship\ndevious\ndeviously\ndeviousness\ndevisal\ndevise\ndevised\ndevisee\ndevising\ndevitalization\ndevitedness\ndevoid\ndevoir\ndevoirs\ndevolution\ndevolve\ndevon\ndevonian\ndevote\ndevoted\ndevoted(p)\ndevotedly\ndevotee\ndevotion\ndevotional\ndevour\ndevoured\ndevouring\ndevout\ndevoutless\ndevoutly\ndevoutness\ndew\ndewali\ndewan\ndewberry\ndewdrop\ndewdrops\ndewey\ndeweyan\ndewlap\ndewy\ndexamethasone\ndexter\ndexterity\ndexterous\ndexterously\ndexterousness\ndextrad\ndextral\ndextrality\ndextrally\ndextro\ndextrorotary\ndextrorsal\ndextrorse\ndextrose\ndey\ndf\ndfie\ndg\ndhak\ndharma\ndhaulagiri\ndhawa\ndhegiha\ndheur\ndhobi\ndhole\ndhonneur\ndhote\ndhoti\ndhow\ndhu\ndhu'l-hijja\ndhu'l-qa'dah\ndi\ndi-iodotyrosine\ndiabatic\ndiabetes\ndiabetic\ndiable\ndiablerie\ndiabolatry\ndiabolic\ndiabolical\ndiabolically\ndiabolism\ndiabolology\ndiacalpa\ndiachronic\ndiaconicum\ndiacoustics\ndiacritic\ndiacritical\ndiadem\ndiadophis\ndiadromous\ndiagnosable\ndiagnosis\ndiagnostic\ndiagnostician\ndiagnostics\ndiagnus\ndiagonal\ndiagonalizable\ndiagonalization\ndiagonally\ndiagram\ndiagrammatic\ndiagrammatically\ndiakinesis\ndial\ndialect\ndialectal\ndialectic\ndialectical\ndialectically\ndialectician\ndialectics\ndialeurodes\ndialnot\ndialogism\ndialogist\ndialogue\ndialysis\ndialyzer\ndiamagnet\ndiamagnetic\ndiamagnetism\ndiamantine\ndiameter\ndiametral\ndiametric\ndiametrically\ndiamine\ndiamond\ndiamondback\ndiana\ndianoetic\ndianthus\ndiapason\ndiapensia\ndiapensiaceae\ndiapensiales\ndiaper\ndiaphaneity\ndiaphanous\ndiapheromera\ndiaphone\ndiaphonics\ndiaphoresis\ndiaphoretic\ndiaphragm\ndiaphyseal\ndiaphysis\ndiaporesis\ndiapsid\ndiapsida\ndiarist\ndiarrhea\ndiarrheal\ndiarrhoea\ndiary\ndiaskeaus\ndiaspididae\ndiaspora\ndiastasis\ndiaster\ndiastole\ndiastrophism\ndiatessaron\ndiathermal\ndiathermancy\ndiathermanous\ndiathermy\ndiathesis\ndiatom\ndiatomic\ndiatonic\ndiatribe\ndiavolo\ndiazepam\ndiazo\ndiazonium\ndib\ndibatter\ndibble\ndibranchiata\ndibranchiate\ndibs\ndibucaine\ndicacity\ndicamptodon\ndicamptodontidae\ndicamus\ndicarboxylic\ndice\ndicen\ndicentra\ndicer\ndicere\ndiceros\ndicers\ndichloride\ndichondra\ndichotomization\ndichotomize\ndichotomous\ndichotomously\ndichotomy\ndichroic\ndichroism\ndichromatic\ndichter\ndichtung\ndichtunggerman\ndicis\ndick\ndickens\ndicker\ndickey\ndickeybird\ndicksonia\ndicksoniaceae\ndicky\ndiclinous\ndicot\ndicotyledones\ndicotyledonous\ndicousu\ndicranaceae\ndicranales\ndicranopteris\ndicranum\ndicrostonyx\ndicta\ndictamnus\ndictaphone\ndictate\ndictates\ndictation\ndictator\ndictatorial\ndictatorially\ndictatorship\ndiction\ndictionary\ndicto\ndictostylium\ndictu\ndictum\ndictyophera\ndictyoptera\ndictyopteran\ndicynodont\ndicynodontia\ndid\ndidactic\ndidactically\ndidder\ndiddle\ndiddler\ndidelphidae\ndidelphis\ndido\ndiduction\ndie\ndie-cast\ndie-hard(a)\ndieback\ndieffenbachia\ndiegueno\ndiem\ndiemaker\ndiencephalon\ndiener\ndiervilla\ndies\ndiesel\ndiestock\ndiestrous\ndiestrus\ndiet\ndiet(a)\ndietary\ndietetic\ndietetics\ndiethylstilbestrol\ndietician\ndieu\ndiffer\ndifference\ndifferences\ndifferent\ndifferentia\ndifferentiable\ndifferential\ndifferentially\ndifferentiated\ndifferentiation\ndifferentiator\ndifferently\ndiffering\ndiffering(a)\ndifficile\ndifficili\ndifficult\ndifficulties\ndifficulty\ndiffide\ndiffidence\ndiffident\ndiffidently\ndiffluent\ndifflugia\ndiffraction\ndiffuse\ndiffused\ndiffusely\ndiffuseness\ndiffuser\ndiffusing(a)\ndiffusion\ndiffusive\ndiflunisal\ndig\ndigamy\ndigenesis\ndigenetic\ndigest\ndigested\ndigester\ndigestibility\ndigestible\ndigestion\ndigestive\ndigger\ndigging\ndiggings\ndight\ndighted\ndigit\ndigital\ndigitalis\ndigitalization\ndigitally\ndigitaria\ndigitate\ndigitated\ndigitately\ndigitigrade\ndigitization\ndigitizer\ndigladiation\ndiglot\ndignification\ndignified\ndignify\ndignifying\ndignitaries\ndignitate\ndignitatem\ndignities\ndignity\ndignus\ndigraph\ndigress\ndigression\ndigressive\ndihydrostreptomycin\ndii\ndiis\ndijudication\ndike\ndilaceration\ndilapidate\ndilapidated\ndilapidation\ndilatability\ndilatation\ndilate\ndilated\ndilating\ndilation\ndilatoriness\ndilatory\ndildo\ndilection\ndilemma\ndilettant\ndilettante\ndilettanti\ndilettantism\ndiligence\ndiligent\ndiligently\ndill\ndillenia\ndilleniaceae\ndilleniidae\ndiller\ndilly\ndillydally\ndiltiazem\ndilucidation\ndiluent\ndilute\ndiluted\ndilution\ndiluvian\ndim\ndim-sighted\ndim-witted\ndime\ndimenhydrinate\ndimension\ndimensional\ndimensionality\ndimensioning\ndimensions\ndimer\ndimes\ndimethylglyoxime\ndimetrodon\ndimeyed\ndimidiate\ndimidiation\ndimidium\ndiminish\ndiminished\ndiminishing\ndiminishment\ndiminuendo\ndiminution\ndiminutive\ndiminutiveness\ndimittis\ndimity\ndimly\ndimmed\ndimmer\ndimness\ndimocarpus\ndimorphotheca\ndimple\ndimsighted\ndimsightedness\ndimwit\ndin\ndinarchy\ndindustrie\ndine\ndiner\nding\nding-dong\ndingbat\ndingdong\ndinghy\ndingily\ndinginess\ndingle\ndingo\ndingus\ndingy\ndining\ndininghall\ndinka\ndinky\ndinmont\ndinner\ndinners\ndinnertime\ndinoceras\ndinocerata\ndinocerate\ndinoflagellata\ndinoflagellate\ndinornis\ndinornithidae\ndinornithiformes\ndinosaur\ndint\ndinvention\ndio\ndiocesan\ndiocese\ndiode\ndiodon\ndiodontidae\ndioecious\ndiogenes\ndiol\ndiomedeidae\ndionaea\ndionysia\ndionysus\ndioon\ndioptrics\ndiorama\ndiorism\ndioristic\ndiorite\ndios\ndioscorea\ndioscoreaceae\ndiospyros\ndioxide\ndioxin\ndip\ndiphenylhydantoin\ndiphtheria\ndiphthong\ndiphyletic\ndiphylla\ndiplococcus\ndiplodocus\ndiploid\ndiploma\ndiplomacy\ndiplomat\ndiplomate\ndiplomatic\ndiplomatically\ndiplomatics\ndiplomatique\ndiplomatist\ndiplopoda\ndiplopterygium\ndiplotaxis\ndiplotene\ndipnoi\ndipodidae\ndipodomys\ndipogon\ndipolar\ndipole\ndipped\ndipper\ndipsacaceae\ndipsacus\ndipsomania\ndipsomaniac\ndipsosaurus\ndipstick\ndiptera\ndipterocarp\ndipterocarpaceae\ndipteronia\ndiptych\ndipus\ndipylon\ndirca\ndire\ndirect\ndirected\ndirecting\ndirectingpost\ndirection\ndirectional\ndirections\ndirective\ndirectivity\ndirectly\ndirectness\ndirector\ndirectorate\ndirectorship\ndirectory\ndireful\ndirefully\ndiremption\ndireption\ndirge\ndirige\ndirigenos\ndirk\ndirndl\ndirt\ndirtily\ndirtiness\ndirty\ndirty-minded\ndiruption\ndis\ndisa\ndisability\ndisable\ndisabled\ndisablement\ndisabling\ndisabuse\ndisabused(p)\ndisaccharide\ndisaccord\ndisadvantage\ndisadvantageous\ndisaffected\ndisaffection\ndisaffirm\ndisagree\ndisagreeable\ndisagreeableness\ndisagreeably\ndisagreeing\ndisagreement\ndisallow\ndisallowance\ndisannul\ndisant\ndisappear\ndisappearance\ndisappearing\ndisappoint\ndisappointed\ndisappointedly\ndisappointing\ndisappointingly\ndisappointment\ndisapppointed\ndisapprobation\ndisapproval\ndisapprove\ndisapproved\ndisapprover\ndisapproving\ndisapprovingly\ndisarm\ndisarming\ndisarrange\ndisarranged\ndisarray\ndisarrayed\ndisassociation\ndisaster\ndisastrous\ndisastrously\ndisavow\ndisavowable\ndisavowal\ndisband\ndisbandment\ndisbar\ndisbarment\ndisbelief\ndisbelieve\ndisbeliever\ndisbelieving\ndisbench\ndisbowel\ndisbranch\ndisburden\ndisburdened\ndisburse\ndisbursement\ndisc\ndiscalced\ndiscard\ndiscarded\ndisce\ndisceptation\ndiscern\ndiscernability\ndiscernible\ndiscerning\ndiscernment\ndiscerptible\ndiscerption\ndiscers\ndiscet\ndischarge\ndischarged\ndiscimus\ndiscina\ndiscind\ndisciple\ndisciples\ndiscipleship\ndisciplinal\ndisciplinarian\ndisciplinary\ndiscipline\ndisciplined\ndiscit\ndisclaim\ndisclaimer\ndisclose\ndisclosed\ndisclosing\ndisclosure\ndisco\ndiscocephali\ndiscoglossidae\ndiscoid\ndiscolor\ndiscoloration\ndiscolored\ndiscombobulate\ndiscombobulated\ndiscomfit\ndiscomfited\ndiscomfiture\ndiscomfort\ndiscommend\ndiscommendation\ndiscommode\ndiscommodious\ndiscommodity\ndiscompose\ndiscomposed\ndiscomposure\ndiscomycete\ndiscomycetes\ndiscomycetous\ndisconcert\ndisconcerted\ndisconcerting\ndisconcertingly\ndisconfirming\ndisconformity\ndiscongruity\ndisconnect\ndisconnected\ndisconnection\ndisconsolate\ndisconsolateness\ndiscontent\ndiscontented\ndiscontentedly\ndiscontentment\ndiscontinuance\ndiscontinue\ndiscontinued\ndiscontinuity\ndiscontinuous\ndiscontinuously\ndiscord\ndiscordance\ndiscordant\ndiscordantly\ndiscorporate\ndiscors\ndiscount\ndiscountenance\ndiscounting\ndiscourage\ndiscouraged\ndiscouragement\ndiscouraging\ndiscouragingly\ndiscourse\ndiscoursive\ndiscourteous\ndiscourteously\ndiscourtesv\ndiscourtesy\ndiscourtesyill\ndiscous\ndiscover\ndiscovered\ndiscoverer\ndiscovery\ndiscredit\ndiscreditable\ndiscredited\ndiscreet\ndiscreetly\ndiscrepance\ndiscrepancy\ndiscrepant\ndiscrete\ndiscreteness\ndiscretion\ndiscretional\ndiscretionary\ndiscriminable\ndiscriminate\ndiscriminating\ndiscrimination\ndiscriminative\ndiscriminatory\ndiscrition\ndisculpate\ndiscumbency\ndiscursion\ndiscursive\ndiscursively\ndiscursiveness\ndiscursory\ndiscus\ndiscuss\ndiscussion\ndisdain\ndisdainful\ndisdainfully\ndisdains\ndisease\ndiseased\ndisembark\ndisembarkation\ndisembarrass\ndisembarrassed\ndisembarrassment\ndisembodied\ndisembody\ndisembogue\ndisembowwl\ndisembroil\ndisenable\ndisenchant\ndisenchanted\ndisenchanting\ndisenchantment\ndisencumber\ndisencumbered\ndisencumbrance\ndisendow\ndisendowment\ndisenfranchised\ndisenfranchisement\ndisengage\ndisengaged\ndisengagement\ndisentagle\ndisentangle\ndisentangled\ndisentanglement\ndisenthral\ndisenthrall\ndisenthrallment\ndisenthralment\ndisenthrone\ndisentient\ndisentitle\ndisentitled\ndisequilibrium\ndisespouse\ndisestablish\ndisestablishment\ndisesteem\ndisestimation\ndisfavor\ndisfigure\ndisfigured\ndisfigurement\ndisfranchise\ndisfranchised\ndisfranchisement\ndisfurnish\ndisgorge\ndisgrace\ndisgraced\ndisgraceful\ndisgracefully\ndisgruntled\ndisgruntlement\ndisguise\ndisguised\ndisguisement\ndisgust\ndisgusted\ndisgustedly\ndisgusting\ndisgustingly\ndisgustingness\ndish\ndishabille\ndisharmony\ndishearten\ndisheartened\ndisheartening\ndisheartenment\ndished\ndisherison\ndishevel\ndisheveled\ndishing\ndishonest\ndishonestly\ndishonesty\ndishonor\ndishonorable\ndishonorableness\ndishonorably\ndishonored\ndishpan\ndishrag\ndishwasher\ndishwashing\ndishwater\ndishy\ndisillusioned\ndisincentive\ndisinclination\ndisincline\ndisinclined\ndisinfect\ndisinfectant\ndisinfection\ndisinfestation\ndisinflation\ndisingenuous\ndisingenuously\ndisingenuousness\ndisinherit\ndisinheritance\ndisinherited\ndisintegrate\ndisintegrated\ndisintegration\ndisintegrative\ndisinter\ndisinterest\ndisinterested\ndisinterestedly\ndisinterestedness\ndisinterment\ndisinvestment\ndisjecta\ndisjoin\ndisjoined\ndisjoint\ndisjointed\ndisjointedly\ndisjointedness\ndisjunct\ndisjunction\ndisjunctive\ndisk\ndiskette\ndiskindness\ndislikable\ndislike\ndisliked\ndisliking\ndislimb\ndislocate\ndislocated\ndislocation\ndislodge\ndislodgment\ndisloyal\ndisloyally\ndisloyalty\ndismal\ndismally\ndismals\ndismantle\ndismantled\ndismantling\ndismask\ndismast\ndismay\ndismember\ndismemberment\ndismet\ndismiss\ndismissal\ndismissible\ndismissive\ndismount\ndismounted\ndisobedience\ndisobedient\ndisobediently\ndisobey\ndisoblige\ndisobliging\ndisomatous\ndisorder\ndisordered\ndisorderliness\ndisorderly\ndisorganization\ndisorganize\ndisorganized\ndisorientation\ndisorienting\ndisown\ndisowned\ndisownment\ndispair\ndispansion\ndisparage\ndisparagement\ndisparaging\ndisparagingly\ndisparate\ndisparateness\ndisparity\ndispariumque\ndispart\ndispassion\ndispassionate\ndispassionately\ndispatch\ndispatched\ndispel\ndispensability\ndispensable\ndispensary\ndispensataion\ndispensation\ndispensations\ndispensatory\ndispense\ndispensed\ndispenser\ndispeople\ndispermic\ndispermy\ndisperse\ndispersed\ndispersion\ndispersive\ndispirit\ndispirited\ndispiritedly\ndisplace\ndisplaced\ndisplacement\ndisplacency\ndisplant\ndisplay\ndisplease\ndispleased\ndispleasing\ndispleasingly\ndispleasure\ndisplode\ndisplosion\ndisplume\ndisport\ndisposable\ndisposal\ndispose\ndisposed\ndisposed(p)\ndispositio\ndisposition\ndispossess\ndispossessed\ndispossession\ndispraise\ndispread\ndisprize\ndisproof\ndisproportion\ndisproportionate\ndisproportionated\ndisproportionately\ndisproportionateness\ndisprove\ndisputable\ndisputant\ndisputare\ndisputation\ndisputatious\ndisputatiously\ndispute\ndisputed\ndisputes\ndisqualification\ndisqualified\ndisqualify\ndisquiet\ndisquieted\ndisquieting\ndisquietingly\ndisquietude\ndisquiparant\ndisquisition\ndisquisitionary\ndisraeli\ndisreeli\ndisregard\ndisregarded\ndisregarding\ndisrelish\ndisrepair\ndisreputable\ndisreputably\ndisrepute\ndisrespect\ndisrespectful\ndisrespectfully\ndisrobe\ndisrupted\ndisruption\ndisruptive\ndisruptively\ndissatisfaction\ndissatisfied\ndissatisfy\ndisscit\ndissect\ndissection\ndisseize\ndissemblance\ndissemble\ndissembler\ndissembling\ndisseminate\ndisseminated\ndissemination\ndissension\ndissent\ndissenter\ndissentient\ndissentiente\ndissenting\ndissentious\ndissepiment\ndissert\ndissertation\ndisservice\ndisserviceable\ndissever\ndisseverance\ndissidence\ndissident\ndissilience\ndissimilar\ndissimilaritude\ndissimilarity\ndissimilation\ndissimilitude\ndissimulate\ndissimulation\ndissipate\ndissipated\ndissipation\ndissociability\ndissociable\ndissocial\ndissociate\ndissociated\ndissociation\ndissociative\ndissogeny\ndissolubility\ndissolute\ndissoluteness\ndissolution\ndissolvable\ndissolve\ndissolved\ndissolving\ndissonance\ndissonant\ndissuade\ndissuaded\ndissuading\ndissuasion\ndissuasive\ndissyllable\ndistaff\ndistain\ndistal\ndistally\ndistance\ndistant\ndistantly\ndistaste\ndistasteful\ndistastefully\ndistemper\ndistemperature\ndistempering\ndistend\ndistended\ndistensible\ndistension\ndistention\ndistich\ndistichous\ndistill\ndistillate\ndistillation\ndistiller\ndistillery\ndistinct\ndistinction\ndistinctive\ndistinctively\ndistinctly\ndistinctness\ndistingue\ndistinguish\ndistinguishable\ndistinguished\ndistinguishing\ndistored\ndistort\ndistortable\ndistorted\ndistorting\ndistortion\ndistortionist\ndistract\ndistracted\ndistractedly\ndistraction\ndistrain\ndistraint\ndistrait\ndistraught\ndistress\ndistressed\ndistressfully\ndistressing\ndistribuit\ndistributary\ndistribute\ndistributed\ndistribution\ndistributional\ndistributive\ndistributively\ndistributor\ndistrict\ndistrust\ndistrustful\ndistrustfully\ndisturb\ndisturbance\ndisturbed\ndisturbingly\ndisulfiram\ndisunction\ndisunctive\ndisunion\ndisunite\ndisunited\ndisunity\ndisusage\ndisuse\ndisused\ndisvaluation\ndisvalue\ndisyllabic\ndisyllable\ndit\ndita\nditch\nditchwater\nditheism\ndither\ndithering\ndithyramb\ndithyrambic\nditto\nditty\ndiurnal\ndiuturnal\ndiuturnity\ndiva\ndivagate\ndivagation\ndivan\ndivaricate\ndivarication\ndive\ndive-bombing\ndivellicate\ndiver\ndiverge\ndivergence\ndivergency\ndivergent\ndiverging\ndivers\ndivers(a)\ndiverscolored\ndiverse\ndiverseness\ndiversification\ndiversified\ndiversify\ndiversion\ndiversionary\ndiversity\ndivert\ndiverted\ndiverticulitis\ndiverticulosis\ndiverticulum\ndivertimento\ndiverting\ndivertissement\ndives\ndivest\ndivested\ndivestiture\ndivestment\ndivi-divi\ndividable\ndivide\ndivided\ndividend\ndividers\ndividing\ndividing(a)\ndivina\ndivination\ndivinatory\ndivine\ndivinely\ndivineness\ndiviner\ndiving\ndivining\ndivinities\ndivinity\ndivino\ndivinum\ndivisible\ndivision\ndivisional\ndivisions\ndivisor\ndivoce\ndivorce\ndivorced\ndivorcee\ndivorcement\ndivot\ndivulge\ndivulgence\ndivulsion\ndivvy\ndixi\ndixie\ndixies\ndixit\ndizdar\ndizen\ndizygotic\ndizzard\ndizzily\ndizziness\ndizzy\ndjibouti\ndjiboutian\ndo\ndo-it-yourself\ndo-nothing(a)\ndo-si-do\ndoa\ndoberman\ndobra\ndobson\ndocendo\ndocent\ndocerekill\ndoces\ndocet\ndocibleness\ndocile\ndocility\ndocimastic\ndock\ndockage\ndocked\ndocket\ndocking\ndockside\ndockyard\ndoctor\ndoctoral\ndoctorfish\ndoctors\ndoctrinaire\ndoctrinal\ndoctrinally\ndoctrine\ndocument\ndocumentary\ndocumentation\ndocumented\ndodder\ndodderer\ndoddering\ndodecagon\ndodecahedron\ndodge\ndodger\ndodging\ndodo\ndoe\ndoeil\ndoer\ndoes\ndoeskin\ndoesn\ndoesnt\ndoet\ndoeuvre\ndoff\ndog\ndog-ear\ndog-eared\ndogbane\ndogcart\ndoge\ndogfish\ndogged\ndoggedly\ndoggedness\ndogger\ndoggerel\ndogging\ndoggish\ndoggo\ndoghold\ndoghouse\ndogie\ndoglike\ndogma\ndogmatic\ndogmatically\ndogmatics\ndogmatism\ndogmatist\ndogmatize\ndogmatizer\ndogobah\ndogs\ndogsick\ndogsled\ndogtooth\ndogtrot\ndogwatch\ndogweary\ndogwood\ndoha\ndohickey\ndoily\ndoing\ndoings\ndoit\ndokhma\ndol\ndolabriform\ndolce\ndoldrums\ndole\ndoleful\ndolefully\ndolefulness\ndolere\ndolesome\ndolichocephalic\ndolichonyx\ndolichos\ndolichotis\ndoliolidae\ndoliolum\ndolittle\ndoll\ndollar\ndollarfish\ndollhouse\ndollop\ndolly\ndolman\ndolmas\ndolmen\ndolomite\ndolor\ndolore\ndolorem\ndolorific\ndoloris\ndolorous\ndolour\ndolphin\ndolphinfish\ndolphinum\ndolt\ndoltish\ndomain\ndombeya\ndomdaniel\ndome\ndomed\ndomesday\ndomesdaybook\ndomesman\ndomestic\ndomestically\ndomesticate\ndomesticated\ndomestication\ndomesticity\ndomi\ndomicile\ndomiciled\ndomiciliary\ndomiciliated\ndominance\ndominant\ndominantly\ndominate\ndominated\ndominating\ndomination\ndomine\ndomineer\ndomineering\ndomineeringly\ndomini\ndominica\ndominical\ndominican\ndominie\ndominion\ndominique\ndomino\ndominoes\ndominos\ndominus\ndomum\ndon\ndon't-know\ndona\ndonar\ndonated\ndonation\ndonative\ndonatus\ndone\ndonec\ndonee\ndonetsk\ndong\ndongue\ndonjon\ndonkey\ndonna\ndonne\ndonnean\ndonner\ndonnybrook\ndono\ndonor\ndonship\ndont\ndonzel\ndoodad\ndoodia\ndoodle\ndoodlebug\ndoodlesack\ndoohickey\ndoolie\ndooly\ndoom\ndoomage\ndoomed\ndoomsday\ndoor\ndoor-to-door\ndoorbell\ndoorframe\ndoorjamb\ndoorkeeper\ndoorknob\ndoorlock\ndoormat\ndoornail\ndoorplate\ndoorpost\ndoors\ndoorsill\ndoorstop\ndoorway\ndooryard\ndopamine\ndope\ndoped\ndophagy\ndoquet\ndor\ndorado\ndorbeetle\ndordre\ndorian\ndoric\ndoris\ndorm\ndormancy\ndormant\ndormant(ip)\ndormer\ndormeuse\ndormie\ndormir\ndormitory\ndormouse\ndoronicum\ndorotheanthus\ndorp\ndorsal\ndorsally\ndorser\ndorsigerous\ndorsoventral\ndorsoventrally\ndorsum\ndort\ndory\ndorylinae\ndoryopteris\ndos\ndosages\ndose\ndosed\ndosemeter\ndosser\ndossier\ndossil\ndostoevski\ndostoevskian\ndosvidanya\ndot\ndotage\ndotard\ndotation\ndote\ndoth\ndoting\ndots\ndotted\ndotterel\ndottings\ndottle\ndotty\ndouala\ndouanier\ndouble\ndouble-barreled\ndouble-bedded\ndouble-bogey\ndouble-breasted\ndouble-chinned\ndouble-crosser\ndouble-edged\ndouble-faced\ndouble-geared\ndouble-jointed\ndouble-prop\ndouble-quick\ndouble-spaced\ndouble-spacing\ndoubleacrostic\ndoubled\ndoubledistilled\ndoubleedged\ndoubleentendre\ndoublefaced\ndoubleminded\ndoubles\ndoubleshotted\ndoublespeak\ndoublet\ndoublethink\ndoubletongued\ndoublets\ndoubling\ndoubloon\ndoubly\ndoublydyed\ndoubt\ndoubtful\ndoubtfully\ndoubtfulness\ndoubthesitate\ndoubting\ndoubtless\ndoubts\ndouceur\ndouche\ndough\ndoughface\ndoughfaced\ndoughnut\ndoughty\ndoughy\ndouloureux\ndour\ndourly\ndouroucouli\ndouse\ndouvre\ndoux\ndove\ndovecote\ndovelike\ndover\ndoves\ndovetail\ndovetailed\ndovetailing\ndovishness\ndovyalis\ndowager\ndowdily\ndowdiness\ndowdy\ndowel\ndoweling\ndower\ndowered\ndowerless\ndowitcher\ndown\ndown(a)\ndown(p)\ndown-and-out\ndown-bow\ndown-to-earth\ndownbeat\ndownbound\ndowncast\ndowndraft\ndowneaster\ndownfall\ndownfield\ndowngrade\ndownhearted\ndownheartedness\ndownhill\ndowniness\ndownmarket\ndownpour\ndownreaching\ndownright\ndownrightness\ndownriver\ndowns\ndownscale\ndownspin\ndownstage\ndownstairs\ndownstream\ndownstroke\ndownswing\ndowntick\ndowntime\ndowntown\ndowntrodden\ndownturn\ndownward\ndownward(ip)\ndownwards\ndownwind\ndowny\ndowry\ndowse\ndowsing\ndoxepin\ndoxology\ndoxorubicin\ndoxy\ndoxycycline\ndoyen\ndoyley\ndoze\ndozen\ndozy\ndphil\ndr\ndrab\ndraba\ndrabble\ndrably\ndracaena\ndracenaceae\ndrachma\ndraco\ndracocephalum\ndraconian\ndracontium\ndracula\ndracunculidae\ndracunculus\ndraff\ndraft\ndraftee\ndrafter\ndrafting\ndraftsman\ndrafty\ndrag\ndragee\ndragging\ndraggingly\ndraggle\ndraggletail\ndraggletailed\ndragnet\ndragoman\ndragon\ndragonet\ndragonfly\ndragonhead\ndragonnade\ndragons\ndragoon\ndrain\ndrainage\ndrainboard\ndrained\ndraining\ndrainplug\ndrake\ndrakes\ndram\ndrama\ndramatic\ndramatically\ndramatics\ndramatis\ndramatist\ndramatization\ndramaturgic\ndramaturgy\ndrame\ndrap\ndrape\ndraped\ndraper\ndrapery\ndrastic\ndrastically\ndrat\ndraught\ndraughts\ndraughtsman\ndravidian\ndraw\ndrawback\ndrawbridge\ndrawcansir\ndrawee\ndrawer\ndrawers\ndrawing\ndrawingroom\ndrawknife\ndrawl\ndrawler\ndrawling\ndrawn\ndrawn-out\ndrawnat\ndrawnwork\ndrawstring\ndrawtogether\ndray\ndrayman\ndread\ndreadbolted\ndreadful\ndreadfully\ndreadless\ndreadnought\ndream\ndreamed(a)\ndreamer\ndreamily\ndreaming\ndreamland\ndreamless\ndreamlike\ndreams\ndreamy\ndreary\ndreck\ndredge\ndredger\ndredging\ndreg\ndreggy\ndregs\ndreissena\ndrench\ndrenched\ndrenching\ndrepanididae\ndrepanis\ndresden\ndress\ndressage\ndressed\ndressed(p)\ndresser\ndressing\ndressmaker\ndressmaking\ndressy\ndreyfus\ndribble\ndribbler\ndribbling\ndriblet\ndriblets\ndried\ndried-up\ndrier\ndrift\ndriftage\ndrifted\ndriftest\ndriftfish\ndrifting\ndriftless\ndriftwood\ndrill\ndrilled\ndrilling\ndrily\ndrimys\ndrink\ndrinkable\ndrinker\ndrinking\ndrinks\ndrip\ndrip-dry\ndripping\ndrippy\ndrive\ndrive-in\ndrivel\ndriveler\ndriveling\ndriveller\ndriven\ndriver\ndriveshaft\ndriveway\ndriving\ndrixoral\ndrizzle\ndrizzling\ndroger\ndrogheda\ndrogue\ndroil\ndroit\ndrole\ndroll\ndrollery\ndrollish\ndromaeosaur\ndromaeosauridae\ndromaius\ndromedary\ndrone\ndronish\ndrony\ndrool\ndroop\ndrooping\ndroopingly\ndrop\ndrop-leaf\ndropkick\ndropkicker\ndroplet\ndropline\ndropout\ndropp\ndropped\ndropper\ndropping\ndroppings\ndrops\ndropseed\ndropsical\ndropsy\ndrosera\ndroseraceae\ndroshki\ndroshky\ndrosky\ndrosophila\ndrosophilidae\ndrosophyllum\ndross\ndrossiness\ndrought\ndroughter\ndrouth\ndrouthy\ndrove\ndrover\ndroves\ndrow\ndrown\ndrowned\ndrowse\ndrowsily\ndrowsiness\ndrowsy\ndrowze\ndrub\ndrubbing\ndrudge\ndrudgery\ndrudging\ndrug\ndrugged\ndrugget\ndruggist\ndrugless\ndrugs\ndrugstore\ndruid\ndrum\ndrumbeat\ndrumhead\ndrumlin\ndrummer\ndrumming\ndrums\ndrumstick\ndrunk\ndrunk-and-disorderly\ndrunkard\ndrunken\ndrunkenly\ndrunkenness\ndrupaceous\ndrupe\ndrupelet\ndry\ndry-cleaned\ndry-gulching\ndry-shod\ndryad\ndryadella\ndryas\ndryasdust\ndryden\ndryer\ndrygoods\ndrymarchon\ndrymoglossum\ndrynaria\ndryness\ndrynurse\ndryopithecine\ndryopithecus\ndryopteridaceae\ndryopteris\ndrypis\ndsillusionner\ndsorient\u0002\ndt\ndu\ndual\ndualism\ndualist\ndualistic\nduality\nduarchy\ndub\ndubash\ndubbin\ndubbing\ndubiety\ndubious\ndubitancy\ndubitation\ndubitousness\ndublin\ndubliner\ndubrovnik\nducal\nducat\nduce\nduchess\nduchy\nduck\nduckbill\nducking\nduckling\nduckpin\nduckpins\nducks\nduckweed\nduct\nductile\nductility\nductless\nductule\ndud\ndude\ndudeen\ndudgeon\nduds\ndue\ndue(p)\nduel\ndueler\nduelist\nduello\ndueness\nduenna\ndues\nduet\nduff\nduffel\nduffer\ndug\ndugong\ndugongidae\ndugout\nduke\ndukedom\ndukes\ndulce\ndulcet\ndulciana\ndulcification\ndulcify\ndulcimer\ndulcinea\ndulcitude\ndulcius\ndulcorate\ndulcoration\ndulden\ndulia\ndull\ndullard\ndulled\ndullhead\ndullness\ndully\ndulse\nduly\ndum\nduma\ndumb\ndumbbell\ndumbfounded\ndumbly\ndumbness\ndumbwaiter\ndumdum\ndumetella\ndumfound\ndumfounder\ndumfoundered\ndummodo\ndummy\ndump\ndumpcart\ndumped\ndumpiness\ndumping\ndumpish\ndumpling\ndumps\ndumpy\ndumuzi\ndun\ndunce\ndunces\ndunderhead\ndunderpate\ndune\ndung\ndungeon\ndungeons\ndunghill\ndunk\ndunked\ndunker\ndunkirk\nduo\nduodecimal\nduodecimo\nduodenal\nduodenary\nduodenum\nduologue\ndupe\nduped\nduple\nduplex\nduplicable\nduplicate\nduplication\nduplicator\nduplicature\nduplicidentata\nduplicity\ndura\ndurability\ndurable\ndurableness\ndurables\ndural\nduralumin\ndurance\nduration\ndurative\ndurbar\ndure\nduress\ndurga\ndurham\ndurian\nduring\ndurio\ndurity\ndurmast\ndurra\ndurum\ndurwan\ndusanbe\ndusicyon\ndusk\ndusky\ndusseldorf\ndust\ndustcloth\ndustcolored\nduster\ndustman\ndustmop\ndustoori\ndustpan\ndusty\ndutch\ndutchman\ndutchman's-pipe\nduteous\ndutiable\nduties\ndutiful\ndutifully\ndutifulness\nduty\nduty-bound(p)\nduty-free\nduumvirate\nduval\ndwarf\ndwarfed\ndwarfish\ndwarfishness\ndwarfism\ndwell\ndweller\ndwelling\ndwellings\ndwerger\ndwindle\ndwindling\ndyad\ndyadic\ndyarchy\ndyaus\ndybbuk\ndye\ndye-works\ndyed\ndyed-in-the-wool\ndyeing\ndyer\ndyes\ndyewood\ndying\ndying(a)\ndyirbal\ndyke\ndylan\ndynamic\ndynamical\ndynamically\ndynamics\ndynamism\ndynamitard\ndynamite\ndynamiter\ndynamo\ndynamometer\ndynast\ndynastic\ndynasty\ndyne\ndysdercus\ndysentery\ndysfunction\ndysfunctional\ndysgenesis\ndysgenic\ndysgenics\ndyskinesia\ndyslectic\ndyslexia\ndyslexic\ndyslogistic\ndysmenorrhea\ndysmerogenesis\ndysmeromorph\ndyspepsia\ndyspeptic\ndysphemistic\ndysphony\ndysphoria\ndysphoric\ndysplasia\ndysplastic\ndyspnaeal\ndyspnaeic\ndyspncea\ndyspnea\ndysprosium\ndystopia\ndystopian\ndytiscidae\ne\ne'en\nea\neach\neach(a)\neacles\neager\neagerly\neagerness\neagle\neagle-eyed\neagleeyed\neagles\neaglet\neagre\nean\neandem\near\nearache\neardeafening\neardrum\neared\nearflap\nearful\nearl\nearldom\nearless\nearlier\nearliness\nearlobe\nearly\nearly(a)\nearlyish\nearmark\nearn\nearned\nearner\nearnest\nearnestly\nearnestness\nearnings\nearphone\nearpiercing\nearrending\nearring\nears\nearshot\nearsplitting\nearth\nearth-god\nearth-goddess\nearthball\nearthborn\nearthbound\nearthen\nearthenware\nearthflax\nearthlike\nearthling\nearthly\nearthlyminded\nearthnut\nearthquake\nearths\nearthshaking\nearthstar\nearthtongue\nearthwork\nearthworm\nearthy\nearwig\nearwitness\nease\neasel\neasement\neasier\neasily\neasiness\neasing\neast\neast-central\neast-sider\neastbound\neaster\neasterly\neastern\neasterner\neasternmost\neastertide\neastside\neastward\neasy\neasygoing\neasygoingness\neat\neatable\neatables\neatage\neaten\neater\neating\neau\neaux\neaves\neavesdropper\neavesdropping\neb\nebauche\nebb\nebb(a)\nebbing\nebbs\nebbtide\nebdomarius\nebenaceae\nebenales\nebenezer\nebionite\neblis\nebon\nebony\neboulement\nebriety\nebriosity\nebro\nebullient\nebulliently\nebullioscope\nebullition\neburin\neburophyton\necarte\necballium\necbatic\necce\neccentric\neccentricity\necchymosis\necclesiarch\necclesiastic\necclesiastical\necclesiastically\necclesiasticism\necclesiolatry\necclesiological\necclesiologist\necclesiology\necco\neccrine\neccrinology\necdemic\nechafaudage\nechappee\nechapper\nechelon\necheneididae\necheneis\nechidna\nechidnophaga\nechinacea\nechinate\nechinocactus\nechinocereus\nechinochloa\nechinococcosis\nechinococcus\nechinoderm\nechinodermata\nechinoidea\nechinops\nechium\necho\nechoes\nechoic\nechoing\nechoing(a)\necholess\necholocation\nechovirus\neclair\neclaircissement\neclampsia\neclat\neclectic\neclecticism\neclesctism\neclipse\neclipsed\necliptic\neclogue\necobabble\necological\necologically\necologist\necology\neconometric\neconometrician\neconometrics\neconomic\neconomical\neconomically\neconomics\neconomist\neconomize\neconomizer\neconomy\necosystem\necphonesis\necrasez\necrhythmus\necstacy\necstasis\necstasy\necstatic\necstatica\necstatically\nectasy\necto\nectoderm\nectodermal\nectogenous\nectomorph\nectomorphic\nectoparasite\nectopistes\nectoplasm\nectoproct\nectoprocta\nectropy\nectstasies\nectype\necuador\necuadorian\necumenic\necumenical\necumenism\neczema\ned\nedacious\nedacity\nedam\nedaphosauridae\nedaphosaurus\nedax\nedda\neddington\neddish\neddy\nedel\nedelweiss\nedema\nedematous\neden\nedental\nedentata\nedentate\nedentulous\nedge\nedge(a)\nedged\nedgeless\nedger\nedgeways\nedgewise\nedginess\nedging\nedgy\nedibility\nedible\nedibles\nedict\nedification\nedifice\nedified\nedify\nedifying\nedile\nedinburgh\nedipus\nedirne\nedison\nedit\nediting\neditio\nedition\neditor\neditorial\neditorially\neditorship\nedmonton\nedmontonia\nedmontosaurus\neducate\neducated\neducation\neducational\neducationally\neducationist\neducative\neducator\neduce\neduct\neduction\nedulcorate\nedward\nedwardian\neel\neelblenny\neelgrass\neellike\neelpout\neelspear\neelworm\neerie\neerily\neeriness\neether\nefface\neffaceable\neffaced\neffacement\neffect\neffective\neffectively\neffectiveness\neffector\neffects\neffectual\neffectually\neffectuate\neffeminacy\neffeminate\neffendi\nefferent\neffervesce\neffervescence\neffervescent\neffete\nefficacious\nefficaciously\nefficacy\nefficiency\nefficient\nefficiently\neffigies\neffigy\nefflation\neffleurage\neffleurer\nefflorescence\nefflorescent\neffluence\neffluent\neffluvium\neffluxion\nefform\nefformation\neffort\neffortful\neffortfulness\neffortless\neffortlessly\neffortlessness\nefforts\neffrontery\neffulgence\neffulgent\neffuse\neffused\neffusion\neffusive\neffusively\neffusiveness\neft\neftsoons\neg\negad\negalitarian\negalitarianism\negeria\negesta\negestion\neget\negg\negg-and-dart\negg-producing(a)\negg-shaped\neggar\neggbeater\neggcup\negghead\neggnog\neggplant\neggs\neggshake\neggshaped\neggshell\nego\negocentric\negohood\negoism\negoist\negoistic\negoistical\negomania\negomaniac\negotism\negotist\negotistic\negotistical\negotistically\negregious\negregiously\negress\negression\negret\negretta\negurgitate\negypt\negyptian\negyptology\neheu\neichhornia\neicosahedron\neider\neiderdown\neidetic\neidoloclast\neidolon/gr\neidouranion\neight\neight-spot\neighteen\neighteenth\neighth\neighties\neightieth\neightpence\neightpenny\neightsome\neighty\neigner\neile\neileton\neimeriidae\nein\neinstein\neinsteinian\neinsteinium\neira\neisegesis\neisen\neisenhower\neisteddfod\neither\nej\nejaculate\nejaculation\nejaculator\nejaculatory\neject\nejecta\nejection\nejectment\neke\nel\nelaborate\nelaborately\nelaborateness\nelaboration\nelaeagnaceae\nelaeagnus\nelaeis\nelaeocarpaceae\nelaeocarpus\nelagatis\nelaine\nelamite\nelamitic\nelan\neland\nelanoides\nelanus\nelaphe\nelaphurus\nelapid\nelapidae\nelapse\nelapsed\nelapsing\nelasmobranch\nelasmobranchii\nelastance\nelastic\nelasticity\nelasticized\nelastin\nelastomer\nelastoplast\nelate\nelated\nelateridae\nelating\nelation\nelbe\nelbow\nelbowing\nelbowroom\nelbows\neld\nelder\nelderberry\nelderly\nelders\neldership\neldest\neldritch\nelead\nelecampane\nelect\nelect(ip)\nelected\nelection\nelectioneering\nelective\nelector\nelectoral\nelectorate\nelectra\nelectric\nelectrical\nelectrically\nelectrician\nelectricity\nelectrification\nelectrify\nelectrifying\nelectrobiology\nelectrocardiogram\nelectrochemistry\nelectrocute\nelectrocution\nelectrocutioner\nelectrode\nelectrodeposition\nelectrodynamometer\nelectroencephalogram\nelectroencephalograph\nelectrograph\nelectrolier\nelectrologist\nelectrolysis\nelectrolyte\nelectrolytic\nelectrolyze\nelectromagnet\nelectromagnetic\nelectromagnetism\nelectromechanical\nelectrometer\nelectromotive\nelectromyogram\nelectromyograph\nelectromyography\nelectron\nelectronic\nelectronically\nelectronics\nelectrophoresis\nelectrophoretic\nelectrophoridae\nelectrophorus\nelectroplate\nelectroscope\nelectrostatic\nelectrostatically\nelectrotherapist\nelectrotype\nelectrum\nelectuary\neleemosynary\nelegaic\nelegance\nelegant\nelegantiarum\nelegantly\nelegiac\nelegiacs\nelegist\nelegy\nelement\nelemental\nelementarily\nelementary\nelementary(a)\nelements\nelemi\nelench\nelenchi\nelenchus\neleocharis\neleotridae\nelephant\nelephant's-foot\nelephantiasis\nelephantidae\nelephantine\nelephantopus\nelephantus\nelephas\nelettaria\neleusine\neleutherian\neleutherodactylus\nelevate\nelevated\nelevation\nelevator\neleve\neleven\neleven-plus\neleventh\nelf\nelfin\nelfish\nelflike\nelicit\nelicited\nelief\neligibility\neligible\nelijah\neliminate\nelimination\neliomys\neliot\nelision\nelite\nelitism\nelitist\nelixation\nelixir\nelizabeth\nelizabethan\nelk\nell\nelli\nellipse\nellipsis\nellipsoid\nelliptic\nelliptical\nelm\nelmos\nelocation\nelocution\nelocutionary\nelocutionist\nelodea\neloge\nelongate\nelongated\nelongation\nelope\nelopement\nelopidae\nelops\neloquence\neloquent\neloquently\nelre\nelse\nelse(ip)\nelsewhere\nelsholtzia\neluate\nelucidate\nelucidation\nelude\nelul\nelusion\nelusive\nelusiveness\nelusory\nelution\nelutriate\neluvium\nelver\nelves\nelymus\nelysian\nelysium\nelytron\nelytrum\nelzevir\nem\nemaciated\nemaciation\nemanate\nemanation\nemancipate\nemancipated\nemancipating\nemancipation\nemancipator\nemaniation\nemarginate\nemasculate\nemasculation\nembalm\nembalmer\nembalmment\nembalms\nembankment\nembarassed\nembarcation\nembargo\nembark\nembarkation\nembarras\nembarrass\nembarrassed\nembarrassing\nembarrassingly\nembarrassment\nembase\nembassador\nembassy\nembattled\nembay\nembed\nembedded\nembellish\nembellished\nembellishment\nember\nemberiza\nemberizidae\nembers\nembezzle\nembezzled\nembezzlement\nembezzler\nembioptera\nembiotocidae\nembitter\nembitterment\nemblazon\nemblem\nemblematic\nembodied\nembodiment\nembody\nembogue\nembolden\nemboldened\nembolectomy\nembolic\nembolism\nembolismal\nembolus\nembonpoint\nembosom\nembosomed\nemboss\nembossment\nembothrium\nembouchure\nembowed\nembowel\nembowered\nembrace\nembrangle\nembranglement\nembrasure\nembrocation\nembroider\nembroidered\nembroiderer\nembroideress\nembroidery\nembroil\nembroiled\nembroilment\nembrown\nembryo\nembryo(a)\nembryology\nembryonic\nembryotic\nemendation\nemendatory\nemended\nemerald\nemerge\nemergence\nemergency\nemergent\nemerging\nemeritus\nemersion\nemerson\nemery\nemesis\nemetic\nemetrol\nemeute\nemication\nemigrant\nemigrate\nemigration\nemile\nemilia\nemilia-romagna\neminence\neminent\neminently\nemir\nemirate\nemissary\nemission\nemissum\nemit\nemitted\nemitter\nemitting\nemmanthe\nemmanuel\nemmeleia\nemmenthal\nemmer\nemmet\nemmigrant\nemo\nemollient\nemolument\nemotion\nemotional\nemotionality\nemotionally\nemotionless\nemotionlessness\nemotive\nempale\nempalement\nempanel\nempathic\nempathy\nemperor\nempetraceae\nempetrum\nemphasis\nemphasize\nemphasizing\nemphatic\nemphatically\nemphysema\nemphysematous\nempierce\nempire\nempirema\nempires\nempiric\nempirical\nempirically\nempiricism\nempiricist\nemplacement\nemplastrum\nemploy\nemployable\nemploye\nemployed\nemployee\nemployer\nemployment\nemply\nempo\nempoison\nemporium\nempower\nempowered\nempress\nempressement\nempressment\nemprise\nempta\nemptied\nemptiness\nemption\nemptor\nempty\nempty-handed\nemptyhanded\nemptying\nempurple\nempurpled\nempyreal\nempyrean\nempyreuma\nempyreumatic\nempyrosis\nemu\nemulate\nemulation\nemulous\nemulously\nemulsified\nemulsifier\nemulsion\nemulsive\nemunctory\nemydidae\nen\nenable\nenablement\nenabling\nenact\nenactment\nenallage\nename\nenamel\nenameled\nenameler\nenamelist\nenamelware\nenamine\nenamor\nenamored\nenanthem\nenanthema\nenantiomorph\nenantiomorphism\nenate\nenbas\nencage\nencamp\nencampment\nencasement\nencaustic\nenceinte\nencelia\nenceliopsis\nencephalartos\nencephalitis\nenchafe\nenchain\nenchant\nenchanted\nenchanter\nenchanting\nenchantment\nenchantress\nenchase\nenchilada\nenchiridion\nenchymatous\nencintcture\nencircle\nencircled\nencircling\nencircling(a)\nenclave\nenclose\nenclosed\nenclosure\nencoding\nencolure\nencomiast\nencomiastic\nencomium\nencompass\nencompassed\nencompassing(a)\nencompassment\nencompilation\nencore\nencounter\nencourage\nencouragement\nencouraging\nencouragingly\nencratism\nencratite\nencroach\nencroaching(a)\nencroachment\nencuirassed\nencumber\nencumbered\nencumbrance\nencyclia\nencyclic\nencyclical\nencyclopedia\nencyclopedic\nencyclopedical\nencyclopedist\nencysted\nend\nend-all\nend-rhymed\nend-stopped\nendaemonism\nendaemonist\nendamage\nendameba\nendamoeba\nendamoebidae\nendanger\nendangered\nendear\nendearment\nendeavor\nended\nendemic\nendenization\nendenizen\nendermic\nendimanch\nendimanche\nending\nendings\nendive\nendless\nendlessly\nendlessness\nendlong\nendmost\nendo\nendocarditis\nendocardium\nendocarp\nendocentric\nendocrine\nendocrinology\nendoderm\nendodontic\nendodontics\nendodontist\nendoergic\nendogamic\nendogamous\nendogamy\nendogenetic\nendogenous\nendome\nendometrial\nendometriosis\nendometrium\nendomorph\nendomorphic\nendomorphy\nendomycetales\nendoparasite\nendoparasitic\nendoplasm\nendorphin\nendorse\nendorsed\nendorsement\nendorser\nendoscope\nendoscopic\nendoscopy\nendoskeleton\nendosmose\nendosmosis\nendosmosmic\nendosmotic\nendosperm\nendospore\nendothelial\nendothelium\nendothermic\nendow\nendowed\nendowment\nendpoint\nendrology\nends\nendue\nendurance\nendure\nendured\nenduring\nenduringly\nendways\nendwise\nene\nenema\nenemy\nenemy(a)\nenemys\nenergetic\nenergetically\nenergid\nenergies\nenergize\nenergizing\nenergumen\nenergy\nenervate\nenervated\nenervation\nenets\nenface\nenfant\nenfeeble\nenfeoffment\nenfield\nenfilade\nenflurane\nenfold\nenforce\nenforceable\nenforced\nenforcement\nenfranchise\nenfranchised\nenfranchisement\nengage\nengaged\nengagement\nengaging\nengarrison\nengelmannia\nengels\nengender\nengine\nengineer\nengineering\nenginery\nengird\nengland\nenglish\nenglish-speaking\nenglishman\nenglishwoman\nengobe\nengorge\nengorgement\nengram\nengraulidae\nengraulis\nengrave\nengraved\nengraver\nengraving\nengross\nengrossed\nengulf\nengulfed\nenhance\nenhanced\nenhancement\nenharmonic\nenhydra\nenigma\nenigmatic\nenigmatical\nenim\neniwetok\nenjoin\nenjoy\nenjoyable\nenjoyableness\nenjoying\nenjoyment\nenkephalin\nenki\nenkidu\nenkindle\nenlarge\nenlarged\nenlargement\nenlarger\nenleague\nenlighten\nenlightened\nenlightening\nenlightenment\nenlil\nenlist\nenlisted(a)\nenlistment\nenliven\nenlivened\nenmesh\nenmeshed\nenmity\nennoble\nennoblement\nennobling\nennui\nenologist\nenology\nenormity\nenormous\nenormously\nenormousness\nenough\nenphagy\nenrage\nenrapture\nenraptured\nenraptures\nenravish\nenravished\nenravishing\nenravishment\nenrich\nenrichment\nenrobe\nenroll\nenrolled\nenrollee\nenrollment\nens\nensample\nensanguined\nensconce\nensconced\nensemble\nensete\nenshrine\nenshrinement\nensiform\nensign\nensilage\nensis\nenslave\nenslaved\nenslavement\nensnare\nensue\nensuing\nensure\nentablature\nentail\nentandrophragma\nentangle\nentangled\nentanglement\nentbehr\nentbehre\nentelechy\nentellus\nentendre\nentendu\nentente\nenter\nentered\nenteric\nentering\nentering(p)\nenteritis\nenterobacteriaceae\nenterobius\nenterolobium\nenteron\nenterotoxemia\nenterovirus\nenterprise\nenterprising\nenterprisingly\nentertain\nentertained\nentertainer\nentertaining\nentertainingly\nentertainment\nentete\nentgtg\nenthrall\nenthrallment\nenthrone\nenthronement\nenthusiasm\nenthusiast\nenthusiastic\nenthusiastically\nenthymeme\nentice\nenticement\nenticing\nentire\nentirely\nentirety\nentitle\nentitled\nentitlement\nentity\nentium\nentoloma\nentolomataceae\nentomb\nentombment\nentomological\nentomologist\nentomology\nentomophilous\nentomophobia\nentomophthora\nentomophthoraceae\nentomophthorales\nentomostraca\nentoproct\nentoprocta\nentourage\nentozoan\nentozoic\nentozoon\nentr'acte\nentrails\nentrain\nentrammel\nentrance\nentranced\nentrancement\nentrancing\nentrant\nentrap\nentrate\nentre\nentreat\nentreaty\nentree\nentremet\nentrenched\nentrenchment\nentrepot\nentrepreneur\nentrepreneurial\nentresol\nentropy\nentrust\nentry\nentwine\nenucleate\nenumerate\nenumeration\nenunciate\nenunciation\nenunciative\nenured\nenvelope\nenveloping(a)\nenvenom\nenvenomed\nenviable\nenviably\nenvious\nenviously\nenviousness\nenviron\nenvironment\nenvironmental\nenvironmentalist\nenvironmentally\nenvirons\nenvisioned\nenviva\nenvoy\nenvy\nenwrap\nenwrapped\nenzymatic\nenzyme\neocene\neohippus\neolian\neolith\neolithic\neolus\neon\neonian\neoraptor\neos\neosin\neosinophil\neosinophilia\neosinophilic\nepacme\nepacridaceae\nepacris\nepact\nepagoge\nepanalepsis\nepanaphora\nepanchement\nepanodos\nepanorthosis\neparch\neparchial\neparchy\nepaulet\nepaulette\nepauliere\nepee\neperdu\neperdument\nepergne\neperon\nephah\nephedra\nephedraceae\nephedrine\nephemera\nephemeral\nephemerality\nephemerid\nephemeridae\nephemeris\nephemeroptera\nephesian\nephestia\nephialtes\nephippidae\nephippiorhynchus\nephor\nepic\nepicalyx\nepicarp\nepicarpal\nepicedial\nepicedium\nepicene\nepicenter\nepicier\nepicure\nepicurean\nepicureanism\nepicurism\nepicurus\nepicycle\nepicyclic\nepicycloid\nepidemic\nepidemiologic\nepidemiologist\nepidemiology\nepidendron\nepidendrum\nepidermis\nepidiascope\nepididymis\nepidural\nepigaea\nepigenesis\nepiglottis\nepigone\nepigram\nepigrammatic\nepigrammatist\nepigraph\nepigraphy\nepikeratophakia\nepilachna\nepilepsy\nepileptic\nepilithic\nepilobium\nepilogue\nepimedium\nepimetheus\nepimorphic\nepinephelus\nepingles\nepipactis\nepiphany\nepiphenomenon\nepiphora\nepiphyllum\nepiphyseal\nepiphysis\nepiphytic\nepiphytotic\nepiplexis\nepipremnum\nepiscia\nepiscopacy\nepiscopal\nepiscopalian\nepiscopalianism\nepiscopate\nepisiotomy\nepisode\nepisodic\nepisodically\nepispadias\nepistemic\nepistemologist\nepistemology\nepistle\nepistles\nepistolary\nepistyle\nepitaph\nepithalamium\nepithelial\nepitheliod\nepithelioma\nepithelium\nepithem\nepithet\nepitome\nepitomist\nepitomize\nepitomizer\nepizoan\nepizoic\nepizootic\nepkwele\nepoch\nepochal\nepode\nepona\neponym\neponymous\nepopee\nepopoca\nepos\nepoxy\neppur\nepsilon\nepsom\neptatretus\neptesicus\nepulation\nepulotic\nepuration\nequable\nequably\nequal\nequality\nequalization\nequalize\nequalized\nequalizer\nequally\nequanimity\nequatability\nequatable\nequate\nequation\nequations\nequator\nequatorial\nequerry\nequestrian\nequetus\nequiangular\nequibalanced\nequidae\nequidistance\nequidistant\nequilateral\nequilibration\nequilibrio\nequilibrium\nequinam\nequine\nequinoctial\nequinox\nequip\nequipage\nequiparant\nequipment\nequipmentage\nequipoise\nequipoised\nequipollence\nequipollent\nequiponderance\nequiponderant\nequiponderous\nequipotent\nequipped\nequiprobable\nequisetaceae\nequisetales\nequisetum\nequitable\nequitableness\nequitably\nequitation\nequity\nequivalence\nequivalent\nequivocal\nequivocalness\nequivocate\nequivocation\nequivoque\nequus\ner\nera\neradicable\neradicate\neradication\neragrostis\neram\neranthis\nerase\neraser\nerasmian\nerasmus\nerastian\nerastianism\nerasure\nerat\nerato\neratosthenes\nerbium\nere\nerebus\nerect\nerected\nerectile\nerecting\nerection\nerectly\nerectness\neremite\neremitic\nereshkigal\nereste\nerethizon\nerethizontidae\neretmochelys\nerewhile\nerewhon\nerg\nergo\nergonomic\nergophobia\nergosterol\nergot\nergotic\nergotism\nergotize\nergotropic\nergotropism\nerianthus\nerica\nericaceae\nericales\nerigeron\nerignathus\nerinaceidae\nerinaceus\neriobotrya\neriocaulaceae\neriocaulon\neriodictyon\neriogonum\neriometer\neriophorum\neriophyllum\neriosoma\neripuit\neris\neristic\neristical\nerit\nerithacus\neritrea\neritrean\nerlang\nermine\nern\nerode\neroded\nerodium\nerogenous\nerolia\neros\neros/gr\nerose\nerosion\nerosive\nerotic\nerotically\neroticism\nerous\nerr\nerrancy\nerrand\nerrando\nerrant\nerrantry\nerrare\nerratic\nerratically\nerratum\nerrhine\nerroneous\nerroneousness\nerror\nerroris\nerrorless\nersatz\nerst\nerste\nerstwhile(a)\nerubescence\nerubescent\nerubuit\neruca\neructate\neructation\nerudite\neruditely\neruditeness\nerudition\nerunt\neruption\neruptive\nerwinia\neryngium\neryngo\nerysimum\nerysipelas\nerysiphaceae\nerysiphales\nerysiphe\nerythroblast\nerythrocebus\nerythroid\nerythromycin\nerythronium\nerythroxylaceae\nerythroxylon\nes\nesau\nescalade\nescalation\nescalator\nescalop\nescamoter\nescamoterie\nescapade\nescape\nescaped\nescapee\nescapement\nescapist\nescapologist\nescargot\nescarp\nescarpment\neschar\nescharotic\neschatologist\neschatology\nescheat\nescherichia\neschew\neschrichtiidae\neschrichtius\neschscholtzia\nesclandre\nescolar\nescopet\nescopette\nescort\nescrapment\nescritoire\nescrow\nesculent\nescutcheon\neskimo\neskimo-aleut\nesocidae\nesophageal\nesophagus\nesoteric\nesoterica\nesox\nespadrille\nespagne\nespalier\nespanole\nespecial\nespecial(a)\nespecially\nesperantido\nesperanto\nespial\nespieglerie\nespionage\nespionnage\nesplanade\nespousals\nespouse\nespresso\nespri\nesprit\nespy\nesquimaux\nesquire\nessay\nessaying\nessayist\nesse\nesselen\nessen\nessence\nessential\nessentiality\nessentially\nessentialness\nest\nestablish\nestablished\nestablishment\nestafette\nestaminet\nestate\nesteem\nesteemed\nester\nesther\nesthete\nesthetician\nestimable\nestimate\nestimated\nestimation\nestival\nestivation\nesto\nestonia\nestonian\nestop\nestoppel\nestrade\nestrange\nestranged\nestrangement\nestranging\nestrapade\nestrays\nestreat\nestrilda\nestrogen\nestrogenic\nestrone\nestrous\nestrus\nestuarine\nestuary\nestuation\nesuriens\nesurient\net\neta\netage\netagere\netalage\netamine\netat\netc\netcetera\netch\netcher\netching\netercoral\neternal\neternity\neternize\nethane\nether\netherea\nethereal\nethernet\nethic\nethical\nethically\nethicism\nethics\nethiop\nethiopia\nethiopian\nethiopias\nethiopic\nethnic\nethnical\nethnically\nethnicity\nethnocentric\nethnocentrism\nethnographer\nethnographic\nethnography\nethnological\nethnologist\nethnology\nethological\nethologist\nethology\nethos\nethosuximide\nethyl\nethylene\netiolate\netiolated\netiolation\netiological\netiologist\netiology\netiquette\netna\netodolac\netoile\netourderie\netre\netropus\netude\netui\netymological\netymologist\netymology\netymon\neuarctos\neuascomycetes\neubacteria\neubacteriales\neubryales\neucalyptus\neucarya\neucharist\neucharistic\neucharistical\neuchre\neucinostomus\neuclid\neuclidian\neudemon\neudemonic\neudemonism\neuderma\neudiometer\neudioscope\neudyptes\neuge\neugenia\neugenic\neugenics\neuglena\neuglenaceae\neuglenoid\neuglenophyceae\neuglenophyta\neukaryote\neukaryotic\neulogist\neulogistic\neulogium\neulogize\neulogy\neumeces\neumenes\neumenides\neumerogenesis\neumetopias\neumops\neumycetes\neumycota\neundo\neundum\neunectes\neunuch\neuonymus\neupatorium\neupepsia\neuphagus\neuphausiacea\neuphemism\neuphemist\neuphemistic\neuphemistically\neuphonic\neuphonical\neuphonious\neuphonism\neuphonium\neuphony\neuphorbia\neuphorbiaceae\neuphorbium\neuphoria\neuphoriant\neuphoric\neuphory\neuphractus\neuphrates\neuphrosyne\neuphuism\neuphuist\neuphuistic\neuplectella\neuproctis\neurafrican\neurasia\neurasian\neureka\neurhythmics\neuripus\neurobabble\neurodollar\neuronithopoda\neuropa\neuropan\neurope\neuropean\neuropeanization\neuropium\neurotiales\neurotium\neuryale\neuryalida\neuryalus\neurydice\neurylaimi\neurylaimidae\neurypterid\neurypterida\neurythmy\neusebian\neusporangiate\neusporangium\neustoma\neutamias\neutectic\neuterpe\neuthanasia\neutheria\neutherian\neuthynnus\neutrophy\nevacuate\nevacuated\nevacuation\nevacuee\nevade\nevagation\nevaluation\nevaluator\nevanascence\nevanesce\nevanescence\nevanescent\nevangel\nevangelical\nevangelicalism\nevangelism\nevangelist\nevangelistic\nevangelists\nevanid\nevaporable\nevaporate\nevaporated\nevaporation\nevaporative\nevaporite\nevasion\nevasive\nevasively\neve\nevection\neveille\neven\neven-pinnate\nevenhanded\nevening\nevening-snow\nevenki\nevenly\nevenness\nevensong\nevent\neventful\neventide\nevents\neventual\neventual(a)\neventuality\neventually\neventuate\never\never-present\neverduring\neverest\neverflowing\neverglades\nevergreen\neverlasting\neverlastingly\neverlastingness\neverliving\nevermore\neverness\neversion\nevert\nevery\nevery(a)\neverybody\neveryday\neveryman\neveryone\neverything\neverywhere\neves\nevict\neviction\nevidence\nevidenced\nevidencefr\nevident\nevidential\nevidentiary\nevil\nevil-minded\nevildisposed\nevildoer\nevildoing\nevilminded\nevilspeaking\nevince\neviscerate\neviscerated\nevitable\nevlugate\nevocation\nevocative\nevoke\nevolution\nevolutionary\nevolutions\nevolve\nevolved\nevolvement\nevolving\nevove\nevulsion\newe\newer\newigweibliche\nex\nex(a)\nex-directory\nex-gambler\nex-husband\nex-mayor\nex-president\nex-spouse\nexacerbate\nexacerbation\nexact\nexacta\nexacting\nexaction\nexactitude\nexactly\nexactment\nexactness\nexacum\nexaeretodon\nexaggerate\nexaggerated\nexaggeration\nexalt\nexaltation\nexalte\nexalted\nexaltee\nexamen\nexamination\nexamine\nexaminer\nexample\nexamples\nexanimate\nexanthem\nexanthema\nexarch\nexasperate\nexasperated\nexasperating\nexasperatingly\nexasperation\nexaugural\nexboyfriend\nexcalibur\nexcathedra\nexcavate\nexcavation\nexcavator\nexcecation\nexceed\nexceeding\nexceedingly\nexcel\nexcellence\nexcellency\nexcellent\nexcellently\nexcelsior\nexcentric\nexcentricity\nexceprtion\nexcept\nexcepting\nexception\nexceptionable\nexceptional\nexceptional(a)\nexceptionally\nexceptions\nexceptious\nexceptis\nexcercise\nexcern\nexcerpt\nexcerpta\nexcess\nexcessive\nexcessively\nexchange\nexchangeability\nexchangeable\nexchanged\nexchanger\nexchequer\nexcipiendis\nexcise\nexciseman\nexcision\nexcitabat\nexcitability\nexcitable\nexcitant\nexcitation\nexcite\nexcited\nexcitedly\nexcitement\nexciting\nexcitingly\nexclaim\nexclamation\nexclamations\nexclude\nexcluded\nexcluding\nexclusion\nexclusive\nexcogitate\nexcogitation\nexcommunicate\nexcommunication\nexcoriate\nexcoriation\nexcrement\nexcrementitious\nexcrescence\nexcrescent\nexcreta\nexcrete\nexcretion\nexcretory\nexcruciating\nexcrutiate\nexculpate\nexculpation\nexculpatory\nexcursiion\nexcursion\nexcursionist\nexcursive\nexcursus\nexcusable\nexcusably\nexcusat\nexcuse\nexcused\nexecrable\nexecrate\nexecratescowl\nexecration\nexecutant\nexecute\nexecuted\nexecution\nexecutioner\nexecutive\nexecutor\nexecutors\nexecutrix\nexegesis\nexegetic\nexegetical\nexegi\nexempie\nexempla\nexemplar\nexemplary\nexempli\nexemplia\nexemplification\nexemplify\nexemplifying\nexemplum\nexempt\nexemption\nexenterate\nexequatur\nexequies\nexercet\nexercise\nexercises\nexercitation\nexereta\nexert\nexertion\nexfoliate\nexfoliation\nexhalation\nexhale\nexhaled\nexhaling\nexhaust\nexhausted\nexhaustible\nexhausting\nexhaustion\nexhaustive\nexhaustless\nexhibit\nexhibition\nexhibitionism\nexhibitionist\nexhibitionist(a)\nexhibitor\nexhilarate\nexhilarating\nexhilaration\nexhort\nexhortation\nexhortative\nexhumation\nexhume\nexigeant\nexigency\nexigencypinch\nexigent\nexiguity\nexiguous\nexile\nexility\nexist\nexistence\nexistences\nexistent\nexistential\nexistentialism\nexistentialist\nexisting\nexit\nexitus\nexmoor\nexocentric\nexocoetidae\nexocrine\nexocycloida\nexode\nexodontic\nexodontics\nexodontist\nexodus\nexoergic\nexogamic\nexogamous\nexogamy\nexogenetic\nexogenous\nexomorphic\nexonerate\nexonerated\nexoneration\nexophagy\nexophthalmos\nexopterygota\nexorable\nexorbitance\nexorbitant\nexorbitantly\nexorcise\nexorcism\nexorcist\nexordium\nexoskeleton\nexosmose\nexosphere\nexostosis\nexoteric\nexothermic\nexotic\nexoticism\nexpand\nexpandable\nexpanded\nexpanding\nexpanse\nexpansibility\nexpansion\nexpansionism\nexpansive\nexpansively\nexpansiveness\nexpatiate\nexpatriate\nexpatriation\nexpect\nexpectable\nexpectance\nexpectancy\nexpectant\nexpectante\nexpectantly\nexpectat\nexpectation\nexpectations\nexpected\nexpectedness\nexpecting\nexpectorant\nexpectorate\nexpectoration\nexpedience\nexpediency\nexpedient\nexpediently\nexpedients\nexpedite\nexpedited\nexpedition\nexpeditionary\nexpeditious\nexpel\nexpend\nexpendable\nexpende\nexpended\nexpending\nexpenditure\nexpense\nexpenseless\nexpenses\nexpensive\nexpensively\nexpensiveness\nexperience\nexperienced\nexperiences\nexperientiagr/gnothi\nexperiential\nexperiment\nexperimental\nexperimentalism\nexperimentalist\nexperimentally\nexperimenter\nexperimentist\nexperimentum\nexpert\nexpertly\nexpertness\nexpertus\nexpiable\nexpiate\nexpiation\nexpiatory\nexpiration\nexpiratory\nexpire\nexpired\nexpiring(a)\nexpiry\nexplain\nexplainable\nexplainer\nexplanation\nexplanatory\nexpletive\nexplicable\nexplication\nexplicative\nexplicatory\nexplicit\nexplicitly\nexplicitness\nexplode\nexploded\nexploding\nexploit\nexploitation\nexploitative\nexploited\nexploiter\nexploration\nexploratory\nexplore\nexplorer\nexplosion\nexplosive\nexplosively\nexponent\nexponential\nexponentially\nexponentiation\nexport\nexportable\nexporter\nexporting\nexpose\nexposed\nexposition\nexpositor\nexpository\nexpostulate\nexpostulation\nexpostulatory\nexposure\nexpound\nexpounder\nexpounding\nexpress\nexpressed\nexpressible\nexpressing\nexpression\nexpressionism\nexpressionist\nexpressions\nexpressive\nexpressive(p)\nexpressively\nexpressiveness\nexpressly\nexpressman\nexpressway\nexprobate\nexprobation\nexprobration\nexpropriate\nexpropriated\nexpropriation\nexpugnable\nexpugnation\nexpuitition\nexpulsion\nexpunction\nexpunge\nexpurgate\nexpurgated\nexpurgatorius\nexquisite\nexquisitely\nexquisiteness\nexsiccate\nexsiccation\nexspuitition\nexsufflation\nexsuscitate\nextant\nextasy\nextemporaneous\nextemporaneously\nextempore\nextemporization\nextemporize\nextend\nextended\nextendibility\nextendible\nextensibility\nextensile\nextensiole\nextension\nextensional\nextensive\nextensively\nextenso\nextent\nextenuate\nextenuated\nextenuating\nextenuation\nexterior\nexteriority\nexteriorly\nexterminable\nexterminate\nextermination\nexterminator\nextern\nexternal\nexternalization\nexternally\nexteroception\nexteroceptive\nextinct\nextincteur\nextinction\nextinguish\nextinguishable\nextinguished\nextinguisher\nextinguishment\nextinguuntur\nextirpate\nextirpation\nextispicious\nextispicy\nextol\nextollimus\nextort\nextorted\nextortion\nextortionate\nextortioner\nextra\nextracellular\nextract\nextractable\nextracted\nextracting\nextraction\nextractor\nextracts\nextracurricular\nextradition\nextrados\nextragalactic\nextrajudicial\nextralegal\nextralimitary\nextralinguistic\nextramundane\nextramural\nextraneous\nextraneousness\nextraordinariness\nextraordinary\nextraordinary(p)\nextrapolated\nextrapolation\nextraregarding\nextrasensory\nextraterrestrial\nextraterritorial\nextravagance\nextravagant\nextravagantly\nextravaganza\nextravagation\nextravasate\nextravasation\nextravastate\nextreme\nextremely\nextremes\nextremis\nextremism\nextremist\nextremity\nextremum\nextricable\nextricate\nextrication\nextrinsic\nextrinsical\nextrinsicality\nextrinsically\nextrospective\nextroversion\nextroversive\nextrovert\nextroverted\nextrovertish\nextrude\nextrusion\nextrusive\nexuberance\nexuberant\nexuberantly\nexuberate\nexudate\nexudation\nexude\nexulat\nexulcerate\nexult\nexultant\nexultantly\nexultation\nexulting\nexunge\nexurbia\nexuviae\nexuvial\nexwife\nexxpatiate\neyas\neye\neye-beaming\neye-deceiving\neye-lotion\neyeball\neyebrow\neyecup\neyed\neyedness\neyeful\neyeglass\neyeish\neyelash\neyeless\neyelessness\neyelet\neyelid\neyelids\neyelike\neyeliner\neyepatch\neyepiece\neyes\neyes-only\neyeshadow\neyesight\neyesore\neyespot\neyestrain\neyeteeth\neyewater\neyewitness\neyot\neyre\neyry\nezochen/gr\nezochin/\nezokin/gr\nf\bte\nf\bted\nfa\u0007on\nfa\nfaber\nfabian\nfabiana\nfabianism\nfable\nfabled\nfabric\nfabricate\nfabricated\nfabrication\nfabrics\nfabula\nfabulist\nfabulous\nfabulously\nfaburden\nfac\nfacade\nface\nface-off\nface-saving\nface-to-face\nfaced\nfaceless\nfaceplate\nfacer\nfaces\nfacet\nfaceted\nfacetiae\nfacetious\nfacetiously\nfacetiousness\nfacia\nfacial\nfacially\nfacias\nfacie\nfacile\nfaciles\nfacilis\nfacilitate\nfacilitated\nfacilitation\nfacilitative\nfacilitator\nfacilitatory\nfacility\nfacing\nfacinorous\nfacinus\nfacit\nfacon\nfacsimile\nfact\nfact-finding\nfacta\nfacti\nfaction\nfactious\nfactitious\nfacto\nfactoid\nfactor\nfactorization\nfactory\nfactory-made\nfactotum\nfacts\nfactual\nfactuality\nfactually\nfactum\nfactus\nfacula\nfacultative\nfaculties\nfaculty\nfacundity\nfad\nfaddish\nfaddishly\nfaddist\nfaddle\nfade\nfaded\nfadeout\nfadge\nfading\nfaeces\nfaedera\nfaenum\nfag\nfagaceae\nfagales\nfagend\nfagging\nfaggot\nfagin\nfagopyrum\nfagot\nfagots\nfagus\nfahrenheit\nfaible\nfaience\nfail\nfail-safe\nfailed\nfailing\nfaille\nfails\nfailure\nfain\nfaineance\nfaineant\nfaint\nfainthearted\nfaintheartedness\nfainting\nfaintish\nfaintly\nfaintness\nfair\nfair(a)\nfair-and-square\nfair-minded\nfaire\nfairground\nfairing\nfairlead\nfairly\nfairness\nfairway\nfairy\nfairyland\nfairylike\nfairymythology\nfairytale\nfaisant\nfait\nfaith\nfaithful\nfaithfully\nfaithfulness\nfaithless\nfaithlessly\nfaithlessness\nfake\nfaker\nfakery\nfakir\nfalafel\nfalak\nfalcade\nfalcate\nfalcated\nfalcatifolium\nfalchion\nfalcidian\nfalciform\nfalco\nfalcon\nfalcon-gentle\nfalconer\nfalconet\nfalconidae\nfalconiformes\nfalconine\nfalconry\nfalderal\nfaldstool\nfall\nfallacies\nfallacious\nfallaciousness\nfallacy\nfallboard\nfallen\nfallibility\nfallible\nfalling\nfalln\nfallout\nfallow\nfalls\nfalse\nfalsehearted\nfalsehood\nfalsely\nfalseness\nfalsetto\nfalsi\nfalsie\nfalsification\nfalsified\nfalsifier\nfalsify\nfalsity\nfalstaff\nfalstaffian\nfalsus\nfalter\nfaltering\nfalutin\nfama\nfame\nfamed\nfames\nfamilial\nfamiliar\nfamiliarity\nfamiliarization\nfamiliarize\nfamiliarizing\nfamiliarly\nfamilist\nfamilistere\nfamilistery\nfamille\nfamily\nfamine\nfaminestricken\nfamish\nfamished\nfamishment\nfamotidine\nfamous\nfamously\nfamousness\nfamulus\nfan\nfanaloka\nfanatic\nfanatical\nfanatically\nfanaticism\nfanatico\nfancier\nfancies\nfanciful\nfancifully\nfancy\nfancy-free\nfancyfree\nfandango\nfandi\nfando\nfandom\nfane\nfanfare\nfanfaron\nfanfaronade\nfang\nfangs\nfanion\nfanlight\nfanlike\nfanned\nfannel\nfanning\nfanny\nfano\nfanon\nfantail\nfantan\nfantasia\nfantasist\nfantast\nfantastic\nfantastical\nfantasy\nfantoccini\nfantods\nfaquir\nfar\nfar-flung\nfar-out\nfarad\nfaraday\nfarandole\nfaraway\nfarce\nfarceur\nfarcical\nfarcicalcomedy\nfarcically\nfarcry\nfardel\nfare\nfare-stage\nfare-thee-well\nfarewell\nfarewell(a)\nfarfamed\nfarfetched\nfargo\nfari\nfarina\nfarinaceous\nfarkleberry\nfarm\nfarm(a)\nfarmer\nfarmerette\nfarmers\nfarmhand\nfarmhouse\nfarming\nfarmland\nfarmplace\nfarmstead\nfarmyard\nfarness\nfaro\nfaroese\nfarrago\nfarrier\nfarriery\nfarrow\nfarseeing\nfarsi\nfarsighted\nfart\nfarther\nfarthermost\nfarthest\nfarthing\nfarthingale\nfas\nfasces\nfascia\nfascicle\nfasciculated\nfasciculation\nfascicule\nfasciculus\nfascinate\nfascinated\nfascinating\nfascinatingly\nfascination\nfascine\nfasciola\nfasciolidae\nfascism\nfascist\nfash\nfashion\nfashionable\nfashionably\nfashioned\nfashions\nfast\nfast-flying\nfastball\nfastbreak\nfasten\nfastened\nfastener\nfasteners\nfastening\nfastidious\nfastidiously\nfastidiousness\nfastidousness\nfastidouus\nfastigiate\nfasting\nfastnacht\nfastness\nfat\nfata\nfatal\nfatalism\nfatalist\nfatality\nfatally\nfatback\nfate\nfated\nfateful\nfatefully\nfates\nfathead\nfatheaded\nfather\nfather-in-law\nfatherhood\nfatherland\nfatherless\nfatherliness\nfatherly\nfathers\nfathership\nfathom\nfathomable\nfathomless\nfatidic\nfatidical\nfatigation\nfatigue\nfatigued\nfatigues\nfatiguing\nfatihah\nfatiloquent\nfatling\nfatness\nfatras\nfatso\nfatta\nfatted\nfatten\nfattened\nfattening\nfattish\nfattism\nfatty\nfatuis\nfatuity\nfatuous\nfatuously\nfatuum\nfatuus\nfaubourg\nfaubourgs\nfaucal\nfauces\nfaucet\nfaucibus\nfaugh\nfauld\nfaule\nfaulkner\nfault\nfaultfinder\nfaultfinding\nfaultfinding(a)\nfaultily\nfaultless\nfaultlessly\nfaulty\nfaun\nfauna\nfaunus\nfaust\nfaustian\nfaustus\nfaut\nfaute\nfauteuil\nfautor\nfaux\nfavaginous\nfaveolate\nfavet\nfavillous\nfavor\nfavorable\nfavorableness\nfavorably\nfavored\nfavorer\nfavorite\nfavoritism\nfavose\nfawn\nfawncolored\nfawning\nfax\nfay\nfayetteville\nfaze\nfb\nfc\nfd\nfe\nfealty\nfear\nfearful\nfearfully\nfearfulness\nfearing\nfearless\nfearlessly\nfearlessness\nfears\nfearsomely\nfeasibility\nfeasible\nfeast\nfeasting\nfeat\nfeather\nfeatherbedding\nfeathered\nfeatherfoil\nfeatherlike\nfeathers\nfeathertop\nfeatherweight\nfeathery\nfeatly\nfeats\nfeature\nfeatured\nfeatureless\nfeatures\nfeaze\nfebrifugal\nfebrifuge\nfebrile\nfebruary\nfecal\nfece\nfecerat\nfeces\nfecit\nfeckless\nfecklessly\nfecklessness\nfecula\nfeculent\nfecund\nfecundate\nfecundation\nfecundify\nfecundity\nfed\nfedelline\nfederal\nfederalism\nfederalist\nfederalists\nfederalization\nfederalize\nfederally\nfederals\nfederate\nfederation\nfederative\nfedora\nfee\nfeeble\nfeebleminded\nfeeblemindedness\nfeebleness\nfeebly\nfeed\nfeedback\nfeeder\nfeeding\nfeedlot\nfeefawfum\nfeel\nfeeler\nfeeling\nfeelingly\nfeelings\nfeels\nfeet\nfeeze\nfeign\nfeigned\nfeijoa\nfeint\nfeisty\nfeldspar\nfelice\nfelicia\nfelicitas\nfelicitate\nfelicitation\nfelicitous\nfelicitously\nfelicity\nfelidae\nfeline\nfelis\nfelix\nfell\nfellah\nfellatio\nfelled\nfelloe\nfellow\nfellow(a)\nfellowcommoner\nfellowfeeling\nfellowman\nfellows\nfellowship\nfellowstudent\nfellowtraveller\nfelo\nfelo-de-se\nfelon\nfelonious\nfelony\nfelt\nfelted\nfelucca\nfelwort\nfemale\nfemaleness\nfeme\nfemina\nfeminality\nfeminate\nfeminine\nfemininity\nfeminism\nfeminist\nfemme\nfemoral\nfemtogram\nfemtometer\nfemtosecond\nfemur\nfen\nfence\nfenced\nfenceless\nfencer\nfences\nfencible\nfencing\nfend\nfender\nfender-bender\nfendre\nfeneration\nfenestra\nfenestral\nfenian\nfennel\nfennic\nfenny\nfenugreek\nfenusa\nfeodal\nfeodality\nfeoff\nfeoffee\nfeoffer\nfer\nfer-de-lance\nferal\nferat\nfere\nferentes\nfergusonite\nferia\nferial\nferiam\nferine\nferity\nfermat\nferment\nfermentation\nfermented\nfermion\nfermium\nfern\nfernam\nferned\nfernless\nfernlike\nferocactus\nferocious\nferociously\nferociousness\nferocity\nferoe\nferrara\nferre\nferret\nferric\nferricyanide\nferrimagnetism\nferrite\nferritin\nferrocerium\nferrocyanide\nferromagnetic\nferromagnetism\nferrule\nferry\nferryman\nfertile\nfertility\nfertilizable\nfertilization\nfertilize\nfertilized\nfertilizer\nfertur\nferule\nfervency\nfervens\nfervent\nfervid\nfervor\nfescennine\nfescue\nfesse\nfestal\nfester\nfestering\nfestina\nfestinas\nfestins\nfestival\nfestive\nfestivity\nfestoon\nfestooned\nfestuca\nfetal\nfetch\nfetching\nfete\nfeted\nfeterita\nfetich\nfetichism\nfeticide\nfeticism\nfetid\nfetish\nfetishism\nfetlock\nfetoprotein\nfetor\nfetter\nfetterbush\nfettered\nfetters\nfettle\nfettuccine\nfetus\nfeu\nfeud\nfeudal\nfeudalism\nfeudality\nfeudally\nfeudatory\nfeudejoie\nfever\nfevered\nfeverfew\nfeverish\nfeverishly\nfeverroot\nfeverwort\nfew\nfewer\nfewest(a)\nfewness\nfey\nfez\nff\nfg\nfh\nfianc\nfiance\nfiancee\nfiasco\nfiat\nfib\nfibbing\nfiber\nfiber-optic\nfiberboard\nfiberglass\nfiberscope\nfibril\nfibrillation\nfibrillose\nfibrillous\nfibrin\nfibrinogen\nfibrinolysis\nfibrinous\nfibroblast\nfibrocalcific\nfibrocartilage\nfibrocartilaginous\nfibrosis\nfibrositis\nfibrous\nfibula\nfica\nfichu\nfickle\nfickleness\nfictile\nfiction\nfictional\nfictitious\nfictive\nficus\nfid\nfiddle\nfiddlededee\nfiddlefaddle\nfiddleneck\nfiddler\nfiddlestick\nfiddling\nfide\nfidei\nfideli\nfidelibus\nfidelis\nfidelity\nfides\nfidget\nfidgetiness\nfidgets\nfidgety\nfiducial\nfiduciary\nfidus\nfie\nfief\nfieff\nfield\nfielder\nfieldfare\nfielding\nfields\nfieldstone\nfieldwork\nfieldworker\nfiend\nfiendish\nfiendlike\nfierce\nfiercely\nfierceness\nfieri\nfierily\nfieriness\nfiery\nfiesta\nfife\nfifer\nfifteen\nfifteenth\nfifth\nfifthly\nfifties\nfiftieth\nfifty\nfig\nfig-bird\nfight\nfighter\nfighting\nfightingcock\nfiglia\nfigment\nfigural\nfigurante\nfigurate\nfiguration\nfigurative\nfiguratively\nfigurativeness\nfigure\nfigured\nfigurehead\nfigurine\nfiguriste\nfigwort\nfiji\nfijian\nfilaceous\nfilament\nfilamentiferous\nfilamentous\nfilar\nfilaria\nfilarial\nfilariasis\nfilariid\nfilariidae\nfilature\nfilch\nfilcher\nfile\nfilefish\nfiler\nfiles\nfilial\nfiliation\nfilibuster\nfilibustering\nfilibusterism\nfilicales\nfiliciform\nfilicoid\nfilicopsida\nfiliform\nfiligree\nfiling\nfilings\nfilipino\nfilius\nfill\nfille\nfilled\nfiller\nfillet\nfillibeg\nfilling\nfillip\nfillmore\nfilly\nfilm\nfilmable\nfilmed\nfilming\nfilmy\nfils\nfilter\nfilter-tipped\nfilth\nfilthy\nfiltrate\nfiltration\nfimbriae\nfimbriate\nfimbriated\nfimetarious\nfimicolous\nfin\nfinable\nfinal\nfinale\nfinalist\nfinality\nfinalization\nfinally\nfinance\nfinanced\nfinancial\nfinancially\nfinancier\nfinancing\nfinback\nfinch\nfind\nfinder\nfinding\nfinds\nfine\nfine-looking\nfine-toothed(a)\nfined\nfinedraw\nfinefingered\nfinely\nfinem\nfineness\nfiner\nfinery\nfinespoken\nfinespun\nfinesse\nfinest\nfinestill\nfinetoned\nfing\nfinger\nfinger-roll\nfingerboard\nfingered\nfingering\nfingerless\nfingermark\nfingernail\nfingerpost\nfingerprint\nfingerprinting\nfingers\nfingerspelling\nfingerstall\nfingertip\nfinglefangle\nfinial\nfinical\nfinikin\nfinis\nfinish\nfinished\nfinisher\nfinishing\nfinistre\nfinite\nfinitely\nfiniteness\nfink\nfinland\nfinn\nfinnish\nfinno-ugric\nfio\nfiord\nfipple\nfir\nfire\nfire-eater\nfire-on-the-mountain\nfire-retardant\nfirearm\nfirearms\nfireball\nfirebarrel\nfirebase\nfireboat\nfirebomb\nfirebox\nfirebrand\nfirebrat\nfirebreak\nfirebrick\nfirebug\nfireclay\nfirecracker\nfiredamp\nfiredog\nfiredrake\nfireeyed\nfirefly\nfireirons\nfirelight\nfirelighter\nfirelock\nfireman\nfirenew\nfirenze\nfireplace\nfireplug\nfirepower\nfireproof\nfires\nfireside\nfirestone\nfirestorm\nfiretrap\nfirewall\nfirewater\nfireweed\nfirewood\nfirework\nfireworks\nfireworship\nfiring\nfirkin\nfirm\nfirma\nfirmament\nfirmamental\nfirman\nfirmes\nfirmhold\nfirmiana\nfirmly\nfirmness\nfirmware\nfirst\nfirst-class\nfirst-come-first-serve(p)\nfirst-nighter\nfirst-rate\nfirst-rater\nfirst-string\nfirstborn\nfirstclass\nfirstfruits\nfirsthand\nfirstling\nfirstlings\nfirstrate\nfirth\nfisc\nfiscal\nfiscalize\nfiscally\nfish\nfishbone\nfisher\nfisherman\nfishery\nfishes\nfishfly\nfishhook\nfishing\nfishlike\nfishmonger\nfishnet\nfishpaste\nfishplate\nfishpond\nfishs\nfishy\nfisk\nfissibility\nfissile\nfission\nfissionable\nfissiparous\nfissipedia\nfissure\nfissurella\nfissurellidae\nfist\nfisted\nfistfight\nfisticuffs\nfistula\nfistular\nfistularia\nfistulariidae\nfistulina\nfistulinaceae\nfistulous\nfit\nfit(p)\nfitchet\nfitchew\nfitful\nfitfully\nfitfulness\nfitly\nfitment\nfitness\nfitout\nfits\nfitted\nfitter\nfitting\nfittings\nfive\nfive-hitter\nfive-spot\nfiveact\nfivepence\nfivepenny\nfiver\nfives\nfix\nfixable\nfixation\nfixative\nfixe\nfixed\nfixedly\nfixedness\nfixer\nfixity\nfixture\nfixtures\nfizgig\nfizz\nfizzing\nfizzle\nfj\nfjord\nfk\nfl\nflab\nflabbergast\nflabbergasted\nflabbily\nflabbiness\nflabby\nflabelliform\nflabellum\nflaccid\nflaccidity\nflacourtia\nflacourtiaceae\nflag\nflagellant\nflagellate\nflagellation\nflagelliform\nflagellum\nflageolet\nflagfish\nflagging\nflagitious\nflagon\nflagpole\nflagrancy\nflagrant\nflagrante\nflagrantly\nflagration\nflags\nflagship\nflagstaff\nflagstone\nflail\nflailing\nflair\nflak\nflake\nflakiness\nflaky\nflam\nflamb\nflambe\nflambeau\nflamboyance\nflamboyant\nflamboyantly\nflame\nflamecolored\nflamefish\nflamen\nflamenco\nflameproof\nflames\nflamethrower\nflaming\nflamingo\nflaminius\nflammae\nflammulina\nflan\nflanders\nflaneur\nflange\nflank\nflanked\nflanking\nflannel\nflannelbush\nflannelette\nflant\nflap\nflapdoodle\nflapjack\nflapper\nflapping\nflare\nflared\nflaring\nflash\nflash-frozen\nflashback\nflashiness\nflashing\nflashlight\nflashy\nflask\nflasket\nflat\nflat-bottomed\nflat-footed\nflat-topped\nflatboat\nflatbread\nflatbrod\nflatcar\nflatfish\nflatfoot\nflathead\nflatiron\nflatlet\nflatly\nflatmate\nflatness\nflats\nflatten\nflatter\nflatterer\nflattering\nflattery\nflatulence\nflatulency\nflatulent\nflatus\nflatware\nflatwork\nflatworm\nflaunt\nflaunting\nflautist\nflavian\nflavor\nflavored\nflavorer\nflavorful\nflavorlessness\nflavorsomeness\nflavous\nflaw\nflawless\nflawlessly\nflax\nflaxen\nflay\nflea\nfleabane\nfleabite\nfleapit\nfleawort\nfleck\nflecked\nfleckered\nflectes\nflecti\nflection\nfled\nfledge\nfledged\nfledgling\nfledgling(a)\nflee\nfleece\nfleeceable\nfleeced\nfleeing(a)\nfleer\nfleet\nfleeting\nfleetness\nfleissig\nflemish\nflesh\nflesh-eating(a)\nfleshcolored\nfleshiness\nfleshlyhuman\nfleshspots\nfleshy\nfletcher\nfleur\nfleur-de-lis\nfleurdelis\nfleuron\nfleurs\nflex\nflexibility\nflexible\nflexibly\nflexile\nflexion\nflexuous\nflexure\nflibbertigibbet\nflick\nflick-knife\nflicker\nflickering\nflickertail\nflier\nfliers\nflies\nflight\nflighted(ip)\nflightiness\nflightless\nflighty\nflimflam\nflimsily\nflimsiness\nflimsy\nflinch\nflindersia\nfling\nflinger\nflint\nflinthearted\nflintlock\nflintstone\nflinty\nflip\nflip-flap\nflip-flop\nflippancy\nflippant\nflippantly\nflipper\nflirt\nflirtation\nflirting\nflit\nflitch\nflitter\nflitting\nfloat\nfloater\nfloating\nfloating(a)\nfloating-moss\nfloatplane\nfloats\nflobert\nfloccillation\nfloccose\nflocculation\nfloccule\nflocculent\nflocculi\nflock\nflocks\nflodden\nfloe\nflog\nflogged\nflood\nflood(a)\nflooded\nfloodgate\nfloodgates\nfloodhead\nflooding\nfloodlit\nfloor\nfloorboard\nfloorcover\nfloored\nflooring\nfloorwalker\nfloozy\nflop\nflophouse\nfloppy\nflora\nfloral\nfloreal\nflorentine\nfloret\nfloricultural\nfloriculture\nflorid\nflorida\nfloridian\nfloridly\nfloridness\nflorilegium\nflorist\nflosculi\nfloss\nflotation\nflotilla\nflotsam\nflounce\nflounder\nflour\nflourish\nflourishing\nfloury\nflout\nflow\nflowage\nflower\nflower-of-an-hour\nflowerbed\nflowering\nflowerless\nflowers\nflowery\nflowing\nflowmeter\nflown\nflowret\nflu\nflub\nfluctibus\nfluctuate\nfluctuating\nfluctuation\nfluctus\nflue\nfluency\nfluent\nfluently\nfluff\nfluffy\nflugelhorn\nflugelman\nfluid\nfluidity\nfluidounce\nfluidram\nfluids\nfluke\nflume\nflummery\nflummox\nflunk\nflunker\nflunkey\nflunkeyism\nflunky\nfluorapatite\nfluorescein\nfluorescence\nfluorescent\nfluoridation\nfluoride\nfluorine\nfluorite\nfluoroboride\nfluorocarbon\nfluorochrome\nfluorography\nfluoroscope\nfluoroscopy\nfluorouracil\nfluosilicate\nfluoxetine\nflurbiprofen\nflurry\nflush\nflush(p)\nflush-seamed\nflushed\nflusher\nfluster\nflustered\nflute\nfluted\nfluting\nflutist\nflutter\nfluttering\nfluviaatile\nfluvial\nflux\nfluxion\nfluxional\nfluxions\nfluxmeter\nfly\nfly-by-night\nfly-fishing\nflyaway\nflyblown\nflyer\nflying\nflyleaf\nflyover\nflypaper\nflytrap\nflyweight\nflywheel\nfoal\nfoaled\nfoam\nfoamflower\nfoaminess\nfoaming\nfoamy\nfob\nfocal\nfocalization\nfocally\nfocis\nfocus\nfocused\nfodder\nfoe\nfoederris\nfoehn\nfoeman\nfoeniculum\nfoenum\nfoes\nfoeticide\nfoetus\nfoex\nfog\nfogbank\nfogbound\nfogey\nfogged\nfoggy\nfoghorn\nfoglamp\nfogsignal\nfogy\nfogyish\nfoh\nfohn\nfoi\nfoible\nfoil\nfoiled\nfoils\nfoin\nfoist\nfolatre\nfold\nfoldable\nfolded\nfolded-up\nfolder\nfolderol\nfoliaceous\nfoliage\nfoliate\nfoliated\nfoliation\nfolio\nfoliolate\nfolium\nfolk\nfolkland\nfolklore\nfolks\nfolksy\nfolktale\nfollicle\nfollicular\nfollies\nfollow\nfollow-on\nfollow-through\nfollow-up\nfollower\nfollowers\nfollowing\nfollowing(a)\nfollows\nfolly\nfoment\nfomentation\nfomes\nfomor\nfonctionnaire\nfond\nfond(p)\nfondant\nfonder\nfondle\nfondling\nfondly\nfondness\nfondre\nfondue\nfons\nfont\nfontanel\nfontanelle\nfontenoy\nfontent\nfood\nfoodless\nfoodstuff\nfool\nfoolhardihood\nfoolhardness\nfoolhardy\nfooling\nfoolish\nfoolishly\nfoolishness\nfoolproof\nfools\nfoolscap\nfoot\nfoot-lambert\nfoot-pound\nfoot-poundal\nfoot-ton\nfootage\nfootball\nfootbath\nfootboard\nfootboy\nfootbridge\nfootcandle\nfooted\nfootedness\nfooter\nfootfall\nfootfault\nfoothill\nfoothills\nfoothold\nfooting\nfootless\nfootlight\nfootlights\nfootlocker\nfootloose\nfootman\nfootmark\nfootnote\nfootpad\nfootpath\nfootplate\nfootprint\nfootrace\nfoots\nfootsore\nfootstep\nfootsteps\nfootsteps-of-spring\nfootstool\nfootwall\nfootwear\nfootwork\nfop\nfoppery\nfoppish\nfor\nforage\nforaging\nforam\nforamen\nforaminifera\nforaminous\nforasmuch\nforay\nforbear\nforbearance\nforbearing\nforbears\nforbid\nforbidden\nforbidding\nforbiddingly\nforce\nforced\nforceful\nforcefully\nforceless\nforcemeat\nforceps\nforces\nforcible\nforcibly\nforcing\nford\nfordable\nfordhooks\nfore\nfore(a)\nfore-and-aft\nfore-and-after\nfore-topmast\nfore-topsail\nforearm\nforebear\nforebode\nforeboding\nforebrain\nforecast\nforecaster\nforecastle\nforeclose\nforeclosure\nforecourt\nforeday\nforedeck\nforedoom\nforefather\nforefathers\nforefend\nforefinger\nforefoot\nforefront\nforego\nforegoing\nforegoing(a)\nforegolf\nforegone\nforeground\nforehand\nforehand(a)\nforehanded\nforehead\nforeign\nforeign-born\nforeigner\nforeignness\nforejudge\nforeknow\nforeknowledge\nforeland\nforelay\nforeleg\nforelimb\nforelock\nforelooper\nforeloper\nforeman\nforemanship\nforemast\nforemost\nforemother\nforenoon\nforensic\nforensis\nforeordain\nforeordained\nforeordination\nforepart\nforepaw\nforeperson\nforeplay\nforequarter\nforerun\nforerunner\nforesail\nforesee\nforeseeable\nforeseeing\nforeseen\nforeshadow\nforeshank\nforeshock\nforeshore\nforeshorten\nforeshow\nforesight\nforest\nforestall\nforestay\nforested\nforester\nforestiera\nforestry\nforetaste\nforetell\nforethought\nforethoughtful\nforetoken\nforetop\nforever\nforevermore\nforewarn\nforewarned\nforewarning\nforewing\nforewoman\nforeword\nforfeit\nforfeited\nforfeiture\nforfend\nforficula\nforficulidae\nforgather\nforge\nforged\nforger\nforgery\nforget\nforget-me-not\nforgetful\nforgetfully\nforgetfulness\nforgetmenots\nforgets\nforgettable\nforgetting\nforging\nforgive\nforgiven\nforgiveness\nforgiving\nforgivingly\nforgivingness\nforgot\nforgotten\nforint\nfork\nforked\nforking\nforlorn\nforlornly\nforlornness\nform\nform-only(a)\nforma\nformae\nformal\nformaldehyde\nformalin\nformalism\nformalist\nformalistic\nformality\nformalization\nformalized\nformally\nformalwear\nformat\nformation\nformative\nformativus\nformed\nformer\nformer(a)\nformerly\nformic\nformica\nformicariidae\nformicarius\nformication\nformicidae\nformidability\nformidable\nformidably\nforming\nformless\nformlessly\nformosa\nformosan\nforms\nformula\nformulaic\nformulary\nformulate\nformulated\nformulation\nfornication\nfornicator\nfornicatress\nfornix\nforo\nforsake\nforsaken\nforsaking\nforsan\nforseti\nforsooth\nforswear\nforsworn\nforsythia\nfort\nfortalice\nforte\nfortelage\nfortemente\nforth\nforthcoming\nforthwith\nforti\nfortier\nforties\nfortieth\nfortification\nfortify\nfortifying\nfortior\nfortiori\nfortioribus\nfortis\nfortissimo\nfortiter\nfortitude\nfortnight\nfortnightly\nfortran\nfortress\nfortuitous\nfortuitousness\nfortuna\nfortunae\nfortunate\nfortunately\nfortunatus\nfortunatuss\nfortune\nfortune-teller\nfortunehunter\nfortuneless\nfortunella\nfortunes\nforty\nforty-five\nforty-niner\nforum\nforune\nforward\nforwarding\nforwardness\nforwards\nfoss\nfossa\nfosse\nfossil\nfossiliferous\nfossilization\nfossilized\nfossils\nfossorial\nfoster\nfostered\nfostering\nfothergilla\nfou\nfouet\nfoul\nfoul-mouthed\nfoulard\nfoule\nfoully\nfoulmouthed\nfoulness\nfoulspoken\nfound\nfoundation\nfoundations\nfounded\nfounder\nfoundered\nfoundering\nfounders\nfoundery\nfoundling\nfoundress\nfoundry\nfount\nfountain\nfountainhead\nfountains\nfouquieria\nfouquieriaceae\nfour\nfour-dimensional\nfour-hitter\nfour-in-hand\nfour-lane\nfour-ply\nfour-poster\nfour-pounder\nfour-spot\nfour-wheeler\nfourchette\nfourflush\nfourfold\nfourierism\nfourinhand\nfourmart\nfourpence\nfourpenny\nfours\nfourscore\nfoursquare\nfourteen\nfourteenth\nfourth\nfourthing\nfourthly\nfourtyfour\nfourwheeler\nfous\nfovea\nfowl\nfowler\nfowling\nfowls\nfox\nfox-trot\nfoxglove\nfoxhole\nfoxhound\nfoxhunt\nfoxtail\nfoxy\nfoyer\nfr\nfraca\nfracas\nfracta\nfractal\nfraction\nfractional\nfractionation\nfractious\nfractiously\nfracture\nfractured\nfragaria\nfragibility\nfragile\nfragility\nfragment\nfragmental\nfragmentary\nfragrance\nfragrant\nfrail\nfrailty\nfrais\nfraise\nframe\nframe-up\nframed\nframer\nframework\nframing\nfranc\nfranc-tireur\nfrancaise\nfrance\nfranche-comte\nfranchise\nfranciscan\nfrancium\nfranco-american\nfrancoa\nfrancophile\nfrancophobe\nfranctireur\nfrangas\nfrangi\nfrangible\nfrangipane\nfrangipani\nfrank\nfrankalmoigne\nfrankensteins\nfrankfort\nfrankhearted\nfrankincense\nfranklin\nfrankliniella\nfrankness\nfrantic\nfrantically\nfrapp\nfrappe\nfrasera\nfratercula\nfraternal\nfraternally\nfraternity\nfraternization\nfraternize\nfratricide\nfratrum\nfrau\nfraud\nfraudem\nfraudulence\nfraudulency\nfraudulent\nfraudulently\nfraught\nfraught(p)\nfraus\nfraxinella\nfraxinus\nfray\nfrayed\nfrazzle\nfreak\nfreakish\nfreaky\nfreckle\nfreckled\nfredaine\nfredericksburg\nfredericton\nfree\nfree-liver\nfree-living\nfree-range\nfree-reed\nfree-soil\nfree-swimming\nfree-thinking\nfreebooter\nfreeborn\nfreed\nfreedman\nfreedom\nfreeforall\nfreehold\nfreeholder\nfreelance\nfreelance(a)\nfreeloader\nfreely\nfreeman\nfreemason\nfreemasonry\nfreesia\nfreespoken\nfreestanding\nfreestone\nfreestyle\nfreetail\nfreethinker\nfreethinking\nfreetown\nfreeware\nfreeway\nfreewheeling\nfreewill\nfreeze\nfreeze-dried\nfreezedry\nfreezes\nfreezing\nfregata\nfregatidae\nfreight\nfreightage\nfremontodendron\nfrench\nfrench-speaking\nfrenchhorn\nfrenchman\nfrenetic\nfrenzied\nfrenziedly\nfrenzy\nfrequency\nfrequent\nfrequently\nfresco\nfresh\nfresh(a)\nfresh-cut\nfreshen\nfreshet\nfreshman\nfreshness\nfreshpluckt\nfreshwater\nfresno\nfret\nfretful\nfretfully\nfretta\nfretted\nfretwork\nfreud\nfreudian\nfreund\nfrey\nfreya\nfriability\nfriable\nfriandise\nfriar\nfriar's-cowl\nfriars\nfriary\nfribble\nfricandeau\nfricassee\nfrication\nfricative\nfriction\nfrictional\nfrictionless\nfriday\nfridge\nfried\nfriedcake\nfriend\nfriendless\nfriendlessness\nfriendliness\nfriendly\nfriends\nfriendship\nfriesian\nfrieze\nfrigate\nfrigg\nfright\nfrighten\nfrightened\nfrighteningly\nfrightful\nfrightfully\nfrightfulness\nfrigid\nfrigidarium\nfrigidity\nfrigorific\nfrijole\nfrill\nfrilled\nfrills\nfrimaire\nfringe\nfringed\nfringepod\nfringilla\nfringillidae\nfrippery\nfrise\nfriseur\nfrisian\nfrisk\nfriskily\nfriskiness\nfrisky\nfrisson\nfrith\nfritillaria\nfritillary\nfritiniancy\nfrittata\nfritter\nfrivol\nfrivolity\nfrivolous\nfrivolously\nfrizz\nfrizzle\nfrizzly\nfro\nfrock\nfroelichia\nfrog\nfrogbit\nfrogfish\nfroghopper\nfrogmouth\nfroid\nfrolic\nfrolick\nfrolicsome\nfrom\nfrond\nfronder\nfrondeur\nfrons\nfront\nfront(a)\nfront-runner\nfrontage\nfrontal\nfrontally\nfrontbench\nfrontbencher\nfronte\nfronti\nfrontier\nfrontier(a)\nfrontiersman\nfronting\nfrontispiece\nfrontlet\nfrore\nfrost\nfrost-bound\nfrostbite\nfrostbitten\nfrostbound\nfrosted\nfrostian\nfrostily\nfrostiness\nfrosting\nfrostnipped\nfrostweed\nfrosty\nfroth\nfrothily\nfrothy\nfrottage\nfrotteur\nfroude\nfrounce\nfrouzy\nfrow\nfroward\nfrown\nfrowning\nfrowningly\nfrowns\nfrowsy\nfrozen\nfrozen(p)\nfructidor\nfructification\nfructify\nfructose\nfrugal\nfrugality\nfrugally\nfruges\nfrugiferous\nfruit\nfruitage\nfruitarian\nfruitbearing\nfruitcake\nfruiterer\nfruitful\nfruitfulness\nfruiting\nfruition\nfruitless\nfruitlessness\nfruitlet\nfruits\nfruitwood\nfruity\nfrumenty\nfrump\nfrumpish\nfrustrate\nfrustrated\nfrustrating\nfrustration\nfrustum\nfry\nfryer\nfrying\nfryingpan\nfucaceae\nfucales\nfuchsia\nfuchsine\nfuck\nfucked-up\nfucker\nfucking\nfucoid\nfucus\nfuddle\nfuddled\nfuddy-duddy\nfudge\nfuego\nfuel\nfueled\nfueling\nfuels\nfug\nfugaces\nfugacious\nfugacity\nfugal\nfugally\nfuge\nfugerit\nfuggy\nfugit\nfugitive\nfugleman\nfugu\nfugue\nfui\nfuimus\nfuit\nfuji\nfukuoka\nfula\nfulciment\nfulcrum\nfulfill\nfulfilled\nfulfillment\nfulgent\nfulgid\nfulgidity\nfulgor\nfulgoridae\nfulgurate\nfulgurating\nfulguration\nfulgurite\nfulica\nfuliginous\nfull\nfull-blooded\nfull-blown\nfull-bodied\nfull-dress\nfull-fashioned\nfull-fledged\nfull-length\nfull-page\nfull-term\nfull-time\nfullback\nfullblown\nfullc\nfullcharged\nfullcolored\nfuller\nfullfed\nfullflavored\nfullfraught\nfullgrown\nfullhanded\nfullhouse\nfullladen\nfullmouthed\nfullness\nfulltilt\nfulltoned\nfully\nfulmar\nfulmarus\nfulmen\nfulminate\nfulmination\nfulsome\nfulsomeness\nfulton\nfulvid\nfulvous\nfumaria\nfumariaceae\nfumble\nfumbler\nfume\nfumed\nfumes\nfumewort\nfumid\nfumigant\nfumigate\nfumigation\nfuming\nfumitory\nfumo\nfun\nfunambulist\nfunction\nfunctional\nfunctionalism\nfunctionalist\nfunctionally\nfunctionary\nfunctioning\nfunctions\nfunctus\nfund\nfundamental\nfundamentalism\nfundamentalist\nfundamentally\nfundamentals\nfunded\nfunds\nfundulus\nfunebrial\nfunera\nfuneral\nfunerary\nfunereal\nfungal\nfungi\nfungia\nfungible\nfungicidal\nfungicide\nfungiform\nfungoid\nfungology\nfungosity\nfungus\nfunicle\nfunicular\nfunk\nfunky\nfunnel\nfunny\nfur\nfur-piece\nfuracious\nfuran\nfurbelow\nfurbish\nfurcated\nfurcation\nfurcula\nfurcular\nfurfur\nfurfuraceous\nfurfural\nfuriata\nfuries\nfurioso\nfurious\nfuriously\nfurl\nfurled\nfurlike\nfurlong\nfurlough\nfurnace\nfurnariidae\nfurnarius\nfurnish\nfurnished\nfurnishings\nfurniture\nfuror\nfurore\nfurred\nfurring\nfurrow\nfurrowed\nfurst\nfurther\nfurtherance\nfurthermore\nfurthest\nfurtive\nfurtively\nfurtiveness\nfurto\nfuruncle\nfury\nfurze\nfuscoboletinus\nfuscous\nfuse\nfusee\nfusel\nfuselage\nfusible\nfusiform\nfusil\nfusileer\nfusilier\nfusillade\nfusion\nfuss\nfussily\nfussiness\nfussy\nfustee\nfustian\nfustie\nfustigate\nfustigation\nfusty\nfutile\nfutilely\nfutility\nfuton\nfuture\nfuture(a)\nfutureless\nfuturi\nfuturism\nfuturistic\nfuturition\nfuturity\nfuturum\nfuzz\nfuzzed\nfuzzle\nfuzzy\nfv\nfylfot\ng\ng-man\ng-string\nga\ngab\ngabardine\ngabble\ngabbro\ngabel\ngabelle\ngaberlunzie\ngable\ngabled\ngabon\ngabonese\ngaborone\ngaby\ngad\ngadaba\ngadabout\ngaddi\ngadding\ngadfly\ngadget\ngadgeteer\ngadgetry\ngadidae\ngadiformes\ngadling\ngadoid\ngadolinite\ngadolinium\ngadus\ngaea\ngael\ngaelic\ngaff\ngaffe\ngaffer\ngaffsail\ngag\ngaga\ngage\ngager\ngaggle\ngagman\ngaiete\ngaiety\ngaillard\ngaillardia\ngaily\ngain\ngainer\ngainful\ngainfully\ngaining\ngainless\ngainly\ngainsay\ngainsborough\ngainst\ngairish\ngait\ngaiter\ngal\ngala\ngala(a)\ngalactic\ngalactose\ngalactosis\ngalago\ngalahad\ngalan\ngalangal\ngalantine\ngalantuomo\ngalavant\ngalax\ngalaxy\ngalbanum\ngalbulidae\ngale\ngalega\ngaleiform\ngalen\ngalena\ngalenicals\ngaleocerdo\ngaleopsis\ngaleorhinus\ngaleras\ngalere\ngalicia\ngalician\ngalilean\ngalileo\ngalimathias\ngalingale\ngaliongee\ngalionji\ngaliot\ngalipot\ngalium\ngall\ngallant\ngallantly\ngallantry\ngallanty\ngallantyshow\ngallbladder\ngalled\ngalleon\ngalleria\ngallery\ngalley\ngalleyfoist\ngalleys\ngallfly\ngalliass\ngallic\ngallicism\ngalliformes\ngalligaskin\ngallimaufry\ngallinaceous\ngallinago\ngalling\ngallinula\ngallinule\ngallipot\ngallirallus\ngallium\ngallivant\ngallon\ngalloon\ngallop\ngallope\ngalloping\ngalloway\ngallows\ngallstone\ngallus\ngaloche\ngaloot\ngalop\ngalopade\ngalore\ngalore(ip)\ngalosh\ngalvanic\ngalvanism\ngalvanize\ngalvanometer\ngalwegian\ngam\ngamache\ngamaliel\ngamashes\ngamba\ngambade\ngambado\ngambelia\ngambia\ngambian\ngambit\ngamble\ngambler\ngambling\ngamboge\ngambol\ngambrel\ngambusia\ngame\ngamebag\ngamecock\ngamekeeper\ngamely\ngames\ngames-master\ngamesmanship\ngamesome\ngamester\ngametangium\ngamete\ngametocyte\ngametophore\ngametophyte\ngamey\ngamic\ngamin\ngaminess\ngaming\ngamma\ngammadion\ngammer\ngammon\ngammy\ngamopetalous\ngamp\ngamut\ngamy\ngander\ngandhi\ngandhian\nganesa\ngang\nganger\nganges\ngangling\nganglion\ngangplank\ngangrene\ngangrenous\ngangsaw\ngangster\ngangue\ngangway\nganja\ngannet\nganoid\nganoidei\nganoin\ngansu\ngantlet\ngantry\nganymede\ngaol\ngaoler\ngap\ngap-toothed\ngape\ngaping\ngar\ngarage\ngarambulla\ngarb\ngarbage\ngarble\ngarbled\ngarbling\ngarboard\ngarbology\ngarcinia\ngarde\ngarden\ngardener\ngardenia\ngardening\ngardens\ngarderobe\ngarderoyale\ngarfield\ngarganey\ngargantua\ngargantuan\ngargle\ngargoyle\ngaribaldi\ngarish\ngarishly\ngarishness\ngariwala\ngarland\ngarlic\ngarment\ngarmented\ngarmentmaker\ngarner\ngarnet\ngarnierite\ngarnish\ngarnished\ngarnishee\ngarniture\ngarran\ngarret\ngarrick\ngarrison\ngarrisoned\ngarron\ngarrote\ngarroter\ngarrotte\ngarrotto\ngarrulinae\ngarrulity\ngarrulous\ngarrulus\ngarter\ngarterblue\ngarters\ngarth\ngaruda\ngas\ngasbag\ngascogne\ngascon\ngasconade\ngasconading\ngaseity\ngaselier\ngaseous\ngaseousness\ngasfield\ngash\ngasherbrum\ngasification\ngasified\ngasify\ngasket\ngaskin\ngaskins\ngaslight\ngasman\ngasmask\ngasmeter\ngasohol\ngasoline\ngasometer\ngasp\ngasping\ngassing\ngassy\ngasteromycete\ngasteromycetes\ngasterophilidae\ngasterophilus\ngasterosteidae\ngasterosteus\ngastric\ngastriloquism\ngastritis\ngastroboletus\ngastrocnemius\ngastrocybe\ngastroenteritis\ngastroenterologist\ngastrointestinal\ngastromancy\ngastronome\ngastronomic\ngastronomy\ngastrophryne\ngastropod\ngastropoda\ngastroscope\ngastroscopy\ngastrula\ngastrulation\ngasworks\ngat\ngate\ngate-crashing\ngateau\ngatecrasher\ngatehouse\ngatepost\ngaterum\ngates\ngateway\ngath\ngather\ngathered\ngatherer\ngathering\ngathers\ngatherum\ngathic\ngatling\ngauche\ngaucherie\ngaucho\ngaud\ngaudery\ngaudiness\ngaudy\ngauge\ngauger\ngauging\ngauguin\ngauguinesque\ngaul\ngaultheria\ngaum\ngaumless\ngaunt\ngauntlet\ngauntleted\ngaur\ngauri\ngauss\ngaussian\ngautama\ngauze\ngavel\ngavelkind\ngavelock\ngavia\ngavial\ngavialidae\ngavialis\ngavidae\ngaviiformes\ngavot\ngavotte\ngawain\ngawk\ngawkiness\ngawky\ngay\ngayal\ngayety\ngaylussacia\ngazania\ngaze\ngazebo\ngazella\ngazelle\ngazer\ngazette\ngazetted\ngazetteer\ngazetter\ngazing\ngazingstock\ngb\ngc\ngd\ngdansk\nge\ngean\ngeant\ngear\ngearbox\ngeared\ngearing\ngearset\ngearshift\ngeastraceae\ngeastrum\ngeb\ngeblueteger\ngecko\ngee\ngee-gee\ngeebung\ngeek\ngeese\ngeezer\ngeglossaceae\ngehenna\ngeiger\ngeisha\ngeist\ngekkonidae\ngel\ngelasma/gr\ngelatin\ngelatinous\ngelatinousness\ngeld\ngelding\ngelebt\ngelechia\ngelechiid\ngelechiidae\ngelid\ngeliebet\ngelignite\ngeloscopy\ngelsemium\ngelt\ngem\ngeminate\ngemination\ngemini\ngemma\ngemmation\ngemmule\ngemote\ngempylid\ngempylidae\ngempylus\ngems\ngemsbok\ngemuthe\ngen\ngen/gr\ngendarme\ngendarmerie\ngender\ngene\ngenealogic\ngenealogically\ngenealogist\ngenealogy\ngenera\ngeneral\ngeneral-purpose\ngenerale\ngeneralissimo\ngenerality\ngeneralitymedian\ngeneralization\ngeneralize\ngeneralized\ngenerally\ngeneralship\ngeneralthe\ngenerat\ngenerate\ngeneration\ngenerative\ngenerator\ngeneric\ngenerically\ngeneris\ngenerosity\ngenerous\ngenesis\ngenet\ngenethliacs\ngenetic\ngenetically\ngeneticist\ngenetics\ngenetive\ngenetous\ngenetta\ngeneva\ngenial\ngeniality\ngenic\ngeniculated\ngenie\ngenip\ngenipa\ngenipap\ngenista\ngenital\ngenitalia\ngenitive\ngenitor\ngenitourinary\ngeniture\ngenius\ngenlisea\ngenoa\ngenocide\ngenoese\ngenoise\ngenome\ngenomics\ngenossen\ngenotype\ngenotypical\ngenou\ngenre\ngens\ngent\ngentamicin\ngenteel\ngenteelly\ngentian\ngentiana\ngentianaceae\ngentianales\ngentianella\ngentianopsis\ngentile\ngentilhomme\ngentilism\ngentility\ngentium\ngentle\ngentlefolk\ngentleman\ngentleman-at-arms\ngentlemanlike\ngentlemanliness\ngentlemanly\ngentlemen\ngentleness\ngently\ngentoo\ngentry\ngenuflection\ngenuflexion\ngenug\ngenuine\ngenuineness\ngenus\ngenyonemus\ngeocentric\ngeochelone\ngeochemistry\ngeococcyx\ngeodesia\ngeodesic\ngeodesy\ngeodetic\ngeodetical\ngeodetics\ngeoffroea\ngeoglossaceae\ngeoglossum\ngeognosy\ngeographer\ngeographic\ngeographically\ngeography\ngeological\ngeologically\ngeologist\ngeology\ngeomancer\ngeomancy\ngeometer\ngeometric\ngeometrical\ngeometrically\ngeometrid\ngeometridae\ngeometry\ngeomorphologic\ngeomyidae\ngeomys\ngeophilidae\ngeophilomorpha\ngeophilous\ngeophilus\ngeophysical\ngeophysics\ngeophyte\ngeophytic\ngeopolitical\ngeopolitics\ngeoponics\ngeorama\ngeorge\ngeorges\ngeorgetown\ngeorgette\ngeorgia\ngeorgian\ngeorgics\ngeoscopy\ngeostationary\ngeothlypis\ngeotic\ngeotropism\ngeraniaceae\ngeraniales\ngeranium\ngerardia\ngerbera\ngerbil\ngerbillinae\ngerbillus\ngerea\ngerenuk\ngerfalcon\ngeriatric\ngeriatrics\ngerm\ngerman\ngerman-speaking\ngermander\ngermane\ngermane(p)\ngermaneness\ngermanic\ngermanism\ngermanite\ngermanium\ngermany\ngermfree\ngerminal\ngerminate\ngermination\ngermy\ngern\ngerontic\ngerontologist\ngerreidae\ngerres\ngerrhonotus\ngerrididae\ngerris\ngerrymander\ngerund\ngerundial\ngesneria\ngesneriaceae\ngesneriad\ngesserit\ngest\ngestalt\ngestapo\ngestation\ngestational\ngeste\ngestic\ngesticulate\ngesticulating\ngesticulation\ngestural\ngesture\nget\ngetaway\ngete\ngets\ngettable\ngetting\ngettysburg\ngeture\ngeum\ngewgaw\ngewonnen\ngeyser\ngf\ngfront\ngg\ngh\nghana\nghanaian\nghanian\ngharry\ngharrywallah\nghastliness\nghastly\nghat\nghatti\nghaut\nghazal\nghee\ngheg\ngherkin\nghetto\nghillie\nghost\nghostlike\nghostly\nghosts\nghostwriter\nghoul\nghoulish\nghurry\nghyll\ngi\ngiant\ngiantess\ngiantism\ngiants\ngiaour\ngiardia\ngib\ngibber\ngibberellin\ngibberish\ngibbet\ngibblegabble\ngibbon\ngibbosity\ngibbous\ngibcat\ngibe\ngibier\ngiblet\ngibraltar\ngibraltarian\ngidar\ngiddiness\ngiddy\ngiddyhead\ngiddypaced\ngidgee\ngift\ngifted\ngifts\ngig\ngigabyte\ngigahertz\ngigantean\ngigantic\ngigantism\ngigartinaceae\ngiggle\ngigolo\ngigue\ngikuyu\ngila\ngilbert\ngilbertian\ngilbertine\ngild\ngilded\ngilder\ngildhall\ngilding\ngilead\ngiles\ngilgamish\ngill\ngillie\ngills\ngilt\ngilt-edged\ngiltedged\ngimbaled\ngimbals\ngimcrack\ngimel\ngimerack\ngimlet\ngimmick\ngimmickry\ngimmicks\ngimp\ngin\nginger\ngingerbread\ngingerly\ngingerol\ngingersnap\ngingery\ngingham\ngingiva\ngingival\ngingivitis\ngingle\nginglymostoma\ngink\nginkgo\nginkgoaceae\nginkgoales\nginkgopsida\nginseng\nginsling\ngiova\ngiovane\ngipsywort\ngirace\ngiraffa\ngiraffe\ngiraffidae\ngirandole\ngirasol\ngirasole\ngird\ngirder\ngirdle\ngiriama\ngirl\ngirlbachelor\ngirlfriend\ngirlhood\ngirlish\ngirlishly\ngirlishness\ngiro\ngirru\ngirt\ngirth\ngisarme\ngismo\ngist\ngite\ngittern\ngive\ngive-and-go\ngiveaway\ngiven\ngivenness\ngiver\ngivers\ngiving\ngiza\ngizzard\ngj\ngk\ngl\nglabrous\nglac\nglacial\nglacially\nglaciarum\nglaciate\nglaciated\nglaciation\nglacier\nglacis\nglad\ngladden\ngladdened\nglade\ngladiate\ngladiator\ngladiatorial\ngladiatorship\ngladii\ngladio\ngladiolus\ngladly\ngladness\ngladsome\nglagging\nglair\nglaive\nglamor\nglamorization\nglamorous\nglance\nglances\ngland\nglanders\nglandular\nglans\nglar\nglare\nglareola\nglareolidae\nglaring\nglaringly\nglasgow\nglass\nglassblower\nglasscutter\nglasses\nglassite\nglassmaker\nglassware\nglassworks\nglasswort\nglassy\nglaswegian\nglaucium\nglaucoma\nglaucomys\nglauconite\nglaucous\nglaux\nglave\nglaver\nglaze\nglazed\ngle\ngleam\nglean\ngleaner\ngleaning\ngleanings\ngleba\nglebe\ngleboe\nglechoma\ngleditsia\nglee\ngleeful\ngleefully\ngleek\ngleesome\ngleet\ngleichenia\ngleicheniaceae\nglen\nglengarry\nglenn\ngles\nglib\nglibly\nglibness\nglide\nglider\nglim\ngliming\nglimmer\nglimmering\nglimpse\nglint\nglioma\ngliricidia\ngliridae\nglis\nglissade\nglissando\nglisten\nglistening\nglister\nglitch\nglitter\nglittering\nglitters\nglitz\ngloam\ngloaming\ngloar\ngloat\ngloatingly\nglob\nglobal\nglobally\nglobated\nglobe\nglobeflower\nglobegirdler\nglobetrotter\nglobicephala\nglobigerina\nglobigerinidae\nglobin\nglobose\nglobosity\nglobous\nglobular\nglobule\nglobulin\nglockenspiel\nglogg\ngloire\nglom\nglomeration\nglomerular\nglomerulonephritis\nglomerulus\ngloom\ngloomily\ngloominess\nglooming\ngloomy\nglop\ngloria\ngloriae\ngloriation\nglories\nglorification\nglorify\ngloriosa\nglorious\ngloriously\nglory\ngloss\nglossarist\nglossary\nglossily\nglossinidae\nglossitis\nglossodia\nglossographer\nglossography\nglossolinguist\nglossology\nglossopsitta\nglossy\nglottal\nglottis\nglottochronological\nglottochronology\nglottology\nglout\nglove\ngloved\ngloveless\ngloves\nglow\nglower\nglowering\ngloweringly\nglowing\nglowing(a)\nglowingly\nglowworm\ngloxinia\ngloze\nglucagon\ngluck\ngluckliche\nglucocorticoid\nglucose\nglucoside\nglue\nglued\ngluey\nglum\nglume\ngluon\nglut\nglutamate\nglutamine\ngluteal\nglutelin\ngluten\ngluteus\nglutinosity\nglutinous\nglutinousness\nglutted\nglutton\ngluttonous\ngluttonously\ngluttony\ngly\nglyburide\nglyceraldehyde\nglyceria\nglyceride\nglycerin\nglycerine\nglycerite\nglycerogelatin\nglyceryl\nglycine\nglycogen\nglycolysis\nglycoprotein\nglycoside\nglycyrrhiza\nglyph\nglyphograph\nglyphography\nglyptics\nglyptograph\nglyptography\nglyptotheca\nglyster\ngnaphalium\ngnarl\ngnarled\ngnash\ngnashing\ngnat\ngnatcatcher\ngnathostomata\ngnathostome\ngnaw\ngnawing\ngneiss\ngnetaceae\ngnetales\ngnetopsida\ngnetum\ngnome\ngnomic\ngnomish\ngnomon\ngnosis\ngnostic\ngnosticism\ngnothi\ngnu\ngo\ngo-as-you-please\ngo-getter\ngo-kart\ngo-slow\ngoad\ngoahead\ngoal\ngoal-directed\ngoal-kick\ngoaler\ngoalkeeper\ngoalmouth\ngoalpost\ngoat\ngoatee\ngoateed\ngoatfish\ngoatherder\ngoatish\ngoatsfoot\ngoatskin\ngoatsucker\ngob\ngobbet\ngobble\ngobbledygook\ngobemouche\ngobetween\ngobi\ngobiesocidae\ngobiesox\ngobiidae\ngobio\ngoblet\ngoblin\ngobs\ngoby\ngocart\ngod\ngodchild\ngoddam\ngoddaughter\ngoddess\ngoddesses\ngodfather\ngodhead\ngodiva\ngodless\ngodlike\ngodliness\ngodly\ngodmother\ngodown\ngodparent\ngods\ngodsend\ngodship\ngodson\ngodspeed\ngodwit\ngoedesy\ngoer\ngoes\ngoethe\ngoethean\ngoethes\ngoethite\ngoffer\ngog\ngoggle\ngoggle-eyed\ngoggleeyed\ngoggleeyes\ngoggles\ngogo\ngoing\ngoing(a)\ngoing-over\ngoings\ngoiter\ngoitrogen\ngolconda\ngold\ngoldbeater\ngoldbrick\ngoldbricking(a)\ngoldcolored\ngoldcrest\ngolden\ngoldenbush\ngoldeneye\ngoldenrod\ngoldenseal\ngoldes\ngoldfield\ngoldfields\ngoldfinch\ngoldfish\ngoldilocks\ngoldmine\ngoldsmith\ngoldstone\ngoldthread\ngolem\ngolf\ngolfcart\ngolfclub\ngolfer\ngolfing\ngolgotha\ngoliath\ngolliwog\ngolly\ngoloshes\ngomashta\ngomorrah\ngomphothere\ngomphotheriidae\ngomphotherium\ngomphrena\ngonadal\ngonadotropic\ngonadotropin\ngond\ngondi\ngondola\ngondolier\ngondwanaland\ngone\ngone(p)\ngoneness\ngoner\ngonfalon\ngong\ngoniometer\ngoniometry\ngoniopteris\ngonococcus\ngonorhynchidae\ngonorhynchus\ngonorrhea\ngoo\ngood\ngood(p)\ngood-for-nothing\ngood-hearted\ngood-king-henry\ngood-natured\ngood-naturedly\ngood-temperedness\ngoodbye\ngoodday\ngoodfellow\ngoodhumored\ngoodish\ngoodlooking\ngoodly\ngoodmannered\ngoodnatured\ngoodness\ngoods\ngoodwife\ngoodwill\ngoodwin\ngoody\ngoody-goody\ngoodyera\ngooey\ngoof\ngoogly\ngoogol\ngook\ngoon\ngoop\ngoosander\ngoose\ngooseberry\ngooseberryeyed\ngoosecap\ngoosefish\ngooseflesh\ngoosefoot\ngooseneck\ngoosh\ngopher\ngopherus\ngoral\ngoran\ngordian\ngore\ngorge\ngorged\ngorgeous\ngorgeously\ngorgeousness\ngorgerin\ngorget\ngorgon\ngorgonacea\ngorgonian\ngorgonocephalus\ngorgons\ngorgonzola\ngorilla\ngorki\ngormandize\ngormandizing\ngorse\ngory\ngosainthan\ngosh\ngoshawk\ngosling\ngospel\ngospels\ngossamer\ngossamery\ngossip\ngossiping\ngossoon\ngossypium\ngoteborg\ngoth\ngotha\ngotham\ngothamite\ngothic\ngothicism\ngotterdammerung\ngouache\ngouda\ngouge\ngoulash\ngourd\ngourde\ngourmand\ngourmet\ngout\ngoutte\ngouttes\ngouty\ngovern\ngoverned\ngoverness\ngoverning\ngovernment\ngovernment-in-exile\ngovernmental\ngovernmentally\ngovernor\ngovernors\ngovernorship\ngowk\ngown\ngowned\ngownsman\ngoy\ngpa\ngr\u0003ce\ngr\ngr/\ngr/anerythmon\ngr/ariston\ngr/dos\ngr/eidolon/gr\ngr/eros/gr\ngr/gnothi\ngr/hysteron\ngr/kat\ngr/kudos/gr\ngr/noemata/gr\ngr/phonanta\ngr/pou\ngr/storge/gr\ngr/to\ngr/trikumia/\ngrab\ngrab(a)\ngrabble\ngrace\ngraceful\ngracefully\ngracefulness\ngraceless\ngracelessly\ngracelessness\ngraces\ngracilariid\ngracilariidae\ngracile\ngracious\ngraciously\ngraciousness\ngrackle\ngracula\ngrad\ngradable\ngradatim\ngradation\ngradational\ngradations\ngrade\ngrade-constructed\ngraded\ngrader\ngradient\ngrading\ngrado\ngradual\ngraduality\ngradually\ngradualness\ngraduate\ngraduate(a)\ngraduated\ngraduation\ngradus\ngraeculus\ngraf\ngraffiti\ngraffito\ngraft\ngrag\ngraham\ngrail\ngrain\ngrained\ngrainfield\ngrainger\ngraining\ngrains\ngrallatory\ngram\ngram-negative\ngram-positive\ngrama\ngramercy\ngramicidin\ngraminales\ngramineae\ngraminivorous\ngrammar\ngrammarian\ngrammatical\ngrammatically\ngrammatophyllum\ngramophone\ngrampus\ngranada\ngranadilla\ngranary\ngrand\ngrandam\ngrandchild\ngrandchildren\ngranddaughter\ngrande\ngrandee\ngrandeur\ngrandfather\ngrandfathers\ngrandiloquence\ngrandiloquent\ngrandiloquently\ngrandiose\ngrandiosity\ngrandly\ngrandma\ngrandmother\ngrandparent\ngrands\ngrandsire\ngrandson\ngrandstand\ngrange\ngranger\ngranicus\ngranite\ngraniteware\ngranitic\ngranivorous\ngranny\ngrano\ngranola\ngrant\ngrant-in-aid\ngranted\ngrantee\ngrantinaid\ngrantor\ngranular\ngranulate\ngranulated\ngranulation\ngranule\ngranuliferous\ngranulocyte\ngranulocytic\ngranuloma\ngranulomatous\ngrape\ngrapefruit\ngrapelike\ngrapes\ngrapeshot\ngrapevine\ngrapey\ngraph\ngraphic\ngraphically\ngraphics\ngraphite\ngraphoidea\ngraphologist\ngraphology\ngraphomania\ngraphometer\ngraphotype\ngrapnel\ngrappa\ngrapple\ngrappling\ngraptophyllum\ngras\ngrasmicareme\ngrasp\ngrasping\ngrasps\ngrass\ngrass-covered\ngrass-eating(a)\ngrassfinch\ngrassfire\ngrasshopper\ngrassland\ngrassless\ngrasslike\ngrassplat\ngrassplot\ngrassroots\ngrassy\ngrata\ngrate\ngrated\ngrateful\ngratefulness\ngrater\ngratia\ngratification\ngratified\ngratify\ngratifying\ngratifyingly\ngrating\ngratingly\ngratior\ngratis\ngratissimus\ngratitude\ngratuitious\ngratuitous\ngratuitously\ngratuity\ngratulate\ngratulation\ngratulatory\ngravamen\ngrave\ngraveclothes\ngravedigger\ngravel\ngraveled\ngravelly\ngravelweed\ngravely\ngraven\ngraveness\ngraveolent\ngraver\ngraverobber\ngraves\ngravestone\ngraveunknelld\ngravida\ngravidity\ngravimeter\ngravior\ngravis\ngravitate\ngravitation\ngravitational\ngravitationally\ngravitons\ngravity\ngravity-assist\ngravius\ngravure\ngravy\ngray\ngrayback\ngraybeard\ngrayheaded\ngrayhen\ngraylag\ngrayly\ngraze\ngrazed\ngrazier\ngrazing\ngre\ngrease\ngrease-gun\ngreasepaint\ngreaseproof\ngreaser\ngreasewood\ngreasily\ngreasiness\ngreasy\ngreat\ngreat-aunt\ngreat-nephew\ngreat-niece\ngreat-uncle\ngreatcoat\ngreater\ngreatest\ngreatgrandchild\ngreathearted\ngreatly\ngreatness\ngreave\ngrebe\ngreco-roman\ngreece\ngreed\ngreediness\ngreedy\ngreek\ngreen\ngreenback\ngreenbelt\ngreenbottle\ngreene\ngreenery\ngreeneye\ngreeneyed\ngreenfly\ngreengage\ngreengrocer\ngreengrocery\ngreenhorn\ngreenhouse\ngreening\ngreenish\ngreenishness\ngreenland\ngreenling\ngreenly\ngreenmail\ngreenness\ngreenockite\ngreenrobed\ngreenroom\ngreens\ngreensand\ngreensboro\ngreenshank\ngreenside\ngreenskeeper\ngreensward\ngreenwich\ngreenwing\ngreenwood\ngreet\ngreeting\ngregarine\ngregarinida\ngregarious\ngregariously\ngregariousness\ngregorian\ngregory\ngreisen\ngremlin\ngrenada\ngrenade\ngrenadian\ngrenadier\ngrenadine\ngrevillea\ngrewia\ngrewsome\ngrey\ngreyhound\ngrias\ngrid\ngriddle\ngriddlecake\ngridelin\ngridiron\ngrids\ngrieat\ngrief\ngrievance\ngrieve\ngrievous\ngrievously\ngriffin\ngriffo\ngriffon\ngriffonage\ngrift\ngrig\ngrigri\ngrill\ngrille\ngrillroom\ngrim\ngrimace\ngrimacer\ngrimacier\ngrimalkin\ngrime\ngrimfaced\ngrimffe\ngriminess\ngrimly\ngrimm\ngrimoire\ngrimvisaged\ngrimy\ngrin\ngrind\ngrindelia\ngrinder\ngrindery\ngrinding\ngrindstone\ngringo\ngriot\ngrip\ngripe\ngriped\ngriping\ngrippe\ngrips\ngripsack\ngrisaille\ngriselinia\ngriseofulvin\ngrisette\ngrisly\ngrison\ngrissino\ngrist\ngristle\ngristly\ngristmill\ngrit\ngrits\ngritty\ngriveous\ngrivet\ngrizzle\ngrizzled\ngrizzly\ngroan\ngroaning\ngroat\ngroats\ngrocer\ngroceries\ngrocery\ngrody\ngroecas\ngroenendael\ngroenlandia\ngrog\ngrogginess\ngroggy\ngrogram\ngroin\ngrommet\ngromwell\ngroom\ngroomed\ngroomsman\ngroove\ngrooved\ngroover\ngrooves\ngrooving\ngroovy\ngrope\ngroping\ngropingly\ngropius\ngrosbeak\ngroschen\ngrosgrain\ngross\ngrosse\ngrosshead\ngrossheaded\ngrossieret\ngrossierete\ngrossly\ngrossness\ngrossulariaceae\ngrosz\ngrot\ngrotesque\ngrotesquely\ngrotesqueness\ngrotto\ngrotty\ngrouch\ngrouchy\ngroudwork\nground\nground-floor\nground-shaker\ngroundbreaking\ngroundcover\ngrounded\ngrounder\ngroundfish\ngroundhog\ngrounding\ngroundless\ngroundling\ngroundmass\ngroundnut\ngrounds\ngroundsel\ngroundsheet\ngroundsman\ngroundspeed\ngroundwork\ngrount\ngroup\ngrouped\ngrouper\ngroupie\ngrouping\ngroups\ngroupware\ngrouse\ngrouseberry\ngroush\ngrout\ngrouty\ngrove\ngrovel\ngroveling\ngrow\ngrowing\ngrowl\ngrowler\ngrowling\ngrown\ngrowth\ngroyne\ngrub\ngrubbing\ngrubby\ngrubstake\ngrubstreet\ngrudge\ngrudging\ngrudgingly\ngrue\ngruel\ngruesomely\ngruff\ngruffly\ngruffness\ngrugru\ngruidae\ngruiformes\ngrum\ngrumble\ngrumbler\ngrumbling\ngrume\ngrumous\ngrump\ngrumpy\ngrundy\ngrunt\ngrunting\ngruntle\ngrus\ngruyere\ngrv\ngryllidae\ngryphon\nguadagna\nguadalajara\nguadalcanal\nguadeloupe\nguaiacum\nguallatiri\nguam\nguama\nguan\nguanaco\nguanine\nguano\nguar\nguarani\nguarantee\nguarantor\nguaranty\nguard\nguarda\nguardant(ip)\nguarded\nguardhouse\nguardian\nguardianship\nguardless\nguardroom\nguards\nguardship\nguardsman\nguatemala\nguatemalan\nguava\nguayaquil\nguayule\ngubernation\ngubernatorial\nguck\ngudgeon\nguenon\nguerdon\nguereza\ngueridon\nguerilla\ngueristoi\nguernsey\nguerre\nguerrilla\nguerrilla(a)\nguess\nguesstimate\nguesswork\nguest\nguesthouse\nguestroom\nguests\nguet\nguetapens\nguevina\nguff\nguffaw\nguggle\nguiana\nguidance\nguide\nguidebook\nguided\nguideless\nguideline\nguidepost\nguiding\nguidon\nguild\nguilder\nguildhall\nguile\nguileless\nguillemets\nguillemot\nguilloche\nguillotine\nguilt\nguilt-ridden\nguiltily\nguiltiness\nguiltless\nguiltlessness\nguilty\nguimpe\nguinde\nguinea\nguinea-bissau\nguinean\nguinesss\nguinevere\nguinness\nguisard\nguise\nguiser\nguitar\nguitarfish\nguitarist\ngujarat\ngujarati\ngula\ngulag\ngulch\ngules\ngulf\ngulfweed\ngull\ngullery\ngullet\ngullible\ngulliver\ngully\ngulo\ngulosity\ngulp\ngulph\ngulping\ngum\ngum-lac\ngumbo\ngumbo-limbo\ngumboil\ngumdrop\ngumma\ngummed\ngummite\ngummosis\ngummosity\ngumption\ngums\ngumweed\ngumwood\ngun\ngunboat\nguncotton\ngunfight\ngunfire\ngunflint\ngungho\ngunite\ngunk\ngunlock\ngunman\ngunmetal\ngunnel\ngunner\ngunnery\ngunnysack\ngunpowder\ngunrunner\ngunrunning\nguns\ngunsight\ngunsmith\ngunter\ngunwale\nguod\nguppy\ngur\ngurge\ngurgel\ngurgle\ngurgoyle\ngurkha\ngurnard\ngurney\ngurry\nguru\ngush\ngusher\ngushing\ngushingly\ngusset\ngusseted\ngust\ngustable\ngustation\ngustatory\ngustful\ngustless\ngusto\ngusty\ngut\ngutierrezia\ngutless\ngutlessness\nguts\ngutsiness\ngutsy\ngutta-percha\nguttaserena\ngutted\ngutter\nguttering\nguttiferae\nguttiferales\nguttle\nguttling\nguttural\ngutturally\nguvnor\nguy\nguyana\nguyanese\nguyot\nguzzle\nguzzler\nguzzling\ngvisum\ngvr\ngwydion\ngwyn\ngy\ngybe\ngym\ngymkhana\ngymnadenia\ngymnadeniopsis\ngymnasium\ngymnast\ngymnastic\ngymnastics\ngymnelis\ngymnocalycium\ngymnocarpium\ngymnocladus\ngymnogyps\ngymnophiona\ngymnopilus\ngymnorhina\ngymnosophical\ngymnosophist\ngymnosophy\ngymnosperm\ngymnospermae\ngymnospermous\ngymnosporangium\ngymnura\ngymslip\ngynaecic\ngynaeocracy\ngynandromorphic\ngynarchy\ngynecaeum\ngynecic\ngynecological\ngynecologist\ngynecology\ngynecomastia\ngyneolatry\ngynephobia\ngynocracy\ngynogenesis\ngynophobia\ngynura\ngyp\ngypaetus\ngyps\ngypsophila\ngypsum\ngypsy\ngyral\ngyrate\ngyration\ngyratory\ngyre\ngyrfalcon\ngyrinidae\ngyro\ngyrocompass\ngyromancy\ngyromitra\ngyroscope\ngyroscopic\ngyrostabilizer\ngyrus\ngysart\ngyve\nha\nhaastia\nhabe\nhabeas\nhabenaria\nhabent\nhaber\nhaberdasher\nhaberdashery\nhabergeon\nhabet\nhabiles\nhabiliment\nhabilitation\nhabilite\nhabit\nhabitable\nhabitant\nhabitat\nhabitation\nhabited\nhabitmaker\nhabits\nhabitual\nhabitually\nhabituate\nhabituated\nhabituation\nhabitude\nhabitue\nhac\nhacek\nhachiman\nhachure\nhacienda\nhack\nhackamore\nhackberry\nhackbut\nhackee\nhackelia\nhacker\nhackery\nhackle\nhackman\nhackney\nhackneyed\nhacksaw\nhackwork\nhad\nhadal\nhaddock\nhadean\nhades\nhadj\nhadji\nhadron\nhadrosaur\nhadrosauridae\nhae\nhaec\nhaemanthus\nhaematobia\nhaematobious\nhaematopodidae\nhaematopus\nhaematoxylum\nhaemodoraceae\nhaemodorum\nhaemophilic\nhaemopis\nhaemoproteid\nhaemoproteidae\nhaemoproteus\nhaemosporidia\nhaemosporidian\nhaemulidae\nhaemulon\nhaerent\nhaeres\nhaeret\nhaesit\nhafnium\nhaft\nhag\nhag-ridden\nhagberry\nhageman\nhagfish\nhaggard\nhaggardly\nhaggis\nhaggle\nhagiographa\nhagiography\nhagiolatry\nhagiology\nhaguebut\nhaha\nhaida\nhaifa\nhaik\nhaiku\nhail\nhailstone\nhailstorm\nhair\nhair's-breadth\nhairball\nhairbreadth\nhairbrush\nhaircloth\nhaircut\nhairdo\nhairdresser\nhairdressing\nhaired\nhairif\nhairiness\nhairless\nhairlessness\nhairline\nhairnet\nhairpiece\nhairpin\nhairs\nhairsplitting\nhairspring\nhairstreak\nhairy\nhaiti\nhaitian\nhajj\nhajji\nhake\nhakea\nhakka\nhalberd\nhalberdier\nhalchidhoma\nhalcyon\nhaldea\nhale\nhalenia\nhaler\nhalesia\nhalf\nhalf(a)\nhalf-and-half\nhalf-baked\nhalf-blooded\nhalf-bound\nhalf-breed\nhalf-caste\nhalf-century\nhalf-clothed\nhalf-cock\nhalf-hardy\nhalf-heartedly\nhalf-holiday\nhalf-hour\nhalf-hourly\nhalf-intensity\nhalf-length\nhalf-light\nhalf-mast\nhalf-moon\nhalf-pay\nhalf-price\nhalf-seas-over\nhalf-size\nhalf-timber\nhalf-time\nhalf-track\nhalf-truth\nhalf-yearly\nhalfadozen\nhalfandhalf\nhalfback\nhalfbaked\nhalfbeak\nhalfblind\nhalfblood\nhalfbreed\nhalfcaste\nhalfhearted\nhalflearned\nhalfmoon\nhalfpenny\nhalfpennyworth\nhalfprice\nhalfstarved\nhalftime\nhalftone\nhalfway\nhalfwit\nhaliaeetus\nhalibut\nhalicoeres\nhalictidae\nhalide\nhalifax\nhalimodendron\nhaliotidae\nhaliotis\nhalitosis\nhall\nhallel\nhallelujah\nhallmark\nhalloa\nhalloo\nhallow\nhallowed\nhalloween\nhallowmas\nhallstand\nhallucination\nhallucinatory\nhallucinogen\nhallucinogenic\nhallway\nhalma\nhalo\nhalobacteria\nhalocarbon\nhalocarpus\nhalogen\nhalogeton\nhalomancy\nhalophile\nhalophyte\nhaloragidaceae\nhalothane\nhalser\nhalt\nhalter\nhalting\nhaltingly\nhalve\nhalves\nhalvesgo\nhalving\nhalyard\nham\nhamadryad\nhamal\nhamamelidaceae\nhamamelidae\nhamamelidanthum\nhamamelidoxylon\nhamamelis\nhamamelites\nhaman\nhamate\nhamburg\nhamburger\nhame\nhamelia\nhamfatter\nhamiform\nhamilton\nhaminoea\nhamitic\nhamlet\nhammer\nhammered\nhammerhead\nhammering\nhammerlock\nhammertoe\nhamming\nhammock\nhammy\nhamous\nhamper\nhampshire\nhamster\nhamstring\nhanaper\nhand\nhand-held\nhand-loomed\nhand-me-down\nhand-operated\nhand-picked\nhand-to-hand\nhand-to-mouth(a)\nhandball\nhandbarrow\nhandbell\nhandbook\nhandbow\nhandbreadth\nhandbreath\nhandcar\nhandcart\nhandclap\nhandcuff\nhandcuffs\nhanded\nhanded-down\nhandedness\nhandel\nhandelian\nhanderchief\nhandfast\nhandful\nhandhold\nhandicap\nhandicraft\nhandicraftsman\nhandily\nhandiness\nhandiwork\nhandkerchief\nhandle\nhandlebar\nhandled\nhandleless\nhandless\nhandline\nhandling\nhandloom\nhandmade\nhandmaid\nhandoff\nhandout\nhandover\nhandpost\nhandrest\nhands\nhands-down\nhandsaw\nhandsel\nhandset\nhandsewn\nhandshake\nhandsome\nhandsomely\nhandsomeness\nhandspike\nhandspring\nhandstamp\nhandstand\nhandwear\nhandwheel\nhandwriting\nhandwritten\nhandy\nhandyman\nhanemolia\nhang\nhang-up\nhangar\nhangdog\nhanged\nhanger\nhangeron\nhangers\nhanging\nhangman\nhangnail\nhangover\nhangs\nhangzhou\nhani\nhank\nhanker\nhankering\nhannibal\nhannibalem\nhannover\nhanoi\nhanover\nhanoverian\nhansard\nhansards\nhansom\nhanukkah\nhanuman\nhao\nhap\nhaphazard\nhapless\nhaplography\nhaploid\nhaplopappus\nhaplosporidia\nhaplosporidian\nhaply\nhappen\nhappening\nhappens\nhappier\nhappily\nhappiness\nhappy\nhappygolucky\nhapsburg\nhaptic\nharakiri\nharangue\nharare\nharass\nharassing\nharassment\nharbinger\nharbor\nharborage\nharborless\nhard\nhard(a)\nhard-and-fast\nhard-baked\nhard-bitten\nhard-boiled\nhard-fought\nhard-hitting\nhard-of-hearing\nhard-surfaced\nhard-to-please(a)\nhardback\nhardbacked\nhardbake\nhardball\nhardearned\nharden\nhardenbergia\nhardened\nhardening\nhardfought\nhardheaded\nhardhearted\nhardihood\nharding\nhardinggrass\nhardliner\nhardly\nhardmouthed\nhardness\nhardpan\nhardscrabble\nhardshell\nhardship\nhardtack\nhardtop\nhardware\nhardwood\nhardworking\nhardy\nhare\nharebell\nharebrained\nharelip\nharelipped\nharem\nhargeisa\nharicot\nhariff\nhariolation\nhark\nharken\nharlem\nharlequin\nharlequinade\nharlot\nharlotry\nharm\nharmattan\nharmed\nharmful\nharmfulness\nharmless\nharmlessly\nharmonic\nharmonica\nharmonical\nharmonically\nharmonicon\nharmonics\nharmonious\nharmoniously\nharmoniphon\nharmoniphone\nharmonist\nharmonium\nharmonizable\nharmonization\nharmonize\nharmony\nharms\nharness\nharnessed\nharp\nharpagon\nharper\nharpia\nharping\nharpist\nharpoon\nharpooner\nharpsichord\nharpsichordist\nharpulla\nharpullia\nharpy\nharquebuss\nharrent\nharridan\nharrier\nharrisburg\nharrisia\nharrison\nharrow\nharrowing\nharry\nharsh\nharshly\nharshness\nhart\nhart's-tongue\nhartebeest\nhartford\nhartshorn\nharum-scarum\nharumscarum\nharupsical\nharuspex\nharuspice\nharuspicy\nharvest\nharvest-lice\nharvester\nharvestfish\nharvesthome\nharvestman\nhas\nhas-been\nhasdrubal\nhash\nhashish\nhaslet\nhasp\nhassle\nhassock\nhast\nhasta\nhastate\nhaste\nhasten\nhastening\nhastily\nhastiness\nhastings\nhasty\nhastyquick\nhat\nhatband\nhatbox\nhatch\nhatchback\nhatched\nhatchel\nhatchery\nhatches\nhatchet\nhatchetfaced\nhatchling\nhatchment\nhatchway\nhate\nhated\nhateful\nhatefully\nhatefulness\nhatemonger\nhater\nhatful\nhath\nhating\nhatiora\nhatless\nhatmaker\nhatpin\nhatrack\nhatred\nhatted\nhatter\nhattisherif\nhattock\nhauberk\nhaud\nhaugh\nhaughtily\nhaughtiness\nhaughty\nhaul\nhaulage\nhauler\nhauling\nhaulm\nhaunch\nhaunt\nhaunted\nhaunter\nhaunting\nhausa\nhausmannite\nhaustorium\nhaut\nhautboy\nhaute\nhaute-normandie\nhauteur\nhautgout\nhavana\nhavasupai\nhave\nhaven\nhaversack\nhaving\nhavoc\nhaw\nhawaii\nhawaiian\nhawfinch\nhawk\nhawk's-beard\nhawkbit\nhawker\nhawkeyed\nhawking\nhawkishness\nhawklike\nhawkmoth\nhawkweed\nhawse\nhawser\nhawthorn\nhay\nhay-scented\nhaycock\nhaydn\nhayes\nhayfield\nhayfork\nhaying\nhayley\nhayloft\nhaymaker\nhaymaking\nhayrack\nhayseed\nhaystack\nhayward\nhaywire\nhazard\nhazarded\nhazardia\nhazardous\nhazardousness\nhazards\nhaze\nhazel\nhazelnut\nhazily\nhaziness\nhazmat\nhazy\nhb\nhc\nhd\nhe\nheaavy\nhead\nhead(a)\nhead-on\nheadache\nheadband\nheadboard\nheadcheese\nheaddress\nheaded\nheader\nheadfast\nheadfirst\nheadforemost\nheadful\nheadgear\nheadhunter\nheading\nheadland\nheadless\nheadlight\nheadlike\nheadline\nheadliner\nheadlinese\nheadlock\nheadlong\nheadman\nheadmaster\nheadmastership\nheadmistress\nheadmistresship\nheadmost\nheadpiece\nheadpin\nheadquarters\nheadrace\nheadrest\nheadroom\nheads\nheads-up\nheadsail\nheadscarf\nheadset\nheadshake\nheadship\nheadshot\nheadsman\nheadspace\nheadstall\nheadstand\nheadstock\nheadstone\nheadstrong\nheadwaters\nheadway\nheadwind\nheadword\nheady\nheal\nheald\nhealer\nhealing\nhealth\nhealthful\nhealthfulness\nhealthgiving\nhealthily\nhealthiness\nhealthless\nhealthy\nheap\nheaped\nheaps\nhear\nheard\nhearer\nhearing\nhearing(a)\nhearken\nhearsay\nhearse\nheart\nheart-whole\nheartache\nheartbreaking\nheartbroken\nheartburn\nheartburning\nheartcorroding\nhearted\nheartedness\nheartening\nheartexpanding\nheartfelt\nheartgrief\nhearth\nhearthrug\nhearthstone\nheartily\nheartiness\nheartleaf\nheartless\nheartlessly\nheartlessness\nheartquake\nheartrending\nheartrobbing\nheartrot\nhearts\nheartscalded\nheartseed\nheartsick\nheartsickening\nheartsinking\nheartsome\nheartstirring\nheartstricken\nheartswelling\nheartthrilling\nheartthrob\nheartwarming\nheartwood\nheartworm\nheartwounding\nhearty\nheat\nheatable\nheated\nheatedly\nheater\nheath\nheathen\nheathendom\nheathenish\nheathenism\nheathenmythology\nheather\nheathlike\nheating\nheatless\nheatstroke\nheaume\nheautontimorumenos\nheave\nheaven\nheaven-sent\nheavenborn\nheavendirected\nheavenly\nheavens\nheavenward\nheaver\nheaves\nheavily\nheaviness\nheaving\nheavy\nheavy-armed\nheavy-coated\nheavy-duty\nheavy-footed\nheavy-handed\nheavyhanded\nheavyhearted\nheavyheartedness\nheavyweight\nhebdomadal\nhebdomadally\nhebdomadary\nhebe\nhebephrenia\nhebephrenic\nhebetate\nhebetation\nhebetic\nhebetude\nhebetudinous\nhebraic\nhebraist\nhebrew\nhebrews\nhebridean\nhebrides\nhecatomb\nhecha\nheck\nheckelphone\nheckle\nheckler\nheckling\nhectare\nhectic\nhectogram\nhectograph\nhectoliter\nhectometer\nhector\nhectoring\nheddle\nhedeoma\nhedera\nhedge\nhedged\nhedgehog\nhedonic\nhedonics\nhedonism\nhedonist\nhedonsim\nhedysarum\nhee-haw\nheed\nheedful\nheedfulness\nheedless\nheedlessnes\nheedlessness\nheel\nheelbone\nheeler\nheelpiece\nheels\nheeltap\nheft\nhefty\nhegari\nhegel\nhegelian\nhegemonic\nhegemonical\nhegemony\nhegoat\nheh\nheifer\nheighho\nheight\nheighten\nheightening\nheightening(a)\nheights\nheil\nheimdall\nheinous\nheinously\nheir\nheir-at-law\nheirapparent\nheiress\nheirloom\nheirpresumptive\nheirs\nheirship\nheiss\nheist\nhejira\nhel\nheld\nhelen\nhelena\nhelenium\nheliacal\nheliamphora\nhelianthemum\nhelical\nhelicanhorn\nhelichrysum\nhelicidae\nhelicon\nhelicopter\nhelicteres\nheliocentric\nheliogabalus\nheliogram\nheliograph\nheliographic\nheliography\nheliolatry\nheliometer\nheliopause\nheliophagous\nheliophila\nheliopsis\nhelios\nhelioscope\nheliosphere\nheliothis\nheliotrope\nheliotropism\nheliotype\nheliozoa\nheliozoan\nheliport\nhelipterum\nhelium\nhelix\nhell\nhell-bent\nhell-kite\nhellbender\nhellborn\nhellcat\nhellebore\nhelleborine\nhelleborus\nhellenic\nhellenism\nhellfire\nhellgrammiate\nhellhag\nhellhound\nhellion\nhellish\nhello\nhells\nhelluo\nhelm\nhelmet\nhelmeted\nhelmetflower\nhelmholtz\nhelminth\nhelminthagogue\nhelminthology\nhelminthostachys\nhelmsman\nheloderma\nhelodermatidae\nhelot\nhelotiaceae\nhelotiales\nhelotium\nhelp\nhelped\nhelper\nhelpers\nhelpful\nhelpfully\nhelpfulness\nhelping\nhelpless\nhelplessly\nhelplessness\nhelpmate\nhelps\nhelsinki\nhelter-skelter\nhelterskelter\nhelve\nhelvella\nhelvellaceae\nhelxine\nhem\nhemachatus\nhemal\nhemangioma\nhematite\nhematochrome\nhematologic\nhematologist\nhematology\nhematoma\nhematuria\nheme\nhemeralopia\nhemerobiidae\nhemerocallidaceae\nhemerocallis\nhemi\nhemiacetal\nhemiascomycetes\nhemic\nhemiepiphyte\nhemigalus\nhemigrammus\nhemimetabolous\nhemimorphite\nhemingway\nhemingwayesque\nhemiparasite\nhemiparasitic\nhemiplegia\nhemiplegic\nhemiprocnidae\nhemiptera\nhemipteronatus\nhemiramphidae\nhemisphere\nhemispheric\nhemispherical\nhemitripterus\nhemline\nhemlock\nhemming-stitch\nhemodialysis\nhemoglobin\nhemoglobinopathy\nhemolysin\nhemolysis\nhemolytic\nhemophilia\nhemophiliac\nhemorrhage\nhemorrhagic\nhemorrhoid\nhemorrhoids\nhemosiderin\nhemostat\nhemp\nhempen\nhemstitch\nhen\nhen-of-the-woods\nhenbane\nhenbit\nhence\nhenceforth\nhenceforwards\nhenchman\nhencoop\nhendiadys\nhenhearted\nhenhussy\nhenna\nhennaed\nhenpecked\nhenroost\nhenry\nhep\nhepadnavirus\nheparin\nhepatic\nhepatica\nhepaticopsida\nhepatitis\nhepatize\nhephaestus\nheptagon\nheptane\nher\nhera\nheracleum\nheraclitus\nherald\nheralded\nheraldic\nheraldry\nherb\nherba\nherbaceous\nherbage\nherbal\nherbalist\nherbarian\nherbarist\nherbarium\nherbe\nherbert\nherbicide\nherbist\nherbivore\nherbivorous\nherborist\nherborization\nherborize\nherbs\nherculaneum\nherculean\nherculem\nhercules\nhercules'-club\nherd\nherder\nherding(a)\nherds\nherdsman\nhere\nhere(p)\nhereabout\nhereafter\nhereby\nhereditament\nhereditaments\nhereditary\nhereditas\nheredity\nhereford\nherein\nhereinafter\nhereinbefore\nhereness\nhereof\nheresy\nheretic\nheretical\nhereto\nheretofore\nhereunto\nhereupon\nherewith\nheritage\nheritiera\nheritor\nhermannia\nhermaphrodite\nhermaphroditic\nhermeneutics\nhermes\nhermetic\nhermetically\nhermissenda\nhermit\nhermitage\nhernaria\nhernia\nhero\nherod\nherodotus\nheroic\nheroically\nheroics\nheroin\nheroine\nheroism\nheron\nheronry\nherpangia\nherpes\nherpestes\nherpetologist\nherpetology\nherr\nherrerasaur\nherring\nherringbone\nhers\nherself\nhert\nhertz\nhertzian\nheshvan\nhesitance\nhesitancy\nhesitant\nhesitantly\nhesitate\nhesitating\nhesitation\nhesperian\nhesperides\nhesperiphona\nhesperis\nhessian\nhest\nhesterni\nhestia\nheteranthera\nheterarchy\nheterobasidiomycetes\nheterocephalus\nheterocercal\nheteroclite\nheterocyclic\nheterodactyl\nheterodon\nheterodox\nheterodoxy\nheterodyne\nheteroecious\nheterogamy\nheterogeneity\nheterogeneous\nheterogenesis\nheterogenetic\nheterogenous\nheterograft\nheterokontophyta\nheterologous\nheteromeles\nheterometabolous\nheteromyidae\nheteronomy\nheteronym\nheteropathic\nheteropathy\nheteroptera\nheteroscelus\nheterosexism\nheterosexual\nheterosexuality\nheterosomata\nheterosporous\nheterospory\nheterostracan\nheterostraci\nheterotheca\nheterotrichales\nheterozygous\nheth\nhetman\nheuchera\nheure\nheuristic\nhevea\nhew\nhewer\nhewers\nhewn\nhex\nhexachloraphene\nhexadecimal\nhexaglot\nhexagon\nhexagram\nhexagrammidae\nhexagrammos\nhexahedron\nhexalectris\nhexameter\nhexamita\nhexanchidae\nhexanchus\nhexane\nhexangular\nhexed\nhexose\nhey\nheyday\nheydey\nhf\nhi\nhi-fi\nhiation\nhiatus\nhiawatha\nhibachi\nhibbertia\nhibernal\nhibernate\nhibernation\nhibernian\nhibernicism\nhibiscus\nhic\nhiccough\nhiccup\nhick\nhickory\nhid\nhidalgo\nhidatsa\nhidden\nhiddenite\nhide\nhide-and-seek\nhideaway\nhidebound\nhideous\nhideously\nhideousness\nhideout\nhider\nhiding\nhie\nhieracium\nhierarch\nhierarchcal\nhierarchical\nhierarchically\nhierarchy\nhieratic\nhieroglyph\nhieroglyphic\nhieroglyphical\nhieroglyphically\nhierographa\nhieromancy\nhierophant\nhieroscopy\nhiggle\nhiggledy-piggledy\nhiggledypiggledy\nhiggler\nhigh\nhigh-backed\nhigh-ceilinged\nhigh-class\nhigh-fidelity\nhigh-flown\nhigh-handedly\nhigh-keyed\nhigh-level\nhigh-low\nhigh-mindedly\nhigh-mindedness\nhigh-muck-a-muck\nhigh-necked\nhigh-octane\nhigh-pitched\nhigh-powered\nhigh-principled\nhigh-resolution\nhigh-rise\nhigh-speed\nhigh-spiritedness\nhigh-stepped\nhigh-sudsing\nhigh-tech\nhigh-tension\nhigh-top\nhigh-voltage\nhighball\nhighbinder\nhighboard\nhighboy\nhighbrow\nhighchair\nhighcolored\nhigher\nhigher(a)\nhighest\nhighfalutin\nhighfaluting\nhighfed\nhighflier\nhighflown\nhighflying\nhighhanded\nhighjacker\nhighjacking\nhighland\nhighlander\nhighlands\nhighlight\nhighlow\nhighly\nhighlywrought\nhighmettled\nhighminded\nhighness\nhighpitched\nhighprincipled\nhighreaching\nhighroad\nhighsouled\nhighsounding\nhighspirited\nhighstrung\nhight\nhightasted\nhightoned\nhighway\nhighwayman\nhighways\nhighwrought\nhigi\nhijack\nhijacking\nhike\nhiker\nhilar\nhilarious\nhilariously\nhilarity\nhill\nhillbilly\nhillock\nhills\nhillside\nhilltop\nhilly\nhilt\nhilum\nhim\nhimalayan\nhimalayas\nhimalayish\nhimantoglossum\nhimantopus\nhimself\nhin\nhinan\nhinayana\nhinayanist\nhinc\nhind\nhindbrain\nhinder\nhindered\nhinderer\nhindering\nhindermost\nhindfoot\nhindgut\nhindi\nhindmost\nhindquarter\nhindquarters\nhindrance\nhindshank\nhindsight\nhindu\nhinduism\nhindustan\nhindustani\nhinge\nhinny\nhint\nhip\nhipbone\nhipflask\nhipless\nhipline\nhippeastrum\nhipped\nhippie\nhippobosca\nhippoboscidae\nhippocampus\nhippocastanaceae\nhippocrates\nhippocratic\nhippocrepis\nhippodamia\nhippodrome\nhippoglossoides\nhippoglossus\nhippolytus\nhippophagy\nhippopotamidae\nhippopotamus\nhipposideridae\nhipposideros\nhippotragus\nhipsurus\nhircine\nhirdygirdy\nhire\nhired\nhireling\nhiroshima\nhirsute\nhirsuteness\nhirudinea\nhirudinidae\nhirudo\nhirundinidae\nhirundo\nhis\nhispanic\nhispaniola\nhispaniolan\nhispid\nhiss\nhissing\nhist\nhistaminase\nhistamine\nhistidine\nhistiocyte\nhistiocytosis\nhistogram\nhistology\nhistone\nhistorian\nhistoric\nhistorical\nhistorically\nhistoricalness\nhistoriette\nhistoriographer\nhistoriography\nhistory\nhistrionem\nhistrionic\nhistrionics\nhit\nhit-and-run\nhit-and-run(a)\nhitch\nhitchhiker\nhitchiti\nhitchrack\nhither\nhitherto\nhitler\nhitlerian\nhitless\nhitting\nhittite\nhive\nhms\nho\nhoar\nhoard\nhoarder\nhoards\nhoariness\nhoarse\nhoarsely\nhoarseness\nhoary\nhoatzin\nhoax\nhob\nhobart\nhobble\nhobbledehoy\nhobby\nhobbyhorse\nhobbyist\nhobglobin\nhobgoblin\nhobnail\nhobnailed\nhobo\nhoboism\nhobsons\nhoc\nhock\nhockey\nhocus\nhocuspocus\nhod\nhoddydoddy\nhodgepodge\nhoe\nhoecake\nhoenir\nhog\nhogan\nhogchoker\nhogfish\nhoggish\nhogmanay\nhogs\nhogshead\nhogwash\nhohenlinden\nhohenzollern\nhoheria\nhoi\nhoist\nhoity\nhoitytoity\nhokan\nhokkaido\nholarrhena\nholbrookia\nholcus\nhold\nholder\nholdfast\nholding\nholdout\nholdover\nholds\nholdup\nhole\nholes\nholey\nholf\nholiday\nholidays\nholier-than-thou\nholies\nholiness\nholistic\nhollandaise\nholloa\nhollow\nhollowed\nhollowhearted\nhollowness\nhollowware\nholly\nhollyhock\nholmium\nholocaust\nholocene\nholocentridae\nholocentrus\nholocephalan\nholocephali\nhologram\nholograph\nholographic\nholography\nholometabolic\nholonym\nholonymy\nholophytic\nholothuria\nholothuridae\nholothuroidea\nholozoic\nholster\nholt\nholus\nholy\nholyday\nholystone\nhomage\nhomaridae\nhomarus\nhombre\nhome\nhome(a)\nhome-baked\nhome-brewed\nhome-cured\nhome-farm\nhome-loving\nhomebound\nhomeboy\nhomebuilder\nhomecoming\nhomefolk\nhomegirl\nhomegrown\nhomeless\nhomelessness\nhomelike\nhomeliness\nhomely\nhomemade\nhomemaker\nhomemaking\nhomeopath\nhomeopathic\nhomeopathy\nhomeowner\nhomer\nhomeric\nhomes\nhomesick\nhomesickness\nhomespun\nhomespun(p)\nhomestall\nhomestead\nhomestretch\nhometown\nhomeward\nhomework\nhomicidal\nhomicide\nhomiletic\nhomiletical\nhomiletics\nhomily\nhominal\nhominem\nhominemlat\nhomines\nhoming\nhomini\nhominian\nhominid\nhominidae\nhominine\nhominoid\nhominoidea\nhominy\nhomme\nhommes\nhomo\nhomobasidiomycetes\nhomocentric\nhomocercal\nhomocyclic\nhomoecious\nhomoeopathic\nhomoerotic\nhomogenate\nhomogeneity\nhomogeneous\nhomogeneously\nhomogeneousness\nhomogenesis\nhomogenization\nhomogenize\nhomogenized\nhomogeny\nhomograft\nhomograph\nhomogyne\nhomoiothermic\nhomoiousia\nhomoiousian\nhomologate\nhomologic\nhomologous\nhomologousof\nhomologue\nhomology\nhomolousian\nhomona\nhomonym\nhomonymous\nhomonymy\nhomoousia\nhomoousian\nhomophobia\nhomophone\nhomophonic\nhomophonous\nhomophony\nhomoptera\nhomosexual\nhomosexuality\nhomosporous\nhomospory\nhomozygous\nhomunculus\nhonduran\nhonduras\nhone\nhonest\nhonest-to-god\nhonesta\nhonestly\nhonesty\nhoney\nhoneybee\nhoneycomb\nhoneycombed\nhoneycreeper\nhoneydew\nhoneyed\nhoneyflower\nhoneylike\nhoneymoon\nhoneymouthed\nhoneypot\nhoneysuckle\nhoneytongued\nhoni\nhonied\nhonk\nhonker\nhonkytonk\nhonolulu\nhonor\nhonorabit\nhonorable\nhonorableness\nhonorably\nhonorarium\nhonorary\nhonored\nhonoree\nhonores\nhonoribus\nhonorific\nhonoring\nhonors\nhonos\nhonours\nhonshu\nhonte\nhooch\nhood\nhooded\nhoodlum\nhoodoo\nhoodooed\nhoodwink\nhoodwinked\nhoof\nhoofer\nhooflike\nhoofprint\nhook\nhook-nosed\nhookah\nhooked\nhooker\nhookey\nhooks\nhookup\nhookworm\nhooligan\nhoop\nhoopoe\nhoopskirt\nhoosegow\nhoosier\nhoot\nhoover\nhop\nhop-picker\nhope\nhopeful\nhopefully\nhopefulness\nhopegiving\nhopeless\nhopelessly\nhopelessness\nhopes\nhopi\nhoping\nhopomythumb\nhopped-up\nhopper\nhopple\nhopples\nhops\nhopsacking\nhopscotch\nhor\nhora\nhorace\nhorary\nhorde\nhordeum\nhorehound\nhorizon\nhorizontal\nhorizontality\nhorizontally\nhormonal\nhormone\nhorn\nhorn-rimmed\nhornbeam\nhornbill\nhornblende\nhorned\nhornel\nhorneophyton\nhornet\nhornets\nhornfels\nhorniness\nhornless\nhornmad\nhornpipe\nhorns\nhornwort\nhorny\nhorologe\nhorology\nhorometry\nhoroscope\nhoroscoppe\nhoroscopy\nhorrendum\nhorresco\nhorrible\nhorribly\nhorrid\nhorrida\nhorrific\nhorrified\nhorrify\nhorrifying\nhorrifyingly\nhorripilate\nhorripilation\nhorrisonous\nhorror\nhorrors\nhorrorstricken\nhorrorstrruck\nhors\nhorse\nhorse-and-buggy\nhorse-trail\nhorseback\nhorsebox\nhorsecar\nhorsecart\nhorsecloth\nhorsefly\nhorsehair\nhorsehide\nhorseleech\nhorseman\nhorsemanship\nhorsemeat\nhorsemint\nhorseplay\nhorsepond\nhorsepower\nhorsepower-hour\nhorseradish\nhorses\nhorseshoe\nhorseshow\nhorseson\nhorsetail\nhorseweed\nhorsewhip\nhorsewhipping\nhorsewoman\nhorst\nhorsy\nhortative\nhortatory\nhortensia\nhorticultural\nhorticulturally\nhorticulture\nhorticulturist\nhortus\nhorus\nhosanna\nhosannah\nhose\nhosea\nhosier\nhosiery\nhospes\nhosphor\nhospice\nhospitable\nhospitableness\nhospitably\nhospital\nhospitaler\nhospitality\nhospitalization\nhospodar\nhost\nhosta\nhostaceae\nhostage\nhostel\nhosteller\nhostelry\nhostess\nhostile\nhostilities\nhostility\nhostler\nhosts\nhot\nhot-blooded\nhotbed\nhotblooded\nhotbox\nhotbrained\nhotchpot\nhotchpotch\nhotdog\nhotei\nhotel\nhotel-casino\nhotelier\nhotfoot\nhoth\nhotheaded\nhothouse\nhotness\nhotpress\nhotspur\nhottentot\nhottonia\nhough\nhound\nhound's-tongue\nhounds\nhouppelande\nhour\nhourglass\nhouri\nhourly\nhours\nhourse\nhouse\nhouse-proud\nhouse-raising\nhouseboat\nhousebreaker\nhousebreaking\nhousebroken\nhousecraft\nhousedog\nhousefather\nhousefly\nhouseful\nhousehold\nhouseholder\nhousekeeper\nhousekeeping\nhouseless\nhouselights\nhousemaster\nhousemother\nhouseplant\nhouseroom\nhousetop\nhousetops\nhousewarming\nhousewife\nhousewifely\nhousewifery\nhousework\nhousewrecker\nhousing\nhouston\nhouttuynia\nhouyhnhnm\nhovea\nhovel\nhover\nhovercraft\nhovering\nhow\nhow-do-you-do\nhowbeit\nhowdah\nhowever\nhowitzer\nhowker\nhowl\nhowling\nhowsoever\nhoy\nhoya\nhoyden\nhoyden(a)\nhoydenish\nhoydenism\nhoyle\nhreath\nhte\nhuainaputina\nhuarache\nhuascaran\nhub\nhubble\nhubbly\nhubbub\nhubby\nhubcap\nhubris\nhuck\nhuckleberry\nhuckster\nhuddle\nhuddled\nhudibrastic\nhudson\nhudsonia\nhue\nhued(p)\nhueless\nhuff\nhuffily\nhuffiness\nhuffing\nhuffish\nhuffy\nhug\nhug-me-tight\nhuge\nhugeness\nhugger\nhugger-mugger\nhugo\nhugoesque\nhugueninia\nhuguenot\nhuh\nhuis\nhuisache\nhuissier\nhuitre\nhuke\nhukm\nhula\nhula-hoop\nhulk\nhulking\nhulks\nhulky\nhull\nhullabaloo\nhulsea\nhum\nhuman\nhuman-centered\nhumane\nhumanely\nhumaneness\nhumani\nhumaniores\nhumanism\nhumanist\nhumanistic\nhumanitarian\nhumanitarianism\nhumanities\nhumanity\nhumanization\nhumanize\nhumanly\nhumanness\nhumano\nhumanum\nhumble\nhumbled\nhumbleness\nhumbler\nhumbly\nhumbug\nhumdinger\nhumdrum\nhumect\nhumectate\nhumectation\nhumeri\nhumerus\nhumid\nhumidity\nhumilia\nhumiliate\nhumiliating\nhumiliatingly\nhumiliation\nhumility\nhummer\nhumming\nhummingbird\nhummock\nhummocky\nhumongous\nhumor\nhumoral\nhumorist\nhumorless\nhumorlessly\nhumorous\nhumorously\nhumorsome\nhump\nhumpback\nhumph\nhumphrey\nhumulus\nhumus\nhun\nhunc\nhunch\nhunched\nhunddred\nhundi\nhundred\nhundredfold\nhundreds\nhundredth\nhundredweight\nhundreth\nhung\nhungarian\nhungary\nhunger\nhungrily\nhungry\nhunk\nhunkpapa\nhunks\nhunnemannia\nhunt\nhunted\nhunter\nhunting\nhuntington\nhuntress\nhuntsman\nhupa\nhurdle\nhurdler\nhurdles\nhurdygurdy\nhurl\nhurler\nhurling\nhurlothrumbo\nhurlyburly\nhurrah\nhurricane\nhurried\nhurriedly\nhurry\nhurrying\nhurryskurry\nhurst\nhurt\nhurtful\nhurtfulness\nhurting\nhurtle\nhurtless\nhurtling\nhusband\nhusbandly\nhusbandman\nhusbandry\nhush\nhushed\nhushed-up\nhusk\nhuskiness\nhusking\nhuskingbee\nhusky\nhussar\nhussif\nhussy\nhustings\nhustle\nhustler\nhut\nhutch\nhutment\nhuxley\nhuxleyan\nhuzza\nhyacinth\nhyacinthaceae\nhyacinthoides\nhyades\nhyaenidae\nhyaline\nhyalinization\nhyaloid\nhyalophora\nhyalosperma\nhyalospongiae\nhybrid\nhybridization\nhybridoma\nhydnaceae\nhydnocarpus\nhydnum\nhydra\nhydrangea\nhydrangeaceae\nhydrant\nhydrargyrum\nhydras\nhydrastis\nhydrate\nhydration\nhydraulic\nhydraulically\nhydraulicostatics\nhydraulics\nhydrazine\nhydrazoite\nhydric\nhydride\nhydrilla\nhydrobates\nhydrobatidae\nhydrocarbon\nhydrocephalic\nhydrocephalus\nhydrocharis\nhydrocharitaceae\nhydrochloride\nhydrochlorothiazide\nhydrochoeridae\nhydrochoerus\nhydrocortisone\nhydrocyanic\nhydrodamalis\nhydrodynamic\nhydrodynamics\nhydroelectric\nhydroelectricity\nhydrofoil\nhydrogen\nhydrographer\nhydrographic\nhydrography\nhydrokinetic\nhydrology\nhydrolysate\nhydrolysis\nhydrolyzable\nhydromancy\nhydromantes\nhydromel\nhydrometer\nhydrometric\nhydrometry\nhydromyinae\nhydromys\nhydropathic\nhydropathy\nhydrophidae\nhydrophilic\nhydrophobia\nhydrophobic\nhydrophthalmus\nhydrophyllaceae\nhydrophyllum\nhydrophytic\nhydroplane\nhydroponics\nhydropot\nhydrosphere\nhydrostatic\nhydrostatics\nhydrous\nhydroxide\nhydroxyl\nhydroxymethyl\nhydroxyproline\nhydrozoa\nhydrozoan\nhydrus\nhyemal\nhyemoschus\nhyena\nhyetography\nhyetology\nhygeia\nhygeian\nhygiantics\nhygiastics\nhygiene\nhygienic\nhygienically\nhygre\nhygrocybe\nhygrodeik\nhygrometer\nhygrometry\nhygrophoraceae\nhygrophorus\nhygrophytic\nhygroscope\nhygroscopic\nhygrotrama\nhyla\nhylactophryne\nhyle\nhylidae\nhylobates\nhylobatidae\nhylocereus\nhylocichla\nhylophylax\nhylotheism\nhymen\nhymenaea\nhymenal\nhymeneal\nhymenium\nhymenogastrales\nhymenomycetes\nhymenophyllaceae\nhymenophyllum\nhymenoptera\nhymenopterous\nhymn\nhymnal\nhynerpeton\nhyoid\nhyoscyamus\nhypallage\nhype\nhypentelium\nhyperactive\nhyperactivity\nhyperbaton\nhyperbilirubinemia\nhyperbola\nhyperbole\nhyperbolic\nhyperbolical\nhyperbolically\nhyperbolize\nhyperboreal\nhyperborean\nhypercapnia\nhypercatalectic\nhypercellularity\nhypercritical\nhypercriticism\nhyperdulia\nhyperemia\nhyperemic\nhyperextension\nhyperfine\nhyperglycemia\nhypericaceae\nhypericism\nhypericum\nhyperion\nhyperlipemia\nhypermarket\nhypermastigina\nhypermastigote\nhypermedia\nhypermodern\nhypernym\nhypernymy\nhyperoglyphe\nhyperon\nhyperoodon\nhyperope\nhyperopia\nhyperopic\nhyperorthodoxy\nhyperphysical\nhyperplasia\nhyperpnea\nhyperpyrexia\nhypersensitivity\nhypersomnia\nhypertensive\nhypertext\nhyperthermal\nhyperthermia\nhyperthyroidism\nhypertonic\nhypertrophied\nhypertrophy\nhypervelocity\nhyperventilation\nhypervitaminosis\nhypha\nhyphantria\nhyphen\nhyphenated\nhyphenation\nhypnology\nhypnophobia\nhypnosis\nhypnotic\nhypnotism\nhypnotist\nhypo\nhypoactive\nhypobasidium\nhypobetalipoproteinemia\nhypocapnia\nhypocaust\nhypocellularity\nhypochaeris\nhypochlorite\nhypochondria\nhypochondriac\nhypochondriasis\nhypochondriassis\nhypocreaceae\nhypocreales\nhypocrisy\nhypocrite\nhypocritical\nhypocritically\nhypocycloid\nhypoderma\nhypodermal\nhypodermic\nhypodermis\nhypogammaglobulinemia\nhypoglossal\nhypoglycemia\nhypohondriacal\nhyponym\nhyponymy\nhypopachus\nhypophyseal\nhypophysectomized\nhypopitys\nhypoplasia\nhypostasis\nhypostatic\nhypostatization\nhypotension\nhypotensive\nhypotenuse\nhypothalamic\nhypothalamically\nhypothalamus\nhypothecate\nhypothecation\nhypothenuse\nhypothermia\nhypothermic\nhypothesi\nhypothesis\nhypothetical\nhypothetically\nhypothyroidism\nhypotonic\nhypovolemia\nhypovolemic\nhypoxia\nhypoxidaceae\nhypoxis\nhypozeugma\nhypozeuxis\nhypped\nhyppish\nhypsiglena\nhypsiprymnodon\nhypsometry\nhyracoidea\nhyracotherium\nhyrax\nhyrcynian\nhysique\nhyssop\nhyssopus\nhysterectomy\nhysteria\nhysteric\nhysterical\nhysterically\nhysterics\nhysterocatalepsy\nhysteron\nhystricidae\nhystricomorpha\ni\ni-beam\ni.e.\niamb\niambic\niapetus\niberian\niberis\nibero-mesornis\nibex\nibi\nibid.\nibis\nibsen\nibsenian\nibuprofen\nicarus\niccusion\nice\nice-clogged\nice-free\nice-skater\nicebag\niceberg\niceboat\nicebound\nicebox\nicebreaker\nicecap\nicefall\nicefloe\nicehouse\niceland\nicelander\nicelandic\nicelandic-speaking\niceman\nicenot\nicepail\nicepick\nicetray\nicewagon\nich\nichabod\nichiban\nichiro\nichneumon\nichneumonidae\nichnography\nichor\nichorous\nichthy\nichthycolla\nichthyivorous\nichthyocol\nichthyolatry\nichthyologist\nichthyology\nichthyomancy\nichthyophagy\nichthyosaur\nichthyosauria\nichthyosauridae\nichthyosaurus\nichthyotomy\nichyostega\nicicle\nicily\nicing\nicky\nicon\niconic\niconoclasm\niconoclast\niconoclastic\niconography\niconolatry\niconoscope\nicosahedral\nicosahedron\nictalurus\nicteria\nicteridae\nicterus\nictiobus\nictodosaur\nictodosauria\nictonyx\nictu\nictus\nicy\nid\nidaho\nidahoan\nidas\nide\nidea\nideal\nidealism\nidealist\nideality\nidealization\nidealize\nidealized\nideally\nideas\nideation\nideawake\nidemnity\nidentical\nidentically\nidentifiable\nidentifiably\nidentification\nidentified\nidentify\nidentikit\nidentity\nideogram\nideograph\nideographic\nideographically\nideography\nideological\nideologically\nideologist\nideology\nideophone\nides\nidesia\nidicate\nidiocrasy\nidiocy\nidiolatry\nidiolect\nidiom\nidiomatic\nidiomatically\nidiosyncrasy\nidiosyncratic\nidiot\nidiotic\nidiotically\nidiotism\nidiotypical\nidle\nidleness\nidler\nidly\nido\nidocy\nidol\nidolater\nidolator\nidolatress\nidolatrous\nidolatrously\nidolatry\nidolism\nidolization\nidolize\nidoloclast\nidols\nidonea\nidoneous\nidotism\nidun\nidyl\nidyll\nidyllic\nidyllically\nie\nies\nievidelicet\nif\nigigi\nigloo\niglu\nignara\nignavus\nigneous\nignescent\nignis\nignite\nignited\nigniter\nignition\nignobile\nignoble\nignobleness\nignominious\nignominy\nignoramus\nignorance\nignorant\nignorantc\nignorantia\nignorantly\nignorantness\nignoratio\nignore\nignored\nignoscito\nignotius\nignotum\niguanid\niguanidae\niguanodon\niguanodontidae\niguazu\nii\niii\niiip\nij\nil\nilama\nilang-ilang\nile-de-france\nile-st-louis\nileum\nilex\niliac\niliad\niliamna\nilium\nilk\nill\nill-advised\nill-being\nill-bred\nill-conceived\nill-considered\nill-defined\nill-equipped\nill-famed\nill-favored\nill-fed\nill-mannered\nill-natured\nill-proportioned\nill-sorted\nill-timed(a)\nilla\nilladvised\nillae\nillaffected\nillampu\nillapse\nillaqueate\nillassorted\nillation\nillative\nillaudable\nillbehaved\nillboding\nillbred\nillbreeding\nillconditioned\nillconducted\nillcontrived\nilldefined\nilldevised\nilldigested\nilldisposed\nillecebrum\nillegal\nillegality\nillegally\nillegibility\nillegible\nillegibly\nillegitimacy\nillegitimate\nillegitimately\nillfated\nillflavored\nillformed\nillfurnished\nillhumored\nilliberal\nilliberality\nillicit\nillicitness\nillicium\nillimagined\nillimani\nillimitable\nillimited\nillinois\nillinoisan\nillis\nilliteracy\nilliterate\nilljudged\nilljudging\nillluck\nillmannered\nillmarked\nillnatured\nillness\nilloff\nillogical\nillogicality\nillogically\nillomened\nillprovided\nillqualified\nills\nillsorted\nillspent\nillstarred\nilltempered\nilltimed\nilltreat\nilltreatment\nillume\nilluminance\nilluminant\nilluminate\nilluminated\nilluminati\nilluminating\nillumination\nilluminations\nillumine\nilluse\nillused\nillusion\nillusional\nillusive\nillusory\nillustrate\nillustrated\nillustration\nillustrative\nillustrator\nillustrious\nillustriously\nillustriousness\nillwill\nillyria\nillyrian\nilmenite\nils\nim\nimagainative\nimage\nimagery\nimages\nimaginaire\nimaginary\nimagination\nimaginative\nimaginatively\nimagine\nimagined\nimaging\nimagining\nimagism\nimago\nimagry\nimam\nimaret\nimaum\nimbalance\nimbecile\nimbecility\nimbed\nimbedded\nimbelle\nimbeu\nimbibation\nimbibe\nimbibition\nimbrangle\nimbreu\nimbricate\nimbricated\nimbrication\nimbroglio\nimbrue\nimbue\nimbued\nimbued(p)\nimburse\nimcompleteness\nimcompressibility\nimidazole\nimide\nimipramine\nimitable\nimitate\nimitated\nimitation\nimitative\nimitator\nimmaculale\nimmaculate\nimmaculately\nimmanence\nimmanent\nimmanity\nimmanuel\nimmaterial\nimmaterialism\nimmateriality\nimmaterialness\nimmateriate\nimmature\nimmaturely\nimmaturity\nimmeasurable\nimmeasurably\nimmediacy\nimmediate\nimmediately\nimmediateness\nimmedicabile\nimmedicable\nimmelodious\nimmemorial\nimmemorial(ip)\nimmense\nimmensity\nimmensum\nimmerge\nimmerse\nimmersed\nimmersion\nimmesh\nimmethodical\nimmigrant\nimmigration\nimminence\nimminent\nimminently\nimmiscibility\nimmiscible\nimmission\nimmitigable\nimmix\nimmobile\nimmobility\nimmobilization\nimmoderate\nimmoderately\nimmoderation\nimmodest\nimmodestly\nimmodesty\nimmolate\nimmolation\nimmoral\nimmorality\nimmorally\nimmortal\nimmortality\nimmortalize\nimmortelle\nimmotile\nimmotility\nimmovability\nimmovable\nimmovably\nimmserion\nimmundicity\nimmundity\nimmune\nimmunity\nimmunization\nimmunized\nimmunizing\nimmunoelectrophoresis\nimmunofluorescence\nimmunogen\nimmunoglobulin\nimmunological\nimmunologist\nimmunology\nimmunopathology\nimmunosuppressant\nimmunosuppression\nimmunosuppressive\nimmure\nimmutability\nimmutable\nimo\nimp\nimpact\nimpacted\nimpaction\nimpair\nimpaired\nimpairment\nimpala\nimpale\nimpalement\nimpalpable\nimpanation\nimparadise\nimparity\nimparo\nimpart\nimpartial\nimpartiality\nimpartially\nimpassable\nimpassibility\nimpassible\nimpassiblenesss\nimpassiblity\nimpassion\nimpassionable\nimpassioned\nimpassive\nimpassively\nimpasto\nimpatience\nimpatient\nimpatient(p)\nimpatiently\nimpawn\nimpeach\nimpeachability\nimpeachment\nimpeccability\nimpeccable\nimpeccably\nimpeccancy\nimpeccant\nimpecuniosity\nimpecunious\nimpecuniousness\nimpede\nimpeded\nimpedient\nimpediment\nimpedimenta\nimpedimentary\nimpediments\nimpedite\nimpedition\nimpeditive\nimpel\nimpelled\nimpellent\nimpeller\nimpelling\nimpend\nimpendere\nimpending\nimpenetrability\nimpenetrable\nimpenitence\nimpenitent\nimpenitently\nimperative\nimperatively\nimperativeness\nimperator\nimperceptibility\nimperceptible\nimperceptibly\nimperceptility\nimpercipient\nimperdible\nimperfect\nimperfectibility\nimperfection\nimperfective\nimperfectly\nimperfectness\nimperforate\nimperforation\nimperial\nimperialism\nimperialist\nimperialistic\nimperially\nimperiatorial\nimperil\nimperio\nimperious\nimperiously\nimperiousness\nimperishability\nimperishable\nimperium\nimpermanence\nimpermanent\nimpermeability\nimpermeable\nimpermissibility\nimpermissible\nimpermissibly\nimpersonal\nimpersonally\nimpersonate\nimpersonation\nimpersonator\nimperspicuity\nimpertinence\nimpertinent\nimpertinently\nimpertubation\nimperturbability\nimperturbable\nimperturbation\nimpervious\nimperviousness\nimpetiginous\nimpetigo\nimpetrate\nimpetration\nimpetuosity\nimpetuous\nimpetuously\nimpetuousness\nimpetus\nimpiety\nimpignorate\nimpinge\nimpingement\nimpious\nimpiously\nimpish\nimpishly\nimpishness\nimpissation\nimplacability\nimplacable\nimplant\nimplantation\nimplanted\nimplausibility\nimplausible\nimplead\nimplement\nimplemental\nimplementation\nimpletion\nimplex\nimplicate\nimplicated\nimplication\nimplicational\nimplicit\nimplicitly\nimplicitness\nimplied\nimploration\nimplore\nimplosion\nimply\nimplying\nimpolicy\nimpolite\nimpolitely\nimpoliteness\nimpolitic\nimponderability\nimponderable\nimponderous\nimporosity\nimporous\nimport\nimportance\nimportant\nimportant-looking\nimportantly\nimportation\nimported\nimporter\nimporting\nimportunate\nimportune\nimportunity\nimpose\nimposed\nimposing\nimpositae\nimposition\nimpossibile\nimpossibilities\nimpossibility\nimpossible\nimpossibly\nimpost\nimposter\nimposthume\nimposture\nimpotence\nimpotent\nimpound\nimpoundment\nimpoverish\nimpracticability\nimpracticable\nimpracticably\nimpractical\nimpracticality\nimprecate\nimprecation\nimprecise\nimprecisely\nimpreciseness\nimpregnability\nimpregnable\nimpregnably\nimpregnate\nimpregnated\nimpregnation\nimpresario\nimprescriptible\nimpress\nimpressed\nimpressed(p)\nimpressibility\nimpressible\nimpression\nimpressionable\nimpressionism\nimpressionist\nimpressive\nimpressively\nimpressiveness\nimprimis\nimprimit\nimprint\nimprinting\nimprison\nimprisoned\nimprisonment\nimprobability\nimprobable\nimprobate\nimprobation\nimprobity\nimpromptu\nimproper\nimproperly\nimpropriate\nimpropriation\nimpropriator\nimpropriety\nimprosperous\nimprovable\nimprove\nimproved\nimprovement\nimprovements\nimprovidence\nimprovident\nimprovidently\nimproving\nimprovisate\nimprovisation\nimprovisatore\nimprovisatory\nimprovise\nimprovised\nimproviso\nimprudence\nimprudent\nimprudently\nimpudence\nimpudent\nimpudicity\nimpugn\nimpugnable\nimpugnation\nimpuissance\nimpulse\nimpulsion\nimpulsive\nimpulsiveness\nimpune\nimpunity\nimpure\nimpurity\nimpuslive\nimputable\nimputation\nimputative\nimpute\nimputrescible\nin\nin(a)\nin(p)\nin-basket\nin-between\nin-bounds\nin-chief(ip)\nin-fighting\nin-law\nin-person(a)\nin-situ\ninabeyance\ninability\ninabstinence\ninaccessibility\ninaccessible\ninaccessibly\ninaccuracy\ninaccurate\ninaccurately\ninachis\ninaction\ninactivate\ninactive\ninactively\ninactiveness\ninactivity\ninadequacy\ninadequate\ninadequately\ninadequateness\ninadmissibility\ninadmissible\ninadvertence\ninadvertency\ninadvertent\ninadvertently\ninadvisability\ninadvisable\ninaesthetic\ninaffable\ninalienable\ninalienably\ninamorata\ninamorato\ninane\ninani\ninanimate\ninanimateness\ninanition\ninanity\ninanna\ninappetency\ninapplicability\ninapplicable\ninapposite\ninappreciable\ninapprehensible\ninappropriate\ninappropriately\ninappropriateness\ninapt\ninaptitude\ninaptness\ninarguable\ninarticulate\ninarticulately\ninarticulateness\ninartificial\ninartistic\ninasmuch\ninattention\ninattentive\ninattentively\ninattentiveness\ninaudibility\ninaudible\ninaudibleness\ninaudibly\ninaugural\ninaugurally\ninaugurate\ninauguration\ninauspicious\ninauspiciously\ninauspiciousness\ninauthentic\ninbeing\ninboard\ninborn\ninbred\ninbreeding\ninca\nincage\nincalculable\nincalculably\nincalescence\nincalescent\nincandescence\nincandescent\nincantation\nincantatory\nincapability\nincapable\nincapable(p)\nincapacious\nincapacitate\nincapacity\nincarcerate\nincarceration\nincarnadine\nincarnate\nincarnation\nincase\nincaution\nincautious\nincautiously\nincendiarism\nincendiary\nincendium\nincense\nincensebreathing\nincension\nincentive\nincept\ninception\ninceptive\nincepto\ninceptor\nincertitude\nincessant\nincessantly\nincest\nincestuous\nincestuously\ninch\ninches\ninchoate\ninchoation\ninchoative\ninchon\nincide\nincidence\nincident\nincidental\nincidentally\nincidit\nincinerate\nincineration\nincinerator\nincipience\nincipiency\nincipient\nincircumspect\nincise\nincised\nincision\nincisive\nincisively\nincisiveness\nincisor\nincitation\nincite\nincitement\nincivility\nincivism\ninclasp\ninclemency\ninclement\ninclination\nincline\ninclined\ninclined(p)\ninclines\ninclinometer\ninclose\ninclosed\ninclosure\ninclude\nincluded\nincluding\ninclusion\ninclusive\nincogitable\nincogitancy\nincognita\nincognito\nincognito(p)\nincognizable\nincognizant\nincoherence\nincoherent\nincoherently\nincombustibility\nincombustible\nincombustibleness\nincome\nincoming\nincomings\nincommensurability\nincommensurable\nincommensurate\nincommode\nincommodious\nincommunicable\nincommunicado\nincommutability\nincommutable\nincomparable\nincomparably\nincompassionate\nincompassionateness\nincompatibility\nincompatible\nincompatibly\nincompentency\nincompetence\nincompetent\nincompetently\nincomplete\nincompletely\nincompleteness\nincomplex\nincompliance\nincomprehensibility\nincomprehensible\nincomprehension\nincompressed\nincompressibility\nincompressible\nincomputable\ninconcealable\ninconceivability\ninconceivable\ninconceivableness\ninconceivably\ninconceptible\ninconcinnity\ninconclusive\ninconclusively\ninconclusiveness\ninconcoction\nincondite\ninconel\nincongruence\nincongruent\nincongruity\nincongruous\nincongruously\ninconnection\ninconsequence\ninconsequent\ninconsequential\ninconsequentially\ninconsiderable\ninconsiderate\ninconsiderately\ninconsiderateness\ninconsideration\ninconsistency\ninconsistent\ninconsistently\ninconsolable\ninconsonant\ninconspicuous\ninconspicuously\ninconspicuousness\ninconstancy\ninconstant\nincontestable\nincontiguous\nincontinence\nincontinent\nincontinently\nincontrollable\nincontrovertibility\nincontrovertible\ninconvenience\ninconvenient\ninconvenienti\ninconveniently\ninconversable\ninconvertibility\ninconvertible\ninconvincible\nincoordination\nincorporal\nincorporate\nincorporated\nincorporation\nincorporative\nincorporeal\nincorporeity\nincorrect\nincorrectly\nincorrectness\nincorrigible\nincorrupibility\nincorrupt\nincorrupta\nincorruptibility\nincorruptible\nincorruption\nincorruptness\nincrassate\nincrassation\nincrease\nincreased\nincreasing\nincreasingly\nincredibility\nincredible\nincredibleness\nincredibly\nincredulity\nincredulous\nincredulously\nincredulousness\nincrement\nincremental\nincrepation\nincriminate\nincriminatingly\nincrimination\nincrust\nincrustation\nincubation\nincubator\nincubus\ninculcate\ninculcated\ninculcation\ninculpable\ninculpate\ninculpation\ninculpatory\ninculture\nincumbency\nincumbent\nincumber\nincumbered\nincumbrance\nincunabilis\nincunabula\nincur\nincurability\nincurable\nincurably\nincuriam\nincuriosi\nincuriosity\nincurious\nincuriousness\nincurrence\nincurring\nincursion\nincursive\nincurvate\nincurvation\nincurvature\nincurvity\nincus\nindagation\nindebted\nindebted(p)\nindebtedness\nindebtment\nindecency\nindecent\nindecently\nindeciduous\nindecipherable\nindecision\nindecisive\nindecisively\nindecisiveness\nindeclinable\nindecorous\nindecorously\nindecorum\nindeed\nindefatigability\nindefatigable\nindefatigableness\nindefatigably\nindefatigation\nindefeasible\nindefectibility\nindefectible\nindefective\nindefensible\nindeficient\nindefinable\nindefinite\nindefinitely\nindefiniteness\nindehiscent\nindeliberate\nindelible\nindelibly\nindelicacy\nindelicate\nindemnification\nindemnify\nindemnity\nindene\nindent\nindentation\nindenture\nindependence\nindependent\nindependently\nindescribable\nindesinent\nindestructibility\nindestructible\nindeterminable\nindeterminably\nindeterminate\nindetermination\nindevotion\nindevout\nindex\nindexation\nindexer\nindexical\nindexing\nindexless\nindexterity\nindia\nindiaman\nindian\nindiana\nindianan\nindianapolis\nindianyellow\nindic\nindicate\nindicated\nindicating\nindication\nindicative\nindicator\nindicatoridae\nindicatory\nindice\nindices\nindicolite\nindict\nindiction\nindictment\nindifference\nindifferent\nindifferently\nindigence\nindigene\nindigenous\nindigenousness\nindigent\nindigestaque\nindigested\nindigestibility\nindigestible\nindigestion\nindigitate\nindign\nindignant\nindignantly\nindignation\nindignity\nindigo\nindigofera\nindiligence\nindirect\nindirection\nindirectly\nindirectness\nindiscernible\nindiscerptibility\nindiscerptible\nindiscipline\nindiscoverable\nindiscreet\nindiscreetly\nindiscrete\nindiscretion\nindiscriminate\nindiscrimination\nindisolvableness\nindispensability\nindispensable\nindispose\nindisposed\nindisposedness\nindisposition\nindisputability\nindisputable\nindissoluble\nindissolvable\nindistinct\nindistinction\nindistinctness\nindistinguishable\nindisturbance\nindite\nindium\nindividual\nindividual(a)\nindividualism\nindividualist\nindividualistic\nindividualistically\nindividuality\nindividualization\nindividualize\nindividualized\nindividually\nindividuity\nindivisibility\nindivisible\nindo-european\nindo-iranian\nindochina\nindocile\nindocility\nindoctrinate\nindoctrination\nindolence\nindolent\nindolently\nindomethacin\nindomitability\nindomitable\nindonesia\nindonesian\nindoor\nindoor(a)\nindorse\nindorsement\nindra\nindraught\nindrawn\nindri\nindriidae\nindubious\nindubitable\nindubitableness\nindubitably\ninduce\ninduced\ninducement\ninduct\ninductance\ninduction\ninductive\ninductor\nindue\nindulge\nindulged\nindulgence\nindulgency\nindulgent\nindulgently\nindurate\nindurated\ninduration\nindus\nindusium\nindustrial\nindustrialism\nindustrialist\nindustrialization\nindustrialized\nindustrially\nindustrious\nindustriously\nindustry\nindweller\nindwelling\ninebriate\ninebriated\ninebriates\ninebriation\ninebriety\ninebrious\ninedible\nineffable\nineffably\nineffaceable\nineffective\nineffectiveness\nineffectual\nineffectually\ninefficacious\ninefficaciously\ninefficaciousness\ninefficacy\ninefficiency\ninefficiencyc\ninefficient\ninefficiently\ninelaborate\ninelastic\ninelasticity\ninelegance\ninelegant\ninelegantly\nineligibility\nineligible\nineloquently\nineluctable\ninept\nineptitude\nineptly\ninequality\ninequation\ninequitable\ninequitably\nineradicable\ninerclude\ninerrable\ninerrancy\ninert\ninertia\ninertiae\ninertial\ninertioe\ninertion\ninertness\ninescapably\ninessential\ninessentiality\ninest\ninestimable\ninevitability\ninevitable\ninevitableness\ninevitably\ninexact\ninexactness\ninexcitability\ninexcitable\ninexcusable\ninexcusably\ninexecution\ninexhaustible\ninexistence\ninexistent\ninexorable\ninexorably\ninexpectant\ninexpectation\ninexpedience\ninexpediency\ninexpedient\ninexpediently\ninexpensive\ninexpensiveness\ninexperience\ninexperienced\ninexpert\ninexpiable\ninexplicable\ninexplicitness\ninexpressible\ninexpressibles\ninexpressile\ninexpression\ninexpressive\ninexpressively\ninexpugnable\ninexpungible\ninextension\ninexterminable\ninextinguishable\ninextricable\ninextricably\ninfallibility\ninfallible\ninfallibleness\ninfamous\ninfamy\ninfancy\ninfandum\ninfant\ninfanta\ninfanticde\ninfanticide\ninfantile\ninfantilism\ninfantine\ninfantry\ninfantrym\ninfantryman\ninfarct\ninfarction\ninfare\ninfatuate\ninfatuated\ninfatuation\ninfaustus\ninfeasibility\ninfeasible\ninfect\ninfecta\ninfection\ninfectious\ninfective\ninfecund\ninfecundity\ninfelicitous\ninfelicitously\ninfelicity\ninfer\ninference\ninferential\ninferior\ninferiority\ninfernal\ninfernally\ninferno\ninfertile\ninfertility\ninfest\ninfestation\ninfested\ninfestivity\ninfibulation\ninfidel\ninfidelity\ninfielder\ninfiltrate\ninfiltration\ninfiltrator\ninfinitam\ninfinite\ninfinitely\ninfiniteness\ninfinitesimal\ninfinitival\ninfinitive\ninfinitude\ninfinitum\ninfinity\ninfirm\ninfirmary\ninfirmity\ninfix\ninflame\ninflamed\ninflammability\ninflammable\ninflammation\ninflammatory\ninflatable\ninflate\ninflated\ninflater\ninflation\ninflationary\ninflect\ninflected\ninflection\ninflectional\ninflexibility\ninflexible\ninflexibly\ninflexion\ninflexions\ninflict\ninfliction\ninflictive\ninflorescence\ninflow\ninflowing\ninfluence\ninfluential\ninfluentially\ninfluenza\ninflux\ninfomercial\ninform\ninformal\ninformality\ninformally\ninformant\ninformation\ninformative\ninformatively\ninforme\ninformed\ninformer\ninforming\ninformity\ninfra\ninfraction\ninfrahuman\ninfrangible\ninfrared\ninfrasonic\ninfrastructure\ninfreqent\ninfrequency\ninfrequent\ninfrequently\ninfrigidation\ninfringe\ninfringement\ninfumate\ninfundibul\ninfundibular\ninfundibuliform\ninfuriate\ninfuriation\ninfuscate\ninfuscation\ninfuse\ninfused\ninfusible\ninfusion\ninfusoria\ninfusorian\ning\ninga\ningannation\ningate\ningathering\ningeminate\ningemination\ningenerate\ningeniosa\ningenious\ningeniously\ningenite\ningenium\ningens\ningenu\ningenue\ningenuity\ningenuous\ningenuousness\ningest\ningesta\ningested\ningestion\ningle\ninglese\ningleside\ninglorious\ningloriousness\ningot\ningraft\ningrafted\ningrain\ningrained\ningrate\ningratiate\ningratiating\ningratiatingly\ningratitude\ningredient\ningress\ningression\ningrian\ningrowing\ningrowth\ninguishable\ningulf\ningurgitate\ningurgitation\ningustible\ninhabile\ninhabit\ninhabitancy\ninhabitant\ninhabitants\ninhabited\ninhabiting\ninhalant\ninhalation\ninhale\ninhaled\ninhaler\ninhaling\ninharmonious\ninhere\ninherence\ninherent\ninherentessential\ninherently\ninherit\ninheritable\ninheritance\ninherited\ninheriting\ninheritor\ninheritress\ninheritrix\ninhesion\ninhibit\ninhibited\ninhibiting\ninhibition\ninhibitor\ninhomogeneous\ninhospitable\ninhospitableness\ninhospitably\ninhospitality\ninhuman\ninhumane\ninhumanely\ninhumaneness\ninhumanity\ninhumanum\ninhumation\ninhume\ninimaginable\ninimical\ninimicorum\ninimicum\ninimitable\ninimitably\ninipar\niniquitous\niniquitously\niniquity\ninirritability\ninit\ninitial\ninitially\ninitiate\ninitiated\ninitiation\ninitiative\ninitiatory\ninitio\ninject\ninjectable\ninjection\ninjudicial\ninjudicious\ninjudiciously\ninjudiciousness\ninjunction\ninjure\ninjured\ninjuria\ninjurious\ninjuriously\ninjury\ninjustice\nink\nink-black\ninkberry\ninkle\ninkling\ninkstand\ninkwell\ninky\ninlaid\ninland\ninlay\ninlayd\ninlet\ninly\ninmate\ninmost\ninn\ninnate\ninnately\ninnavigable\ninnefficient\ninner\ninner(a)\ninnermost\ninning\ninnings\ninnins\ninnkeeper\ninnocence\ninnocent\ninnocently\ninnocuous\ninnominate\ninnovate\ninnovation\ninnovative\ninnovativeness\ninnoxious\ninnuendo\ninnumerable\ninnumerableness\ninnumerate\ninnutritious\ninobjectionable\ninobservance\ninoccupation\ninoculate\ninoculated\ninoculating\ninoculation\ninodorate\ninodorous\ninodorousness\ninoffensive\ninoffensively\ninoffice\ninofficious\ninoperable\ninoperative\ninopioe\ninopportune\ninopportunely\ninopportuneness\ninordinate\ninordinately\ninorganic\ninorganically\ninorganization\ninorganized\ninornate\ninosculate\ninosculation\ninositol\ninpatient\ninpersuasible\ninpervious\ninposture\ninpouring\ninquest\ninquietude\ninquinat\ninquinate\ninquination\ninquire\ninquirendum\ninquirer\ninquiring\ninquiringly\ninquiry\ninquisition\ninquisitive\ninquisitiveness\ninquisitor\ninquisitorial\ninquisitory\ninroad\ninrollment\nins\ninsalubrious\ninsalubrity\ninsane\ninsanely\ninsanire\ninsanity\ninsatiable\ninsatiably\ninsatiate\ninscribe\ninscribed\ninscription\ninscroll\ninscrutability\ninscrutable\ninsculpture\ninsculptured\ninsecable\ninsect\ninsecta\ninsectan\ninsecticide\ninsectifuge\ninsectivora\ninsectivore\ninsectivorous\ninsecure\ninsecurely\ninsecureness\ninsecurity\ninsemination\ninsensate\ninsensibility\ninsensible\ninsensible(p)\ninsensibleness\ninsensibly\ninsensitive\ninsensitively\ninsensitivity\ninsentience\ninsentient\ninseparability\ninseparable\ninseparableness\ninseparably\ninsert\ninserted\ninsertion\ninservient\ninsessorial\ninset\ninseverable\ninshore\ninside\ninside(a)\ninsider\ninsidious\ninsidiously\ninsidiousness\ninsight\ninsightful\ninsightfulness\ninsignia\ninsignificance\ninsignificant\ninsignificantly\ninsincere\ninsincerely\ninsincerity\ninsinglass\ninsinuate\ninsinuatingly\ninsinuation\ninsipid\ninsipidity\ninsipidly\ninsist\ninsistence\ninsistent\ninsistently\ninsobriety\ninsofar\ninsolation\ninsole\ninsolence\ninsolent\ninsolently\ninsolubility\ninsoluble\ninsolvable\ninsolvency\ninsolvent\ninsomnia\ninsomniac\ninsomnium\ninsomuch\ninsouciance\ninsouciant\ninspan\ninspecite\ninspect\ninspecting\ninspection\ninspector\ninspectorate\ninspectorship\ninspicit\ninspiration\ninspirational\ninspirationally\ninspiratory\ninspird\ninspire\ninspired\ninspiring\ninspirit\ninspiriting\ninspissate\ninspissation\ninstability\ninstall\ninstallation\ninstallment\ninstallmentsby\ninstance\ninstant\ninstantaneity\ninstantaneous\ninstantaneously\ninstantaneousness\ninstanter\ninstar\ninstauration\ninstead\ninstep\ninstigate\ninstigation\ninstigator\ninstil\ninstill\ninstillation\ninstillator\ninstinct\ninstinctive\ninstinctively\ninstitute\ninstitution\ninstitutional\ninstitutionalized\ninstitutionally\ninstitutions\ninstitutor\ninstrmentality\ninstroke\ninstruct\ninstructed\ninstruction\ninstructional\ninstructions\ninstructive\ninstructor\ninstructorship\ninstructress\ninstrument\ninstrumental\ninstrumentalist\ninstrumentality\ninstrumentation\ninstruments\ninsuavity\ninsubordinate\ninsubordination\ninsubstantial\ninsubstantiality\ninsubstantially\ninsufferable\ninsufficiency\ninsufficient\ninsufficiently\ninsufflation\ninsular\ninsularity\ninsulate\ninsulation\ninsulator\ninsulin\ninsulse\ninsult\ninsulting\ninsultingly\ninsults\ninsuperable\ninsuperably\ninsupportable\ninsuppressible\ninsurance\ninsure\ninsured\ninsurgency\ninsurgent\ninsurmountable\ninsurrection\ninsurrectional\ninsusceptible\ninsuspense\nint\nintact\nintactness\nintaglio\nintake\nintangibility\nintangible\ninteger\nintegral\nintegrally\nintegrant\nintegrate\nintegrated\nintegration\nintegrative\nintegrator\nintegrity\nintegument\nintellect\nintellection\nintellectual\nintellectuality\nintellectually\nintelleet\nintelleetual\nintelligence\nintelligencer\nintelligent\nintelligently\nintelligentsia\nintelligibility\nintelligible\nintelligibly\nintemperance\nintemperate\nintemperateinabstinent\nintempestivity\nintend\nintendant\nintended\nintending\nintense\nintensely\nintensification\nintensified\nintensifier\nintensify\nintensifying\nintensional\nintensity\nintensive\nintensively\nintent\nintention\nintentional\nintentionality\nintentionally\nintentioned\nintentions\nintentiveness\nintently\nintentness\nintents\ninter\ninteraction\ninteractional\ninteral\ninterbred\nintercalary\nintercalate\nintercalation\nintercede\nintercellular\nintercept\ninterception\ninterceptor\nintercession\nintercessor\nintercessory\ninterchange\ninterchangeability\ninterchangeable\ninterchangeableness\ninterchangeably\ninterchanged\ninterchurch\nintercipient\ninterclusion\nintercollegiate\nintercommunication\nintercommunion\nintercommunity\ninterconnected\ninterconnection\nintercontinental\nintercostal\nintercourse\nintercurrence\nintercurrent\ninterdepartmental\ninterdependence\ninterdependent\ninterdict\ninterdiction\ninterdigitate\ninterdigitation\ninterdisciplinary\ninterdum\ninterest\ninterested\ninterestedness\ninteresting\ninterestingly\ninterface\ninterfacial\ninterfaith\ninterfere\ninterference\ninterfering\ninterferometer\ninterferon\nintergalactic\ninterim\ninterior\ninteriority\ninterjacence\ninterjacent\ninterject\ninterjection\ninterlace\ninterlacing\ninterland\ninterlanguage\ninterlard\ninterlarding\ninterlayer\ninterleave\ninterline\ninterlinear\ninterlineation\ninterlingua\ninterlink\ninterlobular\ninterlocation\ninterlocution\ninterlocutor\ninterlocutory\ninterloper\ninterlude\nintermarriage\nintermeddle\nintermeddler\nintermeddling\nintermediary\nintermediate\nintermediate(a)\nintermediately\nintermedium\ninterment\nintermezzo\nintermigration\ninterminability\ninterminable\ninterminably\nintermingle\nintermission\nintermit\nintermittence\nintermittent\nintermittently\nintermitting\nintermix\nintermixture\nintermolecular\nintermural\nintermutation\nintern\ninternal\ninternalization\ninternally\ninternational\ninternationale\ninternationalism\ninternationalist\ninternationality\ninternationalization\ninternationally\ninternecine\ninternee\ninternet\ninternist\ninternment\ninternship\ninternuncio\ninteroception\ninteroceptive\ninterpel\ninterpellation\ninterpenetrate\ninterpenetration\ninterpersonal\ninterphone\ninterplanetary\ninterplay\ninterpolate\ninterpolation\ninterpose\ninterposit\ninterposition\ninterpret\ninterpretation\ninterpretative\ninterpreted\ninterpreter\ninterracial\ninterracially\ninterregnum\ninterrelation\ninterrogate\ninterrogation\ninterrogative\ninterrogatively\ninterrogatory\ninterrupt\ninterrupted\ninterrupter\ninterruption\ninterscholastic\nintersect\nintersection\nintersexual\nintersocial\ninterspace\ninterspecies\nintersperse\ninterspersion\ninterstate\ninterstellar\ninterstice\ninterstitial\nintertertexture\nintertexture\nintertidal\nintertribal\nintertwine\nintertwined\nintertwist\ninterval\nintervallo\nintervals\nintervene\nintervenience\nintervenient\nintervening\nintervention\nintervert\nintervertebral\ninterview\ninterviewee\ninterviewer\nintervolved\ninterweave\ninterworking\nintestate\nintestinal\nintestine\nintestines\nintesting\ninthrall\ninti\nintima\nintimacy\nintimal\nintimate\nintimately\nintimation\nintimidate\nintimidated\nintimidation\nintinerant\ninto\nintolerable\nintolerance\nintolerant\nintolerantly\nintonation\nintone\nintort\nintout\nintoxicant\nintoxicate\nintoxicated\nintoxicating\nintoxication\nintra\nintracellular\nintracranial\nintractability\nintractable\nintradepartmental\nintradermal\nintradermally\nintrados\nintralinguistic\nintralobular\nintramural\nintramuscular\nintramuscularly\nintrans\nintransient\nintransigency\nintransitive\nintransitively\nintransitivity\nintransmutable\nintrap\nintrapulmonary\nintraregarding\nintrasentential\nintraspecies\nintrastate\nintrauterine\nintravenous\nintravenously\nintraventricular\nintrench\nintrenchment\nintrepid\nintrepidity\nintricacy\nintricate\nintrication\nintrigant\nintrigue\nintriguer\nintriguing\nintrinsic\nintrinsical\nintrinsicality\nintrinsically\nintro\nintroception\nintroduce\nintroduced\nintroduction\nintroductory\nintrogression\nintroit\nintroject\nintrojected\nintrojection\nintromission\nintromit\nintrospection\nintrospective\nintrospectiveness\nintroversion\nintroversive\nintrovert\nintrovertish\nintrude\nintruder\nintruding\nintrusion\nintrusive\nintrusiveness\nintrust\nintuition\nintuitionism\nintuitionist\nintuitive\nintuitively\nintumescence\nintwine\ninuendo\ninula\ninulin\ninunction\ninundate\ninundation\ninunderstanding\ninurbanity\ninure\ninured\ninurement\ninurn\ninusitation\ninutile\ninutility\ninvade\ninvader\ninvagination\ninvalid\ninvalidate\ninvalidated\ninvalidation\ninvalided\ninvalides\ninvalidism\ninvalidity\ninvaluable\ninvaluableness\ninvar\ninvariability\ninvariable\ninvariably\ninvariant\ninvasion\ninvasive\ninvective\ninveigh\ninveigle\ninvenit\ninvent\ninvente\ninvented\ninvention\ninventive\ninventively\ninventiveness\ninventor\ninventory\ninventus\ninverse\ninversely\ninversion\ninvert\ninvertebrate\ninverted\ninverter\ninvest\ninvested\ninvestigate\ninvestigation\ninvestigator\ninvesting\ninvestiture\ninvestment\ninvestments\ninvestor\ninveterate\ninvidia\ninvidiam\ninvidious\ninvidiously\ninvigilation\ninvigilator\ninvigorate\ninvigorating\ninvigoration\ninvincible\ninvincibly\ninviolable\ninviolate\ninvious\ninvisibility\ninvisible\ninvisibleness\ninvisibly\ninvita\ninvitation\ninvitation(a)\ninvitatory\ninvite\ninvited\ninviting\ninvitingness\ninvito\ninvocation\ninvoice\ninvoke\ninvolucrate\ninvolucre\ninvolucrum\ninvoluntarily\ninvoluntariness\ninvoluntary\ninvolute\ninvolution\ninvolve\ninvolved\ninvolvement\ninvulnerability\ninvulnerable\ninvulnerableness\ninward\ninward-developing\ninward-moving\ninwardly\ninwardness\ninwards\ninweave\ninwrap\ninwrought\nio\niodide\niodinated\niodinating\niodination\niodine\niodocompound\niodoform\niodoprotein\niodothyronine\niodotyrosine\nion\nionia\nionian\nionic\nionization\nionized\nionosphere\niota\niou\niowa\niowan\nipecac\nipecacuanha\nipomoea\nipsa\nipse\nipsilateral\nipsissima\nipsissimis\nipso\nipsus\nira\nirae\niran\nirani\niranian\niraq\niraqi\nirascibility\nirascible\nirascibleness\nirate\nirately\nirdische\nire\nireful\nireland\nirena\nirenic\nirenidae\niresine\niridaceae\niridaceous\niridescence\niridescent\niridic\niridium\niridocyclitis\niridokeratitis\niridoprocne\niris\nirisated\nirish\nirishism\nirishman\nirishwoman\nirk\nirksome\niroin\niron\niron-gray\nironbound\nironclad\nironed\nirongray\nironhanded\nironhearted\nironic\nironical\nironically\nironing\nironlike\nironmonger\nironplated\nirons\nironshod\nironside\nirontree\nironweed\nironwood\nironwork\nironworker\nironworks\nirony\niroquoian\niroquois\nirota\nirradiate\nirradiation\nirrational\nirrationality\nirrationally\nirreclaimable\nirreconcilable\nirreconcilableness\nirrecoverable\nirredeemable\nirredenta\nirredentism\nirredentist\nirreducible\nirrefragable\nirrefutable\nirregular\nirregularity\nirregularly\nirrelation\nirrelative\nirrelevance\nirrelevancy\nirrelevant\nirrelevantly\nirreligion\nirreligious\nirreligiously\nirreligiousness\nirreligon\nirremediable\nirremissible\nirremovable\nirreparable\nirreparably\nirrepentance\nirreplaceable\nirreplaceableness\nirreprehensible\nirrepressibility\nirrepressible\nirreproachable\nirreproachably\nirreproducibility\nirreprovable\nirresilient\nirresistible\nirresoluble\nirresoluion\nirresolute\nirresolutely\nirresoluteness\nirresolution\nirresolvable\nirresolved\nirresolvedly\nirrespective\nirrespectively\nirresponsibility\nirresponsible\nirresponsibly\nirretrievable\nirretrievably\nirrevealable\nirreverence\nirreverent\nirreverently\nirreversibility\nirreversible\nirreversibly\nirrevocabile\nirrevocable\nirrevocably\nirrigate\nirrigation\nirriguous\nirrision\nirritabile\nirritability\nirritable\nirritably\nirritant\nirritare\nirritate\nirritating\nirritation\nirrittaabile\nirruption\nirruptive\nirtish\nirula\nirvingia\nirvingite\nis\nisaac\nisatis\nischia\nischiagra\nischigualastia\nischium\nisere\niseult\nish(ip)\nishtar\nisis\nislam\nislamabad\nislamism\nisland\nislander\nisle\nislet\nisobar\nisobath\nisobathic\nisobutylene\nisocarboxazid\nisocheimal\nisocheimenal\nisocheimic\nisochronal\nisochrone\nisochronism\nisochronous\nisocyanate\nisoetaceae\nisoetales\nisoetes\nisogamete\nisogamy\nisogonic\nisogram\nisohel\nisolable\nisolate\nisolated\nisolating(a)\nisolation\nisolationism\nisolationist\nisoleucine\nisomer\nisomeric\nisomerism\nisometric\nisometrics\nisometropia\nisometry\nisomorphism\nisomorphous\nisoperimetric\nisoperimetrical\nisopod\nisopoda\nisoptera\nisopteran\nisopyrum\nisosceles\nisospondyli\nisotheral\nisotherm\nisothermal\nisothermic\nisothiocyanate\nisotonic\nisotope\nisotopic\nisotropic\nisrael\nisraeli\nissue\nissueless\nissuer\nissus\nist\nistanbul\nisthmus\nistic\nistiophoridae\nistiophorus\nistos\nisuridae\nisurus\nit\nitalian\nitalian-speaking\nitalic\nitalics\nitaly\nitch\nitching\nitem\nitemize\nitems\niterate\niteration\niterative\niterum\nitinerant\nitinerary\nits\nitself\nitur\niv\nivied\nivory\nivorybill\nivry\nivy\niwo\nixia\nixion\nixobrychus\nixodes\nixodidae\niyar\nizanagi\nizanami\nja\njab\njabber\njabiru\njabot\njaboticaba\njacal\njacamar\njacent\njacet\njacinth\njack\njack-in-the-box\njack-in-the-pulpit\njack-o'-lantern\njackal\njackanapes\njackass\njackdaw\njacket\njackfruit\njackknife\njackknife-fish\njackleg\njackpot\njackpudding\njackrabbit\njacks\njackscrew\njacksmelt\njacksnipe\njackson\njacksonia\njacksonian\njacksonville\njackstones\njackstraw\njackstraws\njacob\njacobean\njacobin\njacobinic\njacobinism\njacobite\njacquerie\njacquinia\njacta\njactancy\njactitation\njaculate\njaculus\njade\njaded\njadeite\njaeger\njaffa\njag\njagah\njagannath\njager\njagged\njaggedness\njaggery\njaguar\njaguarundi\njahannan\njahre\njail\njailer\njain\njainism\njainist\njakarta\njakes\njalapeno\njaldi\njalousie\njam\njamaica\njamaican\njamais\njamb\njambalaya\njamboree\njambos\njames\njamesonia\njammed\njamming\njampan\njampot\njangle\njangled\njangling\njanissary\njanitor\njansen\njansenism\njansenist\njanty\njantyk\njanua\njanuary\njanus\njanus-faced\njao\njap\njapan\njapanese\njapanning\njaponica\njaquima\njar\njardiniere\njargon\njargoon\njarring\njarringly\njars\njasmine\njasminum\njason\njasper\njassid\njassidae\njatropha\njaundice\njaundiced\njaunt\njauntily\njauntiness\njaunting\njaunty\njava\njavanese\njavanthropus\njavelin\njaw\njawbreaker\njawfish\njawless\njaws\njay\njaywalker\njazz\njazzy\njb\njc\njd\nje\njealous\njealously\njealousy\njealousyjealousness\njean\njecur\njeep\njeer\njeeringly\njefferson\njeffersonian\njehovah\njehu\njejune\njejunitis\njejunity\njejunoileitis\njejunostomy\njejunum\njejunus\njell\njell-o\njellaba\njelly\njellyfish\njellyroll\njemidar\njemmy\njena\njennet\njenny\njeopard\njeopardize\njeopardy\njerboa\njercks\njereed\njeremiad\njeremiade\njeremiah\njeremy\njerevan\njerez\njericho\njerid\njerk\njerker\njerkily\njerkin\njerks\njerkwater\njerky\njeroboam\njerry\njerry-builder\njerry-building\njerry-built\njersey\njerusalem\njessamy\njest\njester\njesting\njestingstock\njests\njesuit\njesuitical\njesuitism\njesuitry\njesus\njet\njet-propelled\njetblack\njeter\njeth\njets\njetsam\njetting\njettison\njetty\njeu\njeune\njew\njew's-ear\njewbush\njewel\njeweler\njewellery\njewelry\njewels\njewels-of-opar\njewelweed\njewess\njewfish\njewis\njewish\njews\njezebel\njf\njhil\njhilmil\njhuth\njiao\njib\njibboom\njibe\njiffy\njig\njigger\njiggered\njiggermast\njiggle\njigs\njigsaw\njihad\njilt\njilted\njimdandy\njimmies\njimmy\njimp\njimsonweed\njingal\njinghpo\njingle\njingling\njingo\njinks\njinn\njinrikisha\njinx\njio\njiqui\njird\njitterbug\njitteriness\njiujitsu\njiva\njive\njnuis\njoan\njob\njobation\njobber\njobbernowl\njobbery\njobbing\njobholder\njobs\njocando\njock\njockey\njockeyship\njocose\njocosely\njocoseness\njocosity\njocular\njocularity\njocund\njocundity\njodphur\njoe\njog\njogger\njogging\njoggle\njohannesburg\njohn\njohnnycake\njohnson\njohnsonian\njohnsons\njoie\njoin\njoinder\njoined\njoiner\njoinery\njoining\njoint\njointed\njointer\njointly\njointstock\njointure\njoist\njoke\njoker\njokes\njoking\njokingly\njole\njollification\njollity\njolly\njolt\njolted\njolterhead\njolthead\njonah\njonathan\njones\njoness\njonquil\njonson\njordan\njordanella\njordanian\njornada\njorum\njoseph\njosephs\njosh\njoshua\njoss\njostle\njostling\njot\njotter\njotting\njottings\njotun\njouir\njoule\njounce\njour\njournal\njournalese\njournalism\njournalist\njournalistic\njournalistically\njourney\njourneying\njourneyman\njourneys\njoust\njove\njovial\njoviality\njovially\njovialness\njovian\njovinianist\njowl\njown\njoy\njoyful\njoyless\njoylessly\njoylessness\njoyous\njoyride\njoys\njoystick\njp\njuan\njubbah\njube\njubeo\njubilant\njubilation\njubilee\njucundity\njudaeus\njudaic\njudaica\njudaical\njudaism\njudas\njudeo-christian\njudge\njudged\njudgement\njudges\njudging\njudgment\njudgmental\njudgship\njudicable\njudically\njudicantur\njudicata\njudication\njudicatory\njudicature\njudice\njudicial\njudicially\njudiciary\njudicious\njudiciously\njudiciousness\njudith\njudo\njudy\njug\njugement\njugend\njuggernath\njuggernaut\njuggle\njuggler\njugglery\njuggling\njuglandaceae\njuglandales\njuglans\njugular\njugulate\njugulo\njuice\njuiceless\njuicy\njujitsu\njuju\njujube\njujutsu\njukebox\njul\njulep\njulian\njuliet\njulius\njuly\njumb\njumber\njumble\njumbo\njument\njumentous\njump\njumped-up\njumper\njumpers\njumping\njumps\njuncaceae\njunco\njuncta\njunction\njuncture\njuncus\njune\njuneau\njuneberry\njung\njungere\njungermanniaceae\njungermanniales\njungian\njungle\njungly\njunior\njunior(a)\njunior-grade\njuniority\njuniper\njuniperus\njunius\njunk\njunker\njunket\njunketing\njunkyard\njuno\njunoesque\njunta\njunto\njupati\njupe\njupiter\njural\njuramentado\njurare\njurassic\njurat\njure\njuridical\njuris\njurisdiction\njurisdictional\njurisprudence\njurisprudential\njurisprudentially\njurist\njuror\njury\njuryman\njus\njussive\njust\njuste\njustice\njustices\njusticiar\njusticiary\njustifiable\njustifiably\njustification\njustificative\njustified\njustifier\njustify\njustitia\njustititiae\njustle\njustly\njustness\njustus\njut\njute\njutland\njutting\njutty\njuvabit\njuvenal\njuvenescence\njuvenile\njuvenility\njuxtaposed\njuxtaposition\njy\njynx\nka\nkabob\nkabul\nkachcha\nkachin\nkadai\nkadi\nkaffir\nkaffiyeh\nkafir\nkafiri\nkafka\nkafkaesque\nkahikatea\nkahin\nkahlua\nkahoolawe\nkai\nkain\nkainite\nkaiser\nkaka\nkakatoe\nkakemono\nkaki\nkal\nkala\nkala-azar\nkalahari\nkalapooia\nkalapooian\nkale\nkaleidoscope\nkaleidoscopic\nkalends\nkali\nkalki\nkalmia\nkalon\nkalon/gr\nkalotermes\nkalotermitidae\nkalumpang\nkam-sui\nkama\nkamarupan\nkamba\nkamet\nkami\nkamia\nkamikaze\nkamikazi\nkampala\nkampong\nkanawha\nkanchenjunga\nkanchil\nkangaroo\nkann\nkannada\nkansa\nkansan\nkansas\nkant\nkantian\nkantikoy\nkanzu\nkaoliang\nkaolinite\nkaon\nkaph\nkapok\nkappa\nkapuka\nkaput\nkarachi\nkarakalpak\nkarakoram\nkarat\nkarate\nkarelia\nkarelian\nkaren\nkarma\nkaro\nkarok\nkartik\nkartikeya\nkaryaster\nkaryoplasmic\nkaryotype\nkasha\nkashmir\nkashmiri\nkassis\nkassite\nkat\nkatabatic\nkatamorphism\nkaterfelto\nkatharevusa\nkatharobe\nkatharobic\nkatharometer\nkatmandu\nkatsuwonidae\nkatsuwonus\nkatydid\nkauai\nkaunas\nkauri\nkava\nkavass\nkawaka\nkayak\nkazak\nkazakstan\nkazan\nkaziaskier\nkazoo\nkb\nkc\nkd\nke\nkea\nkean\nkeats\nkeble\nkeck\nkeddah\nkedge\nkedgeree\nkeel\nkeelhaul\nkeelson\nkeen\nkeener\nkeeneyed\nkeenly\nkeenness\nkeep\nkeeper\nkeeping\nkeeps\nkeepsake\nkeeshond\nkeg\nkein\nkekchi\nkelp\nkelpie\nkelvin\nkempt\nken\nkenaf\nkennedia\nkennedy\nkennel\nkenning\nkent\nkentish\nkentuckian\nkentucky\nkenya\nkenyan\nkenyapithecus\nkepi\nkepler\nkept\nkera\nkeratin\nkeratitis\nkeratoiritis\nkeratoplasty\nkeratoscleritis\nkeratosis\nkerchief\nkern\nkernel\nkernicterus\nkernite\nkerosene\nkerosine\nkerygma\nkestrel\nketch\nketeleeria\nketembilla\nketoacidosis\nketone\nketonemia\nketonuria\nketoprofen\nketorolac\nketose\nkettle\nkeurboom\nkey\nkeyboard\nkeyed\nkeyhole\nkeyless\nkeynes\nkeynesian\nkeynesianism\nkeynote\nkeystone\nkf\nkg\nkh\nkhaki\nkhalkha\nkhamsin\nkhamti\nkhan\nkhana\nkhansamah\nkhansaman\nkhanty\nkharkov\nkhartoum\nkhaya\nkhedive\nkhepera\nkhitmutgar\nkhmer\nkhoikhoin\nkhoisan\nkhoja\nkhoum\nkhowar\nkhudd\nkhuen\nki\nkiack\nkiang\nkibbutz\nkibbutznik\nkibe\nkibitka\nkibitz\nkichaga\nkichai\nkick\nkickback\nkicker\nkicking\nkickoff\nkickshaws\nkicksorter\nkid\nkidd\nkidding\nkiddy\nkidnap\nkidnaper\nkidnapped\nkidnapper\nkidnapping\nkidney\nkiev\nkigali\nkiggelaria\nkike\nkila\nkilderkin\nkilimanjaro\nkiliwa\nkilkenny\nkill\nkillable\nkilldeer\nkilled\nkiller\nkillick\nkillifish\nkilling\nkillingly\nkilljoy\nkills\nkiln\nkilobyte\nkilogram\nkilogram-meter\nkilohertz\nkiloliter\nkilometer\nkiloton\nkilovolt\nkilovolt-ampere\nkilowatt\nkilt\nkilter\nkimbo\nkimono\nkin\nkina\nkind\nkindergarten\nkinderspiel\nkindgom\nkindhearted\nkindheartedness\nkindle\nkindliness\nkindling\nkindly\nkindness\nkindred\nkinds\nkine\nkinematics\nkinematicss\nkinescope\nkinesis\nkinesthesia\nkinesthesis\nkinesthetic\nkinesthetically\nkinetic\nkinfolk\nking\nking-size\nkingbird\nkingbolt\nkingcraft\nkingdom\nkingfish\nkingfisher\nkinghood\nkinglet\nkingly\nkingpin\nkings\nkingship\nkingston\nkingstown\nkingsyellow\nkingwood\nkink\nkinkajou\nkinky\nkino\nkinosternidae\nkinosternon\nkinshasa\nkinship\nkinsman\nkinswoman\nkinyarwanda\nkiosk\nkiowa\nkip\nkipling\nkiplingesque\nkipper\nkirghiz\nkiribati\nkirk\nkirkia\nkirsch\nkirtle\nkishar\nkishinev\nkishke\nkislev\nkismet\nkiss\nkisser\nkisses\nkissing\nkiswahili\nkit\nkitakyushu\nkitbag\nkitcat\nkitchen\nkitchener\nkitchenette\nkitchenware\nkite\nkites\nkith\nkithless\nkitsch\nkitten\nkitten-tails\nkittenish\nkittereen\nkittiwake\nkitty\nkiwi\nkj\nkl\nklan\nklansman\nklaxon\nkleenex\nkleptodipsomania\nkleptomania\nkleptomaniac\nklondike\nklutz\nklystron\nklyuchevskaya\nknack\nknacker\nknackwurst\nknag\nknaggy\nknap\nknapsack\nknapweed\nknarl\nknarled\nknave\nknavery\nknavish\nknawel\nknead\nknee\nknee-deep\nkneed\nkneel\nkneeler\nkneeling\nknees\nknell\nknesset\nknickerbockers\nknickknack\nknickknacks\nknicknack\nknife\nknifelike\nknight\nknight-errant\nknighterrant\nknighterrantry\nknighthood\nknightia\nknights\nkniphofia\nknish\nknit\nknitted\nknitter\nknitting\nknitwear\nknob\nknobble\nknobby\nknobkerrie\nknock\nknock-down(a)\nknock-knee\nknock-kneed\nknockabout\nknockdown\nknockdown-dragout\nknocked\nknocked-out(a)\nknocker\nknockkneed\nknockout\nknocks\nknoll\nknot\nknotgrass\nknothole\nknotted\nknotty\nknout\nknow\nknow-how\nknow-it-all\nknowing\nknowingly\nknowingness\nknowldge\nknowledge\nknowlege\nknown\nknows\nknuckle\nknuckleball\nknuckles\nknur\nknurl\nknurly\nknurr\nkoala\nkoasati\nkob\nkobo\nkobold\nkobus\nkodagu\nkogia\nkohinoor\nkohl\nkohleria\nkohlrabi\nkoine\nkokka\nkola\nkolam\nkolami\nkolkhoz\nkolkhoznik\nkolkwitzia\nkomi\nkomondor\nkongo\nkonini\nkoniology\nkook\nkookaburra\nkooshti\nkopek\nkopje\nkoran\nkoranic\nkordofan\nkordofanian\nkorea\nkorean\nkoruna\nkos\nkosher\nkosmos\nkosteletzya\nkota\nkoto\nkotoko\nkotow\nkoumiss\nkowhai\nkowtow\nkr\nkraal\nkraft\nkrait\nkrakatau\nkraken\nkranke\nkraut\nkremlin\nkriegspiel\nkriegsspiel\nkrigia\nkrill\nkris\nkrishna\nkrishnaism\nkrubi\nkrummhorn\nkrupp\nkrypterophaneron\nkrypton\nkshatriya\nkuchean\nkudos/gr\nkudu\nkudzu\nkui\nkuki\nkuklux\nkulanapan\nkummel\nkumquat\nkunlun\nkunzite\nkura\nkurbash\nkurdish\nkurrajong\nkursi\nkuru\nkurux\nkusan\nkutcherry\nkuvasz\nkuvi\nkuwait\nkuwaiti\nkvass\nkw\nkwa\nkwack\nkwajalein\nkwakiutl\nkwan-yin\nkwannon\nkwanza\nkwanzaa\nkwela\nkyanize\nkyat\nkyles\nkylie\nkymograph\nkyoto\nkyphosidae\nkyphosis\nkyphosus\nkyrgystan\nkyushu\nl\nl-plate\nla\nlaager\nlab\nlabarum\nlabdanum\nlabefy\nlabel\nlabeled\nlabent\nlabetur\nlabial\nlabiatae\nlabiate\nlabiated\nlabile\nlabitur\nlabium\nlablab\nlabor\nlabora\nlaboratory\nlabored\nlaborem\nlaborer\nlaboring\nlaborious\nlaboriously\nlaboriousness\nlaboro\nlabors\nlaborsaving\nlabourite\nlabrador\nlabridae\nlabryrinthian\nlabstenir\nlabuntur\nlaburnum\nlabyrinth\nlabyrinthian\nlabyrinthic\nlabyrinthine\nlabyrinthitis\nlabyrinthodont\nlabyrinthodontia\nlac\nlaccopetalum\nlace\nlacebark\nlaced\nlacerable\nlacerate\nlaceration\nlacerta\nlacertidae\nlacessit\nlacewing\nlacework\nlaches\nlachesis\nlachnolaimus\nlachrymae\nlachrymals\nlachrymation\nlachrymatory\nlachrymis\nlachrymose\nlaciform\nlaciniate\nlaciniform\nlaciniose\nlack\nlackadaisical\nlackadaisically\nlackadaisy\nlackbrain\nlackbrained\nlacker\nlackey\nlacking\nlacking(p)\nlackluster\nlackwit\nlaconia\nlaconian\nlaconic\nlaconically\nlaconism\nlacquer\nlacquerware\nlacrimal\nlacrimation\nlacrimatory\nlacrosse\nlacrymae\nlactalbumin\nlactarius\nlactation\nlactea\nlacteal\nlactean\nlacteous\nlactescence\nlactescent\nlactic\nlactiferous\nlactobacillaceae\nlactobacillus\nlactophrys\nlactose\nlactuca\nlacuna\nlacuslake\nlacuspile\nlacustrine\nlacy\nlad\nladder\nladder-back\nlade\nladen\nlading\nladino\nladle\nlady\nlady's-eardrop\nlady-in-waiting\nlady-of-the-night\nladybug\nladyfinger\nladyfish\nladylike\nladylikeness\nladylove\nladys\nladyship\nlaelia\nlaetificant\nlag\nlagan\nlagarostrobus\nlagenaria\nlagenophera\nlager\nlagerstroemia\nlaggard\nlagging\nlagidium\nlagniappe\nlagodon\nlagomorph\nlagomorpha\nlagoon\nlagopus\nlagorchestes\nlagos\nlagostomus\nlagothrix\nlags\nlaguncularia\nlagune\nlahar\nlahu\nlaic\nlaical\nlaid\nlaimable\nlain\nlair\nlaird\nlais\nlaisse\nlaisser\nlait\nlaity\nlake\nlakefront\nlakeside\nlakshmi\nlallans\nlallegro\nlally\nlama\nlamaism\nlamaist\nlamarck\nlamarckian\nlamarckism\nlamarkism\nlamasery\nlamb\nlamb's-quarter\nlamb's-quarters\nlamba\nlambaste\nlambchop\nlambda\nlambent\nlambert\nlambertia\nlambeth\nlambis\nlambkin\nlamblike\nlambrequin\nlambskin\nlame\nlamedh\nlamella\nlamellar\nlamellated\nlamellibranch\nlamellicornia\nlamelliform\nlamely\nlamende\nlameness\nlament\nlamentable\nlamentably\nlamentation\nlamented\nlamenting\nlamia\nlamina\nlaminaria\nlaminariaceae\nlaminariales\nlaminate\nlaminated\nlamination\nlaminectomy\nlaminiferous\nlaminitis\nlamium\nlammas\nlammastide\nlamna\nlamnidae\nlamp\nlampblak\nlampe\nlamplight\nlamplighter\nlamplit\nlampoon\nlampooner\nlamppost\nlamprey\nlampridae\nlampris\nlampropeltis\nlampshade\nlampyridae\nlana\nlanai\nlanate\nlanated\nlancaster\nlancastrian\nlance\nlancelet\nlancelot\nlanceolate\nlancer\nlancers\nlanceshaped\nlancet\nlancetfish\nlancewood\nlancinate\nland\nland(a)\nlandamman\nlandau\nlanded\nlandfall\nlandfill\nlandgrave\nlandholding\nlanding\nlandler\nlandless\nlandlocked\nlandloper\nlandlord\nlandlubber\nlandmark\nlandmass\nlandowner\nlandreeve\nlands\nlandscape\nlandscaped\nlandscaping\nlandscapist\nlandscip\nlandside\nlandslide\nlandslip\nlandsman\nlandsturm\nlandward\nlandwehr\nlane\nlang\nlangbeinite\nlanglauffer\nlangley\nlangrage\nlangrel\nlangside\nlangsyne\nlanguage\nlanguages\nlanguedoc-roussillon\nlanguid\nlanguidly\nlanguille\nlanguish\nlanguishing\nlanguishment\nlanguor\nlanguorously\nlangur\nlaniate\nlaniidae\nlanius\nlank\nlankiness\nlanky\nlanolin\nlanquid\nlanseh\nlansing\nlantana\nlantern\nlantern-jawed\nlanterne\nlanternfish\nlanternjawed\nlanthanotidae\nlanthanotus\nlanthanum\nlanthorn\nlanuginose\nlanuginous\nlanyard\nlanzhou\nlao\nlao-tzu\nlaocoon\nlaos\nlaotian\nlap\nlap-jointed\nlaparoscope\nlaparoscopy\nlaparotomy\nlapboard\nlapdog\nlapel\nlapful\nlapidarian\nlapidary\nlapidate\nlapidation\nlapidem\nlapidescence\nlapidification\nlapin\nlapis\nlaportea\nlapp\nlappet\nlappic\nlappland\nlapse\nlapsed\nlapster\nlapsus\nlapt\nlaptop\nlaputa\nlapwing\nlarboard\nlarcenist\nlarceny\nlarch\nlard\nlardaceous\nlarder\nlardizabala\nlardizabalaceae\nlares\nlarge\nlarge-scale\nlargehearted\nlargely\nlargemouth\nlargeness\nlarger\nlargess\nlargest\nlarghetto\nlarghissimo\nlargiloquent\nlargiri\nlargo\nlari\nlariat\nlaricariidae\nlarid\nlaridae\nlarigo\nlarix\nlark\nlarkspur\nlarmes\nlarmoyante\nlarrea\nlarrigan\nlarrikin\nlarrup\nlart\nlartisan\nlarum\nlarus\nlarva\nlarvacea\nlarvacean\nlarval\nlarvicide\nlaryngeal\nlaryngectomy\nlaryngitis\nlaryngopharynx\nlaryngoscope\nlarynx\nlas\nlasagna\nlascar\nlasciate\nlasciviency\nlascivious\nlasciviously\nlase\nlaser\nlash\nlash-up\nlashd\nlasher\nlashing\nlasiocampa\nlasiocampid\nlasiocampidae\nlasisser\nlasiurus\nlass\nlassidude\nlassie\nlassitude\nlasso\nlassoo\nlast\nlast(a)\nlast-minute\nlasthenia\nlasting\nlastingly\nlastingness\nlastreopsis\nlat\nlatch\nlatchet\nlatchkey\nlatchstring\nlate\nlate(a)\nlatecomer\nlateen\nlateen-rig\nlately\nlatency\nlateness\nlatent\nlater\nlater(a)\nlateral\nlaterality\nlaterally\nlateri\nlatericiam\nlaterite\nlateritious\nlatest\nlatet\nlateward\nlatex\nlath\nlathe\nlather\nlathery\nlathi\nlathyrus\nlatifoliate\nlatifolous\nlatimeria\nlatimeridae\nlatin\nlatin-american\nlatinate\nlatinesce\nlatinist\nlatino\nlatish\nlatitancy\nlatitat\nlatitation\nlatitude\nlatitudinal\nlatitudinarian\nlatitudinarianism\nlatium\nlatrant\nlatration\nlatria\nlatrine\nlatrines\nlatrocinium\nlatrociny\nlatrodectus\nlatter\nlatter(a)\nlatter-day\nlatterday\nlatterly\nlattice\nlatuit\nlatvia\nlatvian\nlaud\nlaudable\nlaudant\nlaudantes\nlaudantlatin\nlaudanum\nlaudari\nlaudation\nlaudato\nlaudator\nlaudatory\nlaudatur\nlaudo\nlaugh\nlaughable\nlaughably\nlaughing\nlaughing(a)\nlaughingly\nlaughingstock\nlaughs\nlaught\nlaughter\nlaunch\nlaunched\nlauncher\nlaunching\nlaunchout\nlaunder\nlaunderette\nlaundering\nlaundress\nlaundry\nlaundryman\nlauraceae\nlaurasia\nlaureate\nlaurel\nlaurel-tree\nlaureled\nlaurels\nlaurelwood\nlaurus\nlava\nlavage\nlavaliere\nlavandula\nlavatera\nlavation\nlavatory\nlave\nlavement\nlavender\nlaver\nlavish\nlavishly\nlavishment\nlavishness\nlaw\nlaw-abiding\nlawcourt\nlawful\nlawfully-begotten\nlawfulness\nlawgiver\nlawless\nlawlessness\nlawman\nlawn\nlawrencium\nlaws\nlawsuit\nlawyer\nlawyerbush\nlax\nlaxative\nlaxity\nlaxly\nlaxness\nlay\nlayby\nlayer\nlayered\nlayette\nlayia\nlaying\nlayman\nlayoff\nlayout\nlays\nlaystall\nlazaretto\nlazarhouse\nlazarillo\nlazarus\nlazily\nlazuli\nlazy\nlazybones\nlazzarone\nlb\nlc\nld\nle\nlea\nleach\nlead\nlead-free\nlead-in\nleaded\nleaden\nleader\nleaderless\nleadership\nleading\nleading(p)\nleadplant\nleads\nleadwort\nleaf\nleafed\nleafhopper\nleafless\nleaflet\nleaflike\nleafy\nleague\nleak\nleakage\nleakey\nleaking\nleakproof\nleaky\nleal\nlean\nlean-to\nleaned\nleaning\nleanness\nleanto\nleap\nleapfrog\nleaping\nleaps\nlear\nlearl\nlearn\nlearned\nlearner\nlearning\nlearnt\nleas\nlease\nleasehold\nleaseholder\nleaseholds\nleash\nleast\nleast(a)\nleather\nleatherette\nleatherjacket\nleatherleaf\nleatherwood\nleatherwork\nleathery\nleau\nleave\nleaven\nleavened\nleaves\nleaving\nleavings\nlebanese\nlebanon\nleben\nlebistes\nlecanopteris\nlecanora\nlecanoraceae\nleccinum\nlechanorales\nlechartelierite\nlecher\nlecherous\nlecherousness\nlechery\nlechwe\nlecithin\nlectern\nlectin\nlection\nlector\nlecture\nlecturer\nlectureship\nlecythidaceae\nled\nleda\nledge\nledger\nledum\nlee\nleech\nleechcraft\nleeches\nleeds\nleeenfield\nleef\nleek\nleemetford\nleer\nleering\nleery\nlees\nleeward\nleeway\nleft\nleft(a)\nleft-hand(a)\nleft-handed\nleft-handedness\nleft-hander\nleftfield\nlefthanded\nleftish\nleftist\nleftover\nleg\nleg-pull\nlegacy\nlegadero\nlegal\nlegalese\nlegalism\nlegality\nlegalization\nlegalize\nlegalized\nlegally\nlegatary\nlegate\nlegatee\nlegation\nlegato\nlegem\nlegend\nlegendary\nlegerdemain\nlegerete\nleges\nlegged\nlegging\nleggy\nlegibility\nlegible\nlegibly\nlegion\nlegionary\nlegionnaire\nlegis\nlegislate\nlegislation\nlegislative\nlegislatively\nlegislator\nlegislatorial\nlegislatorship\nlegislature\nlegist\nlegitimacy\nlegitimate\nlegitimately\nlegitimateness\nlegless\nleglike\nlegon\nlegs\nlegume\nlegumin\nleguminious\nleguminosae\nleguminous\nlehren\nlei\nleibniz\nleibnizian\nleicester\nleicestershire\nleid\nleiden\nleiodermatous\nleiopelma\nleiopelmatidae\nleiophyllum\nleipzig\nleishmaniasis\nleisure\nleisure(a)\nleisureiy\nleisureliness\nleisurely\nleitmotiv\nleitneria\nleitneriaceae\nlek\nlekvar\nlemaireocereus\nleman\nlemma\nlemming\nlemmus\nlemna\nlemnaceae\nlemniscus\nlemnos\nlemon\nlemonade\nlemoncolored\nlemonwood\nlemony\nlemonyellow\nlempira\nlempriere\nlemur\nlemures\nlemuridae\nlemuroidea\nlena\nlenclume\nlend\nlend-lease\nlender\nlending\nlends\nlength\nlengthen\nlengthened\nlengthening\nlengthily\nlengthiness\nlengths\nlengthways\nlengthwise\nlengthy\nlenience\nleniency\nlenient\nlenify\nlenin\nlenitive\nlenity\nlennoaceae\nlens\nlensman\nlent\nlente\nlenten\nlentibulariaceae\nlenticular\nlentiform\nlentiginous\nlentil\nlentinus\nlentissimo\nlento\nlentor\nlentous\nleo\nleonardesque\nleonardo\nleone\nleonem\nleones\nleonidas\nleonine\nleonotis\nleontocebus\nleontodon\nleontopodium\nleonurus\nleopard\nleopard's-bane\nleopardess\nleopards\nlepadidae\nlepanto\nlepas\nlepechinia\nleper\nlepicurisme\nlepidium\nlepidobotryaceae\nlepidobotrys\nlepidochelys\nlepidocrocite\nlepidocybium\nlepidodendraceae\nlepidodendrales\nlepidolite\nlepidomelane\nlepidophobia\nlepidoptera\nlepidopterist\nlepidosauria\nlepidote\nlepidothamnus\nlepiota\nlepiotaceae\nlepisma\nlepismatidae\nlepisosteidae\nlepisosteus\nlepomis\nlepore\nleporid\nleporidae\nleprechaun\nleprosy\nleprous\nleptarrhena\nleptinotarsa\nleptocephalus\nleptodactylidae\nleptodactylus\nleptoglossus\nleptomeninges\nlepton\nleptopteris\nleptoptilus\nleptorrhine\nleptospira\nleptosporangiate\nleptosporangium\nleptotene\nleptotyphlopidae\nleptotyphlops\nlepus\nlerot\nlerret\nles\nlesbian\nlesbianism\nlesbos\nlese\nlesion\nlesotho\nlesperance\nlesprit\nlesquerella\nless\nless(a)\nless-traveled\nlessee\nlessen\nlessened\nlessening\nlesser\nlesson\nlessor\nlest\nlet\nlethal\nlethalis\nlethargic\nlethargical\nlethargically\nlethargy\nlethe\nlethean\nlethiferous\nleti\nleto\nletoile\nlets\nletter\nletter-perfect\nlettercard\nlettered\nletterhead\nletterman\nletterpress\nletters\nlettre\nlettres\nlettuce\nletup\nleu\nleucadendron\nleucaena\nleucanthemum\nleucine\nleuciscus\nleuco\nleucocytozoan\nleucogenes\nleucorrhea\nleucothoe\nleuctra\nleukemia\nleukocyte\nleukoderma\nleukopenia\nlev\nlevant\nlevanter\nlevantine\nleve\nlevee\nlevel\nleveler\nlevels\nlever\nleverage\nleveret\nlevi's\nleviathan\nlevies\nlevigate\nlevigation\nlevin\nlevirate\nlevisticum\nlevitation\nlevite\nlevitical\nleviticus\nlevity\nlevorotary\nlevy\nlewd\nlewdly\nlewisia\nlex\nlexeme\nlexical\nlexically\nlexicographer\nlexicographic\nlexicography\nlexicologist\nlexicology\nlexicon\nlexicostatistic\nlexicostatistics\nlexington\nlexis\nlexocography\nley\nleycesteria\nleymus\nleyte\nlf\nlg\nlh\nlhasa\nlheure\nlhomme\nlhonneur\nlhotse\nli\nliabilities\nliability\nliable\nliable(p)\nliableness\nliaison\nliana\nliar\nliatris\nlibadist\nlibation\nlibations\nlibel\nlibeler\nlibelous\nliberal\nliberalism\nliberalistic\nliberality\nliberalization\nliberally\nliberals\nliberate\nliberated\nliberation\nliberator\nliberavi\nlibere\nliberia\nliberian\nlibertarian\nlibertarianism\nlibertas\nlibertatem\nliberties\nlibertinage\nlibertine\nlibertinish\nliberty\nliberum\nlibet\nlibidinal\nlibidinous\nlibido\nlibitum\nlibocedrus\nlibra\nlibrarian\nlibrarianship\nlibrary\nlibrate\nlibration\nlibratory\nlibrettist\nlibretto\nlibreville\nlibrorum\nlibya\nlibyan\nlice\nlicense\nlicensed\nlicensee\nlicentia\nlicentiate\nlicentious\nlicentiously\nlicentiousness\nlicentitate\nlicet\nlich\nlichanura\nlichen\nlichenales\nlichenes\nlicit\nlicitness\nlick\nlicked\nlickerish\nlickpenny\nlickspittle\nlicorice\nlictor\nlid\nlidded\nlidless\nlido\nlidocaine\nlie\nlie-abed\nlie-in\nliebfraumilch\nliechtenstein\nliechtensteiner\nlied\nliedertafel\nlief\nliege\nliegeman\nliek\nlien\nlienteria\nlientery\nlies\nlieu\nlieutenancy\nlieutenant\nlife\nlife-giving\nlife-size\nlifeblood\nlifeboat\nlifegiving\nlifeguard\nlifeless\nlifelessly\nlifelike\nlifeline\nlifelong\nlifer\nlifesaving\nlifetime\nlifeweary\nlifework\nlift\nliftoff\nligament\nligand\nligation\nligature\nliger\nlight\nlight-armed\nlight-duty\nlight-fingered\nlight-footed\nlight-handed\nlight-handedly\nlight-heartedly\nlight-o'-love\nlight-sensitive\nlight-skinned\nlightcolored\nlighted\nlighten\nlighter\nlighterage\nlighterman\nlightfingered\nlightfooted\nlightheaded\nlightheadedness\nlighthouse\nlighting\nlighting-up(a)\nlightlamp\nlightlegged\nlightless\nlightly\nlightminded\nlightness\nlightning\nlights\nlights-out\nlightship\nlightsome\nlightsomely\nlightsomeness\nlightweight\nlightwood\nligible\nlignaloes\nligne\nligneous\nlignified\nlignin\nlignite\nligno\nlignograph\nlignography\nlignosae\nlignous\nlignum\nligularia\nliguria\nligustrum\nlikable\nlike\nlike-minded\nliked\nlikelihood\nlikeliness\nlikely\nlikeness\nlikening\nlikes\nlikewise\nlikin\nliking\nlikuta\nlilac\nlilangeni\nliliaceae\nliliaceous\nliliales\nliliidae\nliliputian\nlilith\nlilium\nlilliput\nlilliputian\nlilly\nlilo\nlilongwe\nlilt\nlilting\nlily\nlily-white\nlilyhearted\nlilylivered\nlilyturf\nlima\nlimacidae\nlimae\nlimagination\nlimanda\nlimature\nlimax\nlimb\nlimbed\nlimber\nlimbic\nlimbless\nlimbo\nlimbs\nlimbus\nlime\nlimeade\nlimekiln\nlimelight\nlimenitis\nlimerick\nlimestone\nlimewater\nlimey\nlimicolae\nlimine\nlimit\nlimitarian\nlimitation\nlimitative\nlimited\nlimiter\nlimiting\nlimitless\nlimits\nlimn\nlimner\nlimnobium\nlimnocryptes\nlimnodromus\nlimnology\nlimoe\nlimonene\nlimonite\nlimonium\nlimosa\nlimousin\nlimousine\nlimp\nlimpa\nlimpet\nlimpid\nlimpidity\nlimpkin\nlimply\nlimpopo\nlimproviste\nlimulidae\nlimulus\nlimy\nlin\nlinaceae\nlinage\nlinalool\nlinanthus\nlinaria\nlinchpin\nlincoln\nlincolnesque\nlincolnshire\nlincomycin\nlincture\nlinctus\nlindane\nlinden\nlindera\nlindheimera\nlindley\nlindy\nline\nlinea\nlineage\nlineal\nlineally\nlineament\nlinear\nlinearly\nlineation\nlinebacker\nlinecut\nlined\nlinelike\nlineman\nlinemen\nlinen\nlinendraper\nliner\nlines\nlinesman\nlineup\nlinfame\nling\nling-pao\nlingam\nlingcod\nlinger\nlingerer\nlingerie\nlingering\nlingering(a)\nlingeringly\nlingerstennyson\nlingo\nlingonberry\nlingua\nlinguacious\nlinguae\nlingual\nlingualumina\nlinguiform\nlinguine\nlinguist\nlinguistic\nlinguistically\nlinguistics\nlingulate\nliniment\nlining\nlink\nlinkage\nlinkboy\nlinked\nlinn\nlinnaea\nlinnaeus\nlinnet\nlinocut\nlinoleum\nlinotype\nlinouae\nlinseed\nlinsey-woolsey\nlinseywoolsey\nlinstock\nlint\nlintel\nliomys\nlion\nlion's-ear\nlion-hunter\nlioness\nlionet\nlionfish\nlionhearted\nlionize\nlions\nlip\nliparididae\nliparis\nlipase\nlipid\nlipless\nlipoma\nlipophilic\nlipoprotein\nliposcelis\nlipothymy\nlipotype\nlipotyphla\nlipped\nlippitude\nlipreading\nlips\nlipstick\nliquate\nliquation\nliquefaction\nliquefiable\nliquefied\nliquefy\nliquescence\nliquescency\nliquescent\nliqueur\nliquid\nliquidambar\nliquidate\nliquidation\nliquidator\nliquidity\nliquidness\nliquids\nliquor\nlir\nlira\nliriodendron\nliriope\nlis\nlisbon\nlisinopril\nlisle\nlisom\nlisp\nlisper\nlispingly\nlissome\nlissomeness\nlist\nlisted\nlistel\nlisten\nlistened\nlistener\nlistening\nlister\nlistera\nlisting\nlistless\nlistlessly\nlistlessness\nlistning\nlists\nlisu\nlitany\nlitchi\nlite\nlitem\nliter\nliteracy\nliterae\nliteral\nliteralism\nliterally\nliteralness\nliterarum\nliterary\nliterate\nliterati\nliteratim\nliterature\nlites\nlithagogue\nlithe\nlithesome\nlithiasis\nlithic\nlithium\nlithocarpus\nlithodidae\nlithograph\nlithographer\nlithographic\nlithography\nlithoidal\nlithology\nlithomancy\nlithomantic\nlithophragma\nlithophyte\nlithophytic\nlithops\nlithospermum\nlithosphere\nlithotint\nlithotomy\nlithuania\nlithuanian\nlithuresis\nlitigant\nlitigate\nlitigation\nlitigious\nlitmus\nlitocranius\nlitote\nlitotes\nlitter\nlittera\nlitteraire\nlitterateur\nlitterbin\nlitterer\nlittle\nlittle(a)\nlittleneck\nlittleness\nlittoral\nlittorina\nlittorinidae\nliturgical\nliturgist\nliturgy\nlivable\nlive\nlive(a)\nlived\nlivelihood\nliveliness\nlivelong\nlively\nliver\nlivercolored\nliveried\nliverpool\nliverpudlian\nliverwort\nlivery\nliveryman\nlives\nlivestock\nlivid\nlividity\nlividly\nlividness\nliving\nliving(a)\nlivistona\nlivonia\nlivonian\nlivor\nlivraison\nlivret\nlivy\nlixiviate\nlixiviation\nlixivium\nliza\nlizard\nlizard's-tail\nlizardfish\nlj\nljubljana\nlk\nll\nllaga\nllama\nllano\nlll\nlloyds\nllud\nllullaillaco\nllyr\nlm\nln\nlo\nloach\nload\nload-bearing(a)\nload-shedding\nloaded\nloading\nloading(a)\nloadstar\nloadstone\nloaf\nloafer\nloam\nloamless\nloamy\nloan\nloanblend\nloanword\nloasa\nloasaceae\nloath\nloathe\nloathed\nloathful\nloathing\nloathsome\nloathsomeness\nloaves\nlob\nlobar\nlobata\nlobate\nlobby\nlobbyism\nlobbyist\nlobe\nlobectomy\nlobed\nlobelia\nlobeliaceae\nlobiform\nlobipes\nloblolly\nlobotes\nlobotidae\nlobotomy\nlobs\nlobscouse\nlobster\nlobsterman\nlobular\nlobularia\nlobularity\nlobule\nloca\nlocal\nlocale\nlocalism\nlocality\nlocalization\nlocalize\nlocalized\nlocally\nlocate\nlocated\nlocation\nlocative\nlocator\nloch\nlochaber\nloci\nlock\nlock-gate\nlockage\nlocke\nlocker\nlocket\nlocking\nlockjaw\nlockmaster\nlocknut\nlockout\nlockring\nlocks\nlocksmith\nlockstitch\nlockup\nlockweir\nloco\nlocofoco\nlocomotion\nlocomotive\nlocos\nlocoweed\nlocular\nlocum\nlocus\nlocust\nlocusta\nlocusts\nlocution\nlode\nlodestar\nlodestone\nlodge\nlodger\nlodging\nlodgment\nlodz\nloeil\nloess\nlofortyx\nlofoten\nloft\nloftily\nloftiness\nlofty\nloftyminded\nlog\nlogan\nloganberry\nlogania\nloganiaceae\nlogarithm\nlogarithmic\nlogarithmically\nlogbook\nloge\nloggan\nlogger\nloggerhead\nloggerheads\nloggia\nlogging\nlogic\nlogical\nlogicality\nlogically\nlogician\nlogicism\nloginess\nlogistic\nlogistics\nlogjam\nlogo\nlogogram\nlogography\nlogogriph\nlogomach\nlogomachy\nlogometer\nlogometric\nlogorrhea\nlogos\nlogotype\nlogrolling\nlogwood\nlogy\nloin\nloins\nloir\nloire\nloiseleuria\nloisir\nloiter\nloiterer\nloki\nloligo\nlolium\nloll\nlolling\nlollipop\nlollop\nlolly\nlolo\nlolo-burmese\nloloish\nloma\nlomariopsidaceae\nlomatia\nlombard\nlombardy\nlome\nloment\nlomogramma\nlomotil\nlonas\nlonchocarpus\nlondon\nlondoner\nlone\nlone(a)\nloneliness\nlonely\nlonely(a)\nloner\nlonesome\nlong\nlong-acting\nlong-ago\nlong-dated\nlong-distance\nlong-faced\nlong-familiar\nlong-haired\nlong-headed\nlong-play\nlong-range\nlong-run\nlong-sufferance\nlong-winded\nlongan\nlonganberry\nlonganimity\nlongas\nlongboat\nlongbow\nlongbowman\nlongdrawn\nlonged-for\nlonger\nlongest\nlongeval\nlongevity\nlongfaced\nlongfellow\nlongfellowl\nlonghand\nlonghead\nlongheaded\nlonghorn\nlonging\nlongingly\nlonginquity\nlongish\nlongitude\nlongitudinal\nlongitudinally\nlonglived\nlongness\nlongo\nlongpending\nlongshoreman\nlongshot\nlongsome\nlongspun\nlongstanding\nlongsufferance\nlongsuffering\nlongtime(a)\nlongueur\nlongways\nlongwinded\nlonicera\nloo\nlooby\nloofa\nloofah\nlook\nlookd\nlookdown\nlookeron\nlooking\nlookingglass\nlookout\nlooks\nloom\nlooming\nloon\nloop\nloop-line\nlooped\nloophole\nloopholed\nloose\nloose-jointed\nlooseleaf\nloosely\nloosen\nlooseness\nloosening\nloosestrife\nloot\nlooted\nlooting\nlop\nlop-eared\nlope\nlophiidae\nlophius\nlophodytes\nlopholatilus\nlophophora\nlophophorus\nlophosoria\nlophosoriaceae\nlopped\nlopper\nlopsided\nlopsidedly\nlopsidedness\nloquacious\nloquaciously\nloquaciousness\nloquacity\nloquat\nloquendi\nloquimur\nloquitur\nlora\nloranthaceae\nloranthus\nlorcha\nlorchel\nlord\nlordless\nlordling\nlordly\nlordolatry\nlordosis\nlords\nlordship\nlore\nlorelei\nlorette\nlorettine\nlorgnette\nlorica\nloricata\nloricated\nlorication\nloriinae\nlorikeet\nlorisidae\nlorn\nlorraine\nlorry\nlory\nlose\nlosel\nloser\nlosing\nlosing(a)\nlosings\nloss\nlost\nlot\nlota\nloth\nlothario\nloti\nlotion\nloto\nlots\nlottery\nlotto\nlotus\nlouche\nloud\nloud-mouthed\nloud-voiced\nlouder\nloudly\nloudmouth\nloudness\nloudspeaker\nlough\nlouisiana\nlouisianan\nlouisville\nlounge\nlounger\nloup\nloupe\nloupgarou\nloups\nlouse\nlousy\nlout\nloutish\nlouvar\nlouver\nlouvered\nlouvre\nlovable\nlovage\nlovastatin\nlove\nlove-in-a-mist\nlove-in-winter\nlove-lies-bleeding\nlove-token\nlovebird\nloved\nloveless\nloveliness\nlovelock\nlovelorn\nlovely\nlovemaking\nlover\nloverlike\nlovers\nloves\nlovesick\nlovesickness\nlovesong\nloving\nloving-kindness\nlovingkindness\nlovingness\nlovoa\nlow\nlow-backed\nlow-beam(a)\nlow-ceilinged\nlow-cost\nlow-cut\nlow-density(a)\nlow-density(p)\nlow-grade\nlow-key\nlow-level\nlow-lying\nlow-pitched\nlow-pressure\nlow-resolution\nlow-rise\nlow-sudsing\nlow-tech\nlow-tension\nlow-warp-loom\nlowborn\nlowbrow\nlowell\nlower\nlower-class\nlower-middle-class\nlowercase\nlowerclassman\nlowered\nlowering\nloweringly\nlowest\nlowland\nlowlander\nlowlands\nlowlihood\nlowliness\nlowly\nlowminded\nlown\nlowness\nlowring\nlowthoughted\nlowtoned\nlox\nloxia\nloxodonta\nloxoma\nloxomataceae\nloxostege\nloy\nloyal\nloyalist\nloyally\nloyalty\nloyaut\nloyaute\nlozenge\nlp\nlq\nlsd\nluanda\nluba\nlubbard\nlubber\nlubberly\nlubricant\nlubricate\nlubricated\nlubricating\nlubrication\nlubricious\nlubricitate\nlubricity\nlubricous\nlubrification\nlucan\nlucanidae\nluce\nlucendo\nlucent\nlucid\nlucida\nlucidity\nlucidly\nlucidness\nlucidus\nlucifer\nluciferin\nluciferous\nlucific\nlucilia\nlucimeter\nlucite\nlucius\nluck\nluckless\nlucknow\nlucky\nlucrative\nlucre\nlucretia\nlucretius\nlucri\nluctation\nlucubration\nluculent\nlucus\nlucy\nlud\nluddite\nluddites\nludere\nludian\nludibrious\nludicrous\nludlams\nludo\nluengo\nluetic\nlufengpithecus\nluff\nluffa\nlug\nluganda\nluge\nluger\nluggage\nlugger\nluggy\nluging\nlugsail\nlugubrious\nlugubriously\nlugworm\nluke\nlukes\nlukewarm\nlukewarmly\nlukewarmness\nlull\nlullaby\nlulld\nlumbago\nlumbar\nlumber\nlumberhouse\nlumbering\nlumberman\nlumbermill\nlumberyard\nlumbriciform\nlumen\nluminary\nluminescence\nluminescent\nluminiferous\nluminosity\nluminous\nluminousness\nlummox\nlump\nlumpectomy\nlumpenus\nlumper\nlumpfish\nlumping\nlumpisb\nlumpish\nlumpkin\nlumpsucker\nlumpy\nluna\nlunacy\nlunar\nlunaria\nlunate\nlunatic\nlunation\nlunch\nluncheon\nluncher\nlunching\nlunchroom\nlunchtime\nlund\nlunda\nlune\nlunette\nlung\nlung-power\nlunge\nlungfish\nlungi\nlungs\nluniform\nlunisolar\nlunkhead\nlunula\nlunular\nlunule\nluo\nluoyang\nlupanar\nlupine\nlupinus\nlupus\nlurch\nlurching\nlure\nlurid\nluridly\nlurk\nlurker\nlurking\nlusaka\nluscinia\nluscious\nlusciously\nlush\nlushy\nlusitania\nlusitanian\nlusk\nlusory\nlust\nluster\nlusterware\nlustful\nlustfully\nlustihood\nlustily\nlustless\nlustquencher\nlustration\nlustrous\nlustrum\nlusty\nlusus\nlute\nlutefisk\nluteous\nlutetium\nluther\nlutheran\nlutheranism\nlutist\nlutjanidae\nlutjanus\nlutose\nlutra\nlutrinae\nlutzen\nluvaridae\nluvarus\nluwian\nlux\nluxation\nluxe\nluxembourg\nluxembourgian\nluxemburger\nluxor\nluxuriance\nluxuriant\nluxuriantly\nluxuriate\nluxurious\nluxuriously\nluxuriousness\nluxury\nluyia\nluzon\nlwei\nly\nlycaena\nlycaenid\nlycaenidae\nlycaeon\nlycanthropy\nlyceum\nlychgate\nlychnis\nlycian\nlycium\nlycoperdaceae\nlycoperdales\nlycoperdon\nlycopersicon\nlycophyta\nlycopodiaceae\nlycopodiales\nlycopodineae\nlycopodium\nlycopsida\nlycopus\nlycosa\nlycosidae\nlyddite\nlydford\nlydian\nlye\nlygaeid\nlygaeidae\nlyginopteris\nlygodium\nlygus\nlying\nlying(a)\nlyking\nlymantria\nlymantriid\nlymantriidae\nlymph\nlymphangioma\nlymphatic\nlymphoblast\nlymphocyte\nlymphocytic\nlymphoid\nlymphoma\nlynch\nlynchburg\nlyncher\nlynching\nlynx\nlynxeyed\nlyon\nlyonia\nlyonnais\nlyonnaise\nlyophilize\nlyophilized\nlyra\nlyram\nlyrate\nlyre\nlyrebird\nlyric\nlyrical\nlyricality\nlyrically\nlyricism\nlyricist\nlyrist\nlyrurus\nlysander\nlyse\nlysichiton\nlysiloma\nlysimachia\nlysimachus\nlysine\nlysis\nlysol\nlythraceae\nlythrum\nlytton\nm\nma\nmaalox\nmaana\nmab\nmabap\nmac\nmacaca\nmacadam\nmacadamia\nmacadamize\nmacaire\nmacao\nmacaque\nmacaroni\nmacaronic\nmacaronics\nmacaroon\nmacaw\nmacbeth\nmacbethl\nmace\nmacebearer\nmacedoine\nmacedon\nmacedonian\nmacerate\nmaceration\nmacerative\nmachaeranthera\nmacheath\nmachete\nmachiavel\nmachiavelian\nmachiavelism\nmachiavelli\nmachiavellian\nmachiavellianism\nmachicolated\nmachicolation\nmachilidae\nmachina\nmachinal\nmachination\nmachinations\nmachinator\nmachine\nmachine-accessible\nmachine-made\nmachinery\nmaching\nmachinist\nmachismo\nmachmeter\nmacht\nmacilency\nmacilent\nmacintosh\nmackenzie\nmackerel\nmackinaw\nmackintosh\nmackle\nmacleaya\nmaclura\nmacon\nmacoun\nmacowanites\nmacrame\nmacrencephalic\nmacrencephaly\nmacro\nmacrobiotic\nmacrobiotics\nmacrocephalic\nmacrocephalon\nmacrocephaly\nmacrocheira\nmacroclemys\nmacrocolous\nmacrocosm\nmacrocosmic\nmacrocytosis\nmacrodactylus\nmacrogamete\nmacroglossia\nmacrology\nmacromolecular\nmacromolecule\nmacron\nmacronectes\nmacrophage\nmacropodidae\nmacropus\nmacrorhamphosidae\nmacroscopic\nmacroscopically\nmacrothelypteris\nmacrotis\nmacrotus\nmacrotyloma\nmacrouridae\nmacrozamia\nmacrozoarces\nmacsycophant\nmactation\nmacte\nmacula\nmaculate\nmaculation\nmacule\nmacumba\nmacushla\nmad\nmadagascan\nmadagascar\nmadam\nmadame\nmadbrained\nmadcap\nmadden\nmaddened\nmadder\nmadderwort\nmadding\nmade\nmade-up\nmadefaction\nmadeira\nmadia\nmadid\nmadison\nmadly\nmadman\nmadness\nmadonna\nmadras\nmadreporaria\nmadrid\nmadrigal\nmadrigalist\nmadrilene\nmadrona\nmadstone\nmadwoman\nmaeandra\nmaelstrom\nmaenad\nmaestoso\nmaestro\nmafia\nmagadhan\nmagazine\nmagdalen\nmage\nmagellan\nmagenta\nmaggior\nmaggiore\nmaggire\nmaggot\nmaggoty\nmaggotyheaded\nmagh\nmagi\nmagic\nmagical\nmagically\nmagician\nmagicicada\nmagilp\nmagister\nmagisterial\nmagistery\nmagistracy\nmagistrate\nmagistrature\nmagistri\nmagma\nmagna\nmagnanimity\nmagnanimous\nmagnanimously\nmagnas\nmagnate\nmagnates\nmagnatum\nmagnesite\nmagnesium\nmagnet\nmagnetic\nmagnetically\nmagnetism\nmagnetite\nmagnetization\nmagnetize\nmagneto\nmagnetohydrodynamics\nmagnetometer\nmagneton\nmagnetosphere\nmagnetron\nmagni\nmagnificat\nmagnification\nmagnificence\nmagnificent\nmagnificently\nmagnificio\nmagnifico\nmagnifier\nmagnifique\nmagnify\nmagniloquence\nmagniloquent\nmagnislat\nmagnitude\nmagno\nmagnolia\nmagnoliaceae\nmagnoliidae\nmagnos\nmagnum\nmagnus\nmagog\nmagpie\nmagsman\nmaguey\nmagus\nmah-jongg\nmahabharata\nmaharaja\nmaharajah\nmaharani\nmaharashtra\nmahatma\nmahayana\nmahayanist\nmahlstick\nmahoe\nmahogany\nmahonia\nmahout\nmahuang\nmai\nmaianthemum\nmaid\nmaidan\nmaiden\nmaidenhair\nmaidenhead\nmaidenhood\nmaidenlike\nmaidenliness\nmaidenly\nmaidenvirgin\nmaidservant\nmaidu\nmaigre\nmail\nmail-clad\nmailable\nmailbag\nmailboat\nmailbox\nmaildrop\nmailer\nmailing\nmaillot\nmailman\nmailsorter\nmailstate\nmaim\nmaimed\nmain\nmain(a)\nmain-topmast\nmain-topsail\nmaine\nmainer\nmainframe\nmainland\nmainly\nmainmast\nmainpernor\nmainsail\nmainspring\nmainstay\nmainstream\nmainstreamed\nmaintain\nmaintainable\nmaintained\nmaintaining\nmaintenance\nmaintien\nmais\nmaison\nmaisonnette\nmaitre\nmaitreya\nmaja\nmajeste\nmajestic\nmajestical\nmajestically\nmajesty\nmajeure\nmajidae\nmajolica\nmajor\nmajor(ip)\nmajor-domo\nmajor-general\nmajorana\nmajorca\nmajordomo\nmajori\nmajority\nmajorum\nmajuscular\nmajuscule\nmakaira\nmakalu\nmake\nmake-believe\nmakebelieve\nmakepeace\nmaker\nmakeready\nmakes\nmakeshift\nmakeup\nmakeweight\nmaking\nmako\nmakomako\nmal\nmala\nmalabo\nmalabsorption\nmalacanthidae\nmalacca\nmalachite\nmalacia\nmalaclemys\nmalacology\nmalaconotinae\nmalacopterygii\nmalacosoma\nmalacostraca\nmalacothamnus\nmaladaptive\nmalade\nmaladie\nmaladjusted\nmaladjustive\nmaladjustment\nmaladministration\nmaladroit\nmaladroitly\nmalady\nmalaise\nmalamute\nmalapert\nmalaprop\nmalapropism\nmalapropos\nmalaria\nmalarial\nmalathion\nmalawi\nmalawian\nmalaxis\nmalay\nmalayalam\nmalayo-polynesian\nmalaysia\nmalaysian\nmalcolmia\nmalconformation\nmalcontent\nmaldives\nmaldivian\nmaldon\nmaldu\nmale\nmaleate\nmalebat\nmaleberry\nmalecite\nmalediction\nmalefaction\nmalefactor\nmalefic\nmaleficence\nmaleficent\nmaleness\nmaleo\nmalevolence\nmalevolent\nmalevolently\nmalevolus\nmalfeasance\nmalfeasant\nmalformation\nmalfunction\nmalfunctioning\nmalgre\nmali\nmalian\nmalice\nmalicious\nmaliciously\nmaliciousness\nmalign\nmalignancy\nmalignant\nmalignantly\nmaligned\nmalignity\nmalinger\nmalingerer\nmalingering\nmalinois\nmalis\nmalison\nmalkin\nmall\nmallard\nmalleability\nmalleable\nmallee\nmallet\nmalleus\nmallophaga\nmallotus\nmallow\nmalm\nmalmo\nmalmsey\nmalnourished\nmalnutrition\nmalo\nmalocclusion\nmalodor\nmalodorous\nmalodorousness\nmalope\nmalopterurus\nmalosma\nmalpighia\nmalpighiaceae\nmalposed\nmalposition\nmalpractice\nmalt\nmalta\nmalted\nmaltese\nmaltha\nmalthus\nmalthusian\nmalthusianism\nmalto\nmaltose\nmaltreat\nmaltreatment\nmaltster\nmalum\nmalus\nmalva\nmalvaceae\nmalvales\nmalvasia\nmalvastrum\nmalvaviscus\nmalversation\nmam\nmama\nmamba\nmamelon\nmameluke\nmamey\nmamma\nmammal\nmammalia\nmammalian\nmammalogy\nmammary\nmammea\nmammet\nmammiliform\nmammilla\nmammillaria\nmammogram\nmammography\nmammon\nmammoth\nmammothermography\nmammut\nmammuthus\nmammutidae\nmammy\nmamo\nman\nman-at-arms\nman-made\nman-of-the-earth\nman-of-war\nman-on-a-horse\nman-sized\nman-to-man\nmanacle\nmanage\nmanageability\nmanageable\nmanageably\nmanaged\nmanagement\nmanager\nmanageress\nmanagerial\nmanagership\nmanagery\nmanagua\nmanakin\nmanama\nmanannan\nmanatee\nmanawydan\nmanbird\nmanche\nmanchester\nmanchu\nmanchuria\nmanchurian\nmancipation\nmanciple\nmancunian\nmanda\nmandalay\nmandamus\nmandara\nmandarin\nmandatary\nmandate\nmande\nmandevilla\nmandible\nmandibular\nmandibulate\nmandibulofacial\nmandola\nmandolin\nmandoline\nmandragora\nmandrake\nmandrel\nmandrill\nmandrillus\nmanduca\nmanducation\nmane\nmaneater\nmanege\nmanes\nmanet\nmaneuver\nmaneuverability\nmaneuverable\nmaneuverer\nmaneuvering\nmanful\nmanfully\nmanfulness\nmangabey\nmanganate\nmanganese\nmanganite\nmange\nmangel-wurzel\nmangent\nmanger\nmangifera\nmangily\nmangle\nmangled\nmango\nmangosteen\nmangrove\nmangy\nmanhattan\nmanhole\nmanhood\nmanhunt\nmania\nmaniac\nmaniacal\nmaniacally\nmanibus\nmanic-depressive\nmanicheism\nmanichord\nmaniclike\nmanicotti\nmanicure\nmanicurist\nmanidae\nmanie\nmaniere\nmanifest\nmanifestation\nmanifested\nmanifestly\nmanifesto\nmanifold\nmanihot\nmanikin\nmanila\nmanilkara\nmaniple\nmanipulability\nmanipulate\nmanipulation\nmanipulative\nmaniraptor\nmaniraptora\nmanis\nmanito\nmanitoba\nmanitou\nmanitu\nmanjapanese\nmankind\nmanlike\nmanliness\nmanly\nmann\nmanna\nmanned\nmannequin\nmanner\nmannered\nmannerism\nmannerist\nmannerly\nmanners\nmannheim\nmannikin\nmannish\nmannitol\nmanofwar\nmanofwars\nmanometer\nmanomotor\nmanor\nmanorhouse\nmanorial\nmanque\nmans\nmansard\nmanse\nmanservant\nmansi\nmansion\nmanslaughter\nmansuetude\nmanta\nmantel\nmantelet\nmanteodea\nmanticore\nmantidae\nmantilla\nmantinea\nmantis\nmantispid\nmantispidae\nmantissa\nmantle\nmantleshelf\nmantlet\nmantology\nmantra\nmantrap\nmantua\nmanu\nmanual\nmanual(a)\nmanually\nmanubial\nmanubrium\nmanufactory\nmanufacture\nmanufactured\nmanufacturer\nmanul\nmanumission\nmanumit\nmanure\nmanus\nmanuscript\nmanx\nmany\nmany-sided\nmanycolored\nmanyheaded\nmanyhued\nmanysided\nmanytongued\nmanzanilla\nmanzanita\nmao\nmaoism\nmaoist\nmaori\nmap\nmap-reader\nmapinguari\nmaple\nmaple-leaf\nmapmaking\nmapping\nmaputo\nmaquiladora\nmaquis\nmar\nmara\nmarabou\nmarabout\nmaraco\nmarah\nmaranatha\nmarang\nmaranta\nmarantaceae\nmarasca\nmaraschino\nmarasmius\nmarasmus\nmaratha\nmarathi\nmarathon\nmarathoner\nmarattia\nmarattiaceae\nmarattiales\nmaraud\nmarauder\nmarauding\nmarble\nmarbleconstant\nmarbled\nmarblehearted\nmarbleizing\nmarbles\nmarblewood\nmarbling\nmarc\nmarch\nmarchantia\nmarchantiaceae\nmarchantiales\nmarche\nmarcher\nmarches\nmarching\nmarchioness\nmarcid\nmarconigram\nmarcor\nmardi\nmarduk\nmare\nmarechal\nmarek\nmarengo\nmares\nmarescent\nmargaret\nmargarin\nmargarine\nmargarita\nmargate\nmargay\nmargin\nmarginal\nmarginality\nmarginally\nmarginated\nmarginocephalia\nmargrave\nmargravine\nmarguerite\nmari\nmaria\nmariachi\nmariage\nmarial\nmarian\nmaricopa\nmarigold\nmarigraph\nmarijuana\nmarimba\nmarina\nmarinade\nmarinara\nmarine\nmarineland\nmariner\nmarines\nmarinism\nmariolatry\nmarionette\nmarionettes\nmariposa\nmariposan\nmarish\nmarital\nmaritiime\nmaritime\nmarjoram\nmark\nmarkbelow\nmarked\nmarkedly\nmarker\nmarket\nmarketable\nmarketing\nmarketovert\nmarketplace\nmarkhor\nmarking\nmarkka\nmarks\nmarksman\nmarksmanship\nmarkup\nmarl\nmarland\nmarlberry\nmarlin\nmarline\nmarlinespike\nmarmalade\nmarmara\nmarmite\nmarmoreal\nmarmoream\nmarmoset\nmarmot\nmarmota\nmarocain\nmaroon\nmarooned\nmarowbones\nmarplot\nmarque\nmarquee\nmarquess\nmarquetry\nmarquis\nmarquisate\nmarred\nmarriage\nmarriageability\nmarriageable\nmarriages\nmarried\nmarrow\nmarrowbone\nmarrowbones\nmarrowless\nmarrubium\nmarry\nmars\nmarsala\nmarseillaise\nmarseilles\nmarsh\nmarshal\nmarshall\nmarshalsea\nmarshalship\nmarshmallow\nmarshy\nmarsilea\nmarsileaceae\nmarsorange\nmarsupial\nmarsupialia\nmarsupium\nmart\nmarte\nmartello\nmarten\nmartensite\nmartes\nmartial\nmartial(a)\nmartially\nmartian\nmartin\nmartinet\nmartingale\nmartini\nmartinihenry\nmartinique\nmartinmas\nmartins\nmartydom\nmartynia\nmartyniaceae\nmartyr\nmartyrdom\nmarumi\nmarupa\nmarut\nmarvel\nmarvelous\nmarvelously\nmarx\nmarxist\nmarxist-leninist\nmary\nmaryland\nmarzipan\nmas\nmasa\nmasai\nmasaniello\nmascara\nmascot\nmascotte\nmasculine\nmasculinity\nmasculinization\nmasdevallia\nmaser\nmaseru\nmash\nmasher\nmashhad\nmashi\nmashie\nmasjid\nmask\nmasked\nmasker\nmasking\nmasochism\nmasochist\nmasochistic\nmasochistically\nmason\nmasonic\nmasonite\nmasonry\nmasorah\nmasque\nmasquerade\nmass\nmass-produced\nmass-spectrometric\nmassachuset\nmassachusetts\nmassacre\nmassage\nmassager\nmassasauga\nmasse\nmassein\nmasses\nmasseur\nmassif\nmassinger\nmassive\nmassively\nmassy\nmast\nmastaba\nmastalgia\nmastectomy\nmasted\nmaster\nmaster(a)\nmaster-at-arms\nmasterdom\nmasterfully\nmastering\nmasterkey\nmasterly\nmastermind\nmasterpiece\nmastership\nmasterstroke\nmasterwriters\nmastery\nmasthead\nmastic\nmasticate\nmastication\nmasticophis\nmastiff\nmastigomycota\nmastigophora\nmastigoproctus\nmastitis\nmastodon\nmastoid\nmastoidal\nmastoidectomy\nmastoiditis\nmastology\nmastotermes\nmastotermitidae\nmasturbation\nmasturbator\nmat\nmatador\nmatai\nmatakam\nmatch\nmatchboard\nmatchbook\nmatchbox\nmatched\nmatchless\nmatchlock\nmatchmaker\nmatchreach\nmatchstick\nmatchweed\nmatchwood\nmate\nmated\nmateless\nmatelote\nmater\nmaterfamilias\nmateria\nmaterial\nmaterialism\nmaterialist\nmaterialistic\nmaterialistically\nmateriality\nmaterialization\nmaterialize\nmaterially\nmaterialness\nmaterials\nmateriam\nmateriel\nmaternal\nmaternalistic\nmaternally\nmaternity\nmath\nmathematical\nmathematically\nmathematician\nmathematics\nmathesis\nmatin\nmatine\nmatinee\nmatins\nmatrass\nmatriarchal\nmatriarchic\nmatriarchy\nmatricaria\nmatricentric\nmatricide\nmatriculate\nmatriculation\nmatrilineage\nmatrilineal\nmatrilineally\nmatrimonial\nmatrimonii\nmatrimony\nmatrix\nmatron\nmatronage\nmatronhood\nmatronize\nmatronly\nmatronymic\nmatross\nmatsyendra\nmatted\nmatter\nmatter-of-course\nmatter-of-fact\nmatterhorn\nmatteroffact\nmatters\nmatteuccia\nmatthew\nmatthiola\nmatting\nmattock\nmattole\nmattre\nmattress\nmaturate\nmaturation\nmaturational\nmature\nmaturely\nmaturine\nmaturity\nmatutinal\nmatzo\nmaud\nmauder\nmaudlin\nmauers\nmauger\nmaui\nmaukin\nmaul\nmaulstick\nmaund\nmaunder\nmaundering\nmaure\nmauritania\nmauritanian\nmauritian\nmauritius\nmauser\nmausoleum\nmauvais\nmauvaise\nmauve\nmauvis\nmaux\nmaverick\nmavis\nmaw\nmawkish\nmawkishly\nmawkishness\nmawworm\nmax\nmaxi\nmaxillaria\nmaxillary\nmaxillodental\nmaxillofacial\nmaxillomandibular\nmaxim\nmaximal\nmaximally\nmaxime\nmaximis\nmaximization\nmaximizing\nmaxims\nmaximum\nmaxostoma\nmaxwell\nmaxzide\nmay\nmaya\nmayaca\nmayacaceae\nmayan\nmayapple\nmaybe\nmayday\nmayeng\nmayenne\nmayetiola\nmayflower\nmayfly\nmayhap\nmayhaw\nmayhem\nmaying\nmayonnaise\nmayor\nmayoral\nmayoralty\nmayoress\nmayors\nmaypole\nmaypop\nmayweed\nmazama\nmazard\nmaze\nmazed\nmazer\nmazurka\nmazy\nmb\nmbabane\nmc\nmccarthyism\nmccoy\nmcintosh\nmckinley\nmd\nme\nmea\nmead\nmeadow\nmeadowgrass\nmeadowlark\nmeager\nmeagerly\nmeagerness\nmeal\nmealie\nmealtime\nmealworm\nmealy\nmealybug\nmealymouthed\nmealymouthedness\nmeam\nmean\nmeander\nmeandering\nmeandering(a)\nmeanderingly\nmeandrous\nmeandry\nmeanest\nmeanie\nmeaning\nmeaning(a)\nmeaningful\nmeaningfully\nmeaningfulness\nmeaningless\nmeaninglessness\nmeanings\nmeanly\nmeanness\nmeans\nmeanspirited\nmeant\nmeantime\nmeanwhile\nmeasles\nmeasly\nmeasurable\nmeasurably\nmeasure\nmeasured\nmeasuredly\nmeasureless\nmeasurement\nmeasures\nmeasuring\nmeat\nmeatball\nmeatless\nmeatus\nmeaty\nmebendazole\nmecaenas\nmecate\nmecca\nmechanic\nmechanical\nmechanically\nmechanician\nmechanics\nmechanism\nmechanistic\nmechanistically\nmechanized\nmeclizine\nmeclofenamate\nmeconium\nmeconopsis\nmecum\nmedaglia\nmedal\nmedalist\nmedallion\nmedallist\nmeddle\nmeddler\nmeddlesome\nmeddling\nmedea\nmedecin\nmedecine\nmedeival\nmedendo\nmedes\nmedia\nmediacy\nmedial\nmedially\nmedian\nmedian(a)\nmediant\nmedias\nmediastinum\nmediate\nmediated\nmediation\nmediatization\nmediatize\nmediator\nmediatorial\nmediatorship\nmediatory\nmedic\nmedica\nmedicago\nmedicaid\nmedical\nmedically\nmedicament\nmedicare\nmedicaster\nmedicate\nmedication\nmedicative\nmedicatrix\nmedicina\nmedicinal\nmedicinally\nmedicine\nmedicolegal\nmediety\nmedieval\nmedievalism\nmedievalist\nmediis\nmedinilla\nmedio\nmediocre\nmediocritas\nmediocrity\nmeditate\nmeditation\nmeditative\nmeditatively\nmediterranean\nmedium\nmedlar\nmedley\nmedoc\nmedroxyprogesterone\nmedulla\nmedullary\nmedusa\nmedusoid\nmeed\nmeek\nmeekly\nmeekness\nmeerkat\nmeerschaum\nmeet\nmeeting\nmeetinghouse\nmeets\nmegabit\nmegabyte\nmegachile\nmegachilidae\nmegachiroptera\nmegacolon\nmegacosm\nmegadeath\nmegaderma\nmegadermatidae\nmegaera\nmegahertz\nmegakaryocyte\nmegakaryocytic\nmegalith\nmegalithic\nmegalobatrachus\nmegaloblast\nmegaloblastic\nmegalocyte\nmegalomania\nmegalomaniac\nmegalomaniacal\nmegalonychidae\nmegalopolis\nmegaloptera\nmegalosaur\nmegalosauridae\nmegaphone\nmegapode\nmegapodiidae\nmegapodius\nmegaptera\nmegascope\nmegascopic\nmegaspore\nmegatherian\nmegatheriidae\nmegatherium\nmegaton\nmegawatt\nmegesterol\nmegilp\nmeglio\nmegohm\nmegrims\nmehr\nmeiden\nmein\nmeiosis\nmeiotic\nmeistersinger\nmekong\nmel\nmelagra\nmelamine\nmelampodium\nmelampsora\nmelampsoraceae\nmelancholia\nmelancholic\nmelancholy\nmelanerpes\nmelanesia\nmelange\nmelanin\nmelanitta\nmelanoblast\nmelanocyte\nmelanoderma\nmelanogrammus\nmelanoma\nmelanoplus\nmelanosis\nmelanotis\nmelanthiaceae\nmelastoma\nmelastomataceae\nmelatonin\nmelbourne\nmeld\nmeleagrididae\nmeleagris\nmelee\nmelena\nmeles\nmelia\nmeliaceae\nmelibean\nmelic\nmelicoccus\nmelilotus\nmelinae\nmelinite\nmeliora\nmeliorate\nmelioration\nmelioribus\nmelioris\nmeliorism\nmeliphagidae\nmelissa\nmelius\nmelliferous\nmellifluous\nmellivora\nmellow\nmellowingly\nmellowly\nmellowness\nmelocactus\nmelodic\nmelodically\nmelodious\nmelodiously\nmelodiousness\nmelodist\nmelodrama\nmelodramatic\nmelodramatically\nmelodrame\nmelody\nmelogale\nmeloidae\nmelolontha\nmelolonthidae\nmelon\nmelophagus\nmelopsittacus\nmelosa\nmelospiza\nmelphalan\nmelpomene\nmelt\nmeltable\nmeltdown\nmelted\nmelting\nmelursus\nmelville\nmem\nmember\nmembers\nmembership\nmembra\nmembracidae\nmembrane\nmembranous\nmeme\nmemento\nmeminisse\nmemo\nmemoir\nmemor\nmemorabilia\nmemorable\nmemorably\nmemorandum\nmemorandumbook\nmemorem\nmemoria\nmemoriae\nmemorial\nmemorialist\nmemorialize\nmemorials\nmemoriam\nmemoriter\nmemorization\nmemorize\nmemorizer\nmemory\nmemphis\nmemsahib\nmen\nmenace\nmenacing\nmenacingly\nmenage\nmenagerie\nmenagery\nmenaing\nmenarche\nmend\nmendacem\nmendacia\nmendacior\nmendacious\nmendaciously\nmendacity\nmendel\nmendelevium\nmendeleyev\nmendelian\nmendelism\nmender\nmendez\nmendicancy\nmendicant\nmendicate\nmendicity\nmending\nmene\nmenhaden\nmenhir\nmenial\nmenially\nmeningeal\nmeningioma\nmeningism\nmeningitis\nmeningocele\nmeninx\nmenippe\nmeniscectomy\nmeniscium\nmeniscus\nmenispermaceae\nmenispermum\nmennonite\nmennonitism\nmenomini\nmenopausal\nmenopause\nmenopon\nmenorrhagia\nmenorrhea\nmenotyphla\nmens\u0003\nmens\nmensal\nmensch\nmenses\nmensongesfrench\nmenstrual\nmenstruating\nmenstruation\nmenstruum\nmensural\nmensuration\nmental\nmentalism\nmentality\nmentally\nmente\nmentem\nmenteur\nmentha\nmenthe\nmenthol\nmentholated\nmenticirrhus\nmenticulture\nmention\nmentioned\nmentioning\nmentira\nmentis\nmentor\nmentum\nmentzelia\nmenu\nmenura\nmenurae\nmenuridae\nmenyanthaceae\nmenyanthes\nmenziesia\nmeow\nmeperidine\nmephenytoin\nmephistopheles\nmephistophelian\nmephitic\nmephitinae\nmephitis\nmephobarbital\nmepriser\nmeprobamate\nmer\nmeracious\nmeralgia\nmerbromine\nmercantile\nmercantilism\nmercaptopurine\nmercatoria\nmercature\nmercedario\nmercenaria\nmercenary\nmercenary(a)\nmercer\nmerces\nmerchandise\nmerchang\nmerchangman\nmerchant\nmerci\nmercies\nmerciful\nmercifully\nmercifulness\nmerciless\nmercilessly\nmercilessness\nmercurial\nmercurialis\nmercuric\nmercurius\nmercurous\nmercury\nmercurys\nmercy\nmercys\nmere\nmere(a)\nmeredith\nmerelles\nmerely\nmerestead\nmerestone\nmeretricious\nmeretriciously\nmerfolk\nmerganser\nmerge\nmerged\nmerginae\nmerging\nmergus\nmerida\nmeridian\nmeridional\nmeridith\nmeringue\nmerino\nmeriones\nmeristem\nmerit\nmerited\nmeriting\nmerito\nmeritocractic\nmeritocracy\nmeritorious\nmeritoriously\nmerlangus\nmerlin\nmerluccius\nmermaid\nmerman\nmero\nmerodoam\nmerogenesis\nmeromelia\nmeromotu\nmeronym\nmeronymy\nmeropidae\nmerops\nmerostomata\nmerozoite\nmerrick\nmerriment\nmerry\nmerryandrew\nmerrygoround\nmerrymaking\nmerrythought\nmertensia\nmeruit\nmerveille\nmeryta\nmesa\nmesalliance\nmescal\nmescaline\nmesembryanthemum\nmesenchyme\nmesenteric\nmesentery\nmesh\nmeshed\nmeshes\nmesial\nmesic\nmesilla\nmesmer\nmesmerism\nmesmerist\nmesmerize\nmesne\nmesoblastic\nmesocolon\nmesocricetus\nmesoderm\nmesohippus\nmesolithic\nmesomorph\nmesomorphic\nmeson\nmesonic\nmesophyte\nmesophytic\nmesopotamia\nmesosphere\nmesothelioma\nmesothelium\nmesozoic\nmespilus\nmesquite\nmess\nmessage\nmessalina\nmessenger\nmessiah\nmessiahship\nmessianic\nmessidor\nmessily\nmessina\nmessmate\nmessuage\nmessy\nmestee\nmestizo\nmestranol\nmesua\nmet\nmetabolic\nmetabolism\nmetabolite\nmetacarpal\nmetacarpus\nmetacenter\nmetacentric\nmetachronism\nmetage\nmetagenesis\nmetagrammatism\nmetagrobolized\nmetaknowledge\nmetal\nmetalanguage\nmetalepsis\nmetallic\nmetallike\nmetallography\nmetalloid\nmetallurgical\nmetallurgist\nmetallurgy\nmetals\nmetalwork\nmetalworking\nmetamathematics\nmetamere\nmetameric\nmetamorphic\nmetamorphism\nmetamorphopsia\nmetamorphose\nmetamorphosis\nmetamorphous\nmetaphase\nmetaphor\nmetaphorical\nmetaphorically\nmetaphrase\nmetaphrast\nmetaphrastic\nmetaphysical\nmetaphysically\nmetaphysician\nmetaphysics\nmetaphysis\nmetaplasm\nmetaproterenol\nmetarule\nmetasequoia\nmetastasis\nmetastatic\nmetatarsal\nmetatarsus\nmetatheria\nmetatherian\nmetathesis\nmetazoa\nmete\nmetemphirical\nmetempsychosis\nmetencephalon\nmeteor\nmeteoric\nmeteorite\nmeteoritic\nmeteoroid\nmeteorologic\nmeteorological\nmeteorologically\nmeteorologist\nmeteorology\nmeteoromancy\nmeteors\nmeteortropism\nmeter\nmethacholine\nmethadone\nmethamphetamine\nmethane\nmethanogen\nmethanol\nmethapyrilene\nmethaqualone\nmetharbital\nmetheglin\nmethenamine\nmethicillin\nmethionine\nmethocarbamol\nmethod\nmethodical\nmethodically\nmethodism\nmethodist\nmethodize\nmethodological\nmethodologically\nmethodology\nmethotrexate\nmethuselah\nmethyl\nmethylated\nmethyldopa\nmethylenedioxymethamphetamine\nmethylphenidate\nmethyltestosterone\nmetic\nmetical\nmeticulous\nmeticulously\nmeticulousness\nmetier\nmetimur\nmetis\nmetogenesis\nmetonym\nmetonymic\nmetonymically\nmetonymy\nmetoposcopy\nmetoprolol\nmetralgia\nmetric\nmetrical\nmetrically\nmetrification\nmetritis\nmetro\nmetrology\nmetron/gr\nmetronome\nmetropolis\nmetropolitan\nmetroptosis\nmetrorrhagia\nmetroxylon\nmettle\nmettlesome\nmettlesomeness\nmettre\nmetuit\nmeum\nmeurt\nmeus\nmeuse\nmew\nmewed\nmewl\nmews\nmexican\nmexico\nmexiletine\nmezereon\nmezereum\nmezzanine\nmezzo\nmezzo-relievo\nmezzo-soprano\nmezzofanti\nmezzorilevo\nmezzorilievo\nmezzotint\nmf\nmflops\nmg\nmh\nmho\nmi\nmiami\nmiasm\nmiasma\nmiasmal\nmiasmic\nmica\nmicaceous\nmicawber\nmice\nmicelle\nmich\nmichaelmas\nmichaelmastide\nmichelangelesque\nmichelangelo\nmichigan\nmichigander\nmick\nmicmac\nmicomicon\nmicro\nmicrobalance\nmicrobe\nmicrobial\nmicrobiology\nmicrobrachia\nmicrocentrum\nmicrocephalic\nmicrocephaly\nmicrochiroptera\nmicrococcaceae\nmicrococcus\nmicrocosm\nmicrocosmic\nmicrocrystalline\nmicrocyte\nmicrocytosis\nmicrodesmidae\nmicrodipodops\nmicrodot\nmicroelectronic\nmicroelectronics\nmicrofiche\nmicrofilm\nmicrofossil\nmicrogamete\nmicrogauss\nmicroglia\nmicrogliacyte\nmicrogram\nmicrogramma\nmicrography\nmicrohylidae\nmicromeria\nmicrometeoric\nmicrometeorite\nmicrometeoritic\nmicrometer\nmicromyx\nmicron\nmicronesia\nmicroorganism\nmicropaleontology\nmicrophone\nmicrophoning\nmicrophotometer\nmicropogonias\nmicroprocessor\nmicropterus\nmicropyle\nmicroradian\nmicroscope\nmicroscopic\nmicroscopically\nmicroscopist\nmicroscopy\nmicrosecond\nmicrosomal\nmicrosome\nmicrosorium\nmicrospore\nmicrosporidian\nmicrosporum\nmicrostomus\nmicrostrobos\nmicrotome\nmicrotubule\nmicrotus\nmicrowave\nmicrozeal\nmicrozoa\nmicruroides\nmicrurus\nmicteria\nmicturition\nmid\nmid(a)\nmid-april\nmid-august\nmid-december\nmid-february\nmid-january\nmid-july\nmid-june\nmid-march\nmid-may\nmid-november\nmid-october\nmid-off\nmid-on\nmid-september\nmid-water\nmidafternoon\nmidair\nmidas\nmidbrain\nmidcourse\nmidday\nmidden\nmiddle\nmiddle-aged\nmiddle-class\nmiddle-level\nmiddle-of-the-road\nmiddleaged\nmiddlebrow\nmiddleclass\nmiddleman\nmiddlemost\nmiddleweight\nmiddling\nmiddy\nmidfield\nmidgard\nmidge\nmidgrass\nmidi\nmidi-pyrenees\nmidinette\nmidiron\nmidland\nmidmost\nmidnight\nmidplane\nmidrib\nmidriff\nmidshipman\nmidships\nmidst\nmidstream\nmidsummer\nmidsummernights\nmidterm\nmidway\nmidweek\nmidwest\nmidwestern\nmidwife\nmidwifery\nmidwinter\nmien\nmieux\nmiff\nmight\nmight-have-been\nmightiest\nmightily\nmightiness\nmighty\nmignonette\nmigraine\nmigrant\nmigrate\nmigration\nmigratory\nmihi\nmikado\nmikania\nmikir-meithei\nmil\nmilady\nmilan\nmilanese\nmilch\nmild\nmild-mannered\nmildew\nmildewed\nmildly\nmildness\nmile\nmileage\nmilepost\nmiler\nmilestone\nmilier\nmilieu\nmilitant\nmilitare\nmilitarily\nmilitarism\nmilitarist\nmilitaristic\nmilitarized\nmilitary\nmilitat\nmilitate\nmilitia\nmilitiaman\nmilk\nmilk(a)\nmilk-white\nmilkcap\nmilkiness\nmilkless\nmilklivered\nmilkman\nmilkshake\nmilksop\nmilkwagon\nmilkweed\nmilkwhite\nmilkwort\nmilky\nmill\nmill-girl\nmill-hand\nmillboard\nmilldam\nmilled\nmillenarian\nmillenarianism\nmillenary\nmillenium\nmillennial\nmillennium\nmiller\nmiller's-thumb\nmillerite\nmillesimal\nmillet\nmillettia\nmilliammeter\nmilliampere\nmilliard\nmilliary\nmillibar\nmillicurie\nmillidegree\nmilliequivalent\nmillifarad\nmilligram\nmillihenry\nmilliliter\nmillime\nmillimeter\nmilline\nmilliner\nmillinery\nmilling\nmillion\nmillionaire\nmillionairess\nmillionfold\nmillions\nmillionth\nmillipede\nmilliradian\nmillisecond\nmillivolt\nmillivoltmeter\nmilliwatt\nmillpond\nmillrace\nmillstone\nmillwheel\nmillwork\nmillwright\nmilo\nmilord\nmilt\nmilton\nmiltonia\nmilvus\nmilwaukee\nmime\nmimeograph\nmimer\nmimesis\nmimetic\nmimic\nmimicking\nmimicry\nmimidae\nmimiery\nmimir\nmimium\nmimmitation\nmimographer\nmimosa\nmimosaceae\nmimosoideae\nmimus\nmin\nminacious\nminacity\nminaret\nminatorv\nminature\nminauderie\nmince\nmincemeat\nmincement\nmincer\nmincing\nmincingly\nmind\nmind-altering\nmind-bending\nmind-blowing\nmind-boggling\nminded\nminden\nmindful\nmindfully\nmindfulness\nmindless\nmindlessly\nmindnumbing\nmindoro\nminds\nmine\nmined\nminefield\nminelayer\nminer\nmineral\nmineralized\nmineralocorticoid\nmineralogist\nmineralogy\nminers\nminerva\nmineshaft\nminesweeper\nminesweeping\nming\nmingle\nmingled\nmingling\nmini\nminiature\nminiaturist\nminiaturization\nminibar\nminibike\nminicomputer\nminikin\nminim\nminima\nminimal\nminimally\nminimization\nminimize\nminimized\nminimum\nmining\nminion\nminis\nminister\nministerial\nministerially\nministering\nministers\nministrant\nministrat\nministrate\nministration\nministry\nminisub\nminium\nminivan\nminiver\nmink\nminneapolis\nminnesinger\nminnesota\nminnesotan\nminniebush\nminnow\nminnows\nminoan\nminocycline\nminor\nminor(ip)\nminore\nminorites\nminority\nminos\nminotaur\nminoxidil\nminsk\nminster\nminstrel\nminstrelsy\nmint\nmint(a)\nminuartia\nminuend\nminuet\nminus\nminuscular\nminuscule\nminute\nminutely\nminuteman\nminuteness\nminutes\nminutia\nminutiae\nminx\nmiocene\nmips\nmir\nmirabile\nmirabilis\nmiracle\nmiraculous\nmiraculously\nmirage\nmire\nmiri\nmiridae\nmiro\nmirounga\nmirror\nmirrored\nmirrorlike\nmirrors\nmirth\nmirthful\nmirthless\nmis\nmisacceptation\nmisadventure\nmisadvised\nmisalignment\nmisalliance\nmisanthrope\nmisanthropic\nmisanthropist\nmisanthropy\nmisapplication\nmisapply\nmisapprehend\nmisapprehension\nmisappropriate\nmisappropriation\nmisarrange\nmisbecome\nmisbecoming\nmisbegotten\nmisbehave\nmisbehavior\nmisbelief\nmisbelieve\nmisbeliever\nmisbranded\nmisc\nmiscalculate\nmiscalculation\nmiscall\nmiscarriage\nmiscarry\nmisce\nmiscegenate\nmiscegenation\nmiscellaneous\nmiscellaneousness\nmiscellany\nmischance\nmischief\nmischiefmaker\nmischiefmaking\nmischievous\nmiscible\nmiscite\nmisclassified\nmiscomputation\nmiscompute\nmisconceive\nmisconception\nmisconduct\nmisconducted\nmisconjecture\nmisconstrual\nmisconstruction\nmisconstrue\nmiscorrect\nmiscount\nmiscreance\nmiscreant\nmiscreated\nmiscue\nmisdate\nmisdated\nmisdeal\nmisdeed\nmisdemean\nmisdemeanant\nmisdemeanor\nmisdescribe\nmisdevotion\nmisdirect\nmisdirection\nmisdo\nmisdoing\nmisdoubt\nmise\nmisemploy\nmisemployment\nmiser\nmiserabile\nmiserable\nmiserably\nmisere\nmiserere\nmiseri\nmiseria\nmisericordia\nmisericordiam\nmiseries\nmiseris\nmiserliness\nmiserly\nmisers\nmisery\nmisestimate\nmisfeasance\nmisfeasanee\nmisfire\nmisfit\nmisfortune\nmisgiving\nmisgovernment\nmisguidance\nmisguide\nmisguided\nmishap\nmishmash\nmishna\nmisinform\nmisinformation\nmisinformaton\nmisinformed\nmisinstruct\nmisinstruction\nmisintelligence\nmisinterpreptation\nmisinterpret\nmisinterpretation\nmisinterpreted\nmisjoinder\nmisjoined\nmisjoining\nmisjudge\nmisjudged\nmisjudgent\nmisjudging\nmisjudgment\nmislaid\nmislay\nmislead\nmisleading\nmislike\nmismanage\nmismanagement\nmismatch\nmismatched\nmisname\nmisnamed\nmisnomer\nmisocainea\nmisogamist\nmisogamy\nmisogynic\nmisogynist\nmisogynous\nmisogyny\nmisology\nmisoneism\nmisopedia\nmispersuasion\nmisplace\nmisplaced\nmisplacement\nmisprint\nmisprision\nmisprize\nmispronounce\nmispronunciation\nmisproportion\nmisproportioned\nmisquotation\nmisquote\nmisreading\nmisreckon\nmisrelated\nmisrelation\nmisrelish\nmisreport\nmisrepresent\nmisrepresentation\nmisrepresented\nmisrule\nmiss\nmissa\nmissal\nmissay\nmissayingk\nmissend\nmisshape\nmisshapen\nmissile\nmissing\nmission\nmissionary\nmississippi\nmississippian\nmissive\nmissouri\nmissourian\nmisspell\nmisspelling\nmisspend\nmisstanding\nmisstate\nmisstatement\nmissus\nmist\nmistake\nmistaken\nmistakenly\nmisteach\nmisteaching\nmister\nmisterm\nmistflower\nmisthink\nmistily\nmistime\nmistimed\nmistletoe\nmistral\nmistranslate\nmistranslation\nmistreatment\nmistress\nmistrial\nmistrust\nmists\nmisty\nmisty-eyed\nmisunderstand\nmisunderstanding\nmisunderstood\nmisusage\nmisuse\nmisused\nmit\nmitad\nmitchella\nmite\nmitella\nmiter\nmiterwort\nmithai\nmithraic\nmithraism\nmithraist\nmithramycin\nmithras\nmithridate\nmithridates\nmitigable\nmitigate\nmitigated\nmitigation\nmitochondrion\nmitosis\nmitra\nmitraille\nmitrailleur\nmitrailleuse\nmitral\nmitt\nmitten\nmittimus\nmiwok\nmix\nmixed\nmixed-blood\nmixen\nmixer\nmixtura\nmixture\nmizzen\nmizzenmast\nmizzle\nmj\nmk\nml\nmnd\nmnemonic\nmnemonics\nmnemosyne\nmnemotechnics\nmniaceae\nmnium\nmo\nmoa\nmoan\nmoat\nmoated\nmob\nmobcap\nmobile\nmobilier\nmobility\nmobilization\nmobilize\nmoblige\nmobocracy\nmobula\nmobulidae\nmocassin\nmoccosin\nmocha\nmock\nmock-up\nmockernut\nmockery\nmocking\nmockingbird\nmod\nmodal\nmodal(a)\nmodality\nmodals\nmode\nmodel\nmodeled\nmodeler\nmodeling\nmodelled\nmodem\nmoderate\nmoderated\nmoderately\nmoderating\nmoderatio\nmoderation\nmoderatism\nmoderato\nmoderator\nmoderatorship\nmodern\nmoderne\nmodernism\nmodernist\nmodernistic\nmodernity\nmodernization\nmodernize\nmodernized\nmodes\nmodest\nmodestly\nmodesty\nmodestys\nmodicum\nmodifiable\nmodification\nmodified\nmodifier\nmodify\nmodillion\nmodish\nmodo\nmodue\nmodular\nmodulate\nmodulated\nmodulation\nmodule\nmodulus\nmodus\nmoehringia\nmofussil\nmogadiscio\nmogul\nmohair\nmohammed\nmohammedan\nmohammedanism\nmohammedian\nmohave\nmohawk\nmohican\nmohock\nmohria\nmoi\nmoider\nmoiety\nmoil\nmoirai\nmoire\nmoist\nmoisten\nmoistened\nmoistening\nmoistness\nmoisture\nmojarra\nmojave\nmoke\nmokes\nmokulu\nmolar\nmolar(a)\nmolarity\nmolasses\nmold\nmoldboard\nmolded\nmolder\nmoldered\nmoldering\nmoldiness\nmolding\nmoldova\nmoldy\nmole\nmolecular\nmolecular(a)\nmolecule\nmoleeyed\nmolehill\nmolellills\nmoles\nmoleskin\nmolest\nmolestation\nmolester\nmolidae\nmoliere\nmolise\nmoll\nmollah\nmolles\nmollia\nmollie\nmollienesia\nmollification\nmollify\nmolluga\nmollusca\nmolluscous\nmollusk\nmolly\nmollycoddle\nmoloch\nmolokai\nmolossidae\nmolothrus\nmolt\nmolten\nmolting\nmolto\nmolucella\nmolva\nmolybdenite\nmolybdenum\nmom\nmombasa\nmombin\nmomemt\nmoment\nmomentaneous\nmomentarily\nmomentary\nmomentous\nmomentously\nmomentousness\nmoments\nmomentum\nmomism\nmommon\nmomordica\nmomotidae\nmomotus\nmomus\nmon\nmon-khmer\nmonacan\nmonachal\nmonachism\nmonachy\nmonaco\nmonad\nmonadic\nmonagne\nmonal\nmonandrous\nmonandry\nmonarch\nmonarchal\nmonarchical\nmonarchies\nmonarchism\nmonarchist\nmonarchy\nmonarda\nmonardella\nmonario\nmonasterial\nmonastery\nmonastic\nmonasticism\nmonaural\nmonaurally\nmonazite\nmonday\nmonde\nmonegasque\nmonera\nmoneran\nmoneses\nmonestrous\nmonetarism\nmonetarist\nmonetary\nmonetization\nmonetize\nmoney\nmoneybag\nmoneyed\nmoneyer\nmoneygrubber\nmoneyless\nmoneymaker\nmoneymaking\nmoneys\nmoneywort\nmong\nmonger\nmongo\nmongol\nmongolia\nmongolian\nmongolism\nmongoloid\nmongoose\nmongrel\nmonied\nmonilia\nmoniliaceae\nmoniliales\nmoniliform\nmonism\nmonistic\nmonition\nmonitive\nmonitor\nmonitoring\nmonitory\nmonk\nmonkery\nmonkey\nmonkeywrench\nmonkfish\nmonkhood\nmonkish\nmonkshood\nmonmouth\nmono\nmono-iodotyrosine\nmonoamine\nmonocanthidae\nmonocanthus\nmonocarboxylic\nmonocarp\nmonocarpic\nmonochamus\nmonochord\nmonochromatic\nmonochrome\nmonocle\nmonoclinic\nmonoclinous\nmonoclonal\nmonocot\nmonocotyledones\nmonocotyledonous\nmonocracy\nmonoculous\nmonocyte\nmonodic\nmonodon\nmonodontidae\nmonodrame\nmonody\nmonoecious\nmonogamist\nmonogamous\nmonogamy\nmonogenesis\nmonogram\nmonograph\nmonogynous\nmonolatry\nmonolingual\nmonolingually\nmonolith\nmonolithic\nmonologist\nmonologue\nmonologueduologue\nmonology\nmonomachy\nmonomania\nmonomaniac\nmonomaniacal\nmonomer\nmonometallic\nmonomorium\nmonomorphemic\nmononuclear\nmononucleosis\nmonophonic\nmonoplane\nmonopolist\nmonopolistic\nmonopolization\nmonopolize\nmonopoly\nmonopsony\nmonopteral\nmonorail\nmonosaccharide\nmonosemous\nmonosemy\nmonospermous\nmonostich\nmonosyllabic\nmonosyllabically\nmonosyllable\nmonosyllables\nmonotheism\nmonotheist\nmonotheistic\nmonotone\nmonotonic\nmonotonous\nmonotonously\nmonotony\nmonotremata\nmonotreme\nmonotropa\nmonotropaceae\nmonotype\nmonotypic\nmonovalent\nmonoxide\nmonozygotic\nmonroe\nmonrovia\nmons\nmonsieur\nmonsignor\nmonsoon\nmonster\nmonstera\nmonstrance\nmonstrosity\nmonstrous\nmonstrously\nmonstrum\nmont\nmontagne\nmontana\nmontanan\nmontane\nmonte\nmontee\nmontenegro\nmontes\nmontevideo\nmontezuma\nmontfort\nmontgolfier\nmontgomery\nmonth\nmonthly\nmonths\nmontia\nmonticle\nmontpelier\nmontreal\nmontserrat\nmontserratian\nmontserration\nmonument\nmonumental\nmonumentum\nmoo\nmooch\nmoocher\nmood\nmoodily\nmoodiness\nmoodish\nmoodishness\nmoods\nmoody\nmoon\nmoon-faced\nmoon-splashed\nmoon-worship\nmoonbeam\nmooncalf\nmooneyed\nmoonfish\nmoonflower\nmoonglade\nmoonless\nmoonlight\nmoonlike\nmoonlit\nmoonseed\nmoonshell\nmoonshine\nmoonstone\nmoonstruck\nmoonwalk\nmoonwort\nmoor\nmoorcock\nmoore\nmoored\nmoorhen\nmooring\nmoorings\nmoorish\nmoorland\nmoory\nmoose\nmoosewood\nmoot\nmooted\nmop\nmop-headed\nmopboard\nmope\nmoped\nmopeeyed\nmoping\nmopish\nmopper\nmopsey\nmopsy\nmopus\nmoquelumnan\nmoquette\nmora\nmoraceae\nmoraceous\nmoraine\nmoral\nmoral(a)\nmorale\nmoralist\nmoralistic\nmorality\nmoralize\nmoralizing\nmorally\nmorals\nmorass\nmoratorium\nmoray\nmorbid\nmorbidity\nmorbidly\nmorbiferous\nmorbific\nmorbo\nmorbose\nmorbosity\nmorceau\nmorchella\nmorchellaceae\nmordacious\nmordacity\nmordant\nmordva\nmore\nmore(a)\nmorel\nmorello\nmoremajorum\nmorent\nmoreover\nmores\nmoresque\nmorgan\nmorgana\nmorganatic\nmorganite\nmorgantown\nmorgen\nmorgue\nmori\nmoribund\nmorient\nmorion\nmorisco\nmormo\nmormon\nmormonism\nmorn\nmornful\nmorning\nmorning(a)\nmoroccan\nmorocco\nmorone\nmoronic\nmoronity\nmorose\nmorosely\nmoroseness\nmorosity\nmorosoph\nmorphallaxis\nmorphea\nmorpheme\nmorphemic\nmorpheus\nmorphine\nmorphism\nmorphologic\nmorphologically\nmorphology\nmorphophoneme\nmorphophonemic\nmorphophonemics\nmorra\nmorrigan\nmorris\nmorrow\nmors\nmorse\nmorsel\nmort\nmorta\nmortal\nmortal(a)\nmortality\nmortally\nmortar\nmortarboard\nmortem\nmortgage\nmortgaged\nmortgagee\nmortgagor\nmortician\nmortiferous\nmortification\nmortify\nmortifying\nmortis\nmortise\nmortmain\nmortua\nmortuary\nmortuis\nmortuum\nmorus\nmosaic\nmosan\nmoschus\nmoscow\nmoselle\nmoses\nmoslem\nmosque\nmosquito\nmosquitofish\nmoss\nmoss-grown\nmossa\nmossgrown\nmosslike\nmosstrooper\nmossy\nmost\nmost(a)\nmost-valuable\nmostaccioli\nmot\nmotacilla\nmotacillidae\nmotazilite\nmote\nmotel\nmotet\nmoth\nmoth-eaten\nmothball\nmotheaten\nmother\nmother-in-law\nmother-naked\nmother-of-pearl\nmotherhood\nmotherless\nmotherlike\nmotherliness\nmotherly\nmotherofpearl\nmothers\nmotherwort\nmothproof\nmotif\nmotile\nmotility\nmotion\nmotional\nmotionless\nmotionlessly\nmotionlessness\nmotivated\nmotivation\nmotivational\nmotivative(a)\nmotive\nmotive(a)\nmotiveless\nmotives\nmotley\nmotmot\nmotor\nmotor-assisted\nmotorbike\nmotorboat\nmotorcade\nmotorcycle\nmotorcycling\nmotorist\nmotorization\nmotorized\nmotorman\nmotory\nmots\nmotte\nmottled\nmottling\nmotto\nmotu\nmouchard\nmouche\nmouflon\nmould\nmoulin\nmoulins\nmound\nmount\nmountain\nmountain(a)\nmountaineer\nmountainflax\nmountainous\nmountains\nmountainside\nmountebank\nmounted\nmountie\nmounting\nmounts\nmourn\nmourner\nmourners\nmournful\nmournfully\nmournfulness\nmourning\nmouse\nmousecolored\nmousehole\nmouser\nmousetrap\nmoussaka\nmousse\nmousseux\nmoustache\nmousy\nmouth\nmouth-watering\nmouthbreeder\nmouthed\nmouthful\nmouthlike\nmouthpart\nmouthpiece\nmouths\nmouthwatering\nmouthy\nmouton\nmoutonne\nmoutons\nmovability\nmovable\nmovableness\nmovables\nmove\nmoved\nmoved(p)\nmoveless\nmovement\nmovent\nmover\nmovere\nmoves\nmovie\nmoviegoer\nmovies\nmoving\nmovingly\nmow\nmown\nmoxa\nmozambican\nmozambique\nmozart\nmozartian\nmozetta\nmozzarella\nmp\nmpriser\nmr\nmrs\nms\nms-dos\nms.\nmsasa\nmu\nmuch\nmuch(a)\nmuchness\nmuchos\nmucid\nmucilage\nmucilaginous\nmucin\nmucinoid\nmucinous\nmuck\nmuckle\nmuckrake\nmuckraker\nmuckworm\nmucky\nmucoid\nmucor\nmucoraceae\nmucorales\nmucosity\nmucous\nmucronate\nmucronated\nmuculent\nmucuna\nmucus\nmud\nmud-beplastered\nmuddle\nmuddled\nmuddy\nmudguard\nmudlark\nmudra\nmudskipper\nmudslide\nmuenster\nmuerte\nmuesli\nmuezzin\nmuff\nmuffin\nmuffle\nmuffled\nmuffler\nmufti\nmug\nmuggee\nmugger\nmugginess\nmugging\nmuggy\nmugient\nmugil\nmugilidae\nmugiloidea\nmugwort\nmugwump\nmuhammadan\nmuharram\nmuhlenbergia\nmuishond\nmujer\nmulada\nmulatto\nmulberry\nmulch\nmulct\nmule\nmulet\nmuleteer\nmuliebrity\nmulish\nmull\nmullah\nmullein\nmullet\nmullidae\nmulligatawny\nmullion\nmullioned\nmulloidichthys\nmulloway\nmullus\nmulta\nmultangular\nmultarum\nmulti\nmulticellular\nmulticultural\nmultidimensional\nmultifarious\nmultifariousness\nmultifid\nmultiflora\nmultifold\nmultiform\nmultiformity\nmultigenerous\nmultihued\nmultilane\nmultilateral\nmultilingual\nmultilocular\nmultiloquence\nmultiloquous\nmultimedia\nmultinational\nmultinominal\nmultinucleate\nmultiparous\nmultipartite\nmultiple\nmultiple-choice\nmultiplex\nmultiplexer\nmultiplicand\nmultiplication\nmultiplicative\nmultiplicator\nmultiplicity\nmultiplied\nmultiplier\nmultiply\nmultipotent\nmultiprocessing\nmultiprocessor\nmultiprogramming\nmultipurpose\nmultiracial\nmultis\nmultisonous\nmultistage\nmultistory\nmultitude\nmultitudinous\nmultitudinousness\nmultivalent\nmultos\nmultum\nmulture\nmum\nmumble\nmumbletypeg\nmumbling\nmumbo\nmumify\nmummer\nmummery\nmummichog\nmummification\nmummy\nmump\nmumper\nmumpish\nmumps\nmumpsimus\nmums\nmunch\nmunchausen\nmunchil\nmunching\nmunda\nmundane\nmundanely\nmundation\nmundi\nmundify\nmunditiis\nmundivagrant\nmundus\nmunerary\nmunerate\nmung\nmunich\nmunicipal\nmunicipality\nmunicipally\nmunificence\nmunificent\nmuniment\nmuniments\nmunition\nmunitions\nmunj\nmunshi\nmuntiacus\nmuntingia\nmuntjac\nmuon\nmuori\nmuove\nmuraenidae\nmural\nmurder\nmurdered\nmurderer\nmurderess\nmurderous\nmurderously\nmurderousness\nmurem\nmuricated\nmuridae\nmurk\nmurkily\nmurksome\nmurky\nmurmerer\nmurmur\nmurmuring\nmurmurous\nmuroidea\nmuros\nmurrain\nmurray\nmurre\nmurrey\nmurrion\nmurus\nmus\nmusa\nmusaceae\nmusaeo\nmusales\nmusca\nmuscadet\nmuscadine\nmuscardinus\nmuscari\nmuscas\nmuscat\nmuschis\nmuscicapa\nmuscicapidae\nmuscidae\nmuscivora\nmuscle\nmuscle-bound\nmuscleman\nmuscoidea\nmuscovite\nmuscovy\nmuscular\nmuse\nmuseful\nmuseology\nmuses\nmusette\nmuseum\nmusgu\nmush\nmushiness\nmushroom\nmushrooms\nmushy\nmusic\nmusical\nmusicality\nmusically\nmusician\nmusicianship\nmusics\nmusing\nmusingly\nmusk\nmuskellunge\nmusket\nmusketeer\nmusketoon\nmusketry\nmuskhogean\nmuskmelon\nmuskogee\nmuskrat\nmuskwood\nmusky\nmuslim\nmuslin\nmusnud\nmusophaga\nmusophagidae\nmusophobia\nmuss\nmussel\nmussolini\nmussuk\nmussulman\nmussy\nmust\nmust(a)\nmustache\nmustachio\nmustachioed\nmustang\nmustard\nmustela\nmustelidae\nmustelus\nmuster\nmustiness\nmusty\nmut\nmuta\nmutabile\nmutability\nmutable\nmutagenesis\nmutal\nmutamur\nmutandis\nmutant\nmutantur\nmutari\nmutation\nmutational\nmutatis\nmutative\nmutato\nmutatus\nmutchkin\nmute\nmutely\nmuteness\nmutilate\nmutilated\nmutilation\nmutineer\nmutineering\nmutinous\nmutinousness\nmutinus\nmutiny\nmutisia\nmutt\nmutter\nmutterer\nmutton\nmuttonhead\nmutual\nmutuality\nmutually\nmutum\nmuzhik\nmuzzle\nmuzzled\nmuzzleloader\nmuzzy\nmwera\nmy\nmya\nmyaceae\nmyacidae\nmyadestes\nmyalgia\nmyalgic\nmyanmar\nmyatism\nmycelium\nmycenae\nmycenaean\nmycetophilidae\nmycobacteria\nmycobacteriacaea\nmycologist\nmycology\nmycomycin\nmycophagist\nmycoplasma\nmycoplasmatacaea\nmycoplasmatales\nmycrosporidia\nmycteroperca\nmyctophidae\nmyelencephalon\nmyelic\nmyelin\nmyelinated\nmyelinic\nmyelitis\nmyeloblast\nmyelocyte\nmyelofibrosis\nmyeloid\nmyeloma\nmyelomeningocele\nmylanta\nmylar\nmyliobatidae\nmylodon\nmylodontid\nmylodontidae\nmyna\nmynheer\nmyocardial\nmyocardium\nmyocastor\nmyofibril\nmyoglobin\nmyoid\nmyology\nmyoma\nmyomancy\nmyomorpha\nmyope\nmyopia\nmyopic\nmyopus\nmyosin\nmyosotis\nmyotis\nmyotonia\nmyrcia\nmyrciaria\nmyriad\nmyriagram\nmyriameter\nmyriapod\nmyrica\nmyricaceae\nmyricales\nmyricaria\nmyriophyllum\nmyristica\nmyristicaceae\nmyrmecia\nmyrmecobius\nmyrmecophaga\nmyrmecophagidae\nmyrmecophagous\nmyrmecophile\nmyrmecophilous\nmyrmecophyte\nmyrmecophytic\nmyrmeleon\nmyrmeleontidae\nmyrmidon\nmyroxylon\nmyrrh\nmyrrhis\nmyrsinaceae\nmyrsine\nmyrtaceae\nmyrtales\nmyrtillocactus\nmyrtle\nmyrtus\nmyself\nmysidacea\nmysidae\nmysis\nmysophilia\nmysophobia\nmysophobic\nmysterious\nmysteriously\nmystery\nmystic\nmystical\nmystically\nmysticeti\nmysticism\nmystification\nmystify\nmystique\nmyth\nmythic\nmythical\nmythogenesis\nmythological\nmythologist\nmythologization\nmythology\nmytilidae\nmytilus\nmyxedema\nmyxine\nmyxinidae\nmyxiniformes\nmyxinikela\nmyxobacteria\nmyxocephalus\nmyxomatosis\nmyxomycetes\nmyxomycota\nmyxophyceae\nmyxosporidia\nmyxosporidian\nmyxovirus\nn\nn't\nna\nna-dene\nnab\nnabalus\nnabob\nnaboom\nnabu\nnabumetone\nnacelle\nnacho\nnacimiento\nnacre\nnacreous\nnadir\nnadolol\nnaemorhedus\nnaevose\nnag\nnaga\nnagami\nnagari\nnagasaki\nnageia\nnager\nnagi\nnahuatl\nnaiad\nnaiadaceae\nnaiadales\nnaiant\nnaias\nnaik\nnaiki\nnail\nnailbrush\nnailed\nnailery\nnailfile\nnailhead\nnails\nnainsook\nnaira\nnairne\nnairobi\nnaive\nnaively\nnaivete\nnaja\nnaked\nnakedly\nnakedness\nnakedwood\nnam\nnamby-pamby\nnambypamby\nname\nname-dropping\nnamed\nnamedropper\nnameko\nnameless\nnamely\nnameplate\nnames\nnamesake\nnamibia\nnamibian\nnaming\nnammu\nnamtar\nnana\nnancere\nnandrolone\nnankeen\nnanking\nnanna\nnanny\nnanogram\nnanometer\nnanomia\nnanosecond\nnantes\nnanticoke\nnantua\nnantucket\nnaomi\nnap\nnapaea\nnapalm\nnape\nnapha\nnaphtha\nnaphthalene\nnapier\nnapiers\nnapkin\nnaples\nnapless\nnapoleon\nnapoleonic\nnapoli\nnapping\nnappy\nnaprapath\nnaprapathy\nnaproxen\nnaptha\nnapu\nnaranjilla\nnarc\nnarcissist\nnarcissus\nnarcolepsy\nnarcoleptic\nnarcosis\nnarcotic\nnard\nnardoo\nnarial\nnaris\nnark\nnarraganset\nnarrate\nnarration\nnarrative\nnarrator\nnarratur\nnarrow\nnarrow-minded\nnarrow-mindedly\nnarrow-mindedness\nnarrowed\nnarrowing\nnarrowly\nnarrowminded\nnarrowness\nnarrows\nnarrowsouled\nnarthecium\nnarthex\nnarwhal\nnary\nnary(a)\nnasal\nnasalis\nnasality\nnasalization\nnasally\nnascent\nnasci\nnascitur\nnasdaq\nnaseby\nnashville\nnaso\nnasopharynx\nnassau\nnasser\nnast\nnastily\nnastiness\nnasturtium\nnasty\nnasua\nnata\nnatal\nnatantia\nnatare\nnatation\nnatch\nnathless\nnati\nnaticidae\nnation\nnational\nnationale\nnationalism\nnationalist\nnationality\nnationalization\nnationally\nnations\nnative\nnative-born\nnativeness\nnativism\nnativist\nnativity\nnatrix\nnatterjack\nnattily\nnatty\nnatura\nnaturae\nnatural\nnaturalibus\nnaturalism\nnaturalist\nnaturalistic\nnaturalization\nnaturalize\nnaturalized\nnaturally\nnaturalness\nnature\nnatureal\nnaturel\nnatures\nnaturistic\nnaturoe\nnaturopath\nnaturopathy\nnatus\nnauclea\nnaucrates\nnaught\nnaughtiness\nnaughty\nnaumachia\nnaumachy\nnauran\nnauru\nnauruan\nnausea\nnauseam\nnauseate\nnauseated\nnauseating\nnauseous\nnautch\nnautchgirl\nnautical\nnautilidae\nnautilus\nnavaho\nnaval\nnavarch\nnavarino\nnave\nnavel\nnavicular\nnavigability\nnavigable\nnavigate\nnavigation\nnavigational\nnavigator\nnavvy\nnavy\nnawab\nnay\nnaysayer\nnaysaying\nnazarewne\nnaze\nnazi\nnazism\nnb\nnc\nnd\nndebele\nndjamena\nne\nneaf\nneanderthal\nneap\nneapolitan\nnear\nnear(a)\nnearby\nnearer\nnearest\nnearly\nnearness\nnearside\nnearsighted\nneat\nneathanded\nneatherd\nneatly\nneatness\nneats\nneb\nnebraska\nnebraskan\nnebula\nnebulae\nnebular\nnebulosity\nnebulous\nnebulously\nnec\nnecessarian\nnecessaries\nnecessarily\nnecessary\nnecessita\nnecessitarian\nnecessitas\nnecessitate\nnecessitation\nnecessities\nnecessitous\nnecessity\nnecio\nneck\nneckar\nneckband\nneckcloth\nnecked\nneckerchief\nnecklace\nneckless\nnecklet\nnecklike\nneckline\nneckpiece\nnecks\nnecktie\nneckwear\nnecrobiosis\nnecrology\nnecromancer\nnecromancy\nnecromantic\nnecrophagia\nnecrophilia\nnecropolis\nnecropsy\nnecroscopic\nnecrosis\nnecrotic\nnectar\nnectarine\nnectarious\nnectary\nnecturus\nnee\nneed\nneeded\nneedful\nneedfulness\nneediness\nneedle\nneedlebush\nneedlefish\nneedlepoint\nneedles\nneedless\nneedlessly\nneedlewoman\nneedlewood\nneedlework\nneedleworker\nneeds\nneedy\nneem\nneencephalon\nneer\nneerdowell\nnefarious\nnefariously\nnefariousness\nnefas\nnegaprion\nnegation\nnegative\nnegatively\nnegativeness\nnegativism\nnegatory\nnegev\nneglect\nneglected\nneglectful\nneglectfully\nneglecting\nnegligable\nneglige\nnegligee\nnegligence\nnegligent\nnegligently\nnegligible\nnegotiable\nnegotiate\nnegotiation\nnegotiations\nnegotiator\nnegotiatress\nnegotiis\nnegress\nnegritude\nnegro\nnegroid\nnegrophobia\nnegus\nnehru\nneif\nneigh\nneighbor\nneighborhood\nneighboring\nneighborliness\nneighborly\nneither\nnekton\nnella\nnelsons\nnelumbo\nnelumbonaceae\nnem\nnematocera\nnematoda\nnematode\nnemertea\nnemesis\nnemine\nneminem\nnemo\nnemophila\nnenets\nnenia\nneo\nneo-darwinian\nneo-darwinism\nneo-lamarckian\nneo-lamarckism\nneo-latin\nneoceratodus\nneoclassic\nneoclassicism\nneoclassicist\nneocolonialism\nneocortical\nneodarwinism\nneodymium\nneoexpressionism\nneofiber\nneogamist\nneohygrophorus\nneolamarkism\nneolentinus\nneoliberal\nneoliberalism\nneolith\nneolithic\nneologic\nneological\nneologism\nneologist\nneology\nneomycin\nneomys\nneon\nneonatal\nneonatalmed\nneonate\nneopallium\nneophron\nneophyte\nneoplasia\nneoplastic\nneoplatonism\nneopolitan\nneoprene\nneoromanticism\nneosho\nneoteric\nneotoma\nnepa\nnepal\nnepalese\nnepali\nnepenthaceae\nnepenthe\nnepenthes\nnepeta\nnephalism\nnephelium\nnephelognosy\nnephew\nnephograph\nnephology\nnephoscope\nnephrectomy\nnephrite\nnephritic\nnephritis\nnephrolepis\nnephrolithiasis\nnephron\nnephrops\nnephropsidae\nnephthys\nnephthytis\nnepidae\nnepos\nnepotism\nnepotist\nneptune\nneptunium\nneque\nnereid\nnereus\nnergal\nnerita\nneritic\nneritid\nneritidae\nneritina\nnerium\nnerodia\nnerthus\nnerve\nnerve-racking\nnerveless\nnerves\nnervos\nnervous\nnervously\nnervousness\nnervy\nnescape\nnescience\nnescient\nnescio\nnescit\nnesokia\nness\nnessun\nnest\nnesting\nnestle\nnestled\nnestling\nnestor\nnet\nnetball\nnether\nnetherlander\nnetherlands\nnethermost\nnetscape\nnetting\nnettle\nnetwork\nnetworklike\nneural\nneuralgia\nneuralgic\nneurasthenia\nneurasthenic\nneurectomy\nneuritis\nneuroanatomic\nneuroanatomy\nneurobiological\nneurobiology\nneuroblast\nneurochemical\nneuroglia\nneurogliacyte\nneuroglial\nneurological\nneurologist\nneurology\nneuromotor\nneuromuscular\nneurophysiological\nneurophysiology\nneuropsychiatric\nneuropsychiatry\nneuropsychological\nneuroptera\nneuropteron\nneurosarcoma\nneuroscience\nneurosis\nneurospora\nneurosurgeon\nneurosurgery\nneurotic\nneurotically\nneurotransmitter\nneurotrichus\nneurotropism\nneuter\nneutering\nneutral\nneutralism\nneutralist\nneutrality\nneutralization\nneutralize\nneutralized\nneutrino\nneutron\nneutropenia\nneutrophil\nnevada\nnevadan\nneve\nnever\nneverdying\nneverending\nneverfading\nneverfailing\nnevermore\nneverness\nnevertheless\nnevis\nnew\nnew(a)\nnew-made\nnewari\nnewark\nnewborn\nnewcastle\nnewcomer\nnewel\nnewfangled\nnewfashioned\nnewfledged\nnewfound\nnewfoundland\nnewgate\nnewly\nnewlywed\nnewmarket\nnewness\nnewport\nnews\nnewsagent\nnewsboy\nnewscast\nnewscaster\nnewsless\nnewsletter\nnewsmonger\nnewspaper\nnewspapers\nnewsreel\nnewsroom\nnewsstand\nnewswoman\nnewsworthiness\nnewsworthy\nnewsy\nnewt\nnewton\nnewtonian\nnex\nnext\nnexus\nnf\nnfld\nnfor\nng\nnganasan\nngultrum\nnguni\nngwee\nnh\nni\nni-hard\nni-resist\nniacin\nniagara\nniais\nniaiserie\nniamey\nnib\nnibbed\nnibble\nniblick\nnicad\nnicandra\nnicaragua\nnicaraguan\nnice\nnicely\nnicene\nniceness\nnicety\nniche\nnicher\nnichrome\nnicht\nnichts\nnichtsger\nnick\nnickel\nnickel-and-dime\nnickel-and-dime(a)\nnickname\nnicosia\nnicotiana\nnicotine\nnictate\nnictitate\nnictitation\nnidget\nnidicolous\nnidification\nnidifugous\nnidor\nnidorous\nnidularia\nnidulariaceae\nnidulariales\nnidus\nniece\nniente\nniff\nniffy\nnigella\nniger\nniger-congo\nniger-kordofanian\nnigeria\nnigerian\nnigerien\nniggard\nniggardly\nnigger\nniggle\nniggling\nnigh\nnighest\nnight\nnight-line\nnight-stop\nnightcap\nnightclothes\nnightfall\nnightgown\nnighthawk\nnightingale\nnightlight\nnightlong\nnightly\nnightmare\nnights\nnightshade\nnightshirt\nnighttime\nnightwork\nnigrescent\nnigrification\nnigroporus\nnihau\nnihil\nnihilism\nnihilist\nnihilistic\nnihility\nnihilo\nnijmegen\nnike\nnil\nnile\nnilgai\nnill\nnilly\nnilo-saharan\nnilotic\nnilpotent\nnim\nnimble\nnimblefingered\nnimblefooted\nnimbleness\nnimblewill\nnimblewitted\nnimbus\nnimiety\nnimis\nnimium\nnimporte\nnimravus\nnimrod\nnina\nnincompoop\nnine\nnine-spot\nninefold\nninepence\nninepenny\nninepin\nninepins\nnineteen\nnineteenth\nnineties\nninetieth\nninety\nnineveh\nningal\nningirsu\nningishzida\nninigi\nninkhursag\nninny\nninnyhammer\nninos\nninth\nnintu\nninurta\nniobe\nniobite\nniobium\nniobrara\nnios\nnip\nnipa\nnipperkin\nnippers\nnipping\nnipple\nnirvana\nnis\nnisan\nnisi\nnisus\nnit\nnitella\nnitency\nniter\nnitid\nnitor\nnitrate\nnitric\nnitrification\nnitrile\nnitrite\nnitrobacter\nnitrobacteriaceae\nnitrobenzene\nnitrocalcite\nnitrochlorohydric\nnitrofurantoin\nnitrogen\nnitrogenous\nnitroglycerin\nnitromuriatic\nnitrosobacteria\nnitrosomonas\nnitrospan\nnitrous\nnits\nnitwitted\nniveous\nnivose\nnix\nnixie\nnixon\nnizam\nnizy\nnj\nnjord\nnk\nnl\nnnumber\nno\nno(a)\nno-brainer\nno-go\nno-goal\nno-hit\nno-man's-land\nno-nonsense\nno-show\nnoah\nnoahs\nnob\nnobelist\nnobelium\nnobile\nnobilitate\nnobility\nnobis\nnoble\nnobleman\nnobleminded\nnobleness\nnobler\nnoblesse\nnoblest\nnobly\nnobody\nnocendi\nnocens\nnocent\nnocet\nnociceptive\nnock\nnoctambulation\nnoctambulism\nnoctambulist\nnoctiluca\nnoctilucent\nnoctivagant\nnoctivagation\nnoctivagous\nnoctivagrant\nnoctograph\nnoctua\nnoctuidae\nnocturnal\nnocturnally\nnocturne\nnocuisse\nnocuous\nnod\nnodding\nnoddle\nnoddy\nnode\nnodosity\nnods\nnodular\nnodule\nnodulose\nnodus\nnoemata/gr\nnog\nnoggin\nnogging\nnoir\nnoire\nnoise\nnoiseless\nnoiselessly\nnoiselessness\nnoisemaker\nnoises\nnoisily\nnoisiness\nnoisome\nnoisy\nnolens\nnoli\nnoli-me-tangere\nnolina\nnolition\nnolle\nnolleity\nnolumus\nnom\nnomad\nnomadic\nnomadism\nnomadize\nnomancy\nnombril\nnomenclature\nnomenklatura\nnomia\nnomina\nnominal\nnominalism\nnominalistic\nnominally\nnominate\nnominated\nnomination\nnominative\nnomine\nnominee\nnominis\nnomogram\nnomology\nnon\nnon-catholic\nnon-discrimination\nnon-engagement\nnon-resistant\nnon-u\nnona\nnonabsorbency\nnonabsorbent\nnonacceptance\nnonaccomplishment\nnonachiever\nnonaddictive\nnonaddition\nnonadhesion\nnonadhesive\nnonadjacent\nnonadmission\nnonadsorbent\nnonage\nnonagenarian\nnonaggression\nnonagon\nnonalcoholic\nnonaligned\nnonalignment\nnonapparent\nnonappearance\nnonappointive\nnonarbitrable\nnonarbitrary\nnonarboreal\nnonassemblage\nnonassertive\nnonassociative\nnonastringent\nnonattendance\nnonautonomous\nnonbearing\nnonbeing\nnonbelligerent\nnonbiblical\nnoncandidate\nnoncarbonated\nnoncausative\nnonce\nnoncellular\nnonchalance\nnonchalant\nnoncivilized\nnonclassical\nnoncohesive\nnoncoincidence\nnoncollapsible\nnoncolumned\nnoncombatant\nnoncombinative\nnoncombining\nnoncombustible\nnoncomformity\nnoncommercial\nnoncommissioned\nnoncommital\nnoncommunicable\nnoncompetitive\nnoncompetitively\nnoncompletion\nnoncompliance\nnoncomprehensive\nnoncomprehensively\nnonconductive\nnonconforming\nnonconformist\nnonconformity\nnonconscious\nnoncontent\nnoncontentious\nnonconvergent\nnoncritical\nnoncrucial\nnoncrystalline\nnoncurrent\nnoncyclic\nnondeductible\nnondenominational\nnondescript\nnondevelopment\nnondigestible\nnondisposable\nnondriver\nnone\nnonechoic\nnoneffervescent\nnonelective\nnonendurance\nnonentity\nnonenzymatic\nnonequivalence\nnonequivalent\nnones\nnonessential\nnonesuch\nnonevent\nnonexempt\nnonexistence\nnonexistent\nnonexpectant\nnonexpectation\nnonexploratory\nnonexplosive\nnonextant\nnonextensile\nnonextension\nnonfat\nnonfatal\nnonfeasance\nnonfiction\nnonfictional\nnonfinancial\nnonfissile\nnonfissionable\nnonflammable\nnonfulfillment\nnonfunctional\nnonglutinous\nnongregarious\nnonhairy\nnonharmonic\nnonhereditary\nnonhierarchical\nnonhuman\nnonillion\nnonimitation\nnonimitative\nnoninclusion\nnonincrease\nnonindulgent\nnonindustrial\nnoninfectious\nnoninflammatory\nnoninheritable\nnoninstitutional\nnoninstitutionalized\nnonintegrated\nnonintellectual\nnoninterchangeable\nnoninterference\nnonintervention\nnoninvasive\nnonionic\nnonionized\nnonius\nnonjudgmental\nnonjuring\nnonjuror\nnonkosher\nnonlethal\nnonlexical\nnonlexically\nnonlinear\nnonlinguistic\nnonmagnetic\nnonmandatory\nnonmechanical\nnonmechanistic\nnonmember\nnonmetal\nnonmetallic\nnonmetamorphic\nnonmigratory\nnonmodern\nnonmonotonic\nnonmoral\nnonmotile\nnonmoving\nnonnative\nnonnatural\nnonnaturals\nnonnegative\nnonnitrogenous\nnonnomadic\nnonnormative\nnonny\nnonobjective\nnonobservance\nnonobservant\nnonoccurrence\nnonoperational\nnonoscillatory\nnonparallel\nnonparametric\nnonpareil\nnonparticipant\nnonparticulate\nnonpartisan\nnonparty\nnonpasserine\nnonpayment\nnonperformance\nnonpersonal\nnonpertinence\nnonphilosophical\nnonphotosynthetic\nnonplus\nnonplussed\nnonpoisonous\nnonpolitical\nnonporous\nnonpregnant\nnonprehensile\nnonpreparation\nnonprescription(a)\nnonprevalence\nnonproduction\nnonproductive\nnonprofessional\nnonprofit\nnonprognosticative\nnonproprietary\nnonpsychoactive\nnonpublic\nnonpurulent\nnonracial\nnonradioactive\nnonrandom\nnonrational\nnonreciprocal\nnonreciprocating\nnonrecreational\nnonreflective\nnonrepresentational\nnonrepresentative\nnonresidence\nnonresident\nnonresidential\nnonresilient\nnonresistance\nnonresistant\nnonresisting\nnonresonance\nnonresonant\nnonrestrictive\nnonreticulate\nnonretractile\nnonreturnable\nnonreversible\nnonrhythmic\nnonrigid\nnonruminant\nnonscripta\nnonsectarian\nnonsense\nnonsense(a)\nnonsensical\nnonsensitive\nnonsignificant\nnonskid\nnonslip\nnonslippery\nnonsmoker\nnonspatial\nnonspeaking\nnonspecific\nnonspecifically\nnonspherical\nnonstandard\nnonstarter\nnonsteroidal\nnonstick\nnonstop\nnonstructural\nnonsubjective\nnonsubmersible\nnonsubsistence\nnonsuccess\nnonsuch\nnonsuit\nnonsuited\nnonsuppurative\nnonsurgical\nnonsyllabic\nnonsynchronous\nnont\nnontaxable\nnontechnical\nnontelescopic\nnonterritorial\nnonthermal\nnontoxic\nnontraditional\nnontransferable\nnontranslational\nnontropical\nnonturbulent\nnonum\nnonuniformity\nnonunion\nnonuple\nnonvenomous\nnonverbal\nnonverbally\nnonviable\nnonviolent\nnonviolently\nnonvisual\nnonvolatile\nnonwashable\nnonwoody\nnonworker\nnoobe\nnoodle\nnoodlehead\nnook\nnoon\nnoonday\nnooning\nnoontide\nnoontime\nnooscopic\nnoose\nnootka\nnopal\nnopalea\nnope\nnor\nnoradrenaline\nnord\nnord-pas-de-calais\nnordic\nnorethindrone\nnorfolk\nnoria\nnorm\nnorma\nnormal\nnormalcy\nnormality\nnormalize\nnormally\nnormalness\nnorman\nnorman-french\nnormand\nnormandie\nnormative\nnormotensive\nnormothermia\nnorn\nnornal\nnorth\nnorth-american\nnorth-central\nnorth-northeast\nnorth-northwest\nnorth-polar\nnorthbound\nnortheast\nnortheaster\nnortheasterly\nnortheastern\nnortheastward\nnorther\nnortherly\nnorthern\nnortherner\nnorthernmost\nnorthernness\nnorthwest\nnorthwesterly\nnorthwestern\nnorthwestward\nnorway\nnorwegian\nnos\nnosce\nnoscitur\nnose\nnosebag\nnosebleed\nnosed\nnosegay\nnoseless\nnosepiece\nnosewheel\nnosh-up\nnosiness\nnosology\nnostalgia\nnostalgically\nnostoc\nnostocaceae\nnostology\nnostra\nnostril\nnostrils\nnostrum\nnosy\nnot\nnota\nnotabilia\nnotability\nnotable\nnotables\nnotably\nnotary\nnotation\nnotbilities\nnotch\nnotched\nnote\nnotebook\nnotechis\nnoted\nnotemigonus\nnotepad\nnotes\nnoteworthy\nnothing\nnothingness\nnothings\nnothofagus\nnothosaur\nnothosauria\nnotice\nnoticeable\nnoticed\nnotifiable\nnotification\nnotify\nnotion\nnotional\nnotions\nnotissima\nnotochord\nnotomys\nnotonecta\nnotonectidae\nnotophthalmus\nnotoriety\nnotorious\nnotoriously\nnotornis\nnotoryctidae\nnotoryctus\nnotostraca\nnotre\nnotropis\nnotturnoitalian\nnotwithstanding\nnouakchott\nnougat\nnought\nnoumenon\nnoun\nnounbyword\nnourish\nnourished\nnourishment\nnous\nnousel\nnousle\nnouveau\nnouveau-riche\nnouvelles\nnov-esperanto\nnov-latin\nnova\nnovaculite\nnovation\nnovel\nnovelette\nnovelist\nnovelization\nnovello\nnovelty\nnovember\nnovial\nnovice\nnovitiate\nnovo\nnovobiocin\nnovosibirsk\nnovus\nnow\nnowadays\nnowhere\nnowise\nnox\nnoxious\nnoyade\nnoyerait\nnozzle\nnt\nnth\nnu\nnuance\nnuances\nnub\nnubbin\nnube\nnubere\nnubes\nnubibus\nnubiferous\nnubile\nnucellus\nnucifraga\nnuclear\nnucleolus\nnucleon\nnucleoplasm\nnucleoside\nnucleotide\nnucleus\nnuda\nnudation\nnude\nnudge\nnudibranchia\nnudism\nnudist\nnudity\nnues\nnugacity\nnugae\nnugas\nnugatory\nnuggah\nnugget\nnuisance\nnuit\nnuke\nnul\nnull\nnulla\nnulli\nnullibiety\nnullification\nnullify\nnullis\nnullity\nnullius\nnullum\nnullus\nnumb\nnumbat\nnumbed\nnumber\nnumbered\nnumbering\nnumberless\nnumbers\nnumbing\nnumbly\nnumbness\nnumbskull\nnumdah\nnumen\nnumenius\nnumerabis\nnumerable\nnumeracy\nnumeral\nnumerality\nnumerantur\nnumerate\nnumeration\nnumerator\nnumeric\nnumerical\nnumerically\nnumero\nnumerose\nnumerosity\nnumerous\nnumerousness\nnumida\nnumididae\nnuminous\nnumismatical\nnumismatics\nnumismatist\nnummary\nnummi\nnummulite\nnummulitidae\nnumps\nnumskull\nnun\nnunc\nnuncio\nnuncupation\nnuncupative\nnuncupatory\nnundinate\nnundination\nnung\nnunnation\nnunnery\nnunquam\nnuova\nnuphar\nnuptial\nnuptials\nnuptse\nnuremburg\nnurse\nnursed\nnurseling\nnursemaid\nnursery\nnursing\nnursling\nnurtural\nnurture\nnusku\nnusquam\nnut\nnutation\nnutbrown\nnutcracker\nnutgrass\nnuthatch\nnutlike\nnutmeg\nnutriment\nnutrition\nnutritional\nnutritionally\nnutritious\nnutritiousness\nnutritive\nnuts\nnutshell\nnutty\nnuture\nnux\nnuytsia\nnuzzle\nnwill\nny\nnyala\nnyamwezi\nnybble\nnyctaginaceae\nnyctaginia\nnyctalopia\nnyctanassa\nnyctereutes\nnycticebus\nnycticorax\nnyctimene\nnyctophobia\nnylon\nnylons\nnymph\nnympha\nnymphaea\nnymphaeaceae\nnymphalid\nnymphalidae\nnymphalis\nnymphet\nnymphicus\nnympholepsy\nnympholept\nnymphomania\nnymphomaniac\nnymphomaniacal\nnyssa\nnyssaceae\nnystagmus\nnystatin\no\no'clock\noaf\noahu\noak\noaken\noakland\noakum\noar\noarfish\noars\noarsman\noarsmanship\noarswoman\noasis\noast\noat\noatcake\noaten\noath\noaths\noatmeal\noats\noaxaca\nob\nobbligato\nobduction\nobduracy\nobdurate\nobduration\nobeah\nobeche\nobediant\nobedience\nobedient\nobediently\nobedlam\nobeisance\nobelisk\noberon\nobese\nobesity\nobey\nobeyed\nobfuscate\nobfuscated\nobi\nobiism\nobit\nobiter\nobituary\nobject\nobjectification\nobjection\nobjectionable\nobjective\nobjectively\nobjectiveness\nobjectivity\nobjects\nobjurgate\nobjurgation\nobjurgatory\noblanceolate\noblate\noblateness\noblation\noblection\nobligate\nobligated(p)\nobligation\nobligational\nobligations\nobligatorily\nobligatory\noblige\nobliged\nobligee\nobligefr\nobliging\nobligingly\nobligor\nobliquation\noblique\nobliquely\nobliqueness\nobliquity\nobliterable\nobliterate\nobliterated\nobliterating\nobliteration\noblivion\noblivious\noblivious(p)\nobliviousnedd\nobliviousness\noblong\nobloquy\nobmutescence\nobnoxious\nobnubilated\noboe\noboist\nobolus\nobovate\nobreption\nobreptitious\nobscene\nobscenely\nobscenity\nobscura\nobscurantism\nobscurantist\nobscuration\nobscure\nobscurely\nobscureness\nobscurity\nobscurius\nobscurum\nobscurus\nobsecration\nobsecratory\nobsequies\nobsequious\nobsequiously\nobsequiousness\nobservance\nobservant\nobservantly\nobservation\nobservatory\nobserve\nobserved\nobserver\nobserving\nobsessed\nobsession\nobsessional\nobsessive-compulsive\nobsessiveness\nobsidian\nobsidional\nobsolescence\nobsolescent\nobsoleseence\nobsolete\nobstacle\nobstant\nobstante\nobstare\nobstetric\nobstetrician\nobstetrics\nobstinacy\nobstinancy\nobstinate\nobstinately\nobstinateness\nobstipation\nobstreperous\nobstreperously\nobstreperousness\nobstruct\nobstructed\nobstruction\nobstructionism\nobstructionist\nobstructive\nobstructively\nobstruent\nobstupefaction\nobstupui\nobtain\nobtainable\nobtainment\nobtenebration\nobtensible\nobtest\nobtestation\nobtrectation\nobtrude\nobtruncate\nobtrusion\nobtrusive\nobtrusively\nobtrusiveness\nobtund\nobtuse\nobtuseness\nobumbrate\nobumbration\nobverse\nobviate\nobviation\nobvious\nobviously\nobviousness\noca\nocarina\noccasio\noccasion\noccasional\noccasional(a)\noccasionally\noccasionem\noccasioner\noccasions\noccidental\noccipital\nocciput\nocclude\noccluded\nocclusion\nocclusive\noccult\noccultation\noccultist\noccultness\noccupancy\noccupant\noccupation\noccupational\noccupations\noccupied\noccupier\noccupy\noccupying\noccur\noccurence\noccurred\noccurrence\noccurrere\noccurrrenit\noccursion\nocean\noceanfront\noceangoing\noceania\noceanic\noceanid\noceanites\noceanographer\noceanography\noceanus\nocellated\nocelot\nocher\nochlocracy\nochna\nochnaceae\nochotona\nochotonidae\nochreous\nochroma\nocimum\noclock\nocotillo\noctagon\noctahedron\noctal\noctameter\noctane\noctangular\noctant\noctateuch\noctave\noctavo\noctet\noctifid\noctober\noctodecimo\noctogenarian\noctopod\noctopoda\noctopodidae\noctopus\noctoroon\noctosyllabic\noctosyllable\noctroi\noctuple\nocular\noculi\noculis\noculist\noculomotor\nocyurus\nod\nodalisque\nodd\nodd-job(a)\nodd-pinnate\noddity\noddments\nodds\nodds-on\node\noder\nodessa\nodi\nodin\nodiosa\nodious\nodium\nodobenidae\nodobenus\nodocoileus\nodometer\nodonata\nodonate\nodontalgia\nodontoceti\nodontoglossum\nodontoid\nodontophorus\nodor\nodorament\nodorant\nodoriferous\nodorless\nodorous\nodylic\nodyllic\nodysseus\nodyssey\nodzookens\noecanthus\noecology\noecumenical\noedematous\noedipus\noedogoniaceae\noedogoniales\noedogonium\noemula\noenanthe\noenomancy\noenophile\noenothera\noer\noersted\noertop\noestridae\noestrus\noeuvre\noevum\nof\noff\noff(p)\noff-base\noff-broadway\noff-center\noff-day\noff-hand\noff-limits\noff-line\noff-peak\noff-putting\noff-road\noff-season\noff-site\noff-street\noff-the-rack\noffal\noffbring\noffend\noffended\noffender\noffending\noffense\noffenseless\noffensive\noffensively\noffensiveness\noffer\noffered\noffering\noffers\noffertory\noffhand\noffice\noffice-bearer\nofficeholder\nofficer\noffices\nofficial\nofficialese\nofficialism\nofficially\nofficiate\nofficio\nofficious\nofficiously\nofficiousness\noffing\noffish\noffones\noffprint\noffroad\noffscourings\noffset\noffshoot\noffshore\noffside\noffspring\noffstage\noffuscate\noffuscation\nofiform\noflove\nofmake\nofo\noft\noften\noftener\noftenness\noftentimes\noftness\nofttimes\nogcocephalidae\nogham\noglala\nogle\nogler\nogni\nogre\nogress\noh\nohio\nohioan\nohm\nohmage\nohmic\nohmmeter\nohne\noil\noil-bearing\noil-fired\noilbird\noilcan\noilcloth\noiled\noilfield\noilfish\noiling\noilman\noilpaper\noils\noilseed\noilskin\noilskins\noilstone\noily\noinomania\nointer\nointment\noireachtas\nojibwa\nok\noka\nokapi\nokapia\nokay\nokinawa\noklahoma\nokra\nola\nolantern\nold\nold(a)\nold-fashioned\nold-fashionedness\nold-maidish\nold-man-of-the-woods\nold-time\nold-timer\nold-world\noldbuck\nolden\nolder\noldest\noldfashioned\noldhat\noldish\noldness\noldster\noldwomanish\noldworld\nolea\noleaceae\noleaceous\noleagine\noleaginous\noleales\noleander\noleandra\noleandraceae\nolearia\noleaster\noleophilic\noleophobic\noleoresin\noleum\nolfaction\nolfactories\nolfactory\nolfersia\nolibanum\nolid\nolidous\noligarch\noligarchic\noligarchy\noligocene\noligochaeta\noligochaete\noligodendrocyte\noligodendroglia\noligomenorrhea\noligoplites\noligoporus\noligosaccharide\noliguria\nolim\nolio\nolive\nolive-brown\nolive-drab\nolivebranch\noliver\nolivine\nollapodrida\nollari\nolm\nolmo\nology\nolympia\nolympiad\nolympian\nolympic\nolympus\nom\nomaha\noman\nomani\nombeer\nombres\nombrometer\nombu\nombudsman\nomdurman\nomega\nomelet\nomen\nomentum\nomicron\nominate\nominia\nominous\nominously\nomission\nomit\nomitted\nommastrephes\nomne\nomnem\nomnes\nomni\nomnia\nomnialatin\nomniation\nomnib\nomnibus\nomnibus(a)\nomnidirectional\nomnifarious\nomnific\nomniform\nomniformity\nomnigenous\nomniousness\nomnipotence\nomnipotent\nomnipresence\nomnipresent\nomnis\nomniscience\nomniscient\nomnium\nomnivore\nomnivorous\nomomyid\nomophagia\nomophagic\nomophagous\nomotic\nomphalos\nomphaloskepsis\nomphalotus\nompredre\nomsk\non\non(p)\non-key\non-license\non-line\non-line(a)\non-site\non-street\non-the-job\non-the-spot(a)\nonager\nonagraceae\nonce\nonce-over\nonchocerciasis\nonchorynchus\noncidium\noncogene\noncological\noncologist\noncology\nondatra\nondine\none\none(a)\none-and-one\none-armed\none-billionth\none-dimensionality\none-eared\none-eighth\none-eyed\none-fifth\none-fourth\none-half\none-hitter\none-hundredth\none-liner\none-man(a)\none-millionth\none-ninth\none-on-one\none-party\none-piece\none-quadrillionth\none-quintillionth\none-seventh\none-sided\none-sixth\none-step\none-tenth\none-third\none-thousandth\none-trillionth\none-upmanship\none-way\noneborse\noneeyed\nonehorse\noneida\noneirocritic\noneirology\noneiromancy\noneness\nonepan\nonerous\nonerously\nones\noneself\nonesided\noneslef\nongoing\nonine\nonion\nonionskin\noniscidae\noniscus\nonlooker\nonly\nonobrychis\nonoclea\nonomancy\nonomasticon\nonomastics\nonomatopoeia\nonomatopoeic\nonondaga\nononis\nonopordum\nonor\nonosmodium\nonrush\nons\nonself\nonset\nonshore\nonside\nonslaught\nonstage\nontario\nontogenetic\nontogeny\nontological\nontology\nonus\nonvert\nonward\nonychium\nonychogalea\nonychomancy\nonychomys\nonychophora\nonychophoran\nonymous\nonyx\noocyte\noogamy\noogenesis\noogenetic\noolong\noomycetes\noophorectomy\noosphere\noospore\nooze\noozing\nopacity\nopacous\nopah\nopal\nopalesce\nopalescence\nopalescent\nopaline\nopaque\nopaquely\nopaqueness\nope\nopen\nopen(a)\nopen-collared\nopen-ended\nopen-minded\nopenbill\nopencast\nopened\nopener\nopeneyed\nopenhearted\nopening\nopenly\nopenmouthed\nopenness\nopenwork\nopepe\nopera\noperable\noperae\noperagoer\noperahouse\noperand\noperandi\noperate\noperatic\noperating\noperation\noperational\noperationalism\noperationalist\noperationally\noperations\noperative\noperatively\noperator\noperculate\noperculated\noperculum\noperetta\noperose\noperoseness\noperosity\nopes\nopheodrys\nophicleide\nophidiidae\nophiodon\nophiodontidae\nophioglossaceae\nophioglossales\nophioglossum\nophiolatry\nophiology\nophiomancy\nophiophagus\nophisaurus\nophite\nophiurida\nophiuroidea\nophrys\nophthalmia\nophthalmic\nophthalmologist\nophthalmology\nophthalmoscope\nopiate\nopima\nopinative\nopinator\nopini\u0003tre\nopiniative\nopiniator\nopiniatry\nopiniodz\nopinion\nopinionate\nopinionated\nopinionatedness\nopinionatist\nopinionative\nopinionativeness\nopinioned\nopinionist\nopinions\nopinon\nopintiveness\nopisthobranchia\nopisthocomidae\nopisthocomus\nopisthognathidae\nopisthognathous\nopitulation\nopium\nopopanax\noportet\nopossum\nopp\noppenheimer\noppidan\noppilation\nopponent\nopportune\nopportunely\nopportuneness\nopportunism\nopportunist\nopportunity\nopposable\noppose\nopposed\nopposing\nopposit\nopposite\noppositely\noppositeness\nopposition\noppositionist\noppress\noppressed\noppression\noppressive\noppressively\noppressor\nopprobrious\nopprobrium\noppugn\noppugnancy\noppugnation\nops\nopsimathy\nopsin\nopt\noptative\noptez\noptic\noptical\noptically\noptician\noptics\noptimacy\noptimally\noptimates\noptime\noptimism\noptimist\noptimistic\noptimistically\noptimum\noptinionist\noption\noptional\noptionally\noptometrist\noptometry\nopulence\nopulent\nopum\nopuntia\nopuntiales\nopus\nopuscule\nor\nora\norach\noracle\noracles\noracular\norad\noral\norally\noran\norange\norangeade\norangecolored\norangeman\norangery\norangewood\norangutan\noratio\noration\norator\noratorical\noratorio\noratory\noratress\noratrix\norb\norbed\norbem\norbicular\norbiculate\norbignya\norbit\norbital\norbs\norc\norchard\norchestia\norchestiidae\norchestra\norchestral\norchestrated\norchestration\norchestrator\norchestrina\norchid\norchidaceae\norchidales\norchidectomy\norchil\norchis\norchitis\norchotomy\norcinus\nordain\nordained\nordeal\norder\nordered\norderin\nordering\norderless\norderliness\norderly\norders\nordinaire\nordinal\nordinance\nordinand\nordinariness\nordinary\nordinate\nordination\nordnance\nordo\nordonnance\nordovician\nordure\nore\noread\noreamnos\norectolobidae\norectolobus\noregano\noregon\noregonian\noreilles\noreo\noreopteris\noreortyx\norestes\norgan\norgan-grinder\norgandy\norganelle\norganic\norganically\norganicism\norganicistic\norganification\norganism\norganismal\norganist\norganization\norganizational\norganizationally\norganize\norganized\norganizer\norganizing\norganography\norganon\norgans\norganza\norgasm\norgastic\norgiastic\norgies\norgy\noriel\norient\noriental\norientalist\norientate\norientation\noriented\norienting\norifice\noriflamb\noriflamme\noriganum\norigenism\norigianlly\norigin\noriginal\noriginalism\noriginality\noriginally\noriginate\norigination\noriginator\norigine\norigo\norinoco\noriolidae\noriolus\norion\norions\norismological\norismology\norison\norissa\norites\noritur\noriya\norizaba\norlando\norleanais\norleanist\norleans\norlon\norly\normazd\normer\normolu\normosia\normuzd\nornament\nornamental\nornamentation\nornamented\nornate\nornately\nornateness\nornature\nornavit\norniscopy\nornithine\nornithischia\nornithischian\nornithogalum\nornithological\nornithologist\nornithology\nornithomancy\nornithomimid\nornithomimida\nornithopod\nornithorhynchidae\nornithorhynchus\noro\norobanchaceae\norontium\noropharynx\norotund\norotundity\norphan\norphanage\norpheus\norphrey\norpiment\norpine\norpington\norrery\norrisroot\nortalis\northicon\northochorea\northoclase\northodontic\northodontics\northodontist\northodox\northodoxy\northoepy\northogonal\northogonality\northograph\northographic\northography\northology\northometry\northomyxovirus\northopedic\northopedics\northopedist\northopedy\northopraxy\northopristis\northoptera\northoscope\northotomus\northotropous\norti\nortolan\norts\nortygan\noryalist\norycteropodidae\norycteropus\noryctography\noryctolagus\noryctology\noryx\noryza\noryzomys\noryzopsis\norzo\nos\nosage\nosaka\noscan\noscar\noscillate\noscillating\noscillation\noscillator\noscillatoriaceae\noscillatory\noscillogram\noscillograph\noscilloscope\noscine\noscines\noscitancy\noscitant\noscitation\nosco-umbrian\nosculate\nosculation\nosculatory\nosier\nosiris\noslo\nosmanli\nosmanthus\nosmeridae\nosmerus\nosmium\nosmosis\nosmotic\nosmotically\nosmundaceae\nosprey\nossa\nosseous\nossete\nossicle\nossicular\nossiferous\nossific\nossification\nossified\nossify\nossuary\nostariophysi\nosteal\nosteichthyes\nosteitis\nostensible\nostensibly\nostentation\nostentatious\nostentatiously\nosteoarthritis\nosteoblast\nosteology\nosteolysis\nosteoma\nosteomalacia\nosteomyelitis\nosteopath\nosteopathist\nosteopathy\nosteopetrosis\nosteoporosis\nosteosarcoma\nosteostracan\nosteostraci\nostiary\nostinato\nostiole\nostium\nostler\nostraciidae\nostracism\nostracize\nostracoda\nostracoderm\nostracodermi\nostrea\nostreidae\nostrich\nostrogoth\nostrya\nostryopsis\nostyak\not\notalgia\notaria\notariidae\note\nothello\nothellos\nother\nother(a)\notherness\nothers\notherwise\nothewisp\nothodox\nothonna\notia\notic\notides\notididae\notiose\notiosity\notis\notitis\notium\noto\notology\notoscope\notrigger\nottar\nottawa\notter\notterhound\notto\nottoman\notus\nou\nouachita\noublic\noubliette\nouch\noudre\nough\nought\nouguiya\noui\nouija\nounce\nouphe\nour\nouranopithecus\nouranos\nours\nourselves\noust\nouster\nout\nout(a)\nout(p)\nout-and-outer\nout-basket\nout-of-bounds\nout-of-school\nout-of-the-way\nout-of-town\noutage\noutas\noutback\noutbalance\noutboard\noutbrave\noutbrazen\noutbreak\noutbred\noutbuilding\noutburst\noutcast\noutcaste\noutclassed\noutcome\noutcrop\noutcry\noutdo\noutdoor\noutdoor(a)\noutdoors\noutdoorsy\nouter\nouter(a)\noutercourse\noutermost\nouterwear\noutfall\noutfield\noutfielder\noutfit\noutfitted\noutfitter\noutfitting\noutflank\noutflow\noutgate\noutgeneral\noutgo\noutgoing\noutgrow\noutgrowth\noutherod\nouthouse\nouting\noutjump\noutlandish\noutlandishly\noutlandishness\noutlast\noutlaw\noutlawry\noutleap\noutlet\noutlier\noutline\noutlines\noutlive\noutlook\noutlying\noutlying(a)\noutmaneuver\noutmarch\noutnumber\noutpatient\noutport\noutpost\noutpour\noutpouring\noutput\noutrage\noutrageous\noutrageously\noutrageousness\noutrance\noutrank\noutre\noutreach\noutreckon\noutride\noutrider\noutrigged\noutrigger\noutright\noutrival\noutrun\nouts\noutscourings\noutset\noutshine\noutside\noutside(a)\noutsider\noutsize\noutskirt\noutskirts\noutsole\noutspan\noutspeak\noutspoken\noutspokenly\noutspread\noutstanding\noutstandingly\noutstare\noutstation\noutstep\noutstretched\noutstrip\noutstroke\nouttake\nouttalk\noutthrust\noutvie\noutvote\noutward\noutward-developing\noutward-moving\noutwardly\noutwardness\noutwards\noutweigh\noutwit\noutwork\noutworn\nouverts\nouzo\noval\novalipes\novarian\novary\novate\novation\noven\novenbird\novenware\nover\nover-the-counter\noverabound\noverabundance\noverabundant\noverachievement\noverachiever\noveract\noveracted\noveractivity\noverage\noverall\noveralls\noverambitious\noveranxiety\noveranxious\noverarch\noverawe\noverawed\noverbalance\noverbalanced\noverbear\noverbearance\noverbearing\noverbearingly\noverbid\noverblown\noverboard\noverborne\noverburden\noverbusy\novercapitalization\novercareful\novercast\novercautious\novercharge\novercharged\novercoat\novercolor\novercome\novercompensation\noverconfidence\noverconfident\novercredulity\novercredulous\novercritical\novercurious\noverdate\noverdelicate\noverdistension\noverdo\noverdone\noverdose\noverdraft\noverdraw\noverdrawn\noverdressed\noverdrive\noverdue\novereager\novereat\noveremotional\noveremphasis\noverenthusiastic\noverestimate\noverestimated\noverestimation\noverexcited\noverexertion\noverexploitation\noverexposure\noverfatigued\noverfed\noverfeed\noverfeeding\noverflow\noverflowing\noverfond\novergarment\novergo\novergorge\novergorged\novergreedy\novergrown\novergrowth\noverhand\noverhang\noverhanging\noverhaul\noverhead\noverhear\noverheated\noverheating\noverindulgence\noverjoyed\noverjump\noverkill\noverladen\noverland\noverlap\noverlapping\noverlarge\noverlay\noverleaf\noverleap\noverliberal\noverlie\noverload\noverlook\noverlooked\noverlooker\noverloook\noverlord\noverlordship\noverlying\novermantel\novermaster\novermatch\novermeasure\novermodest\novermuch\novernight\novernighter\noverofficious\noverpaid\noverpass\noverpayment\noverpersuade\noverplus\noverpoise\noverpopulation\noverpower\noverpowering\noverpraise\noverpriced\noverprint\noverprize\noverproduction\noverprotective\noverproud\noverrate\noverreach\noverreaching\noverreaction\noverreckon\noverrefined\noverrefinement\noverreligious\noverride\noverriding\noverrighteous\noverripe\noverrule\noverruling\noverrun\noverscrupulous\noversea\noverseas\noverseer\noversensitive\noversensitiveness\noverserious\noverset\noversexed\novershadow\novershoe\novershoot\novershot\noverside\noversight\noversimplification\noversimplified\noverskip\noverskirt\noversleep\noversolicitous\noverspent\noverspread\noverstate\noverstep\noverstock\noverstrain\noverstrung\noverstuffed\noversubscribed\noversupply\noversuspicious\novert\novertake\novertaken\novertask\novertax\noverthrow\noverthwart\novertime\novertired\novertly\novertolerance\novertone\novertop\noverture\noverturn\noverturned\novervaliant\novervaluation\novervalue\noverweening\noverweigh\noverwhelm\noverwhelmed\noverwhelming\noverwhelmingly\noverwise\noverwork\noverwrought\noverzealous\novibos\novid\noviparous\novipositor\noviraptorid\novis\novo\novoid\novoviviparous\novrerhasty\novular\novulation\novule\novum\now\nowe\nowing\nowing(p)\nowl\nowlet\nowlish\nowlishly\nowls\nowlslight\nown\nown(a)\nowned\nowner\nowner-driver\nowner-occupied\nowner-occupier\nownership\nowns\nowr\nox\nox-eyed\noxalacetate\noxalate\noxalidaceae\noxalis\noxandra\noxaprozin\noxazepam\noxbow\noxbridge\noxcart\noxeye\noxford\noxford-gray\noxgoad\noxicillin\noxidant\noxidase\noxidation\noxidation-reduction\noxidative\noxide\noxidizable\noxidized\noxidoreductase\noxime\noximeter\noxlip\noxonian\noxreim\noxtail\noxtant\noxtongue\noxyacetylene\noxyacid\noxybelis\noxycephaly\noxydendrum\noxygen\noxygenase\noxygenated\noxygenation\noxygon\noxygonal\noxylebius\noxymoron\noxyphenbutazone\noxytetracycline\noxytocin\noxytropis\noxyura\noxyuranus\noxyuridae\noyabun\noyer\noyez\noyster\noystercatcher\nozarks\nozone\nozonium\nozothamnus\np\npa\npa'anga\npablum\npabulum\npaca\npacatolus\npace\npaced\npacem\npacemaker\npacer\npaces\npachinko\npachisi\npachuco\npachycephala\npachycephalosaur\npachyderm\npachydermatous\npachyrhizus\npachysandra\npachytene\npacific\npacification\npacified\npacifism\npacifist\npacifist(a)\npacifistically\npacify\npacing\npack\npackage\npackaged\npackaging\npacked\npacker\npackera\npacket\npackhorse\npacking\npackinghouse\npackrat\npacksaddle\npackthread\npact\npaction\npad\npadauk\npadda\npadding\npaddle\npaddlebox\npaddlefish\npaddlewheel\npaddock\npaddy\npademelon\npadishah\npadlock\npadre\npadrone\npadua\npaean\npaella\npaena\npaenitentiae\npaenititentiae\npaeonia\npaeoniaceae\npagan\npaganism\npage\npageant\npageantry\npageboy\npagellus\npagina\npagination\npaging\npagoda\npagophila\npagophilus\npagri\npagrus\npaguridae\npagurus\npah\npahautea\npaid\npaid-up\npail\npaillard\npaillasse\npain\npain-free\npained\npainful\npainfully\npainfulness\npainim\npainkiller\npainless\npainlessly\npains\npainstaking\npainstakingly\npaint\npaintable\npaintbox\npaintbrush\npainted\npainter\npainterly\npainting\npair\npaired\npairs\npaisa\npaisley\npaiute\npaiwanic\npaixhan\npajama\npajamas\npakistan\npakistani\npaktong\npal\npalace\npaladin\npalaemon\npalaemonidae\npalaestra\npalaetiology\npalaic\npalais\npalang\npalankeen\npalanquin\npalaquium\npalatability\npalatable\npalatably\npalatal\npalate\npalatial\npalatinate\npalatine\npalaver\npalce\npale\npaleacrita\npaleencephalon\npaleface\npalefaced\npalely\npaleness\npaleoanthropic\npaleoanthropological\npaleoanthropology\npaleobiology\npaleobotany\npaleocene\npaleoclimatology\npaleocortical\npaleocrystic\npaleodendrology\npaleoethnography\npaleogeography\npaleogeology\npaleography\npaleolith\npaleolithic\npaleology\npaleomammalogy\npaleontological\npaleontologist\npaleontology\npaleopathology\npaleornithology\npaleozoic\npaleozoology\npalermo\npalestine\npalestinian\npalestra\npalestric\npalestrical\npaletiology\npaletot\npalette\npalfrey\npalimony\npalimpsest\npalindrome\npaling\npalingenesis\npalingenetic\npalinode\npalinody\npalinuridae\npalinurus\npalisade\npalish\npaliurus\npalki\npall\npall-mall\npalladium\npallbearer\npallescere\npallet\npallette\npalliament\npalliate\npalliation\npalliative\npallid\npallidity\npallidly\npallium\npallmall\npallone\npallor\npalm\npalmae\npalmales\npalmam\npalmar\npalmate\npalmated\npalmately\npalmatifid\npalmer\npalmetto\npalmiped\npalmist\npalmistry\npalmitin\npalms\npalmy\npalmyra\npalometa\npalomino\npalpability\npalpable\npalpably\npalpation\npalpitate\npalpitation\npalpus\npalsgrave\npalsied\npalsy\npalsystricken\npalter\npaltering\npaltriness\npaltry\npaludal\npamlico\npampas\npamper\npampered\npampering(a)\npamphjlet\npamphlet\npamphleteer\npan\npanacea\npanache\npanama\npanamanian\npanamerican\npananquin\npanatela\npanax\npancake\npanchromatic\npancreas\npancreatic\npancreatitis\npancytopenia\npandanaceae\npandanales\npandanus\npandar\npandean\npandect\npandemic\npandemonium\npander\npandiculation\npandion\npandionidae\npandora\npandoras\npandoz\npandurate\npane\npaned\npanegyric\npanegyrical\npanegyrize\npanel\npaneled\npaneling\npanelist\npanencephalitis\npanfish\npang\npangaea\npangermanic\npangloss\npangolin\npangs\npanhandle\npanhandler\npanharmonic\npanhellenic\npani\npanic\npanicky\npanicle\npanicled\npaniculate\npanicum\npanier\npanini\npanipat\npannel\npannier\npannikin\npanofsky\npanonychus\npanoplied\npanoply\npanoptic\npanopticon\npanorama\npanoramic\npanpipe\npansophy\npansy\npant\npantaloon\npantaloons\npantechnicon\npantheist\npantheistic\npantheon\npanther\npanthera\npantie\npantile\npanting\npantingly\npantisocracy\npanto\npantograph\npantologist\npantology\npantometer\npantomime\npantomimic\npantomimist\npantophagous\npantophagy\npantotheria\npantropical\npantry\npants\npantyhose\npanurgy\npanzer\npap\npapa\npapacy\npapain\npapal\npapaver\npapaveraceae\npapaverine\npapaw\npapaya\npapeete\npaper\npaperback\npaperboard\npaperboy\npaperclip\npaperhanger\npapering\npapers\npaperweight\npaperwork\npapery\npaphian\npaphiopedilum\npapier-mache\npapiermache\npapilionaceae\npapilionaceous\npapilionoideae\npapilla\npapillary\npapillate\npapilloma\npapillon\npapillose\npapillote\npapilose\npapio\npapism\npapist\npapistry\npapoose\npapovavirus\npappose\npappous\npappus\npaprika\npaprilus\npapuan\npapula\npapule\npapulous\npapyrus\npar\npara\nparaafin\nparable\nparabola\nparabolic\nparaboloid\nparaboloidal\nparacentesis\nparacheirodon\nparachronism\nparachute\nparachutist\nparaclete\nparacme\nparade\nparadiddle\nparadigm\nparadigmatic\nparadisaeidae\nparadise\nparadisiacal\nparadox\nparadoxical\nparadoxically\nparadoxurus\nparaesthesia\nparaffin\nparagon\nparagonite\nparagram\nparagraph\nparagrapher\nparaguay\nparaguayan\nparakeet\nparalanguage\nparalegal\nparalelpsis\nparalepsis\nparalichthys\nparalithodes\nparallax\nparallel\nparallelepiped\nparallelism\nparallelogram\nparallelopiped\nparalogism\nparalogize\nparalogy\nparalysis\nparalytic\nparalyticdyspeptic\nparalyze\nparalyzed\nparamagnet\nparamagnetic\nparamagnetism\nparamaribo\nparamecium\nparamedic\nparameter\nparametric\nparamilitary\nparamnesia\nparamount\nparamountcy\nparamour\nparamyxovirus\nparana\nparang\nparanoia\nparanoid\nparanomasia\nparanormal\nparanthias\nparanthropus\nparapet\nparaph\nparaphernalia\nparaphrase\nparaphrast\nparaphrastic\nparaphysis\nparaplegia\nparaplegic\nparapodium\nparaprofessional\nparapsychological\nparapsychologist\nparasail\nparasailing\nparascalops\nparashurama\nparasitaxus\nparasite\nparasitic\nparasitical\nparasitically\nparasitism\nparasol\nparasympathetic\nparasympathomimetic\nparathelypteris\nparatrooper\nparatroops\nparatus\nparatyphoid\nparazoa\nparbleu\nparboil\nparbuckle\nparcae\nparcel\nparcels\nparcenary\nparcere\nparch\nparched\nparcheesi\nparchment\nparcity\npardon\npardonable\npardoner\npardonner\npardonnerfrench\npardons\npare\nparegmenon\nparegoric\npareil\nparenchyma\nparendum\nparent\nparentage\nparental\nparentally\nparented\nparenteral\nparenterally\nparenthese\nparenthesis\nparenthetic\nparenthetical\nparenthetically\nparenthically\nparenthood\nparer\npares\nparesis\nparesthesia\nparetic\nparfait\nparget\npargeting\nparhelion\npari\npariah\nparian\nparibus\nparidae\nparies\nparietal\nparietales\nparietaria\nparietes\nparietibus\nparimutuel\nparing\nparings\nparis\nparish\nparishioner\nparisian\nparisienne\nparisology\nparit\nparitor\nparitur\nparity\nparium\nparjanya\nparji\npark\nparka\nparked\nparkeriaceae\nparkersburg\nparkia\nparking\nparkinson\nparkinsonia\nparl\nparlance\nparlay\nparlementaire\nparler\nparley\nparliament\nparliamentarian\nparliamentary\nparlor\nparlormaid\nparlous\nparmelia\nparmeliaceae\nparmesan\nparnassia\nparnassus\nparochetus\nparochial\nparochialism\nparochially\nparodist\nparody\nparol\nparole\nparoles\nparolles\nparonomasio\nparonychia\nparonymous\nparophrys\nparotid\nparotitis\nparoxysm\nparoxysmal\nparpeer\nparquet\nparquetry\nparr\nparricide\nparrot\nparrotfish\nparrotia\nparrotiopsis\nparrotlike\nparrott\nparry\npars\nparse\nparsec\nparsee\nparsiism\nparsimonia\nparsimonious\nparsimoniousness\nparsimony\nparsley\nparsnip\nparson\nparsonage\nparsque\npart\npart(a)\npart-owner\npart-singing\npart-time\npart-timer\npartake\npartaker\npartaking\nparte\nparted\npartem\nparterre\nparthenium\nparthenocissus\nparthenogenesis\nparthenon\nparthia\nparthian\nparthis\nparti\npartial\npartiality\npartially\npartialness\npartibility\npartible\nparticeps\nparticipant\nparticipate\nparticipation\nparticipator\nparticipatory\nparticipial\nparticiple\nparticle\nparticles\nparticula\nparticular\nparticular(a)\nparticularism\nparticularistic\nparticularity\nparticularization\nparticularize\nparticularized\nparticularly\nparticulars\nparticulate\npartie\npartimony\nparting\npartisan\npartisanship\npartition\npartitioned\npartitionist\npartitive\npartlet\npartly\npartner\npartnership\npartout\npartria\npartridge\npartridgeberry\nparts\npartsong\nparturiency\nparturient\nparturition\nparturiunt\nparty\nparty(a)\nparty-spirited\npartycolored\npartygoer\nparula\nparulidae\nparus\nparva\nparvati\nparvenu\nparvis\nparvitude\nparvity\nparvo\nparvovirus\nparvum\nparvumparvo\npas\npasadena\npascal\npasch\npaschal\npascit\npasfrench\npasha\npashalic\npashto\npasigraphie\npasigraphy\npasiphae\npasqueflower\npasquinade\npass\npassable\npassably\npassado\npassage\npassages\npassageway\npassamaquody\npassamezzoitalina\npassant\npassant(ip)\npassati\npasse\npasse-partout\npassed\npassenger\npassepartout\npasser\npasserby\npasseridae\npasseriformes\npasserina\npasserine\npassero\npassetemps\npassibus\npassiflora\npassifloraceae\npassim\npassing\npassing(a)\npassion\npassionate\npassionately\npassionflower\npassionless\npassionqueller\npassions\npassive\npassively\npassiveness\npassivity\npasskey\npassover\npassparole\npassport\npassu\npassword\npast\npast(a)\npasta\npaste\npaste-up\npasteboard\npastel\npastern\npasteur\npasteurian\npasteurization\npasteurize\npasteurized\npasticcio\npastiche\npasties\npastil\npastille\npastime\npastinaca\npastis\npastness\npastor\npastoral\npastorale\npastorate\npastorship\npastrami\npastry\npasturage\npasture\npasty\npasurage\npat\npataca\npatagonia\npatagonian\npatas\npatch\npatchcord\npatched\npatchily\npatchiness\npatching\npatchouli\npatchwork\npatchy\npate\npatefaction\npatella\npatellar\npatellidae\npatent\npatented\npatentee\npater\npatera\npaterfamilias\npaternal\npaternalism\npaternalistic\npaternally\npaternity\npaternoster\npath\npathetic\npathetically\npathless\npathogen\npathogenesis\npathogenically\npathognomonic\npathological\npathologically\npathology\npathos\npathoscopic\npaths\npathway\npatience\npatient\npatiently\npatimur\npatina\npatio\npatisserie\npatitur\npatois\npatria\npatriae\npatrial\npatriarch\npatriarchal\npatriarchate\npatriarchic\npatriarchs\npatriarchy\npatricentric\npatrician\npatricide\npatrick\npatrilineage\npatrilineal\npatrilineally\npatrimony\npatriot\npatriotic\npatriotically\npatriotism\npatristic\npatrol\npatroller\npatrolling\npatrolman\npatron\npatronage\npatroness\npatronize\npatronized\npatronymic\npatten\npattens\npatter\npatterer\npattern\npatterned\npatternmaker\npattes\npatty\npatty-pan\npatulous\npatwin\npatzer\npauciloquy\npaucis\npaucity\npaul\npaulo\npaunch\npauper\npauperis\npauperism\npauperization\npauperize\npauropoda\npause\npauvre\npavage\npavane\npave\npaved\npavement\npavilion\npaving\npavior\npavis\npavlov\npavlovian\npavo\npavonia\npavonine\npaw\npawky\npawl\npawn\npawnbroker\npawnee\npawnshop\npawpaw\npax\npay\npay-phone\npayables\npaycheck\npayday\npaydown\npaye\npayee\npayena\npayer\npaying\npaymaster\npayment\npaynes\npaynim\npayoff\npayola\npayroll\npays\npayslip\npb\npc\npcoat\npd\npe\npea\npea-green\npeace\npeaceable\npeaceableness\npeaceably\npeaceful\npeacefully\npeacekeeper\npeacemaker\npeacetime\npeach\npeachcolored\npeachick\npeacock\npeacock-blue\npeacock-throne\npeacocks\npeafowl\npeahen\npeak\npeaked\npeaks\npeaky\npeal\npeanut\npear\npearl\npearlfish\npearliness\npearlite\npearls\npearlwort\npearly\npearmain\npearshaped\npeas\npeasant\npeasanthood\npeasantry\npeat\npeaty\npeavey\npeba\npebble\npebbles\npecan\npeccability\npeccable\npeccadillo\npeccancy\npeccant\npeccare\npeccary\npeccavi\npeck\npecker\npeckish\npecksniff\npecopteris\npecos\npectic\npectin\npectinate\npectinated\npectinibranchia\npectinidae\npectoral\npectore\npectoris\npeculate\npeculation\npeculator\npeculiar\npeculiar(a)\npeculiarities\npeculiarity\npeculiarly\npecuniam\npecuniary\npecunious\nped\npedagogical\npedagogue\npedal\npedaler\npedaliaceae\npedant\npedantic\npedantically\npedantry\npedate\npeddle\npeddler\npeddlers\npeddling\npede\npederast\npederastic\npederasty\npederero\npedestal\npedestrian\npediatric\npediatrics\npedibus\npedibusque\npedicab\npedicel\npedicle\npediculati\npediculidae\npediculosis\npediculus\npedicure\npedigree\npedigree(a)\npedilanthus\npediment\npediocactus\npedioecetes\npedionomus\npedipalpi\npedir\npedlar\npedodontist\npedofile\npedometer\npeduncle\npedunculate\npee\npeeing\npeek\npeekaboo\npeel\npeeler\npeelhouse\npeeling\npeep\npeephole\npeepshow\npeer\npeerage\npeerless\npeers\npeeve\npeevish\npeevishly\npeg\npegasus\npegboard\npegged-down\npegging\npegmatite\npegology\npegomancy\npegs\npeice\npeindre\npeine\npeines\npeirce\npejoratively\npekinese\npelagian\npelagic\npelargonium\npelecanidae\npelecaniformes\npelecanoididae\npelecanus\npelerine\npeleus\npelf\npelham\npelican\npelion\npelisse\npellaea\npellagra\npellet\npellicle\npellicularia\npellitory\npellitory-of-the-wall\npellmell\npellucid\npellucidity\npellucidness\npelobatidae\npeloponnese\npeloponnesian\npelote\npelt\npeltandra\npeltate\npeltry\npeludo\npelvic\npelvis\npelycosaur\npelycosauria\npembroke\npemmican\npempheridae\npemphigus\npen\npen-and-ink\npena\npenal\npenalties\npenalty\npenance\npenandink\npenates\npence\npenchant\npencil\npenciled\npendant\npendency\npendent\npendente\npending\npendragon\npendulate\npendulous\npendulum\npeneidae\npenelope\npenetrability\npenetrable\npenetralia\npenetrate\npenetrated\npenetrating\npenetratingly\npenetration\npenetrative\npeneus\npengo\npenguin\npenicillin\npenicillium\npenile\npeninsula\npeninsular\npenis\npenitence\npenitent\npenitential\npenitentiary\npenitently\npenknife\npenlight\npenman\npenmanship\npenmen\npennant\npennate\npennatula\npennatulidae\npenni\npenniless\npennisetum\npennon\npennoncel\npennsylvania\npennsylvanian\npenny\npenny-wise\npennya\npennyante\npennycress\npennyroyal\npennyweight\npennywhistle\npennyworth\npenobscot\npenocha\npenologist\npenology\npenpusher\npense\npensee\npenseroso\npensieri\npensiero\npensile\npension\npensionable\npensionary\npensioner\npensive\npensively\npensiveness\npenstemon\npenstock\npent\npent-up\npentacle\npentagon\npentahedron\npentail\npentamerous\npentameter\npentangular\npentastomida\npentasyllabic\npentateuch\npentathlete\npentathlon\npentatonic\npentavalent\npentecost\npentecostal\npentecostalism\npenthesilean\npenthouse\npentile\npentlandite\npentode\npentose\npentoxide\npentylenetetrazol\npenult\npenultimate\npenumbra\npenurious\npenuriously\npenuriousness\npenury\npenutian\npeon\npeonage\npeony\npeople\npeopled\npep\npepastic\npepercerit\npeperomia\npepper\npepper-and-salt\npeppercorn\npeppermint\npepperoni\npeppershaker\npeppery\npeppy\npepsi\npepsin\npeptic\npeptide\npeptization\npeptizing\npepto-bismal\npeptone\nper\nperadventure\nperagrate\nperambulate\nperambulating\nperambulation\nperambulator\nperamelidae\nperas\nperca\npercale\nperceivable\nperceive\nperceived\nperceiver\npercentage\npercept\nperceptibility\nperceptible\nperceptibly\nperception\nperceptions\nperceptive\nperceptively\nperceptiveness\nperceptivity\nperceptual\nperceptually\nperch\nperchance\nperched\nperches\nperching\nperchlorate\npercidae\nperciformes\npercina\npercipience\npercoidea\npercolate\npercolation\npercolator\npercophidae\npercursory\npercussion\npercussionist\npercussive\nperde\nperdere\nperdicidae\nperdidit\nperdition\nperdix\nperdre\nperdrix\nperdu\nperdurability\nperdy\npere\nperegrination\nperegrinator\nperegrine\nperemptorily\nperemptory\nperennial\nperennially\nperennity\nperennium\nperennius\npereption\npererration\npereskia\nperfect\nperfected\nperfectibility\nperfectible\nperfection\nperfectionism\nperfectionist\nperfections\nperfective\nperfectly\nperfectness\nperfervidum\nperfice\nperfidious\nperfidiously\nperfidiousness\nperfidy\nperflate\nperflation\nperfoliate\nperforate\nperforated\nperforation\nperforator\nperforce\nperform\nperformable\nperformance\nperformances\nperformer\nperfumatory\nperfume\nperfumed\nperfumer\nperfumery\nperfumes\nperfunctorily\nperfunctory\nperfusion\nperhaps\nperi\nperianth\nperiapsis\nperiapt\npericallis\npericardial\npericarditis\npericardium\npericarp\npericementoclasia\npericlase\npericranium\npericulo\npericulous\npericulum\nperidinian\nperidiniidae\nperidinium\nperidition\nperidium\nperidot\nperigee\nperigon\nperihelion\nperijove\nperil\nperilepsis\nperilla\nperilous\nperilously\nperimeter\nperinatal\nperineal\nperineum\nperiod\nperiodic\nperiodical\nperiodically\nperiodicity\nperiodontic\nperiodontics\nperiodontist\nperiods\nperiophthalmus\nperiosteum\nperipatetic\nperipatidae\nperipatopsidae\nperipatopsis\nperipheral\nperipherally\nperiphery\nperiphrase\nperiphrasis\nperiphrastic\nperiplaneta\nperiploca\nperiplus\nperipteral\nperiscope\nperiscopic\nperiscopism\nperiselene\nperish\nperishable\nperishables\nperished\nperisher\nperishing\nperisoreus\nperissodactyla\nperissology\nperistalsis\nperistaltic\nperistediinae\nperistedion\nperistome\nperistylar\nperistyle\nperit\nperithecium\nperitoneal\nperitoneum\nperitonitis\nperiwig\nperiwinkle\nperjure\nperjured\nperjurer\nperjury\nperk\nperked\nperkily\nperlustration\nperm\npermafrost\npermalloy\npermanence\npermanent\npermanently\npermanganate\npermeability\npermeable\npermeant\npermeate\npermeated\npermeation\npermed\npermian\npermic\npermissibility\npermissible\npermissibly\npermissin\npermission\npermissive\npermissively\npermissiveness\npermit\npermitted\npermitting\npermutability\npermutation\npermute\npernicious\nperniciousness\npernicity\npernickety\npernis\npernod\nperodicticus\nperognathus\nperomyscus\nperoneal\nperonospora\nperonosporaceae\nperonosporales\nperorate\nperoration\nperoxide\nperpend\nperpendicular\nperpendicularity\nperpendicularly\nperpension\nperpetrate\nperpetration\nperpetrator\nperpetua\nperpetual\nperpetually\nperpetuate\nperpetuation\nperpetuity\nperpetuum\nperplex\nperplexed\nperplexedly\nperplexing\nperplexity\nperquisite\nperquisition\nperron\nperry\nperscrutation\npersea\npersecute\npersecution\npersephone\npersepolis\nperserverance\nperseus\nperseverance\npersevere\npersevering\nperseveringly\npersia\npersian\npersians\npersides\npersiflage\npersifleur\npersimmon\npersist\npersistence\npersistent\npersistently\npersisting\npersius\nperson\nperson-to-person\npersona\npersonable\npersonableness\npersonae\npersonage\npersonal\npersonalities\npersonality\npersonalized\npersonally\npersonalty\npersonate\npersonation\npersonhood\npersonification\npersonify\npersonnel\npersons\npersoonia\nperspective\nperspicacious\nperspicacity\nperspicacy\nperspicuity\nperspicuous\nperspicuousness\nperspiration\nperspire\nperspiring\nperstringe\npersuadable\npersuade\npersuaded\npersuader\npersuasibility\npersuasible\npersuasibleness\npersuasion\npersuasive\npersuasively\npersuasiveness\npersuasory\npert\npertain\npertainym\nperte\nperth\npertinacious\npertinaciously\npertinaciousness\npertinacity\npertinacy\npertinax\npertinence\npertinencey\npertinent\npertinently\npertingent\npertubation\npertunctory\nperturb\nperturbation\npertusaria\npertusariaceae\npertusion\npertussis\nperu\nperuke\nperuked\nperusal\nperuse\nperuvian\npervade\npervading\npervaporation\npervasion\npervasively\npervasiveness\nperverse\nperversely\nperversion\nperversity\npervert\nperverted\npervestigation\npervicacious\npervicacity\npervicacy\npervigilium\npervious\npeseta\npesewa\npesky\npessimal\npessimism\npessimist\npessimistic\npessimistically\npessimum\npessomancy\npessoribus\npest\npester\npestering\npesthole\npesthouse\npesticide\npestiferous\npestilence\npestilent\npestilential\npestle\npet\npetal\npetalous\npetard\npetasites\npetaurista\npetauristidae\npetaurus\npetcock\npetentibus\npeter\npetersburg\npeterto\npetfood\npetimusque\npetiole\npetit\npetite\npetitio\npetition\npetitionary\npetitioner\npetitions\npetrarch\npetrel\npetrifaction\npetrification\npetrified\npetrify\npetrifying\npetrochemical\npetrocoptis\npetrogale\npetrolatum\npetroleum\npetroleuse\npetrology\npetromyzon\npetromyzoniformes\npetromyzontidae\npetronel\npetronius\npetroselinum\npetrous\npetteria\npetticoat\npetticoated\npettifogger\npettifogging\npettily\npettiness\npettish\npetto\npetty\npetulance\npetulant\npetunia\npeu\npeur\npeurfortes\npeut\npew\npewee\npewter\npeziza\npezizaceae\npezizales\npezophaps\npf\npfannkuchen\npfennig\npg\nph\nphacochoerus\nphaedrus\nphaeophyceae\nphaeophyta\nphaethon\nphaethontidae\nphaeton\nphage\nphagocyte\nphagocytic\nphagun\nphaius\nphalacrocoracidae\nphalacrocorax\nphalaenopsis\nphalaenoptilus\nphalanger\nphalangeridae\nphalangida\nphalangiidae\nphalangitis\nphalangium\nphalansterianism\nphalanx\nphalaris\nphalarope\nphalaropidae\nphalaropus\nphallaceae\nphallales\nphallic\nphallus\nphalsa\nphanerogamae\nphaneromania\nphantasm\nphantasma\nphantasmagoria\nphantasmagoric\nphantasy\nphantom\npharaoh\npharaonic\npharisaical\npharisaism\npharisee\npharmaceutical\npharmaceutics\npharmaceutist\npharmacist\npharmacological\npharmacologically\npharmacologist\npharmacology\npharmacon\npharmacopoeia\npharmacopolist\npharmacy\npharomacrus\npharos\npharsalus\npharyngeal\nphascogale\nphascolarctos\nphase\nphaseolus\nphases\nphasianid\nphasianidae\nphasianus\nphasis\nphasma\nphasmid\nphasmida\nphasmidae\nphasmidia\nphd\npheasant\npheasant's-eye\nphegopteris\nphelgm\nphellem\nphellodendron\nphenacomys\nphenix\nphenol\nphenomenal\nphenomenally\nphenomenon\nphenothiazine\nphenotype\nphenotypical\nphenylalanine\nphenylbutazone\nphew\nphi\nphial\nphidias\nphiladelphaceae\nphiladelphia\nphiladelphus\nphilaenus\nphilander\nphilanthropic\nphilanthropically\nphilanthropist\nphilanthropy\nphilatelic\nphilatelist\nphilately\nphilharmonic\nphilhellene\nphilhellenic\nphilibeg\nphilip\nphilippi\nphilippic\nphilippine\nphilippines\nphilister\nphilistine\nphillistine\nphillyrea\nphilodendron\nphilogy\nphilohela\nphilologer\nphilological\nphilologist\nphilology\nphilomachus\nphilomel\nphilophylla\nphilosopher\nphilosophers\nphilosophia\nphilosophic\nphilosophical\nphilosophically\nphilosophie\nphilosophizing\nphilosophy\nphilter\nphiz\nphlebitis\nphlebodium\nphlebotomus\nphlebotomy\nphlegm\nphlegmatic\nphlegmatically\nphlegmy\nphleum\nphloem\nphlogiston\nphlogopite\nphlomis\nphlox\npho\nphobia\nphobic\nphobophobia\nphoca\nphocaena\nphocidae\nphocine\nphocomelia\nphoebe\nphoebus\nphoenicia\nphoenician\nphoenicophorium\nphoenicopteridae\nphoeniculidae\nphoeniculus\nphoenicurus\nphoenix\npholadidae\npholas\npholidae\npholidota\npholiota\npholis\npholistoma\nphon\nphonanta\nphone\nphone-in\nphonebook\nphoneme\nphonemic\nphonetic\nphonetically\nphonetician\nphonetics\nphonetism\nphoney\nphonic\nphonics\nphonocamptic\nphonogram\nphonogramic\nphonograph\nphonography\nphonological\nphonologist\nphonology\nphonorganon\nphony\nphooey\nphoradendron\nphoronid\nphoronida\nphosgene\nphosphate\nphosphine\nphospholipid\nphosphoprotein\nphosphorescence\nphosphorescent\nphosphoric\nphosphorous\nphosphorus\nphot\nphotic\nphotinia\nphoto\nphotoblepharon\nphotocathode\nphotochemical\nphotochemistry\nphotoconductive\nphotoconductivity\nphotocopier\nphotocopy\nphotoelectric\nphotoelectrically\nphotoelectron\nphotoemission\nphotoemissive\nphotogenic\nphotograph\nphotographer\nphotographic\nphotographically\nphotography\nphotogravure\nphotojournalism\nphotolithograph\nphotolithography\nphotology\nphotomechanical\nphotometer\nphotometry\nphotomicrograph\nphotomontage\nphoton\nphotosensitivity\nphotosphere\nphotostat\nphotosynthesis\nphotosynthetic\nphototropism\nphotovoltaic\nphoxinus\nphr\nphragmipedium\nphragmites\nphragmocone\nphrasal\nphrase\nphrasemonger\nphraseology\nphrases\nphrasing\nphrenic\nphrenitis\nphrenoia\nphrenologist\nphrenology\nphrenotypics\nphrensy\nphrenzied\nphrlike\nphrthe\nphrygia\nphrygian\nphryne\nphrynosoma\nphthiozoics\nphthiriidae\nphthirius\nphthisic\nphthisis\nphthisozoics\nphthorimaea\nphycobilin\nphycocyanin\nphycoerythrin\nphycomycetes\nphylacteric\nphylactery\nphyle\nphyllidae\nphylliform\nphyllitis\nphyllium\nphyllo\nphyllocladaceae\nphyllocladus\nphyllode\nphyllodial\nphyllodoce\nphylloporus\nphyllorhynchus\nphylloscopus\nphyllostachys\nphyllostomidae\nphyllostomus\nphylloxera\nphylloxeridae\nphylogenetic\nphylogenetically\nphylogeny\nphylum\nphysa\nphysalia\nphysalis\nphysaria\nphyseter\nphyseteridae\nphysic\nphysical\nphysically\nphysician\nphysicism\nphysicist\nphysicochemical\nphysics\nphysidae\nphysiognomy\nphysiologic\nphysiological\nphysiologically\nphysiologist\nphysiology\nphysiotherapeutic\nphysique\nphysostegia\nphysostigma\nphysostigmine\nphytelephas\nphytivorous\nphytography\nphytohormone\nphytolacca\nphytolaccaceae\nphytology\nphytomastigina\nphytophagous\nphytophthora\nphytoplankton\nphytotomy\nphytozoaria\npi\npia\npiacere\npiaffe\npiaget\npiagetian\npianino\npianism\npianissimo\npianist\npianistic\npiano\npianoforte\npiaster\npiazza\npibroch\npica\npicacho\npicador\npicardie\npicaresco\npicaresque\npicariae\npicaroon\npicasso\npicayune\npiccalilli\npiccolo\npicea\npichi\npichiciago\npicidae\npiciformes\npick\npickaninny\npickax\npicked\npickeer\npickeerer\npickelhaube\npicker\npickerel\npickerelweed\npickeringia\npicket\npickethaube\npicketing\npicking\npickings\npickle\npickled\npickleherring\npickmeup\npicknicker\npickpocket\npickthank\npickup\npicnic\npicofarad\npicogram\npicoides\npicometer\npicornavirus\npicosecond\npicot\npicquet\npicrasma\npicris\npictograph\npictographic\npictorial\npictorially\npictra\npictura\npicture\npictures\npicturesque\npicturesquely\npicturesqueness\npicturing\npicul\npiculet\npicumnus\npicus\npiddle\npiddling\npiddock\npide\npidgin\npie\npiebald\npiece\npiecemeal\npieces\npiecework\npied\npied-a-terre\npiedmont\npieplant\npier\npierce\npierced\npiercer\npierching\npiercing\npiercingly\npierglass\npierian\npierid\npieridae\npierides\npieris\npiernas\npierre\npierrot\npiet\npieta\npiete\npietism\npietist\npietistic\npietistical\npiety\npiezoelectric\npiezoelectricity\npiezometer\npiffle\npig\npigeon\npigeon-breasted\npigeon-toed\npigeonhearted\npigeonhole\npigeonholes\npigfish\npiggery\npiggish\npiggishly\npiggyback\npigheaded\npiglet\npigment\npigmentation\npigments\npigmy\npignoration\npignut\npigskin\npigsticking\npigsty\npigtail\npiguid\npigweed\npigwidgeon\npika\npike\npikeblenny\npikeman\npikeperch\npikestaff\npikstaff\npilaf\npilaster\npilchard\npile\npilea\npiledriving\npileous\npiles\npileup\npilfer\npilferage\npilferer\npilgarlic\npilgrim\npilgrimage\npili\npiling\npill\npillage\npillager\npillar\npillared\npillarist\npillars\npillbox\npillion\npillory\npillow\npillowcase\npillowslip\npillwort\npilon\npilose\npilosella\npilosity\npilot\npilotage\npilotfish\npilothouse\npilotless\npilous\npilsener\npilsner\npilularia\npilus\npima\npimenta\npimento\npiments\npimiento\npimp\npimpernel\npimpinella\npimple\npin\npinaceae\npinacotheca\npinafore\npinata\npinball\npince-nez\npincenez\npincer\npincers\npinch\npinchbeck\npinche\npinched\npinches\npinchgut\npinching\npinckneya\npinctada\npincushion\npindar\npindaric\npine\npineal\npineapple\npinecone\npinery\npinesap\npinetum\npineus\npinfish\npinfold\nping\npingpong\npinguecula\npinguicula\npinguinus\npinhead\npinhole\npinicola\npining\npinion\npinioned\npink\npinkie\npinkroot\npinna\npinnace\npinnacle\npinnate\npinnately\npinnatifid\npinnatisect\npinned\npinner\npinning\npinnipedia\npinnotheres\npinnotheridae\npinochle\npinon\npinopsida\npinot\npinpoint\npinpoint(a)\npinprick\npins\npinscher\npinstriped\npint\npint-size\npintail\npintle\npinto\npinus\npinwheel\npinworm\npion\npioneer\npious\npiously\npip\npip-squeak\npipa\npipage\npipal\npipe\npipeclay\npiped\npipefish\npipefitting\npipeful\npipelaying\npipeline\npiper\npiperaceae\npiperales\npiperin\npiperocaine\npipes\npipette\npipewort\npipidae\npipile\npipilo\npiping\npipistrelle\npipistrellus\npipit\npipkin\npippin\npipra\npipridae\npipsissewa\npiptadenia\npipturus\npiquancy\npiquant\npiquante\npiquantly\npique\npiqueerer\npiqueria\npiquet\npiracy\npiranga\npiranha\npirate\npiratical\npiratically\npirogi\npirogue\npiroplasm\npirouette\npiroxicam\npis\npisa\npisanosaur\npiscatorial\npiscatory\npiscem\npisces\npisciculture\npiscidia\npiscina\npiscine\npiscivorous\npish\npisiform\npisonia\npiss\npissis\npissoir\npistachio\npistacia\npistareen\npiste\npistia\npistil\npistillate\npistol\npistoleer\npiston\npisum\npit\npit-a-pat\npita\npitahaya\npitapat\npitch\npitched\npitcher\npitchfork\npitchforks\npitching\npitchpipe\npitchstone\npitchy\npiteous\npiteously\npitfall\npith\npithead\npithecanthropus\npithecellobium\npithecia\npithiness\npithless\npithy\npitiable\npitied\npitiful\npitifully\npitiless\npitilessness\npitocin\npiton\npitprop\npitsaw\npitta\npittance\npitted\npitter-patter\npittidae\npitting\npittsburgh\npituitary\npituite\npituitous\npituophis\npity\npitying\npityingly\npitymys\npityrogramma\npitys\npiu\npivot\npivotal\npix\npixel\npixy\npizazz\npizza\npizzeria\npizzicato\npizzle\npj\npk\npl\nplacability\nplacable\nplacard\nplacate\nplacatingly\nplacation\nplace\nplace-kicker\nplacebit\nplacebo\nplaced\nplaceman\nplacement\nplacenta\nplacental\nplacentation\nplacer\nplaces\nplacet\nplacid\nplacidity\nplacidly\nplacit\nplacket\nplacoderm\nplacodermi\nplacoid\nplacuna\nplafond\nplafrey\nplage\nplagianthus\nplagiarism\nplagiarist\nplagiaristic\nplagiarization\nplagiarize\nplagiary\nplagihedral\nplagioclase\nplagioclastic\nplague\nplaguey\nplaguing\nplaguy\nplaice\nplaid\nplaidoyer\nplain\nplainclothesman\nplainer\nplainly\nplainness\nplainsman\nplainsong\nplainspoken\nplaint\nplaintful\nplaintiff\nplaintive\nplaintively\nplaintiveness\nplaisance\nplaisanterie\nplaisir\nplait\nplan\nplanar\nplanarian\nplanate\nplanation\nplanchet\nplanchette\nplanchment\nplane\nplanet\nplanetal\nplanetarium\nplanetary\nplanetesimal\nplanetoid\nplanets\nplanetstruck\nplangency\nplangent\nplank\nplank-bed\nplanking\nplankton\nplanktonic\nplanned\nplanner\nplanning\nplanococcus\nplanoconcave\nplanoconvex\nplanographic\nplanscheme\nplant\nplant-eating(a)\nplantae\nplantagenet\nplantaginaceae\nplantaginales\nplantago\nplantain\nplantal\nplantar\nplantation\nplanted\nplanter\nplantigrade\nplanting\nplants\nplap\nplaque\nplash\nplashy\nplasm\nplasma\nplasmablast\nplasmapheresis\nplasmation\nplasmature\nplasmic\nplasmin\nplasminogen\nplasmodiidae\nplasmodiophora\nplasmodiophoraceae\nplasmodium\nplassey\nplaster\nplasterboard\nplastered\nplasterer\nplastering\nplastic\nplastically\nplasticine\nplasticity\nplasticizer\nplastid\nplat\nplataea\nplatalea\nplataleidae\nplatanaceae\nplatanistidae\nplatanthera\nplatanus\nplate\nplateau\nplated\nplatelayer\nplatelet\nplaten\nplatform\nplatichthys\nplating\nplatinum\nplatitude\nplato\nplatonic\nplatonism\nplatonist\nplatonistic\nplatoon\nplatte\nplatter\nplaty\nplatycephalidae\nplatycerium\nplatyctenea\nplatyctenean\nplatyhelminthes\nplatylobium\nplatymiscium\nplatypoecilus\nplatypus\nplatyrrhine\nplatyrrhini\nplatystemon\nplaudit\nplaudite\nplausibility\nplausible\nplautus\nplay\nplaya\nplayable\nplayback\nplaybill\nplaybox\nplayboy\nplayed\nplayer\nplayfellow\nplayful\nplayfully\nplayfulness\nplaygoer\nplayground\nplayhouse\nplaying\nplaylet\nplaymate\nplayoff\nplaypen\nplays\nplayschool\nplaysome\nplaysuit\nplaything\nplaytime\nplaywright\nplaza\nplea\nplead\npleader\npleading\npleadings\npleas\npleasance\npleasant\npleasantly\npleasantness\npleasantry\nplease\npleased\npleaser\npleases\npleasing\npleasingly\npleasingness\npleasurable\npleasurableness\npleasure\npleasuregiving\npleasures\npleat\npleated\nplebe\nplebeian\nplebiscite\nplebiscitum\nplecoptera\nplecotus\nplectania\nplectognath\nplectognathi\nplectomycetes\nplectophera\nplectorrhiza\nplectranthus\nplectrophenax\nplectuntur\npledge\npledged\npledget\npledging\npleiades\npleione\npleiospilos\npleistocene\nplena\nplenarily\nplenary\nplenipotent\nplenipotentiary\nplenitude\nplenteous\nplentiful\nplenty\nplenum\nplenus\npleochroic\npleochroism\npleomorphic\npleomorphism\npleonasm\npleonastc\npleonastic\nplerophory\nplesianthropus\nplesiosaur\nplesiosauria\nplethodon\nplethodontidae\nplethora\nplethoric\npleura\npleural\npleurisy\npleurobrachia\npleurobrachiidae\npleurocarp\npleurocarpous\npleurodont\npleurodynia\npleuronectes\npleuronectidae\npleurosorus\npleurothallis\npleurotus\nplevna\nplexiglas\nplexor\nplexus\npliability\npliable\npliableness\npliancy\npliant\npliantly\nplicate\nplication\nplicatoperipatus\nplicature\npliers\nplight\nplighted\nplimsoll\nplinth\npliny\npliocene\nploce\nploceidae\nploceus\nplod\nplodder\nplodding\nploddingly\nplomb\nplonk\nplop\nplosion\nplosive\nplot\nplotter\nplough\nploughed\nplover\nplow\nplowboy\nplowed\nplowing\nplowman\nplowshare\nplowshares\nplowwright\nploy\npluck\nplucked\npluckily\nplucky\nplug\nplughole\nplugugly\nplum\nplum-yew\nplumage\nplumaged\nplumate\nplumb\nplumbable\nplumbaginaceae\nplumbaginaceous\nplumbaginales\nplumbago\nplumbed\nplumber\nplumbic\nplumbing\nplumcolored\nplumcot\nplume\nplumed\nplumelike\nplumeria\nplumes\nplumigerous\nplummet\nplummy\nplumose\nplumosity\nplump\nplumper\nplumping\nplumpness\nplumule\nplunder\nplunderage\nplunderer\nplundering\nplunge\nplunged\nplunger\nplunk\npluperfect\nplural\npluralism\npluralist\npluralistic\nplurality\nplurimae\nplus\nplush\nplutarch\npluteaceae\npluteus\npluto\nplutocracy\nplutocrat\nplutocratic\nplutonic\nplutonium\nplutus\npluvial\npluvialis\npluvianus\npluviometer\npluviose\nply\nplymouth\nplywood\npm\npness\npneumatic\npneumatically\npneumatics\npneumatograph\npneumatology\npneumatometer\npneumatoscopic\npneumatostatics\npneumococcus\npneumoconiosis\npneumocystosis\npneumogastric\npneumometer\npneumonectomy\npneumonia\npneumonic\npneumonitis\npo\npoa\npoach\npoacher\npoaching\npoachy\npobreza\npoca\npocahontas\npochard\npock\npocked\npocket\npocket-handkerchief\npocketbook\npocketcomb\npocketful\npocketknife\npockets\npoco\npococurante\npocos\npocula\npod\npodagra\npodagric\npodalyria\npodargidae\npodargus\npodaxaceae\npodesta\npodetium\npodiatrist\npodiatry\npodiceps\npodicipedidae\npodicipitiformes\npodilymbus\npodocarp\npodocarpaceae\npodocarpus\npodophyllum\npodzol\npoeciliidae\npoecilocapsus\npoecilogale\npoem\npoema\npoephila\npoesy\npoet\npoetae\npoetaster\npoetess\npoetic\npoetical\npoetically\npoetics\npoeticus\npoetize\npoetry\npoets\npogge\npogonia\npogonophora\npogostemon\npogrom\npoi\npoignance\npoignancy\npoignant\npoignards\npoikilothermic\npoinciana\npoinsettia\npoint\npoint-blank\npoint-of-sale\npointblank\npointed\npointedly\npointer\npointing\npointless\npointlessly\npoints\npointsman\npointy-toed\npoise\npoised\npoison\npoisoned\npoisoner\npoisoning\npoisonous\npoisonously\npoisons\npoitiers\npoitou-charentes\npoke\npoker\npokerdice\npokerish\npokeweed\npokey\npokomo\npolacca\npolack\npolacre\npolak\npoland\npolanisia\npolar\npolarimeter\npolaris\npolariscope\npolarity\npolarization\npolaroid\npolder\npole\npoleax\npolecat\npolemic\npolemical\npolemics\npolemoniaceae\npolemoniaceous\npolemoniales\npolemonium\npolemoscope\npoles\npolestar\npolianthes\npolice\npoliceman\npolicy\npolicy-making\npolicyholder\npolio\npoliomyelitis\npolioptila\npoliovirus\npolish\npolished\npolisson\npolistes\npolitburo\npolite\npolitely\npoliteness\npolitic\npolitical\npolitically\npolitician\npolitics\npolity\npolk\npolka\npoll\npollachius\npollack\npollard\npollen\npollination\npollinator\npolloi\npolls\npollster\npollucite\npollutant\npollute\npollution\npollux\npolo\npolo-neck\npolonaise\npolonium\npolony\npoltergeist\npoltroon\npoltroonery\npolyamide\npolyandrism\npolyandrist\npolyandrous\npolyandry\npolyangiaceae\npolyangium\npolyanthus\npolyborus\npolybotrya\npolybutylene\npolychaeta\npolychaete\npolychord\npolychromatic\npolychrome\npolycirrus\npolycrystalline\npolycythemia\npolydactylus\npolyelectrolyte\npolyergus\npolyester\npolyestrous\npolyethylene\npolyfoam\npolygala\npolygalaceae\npolygamist\npolygamous\npolygamy\npolygastric\npolyglot\npolygon\npolygonaceae\npolygonal\npolygonales\npolygonally\npolygonatum\npolygonia\npolygonum\npolygraphy\npolygynist\npolygynous\npolyhedral\npolyhedron\npolyhymnia\npolylogy\npolymastigina\npolymastigote\npolymer\npolymerase\npolymeric\npolymerization\npolymorph\npolymorphemic\npolymorphic\npolymorphism\npolymorphous\npolymyositis\npolymyxin\npolynemidae\npolynesia\npolynesian\npolyneuritis\npolynomial\npolyodon\npolyodontidae\npolyoma\npolyp\npolypedates\npolypedatidae\npolypeptide\npolypetalous\npolyphone\npolyphonic\npolyphonically\npolyphonism\npolyphonist\npolyphony\npolyphosphate\npolyplacophora\npolyploid\npolypodiaceae\npolypodium\npolypody\npolyporaceae\npolypore\npolyporus\npolyprion\npolypropylene\npolyptoton\npolypus\npolysaccharide\npolyscope\npolysemous\npolysemy\npolystichum\npolystyrene\npolysyllabic\npolysyllabically\npolysyllable\npolysyndeton\npolytheism\npolytheist\npolytheistic\npolytonal\npolytonality\npolyunsaturated\npolyurethane\npolyvalent\npolyvinyl-formaldehyde\npomacanthus\npomacentridae\npomacentrus\npomade\npomaded\npomaderris\npomatomidae\npomatomus\npome\npomegranate\npomelo\npomeranian\npomfret\npommel\npommy\npomo\npomolobus\npomology\npomoxis\npomp\npompadour\npompano\npompeii\npompey\npompom\npompon\npomposity\npompous\npompously\nponca\nponcho\nponcirus\npond\nponder\npondera\nponderable\nponderation\npondere\npondering\nponderosa\nponderous\nponderously\npondorosity\npondus\npondweed\npongamia\npongee\npongidae\npongo\nponiard\nponlard\npons\npontederia\npontederiaceae\npontem\npontiac\npontiff\npontifical\npontificals\npontificate\npontoon\npontus\npony\npony-trekking\nponycart\nponytail\npooch\npood\npoodle\npooecetes\npooh\npoohpooh\npoohpoohpooh\npool\npoolroom\npoon\npoonghie\npoop\npoor\npoorhouse\npoori\npoorly\npoorness\npoorwill\npop\npopcorn\npope\npopedom\npopery\npopeyed\npopgun\npopillia\npopin\npopinjay\npopishly\npoplar\npoplin\npopliteal\npopover\npoppet\npoppy\npoppycock\npopulace\npopular\npopularis\npopularism\npopularity\npopularization\npopularize\npopularized\npopularly\npopulated\npopulation\npopuli\npopulism\npopulous\npopulousness\npopulus\nporbeagle\nporcelain\nporcellio\nporcellionidae\nporch\nporcine\nporcupine\nporcupinefish\npore\nporelatical\nporgy\nporifera\nporism\npork\npork-barreling\nporkchop\nporker\nporkfish\nporkholt\nporkpie\npornographer\npornographic\npornography\nporonotus\nporose\nporosity\nporous\nporousness\nporphyra\nporphyria\nporphyrio\nporphyritic\nporphyrula\nporphyry\nporpoise\nporridge\nporringer\nport\nport-au-prince\nport-of-spain\nportability\nportable\nportage\nportal\nportative\nportcullis\nporte\nporte-cochere\nportemonnaie\nportend\nportent\nportentous\nportentously\nporter\nporterage\nporterhouse\nportfire\nportfolio\nporthole\nportibre\nportico\nportiere\nportion\nportland\nportly\nportmanteau\nportrait\nportraitist\nportraiture\nportray\nportrayal\nportreeve\nportugal\nportuguese\nportulaca\nportulacacaea\nportunidae\nportunus\nportwatcher\nporzana\nposada\npose\nposeidon\nposer\nposession\nposeur\nposeuse\nposh\nposited\nposition\npositionable\npositional\npositive\npositively\npositiveness\npositivism\npositivist\npositron\nposnet\nposology\nposse\nposseman\npossess\npossessed\npossessing\npossession\npossessions\npossessive\npossessively\npossessiveness\npossessor\nposset\npossibility\npossible\npossibly\npossidetis\npossum\npossumus\npost\npost-free\npost-haste\npostage\npostal\npostbox\npostboy\npostcard\npostdate\npostdiluvial\npostdiluvian\nposter\nposterior\nposteriority\nposteriors\nposterity\npostern\npostganglionic\nposthaste\nposthitis\nposthole\nposthorn\nposthumous\nposthumously\npostilion\npostimpressionist\npostliminious\npostmark\npostmaster\npostmenopausal\npostmeridian\npostmillennial\npostmistress\npostmodernism\npostmodernist\npostmortem\npostnatal\npostnate\npostnuptial\npostolet\npostoperative\npostoperatively\npostpaid\npostpartum\npostpone\npostponed\npostponement\npostposition\npostpositive\npostprandial\npostscript\npostulant\npostulate\npostulation\npostulatory\npostulatum\npostural\nposture\nposturemaster\nposturing\npostwar\nposy\npot\npot-au-feu\npotable\npotage\npotager\npotamogalidae\npotamogeton\npotamogetonaceae\npotamophis\npotash\npotassium\npotation\npotations\npotato\npotawatomi\npotbelly\npotboiler\npotbound\npotboy\npoteen\npotence\npotency\npotent\npotentate\npotential\npotentiality\npotentilla\npotentiometer\npotently\npoterium\npotest\npotestlat\npothead\npother\npotherb\npotholder\npothole\npotholer\npothook\npothooks\npothos\npothunter\npotion\npotior\npotluck\npotomac\npotoroinae\npotoroo\npotorous\npotos\npotosi\npotpie\npotpourri\npotsherd\npotshot\npottage\npotted\npotter\npottering\npotters\npottery\npottle\npotto\npotty\npotty-trained\npotu\npotuit\npotulent\npotvaliant\npotwalloper\npotwallopper\npou\npouch\npoudre\npoulette\npoultice\npoultry\npoultryman\npounce\npound\npound-foolish\npoundage\npoundal\npounder\npounding\npounds\npour\npourboire\npouring\npourparler\npours\npousse-cafe\npout\npouteria\npoutingly\npoverty\npovertystricken\npow\npowder\npowder-puff\npowdered\npowderiness\npowdering\npowderpuff\npowdery\npower\npower-assisted\npowered\npowerfor\npowerful\npowerfully\npowerhouse\npowerless\npowerlessly\npowerlessness\npowers\npowhatan\npowt\npowwow\npox\npoxvirus\nppepare\nppour\npr\npraam\npracticabiity\npracticability\npracticable\npracticableness\npracticably\npractical\npracticality\npractically\npractice\npracticed\npracticing\npractitioner\npraecognita\npraemium\npraenomen\npraesepi\npraeterea\npraetor\npraetorian\npraetorium\npraetorship\npraevalet\npragmatic\npragmatical\npragmatically\npragmatics\npragmatism\npragmatist\nprague\nprahu\npraia\nprairial\nprairie\npraise\npraised\npraises\npraiseworthiness\npraiseworthy\npraisworthiness\npraisworthy\nprajapati\npraline\nprame\nprance\nprancer\nprandial\nprank\npranked\nprankishness\npranks\nprankster\npraseodymium\nprate\npratincole\nprattle\nprattler\nprattling\npraunus\npravity\nprawn\npraxis\npraxiteles\npray\npraya\nprayer\nprayerful\nprayers\npraying\npre\npre-columbian\npre-eminently\npre-raphaelite\npreach\npreacher\npreaching\npreachment\npreadamite\npreakness\npreamble\npreanal\npreapprehension\nprearranged\nprearrangement\nprebend\nprebendary\nprebendaryship\nprecambrian\nprecarious\nprecariously\nprecariousness\nprecast\nprecatory\nprecaution\nprecautionary\nprecautions\nprece\nprecede\nprecedence\nprecedent\nprecedented\nprecedentedly\nprecedential\nprecedents\npreceding\npreceding(a)\nprecentor\nprecentorship\nprecept\npreceptor\npreceptorship\nprecession\nprechlorination\nprecieuse\nprecinct\nprecincts\npreciosity\nprecious\npreciously\nprecipice\nprecipitancy\nprecipitant\nprecipitate\nprecipitately\nprecipitating(a)\nprecipitation\nprecipitator\nprecipitin\nprecipitiousness\nprecipitous\nprecipitously\nprecis\nprecise\nprecisely\npreciseness\nprecisian\nprecisianism\nprecision\npreclude\npreclusion\npreclusive\nprecocial\nprecocious\nprecociously\nprecociousness\nprecocity\nprecognition\nprecolumbian\npreconceived\npreconception\npreconcert\npreconcertation\npreconcerted\nprecondition\npreconditioned\nprecooked\nprecooled\nprecursive\nprecursor\nprecursory\npredaceous\npredacious\npredal\npredation\npredator\npredatorial\npredatory\npredecessor\npredeliberation\npredesigned\npredestinate\npredestination\npredestine\npredetermination\npredetermine\npredetermined\npredial\npredicable\npredicament\npredicate\npredication\npredicative\npredicatively\npredicator\npredicatory\npredict\npredictability\npredictable\npredictably\npredicting\nprediction\npredictive\npredigested\npredilection\npredispose\npredisposed\npredisposition\nprednisone\npredominance\npredominancy\npredominant\npredominantly\npredominate\npredomination\npreeclampsia\npreeminence\npreeminent\npreeminently\npreemption\npreemptive\npreen\npreengage\npreengagement\npreestablish\npreexamine\npreexist\npreexistence\npreexistent\nprefab\nprefabrication\npreface\nprefatory\nprefect\nprefectural\nprefecture\nprefer\npreferable\npreferably\npreference\npreferential\npreferentially\npreferment\nprefiguration\nprefigure\nprefigurement\nprefix\nprefrontal\npreglacial\npregnable\npregnancy\npregnant\nprehensile\nprehension\nprehistoric\nprehistory\npreinstruct\nprejudge\nprejudgement\nprejudgment\nprejudicate\nprejudication\nprejudice\nprejudiced\nprejudicial\nprelacy\nprelate\nprelation\nprelector\nprelibation\npreliminaries\npreliminary\npreliterate\nprelude\npreludious\nprelusive\nprelusory\nprematur\npremature\nprematurely\nprematureness\nprematurity\npremedical\npremeditate\npremeditated\npremeditation\npremenopausal\npremices\npremier\npremier(a)\npremiere\npremiership\npremise\npremises\npremit\npremium\npremolar\npremonish\npremonishment\npremonition\npremonitory\npremonstatensian\npremonstration\npremunire\nprenanthes\nprenatal\nprendre\nprenotion\nprensation\nprentice\nprenticeship\nprenuptial\npreoccupancy\npreoccupation\npreoccupied\npreoption\npreordain\npreordination\nprep\nprepackaged\npreparation\npreparations\npreparative\npreparatory\nprepare\nprepared\npreparedness\npreparer\npreparing\nprepayment\nprepense\nprepollence\nprepon/gr\npreponderance\npreponderant\npreponderate\npreponderation\npreposition\nprepositional\nprepositionally\nprepossess\nprepossessed\nprepossessing\nprepossession\npreposterous\npreposterously\nprepotency\npreprandial\nprepubescent\nprepuce\nprepupal\npreraphaelite\npreraphaelitism\nprerecorded\nprerequire\nprerequisite\nprerogative\npresage\npresbyope\npresbyopia\npresbyopic\npresbyter\npresbyterian\npresbyterianism\npresbytery\npresbytes\npreschool\nprescience\nprescient\npresciently\nprescious\nprescribe\nprescribed\nprescript\nprescription\nprescription(a)\nprescriptive\npreseason\npresence\npresent\npresent(a)\npresentable\npresentably\npresentation\npresentational\npresentday\npresentiment\npresenting\npresently\npresentment\npresentness\npresents\npreservable\npreservation\npreservative\npreservatory\npreserve\npreserved\npreserver\npreserving\npreset\npreshow\npreside\npresidency\npresident\npresidential\npresidents\npresidentship\npresidium\npress\npressed\npressing\npressingly\npressure\npressure-cooker(a)\npressurized\nprester\nprestidigitation\nprestige\nprestigiation\nprestigiatory\nprestigious\nprestigitator\nprestissimo\npresto\nprestriction\npresumable\npresumably\npresume\npresumed(a)\npresumption\npresumptive\npresumptuous\npresumptuously\npresuppose\npresupposition\npresurmise\npretend\npretended\npretender\npretending\npretense\npretenses\npretension\npretensions\npretentious\npretentiously\npretentiousness\npreterist\npreterit\npreterition\npreterlapsed\npretermission\npretermit\npreternatuarally\npreternatural\npreterperfect\npreterpluperfect\npretext\npretio\npretiosa\npretium\npretoria\npretrial\nprettily\nprettiness\npretty\npretty-pretty\npretypify\npretzel\npreux\nprevail\nprevailing\nprevalence\nprevalent\nprevaricate\nprevarication\nprevenance\nprevenient\nprevent\npreventable\npreventative\nprevention\npreventive\nprevents\npreview\nprevious\nprevious(a)\nprevious(p)\npreviously\nprevision\nprewar\nprewarn\nprey\nprfer\npriacanthidae\npriacanthus\npriam\npriapic\npriapus\nprice\nprice-controlled\npriced\npriceless\nprices\npricing\nprick\npricket\npricking\nprickings\nprickle\nprickleback\nprickling\nprickly\npricks\npride\nprie-dieu\npriest\npriest-ridden\npriestcraft\npriestess\npriesthood\npriestly\npriestridden\nprietio\nprig\npriggish\npriggishly\npriggishness\npriggism\nprim\u0003\nprim\nprima\nprimacy\nprimaries\nprimarily\nprimary\nprimate\nprimates\nprimateship\nprimatology\nprime\nprime(a)\nprimed\nprimer\nprimeval\nprimigenous\nprimigravida\npriming\nprimitive\nprimitively\nprimitivism\nprimly\nprimness\nprimo\nprimogenesis\nprimogenial\nprimogeniture\nprimordial\nprimordinate\nprimordium\nprimp\nprimping\nprimrose\nprimrosecolored\nprimulaceae\nprimulales\nprimum\nprimus\nprince\nprince's-feather\nprince-of-wales'-heath\nprincedom\nprincely\nprinceps\nprincess\nprinceton\nprincewood\nprincipal\nprincipality\nprincipally\nprincipalship\nprincipe\nprincipia\nprincipii\nprincipiis\nprincipio\nprincipium\nprinciple\nprincipled\nprinia\nprink\nprint\nprintable\nprinted\nprinter\nprinters\nprinting\nprintless\nprintmaker\nprintmaking\nprintout\npriodontes\nprionace\nprionotus\nprior\npriores\nprioress\npriori\npriority\npriorship\npriory\npris\npriscians\nprism\nprismatic\nprismatoid\nprismoid\nprison\nprisoner\nprisonlike\nprisons\nprissy\npristidae\npristine\npristis\nprithee\nprittleprattle\npritzelago\nprius\nprivacy\nprivate\nprivateer\nprivateering\nprivately\nprivation\nprivative\nprivet\nprivilege\nprivileged\nprivily\nprivity\nprivy\nprivy(p)\nprize\nprizefight\nprizefighter\nprizeman\nprizer\npro\nproa\nprobabilism\nprobabilistic\nprobabilistically\nprobabilities\nprobability\nprobable\nprobably\nprobandi\nprobantur\nprobat\nprobate\nprobation\nprobationary\nprobationer\nprobative\nprobatory\nprobatum\nprobe\nprobitas\nprobity\nproblem\nproblematical\nproblematically\nproboscidea\nproboscidean\nproboscis\nprocacity\nprocaine\nprocavia\nprocaviidae\nprocedural\nprocedure\nproceed\nproceeding\nproceedings\nproceeds\nprocellaria\nprocellariidae\nprocellariiformes\nprocerity\nproces\nprocess\nprocess-server\nprocessed\nprocesses\nprocessing\nprocession\nprocessional\nprocessor\nprochronism\nprociphilus\nproclaim\nproclamation\nproclivity\nprocnias\nproconsul\nproconsular\nproconsulship\nprocrastinate\nprocrastination\nprocrastinator\nprocreant\nprocreate\nprocreation\nprocreative\nprocreator\nprocrustean\nprocrustes\nproctitis\nproctologist\nproctology\nproctor\nproctorship\nproctoscope\nprocumbent\nprocuration\nprocurationem\nprocurator\nprocure\nprocurement\nprocuress\nprocyon\nprocyonid\nprocyonidae\nprod\nprodest\nprodigal\nprodigality\nprodigally\nprodigence\nprodigious\nprodigiously\nprodigy\nprodition\nproditur\nprodrome\nprodromos\nprodromous\nprodromus\nproduce\nproduced\nproducer\nproducible\nproducing\nproduct\nproduction\nproductive\nproductively\nproductiveness\nproductivity\nproducts\nproem\nproemial\nproemium\nproestantior\nproeterea\nprof\nprofanation\nprofanatory\nprofane\nprofaned\nprofanely\nprofaneness\nprofanity\nprofanum\nprofess\nprofessed(a)\nprofessedly\nprofessing\nprofession\nprofessional\nprofessionalism\nprofessionally\nprofessor\nprofessorial\nprofessorially\nprofessorship\nproffer\nprofiadol\nproficiency\nproficient\nproficiently\nproficuous\nprofile\nprofit\nprofit-maximizing\nprofitable\nprofitableness\nprofiteer\nprofitless\nprofitlessly\nprofligacy\nprofligate\nprofligately\nprofluence\nprofluent\nprofondo\nprofound\nprofoundly\nprofundis\nprofundity\nprofuse\nprofuseness\nprofusion\nprofusus\nprog\nprogenerate\nprogeneration\nprogenitor\nprogeny\nprogess\nprogestational\nprogesterone\nprogestin\nprogmatic\nprognathous\nprogne\nprognosis\nprognostic\nprognosticate\nprognostication\nprogram\nprogramma\nprogramme\nprogrammer\nprogramming\nprogress\nprogression\nprogressive\nprogressiveness\nprogressivism\nprogymnosperm\nproh\nprohibit\nprohibited\nprohibition\nprohibitionist\nprohibitive\nprohibitively\nprohibitory\nprohibitum\nproject\nprojected\nprojectile\nprojectiles\nprojecting\nprojection\nprojectionist\nprojector\nprokaryote\nprokaryotic\nprolamine\nprolapse\nprolate\nprolation\nprole\nprolection\nprolegomena\nprolepsis\nproletaire\nproletarian\nproletariat\nproletary\nproliferation\nprolific\nproline\nprolix\nprolixity\nprolocutor\nprolog\nprologue\nprolong\nprolongation\nprolonged\nprolusion\nprolusory\nprom\npromenade\npromethean\nprometheus\npromethium\nprominence\nprominent\nprominently\npromiscuity\npromiscuous\npromiscuously\npromise\npromised\npromisee\npromiser\npromising\npromisingly\npromissary\npromissory\npromontory\npromote\npromoter\npromotion\npromotional\npromotive\nprompt\npromptbook\nprompter\nprompting\npromptitude\npromptly\npromptness\npromptuary\npromulgate\npromulgated\npromulgation\npromulgator\npromulgatory\npronation\nprone\nproneness\nproner\nproneur\nprong\npronged\npronghorn\nprongs\npronominal\npronoun\npronounce\npronounceable\npronounced\npronouncement\npronouns\npronunciation\npronunciative\npronunziamento\nproof\nproof(p)\nproofed\nproofreader\nprop\npropaedeutic\npropaedeutical\npropaedeutics\npropagable\npropaganda\npropagandism\npropagandist\npropagate\npropagation\npropagator\npropanal\npropane\npropanol\npropel\npropellant\npropelled\npropeller\npropelling\npropelment\npropenal\npropend\npropendency\npropenoate\npropenonitrile\npropense\npropenseness\npropension\npropensity\nproper\nproper(a)\nproper(ip)\nproperly\npropertied\nproperties\npropertius\nproperty\npropertyless\npropets\nprophase\nprophasis\nprophecy\nprophesy\nprophet\nprophetess\nprophetfigs\nprophetic\nprophetically\nprophets\nprophylactic\nprophylaxis\npropinquity\npropitiate\npropitiation\npropitiative\npropitiator\npropitious\npropjet\nproplasm\nproportion\nproportionable\nproportional\nproportionate\nproportionately\nproportioned\nproportions\npropos\nproposal\npropose\nproposed\nproposer\nproposing\npropositi\nproposition\npropositus\npropound\npropoxyphene\npropranolol\npropre\nproprec\npropria\nproprietary\nproprietor\nproprietorship\nproprietress\npropriety\nproprio\nproprioception\nproprioceptive\nproprionamide\npropter\npropugn\npropugnation\npropugner\npropulsion\npropulsive\npropyl\npropylactic\npropylaeum\npropylene\npropylon\npropylthiouracil\nproquo\nproration\nprore\nprorogation\nprorogue\nproruption\npros\nprosaic\nprosaically\nprosaicism\nprosaism\nprosaist\nprosalprosy\nprosauropoda\nproscenium\nprosciuto\nproscribe\nproscription\nproscriptive\nprose\nprosecute\nprosecution\nprosecutor\nproselyte\nproselytism\nprosepct\nprosequi\nproser\nproserpina\nprosily\nprosimian\nprosimii\nprosiness\nprosing\nprosit\nprosodic\nprosody\nprosopis\nprosopium\nprosopopoeia\nprospect\nprospection\nprospective\nprospective(a)\nprospectively\nprospector\nprospectus\nprosper\nprosperita\nprosperity\nprosperous\nprosperously\nprospicience\nprossecute\nprostaglandin\nprostate\nprostatectomy\nprostatitis\nprosternation\nprosthesis\nprostitute\nprostitution\nprostrate\nprostration\nprostyle\nprosy\nprosyllogism\nprotactinium\nprotagonist\nprotamine\nprotanopia\nprotanopic\nprotasis\nprotea\nproteaceae\nproteales\nprotean\nprotease\nprotect\nprotected\nprotecting\nprotecting(a)\nprotection\nprotectionism\nprotectionist\nprotective\nprotective(p)\nprotectively\nprotectiveness\nprotector\nprotectorate\nprotectorship\nproteg\u0002\nprotege\nprotegee\nproteidae\nproteiform\nprotein\nproteinaceous\nproteles\nproteolysis\nproteolytic\nproterochampsa\nproteron/gr\nproterozoic\nprotervity\nprotest\nprotestant\nprotestantism\nprotestation\nprotested\nprotesting(a)\nprotestingly\nproteus\nprotg\u0002\nprothesis\nprothonotary\nprothorax\nprothrombin\nprotist\nprotista\nprotium\nproto(a)\nproto-norse\nproto-oncogene\nprotoarcheology\nprotoavis\nprotoceratops\nprotocol\nprotoctist\nprotoctista\nprotogenal\nprotogeometric\nprotohippus\nprotohistoric\nprotohistory\nproton\nprotoplasm\nprotoplast\nprototheria\nprototherian\nprototype\nprotoxide\nprotozoa\nprotozoal\nprotozoan\nprotozoological\nprotozoologist\nprotozoology\nprotract\nprotracted\nprotractile\nprotraction\nprotractor\nprotreptical\nprotrude\nprotrusile\nprotrusion\nprotrusive\nprottagonist\nprotuberance\nprotuberant\nprotura\nproturan\nproturberance\nproud\nproudcrested\nprouder\nproudly\nproust\nproustian\nprove\nproved\nproven\nprovencal\nprovence\nprovender\nproverb\nproverbial\nproverbially\nproverbs\nprovide\nprovided\nprovidence\nprovident\nprovidential\nprovidentially\nprovidently\nprovider\nproviding\nprovince\nprovincial\nprovincialism\nprovincially\nprovision\nprovisional\nprovisionally\nprovisions\nproviso\nprovisory\nprovocateur\nprovocation\nprovocative\nprovocatively\nprovoke\nprovoking\nprovoquant\nprovost\nprow\nprowess\nprowl\nprowler\nproxemics\nproxima\nproximal\nproximate\nproximity\nproximo\nproximus\nproxy\nprude\nprudence\nprudent\nprudential\nprudently\nprudery\nprudish\nprudishly\npruina\npruinose\nprumnopitys\nprune\npruned\nprunella\nprunellidae\nprunello\npruner\npruning\nprunus\nprurience\npruriency\nprurient\npruriently\npruritus\nprussia\nprussian\nprussic\nprvenance\npry\nprying\npryingly\nprytaneum\nps\npsalm\npsalmist\npsalmody\npsalter\npsalterium\npsaltery\npsaltriparus\npsammoma\npsammous\npsenes\npsephologist\npsephology\npsephomancy\npsephurus\npsetta\npsettichthys\npseudacris\npseudaletia\npseudechis\npseudemys\npseudo\npseudobombax\npseudococcidae\npseudococcus\npseudocolus\npseudoephedrine\npseudohallucination\npseudohermaphroditic\npseudolarix\npseudology\npseudomonad\npseudomonadales\npseudomonas\npseudomonodaceae\npseudonym\npseudonymous\npseudonymy\npseudophloem\npseudopleuronectes\npseudopod\npseudorevelation\npseudoryx\npseudoscience\npseudoscientific\npseudoscope\npseudotaxus\npseudotsuga\npseudowintera\npshaw\npsi\npsidium\npsilocybin\npsilomelane\npsilophytaceae\npsilophytales\npsilophyte\npsilophyton\npsilopsida\npsilotaceae\npsilotales\npsilotum\npsithyrus\npsittacidae\npsittaciformes\npsittacosaur\npsittacosis\npsittacula\npsittacus\npsocid\npsocidae\npsocoptera\npsophia\npsophiidae\npsophocarpus\npsora\npsoralea\npsoriasis\npsych\npsyche\npsychedelia\npsychedelic\npsychiatric\npsychiatrist\npsychiatry\npsychic\npsychical\npsychically\npsychics\npsychoactive\npsychoanalysis\npsychoanalytical\npsychobabble\npsychodid\npsychodidae\npsychogenic\npsychokinetic\npsycholinguistic\npsycholinguistics\npsychological\npsychologically\npsychologist\npsychology\npsychomancy\npsychometric\npsychometry\npsychopathic\npsychopharmacological\npsychopharmacology\npsychophysics\npsychopomp\npsychopsis\npsychosexual\npsychosexuality\npsychosis\npsychosomatic\npsychotherapeutic\npsychotherapist\npsychotherapy\npsychotic\npsychotria\npsychrometer\npsyllidae\nptah\nptarmigan\npteridaceae\npteridium\npteridophyta\npteridophyte\npteridospermae\npteridospermopsida\npteriidae\npteris\npternohyla\npterocarpus\npterocarya\npterocles\npteroclididae\npterocnemia\npterodactyl\npterodactylidae\npterodactylus\npterois\npteropogon\npteropsida\npteropus\npterosaur\npterosauria\npterospermum\npterostylis\npterygium\nptilocercus\nptilocrinus\nptilonorhynchidae\nptilonorhynchus\nptisan\nptloris\nptolemaic\nptolemy\nptomaine\nptosis\nptyalin\nptyalism\nptyas\nptychozoon\npu\npub\npuberty\npubes\npubescence\npubescent\npubic\npubis\npublic\npublic-spirited\npublican\npublication\npublicist\npublicity\npublicized\npublicly\npublico\npublish\npublishable\npublished\npublisher\npuccinia\npucciniaceae\npuccoon\npuce\npucelage\npuceron\npuck\npucker\npuckered\npuckish\npudder\npudding\npuddingface\npuddingwife\npuddle\npuddler\npudendal\npudendum\npudgy\npudicity\npudor\npueblo\npueraria\npueri\npuerile\npuerility\npuerisque\npuerperal\npuerperous\npuff\npuffball\npuffbird\npuffed\npuffer\npuffery\npuffin\npuffiness\npuffing\npuffingl\npuffinus\npuffy\npug\npug-nosed\npuggaree\npugh\npugilism\npugilist\npugilistic\npuglia\npugnacious\npugnaciously\npugnacity\npugnis\npugugly\npuisne\npuissance\npuissant\npujunan\npuka\npuke\npukka\npuku\npul\npula\npulasan\npulchritude\npulchritudinous\npulchro\npulcinella\npulcinello\npule\npulex\npulicaria\npulicidae\npull\npull-in\npull-through\npulled\npullet\npulley\npulling\npullman\npullover\npullulate\npullulation\npulmonary\npulmonata\npulmonic\npulp\npulpiness\npulpit\npulpwood\npulpy\npulque\npulsar\npulsate\npulsatilla\npulsating\npulsation\npulsatory\npulse\npulsed\npulsion\npultaceous\npulverizable\npulverization\npulverize\npulverized\npulverulence\npulverulent\npulvil\npuma\npumice\npummel\npump\npumped-up(a)\npumpkin\npumpkinseed\npun\npunce\npunch\npunch-drunk\npunch-up\npuncheon\npuncher\npunchinello\npunching\npunctated\npunctilio\npunctilious\npunctiliously\npunctiliousness\npuncto\npunctual\npunctuality\npunctually\npunctuate\npunctuation\npunctum\npuncturable\npuncture\npunctureless\npuncuality\npundit\npung\npungapung\npungency\npungent\npungently\npunic\npunica\npunicaceae\npunily\npuniness\npunish\npunishable\npunished\npunishing\npunishingly\npunishment\npunishmentpunition\npunishmnt\npunitive\npunitively\npunitory\npunitur\npunjab\npunjabi\npunk\npunkah\npunkie\npunnet\npunning\npunster\npunt\npunter\npuny\npup\npupa\npupal\npupil\npupilage\npupilarity\npupillari\npuppet\npuppeteer\npuppetry\npuppy\npuppyish\npuppyism\npur\npurace\npurana\npurblind\npurblindness\npurchasable\npurchase\npurchased\npurchaser\npurchasing\npurdah\npure\npurebred\npuree\npureeyed\npurely\npurgation\npurgative\npurgatorial\npurgatory\npurge\npurger\npurification\npurified\npurifier\npurify\npurifyc\npurifying\npurim\npurine\npuris\npurism\npurist\npuritan\npuritanical\npuritanism\npurity\npurl\npurlieus\npurling\npurloin\npurloined\npurloo\npurple\npurport\npurportedly\npurpose\npurpose-built\npurposed\npurposeful\npurposefully\npurposefulness\npurposeless\npurposelessly\npurposelessness\npurposely\npurposes\npurposive\npurpura\npurpure\npurr\npurse\npurse-proud\npurseproud\npurser\npurslane\npursuance\npursuant\npursuant(p)\npursue\npursued\npursuer\npursuing\npursuit\npursuivant\npursy\npurulence\npurulent\npurus\npurvey\npurveyance\npurveyor\npurview\npus\npusan\npuseyism\npuseyite\npush\npush-bike\npushan\npushball\npushed\npusher\npushing\npushover\npushup\npusillanimity\npusillanimous\npusillanimously\npuss\npussy\npussy-paw\npussycat\npustule\nput\nput-down\nputative\nputative(a)\nputdownable\nputid\nputo\nputout\nputrefaction\nputrefactive\nputrefied\nputrefy\nputrescence\nputrescent\nputrescine\nputrid\nputridity\nputt\nputtee\nputter\nputterer\nputting\nputty\nputtyroot\npuursuit\npuzzle\npuzzled\npuzzling\npvrk\npy\npya\npyaemia\npycnanthemum\npycnidium\npycnodysostosis\npycnogonida\npycnosis\npydna\npyelonephritis\npygmalion\npygmy\npygopodidae\npygopus\npygoscelis\npyinma\npyknotic\npylades\npylodictus\npylon\npyloric\npylorus\npyocyanase\npyocyanin\npyongyang\npyorrhea\npyracantha\npyralid\npyralidae\npyralis\npyramid\npyramidal\npyramids\npyrausta\npyre\npyrenees\npyrenomycetes\npyrethrum\npyretic\npyrex\npyridine\npyriform\npyrimidine\npyrite\npyrites\npyrocephalus\npyrolaceae\npyrolatry\npyrology\npyrolusite\npyromancy\npyromania\npyromaniac\npyrometer\npyrophobia\npyrophorus\npyrosis\npyrostat\npyrotechnic\npyrotechnics\npyrotechny\npyrotic\npyroxene\npyroxyline\npyrrhic\npyrrhocoridae\npyrrhonism\npyrrhonist\npyrrhula\npyrrhuloxia\npyrrhus\npyrrosia\npyrularia\npyrus\npythagoras\npythagorean\npythagorism\npythia\npythiaceae\npythian\npythias\npythium\npythius\npython\npythoness\npythonidae\npythoninae\npyx\npyxidanthera\npyxie\npyxis\nq\nqatar\nqatari\nqc\nqed\nqfever\nqiana\nqiang\nqindarka\nqoph\nqs\nqua\nquack\nquack(a)\nquack-quack\nquackerism\nquackery\nquackish\nquacksalver\nquad\nquadfifoil\nquadfiform\nquadrable\nquadragesima\nquadragesimal\nquadrangle\nquadrangular\nquadrant\nquadraphonic\nquadraphony\nquadrate\nquadratic\nquadratics\nquadrature\nquadrible\nquadric\nquadriceps\nquadricycle\nquadrifid\nquadrifoliate\nquadrifoliolate\nquadrigeminal\nquadrigeminate\nquadrilateral\nquadrille\nquadrillion\nquadripartite\nquadripartition\nquadriplanar\nquadriplegia\nquadriplegic\nquadrireme\nquadrisection\nquadriserial\nquadroon\nquadrumanous\nquadrumvirate\nquadruped\nquadrupedal\nquadruple\nquadruplet\nquadruplicate\nquadruplication\nquadrupling\nquae\nquaenocent\nquaeramus\nquaere\nquaestio\nquaff\nquagga\nquaggy\nquagmire\nquahaug\nquahog\nquail\nquaint\nquaintly\nquaintness\nquake\nquaker\nquakeress\nquakerish\nquakerism\nquaking\nqualification\nqualified\nqualify\nqualifying\nqualis\nqualitative\nqualitatively\nqualities\nquality\nqualm\nqualms\nquam\nquamdiu\nquamvis\nquand\nquandary\nquandiu\nquando\nquandong\nquantifiability\nquantifiable\nquantification\nquantifier\nquantify\nquantitate\nquantitative\nquantitatively\nquantities\nquantity\nquantization\nquantum\nquapaw\nquaquaversum\nquarantine\nquark\nquarrel\nquarrelling\nquarrelsome\nquarry\nquarrying\nquarryman\nquart\nquarter\nquarter(a)\nquarter-century\nquarter-hour\nquarterback\nquarterbacking\nquarterdeck\nquartered\nquarterevil\nquarterfinal\nquarterill\nquartering\nquarterlight\nquarterly\nquartermaster\nquartern\nquarteron\nquarters\nquarterstaff\nquartertone\nquartet\nquartett\nquartile\nquarto\nquartz\nquartzite\nquasar\nquasative\nquash\nquasi\nquasi(a)\nquasi-royal\nquassation\nquassia\nquatercentennial\nquaternal\nquaternary\nquaternate\nquaternion\nquaternity\nquaterque\nquatrain\nquatre\nquatrefoil\nquattrocento\nquaver\nquavering\nquaveringly\nquay\nqubibble\nqucksands\nque\nquean\nqueasily\nqueasiness\nqueasy\nquebec\nquechua\nqueen\nqueen-size\nqueencraft\nqueenly\nqueens\nqueensland\nqueer\nqueerly\nquell\nquelled\nquells\nquem\nquemque\nquench\nquenched\nquenchless\nquercitron\nquercus\nquerimonious\nquerist\nquern\nquerulous\nquerulousness\nquery\nquest\nquestion\nquestionable\nquestionably\nquestioning\nquestioningly\nquestionist\nquestionless\nquestionnaire\nquestions\nquestor\nquetzal\nquetzalcoatl\nqueue\nqui\nquia\nquib\nquibble\nquibbler\nquibbling\nquibusdam\nquiche\nquick\nquick-change(a)\nquick-sighted\nquick-witted\nquicken\nquickening\nquicker\nquickest\nquickfreeze\nquickly\nquickness\nquicksand\nquicksands\nquickscented\nquickset\nquickset(a)\nquicksilver\nquickstep\nquickwitted\nquid\nquidam\nquiddet\nquiddity\nquidem\nquidnunc\nquiescence\nquiescent\nquiet\nquieta\nquietem\nquietism\nquietist\nquietly\nquietness\nquietude\nquietus\nquiff\nquiietus\nquil\nquilck\nquill\nquiller\nquillet\nquills\nquillwort\nquilt\nquilted\nquilting\nquinacrine\nquinary\nquince\nquincentennial\nquincux\nquine\nquinine\nquinquagesima\nquinquarticular\nquinquefid\nquinquefoliate\nquinqueliteral\nquinquennium\nquinquepartite\nquinquesect\nquinquesection\nquinquina\nquinsy\nquint\nquintain\nquintal\nquinteron\nquintessence\nquintet\nquintilian\nquintillion\nquintroon\nquintuple\nquintuplet\nquintuplicate\nquintupling\nquinze\nquip\nquiproquo\nquips\nquipu\nquira\nquire\nquirites\nquirk\nquirt\nquis\nquiscalus\nquisque\nquisquis\nquit\nquit(p)\nquitclaim\nquite\nquito\nquits\nquittance\nquitter\nquiver\nquivering\nquixote\nquixotic\nquixotically\nquixotism\nquixotry\nquiz\nquizzical\nquizzing\nqum\nquntain\nquo\nquoad\nquod\nquodlibet\nquoerere\nquoi\nquoin\nquoit\nquoits\nquondam\nquoque\nquoratean\nquorum\nquos\nquot\nquota\nquotability\nquotable\nquotation\nquote\nquoth\nquotidian\nquotient\nquoties\nquovis\nqurush\nquun\nr\nra\nrabat\nrabato\nrabbet\nrabbi\nrabbin\nrabbinate\nrabbinical\nrabbinist\nrabbist\nrabbit\nrabbitfish\nrabbitweed\nrabbitwood\nrabble\nrabelais\nrabelaisian\nrabid\nrabies\nraccoon\nraccroc\nrace\nraceabout\nracecard\nraceculture\nracehorse\nraceme\nracemose\nracer\nracerunner\nracetrack\nraceway\nrachidian\nrachis\nrachitic\nrachitis\nrachycentridae\nrachycentron\nracial\nracially\nracily\nracine\nraciness\nracing\nracism\nracist\nrack\nrackabones\nracket\nracketeer\nracketeering\nracketing\nrackets\nrackety\nracking\nrackrent\nraconteur\nracquetball\nracy\nrad\nradar\nraddle\nraddled\nradial\nradially\nradian\nradiance\nradiant\nradiantly\nradiate\nradiating(a)\nradiation\nradiator\nradical\nradicalism\nradically\nradicchio\nradicle\nradiigera\nradio\nradio-controlled\nradio-phonograph\nradioactive\nradioactivity\nradiocarbon\nradiochemistry\nradiochlorine\nradiogram\nradiograph\nradiographer\nradiographic\nradiography\nradioisotope\nradiolaria\nradiolarian\nradiological\nradiologist\nradiology\nradiolucent\nradiometer\nradiomicrometer\nradiopaque\nradiopharmeceutical\nradioscopy\nradiosensitive\nradiotelegraph\nradiotelephone\nradiotelephonic\nradiotherapy\nradish\nradium\nradius\nradix\nradome\nradon\nradoter\nradoteur\nradyera\nraff\nraffia\nraffinose\nraffish\nraffle\nraft\nrafter\nraftered\nraftsman\nrag\nragamuffin\nragbag\nrage\nrageful\nrages\nragged\nraggedly\nraggedness\nraging\nraglan\nragout\nrags\nragtag\nragtime\nragweed\nragwort\nrah\nrahu\nraid\nraider\nrail\nrail-splitter\nrailhead\nrailing\nraillerie\nraillery\nrailroad\nrailway\nraiment\nrain\nrainbow\nrainbowcolored\nraincoat\nraindrop\nrainfall\nraining\nrainless\nrainmaking\nrainproof\nrains\nrainstorm\nrainy\nraisable\nraise\nraised\nraised(a)\nraisin\nraising\nraison\nraisonne\nraj\nraja\nrajab\nrajah\nrajidae\nrajiformes\nrajput\nrakaposhi\nrake\nrake-off\nrakehell\nraking\nrakish\nrakishly\nrakishness\nraleigh\nraleighl\nrallentando\nrallidae\nrally\nrallying\nram\nram's-head\nrama\nramachandra\nramadan\nramage\nramazan\nramble\nrambler\nrambling\nrambouillet\nrambutan\nramekin\nramie\nramification\nramify\nramjet\nramman\nrammer\nramose\nramous\nramp\nrampage\nrampageous\nrampant\nrampant(ip)\nrampantly\nrampart\nramphastidae\nramphomicron\nrampion\nramrod\nrams\nramshackle\nrana\nranales\nranatra\nranch\nrancher\nranching\nrancid\nrancidity\nrancor\nrancorous\nrand\nrandan\nrandom\nrandomization\nrandomize\nrandomized\nrandomly\nrandomness\nrandy\nrange\nrangefinder\nrangeland\nranger\nrangifer\nranging\nrangoon\nrangpur\nrangy\nrani\nranidae\nranitidine\nrank\nranker\nrankine\nranking\nranking(a)\nrankle\nrankling\nranks\nransack\nransacking\nransom\nransomed\nrant\nranter\nrantipole\nranunculaceae\nranunculus\nrao\nraoulia\nrap\nrapacious\nrapaciously\nrapaciousness\nrapacity\nrapateaceae\nrape\nraper\nrapeseed\nraphanus\nraphicerus\nraphidae\nraphidiidae\nraphus\nrapid\nrapidity\nrapids\nrapier\nrapine\nrapit\nrapparee\nrappee\nrappel\nrapping\nrapport\nrapports\nrapprochement\nrapscallion\nrapt\nraptores\nraptorial\nraptorials\nraptors\nrapture\nraptures\nrapturous\nrara\nrare\nraree\nraree-show\nrareeshow\nrarefaction\nrarefiable\nrarefied\nrarefy\nrarely\nrareness\nrari\nrarior\nrarity\nraro\nrasa\nrascal\nrascality\nrascallion\nrascally\nrase\nrash\nrasher\nrashling\nrashness\nraskolnikov\nrasorial\nrasp\nraspberry\nrasper\nrast\nrastafarian\nrastafarianism\nraster\nrasure\nrat\nrat-a-tat-tat\nrat-catcher\nratability\nratable\nratables\nratafia\nratan\nrataplan\nratatat\nratchet\nrate\nratel\nrath\nrather\nrathole\nrati\nratibida\nratification\nratified\nratify\nrating\nratio\nratiocination\nratiocinative\nration\nrational\nrationale\nrationalism\nrationalist\nrationalistic\nrationality\nrationalization\nrationally\nratione\nrationed\nrationi\nrationing\nrationis\nrations\nratitae\nratite\nratlike\nratline\nratlings\nrats\nratsbane\nrattan\nratten\nrattle\nrattlebrained\nrattlesnake\nrattletraps\nrattling\nrattrap\nrattus\nraucity\nraucous\nraucously\nrauwolfia\nravage\nravages\nravaging\nrave\nrave-up\nravehook\nravel\nraveled\nravelin\nraveling\nraven\nravening\nravenna\nravenous\nravenousness\nraver\nravigote\nravine\nraving\nravioli\nravish\nravishing\nravishingly\nravishment\nraw\nraw(a)\nrawboned\nrawhide\nrawness\nray\nrayon\nrays\nraze\nrazing\nrazor\nrazor-sharp\nrazorback\nrazorbill\nrazorblade\nrazorfish\nrazure\nrazz\nrazzia\nrazzle-dazzle\nrb\nrc\nrchauff\u0002\nrd\nre\nre-created\nre-creation\nre-echo\nre-entrant\nre-formed\nre-introduction\nreabsorb\nreach\nreaching\nreact\nreactance\nreactant\nreaction\nreactionary\nreactionism\nreactionist\nreactive\nreactivity\nreactor\nread\nreadability\nreadable\nreader\nreadership\nreadily\nreadiness\nreading\nreadjust\nreadjustment\nreadmission\nreadmit\nready\nready(a)\nready-made\nready-mix\nreaffiliation\nreaffirm\nreagan\nreagent\nreal\nreal(a)\nrealgar\nrealism\nrealist\nrealistic\nrealistically\nreality\nrealizable\nrealization\nrealize\nreallocation\nreallotment\nreally\nrealm\nrealms\nrealtor\nrealty\nream\nreamer\nreams\nreanimate\nreanimated\nreanimation\nreap\nreappear\nreappearance\nreappearing\nreappraisal\nrear\nrear(a)\nrearguard\nrearmament\nrearrangement\nrearward\nreason\nreasonable\nreasonableness\nreasonably\nreasoned\nreasoner\nreasoning\nreasoningless\nreasonless\nreasons\nreassemble\nreassembly\nreassert\nreassignment\nreassurance\nreassure\nreassured\nreassuring\nreassuringly\nreasty\nreaumur\nreave\nrebarbative\nrebate\nrebatement\nrebeck\nrebel\nrebel(a)\nrebellion\nrebellious\nrebelliously\nrebellow\nrebellowing\nrebirth\nreboant\nreboation\nrebound\nrebours\nrebuff\nrebuild\nrebuilding\nrebuilt\nrebuke\nrebukingly\nreburying\nrebus\nrebut\nrebuttal\nrebutter\nrecalcitrant\nrecalcitrate\nrecalcitration\nrecalesce\nrecalescence\nrecall\nrecant\nrecantation\nrecapitulate\nrecapitulation\nrecapper\nrecapture\nrecast\nrecce\nrecede\nreceding\nreceding(a)\nreceipt\nreceipts\nreceivable\nreceivables\nreceive\nreceived\nreceiver\nreceivership\nreceiving\nrecency\nrecension\nrecent\nrecentium\nrecently\nreceptacle\nreception\nreceptionist\nreceptive\nreceptively\nreceptiveness\nrecess\nrecessed\nrecesses\nrecession\nrecessional\nrecessive\nrechargeable\nrechauff\u0002\nrechauffe\nrecherche\nrecidivate\nrecidivation\nrecidivism\nrecidivist\nrecidivity\nrecidivous\nrecife\nreciminate\nrecipe\nrecipient\nreciprocal\nreciprocality\nreciprocally\nreciprocalness\nreciprocate\nreciprocating\nreciprocation\nreciprocative\nreciprocity\nrecision\nrecital\nrecitalist\nrecitation\nrecitative\nrecitativo\nrecite\nreck\nrecklessc\nrecklessly\nrecklessness\nreckon\nreckoning\nreclaim\nreclaimable\nreclaimed\nreclamation\nreclassification\nreclination\nrecline\nrecliner\nreclining\nreclivate\nrecluse\nreclusion\nreclusiveness\nrecoding\nrecognition\nrecognizable\nrecognizably\nrecognizance\nrecognize\nrecognized\nrecoil\nrecoiling\nrecoilless\nrecoils\nrecollect\nrecollection\nrecommence\nrecommend\nrecommendation\nrecommendatory\nrecommended\nrecompense\nreconcilable\nreconcile\nreconciled\nreconcilement\nreconciliation\nrecondite\nreconditeness\nreconditioned\nreconized\nreconnaissance\nreconnoiter\nreconnoitering\nreconsider\nreconsideration\nreconstitute\nreconstitution\nreconstruct\nreconstructed\nreconstruction\nreconstructive\nreconversion\nreconvert\nrecord\nrecord(a)\nrecord-breaker\nrecord-breaking\nrecorded\nrecorder\nrecording\nrecords\nrecount\nrecoup\nrecourse\nrecover\nrecoverable\nrecovered(p)\nrecovery\nrecreant\nrecreate\nrecreational\nrecreative\nrecrimination\nrecriminative\nrecriminatory\nrecrudescence\nrecruit\nrecruiter\nrecruiting\nrecruiting-sergeant\nrecruitment\nrecruits\nrectal\nrectangle\nrectangular\nrectangularity\nrecte\nrecti\nrectification\nrectified\nrectifier\nrectify\nrectilineal\nrectilinear\nrectilinearity\nrectitude\nrecto\nrector\nrectorship\nrectory\nrectrix\nrectum\nrectus\nrecubant\nreculade\nreculer\nreculons\nrecumbency\nrecumbent\nrecuperation\nrecuperative\nrecur\nrecure\nrecurrence\nrecurrent\nrecurrently\nrecurring\nrecursion\nrecursive\nrecurvation\nrecurve\nrecurved\nrecurvirostra\nrecurvirostridae\nrecurvity\nrecurvous\nrecusance\nrecusancy\nrecusant\nrecusation\nrecusent\nrecycling\nred\nred-coated\nred-hot\nred-rimmed\nredaction\nredactor\nredan\nredargue\nredargution\nredberry\nredbone\nredbud\nredcap\nredcoat\nredden\nreddened\nreddish\nreddition\nrededication\nredeem\nredeemable\nredeemableness\nredeemer\nredeeming(a)\nredefinition\nredemption\nredemptive\nredeployment\nredeposition\nredet\nredetermination\nredeye\nredfish\nredhanded\nredhead\nredheaded\nredhorse\nredhot\nrediffusion\nredintegrate\nredintegratioamoris\nredintegration\nrediscovery\nredistributed\nredistribution\nredition\nredivivus\nredletter\nredly\nredneck\nredness\nredolence\nredolent\nredolent(p)\nredonda\nredouble\nredoubled\nredoubt\nredoubtable\nredound\nredoundto\nredount\nredpoll\nredraft\nredress\nredshank\nredskin\nredstart\nredtail\nredtape\nredtapism\nredtapist\nreduce\nreduced\nreducer\nreducible\nreductio\nreduction\nreductionism\nreductionist\nreductive\nredundance\nredundancy\nredundant\nreduplicate\nreduplication\nreduviidae\nredux(ip)\nredwing\nredwood\nreecho\nreechy\nreed\nreedy\nreef\nreefs\nreefy\nreek\nreeking\nreekless\nreeky\nreel\nreelection\nreelprocitist\nreembody\nreenforce\nreenforcement\nreenforcements\nreenforeed\nreenter\nreentering\nreentry\nreerement\nreestablish\nreestablishment\nreestate\nreevaluation\nreeve\nrefashion\nrefect\nrefection\nrefectory\nrefer\nreferable\nreferee\nreference\nreferenced\nreferendary\nreferendum\nreferens\nreferent\nreferential\nreferible\nreferment\nrefero\nreferral\nreferrible\nrefief\nrefill\nrefilling\nrefine\nrefined\nrefinement\nrefiner\nrefinery\nrefining\nrefinisher\nrefit\nreflation\nreflect\nreflected\nreflecting\nreflection\nreflective\nreflectively\nreflector\nreflex\nreflexion\nreflexive\nreflexively\nreflexivity\nreflexly\nrefluence\nrefluent\nreflux\nrefocillate\nrefocillation\nrefocusing\nreforestation\nreform\nreformation\nreformative\nreformatory\nreformed\nreformer\nrefound\nrefraction\nrefractive\nrefractivity\nrefractometer\nrefractoriness\nrefractory\nrefrain\nrefraining\nrefresh\nrefreshed\nrefresher\nrefreshing\nrefreshingly\nrefreshment\nrefrigerant\nrefrigerate\nrefrigerated\nrefrigeration\nrefrigerator\nrefrigeratory\nreft\nrefuge\nrefugee\nrefulgence\nrefulgent\nrefund\nrefurbish\nrefusal\nrefuse\nrefused\nrefusing\nrefutable\nrefutation\nrefute\nrefuted\nregain\nregal\nregale\nregalecidae\nregalement\nregalia\nregality\nregally\nregard\nregardant(ip)\nregarder\nregardful\nregardless\nregards\nregatta\nregelate\nregem\nregency\nregeneracy\nregenerate\nregenerated\nregenerateness\nregenerating\nregeneration\nregent\nregent(ip)\nregentship\nreges\nreggae\nregia\nregibus\nregicide\nregiert\nregime\nregimen\nregiment\nregimental\nregimentally\nregimentals\nregimentation\nregimented\nregina\nregion\nregional\nregionally\nregions\nregis\nregister\nregistered\nregistrant\nregistrar\nregistrary\nregistration\nregistry\nreglaecus\nregle\nreglet\nregna\nregnant\nregnellidium\nregni\nregorge\nregosol\nregrade\nregrate\nregrater\nregress\nregression\nregressive\nregret\nregretful\nregretfully\nregrettable\nregretted\nregretting\nregrowth\nreguerdon\nregular\nregular(a)\nregularity\nregularized\nregularly\nregulars\nregulate\nregulated\nregulating\nregulation\nregulative\nregulator\nregulus\nregum\nregur\nregurgitate\nregurgitation\nrehabilitate\nrehabilitated\nrehabilitation\nrehabilitative\nreharmonization\nrehash\nrehearsal\nrehearse\nrei\nreich\nreichsrath\nreigh\nreign\nreimburse\nreimbursement\nreimposition\nrein\nreincarnate\nreincarnation\nreindeer\nreinforce\nreinforced\nreinforcement\nreinless\nreins\nreinstall\nreinstate\nreinstatement\nreinsurance\nreinterpretation\nreinvest\nreinvestment\nreinvigorate\nreipublicoe\nreis\nreise\nreissue\nreiterate\nreiteration\nreithrodontomys\nreject\nrejectaneous\nrejected\nrejection\nrejectious\nrejective\nrejoice\nrejoicing\nrejoin\nrejoinder\nrejuvenate\nrejuvenated\nrejuvenation\nrejuvenescence\nrekindle\nrelapse\nrelate\nrelated\nrelatedness\nrelates\nrelating\nrelation\nrelational\nrelations\nrelationship\nrelationships\nrelative\nrelatively\nrelativism\nrelativistic\nrelativistically\nrelativity\nrelator\nrelatum\nrelax\nrelaxant\nrelaxation\nrelaxed\nrelaxing\nrelay\nrelays\nrelease\nreleased\nreleasee\nrelegate\nrelegation\nrelent\nrelentless\nrelentlessly\nrelentlessness\nrelessee\nrelevance\nrelevancy\nrelevant\nrelevantly\nreleve\nreliability\nreliable\nreliance\nreliant\nrelic\nrelics\nrelict\nrelief\nrelieve\nrelieved\nreliever\nrelieving\nrelievo\nreligieuse\nreligio\nreligion\nreligionism\nreligionist\nreligiosity\nreligious\nreligiously\nreligiousness\nrelinqishing\nrelinquish\nrelinquished\nrelinquishment\nreliquary\nreliquiae\nreliquit\nrelish\nrelistening\nreliving\nrelocated\nreluce\nrelucent\nreluct\nreluctance\nreluctant\nreluctantly\nreluctate\nreluctation\nreluctivity\nrelume\nrely\nrem\nremain\nremainder\nremainderman\nremaining\nremains\nremake\nremand\nremanet\nremark\nremarkable\nremarkably\nremarriage\nrembrandt\nrembrandtesque\nremedes\nremediable\nremedial\nremedies\nremediless\nremedio\nremedy\nremember\nremembered\nremembering\nremembrance\nremembrancer\nremembrances\nrememoration\nremiform\nremigration\nremilegia\nremilitarization\nremind\nreminder\nreminds\nreminiscence\nreminiscential\nreminiscently\nremis\nremise\nremiss\nremission\nremissness\nremit\nremittance\nremittent\nremitter\nremnant\nremodel\nremollient\nremonetize\nremonstrance\nremonstrate\nremora\nremorse\nremorseless\nremote\nremotely\nremoteness\nremotest\nremotion\nremount\nremovable\nremoval\nremove\nremoved\nremoved(p)\nremovedness\nremover\nremuda\nremugient\nremunerate\nremuneration\nremunerative\nremuneratory\nremus\nrenaissance\nrenascent\nrencontre\nrencounter\nrend\nrender\nrendering\nrenderrough\nrendezvous\nrending\nrendition\nrendu\nrenegade\nrenew\nrenewable\nrenewal\nrenewed\nrenewing\nreniform\nrenitence\nrenitency\nrenitent\nrennet\nrennin\nreno\nrenounce\nrenovare\nrenovate\nrenovated\nrenovation\nrenown\nrenowned\nrenownless\nrensselaerite\nrent\nrent-free\nrent-rebate\nrent-roll\nrentable\nrentage\nrental\nrenter\nrentfree\nrentier\nrents\nrenunciant\nrenunciation\nreordering\nreorganization\nreorganize\nreorganized\nreorientation\nreovirus\nrep\nrepair\nrepairman\nrepand\nreparable\nreparation\nreparative\nreparatory\nrepartee\nreparteeist\nrepartition\nrepass\nrepast\nrepatriate\nrepatriation\nrepay\nrepayable\nrepayment\nrepeal\nrepeat\nrepeatable\nrepeated\nrepeatedly\nrepeater\nrepel\nrepellant\nrepellent\nrepellently\nrepelling\nrepent\nrepentance\nrepentant\nrepente\nrepenting\nrepercuss\nrepercussion\nrepercussive\nrepertoire\nrepertorium\nrepertory\nrepetend\nrepetita\nrepetition\nrepetitional\nrepetitionary\nrepetitively\nrepetitiveness\nrepicolous\nrepine\nrepining\nrepitition\nreplace\nreplaceability\nreplaceable\nreplacement\nreplay\nreplenish\nreplete\nrepletion\nreplevin\nreplevy\nreplica\nreplication\nreply\nreply-paid\nrepondre\nreport\nreportable\nreported\nreportedly\nreporter\nreports\nrepose\nreposing\nreposit\nrepositing\nreposition\nrepositioning\nrepository\nrepossession\nrepostum\nrepousse\nreprehend\nreprehensibility\nreprehensible\nreprehensibly\nreprehension\nrepresent\nrepresentable\nrepresentation\nrepresentational\nrepresentative\nrepresentatives\nrepresented\nrepresenting\nrepresentment\nrepress\nrepression\nrepressionist\nreprieval\nreprieve\nreprimand\nreprint\nreprisal\nreprise\nreproach\nreproaches\nreproachful\nreproachfully\nreprobate\nreprobation\nreproche\nreproduce\nreproduced\nreproducer\nreproducibility\nreproducible\nreproducibly\nreproduction\nreproductive\nreproof\nreprove\nreprover\nreprovingly\nreptantia\nreptatorial\nreptile\nreptilia\nreptilian\nrepublic\nrepublican\nrepublicanism\nrepublicans\nrepublication\nrepudiate\nrepudiation\nrepudiative\nrepugn\nrepugnance\nrepugnant\nrepugnanti\nrepulse\nrepulsion\nrepulsive\nrepulsive(a)\nrepurchase\nreputable\nreputableness\nreputably\nreputation\nrepute\nreputedly\nrequest\nrequested\nrequesting\nrequiem\nrequies\nrequiescat\nrequire\nrequired\nrequirement\nrequiring\nrequisit\nrequisite\nrequisiteness\nrequisition\nrequisitive\nrequisitory\nrequital\nrequite\nrequited\nrerebrace\nreredos\nrerum\nrerun\nres\nresale\nrescind\nrescindable\nrescission\nrescript\nrescription\nrescuable\nrescue\nrescued\nrescuer\nresearch\nresearchable\nreseat\nreseau\nresection\nreseda\nresedaceae\nresemblance\nresemble\nresembling\nresent\nresentful\nresentfully\nresentive\nresentment\nreserpine\nreservation\nreservatory\nreserve\nreserve(a)\nreserved\nreservedly\nreserves\nreservist\nreservoir\nreset\nresettlement\nresh\nreshipment\nreshuffle\nresiance\nresiant\nreside\nresidence\nresidences\nresidency\nresident\nresidential\nresidentially\nresidentiary\nresidual\nresiduary\nresidue\nresiduum\nresign\nresignation\nresigned\nresigned(p)\nresignedly\nresilience\nresilient\nresin\nresinated\nresinoid\nresinous\nresiny\nresipiscence\nresist\nresistance\nresistant\nresistible\nresisting\nresistive\nresistless\nresistor\nresolute\nresolutely\nresoluteness\nresolution\nresolvable\nresolve\nresolved\nresolvent\nresolvit\nresonance\nresonant\nresonate\nresonator\nresorb\nresorcinol\nresort\nresorts\nresound\nresounding\nresoundingly\nresource\nresourceful\nresourcefully\nresourcefulness\nresourceless\nresources\nrespect\nrespectability\nrespectable\nrespectableness\nrespectably\nrespected\nrespecter\nrespectful\nrespectfully\nrespecting\nrespective\nrespective(a)\nrespectively\nrespectless\nrespects\nresperse\nrespersion\nrespice\nrespicere\nrespiration\nrespirator\nrespiratory\nrespire\nrespirer\nrespiro\nrespite\nresplendence\nresplendent\nrespond\nrespondent\nresponse\nresponsibility\nresponsible\nresponsibly\nresponsive\nresponsiveness\nrespublica\nressort\nrest\nrest-cure\nrestatement\nrestaurant\nrestaurateur\nreste\nrested\nrestful\nrestfully\nrestfulness\nrestharrow\nrestiff\nresting\nrestituit\nrestitution\nrestitutionist\nrestive\nrestively\nrestless\nrestlessly\nrestlessness\nrestorable\nrestoral\nrestoration\nrestorative\nrestore\nrestored\nrestorer\nrestoring\nrestrain\nrestrainable\nrestrained\nrestraint\nrestrengthen\nrestrict\nrestricted\nrestriction\nrestrictive\nrestrictively\nrestrictiveness\nrestringency\nrestringent\nresty\nresublimed\nresult\nresultance\nresultant\nresulting\nresults\nresume\nresumption\nresupination\nresurge\nresurgent\nresurrection\nresurvey\nresuscitate\nresuscitated\nresuscitation\nresuspension\nretable\nretail\nretailer\nretailing\nretain\nretained\nretainer\nretaining\nretake\nretaliate\nretaliating\nretaliation\nretaliative\nretaliatory\nretama\nretard\nretardation\nretarded\nretardment\nretch\nretection\nretem\nretention\nretentive\nretentively\nretentiveness\nreticence\nreticent\nreticently\nreticle\nreticular\nreticulate\nreticulated\nreticulation\nreticule\nreticulitermes\nreticulum\nretiform\nretina\nretinal\nretinene\nretinitis\nretinue\nretire\nretired\nretiree\nretirement\nretiring\nretold\nretort\nretouch\nretour\nretrace\nretract\nretractable\nretractation\nretracted\nretractile\nretraction\nretractor\nretraining\nretral\nretread\nretreat\nretreatant\nretreated\nretreating\nretrench\nretrenchment\nretrial\nretribute\nretribution\nretributive\nretrievable\nretrieval\nretrieve\nretrieveable\nretriever\nretroaction\nretroactive\nretroactively\nretrocede\nretrocession\nretroflection\nretroflex\nretrogradation\nretrograde\nretrograduation\nretrogression\nretrogressive\nretrophyllum\nretrorocket\nretrorse\nretrorsum\nretrospect\nretrospection\nretrospective\nretrospectively\nretrousse\nretroversion\nretrovert\nretrovirus\nretrovision\nretrude\nretsina\nreturn\nreturnable\nreturning\nreturning(a)\nreturns\nreunion\nreuptake\nrev\nrevanche\nreveal\nrevealed\nrevealing\nrevealment\nreveille\nreveiller\nreveillez\nrevel\nrevelation\nrevelations\nreveler\nrevelers\nreveling\nrevelling\nrevelry\nrevels\nrevenant\nrevendicate\nrevendication\nrevenge\nrevengeful\nrevengefully\nrevengement\nrevenons\nrevenouns\nrevenue\nrevenues\nreverberant\nreverberate\nreverberation\nreverberations\nreverberatory\nrevere\nrevered\nreverence\nreverenced\nreverend\nreverent\nreverentia\nreverential\nreverentially\nreverie\nrevers\nreversal\nreverse\nreversed\nreverseless\nreversely\nreversibility\nreversible\nreversibly\nreversion\nreversionary\nreversioner\nreversis\nrevert\nreverti\nrevertible\nreverting\nrevest\nrevetment\nreviction\nreview\nreview(a)\nreviewer\nrevile\nreviler\nrevisal\nrevise\nrevised\nrevising\nrevision\nrevisionism\nrevisionist\nrevisit\nrevitalized\nrevival\nrevivalism\nrevivalist\nrevivalistic\nrevive\nrevived\nrevivessence\nrevivication\nrevivification\nrevivify\nreviviscence\nrevocable\nrevocation\nrevocatory\nrevoir\nrevoke\nrevokement\nrevolt\nrevolter\nrevolting\nrevolution\nrevolutionary\nrevolutionist\nrevolutionize\nrevolve\nrevolver\nrevolving\nrevue\nrevulsion\nrevulsive\nrewa-rewa\nreward\nrewardable\nrewardful\nrewarding\nreword\nrewording\nrewrite\nrewriting\nreykjavik\nreynard\nrez\nrezdechaussee\nrf\nrfarceur\nrg\nrh\nrhabdology\nrhabdomancy\nrhabdovirus\nrhadamanthus\nrhadamnathus\nrhagoletis\nrhamnaceae\nrhamnales\nrhamnus\nrhamphoid\nrhapis\nrhapsodical\nrhapsodist\nrhapsody\nrhea\nrheidae\nrheiformes\nrhenish\nrhenium\nrheologic\nrheology\nrheometer\nrheostat\nrhesus\nrhetoric\nrhetorical\nrhetorically\nrhetorician\nrheum\nrheumatic\nrheumatism\nrheumatologist\nrheumatology\nrhexia\nrhinal\nrhincodon\nrhincodontidae\nrhine\nrhineland\nrhinestone\nrhinitis\nrhino\nrhinobatidae\nrhinoceros\nrhinocerotidae\nrhinolophidae\nrhinonicteris\nrhinoptera\nrhinotermitidae\nrhinotracheitis\nrhipidate\nrhipsalis\nrhizobiaceae\nrhizobium\nrhizoctinia\nrhizoid\nrhizome\nrhizomorph\nrhizophora\nrhizophoraceae\nrhizopod\nrhizopoda\nrhizopogon\nrhizopogonaceae\nrhizopus\nrho\nrhodium\nrhodochrosite\nrhododendron\nrhodophyceae\nrhodophyta\nrhodosphaera\nrhodymenia\nrhodymeniaceae\nrhoeadales\nrhomb\nrhombic\nrhombohedral\nrhombohedron\nrhomboid\nrhomboidal\nrhombus\nrhone\nrhone-alpes\nrhubarb\nrhumb\nrhus\nrhyacotriton\nrhyme\nrhymed\nrhymeless\nrhymer\nrhymes\nrhymester\nrhyming\nrhymist\nrhymster\nrhynchocephalia\nrhynchoelaps\nrhyncostylis\nrhynia\nrhyniaceae\nrhyolite\nrhythm\nrhythmical\nrhythmically\nrhythmicity\nri\nriant\nrib\nribald\nribaldry\nriband\nribband\nribbed\nribbing\nribbon\nribbonfish\nribbonlike\nribbons\nribes\nribhus\nribier\nribless\nriblike\nribose\nribosome\nrice\nricegrass\nrich\nrichard\nrichards\nrichea\nrichelieu\nriches\nrichesses\nrichly\nrichmond\nrichmondena\nrichness\nrichweed\nricin\nricinus\nrick\nrickets\nrickettsia\nrickettsiaceae\nrickettsial\nrickettsiales\nrickettsias\nrickety\nrickey\nrickrack\nricksha\nrickshaw\nricochet\nricordarsi\nricordo\nricotta\nrictus\nrid\nriddance\nridden(ip)\nriddle\nriddled\nride\nrideau\nridentem\nrider\nrideret\nriderhorseman\nriderless\nridge\nridged\nridgeling\nridicule\nridiculous\nridiculously\nridiculousness\nridiculus\nriding\nridley\nridotto\nriel\nriemann\nriemannian\nrien\nrienter\nriesling\nrifacimento\nrife\nriff\nriffle\nriffraff\nrifle\nriflebird\nrifled\nrifleman\nrifler\nrifles\nrifleschasseur\nrift\nrig\nrig-veda\nriga\nrigadoon\nrigatoni\nrigel\nrigged\nrigger\nrigging\nriggish\nright\nright(a)\nright-angled\nright-down\nright-hand\nright-hand(a)\nright-handed\nright-handedness\nright-hander\nright-minded\nright-side-out(p)\nright-side-up(p)\nrightabout\nrighted\nrighteous\nrighteously\nrighteousness\nrightfield\nrightful\nrightful(a)\nrightfully\nrighthand\nrighthanded\nrightish\nrightist\nrightly\nrightminded\nrightness\nrights\nrigid\nrigidity\nrigidly\nrigmarole\nrigor\nrigorous\nrigorously\nrigout\nrigsdag\nrigueur\nriksdag\nrile\nrill\nrillet\nrim\nrime\nrimer\nrimfire\nrimiform\nrimless\nrimmed\nrimose\nrimple\nrimu\nrimulose\nrind\nrinderpest\nring\nring-around-the-rosy\nringdove\nringed\nringer\nringgit\nringhals\nringing\nringleader\nringlet\nringleted\nringlike\nringmaster\nrings\nringside\nringtail\nringworm\nrink\nrinse\nrinsings\nrioja\nriot\nrioter\nrioting\nriotous\nrip\nriparia\nriparian\nripcord\nripe\nripe(p)\nripely\nripen\nripeness\nripening\nriposte\nripper\nripple\nrippled\nriprap\nripsaw\nriptide\nrirder\nrire\nrise\nrisen\nriser\nrisibility\nrisible\nrising\nrisk\nrisk-free\nriskily\nriskiness\nrisklessness\nrisks\nrisotto\nrisqu\nrisque\nrissa\nrissole\nrisum\nrit\nrite\nrited\nrites\nritornello\nritual\nritualism\nritualist\nritualistic\nritzy\nrival\nrivalry\nrive\nrivel\nriver\nriverbank\nriverbed\nriverboat\nrivet\nriveted\nriveter\nriviera\nrivina\nrivulation\nrivulet\nrivulose\nrivulus\nrixation\nrixiform\nriyadh\nriyal-omani\nrj\nrk\nrl\nro\nroach\nroad\nroad(a)\nroadbed\nroadblock\nroadbook\nroadhouse\nroadman\nroadrunner\nroads\nroadstead\nroadster\nroadway\nroadworthiness\nroadworthy\nroam\nroan\nroanoke\nroar\nroarer\nroaring\nroast\nroaster\nroasting\nrob\nroba\nrobber\nrobbery\nrobbing\nrobe\nrobert\nroberts\nrobes\nrobin\nrobinia\nrobinson\nroble\nrobolo\nroborant\nrobotics\nrobust\nrobustly\nrobustness\nroc\nroccella\nroccellaceae\nroccus\nrochester\nrocinante\nrock\nrock-ribbed\nrockaway\nrocker\nrocket\nrocketry\nrockfish\nrockies\nrocking\nrockingstone\nrockrose\nrocks\nrockslide\nrockweed\nrocky\nrococo\nrocroi\nrod\nrod-shaped\nrodent\nrodentia\nrodeo\nrodolia\nrodomontade\nroe\nroebuck\nroentgen\nroentgenogram\nroentgenographic\nroentgenography\nroentgenray\nrogation\nroger\nrogers\nrogets\nrogue\nroguery\nroguish\nroguishly\nroi\nroil\nrois\nroister\nroisterer\nroistering\nrolaids\nroland\nrole\nroleplaying\nroll\nroll-on\nrollback\nrolled\nroller\nroller-skater\nrollers\nrollick\nrollicker\nrollicking\nrollickingly\nrolling\nrollingpin\nrollingstone\nrolls\nrolodex\nroly-poly\nroma\nromaic\nroman\nromana\nromanal\nromance\nromancer\nromanesque\nromania\nromanian\nromanism\nromanist\nromanorum\nromanov\nromans\nromantic\nromantically\nromanticism\nromanticist\nromanus\nromany\nrome\nromeo\nromish\nrommel\nromneya\nromona\nromp\nromper\nrompish\nrompre\nromps\nromulus\nron\nrondeau\nrondo\nrondolet\nronian\nrood\nroodloft\nroods\nroodscreen\nroof\nroofed\nroofer\nroofing\nroofless\nrooftop\nrooibos\nrook\nrookery\nroom\nroomage\nroomette\nroomful\nroomily\nroommate\nrooms\nroomy\nroorback\nroosevelt\nrooseveltian\nroost\nrooster\nroot\nrootbound\nrooted\nrooting\nrootless\nrootlet\nroots\nrootstock\nrope\nropedancer\nropedancing\nropes\nropewalk\nropewalker\nropey\nroping\nroppe\nropy\nroquefort\nroquelaure\nroral\nroric\nrorid\nroridula\nroridulaceae\nrorippa\nrorqual\nrosa\nrosaceae\nrosaceous\nrosales\nrosary\nrosas\nroscid\nroscius\nrose\nrose-colored\nrose-red\nrose-root\nroseate\nroseau\nrosebay\nrosebud\nrosecolored\nrosefish\nroselle\nrosellinia\nrosemary\nroses\nrosette\nrosewood\nrosicrucian\nrosicrucianism\nrosidae\nrosilla\nrosin\nrosinweed\nrosita\nrosmarinus\nross\nrossbach\nrossetti\nrossini\nroster\nrostiferous\nrostov\nrostrate\nrostro\nrostroid\nrostrum\nrosy\nrot\nrota\nrotarian\nrotary\nrotatable\nrotate\nrotated\nrotating\nrotation\nrotational\nrotationally\nrotatory\nrotavirus\nrote\nrotenone\nrotgut\nroti\nrotifer\nrotifera\nrotisserie\nrotl\nrotogravure\nrotor\nrotten\nrottenness\nrotter\nrotterdam\nrotting\nrottweiler\nrotulorum\nrotund\nrotunda\nrotundity\nrotundus\nroturier\nrou\nroue\nrouge\nrouged\nrouges\nrough\nrough-and-tumble\nrough-spoken\nroughage\nroughcast\nroughdried\nroughen\nroughhew\nroughhewn\nroughish\nroughly\nroughness\nroughrider\nroughshod\nroulade\nrouleau\nroulette\nround\nround-arm\nround-bottomed\nround-eyed\nroundabout\nrounded\nroundedness\nroundel\nroundelay\nrounder\nrounders\nroundhead\nroundhouse\nroundish\nroundlet\nroundly\nroundness\nrounds\nroundsman\nroundup\nroundworms\nroup\nrouse\nrouser\nrousing\nrousseau\nrousseauan\nroustabout\nrout\nroute\nroutemarch\nroutine\nroutinely\nroux\nrove\nrover\nrovescio\nroving\nrow\nrowan\nrowanberry\nrowdily\nrowdiness\nrowdy\nrowdyism\nrowel\nrowen\nrower\nrowing\nrowling\nrowlock\nroyal\nroyaliste\nroyally\nroyalty\nroystonea\nrs\nruade\nruat\nrub\nrub-a-dub\nrubadub\nrubber\nrubberized\nrubberneck\nrubbernecker\nrubbernecku\nrubberneek\nrubbers\nrubbery\nrubbing\nrubbish\nrubbishy\nrubble\nrubcate\nrubdown\nrube\nrubefacient\nrubeola\nrubescence\nrubia\nrubiaceae\nrubiales\nrubicelle\nrubicon\nrubicund\nrubicundity\nrubidium\nrubification\nrubiform\nrubify\nrubigo\nrubineous\nruble\nrubric\nrubricate\nrubricose\nrubus\nruby\nrubycolored\nruck\nruckus\nructation\nruction\nrudapithecus\nrudbeckia\nrudd\nrudder\nrudderfish\nrudderless\nrudderpost\nruddiness\nruddle\nruddy\nrude\nrudera\nrudge\nrudiment\nrudimental\nrudimentary\nrudiments\nrudis\nrudra\nrue\nrueful\nruefully\nruff\nruffian\nruffianism\nruffianly\nruffle\nruffled\nrufous\nrufulous\nrug\nrugby\nrugged\nruggedization\nruggedly\nrugose\nrugosity\nrugous\nruhe\nruhr\nruin\nruination\nruined\nruinous\nruinously\nruinousness\nruins\nrule\nruled\nruler\nrulers\nrulership\nrules\nruling\nruly\nrum\nrumal\nrumanian\nrumba\nrumble\nrumbling\nrumen\nrumex\nruminant\nruminantia\nruminate\nrumination\nrummage\nrummer\nrummy\nrumohra\nrumor\nrumored\nrump\nrumpelstiltskin\nrumple\nrumpunt\nrumpus\nrumrunner\nrun\nrun-down\nrun-of-the-mill\nrun-on\nrun-up\nrunabout\nrunagate\nrunaway\nruncinate\nrundle\nrundlet\nrundstedt\nrune\nruner\nrunes\nrung\nrunic\nrunnel\nrunner\nrunner-up\nrunneth\nrunning\nrunning(a)\nrunningover\nrunnion\nrunoff\nrunproof\nruns\nrunt\nrunway\nrupees\nrupert\nrupestral\nrupiah\nrupicapra\nrupicola\nruptiliocarpon\nrupture\nrupturewort\nrural\nruralist\nrurally\nruritania\nruritanian\nrus\nruscaceae\nruscus\nruse\nrush\nrush(a)\nrushgrass\nrushing\nrushlight\nrushmore\nrushy\nrusk\nrusse\nrussell\nrusset\nrussia\nrussian\nrussian-speaking\nrussula\nrussulaceae\nrust\nrust-free\nrustbelt\nrusted\nrustic\nrusticate\nrustication\nrusticity\nrusticus\nrustiness\nrustle\nrustler\nrustless\nrustling\nrustproof\nrusty\nrut\nruta\nrutabaga\nrutaceae\nruth\nruthenium\nrutherford\nrutherfordium\nruthful\nruthless\nruthlessly\nruthlessness\nrutilant\nrutile\nrutilus\nrutted\nruttish\nrutundo\nrv\nrwanda\nrwandan\nrya\nrydberg\nrye\nrynchopidae\nrynchops\nryot\nrypticus\nryukyuan\ns\nsa\nsaba\nsabah\nsabahan\nsabal\nsabaoth\nsabbat\nsabbatarian\nsabbatarianism\nsabbath\nsabbatia\nsabbatical\nsabbatism\nsabe\nsabellian\nsabellianism\nsaber\nsaber-toothed\nsabian\nsabianism\nsabicu\nsabin\nsabine\nsabinea\nsable\nsabotage\nsaboteur\nsabr\nsabra\nsabreur\nsabuline\nsabulous\nsac\nsacatra\nsacchariferous\nsaccharin\nsaccharine\nsaccharinity\nsaccharomyces\nsaccharomycetaceae\nsaccharum\nsaccular\nsacculated\nsaccule\nsacerdotal\nsacerdotalism\nsachel\nsachem\nsachet\nsack\nsackage\nsackbut\nsackcloth\nsacking\nsacra\nsacral\nsacrament\nsacramental\nsacramento\nsacraments\nsacrarium\nsacre\nsacred\nsacredness\nsacrifice\nsacrificeable\nsacrificed\nsacrifices\nsacrificial\nsacrilege\nsacrilegious\nsacrilegiously\nsacrilegiousness\nsacrilegist\nsacring\nsacris\nsacristan\nsacristy\nsacrosanct\nsacrum\nsad\nsadden\nsadder\nsaddle\nsaddle-sore\nsaddleback\nsaddlebag\nsaddlebags\nsaddlebill\nsaddled\nsaddler\nsaddlery\nsaddleshaped\nsadducee\nsade\nsadhe\nsadhu\nsadism\nsadist\nsadistic\nsadleria\nsadly\nsadness\nsadomasochism\nsadomasochist\nsadomasochistic\nsaek\nsaepe\nsafar\nsafari\nsafe\nsafe(p)\nsafe-conduct\nsafe-deposit\nsafebreaker\nsafeconduct\nsafedeposit\nsafeguard\nsafehold\nsafekeeping\nsafely\nsafeness\nsafety\nsafety-related\nsafflower\nsaffron\nsaffroncolored\nsag\nsaga\nsagacious\nsagacity\nsagamore\nsage\nsagebrush\nsages\nsaggittary\nsagina\nsagitta\nsagittal\nsagittaria\nsagittariidae\nsagittarius\nsagittate\nsagittate-leaf\nsagittiform\nsago\nsaguaro\nsaguntum\nsahara\nsaharan\nsahib\nsaick\nsaid\nsaiga\nsail\nsailboat\nsailcloth\nsailer\nsailfish\nsailing\nsailing-race\nsailmaker\nsailor\nsailor's-choice\nsails\nsaimiri\nsainfoin\nsaint\nsaint-bernard's-lily\nsaint-mihiel\nsainthood\nsaintlike\nsaintliness\nsaintly\nsaintpaulia\nsaints\nsaintship\nsaipan\nsais\nsait\nsajama\nsake\nsaki\nsal\nsalaam\nsalaams\nsalable\nsalacious\nsalacity\nsalad\nsalade\nsalai\nsalal\nsalamander\nsalamandra\nsalamandridae\nsalamandriform\nsalami\nsalaried\nsalary\nsale\nsalebrosity\nsalebrous\nsalem\nsalerno\nsalesclerk\nsalesgirl\nsalesman\nsalesmanship\nsalesperson\nsalicaceae\nsalicales\nsalicornia\nsalicylate\nsalience\nsalient\nsalient(ip)\nsalientia\nsaliferous\nsaline\nsalinometer\nsalis\nsalish\nsalislatin\nsaliva\nsalivary\nsalivation\nsalix\nsalleamanger\nsallet\nsallow\nsallowness\nsallust\nsally\nsallyport\nsalmagundi\nsalmi\nsalmis\nsalmo\nsalmon\nsalmonberry\nsalmoncolored\nsalmonella\nsalmonellosis\nsalmonid\nsalmonidae\nsalol\nsalome\nsalon\nsaloon\nsalp\nsalpeter\nsalpichroa\nsalpidae\nsalpiglossis\nsalpinctes\nsalpingectomy\nsalpingitis\nsalpinx\nsalsa\nsalsify\nsalsilla\nsalsola\nsalt\nsalt(a)\nsaltation\nsaltatoric\nsaltatory\nsaltbox\nsaltbush\nsaltcellar\nsaltimbanco\nsaltimbanque\nsaltine\nsaltiness\nsalting\nsalto\nsaltpan\nsaltpeter\nsalts\nsaltshaker\nsaltu\nsaltum\nsaltworks\nsaltwort\nsalty\nsalubrious\nsalubrity\nsaluki\nsalutaris\nsalutary\nsalutation\nsalutatorian\nsalutatory\nsalute\nsalutiferous\nsalva\nsalvable\nsalvadoran\nsalvage\nsalvageable\nsalvager\nsalvation\nsalve\nsalvelinus\nsalver\nsalvinia\nsalviniaceae\nsalvo\nsalyut\nsam\nsama-veda\nsamael\nsamara\nsamaritan\nsamarium\nsamarkand\nsamarskite\nsamba\nsambar\nsambo\nsambucus\nsame\nsame(p)\nsamekh\nsameness\nsamia\nsamiel\nsamnite\nsamoa\nsamoan\nsamolus\nsamovar\nsamoyed\nsamoyedic\nsampan\nsample\nsampler\nsampling\nsams\nsamsara\nsamson\nsamurai\nsana\nsanaa\nsanable\nsanatarium\nsanation\nsanative\nsanativeness\nsanatorium\nsanatory\nsanctification\nsanctified\nsanctify\nsanctimonious\nsanctimoniously\nsanctimoniousness\nsanctimony\nsanction\nsanctionative\nsanctioned\nsanctitude\nsanctity\nsanctorum\nsanctuary\nsanctum\nsanctus\nsand\nsandal\nsandaled\nsandalwood\nsandarac\nsandbag\nsandbagger\nsandbank\nsandbar\nsandblast\nsandblaster\nsandbox\nsandboy\nsandbur\nsandemanian\nsanderling\nsandfish\nsandgrouse\nsandiness\nsandlot\nsandpapery\nsandpiper\nsandpit\nsands\nsandstone\nsandwich\nsandwichman\nsandwichwise\nsandwort\nsandy\nsane\nsanely\nsanfte\nsang\nsangapenum\nsangar\nsangaree\nsangay\nsango\nsangraal\nsanguinaria\nsanguinary\nsanguine\nsanguinity\nsanguinolent\nsanguisuge\nsanhedrim\nsanicle\nsanicula\nsanies\nsanitaire\nsanitarian\nsanitariness\nsanitarium\nsanitary\nsanitation\nsanitized\nsanitorium\nsanity\nsannup\nsano\nsans\nsansculottes\nsansevieria\nsanskrit\nsanskritish\nsantalaceae\nsantalales\nsantalum\nsante\nsantee\nsantiago\nsanto\nsantolina\nsanton\nsanvitalia\nsanyasi\nsaone\nsap\nsapere\nsapid\nsapidity\nsapience\nsapiens\nsapient\nsapientes\nsapienti\nsapientia\nsapindaceae\nsapindales\nsapindus\nsapir\nsapis\nsapit\nsapless\nsapling\nsapodilla\nsaponaceous\nsaponaria\nsaponification\nsaponified\nsaponin\nsapor\nsaporific\nsapotaceae\nsapote\nsapper\nsappers\nsapphic\nsapphire\nsapphirine\nsapphism\nsapphist\nsappho\nsapporo\nsappy\nsapremia\nsaprobe\nsaprobic\nsaprogenic\nsaprogenous\nsaprolegnia\nsaprolegniales\nsaprolite\nsapromyiophyllous\nsapropel\nsaprophagous\nsaprophyte\nsaprophytic\nsapsago\nsapsucker\nsapwood\nsaqqara\nsar\nsara\nsaraband\nsaracen\nsarajevo\nsaran\nsarasvati\nsaratoga\nsaratov\nsarawak\nsarawakian\nsarcasm\nsarcastic\nsarcastically\nsarcobatus\nsarcocephalus\nsarcochilus\nsarcocystis\nsarcodes\nsarcodina\nsarcodinian\nsarcoidosis\nsarcolemma\nsarcolemmal\nsarcolemmic\nsarcology\nsarcoma\nsarcomere\nsarcophaga\nsarcophagus\nsarcophilus\nsarcoplasm\nsarcoptes\nsarcoptidae\nsarcorhamphus\nsarcoscyphaceae\nsarcosine\nsarcosomal\nsarcosomataceae\nsarcosome\nsarcosporidia\nsarcosporidian\nsarcostemma\nsarculation\nsard\nsarda\nsardanaphalus\nsardina\nsardine\nsardinia\nsardinian\nsardinops\nsardonic\nsardonyx\nsari\nsark\nsarmentum\nsarong\nsarpanitu\nsarracenia\nsarraceniaceae\nsarraceniales\nsarrischia\nsarsaparilla\nsartorial\nsartorius\nsarum\nsash\nsashay\nsashimi\nsaskatchewan\nsaskatoon\nsass\nsassaby\nsassafras\nsassenach\nsastra\nsat\nsatan\nsatang\nsatanic\nsatanism\nsatanist\nsatanophobia\nsatchel\nsate\nsateen\nsatellite\nsatiable\nsatiate\nsatiated\nsatiation\nsatiety\nsatin\nsatinleaf\nsatinwood\nsatiny\nsatire\nsatirical\nsatirically\nsatirist\nsatirize\nsatis\nsatisfaction\nsatisfactorily\nsatisfactoriness\nsatisfactory\nsatisfied\nsatisfy\nsatrap\nsatsuma\nsaturate\nsaturated\nsaturation\nsaturday\nsatureja\nsaturity\nsaturn\nsaturnalia\nsaturnia\nsaturnian\nsaturniid\nsaturniidae\nsaturnine\nsatyr\nsatyriasis\nsatyric\nsauce\nsaucebox\nsaucepan\nsaucer\nsaucer-eyed\nsauciness\nsaucy\nsaudi\nsaudi-arabian\nsauerbraten\nsauerkraut\nsauk\nsauna\nsaunter\nsaunterer\nsauria\nsaurian\nsaurischia\nsaurischian\nsauromalus\nsauropod\nsauropoda\nsauropodomorpha\nsauropterygia\nsaurosuchus\nsaururaceae\nsaururus\nsaury\nsausage\nsaussurea\nsaut\nsaute\nsauter\nsauterne\nsauvage\nsauve\nsavage\nsavagely\nsavageness\nsavagery\nsavanna\nsavannah\nsavant\nsavara\nsavarin\nsave\nsave-all\nsaveall\nsaved\nsaveloy\nsavent\nsaver\nsaving\nsavingness\nsavings\nsavior\nsaviour\nsavitar\nsavoir\nsavoir-faire\nsavor\nsavoriness\nsavorless\nsavory\nsavoyard\nsavvy\nsaw\nsawan\nsawder\nsawdust\nsawed-off\nsawfish\nsawfly\nsawhorse\nsawmill\nsawney\nsawpit\nsawtooth\nsawwort\nsawyer\nsawyers\nsax\nsaxe\nsaxe-gothea\nsaxhorn\nsaxicola\nsaxicolous\nsaxifraga\nsaxifragaceae\nsaxifrage\nsaxon\nsaxony\nsaxophonist\nsay\nsay-so\nsaying\nsayonara\nsayornis\nsazerac\nsb\nsbirro\nsc\nscab\nscabbard\nscabby\nscabicide\nscabies\nscabious\nscabrous\nscad\nscads\nscaffold\nscaffolding\nscagliola\nscalable\nscalage\nscalar\nscalawag\nscald\nscale\nscaled\nscaleless\nscalene\nscalenus\nscales\nscalic\nscaliness\nscaling\nscallawag\nscallion\nscallop\nscallopine\nscalp\nscalpel\nscaly\nscam\nscamble\nscammony\nscamp\nscamped\nscamper\nscampi\nscampish\nscan\nscandal\nscandaleuse\nscandalization\nscandalize\nscandalized\nscandalmonger\nscandalmongering\nscandalous\nscandalously\nscandalousness\nscandalum\nscandent\nscandentia\nscandinavia\nscandinavian\nscandium\nscanner\nscanning\nscansion\nscant\nscantily\nscantiness\nscantling\nscanty\nscape\nscapegoat\nscapegrace\nscapes\nscaphiopus\nscaphocephaly\nscaphoid\nscaphopod\nscaphopoda\nscaphosepalum\nscapin\nscapose\nscapula\nscapular\nscapulary\nscapulohumeral\nscar\nscarab\nscarabaeidae\nscarabaeus\nscaramouch\nscarce\nscarcely\nscarcity\nscardinius\nscare\nscarecrow\nscarecrowish\nscaremonger\nscarf\nscarfskin\nscaridae\nscarify\nscarlatina\nscarlet\nscarp\nscarpines\nscarred\nscars\nscartella\nscat\nscath\nscathe\nscathful\nscathing\nscathingly\nscathless\nscatological\nscatology\nscatophagy\nscatter\nscatterbrain\nscatterbrained\nscattered\nscattering\nscatterling\nscaup\nscavenge\nscavenger\nscavenging\nsceleratis\nscelerisque\nsceliphron\nsceloglaux\nsceloporus\nscelus\nscenario\nscenarist\nscend\nscene\nscene-stealer\nscenery\nscenes\nsceneshifter\nscenic\nscenically\nscenography\nscent\nscentbag\nscented\nscentless\nscepter\nsceptically\nsceptrumque\nschaffneria\nschatchen\nschedule\nscheduled\nscheduling\nscheelite\nschefflera\nschema\nschematic\nschematically\nschematist\nschematization\nscheme\nschemer\nschemist\nschenck\nschenectady\nschenk\nscherif\nscherzando\nscherzo\nschesis\nscheuchzeriaceae\nschiller\nschilling\nschinus\nschipperke\nschism\nschismatic\nschismatically\nschismaticalnes\nschismaticism\nschismless\nschist\nschistose\nschistosoma\nschistosomatidae\nschistosome\nschistosomiasis\nschistous\nschizachyrium\nschizaea\nschizaeaceae\nschizogony\nschizoid\nschizomycetes\nschizopetalon\nschizophragma\nschizophrenia\nschizophrenic\nschizophyta\nschizopoda\nschizosaccharomyces\nschizosaccharomycetaceae\nschizothymia\nschlep\nschlock\nschlockmeister\nschlumbergera\nschmaltz\nschmeer\nschmuck\nschnapps\nschnauzer\nschnitzel\nschnook\nschnorrer\nscholar\nscholarly\nscholarship\nscholastic\nscholastically\nscholasticism\nscholiast\nscholium\nschomburgkia\nschonheit\nschool\nschoolbook\nschoolboy\nschoolboys\nschoolchild\nschooldays\nschoolfellow\nschoolfriend\nschoolgirl\nschooling\nschoolman\nschoolmarm\nschoolmaster\nschoolmate\nschoolmistress\nschoolroom\nschoolteacher\nschoolyard\nschooner\nschottische\nschrod\nschrodinger\nschucks\nschwa\nschweigt\nschwer\nschwere\nsciadopityaceae\nsciadopitys\nsciaena\nsciaenidae\nsciaenops\nsciagraph\nsciagraphy\nsciamachy\nsciaridae\nsciatic\nsciatica\nscience\nsciences\nscienter\nscientific\nscientifically\nscientist\nscilla\nscimitar\nscincella\nscincidae\nscincus\nscindapsus\nscintilla\nscintillant\nscintillate\nscintillating\nscintillation\nscintillula\nsciolism\nsciolist\nsciollo\nsciolto\nsciomancy\nscion\nsciotlo\nscipio\nscire\nscirpus\nscissile\nscission\nscissor\nscissor-tailed\nscissors\nscissortail\nscissure\nsciuridae\nsciuromorpha\nsciurus\nsclera\nscleranthus\nsclerite\nscleritis\nsclerodema\nscleroderma\nsclerodermataceae\nsclerodermatales\nsclerometer\nscleroparei\nscleroprotein\nsclerosis\nsclerotic\nsclerotics\nsclerotinia\nsclerotiniaceae\nsclerotium\nsclerotomy\nscobs\nscoff\nscoffer\nscoffing\nscofflaw\nscold\nscolding\nscolecoid\nscolion\nscoliosis\nscollop\nscolopacidae\nscolopax\nscolopendrium\nscolymus\nscolytidae\nscolytus\nscomber\nscomberesocidae\nscomberesox\nscomberomorus\nscombridae\nscombroid\nscombroidea\nsconce\nscone\nscoop\nscoot\nscooter\nscootertrolley\nscopa\nscopal\nscope\nscophthalmus\nscopolamine\nscopolia\nscorbutic\nscorch\nscorched\nscorcher\nscorching\nscore\nscoreboard\nscorekeeper\nscoreless\nscorer\nscores\nscoriae\nscorification\nscorify\nscorn\nscornful\nscorpaena\nscorpaenid\nscorpaenidae\nscorpaenoid\nscorpaenoidea\nscorpio\nscorpion\nscorpionfish\nscorpionida\nscorpions\nscorpionweed\nscorpius\nscorse\nscorzonera\nscot\nscotch\nscotchman\nscoter\nscotfree\nscotland\nscotograph\nscotomy\nscots\nscotswoman\nscott\nscotticism\nscottish\nscoundrel\nscour\nscourer\nscourge\nscouring\nscourings\nscours\nscout\nscouting\nscoutmaster\nscow\nscowl\nscowling\nscowls\nscrabble\nscrabbly\nscrag\nscraggly\nscraggy\nscram\nscramble\nscrambled\nscrambler\nscrambling\nscranch\nscrannel\nscrap\nscrapbook\nscrape\nscraped\nscraper\nscrapheap\nscrapie\nscraping\nscrappiness\nscrappy\nscratch\nscratching\nscratchpad\nscrawl\nscrawled\nscrawny\nscreak\nscreaky\nscream\nscreamer\nscreaming\nscreaming(a)\nscreamingly\nscree\nscreech\nscreed\nscreeen\nscreen\nscreening\nscreenplay\nscreenwriter\nscrew\nscrew-loose\nscrew-topped\nscrewball\nscrewdriver\nscrewed\nscrews\nscrewshaped\nscribble\nscribbler\nscribbling\nscribe\nscribendi\nscriber\nscrim\nscrimmage\nscrimp\nscrimshanker\nscrimshaw\nscrimy\nscrip\nscript\nscripta\nscriptae\nscripted\nscriptural\nscripture\nscriptures\nscriptwriter\nscrivened\nscrivener\nscrod\nscrofula\nscrofulous\nscroll\nscrophularia\nscrophulariaceae\nscrophulariales\nscrotal\nscrotum\nscrounge\nscrub\nscrubbed\nscrubber\nscrubbird\nscrubby\nscrubland\nscruff\nscruffy\nscrum\nscrunch\nscruple\nscrupulosity\nscrupulous\nscrupulously\nscrupulousness\nscrutator\nscrutineer\nscrutinize\nscrutinizer\nscrutiny\nscrutoire\nscud\nscuddle\nscuff\nscuffle\nscull\nsculler\nscullery\nsculling\nscullion\nsculpin\nsculpsit\nsculpt\nsculptor\nsculptress\nsculptural\nsculpture\nsculptured\nscum\nscumble\nscummy\nscup\nscupper\nscuppernong\nscurf\nscurfiness\nscurfy\nscurrile\nscurrility\nscurrilous\nscurrilously\nscurry\nscurvy\nscut\nscutate\nscutcheon\nscute\nscutellaria\nscutiform\nscutigera\nscutigerella\nscutigeridae\nscutter\nscuttle\nscutum\nscyliorhinidae\nscylla\nscyllam\nscyphiform\nscyphose\nscyphozoa\nscyphozoan\nscyphus\nscythe\nsd\nsdeath\nse\nsea\nsea(a)\nsea-duty\nsea-god\nsea-green\nsea-rocket\nseabag\nseabank\nseabeach\nseabird\nseaboard\nseaboard(a)\nseaborne\nseacoast\nseafarer\nseafaring\nseafood\nseafront\nseagirt\nseagoing\nseagrass\nseahorse\nseal\nsealant\nseale\nsealed\nsealing\nseals\nsealskin\nseam\nseamaid\nseaman\nseamanlike\nseamanship\nseamark\nseamed\nseamless\nseamount\nseamstress\nseamy\nseance\nseapiece\nseaplane\nseaport\nseaquake\nsear\nsearch\nsearcher\nsearching\nsearchingly\nsearchless\nsearchlight\nseared\nseas\nseascape\nseashell\nseashore\nseashore(a)\nseasickness\nseaside\nseasnail\nseason\nseasonable\nseasonableness\nseasonably\nseasonal\nseasonally\nseasond\nseasoned\nseasoning\nseat\nseated\nseating\nseats\nseattle\nseauton/gr\nseawan\nseawant\nseaward\nseawater\nseaway\nseaweed\nseaworthiness\nseaworthy\nsebaceous\nsebastiana\nsebastodes\nsebastopol\nseborrhea\nsebum\nsec\nsecale\nsecant\nsecede\nseceder\nsecern\nsecession\nsecessionism\nsecessionist\nseckel\nseclude\nsecluded\nseclusion\nsecond\nsecond-best\nsecond-class\nsecond-in-command\nsecond-rater\nsecond-string\nsecondarily\nsecondariness\nsecondary\nsecondary(a)\nsecondbest\nseconder\nsecondhand\nsecondly\nsecondment\nsecondrate\nsecotiaceae\nsecotiales\nsecrecy\nsecret\nsecretaire\nsecretarial\nsecretariat\nsecretary\nsecretaryship\nsecrete\nsecretin\nsecretion\nsecretive\nsecretively\nsecretiveness\nsecretly\nsecretness\nsecrets\nsect\nsectarian\nsectarianism\nsectarism\nsectarist\nsectary\nsection\nsectional\nsectionalism\nsector\nsectorial\nsects\nsecula\nsecular\nsecularism\nsecularist\nsecularization\nsecularize\nsecularized\nseculorum\nsecundigravida\nsecundines\nsecundum\nsecundus\nsecure\nsecurely\nsecureness\nsecurities\nsecurity\nsed\nsedan\nsedate\nsedated\nsedately\nsedateness\nsedation\nsedative\nsedative-hypnotic\nsedentary\nseder\nsedge\nsedgy\nsedile\nsediment\nsedimentary\nsedition\nseditions\nseditiosissimus\nseduce\nseduced\nseducement\nseducer\nseducing\nseduction\nseductive\nseductively\nseductor\nsedulity\nsedulous\nsedulously\nsedum\nsee\nseed\nseedbed\nseedcake\nseeded\nseeder\nseedless\nseedling\nseeds\nseedsman\nseedtime\nseedy\nseeing\nseek\nseeker\nseeking\nseel\nseelen\nseem\nseeming\nseemingly\nseemless\nseemliness\nseemly\nseems\nseen\nseent\nseep\nseepage\nseer\nseersucker\nseesaw\nseethe\nseething\nsegar\nsegment\nsegmental\nsegnitude\nsegnity\nsegno\nsegosiller\nsegregate\nsegregated\nsegregation\nsegregationist\nseiche\nseidel\nseigneur\nseigneury\nseignior\nseigniorage\nseigniority\nseigniory\nseignority\nseine\nseines\nseipso\nseisin\nseismic\nseismograph\nseismological\nseismologist\nseismology\nseismometer\nseismosaurus\nseiurus\nseize\nseized\nseizin\nseizing\nseizure\nsejunction\nsekhet\nselaginella\nselaginellaceae\nselaginellales\nselar\nselden\nseldom\nseldomness\nselect\nselected\nselection\nselective\nselectively\nselectivity\nselectman\nselector\nselectwoman\nselenarctos\nselene\nselenicereus\nselenipedium\nselenium\nseleucus\nself\nself-absorbed\nself-absorption\nself-accusation\nself-acting\nself-addressed\nself-aggrandizement\nself-analysis\nself-appointed\nself-assertion\nself-assured\nself-awareness\nself-conscious\nself-consciously\nself-consciousness\nself-consistent\nself-contained\nself-control\nself-criticism\nself-deception\nself-defeating\nself-defense\nself-denial\nself-denying\nself-deprecating\nself-depreciation\nself-destructive\nself-determination\nself-discipline\nself-disciplined\nself-discovery\nself-disgust\nself-educated\nself-education\nself-enclosed\nself-esteem\nself-examining\nself-explanatory\nself-expression\nself-feeder\nself-fertilization\nself-fertilized\nself-forgetful\nself-fulfillment\nself-generated\nself-government\nself-gratification\nself-heal\nself-help\nself-hypnosis\nself-imposed\nself-improvement\nself-incrimination\nself-induction\nself-indulgent\nself-insurance\nself-knowledge\nself-limited\nself-locking\nself-love\nself-made\nself-organization\nself-pity\nself-pollination\nself-preservation\nself-propelled\nself-punishment\nself-reproach\nself-restraint\nself-sealing\nself-seeded\nself-service\nself-serving\nself-starter\nself-styled\nself-sufficient\nself-supporting\nself-sustained\nself-torture\nself-whispered\nself-winding\nselfabasement\nselfabnegation\nselfaccusation\nselfaccusing\nselfadmiration\nselfadmiring\nselfannulling\nselfapplauding\nselfapplause\nselfapprobation\nselfcommand\nselfcommuning\nselfcomplacency\nselfconceit\nselfcondemnation\nselfconfidence\nselfconfident\nselfconscious\nselfcontrol\nselfconvicted\nselfconviction\nselfcounsel\nselfdeception\nselfdecit\nselfdefense\nselfdelusion\nselfdenial\nselfdenying\nselfdestruct\nselfdiscipline\nselfesteem\nselfevident\nselfexamination\nselfexistent\nselfexisting\nselfflattering\nselfglorification\nselfglorious\nselfgovernment\nselfgratulation\nselfhelp\nselfimmolation\nselfindulgence\nselfindulgent\nselfinterest\nselfinterested\nselfish\nselfishness\nselfknowledge\nselflaudation\nselflessness\nselflove\nselfluminous\nselfness\nselfopinionated\nselfopinioned\nselfpossessed\nselfpossession\nselfpraise\nselfpreservation\nselfreliance\nselfreliant\nselfreproach\nselfreproof\nselfrespect\nselfrestraint\nselfsacrifice\nselfsacrificing\nselfsame\nselfsameness\nselfsatisfied\nselfseeking\nselfsufficiency\nselfsufficient\nselftaught\nselftormentor\nselftrust\nselfwill\nselfwilled\nselfworship\nselkup\nsell\nseller\nselliform\nselling\nsellout\nselma\nselon\nseltzer\nselvage\nselvedge\nsemantic\nsemantically\nsemanticist\nsemantics\nsemaphore\nsematic\nsembarquer\nsemblance\nsemeiology\nsemeiotics\nsemel\nsemen\nsemester\nsemestral\nsemi\nsemi-abstraction\nsemi-processed\nsemiabstract\nsemiannual\nsemiaquatic\nsemiarid\nsemibarbarian\nsemibreve\nsemicircle\nsemicircular\nsemicolon\nsemicoma\nsemicomatose\nsemiconducting\nsemiconductor\nsemiconscious\nsemidarkness\nsemidesert\nsemidetached\nsemidiameter\nsemidiaphanous\nsemiempirical\nsemifinal\nsemifinalist\nsemifluid\nsemifluidity\nsemiformal\nsemigloss\nsemihard\nsemiliquid\nsemiliquididty\nsemiliquidity\nsemiliterate\nsemilunar\nsemimonthly\nseminal\nseminar\nseminarian\nseminary\nsemination\nseminiferous\nseminole\nseminoma\nseminude\nsemiofficial\nsemiology\nsemiopacous\nsemiopaque\nsemiotic\nsemiotics\nsemipellucid\nsemipermeable\nsemiprecious\nsemiprofessional\nsemipublic\nsemiquaver\nsemirigid\nsemiskilled(a)\nsemisolid\nsemite\nsemiterrestrial\nsemitic\nsemitone\nsemitrailer\nsemitrance\nsemitransparency\nsemitransparent\nsemivowel\nsemiweekly\nsemolina\nsempatch\nsemper\nsempervirent\nsempervirid\nsempiternal\nsempiternity\nsempre\nsempstress\nsemstress\nsen\nsenary\nsenate\nsenator\nsenatorial\nsenators\nsenatorship\nsenatus\nsend\nsender\nsending\nsene\nseneca\nsenecan\nsenecio\nsenega\nsenegal\nsenegalese\nsenes\nsenesce\nsenescence\nseneschal\nseneschalship\nsenhor\nsenile\nsenility\nsenior\nsenior(a)\nseniores\nseniority\nseniti\nsenna\nsenor\nsenora\nsenorita\nsens\nsensate\nsensation\nsensational\nsensationalism\nsensationalist\nsensationally\nsensations\nsense\nsenseless\nsenselessly\nsenses\nsensibility\nsensible\nsensibleness\nsensibly\nsensing\nsensitive\nsensitively\nsensitiveness\nsensitivity\nsensitization\nsensitizer\nsensitizing\nsensitometer\nsensorial\nsensorimotor\nsensorineural\nsensorium\nsensory\nsensual\nsensualism\nsensualist\nsensuality\nsensually\nsensuous\nsensuously\nsensuousness\nsent\nsente\nsentence\nsentences\nsentend\nsententiae\nsentential\nsententiarum\nsententious\nsententiousness\nsentiat\nsentience\nsentient\nsentiment\nsentimental\nsentimentalism\nsentimentalist\nsentimentality\nsentimentalization\nsentimentally\nsentiments\nsentinel\nsentry\nseor\nseoul\nsepal\nseparability\nseparable\nseparably\nseparate\nseparatec\nseparated\nseparately\nseparateness\nseparatio\nseparation\nseparatist\nseparative\nsepia\nsepiidae\nseposition\nsepoy\nseppuku\nsepsis\nsept\nseptal\nseptation\nseptember\nseptentrional\nseptet\nseptett\nseptic\nsepticemic\nsepticity\nseptobasidiaceae\nseptobasidium\nseptrional\nseptuagenarian\nseptuagesima\nseptuagint\nseptum\nseptuple\nsepulcher\nsepulchral\nsepulture\nsequacious\nsequaciousness\nsequacity\nsequel\nsequela\nsequella\nsequence\nsequent\nsequester\nsequestered\nsequestrate\nsequestration\nsequin\nsequitur\nsequoia\nsequoiadendron\nser\nsera\nserac\nseraglio\nserape\nseraph\nseraphic\nseraphim\nseraphina\nseraskier\nserbia\nserbian\nserbo-croat\nsere\nserein\nserenade\nserenading\nserendipity\nserene\nserenely\nsereness\nserenity\nserenoa\nserer\nserf\nserfdom\nserflike\nserge\nsergeant\nsergeantatlaw\nserger\nseria\nserial\nserialism\nserialization\nserially\nseriatim\nsericocarpus\nsericultural\nsericulture\nsericulturist\nseries\nserieux\nserif\nserigraphy\nserin\nserine\nserinus\nseriocomedy\nseriocomic\nseriola\nserious\nseriously\nseriousness\nseriphidium\nseriphus\nseris\nserjeant-at-law\nsermon\nsermonize\nsermonizer\nsermons\nserologic\nserology\nseron\nseroon\nserosity\nserotine\nserotonin\nserous\nserow\nserpent\nserpentes\nserpentine\nserranidae\nserranus\nserrasalmus\nserrate\nserrated\nserration\nserratula\nserratus\nserried\nserrulate\nsertularia\nsertularian\nserum\nserval\nservans\nservant\nservare\nserve\nserved\nservente\nserver\nservice\nserviceability\nserviceable\nserviceman\nservices\nservicing\nservile\nservile(a)\nservility\nserving\nservitor\nservitorship\nservitude\nservitus\nservo\nservomechanical\nses\nsesame\nsesamum\nsesbania\nseseli\nseso\nsesotho\nsespuipedalia\nsesqui\nsesquipedal\nsesquipedalian\nsesquipedality\nsess\nsessile\nsession\nsessions\nsestet\nsestiad\nset\nset(p)\nset-to\nseta\nsetaceous\nsetaria\nsetarious\nsetback\nsetdown\nsetoff\nsetophaga\nsetose\nsetous\nsett\nsettee\nsetter\nsetting\nsettle\nsettled\nsettlement\nsettler\nsettles\nsettling\nsettlor\nsetto\nsetup\nseven\nseven-spot\nseven-up\nsevenfold\nsevens\nseventeen\nseventeenth\nseventh\nseventhly\nseventies\nseventieth\nseventy\nsever\nseverable\nseveral\nseveral(a)\nseveral(p)\nseverality\nseveralize\nseverally\nseveralty\nseverance\nsevere\nsevered\nseverely\nseverity\nsevers\nseville\nsew\nsewage\nseward\nsewed\nsewer\nsewerage\nsewing\nsex\nsex-limited\nsex-linkage\nsex-linked\nsex-starved\nsexagenarian\nsexagenary\nsexagesimal\nsexed\nsexism\nsexist\nsexless\nsext\nsextant\nsextet\nsextodecimo\nsexton\nsextuple\nsexual\nsexually\nsexy\nseychelles\nseychellois\nseymour\nseyyid\nsf\nsfax\nsforzando\nsg\nsgosiller\nsgraffito\nsh\nsha'ban\nshabbily\nshabbiness\nshabby\nshabby-genteel\nshack\nshackle\nshad\nshade\nshaded\nshades\nshading\nshadow\nshadowboxing\nshadowed\nshadowing\nshadowness\nshadows\nshadowy\nshady\nshaft\nshag\nshagbark\nshagged\nshaggily\nshagginess\nshaggy\nshaggymane\nshagreen\nshah\nshahaptian\nshaitan\nshakable\nshake\nshakedown\nshaken\nshakeout\nshaker\nshakes\nshakespeare\nshakespearian\nshakily\nshakiness\nshaking\nshako\nshakti\nshaktism\nshaktist\nshaky\nshale\nshall\nshallop\nshallot\nshallow\nshallowbrain\nshallowly\nshallowness\nshallowpated\nshallows\nshallu\nsham\nshaman\nshamanism\nshamanist\nshamash\nshamble\nshambles\nshambling\nshame\nshamefaced\nshamefacedly\nshamefacedness\nshameful\nshamefulness\nshameless\nshamelessness\nshamesense\nshampoo\nshamrock\nshandredhan\nshandygaff\nshanghai\nshanghaier\nshank\nshanks\nshankss\nshanny\nshantung\nshanty\nshantytown\nshape\nshape-up\nshaped\nshapeless\nshapelessly\nshapelessness\nshapeley\nshapeliness\nshapely\nshapen\nshapes\nshaping\nshar\nshard\nshare\nsharecropper\nshared\nshareholder\nshareholding\nsharer\nshares\nshareware\nsharif\nsharing\nshark\nsharkskin\nsharksucker\nsharp\nsharp-cornered\nsharp-eared\nsharp-eyed\nsharp-limbed\nsharpen\nsharpened\nsharpener\nsharpens\nsharper\nsharpie\nsharping\nsharply\nsharpness\nsharpset\nsharpshooter\nsharpshooting\nsharptoothd\nshasta\nshastan\nshastra\nshatter\nshattered\nshattering\nshatterpated\nshatterproof\nshattery\nshave\nshaven\nshaver\nshavian\nshaving\nshavous\nshaw\nshawl\nshawm\nshawnee\nshawwal\nshay\nshe\nshe-oak\nsheaf\nshear\nsheared\nshearing\nshears\nshearwater\nsheath\nsheathe\nsheathed\nsheathing\nshebang\nshebat\nshebeen\nshed\nshedding\nshedim\nsheen\nsheeny\nsheep\nsheepfold\nsheepherder\nsheepish\nsheepishly\nsheeplike\nsheepman\nsheeps\nsheepshank\nsheepshead\nsheepshearing\nsheepskin\nsheeptick\nsheepwalk\nsheer\nsheet\nsheeting\nsheetlike\nsheetrock\nsheets\nsheffield\nshegetz\nsheik\nsheika\nsheikdom\nshekel\nshekels\nsheldrake\nshelduck\nshelf\nshelfful\nshell\nshell-less\nshellac\nshelled\nshelley\nshellfire\nshellfish\nshellflower\nshelter\nsheltered\nsheltie\nshelve\nshelved\nshelving\nshen-pao\nshenanigan\nshend\nshenstone\nshenyang\nsheol\nshepard\nshepherd\nshepherdess\nshepherds\nsheppard\nsheraton\nsherbert\nsheridan\nsheriff\nsherry\nshetland\nshiah\nshibboleth\nshield\nshielded\nshielding\nshift\nshiftily\nshifting\nshiftless\nshiftlessness\nshifts\nshigella\nshigellosis\nshih-tzu\nshiitake\nshiite\nshikar\nshikari\nshikoku\nshiksa\nshillelagh\nshillelah\nshilling\nshillings\nshillyshally\nshiloh\nshim\nshimmer\nshimmering(a)\nshin\nshina\nshindig\nshindy\nshine\nshiner\nshines\nshingle\nshingling\nshingon\nshininess\nshining\nshinney\nshinny\nshinplaster\nshintiyan\nshinto\nshintoist\nshiny\nship\nship-breaker\nshipboard\nshipbuilder\nshipbuilding\nshiplaster\nshipload\nshipman\nshipmate\nshipment\nshipowner\nshippen\nshipper\nshipping\nshipshape\nshipside\nshipworm\nshipwreck\nshipwrecked\nshipwright\nshipyard\nshiraz\nshire\nshirink\nshirk\nshirker\nshirking\nshirring\nshirt\nshirtdress\nshirtfront\nshirting\nshirtmaker\nshirtsleeve\nshirtsleeves\nshirttail\nshirtwaist\nshirty\nshit\nshitless\nshittah\nshittimwood\nshiv\nshiva\nshivaism\nshivaist\nshivaree\nshive\nshiver\nshivering\nshivers\nshivery\nshizoku\nshoal\nshoals\nshoaly\nshock\nshock-headed\nshockable\nshocker\nshocking\nshockingly\nshod\nshoddily\nshoddiness\nshoddy\nshoe\nshoebill\nshoebox\nshoeful\nshoehorn\nshoelace\nshoemaker\nshoemaking\nshoes\nshoeshop\nshoestring\nshoetree\nshofar\nshofle\nshog\nshogi\nshogun\nshoji\nshoo\nshoofly\nshook\nshoot\nshoot-'em-up\nshoot-down\nshooter\nshooting\nshootingcoat\nshop\nshopfront\nshopkeeper\nshoplifter\nshoplifting\nshopman\nshopmate\nshopper\nshopping\nshopworn\nshore\nshorea\nshorebird\nshoreless\nshoreline\nshoring\nshorn\nshort\nshort-dated\nshort-handed\nshort-range\nshort-run\nshortage\nshortbread\nshortbreathed\nshortcake\nshortcoming\nshortcut\nshorten\nshortening\nshortgrass\nshorthand\nshorthorn\nshortia\nshortish\nshortlived\nshortly\nshortness\nshorts\nshortsighted\nshortsightedness\nshortstop\nshortwinded\nshoshone\nshoshonean\nshot\nshotfree\nshotgun\nshots\nshould\nshoulder\nshoulder-to-shoulder\nshouldered\nshoulders\nshout\nshouted\nshove\nshovel\nshoveler\nshovelhead\nshoveling\nshow\nshowboat\nshowcase\nshower\nshowerhead\nshowers\nshowery\nshoweryrainy\nshowing\nshowingcondemned\nshowjumping\nshowman\nshowmanship\nshown\nshowplace\nshowroom\nshowshoe\nshowy\nshrapnel\nshred\nshrew\nshrewd\nshrewdness\nshrewish\nshrewishly\nshrewishness\nshriek\nshrieked\nshrievalty\nshrieve\nshrift\nshriftless\nshrike\nshrill\nshrilling\nshrilling(a)\nshrillness\nshrilly\nshrimp\nshrimpfish\nshrine\nshrink\nshrinkable\nshrinkage\nshrinking\nshrive\nshrivel\nshriveled\nshroud\nshrouded\nshrovetide\nshrub\nshrubbery\nshrubby\nshrublet\nshrug\nshrunk\nshuck\nshucks\nshudder\nshuddering\nshudderingly\nshudra\nshuffle\nshuffleboard\nshuffler\nshuffling\nshufflng\nshuha\nshun\nshunt\nshunted\nshunter\nshush\nshut\nshuteye\nshutout\nshutter\nshutterbug\nshuttered\nshutting\nshuttle\nshuttlecock\nshutvulgar\nshy\nshy(p)\nshyly\nshyness\nshyster\nsi\nsiaats\nsialadenitis\nsialia\nsialidae\nsialis\nsialolith\nsiamang\nsiamese\nsib\nsiberia\nsiberian\nsibi\nsibilant\nsibilation\nsibling\nsibyl\nsibylline\nsic\nsiccity\nsiccus\nsicence\nsich\nsicilian\nsicily\nsick\nsickbay\nsickbed\nsicken\nsickener\nsickening\nsickle\nsicklepod\nsickly\nsickness\nsickroom\nsida\nsidalcea\nside\nside(a)\nside-glance\nside-wheeler\nsidearm\nsidebar\nsideboard\nsideburn\nsidecar\nsided\nsidelight\nsideline\nsideling\nsidelong\nsidera\nsideral\nsideration\nsidereal\nsiderite\nsideritis\nsideroblast\nsiderocyte\nsideromancy\nsideropenia\nsiderosis\nsides\nsidesaddle\nsideshow\nsidesman\nsidestep\nsidestroke\nsidetrack\nsidewalk\nsidewall\nsideward\nsideway\nsideways\nsidewheeler\nsidewinder\nsidewipe\nsiding\nsidle\nsidling\nsidney\nsiege\nsienna\nsierra\nsiesta\nsieve\nsieze\nsif\nsift\nsifter\nsifting\nsigd\nsigh\nsighd\nsighing\nsight\nsighted\nsightedness\nsighting\nsightless\nsightly\nsightof\nsights\nsightseeing\nsightseer\nsigil\nsigma\nsigmodon\nsigmoid\nsigmoidal\nsigmoidectomy\nsigmoidoscope\nsign\nsignal\nsignaler\nsignalization\nsignalize\nsignally\nsignalman\nsignature\nsignboard\nsigned\nsigner\nsignet\nsignificance\nsignificant\nsignificantly\nsignification\nsignificative\nsignificatory\nsignified\nsignifies\nsignify\nsignifying\nsignior\nsigno\nsignor\nsignora\nsignore\nsignorina\nsignpost\nsigns\nsignum\nsigyn\nsike\nsikes\nsikh\nsilage\nsild\nsilence\nsilenced\nsilencer\nsilene\nsilent\nsilentio\nsilently\nsilenus\nsilesia\nsilex\nsilhouette\nsilica\nsilicate\nsiliceous\nsilicide\nsilicle\nsilicon\nsilicone\nsilicosis\nsilique\nsiliquose\nsiliquous\nsilk\nsilk-screen\nsilken\nsilkily\nsilkiness\nsilks\nsilkworm\nsilky\nsill\nsillaginidae\nsillago\nsilliness\nsilly\nsilo\nsiloxane\nsilphium\nsilt\nsiltstone\nsilty\nsilurian\nsilurid\nsiluridae\nsiluriformes\nsilurus\nsilva\nsilvan\nsilver\nsilverback\nsilverberry\nsilvered\nsilverfish\nsilverfooted\nsilvern\nsilverpoint\nsilverrod\nsilverside\nsilversmith\nsilverspot\nsilversword\nsilvertoned\nsilvervine\nsilverware\nsilverweed\nsilverwork\nsilvery\nsilvex\nsilvia\nsilviculture\nsilybum\nsimagre\nsimarouba\nsimaroubaceae\nsimazine\nsimeon\nsimian\nsimilar\nsimilarity\nsimilarly\nsimile\nsimilibus\nsimililitude\nsimilitude\nsimious\nsimmer\nsimmering\nsimnel\nsimon\nsimonianism\nsimony\nsimoom\nsimoon\nsimous\nsimper\nsimpering\nsimple\nsimple-minded\nsimplehearted\nsimpleminded\nsimpleness\nsimples\nsimpleton\nsimplex\nsimplicity\nsimplification\nsimplified\nsimplify\nsimply\nsimpson\nsimpulo\nsimulacrum\nsimulate\nsimulated\nsimulating\nsimulation\nsimulator\nsimulcast\nsimuliidae\nsimulium\nsimultaneity\nsimultaneous\nsimultaneously\nsimultaneousness\nsin\nsinai\nsinanthropus\nsinapis\nsinapism\nsince\nsincere\nsincerefriendshipfriendship\nsincerely\nsincerity\nsinciput\nsind\nsindhi\nsine\nsinecure\nsinew\nsinewless\nsinews\nsinewy\nsinful\nsinfully\nsinfulness\nsing\nsingable\nsingapore\nsingaporean\nsinge\nsinger\nsinghalese\nsinging\nsingle\nsingle(a)\nsingle-barreled\nsingle-bedded\nsingle-breasted\nsingle-handed\nsingle-lane\nsingle-leaf\nsingle-mindedness\nsingle-shelled\nsingle-spaced\nsingle-spacing\nsinglehanded\nsinglehearted\nsingleminded\nsingleness\nsingles\nsinglestick\nsinglet\nsingleton\nsingly\nsingsong\nsingular\nsingularity\nsingularly\nsingulis\nsinhala\nsinhalese\nsinister\nsinistral\nsinistrality\nsinistrally\nsinistrorsal\nsinistrorse\nsinistrous\nsinistrously\nsinitic\nsink\nsinkable\nsinker\nsinkhole\nsinking\nsinless\nsinned\nsinner\nsinning\nsinningia\nsino\nsino-tibetan\nsinologist\nsinologue\nsinology\nsinopis\nsinornis\nsins\nsintered\nsinuate\nsinuation\nsinuosity\nsinuous\nsinus\nsinusitis\nsinusoid\nsinusoidal\nsinusoidally\nsiouan\nsioux\nsip\nsiphon\nsiphonaptera\nsiphonophora\nsiphonophore\nsippet\nsipuncula\nsir\nsircar\nsirdar\nsire\nsiren\nsirene\nsirenia\nsirenidae\nsirens\nsiriasis\nsiris\nsirius\nsirkar\nsirloin\nsirocco\nsirpertinax\nsirrah\nsirup\nsisal\nsiskin\nsison\nsissoo\nsissy\nsister\nsister-in-law\nsisterhood\nsisterly\nsisters\nsistrurus\nsisyphean\nsisyphus\nsisyridae\nsisyrinchium\nsit\nsit-down\nsit-in\nsita\nsitar\nsitcom\nsite\nsith\nsitophylus\nsitotroga\nsitta\nsittidae\nsitting\nsitu\nsituate\nsituated\nsituation\nsitzt\nsium\nsiva\nsivan\nsivapithecus\nsix\nsix-footer\nsix-pack\nsix-spot\nsixes\nsixfold\nsixpence\nsixpenny\nsixshooter\nsixteen\nsixteenth\nsixth\nsixth-former\nsixthly\nsixties\nsixtieth\nsixty\nsizar\nsize\nsized\nsizzle\nsizzling\nsj\nsjambok\nsk\nskagit\nskald\nskaldic\nskanda\nskate\nskateboard\nskateboarder\nskateboarding\nskater\nskates\nskating\nskean\nskedadle\nskeel\nskeesicks\nskeet\nskeezix\nskeg\nskein\nskeletal\nskeleton\nskep\nskepful\nskeptic\nskeptical\nskepticism\nsketch\nsketchbook\nsketcher\nsketchily\nsketchiness\nsketchy\nskew\nskew-eyed\nskewed\nskewer\nski\nski-plane\nskiagraphy\nskibob\nskid\nskidpan\nskier\nskies\nskiff\nskiffle\nskiing\nskill\nskilled\nskillet\nskilletfish\nskillful\nskillfully\nskillfulness\nskilly\nskim\nskimmer\nskimming\nskimp\nskimpily\nskin\nskin-deep\nskin-diver\nskindeep\nskinflint\nskinhead\nskink\nskinned\nskinner\nskinnerian\nskinniness\nskinny\nskintight\nskip\nskipjack\nskipper\nskippet\nskippingly\nskips\nskirl\nskirmish\nskirmisher\nskirmishers\nskirret\nskirt\nskirtdance\nskirting\nskirts\nskit\nskitter\nskittish\nskittishly\nskittishness\nskittle\nskittles\nskivvies\nskivvy\nskoal\nskua\nskuld\nskulk\nskulking\nskull\nskullcap\nskunk\nskunkweed\nskurry\nsky\nsky-diving\nsky-high\nskyaspiring\nskyblue\nskycolored\nskydiver\nskydyed\nskye\nskylab\nskylark\nskylarking\nskylight\nskyline\nskyrocket\nskysail\nskyscraper\nskyward\nskywriting\nsl\nslab\nslabber\nslabby\nslack\nslacken\nslackening\nslacker\nslackness\nslacks\nslade\nslag\nslagheap\nslain\nslake\nslalom\nslam\nslam-dunk\nslammerkin\nslammock\nslammocky\nslander\nslanderer\nslanderous\nslang\nslangily\nslanginess\nslangwhanger\nslangy\nslant\nslantingly\nslantwise\nslap\nslap-bang\nslapbang\nslapdash\nslapshot\nslapstick\nslash\nslashed\nslashing\nslat\nslate\nslate-gray\nslates\nslating\nslattern\nslatternliness\nslatternly\nslaty\nslaughter\nslaughterhouse\nslaughtering\nslaughterous\nslav\nslave\nslave(a)\nslaveholder\nslaveholding\nslavelike\nslaver\nslavery\nslaves\nslavic\nslavish\nslavishly\nslavonic\nslay\nslayer\nsleave\nsleazy\nsled\nsledder\nsledding\nsledge\nsleek\nsleekly\nsleep\nsleeper\nsleepers\nsleepful\nsleepily\nsleepiness\nsleeping\nsleeping(a)\nsleeplessly\nsleeplessness\nsleepwalker\nsleepwalking\nsleepy\nsleepyhead\nsleet\nsleety\nsleeve\nsleeved\nsleeveless\nsleeves\nsleigh\nsleight\nslender\nslender-waisted\nslenderly\nslenderness\nsleuth\nsleuthhound\nsleve\nslew\nslice\nsliced\nslicer\nslicing\nslick\nslickness\nslide\nslider\nsliding\nslience\nslight\nslightest\nslightingly\nslightly\nslightmade\nsliky\nslily\nslim\nslime\nslimed\nsliminess\nslimy\nsling\nslinger\nslinging\nslink\nslinky\nslip\nslip-on\nslipcover\nslipknot\nsliplet\nslippage\nslipper\nslippered\nslipperiness\nslippers\nslippery\nslipping\nslippy\nslipslod\nslipslop\nslipstickcoll\nslipstream\nslit\nslither\nslithery\nsliver\nslivovitz\nsloanea\nslob\nslobber\nsloe\nslog\nslogan\nsloganeer\nsloganeering\nsloop\nslop\nslope\nslopeness\nslopewise\nsloping\nslopper\nsloppily\nsloppiness\nsloppy\nslops\nslopseller\nslopshop\nslosh\nslot\nsloth\nslothful\nslouch\nslouched\nslouchily\nslouching\nslouchingly\nslouchy\nslough\nslovak\nsloven\nslovene\nslovenia\nslovenian\nslovenliness\nslovenly\nslovenry\nslow\nslow-moving\nslowconsuming\nslowdown\nslower\nslowest\nslowgoing\nslowly\nslowness\nsloyd\nslub\nslubber\nslubberdegullion\nsludge\nslug\nsluggard\nsluggardize\nslugger\nsluggish\nsluggishly\nsluggishness\nsluice\nsluicegate\nsluices\nsluicing\nslum\nslumber\nslumberer\nslumberous\nslumgullion\nslummock\nslummocky\nslummy\nslump\nslung\nslur\nslurp\nslurred\nslurry\nslush\nslushy\nslut\nsluttish\nsly\nslyness\nsm\nsmack\nsmacker\nsmall\nsmall(a)\nsmall-scale\nsmaller\nsmallest\nsmallholder\nsmallholding\nsmallish\nsmallknowing\nsmallmouth\nsmallness\nsmallpox\nsmalls\nsmalt\nsmaltite\nsmart\nsmarten\nsmarting\nsmartly\nsmartness\nsmarts\nsmash\nsmasher\nsmashing\nsmatch\nsmatter\nsmatterer\nsmattering\nsmear\nsmeared\nsmegma\nsmell\nsmellfeast\nsmelling\nsmells\nsmelt\nsmelter\nsmew\nsmidgen\nsmilacaceae\nsmilax\nsmile\nsmiledon\nsmiles\nsmiling\nsmilingly\nsmilo\nsmirch\nsmirk\nsmitane\nsmite\nsmith\nsmithereens\nsmitten\nsmock\nsmockfaced\nsmocking\nsmog\nsmoggy\nsmoke\nsmoke-filled\nsmoke-free\nsmoked\nsmokehouse\nsmokeless\nsmoker\nsmokerpartysociable\nsmokestack\nsmokey\nsmoking\nsmoky\nsmolder\nsmoldering\nsmolderingly\nsmollett\nsmooch\nsmooth\nsmoothbark\nsmoothed\nsmoothen\nsmoothfaced\nsmoothhound\nsmoothie\nsmoothly\nsmoothness\nsmoothtongued\nsmorgasbord\nsmother\nsmothered\nsmoulder\nsmudge\nsmug\nsmuggle\nsmuggler\nsmuggling\nsmugly\nsmugness\nsmush\nsmut\nsmutch\nsmuttily\nsmuttiness\nsmutty\nsmyrnium\nsn\nsnack\nsnacks\nsnaffle\nsnafu\nsnag\nsnaggy\nsnags\nsnail\nsnailfish\nsnailflower\nsnaillike\nsnails\nsnake\nsnakebird\nsnakebite\nsnakeblenny\nsnakefly\nsnakelike\nsnakes\nsnakestone\nsnakewood\nsnaky\nsnap\nsnapdragon\nsnapper\nsnappish\nsnappishly\nsnappy\nsnapshot\nsnare\nsnarl\nsnarling\nsnatch\nsnatcher\nsnatches\nsnazzy\nsneak\nsneaking\nsneaking(a)\nsneakingly\nsneaky\nsneer\nsneering\nsneeringly\nsneeze\nsneezed\nsneezeweed\nsneezing\nsneezy\nsnick\nsnicker\nsnide\nsnider\nsniff\nsniffing\nsniffle\nsniffling(a)\nsnifter\nsnigger\nsniggle\nsnip\nsnipe\nsnipefish\nsniper\nsnippet\nsnips\nsnipsnap\nsnit\nsnitch\nsnivel\nsniveling\nsno-cat\nsnob\nsnobbery\nsnobbish\nsnobbishly\nsnobbishness\nsnogging\nsnood\nsnook\nsnooker\nsnooks\nsnoop\nsnooze\nsnore\nsnoren\nsnorer\nsnoring\nsnorkel\nsnorkeling\nsnort\nsnorter\nsnorting\nsnot\nsnotty\nsnout\nsnow\nsnow-blind\nsnow-clad\nsnow-in-summer\nsnow-on-the-mountain\nsnow-white\nsnowball\nsnowbank\nsnowbell\nsnowberry\nsnowblindness\nsnowbound\nsnowdrift\nsnowfield\nsnowflake\nsnowman\nsnowmobile\nsnowplough\nsnowplow\nsnowsuit\nsnowwhite\nsnowy\nsnub\nsnuff\nsnuff-color\nsnuffbox\nsnuffcolored\nsnuffer\nsnuffers\nsnuffing\nsnuffle\nsnuffs\nsnuffy\nsnug\nsnuggery\nsnugly\nsnugness\nso\nso(p)\nsoak\nsoaker\nsoaking\nsoakingsoft\nsoandso\nsoap\nsoapberry\nsoapbox\nsoapfish\nsoapstone\nsoapsuds\nsoapweed\nsoapwort\nsoapy\nsoar\nsoaring\nsoave\nsob\nsobbing\nsobbingly\nsober\nsobering\nsoberminded\nsoberness\nsobersided\nsobersides\nsobralia\nsobriety\nsobriquet\nsoc\nsocage\nsocalled\nsoccer\nsociability\nsociable\nsociableness\nsociably\nsocial\nsocialism\nsocialist\nsocialistic\nsocialists\nsocialite\nsociality\nsocialization\nsocialized\nsocially\nsociety\nsociis\nsocinian\nsocinianism\nsociobiologic\nsociobiologically\nsociobiologist\nsociobiology\nsociocultural\nsocioeconomic\nsocioeconomically\nsociolinguist\nsociolinguistic\nsociolinguistically\nsociolinguistics\nsociological\nsociologically\nsociologist\nsociology\nsociometry\nsociopath\nsociopathic\nsocius\nsock\nsockdolager\nsocket\nsockeye\nsocle\nsocrates\nsocratic\nsod\nsoda\nsodalist\nsodalite\nsodality\nsodden\nsodium\nsodoku\nsodom\nsodomite\nsodomy\nsoepe\nsofa\nsofia\nsoft\nsoft-boiled\nsoft-footed\nsoft-shoe\nsoft-spoken\nsoftball\nsoftbuzzing\nsoften\nsoftened\nsoftener\nsoftening\nsofter\nsofthearted\nsoftheartedness\nsoftish\nsoftling\nsoftly\nsoftness\nsoftspoken\nsoftware\nsoftwood\nsofty\nsogginess\nsoggy\nsoho\nsoi\nsoidisant\nsoigne\nsoil\nsoiled\nsoiliness\nsoiling\nsoilure\nsoimemefrench\nsoiree\nsoirie\nsoissons\nsoit\nsoixante-neuf\nsojourn\nsojourner\nsoke\nsokoro\nsol\nsola\nsolace\nsolan\nsolanaceae\nsolanaceous\nsolandra\nsolanopteris\nsolanum\nsolar\nsolarization\nsolatium\nsold\nsold-out\nsold-out(a)\nsoldan\nsolder\nsoldering\nsoldier\nsoldierfish\nsoldiering\nsoldierlike\nsoldierly\nsoldiers\nsoldiership\nsoldiery\nsole\nsolea\nsolecism\nsolecistic\nsolecistical\nsolecize\nsoled\nsoleidae\nsoleil\nsoleless\nsolemn\nsolemnity\nsolemnization\nsolemnize\nsolemnly\nsolemnment\nsolenichthyes\nsolenidae\nsolenogaster\nsolenogastres\nsolenoid\nsolenopsis\nsolenostemon\nsoleus\nsolfa\nsolfeggio\nsolferino\nsolicit\nsolicitant\nsolicitation\nsolicited\nsolicitor\nsolicitorship\nsolicitous\nsolicitously\nsolicitude\nsolid\nsolid-state\nsolidago\nsolidarity\nsolidate\nsolidation\nsolidification\nsolidified\nsolidify\nsolidity\nsolidly\nsolidness\nsolidungulate\nsolidus\nsoliloquize\nsoliloquizing\nsoliloquy\nsoliped\nsolipsism\nsolitaire\nsolitarily\nsolitariness\nsolitary\nsolito\nsolitude\nsolitudo\nsolleret\nsolmization\nsolo\nsoloist\nsolomon\nsolomon's-seal\nsolon\nsolresol\nsolstice\nsolubility\nsoluble\nsolubleness\nsolum\nsolumforti\nsolus\nsolute\nsolution\nsolvable\nsolvate\nsolvation\nsolve\nsolved\nsolvency\nsolvent\nsolving\nsoma\nsomali\nsomalia\nsomalian\nsomateria\nsomatic\nsomatics\nsomatism\nsomatist\nsomatology\nsomatoscopic\nsomatosense\nsomatosensory\nsomatotropin\nsomber\nsomberly\nsombrero\nsombrous\nsome\nsome(a)\nsomebody\nsomeday\nsomehow\nsomersault\nsomerset\nsomesthesia\nsomething\nsometime\nsometimes\nsomewhat\nsomewhere\nsomme\nsommelier\nsomnambulism\nsomnambulist\nsomnia\nsomnifacient\nsomniferous\nsomnific\nsomniloquist\nsomnolence\nsomnolent\nsomnus\nsomrai\nson\nson-in-law\nsonant\nsonar\nsonata\nsonchus\nsonderbund\nsone\nsong\nsongbird\nsongbook\nsonge\nsonges\nsonghai\nsongster\nsongstress\nsongtag\nsongwriter\nsonhabilite\nsonic\nsoniferous\nsonnet\nsonneteer\nsonogram\nsonography\nsonora\nsonorant\nsonorific\nsonorous\nsonorously\nsonorousness\nsons\nsonship\nsont\nsoon\nsooner\nsoonest\nsoot\nsooth\nsoothe\nsoothing\nsoothingly\nsoothsayer\nsoothsaying\nsoothysay\nsooty\nsop\nsoph\nsophi\nsophism\nsophist\nsophister\nsophistic\nsophistical\nsophisticate\nsophisticated\nsophistication\nsophistry\nsophomore\nsophomore(a)\nsophora\nsoporiferous\nsoporific\nsoporous\nsopranino\nsoprano\nsorb\nsorbate\nsorbent\nsorbet\nsorbian\nsorbus\nsorcerer\nsorceress\nsorcery\nsordes\nsordet\nsordid\nsordidly\nsordidness\nsordine\nsore\nsore-eyed\nsorely\nsoreness\nsorex\nsorghum\nsorgo\nsoricidae\nsorites\nsoror\nsororal\nsorority\nsorption\nsorrel\nsorrow\nsorrowfu\nsorrowful\nsorrowfully\nsorrowing\nsorrows\nsorry\nsort\nsortable\nsortance\nsorted\nsorter\nsortes\nsortie\nsortilege\nsortilegy\nsorting\nsortition\nsorts\nsorus\nsos\nsoso\nsossle\nsot\nsoterial\nsotho\nsotnia\nsottish\nsottishly\nsottishness\nsotto\nsou\nsou'wester\nsouari\nsoubise\nsoubrette\nsouci\nsouffl\nsouffle\nsough\nsoughingly\nsought\nsouk\nsoul\nsoul-destroying\nsoul-searching\nsoulful\nsoulfully\nsoulless\nsoullessly\nsouls\nsoulstirring\nsound\nsoundbox\nsounding\nsoundingboard\nsoundings\nsoundless\nsoundly\nsoundman\nsoundminded\nsoundness\nsoundproof\nsounds\nsoundtrack\nsoup\nsoup-strainer\nsoupcon\nsoupe\nsoupspoon\nsour\nsourball\nsourbread\nsource\nsources\nsourdet\nsourdine\nsourdough\nsourdough(a)\nsoured\nsouring\nsourish\nsourly\nsourness\nsourpuss\nsoursop\nsous\nsousa\nsouse\nsoutache\nsoutane\nsouth\nsouth-american\nsouth-central\nsouth-polar\nsouth-southeast\nsouth-southwest\nsouthbound\nsoutheast\nsoutheaster\nsoutheasterly\nsoutheastern\nsoutheastward\nsoutherly\nsouthern\nsoutherner\nsouthernism\nsouthernmost\nsouthernness\nsouthernwood\nsouthey\nsouthward\nsouthwest\nsouthwester\nsouthwesterly\nsouthwestern\nsouthwestward\nsouvenir\nsouvlaki\nsovereign\nsovereignty\nsoviet\nsoviets\nsow\nsowbane\nsowbelly\nsowbread\nsower\nsoweto\nsowing\nsown\nsows\nsoy\nsoyamilk\nsoybean\nsozzle\nsozzly\nspa\nspace\nspace-time\nspacecraft\nspaced\nspaceflight\nspaces\nspaceship\nspacesuit\nspacetime\nspacing\nspacious\nspackle\nspaddle\nspade\nspadefish\nspadefoot\nspadework\nspadix\nspaghetti\nspaghettini\nspahee\nspahi\nspain\nspake\nspalacidae\nspalax\nspam\nspan\nspandau\nspandex\nspangle\nspangled\nspaniard\nspaniel\nspanish\nspanish-speaking\nspank\nspanker\nspanking\nspannew\nspar\nsparaxis\nspare\nspare-tire\nspared\nsparely\nsparerib\nspareribs\nsparganiaceae\nsparganium\nspargefaction\nspargere\nsparid\nsparidae\nsparing\nsparingly\nspark\nsparkle\nsparkler\nsparkling\nsparks\nsparling\nsparmannia\nsparring\nsparrow\nsparse\nsparsely\nsparseness\nsparsim\nsparta\nspartacus\nspartan\nspartina\nspartium\nspasm\nspasmodic\nspasmodically\nspasmolysis\nspastic\nspasticity\nspat\nspatangoida\nspatchcock\nspathe\nspathic\nspathiphyllum\nspathose\nspatial\nspatially\nspatiotemporal\nspatter\nspatterdash\nspatterdock\nspatula\nspatulate\nspavin\nspavined\nspawn\nspay\nspayed\nspaying\nspazza\nspe\nspeak\nspeakable\nspeakeasy\nspeaker\nspeakership\nspeaking\nspeaking(a)\nspeaks\nspear\nspearfish\nspearhead\nspearman\nspearmint\nspecial\nspeciali\nspecialism\nspecialist\nspecialist(a)\nspecialite\nspeciality\nspecialization\nspecialize\nspecialized\nspecially\nspecialty\nspeciation\nspecie\nspecies\nspecifiable\nspecific\nspecifically\nspecification\nspecificity\nspecificness\nspecified\nspecify\nspecifying\nspecimen\nspecious\nspeciously\nspeciousness\nspeck\nspeckle\nspeckled\nspecscoll\nspectacle\nspectacles\nspectacular\nspectacularly\nspectare\nspectator\nspecter\nspectinomycin\nspectral\nspectrogram\nspectrograph\nspectrographic\nspectrographically\nspectrometric\nspectrophotometer\nspectroscope\nspectroscopic\nspectroscopy\nspectrum\nspeculate\nspeculation\nspeculative\nspeculatively\nspeculativeness\nspeculator\nspeculum\nsped\nspeech\nspeechdeliver\nspeechify\nspeechifying\nspeechless\nspeechlessly\nspeechlessness\nspeechmaker\nspeechwriter\nspeed\nspeed-reading\nspeedboat\nspeeder\nspeedily\nspeeding\nspeedometer\nspeedway\nspeedy\nspeleology\nspell\nspell-checker\nspellbinder\nspellbound\nspeller\nspelling\nspelt\nspem\nspence\nspencer\nspend\nspender\nspending\nspendthrift\nspenser\nspent\nsperanza\nspergula\nspergularia\nsperm\nspermaceti\nspermary\nspermatic\nspermatid\nspermative\nspermatocele\nspermatocyte\nspermatogenesis\nspermatophyta\nspermatophyte\nspermatozoon\nspermicidal\nspermicide\nspermous\nspero\nspes\nspew\nsphacelate\nsphacelation\nsphacelotheca\nsphacelus\nsphaeralcea\nsphaeriaceae\nsphaeriales\nsphaerobolaceae\nsphaerocarpaceae\nsphaerocarpales\nsphaerocarpus\nsphagnales\nsphagnum\nsphecidae\nsphecius\nsphecoidea\nsphecotheres\nspheniscidae\nsphenisciformes\nspheniscus\nsphenodon\nsphenopsida\nsphere\nspheres\nspherical\nspherically\nsphericity\nspherocyte\nspheroid\nspheroidal\nspheroidity\nspherometer\nspherule\nsphery\nsphincter\nsphingidae\nsphinx\nsphygmomanometer\nsphyraena\nsphyraenidae\nsphyrapicus\nsphyrna\nsphyrnidae\nspial\nspic\nspica\nspicate\nspice\nspicebush\nspicemill\nspicilegium\nspiciness\nspick\nspiculate\nspicule\nspiculum\nspicy\nspider\nspiderflower\nspiderwort\nspiegeleisen\nspiel\nspieler\nspigot\nspike\nspikebit\nspiked\nspikelet\nspikemoss\nspiketeam\nspiky\nspill\nspillage\nspillover\nspillway\nspilogale\nspin\nspina\nspinach\nspinacia\nspinal\nspinally\nspindle\nspindle-legged\nspindlelegs\nspindleshanks\nspindrift\nspine\nspinel\nspinelessness\nspinelle\nspinet\nspinnability\nspinnaker\nspinner\nspinney\nspinning\nspinose\nspinosity\nspinous\nspinout\nspinster\nspinsterhood\nspinuliferous\nspinus\nspiny\nspiracle\nspiraea\nspiral\nspirally\nspiranthes\nspire\nspirea\nspiriferous\nspirillaceae\nspirillum\nspirit\nspirited\nspiritful\nspiritless\nspiritoso\nspirits\nspiritstirring\nspiritual\nspiritualism\nspiritualist\nspiritualistic\nspirituality\nspiritualization\nspiritualize\nspiritually\nspiritualty\nspirituel\nspirituous\nspiro\nspirochaeta\nspirochaetaceae\nspirochaetales\nspirochete\nspirodela\nspirogram\nspirograph\nspirogyra\nspiroid\nspirometer\nspironolactone\nspirt\nspirtle\nspirula\nspirulidae\nspissitude\nspit\nspite\nspiteful\nspitefully\nspitfire\nspitsbergen\nspittle\nspittoon\nspitz\nspiv\nspizella\nsplanchnology\nsplash\nsplashboard\nsplashdown\nsplashed\nsplat\nsplatter\nsplay\nsplayfooted\nspleen\nspleenish\nspleenless\nspleenly\nspleenwort\nsplendent\nsplendid\nsplendor\nsplenectomy\nsplenetic\nsplenic\nsplenitis\nsplenomegaly\nsplice\nspliced\nsplicer\nspline\nsplint\nsplinter\nsplintery\nsplit\nsplit-pea\nsplitting\nsplotch\nsplurge\nsplutter\nspluttering\nspode\nspodoptera\nspodumene\nspoil\nspoilage\nspoiled\nspoiler\nspoiling\nspoils\nspoilsport\nspoke\nspoken\nspokesman\nspokesperson\nspokeswoman\nspolia\nspoliate\nspoliation\nspondaic\nspondee\nspondias\nspondylarthritis\nspondylitis\nspondylolisthesis\nsponge\nspongefly\nsponger\nsponginess\nsponging\nspongy\nsponsion\nsponsor\nsponsorship\nspontaieous\nspontaneity\nspontaneous\nspontaneously\nspontaneousness\nspontoon\nspoof\nspook\nspool\nspoon\nspoonbill\nspoonerism\nspoonfeeding\nspoonful\nspoonmeat\nspoony\nspoor\nsporaceous\nsporadic\nsporadically\nsporangiophore\nsporangium\nspore\nsporobolus\nsporocarp\nsporogenous\nsporophore\nsporophorous\nsporophyll\nsporophyte\nsporotrichosis\nsporous\nsporozoa\nsporozoan\nsporozoite\nsporran\nsport\nsporting\nsportingly\nsportive\nsportively\nsports\nsportsman\nsportsmanship\nsportula\nsportulary\nsportule\nsporule\nsposa\nsposh\nsposo\nspot\nspotless\nspotlessly\nspotlessness\nspotlight\nspots\nspotsylvania\nspotted\nspottiness\nspotty\nspousal\nspousals\nspouse\nspouseless\nspout\nsppiritual\nsprag\nspraguea\nsprain\nsprat\nsprawl\nsprawled\nsprawling\nspray\nspray-dried\nsprayer\nspraying\nspread\nspread-eagle\nspreadeagle\nspreadeagleism\nspreader\nspreading\nspreadsheet\nspree\nspretae\nsprig\nsprigged\nsprightful\nsprightly\nspring\nspring(a)\nspring-cleaning\nspringboard\nspringbok\nspringe\nspringer\nspringfield\nspringiness\nspringing\nspringle\nspringless\nspringlike\nspringnet\nsprings\nspringtide\nsprinkle\nsprinkled\nsprinkler\nsprinkling\nsprint\nsprinter\nsprit\nsprite\nsprites\nspritsail\nspritzer\nsprocket\nsprout\nsprouted\nspruce\nsprue\nsprung\nspry\nspud\nspume\nspun\nspunk\nspur\nspurge\nspurious\nspuriously\nspuriousness\nspurn\nspurred\nspurs\nspurt\nspurts\nsputa\nsputnik\nsputter\nspy\nspyeria\nspyglass\nspying\nsqib\nsquab\nsquabble\nsquabby\nsquad\nsquadron\nsquadrong\nsquadroom\nsqualid\nsqualidae\nsquall\nsqually\nsqualor\nsqualus\nsquama\nsquamata\nsquamiferous\nsquamous\nsquamule\nsquamulose\nsquander\nsquandered\nsquanderer\nsquandering\nsquandermania\nsquantum\nsquare\nsquare(a)\nsquare(p)\nsquare-bashing\nsquare-built\nsquare-rigged\nsquare-toed\nsquared\nsquarely\nsquareness\nsquares\nsquaretail\nsquarish\nsquash\nsquashed\nsquashy\nsquat\nsquatina\nsquatinidae\nsquatness\nsquatter\nsquaw\nsquawbush\nsquawk\nsqueak\nsqueal\nsquealer\nsqueamish\nsqueamishly\nsqueasy\nsqueegee\nsqueezable\nsqueeze\nsqueezer\nsqueezers\nsqueezing\nsquelch\nsquib\nsquid\nsquiffy\nsquiggle\nsquiggly\nsquill\nsquilla\nsquillidae\nsquinch\nsquinched\nsquint\nsquint-eyed\nsquirarchy\nsquire\nsquireen\nsquirm\nsquirming\nsquirrel\nsquirrelfish\nsquirt\nsquish\nssc\nst\nst.-bruno's-lily\nstab\nstabbed\nstabber\nstabbing\nstabile\nstabiliment\nstabilitate\nstability\nstabilization\nstabilized\nstabilizer\nstabilizing\nstable\nstableman\nstablemate\nstabling\nstablish\nstabs\nstaccato\nstachyose\nstachys\nstack\nstacked\nstacks\nstaddle\nstadholder\nstadium\nstael\nstaff\nstag\nstage\nstage-struck\nstagecoach\nstagecraft\nstaged\nstagehand\nstageplay\nstager\nstagery\nstages\nstagflation\nstagger\nstaggerbush\nstaggerer\nstaggering\nstaggers\nstaghound\nstagily\nstaginess\nstagirite\nstagnancy\nstagnant\nstagnate\nstagnation\nstagy\nstaid\nstaidness\nstain\nstainability\nstainable\nstained\nstaining\nstainless\nstair\nstair-carpet\nstair-rod\nstaircase\nstairhead\nstairs\nstairway\nstairwell\nstake\nstakeholder\nstakeout\nstalactite\nstalagamite\nstalagmite\nstale\nstalemate\nstaleness\nstalin\nstalk\nstalker\nstalking\nstalking-horse\nstalkinghorse\nstall\nstall-fed\nstallion\nstalls\nstalwart\nstamen\nstamina\nstammel\nstammell\nstammer\nstammerer\nstammering\nstammering(a)\nstammeringly\nstamp\nstampa\nstamped\nstampede\nstance\nstanch\nstanchion\nstanchless\nstand\nstand-alone\nstand-in\nstand-up\nstandard\nstandard-bearer\nstandardization\nstandardize\nstandardized\nstandby\nstanddown\nstandfire\nstandi\nstanding\nstanding(a)\nstandoff\nstandoffishly\nstandpipe\nstandpoint\nstands\nstandstill\nstanhopea\nstanleya\nstannary\nstannite\nstanza\nstapedectomy\nstapelia\nstapes\nstaphylaceae\nstaphylea\nstaphylinidae\nstaphylococcal\nstaphylococcus\nstaple\nstapler\nstar\nstar-duckweed\nstar-of-bethlehem\nstar-thistle\nstarboard\nstarch\nstarched\nstarchless\nstarchlike\nstarchy\nstardom\nstardust\nstare\nstares\nstarets\nstarfish\nstarflower\nstargazer\nstargazing\nstaring\nstaringly\nstark\nstarkblind\nstarkers\nstarkly\nstarless\nstarlet\nstarlight\nstarlike\nstarling\nstarlit\nstarry\nstarry-eyed\nstars\nstarsun\nstart\nstarter\nstarting\nstartle\nstartled\nstartling\nstartlingly\nstartlish\nstarts\nstartup\nstarvation\nstarve\nstarved\nstarveing\nstarveling\nstash\nstasis\nstatant(ip)\nstate\nstate-of-the-art\nstateaided\nstatecraft\nstated\nstatehouse\nstateliness\nstately\nstatement\nstatemonger\nstateroom\nstates\nstatesgeneral\nstatesman\nstatesmanlike\nstatesmanship\nstatewide\nstatic\nstatically\nstatics\nstation\nstationariness\nstationary\nstationer\nstationery\nstationmaster\nstatist\nstatistic\nstatistical\nstatistically\nstatistician\nstatistics\nstative\nstator\nstatu\nstatuary\nstatue\nstatuec\nstatuelike\nstatuette\nstature\nstatus\nstatutable\nstatute\nstatutorily\nstatutory\nstatuvolence\nstatuvolent\nstatuvolic\nstaunch\nstaunchly\nstaurikosaur\nstave\nstay\nstay-at-home\nstay-at-home(a)\nstayathome\nstayed\nstayer\nstayman\nstays\nstaysail\nstead\nsteadfast\nsteadfastness\nsteadied\nsteadily\nsteadiness\nsteady\nsteadying\nsteak\nsteakhouse\nsteal\nstealing\nstealth\nstealthily\nstealthiness\nstealthy\nsteam\nsteamboat\nsteamed\nsteamer\nsteaming\nsteamroller\nsteamship\nstearic\nstearin\nsteatornis\nsteatornithidae\nsteatorrhea\nsteed\nsteel\nsteeled\nsteelmaker\nsteelplate\nsteely\nsteelyard\nsteenbok\nsteep\nsteeped\nsteepish\nsteeple\nsteeplechase\nsteeplechaser\nsteeplejack\nsteeply\nsteepness\nsteeps\nsteer\nsteerable\nsteerage\nsteerageway\nsteerer\nsteering\nsteermate\nsteersman\nsteganographic\nsteganography\nsteganopus\nstegocephalia\nstegosaur\nstein\nstele\nstelis\nstellar\nstellaria\nstellated\nstelliform\nstellite\nstelography\nstem\nstem-winder\nstemless\nstemma\nstemmed\nstench\nstencil\nstenocarpus\nstenochlaena\nstenographer\nstenographic\nstenography\nstenopelmatidae\nstenopelmatus\nstenopterygius\nstenosis\nstenotaphrum\nstenotomus\nstenotus\nstentor\nstentorian\nstentorophonic\nstenua\nstep\nstepbrother\nstepchild\nstepdaughter\nstepfather\nstephanomeria\nstephanotis\nstepmother\nstepparent\nsteppe\nstepping\nsteppingstone\nsteprelationship\nsteps\nstepson\nstepwise\nsteradian\nsteraming\nstercoraceous\nstercorariidae\nstercorarius\nsterculia\nsterculiaceae\nstereo\nstereognosis\nstereognostic\nstereometry\nstereophonic\nstereopticon\nstereoscope\nstereoscopic\nstereoscopy\nstereospondyli\nstereotype\nstereotyped\nsterile\nsterility\nsterilization\nsterilize\nsterilized\nsterling\nstern\nstern(a)\nsterna\nsternal\nsterninae\nsternly\nsternmost\nsternness\nsternocleidomastoid\nsternotherus\nsternpost\nsternum\nsternutation\nsternutative\nsternutator\nsternutatory\nsternway\nsternwheeler\nsteroid\nsteroidal\nsterol\nsterope\nstertorous\nstertorously\nstet\nsteteruntque\nstethograph\nstethoscope\nstevedore\nstevia\nstew\nsteward\nstewardess\nstewardship\nstewing\nstewpan\nsthene\nstheno\nstibnite\nstichaeidae\nsticherus\nstichomancy\nstick\nstick-on\nstickball\nstickily\nstickiness\nsticking\nstickle\nstickleback\nstickler\nstickpin\nsticks\nsticktight\nsticky\nstictomys\nstictopelia\nstiff\nstiff-backed\nstiff-necked\nstiffbacked\nstiffen\nstiffened\nstiffener\nstiffening\nstiffly\nstiffnecked\nstiffness\nstifle\nstifled\nstifling\nstigma\nstigmata\nstigmatic\nstigmatism\nstigmatization\nstigmatize\nstile\nstiletto\nstill\nstillatitious\nstillborn\nstille\nstillhamlet\nstillhunt\nstillicidous\nstillicidum\nstillness\nstillroom\nstilly\nstilt\nstilted\nstiltedly\nstilton\nstilts\nstimulant\nstimulate\nstimulated\nstimulating\nstimulation\nstimulative\nstimulos\nstimulus\nsting\nstinger\nstingily\nstinginess\nstinging\nstingless\nstingo\nstingray\nstings\nstingy\nstink\nstinkhorn\nstinking\nstinkpot\nstint\nstinted\nstintless\nstipe\nstipend\nstipendiary\nstipiform\nstipple\nstipulate\nstipulation\nstir\nstirk\nstirps\nstirred\nstirring\nstirringly\nstirrup\nstitch\nstitchwort\nstive\nstiver\nstizidae\nstizostedion\nstketcher\nsto\nsto/gr\nstoat\nstoccado\nstochastic\nstochastically\nstock\nstock-in-trade\nstockade\nstockbroker\nstockcar\nstocked\nstocker\nstockfish\nstockholder\nstockholding\nstockholm\nstockily\nstockinet\nstocking\nstockist\nstockjobber\nstockjobbing\nstockman\nstockpile\nstockpiling\nstockpot\nstockroom\nstocks\nstocktaking\nstocky\nstockyard\nstodge\nstodginess\nstodgy\nstogy\nstoic\nstoical\nstoically\nstoicism\nstoke\nstokehold\nstoker\nstokesia\nstole\nstolen\nstolid\nstolidity\nstolidly\nstolon\nstoma\nstomach\nstomachache\nstomacher\nstomachus\nstomatal\nstomatitis\nstomatopod\nstomatopoda\nstomatous\nstomp\nstone\nstone-blind\nstone-cold\nstone-dead\nstoneblind\nstonechat\nstonecolored\nstonecress\nstonecrop\nstonefish\nstonefly\nstoneless\nstones\nstonewaller\nstonewalling\nstoneware\nstonework\nstonewort\nstonily\nstony\nstonyhearted\nstood\nstooge\nstook\nstool\nstools\nstoop\nstoopto\nstop\nstopcock\nstope\nstopgap\nstoplight\nstopover\nstoppable\nstoppage\nstopped\nstopper\nstoppered\nstopping\nstopple\nstopwatch\nstorage\nstorax\nstore\nstorecloset\nstored\nstorehouse\nstoreria\nstoreroom\nstores\nstoreship\nstorge/gr\nstoried\nstoring\nstork\nstorksbill\nstorm\nstorm-beaten\nstormbound\nstormily\nstorminess\nstorming\nstormproof\nstormy\nstorthing\nstory\nstoryline\nstoryteller\nstot\nstotinka\nstound\nstoup\nstour\nstout\nstoutheartedness\nstoutly\nstoutness\nstoutstouthearted\nstove\nstovepipe\nstover\nstow\nstowage\nstowaway\nstr\nstrabism\nstrabismus\nstrabotomy\nstraddle\nstraggle\nstraggler\nstraggling\nstraggly\nstraight\nstraight-arm\nstraight-backed\nstraightarrow(a)\nstraightaway\nstraightedge\nstraighten\nstraightened\nstraightforth\nstraightforward\nstraightness\nstraightway\nstrain\nstrained\nstrainer\nstrains\nstrait\nstraitened\nstraitjacket\nstraitlaced\nstraits\nstraitwaistcoat\nstramash\nstrand\nstranded\nstrange\nstrangely\nstranger\nstrangle\nstrangled\nstranglehold\nstrangler\nstrangulated\nstrangulation\nstrap\nstraphanger\nstraping\nstrapless\nstrappado\nstrapper\nstrapping\nstrapwork\nstratagem\nstrategic\nstrategical\nstrategically\nstrategics\nstrategist\nstrategy\nstratford-on-avon\nstrath\nstrathspey\nstratification\nstratified\nstratiform\nstratocracy\nstratosphere\nstratum\nstratus\nstravinsky\nstravinskyan\nstraw\nstrawbail\nstrawberry\nstrawboard\nstrawcolored\nstrawflower\nstraws\nstrawworm\nstray\nstraying\nstreak\nstreaked\nstreaker\nstream\nstreambed\nstreamer\nstreaming\nstreamlet\nstreamlined\nstreamliner\nstreamy\nstreet\nstreetcar\nstreetlight\nstreets\nstreetwalker\nstrekelia\nstrelitzia\nstrelitziaceae\nstrength\nstrengthen\nstrengthener\nstrengthening\nstrengthens\nstrengthless\nstrenuous\nstrenuously\nstrepera\nstrephon\nstrepitus\nstrepsirhini\nstreptobacillus\nstreptocarpus\nstreptococcal\nstreptococcus\nstreptodornase\nstreptokinase\nstreptolysin\nstreptomyces\nstreptomycetacaea\nstreptomycin\nstreptopelia\nstreptosolen\nstreptothricin\nstress\nstressed\nstretch\nstretch(a)\nstretchable\nstretched\nstretcher\nstretcher-bearer\nstretching\nstretching(a)\nstretti\nstrew\nstrewn\nstria\nstriae\nstriate\nstriated\nstrick\nstricken\nstrict\nstrictest\nstrictly\nstrictness\nstricture\nstride\nstridently\nstrides\nstridor\nstridulation\nstridulous\nstrife\nstrigae\nstrigidae\nstrigiformes\nstrigose\nstrike\nstrikebound\nstrikebreaking\nstrikeout\nstriker\nstrikes\nstriking\nstrikingly\nstring\nstringed\nstringency\nstringent\nstringer\nstrings\nstringy\nstringybark\nstriolate\nstrip\nstrip-mined\nstripe\nstriped\nstripes\nstriping\nstripling\nstripped\nstripper\nstriptease\nstrive\nstriving\nstrix\nstrobe\nstrobilomyces\nstroboscope\nstroke\nstrokes\nstroll\nstrolling\nstrom\nstroma\nstromateidae\nstrombidae\nstrombus\nstrong\nstrong-boned\nstrong-minded\nstrongarm\nstrongbox\nstronger\nstrongest\nstrongheaded\nstronghold\nstrongly\nstrongminded\nstrongroom\nstrongscented\nstrongsmelling\nstrongwilled\nstrongylodon\nstrontianite\nstrontium\nstrop\nstrophanthus\nstropharia\nstrophariaceae\nstrophe\nstrow\nstrt\nstruck\nstructural\nstructuralism\nstructurally\nstructure\nstructured\nstructures\nstrudel\nstruggle\nstruggling\nstrum\nstruma\nstrumpet\nstrung\nstrut\nstruthio\nstruthiomimus\nstruthionidae\nstruthioniformes\nstrychnine\nstrymon\nstuart\nstub\nstubbed\nstubble\nstubborn\nstubbornly\nstubbornness\nstubby\nstubstitute\nstuccco\nstucco\nstuck\nstuckup\nstud\nstudbook\nstudded\nstudent\nstudentship\nstudia\nstudied\nstudies\nstudio\nstudious\nstudiously\nstudiousness\nstudy\nstuff\nstuffed\nstuffily\nstuffing\nstuffs\nstuffy\nstullitiam\nstulti\nstultification\nstultified\nstultify\nstultiloquence\nstultiloquy\nstultorum\nstultos\nstumble\nstumblebum\nstumblingblock\nstumblingstone\nstumblng\nstump\nstumping\nstumps\nstumpy\nstun\nstundism\nstundist\nstung\nstunned\nstunner\nstunning\nstunt\nstunted\nstupa\nstupe\nstupefaction\nstupefied\nstupefy\nstupefying\nstupendous\nstupendously\nstupid\nstupidity\nstupidly\nstupor\nstupration\nsturdily\nsturdiness\nsturdy\nsturgeon\nsturnella\nsturnidae\nsturnus\nstutter\nstuttgart\nsty\nstygian\nstyle\nstyled\nstyleless\nstylet\nstylish\nstylishly\nstylist\nstylistic\nstylistically\nstylite\nstylites\nstylization\nstylomecon\nstylophorum\nstylus\nstymie\nstyphelia\nstyptic\nstyracaceae\nstyracosaur\nstyrax\nstyrene\nstyrofoam\nstyx\nsu\nsua\nsuae\nsuanpan\nsuant\nsuasible\nsuasion\nsuasive\nsuasory\nsuave\nsuavely\nsuaviter\nsuavity\nsub\nsub-interval\nsub-rosa\nsub-test\nsubacid\nsubaction\nsubacute\nsubahdar\nsubalpine\nsubaltern\nsubaqueous\nsubarborescent\nsubarctic\nsubartesian\nsubastral\nsubation\nsubatomic\nsubaudition\nsubbase\nsubbing\nsubclass\nsubclavate\nsubclavian\nsubclinical\nsubcommittee\nsubconscious\nsubconsciously\nsubconsciousness\nsubcontinent\nsubcontract\nsubcontractor\nsubcontrary\nsubculture\nsubcutaneous\nsubcutaneously\nsubdean\nsubdepartment\nsubdichotomy\nsubdirectory\nsubdititious\nsubdivide\nsubdivided\nsubdivision\nsubdolous\nsubdominant\nsubduable\nsubdual\nsubduct\nsubduction\nsubdue\nsubdued\nsubduing\nsubdural\nsubeditor\nsuberose\nsuberous\nsubfamily\nsubfigure\nsubfusc\nsubgenus\nsubgross\nsubgroup\nsubheading\nsubhuman\nsubitaneous\nsubito\nsubjacent\nsubject\nsubject(p)\nsubjecta\nsubjected\nsubjection\nsubjectis\nsubjective\nsubjectively\nsubjectiveness\nsubjectivism\nsubjectivist\nsubjectivity\nsubjoin\nsubjugate\nsubjugated\nsubjugation\nsubjunctive\nsubkingdom\nsublapsarian\nsublation\nsublease\nsublevation\nsublieutenant\nsublimate\nsublimated\nsublimation\nsublime\nsublimed\nsublimely\nsublimi\nsublimification\nsubliminal\nsublimination\nsublimity\nsublineation\nsublingual\nsubliterary\nsublittoral\nsublunar\nsublunary\nsubluxation\nsubmarine\nsubmariner\nsubmediant\nsubmerge\nsubmerged\nsubmergence\nsubmerse\nsubmersible\nsubmersion\nsubminister\nsubministration\nsubmission\nsubmissive\nsubmissiveness\nsubmissness\nsubmit\nsubmonish\nsubmonition\nsubmucosa\nsubmultiple\nsubnormal\nsubnormality\nsuboceanic\nsuborbital\nsuborder\nsubordinacy\nsubordinancy\nsubordinate\nsubordinateness\nsubordinating(a)\nsubordination\nsubordinator\nsuborn\nsubornation\nsubpanation\nsubpart\nsubphylum\nsubpoena\nsubpopulation\nsubreption\nsubscribe\nsubscribed\nsubscriber\nsubscript\nsubscription\nsubsection\nsubsequence\nsubsequent\nsubsequently\nsubserve\nsubservience\nsubserviency\nsubservient\nsubset\nsubshrub\nsubside\nsubsidence\nsubsidiary\nsubsidize\nsubsidized\nsubsidy\nsubsist\nsubsistence\nsubsoil\nsubsonic\nsubspace\nsubspecies\nsubstance\nsubstances\nsubstandard\nsubstantial\nsubstantiality\nsubstantially\nsubstantialness\nsubstantiate\nsubstantival\nsubstantive\nsubstation\nsubstitutable\nsubstitute\nsubstituted\nsubstitution\nsubstrate\nsubstratum\nsubstructure\nsubsultorily\nsubsultory\nsubsultus\nsubsumption\nsubsurface\nsubsystem\nsubtend\nsubterfuge\nsubterminal\nsubterranean\nsubterrene\nsubtile\nsubtilie\nsubtilin\nsubtility\nsubtilization\nsubtilize\nsubtilty\nsubtitle\nsubtle\nsubtlety\nsubtly\nsubtonic\nsubtopia\nsubtract\nsubtracted\nsubtraction\nsubtractive\nsubtrahend\nsubtreasury\nsubtropical\nsubtropics\nsubtype\nsubularia\nsubulate\nsuburb\nsuburban\nsuburbanized\nsuburbia\nsuburbs\nsubvention\nsubversion\nsubversive\nsubvert\nsubway\nsuccedaneum\nsucceed\nsucceeding\nsucceeding(a)\nsucces\nsuccesof\nsuccess\nsuccessful\nsuccessfully\nsuccessfulness\nsuccession\nsuccessive\nsuccessively\nsuccessiveness\nsuccessless\nsuccesslessness\nsuccessor\nsuccinct\nsuccinctly\nsuccinic\nsuccinylcholine\nsuccor\nsuccors\nsuccos\nsuccotash\nsuccuba\nsuccubus\nsucculence\nsucculent\nsuccumb\nsuccurrere\nsuccussion\nsuceptibleness\nsuch\nsuch(a)\nsuch(p)\nsuch-and-such\nsuchlike\nsuck\nsucker\nsucking\nsuckle\nsuckling\nsucralfate\nsucre\nsucrose\nsuction\nsuctorial\nsudan\nsudanese\nsudarium\nsudary\nsudatory\nsudden\nsuddenly\nsuddenness\nsudorific\nsuds\nsue\nsuede\nsuerte\nsuet\nsuetonius\nsuety\nsuey\nsuffer\nsufferable\nsufferance\nsufferd\nsufferer\nsuffering\nsuffice\nsufficiency\nsufficient\nsufficiently\nsufficit\nsuffix\nsufflation\nsuffocate\nsuffocating\nsuffocation\nsuffoccate\nsuffragan\nsuffrage\nsuffragette\nsuffragist\nsuffrance\nsuffrutescent\nsuffuse\nsuffused\nsuffusion\nsuffusive\nsufi\nsufiism\nsugar\nsugar-bush\nsugarberry\nsugarcandy\nsugarcane\nsugared\nsugargerry\nsugariness\nsugarless\nsugarloaf\nsugarplum\nsugary\nsuggest\nsuggestibility\nsuggestible\nsuggestio\nsuggestion\nsuggestive\nsui\nsuicidal\nsuicide\nsuidae\nsuigenetic\nsuillus\nsuis\nsuisse\nsuit\nsuitability\nsuitable\nsuite\nsuited\nsuiting\nsuitor\nsuivant\nsujet\nsukiyaki\nsuksdorfia\nsukur\nsula\nsulcate\nsulcated\nsulcus\nsulenness\nsulfacetamide\nsulfadiazine\nsulfamethazine\nsulfanilamide\nsulfapyridine\nsulfate\nsulfide\nsulfonate\nsulfonylurea\nsulfur\nsulidae\nsulindac\nsulk\nsulkily\nsulkiness\nsulks\nsulky\nsulla\nsullen\nsullenness\nsullivan\nsully\nsulphate\nsulphur\nsulphuretted\nsulphuric\nsultan\nsultana\nsultanate\nsultriness\nsultry\nsum\nsumac\nsumatra\nsumatran\nsumer\nsumerian\nsumerology\nsumless\nsumma\nsummarily\nsummarize\nsummary\nsummation\nsummational\nsummer\nsummer(a)\nsummercater\nsummerhouse\nsummerset\nsummery\nsummit\nsummity\nsummon\nsummons\nsummum\nsumo\nsump\nsumpter\nsumptuary\nsumptuous\nsumptuously\nsumtotal\nsun\nsun-drenched\nsun-dried\nsun-god\nsunbaked\nsunbather\nsunbeam\nsunbeams\nsunbelt\nsunbonnet\nsunburn\nsunburned\nsunburnt\nsunburst\nsundacarpus\nsundanese\nsunday\nsunder\nsundew\nsundial\nsundog\nsundown\nsundowner\nsundrops\nsundry\nsunfish\nsunflower\nsung\nsunglasses\nsunk\nsunken\nsunlamp\nsunless\nsunlight\nsunlit\nsunni\nsunniness\nsunnite\nsunny\nsunray\nsunrise\nsunrise(a)\nsunroof\nsuns\nsunscreen\nsunset\nsunset(a)\nsunshade\nsunshine\nsunspot\nsunstone\nsunstroke\nsunsuit\nsunt\nsuntrap\nsunup\nsuo\nsuos\nsup\nsupawn\nsuper\nsuperabat\nsuperable\nsuperabound\nsuperabundance\nsuperabundant\nsuperadd\nsuperaddition\nsuperaltation\nsuperannuated\nsuperannuation\nsuperb\nsuperbug\nsupercargo\nsupercharged\nsupercharger\nsupercherie\nsupercilious\nsuperciliousness\nsuperclass\nsuperconductivity\nsupercritical\nsuperego\nsupereminence\nsupereminent\nsupererogation\nsupererogatory\nsuperexcellence\nsuperexcellent\nsuperfamily\nsuperfatted\nsuperfecta\nsuperfecundation\nsuperfetation\nsuperficial\nsuperficiality\nsuperficially\nsuperficies\nsuperfine\nsuperfluence\nsuperfluitant\nsuperfluity\nsuperfluous\nsuperfluously\nsupergiant\nsuperhuman\nsuperimpose\nsuperimposed\nsuperincumbent\nsuperinduce\nsuperinfection\nsuperintend\nsuperintendence\nsuperintendent\nsuperior\nsuperior(p)\nsuperiority\nsuperjacent\nsuperjunction\nsuperlative\nsuperlatively\nsuperman\nsupermarket\nsupernal\nsupernatant\nsupernatural\nsupernaturalism\nsupernaturalist\nsupernormal\nsupernova\nsupernumerary\nsupernumernry\nsuperorder\nsuperordinate\nsuperphylum\nsuperphysical\nsuperplus\nsuperpose\nsuperposition\nsuperque\nsupersaturate\nsupersaturated\nsuperscript\nsuperscription\nsupersede\nsupersedure\nsupersensible\nsupersonic\nsuperstar\nsuperstition\nsuperstitione\nsuperstitions\nsuperstitious\nsuperstitiously\nsuperstratum\nsuperstring\nsuperstructure\nsupersymmetry\nsupertitle\nsupertonic\nsupervacaneous\nsupervene\nsupervention\nsupervise\nsupervised\nsupervision\nsupervisor\nsupervisory\nsupination\nsupine\nsupinely\nsupineness\nsuppeditate\nsupper\nsupperless\nsupping\nsupplant\nsupplanting\nsupple\nsupplejack\nsupplement\nsupplemental\nsupplementary\nsupplementation\nsupplesupple\nsuppletory\nsuppliant\nsupplicant\nsupplicate\nsupplication\nsupplicatory\nsupplier\nsupplies\nsupply\nsupport\nsupportance\nsupported\nsupporter\nsupporters\nsupporting\nsupportive\nsupposable\nsuppose\nsupposed\nsupposed(a)\nsupposed(p)\nsupposing\nsupposition\nsuppositional\nsuppositious\nsupposititious\nsuppositive\nsuppository\nsuppostion\nsuppress\nsuppressed\nsuppressio\nsuppression\nsuppressive\nsuppressor\nsuppuration\nsuppurative\nsupputation\nsuppute\nsupra\nsuprainfection\nsupralapsarian\nsupramundane\nsupranational\nsuprasegmental\nsupremacist\nsupremacy\nsupreme\nsupremely\nsur\nsurbase\nsurbate\nsurbated\nsurcease\nsurcharge\nsurcingle\nsurcoat\nsurd\nsurdity\nsure\nsure-handed\nsurefooted\nsurely\nsureness\nsurety\nsurf\nsurface\nsurface-active\nsurface-to-air\nsurfaces\nsurfacing\nsurfbird\nsurfboard\nsurfboat\nsurfeit\nsurfer\nsurficial\nsurfing\nsurfperch\nsurge\nsurgeon\nsurgeonfish\nsurgery\nsurgical\nsurgically\nsurgit\nsuricata\nsuricate\nsuriname\nsurly\nsurmise\nsurmount\nsurmountable\nsurmounted\nsurname\nsurnia\nsurpass\nsurpassing\nsurpassingly\nsurplice\nsurpliced\nsurplus\nsurplusage\nsurprise\nsurprised\nsurprisedly\nsurprising\nsurprisingly\nsurrealism\nsurrealist\nsurrebutter\nsurrejoinder\nsurrender\nsurrendering\nsurreptitious\nsurreptitiously\nsurreptititous\nsurreptitiuos\nsurrey\nsurrogate\nsurround\nsurrounded\nsurrounding\nsurroundings\nsursum\nsurtax\nsurtout\nsurveillance\nsurvene\nsurvey\nsurveying\nsurveyintrospection\nsurveyor\nsurvival\nsurvivance\nsurvive\nsurviving\nsurvivor\nsurya\nsus\nsusceptibility\nsusceptibiliy\nsusceptible\nsusceptive\nsusceptivity\nsuscipiency\nsuscipient\nsuscitate\nsuscitation\nsushi\nsuslik\nsuspect\nsuspected\nsuspend\nsuspended\nsuspendens\nsuspenders\nsuspense\nsuspension\nsuspensive\nsuspicio\nsuspicion\nsuspicions\nsuspicious\nsuspiciously\nsuspiciousness\nsuspiration\nsusquehanna\nsustain\nsustainability\nsustainable\nsustained\nsustaining\nsustenance\nsustentacular\nsustentation\nsusteritation\nsusurrant\nsusurrate\nsusurration\nsusurrous\nsutler\nsuttee\nsuture\nsuturing\nsuum\nsuva\nsuzerain\nsuzerainty\nsvalbard\nswab\nswabbing\nswad\nswaddle\nswaddling\nswag\nswagger\nswaggerer\nswaggering\nswagman\nswagsman\nswahili\nswain\nswainsona\nswale\nswallow\nswallow-tailed\nswami\nswamp\nswamped\nswampy\nswan\nswank\nswans\nswansea\nswap\nsward\nswarm\nswarming\nswart\nswarthy\nswartliness\nswash\nswashbuckler\nswashbuckling\nswashy\nswastika\nswat\nswatch\nswath\nswathe\nswathing\nswatter\nsway\nswaying\nswazi\nswaziland\nsweal\nswear\nswearer\nswearing\nsweat\nsweatband\nsweatbox\nsweater\nsweating\nsweatshirt\nsweatshop\nswede\nsweden\nswedenborgian\nswedish\nsweek\nsweep\nsweeper\nsweeping\nsweepingly\nsweepings\nsweepstake\nsweepstakes\nsweet\nsweet-faced\nsweetbread\nsweetbrier\nsweeten\nsweetened\nsweetening\nsweetest\nsweetheart\nsweetish\nsweetleaf\nsweetly\nsweetmeat\nsweetness\nsweets\nsweetscented\nsweetsop\nswell\nswelling\nswelter\nsweltered\nsweltering\nswept\nsweptback\nsweptwing\nswertia\nswerve\nswerving\nswietinia\nswift\nswifter\nswiftlet\nswiftly\nswiftness\nswig\nswill\nswim\nswimmer\nswimmeret\nswimming\nswimmingly\nswimsuit\nswindle\nswindler\nswine\nswineherd\nswing\nswinge\nswingeing\nswinger\nswinging\nswinish\nswink\nswipe\nswirl\nswish\nswishing\nswiss\nswitch\nswitch(a)\nswitch-hitter\nswitchblade\nswitchboard\nswitcheroo\nswitchman\nswithin\nswithins\nswitzerland\nswivel\nswiz\nswizzle\nswollen\nswoon\nswoop\nswop\nsword\nsword-cut\nswordbayonet\nswordfish\nswords\nswordshaped\nswordsman\nswordsmanship\nswordstick\nswordtail\nsworn\nswot\nsybarite\nsybaritical\nsybaritism\nsycamore\nsyce\nsyconium\nsycophancy\nsycophant\nsycophantic\nsydney\nsyenite\nsyllabary\nsyllabic\nsyllabically\nsyllabicate\nsyllabication\nsyllabicity\nsyllable\nsyllabled\nsyllables\nsyllabub\nsyllabus\nsyllepsis\nsyllogism\nsyllogistic\nsylph\nsylphic\nsylphid\nsylphlike\nsylvan\nsylvanite\nsylvanus\nsylviidae\nsylviinae\nsylvilagus\nsylvis\nsylvite\nsymbiosis\nsymbiotic\nsymbiotically\nsymbol\nsymbolatry\nsymbolic\nsymbolically\nsymbolism\nsymbolist\nsymbolization\nsymbolize\nsymbolizing\nsymmetric\nsymmetrical\nsymmetrically\nsymmetry\nsympathectomy\nsympathetic\nsympathetically\nsympathize\nsympathizer\nsympathizing\nsympathy\nsympatric\nsymphalangus\nsymphonic\nsymphonious\nsymphonize\nsymphonizing\nsymphony\nsymphoricarpos\nsymphyla\nsymphysis\nsymphytum\nsymplocaceae\nsymplocarpus\nsymploce\nsymplocus\nsymposium\nsymptom\nsymptomatic\nsymptomatically\nsymptomatology\nsynagogue\nsynagrops\nsynanceja\nsynapse\nsynapsid\nsynapsida\nsynapsis\nsynaptic\nsynaptomys\nsyncarpous\nsynchrocyclotron\nsynchroflash\nsynchromesh\nsynchronal\nsynchronic\nsynchronical\nsynchronism\nsynchronistical\nsynchronization\nsynchronize\nsynchronized\nsynchronous\nsynchronously\nsynchrotron\nsynchysis\nsynchytriaceae\nsynchytrium\nsynclinal\nsyncopated\nsyncopation\nsyncope\nsyncretic\nsyncretism\nsyncretistic\nsyncytium\nsyndactyly\nsyndetic\nsyndic\nsyndicalism\nsyndicate\nsyndication\nsyndrome\nsyne\nsynecdoche\nsynecdochic\nsynechia\nsynentognathi\nsynercus\nsyneresis\nsynergetic\nsynergism\nsynergist\nsynergistic\nsynergy\nsynesthesia\nsynesthetic\nsynetoisy/gr\nsyngenic\nsyngnathidae\nsyngnathus\nsyngonium\nsynizesis\nsynod\nsynodontidae\nsynonym\nsynonymist\nsynonymous\nsynonymously\nsynonymy\nsynopsis\nsynoptic\nsynovia\nsynovial\nsynovitis\nsyntactic\nsyntactically\nsyntagma\nsyntax\nsyntaxis\nsyntectic\nsyntectical\nsyntexis\nsynthesis\nsynthesizer\nsynthetic\nsynthetically\nsynthetism\nsyphilis\nsyphilitic\nsyracuse\nsyria\nsyrian\nsyringa\nsyringe\nsyrinx\nsyrrhaptes\nsyrt\nsyrtis\nsyrup\nsyrupy\nsyrus\nsystem\nsystematic\nsystematically\nsystematics\nsystematization\nsystematize\nsystematized\nsystemic\nsystole\nsyzygium\nsyzygy\nszechwan\nt\u0003tonnement\nt\u0003tonner\nt\u0003tons\nt\nt-bar\nt-junction\nt-man\nt-square\nta\ntab\ntabanidae\ntabard\ntabasco\ntabble\ntabbouleh\ntabby\ntabefaction\ntabernacle\ntabernaemontana\ntabes\ntabetic\ntabi\ntabid\ntabita\ntablature\ntable\ntableau\ntablecloth\ntablefork\ntableland\ntablemate\ntables\ntablespoon\ntablet\ntabletop\ntablets\ntableware\ntablier\ntablinum\ntabloid\ntaboe\ntaboo\ntabor\ntabora\ntaboret\ntaborin\ntabouret\ntabourine\ntabret\ntabriz\ntabula\ntabular\ntabulate\ntabulation\ntacca\ntaccaceae\ntace\ntacent\ntachinidae\ntachistoscope\ntachogram\ntachograph\ntachometer\ntachycardia\ntachyglossidae\ntachyglossus\ntachygraphy\ntachylite\ntachymeter\ntachypleus\ntacit\ntacitis\ntacitly\ntacitum\ntaciturn\ntaciturnity\ntacitus\ntack\ntackey\ntackle\ntackler\ntackling\ntacky\ntaco\ntacoma\ntaconite\ntact\ntactful\ntactfully\ntactic\ntactical\ntactically\ntactician\ntactics\ntactile\ntactility\ntaction\ntactless\ntactlessly\ntactlessness\ntactual\ntactually\ntad\ntadarida\ntadorna\ntadpole\ntaedium\ntaegu\ntael\ntaenia\ntaeniate\ntaeniform\ntaeniidae\ntaenioid\ntaffeta\ntaffrail\ntaffy\ntaft\ntag\ntagalog\ntagalong\ntagasaste\ntageteste\ntagtail\ntaguan\ntagus\ntahiti\ntahitian\ntai\ntaichung\ntaidera\ntaiga\ntail\ntail(a)\ntailback\ntailed\ntailgate\ntaillight\ntailor\ntailorbird\ntailored\ntailoring\ntailpiece\ntailpipe\ntailrace\ntails\ntailspin\ntailstock\ntailwind\ntaint\ntainted\ntaintless\ntainture\ntaipan\ntaipei\ntaiwan\ntaiwanese\ntajiki\ntajikistan\ntajo\ntaka\ntake\ntake-home\ntake-up\ntakedown\ntakelma\ntaken\ntakeoff\ntakeout\ntakeover\ntaker\ntakes\ntaketime\ntakilman\ntakin\ntaking\ntakk\ntala\ntalapoin\ntalbotype\ntalc\ntalcum\ntale\ntalebearer\ntalent\ntalented\ntalentlessness\ntalents\ntales\ntalesman\ntalia\ntalinum\ntalionic\ntalionis\ntaliped\ntalipedic\ntalipes\ntalipot\ntalisman\ntalismanic\ntalk\ntalkative\ntalkativeness\ntalked\ntalker\ntalking\ntalking(a)\ntall\ntall(a)\ntallage\ntallahassee\ntaller\ntallgrass\ntallies\ntallinn\ntallish\ntallith\ntallness\ntallow\ntalls\ntally\ntallyho\ntallyman\ntalma\ntalmud\ntalon\ntalons\ntalpidae\ntalus\ntam\ntamable\ntamale\ntamandua\ntamarau\ntamaricaceae\ntamarin\ntamarind\ntamarindus\ntamarisk\ntamarix\ntamasha\ntambala\ntambour\ntambourine\ntame\ntamed\ntameless\ntamely\ntamen\ntameness\ntamer\ntamerlane\ntamias\ntamiasciurus\ntamil\ntaming\ntammany\ntammuz\ntammy\ntamoshanter\ntamp\ntampa\ntamper\ntampering\ntampon\ntamtam\ntamus\ntan\ntanacetum\ntanager\ntanbark\ntandem\ntanekaha\ntang\ntanganyika\ntangelo\ntangency\ntangent\ntangential\ntangentially\ntangere\ntangerine\ntangibility\ntangible\ntangibly\ntangier\ntangle\ntanglebush\ntangled\ntango\ntangram\ntangre\ntank\ntanka\ntankage\ntankard\ntanker\ntanned\ntannenberg\ntanner\ntannery\ntannic\ntannin\ntannoy\ntanoan\ntansy\ntant\ntantaene\ntantalite\ntantalization\ntantalize\ntantalizing\ntantalizingly\ntantalum\ntantalus\ntantamount\ntantara\ntantas\ntanti\ntantilla\ntantivy\ntanto\ntantra\ntantric\ntantrism\ntantrist\ntantrum\ntantrums\ntantulus\ntanzania\ntanzanian\ntao\ntaoism\ntaoist\ntaos\ntap\ntapa\ntape\ntaped\ntapenade\ntaper\ntapered\ntapering\ntapestried\ntapestry\ntapeworm\ntaphephobia\ntapinois\ntapioca\ntapir\ntapiridae\ntapirus\ntapis\ntapped\ntappet\ntapping\ntaproot\ntaps\ntar\ntara\ntaracahitian\ntaradiddle\ntarahumara\ntarantella\ntarantism\ntarantula\ntarawa\ntaraxacum\ntarboosh\ntardi\ntardigrada\ntardigrade\ntardiloquence\ntardiness\ntardy\ntare\ntares\ntarget\ntarget-hunting\ntaricha\ntariff\ntarmacadam\ntarn\ntarnish\ntaro\ntarot\ntarp\ntarpan\ntarpaulin\ntarpon\ntarragon\ntarred-and-feathered(a)\ntarrietia\ntarry\ntarrying\ntarsal\ntarsier\ntarsiidae\ntarsioidea\ntarsitis\ntarsius\ntarsus\ntart\ntartan\ntartane\ntartar\ntartaran\ntartaric\ntartars\ntartarus\ntartfufe\ntartlet\ntartly\ntartness\ntartrate\ntartufe\ntartuffe\ntartuffish\ntarweed\ntarwood\ntarzan\ntashkent\ntashmit\ntask\ntaskmaster\ntaskmistress\ntaskthankless\ntasmania\ntasmanian\ntassel\ntasseled\ntasset\ntaste\ntastebud\ntasteful\ntastefully\ntastefulness\ntasteless\ntastelessly\ntastelessness\ntaster\ntastes\ntastily\ntasting\ntasty\ntat\ntatahumara\ntatar\ntatouay\ntatter\ntatterdemalion\ntattered\ntatters\ntattersalls\ntatting\ntattle\ntattler\ntattletale\ntattoo\ntau\ntaught\ntaunt\ntauntingly\ntauon\ntauromachy\ntaurotragus\ntaurus\ntaut\ntautly\ntautog\ntautoga\ntautogolabrus\ntautology\ntautophony\ntavern\ntaw\ntawdriness\ntawdry\ntawniness\ntawny\ntawse\ntax\ntax-exempt\ntax-increase\ntaxability\ntaxable\ntaxaceae\ntaxales\ntaxes\ntaxi\ntaxicab\ntaxicoach\ntaxidea\ntaxidermist\ntaxidermy\ntaxidriver\ntaximeter\ntaxis\ntaxiway\ntaxodiaceae\ntaxodium\ntaxonomic\ntaxonomically\ntaxonomy\ntaxopsida\ntaxpayer\ntaxpaying\ntaxus\ntay\ntayalic\ntayassu\ntayassuidae\ntaylor\ntayra\ntazza\ntb\ntbilisi\ntc\ntd\nte\ntea\ntea-strainer\nteaberry\nteacake\nteach\nteach-in\nteachable\nteacher\nteachership\nteaching\nteacup\nteafight\nteak\nteakettle\nteal\nteam\nteammate\nteamster\nteamwork\nteaparty\nteapot\nteapoy\ntear\ntearaway\nteardrop\ntearful\ntearfully\ntearing\ntearless\ntears\nteary\ntease\nteased\nteasel\nteaser\nteashop\nteasing\nteaspoon\nteat\nteatable\ntebet\ntechnetium\ntechnica\ntechnical\ntechnicality\ntechnically\ntechnician\ntechnicolor\ntechnique\ntechnobabble\ntechnocracy\ntechnocrat\ntechnological\ntechnologically\ntechnology\ntechy\ntecophilaeacea\ntecta\ntectaria\ntectiform\ntectona\ntectonic\ntectonics\ntecum\ntecumseh\nted\nteddy\ntedge\ntedious\ntediousness\ntedium\ntee\nteem\nteemful\nteeming\nteemless\nteen\nteens\nteensy\nteeny\nteeter\nteeth\nteethe\nteetotal\nteetotaler\nteetotaling\nteetotalism\nteetotalist\nteetotum\nteff\nteflon\nteg\ntegatur\ntegendo\ntegucigalpa\ntegular\ntegument\ntegumentary\ntehee\nteheran\nteichopsia\nteiidae\nteju\ntekel\ntektite\ntel\ntelanthera\ntelecast\ntelecommunication\nteleconference\ntelegnosis\ntelegnostic\ntelegonous\ntelegony\ntelegram\ntelegraph\ntelegrapher\ntelegraphese\ntelegraphic\ntelegraphically\ntelegraphy\ntelekinesis\ntelemarketing\ntelemeter\ntelemetered\ntelemetry\ntelencephalon\nteleological\nteleologist\nteleology\nteleostei\ntelepathic\ntelepathist\ntelepathy\ntelephone\ntelephonic\ntelephotograph\ntelephotography\nteleprompter\ntelerobotics\ntelescope\ntelescoped\ntelescopic\ntelescopically\ntelesm\ntelethermometer\nteletypewriter\ntelevangelist\ntelevision\ntell\nteller\ntellima\ntelling\ntellingly\ntelltale\ntellurian\ntelluric\ntelluride\ntellurium\ntellus\ntelocentric\ntelopea\ntelophase\nteloque\ntelosporidia\ntelpher\ntelpherage\ntelugu\ntelum\ntem\ntemerarious\ntemeritas\ntemerity\ntemnit\ntemnospondyli\ntemp\ntemper\ntempera\ntemperament\ntemperamental\ntemperamentally\ntemperance\ntemperate\ntemperately\ntemperateness\ntemperature\ntempered\ntemperet\ntempering\ntemperize\ntempest\ntempestas\ntempestivity\ntempesttossed\ntempestuous\ntempestuousness\ntempete\ntempi\ntemplar\ntemplate\ntemple\ntempletonia\ntempo\ntempora\ntemporal\ntemporality\ntemporally\ntemporalty\ntemporarily\ntemporariness\ntemporary\ntempore\ntempori\ntemporis\ntemporization\ntemporize\ntemporizer\ntemps\ntempt\ntemptable\ntemptation\ntempter\ntempting\ntempura\ntempus\ntemulency\ntemulent\ntemulentive\nten\nten-spot\ntenable\ntenacious\ntenacity\ntenaculum\ntenancity\ntenancy\ntenant\ntenantless\ntenantry\ntenants\ntenax\ntench\ntend\ntendence\ntendency\ntendentious\ntendentiously\ntendentiousness\ntender\ntenderconscienced\ntenderfoot\ntendergreen\ntenderhearted\ntenderization\ntenderized\ntenderizer\ntenderloin\ntenderly\ntenderness\ntending\ntendinitis\ntendinous\ntendon\ntendril\nteneatis\ntenebrionidae\ntenebrious\ntenebrous\ntenement\ntenements\ntenens\ntenentur\nteneris\ntenet\ntenets\ntenez\ntenfold\ntennessean\ntennessee\ntennis\ntennyson\ntenon\ntenor\ntenoretic\ntenoroon\ntenorroutine\ntenosynovitis\ntenpence\ntenpenny\ntenpin\ntenpins\ntenpounder\ntenrec\ntenrecidae\ntense\ntensely\ntenses\ntensile\ntensimeter\ntensiometer\ntension\ntensional\ntensionless\ntensor\ntensure\ntent\ntent-fly\ntentacle\ntentacled\ntentacular\ntentaculata\ntentanda\ntentaris\ntentative\ntentatively\ntente\ntented\ntenter\ntenterhook\ntenterhooks\ntenth\ntenthly\ntenthredinidae\ntenths\ntentmaker\ntentorium\ntents\ntenue\ntenuere\ntenuity\ntenuous\ntenuously\ntenure\ntenured\ntenus\ntepee\ntepefaction\ntephramancy\ntephrosia\ntepid\ntepidness\ntequila\nter\ntera\nterabyte\nteratiology\nteratism\nteratogen\nteratogenic\nteratology\nterbium\nterce\ntercentennial\nterceron\nterebella\nterebellidae\nterebinth\nterebration\nteredinidae\nteredo\nterence\nterencel\nteres\nterete\ntergal\ntergiversation\ntergo\nteriyaki\nterm\ntermagant\ntermes\nterminable\nterminal\nterminally\nterminate\nterminated\ntermination\nterminative\ntermine\nterminer\ntermini\nterminological\nterminology\nterminus\ntermite\ntermitidae\ntermless\nterms\ntern\nternary\nternate\nternion\nterpene\nterpsichore\nterpsichorean\nterra\nterrace\nterrain\nterrapene\nterrapin\nterraqueous\nterre\nterrence\nterrene\nterreous\nterrestrial\nterrestrious\nterret\nterrible\nterribly\nterrier\nterrific\nterrify\nterrine\nterritorial\nterritoriality\nterritorialization\nterritorially\nterritory\nterroe\nterror\nterror-stricken\nterrorem\nterrorism\nterrorist\nterrorization\nterrorize\nterrors\nterrorstricken\nterry\ntersanctus\nterse\nterseclose\nterseness\ntertian\ntertiary\ntertigravida\ntertium\ntertiun\ntertry\ntertullian\nterzetto\ntesla\ntesselated\ntessellated\ntessellation\ntessera\ntesserae\ntest\ntesta\ntestacea\ntestacean\ntestaceology\ntestaceous\ntestament\ntestamentary\ntestamur\ntestate\ntestator\ntestatrix\nteste\ntested\ntestee\ntester\ntesticular\ntestification\ntestifier\ntestify\ntestigo\ntestily\ntestimonial\ntestimony\ntestiness\ntesting\ntestis\ntestosterone\ntestudinidae\ntestudo\ntesty\ntet\ntetanus\ntetchily\ntetchy\ntete\ntete-a-tete\nteteatete\nteth\ntether\ntetherball\ntethered\ntethys\ntetigisti\ntetigit\ntetra\ntetracaine\ntetrachloride\ntetrachord\ntetraclinis\ntetract\ntetractic\ntetractinal\ntetracycline\ntetrad\ntetrafluoroethylene\ntetragon\ntetragonal\ntetragonia\ntetragonurus\ntetragram\ntetragrammaton\ntetrahalide\ntetrahedral\ntetrahedron\ntetrahymena\ntetralogy\ntetramerous\ntetrameter\ntetrametric\ntetraneuris\ntetranychidae\ntetrao\ntetraodontidae\ntetraonidae\ntetrapod\ntetrapturus\ntetrarch\ntetrasaccharide\ntetrasporangium\ntetraspore\ntetravalent\ntetrode\ntetter\ntettigoniidae\ntetto\nteucrium\nteuton\nteutonic\ntexan\ntexas\ntext\ntextbook\ntextile\ntextual\ntextuary\ntextural\ntexture\ntextured\ntextures\ntf\ntg\nth\u0003tre\nth\nthai\nthailand\nthais\nthalamocortical\nthalamus\nthalarctos\nthalassemia\nthalassic\nthalassoma\nthalia\nthaliacea\nthalictrum\nthalidomide\nthallium\nthallophyta\nthallophyte\nthallophytic\nthallus\nthalweg\nthames\nthamnophilus\nthamnophis\nthan\nthana\nthanatophobia\nthane\nthaneship\nthank\nthankful\nthankfully\nthankfulness\nthankless\nthanklessness\nthanks\nthanksgiving\nthankyemaam\nthat\nthatch\nthatcher\nthats\nthaumatolatry\nthaumatrope\nthaumaturgist\nthaumaturgy\nthaw\nthawed\nthe\u0003tre\nthe\nthea\ntheaceae\ntheanthropism\nthearchy\ntheater\ntheatre\ntheatric\ntheatrical\ntheatrically\ntheatricals\ntheban\nthebe\nthebes\ntheca\nthecodont\nthecodontia\nthee\ntheft\nthegoose\ntheir\ntheirs\ntheism\ntheist\ntheistic\nthelephoraceae\nthelypteridaceae\nthelypteris\nthem\nthematic\nthematically\ntheme\nthemis\nthemselves\nthen\nthen(a)\nthence\nthenceforth\nthenceforward\ntheobroma\ntheocracy\ntheocratic\ntheodolite\ntheogony\ntheolgian\ntheologian\ntheological\ntheologically\ntheologicum\ntheologist\ntheologue\ntheology\ntheomancy\ntheopathy\ntheophany\ntheophneusted\ntheophobist\ntheophrastaceae\ntheopneustic\ntheopneusty\ntheorbo\ntheorem\ntheoretical\ntheoretically\ntheories\ntheorist\ntheorization\ntheorize\ntheory\ntheory-based\ntheosophical\ntheosophist\ntheosophy\ntherapeutic\ntherapeutically\ntherapeutics\ntheraphosidae\ntherapist\ntherapsid\ntherapsida\ntherapy\nthere\nthereabout\nthereabouts\nthereafter\nthereby\ntherefor\ntherefore\ntherein\nthereinafter\nthereness\nthereof\nthereon\ntheres\nthereto\ntheretofore\nthereunder\nthereupon\ntherewith\ntherewithal\ntheriac\ntheridiidae\ntherm\nthermal\nthermally\nthermic\nthermidor\nthermion\nthermionic\nthermionics\nthermistor\nthermoacidophile\nthermobia\nthermocouple\nthermodynamic\nthermodynamically\nthermodynamics\nthermoelectric\nthermoelectricity\nthermograph\nthermohydrometer\nthermohydrometric\nthermojunction\nthermology\nthermometer\nthermometric\nthermometrograph\nthermometry\nthermonuclear\nthermopile\nthermoplastic\nthermopsis\nthermopylae\nthermoreceptor\nthermos\nthermoscope\nthermosetting\nthermosphere\nthermostat\nthermostated\nthermostatic\nthermostatically\nthermostatics\nthermotics\ntheropod\ntheropoda\nthersites\nthesaurus\nthese\ntheseus\nthesis\nthespesia\nthespian\nthespis\nthessalia\nthessalonika\ntheta\nthetis\ntheurgist\ntheurgy\nthevetia\nthews\nthey\nthg\nthiazine\nthick\nthickcoming\nthicken\nthickened\nthickening\nthickens\nthicket\nthickets\nthickeyd\nthickhead\nthickly\nthickness\nthickribbed\nthickset\nthickskinned\nthickskull\nthickskulled\nthickspread\nthief\nthielavia\nthieve\nthievery\nthieves\nthieving\nthieving(a)\nthievish\nthievishly\nthievishness\nthigh\nthill\nthimble\nthimbleful\nthimblerig\nthimblerigger\nthimbleweed\nthimerosal\nthin\nthine\nthing\nthings\nthingumbob\nthingummy\nthink\nthinkable\nthinker\nthinkest\nthinking\nthinks\nthinly\nthinness\nthinskinned\nthiobacillus\nthiobacteria\nthiobacteriaceae\nthiocyanate\nthioguanine\nthiopental\nthioridazine\nthiouracil\nthird\nthird(a)\nthird-rate\nthird-rater\nthirdly\nthirds\nthirgyone\nthirst\nthirstily\nthirstiness\nthirsty\nthirteen\nthirteenth\nthirties\nthirtieth\nthirty\nthirtynine\nthis\nthistle\nthistledown\nthistly\nthither\nthlaspi\ntho\nthole\nthomas\nthomism\nthomomys\nthompson\nthomson\nthong\nthor\nthorax\nthoreau\nthoreauvian\nthoriated\nthorite\nthorium\nthorn\nthornbill\nthornless\nthorns\nthorny\nthoro\nthorough\nthoroughbass\nthoroughbred\nthoroughfare\nthoroughgoing\nthoroughly\nthoroughness\nthoroughpaced\nthorp\nthorshavn\nthortveitite\nthose\nthoth\nthou\nthough\nthought\nthoughtful\nthoughtfully\nthoughtfulness\nthoughtless\nthoughtlessly\nthoughtlessness\nthoughts\nthousand\nthousand-fold\nthousandth\nthrace\nthracian\nthraco-phrygian\nthraldom\nthrall\nthrash\nthrasher\nthrashing\nthraso\nthrasonic\nthraupidae\nthread\nthreadbare\nthreadfin\nthreadfish\nthreadlike\nthreat\nthreaten\nthreatened\nthreatening\nthree\nthree-cornered\nthree-d\nthree-decker\nthree-dimensional\nthree-dimensionality\nthree-figure\nthree-fourths\nthree-hitter\nthree-lane\nthree-legged\nthree-piece\nthree-ply\nthree-quarter\nthree-wheeled\nthreecolor\nthreefold\nthreepence\nthreepenny\nthreescore\nthreetailed\nthrene\nthrenetic\nthrenody\nthreonine\nthresh\nthresher\nthreshing\nthreshold\nthreskiornis\nthreskiornithidae\nthrice\nthricetold\nthrid\nthrift\nthriftily\nthriftiness\nthriftless\nthriftlessly\nthriftlessness\nthriftshop\nthrifty\nthrill\nthrilled\nthriller\nthrillful\nthrilling\nthrinax\nthripidae\nthrips\nthrive\nthriving\nthroat\nthroated\nthroats\nthroatwort\nthroaty\nthrob\nthrobbing\nthroe\nthroes\nthrombasthenia\nthrombectomy\nthrombin\nthrombocytopenia\nthromboembolism\nthrombolytic\nthrombophlebitis\nthromboplastin\nthrombosed\nthrombosis\nthrombus\nthrone\nthroned\nthrong\nthronged\nthrostle\nthrottle\nthrough\nthrough(a)\nthroughbred\nthroughout\nthroughput\nthrow\nthrow-in\nthrow-weight\nthrowaway\nthrowaway(p)\nthrower\nthrown\nthrowster\nthrum\nthrush\nthrust\nthryothorus\nthucydides\nthud\nthug\nthuggee\nthuggery\nthuggism\nthuja\nthujopsis\nthule\nthulium\nthumb\nthumbed\nthumbhole\nthumbnail\nthumbs\nthumbscrew\nthumbstall\nthumbtack\nthump\nthumper\nthumping\nthunbergia\nthunder\nthunderbird\nthunderbold\nthunderbolt\nthunderclap\nthunderhead\nthunderheaded\nthundering\nthunderous\nthunders\nthundershower\nthunderstorm\nthunderstruck\nthundertube\nthundery\nthunk\nthunnus\nthurible\nthurifer\nthuriferous\nthurification\nthuringia\nthurlow\nthursday\nthus\nthwack\nthwart\nthy\nthylacine\nthylacinus\nthylogale\nthyme\nthymelaeaceae\nthymine\nthymol\nthymus\nthyreophora\nthyroglobulin\nthyroid\nthyroidectomy\nthyronine\nthyroprotein\nthyrotoxic\nthyrotrophin\nthyroxine\nthyrsopteris\nthysanocarpus\nthysanopter\nthysanoptera\nthysanura\nthyself\nti\ntiamat\ntianjin\ntiara\ntiarella\ntiber\ntibet\ntibetan\ntibeto-burman\ntibi\ntibia\ntibial\ntibialis\ntibicen\ntibs\ntibullus\ntic\ntichodroma\nticino\ntick\nticker\ntickertape\nticket\nticket-of-leave\nticking\ntickle\ntickled\ntickler\ntickling\nticklish\nticktack\nticktacktoe\nticktock\ntidal\ntidbit\ntiddlywinks\ntide\ntidemark\ntides\ntidewater\ntideway\ntidily\ntidiness\ntidings\ntidy\ntidyneat\ntidytips\ntie\ntie-on\ntiebeam\ntiebreaker\ntied\ntied(p)\ntien\ntien-pao\ntiene\ntienoscope\ntier\ntierce\ntiercel\nties\ntiff\ntiffin\ntigella\ntiger\ntigerish\ntight\ntight-fitting\ntight-knit\ntighten\ntightened\ntightening\ntightly\ntightness\ntightrope\ntights\ntiglon\ntigress\ntigris\ntijuana\ntike\ntilapia\ntilbury\ntilde\ntile\ntiled\ntilefish\ntilia\ntiliaceae\ntiling\ntiliomycetes\ntill\ntillage\ntillandsia\ntilled\ntiller\ntilletia\ntilletiaceae\ntilling\ntilmus\ntilpah\ntilt\ntilted\ntilth\ntilting\ntiltyard\ntimalia\ntimaliidae\ntimbal\ntimbale\ntimber\ntimber-framed\ntimbered\ntimberland\ntimbre\ntimbrel\ntimbres\ntimbrology\ntimbuktu\ntime\ntime-ball\ntime-fuse\ntime-honored\ntime-out\ntime-switch\ntimecard\ntimed\ntimeful\ntimehonored\ntimekeeper\ntimekeeping\ntimeless\ntimeliness\ntimely\ntimens\ntimeo\ntimepiece\ntimepleaser\ntimer\ntimes\ntimeserver\ntimeserving\ntimesion\ntimetable\ntimework\ntimeworn\ntimid\ntimidity\ntiming\ntimist\ntimocracy\ntimon\ntimor\ntimorous\ntimorously\ntimothy\ntimpani\ntimucu\ntin\ntinamidae\ntinamiformes\ntinamou\ntinca\ntinct\ntinctorial\ntincture\ntinctured\ntinder\ntinderbox\ntine\ntinea\ntineid\ntineidae\ntineoid\ntineoidea\ntineola\ntinfoil\nting\ntinge\ntingent\ntingible\ntingidae\ntingle\ntingling\ntininess\ntink\ntinker\ntinkering\ntinkers\ntinkle\ntinkling\ntinned\ntinnient\ntinning\ntinnitus\ntinny\ntinsel\ntinsmith\ntint\ntintack\ntintamarre\ntinted\ntinting\ntintinnabulary\ntinware\ntiny\ntip\ntip-off\ntip-top\ntip-up\ntipcat\ntipped\ntippet\ntipple\ntippler\ntippybob\ntips\ntipstaff\ntipster\ntipsy\ntiptoe\ntiptop\ntipu\ntipuana\ntipulidae\ntirade\ntirailleur\ntirana\ntiranno\ntire\ntired\ntiredly\ntiredness\ntirer\ntiresias\ntiresome\ntiring\ntis\ntisane\ntishri\ntisiphone\ntisk\ntissue\ntit\ntitan\ntitaness\ntitanic\ntitanium\ntitanosaur\ntitanosauridae\ntitanosaurus\ntitbit\ntiter\ntithe\ntithing\ntithingman\ntiti\ntitian\ntitillate\ntitillating\ntitillation\ntitillative\ntitivate\ntitle\ntitled\ntitlepage\ntitmouse\ntitration\ntitter\ntittle\ntittletattle\ntitubancy\ntitubate\ntitubation\ntitular\ntitus\ntiu\ntivoli\ntizzy\ntj\ntk\ntl\ntlingit\ntmesis\ntnt\nto\ntoad\ntoad-in-the-hole\ntoadeater\ntoadeating\ntoadfish\ntoadflax\ntoadstool\ntoady\ntoast\ntoasted\ntoaster\ntoasting\ntoastmaster\ntoastrack\ntobacco\ntobago\ntobagonian\ntobbaconist\ntobe\ntoboggan\ntobogganing\ntobogganist\ntoby\ntoccata\ntocharian\ntocogony\ntocology\ntocsin\ntod\ntoda\ntoday\ntoddle\ntoddler\ntoddy\ntodea\ntodidae\ntodo\ntodus\ntody\ntoe\ntoe-in\ntoea\ntoecap\ntoed\ntoehold\ntoeless\ntoenail\ntoes\ntoetoe\ntoff\ntofieldia\ntoft\ntoga\ntogavirus\ntogether\ntogetherness\ntogged\ntoggery\ntoggle\ntogo\ntogolese\ntogs\ntoil\ntoiler\ntoilet\ntoileth\ntoiletry\ntoilette\ntoils\ntoilsom\ntoilsome\ntoilworn\ntoimeme\ntoity\ntokamak\ntokay\ntoke\ntoken\ntokyo\ntolazamide\ntolbutamide\ntold\ntolderolloll\ntoledo\ntolerable\ntolerably\ntolerance\ntolerant\ntolerantly\ntolerate\ntolerated\ntoleration\ntoll\ntollbooth\ntollboth\ntollenda\ntoller\ntollgate\ntolling\ntollitur\ntollkeeper\ntolmiea\ntolstoy\ntolu\ntoluene\ntolypeutes\ntom\ntomahawk\ntomalley\ntomatillo\ntomato\ntomb\ntombac\ntombe\ntomber\ntombigbee\ntombola\ntomboy\ntomcat\ntome\ntomentose\ntomentum\ntomfool\ntomfoolery\ntomistoma\ntommy\ntomograph\ntomorrow\ntompion\ntomtate\ntomtit\ntomtom\nton\ntonal\ntonality\ntone\ntone-deaf\ntoned\ntoneless\ntonelessly\ntoner\ntong\ntonga\ntongan\ntongs\ntongu\ntongue\ntongued\ntonguefish\ntongueflower\ntongueless\ntonguep\ntongues\ntonguetied\ntonic\ntonicity\ntonight\ntonjon\ntonnage\ntons\ntonsil\ntonsilitis\ntonsillectomy\ntonsillitis\ntonsils\ntonsorial\ntonsure\ntonsured\ntontine\ntonue\ntony\ntoo\ntool\ntoolbox\ntooling\ntoolmaker\ntools\ntoona\ntoot\ntooth\ntoothache\ntoothbrush\ntoothed\ntoothful\ntoothless\ntoothlike\ntoothpaste\ntoothpick\ntoothsome\ntoothy\ntootle\ntoots\ntop\ntop(a)\ntop-down\ntop-flight\ntop-heavy\ntop-secret\ntopaz\ntope\ntopek\ntopeka\ntoper\ntopful\ntopgallant\ntopheavy\ntophet\ntopi\ntopiary\ntopic\ntopical\ntoping\ntopknot\ntopless\ntopmast\ntopminnow\ntopmost\ntopognosia\ntopographical\ntopographically\ntopography\ntopolatry\ntopology\ntopped\ntopping\ntopple\ntoppletopple\ntoppling\ntops\ntopsail\ntopsails\ntopside\ntopsoil\ntopspin\ntopsy\ntopsyturvy\ntor\ntorah\ntorch\ntorchbearer\ntorchlight\ntorero\ntories\ntorment\ntormenter\ntormenting\ntormentor\ntormes\ntormina\ntorminous\ntorn\ntornado\ntoroid\ntoroidal\ntoronto\ntorose\ntorpedinidae\ntorpediniformes\ntorpedinous\ntorpedo\ntorpedoboat\ntorpedocatcher\ntorpedodestroyer\ntorpescence\ntorpescent\ntorpid\ntorpidity\ntorpids\ntorpor\ntorporific\ntorque\ntorr\ntorrefaction\ntorrefy\ntorrent\ntorrential\ntorrents\ntorreya\ntorricelli\ntorrid\ntorridity\ntorsion\ntorso\ntort\ntorte\ntortellini\ntorticollis\ntortile\ntortilla\ntortious\ntortive\ntortoise\ntortoiseshell\ntortricid\ntortricidae\ntortuosity\ntortuous\ntortuousinsidious\ntortuously\ntorture\ntortured\ntorturer\ntortus\ntorulose\ntorus\ntorvity\ntorvous\ntory\ntosk\ntoss\ntoss-up\ntossing\ntost\ntostada\ntot\ntotal\ntotaled\ntotalitarian\ntotality\ntotalizator\ntotalizer\ntotally\ntotalness\ntotara\ntote\ntotem\ntotemic\ntotemism\ntotidem\ntotient\ntoties\ntotis\ntotitive\ntoto\ntotter\ntottering\ntotus\ntoucan\ntoucanet\ntoucb\ntouch\ntouch-typist\ntouchback\ntouchd\ntouchdown\ntouched\ntouches\ntouchily\ntouching\ntouchline\ntouchstone\ntouchwood\ntouchy\ntough\ntough-minded\ntoughly\ntoughness\ntoujours\ntoupee\ntoupeed\ntour\ntouraco\ntourism\ntourist\ntouristed\ntourmaline\ntournament\ntournay\ntournedos\ntourner\ntourney\ntourniquet\ntournure\ntous\ntouse\ntousle\ntout\ntoute\ntouter\ntow\ntowage\ntoward\ntowardly\ntowards\ntowars\ntowel\ntoweling\ntower\ntowering\ntowhee\ntowhelp\ntowith\ntowline\ntown\ntownee\ntownhouse\ntownie\ntownsendia\ntownship\ntownsman\ntowpath\ntowrow\ntowzle\ntoxemia\ntoxic\ntoxicity\ntoxicodendron\ntoxicological\ntoxicologist\ntoxicology\ntoxiferous\ntoxin\ntoxophilite\ntoxostoma\ntoxotes\ntoxotidae\ntoy\ntoyon\ntoyshop\ntr\ntra-la\ntrabeated\ntracasserie\ntrace\ntraceable\ntracer\ntracery\ntraces\ntrachea\ntracheal\ntracheid\ntracheitis\ntrachelospermum\ntracheocele\ntracheophyta\ntracheostomy\ntrachinotus\ntrachipteridae\ntrachipterus\ntrachodon\ntrachoma\ntrachurus\ntracing\ntrack\ntracked\ntracker\ntrackless\ntracks\ntract\ntractability\ntractable\ntractarian\ntractarianism\ntractate\ntractation\ntractile\ntractility\ntraction\ntractive\ntractor\ntrad\ntrade\ntrade(a)\ntrade-in\ntrade-last\ntradecraft\ntraded\ntrademark\ntrademarked\ntrader\ntrades\ntradescantia\ntradesfolk\ntradesman\ntradespeople\ntrading\ntradition\ntraditional\ntraditionalism\ntraditionalist\ntraditionalistic\ntraditionally\ntraditionary\ntraduce\ntraducement\ntraducer\ntrafalgar\ntraffic\ntragacanth\ntragalism\ntragalist\ntragedian\ntragedienne\ntragedy\ntragelaphus\ntragic\ntragical\ntragically\ntragicomedy\ntragicomic\ntragopan\ntragopogon\ntragulidae\ntragulus\ntrahit\ntrail\ntrailblazer\ntrailer\ntrailing\ntrain\ntrainband\ntrainbearer\ntrained\ntrainee\ntraineeship\ntrainer\ntraining\ntrainload\ntrainman\ntraipse\ntrait\ntraiter\ntraitor\ntraitorous\ntraitors\ntraitress\ntraits\ntrajection\ntrajectory\ntralatitious\ntralineate\ntralucent\ntram\ntramline\ntrammel\ntrammels\ntramontane\ntramp\ntramper\ntramping\ntrample\ntrampled\ntrampoline\ntramway\ntrance\ntrancelike\ntranchant\ntranquil\ntranquility\ntranquilization\ntranquilize\ntranquilizer\ntranquillity\ntranquillization\ntranquillize\ntranquilly\ntrans\ntrans(a)\ntransact\ntransaction\ntransactions\ntransalpine\ntransaminase\ntransamination\ntransanimation\ntransatlantic\ntranscalency\ntranscend\ntranscendence\ntranscendency\ntranscendent\ntranscendental\ntranscendentalism\ntranscendentalist\ntranscendentally\ntranscendentalplus\ntranscolate\ntranscontinental\ntranscribe\ntranscribed\ntranscriber\ntranscript\ntranscription\ntranscultural\ntranscursion\ntransdermal\ntransducer\ntransduction\ntranseat\ntransept\ntranseunt\ntransfer\ntransferability\ntransference\ntransferred\ntransfiguration\ntransfigure\ntransfix\ntransfixed\ntransforation\ntransform\ntransformation\ntransformed\ntransformer\ntransfuse\ntransfusion\ntransgfress\ntransgress\ntransgression\ntransgressive\ntransgressor\ntransi\ntransience\ntransient\ntransiently\ntransientness\ntransilience\ntransiliency\ntransillumination\ntransistor\ntransistorized\ntransit\ntransition\ntransitional\ntransitionally\ntransitive\ntransitively\ntransitivity\ntransitorily\ntransitory\ntransitu\ntranslatable\ntranslate\ntranslation\ntranslational\ntranslator\ntransliteration\ntranslocation\ntranslucence\ntranslucency\ntranslucent\ntranslumination\ntranslunar\ntransmarine\ntransmigration\ntransmission\ntransmit\ntransmittance\ntransmitted\ntransmitter\ntransmogrification\ntransmogrify\ntransmundane\ntransmutation\ntransmute\ntransmuted\ntransoceanic\ntransom\ntransparence\ntransparency\ntransparent\ntransparently\ntranspicuous\ntranspierce\ntranspiration\ntranspire\ntranspiring\ntransplace\ntransplacental\ntransplant\ntransplantable\ntransplantation\ntransplendency\ntransplendent\ntranspolar\ntransponder\ntranspontine\ntransport\ntransportation\ntransported\ntransporter\ntransposal\ntranspose\ntransposition\ntranssexual\ntranssexual(a)\ntransshipment\ntransubstantiation\ntransudation\ntransude\ntransume\ntransumption\ntransvaal\ntransversal\ntransversalis\ntransverse\ntransversely\ntransversion\ntransvestic\ntransvestism\ntransvestite\ntranter\ntrap\ntrapa\ntrapaceae\ntrapan\ntrapdoor\ntrapes\ntrapeze\ntrapezium\ntrapezohedron\ntrapezoid\ntrapezoidal\ntrapper\ntrapping\ntrappings\ntrappist\ntraps\ntrapshooter\ntrash\ntrashy\ntraulism\ntrauma\ntraumatic\ntraumatophobia\ntrautvetteria\ntravail\ntrave\ntravel\ntravel-soiled\ntravel-worn\ntraveled\ntraveler\ntravelers\ntraveling\ntravellers\ntravelogue\ntravels\ntravelstained\ntravers\ntraversable\ntraversal\ntraverse\ntravess\ntravestie\ntravesty\ntravis\ntrawl\ntrawler\ntray\ntreacherous\ntreachery\ntreacle\ntread\ntreadle\ntreadmill\ntreads\ntreason\ntreasure\ntreasurer\ntreasurership\ntreasures\ntreasuretrove\ntreasuries\ntreasury\ntreat\ntreated\ntreatise\ntreatment\ntreaty\ntreble\ntrebleness\ntrebly\ntrebucbet\ntrebuchet\ntrebucket\ntrebuket\ntrecento\ntrecker\ntree\ntreehopper\ntreenail\ntrees\ntrefoil\ntrek\ntrekker\ntrellis\ntrematoda\ntremble\ntrembles\ntrembling\ntremblingly\ntremella\ntremellaceae\ntremellales\ntremellose\ntremendous\ntremendously\ntremens\ntremolite\ntremolo\ntremor\ntremulous\ntremulously\ntrench\ntrenchang\ntrenchant\ntrenchantly\ntrencher\ntrenches\ntrenchfoot\ntrend\ntrend-setter\ntrending\ntrendsetting\ntrendy\ntrennel\ntrenton\ntrepan\ntrepang\ntrephination\ntrepidat\ntrepidation\ntreponema\ntreponemataceae\ntres\ntrespass\ntress\ntresspass\ntrestle\ntrestlework\ntret\ntrevet\ntrews\ntrey\ntri-iodothyronine\ntria\ntriad\ntriadelphous\ntriadie\ntriaenodon\ntriage\ntriakidae\ntrial\ntrial-and-error\ntrialeurodes\ntriality\ntrialogue\ntriamcinolone\ntriangle\ntriangular\ntriangularity\ntriangulate\ntriangulation\ntriarch\ntriarchy\ntriassic\ntriatoma\ntribadistic\ntribal\ntribalism\ntribe\ntribes\ntribesman\ntribolium\ntriboloby\ntribologist\ntribonema\ntribonemaceae\ntribromoethanol\ntribuens\ntribulation\ntribulus\ntribunal\ntribune\ntribuneship\ntributary\ntribute\ntributyrin\ntricapsular\ntrice\ntricentenary\ntriceps\ntriceratops\ntrichechidae\ntrichechus\ntrichion\ntrichiuridae\ntrichloride\ntrichoceros\ntrichodesmium\ntrichodontidae\ntrichogenous\ntrichoglossus\ntrichoid\ntricholoma\ntricholomataceae\ntrichomanes\ntrichomonad\ntrichomoniasis\ntrichophaga\ntrichophyton\ntrichoptera\ntrichosis\ntrichostema\ntrichosurus\ntrichotillomania\ntrichotomous\ntrichotomy\ntrichroism\ntrichromatic\ntrichys\ntrick\ntricked\ntricked-out\ntrickery\ntrickle\ntrickly\ntricks\ntrickster\ntricksy\ntricky\ntriclinic\ntricolor\ntricorn\ntricot\ntricuspid\ntricycle\ntricyclic\ntridacna\ntridacnidae\ntrident\ntridental\ntridentate\ntridentiferous\ntridymite\ntried\ntriennial\ntriennium\ntrier\ntrifid\ntrifle\ntrifled\ntrifler\ntrifles\ntrifling\ntrifoliate\ntrifolium\ntriform\ntrifurcate\ntrifurcation\ntrig\ntriga\ntrigamy\ntrigeminal\ntrigger\ntrigger-happy\ntriggerfish\ntriglidae\ntriglinae\ntriglochin\ntriglyceride\ntrigness\ntrigon\ntrigonal\ntrigonella\ntrigonometric\ntrigonometrician\ntrigonometry\ntrigram\ntrigrammatic\ntrigrammic\ntrigraph\ntrikumia/\ntrilateral\ntrilingual\ntrilisa\ntrill\ntrilliaceae\ntrillion\ntrillionth\ntrillium\ntrills\ntrilobate\ntrilogistic\ntrilogy\ntrim\ntrimaran\ntrimer\ntrimester\ntrimly\ntrimmed\ntrimmer\ntrimming\ntrimorphodon\ntrimotored\ntrimurti\ntrinal\ntrine\ntrinectes\ntringa\ntrinidad\ntrinidadian\ntrininty\ntrinitarian\ntrinitarianism\ntrinity\ntrinket\ntrinkgeld\ntrinomial\ntrinormial\ntrio\ntriode\ntriolein\ntrionychidae\ntrionym\ntrionyx\ntriopidae\ntriops\ntriostium\ntrioxide\ntrip\ntripalmitin\ntripartite\ntripartition\ntripe\ntripetalous\ntriphammer\ntriphosphopyridine\ntriphthong\ntripinnate\ntripinnatifid\ntriplane\ntriple\ntriple-crown\ntriple-spacing\ntriplet\ntripletail\ntripleurospermum\ntriplicate\ntriplication\ntriplicity\ntripling\ntriplochiton\ntriplopia\ntripod\ntripodal\ntripodic\ntripoli\ntripos\ntripotage\ntripper\ntripping\ntrippingly\ntripsis\ntriptolemus\ntriptych\ntriquetral\ntriquetrous\ntrireme\ntrisaccharide\ntrisagion\ntrisect\ntrisected\ntrisection\ntriseme\ntriskaidekaphobia\ntriskaidekaphobic\ntriskele\ntriskelion\ntrismus\ntristan\ntriste\ntristearin\ntristem\ntristful\ntrisula\ntrisulcate\ntrisyllable\ntritanopia\ntritanopic\ntrite\ntritely\ntriteness\ntritheism\ntritheist\ntriticum\ntriton\ntriturate\ntrituration\ntriturus\ntriumliterarum\ntriump\ntriumph\ntriumphal\ntriumphant\ntriumphantly\ntriumphe\ntriumphum\ntriumvir\ntriumvirate\ntriune\ntriunity\ntrivalent\ntrivere\ntrivet\ntrivia\ntrivial\ntriviality\ntrivially\ntroat\ntrocar\ntrocha\ntrochaic\ntrochee\ntrochilic\ntrochilics\ntrochilidae\ntrochlear\ntroes\ntrogium\ntroglodyte\ntroglodytes\ntroglodytic\ntroglodytidae\ntrogon\ntrogonidae\ntrogoniformes\ntroika\ntroilus\ntrojan\ntroll\ntrolley\ntrolleybus\ntrollius\ntrollop\ntrombicula\ntrombiculiasis\ntrombiculidae\ntrombiculiid\ntrombidiid\ntrombidiidae\ntrombone\ntrombonist\ntromp\ntrompillo\ntrondheim\ntroop\ntrooper\ntroops\ntroopship\ntrop\ntropaeolaceae\ntropaeolum\ntrope\ntrophonius\ntrophoplasm\ntrophotropic\ntrophotropism\ntrophozoite\ntrophy\ntropic\ntropical\ntropically\ntropidoclonion\ntropism\ntroponym\ntroponymy\ntropopause\ntroposphere\ntroppo\ntrot\ntroth\ntrothless\ntrotter\ntrotters\ntrottoir\ntrou-de-loup\ntroubadour\ntrouble\ntrouble-free\ntroubled\ntroublemaker\ntroubles\ntroubleshooter\ntroublesome\ntroublesomeness\ntroublous\ntrough\ntrounce\ntroupe\ntrouser\ntrousers\ntrousseau\ntrout\ntrouvaille\ntrouvere\ntrovato\ntrove\ntrover\ntrow\ntrowel\ntrowsers\ntroy\ntruancy\ntruant\ntruce\ntrucidation\ntruck\ntruckle\ntrucklebed\ntruckler\ntruckling\ntruckman\ntruculence\ntruculent\ntruculently\ntrudge\ntruditur\ntrue\ntrue(a)\ntrue-blue\ntrue-false\ntrue-to-life(a)\ntruehearted\ntruelove\ntrueness\ntruepenny\ntruffle\ntruism\ntrulgus\ntrull\ntruly\ntruman\ntrump\ntrumped\ntrumped-up(a)\ntrumpery\ntrumpet\ntrumpeter\ntrumpetfish\ntrumpets\ntrumpettoned\ntrumpettongued\ntrumpetwood\ntrumps\ntruncate\ntruncated\ntruncheon\ntruncocolumella\ntrundle\ntrunk\ntrunkmaker\ntrunnion\ntruss\ntrussed\ntrust\ntrustee\ntrustful\ntrustfully\ntrusting\ntrustless\ntrustworthiness\ntrustworthy\ntrusty\ntruth\ntruthful\ntruthfully\ntruthfulness\ntruthless\ntruthloving\ntrutination\ntry\ntrying\ntrypetidae\ntryptophan\ntryst\ntrysting\ntsar\ntsimshian\ntsouic\ntsuga\ntsungli\ntsushima\ntswana\ntu\ntua\ntuareg\ntuatara\ntub\ntub-thumper\ntuba\ntubal\ntubam\ntubate\ntube\ntubed\ntubeless\ntuber\ntuberaceae\ntuberales\ntubercle\ntubercular\ntubercularia\ntuberculariaceae\ntuberculin\ntuberculoid\ntuberculosis\ntuberculous\ntuberose\ntuberosity\ntuberous\ntubman\ntubocurarine\ntubular\ntubulated\ntubule\ntubulidentata\ntubulous\ntucana\ntuck\ntucked\ntucker\ntucson\ntudor\ntuen\ntuesday\ntuft\ntufted\ntufthunter\ntufthunting\ntug\ntug-of-war\ntugboat\ntugela\ntugend\ntugrik\ntuille\ntuition\ntularemia\ntulip\ntulipa\ntulipwood\ntulit\ntulle\ntullian\ntulostoma\ntulostomaceae\ntulostomatales\ntulsa\ntulu\ntumble\ntumblebug\ntumbledown\ntumbler\ntumblewed\ntumbleweed\ntumbling\ntumbrel\ntumefacient\ntumefaction\ntumefying\ntumescence\ntumid\ntumor\ntumour\ntumous\ntump\ntums\ntumult\ntumultuaary\ntumultuary\ntumultuation\ntumultuous\ntumultuously\ntumulus\ntun\ntuna\ntunable\ntunaburger\ntund\ntundra\ntune\ntune-up\ntuned\ntuneful\ntuneless\ntunelessly\ntuner\ntunga\ntungstate\ntungsten\ntungus\ntungusic\ntunic\ntunicate\ntunicle\ntuning\ntunis\ntunisia\ntunisian\ntunker\ntunnage\ntunnel\ntup\ntupaia\ntupaiidae\ntupelo\ntupi\ntupi-guarani\ntupik\ntupinambis\ntupungatito\ntupungato\nturban\nturbaned\nturbary\nturbellaria\nturbid\nturbidity\nturbinate\nturbinated\nturbination\nturbine\nturbiniform\nturbogenerator\nturbojet\nturbot\nturbulence\nturbulent\nturbulently\nturcism\nturd\nturdidae\nturdinae\nturdus\ntureen\nturf\nturfan\nturflike\nturfman\nturfy\nturgescence\nturgescent\nturgid\nturgidity\nturgidly\nturgidness\nturgor\nturin\nturk\nturk's-cap\nturkey\nturkeys\nturki\nturkish\nturkistan\nturkmen\nturkmenistan\nturkoman\nturlupinade\nturmeric\nturmoil\nturn\nturn-on\nturnaround\nturncock\nturned\nturner\nturnerfest\nturnery\nturnicidae\nturning\nturnings\nturnip\nturnix\nturnkey\nturnoff\nturnout\nturnover\nturnpike\nturns\nturnscrew\nturnspit\nturnstile\nturnstone\nturntable\nturnverein\nturpentine\nturpi\nturpin\nturpissimus\nturpitude\nturquois\nturquoise\nturreae\nturret\nturritis\ntursiops\nturtle\nturtledove\nturtledoves\nturtleneck\nturvy\ntuscan\ntuscany\ntuscarora\ntush\ntushery\ntusk\ntussah\ntussilago\ntussle\ntut\ntuta\ntutee\ntutelage\ntutelary\ntutelo\ntutissimus\ntutor\ntutorage\ntutorial\ntutorially\ntutorship\ntutti\ntutti-frutti\ntutto\ntutus\ntuum\ntuvalu\ntuxedo\ntuxedoed\ntuyare\ntvg\ntvr\ntw\ntwaddle\ntwaddling\ntwain\ntwang\ntwas\ntwattle\ntwattlle\ntwayblade\ntweak\ntweed\ntweedle\ntweedledee\ntweedledum\ntweet\ntweeter\ntweeze\ntwelfth\ntwelfthtide\ntwelve\ntwenties\ntwentieth\ntwenty\ntwenty-eight\ntwenty-eighth\ntwenty-fifth\ntwenty-first\ntwenty-five\ntwenty-four\ntwenty-fourth\ntwenty-nine\ntwenty-ninth\ntwenty-one\ntwenty-second\ntwenty-seven\ntwenty-seventh\ntwenty-six\ntwenty-sixth\ntwenty-third\ntwenty-three\ntwenty-two\ntwentyfive\ntwentyfour\ntwentyfourth\ntwentyone\ntwerp\ntwice\ntwicetold\ntwiddle\ntwig\ntwilight\ntwill\ntwin\ntwin-bedded\ntwinberry\ntwine\ntwined\ntwineth\ntwinflower\ntwinge\ntwinjet\ntwinkle\ntwinkling\ntwinkling(a)\ntwins\ntwire\ntwirl\ntwirlingly\ntwist\ntwisted\ntwit\ntwitch\ntwitter\ntwitting\ntwixt\ntwo\ntwo-by-four\ntwo-dimensional\ntwo-dimensionality\ntwo-eared\ntwo-handed\ntwo-hitter\ntwo-lane\ntwo-piece\ntwo-ply\ntwo-step\ntwo-thirds\ntwo-way\ntwoedged\ntwofold\ntwopan\ntwopence\ntwopenny\ntwopennyhalfpenny\ntwosided\ntyche\ntycoon\ntyg\ntying\ntyke\ntylenchidae\ntylenchus\ntyler\ntymbal\ntympani\ntympanic\ntympanist\ntympanuchus\ntympanum\ntympany\ntyne\ntype\ntyped\ntypes\ntypescript\ntypewriter\ntypewriting\ntypha\ntyphaceae\ntyphlopidae\ntyphoeus\ntyphoid\ntyphon\ntyphoon\ntyphus\ntypical\ntypicality\ntypically\ntypification\ntypify\ntyping\ntypist\ntypographic\ntypographical\ntypographically\ntypography\ntyr\ntyranni\ntyrannic\ntyrannical\ntyrannicide\ntyrannid\ntyrannidae\ntyrannis\ntyrannize\ntyrannosaur\ntyrannus\ntyranny\ntyrant\ntyre\ntyro\ntyrocidine\ntyrol\ntyrolean\ntyrosine\ntyrothricin\ntyto\ntytonidae\ntzar\nu\nu-turn\nuakari\nuberous\nuberrima\nuberty\nubi\nubiety\nubique\nubiquitarian\nubiquitariness\nubiquitary\nubiquitous\nubiquity\nubykh\nuca\nucalegon\nudder\nudmurt\nudometer\nufa\nuganda\nugandan\nugaritic\nugh\nugliness\nugly\nugric\nuhlanmounted\nuigur\nuintatheriidae\nuintatherium\nuisquebaugh\nukase\nuke\nukraine\nukrainian\nukranian\nul\nulatrophia\nulcer\nulcerate\nulcerative\nulcus\nulema\nulex\nulfilas\nuliginous\nulitis\null\nullage\nullalulla\nulmaceae\nulmus\nulna\nulnar\nulster\nulterior\nultima\nultimacy\nultimate\nultimately\nultimatum\nultimo\nultimogeniture\nultimus\nultio\nultra\nultracentrifugation\nultracentrifuge\nultraconservative\nultramarine\nultramicroscope\nultramicroscopic\nultramodern\nultramontane\nultramontanism\nultramundane\nultrasonically\nultrasuede\nultraviolet\nululation\nulva\nulvaceae\nulvales\nulvophyceae\nulysses\numa\numbel\numbellales\numbellate\numbellifer\numbelliferae\numbelliferous\numbellularia\number\numbilical\numbilicate\numbilicus\numbo\numbra\numbrage\numbrageous\numbrella\numbrellawort\numbria\numbrian\numbrina\numbundu\numlaut\numpirage\numpire\numpteen\numpteenth\nun\nun-american\nun-come-at-able\nuna\nunabashed\nunabashedly\nunabated\nunable\nunable(p)\nunabridged\nunabused\nunaccented\nunacceptability\nunacceptable\nunacceptably\nunaccommodating\nunaccompanied\nunaccomplished\nunaccountable\nunaccountably\nunaccredited\nunaccustomed\nunachievable\nunacknowledged\nunacquaintance\nunacquainted\nunacquainted(p)\nunacquired\nunacquisitive\nunactable\nunadaptability\nunadaptable\nunadapted\nunaddicted\nunaddressed\nunadjustable\nunadjusted\nunadmonished\nunadoptable\nunadorned\nunadulterated\nunadventurous\nunadvisable\nunadvised\nunadvisedly\nunaerated\nunaffected\nunaffected(p)\nunaffectedness\nunaffecting\nunaffectionate\nunaffiliated\nunaffixed\nunafflicted\nunafraid(p)\nunaged\nunaggressive\nunagitated\nunaided\nunairworthy\nunalarming\nunalaxmed\nunalert\nunalienable\nunaligned\nunalike\nunallayed\nunallied\nunallowable\nunallowed\nunalloyed\nunalluring\nunalterability\nunalterable\nunalterably\nunaltered\nunamazed\nunambiguity\nunambiguous\nunambiguously\nunambitious\nunambitiously\nunamenable\nunamended\nunamiable\nunamusing\nunanalyzable\nunanalyzed\nunangry(p)\nunanimated\nunanimity\nunanimous\nunanimously\nunannexed\nunannounced\nunanswerable\nunanswered\nunanticipated\nunapologetic\nunappalled\nunappareled\nunapparent\nunappealable\nunappealing\nunappealingly\nunappeasable\nunappendaged\nunappetizing\nunappetizingness\nunapplied\nunappreciated\nunappreciative\nunapprehended\nunapprehensive\nunapprized\nunapproachability\nunapproachable\nunapproached\nunappropriated\nunapproved\nunapt\nunarguably\nunargumentative\nunarmed\nunarmored\nunarranged\nunarticulated\nunary\nunascertainable\nunascertained\nunashamed\nunashamedly\nunasked\nunaspiring\nunassailable\nunassailed\nunassembled\nunassertive\nunassertively\nunassertiveness\nunassigned\nunassisted\nunassociated\nunassuming\nunassumingly\nunassured\nunasterisked\nunasupicious\nunatoned\nunattached\nunattackable\nunattainable\nunattainableness\nunattainably\nunattained\nunattempted\nunattended\nunattested\nunattracted\nunattractive\nunattractively\nunattractiveness\nunattributable\nunauthentic\nunauthenticated\nunauthoritative\nunauthorized\nunavailable\nunavailing\nunavenged\nunavoidable\nunavowed\nunawakened\nunaware\nunawares\nunawed\nunbacked\nunbaffled\nunbalanced\nunbalconied\nunbanded\nunbar\nunbarred\nunbarreled\nunbearable\nunbearably\nunbeatable\nunbeaten\nunbeauteous\nunbeautiful\nunbecoming\nunbecomingness\nunbefitting\nunbegotten\nunbeguile\nunbegun\nunbeholden(p)\nunbeknown\nunbeknown(p)\nunbelief\nunbelievably\nunbeliever\nunbelieving\nunbeloved\nunbelted\nunbend\nunbending\nunbeneficed\nunbenevolent\nunbenign\nunbent\nunbeseeming\nunbesought\nunbestowed\nunbetrayed\nunbewailed\nunbiased\nunbiassed\nunbidden\nunbigoted\nunbind\nunbitter\nunblamable\nunblamed\nunblanched\nunblanching\nunbleached\nunblemished\nunblended\nunblessed\nunblest\nunblinking\nunblinkingly\nunbloodied\nunblown\nunblushing\nunblushinghly\nunblushlng\nunboastful\nunbodied\nunboiled\nunbolt\nunbooked\nunbookish\nunbordered\nunborn\nunborrowed\nunbosom\nunbought\nunbound\nunbounded\nunbowed\nunbrace\nunbraced\nunbrainwashed\nunbranched\nunbranded\nunbreakable\nunbreakableness\nunbreathed\nunbred\nunbribed\nunbridgeable\nunbridled\nunbroken\nunbrowsed\nunbruised\nunbrushed\nunburden\nunburdened\nunburied\nunburnished\nunbusied\nunbuttoned\nuncalculating\nuncalled\nuncalled-for\nuncamphorated\nuncandid\nuncannily\nuncanny\nuncanonical\nuncapped\nuncared\nuncared-for\nuncaredfor\nuncarpeted\nuncarved\nuncastrated\nuncate\nuncategorized\nuncaught\nuncaulked\nuncaused\nunceasing\nuncensored\nuncensured\nunceremonious\nunceremoniously\nunceremoniousness\nuncertain\nuncertainly\nuncertainty\nuncertified\nunchain\nunchained\nunchallengeable\nunchallenged\nunchangeable\nunchangeableness\nunchanged\nunchanging\nuncharacteristic\nuncharacteristically\nuncharged\nuncharitable\nuncharitableness\nunchartered\nunchaste\nunchastised\nuncheckable\nunchecked\nuncheckered\nuncheerful\nuncheerfulness\nuncheery\nunchivalric\nunchivalrously\nunchristian\nunchristianly\nuncial\nuncinated\nuncircumscribed\nuncircumspect\nuncivil\nuncivilized\nuncivilly\nunclaimed\nunclassical\nunclassifiable\nunclassified\nuncle\nunclean\nuncleanliness\nuncleanly\nuncleanness\nunclear\nuncleared\nunclearness\nuncles\nunclimbable\nunclipped\nunclog\nunclogged\nunclose\nunclosed\nunclothed\nunclouded\nunclubbable\nunclutch\nuncluttered\nuncoated\nuncoerced\nuncoffind\nuncoif\nuncoil\nuncoiled\nuncolored\nuncombable\nuncombed\nuncombined\nuncomeatable\nuncomely\nuncomendable\nuncomformable\nuncomfortable\nuncomfortably\nuncommenced\nuncommendable\nuncommensurable\nuncommercial\nuncommercialized\nuncommitted\nuncommon\nuncommonly\nuncommonness\nuncommunicated\nuncommunicative\nuncommunicativeness\nuncompact\nuncompartmented\nuncompassionate\nuncompelled\nuncompensated\nuncompetitive\nuncomplaining\nuncomplainingly\nuncomplaisant\nuncompleted\nuncompliant\nuncomplicated\nuncomplimentary\nuncomplying\nuncompounded\nuncomprehended\nuncomprehending\nuncompressed\nuncompromising\nuncompromisingly\nunconcealable\nunconcealed\nunconceived\nunconcern\nunconcerned\nunconcernedly\nunconcocted\nuncondemned\nuncondensed\nunconditional\nunconditionally\nunconditioned\nunconducing\nunconducive\nunconducting\nunconfessed\nunconfident\nunconfined\nunconfirmed\nunconformable\nunconformably\nunconformity\nunconfused\nunconfuted\nuncongealed\nuncongenial\nuncongeniality\nunconnected\nunconnectedness\nunconquerable\nunconquered\nunconscientious\nunconscientiousness\nunconscionable\nunconscious\nunconscious(p)\nunconsciously\nunconsciousness\nunconsenting\nunconsidered\nunconsolable\nunconsolidated\nunconsonant\nunconspicuous\nunconstipated\nunconstitutional\nunconstitutionally\nunconstrained\nunconstricted\nunconstructive\nunconsumed\nunconsummated\nuncontaminated\nuncontested\nuncontradicted\nuncontrite\nuncontrollable\nuncontrollably\nuncontrolled\nuncontroversial\nuncontroversially\nuncontroverted\nunconventional\nunconventionality\nunconventionally\nunconversable\nunconversant\nunconverted\nunconvinced\nunconvincing\nunconvincingly\nuncooked\nuncooperative\nuncoordinated\nuncopied\nuncordial\nuncork\nuncorrected\nuncorrelated\nuncorroborated\nuncorrupt\nuncorrupted\nuncountably\nuncounted\nuncoupled\nuncourteous\nuncourteousness\nuncourtly\nuncousinly\nuncouth\nuncouthly\nuncover\nuncovered\nuncrannied\nuncreated\nuncreative\nuncreativeness\nuncritical\nuncritically\nuncropped\nuncrossed\nuncrowded\nuncrown\nuncrowned\nuncrystallized\nunction\nunctuosity\nunctuous\nunctuously\nunctuousness\nunculled\nunculpable\nuncultivable\nuncultivated\nuncurbed\nuncured\nuncurl\nuncurled\nuncurved\nuncustomary\nuncut\nund\nundamaged\nundamped\nundatable\nundated\nundaunted\nundazzled\nundebauched\nundecagon\nundeceive\nundeceived\nundecided\nundecipherable\nundeciphered\nundecked\nundeclared\nundecomposed\nundedicated\nundefaced\nundefeated\nundefended\nundeferential\nundefiled\nundefinable\nundefined\nundeformed\nundelineated\nundemanding\nundemocratic\nundemocratically\nundemolished\nundemonstrable\nundemonstrated\nundemonstrative\nundeniable\nundeniably\nundenominational\nundependability\nundependable\nundepicted\nundeplored\nundepraved\nundeprived\nunder\nunder(a)\nunder-the-counter\nunderachievement\nunderachiever\nunderage\nunderarm\nunderbelly\nunderbreath\nunderbred\nunderbrush\nundercarriage\nundercharge\nunderclass(a)\nunderclothing\nundercoat\nundercoated\nundercover\nundercurrent\nundercurrents\nunderdeveloped\nunderdevelopment\nunderdog\nunderdressed\nundereducated\nunderemployed\nunderestimate\nunderestimation\nunderevaluation\nunderexposure\nunderfelt\nunderfoot\nundergarment\nundergo\nundergraduate\nunderground\nunderhand\nunderhandedly\nunderhung\nunderivative\nunderived\nunderlay\nunderlessee\nunderlet\nunderlie\nunderline\nunderling\nunderlining\nunderlying\nundermine\nundermined\nundermost\nunderneath\nundernourishment\nundernsong\nunderpaid\nunderpants\nunderpart\nunderpass\nunderpayment\nunderplot\nunderpopulated\nunderprivileged\nunderproduction\nunderrate\nunderreckon\nunderscore\nundersecretary\nundersell\nundersexed\nundersign\nundersize\nundersized\nunderslung\nunderspent\nunderstand\nunderstanding\nunderstandingly\nunderstated\nunderstatement\nunderstood\nunderstrapper\nunderstudy\nundertake\nundertaker\nundertaking\nundertide\nundertone\nundertow\nundervaluation\nundervalue\nundervaluing\nunderwear\nunderwing\nunderwood\nunderworld\nunderwrite\nunderwriter\nundescended\nundescribed\nundescriptive\nundeserved\nundeservedly\nundeserving\nundesigned\nundesigning\nundesirability\nundesirable\nundesirableness\nundesirably\nundesired\nundesirous\nundespairing\nundestroyable\nundestroyed\nundetected\nundetermindtion\nundetermined\nundeterred\nundeveloped\nundeviating\nundevout\nundiagnosable\nundiagnosed\nundies\nundifferentiated\nundigested\nundignified\nundiluted\nundiminished\nundimmed\nundine\nundiplomatic\nundiplomatically\nundirected\nundiscernible\nundiscerning\nundischarged\nundisciplined\nundisclosed\nundiscoverable\nundiscovered\nundiscriminating\nundisguised\nundismayed\nundisposed\nundisputed\nundissembling\nundissolved\nundistinguishable\nundistinguished\nundistorted\nundistracted\nundistributed\nundisturbed\nundiversified\nundividable\nundivided\nundo\nundocked\nundocumented\nundoing\nundomestic\nundomesticated\nundone\nundoubted\nundoubtedly\nundrained\nundramatic\nundramatically\nundraped\nundrawn\nundreaded\nundreamed\nundreamt\nundress\nundressed\nundried\nundrilled\nundrinkable\nundrooping\nundue\nundueness\nundulate\nundulation\nundulatory\nunduly\nunduteous\nundutiful\nundutifulness\nundyed\nundying\nundynamic\nune\nuneager\nuneared\nunearned\nunearth\nunearthly\nuneasiness\nuneasy\nuneaten\nuneconomical\nuneder\nunedifying\nunedited\nuneducated\nunembarrassed\nunembodied\nunemotional\nunemotionality\nunemotionally\nunemphatic\nunemployable\nunemployed\nunemployment\nunenclosed\nunencouraging\nunencumbered\nunendeared\nunended\nunending\nunendowed\nunendurable\nunenforceable\nunenforced\nunengaged\nunenglightened\nunenjoyed\nunenlightened\nunenlightening\nunenlightenment\nunenlivened\nunenslaved\nunenterprising\nunentertaining\nunenthralled\nunenthusiastic\nunenthusiastically\nunentitled\nunenviable\nunenvied\nunequal\nunequalized\nunequalled\nunequipped\nunequitable\nunequivocal\nunequivocally\nunerect\nunergo\nuneroded\nunerring\nunerringly\nunessayed\nunessential\nunestablished\nunethical\nunethically\nuneven\nunevenly\nunevenness\nuneventful\nuneventfully\nunexact\nunexacting\nunexaggerated\nunexamined\nunexampled\nunexcelled\nunexceptionable\nunexceptional\nunexchangeability\nunexchangeable\nunexcitable\nunexcited\nunexciting\nunexcitingly\nunexclusive\nunexcused\nunexecuted\nunexempt\nunexercised\nunexerted\nunexhausted\nunexpanded\nunexpansive\nunexpected\nunexpectedly\nunexpectedness\nunexpendable\nunexpensive\nunexpired\nunexplained\nunexploited\nunexplored\nunexportable\nunexposed\nunexpressed\nunexpressive\nunexpurgated\nunextended\nunextinguished\nunfaceted\nunfaded\nunfading\nunfailing\nunfailingly\nunfair\nunfairly\nunfairness\nunfaithful\nunfaithfully\nunfallen\nunfaltering\nunfamiliar\nunfamiliarity\nunfashionable\nunfashionably\nunfashioned\nunfastened\nunfastidious\nunfathomable\nunfathomed\nunfattened\nunfavorable\nunfavorableness\nunfavorably\nunfeared\nunfeasible\nunfeathered\nunfed\nunfeeling\nunfeelingly\nunfeelingness\nunfeigned\nunfeignedly\nunfelled\nunfelt\nunfeminine\nunfenced\nunfermented\nunfertile\nunfertilized\nunfetter\nunfettered\nunfiled\nunfilled\nunfilmed\nunfinished\nunfirm\nunfit\nunfitness\nunfitted\nunfitting\nunfixed\nunflagging\nunflammable\nunflattering\nunflavored\nunfledged\nunfleshly\nunflinching\nunflurried\nunfocused\nunfold\nunfolded\nunfolding\nunfoldment\nunforbid\nunforbidden\nunforced\nunforeseeable\nunforeseen\nunforested\nunforethoughtful\nunforfeitable\nunforfeited\nunforgettable\nunforgiving\nunforgivingly\nunforgotten\nunformed\nunforsaken\nunfortified\nunfortunate\nunfortunately\nunfounded\nunfractured\nunframed\nunfree\nunfrequent\nunfrequented\nunfrequently\nunfretted\nunfriended\nunfriendliness\nunfriendly\nunfrightened\nunfrock\nunfrosted\nunfrozen\nunfruitful\nunfruitfulness\nunfueled\nunfulfilled\nunfunctional\nunfunded\nunfunny\nunfurl\nunfurnished\nunfurrowed\nungainly\nungallant\nungarnished\nungathered\nungeared\nungenerous\nungenerously\nungenial\nungenteel\nungentle\nungentlemanlike\nungentlemanly\nungifted\nunglazed\nunglorified\nunglue\nungodliness\nungodly\nungovernable\nungoverned\nungraceful\nungracious\nungraciously\nungraciousness\nungraded\nungrammatical\nungrammatically\nungranted\nungrasped\nungrateful\nungratefully\nungratified\nungregarious\nungroomed\nungrounded\nungrudging\nungrudgingly\nungual\nunguaranteed\nunguarded\nungue\nunguem\nunguent\nunguibus\nunguiculata\nunguiculate\nunguided\nunguiform\nunguilty\nunguinous\nunguis\nungulata\nungulate\nungummed\nungusseted\nunhabituated\nunhackneyed\nunhallowed\nunhampered\nunhand\nunhandseled\nunhandsome\nunhandy\nunhappily\nunhappiness\nunhappy\nunharbored\nunhardened\nunharmed\nunharmonious\nunharness\nunhatched\nunhazarded\nunheaded\nunhealed\nunhealthful\nunhealthfulness\nunhealthiness\nunhealthy\nunheard\nunheard-of\nunheated\nunheeded\nunheeding\nunhelpful\nunhelpfully\nunhelpfulness\nunhesitating\nunhesitatingly\nunhewn\nunhindered\nunhinge\nunhinged\nunholiness\nunhollowed\nunholy\nunhomogenized\nunhonored\nunhoped\nunhorsed\nunhostile\nunhouse\nunhoused\nunhurried\nunhurriedly\nunhurt\nunhygienic\nunhygienically\nuniat\nunicameral\nunicellular\nunicorn\nunicycle\nunideal\nunidentifiable\nunidentified\nunidimensional\nunidirectional\nunifacial\nunific\nunification\nunifilar\nuniflorous\nunifoliate\nuniforinity\nuniform\nuniformed\nuniformity\nuniformly\nuniformness\nunigenital\nunijocular\nunijugate\nunilateral\nunilateralism\nunilateralist\nunilaterally\nuniliteral\nunilluminated\nunilluminating\nunillustrated\nunimaginable\nunimaginably\nunimaginative\nunimaginatively\nunimagined\nunimitated\nunimodal\nunimodular\nunimpaired\nunimpassioned\nunimpeachable\nunimpeached\nunimpeded\nunimportance\nunimportant\nunimposing\nunimpressed\nunimpressible\nunimpressionable\nunimpressive\nunimpressively\nunimproved\nunincorporated\nunincreased\nunindebted\nuninduced\nunindustrialized\nuninebriated\nuninfected\nuninfectious\nuninflammable\nuninflected\nuninfluenced\nuninfluential\nuninformative\nuninformatively\nuninformed\nuningenuous\nuninhabitable\nuninhabited\nuninhibited\nuninitiate\nuninitiated\nuninjectable\nuninjured\nuninjurious\nuninominal\nuninquiring\nuninquisitive\nuninspired\nuninspiring\nuninstructed\nuninstructive\nuninsured\nunintellectual\nunintelligent\nunintelligently\nunintelligibility\nunintelligibilty\nunintelligible\nunintelligibleness\nunintelligibly\nunintended\nunintentional\nunintentionally\nuninterested\nuninteresting\nuninterestingly\nuninterestingness\nunintermitting\nuninterrupted\nuninterruptedly\nunintroduced\nunintrusive\nuninucleate\nuninured\nuninvented\nuninvestigated\nuninvited\nuninviting\nuninvolved\nunio\nunion\nunionidae\nunionism\nunionization\nuniparous\nunipolar\nunique\nunique(p)\nuniquely\nunironed\nunirritating\nunisex\nunisexual\nunison\nunisonance\nunisonant\nunisulcate\nunit\nunitarian\nunitarianism\nunitary\nunite\nunited\nunits\nunity\nunivalent\nunivalve\nuniversal\nuniversalism\nuniversalistic\nuniversality\nuniversally\nuniverse\nuniversity\nunix\nunjointed\nunjust\nunjustifiable\nunjustifiably\nunjustified\nunjustly\nunkempt\nunkennel\nunkind\nunkindest\nunkindled\nunkindly\nunkindness\nunkirid\nunknightly\nunknowing\nunknowingness\nunknowlable\nunknown\nunlabeled\nunlabored\nunlaced\nunlade\nunladylike\nunlamented\nunlaureled\nunlawful\nunlawfully\nunlawfulness\nunleaded\nunlearn\nunlearned\nunleavened\nunled\nunless\nunlettered\nunlicensed\nunlicked\nunlighted\nunlikable\nunlike\nunlikelihood\nunlikely\nunlikeness\nunlimber\nunlimited\nunlined\nunliquefied\nunlisted\nunliterary\nunlivable\nunlively\nunliveried\nunload\nunloaded\nunloading\nunlobed\nunlocated\nunlock\nunlooked\nunloose\nunlovable\nunloved\nunlovely\nunloving\nunlubricated\nunlucky\nunmade\nunmaimed\nunmake\nunmalicious\nunmaligned\nunmalleability\nunmalleable\nunman\nunmanageable\nunmanageably\nunmanfully\nunmanly\nunmanned\nunmannered\nunmannerly\nunmarked\nunmarketable\nunmarred\nunmarried\nunmask\nunmatched\nunmated\nunmeaning\nunmeaningness\nunmeant\nunmeasured\nunmechanical\nunmechanized\nunmediated\nunmedicinal\nunmeditated\nunmeet\nunmellowed\nunmelodious\nunmelodiously\nunmelted\nunmemorable\nunmemorably\nunmentionable\nunmercenary\nunmerciful\nunmerited\nunmeritorious\nunmethodical\nunmilitary\nunmindful\nunmindfully\nunmindfulness\nunmined\nunmingled\nunmissed\nunmistakable\nunmistakably\nunmitigable\nunmitigated\nunmixed\nunmoderated\nunmodernized\nunmodifiable\nunmodified\nunmodulated\nunmolested\nunmoneyed\nunmoral\nunmotivated\nunmotorized\nunmounted\nunmourned\nunmoved\nunmoved(p)\nunmoving\nunmown\nunmurmuring\nunmusical\nunmusically\nunmuzzled\nunmyelinated\nunnamed\nunnatural\nunnaturalized\nunnaturally\nunnaturalness\nunnavigable\nunnecessarily\nunnecessary\nunneeded\nunneighborliness\nunneighborly\nunnerve\nunnerved\nunneurotic\nunnilhexium\nunnilquintium\nunnilseptium\nunnotched\nunnoted\nunnoticeable\nunnoticeableness\nunnoticed\nunnourished\nunnumbered\nunnurtured\nuno\nunobeyed\nunobjectionable\nunobjective\nunobligated\nunobnoxious\nunobscured\nunobservable\nunobservant\nunobserved\nunobstructed\nunobtainable\nunobtained\nunobtrusive\nunobtrusively\nunobtrusiveness\nunobvious\nunoccupied\nunoffended\nunoffending\nunofficial\nunofficially\nunoften\nunoiled\nunopened\nunopposable\nunopposed\nunordered\nunorganized\nunoriented\nunoriginal\nunoriginality\nunornamental\nunornamented\nunorthodox\nunorthodoxy\nunostentatious\nunowed\nunowned\nunpacific\nunpacified\nunpack\nunpackaged\nunpadded\nunpaid\nunpaidfor\nunpaintable\nunpainted\nunpalatability\nunpalatable\nunpalatably\nunparagoned\nunparallel\nunparalleled\nunpardonable\nunparented\nunpariotic\nunparliamentary\nunpartitioned\nunpassable\nunpassionate\nunpasteurized\nunpatented\nunpatriotic\nunpatriotically\nunpatronized\nunpaved\nunpeaceable\nunpeaceful\nunpeople\nunpeopled\nunperceived\nunperceptive\nunperceptiveness\nunperformed\nunperjured\nunpermed\nunpermissive\nunpermissiveness\nunperplexed\nunpersuadable\nunpersuasive\nunpersuasiveness\nunperturbed\nunphilosophical\nunphilosphical\nunpierced\nunpigmented\nunpillared\nunpitied\nunpitying\nunplaced\nunplagued\nunplanned\nunplanted\nunplayable\nunplayful\nunpleasant\nunpleasantly\nunpleasantness\nunpleasing\nunpleasingness\nunplowed\nunpoetical\nunpointed\nunpointedness\nunpolished\nunpolite\nunpompous\nunpopular\nunpopularity\nunportable\nunportioned\nunpossessed\nunpotted\nunpowered\nunpracticed\nunprecedented\nunprecedentedly\nunpredictability\nunpredictable\nunpredictive\nunprejudiced\nunpremeditated\nunprepared\nunprepossessed\nunprepossessing\nunpresentable\nunpresidential\nunpressed\nunpretending\nunpretentious\nunpretentiously\nunpretentiousness\nunpreventable\nunprevented\nunpriced\nunpriestly\nunprincipled\nunprintable\nunprivileged\nunprized\nunprocessed\nunproclaimed\nunproduced\nunproductive\nunproductively\nunproductiveness\nunprofessional\nunproficiency\nunprofitable\nunprofitableness\nunprolific\nunpromising\nunprompted\nunpronounceable\nunprophetic\nunpropitious\nunprosperous\nunprotected\nunprotective\nunprovable\nunproved\nunprovided\nunprovocative\nunpublishable\nunpublished\nunpunctual\nunpunctuality\nunpunished\nunpurchased\nunpurified\nunpursued\nunputdownable\nunquain\nunqualified\nunqualifiedly\nunquelled\nunquenchable\nunquenched\nunquestionable\nunquestionableness\nunquestionably\nunquestioned\nunquestioning\nunquestioningly\nunquiet\nunquietly\nunratable\nunratified\nunravel\nunreached\nunreactive\nunread\nunready\nunreal\nunrealistic\nunrealistically\nunreality\nunreasonable\nunreasonableness\nunreasonably\nunreasoning\nunreassuring\nunreceptive\nunreclaimed\nunrecognizable\nunrecognizably\nunrecognized\nunreconciled\nunreconfuted\nunreconstructed\nunrecorded\nunrecounted\nunrecoverable\nunredressed\nunreduced\nunreeling\nunrefined\nunreflected\nunreflecting\nunreflective\nunreformed\nunrefreshed\nunrefuted\nunregarded\nunregenerate\nunregistered\nunregretful\nunregulated\nunreined\nunrelated\nunrelatedness\nunrelaxed\nunreleased\nunrelenting\nunreliable\nunrelieved\nunremedied\nunremembered\nunremitting\nunremoved\nunremunerated\nunremunerative\nunrenewable\nunrentable\nunrepaired\nunrepealed\nunrepeatable\nunrepeated\nunrepentant\nunrepented\nunrepining\nunreplenished\nunreportable\nunreported\nunrepresentative\nunrepressed\nunreprieved\nunreproached\nunreproducible\nunreproved\nunrequested\nunrequited\nunresented\nunresentful\nunreserve\nunreserved\nunreservedly\nunresisted\nunresisting\nunresolvable\nunresolved\nunrespectability\nunrespectable\nunrespected\nunrespited\nunresponsive\nunrest\nunrestored\nunrestrained\nunrestrainedly\nunrestraint\nunrestricted\nunrestrictive\nunretentive\nunretracted\nunrevenged\nunreverberant\nunreversed\nunrevised\nunrevived\nunrevoked\nunrewarded\nunrewarding\nunrhetorical\nunrhymed\nunrhythmical\nunriddle\nunrifled\nunrig\nunrigged\nunrighteous\nunrighteously\nunrighteousness\nunrip\nunripe\nunrivaled\nunroll\nunrolled\nunromantic\nunromantically\nunroofed\nunroot\nunrotten\nunrouged\nunruffled\nunruliness\nunruly\nuns\nunsaddle\nunsaddled\nunsafe\nunsaid\nunsalable\nunsalted\nunsaluted\nunsanctified\nunsanctioned\nunsanitariness\nunsanitary\nunsaponified\nunsarcastic\nunsated\nunsatisfactorily\nunsatisfactoriness\nunsatisfactory\nunsatisfiable\nunsatisfied\nunsaturated\nunsavoriness\nunsavory\nunsay\nunscalable\nunscanned\nunscathed\nunscheduled\nunscholarly\nunschooled\nunscientific\nunscientifically\nunscoured\nunscripted\nunscriptural\nunscrupulous\nunscrupulously\nunscrupulousness\nunseal\nunsealed\nunseamanlike\nunseamed\nunsearched\nunseasonable\nunseasonableness\nunseasonably\nunseasoned\nunseat\nunseaworthy\nunseductive\nunseeded\nunseemliness\nunseemly\nunseen\nunsegmented\nunsegmentic\nunseldom\nunselected\nunselective\nunselfconscious\nunselfconsciously\nunselfconsciousness\nunselfish\nunselfishly\nunselfishness\nunsensational\nunsent\nunsentimentally\nunseparated\nunserviceable\nunservile\nunsessile\nunsettle\nunsettled\nunsettlement\nunsevered\nunsex\nunsexy\nunshackled\nunshaded\nunshadowed\nunshaken\nunshaped\nunshapely\nunshared\nunsharpened\nunshaven\nunsheared\nunsheathe\nunsheathed\nunshielded\nunshifting\nunship\nunshockable\nunshocked\nunshod\nunshorn\nunshortened\nunshrinkable\nunshrinking\nunshuttered\nunsifted\nunsightliness\nunsightly\nunsigned\nunsilenced\nunsinged\nunsinkable\nunsized\nunskilled\nunskillful\nunskillfulness\nunslaked\nunsleeping\nunsmiling\nunsmilingly\nunsmooth\nunsmoothed\nunsnarling\nunsociability\nunsociable\nunsociably\nunsocial\nunsoiled\nunsold\nunsoldierlike\nunsoldierly\nunsolicited\nunsolicitous\nunsolved\nunsophisticated\nunsorted\nunsought\nunsound\nunsoundable\nunsoundness\nunsoured\nunsown\nunspaced\nunsparing\nunspeakable\nunspecialized\nunspecified\nunspectacular\nunspent\nunspied\nunspiritual\nunspoken\nunsportingly\nunspotted\nunstable\nunstaged\nunstaid\nunstained\nunstartling\nunstatesmanlike\nunsteadfast\nunsteadily\nunsteadiness\nunsteady\nunsterilized\nunstilted\nunstimulating\nunstinted\nunstinting\nunstintingly\nunstirred\nunstoppable\nunstopped\nunstoppered\nunstored\nunstrained\nunstratified\nunstrengthened\nunstressed\nunstruck\nunstructured\nunstrung\nunstuck\nunstudied\nunstudious\nunsubdued\nunsubject\nunsubmissive\nunsubsantial\nunsubservience\nunsubservient\nunsubstantial\nunsubstantiality\nunsuccessful\nunsuccessfully\nunsuccessive\nunsugared\nunsuitability\nunsuitable\nunsuited\nunsullied\nunsung\nunsunnd\nunsupervised\nunsupplied\nunsupportable\nunsupported\nunsupportive\nunsuppressed\nunsurmountable\nunsurp\nunsurpassable\nunsurpassed\nunsurpation\nunsurprised\nunsurprising\nunsusceptibility\nunsusceptible\nunsuspected\nunsuspecting\nunsuspectingly\nunsuspicious\nunsustainable\nunsweet\nunsweetened\nunswept\nunswerving\nunswervingly\nunsworn\nunsyllabled\nunsymmetric\nunsympathetic\nunsympathetically\nunsympathizing\nunsystematic\nunsystematically\nunsystematized\nuntainted\nuntalked\nuntamed\nuntangled\nuntanned\nuntapped\nuntarnished\nuntasted\nuntaught\nuntaxed\nunteach\nunteachabel\nunteachable\nuntempered\nuntenable\nuntenanted\nuntended\nuntested\nuntethered\nunthanked\nunthankful\nunthankfulness\nunthawed\nuntheatrical\nunthematic\nunthinkable\nunthinking\nunthought\nunthoughtfulness\nunthread\nunthreatened\nunthriftiness\nunthrifty\nunthrone\nuntidiness\nuntidy\nuntie\nuntied\nuntil\nuntilled\nuntimbered\nuntimeliness\nuntimely\nuntinged\nuntipped\nuntired\nuntiring\nuntitled\nunto\nuntoasted\nuntold\nuntouchable\nuntouched\nuntoward\nuntraceable\nuntraced\nuntracked\nuntractable\nuntrained\nuntrammeled\nuntranslatable\nuntranslated\nuntraveled\nuntraversable\nuntraversed\nuntreasured\nuntreated\nuntried\nuntrimmed\nuntrodden\nuntroubled\nuntrue\nuntruly\nuntrustworthiness\nuntrustworthy\nuntruth\nuntruthful\nuntruthfulness\nuntucked\nuntufted\nuntunable\nunturned\nuntutored\nuntwine\nuntwist\nuntwisted\nuntying\nununderstood\nununprepared\nunused\nunusual\nunusually\nunusualness\nunutterable\nunvaccinated\nunvalued\nunvanquished\nunvaried\nunvariedmonotonous\nunvariedness\nunvarnished\nunvarying\nunveil\nunveiled\nunveiling\nunvented\nunventilated\nunveracious\nunverified\nunversed\nunvexed\nunvindictive\nunviolated\nunvisited\nunvitrified\nunvoiced\nunvulcanized\nunwanted\nunwantedly\nunwarily\nunwariness\nunwarlike\nunwarmed\nunwarned\nunwarped\nunwarrantable\nunwarrantably\nunwarranted\nunwary\nunwashed\nunwasted\nunwatchful\nunwavering\nunwaxed\nunweakened\nunweaned\nunwearable\nunwearied\nunwebbed\nunwed\nunwedded\nunweeded\nunweeting\nunweighed\nunwelcome\nunwell\nunwholesome\nunwholesomeness\nunwieldly\nunwieldy\nunwilled\nunwilling\nunwillingly\nunwillingness\nunwind\nunwiped\nunwise\nunwished\nunwithered\nunwitnessed\nunwitting\nunwittingly\nunwomanly\nunwonted\nunwontedly\nunwooded\nunworkmanlike\nunworldly\nunworn\nunworshiped\nunworthily\nunworthiness\nunworthy\nunwound\nunwounded\nunwoven\nunwrap\nunwrapped\nunwrinkled\nunwritten\nunwronged\nunwrought\nunwrung\nunyielding\nup\nup(a)\nup(p)\nup-bow\nup-country\nup-to-date\nup-to-the-minute\nupanishad\nupanishads\nupas\nupbear\nupbeat\nupbound\nupbraid\nupbraiding\nupbringing\nupcast\nupcurved\nupdate\nupdating\nupdraft\nupended\nupfield\nupgrade\nupgrow\nupgrowth\nupharsin\nupheaval\nupheave\nuphill\nuphoist\nuphold\nupholder\nupholsterer\nupholstery\nupland\nuplands\nuplift\nuplifted\nuplifting\nuplink\nupmarket\nupon\nuponsand\nupper\nupper-class\nupper-lower-class\nupper-middle-class\nuppercase\nuppercut\nuppermost\nuppers\nuppityness\nupraise\nupraised\nuprear\nupright\nuprightly\nuprightness\nuprise\nuprising\nupriver\nuproar\nuproarious\nuproot\nuprude\nups\nupscale\nupset\nupset(a)\nupshot\nupside\nupsilon\nupstage\nupstaged\nupstairs\nupstart\nupstate\nupstream\nupstroke\nuptake\nuptick\nuptodate\nuptothemoment\nuptown\nupturn\nupturned\nupupa\nupupidae\nupward\nupwards\nupwind\nur\nuracil\nural-altaic\nuralic\nurals\nurania\nuranic\nuraninite\nuranium\nuranogrraphy\nuranolite\nuranology\nuranoscopidae\nuranus\nuranyl\nurban\nurbane\nurbanely\nurbanity\nurbanization\nurbanized\nurbe\nurbent\nurbis\nurceolate\nurceole\nurceus\nurchin\nurd\nurdu\nurea\nuredinales\nuremarked\nuremia\nureter\nureteritis\nurethane\nurethra\nurethral\nurethritis\nurge\nurgency\nurgent\nurgently\nurginea\nurging\nuria\nurial\nuric\nurinal\nurinalysis\nurinary\nurine\nurn\nurnam\nurochord\nurochordata\nurocyon\nurocystis\nurodele\nurodella\nurologist\nurology\nurophycis\nuropsilus\nuropygium\nurosaurus\nursidae\nursine\nursinia\nursus\nurtica\nurticaceae\nurticales\nurtication\nurubupunga\nuruguay\nuruguayan\nus\nusage\nusance\nuse\nuseable\nused\nuseful\nusefully\nusefulness\nuseless\nuselessly\nuselessness\nusemake\nuser\nushas\nusher\nusherette\nusine\nusing\nusnea\nusneaceae\nusque\nusquebaugh\nustilaginaceae\nustilaginales\nustilaginoidea\nustilago\nustulation\nusual\nusually\nusualness\nusucapient\nusucaption\nusufruct\nusurer\nusurious\nusurp\nusurpation\nusurped\nusurper\nusury\nusus\nut\nuta\nutah\nutahan\nutahraptor\nute\nutensil\nutensils\nuterine\nuterus\nuti\nutica\nutilitarian\nutilitarianism\nutilitate\nutility\nutility(a)\nutilizable\nutilization\nutilize\nutilized\nutmost\nutnapishtim\nuto-aztecan\nutopia\nutopian\nutopianism\nutopist\nutra\nutrecht\nutricle\nutricularia\nutrillo\nutrumque\nutter\nutterance\nuttered\nutterly\nuttermost\nutu\nuvea\nuveal\nuveitis\nuvula\nuvular\nuvularia\nuvulariaceae\nuvulitis\nuxor\nuxoricide\nuxorious\nuxoriously\nuxoriousness\nuzbek\nuzbekistan\nuzi\nv\nv-day\nva\nvac\nvacancy\nvacant\nvacantly\nvacate\nvacation\nvacationer\nvacationing\nvacatur\nvaccaria\nvaccination\nvaccine\nvaccinee\nvaccinium\nvache\nvacillant\nvacillate\nvacillating\nvacillation\nvacuity\nvacuolar\nvacuolate\nvacuole\nvacuolization\nvacuometer\nvacuous\nvacuously\nvacuum\nvade\nvadium\nvaduz\nvae\nvagabond\nvagabondage\nvagabondism\nvagal\nvagary\nvagas\nvagile\nvagina\nvaginal\nvaginate\nvaginitis\nvagitus\nvagrancy\nvagrant\nvague\nvaguely\nvagueness\nvagus\nvail\nvain\nvaincre\nvainglorious\nvainglory\nvainly\nvaishnava\nvaishnavism\nvaisya\nvajra\nvakas\nvakass\nvakil\nval\nvalance\nvalde\nvaldez\nvaldosta\nvale\nvaleant\nvaleat\nvalediction\nvaledictorian\nvaledictory\nvalence\nvalencia\nvalency\nvalentine\nvaleque\nvalere\nvalerian\nvaleriana\nvalerianaceae\nvalerianella\nvalet\nvalete\nvaletudinarian\nvaletudinarianism\nvaletudinary\nvalgono\nvalhalla\nvali\nvaliant\nvaliantly\nvalid\nvalidated\nvalidation\nvalidity\nvalidly\nvaline\nvalise\nvalkyrie\nvallation\nvalletta\nvalley\nvalleys\nvallisneria\nvallum\nvalmy\nvaloir\nvalor\nvalorem\nvalorization\nvalorous\nvalparaiso\nvaluable\nvaluation\nvalue\nvalue-added\nvalued\nvalueless\nvaluelessness\nvaluer\nvalues\nvalve\nvalved\nvalvular\nvalvulitis\nvambrace\nvamoose\nvamose\nvamp\nvampire\nvampirism\nvan\nvana\nvanadate\nvanadinite\nvanadium\nvancomycin\nvancourier\nvancouver\nvanda\nvandal\nvandalism\nvandyke\nvane\nvanellus\nvanessa\nvanfos\nvanguard\nvangueria\nvanilla\nvanillin\nvanir\nvanish\nvanished\nvanishing\nvanishingly\nvanitas\nvanitatum\nvanity\nvanquish\nvantage\nvanuatu\nvapid\nvapidly\nvapor\nvaporarium\nvaporation\nvaporer\nvaporific\nvaporing\nvaporizable\nvaporization\nvaporize\nvaporizer\nvaporous\nvaporousness\nvapors\nvaquero\nvara\nvaranidae\nvaranus\nvargueno\nvariability\nvariable\nvariably\nvariance\nvariant\nvariation\nvariations\nvaricella\nvaricolored\nvaricose\nvaricosis\nvaried\nvariedness\nvariegate\nvariegated\nvariegation\nvariety\nvarietys\nvariform\nvariola\nvariometer\nvariorum\nvarious\nvariously\nvarium\nvarlet\nvarmint\nvarnish\nvarsity\nvaruna\nvary\nvarying\nvascular\nvasculitis\nvasculum\nvase\nvasectomy\nvaseline\nvasicular\nvasoconstriction\nvasoconstrictor\nvasodilation\nvasodilator\nvasomotor\nvasopressin\nvassal\nvassalage\nvast\nvastly\nvasty\nvat\nvaten\nvatican\nvaticide\nvaticinal\nvaticinate\nvaticination\nvatum\nvaudeville\nvaudevillian\nvaughan\nvault\nvaulted\nvaulter\nvaulting\nvaunt\nvaunted\nvauntingly\nvauntmure\nvaunts\nvaurien\nvaut\nvavasour\nvayu\nvb\nvc\nvd\nve\nveadar\nveal\nveas\nveau\nvecchio\nvectigal\nvectigalia\nvection\nvectitation\nvector\nvecture\nveda\nvedalia\nvedas\nvedette\nvedi\nvedic\nveer\nveering\nveery\nvega\nvegan\nvegetability\nvegetable\nvegetal\nvegetality\nvegetarian\nvegetarianism\nvegetate\nvegetation\nvegetative\nvegetive\nvegitous\nvehemence\nvehement\nvehemently\nvehicle\nvehicles\nvehicular\nvehis\nveil\nveiled\nvein\nveinal\nveined\nveinlet\nveins\nvel\nvelar\nvelcro\nveld\nveldt\nveldtschoen\nvelis\nvelit\nvelitation\nvelleity\nvellicate\nvellicating\nvellum\nvelo\nveloce\nvelocipede\nvelociraptor\nvelocity\nvelours\nveloute\nvelow\nvelumen\nveluti\nvelutinous\nvelveeta\nvelvet\nvelveteen\nvelvetleaf\nvelvety\nvena\nvenal\nvenality\nvenation\nvend\nvendaval\nvendee\nvendemiaire\nvender\nvendere\nvendetta\nvendibility\nvendible\nvendibleness\nvending\nvenditation\nvendor\nvendue\nveneer\nveneering\nvenenation\nvenenum\nvenerable\nvenerate\nveneration\nveneridae\nvenery\nvenesection\nvenetian\nveneto\nvenezuela\nvenezuelan\nvengeance\nvengeful\nveni\nveniable\nvenial\nveniam\nvenice\nveniens\nveniente\nvenir\nvenire\nvenison\nvenit\nvennel\nvenogram\nvenography\nvenom\nvenomed\nvenomous\nvenose\nvenous\nvent\nvented\nventer\nventhole\nventiduct\nventilate\nventilated\nventilation\nventilator\nventilatory\nventose\nventosity\nventpeg\nventral\nventrally\nventre\nventricle\nventricose\nventricular\nventriloquism\nventriloquist\nventure\nventuresome\nventuri\nventurous\nvenue\nvenula\nvenule\nvenus\nvenuss\nveps\nvera\nveracious\nveracity\nveracruz\nveranda\nverandah\nverapamil\nveratrum\nverb\nverba\nverbal\nverbalization\nverbally\nverbarian\nverbascum\nverbatim\nverbena\nverbenaceae\nverbera\nverbesina\nverbiage\nverbis\nverbolatry\nverborum\nverbose\nverbosely\nverboseness\nverbosity\nverbs\nverbum\nverd\nverdad\nverdandi\nverdant\nverdice\nverdict\nverdigris\nverdin\nverdine\nverditer\nverditure\nverdun\nverdure\nverdurous\nverecundiam\nverecundity\nverein\nverge\nvergeht\nvergent\nverger\nvergil\nverging\nverguenza\nveri\nveridical\nveriest\nverifiable\nverification\nverified\nverify\nverily\nverisimilar\nverisimilitude\nverita\nveritable\nveritas\nveritatem\nveritatis\nverite\nverity\nverjuice\nvermeology\nvermicelli\nvermicular\nvermiculate\nvermiculation\nvermiform\nvermifuge\nvermilion\nvermin\nverminous\nvermont\nvermonter\nvermouth\nvernacular\nvernal\nvernier\nvernunft\nvero\nverona\nveronica\nverpa\nverrazano\nverre\nverrons\nverruca\nverrucose\nversa\nversailles\nversatile\nversatility\nverse\nversed\nverses\nversicle\nversicolor\nversification\nversifier\nversion\nversity\nverso\nverst\nverstand\nversus\nvert\nverte\nvertebra\nvertebral\nvertebrata\nvertebrate\nvertebration\nvertex\nvertical\nverticality\nvertically\nvertice\nverticil\nverticillate\nverticilliosis\nverticillium\nverticity\nverticle\nvertiginous\nvertigo\nvertilabrum\nvertu\nverum\nverve\nvervet\nvervis\nvery\nvery(a)\nvesalius\nvesical\nvesicant\nvesicaria\nvesicatory\nvesicle\nvesicular\nvesiculitis\nvespa\nvespers\nvespertilio\nvespertilionidae\nvespertine\nvespid\nvespidae\nvespucci\nvespula\nvessel\nvest\nvesta\nvestal\nvested\nvestiary\nvestibular\nvestibule\nvestige\nvestigia\nvestigial\nvestment\nvestmental\nvestmented\nvestments\nvestry\nvestryman\nvestrywoman\nvesture\nvesuvian\nvesuvianite\nvesuvius\nvet\nvetch\nvetchling\nvetera\nveteran\nveterinarian\nveterinary\nveteris\nveto\nvettura\nvetturino\nveut\nveuve\nvex\nvexata\nvexation\nvexatious\nvexed\nvexillum\nvf\nvg\nvgreat\nvi\nvia\nviability\nviable\nviaduct\nvial\nvials\nviameter\nviand\nviands\nvias\nviatical\nviatication\nviaticum\nvibes\nvibrant\nvibraphone\nvibrate\nvibratile\nvibrating\nvibration\nvibrational\nvibratiuncle\nvibrato\nvibrator\nvibratory\nvibrio\nvibrionic\nvibroscope\nviburnum\nvicar\nvicar-general\nvicarage\nvicarial\nvicariate\nvicarious\nvicariously\nvicarship\nvice\nvice-presidential\nvice-regent\nvicegerency\nvicegerent\nvicenary\nvicennial\nviceregal\nviceregent\nvicereine\nviceroy\nviceroyalty\nviceroyship\nvicesimal\nvici\nvicia\nvicinage\nvicinal\nvicinism\nvicinity\nvicious\nviciously\nviciousness\nvicissim\nvicissitude\nvicissitudes\nvicksburg\nvictim\nvictimize\nvictimized\nvictis\nvictor\nvictoria\nvictoriam\nvictorian\nvictoriana\nvictories\nvictorious\nvictoriously\nvictory\nvictrix\nvictrola\nvictual\nvictualer\nvictuals\nvicugna\nvicuna\nvida\nvide\nvidelicet\nvident\nvideo\nvideocassette\nvideotape\nvideri\nvideris\nvidi\nvidrio\nvidua\nviduity\nvie\nvielle\nvienna\nviennese\nvient\nvientiane\nvietnam\nvietnamese\nvieux\nview\nviewer\nviewgraph\nviewless\nviews\nvigesimal\nvigeur\nvigil\nvigilance\nvigilant\nvigilante\nvigilantism\nvigilantly\nvigils\nvigna\nvigneron\nvignette\nvigor\nvigorous\nvigorously\nvii\nviii\nviking\nvila\nvile\nvilely\nvileness\nvilification\nvilify\nvilipend\nvilipendency\nvilla\nvillage\nvillager\nvillain\nvillainess\nvillainy\nvillanous\nvillany\nvillein\nvilleinage\nvillenage\nvilli\nvillian\nvillify\nvillous\nvillus\nvilnius\nvim\nviminaria\nvin\nvina\nvinaigrette\nvinblastine\nvinca\nvincer\nvinces\nvincetis\nvincetoxicum\nvincible\nvincit\nvincite\nvincristine\nvincture\nvinculo\nvinculum\nvindex\nvindicate\nvindicated\nvindicating\nvindication\nvindicator\nvindice\nvindictive\nvindictiveness\nvine\nvinefretter\nvinegar\nvinegariness\nvinegarroon\nvinegrub\nvinery\nvineyard\nvingtun\nvinifera\nvino\nvinous\nvintage\nvintner\nvinyl\nvinylite\nviol\nviola\nviolable\nviolaceae\nviolate\nviolating\nviolation\nviolator\nviolence\nviolent\nviolently\nviolet\nviolin\nviolinist\nvioloncello\nviolone\nviomycin\nviper\nvipera\nviperidae\nvirago\nviral\nvirent\nvireo\nvireonidae\nvires\nvirescent\nvirga\nvirgate\nvirgil\nvirgilia\nvirgilianae\nvirgin\nvirginal\nvirginals\nvirginia\nvirginian\nvirginibus\nvirginity\nvirgo\nviribus\nviricidal\nviricide\nviridescence\nviridity\nvirile\nvirilis\nvirility\nvirion\nviro\nviroid\nvirological\nvirology\nvirtility\nvirtu\nvirtual\nvirtual(a)\nvirtually\nvirtue\nvirtueless\nvirtues\nvirtuosity\nvirtuoso\nvirtuous\nvirtuously\nvirtuousness\nvirtus\nvirtute\nvirtutem\nvirtutis\nvirulence\nvirulent\nvirulently\nvirum\nvirus\nvis\nvis-a-vis\nvisa\nvisage\nvisaged\nvisavis\nvisayan\nviscaceae\nviscacha\nviscera\nvisceral\nviscerally\nviscid\nviscidity\nviscoelastic\nviscometer\nviscometric\nviscometry\nviscosity\nviscount\nviscountcy\nviscountess\nviscounty\nviscous\nviscum\nvise\nviselike\nvishnu\nvisibility\nvisible\nvisibly\nvisigoth\nvision\nvisionary\nvisionless\nvisit\nvisitation\nvisitations\nvisite\nvisiting\nvisitings\nvisitor\nvisits\nvisor\nvisored\nvista\nvistula\nvisu\nvisual\nvisualizer\nvisually\nvisum\nvisus\nvita\nvitaceae\nvitae\nvital\nvitalic\nvitalism\nvitalist\nvitality\nvitalization\nvitalize\nvitally\nvitalness\nvitals\nvitam\nvitamin\nvitant\nvitare\nvitharr\nvitia\nvitiate\nvitiated\nvitiation\nviticulture\nviticulturist\nvitiliginous\nvitiligo\nvitis\nvitium\nvitreform\nvitreous\nvitrics\nvitrification\nvitrify\nvitrine\nvitriol\nvitrite\nvittaria\nvittariaceae\nvituperate\nvituperation\nvituperative\nvituperator\nvituss\nviva\nviva-voce\nvivace\nvivacious\nvivaciously\nvivaciousness\nvivacity\nvivandiere\nvivant\nvivarium\nvive\nvivendi\nvivere\nviverra\nviverricula\nviverridae\nviverrine\nvivid\nvividly\nvivificate\nvivification\nvivified\nvivify\nvivifying\nvivimus\nviviparous\nvivisection\nvivisectionist\nvivit\nvivitque\nvivre\nvivrece\nvivvamus\nvixen\nvixenish\nvixit\nviyella\nviz\nvizier\nviziership\nvizla\nvizor\nvladivostok\nvlei\nvobis\nvobiscum\nvocable\nvocabulary\nvocal\nvocalic\nvocalism\nvocalist\nvocality\nvocalization\nvocalize\nvocally\nvocation\nvocational\nvocationally\nvocations\nvocative\nvoce\nvoces\nvociferate\nvociferation\nvociferous\nvociferously\nvocis\nvodka\nvogue\nvogul\nvoi\nvoice\nvoiced\nvoiceless\nvoid\nvoile\nvoir\nvoiturier\nvoivode\nvol\nvol-au-vent\nvoland\nvolant\nvolant(ip)\nvolapuk\nvolat\nvolatile\nvolatility\nvolatilization\nvolatilize\nvolatilized\nvolauvent\nvolcanic\nvolcanically\nvolcanism\nvolcano\nvolcanology\nvole\nvolens\nvolente\nvoles\nvolga\nvolgaic\nvolgograd\nvolie\nvolitare\nvolitation\nvolitient\nvolition\nvolitional\nvolitive\nvolk\nvolksraad\nvolley\nvolleyball\nvolo\nvolonte\nvolt\nvolt-ampere\nvoltage\nvoltaic\nvoltaire\nvoltaism\nvoltarian\nvolteface\nvoltigeur\nvoltmeter\nvolto\nvolubilis\nvolubility\nvoluble\nvolume\nvolumes\nvolumetric\nvolumetrically\nvoluminous\nvoluntarily\nvoluntariness\nvoluntary\nvoluntas\nvolunteer\nvolunteering\nvolunteers\nvoluptales\nvoluptas\nvoluptuary\nvoluptuous\nvoluptuously\nvoluptuousness\nvolutation\nvolute\nvolution\nvolva\nvolvaria\nvolvariaceae\nvolvariella\nvolvocaceae\nvolvocales\nvolvox\nvombatidae\nvomica\nvomit\nvomition\nvomitory\nvoodoo\nvoodooism\nvoorlooper\nvoortrekker\nvoracious\nvoraciously\nvoracity\nvorspielgerman\nvorstellen\nvorstellung\nvortex\nvortical\nvorticella\nvorticose\nvos\nvosky\nvostro\nvotary\nvote\nvoter\nvoting\nvotis\nvotive\nvoto\nvotograph\nvouch\nvouchee\nvoucher\nvouchsafe\nvouchsafement\nvouge\nvouloir\nvoulu\nvous\nvoussoir\nvow\nvowel\nvowellike\nvows\nvox\nvoxfaucibus\nvoyage\nvoyager\nvoyeur\nvoyeurism\nvoyeuristic\nvoyeuristically\nvr\nvraitsemblance\nvrouw\nvshillyshally\nvt\nvue\nvulcan\nvulcanization\nvulgar\nvulgaria\nvulgarian\nvulgarism\nvulgarity\nvulgate\nvulgum\nvulgus\nvulguslat\nvull\nvulnerability\nvulnerable\nvulnerably\nvulnerary\nvulnus\nvulpes\nvulpine\nvult\nvultu\nvultur\nvulture\nvultus\nvulva\nvulvar\nvulvectomy\nvulvitis\nvwith\nvying\nwa\nwabash\nwabble\nwad\nwadding\nwaddle\nwaddy\nwade\nwadi\nwading\nwafer\nwaffle\nwaft\nwafted\nwafture\nwag\nwage\nwage-earning\nwager\nwages\nwaggery\nwaggish\nwaggishly\nwaggishness\nwaggle\nwaggon\nwagner\nwagnerian\nwagon\nwagoner\nwagonette\nwagonload\nwagonwright\nwagram\nwags\nwagtail\nwahabi\nwahoo\nwahr\nwahrheil\nwahrheit\nwai\nwaif\nwaifs\nwail\nwailing\nwain\nwainscot\nwaist\nwaist-deep\nwaistcoat\nwait\nwaiter\nwaiting\nwaitress\nwaits\nwaive\nwakashan\nwake\nwakeful\nwakefulness\nwaking\nwaking(a)\nwalapai\nwalbiri\nwaldgrave\nwale\nwaler\nwales\nwalhall\nwali\nwalk\nwalk-in(a)\nwalk-on\nwalk-to(a)\nwalk-up\nwalkabout\nwalked\nwalker\nwalkie-talkie\nwalking\nwalkon\nwalkout\nwalkover\nwalks\nwall\nwall-less\nwallaby\nwallace\nwallah\nwallboard\nwalled\nwallet\nwalleye\nwalleyed\nwallflower\nwallop\nwalloper\nwallow\nwallpaper\nwalls\nwallsend\nwalnut\nwalnuts\nwalpole\nwalrus\nwaltz\nwamble\nwampanoag\nwampum\nwampumpeag\nwan\nwand\nwander\nwanderer\nwandering\nwanderlust\nwanders\nwandflower\nwane\nwangan\nwangle\nwanigan\nwaning\nwanly\nwant\nwanted\nwanting\nwantless\nwanton\nwantonly\nwants\nwapentake\nwapiti\nwar\nwar-torn\nwarant\nwaratah\nwarble\nwarbler\nwarcry\nward\nwarden\nwardenship\nwarder\nwardership\nwardmote\nwardress\nwardrobe\nwardroom\nwardship\nware\nwarehouse\nwarehouser\nwareroom\nwarf\nwarfare\nwarfarin\nwarhead\nwarhorse\nwarily\nwariness\nwarji\nwarlike\nwarlock\nwarlord\nwarm\nwarm-blooded\nwarm-toned\nwarm-up\nwarmed\nwarmhearted\nwarmheartedness\nwarming\nwarmongering\nwarmth\nwarn\nwarned\nwarning\nwarp\nwarpath\nwarped\nwarrant\nwarranted\nwarrantee\nwarranty\nwarren\nwarrener\nwarrigal\nwarrior\nwarsaw\nwarship\nwart\nwarthog\nwartime\nwarwhoop\nwary\nwas\nwash\nwash-and-wear\nwashable\nwashbasin\nwashboard\nwashcloth\nwashday\nwashed\nwasher\nwasherman\nwasherwoman\nwashhouse\nwashing\nwashing-up\nwashington\nwashingtonian\nwashout\nwashroom\nwashstand\nwashtub\nwashwoman\nwashy\nwasp\nwaspish\nwasps\nwassail\nwassailer\nwastage\nwaste\nwasted\nwasteful\nwastefully\nwastepaper\nwasting\nwastrel\nwat\nwatch\nwatchband\nwatchdog\nwatched\nwatches\nwatchett\nwatchful\nwatchfulness\nwatchmaker\nwatchman\nwatchtower\nwatchword\nwater\nwater-cooled\nwater-rate\nwater-repellent\nwater-shield\nwater-skiing\nwater-washed\nwaterborne\nwaterbuck\nwatercolor\nwatercolorist\nwatercourse\nwatercraft\nwatercress\nwaterdog\nwaterdrinker\nwatered\nwaterfall\nwaterfowl\nwaterfront\nwateriness\nwatering\nwaterleaf\nwaterline\nwaterlogged\nwaterloo\nwaterman\nwatermark\nwatermeal\nwatermelon\nwatermint\nwaterpower\nwaterproof\nwaterproofing\nwaters\nwatershed\nwaterside\nwaterskin\nwaterspout\nwatertight\nwaterway\nwaterweed\nwaterwheel\nwaterworks\nwaterworn\nwatery\nwats\nwatt\nwatt-hour\nwattle\nwave\nwaveguide\nwavelength\nwaver\nwaverer\nwavering\nwaves\nwaveson\nwaving\nwavy\nwaw\nwax\nwax-chandler\nwaxed\nwaxen\nwaxflower\nwaxing\nwaxlike\nwaxmallow\nwaxwing\nwaxwork\nwaxy\nwaxycap\nway\nwayfarer\nwayfaring\nwayland\nwaylay\nwayless\nways\nwayside\nwayward\nwayworn\nwayzgoos\nwb\nwc\nwd\nwe\nweak\nweak-kneed\nweaken\nweakened\nweakening\nweaker\nweakest\nweakfish\nweakhearted\nweakling\nweakly\nweakminded\nweakness\nweal\nweald\nwealth\nwealthily\nwealthy\nwean\nweaned\nweaning\nweanling\nweapon\nweaponless\nweaponry\nweapons\nwear\nwearable\nwearer\nwearily\nweariness\nwearing\nwearisome\nwearisomeness\nwears\nweary\nwearying\nweasand\nweasel\nweather\nweather-beaten\nweather-bound\nweather-stripped\nweatherbeaten\nweathercock\nweathered\nweatherglass\nweatherman\nweatherproof\nweathervane\nweatherwise\nweave\nweaver\nweaving\nweazen\nweb\nweb-footed\nwebbed\nwebbing\nweber\nwebfoot\nwebfooted\nwebster\nwebwork\nwebworm\nwed\nwedded\nwedding\nwedge\nwedged\nwedges\nwedgeshaped\nwedgie\nwedgwood\nwedlock\nwednesday\nwee\nweed\nweeded\nweeding\nweedless\nweeds\nweedy\nweek\nweekday\nweekend\nweekender\nweekly\nweeknight\nweel\nween\nweep\nweeper\nweepiness\nweeping\nweepy\nweet\nweetless\nweetweet\nweevil\nweft\nweigela\nweigh\nweighbridge\nweighed\nweighing\nweight\nweighted\nweightily\nweightless\nweightlifter\nweightlifting\nweights\nweighty\nweile\nweimar\nweimaraner\nweir\nweird\nweirdly\nweismannism\nweiss\nweisshorn\nweit\nweka\nwelcher\nwelcome\nwelcomed\nwelcomed(a)\nwelcomeness\nwelcoming\nweld\nwelder\nwelding\nweldment\nwelfare\nwelfarist\nwelkin\nwell\nwell(p)\nwell-adjusted\nwell-advised\nwell-appointed\nwell-balanced\nwell-behaved\nwell-being\nwell-bound\nwell-bred\nwell-conducted\nwell-defined\nwell-done\nwell-fed\nwell-intentioned\nwell-knit\nwell-known(a)\nwell-lined\nwell-made\nwell-marked\nwell-meaning\nwell-mined\nwell-off\nwell-ordered\nwell-preserved\nwell-proportioned\nwell-qualified\nwell-read\nwell-spoken\nwell-turned\nwell-wishing\nwell-wishing(a)\nwell-worn\nwelladay\nwelladvised\nwellaffected\nwellaway\nwellbehaved\nwellbeing\nwellborn\nwellbred\nwellbrought\nwellcomposed\nwelldefined\nwelldevised\nwelldoing\nwelldrawn\nwellerism\nwellfavored\nwellfed\nwellformed\nwellfounded\nwellget\nwellgred\nwellgroomed\nwellgrounded\nwellhead\nwellington\nwellintentioned\nwellknit\nwellknown\nwelllaid\nwellmade\nwellmeaning\nwellmeant\nwellnatured\nwellness\nwellnigh\nwellproportioned\nwellprovided\nwellregulated\nwellrounded\nwellspent\nwellspring\nwellsprings\nwellstocked\nwelltasted\nwelltimed\nwelltrodden\nwellweighed\nwellwisher\nwellwooded\nwellworn\nwelsh\nwelsher\nwelshman\nwelt\nweltanschauung\nwelter\nweltergesicht\nwelterweight\nweltgericht\nweltgeschichte\nwelwitschia\nwelwitschiaceae\nwem\nwembly\nwen\nwench\nwenching\nwend\nwere\nweregild\nwerewolf\nwergild\nwerowance\nwerth\nwesley\nwesleyan\nwesleyanism\nwest\nwest-central\nwest-sider\nwestbound\nwesterly\nwestern\nwesterner\nwesternization\nwesternmost\nwestley\nwestminster\nwestside\nwestward\nwet\nwether\nwetland\nwetness\nwetting\nwf\nwg\nwh\nwha\nwhack\nwhacked\nwhacker\nwhacking\nwhale\nwhaleboat\nwhalebone\nwhaler\nwhalesucker\nwham\nwhap\nwharf\nwharfage\nwhat\nwhatchacallim\nwhatchamacallit\nwhatever\nwhats\nwhatsoever\nwhean\nwheat\nwheatear\nwheaten\nwheatfield\nwheatflake\nwheatgrass\nwheatworm\nwheedle\nwheedling\nwheel\nwheelbarrow\nwheelbase\nwheelchair\nwheeled\nwheeler\nwheeling\nwheelless\nwheelman\nwheels\nwheelwork\nwheelwright\nwheeze\nwheezily\nwheeziness\nwheezy\nwhelk\nwhelm\nwhelp\nwhelped\nwhen\nwhence\nwhenever\nwhensoever\nwhere\nwhereabout\nwhereabouts\nwhereas\nwhereat\nwhereby\nwherefore\nwherein\nwhereness\nwhereof\nwhereon\nwheresoever\nwhereto\nwhereunto\nwhereupon\nwherever\nwherewith\nwherewithal\nwherret\nwherry\nwhet\nwhether\nwhetstone\nwhew\nwhewell\nwhey\nwhich\nwhif\nwhiff\nwhiffet\nwhiffle\nwhiffletree\nwhig\nwhigs\nwhile\nwhilom\nwhilst\nwhim\nwhimper\nwhims\nwhimsey\nwhimsical\nwhimsicality\nwhimsy\nwhimwham\nwhin\nwhinchat\nwhine\nwhiner\nwhining\nwhiningly\nwhinyard\nwhip\nwhip-round\nwhipcord\nwhipe\nwhiplash\nwhipper\nwhipper-in\nwhippersnapper\nwhippet\nwhipping\nwhippoorwill\nwhipscorpion\nwhipsnake\nwhipster\nwhipstitch\nwhiptail\nwhir\nwhirl\nwhirligig\nwhirling\nwhirlpook\nwhirlpool\nwhirls\nwhirlwind\nwhirlwinds\nwhirr\nwhirring\nwhish\nwhisk\nwhisker\nwhisket\nwhiskey\nwhisky\nwhisper\nwhispered\nwhisperer\nwhisperings\nwhissky\nwhist\nwhistle\nwhistler\nwhistling\nwhit\nwhit-tuesday\nwhite\nwhite-collar\nwhite-hot\nwhite-lipped\nwhite-tie\nwhitebait\nwhitecap\nwhitecaps\nwhitechapel\nwhitecup\nwhited\nwhiteface\nwhitefish\nwhitefly\nwhitehead\nwhitehorse\nwhitelash\nwhitelivered\nwhiten\nwhiteness\nwhitening\nwhiteout\nwhites\nwhitethorn\nwhitewash\nwhitewashed\nwhitewasher\nwhitewashing\nwhitey\nwhither\nwhiting\nwhitish\nwhitleather\nwhitlowwort\nwhitmonday\nwhitney\nwhitsun\nwhitsuntide\nwhitter\nwhittier\nwhittle\nwhittled\nwhitworth\nwhiz\nwhizbang\nwhizzing\nwho\nwhoa\nwhole\nwholeheartedly\nwholeheartedness\nwholeness\nwholesale\nwholesome\nwholesomely\nwholesomeness\nwholesouled\nwholly\nwhom\nwhomp\nwhoop\nwhoopee\nwhooper\nwhooping\nwhoops\nwhoosh\nwhop\nwhopper\nwhopping\nwhore\nwhoredom\nwhorehouse\nwhoremaster\nwhoremonger\nwhorl\nwhos\nwhose\nwhy\nwhydah\nwi\nwibblewabble\nwichita\nwick\nwicked\nwickedly\nwickedness\nwicker\nwicket\nwicket-keeper\nwickiup\nwide\nwide-open\nwide-ranging\nwide-screen(a)\nwideawake\nwidely\nwiden\nwideness\nwidening\nwidespread\nwidewasting\nwidgeon\nwidow\nwidowed\nwidower\nwidowhood\nwidth\nwie\nwiedersehen\nwield\nwieldy\nwierdo\nwiesenboden\nwife\nwifeism\nwifeless\nwifely\nwifery\nwiffle\nwig\nwigged\nwigging\nwiggle\nwiggler\nwiggliness\nwiggly\nwight\nwigless\nwigmaker\nwigwam\nwild\nwild-eyed\nwildcat\nwildcatter\nwilderness\nwildest\nwildfire\nwildflower\nwildfowl\nwildgoose\nwilding\nwildlife\nwildly\nwildness\nwile\nwiles\nwilig\nwilkins\nwill\nwill-o'-the-wisp\nwillet\nwillful\nwillfully\nwilling\nwillingfully\nwillingly\nwillingness\nwillis\nwillothe\nwillow\nwillowherb\nwillowware\nwilly\nwilly-nilly\nwilmington\nwilson\nwilsonian\nwilt\nwilted\nwilton\nwily\nwimble\nwimp\nwimpish\nwimple\nwimpy\nwin\nwince\nwincey\nwinceyette\nwinch\nwincing\nwind\nwindage\nwindblown\nwindbound\nwindbreak\nwinded\nwinder\nwindfall\nwindflower\nwindgauge\nwindiness\nwinding\nwindings\nwindjammer\nwindlass\nwindless\nwindmill\nwindow\nwindow-washing\nwindowpane\nwindows\nwindowsill\nwindpipe\nwinds\nwindshield\nwindsock\nwindsor\nwindstorm\nwindswept\nwindup(a)\nwindward\nwindwards\nwindy\nwine\nwineberry\nwinebibber\nwinebibbing\nwineglass\nwinemaking\nwinepress\nwinery\nwinesap\nwineskin\nwing\nwingback\nwinged\nwinger\nwingless\nwinglike\nwingman\nwingnut\nwings\nwingspan\nwingspread\nwingstem\nwink\nwinker\nwinking\nwinks\nwinless\nwinnebago\nwinner\nwinning\nwinnings\nwinnipeg\nwinnow\nwins\nwinsome\nwinsomely\nwinsomeness\nwinston-salem\nwinter\nwinter(a)\nwinteraceae\nwintergreen\nwinters\nwintry\nwintun\nwiny\nwipe\nwiper\nwird\nwire\nwire-haired\nwire-puller\nwired\nwiredrawn\nwirehair\nwireless\nwirepuller\nwires\nwiretap\nwirework\nwireworm\nwiring\nwiry\nwis\nwisconsin\nwisconsinite\nwisdom\nwise\nwiseacre\nwisecrack\nwisely\nwisent\nwiser\nwish\nwish-wash\nwishbone\nwished\nwishes\nwishful\nwishfully\nwishfulness\nwishing\nwishingcap\nwishwash\nwishywashy\nwisk\nwisket\nwisp\nwisplike\nwisteria\nwistful\nwistfully\nwistfulness\nwit\nwitan\nwitch\nwitch-hunt\nwitch-hunter\nwitchcraft\nwitchery\nwitches\nwitchgrass\nwitching\nwitchlike\nwitcracker\nwitenagemote\nwith\nwithal\nwithdraw\nwithdrawal\nwithdrawing\nwithe\nwither\nwitherd\nwithered\nwithering\nwitheringly\nwithers\nwitherso\nwithhold\nwithholding\nwithim\nwithin\nwithinside\nwithout\nwithstand\nwithtip\nwithy\nwitless\nwitling\nwitness\nwitnessed\nwitnesses\nwits\nwitsnapper\nwitted\nwittgenstein\nwittgensteinian\nwitticism\nwittily\nwittiness\nwitting\nwittingly\nwittol\nwitty\nwitworm\nwive\nwives\nwiz\nwizard\nwizen\nwizened\nwj\nwk\nwl\nwm\nwoad\nwobble\nwobbler\nwobbling\nwoden\nwoe\nwoebegone\nwoeful\nwoefully\nwok\nwold\nwolf\nwolffia\nwolffiella\nwolffish\nwolfhound\nwolflike\nwolframite\nwolfsbane\nwolof\nwolverine\nwoman\nwomanhood\nwomanish\nwomanizer\nwomankind\nwomanlike\nwomanliness\nwomanly\nwomb\nwombat\nwommerah\nwon\nwonder\nwonder-struck\nwonderful\nwonderfully\nwonderland\nwonderment\nwonders\nwonderworking\nwondrous\nwont\nwonted\nwoo\nwood\nwoodbine\nwoodborer\nwoodcarver\nwoodcarving\nwoodchuck\nwoodcock\nwoodcraft\nwoodcut\nwoodcutter\nwooded\nwooden\nwoodfrog\nwoodgrain\nwoodhewer\nwoodlands\nwoodlouse\nwoodnote\nwoodpecker\nwoodpile\nwoodruff\nwoods\nwoodscrew\nwoodshed\nwoodsia\nwoodsman\nwoodsy\nwoodwardia\nwoodwaxen\nwoodwind\nwoodwork\nwoodworker\nwoodworm\nwoody\nwooer\nwoof\nwoofer\nwooing\nwool\nwoolen\nwoolgathering\nwoolly\nwoolpack\nwoolsack\nwooly\nwoon\nwop\nworcester\nword\nword-painter\nwordbook\nwordcatcher\nwordds\nwordfence\nwording\nwordless\nwordnet\nwordplay\nwords\nwordsflux\nwordsmith\nwordsworth\nwordsworthian\nwordsworthl\nwordy\nwork\nwork-clothing\nwork-in\nworkaday\nworkbasket\nworkbench\nworkboard\nworkbook\nworkday\nworked\nworker\nworkhorse\nworkhouse\nworking\nworking(a)\nworkings\nworkload\nworkman\nworkmanlike\nworkmanship\nworkmate\nworkpiece\nworkplace\nworkroom\nworks\nworksheet\nworkshop\nworkspace\nworkstation\nworktable\nworkweek\nworkwoman\nworld\nworld-weariness\nworldliness\nworldling\nworldly\nworldly-wise\nworldlywise\nworldmineral\nworlds\nworldwide\nworldwideness\nworldwithoutend\nworm\nwormcast\nwormeaten\nwormfish\nwormhole\nworms\nwormwood\nworn\nworried\nworriedly\nworrier\nworry\nworrying\nworryingly\nworse\nworsened\nworsening\nworship\nworshiper\nworshipful\nworshiping\nworshipper\nworshipping\nworst\nworsted\nwort\nworth\nworth(p)\nworthily\nworthiness\nworthless\nworthlessly\nworthlessness\nworthwhile\nworthwhileness\nworthy\nwot\nwotan\nwould\nwouldbe\nwound\nwounded\nwounds\nwoven\nwow\nwrack\nwraith\nwrangle\nwrangler\nwrangling\nwrap\nwraparound\nwrapped\nwrapper\nwrapping\nwraprascal\nwrapt\nwrasse\nwrath\nwrathful\nwrathfully\nwreak\nwreath\nwreathe\nwreathed\nwreathy\nwreck\nwreckage\nwrecked\nwrecker\nwren\nwren-tit\nwrench\nwrest\nwrestle\nwrestler\nwrestles\nwrestling\nwretch\nwretched\nwretchedly\nwretchedness\nwriggle\nwright\nwring\nwringer\nwringing\nwrinkle\nwrinkled\nwrist\nwristband\nwristlet\nwristwatch\nwrit\nwrite\nwrite-off\nwriter\nwriterr\nwrithe\nwriting\nwritings\nwritten\nwroclaw\nwrong\nwrong-side-out(p)\nwrongdoer\nwrongdoing\nwronged\nwrongful\nwrongheaded\nwrongheadedly\nwrongly\nwrongness\nwrongs\nwrote\nwrought\nwry\nwryly\nwrymouth\nwryneck\nwth\nwu\nwurzburg\nwuther\nwyethia\nwynd\nwynnea\nwyoming\nwyomingite\nwyrd\nwyvern\nx\nx-axis\nx-raying\nxanorphica\nxanthein\nxanthian\nxanthic\nxanthin\nxanthite\nxanthium\nxanthocarpous\nxanthochroid\nxanthocyanopia\nxanthoma\nxanthomonad\nxanthomonas\nxanthophyceae\nxanthophyll\nxanthopous\nxanthopsia\nxanthorrhoeaceae\nxanthorroea\nxanthosis\nxanthosoma\nxanthous\nxantippe\nxantusiidae\nxebec\nxenarthra\nxenicidae\nxenicus\nxenodochium\nxenogenesis\nxenogenetic\nxenogeny\nxenon\nxenophobia\nxenophobic\nxenopodidae\nxenopus\nxenorhyncus\nxenosauridae\nxenosaurus\nxenotime\nxeranthemum\nxeres\nxeric\nxerobates\nxerographic\nxerography\nxerophagy\nxerophyllum\nxerophytic\nxerox\nxhosa\nxi\nxian\nxiphias\nxiphiidae\nxiphoid\nxiphosura\nxrated\nxray\nxvi\nxviii\nxylaria\nxylariaceae\nxylem\nxylene\nxylocopa\nxylograph\nxylography\nxylomelum\nxylophagous\nxylophone\nxylophonist\nxylopia\nxylosma\nxyphophorus\nxyridaceae\nxyridales\nxyris\ny\ny-axis\nyacca\nyach\nyacht\nyachting\nyachtsman\nyade\nyaffle\nyager\nyagi\nyah\nyahi\nyahoo\nyahweh\nyajur-veda\nyak\nyakut\nyalta\nyalu\nyam\nyama\nyamen\nyammer\nyana\nyanan\nyang\nyangtze\nyank\nyankee\nyaounde\nyap\nyard\nyardage\nyardarm\nyarder\nyardgrass\nyardie\nyardline\nyardman\nyardmaster\nyardsc\nyardstick\nyare\nyarmulke\nyarn\nyarn-spinning\nyarr\nyarrow\nyashmak\nyatacban\nyataghan\nyaup\nyautia\nyavapai\nyaw\nyawl\nyawn\nyawning\nyawp\nyaws\nyay\nye\nyea\nyeah\nyean\nyear\nyear-end\nyear-round\nyearbook\nyearling\nyearlong\nyearly\nyearn\nyearning\nyears\nyeast\nyeasty\nyeats\nyeatsian\nyeh\nyeild\nyeleped\nyell\nyelling\nyellow\nyellow-green\nyelloweyed\nyellowfin\nyellowflag\nyellowhammer\nyellowlegs\nyellowness\nyellows\nyellowstone\nyellowtail\nyellowthroat\nyellowwood\nyelp\nyemen\nyemeni\nyen\nyenisei\nyeniseian\nyenta\nyeoman\nyeomanry\nyeomans\nyeomen\nyep\nyerk\nyerupaja\nyes\nyesterday\nyesterdays\nyesteryear\nyet\nyeux\nyew\nyiddish\nyield\nyieldance\nyielding\nyieldingness\nyip\nymir\nyo\nyo-yo\nyodel\nyodeling\nyodeller\nyodh\nyoga\nyogi\nyogistic\nyogurt\nyoho\nyoicks\nyoke\nyokel\nyokel-like\nyokemate\nyokuts\nyolk\nyon\nyonder\nyore\nyork\nyorkshire\nyorktown\nyoruba\nyosemite\nyou\nyoung\nyoung-begetting(a)\nyounger\nyoungish\nyoungness\nyoungster\nyoungun\nyounker\nyour\nyoure\nyours\nyourself\nyouth\nyouthful\nyouthfully\nyouthhood\nyow\nyowl\nypres\nytterbium\nyttrium\nyuan\nyucatan\nyucatec\nyucca\nyuck\nyue\nyugoslav\nyugoslavia\nyugoslavian\nyukon\nyule\nyuletide\nyum\nyuma\nyuman\nyunnan\nyup\nyuppie\nyurt\nz\nz-axis\nza\nzaar\nzabaglione\nzadkiel\nzaffer\nzaglossus\nzagreb\nzaire\nzairean\nzairese\nzalophus\nzama\nzambezi\nzambia\nzambian\nzambo\nzambomba\nzamia\nzamiaceae\nzamiel\nzannichellia\nzannichelliaceae\nzantedeschia\nzanthoxylum\nzany\nzanzibar\nzaofulvin\nzap\nzapodidae\nzapus\nzaragoza\nzarf\nzarpall\nzayin\nzb\nzc\nzd\nze\nzea\nzeal\nzealand\nzealander\nzealot\nzealotry\nzealous\nzealously\nzebra\nzebrawood\nzebrule\nzebu\nzee\nzeidae\nzeitgeist\nzembla\nzemindar\nzemindary\nzen\nzenaidura\nzenana\nzendavest\nzenith\nzenithal\nzeno\nzeomorphi\nzephyr\nzephyrs\nzeppelin\nzero\nzero(a)\nzerronnen\nzest\nzestful\nzestfully\nzeta\nzetetic\nzeugma\nzeus\nzhuang\nzieht\nzigadenus\nzigzag\nzilch\nzill\nzillion\nzimbabwe\nzimbabwean\nzimmermann\nzinc\nzincograph\nzincography\nzinfandel\nzing\nzingano\nzingaro\nzingiber\nzingiberaceae\nzinjanthropus\nzinkenite\nzinnia\nzinnwaldite\nzion\nzionism\nzionist\nzip\nziphiidae\nzipper\nzippo\nzircon\nzirconium\nzither\nziti\nzizania\nziziphus\nzloty\nzoanthropy\nzoarces\nzoarcidae\nzocle\nzodiac\nzodiacal\nzoetic\nzoic\nzoilus\nzola\nzolaesque\nzollverein\nzomba\nzombi\nzombie\nzonal\nzonam\nzonary\nzone\nzoning\nzonk\nzonotrichia\nzoo\nzoogloea\nzoogloeic\nzoogloeoid\nzoography\nzoohygiantics\nzooid\nzoolatry\nzoological\nzoologist\nzoology\nzoom\nzoomastigina\nzoomastigote\nzoonomy\nzoonosis\nzoophobia\nzoophorus\nzoophyte\nzooplankton\nzoospore\nzoothapsis\nzootomy\nzoril\nzoroaster\nzoroastrian\nzoroastrianism\nzostera\nzosteraceae\nzouave\nzounds\nzoysia\nzu\nzucchini\nzulu\nzurich\nzweierleiger\nzwieback\nzwischen\nzygnema\nzygnemataceae\nzygnematales\nzygocactus\nzygodactyl\nzygoma\nzygomorphic\nzygomycetes\nzygomycota\nzygophyllaceae\nzygophyllum\nzygoptera\nzygote\nzygotene\nzygotic\nzymase\nzymosis\nzymotic\nzymurgy"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/timezone/index.html",
    "content": "<!--\r\n# To the extent possible under law, Eric Muller has waived all\r\n# copyright and related or neighboring rights to the efele.net/tz maps\r\n# (comprising the shapefiles, the web pages describing them and the scripts\r\n# and data used to build them. This work is published from the United States of\r\n# America.\r\n#\r\n# See http://creativecommons.org/publicdomain/zero/1.0/ for more details.\r\n-->\r\n\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n<title>A shapefile of the TZ timezones of the world</title>\r\n<style type=\"text/css\">\r\n<!--\r\n.style4 {font-size: small}\r\n-->\r\n</style>\r\n</head>\r\n\r\n<body>\r\n<h2 align='center'>tz_world, an efele.net/tz map</h2>\r\n<h3 align='center'>A shapefile of the TZ timezones of the world</h3>\r\n<p align='center'>Last data update: November 26, 2013</p>\r\n<p align='center'>Last page update: July 5, 2013</p>\r\n<p>&nbsp;</p>\r\n<p>The <a href=\"tz_world.zip\">tz_world</a> shapefile captures the boundaries of the TZ timezones across the world, as of TZ 2013b. The geometries are all POLYGONs, and a TZ timezone will sometimes have multiple polygons. There are about 28,000 rows.</p>\r\n<p>The <a href=\"tz_world_mp.zip\">tz_world_mp</a> shapefile  captures the same boundaries. The geometries are either POLYGONs or MULTIPOLYGONs, and there is a single geometry for each TZ timezone.</p>\r\n<p>There is a companion map for the TZ timezones used in <a href=\"../antarctica\">Antarctica</a> stations.</p>\r\n<p>The geometries are primarily derived from the <a href=\"/maps/fips-10/map\">fip10s</a> data (itself derived from the VMAP0 data), augmented with data presented in the pages for the maps of the <a href=\"../us\">United States</a>, <a href='../canada'>Canada</a>, <a href=\"../russia\">Russia</a> and <a href='../china'>China</a>.</p>\r\n<h3>Snapshot of the zones</h3>\r\n<p>This is a snaphsot of the zones (many of the smaller zones are not visible at this scale):</p>\r\n<p align=\"center\"><img src=\"tz_world.png\" /></p>\r\n\r\n<h3>&nbsp;</h3>\r\n<h3>Timezones at sea</h3>\r\n<p>The tz database says: “A ship within the territorial  waters of any nation uses that nation's time. In international  waters, time zone boundaries are meridians 15° apart, except that  UTC−12 and UTC+12 are each 7.5° wide and are separated by  the 180° meridian (not by the International Date Line, which is  for land and territorial waters only). A captain can change ship's  clocks any time after entering a new time zone; midnight changes are  common.” </p>\r\n<p>While the boundaries in international waters are not difficult to construct, the boundaries of territorial waters are a completely different story, and are similar to the boundaries between countries. Unfortunately, VMAP0 does not provide geometries for the territorial waters. As a consequence, the shapefiles presented here do not cover seas and oceans.</p>\r\n<p>&nbsp;</p>\r\n<h3>Logical description of the zones</h3>\r\n<p>The vast majority of the tz timezones are by construction matching a country, so little needs to be said about them: we equate them with the corresponding region in our fips10s data. We also discussed the situation of the  <a href=\"../us\">United States</a>, <a href='../canada'>Canada</a>, <a href='../russia'>Russia</a> and <a href='../china'>China</a> in other pages. That leaves only a few countries with multiple time zones, and again, most of them are straightforward, with timezone matching administrative divisions of the countries: the correspondance is documented in the script we use to build the shapefile (see below). The rest of this section discusses the remaining cases, where the definition of the extent of a zone is open to interpretation.</p>\r\n<h4>Uzbekistan</h4>\r\n<p>This country is covered by two tz timezones, <em>Tashkent</em> and <em>Samarkand</em>. There is not enough information in the tz data to figure out how Uzbekistan is divided between them. We use a separation along first-level administrative divisons that matches WTE.</p>\r\n<h4>Ukraine</h4>\r\n<p>This country is covered by four tz timezones. There is not enough information in the tz data to figure out how Ukraine is divided between them. We use a separation along first-level administrative divisons that matches WTE.</p>\r\n<h4>Australia</h4>\r\n<p>The extent of the <em>Eucla</em> zone is not clear from the tz data. The only clue is that the timezone ends just east of Caiguna. We arbitrarily make that zone the intersection of a rectangle and   Western Australia: Caiguna is a 125.490E, so we use somewhat   arbitrarily 125.5E as the west boundary; the north boundary is   arbitrarily -31.3S; the south boundary is the ocean; the east   boundary is the Western Australia/South Australia border.</p>\r\n<p>The <em>Lindeman</em> zone is said by tz to include the three islands of  Hayman, Lindeman, Hamilton, all in Queensland. There are other islands in this area, but we don't include them, for lack of better information.</p>\r\n<p>The <em>Broken_Hill</em> timezone is defined by <a href=\"http://www.lawlink.nsw.gov.au/Lawlink/cru/ll_cru.nsf/pages/cru_daylightsaving\">law</a> as the county of Yancowinna, New South Wales. The geometry was obtained by georeferencing and tracing of this <a href=\"http://en.wikipedia.org/wiki/Image:New_South_Wales_cadastral_divisions.png\">image of the counties</a> of New South Wales</p>\r\n<h4>Mongolia</h4>\r\n<p>It is difficult to obtain reliable and verifiable information on time zones in Mongolia: see this <a href=\"http://statoids.com/tmn.html\">summary</a>. We use the following setting:</p>\r\n<ul>\r\n  <li><em>Asia/Choibalsan</em>: Dornod (MG06), Suhbaatar (MG17)</li>\r\n  <li><em>Asia/Hovd</em>: Bayan-Olgiy (MG03), Dzavhan (MG09), Govi-Altay (MG10), Hovd (MG12), Uvs (MG19)</li>\r\n  <li><em>Asia/Ulaanbaatar</em>: rest of Mongolia<br />\r\n  </li>\r\n</ul>\r\n<h4>Cuba</h4>\r\n<p>According to <a href=\"http://www.timegenie.com/country.time/cu/\">this report</a>, </p>\r\n<blockquote>\r\n  <p>Regardless of what Cuba does, the U.S. Naval  Base Guantanamo Bay will always be in the Eastern Time zone and will always  observe daylight saving time as this allows the naval base to remain in synch with their  headquarters in Florida. This information was confirmed with the assistance of  the Public Affairs Officer at the <a href=\"http://www.nsgtmo.navy.mil/\" title=\"U.S. Naval Base Guantanamo Bay\">U.S. Naval Base Guantanamo Bay</a>.</p>\r\n</blockquote>\r\n<p>Accordingly, we assign it the<em> America/New_York</em> timezone.</p>\r\n<h4>Marshall Islands</h4>\r\n<p>The <em> Kwajalein</em> zone is certainly no less than the 11 islands leased by the US on the Kwajalein atoll. It’s also very unlikely to be more than the Kwajalein atoll itself. We use the whole atoll as the extent of the zone.</p>\r\n\r\n<h4>Spain</h4>\r\n<p>Most of Spain is easy. The open question concerns the <a href=\"http://en.wikipedia.org/wiki/Plaza_de_soberanía\">Plazas de  soberania</a>. Clearly, Ceuta is in Africa/Ceuta, but the timezone of Melilla, Chafarinas, Peñón de Alhucemas, and Peñón de Vélez de la Gomera is unclear. Our base map does not have areas for the last two, and we use Africa/Ceuta for Melilla and the Chafarinas.</p>\r\n\r\n<h4>Antarctica</h4>\r\n<p>In this map, we consider all of Antarctica to be uninhabited. The companion map <a href=\"../antarctica\">Antarctica</a> captures the permanent bases and the TZ timezones they use.</p>\r\n\r\n<h4>Greenland</h4>\r\n<p>The boundaries between the four zones are somewhat arbitrary.</p>\r\n<h4>Mexico</h4>\r\n\r\n<p>We use a somewhat arbitrary boundary between the America/Tijuana\r\nand America/Santa_Isabel timezones. The former is defined as the zone\r\nwithin 20 km of the US border, the town of Ensada and \"towards the\r\ninterior of the country\". Similarly, we use a somewhat arbitrary boundary between America/Bahia_Banderas and America/Mazatlan.</p>\r\n\r\n<h3>Construction of the shapefile</h3>\r\n\r\n<p>The zip of the <a href=\"tz_world_ingredients.zip\">ingredients</a> contains a script to build the map from the source, as well as a .prj file for the source map.</p>\r\n\r\n<p>For the United States, Russia and China, we started from the tz maps for those countries and turned them manually into masks; the idea is to retain from them only the boundaries that are not present in our base map. Those mask shapefiles are in the ingredients.</p>\r\n\r\n<p>For Mexico, we use a mask that captures the 20km zone next to the US boundary, extended to include the town of Ensanada.</p>\r\n\r\n<p>For Canada, we use directly the masks that served to create the Canada map.</p>\r\n\r\n<p>For Brazil, we use a mask built from 1) the great circle from Tabatinga to Porto Acre to divide east from west Amazonas, and 2) the Xigu and Javary rivers from the VMAP0 inwatera (inland water area) layer to divide east from west Para.</p>\r\n\r\n<p>For the exact details of the tz assignments, see the script in the ingredients.</p>\r\n<h3>&nbsp;</h3>\r\n<hr/>\r\n<h3>Terms of use</h3>\r\n\r\n<p><a rel=\"license\" href=\"http://creativecommons.org/publicdomain/zero/1.0/\" style=\"text-decoration:none;\"><img src=\"http://i.creativecommons.org/l/zero/1.0/80x15.png\" border=\"0\" alt=\"CC0\" /></a> To the extent possible under law, Eric Muller has waived all copyright and related or neighboring rights to the <span property=\"dct:title\">efele.net/tz maps</span> (comprising the shapefiles, the web pages describing them and the scripts and data used to build them).\r\nThis work is published from <span about=\"http://efele.net/maps/tz\" property=\"vcard:Country\" datatype=\"dct:ISO3166\" content=\"US\">the United States of America</span>.</p>\r\n\r\n\r\n<p>Note that this does not affect the rights others may have. I am not qualified to determine whether such rights exist.</p>\r\n<hr/>\r\n<p><span class=\"style4\"><a href=\"/contact.html\">Contact</a> - <a href=\"../../thanks.html\">Thanks</a></span></p>\r\n<p>History:</p>\r\n<ul>\r\n  <li>November 26, 2013:\r\n    <ul>\r\n      <li>fixed a problem along the boundary of America/Menominee and Lake Michigan. Thanks to James Diebel for reporting the problem.</li>\r\n    </ul>\r\n  </li>\r\n  <li>October 8, 2013:\r\n    <ul>\r\n      <li>fixed some self-intersections in Asia/Magadan. Thanks to Edward Judge for reporting the problem.</li>\r\n    </ul>\r\n  </li>\r\n  <li>July 5, 2013:\r\n    <ul>\r\n      <li>some of the Aleutian islands were incorrectly in America/Adak rather than America/Nome. Thanks to Eric Michielli for reporting the problem.</li>\r\n    </ul>\r\n    <ul>\r\n      <li>some islands in the Yamalo-Nenetskiy autonomous okrug of Russia were incorrectly in Europe/Moscou rather than Europe/Yekaterinburg, because of a problem the fips10 map (in turn traced to a problem with VMAP0). Thanks to Derick Rethans for reporting this error.</li>\r\n    </ul>\r\n  </li>\r\n  <li>April 17, 2013:\r\n  <ul>\r\n    <li>added Asia/Ust-Nera and Asia/Khandyga</li>\r\n  </ul>\r\n  </li>\r\n  <li>October 16, 2012:\r\n    <ul>\r\n      <li>Alderney Island (part of the Channel Islands) was incorrectly assigned to Europe/London, instead of the correct Europe/Guernsey; thanks to E. Jarecki for reporting this error.</li>\r\n    </ul>\r\n  </li>\r\n\r\n  <li>September 30, 2012:\r\n    <ul>\r\n      <li>extended America/Metlakatla to cover the whole of Annette island and a few nearby islands. Thanks to James Diebel for pointing out this problem.</li>\r\n      <li>removed a couple of artifacts of the VMAP0 15° grid in Honduras; thanks to Don Tong for pointing these out.</li>\r\n    </ul>\r\n  </li>\r\n  <li>June 24, 2012:\r\n    <ul>\r\n      <li>added Africa/Juba</li>\r\n      <li>added Asia/Hebron</li>\r\n      <li>added America/Lower_Princes and America/Kralendijk</li>\r\n      <li>added America/Sitka and America/Metlakatla.</li>\r\n      <li>added America/Creston</li>\r\n      <li>fixed small problems in Canada, thanks to Paul Salber</li>\r\n      <li>fixed a small problem in Russia, thanks to Adalbert Michelic</li>\r\n      <li>fixed a small problem in US, thanks to Dan O’Neill</li>\r\n    </ul>\r\n  </li>\r\n  <li>February 7, 2011:\r\n    <ul>\r\n      <li>added America/North_Dakota/Beulah</li>\r\n      <li>fixes to the polygons of America/Winnipeg, America/Belem, Asia/Urumqi, thanks to Veeder South</li>\r\n    </ul>\r\n  </li>\r\n  <li>November 18, 2010:\r\n  <ul>\r\n    <li>added a paragraph about the timezones at sea</li>\r\n  </ul>\r\n  </li>\r\n  <li>August 26, 2010:\r\n  <ul>\r\n    <li>added the new zone America/Bahia_Banderas</li>\r\n    <li>added the zones for Greenland</li>\r\n    <li>renamed Pacific/Truk to Pacific/Chuuk and Pacific/Ponape to Pacific/Pohnpei</li>\r\n  </ul>\r\n  </li>\r\n  <li>April 14, 2010:\r\n    <ul>\r\n      <li>added the zone introduced in TZ 2010f: Antarctica/Macquarie.</li>\r\n      <li>fixed a typo in America/Monterrey and America/Chihuahua; thanks to Boaz Stuller and Russel Sayers for reporting these problems.</li>\r\n    </ul>\r\n  </li>\r\n  <li>March 16, 2010:\r\n    <ul>\r\n      <li>fixed an overlap problem around lon -85.39, lat 29.98 and a gap around lon -69.9, lat -4.23; thanks to Gary Baker for reporting these problems.</li>\r\n    </ul>\r\n  </li>\r\n  <li>March 15, 2010:\r\n    <ul>\r\n      <li>added the tz_world_mp shapefile; thanks to Adam Green for noticing the change in the geometry type of the shapefile.</li>\r\n    </ul>\r\n  </li>\r\n  <li>March 13, 2010: \r\n    <ul><li>incorporated the udpates to the Russia map.</li>\r\n      <li>added the zones introduced between TZ 2009r and TZ 2010e: America/Matamoros, America/Ojinaga, America/Santa_Isabel.</li>\r\n    </ul>\r\n  </li>\r\n  <li>November 23, 2009: fixed a typo in America/Yakutat; thanks to Rob Ellison for noticing this.</li>\r\n  <li>November 14, 2009: incorporated the updates to the US map</li>\r\n  <li>November 10, 2009: rebuilt using the updated sources; updated the script to match tz2009r; added the terms of use</li>\r\n  <li>October 4, 2008: added commentary on Mongolia and Cuba</li>\r\n  <li>September 19, 2008: fixed the description of <em>Broken_Hill</em>, thanks to Eric Ulevic</li>\r\n  <li>September 16, 2008: first version</li>\r\n</ul>\r\n<p>&nbsp;</p>\r\n\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/timezone/tz_world.prj",
    "content": "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/unicode/unicode.defaults",
    "content": "0x0020,0x007F,LATIN,LATIN1\n0x00A0,0x00FF,LATIN,LATINI1_SUPPLEMENT\n0x0100,0x017F,LATIN,LATIN1_EXTENDED_A\n0x0180,0x024F,LATIN,LATIN1_EXTENDED_B\n0x1E00,0x1EFF,LATIN,LATIN1_EXTENDED_ADDITIONAL\n0x0370,0x03FF,GREEK,GREEK_COPTIC\n0x0400,0x04FF,CYRILLIC,CYRILLIC\n0x0500,0x052F,CYRILLIC,CYRILLIC_SUPPLEMENTAL\n0x0600,0x06FF,ARABIC,ARABIC\n0x1100,0x11FF,HANGUL,HANGUL_JAMO\n0x3130,0x318F,HANGUL,HANGUL_COMPATIBILITY_JAMO\n0xAC00,0xD7AF,HANGUL,HANGUL_SYLLABLES\n0x2E80,0x2EFF,CJK,CJK_RADICALS_SUPPLEMENT\n0x3040,0x309F,CJK,HIRAGANA\n0x30A0,0x30FF,CJK,KATAKANA\n0x3400,0x4DBF,CJK,CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A\n0x4E00,0x9FFF,CJK,CJK_UNIFIED_IDEOGRAPHS\n0x3000,0x303F,CJK,CJK_SYMBOLS_AND_PUNCTUATION\n0xF900,0xFAFF,CJK,CJK_COMPATIBILITY_IDEOGRAPHS\n0x02B0,0x02FF,SPACING_MODIFIER_LETTERS,SPACING_MODIFIER_LETTERS\n0x1E00,0x1EFF,LATIN,LATIN_EXTENDED_ADDITIONAL\n0xFFF0,0xFFFF,SPECIALS,SPECIALS\n0x0E00,0x0E7F,THAI,THAI\n0x2190,0x21FF,ARROW_SYMBOLS,ARROW_SYMBOLS\n0x3100,0x312F,BOPOMOFO,BOPOMOFO\n0xFF00,0xFFEF,HALFWIDTH_AND_FULLWIDTH_FORMS,HALFWIDTH_AND_FULLWIDTH_FORMS\n0x0250,0x02AF,IPA_EXTENSIONS,IPA_EXTENSIONS\n0x0530,0x058F,ARMENIAN,ARMENIAN\n0x2100,0x214F,LETTERLIKE_SYMBOLS,LETTERLIKE_SYMBOLS\n0x2150,0x218F,NUMBER_FORMS,NUMBER_FORMS\n0x0F00,0x0FFF,TIBETAN,TIBETAN\n0x2460,0x24FF,ENCLOSED_ALPHANUMERICS,ENCLOSED_ALPHANUMERICS\n0x25A0,0x25FF,GEOMETRIC_SHAPES,GEOMETRIC_SHAPES\n0xFE70,0xFEFF,ARABIC_PRESENTATION_FORMS_B,ARABIC_PRESENTATION_FORMS_B\n0x1780,0x17FF,KHMER,KHMER\n0x0590,0x05FF,HEBREW,HEBREW\n0x0E80,0x0EFF,LAO,LAO\n0x0B80,0x0BFF,TAMIL,TAMIL\n0x0D80,0x0DFF,SINHALA,SINHALA\n0x1200,0x137F,ETHIOPIC,ETHIOPIC\n0x009D,0x009D,OPERATING_SYSTEM_CONTROL_CHARACTERS,OPERATING_SYSTEM_CONTROL_CHARACTERS\n0x10A0,0x10FF,GEORGIAN,GEORGIAN\n0x0980,0x09FF,BENGALI,BENGALI\n0x0700,0x074F,SYRIAC,SYRIAC\n0xFB50,0xFDFF,ARABIC_PRESENTATION_FORMS,ARABIC_PRESENTATION_FORMS\n0x1000,0x109F,MYANMAR,MYANMAR\n0x0900,0x097F,DEVANAGARI,DEVANAGARI\n0x2200,0x22FF,MATHEMATICAL_OPERATORS,MATHEMATICAL_OPERATORS\n0x07C0,0x07FA,NKO,NKO\n0x2D30,0x2D7F,TIFINAGH,TIFINAGH\n0x0030,0x003B,IGNORE,IGNORE_LATIN1_NUMBER\n0x2000,0x206F,IGNORE,IGNORE_GENERAL_PUNCTUATION\n0x0300,0x036F,IGNORE,IGNORE_COMBINING_DIACRITICAL_MARKS\n0xFF08,0xFF09,IGNORE,IGNORE_FULLWIDTH_PARENS\n0xFF0C,0xFF0C,IGNORE,IGNORE_FULLWIDTH_COMMA\n0xFF00,0xFF00,IGNORE,IGNORE_FULLWIDTH_SPACE\n0x0028,0x0029,IGNORE,IGNORE_LATIN1_PARENS\n0x0020,0x0020,IGNORE,IGNORE_LATIN1_SPACE\n0x0022,0x0022,IGNORE,IGNORE_LATIN1_QUOTE\n0x007E,0x007E,IGNORE,IGNORE_LATIN1_TILDA\n0x002C,0x002F,IGNORE,IGNORE_COMMA_DASH_PERIOD_SLASH\n0x007C,0x007C,IGNORE,IGNORE_LATIN1_PIPE\n0x005B,0x005D,IGNORE,IGNORE_SQUARE_BRACKETS_AND_BACKSLASH"
  },
  {
    "path": "src/main/resources/org/openstreetmap/atlas/utilities/vectortiles/minimum-zooms.json",
    "content": "[\n  {\n    \"key\": \"highway\",\n    \"default\": 14,\n    \"values\": {\n      \"motorway\": 6,\n      \"trunk\": 6,\n      \"primary\": 8,\n      \"secondary\": 9,\n      \"tertiary\": 10,\n      \"motorway_link\": 12,\n      \"trunk_link\": 12,\n      \"primary_link\": 12,\n      \"secondary_link\": 12,\n      \"residential\": 12,\n      \"unclassified\": 12,\n      \"service\": 13,\n      \"living_street\": 13,\n      \"pedestrian\": 13,\n      \"bridleway\": 13,\n      \"footway\": 13,\n      \"cycleway\": 13,\n      \"track\": 13,\n      \"steps\": 13\n    }\n  },\n  {\n    \"key\": \"building\",\n    \"default\": 13\n  },\n  {\n    \"key\": \"amenity\",\n    \"default\": 13,\n    \"values\": {\n      \"hospital\": 12\n    }\n  },\n  {\n    \"key\": \"waterway\",\n    \"default\": 12,\n    \"values\": {\n      \"river\": 8\n    }\n  },\n  {\n    \"key\": \"railway\",\n    \"default\": 13,\n    \"values\": {\n      \"rail\": 8,\n      \"light_rail\": 8,\n      \"tram\": 12,\n      \"subway\": 12,\n      \"disused\": 14\n    }\n  },\n  {\n    \"key\": \"landuse\",\n    \"default\": 12,\n    \"values\": {\n      \"basin\": 7,\n      \"forest\": 8\n    }\n  },\n  {\n    \"key\": \"route\",\n    \"default\": 14,\n    \"values\": {\n      \"ferry\": 8\n    }\n  },\n  {\n    \"key\": \"place\",\n    \"default\": 12,\n    \"values\": {\n      \"country\": 3,\n      \"state\": 5\n    }\n  },\n  {\n    \"key\": \"leisure\",\n    \"default\": 13,\n    \"values\": {\n      \"park\": 8\n    }\n  },\n  {\n    \"key\": \"natural\",\n    \"default\": 12,\n    \"values\": {\n      \"glacier\": 8\n    }\n  }\n]\n"
  },
  {
    "path": "src/test/groovy/org/openstreetmap/atlas/geography/converters/jts/JtsPolyLineConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters.jts;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.locationtech.jts.geom.LineString;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\n\n/**\n * @author lcram\n */\npublic class JtsPolyLineConverterTest\n{\n    @Test\n    public void testBackwardConvert()\n    {\n        final PolyLine polyLine = PolyLine.wkt(\"LINESTRING(0 0, 1 1)\");\n        final LineString lineString = new JtsPolyLineConverter().convert(polyLine);\n        Assert.assertEquals(\"LINESTRING (0 0, 1 1)\",\n                new JtsPolyLineConverter().backwardConvert(lineString).toWkt());\n    }\n\n    @Test\n    public void testConvertLineString()\n    {\n        final PolyLine polyLine = PolyLine.wkt(\"LINESTRING(0 0, 1 1)\");\n        Assert.assertEquals(\"LINESTRING (0 0, 1 1)\",\n                new JtsPolyLineConverter().convert(polyLine).toString());\n    }\n\n    @Test\n    public void testConvertLinearRing()\n    {\n        final PolyLine polyLine = Polygon.wkt(\n                \"POLYGON ((-61.875 15.2841851, -61.875 15.9613291, -61.171875 15.9613291, -61.171875 15.2841851, -61.875 15.2841851))\");\n        Assert.assertEquals(\n                \"LINEARRING (-61.875 15.2841851, -61.875 15.9613291, -61.171875 15.9613291, -61.171875 15.2841851, -61.875 15.2841851)\",\n                new JtsPolyLineConverter().convert(polyLine).toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/event/EventBusTest.java",
    "content": "package org.openstreetmap.atlas.event;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\n\nimport com.google.common.eventbus.EventBus;\n\n/**\n * Tests for {@link EventBus}.\n *\n * @author mkalender\n */\npublic class EventBusTest\n{\n    @Test\n    public void testEventResult()\n    {\n        testEventResult(new TestEvent(null));\n        testEventResult(new TestEvent(\"\"));\n        testEventResult(new TestEvent(\"This is a test!!!\"));\n    }\n\n    @Test\n    public void testOneComplete()\n    {\n        testCompleteCount(1);\n    }\n\n    @Test\n    public void testOneThousandComplete()\n    {\n        testCompleteCount(1000);\n    }\n\n    @Test\n    public void testSingleThreadOneEvent()\n    {\n        testProcessCount(1, 1);\n    }\n\n    @Test\n    public void testSingleThreadOneMillionEvent()\n    {\n        testProcessCount(1, 1000000);\n    }\n\n    @Test\n    public void testSingleThreadOneThousandEvent()\n    {\n        testProcessCount(1, 1000);\n    }\n\n    @Test\n    public void testSingleThreadTenThousandEvent()\n    {\n        testProcessCount(1, 10000);\n    }\n\n    @Test\n    public void testSingleThreadZeroEvent()\n    {\n        testProcessCount(1, 0);\n    }\n\n    @Test\n    public void testTenThreadsOneEvent()\n    {\n        testProcessCount(10, 1);\n    }\n\n    @Test\n    public void testTenThreadsOneThousandEvent()\n    {\n        testProcessCount(10, 1000);\n    }\n\n    @Test\n    public void testTenThreadsZeroEvent()\n    {\n        testProcessCount(10, 0);\n    }\n\n    @Test\n    public void testTwoComplete()\n    {\n        testCompleteCount(2);\n    }\n\n    @Test\n    public void testTwoThreadsOneEvent()\n    {\n        testProcessCount(2, 1);\n    }\n\n    @Test\n    public void testTwoThreadsOneThousandEvent()\n    {\n        testProcessCount(2, 1000);\n    }\n\n    @Test\n    public void testTwoThreadsTenThousandEvent()\n    {\n        testProcessCount(2, 10000);\n    }\n\n    @Test\n    public void testTwoThreadsZeroEvent()\n    {\n        testProcessCount(2, 0);\n    }\n\n    @Test\n    public void testZeroComplete()\n    {\n        testCompleteCount(0);\n    }\n\n    private void testCompleteCount(final int completeCount)\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final EventBus eventBus = new EventBus();\n        eventBus.register(testProcessor);\n\n        // Straight complete\n        for (int index = 0; index < completeCount; index++)\n        {\n            eventBus.post(new ShutdownEvent());\n        }\n\n        // Validate\n        Assert.assertEquals(0, testProcessor.getProcessCount());\n        Assert.assertEquals(completeCount, testProcessor.getCompleteCount());\n    }\n\n    private void testEventResult(final TestEvent event)\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final EventBus eventBus = new EventBus();\n        eventBus.register(testProcessor);\n\n        // Send event and complete\n        eventBus.post(event);\n        eventBus.post(new ShutdownEvent());\n\n        // Validate\n        Assert.assertEquals(event.getMessage(), testProcessor.getLastEventMessage());\n        Assert.assertEquals(event != null ? 1 : 0, testProcessor.getProcessCount());\n        Assert.assertEquals(1, testProcessor.getCompleteCount());\n    }\n\n    private void testProcessCount(final int threadCount, final int eventCount)\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final EventBus eventBus = new EventBus();\n        eventBus.register(testProcessor);\n\n        // Send events\n        final Pool threadPool = new Pool(threadCount, \"Test event pool\", Duration.ONE_MINUTE);\n        for (int threadIndex = 0; threadIndex < threadCount; threadIndex++)\n        {\n            threadPool.queue(() ->\n            {\n                for (int index = 0; index < eventCount; index++)\n                {\n                    eventBus.post(new TestEvent(\"test \" + index));\n                }\n            });\n        }\n        threadPool.close();\n\n        // Complete\n        eventBus.post(new ShutdownEvent());\n\n        // Validate\n        Assert.assertEquals(eventCount * threadCount, testProcessor.getProcessCount());\n        Assert.assertEquals(1, testProcessor.getCompleteCount());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/event/EventServiceTest.java",
    "content": "package org.openstreetmap.atlas.event;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\n\n/**\n * Tests for {@link EventService}.\n *\n * @author mkalender\n * @author Yazad Khambata\n */\npublic class EventServiceTest\n{\n    @Test\n    public void testEventResult()\n    {\n        testEventResult(new TestEvent(null));\n        testEventResult(new TestEvent(\"\"));\n        testEventResult(new TestEvent(\"This is a test!!!\"));\n    }\n\n    @Test\n    public void testNullEvent()\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final EventServiceable eventService = EventService.get(\"Test service for null event\");\n        eventService.register(testProcessor);\n\n        // Send event and complete\n        eventService.post(null);\n        eventService.complete();\n\n        // Validate\n        Assert.assertEquals(0, testProcessor.getProcessCount());\n        Assert.assertEquals(1, testProcessor.getCompleteCount());\n    }\n\n    @Test\n    public void testOneComplete()\n    {\n        testCompleteCount(1);\n    }\n\n    @Test\n    public void testOneThousandComplete()\n    {\n        testCompleteCount(1000);\n    }\n\n    @Test\n    public void testPostAfterComplete()\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final EventServiceable eventService = EventService\n                .get(\"Test service for posting after completion\");\n        eventService.register(testProcessor);\n\n        // Send event and complete\n        eventService.complete();\n        eventService.post(new TestEvent(\"a message\"));\n\n        // Validate\n        Assert.assertEquals(null, testProcessor.getLastEventMessage());\n        Assert.assertEquals(0, testProcessor.getProcessCount());\n        Assert.assertEquals(1, testProcessor.getCompleteCount());\n    }\n\n    @Test\n    public void testSingleThreadOneEvent()\n    {\n        testProcessCount(1, 1);\n    }\n\n    @Test\n    public void testSingleThreadOneMillionEvent()\n    {\n        testProcessCount(1, 1000000);\n    }\n\n    @Test\n    public void testSingleThreadOneThousandEvent()\n    {\n        testProcessCount(1, 1000);\n    }\n\n    @Test\n    public void testSingleThreadTenThousandEvent()\n    {\n        testProcessCount(1, 10000);\n    }\n\n    @Test\n    public void testSingleThreadZeroEvent()\n    {\n        testProcessCount(1, 0);\n    }\n\n    @Test\n    public void testTenThreadsOneEvent()\n    {\n        testProcessCount(10, 1);\n    }\n\n    @Test\n    public void testTenThreadsOneThousandEvent()\n    {\n        testProcessCount(10, 1000);\n    }\n\n    @Test\n    public void testTenThreadsZeroEvent()\n    {\n        testProcessCount(10, 0);\n    }\n\n    @Test\n    public void testTwoComplete()\n    {\n        testCompleteCount(2);\n    }\n\n    @Test\n    public void testTwoThreadsOneEvent()\n    {\n        testProcessCount(2, 1);\n    }\n\n    @Test\n    public void testTwoThreadsOneThousandEvent()\n    {\n        testProcessCount(2, 1000);\n    }\n\n    @Test\n    public void testTwoThreadsTenThousandEvent()\n    {\n        testProcessCount(2, 10000);\n    }\n\n    @Test\n    public void testTwoThreadsZeroEvent()\n    {\n        testProcessCount(2, 0);\n    }\n\n    @Test\n    public void testUnregisterProcessor()\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final EventServiceable eventService = EventService\n                .get(\"Event service for unregistering processor\");\n\n        eventService.register(testProcessor);\n        eventService.unregister(testProcessor);\n        eventService.post(new TestEvent(\"TESTING\"));\n\n        eventService.complete();\n\n        Assert.assertEquals(0, testProcessor.getCompleteCount());\n        Assert.assertEquals(0, testProcessor.getProcessCount());\n    }\n\n    @Test\n    public void testZeroComplete()\n    {\n        testCompleteCount(0);\n    }\n\n    private void testCompleteCount(final int completeCount)\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final EventServiceable eventService = EventService\n                .get(\"Test service complete \" + completeCount);\n        eventService.register(testProcessor);\n\n        // Straight complete\n        for (int index = 0; index < completeCount; index++)\n        {\n            eventService.complete();\n        }\n\n        // Validate\n        Assert.assertEquals(0, testProcessor.getProcessCount());\n        Assert.assertEquals(Math.min(completeCount, 1), testProcessor.getCompleteCount());\n    }\n\n    private void testEventResult(final TestEvent event)\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final EventServiceable eventService = EventService\n                .get(\"Test service for \" + event.getMessage());\n        eventService.register(testProcessor);\n\n        // Send event and complete\n        eventService.post(event);\n        eventService.complete();\n\n        // Validate\n        Assert.assertEquals(event.getMessage(), testProcessor.getLastEventMessage());\n        Assert.assertEquals(1, testProcessor.getProcessCount());\n        Assert.assertEquals(1, testProcessor.getCompleteCount());\n    }\n\n    private void testProcessCount(final int threadCount, final int eventCount)\n    {\n        final TestProcessor testProcessor = new TestProcessor();\n        final TestProcessor otherProcessor = new TestProcessor();\n        final EventServiceable eventService = EventService\n                .get(\"Test service for \" + threadCount + \"-\" + eventCount);\n        eventService.register(testProcessor);\n        eventService.register(otherProcessor);\n\n        // Send events\n        final Pool threadPool = new Pool(threadCount,\n                \"Test pool for \" + threadCount + \"-\" + eventCount, Duration.ONE_MINUTE);\n        for (int threadIndex = 0; threadIndex < threadCount; threadIndex++)\n        {\n            threadPool.queue(() ->\n            {\n                for (int index = 0; index < eventCount; index++)\n                {\n                    eventService.post(new TestEvent(\"test \" + index));\n                }\n            });\n        }\n        threadPool.close();\n\n        // Complete\n        eventService.complete();\n\n        // Validate\n        Assert.assertEquals(eventCount * threadCount, testProcessor.getProcessCount());\n        Assert.assertEquals(1, testProcessor.getCompleteCount());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/event/TestEvent.java",
    "content": "package org.openstreetmap.atlas.event;\n\n/**\n * Sample {@link Event} to be used for testing.\n *\n * @author mkalender\n */\npublic class TestEvent extends Event\n{\n    private final String message;\n\n    public TestEvent(final String message)\n    {\n        this.message = message;\n    }\n\n    public String getMessage()\n    {\n        return this.message;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/event/TestProcessor.java",
    "content": "package org.openstreetmap.atlas.event;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport com.google.common.eventbus.Subscribe;\n\n/**\n * Sample {@link Processor} for testing.\n *\n * @author mkalender\n */\npublic class TestProcessor implements Processor<TestEvent>\n{\n    private String lastMessage;\n    private final AtomicInteger completeCount = new AtomicInteger(0);\n    private final AtomicInteger processCount = new AtomicInteger(0);\n\n    @Override\n    @Subscribe\n    public void process(final ShutdownEvent event)\n    {\n        this.completeCount.incrementAndGet();\n    }\n\n    @Override\n    @Subscribe\n    public void process(final TestEvent event)\n    {\n        this.lastMessage = event.getMessage();\n        this.processCount.incrementAndGet();\n    }\n\n    int getCompleteCount()\n    {\n        return this.completeCount.get();\n    }\n\n    String getLastEventMessage()\n    {\n        return this.lastMessage;\n    }\n\n    int getProcessCount()\n    {\n        return this.processCount.get();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/exception/CoreExceptionTest.java",
    "content": "package org.openstreetmap.atlas.exception;\n\nimport java.util.function.BooleanSupplier;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.random.RandomTextGenerator;\n\n/**\n * @author matthieun\n */\npublic class CoreExceptionTest\n{\n    @Test\n    public void testTransitive()\n    {\n        final BooleanSupplier nested = () ->\n        {\n            throw new CoreException(\"Nested Message\");\n        };\n        final BooleanSupplier wrapping = () ->\n        {\n            try\n            {\n                nested.getAsBoolean();\n                return false;\n            }\n            catch (final CoreException e)\n            {\n                throw new CoreException(\"Wrapping Message: {}\", new RandomTextGenerator().newWord(),\n                        e);\n            }\n        };\n        try\n        {\n            wrapping.getAsBoolean();\n        }\n        catch (final CoreException e)\n        {\n            Assert.assertNotNull(e.getCause());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/exception/change/FeatureChangeMergeExceptionTest.java",
    "content": "package org.openstreetmap.atlas.exception.change;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic class FeatureChangeMergeExceptionTest\n{\n    @Test\n    public void testTruncate()\n    {\n        final String one = \"abc\";\n        final StringBuilder twoBuilder = new StringBuilder();\n        for (int index = 0; index < 2100; index++)\n        {\n            twoBuilder.append(\"a\");\n        }\n        final String two = twoBuilder.toString();\n\n        Assert.assertEquals(one, FeatureChangeMergeException.truncate(one));\n        Assert.assertEquals(2100, two.length());\n        Assert.assertEquals(FeatureChangeMergeException.MAXIMUM_MESSAGE_SIZE,\n                FeatureChangeMergeException.truncate(two).length());\n    }\n\n    @Test\n    public void testTruncateInConstructor()\n    {\n        final String one = \"abc{}d\";\n        final StringBuilder twoBuilder = new StringBuilder();\n        for (int index = 0; index < 2100; index++)\n        {\n            twoBuilder.append(\"a\");\n        }\n        final String two = twoBuilder.toString();\n\n        final FeatureChangeMergeException fcme = new FeatureChangeMergeException(\n                MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE, one, two,\n                new CoreException(\"I am the cause\"));\n        Assert.assertEquals(FeatureChangeMergeException.MAXIMUM_MESSAGE_SIZE,\n                fcme.getMessage().length());\n        Assert.assertEquals(CoreException.class, fcme.getCause().getClass());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/AltitudeTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Tests {@link Altitude} functionality.\n *\n * @author mgostintsev\n */\npublic class AltitudeTest\n{\n    @Test\n    public void testAltitudeEquals()\n    {\n        final Altitude positiveAltitude = Altitude.meters(40);\n        final Altitude duplicatePositiveAltitude = Altitude.meters(40);\n        final Altitude negativeAltitude = Altitude.meters(-40);\n\n        Assert.assertNotEquals(positiveAltitude, negativeAltitude);\n        Assert.assertEquals(positiveAltitude, duplicatePositiveAltitude);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testInvalidAltitude()\n    {\n        @SuppressWarnings(\"unused\")\n        final Altitude impossible = Altitude.meters(-7371000);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/CompressedPolyLineTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Test a {@link CompressedPolyLine}\n *\n * @author matthieun\n */\npublic class CompressedPolyLineTest\n{\n    @Test\n    public void testCompressionDecompression()\n    {\n        final PolyLine polyLine = PolyLine.TEST_POLYLINE;\n        System.out.println(polyLine);\n        final CompressedPolyLine compressed = new CompressedPolyLine(polyLine);\n        System.out.println(compressed);\n        final PolyLine decompressed = compressed.asPolyLine();\n        System.out.println(decompressed);\n        Assert.assertEquals(polyLine, decompressed);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/HeadingTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\n\n/**\n * Test {@link Heading}s\n *\n * @author mkalender\n */\npublic class HeadingTest\n{\n    @Test\n    public void testAngles()\n    {\n        Assert.assertEquals(Angle.degrees(-360), Heading.SOUTH);\n        Assert.assertEquals(Angle.degrees(-270), Heading.WEST);\n        Assert.assertEquals(Angle.degrees(-180), Heading.NORTH);\n        Assert.assertEquals(Angle.degrees(-90), Heading.EAST);\n        Assert.assertEquals(Angle.degrees(0), Heading.SOUTH);\n        Assert.assertEquals(Angle.degrees(90), Heading.WEST);\n        Assert.assertEquals(Angle.degrees(180), Heading.NORTH);\n        Assert.assertEquals(Angle.degrees(270), Heading.EAST);\n        Assert.assertEquals(Angle.degrees(360), Heading.SOUTH);\n        Assert.assertEquals(Angle.degrees(450), Heading.WEST);\n    }\n\n    @Test\n    public void testDifference()\n    {\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(-180).difference(Heading.degrees(0)));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(20)),\n                Angle.degrees(160));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(70)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(90)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(160)),\n                Angle.degrees(20));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(180)),\n                Angle.degrees(0));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(200)),\n                Angle.degrees(20));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(250)),\n                Angle.degrees(70));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(270)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(290)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(340)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(-180).difference(Heading.degrees(360)));\n        Assert.assertEquals(Heading.degrees(-180).difference(Heading.degrees(380)),\n                Angle.degrees(160));\n\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(-90).difference(Heading.degrees(0)));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(20)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(70)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(-90).difference(Heading.degrees(90)));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(160)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(180)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(200)),\n                Angle.degrees(70));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(250)),\n                Angle.degrees(20));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(270)),\n                Angle.degrees(0));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(290)),\n                Angle.degrees(20));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(340)),\n                Angle.degrees(70));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(360)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(-90).difference(Heading.degrees(380)),\n                Angle.degrees(110));\n\n        Assert.assertEquals(Angle.degrees(0), Heading.degrees(0).difference(Heading.degrees(0)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(0).difference(Heading.degrees(20)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(0).difference(Heading.degrees(70)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(0).difference(Heading.degrees(90)));\n        Assert.assertEquals(Heading.degrees(0).difference(Heading.degrees(160)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(0).difference(Heading.degrees(180)));\n        Assert.assertEquals(Heading.degrees(0).difference(Heading.degrees(200)),\n                Angle.degrees(160));\n        Assert.assertEquals(Heading.degrees(0).difference(Heading.degrees(250)),\n                Angle.degrees(110));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(0).difference(Heading.degrees(270)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(0).difference(Heading.degrees(290)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(0).difference(Heading.degrees(340)));\n        Assert.assertEquals(Angle.degrees(0), Heading.degrees(0).difference(Heading.degrees(360)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(0).difference(Heading.degrees(380)));\n\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(90).difference(Heading.degrees(0)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(90).difference(Heading.degrees(20)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(90).difference(Heading.degrees(70)));\n        Assert.assertEquals(Angle.degrees(0), Heading.degrees(90).difference(Heading.degrees(90)));\n        Assert.assertEquals(Heading.degrees(90).difference(Heading.degrees(160)),\n                Angle.degrees(70));\n        Assert.assertEquals(Heading.degrees(90).difference(Heading.degrees(180)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(90).difference(Heading.degrees(200)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(90).difference(Heading.degrees(250)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(90).difference(Heading.degrees(270)));\n        Assert.assertEquals(Heading.degrees(90).difference(Heading.degrees(290)),\n                Angle.degrees(160));\n        Assert.assertEquals(Heading.degrees(90).difference(Heading.degrees(340)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(90).difference(Heading.degrees(360)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(90).difference(Heading.degrees(380)),\n                Angle.degrees(70));\n\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(180).difference(Heading.degrees(0)));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(20)),\n                Angle.degrees(160));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(70)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(90)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(160)),\n                Angle.degrees(20));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(180)),\n                Angle.degrees(0));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(200)),\n                Angle.degrees(20));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(250)),\n                Angle.degrees(70));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(270)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(290)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(340)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(180).difference(Heading.degrees(360)));\n        Assert.assertEquals(Heading.degrees(180).difference(Heading.degrees(380)),\n                Angle.degrees(160));\n\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(270).difference(Heading.degrees(0)));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(20)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(70)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(270).difference(Heading.degrees(90)));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(160)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(180)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(200)),\n                Angle.degrees(70));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(250)),\n                Angle.degrees(20));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(270)),\n                Angle.degrees(0));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(290)),\n                Angle.degrees(20));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(340)),\n                Angle.degrees(70));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(360)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(270).difference(Heading.degrees(380)),\n                Angle.degrees(110));\n    }\n\n    @Test\n    public void testHeadingAngleDifference()\n    {\n        Assert.assertEquals(Angle.NONE, Heading.degrees(-180).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(-180).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(-180).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(-180).difference(Angle.degrees(90)));\n        Assert.assertEquals(Heading.degrees(-180).difference(Angle.degrees(160)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(-180).difference(Angle.degrees(180)));\n        Assert.assertEquals(Heading.degrees(-180).difference(Angle.degrees(200)),\n                Angle.degrees(160));\n        Assert.assertEquals(Heading.degrees(-180).difference(Angle.degrees(250)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(-180).difference(Angle.degrees(270)),\n                Angle.degrees(90));\n        Assert.assertEquals(Heading.degrees(-180).difference(Angle.degrees(290)),\n                Angle.degrees(70));\n        Assert.assertEquals(Heading.degrees(-180).difference(Angle.degrees(340)),\n                Angle.degrees(20));\n        Assert.assertEquals(Angle.NONE, Heading.degrees(-180).difference(Angle.degrees(360)));\n        Assert.assertEquals(Heading.degrees(-180).difference(Angle.degrees(380)),\n                Angle.degrees(20));\n\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(-90).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(-90).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(-90).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.NONE, Heading.degrees(-90).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(-90).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(-90).difference(Angle.degrees(180)));\n        Assert.assertEquals(Heading.degrees(-90).difference(Angle.degrees(200)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(-90).difference(Angle.degrees(250)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(-90).difference(Angle.degrees(270)));\n        Assert.assertEquals(Heading.degrees(-90).difference(Angle.degrees(290)),\n                Angle.degrees(160));\n        Assert.assertEquals(Heading.degrees(-90).difference(Angle.degrees(340)),\n                Angle.degrees(110));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(-90).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(-90).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(0).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(160), Heading.degrees(0).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(110), Heading.degrees(0).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(0).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(0).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.NONE, Heading.degrees(0).difference(Angle.degrees(180)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(0).difference(Angle.degrees(200)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(0).difference(Angle.degrees(250)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(0).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(110), Heading.degrees(0).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(160), Heading.degrees(0).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(0).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(160), Heading.degrees(0).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(90).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(110), Heading.degrees(90).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(160), Heading.degrees(90).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(90).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(110), Heading.degrees(90).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(90).difference(Angle.degrees(180)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(90).difference(Angle.degrees(200)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(90).difference(Angle.degrees(250)));\n        Assert.assertEquals(Angle.NONE, Heading.degrees(90).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(90).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(90).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(90).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(110), Heading.degrees(90).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.NONE, Heading.degrees(180).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(180).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(180).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(180).difference(Angle.degrees(90)));\n        Assert.assertEquals(Heading.degrees(180).difference(Angle.degrees(160)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(180).difference(Angle.degrees(180)));\n        Assert.assertEquals(Heading.degrees(180).difference(Angle.degrees(200)),\n                Angle.degrees(160));\n        Assert.assertEquals(Heading.degrees(180).difference(Angle.degrees(250)),\n                Angle.degrees(110));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(180).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(180).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(180).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.NONE, Heading.degrees(180).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(180).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(270).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(270).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(20), Heading.degrees(270).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.NONE, Heading.degrees(270).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(270).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(270).difference(Angle.degrees(180)));\n        Assert.assertEquals(Heading.degrees(270).difference(Angle.degrees(200)),\n                Angle.degrees(110));\n        Assert.assertEquals(Heading.degrees(270).difference(Angle.degrees(250)),\n                Angle.degrees(160));\n        Assert.assertEquals(Angle.MAXIMUM, Heading.degrees(270).difference(Angle.degrees(270)));\n        Assert.assertEquals(Heading.degrees(270).difference(Angle.degrees(290)),\n                Angle.degrees(160));\n        Assert.assertEquals(Heading.degrees(270).difference(Angle.degrees(340)),\n                Angle.degrees(110));\n        Assert.assertEquals(Angle.degrees(90), Heading.degrees(270).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(70), Heading.degrees(270).difference(Angle.degrees(380)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/LatitudeTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class LatitudeTest\n{\n    @Test\n    public void testCreation()\n    {\n        final Latitude one = Latitude.degrees(37);\n        Assert.assertEquals(370_000_000, one.asDm7());\n        try\n        {\n            Latitude.degrees(37 + 100);\n            fail(\"Latitude should not have been created!\");\n        }\n        catch (final Exception e)\n        {\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/LocationTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.function.BiFunction;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n * @author mgostintsev\n */\npublic class LocationTest extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(LocationTest.class);\n    private static final double DELTA = 1e-15;\n\n    private static final Switch<BiFunction<Location, Location, Distance>> DISTANCE_TYPE = new Switch<>(\n            \"dist\", \"equirectangular or haversine or mixed\", value ->\n            {\n                switch (value)\n                {\n                    case \"equirectangular\":\n                        return (one, two) -> one.equirectangularDistanceTo(two);\n                    case \"haversine\":\n                        return (one, two) -> one.haversineDistanceTo(two);\n                    case \"mixed\":\n                        return (one, two) -> one.distanceTo(two);\n                    default:\n                        throw new CoreException(\"{} is not a recognized distance\", value);\n                }\n            }, Optionality.OPTIONAL, \"equirectangular\");\n\n    public static void main(final String[] args)\n    {\n        new LocationTest().run(args);\n    }\n\n    @Test\n    public void testAntiMeridianMidPoint()\n    {\n        final Location location1 = Location.forWkt(\"POINT(-180 25)\");\n        final Location location2 = Location.forWkt(\"POINT(-180 -25)\");\n        Assert.assertEquals(Location.forWkt(\"POINT(-180 0)\"), location1.midPoint(location2));\n        Assert.assertEquals(Location.forWkt(\"POINT(-180 0)\"), location2.midPoint(location1));\n\n        final Location location3 = Location.forWkt(\"POINT(180 25)\");\n        final Location location4 = Location.forWkt(\"POINT(180 -25)\");\n        Assert.assertEquals(Location.forWkt(\"POINT(180 0)\"), location3.midPoint(location4));\n        Assert.assertEquals(Location.forWkt(\"POINT(180 0)\"), location4.midPoint(location3));\n    }\n\n    @Test\n    public void testCrossingAntimeridian()\n    {\n        final Location one = new Location(Latitude.degrees(37), Longitude.degrees(179.998));\n        final Location two = new Location(Latitude.degrees(37), Longitude.degrees(179.999));\n        // This one is actually -180.0\n        final Location thr = new Location(Latitude.degrees(37), Longitude.degrees(180.0));\n        final Location fur = new Location(Latitude.degrees(37), Longitude.degrees(-179.999));\n        logger.info(\"one equirectangular two: {}\", one.equirectangularDistanceTo(two));\n        logger.info(\"two equirectangular thr: {}\", two.equirectangularDistanceTo(thr));\n        logger.info(\"thr equirectangular fur: {}\", thr.equirectangularDistanceTo(fur));\n        logger.info(\"one haversine two: {}\", one.haversineDistanceTo(two));\n        logger.info(\"two haversine thr: {}\", two.haversineDistanceTo(thr));\n        logger.info(\"thr haversine fur: {}\", thr.haversineDistanceTo(fur));\n        logger.info(\"one mixed two: {}\", one.distanceTo(two));\n        logger.info(\"two mixed thr: {}\", two.distanceTo(thr));\n        logger.info(\"thr mixed fur: {}\", thr.distanceTo(fur));\n        logger.info(\"one heading two: {}\", one.headingTo(two));\n        logger.info(\"two heading thr: {}\", two.headingTo(thr));\n        logger.info(\"thr heading fur: {}\", thr.headingTo(fur));\n\n        Assert.assertEquals(one.distanceTo(two).asMeters(), two.distanceTo(thr).asMeters(), 3);\n        Assert.assertEquals(two.distanceTo(thr).asMeters(), thr.distanceTo(fur).asMeters(), 3);\n\n        Assert.assertEquals(one.headingTo(two).asDegrees(), two.headingTo(thr).asDegrees(), 3);\n        Assert.assertEquals(two.headingTo(thr).asDegrees(), thr.headingTo(fur).asDegrees(), 3);\n    }\n\n    @Test\n    public void testDistanceTo()\n    {\n        final Location location1 = new Location(Latitude.degrees(37.336900),\n                Longitude.degrees(-122.005414));\n        logger.info(\"Location1: \" + location1);\n        final Location location2 = new Location(Latitude.degrees(37.332758),\n                Longitude.degrees(-122.005409));\n        logger.info(\"Location2: \" + location1);\n\n        final Distance approximation1 = location1.equirectangularDistanceTo(location2);\n        final Distance approximation2 = location1.haversineDistanceTo(location2);\n\n        Assert.assertTrue(Distance.FIFTEEN_HUNDRED_FEET.difference(approximation1)\n                .isLessThan(Distance.meters(10)));\n        Assert.assertTrue(Distance.FIFTEEN_HUNDRED_FEET.difference(approximation2)\n                .isLessThan(Distance.meters(10)));\n\n        // So the two methods have basically no difference, should choose equirectangular\n        // approximation for most cases\n        Assert.assertTrue(approximation1.difference(approximation2).isLessThan(Distance.meters(1)));\n    }\n\n    @Test\n    public void testHeadingTo()\n    {\n        final Location location1 = new Location(Latitude.degrees(37.336900),\n                Longitude.degrees(-122.005414));\n        final Location location2 = new Location(Latitude.degrees(37.332758),\n                Longitude.degrees(-122.005409));\n\n        Assert.assertTrue(Heading.degrees(180).difference(location1.headingTo(location2))\n                .isLessThan(Angle.degrees(1)));\n    }\n\n    @Test\n    public void testLoxodromicMidPoint()\n    {\n        final Location location1 = new Location(Latitude.degrees(51.127), Longitude.degrees(1.338));\n        final Location location2 = new Location(Latitude.degrees(50.964), Longitude.degrees(1.853));\n        final Location midpoint = location1.loxodromicMidPoint(location2);\n\n        Assert.assertEquals(51.0455, midpoint.getLatitude().asDegrees(), DELTA);\n        Assert.assertEquals(1.5957265, midpoint.getLongitude().asDegrees(), DELTA);\n\n        final Location location3 = new Location(Latitude.degrees(49), Longitude.degrees(-95.153));\n        final Location location4 = new Location(Latitude.degrees(49), Longitude.degrees(-123.323));\n        final Location midpoint2 = location3.loxodromicMidPoint(location4);\n\n        Assert.assertEquals(49.0, midpoint2.getLatitude().asDegrees(), DELTA);\n        Assert.assertEquals(-109.238, midpoint2.getLongitude().asDegrees(), DELTA);\n\n        final Location location5 = new Location(Latitude.degrees(40.0), Longitude.degrees(-180.0));\n        final Location location6 = new Location(Latitude.degrees(50.0), Longitude.degrees(-180.0));\n        final Location midpoint3 = location5.loxodromicMidPoint(location6);\n\n        Assert.assertEquals(45.0, midpoint3.getLatitude().asDegrees(), DELTA);\n        Assert.assertEquals(-180.0, midpoint3.getLongitude().asDegrees(), DELTA);\n\n        final Location location7 = new Location(Latitude.degrees(40.0), Longitude.degrees(180.0));\n        final Location location8 = new Location(Latitude.degrees(50.0), Longitude.degrees(180.0));\n        final Location midpoint4 = location7.loxodromicMidPoint(location8);\n\n        Assert.assertEquals(45.0, midpoint4.getLatitude().asDegrees(), DELTA);\n        Assert.assertEquals(180.0, midpoint4.getLongitude().asDegrees(), DELTA);\n\n        final Location location9 = new Location(Latitude.degrees(-16.5), Longitude.degrees(-180));\n        final Location location10 = new Location(Latitude.degrees(-17.0), Longitude.degrees(-180));\n        final Location midpoint5 = location9.loxodromicMidPoint(location10);\n\n        Assert.assertEquals(-16.75, midpoint5.getLatitude().asDegrees(), DELTA);\n        Assert.assertEquals(-180.0, midpoint5.getLongitude().asDegrees(), DELTA);\n    }\n\n    @Test\n    public void testMidPoint()\n    {\n        final Location location1 = new Location(Latitude.degrees(52.205), Longitude.degrees(0.119));\n        final Location location2 = new Location(Latitude.degrees(48.857), Longitude.degrees(2.351));\n        final Location midpoint = location1.midPoint(location2);\n\n        Assert.assertEquals(50.5363269, midpoint.getLatitude().asDegrees(), DELTA);\n        Assert.assertEquals(1.2746141, midpoint.getLongitude().asDegrees(), DELTA);\n    }\n\n    @Test\n    public void testMidPointAccuracyAndSpeed()\n    {\n        final Location location1 = new Location(Latitude.degrees(51.127), Longitude.degrees(1.338));\n        final Location location2 = new Location(Latitude.degrees(50.964), Longitude.degrees(1.853));\n\n        final Time beginning = Time.now();\n        final Location derivedMidPoint = location1.shiftAlongGreatCircle(\n                location1.headingTo(location2),\n                location1.distanceTo(location2).scaleBy(Ratio.HALF));\n        System.out.println(\"Derived Duration: \" + beginning.elapsedSince());\n        System.out.println(derivedMidPoint.toString() + \"\\n\");\n\n        final Time beginning2 = Time.now();\n        final Location calculatedMidPoint = location1.midPoint(location2);\n        System.out.println(\"Calculated Duration: \" + beginning2.elapsedSince());\n        System.out.println(calculatedMidPoint.toString() + \"\\n\");\n\n        final Time beginning3 = Time.now();\n        final Location calculatedLoxodromicMidPoint = location1.loxodromicMidPoint(location2);\n        System.out.println(\"Calculated Loxodromic Duration: \" + beginning3.elapsedSince());\n        System.out.println(calculatedLoxodromicMidPoint.toString() + \"\\n\");\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        long counter = 0L;\n        @SuppressWarnings(\"unchecked\")\n        final BiFunction<Location, Location, Distance> distance = (BiFunction<Location, Location, Distance>) command\n                .get(DISTANCE_TYPE);\n        while (true)\n        {\n            final Location location1 = Location.random(Rectangle.MAXIMUM);\n            final Location location2 = Location.random(Rectangle.MAXIMUM);\n            distance.apply(location1, location2);\n            counter++;\n            if (counter % 10_000_000 == 0)\n            {\n                logger.info(\"{}\", counter);\n            }\n        }\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(DISTANCE_TYPE);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/LongitudeTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n * @author tony\n */\npublic class LongitudeTest\n{\n    @Test\n    public void testAntimeridian()\n    {\n        final Longitude oneEighty = Longitude.degrees(180);\n        Assert.assertEquals(1_800_000_000, oneEighty.asDm7());\n\n        final Longitude minusOneEighty = Longitude.degrees(-180);\n        Assert.assertEquals(-1_800_000_000, minusOneEighty.asDm7());\n\n        Assert.assertFalse(oneEighty.equals(minusOneEighty));\n    }\n\n    @Test\n    public void testAssertCompareAntiMeridian()\n    {\n        final Longitude oneEighty = Longitude.degrees(180);\n        final Longitude minusOneEighty = Longitude.degrees(-180);\n        final Longitude ninety = Longitude.degrees(90);\n\n        Assert.assertTrue(oneEighty.isGreaterThan(minusOneEighty));\n        Assert.assertTrue(minusOneEighty.isLessThan(oneEighty));\n        Assert.assertFalse(minusOneEighty.isGreaterThanOrEqualTo(oneEighty));\n        Assert.assertTrue(oneEighty.isGreaterThanOrEqualTo(oneEighty));\n        Assert.assertTrue(oneEighty.isLessThanOrEqualTo(oneEighty));\n        Assert.assertTrue(oneEighty.isGreaterThan(ninety));\n        Assert.assertTrue(ninety.isLessThanOrEqualTo(oneEighty));\n    }\n\n    @Test\n    public void testCreation()\n    {\n        final Longitude one = Longitude.degrees(-122);\n        Assert.assertEquals(-1_220_000_000, one.asDm7());\n        try\n        {\n            Longitude.degrees(-122 - 100);\n            fail(\"Longitude should not have been created!\");\n        }\n        catch (final Exception e)\n        {\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/MultiPolyLineTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.MultiLineString;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolyLineConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonConstants;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonType;\nimport org.openstreetmap.atlas.streaming.readers.json.converters.PointCoordinateConverter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport com.google.gson.JsonObject;\n\n/**\n * {@link MultiPolyLine} tests.\n *\n * @author yalimu\n * @author mgostintsev\n */\npublic class MultiPolyLineTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(MultiPolyLineTest.class);\n\n    @Test\n    public void testBounds()\n    {\n        final String wkt = \"MULTILINESTRING ((113.9980787038803 7.3216002915048872, 113.99803847074506 7.3215225281339456), \"\n                + \"(113.99799555540086 7.3218335816030984, 113.99808806341453 7.3217805876994444))\";\n        final MultiPolyLine multiPolyLine = MultiPolyLine.wkt(wkt);\n        final Rectangle bound = multiPolyLine.bounds();\n\n        final List<Location> locations = Lists.newArrayList();\n        locations.add(Location.forString(\"7.3216002915048872, 113.9980787038803\"));\n        locations.add(Location.forString(\"7.3215225281339456, 113.99803847074506\"));\n        locations.add(Location.forString(\"7.3218335816030984, 113.99799555540086\"));\n        locations.add(Location.forString(\"7.3217805876994444, 113.99808806341453\"));\n        final Rectangle rectangle = Rectangle.forLocations(locations);\n        Assert.assertTrue(rectangle.equals(bound));\n    }\n\n    @Test\n    public void testConvertMultiPloyLineToGeoJson()\n    {\n        final String wkt = \"MULTILINESTRING ((113.9980787038803 7.3216002915048872, \"\n                + \"113.99803847074506 7.3215225281339456))\";\n        final MultiPolyLine multiPolyLine = MultiPolyLine.wkt(wkt);\n        final JsonObject geojson = multiPolyLine.asGeoJson();\n        Assert.assertEquals(GeoJsonType.MULTI_LINESTRING, GeoJsonType.forJson(geojson));\n        Assert.assertEquals(Location.forString(\"7.3216002915048872, 113.9980787038803\"),\n                new PointCoordinateConverter().revert()\n                        .apply(geojson.get(GeoJsonConstants.COORDINATES).getAsJsonArray().get(0)\n                                .getAsJsonArray().get(0).getAsJsonArray()));\n    }\n\n    @Test\n    public void testConvertMultiPloyLineToJson()\n    {\n        final String wkt = \"MULTILINESTRING ((113.9980787038803 7.3216002915048872, \"\n                + \"113.99803847074506 7.3215225281339456))\";\n        final MultiPolyLine multiPolyLine = MultiPolyLine.wkt(wkt);\n        final Set<Location> locations = Sets.newHashSet();\n        locations.add(Location.forString(\"7.3216002915048872, 113.9980787038803\"));\n        locations.add(Location.forString(\"7.3215225281339456, 113.99803847074506\"));\n        multiPolyLine.asLocationIterableProperties().forEach(property -> property.getLocations()\n                .forEach(location -> Assert.assertTrue(locations.contains(location))));\n    }\n\n    @Test\n    public void testCreateMultiLineStringFromMultiPolyLine()\n    {\n        final String wkt = \"MULTILINESTRING ((107.68471354246141 2.2346191319821231, 107.68471354246141 2.2345360028045156), \"\n                + \"(107.68454724550249 2.2345601370821555, 107.68453115224835 2.2345601370821555, \"\n                + \"107.68449419872607 2.2344243539043736))\";\n\n        final MultiPolyLine multiPolyLine = MultiPolyLine.wkt(wkt);\n        final PolyLine polyLine1 = PolyLine.wkt(\n                \"LINESTRING (107.68471354246141 2.2346191319821231, 107.68471354246141 2.2345360028045156)\");\n        final PolyLine polyLine2 = PolyLine\n                .wkt(\"LINESTRING (107.68454724550249 2.2345601370821555, \"\n                        + \"107.68453115224835 2.2345601370821555, 107.68449419872607 2.2344243539043736)\");\n\n        final LineString lineString1 = new JtsPolyLineConverter().convert(polyLine1);\n        final LineString lineString2 = new JtsPolyLineConverter().convert(polyLine2);\n\n        final MultiLineString multiLineString = new JtsMultiPolyLineConverter()\n                .convert(multiPolyLine);\n        Assert.assertTrue(\"First line is contained\", multiLineString.contains(lineString1));\n        Assert.assertTrue(\"Second line is contained\", multiLineString.contains(lineString2));\n    }\n\n    @Test\n    public void testCreateMultiPolyLineFromPolyLine()\n    {\n        final PolyLine polyLine1 = PolyLine.wkt(\n                \"LINESTRING (10.5553105 48.3419094, 10.5552096 48.3417501, 10.5551312 48.3416583, \"\n                        + \"10.5551027 48.341611, 10.5550183 48.3415143, 10.5549357 48.3414668, \"\n                        + \"10.5548325 48.3414164, 10.5548105 48.3415201, 10.5548015 48.3415686, \"\n                        + \"10.5548925 48.3416166, 10.5550334 48.3416375, 10.5551312 48.3416583)\");\n        final PolyLine polyLine2 = PolyLine.wkt(\n                \"LINESTRING (10.5551312 48.3416583, 10.5551027 48.341611, 10.5550183 48.3415143, \"\n                        + \"10.5549357 48.3414668, 10.5548325 48.3414164, 10.5548105 48.3415201, \"\n                        + \"10.5548015 48.3415686, 10.5548925 48.3416166, 10.5550334 48.3416375)\");\n        final MultiPolyLine multiPolyLine = new MultiPolyLine(Arrays.asList(polyLine1, polyLine2));\n        logger.info(\"Create MultiPolyLine from a list of PolyLine {}\", multiPolyLine.toString());\n        final Set<PolyLine> polyLines = Sets.newHashSet();\n        multiPolyLine.iterator().forEachRemaining(polyLines::add);\n        Assert.assertEquals(2, polyLines.size());\n        Assert.assertTrue(polyLines.contains(polyLine1));\n        Assert.assertTrue(polyLines.contains(polyLine2));\n    }\n\n    @Test\n    public void testCreateMultiPolyLineFromWKT()\n    {\n        final String wkt = \"MULTILINESTRING ((107.68471354246141 2.2346191319821231, 107.68471354246141 2.2345360028045156), \"\n                + \"(107.68454724550249 2.2345601370821555, 107.68453115224835 2.2345601370821555, \"\n                + \"107.68449419872607 2.2344243539043736))\";\n\n        final MultiPolyLine multiPolyLine = MultiPolyLine.wkt(wkt);\n        logger.info(\"Create MultiPolyLine from wkt {}\", multiPolyLine.toString());\n        final String polyLineWkt1 = \"LINESTRING (107.68471354246141 2.2346191319821231, 107.68471354246141 2.2345360028045156)\";\n        final String polyLineWkt2 = \"LINESTRING (107.68454724550249 2.2345601370821555, \"\n                + \"107.68453115224835 2.2345601370821555, 107.68449419872607 2.2344243539043736))\";\n        final PolyLine polyLine1 = PolyLine.wkt(polyLineWkt1);\n        final PolyLine polyLine2 = PolyLine.wkt(polyLineWkt2);\n\n        final Set<PolyLine> polyLines = Sets.newHashSet();\n        multiPolyLine.iterator().forEachRemaining(polyLines::add);\n        Assert.assertTrue(polyLines.contains(polyLine1));\n        Assert.assertTrue(polyLines.contains(polyLine2));\n    }\n\n    @Test\n    public void testDuplicatedPolyLines()\n    {\n        final List<PolyLine> polyLines = new ArrayList<>();\n        final PolyLine polyLine = PolyLine.wkt(\n                \"LINESTRING (107.68471354246141 2.2346191319821231, 107.68471354246141 2.2345360028045156)\");\n        final PolyLine polyLine2 = PolyLine.wkt(\n                \"LINESTRING (107.68454724550249 2.2345601370821555, 107.68453115224835 2.2345601370821555, \"\n                        + \"107.68449419872607 2.2344243539043736)\");\n        polyLines.add(polyLine);\n        polyLines.add(polyLine);\n        polyLines.add(polyLine2);\n        final MultiPolyLine multiPolyLine = new MultiPolyLine(polyLines);\n\n        Assert.assertEquals(3, polyLines.size());\n        Assert.assertEquals(2, multiPolyLine.getPolyLineList().size());\n    }\n\n    @Test\n    public void testEquals()\n    {\n        final String wkt1 = \"MULTILINESTRING ((113.9980787038803 7.3216002915048872, \"\n                + \"113.99803847074506 7.3215225281339456))\";\n        final String wkt3 = \"MULTILINESTRING ((107.68471354246141 2.2346191319821231, 107.68471354246141 2.2345360028045156), \"\n                + \"(107.68454724550249 2.2345601370821555, 107.68453115224835 2.2345601370821555, \"\n                + \"107.68449419872607 2.2344243539043736))\";\n        // wkt4 = wkt3 with duplicated polylines\n        final String wkt4 = \"MULTILINESTRING ((107.68471354246141 2.2346191319821231, 107.68471354246141 2.2345360028045156), \"\n                + \"(107.68454724550249 2.2345601370821555, 107.68453115224835 2.2345601370821555, \"\n                + \"107.68449419872607 2.2344243539043736), \"\n                + \"(107.68471354246141 2.2346191319821231, 107.68471354246141 2.2345360028045156))\";\n        final MultiPolyLine multiPolyLine1 = MultiPolyLine.wkt(wkt1);\n        final MultiPolyLine multiPolyLine2 = MultiPolyLine.wkt(wkt1);\n        final MultiPolyLine multiPolyLine3 = MultiPolyLine.wkt(wkt3);\n        final MultiPolyLine multiPolyLine4 = MultiPolyLine.wkt(wkt4);\n\n        Assert.assertEquals(multiPolyLine1, multiPolyLine2);\n        Assert.assertNotSame(multiPolyLine1, multiPolyLine2);\n        Assert.assertNotEquals(null, multiPolyLine1);\n        Assert.assertNotEquals(multiPolyLine1, multiPolyLine3);\n        Assert.assertEquals(multiPolyLine3, multiPolyLine4);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/MultiPolygonTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.RelationOrAreaToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.openstreetmap.atlas.utilities.maps.MultiMapTest;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlasHandler;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonObject;\n\n/**\n * @author matthieun\n * @author hallahan\n */\npublic class MultiPolygonTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(MultiPolygonTest.class);\n\n    private static final MultiPolygon DEFAULT_MULTIPOLYGON = MultiPolygon\n            .wkt(\"MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),\"\n                    + \"((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),\"\n                    + \"(30 20, 20 15, 20 25, 30 20)))\");\n\n    public static MultiPolygon getFrom(final String name)\n    {\n        return getFrom(name, MultiPolygonTest.class);\n    }\n\n    public static MultiPolygon getFrom(final String name, final Class<?> clazz)\n    {\n        if (name.endsWith(FileSuffix.WKT.toString()))\n        {\n            final String wkt = new InputStreamResource(() -> clazz.getResourceAsStream(name)).all();\n            return MultiPolygon.wkt(wkt);\n        }\n        else if (name.endsWith(\".josm.osm\"))\n        {\n            final Atlas atlas = TestAtlasHandler.getAtlasFromJosmOsmResource(true,\n                    new InputStreamResource(() -> clazz.getResourceAsStream(name)), name);\n            return new RelationOrAreaToMultiPolygonConverter()\n                    .convert(atlas.relations().iterator().next());\n        }\n        throw new CoreException(\"Unknown File Type {}\", name);\n    }\n\n    @Test\n    public void testAsGeoJsonGeometry()\n    {\n        final String geoJson = \"{\\\"type\\\":\\\"MultiPolygon\\\",\\\"coordinates\\\":[[[[40.0,40.0],[20.0,45.0],[45.0,30.0],[40.0,40.0]]],[[[20.0,35.0],[10.0,30.0],[10.0,10.0],[30.0,5.0],[45.0,20.0],[20.0,35.0]],[[30.0,20.0],[20.0,15.0],[20.0,25.0],[30.0,20.0]]]]}\";\n        final JsonObject geometry = DEFAULT_MULTIPOLYGON.asGeoJsonGeometry();\n        Assert.assertEquals(geoJson, geometry.toString());\n    }\n\n    @Test\n    public void testCoversPolygon()\n    {\n        logger.info(\"multiPolygon: {}\", DEFAULT_MULTIPOLYGON.toWkt());\n        final Polygon coveringPolygon = new Polygon(Location.forString(\"48.861903, 2.344141\"),\n                Location.forString(\"6.215559, 1.431353\"),\n                Location.forString(\"-1.302400, 36.818213\"),\n                Location.forString(\"22.648164, 50.364465\"));\n        logger.info(\"coveringPolygon: {}\", coveringPolygon.toWkt());\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.overlaps(coveringPolygon));\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.intersects(coveringPolygon));\n\n        final Polygon insideInnerPolygon = new Polygon(Location.forString(\"20.146558, 23.310950\"),\n                Location.forString(\"19.623812, 24.507328\"),\n                Location.forString(\"19.247746, 23.339148\"));\n        logger.info(\"insideInnerPolygon: {}\", insideInnerPolygon.toWkt());\n        Assert.assertFalse(DEFAULT_MULTIPOLYGON.overlaps(insideInnerPolygon));\n        Assert.assertFalse(DEFAULT_MULTIPOLYGON.intersects(insideInnerPolygon));\n\n        final Polygon intersectingInnerPolygon = new Polygon(\n                Location.forString(\"20.146558, 23.310950\"),\n                Location.forString(\"19.623812, 24.507328\"),\n                Location.forString(\"27.156014, 30.298381\"));\n        logger.info(\"intersectingInnerPolygon: {}\", intersectingInnerPolygon.toWkt());\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.overlaps(intersectingInnerPolygon));\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.intersects(intersectingInnerPolygon));\n\n        final Polygon intersectingOuterPolygon = new Polygon(\n                Location.forString(\"48.861903, 2.344141\"),\n                Location.forString(\"22.648164, 50.364465\"),\n                Location.forString(\"27.156014, 30.298381\"));\n        logger.info(\"intersectingOuterPolygon: {}\", intersectingOuterPolygon.toWkt());\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.overlaps(intersectingInnerPolygon));\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.intersects(intersectingInnerPolygon));\n    }\n\n    @Test\n    public void testCoverssPolyline()\n    {\n        final PolyLine nonCoveredLine = new PolyLine(Location.forString(\"19.507134, 23.999584\"),\n                Location.forString(\"32.062144, 32.049430\"));\n        Assert.assertFalse(DEFAULT_MULTIPOLYGON.fullyGeometricallyEncloses(nonCoveredLine));\n        final PolyLine coveredLine = new PolyLine(Location.forString(\"32.095270, 19.955880\"),\n                Location.forString(\"22.175213, 36.610574\"));\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.fullyGeometricallyEncloses(coveredLine));\n        final PolyLine outside = PolyLine.TEST_POLYLINE;\n        Assert.assertFalse(DEFAULT_MULTIPOLYGON.fullyGeometricallyEncloses(outside));\n        final PolyLine jumpyLine = new PolyLine(Location.forString(\"32.095270, 19.955880\"),\n                Location.forString(\"39.927387, 32.842205\"));\n        Assert.assertFalse(DEFAULT_MULTIPOLYGON.fullyGeometricallyEncloses(jumpyLine));\n    }\n\n    @Test\n    public void testEmptyMultiPolygon()\n    {\n        final MultiPolygon empty = new MultiPolygon(new MultiMap<>());\n        Assert.assertNull(empty.bounds());\n    }\n\n    @Test\n    public void testFullyGeometricallyEncloses()\n    {\n        final MultiPolygon multiPolygon1 = MultiPolygon\n                .wkt(\"MULTIPOLYGON (\" + \"((-10 10, 10 10, 10 -10, -10 -10, -10 10),\"\n                        + \"(-5 5, 5 5, 5 -5, -5 -5, -5 5)))\");\n        final MultiPolygon multiPolygon2 = MultiPolygon.wkt(\"MULTIPOLYGON (\"\n                + \"((-8 8, 8 8, 8 -8, -8 -8, -8 8),\" + \"(-6 6, 6 6, 6 -6, -6 -6, -6 6)))\");\n        final MultiPolygon multiPolygon3 = MultiPolygon.wkt(\"MULTIPOLYGON (\"\n                + \"((-8 8, 8 8, 8 -8, -8 -8, -8 8),\" + \"(-4 4, 4 4, 4 -4, -4 -4, -4 4)))\");\n        final MultiPolygon multiPolygon4 = MultiPolygon\n                .wkt(\"MULTIPOLYGON (\" + \"((-8 8, 8 8, 8 -8, -8 -8, -8 8)))\");\n        final MultiPolygon multiPolygon5 = MultiPolygon.wkt(\"MULTIPOLYGON (\"\n                + \"((-4 4, 4 4, 4 -4, -4 -4, -4 4),\" + \"(-3 3, 3 3, 3 -3, -3 -3, -3 3)))\");\n        final MultiPolygon multiPolygon6 = MultiPolygon\n                .wkt(\"MULTIPOLYGON (\" + \"((8 9, 9 9, 9 8, 8 8, 8 9),\"\n                        + \"(8.25 8.75, 8.75 8.75, 8.75 8.25, 8.25 8.25, 8.25 8.75)))\");\n        final MultiPolygon multiPolygon7 = MultiPolygon\n                .wkt(\"MULTIPOLYGON (\" + \"((89 90, 90 90, 90 89, 89 89, 89 90),\"\n                        + \"(89.25 89.75, 89.75 89.75, 89.75 89.25, 89.25 89.25, 89.25 89.75)))\");\n        // Test Multipolygon enclose\n        Assert.assertTrue(multiPolygon1.fullyGeometricallyEncloses(multiPolygon2));\n        Assert.assertFalse(multiPolygon2.fullyGeometricallyEncloses(multiPolygon1));\n        Assert.assertFalse(multiPolygon1.fullyGeometricallyEncloses(multiPolygon3));\n        Assert.assertFalse(multiPolygon1.fullyGeometricallyEncloses(multiPolygon4));\n        Assert.assertFalse(multiPolygon1.fullyGeometricallyEncloses(multiPolygon5));\n        Assert.assertFalse(multiPolygon5.fullyGeometricallyEncloses(multiPolygon1));\n        Assert.assertFalse(\n                multiPolygon1.fullyGeometricallyEncloses(multiPolygon2.merge(multiPolygon5)));\n        Assert.assertTrue(multiPolygon1.fullyGeometricallyEncloses(multiPolygon6));\n        Assert.assertFalse(multiPolygon1.fullyGeometricallyEncloses(multiPolygon7));\n\n        final PolyLine polyLine1 = PolyLine.wkt(\"LINESTRING (6 6, -6 -6)\");\n        final PolyLine polyLine2 = PolyLine.wkt(\"LINESTRING (6 6, -6 6, -6 -6, 6 -6)\");\n        final Polygon polygon1 = Polygon.wkt(\"POLYGON ((6 6, -6 6, -6 -6, 6 -6, 6 6))\");\n\n        // Test Polyline Enclose\n        Assert.assertFalse(multiPolygon1.fullyGeometricallyEncloses(polyLine1));\n        Assert.assertTrue(multiPolygon1.fullyGeometricallyEncloses(polyLine2));\n        Assert.assertFalse(multiPolygon1.fullyGeometricallyEncloses(polygon1));\n    }\n\n    @Test\n    public void testOverlap()\n    {\n        final MultiPolygon multiPolygon1 = MultiPolygon.wkt(\"MULTIPOLYGON (\"\n                + \"((0 50, 50 50, 50 0, 0 0, 0 50),\" + \"(5 45, 45 45, 45 5, 5 5, 5 45)))\");\n        final MultiPolygon multiPolygon2 = MultiPolygon\n                .wkt(\"MULTIPOLYGON (\" + \"((10 35, 35 35, 35 10, 10 10, 10 35),\"\n                        + \"(15 30, 30 30, 30 15, 15 15, 15 30)))\");\n        final MultiPolygon multiPolygon3 = DEFAULT_MULTIPOLYGON;\n\n        Assert.assertFalse(multiPolygon1.overlaps(multiPolygon2));\n        Assert.assertFalse(multiPolygon2.overlaps(multiPolygon1));\n        Assert.assertTrue(multiPolygon2.overlaps(multiPolygon3));\n        Assert.assertTrue(multiPolygon3.overlaps(multiPolygon2));\n    }\n\n    @Test\n    public void testSerialization() throws ClassNotFoundException\n    {\n        final MultiPolygon map = new MultiPolygon(MultiMapTest.getMultiMap());\n        final WritableResource out = new ByteArrayResource();\n        try (ObjectOutputStream outStream = new ObjectOutputStream(out.write()))\n        {\n            outStream.writeObject(map);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Unable to write to {}\", out, e);\n        }\n\n        try (ObjectInputStream inStream = new ObjectInputStream(out.read()))\n        {\n            final MultiPolygon result = (MultiPolygon) inStream.readObject();\n            Assert.assertEquals(map, result);\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Unable to read from {}\", out, e);\n        }\n    }\n\n    // Adapted from PolygonTest.testSurface()\n    @Test\n    public void testSurface()\n    {\n        final Rectangle rectangle = Rectangle.TEST_RECTANGLE;\n        Assert.assertEquals(rectangle.surface(), MultiPolygon.forPolygon(rectangle).surface());\n\n        final MultiPolygon tilted = MultiPolygon.forPolygon(new Polygon(rectangle.lowerLeft(),\n                new Location(rectangle.upperLeft().getLatitude(),\n                        Longitude.degrees(rectangle.upperLeft().getLongitude().asDegrees() + 0.1)),\n                new Location(rectangle.upperRight().getLatitude(),\n                        Longitude.degrees(rectangle.upperRight().getLongitude().asDegrees() + 0.1)),\n                rectangle.lowerRight()));\n        Assert.assertEquals(rectangle.surface(), tilted.surface());\n    }\n\n    @Test\n    public void testValidity0()\n    {\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.isOGCValid());\n        Assert.assertTrue(DEFAULT_MULTIPOLYGON.isOSMValid());\n    }\n\n    @Test\n    public void testValidity1()\n    {\n        // Self intersecting multipolygon\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom1.wkt\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertFalse(multiPolygon.isOSMValid());\n    }\n\n    @Test\n    public void testValidity10()\n    {\n        // Two inners hug each other OSM valid, but two inners do not\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom10.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        // Assert.assertFalse(multiPolygon.isOSMValid());\n    }\n\n    @Test\n    public void testValidity2()\n    {\n        // Two inners touch each other on a segment\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom2.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertTrue(multiPolygon.isOSMValid());\n    }\n\n    @Test\n    public void testValidity3()\n    {\n        // Three inners touch each other in one segment each (2 segments in total) and one point\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom3.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertTrue(multiPolygon.isOSMValid());\n    }\n\n    @Test\n    public void testValidity4()\n    {\n        // Three inners touch each other in one segment each (2 segments in total)\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom4.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertTrue(multiPolygon.isOSMValid());\n    }\n\n    @Test(expected = CoreException.class)\n    public void testValidity5()\n    {\n        // One of the inners intersects an outer\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom5.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertFalse(multiPolygon.isOSMValid());\n    }\n\n    @Test\n    public void testValidity6()\n    {\n        // Nested inners\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom6.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertFalse(multiPolygon.isOSMValid());\n    }\n\n    @Test\n    public void testValidity7()\n    {\n        // Nested outers\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom7.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertFalse(multiPolygon.isOSMValid());\n    }\n\n    @Test\n    public void testValidity8()\n    {\n        // Duplicate inners\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom8.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertFalse(multiPolygon.isOSMValid());\n    }\n\n    @Test(expected = CoreException.class)\n    public void testValidity9()\n    {\n        // Inner outside outer, hugging it along a segment\n        final MultiPolygon multiPolygon = getFrom(\"MultiPolygonTestGeom9.josm.osm\");\n        Assert.assertFalse(multiPolygon.isOGCValid());\n        Assert.assertFalse(multiPolygon.isOSMValid());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/PolyLineCoveringPolygonTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * Tests overlap/containment/intersection functionality between a {@link Polygon} and\n * {@link PolyLine}.\n *\n * @author mgostintsev\n */\npublic class PolyLineCoveringPolygonTest\n{\n    @Rule\n    public final PolyLineCoveringPolygonTestRule rule = new PolyLineCoveringPolygonTestRule();\n\n    @Test\n    public void testPolyLinesAsEntirePolygonBoundary()\n    {\n        final Atlas atlas = this.rule.getPolyLinesAsEntirePolygonBoundaryAtlas();\n        verifyOverlapExists(atlas);\n        verifyFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testPolyLinesAsPartOfPolygonBoundary()\n    {\n        final Atlas atlas = this.rule.getPolyLinesAsPartOfPolygonBoundaryAtlas();\n        verifyOverlapExists(atlas);\n        verifyFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testPolyLinesCuttingThroughPolygon()\n    {\n        final Atlas atlas = this.rule.getPolyLinesCuttingThroughPolygonAtlas();\n        verifyOverlapExists(atlas);\n        verifyNoFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testPolyLinesFullyInsidePolygon()\n    {\n        final Atlas atlas = this.rule.getPolyLinesFullyInsidePolygonAtlas();\n        verifyOverlapExists(atlas);\n        verifyFullContainment(atlas);\n        verifyNoIntersection(atlas);\n    }\n\n    @Test\n    public void testPolyLinesFullyOutsidePolygon()\n    {\n        final Atlas atlas = this.rule.getPolyLinesFullyOutsidePolygonAtlas();\n        verifyNoOverlap(atlas);\n        verifyNoFullContainment(atlas);\n        verifyNoIntersection(atlas);\n    }\n\n    @Test\n    public void testPolyLinesHalfwayInsidePolygon()\n    {\n        final Atlas atlas = this.rule.getPolyLinesHalfwayInsidePolygonAtlas();\n        verifyOverlapExists(atlas);\n        verifyNoFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testPolyLinesTouchingPolygonBoundary()\n    {\n        final Atlas atlas = this.rule.getPolyLinesTouchingPolygonBoundaryAtlas();\n        verifyOverlapExists(atlas);\n        verifyNoFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testPolyLinesTouchingPolygonVertex()\n    {\n        final Atlas atlas = this.rule.getPolyLinesTouchingPolygonVertexAtlas();\n        verifyOverlapExists(atlas);\n        verifyNoFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    private void verifyFullContainment(final Atlas atlas)\n    {\n        Assert.assertTrue(atlas.area(1).asPolygon()\n                .fullyGeometricallyEncloses(atlas.edge(159019301).asPolyLine()));\n        Assert.assertTrue(atlas.area(1).asPolygon()\n                .fullyGeometricallyEncloses(atlas.edge(-159019301).asPolyLine()));\n    }\n\n    private void verifyIntersection(final Atlas atlas)\n    {\n        Assert.assertTrue(atlas.area(1).asPolygon().intersects(atlas.edge(159019301).asPolyLine()));\n        Assert.assertTrue(\n                atlas.area(1).asPolygon().intersects(atlas.edge(-159019301).asPolyLine()));\n    }\n\n    private void verifyNoFullContainment(final Atlas atlas)\n    {\n        Assert.assertFalse(atlas.area(1).asPolygon()\n                .fullyGeometricallyEncloses(atlas.edge(159019301).asPolyLine()));\n        Assert.assertFalse(atlas.area(1).asPolygon()\n                .fullyGeometricallyEncloses(atlas.edge(-159019301).asPolyLine()));\n    }\n\n    private void verifyNoIntersection(final Atlas atlas)\n    {\n        Assert.assertFalse(\n                atlas.area(1).asPolygon().intersects(atlas.edge(159019301).asPolyLine()));\n        Assert.assertFalse(\n                atlas.area(1).asPolygon().intersects(atlas.edge(-159019301).asPolyLine()));\n    }\n\n    private void verifyNoOverlap(final Atlas atlas)\n    {\n        Assert.assertFalse(atlas.area(1).asPolygon().overlaps(atlas.edge(159019301).asPolyLine()));\n        Assert.assertFalse(atlas.area(1).asPolygon().overlaps(atlas.edge(-159019301).asPolyLine()));\n    }\n\n    private void verifyOverlapExists(final Atlas atlas)\n    {\n        Assert.assertTrue(atlas.area(1).asPolygon().overlaps(atlas.edge(159019301).asPolyLine()));\n        Assert.assertTrue(atlas.area(1).asPolygon().overlaps(atlas.edge(-159019301).asPolyLine()));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/PolyLineCoveringPolygonTestRule.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * {@link PolyLineCoveringPolygonTest} data\n *\n * @author mgostintsev\n */\npublic class PolyLineCoveringPolygonTestRule extends CoreTestRule\n{\n    // Rectangle #1\n    private static final String OUTER_TOP_LEFT = \"39.9978684, 116.2741473\";\n    private static final String OUTER_TOP_RIGHT = \"39.9979242, 116.2747418\";\n    private static final String OUTER_BOTTOM_RIGHT = \"39.9975751, 116.2748088\";\n    private static final String OUTER_BOTTOM_LEFT = \"39.9975152, 116.2741904\";\n    private static final String OUTER_ABOVE_BOTTOM_LEFT = \"39.9976033, 116.2741749\";\n\n    // Polygon #2, fully contained within Rectangle #1\n    private static final String INNER_TOP_LEFT = \"39.9977887, 116.2743726\";\n    private static final String INNER_TOP_RIGHT = \"39.9977948, 116.2744396\";\n    private static final String INNER_BOTTOM_RIGHT = \"39.9977096, 116.2744528\";\n\n    // Three points, fully outside of Rectangle #1 and #2\n    private static final String POINT_OUTSIDE_OUTER = \"39.9977319, 116.2740253\";\n    private static final String SECOND_POINT_OUTSIDE_OUTER = \"39.9976067, 116.2740461\";\n    private static final String THIRD_POINT_OUTSIDE_OUTER = \"39.9980675, 116.274285\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = POINT_OUTSIDE_OUTER)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SECOND_POINT_OUTSIDE_OUTER)),\n\n            }, edges = { @Edge(id = \"159019301\", coordinates = { @Loc(value = POINT_OUTSIDE_OUTER),\n                    @Loc(value = SECOND_POINT_OUTSIDE_OUTER) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = {\n                            @Loc(value = SECOND_POINT_OUTSIDE_OUTER),\n                            @Loc(value = POINT_OUTSIDE_OUTER) }, tags = { \"highway=tertiary\" })\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polyLinesFullyOutsidePolygon;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = INNER_TOP_LEFT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SECOND_POINT_OUTSIDE_OUTER)),\n                    @Node(id = \"7\", coordinates = @Loc(value = THIRD_POINT_OUTSIDE_OUTER)),\n\n            }, edges = { @Edge(id = \"159019301\", coordinates = {\n                    @Loc(value = THIRD_POINT_OUTSIDE_OUTER), @Loc(value = INNER_TOP_LEFT),\n                    @Loc(value = SECOND_POINT_OUTSIDE_OUTER) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = {\n                            @Loc(value = SECOND_POINT_OUTSIDE_OUTER), @Loc(value = INNER_TOP_LEFT),\n                            @Loc(value = THIRD_POINT_OUTSIDE_OUTER) }, tags = {\n                                    \"highway=tertiary\" })\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polyLinesCuttingThroughPolygon;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = SECOND_POINT_OUTSIDE_OUTER)),\n\n            }, edges = { @Edge(id = \"159019301\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                    @Loc(value = SECOND_POINT_OUTSIDE_OUTER) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = {\n                            @Loc(value = SECOND_POINT_OUTSIDE_OUTER),\n                            @Loc(value = OUTER_TOP_LEFT) }, tags = { \"highway=tertiary\" })\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polyLinesTouchingPolygonVertex;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = OUTER_ABOVE_BOTTOM_LEFT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SECOND_POINT_OUTSIDE_OUTER))\n\n            }, edges = {\n                    @Edge(id = \"159019301\", coordinates = { @Loc(value = OUTER_ABOVE_BOTTOM_LEFT),\n                            @Loc(value = SECOND_POINT_OUTSIDE_OUTER) }, tags = {\n                                    \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = {\n                            @Loc(value = SECOND_POINT_OUTSIDE_OUTER),\n                            @Loc(value = OUTER_ABOVE_BOTTOM_LEFT) }, tags = { \"highway=tertiary\" })\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT),\n                            @Loc(OUTER_ABOVE_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polyLinesTouchingPolygonBoundary;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = INNER_TOP_LEFT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = INNER_TOP_RIGHT)),\n                    @Node(id = \"7\", coordinates = @Loc(value = INNER_BOTTOM_RIGHT)),\n                    @Node(id = \"8\", coordinates = @Loc(value = POINT_OUTSIDE_OUTER)) }, edges = {\n                            @Edge(id = \"159019301\", coordinates = { @Loc(value = INNER_TOP_LEFT),\n                                    @Loc(value = INNER_TOP_RIGHT),\n                                    @Loc(value = INNER_BOTTOM_RIGHT) }, tags = {\n                                            \"highway=tertiary\" }),\n                            @Edge(id = \"-159019301\", coordinates = {\n                                    @Loc(value = INNER_BOTTOM_RIGHT), @Loc(value = INNER_TOP_RIGHT),\n                                    @Loc(value = INNER_TOP_LEFT) }, tags = { \"highway=tertiary\" })\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polyLinesFullyInsidePolygon;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = INNER_TOP_RIGHT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SECOND_POINT_OUTSIDE_OUTER))\n\n            }, edges = {\n                    @Edge(id = \"159019301\", coordinates = {\n                            @Loc(value = SECOND_POINT_OUTSIDE_OUTER),\n                            @Loc(value = INNER_TOP_RIGHT) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = { @Loc(value = INNER_TOP_RIGHT),\n                            @Loc(value = SECOND_POINT_OUTSIDE_OUTER) }, tags = {\n                                    \"highway=tertiary\" })\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polyLinesHalfwayInsidePolygon;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = OUTER_ABOVE_BOTTOM_LEFT))\n\n            }, edges = {\n                    @Edge(id = \"159019301\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_ABOVE_BOTTOM_LEFT) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = { @Loc(value = OUTER_ABOVE_BOTTOM_LEFT),\n                            @Loc(value = OUTER_TOP_LEFT) }, tags = { \"highway=tertiary\" })\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT),\n                            @Loc(OUTER_ABOVE_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polyLinesAsPartOfPolygonBoundary;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = OUTER_ABOVE_BOTTOM_LEFT))\n\n            }, edges = {\n                    @Edge(id = \"159019301\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT), @Loc(value = OUTER_ABOVE_BOTTOM_LEFT),\n                            @Loc(value = OUTER_TOP_LEFT) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_ABOVE_BOTTOM_LEFT), @Loc(value = OUTER_BOTTOM_LEFT),\n                            @Loc(value = OUTER_BOTTOM_RIGHT), @Loc(value = OUTER_TOP_RIGHT),\n                            @Loc(value = OUTER_TOP_LEFT) }, tags = { \"highway=tertiary\" })\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT),\n                            @Loc(OUTER_ABOVE_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polyLinesAsEntirePolygonBoundary;\n\n    public Atlas getPolyLinesAsEntirePolygonBoundaryAtlas()\n    {\n        return this.polyLinesAsEntirePolygonBoundary;\n    }\n\n    public Atlas getPolyLinesAsPartOfPolygonBoundaryAtlas()\n    {\n        return this.polyLinesAsPartOfPolygonBoundary;\n    }\n\n    public Atlas getPolyLinesCuttingThroughPolygonAtlas()\n    {\n        return this.polyLinesCuttingThroughPolygon;\n    }\n\n    public Atlas getPolyLinesFullyInsidePolygonAtlas()\n    {\n        return this.polyLinesFullyInsidePolygon;\n    }\n\n    public Atlas getPolyLinesFullyOutsidePolygonAtlas()\n    {\n        return this.polyLinesFullyOutsidePolygon;\n    }\n\n    public Atlas getPolyLinesHalfwayInsidePolygonAtlas()\n    {\n        return this.polyLinesHalfwayInsidePolygon;\n    }\n\n    public Atlas getPolyLinesTouchingPolygonBoundaryAtlas()\n    {\n        return this.polyLinesTouchingPolygonBoundary;\n    }\n\n    public Atlas getPolyLinesTouchingPolygonVertexAtlas()\n    {\n        return this.polyLinesTouchingPolygonVertex;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/PolyLineTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.converters.WktPolyLineConverter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonObject;\n\n/**\n * @author matthieun\n * @author mgostintsev\n * @author hallahan\n */\npublic class PolyLineTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(PolyLineTest.class);\n\n    @Test\n    public void testAntimeridianHandling()\n    {\n        final PolyLine antimeridianWest = new PolyLine(\n                new Location(Latitude.degrees(40), Longitude.ANTIMERIDIAN_WEST),\n                new Location(Latitude.degrees(41), Longitude.ANTIMERIDIAN_WEST));\n        final PolyLine antimeridianEast = new PolyLine(\n                new Location(Latitude.degrees(40), Longitude.ANTIMERIDIAN_EAST),\n                new Location(Latitude.degrees(41), Longitude.ANTIMERIDIAN_EAST));\n\n        Assert.assertTrue(antimeridianWest.intersections(antimeridianEast).isEmpty());\n        Assert.assertTrue(antimeridianEast.length().equals(antimeridianWest.length()));\n        Assert.assertTrue(antimeridianEast.length().isLessThan(Distance.miles(100)));\n    }\n\n    @Test\n    public void testAppend()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final PolyLine line2 = new PolyLine(Location.TEST_1, Location.TEST_7);\n        final PolyLine appended = line.append(line2);\n        Assert.assertTrue(appended.equalsShape(\n                new PolyLine(Location.CROSSING_85_280, Location.TEST_1, Location.TEST_7)));\n    }\n\n    @Test\n    public void testAsGeoJsonGeometry()\n    {\n        final PolyLine polyLine = PolyLine.wkt(\n                \"LINESTRING (-75.616330 40.194570, -75.616330 40.194570, -75.616330 40.194570, -75.616340 40.194580, -75.616340 40.194590)\");\n        final String geoJson = \"{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-75.61633,40.19457],[-75.61633,40.19457],[-75.61633,40.19457],[-75.61634,40.19458],[-75.61634,40.19459]]}\";\n        final JsonObject geometry = polyLine.asGeoJsonGeometry();\n        Assert.assertEquals(geoJson, geometry.toString());\n    }\n\n    @Test\n    public void testContains()\n    {\n        final PolyLine line = PolyLine.wkt(\n                \"LINESTRING (10.5553105 48.3419094, 10.5552096 48.3417501, 10.5551312 48.3416583, \"\n                        + \"10.5551027 48.341611, 10.5550183 48.3415143, 10.5549357 48.3414668, \"\n                        + \"10.5548325 48.3414164, 10.5548105 48.3415201, 10.5548015 48.3415686, \"\n                        + \"10.5548925 48.3416166, 10.5550334 48.3416375, 10.5551312 48.3416583)\");\n        Assert.assertTrue(\"Verifying PolyLine contains Location\",\n                line.contains(Location.forString(\"48.3419094, 10.5553105\")));\n        Assert.assertFalse(\"Verifying PolyLine doesn't contain Location\",\n                line.contains(Location.COLOSSEUM));\n        Assert.assertTrue(\"Verifying PolyLine contains Segment\",\n                line.contains(new Segment(Location.forString(\"48.3419094, 10.5553105\"),\n                        Location.forString(\"48.3417501, 10.5552096\"))));\n        Assert.assertFalse(\"Verifying PolyLine doesn't contain Segment\",\n                line.contains(new Segment(Location.TEST_2, Location.COLOSSEUM)));\n    }\n\n    @Test\n    public void testDistanceCost()\n    {\n        final PolyLine source = new PolyLine(Location.CROSSING_85_280, Location.TEST_2,\n                Location.TEST_1);\n        final PolyLine destination = new PolyLine(Location.CROSSING_85_280, Location.TEST_6,\n                Location.TEST_1);\n        final Distance distanceToOneWay = source.averageOneWayDistanceTo(destination);\n        final Distance distanceFromOneWay = destination.averageOneWayDistanceTo(source);\n        final Distance distanceTo = source.averageDistanceTo(destination);\n        final Distance distanceFrom = destination.averageDistanceTo(source);\n        logger.info(\"distanceToOneWay: {}\", distanceToOneWay);\n        logger.info(\"distanceFromOneWay: {}\", distanceFromOneWay);\n        logger.info(\"distanceTo: {}\", distanceTo);\n        logger.info(\"distanceFrom: {}\", distanceFrom);\n        Assert.assertEquals(distanceTo, distanceFrom);\n        Assert.assertTrue(\n                Location.TEST_2.distanceTo(Location.TEST_6).asMeters() / 3 > distanceTo.asMeters());\n    }\n\n    @Test\n    public void testEqualsShape()\n    {\n        final PolyLine polyLine1 = PolyLine.wkt(\n                \"LINESTRING (10.5553105 48.3419094, 10.5552096 48.3417501, 10.5551312 48.3416583, \"\n                        + \"10.5551027 48.341611, 10.5550183 48.3415143, 10.5549357 48.3414668, \"\n                        + \"10.5548325 48.3414164, 10.5548105 48.3415201, 10.5548015 48.3415686, \"\n                        + \"10.5548925 48.3416166, 10.5550334 48.3416375, 10.5551312 48.3416583)\");\n        final PolyLine polyLine2 = PolyLine.wkt(\n                \"LINESTRING (10.5551312 48.3416583, 10.5551027 48.341611, 10.5550183 48.3415143, \"\n                        + \"10.5549357 48.3414668, 10.5548325 48.3414164, 10.5548105 48.3415201, \"\n                        + \"10.5548015 48.3415686, 10.5548925 48.3416166, 10.5550334 48.3416375, \"\n                        + \"10.5551312 48.3416583, 10.5552096 48.3417501, 10.5553105 48.3419094)\");\n        Assert.assertFalse(polyLine1.equals(polyLine2));\n        Assert.assertTrue(polyLine1.equalsShape(polyLine2));\n    }\n\n    @Test\n    public void testInnerLocations()\n    {\n        final PolyLine source = new PolyLine(Location.CROSSING_85_280, Location.TEST_7,\n                Location.TEST_1);\n        Assert.assertTrue(\"Make sure there is a single inner location\",\n                source.innerLocations().iterator().next().equals(Location.TEST_7));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testInvalidAppend()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final PolyLine line2 = new PolyLine(Location.CROSSING_85_280, Location.TEST_7);\n        @SuppressWarnings(\"unused\")\n        final PolyLine appended = line.append(line2);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testInvalidPrepend()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final PolyLine line2 = new PolyLine(Location.CROSSING_85_280, Location.TEST_7);\n        @SuppressWarnings(\"unused\")\n        final PolyLine prepended = line.prepend(line2);\n    }\n\n    @Test\n    public void testNoInnerLocations()\n    {\n        final PolyLine source = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        Assert.assertTrue(\"Make sure the iterable is empty\",\n                Iterables.isEmpty(source.innerLocations()));\n    }\n\n    @Test\n    public void testOverallHeading()\n    {\n        final PolyLine line1 = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final PolyLine line2 = new PolyLine(Location.CROSSING_85_280, Location.TEST_1,\n                Location.CROSSING_85_280);\n        final PolyLine line3 = new PolyLine(Location.CROSSING_85_280);\n        Assert.assertTrue(line1.overallHeading().isPresent());\n        Assert.assertEquals(Heading.degrees(85.5165015), line1.overallHeading().get());\n        Assert.assertFalse(line2.overallHeading().isPresent());\n        Assert.assertFalse(line3.overallHeading().isPresent());\n    }\n\n    @Test\n    public void testOverlapsShape()\n    {\n        final PolyLine larger = new PolyLine(Location.CROSSING_85_280, Location.TEST_1,\n                Location.TEST_7);\n        final PolyLine smaller = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final PolyLine smallerReversed = new PolyLine(Location.TEST_1, Location.CROSSING_85_280);\n\n        Assert.assertTrue(larger.overlapsShapeOf(smaller));\n        Assert.assertTrue(larger.overlapsShapeOf(smallerReversed));\n        Assert.assertTrue(smaller.overlapsShapeOf(smallerReversed));\n        Assert.assertTrue(smallerReversed.overlapsShapeOf(smaller));\n\n        Assert.assertFalse(smaller.overlapsShapeOf(larger));\n        Assert.assertFalse(smallerReversed.overlapsShapeOf(larger));\n    }\n\n    @Test\n    public void testPrepend()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final PolyLine line2 = new PolyLine(Location.TEST_7, Location.CROSSING_85_280);\n        final PolyLine prepended = line.prepend(line2);\n        Assert.assertTrue(prepended.equalsShape(\n                new PolyLine(Location.TEST_7, Location.CROSSING_85_280, Location.TEST_1)));\n    }\n\n    @Test\n    public void testSelfIntersects()\n    {\n        final PolyLine polyLine = new WktPolyLineConverter().backwardConvert(\n                \"LINESTRING(-122.0095413 37.3362091,-122.0095716 37.3353178,-122.009566 37.33531,-122.0095604 37.3353178,-122.0095907 37.3362091)\");\n        Assert.assertTrue(polyLine.selfIntersects());\n        Assert.assertEquals(1, polyLine.selfIntersections().size());\n    }\n\n    @Test\n    public void testSelfIntersectsClosedLoop()\n    {\n        final PolyLine polyLine = PolyLine.wkt(\"LINESTRING(1 1, 2 2, 3 3, 3 1, 1 1)\");\n        Assert.assertTrue(polyLine.selfIntersects());\n        final Set<Location> intersections = polyLine.selfIntersections();\n        Assert.assertEquals(1, intersections.size());\n        Assert.assertEquals(Location.forString(\"1,1\"), intersections.toArray()[0]);\n    }\n\n    @Test\n    public void testSelfIntersectsNoIntersections()\n    {\n        final PolyLine polyLine = PolyLine.wkt(\"LINESTRING(1 1, 2 2, 3 3, 3 1)\");\n        Assert.assertFalse(polyLine.selfIntersects());\n        Assert.assertEquals(0, polyLine.selfIntersections().size());\n    }\n\n    @Test\n    public void testSelfIntersectsPolygonNoIntersections()\n    {\n        final Polygon polygon = Polygon.wkt(\"POLYGON ((1 1, 2 2, 3 3, 1 3, 1 1))\");\n        Assert.assertFalse(polygon.selfIntersects());\n        Assert.assertEquals(0, polygon.selfIntersections().size());\n    }\n\n    @Test\n    public void testSelfIntersectsTouchFirst()\n    {\n        final PolyLine polyLine = PolyLine.wkt(\"LINESTRING(2 1, 3 2, 3 1, 1 1)\");\n        Assert.assertTrue(polyLine.selfIntersects());\n        final Set<Location> intersections = polyLine.selfIntersections();\n        Assert.assertEquals(1, intersections.size());\n        Assert.assertEquals(Location.forString(\"1,2\"), intersections.toArray()[0]);\n    }\n\n    @Test\n    public void testSelfIntersectsTouchLast()\n    {\n        final PolyLine polyLine = PolyLine.wkt(\"LINESTRING(1 1, 3 1, 3 2, 2 1)\");\n        Assert.assertTrue(polyLine.selfIntersects());\n        final Set<Location> intersections = polyLine.selfIntersections();\n        Assert.assertEquals(1, intersections.size());\n        Assert.assertEquals(Location.forString(\"1,2\"), intersections.toArray()[0]);\n    }\n\n    @Test\n    public void testSelfIntersectsTouchMiddle()\n    {\n        final PolyLine polyLine = PolyLine.wkt(\"LINESTRING(1 1, 3 1, 3 2, 2 1, 1 2)\");\n        Assert.assertTrue(polyLine.selfIntersects());\n        final Set<Location> intersections = polyLine.selfIntersections();\n        Assert.assertEquals(1, intersections.size());\n        Assert.assertEquals(Location.forString(\"1,2\"), intersections.toArray()[0]);\n    }\n\n    @Test\n    public void testToString()\n    {\n        final PolyLine singleLocationPolyLine = new PolyLine(Location.CROSSING_85_280);\n        final PolyLine multipleLocationPolyLine = new PolyLine(Location.CROSSING_85_280,\n                Location.TEST_1);\n\n        Assert.assertEquals(\"POINT (-122.05576 37.332439)\", singleLocationPolyLine.toString());\n        Assert.assertEquals(\"LINESTRING (-122.05576 37.332439, -122.009566 37.33531)\",\n                multipleLocationPolyLine.toString());\n    }\n\n    @Test\n    public void testTruncatingFromBothSides()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1,\n                Location.TEST_2);\n        final Iterable<Location> truncated = line.truncate(1, 1);\n        Assert.assertTrue(new PolyLine(truncated).equals(new PolyLine(Location.TEST_1)));\n    }\n\n    @Test\n    public void testTruncatingFromEnd()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final Iterable<Location> truncated = line.truncate(0, 1);\n        Assert.assertTrue(new PolyLine(truncated).equals(new PolyLine(Location.CROSSING_85_280)));\n    }\n\n    @Test\n    public void testTruncatingFromStart()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final Iterable<Location> truncated = line.truncate(1, 0);\n        Assert.assertTrue(new PolyLine(truncated).equals(new PolyLine(Location.TEST_1)));\n    }\n\n    @Test\n    public void testTruncatingWithEndIndexEqualToPolyLineSize()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final Iterable<Location> truncated = line.truncate(0, 2);\n        Assert.assertTrue(\"Make sure truncated is empty\", Iterables.isEmpty(truncated));\n    }\n\n    @Test\n    public void testTruncatingWithInvalidIndex()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final Iterable<Location> truncated = line.truncate(-1, 1);\n        Assert.assertTrue(\"Make sure truncated is empty\", Iterables.isEmpty(truncated));\n    }\n\n    @Test\n    public void testTruncatingWithStartAndEndCombinedEqualToPolyLineSize()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final Iterable<Location> truncated = line.truncate(1, 1);\n        Assert.assertTrue(\"Make sure truncated is empty\", Iterables.isEmpty(truncated));\n    }\n\n    @Test\n    public void testTruncatingWithStartIndexEqualToPolyLineSize()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final Iterable<Location> truncated = line.truncate(2, 0);\n        Assert.assertTrue(\"Make sure truncated is empty\", Iterables.isEmpty(truncated));\n    }\n\n    @Test\n    public void testTruncatingWithZeroIndices()\n    {\n        final PolyLine line = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final Iterable<Location> truncated = line.truncate(0, 0);\n        Assert.assertTrue(new PolyLine(truncated).equals(line));\n    }\n\n    @Test\n    public void testWithoutDuplicateConsecutiveShapePoints()\n    {\n        final PolyLine polyLine1 = PolyLine.wkt(\n                \"LINESTRING (-75.616326 40.194564, -75.616330 40.194570, -75.616330 40.194570, -75.616340 40.194580)\");\n        final PolyLine polyLine2 = PolyLine.wkt(\n                \"LINESTRING (-75.616326 40.194564, -75.616330 40.194570, -75.616340 40.194580)\");\n        final PolyLine polyLine3 = PolyLine.wkt(\n                \"LINESTRING (-75.616326 40.194564, -75.616330 40.194570, -75.616340 40.194580, -75.616330 40.194570)\");\n        final PolyLine polyLine4 = PolyLine.wkt(\n                \"LINESTRING (-75.616330 40.194570, -75.616330 40.194570, -75.616330 40.194570, -75.616340 40.194580, -75.616340 40.194590)\");\n        final PolyLine polyLine5 = PolyLine.wkt(\n                \"LINESTRING (-75.616330 40.194570, -75.616340 40.194580, -75.616340 40.194590)\");\n        Assert.assertEquals(polyLine2.toWkt(),\n                polyLine1.withoutDuplicateConsecutiveShapePoints().toWkt());\n        Assert.assertEquals(polyLine3.toWkt(),\n                polyLine3.withoutDuplicateConsecutiveShapePoints().toWkt());\n        Assert.assertEquals(polyLine5.toWkt(),\n                polyLine4.withoutDuplicateConsecutiveShapePoints().toWkt());\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/PolygonCoveringPolygonTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * Tests overlap/containment/intersection functionality between multiple {@link Polygon}s.\n *\n * @author mgostintsev\n */\npublic class PolygonCoveringPolygonTest\n{\n    @Rule\n    public final PolygonCoveringPolygonTestRule rule = new PolygonCoveringPolygonTestRule();\n\n    @Test\n    public void testPolygonFullyInsidePolygon()\n    {\n        final Atlas atlas = this.rule.getPolygonWithinPolygonAtlas();\n        verifyOverlapExists(atlas);\n        verifyFullContainment(atlas);\n\n        // This may seen un-intuitive, but because the underlying polylines never intersect, this\n        // will not have an intersection, even though one Polygon fully encloses the other.\n        verifyNoIntersection(atlas);\n    }\n\n    @Test\n    public void testTwoIdenticalPolygonsStackedOnEachOther()\n    {\n        final Atlas atlas = this.rule.getPolygonsStackedOnEachOtherAtlas();\n        verifyOverlapExists(atlas);\n        verifyFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testTwoPolygonsFarApart()\n    {\n        final Atlas atlas = this.rule.getNonOverlappingNonTouchingPolygonsAtlas();\n        verifyNoOverlap(atlas);\n        verifyNoFullContainment(atlas);\n        verifyNoIntersection(atlas);\n    }\n\n    @Test\n    public void testTwoPolygonsOverlappingAtCorner()\n    {\n        final Atlas atlas = this.rule.getPolygonsOverlappingAtCornerAtlas();\n        verifyOverlapExists(atlas);\n        verifyNoFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testTwoPolygonsOverlappingAtSide()\n    {\n        final Atlas atlas = this.rule.getPolygonsOverlappingAtSideAtlas();\n        verifyOverlapExists(atlas);\n        verifyNoFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testTwoPolygonsSharingSide()\n    {\n        final Atlas atlas = this.rule.getPolygonsSharingSideAtlas();\n        verifyOverlapExists(atlas);\n        verifyNoFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    @Test\n    public void testTwoPolygonsTouchingAtVertex()\n    {\n        final Atlas atlas = this.rule.getPolygonsTouchingAtVertexAtlas();\n        verifyOverlapExists(atlas);\n        verifyNoFullContainment(atlas);\n        verifyIntersection(atlas);\n    }\n\n    private void verifyFullContainment(final Atlas atlas)\n    {\n        Assert.assertTrue(\n                atlas.area(1).asPolygon().fullyGeometricallyEncloses(atlas.area(2).asPolygon()));\n    }\n\n    private void verifyIntersection(final Atlas atlas)\n    {\n        Assert.assertTrue(atlas.area(1).asPolygon().intersects(atlas.area(2).asPolygon()));\n    }\n\n    private void verifyNoFullContainment(final Atlas atlas)\n    {\n        Assert.assertFalse(\n                atlas.area(1).asPolygon().fullyGeometricallyEncloses(atlas.area(2).asPolygon()));\n    }\n\n    private void verifyNoIntersection(final Atlas atlas)\n    {\n        Assert.assertFalse(atlas.area(1).asPolygon().intersects(atlas.area(2).asPolygon()));\n    }\n\n    private void verifyNoOverlap(final Atlas atlas)\n    {\n        Assert.assertFalse(atlas.area(1).asPolygon().overlaps(atlas.area(2).asPolygon()));\n    }\n\n    private void verifyOverlapExists(final Atlas atlas)\n    {\n        Assert.assertTrue(atlas.area(1).asPolygon().overlaps(atlas.area(2).asPolygon()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/PolygonCoveringPolygonTestRule.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * {@link PolygonCoveringPolygonTest} data\n *\n * @author mgostintsev\n */\npublic class PolygonCoveringPolygonTestRule extends CoreTestRule\n{\n    // Rectangle #1\n    private static final String OUTER_TOP_LEFT = \"39.9978684, 116.2741473\";\n    private static final String OUTER_TOP_RIGHT = \"39.9979242, 116.2747418\";\n    private static final String OUTER_BOTTOM_RIGHT = \"39.9975751, 116.2748088\";\n    private static final String OUTER_BOTTOM_LEFT = \"39.9975152, 116.2741904\";\n\n    // Rectangle #2, fully contained within Rectangle #1\n    private static final String INNER_2_TOP_LEFT = \"39.9977887, 116.2743726\";\n    private static final String INNER_2_TOP_RIGHT = \"39.9977948, 116.2744396\";\n    private static final String INNER_2_BOTTOM_RIGHT = \"39.9977096, 116.2744528\";\n    private static final String INNER_2_BOTTOM_LEFT = \"39.9977035, 116.2743859\";\n\n    // Rectangle #3, fully contained within Rectangle #1, to the right of Rectangle #2\n    private static final String INNER_3_TOP_LEFT = \"39.9977983, 116.274481\";\n    private static final String INNER_3_TOP_RIGHT = \"39.9978044, 116.2745479\";\n    private static final String INNER_3_BOTTOM_RIGHT = \"39.9977192, 116.2745612\";\n    private static final String INNER_3_BOTTOM_LEFT = \"39.9977131, 116.2744943\";\n\n    // Rectangle #4, independent of others\n    private static final String TOP_LEFT = \"39.9978998, 116.2744895\";\n    private static final String TOP_RIGHT = \"39.9979242, 116.2747418\";\n    private static final String BOTTOM_RIGHT = \"39.9978469, 116.2747545\";\n    private static final String BOTTOM_LEFT = \"39.9978225, 116.2745022\";\n\n    // Rectangle #5, covers a corner of #4\n    private static final String CORNER_INTERSECT_TOP_LEFT = \"39.9978381, 116.2744597\";\n    private static final String CORNER_INTERSECT_TOP_RIGHT = \"39.9978487, 116.2745362\";\n    private static final String CORNER_INTERSECT_BOTTOM_RIGHT = \"39.9977605, 116.2745571\";\n    private static final String CORNER_INTERSECT_BOTTOM_LEFT = \"39.9977499, 116.2744806\";\n\n    // Rectangle #6, covers a side of #4\n    private static final String SIDE_INTERSECT_TOP_LEFT = \"39.9978772, 116.274453\";\n    private static final String SIDE_INTERSECT_TOP_RIGHT = \"39.9978857, 116.2745267\";\n    private static final String SIDE_INTERSECT_BOTTOM_RIGHT = \"39.9978464, 116.2745345\";\n    private static final String SIDE_INTERSECT_BOTTOM_LEFT = \"39.9978379, 116.2744607\";\n\n    // Rectangle #7, touches a corner of #4\n    private static final String TOUCHES_CORNER_TOP_LEFT = \"39.9978142, 116.2744215\";\n    private static final String TOUCHES_CORNER_TOP_RIGHT = \"39.9978256, 116.2745047\";\n    private static final String TOUCHES_CORNER_BOTTOM_RIGHT = \"39.9977801, 116.2745154\";\n    private static final String TOUCHES_CORNER_BOTTOM_LEFT = \"39.9977686, 116.2744321\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = INNER_2_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = INNER_2_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = INNER_2_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = INNER_2_BOTTOM_LEFT))\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = INNER_2_BOTTOM_LEFT),\n                            @Loc(value = INNER_2_TOP_LEFT), @Loc(value = INNER_2_TOP_RIGHT),\n                            @Loc(value = INNER_2_BOTTOM_RIGHT) }, tags = { \"building=yes\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = INNER_2_BOTTOM_LEFT),\n                            @Loc(value = INNER_2_TOP_LEFT), @Loc(value = INNER_2_TOP_RIGHT),\n                            @Loc(value = INNER_2_BOTTOM_RIGHT) }, tags = { \"building=yes\" }) })\n    private Atlas polygonsStackedOnEachOther;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = INNER_2_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = INNER_2_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = INNER_2_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = INNER_2_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = INNER_3_TOP_LEFT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = INNER_3_TOP_RIGHT)),\n                    @Node(id = \"7\", coordinates = @Loc(value = INNER_3_BOTTOM_RIGHT)),\n                    @Node(id = \"8\", coordinates = @Loc(value = INNER_3_BOTTOM_LEFT)),\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = INNER_2_TOP_LEFT),\n                            @Loc(value = INNER_2_TOP_RIGHT), @Loc(value = INNER_2_BOTTOM_RIGHT),\n                            @Loc(value = INNER_2_BOTTOM_LEFT) }, tags = { \"building=yes\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = INNER_3_TOP_LEFT),\n                            @Loc(value = INNER_3_TOP_RIGHT), @Loc(value = INNER_3_BOTTOM_RIGHT),\n                            @Loc(value = INNER_3_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas nonOverlappingNonTouchingPolygons;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = INNER_2_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = INNER_2_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = INNER_2_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = INNER_2_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = OUTER_TOP_LEFT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = OUTER_TOP_RIGHT)),\n                    @Node(id = \"7\", coordinates = @Loc(value = OUTER_BOTTOM_RIGHT)),\n                    @Node(id = \"8\", coordinates = @Loc(value = OUTER_BOTTOM_LEFT)),\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = OUTER_TOP_LEFT),\n                            @Loc(value = OUTER_TOP_RIGHT), @Loc(value = OUTER_BOTTOM_RIGHT),\n                            @Loc(value = OUTER_BOTTOM_LEFT) }, tags = { \"building=yes\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = INNER_2_TOP_LEFT),\n                            @Loc(value = INNER_2_TOP_RIGHT), @Loc(value = INNER_2_BOTTOM_RIGHT),\n                            @Loc(value = INNER_2_BOTTOM_LEFT) }, tags = { \"building=yes\" }) })\n    private Atlas polygonWithinPolygon;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = INNER_2_TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = INNER_2_TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = INNER_2_BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = INNER_2_BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = INNER_3_TOP_RIGHT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = INNER_3_BOTTOM_RIGHT))\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = INNER_2_TOP_LEFT),\n                            @Loc(value = INNER_2_TOP_RIGHT), @Loc(value = INNER_2_BOTTOM_RIGHT),\n                            @Loc(value = INNER_2_BOTTOM_LEFT) }, tags = { \"building=yes\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = INNER_2_TOP_RIGHT),\n                            @Loc(value = INNER_3_TOP_RIGHT), @Loc(value = INNER_3_BOTTOM_RIGHT),\n                            @Loc(value = INNER_2_BOTTOM_RIGHT) }, tags = { \"building=yes\" }) })\n    private Atlas polygonsSharingSide;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = TOUCHES_CORNER_TOP_LEFT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = TOUCHES_CORNER_TOP_RIGHT)),\n                    @Node(id = \"7\", coordinates = @Loc(value = TOUCHES_CORNER_BOTTOM_RIGHT)),\n                    @Node(id = \"8\", coordinates = @Loc(value = TOUCHES_CORNER_BOTTOM_LEFT)),\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = TOP_LEFT), @Loc(value = TOP_RIGHT),\n                            @Loc(value = BOTTOM_RIGHT),\n                            @Loc(value = BOTTOM_LEFT) }, tags = { \"building=yes\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = TOUCHES_CORNER_TOP_LEFT),\n                            @Loc(value = TOUCHES_CORNER_TOP_RIGHT),\n                            @Loc(value = TOUCHES_CORNER_BOTTOM_RIGHT),\n                            @Loc(value = TOUCHES_CORNER_BOTTOM_LEFT) }, tags = {\n                                    \"building=yes\" }) })\n    private Atlas polygonsTouchingAtVertex;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = CORNER_INTERSECT_TOP_LEFT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = CORNER_INTERSECT_TOP_RIGHT)),\n                    @Node(id = \"7\", coordinates = @Loc(value = CORNER_INTERSECT_BOTTOM_RIGHT)),\n                    @Node(id = \"8\", coordinates = @Loc(value = CORNER_INTERSECT_BOTTOM_LEFT)),\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = TOP_LEFT), @Loc(value = TOP_RIGHT),\n                            @Loc(value = BOTTOM_RIGHT),\n                            @Loc(value = BOTTOM_LEFT) }, tags = { \"building=yes\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = CORNER_INTERSECT_TOP_LEFT),\n                            @Loc(value = CORNER_INTERSECT_TOP_RIGHT),\n                            @Loc(value = CORNER_INTERSECT_BOTTOM_RIGHT),\n                            @Loc(value = CORNER_INTERSECT_BOTTOM_LEFT) }, tags = {\n                                    \"building=yes\" }) })\n    private Atlas polygonsOverlappingAtCorner;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = TOP_LEFT)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TOP_RIGHT)),\n                    @Node(id = \"3\", coordinates = @Loc(value = BOTTOM_RIGHT)),\n                    @Node(id = \"4\", coordinates = @Loc(value = BOTTOM_LEFT)),\n                    @Node(id = \"5\", coordinates = @Loc(value = SIDE_INTERSECT_TOP_LEFT)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIDE_INTERSECT_TOP_RIGHT)),\n                    @Node(id = \"7\", coordinates = @Loc(value = SIDE_INTERSECT_BOTTOM_RIGHT)),\n                    @Node(id = \"8\", coordinates = @Loc(value = SIDE_INTERSECT_BOTTOM_LEFT)),\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = TOP_LEFT), @Loc(value = TOP_RIGHT),\n                            @Loc(value = BOTTOM_RIGHT),\n                            @Loc(value = BOTTOM_LEFT) }, tags = { \"building=yes\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = SIDE_INTERSECT_TOP_LEFT),\n                            @Loc(value = SIDE_INTERSECT_TOP_RIGHT),\n                            @Loc(value = SIDE_INTERSECT_BOTTOM_RIGHT),\n                            @Loc(value = SIDE_INTERSECT_BOTTOM_LEFT) }, tags = {\n                                    \"building=yes\" }) })\n    private Atlas polygonsOverlappingAtSide;\n\n    public Atlas getNonOverlappingNonTouchingPolygonsAtlas()\n    {\n        return this.nonOverlappingNonTouchingPolygons;\n    }\n\n    public Atlas getPolygonWithinPolygonAtlas()\n    {\n        return this.polygonWithinPolygon;\n    }\n\n    public Atlas getPolygonsOverlappingAtCornerAtlas()\n    {\n        return this.polygonsOverlappingAtCorner;\n    }\n\n    public Atlas getPolygonsOverlappingAtSideAtlas()\n    {\n        return this.polygonsOverlappingAtSide;\n    }\n\n    public Atlas getPolygonsSharingSideAtlas()\n    {\n        return this.polygonsSharingSide;\n    }\n\n    public Atlas getPolygonsStackedOnEachOtherAtlas()\n    {\n        return this.polygonsStackedOnEachOther;\n    }\n\n    public Atlas getPolygonsTouchingAtVertexAtlas()\n    {\n        return this.polygonsTouchingAtVertex;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/PolygonTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.locationtech.jts.io.WKBWriter;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.JsonObject;\n\n/**\n * @author matthieun\n * @author mgostintsev\n * @author james-gage\n * @author hallahan\n */\npublic class PolygonTest\n{\n    /*\n     * The .osm files associated with some of these tests describe some of the shapes used in tests\n     * here, if the wkt's are updated in these tests the matching .osm files should also be updated.\n     */\n\n    private static final Logger logger = LoggerFactory.getLogger(PolygonTest.class);\n\n    @Rule\n    public final PolygonTestRule setup = new PolygonTestRule();\n\n    private Polygon quadrant;\n    private Rectangle rectangle;\n\n    @Before\n    public void init()\n    {\n        this.quadrant = new Polygon(Location.TEST_3, Location.TEST_4, Location.TEST_1,\n                Location.TEST_2);\n        this.rectangle = Rectangle\n                .forLocations(Iterables.iterable(Location.TEST_5, Location.TEST_3));\n    }\n\n    @Test\n    public void testAngle()\n    {\n        final PolyLine polyLine = new PolyLine(this.quadrant);\n        Assert.assertEquals(Angle.degrees(148.2126213), polyLine.maximumAngle());\n    }\n\n    @Test\n    public void testAngleLocation()\n    {\n        final PolyLine polyLine = new PolyLine(this.quadrant);\n        Assert.assertEquals(Location.forString(\"37.33531,-122.009566\"),\n                polyLine.maximumAngleLocation().get());\n    }\n\n    @Test\n    public void testAntimeridian()\n    {\n        final Polygon antimeridianWest = new Polygon(Location.forString(\"47.6778433, -180\"),\n                Location.forString(\"47.6779773, -180\"), Location.forString(\"47.6776721, -180\"),\n                Location.forString(\"47.6775366, -179.9\"), Location.forString(\"47.6776969, -179.9\"),\n                Location.forString(\"47.6778433, -179.9\"));\n\n        final Polygon antimeridianEast = new Polygon(Location.forString(\"47.6778433, 180\"),\n                Location.forString(\"47.6779773, 180\"), Location.forString(\"47.6776721, 180\"),\n                Location.forString(\"47.6775366, 179.9\"), Location.forString(\"47.6776969, 179.9\"),\n                Location.forString(\"47.6778433, 179.9\"));\n\n        Assert.assertFalse(antimeridianWest.intersects(antimeridianEast));\n\n    }\n\n    @Test\n    public void testAsGeoJsonGeometry()\n    {\n        final Polygon tennisCourt = new Polygon(Location.forString(\"47.6778433, -122.2012807\"),\n                Location.forString(\"47.6779773, -122.2008187\"),\n                Location.forString(\"47.6776721, -122.2006229\"),\n                Location.forString(\"47.6775366, -122.2010923\"),\n                Location.forString(\"47.6776969, -122.2011787\"),\n                Location.forString(\"47.6778433, -122.2012807\"));\n        final String geoJson = \"{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[-122.2012807,47.6778433],[-122.2008187,47.6779773],[-122.2006229,47.6776721],[-122.2010923,47.6775366],[-122.2011787,47.6776969],[-122.2012807,47.6778433]]]}\";\n        final JsonObject geometry = tennisCourt.asGeoJsonGeometry();\n        Assert.assertEquals(geoJson, geometry.toString());\n    }\n\n    @Test\n    public void testCenter()\n    {\n        // test for rectangle\n        final Polygon rectangle = new Polygon(Rectangle.TEST_RECTANGLE);\n        final Location center = rectangle.center();\n        Assert.assertEquals(center, Location.forString(\"37.3292805,-122.030478\"));\n        // test for triangle\n        final Polygon triangle = new Polygon(Arrays.asList(Location.forString(\"1,1\"),\n                Location.forString(\"2,2\"), Location.forString(\"1,2\")));\n        final Location triangleCenter = triangle.center();\n        Assert.assertEquals(triangleCenter, Location.forString(\"1.3333333,1.6666667\"));\n    }\n\n    @Test\n    public void testClockwise()\n    {\n        String polygonWkt = \"POLYGON ((-70.0020146 12.5265405, -70.0019978 12.5265112, -70.0019564 12.526534, -70.0019733 12.5265633, -70.0020146 12.5265405))\";\n        Polygon polygon = Polygon.wkt(polygonWkt);\n        Assert.assertFalse(polygon.isClockwise());\n        Assert.assertTrue(polygon.reversed().isClockwise());\n\n        polygonWkt = \"POLYGON((70.5233941 -49.2953329,70.5234584 -49.2955148,70.5235872 -49.2963964,70.5230722 -49.2963964,70.5229649 -49.2962145,70.5227718 -49.2961585,70.5225143 -49.2960186,70.5224285 -49.2958507,70.5226216 -49.2955568,70.5223426 -49.2955288,70.5225358 -49.295095,70.5233941 -49.2953329))\";\n        polygon = Polygon.wkt(polygonWkt);\n        Assert.assertTrue(polygon.isClockwise());\n        Assert.assertFalse(polygon.reversed().isClockwise());\n\n        polygonWkt = \"POLYGON((69.5530556 -49.2504684,69.5530878 -49.2504019,69.5530985 -49.2503494,69.5531736 -49.2504089,69.5533614 -49.2504054,69.553415 -49.2504439,69.553356 -49.250584,69.5531629 -49.2505455,69.5530556 -49.2504684))\";\n        polygon = Polygon.wkt(polygonWkt);\n        Assert.assertTrue(polygon.isClockwise());\n        Assert.assertFalse(polygon.reversed().isClockwise());\n\n        polygonWkt = \"POLYGON((-58.7546961 -51.5537665,-58.754588 -51.5537003,-58.754411 -51.553737,-58.7543466 -51.5538204,-58.7543573 -51.5539338,-58.7544324 -51.5539838,-58.7545995 -51.5539833,-58.754706 -51.5539371,-58.7547543 -51.5538638,-58.7547597 -51.5538004,-58.7553229 -51.5538738,-58.7546961 -51.5537665))\";\n        polygon = Polygon.wkt(polygonWkt);\n        Assert.assertTrue(polygon.isClockwise());\n        Assert.assertFalse(polygon.reversed().isClockwise());\n\n        polygonWkt = \"POLYGON((72.3694563 -7.2629912,72.3701314 -7.2625735,72.3702161 -7.2625939,72.370768 -7.2634826,72.3707654 -7.263559,72.37008 -7.2640046,72.3700107 -7.2639715,72.369428 -7.2630548,72.3694563 -7.2629912))\";\n        polygon = Polygon.wkt(polygonWkt);\n        Assert.assertTrue(polygon.isClockwise());\n        Assert.assertFalse(polygon.reversed().isClockwise());\n    }\n\n    @Test\n    public void testClosedLoop()\n    {\n        final Polygon polygon = Polygon.SILICON_VALLEY;\n        final Polygon initialClosedLoop = new Polygon(polygon.closedLoop());\n        Assert.assertNotEquals(\n                \"Last and Last - 1 locations for the polygon should not be duplicate.\",\n                initialClosedLoop.last(), initialClosedLoop.get(initialClosedLoop.size() - 2));\n        final Polygon doubleClosedLoop = new Polygon(initialClosedLoop.closedLoop());\n        Assert.assertNotEquals(\"Last and Last - 1 locations for polygon should not be duplicate.\",\n                doubleClosedLoop.last(), doubleClosedLoop.get(doubleClosedLoop.size() - 2));\n    }\n\n    @Test\n    public void testComplexPolygon()\n    {\n        final Polygon polygon = Polygon\n                .wkt(\"POLYGON ((0.0269943 0.0252426, 0.0441521 0.0453649, 0.0584354 0.0381783, \"\n                        + \"0.0706524 0.0269494, 0.0770305 0.011678, 0.0716406 -0.0064678, 0.05062 -0.007995, \"\n                        + \"0.0498115 -0.0120374, 0.0594235 -0.0167086, 0.0675982 -0.013295, 0.074066 -0.0169781, \"\n                        + \"0.0688558 -0.0273986, 0.0557404 -0.0238053, 0.0534946 -0.0191341, 0.0469369 -0.0153611,\"\n                        + \" 0.0464878 -0.0259613, 0.0557404 -0.029824, 0.0641846 -0.0317105, 0.060232 -0.0455445,\"\n                        + \" 0.0462183 -0.0389868, 0.0313062 -0.0173374, 0.0269943 0.0252426))\");\n        final Location inside1 = Location.forString(\"-0.0209583, 0.0651123\");\n        final Location onBoundary1 = Location.forString(\"0.0301426, 0.0669962\");\n        final Location onBoundary2 = Location.forString(\"-0.0170728, 0.0317908\");\n        final Location onBoundary3 = Location.forString(\"0.0269494, 0.0706524\");\n        final Location outside1 = Location.forString(\".0427412, .0722947\");\n        final Location outside2 = Location.forString(\"-0.022018, 0.0499233\");\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(inside1));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(onBoundary1));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(onBoundary2));\n        // see awt definition of contains\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(onBoundary3));\n        Assert.assertFalse(polygon.fullyGeometricallyEncloses(outside1));\n        Assert.assertFalse(polygon.fullyGeometricallyEncloses(outside2));\n    }\n\n    @Test\n    public void testConcave()\n    {\n        final Polygon concave = new Polygon(Location.forString(\"1, -1\"), Location.forString(\"1,1\"),\n                Location.forString(\"-1,1\"), Location.forString(\"0,0\"), Location.forString(\"-1,-1\"));\n        final Location outside1 = Location.forString(\"-1, 0\");\n        final Location outside2 = Location.forString(\"0, 2\");\n        final Location outside3 = Location.forString(\"-2, 0\");\n        final Location inside1 = Location.forString(\"-0.5, 0.8\");\n        final Location onBoundary1 = Location.forString(\"-0.5, 0.5\");\n        final Location onBoundary2 = Location.forString(\"0, 1\");\n\n        Assert.assertTrue(concave.fullyGeometricallyEncloses(inside1));\n        Assert.assertTrue(concave.fullyGeometricallyEncloses(onBoundary1));\n        Assert.assertTrue(concave.fullyGeometricallyEncloses(onBoundary2));\n        Assert.assertFalse(concave.fullyGeometricallyEncloses(outside1));\n        Assert.assertFalse(concave.fullyGeometricallyEncloses(outside2));\n        Assert.assertTrue(concave.intersects(new Segment(outside1, inside1)));\n        Assert.assertTrue(concave.intersects(new Segment(outside1, outside2)));\n        Assert.assertFalse(concave.intersects(new Segment(outside1, outside3)));\n    }\n\n    @Test\n    public void testContains()\n    {\n        final Rectangle rectangle = Rectangle.TEST_RECTANGLE;\n        final Polygon polygon = new Polygon(rectangle);\n        Assert.assertTrue(\"Checking that the Location exists in the Polygon\",\n                polygon.contains(Rectangle.TEST_RECTANGLE.first()));\n        Assert.assertTrue(\"Checking that the Segent that closes the Polygon exists\",\n                polygon.contains(new Segment(Rectangle.TEST_RECTANGLE.last(),\n                        Rectangle.TEST_RECTANGLE.first())));\n    }\n\n    @Test\n    public void testCovers()\n    {\n        final Rectangle rwac = Rectangle\n                .forLocations(Iterables.iterable(Location.TEST_3, Location.TEST_1));\n\n        logger.info(\"Polygon: \" + this.quadrant);\n\n        final boolean containsTest6 = this.quadrant.fullyGeometricallyEncloses(Location.TEST_6);\n        final boolean containsTest5 = this.quadrant.fullyGeometricallyEncloses(Location.TEST_5);\n        final boolean containsTestRectangle = this.quadrant\n                .fullyGeometricallyEncloses(Rectangle.TEST_RECTANGLE_2);\n        final boolean containsRWAC = this.quadrant.fullyGeometricallyEncloses(rwac);\n\n        logger.info(\"Test 6: {} -> {}\", Location.TEST_6, containsTest6);\n        logger.info(\"Test 5: {} -> {}\", Location.TEST_5, containsTest5);\n        logger.info(\"Test Rectangle: {} -> {}\", Rectangle.TEST_RECTANGLE_2, containsTestRectangle);\n        logger.info(\"RWAC 2: {} -> {}\", rwac, containsRWAC);\n\n        Assert.assertTrue(!containsTest5);\n        Assert.assertTrue(containsTest6);\n        Assert.assertTrue(containsTestRectangle);\n        Assert.assertTrue(!containsRWAC);\n    }\n\n    @Test\n    public void testCoversMultiPolygon()\n    {\n        final MultiPolygon multiPolygon = MultiPolygon\n                .wkt(\"MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),\"\n                        + \"((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),\"\n                        + \"(30 20, 20 15, 20 25, 30 20)))\");\n        logger.info(\"multiPolygon: {}\", multiPolygon.toWkt());\n        final Polygon coveringPolygon = new Polygon(Location.forString(\"48.861903, 2.344141\"),\n                Location.forString(\"6.215559, 1.431353\"),\n                Location.forString(\"-1.302400, 36.818213\"),\n                Location.forString(\"22.648164, 50.364465\"));\n        logger.info(\"coveringPolygon: {}\", coveringPolygon.toWkt());\n        Assert.assertTrue(coveringPolygon.overlaps(multiPolygon));\n\n        final Polygon insideInnerPolygon = new Polygon(Location.forString(\"20.146558, 23.310950\"),\n                Location.forString(\"19.623812, 24.507328\"),\n                Location.forString(\"19.247746, 23.339148\"));\n        logger.info(\"insideInnerPolygon: {}\", insideInnerPolygon.toWkt());\n        Assert.assertFalse(insideInnerPolygon.overlaps(multiPolygon));\n\n        final Polygon intersectingInnerPolygon = new Polygon(\n                Location.forString(\"20.146558, 23.310950\"),\n                Location.forString(\"19.623812, 24.507328\"),\n                Location.forString(\"27.156014, 30.298381\"));\n        logger.info(\"intersectingInnerPolygon: {}\", intersectingInnerPolygon.toWkt());\n        Assert.assertTrue(intersectingInnerPolygon.overlaps(multiPolygon));\n\n        final Polygon intersectingOuterPolygon = new Polygon(\n                Location.forString(\"48.861903, 2.344141\"),\n                Location.forString(\"22.648164, 50.364465\"),\n                Location.forString(\"27.156014, 30.298381\"));\n        logger.info(\"intersectingOuterPolygon: {}\", intersectingOuterPolygon.toWkt());\n        Assert.assertTrue(intersectingOuterPolygon.overlaps(multiPolygon));\n    }\n\n    @Test\n    public void testFindingAnglesGreaterThanTarget()\n    {\n        final PolyLine polyLine = new PolyLine(this.quadrant);\n\n        // Find all angles greater than or equal to 140 degrees.\n        final List<Tuple<Angle, Location>> result = polyLine\n                .anglesGreaterThanOrEqualTo(Angle.degrees(140));\n\n        Assert.assertEquals(1, result.size());\n        Assert.assertEquals(Tuple.createTuple(Angle.degrees(148.2126213),\n                Location.forString(\"37.33531,-122.009566\")), result.get(0));\n    }\n\n    @Test\n    public void testFindingAnglesLessThanTarget()\n    {\n        final PolyLine polyLine = new PolyLine(this.quadrant);\n\n        // Find all angles less than or equal to 30 degrees.\n        final List<Tuple<Angle, Location>> result = polyLine\n                .anglesLessThanOrEqualTo(Angle.degrees(30));\n\n        Assert.assertEquals(1, result.size());\n        Assert.assertEquals(Tuple.createTuple(Angle.degrees(28.3372516),\n                Location.forString(\"37.332451,-122.028932\")), result.get(0));\n    }\n\n    @Test\n    public void testFourSelfIntersectingPolygonContains()\n    {\n        // Shape represents a square with an additional smaller square on each corner, formed by 4\n        // self intersections\n        final Polygon polygon = Polygon.wkt(\n                \"POLYGON ((-0.0380885 0.0238053, -0.0381783 0.0334173, -0.0255121 0.0337766, -0.0247036 -0.0263206, \"\n                        + \"-0.0368309 -0.0268596, -0.03728 -0.0141933, 0.0270392 -0.0132052, 0.0273986 -0.0246138,\"\n                        + \" 0.0171578 -0.0248833, 0.0153611 0.0342258, 0.0253324 0.0344953, 0.0257816 0.0256918, \"\n                        + \"-0.0380885 0.0238053))\");\n        final Location upperLeft = Location.forString(\"0.0279376, -0.0300936\");\n        final Location upperRight = Location.forString(\"0.0297342, 0.0212002\");\n        final Location lowerLeft = Location.forString(\"-0.0203918, -0.0302732\");\n        final Location lowerRight = Location.forString(\"-0.0196731, 0.022782\");\n        final Location center = Location.forString(\"0.06378, -0.0053899\");\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(upperLeft));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(upperRight));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(lowerLeft));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(lowerRight));\n        // Interior square is considered exterior to the polygon\n        Assert.assertFalse(polygon.fullyGeometricallyEncloses(center));\n    }\n\n    @Test\n    public void testFullyGeometricallyEnclosesRectangleAndPolyLine()\n    {\n        final Polygon polygon3 = new Polygon(Location.forString(\"18.2226, -63.0508\"),\n                Location.forString(\"18.2209, -63.0437\"), Location.forString(\"18.2117, -63.0484\"),\n                Location.forString(\"18.2139, -63.0563\"), Location.forString(\"18.2226, -63.0508\"));\n        final Polygon polygon4 = Rectangle.forCorners(Location.forString(\"18.2112, -63.0568\"),\n                Location.forString(\"18.2232, -63.0432\"));\n\n        Assert.assertTrue(polygon4.fullyGeometricallyEncloses(polygon3.bounds()));\n        Assert.assertTrue(polygon4.fullyGeometricallyEncloses(new Polygon(polygon3.closedLoop())));\n        Assert.assertFalse(polygon3.fullyGeometricallyEncloses(polygon4.bounds()));\n        Assert.assertFalse(polygon3.fullyGeometricallyEncloses(new Polygon(polygon4.closedLoop())));\n    }\n\n    @Test\n    public void testFullyGeometricallyEnclosingMultiPolygon()\n    {\n        final MultiPolygon multiPolygon1 = MultiPolygon\n                .wkt(\"MULTIPOLYGON (\" + \"((-10 10, 10 10, 10 -10, -10 -10, -10 10),\"\n                        + \"(-6 6, 6 6, 6 -6, -6 -6, -6 6)))\");\n        final MultiPolygon multiPolygon2 = MultiPolygon.wkt(\"MULTIPOLYGON (\"\n                + \"((-4 4, 4 4, 4 -4, -4 -4, -4 4),\" + \"(-3 3, 3 3, 3 -3, -3 -3, -3 3)))\");\n        final Polygon polygon1 = Polygon.wkt(\"POLYGON \" + \"((-8 8, 8 8, 8 -8, -8 -8, -8 8))\");\n        final Polygon polygon2 = Polygon.wkt(\"POLYGON \" + \"((-5 5, 5 5, 5 -5, -5 -5, -5 5))\");\n\n        Assert.assertFalse(polygon2.fullyGeometricallyEncloses(multiPolygon1));\n        Assert.assertTrue(polygon1.fullyGeometricallyEncloses(multiPolygon2));\n        Assert.assertFalse(polygon1.fullyGeometricallyEncloses(multiPolygon1));\n        Assert.assertTrue(polygon2.fullyGeometricallyEncloses(multiPolygon2));\n    }\n\n    @Test\n    public void testIntersects()\n    {\n        final Set<Location> intersection = this.quadrant.intersections(new Segment(Location.TEST_5,\n                new Location(Location.TEST_3.getLatitude(), Location.TEST_5.getLongitude())));\n        Assert.assertEquals(2, intersection.size());\n\n        final Set<Location> intersections = this.quadrant.intersections(this.rectangle);\n        Assert.assertEquals(3, intersections.size());\n\n        Assert.assertTrue(this.quadrant.intersects(this.rectangle));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedHalfCircle()\n    {\n        // Test half-circle like polygon (has 10 sides)\n        // POLYGON ((-122.003467 37.324233, -122.003491 37.3242521, -122.003519 37.3242677,\n        // -122.0035504 37.3242794, -122.0035845 37.324287, -122.0036207 37.32429, -122.0036583\n        // 37.3242884, -122.0036964 37.3242819, -122.0037343 37.3242705, -122.0037712 37.3242542,\n        // -122.0038063 37.324233, -122.003467 37.324233))\n        final Polygon halfCircleLike = new Polygon(Location.STEVENS_CREEK,\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(315),\n                        Distance.meters(3)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(310),\n                        Distance.meters(6)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(305),\n                        Distance.meters(9)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(300),\n                        Distance.meters(12)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(295),\n                        Distance.meters(15)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(290),\n                        Distance.meters(18)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(285),\n                        Distance.meters(21)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(280),\n                        Distance.meters(24)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(275),\n                        Distance.meters(27)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(270),\n                        Distance.meters(30)));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(3, Angle.degrees(5)));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(3, Angle.degrees(10)));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(3, Angle.degrees(30)));\n        Assert.assertTrue(halfCircleLike.isApproximatelyNSided(3, Angle.degrees(45)));\n        Assert.assertFalse(halfCircleLike.isApproximatelyNSided(10, Angle.MINIMUM));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedLine()\n    {\n        // Test a line (has no sides)\n        // POLYGON ((-122.05576 37.332439, -121.955918 37.255731, -122.05576 37.332439, -122.05576\n        // 37.332439))\n        final Polygon polygon = new Polygon(Location.CROSSING_85_280, Location.CROSSING_85_17);\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(90)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MAXIMUM));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedLineWithDuplicates()\n    {\n        // Test a line with duplicates (has no sides)\n        // POLYGON ((-122.05576 37.332439, -121.955918 37.255731, -121.955918 37.255731, -122.05576\n        // 37.332439))\n        final Polygon polygon = new Polygon(Location.CROSSING_85_280, Location.CROSSING_85_17,\n                Location.CROSSING_85_17);\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(90)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MAXIMUM));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedPoint()\n    {\n        // Test point (has no sides)\n        // POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234\n        // 41.890224))\n        final Polygon polygon = new Polygon(Location.COLOSSEUM);\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(90)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MAXIMUM));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedSelfIntersectingPolygon()\n    {\n        // Test a self-intersecting polygon\n        // POLYGON ((-122.003467 37.324233, -122.05576 37.332439, -121.955918 37.255731, -122.003467\n        // 37.324233, -122.0023361 37.324233, -122.003467 37.3251323, -122.003467 37.324233))\n        final Polygon polygon = new Polygon(Location.STEVENS_CREEK, Location.CROSSING_85_280,\n                Location.CROSSING_85_17, Location.STEVENS_CREEK,\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.EAST, Distance.meters(100)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.NORTH, Distance.meters(100)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(90)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(4, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(5, Angle.MINIMUM));\n        Assert.assertTrue(polygon.isApproximatelyNSided(6, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(6, Angle.MAXIMUM));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedSquare()\n    {\n        // Test a square with ~90 degree heading changes\n        // POLYGON ((-122.003467 37.324233, -122.0033539 37.324233, -122.0033539 37.3241431,\n        // -122.003467 37.3241431, -122.003467 37.324233))\n        final Polygon polygon = new Polygon(Location.STEVENS_CREEK,\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.EAST, Distance.meters(10)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.EAST, Distance.meters(10))\n                        .shiftAlongGreatCircle(Heading.SOUTH, Distance.meters(10)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.SOUTH, Distance.meters(10)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(30)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(60)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(89)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(89.99)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(90)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(4, Angle.MINIMUM));\n        Assert.assertTrue(polygon.isApproximatelyNSided(4, Angle.degrees(1)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(4, Angle.degrees(89)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(4, Angle.degrees(89.99)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(4, Angle.degrees(90)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(5, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(5, Angle.MAXIMUM));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedTriangle()\n    {\n        // Test a perfect triangle\n        // POLYGON ((-122.05576 37.332439, -121.955918 37.255731, -122.003467 37.324233, -122.05576\n        // 37.332439))\n        final Polygon polygon = new Polygon(Location.CROSSING_85_280, Location.CROSSING_85_17,\n                Location.STEVENS_CREEK);\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(30)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(45)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(60)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(90)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(4, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(4, Angle.MAXIMUM));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedTriangleLike1()\n    {\n        // Test triangular like shape with one additional inner point\n        // POLYGON ((-122.003467 37.324233, -122.003418 37.3242555, -122.0033691 37.324278,\n        // -122.0035536 37.3242908, -122.003467 37.324233))\n        final Polygon polygon = new Polygon(Location.STEVENS_CREEK,\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(60),\n                        Distance.meters(5)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(60),\n                        Distance.meters(10)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(310),\n                        Distance.meters(10)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(0.1)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(30)));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedTriangleLike2()\n    {\n        // Test another triangular like shape with one additional inner point\n        // POLYGON ((-122.05576 37.332439, -122.055711 37.3324615, -122.055662 37.332484,\n        // -122.0558466 37.3324968, -122.05576 37.332439))\n        final Polygon polygon = new Polygon(Location.STEVENS_CREEK,\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(59),\n                        Distance.meters(5)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(60),\n                        Distance.meters(10)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(310),\n                        Distance.meters(10)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(0.1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(0.5)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(2)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(3)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(30)));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedTriangleLike3()\n    {\n        // Test another triangular like shape with several additional inner points\n        // POLYGON ((-122.003467 37.324233, -122.0034571 37.3242374, -122.0033991 37.3242654,\n        // -122.0033691 37.324278, -122.0035536 37.3242908, -122.0034922 37.3242511, -122.003467\n        // 37.324233))\n        final Polygon polygon = new Polygon(Location.STEVENS_CREEK,\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(61),\n                        Distance.meters(1)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(59),\n                        Distance.meters(7)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(60),\n                        Distance.meters(10)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(310),\n                        Distance.meters(10)),\n                Location.STEVENS_CREEK.shiftAlongGreatCircle(Heading.degrees(312),\n                        Distance.meters(3)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(0.1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(0.5)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(2)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(3)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(30)));\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedTriangles()\n    {\n        // Test triangular generated by triangles method\n        Polygon.SILICON_VALLEY_2.triangles().forEach(aTriangle ->\n        {\n            Assert.assertTrue(aTriangle.isApproximatelyNSided(3, Angle.MINIMUM));\n            Assert.assertTrue(aTriangle.isApproximatelyNSided(3, Angle.degrees(1)));\n            Assert.assertTrue(aTriangle.isApproximatelyNSided(3, Angle.degrees(30)));\n        });\n    }\n\n    @Test\n    public void testIsApproximatelyNSidedTrianglesWithDuplicates()\n    {\n        // Test a perfect triangle with duplicate inner points\n        // POLYGON ((-122.05576 37.332439, -122.05576 37.332439, -121.955918 37.255731, -121.955918\n        // 37.255731, -121.955918 37.255731, -122.003467 37.324233, -122.003467 37.324233,\n        // -122.05576 37.332439))\n        final Polygon polygon = new Polygon(Location.CROSSING_85_280, Location.CROSSING_85_280,\n                Location.CROSSING_85_17, Location.CROSSING_85_17, Location.CROSSING_85_17,\n                Location.STEVENS_CREEK, Location.STEVENS_CREEK);\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(1, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(2, Angle.MAXIMUM));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.MINIMUM));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(1)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(30)));\n        Assert.assertTrue(polygon.isApproximatelyNSided(3, Angle.degrees(45)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(60)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.degrees(90)));\n        Assert.assertFalse(polygon.isApproximatelyNSided(3, Angle.MAXIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(4, Angle.MINIMUM));\n        Assert.assertFalse(polygon.isApproximatelyNSided(4, Angle.MAXIMUM));\n    }\n\n    @Test\n    public void testMassivePolygonWithJTS()\n    {\n        final Polygon polygon = Polygon\n                .wkt(\"POLYGON ((-160 -20, 179 -20, 179 -85, -160 -85, -160 -20))\");\n        final Location interior1 = Location.forString(\"-25,-150\");\n        final Location onBoundary1 = Location.forString(\"-20, -160\");\n        final Location onBoundary2 = Location.forString(\"-20, 179\");\n        final Location exterior1 = Location.forString(\"20, 0\");\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(interior1));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(onBoundary1));\n        // this would be excluded by awt definition of contains\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(onBoundary2));\n        Assert.assertFalse(polygon.fullyGeometricallyEncloses(exterior1));\n    }\n\n    @Test\n    public void testMaxWdithPolygonCausesAwtOverflow()\n    {\n        final Polygon maxWidthPolygon = Polygon.wkt(\n                \"POLYGON ((-180 -89.9999, -180 -62.8442818, 180 -62.8442818, 180 -89.9999, -180 -89.9999))\");\n        final Rectangle innerRectangle = Rectangle.forString(\"-80,-160:-70, 170\");\n        final Boolean encloses = maxWidthPolygon.fullyGeometricallyEncloses(innerRectangle);\n        Assert.assertEquals(true, encloses);\n    }\n\n    @Test\n    public void testNoAnglesMatchingOurCriteria()\n    {\n        final PolyLine polyLine = new PolyLine(this.quadrant);\n\n        // Find all angles greater than or equal to 140 degrees.\n        final List<Tuple<Angle, Location>> result = polyLine\n                .anglesGreaterThanOrEqualTo(Angle.degrees(179));\n\n        Assert.assertTrue(result.size() == 0);\n    }\n\n    @Test\n    public void testOffset()\n    {\n        Assert.assertEquals(Location.TEST_3, this.quadrant.offsetFromStart(Ratio.MINIMUM));\n        Assert.assertEquals(Location.TEST_3, this.quadrant.offsetFromStart(Ratio.MAXIMUM));\n        Assert.assertEquals(Location.forString(\"37.3352356,-122.0096687\"), this.quadrant.middle());\n    }\n\n    @Test\n    public void testPolygonWith45DegreeZeroAreaPartContains()\n    {\n        // Shape is a triangle, with a zero area line protruding from one of the corners on an\n        // incline\n        final Polygon polygon = Polygon.wkt(\"POLYGON ((-0.0065127 0.0214697, -0.0092975 0.0054797,\"\n                + \" -0.0233112 -0.0085339, 0.0027398 0.0175171, -0.0065127 0.0214697))\");\n        final Location middleZeroAreaPart = polygon.segmentForIndex(1).middle();\n        final Location endpointZeroAreaPart = polygon.segmentForIndex(1).end();\n        final Location middleThirdSegment = polygon.segmentForIndex(2).middle();\n        // Locations on the zero area part are still on the boundary, and therefore contained\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(middleZeroAreaPart));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(endpointZeroAreaPart));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(middleThirdSegment));\n    }\n\n    @Test\n    public void testPolygonWithFlatZeroAreaPartContains()\n    {\n        // Shape is a triangle, with a zero area line protruding from one of the corners as if\n        // extending from the base\n        final Polygon polygon = Polygon.wkt(\"POLYGON ((-0.0000449 0.0091179, -0.0063331 0.0007635, \"\n                + \"-0.0174722 0.0007635, 0.0135196 0.0007635, -0.0000449 0.0091179))\");\n        final Location onZeroAreaPart = Location.forString(\"0.0007635, 0.0132954\");\n        final Location endpoint = Location.forString(\"0.0007635,-0.0174722\");\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(onZeroAreaPart));\n        // see awt definition of contains\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(endpoint));\n    }\n\n    @Test\n    public void testRandom()\n    {\n        final Rectangle bounds = Rectangle.TEST_RECTANGLE;\n        final Polygon randomPolygon = Polygon.random(50, bounds);\n        for (final Location point : randomPolygon)\n        {\n            Assert.assertTrue(bounds.fullyGeometricallyEncloses(point));\n        }\n        final PolyLine randomPolyLine = PolyLine.random(50, bounds);\n        for (final Location point : randomPolyLine)\n        {\n            Assert.assertTrue(bounds.fullyGeometricallyEncloses(point));\n        }\n    }\n\n    @Test\n    public void testSelfIntersectingPolygon()\n    {\n        // shape is a figure 8, self intersecting in the middle\n        final Polygon polygon = Polygon.wkt(\n                \"POLYGON ((-0.0256918 0.0054797, -0.0220985 0.0120374, -0.0119475 0.0121272, -0.0054797 0.006917,\"\n                        + \" -0.007995 -0.0128459, -0.0007186 -0.0194934, 0.0103306 -0.0186849, 0.0125764 -0.0098814, \"\n                        + \"0.0026051 -0.0048509, -0.013834 -0.0050305, -0.0242545 -0.0008983, -0.0256918 0.0054797))\");\n        final Location area1 = Location.forString(\"0.0034136, -0.016529\");\n        final Location area2 = Location.forString(\"-0.0126662, 0.0026051\");\n        final Location outside1 = Location.forString(\"0.0007187, 0.0029644\");\n        final Location boundary1 = Location.forString(\"0.0120374, -0.0220985\");\n        // both areas formed by the self intersection are contained\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(area1));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(area2));\n        Assert.assertFalse(polygon.fullyGeometricallyEncloses(outside1));\n        Assert.assertTrue(polygon.fullyGeometricallyEncloses(boundary1));\n    }\n\n    @Test\n    public void testSurface()\n    {\n        final Rectangle rectangle = Rectangle.TEST_RECTANGLE;\n        Assert.assertEquals(rectangle.surface(), new Polygon(rectangle).surface());\n\n        final Polygon tilted = new Polygon(rectangle.lowerLeft(),\n                new Location(rectangle.upperLeft().getLatitude(),\n                        Longitude.degrees(rectangle.upperLeft().getLongitude().asDegrees() + 0.1)),\n                new Location(rectangle.upperRight().getLatitude(),\n                        Longitude.degrees(rectangle.upperRight().getLongitude().asDegrees() + 0.1)),\n                rectangle.lowerRight());\n        Assert.assertEquals(rectangle.surface(), tilted.surface());\n    }\n\n    @Test\n    public void testSurfaceOnSphere()\n    {\n        // OSM way 58674551\n        final Polygon tennisCourt = new Polygon(Location.forString(\"47.6778433, -122.2012807\"),\n                Location.forString(\"47.6779773, -122.2008187\"),\n                Location.forString(\"47.6776721, -122.2006229\"),\n                Location.forString(\"47.6775366, -122.2010923\"),\n                Location.forString(\"47.6776969, -122.2011787\"),\n                Location.forString(\"47.6778433, -122.2012807\"));\n\n        // Expected surface area obtained from JOSM\n        Assert.assertEquals(1384.480, tennisCourt.surfaceOnSphere().asMeterSquared(), 2);\n\n        // Try on a larger, more complex area (OSM way 23408944). Surface area from JOSM\n        final Area forest = this.setup.getForestPolygon().area(23408944000000L);\n        Assert.assertEquals(229960, forest.asPolygon().surfaceOnSphere().asMeterSquared(), 300);\n    }\n\n    @Test\n    public void testToWkb()\n    {\n        final Polygon polygon = Polygon.wkt(\n                \"POLYGON((-22.5092778 65.0717446,-22.5092214 65.0717039,-22.5091195 65.0716869,-22.509031 65.0716847,-22.5089264 65.071705,-22.5088164 65.0717638,-22.5085884 65.0717785,-22.508449 65.0717593,-22.5082612 65.0717378,-22.5079903 65.0717367,-22.5078401 65.0717333,-22.5074565 65.0717875,-22.5072849 65.0718531,-22.5071454 65.0719017,-22.5070113 65.07193,-22.5069094 65.0719797,-22.5067806 65.0720103,-22.5066465 65.0720487,-22.5065419 65.0720894,-22.5065741 65.0721583,-22.506676 65.0721697,-22.5068691 65.0722341,-22.5071883 65.0722488,-22.5075585 65.0722307,-22.5078964 65.0721923,-22.5081754 65.0721493,-22.508508 65.0720905,-22.5088862 65.0720091,-22.509023 65.0719277,-22.5091329 65.0719029,-22.5091544 65.0718644,-22.5091705 65.0718102,-22.5092214 65.0717751,-22.5092778 65.0717446))\");\n        final String validPolygonWkb = \"00000000030000000100000022C036826007A7942E4050449776A9AA89C036825C556B6C6040504496CBF45FB9C0368255A7D241814050449684A6C34EC036824FDB09A672405044967B6C87E9C03682490024122E40504496D0917D6BC0368241CAA5AB2040504497C7318259C0368232D9711FFE4050449804D98394C0368229B6B2AF1440504497B451ABC4C036821D67EFD362405044975A2438C4C036820BA6FD2F6B4050449755871B12C0368201CF0D18ED4050449747446230C03681E8AB4FA483405044982A9930BEC03681DD6C57412F405044993DBEA770C03681D447EB511C4050449A099681B7C03681CB7E1833D34050449A8049667BC03681C4D07F08F54050449B50BE5E75C03681BC5F973F4C4050449BD116DE69C03681B395C422034050449C72268E09C03681ACBADE8DBF4050449D1CDBD8D9C03681AED718802A4050449E3DD8A8A3C03681B584B1AB084050449E6DA950C7C03681C22C5FDA5B4050449F7BC649FCC03681D717A969F14050449FB96E4B37C03681EF5A964E8B4050449F6D83911AC03682057F9BC1924050449ECC73E179C0368217C873A1B84050449E1818FB7AC036822D948DC11E4050449D2178F68CC03682465DB262BE4050449BCC0E60ECC036824F54D1E96C4050449A76A3CB4CC036825688A2D1514050449A0E9EFF34C0368257F1589D50405044996D23EFC9C0368258FF7596854050449889CF213BC036825C556B6C6040504497F696CAB2C036826007A7942E4050449776A9AA89\";\n        Assert.assertEquals(validPolygonWkb, WKBWriter.toHex(polygon.toWkb()));\n    }\n\n    @Test\n    public void testTriangulate()\n    {\n        final Polygon polygon = Polygon.SILICON_VALLEY_2;\n        final List<Polygon> triangles = polygon.triangles();\n        Assert.assertEquals(3, triangles.size());\n        for (final Polygon triangle : triangles)\n        {\n            Assert.assertEquals(3, triangle.size());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/PolygonTestRule.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * {@link PolygonTest} data\n *\n * @author mgostintsev\n */\npublic class PolygonTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"BLR-small.atlas.txt.gz\")\n    private Atlas complexForestPolygon;\n\n    public Atlas getForestPolygon()\n    {\n        return this.complexForestPolygon;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/RectangleTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport java.util.Arrays;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\n\nimport com.google.gson.JsonArray;\n\n/**\n * @author matthieun\n */\npublic class RectangleTest\n{\n    private final Location location1 = Location.TEST_6;\n    private final Location location2 = Location.TEST_4;\n    private final Location location3 = Location.TEST_1;\n    private final Location location4 = Location.TEST_2;\n\n    private final Rectangle rectangle1 = Rectangle.forLocations(this.location1, this.location2);\n    private final Rectangle rectangle2 = Rectangle.forLocations(this.location2, this.location3);\n    private final Rectangle rectangle3 = Rectangle.forLocations(this.location1, this.location3);\n    private final Rectangle rectangle4 = Rectangle.forLocations(this.location2, this.location4);\n\n    @Test\n    public void testAntiMeridianEastRectangle()\n    {\n        final Location antiMeridian = new Location(Latitude.ZERO, Longitude.degrees(180));\n        final Location lowerLeftAntiMeridianRectangle = new Location(Latitude.degrees(-10),\n                Longitude.degrees(170));\n        final Location lowerLeftTestRectangle = new Location(Latitude.degrees(-10),\n                Longitude.degrees(150));\n        final Location upperRightTestRectangle1 = new Location(Latitude.ZERO,\n                Longitude.degrees(160));\n        final Location upperRightTestRectangle2 = new Location(Latitude.ZERO,\n                Longitude.degrees(175));\n\n        // List construction\n        final Rectangle antiMeridianRectangle1 = Rectangle\n                .forLocations(Arrays.asList(antiMeridian, lowerLeftAntiMeridianRectangle));\n        final Rectangle testRectangle1 = Rectangle\n                .forLocations(Arrays.asList(upperRightTestRectangle1, lowerLeftTestRectangle));\n        Assert.assertFalse(testRectangle1.overlaps(antiMeridianRectangle1));\n        Assert.assertFalse(antiMeridianRectangle1.overlaps(testRectangle1));\n\n        // Corners construction\n        final Rectangle antiMeridianRectangle2 = Rectangle\n                .forCorners(lowerLeftAntiMeridianRectangle, antiMeridian);\n        final Rectangle testRectangle2 = Rectangle.forCorners(lowerLeftTestRectangle,\n                upperRightTestRectangle2);\n        Assert.assertTrue(testRectangle2.overlaps(antiMeridianRectangle2));\n        Assert.assertTrue(antiMeridianRectangle2.overlaps(testRectangle2));\n    }\n\n    @Test\n    public void testAsGeoJsonBbox()\n    {\n        final Rectangle rectangle = Rectangle.TEST_RECTANGLE;\n        final JsonArray array = rectangle.asGeoJsonBbox();\n        Assert.assertEquals(\"[-122.031905,37.328167,-122.029051,37.330394]\", array.toString());\n    }\n\n    @Test(expected = CoreException.class)\n    public void testConstructInvalidRectangle()\n    {\n        // The lower left is actually the lower right and the upper right is actually the upper\n        // left, making this an invalid specification.\n        @SuppressWarnings(\"unused\")\n        final Rectangle invalidRectangle = Rectangle.forCorners(\n                Location.forWkt(\"POINT (-122.288925 47.618916)\"),\n                Location.forWkt(\"POINT (-122.288935 47.618946)\"));\n    }\n\n    @Test\n    public void testContract()\n    {\n        // Test over contract\n        final Distance rectangle1CornerToCorner = this.rectangle1.lowerLeft()\n                .distanceTo(this.rectangle1.upperRight());\n        final Rectangle collapsedRectangle1 = this.rectangle1.contract(rectangle1CornerToCorner);\n        Assert.assertEquals(collapsedRectangle1.center().bounds(), collapsedRectangle1);\n        Assert.assertEquals(Surface.forDm7Squared(0), collapsedRectangle1.surface());\n\n        // test compatibility with expand\n        final Rectangle expanded = this.rectangle1.expand(Distance.ONE_METER);\n        Assert.assertEquals(this.rectangle1, expanded.contract(Distance.ONE_METER));\n\n        // test collapse horizontally\n        final Location rectangle1UpperLeft = Iterables.asList(this.rectangle1).get(1);\n        final Distance contractRectangle1Distance = rectangle1UpperLeft\n                .distanceTo(this.rectangle1.upperRight()).scaleBy(.51);\n        final Rectangle collapsedHorizontally = this.rectangle1\n                .contract(contractRectangle1Distance);\n        Assert.assertEquals(Surface.forDm7Squared(0), collapsedHorizontally.surface());\n        Assert.assertEquals(collapsedHorizontally.surface(),\n                this.rectangle1.contract(contractRectangle1Distance.scaleBy(10)).surface());\n        Assert.assertEquals(Math.round(contractRectangle1Distance.asMeters()),\n                Math.round(new Location(this.rectangle1.lowerLeft().getLatitude(),\n                        collapsedHorizontally.middle().getLongitude())\n                        .distanceTo(collapsedHorizontally.lowerLeft()).asMeters()));\n\n        // test collapse vertically\n        final Location rectangle2UpperLeft = Iterables.asList(this.rectangle2).get(1);\n        final Distance contractRectangle2Distance = rectangle2UpperLeft\n                .distanceTo(this.rectangle2.lowerLeft()).scaleBy(.51);\n        final Rectangle collapsedVertically = this.rectangle2.contract(contractRectangle2Distance);\n        Assert.assertEquals(Surface.forDm7Squared(0), collapsedVertically.surface());\n        Assert.assertEquals(collapsedVertically.surface(),\n                this.rectangle2.contract(contractRectangle2Distance.scaleBy(10)).surface());\n        Assert.assertEquals(Math.round(contractRectangle2Distance.asMeters()),\n                Math.round(new Location(collapsedVertically.middle().getLatitude(),\n                        this.rectangle2.lowerLeft().getLongitude())\n                        .distanceTo(collapsedVertically.lowerLeft()).asMeters()));\n\n        // Test fully collapse Collapsed\n        Assert.assertEquals(this.rectangle1.center().bounds(),\n                collapsedHorizontally.contract(Distance.TEN_MILES));\n        Assert.assertEquals(this.rectangle2.center().bounds(),\n                collapsedVertically.contract(Distance.TEN_MILES));\n    }\n\n    @Test\n    public void testCoversAndCoversPartially()\n    {\n        Assert.assertTrue(\"Rectangle 3 fully contains rectangle 2\",\n                this.rectangle3.fullyGeometricallyEncloses(this.rectangle2));\n        Assert.assertTrue(\"That means, it should also cover it partially\",\n                this.rectangle3.overlaps(this.rectangle2));\n\n        Assert.assertTrue(\"Rectangle 3 only partially covers rectangle 4\",\n                this.rectangle3.overlaps(this.rectangle4));\n        Assert.assertFalse(\"It should not fully contain it\",\n                this.rectangle3.fullyGeometricallyEncloses(this.rectangle4));\n\n        Assert.assertTrue(\"Rectangle 2 only partly overlaps rectangle 4\",\n                this.rectangle2.overlaps(this.rectangle4));\n        Assert.assertFalse(\"But does not fully contain it\",\n                this.rectangle2.fullyGeometricallyEncloses(this.rectangle4));\n    }\n\n    @Test\n    public void testExpansionAcrossMeridians()\n    {\n        final long kilometersPerDegreeLongitudeNearNorthPole = Math.round(Distance\n                .distancePerDegreeLongitudeAt(Location.forWkt(\"POINT(178 87)\")).asKilometers());\n        final long kilometersPerDegreeLongitudeNearSouthPole = Math.round(Distance\n                .distancePerDegreeLongitudeAt(Location.forWkt(\"POINT(-178 87)\")).asKilometers());\n\n        Rectangle closeToNorthPole = Rectangle.forCorners(Location.forWkt(\"POINT(178 84)\"),\n                Location.forWkt(\"POINT(179 87)\"));\n        closeToNorthPole = closeToNorthPole.expandHorizontally(\n                Distance.kilometers(kilometersPerDegreeLongitudeNearNorthPole * 100));\n        Assert.assertEquals(\n                \"POLYGON ((135.8977545 81.937154, 135.8977545 83.8283367, 179.9999999 83.8283367, 179.9999999 81.937154, 135.8977545 81.937154))\",\n                closeToNorthPole.toWkt());\n\n        Rectangle closeToSouthPole = Rectangle.forCorners(Location.forWkt(\"POINT(-179 84)\"),\n                Location.forWkt(\"POINT(-178 87)\"));\n        closeToSouthPole = closeToSouthPole.expandHorizontally(\n                Distance.kilometers(kilometersPerDegreeLongitudeNearSouthPole * 100));\n        Assert.assertEquals(\n                \"POLYGON ((-180 81.937154, -180 83.8283367, -116.9898158 83.8283367, -116.9898158 81.937154, -180 81.937154))\",\n                closeToSouthPole.toWkt());\n    }\n\n    @Test\n    public void testExpansionAcrossPoles()\n    {\n        final long kilometersPerDegreeLatitude = Math\n                .round(Distance.APPROXIMATE_DISTANCE_PER_DEGREE_AT_EQUATOR.asKilometers());\n\n        Rectangle closeToSouthPole = Rectangle.forCorners(Location.forWkt(\"POINT(0 -87)\"),\n                Location.forWkt(\"POINT(1 -84)\"));\n        // this should cross the south pole\n        closeToSouthPole = closeToSouthPole\n                .expand(Distance.kilometers(kilometersPerDegreeLatitude * 4));\n        Assert.assertEquals(\n                \"POLYGON ((-89.9998709 -86.0070121, -89.9998709 -79.2463163, 22.9131203 -79.2463163, 22.9131203 -86.0070121, -89.9998709 -86.0070121))\",\n                closeToSouthPole.toWkt());\n\n        Rectangle closeToNorthPole = Rectangle.forCorners(Location.forWkt(\"POINT(0 84)\"),\n                Location.forWkt(\"POINT(1 87)\"));\n        // this should cross the north pole\n        closeToNorthPole = closeToNorthPole\n                .expand(Distance.kilometers(kilometersPerDegreeLatitude * 4));\n        Assert.assertEquals(\n                \"POLYGON ((-21.9131203 79.2463163, -21.9131203 86.0070121, 90.9998709 86.0070121, 90.9998709 79.2463163, -21.9131203 79.2463163))\",\n                closeToNorthPole.toWkt());\n    }\n\n    @Test\n    public void testIntersection()\n    {\n        Assert.assertEquals(Rectangle.forLocations(this.location2),\n                this.rectangle1.intersection(this.rectangle2));\n        Assert.assertEquals(this.rectangle1, this.rectangle1.intersection(this.rectangle3));\n        Assert.assertEquals(\n                Rectangle.forLocations(Location.TEST_4,\n                        new Location(Location.TEST_6.getLatitude(),\n                                Location.TEST_2.getLongitude())),\n                this.rectangle3.intersection(this.rectangle4));\n    }\n\n    @Test\n    public void testIntersectsAndCoversWithPolyLine()\n    {\n        final PolyLine polyLine = new PolyLine(this.location1, this.location2);\n\n        Assert.assertTrue(\"Larger rectangle intersects polyline\",\n                this.rectangle4.intersects(polyLine));\n        Assert.assertTrue(\"Smaller rectangle intersects polyline\",\n                this.rectangle3.intersects(polyLine));\n\n        Assert.assertTrue(\"Smaller rectangle only touches the polyline, but does not cover it\",\n                this.rectangle3.fullyGeometricallyEncloses(polyLine));\n        Assert.assertFalse(\"Larger rectangle fully contains the polyline\",\n                this.rectangle4.fullyGeometricallyEncloses(polyLine));\n    }\n\n    @Test\n    public void testSurface()\n    {\n        final Surface surface = this.rectangle3.intersection(this.rectangle4).surface();\n        Assert.assertTrue(surface.isLessThan(this.rectangle3.surface()));\n        Assert.assertTrue(this.rectangle1.surface().add(this.rectangle2.surface())\n                .isLessThan(this.rectangle3.surface()));\n\n        Assert.assertEquals(6479999998200000000L, Rectangle.MAXIMUM.surface().asDm7Squared());\n    }\n\n    @Test\n    public void testWidth()\n    {\n        Assert.assertEquals(50160, this.rectangle1.width().asDm7());\n        Assert.assertEquals(-1, Rectangle.MAXIMUM.width().asDm7());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/SegmentTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * @author matthieun\n */\npublic class SegmentTest\n{\n\n    private static Segment SEGMENT_SAME_END_1 = PolyLine\n            .wkt(\"LINESTRING (112.9699474 -84.7999528, 112.9699948 -84.7999669)\").segments().get(0);\n    private static Segment SEGMENT_SAME_END_2 = PolyLine\n            .wkt(\"LINESTRING (112.9650809 -84.7999622, 112.9699948 -84.7999669)\").segments().get(0);\n\n    @Test\n    public void testAntimeridianNoIntersection()\n    {\n        final Segment antimeridianWest = new Segment(\n                new Location(Latitude.degrees(40), Longitude.ANTIMERIDIAN_WEST),\n                new Location(Latitude.degrees(41), Longitude.ANTIMERIDIAN_WEST));\n\n        final Segment antimeridianEast = new Segment(\n                new Location(Latitude.degrees(40), Longitude.ANTIMERIDIAN_EAST),\n                new Location(Latitude.degrees(41), Longitude.ANTIMERIDIAN_EAST));\n        Assert.assertNull(antimeridianEast.intersection(antimeridianWest));\n        Assert.assertTrue(antimeridianEast.length().equals(antimeridianWest.length()));\n        Assert.assertTrue(antimeridianEast.length().isLessThan(Distance.miles(100)));\n    }\n\n    @Test\n    public void testIntersection()\n    {\n        Assert.assertEquals(null, new Segment(Location.TEST_1, Location.TEST_2)\n                .intersection(new Segment(Location.TEST_4, Location.TEST_3)));\n        Assert.assertEquals(false, new Segment(Location.TEST_1, Location.TEST_2)\n                .intersects(new Segment(Location.TEST_4, Location.TEST_3)));\n\n        Assert.assertEquals(\n                new Location(Latitude.degrees(37.3273389), Longitude.degrees(-122.0287109)),\n                new Segment(Location.TEST_1, Location.TEST_3)\n                        .intersection(new Segment(Location.TEST_4, Location.TEST_2)));\n        Assert.assertEquals(true, new Segment(Location.TEST_1, Location.TEST_3)\n                .intersects(new Segment(Location.TEST_4, Location.TEST_2)));\n    }\n\n    @Test\n    public void testIntersectionOverflow()\n    {\n        final Segment maxXandYMovement = new Segment(\n                new Location(Latitude.MINIMUM, Longitude.MINIMUM),\n                new Location(Latitude.MAXIMUM, Longitude.MAXIMUM));\n        final Segment maxXandNegativeYMovement = new Segment(\n                new Location(Latitude.MAXIMUM, Longitude.MINIMUM),\n                new Location(Latitude.MINIMUM, Longitude.MAXIMUM));\n\n        Assert.assertTrue(\"Overflow issue\", maxXandYMovement.intersects(maxXandNegativeYMovement));\n        Assert.assertTrue(\"Overflow issue\", maxXandNegativeYMovement.intersects(maxXandYMovement));\n\n    }\n\n    @Test\n    public void testIntersectionWithSameEnd()\n    {\n        Assert.assertTrue(\"Same start is broken.\",\n                SEGMENT_SAME_END_1.reversed().intersects(SEGMENT_SAME_END_2.reversed()));\n        Assert.assertTrue(\"Same start is broken.\",\n                SEGMENT_SAME_END_2.reversed().intersects(SEGMENT_SAME_END_1.reversed()));\n        Assert.assertTrue(\"Same End is broken\", SEGMENT_SAME_END_1.intersects(SEGMENT_SAME_END_2));\n        Assert.assertTrue(\"Same End is broken\", SEGMENT_SAME_END_2.intersects(SEGMENT_SAME_END_1));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/SelfIntersectingPolyLineTestCase.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Test case for the self intersecting line API.\n *\n * @author cstaylor\n */\npublic class SelfIntersectingPolyLineTestCase\n{\n    private static final Location ONE = Location.forString(\"40.0000001, -80.0000003\");\n    private static final Location TWO = Location.forString(\"40.0000001, -80.0000001\");\n    private static final Location THREE = Location.forString(\"40.0000003, -80.0000001\");\n    private static final Location FOUR = Location.forString(\"40.0000003, -80.0000003\");\n\n    @Test\n    public void noSelfIntersection()\n    {\n        final PolyLine line = new PolyLine(ONE, TWO, THREE, FOUR);\n        Assert.assertFalse(line.selfIntersects());\n    }\n\n    @Test\n    public void selfIntersects()\n    {\n        final PolyLine line = new PolyLine(ONE, TWO, THREE, FOUR, TWO);\n        Assert.assertTrue(line.selfIntersects());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/SnapperTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n * @author bbreithaupt\n */\npublic class SnapperTest\n{\n    private static final Snapper.SnappedLocation SNAP_1 = new Snapper.SnappedLocation(\n            Location.TEST_1, Location.TEST_2, new PolyLine(Location.TEST_2, Location.TEST_3));\n    private static final Snapper.SnappedLocation SNAP_2 = new Snapper.SnappedLocation(\n            Location.TEST_2, Location.TEST_1, new PolyLine(Location.TEST_3, Location.TEST_2));\n\n    @Test\n    public void testMultiPolygon()\n    {\n        final MultiPolygon shape = MultiPolygon.TEST_MULTI_POLYGON;\n        final Location origin1 = Location.forString(\"37.328709, -122.032873\");\n        final Location origin2 = Location.forString(\"37.324014, -122.046642\");\n        Assert.assertEquals(Location.forString(\"37.3283544,-122.0322605\"), origin1.snapTo(shape));\n        Assert.assertEquals(Location.forString(\"37.3249067,-122.0459561\"), origin2.snapTo(shape));\n    }\n\n    @Test\n    public void testPolyLine()\n    {\n        final PolyLine shape = new PolyLine(Location.TEST_6, Location.TEST_1,\n                Location.EIFFEL_TOWER);\n        final Location origin = Location.TEST_2;\n        Assert.assertEquals(Location.forString(\"37.3268107,-122.030562\"), origin.snapTo(shape));\n        Assert.assertEquals(Location.TEST_6, shape.snapFrom(Location.TEST_3));\n        Assert.assertEquals(Location.EIFFEL_TOWER, shape.snapFrom(Location.COLOSSEUM));\n    }\n\n    @Test\n    public void testPolygon()\n    {\n        final Polygon shape = new Polygon(Location.TEST_6, Location.TEST_1, Location.EIFFEL_TOWER);\n        final Location origin = Location.forString(\"37.325315, -122.008007\");\n        Assert.assertEquals(Location.forString(\"37.3278247,-122.0082398\"), origin.snapTo(shape));\n    }\n\n    @Test\n    public void testSegment()\n    {\n        final Segment shape = new Segment(Location.TEST_6, Location.TEST_1);\n        final Location origin = Location.TEST_2;\n        Assert.assertEquals(Location.forString(\"37.3268107,-122.030562\"), origin.snapTo(shape));\n        Assert.assertEquals(Location.TEST_6, shape.snapFrom(Location.TEST_3));\n        Assert.assertEquals(Location.TEST_1, shape.snapFrom(Location.EIFFEL_TOWER));\n    }\n\n    @Test\n    public void testSnappedLocationEqualsLocationFalse()\n    {\n        Assert.assertNotEquals(SNAP_1, Location.TEST_1);\n    }\n\n    @Test\n    @SuppressWarnings(\"squid:S3415\")\n    public void testSnappedLocationEqualsLocationTrue()\n    {\n        Assert.assertEquals(SNAP_1, Location.TEST_2);\n    }\n\n    @Test\n    public void testSnappedLocationEqualsSnappedLocationFalse()\n    {\n        Assert.assertNotEquals(SNAP_1, SNAP_2);\n    }\n\n    @Test\n    @SuppressWarnings(\"squid:S3415\")\n    public void testSnappedLocationEqualsSnappedLocationTrue()\n    {\n        Assert.assertEquals(SNAP_1, SNAP_1);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/StringCompressedPolyLineTest.java",
    "content": "package org.openstreetmap.atlas.geography;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * Test a {@link StringCompressedPolyLine}\n *\n * @author matthieun\n */\npublic class StringCompressedPolyLineTest\n{\n    @Test\n    public void testCompressionDecompression()\n    {\n        final PolyLine polyLine = PolyLine.TEST_POLYLINE;\n        System.out.println(polyLine);\n        final StringCompressedPolyLine compressed = new StringCompressedPolyLine(polyLine);\n        System.out.println(compressed);\n        final PolyLine decompressed = compressed.asPolyLine();\n        System.out.println(decompressed);\n        Assert.assertEquals(polyLine, decompressed);\n    }\n\n    /**\n     * Here the delta longitude between loc1/loc2 and loc3/loc4 is more than 180 degrees, so the\n     * algorithm falls back on WKB.\n     */\n    @Test\n    public void testCompressionWkbFallback()\n    {\n        final Location location1 = new Location(Latitude.degrees(45.0), Longitude.degrees(-179.0));\n        final Location location2 = new Location(Latitude.degrees(45.0), Longitude.degrees(179.0));\n        final Location location3 = new Location(Latitude.degrees(45.0), Longitude.degrees(179.0));\n        final Location location4 = new Location(Latitude.degrees(45.0), Longitude.degrees(-179.0));\n        final PolyLine line1 = new PolyLine(location1, location2);\n        final PolyLine line2 = new PolyLine(location3, location4);\n        final StringCompressedPolyLine compressedLine1 = new StringCompressedPolyLine(line1);\n        final StringCompressedPolyLine compressedLine2 = new StringCompressedPolyLine(line2);\n\n        // the toString method should return WKT since the compression is WKB instead of MapQuest\n        // string compression\n        Assert.assertEquals(compressedLine1.toString(), compressedLine1.asPolyLine().toWkt());\n        Assert.assertEquals(compressedLine2.toString(), compressedLine2.asPolyLine().toWkt());\n    }\n\n    @Test\n    public void testSmallPolyLine()\n    {\n        final Distance distance = Distance.meters(100);\n        final Location loc1 = Location.TEST_5;\n        final Location loc2 = loc1.shiftAlongGreatCircle(Heading.EAST, distance);\n        final Location loc3 = loc2.shiftAlongGreatCircle(Heading.NORTH, distance);\n        final Location loc4 = loc3.shiftAlongGreatCircle(Heading.WEST, distance);\n        final Location loc5 = loc4.shiftAlongGreatCircle(Heading.NORTH, distance);\n        final PolyLine polyLine = new PolyLine(loc1, loc2, loc3, loc4, loc5);\n\n        System.out.println(polyLine);\n        final StringCompressedPolyLine compressed = new StringCompressedPolyLine(polyLine);\n        System.out.println(compressed);\n        final PolyLine decompressed = compressed.asPolyLine();\n        System.out.println(decompressed);\n        Assert.assertEquals(polyLine, decompressed);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/AtlasResourceLoaderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.nio.file.Paths;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * @author lcram\n */\npublic class AtlasResourceLoaderTest\n{\n    private static final long BYTE_ARRAY_SIZE = 8192L;\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void attemptToLoadNonAtlasData()\n    {\n        final ByteArrayResource nonAtlasResource = new ByteArrayResource();\n        nonAtlasResource.writeAndClose(\"some random data\");\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Failed to load an atlas\");\n        new AtlasResourceLoader().load(nonAtlasResource);\n    }\n\n    @Test\n    public void attemptToLoadResourcesWithMixedEmpty()\n    {\n        final ByteArrayResource emptyResource = new ByteArrayResource();\n        final Resource atlasResource = getAtlasResource(this::getSinglePointAtlas);\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"had zero length!\");\n        new AtlasResourceLoader().load(atlasResource, emptyResource);\n    }\n\n    @Test\n    public void attemptToLoadSingleEmptyResource()\n    {\n        final ByteArrayResource emptyResource = new ByteArrayResource();\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"had zero length!\");\n        new AtlasResourceLoader().load(emptyResource);\n    }\n\n    @Test\n    public void basicCompressedLoadTest()\n    {\n        final Atlas atlas = new AtlasResourceLoader()\n                .load(getCompressedAtlasResource(this::getSinglePointAtlas));\n        Assert.assertEquals(1, atlas.numberOfPoints());\n\n        final Atlas atlasFromText = new AtlasResourceLoader()\n                .load(getCompressedTextAtlasResource(this::getSinglePointAtlas));\n        Assert.assertEquals(1, atlasFromText.numberOfPoints());\n    }\n\n    @Test\n    public void basicLoadTest()\n    {\n        final Atlas atlas = new AtlasResourceLoader()\n                .load(getAtlasResource(this::getSinglePointAtlas));\n        Assert.assertEquals(1, atlas.numberOfPoints());\n\n        final Atlas atlasFromText = new AtlasResourceLoader()\n                .load(getTextAtlasResource(this::getSinglePointAtlas));\n        Assert.assertEquals(1, atlasFromText.numberOfPoints());\n    }\n\n    @Test\n    public void multipleLoadTest()\n    {\n        final Atlas atlasLoadedFromAllPacked = new AtlasResourceLoader().withMultiAtlasName(\"foo\")\n                .load(getAtlasResource(this::getSinglePointAtlas),\n                        getAtlasResource(this::getMultiplePointAtlas));\n        Assert.assertEquals(3, atlasLoadedFromAllPacked.numberOfPoints());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"b\"), atlasLoadedFromAllPacked.point(1L).getTags());\n        Assert.assertEquals(Maps.hashMap(\"c\", \"d\"), atlasLoadedFromAllPacked.point(2L).getTags());\n        Assert.assertEquals(Maps.hashMap(\"e\", \"f\"), atlasLoadedFromAllPacked.point(3L).getTags());\n        Assert.assertEquals(\"foo\", atlasLoadedFromAllPacked.getName());\n\n        final Atlas atlasLoadedFromPackedTextMix = new AtlasResourceLoader().load(\n                getAtlasResource(this::getSinglePointAtlas),\n                getTextAtlasResource(this::getMultiplePointAtlas));\n        Assert.assertEquals(3, atlasLoadedFromPackedTextMix.numberOfPoints());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"b\"),\n                atlasLoadedFromPackedTextMix.point(1L).getTags());\n        Assert.assertEquals(Maps.hashMap(\"c\", \"d\"),\n                atlasLoadedFromPackedTextMix.point(2L).getTags());\n        Assert.assertEquals(Maps.hashMap(\"e\", \"f\"),\n                atlasLoadedFromPackedTextMix.point(3L).getTags());\n\n        final Atlas atlasLoadedFromAllText = new AtlasResourceLoader().load(\n                getTextAtlasResource(this::getSinglePointAtlas),\n                getTextAtlasResource(this::getMultiplePointAtlas));\n        Assert.assertEquals(3, atlasLoadedFromAllText.numberOfPoints());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"b\"), atlasLoadedFromAllText.point(1L).getTags());\n        Assert.assertEquals(Maps.hashMap(\"c\", \"d\"), atlasLoadedFromAllText.point(2L).getTags());\n        Assert.assertEquals(Maps.hashMap(\"e\", \"f\"), atlasLoadedFromAllText.point(3L).getTags());\n\n        final Atlas filteredAtlas = new AtlasResourceLoader()\n                .withAtlasEntityFilter(entity -> entity.getIdentifier() != 3)\n                .load(getAtlasResource(this::getMultiplePointAtlas));\n        Assert.assertEquals(2, filteredAtlas.numberOfPoints());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"b\"), filteredAtlas.point(1L).getTags());\n        Assert.assertEquals(Maps.hashMap(\"c\", \"d\"), filteredAtlas.point(2L).getTags());\n    }\n\n    @Test\n    public void safeLoadTest()\n    {\n        Optional<Atlas> atlas = new AtlasResourceLoader()\n                .safeLoad(getAtlasResource(this::getSinglePointAtlas));\n        Assert.assertTrue(atlas.isPresent());\n        Assert.assertEquals(1, atlas.get().numberOfPoints());\n\n        final ByteArrayResource nonAtlasResource = new ByteArrayResource();\n        nonAtlasResource.writeAndClose(\"some random data\");\n\n        atlas = new AtlasResourceLoader().safeLoad(nonAtlasResource);\n        Assert.assertFalse(atlas.isPresent());\n    }\n\n    @Test\n    public void testLoadRecursively()\n    {\n        final File parent = File.temporaryFolder();\n        final File subFolder = parent.child(\"subfolder\");\n        subFolder.mkdirs();\n        try\n        {\n            final File atlasFile1 = parent.child(\"hello1.atlas.txt\");\n            new TextAtlasBuilder().write(getSinglePointAtlas(), atlasFile1);\n\n            final File atlasFile2 = subFolder.child(\"hello2.atlas\");\n            getMultiplePointAtlas().save(atlasFile2);\n\n            final File notAtlas1 = parent.child(\"random1.txt\");\n            notAtlas1.writeAndClose(\"hello world\");\n\n            final File notAtlas2 = subFolder.child(\"random2.txt\");\n            notAtlas2.writeAndClose(\"hello world again\");\n\n            final Atlas atlas = new AtlasResourceLoader().loadRecursively(parent);\n            Assert.assertEquals(3, atlas.numberOfPoints());\n            Assert.assertEquals(Maps.hashMap(\"a\", \"b\"), atlas.point(1L).getTags());\n            Assert.assertEquals(Maps.hashMap(\"c\", \"d\"), atlas.point(2L).getTags());\n            Assert.assertEquals(Maps.hashMap(\"e\", \"f\"), atlas.point(3L).getTags());\n\n            final Atlas filteredAtlas = new AtlasResourceLoader()\n                    .withAtlasEntityFilter(entity -> entity.getIdentifier() != 3L)\n                    .loadRecursively(parent);\n            Assert.assertEquals(2, filteredAtlas.numberOfPoints());\n            Assert.assertEquals(Maps.hashMap(\"a\", \"b\"), filteredAtlas.point(1L).getTags());\n            Assert.assertEquals(Maps.hashMap(\"c\", \"d\"), filteredAtlas.point(2L).getTags());\n        }\n        finally\n        {\n            parent.deleteRecursively();\n        }\n    }\n\n    @Test\n    public void testLoadSingleFileRecursively()\n    {\n        final File parent = File.temporaryFolder();\n        try\n        {\n            final File atlasFile = parent.child(\"hello.atlas\");\n            getMultiplePointAtlas().save(atlasFile);\n\n            final Atlas atlas = new AtlasResourceLoader().loadRecursively(atlasFile);\n            Assert.assertEquals(3, atlas.numberOfPoints());\n            Assert.assertEquals(Maps.hashMap(\"a\", \"b\"), atlas.point(1L).getTags());\n            Assert.assertEquals(Maps.hashMap(\"c\", \"d\"), atlas.point(2L).getTags());\n            Assert.assertEquals(Maps.hashMap(\"e\", \"f\"), atlas.point(3L).getTags());\n        }\n        finally\n        {\n            parent.deleteRecursively();\n        }\n    }\n\n    @Test\n    public void testResourceFilter()\n    {\n        final StringResource stringResource = new StringResource().withName(\"hello.atlas.txt\");\n        new TextAtlasBuilder().write(getSinglePointAtlas(), stringResource);\n\n        final Resource otherResource = getAtlasResource(this::getMultiplePointAtlas);\n\n        final Atlas atlas = new AtlasResourceLoader()\n                .withResourceFilter(resource -> resource instanceof StringResource)\n                .load(stringResource, otherResource);\n\n        Assert.assertEquals(1, atlas.numberOfPoints());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"b\"), atlas.point(1L).getTags());\n    }\n\n    @Test\n    public void testSafeLoadRecursively()\n    {\n        final File parent = File.temporaryFolder();\n        final File subFolder = parent.child(\"subfolder\");\n        subFolder.mkdirs();\n        try\n        {\n            final File atlasFile1 = parent.child(\"hello1.atlas.txt\");\n            new TextAtlasBuilder().write(getSinglePointAtlas(), atlasFile1);\n\n            final File atlasFile2 = subFolder.child(\"hello2.atlas\");\n            getMultiplePointAtlas().save(atlasFile2);\n\n            final File notAtlas1 = parent.child(\"random1.txt\");\n            notAtlas1.writeAndClose(\"hello world\");\n\n            final File notAtlas2 = subFolder.child(\"random2.txt\");\n            notAtlas2.writeAndClose(\"hello world again\");\n\n            final Optional<Atlas> atlas = new AtlasResourceLoader().safeLoadRecursively(parent);\n            Assert.assertTrue(atlas.isPresent());\n            Assert.assertEquals(3, atlas.get().numberOfPoints());\n            Assert.assertEquals(Maps.hashMap(\"a\", \"b\"), atlas.get().point(1L).getTags());\n            Assert.assertEquals(Maps.hashMap(\"c\", \"d\"), atlas.get().point(2L).getTags());\n            Assert.assertEquals(Maps.hashMap(\"e\", \"f\"), atlas.get().point(3L).getTags());\n        }\n        finally\n        {\n            parent.deleteRecursively();\n        }\n    }\n\n    @Test\n    public void testSafeLoadRecursivelyFail()\n    {\n        final File parent = File.temporaryFolder();\n        final File subFolder = parent.child(\"subfolder\");\n        subFolder.mkdirs();\n        try\n        {\n            final File atlasFile1 = parent.child(\"hello1.atlas.txt\");\n            new TextAtlasBuilder().write(getSinglePointAtlas(), atlasFile1);\n\n            final File atlasFile2 = subFolder.child(\"hello2.atlas\");\n            getMultiplePointAtlas().save(atlasFile2);\n\n            final File corruptedAtlas1 = parent.child(\"corrupted.atlas\");\n            corruptedAtlas1.writeAndClose(\"some random non-atlas data\");\n\n            final File notAtlas1 = parent.child(\"random1.txt\");\n            notAtlas1.writeAndClose(\"hello world\");\n\n            final Optional<Atlas> atlas = new AtlasResourceLoader().safeLoadRecursively(parent);\n            Assert.assertFalse(atlas.isPresent());\n        }\n        finally\n        {\n            parent.deleteRecursively();\n        }\n    }\n\n    @Test\n    public void tryFilterThatEmptiesAnAtlas()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Entity filter resulted in an empty atlas\");\n        final Atlas filteredAtlas = new AtlasResourceLoader()\n                .withAtlasEntityFilter(entity -> entity.getIdentifier() != 1)\n                .load(getAtlasResource(this::getSinglePointAtlas));\n    }\n\n    @Test\n    public void tryToLoadDirectoryNonRecursively()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"was of type File but it was a directory. Try loadRecursively instead.\");\n        final Atlas atlas = new AtlasResourceLoader()\n                .load(new File(Paths.get(System.getProperty(\"user.home\")).toString()));\n    }\n\n    @Test\n    public void tryToLoadNonExistentFile()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"was of type File but it could not be found\");\n        new AtlasResourceLoader().load(new File(\n                Paths.get(System.getProperty(\"user.home\"), \"SomeFileThatDoesNotExist\").toString()));\n    }\n\n    @Test\n    public void tryToLoadNonFileRecursively()\n    {\n        final Resource resource = getAtlasResource(this::getSinglePointAtlas);\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"was not a File, instead was\");\n        final Atlas atlas = new AtlasResourceLoader().loadRecursively(resource);\n    }\n\n    private Resource getAtlasResource(final Supplier<Atlas> atlasSupplier)\n    {\n        final Atlas atlas = atlasSupplier.get();\n        final ByteArrayResource resource = new ByteArrayResource(BYTE_ARRAY_SIZE)\n                .withName(\"hello.atlas\");\n        atlas.save(resource);\n        return resource;\n    }\n\n    private Resource getCompressedAtlasResource(final Supplier<Atlas> atlasSupplier)\n    {\n        final Atlas atlas = atlasSupplier.get();\n        final ByteArrayResource resource = new ByteArrayResource(BYTE_ARRAY_SIZE)\n                .withName(\"hello.atlas.gz\");\n        resource.setCompressor(Compressor.GZIP);\n        atlas.save(resource);\n        return resource;\n    }\n\n    private Resource getCompressedTextAtlasResource(final Supplier<Atlas> atlasSupplier)\n    {\n        final Atlas atlas = atlasSupplier.get();\n        final ByteArrayResource resource = new ByteArrayResource(BYTE_ARRAY_SIZE)\n                .withName(\"hello.atlas.txt.gz\");\n        resource.setCompressor(Compressor.GZIP);\n        new TextAtlasBuilder().write(atlas, resource);\n        return resource;\n    }\n\n    private Atlas getMultiplePointAtlas()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addPoint(1, new Location(Latitude.degrees(1), Longitude.degrees(1)),\n                Maps.hashMap(\"a\", \"b\"));\n        builder.addPoint(2, new Location(Latitude.degrees(2), Longitude.degrees(2)),\n                Maps.hashMap(\"c\", \"d\"));\n        builder.addPoint(3, new Location(Latitude.degrees(3), Longitude.degrees(3)),\n                Maps.hashMap(\"e\", \"f\"));\n        return builder.get();\n    }\n\n    private Atlas getSinglePointAtlas()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addPoint(1, new Location(Latitude.degrees(1), Longitude.degrees(1)),\n                Maps.hashMap(\"a\", \"b\"));\n        return builder.get();\n    }\n\n    private Resource getTextAtlasResource(final Supplier<Atlas> atlasSupplier)\n    {\n        final Atlas atlas = atlasSupplier.get();\n        final ByteArrayResource resource = new ByteArrayResource(BYTE_ARRAY_SIZE)\n                .withName(\"hello.atlas.txt\");\n        new TextAtlasBuilder().write(atlas, resource);\n        return resource;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/AtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\n\n/**\n * @author Yazad Khambata\n */\npublic class AtlasTest\n{\n    @Rule\n    public final AtlasTestRule rule = new AtlasTestRule();\n\n    @Test\n    public void entitiesByIds()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Map<ItemType, ? extends Iterable<? extends AtlasEntity>> itemTypeToEntities = Arrays\n                .stream(ItemType.values())\n                .map(itemType -> Pair.of(itemType, itemType.entitiesForIdentifiers(atlas, 0L, 1L)))\n                .collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n        Assert.assertEquals(ItemType.values().length, itemTypeToEntities.size());\n        itemTypeToEntities.entrySet().forEach(entry ->\n        {\n            final ItemType itemType = entry.getKey();\n            final List<AtlasEntity> atlasEntities = new ArrayList<>((Collection) entry.getValue());\n            Assert.assertNotNull(atlasEntities);\n            Assert.assertFalse(atlasEntities.isEmpty());\n            Assert.assertEquals(\"itemType: \" + itemType, 2, atlasEntities.size());\n            atlasEntities.forEach(atlasEntity ->\n            {\n                final Class<AtlasEntity> memberClass = itemType.getMemberClass();\n                final Class<? extends AtlasEntity> atlasEntityClass = atlasEntity.getClass();\n                Assert.assertTrue(\n                        \"itemType: \" + itemType + \"; memberClass: \" + memberClass\n                                + \"; atlasEntityClass: \" + atlasEntityClass,\n                        memberClass.isAssignableFrom(atlasEntityClass));\n            });\n        });\n    }\n\n    @Test\n    public void nodesByIds()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Iterable<Node> nodes = atlas.nodes(0L, 1L);\n        final long count = StreamSupport.stream(nodes.spliterator(), false).count();\n        Assert.assertEquals(2, count);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/AtlasTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class AtlasTestRule extends CoreTestRule\n{\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n    private static final String THREE = \"37.780724, -122.472249\";\n    private static final String FOUR = \"37.780825, -122.471896\";\n\n    @TestAtlas(nodes = {\n\n            @Node(id = \"0\", coordinates = @Loc(value = FOUR)),\n            @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n            @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n            @Node(id = \"3\", coordinates = @Loc(value = THREE))\n\n    }, edges = {\n\n            @Edge(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }),\n            @Edge(id = \"1\", coordinates = { @Loc(value = TWO), @Loc(value = THREE) })\n\n    }, areas = {\n\n            @Area(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                    @Loc(value = THREE) }),\n            @Area(id = \"1\", coordinates = { @Loc(value = TWO), @Loc(value = THREE),\n                    @Loc(value = FOUR) })\n\n    }, lines = {\n\n            @Line(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }),\n            @Line(id = \"1\", coordinates = { @Loc(value = TWO), @Loc(value = FOUR) })\n\n    }, points = {\n\n            @Point(id = \"0\", coordinates = @Loc(value = ONE)),\n            @Point(id = \"1\", coordinates = @Loc(value = TWO)),\n            @Point(id = \"2\", coordinates = @Loc(value = THREE)),\n            @Point(id = \"3\", coordinates = @Loc(value = FOUR))\n\n    }, relations = {\n\n            @Relation(id = \"0\", members = { @Member(id = \"0\", role = \"from\", type = \"edge\"),\n                    @Member(id = \"2\", role = \"via\", type = \"node\"),\n                    @Member(id = \"1\", role = \"to\", type = \"edge\") }),\n            @Relation(id = \"1\", members = { @Member(id = \"0\", role = \"inside\", type = \"area\"),\n                    @Member(id = \"1\", role = \"outside\", type = \"line\") }),\n\n    })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/BareAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\n\nimport org.apache.commons.io.IOUtils;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\nimport com.google.gson.JsonObject;\n\n/**\n * @author matthieun\n * @author hallahan\n */\npublic class BareAtlasTest\n{\n    @Rule\n    public final BareAtlasTestRule rule = new BareAtlasTestRule();\n\n    @Rule\n    public ExpectedException testGetEntitiesWithWrongTypeSpecifiedException = ExpectedException\n            .none();\n\n    @Test\n    public void testGetEntitiesWithTypeSpecified()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n\n        final Set<Node> nodes = Iterables.stream(atlas.nodes()).collectToSet();\n        final Set<Edge> edges = Iterables.stream(atlas.edges()).collectToSet();\n        final Set<Area> areas = Iterables.stream(atlas.areas()).collectToSet();\n        final Set<Line> lines = Iterables.stream(atlas.lines()).collectToSet();\n        final Set<Point> points = Iterables.stream(atlas.points()).collectToSet();\n        final Set<Relation> relations = Iterables.stream(atlas.relations()).collectToSet();\n\n        final Set<Node> nodes2 = Iterables.stream(atlas.entities(ItemType.NODE, Node.class))\n                .collectToSet();\n        final Set<Edge> edges2 = Iterables.stream(atlas.entities(ItemType.EDGE, Edge.class))\n                .collectToSet();\n        final Set<Area> areas2 = Iterables.stream(atlas.entities(ItemType.AREA, Area.class))\n                .collectToSet();\n        final Set<Line> lines2 = Iterables.stream(atlas.entities(ItemType.LINE, Line.class))\n                .collectToSet();\n        final Set<Point> points2 = Iterables.stream(atlas.entities(ItemType.POINT, Point.class))\n                .collectToSet();\n        final Set<Relation> relations2 = Iterables\n                .stream(atlas.entities(ItemType.RELATION, Relation.class)).collectToSet();\n\n        Assert.assertEquals(nodes, nodes2);\n        Assert.assertEquals(edges, edges2);\n        Assert.assertEquals(areas, areas2);\n        Assert.assertEquals(lines, lines2);\n        Assert.assertEquals(points, points2);\n        Assert.assertEquals(relations, relations2);\n    }\n\n    @Test\n    public void testGetEntitiesWithWrongTypeSpecified()\n    {\n        this.testGetEntitiesWithWrongTypeSpecifiedException.expect(CoreException.class);\n        this.testGetEntitiesWithWrongTypeSpecifiedException.expectMessage(\"do not match!\");\n        final Atlas atlas = this.rule.getAtlas();\n        atlas.entities(ItemType.NODE, Relation.class);\n    }\n\n    @Test\n    public void testRelationsOrder()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final List<Long> expectedRelationIdentifiers = new ArrayList<>();\n        expectedRelationIdentifiers.add(1L);\n        expectedRelationIdentifiers.add(2L);\n        expectedRelationIdentifiers.add(3L);\n        expectedRelationIdentifiers.add(5L);\n        expectedRelationIdentifiers.add(4L);\n        Assert.assertEquals(expectedRelationIdentifiers,\n                Iterables.stream(atlas.relationsLowerOrderFirst())\n                        .map(relation -> relation.getIdentifier()).collectToList());\n    }\n\n    @Test\n    public void testSaveAsLineDelimitedGeoJsonFeatures()\n    {\n        final InputStream inputStream = BareAtlasTest.class\n                .getResourceAsStream(\"line-delimited-geojson.txt\");\n        try\n        {\n            final String correctText = IOUtils.toString(inputStream, StandardCharsets.UTF_8);\n\n            final Atlas atlas = this.rule.getAtlas();\n            final StringResource stringResource = new StringResource();\n            final BiConsumer<AtlasEntity, JsonObject> jsonMutator = (atlasEntity, feature) ->\n            {\n            };\n            atlas.saveAsLineDelimitedGeoJsonFeatures(stringResource, jsonMutator);\n            final String text = stringResource.writtenString();\n\n            Assert.assertEquals(correctText, text);\n\n        }\n        catch (final IOException exception)\n        {\n            exception.printStackTrace();\n            Assert.fail();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/BareAtlasTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class BareAtlasTestRule extends CoreTestRule\n{\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n    private static final String THREE = \"37.780724, -122.472249\";\n    private static final String FOUR = \"37.780825, -122.471896\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE))\n\n            }, edges = {\n\n                    @Edge(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"fixme=please\" }),\n                    @Edge(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" })\n\n            }, areas = {\n\n                    @Area(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"addr:housenumber=25\" }),\n                    @Area(id = \"1\", coordinates = { @Loc(value = TWO), @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"natural=water\", \"water=lake\" })\n\n            }, lines = {\n\n                    @Line(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"railway=station\", \"FIXME=0\" }),\n                    @Line(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = FOUR) }, tags = { \"train=yes\",\n                                    \"public_transport=stop_position\" })\n\n            }, points = {\n\n                    @Point(id = \"0\", coordinates = @Loc(value = ONE), tags = { \"addr:street=coco\",\n                            \"addr:housenumber=25\" }),\n                    @Point(id = \"1\", coordinates = @Loc(value = TWO), tags = {\n                            \"fixme=wrong name\" }),\n                    @Point(id = \"2\", coordinates = @Loc(value = THREE), tags = { \"landuse=basin\" }),\n                    @Point(id = \"3\", coordinates = @Loc(value = FOUR), tags = { \"amenity=school\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=restriction\",\n                            \"restriction=no_left_turn\" }, members = {\n                                    @Member(id = \"0\", role = \"from\", type = \"edge\"),\n                                    @Member(id = \"2\", role = \"via\", type = \"node\"),\n                                    @Member(id = \"1\", role = \"to\", type = \"edge\") }),\n                    @Relation(id = \"2\", tags = { \"type=half_inside\" }, members = {\n                            @Member(id = \"0\", role = \"inside\", type = \"area\"),\n                            @Member(id = \"1\", role = \"outside\", type = \"line\") }),\n                    @Relation(id = \"3\", tags = { \"type=outside\" }, members = {\n                            @Member(id = \"1\", role = \"outside\", type = \"area\"),\n                            @Member(id = \"1\", role = \"outside\", type = \"line\") }),\n                    @Relation(id = \"5\", tags = {\n                            \"type=inside_because_of_relation_inside\" }, members = {\n                                    @Member(id = \"1\", role = \"inside\", type = \"relation\"),\n                                    @Member(id = \"1\", role = \"outside\", type = \"line\") }),\n                    @Relation(id = \"4\", tags = {\n                            \"type=inside_because_of_level_2_relation_inside\" }, members = {\n                                    @Member(id = \"5\", role = \"inside\", type = \"relation\") })\n\n            })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/IsAtlasTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.io.InputStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * Test case for the isAtlas method\n *\n * @author cstaylor\n */\npublic class IsAtlasTestCase\n{\n    /**\n     * Fake Resource for testing the getName method\n     *\n     * @author cstaylor\n     */\n    private static final class NamedResource implements Resource\n    {\n        private final String name;\n\n        NamedResource(final String name)\n        {\n            this.name = name;\n        }\n\n        @Override\n        public String getName()\n        {\n            return this.name;\n        }\n\n        @Override\n        public long length()\n        {\n            return 0;\n        }\n\n        @Override\n        public InputStream read()\n        {\n            return null;\n        }\n    }\n\n    @Test\n    public void atlasCapsName()\n    {\n        Assert.assertFalse(\n                AtlasResourceLoader.HAS_ATLAS_EXTENSION.test(new NamedResource(\"somefile.ATLAS\")));\n    }\n\n    @Test\n    public void atlasCompressed()\n    {\n        Assert.assertTrue(AtlasResourceLoader.HAS_ATLAS_EXTENSION\n                .test(new NamedResource(\"somefile.atlas.gz\")));\n    }\n\n    @Test\n    public void atlasName()\n    {\n        Assert.assertTrue(\n                AtlasResourceLoader.HAS_ATLAS_EXTENSION.test(new NamedResource(\"somefile.atlas\")));\n    }\n\n    @Test\n    public void compressed()\n    {\n        Assert.assertFalse(\n                AtlasResourceLoader.HAS_ATLAS_EXTENSION.test(new NamedResource(\"somefile.gz\")));\n    }\n\n    @Test\n    public void nonAtlasName()\n    {\n        Assert.assertFalse(\n                AtlasResourceLoader.HAS_ATLAS_EXTENSION.test(new NamedResource(\"somefile.txt\")));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/ShardFileOverlapsPolygonTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.util.function.Predicate;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.sharding.DynamicTileSharding;\nimport org.openstreetmap.atlas.streaming.StringInputStream;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\n\n/**\n * Unit test for ShardFileOverlapsPolygon atlas resource predicate.\n *\n * @author rmegraw\n */\npublic class ShardFileOverlapsPolygonTest\n{\n    private static final DynamicTileSharding SHARDING_TREE = new DynamicTileSharding(\n            new File(ShardFileOverlapsPolygonTest.class\n                    .getResource(\n                            \"/org/openstreetmap/atlas/geography/boundary/tree-6-14-100000.txt.gz\")\n                    .getFile()));\n\n    private static final Polygon POLYGON = Rectangle.forCorners(\n            Location.forString(\"55.5868837,12.3541246\"), Location.forString(\"55.752623,12.71942\"));\n\n    private static final Predicate<Resource> PREDICATE = new ShardFileOverlapsPolygon(SHARDING_TREE,\n            POLYGON);\n\n    @Test\n    public void testBadRegex()\n    {\n        // variations on valid filename\n        final Predicate<Resource> myPredicate = new ShardFileOverlapsPolygon(SHARDING_TREE, POLYGON,\n                \"^.+_\\\\d{1,2}-\\\\d+-\\\\d+(\\\\.atlas)?(\\\\.gz)?$\");\n        Assert.assertFalse(\n                myPredicate.test(new File(\"/some/path/XYZ_11-1095-641.atlas.gz\", false)));\n        Assert.assertFalse(myPredicate.test(new File(\"/some/path/XYZ_11-1095-641.atlas\", false)));\n        Assert.assertFalse(myPredicate.test(new File(\"/some/path/XYZ_11-1095-641.gz\", false)));\n        Assert.assertFalse(myPredicate.test(new File(\"/some/path/XYZ_11-1095-641\", false)));\n    }\n\n    @Test\n    public void testFilenameFormat()\n    {\n        // no .gz extension should be ok\n        Assert.assertTrue(PREDICATE.test(new File(\"/some/path/XYZ_11-1095-641.atlas\", false)));\n\n        // some filenames that aren't formatted properly\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_11_1095_641.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_11_1095_641.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/11-1095-641.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_11-1095-641.atl.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_110-1095-641.atlas.gz\", false)));\n        Assert.assertFalse(\n                PREDICATE.test(new File(\"/some/path/XYZ_11-1095-641.atlas.gzip\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/foo\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"\", false)));\n\n        // these don't match the default regex, but should match with an alternative regex in\n        // another test\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_11-1095-641.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_11-1095-641\", false)));\n    }\n\n    @Test\n    public void testFilenameVariations()\n    {\n        // variations on valid filename\n        final Predicate<Resource> myPredicate = new ShardFileOverlapsPolygon(SHARDING_TREE, POLYGON,\n                \"^.+_(\\\\d{1,2}-\\\\d+-\\\\d+)(\\\\.atlas)?(\\\\.gz)?$\");\n        Assert.assertTrue(myPredicate.test(new File(\"/some/path/XYZ_11-1095-641.atlas.gz\", false)));\n        Assert.assertTrue(myPredicate.test(new File(\"XYZ_11-1095-641.atlas.gz\", false)));\n        Assert.assertTrue(myPredicate.test(new File(\"/some/path/XYZ_11-1095-641.atlas\", false)));\n        Assert.assertTrue(myPredicate.test(new File(\"/some/path/XYZ_11-1095-641.gz\", false)));\n        Assert.assertTrue(myPredicate.test(new File(\"/some/path/XYZ_11-1095-641\", false)));\n    }\n\n    @Test\n    public void testInputStreamResource()\n    {\n        // input stream resources require name to be set explicitly\n        Assert.assertTrue(\n                PREDICATE.test(new InputStreamResource(() -> new StringInputStream(\"foo bar\"))\n                        .withName(\"/some/path/XYZ_11-1095-641.atlas.gz\")));\n        Assert.assertFalse(\n                PREDICATE.test(new InputStreamResource(() -> new StringInputStream(\"foo bar\"))));\n    }\n\n    @Test\n    public void testShardsInBounds()\n    {\n        // shards that should overlap a polygon in Copehagen\n        Assert.assertTrue(PREDICATE.test(new File(\"/some/path/XYZ_11-1095-641.atlas.gz\", false)));\n        Assert.assertTrue(PREDICATE.test(new File(\"/some/path/XYZ_11-1094-641.atlas.gz\", false)));\n        Assert.assertTrue(PREDICATE.test(new File(\"/some/path/XYZ_11-1095-640.atlas.gz\", false)));\n        Assert.assertTrue(PREDICATE.test(new File(\"/some/path/XYZ_11-1094-640.atlas.gz\", false)));\n        Assert.assertTrue(PREDICATE.test(new File(\"/some/path/XYZ_10-548-320.atlas.gz\", false)));\n    }\n\n    @Test\n    public void testShardsOutOfBounds()\n    {\n        // shards that should not overlap a polygon in Copenhagen\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-546-319.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-547-319.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-548-319.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-546-320.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-546-321.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-547-321.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-548-321.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-546-321.atlas.gz\", false)));\n        Assert.assertFalse(PREDICATE.test(new File(\"/some/path/XYZ_10-548-321.atlas.gz\", false)));\n    }\n\n    @Test\n    public void testStringResource()\n    {\n        // string resources require name to be set explicitly\n        final String path = \"/some/path/XYZ_11-1095-641.atlas.gz\";\n        Assert.assertTrue(PREDICATE.test(new StringResource(path).withName(path)));\n        Assert.assertFalse(PREDICATE.test(new StringResource(path)));\n        Assert.assertFalse(PREDICATE.test(new StringResource(path).withName(\"foo\")));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/SubAtlasRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class SubAtlasRule extends CoreTestRule\n{\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n    private static final String THREE = \"37.780724, -122.472249\";\n    private static final String FOUR = \"37.780825, -122.471896\";\n    private static final String FIVE = \"37.780835, -122.471896\";\n    private static final String FIVE_PRIME = \"37.7807005, -122.4728703\";\n\n    private static final String SIX = \"37.045982,-121.7539795\";\n    private static final String SIX_PRIME = \"37.0459867,-121.7539853\";\n    private static final String SEVEN = \"37.0459913,-121.7539913\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FIVE_PRIME))\n\n            }, edges = {\n\n                    @Edge(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"fixme=please\" }),\n                    @Edge(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"2\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = FIVE_PRIME) }, tags = { \"highway=trunk\" })\n\n            }, areas = {\n\n                    @Area(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"addr:housenumber=25\" }),\n                    @Area(id = \"1\", coordinates = { @Loc(value = TWO), @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"natural=water\", \"water=lake\" })\n\n            }, lines = {\n\n                    @Line(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"railway=station\", \"FIXME=0\" }),\n                    @Line(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = FOUR) }, tags = { \"train=yes\",\n                                    \"public_transport=stop_position\" })\n\n            }, points = {\n\n                    @Point(id = \"0\", coordinates = @Loc(value = ONE), tags = { \"addr:street=coco\",\n                            \"addr:housenumber=25\" }),\n                    @Point(id = \"1\", coordinates = @Loc(value = TWO), tags = {\n                            \"fixme=wrong name\" }),\n                    @Point(id = \"2\", coordinates = @Loc(value = THREE), tags = { \"landuse=basin\" }),\n                    @Point(id = \"3\", coordinates = @Loc(value = FOUR), tags = { \"amenity=school\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=restriction\",\n                            \"restriction=no_left_turn\" }, members = {\n                                    @Member(id = \"0\", role = \"from\", type = \"edge\"),\n                                    @Member(id = \"2\", role = \"via\", type = \"node\"),\n                                    @Member(id = \"1\", role = \"to\", type = \"edge\") }),\n                    @Relation(id = \"2\", tags = { \"type=half_inside\" }, members = {\n                            @Member(id = \"0\", role = \"inside\", type = \"area\"),\n                            @Member(id = \"1\", role = \"outside\", type = \"line\") }),\n                    @Relation(id = \"3\", tags = { \"type=outside\" }, members = {\n                            @Member(id = \"1\", role = \"outside\", type = \"area\"),\n                            @Member(id = \"1\", role = \"outside\", type = \"line\") }),\n                    @Relation(id = \"4\", tags = {\n                            \"type=inside_because_of_relation_inside\" }, members = {\n                                    @Member(id = \"1\", role = \"inside\", type = \"relation\"),\n                                    @Member(id = \"1\", role = \"outside\", type = \"line\") }),\n                    @Relation(id = \"5\", tags = {\n                            \"type=inside_because_of_level_2_relation_inside\" }, members = {\n                                    @Member(id = \"4\", role = \"inside\", type = \"relation\") }),\n                    @Relation(id = \"6\", tags = { \"type=inside-fully\" }, members = {\n                            @Member(id = \"4\", role = \"inside\", type = \"node\"),\n                            @Member(id = \"2\", role = \"inside\", type = \"edge\") }),\n                    @Relation(id = \"7\", tags = { \"type=single-member-inside\" }, members = {\n                            @Member(id = \"0\", role = \"inside\", type = \"edge\"),\n                            @Member(id = \"2\", role = \"inside\", type = \"edge\") })\n\n            })\n    private Atlas atlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            }, edges = {\n\n                    @Edge(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"fixme=please\" }),\n                    @Edge(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" })\n\n            }, relations = {\n\n                    // Must have a node that wasn't indexed by the above edges.\n                    @Relation(id = \"0\", tags = { \"type=outside\" }, members = {\n                            @Member(id = \"1\", role = \"to\", type = \"node\"),\n                            @Member(id = \"4\", role = \"via\", type = \"node\") })\n\n            })\n    private Atlas nestedUnindexedNodeWithinRelationAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            }, edges = {\n\n                    @Edge(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"fixme=please\" }),\n                    @Edge(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" })\n\n            }, relations = {\n\n                    @Relation(id = \"0\", tags = { \"type=outside\" }, members = {\n                            @Member(id = \"3\", role = \"to\", type = \"node\") }),\n                    @Relation(id = \"1\", tags = { \"type=outside\" }, members = {\n                            @Member(id = \"0\", role = \"something\", type = \"relation\") }),\n                    // Must have a node that wasn't indexed by the above edges.\n                    @Relation(id = \"2\", tags = { \"type=outside\" }, members = {\n                            @Member(id = \"1\", role = \"to\", type = \"node\"),\n                            @Member(id = \"4\", role = \"via\", type = \"node\"),\n                            @Member(id = \"1\", role = \"something\", type = \"relation\") })\n\n            })\n\n    private Atlas nestedUnindexedRelationWithinRelationAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR), tags = { \"type=excluded\" }),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"type=excluded\" })\n\n            }, edges = {\n\n                    @Edge(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"fixme=please\" }),\n                    @Edge(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" })\n\n            }, relations = {\n\n                    @Relation(id = \"8\", tags = { \"type=excluded\" }, members = {\n                            @Member(id = \"1\", role = \"to\", type = \"node\"),\n                            @Member(id = \"2\", role = \"via\", type = \"node\") }),\n                    @Relation(id = \"9\", tags = { \"type=multipolygon\" }, members = {\n                            @Member(id = \"8\", role = \"something\", type = \"relation\"),\n                            @Member(id = \"1\", role = \"to\", type = \"node\"),\n                            @Member(id = \"4\", role = \"excluded\", type = \"node\") })\n\n            })\n    private Atlas filteredOutMemberRelationAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX)),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN)),\n\n            }, edges = {\n\n                    @Edge(id = \"12\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" }),\n\n                    @Edge(id = \"67\", coordinates = { @Loc(value = SIX), @Loc(value = SIX_PRIME),\n                            @Loc(value = SEVEN) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"-67\", coordinates = { @Loc(value = SEVEN), @Loc(value = SIX_PRIME),\n                            @Loc(value = SIX) }, tags = { \"highway=primary\" }),\n\n                    @Edge(id = \"76\", coordinates = { @Loc(value = SEVEN), @Loc(value = SIX_PRIME),\n                            @Loc(value = SIX) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"-76\", coordinates = { @Loc(value = SIX), @Loc(value = SIX_PRIME),\n                            @Loc(value = SEVEN) }, tags = { \"highway=primary\" })\n\n            })\n    private Atlas atlasWithEdgeAlongBoundary;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX)),\n\n            }, edges = {\n\n                    @Edge(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=residential\" }),\n                    @Edge(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=residential\" }),\n                    @Edge(id = \"2\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = FIVE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"3\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"highway=trunk\" })\n\n            }, points = {\n\n                    @Point(id = \"0\", coordinates = @Loc(value = ONE), tags = { \"addr:street=coco\",\n                            \"addr:housenumber=25\" }),\n                    @Point(id = \"1\", coordinates = @Loc(value = TWO), tags = {\n                            \"fixme=wrong name\" }),\n                    @Point(id = \"2\", coordinates = @Loc(value = THREE), tags = { \"landuse=basin\" }),\n                    @Point(id = \"3\", coordinates = @Loc(value = FOUR), tags = { \"amenity=school\" })\n\n            }, areas = {\n\n                    @Area(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"addr:housenumber=25\" }),\n                    @Area(id = \"1\", coordinates = { @Loc(value = TWO), @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"natural=water\", \"water=lake\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=restriction\",\n                            \"restriction=no_u_turn\" }, members = {\n                                    @Member(id = \"0\", role = \"from\", type = \"edge\"),\n                                    @Member(id = \"2\", role = \"via\", type = \"node\"),\n                                    @Member(id = \"1\", role = \"to\", type = \"edge\") }),\n                    @Relation(id = \"2\", tags = { \"type=route\", \"route=bus\" }, members = {\n                            @Member(id = \"2\", role = \"\", type = \"edge\"),\n                            @Member(id = \"3\", role = \"\", type = \"edge\") })\n\n            })\n    private Atlas hardCutPredicateAtlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    public Atlas getAtlasWithEdgeAlongBoundary()\n    {\n        return this.atlasWithEdgeAlongBoundary;\n    }\n\n    public Atlas getFilteredOutMemberRelationAtlas()\n    {\n        return this.filteredOutMemberRelationAtlas;\n    }\n\n    public Atlas getHardCutPredicateAtlas()\n    {\n        return this.hardCutPredicateAtlas;\n    }\n\n    public Atlas getNodeNestedWithinRelationAtlas()\n    {\n        return this.nestedUnindexedNodeWithinRelationAtlas;\n    }\n\n    public Atlas getRelationNestedWithinRelationAtlas()\n    {\n        return this.nestedUnindexedRelationWithinRelationAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/SubAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas;\n\nimport java.util.function.Predicate;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.delta.AtlasDelta;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n * @author mgostintsev\n */\npublic class SubAtlasTest\n{\n    @Rule\n    public final SubAtlasRule rule = new SubAtlasRule();\n\n    @Test\n    public void testFilteredOutSubRelation()\n    {\n        final Atlas source = this.rule.getFilteredOutMemberRelationAtlas();\n        final Predicate<AtlasEntity> filteredOutPredicate = entity -> !\"excluded\"\n                .equals(entity.getTag(\"type\").orElse(\"\"));\n        final Atlas filteredOutSubRelationAtlas = source\n                .subAtlas(filteredOutPredicate, AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        Assert.assertNull(filteredOutSubRelationAtlas.node(5));\n        Assert.assertNotNull(filteredOutSubRelationAtlas.node(3));\n\n        Assert.assertNotNull(filteredOutSubRelationAtlas.node(4));\n        Assert.assertNotNull(filteredOutSubRelationAtlas.relation(8));\n    }\n\n    @Test\n    public void testSubAtlasHardCutRelationsWithPolygon()\n    {\n        final Atlas source = this.rule.getAtlas();\n        // This Rectangle covers only the Node 1, Node 4, Edge 2, Point 0, and Relation 6 made up of\n        // Edge 4 and Node 2\n        final Atlas sub = source\n                .subAtlas(\n                        Rectangle.forCorners(Location.forString(\"37.780400, -122.473149\"),\n                                Location.forString(\"37.780785, -122.472631\")),\n                        AtlasCutType.HARD_CUT_RELATIONS_ONLY)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // The sub-atlas should be a combination of results from a soft-cut for all AtlasItems and a\n        // hard-cut for all Relations\n\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        Assert.assertNotNull(sub.node(1));\n        Assert.assertNotNull(source.node(2));\n        Assert.assertNotNull(sub.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNull(sub.node(3));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNotNull(sub.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNull(sub.edge(1));\n\n        // Areas\n        Assert.assertNotNull(source.area(0));\n        Assert.assertNotNull(sub.area(0));\n        Assert.assertNotNull(source.area(1));\n        Assert.assertNull(sub.area(1));\n\n        // Lines\n        Assert.assertNotNull(source.line(0));\n        Assert.assertNotNull(sub.line(0));\n        Assert.assertNotNull(source.line(1));\n        Assert.assertNull(sub.line(1));\n\n        // Points\n        Assert.assertNotNull(source.point(0));\n        Assert.assertNotNull(sub.point(0));\n        Assert.assertNotNull(source.point(1));\n        Assert.assertNull(sub.point(1));\n        Assert.assertNotNull(source.point(2));\n        Assert.assertNull(sub.point(2));\n        Assert.assertNotNull(source.point(3));\n        Assert.assertNull(sub.point(3));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertNull(sub.relation(1));\n        Assert.assertNotNull(source.relation(2));\n        Assert.assertNull(sub.relation(2));\n        Assert.assertNotNull(source.relation(3));\n        Assert.assertNull(sub.relation(3));\n        Assert.assertNotNull(source.relation(4));\n        Assert.assertNull(sub.relation(4));\n        Assert.assertNotNull(source.relation(5));\n        Assert.assertNull(sub.relation(5));\n        Assert.assertNotNull(source.relation(6));\n        Assert.assertEquals(2, source.relation(6).members().size());\n        Assert.assertNotNull(sub.relation(6));\n        Assert.assertEquals(2, sub.relation(6).members().size());\n        Assert.assertNotNull(source.relation(7));\n        Assert.assertEquals(2, source.relation(7).members().size());\n        Assert.assertNotNull(sub.relation(7));\n        Assert.assertEquals(1, sub.relation(7).members().size());\n    }\n\n    @Test\n    public void testSubAtlasHardCutWithPolygon()\n    {\n        final Atlas source = this.rule.getAtlas();\n        // This Rectangle covers only the Node 1, Node 4, Edge 2, Point 0, and Relation 6 made up of\n        // Edge 4 and Node 2\n        final Atlas sub = source\n                .subAtlas(\n                        Rectangle.forCorners(Location.forString(\"37.780400, -122.473149\"),\n                                Location.forString(\"37.780785, -122.472631\")),\n                        AtlasCutType.HARD_CUT_ALL)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        Assert.assertNotNull(sub.node(1));\n        Assert.assertNotNull(source.node(2));\n        Assert.assertNull(sub.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNull(sub.node(3));\n        Assert.assertNotNull(source.node(4));\n        Assert.assertNotNull(sub.node(4));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNull(sub.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNull(sub.edge(1));\n        Assert.assertNotNull(source.edge(2));\n        Assert.assertNotNull(sub.edge(2));\n\n        // Areas\n        Assert.assertNotNull(source.area(0));\n        Assert.assertNull(sub.area(0));\n        Assert.assertNotNull(source.area(1));\n        Assert.assertNull(sub.area(1));\n\n        // Lines\n        Assert.assertNotNull(source.line(0));\n        Assert.assertNull(sub.line(0));\n        Assert.assertNotNull(source.line(1));\n        Assert.assertNull(sub.line(1));\n\n        // Points\n        Assert.assertNotNull(source.point(0));\n        Assert.assertNotNull(sub.point(0));\n        Assert.assertNotNull(source.point(1));\n        Assert.assertNull(sub.point(1));\n        Assert.assertNotNull(source.point(2));\n        Assert.assertNull(sub.point(2));\n        Assert.assertNotNull(source.point(3));\n        Assert.assertNull(sub.point(3));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertNull(sub.relation(1));\n        Assert.assertNotNull(source.relation(2));\n        Assert.assertNull(sub.relation(2));\n        Assert.assertNotNull(source.relation(3));\n        Assert.assertNull(sub.relation(3));\n        Assert.assertNotNull(source.relation(4));\n        Assert.assertNull(sub.relation(4));\n        Assert.assertNotNull(source.relation(5));\n        Assert.assertNull(sub.relation(5));\n        Assert.assertNotNull(source.relation(6));\n        Assert.assertEquals(2, source.relation(6).members().size());\n        Assert.assertNotNull(sub.relation(6));\n        Assert.assertEquals(2, sub.relation(6).members().size());\n        Assert.assertNotNull(source.relation(7));\n        Assert.assertEquals(2, source.relation(7).members().size());\n        Assert.assertNotNull(sub.relation(7));\n        Assert.assertEquals(1, sub.relation(7).members().size());\n    }\n\n    @Test\n    public void testSubAtlasPredicateHardCut()\n    {\n        final Atlas source = this.rule.getHardCutPredicateAtlas();\n        final Predicate<AtlasEntity> filteredOutPredicate = entity -> entity instanceof Relation\n                || entity instanceof Node\n                || Validators.isOfType(entity, HighwayTag.class, HighwayTag.RESIDENTIAL);\n        final Atlas filtered = source.subAtlas(filteredOutPredicate, AtlasCutType.HARD_CUT_ALL)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // Verify counts\n        Assert.assertEquals(\"Two connected edges got filtered out, so there should be 3 less nodes\",\n                source.numberOfNodes() - 3, filtered.numberOfNodes());\n        Assert.assertEquals(\"Two non-residential edges got filtered out\",\n                source.numberOfEdges() - 2, filtered.numberOfEdges());\n        Assert.assertEquals(\"There should not be any areas left\", 0, filtered.numberOfAreas());\n        Assert.assertEquals(\"There should not be any points left\", 0, filtered.numberOfPoints());\n        Assert.assertEquals(\"One relation should have gotten removed due to empty members\", 1,\n                filtered.numberOfRelations());\n\n        // Verify filtered entities\n        Assert.assertNull(filtered.edge(2));\n        Assert.assertNull(filtered.edge(3));\n        Assert.assertNull(filtered.node(4));\n        Assert.assertNull(filtered.node(5));\n        Assert.assertNull(filtered.node(6));\n        Assert.assertNull(filtered.relation(2));\n        Assert.assertNotNull(filtered.relation(1));\n    }\n\n    @Test\n    public void testSubAtlasPredicateHardCutRelationsOnly()\n    {\n        final Atlas source = this.rule.getHardCutPredicateAtlas();\n        final Predicate<AtlasEntity> filteredOutPredicate = entity -> entity instanceof Relation\n                || entity instanceof Node\n                || Validators.isOfType(entity, HighwayTag.class, HighwayTag.RESIDENTIAL);\n        final Atlas filtered = source\n                .subAtlas(filteredOutPredicate, AtlasCutType.HARD_CUT_RELATIONS_ONLY)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // Verify counts\n        Assert.assertEquals(\"No nodes should be filtered out\", source.numberOfNodes(),\n                filtered.numberOfNodes());\n        Assert.assertEquals(\"Two non-residential edges got filtered out\",\n                source.numberOfEdges() - 2, filtered.numberOfEdges());\n        Assert.assertEquals(\"There should not be any areas left\", 0, filtered.numberOfAreas());\n        Assert.assertEquals(\"There should not be any points left\", 0, filtered.numberOfPoints());\n        Assert.assertEquals(\"One relation should have gotten removed due to empty members\", 1,\n                filtered.numberOfRelations());\n\n        // Verify filtered entities\n        Assert.assertNull(filtered.edge(2));\n        Assert.assertNull(filtered.edge(3));\n        Assert.assertNull(filtered.relation(2));\n        Assert.assertNotNull(filtered.relation(1));\n    }\n\n    @Test\n    public void testSubAtlasPredicateSilkCut()\n    {\n        final Atlas source = this.rule.getAtlas();\n\n        // Should return back all entities in this atlas\n        final Predicate<AtlasEntity> allEntities = entity -> true;\n\n        // Should return back only Entities with identifier 0\n        final Predicate<AtlasEntity> entitiesWithIdentifierZero = entity -> entity\n                .getIdentifier() == 0;\n\n        final Atlas identicalSubAtlas = source.subAtlas(allEntities, AtlasCutType.SILK_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        final Atlas subAtlasWithZeroBasedIdentifiers = source\n                .subAtlas(entitiesWithIdentifierZero, AtlasCutType.SILK_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        Assert.assertNotNull(identicalSubAtlas.node(1));\n        Assert.assertNotNull(source.node(2));\n        Assert.assertNotNull(identicalSubAtlas.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNotNull(identicalSubAtlas.node(3));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNotNull(identicalSubAtlas.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNotNull(identicalSubAtlas.edge(1));\n\n        // Areas\n        Assert.assertNotNull(source.area(0));\n        Assert.assertNotNull(identicalSubAtlas.area(0));\n        Assert.assertNotNull(source.area(1));\n        Assert.assertNotNull(identicalSubAtlas.area(1));\n\n        // Lines\n        Assert.assertNotNull(source.line(0));\n        Assert.assertNotNull(identicalSubAtlas.line(0));\n        Assert.assertNotNull(source.line(1));\n        Assert.assertNotNull(identicalSubAtlas.line(1));\n\n        // Points\n        Assert.assertNotNull(source.point(0));\n        Assert.assertNotNull(identicalSubAtlas.point(0));\n        Assert.assertNotNull(source.point(1));\n        Assert.assertNotNull(identicalSubAtlas.point(1));\n        Assert.assertNotNull(source.point(2));\n        Assert.assertNotNull(identicalSubAtlas.point(2));\n        Assert.assertNotNull(source.point(3));\n        Assert.assertNotNull(identicalSubAtlas.point(3));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertNotNull(identicalSubAtlas.relation(1));\n        Assert.assertNotNull(source.relation(2));\n        Assert.assertEquals(2, source.relation(2).members().size());\n        Assert.assertNotNull(identicalSubAtlas.relation(2));\n        Assert.assertEquals(2, identicalSubAtlas.relation(2).members().size());\n        Assert.assertNotNull(source.relation(3));\n        Assert.assertNotNull(identicalSubAtlas.relation(3));\n        Assert.assertNotNull(source.relation(4));\n        Assert.assertEquals(2, source.relation(4).members().size());\n        Assert.assertNotNull(identicalSubAtlas);\n        Assert.assertEquals(2, identicalSubAtlas.relation(4).members().size());\n        Assert.assertNotNull(source.relation(5));\n        Assert.assertEquals(1, source.relation(5).members().size());\n        Assert.assertNotNull(identicalSubAtlas.relation(5));\n        Assert.assertEquals(1, identicalSubAtlas.relation(5).members().size());\n\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        // Node 1 gets pulled in by Edge 0\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.node(1));\n        Assert.assertNotNull(source.node(2));\n        // Node 2 gets pulled in by Edge 0\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.node(3));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.edge(1));\n\n        // Areas\n        Assert.assertNotNull(source.area(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.area(0));\n        Assert.assertNotNull(source.area(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.area(1));\n\n        // Lines\n        Assert.assertNotNull(source.line(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.line(0));\n        Assert.assertNotNull(source.line(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.line(1));\n\n        // Points\n        Assert.assertNotNull(source.point(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.point(0));\n        // Point1 gets pulled in because it is part of the geometry of line0\n        Assert.assertNotNull(source.point(1));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.point(1));\n        Assert.assertNotNull(source.point(2));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.point(2));\n        Assert.assertNotNull(source.point(3));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.point(3));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(1));\n        Assert.assertNotNull(source.relation(2));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(2));\n        Assert.assertNotNull(source.relation(3));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(3));\n        Assert.assertNotNull(source.relation(4));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(4));\n        Assert.assertNotNull(source.relation(5));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(5));\n    }\n\n    @Test\n    public void testSubAtlasPredicateSoftCut()\n    {\n        final Atlas source = this.rule.getAtlas();\n\n        // Should return back all entities in this atlas\n        final Predicate<AtlasEntity> allEntities = entity -> true;\n\n        // Should return back only Entities with identifier 0\n        final Predicate<AtlasEntity> entitiesWithIdentifierZero = entity -> entity\n                .getIdentifier() == 0;\n\n        final Atlas identicalSubAtlas = source.subAtlas(allEntities, AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        final Atlas subAtlasWithZeroBasedIdentifiers = source\n                .subAtlas(entitiesWithIdentifierZero, AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        Assert.assertNotNull(identicalSubAtlas.node(1));\n        Assert.assertNotNull(source.node(2));\n        Assert.assertNotNull(identicalSubAtlas.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNotNull(identicalSubAtlas.node(3));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNotNull(identicalSubAtlas.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNotNull(identicalSubAtlas.edge(1));\n\n        // Areas\n        Assert.assertNotNull(source.area(0));\n        Assert.assertNotNull(identicalSubAtlas.area(0));\n        Assert.assertNotNull(source.area(1));\n        Assert.assertNotNull(identicalSubAtlas.area(1));\n\n        // Lines\n        Assert.assertNotNull(source.line(0));\n        Assert.assertNotNull(identicalSubAtlas.line(0));\n        Assert.assertNotNull(source.line(1));\n        Assert.assertNotNull(identicalSubAtlas.line(1));\n\n        // Points\n        Assert.assertNotNull(source.point(0));\n        Assert.assertNotNull(identicalSubAtlas.point(0));\n        Assert.assertNotNull(source.point(1));\n        Assert.assertNotNull(identicalSubAtlas.point(1));\n        Assert.assertNotNull(source.point(2));\n        Assert.assertNotNull(identicalSubAtlas.point(2));\n        Assert.assertNotNull(source.point(3));\n        Assert.assertNotNull(identicalSubAtlas.point(3));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertNotNull(identicalSubAtlas.relation(1));\n        Assert.assertNotNull(source.relation(2));\n        Assert.assertEquals(2, source.relation(2).members().size());\n        Assert.assertNotNull(identicalSubAtlas.relation(2));\n        Assert.assertEquals(2, identicalSubAtlas.relation(2).members().size());\n        Assert.assertNotNull(source.relation(3));\n        Assert.assertNotNull(identicalSubAtlas.relation(3));\n        Assert.assertNotNull(source.relation(4));\n        Assert.assertEquals(2, source.relation(4).members().size());\n        Assert.assertNotNull(identicalSubAtlas);\n        Assert.assertEquals(2, identicalSubAtlas.relation(4).members().size());\n        Assert.assertNotNull(source.relation(5));\n        Assert.assertEquals(1, source.relation(5).members().size());\n        Assert.assertNotNull(identicalSubAtlas.relation(5));\n        Assert.assertEquals(1, identicalSubAtlas.relation(5).members().size());\n\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        // Node 1 gets pulled in by Edge 0\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.node(1));\n        Assert.assertNotNull(source.node(2));\n        // Node 2 gets pulled in by Edge 0\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.node(3));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.edge(1));\n\n        // Areas\n        Assert.assertNotNull(source.area(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.area(0));\n        Assert.assertNotNull(source.area(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.area(1));\n\n        // Lines\n        Assert.assertNotNull(source.line(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.line(0));\n        Assert.assertNotNull(source.line(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.line(1));\n\n        // Points\n        Assert.assertNotNull(source.point(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.point(0));\n        Assert.assertNotNull(source.point(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.point(1));\n        Assert.assertNotNull(source.point(2));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.point(2));\n        Assert.assertNotNull(source.point(3));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.point(3));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(1));\n        Assert.assertNotNull(source.relation(2));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(2));\n        Assert.assertNotNull(source.relation(3));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(3));\n        Assert.assertNotNull(source.relation(4));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(4));\n        Assert.assertNotNull(source.relation(5));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.relation(5));\n    }\n\n    @Test\n    public void testSubAtlasSilkCutWithPolygon()\n    {\n        final Atlas source = this.rule.getAtlas();\n        // This Rectangle covers only the Node 1, Edge 0, Area 0, Line 0 and Point 0.\n        final Atlas sub = source\n                .subAtlas(\n                        Rectangle.forCorners(Location.forString(\"37.780400, -122.473149\"),\n                                Location.forString(\"37.780785, -122.472631\")),\n                        AtlasCutType.SILK_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        Assert.assertNotNull(sub.node(1));\n        Assert.assertNotNull(source.node(2));\n        Assert.assertNotNull(sub.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNull(sub.node(3));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNotNull(sub.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNull(sub.edge(1));\n\n        // Areas\n        Assert.assertNotNull(source.area(0));\n        Assert.assertNotNull(sub.area(0));\n        Assert.assertNotNull(source.area(1));\n        Assert.assertNull(sub.area(1));\n\n        // Lines\n        Assert.assertNotNull(source.line(0));\n        Assert.assertNotNull(sub.line(0));\n        Assert.assertNotNull(source.line(1));\n        Assert.assertNull(sub.line(1));\n\n        // Check that points for line coordinates were preserved\n        sub.lines().forEach(line ->\n        {\n            line.asPolyLine().forEach(location ->\n            {\n                source.pointsAt(location).forEach(point ->\n                {\n                    Assert.assertTrue(sub.point(point.getIdentifier()) != null);\n                });\n            });\n        });\n\n        // Points\n        Assert.assertNotNull(source.point(0));\n        Assert.assertNotNull(sub.point(0));\n        Assert.assertNotNull(source.point(1));\n        Assert.assertNotNull(sub.point(1));\n        Assert.assertNotNull(source.point(2));\n        Assert.assertNull(sub.point(2));\n        Assert.assertNotNull(source.point(3));\n        Assert.assertNull(sub.point(3));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertNotNull(sub.relation(1));\n        Assert.assertNotNull(source.relation(2));\n        Assert.assertEquals(2, source.relation(2).members().size());\n        Assert.assertNotNull(sub.relation(2));\n        Assert.assertEquals(1, sub.relation(2).members().size());\n        Assert.assertNotNull(source.relation(3));\n        Assert.assertNull(sub.relation(3));\n        Assert.assertNotNull(source.relation(4));\n        Assert.assertEquals(2, source.relation(4).members().size());\n        Assert.assertNotNull(sub.relation(4));\n        Assert.assertEquals(1, sub.relation(4).members().size());\n        Assert.assertNotNull(source.relation(5));\n        Assert.assertEquals(1, source.relation(5).members().size());\n        Assert.assertNotNull(sub.relation(5));\n        Assert.assertEquals(1, sub.relation(5).members().size());\n    }\n\n    @Test\n    public void testSubAtlasSoftCutWithMultiPolygon()\n    {\n        final Atlas source = this.rule.getAtlas();\n        final Rectangle rectangle1 = Rectangle.forCorners(\n                Location.forString(\"37.780400, -122.473149\"),\n                Location.forString(\"37.780785, -122.472631\"));\n        final Rectangle rectangle2 = Rectangle.forCorners(\n                Location.forString(\"37.780422500976194, -122.47218757867812\"),\n                Location.forString(\"37.781049995371575, -122.47145265340805\"));\n        final Atlas sub1 = source.subAtlas(rectangle1, AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n        final Atlas sub2 = source.subAtlas(rectangle2, AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // cut an atlas with a multipolygon of the two rectangles\n        final MultiPolygon bothRectangles = MultiPolygon.forOuters(rectangle1, rectangle2);\n        final Atlas subBoth = source.subAtlas(bothRectangles, AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // assert no differences between subAtlas with MultiPolygon and MultiAtlas of subAtlases\n        // with the Multipolygon's outer Polygons\n        final AtlasDelta delta = new AtlasDelta(subBoth, new MultiAtlas(sub1, sub2));\n        Assert.assertTrue(delta.getDifferences().isEmpty());\n    }\n\n    @Test\n    public void testSubAtlasSoftCutWithPolygon()\n    {\n        final Atlas source = this.rule.getAtlas();\n        // This Rectangle covers only the Node 1, Edge 0, Area 0, Line 0 and Point 0.\n        final Atlas sub = source\n                .subAtlas(\n                        Rectangle.forCorners(Location.forString(\"37.780400, -122.473149\"),\n                                Location.forString(\"37.780785, -122.472631\")),\n                        AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        Assert.assertNotNull(sub.node(1));\n        Assert.assertNotNull(source.node(2));\n        Assert.assertNotNull(sub.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNull(sub.node(3));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNotNull(sub.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNull(sub.edge(1));\n\n        // Areas\n        Assert.assertNotNull(source.area(0));\n        Assert.assertNotNull(sub.area(0));\n        Assert.assertNotNull(source.area(1));\n        Assert.assertNull(sub.area(1));\n\n        // Lines\n        Assert.assertNotNull(source.line(0));\n        Assert.assertNotNull(sub.line(0));\n        Assert.assertNotNull(source.line(1));\n        Assert.assertNull(sub.line(1));\n\n        // Points\n        Assert.assertNotNull(source.point(0));\n        Assert.assertNotNull(sub.point(0));\n        Assert.assertNotNull(source.point(1));\n        Assert.assertNull(sub.point(1));\n        Assert.assertNotNull(source.point(2));\n        Assert.assertNull(sub.point(2));\n        Assert.assertNotNull(source.point(3));\n        Assert.assertNull(sub.point(3));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertNotNull(sub.relation(1));\n        Assert.assertNotNull(source.relation(2));\n        Assert.assertEquals(2, source.relation(2).members().size());\n        Assert.assertNotNull(sub.relation(2));\n        Assert.assertEquals(1, sub.relation(2).members().size());\n        Assert.assertNotNull(source.relation(3));\n        Assert.assertNull(sub.relation(3));\n        Assert.assertNotNull(source.relation(4));\n        Assert.assertEquals(2, source.relation(4).members().size());\n        Assert.assertNotNull(sub.relation(4));\n        Assert.assertEquals(1, sub.relation(4).members().size());\n        Assert.assertNotNull(source.relation(5));\n        Assert.assertEquals(1, source.relation(5).members().size());\n        Assert.assertNotNull(sub.relation(5));\n        Assert.assertEquals(1, sub.relation(5).members().size());\n    }\n\n    @Test\n    public void testSubAtlasWithNodeNestedWithinRelationCase()\n    {\n        final Atlas source = this.rule.getNodeNestedWithinRelationAtlas();\n\n        final Predicate<AtlasEntity> entitiesWithIdentifierZero = entity -> entity\n                .getIdentifier() == 0;\n\n        final Atlas subAtlasWithZeroBasedIdentifiers = source\n                .subAtlas(entitiesWithIdentifierZero, AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.node(1));\n        Assert.assertNotNull(source.node(2));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.node(3));\n        Assert.assertNotNull(source.node(4));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.node(4));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNull(subAtlasWithZeroBasedIdentifiers.edge(1));\n\n        // Relations\n        Assert.assertNotNull(source.relation(0));\n        Assert.assertEquals(2, source.relation(0).members().size());\n        Assert.assertNotNull(subAtlasWithZeroBasedIdentifiers.relation(0));\n        Assert.assertEquals(2, subAtlasWithZeroBasedIdentifiers.relation(0).members().size());\n    }\n\n    @Test\n    public void testSubAtlasWithPolygonAndEdgeAtBoundary()\n    {\n        final Atlas source = this.rule.getAtlasWithEdgeAlongBoundary();\n        final Polygon boundary = Polygon\n                .wkt(\"POLYGON ((-121.7540269 37.0463639, -121.75403 37.04635, \"\n                        + \"-121.75408 37.0462, -121.75408 37.04611, -121.75406 37.04606, \"\n                        + \"-121.75399 37.04599, -121.75344 37.04557, -121.75338 37.0455, \"\n                        + \"-121.7533422 37.0454102, -121.7544982 37.0454102, \"\n                        + \"-121.7544982 37.0463639, -121.7540269 37.0463639))\");\n        final Atlas result = source.subAtlas(boundary, AtlasCutType.SOFT_CUT).get();\n        Assert.assertEquals(4, result.numberOfEdges());\n        // Does not clip with JTS\n        Assert.assertNotNull(result.edge(67));\n        // Does clip with JTS\n        Assert.assertNotNull(result.edge(-67));\n        // Does clip with JTS\n        Assert.assertNotNull(result.edge(76));\n        // Does not clip with JTS\n        Assert.assertNotNull(result.edge(-76));\n    }\n\n    @Test\n    public void testSubAtlasWithRelationNestedWithinRelationCase()\n    {\n        final Atlas source = this.rule.getRelationNestedWithinRelationAtlas();\n\n        final Predicate<AtlasEntity> entitiesWithIdentifierZero = entity -> entity\n                .getIdentifier() == 2;\n\n        final Atlas subAtlasWithTwoBasedIdentifiers = source\n                .subAtlas(entitiesWithIdentifierZero, AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"SubAtlas was not present.\"));\n\n        // Nodes\n        Assert.assertNotNull(source.node(1));\n        Assert.assertNotNull(subAtlasWithTwoBasedIdentifiers.node(1));\n        Assert.assertNotNull(source.node(2));\n        Assert.assertNotNull(subAtlasWithTwoBasedIdentifiers.node(2));\n        Assert.assertNotNull(source.node(3));\n        Assert.assertNotNull(subAtlasWithTwoBasedIdentifiers.node(3));\n        Assert.assertNotNull(source.node(4));\n        Assert.assertNotNull(subAtlasWithTwoBasedIdentifiers.node(4));\n\n        // Edges\n        Assert.assertNotNull(source.edge(0));\n        Assert.assertNull(subAtlasWithTwoBasedIdentifiers.edge(0));\n        Assert.assertNotNull(source.edge(1));\n        Assert.assertNull(subAtlasWithTwoBasedIdentifiers.edge(1));\n\n        // Relations\n        Assert.assertNotNull(source.relation(1));\n        Assert.assertEquals(1, source.relation(1).members().size());\n        Assert.assertNotNull(subAtlasWithTwoBasedIdentifiers.relation(1));\n        Assert.assertEquals(1, subAtlasWithTwoBasedIdentifiers.relation(1).members().size());\n        Assert.assertNotNull(subAtlasWithTwoBasedIdentifiers.relation(2));\n        Assert.assertEquals(3, subAtlasWithTwoBasedIdentifiers.relation(2).members().size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/builder/GeoJsonAtlasBuilderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class GeoJsonAtlasBuilderTest\n{\n    @Test\n    public void testCreation()\n    {\n        final StringResource resource = new StringResource(new InputStreamResource(\n                () -> GeoJsonAtlasBuilderTest.class.getResourceAsStream(\"overpass-turbo.geojson\")));\n        final Atlas created = new GeoJsonAtlasBuilder().create(resource);\n        System.out.println(created);\n        Assert.assertEquals(50, Iterables.size(created.edges()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/builder/PackedAtlasBuilderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.exception.AtlasIntegrityException;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class PackedAtlasBuilderTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(PackedAtlasBuilderTest.class);\n\n    @Test\n    public void testManyVeryCloseNodes()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addNode(1, Location.forString(\"48.3406719,10.5563445\"), Maps.hashMap());\n        builder.addNode(2, Location.forString(\"48.34204,10.55844\"), Maps.hashMap());\n        builder.addNode(3, Location.forString(\"48.3406720,10.5563445\"), Maps.hashMap());\n        builder.addNode(4, Location.forString(\"48.3406719,10.5563446\"), Maps.hashMap());\n        // First node is slightly off.\n        builder.addEdge(5, PolyLine.wkt(\"LINESTRING (10.5563444 48.3406717, 10.55844 48.34204)\"),\n                Maps.hashMap(HighwayTag.KEY, HighwayTag.RESIDENTIAL.getTagValue()));\n        final Atlas result = builder.get();\n        logger.info(\"{}\", result);\n        Assert.assertNotNull(result);\n    }\n\n    @Test(expected = AtlasIntegrityException.class)\n    public void testNotSoCloseNode()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addNode(1, Location.forString(\"48.3406719,10.5563445\"), Maps.hashMap());\n        builder.addNode(2, Location.forString(\"48.34204,10.55844\"), Maps.hashMap());\n        // First node is too off.\n        builder.addEdge(3, PolyLine.wkt(\"LINESTRING (10.5563430 48.3406710, 10.55844 48.34204)\"),\n                Maps.hashMap(HighwayTag.KEY, HighwayTag.RESIDENTIAL.getTagValue()));\n        final Atlas result = builder.get();\n        logger.info(\"{}\", result);\n        Assert.assertNotNull(result);\n    }\n\n    @Test\n    public void testVeryCloseNode()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addNode(1, Location.forString(\"48.3406719,10.5563445\"), Maps.hashMap());\n        builder.addNode(2, Location.forString(\"48.34204,10.55844\"), Maps.hashMap());\n        // First node is slightly off.\n        builder.addEdge(3, PolyLine.wkt(\"LINESTRING (10.5563444 48.3406717, 10.55844 48.34204)\"),\n                Maps.hashMap(HighwayTag.KEY, HighwayTag.RESIDENTIAL.getTagValue()));\n        final Atlas result = builder.get();\n        logger.info(\"{}\", result);\n        Assert.assertNotNull(result);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveObjectStoreTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.packed.RandomPackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.packed.RandomPackedAtlasBuilder.AtlasStartIdentifiers;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * @author tony\n */\npublic class AtlasPrimitiveObjectStoreTest\n{\n    private static final Map<String, String> EMPTY = new HashMap<>();\n\n    @Test\n    public void testBuild()\n    {\n        final AtlasPrimitiveObjectStore store = new AtlasPrimitiveObjectStore();\n\n        store.addNode(new AtlasPrimitiveLocationItem(1, Location.TEST_6, EMPTY));\n        store.addNode(new AtlasPrimitiveLocationItem(2, Location.TEST_5, EMPTY));\n        store.addEdge(new AtlasPrimitiveLineItem(11, new Segment(Location.TEST_6, Location.TEST_5),\n                EMPTY));\n        store.addEdge(new AtlasPrimitiveLineItem(-11, new Segment(Location.TEST_5, Location.TEST_6),\n                EMPTY));\n\n        final Atlas atlas = store.build();\n        Assert.assertEquals(2, atlas.numberOfEdges());\n        Assert.assertEquals(2, atlas.numberOfNodes());\n    }\n\n    @Test\n    public void testCreation()\n    {\n        final Atlas source = createSource();\n\n        final AtlasPrimitiveObjectStore store = new AtlasPrimitiveObjectStore();\n        source.nodes().forEach(node -> store.addNode(convert(node)));\n        source.points().forEach(point -> store.addPoint(convert(point)));\n        source.edges().forEach(edge -> store.addEdge(convert(edge)));\n        source.lines().forEach(line -> store.addLine(convert(line)));\n        source.areas().forEach(area -> store.addArea(convert(area)));\n        source.relations().forEach(relation -> store.addRelation(convert(relation)));\n\n        final Atlas copy = store.build();\n        Assert.assertEquals(source.numberOfAreas(), copy.numberOfAreas());\n        Assert.assertEquals(source.numberOfEdges(), copy.numberOfEdges());\n        Assert.assertEquals(source.numberOfNodes(), copy.numberOfNodes());\n        Assert.assertEquals(source.numberOfPoints(), copy.numberOfPoints());\n        Assert.assertEquals(source.numberOfLines(), copy.numberOfLines());\n        Assert.assertEquals(source.numberOfRelations(), copy.numberOfRelations());\n    }\n\n    @Test\n    public void testIntegrity()\n    {\n        final AtlasPrimitiveObjectStore store = new AtlasPrimitiveObjectStore();\n\n        store.addNode(new AtlasPrimitiveLocationItem(1, Location.TEST_6, EMPTY));\n        store.addNode(new AtlasPrimitiveLocationItem(2, Location.TEST_5, EMPTY));\n        store.addEdge(new AtlasPrimitiveLineItem(11, new Segment(Location.TEST_6, Location.TEST_5),\n                EMPTY));\n\n        Assert.assertFalse(store.checkDataIntegrity().isPresent());\n\n        // Add an edge reference to a not existed node\n        store.addEdge(new AtlasPrimitiveLineItem(12, new Segment(Location.TEST_1, Location.TEST_6),\n                EMPTY));\n        final Optional<TemporaryObjectStore> missingObjects = store.checkDataIntegrity();\n        Assert.assertTrue(missingObjects.isPresent());\n        Assert.assertEquals(Location.TEST_1, missingObjects.get().getLocations().iterator().next());\n        Assert.assertEquals(1, missingObjects.get().size());\n    }\n\n    private AtlasPrimitiveArea convert(final Area area)\n    {\n        return new AtlasPrimitiveArea(area.getIdentifier(), area.asPolygon(), area.getTags());\n    }\n\n    private AtlasPrimitiveLineItem convert(final LineItem item)\n    {\n        return new AtlasPrimitiveLineItem(item.getIdentifier(), item.asPolyLine(), item.getTags());\n    }\n\n    private AtlasPrimitiveLocationItem convert(final LocationItem item)\n    {\n        return new AtlasPrimitiveLocationItem(item.getIdentifier(), item.getLocation(),\n                item.getTags());\n    }\n\n    private AtlasPrimitiveRelation convert(final Relation relation)\n    {\n        final RelationBean bean = new RelationBean();\n        for (final RelationMember member : relation.members())\n        {\n            bean.addItem(member.getEntity().getIdentifier(), member.getRole(),\n                    member.getEntity().getType());\n        }\n        return new AtlasPrimitiveRelation(relation.getIdentifier(), relation.getIdentifier(), bean,\n                relation.getTags(), relation.bounds());\n    }\n\n    private Atlas createSource()\n    {\n        final Rectangle bounds = Location.TEST_1.boxAround(Distance.TEN_MILES);\n        final AtlasSize estimates = new AtlasSize(8, 10, 4, 7, 9, 2);\n        final long startIdentifier = 100;\n        final AtlasStartIdentifiers startIdentifiers = new AtlasStartIdentifiers(\n                startIdentifier + 100, startIdentifier + 200, startIdentifier + 300,\n                startIdentifier + 400, startIdentifier + 500, startIdentifier + 600);\n        return new RandomPackedAtlasBuilder().generate(estimates, startIdentifiers, bounds);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/builder/store/AtlasPrimitiveRouteTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.store;\n\nimport static org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveRoute.ROUTE_SIZE_COMPARATOR;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\n\n/**\n * @author Sid\n */\npublic class AtlasPrimitiveRouteTest\n{\n    @Test\n    public void testComparator()\n    {\n        final PolyLine polyline1 = new PolyLine(Location.CROSSING_85_280, Location.TEST_1);\n        final PolyLine polyline2 = new PolyLine(Location.TEST_1, Location.TEST_7);\n        final PolyLine polyline3 = new PolyLine(Location.TEST_7, Location.TEST_6);\n\n        final AtlasPrimitiveEdge edge1 = new AtlasPrimitiveEdge(1, polyline1, new HashMap<>());\n        final AtlasPrimitiveEdge edge2 = new AtlasPrimitiveEdge(2, polyline2, new HashMap<>());\n        final AtlasPrimitiveEdge edge3 = new AtlasPrimitiveEdge(3, polyline3, new HashMap<>());\n\n        final AtlasPrimitiveRoute routeOne = new AtlasPrimitiveRoute(Arrays.asList(edge1));\n        final AtlasPrimitiveRoute routeTwo = new AtlasPrimitiveRoute(Arrays.asList(edge1, edge2));\n        final AtlasPrimitiveRoute routeThree = new AtlasPrimitiveRoute(\n                Arrays.asList(edge1, edge2, edge3));\n\n        Assert.assertEquals(\"Compare same route should be 0\", 0,\n                ROUTE_SIZE_COMPARATOR.compare(routeOne, routeOne));\n        Assert.assertEquals(\"Compare same route should be 0\", 0,\n                ROUTE_SIZE_COMPARATOR.compare(routeTwo, routeTwo));\n        Assert.assertEquals(\"Compare same route should be 0\", 0,\n                ROUTE_SIZE_COMPARATOR.compare(routeThree, routeThree));\n\n        Assert.assertEquals(\"Compare longer to shorter route should be -1\", -1,\n                ROUTE_SIZE_COMPARATOR.compare(routeThree, routeTwo));\n        Assert.assertEquals(\"Compare shorter to longer route should be 1\", 1,\n                ROUTE_SIZE_COMPARATOR.compare(routeTwo, routeThree));\n\n        final Set<AtlasPrimitiveRoute> atlasPrimitiveRoutes = new TreeSet<>(ROUTE_SIZE_COMPARATOR);\n        atlasPrimitiveRoutes.add(routeOne);\n        atlasPrimitiveRoutes.add(routeTwo);\n        atlasPrimitiveRoutes.add(routeThree);\n\n        int size = Integer.MAX_VALUE;\n        for (final AtlasPrimitiveRoute route : atlasPrimitiveRoutes)\n        {\n            Assert.assertTrue(\"Set is sorted in descending order\", size >= route.size());\n            size = route.size();\n        }\n    }\n\n    @Test\n    public void testOverlap()\n    {\n        // We dont need anything more than ids for test edges\n        final AtlasPrimitiveEdge edge1 = new AtlasPrimitiveEdge(1, null, new HashMap<>());\n        final AtlasPrimitiveEdge edge2 = new AtlasPrimitiveEdge(2, null, new HashMap<>());\n        final AtlasPrimitiveEdge edge3 = new AtlasPrimitiveEdge(3, null, new HashMap<>());\n        final AtlasPrimitiveEdge edge4 = new AtlasPrimitiveEdge(4, null, new HashMap<>());\n        final AtlasPrimitiveEdge edge5 = new AtlasPrimitiveEdge(5, null, new HashMap<>());\n        final AtlasPrimitiveEdge edge6 = new AtlasPrimitiveEdge(6, null, new HashMap<>());\n\n        final List<AtlasPrimitiveEdge> routeList = new ArrayList<>();\n        routeList.add(edge1);\n        routeList.add(edge2);\n        routeList.add(edge3);\n        routeList.add(edge4);\n        routeList.add(edge2);\n        routeList.add(edge3);\n        routeList.add(edge4);\n        routeList.add(edge5);\n\n        final AtlasPrimitiveRoute route = new AtlasPrimitiveRoute(routeList);\n\n        // First test - Multiple counts\n        final List<AtlasPrimitiveEdge> subRouteList = new ArrayList<>();\n        subRouteList.add(edge2);\n        subRouteList.add(edge3);\n        Assert.assertEquals(\"The number of overlaps for Multiple overlap subroute\", 2,\n                route.overlapCount(new AtlasPrimitiveRoute(subRouteList)));\n\n        // Second test - Longer sub route\n        subRouteList.clear();\n        subRouteList.add(edge2);\n        subRouteList.add(edge3);\n        subRouteList.add(edge4);\n        subRouteList.add(edge5);\n        Assert.assertEquals(\"The number of overlaps for Longer subroute\", 1,\n                route.overlapCount(new AtlasPrimitiveRoute(subRouteList)));\n\n        // Third test - empty\n        subRouteList.clear();\n        Assert.assertEquals(\"The number of overlaps for empty subroute\", 0,\n                route.overlapCount(new AtlasPrimitiveRoute(subRouteList)));\n\n        // Fourth test - Non existent\n        subRouteList.clear();\n        subRouteList.add(edge4);\n        subRouteList.add(edge5);\n        subRouteList.add(edge6);\n        Assert.assertEquals(\"The number of overlaps for Non existent subroute\", 0,\n                route.overlapCount(new AtlasPrimitiveRoute(subRouteList)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/builder/text/TextAtlasBuilderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.builder.text;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.packed.RandomPackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class TextAtlasBuilderTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(TextAtlasBuilderTest.class);\n\n    private final TextAtlasBuilder textAtlasBuilder = new TextAtlasBuilder();\n\n    @Test\n    public void testDataIntegrity()\n    {\n        final Atlas atlas = RandomPackedAtlasBuilder.generate(2, 0);\n        final StringResource resource = new StringResource();\n        this.textAtlasBuilder.write(atlas, resource);\n        resource.lines().forEach(System.out::println);\n        final Atlas read = this.textAtlasBuilder.read(resource);\n        Assert.assertEquals(atlas, read);\n    }\n\n    @Test\n    public void testFunkyTags()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addArea(1, Polygon.SILICON_VALLEY,\n                Maps.hashMap(\"key\", \"line1\" + System.lineSeparator() + \"line2\"));\n        builder.addArea(2, Polygon.SILICON_VALLEY, Maps.hashMap(\"key && key\", \"value\"));\n        final Atlas atlas = builder.get();\n\n        final WritableResource resource = new StringResource();\n        final TextAtlasBuilder textAtlasBuilder = new TextAtlasBuilder();\n        textAtlasBuilder.write(atlas, resource);\n\n        final Atlas read = textAtlasBuilder.read(resource);\n        logger.info(\"{}\", read.size());\n        Assert.assertEquals(2, read.numberOfAreas());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/AbstractChangeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.Rule;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Yazad Khambata\n */\npublic abstract class AbstractChangeTest\n{\n    protected static final Logger log = LoggerFactory.getLogger(AtlasResourceLoader.class);\n\n    public static final long TEST_IDENTIFIER = 123L;\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    protected Change newChangeWith2Areas()\n    {\n        final String key1 = \"key\";\n        final String value1 = \"value\";\n\n        return newChangeWith2Areas(TEST_IDENTIFIER, TEST_IDENTIFIER, key1, value1);\n    }\n\n    protected Change newChangeWith2Areas(final long identifier1, final long identifier2,\n            final String key1, final String value1)\n    {\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, new CompleteArea(\n                identifier1, Polygon.TEST_BUILDING, Maps.hashMap(key1, value1), null));\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(identifier2, Polygon.TEST_BUILDING, null, null));\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(featureChange1);\n        builder.add(featureChange2);\n        return builder.get();\n    }\n\n    protected Change newChangeWithAreaAndLine()\n    {\n        final long identifier1 = TEST_IDENTIFIER;\n        final long identifier2 = TEST_IDENTIFIER;\n        return newChangeWithAreaAndLine(identifier1, identifier2);\n    }\n\n    protected Change newChangeWithAreaAndLine(final long identifier1, final long identifier2)\n    {\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(identifier1, Polygon.TEST_BUILDING, Maps.hashMap(), null));\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.REMOVE,\n                new CompleteLine(identifier2, Polygon.TEST_BUILDING, null, null));\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(featureChange1);\n        builder.add(featureChange2);\n        return builder.get();\n    }\n\n    protected Change newChangeWithRelationMemberSet1()\n    {\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, new CompleteRelation(\n                TEST_IDENTIFIER, Maps.hashMap(), Rectangle.forLocations(Location.COLOSSEUM),\n                RelationBean.fromSet(\n                        Set.of(new RelationBean.RelationBeanItem(124L, \"outer\", ItemType.EDGE))),\n                List.of(), null, 124000000L, null));\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(featureChange1);\n        return builder.get();\n    }\n\n    protected Change newChangeWithRelationMemberSet2()\n    {\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(TEST_IDENTIFIER, Maps.hashMap(),\n                        Rectangle.forLocations(Location.COLOSSEUM),\n                        RelationBean.fromSet(Set.of(\n                                new RelationBean.RelationBeanItem(124L, \"outer\", ItemType.EDGE),\n                                new RelationBean.RelationBeanItem(125L, \"inner\", ItemType.LINE))),\n                        List.of(), null, 124000000L, null));\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(featureChange1);\n        return builder.get();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/AtlasChangeGeneratorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class AtlasChangeGeneratorTest\n{\n    public static final String HIGHWAY = \"highway\";\n\n    @Rule\n    public final AtlasChangeGeneratorTestRule rule = new AtlasChangeGeneratorTestRule();\n\n    @Test\n    public void testAddLocation()\n    {\n        final Atlas source = this.rule.getNodeBoundsExpansionAtlas();\n        final Set<FeatureChange> result = new HashSet<>();\n        for (final Node node : source.nodes())\n        {\n            final CompleteNode completeNode = CompleteNode.shallowFrom(node)\n                    .withLocation(node.getLocation()).withTags(node.getTags());\n            result.add(FeatureChange.add(completeNode));\n        }\n        final Set<FeatureChange> changes = new FeatureChangeBoundsExpander(result, source).apply();\n        for (final FeatureChange featureChange : changes)\n        {\n            if (featureChange.getIdentifier() == 177633000000L)\n            {\n                Assert.assertEquals(Location.forWkt(\"POINT (4.2194855 38.8231656)\"),\n                        ((CompleteNode) featureChange.getAfterView()).getLocation());\n                Assert.assertEquals(\n                        \"POLYGON ((4.2177433 38.8228217, 4.2177433 38.8235147, 4.2197697 38.8235147,\"\n                                + \" 4.2197697 38.8228217, 4.2177433 38.8228217))\",\n                        featureChange.bounds().toWkt());\n            }\n        }\n    }\n\n    @Test\n    public void testEmptyChange()\n    {\n        final AtlasChangeGenerator generator = atlas -> new HashSet<>();\n        final Atlas source = this.rule.getNodeBoundsExpansionAtlas();\n        Assert.assertTrue(generator.apply(source).isEmpty());\n    }\n\n    @Test\n    public void testExpandNode()\n    {\n        final Atlas source = this.rule.getNodeBoundsExpansionAtlas();\n\n        final Set<FeatureChange> result = new HashSet<>();\n        final String key = \"changed\";\n        final String value = \"yes\";\n        for (final AtlasEntity entity : source)\n        {\n            final CompleteEntity completeEntity = ((CompleteEntity) CompleteEntity.from(entity))\n                    .withAddedTag(key, value);\n            result.add(FeatureChange.add((AtlasEntity) completeEntity));\n        }\n        // bonus!\n        result.add(FeatureChange.add(new CompleteEdge(123L, PolyLine.wkt(\n                \"LINESTRING (4.2194855 38.8231656, 4.2202479 38.8233871, 4.2200000 38.8235147)\"),\n                Maps.hashMap(HIGHWAY, \"primary\"), 177633000000L, 456L, Sets.hashSet())));\n        result.add(FeatureChange.add(new CompleteNode(456L,\n                Location.forWkt(\"POINT (4.2200000 38.8235147)\"), Maps.hashMap(HIGHWAY, \"primary\"),\n                Sets.treeSet(), Sets.treeSet(123L), Sets.hashSet())));\n        final Set<FeatureChange> changes = new FeatureChangeBoundsExpander(result, source).apply();\n        for (final FeatureChange featureChange : changes)\n        {\n            if (featureChange.getIdentifier() == 177633000000L)\n            {\n                Assert.assertEquals(\n                        \"POLYGON ((4.2177433 38.8228217, 4.2177433 38.8235147, 4.2202479 38.8235147,\"\n                                + \" 4.2202479 38.8228217, 4.2177433 38.8228217))\",\n                        featureChange.bounds().toWkt());\n            }\n        }\n    }\n\n    @Test\n    public void testExpandRelation()\n    {\n        final Atlas source = this.rule.getNodeBoundsExpansionAtlas();\n        final Set<FeatureChange> result = new HashSet<>();\n        result.add(FeatureChange.add(CompleteEdge.from(source.edge(177630000000L))));\n        final Set<FeatureChange> changes = new FeatureChangeBoundsExpander(result, source).apply();\n        for (final FeatureChange featureChange : changes)\n        {\n            if (featureChange.getIdentifier() == 177763000000L)\n            {\n                Assert.assertEquals(\n                        \"POLYGON ((4.2177433 38.8228217, 4.2177433 38.8235147, 4.2197697 38.8235147,\"\n                                + \" 4.2197697 38.8228217, 4.2177433 38.8228217))\",\n                        featureChange.bounds().toWkt());\n            }\n        }\n    }\n\n    @Test\n    public void testValidBeforeView()\n    {\n        final Atlas source = this.rule.getNodeBoundsExpansionAtlas();\n        final AtlasChangeGenerator generator = atlas -> Sets.hashSet(FeatureChange.add(CompleteNode\n                .shallowFrom(source.node(177628000000L)).withAddedTag(HIGHWAY, \"traffic_signals\")));\n        Assert.assertNotNull(generator.apply(source).iterator().next().getBeforeView());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/AtlasChangeGeneratorTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class AtlasChangeGeneratorTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"nodeBoundsExpansionAtlas.josm.osm\")\n    private Atlas nodeBoundsExpansionAtlas;\n\n    public Atlas getNodeBoundsExpansionAtlas()\n    {\n        return this.nodeBoundsExpansionAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/CascadeDeleteTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * @author Yazad Khambata\n */\npublic class CascadeDeleteTest\n{\n    @Rule\n    public final CascadeDeleteTestRule rule = new CascadeDeleteTestRule();\n\n    private final CascadeDeleteTestHelper helper = new CascadeDeleteTestHelper(this.rule);\n\n    @Test\n    public void deleteEdgeStartNode()\n    {\n        final Atlas atlas = this.helper.getAtlas();\n        final long nodeIdToDelete = CascadeDeleteTestRule.START_END_EDGE_NODE;\n        final long nodeRelationId = CascadeDeleteTestRule.NON_EDGE_NODE_RELATION_IDENTIFIER;\n        final Atlas changeAtlas = this.helper.deleteNode(atlas, nodeIdToDelete, nodeRelationId);\n        this.helper.verifyCounts(changeAtlas, this.helper.override(ItemType.NODE, -1),\n                this.helper.override(ItemType.EDGE, -2));\n    }\n\n    @Test\n    public void deleteRelation()\n    {\n        final long entityIdentifier = CascadeDeleteTestRule.TOP_LEVEL_RELATION_IDENTIFIER;\n        final Atlas atlas = this.helper.getAtlas();\n        final Atlas changeAtlas = this.helper.deleteSimpleRelation(entityIdentifier, atlas);\n        this.helper.verifyCounts(changeAtlas, this.helper.override(ItemType.RELATION, -1));\n    }\n\n    @Test\n    public void deleteUnrelatedNode()\n    {\n        final Atlas atlas = this.helper.getAtlas();\n        final long nodeIdToDelete = CascadeDeleteTestRule.NON_EDGE_NODE_IDENTIFIER;\n        final long nodeRelationId = CascadeDeleteTestRule.NON_EDGE_NODE_RELATION_IDENTIFIER;\n        final Atlas changeAtlas = this.helper.deleteNode(atlas, nodeIdToDelete, nodeRelationId);\n        this.helper.verifyCounts(changeAtlas, this.helper.override(ItemType.NODE, -1));\n    }\n\n    @Test\n    public void testAutoDeleteEmptyRelations()\n    {\n        final ItemType itemType = ItemType.POINT;\n        final long entityIdentifier = CascadeDeleteTestRule.THE_ONLY_RELATION_MEMBER_POINT_IDENTIFIER;\n        final long entityRelationIdentifier = CascadeDeleteTestRule.ONE_MEMBER_RELATION_IDENTIFIER;\n        this.helper.testDeleteSimple(itemType, entityIdentifier, entityRelationIdentifier, true);\n    }\n\n    @Test\n    public void testDeleteAreaToRelationMemberCascade()\n    {\n        final ItemType itemType = ItemType.AREA;\n        final long entityIdentifier = CascadeDeleteTestRule.AREA_IDENTIFIER;\n        final long entityRelationIdentifier = CascadeDeleteTestRule.AREA_RELATION_IDENTIFIER;\n\n        this.helper.testDeleteSimple(itemType, entityIdentifier, entityRelationIdentifier);\n    }\n\n    @Test\n    public void testDeleteForwardEdgeToRelationMemberCascadeButNotReverseEdge()\n    {\n        final long edgeIdentifier = CascadeDeleteTestRule.EDGE_IDENTIFIER;\n        this.helper.deleteEdgeButNotReverse(edgeIdentifier,\n                CascadeDeleteTestRule.EDGE_RELATION_IDENTIFIER);\n    }\n\n    @Test\n    public void testDeleteLineToRelationMemberCascade()\n    {\n        final ItemType itemType = ItemType.LINE;\n        final long entityIdentifier = CascadeDeleteTestRule.LINE_IDENTIFIER;\n        final long entityRelationIdentifier = CascadeDeleteTestRule.LINE_RELATION_IDENTIFIER;\n\n        this.helper.testDeleteSimple(itemType, entityIdentifier, entityRelationIdentifier);\n    }\n\n    @Test\n    public void testDeletePointToRelationMemberCascade()\n    {\n        final ItemType itemType = ItemType.POINT;\n        final long entityIdentifier = CascadeDeleteTestRule.POINT_IDENTIFIER;\n        final long entityRelationIdentifier = CascadeDeleteTestRule.POINT_RELATION_IDENTIFIER;\n\n        this.helper.testDeleteSimple(itemType, entityIdentifier, entityRelationIdentifier);\n    }\n\n    @Test\n    public void testDeleteReverseEdgeToRelationMemberCascadeButNotForwardEdge()\n    {\n        final long edgeIdentifier = -(CascadeDeleteTestRule.EDGE_IDENTIFIER);\n        this.helper.deleteEdgeButNotReverse(edgeIdentifier,\n                CascadeDeleteTestRule.EDGE_RELATION_IDENTIFIER);\n    }\n\n    @Test\n    public void testDeleteSubRelation()\n    {\n        final ItemType itemType = ItemType.RELATION;\n        final long entityIdentifier = CascadeDeleteTestRule.SUB_RELATION_IDENTIFIER;\n        final long entityRelationIdentifier = CascadeDeleteTestRule.PARENT_RELATION_IDENTIFIER;\n        this.helper.testDeleteSimple(itemType, entityIdentifier, entityRelationIdentifier);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/CascadeDeleteTestHelper.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.junit.Assert;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteItemType;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ConnectedEdgeType;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\n\n/**\n * @author Yazad Khambata\n */\nclass CascadeDeleteTestHelper\n{\n    private final CascadeDeleteTestRule rule;\n\n    protected static <E extends AtlasEntity> long count(final Iterable<E> entities)\n    {\n        return StreamSupport.stream(entities.spliterator(), false).count();\n    }\n\n    CascadeDeleteTestHelper(final CascadeDeleteTestRule rule)\n    {\n        this.rule = rule;\n    }\n\n    protected void deleteEdgeButNotReverse(final long edgeIdentifier, final long relationIdentifier)\n    {\n        final long reverseIdentifier = -edgeIdentifier;\n\n        final Atlas atlas = getAtlas();\n        final long countBefore = count(atlas.edges());\n        Assert.assertTrue(countBefore > 2);\n\n        final Edge edge = atlas.edge(edgeIdentifier);\n        Assert.assertNotNull(edge);\n        final Edge negativeEdge = atlas.edge(reverseIdentifier);\n        Assert.assertNotNull(negativeEdge);\n\n        final Node startForward = edge.start();\n        final Node endForward = edge.end();\n\n        final Node startReverse = negativeEdge.start();\n        final Node endReverse = negativeEdge.end();\n\n        Assert.assertEquals(startForward, endReverse);\n        Assert.assertEquals(endForward, startReverse);\n\n        Assert.assertTrue(isEdgeRelatedToNode(startForward, edgeIdentifier, ConnectedEdgeType.OUT));\n        Assert.assertTrue(isEdgeRelatedToNode(endForward, edgeIdentifier, ConnectedEdgeType.IN));\n        Assert.assertTrue(isEdgeRelatedToNode(endReverse, edgeIdentifier, ConnectedEdgeType.OUT));\n        Assert.assertTrue(isEdgeRelatedToNode(startReverse, edgeIdentifier, ConnectedEdgeType.IN));\n\n        final Set<Long> edgeIdentifiers = getMatchingEdgesInRelation(atlas, relationIdentifier,\n                edgeIdentifier);\n\n        Assert.assertEquals(edgeIdentifiers.size(), 2);\n        Assert.assertTrue(edgeIdentifiers.contains(edgeIdentifier));\n        Assert.assertTrue(edgeIdentifiers.contains(reverseIdentifier));\n\n        Assert.assertNotNull(atlas.node(startForward.getIdentifier()));\n        Assert.assertNotNull(atlas.node(endForward.getIdentifier()));\n\n        verifyCounts(atlas);\n\n        final FeatureChange removeFeatureChange = FeatureChange\n                .remove(CompleteEdge.shallowFrom(edge), atlas);\n\n        final Change change = ChangeBuilder.newInstance().add(removeFeatureChange).get();\n\n        final Atlas changedAtlas = new ChangeAtlas(atlas, change);\n\n        Assert.assertNull(changedAtlas.edge(edgeIdentifier));\n        Assert.assertNotNull(changedAtlas.edge(reverseIdentifier));\n\n        final long countAfter = count(changedAtlas.edges());\n\n        Assert.assertEquals(countBefore - 1, countAfter);\n\n        Assert.assertNotNull(changedAtlas.node(startForward.getIdentifier()));\n        Assert.assertNotNull(changedAtlas.node(endForward.getIdentifier()));\n\n        final Set<Long> edgeIdentifiersAfterDelete = getMatchingEdgesInRelation(changedAtlas,\n                relationIdentifier, edgeIdentifier);\n\n        Assert.assertEquals(edgeIdentifiersAfterDelete.size(), 1);\n        Assert.assertFalse(edgeIdentifiersAfterDelete.contains(edgeIdentifier));\n        Assert.assertTrue(edgeIdentifiersAfterDelete.contains(reverseIdentifier));\n\n        Assert.assertFalse(isEdgeRelatedToNode(changedAtlas, startForward.getIdentifier(),\n                edgeIdentifier, ConnectedEdgeType.OUT));\n        Assert.assertFalse(isEdgeRelatedToNode(changedAtlas, endForward.getIdentifier(),\n                edgeIdentifier, ConnectedEdgeType.IN));\n        Assert.assertFalse(isEdgeRelatedToNode(changedAtlas, endReverse.getIdentifier(),\n                edgeIdentifier, ConnectedEdgeType.OUT));\n        Assert.assertFalse(isEdgeRelatedToNode(changedAtlas, startReverse.getIdentifier(),\n                edgeIdentifier, ConnectedEdgeType.IN));\n\n        verifyCounts(changedAtlas, Pair.of(ItemType.EDGE, CascadeDeleteTestRule.EDGE_COUNT - 1));\n    }\n\n    protected Atlas deleteNode(final Atlas atlas, final long nodeIdToDelete, final long relationId)\n    {\n        verifyCounts(atlas);\n        final Node node = atlas.node(nodeIdToDelete);\n        Assert.assertNotNull(node);\n\n        final Relation relation = atlas.relation(relationId);\n        Assert.assertNotNull(relation);\n        Assert.assertFalse(relation.membersOfType(ItemType.NODE).isEmpty());\n        Assert.assertTrue(isNodeMemberPresent(atlas, nodeIdToDelete, relationId));\n\n        final FeatureChange featureChangeRemoveNode = FeatureChange\n                .remove(CompleteEntity.shallowFrom(node), atlas);\n        final Change change = ChangeBuilder.newInstance().add(featureChangeRemoveNode).get();\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        Assert.assertNull(changeAtlas.node(nodeIdToDelete));\n        Assert.assertFalse(isNodeMemberPresent(changeAtlas, nodeIdToDelete, relationId));\n        return changeAtlas;\n    }\n\n    protected Atlas deleteSimpleRelation(final long entityIdentifier, final Atlas atlas)\n    {\n        verifyCounts(atlas);\n\n        final Relation relation = atlas.relation(entityIdentifier);\n\n        final FeatureChange featureChange = FeatureChange\n                .remove(CompleteRelation.shallowFrom(relation), atlas);\n\n        final Change change = ChangeBuilder.newInstance().add(featureChange).get();\n\n        return new ChangeAtlas(atlas, change);\n    }\n\n    protected Atlas getAtlas()\n    {\n        return this.rule.getAtlas();\n    }\n\n    protected Set<Long> getMatchingEdgesInRelation(final Atlas atlas, final long relationIdentifier,\n            final long edgeIdentifier)\n    {\n        return atlas.relation(relationIdentifier).members().stream()\n                .filter(relationMember -> relationMember.getEntity().getType() == ItemType.EDGE)\n                .filter(relationMember -> Math.abs(\n                        relationMember.getEntity().getIdentifier()) == Math.abs(edgeIdentifier))\n                .map(relationMember -> relationMember.getEntity().getIdentifier())\n                .collect(Collectors.toSet());\n    }\n\n    protected boolean isEdgeRelatedToNode(final Atlas atlas, final long nodeId, final long edgeId,\n            final ConnectedEdgeType connectedEdgeType)\n    {\n        final Node node = atlas.node(nodeId);\n        Assert.assertNotNull(node);\n        return isEdgeRelatedToNode(node, edgeId, connectedEdgeType);\n    }\n\n    protected boolean isEdgeRelatedToNode(final Node node, final long edgeId,\n            final ConnectedEdgeType connectedEdgeType)\n    {\n        return node.connectedEdges(connectedEdgeType).stream()\n                .filter(edge -> edge.getIdentifier() == edgeId).findFirst().isPresent();\n    }\n\n    protected boolean isEntityPresentInRelation(final Atlas atlas, final ItemType itemType,\n            final long entityRelationIdentifier, final long entityIdentifier)\n    {\n        final Relation relation = atlas.relation(entityRelationIdentifier);\n\n        Assert.assertNotNull(relation);\n\n        return relation.members().stream()\n                .filter(relationMember -> relationMember.getEntity().getType() == itemType)\n                .filter(relationMember -> relationMember.getEntity()\n                        .getIdentifier() == entityIdentifier)\n                .findFirst().isPresent();\n    }\n\n    protected boolean isNodeMemberPresent(final Atlas atlas, final long nodeIdToDelete,\n            final long relationId)\n    {\n        final Predicate<RelationMember> relationMemberPredicate = member -> member.getEntity()\n                .getType() == ItemType.NODE && member.getEntity().getIdentifier() == nodeIdToDelete;\n\n        return !atlas.relation(relationId).membersMatching(relationMemberPredicate).isEmpty();\n    }\n\n    protected Pair<ItemType, Long> override(final ItemType itemType, final int override)\n    {\n        return Pair.of(itemType, this.rule.getCountExpectationMapping().get(itemType) + override);\n    }\n\n    protected void testDeleteSimple(final ItemType itemType, final long entityIdentifier,\n            final long entityRelationIdentifier)\n    {\n        final boolean relationEmptiesAndAutoDeletes = false;\n\n        testDeleteSimple(itemType, entityIdentifier, entityRelationIdentifier,\n                relationEmptiesAndAutoDeletes);\n    }\n\n    protected void testDeleteSimple(final ItemType itemType, final long entityIdentifier,\n            final long entityRelationIdentifier, final boolean relationEmptiesAndAutoDeletes)\n    {\n        final Atlas atlas = getAtlas();\n\n        final AtlasEntity atlasEntity = itemType.entityForIdentifier(atlas, entityIdentifier);\n        Assert.assertNotNull(atlasEntity);\n\n        final Relation relation = atlas.relation(entityRelationIdentifier);\n        Assert.assertNotNull(relation);\n        Assert.assertTrue(isEntityPresentInRelation(atlas, itemType, entityRelationIdentifier,\n                entityIdentifier));\n\n        verifyCounts(atlas);\n\n        final CompleteItemType completeItemType = CompleteItemType.from(itemType);\n        final FeatureChange featureChange = FeatureChange\n                .remove(completeItemType.completeEntityShallowFrom(atlasEntity), atlas);\n        final Change change = ChangeBuilder.newInstance().add(featureChange).get();\n\n        final ChangeAtlas changeAtlas = new ChangeAtlas(atlas, change);\n\n        Assert.assertNull(itemType.entityForIdentifier(changeAtlas, entityIdentifier));\n\n        if (!relationEmptiesAndAutoDeletes)\n        {\n            Assert.assertNotNull(changeAtlas.relation(entityRelationIdentifier));\n            Assert.assertFalse(isEntityPresentInRelation(changeAtlas, itemType,\n                    entityRelationIdentifier, entityIdentifier));\n            verifyCounts(changeAtlas, override(itemType, -1));\n        }\n        else\n        {\n            Assert.assertNull(changeAtlas.relation(entityRelationIdentifier));\n            verifyCounts(changeAtlas, override(itemType, -1), override(ItemType.RELATION, -1));\n        }\n    }\n\n    protected void verifyCounts(final Atlas atlas, final Pair<ItemType, Long>... overrides)\n    {\n        final Map<ItemType, Long> countExpectationMapping = this.rule.getCountExpectationMapping();\n\n        final Map<ItemType, Long> overrideMapping = Arrays.stream(overrides)\n                .collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n\n        countExpectationMapping.putAll(overrideMapping);\n\n        Assert.assertEquals(ItemType.values().length, countExpectationMapping.size());\n\n        for (final Map.Entry<ItemType, Long> entry : countExpectationMapping.entrySet())\n        {\n            Assert.assertEquals(\"entry failed: \" + entry, (long) entry.getValue(),\n                    entry.getKey().numberOfEntities(atlas));\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/CascadeDeleteTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author Yazad Khambata\n */\npublic class CascadeDeleteTestRule extends CoreTestRule\n{\n    public static final long EDGE_IDENTIFIER = 1L;\n    public static final long EDGE_RELATION_IDENTIFIER = 1L;\n    public static final long LINE_IDENTIFIER = 1L;\n    public static final long LINE_RELATION_IDENTIFIER = 2L;\n    public static final long AREA_IDENTIFIER = 0L;\n    public static final long AREA_RELATION_IDENTIFIER = 2L;\n    public static final long POINT_IDENTIFIER = 1L;\n    public static final long POINT_RELATION_IDENTIFIER = 3L;\n    public static final long NON_EDGE_NODE_IDENTIFIER = 5L;\n    public static final long NON_EDGE_NODE_RELATION_IDENTIFIER = 6L;\n    public static final long START_END_EDGE_NODE = 3L;\n    public static final long TOP_LEVEL_RELATION_IDENTIFIER = 6L;\n    public static final long SUB_RELATION_IDENTIFIER = 5L;\n    public static final long PARENT_RELATION_IDENTIFIER = 4L;\n    public static final long THE_ONLY_RELATION_MEMBER_POINT_IDENTIFIER = 0L;\n    public static final long ONE_MEMBER_RELATION_IDENTIFIER = 7L;\n\n    public static final long NODE_COUNT = 6;\n    public static final long POINT_COUNT = 4;\n    public static final long EDGE_COUNT = 4;\n    public static final long LINE_COUNT = 2;\n    public static final long AREA_COUNT = 2;\n    public static final long RELATION_COUNT = 7;\n\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n    private static final String THREE = \"37.780724, -122.472249\";\n    private static final String FOUR = \"37.780825, -122.471896\";\n    private static final String FIVE = \"38, -123\";\n    private static final String SIX = \"39, -124\";\n\n    private final Map<ItemType, Long> countExpectationMapping = new HashMap()\n    {\n        private static final long serialVersionUID = 6255547290912151165L;\n\n        {\n            put(ItemType.NODE, NODE_COUNT);\n            put(ItemType.POINT, POINT_COUNT);\n            put(ItemType.EDGE, EDGE_COUNT);\n            put(ItemType.LINE, LINE_COUNT);\n            put(ItemType.AREA, AREA_COUNT);\n            put(ItemType.RELATION, RELATION_COUNT);\n        }\n    };\n\n    @TestAtlas(nodes = { @TestAtlas.Node(id = \"1\", coordinates = @TestAtlas.Loc(value = ONE)),\n            @TestAtlas.Node(id = \"2\", coordinates = @TestAtlas.Loc(value = TWO)),\n            @TestAtlas.Node(id = \"3\", coordinates = @TestAtlas.Loc(value = THREE)),\n            @TestAtlas.Node(id = \"4\", coordinates = @TestAtlas.Loc(value = FOUR)),\n            @TestAtlas.Node(id = \"5\", coordinates = @TestAtlas.Loc(value = FIVE)),\n            @TestAtlas.Node(id = \"6\", coordinates = @TestAtlas.Loc(value = SIX)) },\n\n            edges = {\n                    @TestAtlas.Edge(id = \"0\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO) }),\n                    @TestAtlas.Edge(id = \"1\", coordinates = { @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = THREE) }),\n                    @TestAtlas.Edge(id = \"-1\", coordinates = { @TestAtlas.Loc(value = THREE),\n                            @TestAtlas.Loc(value = TWO) }),\n                    @TestAtlas.Edge(id = \"2\", coordinates = { @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = SIX) }) },\n\n            areas = {\n                    @TestAtlas.Area(id = \"0\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO), @TestAtlas.Loc(value = THREE) }),\n                    @TestAtlas.Area(id = \"1\", coordinates = { @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = THREE), @TestAtlas.Loc(value = FOUR) }) },\n\n            lines = {\n                    @TestAtlas.Line(id = \"0\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO) }),\n                    @TestAtlas.Line(id = \"1\", coordinates = { @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = FOUR) }) },\n\n            points = { @TestAtlas.Point(id = \"0\", coordinates = @TestAtlas.Loc(value = ONE)),\n                    @TestAtlas.Point(id = \"1\", coordinates = @TestAtlas.Loc(value = TWO)),\n                    @TestAtlas.Point(id = \"2\", coordinates = @TestAtlas.Loc(value = THREE)),\n                    @TestAtlas.Point(id = \"3\", coordinates = @TestAtlas.Loc(value = FOUR)) },\n\n            relations = {\n                    @TestAtlas.Relation(id = \"1\", members = {\n                            @TestAtlas.Relation.Member(id = \"0\", role = \"from\", type = \"edge\"),\n                            @TestAtlas.Relation.Member(id = \"2\", role = \"via\", type = \"node\"),\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"to\", type = \"edge\"),\n                            @TestAtlas.Relation.Member(id = \"-1\", role = \"to\", type = \"edge\") }),\n\n                    @TestAtlas.Relation(id = \"2\", members = {\n                            @TestAtlas.Relation.Member(id = \"0\", role = \"inside\", type = \"area\"),\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"outside\", type = \"line\") }),\n\n                    @TestAtlas.Relation(id = \"3\", tags = { \"type=outside\" }, members = {\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"outside\", type = \"area\"),\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"outside\", type = \"line\"),\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"outside\", type = \"point\") }),\n\n                    @TestAtlas.Relation(id = \"5\", members = {\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"a\", type = \"relation\"),\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"b\", type = \"line\") }),\n\n                    @TestAtlas.Relation(id = \"4\", members = {\n                            @TestAtlas.Relation.Member(id = \"5\", role = \"a\", type = \"relation\"),\n                            @TestAtlas.Relation.Member(id = \"2\", role = \"b\", type = \"node\") }),\n\n                    @TestAtlas.Relation(id = \"6\", members = {\n                            @TestAtlas.Relation.Member(id = \"5\", role = \"a\", type = \"node\"),\n                            @TestAtlas.Relation.Member(id = \"3\", role = \"b\", type = \"node\"),\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"c\", type = \"edge\"),\n                            @TestAtlas.Relation.Member(id = \"-1\", role = \"d\", type = \"edge\") }),\n\n                    @TestAtlas.Relation(id = \"7\", members = {\n                            @TestAtlas.Relation.Member(id = \"0\", role = \"a\", type = \"point\"), }),\n\n            })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    public Map<ItemType, Long> getCountExpectationMapping()\n    {\n        return new HashMap<>(this.countExpectationMapping);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/ChangeAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Heading;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation.Ring;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicerTest;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicerTestRule;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolyLineConverter;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * @author matthieun\n */\npublic class ChangeAtlasTest\n{\n    private static final Location NEW_LOCATION = Location.forString(\"37.592796,-122.2457961\");\n\n    private static final CountryBoundaryMap boundary;\n    static\n    {\n        boundary = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> RawAtlasSlicerTest.class\n                        .getResourceAsStream(\"CIV_GIN_LBR_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n    }\n\n    @Rule\n    public ChangeAtlasTestRule rule = new ChangeAtlasTestRule();\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Rule\n    public final RawAtlasSlicerTestRule setup = new RawAtlasSlicerTestRule();\n\n    @Test\n    public void aChangeMemberGeometryTest()\n    {\n        final Atlas rawAtlas = this.setup.getSimpleMultiPolygonAtlas();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n        final ChangeBuilder removeChanges = new ChangeBuilder();\n        removeChanges.add(\n                FeatureChange.remove(CompleteArea.shallowFrom(lbrSlicedAtlas.area(214581000000L))));\n        removeChanges.add(FeatureChange.add(CompleteRelation.from(lbrSlicedAtlas.relation(2000L))\n                .withRemovedMember(lbrSlicedAtlas.area(214581000000L)), lbrSlicedAtlas));\n        final ChangeAtlas removed = new ChangeAtlas(lbrSlicedAtlas, removeChanges.get());\n\n        Assert.assertNotEquals(removed.relation(2000L).asMultiPolygon().get(),\n                lbrSlicedAtlas.relation(2000L).asMultiPolygon().get());\n        Assert.assertTrue(removed.relation(2000L).asMultiPolygon().get().getArea() < lbrSlicedAtlas\n                .relation(2000L).asMultiPolygon().get().getArea());\n\n        final ChangeBuilder changes = new ChangeBuilder();\n        final CompleteArea area = CompleteArea.from(lbrSlicedAtlas.area(214581000000L));\n        changes.add(FeatureChange.add(area, removed));\n        final CompleteRelation relation = CompleteRelation.from(removed.relation(2000L));\n        relation.withAddedMember(area, Ring.OUTER.toString());\n        changes.add(FeatureChange.add(relation, removed));\n        final ChangeAtlas added = new ChangeAtlas(removed, changes.get());\n        Assert.assertEquals(added.relation(2000L).asMultiPolygon().get().norm(),\n                lbrSlicedAtlas.relation(2000L).asMultiPolygon().get().norm());\n    }\n\n    @Test(expected = CoreException.class)\n    public void aChangeMemberGeometryTest2()\n    {\n        final Atlas rawAtlas = this.setup.getComplexMultiPolygonWithHoleUsingOpenLinesAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n\n        final ChangeBuilder removeChanges = new ChangeBuilder();\n        removeChanges.add(\n                FeatureChange.remove(CompleteLine.shallowFrom(civSlicedAtlas.line(106033001000L))));\n        removeChanges\n                .add(FeatureChange.add(\n                        CompleteRelation.from(civSlicedAtlas.relation(214805001000L))\n                                .withRemovedMember(civSlicedAtlas.line(106033001000L)),\n                        civSlicedAtlas));\n        final ChangeAtlas removed = new ChangeAtlas(civSlicedAtlas, removeChanges.get());\n    }\n\n    @Test\n    public void aChangeMemberGeometryTest3()\n    {\n        final Atlas rawAtlas = this.setup.getComplexMultiPolygonWithHoleUsingOpenLinesAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n\n        final ChangeBuilder changes = new ChangeBuilder();\n        changes.add(\n                FeatureChange.remove(CompleteLine.shallowFrom(civSlicedAtlas.line(106033001000L))));\n\n        final CompleteLine replace = new CompleteLine(106033002000L,\n                civSlicedAtlas.line(106033001000L).asPolyLine(), new HashMap<>(), new HashSet<>());\n        final CompleteRelation relation = CompleteRelation\n                .shallowFrom(civSlicedAtlas.relation(214805001000L));\n        relation.withAddedMember(replace, Ring.OUTER.toString());\n        changes.add(FeatureChange.add(relation, civSlicedAtlas));\n        changes.add(FeatureChange.add(replace));\n\n        final ChangeAtlas replaced = new ChangeAtlas(civSlicedAtlas, changes.get());\n\n        Assert.assertEquals(replaced.relation(214805001000L).asMultiPolygon().get().norm(),\n                replaced.relation(214805001000L).asMultiPolygon().get().norm());\n    }\n\n    @Test\n    public void testAntimeridian()\n    {\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n\n        final PolyLine antimeridianWest = new PolyLine(\n                new Location(Latitude.degrees(40), Longitude.ANTIMERIDIAN_WEST),\n                new Location(Latitude.degrees(41), Longitude.ANTIMERIDIAN_WEST));\n        final PolyLine antimeridianEast = new PolyLine(\n                new Location(Latitude.degrees(40), Longitude.ANTIMERIDIAN_EAST),\n                new Location(Latitude.degrees(41), Longitude.ANTIMERIDIAN_EAST));\n\n        final FeatureChange featureChangeWest = FeatureChange.add(new CompleteLine(123L,\n                antimeridianWest, Maps.hashMap(\"k\", \"v\"), Sets.hashSet(123L)));\n        final FeatureChange featureChangeEast = FeatureChange.add(new CompleteLine(124L,\n                antimeridianEast, Maps.hashMap(\"k\", \"v\"), Sets.hashSet(124L)));\n        changeBuilder.add(featureChangeWest);\n        changeBuilder.add(featureChangeEast);\n\n        final Change change = changeBuilder.get();\n        final Atlas result = new ChangeAtlas(change);\n        Assert.assertNotNull(result.line(123L));\n        Assert.assertNotNull(result.line(124L));\n        Assert.assertEquals(0, Iterables.size(result.points()));\n        Assert.assertFalse(\n                result.line(123L).asPolyLine().intersects(result.line(124L).asPolyLine()));\n    }\n\n    @Test\n    public void testBounds()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        Assert.assertEquals(\"POLYGON ((-122.2450237 37.5920679, -122.2450237 37.5938783, \"\n                + \"-122.2412753 37.5938783, -122.2412753 37.5920679, -122.2450237 37.5920679))\",\n                atlas.bounds().toWkt());\n\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        // One-way motorway\n        final Edge source = atlas.edge(39004000002L);\n        final PolyLine newPolyLine = source.asPolyLine().shiftLastAlongGreatCircle(Heading.NORTH,\n                Distance.ONE_METER);\n        final Location end = newPolyLine.last();\n        final FeatureChange featureChange = new FeatureChange(ChangeType.ADD,\n                CompleteEdge.shallowFrom(source).withPolyLine(newPolyLine));\n        changeBuilder.add(featureChange);\n        changeBuilder.add(new FeatureChange(ChangeType.ADD,\n                CompleteNode.shallowFrom(atlas.node(38990000000L)).withLocation(end)));\n        final Change change = changeBuilder.get();\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        Assert.assertEquals(\"POLYGON ((-122.2450237 37.5920679, -122.2450237 37.5938873, \"\n                + \"-122.2412753 37.5938873, -122.2412753 37.5920679, -122.2450237 37.5920679))\",\n                changeAtlas.bounds().toWkt());\n    }\n\n    @Test\n    public void testBuildFromScratch()\n    {\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        final FeatureChange featureChange1 = FeatureChange.add(new CompleteArea(123L,\n                Polygon.SILICON_VALLEY, Maps.hashMap(\"k\", \"v\"), Sets.hashSet(123L)));\n        changeBuilder.add(featureChange1);\n        final Change change = changeBuilder.get();\n        final Atlas result = new ChangeAtlas(change);\n        Assert.assertNotNull(result.area(123L));\n        Assert.assertEquals(0, Iterables.size(result.points()));\n    }\n\n    @Test\n    public void testBuildFromScratchError()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"ChangeAtlas needs all ADD featureChanges to be full\");\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        changeBuilder.add(getFeatureChangeUpdatedEdgePolyLine().getFirst());\n        final Change change = changeBuilder.get();\n        new ChangeAtlas(change);\n    }\n\n    @Test\n    public void testChangeRelationTags()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        final Relation disconnectedFeatures = atlas.relation(41834000000L);\n        final Map<String, String> tags = disconnectedFeatures.getTags();\n        tags.put(\"newKey\", \"newValue\");\n        changeBuilder.add(FeatureChange\n                .add(CompleteRelation.shallowFrom(disconnectedFeatures).withTags(tags)));\n        final Change change = changeBuilder.get();\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        final Relation changeRelation = changeAtlas.relation(41834000000L);\n        Assert.assertEquals(tags, changeRelation.getTags());\n        Assert.assertEquals(disconnectedFeatures.members().asBean(),\n                changeRelation.members().asBean());\n\n        final Relation parentRelation = changeAtlas.relation(41860000000L);\n        final Relation changeRelationFromParentRelation = (Relation) Iterables\n                .stream(parentRelation.members())\n                .firstMatching(member -> \"child1\".equals(member.getRole())).get().getEntity();\n        Assert.assertEquals(tags, changeRelationFromParentRelation.getTags());\n        Assert.assertEquals(disconnectedFeatures.members().asBean(),\n                changeRelationFromParentRelation.members().asBean());\n    }\n\n    @Test\n    public void testModifyEdgeAndNode()\n    {\n        final Atlas atlas = this.rule.getAtlasEdge();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n\n        final Tuple<FeatureChange, FeatureChange> featureChange1 = getFeatureChangeUpdatedEdgePolyLine();\n        changeBuilder.add(featureChange1.getFirst());\n        changeBuilder.add(featureChange1.getSecond());\n\n        changeBuilder.add(getFeatureChangeMovedNode());\n\n        final Change change = changeBuilder.get();\n        Assert.assertEquals(\"[Edge: id=39001000001, startNode=38999000000, endNode=39002000000, \"\n                + \"polyLine=LINESTRING (-122.2457961 37.592796, -122.2450237 37.5926929, \"\n                + \"-122.2441049 37.5930666, -122.2429584 37.5926993), \"\n                + \"[Tags: [last_edit_user_name => myself], [last_edit_changeset => 1], \"\n                + \"[last_edit_time => 1513719782000], [last_edit_user_id => 1], [name => primary], \"\n                + \"[highway => primary], [last_edit_version => 1]]]\",\n                new ChangeAtlas(atlas, change).edge(39001000001L).toString());\n    }\n\n    @Test\n    public void testModifyEdgeWithoutStartNode()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"does not match with its start Node\");\n\n        final Atlas atlas = this.rule.getAtlasEdge();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n\n        final Tuple<FeatureChange, FeatureChange> featureChange1 = getFeatureChangeUpdatedEdgePolyLine();\n        changeBuilder.add(featureChange1.getFirst());\n        changeBuilder.add(featureChange1.getSecond());\n\n        final Change change = changeBuilder.get();\n        new ChangeAtlas(atlas, change);\n    }\n\n    @Test\n    public void testModifyForwardEdgeWithoutReverseEdge()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"have mismatching PolyLines\");\n\n        final Atlas atlas = this.rule.getAtlasEdge();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n\n        final Edge edge = atlas.edge(39001000001L);\n        final PolyLine oldPolyLine = edge.asPolyLine();\n        final PolyLine newPolyLine = new PolyLine(oldPolyLine.first(), NEW_LOCATION,\n                oldPolyLine.last());\n        final CompleteEdge bloatedEdge = CompleteEdge.shallowFrom(edge).withPolyLine(newPolyLine);\n        final FeatureChange featureChange = new FeatureChange(ChangeType.ADD, bloatedEdge);\n        changeBuilder.add(featureChange);\n\n        final Change change = changeBuilder.get();\n        new ChangeAtlas(atlas, change);\n    }\n\n    @Test\n    public void testMoveArea()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        final Area source = atlas.area(41795000000L);\n        final Polygon origin = source.asPolygon();\n        final Polygon newPolygon = new Polygon(\n                origin.shiftFirstAlongGreatCircle(Heading.NORTH, Distance.ONE_METER));\n        changeBuilder.add(new FeatureChange(ChangeType.ADD,\n                CompleteArea.shallowFrom(source).withPolygon(newPolygon)));\n        final Change change = changeBuilder.get();\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        Assert.assertEquals(newPolygon, changeAtlas.area(41795000000L).asPolygon());\n\n        final Relation disconnectedFeatures = changeAtlas.relation(41834000000L);\n        final Area fromRelation = (Area) Iterables.stream(disconnectedFeatures.members())\n                .firstMatching(member -> \"pond\".equals(member.getRole())).get().getEntity();\n        Assert.assertEquals(newPolygon, fromRelation.asPolygon());\n    }\n\n    @Test\n    public void testMoveEdgeAndNode()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        final Edge source = atlas.edge(39004000002L);\n        final PolyLine origin = source.asPolyLine();\n        final PolyLine newPolyLine = origin.shiftLastAlongGreatCircle(Heading.NORTH,\n                Distance.ONE_METER);\n        final Location newLocation = newPolyLine.last();\n        final FeatureChange featureChange = new FeatureChange(ChangeType.ADD,\n                CompleteEdge.shallowFrom(source).withPolyLine(newPolyLine));\n        changeBuilder.add(featureChange);\n        changeBuilder.add(new FeatureChange(ChangeType.ADD,\n                CompleteNode.shallowFrom(atlas.node(38990000000L)).withLocation(newLocation)));\n        final Change change = changeBuilder.get();\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        final Edge changeEdge = changeAtlas.edge(39004000002L);\n        final Node changeNode = changeAtlas.node(38990000000L);\n        Assert.assertEquals(newPolyLine, changeEdge.asPolyLine());\n        Assert.assertEquals(newLocation, changeNode.getLocation());\n\n        final Relation routeA = changeAtlas.relation(39010000000L);\n        final Edge edgeFromRelation = (Edge) Iterables.stream(routeA.members())\n                .firstMatching(member -> member.getEntity().getIdentifier() == 39004000002L).get()\n                .getEntity();\n        final Node nodeFromRelation = (Node) Iterables.stream(routeA.members())\n                .firstMatching(member -> member.getEntity().getIdentifier() == 38990000000L).get()\n                .getEntity();\n        Assert.assertEquals(newPolyLine, edgeFromRelation.asPolyLine());\n        Assert.assertEquals(newLocation, nodeFromRelation.getLocation());\n    }\n\n    @Test\n    public void testMoveLine()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        final Line source = atlas.line(41771000000L);\n        final PolyLine origin = source.asPolyLine();\n        final PolyLine newPolyLine = origin.shiftFirstAlongGreatCircle(Heading.NORTH,\n                Distance.ONE_METER);\n        changeBuilder.add(new FeatureChange(ChangeType.ADD,\n                CompleteLine.shallowFrom(source).withPolyLine(newPolyLine)));\n        final Change change = changeBuilder.get();\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        Assert.assertEquals(newPolyLine, changeAtlas.line(41771000000L).asPolyLine());\n\n        final Relation disconnectedFeatures = changeAtlas.relation(41834000000L);\n        final Line fromRelation = (Line) Iterables.stream(disconnectedFeatures.members())\n                .firstMatching(member -> \"river\".equals(member.getRole())).get().getEntity();\n        Assert.assertEquals(newPolyLine, fromRelation.asPolyLine());\n    }\n\n    @Test\n    public void testMovePoint()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        final Point source = atlas.point(41822000000L);\n        final Location newLocation = source.getLocation().shiftAlongGreatCircle(Heading.NORTH,\n                Distance.ONE_METER);\n        changeBuilder.add(new FeatureChange(ChangeType.ADD,\n                CompletePoint.shallowFrom(source).withLocation(newLocation)));\n        final Change change = changeBuilder.get();\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        Assert.assertEquals(newLocation, changeAtlas.point(41822000000L).getLocation());\n\n        final Relation disconnectedFeatures = changeAtlas.relation(41834000000L);\n        final Point fromRelation = (Point) Iterables.stream(disconnectedFeatures.members())\n                .firstMatching(member -> \"tree\".equals(member.getRole())).get().getEntity();\n        Assert.assertEquals(newLocation, fromRelation.getLocation());\n    }\n\n    @Test\n    public void testNodePropertyMergeFromInconsistentBeforeAtlases()\n    {\n        final Atlas fullSizedAtlas = this.rule.differentNodeAndEdgeProperties1();\n        final Atlas subbedAtlas = this.rule.differentNodeAndEdgeProperties2();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n\n        // remove Edge -1\n        changeBuilder.add(FeatureChange.remove(CompleteEdge.shallowFrom(fullSizedAtlas.edge(-1L)),\n                fullSizedAtlas));\n\n        // remove Edge -1 from the referenced out edges of node2\n        final CompleteNode node2FromFullAtlas = CompleteNode.shallowFrom(fullSizedAtlas.node(2L));\n        node2FromFullAtlas\n                .withOutEdgeIdentifiers(fullSizedAtlas.node(2L).outEdges().stream()\n                        .map(Edge::getIdentifier).collect(Collectors.toCollection(TreeSet::new)))\n                .withRemovedOutEdgeIdentifier(-1L);\n        changeBuilder.add(FeatureChange.add(node2FromFullAtlas, fullSizedAtlas));\n\n        // change a tag in node 2, but use a different atlas context that cannot see edge -1\n        final CompleteNode node2FromSubbedAtlas = CompleteNode.shallowFrom(subbedAtlas.node(2L));\n        node2FromSubbedAtlas.withTags(subbedAtlas.node(2L).getTags()).withAddedTag(\"new\", \"tag\");\n        changeBuilder.add(FeatureChange.add(node2FromSubbedAtlas, subbedAtlas));\n\n        final Atlas changeAtlas = new ChangeAtlas(fullSizedAtlas, changeBuilder.get());\n\n        final Set<Long> goldenOutEdgeIdentifiers = Sets.hashSet(2L);\n        Assert.assertEquals(goldenOutEdgeIdentifiers, changeAtlas.node(2L).outEdges().stream()\n                .map(Edge::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testRemovedEdgeWhenAConnectedNodeIsMissing()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteNode.shallowFrom(atlas.node(38982000000L))));\n        final Atlas changeAtlas = new ChangeAtlas(atlas, changeBuilder.get());\n\n        // Check that appropriate edges were deleted, triggered by removal of the node\n        Assert.assertNull(changeAtlas.node(38982000000L));\n        Assert.assertNull(changeAtlas.edge(39004000002L));\n        Assert.assertNull(changeAtlas.edge(39004000001L));\n        Assert.assertNull(changeAtlas.edge(39002000001L));\n        Assert.assertNull(changeAtlas.edge(-39002000001L));\n        Assert.assertNull(changeAtlas.edge(39002000002L));\n        Assert.assertNull(changeAtlas.edge(-39002000002L));\n\n        // Check that appropriate features were left alone\n        Assert.assertNotNull(changeAtlas.node(38990000000L));\n        Assert.assertNotNull(changeAtlas.node(38978000000L));\n        Assert.assertNotNull(changeAtlas.node(38986000000L));\n        Assert.assertNotNull(changeAtlas.node(38984000000L));\n        Assert.assertNotNull(changeAtlas.edge(39006000001L));\n    }\n\n    @Test\n    public void testRemovedRelationMember()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        final Relation disconnectedFeatures = atlas.relation(41834000000L);\n        // Remove the point from the list\n        final RelationMemberList newMembers = new RelationMemberList(disconnectedFeatures.members()\n                .stream().filter(member -> !(member.getEntity() instanceof Point))\n                .collect(Collectors.toList()));\n        changeBuilder.add(\n                new FeatureChange(ChangeType.ADD, CompleteRelation.shallowFrom(disconnectedFeatures)\n                        .withMembersAndSource(newMembers, disconnectedFeatures)));\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompletePoint.shallowFrom(atlas.point(41822000000L))));\n        final Change change = changeBuilder.get();\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        Assert.assertEquals(newMembers.asBean(),\n                changeAtlas.relation(41834000000L).members().asBean());\n\n        final Relation parentRelation = changeAtlas.relation(41860000000L);\n        final Relation fromRelation = (Relation) Iterables.stream(parentRelation.members())\n                .firstMatching(member -> \"child1\".equals(member.getRole())).get().getEntity();\n        Assert.assertEquals(newMembers.asBean(), fromRelation.members().asBean());\n    }\n\n    @Test\n    public void testRemovedRelationMemberEasy()\n    {\n        final Atlas atlas = this.rule.getPointAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n\n        /*\n         * Remove Point 1 role b, Point 2 role b, and Point 3 role c. The Point 3 remove will\n         * silently fail because Point 3 has role b and not role c.\n         */\n        final CompleteRelation completeRelation = CompleteRelation.shallowFrom(atlas.relation(1L))\n                .withMembers(atlas.relation(1L).members()).withRemovedMember(atlas.point(1L))\n                .withRemovedMember(atlas.point(2L), \"b\").withRemovedMember(atlas.point(3L), \"c\");\n        changeBuilder.add(FeatureChange.add(completeRelation));\n\n        final CompletePoint completePoint = CompletePoint.shallowFrom(atlas.point(1L))\n                .withRelations(atlas.point(1L).relations()).withRemovedRelationIdentifier(1L);\n        changeBuilder.add(FeatureChange.add(completePoint));\n        final CompletePoint completePoint2 = CompletePoint.shallowFrom(atlas.point(2L))\n                .withRelations(atlas.point(1L).relations()).withRemovedRelationIdentifier(1L);\n        changeBuilder.add(FeatureChange.add(completePoint2));\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, changeBuilder.get());\n        Assert.assertFalse(changeAtlas.relation(1L).members().asBean()\n                .contains(new RelationBean.RelationBeanItem(1L, \"b\", ItemType.POINT)));\n        Assert.assertFalse(changeAtlas.relation(1L).members().asBean()\n                .contains(new RelationBean.RelationBeanItem(2L, \"b\", ItemType.POINT)));\n    }\n\n    @Test\n    public void testRemovedRelationMemberFromIndirectRemoval()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n\n        // Remove a node. This removal will trigger an indirect removal of the connected edges. This\n        // should also trigger an indirect removal of these members from any relation member lists.\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteNode.shallowFrom(atlas.node(38984000000L))));\n        final Atlas changeAtlas = new ChangeAtlas(atlas, changeBuilder.get());\n        final RelationMemberList memberList = changeAtlas.relation(39008000000L).members();\n        final RelationMemberList newMembers = new RelationMemberList(\n                changeAtlas.relation(39008000000L).members().stream()\n                        .filter(member -> member.getEntity().getIdentifier() == -39002000001L\n                                || member.getEntity().getIdentifier() == 39002000001L)\n                        .collect(Collectors.toList()));\n        Assert.assertEquals(newMembers, memberList);\n\n        // Now, let's do the same thing but remove an additional node. This will trigger the\n        // indirect removal of all the relation's members - so we should see the relation disappear.\n        final ChangeBuilder changeBuilder2 = new ChangeBuilder();\n        changeBuilder2.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteNode.shallowFrom(atlas.node(38984000000L))));\n        changeBuilder2.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteNode.shallowFrom(atlas.node(38982000000L))));\n        final Atlas changeAtlas2 = new ChangeAtlas(atlas, changeBuilder2.get());\n        Assert.assertNull(changeAtlas2.relation(39008000000L));\n    }\n\n    @Test\n    public void testRemovedRelationMemberIsReflectedInMemberListAutomatically()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        final Relation disconnectedFeatures = atlas.relation(41834000000L);\n        // Remove the point from the list\n        final RelationMemberList newMembers = new RelationMemberList(disconnectedFeatures.members()\n                .stream().filter(member -> !(member.getEntity() instanceof Point))\n                .collect(Collectors.toList()));\n        // Here only remove the point. Do not remove the member in the relation. It should\n        // automatically be removed.\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompletePoint.shallowFrom(atlas.point(41822000000L))));\n        final Change change = changeBuilder.get();\n\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        Assert.assertEquals(newMembers.asBean(),\n                changeAtlas.relation(41834000000L).members().asBean());\n\n        final Relation parentRelation = changeAtlas.relation(41860000000L);\n        final Relation fromRelation = (Relation) Iterables.stream(parentRelation.members())\n                .firstMatching(member -> \"child1\".equals(member.getRole())).get().getEntity();\n        Assert.assertEquals(newMembers.asBean(), fromRelation.members().asBean());\n    }\n\n    @Test\n    public void testRemovedShallowRelations()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n\n        // These changes remove all members of relations 41834000000 and 39008000000. Both of these\n        // relations will be dropped, due to becoming empty. Additionally, relation 41860000000\n        // will also become shallow and be dropped, because its only 2 members are the\n        // aforementioned empty relations. Finally. relation 41861000000 will also be dropped, since\n        // its only member was relation 41860000000, which was dropped due to becoming shallow.\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteEdge.shallowFrom(atlas.edge(39002000001L))));\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteEdge.shallowFrom(atlas.edge(-39002000001L))));\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteEdge.shallowFrom(atlas.edge(39002000002L))));\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteEdge.shallowFrom(atlas.edge(-39002000002L))));\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteEdge.shallowFrom(atlas.edge(39006000001L))));\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompletePoint.shallowFrom(atlas.point(41822000000L))));\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteLine.shallowFrom(atlas.line(41771000000L))));\n        changeBuilder.add(new FeatureChange(ChangeType.REMOVE,\n                CompleteArea.shallowFrom(atlas.area(41795000000L))));\n\n        // Now, we are going to add a new relation 41862000000 to the changeset. However, the new\n        // relation will only have a single member, which is a shallow relation that will be\n        // removed. So this brand new relation will also become shallow, and should also be removed.\n        final RelationBean newBean = new RelationBean();\n        newBean.addItem(41861000000L, \"someChild\", ItemType.RELATION);\n        changeBuilder.add(\n                new FeatureChange(ChangeType.ADD, new CompleteRelation(41862000000L, Maps.hashMap(),\n                        atlas.relation(41861000000L).bounds(), newBean, null, null, null, null)));\n\n        // Build the ChangeAtlas and verify relations were removed correctly\n        final Atlas changeAtlas = new ChangeAtlas(atlas, changeBuilder.get());\n        Assert.assertNull(changeAtlas.relation(41834000000L));\n        Assert.assertNull(changeAtlas.relation(39008000000L));\n        Assert.assertNull(changeAtlas.relation(41860000000L));\n        Assert.assertNull(changeAtlas.relation(41861000000L));\n        Assert.assertNull(changeAtlas.relation(41862000000L));\n\n        // Check to make sure we did not accidentally drop relation 39010000000. This relation\n        // contains members which still exist and so must be preserved.\n        Assert.assertFalse(changeAtlas.relation(39010000000L).members().isEmpty());\n        Assert.assertNotNull(changeAtlas.relation(39010000000L));\n    }\n\n    @Test\n    public void testStackedChangeAtlasesWithContextFreeTagChanges()\n    {\n        final Atlas atlas = this.rule.getTagAtlas();\n\n        final CompletePoint point1 = CompletePoint.from(atlas.point(1L)).withAddedTag(\"c\", \"3\");\n        final FeatureChange featureChange1 = FeatureChange.add(point1);\n        final Change change1 = ChangeBuilder.newInstance().add(featureChange1).get();\n        final ChangeAtlas changeAtlas1 = new ChangeAtlas(atlas, change1);\n\n        final CompletePoint point2 = CompletePoint.from(changeAtlas1.point(1L)).withAddedTag(\"d\",\n                \"4\");\n        final FeatureChange featureChange2 = FeatureChange.add(point2);\n        final Change change2 = ChangeBuilder.newInstance().add(featureChange2).get();\n        final ChangeAtlas changeAtlas2 = new ChangeAtlas(changeAtlas1, change2);\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\", \"d\", \"4\"),\n                changeAtlas2.point(1L).getTags());\n    }\n\n    @Test\n    public void testStackedChangeAtlasesWithTagChanges()\n    {\n        final Atlas atlas = this.rule.getTagAtlas();\n\n        final CompletePoint point1 = CompletePoint.from(atlas.point(1L)).withAddedTag(\"c\", \"3\");\n        final FeatureChange featureChange1 = FeatureChange.add(point1, atlas);\n        final Change change1 = ChangeBuilder.newInstance().add(featureChange1).get();\n        final ChangeAtlas changeAtlas1 = new ChangeAtlas(atlas, change1);\n\n        final CompletePoint point2 = CompletePoint.from(changeAtlas1.point(1L)).withAddedTag(\"d\",\n                \"4\");\n        final FeatureChange featureChange2 = FeatureChange.add(point2, changeAtlas1);\n        final Change change2 = ChangeBuilder.newInstance().add(featureChange2).get();\n        final ChangeAtlas changeAtlas2 = new ChangeAtlas(changeAtlas1, change2);\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\", \"d\", \"4\"),\n                changeAtlas2.point(1L).getTags());\n\n        final CompletePoint point3 = CompletePoint.from(atlas.point(1L)).withRemovedTag(\"b\");\n        final FeatureChange featureChange3 = FeatureChange.add(point3, atlas);\n        final Change change3 = ChangeBuilder.newInstance().add(featureChange3).get();\n        final ChangeAtlas changeAtlas3 = new ChangeAtlas(atlas, change3);\n\n        final CompletePoint point4 = CompletePoint.from(changeAtlas3.point(1L)).withRemovedTag(\"a\");\n        final FeatureChange featureChange4 = FeatureChange.add(point4, changeAtlas3);\n        final Change change4 = ChangeBuilder.newInstance().add(featureChange4).get();\n        final ChangeAtlas changeAtlas4 = new ChangeAtlas(changeAtlas3, change4);\n        Assert.assertEquals(Maps.hashMap(), changeAtlas4.point(1L).getTags());\n\n        final CompletePoint point5 = CompletePoint.from(atlas.point(1L)).withReplacedTag(\"a\",\n                \"new_a\", \"new_1\");\n        final FeatureChange featureChange5 = FeatureChange.add(point5, atlas);\n        final Change change5 = ChangeBuilder.newInstance().add(featureChange5).get();\n        final ChangeAtlas changeAtlas5 = new ChangeAtlas(atlas, change5);\n\n        final CompletePoint point6 = CompletePoint.from(changeAtlas5.point(1L)).withReplacedTag(\"b\",\n                \"new_b\", \"new_2\");\n        final FeatureChange featureChange6 = FeatureChange.add(point6, changeAtlas5);\n        final Change change6 = ChangeBuilder.newInstance().add(featureChange6).get();\n        final ChangeAtlas changeAtlas6 = new ChangeAtlas(changeAtlas5, change6);\n        Assert.assertEquals(Maps.hashMap(\"new_a\", \"new_1\", \"new_b\", \"new_2\"),\n                changeAtlas6.point(1L).getTags());\n\n        final CompletePoint point7 = CompletePoint.from(atlas.point(1L))\n                .withTags(Maps.hashMap(\"new_a\", \"new_1\", \"new_b\", \"new_2\"));\n        final FeatureChange featureChange7 = FeatureChange.add(point7, atlas);\n        final Change change7 = ChangeBuilder.newInstance().add(featureChange7).get();\n        final ChangeAtlas changeAtlas7 = new ChangeAtlas(atlas, change7);\n\n        final CompletePoint point8 = CompletePoint.from(changeAtlas7.point(1L))\n                .withTags(Maps.hashMap(\"new_a\", \"new_1\", \"new_b\", \"new_2\", \"new_c\", \"new_3\"));\n        final FeatureChange featureChange8 = FeatureChange.add(point8, changeAtlas7);\n        final Change change8 = ChangeBuilder.newInstance().add(featureChange8).get();\n        final ChangeAtlas changeAtlas8 = new ChangeAtlas(changeAtlas7, change8);\n        Assert.assertEquals(Maps.hashMap(\"new_a\", \"new_1\", \"new_b\", \"new_2\", \"new_c\", \"new_3\"),\n                changeAtlas8.point(1L).getTags());\n    }\n\n    @Test\n    public void testUpdateAreaGeometry()\n    {\n        final Atlas rawAtlas = this.setup.getSimpleMultiPolygonAtlas();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        final ChangeBuilder removeChanges = new ChangeBuilder();\n        final CompleteArea area = CompleteArea.from(lbrSlicedAtlas.area(214581000000L))\n                .withPolygon(Polygon.SILICON_VALLEY);\n        removeChanges.add(FeatureChange.add(area, lbrSlicedAtlas));\n\n        final CompleteRelation relation = CompleteRelation.from(lbrSlicedAtlas.relation(2000L));\n        relation.getAddedGeometry().add(new JtsPolyLineConverter().convert(area.asPolygon()));\n        relation.getRemovedGeometry().add(\n                new JtsPolyLineConverter().convert(lbrSlicedAtlas.area(214581000000L).asPolygon()));\n        removeChanges.add(FeatureChange.add(relation, lbrSlicedAtlas));\n        final ChangeAtlas removed = new ChangeAtlas(lbrSlicedAtlas, removeChanges.get());\n        Assert.assertTrue(removed.relation(2000L).asMultiPolygon().isPresent());\n        Assert.assertTrue(removed.relation(2000L).asMultiPolygon().get()\n                .covers(new JtsPolyLineConverter().convert(area.asPolygon())));\n        Assert.assertFalse(\n                removed.relation(2000L).asMultiPolygon().get().intersects(new JtsPolyLineConverter()\n                        .convert(lbrSlicedAtlas.area(214581000000L).asPolygon())));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testUpdateAreaGeometryButNotGeometricRelation()\n    {\n        final Atlas rawAtlas = this.setup.getSimpleMultiPolygonAtlas();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        final ChangeBuilder removeChanges = new ChangeBuilder();\n        final CompleteArea area = CompleteArea.from(lbrSlicedAtlas.area(214581000000L))\n                .withPolygon(Polygon.CENTER);\n        removeChanges.add(FeatureChange.add(area, lbrSlicedAtlas));\n        final ChangeAtlas removed = new ChangeAtlas(lbrSlicedAtlas, removeChanges.get());\n\n        final CompleteRelation relation = CompleteRelation.from(lbrSlicedAtlas.relation(2000L));\n        relation.getAddedGeometry().add(new JtsPolyLineConverter().convert(area.asPolygon()));\n        relation.getRemovedGeometry().add(\n                new JtsPolyLineConverter().convert(lbrSlicedAtlas.area(214581000000L).asPolygon()));\n        removeChanges.add(FeatureChange.add(relation, lbrSlicedAtlas));\n        final ChangeAtlas removed2 = new ChangeAtlas(lbrSlicedAtlas, removeChanges.get());\n    }\n\n    @Test(expected = CoreException.class)\n    public void testUpdateAreaGeometryButNotGeometricRelation2()\n    {\n        final Atlas rawAtlas = this.setup.getSimpleMultiPolygonAtlas();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        final ChangeBuilder removeChanges = new ChangeBuilder();\n        final CompleteArea area = CompleteArea.from(lbrSlicedAtlas.area(214581000000L))\n                .withPolygon(Polygon.CENTER);\n        removeChanges.add(FeatureChange.add(area, lbrSlicedAtlas));\n        final ChangeAtlas removed = new ChangeAtlas(lbrSlicedAtlas, removeChanges.get());\n\n        final CompleteRelation relation = CompleteRelation.from(lbrSlicedAtlas.relation(2000L));\n        relation.getAddedGeometry().add(new JtsPolyLineConverter().convert(area.asPolygon()));\n        removeChanges.add(FeatureChange.add(relation, lbrSlicedAtlas));\n        final ChangeAtlas removed2 = new ChangeAtlas(lbrSlicedAtlas, removeChanges.get());\n    }\n\n    /**\n     * @return Feature change 2: Update the edge 39001000001L's start node: 38999000000L\n     */\n    private FeatureChange getFeatureChangeMovedNode()\n    {\n        final Atlas atlas = this.rule.getAtlasEdge();\n\n        final Node originalNode = atlas.node(38999000000L);\n        final CompleteNode bloatedNode = CompleteNode.shallowFrom(originalNode)\n                .withLocation(NEW_LOCATION);\n        return new FeatureChange(ChangeType.ADD, bloatedNode);\n    }\n\n    /**\n     * @return Feature change 1: Update the first location in the edge 39001000001L's polyLine\n     */\n    private Tuple<FeatureChange, FeatureChange> getFeatureChangeUpdatedEdgePolyLine()\n    {\n        final Atlas atlas = this.rule.getAtlasEdge();\n\n        final Edge originalEdge1 = atlas.edge(39001000001L);\n        final Edge originalEdge1Reverse = atlas.edge(-39001000001L);\n\n        // Forward:\n        final PolyLine originalPolyLine1 = originalEdge1.asPolyLine();\n        final PolyLine originalPolyLine1Modified = new PolyLine(\n                originalPolyLine1.prepend(new PolyLine(NEW_LOCATION, originalPolyLine1.first())));\n        final CompleteEdge bloatedEdge1 = CompleteEdge.shallowFrom(originalEdge1)\n                .withPolyLine(originalPolyLine1Modified);\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, bloatedEdge1);\n\n        // Backward\n        final PolyLine originalPolyLine1Reverse = originalEdge1Reverse.asPolyLine();\n        final PolyLine originalPolyLine1ModifiedReverse = new PolyLine(originalPolyLine1Reverse\n                .append(new PolyLine(originalPolyLine1Reverse.last(), NEW_LOCATION)));\n        final CompleteEdge bloatedEdge1Reverse = CompleteEdge.shallowFrom(originalEdge1Reverse)\n                .withPolyLine(originalPolyLine1ModifiedReverse);\n        final FeatureChange featureChange1Reverse = new FeatureChange(ChangeType.ADD,\n                bloatedEdge1Reverse);\n\n        return new Tuple<>(featureChange1, featureChange1Reverse);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/ChangeAtlasTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestExtension;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class ChangeAtlasTestRule extends CoreTestExtension\n{\n    private static final String ONE = \"15.420563,-61.336198\";\n    private static final String TWO = \"15.429499,-61.332850\";\n    private static final String THREE = \"15.4855,-61.3041\";\n    private static final String FOUR = \"15.4809,-61.3366\";\n    private static final String FIVE = \"15.4811,-61.3366\";\n\n    @TestAtlas(loadFromJosmOsmResource = \"ChangeAtlasTest.josm.osm\")\n    private Atlas atlas;\n\n    @TestAtlas(loadFromJosmOsmResource = \"ChangeAtlasTestEdge.josm.osm\")\n    private Atlas atlasEdge;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO), tags = { \"tag1=value1\" }),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR), tags = { \"tag1=value1\" })\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=secondary\" }),\n                    @Edge(id = \"-1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"2\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"-2\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"3\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"-3\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\" })\n\n            }\n\n    )\n    private Atlas differentNodeAndEdgeProperties1;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO), tags = { \"tag1=value1\" }),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR), tags = { \"tag1=value1\" })\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"2\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"-2\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"3\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"-3\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\" })\n\n            }\n\n    )\n    private Atlas differentNodeAndEdgeProperties2;\n\n    @TestAtlas(\n\n            points = {\n\n                    @Point(id = \"1\", coordinates = @Loc(value = ONE), tags = { \"a=1\", \"b=2\" })\n\n            }\n\n    )\n    private Atlas tagAtlas;\n\n    @TestAtlas(\n\n            points = {\n\n                    @Point(id = \"1\", coordinates = @Loc(value = ONE), tags = { \"a=1\", \"b=2\" }),\n                    @Point(id = \"2\", coordinates = @Loc(value = TWO), tags = { \"a=1\", \"b=2\" }),\n                    @Point(id = \"3\", coordinates = @Loc(value = THREE), tags = { \"a=1\", \"b=2\" }),\n                    @Point(id = \"4\", coordinates = @Loc(value = FOUR), tags = { \"a=1\", \"b=2\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"1\", role = \"b\", type = \"point\"),\n                            @Member(id = \"2\", role = \"b\", type = \"point\"),\n                            @Member(id = \"3\", role = \"b\", type = \"point\"),\n                            @Member(id = \"4\", role = \"b\", type = \"point\"),\n\n                    })\n\n            }\n\n    )\n    private Atlas pointAtlas;\n\n    @TestAtlas(points = { @Point(id = \"1000000\", coordinates = @Loc(ONE)),\n            @Point(id = \"2000000\", coordinates = @Loc(TWO)),\n            @Point(id = \"3000000\", coordinates = @Loc(THREE)),\n            @Point(id = \"6000000\", coordinates = @Loc(Location.TEST_6_COORDINATES)),\n            @Point(id = \"7000000\", coordinates = @Loc(Location.TEST_7_COORDINATES)) }, nodes = {\n                    @Node(id = \"4000000\", coordinates = @Loc(FOUR)),\n                    @Node(id = \"5000000\", coordinates = @Loc(FIVE)) }, areas = @Area(id = \"3000000\", tags = \"name=Something\", coordinates = {\n                            @Loc(Location.TEST_6_COORDINATES), @Loc(Location.TEST_7_COORDINATES),\n                            @Loc(THREE) }), lines = @Line(id = \"1000000\", coordinates = { @Loc(ONE),\n                                    @Loc(TWO),\n                                    @Loc(THREE) }, tags = \"name=Something\"), edges = @Edge(id = \"2000000\", coordinates = {\n                                            @Loc(FOUR), @Loc(FIVE) }, tags = \"highway=residential\"))\n    private Atlas geometryChangeAtlas;\n\n    public Atlas differentNodeAndEdgeProperties1()\n    {\n        return this.differentNodeAndEdgeProperties1;\n    }\n\n    public Atlas differentNodeAndEdgeProperties2()\n    {\n        return this.differentNodeAndEdgeProperties2;\n    }\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    public Atlas getAtlasEdge()\n    {\n        return this.atlasEdge;\n    }\n\n    /**\n     * Get an atlas designed to test geometry changes\n     *\n     * @return The geometry atlas\n     */\n    public Atlas getGeometryChangeAtlas()\n    {\n        return this.geometryChangeAtlas;\n    }\n\n    public Atlas getPointAtlas()\n    {\n        return this.pointAtlas;\n    }\n\n    public Atlas getTagAtlas()\n    {\n        return this.tagAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/ChangeBuilderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Arrays;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * @author Yazad Khambata\n */\npublic class ChangeBuilderTest\n{\n\n    public static final long TEST_IDENTIFIER = 123L;\n\n    @Test\n    public void addAll()\n    {\n        final FeatureChange featureChange1 = newAreaFeatureChange();\n        final FeatureChange featureChange2 = newLineFeatureChange();\n        final FeatureChange featureChange3 = newNodeFeatureChange();\n        final FeatureChange featureChange4 = newPointFeatureChange();\n\n        final Change change1 = ChangeBuilder.newInstance().add(featureChange1).add(featureChange2)\n                .add(featureChange3).add(featureChange4).get();\n\n        final Change change2 = ChangeBuilder.newInstance().addAll(featureChange1, featureChange2)\n                .addAll(Arrays.asList(featureChange3, featureChange4)).get();\n\n        Assert.assertEquals(change1, change2);\n    }\n\n    @Test\n    public void fluency()\n    {\n        final FeatureChange featureChange1 = newAreaFeatureChange();\n        final FeatureChange featureChange2 = newLineFeatureChange();\n        final ChangeBuilder verboseBuilder = new ChangeBuilder();\n        verboseBuilder.add(featureChange1);\n        verboseBuilder.add(featureChange2);\n        final Change verboseChange = verboseBuilder.get();\n        final Change fluentChange = ChangeBuilder.newInstance().add(featureChange1)\n                .add(featureChange2).get();\n        Assert.assertEquals(verboseChange, fluentChange);\n    }\n\n    @Test\n    public void nameAvailable()\n    {\n        final Change change = ChangeBuilder.newInstance().withName(this.getClass().getName())\n                .add(newAreaFeatureChange()).get();\n        Assert.assertEquals(this.getClass().getName(), change.getName());\n    }\n\n    @Test\n    public void nameNotAvailable()\n    {\n        final Change change = ChangeBuilder.newInstance().add(newAreaFeatureChange()).get();\n        Assert.assertEquals(String.valueOf(change.getIdentifier()), change.getName());\n    }\n\n    @Test\n    public void nameNull()\n    {\n        final Change change = ChangeBuilder.newInstance().withName(null).add(newAreaFeatureChange())\n                .get();\n        Assert.assertEquals(String.valueOf(change.getIdentifier()), change.getName());\n    }\n\n    private FeatureChange newAreaFeatureChange()\n    {\n        return new FeatureChange(ChangeType.ADD,\n                new CompleteArea(TEST_IDENTIFIER, Polygon.CENTER, Maps.hashMap(), null));\n    }\n\n    private FeatureChange newLineFeatureChange()\n    {\n        return new FeatureChange(ChangeType.REMOVE,\n                new CompleteLine(TEST_IDENTIFIER, PolyLine.CENTER, Maps.hashMap(), null));\n    }\n\n    private FeatureChange newNodeFeatureChange()\n    {\n        return new FeatureChange(ChangeType.ADD, new CompleteNode(TEST_IDENTIFIER, Location.CENTER,\n                Maps.hashMap(), null, null, null));\n    }\n\n    private FeatureChange newPointFeatureChange()\n    {\n        return new FeatureChange(ChangeType.ADD,\n                new CompletePoint(TEST_IDENTIFIER, Location.CENTER, Maps.hashMap(), null));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/ChangeMergeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.stream.IntStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Test the merge feature of Change class.\n *\n * @author Yazad Khambata\n */\npublic class ChangeMergeTest extends AbstractChangeTest\n{\n    @Test\n    public void testMergeDifferentItemTypes()\n    {\n        final Change changeWithAreaAndLine1 = newChangeWithAreaAndLine(1, 2);\n        final Change changeWithAreaAndLine2 = newChangeWith2Areas(1, 2, \"key1\", \"value1\");\n        final Change merged = Change.merge(changeWithAreaAndLine1, changeWithAreaAndLine2);\n        log.info(\"merged: {}\", merged);\n        Assert.assertEquals(3, merged.changes().count());\n    }\n\n    @Test\n    public void testMergeEmptySelf()\n    {\n        final Change changeWithAreaAndLine1 = newChangeWithAreaAndLine();\n        final Change merged = Change.merge(changeWithAreaAndLine1, changeWithAreaAndLine1);\n        log.info(\"merged: {}\", merged);\n        log.info(\"changeWithAreaAndLine1: {}\", changeWithAreaAndLine1);\n        // Ensures order.\n        Assert.assertEquals(changeWithAreaAndLine1, merged);\n    }\n\n    @Test\n    public void testMergeSameChangeTypeAndItemType()\n    {\n        final int identifier1 = 1;\n        final Change[] changes = IntStream.range(0, 3).boxed()\n                .map(index -> newChangeWith2Areas(identifier1, 2, \"access\" + index, \"private\"))\n                .toArray(Change[]::new);\n        final Change mergedChange = Change.merge(changes);\n        log.info(\"mergedChange: {}\", mergedChange);\n        Assert.assertNotNull(mergedChange);\n        final FeatureChange[] mergedFeatureChanges = mergedChange.changes()\n                .toArray(FeatureChange[]::new);\n        Assert.assertEquals(2, mergedFeatureChanges.length);\n\n        Assert.assertEquals(identifier1, mergedFeatureChanges[0].getIdentifier());\n        Assert.assertEquals(3, mergedFeatureChanges[0].getTags().size());\n    }\n\n    @Test\n    public void testMergeSameEmpty()\n    {\n        final Change changeWithAreaAndLine1 = newChangeWithAreaAndLine();\n        final Change changeWithAreaAndLine2 = newChangeWithAreaAndLine();\n        final Change merged = Change.merge(changeWithAreaAndLine1, changeWithAreaAndLine2);\n        Assert.assertEquals(changeWithAreaAndLine1, merged);\n        Assert.assertEquals(changeWithAreaAndLine2, merged);\n    }\n\n    @Test\n    public void testNoMerge()\n    {\n        final Change changeWithAreaAndLine1 = newChangeWithAreaAndLine();\n        final Change merged = Change.merge(changeWithAreaAndLine1);\n        log.info(\"merged: {}\", merged);\n        log.info(\"changeWithAreaAndLine1: {}\", changeWithAreaAndLine1);\n        // Ensures order.\n        Assert.assertEquals(changeWithAreaAndLine1, merged);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/ChangeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.HashSet;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * @author matthieun\n * @author Yazad Khambata\n */\npublic class ChangeTest extends AbstractChangeTest\n{\n    @Test\n    public void testAdd()\n    {\n        final Change change = newChangeWithAreaAndLine();\n\n        Assert.assertTrue(change.changeFor(ItemType.AREA, TEST_IDENTIFIER).isPresent());\n        Assert.assertTrue(change.changeFor(ItemType.LINE, TEST_IDENTIFIER).isPresent());\n    }\n\n    @Test\n    public void testAddRemoveSameIdentifier()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Cannot merge two feature changes\");\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(TEST_IDENTIFIER, Polygon.CENTER, Maps.hashMap(), null));\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.REMOVE,\n                new CompleteArea(TEST_IDENTIFIER, Polygon.CENTER, null, null));\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(featureChange1);\n        builder.add(featureChange2);\n    }\n\n    @Test\n    public void testAddSameIdentifierMerge()\n    {\n        final Change result = newChangeWith2Areas();\n        final FeatureChange merged = result.changeFor(ItemType.AREA, TEST_IDENTIFIER).get();\n        final Area area = (Area) merged.getAfterView();\n        Assert.assertEquals(Polygon.TEST_BUILDING, area.asPolygon());\n        Assert.assertEquals(Maps.hashMap(\"key\", \"value\"), area.getTags());\n    }\n\n    @Test\n    public void testAllChangesMappedByAtlasEntityKey()\n    {\n        final CompletePoint point = new CompletePoint(123L, Location.COLOSSEUM, Maps.stringMap(),\n                new HashSet<>());\n        final Change change = ChangeBuilder.newInstance().add(FeatureChange.add(point)).get();\n        final Map<AtlasEntityKey, FeatureChange> allChangesMappedByAtlasEntityKey1 = change\n                .allChangesMappedByAtlasEntityKey();\n        final Map<AtlasEntityKey, FeatureChange> allChangesMappedByAtlasEntityKey2 = change\n                .allChangesMappedByAtlasEntityKey();\n        Assert.assertSame(allChangesMappedByAtlasEntityKey1, allChangesMappedByAtlasEntityKey2);\n        Assert.assertEquals(1, allChangesMappedByAtlasEntityKey1.size());\n        Assert.assertNotNull(\n                allChangesMappedByAtlasEntityKey1.get(new AtlasEntityKey(ItemType.POINT, 123L)));\n    }\n\n    @Test\n    public void testEdgeNodeValidation()\n    {\n        final CompleteEdge edge1 = new CompleteEdge(1L, PolyLine.SIMPLE_POLYLINE,\n                Maps.hashMap(\"key1\", \"value1\", \"newKey\", \"newValue\"), null, null, null);\n        final CompleteEdge edge1Reverse = new CompleteEdge(-1L, PolyLine.SIMPLE_POLYLINE.reversed(),\n                Maps.hashMap(\"key1\", \"value1\", \"newKey\", \"newValue\"), 2L, 1L, null);\n        final Change change1 = ChangeBuilder.newInstance()\n                .addAll(FeatureChange.add(edge1), FeatureChange.add(edge1Reverse)).get();\n        Assert.assertEquals(2, change1.changeCount());\n    }\n\n    @Test\n    public void testEdgeNodeValidationFail()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Forward edge 1 start node CompleteNode\");\n        final CompleteEdge edge2 = new CompleteEdge(1L, PolyLine.SIMPLE_POLYLINE,\n                Maps.hashMap(\"key1\", \"value1\", \"newKey\", \"newValue\"), 3L, 2L, null);\n        final CompleteEdge edge2Reverse = new CompleteEdge(-1L, PolyLine.SIMPLE_POLYLINE.reversed(),\n                Maps.hashMap(\"key1\", \"value1\", \"newKey\", \"newValue\"), 2L, 1L, null);\n        final Change change2 = ChangeBuilder.newInstance()\n                .addAll(FeatureChange.add(edge2), FeatureChange.add(edge2Reverse)).get();\n    }\n\n    @Test\n    public void testEqualsAndHashCode()\n    {\n        final Change changeWithAreaAndLine1 = newChangeWithAreaAndLine();\n        final Change changeWithAreaAndLine2 = newChangeWithAreaAndLine();\n\n        Assert.assertTrue(changeWithAreaAndLine1 != changeWithAreaAndLine2);\n\n        Assert.assertEquals(changeWithAreaAndLine1, changeWithAreaAndLine2);\n        Assert.assertEquals(changeWithAreaAndLine1.hashCode(), changeWithAreaAndLine2.hashCode());\n\n        final Change changeWith2Areas1 = newChangeWith2Areas();\n        final Change changeWith2Areas2 = newChangeWith2Areas();\n\n        Assert.assertTrue(changeWith2Areas1 != changeWith2Areas2);\n\n        Assert.assertEquals(changeWith2Areas1, changeWith2Areas2);\n        Assert.assertEquals(changeWith2Areas1.hashCode(), changeWith2Areas2.hashCode());\n\n        Assert.assertNotEquals(changeWithAreaAndLine1, changeWith2Areas1);\n        Assert.assertNotEquals(changeWithAreaAndLine1.hashCode(), changeWith2Areas1.hashCode());\n    }\n\n    @Test\n    public void testUnequalAndDifferentHashCodeRelations()\n    {\n        final Change changeRelation1 = newChangeWithRelationMemberSet1();\n        final Change changeRelation2 = newChangeWithRelationMemberSet2();\n\n        Assert.assertNotSame(changeRelation1, changeRelation2);\n        Assert.assertNotEquals(changeRelation1, changeRelation2);\n        Assert.assertNotEquals(changeRelation1.hashCode(), changeRelation2.hashCode());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/FeatureChangeMergerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.openstreetmap.atlas.exception.change.FeatureChangeMergeException;\nimport org.openstreetmap.atlas.exception.change.MergeFailureType;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class FeatureChangeMergerTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(FeatureChangeMergerTest.class);\n\n    @Test\n    public void testBeforeViewAreaMerge()\n    {\n        final CompleteArea before1 = new CompleteArea(123L, null, Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                null).withBoundsExtendedBy(Polygon.SILICON_VALLEY.bounds());\n        final CompleteArea after1 = new CompleteArea(123L, null,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null)\n                .withBoundsExtendedBy(Polygon.SILICON_VALLEY.bounds());\n        final CompleteArea before2 = new CompleteArea(123L, Polygon.SILICON_VALLEY, null, null);\n        final CompleteArea after2 = new CompleteArea(123L, Polygon.SILICON_VALLEY_2, null, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, after1, before1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, after2, before2);\n        final FeatureChange mergedFeatureChange = featureChange1.merge(featureChange2);\n        final CompleteArea mergedBefore = (CompleteArea) mergedFeatureChange.getBeforeView();\n        final CompleteArea mergedAfter = (CompleteArea) mergedFeatureChange.getAfterView();\n        Assert.assertEquals(Polygon.SILICON_VALLEY, mergedBefore.asPolygon());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), mergedBefore.getTags());\n        Assert.assertEquals(Polygon.SILICON_VALLEY_2, mergedAfter.asPolygon());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), mergedAfter.getTags());\n    }\n\n    @Test\n    public void testBeforeViewEdgeMerge()\n    {\n        final CompleteEdge before1 = new CompleteEdge(123L, null, Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                null, null, null).withBoundsExtendedBy(PolyLine.TEST_POLYLINE.bounds());\n        final CompleteEdge after1 = new CompleteEdge(123L, null,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null, null, null)\n                .withBoundsExtendedBy(PolyLine.TEST_POLYLINE.bounds());\n        final CompleteEdge before2 = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null,\n                null, null);\n        final CompleteEdge after2 = new CompleteEdge(123L, PolyLine.TEST_POLYLINE_2, null, null,\n                null, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, after1, before1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, after2, before2);\n        final FeatureChange mergedFeatureChange = featureChange1.merge(featureChange2);\n        final CompleteEdge mergedBefore = (CompleteEdge) mergedFeatureChange.getBeforeView();\n        final CompleteEdge mergedAfter = (CompleteEdge) mergedFeatureChange.getAfterView();\n        Assert.assertEquals(PolyLine.TEST_POLYLINE, mergedBefore.asPolyLine());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), mergedBefore.getTags());\n        Assert.assertEquals(PolyLine.TEST_POLYLINE_2, mergedAfter.asPolyLine());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), mergedAfter.getTags());\n    }\n\n    @Test\n    public void testBeforeViewLineMerge()\n    {\n        final CompleteLine before1 = new CompleteLine(123L, null, Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                null).withBoundsExtendedBy(PolyLine.TEST_POLYLINE.bounds());\n        final CompleteLine after1 = new CompleteLine(123L, null,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null)\n                .withBoundsExtendedBy(PolyLine.TEST_POLYLINE.bounds());\n        final CompleteLine before2 = new CompleteLine(123L, PolyLine.TEST_POLYLINE, null, null);\n        final CompleteLine after2 = new CompleteLine(123L, PolyLine.TEST_POLYLINE_2, null, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, after1, before1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, after2, before2);\n        final FeatureChange mergedFeatureChange = featureChange1.merge(featureChange2);\n        final CompleteLine mergedBefore = (CompleteLine) mergedFeatureChange.getBeforeView();\n        final CompleteLine mergedAfter = (CompleteLine) mergedFeatureChange.getAfterView();\n        Assert.assertEquals(PolyLine.TEST_POLYLINE, mergedBefore.asPolyLine());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), mergedBefore.getTags());\n        Assert.assertEquals(PolyLine.TEST_POLYLINE_2, mergedAfter.asPolyLine());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), mergedAfter.getTags());\n    }\n\n    @Test\n    public void testBeforeViewNodeMerge()\n    {\n        final CompleteNode before1 = new CompleteNode(123L, null, Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                null, null, null).withBoundsExtendedBy(Location.TEST_1.bounds());\n        final CompleteNode after1 = new CompleteNode(123L, null,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null, null, null)\n                .withBoundsExtendedBy(Location.TEST_1.bounds());\n        final CompleteNode before2 = new CompleteNode(123L, Location.TEST_1, null, null, null,\n                null);\n        final CompleteNode after2 = new CompleteNode(123L, Location.TEST_2, null, null, null, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, after1, before1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, after2, before2);\n        final FeatureChange mergedFeatureChange = featureChange1.merge(featureChange2);\n        final CompleteNode mergedBefore = (CompleteNode) mergedFeatureChange.getBeforeView();\n        final CompleteNode mergedAfter = (CompleteNode) mergedFeatureChange.getAfterView();\n        Assert.assertEquals(Location.TEST_1, mergedBefore.getLocation());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), mergedBefore.getTags());\n        Assert.assertEquals(Location.TEST_2, mergedAfter.getLocation());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), mergedAfter.getTags());\n    }\n\n    @Test\n    public void testBeforeViewPointMerge()\n    {\n        final CompletePoint before1 = new CompletePoint(123L, null,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null)\n                .withBoundsExtendedBy(Location.TEST_1.bounds());\n        final CompletePoint after1 = new CompletePoint(123L, null,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null)\n                .withBoundsExtendedBy(Location.TEST_1.bounds());\n        final CompletePoint before2 = new CompletePoint(123L, Location.TEST_1, null, null);\n        final CompletePoint after2 = new CompletePoint(123L, Location.TEST_2, null, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, after1, before1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, after2, before2);\n        final FeatureChange mergedFeatureChange = featureChange1.merge(featureChange2);\n        final CompletePoint mergedBefore = (CompletePoint) mergedFeatureChange.getBeforeView();\n        final CompletePoint mergedAfter = (CompletePoint) mergedFeatureChange.getAfterView();\n        Assert.assertEquals(Location.TEST_1, mergedBefore.getLocation());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), mergedBefore.getTags());\n        Assert.assertEquals(Location.TEST_2, mergedAfter.getLocation());\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), mergedAfter.getTags());\n    }\n\n    @Test\n    public void testMergeAreasFail()\n    {\n        final CompleteArea beforeArea1 = new CompleteArea(123L, Polygon.CENTER,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY_2, null, null), beforeArea1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY, Maps.hashMap(\"a\", \"1\"), null),\n                beforeArea1);\n\n        /*\n         * This merge will fail, because the FeatureChanges have conflicting changed polygons. There\n         * is no way to resolve conflicting geometry during a merge.\n         */\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.MUTUALLY_EXCLUSIVE_ADD_ADD_CONFLICT,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception.traceContainsFailureType(\n                    MergeFailureType.MUTUALLY_EXCLUSIVE_ADD_ADD_CONFLICT));\n            Assert.assertTrue(exception.traceContainsExactFailureSubSequence(Arrays.asList(\n                    MergeFailureType.MUTUALLY_EXCLUSIVE_ADD_ADD_CONFLICT,\n                    MergeFailureType.DIFF_BASED_POLYGON_MERGE_FAIL,\n                    MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED)));\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(Arrays.asList(\n                    MergeFailureType.MUTUALLY_EXCLUSIVE_ADD_ADD_CONFLICT,\n                    MergeFailureType.DIFF_BASED_POLYGON_MERGE_FAIL,\n                    MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergeAreasSuccess()\n    {\n        final CompleteArea beforeArea1 = new CompleteArea(123L, Polygon.SILICON_VALLEY,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\", \"d\", \"4\", \"e\", \"5\"), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY_2,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"12\", \"d\", \"4\", \"y\", \"25\"), null),\n                beforeArea1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY_2,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"12\", \"c\", \"3\", \"d\", \"4\", \"z\", \"26\"), null),\n                beforeArea1);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * Here we have a busy tag map merge. The left and right both add and remove things on their\n         * own, as well as share some removes and modifies. We also check that the area geometry\n         * updated correctly.\n         */\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"12\", \"d\", \"4\", \"y\", \"25\", \"z\", \"26\"),\n                ((Area) merged.getAfterView()).getTags());\n\n        Assert.assertEquals(Polygon.SILICON_VALLEY_2, ((Area) merged.getAfterView()).asPolygon());\n    }\n\n    @Test\n    public void testMergeAreasWithNonConflictingChangedFields()\n    {\n        final CompleteArea beforeArea1 = new CompleteArea(123L, Polygon.SILICON_VALLEY, null,\n                Sets.hashSet(1L, 2L, 3L));\n        final CompleteArea beforeArea2 = new CompleteArea(123L, Polygon.SILICON_VALLEY,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY, null, Sets.hashSet(1L, 2L, 3L, 4L)),\n                beforeArea1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null),\n                beforeArea2);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * We check that the simple tag ADD [c=3] gets merged properly. Also, we check that the\n         * parent relations set is merged.\n         */\n        Assert.assertEquals(Polygon.SILICON_VALLEY, ((Area) merged.getAfterView()).asPolygon());\n\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"),\n                ((Area) merged.getAfterView()).getTags());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L, 4L), ((Area) merged.getAfterView()).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n\n        // Test that the beforeView was merged properly\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                ((Area) merged.getBeforeView()).getTags());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L), ((Area) merged.getBeforeView()).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testMergeEdgesFail()\n    {\n        final CompleteEdge beforeEdge1 = new CompleteEdge(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(\"a\", \"1\"), 1L, null, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, new CompleteEdge(\n                123L, PolyLine.TEST_POLYLINE, Maps.hashMap(\"a\", \"2\"), 2L, null, null), beforeEdge1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, new CompleteEdge(\n                123L, PolyLine.TEST_POLYLINE, Maps.hashMap(\"a\", \"3\"), 1L, null, null), beforeEdge1);\n\n        /*\n         * This merge will fail, because the FeatureChanges have a tag ADD/ADD conflict (a->2 vs.\n         * a->3).\n         */\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.DIFF_BASED_TAG_ADD_ADD_CONFLICT,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception\n                    .traceContainsFailureType(MergeFailureType.DIFF_BASED_TAG_ADD_ADD_CONFLICT));\n            Assert.assertTrue(exception.traceContainsExactFailureSubSequence(Arrays.asList(\n                    MergeFailureType.DIFF_BASED_TAG_ADD_ADD_CONFLICT,\n                    MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED)));\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(Arrays.asList(\n                    MergeFailureType.DIFF_BASED_TAG_ADD_ADD_CONFLICT,\n                    MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergeEdgesSuccess()\n    {\n        final CompleteEdge beforeEdge1 = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, 1L,\n                null, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteEdge(123L, PolyLine.TEST_POLYLINE_2, null, 2L, null, null),\n                beforeEdge1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteEdge(123L, PolyLine.TEST_POLYLINE_2, null, null, null, null),\n                beforeEdge1);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * Check that the polyline change and the start node change merged properly.\n         */\n        Assert.assertEquals(PolyLine.TEST_POLYLINE_2, ((Edge) merged.getAfterView()).asPolyLine());\n\n        Assert.assertEquals(2L, ((Edge) merged.getAfterView()).start().getIdentifier());\n    }\n\n    @Test\n    public void testMergeEdgesWithNonConflictingChangedFields()\n    {\n        final CompleteEdge beforeEdge1 = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, 1L,\n                null, null);\n        final CompleteEdge beforeEdge2 = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null,\n                2L, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, 3L, null, null), beforeEdge1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, 4L, null), beforeEdge2);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * We check that the start and end node changes were merged properly.\n         */\n        Assert.assertEquals(3L, ((Edge) merged.getAfterView()).start().getIdentifier());\n\n        Assert.assertEquals(4L, ((Edge) merged.getAfterView()).end().getIdentifier());\n\n        // Test that the beforeView was merged properly\n        Assert.assertEquals(1L, ((Edge) merged.getBeforeView()).start().getIdentifier());\n\n        Assert.assertEquals(2L, ((Edge) merged.getBeforeView()).end().getIdentifier());\n    }\n\n    @Test\n    public void testMergeFailMismatchChangeType()\n    {\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY, null, null), null);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.REMOVE,\n                new CompleteArea(123L, Polygon.CENTER, null, null), null);\n\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.FEATURE_CHANGE_INVALID_ADD_REMOVE_MERGE,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception.traceContainsFailureType(\n                    MergeFailureType.FEATURE_CHANGE_INVALID_ADD_REMOVE_MERGE));\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(\n                    Arrays.asList(MergeFailureType.FEATURE_CHANGE_INVALID_ADD_REMOVE_MERGE,\n                            MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergeFailMismatchIdentifier()\n    {\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY, null, null), null);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(456L, Polygon.SILICON_VALLEY, null, null), null);\n\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.FEATURE_CHANGE_INVALID_PROPERTIES_MERGE,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception.traceContainsFailureType(\n                    MergeFailureType.FEATURE_CHANGE_INVALID_PROPERTIES_MERGE));\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(\n                    Arrays.asList(MergeFailureType.FEATURE_CHANGE_INVALID_PROPERTIES_MERGE,\n                            MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergeFailMismatchItemType()\n    {\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.REMOVE,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY, null, null), null);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.REMOVE,\n                new CompletePoint(123L, Location.CENTER, null, null), null);\n\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.FEATURE_CHANGE_INVALID_PROPERTIES_MERGE,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception.traceContainsFailureType(\n                    MergeFailureType.FEATURE_CHANGE_INVALID_PROPERTIES_MERGE));\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(\n                    Arrays.asList(MergeFailureType.FEATURE_CHANGE_INVALID_PROPERTIES_MERGE,\n                            MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergeLinesFail()\n    {\n        final CompleteLine beforeLine1 = new CompleteLine(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(), Sets.hashSet(1L, 2L, 3L));\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteLine(123L, PolyLine.TEST_POLYLINE_2, Maps.hashMap(\"a\", \"1\"),\n                        Sets.hashSet(4L, 5L, 6L)),\n                beforeLine1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteLine(123L, PolyLine.TEST_POLYLINE, Maps.hashMap(\"a\", \"2\"),\n                        Sets.hashSet(1L, 2L)),\n                beforeLine1);\n\n        /*\n         * This merge will fail, because the FeatureChanges have an ADD/ADD tag conflict (a->1 vs.\n         * a->2)\n         */\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.DIFF_BASED_TAG_ADD_ADD_CONFLICT,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(Arrays.asList(\n                    MergeFailureType.DIFF_BASED_TAG_ADD_ADD_CONFLICT,\n                    MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergeLinesSuccess()\n    {\n        final CompleteLine beforeLine1 = new CompleteLine(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.hashSet(1L, 2L, 3L));\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteLine(123L, PolyLine.TEST_POLYLINE_2,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), Sets.hashSet(1L, 2L, 3L, 4L)),\n                beforeLine1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteLine(123L, PolyLine.TEST_POLYLINE_2, Maps.hashMap(\"a\", \"1\", \"b\", \"12\"),\n                        Sets.hashSet(2L, 3L)),\n                beforeLine1);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * Check that the geometry, tag map, and parent relations sets merged properly.\n         */\n        Assert.assertEquals(PolyLine.TEST_POLYLINE_2, ((Line) merged.getAfterView()).asPolyLine());\n\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"12\", \"c\", \"3\"),\n                ((Line) merged.getAfterView()).getTags());\n\n        Assert.assertEquals(Sets.hashSet(2L, 3L, 4L), ((Line) merged.getAfterView()).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testMergeLinesWithNonConflictingChangedFields()\n    {\n        final CompleteLine beforeLine1 = new CompleteLine(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null);\n        final CompleteLine beforeLine2 = new CompleteLine(123L, PolyLine.TEST_POLYLINE, null,\n                Sets.hashSet(1L, 2L, 3L));\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteLine(123L, PolyLine.TEST_POLYLINE,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null),\n                beforeLine1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteLine(123L, PolyLine.TEST_POLYLINE, null, Sets.hashSet(1L, 2L, 3L, 4L)),\n                beforeLine2);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * Check that the tag map and parent relations sets merged properly.\n         */\n\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"),\n                ((Line) merged.getAfterView()).getTags());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L, 4L), ((Line) merged.getAfterView()).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n\n        // Test that the beforeView was merged properly\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                ((Line) merged.getBeforeView()).getTags());\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L), ((Line) merged.getBeforeView()).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testMergeMetaData()\n    {\n        final CompleteNode beforeNode1 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.treeSet(1L, 2L, 3L),\n                Sets.treeSet(10L, 11L, 12L), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.EIFFEL_TOWER, Maps.hashMap(\"a\", \"1\", \"c\", \"3\"),\n                        Sets.treeSet(1L, 2L, 3L, 4L), Sets.treeSet(10L, 11L, 12L, 13L), null),\n                beforeNode1);\n        featureChange1.addMetaData(\"key1\", \"value1\");\n        featureChange1.addMetaData(\"key\", \"value1\");\n        featureChange1.addMetaData(\"same\", \"value\");\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.EIFFEL_TOWER,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), Sets.treeSet(1L, 2L, 3L, 5L),\n                        Sets.treeSet(10L, 11L), null),\n                beforeNode1);\n        featureChange2.addMetaData(\"key2\", \"value2\");\n        featureChange2.addMetaData(\"key\", \"value2\");\n        featureChange2.addMetaData(\"same\", \"value\");\n\n        FeatureChange merged = featureChange1.merge(featureChange2);\n        Assert.assertEquals(\"value1\", merged.getMetaData().get(\"key1\"));\n        Assert.assertEquals(\"value2\", merged.getMetaData().get(\"key2\"));\n        Assert.assertEquals(\"value1,value2\", merged.getMetaData().get(\"key\"));\n        Assert.assertEquals(\"value\", merged.getMetaData().get(\"same\"));\n\n        merged = featureChange2.merge(featureChange1);\n        Assert.assertEquals(\"value1\", merged.getMetaData().get(\"key1\"));\n        Assert.assertEquals(\"value2\", merged.getMetaData().get(\"key2\"));\n        Assert.assertEquals(\"value1,value2\", merged.getMetaData().get(\"key\"));\n        Assert.assertEquals(\"value\", merged.getMetaData().get(\"same\"));\n    }\n\n    @Test\n    public void testMergeMetaDataOrdering()\n    {\n        final CompleteNode beforeNode1 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.treeSet(1L, 2L, 3L),\n                Sets.treeSet(10L, 11L, 12L), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.EIFFEL_TOWER, Maps.hashMap(\"a\", \"1\", \"c\", \"3\"),\n                        Sets.treeSet(1L, 2L, 3L, 4L), Sets.treeSet(10L, 11L, 12L, 13L), null),\n                beforeNode1);\n        featureChange1.addMetaData(\"key1\", \"b,e,g\");\n\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.EIFFEL_TOWER,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), Sets.treeSet(1L, 2L, 3L, 5L),\n                        Sets.treeSet(10L, 11L), null),\n                beforeNode1);\n        featureChange2.addMetaData(\"key1\", \"a,d,f,h\");\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        Assert.assertEquals(\"a,b,d,e,f,g,h\", merged.getMetaData().get(\"key1\"));\n\n        final FeatureChange featureChange3 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.EIFFEL_TOWER,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), Sets.treeSet(1L, 2L, 3L, 5L),\n                        Sets.treeSet(10L, 11L), null),\n                beforeNode1);\n        featureChange3.addMetaData(\"key1\", \"c\");\n\n        final FeatureChange merged2 = merged.merge(featureChange3);\n        Assert.assertEquals(\"a,b,c,d,e,f,g,h\", merged2.getMetaData().get(\"key1\"));\n    }\n\n    @Test\n    public void testMergeNodesFail()\n    {\n        final CompleteNode beforeNode1 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.treeSet(1L, 2L, 3L),\n                Sets.treeSet(10L, 11L, 12L), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.COLOSSEUM, Maps.hashMap(\"a\", \"1\", \"c\", \"3\"),\n                        Sets.treeSet(1L, 2L, 3L, 4L), Sets.treeSet(10L, 11L, 12L, 13L), null),\n                beforeNode1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.COLOSSEUM,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"3\", \"c\", \"3\"), Sets.treeSet(1L, 2L, 3L, 5L),\n                        Sets.treeSet(10L, 11L), null),\n                beforeNode1);\n\n        /*\n         * This merge will fail, because featureChange1 removes tag [b=2], while featureChange2\n         * modifies it to [b=3]. This generates a tag ADD/REMOVE conflict.\n         */\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.DIFF_BASED_TAG_ADD_REMOVE_CONFLICT,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(Arrays.asList(\n                    MergeFailureType.DIFF_BASED_TAG_ADD_REMOVE_CONFLICT,\n                    MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergeNodesSuccess()\n    {\n        final CompleteNode beforeNode1 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.treeSet(1L, 2L, 3L),\n                Sets.treeSet(10L, 11L, 12L), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.EIFFEL_TOWER, Maps.hashMap(\"a\", \"1\", \"c\", \"3\"),\n                        Sets.treeSet(1L, 2L, 3L, 4L), Sets.treeSet(10L, 11L, 12L, 13L), null),\n                beforeNode1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.EIFFEL_TOWER,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), Sets.treeSet(1L, 2L, 3L, 5L),\n                        Sets.treeSet(10L, 11L), null),\n                beforeNode1);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * Here, we merged [a=1,c=3] and [a=1,b=2,c=3] given beforeView [a=1,b=2]. We can safely\n         * remove [b=2] from the merged result, since one side did not modify and one side removed.\n         * We can safely add [c=3] since one side added, and the other did not remove or add a\n         * conflicting value for 'c'.\n         */\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"c\", \"3\"),\n                ((Node) merged.getAfterView()).getTags());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L, 4L, 5L), ((Node) merged.getAfterView())\n                .inEdges().stream().map(Edge::getIdentifier).collect(Collectors.toSet()));\n\n        Assert.assertEquals(Sets.hashSet(10L, 11L, 13L), ((Node) merged.getAfterView()).outEdges()\n                .stream().map(Edge::getIdentifier).collect(Collectors.toSet()));\n\n        Assert.assertEquals(Location.EIFFEL_TOWER, ((Node) merged.getAfterView()).getLocation());\n    }\n\n    @Test\n    public void testMergeNodesWithConflictingBeforeViews()\n    {\n        final CompleteNode beforeNode1 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.treeSet(1L, 2L),\n                Sets.treeSet(10L, 11L, 12L, 13L), Sets.hashSet(1L));\n\n        final CompleteNode beforeNode2 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.treeSet(2L, 3L, 4L), Sets.treeSet(11L),\n                Sets.hashSet(1L));\n\n        final CompleteNode afterNode1 = new CompleteNode(123L, Location.COLOSSEUM, null,\n                Sets.treeSet(1L, 2L), Sets.treeSet(10L, 11L, 12L, 13L), null);\n        afterNode1.withRemovedInEdgeIdentifier(1L);\n        afterNode1.withRemovedOutEdgeIdentifier(12L);\n        afterNode1.withRemovedOutEdgeIdentifier(13L);\n\n        final CompleteNode afterNode2 = new CompleteNode(123L, Location.COLOSSEUM, null,\n                Sets.treeSet(2L, 3L), Sets.treeSet(11L), null);\n        afterNode2.withRemovedInEdgeIdentifier(4L);\n        afterNode2.withAddedInEdgeIdentifier(100L);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, afterNode1,\n                beforeNode1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, afterNode2,\n                beforeNode2);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        final Set<Long> goldenInEdgeSet = Sets.hashSet(2L, 3L, 100L);\n        final Set<Long> goldenOutEdgeSet = Sets.hashSet(10L, 11L);\n        final Set<Long> goldenExplicitlyExcludedInEdgeSet = Sets.hashSet(1L, 4L);\n        final Set<Long> goldenExplicitlyExcludedOutEdgeSet = Sets.hashSet(12L, 13L);\n\n        final CompleteNode mergedAfterNode = (CompleteNode) merged.getAfterView();\n        Assert.assertEquals(goldenInEdgeSet, mergedAfterNode.inEdges().stream()\n                .map(Edge::getIdentifier).collect(Collectors.toSet()));\n        Assert.assertEquals(goldenOutEdgeSet, mergedAfterNode.outEdges().stream()\n                .map(Edge::getIdentifier).collect(Collectors.toSet()));\n        Assert.assertEquals(goldenExplicitlyExcludedInEdgeSet,\n                mergedAfterNode.explicitlyExcludedInEdgeIdentifiers());\n        Assert.assertEquals(goldenExplicitlyExcludedOutEdgeSet,\n                mergedAfterNode.explicitlyExcludedOutEdgeIdentifiers());\n    }\n\n    @Test\n    public void testMergeNodesWithNonConflictingChangedFields()\n    {\n        final CompleteNode beforeNode1 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null, null, null);\n        final CompleteNode beforeNode2 = new CompleteNode(123L, Location.COLOSSEUM, null,\n                Sets.treeSet(1L, 2L, 3L), null, null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.COLOSSEUM, Maps.hashMap(\"a\", \"1\", \"b\", \"12\"), null,\n                        null, null),\n                beforeNode1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteNode(123L, Location.COLOSSEUM, null, Sets.treeSet(1L, 2L, 3L, 4L), null,\n                        null),\n                beforeNode2);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * We check that the modify tag ADD [b=12] gets merged properly. Also, we check that the\n         * inEdges set merged properly.\n         */\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"12\"),\n                ((Node) merged.getAfterView()).getTags());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L, 4L), ((Node) merged.getAfterView()).inEdges()\n                .stream().map(Edge::getIdentifier).collect(Collectors.toSet()));\n\n        // Test that the beforeView was merged properly\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                ((Node) merged.getBeforeView()).getTags());\n\n        Assert.assertEquals(Sets.treeSet(1L, 2L, 3L), ((Node) merged.getBeforeView()).inEdges()\n                .stream().map(Edge::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testMergePointsFail()\n    {\n        final CompletePoint beforePoint1 = new CompletePoint(123L, Location.CENTER,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompletePoint(123L, Location.COLOSSEUM, Maps.hashMap(\"a\", \"1\"), null),\n                beforePoint1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompletePoint(123L, Location.EIFFEL_TOWER,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null),\n                beforePoint1);\n\n        /*\n         * This merge will fail, because featureChange1 and featureChange2 have conflicting changed\n         * locations.\n         */\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.MUTUALLY_EXCLUSIVE_ADD_ADD_CONFLICT,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(Arrays.asList(\n                    MergeFailureType.MUTUALLY_EXCLUSIVE_ADD_ADD_CONFLICT,\n                    MergeFailureType.DIFF_BASED_LOCATION_MERGE_FAIL,\n                    MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergePointsSuccess()\n    {\n        final CompletePoint beforePoint1 = new CompletePoint(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, new CompletePoint(\n                123L, Location.EIFFEL_TOWER, Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null), beforePoint1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompletePoint(123L, Location.EIFFEL_TOWER,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null),\n                beforePoint1);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * We check that the simple tag ADD [c=3] gets merged properly. Also, we check that the\n         * location updated properly.\n         */\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"),\n                ((Point) merged.getAfterView()).getTags());\n\n        Assert.assertEquals(Location.EIFFEL_TOWER, ((Point) merged.getAfterView()).getLocation());\n    }\n\n    @Test\n    public void testMergePointsWithNonConflictingChangedFields()\n    {\n        final CompletePoint beforePoint1 = new CompletePoint(123L, Location.COLOSSEUM, null,\n                Sets.hashSet(1L, 2L, 3L));\n        final CompletePoint beforePoint2 = new CompletePoint(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), null);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompletePoint(123L, Location.COLOSSEUM, null, Sets.hashSet(1L, 2L, 3L, 4L)),\n                beforePoint1);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompletePoint(123L, Location.COLOSSEUM,\n                        Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), null),\n                beforePoint2);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * We check that the simple tag ADD [c=3] gets merged properly. Also, we check that the\n         * parent relations set is merged.\n         */\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"),\n                ((Point) merged.getAfterView()).getTags());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L, 4L), ((Point) merged.getAfterView())\n                .relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n\n        // Test that the beforeView was merged properly\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                ((Point) merged.getBeforeView()).getTags());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L), ((Point) merged.getBeforeView()).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testMergeRelationsExplicitlyExcludedSets()\n    {\n        /*\n         * This test uses the withMembersAndSource API in CompleteRelation to check that the\n         * explicitlyExcluded logic is properly computed and merged.\n         */\n        final RelationBean beforeMemberBean = new RelationBean();\n        beforeMemberBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeMemberBean.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeMemberBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeMemberBean.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final CompleteRelation beforeRelation = new CompleteRelation(123L,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Rectangle.TEST_RECTANGLE, beforeMemberBean,\n                Arrays.asList(10L, 11L, 12L), null, 123456L, Sets.hashSet(1L, 2L, 3L));\n\n        /*\n         * Explicitly REMOVE [1, 'areaRole1', AREA] and [2, 'areaRole2', AREA].\n         */\n        final RelationMemberList updatedMembers1 = new RelationMemberList(\n                beforeRelation.members().stream()\n                        .filter(member -> !member.getRole().equals(\"areaRole1\")\n                                && !member.getRole().equals(\"areaRole2\"))\n                        .collect(Collectors.toList()));\n        final CompleteRelation afterRelation1 = CompleteRelation.shallowFrom(beforeRelation)\n                .withMembersAndSource(updatedMembers1.asBean(), beforeRelation,\n                        Rectangle.TEST_RECTANGLE);\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, afterRelation1,\n                beforeRelation);\n\n        /*\n         * Explicitly REMOVE [1, 'areaRole1', AREA] and [1, 'pointRole1', POINT].\n         */\n        final RelationMemberList updatedMembers2 = new RelationMemberList(\n                beforeRelation.members().stream()\n                        .filter(member -> !member.getRole().equals(\"areaRole1\")\n                                && !member.getRole().equals(\"pointRole1\"))\n                        .collect(Collectors.toList()));\n        final CompleteRelation afterRelation2 = CompleteRelation.shallowFrom(beforeRelation)\n                .withMembersAndSource(updatedMembers2.asBean(), beforeRelation,\n                        Rectangle.TEST_RECTANGLE);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, afterRelation2,\n                beforeRelation);\n\n        /*\n         * The golden merged bean should only contain [2, 'pointRole2', POINT]. But its\n         * explicitlyExcluded set should contain [[1, 'areaRole1', AREA], [2, 'areaRole2', AREA],\n         * [1, 'pointRole1', POINT]]\n         */\n        final RelationBean goldenMergedMemberBean = new RelationBean();\n        goldenMergedMemberBean.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        goldenMergedMemberBean\n                .addItemExplicitlyExcluded(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        goldenMergedMemberBean\n                .addItemExplicitlyExcluded(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        goldenMergedMemberBean\n                .addItemExplicitlyExcluded(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n\n        /*\n         * Assert that the golden bean and the afterView bean are equivalent, including\n         */\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        Assert.assertTrue(goldenMergedMemberBean.equalsIncludingExplicitlyExcluded(\n                ((Relation) merged.getAfterView()).members().asBean()));\n    }\n\n    @Test\n    public void testMergeRelationsFail()\n    {\n        final RelationBean beforeMemberBean = new RelationBean();\n        beforeMemberBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeMemberBean.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeMemberBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        final CompleteRelation beforeRelation = new CompleteRelation(123L,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Rectangle.TEST_RECTANGLE, beforeMemberBean,\n                Arrays.asList(10L, 11L, 12L), null, 123456L, Sets.hashSet(1L, 2L, 3L));\n\n        final RelationBean afterMemberBean1 = new RelationBean();\n        afterMemberBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterMemberBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterMemberBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(123L, Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"),\n                        Rectangle.TEST_RECTANGLE, afterMemberBean1,\n                        Arrays.asList(10L, 11L, 12L, 13L), null, 1234567L,\n                        Sets.hashSet(1L, 2L, 3L, 4L)),\n                beforeRelation);\n\n        final RelationBean afterMemberBean2 = new RelationBean();\n        afterMemberBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterMemberBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterMemberBean2.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterMemberBean2.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(123L, Maps.hashMap(\"b\", \"100\"), Rectangle.TEST_RECTANGLE_2,\n                        afterMemberBean2, Arrays.asList(11L, 12L), null, 1234567L,\n                        Sets.hashSet(2L, 3L)),\n                beforeRelation);\n\n        /*\n         * This merge will fail due to an ADD/ADD conflict in the member list bean.\n         */\n        boolean caught = false;\n        try\n        {\n            featureChange1.merge(featureChange2);\n        }\n        catch (final FeatureChangeMergeException exception)\n        {\n            caught = true;\n            Assert.assertEquals(MergeFailureType.DIFF_BASED_RELATION_BEAN_ADD_ADD_CONFLICT,\n                    exception.rootLevelFailure());\n            Assert.assertTrue(exception.traceMatchesExactFailureSequence(Arrays.asList(\n                    MergeFailureType.DIFF_BASED_RELATION_BEAN_ADD_ADD_CONFLICT,\n                    MergeFailureType.AFTER_VIEW_CONSISTENT_BEFORE_VIEW_MERGE_STRATEGY_FAILED,\n                    MergeFailureType.HIGHEST_LEVEL_MERGE_FAILURE)));\n        }\n\n        if (!caught)\n        {\n            Assert.fail(\"Did not catch expected FeatureChangeMergeException\");\n        }\n    }\n\n    @Test\n    public void testMergeRelationsSuccess()\n    {\n        final RelationBean beforeMemberBean = new RelationBean();\n        beforeMemberBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeMemberBean.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeMemberBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeMemberBean.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final RelationBean beforeAllKnownOsmBean = new RelationBean();\n        beforeAllKnownOsmBean.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeAllKnownOsmBean.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        final CompleteRelation beforeRelation = new CompleteRelation(123L,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Rectangle.TEST_RECTANGLE, beforeMemberBean,\n                Arrays.asList(10L, 11L, 12L), beforeAllKnownOsmBean, 123456L,\n                Sets.hashSet(1L, 2L, 3L));\n\n        final MultiPolygon multipolygon = new JtsMultiPolygonToMultiPolygonConverter()\n                .backwardConvert(org.openstreetmap.atlas.geography.MultiPolygon.TEST_MULTI_POLYGON);\n\n        final RelationBean afterMemberBean1 = new RelationBean();\n        afterMemberBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterMemberBean1\n                .addItemExplicitlyExcluded(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final RelationBean afterAllKnownOsmBean1 = new RelationBean();\n        afterAllKnownOsmBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterAllKnownOsmBean1.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        afterAllKnownOsmBean1.addItem(new RelationBeanItem(3L, \"lineRole3\", ItemType.LINE));\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(123L, Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"),\n                        Rectangle.TEST_RECTANGLE, afterMemberBean1,\n                        Arrays.asList(10L, 11L, 12L, 13L), afterAllKnownOsmBean1, 123456L,\n                        Sets.hashSet(1L, 2L, 3L, 4L)).withMultiPolygonGeometry(multipolygon),\n                beforeRelation);\n\n        final RelationBean afterMemberBean2 = new RelationBean();\n        afterMemberBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterMemberBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterMemberBean2.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterMemberBean2.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterMemberBean2.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        final RelationBean afterAllKnownOsmBean2 = new RelationBean();\n        afterAllKnownOsmBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        afterAllKnownOsmBean2\n                .addItemExplicitlyExcluded(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(123L, Maps.hashMap(\"b\", \"100\"), Rectangle.TEST_RECTANGLE_2,\n                        afterMemberBean2, Arrays.asList(11L, 12L), afterAllKnownOsmBean2, 1234567L,\n                        Sets.hashSet(2L, 3L)).withMultiPolygonGeometry(multipolygon),\n                beforeRelation);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * Check that all the relation fields merged correctly.\n         */\n        final Optional<MultiPolygon> mergedGeometry = ((Relation) merged.getAfterView())\n                .asMultiPolygon();\n        Assert.assertEquals(Maps.hashMap(\"b\", \"100\", \"c\", \"3\"),\n                ((Relation) merged.getAfterView()).getTags());\n        Assert.assertTrue(mergedGeometry.isPresent());\n        Assert.assertFalse(mergedGeometry.get().isEmpty());\n        Assert.assertEquals(multipolygon, mergedGeometry.get());\n\n        final RelationBean goldenMergedMemberBean = new RelationBean();\n        goldenMergedMemberBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        Assert.assertEquals(goldenMergedMemberBean,\n                ((Relation) merged.getAfterView()).members().asBean());\n\n        final RelationBean goldenMergedOsmBean = new RelationBean();\n        goldenMergedOsmBean.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        goldenMergedOsmBean.addItem(new RelationBeanItem(3L, \"lineRole3\", ItemType.LINE));\n        Assert.assertEquals(goldenMergedOsmBean,\n                ((Relation) merged.getAfterView()).allKnownOsmMembers().asBean());\n\n        Assert.assertEquals(Sets.hashSet(11L, 12L, 13L),\n                ((Relation) merged.getAfterView()).allRelationsWithSameOsmIdentifier().stream()\n                        .map(Relation::getIdentifier).collect(Collectors.toSet()));\n\n        Assert.assertEquals(new Long(1234567L),\n                ((Relation) merged.getAfterView()).osmRelationIdentifier());\n\n        Assert.assertEquals(Sets.hashSet(2L, 3L, 4L), ((Relation) merged.getAfterView()).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testMergeRelationsWithConflictingBeforeViews()\n    {\n        final RelationBean beforeMemberBean1 = new RelationBean();\n        beforeMemberBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeMemberBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeMemberBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeMemberBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final CompleteRelation beforeRelation1 = new CompleteRelation(123L,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), Rectangle.TEST_RECTANGLE,\n                beforeMemberBean1, Arrays.asList(10L, 11L, 12L), null, null,\n                Sets.hashSet(1L, 2L, 3L));\n\n        final RelationBean beforeMemberBean2 = new RelationBean();\n        beforeMemberBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeMemberBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeMemberBean2.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeMemberBean2.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        beforeMemberBean2.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        final CompleteRelation beforeRelation2 = new CompleteRelation(123L,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), Rectangle.TEST_RECTANGLE,\n                beforeMemberBean2, Arrays.asList(10L, 11L, 12L), null, null,\n                Sets.hashSet(1L, 2L, 3L));\n\n        final RelationBean afterMemberBean1 = new RelationBean();\n        afterMemberBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(3L, \"areaRole3\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterMemberBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(123L, Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                        Rectangle.TEST_RECTANGLE, afterMemberBean1,\n                        Arrays.asList(10L, 11L, 12L, 13L), null, null, Sets.hashSet(1L, 2L)),\n                beforeRelation1);\n\n        final RelationBean afterMemberBean2 = new RelationBean();\n        afterMemberBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterMemberBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterMemberBean2.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterMemberBean2.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterMemberBean2.addItem(new RelationBeanItem(4L, \"pointRole4\", ItemType.POINT));\n        afterMemberBean2\n                .addItemExplicitlyExcluded(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(123L, Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                        Rectangle.TEST_RECTANGLE, afterMemberBean2,\n                        Arrays.asList(10L, 11L, 12L, 13L), null, null, Sets.hashSet(1L, 2L)),\n                beforeRelation2);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n\n        final RelationBean goldenMergedMemberBean = new RelationBean();\n        goldenMergedMemberBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(3L, \"areaRole3\", ItemType.AREA));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(4L, \"pointRole4\", ItemType.POINT));\n        goldenMergedMemberBean\n                .addItemExplicitlyExcluded(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n\n        Assert.assertTrue(goldenMergedMemberBean.equalsIncludingExplicitlyExcluded(\n                ((Relation) merged.getAfterView()).members().asBean()));\n    }\n\n    @Test\n    public void testMergeRelationsWithNonConflictingChangeFields()\n    {\n        final RelationBean beforeMemberBean1 = new RelationBean();\n        beforeMemberBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeMemberBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeMemberBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeMemberBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final CompleteRelation beforeRelation1 = new CompleteRelation(123L,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"), Rectangle.TEST_RECTANGLE,\n                beforeMemberBean1, Arrays.asList(10L, 11L, 12L), null, null,\n                Sets.hashSet(1L, 2L, 3L));\n\n        final RelationBean beforeAllKnownOsmBean2 = new RelationBean();\n        beforeAllKnownOsmBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeAllKnownOsmBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        final CompleteRelation beforeRelation2 = new CompleteRelation(123L, null,\n                Rectangle.TEST_RECTANGLE, null, null, beforeAllKnownOsmBean2, 123456L, null);\n\n        final RelationBean afterMemberBean1 = new RelationBean();\n        afterMemberBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterMemberBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(123L, Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                        Rectangle.TEST_RECTANGLE, afterMemberBean1,\n                        Arrays.asList(10L, 11L, 12L, 13L), null, null, Sets.hashSet(1L, 2L)),\n                beforeRelation1);\n\n        final RelationBean afterAllKnownOsmBean2 = new RelationBean();\n        afterAllKnownOsmBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD,\n                new CompleteRelation(123L, null, Rectangle.TEST_RECTANGLE, null, null,\n                        afterAllKnownOsmBean2, 1234567L, null),\n                beforeRelation2);\n\n        final FeatureChange merged = featureChange1.merge(featureChange2);\n        /*\n         * Check that the before and after views merged correctly.\n         */\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                ((Relation) merged.getAfterView()).getTags());\n\n        final RelationBean goldenMergedMemberBean = new RelationBean();\n        goldenMergedMemberBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        goldenMergedMemberBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        Assert.assertEquals(goldenMergedMemberBean,\n                ((Relation) merged.getAfterView()).members().asBean());\n\n        Assert.assertEquals(Sets.hashSet(10L, 11L, 12L, 13L),\n                ((Relation) merged.getAfterView()).allRelationsWithSameOsmIdentifier().stream()\n                        .map(Relation::getIdentifier).collect(Collectors.toSet()));\n\n        final RelationBean goldenMergedOsmBean = new RelationBean();\n        goldenMergedOsmBean.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        Assert.assertEquals(goldenMergedOsmBean,\n                ((Relation) merged.getAfterView()).allKnownOsmMembers().asBean());\n\n        Assert.assertEquals(new Long(1234567L),\n                ((Relation) merged.getAfterView()).osmRelationIdentifier());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L), ((Relation) merged.getAfterView()).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n\n        // Check merged before view\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\"),\n                ((Relation) merged.getBeforeView()).getTags());\n\n        Assert.assertEquals(beforeMemberBean1,\n                ((Relation) merged.getBeforeView()).members().asBean());\n\n        Assert.assertEquals(Sets.hashSet(10L, 11L, 12L),\n                ((Relation) merged.getBeforeView()).allRelationsWithSameOsmIdentifier().stream()\n                        .map(Relation::getIdentifier).collect(Collectors.toSet()));\n\n        Assert.assertEquals(beforeAllKnownOsmBean2,\n                ((Relation) merged.getBeforeView()).allKnownOsmMembers().asBean());\n\n        Assert.assertEquals(new Long(123456L),\n                ((Relation) merged.getBeforeView()).osmRelationIdentifier());\n\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L), ((Relation) merged.getBeforeView())\n                .relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testRemoveMerge()\n    {\n        /*\n         * Test the basic REMOVE merge case, where no special logic is needed.\n         */\n        final CompletePoint beforePoint = new CompletePoint(123L, Location.CENTER,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.hashSet(1L, 2L));\n        final CompletePoint afterPoint = CompletePoint.shallowFrom(beforePoint);\n        final FeatureChange pointChange1 = new FeatureChange(ChangeType.REMOVE, afterPoint,\n                beforePoint);\n        final FeatureChange pointChange2 = new FeatureChange(ChangeType.REMOVE, afterPoint,\n                beforePoint);\n        final FeatureChange pointChangeMerged = pointChange1.merge(pointChange2);\n        Assert.assertEquals(beforePoint, pointChangeMerged.getBeforeView());\n\n        /*\n         * Test REMOVE merge logic for relations.\n         */\n        final RelationBean beforeMemberBean1 = new RelationBean();\n        beforeMemberBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeMemberBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeMemberBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeMemberBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        final RelationBean beforeAllKnownOsmBean1 = new RelationBean();\n        beforeAllKnownOsmBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        final CompleteRelation beforeRelation1 = new CompleteRelation(123L,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Rectangle.TEST_RECTANGLE, beforeMemberBean1,\n                Arrays.asList(10L, 11L, 12L), beforeAllKnownOsmBean1, 123456L,\n                Sets.hashSet(1L, 2L, 3L));\n        final RelationBean beforeMemberBean2 = new RelationBean();\n        beforeMemberBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeMemberBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeMemberBean2.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        final RelationBean beforeAllKnownOsmBean2 = new RelationBean();\n        beforeAllKnownOsmBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        final CompleteRelation beforeRelation2 = new CompleteRelation(123L,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Rectangle.TEST_RECTANGLE, beforeMemberBean2,\n                Arrays.asList(10L, 11L, 12L), beforeAllKnownOsmBean2, 123456L,\n                Sets.hashSet(1L, 2L, 3L));\n        final CompleteRelation afterRelation = CompleteRelation.shallowFrom(beforeRelation1);\n        final FeatureChange relationChange1 = new FeatureChange(ChangeType.REMOVE, afterRelation,\n                beforeRelation1);\n        final FeatureChange relationChange2 = new FeatureChange(ChangeType.REMOVE, afterRelation,\n                beforeRelation2);\n        final FeatureChange relationChangeMerged = relationChange1.merge(relationChange2);\n        final Relation relationBeforeView = (Relation) relationChangeMerged.getBeforeView();\n        Assert.assertTrue(beforeMemberBean1\n                .equalsIncludingExplicitlyExcluded(relationBeforeView.members().asBean()));\n        Assert.assertTrue(beforeAllKnownOsmBean1.equalsIncludingExplicitlyExcluded(\n                relationBeforeView.allKnownOsmMembers().asBean()));\n        Assert.assertTrue(beforeAllKnownOsmBean2.equalsIncludingExplicitlyExcluded(\n                relationBeforeView.allKnownOsmMembers().asBean()));\n\n        /*\n         * Test REMOVE merge logic for nodes.\n         */\n        final CompleteNode beforeNode1 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.treeSet(1L, 2L, 3L), Sets.treeSet(10L, 11L),\n                Sets.hashSet(1L));\n        final CompleteNode beforeNode2 = new CompleteNode(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.treeSet(1L, 2L), Sets.treeSet(10L, 11L, 12L),\n                Sets.hashSet(1L));\n        final CompleteNode afterNode = CompleteNode.shallowFrom(beforeNode1);\n        final FeatureChange nodeChange1 = new FeatureChange(ChangeType.REMOVE, afterNode,\n                beforeNode1);\n        final FeatureChange nodeChange2 = new FeatureChange(ChangeType.REMOVE, afterNode,\n                beforeNode2);\n        final FeatureChange nodeChangeMerged = nodeChange1.merge(nodeChange2);\n        final Node nodeBeforeView = (Node) nodeChangeMerged.getBeforeView();\n        Assert.assertEquals(\n                beforeNode1.inEdges().stream().map(Edge::getIdentifier).collect(Collectors.toSet()),\n                nodeBeforeView.inEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toSet()));\n        Assert.assertEquals(\n                beforeNode2.outEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toSet()),\n                nodeBeforeView.outEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toSet()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/FeatureChangeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescription;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.ChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.change.description.descriptors.TagChangeDescriptor;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.validators.FeatureChangeUsefulnessValidator;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\nclass FeatureChangeTest\n{\n    @RegisterExtension\n    static ChangeAtlasTestRule rule = new ChangeAtlasTestRule();\n\n    /**\n     * The arguments to use for {@link #testChangeDescriptionGeometryAtlasAddGeometry()}.\n     *\n     * @return The stream of arguments.\n     */\n    static Stream<Arguments> testChangeDescriptionGeometryAtlasDeleteGeometry()\n    {\n        return Stream.of(Arguments.of(\"line\",\n                // Node 3 is shared with area 3, so it is not deleted.\n                \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><osmChange generator=\\\"atlas ChangeDescription v0.0.1\\\" version=\\\"0.6\\\"><delete><way action=\\\"delete\\\" id=\\\"1\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"true\\\"><tag k=\\\"name\\\" v=\\\"Something\\\"/><nd ref=\\\"1\\\"/><nd ref=\\\"2\\\"/></way><node action=\\\"delete\\\" id=\\\"1\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"false\\\"/><node action=\\\"delete\\\" id=\\\"2\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"false\\\"/></delete></osmChange>\",\n                (Supplier<AtlasEntity>) () -> rule.getGeometryChangeAtlas().line(1_000_000)),\n                Arguments\n                        .of(\"edge\",\n                                \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><osmChange generator=\\\"atlas ChangeDescription v0.0.1\\\" version=\\\"0.6\\\"><delete><way action=\\\"delete\\\" id=\\\"2\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"true\\\"><tag k=\\\"highway\\\" v=\\\"residential\\\"/><nd ref=\\\"4\\\"/><nd ref=\\\"5\\\"/></way><node action=\\\"delete\\\" id=\\\"4\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"false\\\"/><node action=\\\"delete\\\" id=\\\"5\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"false\\\"/></delete></osmChange>\",\n                                (Supplier<AtlasEntity>) () -> rule\n                                        .getGeometryChangeAtlas().edge(2_000_000)),\n                Arguments.of(\"area\",\n                        // Node 3 is shared with line 1, so it is not deleted.\n                        \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><osmChange generator=\\\"atlas ChangeDescription v0.0.1\\\" version=\\\"0.6\\\"><delete><way action=\\\"delete\\\" id=\\\"3\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"true\\\"><tag k=\\\"name\\\" v=\\\"Something\\\"/><nd ref=\\\"6\\\"/><nd ref=\\\"7\\\"/></way><node action=\\\"delete\\\" id=\\\"6\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"false\\\"/><node action=\\\"delete\\\" id=\\\"7\\\" if-unused=\\\"true\\\" version=\\\"1\\\" visible=\\\"false\\\"/></delete></osmChange>\",\n                        (Supplier<AtlasEntity>) () -> rule.getGeometryChangeAtlas()\n                                .area(3_000_000)));\n    }\n\n    @Test\n    void testAfterViewIsFull()\n    {\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.SILICON_VALLEY, null, null));\n        assertFalse(featureChange1.afterViewIsFull());\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, new CompleteArea(\n                123L, Polygon.SILICON_VALLEY, Maps.hashMap(\"key1\", \"value2\"), Sets.hashSet(123L)));\n        assertTrue(featureChange2.afterViewIsFull());\n    }\n\n    @Test\n    void testBeforeViewUsefulnessValidationArea()\n    {\n        final Polygon polygon = Polygon.CENTER;\n        final Map<String, String> tags = Maps.hashMap(\"a\", \"1\", \"b\", \"2\");\n        final Set<Long> relations = Sets.hashSet(1L, 2L);\n\n        final CompleteArea before = new CompleteArea(123L, polygon, tags, relations);\n\n        final CompleteArea after = CompleteArea.shallowFrom(before).withPolygon(polygon)\n                .withTags(tags).withRelationIdentifiers(relations);\n\n        final FeatureChangeUsefulnessValidator featureChangeUsefulnessValidator = new FeatureChangeUsefulnessValidator(\n                new FeatureChange(ChangeType.ADD, after, before));\n        final CoreException coreException = assertThrows(CoreException.class,\n                featureChangeUsefulnessValidator::validate);\n        assertTrue(coreException.getMessage().contains(\"is not useful\"));\n    }\n\n    @Test\n    void testBeforeViewUsefulnessValidationEdge()\n    {\n        final PolyLine line = PolyLine.CENTER;\n        final Map<String, String> tags = Maps.hashMap(\"a\", \"1\", \"b\", \"2\");\n        final Long startNode = 1L;\n        final Long endNode = 2L;\n        final Set<Long> relations = Sets.hashSet(1L, 2L);\n\n        final CompleteEdge before = new CompleteEdge(123L, line, tags, startNode, endNode,\n                relations);\n\n        final CompleteEdge after = CompleteEdge.shallowFrom(before).withPolyLine(line)\n                .withTags(tags).withStartNodeIdentifier(startNode).withEndNodeIdentifier(endNode)\n                .withRelationIdentifiers(relations);\n\n        final FeatureChangeUsefulnessValidator featureChangeUsefulnessValidator = new FeatureChangeUsefulnessValidator(\n                new FeatureChange(ChangeType.ADD, after, before));\n        final CoreException coreException = assertThrows(CoreException.class,\n                featureChangeUsefulnessValidator::validate);\n        assertTrue(coreException.getMessage().contains(\"is not useful\"));\n    }\n\n    @Test\n    void testBeforeViewUsefulnessValidationLine()\n    {\n        final PolyLine line = PolyLine.CENTER;\n        final Map<String, String> tags = Maps.hashMap(\"a\", \"1\", \"b\", \"2\");\n        final Set<Long> relations = Sets.hashSet(1L, 2L);\n\n        final CompleteLine before = new CompleteLine(123L, line, tags, relations);\n\n        final CompleteLine after = CompleteLine.shallowFrom(before).withPolyLine(line)\n                .withTags(tags).withRelationIdentifiers(relations);\n\n        final FeatureChangeUsefulnessValidator featureChangeUsefulnessValidator = new FeatureChangeUsefulnessValidator(\n                new FeatureChange(ChangeType.ADD, after, before));\n        final CoreException coreException = assertThrows(CoreException.class,\n                featureChangeUsefulnessValidator::validate);\n        assertTrue(coreException.getMessage().contains(\"is not useful\"));\n    }\n\n    @Test\n    void testBeforeViewUsefulnessValidationNode()\n    {\n        final Location location = Location.CENTER;\n        final Map<String, String> tags = Maps.hashMap(\"a\", \"1\", \"b\", \"2\");\n        final SortedSet<Long> inEdges = Sets.treeSet(1L, 2L);\n        final SortedSet<Long> outEdges = Sets.treeSet(3L, 4L);\n        final Set<Long> relations = Sets.hashSet(1L, 2L);\n\n        final CompleteNode before = new CompleteNode(123L, location, tags, inEdges, outEdges,\n                relations);\n\n        final CompleteNode after = CompleteNode.shallowFrom(before).withLocation(location)\n                .withTags(tags).withInEdgeIdentifiers(inEdges).withOutEdgeIdentifiers(outEdges)\n                .withRelationIdentifiers(relations);\n\n        final FeatureChangeUsefulnessValidator featureChangeUsefulnessValidator = new FeatureChangeUsefulnessValidator(\n                new FeatureChange(ChangeType.ADD, after, before));\n        final CoreException coreException = assertThrows(CoreException.class,\n                featureChangeUsefulnessValidator::validate);\n        assertTrue(coreException.getMessage().contains(\"is not useful\"));\n    }\n\n    @Test\n    void testBeforeViewUsefulnessValidationPoint()\n    {\n        final Location location = Location.CENTER;\n        final Map<String, String> tags = Maps.hashMap(\"a\", \"1\", \"b\", \"2\");\n        final Set<Long> relations = Sets.hashSet(1L, 2L);\n\n        final CompletePoint before = new CompletePoint(123L, location, tags, relations);\n\n        final CompletePoint after = CompletePoint.shallowFrom(before).withLocation(location)\n                .withTags(tags).withRelationIdentifiers(relations);\n\n        final FeatureChangeUsefulnessValidator featureChangeUsefulnessValidator = new FeatureChangeUsefulnessValidator(\n                new FeatureChange(ChangeType.ADD, after, before));\n        final CoreException coreException = assertThrows(CoreException.class,\n                featureChangeUsefulnessValidator::validate);\n        assertTrue(coreException.getMessage().contains(\"is not useful\"));\n    }\n\n    @Test\n    void testBeforeViewUsefulnessValidationRelation()\n    {\n        final Rectangle bounds = Rectangle.TEST_RECTANGLE;\n        final Map<String, String> tags = Maps.hashMap(\"a\", \"1\", \"b\", \"2\");\n        final RelationBean members = new RelationBean();\n        members.addItem(new RelationBeanItem(1L, \"role\", ItemType.POINT));\n        final List<Long> allRelationsWithSameOsmIdentifier = Arrays.asList(1L, 2L);\n        final RelationBean allKnownOsmMembers = new RelationBean();\n        allKnownOsmMembers.addItem(new RelationBeanItem(2L, \"role2\", ItemType.AREA));\n        final Long osmRelationIdentifier = 456L;\n        final Set<Long> relations = Sets.hashSet(1L, 2L);\n\n        final CompleteRelation before = new CompleteRelation(123L, tags, bounds, members,\n                allRelationsWithSameOsmIdentifier, allKnownOsmMembers, osmRelationIdentifier,\n                relations);\n\n        final CompleteRelation after = CompleteRelation.shallowFrom(before).withTags(tags)\n                .withMembers(members, bounds).withRelationIdentifiers(relations)\n                .withAllRelationsWithSameOsmIdentifier(allRelationsWithSameOsmIdentifier)\n                .withAllKnownOsmMembers(allKnownOsmMembers)\n                .withOsmRelationIdentifier(osmRelationIdentifier);\n\n        final FeatureChangeUsefulnessValidator featureChangeUsefulnessValidator = new FeatureChangeUsefulnessValidator(\n                new FeatureChange(ChangeType.ADD, after, before));\n        final CoreException coreException = assertThrows(CoreException.class,\n                featureChangeUsefulnessValidator::validate);\n        assertTrue(coreException.getMessage().contains(\"is not useful\"));\n    }\n\n    @Test\n    void testChangeDescriptionGeometry()\n    {\n        final PolyLine polyline1 = PolyLine\n                .wkt(\"LINESTRING(1 1, 2 2, 3 3, 10 10, 20 20, 4 4, 5 5)\");\n        final PolyLine polyline2 = PolyLine\n                .wkt(\"LINESTRING(1 1, 3 3, -10 -10, -20 -20, 4 4, 5 5, 6 6)\");\n\n        final CompleteLine before1 = new CompleteLine(123L, polyline1, null, null);\n        final CompleteLine after1 = new CompleteLine(123L, polyline2, null, null);\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, after1, before1);\n        final ChangeDescription description = featureChange1.explain();\n\n        final String goldenString = \"ChangeDescription [\\n\" + \"UPDATE LINE 123\\n\"\n                + \"GEOMETRY(ADD, 7/7, POINT (6 6))\\n\"\n                + \"GEOMETRY(UPDATE, 3/7, LINESTRING (10 10, 20 20) => LINESTRING (-10 -10, -20 -20))\\n\"\n                + \"GEOMETRY(REMOVE, 1/7, POINT (2 2))\\n\" + \"]\";\n\n        assertEquals(goldenString, description.toString());\n    }\n\n    @Test\n    void testChangeDescriptionGeometryAtlasAddGeometry()\n    {\n        final Atlas atlas = this.rule.getGeometryChangeAtlas();\n        final Line line = atlas.line(1000000);\n        final CompleteEntity<CompleteLine> reversedLine = (CompleteEntity<CompleteLine>) CompleteEntity\n                .shallowFrom(line);\n        final List<Location> locations = Iterables.asList(line.asPolyLine());\n        locations.add(1, Location.CENTER);\n        reversedLine.withGeometry(locations);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                (AtlasEntity) reversedLine);\n        featureChange1.setOptions(FeatureChange.Options.OSC_IF_POSSIBLE);\n        featureChange1.withAtlasContext(atlas);\n\n        final ChangeDescription description = featureChange1.explain();\n\n        final String goldenString = \"{\\\"type\\\":\\\"UPDATE\\\",\" + \"\\\"descriptors\\\":[\"\n                + \"{\\\"name\\\":\\\"GEOMETRY\\\",\\\"type\\\":\\\"ADD\\\",\\\"position\\\":\\\"1/3\\\",\"\n                + \"\\\"afterView\\\":\\\"POINT (0 0)\\\"}],\"\n                // The OSC changes\n                + \"\\\"osc\\\":\\\"\"\n                + Base64.getEncoder().encodeToString(\n                        \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><osmChange generator=\\\"atlas ChangeDescription v0.0.1\\\" version=\\\"0.6\\\"><create><node action=\\\"modify\\\" id=\\\"-1\\\" lat=\\\"0.0\\\" lon=\\\"0.0\\\" version=\\\"1\\\" visible=\\\"true\\\"/></create><modify><way action=\\\"modify\\\" id=\\\"1\\\" version=\\\"1\\\" visible=\\\"true\\\"><tag k=\\\"name\\\" v=\\\"Something\\\"/><nd ref=\\\"1\\\"/><nd ref=\\\"-1\\\"/><nd ref=\\\"2\\\"/><nd ref=\\\"3\\\"/></way></modify></osmChange>\"\n                                .getBytes(StandardCharsets.UTF_8))\n                + \"\\\"}\";\n\n        assertEquals(goldenString, description.toJsonElement().toString());\n    }\n\n    @Test\n    void testChangeDescriptionGeometryAtlasChangeGeometry()\n    {\n        final Atlas atlas = this.rule.getGeometryChangeAtlas();\n        final Line line = atlas.line(1000000);\n        final CompleteEntity<CompleteLine> reversedLine = (CompleteEntity<CompleteLine>) CompleteEntity\n                .shallowFrom(line);\n        final List<Location> locations = Iterables.asList(line.asPolyLine());\n        Collections.reverse(locations);\n        reversedLine.withGeometry(locations);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD,\n                (AtlasEntity) reversedLine);\n        featureChange1.setOptions(FeatureChange.Options.OSC_IF_POSSIBLE);\n        featureChange1.withAtlasContext(atlas);\n\n        final ChangeDescription description = featureChange1.explain();\n\n        final String goldenString = \"{\\\"type\\\":\\\"UPDATE\\\",\" + \"\\\"descriptors\\\":[\"\n                + \"{\\\"name\\\":\\\"GEOMETRY\\\",\\\"type\\\":\\\"ADD\\\",\\\"position\\\":\\\"3/3\\\",\"\n                + \"\\\"afterView\\\":\\\"LINESTRING (-61.33285 15.429499, -61.336198 15.420563)\\\"},\"\n                + \"{\\\"name\\\":\\\"GEOMETRY\\\",\\\"type\\\":\\\"REMOVE\\\",\\\"position\\\":\\\"0/3\\\",\"\n                + \"\\\"beforeView\\\":\\\"LINESTRING (-61.336198 15.420563, -61.33285 15.429499)\\\"}],\"\n                // The OSC changes\n                + \"\\\"osc\\\":\\\"\"\n                + Base64.getEncoder().encodeToString(\n                        \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><osmChange generator=\\\"atlas ChangeDescription v0.0.1\\\" version=\\\"0.6\\\"><modify><way action=\\\"modify\\\" id=\\\"1\\\" version=\\\"1\\\" visible=\\\"true\\\"><tag k=\\\"name\\\" v=\\\"Something\\\"/><nd ref=\\\"3\\\"/><nd ref=\\\"2\\\"/><nd ref=\\\"1\\\"/></way></modify></osmChange>\"\n                                .getBytes(StandardCharsets.UTF_8))\n                + \"\\\"}\";\n\n        assertEquals(goldenString, description.toJsonElement().toString());\n    }\n\n    /**\n     * Check various deletions for an entity\n     *\n     * @param name\n     *            The name to show for the test\n     * @param expectedOsc\n     *            The expected OSC\n     * @param atlasEntitySupplier\n     *            The entity supplier (the entity will be \"deleted\")\n     */\n    @ParameterizedTest(name = \"[{index}] {0}\")\n    @MethodSource\n    void testChangeDescriptionGeometryAtlasDeleteGeometry(final String name,\n            final String expectedOsc, final Supplier<AtlasEntity> atlasEntitySupplier)\n    {\n        final AtlasEntity atlasEntity = atlasEntitySupplier.get();\n        final Atlas atlas = atlasEntity.getAtlas();\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.REMOVE,\n                CompleteEntity.shallowFrom(atlasEntity));\n        featureChange1.setOptions(FeatureChange.Options.OSC_IF_POSSIBLE);\n        featureChange1.withAtlasContext(atlas);\n\n        final ChangeDescription description = featureChange1.explain();\n\n        final String goldenString = \"{\\\"type\\\":\\\"REMOVE\\\",\" + \"\\\"descriptors\\\":[],\\\"osc\\\":\\\"\"\n                + Base64.getEncoder().encodeToString(expectedOsc.getBytes(StandardCharsets.UTF_8))\n                + \"\\\"}\";\n\n        assertEquals(goldenString, description.toJsonElement().toString(),\n                \"Expected:\" + System.lineSeparator() + expectedOsc + System.lineSeparator()\n                        + \"but got\" + System.lineSeparator()\n                        + new String(Base64.getDecoder().decode(description.toJsonElement()\n                                .getAsJsonObject().getAsJsonPrimitive(\"osc\").getAsString())));\n    }\n\n    @Test\n    void testChangeDescriptionInOutEdges()\n    {\n        final CompleteNode before2 = new CompleteNode(123L, Location.forString(\"1,1\"), null,\n                Sets.treeSet(1L, 2L), Sets.treeSet(3L, 4L), null);\n        final CompleteNode after2 = new CompleteNode(123L, Location.forString(\"1,1\"), null,\n                Sets.treeSet(2L, 3L), Sets.treeSet(4L, 5L), null);\n        final FeatureChange featureChange2 = new FeatureChange(ChangeType.ADD, after2, before2);\n        final ChangeDescription description = featureChange2.explain();\n\n        final String goldenString = \"ChangeDescription [\\n\" + \"UPDATE NODE 123\\n\"\n                + \"IN_EDGE(ADD, 3)\\n\" + \"IN_EDGE(REMOVE, 1)\\n\" + \"OUT_EDGE(ADD, 5)\\n\"\n                + \"OUT_EDGE(REMOVE, 3)\\n\" + \"]\";\n\n        assertEquals(goldenString, description.toString());\n    }\n\n    @Test\n    void testChangeDescriptionParentRelations()\n    {\n        final PolyLine polyline1 = PolyLine.wkt(\"LINESTRING(1 1, 2 2, 3 3, 4 4, 5 5)\");\n\n        final CompleteLine before1 = new CompleteLine(123L, polyline1, null,\n                Sets.hashSet(1L, 2L, 3L));\n        final CompleteLine after1 = new CompleteLine(123L, polyline1, null,\n                Sets.hashSet(2L, 3L, 4L));\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, after1, before1);\n        final ChangeDescription description = featureChange1.explain();\n\n        final String goldenString = \"ChangeDescription [\\n\" + \"UPDATE LINE 123\\n\"\n                + \"PARENT_RELATION(ADD, 4)\\n\" + \"PARENT_RELATION(REMOVE, 1)\\n\" + \"]\";\n\n        assertEquals(goldenString, description.toString());\n    }\n\n    @Test\n    void testChangeDescriptionRelationMember()\n    {\n        final CompleteRelation before1 = new CompleteRelation(123L, null, Rectangle.TEST_RECTANGLE,\n                null, null, null, null, null);\n        final CompleteRelation after1 = new CompleteRelation(123L, null, Rectangle.TEST_RECTANGLE,\n                null, null, null, null, null);\n\n        final RelationBean bean1 = new RelationBean();\n        bean1.addItem(123L, \"myRole\", ItemType.AREA);\n        bean1.addItem(456L, \"myRole\", ItemType.AREA);\n        final RelationBean bean2 = new RelationBean();\n        bean2.addItem(456L, \"myRole\", ItemType.AREA);\n        bean2.addItem(789L, \"myRole\", ItemType.AREA);\n\n        before1.withMembers(bean1, Rectangle.TEST_RECTANGLE);\n        after1.withMembers(bean2, Rectangle.TEST_RECTANGLE);\n\n        final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, after1, before1);\n        final ChangeDescription description = featureChange1.explain();\n        System.out.println(description);\n\n        final String goldenString = \"ChangeDescription [\\n\" + \"UPDATE RELATION 123\\n\"\n                + \"RELATION_MEMBER(ADD, AREA, 789, myRole)\\n\"\n                + \"RELATION_MEMBER(REMOVE, AREA, 123, myRole)\\n\" + \"]\";\n\n        assertEquals(goldenString, description.toString());\n    }\n\n    @Test\n    void testChangeDescriptionStartEndNodes()\n    {\n        final PolyLine polyline1 = PolyLine\n                .wkt(\"LINESTRING(1 1, 2 2, 3 3, 10 10, 20 20, 4 4, 5 5)\");\n\n        final CompleteEdge before3 = new CompleteEdge(123L, polyline1, null, 1L, 2L, null);\n        final CompleteEdge after3 = new CompleteEdge(123L, polyline1, null, 10L, 20L, null);\n        final FeatureChange featureChange3 = new FeatureChange(ChangeType.ADD, after3, before3);\n        final ChangeDescription description = featureChange3.explain();\n\n        final String goldenString = \"ChangeDescription [\\n\" + \"UPDATE EDGE 123\\n\"\n                + \"START_NODE(UPDATE, 1 => 10)\\n\" + \"END_NODE(UPDATE, 2 => 20)\\n\" + \"]\";\n\n        assertEquals(goldenString, description.toString());\n    }\n\n    @Test\n    void testChangeDescriptionTag()\n    {\n        final PolyLine polyline1 = PolyLine\n                .wkt(\"LINESTRING(1 1, 2 2, 3 3, 10 10, 20 20, 4 4, 5 5)\");\n\n        final CompleteEdge before3 = new CompleteEdge(123L, polyline1,\n                Maps.hashMap(\"key0\", \"value0\", \"key1\", \"value1\"), null, null, null);\n        final CompleteEdge after3 = new CompleteEdge(123L, polyline1,\n                Maps.hashMap(\"key1\", \"newValue1\", \"key2\", \"value2\"), null, null, null);\n        final FeatureChange featureChange3 = new FeatureChange(ChangeType.ADD, after3, before3);\n        final ChangeDescription description = featureChange3.explain();\n\n        final String goldenString = \"ChangeDescription [\\n\" + \"UPDATE EDGE 123\\n\"\n                + \"TAG(ADD, key2, value2)\\n\" + \"TAG(UPDATE, key1, value1 => newValue1)\\n\"\n                + \"TAG(REMOVE, key0, value0)\\n\" + \"]\";\n\n        assertEquals(goldenString, description.toString());\n    }\n\n    @Test\n    void testExplicitBeforeView()\n    {\n        final Supplier<CompletePoint> completePointGenerator = () -> new CompletePoint(1000000L,\n                Location.CENTER, Collections.emptyMap(), Collections.emptySet());\n        final List<ChangeDescriptor> changeDescriptors = new FeatureChange(ChangeType.ADD,\n                completePointGenerator.get().withAddedTag(NameTag.KEY, \"test\"),\n                completePointGenerator.get()).explain().getChangeDescriptors();\n\n        assertEquals(1, changeDescriptors.size());\n        assertTrue(changeDescriptors.get(0) instanceof TagChangeDescriptor);\n        assertEquals(ChangeDescriptorType.ADD, changeDescriptors.get(0).getChangeDescriptorType());\n    }\n\n    @Test\n    void testShallowValidation()\n    {\n        final CompletePoint before = new CompletePoint(123L, Location.CENTER,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), Sets.hashSet(1L, 2L));\n\n        final CompletePoint after = CompletePoint.shallowFrom(before);\n        final CoreException coreException = assertThrows(CoreException.class,\n                () -> FeatureChange.add(after));\n        assertTrue(coreException.getMessage().contains(\"was shallow\"));\n    }\n\n    @Test\n    void testTags()\n    {\n        final String key = \"key1\";\n        final String value = \"value1\";\n        final Map<String, String> tags = Maps.hashMap(key, value, \"key2\", \"value2\");\n        final FeatureChange featureChange = new FeatureChange(ChangeType.ADD,\n                new CompleteArea(123L, Polygon.CENTER, tags, null));\n        assertEquals(new HashMap<>(tags), featureChange.getTags());\n        assertEquals(value, featureChange.getTag(key).get());\n        assertTrue(featureChange.toString().contains(tags.toString()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/FeatureChangeUnitTestFactory.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\n\n/**\n * A class that exposes the package private {@link FeatureChange} constructor for testing purposes\n * only. This constructor should remain package private since it is not for general use. However,\n * subpackages occasionally have tests that need to access the constructor.\n * \n * @author lcram\n */\npublic final class FeatureChangeUnitTestFactory\n{\n    public static FeatureChange build(final ChangeType type, final AtlasEntity after,\n            final AtlasEntity before)\n    {\n        return new FeatureChange(type, after, before);\n    }\n\n    private FeatureChangeUnitTestFactory()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/MemberMergeStrategiesTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.exception.change.FeatureChangeMergeException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author lcram\n */\npublic class MemberMergeStrategiesTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testConflictingBeforeViewRelationBeanMergeADDREMOVEConflictFail()\n    {\n        final RelationBean beforeBean1 = new RelationBean();\n        beforeBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n\n        final RelationBean afterBean1 = new RelationBean();\n        afterBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n\n        final RelationBean beforeBean2 = new RelationBean();\n        beforeBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n\n        final RelationBean afterBean2 = new RelationBean();\n        afterBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean2.addItemExplicitlyExcluded(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n\n        /*\n         * This will fail with an ADD/REMOVE conflict, since afterBean1 explicitly adds Line 2 while\n         * afterBean2 explicitly removes it. This conflict is only possible in cases where we are\n         * merging RelationBeans with conflicting beforeViews.\n         */\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException\n                .expectMessage(\"conflictingBeforeViewRelationBeanMerger failed due to ADD/REMOVE\");\n        MemberMergeStrategies.conflictingBeforeViewRelationBeanMerger.apply(beforeBean1, afterBean1,\n                beforeBean2, afterBean2);\n    }\n\n    @Test\n    public void testConflictingBeforeViewRelationBeanMergeInconsistentRemovalFail()\n    {\n        final RelationBean beforeBean1 = new RelationBean();\n        beforeBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n\n        final RelationBean afterBean1 = new RelationBean();\n        afterBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n\n        final RelationBean beforeBean2 = new RelationBean();\n        beforeBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n\n        final RelationBean afterBean2 = new RelationBean();\n        afterBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n\n        /*\n         * This will fail because afterBean2 implicitly removes Line 2 without declaring it in the\n         * explicitlyExcluded set. This will corrupt the state of the relation bean and make it\n         * impossible to merge it properly in subsequent operations. Users must always be sure to\n         * use withMembersAndSource when removing members.\n         */\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"explicitlyExcludedRight set did not match the implicitly computed removedFromRight set\");\n        MemberMergeStrategies.conflictingBeforeViewRelationBeanMerger.apply(beforeBean1, afterBean1,\n                beforeBean2, afterBean2);\n    }\n\n    @Test\n    public void testConflictingBeforeViewRelationBeanMergeSuccess()\n    {\n        final RelationBean beforeBean1 = new RelationBean();\n        beforeBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeBean1.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        beforeBean1.addItem(new RelationBeanItem(3L, \"lineRole3\", ItemType.LINE));\n        beforeBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n\n        /*\n         * ADD an Area with ID 3 (which is already present in the other beforeBean - this will\n         * effectively result in a no-op). REMOVE Line with ID 1 - this REMOVE is shared by the\n         * other afterBean. REMOVE Line with ID 2. Add 1 Point with ID 1 - this ADD is shared by the\n         * other afterBean. We ensure that the explicitlyExcluded set is updated with the necessary\n         * REMOVES.\n         */\n        final RelationBean afterBean1 = new RelationBean();\n        afterBean1.addItem(new RelationBeanItem(3L, \"lineRole3\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(3L, \"areaRole3\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean1.addItemExplicitlyExcluded(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean1.addItemExplicitlyExcluded(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n\n        final RelationBean beforeBean2 = new RelationBean();\n        beforeBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        beforeBean2.addItem(new RelationBeanItem(3L, \"lineRole3\", ItemType.LINE));\n        beforeBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        beforeBean2.addItem(new RelationBeanItem(3L, \"areaRole3\", ItemType.AREA));\n        beforeBean2.addItem(new RelationBeanItem(1L, \"edgeRole1\", ItemType.EDGE));\n        beforeBean2.addItem(new RelationBeanItem(2L, \"edgeRole2\", ItemType.EDGE));\n\n        /*\n         * REMOVE Line with ID 1 - this REMOVE is shared by the other afterBean. Add 1 Point with ID\n         * 1 - this ADD is shared by the other afterBean. Remove Edge with ID 1. Add a Node with ID\n         * 1.\n         */\n        final RelationBean afterBean2 = new RelationBean();\n        afterBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        afterBean2.addItem(new RelationBeanItem(3L, \"lineRole3\", ItemType.LINE));\n        afterBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(3L, \"areaRole3\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(2L, \"edgeRole2\", ItemType.EDGE));\n        afterBean2.addItem(new RelationBeanItem(1L, \"nodeRole1\", ItemType.NODE));\n        afterBean2.addItemExplicitlyExcluded(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean2.addItemExplicitlyExcluded(new RelationBeanItem(1L, \"edgeRole1\", ItemType.EDGE));\n\n        /*\n         * The expected result of merging afterBean1 and afterBean2.\n         */\n        final RelationBean goldenImage1 = new RelationBean();\n        goldenImage1.addItem(new RelationBeanItem(3L, \"lineRole3\", ItemType.LINE));\n        goldenImage1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        goldenImage1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        goldenImage1.addItem(new RelationBeanItem(3L, \"areaRole3\", ItemType.AREA));\n        goldenImage1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        goldenImage1.addItem(new RelationBeanItem(2L, \"edgeRole2\", ItemType.EDGE));\n        goldenImage1.addItem(new RelationBeanItem(1L, \"nodeRole1\", ItemType.NODE));\n        goldenImage1\n                .addItemExplicitlyExcluded(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        goldenImage1\n                .addItemExplicitlyExcluded(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        goldenImage1\n                .addItemExplicitlyExcluded(new RelationBeanItem(1L, \"edgeRole1\", ItemType.EDGE));\n\n        Assert.assertTrue(goldenImage1.equalsIncludingExplicitlyExcluded(\n                MemberMergeStrategies.conflictingBeforeViewRelationBeanMerger.apply(beforeBean1,\n                        afterBean1, beforeBean2, afterBean2)));\n    }\n\n    @Test\n    public void testConflictingBeforeViewSetMergerADDREMOVEConflictFail()\n    {\n        final SortedSet<Long> beforeSet1 = Sets.treeSet(1L, 2L, 3L, 4L, 5L);\n        final SortedSet<Long> afterSet1 = Sets.treeSet(1L, 2L, 3L, 4L, 5L, 6L);\n        final Set<Long> explicitlyExcludedSet1 = Sets.hashSet();\n\n        final SortedSet<Long> beforeSet2 = Sets.treeSet(3L, 4L, 5L, 6L);\n        final SortedSet<Long> afterSet2 = Sets.treeSet(3L, 4L, 5L);\n        final Set<Long> explicitlyExcludedSet2 = Sets.hashSet(6L);\n\n        /*\n         * This will fail because the left side added 6L, while the right side explicitly removed\n         * 6L.\n         */\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException\n                .expectMessage(\"conflictingBeforeViewSetMerger failed due to ADD/REMOVE\");\n        MemberMergeStrategies.conflictingBeforeViewSetMerger.apply(beforeSet1, afterSet1,\n                explicitlyExcludedSet1, beforeSet2, afterSet2, explicitlyExcludedSet2);\n    }\n\n    @Test\n    public void testConflictingBeforeViewSetMergerInconsistentRemovalFail()\n    {\n        /*\n         * Left side we explicitly remove 5L.\n         */\n        final SortedSet<Long> beforeSet1 = Sets.treeSet(1L, 2L, 3L, 4L, 5L);\n        final SortedSet<Long> afterSet1 = Sets.treeSet(1L, 2L, 3L, 4L);\n        final Set<Long> explicitlyExcludedSet1 = Sets.hashSet(5L);\n\n        /*\n         * Right side we explicitly remove 3L and add 6L. We also implicitly remove 5L, which will\n         * cause problems when we try to merge.\n         */\n        final SortedSet<Long> beforeSet2 = Sets.treeSet(3L, 4L, 5L);\n        final SortedSet<Long> afterSet2 = Sets.treeSet(4L, 6L);\n        final Set<Long> explicitlyExcludedSet2 = Sets.hashSet(3L);\n\n        /*\n         * This will fail because the right side implicitly removes 5L without declaring it in its\n         * explicitlyExcluded elements.\n         */\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"explicitlyExcludedRight set did not match the implicitly computed removedFromRight set\");\n        MemberMergeStrategies.conflictingBeforeViewSetMerger.apply(beforeSet1, afterSet1,\n                explicitlyExcludedSet1, beforeSet2, afterSet2, explicitlyExcludedSet2);\n    }\n\n    @Test\n    public void testConflictingBeforeViewSetMergerSuccess()\n    {\n        /*\n         * Left side we explicitly remove 5L.\n         */\n        final SortedSet<Long> beforeSet1 = Sets.treeSet(1L, 2L, 3L, 4L, 5L);\n        final SortedSet<Long> afterSet1 = Sets.treeSet(1L, 2L, 3L, 4L);\n        final Set<Long> explicitlyExcludedSet1 = Sets.hashSet(5L);\n\n        /*\n         * Right side we explicitly remove 3L, 5L and add 6L.\n         */\n        final SortedSet<Long> beforeSet2 = Sets.treeSet(3L, 4L, 5L);\n        final SortedSet<Long> afterSet2 = Sets.treeSet(4L, 6L);\n        final Set<Long> explicitlyExcludedSet2 = Sets.hashSet(3L, 5L);\n\n        final SortedSet<Long> goldenAfterSet = Sets.treeSet(1L, 2L, 4L, 6L);\n\n        final SortedSet<Long> mergeResult = MemberMergeStrategies.conflictingBeforeViewSetMerger\n                .apply(beforeSet1, afterSet1, explicitlyExcludedSet1, beforeSet2, afterSet2,\n                        explicitlyExcludedSet2);\n\n        Assert.assertEquals(goldenAfterSet, mergeResult);\n    }\n\n    @Test\n    public void testDiffBasedLocationMergeADDADDConflictFail()\n    {\n        final Location before = Location.CENTER;\n        final Location afterLeft = Location.COLOSSEUM;\n        final Location afterRight = Location.EIFFEL_TOWER;\n\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException.expectMessage(\"mutually exclusive Location merge failed\");\n        MemberMergeStrategies.diffBasedLocationMerger.apply(before, afterLeft, afterRight);\n    }\n\n    @Test\n    public void testDiffBasedLocationMergeSuccess()\n    {\n        final Location before1 = Location.COLOSSEUM;\n        final Location afterLeft1 = Location.COLOSSEUM;\n        final Location afterRight1 = Location.EIFFEL_TOWER;\n\n        Assert.assertEquals(Location.EIFFEL_TOWER, MemberMergeStrategies.diffBasedLocationMerger\n                .apply(before1, afterLeft1, afterRight1));\n\n        final Location before2 = Location.EIFFEL_TOWER;\n        final Location afterLeft2 = Location.COLOSSEUM;\n        final Location afterRight2 = Location.EIFFEL_TOWER;\n\n        Assert.assertEquals(Location.COLOSSEUM, MemberMergeStrategies.diffBasedLocationMerger\n                .apply(before2, afterLeft2, afterRight2));\n\n        final Location before3 = Location.EIFFEL_TOWER;\n        final Location afterLeft3 = Location.COLOSSEUM;\n        final Location afterRight3 = Location.COLOSSEUM;\n\n        Assert.assertEquals(Location.COLOSSEUM, MemberMergeStrategies.diffBasedLocationMerger\n                .apply(before3, afterLeft3, afterRight3));\n    }\n\n    @Test\n    public void testDiffBasedLongMergeADDADDConflictFail()\n    {\n        final Long before = 0L;\n        final Long afterLeft = 1L;\n        final Long afterRight = 2L;\n\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException.expectMessage(\"mutually exclusive Long merge failed\");\n        MemberMergeStrategies.diffBasedLongMerger.apply(before, afterLeft, afterRight);\n    }\n\n    @Test\n    public void testDiffBasedLongMergeSuccess()\n    {\n        final Long before1 = 0L;\n        final Long afterLeft1 = 0L;\n        final Long afterRight1 = 1L;\n\n        Assert.assertEquals(new Long(1L),\n                MemberMergeStrategies.diffBasedLongMerger.apply(before1, afterLeft1, afterRight1));\n\n        final Long before2 = 0L;\n        final Long afterLeft2 = 1L;\n        final Long afterRight2 = 0L;\n\n        Assert.assertEquals(new Long(1L),\n                MemberMergeStrategies.diffBasedLongMerger.apply(before2, afterLeft2, afterRight2));\n\n        final Long before3 = 0L;\n        final Long afterLeft3 = 1L;\n        final Long afterRight3 = 1L;\n\n        Assert.assertEquals(new Long(1L),\n                MemberMergeStrategies.diffBasedLongMerger.apply(before3, afterLeft3, afterRight3));\n    }\n\n    @Test\n    public void testDiffBasedLongSetMergeSuccess()\n    {\n        final Set<Long> before1 = Sets.hashSet(1L, 2L, 3L, 4L);\n        final Set<Long> after1A = Sets.hashSet(2L, 3L, 4L);\n        final Set<Long> after1B = Sets.hashSet(1L, 2L, 3L, 4L, 5L);\n        Assert.assertEquals(Sets.hashSet(2L, 3L, 4L, 5L),\n                MemberMergeStrategies.diffBasedLongSetMerger.apply(before1, after1A, after1B));\n        Assert.assertEquals(Sets.hashSet(2L, 3L, 4L, 5L),\n                MemberMergeStrategies.diffBasedLongSetMerger.apply(before1, after1B, after1A));\n\n        final Set<Long> before2 = Sets.hashSet(1L, 2L, 3L, 4L);\n        final Set<Long> after2A = Sets.hashSet(1L, 2L, 3L, 4L, 5L);\n        final Set<Long> after2B = Sets.hashSet(1L, 2L, 3L, 4L, 6L);\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L, 4L, 5L, 6L),\n                MemberMergeStrategies.diffBasedLongSetMerger.apply(before2, after2A, after2B));\n        Assert.assertEquals(Sets.hashSet(1L, 2L, 3L, 4L, 5L, 6L),\n                MemberMergeStrategies.diffBasedLongSetMerger.apply(before2, after2B, after2A));\n\n        final Set<Long> before3 = Sets.hashSet(1L, 2L, 3L);\n        final Set<Long> after3A = Sets.hashSet();\n        final Set<Long> after3B = Sets.hashSet(1L, 2L, 3L);\n        Assert.assertEquals(Sets.hashSet(),\n                MemberMergeStrategies.diffBasedLongSetMerger.apply(before3, after3A, after3B));\n        Assert.assertEquals(Sets.hashSet(),\n                MemberMergeStrategies.diffBasedLongSetMerger.apply(before3, after3B, after3A));\n    }\n\n    @Test\n    public void testDiffBasedLongSortedSetMergeSuccess()\n    {\n        final SortedSet<Long> before1 = Sets.treeSet(1L, 2L, 3L, 4L);\n        final SortedSet<Long> after1A = Sets.treeSet(2L, 3L, 4L);\n        final SortedSet<Long> after1B = Sets.treeSet(1L, 2L, 3L, 4L, 5L);\n        Assert.assertEquals(Sets.treeSet(2L, 3L, 4L, 5L),\n                MemberMergeStrategies.diffBasedLongSortedSetMerger.apply(before1, after1A,\n                        after1B));\n        Assert.assertEquals(Sets.treeSet(2L, 3L, 4L, 5L),\n                MemberMergeStrategies.diffBasedLongSortedSetMerger.apply(before1, after1B,\n                        after1A));\n\n        final SortedSet<Long> before2 = Sets.treeSet(1L, 2L, 3L, 4L);\n        final SortedSet<Long> after2A = Sets.treeSet(1L, 2L, 3L, 4L, 5L);\n        final SortedSet<Long> after2B = Sets.treeSet(1L, 2L, 3L, 4L, 6L);\n        Assert.assertEquals(Sets.treeSet(1L, 2L, 3L, 4L, 5L, 6L),\n                MemberMergeStrategies.diffBasedLongSortedSetMerger.apply(before2, after2A,\n                        after2B));\n        Assert.assertEquals(Sets.treeSet(1L, 2L, 3L, 4L, 5L, 6L),\n                MemberMergeStrategies.diffBasedLongSortedSetMerger.apply(before2, after2B,\n                        after2A));\n\n        final SortedSet<Long> before3 = Sets.treeSet(1L, 2L, 3L);\n        final SortedSet<Long> after3A = Sets.treeSet();\n        final SortedSet<Long> after3B = Sets.treeSet(1L, 2L, 3L);\n        Assert.assertEquals(Sets.treeSet(), MemberMergeStrategies.diffBasedLongSortedSetMerger\n                .apply(before3, after3A, after3B));\n        Assert.assertEquals(Sets.treeSet(), MemberMergeStrategies.diffBasedLongSortedSetMerger\n                .apply(before3, after3B, after3A));\n    }\n\n    @Test\n    public void testDiffBasedPolyLineMergeADDADDConflictFail()\n    {\n        final PolyLine before = PolyLine.TEST_POLYLINE;\n        final PolyLine afterLeft = PolyLine.TEST_POLYLINE_2;\n        final PolyLine afterRight = PolyLine.CENTER;\n\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException.expectMessage(\"mutually exclusive PolyLine merge failed\");\n        MemberMergeStrategies.diffBasedPolyLineMerger.apply(before, afterLeft, afterRight);\n    }\n\n    @Test\n    public void testDiffBasedPolyLineMergeSuccess()\n    {\n        final PolyLine before1 = PolyLine.CENTER;\n        final PolyLine afterLeft1 = PolyLine.CENTER;\n        final PolyLine afterRight1 = PolyLine.TEST_POLYLINE;\n\n        Assert.assertEquals(PolyLine.TEST_POLYLINE, MemberMergeStrategies.diffBasedPolyLineMerger\n                .apply(before1, afterLeft1, afterRight1));\n\n        final PolyLine before2 = PolyLine.CENTER;\n        final PolyLine afterLeft2 = PolyLine.TEST_POLYLINE;\n        final PolyLine afterRight2 = PolyLine.CENTER;\n\n        Assert.assertEquals(PolyLine.TEST_POLYLINE, MemberMergeStrategies.diffBasedPolyLineMerger\n                .apply(before2, afterLeft2, afterRight2));\n\n        final PolyLine before3 = PolyLine.CENTER;\n        final PolyLine afterLeft3 = PolyLine.TEST_POLYLINE;\n        final PolyLine afterRight3 = PolyLine.TEST_POLYLINE;\n\n        Assert.assertEquals(PolyLine.TEST_POLYLINE, MemberMergeStrategies.diffBasedPolyLineMerger\n                .apply(before3, afterLeft3, afterRight3));\n    }\n\n    @Test\n    public void testDiffBasedPolygonMergeADDADDConflictFail()\n    {\n        final Polygon before = Polygon.CENTER;\n        final Polygon afterLeft = Polygon.SILICON_VALLEY;\n        final Polygon afterRight = Polygon.SILICON_VALLEY_2;\n\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException.expectMessage(\"mutually exclusive Polygon merge failed\");\n        MemberMergeStrategies.diffBasedPolygonMerger.apply(before, afterLeft, afterRight);\n    }\n\n    @Test\n    public void testDiffBasedPolygonMergeSuccess()\n    {\n        final Polygon before1 = Polygon.CENTER;\n        final Polygon afterLeft1 = Polygon.CENTER;\n        final Polygon afterRight1 = Polygon.SILICON_VALLEY;\n\n        Assert.assertEquals(Polygon.SILICON_VALLEY, MemberMergeStrategies.diffBasedPolygonMerger\n                .apply(before1, afterLeft1, afterRight1));\n\n        final Polygon before2 = Polygon.CENTER;\n        final Polygon afterLeft2 = Polygon.SILICON_VALLEY;\n        final Polygon afterRight2 = Polygon.CENTER;\n\n        Assert.assertEquals(Polygon.SILICON_VALLEY, MemberMergeStrategies.diffBasedPolygonMerger\n                .apply(before2, afterLeft2, afterRight2));\n\n        final Polygon before3 = Polygon.CENTER;\n        final Polygon afterLeft3 = Polygon.SILICON_VALLEY;\n        final Polygon afterRight3 = Polygon.SILICON_VALLEY;\n\n        Assert.assertEquals(Polygon.SILICON_VALLEY, MemberMergeStrategies.diffBasedPolygonMerger\n                .apply(before3, afterLeft3, afterRight3));\n    }\n\n    @Test\n    public void testDiffBasedRelationBeanMergeADDADDConflict()\n    {\n        final RelationBean beforeBean = new RelationBean();\n        beforeBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n\n        /*\n         * Add one instance of [2, AREA, areaRole2].\n         */\n        final RelationBean afterBean1 = new RelationBean();\n        afterBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n\n        /*\n         * Add two instances of [2, AREA, areaRole2].\n         */\n        final RelationBean afterBean2 = new RelationBean();\n        afterBean2.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n\n        /*\n         * The merge will fail, since the number of added [2, AREA, areaRole2] conflict.\n         */\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException.expectMessage(\n                \"diffBasedRelationBeanMerger failed due to ADD/ADD conflict on key: [[AREA, 2, areaRole2]]: beforeValue absolute count was 0 but addedLeft/Right diff counts conflict [1 vs 2]\");\n        MemberMergeStrategies.diffBasedRelationBeanMerger.apply(beforeBean, afterBean1, afterBean2);\n    }\n\n    @Test\n    public void testDiffBasedRelationBeanMergeADDREMOVEConflictFail()\n    {\n        final RelationBean beforeBean = new RelationBean();\n        beforeBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n\n        /*\n         * Add an additional instance of [1, LINE, lineRole1].\n         */\n        final RelationBean afterBean1 = new RelationBean();\n        afterBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n\n        /*\n         * Remove the instance of [1, LINE, lineRole1].\n         */\n        final RelationBean afterBean2 = new RelationBean();\n        afterBean2.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean2.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean2.addItemExplicitlyExcluded(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n\n        /*\n         * The merge will fail, since one afterView tries to add an additional [1, LINE, lineRole1]\n         * while the other afterView removes it entirely.\n         */\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException.expectMessage(\n                \"diffBasedRelationBeanMerger failed due to ADD/REMOVE conflict(s) on key(s): [[LINE, 1, lineRole1]]\");\n        MemberMergeStrategies.diffBasedRelationBeanMerger.apply(beforeBean, afterBean1, afterBean2);\n    }\n\n    @Test\n    public void testDiffBasedRelationBeanMergeREMOVEREMOVEConflictFail()\n    {\n        final RelationBean beforeBean = new RelationBean();\n        beforeBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        beforeBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n\n        /*\n         * Remove one instance of [1, AREA, areaRole1].\n         */\n        final RelationBean afterBean1 = new RelationBean();\n        afterBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n\n        /*\n         * Remove both instances of [1, AREA, areaRole1].\n         */\n        final RelationBean afterBean2 = new RelationBean();\n        afterBean2.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean2.addItemExplicitlyExcluded(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n\n        /*\n         * The merge will fail, since the number of removed [1, AREA, areaRole1] conflict.\n         */\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException.expectMessage(\n                \"diffBasedRelationBeanMerger failed due to REMOVE/REMOVE conflict on key: [[AREA, 1, areaRole1]]: beforeValue absolute count was 2 but removedLeft/Right diff counts conflict [1 vs 2]\");\n        MemberMergeStrategies.diffBasedRelationBeanMerger.apply(beforeBean, afterBean1, afterBean2);\n    }\n\n    @Test\n    public void testDiffBasedRelationBeanMergeSuccess()\n    {\n        final RelationBean beforeBean = new RelationBean();\n        beforeBean.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n        beforeBean.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        beforeBean.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n\n        /*\n         * ADD two Areas with ID 2. REMOVE Point with ID 2. Add 2 Lines with ID 2 - this ADD is\n         * shared by the other afterBean. REMOVE Point with ID 3 - this REMOVE is shared by the\n         * other afterBean. We ensure that the explicitlyExcluded set is updated with the necessary\n         * REMOVES.\n         */\n        final RelationBean afterBean1 = new RelationBean();\n        afterBean1.addItem(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        afterBean1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        afterBean1\n                .addItemExplicitlyExcluded(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterBean1\n                .addItemExplicitlyExcluded(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n\n        /*\n         * Change role for Point with ID 1. This effectively REMOVEs the original [1, POINT,\n         * pointRole1] and replaces it with [1, POINT, newPointRole1]. Add 2 Lines with ID 2 - this\n         * ADD is shared by the other afterBean. REMOVE Point with ID 3 - this REMOVE is shared by\n         * the other afterBean. We ensure that the explicitlyExcluded set is updated with the\n         * necessary REMOVES.\n         */\n        final RelationBean afterBean2 = new RelationBean();\n        afterBean2.addItem(new RelationBeanItem(1L, \"newPointRole1\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        afterBean2.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        afterBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        afterBean2.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        afterBean2.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        afterBean2\n                .addItemExplicitlyExcluded(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        afterBean2\n                .addItemExplicitlyExcluded(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n\n        /*\n         * The expected result of merging afterBean1 and afterBean2.\n         */\n        final RelationBean goldenImage1 = new RelationBean();\n        goldenImage1.addItem(new RelationBeanItem(1L, \"newPointRole1\", ItemType.POINT));\n        goldenImage1.addItem(new RelationBeanItem(1L, \"areaRole1\", ItemType.AREA));\n        goldenImage1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        goldenImage1.addItem(new RelationBeanItem(2L, \"areaRole2\", ItemType.AREA));\n        goldenImage1.addItem(new RelationBeanItem(1L, \"lineRole1\", ItemType.LINE));\n        goldenImage1.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        goldenImage1.addItem(new RelationBeanItem(2L, \"lineRole2\", ItemType.LINE));\n        goldenImage1\n                .addItemExplicitlyExcluded(new RelationBeanItem(1L, \"pointRole1\", ItemType.POINT));\n        goldenImage1\n                .addItemExplicitlyExcluded(new RelationBeanItem(2L, \"pointRole2\", ItemType.POINT));\n        goldenImage1\n                .addItemExplicitlyExcluded(new RelationBeanItem(3L, \"pointRole3\", ItemType.POINT));\n\n        Assert.assertTrue(goldenImage1\n                .equalsIncludingExplicitlyExcluded(MemberMergeStrategies.diffBasedRelationBeanMerger\n                        .apply(beforeBean, afterBean1, afterBean2)));\n    }\n\n    @Test\n    public void testDiffBasedTagMergeADDADDConflictFail()\n    {\n        final Map<String, String> before1 = Maps.hashMap(\"a\", \"1\", \"b\", \"2\");\n        final Map<String, String> after1A = Maps.hashMap(\"a\", \"10\", \"b\", \"2\");\n        final Map<String, String> after1B = Maps.hashMap(\"a\", \"12\", \"b\", \"2\");\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException\n                .expectMessage(\"diffBasedTagMerger failed due to ADD/ADD conflict on keys\");\n        MemberMergeStrategies.diffBasedTagMerger.apply(before1, after1A, after1B);\n    }\n\n    @Test\n    public void testDiffBasedTagMergeADDREMOVEConflictFail()\n    {\n        final Map<String, String> before1 = Maps.hashMap(\"a\", \"1\", \"b\", \"2\");\n        final Map<String, String> after1A = Maps.hashMap(\"a\", \"10\", \"b\", \"2\");\n        final Map<String, String> after1B = Maps.hashMap(\"b\", \"2\");\n\n        this.expectedException.expect(FeatureChangeMergeException.class);\n        this.expectedException.expectMessage(\n                \"diffBasedTagMerger failed due to ADD/REMOVE conflict(s) on key(s): [a]\");\n        MemberMergeStrategies.diffBasedTagMerger.apply(before1, after1A, after1B);\n    }\n\n    @Test\n    public void testDiffBasedTagMergeSuccess()\n    {\n        final Map<String, String> before1 = Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\", \"d\", \"4\");\n        final Map<String, String> after1A = Maps.hashMap(\"b\", \"2\", \"c\", \"3\", \"d\", \"4\");\n        final Map<String, String> after1B = Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\", \"d\", \"4\",\n                \"e\", \"5\");\n        Assert.assertEquals(Maps.hashMap(\"b\", \"2\", \"c\", \"3\", \"d\", \"4\", \"e\", \"5\"),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before1, after1A, after1B));\n        Assert.assertEquals(Maps.hashMap(\"b\", \"2\", \"c\", \"3\", \"d\", \"4\", \"e\", \"5\"),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before1, after1B, after1A));\n\n        final Map<String, String> before2 = Maps.hashMap(\"water\", \"lake\");\n        final Map<String, String> after2A = Maps.hashMap(\"water\", \"lake\", \"seasonal\", \"yes\");\n        final Map<String, String> after2B = Maps.hashMap(\"water\", \"lake\", \"salt\", \"yes\");\n        Assert.assertEquals(Maps.hashMap(\"water\", \"lake\", \"seasonal\", \"yes\", \"salt\", \"yes\"),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before2, after2A, after2B));\n        Assert.assertEquals(Maps.hashMap(\"water\", \"lake\", \"seasonal\", \"yes\", \"salt\", \"yes\"),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before2, after2B, after2A));\n\n        final Map<String, String> before3 = Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\", \"d\", \"4\");\n        final Map<String, String> after3A = Maps.hashMap();\n        final Map<String, String> after3B = Maps.hashMap(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\", \"d\", \"4\");\n        Assert.assertEquals(Maps.hashMap(),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before3, after3A, after3B));\n        Assert.assertEquals(Maps.hashMap(),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before3, after3B, after3A));\n\n        final Map<String, String> before4 = Maps.hashMap();\n        final Map<String, String> after4A = Maps.hashMap(\"a\", \"1\");\n        final Map<String, String> after4B = Maps.hashMap(\"b\", \"2\");\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before4, after4A, after4B));\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\", \"b\", \"2\"),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before4, after4B, after4A));\n\n        final Map<String, String> before5 = Maps.hashMap();\n        final Map<String, String> after5A = Maps.hashMap(\"a\", \"1\");\n        final Map<String, String> after5B = Maps.hashMap(\"a\", \"1\");\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\"),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before5, after5A, after5B));\n        Assert.assertEquals(Maps.hashMap(\"a\", \"1\"),\n                MemberMergeStrategies.diffBasedTagMerger.apply(before5, after5B, after5A));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/MultiCascadeDeleteTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.diff.AtlasDiff;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * There are 2 edges AB and BC. Node B is common between the 2 edges.\n *\n * @author Yazad Khambata\n */\npublic class MultiCascadeDeleteTest\n{\n    private static final Logger log = LoggerFactory.getLogger(MultiCascadeDeleteTest.class);\n\n    @Rule\n    public final MultiCascadeDeleteTestRule rule = new MultiCascadeDeleteTestRule();\n\n    @Test\n    public void deleteEdgeAB()\n    {\n        final Atlas atlas = originalAtlas();\n\n        // Step-1: Delete edgeAB\n        final ItemType itemType = ItemType.EDGE;\n        final Long entityIdToDelete = MultiCascadeDeleteTestRule.edgeAB;\n        final int expectedNodes = 3;\n        final int expectedEdges = 1;\n\n        final Atlas changeAtlas = changeAtlasDeletingFeature(atlas, itemType, entityIdToDelete,\n                expectedNodes, expectedEdges);\n\n        // Step-2: check if Nodes A and B in / out edges have changed.\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeA).outEdges().isEmpty());\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeB).inEdges().isEmpty());\n        Assert.assertFalse(changeAtlas.node(MultiCascadeDeleteTestRule.nodeB).outEdges().isEmpty());\n        Assert.assertFalse(changeAtlas.node(MultiCascadeDeleteTestRule.nodeC).inEdges().isEmpty());\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeC).outEdges().isEmpty());\n        final Relation relation = changeAtlas.relation(MultiCascadeDeleteTestRule.relationX);\n        Assert.assertNotNull(relation);\n        Assert.assertEquals(1, relation.membersOfType(ItemType.EDGE).size());\n        Assert.assertEquals(1, relation.membersOfType(ItemType.NODE).size());\n\n        // Step-3 Verify AtlasDiff\n        final Map<AtlasEntityKey, Boolean> expectedChangedAndDeleted = new HashMap<AtlasEntityKey, Boolean>()\n        {\n            private static final long serialVersionUID = 454060048188157314L;\n\n            {\n                put(AtlasEntityKey.from(ItemType.NODE, MultiCascadeDeleteTestRule.nodeA), false);\n                put(AtlasEntityKey.from(ItemType.NODE, MultiCascadeDeleteTestRule.nodeB), false);\n                put(AtlasEntityKey.from(ItemType.EDGE, MultiCascadeDeleteTestRule.edgeAB), true);\n                put(AtlasEntityKey.from(ItemType.RELATION, MultiCascadeDeleteTestRule.relationX),\n                        false);\n            }\n        };\n\n        verifyAtlasDiff(atlas, changeAtlas, expectedChangedAndDeleted);\n    }\n\n    @Test\n    public void deleteNodeA()\n    {\n        final Atlas atlas = originalAtlas();\n\n        // Step-1: Delete edgeAB\n        final ItemType itemType = ItemType.NODE;\n        final Long entityIdToDelete = MultiCascadeDeleteTestRule.nodeA;\n        final int expectedNodes = 2;\n        final int expectedEdges = 1;\n\n        final Atlas changeAtlas = changeAtlasDeletingFeature(atlas, itemType, entityIdToDelete,\n                expectedNodes, expectedEdges);\n\n        // Step-2: check if Nodes A and B in / out edges have changed.\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeB).inEdges().isEmpty());\n        Assert.assertFalse(changeAtlas.node(MultiCascadeDeleteTestRule.nodeB).outEdges().isEmpty());\n        Assert.assertFalse(changeAtlas.node(MultiCascadeDeleteTestRule.nodeC).inEdges().isEmpty());\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeC).outEdges().isEmpty());\n        final Relation relation = changeAtlas.relation(MultiCascadeDeleteTestRule.relationX);\n        Assert.assertNotNull(relation);\n        Assert.assertEquals(1, relation.membersOfType(ItemType.EDGE).size());\n        Assert.assertEquals(1, relation.membersOfType(ItemType.NODE).size());\n\n        // Step-3 Verify AtlasDiff\n        final Map<AtlasEntityKey, Boolean> expectedChangedAndDeleted = new HashMap<AtlasEntityKey, Boolean>()\n        {\n            private static final long serialVersionUID = 3346327267408295085L;\n\n            {\n                put(AtlasEntityKey.from(ItemType.NODE, MultiCascadeDeleteTestRule.nodeA), true);\n                put(AtlasEntityKey.from(ItemType.NODE, MultiCascadeDeleteTestRule.nodeB), false);\n                put(AtlasEntityKey.from(ItemType.EDGE, MultiCascadeDeleteTestRule.edgeAB), true);\n                put(AtlasEntityKey.from(ItemType.RELATION, MultiCascadeDeleteTestRule.relationX),\n                        false);\n            }\n        };\n\n        verifyAtlasDiff(atlas, changeAtlas, expectedChangedAndDeleted);\n    }\n\n    @Test\n    public void deleteNodeB()\n    {\n        final Atlas atlas = originalAtlas();\n\n        // Step-1: Delete edgeAB\n        final ItemType itemType = ItemType.NODE;\n        final Long entityIdToDelete = MultiCascadeDeleteTestRule.nodeB;\n        final int expectedNodes = 2;\n        final int expectedEdges = 0;\n\n        final Atlas changeAtlas = changeAtlasDeletingFeature(atlas, itemType, entityIdToDelete,\n                expectedNodes, expectedEdges);\n\n        // Step-2: check if Nodes A and B in / out edges have changed.\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeA).inEdges().isEmpty());\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeA).outEdges().isEmpty());\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeC).inEdges().isEmpty());\n        Assert.assertTrue(changeAtlas.node(MultiCascadeDeleteTestRule.nodeC).outEdges().isEmpty());\n        final Relation relation = changeAtlas.relation(MultiCascadeDeleteTestRule.relationX);\n        Assert.assertNull(relation);\n\n        // Step-3 Verify AtlasDiff\n        final Map<AtlasEntityKey, Boolean> expectedChangedAndDeleted = new HashMap<AtlasEntityKey, Boolean>()\n        {\n            private static final long serialVersionUID = -8963613354545135623L;\n\n            {\n                put(AtlasEntityKey.from(ItemType.NODE, MultiCascadeDeleteTestRule.nodeA), false);\n                put(AtlasEntityKey.from(ItemType.NODE, MultiCascadeDeleteTestRule.nodeB), true);\n                put(AtlasEntityKey.from(ItemType.NODE, MultiCascadeDeleteTestRule.nodeC), false);\n                put(AtlasEntityKey.from(ItemType.EDGE, MultiCascadeDeleteTestRule.edgeAB), true);\n                put(AtlasEntityKey.from(ItemType.EDGE, MultiCascadeDeleteTestRule.edgeBC), true);\n                put(AtlasEntityKey.from(ItemType.RELATION, MultiCascadeDeleteTestRule.relationX),\n                        true);\n            }\n        };\n\n        verifyAtlasDiff(atlas, changeAtlas, expectedChangedAndDeleted);\n    }\n\n    private Atlas changeAtlasDeletingFeature(final Atlas atlas, final ItemType itemType,\n            final Long entityIdToDelete, final int expectedNodes, final int expectedEdges)\n    {\n        final FeatureChange featureChange = createDeleteFeatureChange(atlas, itemType,\n                entityIdToDelete);\n        return changedAtlas(atlas, featureChange, expectedNodes, expectedEdges);\n    }\n\n    private Atlas changedAtlas(final Atlas atlas, final FeatureChange featureChange,\n            final long expectedNodes, final long expectedEdges)\n    {\n        final Change change = ChangeBuilder.newInstance().add(featureChange).get();\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        Assert.assertEquals(expectedNodes, changeAtlas.numberOfNodes());\n        Assert.assertEquals(expectedEdges, changeAtlas.numberOfEdges());\n        return changeAtlas;\n    }\n\n    private FeatureChange createDeleteFeatureChange(final Atlas atlas, final ItemType itemType,\n            final Long entityIdToDelete)\n    {\n        return FeatureChange.remove(CompleteItemType\n                .shallowFrom(itemType.entityForIdentifier(atlas, entityIdToDelete)));\n    }\n\n    private Atlas originalAtlas()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        Assert.assertEquals(3, atlas.numberOfNodes());\n        Assert.assertEquals(2, atlas.numberOfEdges());\n        Assert.assertEquals(1, atlas.numberOfRelations());\n        return atlas;\n    }\n\n    private void verifyAtlasDiff(final Atlas originalAtlas, final Atlas changeAtlas,\n            final Map<AtlasEntityKey, Boolean> expectedChangedAndDeleted)\n    {\n        final AtlasDiff atlasDiff = new AtlasDiff(originalAtlas, changeAtlas);\n        final Optional<Change> optionalChangeFromDiff = atlasDiff.generateChange();\n        Assert.assertTrue(optionalChangeFromDiff.isPresent());\n        final Change changeFromDiff = optionalChangeFromDiff.get();\n\n        final Map<AtlasEntityKey, FeatureChange> atlasEntityKeyFeatureChangeMap = changeFromDiff\n                .allChangesMappedByAtlasEntityKey();\n\n        atlasEntityKeyFeatureChangeMap.entrySet().stream().forEach(entry ->\n        {\n            log.info(\"{} : {}\", entry.getKey(), entry.getValue());\n        });\n\n        Assert.assertEquals(expectedChangedAndDeleted.size(),\n                atlasEntityKeyFeatureChangeMap.size());\n\n        expectedChangedAndDeleted.entrySet().stream().forEach(expectedEntry ->\n        {\n            Assert.assertNotNull(atlasEntityKeyFeatureChangeMap.get(expectedEntry.getKey()));\n\n            final AtlasEntity changedAtlasEntity = expectedEntry.getKey()\n                    .getAtlasEntity(changeAtlas);\n            Assert.assertTrue((changedAtlasEntity == null) == expectedEntry.getValue());\n        });\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/MultiCascadeDeleteTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author Yazad Khambata\n */\npublic class MultiCascadeDeleteTestRule extends CoreTestRule\n{\n    private static final String LOC_A = \"1,1\";\n    private static final String LOC_B = \"2,2\";\n    private static final String LOC_C = \"3,3\";\n\n    public static final String strNodeA = \"1\";\n    public static final String strNodeB = \"2\";\n    public static final String strNodeC = \"3\";\n    public static final String strEdgeAB = \"1\";\n    public static final String strEdgeBC = \"2\";\n    public static final String strRelationX = \"1\";\n\n    public static final Long nodeA = Long.valueOf(strNodeA);\n    public static final Long nodeB = Long.valueOf(strNodeB);\n    public static final Long nodeC = Long.valueOf(strNodeC);\n\n    public static final Long edgeAB = Long.valueOf(strEdgeAB);\n    public static final Long edgeBC = Long.valueOf(strEdgeBC);\n\n    public static final Long relationX = Long.valueOf(strRelationX);\n\n    @TestAtlas(nodes = {\n            @TestAtlas.Node(id = strNodeA, coordinates = @TestAtlas.Loc(value = LOC_A)),\n            @TestAtlas.Node(id = strNodeB, coordinates = @TestAtlas.Loc(value = LOC_B)),\n            @TestAtlas.Node(id = strNodeC, coordinates = @TestAtlas.Loc(value = LOC_C)), },\n\n            edges = {\n                    @TestAtlas.Edge(id = strEdgeAB, coordinates = { @TestAtlas.Loc(value = LOC_A),\n                            @TestAtlas.Loc(value = LOC_B) }),\n                    @TestAtlas.Edge(id = strEdgeBC, coordinates = { @TestAtlas.Loc(value = LOC_B),\n                            @TestAtlas.Loc(value = LOC_C) }), },\n\n            relations = { @TestAtlas.Relation(id = strRelationX, members = {\n                    @TestAtlas.Relation.Member(id = strEdgeAB, role = \"x\", type = \"edge\"),\n                    @TestAtlas.Relation.Member(id = strEdgeBC, role = \"y\", type = \"edge\"),\n                    @TestAtlas.Relation.Member(id = strNodeB, role = \"y\", type = \"node\"), }) })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/MultipleChangeAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.change.testing.AtlasChangeGeneratorAddTurnRestrictions;\nimport org.openstreetmap.atlas.geography.atlas.change.testing.AtlasChangeGeneratorRemoveReverseEdges;\nimport org.openstreetmap.atlas.geography.atlas.change.testing.AtlasChangeGeneratorSplitRoundabout;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.tags.JunctionTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * Test various use cases where multiple connected features change.\n *\n * @author matthieun\n */\npublic class MultipleChangeAtlasTest\n{\n    @Rule\n    public MultipleChangeAtlasTestRule rule = new MultipleChangeAtlasTestRule();\n\n    private Atlas atlas;\n    private Atlas subAtlas;\n    private ChangeAtlas changeAtlas;\n    private final String saveLocally = null;\n\n    @Test\n    public void addTurnRestrictions()\n    {\n        resetAndChange(\"addTurnRestrictions\", new AtlasChangeGeneratorAddTurnRestrictions());\n        final Node via = this.changeAtlas.node(3985226613000000L);\n        final Relation restriction = via.relations().iterator().next();\n        Assert.assertNotNull(restriction);\n        final RelationBean members = new RelationBean();\n        members.addItem(221434099000002L, \"from\", ItemType.EDGE);\n        members.addItem(via.getIdentifier(), \"via\", ItemType.NODE);\n        members.addItem(634444999000000L, \"to\", ItemType.EDGE);\n        members.addItem(-634444999000000L, \"to\", ItemType.EDGE);\n        Assert.assertEquals(members, restriction.members().asBean());\n    }\n\n    @Test\n    public void allEdgesAreStraight()\n    {\n        final Predicate<Edge> straight = edge -> edge.asPolyLine().size() == 2;\n        resetAndChange(\"allEdgesAreStraight\", atlas ->\n        {\n            return Iterables.stream(atlas.edges()).filter(straight.negate()).map(edge ->\n            {\n                return CompleteEdge.shallowFrom(edge).withPolyLine(\n                        new PolyLine(edge.start().getLocation(), edge.end().getLocation()));\n            }).map(completeEdge -> FeatureChange.add(completeEdge, this.atlas)).collectToSet();\n        });\n        final long straightEdges = Iterables.size(this.changeAtlas.edges(straight));\n        final long originalAtlasStraightEdges = Iterables.size(this.atlas.edges(straight));\n        final long subAtlasStraightEdges = Iterables.size(this.subAtlas.edges(straight));\n        Assert.assertEquals(428, straightEdges);\n        Assert.assertEquals(337, originalAtlasStraightEdges);\n        Assert.assertEquals(266, subAtlasStraightEdges);\n        Assert.assertEquals(straightEdges - originalAtlasStraightEdges + subAtlasStraightEdges,\n                this.subAtlas.numberOfEdges());\n    }\n\n    @Test\n    public void allNodesAreTrafficLights()\n    {\n        resetAndChange(\"allNodesAreTrafficLights\", atlas ->\n        {\n            return Iterables.stream(atlas.nodes())\n                    .map(node -> CompleteNode.shallowFrom(node).withAddedTag(\"highway\",\n                            \"traffic_signals\"))\n                    .map(completeNode -> FeatureChange.add(completeNode, this.atlas))\n                    .collectToSet();\n        });\n        final Predicate<Node> trafficSignal = node -> \"traffic_signals\".equals(node.tag(\"highway\"));\n        final long changeAtlasNodesWithTrafficSignals = Iterables\n                .size(this.changeAtlas.nodes(trafficSignal));\n        final long originalAtlasNodesWithTrafficSignals = Iterables\n                .size(this.atlas.nodes(trafficSignal));\n        final long subAtlasNodesWithTrafficSignals = Iterables\n                .size(this.subAtlas.nodes(trafficSignal));\n        Assert.assertEquals(162, changeAtlasNodesWithTrafficSignals);\n        Assert.assertEquals(9, originalAtlasNodesWithTrafficSignals);\n        Assert.assertEquals(4, subAtlasNodesWithTrafficSignals);\n        Assert.assertEquals(changeAtlasNodesWithTrafficSignals\n                - originalAtlasNodesWithTrafficSignals + subAtlasNodesWithTrafficSignals,\n                this.subAtlas.numberOfNodes());\n    }\n\n    @Test\n    public void removeAllReverseEdges()\n    {\n        resetAndChange(\"removeAllReverseEdges\", new AtlasChangeGeneratorRemoveReverseEdges());\n        final long changeAtlasReverseEdges = Iterables\n                .size(this.changeAtlas.edges(edge -> !edge.isMainEdge()));\n        final long subAtlasReverseEdges = Iterables\n                .size(this.subAtlas.edges(edge -> !edge.isMainEdge()));\n        final long atlasReverseEdges = Iterables.size(this.atlas.edges(edge -> !edge.isMainEdge()));\n        Assert.assertEquals(48, changeAtlasReverseEdges);\n        Assert.assertEquals(166, subAtlasReverseEdges);\n        // The reverse edges from the subAtlas (marked for removal) plus the ones from the\n        // changeAtlas = all the initial reverse edges.\n        Assert.assertEquals(changeAtlasReverseEdges + subAtlasReverseEdges, atlasReverseEdges);\n    }\n\n    @Test\n    public void splitRoundaboutEdges()\n    {\n        resetAndChange(\"splitRoundaboutEdges\", new AtlasChangeGeneratorSplitRoundabout());\n        Assert.assertEquals(6, Iterables.size(this.atlas.edges(JunctionTag::isRoundabout)));\n        Assert.assertEquals(12, Iterables.size(this.changeAtlas.edges(JunctionTag::isRoundabout)));\n        final Set<Long> extectedParentRelations = this.atlas.edge(221434104000005L).relations()\n                .stream().map(Relation::getIdentifier).collect(Collectors.toSet());\n        Assert.assertEquals(extectedParentRelations, this.changeAtlas.edge(14L).relations().stream()\n                .map(Relation::getIdentifier).collect(Collectors.toSet()));\n        Assert.assertEquals(extectedParentRelations, this.changeAtlas.edge(15L).relations().stream()\n                .map(Relation::getIdentifier).collect(Collectors.toSet()));\n        Assert.assertTrue(this.changeAtlas.relation(3001321000000L).members().asBean()\n                .getItemFor(14L, ItemType.EDGE).isPresent());\n        Assert.assertTrue(this.changeAtlas.relation(3001321000000L).members().asBean()\n                .getItemFor(15L, ItemType.EDGE).isPresent());\n        Assert.assertTrue(this.atlas.relation(3001321000000L).members().asBean()\n                .getItemFor(221434104000005L, ItemType.EDGE).isPresent());\n        // Make sure the removed edge was not added back by a mishap in the relation bean merging\n        // somewhere\n        Assert.assertFalse(this.changeAtlas.relation(3001321000000L).members().asBean()\n                .getItemFor(221434104000005L, ItemType.EDGE).isPresent());\n    }\n\n    /**\n     * Get the original test Atlas. Cut it smaller to a subAtlas, and use that smaller Atlas to\n     * generate the changes. Apply the changes back to the original atlas.\n     *\n     * @param name\n     *            The Atlas name for debugging\n     * @param modificationsFunction\n     *            The function generating the changes.\n     */\n    private void resetAndChange(final String name, final AtlasChangeGenerator atlasChangeGenerator)\n    {\n        this.atlas = this.rule.getAtlas();\n        saveLocally(\"original.atlas\", this.atlas);\n        this.subAtlas = this.atlas.subAtlas(this.atlas.bounds().contract(Distance.meters(500)),\n                AtlasCutType.HARD_CUT_ALL).get();\n        saveLocally(\"sub.atlas\", this.subAtlas);\n        final Set<FeatureChange> featureChanges = atlasChangeGenerator.apply(this.subAtlas);\n        final ChangeBuilder builder = new ChangeBuilder();\n        featureChanges.forEach(builder::add);\n        final Change change = builder.get();\n        this.changeAtlas = new ChangeAtlas(this.atlas, change);\n        saveLocally(name + \"_change.atlas\", this.changeAtlas.cloneToPackedAtlas());\n    }\n\n    private void saveLocally(final String name, final Atlas argument)\n    {\n        if (this.saveLocally != null)\n        {\n            final File folder = new File(this.saveLocally);\n            argument.save(folder.child(name));\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/MultipleChangeAtlasTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class MultipleChangeAtlasTestRule extends CoreTestRule\n{\n    // MultipleChangeAtlasTest.osm is the osm file from which MultipleChangeAtlasTest.atlas.txt was\n    // created.\n    // @TestAtlas(loadFromTextResource = \"MultipleChangeAtlasTest.osm\")\n    @TestAtlas(loadFromTextResource = \"MultipleChangeAtlasTest.atlas.txt\")\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/TagChangeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.exception.change.FeatureChangeMergeException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Yazad Khambata\n */\npublic class TagChangeTest\n{\n    private static final String KEY_MARS = \"mars\";\n    private static final String KEY_OPPORTUNITY = \"opportunity\";\n    private static final String VALUE_ROVER = \"rover\";\n    private static final String KEY_SINGLETON = \"singleton\";\n    private static final String VALUE_ONE = \"ONE\";\n    private static final Logger log = LoggerFactory.getLogger(TagChangeTest.class);\n    private static final String ADDED_TAG_KEY = \"added\";\n    private static final String ADDED_TAG_VALUE_1 = \"this\";\n    private static final String ADDED_TAG_VALUE_2 = \"that\";\n    private static final String ADDED_TAG_VALUE_BLANK = \"\";\n    private static final String ADDED_TAG_VALUE_NULL = null;\n    private static final String[] EMPTY_TAG_VALUES = { ADDED_TAG_VALUE_BLANK,\n            ADDED_TAG_VALUE_NULL };\n    private static final int ORIGINAL_TAG_COUNT_1 = 3;\n    private static final int ORIGINAL_TAG_COUNT_2 = 2;\n    @Rule\n    public TagChangeTestRule tagChangeTestRule = new TagChangeTestRule();\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Test\n    public void testDeleteNOP()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final ItemType itemType = completeItemType.getItemType();\n            final Atlas atlas = this.tagChangeTestRule.getAtlas();\n            final AtlasEntity originalAtlasEntity = atlas.entity(TagChangeTestRule.ID_2, itemType);\n            log.info(\"Original: {}.\", originalAtlasEntity);\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2, originalAtlasEntity.getTags().size());\n            final String junk = \"XYZ\";\n            final CompleteEntity completeEntity = completeItemType\n                    .completeEntityFrom(originalAtlasEntity).withRemovedTag(KEY_MARS + junk)\n                    .withRemovedTag(KEY_SINGLETON + junk);\n            final FeatureChange featureChange = FeatureChange.add((AtlasEntity) completeEntity);\n            final Change change = ChangeBuilder.newInstance().add(featureChange).get();\n            final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n            final AtlasEntity changedAtlasEntity = changeAtlas.entity(TagChangeTestRule.ID_2,\n                    itemType);\n            log.info(\"Changed: {}.\", changedAtlasEntity);\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2, changedAtlasEntity.getTags().size());\n            Assert.assertEquals(VALUE_ROVER, changedAtlasEntity.tag(KEY_MARS));\n            Assert.assertNotNull(changedAtlasEntity.tag(KEY_SINGLETON));\n            Assert.assertTrue(changedAtlasEntity.tag(KEY_SINGLETON).isEmpty());\n        });\n    }\n    // Delete (or Remove) Tag - START\n\n    // Insert or Add new Tag Tests - START\n\n    // Delete (or Remove) Tag - START\n    @Test\n    public void testDeleteTags()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final ItemType itemType = completeItemType.getItemType();\n            final Atlas atlas = this.tagChangeTestRule.getAtlas();\n            final AtlasEntity originalAtlasEntity = atlas.entity(TagChangeTestRule.ID_2, itemType);\n            log.info(\"Original: {}.\", originalAtlasEntity);\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2, originalAtlasEntity.getTags().size());\n            final CompleteEntity completeEntity = completeItemType\n                    .completeEntityFrom(originalAtlasEntity).withRemovedTag(KEY_MARS)\n                    .withRemovedTag(KEY_SINGLETON);\n            final FeatureChange featureChange = FeatureChange.add((AtlasEntity) completeEntity);\n            final Change change = ChangeBuilder.newInstance().add(featureChange).get();\n            final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n            final AtlasEntity changedAtlasEntity = changeAtlas.entity(TagChangeTestRule.ID_2,\n                    itemType);\n            log.info(\"Changed: {}.\", changedAtlasEntity);\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2 - 2, changedAtlasEntity.getTags().size());\n        });\n    }\n\n    @Test\n    public void testInsertNewTag()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final String key = ADDED_TAG_KEY;\n            final String value = ADDED_TAG_VALUE_1;\n            insertNewTag(completeItemType, key, value);\n        });\n    }\n\n    @Test\n    public void testInsertNewTagWithEmptyValue()\n    {\n        for (final String emptyTagValue : EMPTY_TAG_VALUES)\n        {\n            checkAllCompleteEntities(completeItemType -> insertNewTag(completeItemType,\n                    ADDED_TAG_KEY, emptyTagValue));\n        }\n    }\n\n    @Test\n    public void testInsertSameTagKeyTwiceWithDifferentValuesAsTwoCompleteEntities()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final Atlas atlas = this.tagChangeTestRule.getAtlas();\n\n            try\n            {\n                final ItemType itemType = completeItemType.getItemType();\n                final AtlasEntity originalAtlasEntity = itemType.entityForIdentifier(atlas,\n                        TagChangeTestRule.ID_1);\n                log.info(\"Original: {}\", originalAtlasEntity);\n                final Map<String, String> originalTags = originalAtlasEntity.getTags();\n                Assert.assertEquals(ORIGINAL_TAG_COUNT_1, originalTags.size());\n                final CompleteEntity completeEntity1 = completeItemType\n                        .completeEntityFrom(originalAtlasEntity)\n                        .withAddedTag(ADDED_TAG_KEY, ADDED_TAG_VALUE_1);\n                final CompleteEntity completeEntity2 = completeItemType\n                        .completeEntityFrom(originalAtlasEntity)\n                        .withAddedTag(ADDED_TAG_KEY, ADDED_TAG_VALUE_2);\n                final FeatureChange featureChange1 = FeatureChange\n                        .add((AtlasEntity) completeEntity1);\n                final FeatureChange featureChange2 = FeatureChange\n                        .add((AtlasEntity) completeEntity2);\n                ChangeBuilder.newInstance().add(featureChange1).add(featureChange2).get();\n            }\n            catch (final CoreException e)\n            {\n                Assert.assertTrue(e.getMessage().startsWith(\"Cannot merge two feature changes\"));\n                Assert.assertEquals(e.getCause().getClass(), FeatureChangeMergeException.class);\n                Assert.assertTrue(e.getCause().getMessage()\n                        .contains(\"Attempted afterViewNoBeforeMerge failed for tags; afterView:\"));\n                return;\n            }\n            Assert.fail(\"The test didn't fail - but was expected to fail. completeItemType: \"\n                    + completeItemType);\n        });\n    }\n\n    @Test\n    public void testInsertSameTagKeyValueTwiceAsTwoCompleteEntities()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final Atlas atlas = this.tagChangeTestRule.getAtlas();\n            final ItemType itemType = completeItemType.getItemType();\n            final AtlasEntity originalAtlasEntity = itemType.entityForIdentifier(atlas,\n                    TagChangeTestRule.ID_1);\n            log.info(\"Original: {}\", originalAtlasEntity);\n            final Map<String, String> originalTags = originalAtlasEntity.getTags();\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_1, originalTags.size());\n            final CompleteEntity completeEntity1 = completeItemType\n                    .completeEntityFrom(originalAtlasEntity)\n                    .withAddedTag(ADDED_TAG_KEY, ADDED_TAG_VALUE_1);\n            final CompleteEntity completeEntity2 = completeItemType\n                    .completeEntityFrom(originalAtlasEntity)\n                    .withAddedTag(ADDED_TAG_KEY, ADDED_TAG_VALUE_1);\n            final FeatureChange featureChange1 = FeatureChange.add((AtlasEntity) completeEntity1);\n            final FeatureChange featureChange2 = FeatureChange.add((AtlasEntity) completeEntity2);\n            final Change change = ChangeBuilder.newInstance().add(featureChange1)\n                    .add(featureChange2).get();\n            final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n            final AtlasEntity changedAtlasEntity = itemType.entityForIdentifier(changeAtlas,\n                    TagChangeTestRule.ID_1);\n            log.info(\"Changed:  {}\", changedAtlasEntity);\n            Assert.assertEquals(ADDED_TAG_VALUE_1, changedAtlasEntity.tag(ADDED_TAG_KEY));\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_1 + 1, changedAtlasEntity.getTags().size());\n            originalTags.forEach((key, value) -> Assert.assertEquals(originalAtlasEntity.tag(key),\n                    changedAtlasEntity.tag(key)));\n        });\n    }\n\n    @Test\n    public void testInsertSameTagTwiceOnCompletedNode()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final Atlas atlas = this.tagChangeTestRule.getAtlas();\n            final ItemType itemType = completeItemType.getItemType();\n            final AtlasEntity originalAtlasEntity = itemType.entityForIdentifier(atlas,\n                    TagChangeTestRule.ID_1);\n            log.info(\"Original: {}\", originalAtlasEntity);\n            final Map<String, String> originalTags = originalAtlasEntity.getTags();\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_1, originalTags.size());\n            final CompleteEntity completeEntity = completeItemType\n                    .completeEntityFrom(originalAtlasEntity)\n                    .withAddedTag(ADDED_TAG_KEY, ADDED_TAG_VALUE_1)\n                    .withAddedTag(ADDED_TAG_KEY, ADDED_TAG_VALUE_2);\n            final FeatureChange featureChange1 = FeatureChange.add((AtlasEntity) completeEntity);\n            final Change change = ChangeBuilder.newInstance().add(featureChange1).get();\n            final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n            final AtlasEntity changedAtlasEntity = itemType.entityForIdentifier(changeAtlas,\n                    TagChangeTestRule.ID_1);\n            log.info(\"Changed:  {}\", changedAtlasEntity);\n            Assert.assertEquals(ADDED_TAG_VALUE_2, changedAtlasEntity.tag(ADDED_TAG_KEY));\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_1 + 1, changedAtlasEntity.getTags().size());\n            originalTags.forEach((key, value) -> Assert.assertEquals(originalAtlasEntity.tag(key),\n                    changedAtlasEntity.tag(key)));\n        });\n    }\n\n    // Insert or Add new Tag Tests - END\n\n    // Update (or Replace) and Upsert Tag - START\n\n    // Overwrite All Tags - Start\n    @Test\n    public void testOverwrite()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final ItemType itemType = completeItemType.getItemType();\n            final Atlas atlas = this.tagChangeTestRule.getAtlas();\n            final AtlasEntity originalAtlasEntity1 = atlas.entity(TagChangeTestRule.ID_1, itemType);\n            final AtlasEntity originalAtlasEntity2 = atlas.entity(TagChangeTestRule.ID_2, itemType);\n            final Map<String, String> tags1 = originalAtlasEntity1.getTags();\n            final Map<String, String> tags2 = originalAtlasEntity2.getTags();\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_1, tags1.size());\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2, tags2.size());\n            final CompleteEntity completeEntity1 = completeItemType\n                    .completeEntityFrom(originalAtlasEntity1).withTags(tags2);\n            final CompleteEntity completeEntity2 = completeItemType\n                    .completeEntityFrom(originalAtlasEntity2).withTags(tags1);\n            final FeatureChange featureChange1 = FeatureChange.add((AtlasEntity) completeEntity1);\n            final FeatureChange featureChange2 = FeatureChange.add((AtlasEntity) completeEntity2);\n            final Change change = ChangeBuilder.newInstance().add(featureChange1)\n                    .add(featureChange2).get();\n            final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n            Assert.assertEquals(tags2,\n                    changeAtlas.entity(TagChangeTestRule.ID_1, itemType).getTags());\n            Assert.assertEquals(tags1,\n                    changeAtlas.entity(TagChangeTestRule.ID_2, itemType).getTags());\n        });\n    }\n    // Overwrite All Tags - End\n\n    @Test\n    public void testUpdateTagKeyValueAndTagValueOnly()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final ItemType itemType = completeItemType.getItemType();\n            final Atlas atlas = this.tagChangeTestRule.getAtlas();\n            final AtlasEntity originalAtlasEntity = atlas.entity(TagChangeTestRule.ID_2, itemType);\n            log.info(\"Original: {}.\", originalAtlasEntity);\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2, originalAtlasEntity.getTags().size());\n            final CompleteEntity completeEntity = completeItemType\n                    .completeEntityFrom(originalAtlasEntity)\n                    .withReplacedTag(KEY_MARS, KEY_OPPORTUNITY, VALUE_ROVER)\n                    .withReplacedTag(KEY_SINGLETON, KEY_SINGLETON, VALUE_ONE);\n            final FeatureChange featureChange = FeatureChange.add((AtlasEntity) completeEntity);\n            final Change change = ChangeBuilder.newInstance().add(featureChange).get();\n            final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n            final AtlasEntity changedAtlasEntity = changeAtlas.entity(TagChangeTestRule.ID_2,\n                    itemType);\n            log.info(\"Changed: {}.\", changedAtlasEntity);\n            Assert.assertEquals(VALUE_ROVER, changedAtlasEntity.tag(KEY_OPPORTUNITY));\n            Assert.assertEquals(VALUE_ONE, changedAtlasEntity.tag(KEY_SINGLETON));\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2, changedAtlasEntity.getTags().size());\n        });\n    }\n\n    // Update (or Replace) and Upsert Tag - END\n\n    @Test\n    public void testUpsertTag()\n    {\n        checkAllCompleteEntities(completeItemType ->\n        {\n            final ItemType itemType = completeItemType.getItemType();\n            final Atlas atlas = this.tagChangeTestRule.getAtlas();\n            final AtlasEntity originalAtlasEntity = atlas.entity(TagChangeTestRule.ID_2, itemType);\n            log.info(\"Original: {}.\", originalAtlasEntity);\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2, originalAtlasEntity.getTags().size());\n            final CompleteEntity completeEntity = completeItemType\n                    .completeEntityFrom(originalAtlasEntity)\n                    .withReplacedTag(KEY_OPPORTUNITY, KEY_OPPORTUNITY, VALUE_ROVER);\n            final FeatureChange featureChange = FeatureChange.add((AtlasEntity) completeEntity);\n            final Change change = ChangeBuilder.newInstance().add(featureChange).get();\n            final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n            final AtlasEntity changedAtlasEntity = changeAtlas.entity(TagChangeTestRule.ID_2,\n                    itemType);\n            log.info(\"Changed: {}.\", changedAtlasEntity);\n            Assert.assertEquals(VALUE_ROVER, changedAtlasEntity.tag(KEY_MARS));\n            Assert.assertEquals(VALUE_ROVER, changedAtlasEntity.tag(KEY_OPPORTUNITY));\n            Assert.assertEquals(ORIGINAL_TAG_COUNT_2 + 1, changedAtlasEntity.getTags().size());\n        });\n    }\n\n    private void checkAllCompleteEntities(final Consumer<CompleteItemType> consumer)\n    {\n        Arrays.stream(CompleteItemType.values()).forEach(consumer);\n    }\n\n    private void insertNewTag(final CompleteItemType completeItemType, final String key,\n            final String value)\n    {\n        final Atlas atlas = this.tagChangeTestRule.getAtlas();\n        final ItemType itemType = completeItemType.getItemType();\n        final AtlasEntity originalAtlasEntity = itemType.entityForIdentifier(atlas,\n                TagChangeTestRule.ID_1);\n        log.info(\"Original: {}\", originalAtlasEntity);\n        final Map<String, String> originalTags = originalAtlasEntity.getTags();\n        Assert.assertEquals(ORIGINAL_TAG_COUNT_1, originalTags.size());\n        final CompleteEntity completeEntity = completeItemType\n                .completeEntityFrom(originalAtlasEntity).withAddedTag(key, value);\n        final FeatureChange featureChange1 = FeatureChange.add((AtlasEntity) completeEntity);\n        final Change change = ChangeBuilder.newInstance().add(featureChange1).get();\n        final Atlas changeAtlas = new ChangeAtlas(atlas, change);\n        final AtlasEntity changedAtlasEntity = itemType.entityForIdentifier(changeAtlas,\n                TagChangeTestRule.ID_1);\n        log.info(\"Changed:  {}\", changedAtlasEntity);\n        Assert.assertEquals(changedAtlasEntity.tag(key), value);\n        Assert.assertEquals(ORIGINAL_TAG_COUNT_1 + 1, changedAtlasEntity.getTags().size());\n        originalTags.forEach((key1, value1) -> Assert.assertEquals(originalAtlasEntity.tag(key1),\n                changedAtlasEntity.tag(key1)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/TagChangeTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author Yazad Khambata\n */\npublic class TagChangeTestRule extends CoreTestRule\n{\n\n    public static final String HELLO_WORLD = \"hello=world\";\n    public static final String CHANGE_ME = \"change=me\";\n    public static final String DELETE_ME = \"delete=me\";\n    public static final String MARS_ROVER = \"mars=rover\";\n    public static final String SINGLETON_EMPTY = \"singleton=\";\n\n    private static final String ID_STR_1 = \"1\";\n    public static final Long ID_1 = Long.valueOf(ID_STR_1);\n\n    private static final String ID_STR_2 = \"2\";\n    public static final Long ID_2 = Long.valueOf(ID_STR_2);\n\n    private static final String NODE = \"12.7,116.5\";\n    private static final String EDGE_1 = \"10.1,149.4\";\n    private static final String EDGE_2 = \"19.9,137.0\";\n    private static final String EDGE_3 = \"13.4,148.4\";\n    private static final String AREA_1 = \"19.7,122.1\";\n    private static final String AREA_2 = \"10.7,112.9\";\n    private static final String AREA_3 = \"16.3,138.9\";\n    private static final String AREA_4 = \"11.5,135.6\";\n    private static final String AREA_5 = \"19.7,122.1\";\n    private static final String AREA_6 = \"17.9,111.9\";\n    private static final String AREA_7 = AREA_1;\n    @TestAtlas(\n\n            nodes = {\n                    @TestAtlas.Node(id = ID_STR_1, coordinates = @TestAtlas.Loc(value = NODE), tags = {\n                            HELLO_WORLD, CHANGE_ME, DELETE_ME }),\n\n                    @TestAtlas.Node(id = ID_STR_2, coordinates = @TestAtlas.Loc(value = EDGE_1), tags = {\n                            MARS_ROVER, SINGLETON_EMPTY }),\n                    @TestAtlas.Node(id = \"3\", coordinates = @TestAtlas.Loc(value = EDGE_2)),\n                    @TestAtlas.Node(id = \"4\", coordinates = @TestAtlas.Loc(value = EDGE_3)),\n\n                    @TestAtlas.Node(id = \"5\", coordinates = @TestAtlas.Loc(value = AREA_1)),\n                    @TestAtlas.Node(id = \"6\", coordinates = @TestAtlas.Loc(value = AREA_2)),\n                    @TestAtlas.Node(id = \"7\", coordinates = @TestAtlas.Loc(value = AREA_3)),\n                    @TestAtlas.Node(id = \"8\", coordinates = @TestAtlas.Loc(value = AREA_4)),\n                    @TestAtlas.Node(id = \"9\", coordinates = @TestAtlas.Loc(value = AREA_5)),\n                    @TestAtlas.Node(id = \"10\", coordinates = @TestAtlas.Loc(value = AREA_6)),\n                    @TestAtlas.Node(id = \"11\", coordinates = @TestAtlas.Loc(value = AREA_7)) },\n\n            points = {\n                    @TestAtlas.Point(id = ID_STR_1, coordinates = @TestAtlas.Loc(value = NODE), tags = {\n                            HELLO_WORLD, CHANGE_ME, DELETE_ME }),\n\n                    @TestAtlas.Point(id = ID_STR_2, coordinates = @TestAtlas.Loc(value = EDGE_1), tags = {\n                            MARS_ROVER, SINGLETON_EMPTY }), },\n\n            edges = { @TestAtlas.Edge(id = ID_STR_1, coordinates = { @TestAtlas.Loc(value = EDGE_1),\n                    @TestAtlas.Loc(value = EDGE_2),\n                    @TestAtlas.Loc(value = EDGE_3) }, tags = { HELLO_WORLD, CHANGE_ME, DELETE_ME }),\n\n                    @TestAtlas.Edge(id = ID_STR_2, coordinates = { @TestAtlas.Loc(value = EDGE_1),\n                            @TestAtlas.Loc(value = EDGE_3) }, tags = { MARS_ROVER,\n                                    SINGLETON_EMPTY })\n\n            },\n\n            lines = { @TestAtlas.Line(id = ID_STR_1, coordinates = { @TestAtlas.Loc(value = EDGE_1),\n                    @TestAtlas.Loc(value = EDGE_2),\n                    @TestAtlas.Loc(value = EDGE_3) }, tags = { HELLO_WORLD, CHANGE_ME, DELETE_ME }),\n\n                    @TestAtlas.Line(id = ID_STR_2, coordinates = { @TestAtlas.Loc(value = EDGE_1),\n                            @TestAtlas.Loc(value = EDGE_3) }, tags = { MARS_ROVER,\n                                    SINGLETON_EMPTY })\n\n            },\n\n            areas = { @TestAtlas.Area(id = ID_STR_1, coordinates = { @TestAtlas.Loc(value = AREA_1),\n                    @TestAtlas.Loc(value = AREA_2), @TestAtlas.Loc(value = AREA_3),\n                    @TestAtlas.Loc(value = AREA_6),\n                    @TestAtlas.Loc(value = AREA_7) }, tags = { HELLO_WORLD, CHANGE_ME, DELETE_ME }),\n\n                    @TestAtlas.Area(id = ID_STR_2, coordinates = { @TestAtlas.Loc(value = AREA_1),\n                            @TestAtlas.Loc(value = AREA_4), @TestAtlas.Loc(value = AREA_5),\n                            @TestAtlas.Loc(value = AREA_7) }, tags = { MARS_ROVER,\n                                    SINGLETON_EMPTY }), },\n\n            relations = { @TestAtlas.Relation(id = ID_STR_1, members = {\n                    @TestAtlas.Relation.Member(id = ID_STR_1, role = \"some role 1\", type = \"node\"),\n                    @TestAtlas.Relation.Member(id = ID_STR_1, role = \"some role 2\", type = \"edge\"),\n                    @TestAtlas.Relation.Member(id = ID_STR_1, role = \"some role 3\", type = \"area\"), }, tags = {\n                            HELLO_WORLD, CHANGE_ME, DELETE_ME }),\n\n                    @TestAtlas.Relation(id = ID_STR_2, members = {\n                            @TestAtlas.Relation.Member(id = ID_STR_2, role = \"some role 1\", type = \"node\"),\n                            @TestAtlas.Relation.Member(id = ID_STR_2, role = \"some role 2\", type = \"point\"),\n                            @TestAtlas.Relation.Member(id = ID_STR_2, role = \"some role 3\", type = \"edge\"),\n                            @TestAtlas.Relation.Member(id = ID_STR_2, role = \"some role 4\", type = \"line\"),\n                            @TestAtlas.Relation.Member(id = ID_STR_2, role = \"some role 5\", type = \"area\"),\n                            @TestAtlas.Relation.Member(id = ID_STR_1, role = \"some role 6\", type = \"relation\"), }, tags = {\n                                    MARS_ROVER, SINGLETON_EMPTY }) })\n    private final Atlas atlas = null;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/description/descriptors/ChangeDescriptorComparatorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.description.descriptors;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * @author lcram\n */\npublic class ChangeDescriptorComparatorTest\n{\n    @Test\n    public void testGlobalSorting()\n    {\n        final TagChangeDescriptor removeTag1 = new TagChangeDescriptor(ChangeDescriptorType.REMOVE,\n                \"keyRemove1\", null, \"valueRemove1\");\n        final TagChangeDescriptor removeTag2 = new TagChangeDescriptor(ChangeDescriptorType.REMOVE,\n                \"keyRemove2\", null, \"valueRemove2\");\n        final TagChangeDescriptor addTag1 = new TagChangeDescriptor(ChangeDescriptorType.ADD,\n                \"keyAdd1\", \"valueAdd1\", null);\n        final TagChangeDescriptor addTag2 = new TagChangeDescriptor(ChangeDescriptorType.ADD,\n                \"keyAdd2\", \"valueAdd2\", null);\n        final TagChangeDescriptor updateTag1 = new TagChangeDescriptor(ChangeDescriptorType.UPDATE,\n                \"keyUpdate1\", \"valueUpdate1Updated\", \"valueUpdate1\");\n        final TagChangeDescriptor updateTag2 = new TagChangeDescriptor(ChangeDescriptorType.UPDATE,\n                \"keyUpdate2\", \"valueUpdate2Updated\", \"valueUpdate2\");\n        final GeometryChangeDescriptor addGeometry1 = GeometryChangeDescriptor\n                .getDescriptorsForGeometry(new ArrayList<>(),\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\")))\n                .get(0);\n        final GeometryChangeDescriptor addGeometry2 = GeometryChangeDescriptor\n                .getDescriptorsForGeometry(new ArrayList<>(),\n                        Arrays.asList(Location.forString(\"3,3\"), Location.forString(\"4,4\")))\n                .get(0);\n        final GeometryChangeDescriptor updateGeometry1 = GeometryChangeDescriptor\n                .getDescriptorsForGeometry(\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                                Location.forString(\"3,3\")),\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"20,20\"),\n                                Location.forString(\"3,3\")))\n                .get(0);\n        final GeometryChangeDescriptor updateGeometry2 = GeometryChangeDescriptor\n                .getDescriptorsForGeometry(\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                                Location.forString(\"3,3\")),\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                                Location.forString(\"30,30\")))\n                .get(0);\n        final GeometryChangeDescriptor removeGeometry1 = GeometryChangeDescriptor\n                .getDescriptorsForGeometry(\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                                Location.forString(\"3,3\")),\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"3,3\")))\n                .get(0);\n        final GeometryChangeDescriptor removeGeometry2 = GeometryChangeDescriptor\n                .getDescriptorsForGeometry(\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                                Location.forString(\"3,3\")),\n                        Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\")))\n                .get(0);\n        final LongElementChangeDescriptor addParentRelation1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.ADD, 1L, ChangeDescriptorName.PARENT_RELATION);\n        final LongElementChangeDescriptor addParentRelation2 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.ADD, 2L, ChangeDescriptorName.PARENT_RELATION);\n        final LongElementChangeDescriptor removeParentRelation1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 1L, ChangeDescriptorName.PARENT_RELATION);\n        final LongElementChangeDescriptor removeParentRelation2 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 2L, ChangeDescriptorName.PARENT_RELATION);\n        final RelationMemberChangeDescriptor addRelationMember1 = new RelationMemberChangeDescriptor(\n                ChangeDescriptorType.ADD, 1L, ItemType.POINT, \"a\");\n        final RelationMemberChangeDescriptor addRelationMember2 = new RelationMemberChangeDescriptor(\n                ChangeDescriptorType.ADD, 2L, ItemType.POINT, \"a\");\n        final RelationMemberChangeDescriptor addRelationMember3 = new RelationMemberChangeDescriptor(\n                ChangeDescriptorType.ADD, 2L, ItemType.POINT, \"b\");\n        final RelationMemberChangeDescriptor addRelationMember4 = new RelationMemberChangeDescriptor(\n                ChangeDescriptorType.ADD, 1L, ItemType.RELATION, \"a\");\n        final RelationMemberChangeDescriptor removeRelationMember1 = new RelationMemberChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 1L, ItemType.POINT, \"a\");\n        final RelationMemberChangeDescriptor removeRelationMember2 = new RelationMemberChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 2L, ItemType.POINT, \"a\");\n        final RelationMemberChangeDescriptor removeRelationMember3 = new RelationMemberChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 2L, ItemType.POINT, \"b\");\n        final RelationMemberChangeDescriptor removeRelationMember4 = new RelationMemberChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 1L, ItemType.RELATION, \"a\");\n        final LongElementChangeDescriptor addInEdge1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.ADD, 1L, ChangeDescriptorName.IN_EDGE);\n        final LongElementChangeDescriptor addInEdge2 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.ADD, 2L, ChangeDescriptorName.IN_EDGE);\n        final LongElementChangeDescriptor removeInEdge1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 1L, ChangeDescriptorName.IN_EDGE);\n        final LongElementChangeDescriptor removeInEdge2 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 2L, ChangeDescriptorName.IN_EDGE);\n        final LongElementChangeDescriptor addOutEdge1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.ADD, 1L, ChangeDescriptorName.OUT_EDGE);\n        final LongElementChangeDescriptor addOutEdge2 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.ADD, 2L, ChangeDescriptorName.OUT_EDGE);\n        final LongElementChangeDescriptor removeOutEdge1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 1L, ChangeDescriptorName.OUT_EDGE);\n        final LongElementChangeDescriptor removeOutEdge2 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.REMOVE, 2L, ChangeDescriptorName.OUT_EDGE);\n        final LongElementChangeDescriptor addStartNode1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.ADD, 1L, ChangeDescriptorName.START_NODE);\n        final LongElementChangeDescriptor updateStartNode1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.UPDATE, 1L, 10L, ChangeDescriptorName.START_NODE);\n        final LongElementChangeDescriptor addEndNode1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.ADD, 1L, ChangeDescriptorName.END_NODE);\n        final LongElementChangeDescriptor updateEndNode1 = new LongElementChangeDescriptor(\n                ChangeDescriptorType.UPDATE, 1L, 10L, ChangeDescriptorName.END_NODE);\n\n        final List<ChangeDescriptor> goldenList = Arrays.asList(addTag1, addTag2, updateTag1,\n                updateTag2, removeTag1, removeTag2, addGeometry1, addGeometry2, updateGeometry1,\n                updateGeometry2, removeGeometry1, removeGeometry2, addParentRelation1,\n                addParentRelation2, removeParentRelation1, removeParentRelation2,\n                addRelationMember1, addRelationMember2, addRelationMember3, addRelationMember4,\n                removeRelationMember1, removeRelationMember2, removeRelationMember3,\n                removeRelationMember4, addInEdge1, addInEdge2, removeInEdge1, removeInEdge2,\n                addOutEdge1, addOutEdge2, removeOutEdge1, removeOutEdge2, addStartNode1,\n                updateStartNode1, addEndNode1, updateEndNode1);\n        final List<ChangeDescriptor> actualList = new ArrayList<>(goldenList);\n        Collections.shuffle(actualList);\n        actualList.sort(new ChangeDescriptorComparator());\n\n        Assert.assertEquals(goldenList, actualList);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/diff/AtlasDiffTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.diff;\n\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.Change;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeAtlas;\nimport org.openstreetmap.atlas.geography.atlas.change.description.ChangeDescriptorType;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class AtlasDiffTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDiffTest.class);\n\n    private static final int IGNORE_EXPECTED_NUMBER_CHANGES = -1;\n\n    @Rule\n    public AtlasDiffTestRule rule = new AtlasDiffTestRule();\n\n    @Test\n    public void testAggregateDiff()\n    {\n        Atlas atlasX = this.rule.simpleAtlas1();\n        Atlas atlasY = this.rule.simpleAtlas2();\n        int expectedNumberOfChanges = 2;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n\n        atlasX = this.rule.simpleAtlas3();\n        atlasY = this.rule.simpleAtlas4();\n        expectedNumberOfChanges = 15;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n    }\n\n    @Test\n    public void testNodeAndEdgePropertyDiff()\n    {\n        final Atlas atlasX = this.rule.differentNodeAndEdgeProperties1();\n        final Atlas atlasY = this.rule.differentNodeAndEdgeProperties2();\n\n        // 4 node changes, 2 edge changes as a result of the node change, 1 edge shape point change\n        final int expectedNumberOfChanges = 7;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n    }\n\n    @Test\n    public void testPointLineAreaPropertyDiff()\n    {\n        final Atlas atlasX = this.rule.differentPointLineArea1();\n        final Atlas atlasY = this.rule.differentPointLineArea2();\n        final int expectedNumberOfChanges = 4;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n    }\n\n    @Test\n    public void testRelationGeometriesDiff()\n    {\n        final Atlas atlasX = this.rule.differentRelations5();\n        final Atlas atlasY = this.rule.differentRelations6();\n        final int expectedNumberOfChanges = 1;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n    }\n\n    @Test\n    public void testRelationMemberRemoval()\n    {\n        final Atlas atlasX = this.rule.removeRelationMember1();\n        final Atlas atlasY = this.rule.removeRelationMember2();\n        final int expectedNumberOfChanges = 2;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n    }\n\n    @Test\n    public void testRelationsDiff()\n    {\n        Atlas atlasX = this.rule.differentRelations1();\n        Atlas atlasY = this.rule.differentRelations2();\n        int expectedNumberOfChanges = 9;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n\n        atlasX = this.rule.differentRelations3();\n        atlasY = this.rule.differentRelations4();\n        expectedNumberOfChanges = 5;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n    }\n\n    @Test\n    public void testTagDiff()\n    {\n        final Atlas atlasX = this.rule.differentTags1();\n        final Atlas atlasY = this.rule.differentTags2();\n        final int expectedNumberOfChanges = 3;\n\n        assertChangeAtlasConsistency(atlasX, atlasY, expectedNumberOfChanges);\n\n        final Change changeBeforeToAfter = new AtlasDiff(atlasX, atlasY).generateChange()\n                .orElseThrow(() -> new CoreException(\n                        \"This Change should never be empty. The unit test may be broken.\"));\n        final Map<ItemType, Map<ChangeDescriptorType, Map<String, AtomicLong>>> tagCountMap = changeBeforeToAfter\n                .tagCountMap();\n        Assert.assertEquals(1, tagCountMap.get(ItemType.NODE).get(ChangeDescriptorType.ADD).size());\n        Assert.assertEquals(1,\n                tagCountMap.get(ItemType.NODE).get(ChangeDescriptorType.REMOVE).size());\n\n        Assert.assertEquals(2,\n                tagCountMap.get(ItemType.NODE).get(ChangeDescriptorType.ADD).get(\"tag2\").get());\n        Assert.assertEquals(1,\n                tagCountMap.get(ItemType.NODE).get(ChangeDescriptorType.REMOVE).get(\"tag2\").get());\n    }\n\n    private void assertChangeAtlasConsistency(final Atlas beforeAtlas, final Atlas afterAtlas,\n            final int expectedNumberOfChanges)\n    {\n        final Change changeBeforeToAfter = new AtlasDiff(beforeAtlas, afterAtlas).generateChange()\n                .orElseThrow(() -> new CoreException(\n                        \"This Change should never be empty. The unit test may be broken.\"));\n        changeBeforeToAfter.changes().forEach(change -> logger.trace(\"{}:\\n{} ->\\n{}\", change,\n                change.getBeforeView(), change.getAfterView()));\n\n        System.out.println(changeBeforeToAfter.toJson());\n\n        if (expectedNumberOfChanges != IGNORE_EXPECTED_NUMBER_CHANGES)\n        {\n            Assert.assertEquals(expectedNumberOfChanges, changeBeforeToAfter.changeCount());\n        }\n        final ChangeAtlas changeAfterAtlas = new ChangeAtlas(beforeAtlas, changeBeforeToAfter);\n\n        // test both ways to catch any slip-ups in the code\n        Assert.assertFalse(\n                new AtlasDiff(changeAfterAtlas, afterAtlas).generateChange().isPresent());\n        Assert.assertFalse(\n                new AtlasDiff(afterAtlas, changeAfterAtlas).generateChange().isPresent());\n\n        Assert.assertEquals(afterAtlas, changeAfterAtlas);\n\n        // Now test that PackedAtlas cloning is consistent. This is guaranteed by AtlasDiff so we\n        // must ensure it holds.\n        Assert.assertEquals(new PackedAtlasCloner().cloneFrom(afterAtlas),\n                new PackedAtlasCloner().cloneFrom(changeAfterAtlas));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/diff/AtlasDiffTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.diff;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author lcram\n */\npublic class AtlasDiffTestRule extends CoreTestRule\n{\n    private static final String ONE = \"15.420563,-61.336198\";\n    private static final String TWO = \"15.429499,-61.332850\";\n    private static final String THREE = \"15.4855,-61.3041\";\n    private static final String FOUR = \"15.4809,-61.3366\";\n    private static final String FIVE = \"15.4852,-61.3816\";\n    private static final String SIX = \"15.4781,-61.3949\";\n    private static final String SEVEN = \"15.4145,-61.3826\";\n    private static final String EIGHT = \"15.4073,-61.3749\";\n    private static final String NINE = \"15.4075,-61.3746\";\n    private static final String TEN = \"15.4081,-61.3741\";\n    private static final String ELEVEN = \"15.4111,-62.3741\";\n\n    @TestAtlas(loadFromJosmOsmResource = \"DiffAtlas1.josm.osm\")\n    private Atlas simpleAtlas1;\n\n    @TestAtlas(loadFromJosmOsmResource = \"DiffAtlas2.josm.osm\")\n    private Atlas simpleAtlas2;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\",\n                            \"tag2=value2\" })\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"99\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"100\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = SEVEN) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"101\", coordinates = { @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=secondary\" })\n\n            },\n\n            areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"landuse=residential\" })\n\n            },\n\n            lines = {\n\n                    @Line(id = \"1\", coordinates = { @Loc(value = FOUR), @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"power=line\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Point(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Point(id = \"100\", coordinates = @Loc(value = ELEVEN))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5\", role = \"a\", type = \"node\"),\n                            @Member(id = \"7\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"32\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"7\", role = \"a\", type = \"node\"),\n                            @Member(id = \"8\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"33\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"6\", role = \"a\", type = \"node\"),\n\n                    }),\n\n                    @Relation(id = \"1000\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"100\", role = \"a\", type = \"point\"),\n\n                    })\n\n            }\n\n    )\n    private Atlas simpleAtlas3;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\",\n                            \"tag2=value2\" }),\n                    @Node(id = \"9\", coordinates = @Loc(value = NINE), tags = { \"tag1=value1\",\n                            \"tag2=value2\" }),\n                    @Node(id = \"10\", coordinates = @Loc(value = TEN), tags = { \"tag1=value1\",\n                            \"tag2=value2\" })\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"99\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"100\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = SEVEN) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"101\", coordinates = { @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"102\", coordinates = { @Loc(value = NINE),\n                            @Loc(value = TEN) }, tags = { \"highway=secondary\" })\n\n            },\n\n            areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = FOUR),\n                            @Loc(value = THREE) }, tags = { \"landuse=residential\" })\n\n            },\n\n            lines = {\n\n                    @Line(id = \"1\", coordinates = { @Loc(value = FOUR), @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"power=line\", \"tag=value\" }),\n                    @Line(id = \"2\", coordinates = { @Loc(value = FOUR), @Loc(value = EIGHT),\n                            @Loc(value = SIX) }, tags = { \"power=line\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Point(id = \"2\", coordinates = @Loc(value = TWO))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"32\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"7\", role = \"a\", type = \"node\"),\n                            @Member(id = \"8\", role = \"b\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"33\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"6\", role = \"a\", type = \"node\"),\n                            @Member(id = \"2\", role = \"b\", type = \"line\")\n\n                    }),\n\n                    @Relation(id = \"34\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"1\", role = \"a\", type = \"point\"),\n                            @Member(id = \"2\", role = \"b\", type = \"point\")\n\n                    })\n\n            }\n\n    )\n    private Atlas simpleAtlas4;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\",\n                            \"tag2=value2\" })\n\n            }\n\n    )\n    private Atlas differentTags1;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\",\n                            \"tag2=value2\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\",\n                            \"tag2=value2\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\" })\n\n            }\n\n    )\n    private Atlas differentTags2;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\" }),\n                    @Node(id = \"9\", coordinates = @Loc(value = NINE), tags = { \"tag1=value1\" })\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"99\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"100\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = SEVEN) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"101\", coordinates = { @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=secondary\" })\n\n            }\n\n    )\n    private Atlas differentNodeAndEdgeProperties1;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\" }),\n                    @Node(id = \"9\", coordinates = @Loc(value = TEN), tags = { \"tag1=value1\" })\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"99\", coordinates = { @Loc(value = ONE), @Loc(value = THREE),\n                            @Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"100\", coordinates = { @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"101\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = SEVEN) }, tags = { \"highway=secondary\" })\n\n            }\n\n    )\n    private Atlas differentNodeAndEdgeProperties2;\n\n    @TestAtlas(\n\n            areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"landuse=residential\" })\n\n            },\n\n            lines = {\n\n                    @Line(id = \"1\", coordinates = { @Loc(value = FOUR), @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"power=line\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"7\", coordinates = @Loc(value = SEVEN)),\n                    @Point(id = \"8\", coordinates = @Loc(value = EIGHT))\n\n            }\n\n    )\n    private Atlas differentPointLineArea1;\n\n    @TestAtlas(\n\n            areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = FOUR),\n                            @Loc(value = THREE) }, tags = { \"landuse=residential\" })\n\n            },\n\n            lines = {\n\n                    @Line(id = \"1\", coordinates = { @Loc(value = FOUR), @Loc(value = FIVE),\n                            @Loc(value = SEVEN) }, tags = { \"power=line\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"7\", coordinates = @Loc(value = NINE)),\n                    @Point(id = \"8\", coordinates = @Loc(value = TEN))\n\n            }\n\n    )\n    private Atlas differentPointLineArea2;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\" })\n\n            },\n\n            lines = {\n\n                    @Line(id = \"12\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5\", role = \"a\", type = \"node\"),\n\n                    }),\n\n                    @Relation(id = \"32\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"7\", role = \"a\", type = \"node\"),\n                            @Member(id = \"8\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"33\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"6\", role = \"a\", type = \"node\"),\n\n                    })\n\n            }\n\n    )\n    private Atlas differentRelations1;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\" })\n\n            },\n\n            points = {\n                    @Point(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }), },\n\n            lines = {\n\n                    @Line(id = \"12\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5\", role = \"a\", type = \"point\"),\n                            @Member(id = \"7\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"32\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"7\", role = \"a\", type = \"node\"),\n\n                    }),\n\n                    @Relation(id = \"33\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"12\", role = \"b\", type = \"line\"),\n\n                    })\n\n            }\n\n    )\n    private Atlas differentRelations2;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5\", role = \"a\", type = \"node\"),\n                            @Member(id = \"7\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"32\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"7\", role = \"a\", type = \"node\"),\n                            @Member(id = \"8\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"33\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"6\", role = \"a\", type = \"node\"),\n\n                    })\n\n            }\n\n    )\n    private Atlas differentRelations3;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" }),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN), tags = { \"tag1=value1\" }),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT), tags = { \"tag1=value1\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5\", role = \"b\", type = \"node\"),\n                            @Member(id = \"7\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"32\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"8\", role = \"a\", type = \"node\")\n\n                    })\n\n            }\n\n    )\n    private Atlas differentRelations4;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5\", role = \"b\", type = \"node\"),\n                            @Member(id = \"6\", role = \"a\", type = \"node\")\n\n                    })\n\n            }\n\n    )\n    private Atlas removeRelationMember1;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5\", role = \"b\", type = \"node\"),\n\n                    })\n\n            }\n\n    )\n    private Atlas removeRelationMember2;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=multipolygon\" }, members = {\n\n                            @Member(id = \"5\", role = \"b\", type = \"node\"),\n\n                    }, wkt = \"MULTIPOLYGON (((-8.3426173 6.9044046, -8.3478098 6.8973366, -8.3415467 6.8936697, -8.3346947 6.8984526, -8.3426173 6.9044046, -8.3426173 6.9044046)), ((-8.3259156 6.8970709, -8.3170295 6.8994623, -8.3154771 6.8895776, -8.3262368 6.8915439, -8.3259156 6.8970709, -8.3259156 6.8970709)))\")\n\n            }\n\n    )\n    private Atlas relationGeometry1;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE), tags = { \"tag1=value1\" }),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX), tags = { \"tag1=value1\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=multipolygon\" }, members = {\n\n                            @Member(id = \"5\", role = \"b\", type = \"node\"),\n\n                    }, wkt = \"MULTIPOLYGON (((-8.3372107 6.8921817, -8.3389237 6.8837848, -8.328271 6.883466, -8.3286993 6.8914376, -8.3372107 6.8921817, -8.3372107 6.8921817)), ((-8.3426173 6.9044046, -8.3478098 6.8973366, -8.3415467 6.8936697, -8.3346947 6.8984526, -8.3426173 6.9044046, -8.3426173 6.9044046)), ((-8.3259156 6.8970709, -8.3170295 6.8994623, -8.3154771 6.8895776, -8.3262368 6.8915439, -8.3259156 6.8970709, -8.3259156 6.8970709)))\")\n\n            }\n\n    )\n    private Atlas relationGeometry2;\n\n    public Atlas differentNodeAndEdgeProperties1()\n    {\n        return this.differentNodeAndEdgeProperties1;\n    }\n\n    public Atlas differentNodeAndEdgeProperties2()\n    {\n        return this.differentNodeAndEdgeProperties2;\n    }\n\n    public Atlas differentPointLineArea1()\n    {\n        return this.differentPointLineArea1;\n    }\n\n    public Atlas differentPointLineArea2()\n    {\n        return this.differentPointLineArea2;\n    }\n\n    public Atlas differentRelations1()\n    {\n        return this.differentRelations1;\n    }\n\n    public Atlas differentRelations2()\n    {\n        return this.differentRelations2;\n    }\n\n    public Atlas differentRelations3()\n    {\n        return this.differentRelations3;\n    }\n\n    public Atlas differentRelations4()\n    {\n        return this.differentRelations4;\n    }\n\n    public Atlas differentRelations5()\n    {\n        return this.relationGeometry1;\n    }\n\n    public Atlas differentRelations6()\n    {\n        return this.relationGeometry2;\n    }\n\n    public Atlas differentTags1()\n    {\n        return this.differentTags1;\n    }\n\n    public Atlas differentTags2()\n    {\n        return this.differentTags2;\n    }\n\n    public Atlas removeRelationMember1()\n    {\n        return this.removeRelationMember1;\n    }\n\n    public Atlas removeRelationMember2()\n    {\n        return this.removeRelationMember2;\n    }\n\n    public Atlas simpleAtlas1()\n    {\n        return this.simpleAtlas1;\n    }\n\n    public Atlas simpleAtlas2()\n    {\n        return this.simpleAtlas2;\n    }\n\n    public Atlas simpleAtlas3()\n    {\n        return this.simpleAtlas3;\n    }\n\n    public Atlas simpleAtlas4()\n    {\n        return this.simpleAtlas4;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/listener/TagChangeListenerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\n\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.consts.FieldChangeOperation;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEntity;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\n\n/**\n * Test for {@link TagChangeListener}.\n *\n * @param <E>\n *            - the {@link CompleteEntity}.\n * @author Yazad Khambata\n */\npublic class TagChangeListenerTest<E extends CompleteEntity<E>>\n{\n    @Test\n    public void addTag()\n    {\n        allForEach(completeEntity ->\n        {\n            final TestTagChangeListenerImplementation tagChangeListener = getTagChangeListener();\n            completeEntity.addTagChangeListener(tagChangeListener);\n\n            preValidation(tagChangeListener);\n\n            final String key = \"add\";\n            final String val = \"me\";\n            completeEntity.withAddedTag(key, val);\n\n            final TagChangeEvent lastEvent = basicPostValidation(tagChangeListener);\n\n            Assert.assertEquals(FieldChangeOperation.ADD, lastEvent.getFieldOperation());\n            validateBasicEventFields(completeEntity, lastEvent);\n            Assert.assertEquals(Pair.of(key, val), lastEvent.getNewValue().get());\n        });\n    }\n\n    @Test\n    public void overwriteTag()\n    {\n        allForEach(completeEntity ->\n        {\n            final TestTagChangeListenerImplementation tagChangeListener = getTagChangeListener();\n            completeEntity.addTagChangeListener(tagChangeListener);\n\n            preValidation(tagChangeListener);\n\n            final Map<String, String> tags = new HashMap<String, String>()\n            {\n                private static final long serialVersionUID = -4353511172908766690L;\n\n                {\n                    put(\"aaa1\", \"bbb1\");\n                    put(\"aaa2\", \"bbb2\");\n                    put(\"aaa3\", \"bbb3\");\n                }\n            };\n\n            completeEntity.withTags(tags);\n\n            final TagChangeEvent lastEvent = basicPostValidation(tagChangeListener);\n\n            Assert.assertEquals(FieldChangeOperation.OVERWRITE, lastEvent.getFieldOperation());\n            validateBasicEventFields(completeEntity, lastEvent);\n            Assert.assertEquals(tags, lastEvent.getNewValue().get());\n        });\n    }\n\n    @Test\n    public void removeTag()\n    {\n        allForEach(completeEntity ->\n        {\n            final TestTagChangeListenerImplementation tagChangeListener = getTagChangeListener();\n            completeEntity.addTagChangeListener(tagChangeListener);\n\n            preValidation(tagChangeListener);\n\n            final String key = \"removeMe\";\n            completeEntity.withRemovedTag(key);\n\n            final TagChangeEvent lastEvent = basicPostValidation(tagChangeListener);\n\n            Assert.assertEquals(FieldChangeOperation.REMOVE, lastEvent.getFieldOperation());\n            validateBasicEventFields(completeEntity, lastEvent);\n            Assert.assertEquals(key, lastEvent.getNewValue().get());\n        });\n    }\n\n    @Test\n    public void replaceTag()\n    {\n        allForEach(completeEntity ->\n        {\n            final TestTagChangeListenerImplementation tagChangeListener = getTagChangeListener();\n            completeEntity.addTagChangeListener(tagChangeListener);\n\n            preValidation(tagChangeListener);\n\n            final String oldKey = \"removeMe\";\n            final String newKey = \"add\";\n            final String newVal = \"me\";\n            completeEntity.withReplacedTag(oldKey, newKey, newVal);\n\n            final TagChangeEvent lastEvent = basicPostValidation(tagChangeListener);\n\n            Assert.assertEquals(FieldChangeOperation.REPLACE, lastEvent.getFieldOperation());\n            validateBasicEventFields(completeEntity, lastEvent);\n            Assert.assertEquals(Triple.of(oldKey, newKey, newVal), lastEvent.getNewValue().get());\n        });\n    }\n\n    private List<E> all()\n    {\n        return (List<E>) Arrays.asList(newCompleteNode1(), newCompletePoint1(), newCompleteLine1(),\n                newCompleteEdge1(), newCompleteArea1(), newCompleteRelation1());\n    }\n\n    private void allForEach(final Consumer<E> consumer)\n    {\n        allStream().forEach(consumer);\n    }\n\n    private Stream<E> allStream()\n    {\n        return this.all().stream();\n    }\n\n    private TagChangeEvent basicPostValidation(\n            final TestTagChangeListenerImplementation tagChangeListener)\n    {\n        Assert.assertEquals(1, tagChangeListener.getCallCount());\n        final TagChangeEvent lastEvent = tagChangeListener.getLastEvent();\n        Assert.assertNotNull(lastEvent);\n        return lastEvent;\n    }\n\n    private TestTagChangeListenerImplementation getTagChangeListener()\n    {\n        return new TestTagChangeListenerImplementation();\n    }\n\n    private CompleteArea newCompleteArea1()\n    {\n        return new CompleteArea(123L, null, null, null);\n    }\n\n    private CompleteEdge newCompleteEdge1()\n    {\n        return new CompleteEdge(123L, null, null, null, null, null);\n    }\n\n    private CompleteLine newCompleteLine1()\n    {\n        return new CompleteLine(123L, null, null, null);\n    }\n\n    private CompleteNode newCompleteNode1()\n    {\n        return new CompleteNode(123L, null, null, null, null, null);\n    }\n\n    private CompletePoint newCompletePoint1()\n    {\n        return new CompletePoint(123L, null, null, null);\n    }\n\n    private CompleteRelation newCompleteRelation1()\n    {\n        return new CompleteRelation(123L, null, null, null, null, null, null, null);\n    }\n\n    private void preValidation(final TestTagChangeListenerImplementation tagChangeListener)\n    {\n        Assert.assertEquals(0, tagChangeListener.getCallCount());\n        Assert.assertNull(tagChangeListener.getLastEvent());\n    }\n\n    private void validateBasicEventFields(final CompleteEntity completeEntity,\n            final TagChangeEvent lastEvent)\n    {\n        Assert.assertEquals(completeEntity.completeItemType(), lastEvent.getCompleteItemType());\n        Assert.assertEquals(completeEntity.getIdentifier(), lastEvent.getIdentifier());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/eventhandling/listener/TestTagChangeListenerImplementation.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener;\n\nimport org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;\n\n/**\n * @author Yazad Khambata\n */\npublic class TestTagChangeListenerImplementation implements TagChangeListener\n{\n    private static final long serialVersionUID = 6697728278083444095L;\n\n    private TagChangeEvent lastEvent;\n    private int callCount = 0;\n\n    @Override\n    public void entityChanged(final TagChangeEvent entityChangeEvent)\n    {\n        this.lastEvent = entityChangeEvent;\n        this.callCount++;\n    }\n\n    public int getCallCount()\n    {\n        return this.callCount;\n    }\n\n    public TagChangeEvent getLastEvent()\n    {\n        return this.lastEvent;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/exception/EmptyChangeExceptionTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.exception;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author Yazad Khambata\n */\npublic class EmptyChangeExceptionTest\n{\n    @Test\n    public void testMessage()\n    {\n        final String message = new EmptyChangeException().getMessage();\n\n        Assert.assertTrue(message.startsWith(CoreException.TOKEN));\n\n        Assert.assertEquals(new EmptyChangeException().getMessage(),\n                new EmptyChangeException(new RuntimeException()).getMessage());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/serializer/ChangeGeoJsonSerializerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.serializer;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.change.Change;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeBuilder;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\nimport com.google.common.collect.Lists;\n\n/**\n * @author matthieun\n */\npublic class ChangeGeoJsonSerializerTest\n{\n    private static final Map<String, String> TAGS = Maps.hashMap(\"tagKey1\", \"tagValue1\", \"tagKey2\",\n            \"tagValue2\");\n    private static final Set<Long> RELATIONS = Sets.hashSet(444L, 555L);\n\n    @Test\n    public void testSerialization()\n    {\n        final CompleteArea area = new CompleteArea(123L, Polygon.TEST_BUILDING, TAGS, RELATIONS);\n        final FeatureChange featureChange1 = FeatureChange.add(area);\n        final CompleteEdge edge = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, TAGS, 456L, 789L,\n                RELATIONS);\n        final FeatureChange featureChange2 = FeatureChange.add(edge);\n        final CompleteLine line = new CompleteLine(123L, PolyLine.TEST_POLYLINE, TAGS, RELATIONS);\n        final FeatureChange featureChange3 = FeatureChange.add(line);\n        final CompleteNode node = new CompleteNode(123L, Location.COLOSSEUM, TAGS,\n                Sets.treeSet(456L, 789L), Sets.treeSet(456L, 789L), RELATIONS);\n        final FeatureChange featureChange4 = FeatureChange.add(node);\n        final CompletePoint point = new CompletePoint(123L, Location.COLOSSEUM, TAGS, RELATIONS);\n        final FeatureChange featureChange5 = FeatureChange.add(point);\n        final RelationBean members = new RelationBean();\n        members.addItem(456L, \"role1\", ItemType.EDGE);\n        members.addItem(789L, \"role2\", ItemType.AREA);\n        final CompleteRelation relation = new CompleteRelation(123L, TAGS, Rectangle.TEST_RECTANGLE,\n                members, Lists.newArrayList(123L), members, 123L, RELATIONS);\n        final FeatureChange featureChange6 = FeatureChange.add(relation);\n\n        final ChangeBuilder changeBuilder = new ChangeBuilder();\n        changeBuilder.add(featureChange1);\n        changeBuilder.add(featureChange2);\n        changeBuilder.add(featureChange3);\n        changeBuilder.add(featureChange4);\n        changeBuilder.add(featureChange5);\n        changeBuilder.add(featureChange6);\n\n        final Change change = changeBuilder.get();\n        assertEquals(change, \"change.json\");\n    }\n\n    private void assertEquals(final Change change, final String fileName)\n    {\n        final String expected = new InputStreamResource(\n                () -> ChangeGeoJsonSerializerTest.class.getResourceAsStream(fileName)).all();\n        Assert.assertEquals(expected, change.toJson(false));\n        final File temporary = File.temporary();\n        try\n        {\n            change.save(temporary, false);\n            Assert.assertEquals(expected, temporary.all());\n        }\n        finally\n        {\n            temporary.delete();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/serializer/FeatureChangeGeoJsonSerializerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.serializer;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeType;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChangeUnitTestFactory;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteArea;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\nimport com.google.common.collect.Lists;\n\n/**\n * @author matthieun\n */\npublic class FeatureChangeGeoJsonSerializerTest\n{\n    private static final Map<String, String> TAGS = Maps.hashMap(\"tagKey1\", \"tagValue1\", \"tagKey2\",\n            \"tagValue2\");\n    private static final Set<Long> RELATIONS = Sets.hashSet(444L, 555L);\n\n    @Rule\n    public FeatureChangeGeoJsonSerializerTestRule rule = new FeatureChangeGeoJsonSerializerTestRule();\n\n    @Test\n    public void testBigFeatureChange()\n    {\n        final Atlas longWaterWay = this.rule.longWaterWayAtlas();\n        final Line waterway = longWaterWay.line(374902834000000L);\n        final List<Location> points = Iterables.stream(waterway).collectToList();\n        Collections.reverse(points);\n        final FeatureChange featureChange = FeatureChange.add(\n                (AtlasEntity) CompleteLine.shallowFrom(waterway).withGeometry(points), longWaterWay,\n                FeatureChange.Options.OSC_IF_POSSIBLE);\n\n        assertResourceEquals(featureChange, \"serializedReverseWay.json\", true);\n    }\n\n    @Test\n    public void testDescriptionSerializationAddEdge()\n    {\n        final CompleteEdge edge = new CompleteEdge(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), 1L, 2L, Sets.hashSet(1L, 2L));\n        final FeatureChange featureChange = FeatureChange.add(edge);\n        assertResourceEquals(featureChange, \"serializedAddEdgeWithDescription.json\", true);\n    }\n\n    @Test\n    public void testDescriptionSerializationAddRelation()\n    {\n        final RelationBean bean = new RelationBean();\n        bean.add(new RelationBean.RelationBeanItem(500L, \"a\", ItemType.POINT));\n        bean.add(new RelationBean.RelationBeanItem(600L, \"a\", ItemType.AREA));\n        final CompleteRelation relation = new CompleteRelation(123L, Maps.hashMap(\"a\", \"1\"),\n                Rectangle.TEST_RECTANGLE, bean, null, null, null, Sets.hashSet(1L, 2L));\n        final FeatureChange featureChange = FeatureChange.add(relation);\n        assertResourceEquals(featureChange, \"serializedAddRelationWithDescription.json\", true);\n    }\n\n    @Test\n    public void testDescriptionSerializationRemoveArea()\n    {\n        final CompleteArea area = new CompleteArea(123L, Polygon.SILICON_VALLEY,\n                Maps.hashMap(\"a\", \"1\"), Sets.hashSet(1L));\n        final FeatureChange featureChange = FeatureChange.remove(area);\n        assertResourceEquals(featureChange, \"serializedRemoveAreaWithDescription.json\", true);\n    }\n\n    @Test\n    public void testDescriptionSerializationUpdateEdge()\n    {\n        final CompleteEdge beforeEdge = new CompleteEdge(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(\"a\", \"1\", \"b\", \"2\"), 1L, 2L, Sets.hashSet(1L, 2L));\n        final CompleteEdge afterEdge = new CompleteEdge(123L, PolyLine.TEST_POLYLINE_2,\n                Maps.hashMap(\"b\", \"2a\", \"c\", \"3\"), 10L, 20L, Sets.hashSet(2L, 3L));\n        final FeatureChange featureChange = FeatureChangeUnitTestFactory.build(ChangeType.ADD,\n                afterEdge, beforeEdge);\n        assertResourceEquals(featureChange, \"serializedUpdateEdgeWithDescription.json\", true);\n    }\n\n    @Test\n    public void testFullAreaSerialization()\n    {\n        final CompleteArea item = new CompleteArea(123L, Polygon.TEST_BUILDING, TAGS, RELATIONS);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedAreaFull.json\", false);\n    }\n\n    @Test\n    public void testFullEdgeSerialization()\n    {\n        final CompleteEdge item = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, TAGS, 456L, 789L,\n                RELATIONS);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedEdgeFull.json\", false);\n    }\n\n    @Test\n    public void testFullLineSerialization()\n    {\n        final CompleteLine item = new CompleteLine(123L, PolyLine.TEST_POLYLINE, TAGS, RELATIONS);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedLineFull.json\", false);\n    }\n\n    @Test\n    public void testFullNodeSerialization()\n    {\n        final CompleteNode item = new CompleteNode(123L, Location.COLOSSEUM, TAGS,\n                Sets.treeSet(456L, 789L), Sets.treeSet(456L, 789L), RELATIONS);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedNodeFull.json\", false);\n    }\n\n    @Test\n    public void testFullPointSerialization()\n    {\n        final CompletePoint item = new CompletePoint(123L, Location.COLOSSEUM, TAGS, RELATIONS);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedPointFull.json\", false);\n    }\n\n    @Test\n    public void testFullRelationSerialization()\n    {\n        final RelationBean members = new RelationBean();\n        members.addItem(456L, \"role1\", ItemType.EDGE);\n        members.addItem(789L, \"role2\", ItemType.AREA);\n        final CompleteRelation item = new CompleteRelation(123L, TAGS, Rectangle.TEST_RECTANGLE,\n                members, Lists.newArrayList(123L), members, 123L, RELATIONS);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedRelationFull.json\", false);\n    }\n\n    @Test\n    public void testNullAreaSerialization()\n    {\n        final CompleteArea item = new CompleteArea(123L, Polygon.TEST_BUILDING, null, null);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedAreaNull.json\", false);\n    }\n\n    @Test\n    public void testNullEdgeSerialization()\n    {\n        final CompleteEdge item = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, null,\n                null);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedEdgeNull.json\", false);\n    }\n\n    @Test\n    public void testNullLineSerialization()\n    {\n        final CompleteLine item = new CompleteLine(123L, PolyLine.TEST_POLYLINE, null, null);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedLineNull.json\", false);\n    }\n\n    @Test\n    public void testNullNodeSerialization()\n    {\n        final CompleteNode item = new CompleteNode(123L, Location.COLOSSEUM, null, null, null,\n                null);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedNodeNull.json\", false);\n    }\n\n    @Test\n    public void testNullPointSerialization()\n    {\n        final CompletePoint item = new CompletePoint(123L, Location.COLOSSEUM, null, null);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedPointNull.json\", false);\n    }\n\n    @Test\n    public void testNullRelationSerialization()\n    {\n        final RelationBean members = new RelationBean();\n        members.addItem(456L, \"role1\", ItemType.EDGE);\n        members.addItem(789L, \"role2\", ItemType.AREA);\n        final CompleteRelation item = new CompleteRelation(123L, null, Rectangle.TEST_RECTANGLE,\n                members, null, null, null, null);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedRelationNull.json\", false);\n    }\n\n    @Test\n    public void testPointTagMapOrderingSerialization()\n    {\n        final Map<String, String> tags = Maps.hashMap(\"tagKey5\", \"tagValue5\", \"tagKey1\",\n                \"tagValue1\", \"tagKey3\", \"tagValue3\", \"tagKey6\", \"tagValue6\", \"tagKey4\", \"tagValue4\",\n                \"tagKey2\", \"tagValue2\");\n        final CompletePoint item = new CompletePoint(123L, Location.COLOSSEUM, tags, RELATIONS);\n        final FeatureChange featureChange = FeatureChange.add(item);\n        featureChange.addMetaData(\"key3\", \"value3\");\n        featureChange.addMetaData(\"key1\", \"value1\");\n        featureChange.addMetaData(\"key2\", \"value2\");\n        assertResourceEquals(featureChange, \"serializedPointWithTags.json\", false);\n    }\n\n    @Test\n    public void testRemoveAreaSerialization()\n    {\n        final CompleteArea item = CompleteArea\n                .shallowFrom(new CompleteArea(123L, Polygon.TEST_BUILDING, null, null));\n        final FeatureChange featureChange = FeatureChange.remove(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedAreaRemove.json\", false);\n    }\n\n    @Test\n    public void testRemoveEdgeSerialization()\n    {\n        final CompleteEdge item = CompleteEdge.shallowFrom(\n                new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, null, null));\n        final FeatureChange featureChange = FeatureChange.remove(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedEdgeRemove.json\", false);\n    }\n\n    @Test\n    public void testRemoveLineSerialization()\n    {\n        final CompleteLine item = CompleteLine\n                .shallowFrom(new CompleteLine(123L, PolyLine.TEST_POLYLINE, null, null));\n        final FeatureChange featureChange = FeatureChange.remove(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedLineRemove.json\", false);\n    }\n\n    @Test\n    public void testRemoveNodeSerialization()\n    {\n        final CompleteNode item = CompleteNode\n                .shallowFrom(new CompleteNode(123L, Location.COLOSSEUM, null, null, null, null));\n        final FeatureChange featureChange = FeatureChange.remove(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedNodeRemove.json\", false);\n    }\n\n    @Test\n    public void testRemovePointSerialization()\n    {\n        final CompletePoint item = CompletePoint\n                .shallowFrom(new CompletePoint(123L, Location.COLOSSEUM, null, null));\n        final FeatureChange featureChange = FeatureChange.remove(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedPointRemove.json\", false);\n    }\n\n    @Test\n    public void testRemoveRelationSerialization()\n    {\n        final RelationBean members = new RelationBean();\n        members.addItem(456L, \"role1\", ItemType.EDGE);\n        members.addItem(789L, \"role2\", ItemType.AREA);\n        final CompleteRelation temporary = new CompleteRelation(123L, null,\n                Rectangle.TEST_RECTANGLE, members, null, null, null, null);\n        final CompleteRelation item = CompleteRelation.shallowFrom(temporary);\n        final FeatureChange featureChange = FeatureChange.remove(item);\n        featureChange.addMetaData(\"key\", \"value\");\n        assertResourceEquals(featureChange, \"serializedRelationRemove.json\", false);\n    }\n\n    private void assertResourceEquals(final FeatureChange featureChange, final String fileName,\n            final boolean checkDescription)\n    {\n        final String expected = new InputStreamResource(\n                () -> FeatureChangeGeoJsonSerializerTest.class.getResourceAsStream(fileName)).all();\n        Assert.assertEquals(expected, featureChange.toPrettyGeoJson(checkDescription));\n        Assert.assertEquals(expected.replaceAll(System.lineSeparator() + \" *\", \"\")\n                .replaceAll(\"(?<!Member): *\", \":\"), featureChange.toGeoJson(checkDescription));\n        final File temporary = File.temporary();\n        try\n        {\n            featureChange.save(temporary, checkDescription);\n            Assert.assertEquals(expected, temporary.all());\n        }\n        finally\n        {\n            temporary.delete();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/serializer/FeatureChangeGeoJsonSerializerTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.serializer;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * Test rule for {@link FeatureChangeGeoJsonSerializerTest}\n *\n * @author Taylor Smock\n */\npublic class FeatureChangeGeoJsonSerializerTestRule extends CoreTestRule\n{\n\n    @TestAtlas(nodes = {\n            @Node(id = \"3782847618000000\", coordinates = @Loc(value = \"-38.359739,176.6485281\")),\n            @Node(id = \"3782847620000000\", coordinates = @Loc(value = \"-38.3597482,176.6482709\")),\n            @Node(id = \"3782847621000000\", coordinates = @Loc(value = \"-38.3597792,176.6504496\")),\n            @Node(id = \"3782847622000000\", coordinates = @Loc(value = \"-38.3598164,176.6500507\")),\n            @Node(id = \"3782847623000000\", coordinates = @Loc(value = \"-38.3598385,176.6506542\")),\n            @Node(id = \"3782847624000000\", coordinates = @Loc(value = \"-38.3598491,176.6479909\")),\n            @Node(id = \"3782847628000000\", coordinates = @Loc(value = \"-38.3599877,176.6508922\")),\n            @Node(id = \"3782847630000000\", coordinates = @Loc(value = \"-38.3600645,176.6571985\")),\n            @Node(id = \"3782847632000000\", coordinates = @Loc(value = \"-38.3600874,176.651242\")),\n            @Node(id = \"3782847634000000\", coordinates = @Loc(value = \"-38.3600989,176.6568853\")),\n            @Node(id = \"3782847635000000\", coordinates = @Loc(value = \"-38.360114,176.6475488\")),\n            @Node(id = \"3782847636000000\", coordinates = @Loc(value = \"-38.3601217,176.6497252\")),\n            @Node(id = \"3782847637000000\", coordinates = @Loc(value = \"-38.3601265,176.6573449\")),\n            @Node(id = \"3782847638000000\", coordinates = @Loc(value = \"-38.3601276,176.6489811\")),\n            @Node(id = \"3782847640000000\", coordinates = @Loc(value = \"-38.3602169,176.6513931\")),\n            @Node(id = \"3782847641000000\", coordinates = @Loc(value = \"-38.3602199,176.649531\")),\n            @Node(id = \"3782847642000000\", coordinates = @Loc(value = \"-38.3602506,176.6493036\")),\n            @Node(id = \"3782847644000000\", coordinates = @Loc(value = \"-38.3603294,176.6514\")),\n            @Node(id = \"3782847647000000\", coordinates = @Loc(value = \"-38.3604405,176.6562182\")),\n            @Node(id = \"3782847648000000\", coordinates = @Loc(value = \"-38.360444,176.6537263\")),\n            @Node(id = \"3782847649000000\", coordinates = @Loc(value = \"-38.3604541,176.6534691\")),\n            @Node(id = \"3782847650000000\", coordinates = @Loc(value = \"-38.360461,176.6467102\")),\n            @Node(id = \"3782847651000000\", coordinates = @Loc(value = \"-38.3604673,176.6578814\")),\n            @Node(id = \"3782847652000000\", coordinates = @Loc(value = \"-38.360514,176.6512962\")),\n            @Node(id = \"3782847653000000\", coordinates = @Loc(value = \"-38.3605184,176.6541608\")),\n            @Node(id = \"3782847654000000\", coordinates = @Loc(value = \"-38.3606028,176.6531058\")),\n            @Node(id = \"3782847655000000\", coordinates = @Loc(value = \"-38.360605,176.6524474\")),\n            @Node(id = \"3782847656000000\", coordinates = @Loc(value = \"-38.360621,176.6514461\")),\n            @Node(id = \"3782847657000000\", coordinates = @Loc(value = \"-38.3606326,176.6582919\")),\n            @Node(id = \"3782847658000000\", coordinates = @Loc(value = \"-38.3606821,176.6516222\")),\n            @Node(id = \"3782847659000000\", coordinates = @Loc(value = \"-38.3606835,176.6521662\")),\n            @Node(id = \"3782847660000000\", coordinates = @Loc(value = \"-38.3606939,176.6554606\")),\n            @Node(id = \"3782847661000000\", coordinates = @Loc(value = \"-38.3607205,176.6547748\")),\n            @Node(id = \"3782847662000000\", coordinates = @Loc(value = \"-38.3608421,176.6461893\")),\n            @Node(id = \"3782847663000000\", coordinates = @Loc(value = \"-38.3608916,176.6585951\")),\n            @Node(id = \"3782847664000000\", coordinates = @Loc(value = \"-38.3609168,176.6586093\")),\n            @Node(id = \"3782847665000000\", coordinates = @Loc(value = \"-38.3610628,176.6457163\")),\n            @Node(id = \"3782847667000000\", coordinates = @Loc(value = \"-38.3611523,176.6451483\")),\n            @Node(id = \"3782847668000000\", coordinates = @Loc(value = \"-38.3611786,176.6587555\")),\n            @Node(id = \"3782847669000000\", coordinates = @Loc(value = \"-38.3612855,176.6589053\")),\n            @Node(id = \"3782847670000000\", coordinates = @Loc(value = \"-38.3613435,176.6609424\")),\n            @Node(id = \"3782847671000000\", coordinates = @Loc(value = \"-38.3613466,176.6590803\")),\n            @Node(id = \"3782847672000000\", coordinates = @Loc(value = \"-38.3613471,176.6448127\")),\n            @Node(id = \"3782847673000000\", coordinates = @Loc(value = \"-38.3613802,176.6611733\")),\n            @Node(id = \"3782847674000000\", coordinates = @Loc(value = \"-38.3614105,176.6603732\")),\n            @Node(id = \"3782847675000000\", coordinates = @Loc(value = \"-38.3614201,176.6595148\")),\n            @Node(id = \"3782847677000000\", coordinates = @Loc(value = \"-38.3614892,176.6445679\")),\n            @Node(id = \"3782847678000000\", coordinates = @Loc(value = \"-38.361592,176.6442583\")),\n            @Node(id = \"3782847679000000\", coordinates = @Loc(value = \"-38.3616204,176.6434867\")),\n            @Node(id = \"3782847681000000\", coordinates = @Loc(value = \"-38.3616489,176.6629946\")),\n            @Node(id = \"3782847684000000\", coordinates = @Loc(value = \"-38.3616874,176.6631695\")),\n            @Node(id = \"3782847685000000\", coordinates = @Loc(value = \"-38.3616943,176.6623956\")),\n            @Node(id = \"3782847686000000\", coordinates = @Loc(value = \"-38.3617311,176.6650042\")),\n            @Node(id = \"3782847687000000\", coordinates = @Loc(value = \"-38.3617632,176.6641469\")),\n            @Node(id = \"3782847688000000\", coordinates = @Loc(value = \"-38.3617811,176.6392288\")),\n            @Node(id = \"3782847689000000\", coordinates = @Loc(value = \"-38.3617894,176.6652375\")),\n            @Node(id = \"3782847690000000\", coordinates = @Loc(value = \"-38.3617976,176.6388002\")),\n            @Node(id = \"3782847691000000\", coordinates = @Loc(value = \"-38.3618447,176.639949\")),\n            @Node(id = \"3782847692000000\", coordinates = @Loc(value = \"-38.3618555,176.6634943\")),\n            @Node(id = \"3782847693000000\", coordinates = @Loc(value = \"-38.3618633,176.6638956\")),\n            @Node(id = \"3782847694000000\", coordinates = @Loc(value = \"-38.3618923,176.6637253\")),\n            @Node(id = \"3782847696000000\", coordinates = @Loc(value = \"-38.3620171,176.6663683\")),\n            @Node(id = \"3782847697000000\", coordinates = @Loc(value = \"-38.3620421,176.6376985\")),\n            @Node(id = \"3782847698000000\", coordinates = @Loc(value = \"-38.3620548,176.6665718\")),\n            @Node(id = \"3782847699000000\", coordinates = @Loc(value = \"-38.3620629,176.6407357\")),\n            @Node(id = \"3782847700000000\", coordinates = @Loc(value = \"-38.362069,176.6655979\")),\n            @Node(id = \"3782847701000000\", coordinates = @Loc(value = \"-38.3620721,176.6404785\")),\n            @Node(id = \"3782847702000000\", coordinates = @Loc(value = \"-38.362074,176.6368411\")),\n            @Node(id = \"3782847703000000\", coordinates = @Loc(value = \"-38.3621267,176.6426298\")),\n            @Node(id = \"3782847704000000\", coordinates = @Loc(value = \"-38.3621479,176.6366732\")),\n            @Node(id = \"3782847705000000\", coordinates = @Loc(value = \"-38.3621652,176.636216\")),\n            @Node(id = \"3782847706000000\", coordinates = @Loc(value = \"-38.3621668,176.6683826\")),\n            @Node(id = \"3782847707000000\", coordinates = @Loc(value = \"-38.3621879,176.6690136\")),\n            @Node(id = \"3782847708000000\", coordinates = @Loc(value = \"-38.3621892,176.6707614\")),\n            @Node(id = \"3782847709000000\", coordinates = @Loc(value = \"-38.3622005,176.642463\")),\n            @Node(id = \"3782847710000000\", coordinates = @Loc(value = \"-38.3622158,176.670047\")),\n            @Node(id = \"3782847711000000\", coordinates = @Loc(value = \"-38.3622256,176.6412033\")),\n            @Node(id = \"3782847712000000\", coordinates = @Loc(value = \"-38.3622337,176.671365\")),\n            @Node(id = \"3782847713000000\", coordinates = @Loc(value = \"-38.3622457,176.6710507\")),\n            @Node(id = \"3782847714000000\", coordinates = @Loc(value = \"-38.3622605,176.641464\")),\n            @Node(id = \"3782847716000000\", coordinates = @Loc(value = \"-38.3622664,176.66753\")),\n            @Node(id = \"3782847717000000\", coordinates = @Loc(value = \"-38.3622802,176.6671585\")),\n            @Node(id = \"3782847718000000\", coordinates = @Loc(value = \"-38.3622832,176.63608\")),\n            @Node(id = \"3782847719000000\", coordinates = @Loc(value = \"-38.3622962,176.6697076\")),\n            @Node(id = \"3782847720000000\", coordinates = @Loc(value = \"-38.3623063,176.6694504\")),\n            @Node(id = \"3782847721000000\", coordinates = @Loc(value = \"-38.3623309,176.6723745\")),\n            @Node(id = \"3782847722000000\", coordinates = @Loc(value = \"-38.3623558,176.6717172\")),\n            @Node(id = \"3782847723000000\", coordinates = @Loc(value = \"-38.3623677,176.6725768\")),\n            @Node(id = \"3782847725000000\", coordinates = @Loc(value = \"-38.3627685,176.673317\")),\n            @Node(id = \"3782847726000000\", coordinates = @Loc(value = \"-38.3628043,176.6360268\")),\n            @Node(id = \"3782847727000000\", coordinates = @Loc(value = \"-38.3628906,176.6736681\")),\n            @Node(id = \"3782847728000000\", coordinates = @Loc(value = \"-38.3629425,176.6740728\")),\n            @Node(id = \"3782847729000000\", coordinates = @Loc(value = \"-38.3629627,176.674926\")),\n            @Node(id = \"3782847732000000\", coordinates = @Loc(value = \"-38.3629646,176.6359503\")),\n            @Node(id = \"3782847735000000\", coordinates = @Loc(value = \"-38.3633276,176.6353128\")),\n            @Node(id = \"3782847737000000\", coordinates = @Loc(value = \"-38.363705,176.6348776\")),\n            @Node(id = \"3782847740000000\", coordinates = @Loc(value = \"-38.3641479,176.6344755\")),\n            @Node(id = \"3782847744000000\", coordinates = @Loc(value = \"-38.3647737,176.6340268\")) }, lines = {\n                    @Line(id = \"374902834000000\", coordinates = {\n                            @Loc(value = \"-38.3629627,176.674926\"),\n                            @Loc(value = \"-38.3629425,176.6740728\"),\n                            @Loc(value = \"-38.3628906,176.6736681\"),\n                            @Loc(value = \"-38.3627685,176.673317\"),\n                            @Loc(value = \"-38.3623677,176.6725768\"),\n                            @Loc(value = \"-38.3623309,176.6723745\"),\n                            @Loc(value = \"-38.3623558,176.6717172\"),\n                            @Loc(value = \"-38.3622337,176.671365\"),\n                            @Loc(value = \"-38.3622457,176.6710507\"),\n                            @Loc(value = \"-38.3621892,176.6707614\"),\n                            @Loc(value = \"-38.3622158,176.670047\"),\n                            @Loc(value = \"-38.3622962,176.6697076\"),\n                            @Loc(value = \"-38.3623063,176.6694504\"),\n                            @Loc(value = \"-38.3621879,176.6690136\"),\n                            @Loc(value = \"-38.3621668,176.6683826\"),\n                            @Loc(value = \"-38.3622664,176.66753\"),\n                            @Loc(value = \"-38.3622802,176.6671585\"),\n                            @Loc(value = \"-38.3620548,176.6665718\"),\n                            @Loc(value = \"-38.3620171,176.6663683\"),\n                            @Loc(value = \"-38.362069,176.6655979\"),\n                            @Loc(value = \"-38.3617894,176.6652375\"),\n                            @Loc(value = \"-38.3617311,176.6650042\"),\n                            @Loc(value = \"-38.3617632,176.6641469\"),\n                            @Loc(value = \"-38.3618633,176.6638956\"),\n                            @Loc(value = \"-38.3618923,176.6637253\"),\n                            @Loc(value = \"-38.3618555,176.6634943\"),\n                            @Loc(value = \"-38.3616874,176.6631695\"),\n                            @Loc(value = \"-38.3616489,176.6629946\"),\n                            @Loc(value = \"-38.3616943,176.6623956\"),\n                            @Loc(value = \"-38.3613802,176.6611733\"),\n                            @Loc(value = \"-38.3613435,176.6609424\"),\n                            @Loc(value = \"-38.3614105,176.6603732\"),\n                            @Loc(value = \"-38.3614201,176.6595148\"),\n                            @Loc(value = \"-38.3613466,176.6590803\"),\n                            @Loc(value = \"-38.3612855,176.6589053\"),\n                            @Loc(value = \"-38.3611786,176.6587555\"),\n                            @Loc(value = \"-38.3609168,176.6586093\"),\n                            @Loc(value = \"-38.3608916,176.6585951\"),\n                            @Loc(value = \"-38.3606326,176.6582919\"),\n                            @Loc(value = \"-38.3604673,176.6578814\"),\n                            @Loc(value = \"-38.3601265,176.6573449\"),\n                            @Loc(value = \"-38.3600645,176.6571985\"),\n                            @Loc(value = \"-38.3600989,176.6568853\"),\n                            @Loc(value = \"-38.3604405,176.6562182\"),\n                            @Loc(value = \"-38.3606939,176.6554606\"),\n                            @Loc(value = \"-38.3607205,176.6547748\"),\n                            @Loc(value = \"-38.3605184,176.6541608\"),\n                            @Loc(value = \"-38.360444,176.6537263\"),\n                            @Loc(value = \"-38.3604541,176.6534691\"),\n                            @Loc(value = \"-38.3606028,176.6531058\"),\n                            @Loc(value = \"-38.360605,176.6524474\"),\n                            @Loc(value = \"-38.3606835,176.6521662\"),\n                            @Loc(value = \"-38.3606821,176.6516222\"),\n                            @Loc(value = \"-38.360621,176.6514461\"),\n                            @Loc(value = \"-38.360514,176.6512962\"),\n                            @Loc(value = \"-38.3603294,176.6514\"),\n                            @Loc(value = \"-38.3602169,176.6513931\"),\n                            @Loc(value = \"-38.3600874,176.651242\"),\n                            @Loc(value = \"-38.3599877,176.6508922\"),\n                            @Loc(value = \"-38.3598385,176.6506542\"),\n                            @Loc(value = \"-38.3597792,176.6504496\"),\n                            @Loc(value = \"-38.3598164,176.6500507\"),\n                            @Loc(value = \"-38.3601217,176.6497252\"),\n                            @Loc(value = \"-38.3602199,176.649531\"),\n                            @Loc(value = \"-38.3602506,176.6493036\"),\n                            @Loc(value = \"-38.3601276,176.6489811\"),\n                            @Loc(value = \"-38.359739,176.6485281\"),\n                            @Loc(value = \"-38.3597482,176.6482709\"),\n                            @Loc(value = \"-38.3598491,176.6479909\"),\n                            @Loc(value = \"-38.360114,176.6475488\"),\n                            @Loc(value = \"-38.360461,176.6467102\"),\n                            @Loc(value = \"-38.3608421,176.6461893\"),\n                            @Loc(value = \"-38.3610628,176.6457163\"),\n                            @Loc(value = \"-38.3611523,176.6451483\"),\n                            @Loc(value = \"-38.3613471,176.6448127\"),\n                            @Loc(value = \"-38.3614892,176.6445679\"),\n                            @Loc(value = \"-38.361592,176.6442583\"),\n                            @Loc(value = \"-38.3616204,176.6434867\"),\n                            @Loc(value = \"-38.3621267,176.6426298\"),\n                            @Loc(value = \"-38.3622005,176.642463\"),\n                            @Loc(value = \"-38.3622605,176.641464\"),\n                            @Loc(value = \"-38.3622256,176.6412033\"),\n                            @Loc(value = \"-38.3620629,176.6407357\"),\n                            @Loc(value = \"-38.3620721,176.6404785\"),\n                            @Loc(value = \"-38.3618447,176.639949\"),\n                            @Loc(value = \"-38.3617811,176.6392288\"),\n                            @Loc(value = \"-38.3617976,176.6388002\"),\n                            @Loc(value = \"-38.3620421,176.6376985\"),\n                            @Loc(value = \"-38.362074,176.6368411\"),\n                            @Loc(value = \"-38.3621479,176.6366732\"),\n                            @Loc(value = \"-38.3621652,176.636216\"),\n                            @Loc(value = \"-38.3622832,176.63608\"),\n                            @Loc(value = \"-38.3628043,176.6360268\"),\n                            @Loc(value = \"-38.3629646,176.6359503\"),\n                            @Loc(value = \"-38.3633276,176.6353128\"),\n                            @Loc(value = \"-38.363705,176.6348776\"),\n                            @Loc(value = \"-38.3641479,176.6344755\"),\n                            @Loc(value = \"-38.3647737,176.6340268\") }, tags = {\n                                    \"waterway=stream\" }) })\n    private Atlas longWaterWayAtlas;\n\n    /**\n     * Get a fairly long waterway to check if reversed geometry is trimmed\n     *\n     * @return An atlas with only a long waterway\n     */\n    public Atlas longWaterWayAtlas()\n    {\n        return this.longWaterWayAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/change/validators/ChangeValidatorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.change.validators;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.change.ChangeBuilder;\nimport org.openstreetmap.atlas.geography.atlas.change.FeatureChange;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class ChangeValidatorTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testMatchingEdgeEndNodes()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange\n                .add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, 456L, null)));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                null, 456L, null, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMatchingEdgeNodes()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange\n                .add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, 654L, 456L, null)));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                null, 456L, 654L, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMatchingEdgePolyLines()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange\n                .add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, null, null)));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                null, null, null, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMatchingEdgeStartNodes()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange\n                .add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, 456L, null, null)));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                null, null, 456L, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMatchingEdgeTags()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange.add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(\"key1\", \"value1\"), null, null, null)));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                Maps.hashMap(\"key1\", \"value1\"), null, null, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMismatchingEdgeAsymmetricParentRelations()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                null, null, null, Sets.hashSet(13L))));\n        builder.get();\n    }\n\n    @Test\n    public void testMismatchingEdgeEndNodes()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"does not match its backward edge start node\");\n\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange\n                .add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, 456L, null)));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                null, 654L, null, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMismatchingEdgeParentRelations()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange.add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null,\n                null, Sets.hashSet(12L))));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                null, null, null, Sets.hashSet(13L))));\n        builder.get();\n    }\n\n    @Test\n    public void testMismatchingEdgePolyLines()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"does not match its backward edge polyline\");\n\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange\n                .add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, null, null)));\n        builder.add(FeatureChange\n                .add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE, null, null, null, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMismatchingEdgeStartNodes()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"does not match its backward edge end node\");\n\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange\n                .add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, 456L, null, null)));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                null, null, 654L, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMismatchingEdgeTags()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange.add(new CompleteEdge(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(\"key1\", \"value1\"), null, null, null)));\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                Maps.hashMap(\"key2\", \"value2\"), null, null, null)));\n        builder.get();\n    }\n\n    @Test\n    public void testMissingForwardEdge()\n    {\n        final ChangeBuilder builder = new ChangeBuilder();\n        builder.add(FeatureChange.add(new CompleteEdge(-123L, PolyLine.TEST_POLYLINE.reversed(),\n                Maps.hashMap(), null, null, null)));\n        builder.get();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/changeset/BinaryChangeSetSerializerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.changeset;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Random;\nimport java.util.UUID;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\n\n/**\n * Tests for {@link BinaryChangeSetDeserializer}.\n *\n * @author mkalender\n * @deprecated - see new API under org.openstreetmap.atlas.geography.atlas.change package.\n */\n@Deprecated\npublic class BinaryChangeSetSerializerTest\n{\n    private static final Random RANDOM_GENERATOR = new Random();\n    private static final List<ItemType> ITEM_TYPES = Arrays.asList(ItemType.values());\n    private static long NEW_ITEM_IDENTIFIER = 1L;\n\n    private static ChangeItem generateChangeItem(final ItemType type, final ChangeAction action)\n    {\n        final List<Location> locations = new ArrayList<>();\n        switch (type)\n        {\n            case AREA:\n            case LINE:\n            case EDGE:\n                final int size = 2 + RANDOM_GENERATOR.nextInt(50);\n                for (int i = 0; i < size; i++)\n                {\n                    locations.add(Location.random(Rectangle.MAXIMUM));\n                }\n                break;\n            case POINT:\n            case NODE:\n            default:\n                locations.add(Location.random(Rectangle.MAXIMUM));\n        }\n\n        return new SimpleChangeItem(-1 * NEW_ITEM_IDENTIFIER++, randomString(), type, action,\n                locations, RandomTagsSupplier.randomTags(5));\n    }\n\n    private static List<ChangeItem> generateChangeItems(final int countPerTypePerAction)\n    {\n        final List<ChangeItem> changeItems = new ArrayList<>();\n        for (final ChangeAction action : ChangeAction.values())\n        {\n            for (int i = 0; i < countPerTypePerAction; i++)\n            {\n                changeItems\n                        .addAll(generateChangeItemsPerAction(countPerTypePerAction, action, true));\n            }\n        }\n        return changeItems;\n    }\n\n    private static List<ChangeItem> generateChangeItemsPerAction(final int countPerTypePerAction,\n            final ChangeAction action, final boolean addRelations)\n    {\n        final List<ChangeItem> changeItems = new ArrayList<>();\n        for (final ItemType type : ITEM_TYPES)\n        {\n            // Skip relations\n            if (type == ItemType.RELATION)\n            {\n                continue;\n            }\n\n            for (int i = 0; i < countPerTypePerAction; i++)\n            {\n                changeItems.addAll(\n                        generateChangeItemsPerTypePerAction(countPerTypePerAction, type, action));\n            }\n\n        }\n\n        if (addRelations)\n        {\n            // Generate relation changes\n            for (int i = 0; i < countPerTypePerAction; i++)\n            {\n                changeItems.add(\n                        generateRelationChangeItem(countPerTypePerAction, action, changeItems));\n            }\n        }\n\n        return changeItems;\n    }\n\n    private static List<ChangeItem> generateChangeItemsPerTypePerAction(final int count,\n            final ItemType type, final ChangeAction action)\n    {\n        final List<ChangeItem> changeItems = new ArrayList<>();\n        for (int i = 0; i < count; i++)\n        {\n            changeItems.add(generateChangeItem(type, action));\n        }\n        return changeItems;\n    }\n\n    private static ChangeItem generateRelationChangeItem(final int memberCount,\n            final ChangeAction action, final List<ChangeItem> candidateMembers)\n    {\n        final int candidateSize = candidateMembers.size();\n        final List<ChangeItemMember> members = new ArrayList<>();\n        for (int i = 0; i < memberCount; i++)\n        {\n            // Pick a random change item as a member\n            // If picked change item is being deleted, then make up a change item member\n            final ChangeItem candidateMember = candidateMembers\n                    .get(RANDOM_GENERATOR.nextInt(candidateSize));\n\n            // Come up with a role\n            // Role is null most of the type\n            String role = null;\n            if (RANDOM_GENERATOR.nextInt(10) < 4)\n            {\n                role = randomString();\n            }\n\n            if (candidateMember.getAction() != ChangeAction.DELETE)\n            {\n                members.add(new SimpleChangeItemMember(candidateMember.getIdentifier(), role,\n                        candidateMember.getType()));\n            }\n            else\n            {\n                members.add(new SimpleChangeItemMember(RANDOM_GENERATOR.nextLong(), role,\n                        ITEM_TYPES.get(RANDOM_GENERATOR.nextInt(ITEM_TYPES.size()))));\n            }\n        }\n\n        final SimpleChangeItem relationChange = new SimpleChangeItem();\n        relationChange.setIdentifier(-1 * NEW_ITEM_IDENTIFIER++);\n        relationChange.setSourceName(randomString());\n        relationChange.setType(ItemType.RELATION);\n        relationChange.setAction(action);\n        relationChange.setTags(RandomTagsSupplier.randomTags(5));\n        relationChange.setMembers(members);\n        return relationChange;\n    }\n\n    private static String randomString()\n    {\n        if (RANDOM_GENERATOR.nextBoolean())\n        {\n            // return null;\n        }\n\n        return UUID.randomUUID().toString();\n    }\n\n    private static ChangeSet serializeAndDeserialize(final ChangeSet changeSet) throws Exception\n    {\n        // Serialize\n        final File tempFile = File.temporary();\n        try (BinaryChangeSetSerializer serializer = new BinaryChangeSetSerializer(tempFile))\n        {\n            serializer.accept(changeSet);\n        }\n\n        // Deserialize\n        Optional<ChangeSet> readChangeSet = Optional.empty();\n        try (BinaryChangeSetDeserializer deserializer = new BinaryChangeSetDeserializer(tempFile))\n        {\n            readChangeSet = deserializer.get();\n            Assert.assertTrue(readChangeSet.isPresent());\n        }\n\n        tempFile.delete();\n\n        return readChangeSet.get();\n    }\n\n    private static void validate(final ChangeSet changeSet) throws Exception\n    {\n        // Serialize and then deserialize\n        final ChangeSet readChangeSet = serializeAndDeserialize(changeSet);\n\n        // Validate\n        Assert.assertEquals(changeSet, readChangeSet);\n    }\n\n    @Test\n    public void changeSetWithCreateFromAllNonRelationTypesTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItemsPerAction(5, ChangeAction.CREATE, false));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void changeSetWithDeleteFromAllNonRelationTypesTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItemsPerAction(5, ChangeAction.DELETE, false));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void changeSetWithRandomChangesFromAllTypeAndActions() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItems(1));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void changeSetWithReadFromAllNonRelationTypesTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItemsPerAction(5, ChangeAction.READ, false));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void changeSetWithSingleCreateTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItemsPerAction(1, ChangeAction.CREATE, false));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void changeSetWithSingleDeleteTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItemsPerAction(1, ChangeAction.DELETE, false));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void changeSetWithSingleReadTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItemsPerAction(1, ChangeAction.READ, false));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void changeSetWithSingleUpdateTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItemsPerAction(1, ChangeAction.UPDATE, false));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void changeSetWithUpdateFromAllNonRelationTypesTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n        changeSet.addAll(generateChangeItemsPerAction(5, ChangeAction.UPDATE, false));\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void emptyChangeSetTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void emptyChangeSetWithVersionAndDescriptionTest() throws Exception\n    {\n        // Create\n        final ChangeSet changeSet = new SimpleChangeSet();\n        changeSet.setDescription(randomString());\n        changeSet.setVersion(randomString());\n\n        // Validate\n        validate(changeSet);\n    }\n\n    @Test\n    public void multipleChangeSetTest() throws Exception\n    {\n        // Create one\n        final ChangeSet aChangeSet = new SimpleChangeSet();\n        aChangeSet.setDescription(randomString());\n        aChangeSet.setVersion(randomString());\n\n        // Create another\n        final ChangeSet anotherChangeSet = new SimpleChangeSet();\n        anotherChangeSet.setDescription(randomString());\n        anotherChangeSet.setVersion(randomString());\n        anotherChangeSet.addAll(generateChangeItemsPerAction(1, ChangeAction.UPDATE, false));\n\n        // Create a third one\n        final ChangeSet aThirdChangeSet = new SimpleChangeSet();\n        aThirdChangeSet.setDescription(randomString());\n        aThirdChangeSet.setVersion(randomString());\n        aThirdChangeSet.addAll(generateChangeItems(1));\n\n        // Create a fourth null one\n        final ChangeSet aFourthChangeSet = null;\n\n        // Serialize\n        final File tempFile = File.temporary();\n        try (BinaryChangeSetSerializer serializer = new BinaryChangeSetSerializer(tempFile))\n        {\n            serializer.accept(aChangeSet);\n            serializer.accept(anotherChangeSet);\n            serializer.accept(aThirdChangeSet);\n            serializer.accept(aFourthChangeSet);\n        }\n\n        // Deserialize\n        try (BinaryChangeSetDeserializer deserializer = new BinaryChangeSetDeserializer(tempFile))\n        {\n            // Validate\n            Optional<ChangeSet> readChangeSet = deserializer.get();\n            Assert.assertTrue(readChangeSet.isPresent());\n            Assert.assertEquals(readChangeSet.get(), aChangeSet);\n\n            // Validate\n            readChangeSet = deserializer.get();\n            Assert.assertTrue(readChangeSet.isPresent());\n            Assert.assertEquals(readChangeSet.get(), anotherChangeSet);\n\n            // Validate\n            readChangeSet = deserializer.get();\n            Assert.assertTrue(readChangeSet.isPresent());\n            Assert.assertEquals(readChangeSet.get(), aThirdChangeSet);\n\n            // Validate\n            readChangeSet = deserializer.get();\n            Assert.assertFalse(readChangeSet.isPresent());\n        }\n\n        // Delete temp file\n        tempFile.delete();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/AtlasFeatureCountsSubCommandTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\n\nimport org.junit.AfterClass;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Test case verifying that protecting against multiple concurrent threads in the AtlasReader report\n * the right output AtlasFeatureCountsSubCommand class\n *\n * @author cstaylor\n */\npublic class AtlasFeatureCountsSubCommandTestCase\n{\n    @ClassRule\n    public static AtlasFeatureCountsSubCommandTestCaseRule setup = new AtlasFeatureCountsSubCommandTestCaseRule();\n\n    private static File temporaryFolder;\n\n    @AfterClass\n    public static void cleanupAtlasesOnDisk() throws IOException\n    {\n        temporaryFolder.deleteRecursively();\n    }\n\n    @BeforeClass\n    public static void prepareAtlasesOnDisk() throws IOException\n    {\n        final File temp = File.temporaryFolder();\n        setup.getFirstAtlas().save(temp.child(\"first.atlas\"));\n        setup.getSecondAtlas().save(temp.child(\"second.atlas\"));\n        setup.getThirdAtlas().save(temp.child(\"third.atlas\"));\n        setup.getFourthAtlas().save(temp.child(\"fourth.atlas\"));\n        temporaryFolder = temp;\n    }\n\n    @Test\n    public void testThreadSafety() throws IOException\n    {\n        final File outputPath = File.temporary();\n        try\n        {\n            AtlasReader.main(\"featureCounts\",\n                    String.format(\"-input=%s\",\n                            AtlasFeatureCountsSubCommandTestCase.temporaryFolder),\n                    \"-parallel\", String.format(\"-output=%s\", outputPath));\n            final boolean[] found = { false };\n            Iterables.filter(outputPath.lines(), line -> line.contains(\"JPN-NODE\")).forEach(line ->\n            {\n                final String[] fields = line.split(\":\");\n                Assert.assertEquals(4, Integer.parseInt(fields[1].trim()));\n                found[0] = true;\n            });\n            Assert.assertTrue(found[0]);\n        }\n        finally\n        {\n            outputPath.delete();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/AtlasFeatureCountsSubCommandTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * Test Fixture for the AtlasFeatureCountsSubCommandTestCase class\n *\n * @author cstaylor\n */\npublic class AtlasFeatureCountsSubCommandTestCaseRule extends CoreTestRule\n{\n    private static final String ONE = \"37.33,-122.00\";\n    private static final String TWO = \"37.33,-122.03\";\n    private static final String THREE = \"37.32,-122.03\";\n    private static final String FOUR = \"37.32,-122.00\";\n\n    @TestAtlas(nodes = { @Node(id = \"1\", coordinates = @Loc(value = ONE)) }, iso = \"JPN\")\n    private Atlas firstAtlas;\n\n    @TestAtlas(nodes = { @Node(id = \"2\", coordinates = @Loc(value = TWO)) }, iso = \"JPN\")\n    private Atlas secondAtlas;\n\n    @TestAtlas(nodes = { @Node(id = \"3\", coordinates = @Loc(value = THREE)) }, iso = \"JPN\")\n    private Atlas thirdAtlas;\n\n    @TestAtlas(nodes = { @Node(id = \"4\", coordinates = @Loc(value = FOUR)) }, iso = \"JPN\")\n    private Atlas fourthAtlas;\n\n    public Atlas getFirstAtlas()\n    {\n        return this.firstAtlas;\n    }\n\n    public Atlas getFourthAtlas()\n    {\n        return this.fourthAtlas;\n    }\n\n    public Atlas getSecondAtlas()\n    {\n        return this.secondAtlas;\n    }\n\n    public Atlas getThirdAtlas()\n    {\n        return this.thirdAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/AtlasFindByAtlasIdentifierSubCommandTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.PrintStream;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.AfterClass;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\n/**\n * Unit test for {@link AtlasFindByAtlasIdentifierSubCommand}.\n *\n * @author bbreithaupt\n */\npublic class AtlasFindByAtlasIdentifierSubCommandTest\n{\n    private static final String TXT_PATH = AtlasFindByAtlasIdentifierSubCommandTest.class\n            .getResource(\"DNK_Copenhagen\").getPath();\n\n    private static final File SHARD_PATH = File.temporaryFolder();\n\n    @BeforeClass\n    public static void createBinaryAtlases()\n    {\n        final List<String> shardList = Arrays.asList(TXT_PATH + \"/DNK_1.atlas.txt\",\n                TXT_PATH + \"/DNK_2.atlas.txt\", TXT_PATH + \"/DNK_3.atlas.txt\");\n        shardList.forEach(shard -> new TextAtlasBuilder().read(new File(shard))\n                .save(new File(SHARD_PATH.getPathString() + shard.replace(\"txt\", \"atlas\"))));\n    }\n\n    @AfterClass\n    public static void deleteBinaryAtlases()\n    {\n        SHARD_PATH.deleteRecursively();\n    }\n\n    @Test\n    public void testConsoleOutput()\n    {\n        // Redirect System.out to capture AtlasFindByFeatureIdentifierLocatorSubCommand output\n        final PrintStream originalOut = System.out;\n        final CaptureOutputStream captureStream = new CaptureOutputStream(originalOut);\n        System.setOut(captureStream);\n\n        try\n        {\n            // Run AtlasFindByAtlasIdentifierSubCommand\n            final String[] args = { \"find-atlas-id\",\n                    String.format(\"-input=%1$s\", SHARD_PATH.getPathString()),\n                    \"-id=546649246000001,575954012000000\" };\n            new AtlasReader(args).runWithoutQuitting(args);\n        }\n        finally\n        {\n            // Reset System.out\n            System.setOut(originalOut);\n        }\n\n        Arrays.stream(captureStream.getLog().split(\"\\n\")).forEach(line -> Assert\n                .assertTrue(line.contains(\"DNK_2.atlas\") || line.contains(\"DNK_3.atlas\")));\n    }\n\n    @Test\n    public void testJoinedOutput()\n    {\n        final File temp = File.temporary();\n\n        // Run AtlasFindByAtlasIdentifierSubCommand\n        final String[] args = { \"find-atlas-id\",\n                String.format(\"-input=%1$s\", SHARD_PATH.getPathString()),\n                \"-id=546649246000001,575954012000000\",\n                String.format(\"-joinedOutput=%1$s\", temp.getPathString()) };\n        new AtlasReader(args).runWithoutQuitting(args);\n\n        Assert.assertTrue(temp.length() > 0);\n        temp.delete();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/AtlasJoinerSubCommandTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.AfterClass;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\n/**\n * Unit tests for {@link AtlasJoinerSubCommand}.\n *\n * @author bbreithaupt\n */\npublic class AtlasJoinerSubCommandTest\n{\n    private static final String TXT_PATH = AtlasFindByAtlasIdentifierSubCommandTest.class\n            .getResource(\"DNK_Copenhagen\").getPath();\n\n    private static final File SHARD_PATH = File.temporaryFolder();\n\n    @BeforeClass\n    public static void createBinaryAtlases()\n    {\n        final List<String> shardList = Arrays.asList(TXT_PATH + \"/DNK_1.atlas.txt\",\n                TXT_PATH + \"/DNK_2.atlas.txt\", TXT_PATH + \"/DNK_3.atlas.txt\");\n        shardList.forEach(shard -> new TextAtlasBuilder().read(new File(shard))\n                .save(new File(SHARD_PATH.getPathString() + shard.replace(\"txt\", \"atlas\"))));\n    }\n\n    @AfterClass\n    public static void deleteBinaryAtlases()\n    {\n        SHARD_PATH.deleteRecursively();\n    }\n\n    @Test\n    public void testJoinedOutput()\n    {\n        final File temp = File.temporary();\n\n        // Run AtlasJoinerSubCommand\n        final String[] args = { \"join\", String.format(\"-input=%1$s\", SHARD_PATH),\n                String.format(\"-output=%1$s\", temp.getPathString()) };\n        new AtlasReader(args).runWithoutQuitting(args);\n\n        Assert.assertTrue(temp.length() > 0);\n        temp.delete();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/AtlasSplitterWithSlippyTileCommandTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.items.AreaTestRule;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasCloner;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Test for AtlasSplitterWithSlippyTileCommand\n *\n * @author yalimu\n */\npublic class AtlasSplitterWithSlippyTileCommandTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(AtlasSplitterWithSlippyTileCommandTest.class);\n\n    @Rule\n    public final AreaTestRule rule = new AreaTestRule();\n\n    @Test\n    public void testRun()\n    {\n        final String testAtlasFile = AtlasSplitterWithSlippyTileCommand.class.getResource(\"\")\n                .getPath() + \"test\" + FileSuffix.ATLAS;\n        generateTestAtlasFile(testAtlasFile);\n        AtlasReader.main(\"split\", String.format(\"-input=%s\", testAtlasFile),\n                String.format(\"-zoom_level=%d\", 16),\n                String.format(\"-output=%s\", Paths\n                        .get(AtlasSplitterWithSlippyTileCommand.class.getResource(\"\").getPath())),\n                \"-combine=false\");\n        combineOutputAndCleanUp();\n    }\n\n    private void combineOutputAndCleanUp()\n    {\n        logger.info(\"Checking if splitted Atlas can be combined into the original Atlas\");\n        final String inputFile = AtlasSplitterWithSlippyTileCommand.class.getResource(\"\").getPath();\n        AtlasReader.main(\"join\", String.format(\"-input=%s\", inputFile),\n                String.format(\"-output=%s\",\n                        Paths.get(inputFile + \"combined_output\" + FileSuffix.ATLAS)),\n                \"-combine=false\");\n\n        final Atlas atlasOriginal = new AtlasResourceLoader().load(new File(\n                AtlasSplitterWithSlippyTileCommand.class.getResource(\"test.atlas\").getPath()));\n        final Atlas atlasCombined = new AtlasResourceLoader()\n                .load(new File(inputFile + \"combined_output\" + FileSuffix.ATLAS));\n\n        Assert.assertNotNull(atlasOriginal);\n        Assert.assertNotNull(atlasCombined);\n\n        Assert.assertEquals(\"Check Edges\", atlasCombined.size().getEdgeNumber(),\n                atlasOriginal.size().getEdgeNumber());\n        Assert.assertEquals(\"Check Area\", atlasCombined.size().getAreaNumber(),\n                atlasOriginal.size().getAreaNumber());\n        Assert.assertEquals(\"Check Line\", atlasCombined.size().getLineNumber(),\n                atlasOriginal.size().getLineNumber());\n        Assert.assertEquals(\"Check Point\", atlasCombined.size().getPointNumber(),\n                atlasOriginal.size().getPointNumber());\n\n        removeAllAtlasFiles(inputFile);\n    }\n\n    private void generateTestAtlasFile(final String filePath)\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final PackedAtlas saveMe = new PackedAtlasCloner().cloneFrom(atlas);\n        saveMe.save(new File(filePath));\n    }\n\n    private void removeAllAtlasFiles(final String inputFile)\n    {\n        try\n        {\n            Files.list(Paths.get(inputFile)).forEach(name ->\n            {\n                if (name.getFileName().toString().contains(FileSuffix.ATLAS.toString()))\n                {\n                    new File(name.toAbsolutePath().toString()).delete();\n                }\n            });\n        }\n        catch (final IOException e)\n        {\n            logger.error(\"Error deleting files\" + e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/CaptureOutputStream.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.io.OutputStream;\nimport java.io.PrintStream;\n\n/**\n * Helper class to capture the output stream for programmatic review.\n *\n * @author bbreithaupt\n */\npublic class CaptureOutputStream extends PrintStream\n{\n    private String log = \"\";\n\n    public CaptureOutputStream(final OutputStream out)\n    {\n        super(out);\n    }\n\n    public String getLog()\n    {\n        return this.log;\n    }\n\n    @Override\n    public void print(final String string)\n    {\n        this.log = this.log.concat(string);\n        super.print(string);\n    }\n\n    @Override\n    public PrintStream printf(final String format, final Object... args)\n    {\n        this.log = this.log.concat(String.format(format, args));\n        return super.printf(format, args);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/ComplexBuildingsTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author jamesgage\n */\npublic class ComplexBuildingsTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"complex-SF.txt\")\n    private Atlas stadiumAtlas;\n\n    public Atlas getStadiumAtlas()\n    {\n        return this.stadiumAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/OsmPbfToAtlasSubCommandTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport java.util.Collections;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\n/**\n * Unit tests for {@link OsmPbfToAtlasSubCommand}.\n *\n * @author bbreithaupt\n */\npublic class OsmPbfToAtlasSubCommandTest\n{\n    private static String PBF = OsmPbfToAtlasSubCommandTest.class\n            .getResource(\"world_islands.osm.pbf\").getPath();\n    private static String COUNTRY_BOUNDARY_MAP_TEXT = OsmPbfToAtlasSubCommandTest.class\n            .getResource(\"continent_map.txt\").getPath();\n    private static String EDGE_FILTER = OsmPbfToAtlasSubCommandTest.class\n            .getResource(\"atlas-edge.json\").getPath();\n    private static String WAY_SECTIONING_FILTER = OsmPbfToAtlasSubCommandTest.class\n            .getResource(\"atlas-way-section.json\").getPath();\n    private static String NODE_FILTER = OsmPbfToAtlasSubCommandTest.class\n            .getResource(\"osm-pbf-node.json\").getPath();\n    private static String RELATION_FILTER = OsmPbfToAtlasSubCommandTest.class\n            .getResource(\"osm-pbf-relation.json\").getPath();\n    private static String WAY_FILTER = OsmPbfToAtlasSubCommandTest.class\n            .getResource(\"osm-pbf-way.json\").getPath();\n\n    private static String ATLAS_NAME = \"test_temp.atlas\";\n\n    @Test\n    public void testDefaultConversion()\n    {\n        final File temp = File.temporaryFolder();\n\n        try\n        {\n            // Run OsmPbfToAtlasSubCommand\n            final String[] args = { \"pbf-to-atlas\", String.format(\"-pbf=%s\", PBF),\n                    String.format(\"-output=%s/%s\", temp, ATLAS_NAME) };\n            new AtlasReader(args).runWithoutQuitting(args);\n\n            // Load new atlas\n            final Atlas atlas = new AtlasResourceLoader()\n                    .load(new File(String.format(\"%s/%s\", temp, ATLAS_NAME)));\n\n            // Test for way sectioning\n            Assert.assertNotNull(atlas.edge(87185620000002L));\n            // Test for country map\n            Assert.assertTrue(atlas.edge(87185039000000L).containsValue(\"iso_country_code\",\n                    Collections.singleton(\"UNK\")));\n            // Inverse test for country codes\n            Assert.assertNotNull(atlas.point(1013787604000000L));\n            // Test default edge filter\n            Assert.assertNotNull(atlas.edge(87186304000001L));\n            // Test default filter\n            Assert.assertNotNull(atlas.point(3698322053000000L));\n            // Test default relation filter\n            Assert.assertNotNull(atlas.relation(2693943000000L));\n            // Test default way filter\n            Assert.assertNotNull(atlas.area(167578604000000L));\n            // Test default way section filter\n            Assert.assertNull(atlas.edge(87186195000018L));\n        }\n        finally\n        {\n            temp.deleteRecursively();\n        }\n    }\n\n    @Test\n    public void testFiltersTextMapCountryCodesConversion()\n    {\n        final File temp = File.temporaryFolder();\n\n        try\n        {\n            // Run OsmPbfToAtlasSubCommand\n            final String[] args = { \"pbf-to-atlas\", String.format(\"-pbf=%s\", PBF),\n                    String.format(\"-output=%s/%s\", temp, ATLAS_NAME),\n                    String.format(\"-country-boundary-map=%s\", COUNTRY_BOUNDARY_MAP_TEXT),\n                    \"-country-codes=NAM,EUR\", String.format(\"-edge-filter=%s\", EDGE_FILTER),\n                    String.format(\"-node-filter=%s\", NODE_FILTER),\n                    String.format(\"-relation-filter=%s\", RELATION_FILTER),\n                    String.format(\"-way-filter=%s\", WAY_FILTER),\n                    String.format(\"-way-section-filter=%s\", WAY_SECTIONING_FILTER), };\n            new AtlasReader(args).runWithoutQuitting(args);\n\n            // Load new atlas\n            final Atlas atlas = new AtlasResourceLoader()\n                    .load(new File(String.format(\"%s/%s\", temp, ATLAS_NAME)));\n\n            // Test for way sectioning\n            Assert.assertNotNull(atlas.edge(87185620000002L));\n            // Test for country map\n            // Test for country codes\n            Assert.assertNotNull(atlas.point(1013654453000000L));\n            // Test edge filter\n            Assert.assertNull(atlas.edge(87186304000001L));\n            // Test node filter\n            Assert.assertNull(atlas.point(3698322053000000L));\n            // Test relation filter\n            Assert.assertNull(atlas.relation(2693943000000L));\n            // Test way filter\n            Assert.assertNotNull(atlas.area(167578604000000L));\n            // Test way section filter\n            Assert.assertNotNull(atlas.edge(87186195000018L));\n        }\n        finally\n        {\n            temp.deleteRecursively();\n        }\n    }\n\n    @Test\n    public void testNoSlicingNoRelationsConversion()\n    {\n        final File temp = File.temporaryFolder();\n\n        try\n        {\n            // Run OsmPbfToAtlasSubCommand\n            final String[] args = { \"pbf-to-atlas\", String.format(\"-pbf=%s\", PBF),\n                    String.format(\"-output=%s/%s\", temp, ATLAS_NAME),\n                    String.format(\"-country-boundary-map=%s\", COUNTRY_BOUNDARY_MAP_TEXT),\n                    \"-country-codes=NAM,EUR\", \"-country-slicing=false\", \"-load-relations=false\" };\n            new AtlasReader(args).runWithoutQuitting(args);\n\n            // Load new atlas\n            final Atlas atlas = new AtlasResourceLoader()\n                    .load(new File(String.format(\"%s/%s\", temp, ATLAS_NAME)));\n            // Test for country slicing\n            Assert.assertFalse(atlas.edge(87185039000000L)\n                    .containsKey(Collections.singleton(\"iso_country_code\")));\n            // Test no load relation\n            Assert.assertFalse(atlas.relations().iterator().hasNext());\n        }\n        finally\n        {\n            temp.deleteRecursively();\n        }\n    }\n\n    @Test\n    public void testNoWaysConversion()\n    {\n        final File temp = File.temporaryFolder();\n\n        try\n        {\n            // Run OsmPbfToAtlasSubCommand\n            final String[] args = { \"pbf-to-atlas\", String.format(\"-pbf=%s\", PBF),\n                    String.format(\"-output=%s/%s\", temp, ATLAS_NAME), \"-load-ways=false\" };\n            new AtlasReader(args).runWithoutQuitting(args);\n\n            // Load new atlas\n            final Atlas atlas = new AtlasResourceLoader()\n                    .load(new File(String.format(\"%s/%s\", temp, ATLAS_NAME)));\n\n            // Test no load ways\n            Assert.assertFalse(atlas.areas().iterator().hasNext());\n            Assert.assertFalse(atlas.edges().iterator().hasNext());\n            Assert.assertFalse(atlas.lines().iterator().hasNext());\n        }\n        finally\n        {\n            temp.deleteRecursively();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/command/TinyBuildingsSearchSubCommandTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.command;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.command.buildings.TinyBuildingsSearchSubCommand;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuilding;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuildingFinder;\n\n/**\n * @author jamesgage\n */\npublic class TinyBuildingsSearchSubCommandTest\n{\n    @Rule\n    public final ComplexBuildingsTestRule setup = new ComplexBuildingsTestRule();\n\n    @Test\n    public void testTooSmall()\n    {\n        final TinyBuildingsSearchSubCommand command = new TinyBuildingsSearchSubCommand();\n        final Atlas stadiumAtlas = this.setup.getStadiumAtlas();\n        final ComplexBuildingFinder finder = new ComplexBuildingFinder();\n        final ComplexBuilding complexBuilding = finder.find(stadiumAtlas).iterator().next();\n        Assert.assertFalse(command.tooSmall(complexBuilding));\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteAreaTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class CompleteAreaTest\n{\n    @Rule\n    public CompleteTestRule rule = new CompleteTestRule();\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testAreaShallowCopyNullBounds()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"bounds were null\");\n\n        final CompleteArea area = new CompleteArea(1L, null, null, null);\n        CompleteArea.shallowFrom(area);\n    }\n\n    @Test\n    public void testBloatedEquals()\n    {\n        final CompleteArea area11 = new CompleteArea(123L, null, null, null);\n        final CompleteArea area12 = new CompleteArea(123L, null, null, null);\n        final CompleteArea area21 = new CompleteArea(123L, Polygon.SILICON_VALLEY, null, null);\n        final CompleteArea area22 = new CompleteArea(123L, Polygon.SILICON_VALLEY, null, null);\n        final CompleteArea area23 = new CompleteArea(123L, Polygon.SILICON_VALLEY_2, null, null);\n        final CompleteArea area31 = new CompleteArea(123L, null, Maps.hashMap(\"key\", \"value\"),\n                null);\n        final CompleteArea area32 = new CompleteArea(123L, null, Maps.hashMap(\"key\", \"value\"),\n                null);\n        final CompleteArea area33 = new CompleteArea(123L, null, Maps.hashMap(), null);\n        final CompleteArea area41 = new CompleteArea(123L, null, null, Sets.hashSet(1L, 2L));\n        final CompleteArea area42 = new CompleteArea(123L, null, null, Sets.hashSet(1L, 2L));\n        final CompleteArea area43 = new CompleteArea(123L, null, null, Sets.hashSet(1L));\n\n        Assert.assertEquals(area11, area12);\n        Assert.assertEquals(area21, area22);\n        Assert.assertEquals(area31, area32);\n        Assert.assertEquals(area41, area42);\n\n        Assert.assertNotEquals(area11, area21);\n        Assert.assertNotEquals(area11, area31);\n        Assert.assertNotEquals(area11, area41);\n        Assert.assertNotEquals(area21, area23);\n        Assert.assertNotEquals(area31, area33);\n        Assert.assertNotEquals(area41, area43);\n    }\n\n    @Test\n    public void testFull()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Area source = atlas.area(27);\n        final CompleteArea result = CompleteArea.from(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        Assert.assertEquals(source.asPolygon(), result.asPolygon());\n        Assert.assertEquals(source.getTags(), result.getTags());\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        Assert.assertEquals(result, result.copy());\n    }\n\n    @Test\n    public void testGetGeometry()\n    {\n        final Polygon polygon = new Polygon(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                Location.forString(\"3,3\"), Location.forString(\"1,1\"));\n        final CompleteArea area = new CompleteArea(123L, polygon, null, null);\n        Assert.assertEquals(Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                Location.forString(\"3,3\"), Location.forString(\"1,1\")), area.getGeometry());\n    }\n\n    @Test\n    public void testIsCompletelyShallow()\n    {\n        final CompleteArea superShallow = new CompleteArea(123L, null, null, null);\n        Assert.assertTrue(superShallow.isShallow());\n    }\n\n    @Test\n    public void testNonFullAreaCopy()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"but it was not full\");\n\n        final CompleteArea area = new CompleteArea(1L, null, null, null);\n        CompleteArea.from(area);\n    }\n\n    @Test\n    public void testShallow()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Area source = atlas.area(27);\n        final CompleteArea result = CompleteArea.shallowFrom(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        result.withPolygon(Polygon.TEST_BUILDING);\n        // When we update a polygon, the bounds should update to the bounds of the new polygon\n        Assert.assertEquals(Rectangle.forLocated(Polygon.TEST_BUILDING), result.bounds());\n        final Map<String, String> tags = Maps.hashMap(\"key\", \"value\");\n        result.withTags(tags);\n        Assert.assertEquals(tags, result.getTags());\n        result.withRelationIdentifiers(source.relations().stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet()));\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        result.withPolygon(Polygon.SILICON_VALLEY);\n        // When we update the polygon again, the bounds recalculation should \"forget\" about the\n        // first update\n        Assert.assertEquals(Rectangle.forLocated(Polygon.SILICON_VALLEY), result.bounds());\n    }\n\n    @Test\n    public void testToWkt()\n    {\n        final CompleteArea area1 = new CompleteArea(123L);\n        area1.withPolygon(\n                Rectangle.forCorners(Location.forString(\"0,0\"), Location.forString(\"1,1\")));\n        Assert.assertEquals(\"POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))\", area1.toWkt());\n\n        final CompleteArea area2 = new CompleteArea(123L);\n        Assert.assertNull(area2.toWkt());\n    }\n\n    @Test\n    public void testWithGeometry()\n    {\n        final CompleteArea area = new CompleteArea(1L);\n        area.withGeometry(\n                Arrays.asList(Location.COLOSSEUM, Location.CENTER, Location.EIFFEL_TOWER));\n        Assert.assertEquals(\n                new Polygon(\n                        Arrays.asList(Location.COLOSSEUM, Location.CENTER, Location.EIFFEL_TOWER)),\n                area.asPolygon());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteEdgeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class CompleteEdgeTest\n{\n    @Rule\n    public CompleteTestRule rule = new CompleteTestRule();\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testBloatedEquals()\n    {\n        final CompleteEdge edge11 = new CompleteEdge(123L, null, null, null, null, null);\n        final CompleteEdge edge12 = new CompleteEdge(123L, null, null, null, null, null);\n        final CompleteEdge edge21 = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, null,\n                null);\n        final CompleteEdge edge22 = new CompleteEdge(123L, PolyLine.TEST_POLYLINE, null, null, null,\n                null);\n        final CompleteEdge edge23 = new CompleteEdge(123L, Polygon.SILICON_VALLEY_2, null, null,\n                null, null);\n        final CompleteEdge edge31 = new CompleteEdge(123L, null, Maps.hashMap(\"key\", \"value\"), null,\n                null, null);\n        final CompleteEdge edge32 = new CompleteEdge(123L, null, Maps.hashMap(\"key\", \"value\"), null,\n                null, null);\n        final CompleteEdge edge33 = new CompleteEdge(123L, null, Maps.hashMap(), null, null, null);\n        final CompleteEdge edge41 = new CompleteEdge(123L, null, null, null, null,\n                Sets.hashSet(1L, 2L));\n        final CompleteEdge edge42 = new CompleteEdge(123L, null, null, null, null,\n                Sets.hashSet(1L, 2L));\n        final CompleteEdge edge43 = new CompleteEdge(123L, null, null, null, null,\n                Sets.hashSet(1L));\n        final CompleteEdge edge51 = new CompleteEdge(123L, null, null, 1L, null, null);\n        final CompleteEdge edge52 = new CompleteEdge(123L, null, null, 1L, null, null);\n        final CompleteEdge edge53 = new CompleteEdge(123L, null, null, 2L, null, null);\n        final CompleteEdge edge61 = new CompleteEdge(123L, null, null, null, 1L, null);\n        final CompleteEdge edge62 = new CompleteEdge(123L, null, null, null, 1L, null);\n        final CompleteEdge edge63 = new CompleteEdge(123L, null, null, null, 2L, null);\n\n        Assert.assertEquals(edge11, edge12);\n        Assert.assertEquals(edge21, edge22);\n        Assert.assertEquals(edge31, edge32);\n        Assert.assertEquals(edge41, edge42);\n        Assert.assertEquals(edge51, edge52);\n        Assert.assertEquals(edge61, edge62);\n\n        Assert.assertNotEquals(edge11, edge21);\n        Assert.assertNotEquals(edge11, edge31);\n        Assert.assertNotEquals(edge11, edge41);\n        Assert.assertNotEquals(edge11, edge51);\n        Assert.assertNotEquals(edge11, edge61);\n        Assert.assertNotEquals(edge21, edge23);\n        Assert.assertNotEquals(edge31, edge33);\n        Assert.assertNotEquals(edge41, edge43);\n        Assert.assertNotEquals(edge51, edge53);\n        Assert.assertNotEquals(edge61, edge63);\n    }\n\n    @Test\n    public void testEdgeShallowCopyNullBounds()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"bounds were null\");\n\n        final CompleteEdge edge = new CompleteEdge(1L, null, null, null, null, null);\n        CompleteEdge.shallowFrom(edge);\n    }\n\n    @Test\n    public void testFull()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Edge source = atlas.edge(3);\n        final CompleteEdge result = CompleteEdge.from(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        Assert.assertEquals(source.asPolyLine(), result.asPolyLine());\n        Assert.assertEquals(source.start().getIdentifier(), result.start().getIdentifier());\n        Assert.assertEquals(source.end().getIdentifier(), result.end().getIdentifier());\n        Assert.assertEquals(source.getTags(), result.getTags());\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        Assert.assertEquals(result, result.copy());\n    }\n\n    @Test\n    public void testGetGeometry()\n    {\n        final PolyLine polyLine = new PolyLine(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                Location.forString(\"3,3\"));\n        final CompleteEdge edge = new CompleteEdge(123L, polyLine, null, null, null, null);\n        Assert.assertEquals(Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                Location.forString(\"3,3\")), edge.getGeometry());\n    }\n\n    @Test\n    public void testIsCompletelyShallow()\n    {\n        final CompleteEdge superShallow = new CompleteEdge(123L, null, null, null, null, null);\n        Assert.assertTrue(superShallow.isShallow());\n    }\n\n    @Test\n    public void testNonFullEdgeCopy()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"but it was not full\");\n\n        final CompleteEdge edge = new CompleteEdge(1L, null, null, null, null, null);\n        CompleteEdge.from(edge);\n    }\n\n    @Test\n    public void testShallow()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Edge source = atlas.edge(3);\n        final CompleteEdge result = CompleteEdge.shallowFrom(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        result.withPolyLine(PolyLine.TEST_POLYLINE);\n        Assert.assertEquals(PolyLine.TEST_POLYLINE.bounds(), result.bounds());\n        final Map<String, String> tags = Maps.hashMap(\"key\", \"value\");\n        result.withTags(tags);\n        Assert.assertEquals(tags, result.getTags());\n        final long startNodeIdentifier = 5;\n        result.withStartNodeIdentifier(startNodeIdentifier);\n        Assert.assertEquals(startNodeIdentifier, result.start().getIdentifier());\n        final long endNodeIdentifier = 6;\n        result.withEndNodeIdentifier(endNodeIdentifier);\n        Assert.assertEquals(endNodeIdentifier, result.end().getIdentifier());\n        result.withRelationIdentifiers(source.relations().stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet()));\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testToWkt()\n    {\n        final CompleteEdge edge1 = new CompleteEdge(123L);\n        edge1.withPolyLine(new PolyLine(Location.forString(\"0,0\"), Location.forString(\"1,1\")));\n        Assert.assertEquals(\"LINESTRING (0 0, 1 1)\", edge1.toWkt());\n\n        final CompleteEdge edge2 = new CompleteEdge(123L);\n        Assert.assertNull(edge2.toWkt());\n    }\n\n    @Test\n    public void testWithGeometry()\n    {\n        final CompleteEdge edge = new CompleteEdge(1L);\n        edge.withGeometry(Arrays.asList(Location.COLOSSEUM, Location.CENTER));\n        Assert.assertEquals(new PolyLine(Arrays.asList(Location.COLOSSEUM, Location.CENTER)),\n                edge.asPolyLine());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteEntityTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class CompleteEntityTest\n{\n    @Test\n    public void testTruncate()\n    {\n        final String one = \"abc\";\n        final StringBuilder twoBuilder = new StringBuilder();\n        for (int index = 0; index < 2100; index++)\n        {\n            twoBuilder.append(\"a\");\n        }\n        final String two = twoBuilder.toString();\n\n        Assert.assertEquals(one + PrettifyStringFormat.TRUNCATE_ELLIPSES,\n                new CompleteArea(1L, null, null, null).truncate(one));\n        Assert.assertEquals(2100, two.length());\n        Assert.assertEquals(\n                PrettifyStringFormat.TRUNCATE_LENGTH\n                        + PrettifyStringFormat.TRUNCATE_ELLIPSES.length(),\n                new CompleteArea(1L, null, null, null).truncate(two).length());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteItemTypeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * @author Yazad Khambata\n */\npublic class CompleteItemTypeTest\n{\n    @Rule\n    public CompleteItemTypeTestRule rule = new CompleteItemTypeTestRule();\n\n    @Test\n    public void shallowFrom()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final List<CompleteEntity> completeEntities = toCompleteEntities(atlas);\n        validate(completeEntities);\n    }\n\n    private List<CompleteEntity> toCompleteEntities(final Atlas atlas)\n    {\n        return Arrays.stream(ItemType.values())\n                .map(itemType -> itemType.entityForIdentifier(atlas, 1L)).map(atlasEntity ->\n                {\n                    final CompleteEntity completeEntity = CompleteItemType.shallowFrom(atlasEntity);\n                    return completeEntity;\n                }).collect(Collectors.toList());\n    }\n\n    private void validate(final List<CompleteEntity> completeEntities)\n    {\n        Assert.assertNotNull(completeEntities);\n        Assert.assertFalse(completeEntities.isEmpty());\n        Assert.assertTrue(completeEntities.size() == ItemType.values().length);\n        final Set<ItemType> itemTypes = completeEntities.stream()\n                .filter(completeEntity -> completeEntity != null).map(CompleteEntity::getType)\n                .collect(Collectors.toSet());\n        Assert.assertEquals(itemTypes,\n                Arrays.stream(ItemType.values()).collect(Collectors.toSet()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteItemTypeTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author Yazad Khambata\n */\npublic class CompleteItemTypeTestRule extends CoreTestRule\n{\n    public static final String ONE = \"35.3,-128.03\";\n    public static final String TWO = \"37.4,-127.02\";\n\n    @TestAtlas(nodes = { @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n            @Node(id = \"2\", coordinates = @Loc(value = TWO)) },\n\n            edges = { @Edge(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }) },\n\n            areas = { @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }) },\n\n            lines = { @Line(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }) },\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = ONE)) },\n\n            relations = { @Relation(id = \"1\", members = {\n                    @Member(id = \"1\", type = \"node\", role = \"node-role\"),\n                    @Member(id = \"1\", type = \"edge\", role = \"edge-role\") }) })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteLineTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class CompleteLineTest\n{\n    @Rule\n    public CompleteTestRule rule = new CompleteTestRule();\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testBloatedEquals()\n    {\n        final CompleteLine line11 = new CompleteLine(123L, null, null, null);\n        final CompleteLine line12 = new CompleteLine(123L, null, null, null);\n        final CompleteLine line21 = new CompleteLine(123L, PolyLine.TEST_POLYLINE, null, null);\n        final CompleteLine line22 = new CompleteLine(123L, PolyLine.TEST_POLYLINE, null, null);\n        final CompleteLine line23 = new CompleteLine(123L, Polygon.SILICON_VALLEY_2, null, null);\n        final CompleteLine line31 = new CompleteLine(123L, null, Maps.hashMap(\"key\", \"value\"),\n                null);\n        final CompleteLine line32 = new CompleteLine(123L, null, Maps.hashMap(\"key\", \"value\"),\n                null);\n        final CompleteLine line33 = new CompleteLine(123L, null, Maps.hashMap(), null);\n        final CompleteLine line41 = new CompleteLine(123L, null, null, Sets.hashSet(1L, 2L));\n        final CompleteLine line42 = new CompleteLine(123L, null, null, Sets.hashSet(1L, 2L));\n        final CompleteLine line43 = new CompleteLine(123L, null, null, Sets.hashSet(1L));\n\n        Assert.assertEquals(line11, line12);\n        Assert.assertEquals(line21, line22);\n        Assert.assertEquals(line31, line32);\n        Assert.assertEquals(line41, line42);\n\n        Assert.assertNotEquals(line11, line21);\n        Assert.assertNotEquals(line11, line31);\n        Assert.assertNotEquals(line11, line41);\n        Assert.assertNotEquals(line21, line23);\n        Assert.assertNotEquals(line31, line33);\n        Assert.assertNotEquals(line41, line43);\n    }\n\n    @Test\n    public void testFull()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Line source = atlas.line(18);\n        final CompleteLine result = CompleteLine.from(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        Assert.assertEquals(source.asPolyLine(), result.asPolyLine());\n        Assert.assertEquals(source.getTags(), result.getTags());\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        Assert.assertEquals(result, result.copy());\n    }\n\n    @Test\n    public void testGetGeometry()\n    {\n        final PolyLine polyLine = new PolyLine(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                Location.forString(\"3,3\"));\n        final CompleteLine line = new CompleteLine(123L, polyLine, null, null);\n        Assert.assertEquals(Arrays.asList(Location.forString(\"1,1\"), Location.forString(\"2,2\"),\n                Location.forString(\"3,3\")), line.getGeometry());\n    }\n\n    @Test\n    public void testIsCompletelyShallow()\n    {\n        final CompleteLine superShallow = new CompleteLine(123L, null, null, null);\n        Assert.assertTrue(superShallow.isShallow());\n    }\n\n    @Test\n    public void testLineShallowCopyNullBounds()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"bounds were null\");\n\n        final CompleteLine line = new CompleteLine(1L, null, null, null);\n        CompleteLine.shallowFrom(line);\n    }\n\n    @Test\n    public void testNonFullLineCopy()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"but it was not full\");\n\n        final CompleteLine line = new CompleteLine(1L, null, null, null);\n        CompleteLine.from(line);\n    }\n\n    @Test\n    public void testShallow()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Line source = atlas.line(18);\n        final CompleteLine result = CompleteLine.shallowFrom(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        result.withPolyLine(PolyLine.TEST_POLYLINE);\n        Assert.assertEquals(PolyLine.TEST_POLYLINE.bounds(), result.bounds());\n        final Map<String, String> tags = Maps.hashMap(\"key\", \"value\");\n        result.withTags(tags);\n        Assert.assertEquals(tags, result.getTags());\n        result.withRelationIdentifiers(source.relations().stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet()));\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testToWkt()\n    {\n        final CompleteLine line1 = new CompleteLine(123L);\n        line1.withPolyLine(new PolyLine(Location.forString(\"0,0\"), Location.forString(\"1,1\")));\n        Assert.assertEquals(\"LINESTRING (0 0, 1 1)\", line1.toWkt());\n\n        final CompleteLine line2 = new CompleteLine(123L);\n        Assert.assertNull(line2.toWkt());\n    }\n\n    @Test\n    public void testWithGeometry()\n    {\n        final CompleteLine line = new CompleteLine(1L);\n        line.withGeometry(Arrays.asList(Location.COLOSSEUM, Location.CENTER));\n        Assert.assertEquals(new PolyLine(Arrays.asList(Location.COLOSSEUM, Location.CENTER)),\n                line.asPolyLine());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteNodeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class CompleteNodeTest\n{\n    @Rule\n    public CompleteTestRule rule = new CompleteTestRule();\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testBloatedEquals()\n    {\n        final CompleteNode node11 = new CompleteNode(123L, null, null, null, null, null);\n        final CompleteNode node12 = new CompleteNode(123L, null, null, null, null, null);\n        final CompleteNode node21 = new CompleteNode(123L, Location.COLOSSEUM, null, null, null,\n                null);\n        final CompleteNode node22 = new CompleteNode(123L, Location.COLOSSEUM, null, null, null,\n                null);\n        final CompleteNode node23 = new CompleteNode(123L, Location.EIFFEL_TOWER, null, null, null,\n                null);\n        final CompleteNode node31 = new CompleteNode(123L, null, Maps.hashMap(\"key\", \"value\"), null,\n                null, null);\n        final CompleteNode node32 = new CompleteNode(123L, null, Maps.hashMap(\"key\", \"value\"), null,\n                null, null);\n        final CompleteNode node33 = new CompleteNode(123L, null, Maps.hashMap(), null, null, null);\n        final CompleteNode node41 = new CompleteNode(123L, null, null, null, null,\n                Sets.hashSet(1L, 2L));\n        final CompleteNode node42 = new CompleteNode(123L, null, null, null, null,\n                Sets.hashSet(1L, 2L));\n        final CompleteNode node43 = new CompleteNode(123L, null, null, null, null,\n                Sets.hashSet(1L));\n        final CompleteNode node51 = new CompleteNode(123L, null, null, Sets.treeSet(1L, 2L), null,\n                null);\n        final CompleteNode node52 = new CompleteNode(123L, null, null, Sets.treeSet(1L, 2L), null,\n                null);\n        final CompleteNode node53 = new CompleteNode(123L, null, null, Sets.treeSet(1L), null,\n                null);\n        final CompleteNode node61 = new CompleteNode(123L, null, null, null, Sets.treeSet(1L, 2L),\n                null);\n        final CompleteNode node62 = new CompleteNode(123L, null, null, null, Sets.treeSet(1L, 2L),\n                null);\n        final CompleteNode node63 = new CompleteNode(123L, null, null, null, Sets.treeSet(1L),\n                null);\n\n        Assert.assertEquals(node11, node12);\n        Assert.assertEquals(node21, node22);\n        Assert.assertEquals(node31, node32);\n        Assert.assertEquals(node41, node42);\n        Assert.assertEquals(node51, node52);\n        Assert.assertEquals(node61, node62);\n\n        Assert.assertNotEquals(node11, node21);\n        Assert.assertNotEquals(node11, node31);\n        Assert.assertNotEquals(node11, node41);\n        Assert.assertNotEquals(node11, node51);\n        Assert.assertNotEquals(node11, node61);\n        Assert.assertNotEquals(node21, node23);\n        Assert.assertNotEquals(node31, node33);\n        Assert.assertNotEquals(node41, node43);\n        Assert.assertNotEquals(node51, node53);\n        Assert.assertNotEquals(node61, node63);\n    }\n\n    @Test\n    public void testFull()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Node source = atlas.node(1);\n        final CompleteNode result = CompleteNode.from(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        Assert.assertEquals(source.getLocation(), result.getLocation());\n        Assert.assertEquals(\n                source.inEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toCollection(TreeSet::new)),\n                result.inEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toCollection(TreeSet::new)));\n        Assert.assertEquals(\n                source.outEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toCollection(TreeSet::new)),\n                result.outEdges().stream().map(Edge::getIdentifier)\n                        .collect(Collectors.toCollection(TreeSet::new)));\n        Assert.assertEquals(source.getTags(), result.getTags());\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        Assert.assertEquals(result, result.copy());\n    }\n\n    @Test\n    public void testGetGeometry()\n    {\n        final Location location = Location.forString(\"1,1\");\n        final CompleteNode node = new CompleteNode(123L, location, null, null, null, null);\n        Assert.assertEquals(Arrays.asList(Location.forString(\"1,1\")), node.getGeometry());\n    }\n\n    @Test\n    public void testIsCompletelyShallow()\n    {\n        final CompleteNode superShallow = new CompleteNode(123L, null, null, null, null, null);\n        Assert.assertTrue(superShallow.isShallow());\n    }\n\n    @Test\n    public void testNodeShallowCopyNullBounds()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"bounds were null\");\n\n        final CompleteNode node = new CompleteNode(1L, null, null, null, null, null);\n        CompleteNode.shallowFrom(node);\n    }\n\n    @Test\n    public void testNonFullNodeCopy()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"but it was not full\");\n\n        final CompleteNode node = new CompleteNode(1L, null, null, null, null, null);\n        CompleteNode.from(node);\n    }\n\n    @Test\n    public void testShallow()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Node source = atlas.node(1);\n        final CompleteNode result = CompleteNode.shallowFrom(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        result.withLocation(Location.CENTER);\n        // When we update a location, the bounds should update to the bounds of the new location\n        Assert.assertEquals(Rectangle.forLocated(Location.CENTER), result.bounds());\n        final Map<String, String> tags = Maps.hashMap(\"key\", \"value\");\n        result.withTags(tags);\n        Assert.assertEquals(tags, result.getTags());\n        final SortedSet<Long> inEdgeIdentifiers = Sets.treeSet(5L, 6L);\n        result.withInEdgeIdentifiers(inEdgeIdentifiers);\n        Assert.assertEquals(inEdgeIdentifiers, result.inEdges().stream().map(Edge::getIdentifier)\n                .collect(Collectors.toCollection(TreeSet::new)));\n        final SortedSet<Long> outEdgeIdentifiers = Sets.treeSet(7L, 8L);\n        result.withOutEdgeIdentifiers(outEdgeIdentifiers);\n        Assert.assertEquals(outEdgeIdentifiers, result.outEdges().stream().map(Edge::getIdentifier)\n                .collect(Collectors.toCollection(TreeSet::new)));\n        result.withRelationIdentifiers(source.relations().stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet()));\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        result.withLocation(Location.COLOSSEUM);\n        // When we update the location again, the bounds recalculation should \"forget\" about the\n        // first update\n        Assert.assertEquals(Rectangle.forLocated(Location.COLOSSEUM), result.bounds());\n    }\n\n    @Test\n    public void testToWkt()\n    {\n        final CompleteNode node1 = new CompleteNode(123L);\n        node1.withLocation(Location.forString(\"0,0\"));\n        Assert.assertEquals(\"POINT (0 0)\", node1.toWkt());\n\n        final CompleteNode node2 = new CompleteNode(123L);\n        Assert.assertNull(node2.toWkt());\n    }\n\n    @Test\n    public void testWithGeometry()\n    {\n        final CompleteNode node = new CompleteNode(1L);\n        node.withGeometry(Arrays.asList(Location.COLOSSEUM));\n        Assert.assertEquals(Location.COLOSSEUM, node.getLocation());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompletePointTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class CompletePointTest\n{\n    @Rule\n    public CompleteTestRule rule = new CompleteTestRule();\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testBloatedEquals()\n    {\n        final CompletePoint point11 = new CompletePoint(123L, null, null, null);\n        final CompletePoint point12 = new CompletePoint(123L, null, null, null);\n        final CompletePoint point21 = new CompletePoint(123L, Location.COLOSSEUM, null, null);\n        final CompletePoint point22 = new CompletePoint(123L, Location.COLOSSEUM, null, null);\n        final CompletePoint point23 = new CompletePoint(123L, Location.EIFFEL_TOWER, null, null);\n        final CompletePoint point31 = new CompletePoint(123L, null, Maps.hashMap(\"key\", \"value\"),\n                null);\n        final CompletePoint point32 = new CompletePoint(123L, null, Maps.hashMap(\"key\", \"value\"),\n                null);\n        final CompletePoint point33 = new CompletePoint(123L, null, Maps.hashMap(), null);\n        final CompletePoint point41 = new CompletePoint(123L, null, null, Sets.hashSet(1L, 2L));\n        final CompletePoint point42 = new CompletePoint(123L, null, null, Sets.hashSet(1L, 2L));\n        final CompletePoint point43 = new CompletePoint(123L, null, null, Sets.hashSet(1L));\n\n        Assert.assertEquals(point11, point12);\n        Assert.assertEquals(point21, point22);\n        Assert.assertEquals(point31, point32);\n        Assert.assertEquals(point41, point42);\n\n        Assert.assertNotEquals(point11, point21);\n        Assert.assertNotEquals(point11, point31);\n        Assert.assertNotEquals(point11, point41);\n        Assert.assertNotEquals(point21, point23);\n        Assert.assertNotEquals(point31, point33);\n        Assert.assertNotEquals(point41, point43);\n    }\n\n    @Test\n    public void testFull()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Point source = atlas.point(33);\n        final CompletePoint result = CompletePoint.from(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        Assert.assertEquals(source.getLocation(), result.getLocation());\n        Assert.assertEquals(source.getTags(), result.getTags());\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        Assert.assertEquals(result, result.copy());\n    }\n\n    @Test\n    public void testGetGeometry()\n    {\n        final Location location = Location.forString(\"1,1\");\n        final CompletePoint point = new CompletePoint(123L, location, null, null);\n        Assert.assertEquals(Arrays.asList(Location.forString(\"1,1\")), point.getGeometry());\n    }\n\n    @Test\n    public void testIsCompletelyShallow()\n    {\n        final CompletePoint superShallow = new CompletePoint(123L, null, null, null);\n        Assert.assertTrue(superShallow.isShallow());\n    }\n\n    @Test\n    public void testNonFullPointCopy()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"but it was not full\");\n\n        final CompletePoint point = new CompletePoint(1L, null, null, null);\n        CompletePoint.from(point);\n    }\n\n    @Test\n    public void testPointShallowCopyNullBounds()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"bounds were null\");\n\n        final CompletePoint point = new CompletePoint(1L, null, null, null);\n        CompletePoint.shallowFrom(point);\n    }\n\n    @Test\n    public void testShallow()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Point source = atlas.point(33);\n        final CompletePoint result = CompletePoint.shallowFrom(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        result.withLocation(Location.CENTER);\n        // When we update a location, the bounds should update to the bounds of the new location.\n        Assert.assertEquals(Rectangle.forLocated(Location.CENTER), result.bounds());\n\n        final Map<String, String> tags = Maps.hashMap(\"key\", \"value\");\n        result.withTags(tags);\n        Assert.assertEquals(tags, result.getTags());\n        result.withRelationIdentifiers(source.relations().stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet()));\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        result.withLocation(Location.COLOSSEUM);\n        // When we update the location again, the bounds recalculation should \"forget\" about the\n        // first update\n        Assert.assertEquals(Rectangle.forLocated(Location.COLOSSEUM), result.bounds());\n    }\n\n    @Test\n    public void testToWkt()\n    {\n        final CompletePoint point1 = new CompletePoint(123L);\n        point1.withLocation(Location.forString(\"0,0\"));\n        Assert.assertEquals(\"POINT (0 0)\", point1.toWkt());\n\n        final CompletePoint point2 = new CompletePoint(123L);\n        Assert.assertNull(point2.toWkt());\n    }\n\n    @Test\n    public void testWithGeometry()\n    {\n        final CompletePoint point = new CompletePoint(1L);\n        point.withGeometry(Arrays.asList(Location.COLOSSEUM));\n        Assert.assertEquals(Location.COLOSSEUM, point.getLocation());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteRelationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author matthieun\n */\npublic class CompleteRelationTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Rule\n    public CompleteTestRule rule = new CompleteTestRule();\n\n    @Test\n    public void testBloatedEquals()\n    {\n        final RelationBean members1 = new RelationBean();\n        members1.addItem(456L, \"myRole\", ItemType.AREA);\n        final RelationBean members2 = new RelationBean();\n        members2.addItem(789L, \"myRole\", ItemType.AREA);\n\n        final List<Long> allRelationsWithSameOsmIdentifier1 = new ArrayList<>();\n        allRelationsWithSameOsmIdentifier1.add(456L);\n        final List<Long> allRelationsWithSameOsmIdentifier2 = new ArrayList<>();\n        allRelationsWithSameOsmIdentifier2.add(789L);\n\n        final CompleteRelation relation11 = new CompleteRelation(123L, null, null, null, null, null,\n                null, null);\n        final CompleteRelation relation12 = new CompleteRelation(123L, null, null, null, null, null,\n                null, null);\n        final CompleteRelation relation21 = new CompleteRelation(123L, null,\n                Polygon.SILICON_VALLEY.bounds(), null, null, null, null, null);\n        final CompleteRelation relation22 = new CompleteRelation(123L, null,\n                Polygon.SILICON_VALLEY.bounds(), null, null, null, null, null);\n        final CompleteRelation relation23 = new CompleteRelation(123L, null,\n                Polygon.SILICON_VALLEY_2.bounds(), null, null, null, null, null);\n        final CompleteRelation relation31 = new CompleteRelation(123L, Maps.hashMap(\"key\", \"value\"),\n                null, null, null, null, null, null);\n        final CompleteRelation relation32 = new CompleteRelation(123L, Maps.hashMap(\"key\", \"value\"),\n                null, null, null, null, null, null);\n        final CompleteRelation relation33 = new CompleteRelation(123L, Maps.hashMap(), null, null,\n                null, null, null, null);\n        final CompleteRelation relation41 = new CompleteRelation(123L, null, null, null, null, null,\n                null, Sets.hashSet(1L, 2L));\n        final CompleteRelation relation42 = new CompleteRelation(123L, null, null, null, null, null,\n                null, Sets.hashSet(1L, 2L));\n        final CompleteRelation relation43 = new CompleteRelation(123L, null, null, null, null, null,\n                null, Sets.hashSet(1L));\n        final CompleteRelation relation51 = new CompleteRelation(123L, null, null, members1, null,\n                null, null, null);\n        final CompleteRelation relation52 = new CompleteRelation(123L, null, null, members1, null,\n                null, null, null);\n        final CompleteRelation relation53 = new CompleteRelation(123L, null, null, members2, null,\n                null, null, null);\n        final CompleteRelation relation61 = new CompleteRelation(123L, null, null, null,\n                allRelationsWithSameOsmIdentifier1, null, null, null);\n        final CompleteRelation relation62 = new CompleteRelation(123L, null, null, null,\n                allRelationsWithSameOsmIdentifier1, null, null, null);\n        final CompleteRelation relation63 = new CompleteRelation(123L, null, null, null,\n                allRelationsWithSameOsmIdentifier2, null, null, null);\n        final CompleteRelation relation71 = new CompleteRelation(123L, null, null, null, null,\n                members1, null, null);\n        final CompleteRelation relation72 = new CompleteRelation(123L, null, null, null, null,\n                members1, null, null);\n        final CompleteRelation relation73 = new CompleteRelation(123L, null, null, null, null,\n                members2, null, null);\n        final CompleteRelation relation81 = new CompleteRelation(123L, null, null, null, null, null,\n                456L, null);\n        final CompleteRelation relation82 = new CompleteRelation(123L, null, null, null, null, null,\n                456L, null);\n        final CompleteRelation relation83 = new CompleteRelation(123L, null, null, null, null, null,\n                789L, null);\n\n        Assert.assertEquals(relation11, relation12);\n        Assert.assertEquals(relation21, relation22);\n        Assert.assertEquals(relation31, relation32);\n        Assert.assertEquals(relation41, relation42);\n        Assert.assertEquals(relation51, relation52);\n        Assert.assertEquals(relation61, relation62);\n        Assert.assertEquals(relation71, relation72);\n        Assert.assertEquals(relation81, relation82);\n\n        // Here bounds are considered a derivation of the rest, and thus do not trigger an un-equal.\n        Assert.assertEquals(relation11, relation21);\n        Assert.assertNotEquals(relation11, relation31);\n        Assert.assertNotEquals(relation11, relation41);\n        // Here bounds are considered a derivation of the rest, and thus do not trigger an un-equal.\n        Assert.assertEquals(relation21, relation23);\n        Assert.assertNotEquals(relation31, relation33);\n        Assert.assertNotEquals(relation41, relation43);\n        Assert.assertNotEquals(relation51, relation53);\n        Assert.assertNotEquals(relation61, relation63);\n        Assert.assertNotEquals(relation71, relation73);\n        Assert.assertNotEquals(relation81, relation83);\n    }\n\n    @Test\n    public void testEdgeShallowCopyNullBounds()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"bounds were null\");\n\n        final CompleteRelation relation = new CompleteRelation(1L, null, null, null, null, null,\n                null, null);\n        CompleteRelation.shallowFrom(relation);\n    }\n\n    @Test\n    public void testFailWithMembersAndSource()\n    {\n        final CompleteRelation relation = new CompleteRelation(1L, null, null, null, null, null,\n                null, null);\n        final RelationBean bean = new RelationBean();\n        bean.addItem(new RelationBeanItem(1L, \"role\", ItemType.AREA));\n\n        final RelationMember member = new RelationMember(\"role\",\n                new CompletePoint(1L, Location.CENTER, null, null), 1L);\n        final RelationMemberList list1 = new RelationMemberList(Arrays.asList(member));\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"This version of withMembersAndSource must use a source Relation that is tied to an atlas\");\n        relation.withMembersAndSource(list1, relation);\n    }\n\n    @Test\n    public void testFull()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Relation source = atlas.relation(22);\n        final CompleteRelation result = CompleteRelation.from(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        Assert.assertEquals(source.members().asBean(), result.members().asBean());\n        Assert.assertEquals(source.allKnownOsmMembers().asBean(),\n                result.allKnownOsmMembers().asBean());\n        Assert.assertEquals(source.osmRelationIdentifier(), result.osmRelationIdentifier());\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n\n        Assert.assertEquals(result, result.copy());\n    }\n\n    @Test\n    public void testIsCompletelyShallow()\n    {\n        final CompleteRelation superShallow = new CompleteRelation(123L, null, null, null, null,\n                null, null, null);\n        Assert.assertTrue(superShallow.isShallow());\n    }\n\n    @Test\n    public void testMemberIsExplicitlyRemovedWhenRoleSwapped()\n    {\n        final String newRole = \"newRole\";\n        final String originalRole = \"originalRole\";\n        final RelationBean members = new RelationBean();\n        final RelationBeanItem originalRelationBeanItem = new RelationBeanItem(1L, originalRole,\n                ItemType.AREA);\n        members.add(originalRelationBeanItem);\n        final CompleteRelation completeRelation = new CompleteRelation(123L, null, null, members,\n                null, null, null, null);\n        completeRelation.changeMemberRole(new CompleteArea(1L, null, null, null), newRole);\n        Assert.assertEquals(1, completeRelation.members().size());\n        Assert.assertEquals(newRole, completeRelation.members().get(0).getRole());\n        Assert.assertTrue(completeRelation.members().asBean().getExplicitlyExcluded()\n                .contains(originalRelationBeanItem));\n    }\n\n    @Test\n    public void testNonFullRelationCopy()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"but it was not full\");\n\n        final CompleteRelation relation = new CompleteRelation(1L, null, null, null, null, null,\n                null, null);\n        CompleteRelation.from(relation);\n    }\n\n    @Test\n    public void testShallow()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Relation source = atlas.relation(22);\n        final CompleteRelation result = CompleteRelation.shallowFrom(source);\n        Assert.assertEquals(source.getIdentifier(), result.getIdentifier());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        result.withMembersAndSource(new RelationMemberList(source.members()), source);\n        Assert.assertEquals(source.members().asBean(), result.members().asBean());\n        result.withMembersAndSource(source.members().asBean(), source, source.bounds());\n        Assert.assertEquals(source.bounds(), result.bounds());\n        Assert.assertEquals(source.members().asBean(), result.members().asBean());\n        result.withAllKnownOsmMembers(source.allKnownOsmMembers().asBean());\n        Assert.assertEquals(source.allKnownOsmMembers().asBean(),\n                result.allKnownOsmMembers().asBean());\n        result.withOsmRelationIdentifier(source.osmRelationIdentifier());\n        Assert.assertEquals(source.osmRelationIdentifier(), result.osmRelationIdentifier());\n        result.withRelationIdentifiers(source.relations().stream().map(Relation::getIdentifier)\n                .collect(Collectors.toSet()));\n        Assert.assertEquals(\n                source.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()),\n                result.relations().stream().map(Relation::getIdentifier)\n                        .collect(Collectors.toSet()));\n    }\n\n    @Test\n    public void testToWkt()\n    {\n        final CompleteRelation relation1 = new CompleteRelation(123L);\n        relation1.withBounds(\n                Rectangle.forCorners(Location.forString(\"0,0\"), Location.forString(\"1,1\")));\n        Assert.assertEquals(\"POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))\", relation1.toWkt());\n\n        final CompleteRelation relation2 = new CompleteRelation(123L);\n        Assert.assertNull(relation2.toWkt());\n    }\n\n    @Test\n    public void testWithExtraMember()\n    {\n        final Atlas atlas = this.rule.getAtlas2();\n\n        final CompleteRelation cRelation = CompleteRelation.from(atlas.relation(1L));\n        Assert.assertEquals(atlas.relation(1L).bounds(), cRelation.bounds());\n        cRelation.withAddedMember(atlas.point(5L), \"a\");\n        Assert.assertEquals(Rectangle.forLocated(atlas.relation(1L).bounds(), atlas.point(5L)),\n                cRelation.bounds());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/CompleteTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class CompleteTestRule extends CoreTestRule\n{\n    public static final String POINT_1_LOCATION = \"37.331417,-122.0304871\";\n    public static final String POINT_2_LOCATION = \"37.333364,-122.0200268\";\n\n    private static final String ONE = \"15.420563,-61.336198\";\n    private static final String TWO = \"15.429499,-61.332850\";\n    private static final String THREE = \"15.4855,-61.3041\";\n    private static final String FOUR = \"15.4809,-61.3366\";\n    private static final String FIVE = \"15.4852,-61.3816\";\n    private static final String SIX = \"15.4781,-61.3949\";\n    private static final String SEVEN = \"15.4145,-61.3826\";\n    private static final String EIGHT = \"15.4073,-61.3749\";\n    private static final String NINE = \"15.4075,-61.3746\";\n    private static final String TEN = \"15.4081,-61.3741\";\n    private static final String ELEVEN = \"15.4111,-62.3741\";\n\n    @TestAtlas(\n\n            nodes = { @Node(id = \"1\", coordinates = @Loc(value = POINT_1_LOCATION)),\n                    @Node(id = \"2\", coordinates = @Loc(value = POINT_2_LOCATION)) },\n\n            edges = { @Edge(id = \"3\", coordinates = { @Loc(value = POINT_1_LOCATION),\n                    @Loc(value = POINT_2_LOCATION) }) },\n\n            areas = { @Area(id = \"27\", coordinates = { @Loc(value = POINT_1_LOCATION),\n                    @Loc(value = POINT_2_LOCATION) }) },\n\n            lines = { @Line(id = \"18\", coordinates = { @Loc(value = POINT_1_LOCATION),\n                    @Loc(value = POINT_2_LOCATION) }) },\n\n            points = { @Point(id = \"33\", coordinates = @Loc(value = POINT_1_LOCATION)) },\n\n            relations = { @Relation(id = \"22\", members = {\n                    @Member(id = \"1\", type = \"node\", role = \"node role\"),\n                    @Member(id = \"3\", type = \"edge\", role = \"edge role\") }) }\n\n    )\n    private Atlas atlas;\n\n    @TestAtlas(\n\n            points = {\n\n                    @Point(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Point(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Point(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Point(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Point(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Point(id = \"6\", coordinates = @Loc(value = SIX)),\n                    @Point(id = \"7\", coordinates = @Loc(value = SEVEN))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"1\", role = \"a\", type = \"point\"),\n                            @Member(id = \"2\", role = \"a\", type = \"point\"),\n                            @Member(id = \"3\", role = \"a\", type = \"point\")\n\n                    })\n\n            }\n\n    )\n    private Atlas atlas2;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    public Atlas getAtlas2()\n    {\n        return this.atlas2;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/complete/EmptyAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.complete;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\n\n/**\n * @author matthieun\n */\npublic class EmptyAtlasTest\n{\n    @Test\n    public void testAssignment()\n    {\n        Assert.assertTrue(\n                new CompleteNode(123).equals(new EmptyAtlas().entity(123, ItemType.NODE)));\n        Assert.assertTrue(\n                new CompleteEdge(123).equals(new EmptyAtlas().entity(123, ItemType.EDGE)));\n        Assert.assertTrue(\n                new CompleteArea(123).equals(new EmptyAtlas().entity(123, ItemType.AREA)));\n        Assert.assertTrue(\n                new CompleteLine(123).equals(new EmptyAtlas().entity(123, ItemType.LINE)));\n        Assert.assertTrue(\n                new CompletePoint(123).equals(new EmptyAtlas().entity(123, ItemType.POINT)));\n        Assert.assertTrue(\n                new CompleteRelation(123).equals(new EmptyAtlas().entity(123, ItemType.RELATION)));\n    }\n\n    @SuppressWarnings(\"unlikely-arg-type\")\n    @Test\n    public void testMemberEquality()\n    {\n        final CompletePoint point1 = new CompletePoint(123);\n        final CompletePoint point11 = new CompletePoint(123);\n        final CompletePoint point2 = new CompletePoint(124);\n        final CompleteArea area = new CompleteArea(777);\n\n        Assert.assertTrue(point1.equals(point11));\n        Assert.assertFalse(point1.equals(point2));\n        Assert.assertFalse(point1.equals(area));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaAreaTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaAreaTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaAreaTest.class);\n\n    @Test\n    public void testDifferentGeometry()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n\n        baseBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        alterBuilder.addArea(1,\n                new Polygon(new MultiIterable<>(Polygon.SILICON_VALLEY, Location.TEST_6)), tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(1, diffs.size());\n        logger.debug(\"testDifferentGeometry(): {}\", Diff.toString(diffs));\n    }\n\n    @Test\n    public void testSame()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n\n        baseBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        alterBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(0, diffs.size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaEdgeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.delta.Diff.DiffType;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaEdgeTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaEdgeTest.class);\n\n    @Test\n    public void testAdded()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        baseBuilder.addNode(2, Location.TEST_2, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(2, Location.TEST_2, tags);\n        alterBuilder.addEdge(0, new Segment(Location.TEST_1, Location.TEST_2), tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        logger.debug(\"testAdded(): {}\", Diff.toString(diffs));\n        logger.debug(\"testAdded(): {}\", Diff.toDiffViewFriendlyString(diffs));\n        Assert.assertEquals(3, diffs.size());\n\n        boolean foundEdge = false;\n        for (final Diff diff : diffs)\n        {\n            if (diff.getItemType() == ItemType.EDGE)\n            {\n                Assert.assertEquals(DiffType.ADDED, diff.getDiffType());\n                foundEdge = true;\n            }\n        }\n        if (!foundEdge)\n        {\n            Assert.fail(\"Did not find a diff on an Edge.\");\n        }\n    }\n\n    @Test\n    public void testDifferentEndNode()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        baseBuilder.addNode(2, Location.TEST_2, tags);\n        baseBuilder.addNode(3, Location.TEST_7, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(2, Location.TEST_7, tags);\n        alterBuilder.addNode(3, Location.TEST_2, tags);\n        baseBuilder.addEdge(0, new Segment(Location.TEST_1, Location.TEST_2), tags);\n        // The alter edge has the same polyLine, but the end node id is different\n        alterBuilder.addEdge(0, new Segment(Location.TEST_1, Location.TEST_2), tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(3, diffs.size());\n\n        logger.debug(\"testDifferentEndNode(): {}\", Diff.toString(diffs));\n        boolean foundEdge = false;\n        for (final Diff diff : diffs)\n        {\n            if (diff.getItemType() == ItemType.EDGE)\n            {\n                Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n                foundEdge = true;\n            }\n        }\n        if (!foundEdge)\n        {\n            Assert.fail(\"Did not find a diff on an Edge.\");\n        }\n    }\n\n    @Test\n    public void testDifferentGeometry()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        baseBuilder.addNode(2, Location.TEST_2, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(2, Location.TEST_2, tags);\n        baseBuilder.addEdge(0, new Segment(Location.TEST_1, Location.TEST_2), tags);\n        alterBuilder.addEdge(0, new PolyLine(Location.TEST_1, Location.TEST_7, Location.TEST_2),\n                tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(1, diffs.size());\n\n        final Diff diff = diffs.first();\n        logger.debug(\"testDifferentGeometry(): {}\", Diff.toString(diffs));\n        Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n    }\n\n    @Test\n    public void testDifferentStartNode()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        baseBuilder.addNode(2, Location.TEST_2, tags);\n        baseBuilder.addNode(3, Location.TEST_7, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(2, Location.TEST_7, tags);\n        alterBuilder.addNode(3, Location.TEST_2, tags);\n        baseBuilder.addEdge(0, new Segment(Location.TEST_2, Location.TEST_1), tags);\n        // The alter edge has the same polyLine, but the end node id is different\n        alterBuilder.addEdge(0, new Segment(Location.TEST_2, Location.TEST_1), tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(3, diffs.size());\n\n        logger.debug(\"testDifferentStartNode(): {}\", Diff.toString(diffs));\n        boolean foundEdge = false;\n        for (final Diff diff : diffs)\n        {\n            if (diff.getItemType() == ItemType.EDGE)\n            {\n                Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n                foundEdge = true;\n            }\n        }\n        if (!foundEdge)\n        {\n            Assert.fail(\"Did not find a diff on an Edge.\");\n        }\n    }\n\n    @Test\n    public void testRemoved()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        baseBuilder.addNode(2, Location.TEST_2, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(2, Location.TEST_2, tags);\n        baseBuilder.addEdge(0, new Segment(Location.TEST_1, Location.TEST_2), tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(3, diffs.size());\n\n        logger.debug(\"testRemoved(): {}\", Diff.toString(diffs));\n        boolean foundEdge = false;\n        for (final Diff diff : diffs)\n        {\n            if (diff.getItemType() == ItemType.EDGE)\n            {\n                Assert.assertEquals(DiffType.REMOVED, diff.getDiffType());\n                foundEdge = true;\n            }\n        }\n        if (!foundEdge)\n        {\n            Assert.fail(\"Did not find a diff on an Edge.\");\n        }\n    }\n\n    @Test\n    public void testWaySectionNoMatch()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder().withName(\"base\");\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder().withName(\"alter\");\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        baseBuilder.addNode(3, Location.TEST_7, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(2, Location.TEST_2, tags);\n        alterBuilder.addNode(3, Location.TEST_7, tags);\n        alterBuilder.addNode(4, Location.TEST_5, tags);\n        baseBuilder.addEdge(1000000,\n                new PolyLine(Location.TEST_1, Location.TEST_2, Location.TEST_7), tags);\n        baseBuilder.addEdge(-1000000,\n                new PolyLine(Location.TEST_7, Location.TEST_2, Location.TEST_1), tags);\n        // The alter edge has the same polyLine, but the end node id is different\n        alterBuilder.addEdge(1000001, new Segment(Location.TEST_1, Location.TEST_2), tags);\n        alterBuilder.addEdge(-1000001, new Segment(Location.TEST_2, Location.TEST_1), tags);\n        alterBuilder.addEdge(1000002, new Segment(Location.TEST_2, Location.TEST_7), tags);\n        alterBuilder.addEdge(-1000002, new Segment(Location.TEST_7, Location.TEST_2), tags);\n        alterBuilder.addEdge(2000000, new Segment(Location.TEST_2, Location.TEST_5), tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter, true).generate().getDifferences();\n        logger.debug(\"testWaySectionNoMatch(): {}\", Diff.toString(diffs));\n\n        Assert.assertEquals(3, diffs.size());\n\n        boolean foundEdge = false;\n        for (final Diff diff : diffs)\n        {\n            if (diff.getItemType() == ItemType.EDGE)\n            {\n                Assert.assertEquals(DiffType.ADDED, diff.getDiffType());\n                foundEdge = true;\n            }\n            if (diff.getItemType() == ItemType.NODE)\n            {\n                Assert.assertEquals(DiffType.ADDED, diff.getDiffType());\n                final long nodeIdentifier = diff.getAfterEntity().getIdentifier();\n                if (nodeIdentifier != 2 && nodeIdentifier != 4)\n                {\n                    Assert.fail(\"Found an unexpected node added: \" + nodeIdentifier);\n                }\n            }\n        }\n        if (!foundEdge)\n        {\n            Assert.fail(\"Did not find a diff on an Edge.\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaLineTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.collections.MultiIterable;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaLineTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaLineTest.class);\n\n    @Test\n    public void testDifferentGeometry()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n\n        baseBuilder.addLine(1, PolyLine.TEST_POLYLINE, tags);\n        alterBuilder.addLine(1,\n                new PolyLine(new MultiIterable<>(PolyLine.TEST_POLYLINE, Location.TEST_6)), tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(1, diffs.size());\n        logger.debug(\"testDifferentGeometry(): {}\", Diff.toString(diffs));\n    }\n\n    @Test\n    public void testSame()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n\n        baseBuilder.addLine(1, PolyLine.TEST_POLYLINE, tags);\n        alterBuilder.addLine(1, PolyLine.TEST_POLYLINE, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(0, diffs.size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaNodeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.delta.Diff.DiffType;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaNodeTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaNodeTest.class);\n\n    @Test\n    public void testAdded()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(2, Location.TEST_2, tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(1, diffs.size());\n\n        final Diff diff = diffs.first();\n        logger.debug(\"testAdded(): {}\", Diff.toString(diffs));\n        Assert.assertEquals(DiffType.ADDED, diff.getDiffType());\n        Assert.assertEquals(2, diff.getIdentifier());\n    }\n\n    @Test\n    public void testDifferentGeometry()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(1, Location.TEST_6, tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(1, diffs.size());\n\n        final Diff diff = diffs.first();\n        logger.debug(\"testDifferentGeometry(): {}\", Diff.toString(diffs));\n        Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n    }\n\n    @Test\n    public void testDifferentInOutEdges()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        baseBuilder.addNode(2, Location.TEST_6, tags);\n        alterBuilder.addNode(2, Location.TEST_6, tags);\n        baseBuilder.addNode(3, Location.TEST_2, tags);\n        alterBuilder.addNode(3, Location.TEST_2, tags);\n\n        baseBuilder.addEdge(1, new Segment(Location.TEST_6, Location.TEST_1), tags);\n        alterBuilder.addEdge(1, new Segment(Location.TEST_6, Location.TEST_1), tags);\n        baseBuilder.addEdge(2, new Segment(Location.TEST_2, Location.TEST_1), tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(3, diffs.size());\n\n        logger.debug(\"testDifferentInOutEdges(): {}\", Diff.toString(diffs));\n\n        for (final Diff diff : diffs)\n        {\n            if (diff.getIdentifier() == 2)\n            {\n                Assert.assertEquals(DiffType.REMOVED, diff.getDiffType());\n                Assert.assertEquals(ItemType.EDGE, diff.getItemType());\n            }\n            else\n            {\n                Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n                Assert.assertEquals(ItemType.NODE, diff.getItemType());\n            }\n        }\n    }\n\n    @Test\n    public void testRemoved()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addNode(1, Location.TEST_1, tags);\n        alterBuilder.addNode(1, Location.TEST_1, tags);\n        baseBuilder.addNode(2, Location.TEST_2, tags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(1, diffs.size());\n\n        final Diff diff = diffs.first();\n        logger.debug(\"testRemoved(): {}\", Diff.toString(diffs));\n        Assert.assertEquals(DiffType.REMOVED, diff.getDiffType());\n        Assert.assertEquals(2, diff.getIdentifier());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaPointTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaPointTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaPointTest.class);\n\n    @Test\n    public void testDifferentGeometry()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n\n        baseBuilder.addPoint(1, Location.TEST_6, tags);\n        alterBuilder.addPoint(1, Location.TEST_1, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(1, diffs.size());\n        logger.debug(\"testDifferentGeometry(): {}\", Diff.toString(diffs));\n    }\n\n    @Test\n    public void testSame()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n\n        baseBuilder.addPoint(1, Location.TEST_6, tags);\n        alterBuilder.addPoint(1, Location.TEST_6, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(0, diffs.size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaRelationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.delta.Diff.DiffType;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaRelationTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaRelationTest.class);\n\n    @Test\n    public void testDifferentMemberOrder()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        alterBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        baseBuilder.addPoint(9, Location.TEST_2, tags);\n        alterBuilder.addPoint(9, Location.TEST_2, tags);\n\n        final RelationBean baseRelationBean = new RelationBean();\n        baseRelationBean.addItem(1L, \"inner\", ItemType.AREA);\n        baseRelationBean.addItem(9L, \"outer\", ItemType.POINT);\n\n        final RelationBean alterRelationBean = new RelationBean();\n        // Different order\n        alterRelationBean.addItem(9L, \"outer\", ItemType.POINT);\n        alterRelationBean.addItem(1L, \"inner\", ItemType.AREA);\n\n        baseBuilder.addRelation(5, 5, baseRelationBean, tags);\n        alterBuilder.addRelation(5, 5, alterRelationBean, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        // The relations members are now ordered by member identifier. So this should always return\n        // 0 differences.\n        Assert.assertEquals(0, diffs.size());\n        logger.debug(\"testDifferentMemberOrder(): {}\", Diff.toString(diffs));\n\n        boolean foundRelation = false;\n        for (final Diff diff : diffs)\n        {\n            Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n            if (diff.getIdentifier() == 5)\n            {\n                foundRelation = true;\n            }\n        }\n        if (/* ! */foundRelation)\n        {\n            Assert.fail(\"Did not find the changed relation\");\n        }\n    }\n\n    @Test\n    public void testDifferentMembersRoles()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        alterBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        baseBuilder.addPoint(9, Location.TEST_2, tags);\n        alterBuilder.addPoint(9, Location.TEST_2, tags);\n\n        final RelationBean baseRelationBean = new RelationBean();\n        baseRelationBean.addItem(1L, \"inner\", ItemType.AREA);\n        baseRelationBean.addItem(9L, \"outer\", ItemType.POINT);\n\n        final RelationBean alterRelationBean = new RelationBean();\n        alterRelationBean.addItem(1L, \"inner\", ItemType.AREA);\n        // Different role\n        alterRelationBean.addItem(9L, \"other outer\", ItemType.POINT);\n\n        baseBuilder.addRelation(5, 5, baseRelationBean, tags);\n        alterBuilder.addRelation(5, 5, alterRelationBean, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(2, diffs.size());\n        logger.debug(\"testDifferentMembersRoles(): {}\", Diff.toString(diffs));\n        logger.debug(\"testDifferentMembersRoles(): {}\", Diff.toDiffViewFriendlyString(diffs));\n\n        boolean foundRelation = false;\n        for (final Diff diff : diffs)\n        {\n            if (diff.getIdentifier() == 5)\n            {\n                Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n                foundRelation = true;\n            }\n        }\n        if (!foundRelation)\n        {\n            Assert.fail(\"Did not find the changed relation\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaRelationsTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.delta.Diff.DiffType;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaRelationsTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaRelationsTest.class);\n\n    @Test\n    public void testDifferentRelations()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        alterBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n\n        final RelationBean baseRelationBean = new RelationBean();\n        baseRelationBean.addItem(1L, \"inner\", ItemType.AREA);\n\n        final RelationBean alterRelationBean = new RelationBean();\n        alterRelationBean.addItem(1L, \"outer\", ItemType.AREA);\n\n        baseBuilder.addRelation(5, 5, baseRelationBean, tags);\n        alterBuilder.addRelation(5, 5, alterRelationBean, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(2, diffs.size());\n        logger.debug(\"testDifferentRelations(): {}\", Diff.toString(diffs));\n        logger.debug(\"testDifferentRelationsHumanFriendly(): {}\",\n                Diff.toDiffViewFriendlyString(diffs));\n\n        boolean foundRelation = false;\n        for (final Diff diff : diffs)\n        {\n            Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n            if (diff.getIdentifier() == 5)\n            {\n                foundRelation = true;\n            }\n        }\n        if (!foundRelation)\n        {\n            Assert.fail(\"Did not find the changed relation\");\n        }\n    }\n\n    @Test\n    public void testReportedParentRelations()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        alterBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        baseBuilder.addNode(2, Location.COLOSSEUM, tags);\n        alterBuilder.addNode(2, Location.COLOSSEUM, tags);\n        baseBuilder.addNode(3, Location.EIFFEL_TOWER, tags);\n        alterBuilder.addNode(3, Location.EIFFEL_TOWER, tags);\n        baseBuilder.addEdge(4, new PolyLine(Location.COLOSSEUM, Location.EIFFEL_TOWER), tags);\n        alterBuilder.addEdge(4, new PolyLine(Location.COLOSSEUM, Location.EIFFEL_TOWER), tags);\n\n        final RelationBean baseRelationBean = new RelationBean();\n        baseRelationBean.addItem(1L, \"inner\", ItemType.AREA);\n        baseRelationBean.addItem(2L, \"node1\", ItemType.NODE);\n        baseRelationBean.addItem(3L, \"node2\", ItemType.NODE);\n        baseRelationBean.addItem(4L, \"someEdge\", ItemType.EDGE);\n\n        baseBuilder.addRelation(5, 5, baseRelationBean, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        logger.debug(\"testDifferentRelationsHumanFriendly(): {}\",\n                Diff.toDiffViewFriendlyString(diffs));\n\n        // Diff size should be 5:\n        // 1. The Area with ID 1 reports different parent relations set across atlases\n        // 2. The Node with ID 2 reports different parent relations set across atlases\n        // 4. The Node with ID 3 reports different parent relations set across atlases\n        // 5. The Edge with ID 4 reports different parent relations set across atlases\n        // 5. The Relation with ID 5 is not present in the alter atlas\n        Assert.assertEquals(5, diffs.size());\n    }\n\n    @Test\n    public void testSameRelations()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        baseBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n        alterBuilder.addArea(1, Polygon.SILICON_VALLEY, tags);\n\n        final RelationBean baseRelationBean = new RelationBean();\n        baseRelationBean.addItem(1L, \"inner\", ItemType.AREA);\n\n        final RelationBean alterRelationBean = new RelationBean();\n        alterRelationBean.addItem(1L, \"inner\", ItemType.AREA);\n\n        baseBuilder.addRelation(5, 5, baseRelationBean, tags);\n        alterBuilder.addRelation(5, 5, alterRelationBean, tags);\n\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n\n        Assert.assertEquals(0, diffs.size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/delta/AtlasDeltaTagTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.delta;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.delta.Diff.DiffType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class AtlasDeltaTagTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AtlasDeltaTagTest.class);\n\n    @Test\n    public void testDifferentTags()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        final Map<String, String> baseTags = new HashMap<>(tags);\n        baseTags.put(\"key\", \"value\");\n        final Map<String, String> alterTags = new HashMap<>(tags);\n        alterTags.put(\"key\", \"values\");\n        baseBuilder.addLine(1, PolyLine.TEST_POLYLINE, baseTags);\n        alterBuilder.addLine(1, PolyLine.TEST_POLYLINE, alterTags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(1, diffs.size());\n\n        final Diff diff = diffs.first();\n        logger.debug(\"testDifferentTags(): {}\", Diff.toString(diffs));\n        Assert.assertEquals(DiffType.CHANGED, diff.getDiffType());\n    }\n\n    @Test\n    public void testSameTags()\n    {\n        final PackedAtlasBuilder baseBuilder = new PackedAtlasBuilder();\n        final PackedAtlasBuilder alterBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = RandomTagsSupplier.randomTags(5);\n        final Map<String, String> baseTags = new HashMap<>(tags);\n        baseTags.put(\"key\", \"value\");\n        final Map<String, String> alterTags = new HashMap<>(tags);\n        alterTags.put(\"key\", \"value\");\n        baseBuilder.addLine(1, PolyLine.TEST_POLYLINE, baseTags);\n        alterBuilder.addLine(1, PolyLine.TEST_POLYLINE, alterTags);\n        final Atlas base = baseBuilder.get();\n        final Atlas alter = alterBuilder.get();\n\n        final SortedSet<Diff> diffs = new AtlasDelta(base, alter).generate().getDifferences();\n        Assert.assertEquals(0, diffs.size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasAggressiveRelationsTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasAggressiveRelationsTestRule;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasTestRule;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasAggressiveRelationsTest\n{\n    @Rule\n    public DynamicAtlasTestRule rule = new DynamicAtlasTestRule();\n\n    @Rule\n    public DynamicAtlasAggressiveRelationsTestRule rule2 = new DynamicAtlasAggressiveRelationsTestRule();\n\n    private Map<Shard, Atlas> store;\n    private final Supplier<DynamicAtlasPolicy> policySupplier = () -> new DynamicAtlasPolicy(\n            shard ->\n            {\n                if (this.store.containsKey(shard))\n                {\n                    return Optional.of(this.store.get(shard));\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }, new SlippyTileSharding(12), new SlippyTile(1350, 1870, 12), Rectangle.MAXIMUM);\n\n    private final Supplier<DynamicAtlasPolicy> policySupplier2 = () -> new DynamicAtlasPolicy(\n            shard ->\n            {\n                if (this.store.containsKey(shard))\n                {\n                    return Optional.of(this.store.get(shard));\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }, new SlippyTileSharding(11), new SlippyTile(998, 708, 11), Rectangle.MAXIMUM);\n\n    @Before\n    public void prepare()\n    {\n        this.store = new HashMap<>();\n        this.store.put(new SlippyTile(1350, 1870, 12), this.rule.getAtlasz12x1350y1870());\n        this.store.put(new SlippyTile(1350, 1869, 12), this.rule.getAtlasz12x1350y1869());\n        this.store.put(new SlippyTile(1349, 1869, 12), this.rule.getAtlasz12x1349y1869());\n        this.store.put(new SlippyTile(1349, 1870, 12), this.rule.getAtlasz12x1349y1870());\n        this.store.put(new SlippyTile(998, 708, 11), this.rule2.getAtlasZ11X998Y708());\n        this.store.put(new SlippyTile(999, 708, 11), this.rule2.getAtlasZ11X999Y708());\n    }\n\n    @Test\n    public void testRelationsAggressively()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(\n                this.policySupplier.get().withAggressivelyExploreRelations(true)\n                        .withExtendIndefinitely(false).withDeferredLoading(true));\n\n        // Prompts load of 12-1350-1869, 12-1349-1870 and 12-1349-1869\n        dynamicAtlas.preemptiveLoad();\n        Assert.assertNotNull(dynamicAtlas.relation(1));\n        Assert.assertNotNull(dynamicAtlas.relation(2));\n        Assert.assertNotNull(dynamicAtlas.relation(3));\n        Assert.assertEquals(9, dynamicAtlas.numberOfEdges());\n    }\n\n    @Test\n    public void testRelationsAggressivelyWithExpantionIndefinite()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(this.policySupplier2.get()\n                .withAggressivelyExploreRelations(true).withExtendIndefinitely(true)\n                .withDeferredLoading(true)\n                .withAtlasEntitiesToConsiderForExpansion(entity -> entity instanceof Relation));\n\n        // Prompts load of 11-999-708\n        dynamicAtlas.preemptiveLoad();\n        final Relation relation = dynamicAtlas.relation(99756000000L);\n        Assert.assertNotNull(relation);\n        Assert.assertEquals(2, relation.members().size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasFilteredEntitiesTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasTestRule;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasFilteredEntitiesTest\n{\n    @Rule\n    public DynamicAtlasTestRule rule = new DynamicAtlasTestRule();\n\n    private Map<Shard, Atlas> store;\n    private final Supplier<DynamicAtlasPolicy> policySupplier = () -> new DynamicAtlasPolicy(\n            shard ->\n            {\n                if (this.store.containsKey(shard))\n                {\n                    return Optional.of(this.store.get(shard));\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }, new SlippyTileSharding(12), new SlippyTile(1350, 1870, 12), Rectangle.MAXIMUM);\n\n    @Before\n    public void prepare()\n    {\n        this.store = new HashMap<>();\n        this.store.put(new SlippyTile(1350, 1870, 12), this.rule.getAtlasz12x1350y1870());\n        this.store.put(new SlippyTile(1350, 1869, 12), this.rule.getAtlasz12x1350y1869());\n        this.store.put(new SlippyTile(1349, 1869, 12), this.rule.getAtlasz12x1349y1869());\n        this.store.put(new SlippyTile(1349, 1870, 12), this.rule.getAtlasz12x1349y1870());\n    }\n\n    @Test\n    public void testLoadEdgesOnlyByTag()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(this.policySupplier.get()\n                .withAtlasEntitiesToConsiderForExpansion(entity -> Validators.isOfType(entity,\n                        HighwayTag.class, HighwayTag.SECONDARY)));\n        runLoadEdgesOnlyTest(dynamicAtlas);\n    }\n\n    @Test\n    public void testLoadEdgesOnlyByType()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(this.policySupplier.get()\n                .withAtlasEntitiesToConsiderForExpansion(entity -> entity instanceof Edge));\n        runLoadEdgesOnlyTest(dynamicAtlas);\n    }\n\n    @Test\n    public void testLoadNoEdgesByTag()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(this.policySupplier.get()\n                .withAtlasEntitiesToConsiderForExpansion(\n                        entity -> \"relation\".equals(entity.getTag(\"type\").orElse(\"\")))\n                .withAggressivelyExploreRelations(true));\n        runLoadNoEdgesTest(dynamicAtlas);\n    }\n\n    @Test\n    public void testLoadNoEdgesByType()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(this.policySupplier.get()\n                .withAtlasEntitiesToConsiderForExpansion(entity -> entity instanceof Relation)\n                .withAggressivelyExploreRelations(true));\n        runLoadNoEdgesTest(dynamicAtlas);\n    }\n\n    private void runLoadEdgesOnlyTest(final DynamicAtlas dynamicAtlas)\n    {\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        // Test an area that does not exist\n        Assert.assertNull(dynamicAtlas.area(5));\n        Assert.assertNotNull(dynamicAtlas.area(1));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        // Usually prompts load of 12-1350-1869, but not here\n        Assert.assertNotNull(dynamicAtlas.area(2));\n        // Still 4\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n\n        // Now onto edges, the behavior should be the same as without the predicate\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        Assert.assertNull(dynamicAtlas.edge(6000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(dynamicAtlas.edge(1000000));\n        Assert.assertTrue(dynamicAtlas.edge(1000000).hasReverseEdge());\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n\n        // Prompts load of 12-1350-1869\n        Assert.assertNotNull(dynamicAtlas.edge(2000000));\n        Assert.assertEquals(6, dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(dynamicAtlas.edge(3000000));\n        Assert.assertEquals(6, dynamicAtlas.numberOfEdges());\n\n        // Prompts load of 12-1349-1869\n        Assert.assertNotNull(dynamicAtlas.edge(4000000));\n        Assert.assertEquals(8, dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(dynamicAtlas.edge(5000000));\n        Assert.assertEquals(8, dynamicAtlas.numberOfEdges());\n\n        // Prompts load of 12-1349-1870\n        Assert.assertNotNull(dynamicAtlas.edge(6000000));\n        Assert.assertEquals(9, dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(dynamicAtlas.edge(7000000));\n        Assert.assertEquals(9, dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(dynamicAtlas.edge(8000000));\n        Assert.assertEquals(9, dynamicAtlas.numberOfEdges());\n    }\n\n    private void runLoadNoEdgesTest(final DynamicAtlas dynamicAtlas)\n    {\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        // Test an area that does not exist\n        Assert.assertNull(dynamicAtlas.area(5));\n        Assert.assertNotNull(dynamicAtlas.area(1));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        // Usually prompts load of 12-1350-1869, but not here\n        Assert.assertNotNull(dynamicAtlas.area(2));\n        // Still 4\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        Assert.assertNull(dynamicAtlas.edge(6000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(dynamicAtlas.edge(1000000));\n        Assert.assertTrue(dynamicAtlas.edge(1000000).hasReverseEdge());\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n\n        // Does NOT Prompt load of 12-1350-1869\n        Assert.assertNotNull(dynamicAtlas.edge(2000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        Assert.assertNull(dynamicAtlas.edge(3000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n\n        // Does NOT Prompt load of 12-1349-1869\n        Assert.assertNull(dynamicAtlas.edge(4000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        Assert.assertNull(dynamicAtlas.edge(5000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n\n        // Does NOT Prompt load of 12-1349-1870\n        Assert.assertNull(dynamicAtlas.edge(6000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        Assert.assertNull(dynamicAtlas.edge(7000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n        // Was already there from the initial shard\n        Assert.assertNotNull(dynamicAtlas.edge(8000000));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n\n        // Does not prompt load of 12-1350-1869\n        // Aggressive loading of relations works only in preemptive load.\n        Assert.assertNotNull(dynamicAtlas.relation(1));\n        System.out.println(dynamicAtlas.relation(1));\n        Assert.assertEquals(4, dynamicAtlas.numberOfEdges());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasMovingTooFastTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasMovingTooFastTestRule;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * This test is there to make sure that querying a {@link DynamicAtlas} using the methods similar to\n * edgesIntersecting(Polygon) does not trigger a Shard expansion only based on the geometry of the\n * Polygon passed in.\n *\n * @author matthieun\n */\npublic class DynamicAtlasMovingTooFastTest\n{\n    @Rule\n    public DynamicAtlasMovingTooFastTestRule rule = new DynamicAtlasMovingTooFastTestRule();\n\n    private DynamicAtlas dynamicAtlas;\n    private Map<Shard, Atlas> store;\n\n    private final Supplier<DynamicAtlasPolicy> policySupplier = () ->\n    {\n        final Set<Shard> initialTiles = new HashSet<>();\n        initialTiles.add(new SlippyTile(5, 34, 6));\n        return new DynamicAtlasPolicy(shard ->\n        {\n            if (this.store.containsKey(shard))\n            {\n                return Optional.of(this.store.get(shard));\n            }\n            else\n            {\n                return Optional.empty();\n            }\n        }, new SlippyTileSharding(6), initialTiles, Rectangle.MAXIMUM).withExtendIndefinitely(false)\n                .withDeferredLoading(true);\n    };\n\n    @Before\n    public void prepare()\n    {\n        prepare(this.policySupplier.get());\n    }\n\n    public void prepare(final DynamicAtlasPolicy policy)\n    {\n        this.store = new HashMap<>();\n        this.store.put(new SlippyTile(5, 34, 6), this.rule.getAtlas()\n                .subAtlas(new SlippyTile(5, 34, 6).bounds(), AtlasCutType.SOFT_CUT).get());\n        this.store.put(new SlippyTile(6, 34, 6), this.rule.getAtlas()\n                .subAtlas(new SlippyTile(6, 34, 6).bounds(), AtlasCutType.SOFT_CUT).get());\n        this.store.put(new SlippyTile(6, 35, 6), this.rule.getAtlas()\n                .subAtlas(new SlippyTile(6, 35, 6).bounds(), AtlasCutType.SOFT_CUT).get());\n        this.store.put(new SlippyTile(5, 35, 6), this.rule.getAtlas()\n                .subAtlas(new SlippyTile(5, 35, 6).bounds(), AtlasCutType.SOFT_CUT).get());\n        this.dynamicAtlas = new DynamicAtlas(policy);\n        this.dynamicAtlas.preemptiveLoad();\n    }\n\n    @Test\n    public void testNotMovingTooFast()\n    {\n        Assert.assertEquals(1, this.dynamicAtlas.numberOfEdges());\n        final Iterable<Edge> edgesIntersecting = this.dynamicAtlas\n                .edgesIntersecting(this.dynamicAtlas.edges().iterator().next().bounds());\n        Assert.assertEquals(1, Iterables.size(edgesIntersecting));\n        Assert.assertEquals(\"primary\",\n                edgesIntersecting.iterator().next().getTags().get(\"highway\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasMultipleInitialShardTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasTestRule;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasMultipleInitialShardTest\n{\n    @Rule\n    public DynamicAtlasTestRule rule = new DynamicAtlasTestRule();\n\n    private DynamicAtlas dynamicAtlas;\n    private Map<Shard, Atlas> store;\n    private final Supplier<DynamicAtlasPolicy> policySupplier = () ->\n    {\n        final Set<Shard> initialShards = new HashSet<>();\n        initialShards.add(new SlippyTile(1350, 1870, 12));\n        initialShards.add(new SlippyTile(1349, 1870, 12));\n        return new DynamicAtlasPolicy(shard ->\n        {\n            if (this.store.containsKey(shard))\n            {\n                return Optional.of(this.store.get(shard));\n            }\n            else\n            {\n                return Optional.empty();\n            }\n        }, new SlippyTileSharding(12), initialShards, Rectangle.MAXIMUM);\n    };\n\n    @Before\n    public void prepare()\n    {\n        prepare(this.policySupplier.get());\n    }\n\n    public void prepare(final DynamicAtlasPolicy policy)\n    {\n        this.store = new HashMap<>();\n        this.store.put(new SlippyTile(1350, 1870, 12), this.rule.getAtlasz12x1350y1870());\n        this.store.put(new SlippyTile(1350, 1869, 12), this.rule.getAtlasz12x1350y1869());\n        this.store.put(new SlippyTile(1349, 1869, 12), this.rule.getAtlasz12x1349y1869());\n        this.store.put(new SlippyTile(1349, 1870, 12), this.rule.getAtlasz12x1349y1870());\n        this.dynamicAtlas = new DynamicAtlas(policy);\n    }\n\n    @Test\n    public void testLoadAreaByIdentifier()\n    {\n        // Already loaded: 12-1350-1870 and 12-1349-1870\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        // Test an area that does not exist\n        Assert.assertNull(this.dynamicAtlas.area(5));\n        Assert.assertNotNull(this.dynamicAtlas.area(1));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        // Prompts load of 12-1350-1869\n        Assert.assertNotNull(this.dynamicAtlas.area(2));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n    }\n\n    @Test\n    public void testLoadEdgeByIdentifier()\n    {\n        // Already loaded: 12-1350-1870 and 12-1349-1870\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNull(this.dynamicAtlas.edge(5000000));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.edge(1000000));\n        Assert.assertTrue(this.dynamicAtlas.edge(1000000).hasReverseEdge());\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n\n        // Prompts load of 12-1350-1869\n        Assert.assertNotNull(this.dynamicAtlas.edge(2000000));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.edge(3000000));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n\n        // Prompts load of 12-1349-1869\n        Assert.assertNotNull(this.dynamicAtlas.edge(4000000));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.edge(5000000));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasPartialInitialShardsTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasPartialInitialShardsTestRule;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasPartialInitialShardsTest\n{\n    @Rule\n    public DynamicAtlasPartialInitialShardsTestRule rule = new DynamicAtlasPartialInitialShardsTestRule();\n\n    private DynamicAtlas dynamicAtlas;\n    private Map<Shard, Atlas> store;\n\n    private final Supplier<DynamicAtlasPolicy> policySupplier = () ->\n    {\n        final Set<Shard> initialTiles = new HashSet<>();\n        initialTiles.add(new SlippyTile(240, 247, 9));\n        initialTiles.add(new SlippyTile(241, 247, 9));\n        return new DynamicAtlasPolicy(shard ->\n        {\n            if (this.store.containsKey(shard))\n            {\n                return Optional.of(this.store.get(shard));\n            }\n            else\n            {\n                return Optional.empty();\n            }\n        }, new SlippyTileSharding(9), initialTiles, Rectangle.MAXIMUM);\n    };\n\n    @Before\n    public void prepare()\n    {\n        prepare(this.policySupplier.get());\n    }\n\n    public void prepare(final DynamicAtlasPolicy policy)\n    {\n        this.store = new HashMap<>();\n        this.store.put(new SlippyTile(240, 247, 9), this.rule.getAtlas());\n        this.dynamicAtlas = new DynamicAtlas(policy);\n    }\n\n    @Test\n    public void testPartialInitialShards()\n    {\n        // Make sure that the DynamicAtlas is still loaded despite one of the initial shards being\n        // missing.\n        Assert.assertEquals(1, this.dynamicAtlas.numberOfAreas());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasPreemptiveLoadTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.AtlasEntityKey;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasPreemptiveLoadTestRule;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasPreemptiveLoadTest\n{\n    private static final Shard INITIAL_SHARD = new SlippyTile(240, 246, 9);\n\n    @Rule\n    public DynamicAtlasPreemptiveLoadTestRule rule = new DynamicAtlasPreemptiveLoadTestRule();\n\n    private Map<Shard, Atlas> store;\n    private final Supplier<DynamicAtlasPolicy> policySupplier = () -> new DynamicAtlasPolicy(\n            shard ->\n            {\n                if (this.store.containsKey(shard))\n                {\n                    return Optional.of(this.store.get(shard));\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }, new SlippyTileSharding(9), INITIAL_SHARD, Rectangle.MAXIMUM)\n            .withDeferredLoading(true).withExtendIndefinitely(false);\n    private final Supplier<DynamicAtlasPolicy> allInitialShardsPolicySupplier = () -> new DynamicAtlasPolicy(\n            shard ->\n            {\n                if (this.store.containsKey(shard))\n                {\n                    return Optional.of(this.store.get(shard));\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }, new SlippyTileSharding(9),\n            Rectangle.forLocated(INITIAL_SHARD.bounds().center(),\n                    new SlippyTile(241, 246, 9).bounds().center()),\n            Rectangle.MAXIMUM).withDeferredLoading(true).withExtendIndefinitely(false);\n    private final Supplier<DynamicAtlasPolicy> singleHitPolicySupplier = () -> new DynamicAtlasPolicy(\n            shard ->\n            {\n                if (this.store.containsKey(shard))\n                {\n                    return Optional.of(this.store.get(shard));\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }, new SlippyTileSharding(9), INITIAL_SHARD, Rectangle.MAXIMUM)\n            .withDeferredLoading(true).withExtendIndefinitely(true)\n            .withAtlasEntitiesToConsiderForExpansion(new Predicate<>()\n            {\n                private final Map<AtlasEntityKey, Integer> identifiersChecked = new HashMap<>();\n\n                @Override\n                public boolean test(final AtlasEntity atlasEntity)\n                {\n                    final AtlasEntityKey key = AtlasEntityKey.from(atlasEntity.getType(),\n                            atlasEntity.getIdentifier());\n                    if (this.identifiersChecked.containsKey(key)\n                            && this.identifiersChecked.get(key) > 9)\n                    {\n                        throw new CoreException(\"Checked {} {} times!\", key,\n                                this.identifiersChecked.get(key));\n                    }\n                    else\n                    {\n                        int newCount = 1;\n                        if (this.identifiersChecked.containsKey(key))\n                        {\n                            newCount += this.identifiersChecked.get(key);\n                        }\n                        this.identifiersChecked.put(key, newCount);\n                    }\n                    return true;\n                }\n            });\n\n    @Test\n    public void loadPreemptivelyCapExpansionCheckTest()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(this.singleHitPolicySupplier.get());\n        dynamicAtlas.preemptiveLoad();\n        Assert.assertEquals(3, dynamicAtlas.numberOfEdges());\n        for (int i = 0; i < 10; i++)\n        {\n            // Make sure that every time the preemptive load is done, the DynamicAtlas does not do\n            // further geometry checks for each feature request\n            dynamicAtlas.nodes().forEach(node -> Assert.assertTrue(node.within(Rectangle.MAXIMUM)));\n        }\n        Assert.assertEquals(2, dynamicAtlas.getTimesMultiAtlasWasBuiltUnderneath());\n    }\n\n    @Test\n    public void loadPreemptivelyTest()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(this.policySupplier.get());\n        dynamicAtlas.preemptiveLoad();\n        Assert.assertEquals(3, dynamicAtlas.numberOfEdges());\n        Assert.assertEquals(2, dynamicAtlas.getTimesMultiAtlasWasBuiltUnderneath());\n    }\n\n    @Test\n    public void loadPreemptivelyWithAllShardsAsInitialTest()\n    {\n        final DynamicAtlas dynamicAtlas = new DynamicAtlas(\n                this.allInitialShardsPolicySupplier.get());\n        dynamicAtlas.preemptiveLoad();\n        Assert.assertEquals(3, dynamicAtlas.numberOfEdges());\n        Assert.assertEquals(1, dynamicAtlas.getTimesMultiAtlasWasBuiltUnderneath());\n    }\n\n    @Before\n    public void prepare()\n    {\n        this.store = new HashMap<>();\n        this.store.put(new SlippyTile(240, 246, 9), this.rule.getAtlasZ9x240y246());\n        this.store.put(new SlippyTile(240, 245, 9), this.rule.getAtlasZ9x240y245());\n        this.store.put(new SlippyTile(241, 245, 9), this.rule.getAtlasZ9x241y245());\n        this.store.put(new SlippyTile(241, 246, 9), this.rule.getAtlasZ9x241y246());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasRestrainedExpansionWithPolygonTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasRestrainedExpansionWithPolygonTestRule;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * This test ensures that when the initial shards are supplied with a {@link Polygon} or\n * {@link MultiPolygon}, and the expansion policy is withExtendIndefinitely=false, then the\n * expansion does not consider the full initial shards for intersection, but the initial polygon.\n *\n * @author matthieun\n */\npublic class DynamicAtlasRestrainedExpansionWithPolygonTest\n{\n    @Rule\n    public DynamicAtlasRestrainedExpansionWithPolygonTestRule rule = new DynamicAtlasRestrainedExpansionWithPolygonTestRule();\n\n    private DynamicAtlas dynamicAtlas;\n    private Map<Shard, Atlas> store;\n\n    private final Supplier<DynamicAtlasPolicy> policySupplier = () ->\n    {\n        final Set<Shard> initialTiles = new HashSet<>();\n        initialTiles.add(new SlippyTile(6, 34, 6));\n        return new DynamicAtlasPolicy(shard ->\n        {\n            if (this.store.containsKey(shard))\n            {\n                return Optional.of(this.store.get(shard));\n            }\n            else\n            {\n                return Optional.empty();\n            }\n        }, new SlippyTileSharding(6),\n                initialTiles.iterator().next().bounds().expand(Distance.ONE_METER),\n                Rectangle.MAXIMUM).withExtendIndefinitely(false).withDeferredLoading(true);\n    };\n\n    @Before\n    public void prepare()\n    {\n        prepare(this.policySupplier.get());\n    }\n\n    public void prepare(final DynamicAtlasPolicy policy)\n    {\n        this.store = new HashMap<>();\n        this.store.put(new SlippyTile(5, 34, 6), this.rule.getAtlas()\n                .subAtlas(new SlippyTile(5, 34, 6).bounds(), AtlasCutType.SOFT_CUT).get());\n        this.store.put(new SlippyTile(6, 34, 6), this.rule.getAtlas()\n                .subAtlas(new SlippyTile(6, 34, 6).bounds(), AtlasCutType.SOFT_CUT).get());\n        this.store.put(new SlippyTile(4, 34, 6), this.rule.getAtlas()\n                .subAtlas(new SlippyTile(4, 34, 6).bounds(), AtlasCutType.SOFT_CUT).get());\n        this.dynamicAtlas = new DynamicAtlas(policy);\n        this.dynamicAtlas.preemptiveLoad();\n    }\n\n    @Test\n    public void testRestrainedExpansionWithPolygon()\n    {\n        Assert.assertEquals(2, this.dynamicAtlas.numberOfEdges());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/DynamicAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.BareAtlas;\nimport org.openstreetmap.atlas.geography.atlas.delta.AtlasDelta;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.policy.DynamicAtlasPolicy;\nimport org.openstreetmap.atlas.geography.atlas.dynamic.rules.DynamicAtlasTestRule;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiRelation;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedRelation;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasTest\n{\n    @Rule\n    public DynamicAtlasTestRule rule = new DynamicAtlasTestRule();\n\n    private DynamicAtlas dynamicAtlas;\n    private Map<Shard, Atlas> store;\n    private final Supplier<DynamicAtlasPolicy> policySupplier = () -> new DynamicAtlasPolicy(\n            shard ->\n            {\n                if (this.store.containsKey(shard))\n                {\n                    return Optional.of(this.store.get(shard));\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }, new SlippyTileSharding(12), new SlippyTile(1350, 1870, 12), Rectangle.MAXIMUM);\n\n    private final Supplier<DynamicAtlasPolicy> policySupplierWithMissingAtlas = () -> new DynamicAtlasPolicy(\n            shard ->\n            {\n                if (shard.equals(new SlippyTile(1349, 1869, 12)))\n                {\n                    return Optional.empty();\n                }\n                if (this.store.containsKey(shard))\n                {\n                    return Optional.of(this.store.get(shard));\n                }\n                else\n                {\n                    return Optional.empty();\n                }\n            }, new SlippyTileSharding(12), new SlippyTile(1350, 1870, 12), Rectangle.MAXIMUM);\n\n    @Before\n    public void prepare()\n    {\n        prepare(this.policySupplier.get());\n    }\n\n    public void prepare(final DynamicAtlasPolicy policy)\n    {\n        this.store = new HashMap<>();\n        this.store.put(new SlippyTile(1350, 1870, 12), this.rule.getAtlasz12x1350y1870());\n        this.store.put(new SlippyTile(1350, 1869, 12), this.rule.getAtlasz12x1350y1869());\n        this.store.put(new SlippyTile(1349, 1869, 12), this.rule.getAtlasz12x1349y1869());\n        this.store.put(new SlippyTile(1349, 1870, 12), this.rule.getAtlasz12x1349y1870());\n        this.dynamicAtlas = new DynamicAtlas(policy);\n    }\n\n    @Test\n    public void testGetLoadedAtlases()\n    {\n        prepare(this.policySupplier.get().withDeferredLoading(true));\n        this.dynamicAtlas.preemptiveLoad();\n\n        final Set<Atlas> atlases = this.dynamicAtlas.getAtlasesLoaded();\n        Assert.assertEquals(4, atlases.size());\n    }\n\n    @Test\n    public void testGetPolicy()\n    {\n        Assert.assertEquals(this.policySupplier.get().getInitialShards(),\n                this.dynamicAtlas.getPolicy().getInitialShards());\n    }\n\n    @Test\n    public void testGetShardToAtlasMap()\n    {\n        prepare(this.policySupplierWithMissingAtlas.get().withDeferredLoading(true));\n        this.dynamicAtlas.preemptiveLoad();\n\n        final Map<Shard, Atlas> atlasMap = this.dynamicAtlas.getShardToAtlasMap();\n        Assert.assertEquals(3, atlasMap.size());\n        Assert.assertTrue(atlasMap.containsKey(new SlippyTile(1350, 1870, 12)));\n        Assert.assertTrue(atlasMap.containsKey(new SlippyTile(1350, 1869, 12)));\n        Assert.assertTrue(atlasMap.containsKey(new SlippyTile(1349, 1870, 12)));\n    }\n\n    @Test\n    public void testLoadAreaByIdentifier()\n    {\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        // Test an area that does not exist\n        Assert.assertNull(this.dynamicAtlas.area(5));\n        Assert.assertNotNull(this.dynamicAtlas.area(1));\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        // Prompts load of 12-1350-1869\n        Assert.assertNotNull(this.dynamicAtlas.area(2));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n    }\n\n    @Test\n    public void testLoadEdgeByIdentifier()\n    {\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNull(this.dynamicAtlas.edge(6000000));\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.edge(1000000));\n        Assert.assertTrue(this.dynamicAtlas.edge(1000000).hasReverseEdge());\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertEquals(1, this.dynamicAtlas.getNumberOfShardsLoaded());\n\n        // Prompts load of 12-1350-1869\n        Assert.assertNotNull(this.dynamicAtlas.edge(2000000));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.edge(3000000));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        Assert.assertEquals(2, this.dynamicAtlas.getNumberOfShardsLoaded());\n\n        // Prompts load of 12-1349-1869\n        Assert.assertNotNull(this.dynamicAtlas.edge(4000000));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.edge(5000000));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n        Assert.assertEquals(3, this.dynamicAtlas.getNumberOfShardsLoaded());\n\n        // Prompts load of 12-1349-1870\n        Assert.assertNotNull(this.dynamicAtlas.edge(6000000));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.edge(7000000));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.edge(8000000));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n        Assert.assertEquals(4, this.dynamicAtlas.getNumberOfShardsLoaded());\n    }\n\n    @Test\n    public void testLoadIndefinitely()\n    {\n        Assert.assertEquals(9, Iterables.size(this.dynamicAtlas.edges()));\n    }\n\n    @Test\n    public void testLoadLineByIdentifier()\n    {\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        // Test an area that does not exist\n        Assert.assertNull(this.dynamicAtlas.line(5));\n        Assert.assertNotNull(this.dynamicAtlas.line(1));\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        // Prompts load of 12-1350-1869\n        Assert.assertNotNull(this.dynamicAtlas.line(2));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n    }\n\n    @Test\n    public void testLoadNodeByIdentifier()\n    {\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.node(1));\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.node(2));\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNull(this.dynamicAtlas.node(4));\n\n        // Prompts load of 12-1350-1869\n        Assert.assertEquals(1, this.dynamicAtlas.node(3).outEdges().size());\n        Assert.assertNotNull(this.dynamicAtlas.node(3));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.node(4));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNull(this.dynamicAtlas.node(6));\n\n        // Prompts load of 12-1349-1869\n        Assert.assertEquals(1, this.dynamicAtlas.node(5).outEdges().size());\n        Assert.assertNotNull(this.dynamicAtlas.node(5));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.node(6));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.node(8));\n\n        // Prompts load of 12-1349-1870\n        Assert.assertEquals(1, this.dynamicAtlas.node(7).outEdges().size());\n        Assert.assertNotNull(this.dynamicAtlas.node(8));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.node(8));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n    }\n\n    @Test\n    public void testLoadNotIndefinitely()\n    {\n        prepare(this.policySupplier.get().withExtendIndefinitely(false));\n        Assert.assertEquals(8, Iterables.size(this.dynamicAtlas.edges()));\n    }\n\n    @Test\n    public void testLoadPointByIdentifier()\n    {\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.point(1));\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.point(2));\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNull(this.dynamicAtlas.point(3));\n\n        // Prompts load of 12-1350-1869\n        this.dynamicAtlas.edge(2000000);\n        Assert.assertNotNull(this.dynamicAtlas.point(3));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.point(4));\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNull(this.dynamicAtlas.point(5));\n\n        // Prompts load of 12-1349-1869\n        this.dynamicAtlas.edge(4000000);\n        Assert.assertNotNull(this.dynamicAtlas.point(5));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.point(6));\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNull(this.dynamicAtlas.point(7));\n\n        // Prompts load of 12-1349-1870\n        this.dynamicAtlas.edge(6000000);\n        Assert.assertNotNull(this.dynamicAtlas.point(7));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n        Assert.assertNotNull(this.dynamicAtlas.point(8));\n        Assert.assertEquals(9, this.dynamicAtlas.numberOfEdges());\n    }\n\n    @Test\n    public void testLoadRelationByIdentifier()\n    {\n        // Already loaded: 12-1350-1870\n        final Relation relation1 = this.dynamicAtlas.relation(1);\n        Assert.assertEquals(1, relation1.members().size());\n        final Relation relation2 = this.dynamicAtlas.relation(2);\n        Assert.assertEquals(1, relation2.members().size());\n\n        // Prompts load of 12-1350-1869\n        this.dynamicAtlas.edge(2000000);\n        Assert.assertEquals(2, relation1.members().size());\n        Assert.assertEquals(1, relation2.members().size());\n\n        // Prompts load of 12-1349-1870\n        this.dynamicAtlas.edge(8000000);\n        Assert.assertEquals(2, relation1.members().size());\n        Assert.assertEquals(2, relation2.members().size());\n    }\n\n    @Test\n    public void testLoadRelationWithOverlappingMembersByIdentifier()\n    {\n        // Already loaded: 12-1350-1870\n        Assert.assertEquals(4, this.dynamicAtlas.numberOfEdges());\n        Assert.assertEquals(1, this.dynamicAtlas.relation(3).members().size());\n\n        // Prompts load of 12-1349-1870\n        this.dynamicAtlas.edge(8000000);\n        Assert.assertEquals(6, this.dynamicAtlas.numberOfEdges());\n\n        // Prompts load of 12-1349-1869\n        final Relation relation3 = this.dynamicAtlas.relation(3);\n        Assert.assertEquals(3, relation3.members().size());\n        Assert.assertEquals(8, this.dynamicAtlas.numberOfEdges());\n    }\n\n    /**\n     * Check to make sure that {@link Atlas#relationsLowerOrderFirst()} works when the {@link Atlas}\n     * is a {@link DynamicAtlas}. In older versions of the code, any relations that had members\n     * which were also relations would be dropped from the set returned by\n     * {@link BareAtlas#relationsLowerOrderFirst()}. This was due to a flaw in the membership\n     * assumptions made by {@link BareAtlas#relationsLowerOrderFirst()}, which assumed that\n     * relations in the main {@link DynamicAtlas} and their equivalent representation as a member\n     * {@link AtlasEntity} of another relation in the same {@link DynamicAtlas} were of consistent\n     * types. However, this was not the case. (The former representation would be of type\n     * {@link DynamicRelation} and the latter would be of type {@link MultiRelation} or\n     * {@link PackedRelation}). This has now been fixed, so this test should always pass.\n     */\n    @Test\n    public void testRelationsLowerOrderFirstConsistency()\n    {\n        final DynamicAtlas localDynamicAtlas;\n        final Map<Shard, Atlas> localStore = new HashMap<>();\n        localStore.put(new SlippyTile(0, 0, 0), this.rule.getAtlasForRelationsTest());\n        final Supplier<DynamicAtlasPolicy> localPolicySupplier = () -> new DynamicAtlasPolicy(\n                shard ->\n                {\n                    if (localStore.containsKey(shard))\n                    {\n                        return Optional.of(localStore.get(shard));\n                    }\n                    else\n                    {\n                        return Optional.empty();\n                    }\n                }, new SlippyTileSharding(0), new SlippyTile(0, 0, 0), Rectangle.MAXIMUM);\n        localDynamicAtlas = new DynamicAtlas(localPolicySupplier.get());\n\n        final Set<Relation> returnedByRelations = new HashSet<>();\n        final Set<Relation> returnedByRelationsLowerOrderFirst = new HashSet<>();\n\n        for (final Relation relation : localDynamicAtlas.relations())\n        {\n            returnedByRelations.add(relation);\n        }\n\n        for (final Relation relation : localDynamicAtlas.relationsLowerOrderFirst())\n        {\n            returnedByRelationsLowerOrderFirst.add(relation);\n        }\n\n        // Assert that their sizes equal, if not then we can fail fast\n        Assert.assertEquals(returnedByRelations.size(), returnedByRelationsLowerOrderFirst.size());\n\n        // Now that we know they have equal sizes, check member equality\n        Assert.assertEquals(returnedByRelations, returnedByRelationsLowerOrderFirst);\n    }\n\n    @Test\n    public void testRuleIntegrity()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Atlas atlasz12x1350y1870 = this.store.get(new SlippyTile(1350, 1870, 12));\n        final Atlas atlasz12x1350y1869 = this.store.get(new SlippyTile(1350, 1869, 12));\n        final Atlas atlasz12x1349y1869 = this.store.get(new SlippyTile(1349, 1869, 12));\n        final Atlas atlasz12x1349y1870 = this.store.get(new SlippyTile(1349, 1870, 12));\n        final Atlas multiAtlas = new MultiAtlas(atlasz12x1350y1870, atlasz12x1350y1869,\n                atlasz12x1349y1869, atlasz12x1349y1870);\n        Assert.assertEquals(\"Found differences: \" + new AtlasDelta(atlas, multiAtlas).toString(),\n                atlas, multiAtlas);\n    }\n\n    @Test\n    public void testSnapsLineItem()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        Assert.assertEquals(5,\n                atlas.snapsLineItem(atlas.node(1L).getLocation(), Distance.meters(100)).size());\n        Assert.assertEquals(3,\n                atlas.snaps(atlas.node(1L).getLocation(), Distance.meters(100)).size());\n    }\n\n    @Test\n    public void testSpatialFilters()\n    {\n        final Area testArea = this.dynamicAtlas.area(1);\n        Assert.assertTrue(this.dynamicAtlas\n                .areasCovering(testArea.asPolygon().center(), area -> area.equals(testArea))\n                .iterator().hasNext());\n        Assert.assertTrue(this.dynamicAtlas\n                .areasIntersecting(testArea.bounds(), area -> area.equals(testArea)).iterator()\n                .hasNext());\n\n        final Edge testEdge = this.dynamicAtlas.edge(1000000);\n        Assert.assertTrue(this.dynamicAtlas\n                .edgesContaining(testEdge.end().getLocation(), edge -> edge.equals(testEdge))\n                .iterator().hasNext());\n        Assert.assertTrue(this.dynamicAtlas\n                .edgesIntersecting(testEdge.bounds(), edge -> edge.equals(testEdge)).iterator()\n                .hasNext());\n\n        final Line testLine = this.dynamicAtlas.line(1);\n        Assert.assertTrue(this.dynamicAtlas\n                .linesContaining(testLine.asPolyLine().first(), edge -> edge.equals(testLine))\n                .iterator().hasNext());\n        Assert.assertTrue(this.dynamicAtlas\n                .linesIntersecting(testLine.bounds(), edge -> edge.equals(testLine)).iterator()\n                .hasNext());\n\n        final Node testNode = this.dynamicAtlas.node(1);\n        Assert.assertTrue(\n                this.dynamicAtlas.nodesWithin(testNode.bounds(), edge -> edge.equals(testNode))\n                        .iterator().hasNext());\n\n        final Point testPoint = this.dynamicAtlas.point(1);\n        Assert.assertTrue(\n                this.dynamicAtlas.pointsWithin(testPoint.bounds(), edge -> edge.equals(testPoint))\n                        .iterator().hasNext());\n\n        final Relation testRelation = this.dynamicAtlas.relation(1);\n        Assert.assertTrue(this.dynamicAtlas.relationsWithEntitiesIntersecting(testRelation.bounds(),\n                edge -> edge.equals(testRelation)).iterator().hasNext());\n    }\n\n    @Test\n    public void testTypeOfReturnedRelationMembers()\n    {\n        final DynamicAtlas localDynamicAtlas;\n        final Map<Shard, Atlas> localStore = new HashMap<>();\n        localStore.put(new SlippyTile(0, 0, 0), this.rule.getAtlasForRelationsTest());\n        final Supplier<DynamicAtlasPolicy> localPolicySupplier = () -> new DynamicAtlasPolicy(\n                shard ->\n                {\n                    if (localStore.containsKey(shard))\n                    {\n                        return Optional.of(localStore.get(shard));\n                    }\n                    else\n                    {\n                        return Optional.empty();\n                    }\n                }, new SlippyTileSharding(0), new SlippyTile(0, 0, 0), Rectangle.MAXIMUM);\n        localDynamicAtlas = new DynamicAtlas(localPolicySupplier.get());\n\n        for (final Relation relation : localDynamicAtlas.relations())\n        {\n            for (final RelationMember member : relation.allKnownOsmMembers())\n            {\n                Assert.assertTrue(atlasEntityIsADynamicEntity(member.getEntity()));\n            }\n            for (final RelationMember member : relation.members())\n            {\n                Assert.assertTrue(atlasEntityIsADynamicEntity(member.getEntity()));\n            }\n        }\n    }\n\n    private boolean atlasEntityIsADynamicEntity(final AtlasEntity entity)\n    {\n        final boolean isDynamicPoint = entity instanceof DynamicPoint;\n        final boolean isDynamicLine = entity instanceof DynamicLine;\n        final boolean isDynamicArea = entity instanceof DynamicArea;\n        final boolean isDynamicNode = entity instanceof DynamicNode;\n        final boolean isDynamicEdge = entity instanceof DynamicEdge;\n        final boolean isDynamicRelation = entity instanceof DynamicRelation;\n\n        return isDynamicPoint || isDynamicLine || isDynamicArea || isDynamicNode || isDynamicEdge\n                || isDynamicRelation;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasAggressiveRelationsTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic.rules;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasAggressiveRelationsTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"DynamicAtlasAgressiveRelationsTest_11-998-708.josm.osm\")\n    private Atlas atlasZ11X998Y708;\n\n    @TestAtlas(loadFromJosmOsmResource = \"DynamicAtlasAgressiveRelationsTest_11-999-708.josm.osm\")\n    private Atlas atlasZ11X999Y708;\n\n    public Atlas getAtlasZ11X998Y708()\n    {\n        return this.atlasZ11X998Y708;\n    }\n\n    public Atlas getAtlasZ11X999Y708()\n    {\n        return this.atlasZ11X999Y708;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasMovingTooFastTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic.rules;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasMovingTooFastTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"DynamicAtlasMovingTooFastTest.osm\")\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasPartialInitialShardsTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic.rules;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasPartialInitialShardsTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"DynamicAtlasPartialInitialShardsTest.osm\")\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasPreemptiveLoadTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic.rules;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasPreemptiveLoadTestRule extends CoreTestRule\n{\n    private static final String ONE = \"7.13463380459,-10.49970749297\";\n    private static final String TWO = \"7.03806608353,-10.65738262866\";\n    private static final String THREE = \"6.90376746323,-10.48617761334\";\n    private static final String FOUR = \"6.93682915051,-10.38626465608\";\n    private static final String FIVE = \"7.1124301863,-10.72451164682\";\n    private static final String SIX = \"7.06078971839,-10.76145862581\";\n    private static final String SEVEN = \"6.97092158452,-10.67871820807\";\n    private static final String EIGHT = \"6.90686709472,-10.71514480708\";\n    private static final String NINE = \"6.88671912738,-10.6230375496\";\n    private static final String TEN = \"6.85107062885,-10.60534463008\";\n    private static final String ELEVEN = \"6.88516924827,-10.5241653523\";\n    private static final String TWELVE = \"6.8185196719,-10.47785153357\";\n    private static final String THIRTEEN = \"6.84693729693,-10.41696707524\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT)),\n                    @Node(id = \"9\", coordinates = @Loc(value = NINE)),\n                    @Node(id = \"13\", coordinates = @Loc(value = THIRTEEN))\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"2000000\", coordinates = { @Loc(value = FIVE), @Loc(value = SIX),\n                            @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=trunk\", \"oneway=yes\" }),\n                    @Edge(id = \"3000000\", coordinates = { @Loc(value = NINE), @Loc(value = TEN),\n                            @Loc(value = ELEVEN), @Loc(value = TWELVE),\n                            @Loc(value = THIRTEEN) }, tags = { \"highway=motorway\", \"oneway=yes\" })\n\n            }\n\n    )\n    private Atlas atlasZ9x240y246;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT))\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"1000000\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=primary\", \"oneway=yes\" }),\n                    @Edge(id = \"2000000\", coordinates = { @Loc(value = FIVE), @Loc(value = SIX),\n                            @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=trunk\", \"oneway=yes\" })\n\n            }\n\n    )\n    private Atlas atlasZ9x240y245;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"1000000\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=primary\", \"oneway=yes\" })\n\n            }\n\n    )\n    private Atlas atlasZ9x241y245;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"9\", coordinates = @Loc(value = NINE)),\n                    @Node(id = \"13\", coordinates = @Loc(value = THIRTEEN))\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"1000000\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=primary\", \"oneway=yes\" }),\n                    @Edge(id = \"3000000\", coordinates = { @Loc(value = NINE), @Loc(value = TEN),\n                            @Loc(value = ELEVEN), @Loc(value = TWELVE),\n                            @Loc(value = THIRTEEN) }, tags = { \"highway=motorway\", \"oneway=yes\" })\n\n            }\n\n    )\n    private Atlas atlasZ9x241y246;\n\n    public Atlas getAtlasZ9x240y245()\n    {\n        return this.atlasZ9x240y245;\n    }\n\n    public Atlas getAtlasZ9x240y246()\n    {\n        return this.atlasZ9x240y246;\n    }\n\n    public Atlas getAtlasZ9x241y245()\n    {\n        return this.atlasZ9x241y245;\n    }\n\n    public Atlas getAtlasZ9x241y246()\n    {\n        return this.atlasZ9x241y246;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasRestrainedExpansionWithPolygonTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic.rules;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class DynamicAtlasRestrainedExpansionWithPolygonTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"DynamicAtlasRestrainedExpansionWithPolygonTest.osm\")\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.dynamic.rules;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * The Geojson representation of the below test atlas files is saved in the test/resources folder,\n * same package\n *\n * @author matthieun\n */\npublic class DynamicAtlasTestRule extends CoreTestRule\n{\n    // Inside 12-1350-1870\n    private static final String ONE = \"15.420563,-61.336198\";\n    private static final String TWO = \"15.429499,-61.332850\";\n    private static final String TWO_BIS = \"15.3907,-61.3112\";\n\n    // Inside 12-1350-1869\n    private static final String THREE = \"15.4855,-61.3041\";\n    private static final String FOUR = \"15.4809,-61.3366\";\n\n    // Inside 12-1349-1869\n    private static final String FIVE = \"15.4852,-61.3816\";\n    private static final String SIX = \"15.4781,-61.3949\";\n\n    // Inside 12-1349-1870\n    private static final String SEVEN = \"15.4145,-61.3826\";\n    private static final String EIGHT = \"15.4073,-61.3749\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX)),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN)),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT))\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"1000000\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"-1000000\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"2000000\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"3000000\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"4000000\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = FIVE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"5000000\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"6000000\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = SEVEN) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"7000000\", coordinates = { @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"8000000\", coordinates = { @Loc(value = EIGHT),\n                            @Loc(value = ONE) }, tags = { \"highway=secondary\" })\n\n            },\n\n            areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = TWO_BIS) }, tags = { \"landuse=residential\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"landuse=residential\" })\n\n            },\n\n            lines = {\n\n                    @Line(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = TWO_BIS) }, tags = { \"power=line\" }),\n                    @Line(id = \"2\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"power=line\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Point(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Point(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Point(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Point(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Point(id = \"6\", coordinates = @Loc(value = SIX)),\n                    @Point(id = \"7\", coordinates = @Loc(value = SEVEN)),\n                    @Point(id = \"8\", coordinates = @Loc(value = EIGHT))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"1000000\", role = \"a\", type = \"edge\"),\n                            @Member(id = \"3000000\", role = \"b\", type = \"edge\")\n\n                    }),\n\n                    @Relation(id = \"2\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"8\", role = \"a\", type = \"point\"),\n                            @Member(id = \"1\", role = \"b\", type = \"area\")\n\n                    }),\n\n                    @Relation(id = \"3\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5000000\", role = \"a\", type = \"edge\"),\n                            @Member(id = \"6000000\", role = \"b\", type = \"edge\"),\n                            @Member(id = \"1\", role = \"c\", type = \"area\")\n\n                    })\n\n            }\n\n    )\n    private Atlas atlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT))\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"1000000\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"-1000000\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"2000000\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"8000000\", coordinates = { @Loc(value = EIGHT),\n                            @Loc(value = ONE) }, tags = { \"highway=secondary\" })\n\n            },\n\n            areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = TWO_BIS) }, tags = { \"landuse=residential\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"landuse=residential\" })\n\n            },\n\n            lines = {\n\n                    @Line(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = TWO_BIS) }, tags = { \"power=line\" }),\n                    @Line(id = \"2\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"power=line\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Point(id = \"2\", coordinates = @Loc(value = TWO))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"1000000\", role = \"a\", type = \"edge\")\n\n                    }),\n\n                    @Relation(id = \"2\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"1\", role = \"b\", type = \"area\")\n\n                    }),\n\n                    @Relation(id = \"3\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"1\", role = \"c\", type = \"area\")\n\n                    }),\n\n            }\n\n    )\n    private Atlas atlasz12x1350y1870;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"2000000\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"3000000\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"4000000\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = FIVE) }, tags = { \"highway=secondary\" })\n\n            },\n\n            areas = {\n\n                    @Area(id = \"2\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"landuse=residential\" })\n\n            },\n\n            lines = {\n\n                    @Line(id = \"2\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"power=line\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Point(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"3000000\", role = \"b\", type = \"edge\")\n\n                    })\n\n            }\n\n    )\n    private Atlas atlasz12x1350y1869;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX)),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN))\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"4000000\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = FIVE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"5000000\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"6000000\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = SEVEN) }, tags = { \"highway=secondary\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Point(id = \"6\", coordinates = @Loc(value = SIX))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"3\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"5000000\", role = \"a\", type = \"edge\"),\n                            @Member(id = \"6000000\", role = \"b\", type = \"edge\")\n\n                    })\n\n            }\n\n    )\n    private Atlas atlasz12x1349y1869;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX)),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN)),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT))\n\n            },\n\n            edges = {\n\n                    @Edge(id = \"6000000\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = SEVEN) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"7000000\", coordinates = { @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"8000000\", coordinates = { @Loc(value = EIGHT),\n                            @Loc(value = ONE) }, tags = { \"highway=secondary\" })\n\n            },\n\n            points = {\n\n                    @Point(id = \"7\", coordinates = @Loc(value = SEVEN)),\n                    @Point(id = \"8\", coordinates = @Loc(value = EIGHT))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"2\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"8\", role = \"a\", type = \"point\")\n\n                    }),\n\n                    @Relation(id = \"3\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"6000000\", role = \"b\", type = \"edge\")\n\n                    })\n\n            }\n\n    )\n    private Atlas atlasz12x1349y1870;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"10\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"11\", coordinates = @Loc(value = SIX)),\n                    @Node(id = \"12\", coordinates = @Loc(value = SEVEN)),\n                    @Node(id = \"13\", coordinates = @Loc(value = EIGHT))\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"31\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"11\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"32\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"12\", role = \"a\", type = \"node\"),\n\n                            @Member(id = \"13\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"33\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"10\", role = \"a\", type = \"node\"),\n\n                            @Member(id = \"11\", role = \"a\", type = \"node\"),\n\n                            @Member(id = \"31\", role = \"b\", type = \"relation\")\n\n                    }),\n\n                    @Relation(id = \"34\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"10\", role = \"a\", type = \"node\"),\n\n                            @Member(id = \"13\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"35\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"11\", role = \"a\", type = \"node\"),\n\n                            @Member(id = \"12\", role = \"a\", type = \"node\")\n\n                    }),\n\n                    @Relation(id = \"36\", tags = { \"type=relation\" }, members = {\n\n                            @Member(id = \"12\", role = \"a\", type = \"node\"),\n\n                            @Member(id = \"34\", role = \"a\", type = \"relation\")\n\n                    }),\n\n            }\n\n    )\n    private Atlas atlasForRelationsTest;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    public Atlas getAtlasForRelationsTest()\n    {\n        return this.atlasForRelationsTest;\n    }\n\n    public Atlas getAtlasz12x1349y1869()\n    {\n        return this.atlasz12x1349y1869;\n    }\n\n    public Atlas getAtlasz12x1349y1870()\n    {\n        return this.atlasz12x1349y1870;\n    }\n\n    public Atlas getAtlasz12x1350y1869()\n    {\n        return this.atlasz12x1350y1869;\n    }\n\n    public Atlas getAtlasz12x1350y1870()\n    {\n        return this.atlasz12x1350y1870;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/AreaEntityTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * @author mgostintsev\n */\npublic class AreaEntityTestRule extends CoreTestRule\n{\n    private static final String ONE = \"37,-122.00\";\n    private static final String TWO = \"37,-122.01\";\n\n    @TestAtlas(\n\n            nodes = { @Node(id = \"1\", coordinates = @Loc(value = ONE), tags = { \"name=abc\" }),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)), })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/AreaTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Iterables;\nimport com.google.common.collect.Lists;\n\n/**\n * @author mgostintsev\n */\npublic class AreaTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AreaTest.class);\n\n    @Rule\n    public final AreaTestRule rule = new AreaTestRule();\n\n    @Test\n    public void testAreaGeometry()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n\n        final Iterable<Location> edge1RawGeometry = atlas.area(1).getRawGeometry();\n        logger.info(\"Edge 1 raw geometry {}\", edge1RawGeometry);\n\n        final Iterable<Location> edge1ClosedGeometry = atlas.area(1).getClosedGeometry();\n        logger.info(\"Edge 1 closed geometry {}\", Lists.newArrayList(edge1ClosedGeometry));\n\n        final Iterable<Location> edge2RawGeometry = atlas.area(2).getRawGeometry();\n        logger.info(\"Edge 2 raw geometry {}\", edge2RawGeometry);\n\n        final Iterable<Location> edge2ClosedGeometry = atlas.area(2).getClosedGeometry();\n        logger.info(\"Edge 2 closed geometry {}\", Lists.newArrayList(edge2ClosedGeometry));\n\n        // Assert single node difference between closed and raw geometry\n        Assert.assertEquals(Iterables.size(edge1RawGeometry) + 1,\n                Iterables.size(edge1ClosedGeometry));\n        Assert.assertEquals(Iterables.size(edge2RawGeometry) + 1,\n                Iterables.size(edge2ClosedGeometry));\n        // Assert raw geometry start and closed geometry end are equal\n        Assert.assertEquals(Iterables.getLast(edge1ClosedGeometry),\n                Iterables.get(edge1RawGeometry, 0));\n        Assert.assertEquals(Iterables.getLast(edge2ClosedGeometry),\n                Iterables.get(edge2RawGeometry, 0));\n        // Assert start and end of closed geometry are equal\n        Assert.assertEquals(Iterables.getLast(edge1ClosedGeometry),\n                Iterables.get(edge1ClosedGeometry, 0));\n        Assert.assertEquals(Iterables.getLast(edge2ClosedGeometry),\n                Iterables.get(edge2ClosedGeometry, 0));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/AreaTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * @author mgostintsev\n */\npublic class AreaTestRule extends CoreTestRule\n{\n    private static final String ONE = \"37.33,-122.00\";\n    private static final String TWO = \"37.33,-122.03\";\n    private static final String THREE = \"37.32,-122.03\";\n    private static final String FOUR = \"37.32,-122.00\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            }, areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE), @Loc(value = FOUR) }, tags = { \"building=yes\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO), }, tags = { \"building=yes\" }),\n\n            })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/AtlasEntityTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * @author Yazad Khambata\n */\npublic class AtlasEntityTest\n{\n    @Rule\n    public final AreaEntityTestRule rule = new AreaEntityTestRule();\n\n    @Test\n    public void getName()\n    {\n        Assert.assertEquals(\"abc\", this.rule.getAtlas().node(1).getName().get());\n        Assert.assertFalse(this.rule.getAtlas().node(2).getName().isPresent());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/AtlasItemIntersectionTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Tests atlas item intersection with {@link Polygon} functionality.\n *\n * @author mgostintsev\n */\npublic class AtlasItemIntersectionTest\n{\n    private static final String WITHIN_TEST_POLYGON_WKT = \"POLYGON ((-122.2886447 47.6182798, -122.2886447 47.618416, -122.289001 47.618416, -122.289001 47.6182798, -122.2886447 47.6182798))\";\n\n    @Rule\n    public final AtlasItemIntersectionTestRule rule = new AtlasItemIntersectionTestRule();\n\n    @Test\n    public void testAreasIntersectingPolygon()\n    {\n        final Atlas atlas = this.rule.getIntersectionAtlas();\n\n        final Rectangle notTouching = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.288935 47.618916)\"),\n                Location.forWkt(\"POINT(-122.288925 47.618946)\"));\n\n        final Rectangle fullyInside = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.288798 47.618472)\"),\n                Location.forWkt(\"POINT(-122.288788 47.618482)\"));\n\n        final Rectangle touchingAtCorner = Rectangle.forCorners(\n                Location.forWkt(\"POINT (-122.2886447 47.6182798)\"),\n                Location.forWkt(\"POINT (-122.288635 47.618286)\"));\n\n        final Rectangle overlapping = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.289001 47.6182798)\"),\n                Location.forWkt(\"POINT(-122.2886447 47.618416)\"));\n\n        Assert.assertEquals(\"There should be zero intersecting areas\", 0,\n                Iterables.size(atlas.areasIntersecting(notTouching)));\n\n        Assert.assertEquals(\"There should be a single intersecting area\", 1,\n                Iterables.size(atlas.areasIntersecting(fullyInside)));\n\n        Assert.assertEquals(\"There should be a single intersecting area\", 1,\n                Iterables.size(atlas.areasIntersecting(touchingAtCorner)));\n\n        Assert.assertEquals(\"There should be a single intersecting area\", 1,\n                Iterables.size(atlas.areasIntersecting(overlapping)));\n    }\n\n    @Test\n    public void testAreasNoIntersectingPolygon()\n    {\n        final Atlas atlas = this.rule.getNoIntersectionAtlas();\n\n        final Polygon triangle = new Polygon(Location.forString(\"47.6263, -122.209198\"),\n                Location.forString(\"47.628685, -122.209305\"),\n                Location.forString(\"47.628704, -122.211761\"));\n\n        Assert.assertEquals(\"There should be no intersecting area\", 0,\n                Iterables.size(atlas.areasIntersecting(triangle)));\n\n        Assert.assertEquals(\"There should be no intersecting area\", 0,\n                Iterables.size(atlas.areasIntersecting(triangle.bounds())));\n    }\n\n    @Test\n    public void testAreasWithinPolygon()\n    {\n        final Atlas atlas = this.rule.getWithinTestAtlas();\n        final Polygon polygon = Polygon.wkt(WITHIN_TEST_POLYGON_WKT);\n        Assert.assertEquals(\"There are 2 areas within this Polygon\", 2,\n                Iterables.size(atlas.areasWithin(polygon)));\n    }\n\n    @Test\n    public void testAtlasItemsWithinPolygon()\n    {\n        final Atlas atlas = this.rule.getWithinTestAtlas();\n\n        // These are equivalent representations of the same shape.\n        final Polygon polygonBoundary = Polygon.wkt(WITHIN_TEST_POLYGON_WKT);\n        final Rectangle rectangleBoundary = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.289001 47.6182798)\"),\n                Location.forWkt(\"POINT(-122.2886447 47.618416)\"));\n\n        // However, when the Polygon is not represented as a Rectangle, we rely on the underlying\n        // awt definition of insideness - which considers Node 4 from edge 0 to be \"outside\" the\n        // polygon, even though it is on the boundary.\n        Assert.assertEquals(\"There are 2 lines, 2 edges, 2 Areas and 2 Nodes within this Polygon\",\n                9, Iterables.size(atlas.itemsWithin(polygonBoundary)));\n\n        // If we represent the Polygon as a Rectangle, then we forego the awt call and the same Node\n        // 4 is now considered inside. This is an unfortunate side-affect of the awt dependency.\n        Assert.assertEquals(\"There are 2 lines, 2 edges, 2 Areas and 3 Nodes within this Polygon\",\n                9, Iterables.size(atlas.itemsWithin(rectangleBoundary)));\n    }\n\n    @Test\n    public void testEdgesIntersectingPolygon()\n    {\n        final Atlas atlas = this.rule.getIntersectionAtlas();\n\n        final Rectangle notTouching = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.288798 47.618472)\"),\n                Location.forWkt(\"POINT (-122.288788 47.618482)\"));\n\n        final Rectangle touchingAtCorner = Rectangle.forCorners(\n                Location.forWkt(\"POINT (-122.2886447 47.6182798)\"),\n                Location.forWkt(\"POINT (-122.288635 47.618286)\"));\n\n        final Rectangle overlapping = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.289001 47.6182798)\"),\n                Location.forWkt(\"POINT(-122.2886447 47.618416)\"));\n\n        Assert.assertEquals(\"There should be zero intersections\", 0,\n                Iterables.size(atlas.edgesIntersecting(notTouching)));\n\n        Assert.assertEquals(\"There should be a single intersection\", 1,\n                Iterables.size(atlas.edgesIntersecting(touchingAtCorner)));\n\n        Assert.assertEquals(\"There should be a single intersection\", 1,\n                Iterables.size(atlas.edgesIntersecting(overlapping)));\n    }\n\n    @Test\n    public void testEdgesWithinPolygon()\n    {\n        final Atlas atlas = this.rule.getWithinTestAtlas();\n        final Polygon polygon = Polygon.wkt(WITHIN_TEST_POLYGON_WKT);\n        Assert.assertEquals(\"There are exactly 2 edges within this Polygon\", 2,\n                Iterables.size(atlas.edgesWithin(polygon)));\n    }\n\n    @Test\n    public void testLineItemsWithinPolygon()\n    {\n        final Atlas atlas = this.rule.getWithinTestAtlas();\n        final Polygon polygon = Polygon.wkt(WITHIN_TEST_POLYGON_WKT);\n        Assert.assertEquals(\"There are 2 lines and 2 edges within this Polygon\", 4,\n                Iterables.size(atlas.lineItemsWithin(polygon)));\n    }\n\n    @Test\n    public void testLinesIntersectingPolygon()\n    {\n        final Atlas atlas = this.rule.getIntersectionAtlas();\n\n        final Rectangle notTouching = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.288798 47.618472)\"),\n                Location.forWkt(\"POINT (-122.288788 47.618482)\"));\n\n        final Rectangle touchingAtCorner = Rectangle.forCorners(\n                Location.forWkt(\"POINT (-122.2886447 47.6182798)\"),\n                Location.forWkt(\"POINT (-122.288635 47.618286)\"));\n\n        final Rectangle overlapping = Rectangle.forCorners(\n                Location.forWkt(\"POINT (-122.289001 47.6182798)\"),\n                Location.forWkt(\"POINT(-122.2886447 47.618416)\"));\n\n        Assert.assertEquals(\"There should be zero intersections\", 0,\n                Iterables.size(atlas.linesIntersecting(notTouching)));\n\n        Assert.assertEquals(\"There should be a single intersection\", 1,\n                Iterables.size(atlas.linesIntersecting(touchingAtCorner)));\n\n        Assert.assertEquals(\"There should be a single intersection\", 1,\n                Iterables.size(atlas.linesIntersecting(overlapping)));\n    }\n\n    @Test\n    public void testLinesWithinPolygon()\n    {\n        final Atlas atlas = this.rule.getWithinTestAtlas();\n        final Polygon polygon = Polygon.wkt(WITHIN_TEST_POLYGON_WKT);\n        Assert.assertEquals(\"There are exactly 2 lines within this Polygon\", 2,\n                Iterables.size(atlas.linesWithin(polygon)));\n    }\n\n    @Test\n    public void testNodesWithinPolygon()\n    {\n        final Atlas atlas = this.rule.getIntersectionAtlas();\n\n        final Rectangle notTouching = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.288798 47.618472)\"),\n                Location.forWkt(\"POINT(-122.288788 47.618482)\"));\n\n        final Rectangle touchingAtCorner = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.289001 47.6182798)\"),\n                Location.forWkt(\"POINT(-122.2886447 47.618416)\"));\n\n        final Rectangle containingMultipleNodes = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.289001 47.6182798)\"),\n                Location.forWkt(\"POINT(-122.2886447 47.621016)\"));\n\n        Assert.assertEquals(\"There should be zero nodes in this Polygon\", 0,\n                Iterables.size(atlas.nodesWithin(notTouching)));\n\n        Assert.assertEquals(\"There should be a single node in this Polygon\", 1,\n                Iterables.size(atlas.nodesWithin(touchingAtCorner)));\n\n        Assert.assertEquals(\"There should be two nodes in this Polygon\", 2,\n                Iterables.size(atlas.nodesWithin(containingMultipleNodes)));\n    }\n\n    @Test\n    public void testRelationsIntersectingPolygon()\n    {\n        final Atlas atlas = this.rule.getIntersectionAtlas();\n\n        final Rectangle notTouching = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.288798 47.618472)\"),\n                Location.forWkt(\"POINT(-122.288788 47.618482)\"));\n\n        final Rectangle intersectingSingleEdge = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.289001 47.6182798)\"),\n                Location.forWkt(\"POINT(-122.2886447 47.618416)\"));\n\n        final Rectangle containingMultipleEdges = Rectangle.forCorners(\n                Location.forWkt(\"POINT(-122.289001 47.6182798)\"),\n                Location.forWkt(\"POINT(-122.2886447 47.621016)\"));\n\n        Assert.assertEquals(\"There should be no parts of the relation touching this Polygon\", 0,\n                Iterables.size(atlas.relationsWithEntitiesIntersecting(notTouching)));\n\n        Assert.assertEquals(\n                \"There should be a single edge, part of one relation, running through this Polygon\",\n                1, Iterables.size(atlas.relationsWithEntitiesIntersecting(intersectingSingleEdge)));\n\n        Assert.assertEquals(\n                \"There should be two edges, both part of the same relation, running through this Polygon\",\n                1,\n                Iterables.size(atlas.relationsWithEntitiesIntersecting(containingMultipleEdges)));\n    }\n\n    @Test\n    public void testRelationsWithinPolygon()\n    {\n        final Atlas atlas = this.rule.getWithinTestAtlas();\n        final Polygon polygon = Polygon.wkt(WITHIN_TEST_POLYGON_WKT);\n        Assert.assertEquals(\n                \"There is a single relation that has all members fully within the polygon\", 1,\n                Iterables.size(atlas.relationsWithEntitiesWithin(polygon)));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/AtlasItemIntersectionTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * {@link AtlasItemIntersectionTest} test data.\n *\n * @author mgostintsev\n */\npublic class AtlasItemIntersectionTestRule extends CoreTestRule\n{\n    private static final String LOCATION_1 = \"47.625534, -122.210083\";\n    private static final String LOCATION_2 = \"47.625576, -122.208305\";\n    private static final String LOCATION_3 = \"47.626934, -122.208412\";\n\n    private static final String LOCATION_4 = \"47.6183566, -122.2888258\";\n    private static final String LOCATION_5 = \"47.6183727, -122.2889442\";\n    private static final String LOCATION_6 = \"47.6182798, -122.2886447\";\n    private static final String LOCATION_7 = \"47.6182598, -122.2886447\";\n    private static final String LOCATION_8 = \"47.6182898, -122.2886347\";\n    private static final String LOCATION_9 = \"47.6182718, -122.2886647\";\n    private static final String LOCATION_10 = \"47.6182998, -122.2886347\";\n    private static final String LOCATION_11 = \"47.6182218, -122.2886647\";\n\n    private static final String LOCATION_12 = \"47.6183566, -122.2888258\";\n    private static final String LOCATION_13 = \"47.6183466, -122.2888258\";\n    private static final String LOCATION_14 = \"47.6183466, -122.2888558\";\n    private static final String LOCATION_15 = \"47.6183566, -122.2888558\";\n    private static final String LOCATION_16 = \"47.6183466, -122.289001\";\n    private static final String LOCATION_17 = \"47.6183566, -122.289001\";\n    private static final String LOCATION_18 = \"47.6183466, -122.289501\";\n    private static final String LOCATION_19 = \"47.6183566, -122.289501\";\n    private static final String LOCATION_20 = \"47.6183566, -122.289301\";\n    private static final String LOCATION_21 = \"47.6183466, -122.289301\";\n\n    @TestAtlas(loadFromTextResource = \"intersectionAtlas.atlas.txt\")\n    private Atlas intersectionAtlas;\n\n    @TestAtlas(areas = { @Area(coordinates = { @Loc(value = LOCATION_1), @Loc(value = LOCATION_2),\n            @Loc(value = LOCATION_3), @Loc(value = LOCATION_1) }) })\n    private Atlas noIntersectionAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"4\", coordinates = @Loc(value = LOCATION_4)),\n                    @Node(id = \"5\", coordinates = @Loc(value = LOCATION_5)),\n                    @Node(id = \"6\", coordinates = @Loc(value = LOCATION_6)),\n                    @Node(id = \"7\", coordinates = @Loc(value = LOCATION_7)),\n                    @Node(id = \"8\", coordinates = @Loc(value = LOCATION_8)),\n                    @Node(id = \"9\", coordinates = @Loc(value = LOCATION_9)),\n                    @Node(id = \"10\", coordinates = @Loc(value = LOCATION_10)),\n                    @Node(id = \"11\", coordinates = @Loc(value = LOCATION_11))\n\n            }, edges = {\n\n                    @Edge(id = \"0\", coordinates = { @Loc(value = LOCATION_4),\n                            @Loc(value = LOCATION_5) }, tags = { \"highway=residential\",\n                                    \"test=fully inside\" }),\n                    @Edge(id = \"1\", coordinates = { @Loc(value = LOCATION_4),\n                            @Loc(value = LOCATION_6) }, tags = { \"highway=residential\",\n                                    \"test=touching boundary\" }),\n                    @Edge(id = \"2\", coordinates = { @Loc(value = LOCATION_4),\n                            @Loc(value = LOCATION_7) }, tags = { \"highway=trunk\",\n                                    \"test=extending outside\" }),\n                    @Edge(id = \"3\", coordinates = { @Loc(value = LOCATION_8),\n                            @Loc(value = LOCATION_9) }, tags = { \"highway=trunk\",\n                                    \"test=running through\" }),\n                    @Edge(id = \"4\", coordinates = { @Loc(value = LOCATION_10),\n                            @Loc(value = LOCATION_11) }, tags = { \"highway=trunk\",\n                                    \"test=fully outside\" })\n\n            }, lines = {\n\n                    @Line(id = \"0\", coordinates = { @Loc(value = LOCATION_4),\n                            @Loc(value = LOCATION_5) }, tags = { \"railway=station\" }),\n                    @Line(id = \"1\", coordinates = { @Loc(value = LOCATION_4),\n                            @Loc(value = LOCATION_6) }, tags = { \"railway=station\" }),\n                    @Line(id = \"2\", coordinates = { @Loc(value = LOCATION_4),\n                            @Loc(value = LOCATION_7) }, tags = { \"railway=station\" }),\n                    @Line(id = \"3\", coordinates = { @Loc(value = LOCATION_8),\n                            @Loc(value = LOCATION_9) }, tags = { \"railway=station\" }),\n                    @Line(id = \"4\", coordinates = { @Loc(value = LOCATION_10),\n                            @Loc(value = LOCATION_11) }, tags = { \"railway=station\" }),\n\n            }, areas = {\n\n                    @Area(id = \"0\", coordinates = { @Loc(value = LOCATION_12),\n                            @Loc(value = LOCATION_13), @Loc(value = LOCATION_14),\n                            @Loc(value = LOCATION_15) }, tags = { \"addr:housenumber=25\",\n                                    \"test=fully inside\" }),\n                    @Area(id = \"1\", coordinates = { @Loc(value = LOCATION_12),\n                            @Loc(value = LOCATION_13), @Loc(value = LOCATION_16),\n                            @Loc(value = LOCATION_17) }, tags = { \"addr:housenumber=25\",\n                                    \"test=touching boundary\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = LOCATION_12),\n                            @Loc(value = LOCATION_13), @Loc(value = LOCATION_18),\n                            @Loc(value = LOCATION_19) }, tags = { \"addr:housenumber=25\",\n                                    \"test=intersecting boundary\" }),\n                    @Area(id = \"3\", coordinates = { @Loc(value = LOCATION_20),\n                            @Loc(value = LOCATION_21), @Loc(value = LOCATION_18),\n                            @Loc(value = LOCATION_19) }, tags = { \"addr:housenumber=25\",\n                                    \"test=fully outside\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=route\" }, members = {\n                            @Member(id = \"0\", role = \"inside\", type = \"edge\"),\n                            @Member(id = \"1\", role = \"touchingEdge\", type = \"edge\") }),\n                    @Relation(id = \"2\", tags = { \"type=route\" }, members = {\n                            @Member(id = \"1\", role = \"touchingEdge\", type = \"edge\"),\n                            @Member(id = \"4\", role = \"outside\", type = \"edge\") }),\n                    @Relation(id = \"3\", tags = { \"type=route\" }, members = {\n                            @Member(id = \"2\", role = \"extendingOutside\", type = \"edge\"),\n                            @Member(id = \"4\", role = \"outside\", type = \"edge\") }),\n                    @Relation(id = \"4\", tags = { \"type=route\" }, members = {\n                            @Member(id = \"3\", role = \"running Through\", type = \"edge\"),\n                            @Member(id = \"4\", role = \"outside\", type = \"edge\") }),\n\n            })\n    private Atlas withinTestAtlas;\n\n    public Atlas getIntersectionAtlas()\n    {\n        return this.intersectionAtlas;\n    }\n\n    public Atlas getNoIntersectionAtlas()\n    {\n        return this.noIntersectionAtlas;\n    }\n\n    public Atlas getWithinTestAtlas()\n    {\n        return this.withinTestAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/EdgeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.tags.MaxSpeedTag;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n * @author mgostintsev\n * @author Yazad Khambata\n */\npublic class EdgeTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(EdgeTest.class);\n\n    @Rule\n    public final EdgeTestRule rule = new EdgeTestRule();\n\n    @Test\n    public void testCompare()\n    {\n        // Make sure the compare function does not use a difference which in the case here will\n        // overflow.\n        final Edge left = new CompleteEdge(Long.MAX_VALUE - 2L, PolyLine.TEST_POLYLINE, null, null,\n                null, null);\n        final Edge right = new CompleteEdge(Long.MIN_VALUE + 2L, PolyLine.TEST_POLYLINE, null, null,\n                null, null);\n        Assert.assertTrue(left.compareTo(right) > 0);\n    }\n\n    @Test\n    public void testConnectedNodes()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Edge edge6 = atlas.edge(6L).directionalized();\n        Assert.assertNotNull(edge6);\n        Assert.assertNotNull(edge6.start());\n        Assert.assertNotNull(edge6.end());\n        Assert.assertNotEquals(edge6.start(), edge6.end());\n        Assert.assertEquals(edge6.start(), edge6.connectedNode(ConnectedNodeType.START));\n        Assert.assertEquals(edge6.end(), edge6.connectedNode(ConnectedNodeType.END));\n    }\n\n    @Test\n    public void testDirectionalizedTags()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Edge edge6 = atlas.edge(6L).directionalized();\n        final Edge edge7 = atlas.edge(7L).directionalized();\n        final Edge edge8 = atlas.edge(8L).directionalized();\n        final Edge edge9 = atlas.edge(9L).directionalized();\n\n        logger.info(\"Edge 6: {}\", edge6);\n        Assert.assertEquals(\"60\", edge6.tag(MaxSpeedTag.KEY));\n        logger.info(\"Edge -6: {}\", edge6.reversed().get());\n        Assert.assertNull(edge6.reversed().get().tag(MaxSpeedTag.KEY));\n\n        logger.info(\"Edge 7: {}\", edge7);\n        Assert.assertNull(edge7.tag(MaxSpeedTag.KEY));\n        logger.info(\"Edge -7: {}\", edge7.reversed().get());\n        Assert.assertEquals(\"70\", edge7.reversed().get().tag(MaxSpeedTag.KEY));\n\n        logger.info(\"Edge 8: {}\", edge8);\n        Assert.assertEquals(\"80\", edge8.tag(MaxSpeedTag.KEY));\n        logger.info(\"Edge -8: {}\", edge8.reversed().get());\n        Assert.assertEquals(\"80\", edge8.reversed().get().tag(MaxSpeedTag.KEY));\n\n        logger.info(\"Edge 9: {}\", edge9);\n        Assert.assertEquals(\"90\", edge9.tag(MaxSpeedTag.KEY));\n        logger.info(\"Edge -9: {}\", edge9.reversed().get());\n        Assert.assertEquals(\"10\", edge9.reversed().get().tag(MaxSpeedTag.KEY));\n    }\n\n    @Test\n    public void testGetMainEdge()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n\n        // Given the reverse edge, should return the main\n        final Edge reverseEdge = atlas.edge(-293669785000001L);\n        Assert.assertEquals(atlas.edge(293669785000001L), reverseEdge.getMainEdge());\n\n        // The main edge should just be itself\n        final Edge mainEdge = atlas.edge(293669785000001L);\n        Assert.assertEquals(mainEdge, mainEdge.getMainEdge());\n\n        // Now, let's try with a one-way edge\n        final Edge mainEdge2 = atlas.edge(293669786000001L);\n        Assert.assertEquals(mainEdge2, mainEdge2.getMainEdge());\n    }\n\n    @Test\n    public void testIsWaySectioned()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n\n        final Edge nonWaySectioned = atlas.edge(293669785000000L);\n        Assert.assertFalse(nonWaySectioned.isWaySectioned());\n\n        final Edge reverseNonWaySectioned = atlas.edge(-293669785000000L);\n        Assert.assertFalse(reverseNonWaySectioned.isWaySectioned());\n\n        final Edge waySectioned = atlas.edge(293669785000001L);\n        Assert.assertTrue(waySectioned.isWaySectioned());\n\n        final Edge reverseWaySectioned = atlas.edge(-293669785000001L);\n        Assert.assertTrue(reverseWaySectioned.isWaySectioned());\n    }\n\n    @Test\n    public void testSize()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Edge edge = atlas.edge(293669785000000L);\n        Assert.assertTrue(edge.numberOfShapePoints() == 2);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/EdgeTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * @author matthieun\n */\npublic class EdgeTestRule extends CoreTestRule\n{\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO))\n\n            }, edges = {\n\n                    @Edge(id = \"6\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"maxspeed:forward=60\" }),\n                    @Edge(id = \"-6\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\", \"maxspeed:forward=60\" }),\n                    @Edge(id = \"7\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"maxspeed:backward=70\" }),\n                    @Edge(id = \"-7\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\",\n                                    \"maxspeed:backward=70\" }),\n                    @Edge(id = \"8\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"maxspeed=80\" }),\n                    @Edge(id = \"-8\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\", \"maxspeed=80\" }),\n                    @Edge(id = \"9\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"maxspeed:backward=10\", \"maxspeed:forward=90\" }),\n                    @Edge(id = \"-9\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\", \"maxspeed:backward=10\",\n                                    \"maxspeed:forward=90\" }),\n                    @Edge(id = \"293669785000000\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"-293669785000000\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"293669785000001\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"-293669785000001\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"293669786000001\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\", \"oneway=yes\" })\n\n            })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/ItemTypeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.BareAtlasTestRule;\n\n/**\n * @author matthieun\n */\npublic class ItemTypeTest\n{\n    @Rule\n    public final BareAtlasTestRule rule = new BareAtlasTestRule();\n\n    @Test\n    public void testEntityForIdentifier()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        Assert.assertEquals(atlas.node(1), ItemType.NODE.entityForIdentifier(atlas, 1));\n        Assert.assertEquals(atlas.edge(1), ItemType.EDGE.entityForIdentifier(atlas, 1));\n        Assert.assertEquals(atlas.area(1), ItemType.AREA.entityForIdentifier(atlas, 1));\n        Assert.assertEquals(atlas.line(1), ItemType.LINE.entityForIdentifier(atlas, 1));\n        Assert.assertEquals(atlas.point(1), ItemType.POINT.entityForIdentifier(atlas, 1));\n        Assert.assertEquals(atlas.relation(1), ItemType.RELATION.entityForIdentifier(atlas, 1));\n    }\n\n    @Test\n    public void testGetMemberClass()\n    {\n        Assert.assertEquals(Node.class, ItemType.NODE.getMemberClass());\n        Assert.assertEquals(Edge.class, ItemType.EDGE.getMemberClass());\n        Assert.assertEquals(Area.class, ItemType.AREA.getMemberClass());\n        Assert.assertEquals(Line.class, ItemType.LINE.getMemberClass());\n        Assert.assertEquals(Point.class, ItemType.POINT.getMemberClass());\n        Assert.assertEquals(Relation.class, ItemType.RELATION.getMemberClass());\n    }\n\n    @Test\n    public void testToShotsString()\n    {\n        Assert.assertEquals(\"N\", ItemType.NODE.toShortString());\n        Assert.assertEquals(\"E\", ItemType.EDGE.toShortString());\n        Assert.assertEquals(\"A\", ItemType.AREA.toShortString());\n        Assert.assertEquals(\"L\", ItemType.LINE.toShortString());\n        Assert.assertEquals(\"P\", ItemType.POINT.toShortString());\n        Assert.assertEquals(\"R\", ItemType.RELATION.toShortString());\n\n        Assert.assertEquals(ItemType.NODE, ItemType.shortValueOf(\"N\"));\n        Assert.assertEquals(ItemType.EDGE, ItemType.shortValueOf(\"E\"));\n        Assert.assertEquals(ItemType.AREA, ItemType.shortValueOf(\"A\"));\n        Assert.assertEquals(ItemType.LINE, ItemType.shortValueOf(\"L\"));\n        Assert.assertEquals(ItemType.POINT, ItemType.shortValueOf(\"P\"));\n        Assert.assertEquals(ItemType.RELATION, ItemType.shortValueOf(\"R\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/LineItemTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * @author matthieun\n */\npublic class LineItemTest\n{\n    @Rule\n    public final LineItemTestRule rule = new LineItemTestRule();\n\n    @Test\n    public void testOverallHeading()\n    {\n        final Atlas atlas = this.rule.getoverallHeadingAtlas();\n        final Line linear = atlas.line(1L);\n        final Line loop = atlas.line(2L);\n        Assert.assertTrue(linear.overallHeading().isPresent());\n        Assert.assertEquals(725290933L, linear.overallHeading().get().asDm7());\n        Assert.assertFalse(loop.overallHeading().isPresent());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/LineItemTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class LineItemTestRule extends CoreTestRule\n{\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n    private static final String THREE = \"37.780724, -122.472249\";\n    private static final String FOUR = \"37.780825, -122.471896\";\n\n    @TestAtlas(\n\n            lines = {\n\n                    @TestAtlas.Line(id = \"1\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = THREE) }, tags = { \"name=Linear\" }),\n                    @TestAtlas.Line(id = \"2\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO), @TestAtlas.Loc(value = THREE),\n                            @TestAtlas.Loc(value = FOUR),\n                            @TestAtlas.Loc(value = ONE) }, tags = { \"name=Loop\" })\n\n            }\n\n    )\n    private Atlas overallHeadingAtlas;\n\n    public Atlas getoverallHeadingAtlas()\n    {\n        return this.overallHeadingAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/LoopingRelationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class LoopingRelationTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(LoopingRelationTest.class);\n\n    @Rule\n    public final LoopingRelationTestRule rule = new LoopingRelationTestRule();\n\n    private Atlas combined;\n\n    @Before\n    public void init()\n    {\n        this.combined = new MultiAtlas(this.rule.getAtlas1(), this.rule.getAtlas2());\n    }\n\n    @Test\n    public void testBounds()\n    {\n        final Rectangle bounds3 = this.combined.relation(3).bounds();\n        logger.info(\"Relation 3: {}\", bounds3);\n        final Rectangle bounds4 = this.combined.relation(4).bounds();\n        logger.info(\"Relation 4: {}\", bounds4);\n    }\n\n    @Test\n    public void testIntersects()\n    {\n        final boolean intersects3 = this.combined.relation(3).intersects(Polygon.SILICON_VALLEY);\n        logger.info(\"Relation 3: {}\", intersects3);\n        final boolean intersects4 = this.combined.relation(4).intersects(Polygon.SILICON_VALLEY);\n        logger.info(\"Relation 4: {}\", intersects4);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/LoopingRelationTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class LoopingRelationTestRule extends CoreTestRule\n{\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n    private static final String THREE = \"37.780724, -122.472249\";\n    private static final String FOUR = \"37.780825, -122.471896\";\n\n    @TestAtlas(\n\n            areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"name=First\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"3\", tags = { \"type=outside\" }, members = {\n                            @Member(id = \"1\", role = \"outside\", type = \"area\") }),\n                    @Relation(id = \"4\", tags = { \"type=inside\" }, members = {\n                            @Member(id = \"1\", role = \"outside\", type = \"area\"),\n                            @Member(id = \"3\", role = \"outside\", type = \"relation\") })\n\n            }\n\n    )\n    private Atlas atlas1;\n\n    @TestAtlas(\n\n            areas = {\n\n                    @Area(id = \"2\", coordinates = { @Loc(value = TWO), @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"name=Second\" })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"4\", tags = { \"type=inside\" }, members = {\n                            @Member(id = \"2\", role = \"outside\", type = \"area\") }),\n                    @Relation(id = \"3\", tags = { \"type=outside\" }, members = {\n                            @Member(id = \"2\", role = \"outside\", type = \"area\"),\n                            @Member(id = \"4\", role = \"outside\", type = \"relation\") })\n\n            }\n\n    )\n    private Atlas atlas2;\n\n    public Atlas getAtlas1()\n    {\n        return this.atlas1;\n    }\n\n    public Atlas getAtlas2()\n    {\n        return this.atlas2;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RelationBeanTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\n\n/**\n * Test for Relation producing its own bean\n *\n * @author jklamer\n */\npublic class RelationBeanTest\n{\n    @Rule\n    public final RelationBeanTestRule setup = new RelationBeanTestRule();\n\n    @Test\n    public void testBeanCreation()\n    {\n        final Relation relation1 = this.setup.getAtlas().relation(1);\n        final Relation relation2 = this.setup.getAtlas().relation(2);\n        final Relation relation3 = this.setup.getAtlas().relation(3);\n\n        final RelationBean relationBean1 = new RelationBean();\n        relationBean1.addItem(1L, \"outside\", ItemType.NODE);\n\n        final RelationBean relationBean2 = new RelationBean();\n        relationBean2.addItem(1L, \"inside\", ItemType.NODE);\n        relationBean2.addItem(2L, \"outside\", ItemType.NODE);\n        relationBean2.addItem(6L, \"outside\", ItemType.NODE);\n\n        final RelationBean relationBean3 = new RelationBean();\n        relationBean3.addItem(3L, \"outside\", ItemType.NODE);\n        relationBean3.addItem(4L, \"front side\", ItemType.NODE);\n        relationBean3.addItem(5L, \"outside\", ItemType.NODE);\n\n        Assert.assertEquals(relationBean1, relation1.getBean());\n        Assert.assertEquals(relationBean2, relation2.getBean());\n        Assert.assertEquals(relationBean3, relation3.getBean());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RelationBeanTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * Test Rule for {@link RelationBeanTest}\n *\n * @author jklamer\n */\npublic class RelationBeanTestRule extends CoreTestRule\n{\n\n    @TestAtlas(nodes = { @Node(id = \"1\"), @Node(id = \"2\"), @Node(id = \"3\"), @Node(id = \"4\"),\n            @Node(id = \"5\"), @Node(id = \"6\") }, relations = {\n                    @Relation(id = \"1\", members = {\n                            @Member(id = \"1\", role = \"outside\", type = \"node\") }),\n                    @Relation(id = \"2\", members = {\n                            @Member(id = \"1\", role = \"inside\", type = \"node\"),\n                            @Member(id = \"2\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"6\", role = \"outside\", type = \"node\") }),\n                    @Relation(id = \"3\", members = {\n                            @Member(id = \"3\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"4\", role = \"front side\", type = \"node\"),\n                            @Member(id = \"5\", role = \"outside\", type = \"node\") }) })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RelationFlatteningRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author samuelgass\n */\npublic class RelationFlatteningRule extends CoreTestRule\n{\n    @TestAtlas(nodes = { @Node(id = \"1\"), @Node(id = \"2\"), @Node(id = \"3\"), @Node(id = \"4\"),\n            @Node(id = \"5\"), @Node(id = \"6\") }, relations = {\n\n                    @Relation(id = \"6\", members = {\n                            @Member(id = \"1\", role = \"outside\", type = \"node\") }),\n                    @Relation(id = \"7\", members = {\n                            @Member(id = \"1\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"2\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"6\", role = \"outside\", type = \"node\") }),\n                    @Relation(id = \"8\", members = {\n                            @Member(id = \"4\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"5\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"8\", role = \"outside\", type = \"relation\") }),\n                    @Relation(id = \"9\", members = {\n                            @Member(id = \"4\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"5\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"7\", role = \"outside\", type = \"relation\"),\n                            @Member(id = \"6\", role = \"outside\", type = \"relation\") }),\n                    @Relation(id = \"10\", members = {\n                            @Member(id = \"6\", role = \"outside\", type = \"relation\"),\n                            @Member(id = \"3\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"9\", role = \"outside\", type = \"relation\") }),\n                    @Relation(id = \"2\", members = {\n                            @Member(id = \"3\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"4\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"6\", role = \"outside\", type = \"relation\") }),\n                    @Relation(id = \"1\", members = {\n                            @Member(id = \"5\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"2\", role = \"outside\", type = \"node\"),\n                            @Member(id = \"2\", role = \"outside\", type = \"relation\") }) })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RelationFlatteningTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author samuelgass\n */\npublic class RelationFlatteningTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(RelationFlatteningTest.class);\n\n    @Rule\n    public final RelationFlatteningRule rule = new RelationFlatteningRule();\n\n    @Test\n    public void testLoopingRelation()\n    {\n        final Relation loopingRelation = this.rule.getAtlas().relation(8);\n        logger.info(\"Looping (self-containing) Relation: {}\", loopingRelation);\n        final Set<AtlasObject> flattened = loopingRelation.flatten();\n        logger.info(\"Flattened: {}\", flattened);\n        assertEquals(2, flattened.size());\n    }\n\n    @Test\n    public void testNestedRelation()\n    {\n        final Relation nestedRelation = this.rule.getAtlas().relation(10);\n        logger.info(\"Nested Relation: {}\", nestedRelation);\n        final Set<AtlasObject> flattened = nestedRelation.flatten();\n        logger.info(\"Flattened: {}\", flattened);\n        assertEquals(6, flattened.size());\n    }\n\n    @Test\n    public void testNodesAndRelationsWithSameId()\n    {\n        final Relation relation = this.rule.getAtlas().relation(1);\n        logger.info(\"Relation containing nodes and relations with the same numeric id: {}\",\n                relation);\n        final Set<AtlasObject> flattened = relation.flatten();\n        logger.info(\"Flattened: {}\", flattened);\n        assertEquals(5, flattened.size());\n    }\n\n    @Test\n    public void testShallowRelation()\n    {\n        final Relation shallowRelation = this.rule.getAtlas().relation(6);\n        logger.info(\"Shallow (1-node) relation: {}\", shallowRelation);\n        final Set<AtlasObject> flattened = shallowRelation.flatten();\n        logger.info(\"Flattened: {}\", flattened);\n        assertEquals(1, flattened.size());\n        final AtlasObject memberNode = flattened.stream().findFirst().get();\n        if (memberNode instanceof Node)\n        {\n            assertEquals(1, ((Node) memberNode).getIdentifier());\n        }\n        else\n        {\n            Assert.fail(\"Member was not the expected type of 'Node'!\");\n        }\n    }\n\n    @Test\n    public void testSubRelationsOfLoopingRelations()\n    {\n        final Relation loopingRelation = this.rule.getAtlas().relation(8);\n        logger.info(\"Looping (self-containing) Relation: {}\", loopingRelation);\n        final Set<Long> subrelations = loopingRelation.flattenRelations();\n        logger.info(\"Flattened: {}\", subrelations);\n        assertEquals(1, subrelations.size());\n    }\n\n    @Test\n    public void testSubRelationsOfNestedRelation()\n    {\n        final Relation nestedRelation = this.rule.getAtlas().relation(10);\n        logger.info(\"Nested Relation: {}\", nestedRelation);\n        final Set<Long> subrelations = nestedRelation.flattenRelations();\n        logger.info(\"Flattened: {}\", subrelations);\n        assertEquals(4, subrelations.size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RelationMemberComparisonTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Ignore;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.identifiers.EntityIdentifierGenerator;\n\n/**\n * Tests RelationMember the compareTo function\n *\n * @author cstaylor\n */\npublic class RelationMemberComparisonTestCase\n{\n    @Rule\n    public RelationMemberComparisonTestCaseRule setup = new RelationMemberComparisonTestCaseRule();\n\n    @Test\n    public void bothRuleRolesNull()\n    {\n        final RelationMember first = new RelationMember(null, this.setup.area1(), 0L);\n        final RelationMember second = new RelationMember(null, this.setup.area1(), 0L);\n        Assert.assertEquals(0, first.compareTo(second));\n    }\n\n    @Test\n    public void deltaNegative()\n    {\n        final RelationMember first = new RelationMember(\"Something\", this.setup.area1(), 0L);\n        final RelationMember second = new RelationMember(\"Something\", this.setup.area2(), 0L);\n        Assert.assertEquals(-1, first.compareTo(second));\n    }\n\n    @Test\n    public void deltaPositive()\n    {\n        final RelationMember first = new RelationMember(\"Something\", this.setup.area1(), 0L);\n        final RelationMember second = new RelationMember(\"Something\", this.setup.area2(), 0L);\n        Assert.assertEquals(1, second.compareTo(first));\n    }\n\n    @Test\n    public void firstRuleRoleNull()\n    {\n        final RelationMember first = new RelationMember(null, this.setup.area1(), 0L);\n        final RelationMember second = new RelationMember(\"Something\", this.setup.area1(), 0L);\n        Assert.assertEquals(-1, first.compareTo(second));\n    }\n\n    @Test\n    public void neitherRuleRollNull()\n    {\n        final RelationMember first = new RelationMember(\"Something\", this.setup.area1(), 0L);\n        final RelationMember second = new RelationMember(\"Something\", this.setup.area1(), 0L);\n        Assert.assertEquals(0, first.compareTo(second));\n    }\n\n    @Ignore(\"We don't verify null\")\n    @Test\n    public void secondNull()\n    {\n        final RelationMember first = new RelationMember(\"Something\", this.setup.area1(), 0L);\n        Assert.assertEquals(1, first.compareTo(null));\n    }\n\n    @Test\n    public void secondRuleRollNull()\n    {\n        final RelationMember first = new RelationMember(\"Something\", this.setup.area1(), 0L);\n        final RelationMember second = new RelationMember(null, this.setup.area1(), 0L);\n        Assert.assertEquals(1, first.compareTo(second));\n    }\n\n    /**\n     * In an older version of this code, the RelationMember#compareTo method used to perform\n     * identifier comparisons by looking at the sign of a \"delta\" such that \"delta = id1 - id2\".\n     * However, in cases where atlas identifiers were extremely large and of opposite signs (e.g.\n     * when they are generated by {@link EntityIdentifierGenerator}) this would cause integer\n     * overflow, returning the opposite of the expected result.\n     */\n    @Test\n    public void testOverflowComparison()\n    {\n        final RelationMember first = new RelationMember(null, this.setup.area3(), 0L);\n        final RelationMember second = new RelationMember(null, this.setup.area4(), 0L);\n\n        Assert.assertTrue(first.compareTo(second) < 0);\n        Assert.assertTrue(second.compareTo(first) > 0);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RelationMemberComparisonTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\n\n/**\n * Configuration data for {@link RelationMemberComparisonTestCase}\n *\n * @author cstaylor\n */\npublic class RelationMemberComparisonTestCaseRule extends CoreTestRule\n{\n    public static final long ID_NAME1 = 1L;\n    public static final String ID_NAME1_STRING = \"1\";\n\n    public static final long ID_NAME2 = 2L;\n    public static final String ID_NAME2_STRING = \"2\";\n\n    public static final long ID_NAME3 = -8902174024992476407L;\n    public static final String ID_NAME3_STRING = \"-8902174024992476407\";\n\n    public static final long ID_NAME4 = 2401632205683965138L;\n    public static final String ID_NAME4_STRING = \"2401632205683965138\";\n\n    @TestAtlas(areas = { @Area(id = ID_NAME1_STRING, tags = { \"name=First\" }),\n            @Area(id = ID_NAME2_STRING, tags = { \"name=Second\" }),\n            @Area(id = ID_NAME3_STRING, tags = { \"name=Third\" }),\n            @Area(id = ID_NAME4_STRING, tags = { \"name=Fourth\" }) })\n    private Atlas atlas;\n\n    public AtlasItem area1()\n    {\n        return this.atlas.area(ID_NAME1);\n    }\n\n    public AtlasItem area2()\n    {\n        return this.atlas.area(ID_NAME2);\n    }\n\n    public AtlasItem area3()\n    {\n        return this.atlas.area(ID_NAME3);\n    }\n\n    public AtlasItem area4()\n    {\n        return this.atlas.area(ID_NAME4);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RelationMemberListTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.Arrays;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\n\n/**\n * @author lcram\n */\npublic class RelationMemberListTest\n{\n    @Test\n    public void testEqualsExplicitlyExcluded()\n    {\n        final RelationMember member = new RelationMember(null,\n                new CompletePoint(1L, null, null, null), 1L);\n\n        final RelationMemberList list1 = new RelationMemberList(Arrays.asList(member));\n        list1.addItemExplicitlyExcluded(new RelationBeanItem(1L, \"role\", ItemType.AREA));\n\n        final RelationMemberList list2 = new RelationMemberList(Arrays.asList(member));\n        list2.addItemExplicitlyExcluded(new RelationBeanItem(1L, \"role\", ItemType.AREA));\n\n        final RelationMemberList list3 = new RelationMemberList(Arrays.asList(member));\n        list3.addItemExplicitlyExcluded(new RelationBeanItem(2L, \"role\", ItemType.AREA));\n\n        Assert.assertTrue(list1.equalsIncludingExplicitlyExcluded(list2));\n        Assert.assertFalse(list1.equalsIncludingExplicitlyExcluded(list3));\n        Assert.assertFalse(list1.equalsIncludingExplicitlyExcluded(\"foo\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RouteTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * @author matthieun\n * @author mgostintsev\n */\npublic class RouteTest\n{\n    @Rule\n    public final RouteTestRule rule = new RouteTestRule();\n\n    private Atlas atlas;\n    private Route route;\n\n    @Before\n    public void init()\n    {\n        this.atlas = this.rule.getPackedAtlas();\n        final Edge edge1 = this.atlas.edge(98);\n        final Edge edge2 = this.atlas.edge(987);\n\n        this.route = Route.forEdge(edge1);\n        this.route = this.route.append(edge2);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testGetIndex()\n    {\n        Assert.assertEquals(98, this.route.get(0).getIdentifier());\n        Assert.assertEquals(987, this.route.get(1).getIdentifier());\n\n        this.route.get(-7);\n        Assert.fail(\"Should complain about wrong index\");\n\n        this.route.get(7);\n        Assert.fail(\"Should complain about wrong index\");\n    }\n\n    /**\n     * Before the addition of the start and end {@link Node}s to the {@link Route #hashCode()}, the\n     * {@link Route}s in this test would generate hash code collisions. This test verifies the\n     * collisions no longer occur.\n     */\n    @Test\n    public void testHashCode()\n    {\n        final Atlas atlas = this.rule.getRouteHashCodeAtlas();\n        final Edge edge1 = atlas.edge(-206786592000008L);\n        final Edge edge2 = atlas.edge(206786592000008L);\n        final Edge edge3 = atlas.edge(206786592000007L);\n        final Edge edge4 = atlas.edge(-206786592000007L);\n\n        final Route route1 = Route.forEdges(edge1, edge2);\n        final Route route2 = Route.forEdges(edge3, edge2);\n\n        Assert.assertTrue(\"Route 1 and Route 2 no longer collide\",\n                route1.hashCode() != route2.hashCode());\n\n        final Route route3 = Route.forEdges(edge1, edge4);\n        final Route route4 = Route.forEdges(edge3, edge4);\n\n        Assert.assertTrue(\"Route 3 and Route 4 no longer collide\",\n                route3.hashCode() != route4.hashCode());\n    }\n\n    @Test\n    public void testOverlappingRoutes()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Route startOnlyRoute = Route.forEdge(atlas.edge(159019301));\n        final Route middleOnlyRoute = Route.forEdge(atlas.edge(128620751));\n        final Route endOnlyRoute = Route.forEdge(atlas.edge(128620796));\n        final Route shorterRouteAtBeginning = Route.forEdges(atlas.edge(159019301),\n                atlas.edge(128620751));\n        final Route shorterRouteAtEnd = Route.forEdges(atlas.edge(128620751),\n                atlas.edge(128620796));\n        final Route longerRoute = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751),\n                atlas.edge(128620796));\n        final Route divergingRoute = Route.forEdges(atlas.edge(128620751), atlas.edge(138620888));\n\n        Assert.assertEquals(\n                \"The route with only the start edge should overlap at its 0th and only index\", 0,\n                startOnlyRoute.overlapIndex(longerRoute));\n        Assert.assertEquals(\n                \"The route with only the middle edge should overlap at its 0th and only index\", 0,\n                middleOnlyRoute.overlapIndex(longerRoute));\n        Assert.assertEquals(\n                \"The route with only the end edge should overlap at its 0th and only index\", 0,\n                endOnlyRoute.overlapIndex(longerRoute));\n        Assert.assertEquals(\"The shorter route should overlap at its last (1st) index\", 1,\n                shorterRouteAtBeginning.overlapIndex(longerRoute));\n        Assert.assertEquals(\"The same should hold true in reverse\", 1,\n                longerRoute.overlapIndex(shorterRouteAtBeginning));\n        Assert.assertEquals(\"The shorter route should overlap at its last (1st) index\", 1,\n                shorterRouteAtEnd.overlapIndex(longerRoute));\n        Assert.assertEquals(\"The entire route should overlap at its middle (1st) index\", 1,\n                longerRoute.overlapIndex(shorterRouteAtBeginning));\n        Assert.assertEquals(\"The entire route should overlap at its last (2nd) index\", 2,\n                longerRoute.overlapIndex(shorterRouteAtEnd));\n        Assert.assertEquals(\"The entire route should overlap at its first (0th) index\", 0,\n                longerRoute.overlapIndex(startOnlyRoute));\n        Assert.assertEquals(\"The entire route should overlap at its middle (1st) index\", 1,\n                longerRoute.overlapIndex(middleOnlyRoute));\n        Assert.assertEquals(\"The entire route should overlap at its last (2nd) index\", 2,\n                longerRoute.overlapIndex(endOnlyRoute));\n        Assert.assertEquals(\"No overlap should happen\", -1,\n                startOnlyRoute.overlapIndex(shorterRouteAtEnd));\n        Assert.assertEquals(\"No overlap should happen\", -1,\n                shorterRouteAtEnd.overlapIndex(startOnlyRoute));\n        Assert.assertEquals(\"No overlap should happen\", -1,\n                longerRoute.overlapIndex(divergingRoute));\n    }\n\n    @Test\n    public void testReverse()\n    {\n        final Atlas biDirectionalAtlas = this.rule.getBiDirectionalEdgeAtlas();\n\n        final Route singleBiDirectionalEdgeRoute = Route\n                .forEdge(biDirectionalAtlas.edge(159019301));\n        Assert.assertEquals(\"There is a reverse route since it's a bi-directional edge\",\n                Route.forEdge(biDirectionalAtlas.edge(-159019301)),\n                singleBiDirectionalEdgeRoute.reverse().get());\n\n        final Route multipleBirectionalEdgeRoute = Route.forEdges(\n                biDirectionalAtlas.edge(159019301), biDirectionalAtlas.edge(28620796),\n                biDirectionalAtlas.edge(138620888));\n        Assert.assertEquals(\"There is a reverse route since all the edges are bi-directional\",\n                Route.forEdges(biDirectionalAtlas.edge(-138620888),\n                        biDirectionalAtlas.edge(-28620796), biDirectionalAtlas.edge(-159019301)),\n                multipleBirectionalEdgeRoute.reverse().get());\n\n        final Atlas uniDirectionalAtlas = this.rule.getUniDirectionalEdgeAtlas();\n        final Route singleUniDirectionalEdgeRoute = Route\n                .forEdge(uniDirectionalAtlas.edge(28620796));\n        Assert.assertFalse(\"There is no reverse route since this is a one-way edge\",\n                singleUniDirectionalEdgeRoute.reverse().isPresent());\n\n        final Route routeWithUniDirectionalEdgeInBetween = Route.forEdges(\n                uniDirectionalAtlas.edge(159019301), uniDirectionalAtlas.edge(28620796),\n                uniDirectionalAtlas.edge(138620888));\n        Assert.assertFalse(\n                \"There is no reverse route since the middle edge is a uni-directional edge\",\n                routeWithUniDirectionalEdgeInBetween.reverse().isPresent());\n    }\n\n    @Test(expected = CoreException.class)\n    public void testRoute()\n    {\n        this.route.append(this.atlas.edge(-9));\n        Assert.fail(\"Should not build non connected route\");\n    }\n\n    @Test\n    public void testSimpleUTurn()\n    {\n        final Atlas atlas = this.rule.getUTurnAtlas();\n        final Route uTurnRoute1 = Route.forEdges(atlas.edge(159019301), atlas.edge(-159019301));\n        final Route uTurnRoute2 = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751),\n                atlas.edge(128620796), atlas.edge(-128620796), atlas.edge(-128620751),\n                atlas.edge(-159019301));\n\n        final Route nonUTurnRoute1 = Route.forEdges(atlas.edge(159019301));\n        final Route nonUTurnRoute2 = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751),\n                atlas.edge(128620796), atlas.edge(138620888));\n        final Route nonUTurnRoute3 = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751),\n                atlas.edge(128620796), atlas.edge(-128620796), atlas.edge(-128620751),\n                atlas.edge(138620889));\n\n        Assert.assertTrue(\"Valid basic 2 edge UTurn is a simple UTurn\",\n                uTurnRoute1.isSimpleUTurn());\n        Assert.assertTrue(\n                \"Valid complex UTurn with even number of edges in route is a simple UTurn\",\n                uTurnRoute2.isSimpleUTurn());\n\n        Assert.assertFalse(\"Route with odd number of edges cannot be a simple UTurn\",\n                nonUTurnRoute1.isSimpleUTurn());\n        Assert.assertFalse(\"Non UTurn route cannot be a simple UTurn\",\n                nonUTurnRoute2.isSimpleUTurn());\n        Assert.assertFalse(\n                \"A route that is almost a UTurn (last edge is off), cannot be a simple UTurn\",\n                nonUTurnRoute3.isSimpleUTurn());\n    }\n\n    @Test\n    public void testStartsWith()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n\n        final Route route = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751),\n                atlas.edge(128620796));\n        final Route duplicateRoute = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751),\n                atlas.edge(128620796));\n        final Route multiRoute = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751));\n        final Route singleRoute = Route.forEdges(atlas.edge(159019301));\n        final Route incorrectMultiRoute = Route.forEdges(atlas.edge(159019301),\n                atlas.edge(128620751), atlas.edge(138620888));\n        final Route incorrectSingleRoute = Route.forEdge(atlas.edge(128620751));\n\n        Assert.assertTrue(\"Verify startsWith returns true when passing in the same route\",\n                route.startsWith(duplicateRoute));\n\n        Assert.assertTrue(\"Verify startsWith returns true for a multiRoute\",\n                route.startsWith(multiRoute));\n        Assert.assertTrue(\"Verify startsWith returns true for a singleRoute\",\n                route.startsWith(singleRoute));\n\n        Assert.assertFalse(\"Verify a startsWith negative case for a multiRoute\",\n                route.startsWith(incorrectMultiRoute));\n        Assert.assertFalse(\"Verify a startsWith negative case for a singleRoute\",\n                route.startsWith(incorrectSingleRoute));\n\n        Assert.assertFalse(\n                \"Verify startsWith returns false when other route is longer than current route\",\n                singleRoute.startsWith(multiRoute));\n    }\n\n    @Test\n    public void testSubRoutes()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Route shorterRoute = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751));\n        final Route longerRoute = Route.forEdges(atlas.edge(159019301), atlas.edge(128620751),\n                atlas.edge(128620796));\n        final Route partialLongerRoute = Route.forEdges(atlas.edge(128620751),\n                atlas.edge(128620796));\n        final Route middleEdgeRoute = Route.forEdge(atlas.edge(128620751));\n\n        // SubRouteIndex and isSubRoute tests\n        Assert.assertEquals(\"Longer route cannot be a subroute of the a shorter one\", -1,\n                shorterRoute.subRouteIndex(longerRoute));\n        Assert.assertFalse(shorterRoute.isSubRoute(longerRoute));\n\n        Assert.assertEquals(\n                \"Shorter route is a subroute of the longer one, with the last overlap at index 1\",\n                1, longerRoute.subRouteIndex(shorterRoute));\n        Assert.assertTrue(longerRoute.isSubRoute(shorterRoute));\n\n        // subRoute tests\n        Assert.assertEquals(\"Subroute the last edge from a route\", shorterRoute,\n                longerRoute.subRoute(0, shorterRoute.size()));\n\n        Assert.assertEquals(\"Subroute the first edge from a route\", partialLongerRoute,\n                longerRoute.subRoute(1, longerRoute.size()));\n\n        Assert.assertEquals(\"Subroute a middle edge from a route\", middleEdgeRoute,\n                longerRoute.subRoute(1, 2));\n\n        Assert.assertEquals(\"Subroute the entire route\", longerRoute,\n                longerRoute.subRoute(0, longerRoute.size()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/RouteTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * {@link RouteTest} test data.\n *\n * @author mgostintsev\n */\npublic class RouteTestRule extends CoreTestRule\n{\n    private static final String ONE = \"39.9970447, 116.279489\";\n    private static final String TWO = \"39.9974907, 116.2835146\";\n    private static final String THREE = \"39.9976118, 116.2836054\";\n    private static final String FOUR = \"40.0007062, 116.2829521\";\n    private static final String FIVE = \"40.0082021, 116.2824999\";\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n\n            }, edges = {\n\n                    @Edge(id = \"159019301\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"128620751\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=tertiary_link\" }),\n                    @Edge(id = \"128620796\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=tertiary\", \"oneway=yes\" }),\n                    @Edge(id = \"138620888\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FIVE) }, tags = { \"highway=tertiary\", \"oneway=yes\" }) })\n    private Atlas atlas;\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            }, edges = {\n\n                    @Edge(id = \"159019301\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=tertiary_link\" }),\n                    @Edge(id = \"28620796\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=tertiary\", }),\n                    @Edge(id = \"-28620796\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = TWO) }, tags = { \"highway=tertiary\", }),\n                    @Edge(id = \"138620888\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=tertiary\", }),\n                    @Edge(id = \"-138620888\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = THREE) }, tags = { \"highway=tertiary\", }) })\n    private Atlas biDirectionalEdgeAtlas;\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            }, edges = {\n\n                    @Edge(id = \"159019301\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=tertiary_link\" }),\n                    @Edge(id = \"28620796\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=tertiary\", }),\n                    @Edge(id = \"138620888\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=tertiary\", }),\n                    @Edge(id = \"-138620888\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = THREE) }, tags = { \"highway=tertiary\", }) })\n    private Atlas uniDirectionalEdgeAtlas;\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n\n            }, edges = {\n\n                    @Edge(id = \"206786592000008\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-206786592000008\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"206786592000007\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = ONE) }, tags = { \"highway=tertiary_link\" }),\n                    @Edge(id = \"-206786592000007\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = THREE) }, tags = { \"highway=tertiary_link\" }) })\n    private Atlas routeHashCodeAtlas;\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n\n            }, edges = {\n\n                    @Edge(id = \"159019301\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"-159019301\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"128620751\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=tertiary_link\" }),\n                    @Edge(id = \"-128620751\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = TWO) }, tags = { \"highway=tertiary_link\" }),\n                    @Edge(id = \"128620796\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=tertiary\", \"oneway=yes\" }),\n                    @Edge(id = \"-128620796\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = THREE) }, tags = { \"highway=tertiary\", \"oneway=yes\" }),\n                    @Edge(id = \"138620888\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = FIVE) }, tags = { \"highway=tertiary\", \"oneway=yes\" }),\n                    @Edge(id = \"-138620888\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = FOUR) }, tags = { \"highway=tertiary\", \"oneway=yes\" }),\n                    @Edge(id = \"138620889\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = FIVE) }, tags = { \"highway=tertiary\", \"oneway=yes\" }) })\n\n    private Atlas uTurnAtlas;\n\n    @TestAtlas(points = {\n            @Point(id = \"1\", coordinates = @Loc(value = Location.TEST_3_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"2\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"3\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"4\", coordinates = @Loc(value = Location.TEST_7_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"5\", coordinates = @Loc(value = Location.TEST_4_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"6\", coordinates = @Loc(value = Location.TEST_1_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"7\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }), },\n\n            nodes = {\n                    @Node(id = \"123\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"1234\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"12345\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                            \"highway=turning_circle\" }), },\n\n            lines = {\n                    @Line(id = \"32\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES) }, tags = { \"power=line\" }),\n                    @Line(id = \"23\", coordinates = { @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES) }, tags = {\n                                    \"aeroway=runway\" }),\n                    @Line(id = \"24\", coordinates = { @Loc(value = Location.TEST_7_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"natural=coastline\", \"waterway=canal\" }) },\n\n            edges = {\n                    @Edge(id = \"9\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_5_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge9\", \"surface=concrete\", \"lanes=3\" }),\n                    @Edge(id = \"-9\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge_9\", \"surface=fine_gravel\" }),\n                    @Edge(id = \"98\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"highway=secondary\", \"name=edge98\", \"bridge=movable\",\n                                    \"maxspeed=100\" }),\n                    @Edge(id = \"987\", coordinates = { @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"highway=residential\", \"name=edge987\", \"tunnel=culvert\",\n                                    \"maxspeed=50 knots\" }) },\n\n            areas = {\n                    @Area(id = \"45\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"leisure=golf_course\", \"natural=grassland\" }),\n                    @Area(id = \"54\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"leisure=swimming_pool\", \"sport=swimming\" }),\n                    @Area(id = \"4554\", coordinates = { @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES) }, tags = {\n                                    \"hello=world\" }) },\n\n            relations = {\n                    @Relation(id = \"1\", members = { @Member(id = \"9\", role = \"in\", type = \"edge\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\"),\n                            @Member(id = \"-9\", role = \"out\", type = \"edge\") }, tags = {}),\n                    @Relation(id = \"2\", members = {\n                            @Member(id = \"45\", role = \"area\", type = \"area\"),\n                            @Member(id = \"32\", role = \"line\", type = \"line\"),\n                            @Member(id = \"5\", role = \"pt\", type = \"point\"),\n                            @Member(id = \"1\", role = \"rel\", type = \"relation\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\") }, tags = {}) })\n    private Atlas packedAtlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    public Atlas getBiDirectionalEdgeAtlas()\n    {\n        return this.biDirectionalEdgeAtlas;\n    }\n\n    public Atlas getPackedAtlas()\n    {\n        return this.packedAtlas;\n    }\n\n    public Atlas getRouteHashCodeAtlas()\n    {\n        return this.routeHashCodeAtlas;\n    }\n\n    public Atlas getUTurnAtlas()\n    {\n        return this.uTurnAtlas;\n    }\n\n    public Atlas getUniDirectionalEdgeAtlas()\n    {\n        return this.uniDirectionalEdgeAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/SnappedLineItemTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * @author chunzc\n */\npublic class SnappedLineItemTest\n{\n    @Rule\n    public final SnappedLineItemTestRule rule = new SnappedLineItemTestRule();\n\n    @Test\n    public void testEquals()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final Location point = atlas.point(1L).getLocation();\n        final LineItem targetLineItem = atlas.lineItems(item -> item.getIdentifier() == 8000000L)\n                .iterator().next();\n        final SnappedLineItem candidate = new SnappedLineItem(\n                point.snapTo(targetLineItem.asPolyLine()), targetLineItem);\n        final List<SnappedLineItem> snappedLineItemList = atlas\n                .snapsLineItem(atlas.node(1L).getLocation(), Distance.meters(100));\n        final SnappedLineItem snappedLineItem = snappedLineItemList\n                .stream().filter(lineItem -> lineItem.getLineItem()\n                        .getIdentifier() == targetLineItem.getIdentifier())\n                .collect(Collectors.toList()).get(0);\n        Assert.assertTrue(snappedLineItemList.contains(candidate));\n        Assert.assertEquals(candidate, snappedLineItem);\n        Assert.assertEquals(candidate.hashCode(), snappedLineItem.hashCode());\n        Assert.assertNotEquals(targetLineItem, snappedLineItem);\n        Assert.assertNotEquals(null, snappedLineItem);\n        Assert.assertNotEquals(snappedLineItemList.get(0), snappedLineItemList.get(1));\n    }\n\n    @Test\n    public void testToString()\n    {\n        final String targetString = \"[SnappedLineItem: LineItem: 8000000, Origin: POINT (-61.336198 15.420563), Snap: POINT (-61.336198 15.420563)]\";\n        final Atlas atlas = this.rule.getAtlas();\n        final Location point = atlas.point(1L).getLocation();\n        final LineItem targetLineItem = atlas.lineItems(item -> item.getIdentifier() == 8000000L)\n                .iterator().next();\n        final SnappedLineItem candidate = new SnappedLineItem(\n                point.snapTo(targetLineItem.asPolyLine()), targetLineItem);\n        Assert.assertEquals(targetString, candidate.toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/SnappedLineItemTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * The Geojson representation of the below test atlas files is saved in the test/resources folder,\n * same package\n *\n * @author matthieun\n */\npublic class SnappedLineItemTestRule extends CoreTestRule\n{\n    // Inside 12-1350-1870\n    private static final String ONE = \"15.420563,-61.336198\";\n    private static final String TWO = \"15.429499,-61.332850\";\n    private static final String TWO_BIS = \"15.3907,-61.3112\";\n\n    // Inside 12-1350-1869\n    private static final String THREE = \"15.4855,-61.3041\";\n    private static final String FOUR = \"15.4809,-61.3366\";\n\n    // Inside 12-1349-1869\n    private static final String FIVE = \"15.4852,-61.3816\";\n    private static final String SIX = \"15.4781,-61.3949\";\n\n    // Inside 12-1349-1870\n    private static final String SEVEN = \"15.4145,-61.3826\";\n    private static final String EIGHT = \"15.4073,-61.3749\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @TestAtlas.Node(id = \"1\", coordinates = @TestAtlas.Loc(value = ONE)),\n                    @TestAtlas.Node(id = \"2\", coordinates = @TestAtlas.Loc(value = TWO)),\n                    @TestAtlas.Node(id = \"3\", coordinates = @TestAtlas.Loc(value = THREE)),\n                    @TestAtlas.Node(id = \"4\", coordinates = @TestAtlas.Loc(value = FOUR)),\n                    @TestAtlas.Node(id = \"5\", coordinates = @TestAtlas.Loc(value = FIVE)),\n                    @TestAtlas.Node(id = \"6\", coordinates = @TestAtlas.Loc(value = SIX)),\n                    @TestAtlas.Node(id = \"7\", coordinates = @TestAtlas.Loc(value = SEVEN)),\n                    @TestAtlas.Node(id = \"8\", coordinates = @TestAtlas.Loc(value = EIGHT))\n\n            },\n\n            edges = {\n\n                    @TestAtlas.Edge(id = \"1000000\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @TestAtlas.Edge(id = \"-1000000\", coordinates = { @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = ONE) }, tags = { \"highway=secondary\" }),\n                    @TestAtlas.Edge(id = \"2000000\", coordinates = { @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = THREE) }, tags = { \"highway=secondary\" }),\n                    @TestAtlas.Edge(id = \"3000000\", coordinates = { @TestAtlas.Loc(value = THREE),\n                            @TestAtlas.Loc(value = FOUR) }, tags = { \"highway=secondary\" }),\n                    @TestAtlas.Edge(id = \"4000000\", coordinates = { @TestAtlas.Loc(value = FOUR),\n                            @TestAtlas.Loc(value = FIVE) }, tags = { \"highway=secondary\" }),\n                    @TestAtlas.Edge(id = \"5000000\", coordinates = { @TestAtlas.Loc(value = FIVE),\n                            @TestAtlas.Loc(value = SIX) }, tags = { \"highway=secondary\" }),\n                    @TestAtlas.Edge(id = \"6000000\", coordinates = { @TestAtlas.Loc(value = SIX),\n                            @TestAtlas.Loc(value = SEVEN) }, tags = { \"highway=secondary\" }),\n                    @TestAtlas.Edge(id = \"7000000\", coordinates = { @TestAtlas.Loc(value = SEVEN),\n                            @TestAtlas.Loc(value = EIGHT) }, tags = { \"highway=secondary\" }),\n                    @TestAtlas.Edge(id = \"8000000\", coordinates = { @TestAtlas.Loc(value = EIGHT),\n                            @TestAtlas.Loc(value = ONE) }, tags = { \"highway=secondary\" })\n\n            },\n\n            areas = {\n\n                    @TestAtlas.Area(id = \"1\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = TWO_BIS) }, tags = { \"landuse=residential\" }),\n                    @TestAtlas.Area(id = \"2\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = THREE) }, tags = { \"landuse=residential\" })\n\n            },\n\n            lines = {\n\n                    @TestAtlas.Line(id = \"1\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = TWO_BIS) }, tags = { \"power=line\" }),\n                    @TestAtlas.Line(id = \"2\", coordinates = { @TestAtlas.Loc(value = ONE),\n                            @TestAtlas.Loc(value = TWO),\n                            @TestAtlas.Loc(value = THREE) }, tags = { \"power=line\" })\n\n            },\n\n            points = {\n\n                    @TestAtlas.Point(id = \"1\", coordinates = @TestAtlas.Loc(value = ONE)),\n                    @TestAtlas.Point(id = \"2\", coordinates = @TestAtlas.Loc(value = TWO)),\n                    @TestAtlas.Point(id = \"3\", coordinates = @TestAtlas.Loc(value = THREE)),\n                    @TestAtlas.Point(id = \"4\", coordinates = @TestAtlas.Loc(value = FOUR)),\n                    @TestAtlas.Point(id = \"5\", coordinates = @TestAtlas.Loc(value = FIVE)),\n                    @TestAtlas.Point(id = \"6\", coordinates = @TestAtlas.Loc(value = SIX)),\n                    @TestAtlas.Point(id = \"7\", coordinates = @TestAtlas.Loc(value = SEVEN)),\n                    @TestAtlas.Point(id = \"8\", coordinates = @TestAtlas.Loc(value = EIGHT))\n\n            },\n\n            relations = {\n\n                    @TestAtlas.Relation(id = \"1\", tags = { \"type=relation\" }, members = {\n\n                            @TestAtlas.Relation.Member(id = \"1000000\", role = \"a\", type = \"edge\"),\n                            @TestAtlas.Relation.Member(id = \"3000000\", role = \"b\", type = \"edge\")\n\n                    }),\n\n                    @TestAtlas.Relation(id = \"2\", tags = { \"type=relation\" }, members = {\n\n                            @TestAtlas.Relation.Member(id = \"8\", role = \"a\", type = \"point\"),\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"b\", type = \"area\")\n\n                    }),\n\n                    @TestAtlas.Relation(id = \"3\", tags = { \"type=relation\" }, members = {\n\n                            @TestAtlas.Relation.Member(id = \"5000000\", role = \"a\", type = \"edge\"),\n                            @TestAtlas.Relation.Member(id = \"6000000\", role = \"b\", type = \"edge\"),\n                            @TestAtlas.Relation.Member(id = \"1\", role = \"c\", type = \"area\")\n\n                    })\n\n            }\n\n    )\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/WithinTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Located;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * @author Yazad Khambata\n */\npublic class WithinTest\n{\n\n    public static final int NUMBER_POINTS = 5;\n\n    @Rule\n    public WithinTestRule containableTestRule = new WithinTestRule();\n\n    @Test\n    public void testAtlasEntities()\n    {\n        final long identifier = WithinTestRule.ID;\n\n        final Atlas atlas = this.containableTestRule.getAtlas();\n\n        Assert.assertTrue(atlas.node(identifier).within(rectThree()));\n        Assert.assertTrue(atlas.edge(identifier).within(rectThree()));\n        Assert.assertTrue(atlas.area(identifier).within(rectThree()));\n        Assert.assertTrue(atlas.relation(identifier).within(rectThree()));\n\n        Assert.assertFalse(atlas.node(identifier).within(rectTwo()));\n        Assert.assertFalse(atlas.edge(identifier).within(rectTwo()));\n        Assert.assertFalse(atlas.area(identifier).within(rectTwo()));\n        Assert.assertFalse(atlas.relation(identifier).within(rectTwo()));\n    }\n\n    @Test(expected = UnsupportedOperationException.class)\n    public void testDefaultBehavior()\n    {\n        ((Located) () -> null).within(null);\n    }\n\n    @Test\n    public void testLocationIsWithin()\n    {\n        final GeometricSurface surface = rectOne();\n\n        final Location center = ((Rectangle) surface).center();\n\n        Assert.assertTrue(center.within(surface));\n\n        Assert.assertTrue(((Rectangle) surface).lowerLeft().within(surface));\n        Assert.assertTrue(((Rectangle) surface).upperRight().within(surface));\n\n        Assert.assertFalse(\n                new Location(Latitude.degrees(50), Longitude.degrees(50)).within(surface));\n    }\n\n    @Test\n    public void testPolyLineIsWithin()\n    {\n        final GeometricSurface surface1 = rectOne();\n\n        final GeometricSurface surface2 = rectTwo();\n\n        final PolyLine polyLine1 = PolyLine.random(NUMBER_POINTS, (Rectangle) surface1);\n\n        final PolyLine polyLine2 = PolyLine.random(NUMBER_POINTS, (Rectangle) surface2);\n\n        Assert.assertTrue(polyLine1.within(surface1));\n\n        Assert.assertTrue(polyLine2.within(surface2));\n\n        Assert.assertFalse(polyLine1.within(surface2));\n        Assert.assertFalse(polyLine2.within(surface1));\n    }\n\n    @Test\n    public void testPolygonIsWithin()\n    {\n        final GeometricSurface surface1 = rectOne();\n        final GeometricSurface surface2 = rectTwo();\n\n        final Polygon polygon1 = Polygon.random(NUMBER_POINTS, (Rectangle) surface1);\n        final Polygon polygon2 = Polygon.random(NUMBER_POINTS, (Rectangle) surface2);\n\n        Assert.assertTrue(polygon1.within(surface1));\n        Assert.assertTrue(polygon2.within(surface2));\n\n        Assert.assertFalse(polygon1.within(surface2));\n        Assert.assertFalse(polygon2.within(surface1));\n    }\n\n    private GeometricSurface rectOne()\n    {\n        final Location lowerLeft = new Location(Latitude.degrees(10), Longitude.degrees(10));\n        final Location upperRight = new Location(Latitude.degrees(20), Longitude.degrees(20));\n\n        return Rectangle.forCorners(lowerLeft, upperRight);\n    }\n\n    private GeometricSurface rectThree()\n    {\n        final Location lowerLeft = new Location(Latitude.degrees(10), Longitude.degrees(100));\n        final Location upperRight = new Location(Latitude.degrees(20), Longitude.degrees(150));\n\n        return Rectangle.forCorners(lowerLeft, upperRight);\n    }\n\n    private GeometricSurface rectTwo()\n    {\n        final Location lowerLeft2 = new Location(Latitude.degrees(-20), Longitude.degrees(-20));\n        final Location upperRight2 = new Location(Latitude.degrees(-10), Longitude.degrees(-10));\n\n        return Rectangle.forCorners(lowerLeft2, upperRight2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/WithinTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author Yazad Khambata\n */\npublic class WithinTestRule extends CoreTestRule\n{\n\n    private static final String ID_STR = \"1\";\n    public static final Long ID = Long.valueOf(ID_STR);\n\n    private static final String NODE = \"12.6559437,116.9700514\";\n\n    private static final String EDGE_1 = \"10.1883391,149.4092916\";\n    private static final String EDGE_2 = \"19.9880592,137.0582643\";\n    private static final String EDGE_3 = \"13.4555177,148.4934186\";\n\n    private static final String AREA_1 = \"19.7435337,122.1343008\";\n    private static final String AREA_2 = \"10.7633736,112.9680864\";\n    private static final String AREA_3 = \"16.3182722,138.9217453\";\n    private static final String AREA_4 = \"11.535563,135.6781525\";\n    private static final String AREA_5 = \"19.7435337,122.1343008\";\n    private static final String AREA_6 = \"17.9736782,111.9741523\";\n    private static final String AREA_7 = AREA_1;\n\n    @TestAtlas(\n\n            nodes = {\n                    @TestAtlas.Node(id = WithinTestRule.ID_STR, coordinates = @TestAtlas.Loc(value = NODE)),\n\n                    @TestAtlas.Node(id = \"2\", coordinates = @TestAtlas.Loc(value = EDGE_1)),\n                    @TestAtlas.Node(id = \"3\", coordinates = @TestAtlas.Loc(value = EDGE_2)),\n                    @TestAtlas.Node(id = \"4\", coordinates = @TestAtlas.Loc(value = EDGE_3)),\n\n                    @TestAtlas.Node(id = \"5\", coordinates = @TestAtlas.Loc(value = AREA_1)),\n                    @TestAtlas.Node(id = \"6\", coordinates = @TestAtlas.Loc(value = AREA_2)),\n                    @TestAtlas.Node(id = \"7\", coordinates = @TestAtlas.Loc(value = AREA_3)),\n                    @TestAtlas.Node(id = \"8\", coordinates = @TestAtlas.Loc(value = AREA_4)),\n                    @TestAtlas.Node(id = \"9\", coordinates = @TestAtlas.Loc(value = AREA_5)),\n                    @TestAtlas.Node(id = \"10\", coordinates = @TestAtlas.Loc(value = AREA_6)),\n                    @TestAtlas.Node(id = \"11\", coordinates = @TestAtlas.Loc(value = AREA_7)) }, edges = {\n                            @TestAtlas.Edge(id = \"1\", coordinates = {\n                                    @TestAtlas.Loc(value = EDGE_1), @TestAtlas.Loc(value = EDGE_2),\n                                    @TestAtlas.Loc(value = EDGE_3) }) }, areas = {\n                                            @TestAtlas.Area(id = \"1\", coordinates = {\n                                                    @TestAtlas.Loc(value = AREA_1),\n                                                    @TestAtlas.Loc(value = AREA_2),\n                                                    @TestAtlas.Loc(value = AREA_3),\n                                                    @TestAtlas.Loc(value = AREA_4),\n                                                    @TestAtlas.Loc(value = AREA_5),\n                                                    @TestAtlas.Loc(value = AREA_6),\n                                                    @TestAtlas.Loc(value = AREA_7) }) }, relations = {\n                                                            @TestAtlas.Relation(id = \"1\", members = {\n                                                                    @TestAtlas.Relation.Member(id = \"1\", role = \"some role 1\", type = \"node\"),\n                                                                    @TestAtlas.Relation.Member(id = \"1\", role = \"some role 2\", type = \"edge\"),\n                                                                    @TestAtlas.Relation.Member(id = \"1\", role = \"some role 3\", type = \"area\"), }) })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/RelationOrAreaToMultiPolygonConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\n\n/**\n * Unit tests for {@link RelationOrAreaToMultiPolygonConverter}.\n *\n * @author bbreithaupt\n */\npublic class RelationOrAreaToMultiPolygonConverterTest\n{\n    public static final RelationOrAreaToMultiPolygonConverter CONVERTER = new RelationOrAreaToMultiPolygonConverter();\n\n    @Rule\n    public RelationOrAreaToMultiPolygonConverterTestRule setup = new RelationOrAreaToMultiPolygonConverterTestRule();\n\n    @Test\n    // Test an outer ring inside an inner ring inside an outer ring\n    public void innerOuterMultiPolygonTest()\n    {\n        final MultiPolygon multiPolygon = CONVERTER\n                .convert(this.setup.innerOuterMultiPolygonAtlas().relation(1447306000000L));\n        Assert.assertEquals(2, multiPolygon.outers().size());\n        // Both inner rings should be mapped to one of the outer rings\n        Assert.assertTrue(\n                multiPolygon.outers().stream().map(outer -> multiPolygon.innersOf(outer).size())\n                        .allMatch(count -> count == 0 || count == 2));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/RelationOrAreaToMultiPolygonConverterTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Unit test rules for {@link RelationOrAreaToMultiPolygonConverter}.\n *\n * @author bbreithaupt\n */\npublic class RelationOrAreaToMultiPolygonConverterTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"InnerOuterMultiPolygon.osm\")\n    private Atlas innerOuterMultiPolygonAtlas;\n\n    public Atlas innerOuterMultiPolygonAtlas()\n    {\n        return this.innerOuterMultiPolygonAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/RelationToMultiPolygonMemberConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation.Ring;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * @author matthieun\n */\npublic class RelationToMultiPolygonMemberConverterTest\n{\n    private static final RelationToMultiPolygonMemberConverter INNER = new RelationToMultiPolygonMemberConverter(\n            Ring.INNER);\n    private static final RelationToMultiPolygonMemberConverter OUTER = new RelationToMultiPolygonMemberConverter(\n            Ring.OUTER);\n\n    private static final Location ONE = Location.TEST_6;\n    private static final Location TWO = Location.TEST_2;\n    private static final Location THR = Location.TEST_1;\n    private static final Location FOR = Location.TEST_5;\n    private static final Location FVE = Location.TEST_4;\n\n    private static final Polygon OUTER_LOOP = new Polygon(ONE, TWO, THR, FOR, FVE);\n\n    private static final Location SIX = Location.TEST_4;\n    private static final Location SVN = Location.TEST_7;\n    private static final Location EIT = Location.TEST_2;\n\n    private static final Polygon INNER_LOOP = new Polygon(SIX, SVN, EIT);\n\n    private Atlas atlas;\n\n    @Before\n    public void init()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addLine(0, new PolyLine(ONE, TWO), Maps.hashMap());\n        builder.addLine(1, new PolyLine(TWO, THR), Maps.hashMap());\n        builder.addLine(2, new PolyLine(THR, FOR), Maps.hashMap());\n        builder.addLine(3, new PolyLine(FOR, FVE), Maps.hashMap());\n        builder.addLine(4, new PolyLine(FVE, ONE), Maps.hashMap());\n\n        builder.addLine(5, new PolyLine(SIX, SVN), Maps.hashMap());\n        builder.addLine(6, new PolyLine(SVN, EIT), Maps.hashMap());\n        builder.addLine(7, new PolyLine(EIT, SIX), Maps.hashMap());\n\n        builder.addArea(0, INNER_LOOP, Maps.hashMap());\n\n        final RelationBean bean = new RelationBean();\n\n        bean.addItem(0L, RelationTypeTag.MULTIPOLYGON_ROLE_OUTER, ItemType.AREA);\n\n        bean.addItem(0L, RelationTypeTag.MULTIPOLYGON_ROLE_OUTER, ItemType.LINE);\n        bean.addItem(1L, RelationTypeTag.MULTIPOLYGON_ROLE_OUTER, ItemType.LINE);\n        bean.addItem(2L, RelationTypeTag.MULTIPOLYGON_ROLE_OUTER, ItemType.LINE);\n        bean.addItem(3L, RelationTypeTag.MULTIPOLYGON_ROLE_OUTER, ItemType.LINE);\n        bean.addItem(4L, RelationTypeTag.MULTIPOLYGON_ROLE_OUTER, ItemType.LINE);\n\n        bean.addItem(5L, RelationTypeTag.MULTIPOLYGON_ROLE_INNER, ItemType.LINE);\n        bean.addItem(6L, RelationTypeTag.MULTIPOLYGON_ROLE_INNER, ItemType.LINE);\n        bean.addItem(7L, RelationTypeTag.MULTIPOLYGON_ROLE_INNER, ItemType.LINE);\n\n        builder.addRelation(0, 0, bean,\n                Maps.hashMap(RelationTypeTag.KEY, RelationTypeTag.MULTIPOLYGON_TYPE));\n\n        this.atlas = builder.get();\n    }\n\n    @Test\n    public void testRings()\n    {\n        final List<Polygon> outers = Iterables.asList(OUTER.convert(this.atlas.relation(0)));\n        Assert.assertTrue(outers.stream().map(Polygon::length).collect(Collectors.toList())\n                .contains(OUTER_LOOP.length()));\n        Assert.assertTrue(outers.stream().map(Polygon::length).collect(Collectors.toList())\n                .contains(INNER_LOOP.length()));\n        Assert.assertEquals(INNER_LOOP.length(),\n                INNER.convert(this.atlas.relation(0)).iterator().next().length());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/aoi/ComplexAreaOfInterestFinderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.aoi;\n\nimport java.util.stream.StreamSupport;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\n\n/**\n * Test cases for {@link ComplexAreaOfInterestFinder}\n *\n * @author sayas01\n */\npublic class ComplexAreaOfInterestFinderTest\n{\n    @Rule\n    public ComplexAreaOfInterestFinderTestRule rule = new ComplexAreaOfInterestFinderTestRule();\n\n    @Test\n    public void testAOIArea()\n    {\n        final Atlas atlas = this.rule.getAoiAreaAtlas();\n        final ComplexAreaOfInterestFinder aoiRelationFinder = new ComplexAreaOfInterestFinder();\n        final Iterable<ComplexAreaOfInterest> complexAOIAreas = aoiRelationFinder.find(atlas);\n        Assert.assertEquals(2, StreamSupport.stream(complexAOIAreas.spliterator(), false).count());\n    }\n\n    @Test\n    public void testBuildingAOIRelation()\n    {\n        final Atlas atlas = this.rule.getBuildingAOIRelationAtlas();\n        final ComplexAreaOfInterestFinder aoiRelationFinder = new ComplexAreaOfInterestFinder();\n        final Iterable<ComplexAreaOfInterest> complexAOIRelations = aoiRelationFinder.find(atlas);\n        Assert.assertEquals(1,\n                StreamSupport.stream(complexAOIRelations.spliterator(), false).count());\n    }\n\n    @Test\n    public void testComplexAOIWithCustomFilter()\n    {\n        final Atlas atlas = this.rule.getComplexAOIWithRelationsAndAreas();\n        final ComplexAreaOfInterestFinder aoiRelationFinder = new ComplexAreaOfInterestFinder();\n        final Iterable<ComplexAreaOfInterest> complexAOIs = aoiRelationFinder.find(atlas,\n                TaggableFilter.forDefinition(\"landuse->VINEYARD|amenity->SCHOOL\"));\n        Assert.assertEquals(3, StreamSupport.stream(complexAOIs.spliterator(), false).count());\n    }\n\n    @Test\n    public void testMultipolygonAOIRelation()\n    {\n        final Atlas atlas = this.rule.getMultipolygonAOIRelationAtlas();\n        final ComplexAreaOfInterestFinder aoiRelationFinder = new ComplexAreaOfInterestFinder();\n        final Iterable<ComplexAreaOfInterest> complexAOIRelations = aoiRelationFinder.find(atlas);\n        Assert.assertEquals(2,\n                StreamSupport.stream(complexAOIRelations.spliterator(), false).count());\n    }\n\n    @Test\n    public void testNonMultipolygonAOIRelation()\n    {\n        final Atlas atlas = this.rule.getNonMultipolygonAOIRelationAtlas();\n        final ComplexAreaOfInterestFinder aoiRelationFinder = new ComplexAreaOfInterestFinder();\n        final Iterable<ComplexAreaOfInterest> complexAOIRelations = aoiRelationFinder.find(atlas);\n        Assert.assertFalse(complexAOIRelations.iterator().hasNext());\n        Assert.assertEquals(0,\n                StreamSupport.stream(complexAOIRelations.spliterator(), false).count());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/aoi/ComplexAreaOfInterestFinderTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.aoi;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * Test data for {@link ComplexAreaOfInterestFinderTest}\n *\n * @author sayas01\n */\npublic class ComplexAreaOfInterestFinderTestRule extends CoreTestRule\n{\n    private static final String LOCATION_ONE = \"1.43958970913, 103.91306306772\";\n    private static final String LOCATION_TWO = \"1.42773565932, 103.90488839864\";\n    private static final String LOCATION_THREE = \"1.43419031086, 103.89654055711\";\n    private static final String LOCATION_FOUR = \"1.41407426416, 103.89600156794\";\n    private static final String LOCATION_FIVE = \"1.40850639889, 103.91621366183\";\n    private static final String LOCATION_SIX = \"1.39835848123, 103.92322052105\";\n    private static final String LOCATION_SEVEN = \"1.41146994174, 103.94549874009\";\n    private static final String LOCATION_EIGHT = \"1.41155974601, 103.96373454036\";\n    private static final String LOCATION_NINE = \"1.43419031086, 103.97038207346\";\n    private static final String LOCATION_TEN = \"1.45116308784, 103.96454302412\";\n    private static final String LOCATION_ELEVEN = \"1.45233052286, 103.93786306018\";\n    private static final String LOCATION_TWELVE = \"1.45367756252, 103.91845945004\";\n    private static final String LOCATION_THIRTEEN = \"1.41193016458, 103.91459020371\";\n    private static final String LOCATION_FOURTEEN = \"1.41624076477, 103.94180915682\";\n    private static final String LOCATION_FIFTEEN = \"1.42638860433, 103.94621090171\";\n    private static final String LOCATION_SIXTEEN = \"1.43141760561, 103.96399754433\";\n    private static final String LOCATION_SEVENTEEN = \"1.44273281808, 103.95447540232\";\n    private static final String LOCATION_EIGHTEEN = \"1.43842226756, 103.93857522179\";\n    private static final String LOCATION_NINETEEN = \"1.44839040324, 103.9256394817\";\n    private static final String LOCATION_TWENTY = \"1.4507140742, 103.9069610144\";\n\n    @TestAtlas(points = { @Point(id = \"39008\", coordinates = @Loc(value = LOCATION_TWENTY)),\n            @Point(id = \"39009\", coordinates = @Loc(value = LOCATION_THREE)),\n            @Point(id = \"39011\", coordinates = @Loc(value = LOCATION_FOUR)),\n            @Point(id = \"39013\", coordinates = @Loc(value = LOCATION_FIVE)),\n            @Point(id = \"39015\", coordinates = @Loc(value = LOCATION_SIX)),\n            @Point(id = \"38985\", coordinates = @Loc(value = LOCATION_ONE)),\n            @Point(id = \"39019\", coordinates = @Loc(value = LOCATION_EIGHT)),\n            @Point(id = \"38988\", coordinates = @Loc(value = LOCATION_TWO)),\n            @Point(id = \"39021\", coordinates = @Loc(value = LOCATION_NINE)),\n            @Point(id = \"39023\", coordinates = @Loc(value = LOCATION_TEN)),\n            @Point(id = \"38992\", coordinates = @Loc(value = LOCATION_THIRTEEN)),\n            @Point(id = \"39025\", coordinates = @Loc(value = LOCATION_ELEVEN)),\n            @Point(id = \"38994\", coordinates = @Loc(value = LOCATION_FOURTEEN)),\n            @Point(id = \"39027\", coordinates = @Loc(value = LOCATION_TWELVE)),\n            @Point(id = \"38996\", coordinates = @Loc(value = LOCATION_FIFTEEN)),\n            @Point(id = \"38998\", coordinates = @Loc(value = LOCATION_SIXTEEN)),\n            @Point(id = \"39017\", coordinates = @Loc(value = LOCATION_SEVEN)),\n            @Point(id = \"39000\", coordinates = @Loc(value = LOCATION_SEVENTEEN)),\n            @Point(id = \"39002\", coordinates = @Loc(value = LOCATION_EIGHTEEN)),\n            @Point(id = \"39004\", coordinates = @Loc(value = LOCATION_NINETEEN)) }, areas = {\n                    @Area(id = \"39010\", coordinates = { @Loc(value = LOCATION_TWENTY),\n                            @Loc(value = LOCATION_THREE), @Loc(value = LOCATION_FOUR),\n                            @Loc(value = LOCATION_FIVE), @Loc(value = LOCATION_SIX),\n                            @Loc(value = LOCATION_SEVEN), @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE), @Loc(value = LOCATION_TEN),\n                            @Loc(value = LOCATION_ELEVEN), @Loc(value = LOCATION_TWELVE),\n                            @Loc(value = LOCATION_TWENTY) }),\n                    @Area(id = \"38989\", coordinates = { @Loc(value = LOCATION_ONE),\n                            @Loc(value = LOCATION_TWO), @Loc(value = LOCATION_THIRTEEN),\n                            @Loc(value = LOCATION_FOURTEEN), @Loc(value = LOCATION_FIFTEEN),\n                            @Loc(value = LOCATION_SIXTEEN), @Loc(value = LOCATION_SEVENTEEN),\n                            @Loc(value = LOCATION_EIGHTEEN), @Loc(value = LOCATION_NINETEEN),\n                            @Loc(value = LOCATION_ONE) }),\n                    @Area(id = \"38987\", coordinates = { @Loc(value = LOCATION_ELEVEN),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_EIGHTEEN),\n                            @Loc(value = LOCATION_SEVENTEEN) }) }, relations = {\n                                    @Relation(id = \"39190\", members = {\n                                            @Member(id = \"39010\", type = \"area\", role = \"outer\"),\n                                            @Member(id = \"38989\", type = \"area\", role = \"inner\") }, tags = {\n                                                    \"type=multipolygon\",\n                                                    \"amenity=SCHOOL\" }, wkt = \"MULTIPOLYGON (((103.906961 1.4507141, 103.8965406 1.4341903, 103.8960016 1.4140743, 103.9162137 1.4085064, 103.9232205 1.3983585, 103.9454987 1.4114699, 103.9637345 1.4115597, 103.9703821 1.4341903, 103.964543 1.4511631, 103.9378631 1.4523305, 103.9184595 1.4536776, 103.906961 1.4507141, 103.906961 1.4507141), (103.9130631 1.4395897, 103.9048884 1.4277357, 103.9145902 1.4119302, 103.9418092 1.4162408, 103.9462109 1.4263886, 103.9639975 1.4314176, 103.9544754 1.4427328, 103.9385752 1.4384223, 103.9256395 1.4483904, 103.9130631 1.4395897, 103.9130631 1.4395897)))\"),\n                                    @Relation(id = \"39990\", members = {\n                                            @Member(id = \"38987\", type = \"area\", role = \"outer\") }, tags = {\n                                                    \"type=boundary\",\n                                                    \"landuse=CEMETERY\" }, wkt = \"MULTIPOLYGON (((103.9378631 1.4523305, 103.964543 1.4511631, 103.9385752 1.4384223, 103.9544754 1.4427328, 103.9378631 1.4523305)))\") })\n    private Atlas multipolygonAOIRelationAtlas;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @Node(id = \"10020\", coordinates = @Loc(value = LOCATION_SEVEN)),\n                    @Node(id = \"21001\", coordinates = @Loc(value = LOCATION_EIGHT)),\n                    @Node(id = \"31233\", coordinates = @Loc(value = LOCATION_NINE)) },\n            // edges\n            edges = {\n                    @Edge(id = \"12333\", coordinates = { @Loc(value = LOCATION_SEVEN),\n                            @Loc(value = LOCATION_EIGHT) }, tags = { \"highway=road\" }),\n                    @Edge(id = \"23332\", coordinates = { @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE) }, tags = { \"highway=road\" }),\n                    @Edge(id = \"31223\", coordinates = { @Loc(value = LOCATION_NINE),\n                            @Loc(value = LOCATION_SEVEN) }, tags = { \"highway=road\" }) },\n            // relations\n            relations = { @Relation(id = \"89765\", members = {\n                    @Member(id = \"12333\", type = \"edge\", role = RelationTypeTag.RESTRICTION_ROLE_FROM),\n                    @Member(id = \"21001\", type = \"node\", role = RelationTypeTag.RESTRICTION_ROLE_VIA),\n                    @Member(id = \"31223\", type = \"edge\", role = RelationTypeTag.RESTRICTION_ROLE_TO) }, tags = {\n                            \"restriction=no_u_turn\", \"landuse=VILLAGE\" }) })\n    private Atlas nonMultipolygonAOIRelationAtlas;\n\n    @TestAtlas(points = { @Point(id = \"39008\", coordinates = @Loc(value = LOCATION_TWENTY)),\n            @Point(id = \"39009\", coordinates = @Loc(value = LOCATION_THREE)),\n            @Point(id = \"39011\", coordinates = @Loc(value = LOCATION_FOUR)),\n            @Point(id = \"39013\", coordinates = @Loc(value = LOCATION_FIVE)),\n            @Point(id = \"39015\", coordinates = @Loc(value = LOCATION_SIX)),\n            @Point(id = \"38985\", coordinates = @Loc(value = LOCATION_ONE)),\n            @Point(id = \"39019\", coordinates = @Loc(value = LOCATION_EIGHT)),\n            @Point(id = \"38988\", coordinates = @Loc(value = LOCATION_TWO)),\n            @Point(id = \"39021\", coordinates = @Loc(value = LOCATION_NINE)),\n            @Point(id = \"39023\", coordinates = @Loc(value = LOCATION_TEN)),\n            @Point(id = \"38992\", coordinates = @Loc(value = LOCATION_THIRTEEN)),\n            @Point(id = \"39025\", coordinates = @Loc(value = LOCATION_ELEVEN)),\n            @Point(id = \"38994\", coordinates = @Loc(value = LOCATION_FOURTEEN)),\n            @Point(id = \"39027\", coordinates = @Loc(value = LOCATION_TWELVE)),\n            @Point(id = \"38996\", coordinates = @Loc(value = LOCATION_FIFTEEN)),\n            @Point(id = \"38998\", coordinates = @Loc(value = LOCATION_SIXTEEN)),\n            @Point(id = \"39017\", coordinates = @Loc(value = LOCATION_SEVEN)),\n            @Point(id = \"39000\", coordinates = @Loc(value = LOCATION_SEVENTEEN)),\n            @Point(id = \"39002\", coordinates = @Loc(value = LOCATION_EIGHTEEN)),\n            @Point(id = \"39004\", coordinates = @Loc(value = LOCATION_NINETEEN)) }, areas = {\n                    @Area(id = \"39010\", coordinates = { @Loc(value = LOCATION_TWENTY),\n                            @Loc(value = LOCATION_THREE), @Loc(value = LOCATION_FOUR),\n                            @Loc(value = LOCATION_FIVE), @Loc(value = LOCATION_SIX),\n                            @Loc(value = LOCATION_SEVEN), @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE), @Loc(value = LOCATION_TEN),\n                            @Loc(value = LOCATION_ELEVEN), @Loc(value = LOCATION_TWELVE),\n                            @Loc(value = LOCATION_TWENTY) }),\n                    @Area(id = \"38989\", coordinates = { @Loc(value = LOCATION_ONE),\n                            @Loc(value = LOCATION_TWO), @Loc(value = LOCATION_THIRTEEN),\n                            @Loc(value = LOCATION_FOURTEEN), @Loc(value = LOCATION_FIFTEEN),\n                            @Loc(value = LOCATION_SIXTEEN), @Loc(value = LOCATION_SEVENTEEN),\n                            @Loc(value = LOCATION_EIGHTEEN), @Loc(value = LOCATION_NINETEEN),\n                            @Loc(value = LOCATION_ONE) }, tags = \"leisure=PARK\"),\n                    @Area(id = \"38987\", coordinates = { @Loc(value = LOCATION_ELEVEN),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_EIGHTEEN),\n                            @Loc(value = LOCATION_SEVENTEEN) }, tags = \"tourism=ZOO\") })\n    private Atlas aoiAreaAtlas;\n\n    @TestAtlas(points = { @Point(id = \"39008\", coordinates = @Loc(value = LOCATION_TWENTY)),\n            @Point(id = \"39009\", coordinates = @Loc(value = LOCATION_THREE)),\n            @Point(id = \"39011\", coordinates = @Loc(value = LOCATION_FOUR)),\n            @Point(id = \"39013\", coordinates = @Loc(value = LOCATION_FIVE)),\n            @Point(id = \"39015\", coordinates = @Loc(value = LOCATION_SIX)),\n            @Point(id = \"38985\", coordinates = @Loc(value = LOCATION_ONE)),\n            @Point(id = \"39019\", coordinates = @Loc(value = LOCATION_EIGHT)),\n            @Point(id = \"38988\", coordinates = @Loc(value = LOCATION_TWO)),\n            @Point(id = \"39021\", coordinates = @Loc(value = LOCATION_NINE)),\n            @Point(id = \"39023\", coordinates = @Loc(value = LOCATION_TEN)),\n            @Point(id = \"38992\", coordinates = @Loc(value = LOCATION_THIRTEEN)),\n            @Point(id = \"39025\", coordinates = @Loc(value = LOCATION_ELEVEN)),\n            @Point(id = \"38994\", coordinates = @Loc(value = LOCATION_FOURTEEN)),\n            @Point(id = \"39027\", coordinates = @Loc(value = LOCATION_TWELVE)),\n            @Point(id = \"38996\", coordinates = @Loc(value = LOCATION_FIFTEEN)),\n            @Point(id = \"38998\", coordinates = @Loc(value = LOCATION_SIXTEEN)),\n            @Point(id = \"39017\", coordinates = @Loc(value = LOCATION_SEVEN)),\n            @Point(id = \"39000\", coordinates = @Loc(value = LOCATION_SEVENTEEN)),\n            @Point(id = \"39002\", coordinates = @Loc(value = LOCATION_EIGHTEEN)),\n            @Point(id = \"39004\", coordinates = @Loc(value = LOCATION_NINETEEN)) }, areas = {\n                    @Area(id = \"39010\", coordinates = { @Loc(value = LOCATION_TWENTY),\n                            @Loc(value = LOCATION_THREE), @Loc(value = LOCATION_FOUR),\n                            @Loc(value = LOCATION_FIVE), @Loc(value = LOCATION_SIX),\n                            @Loc(value = LOCATION_SEVEN), @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE), @Loc(value = LOCATION_TEN),\n                            @Loc(value = LOCATION_ELEVEN), @Loc(value = LOCATION_TWELVE),\n                            @Loc(value = LOCATION_TWENTY) }),\n                    @Area(id = \"38989\", coordinates = { @Loc(value = LOCATION_ONE),\n                            @Loc(value = LOCATION_TWO), @Loc(value = LOCATION_THIRTEEN),\n                            @Loc(value = LOCATION_FOURTEEN), @Loc(value = LOCATION_FIFTEEN),\n                            @Loc(value = LOCATION_SIXTEEN), @Loc(value = LOCATION_SEVENTEEN),\n                            @Loc(value = LOCATION_EIGHTEEN), @Loc(value = LOCATION_NINETEEN),\n                            @Loc(value = LOCATION_ONE) }),\n                    @Area(id = \"38987\", coordinates = { @Loc(value = LOCATION_ELEVEN),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_EIGHTEEN),\n                            @Loc(value = LOCATION_SEVENTEEN) }),\n                    @Area(id = \"45677\", coordinates = { @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_FIVE),\n                            @Loc(value = LOCATION_FOUR) }, tags = {\n                                    \"amenity=SCHOOL\" }) }, relations = {\n                                            @Relation(id = \"39190\", members = {\n                                                    @Member(id = \"39010\", type = \"area\", role = \"outer\"),\n                                                    @Member(id = \"38989\", type = \"area\", role = \"inner\") }, tags = {\n                                                            \"type=multipolygon\",\n                                                            \"amenity=PARKING\" }, wkt = \"MULTIPOLYGON (((103.906961 1.4507141, 103.8965406 1.4341903, 103.8960016 1.4140743, 103.9162137 1.4085064, 103.9232205 1.3983585, 103.9454987 1.4114699, 103.9637345 1.4115597, 103.9703821 1.4341903, 103.964543 1.4511631, 103.9378631 1.4523305, 103.9184595 1.4536776, 103.906961 1.4507141, 103.906961 1.4507141), (103.9130631 1.4395897, 103.9048884 1.4277357, 103.9145902 1.4119302, 103.9418092 1.4162408, 103.9462109 1.4263886, 103.9639975 1.4314176, 103.9544754 1.4427328, 103.9385752 1.4384223, 103.9256395 1.4483904, 103.9130631 1.4395897, 103.9130631 1.4395897)))\"),\n                                            @Relation(id = \"39990\", members = {\n                                                    @Member(id = \"38987\", type = \"area\", role = \"outer\") }, tags = {\n                                                            \"type=boundary\",\n                                                            \"landuse=VINEYARD\" }, wkt = \"MULTIPOLYGON (((103.9378631 1.4523305, 103.964543 1.4511631, 103.9385752 1.4384223, 103.9544754 1.4427328, 103.9378631 1.4523305)))\") })\n    private Atlas complexAOIWithRelationsAndAreas;\n\n    @TestAtlas(points = { @Point(id = \"39008\", coordinates = @Loc(value = LOCATION_TWENTY)),\n            @Point(id = \"39009\", coordinates = @Loc(value = LOCATION_THREE)),\n            @Point(id = \"39011\", coordinates = @Loc(value = LOCATION_FOUR)),\n            @Point(id = \"39013\", coordinates = @Loc(value = LOCATION_FIVE)),\n            @Point(id = \"39015\", coordinates = @Loc(value = LOCATION_SIX)),\n            @Point(id = \"38985\", coordinates = @Loc(value = LOCATION_ONE)),\n            @Point(id = \"39019\", coordinates = @Loc(value = LOCATION_EIGHT)),\n            @Point(id = \"38988\", coordinates = @Loc(value = LOCATION_TWO)),\n            @Point(id = \"39021\", coordinates = @Loc(value = LOCATION_NINE)),\n            @Point(id = \"39023\", coordinates = @Loc(value = LOCATION_TEN)),\n            @Point(id = \"38992\", coordinates = @Loc(value = LOCATION_THIRTEEN)),\n            @Point(id = \"39025\", coordinates = @Loc(value = LOCATION_ELEVEN)),\n            @Point(id = \"38994\", coordinates = @Loc(value = LOCATION_FOURTEEN)),\n            @Point(id = \"39027\", coordinates = @Loc(value = LOCATION_TWELVE)),\n            @Point(id = \"38996\", coordinates = @Loc(value = LOCATION_FIFTEEN)),\n            @Point(id = \"38998\", coordinates = @Loc(value = LOCATION_SIXTEEN)),\n            @Point(id = \"39017\", coordinates = @Loc(value = LOCATION_SEVEN)),\n            @Point(id = \"39000\", coordinates = @Loc(value = LOCATION_SEVENTEEN)),\n            @Point(id = \"39002\", coordinates = @Loc(value = LOCATION_EIGHTEEN)),\n            @Point(id = \"39004\", coordinates = @Loc(value = LOCATION_NINETEEN)) }, areas = {\n                    @Area(id = \"39010\", coordinates = { @Loc(value = LOCATION_TWENTY),\n                            @Loc(value = LOCATION_THREE), @Loc(value = LOCATION_FOUR),\n                            @Loc(value = LOCATION_FIVE), @Loc(value = LOCATION_SIX),\n                            @Loc(value = LOCATION_SEVEN), @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE), @Loc(value = LOCATION_TEN),\n                            @Loc(value = LOCATION_ELEVEN), @Loc(value = LOCATION_TWELVE),\n                            @Loc(value = LOCATION_TWENTY) }),\n                    @Area(id = \"38989\", coordinates = { @Loc(value = LOCATION_ONE),\n                            @Loc(value = LOCATION_TWO), @Loc(value = LOCATION_THIRTEEN),\n                            @Loc(value = LOCATION_FOURTEEN), @Loc(value = LOCATION_FIFTEEN),\n                            @Loc(value = LOCATION_SIXTEEN), @Loc(value = LOCATION_SEVENTEEN),\n                            @Loc(value = LOCATION_EIGHTEEN), @Loc(value = LOCATION_NINETEEN),\n                            @Loc(value = LOCATION_ONE) }),\n                    @Area(id = \"38987\", coordinates = { @Loc(value = LOCATION_ELEVEN),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_EIGHTEEN),\n                            @Loc(value = LOCATION_SEVENTEEN) }) }, relations = {\n                                    @Relation(id = \"39190\", members = {\n                                            @Member(id = \"39010\", type = \"area\", role = \"outer\"),\n                                            @Member(id = \"38989\", type = \"area\", role = \"inner\") }, tags = {\n                                                    \"type=building\",\n                                                    \"amenity=SCHOOL\" }, wkt = \"MULTIPOLYGON (((103.906961 1.4507141, 103.8965406 1.4341903, 103.8960016 1.4140743, 103.9162137 1.4085064, 103.9232205 1.3983585, 103.9454987 1.4114699, 103.9637345 1.4115597, 103.9703821 1.4341903, 103.964543 1.4511631, 103.9378631 1.4523305, 103.9184595 1.4536776, 103.906961 1.4507141, 103.906961 1.4507141), (103.9130631 1.4395897, 103.9048884 1.4277357, 103.9145902 1.4119302, 103.9418092 1.4162408, 103.9462109 1.4263886, 103.9639975 1.4314176, 103.9544754 1.4427328, 103.9385752 1.4384223, 103.9256395 1.4483904, 103.9130631 1.4395897, 103.9130631 1.4395897)))\") })\n    private Atlas buildingAOIRelationAtlas;\n\n    public Atlas getAoiAreaAtlas()\n    {\n        return this.aoiAreaAtlas;\n    }\n\n    public Atlas getBuildingAOIRelationAtlas()\n    {\n        return this.buildingAOIRelationAtlas;\n    }\n\n    public Atlas getComplexAOIWithRelationsAndAreas()\n    {\n        return this.complexAOIWithRelationsAndAreas;\n    }\n\n    public Atlas getMultipolygonAOIRelationAtlas()\n    {\n        return this.multipolygonAOIRelationAtlas;\n    }\n\n    public Atlas getNonMultipolygonAOIRelationAtlas()\n    {\n        return this.nonMultipolygonAOIRelationAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/BigNodeFinderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.bignode;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasLoadingCommand;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNode.Type;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.writers.JsonWriter;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Sid\n * @author mgostintsev\n */\npublic class BigNodeFinderTest extends AtlasLoadingCommand\n{\n    private static final Logger logger = LoggerFactory.getLogger(BigNodeFinderTest.class);\n\n    @Rule\n    public BigNodeFinderTestCaseRule setup = new BigNodeFinderTestCaseRule();\n\n    public static void main(final String[] args)\n    {\n        new BigNodeFinderTest().run(args);\n    }\n\n    @Test\n    public void testBigNodeCandidateComaprator()\n    {\n        final Atlas atlas = this.setup.getAtlas();\n        final BigNodeFinder.NodeComparator comparator = new BigNodeFinder.NodeComparator();\n        final Node node1 = atlas.node(1);\n        final Node node2 = atlas.node(2);\n        final Node node3 = atlas.node(3);\n        final Set<Node> set1 = new TreeSet<>(comparator);\n        set1.add(node1);\n        set1.add(node2);\n        final Set<Node> set2 = new TreeSet<>(comparator);\n        set2.add(node2);\n        set2.add(node1);\n        final Set<Node> set3 = new TreeSet<>(comparator);\n        set3.add(node3);\n        set3.add(node1);\n        final BigNodeFinder.BigNodeCandidate candidate1 = BigNodeFinder.BigNodeCandidate.from(set1);\n        final BigNodeFinder.BigNodeCandidate candidate2 = BigNodeFinder.BigNodeCandidate.from(set2);\n        final BigNodeFinder.BigNodeCandidate candidate3 = BigNodeFinder.BigNodeCandidate.from(set3);\n        Assert.assertEquals(0, candidate1.compareTo(candidate2));\n        Assert.assertEquals(-1, candidate2.compareTo(candidate3));\n        Assert.assertEquals(1, candidate3.compareTo(candidate1));\n    }\n\n    @Test\n    public void testBigNodeExpansion()\n    {\n        final Atlas atlas = this.setup.getExpandBigNodeAtlas();\n        final List<BigNode> bigNodes = Iterables.asList(new BigNodeFinder().find(atlas));\n        bigNodes.forEach(complexEntity -> logger.info(\"{}\", complexEntity.toString()));\n        logger.info(\"Total Number of big Nodes :{}\", bigNodes.size());\n        final Set<BigNode> dualCarriageWayBigNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.DUAL_CARRIAGEWAY))\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Number of Dual Carrriage Way Big Nodes\", 1,\n                dualCarriageWayBigNodes.size());\n        final Long[] expectedDualCarriageWayNodeIdentifiers = { 97800560000000L, 268842854000000L,\n                97800351000000L, 268842855000000L };\n        final Set<Long> dualCarriageWayNodes = dualCarriageWayBigNodes.stream()\n                .flatMap(bigNode -> bigNode.nodes().stream()).map(node -> node.getIdentifier())\n                .collect(Collectors.toSet());\n        Arrays.stream(expectedDualCarriageWayNodeIdentifiers)\n                .forEach(nodeId -> Assert.assertTrue(dualCarriageWayNodes.contains(nodeId)));\n    }\n\n    @Test\n    public void testBigNodeOverlap()\n    {\n        final Atlas atlas = this.setup.getOverlapAtlas();\n        logger.info(\"Atlas: {}\", atlas);\n        final List<BigNode> bigNodes = Iterables.asList(new BigNodeFinder().find(atlas));\n        bigNodes.forEach(complexEntity -> logger.info(\"{}\", complexEntity.toString()));\n        Assert.assertEquals(\"Expect to find 20 Big Nodes for overlap atlas\", 20, bigNodes.size());\n\n        final Set<BigNode> dualCarriageWayBigNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.DUAL_CARRIAGEWAY))\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Number of Dual Carrriage Way Big Nodes\", 1,\n                dualCarriageWayBigNodes.size());\n\n        final Long[] expectedDualCarriageWayNodeIdentifiers = { 4886012997000000L,\n                4886012998000000L, 1029583978000000L, 60382597000000L, 1029583896000000L,\n                60382598000000L, 4886062964000000L, 4886062965000000L, 4878996907000000L,\n                4879025626000000L };\n\n        final Set<Long> dualCarriageWayNodes = dualCarriageWayBigNodes.stream()\n                .flatMap(bigNode -> bigNode.nodes().stream()).map(node -> node.getIdentifier())\n                .collect(Collectors.toSet());\n        Arrays.stream(expectedDualCarriageWayNodeIdentifiers)\n                .forEach(nodeId -> Assert.assertTrue(dualCarriageWayNodes.contains(nodeId)));\n    }\n\n    @Test\n    public void testConfigurableSearchRadius()\n    {\n        final Map<String, Distance> configurableRadius = new HashMap<>();\n        configurableRadius.put(HighwayTag.MOTORWAY.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.TRUNK.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.PRIMARY.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.SECONDARY.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.TERTIARY.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.RESIDENTIAL.name().toLowerCase(), Distance.meters(5));\n\n        final Atlas atlas = this.setup.getOverMergeAtlas();\n        logger.info(\"Atlas: {}\", atlas);\n        final List<BigNode> bigNodes = Iterables\n                .asList(new BigNodeFinder(configurableRadius, null, null).find(atlas));\n        final Set<BigNode> dualCarriageWayBigNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.DUAL_CARRIAGEWAY))\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Expect to find 2 Dual Carriageway Big Node for this atlas\", 2,\n                dualCarriageWayBigNodes.size());\n        final Set<Long> dualCarriageWayNodes = dualCarriageWayBigNodes.stream()\n                .flatMap(bigNode -> bigNode.nodes().stream()).map(node -> node.getIdentifier())\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Expect to find 7 Dual Carriageway Sub Nodes\", 7,\n                dualCarriageWayNodes.size());\n    }\n\n    @Test\n    public void testDualCarriageWayMerging()\n    {\n        logger.info(\"Atlas: {}\", this.setup.getAtlas());\n        final List<BigNode> bigNodes = Iterables\n                .asList(new BigNodeFinder().find(this.setup.getAtlas()));\n        bigNodes.forEach(complexEntity -> logger.info(\"{}\", complexEntity.toString()));\n        logger.info(\"Total Number of big Nodes :{}\", bigNodes.size());\n        Assert.assertEquals(\"Total Number of big Nodes (Simple + Dual Carrriage Way)\", 66,\n                bigNodes.size());\n        final Set<BigNode> dualCarriageWayBigNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.DUAL_CARRIAGEWAY))\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Number of Dual Carrriage Way Big Nodes\", 12,\n                dualCarriageWayBigNodes.size());\n        final Set<BigNode> simpleNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.SIMPLE))\n                .collect(Collectors.toSet());\n\n        final Long[] expectedDualCarriageWayNodeIdentifiers = { 2L, 4L, 5L, 6L, 13L, 14L, 15L, 20L,\n                21L, 22L, 23L, 32L, 33L, 39L, 40L, 41L, 42L, 43L, 44L, 45L, 46L, 49L, 50L, 52L, 53L,\n                56L, 57L, 59L, 60L, 79L, 80L };\n\n        final Set<Long> dualCarriageWayNodes = dualCarriageWayBigNodes.stream()\n                .flatMap(bigNode -> bigNode.nodes().stream()).map(node -> node.getIdentifier())\n                .collect(Collectors.toSet());\n        Arrays.stream(expectedDualCarriageWayNodeIdentifiers)\n                .forEach(nodeId -> Assert.assertTrue(dualCarriageWayNodes.contains(nodeId)));\n\n        // Node Identifiers where bigNodes shouldn't be added\n        final Long[] expectedSimpleNodeIdentifiers = { 62L, 63L, 88L, 89L };\n        final Set<Long> expectedSimpleNodeIdentifiersSet = new HashSet<>(\n                Arrays.asList(expectedSimpleNodeIdentifiers));\n        dualCarriageWayBigNodes.forEach(bigNode -> bigNode.nodes().forEach(node -> Assert\n                .assertFalse(expectedSimpleNodeIdentifiersSet.contains(node.getIdentifier()))));\n\n        // Node Identifiers where not even simple nodes shouldn't be added\n        final Long[] expectedPierNodeIdentifiers = { 66L, 67L, 68L, 69L };\n        final Set<Long> expectedPierNodeIdentifiersSet = new HashSet<>(\n                Arrays.asList(expectedPierNodeIdentifiers));\n        dualCarriageWayBigNodes.forEach(bigNode -> bigNode.nodes().forEach(node -> Assert\n                .assertFalse(expectedPierNodeIdentifiersSet.contains(node.getIdentifier()))));\n        simpleNodes.forEach(bigNode -> bigNode.nodes().forEach(node -> Assert\n                .assertFalse(expectedPierNodeIdentifiersSet.contains(node.getIdentifier()))));\n    }\n\n    @Test\n    public void testExcludeLinkRoadAsDualCarriageWayDNK()\n    {\n        final Atlas atlas = this.setup.getDNKAtlasToTestExcludeLinkRoadAsDualCarriageWay();\n        logger.info(\"Atlas: {}\", atlas);\n        final List<BigNode> bigNodes = Iterables.asList(new BigNodeFinder().find(atlas));\n        final Set<BigNode> dualCarriageWayBigNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.DUAL_CARRIAGEWAY))\n                .collect(Collectors.toSet());\n        dualCarriageWayBigNodes.forEach(bigNode -> logger.info(\"{}\", bigNode.toString()));\n        Assert.assertEquals(\"Expect to find 1 Dual Carriageway Big Node for this atlas\", 1,\n                dualCarriageWayBigNodes.size());\n        final Set<Long> dualCarriageWayNodes = dualCarriageWayBigNodes.stream()\n                .flatMap(bigNode -> bigNode.nodes().stream()).map(node -> node.getIdentifier())\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Expect to find 6 Dual Carriageway Sub Nodes\", 6,\n                dualCarriageWayNodes.size());\n        final long[] nodeIdentfiers = { 29971835000000L, 29971833000000L, 6378459073000000L,\n                6378459072000000L, 6378459074000000L, 6378459067000000L };\n        Arrays.asList(nodeIdentfiers).forEach(nodeIdentifier -> Assert\n                .assertFalse(dualCarriageWayNodes.contains(nodeIdentifier)));\n    }\n\n    @Test\n    public void testExcludeLinkRoadAsDualCarriageWayUKR()\n    {\n        final Atlas atlas = this.setup.getUKRAtlasToTestExcludeLinkRoadAsDualCarriageWay();\n        logger.info(\"Atlas: {}\", atlas);\n        final List<BigNode> bigNodes = Iterables.asList(new BigNodeFinder().find(atlas));\n        final Set<BigNode> dualCarriageWayBigNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.DUAL_CARRIAGEWAY))\n                .collect(Collectors.toSet());\n        dualCarriageWayBigNodes.forEach(bigNode -> logger.info(\"{}\", bigNode.toString()));\n        Assert.assertEquals(\"Expect to find 0 Dual Carriageway Big Node for this atlas\", 0,\n                dualCarriageWayBigNodes.size());\n        Assert.assertEquals(\"Expect to find 31 simple big nodes\", 31, bigNodes.size());\n    }\n\n    @Test\n    public void testHasJunctionEdgeTags()\n    {\n        final Map<String, Distance> configurableRadius = new HashMap<>();\n        configurableRadius.put(HighwayTag.MOTORWAY.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.TRUNK.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.PRIMARY.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.SECONDARY.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.TERTIARY.name().toLowerCase(), Distance.meters(10));\n        configurableRadius.put(HighwayTag.RESIDENTIAL.name().toLowerCase(), Distance.meters(5));\n\n        final Map<String, String> nonJunctionEdgeTagMap = new HashMap<>();\n        nonJunctionEdgeTagMap.put(\"test.way\", \"CROSSWALK\");\n\n        final Atlas atlas = this.setup.getOverMergeAtlas();\n        logger.info(\"Atlas: {}\", atlas);\n        final List<BigNode> bigNodes = Iterables.asList(\n                new BigNodeFinder(configurableRadius, nonJunctionEdgeTagMap, null).find(atlas));\n        final Set<BigNode> dualCarriageWayBigNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.DUAL_CARRIAGEWAY))\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Expect to find 1 Dual Carriageway Big Node for this atlas\", 1,\n                dualCarriageWayBigNodes.size());\n        final Set<Long> dualCarriageWayNodes = dualCarriageWayBigNodes.stream()\n                .flatMap(bigNode -> bigNode.nodes().stream()).map(node -> node.getIdentifier())\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Expect to find 4 Dual Carriageway Sub Nodes\", 4,\n                dualCarriageWayNodes.size());\n    }\n\n    @Test\n    public void testNonStraightLongRoute()\n    {\n        final Atlas atlas = this.setup.getAtlas();\n        final List<Edge> edges = new ArrayList<>();\n        edges.add(atlas.edge(101102));\n        edges.add(atlas.edge(102103));\n        edges.add(atlas.edge(103104));\n        edges.add(atlas.edge(104105));\n        edges.add(atlas.edge(105106));\n        edges.add(atlas.edge(106107));\n        final Route route = Route.forEdges(edges);\n\n        final Map<String, String> configurationMap = new HashMap<>();\n        configurationMap.put(BigNodeFinder.LONG_JUNCTION_ROUTE_LENGTH_KEY, \"100\");\n        configurationMap.put(BigNodeFinder.NON_STRAIGHT_JUNCTION_EDGES_ANGLE_KEY, \"80\");\n        final BigNodeFinder finder = new BigNodeFinder(null, null, configurationMap);\n        Assert.assertTrue(finder.isStraightLongRoute(route));\n        edges.add(atlas.edge(107108));\n        final Route route2 = Route.forEdges(edges);\n        Assert.assertFalse(finder.isStraightLongRoute(route2));\n    }\n\n    @Test\n    public void testOverMergeBigNodes()\n    {\n        final Atlas atlas = this.setup.getOverMergeAtlas();\n        logger.info(\"Atlas: {}\", atlas);\n        final List<BigNode> bigNodes = Iterables.asList(new BigNodeFinder().find(atlas));\n        final Set<BigNode> dualCarriageWayBigNodes = bigNodes.stream()\n                .filter(bigNode -> bigNode.getType().equals(Type.DUAL_CARRIAGEWAY))\n                .collect(Collectors.toSet());\n        dualCarriageWayBigNodes.forEach(bigNode -> logger.info(\"{}\", bigNode.toString()));\n        Assert.assertEquals(\"Expect to find 1 Dual Carriageway Big Node for this atlas\", 1,\n                dualCarriageWayBigNodes.size());\n        final Set<Long> dualCarriageWayNodes = dualCarriageWayBigNodes.stream()\n                .flatMap(bigNode -> bigNode.nodes().stream()).map(node -> node.getIdentifier())\n                .collect(Collectors.toSet());\n        Assert.assertEquals(\"Expect to find 13 Dual Carriageway Sub Nodes\", 13,\n                dualCarriageWayNodes.size());\n        final Long[] overMergeNodeIdentifiers = { 4862123767000000L, 1186028310000000L,\n                1951545347000000L, 4931063979000000L };\n        Arrays.asList(overMergeNodeIdentifiers).forEach(nodeIdentifier -> Assert\n                .assertFalse(dualCarriageWayNodes.contains(nodeIdentifier)));\n    }\n\n    @Test\n    public void testPathsThroughComplexJunction()\n    {\n        final Atlas atlas = this.setup.getComplexJunctionAtlas();\n        logger.info(\"Atlas: {}\", atlas);\n        final List<BigNode> bigNodes = Iterables.asList(new BigNodeFinder().find(atlas));\n        bigNodes.forEach(complexEntity -> logger.info(\"{}\", complexEntity.toString()));\n        Assert.assertEquals(\"Expect to find 9 Big Nodes for this atlas\", 9, bigNodes.size());\n\n        Time timeNow = Time.now();\n        final Set<Route> shortestRoutes = new HashSet<>();\n        bigNodes.forEach(bigNode -> shortestRoutes.addAll(bigNode.shortestPaths()));\n        logger.info(\"Took {} to find {} shortest big node routes\", timeNow.elapsedSince(),\n                shortestRoutes.size());\n\n        logger.info(\"Big Node Shortest Routes: {} \", shortestRoutes);\n        Assert.assertEquals(\"Expect to find 16 shortest paths through these Big Nodes\", 16,\n                shortestRoutes.size());\n\n        timeNow = Time.now();\n        final Set<Route> allRoutes = new HashSet<>();\n\n        bigNodes.forEach(bigNode -> allRoutes.addAll(bigNode.allPaths()));\n        logger.info(\"Took {} to find all {} big node routes\", timeNow.elapsedSince(),\n                allRoutes.size());\n\n        logger.info(\"Big Node All Routes: {} \", allRoutes);\n        Assert.assertEquals(\"Expect to find 28 total paths through these Big Nodes\", 28,\n                allRoutes.size());\n\n        Assert.assertTrue(\"Make sure the shortest routes are a subset of allRoutes\",\n                allRoutes.containsAll(shortestRoutes));\n\n        final Route nonShortestValidRoute = Route.forEdges(atlas.edge(4), atlas.edge(11),\n                atlas.edge(-12), atlas.edge(-9), atlas.edge(3));\n        Assert.assertTrue(\"Valid route should be absent from the shortest path set\",\n                !shortestRoutes.contains(nonShortestValidRoute));\n        Assert.assertTrue(\"Valid route should be present in the total path set\",\n                allRoutes.contains(nonShortestValidRoute));\n    }\n\n    @Test\n    public void testUTurnShapeEdgeAsJunctionEdge()\n    {\n        final Atlas atlas = this.setup.getuTurnShapeEdgeAtlas();\n        final Edge edge1 = atlas.edge(798542598000001L);\n        final Edge edge2 = atlas.edge(798542598000002L);\n        final BigNodeFinder finder = new BigNodeFinder();\n        Assert.assertFalse(finder.startAndEndNodesConnectedToSameEdge(edge1));\n        Assert.assertTrue(finder.startAndEndNodesConnectedToSameEdge(edge2));\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final File folder = (File) command.get(INPUT_FOLDER);\n        final Atlas atlas = loadAtlas(command);\n\n        final File bigNodesFile = folder.child(\"improved.bigNodes.geojson\");\n        final JsonWriter writer = new JsonWriter(bigNodesFile);\n        writer.write(new GeoJsonBuilder().create(\n                Iterables.translate(new BigNodeFinder().find(atlas), BigNode::asGeoJsonBigNode))\n                .jsonObject());\n        writer.close();\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/BigNodeFinderTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.bignode;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * @author Sid\n * @author mgostintsev\n **/\npublic class BigNodeFinderTestCaseRule extends CoreTestRule\n{\n    /**\n     * 1. The atlas is a two dual carriage way intersection in DOM\n     */\n    private static final String ONE = \"19.2202841, -70.5305208\";\n    private static final String TWO = \"19.2203577, -70.5309603\";\n    private static final String THREE = \"19.2201964, -70.5305353\";\n    private static final String FOUR = \"19.2202682, -70.5309606\";\n    private static final String FIVE = \"19.2203771, -70.5310792\";\n    private static final String SIX = \"19.2202916, -70.5310805\";\n    private static final String SEVEN = \"19.2215199, -70.5307007\";\n    private static final String EIGHT = \"19.2215288, -70.5307861\";\n    private static final String NINE = \"19.2205727, -70.5322487\";\n    private static final String TEN = \"19.220495, -70.5322645\";\n    private static final String ELEVEN = \"19.2189432, -70.5311005\";\n    private static final String TWELVE = \"19.2189336, -70.5310225\";\n\n    /**\n     * 2. The nodes below are an use case for junctionRoute. See\n     * http://www.openstreetmap.org/search?query=18.9576533%2C%20-70.4047707#map=19/18.95815/-70.\n     * 40446\n     */\n    private static final String THIRTEEN = \"18.9584473, -70.4051898\";\n    private static final String FOURTEEN = \"18.9585898, -70.4048838\";\n    private static final String FIFTEEN = \"18.9586238, -70.4048091\";\n    private static final String SIXTEEN = \"18.9591569, -70.4055156\";\n    private static final String SEVENTEEN = \"18.9591438, -70.405073\";\n    private static final String EIGHTEEN = \"18.9576533, -70.4047707\";\n    private static final String NINETEEN = \"18.958118, -70.4044936\";\n\n    /**\n     * 3. This tests the cases where junction edges are merged together through non-junction edges\n     * http://www.openstreetmap.org/search?query=19.2263658%2C%20-70.5232922#map=18/19.22637/-70.\n     * 52329\n     */\n    private static final String TWENTY = \"19.2263658, -70.5232922\";\n    private static final String TWENTYONE = \"19.2264076, -70.523348\";\n    private static final String TWENTYTWO = \"19.226494, -70.5232983\";\n    private static final String TWENTYTHREE = \"19.2264474, -70.5232479\";\n    private static final String TWENTYFOUR = \"19.2255758, -70.5221077\";\n    private static final String TWENTYFIVE = \"19.2261509, -70.5229941\";\n    private static final String TWENTYSIX = \"19.2271679, -70.5243228\";\n    private static final String TWENTYSEVEN = \"19.2272337, -70.5242798\";\n    private static final String TWENTYEIGHT = \"19.2267647, -70.5231237\";\n    private static final String TWENTYNINE = \"19.2267337, -70.5230635\";\n\n    /**\n     * 4. Expand to Residential Roads\n     * http://www.openstreetmap.org/way/139612559#map=18/-34.75409/-55.99516&layers=D\n     */\n    private static final String THIRTY = \"-34.7530995, -55.9955144\";\n    private static final String THIRTYONE = \"-34.7531244, -55.9954034\";\n    private static final String THIRTYTWO = \"-34.7538068, -55.995732\";\n    private static final String THIRTYTHREE = \"-34.7538356, -55.9956094\";\n    private static final String THIRTYFOUR = \"-34.7544319, -55.995908\";\n    private static final String THIRTYFIVE = \"-34.7544525, -55.995791\";\n    private static final String THIRTYSIX = \"-34.7534419, -55.9974882\";\n\n    /**\n     * 5. Tertiary dual carriage way roads are on one side of junction road\n     * http://www.openstreetmap.org/way/425027651\n     */\n    private static final String THIRTYSEVEN = \"-34.0999738, -56.2199071\";\n    private static final String THIRTYEIGHT = \"-34.1000359, -56.2198257\";\n    private static final String THIRTYNINE = \"-34.1002258, -56.2208914\";\n    private static final String FORTY = \"-34.100143, -56.2208073\";\n\n    /**\n     * 6. Angled (hence longer in length) junction roads\n     * http://www.openstreetmap.org/way/179178503#map=19/-34.91306/-56.16228\n     */\n    private static final String FORTYONE = \"-34.9128909, -56.1630794\";\n    private static final String FORTYTWO = \"-34.9130465, -56.1628523\";\n    private static final String FORTYTHREE = \"-34.9134932, -56.1630144\";\n    private static final String FORTYFOUR = \"-34.9134742, -56.1628122\";\n    private static final String FORTYFIVE = \"-34.9139186, -56.1629724\";\n    private static final String FORTYSIX = \"-34.9138376, -56.1627738\";\n    private static final String FORTYSEVEN = \"-34.9123896, -56.163135\";\n    private static final String FORTYEIGHT = \"-34.9123744, -56.1629292\";\n\n    /**\n     * 7. Merging Nearby Junction Edges\n     * https://www.openstreetmap.org/#map=19/-34.86409655621848/-56.15216571962752\n     */\n    private static final String FORTYNINE = \"-34.8633275, -56.1520582\";\n    private static final String FIFTY = \"-34.8633582, -56.1518486\";\n    private static final String FIFTYONE = \"-34.864245, -56.1529168\";\n    private static final String FIFTYTWO = \"-34.8638276, -56.1521606\";\n    private static final String FIFTYTHREE = \"-34.8636873, -56.1519129\";\n    private static final String FIFTYFOUR = \"-34.8634207, -56.1514326\";\n    private static final String FIFTYFIVE = \"-34.8644674, -56.1527475\";\n    private static final String FIFTYSIX = \"-34.8641705, -56.1522056\";\n    private static final String FIFTYSEVEN = \"-34.8640427, -56.151971\";\n    private static final String FIFTYEIGHT = \"-34.8632392, -56.1505407\";\n    private static final String FIFTYNINE = \"-34.8645135, -56.1522445\";\n    private static final String SIXTY = \"-34.86453, -56.1520303\";\n\n    /**\n     * 8. Case where a bifurcation point (between a two-way road and 2 one-ways roads ) creates a\n     * invalid junction edge http://www.openstreetmap.org/relation/6517015#map=19/4.88642/-52.28552\n     * Other examples : https://www.openstreetmap.org/#map=20/4.931389860539109/-52.30634515601126\n     * https://www.openstreetmap.org/#map=19/4.887468820117024/-52.28483397495584\n     */\n    private static final String SIXTYONE = \"4.8866669, -52.2865243\";\n    private static final String SIXTYTWO = \"4.8868532, -52.2862674\";\n    private static final String SIXTYTHREE = \"4.8870696, -52.2860041\";\n    private static final String SIXTYFOUR = \"4.8871591, -52.2858701\";\n    private static final String SIXTYFIVE = \"4.8870147, -52.2859692\";\n\n    /**\n     * 9. Filter off piers\n     */\n    private static final String SIXTYSIX = \"41.9043091, 12.4744368\";\n    private static final String SIXTYSEVEN = \"41.9043335, 12.4743707\";\n    private static final String SIXTYEIGHT = \"41.9044448, 12.4744446\";\n    private static final String SIXTYNINE = \"41.9044215, 12.4745081\";\n\n    /**\n     * 10. Inspired by Big Node at Location: 42.386538, -83.139549. Making sure we are returning all\n     * possible paths, instead of just the shortest path\n     */\n    private static final String SEVENTY = \"42.3867848, -83.1395522\";\n    private static final String SEVENTY_ONE = \"42.3866924, -83.139548\";\n    private static final String SEVENTY_TWO = \"42.3865343, -83.1395345\";\n    private static final String SEVENTY_THREE = \"42.3864108, -83.1395301\";\n    private static final String SEVENTY_FOUR = \"42.3866828, -83.139747\";\n    private static final String SEVENTY_FIVE = \"42.3865265, -83.1397502\";\n    private static final String SEVENTY_SIX = \"42.3865507, -83.139331\";\n    private static final String SEVENTY_SEVEN = \"42.3867006, -83.1393079\";\n\n    /**\n     * 11. Exclude cycle ways at inEdges/outEdges\n     */\n    private static final String SEVENTY_EIGHT = \"55.7167715, 12.4571926\";\n    private static final String SEVENTY_NINE = \"55.7168313, 12.4572756\";\n    private static final String EIGHTY = \"55.7169283, 12.4573998\";\n    private static final String EIGHTY_ONE = \"55.7169963, 12.4574844\";\n\n    private static final String EIGHTY_TWO = \"55.71685, 12.45793\";\n    private static final String EIGHTY_THREE = \"55.71675, 12.45792\";\n    private static final String EIGHTY_FOUR = \"55.71668, 12.45771\";\n\n    private static final String EIGHTY_FIVE = \"55.71702, 12.45676\";\n    private static final String EIGHTY_SIX = \"55.71711, 12.45687\";\n    private static final String EIGHTY_SEVEN = \"55.71718, 12.45692\";\n\n    /**\n     * 12. Exclude service roads\n     */\n    private static final String EIGHTY_EIGHT = \"56.1930077, 10.2433446\";\n    private static final String EIGHTY_NINE = \"56.1928659, 10.2430657\";\n    private static final String NINETY = \"56.1930248, 10.2426652\";\n    private static final String NINETY_ONE = \"56.1930801, 10.243183\";\n\n    /**\n     * 13. long straight route e.g:\n     * https://www.openstreetmap.org/search?query=%2042.7764202%20-83.2392182#map=18/42.77623/-83.23883\n     */\n    private static final String ONE_HUNDRED_AND_ONE = \"42.7764202, -83.2392182\";\n    private static final String ONE_HUNDRED_AND_TWO = \"42.7764052, -83.239217\";\n    private static final String ONE_HUNDRED_AND_THREE = \"42.7763731, -83.2392143\";\n    private static final String ONE_HUNDRED_AND_FOUR = \"42.7763412, -83.2392117 \";\n    private static final String ONE_HUNDRED_AND_FIVE = \"42.7763093, -83.2392091\";\n    private static final String ONE_HUNDRED_AND_SIX = \"42.7721413, -83.2387795\";\n    private static final String ONE_HUNDRED_AND_SEVEN = \"42.7720644, -83.2387711\";\n    // add one more point to break the long straight route\n    private static final String ONE_HUNDRED_AND_EIGHT = \"42.7749246, -83.2392277\";\n\n    @TestAtlas(\n\n            nodes = { @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX)),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN)),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT)),\n                    @Node(id = \"9\", coordinates = @Loc(value = NINE)),\n                    @Node(id = \"10\", coordinates = @Loc(value = TEN)),\n                    @Node(id = \"11\", coordinates = @Loc(value = ELEVEN)),\n                    @Node(id = \"12\", coordinates = @Loc(value = TWELVE)),\n\n                    // Second Big Node example\n                    @Node(id = \"13\", coordinates = @Loc(value = THIRTEEN)),\n                    @Node(id = \"14\", coordinates = @Loc(value = FOURTEEN)),\n                    @Node(id = \"15\", coordinates = @Loc(value = FIFTEEN)),\n                    @Node(id = \"16\", coordinates = @Loc(value = SIXTEEN)),\n                    @Node(id = \"17\", coordinates = @Loc(value = SEVENTEEN)),\n                    @Node(id = \"18\", coordinates = @Loc(value = EIGHTEEN)),\n                    @Node(id = \"19\", coordinates = @Loc(value = NINETEEN)),\n\n                    // Third Big Node example\n                    @Node(id = \"20\", coordinates = @Loc(value = TWENTY)),\n                    @Node(id = \"21\", coordinates = @Loc(value = TWENTYONE)),\n                    @Node(id = \"22\", coordinates = @Loc(value = TWENTYTWO)),\n                    @Node(id = \"23\", coordinates = @Loc(value = TWENTYTHREE)),\n                    @Node(id = \"24\", coordinates = @Loc(value = TWENTYFOUR)),\n                    @Node(id = \"25\", coordinates = @Loc(value = TWENTYFIVE)),\n                    @Node(id = \"26\", coordinates = @Loc(value = TWENTYSIX)),\n                    @Node(id = \"27\", coordinates = @Loc(value = TWENTYSEVEN)),\n                    @Node(id = \"28\", coordinates = @Loc(value = TWENTYEIGHT)),\n                    @Node(id = \"29\", coordinates = @Loc(value = TWENTYNINE)),\n\n                    // Fourth example\n                    @Node(id = \"30\", coordinates = @Loc(value = THIRTY)),\n                    @Node(id = \"31\", coordinates = @Loc(value = THIRTYONE)),\n                    @Node(id = \"32\", coordinates = @Loc(value = THIRTYTWO)),\n                    @Node(id = \"33\", coordinates = @Loc(value = THIRTYTHREE)),\n                    @Node(id = \"34\", coordinates = @Loc(value = THIRTYFOUR)),\n                    @Node(id = \"35\", coordinates = @Loc(value = THIRTYFIVE)),\n                    @Node(id = \"36\", coordinates = @Loc(value = THIRTYSIX)),\n\n                    // Fifth example\n                    @Node(id = \"37\", coordinates = @Loc(value = THIRTYSEVEN)),\n                    @Node(id = \"38\", coordinates = @Loc(value = THIRTYEIGHT)),\n                    @Node(id = \"39\", coordinates = @Loc(value = THIRTYNINE)),\n                    @Node(id = \"40\", coordinates = @Loc(value = FORTY)),\n\n                    // Sixth example\n                    @Node(id = \"41\", coordinates = @Loc(value = FORTYONE)),\n                    @Node(id = \"42\", coordinates = @Loc(value = FORTYTWO)),\n                    @Node(id = \"43\", coordinates = @Loc(value = FORTYTHREE)),\n                    @Node(id = \"44\", coordinates = @Loc(value = FORTYFOUR)),\n                    @Node(id = \"45\", coordinates = @Loc(value = FORTYFIVE)),\n                    @Node(id = \"46\", coordinates = @Loc(value = FORTYSIX)),\n                    @Node(id = \"47\", coordinates = @Loc(value = FORTYSEVEN)),\n                    @Node(id = \"48\", coordinates = @Loc(value = FORTYEIGHT)),\n\n                    // Seventh example\n                    @Node(id = \"49\", coordinates = @Loc(value = FORTYNINE)),\n                    @Node(id = \"50\", coordinates = @Loc(value = FIFTY)),\n                    @Node(id = \"51\", coordinates = @Loc(value = FIFTYONE)),\n                    @Node(id = \"52\", coordinates = @Loc(value = FIFTYTWO)),\n                    @Node(id = \"53\", coordinates = @Loc(value = FIFTYTHREE)),\n                    @Node(id = \"54\", coordinates = @Loc(value = FIFTYFOUR)),\n                    @Node(id = \"55\", coordinates = @Loc(value = FIFTYFIVE)),\n                    @Node(id = \"56\", coordinates = @Loc(value = FIFTYSIX)),\n                    @Node(id = \"57\", coordinates = @Loc(value = FIFTYSEVEN)),\n                    @Node(id = \"58\", coordinates = @Loc(value = FIFTYEIGHT)),\n                    @Node(id = \"59\", coordinates = @Loc(value = FIFTYNINE)),\n                    @Node(id = \"60\", coordinates = @Loc(value = SIXTY)),\n\n                    // Eighth example\n                    @Node(id = \"61\", coordinates = @Loc(value = SIXTYONE)),\n                    @Node(id = \"62\", coordinates = @Loc(value = SIXTYTWO)),\n                    @Node(id = \"63\", coordinates = @Loc(value = SIXTYTHREE)),\n                    @Node(id = \"64\", coordinates = @Loc(value = SIXTYFOUR)),\n                    @Node(id = \"65\", coordinates = @Loc(value = SIXTYFIVE)),\n\n                    // Ninth example\n                    @Node(id = \"66\", coordinates = @Loc(value = SIXTYSIX)),\n                    @Node(id = \"67\", coordinates = @Loc(value = SIXTYSEVEN)),\n                    @Node(id = \"68\", coordinates = @Loc(value = SIXTYEIGHT)),\n                    @Node(id = \"69\", coordinates = @Loc(value = SIXTYNINE)),\n\n                    // Eleventh example\n                    @Node(id = \"78\", coordinates = @Loc(value = SEVENTY_EIGHT)),\n                    @Node(id = \"79\", coordinates = @Loc(value = SEVENTY_NINE)),\n                    @Node(id = \"80\", coordinates = @Loc(value = EIGHTY)),\n                    @Node(id = \"81\", coordinates = @Loc(value = EIGHTY_ONE)),\n                    @Node(id = \"82\", coordinates = @Loc(value = EIGHTY_TWO)),\n                    @Node(id = \"83\", coordinates = @Loc(value = EIGHTY_THREE)),\n                    @Node(id = \"84\", coordinates = @Loc(value = EIGHTY_FOUR)),\n                    @Node(id = \"85\", coordinates = @Loc(value = EIGHTY_FIVE)),\n                    @Node(id = \"86\", coordinates = @Loc(value = EIGHTY_SIX)),\n                    @Node(id = \"87\", coordinates = @Loc(value = EIGHTY_SEVEN)),\n\n                    // Twelfth example\n                    @Node(id = \"88\", coordinates = @Loc(value = EIGHTY_EIGHT)),\n                    @Node(id = \"89\", coordinates = @Loc(value = EIGHTY_NINE)),\n                    @Node(id = \"90\", coordinates = @Loc(value = NINETY)),\n                    @Node(id = \"91\", coordinates = @Loc(value = NINETY_ONE)),\n\n                    @Node(id = \"101\", coordinates = @Loc(value = ONE_HUNDRED_AND_ONE)),\n                    @Node(id = \"102\", coordinates = @Loc(value = ONE_HUNDRED_AND_TWO)),\n                    @Node(id = \"103\", coordinates = @Loc(value = ONE_HUNDRED_AND_THREE)),\n                    @Node(id = \"104\", coordinates = @Loc(value = ONE_HUNDRED_AND_FOUR)),\n                    @Node(id = \"105\", coordinates = @Loc(value = ONE_HUNDRED_AND_FIVE)),\n                    @Node(id = \"106\", coordinates = @Loc(value = ONE_HUNDRED_AND_SIX)),\n                    @Node(id = \"107\", coordinates = @Loc(value = ONE_HUNDRED_AND_SEVEN)),\n                    @Node(id = \"108\", coordinates = @Loc(value = ONE_HUNDRED_AND_EIGHT)) },\n\n            edges = {\n                    @Edge(id = \"12\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"43\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"42\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"25\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = FIVE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"56\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"64\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = FOUR) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"27\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = SEVEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"85\", coordinates = { @Loc(value = EIGHT),\n                            @Loc(value = FIVE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"59\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = NINE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"106\", coordinates = { @Loc(value = TEN),\n                            @Loc(value = SIX) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"611\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = ELEVEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"124\", coordinates = { @Loc(value = TWELVE),\n                            @Loc(value = FOUR) }, tags = { \"highway=trunk\" }),\n\n                    // Second Big Node example\n                    @Edge(id = \"1314\", coordinates = { @Loc(value = THIRTEEN),\n                            @Loc(value = FOURTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1415\", coordinates = { @Loc(value = FOURTEEN),\n                            @Loc(value = FIFTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1613\", coordinates = { @Loc(value = SIXTEEN),\n                            @Loc(value = THIRTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1517\", coordinates = { @Loc(value = FIFTEEN),\n                            @Loc(value = SEVENTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1318\", coordinates = { @Loc(value = THIRTEEN),\n                            @Loc(value = EIGHTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1915\", coordinates = { @Loc(value = NINETEEN),\n                            @Loc(value = FIFTEEN) }, tags = { \"highway=trunk\" }),\n\n                    // Third example\n                    @Edge(id = \"2423\", coordinates = { @Loc(value = TWENTYFOUR),\n                            @Loc(value = TWENTYTHREE) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2322\", coordinates = { @Loc(value = TWENTYTHREE),\n                            @Loc(value = TWENTYTWO) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2227\", coordinates = { @Loc(value = TWENTYTWO),\n                            @Loc(value = TWENTYSEVEN) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2025\", coordinates = { @Loc(value = TWENTY),\n                            @Loc(value = TWENTYFIVE) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2120\", coordinates = { @Loc(value = TWENTYONE),\n                            @Loc(value = TWENTY) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2621\", coordinates = { @Loc(value = TWENTYSIX),\n                            @Loc(value = TWENTYONE) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2822\", coordinates = { @Loc(value = TWENTYEIGHT),\n                            @Loc(value = TWENTYTWO) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2221\", coordinates = { @Loc(value = TWENTYTWO),\n                            @Loc(value = TWENTYONE) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2023\", coordinates = { @Loc(value = TWENTY),\n                            @Loc(value = TWENTYTHREE) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"2329\", coordinates = { @Loc(value = TWENTYTHREE),\n                            @Loc(value = TWENTYNINE) }, tags = { \"highway=primary\" }),\n\n                    // Fourth example\n                    @Edge(id = \"3230\", coordinates = { @Loc(value = THIRTYTWO),\n                            @Loc(value = THIRTY) }, tags = { \"highway=residential\" }),\n                    @Edge(id = \"3133\", coordinates = { @Loc(value = THIRTYONE),\n                            @Loc(value = THIRTYTHREE) }, tags = { \"highway=residential\" }),\n                    @Edge(id = \"3432\", coordinates = { @Loc(value = THIRTYFOUR),\n                            @Loc(value = THIRTYTWO) }, tags = { \"highway=residential\" }),\n                    @Edge(id = \"3335\", coordinates = { @Loc(value = THIRTYTHREE),\n                            @Loc(value = THIRTYFIVE) }, tags = { \"highway=residential\" }),\n                    @Edge(id = \"3632\", coordinates = { @Loc(value = THIRTYSIX),\n                            @Loc(value = THIRTYTWO) }, tags = { \"highway=residential\" }),\n                    @Edge(id = \"3233\", coordinates = { @Loc(value = THIRTYTWO),\n                            @Loc(value = THIRTYTHREE) }, tags = { \"highway=residential\",\n                                    \"oneway=no\" }),\n                    @Edge(id = \"-3233\", coordinates = { @Loc(value = THIRTYTHREE),\n                            @Loc(value = THIRTYTWO) }, tags = { \"highway=residential\" }),\n\n                    // Fifth example\n                    @Edge(id = \"3740\", coordinates = { @Loc(value = THIRTYSEVEN),\n                            @Loc(value = FORTY) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"4039\", coordinates = { @Loc(value = FORTY),\n                            @Loc(value = THIRTYNINE) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"3938\", coordinates = { @Loc(value = THIRTYNINE),\n                            @Loc(value = THIRTYEIGHT) }, tags = { \"highway=tertiary\" }),\n\n                    // Sixth example\n                    @Edge(id = \"4741\", coordinates = { @Loc(value = FORTYSEVEN),\n                            @Loc(value = FORTYONE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"4143\", coordinates = { @Loc(value = FORTYONE),\n                            @Loc(value = FORTYTHREE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"4345\", coordinates = { @Loc(value = FORTYTHREE),\n                            @Loc(value = FORTYFIVE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"4644\", coordinates = { @Loc(value = FORTYSIX),\n                            @Loc(value = FORTYFOUR) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"4442\", coordinates = { @Loc(value = FORTYFOUR),\n                            @Loc(value = FORTYTWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"4248\", coordinates = { @Loc(value = FORTYTWO),\n                            @Loc(value = FORTYEIGHT) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"4142\", coordinates = { @Loc(value = FORTYONE),\n                            @Loc(value = FORTYTWO) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"4443\", coordinates = { @Loc(value = FORTYFOUR),\n                            @Loc(value = FORTYTHREE) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"4546\", coordinates = { @Loc(value = FORTYFIVE),\n                            @Loc(value = FORTYSIX) }, tags = { \"highway=tertiary\" }),\n\n                    // Seventh example\n                    @Edge(id = \"5450\", coordinates = { @Loc(value = FIFTYFOUR),\n                            @Loc(value = FIFTY) }, tags = { \"highway=primary_link\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5049\", coordinates = { @Loc(value = FIFTY),\n                            @Loc(value = FORTYNINE) }, tags = { \"highway=primary_link\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5251\", coordinates = { @Loc(value = FIFTYTWO),\n                            @Loc(value = FIFTYONE) }, tags = { \"highway=primary\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5352\", coordinates = { @Loc(value = FIFTYTHREE),\n                            @Loc(value = FIFTYTWO) }, tags = { \"highway=primary\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5453\", coordinates = { @Loc(value = FIFTYFOUR),\n                            @Loc(value = FIFTYTHREE) }, tags = { \"highway=primary\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5556\", coordinates = { @Loc(value = FIFTYFIVE),\n                            @Loc(value = FIFTYSIX) }, tags = { \"highway=primary\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5657\", coordinates = { @Loc(value = FIFTYSIX),\n                            @Loc(value = FIFTYSEVEN) }, tags = { \"highway=primary\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5758\", coordinates = { @Loc(value = FIFTYSEVEN),\n                            @Loc(value = FIFTYEIGHT) }, tags = { \"highway=primary\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5559\", coordinates = { @Loc(value = FIFTYFIVE),\n                            @Loc(value = FIFTYNINE) }, tags = { \"highway=primary_link\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"5960\", coordinates = { @Loc(value = FIFTYNINE),\n                            @Loc(value = SIXTY) }, tags = { \"highway=primary_link\",\n                                    \"name=Avenida José Pedro Varela\" }),\n                    @Edge(id = \"4952\", coordinates = { @Loc(value = FORTYNINE),\n                            @Loc(value = FIFTYTWO) }, tags = { \"highway=primary\",\n                                    \"name=Avenida Dámaso Antonio Larrañaga\" }),\n                    @Edge(id = \"5256\", coordinates = { @Loc(value = FIFTYTWO),\n                            @Loc(value = FIFTYSIX) }, tags = { \"highway=primary\",\n                                    \"name=Avenida Dámaso Antonio Larrañaga\" }),\n                    @Edge(id = \"5659\", coordinates = { @Loc(value = FIFTYSIX),\n                            @Loc(value = FIFTYNINE) }, tags = { \"highway=primary\",\n                                    \"name=Avenida Dámaso Antonio Larrañaga\" }),\n                    @Edge(id = \"6057\", coordinates = { @Loc(value = SIXTY),\n                            @Loc(value = FIFTYSEVEN) }, tags = { \"highway=primary\",\n                                    \"name=Avenida Dámaso Antonio Larrañaga\" }),\n                    @Edge(id = \"5753\", coordinates = { @Loc(value = FIFTYSEVEN),\n                            @Loc(value = FIFTYTHREE) }, tags = { \"highway=primary\",\n                                    \"name=Avenida Dámaso Antonio Larrañaga\" }),\n                    @Edge(id = \"5350\", coordinates = { @Loc(value = FIFTYTHREE),\n                            @Loc(value = FIFTY) }, tags = { \"highway=primary\",\n                                    \"name=Avenida Dámaso Antonio Larrañaga\" }),\n\n                    // Eighth example\n                    @Edge(id = \"6162\", coordinates = { @Loc(value = SIXTYONE),\n                            @Loc(value = SIXTYTWO) }, tags = { \"highway=primary\",\n                                    \"name=La Matouriennea\" }),\n                    @Edge(id = \"-6162\", coordinates = { @Loc(value = SIXTYTWO),\n                            @Loc(value = SIXTYONE) }, tags = { \"highway=primary\",\n                                    \"name=La Matouriennea\" }),\n                    @Edge(id = \"6362\", coordinates = { @Loc(value = SIXTYTHREE),\n                            @Loc(value = SIXTYTWO) }, tags = { \"highway=primary\",\n                                    \"name=La Matouriennea\" }),\n                    @Edge(id = \"6463\", coordinates = { @Loc(value = SIXTYFOUR),\n                            @Loc(value = SIXTYTHREE) }, tags = { \"highway=primary\",\n                                    \"name=La Matouriennea\" }),\n                    @Edge(id = \"6265\", coordinates = { @Loc(value = SIXTYTWO),\n                            @Loc(value = SIXTYFIVE) }, tags = { \"highway=primary\",\n                                    \"name=La Matouriennea\" }),\n\n                    // Ninth example\n                    @Edge(id = \"6667\", coordinates = { @Loc(value = SIXTYSIX),\n                            @Loc(value = SIXTYSEVEN) }, tags = { \"man_made=pier\", \"mooring=yes\" }),\n                    @Edge(id = \"6768\", coordinates = { @Loc(value = SIXTYSEVEN),\n                            @Loc(value = SIXTYEIGHT) }, tags = { \"man_made=pier\", \"mooring=yes\" }),\n                    @Edge(id = \"6869\", coordinates = { @Loc(value = SIXTYEIGHT),\n                            @Loc(value = SIXTYNINE) }, tags = { \"man_made=pier\", \"mooring=yes\" }),\n\n                    // Eleventh example\n                    @Edge(id = \"7879\", coordinates = { @Loc(value = SEVENTY_EIGHT),\n                            @Loc(value = SEVENTY_NINE) }, tags = { \"highway=residential\",\n                                    \"name=Herlev Hovedgade\" }),\n                    @Edge(id = \"7980\", coordinates = { @Loc(value = SEVENTY_NINE),\n                            @Loc(value = EIGHTY) }, tags = { \"highway=residential\",\n                                    \"name=Rytmevej\" }),\n                    @Edge(id = \"8081\", coordinates = { @Loc(value = EIGHTY),\n                            @Loc(value = EIGHTY_ONE) }, tags = { \"highway=residential\",\n                                    \"name=Klokkedybet\" }),\n                    @Edge(id = \"8281\", coordinates = { @Loc(value = EIGHTY_TWO),\n                            @Loc(value = EIGHTY_ONE) }, tags = { \"highway=cycleway\", \"oneway=yes\",\n                                    \"name=Herlev Hovedgade\" }),\n                    @Edge(id = \"8380\", coordinates = { @Loc(value = EIGHTY_THREE),\n                            @Loc(value = EIGHTY) }, tags = { \"highway=secondary\", \"oneway=yes\" }),\n                    @Edge(id = \"8479\", coordinates = { @Loc(value = EIGHTY_FOUR),\n                            @Loc(value = SEVENTY_NINE) }, tags = { \"highway=secondary\",\n                                    \"oneway=yes\" }),\n                    @Edge(id = \"8579\", coordinates = { @Loc(value = EIGHTY_FIVE),\n                            @Loc(value = SEVENTY_NINE) }, tags = { \"highway=secondary\",\n                                    \"oneway=yes\" }),\n                    @Edge(id = \"8086\", coordinates = { @Loc(value = EIGHTY),\n                            @Loc(value = EIGHTY_SIX) }, tags = { \"highway=secondary\",\n                                    \"oneway=yes\" }),\n                    @Edge(id = \"8187\", coordinates = { @Loc(value = EIGHTY_ONE),\n                            @Loc(value = EIGHTY_SEVEN) }, tags = { \"highway=cycleway\", \"oneway=yes\",\n                                    \"name=Herlev Hovedgade\" }),\n                    // Twelfth example\n                    @Edge(id = \"8889\", coordinates = { @Loc(value = EIGHTY_EIGHT),\n                            @Loc(value = EIGHTY_NINE) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"8988\", coordinates = { @Loc(value = EIGHTY_NINE),\n                            @Loc(value = EIGHTY_EIGHT) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"8891\", coordinates = { @Loc(value = EIGHTY_EIGHT),\n                            @Loc(value = NINETY_ONE) }, tags = { \"highway=service\", \"oneway=yes\" }),\n                    @Edge(id = \"9089\", coordinates = { @Loc(value = NINETY),\n                            @Loc(value = EIGHTY_NINE) }, tags = { \"highway=service\",\n                                    \"oneway=yes\" }),\n\n                    @Edge(id = \"101102\", coordinates = { @Loc(value = ONE_HUNDRED_AND_ONE),\n                            @Loc(value = ONE_HUNDRED_AND_TWO) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"102103\", coordinates = { @Loc(value = ONE_HUNDRED_AND_TWO),\n                            @Loc(value = ONE_HUNDRED_AND_THREE) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"103104\", coordinates = { @Loc(value = ONE_HUNDRED_AND_THREE),\n                            @Loc(value = ONE_HUNDRED_AND_FOUR) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"104105\", coordinates = { @Loc(value = ONE_HUNDRED_AND_FOUR),\n                            @Loc(value = ONE_HUNDRED_AND_FIVE) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"105106\", coordinates = { @Loc(value = ONE_HUNDRED_AND_FIVE),\n                            @Loc(value = ONE_HUNDRED_AND_SIX) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"106107\", coordinates = { @Loc(value = ONE_HUNDRED_AND_SIX),\n                            @Loc(value = ONE_HUNDRED_AND_SEVEN) }, tags = { \"highway=tertiary\" }),\n                    @Edge(id = \"107108\", coordinates = { @Loc(value = ONE_HUNDRED_AND_SEVEN),\n                            @Loc(value = ONE_HUNDRED_AND_EIGHT) }, tags = { \"highway=tertiary\" }) })\n    private Atlas atlas;\n\n    @TestAtlas(\n\n            nodes = { @Node(id = \"70\", coordinates = @Loc(value = SEVENTY)),\n                    @Node(id = \"71\", coordinates = @Loc(value = SEVENTY_ONE)),\n                    @Node(id = \"72\", coordinates = @Loc(value = SEVENTY_TWO)),\n                    @Node(id = \"73\", coordinates = @Loc(value = SEVENTY_THREE)),\n                    @Node(id = \"74\", coordinates = @Loc(value = SEVENTY_FOUR)),\n                    @Node(id = \"75\", coordinates = @Loc(value = SEVENTY_FIVE)),\n                    @Node(id = \"76\", coordinates = @Loc(value = SEVENTY_SIX)),\n                    @Node(id = \"77\", coordinates = @Loc(value = SEVENTY_SEVEN)) },\n\n            edges = {\n                    @Edge(id = \"1\", coordinates = { @Loc(value = SEVENTY_THREE),\n                            @Loc(value = SEVENTY_TWO) }, tags = { \"highway=secondary\",\n                                    \"name=Livernois Avenue\" }),\n                    @Edge(id = \"-1\", coordinates = { @Loc(value = SEVENTY_TWO),\n                            @Loc(value = SEVENTY_THREE) }, tags = { \"highway=secondary\",\n                                    \"name=Livernois Avenue\" }),\n                    @Edge(id = \"2\", coordinates = { @Loc(value = SEVENTY_TWO),\n                            @Loc(value = SEVENTY_ONE) }, tags = { \"highway=secondary\",\n                                    \"name=Livernois Avenue\" }),\n                    @Edge(id = \"-2\", coordinates = { @Loc(value = SEVENTY_ONE),\n                            @Loc(value = SEVENTY_TWO) }, tags = { \"highway=secondary\",\n                                    \"name=Livernois Avenue\" }),\n                    @Edge(id = \"3\", coordinates = { @Loc(value = SEVENTY_ONE),\n                            @Loc(value = SEVENTY) }, tags = { \"highway=secondary\",\n                                    \"name=Livernois Avenue\" }),\n                    @Edge(id = \"-3\", coordinates = { @Loc(value = SEVENTY),\n                            @Loc(value = SEVENTY_ONE) }, tags = { \"highway=secondary\",\n                                    \"name=Livernois Avenue\" }),\n                    @Edge(id = \"4\", coordinates = { @Loc(value = SEVENTY_SEVEN),\n                            @Loc(value = SEVENTY_ONE) }, tags = { \"highway=primary\",\n                                    \"name=West Davison Avenue\", \"oneway=yes\" }),\n                    @Edge(id = \"5\", coordinates = { @Loc(value = SEVENTY_ONE),\n                            @Loc(value = SEVENTY_FOUR) }, tags = { \"highway=primary\",\n                                    \"name=West Davison Avenue\", \"oneway=yes\" }),\n                    @Edge(id = \"6\", coordinates = { @Loc(value = SEVENTY_FIVE),\n                            @Loc(value = SEVENTY_TWO) }, tags = { \"highway=primary\",\n                                    \"name=West Davison Avenue\", \"oneway=yes\" }),\n                    @Edge(id = \"7\", coordinates = { @Loc(value = SEVENTY_TWO),\n                            @Loc(value = SEVENTY_SIX) }, tags = { \"highway=primary\",\n                                    \"name=West Davison Avenue\", \"oneway=yes\" }) })\n    private Atlas complexJunctionAtlas;\n\n    @TestAtlas(\n\n            nodes = { @Node(id = \"49\", coordinates = @Loc(value = FORTYNINE)),\n                    @Node(id = \"50\", coordinates = @Loc(value = FIFTY)),\n                    @Node(id = \"51\", coordinates = @Loc(value = FIFTYONE)),\n                    @Node(id = \"52\", coordinates = @Loc(value = FIFTYTWO)),\n                    @Node(id = \"53\", coordinates = @Loc(value = FIFTYTHREE)),\n                    @Node(id = \"54\", coordinates = @Loc(value = FIFTYFOUR)),\n                    @Node(id = \"55\", coordinates = @Loc(value = FIFTYFIVE)),\n                    @Node(id = \"56\", coordinates = @Loc(value = FIFTYSIX)),\n                    @Node(id = \"57\", coordinates = @Loc(value = FIFTYSEVEN)),\n                    @Node(id = \"58\", coordinates = @Loc(value = FIFTYEIGHT)),\n                    @Node(id = \"59\", coordinates = @Loc(value = FIFTYNINE)),\n                    @Node(id = \"60\", coordinates = @Loc(value = SIXTY)), },\n\n            edges = {\n                    @Edge(id = \"1\", coordinates = { @Loc(value = FORTYNINE),\n                            @Loc(value = FIFTYTWO) }, tags = { \"highway=secondary\",\n                                    \"name=Soorkie Avenue\" }),\n                    @Edge(id = \"2\", coordinates = { @Loc(value = FIFTYTHREE),\n                            @Loc(value = FIFTY) }, tags = { \"highway=secondary\",\n                                    \"name=Soorkie Avenue\" }),\n\n                    @Edge(id = \"3\", coordinates = { @Loc(value = FIFTYTWO),\n                            @Loc(value = FIFTYONE) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n                    @Edge(id = \"4\", coordinates = { @Loc(value = FIFTYFIVE),\n                            @Loc(value = FIFTYSIX) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n\n                    @Edge(id = \"5\", coordinates = { @Loc(value = FIFTYSIX),\n                            @Loc(value = FIFTYNINE) }, tags = { \"highway=secondary\",\n                                    \"name=Soorkie Avenue\" }),\n                    @Edge(id = \"6\", coordinates = { @Loc(value = SIXTY),\n                            @Loc(value = FIFTYSEVEN) }, tags = { \"highway=secondary\",\n                                    \"name=Soorkie Avenue\" }),\n\n                    @Edge(id = \"7\", coordinates = { @Loc(value = FIFTYSEVEN),\n                            @Loc(value = FIFTYEIGHT) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n                    @Edge(id = \"8\", coordinates = { @Loc(value = FIFTYFOUR),\n                            @Loc(value = FIFTYTHREE) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n\n                    @Edge(id = \"9\", coordinates = { @Loc(value = FIFTYTWO),\n                            @Loc(value = FIFTYTHREE) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n                    @Edge(id = \"-9\", coordinates = { @Loc(value = FIFTYTHREE),\n                            @Loc(value = FIFTYTWO) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n\n                    @Edge(id = \"10\", coordinates = { @Loc(value = FIFTYTWO),\n                            @Loc(value = FIFTYSIX) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n                    @Edge(id = \"-10\", coordinates = { @Loc(value = FIFTYSIX),\n                            @Loc(value = FIFTYTWO) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n\n                    @Edge(id = \"11\", coordinates = { @Loc(value = FIFTYSIX),\n                            @Loc(value = FIFTYSEVEN) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n                    @Edge(id = \"-11\", coordinates = { @Loc(value = FIFTYSEVEN),\n                            @Loc(value = FIFTYSIX) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n\n                    @Edge(id = \"12\", coordinates = { @Loc(value = FIFTYTHREE),\n                            @Loc(value = FIFTYSEVEN) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }),\n                    @Edge(id = \"-12\", coordinates = { @Loc(value = FIFTYSEVEN),\n                            @Loc(value = FIFTYTHREE) }, tags = { \"highway=secondary\",\n                                    \"name=Biryani Avenue\" }), })\n    private Atlas superComplexJunctionAtlas;\n\n    /*\n     * Intersections where nearby big nodes overlap\n     */\n    @TestAtlas(loadFromTextResource = \"overlap.atlas.txt.gz\")\n    private Atlas overlapAtlas;\n\n    /*\n     * Intersections that test expansion of big nodes\n     */\n    @TestAtlas(loadFromTextResource = \"expand.atlas.txt.gz\")\n    private Atlas expandAtlas;\n\n    /*\n     * Intersections that test over merging of big nodes for residential roads\n     */\n    @TestAtlas(loadFromTextResource = \"overmerge.atlas.txt.gz\")\n    private Atlas overMergeAtlas;\n\n    @TestAtlas(loadFromJosmOsmResource = \"dnk-link-road-test.osm\")\n    private Atlas dnkAtlas;\n\n    @TestAtlas(loadFromJosmOsmResource = \"ukr-link-road-test.osm\")\n    private Atlas ukrAtlas;\n\n    @TestAtlas(loadFromJosmOsmResource = \"u-turn-shape-edge.osm\")\n    private Atlas uTurnShapeEdgeAtlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n\n    public Atlas getComplexJunctionAtlas()\n    {\n        return this.superComplexJunctionAtlas;\n    }\n\n    public Atlas getDNKAtlasToTestExcludeLinkRoadAsDualCarriageWay()\n    {\n        return this.dnkAtlas;\n    }\n\n    public Atlas getExpandBigNodeAtlas()\n    {\n        return this.expandAtlas;\n    }\n\n    public Atlas getOverMergeAtlas()\n    {\n        return this.overMergeAtlas;\n    }\n\n    public Atlas getOverlapAtlas()\n    {\n        return this.overlapAtlas;\n    }\n\n    public Atlas getUKRAtlasToTestExcludeLinkRoadAsDualCarriageWay()\n    {\n        return this.ukrAtlas;\n    }\n\n    public Atlas getuTurnShapeEdgeAtlas()\n    {\n        return this.uTurnShapeEdgeAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/boundaries/ComplexBoundaryTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.boundaries;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class ComplexBoundaryTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(ComplexBoundaryTest.class);\n\n    @Rule\n    public final ComplexBoundaryTestRule rule = new ComplexBoundaryTestRule();\n\n    @Test\n    public void testComplexBoundary()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final List<ComplexBoundary> badEntities = new ArrayList<>();\n        logger.info(\"Atlas: {}\", atlas);\n        final ComplexBoundaryFinder finder = new ComplexBoundaryFinder();\n        finder.setWithSubAreas(true);\n        final List<ComplexBoundary> result = Iterables.stream(finder.find(atlas, badEntities::add))\n                .collectToList();\n        logger.info(\"Complex Boundaries: {}\", result);\n        Assert.assertEquals(2, result.size());\n        final ComplexBoundary first = result.get(0);\n        final ComplexBoundary second = result.get(1);\n        Assert.assertEquals(2, first.getAdministrativeLevel());\n        final Set<ComplexBoundary> firstChildren = first.getSubAreas();\n        Assert.assertEquals(1, firstChildren.size());\n        final ComplexBoundary firstChild = firstChildren.iterator().next();\n        Assert.assertEquals(3, firstChild.getAdministrativeLevel());\n        final Set<ComplexBoundary> firstChildChildren = firstChild.getSubAreas();\n        Assert.assertEquals(1, firstChildChildren.size());\n        final ComplexBoundary firstChildChild = firstChildChildren.iterator().next();\n        Assert.assertEquals(4, firstChildChild.getAdministrativeLevel());\n\n        Assert.assertEquals(3, second.getAdministrativeLevel());\n\n        Assert.assertEquals(1, badEntities.size());\n        logger.info(\"Bad Entity: {}\", badEntities.get(0));\n        logger.info(\"Bad Entity Reason: {}\", badEntities.get(0).getError());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/boundaries/ComplexBoundaryTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.boundaries;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class ComplexBoundaryTestRule extends CoreTestRule\n{\n    private static final String ONE = \"19.2202841, -70.5305208\";\n    private static final String TWO = \"19.2203577, -70.5309603\";\n    private static final String THREE = \"19.2201964, -70.5305353\";\n    private static final String FOUR = \"19.2202682, -70.5309606\";\n    private static final String FIVE = \"19.2203771, -70.5310792\";\n    private static final String SIX = \"19.2202916, -70.5310805\";\n    private static final String SEVEN = \"19.2215199, -70.5307007\";\n    private static final String EIGHT = \"19.2215288, -70.5307861\";\n    private static final String NINE = \"19.2205727, -70.5322487\";\n    private static final String TEN = \"19.220495, -70.5322645\";\n    private static final String ELEVEN = \"19.2189432, -70.5311005\";\n    private static final String TWELVE = \"19.2189336, -70.5310225\";\n\n    @TestAtlas(\n\n            areas = {\n\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"boundary=administrative\",\n                                    \"admin_level=3\", \"name=state1\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = FOUR), @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"boundary=administrative\",\n                                    \"admin_level=4\", \"name=region2\" }) },\n\n            lines = {\n\n                    @Line(id = \"3\", coordinates = { @Loc(value = SEVEN), @Loc(value = EIGHT),\n                            @Loc(value = NINE) }),\n                    @Line(id = \"4\", coordinates = { @Loc(value = NINE), @Loc(value = TEN),\n                            @Loc(value = ELEVEN), @Loc(value = TWELVE), @Loc(value = SEVEN) }),\n                    @Line(id = \"7\", coordinates = { @Loc(value = TEN), @Loc(value = ELEVEN),\n                            @Loc(value = TWELVE), @Loc(value = SEVEN) })\n\n            },\n\n            relations = {\n\n                    @Relation(id = \"5\", members = {\n                            @Member(id = \"2\", role = \"subarea\", type = \"area\"),\n                            @Member(id = \"3\", role = \"outer\", type = \"line\"),\n                            @Member(id = \"4\", role = \"outer\", type = \"line\") }, tags = {\n                                    \"type=boundary\", \"boundary=administrative\", \"admin_level=3\",\n                                    \"name=state5\" }),\n                    @Relation(id = \"6\", members = {\n                            @Member(id = \"1\", role = \"outer\", type = \"area\"),\n                            @Member(id = \"5\", role = \"subarea\", type = \"relation\") }, tags = {\n                                    \"type=boundary\", \"boundary=administrative\", \"admin_level=2\",\n                                    \"name=country6\" }),\n                    // Line 3 and 7 do not match, hence relation 8 and parent 9 should be invalid.\n                    @Relation(id = \"8\", members = {\n                            @Member(id = \"3\", role = \"outer\", type = \"line\"),\n                            @Member(id = \"7\", role = \"outer\", type = \"line\") }, tags = {\n                                    \"type=boundary\", \"boundary=administrative\", \"admin_level=5\",\n                                    \"name=city8\" }),\n                    @Relation(id = \"9\", members = {\n                            @Member(id = \"8\", role = \"subarea\", type = \"relation\"),\n                            @Member(id = \"3\", role = \"outer\", type = \"line\"),\n                            @Member(id = \"4\", role = \"outer\", type = \"line\") }, tags = {\n                                    \"type=boundary\", \"boundary=administrative\", \"admin_level=4\",\n                                    \"name=county9\" })\n\n            }\n\n    )\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/AtlasComplexBuildingTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Test case the verifies we've fixed the problem of negative surface area\n *\n * @author cstaylor\n */\npublic class AtlasComplexBuildingTestCase\n{\n    @Rule\n    public AtlasComplexBuildingTestCaseRule setup = new AtlasComplexBuildingTestCaseRule();\n\n    @Test\n    public void bad()\n    {\n        Assert.assertEquals(0L, this.setup.badBuildings().count());\n    }\n\n    @Test\n    public void good()\n    {\n        Assert.assertEquals(1L, this.setup.goodBuildings().count());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/AtlasComplexBuildingTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area.Known;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Building;\n\n/**\n * Test fixture data for {@link AtlasComplexBuildingTestCase}\n *\n * @author cstaylor\n */\npublic class AtlasComplexBuildingTestCaseRule extends CoreTestRule\n{\n    @TestAtlas(buildings = @Building(id = \"1\", outer = @Area(known = Known.BUILDING_1), inners = @Area(known = Known.BUILDING_2)))\n    private Atlas goodAtlas;\n\n    @TestAtlas(buildings = @Building(id = \"1\", outer = @Area(known = Known.BUILDING_2), inners = @Area(known = Known.BUILDING_1)))\n    private Atlas badAtlas;\n\n    public Stream<ComplexBuilding> badBuildings()\n    {\n        return StreamSupport.stream(\n                new ComplexBuildingFinder().find(this.badAtlas, Finder::ignore).spliterator(),\n                false);\n    }\n\n    public Stream<ComplexBuilding> goodBuildings()\n    {\n        return StreamSupport.stream(\n                new ComplexBuildingFinder().find(this.goodAtlas, Finder::ignore).spliterator(),\n                false);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/BuildingHeightTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Test case for the {@link BuildingHeightTestCase} method\n *\n * @author ajayaswal\n */\npublic class BuildingHeightTestCase\n{\n    @Rule\n    public BuildingHeightTestCaseRule setup = new BuildingHeightTestCaseRule();\n\n    @Test\n    public void shouldContainBaseHeight()\n    {\n        final ComplexBuilding building = this.setup.buildingWithBaseAndTopHeights();\n        Assert.assertTrue(building.baseHeight().isPresent());\n        Assert.assertEquals(193.4, building.baseHeight().get().asMeters(), 0.01);\n    }\n\n    @Test\n    public void shouldContainTopHeight()\n    {\n        final ComplexBuilding building = this.setup.buildingWithBaseAndTopHeights();\n        Assert.assertTrue(building.topHeight().isPresent());\n    }\n\n    @Test\n    public void shouldNotContainBaseHeight()\n    {\n        final ComplexBuilding building = this.setup.buildingWithNoMinHeight();\n        Assert.assertFalse(building.baseHeight().isPresent());\n        Assert.assertTrue(building.topHeight().isPresent());\n    }\n\n    @Test\n    public void shouldParseNegativeBaseHeight()\n    {\n        final ComplexBuilding building = this.setup.buildingWithNegativeMinHeight();\n        Assert.assertTrue(building.baseHeight().isPresent());\n        Assert.assertEquals(-3, building.baseHeight().get().asMeters(), 0.01);\n    }\n\n    @Test\n    public void shouldParseNonNumericBaseHeight()\n    {\n        final ComplexBuilding building = this.setup.buildingWithNonNumericMinHeight();\n        Assert.assertFalse(building.baseHeight().isPresent());\n    }\n\n    @Test\n    public void shouldParseZeroBaseHeight()\n    {\n        final ComplexBuilding building = this.setup.buildingWithZeroMinHeight();\n        Assert.assertTrue(building.baseHeight().isPresent());\n        Assert.assertEquals(0, building.baseHeight().get().asMeters(), 0.01);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/BuildingHeightTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test fixture for the {@link BuildingHeightTestCase}\n *\n * @author ajayaswal\n */\npublic class BuildingHeightTestCaseRule extends CoreTestRule\n{\n    public static final long BUILDING_WITH_ZERO_MIN_HEIGHT_ID = 100000000L;\n    public static final long BUILDING_WITH_NEGATIVE_MIN_HEIGHT_ID = 200000000L;\n    public static final long BUILDING_WITH_BOTH_HEIGHT_ID = 300000000L;\n    public static final long BUILDING_WITH_NO_MIN_HEIGHT_ID = 400000000L;\n    public static final long BUILDING_WITH_NONNUMERIC_MIN_HEIGHT_ID = 500000000L;\n\n    @TestAtlas(loadFromTextResource = \"building_with_minheights.txt\")\n    private Atlas atlasWithBuildingMinHeights;\n\n    public ComplexBuilding buildingWithBaseAndTopHeights()\n    {\n        return Iterables\n                .first(Iterables.filter(\n                        new ComplexBuildingFinder().find(this.atlasWithBuildingMinHeights,\n                                Finder::ignore),\n                        building -> building.getIdentifier() == BUILDING_WITH_BOTH_HEIGHT_ID))\n                .get();\n    }\n\n    public ComplexBuilding buildingWithNegativeMinHeight()\n    {\n        return Iterables.first(Iterables.filter(\n                new ComplexBuildingFinder().find(this.atlasWithBuildingMinHeights, Finder::ignore),\n                building -> building.getIdentifier() == BUILDING_WITH_NEGATIVE_MIN_HEIGHT_ID))\n                .get();\n    }\n\n    public ComplexBuilding buildingWithNoMinHeight()\n    {\n        return Iterables\n                .first(Iterables.filter(\n                        new ComplexBuildingFinder().find(this.atlasWithBuildingMinHeights,\n                                Finder::ignore),\n                        building -> building.getIdentifier() == BUILDING_WITH_NO_MIN_HEIGHT_ID))\n                .get();\n    }\n\n    public ComplexBuilding buildingWithNonNumericMinHeight()\n    {\n        return Iterables.first(Iterables.filter(\n                new ComplexBuildingFinder().find(this.atlasWithBuildingMinHeights, Finder::ignore),\n                building -> building.getIdentifier() == BUILDING_WITH_NONNUMERIC_MIN_HEIGHT_ID))\n                .get();\n    }\n\n    public ComplexBuilding buildingWithZeroMinHeight()\n    {\n        return Iterables\n                .first(Iterables.filter(\n                        new ComplexBuildingFinder().find(this.atlasWithBuildingMinHeights,\n                                Finder::ignore),\n                        building -> building.getIdentifier() == BUILDING_WITH_ZERO_MIN_HEIGHT_ID))\n                .get();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/BuildingLevelsTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Test case for the {@link BuildingLevelsTestCase} method\n *\n * @author ajayaswal\n */\npublic class BuildingLevelsTestCase\n{\n    @Rule\n    public BuildingLevelsTestCaseRule setup = new BuildingLevelsTestCaseRule();\n\n    @Test\n    public void shouldContainValidLevels()\n    {\n        final ComplexBuilding building = this.setup.buildingWithValidLevels();\n        Assert.assertTrue(building.levels().isPresent());\n        Assert.assertEquals(20, building.levels().get(), 0.01);\n        Assert.assertTrue(building.minimumLevel().isPresent());\n        Assert.assertEquals(10, building.minimumLevel().get(), 0.01);\n    }\n\n    @Test\n    public void shouldHandleNonNumericLevels()\n    {\n        final ComplexBuilding building = this.setup.buildingWithNonNumericLevels();\n        Assert.assertFalse(building.levels().isPresent());\n        Assert.assertFalse(building.minimumLevel().isPresent());\n    }\n\n    @Test\n    public void shouldNotContainLevels()\n    {\n        final ComplexBuilding building = this.setup.buildingWithNoLevels();\n        Assert.assertFalse(building.levels().isPresent());\n    }\n\n    @Test\n    public void shouldNotContainMinLevel()\n    {\n        final ComplexBuilding building = this.setup.buildingWithNoMinLevel();\n        Assert.assertFalse(building.minimumLevel().isPresent());\n    }\n\n    @Test\n    public void shouldParseNegativeLevels()\n    {\n        final ComplexBuilding building = this.setup.buildingWithNegativeLevels();\n        Assert.assertTrue(building.levels().isPresent());\n        Assert.assertEquals(-1, building.levels().get(), 0.01);\n        Assert.assertTrue(building.minimumLevel().isPresent());\n        Assert.assertEquals(-3, building.minimumLevel().get(), 0.01);\n    }\n\n    @Test\n    public void shouldParseZeroLevels()\n    {\n        final ComplexBuilding building = this.setup.buildingWithZeroLevels();\n        Assert.assertTrue(building.levels().isPresent());\n        Assert.assertEquals(0, building.levels().get(), 0.01);\n        Assert.assertTrue(building.minimumLevel().isPresent());\n        Assert.assertEquals(0, building.minimumLevel().get(), 0.01);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/BuildingLevelsTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test fixture for the {@link BuildingLevelsTestCase}\n *\n * @author ajayaswal\n */\npublic class BuildingLevelsTestCaseRule extends CoreTestRule\n{\n    public static final long BUILDING_WITH_ZERO_LEVELS_ID = 100000000L;\n    public static final long BUILDING_WITH_NEGATIVE_LEVELS_ID = 200000000L;\n    public static final long BUILDING_WITH_NONNUMERIC_LEVELS_ID = 300000000L;\n    public static final long BUILDING_WITH_VALID_LEVELS_ID = 400000000L;\n    public static final long BUILDING_WITH_NO_MIN_LEVEL_ID = 500000000L;\n    public static final long BUILDING_WITH_NO_LEVELS_ID = 600000000L;\n\n    @TestAtlas(loadFromTextResource = \"building_with_levels.txt\")\n    private Atlas atlasWithBuildingLevels;\n\n    public ComplexBuilding buildingWithNegativeLevels()\n    {\n        return Iterables\n                .first(Iterables.filter(\n                        new ComplexBuildingFinder().find(this.atlasWithBuildingLevels,\n                                Finder::ignore),\n                        building -> building.getIdentifier() == BUILDING_WITH_NEGATIVE_LEVELS_ID))\n                .get();\n    }\n\n    public ComplexBuilding buildingWithNoLevels()\n    {\n        return Iterables.first(Iterables.filter(\n                new ComplexBuildingFinder().find(this.atlasWithBuildingLevels, Finder::ignore),\n                building -> building.getIdentifier() == BUILDING_WITH_NO_LEVELS_ID)).get();\n    }\n\n    public ComplexBuilding buildingWithNoMinLevel()\n    {\n        return Iterables\n                .first(Iterables.filter(\n                        new ComplexBuildingFinder().find(this.atlasWithBuildingLevels,\n                                Finder::ignore),\n                        building -> building.getIdentifier() == BUILDING_WITH_NO_MIN_LEVEL_ID))\n                .get();\n    }\n\n    public ComplexBuilding buildingWithNonNumericLevels()\n    {\n        return Iterables\n                .first(Iterables.filter(\n                        new ComplexBuildingFinder().find(this.atlasWithBuildingLevels,\n                                Finder::ignore),\n                        building -> building.getIdentifier() == BUILDING_WITH_NONNUMERIC_LEVELS_ID))\n                .get();\n    }\n\n    public ComplexBuilding buildingWithValidLevels()\n    {\n        return Iterables\n                .first(Iterables.filter(\n                        new ComplexBuildingFinder().find(this.atlasWithBuildingLevels,\n                                Finder::ignore),\n                        building -> building.getIdentifier() == BUILDING_WITH_VALID_LEVELS_ID))\n                .get();\n    }\n\n    public ComplexBuilding buildingWithZeroLevels()\n    {\n        return Iterables\n                .first(Iterables.filter(\n                        new ComplexBuildingFinder().find(this.atlasWithBuildingLevels,\n                                Finder::ignore),\n                        building -> building.getIdentifier() == BUILDING_WITH_ZERO_LEVELS_ID))\n                .get();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/BuildingsContainsOsmIdentifierTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Test case for the {@link ComplexBuilding#containsOSMIdentifier(long)} method\n *\n * @author cstaylor\n */\npublic class BuildingsContainsOsmIdentifierTestCase\n{\n    @Rule\n    public BuildingsContainsOsmIdentifierTestCaseRule setup = new BuildingsContainsOsmIdentifierTestCaseRule();\n\n    @Test\n    public void shouldContain()\n    {\n        final ComplexBuilding building = this.setup.buildingWithBlocks();\n        Assert.assertTrue(building.containsOSMIdentifier(\n                BuildingsContainsOsmIdentifierTestCaseRule.CONTAINS_OSM_IDENTIFIER));\n    }\n\n    @Test\n    public void shouldNotContain()\n    {\n        final ComplexBuilding building = this.setup.buildingWithBlocks();\n        Assert.assertFalse(building.containsOSMIdentifier(\n                BuildingsContainsOsmIdentifierTestCaseRule.DOES_NOT_CONTAIN_OSM_IDENTIFIER));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/BuildingsContainsOsmIdentifierTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test fixture for the {@link BuildingsContainsOsmIdentifierTestCase}\n *\n * @author cstaylor\n */\npublic class BuildingsContainsOsmIdentifierTestCaseRule extends CoreTestRule\n{\n    public static final long CONTAINS_OSM_IDENTIFIER = 250786481;\n\n    public static final long DOES_NOT_CONTAIN_OSM_IDENTIFIER = 250786482;\n\n    @TestAtlas(loadFromTextResource = \"building_block_atlas.txt\")\n    private Atlas atlas;\n\n    public ComplexBuilding buildingWithBlocks()\n    {\n        return Iterables.first(new ComplexBuildingFinder().find(this.atlas, Finder::ignore)).get();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/HeightConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.buildings;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * @author matthieun\n */\npublic class HeightConverterTest\n{\n    @Test\n    public void testConversion()\n    {\n        Assert.assertEquals(Distance.ONE_METER, new HeightConverter().convert(\"1 m\"));\n        Assert.assertEquals(Distance.ONE_METER, new HeightConverter().convert(\"1\"));\n        Assert.assertEquals(0.3556, new HeightConverter().convert(\"1\\'2\\\"\").asMeters(), 2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/ComplexHighwayAreaTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Verifies test cases for Highway Areas from input files with valid and invalid cases\n *\n * @author isabellehillberg\n */\npublic class ComplexHighwayAreaTestCase\n{\n    @Rule\n    public ComplexHighwayAreaTestCaseRule setup = new ComplexHighwayAreaTestCaseRule();\n\n    @Test\n    public void failedToGetHighwayArea()\n    {\n        Assert.assertEquals(0, Iterables.count(this.setup.invalidHighwayArea(), i -> 1L));\n    }\n\n    @Test\n    public void shouldHaveHighwayArea()\n    {\n        Assert.assertEquals(1, Iterables.count(this.setup.validHighwayArea(), i -> 1L));\n        Assert.assertEquals(1, Iterables.count(this.setup.validHighwayArea1(), i -> 1L));\n        Assert.assertEquals(4, Iterables.count(this.setup.validHighwayArea2(), i -> 1L));\n    }\n\n    @Test\n    public void shouldNotHaveHighwayArea()\n    {\n        Assert.assertEquals(0, Iterables.count(this.setup.noHighwayArea(), i -> 1L));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/ComplexHighwayAreaTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StreamIterable;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test data for {@link ComplexHighwayAreaTestCase}\n *\n * @author isabellehillberg\n */\npublic class ComplexHighwayAreaTestCaseRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"validHighwayArea_1.txt.gz\")\n    private Atlas validAtlas1;\n\n    @TestAtlas(loadFromTextResource = \"validHighwayArea.txt.gz\")\n    private Atlas validAtlas;\n\n    @TestAtlas(loadFromTextResource = \"validHighwayArea_2.txt.gz\")\n    private Atlas validAtlas2;\n\n    @TestAtlas(loadFromTextResource = \"noHighwayArea.txt.gz\")\n    private Atlas noHighwayAreaAtlas;\n\n    @TestAtlas(loadFromTextResource = \"invalidHighwayArea.txt.gz\")\n    private Atlas invalidHighwayAreaAtlas;\n\n    public StreamIterable<ComplexHighwayArea> invalidHighwayArea()\n    {\n        return Iterables.stream(\n                new ComplexHighwayAreaFinder().find(this.invalidHighwayAreaAtlas, Finder::ignore));\n    }\n\n    public StreamIterable<ComplexHighwayArea> noHighwayArea()\n    {\n        return Iterables.stream(\n                new ComplexHighwayAreaFinder().find(this.noHighwayAreaAtlas, Finder::ignore));\n    }\n\n    public StreamIterable<ComplexHighwayArea> validHighwayArea()\n    {\n        return Iterables\n                .stream(new ComplexHighwayAreaFinder().find(this.validAtlas, Finder::ignore));\n    }\n\n    public StreamIterable<ComplexHighwayArea> validHighwayArea1()\n    {\n        return Iterables\n                .stream(new ComplexHighwayAreaFinder().find(this.validAtlas1, Finder::ignore));\n    }\n\n    public StreamIterable<ComplexHighwayArea> validHighwayArea2()\n    {\n        return Iterables\n                .stream(new ComplexHighwayAreaFinder().find(this.validAtlas2, Finder::ignore));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/OutOfOrderEdgesHighwayAreaTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Test case for highway areas that have way-sectioned edged appearing out of order when retrieving\n * them from an Atlas\n *\n * @author cstaylor\n */\npublic class OutOfOrderEdgesHighwayAreaTestCase\n{\n    @Rule\n    public OutOfOrderEdgesHighwayAreaTestCaseRule setup = new OutOfOrderEdgesHighwayAreaTestCaseRule();\n\n    @Test\n    public void lowestIdentifier()\n    {\n        final ComplexHighwayArea highwayArea = this.setup.invalidHighwayArea();\n        Assert.assertEquals(highwayArea.getVisitedEdgeIdentifiers().first().longValue(),\n                highwayArea.getSource().getIdentifier());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/OutOfOrderEdgesHighwayAreaTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test case rule for the {@link OutOfOrderEdgesHighwayAreaTestCase}\n *\n * @author cstaylor\n */\npublic class OutOfOrderEdgesHighwayAreaTestCaseRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"out_of_order_highway_area.txt.gz\")\n    private Atlas outOfOrderAtlas;\n\n    public ComplexHighwayArea invalidHighwayArea()\n    {\n        return Iterables\n                .first(new ComplexHighwayAreaFinder().find(this.outOfOrderAtlas, Finder::ignore))\n                .get();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/SelfIntersectingHighwayAreaTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Test case for self intersecting highway areas\n *\n * @author isabellehillberg\n */\npublic class SelfIntersectingHighwayAreaTestCase\n{\n    @Rule\n    public SelfIntersectingHighwayAreaTestCaseRule setup = new SelfIntersectingHighwayAreaTestCaseRule();\n\n    @Test\n    public void intersectingHighwayArea()\n    {\n        this.setup.findSelfIntersecting().map(ComplexEntity::getSource)\n                .map(AtlasEntity::getOsmIdentifier).forEach(System.out::println);\n        Assert.assertEquals(0, Iterables.count(this.setup.findSelfIntersecting(), i -> 1L));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/SelfIntersectingHighwayAreaTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StreamIterable;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test case rule for {@link SelfIntersectingHighwayAreaTestCase}\n *\n * @author isabellehillberg\n */\npublic class SelfIntersectingHighwayAreaTestCaseRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"intersection.txt.gz\")\n    private Atlas intersectionAtlas;\n\n    public StreamIterable<ComplexHighwayArea> findSelfIntersecting()\n    {\n        return Iterables\n                .stream(new ComplexHighwayAreaFinder().find(this.intersectionAtlas, Finder::ignore))\n                .filter(ComplexHighwayArea::isSelfIntersecting);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/ZeroSizeHighwayAreaTestCase.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.ComplexEntity;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Test case for size zero highway areas\n *\n * @author isabellehillberg\n */\npublic class ZeroSizeHighwayAreaTestCase\n{\n    @Rule\n    public ZeroSizeHighwayAreaTestCaseRule setup = new ZeroSizeHighwayAreaTestCaseRule();\n\n    @Test\n    public void zeroSizeOTA()\n    {\n        System.out.print(\"Zero size ota: \");\n        this.setup.findZeroSized().map(ComplexEntity::getSource).map(AtlasEntity::getOsmIdentifier)\n                .forEach(System.out::println);\n        Assert.assertEquals(0, Iterables.count(this.setup.findZeroSized(), i -> 1L));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/highwayarea/ZeroSizeHighwayAreaTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.highwayarea;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StreamIterable;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test case rule for {@link ZeroSizeHighwayAreaTestCase}\n *\n * @author isabellehillberg\n */\npublic class ZeroSizeHighwayAreaTestCaseRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"size_zero.txt.gz\")\n    private Atlas zeroSizeAtlas;\n\n    public StreamIterable<ComplexHighwayArea> findZeroSized()\n    {\n        return Iterables\n                .stream(new ComplexHighwayAreaFinder().find(this.zeroSizeAtlas, Finder::ignore))\n                .filter(ComplexHighwayArea::isZeroSized);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/islands/ComplexIslandFinderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.islands;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author sbhalekar\n */\npublic class ComplexIslandFinderTest\n{\n    @Rule\n    public ComplexIslandFinderTestRule rule = new ComplexIslandFinderTestRule();\n\n    private final ComplexIslandFinder complexIslandFinder = new ComplexIslandFinder();\n\n    @Test\n    public void testInvalidAreaAtlas()\n    {\n        Assert.assertEquals(0, Iterables.size(this.complexIslandFinder\n                .find(this.rule.getAtlasWithInvalidAreaIsland(), Finder::ignore)));\n    }\n\n    @Test\n    public void testValidAreaAtlas()\n    {\n        Assert.assertEquals(1, Iterables.size(this.complexIslandFinder\n                .find(this.rule.getAtlasWithValidAreaIsland(), Finder::ignore)));\n    }\n\n    @Test\n    public void testValidRelationIsland()\n    {\n        Assert.assertEquals(1, Iterables.size(this.complexIslandFinder\n                .find(this.rule.getValidIsletRelationAtlas(), Finder::ignore)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/islands/ComplexIslandFinderTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.islands;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author sbhalekar\n */\npublic class ComplexIslandFinderTestRule extends CoreTestRule\n{\n    public static final String ISLAND_ID_STRING = \"100000000\";\n    public static final String RELATION_ID_STRING_ONE = \"200000000\";\n\n    private static final String ONE = \"40.0000001, -80.0000003\";\n    private static final String TWO = \"40.0000001, -80.0000001\";\n    private static final String THREE = \"40.0000003, -80.0000001\";\n    private static final String FOUR = \"40.0000003, -80.0000003\";\n\n    @TestAtlas(areas = { @TestAtlas.Area(id = ISLAND_ID_STRING, coordinates = {\n            @TestAtlas.Loc(value = ONE), @TestAtlas.Loc(value = TWO), @TestAtlas.Loc(value = THREE),\n            @TestAtlas.Loc(value = FOUR) }) }, relations = {\n                    @TestAtlas.Relation(id = RELATION_ID_STRING_ONE, tags = { \"type=multipolygon\",\n                            \"place=islet\" }, members = {\n                                    @TestAtlas.Relation.Member(id = ISLAND_ID_STRING, type = \"area\", role = \"outer\") }, wkt = \"MULTIPOLYGON (((-80.0000003 40.0000001, -80.0000001 40.0000001, -80.0000001 40.0000003, -80.0000003 40.0000003, -80.0000003 40.0000001)))\") })\n    private Atlas atlasWithValidIsletRelation;\n\n    @TestAtlas(areas = { @TestAtlas.Area(id = ISLAND_ID_STRING, coordinates = {\n            @TestAtlas.Loc(value = ONE), @TestAtlas.Loc(value = TWO), @TestAtlas.Loc(value = THREE),\n            @TestAtlas.Loc(value = FOUR) }, tags = { \"natural=island\" }) })\n    private Atlas atlasWithValidIslandArea;\n\n    @TestAtlas(areas = { @TestAtlas.Area(id = ISLAND_ID_STRING, coordinates = {\n            @TestAtlas.Loc(value = ONE), @TestAtlas.Loc(value = TWO), @TestAtlas.Loc(value = THREE),\n            @TestAtlas.Loc(value = FOUR) }, tags = { \"natural=islet\" }) })\n    private Atlas atlasWithInvalidIslandArea;\n\n    public Atlas getAtlasWithInvalidAreaIsland()\n    {\n        return this.atlasWithInvalidIslandArea;\n    }\n\n    public Atlas getAtlasWithValidAreaIsland()\n    {\n        return this.atlasWithValidIslandArea;\n    }\n\n    public Atlas getValidIsletRelationAtlas()\n    {\n        return this.atlasWithValidIsletRelation;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/landcover/ComplexLandCoverFinderTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.landcover;\n\nimport java.util.stream.StreamSupport;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\n\n/**\n * @author samg\n */\npublic class ComplexLandCoverFinderTest\n{\n    @Rule\n    public ComplexLandCoverFinderTestRule rule = new ComplexLandCoverFinderTestRule();\n\n    @Test\n    public void testComplexLandCoverWithCustomFilter()\n    {\n        final Atlas atlas = this.rule.getComplexLandCoverWithRelationsAndAreas();\n        final ComplexLandCoverFinder landCoverRelationFinder = new ComplexLandCoverFinder();\n        final Iterable<ComplexLandCover> complexLandCovers = landCoverRelationFinder.find(atlas,\n                TaggableFilter.forDefinition(\"landuse->VINEYARD|surface->paved\"));\n        Assert.assertEquals(2,\n                StreamSupport.stream(complexLandCovers.spliterator(), false).count());\n    }\n\n    @Test\n    public void testLandCoverArea()\n    {\n        final Atlas atlas = this.rule.getLandCoverAreaAtlas();\n        final ComplexLandCoverFinder landCoverRelationFinder = new ComplexLandCoverFinder();\n        final Iterable<ComplexLandCover> complexLandCoverAreas = landCoverRelationFinder\n                .find(atlas);\n        Assert.assertEquals(2,\n                StreamSupport.stream(complexLandCoverAreas.spliterator(), false).count());\n    }\n\n    @Test\n    public void testMultipolygonLandCoverRelation()\n    {\n        final Atlas atlas = this.rule.getMultipolygonLandCoverRelationAtlas();\n        final ComplexLandCoverFinder landCoverRelationFinder = new ComplexLandCoverFinder();\n        final Iterable<ComplexLandCover> complexLandCoverRelations = landCoverRelationFinder\n                .find(atlas);\n        Assert.assertEquals(2,\n                StreamSupport.stream(complexLandCoverRelations.spliterator(), false).count());\n    }\n\n    @Test\n    public void testNonMultipolygonLandCoverRelation()\n    {\n        final Atlas atlas = this.rule.getNonMultipolygonLandCoverRelationAtlas();\n        final ComplexLandCoverFinder landCoverRelationFinder = new ComplexLandCoverFinder();\n        final Iterable<ComplexLandCover> complexLandCoverRelations = landCoverRelationFinder\n                .find(atlas);\n        Assert.assertFalse(complexLandCoverRelations.iterator().hasNext());\n        Assert.assertEquals(0,\n                StreamSupport.stream(complexLandCoverRelations.spliterator(), false).count());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/landcover/ComplexLandCoverFinderTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.landcover;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author samg\n */\npublic class ComplexLandCoverFinderTestRule extends CoreTestRule\n{\n    private static final String LOCATION_ONE = \"1.43958970913, 103.91306306772\";\n    private static final String LOCATION_TWO = \"1.42773565932, 103.90488839864\";\n    private static final String LOCATION_THREE = \"1.43419031086, 103.89654055711\";\n    private static final String LOCATION_FOUR = \"1.41407426416, 103.89600156794\";\n    private static final String LOCATION_FIVE = \"1.40850639889, 103.91621366183\";\n    private static final String LOCATION_SIX = \"1.39835848123, 103.92322052105\";\n    private static final String LOCATION_SEVEN = \"1.41146994174, 103.94549874009\";\n    private static final String LOCATION_EIGHT = \"1.41155974601, 103.96373454036\";\n    private static final String LOCATION_NINE = \"1.43419031086, 103.97038207346\";\n    private static final String LOCATION_TEN = \"1.45116308784, 103.96454302412\";\n    private static final String LOCATION_ELEVEN = \"1.45233052286, 103.93786306018\";\n    private static final String LOCATION_TWELVE = \"1.45367756252, 103.91845945004\";\n    private static final String LOCATION_THIRTEEN = \"1.41193016458, 103.91459020371\";\n    private static final String LOCATION_FOURTEEN = \"1.41624076477, 103.94180915682\";\n    private static final String LOCATION_FIFTEEN = \"1.42638860433, 103.94621090171\";\n    private static final String LOCATION_SIXTEEN = \"1.43141760561, 103.96399754433\";\n    private static final String LOCATION_SEVENTEEN = \"1.44273281808, 103.95447540232\";\n    private static final String LOCATION_EIGHTEEN = \"1.43842226756, 103.93857522179\";\n    private static final String LOCATION_NINETEEN = \"1.44839040324, 103.9256394817\";\n    private static final String LOCATION_TWENTY = \"1.4507140742, 103.9069610144\";\n\n    @TestAtlas(points = { @Point(id = \"39008\", coordinates = @Loc(value = LOCATION_TWENTY)),\n            @Point(id = \"39009\", coordinates = @Loc(value = LOCATION_THREE)),\n            @Point(id = \"39011\", coordinates = @Loc(value = LOCATION_FOUR)),\n            @Point(id = \"39013\", coordinates = @Loc(value = LOCATION_FIVE)),\n            @Point(id = \"39015\", coordinates = @Loc(value = LOCATION_SIX)),\n            @Point(id = \"38985\", coordinates = @Loc(value = LOCATION_ONE)),\n            @Point(id = \"39019\", coordinates = @Loc(value = LOCATION_EIGHT)),\n            @Point(id = \"38988\", coordinates = @Loc(value = LOCATION_TWO)),\n            @Point(id = \"39021\", coordinates = @Loc(value = LOCATION_NINE)),\n            @Point(id = \"39023\", coordinates = @Loc(value = LOCATION_TEN)),\n            @Point(id = \"38992\", coordinates = @Loc(value = LOCATION_THIRTEEN)),\n            @Point(id = \"39025\", coordinates = @Loc(value = LOCATION_ELEVEN)),\n            @Point(id = \"38994\", coordinates = @Loc(value = LOCATION_FOURTEEN)),\n            @Point(id = \"39027\", coordinates = @Loc(value = LOCATION_TWELVE)),\n            @Point(id = \"38996\", coordinates = @Loc(value = LOCATION_FIFTEEN)),\n            @Point(id = \"38998\", coordinates = @Loc(value = LOCATION_SIXTEEN)),\n            @Point(id = \"39017\", coordinates = @Loc(value = LOCATION_SEVEN)),\n            @Point(id = \"39000\", coordinates = @Loc(value = LOCATION_SEVENTEEN)),\n            @Point(id = \"39002\", coordinates = @Loc(value = LOCATION_EIGHTEEN)),\n            @Point(id = \"39004\", coordinates = @Loc(value = LOCATION_NINETEEN)) }, areas = {\n                    @Area(id = \"39010\", coordinates = { @Loc(value = LOCATION_TWENTY),\n                            @Loc(value = LOCATION_THREE), @Loc(value = LOCATION_FOUR),\n                            @Loc(value = LOCATION_FIVE), @Loc(value = LOCATION_SIX),\n                            @Loc(value = LOCATION_SEVEN), @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE), @Loc(value = LOCATION_TEN),\n                            @Loc(value = LOCATION_ELEVEN), @Loc(value = LOCATION_TWELVE),\n                            @Loc(value = LOCATION_TWENTY) }),\n                    @Area(id = \"38989\", coordinates = { @Loc(value = LOCATION_ONE),\n                            @Loc(value = LOCATION_TWO), @Loc(value = LOCATION_THIRTEEN),\n                            @Loc(value = LOCATION_FOURTEEN), @Loc(value = LOCATION_FIFTEEN),\n                            @Loc(value = LOCATION_SIXTEEN), @Loc(value = LOCATION_SEVENTEEN),\n                            @Loc(value = LOCATION_EIGHTEEN), @Loc(value = LOCATION_NINETEEN),\n                            @Loc(value = LOCATION_ONE) }),\n                    @Area(id = \"38987\", coordinates = { @Loc(value = LOCATION_ELEVEN),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_EIGHTEEN),\n                            @Loc(value = LOCATION_SEVENTEEN) }) }, relations = {\n                                    @Relation(id = \"39190\", members = {\n                                            @Member(id = \"39010\", type = \"area\", role = \"outer\"),\n                                            @Member(id = \"38989\", type = \"area\", role = \"inner\") }, tags = {\n                                                    \"type=multipolygon\", \"surface=paved\" }),\n                                    @Relation(id = \"39990\", members = {\n                                            @Member(id = \"38987\", type = \"area\", role = \"outer\") }, tags = {\n                                                    \"type=boundary\", \"landuse=CEMETERY\" }) })\n    private Atlas multipolygonLandCoverRelationAtlas;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @Node(id = \"10020\", coordinates = @Loc(value = LOCATION_SEVEN)),\n                    @Node(id = \"21001\", coordinates = @Loc(value = LOCATION_EIGHT)),\n                    @Node(id = \"31233\", coordinates = @Loc(value = LOCATION_NINE)) },\n            // edges\n            edges = {\n                    @Edge(id = \"12333\", coordinates = { @Loc(value = LOCATION_SEVEN),\n                            @Loc(value = LOCATION_EIGHT) }, tags = { \"highway=road\" }),\n                    @Edge(id = \"23332\", coordinates = { @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE) }, tags = { \"highway=road\" }),\n                    @Edge(id = \"31223\", coordinates = { @Loc(value = LOCATION_NINE),\n                            @Loc(value = LOCATION_SEVEN) }, tags = { \"highway=road\" }) },\n            // relations\n            relations = { @Relation(id = \"89765\", members = {\n                    @Member(id = \"12333\", type = \"edge\", role = RelationTypeTag.RESTRICTION_ROLE_FROM),\n                    @Member(id = \"21001\", type = \"node\", role = RelationTypeTag.RESTRICTION_ROLE_VIA),\n                    @Member(id = \"31223\", type = \"edge\", role = RelationTypeTag.RESTRICTION_ROLE_TO) }, tags = {\n                            \"restriction=no_u_turn\", \"landuse=VILLAGE\" }) })\n    private Atlas nonMultipolygonLandCoverRelationAtlas;\n\n    @TestAtlas(points = { @Point(id = \"39008\", coordinates = @Loc(value = LOCATION_TWENTY)),\n            @Point(id = \"39009\", coordinates = @Loc(value = LOCATION_THREE)),\n            @Point(id = \"39011\", coordinates = @Loc(value = LOCATION_FOUR)),\n            @Point(id = \"39013\", coordinates = @Loc(value = LOCATION_FIVE)),\n            @Point(id = \"39015\", coordinates = @Loc(value = LOCATION_SIX)),\n            @Point(id = \"38985\", coordinates = @Loc(value = LOCATION_ONE)),\n            @Point(id = \"39019\", coordinates = @Loc(value = LOCATION_EIGHT)),\n            @Point(id = \"38988\", coordinates = @Loc(value = LOCATION_TWO)),\n            @Point(id = \"39021\", coordinates = @Loc(value = LOCATION_NINE)),\n            @Point(id = \"39023\", coordinates = @Loc(value = LOCATION_TEN)),\n            @Point(id = \"38992\", coordinates = @Loc(value = LOCATION_THIRTEEN)),\n            @Point(id = \"39025\", coordinates = @Loc(value = LOCATION_ELEVEN)),\n            @Point(id = \"38994\", coordinates = @Loc(value = LOCATION_FOURTEEN)),\n            @Point(id = \"39027\", coordinates = @Loc(value = LOCATION_TWELVE)),\n            @Point(id = \"38996\", coordinates = @Loc(value = LOCATION_FIFTEEN)),\n            @Point(id = \"38998\", coordinates = @Loc(value = LOCATION_SIXTEEN)),\n            @Point(id = \"39017\", coordinates = @Loc(value = LOCATION_SEVEN)),\n            @Point(id = \"39000\", coordinates = @Loc(value = LOCATION_SEVENTEEN)),\n            @Point(id = \"39002\", coordinates = @Loc(value = LOCATION_EIGHTEEN)),\n            @Point(id = \"39004\", coordinates = @Loc(value = LOCATION_NINETEEN)) }, areas = {\n                    @Area(id = \"39010\", coordinates = { @Loc(value = LOCATION_TWENTY),\n                            @Loc(value = LOCATION_THREE), @Loc(value = LOCATION_FOUR),\n                            @Loc(value = LOCATION_FIVE), @Loc(value = LOCATION_SIX),\n                            @Loc(value = LOCATION_SEVEN), @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE), @Loc(value = LOCATION_TEN),\n                            @Loc(value = LOCATION_ELEVEN), @Loc(value = LOCATION_TWELVE),\n                            @Loc(value = LOCATION_TWENTY) }),\n                    @Area(id = \"38989\", coordinates = { @Loc(value = LOCATION_ONE),\n                            @Loc(value = LOCATION_TWO), @Loc(value = LOCATION_THIRTEEN),\n                            @Loc(value = LOCATION_FOURTEEN), @Loc(value = LOCATION_FIFTEEN),\n                            @Loc(value = LOCATION_SIXTEEN), @Loc(value = LOCATION_SEVENTEEN),\n                            @Loc(value = LOCATION_EIGHTEEN), @Loc(value = LOCATION_NINETEEN),\n                            @Loc(value = LOCATION_ONE) }, tags = \"natural=water\"),\n                    @Area(id = \"38987\", coordinates = { @Loc(value = LOCATION_ELEVEN),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_EIGHTEEN),\n                            @Loc(value = LOCATION_SEVENTEEN) }, tags = \"landuse=meadow\") })\n    private Atlas landCoverAreaAtlas;\n\n    @TestAtlas(points = { @Point(id = \"39008\", coordinates = @Loc(value = LOCATION_TWENTY)),\n            @Point(id = \"39009\", coordinates = @Loc(value = LOCATION_THREE)),\n            @Point(id = \"39011\", coordinates = @Loc(value = LOCATION_FOUR)),\n            @Point(id = \"39013\", coordinates = @Loc(value = LOCATION_FIVE)),\n            @Point(id = \"39015\", coordinates = @Loc(value = LOCATION_SIX)),\n            @Point(id = \"38985\", coordinates = @Loc(value = LOCATION_ONE)),\n            @Point(id = \"39019\", coordinates = @Loc(value = LOCATION_EIGHT)),\n            @Point(id = \"38988\", coordinates = @Loc(value = LOCATION_TWO)),\n            @Point(id = \"39021\", coordinates = @Loc(value = LOCATION_NINE)),\n            @Point(id = \"39023\", coordinates = @Loc(value = LOCATION_TEN)),\n            @Point(id = \"38992\", coordinates = @Loc(value = LOCATION_THIRTEEN)),\n            @Point(id = \"39025\", coordinates = @Loc(value = LOCATION_ELEVEN)),\n            @Point(id = \"38994\", coordinates = @Loc(value = LOCATION_FOURTEEN)),\n            @Point(id = \"39027\", coordinates = @Loc(value = LOCATION_TWELVE)),\n            @Point(id = \"38996\", coordinates = @Loc(value = LOCATION_FIFTEEN)),\n            @Point(id = \"38998\", coordinates = @Loc(value = LOCATION_SIXTEEN)),\n            @Point(id = \"39017\", coordinates = @Loc(value = LOCATION_SEVEN)),\n            @Point(id = \"39000\", coordinates = @Loc(value = LOCATION_SEVENTEEN)),\n            @Point(id = \"39002\", coordinates = @Loc(value = LOCATION_EIGHTEEN)),\n            @Point(id = \"39004\", coordinates = @Loc(value = LOCATION_NINETEEN)) }, areas = {\n                    @Area(id = \"39010\", coordinates = { @Loc(value = LOCATION_TWENTY),\n                            @Loc(value = LOCATION_THREE), @Loc(value = LOCATION_FOUR),\n                            @Loc(value = LOCATION_FIVE), @Loc(value = LOCATION_SIX),\n                            @Loc(value = LOCATION_SEVEN), @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_NINE), @Loc(value = LOCATION_TEN),\n                            @Loc(value = LOCATION_ELEVEN), @Loc(value = LOCATION_TWELVE),\n                            @Loc(value = LOCATION_TWENTY) }),\n                    @Area(id = \"38989\", coordinates = { @Loc(value = LOCATION_ONE),\n                            @Loc(value = LOCATION_TWO), @Loc(value = LOCATION_THIRTEEN),\n                            @Loc(value = LOCATION_FOURTEEN), @Loc(value = LOCATION_FIFTEEN),\n                            @Loc(value = LOCATION_SIXTEEN), @Loc(value = LOCATION_SEVENTEEN),\n                            @Loc(value = LOCATION_EIGHTEEN), @Loc(value = LOCATION_NINETEEN),\n                            @Loc(value = LOCATION_ONE) }),\n                    @Area(id = \"38987\", coordinates = { @Loc(value = LOCATION_ELEVEN),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_EIGHTEEN),\n                            @Loc(value = LOCATION_SEVENTEEN) }),\n                    @Area(id = \"45677\", coordinates = { @Loc(value = LOCATION_EIGHT),\n                            @Loc(value = LOCATION_TEN), @Loc(value = LOCATION_FIVE),\n                            @Loc(value = LOCATION_FOUR) }, tags = {\n                                    \"amenity=SCHOOL\" }) }, relations = {\n                                            @Relation(id = \"39190\", members = {\n                                                    @Member(id = \"39010\", type = \"area\", role = \"outer\"),\n                                                    @Member(id = \"38989\", type = \"area\", role = \"inner\") }, tags = {\n                                                            \"type=multipolygon\", \"surface=paved\" }),\n                                            @Relation(id = \"39990\", members = {\n                                                    @Member(id = \"38987\", type = \"area\", role = \"outer\") }, tags = {\n                                                            \"type=boundary\",\n                                                            \"landuse=VINEYARD\" }) })\n    private Atlas complexLandCoverWithRelationsAndAreas;\n\n    public Atlas getComplexLandCoverWithRelationsAndAreas()\n    {\n        return this.complexLandCoverWithRelationsAndAreas;\n    }\n\n    public Atlas getLandCoverAreaAtlas()\n    {\n        return this.landCoverAreaAtlas;\n    }\n\n    public Atlas getMultipolygonLandCoverRelationAtlas()\n    {\n        return this.multipolygonLandCoverRelationAtlas;\n    }\n\n    public Atlas getNonMultipolygonLandCoverRelationAtlas()\n    {\n        return this.nonMultipolygonLandCoverRelationAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/restriction/ComplexTurnRestrictionTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.restriction;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.items.TurnRestriction;\nimport org.openstreetmap.atlas.geography.atlas.items.TurnRestriction.TurnRestrictionType;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNode;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.BigNodeFinder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.bignode.RestrictedPath;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.tags.TurnRestrictionTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n * @author mgostintsev\n */\npublic class ComplexTurnRestrictionTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(ComplexTurnRestrictionTest.class);\n\n    @Rule\n    public ComplexTurnRestrictionTestCaseRule rule = new ComplexTurnRestrictionTestCaseRule();\n\n    @Test\n    public void testBigNodeWithNoRestrictions()\n    {\n        final Atlas atlas = new TextAtlasBuilder().read(new InputStreamResource(\n                () -> ComplexTurnRestrictionTest.class.getResourceAsStream(\"bigNode.txt.gz\"))\n                .withDecompressor(Decompressor.GZIP).withName(\"bigNode.txt.gz\"));\n\n        final List<BigNode> bigNodes = Iterables.asList(new BigNodeFinder().find(atlas));\n\n        final Optional<BigNode> possibleBigNode = bigNodes.stream()\n                .filter(bigNode -> bigNode.getOsmIdentifier() == 3717537957L).findAny();\n\n        Assert.assertTrue(possibleBigNode.isPresent());\n\n        final BigNode bigNode = possibleBigNode.get();\n\n        Assert.assertTrue(!bigNode.allPaths().isEmpty());\n        Assert.assertTrue(bigNode.turnRestrictions().isEmpty());\n    }\n\n    @Test\n    public void testBrokenRoute()\n    {\n        final List<Integer> counter = new ArrayList<>();\n        counter.add(0);\n        new ComplexTurnRestrictionFinder(null).find(this.rule.getAtlasBrokenTurnRestrictionRoute(),\n                broken -> counter.set(0, counter.get(0) + 1)).forEach(System.out::println);\n        Assert.assertEquals(new Integer(1), counter.get(0));\n    }\n\n    @Test\n    public void testFalsePredicate()\n    {\n        Assert.assertEquals(0, Iterables.count(new ComplexTurnRestrictionFinder(x -> false)\n                .find(this.rule.getAtlasNo(), Finder::ignore), x -> 1L));\n    }\n\n    @Test\n    public void testNoRestriction()\n    {\n        final Atlas atlasNo = this.rule.getAtlasNo();\n        logger.trace(\"AtlasNo: {}\", atlasNo);\n        final Route candidate = Route.forEdges(atlasNo.edge(102), atlasNo.edge(203));\n        int counter = 0;\n        for (final ComplexTurnRestriction restriction : new ComplexTurnRestrictionFinder()\n                .find(atlasNo, Finder::ignore))\n        {\n            if (restriction.getTurnRestriction().getTurnRestrictionType() == TurnRestrictionType.NO)\n            {\n                final Route route = restriction.route();\n                if (candidate.equals(route))\n                {\n                    counter++;\n                }\n            }\n        }\n        Assert.assertEquals(1, counter);\n        int counterRestriction = 0;\n        final Iterable<BigNode> bigNodes = new BigNodeFinder().find(atlasNo, Finder::ignore);\n        for (final BigNode bigNode : bigNodes)\n        {\n            final Set<RestrictedPath> paths = bigNode.turnRestrictions();\n            for (final RestrictedPath path : paths)\n            {\n                if (path.getRoute().equals(candidate))\n                {\n                    counterRestriction++;\n                }\n            }\n        }\n        Assert.assertEquals(1, counterRestriction);\n    }\n\n    @Test\n    public void testNullPredicate()\n    {\n        Assert.assertEquals(1, Iterables.count(\n                new ComplexTurnRestrictionFinder(null).find(this.rule.getAtlasNo(), Finder::ignore),\n                x -> 1L));\n    }\n\n    @Test\n    public void testOnlyRestriction()\n    {\n        final Atlas atlasOnly = this.rule.getAtlasOnly();\n        logger.trace(\"AtlasOnly: {}\", atlasOnly);\n        final Route candidate = Route.forEdges(atlasOnly.edge(102), atlasOnly.edge(203));\n        int counter = 0;\n        for (final ComplexTurnRestriction restriction : new ComplexTurnRestrictionFinder()\n                .find(atlasOnly, Finder::ignore))\n        {\n            if (restriction.getTurnRestriction()\n                    .getTurnRestrictionType() == TurnRestrictionType.ONLY)\n            {\n                final Route route = restriction.route();\n                if (candidate.equals(route))\n                {\n                    counter++;\n                }\n            }\n        }\n        Assert.assertEquals(1, counter);\n\n        final Set<RestrictedPath> paths = new HashSet<>();\n        for (final BigNode bigNode : new BigNodeFinder().find(atlasOnly, Finder::ignore))\n        {\n            paths.addAll(bigNode.turnRestrictions());\n        }\n        Assert.assertEquals(3, paths.size());\n    }\n\n    @Test\n    public void testPathThroughBigNodeToTurnRestrictionCriteria()\n    {\n        final Atlas atlasNo = this.rule.getAtlasNo();\n        logger.trace(\"AtlasNo: {}\", atlasNo);\n\n        // All possible paths through this bigNode\n        final Route path1 = Route.forEdges(atlasNo.edge(102), atlasNo.edge(205));\n        final Route path2 = Route.forEdges(atlasNo.edge(102), atlasNo.edge(204));\n        final Route path3 = Route.forEdges(atlasNo.edge(102), atlasNo.edge(203));\n\n        final Iterable<BigNode> bigNodes = new BigNodeFinder().find(atlasNo, Finder::ignore);\n\n        for (final BigNode bigNode : bigNodes)\n        {\n            final Set<Route> allPaths = bigNode.allPaths();\n            if (allPaths.size() > 0)\n            {\n                final Set<RestrictedPath> restrictions = bigNode.turnRestrictions();\n                Assert.assertTrue(restrictions.size() == 1);\n\n                final Route restrictedRoute = restrictions.iterator().next().getRoute();\n                Assert.assertNotEquals(restrictedRoute, path1);\n                Assert.assertNotEquals(restrictedRoute, path2);\n\n                // Only one path should be the restricted one as it fully covers the turn\n                // restriction. The other paths all overlap the restriction, but only\n                // partially.\n                Assert.assertEquals(restrictedRoute, path3);\n            }\n        }\n    }\n\n    @Test\n    public void testTurnRestrictionNoUTurn()\n    {\n        // Test edge as via member\n        final Atlas testAtlas = this.rule.getAtlasNoUTurn();\n        final Optional<TurnRestriction> possibleTurnRestriction = TurnRestriction\n                .from(testAtlas.relation(1L));\n        Assert.assertTrue(possibleTurnRestriction.isPresent());\n    }\n\n    @Test\n    public void testTurnRestrictionTags()\n    {\n        final Atlas testAtlas = this.rule.getAtlasNo();\n\n        final Optional<TurnRestriction> possibleTurnRestriction = TurnRestriction\n                .from(testAtlas.relation(1L));\n\n        Assert.assertTrue(possibleTurnRestriction.isPresent());\n\n        final TurnRestriction turnRestriction = possibleTurnRestriction.get();\n\n        // Make sure both tags exist\n        Assert.assertEquals(2, turnRestriction.getTags().size());\n\n        final Optional<String> turnRestrictionTagValue = turnRestriction\n                .getTag(TurnRestrictionTag.KEY);\n        Assert.assertTrue(turnRestrictionTagValue.isPresent());\n        // The tags defined in the test atlas (and typically in osm) are lower case\n        Assert.assertEquals(TurnRestrictionTag.NO_LEFT_TURN.toString().toLowerCase(),\n                turnRestrictionTagValue.get());\n    }\n\n    @Test\n    public void testTurnRestrictionWithTwoViaNodesInRelation()\n    {\n        final Atlas testAtlas = this.rule.getRelationWithTwoViaNodes();\n        logger.trace(\"Atlas relation with 2 via nodes: {}\", testAtlas);\n        // For more than 1 via nodes in relation for restriction, TurnRestriction will always\n        // return an empty optional\n        final Optional<TurnRestriction> possibleTurnRestriction = TurnRestriction\n                .from(testAtlas.relation(1L));\n        Assert.assertEquals(Optional.empty(), possibleTurnRestriction);\n    }\n\n    @Test\n    public void testTurnRestrictionsFromComplexBigNodes()\n    {\n        final int expectedCountOfRestrictedRoutes = 49;\n\n        // There's an only turn restriction (http://www.openstreetmap.org/relation/6643212)\n        // specifying that 447301069000000 must go to 447301070000000. This route has a corner case\n        // where an otherToOption (-447301069000000) is found before the from edge. Specifically\n        // check to make sure this path is restricted.\n        final String expectedRestrictedRoute = \"[Route: 447301065000000, -447301070000000, 447301074000000, 447301068000000, 338286211000000]\";\n\n        final Atlas complexBigNodeAtlas = this.rule.getBigNodeWithOnlyTurnRestrictionsAtlas();\n\n        final List<Route> restrictedRoutes = StreamSupport\n                .stream(new BigNodeFinder().find(complexBigNodeAtlas, Finder::ignore).spliterator(),\n                        false)\n                .filter(bigNode -> bigNode.getType() == BigNode.Type.DUAL_CARRIAGEWAY)\n                .flatMap(bigNode -> bigNode.turnRestrictions().stream())\n                .map(RestrictedPath::getRoute).collect(Collectors.toList());\n\n        Assert.assertEquals(\"Verify that the expected number of restricted routes is returned\",\n                expectedCountOfRestrictedRoutes, restrictedRoutes.size());\n        Assert.assertTrue(\"Verify that this explicit restricted path is returned\", restrictedRoutes\n                .stream().anyMatch(route -> route.toString().equals(expectedRestrictedRoute)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/restriction/ComplexTurnRestrictionTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.restriction;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class ComplexTurnRestrictionTestCaseRule extends CoreTestRule\n{\n    private static final String FIVE = \"37.780572, -122.472846\";\n    private static final String FOUR = \"37.780716, -122.472395\";\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String SIX = \"37.780592, -122.472142\";\n    private static final String THREE = \"37.780724, -122.472249\";\n    private static final String TWO = \"37.780592, -122.472242\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = SIX)),\n\n            }, edges = {\n\n                    @Edge(id = \"102\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"203\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"204\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = FOUR) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"205\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = SIX) }, tags = { \"highway=trunk\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=restriction\",\n                            \"restriction=no_left_turn\" }, members = {\n                                    @Member(id = \"102\", role = \"from\", type = \"edge\"),\n                                    @Member(id = \"2\", role = \"via\", type = \"node\"),\n                                    @Member(id = \"203\", role = \"to\", type = \"edge\") })\n\n            })\n    private Atlas atlasNo;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE))\n\n            }, edges = {\n\n                    @Edge(id = \"102\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"-102\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"203\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"204\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = FOUR) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"205\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = FIVE) }, tags = { \"highway=trunk\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=restriction\",\n                            \"restriction=only_left_turn\" }, members = {\n                                    @Member(id = \"102\", role = \"from\", type = \"edge\"),\n                                    @Member(id = \"2\", role = \"via\", type = \"node\"),\n                                    @Member(id = \"203\", role = \"to\", type = \"edge\") })\n\n            })\n    private Atlas atlasOnly;\n\n    @TestAtlas(loadFromTextResource = \"bigNodeWithOnlyTurnRestrictions.txt.gz\")\n    private Atlas bigNodeWithOnlyTurnRestrictionsAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n\n            }, edges = {\n\n                    @Edge(id = \"102\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"-102\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=trunk\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=restriction\",\n                            \"restriction=no_u_turn\" }, members = {\n                                    @Member(id = \"102\", role = \"from\", type = \"edge\"),\n                                    @Member(id = \"1\", role = \"via\", type = \"node\"),\n                                    @Member(id = \"2\", role = \"via\", type = \"node\"),\n                                    @Member(id = \"-102\", role = \"to\", type = \"edge\") })\n\n            })\n    private Atlas relationWithTwoViaNodes;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX))\n\n            }, edges = {\n\n                    @Edge(id = \"102\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"203\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"304\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"205\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = SIX) }, tags = { \"highway=trunk\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=restriction\",\n                            \"restriction=no_left_turn\" }, members = {\n                                    @Member(id = \"102\", role = \"from\", type = \"edge\"),\n                                    @Member(id = \"203\", role = \"via\", type = \"edge\"),\n                                    @Member(id = \"304\", role = \"to\", type = \"edge\") })\n\n            })\n    private Atlas atlasNoUTurn;\n\n    @TestAtlas(loadFromJosmOsmResource = \"atlasBrokenTurnRestrictionRoute.josm.osm\")\n    private Atlas atlasBrokenTurnRestrictionRoute;\n\n    public Atlas getAtlasBrokenTurnRestrictionRoute()\n    {\n        return this.atlasBrokenTurnRestrictionRoute;\n    }\n\n    public Atlas getAtlasNo()\n    {\n        return this.atlasNo;\n    }\n\n    public Atlas getAtlasNoUTurn()\n    {\n        return this.atlasNoUTurn;\n    }\n\n    public Atlas getAtlasOnly()\n    {\n        return this.atlasOnly;\n    }\n\n    public Atlas getBigNodeWithOnlyTurnRestrictionsAtlas()\n    {\n        return this.bigNodeWithOnlyTurnRestrictionsAtlas;\n    }\n\n    public Atlas getRelationWithTwoViaNodes()\n    {\n        return this.relationWithTwoViaNodes;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/roundabout/ComplexRoundaboutTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.roundabout;\n\nimport java.util.Collections;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Unit tests for {@link ComplexRoundabout}.\n *\n * @author bbreithaupt\n */\npublic class ComplexRoundaboutTest\n{\n    @Rule\n    public ComplexRoundaboutTestRule setup = new ComplexRoundaboutTestRule();\n\n    @Test\n    public void clockwiseRoundaboutLeftDrivingMissingTagTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.clockwiseRoundaboutLeftDrivingMissingTagAtlas().edge(1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertEquals(ComplexRoundabout.INCOMPLETE_ROUTE_INVALIDATION,\n                complexRoundabout.getAllInvalidations().get(0).getReason());\n    }\n\n    @Test\n    public void clockwiseRoundaboutLeftDrivingTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.clockwiseRoundaboutLeftDrivingAtlas().edge(1234));\n        Assert.assertTrue(complexRoundabout.isValid());\n        Assert.assertEquals(5, complexRoundabout.getRoundaboutEdgeSet().size());\n    }\n\n    @Test\n    public void clockwiseRoundaboutRightDrivingIncompleteTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.clockwiseRoundaboutRightDrivingIncompleteAtlas().edge(1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(2, complexRoundabout.getAllInvalidations().size());\n    }\n\n    @Test\n    public void clockwiseRoundaboutRightDrivingMadeLeftDrivingTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.clockwiseRoundaboutRightDrivingAtlas().edge(1234),\n                Collections.singletonList(\"USA\"));\n        Assert.assertTrue(complexRoundabout.isValid());\n        Assert.assertEquals(5, complexRoundabout.getRoundaboutEdgeSet().size());\n    }\n\n    @Test\n    public void clockwiseRoundaboutRightDrivingTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.clockwiseRoundaboutRightDrivingAtlas().edge(1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertEquals(ComplexRoundabout.WRONG_WAY_INVALIDATION,\n                complexRoundabout.getAllInvalidations().get(0).getReason());\n    }\n\n    @Test\n    public void counterClockwiseConnectedDoubleRoundaboutRightDrivingTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.counterClockwiseConnectedDoubleRoundaboutRightDrivingAtlas().edge(1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertEquals(ComplexRoundabout.INCOMPLETE_ROUTE_INVALIDATION,\n                complexRoundabout.getAllInvalidations().get(0).getReason());\n    }\n\n    @Test\n    public void counterClockwiseRoundaboutLeftDrivingTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.counterClockwiseRoundaboutLeftDrivingAtlas().edge(1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertEquals(ComplexRoundabout.WRONG_WAY_INVALIDATION,\n                complexRoundabout.getAllInvalidations().get(0).getReason());\n    }\n\n    @Test\n    public void counterClockwiseRoundaboutRightDrivingNonCarNavigableTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.counterClockwiseRoundaboutRightDrivingNonCarNavigableAtlas().edge(1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertEquals(ComplexRoundabout.INCOMPLETE_ROUTE_INVALIDATION,\n                complexRoundabout.getAllInvalidations().get(0).getReason());\n    }\n\n    @Test\n    public void counterClockwiseRoundaboutRightDrivingOneWayNoTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.counterClockwiseRoundaboutRightDrivingOneWayNoAtlas().edge(1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertEquals(ComplexRoundabout.INCOMPLETE_ROUTE_INVALIDATION,\n                complexRoundabout.getAllInvalidations().get(0).getReason());\n    }\n\n    @Test\n    public void counterClockwiseRoundaboutRightDrivingOutsideConnectionTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(this.setup\n                .counterClockwiseRoundaboutRightDrivingOutsideConnectionAtlas().edge(1234));\n        Assert.assertTrue(complexRoundabout.isValid());\n        Assert.assertEquals(5, complexRoundabout.getRoundaboutEdgeSet().size());\n    }\n\n    @Test\n    public void counterClockwiseRoundaboutRightDrivingTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.counterClockwiseRoundaboutRightDrivingAtlas().edge(1244));\n        Assert.assertTrue(complexRoundabout.isValid());\n        Assert.assertEquals(5, complexRoundabout.getRoundaboutEdgeSet().size());\n    }\n\n    @Test\n    public void invalidValidFinderTest()\n    {\n        Assert.assertEquals(1,\n                Iterables.size(new ComplexRoundaboutFinder()\n                        .find(new MultiAtlas(this.setup.clockwiseRoundaboutRightDrivingAtlas(),\n                                this.setup.counterClockwiseRoundaboutRightDrivingAtlas()))));\n    }\n\n    @Test\n    public void multiDirectionalRoundaboutTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.multiDirectionalRoundaboutAtlas().edge(1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertEquals(ComplexRoundabout.INCOMPLETE_ROUTE_INVALIDATION,\n                complexRoundabout.getAllInvalidations().get(0).getReason());\n    }\n\n    @Test\n    public void nonMainSourceTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.counterClockwiseRoundaboutRightDrivingOneWayNoAtlas().edge(-1234));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertTrue(complexRoundabout.getAllInvalidations().get(0).getReason()\n                .contains(\"Invalid source Edge\"));\n    }\n\n    @Test\n    public void nonRoundaboutSourceTest()\n    {\n        final ComplexRoundabout complexRoundabout = new ComplexRoundabout(\n                this.setup.clockwiseRoundaboutLeftDrivingMissingTagAtlas().edge(1238));\n        Assert.assertFalse(complexRoundabout.isValid());\n        Assert.assertEquals(1, complexRoundabout.getAllInvalidations().size());\n        Assert.assertTrue(complexRoundabout.getAllInvalidations().get(0).getReason()\n                .contains(\"Invalid source Edge\"));\n    }\n\n    @Test\n    public void validValidFinderTest()\n    {\n        Assert.assertEquals(2,\n                Iterables.size(new ComplexRoundaboutFinder()\n                        .find(new MultiAtlas(this.setup.clockwiseRoundaboutLeftDrivingAtlas(),\n                                this.setup.counterClockwiseRoundaboutRightDrivingAtlas()))));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/roundabout/ComplexRoundaboutTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.roundabout;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Unit test rule for {@link ComplexRoundaboutTest}.\n *\n * @author bbreithaupt\n * @author savannahostrowski\n */\npublic class ComplexRoundaboutTestRule extends CoreTestRule\n{\n    private static final String CLOCKWISE_1 = \"38.905336130818505,-77.03197002410889\";\n    private static final String CLOCKWISE_2 = \"38.90558660084624,-77.03158378601074\";\n    private static final String CLOCKWISE_3 = \"38.905995699991145,-77.0318305492401\";\n    private static final String CLOCKWISE_4 = \"38.90582872103308,-77.03239917755127\";\n    private static final String CLOCKWISE_5 = \"38.905494761938684,-77.03236699104309\";\n\n    private static final String COUNTER_CLOCKWISE_1 = \"38.905361177861046,-77.03205585479736\";\n    private static final String COUNTER_CLOCKWISE_2 = \"38.905528157918816,-77.03158378601074\";\n    private static final String COUNTER_CLOCKWISE_3 = \"38.905937257400495,-77.031809091568\";\n    private static final String COUNTER_CLOCKWISE_4 = \"38.90588716371307,-77.03230261802673\";\n    private static final String COUNTER_CLOCKWISE_5 = \"38.90551980892527,-77.03236699104309\";\n\n    // Clockwise roundabout, left driving country\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_5)) },\n            // edges\n            edges = {\n                    @TestAtlas.Edge(id = \"1234\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_1),\n                            @TestAtlas.Loc(value = CLOCKWISE_5) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_5),\n                            @TestAtlas.Loc(value = CLOCKWISE_4) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_4),\n                            @TestAtlas.Loc(value = CLOCKWISE_3) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_3),\n                            @TestAtlas.Loc(value = CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_2),\n                            @TestAtlas.Loc(value = CLOCKWISE_1) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }) })\n    private Atlas clockwiseRoundaboutLeftDrivingAtlas;\n\n    // Clockwise roundabout, right driving country\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_5)) },\n            // edges\n            edges = {\n                    @TestAtlas.Edge(id = \"1234\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_1),\n                            @TestAtlas.Loc(value = CLOCKWISE_5) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_5),\n                            @TestAtlas.Loc(value = CLOCKWISE_4) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_4),\n                            @TestAtlas.Loc(value = CLOCKWISE_3) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_3),\n                            @TestAtlas.Loc(value = CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_2),\n                            @TestAtlas.Loc(value = CLOCKWISE_1) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }) })\n    private Atlas clockwiseRoundaboutRightDrivingAtlas;\n\n    // Counterclockwise roundabout, left driving country\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5)) },\n            // edges\n            edges = { @TestAtlas.Edge(id = \"1234\", coordinates = {\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                            \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=SGP\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=SGP\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=SGP\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=SGP\",\n                                    \"highway=primary\" }) })\n    private Atlas counterClockwiseRoundaboutLeftDrivingAtlas;\n\n    // Counterclockwise roundabout, right driving country\n    @TestAtlas(\n            // nodes\n            nodes = {\n                    @TestAtlas.Node(id = \"10\", coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1)),\n                    @TestAtlas.Node(id = \"11\", coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2)),\n                    @TestAtlas.Node(id = \"12\", coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3)),\n                    @TestAtlas.Node(id = \"13\", coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4)),\n                    @TestAtlas.Node(id = \"14\", coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5)) },\n            // edges\n            edges = { @TestAtlas.Edge(id = \"1244\", coordinates = {\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                            \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1245\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1246\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1247\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1248\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }) })\n    private Atlas counterClockwiseRoundaboutRightDrivingAtlas;\n\n    // Multi-directional Atlas\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3)) },\n            // edges\n            edges = { @TestAtlas.Edge(id = \"1234\", coordinates = {\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                            \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3),\n                            @TestAtlas.Loc(value = CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_2),\n                            @TestAtlas.Loc(value = CLOCKWISE_1) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                            @TestAtlas.Loc(value = CLOCKWISE_1) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }) })\n    private Atlas multiDirectionalRoundaboutAtlas;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_5)) },\n            // edges\n            edges = {\n                    @TestAtlas.Edge(id = \"1234\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_1),\n                            @TestAtlas.Loc(value = CLOCKWISE_5) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_5),\n                            @TestAtlas.Loc(value = CLOCKWISE_4) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_4),\n                            @TestAtlas.Loc(value = CLOCKWISE_3) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_3),\n                            @TestAtlas.Loc(value = CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=SGP\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_2),\n                            @TestAtlas.Loc(value = CLOCKWISE_1) }, tags = { \"iso_country_code=SGP\",\n                                    \"highway=primary\" }) })\n    private Atlas clockwiseRoundaboutLeftDrivingMissingTagAtlas;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_5)) },\n            // edges\n            edges = { @TestAtlas.Edge(id = \"1234\", coordinates = {\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                            \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1239\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                            @TestAtlas.Loc(value = CLOCKWISE_1) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1240\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_1),\n                            @TestAtlas.Loc(value = CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1241\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_2),\n                            @TestAtlas.Loc(value = CLOCKWISE_3) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1242\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_3),\n                            @TestAtlas.Loc(value = CLOCKWISE_4) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1243\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_4),\n                            @TestAtlas.Loc(value = CLOCKWISE_5) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1244\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_5),\n                            @TestAtlas.Loc(value = CLOCKWISE_1) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }) })\n    private Atlas counterClockwiseConnectedDoubleRoundaboutRightDrivingAtlas;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_4)) },\n            // edges\n            edges = { @TestAtlas.Edge(id = \"1234\", coordinates = {\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                            \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1239\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4),\n                            @TestAtlas.Loc(value = CLOCKWISE_4) }, tags = { \"iso_country_code=USA\",\n                                    \"highway=primary\" }) })\n    private Atlas counterClockwiseRoundaboutRightDrivingOutsideConnectionAtlas;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5)) },\n            // edges\n            edges = { @TestAtlas.Edge(id = \"1234\", coordinates = {\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                            \"iso_country_code=USA\", \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"-1234\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"-1235\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"-1236\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"-1237\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }),\n                    @TestAtlas.Edge(id = \"-1238\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\", \"oneway=no\" }) })\n    private Atlas counterClockwiseRoundaboutRightDrivingOneWayNoAtlas;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5)) },\n            // edges\n            edges = { @TestAtlas.Edge(id = \"1234\", coordinates = {\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1),\n                    @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                            \"iso_country_code=USA\", \"highway=elevator\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_2),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_3),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_4),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1238\", coordinates = {\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_5),\n                            @TestAtlas.Loc(value = COUNTER_CLOCKWISE_1) }, tags = {\n                                    \"junction=roundabout\", \"iso_country_code=USA\",\n                                    \"highway=primary\" }) })\n    private Atlas counterClockwiseRoundaboutRightDrivingNonCarNavigableAtlas;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_3)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_4)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = CLOCKWISE_5)) },\n            // edges\n            edges = {\n                    @TestAtlas.Edge(id = \"1234\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_1),\n                            @TestAtlas.Loc(value = CLOCKWISE_5) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1235\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_5),\n                            @TestAtlas.Loc(value = CLOCKWISE_4) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1236\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_4),\n                            @TestAtlas.Loc(value = CLOCKWISE_3) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1237\", coordinates = {\n                            @TestAtlas.Loc(value = CLOCKWISE_3),\n                            @TestAtlas.Loc(value = CLOCKWISE_2) }, tags = { \"junction=roundabout\",\n                                    \"iso_country_code=USA\", \"highway=primary\" }) })\n    private Atlas clockwiseRoundaboutRightDrivingIncompleteAtlas;\n\n    public Atlas clockwiseRoundaboutLeftDrivingAtlas()\n    {\n        return this.clockwiseRoundaboutLeftDrivingAtlas;\n    }\n\n    public Atlas clockwiseRoundaboutLeftDrivingMissingTagAtlas()\n    {\n        return this.clockwiseRoundaboutLeftDrivingMissingTagAtlas;\n    }\n\n    public Atlas clockwiseRoundaboutRightDrivingAtlas()\n    {\n        return this.clockwiseRoundaboutRightDrivingAtlas;\n    }\n\n    public Atlas clockwiseRoundaboutRightDrivingIncompleteAtlas()\n    {\n        return this.clockwiseRoundaboutRightDrivingIncompleteAtlas;\n    }\n\n    public Atlas counterClockwiseConnectedDoubleRoundaboutRightDrivingAtlas()\n    {\n        return this.counterClockwiseConnectedDoubleRoundaboutRightDrivingAtlas;\n    }\n\n    public Atlas counterClockwiseRoundaboutLeftDrivingAtlas()\n    {\n        return this.counterClockwiseRoundaboutLeftDrivingAtlas;\n    }\n\n    public Atlas counterClockwiseRoundaboutRightDrivingAtlas()\n    {\n        return this.counterClockwiseRoundaboutRightDrivingAtlas;\n    }\n\n    public Atlas counterClockwiseRoundaboutRightDrivingNonCarNavigableAtlas()\n    {\n        return this.counterClockwiseRoundaboutRightDrivingNonCarNavigableAtlas;\n    }\n\n    public Atlas counterClockwiseRoundaboutRightDrivingOneWayNoAtlas()\n    {\n        return this.counterClockwiseRoundaboutRightDrivingOneWayNoAtlas;\n    }\n\n    public Atlas counterClockwiseRoundaboutRightDrivingOutsideConnectionAtlas()\n    {\n        return this.counterClockwiseRoundaboutRightDrivingOutsideConnectionAtlas;\n    }\n\n    public Atlas multiDirectionalRoundaboutAtlas()\n    {\n        return this.multiDirectionalRoundaboutAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/AbstractWaterIslandTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.tags.NaturalTag;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.tags.WaterTag;\nimport org.openstreetmap.atlas.tags.WaterwayTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * @author Sid\n */\npublic abstract class AbstractWaterIslandTest\n{\n    private AtlasBuilder atlasBuilder;\n\n    public AbstractWaterIslandTest()\n    {\n        setUp();\n    }\n\n    protected AtlasBuilder getAtlasBuilder()\n    {\n        return this.atlasBuilder;\n    }\n\n    private void setUp()\n    {\n        this.atlasBuilder = new PackedAtlasBuilder();\n        // Outer\n        this.atlasBuilder.addArea(0L, Polygon.SILICON_VALLEY, Maps.hashMap());\n        // Inner\n        this.atlasBuilder.addArea(1L, Polygon.SILICON_VALLEY_2, Maps.hashMap());\n        // Multi-polygon for outline\n        final RelationBean multipolygon = new RelationBean();\n        multipolygon.addItem(0L, RelationTypeTag.MULTIPOLYGON_ROLE_OUTER, ItemType.AREA);\n        multipolygon.addItem(1L, RelationTypeTag.MULTIPOLYGON_ROLE_INNER, ItemType.AREA);\n\n        // Lake\n        this.atlasBuilder.addRelation(0L, 0L, multipolygon,\n                Maps.hashMap(RelationTypeTag.KEY, RelationTypeTag.MULTIPOLYGON_TYPE, NaturalTag.KEY,\n                        NaturalTag.WATER.name().toLowerCase(), WaterTag.KEY,\n                        WaterTag.LAKE.name().toLowerCase()));\n\n        // Sea\n        this.atlasBuilder.addRelation(7L, 0L, multipolygon,\n                Maps.hashMap(RelationTypeTag.KEY, RelationTypeTag.MULTIPOLYGON_TYPE, NaturalTag.KEY,\n                        NaturalTag.WATER.name().toLowerCase(), WaterTag.KEY,\n                        WaterTag.SEA.name().toLowerCase()));\n\n        // Reservoir\n        this.atlasBuilder.addRelation(1L, 1L, multipolygon,\n                Maps.hashMap(RelationTypeTag.KEY, RelationTypeTag.MULTIPOLYGON_TYPE, NaturalTag.KEY,\n                        NaturalTag.WATER.name().toLowerCase(), WaterTag.KEY,\n                        WaterTag.RESERVOIR.name().toLowerCase()));\n\n        // Rivers\n        this.atlasBuilder.addArea(2L, Polygon.SILICON_VALLEY_2,\n                Maps.hashMap(NaturalTag.KEY, NaturalTag.WATER.name().toLowerCase(), WaterTag.KEY,\n                        WaterTag.RIVER.name().toLowerCase()));\n        this.atlasBuilder.addLine(3L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(NaturalTag.KEY, NaturalTag.WATER.name().toLowerCase(), WaterTag.KEY,\n                        WaterTag.RIVER.name().toLowerCase()));\n\n        // Drain\n        this.atlasBuilder.addLine(4L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(NaturalTag.KEY, NaturalTag.WATER.name().toLowerCase(), WaterwayTag.KEY,\n                        WaterwayTag.DRAIN.name().toLowerCase()));\n        this.atlasBuilder.addArea(5L, Polygon.SILICON_VALLEY,\n                Maps.hashMap(NaturalTag.KEY, NaturalTag.WATER.name().toLowerCase(), WaterwayTag.KEY,\n                        WaterwayTag.DRAIN.name().toLowerCase()));\n\n        // Canal\n        this.atlasBuilder.addLine(6L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(NaturalTag.KEY, NaturalTag.WATER.name().toLowerCase(), WaterTag.KEY,\n                        WaterTag.CANAL.name().toLowerCase()));\n\n        // Pool\n        this.atlasBuilder.addArea(7L, Polygon.SILICON_VALLEY,\n                Maps.hashMap(NaturalTag.KEY, NaturalTag.WATER.name().toLowerCase(), WaterTag.KEY,\n                        WaterTag.POOL.name().toLowerCase()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/ComplexHarborTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * {@link ComplexHarbourTest} test data.\n *\n * @author mgostintsev\n */\npublic class ComplexHarborTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"harborAsRelation.atlas.txt\")\n    private Atlas harborAsRelationAtlas;\n\n    @TestAtlas(loadFromTextResource = \"harborAsArea.atlas.txt\")\n    private Atlas harborAsAreaAtlas;\n\n    public Atlas getHarborAsAreaAtlas()\n    {\n        return this.harborAsAreaAtlas;\n    }\n\n    public Atlas getHarborAsRelationAtlas()\n    {\n        return this.harborAsRelationAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/ComplexHarbourTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.finder.ComplexWaterEntityFinder;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * Tests {@link ComplexWaterEntity} of type {@link WaterType#HARBOUR} creation.\n *\n * @author mgostintsev\n */\npublic class ComplexHarbourTest\n{\n    @Rule\n    public final ComplexHarborTestRule rule = new ComplexHarborTestRule();\n\n    private final ComplexWaterEntityFinder complexWaterEntityFinder = new ComplexWaterEntityFinder();\n\n    @Test\n    public void testHarbourFromArea()\n    {\n        final Atlas harborAsArea = this.rule.getHarborAsAreaAtlas();\n        final Iterable<ComplexWaterEntity> waterEntities = this.complexWaterEntityFinder\n                .find(harborAsArea, Finder::ignore);\n        Assert.assertEquals(\"A single harbor must be created for the Harbor Area in the Atlas.\",\n                harborAsArea.numberOfAreas(), Iterables.size(waterEntities));\n        Assert.assertEquals(WaterType.HARBOUR, waterEntities.iterator().next().getWaterType());\n    }\n\n    @Test\n    public void testHarbourFromRelation()\n    {\n        final Atlas harborAsRelation = this.rule.getHarborAsRelationAtlas();\n        final Iterable<ComplexWaterEntity> waterEntities = this.complexWaterEntityFinder\n                .find(harborAsRelation, Finder::ignore);\n        Assert.assertEquals(\"A single harbor must be created for the Harbor Relation in the Atlas.\",\n                harborAsRelation.numberOfRelations(), Iterables.size(waterEntities));\n        Assert.assertEquals(WaterType.HARBOUR, waterEntities.iterator().next().getWaterType());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/ComplexWaterEntityTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport static org.openstreetmap.atlas.geography.atlas.items.ItemType.AREA;\nimport static org.openstreetmap.atlas.geography.atlas.items.ItemType.LINE;\nimport static org.openstreetmap.atlas.geography.atlas.items.ItemType.RELATION;\n\nimport java.util.Arrays;\nimport java.util.EnumMap;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.finder.ComplexWaterEntityFinder;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieu\n * @author Sid\n */\npublic class ComplexWaterEntityTest extends AbstractWaterIslandTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(ComplexWaterEntityTest.class);\n\n    private Atlas atlas;\n\n    @Before\n    public void setUp()\n    {\n        this.atlas = this.getAtlasBuilder().get();\n        logger.info(\"{}\", this.atlas);\n    }\n\n    @Test\n    public void testComplexWaterEntities()\n    {\n        final Map<WaterType, Integer> waterTypeCount = new EnumMap<>(WaterType.class);\n        final Map<ItemType, Integer> itemTypeCount = new EnumMap<>(ItemType.class);\n\n        final Iterable<ComplexWaterEntity> waterEntities = new ComplexWaterEntityFinder()\n                .find(this.atlas, Finder::ignore);\n        for (final ComplexWaterEntity waterEntity : waterEntities)\n        {\n            add(waterTypeCount, waterEntity.getWaterType());\n            add(itemTypeCount, waterEntity.getSource().getType());\n        }\n        logger.info(\"WaterType Count : {}\", waterTypeCount);\n        logger.info(\"ItemType Count : {}\", itemTypeCount);\n\n        // Validation\n        Assert.assertEquals(\"Mismatch in total number of waterEntities\", 6,\n                Iterables.size(waterEntities));\n\n        final Map<WaterType, Integer> expectedWaterTypeCount = new EnumMap<>(WaterType.class);\n\n        // expectedWaterTypeCount.put(WaterType.LAKE, 1);\n        // expectedWaterTypeCount.put(WaterType.RESERVOIR, 1);\n        expectedWaterTypeCount.put(WaterType.RIVER, 2);\n        expectedWaterTypeCount.put(WaterType.CANAL, 1);\n        expectedWaterTypeCount.put(WaterType.DITCH, 2);\n        expectedWaterTypeCount.put(WaterType.POOL, 1);\n\n        expectedWaterTypeCount.keySet().forEach(type ->\n        {\n            Assert.assertEquals(\"Mismatch in number of \" + type, expectedWaterTypeCount.get(type),\n                    waterTypeCount.get(type));\n        });\n\n        final Map<ItemType, Integer> expectedItemTypeCount = new EnumMap<>(ItemType.class);\n        // expectedItemTypeCount.put(RELATION, 2);\n        expectedItemTypeCount.put(AREA, 3);\n        expectedItemTypeCount.put(LINE, 3);\n\n        Arrays.asList(RELATION, AREA, LINE).forEach(type ->\n        {\n            Assert.assertEquals(\"Mismatch in number of \" + type, expectedItemTypeCount.get(type),\n                    itemTypeCount.get(type));\n        });\n\n    }\n\n    private <T> void add(final Map<T, Integer> typeCount, final T type)\n    {\n        typeCount.putIfAbsent(type, 0);\n        final int count = typeCount.get(type) + 1;\n        typeCount.put(type, count);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/ComplexWaterWayTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.finder.ComplexWaterEntityFinder;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * Test creating water ways from different types of {@link Relation}s. Some {@link Relation}s are\n * comprised of individual water entities and we want to make sure we build each individual piece\n * without building the the relation itself, to avoid duplicate water entities. Other\n * {@link Relation}s are comprised of non-water entities, in which case we want to make sure we\n * build only a single water entity for the parent {@link Relation}, without doing anything to the\n * members.\n *\n * @author mgostintsev\n */\npublic class ComplexWaterWayTest\n{\n    @Rule\n    public final ComplexWaterWayTestRule rule = new ComplexWaterWayTestRule();\n\n    private final ComplexWaterEntityFinder complexWaterEntityFinder = new ComplexWaterEntityFinder();\n\n    @Test\n    public void testWaterWayAsRelationOfWaterWays()\n    {\n        final Atlas canalAsRelationOfCanalEntities = this.rule\n                .getCanalAsRelationOfCanalEntitiesAtlas();\n        final Iterable<ComplexWaterEntity> waterEntities = this.complexWaterEntityFinder\n                .find(canalAsRelationOfCanalEntities, Finder::ignore);\n\n        Assert.assertEquals(\n                \"The number of water entities should be equal to the number of relation members\",\n                canalAsRelationOfCanalEntities.relation(6006326000000L).members().size(),\n                Iterables.size(waterEntities));\n    }\n\n    @Test\n    public void testWaterWaysAsRelationOfNonWaterWays()\n    {\n        final Atlas canalAsRelationOfNonCanalEntities = this.rule\n                .getCanalAsRelatonOfNonCanalEntitiesAtlas();\n        final Iterable<ComplexWaterEntity> waterEntities = this.complexWaterEntityFinder\n                .find(canalAsRelationOfNonCanalEntities, Finder::ignore);\n\n        Assert.assertEquals(\n                \"The number of water entities should be equal to the number of relations in the Atlas\",\n                Iterables.size(canalAsRelationOfNonCanalEntities.relations()),\n                Iterables.size(waterEntities));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/items/complex/water/ComplexWaterWayTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.items.complex.water;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test data for {@link ComplexWaterWayTest}.\n *\n * @author mgostintsev\n */\npublic class ComplexWaterWayTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"canalAsRelationOfCanals.atlas.txt\")\n    private Atlas canalAsRelationOfCanalEntitiesAtlas;\n\n    @TestAtlas(loadFromTextResource = \"canalAsRelation.atlas.txt\")\n    private Atlas canalAsRelationOfNonCanalEntitiesAtlas;\n\n    public Atlas getCanalAsRelationOfCanalEntitiesAtlas()\n    {\n        return this.canalAsRelationOfCanalEntitiesAtlas;\n    }\n\n    public Atlas getCanalAsRelatonOfNonCanalEntitiesAtlas()\n    {\n        return this.canalAsRelationOfNonCanalEntitiesAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightAreaTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Test class for {@link LightArea}\n *\n * @author Taylor Smock\n */\npublic class LightAreaTest\n{\n    @Rule\n    public LightweightTestAtlasRule rule = new LightweightTestAtlasRule();\n\n    @Test\n    public void testLightAreaFromArea()\n    {\n        final LightArea lightArea = LightArea.from(this.rule.getAtlas().area(7000000));\n        assertEquals(7000000, lightArea.getIdentifier());\n        assertEquals(1, lightArea.relations().size());\n        assertEquals(1, lightArea.getRelationIdentifiers().length);\n        assertEquals(8000000, lightArea.getRelationIdentifiers()[0]);\n        assertEquals(4, Iterables.asList(lightArea.asPolyLine()).size());\n        assertEquals(4, Iterables.asList(lightArea.getRawGeometry()).size());\n        assertEquals(lightArea.hashCode(), lightArea.hashCode());\n\n        final LightArea otherArea = new LightArea(7000000, Location.TEST_1, Location.TEST_2,\n                Location.TEST_3, Location.TEST_4);\n        assertNotEquals(lightArea.hashCode(), otherArea.hashCode());\n        assertNotEquals(lightArea, otherArea);\n    }\n\n    @Test\n    public void testLightAreaIdentifier()\n    {\n        final LightArea lightArea = new LightArea(1000000);\n        assertEquals(1000000, lightArea.getIdentifier());\n        assertEquals(0, lightArea.relations().size());\n        assertEquals(0, lightArea.getRelationIdentifiers().length);\n        assertNull(lightArea.asPolyLine());\n        assertNull(lightArea.getRawGeometry());\n        assertNull(lightArea.getGeometry());\n        assertEquals(lightArea.hashCode(), lightArea.hashCode());\n\n        final LightArea otherArea = new LightArea(2000000);\n        assertNotEquals(lightArea.hashCode(), otherArea.hashCode());\n        assertNotEquals(lightArea, otherArea);\n    }\n\n    @Test\n    public void testLightAreaIdentifierLocations()\n    {\n        final LightArea lightArea = new LightArea(1000000, Location.TEST_1, Location.TEST_2);\n        assertEquals(1000000, lightArea.getIdentifier());\n        assertEquals(0, lightArea.relations().size());\n        assertEquals(0, lightArea.getRelationIdentifiers().length);\n        assertEquals(2, Iterables.asList(lightArea.asPolyLine()).size());\n        assertEquals(2, Iterables.asList(lightArea.getRawGeometry()).size());\n        assertEquals(lightArea.hashCode(), lightArea.hashCode());\n\n        final LightArea otherArea = new LightArea(1000000, Location.TEST_3, Location.TEST_4);\n        assertNotEquals(lightArea.hashCode(), otherArea.hashCode());\n        assertNotEquals(lightArea, otherArea);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightEdgeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Test class for {@link LightEdge}\n *\n * @author Taylor Smock\n */\npublic class LightEdgeTest\n{\n    @Rule\n    public LightweightTestAtlasRule rule = new LightweightTestAtlasRule();\n\n    @Test\n    public void testNewEdgeFromEdge()\n    {\n        final LightEdge lightEdge = LightEdge.from(this.rule.getAtlas().edge(6000000));\n        assertEquals(6000000, lightEdge.getIdentifier());\n        assertEquals(1, lightEdge.relations().size());\n        assertEquals(1, lightEdge.getRelationIdentifiers().length);\n        assertEquals(8000000, lightEdge.getRelationIdentifiers()[0]);\n        assertEquals(2, Iterables.asList(lightEdge.asPolyLine()).size());\n        assertEquals(2, Iterables.asList(lightEdge.getRawGeometry()).size());\n        assertEquals(Location.TEST_3, lightEdge.start().getLocation());\n        assertEquals(Location.TEST_4, lightEdge.end().getLocation());\n        assertEquals(3000000, lightEdge.start().getIdentifier());\n        assertEquals(4000000, lightEdge.end().getIdentifier());\n        assertEquals(lightEdge.hashCode(), lightEdge.hashCode());\n\n        final LightEdge otherEdge = new LightEdge(1000000, Location.TEST_1, Location.TEST_2);\n        assertNotEquals(lightEdge.hashCode(), otherEdge.hashCode());\n        assertNotEquals(lightEdge, otherEdge);\n    }\n\n    @Test\n    public void testNewEdgeIdentifier()\n    {\n        final LightEdge lightEdge = new LightEdge(1000000);\n        assertEquals(1000000, lightEdge.getIdentifier());\n        assertEquals(0, lightEdge.relations().size());\n        assertEquals(0, lightEdge.getRelationIdentifiers().length);\n        assertNull(lightEdge.asPolyLine());\n        assertNull(lightEdge.getRawGeometry());\n        assertNull(lightEdge.getGeometry());\n        assertNull(lightEdge.end());\n        assertNull(lightEdge.start());\n        assertEquals(lightEdge.hashCode(), lightEdge.hashCode());\n\n        final LightEdge otherEdge = new LightEdge(2000000);\n        assertNotEquals(lightEdge.hashCode(), otherEdge.hashCode());\n        assertNotEquals(lightEdge, otherEdge);\n    }\n\n    @Test\n    public void testNewEdgeIdentifierLocations()\n    {\n        final LightEdge lightEdge = new LightEdge(1000000, Location.TEST_3, Location.TEST_4);\n        assertEquals(1000000, lightEdge.getIdentifier());\n        assertEquals(0, lightEdge.relations().size());\n        assertEquals(0, lightEdge.getRelationIdentifiers().length);\n        assertEquals(2, Iterables.asList(lightEdge.asPolyLine()).size());\n        assertEquals(2, Iterables.asList(lightEdge.getRawGeometry()).size());\n        assertEquals(Location.TEST_4, lightEdge.end().getLocation());\n        assertEquals(Location.TEST_3, lightEdge.start().getLocation());\n        assertEquals(lightEdge.hashCode(), lightEdge.hashCode());\n\n        final LightEdge otherEdge = new LightEdge(1000000, Location.TEST_1, Location.TEST_2);\n        assertNotEquals(lightEdge.hashCode(), otherEdge.hashCode());\n        assertNotEquals(lightEdge, otherEdge);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightLineTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Test class for {@link LightLine}\n *\n * @author Taylor Smock\n */\npublic class LightLineTest\n{\n    @Rule\n    public LightweightTestAtlasRule rule = new LightweightTestAtlasRule();\n\n    @Test\n    public void testNewLineIdentifier()\n    {\n        final LightLine lightLine = new LightLine(1000000);\n        assertEquals(1000000, lightLine.getIdentifier());\n        assertEquals(0, lightLine.getRelationIdentifiers().length);\n        assertEquals(0, lightLine.relations().size());\n        assertNull(lightLine.asPolyLine());\n        assertNull(lightLine.getGeometry());\n        assertNull(lightLine.getRawGeometry());\n        assertEquals(new LightLine(1000000), lightLine);\n        assertEquals(lightLine.hashCode(), lightLine.hashCode());\n    }\n\n    @Test\n    public void testNewLineIdentifierLocations()\n    {\n        final LightLine lightLine = new LightLine(1000000, Location.TEST_1, Location.TEST_2);\n        assertEquals(1000000, lightLine.getIdentifier());\n        assertEquals(0, lightLine.getRelationIdentifiers().length);\n        assertEquals(0, lightLine.relations().size());\n        assertEquals(2, Iterables.asList(lightLine.asPolyLine()).size());\n        assertEquals(2, Iterables.asList(lightLine.getRawGeometry()).size());\n        assertNotEquals(new LightLine(1000000), lightLine);\n        assertNotEquals(new LightLine(1000000, Location.TEST_1), lightLine);\n        assertEquals(new LightLine(1000000, Location.TEST_1, Location.TEST_2), lightLine);\n        assertEquals(lightLine.hashCode(), lightLine.hashCode());\n    }\n\n    @Test\n    public void testNewLineLine()\n    {\n        final LightLine lightLine = LightLine.from(this.rule.getAtlas().line(5000000));\n        assertEquals(5000000, lightLine.getIdentifier());\n        assertEquals(1, lightLine.getRelationIdentifiers().length);\n        assertEquals(1, lightLine.relations().size());\n        assertEquals(2, Iterables.asList(lightLine.asPolyLine()).size());\n        assertEquals(2, Iterables.asList(lightLine.getRawGeometry()).size());\n        assertNotEquals(new LightLine(5000000), lightLine);\n        assertNotEquals(new LightLine(5000000, Location.TEST_1), lightLine);\n        assertNotEquals(new LightLine(5000000, Location.TEST_1, Location.TEST_2), lightLine);\n        assertEquals(lightLine.hashCode(), lightLine.hashCode());\n        assertEquals(lightLine, new LightLine(lightLine));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightNodeTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\n\n/**\n * Test class for {@link LightNode}\n *\n * @author Taylor Smock\n */\npublic class LightNodeTest\n{\n    @Rule\n    public LightweightTestAtlasRule rule = new LightweightTestAtlasRule();\n\n    @Test\n    public void testNewNodeFromNode()\n    {\n        final LightNode lightNode = new LightNode(this.rule.getAtlas().node(3000000));\n        assertEquals(lightNode, LightNode.from(this.rule.getAtlas().node(3000000)));\n        assertNotEquals(lightNode, LightNode.from(this.rule.getAtlas().node(4000000)));\n        assertEquals(Location.TEST_3, lightNode.getLocation());\n\n        assertEquals(1, lightNode.getOutEdgeIdentifiers().length);\n        lightNode.getOutEdgeIdentifiers()[0] = 5L;\n        assertNotEquals(5L, lightNode.getOutEdgeIdentifiers()[0]);\n\n        final LightNode lightNode4 = LightNode.from(this.rule.getAtlas().node(4000000));\n        lightNode4.getInEdgeIdentifiers()[0] = 5L;\n        assertEquals(1, lightNode4.getInEdgeIdentifiers().length);\n        assertNotEquals(5L, lightNode4.getInEdgeIdentifiers()[0]);\n\n        assertEquals(1, lightNode.outEdges().size());\n        assertEquals(0, lightNode.inEdges().size());\n        assertEquals(0, lightNode4.outEdges().size());\n        assertEquals(1, lightNode4.inEdges().size());\n        // This is checking that the equals method handles some edge cases.\n        assertTrue(lightNode.equals(lightNode));\n        assertFalse(lightNode.equals(null));\n        assertEquals(3000000, lightNode.getIdentifier());\n    }\n\n    @Test\n    public void testNewNodeIdentifier()\n    {\n        final LightNode lightNode = new LightNode(1000000);\n        assertNull(lightNode.getLocation());\n        assertEquals(0, lightNode.getInEdgeIdentifiers().length);\n        assertEquals(0, lightNode.getOutEdgeIdentifiers().length);\n        assertEquals(0, lightNode.outEdges().size());\n        assertEquals(0, lightNode.inEdges().size());\n        assertEquals(0, lightNode.getRelationIdentifiers().length);\n        assertEquals(0, lightNode.relations().size());\n        assertEquals(lightNode.hashCode(), lightNode.hashCode());\n        assertNotEquals(lightNode.hashCode(), new LightNode(2000000).hashCode());\n        assertEquals(lightNode, new LightNode(1000000));\n        assertNotEquals(lightNode, new LightNode(2000000));\n        assertEquals(1000000, lightNode.getIdentifier());\n    }\n\n    @Test\n    public void testNewNodeIdentifierLocation()\n    {\n        final LightNode lightNode = new LightNode(1000000, Location.CENTER);\n        assertEquals(0, lightNode.getInEdgeIdentifiers().length);\n        assertEquals(0, lightNode.getOutEdgeIdentifiers().length);\n        assertEquals(0, lightNode.outEdges().size());\n        assertEquals(0, lightNode.inEdges().size());\n        assertEquals(Location.CENTER, lightNode.getLocation());\n        assertEquals(0, lightNode.getRelationIdentifiers().length);\n        assertEquals(0, lightNode.relations().size());\n        assertEquals(lightNode.hashCode(), lightNode.hashCode());\n        assertNotEquals(lightNode, new LightNode(1000000));\n        assertNotEquals(lightNode, new LightNode(1000000, Location.TEST_1));\n        assertEquals(lightNode, new LightNode(1000000, Location.CENTER));\n        assertEquals(1000000, lightNode.getIdentifier());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightPointTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.Set;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\n\n/**\n * Test class for {@link LightPoint}\n *\n * @author Taylor Smock\n */\npublic class LightPointTest\n{\n    @Rule\n    public LightweightTestAtlasRule rule = new LightweightTestAtlasRule();\n\n    @Test\n    public void testNewPointFromPoint()\n    {\n        final LightPoint lightPoint = new LightPoint(this.rule.getAtlas().point(1000000));\n        assertEquals(lightPoint, LightPoint.from(this.rule.getAtlas().point(1000000)));\n        assertNotEquals(lightPoint, LightPoint.from(this.rule.getAtlas().point(2000000)));\n        // This is checking that the equals method handles some edge cases.\n        assertTrue(lightPoint.equals(lightPoint));\n        assertFalse(lightPoint.equals(null));\n    }\n\n    @Test\n    public void testNewPointIdentifier()\n    {\n        final LightPoint lightPoint = new LightPoint(1000000);\n        assertNull(lightPoint.getLocation());\n        assertEquals(0, lightPoint.getRelationIdentifiers().length);\n        assertEquals(0, lightPoint.relations().size());\n        assertEquals(lightPoint.hashCode(), lightPoint.hashCode());\n        assertNotEquals(lightPoint.hashCode(), new LightPoint(2000000).hashCode());\n        assertEquals(lightPoint, new LightPoint(1000000));\n        assertNotEquals(lightPoint, new LightPoint(2000000));\n    }\n\n    @Test\n    public void testNewPointIdentifierLocation()\n    {\n        final LightPoint lightPoint = new LightPoint(1000000, Location.CENTER);\n        assertEquals(Location.CENTER, lightPoint.getLocation());\n        assertEquals(0, lightPoint.getRelationIdentifiers().length);\n        assertEquals(0, lightPoint.relations().size());\n        assertEquals(lightPoint.hashCode(), lightPoint.hashCode());\n        assertNotEquals(lightPoint, new LightPoint(1000000));\n        assertNotEquals(lightPoint, new LightPoint(1000000, Location.TEST_1));\n        assertEquals(lightPoint, new LightPoint(1000000, Location.CENTER));\n    }\n\n    @Test\n    public void testNewPointIdentifierLocationRelationIdentifiers()\n    {\n        final LightPoint lightPoint = new LightPoint(1000000L, Location.CENTER, Set.of(8000000L));\n        assertEquals(Location.CENTER, lightPoint.getLocation());\n        assertEquals(1, lightPoint.getRelationIdentifiers().length);\n        assertEquals(1, lightPoint.relations().size());\n        assertEquals(lightPoint.hashCode(), lightPoint.hashCode());\n        assertNotEquals(lightPoint, new LightPoint(1000000));\n        assertNotEquals(lightPoint, new LightPoint(1000000, Location.TEST_1));\n        assertNotEquals(lightPoint, new LightPoint(1000000L, Location.TEST_1, Set.of(8000000L)));\n        assertEquals(lightPoint, new LightPoint(1000000L, Location.CENTER, Set.of(8000000L)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightRelationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\n\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Test class from {@link LightRelation}\n *\n * @author Taylor Smock\n */\npublic class LightRelationTest\n{\n    @Rule\n    public LightweightTestAtlasRule atlas = new LightweightTestAtlasRule();\n\n    @Test\n    public void testNewRelationFromRelation()\n    {\n        final LightRelation lightRelation = LightRelation\n                .from(this.atlas.getAtlas().relation(8000000));\n        assertEquals(5, lightRelation.members().size());\n        assertEquals(5, lightRelation.allKnownOsmMembers().size());\n        assertEquals(0, lightRelation.getRelationIdentifiers().length);\n        assertEquals(0, lightRelation.relations().size());\n        assertEquals(8000000, lightRelation.getIdentifier());\n        assertEquals(lightRelation.getOsmIdentifier(), lightRelation.osmRelationIdentifier());\n        assertEquals(lightRelation, new LightRelation(lightRelation));\n        assertNotEquals(lightRelation, new LightRelation(1000000));\n    }\n\n    @Test\n    public void testNewRelationIdentifier()\n    {\n        final LightRelation lightRelation = new LightRelation(8000000);\n        assertEquals(0, lightRelation.members().size());\n        assertEquals(0, lightRelation.allKnownOsmMembers().size());\n        assertEquals(0, lightRelation.relations().size());\n        assertEquals(0, lightRelation.getRelationIdentifiers().length);\n        assertEquals(8000000, lightRelation.getIdentifier());\n        assertEquals(lightRelation.getOsmIdentifier(), lightRelation.osmRelationIdentifier());\n        assertEquals(lightRelation, new LightRelation(8000000));\n        assertNotEquals(lightRelation, new LightRelation(1000000));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/lightweight/LightweightTestAtlasRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.lightweight;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * A test atlas rule for use with the lightweight atlas object tests\n *\n * @author Taylor Smock\n */\npublic class LightweightTestAtlasRule extends CoreTestRule\n{\n    @TestAtlas(points = {\n            @Point(coordinates = @Loc(Location.TEST_1_COORDINATES), tags = \"amenity=fuel\", id = \"1000000\"),\n            @Point(coordinates = @Loc(Location.TEST_2_COORDINATES), tags = \"amenity=shop\", id = \"2000000\") }, nodes = {\n                    @Node(coordinates = @Loc(Location.TEST_3_COORDINATES), tags = \"highway=stop\", id = \"3000000\"),\n                    @Node(coordinates = @Loc(Location.TEST_4_COORDINATES), tags = \"highway=yield\", id = \"4000000\") }, lines = @Line(coordinates = {\n                            @Loc(Location.TEST_1_COORDINATES),\n                            @Loc(Location.TEST_2_COORDINATES) }, tags = \"waterway=stream\", id = \"5000000\"), edges = @Edge(coordinates = {\n                                    @Loc(Location.TEST_3_COORDINATES),\n                                    @Loc(Location.TEST_4_COORDINATES) }, tags = \"highway=residential\", id = \"6000000\"), areas = @Area(coordinates = {\n                                            @Loc(Location.TEST_1_COORDINATES),\n                                            @Loc(Location.TEST_2_COORDINATES),\n                                            @Loc(Location.TEST_3_COORDINATES),\n                                            @Loc(Location.TEST_4_COORDINATES) }, tags = \"amenity=swimming_pool\", id = \"7000000\"), relations = @Relation(id = \"8000000\", members = {\n                                                    @Member(id = \"1000000\", role = \"\", type = \"point\"),\n                                                    @Member(id = \"3000000\", type = \"node\", role = \"\"),\n                                                    @Member(id = \"5000000\", type = \"line\", role = \"east\"),\n                                                    @Member(id = \"6000000\", type = \"edge\", role = \"west\"),\n                                                    @Member(id = \"7000000\", type = \"area\", role = \"pool\") }))\n    private Atlas atlas;\n\n    /**\n     * Get the atlas for testing\n     *\n     * @return An atlas with all types of geography types\n     */\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/multi/MissingMultiEntityRelationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * Expose bugs with the way {@link MultiAtlas} calculates parent relations of its constituent\n * entities. These tests should pass now that all bugs are fixed.\n *\n * @author lcram\n * @see <a href=\"https://github.com/osmlab/atlas/pull/126\">The PR that demonstrates and corrects\n *      these bugs</a>\n */\npublic class MissingMultiEntityRelationTest\n{\n    @Test\n    public void testAreasForMissingRelation()\n    {\n        final List<Location> shapePoints = new ArrayList<>();\n        final PackedAtlasBuilder builderWithRelation = new PackedAtlasBuilder();\n        shapePoints.add(Location.forString(\"1,1\"));\n        shapePoints.add(Location.forString(\"2,2\"));\n        shapePoints.add(Location.forString(\"2,0\"));\n        builderWithRelation.addArea(1L, new Polygon(shapePoints),\n                Maps.hashMap(\"someArea\", \"inTwoAtlases\"));\n        final RelationBean bean = new RelationBean();\n        bean.addItem(1L,\n                \"Area appears in multiple atlases, but parent relation is only in one atlas\",\n                ItemType.AREA);\n        builderWithRelation.addRelation(2, 2, bean, Maps.hashMap());\n\n        shapePoints.clear();\n        final PackedAtlasBuilder builderNoRelation = new PackedAtlasBuilder();\n        shapePoints.add(Location.forString(\"1,1\"));\n        shapePoints.add(Location.forString(\"2,2\"));\n        shapePoints.add(Location.forString(\"2,0\"));\n        builderNoRelation.addArea(1L, new Polygon(shapePoints),\n                Maps.hashMap(\"someArea\", \"inTwoAtlases\"));\n\n        final PackedAtlas atlasWithRelation = (PackedAtlas) builderWithRelation.get();\n        final PackedAtlas atlasNoRelation = (PackedAtlas) builderNoRelation.get();\n\n        // 1 MiB resources\n        final ByteArrayResource resourceWithRelation = new ByteArrayResource(1024 * 1024);\n        final ByteArrayResource resourceNoRelation = new ByteArrayResource(1024 * 1024);\n\n        atlasWithRelation.save(resourceWithRelation);\n        atlasNoRelation.save(resourceNoRelation);\n\n        /*\n         * Add the Atlas that is missing the relation second so that it overwrites the first one.\n         * See the MultiAtlas.populateReferences() function to see this overwrite happen.\n         */\n        final Atlas multiAtlas = new AtlasResourceLoader().load(resourceWithRelation,\n                resourceNoRelation);\n        Assert.assertFalse(multiAtlas.area(1L).relations().isEmpty());\n        final Atlas multiAtlas2 = new AtlasResourceLoader().load(resourceNoRelation,\n                resourceWithRelation);\n        Assert.assertFalse(multiAtlas2.area(1L).relations().isEmpty());\n    }\n\n    @Test\n    public void testEdgesForMissingRelation()\n    {\n        final List<Location> shapePoints = new ArrayList<>();\n        final PackedAtlasBuilder builderWithRelation = new PackedAtlasBuilder();\n        builderWithRelation.addNode(1L, Location.forString(\"1,1\"),\n                Maps.hashMap(\"someNode\", \"inTwoAtlases\"));\n        builderWithRelation.addNode(2L, Location.forString(\"2,2\"),\n                Maps.hashMap(\"someNode\", \"inTwoAtlases\"));\n        shapePoints.add(Location.forString(\"1,1\"));\n        shapePoints.add(Location.forString(\"2,2\"));\n        builderWithRelation.addEdge(1L, new PolyLine(shapePoints),\n                Maps.hashMap(\"someEdge\", \"inTwoAtlases\"));\n        final RelationBean bean = new RelationBean();\n        bean.addItem(1L,\n                \"Node appears in multiple atlases, but parent relation is only in one atlas\",\n                ItemType.NODE);\n        bean.addItem(2L,\n                \"Node appears in multiple atlases, but parent relation is only in one atlas\",\n                ItemType.NODE);\n        bean.addItem(1L,\n                \"Edge appears in multiple atlases, but parent relation is only in one atlas\",\n                ItemType.EDGE);\n        builderWithRelation.addRelation(4, 4, bean, Maps.hashMap());\n\n        shapePoints.clear();\n        final PackedAtlasBuilder builderNoRelation = new PackedAtlasBuilder();\n        builderNoRelation.addNode(1L, Location.forString(\"1,1\"),\n                Maps.hashMap(\"someNode\", \"inTwoAtlases\"));\n        builderNoRelation.addNode(2L, Location.forString(\"2,2\"),\n                Maps.hashMap(\"someNode\", \"inTwoAtlases\"));\n        shapePoints.add(Location.forString(\"1,1\"));\n        shapePoints.add(Location.forString(\"2,2\"));\n        builderNoRelation.addEdge(1L, new PolyLine(shapePoints),\n                Maps.hashMap(\"someEdge\", \"inTwoAtlases\"));\n\n        final PackedAtlas atlasWithRelation = (PackedAtlas) builderWithRelation.get();\n        final PackedAtlas atlasNoRelation = (PackedAtlas) builderNoRelation.get();\n\n        // 1 MiB resources\n        final ByteArrayResource resourceWithRelation = new ByteArrayResource(1024 * 1024);\n        final ByteArrayResource resourceNoRelation = new ByteArrayResource(1024 * 1024);\n\n        atlasWithRelation.save(resourceWithRelation);\n        atlasNoRelation.save(resourceNoRelation);\n\n        /*\n         * Add the atlas that is missing the relation second, since the MultiAtlasBorderFixer bug is\n         * caused by a MultiMapWithSet.put() call that overwrites a set. What it should now be doing\n         * (since the bug is fixed) is performing a set union, not a set overwrite.\n         */\n        final Atlas multiAtlas = new AtlasResourceLoader().load(resourceWithRelation,\n                resourceNoRelation);\n        Assert.assertFalse(multiAtlas.edge(1L).relations().isEmpty());\n        final Atlas multiAtlas2 = new AtlasResourceLoader().load(resourceNoRelation,\n                resourceWithRelation);\n        Assert.assertFalse(multiAtlas2.edge(1L).relations().isEmpty());\n    }\n\n    @Test\n    public void testLinesForMissingRelation()\n    {\n        final List<Location> shapePoints = new ArrayList<>();\n        final PackedAtlasBuilder builderWithRelation = new PackedAtlasBuilder();\n        shapePoints.add(Location.forString(\"1,1\"));\n        shapePoints.add(Location.forString(\"2,2\"));\n        builderWithRelation.addLine(1L, new PolyLine(shapePoints),\n                Maps.hashMap(\"someLine\", \"inTwoAtlases\"));\n        final RelationBean bean = new RelationBean();\n        bean.addItem(1L,\n                \"Line appears in multiple atlases, but parent relation is only in one atlas\",\n                ItemType.LINE);\n        builderWithRelation.addRelation(2, 2, bean, Maps.hashMap());\n\n        shapePoints.clear();\n        final PackedAtlasBuilder builderNoRelation = new PackedAtlasBuilder();\n        shapePoints.add(Location.forString(\"1,1\"));\n        shapePoints.add(Location.forString(\"2,2\"));\n        builderNoRelation.addLine(1L, new PolyLine(shapePoints),\n                Maps.hashMap(\"someLine\", \"inTwoAtlases\"));\n\n        final PackedAtlas atlasWithRelation = (PackedAtlas) builderWithRelation.get();\n        final PackedAtlas atlasNoRelation = (PackedAtlas) builderNoRelation.get();\n\n        // 1 MiB resources\n        final ByteArrayResource resourceWithRelation = new ByteArrayResource(1024 * 1024);\n        final ByteArrayResource resourceNoRelation = new ByteArrayResource(1024 * 1024);\n\n        atlasWithRelation.save(resourceWithRelation);\n        atlasNoRelation.save(resourceNoRelation);\n\n        /*\n         * Add the Atlas that is missing the relation second so that it overwrites the first one.\n         * See the MultiAtlas.populateReferences() function to see this overwrite happen.\n         */\n        final Atlas multiAtlas = new AtlasResourceLoader().load(resourceWithRelation,\n                resourceNoRelation);\n        Assert.assertFalse(multiAtlas.line(1L).relations().isEmpty());\n        final Atlas multiAtlas2 = new AtlasResourceLoader().load(resourceNoRelation,\n                resourceWithRelation);\n        Assert.assertFalse(multiAtlas2.line(1L).relations().isEmpty());\n    }\n\n    @Test\n    public void testNodesForMissingRelation()\n    {\n        final PackedAtlasBuilder builderWithRelation = new PackedAtlasBuilder();\n        builderWithRelation.addNode(1L, Location.forString(\"1,1\"),\n                Maps.hashMap(\"someNode\", \"inTwoAtlases\"));\n        final RelationBean bean = new RelationBean();\n        bean.addItem(1L,\n                \"Node appears in multiple atlases, but parent relation is only in one atlas\",\n                ItemType.NODE);\n        builderWithRelation.addRelation(2, 2, bean, Maps.hashMap());\n\n        final PackedAtlasBuilder builderNoRelation = new PackedAtlasBuilder();\n        builderNoRelation.addNode(1L, Location.forString(\"1,1\"),\n                Maps.hashMap(\"someNode\", \"inTwoAtlases\"));\n\n        final PackedAtlas atlasWithRelation = (PackedAtlas) builderWithRelation.get();\n        final PackedAtlas atlasNoRelation = (PackedAtlas) builderNoRelation.get();\n\n        // 1 MiB resources\n        final ByteArrayResource resourceWithRelation = new ByteArrayResource(1024 * 1024);\n        final ByteArrayResource resourceNoRelation = new ByteArrayResource(1024 * 1024);\n\n        atlasWithRelation.save(resourceWithRelation);\n        atlasNoRelation.save(resourceNoRelation);\n\n        /*\n         * Here, we want to add the atlas without the relation first, since the MultiAtlas appends\n         * identical nodes into the MultiNode list. It then will always choose the first subNode to\n         * get the parent relations. See the MultiAtlas.populateReferences() and\n         * MultiNode.relations() to see what's happening.\n         */\n        final Atlas multiAtlas = new AtlasResourceLoader().load(resourceNoRelation,\n                resourceWithRelation);\n        Assert.assertFalse(multiAtlas.node(1L).relations().isEmpty());\n        final Atlas multiAtlas2 = new AtlasResourceLoader().load(resourceWithRelation,\n                resourceNoRelation);\n        Assert.assertFalse(multiAtlas2.node(1L).relations().isEmpty());\n    }\n\n    @Test\n    public void testPointsForMissingRelation()\n    {\n        final PackedAtlasBuilder builderWithRelation = new PackedAtlasBuilder();\n        builderWithRelation.addPoint(1L, Location.forString(\"1,1\"),\n                Maps.hashMap(\"somePoint\", \"inTwoAtlases\"));\n        final RelationBean bean = new RelationBean();\n        bean.addItem(1L,\n                \"Point appears in multiple atlases, but parent relation is only in one atlas\",\n                ItemType.POINT);\n        builderWithRelation.addRelation(2, 2, bean, Maps.hashMap());\n\n        final PackedAtlasBuilder builderNoRelation = new PackedAtlasBuilder();\n        builderNoRelation.addPoint(1L, Location.forString(\"1,1\"),\n                Maps.hashMap(\"somePoiont\", \"inTwoAtlases\"));\n\n        final PackedAtlas atlasWithRelation = (PackedAtlas) builderWithRelation.get();\n        final PackedAtlas atlasNoRelation = (PackedAtlas) builderNoRelation.get();\n\n        // 1 MiB resources\n        final ByteArrayResource resourceWithRelation = new ByteArrayResource(1024 * 1024);\n        final ByteArrayResource resourceNoRelation = new ByteArrayResource(1024 * 1024);\n\n        atlasWithRelation.save(resourceWithRelation);\n        atlasNoRelation.save(resourceNoRelation);\n\n        /*\n         * Add the Atlas that is missing the relation second so that it overwrites the first one.\n         * See the MultiAtlas.populateReferences() function to see this overwrite happen.\n         */\n        final Atlas multiAtlas = new AtlasResourceLoader().load(resourceWithRelation,\n                resourceNoRelation);\n        Assert.assertFalse(multiAtlas.point(1L).relations().isEmpty());\n        final Atlas multiAtlas2 = new AtlasResourceLoader().load(resourceNoRelation,\n                resourceWithRelation);\n        Assert.assertFalse(multiAtlas2.point(1L).relations().isEmpty());\n    }\n\n    @Test\n    public void testRelationsForMissingRelation()\n    {\n        final PackedAtlasBuilder builderWithRelation = new PackedAtlasBuilder();\n        builderWithRelation.addPoint(1L, Location.forString(\"1,1\"),\n                Maps.hashMap(\"somePoint\", \"inTwoAtlases\"));\n        final RelationBean bean = new RelationBean();\n        bean.addItem(1L, \"some member point\", ItemType.POINT);\n        builderWithRelation.addRelation(1, 1, bean, Maps.hashMap());\n        final RelationBean bean2 = new RelationBean();\n        bean2.addItem(1L, \"parent relation of subrelation\", ItemType.RELATION);\n        builderWithRelation.addRelation(2, 2, bean2, Maps.hashMap());\n\n        final PackedAtlasBuilder builderNoRelation = new PackedAtlasBuilder();\n        builderNoRelation.addPoint(1L, Location.forString(\"1,1\"),\n                Maps.hashMap(\"somePoint\", \"inTwoAtlases\"));\n        final RelationBean bean3 = new RelationBean();\n        bean3.addItem(1L, \"some member point\", ItemType.POINT);\n        builderNoRelation.addRelation(1, 1, bean3, Maps.hashMap());\n\n        final PackedAtlas atlasWithRelation = (PackedAtlas) builderWithRelation.get();\n        final PackedAtlas atlasNoRelation = (PackedAtlas) builderNoRelation.get();\n\n        // 1 MiB resources\n        final ByteArrayResource resourceWithRelation = new ByteArrayResource(1024 * 1024);\n        final ByteArrayResource resourceNoRelation = new ByteArrayResource(1024 * 1024);\n\n        atlasWithRelation.save(resourceWithRelation);\n        atlasNoRelation.save(resourceNoRelation);\n\n        /*\n         * Here, we want to add the atlas without the relation first, since the MultiAtlas appends\n         * identical relations into the MultiRelation list. It then will always choose the first\n         * subRelation to get the parent relations. See the MultiAtlas.populateReferences() and\n         * MultiRelation.relations() to see what's happening.\n         */\n        final Atlas multiAtlas = new AtlasResourceLoader().load(resourceNoRelation,\n                resourceWithRelation);\n        Assert.assertFalse(multiAtlas.relation(1L).relations().isEmpty());\n        final Atlas multiAtlas2 = new AtlasResourceLoader().load(resourceWithRelation,\n                resourceNoRelation);\n        Assert.assertFalse(multiAtlas2.relation(1L).relations().isEmpty());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlasOverlappingNodesFixerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.routing.AStarRouter;\nimport org.openstreetmap.atlas.geography.atlas.routing.Router;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Unit tests for {@link MultiAtlasOverlappingNodesFixer}\n *\n * @author matthieun\n * @author mkalender\n */\npublic class MultiAtlasOverlappingNodesFixerTest extends Command\n{\n    public static final Switch<File> FOLDER = new Switch<>(\"folder\",\n            \"The folder containing Atlas files for routing test\", value -> new File(value),\n            Optionality.REQUIRED);\n    public static final Switch<Location> START = new Switch<>(\"start\", \"The routing start location\",\n            value -> Location.forString(value), Optionality.REQUIRED);\n    public static final Switch<Location> END = new Switch<>(\"end\", \"The routing end location\",\n            value -> Location.forString(value), Optionality.REQUIRED);\n    private static final Logger logger = LoggerFactory\n            .getLogger(MultiAtlasOverlappingNodesFixerTest.class);\n    @Rule\n    public MultiAtlasOverlappingNodesFixerTestRule setup = new MultiAtlasOverlappingNodesFixerTestRule();\n\n    public static void main(final String[] args)\n    {\n        new MultiAtlasOverlappingNodesFixerTest().run(args);\n    }\n\n    @Test\n    public void testOrderOverlappingNode()\n    {\n        final Atlas subAtlas1 = this.setup.overlappingSubAtlas1();\n        final Atlas subAtlas2 = this.setup.overlappingSubAtlas2();\n        final MultiAtlas multiAtlasOrder1 = new MultiAtlas(subAtlas1, subAtlas2);\n        final MultiAtlas multiAtlasOrder2 = new MultiAtlas(subAtlas2, subAtlas1);\n        multiAtlasOrder1.edges().forEach(edge ->\n        {\n            final Edge otherOrderEdge = multiAtlasOrder2.edge(edge.getIdentifier());\n            Assert.assertEquals(edge.start().getIdentifier(),\n                    otherOrderEdge.start().getIdentifier());\n            Assert.assertEquals(edge.end().getIdentifier(), otherOrderEdge.end().getIdentifier());\n            Assert.assertEquals(edge.getTags(), otherOrderEdge.getTags());\n            Assert.assertEquals(edge.asPolyLine(), otherOrderEdge.asPolyLine());\n        });\n    }\n\n    @Test\n    public void testOverlappingAtAntimeridian()\n    {\n        final Atlas subAtlas1 = this.setup.subAtlasOnAntimeridianEast();\n        final Atlas subAtlas2 = this.setup.subAtlasOnAntimeridianWest();\n        final MultiAtlas multiAtlas = new MultiAtlas(subAtlas1, subAtlas2);\n\n        final Router router = AStarRouter.dijkstra(multiAtlas, Distance.meters(40));\n        final Route route = router.route(\n                Location.forString(MultiAtlasOverlappingNodesFixerTestRule.POINT_1_LOCATION),\n                Location.forString(MultiAtlasOverlappingNodesFixerTestRule.POINT_4_LOCATION));\n        Assert.assertEquals(2, route.size());\n    }\n\n    @Test\n    public void testOverlappingNodes()\n    {\n        final Atlas subAtlas1 = this.setup.overlappingSubAtlas1();\n        final Atlas subAtlas2 = this.setup.overlappingSubAtlas2();\n        final MultiAtlas multiAtlas = new MultiAtlas(subAtlas1, subAtlas2);\n\n        final Router router = AStarRouter.dijkstra(multiAtlas, Distance.meters(40));\n        final Route route = router.route(\n                Location.forString(MultiAtlasOverlappingNodesFixerTestRule.POINT_5_LOCATION),\n                Location.forString(MultiAtlasOverlappingNodesFixerTestRule.POINT_8_LOCATION));\n        Assert.assertEquals(2, route.size());\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final MultiAtlas atlas = MultiAtlas.loadFromPackedAtlas(((File) command.get(FOLDER))\n                .listFilesRecursively().stream().filter(AtlasResourceLoader.HAS_ATLAS_EXTENSION)\n                .collect(Collectors.toList()));\n        final Route route = AStarRouter.dijkstra(atlas, Distance.meters(100))\n                .route((Location) command.get(START), (Location) command.get(END));\n        logger.info(\"Route found: {}\", route);\n        logger.info(\"Route length: {}\", route.length());\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(FOLDER, START, END);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlasOverlappingNodesFixerTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * Used for atlas generation for test cases in {@link MultiAtlasOverlappingNodesFixerTest}\n *\n * @author mkalender\n */\npublic class MultiAtlasOverlappingNodesFixerTestRule extends CoreTestRule\n{\n    public static final String POINT_1_ID = \"1234567891000000\";\n    public static final String POINT_2_ID = \"2234567891000000\";\n    public static final String POINT_3_ID = \"3234567891000000\";\n    public static final String POINT_4_ID = \"4234567891000000\";\n    public static final String POINT_5_ID = \"5234567891000000\";\n    public static final String POINT_6_ID = \"6234567891000000\";\n    public static final String POINT_8_ID = \"8234567891000000\";\n\n    public static final long POINT_1_ID_LONG = Long.parseLong(POINT_1_ID);\n    public static final long POINT_2_ID_LONG = Long.parseLong(POINT_2_ID);\n    public static final long POINT_3_ID_LONG = Long.parseLong(POINT_3_ID);\n    public static final long POINT_4_ID_LONG = Long.parseLong(POINT_4_ID);\n    public static final long POINT_5_ID_LONG = Long.parseLong(POINT_5_ID);\n    public static final long POINT_6_ID_LONG = Long.parseLong(POINT_6_ID);\n    public static final long POINT_8_ID_LONG = Long.parseLong(POINT_8_ID);\n\n    public static final String POINT_1_LOCATION = \"37.0,179.0\";\n    public static final String POINT_2_LOCATION = \"37.0,180.0\";\n    public static final String POINT_3_LOCATION = \"37.0,-180.0\";\n    public static final String POINT_4_LOCATION = \"37.0,-179.0\";\n    public static final String POINT_5_LOCATION = \"37.339310,-121.0895660\";\n    public static final String POINT_6_LOCATION = \"37.341310,-121.0705660\";\n    public static final String POINT_8_LOCATION = \"37.345310,-121.0595660\";\n\n    @TestAtlas(\n            // nodes\n            nodes = { @Node(id = POINT_1_ID, coordinates = @Loc(value = POINT_1_LOCATION)),\n                    @Node(id = POINT_2_ID, coordinates = @Loc(value = POINT_2_LOCATION)) },\n            // edges\n            edges = { @Edge(id = \"123456789120001\", coordinates = { @Loc(value = POINT_1_LOCATION),\n                    @Loc(value = POINT_2_LOCATION) }) })\n    private Atlas subAtlasOnAntimeridianEast;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @Node(id = POINT_3_ID, coordinates = @Loc(value = POINT_3_LOCATION)),\n                    @Node(id = POINT_4_ID, coordinates = @Loc(value = POINT_4_LOCATION)) },\n            // edges\n            edges = { @Edge(id = \"223456789120001\", coordinates = { @Loc(value = POINT_3_LOCATION),\n                    @Loc(value = POINT_4_LOCATION) }) })\n    private Atlas subAtlasOnAntimeridianWest;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @Node(id = POINT_5_ID, coordinates = @Loc(value = POINT_5_LOCATION)),\n                    @Node(id = POINT_6_ID, coordinates = @Loc(value = POINT_6_LOCATION)) },\n            // edges\n            edges = { @Edge(id = \"123456789120001\", coordinates = { @Loc(value = POINT_5_LOCATION),\n                    @Loc(value = POINT_6_LOCATION) }) })\n    private Atlas overlappingSubAtlas1;\n\n    @TestAtlas(\n            // nodes\n            nodes = { @Node(id = POINT_6_ID, coordinates = @Loc(value = POINT_6_LOCATION)),\n                    @Node(id = POINT_8_ID, coordinates = @Loc(value = POINT_8_LOCATION)) },\n            // edges\n            edges = { @Edge(id = \"223456789120001\", coordinates = { @Loc(value = POINT_6_LOCATION),\n                    @Loc(value = POINT_8_LOCATION) }) })\n    private Atlas overlappingSubAtlas2;\n\n    public Atlas overlappingSubAtlas1()\n    {\n        return this.overlappingSubAtlas1;\n    }\n\n    public Atlas overlappingSubAtlas2()\n    {\n        return this.overlappingSubAtlas2;\n    }\n\n    public Atlas subAtlasOnAntimeridianEast()\n    {\n        return this.subAtlasOnAntimeridianEast;\n    }\n\n    public Atlas subAtlasOnAntimeridianWest()\n    {\n        return this.subAtlasOnAntimeridianWest;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Iterator;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas.AtlasSerializationFormat;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Test the {@link MultiAtlas}\n *\n * @author matthieun\n */\npublic class MultiAtlasTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(MultiAtlasTest.class);\n    @Rule\n    public MultiAtlasTestRule setup = new MultiAtlasTestRule();\n\n    private Atlas base;\n\n    private Atlas other;\n\n    private MultiAtlas multi;\n\n    @Test\n    public void connectivityTest()\n    {\n        this.multi.forEach(atlasItem -> logger.trace(atlasItem.toString()));\n\n        // Out edges of CC2: 987\n        Assert.assertEquals(1, this.multi.edge(6).end().outEdges().size());\n        Assert.assertEquals(987,\n                this.multi.edge(6).end().outEdges().iterator().next().getIdentifier());\n        Assert.assertEquals(2, this.multi.edge(-9).end().outEdges().size());\n        Assert.assertEquals(2, this.multi.edge(-9).end().inEdges().size());\n        Assert.assertEquals(2, this.multi.edge(987).start().inEdges().size());\n    }\n\n    public Atlas getAtlas()\n    {\n        if (this.multi == null)\n        {\n            setupTest();\n        }\n        return this.multi;\n    }\n\n    @Before\n    public void setupTest()\n    {\n        this.other = this.setup.getAtlas1();\n        this.base = this.setup.getAtlas2();\n        this.multi = new MultiAtlas(this.base, this.other);\n    }\n\n    @Test\n    public void spatialIndexTest()\n    {\n        final Rectangle ac2Box = Location.TEST_1.boxAround(Distance.ONE_METER);\n        Assert.assertEquals(1, Iterables.size(this.multi.nodesWithin(ac2Box)));\n        Assert.assertEquals(4, this.multi.nodesWithin(ac2Box).iterator().next().getIdentifier());\n        Assert.assertEquals(2, Iterables.size(this.multi.edgesIntersecting(ac2Box)));\n        final Iterator<Edge> edgeIterator = this.multi.edgesIntersecting(ac2Box).iterator();\n        Assert.assertEquals(6, edgeIterator.next().getIdentifier());\n        Assert.assertEquals(5, edgeIterator.next().getIdentifier());\n        Assert.assertFalse(edgeIterator.hasNext());\n    }\n\n    @Test\n    public void testFilter()\n    {\n        final WritableResource baseResource = new ByteArrayResource();\n        final WritableResource otherResource = new ByteArrayResource();\n        final PackedAtlas packedBase = this.base.cloneToPackedAtlas();\n        packedBase.setSaveSerializationFormat(AtlasSerializationFormat.JAVA);\n        packedBase.save(baseResource);\n        final PackedAtlas otherBase = this.other.cloneToPackedAtlas();\n        otherBase.setSaveSerializationFormat(AtlasSerializationFormat.JAVA);\n        otherBase.save(otherResource);\n\n        // filter out all resources from one atlas, make sure load still works\n        final Atlas multiFiltered = MultiAtlas.loadFromPackedAtlas(\n                Iterables.from(baseResource, otherResource), false,\n                entity -> entity.getIdentifier() != 123L && entity.getIdentifier() != 12345L\n                        && entity.getIdentifier() != 4L && entity.getIdentifier() != 5L\n                        && entity.getIdentifier() != 6L && entity.getIdentifier() != 1L\n                        && entity.getIdentifier() != 3L);\n        logger.info(\"{}\", multiFiltered.numberOfEdges());\n    }\n\n    @Test\n    public void testOverlappingMeridianNodes()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addNode(1L, Location.forWkt(\"POINT (180 0)\"), new HashMap<>());\n        builder.addNode(2L, Location.forWkt(\"POINT (-180 0)\"), new HashMap<>());\n        builder.addNode(3L, Location.forWkt(\"POINT (179 1)\"), new HashMap<>());\n        builder.addEdge(1L, PolyLine.wkt(\"LINESTRING(179 1, -180 0)\"), new HashMap<>());\n        final Atlas atlas = builder.get();\n\n        final MultiAtlas overlapFixed = new MultiAtlas(atlas);\n        Assert.assertEquals(1L, overlapFixed.edge(1L).end().getIdentifier());\n\n        final MultiAtlas overlapLeftAlone = new MultiAtlas(false, atlas);\n        Assert.assertEquals(2L, overlapLeftAlone.edge(1L).end().getIdentifier());\n\n        final MultiAtlas overlapLeftAlone2 = new MultiAtlas(Arrays.asList(atlas), false, false);\n        Assert.assertEquals(2L, overlapLeftAlone2.edge(1L).end().getIdentifier());\n\n        final MultiAtlas overlapLeftAlone3 = new MultiAtlas(Sets.hashSet(atlas), false, false);\n        Assert.assertEquals(2L, overlapLeftAlone3.edge(1L).end().getIdentifier());\n    }\n\n    @Test\n    public void testSlicedRelation()\n    {\n        final Relation relation1 = this.multi.relation(1L);\n        final RelationMemberList members = relation1.members();\n        Assert.assertEquals(4, members.size());\n        for (int i = 0; i < members.size(); i++)\n        {\n            Assert.assertNotNull(members.get(i));\n        }\n        // Members are ordered by entity type and ascending member identifier\n        Assert.assertEquals(4, members.get(0).getEntity().getIdentifier());\n        Assert.assertEquals(1234, members.get(1).getEntity().getIdentifier());\n        Assert.assertEquals(-9, members.get(2).getEntity().getIdentifier());\n        Assert.assertEquals(9, members.get(3).getEntity().getIdentifier());\n\n        final Relation relation2 = this.multi.relation(2L);\n        final RelationMemberList allMembers2 = relation2.allKnownOsmMembers();\n        final Relation relation3 = this.multi.relation(3L);\n        final RelationMemberList allMembers3 = relation3.allKnownOsmMembers();\n        Assert.assertEquals(8, allMembers2.size());\n        Assert.assertEquals(8, allMembers3.size());\n    }\n\n    @Test\n    public void totalTest()\n    {\n        final Iterator<Edge> edges = this.multi.edges().iterator();\n        int numberEdges = 0;\n        while (edges.hasNext())\n        {\n            numberEdges++;\n            edges.next();\n        }\n        Assert.assertEquals(6, numberEdges);\n        Assert.assertEquals(6, Iterables.size(this.multi.edges()));\n        Assert.assertEquals(4, Iterables.size(this.multi.nodes()));\n        Assert.assertEquals(3, Iterables.size(this.multi.relations()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/multi/MultiAtlasTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.multi;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author samg\n */\n\npublic class MultiAtlasTestRule extends CoreTestRule\n{\n    @TestAtlas(nodes = {\n            @Node(id = \"123\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                    \"highway=traffic_signal\" }),\n            @Node(id = \"12345\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                    \"highway=traffic_signal\" }),\n            @Node(id = \"4\", coordinates = @Loc(value = Location.TEST_1_COORDINATES), tags = {\n                    \"highway=traffic_signal\" }), },\n\n            edges = {\n                    @Edge(id = \"5\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge5\", \"surface=concrete\", \"lanes=3\" }),\n                    @Edge(id = \"6\", coordinates = { @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"highway=secondary\", \"name=edge98\", \"bridge=cantilever\",\n                                    \"maxspeed=100\" }) },\n\n            relations = {\n                    @Relation(id = \"1\", members = {\n                            @Member(id = \"4\", role = \"notThere\", type = \"node\") }),\n                    @Relation(id = \"3\", osmId = \"2\", members = {\n                            @Member(id = \"5\", role = \"in\", type = \"edge\"),\n                            @Member(id = \"12345\", role = \"node\", type = \"node\"),\n                            @Member(id = \"6\", role = \"out\", type = \"edge\") }), })\n    private Atlas atlas1;\n\n    @TestAtlas(points = {\n            @Point(id = \"1\", coordinates = @Loc(value = Location.TEST_3_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"2\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"3\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"4\", coordinates = @Loc(value = Location.TEST_7_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"5\", coordinates = @Loc(value = Location.TEST_4_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"6\", coordinates = @Loc(value = Location.TEST_1_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"7\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }), },\n\n            nodes = {\n                    @Node(id = \"123\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"1234\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"12345\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                            \"highway=turning_circle\" }), },\n\n            lines = {\n                    @Line(id = \"32\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES) }, tags = { \"power=line\" }),\n                    @Line(id = \"23\", coordinates = { @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES) }, tags = {\n                                    \"aeroway=runway\" }),\n                    @Line(id = \"24\", coordinates = { @Loc(value = Location.TEST_7_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"natural=coastline\", \"waterway=canal\" }) },\n\n            edges = {\n                    @Edge(id = \"9\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_5_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge9\", \"surface=concrete\", \"lanes=3\" }),\n                    @Edge(id = \"-9\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge_9\", \"surface=fine_gravel\" }),\n                    @Edge(id = \"98\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"highway=secondary\", \"name=edge98\", \"bridge=movable\",\n                                    \"maxspeed=100\" }),\n                    @Edge(id = \"987\", coordinates = { @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"highway=residential\", \"name=edge987\", \"tunnel=culvert\",\n                                    \"maxspeed=50 knots\" }) },\n\n            areas = {\n                    @Area(id = \"45\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"leisure=golf_course\", \"natural=grassland\" }),\n                    @Area(id = \"54\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"leisure=swimming_pool\", \"sport=swimming\" }),\n                    @Area(id = \"4554\", coordinates = { @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES) }, tags = {\n                                    \"hello=world\" }) },\n\n            relations = {\n                    @Relation(id = \"1\", members = { @Member(id = \"9\", role = \"in\", type = \"edge\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\"),\n                            @Member(id = \"-9\", role = \"out\", type = \"edge\") }, tags = {}),\n                    @Relation(id = \"2\", members = {\n                            @Member(id = \"45\", role = \"area\", type = \"area\"),\n                            @Member(id = \"32\", role = \"line\", type = \"line\"),\n                            @Member(id = \"5\", role = \"pt\", type = \"point\"),\n                            @Member(id = \"1\", role = \"rel\", type = \"relation\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\") }, tags = {}) })\n    private Atlas atlas2;\n\n    public Atlas getAtlas1()\n    {\n        return this.atlas1;\n    }\n\n    public Atlas getAtlas2()\n    {\n        return this.atlas2;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasClonerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.delta.AtlasDelta;\n\n/**\n * @author matthieun\n */\npublic class PackedAtlasClonerTest\n{\n    private static String TEST_KEY_1 = \"TEST_KEY_1\";\n    private static String TEST_KEY_2 = \"TEST_KEY_2\";\n    private static String TEST_KEY_3 = \"TEST_KEY_3\";\n    private static String TEST_KEY_4 = \"TEST_KEY_4\";\n\n    private static String TEST_VALUE_1 = \"TEST_VALUE_1\";\n    private static String TEST_VALUE_2 = \"TEST_VALUE_2\";\n    private static String TEST_VALUE_3 = \"TEST_VALUE_3\";\n    private static String TEST_VALUE_4 = \"TEST_VALUE_4\";\n\n    @Test\n    public void additionalMetaDataTagsTest()\n    {\n        final Map<String, String> additionalTags = new HashMap<>();\n        additionalTags.put(TEST_KEY_1, TEST_VALUE_1);\n        additionalTags.put(TEST_KEY_2, TEST_VALUE_2);\n        additionalTags.put(TEST_KEY_3, TEST_VALUE_3);\n        additionalTags.put(TEST_KEY_4, TEST_VALUE_4);\n\n        final Atlas atlas = RandomPackedAtlasBuilder.generate(50, 0);\n\n        final PackedAtlasCloner cloner = new PackedAtlasCloner()\n                .withAdditionalMetaDataTags(additionalTags);\n        final Atlas copy = cloner.cloneFrom(atlas);\n\n        final Map<String, String> initialTags = atlas.metaData().getTags();\n        final Map<String, String> finalTags = copy.metaData().getTags();\n\n        Assert.assertEquals(\"Unexpected number of tags found\", initialTags.size() + 4,\n                finalTags.size());\n\n        // Validate tags that were found in the old atlas\n        initialTags.forEach((key, value) ->\n        {\n            Assert.assertEquals(value, finalTags.get(key));\n        });\n\n        // Validate additional tags added\n        Assert.assertEquals(TEST_VALUE_1, finalTags.get(TEST_KEY_1));\n        Assert.assertEquals(TEST_VALUE_2, finalTags.get(TEST_KEY_2));\n        Assert.assertEquals(TEST_VALUE_3, finalTags.get(TEST_KEY_3));\n        Assert.assertEquals(TEST_VALUE_4, finalTags.get(TEST_KEY_4));\n    }\n\n    @Test\n    public void cloneTest()\n    {\n        for (int i = 0; i < 5; i++)\n        {\n            final Atlas atlas = RandomPackedAtlasBuilder.generate(50, 0);\n            final PackedAtlasCloner cloner = new PackedAtlasCloner();\n            final Atlas copy = cloner.cloneFrom(atlas);\n            Assert.assertTrue(new AtlasDelta(atlas, copy).generate().getDifferences().isEmpty());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasSerializerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.lang.reflect.Field;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas.AtlasSerializationFormat;\nimport org.openstreetmap.atlas.geography.atlas.routing.AStarRouter;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.resource.AbstractWritableResource;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.arrays.ByteArray;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Surface;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class PackedAtlasSerializerTest\n{\n    /**\n     * @author lcram\n     */\n    private static class TraceableByteArrayResource extends AbstractWritableResource\n    {\n        private static final Logger logger = LoggerFactory\n                .getLogger(TraceableByteArrayResource.class);\n\n        private static final int BYTE_MASK = 0xFF;\n\n        private int numberStreamsClosed = 0;\n\n        private final ByteArray array;\n\n        TraceableByteArrayResource()\n        {\n            this.array = new ByteArray(Long.MAX_VALUE);\n            this.array.setName(\"TraceableByteArrayResource\");\n        }\n\n        public int getNumberStreamsClosed()\n        {\n            return this.numberStreamsClosed;\n        }\n\n        @Override\n        public long length()\n        {\n            return this.array.size();\n        }\n\n        public TraceableByteArrayResource withName(final String name)\n        {\n            setName(name);\n            this.array.setName(name);\n            return this;\n        }\n\n        @Override\n        protected InputStream onRead()\n        {\n            return new InputStream()\n            {\n                private long index = 0L;\n                private boolean readOpen = true;\n\n                @Override\n                public void close()\n                {\n                    TraceableByteArrayResource.this.numberStreamsClosed++;\n                    logger.info(\"Closing a stream in TraceableByteArrayResource {}\",\n                            TraceableByteArrayResource.this.getName());\n                    this.readOpen = false;\n                }\n\n                @Override\n                public int read() throws IOException\n                {\n                    if (!this.readOpen)\n                    {\n                        throw new CoreException(\"Cannot read a closed stream\");\n                    }\n                    if (this.index >= TraceableByteArrayResource.this.array.size())\n                    {\n                        return -1;\n                    }\n                    return TraceableByteArrayResource.this.array.get(this.index++) & BYTE_MASK;\n                }\n            };\n        }\n\n        @Override\n        protected OutputStream onWrite()\n        {\n            return new OutputStream()\n            {\n                private boolean writeOpen = true;\n\n                @Override\n                public void close()\n                {\n                    this.writeOpen = false;\n                    logger.trace(\"Closed writer after {} bytes.\",\n                            TraceableByteArrayResource.this.array.size());\n                }\n\n                @Override\n                public void write(final int byteValue) throws IOException\n                {\n                    if (!this.writeOpen)\n                    {\n                        throw new CoreException(\"Cannot write to a closed stream\");\n                    }\n                    TraceableByteArrayResource.this.array.add((byte) (byteValue & BYTE_MASK));\n                }\n            };\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(PackedAtlasSerializerTest.class);\n\n    @Rule\n    public final PackedAtlasTestRule rule = new PackedAtlasTestRule();\n    private PackedAtlas atlas;\n\n    @Before\n    public void setup()\n    {\n        this.atlas = this.rule.getAtlas().cloneToPackedAtlas();\n    }\n\n    @Test\n    public void testDeserializeThenSerialize()\n    {\n        final Atlas deserialized = deserialized();\n        final ByteArrayResource resource = new ByteArrayResource(5_000_000)\n                .withName(\"testDeserializeThenSerialize\");\n        deserialized.save(resource);\n    }\n\n    @Test\n    public void testDeserializedIntegrity()\n    {\n        final Atlas deserialized = deserialized();\n        final Route route = AStarRouter.balanced(deserialized, Distance.meters(100))\n                .route(deserialized.edge(9), deserialized.edge(98));\n        Assert.assertEquals(2, route.size());\n    }\n\n    @Test\n    public void testDeserializedSpatialIndex()\n    {\n        final Atlas deserialized = deserialized();\n        Assert.assertEquals(2, Iterables.size(\n                deserialized.areasIntersecting(Location.TEST_8.boxAround(Distance.ONE_METER))));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testInvalidFileFormat()\n    {\n        final byte[] values = { 1, 2, 3 };\n        final ByteArrayResource resource = new ByteArrayResource();\n        resource.writeAndClose(values);\n        final Atlas atlas = new AtlasResourceLoader().load(resource);\n        atlas.metaData();\n    }\n\n    @Test\n    public void testPartialLoad() throws NoSuchFieldException, SecurityException\n    {\n        logger.info(\"{}\", this.atlas);\n        final File file = File.temporary();\n        logger.info(\"Saving atlas to {}\", file);\n        this.atlas.save(file);\n        final Time start = Time.now();\n        final PackedAtlas deserialized = PackedAtlas.load(file);\n        logger.info(\"Deserialized Atlas File in {}\", start.elapsedSince());\n\n        // Node\n        final long nodeIdentifier = 1234;\n\n        Assert.assertFalse(getField(deserialized,\n                PackedAtlas.FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX) != null);\n        final Node node = deserialized.node(nodeIdentifier);\n        Assert.assertTrue(getField(deserialized,\n                PackedAtlas.FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX) != null);\n        logger.info(\"Got Node {}\", nodeIdentifier);\n\n        Assert.assertFalse(getField(deserialized, PackedAtlas.FIELD_NODE_LOCATIONS) != null);\n        final Location location = node.getLocation();\n        Assert.assertTrue(getField(deserialized, PackedAtlas.FIELD_NODE_LOCATIONS) != null);\n        logger.info(\"Got Node location {}\", location);\n\n        Assert.assertFalse(getField(deserialized, PackedAtlas.FIELD_EDGE_IDENTIFIERS) != null);\n        Assert.assertFalse(getField(deserialized, PackedAtlas.FIELD_NODE_IN_EDGES_INDICES) != null);\n        Assert.assertFalse(\n                getField(deserialized, PackedAtlas.FIELD_NODE_OUT_EDGES_INDICES) != null);\n        final Set<Edge> connectedEdges = node.connectedEdges();\n        Assert.assertTrue(getField(deserialized, PackedAtlas.FIELD_EDGE_IDENTIFIERS) != null);\n        Assert.assertTrue(getField(deserialized, PackedAtlas.FIELD_NODE_IN_EDGES_INDICES) != null);\n        Assert.assertTrue(getField(deserialized, PackedAtlas.FIELD_NODE_OUT_EDGES_INDICES) != null);\n        logger.info(\"Got Node connected Edges {}\", connectedEdges.stream()\n                .map(edge -> edge.getIdentifier()).collect(Collectors.toList()));\n\n        Assert.assertFalse(getField(deserialized, PackedAtlas.FIELD_NODE_TAGS) != null);\n        final Map<String, String> nodeTags = node.getTags();\n        Assert.assertTrue(getField(deserialized, PackedAtlas.FIELD_NODE_TAGS) != null);\n        logger.info(\"Got Node tags {}\", nodeTags);\n\n        logger.info(\"{}\", node);\n\n        // Edge\n        final long edgeIdentifier = 98;\n        final Edge edge = deserialized.edge(edgeIdentifier);\n        logger.info(\"Got Edge {}\", edgeIdentifier);\n        final PolyLine line = edge.asPolyLine();\n        Assert.assertTrue(getField(deserialized, PackedAtlas.FIELD_EDGE_POLY_LINES) != null);\n        logger.info(\"Got Edge geometry {}\", line);\n        Assert.assertTrue(getField(deserialized, PackedAtlas.FIELD_EDGE_POLY_LINES) != null);\n        final Set<Node> connectedNodes = edge.connectedNodes();\n        logger.info(\"Got Edge connected Nodes {}\", connectedNodes.stream()\n                .map(aNode -> aNode.getIdentifier()).collect(Collectors.toList()));\n        final Map<String, String> edgeTags = edge.getTags();\n        logger.info(\"Got Edge tags {}\", edgeTags);\n        logger.info(\"{}\", edge);\n\n        // Area\n        final long areaIdentifier = 45;\n        final Area area = deserialized.area(areaIdentifier);\n        logger.info(\"Got Area {}\", areaIdentifier);\n        final Surface surface = area.asPolygon().surface();\n        logger.info(\"Got Area surface {}\", surface);\n        final Map<String, String> areaTags = area.getTags();\n        logger.info(\"Got Area tags {}\", areaTags);\n        logger.info(\"{}\", areaTags);\n\n        logger.info(\"Deleting {}\", file);\n        file.delete();\n    }\n\n    @Test\n    public void testResourceClosureOnInvalidLoadFormat()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addPoint(1, new Location(Latitude.degrees(0), Longitude.degrees(0)),\n                Maps.hashMap());\n        final PackedAtlas atlas = (PackedAtlas) builder.get();\n\n        final TraceableByteArrayResource javaResource = new TraceableByteArrayResource();\n        javaResource.setName(\"java\");\n        final TraceableByteArrayResource protoResource = new TraceableByteArrayResource();\n        protoResource.setName(\"proto\");\n\n        assert atlas != null;\n        atlas.setSaveSerializationFormat(AtlasSerializationFormat.JAVA);\n        atlas.save(javaResource);\n        atlas.setSaveSerializationFormat(AtlasSerializationFormat.PROTOBUF);\n        atlas.save(protoResource);\n\n        PackedAtlas.load(javaResource);\n        PackedAtlas.load(protoResource);\n\n        /*\n         * This value should be 2. Why? Because when we automatically determine atlas load format,\n         * we always test for PROTOBUF first. In case of JAVA, this means we will try to load once\n         * as PROTOBUF, fail, then try to load again as JAVA and succeed.\n         */\n        Assert.assertEquals(2, javaResource.getNumberStreamsClosed());\n        /*\n         * We also expect 2 for PROTOBUF case. Why, because in case we detect PROTOBUF, we then\n         * attempt an additional load of the optional `relationGeometries' field to determine if it\n         * is present. This triggers an additional stream open/close.\n         */\n        Assert.assertEquals(2, protoResource.getNumberStreamsClosed());\n    }\n\n    @Test\n    public void testSize()\n    {\n        final ByteArrayResource zipped = new ByteArrayResource(8192).withName(\"zippedByteArray\");\n        zipped.setCompressor(Compressor.GZIP);\n        this.atlas.save(zipped);\n        logger.info(\"Zipped Size: {}\", zipped.length());\n    }\n\n    private Atlas deserialized()\n    {\n        final ByteArrayResource resource = new ByteArrayResource(524288)\n                .withName(\"testSerializationByteArray\");\n        this.atlas.save(resource);\n        return PackedAtlas.load(resource);\n    }\n\n    private Object getField(final Atlas atlas, final String name)\n    {\n        try\n        {\n            final Field field = PackedAtlas.class.getDeclaredField(name);\n            field.setAccessible(true);\n            return field.get(atlas);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not get field {}\", name, e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.tags.AerowayTag;\nimport org.openstreetmap.atlas.tags.BridgeTag;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.LanesTag;\nimport org.openstreetmap.atlas.tags.LeisureTag;\nimport org.openstreetmap.atlas.tags.MaxSpeedTag;\nimport org.openstreetmap.atlas.tags.NaturalTag;\nimport org.openstreetmap.atlas.tags.PowerTag;\nimport org.openstreetmap.atlas.tags.RailwayTag;\nimport org.openstreetmap.atlas.tags.SportTag;\nimport org.openstreetmap.atlas.tags.SurfaceTag;\nimport org.openstreetmap.atlas.tags.TunnelTag;\nimport org.openstreetmap.atlas.tags.WaterTag;\nimport org.openstreetmap.atlas.tags.WaterwayTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Speed;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Test the {@link PackedAtlas}\n *\n * @author matthieun\n */\npublic class PackedAtlasTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(PackedAtlasTest.class);\n\n    @Rule\n    public PackedAtlasTestRule setup = new PackedAtlasTestRule();\n\n    private PackedAtlas atlas;\n\n    @Before\n    public void setup()\n    {\n        this.atlas = this.setup.getAtlas().cloneToPackedAtlas();\n    }\n\n    @Test\n    public void testArea()\n    {\n        final Area area1 = this.atlas.area(45);\n        final Area area2 = this.atlas.area(54);\n        logger.trace(area1.toString());\n        logger.trace(area2.toString());\n\n        Assert.assertTrue(area1.asPolygon().intersects(area2.asPolygon()));\n        Assert.assertEquals(Location.TEST_6,\n                area1.asPolygon().intersections(area2.asPolygon()).iterator().next());\n\n        Assert.assertEquals(\"golf_course\", area1.getTags().get(\"leisure\"));\n        Assert.assertEquals(\"grassland\", area1.getTags().get(\"natural\"));\n        Assert.assertFalse(AerowayTag.get(area1).isPresent());\n        Assert.assertEquals(LeisureTag.GOLF_COURSE,\n                LeisureTag.get(area1).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertEquals(NaturalTag.GRASSLAND,\n                NaturalTag.get(area1).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertFalse(WaterTag.get(area1).isPresent());\n\n        Assert.assertFalse(AerowayTag.get(area2).isPresent());\n        Assert.assertEquals(LeisureTag.SWIMMING_POOL,\n                LeisureTag.get(area2).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertEquals(SportTag.SWIMMING,\n                SportTag.get(area2).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertFalse(NaturalTag.get(area2).isPresent());\n        Assert.assertFalse(WaterTag.get(area2).isPresent());\n    }\n\n    @Test\n    public void testConnectivity()\n    {\n        final Node node123 = this.atlas.node(123);\n        final Node node1234 = this.atlas.node(1234);\n        final Node node12345 = this.atlas.node(12345);\n        final Edge edge9 = this.atlas.edge(9);\n        final Edge edgeMinus9 = this.atlas.edge(-9);\n        final Edge edge98 = this.atlas.edge(98);\n        final Edge edge987 = this.atlas.edge(987);\n\n        Assert.assertTrue(edge9.end().outEdges().contains(edge98));\n        Assert.assertTrue(edge9.end().outEdges().contains(edgeMinus9));\n        Assert.assertTrue(edge98.end().outEdges().contains(edge987));\n        Assert.assertTrue(edge987.end().outEdges().contains(edge9));\n\n        Assert.assertTrue(edge9.start().inEdges().contains(edge987));\n        Assert.assertTrue(edge9.start().inEdges().contains(edgeMinus9));\n        Assert.assertTrue(edge98.start().inEdges().contains(edge9));\n        Assert.assertTrue(edge987.start().inEdges().contains(edge98));\n\n        Assert.assertEquals(node123, edge9.start());\n        Assert.assertEquals(node123, edgeMinus9.end());\n        Assert.assertEquals(node123, edge987.end());\n\n        Assert.assertEquals(node1234, edge9.end());\n        Assert.assertEquals(node1234, edgeMinus9.start());\n        Assert.assertEquals(node1234, edge98.start());\n\n        Assert.assertEquals(node12345, edge98.end());\n        Assert.assertEquals(node12345, edge987.start());\n    }\n\n    @Test\n    public void testEdge()\n    {\n        final Node node123 = this.atlas.node(123);\n        final Node node1234 = this.atlas.node(1234);\n        final Node node12345 = this.atlas.node(12345);\n        final Edge edge9 = this.atlas.edge(9);\n        final Edge edgeMinus9 = this.atlas.edge(-9);\n        final Edge edge98 = this.atlas.edge(98);\n        final Edge edge987 = this.atlas.edge(987);\n        logger.trace(edge9.toString());\n        logger.trace(edgeMinus9.toString());\n        logger.trace(edge98.toString());\n        logger.trace(edge987.toString());\n\n        Assert.assertEquals(2, node123.inEdges().size());\n        Assert.assertEquals(1, node123.outEdges().size());\n\n        Assert.assertEquals(1, node1234.inEdges().size());\n        Assert.assertEquals(2, node1234.outEdges().size());\n\n        Assert.assertEquals(1, node12345.inEdges().size());\n        Assert.assertEquals(1, node12345.outEdges().size());\n\n        Assert.assertEquals(\"edge98\", edge98.getTags().get(\"name\"));\n\n        Assert.assertEquals(HighwayTag.PRIMARY, edge9.highwayTag());\n        Assert.assertEquals(SurfaceTag.CONCRETE,\n                SurfaceTag.get(edge9).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertFalse(BridgeTag.isBridge(edge9));\n        Assert.assertFalse(TunnelTag.isTunnel(edge9));\n        Assert.assertEquals(3, (int) LanesTag.numberOfLanes(edge9)\n                .orElseThrow(CoreException.supplier(\"Should have 3 lanes!\")));\n\n        Assert.assertEquals(HighwayTag.PRIMARY, edgeMinus9.highwayTag());\n        Assert.assertEquals(SurfaceTag.FINE_GRAVEL,\n                SurfaceTag.get(edgeMinus9).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertFalse(BridgeTag.isBridge(edgeMinus9));\n        Assert.assertFalse(TunnelTag.isTunnel(edgeMinus9));\n        Assert.assertFalse(MaxSpeedTag.get(edgeMinus9).isPresent());\n        Assert.assertFalse(LanesTag.numberOfLanes(edgeMinus9).isPresent());\n\n        Assert.assertEquals(HighwayTag.SECONDARY, edge98.highwayTag());\n        Assert.assertFalse(SurfaceTag.get(edge98).isPresent());\n        Assert.assertTrue(BridgeTag.isBridge(edge98));\n        Assert.assertFalse(TunnelTag.isTunnel(edge98));\n        Assert.assertEquals(Speed.kilometersPerHour(100), MaxSpeedTag.get(edge98)\n                .orElseThrow(CoreException.supplier(\"Should have maxspeed\")));\n\n        Assert.assertEquals(HighwayTag.RESIDENTIAL, edge987.highwayTag());\n        Assert.assertFalse(SurfaceTag.get(edge987).isPresent());\n        Assert.assertFalse(BridgeTag.isBridge(edge987));\n        Assert.assertTrue(TunnelTag.isTunnel(edge987));\n        Assert.assertEquals(Speed.knots(50).asKilometersPerHour(), MaxSpeedTag.get(edge987)\n                .orElseThrow(CoreException.supplier(\"Should have maxspeed\")).asKilometersPerHour(),\n                1);\n    }\n\n    @Test\n    public void testGeoJson()\n    {\n        System.out.println(this.atlas.asGeoJson().toString());\n    }\n\n    @Test\n    public void testIndex()\n    {\n        final Rectangle testBox = Location.TEST_6.boxAround(Distance.ONE_METER);\n        Assert.assertEquals(1, Iterables.size(this.atlas.nodesWithin(testBox)));\n        Assert.assertEquals(Location.TEST_6,\n                this.atlas.nodesWithin(testBox).iterator().next().getLocation());\n\n        Assert.assertEquals(3, Iterables.size(this.atlas.edgesIntersecting(testBox)));\n        Assert.assertTrue(\n                Iterables.contains(this.atlas.edgesIntersecting(testBox), this.atlas.edge(9)));\n        Assert.assertTrue(\n                Iterables.contains(this.atlas.edgesIntersecting(testBox), this.atlas.edge(-9)));\n        Assert.assertTrue(\n                Iterables.contains(this.atlas.edgesIntersecting(testBox), this.atlas.edge(987)));\n    }\n\n    @Test\n    public void testLine()\n    {\n        final Line line1 = this.atlas.line(32);\n        final Line line2 = this.atlas.line(23);\n        final Line line3 = this.atlas.line(24);\n        logger.trace(line1.toString());\n        logger.trace(line2.toString());\n        logger.trace(line3.toString());\n\n        Assert.assertTrue(line1.asPolyLine().length().isGreaterThan(line2.asPolyLine().length()));\n        Assert.assertTrue(line2.asPolyLine().intersects(line3.asPolyLine()));\n        Assert.assertFalse(line1.asPolyLine().intersects(line3.asPolyLine()));\n\n        Assert.assertFalse(AerowayTag.get(line1).isPresent());\n        Assert.assertFalse(NaturalTag.get(line1).isPresent());\n        Assert.assertEquals(PowerTag.LINE,\n                PowerTag.get(line1).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertFalse(RailwayTag.get(line1).isPresent());\n        Assert.assertFalse(WaterwayTag.get(line1).isPresent());\n\n        Assert.assertEquals(\"runway\", line2.getTags().get(\"aeroway\"));\n        Assert.assertNull(line2.getTags().get(\"waterway\"));\n        Assert.assertEquals(AerowayTag.RUNWAY,\n                AerowayTag.get(line2).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertFalse(NaturalTag.get(line2).isPresent());\n        Assert.assertFalse(PowerTag.get(line2).isPresent());\n        Assert.assertFalse(RailwayTag.get(line2).isPresent());\n        Assert.assertFalse(WaterwayTag.get(line2).isPresent());\n\n        Assert.assertFalse(AerowayTag.get(line3).isPresent());\n        Assert.assertEquals(NaturalTag.COASTLINE,\n                NaturalTag.get(line3).orElseThrow(CoreException.supplier(\"No tag\")));\n        Assert.assertFalse(PowerTag.get(line3).isPresent());\n        Assert.assertFalse(RailwayTag.get(line3).isPresent());\n        Assert.assertEquals(WaterwayTag.CANAL,\n                WaterwayTag.get(line3).orElseThrow(CoreException.supplier(\"No tag\")));\n    }\n\n    @Test\n    public void testLocationContaining()\n    {\n        Assert.assertTrue(Iterables.size(this.atlas.edgesContaining(Location.TEST_6)) == 3);\n        Assert.assertTrue(Iterables.size(this.atlas.linesContaining(Location.TEST_6)) == 1);\n\n        // Total number of LineItem = sum of the lines and edges\n        Assert.assertTrue(\n                Iterables.size(this.atlas.lineItemsContaining(Location.TEST_6)) == Iterables\n                        .size(this.atlas.edgesContaining(Location.TEST_6))\n                        + Iterables.size(this.atlas.linesContaining(Location.TEST_6)));\n        Assert.assertTrue(Iterables.size(this.atlas.nodesAt(Location.TEST_6)) == 1);\n        Assert.assertTrue(Iterables.size(this.atlas.pointsAt(Location.TEST_6)) == 1);\n\n        // check areasCovering, which uses fullyGeometricallyEncloses to check the covering property\n        Assert.assertTrue(Iterables.size(this.atlas.areasCovering(Location.TEST_8)) == 2);\n\n        // Total number of AtlasItems = sum of the edges, areas, lines, nodes and points\n        Assert.assertTrue(Iterables.size(this.atlas.itemsContaining(Location.TEST_6)) == Iterables\n                .size(this.atlas.edgesContaining(Location.TEST_6))\n                + Iterables.size(this.atlas.linesContaining(Location.TEST_6))\n                + Iterables.size(this.atlas.areasCovering(Location.TEST_6))\n                + Iterables.size(this.atlas.nodesAt(Location.TEST_6))\n                + Iterables.size(this.atlas.pointsAt(Location.TEST_6)));\n    }\n\n    @Test\n    public void testNode()\n    {\n        final Node node1 = this.atlas.node(123);\n        final Node node2 = this.atlas.node(1234);\n        final Node node3 = this.atlas.node(12345);\n        logger.trace(node1.toString());\n        logger.trace(node2.toString());\n        logger.trace(node3.toString());\n        Assert.assertEquals(node1.getLocation(), Location.TEST_6);\n        Assert.assertEquals(node2.getLocation(), Location.TEST_5);\n        Assert.assertEquals(node3.getLocation(), Location.TEST_2);\n\n        Assert.assertEquals(\"turning_circle\", node1.getTags().get(\"highway\"));\n        Assert.assertEquals(\"turning_circle\", node2.getTags().get(\"highway\"));\n        Assert.assertEquals(\"turning_circle\", node3.getTags().get(\"highway\"));\n    }\n\n    @Test\n    public void testPoint()\n    {\n        final Point point1 = this.atlas.point(1);\n        final Point point2 = this.atlas.point(2);\n        final Point point3 = this.atlas.point(3);\n        final Point point4 = this.atlas.point(4);\n        final Point point5 = this.atlas.point(5);\n        final Point point6 = this.atlas.point(6);\n        final Point point7 = this.atlas.point(7);\n        logger.trace(point1.toString());\n        logger.trace(point2.toString());\n        logger.trace(point3.toString());\n        logger.trace(point4.toString());\n        logger.trace(point5.toString());\n        logger.trace(point6.toString());\n        logger.trace(point7.toString());\n\n        Assert.assertTrue(Location.TEST_3.boxAround(Distance.ONE_METER)\n                .fullyGeometricallyEncloses(point1.getLocation()));\n        Assert.assertTrue(Location.TEST_3.boxAround(Distance.kilometers(2))\n                .fullyGeometricallyEncloses(point3.getLocation()));\n        Assert.assertEquals(1, Iterables\n                .size(this.atlas.pointsWithin(Location.TEST_3.boxAround(Distance.ONE_METER))));\n        Assert.assertEquals(3, Iterables\n                .size(this.atlas.pointsWithin(Location.TEST_3.boxAround(Distance.kilometers(2)))));\n\n        final Rectangle box = Location.TEST_3.boxAround(Distance.ONE_METER);\n        Assert.assertEquals(1, Iterables.size(\n                this.atlas.points(point -> box.fullyGeometricallyEncloses(point.getLocation()))));\n    }\n\n    @Test\n    public void testRelation()\n    {\n        final Relation relation1 = this.atlas.relation(1);\n        final Relation relation2 = this.atlas.relation(2);\n\n        // The null members are now forbidden!\n        Assert.assertEquals(/* 4 */3, relation1.members().size());\n        Assert.assertEquals(5, relation2.members().size());\n\n        final RelationMemberList members1 = relation1.members();\n        final RelationMemberList members2 = relation2.members();\n\n        Assert.assertEquals(-9, members1.get(1).getEntity().getIdentifier());\n        Assert.assertEquals(5, members2.get(3).getEntity().getIdentifier());\n        Assert.assertTrue(members2.get(3).getEntity() instanceof Point);\n        Assert.assertEquals(\"area\", members2.get(1).getRole());\n\n        Assert.assertEquals(2, this.atlas.node(1234).relations().size());\n        boolean foundOne = false;\n        boolean foundTwo = false;\n        for (final Relation relation : this.atlas.node(1234).relations())\n        {\n            if (relation.getIdentifier() == 1)\n            {\n                foundOne = true;\n            }\n            if (relation.getIdentifier() == 2)\n            {\n                foundTwo = true;\n            }\n        }\n        if (!foundOne || !foundTwo)\n        {\n            Assert.fail(\"Did not find the proper relations\");\n        }\n\n        Assert.assertEquals(5, relation2.allKnownOsmMembers().size());\n    }\n\n    @Test\n    public void testRelationMembersWithSameIdentifiersButDifferentTypes()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addArea(0, Polygon.SILICON_VALLEY, Maps.hashMap());\n        builder.addLine(0, PolyLine.TEST_POLYLINE, Maps.hashMap());\n        final RelationBean bean = new RelationBean();\n        bean.addItem(0L, \"Role of the Area\", ItemType.AREA);\n        bean.addItem(0L, \"Role of the Line\", ItemType.LINE);\n        builder.addRelation(0, 0, bean, Maps.hashMap());\n        final Atlas result = builder.get();\n        Assert.assertEquals(2, result.relation(0).members().size());\n    }\n\n    @Test\n    public void testSnaps()\n    {\n        Assert.assertEquals(2, this.atlas\n                .snaps(this.atlas.node(12345L).getLocation(), Distance.meters(100)).size());\n        Assert.assertEquals(4, this.atlas\n                .snaps(this.atlas.node(12345L).getLocation(), Distance.meters(100000)).size());\n    }\n\n    @Test\n    public void testValence()\n    {\n        // Case where valences are equals\n        Assert.assertEquals(this.atlas.node(12345L).valence(),\n                this.atlas.node(12345L).absoluteValence());\n\n        // Case where absolute valence becomes a factor\n        Assert.assertTrue(\n                this.atlas.node(1234L).valence() != this.atlas.node(1234L).absoluteValence());\n        Assert.assertEquals(2, this.atlas.node(1234L).valence());\n        Assert.assertEquals(3, this.atlas.node(1234L).absoluteValence());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/packed/PackedAtlasTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author samg\n */\npublic class PackedAtlasTestRule extends CoreTestRule\n{\n    @TestAtlas(points = {\n            @Point(id = \"1\", coordinates = @Loc(value = Location.TEST_3_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"2\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"3\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"4\", coordinates = @Loc(value = Location.TEST_7_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"5\", coordinates = @Loc(value = Location.TEST_4_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"6\", coordinates = @Loc(value = Location.TEST_1_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"7\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }), },\n\n            nodes = {\n                    @Node(id = \"123\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"1234\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"12345\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                            \"highway=turning_circle\" }), },\n\n            lines = {\n                    @Line(id = \"32\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES) }, tags = { \"power=line\" }),\n                    @Line(id = \"23\", coordinates = { @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES) }, tags = {\n                                    \"aeroway=runway\" }),\n                    @Line(id = \"24\", coordinates = { @Loc(value = Location.TEST_7_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"natural=coastline\", \"waterway=canal\" }) },\n\n            edges = {\n                    @Edge(id = \"9\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_5_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge9\", \"surface=concrete\", \"lanes=3\" }),\n                    @Edge(id = \"-9\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge_9\", \"surface=fine_gravel\" }),\n                    @Edge(id = \"98\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"highway=secondary\", \"name=edge98\", \"bridge=movable\",\n                                    \"maxspeed=100\" }),\n                    @Edge(id = \"987\", coordinates = { @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"highway=residential\", \"name=edge987\", \"tunnel=culvert\",\n                                    \"maxspeed=50 knots\" }) },\n\n            areas = {\n                    @Area(id = \"45\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"leisure=golf_course\", \"natural=grassland\" }),\n                    @Area(id = \"54\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"leisure=swimming_pool\", \"sport=swimming\" }),\n                    @Area(id = \"4554\", coordinates = { @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES) }, tags = {\n                                    \"hello=world\" }) },\n\n            relations = {\n                    @Relation(id = \"1\", members = { @Member(id = \"9\", role = \"in\", type = \"edge\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\"),\n                            @Member(id = \"-9\", role = \"out\", type = \"edge\") }, tags = {}),\n                    @Relation(id = \"2\", members = {\n                            @Member(id = \"45\", role = \"area\", type = \"area\"),\n                            @Member(id = \"32\", role = \"line\", type = \"line\"),\n                            @Member(id = \"5\", role = \"pt\", type = \"point\"),\n                            @Member(id = \"1\", role = \"rel\", type = \"relation\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\") }, tags = {}) })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/packed/PackedRelationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\n\n/**\n * @author matthieun\n */\npublic class PackedRelationTest\n{\n    @Rule\n    public PackedRelationTestCaseRule rule = new PackedRelationTestCaseRule();\n\n    @Test\n    public void testSameRelationMemberWithDifferentRole()\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final RelationMemberList relationMembers = atlas\n                .relation(PackedRelationTestCaseRule.RELATION_IDENTIFIER).members();\n        Assert.assertEquals(3, relationMembers.size());\n        Assert.assertTrue(listContains(relationMembers, RelationTypeTag.RESTRICTION_ROLE_FROM));\n        Assert.assertTrue(listContains(relationMembers, RelationTypeTag.RESTRICTION_ROLE_VIA));\n        Assert.assertTrue(listContains(relationMembers, RelationTypeTag.RESTRICTION_ROLE_TO));\n    }\n\n    private boolean listContains(final RelationMemberList list, final String role)\n    {\n        for (final RelationMember member : list)\n        {\n            if (member.getRole().equals(role))\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/packed/PackedRelationTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.tags.RelationTypeTag;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class PackedRelationTestCaseRule extends CoreTestRule\n{\n    private static final String RELATION_IDENTIFIER_VALUE = \"1\";\n    public static final long RELATION_IDENTIFIER = Long.parseLong(RELATION_IDENTIFIER_VALUE);\n\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO))\n\n            }, edges = {\n\n                    @Edge(id = \"102\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" })\n\n            }, relations = {\n\n                    @Relation(id = RELATION_IDENTIFIER_VALUE, tags = { \"type=restriction\",\n                            \"restriction=no_left_turn\" }, members = {\n                                    @Member(id = \"102\", role = RelationTypeTag.RESTRICTION_ROLE_FROM, type = \"edge\"),\n                                    @Member(id = \"2\", role = RelationTypeTag.RESTRICTION_ROLE_VIA, type = \"node\"),\n                                    @Member(id = \"102\", role = RelationTypeTag.RESTRICTION_ROLE_TO, type = \"edge\") })\n\n            })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/packed/RandomPackedAtlasBuilder.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.util.Map;\nimport java.util.PrimitiveIterator.OfLong;\nimport java.util.Random;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.openstreetmap.atlas.utilities.random.RandomTextGenerator;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class RandomPackedAtlasBuilder\n{\n    /**\n     * @author matthieun\n     */\n    public static class AtlasStartIdentifiers\n    {\n        private final long edgeStartIdentifier;\n        private final long nodeStartIdentifier;\n        private final long areaStartIdentifier;\n        private final long lineStartIdentifier;\n        private final long pointStartIdentifier;\n        private final long relationStartIdentifier;\n\n        public AtlasStartIdentifiers(final long edgeStartIdentifier, final long nodeStartIdentifier,\n                final long areaStartIdentifier, final long lineStartIdentifier,\n                final long pointStartIdentifier, final long relationStartIdentifier)\n        {\n            this.edgeStartIdentifier = edgeStartIdentifier;\n            this.nodeStartIdentifier = nodeStartIdentifier;\n            this.areaStartIdentifier = areaStartIdentifier;\n            this.lineStartIdentifier = lineStartIdentifier;\n            this.pointStartIdentifier = pointStartIdentifier;\n            this.relationStartIdentifier = relationStartIdentifier;\n        }\n\n        public long getAreaStartIdentifier()\n        {\n            return this.areaStartIdentifier;\n        }\n\n        public long getEdgeStartIdentifier()\n        {\n            return this.edgeStartIdentifier;\n        }\n\n        public long getLineStartIdentifier()\n        {\n            return this.lineStartIdentifier;\n        }\n\n        public long getNodeStartIdentifier()\n        {\n            return this.nodeStartIdentifier;\n        }\n\n        public long getPointStartIdentifier()\n        {\n            return this.pointStartIdentifier;\n        }\n\n        public long getRelationStartIdentifier()\n        {\n            return this.relationStartIdentifier;\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(RandomPackedAtlasBuilder.class);\n\n    private static final String[] edgeHighway = new String[] { \"motorway\", \"trunk\", \"primary\",\n            \"secondary\", \"tertiary\", \"residential\" };\n    private final Random random;\n\n    private final int printFrequency = 100_000;\n\n    public static PackedAtlas generate(final long size, final long startIdentifier)\n    {\n        final Rectangle bounds = Location.TEST_5.boxAround(Distance.miles(100));\n        final AtlasSize estimates = new AtlasSize(size, size, size, size, size, size);\n        final AtlasStartIdentifiers startIdentifiers = new AtlasStartIdentifiers(startIdentifier,\n                startIdentifier, startIdentifier + size, startIdentifier + 2 * size,\n                startIdentifier + size, startIdentifier);\n        return new RandomPackedAtlasBuilder().generate(estimates, startIdentifiers, bounds);\n    }\n\n    private static Map<String, String> randomTags(final int count)\n    {\n        final Map<String, String> tags1 = RandomTagsSupplier.randomTags(count - 1);\n        final Map<String, String> tags2 = RandomTagsSupplier.randomTags(1, HighwayTag.KEY,\n                edgeHighway);\n        tags1.putAll(tags2);\n        return tags1;\n    }\n\n    /**\n     * Construct\n     */\n    public RandomPackedAtlasBuilder()\n    {\n        this.random = new Random();\n    }\n\n    /**\n     * Build a random {@link Atlas}\n     *\n     * @param estimates\n     *            The size of the generated atlas\n     * @param startIdentifiers\n     *            The start identifier for each category\n     * @param bounds\n     *            The bounds of the Atlas\n     * @return A random Atlas with {@link Node}s, {@link Edge}s, {@link Area}s, {@link Line}s and\n     *         {@link Point}s.\n     */\n    public PackedAtlas generate(final AtlasSize estimates,\n            final AtlasStartIdentifiers startIdentifiers, final Rectangle bounds)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.setSizeEstimates(estimates);\n\n        Time start = Time.now();\n        for (long i = 0; i < estimates.getNodeNumber(); i++)\n        {\n            final Location geometry = Location.random(bounds);\n            builder.addNode(startIdentifiers.getNodeStartIdentifier() + i, geometry,\n                    RandomTagsSupplier.randomTags(5));\n            if ((i + 1) % this.printFrequency == 0)\n            {\n                logger.info(\"Loaded {} Nodes.\", i + 1);\n            }\n        }\n        logger.info(\"Finished Loading {} Nodes in {}\", estimates.getNodeNumber(),\n                start.elapsedSince());\n        start = Time.now();\n        for (long i = 0; i < estimates.getEdgeNumber(); i++)\n        {\n            // Edges\n            final Location[] tips = getTwoNodes(builder.peek(), estimates.getEdgeNumber());\n            final PolyLine geometry = new PolyLine(tips[0], Location.random(bounds),\n                    Location.random(bounds), Location.random(bounds), tips[1]);\n            builder.addEdge(startIdentifiers.getEdgeStartIdentifier() + i, geometry,\n                    RandomTagsSupplier.randomTags(5));\n            if ((i + 1) % this.printFrequency == 0)\n            {\n                logger.info(\"Loaded {} Edges.\", i + 1);\n            }\n        }\n        logger.info(\"Finished Loading {} Edges in {}\", estimates.getEdgeNumber(),\n                start.elapsedSince());\n        start = Time.now();\n        for (long i = 0; i < estimates.getAreaNumber(); i++)\n        {\n            // Areas\n            final Polygon geometry = Polygon.random(5, bounds);\n            builder.addArea(startIdentifiers.getAreaStartIdentifier() + i, geometry,\n                    RandomPackedAtlasBuilder.randomTags(5));\n            if ((i + 1) % this.printFrequency == 0)\n            {\n                logger.info(\"Loaded {} Areas.\", i + 1);\n            }\n        }\n        logger.info(\"Finished Loading {} Areas in {}\", estimates.getAreaNumber(),\n                start.elapsedSince());\n        start = Time.now();\n        for (long i = 0; i < estimates.getLineNumber(); i++)\n        {\n            // Lines\n            final PolyLine geometry = PolyLine.random(5, bounds);\n            builder.addLine(startIdentifiers.getLineStartIdentifier() + i, geometry,\n                    RandomTagsSupplier.randomTags(5));\n            if ((i + 1) % this.printFrequency == 0)\n            {\n                logger.info(\"Loaded {} Lines.\", i + 1);\n            }\n        }\n        logger.info(\"Finished Loading {} Lines in {}\", estimates.getLineNumber(),\n                start.elapsedSince());\n        start = Time.now();\n        for (long i = 0; i < estimates.getPointNumber(); i++)\n        {\n            // Points\n            final Location geometry = Location.random(bounds);\n            builder.addPoint(startIdentifiers.getPointStartIdentifier() + i, geometry,\n                    RandomTagsSupplier.randomTags(5));\n            if ((i + 1) % this.printFrequency == 0)\n            {\n                logger.info(\"Loaded {} Points.\", i + 1);\n            }\n        }\n        logger.info(\"Finished Loading {} Points in {}\", estimates.getPointNumber(),\n                start.elapsedSince());\n        start = Time.now();\n        for (long i = 0; i < estimates.getRelationNumber(); i++)\n        {\n            // Relations\n            final Random random = new Random();\n            final RelationBean structure = new RelationBean();\n            for (int j = 0; j < random.nextInt(5) + 5; j++)\n            {\n                boolean found = false;\n                long featureIdentifier = -1;\n                ItemType type = null;\n                while (!found)\n                {\n                    final int typeIndex = random.nextInt(ItemType.values().length);\n                    type = ItemType.values()[typeIndex];\n                    switch (type)\n                    {\n                        case NODE:\n                            featureIdentifier = builder.peek().nodeIdentifier(\n                                    random.longs(0, estimates.getNodeNumber()).iterator().next());\n                            found = true;\n                            break;\n                        case EDGE:\n                            featureIdentifier = builder.peek().edgeIdentifier(\n                                    random.longs(0, estimates.getEdgeNumber()).iterator().next());\n                            found = true;\n                            break;\n                        case AREA:\n                            featureIdentifier = builder.peek().areaIdentifier(\n                                    random.longs(0, estimates.getAreaNumber()).iterator().next());\n                            found = true;\n                            break;\n                        case LINE:\n                            featureIdentifier = builder.peek().lineIdentifier(\n                                    random.longs(0, estimates.getLineNumber()).iterator().next());\n                            found = true;\n                            break;\n                        case POINT:\n                            featureIdentifier = builder.peek().pointIdentifier(\n                                    random.longs(0, estimates.getPointNumber()).iterator().next());\n                            found = true;\n                            break;\n                        case RELATION:\n                            if (i > 1)\n                            {\n                                // Make sure that this relation is not trying to depend on itself if\n                                // it is the first one...\n                                featureIdentifier = builder.peek().relationIdentifier(\n                                        random.longs(0, i - 1).iterator().next());\n                                found = true;\n                            }\n                            break;\n                        default:\n                            throw new CoreException(\"Unknown type {}\", type);\n                    }\n                }\n                structure.addItem(featureIdentifier, new RandomTextGenerator().newWord(), type);\n            }\n\n            final long identifier = startIdentifiers.getRelationStartIdentifier() + i;\n            builder.addRelation(identifier, identifier, structure,\n                    RandomTagsSupplier.randomTags(5));\n            if ((i + 1) % this.printFrequency == 0)\n            {\n                logger.info(\"Loaded {} Relations.\", i + 1);\n            }\n        }\n        logger.info(\"Finished Loading {} Points in {}\", estimates.getPointNumber(),\n                start.elapsedSince());\n        return (PackedAtlas) builder.get();\n    }\n\n    private Location[] getTwoNodes(final PackedAtlas packedAtlas, final long size)\n    {\n        final OfLong iterator = this.random.longs(0, size).iterator();\n        final long index1 = iterator.next();\n        final long index2 = iterator.next();\n        final Location[] result = new Location[2];\n        result[0] = packedAtlas.nodeLocation(index1);\n        result[1] = packedAtlas.nodeLocation(index2);\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/packed/RelationMultipolygonGeometryTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.packed;\n\nimport java.io.IOException;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsMultiPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class RelationMultipolygonGeometryTest\n{\n    @Test\n    public void testEnhancedAtlas()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n\n            final PackedAtlas atlas = (PackedAtlas) new AtlasResourceLoader()\n                    .load(new File(\"/Users/foo/test.atlas\", filesystem));\n            Assert.assertTrue(atlas.containsEnhancedRelationGeometry());\n            Assert.assertNotNull(atlas.enhancedRelationGeometries());\n\n            final PackedAtlas atlasNoEnhancedGeometry = (PackedAtlas) new AtlasResourceLoader()\n                    .load(new File(\"/Users/foo/test_notEnhanced.atlas\", filesystem));\n            Assert.assertFalse(atlasNoEnhancedGeometry.containsEnhancedRelationGeometry());\n            Assert.assertNull(atlasNoEnhancedGeometry.enhancedRelationGeometries());\n\n            final PackedAtlas atlasJava = (PackedAtlas) new AtlasResourceLoader()\n                    .load(new File(\"/Users/foo/test_java.atlas\", filesystem));\n            Assert.assertFalse(atlasJava.containsEnhancedRelationGeometry());\n            Assert.assertNull(atlasJava.enhancedRelationGeometries());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testReadingGeometry()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n            final PackedAtlas atlas = (PackedAtlas) new AtlasResourceLoader()\n                    .load(new File(\"/Users/foo/test.atlas\", filesystem));\n            Assert.assertTrue(atlas.relation(1L).asMultiPolygon().isPresent());\n            final PackedAtlas atlasNoEnhancedGeometry = (PackedAtlas) new AtlasResourceLoader()\n                    .load(new File(\"/Users/foo/test_notEnhanced.atlas\", filesystem));\n            Assert.assertFalse(atlasNoEnhancedGeometry.relation(1L).asMultiPolygon().isPresent());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem(final FileSystem filesystem)\n    {\n        /*\n         * test.atlas contains a multipolygon with enhanced geometry (storing the JTS geometry\n         * directly).\n         */\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder().withEnhancedRelationGeometry();\n        builder.addArea(1L, Polygon.SILICON_VALLEY, Maps.hashMap(\"name\", \"Silicon Valley\"));\n        builder.addArea(2L, Polygon.wkt(\n                \"POLYGON((-122.0416867 37.35,-122.0194565 37.35,-122.0194565 37.3426160,-122.0416867 37.3426160,-122.0416867 37.35))\"),\n                Maps.hashMap(\"name\", \"Silicon Valley Inner\"));\n        final RelationBean bean = new RelationBean();\n        bean.addItem(1L, \"outer\", ItemType.AREA);\n        bean.addItem(2L, \"inner\", ItemType.AREA);\n        final MultiMap<Polygon, Polygon> outersToInners = new MultiMap<>();\n        outersToInners.add(builder.peek().area(1L).asPolygon(),\n                builder.peek().area(2L).asPolygon());\n        builder.addRelation(1L, 1L, bean, Maps.hashMap(\"type\", \"multipolygon\"),\n                new JtsMultiPolygonToMultiPolygonConverter()\n                        .backwardConvert(new MultiPolygon(outersToInners)));\n        final Atlas atlas = builder.get();\n        final File atlasFile = new File(\"/Users/foo/test.atlas\", filesystem);\n        assert atlas != null;\n        atlas.save(atlasFile);\n\n        /*\n         * test_notEnhanced.atlas contains a multipolygon, but without the special enhancement\n         * (storing the JTS geometry directly).\n         */\n        final PackedAtlasBuilder builder2 = new PackedAtlasBuilder();\n        builder2.addArea(1L, Polygon.SILICON_VALLEY, Maps.hashMap(\"name\", \"Silicon Valley\"));\n        builder2.addArea(2L, Polygon.wkt(\n                \"POLYGON((-122.0416867 37.35,-122.0194565 37.35,-122.0194565 37.3426160,-122.0416867 37.3426160,-122.0416867 37.35))\"),\n                Maps.hashMap(\"name\", \"Silicon Valley Inner\"));\n        final RelationBean bean2 = new RelationBean();\n        bean2.addItem(1L, \"outer\", ItemType.AREA);\n        bean2.addItem(2L, \"inner\", ItemType.AREA);\n        builder2.addRelation(1L, 1L, bean2, Maps.hashMap(\"type\", \"multipolygon\"));\n        final Atlas atlas2 = builder2.get();\n        final File atlasFile2 = new File(\"/Users/foo/test_notEnhanced.atlas\", filesystem);\n        assert atlas2 != null;\n        atlas2.save(atlasFile2);\n\n        /*\n         * test_java.atlas contains a multipolygon, but without the special enhancement because it\n         * is a legacy Java serialized atlas.\n         */\n        final PackedAtlasBuilder builder3 = new PackedAtlasBuilder();\n        builder3.addArea(1L, Polygon.SILICON_VALLEY, Maps.hashMap(\"name\", \"Silicon Valley\"));\n        builder3.addArea(2L, Polygon.wkt(\n                \"POLYGON((-122.0416867 37.35,-122.0194565 37.35,-122.0194565 37.3426160,-122.0416867 37.3426160,-122.0416867 37.35))\"),\n                Maps.hashMap(\"name\", \"Silicon Valley Inner\"));\n        final RelationBean bean3 = new RelationBean();\n        bean3.addItem(1L, \"outer\", ItemType.AREA);\n        bean3.addItem(2L, \"inner\", ItemType.AREA);\n        builder3.addRelation(1L, 1L, bean3, Maps.hashMap(\"type\", \"multipolygon\"));\n        final Atlas atlas3 = builder3.get();\n        final File atlasFile3 = new File(\"/Users/foo/test_java.atlas\", filesystem);\n        assert atlas3 != null;\n        atlas3.save(atlasFile3);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/BridgeConfiguredFilterTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\nimport org.openstreetmap.atlas.utilities.configuration.Configuration;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\n\n/**\n * @author matthieun\n */\npublic class BridgeConfiguredFilterTest\n{\n    @Test\n    public void testBridge()\n    {\n        final BridgeConfiguredFilter bridgeConfiguredFilter1 = new BridgeConfiguredFilter(\"\", \"\",\n                get(\"bridgeConfiguredFilter1.json\"));\n        final BridgeConfiguredFilter bridgeConfiguredFilter2 = new BridgeConfiguredFilter(\"\",\n                \"my-filter\", get(\"bridgeConfiguredFilter2.json\"));\n\n        final CompleteEdge edge = new CompleteEdge(123L, PolyLine.TEST_POLYLINE,\n                Maps.hashMap(\"highway\", \"trunk\"), 1L, 2L, Sets.hashSet());\n        final CompletePoint point = new CompletePoint(123L, Location.COLOSSEUM,\n                Maps.hashMap(\"highway\", \"traffic_signals\"), Sets.hashSet());\n\n        Assert.assertFalse(bridgeConfiguredFilter1.test(edge));\n        Assert.assertTrue(bridgeConfiguredFilter1.test(point));\n        Assert.assertTrue(bridgeConfiguredFilter2.test(edge));\n        Assert.assertFalse(bridgeConfiguredFilter2.test(point));\n    }\n\n    private Configuration get(final String name)\n    {\n        return new StandardConfiguration(new InputStreamResource(\n                () -> BridgeConfiguredFilterTest.class.getResourceAsStream(name)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/OsmPbfIngestTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Supplier;\nimport java.util.function.UnaryOperator;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveLineItem;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveLocationItem;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveObjectStore;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.AtlasSectionProcessor;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.SyntheticBoundaryNodeTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileParser;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileToPbf;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Tests data inclusion and exclusion scenarios when converting from OSM PBF to Atlas format.\n *\n * @author matthieun\n */\npublic class OsmPbfIngestTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(OsmPbfIngestTest.class);\n\n    private static final String COUNTRY_1_NAME = \"COUNTRY_1\";\n    private static final String COUNTRY_2_NAME = \"COUNTRY_2\";\n    private static final Location SHAPEPOINT = Location.forString(\"37.328076,-122.031869\");\n    private static final Location DE_ANZA_AT_280 = Location.forString(\"37.334384,-122.032327\");\n    private static final Location OUTSIDE_COUNTRY_1 = Location.forString(\"37.350987,-121.988496\");\n\n    private MultiPolygon countryShape1;\n    private MultiPolygon countryShape2;\n    private CountryBoundaryMap countryBoundariesAll;\n    private CountryBoundaryMap countryBoundaries1;\n    private AtlasPrimitiveObjectStore store;\n\n    @Before\n    public void init()\n    {\n        final Polygon polygon1 = new Polygon(\n                Location.forString(\"37.330310627190194,-122.08282470703124\"),\n                Location.forString(\"37.25014416051375,-122.04025268554688\"),\n                Location.forString(\"37.242218476496625,-121.937255859375\"),\n                Location.forString(\"37.28060928450999,-121.95030212402344\"),\n                Location.forString(\"37.33310876404607,-122.02986717224121\"),\n                Location.forString(\"37.33604328341095,-122.06462860107422\"));\n        final Polygon polygon2 = new Polygon(\n                Location.forString(\"37.34232140492521,-122.06904888153078\"),\n                Location.forString(\"37.33474664945566,-122.06102371215819\"),\n                Location.forString(\"37.331308755038414,-122.0297384262085\"),\n                Location.forString(\"37.34051308677875,-122.02124118804932\"),\n                Location.forString(\"37.3447096829136,-122.03505992889404\"));\n\n        final Map<String, List<org.locationtech.jts.geom.Polygon>> boundaries = new HashMap<>();\n        final List<org.locationtech.jts.geom.Polygon> country1Boundaries = new ArrayList<>();\n        final List<org.locationtech.jts.geom.Polygon> country2Boundaries = new ArrayList<>();\n        country1Boundaries.add(new JtsPolygonConverter().convert(polygon1));\n        country2Boundaries.add(new JtsPolygonConverter().convert(polygon2));\n        boundaries.put(COUNTRY_1_NAME, country1Boundaries);\n        this.countryBoundaries1 = CountryBoundaryMap.fromBoundaryMap(boundaries);\n        boundaries.put(COUNTRY_2_NAME, country2Boundaries);\n        this.countryBoundariesAll = CountryBoundaryMap.fromBoundaryMap(boundaries);\n        this.store = new AtlasPrimitiveObjectStore();\n\n        // Add Nodes\n        this.store.addNode(\n                new AtlasPrimitiveLocationItem(1, Location.CROSSING_85_280, Maps.stringMap()));\n        this.store.addNode(\n                new AtlasPrimitiveLocationItem(2, Location.CROSSING_85_17, Maps.stringMap()));\n        this.store.addNode(new AtlasPrimitiveLocationItem(3, Location.TEST_7, Maps.stringMap()));\n        this.store.addNode(\n                new AtlasPrimitiveLocationItem(4, Location.EIFFEL_TOWER, Maps.stringMap()));\n        this.store.addNode(new AtlasPrimitiveLocationItem(5, Location.COLOSSEUM, Maps.stringMap()));\n        this.store.addNode(new AtlasPrimitiveLocationItem(6, Location.TEST_6, Maps.stringMap()));\n        this.store.addNode(new AtlasPrimitiveLocationItem(8, SHAPEPOINT, Maps.stringMap()));\n        this.store.addNode(new AtlasPrimitiveLocationItem(9,\n                new Segment(Location.TEST_6, Location.TEST_7).middle(),\n                Maps.stringMap(\"tag_key\", \"tag_value\")));\n        this.store.addNode(new AtlasPrimitiveLocationItem(10, DE_ANZA_AT_280,\n                Maps.stringMap(\"tag_key\", \"tag_value\")));\n        this.store.addNode(new AtlasPrimitiveLocationItem(11, OUTSIDE_COUNTRY_1, Maps.stringMap()));\n\n        // Add Edges\n        this.store.addEdge(new AtlasPrimitiveLineItem(3,\n                new PolyLine(Location.CROSSING_85_280, SHAPEPOINT, Location.TEST_7),\n                Maps.stringMap(HighwayTag.KEY, HighwayTag.MOTORWAY.name().toLowerCase())));\n        this.store.addEdge(new AtlasPrimitiveLineItem(7,\n                new PolyLine(Location.CROSSING_85_280, Location.CROSSING_85_17),\n                Maps.stringMap(HighwayTag.KEY, HighwayTag.MOTORWAY.name().toLowerCase())));\n        this.store.addEdge(\n                new AtlasPrimitiveLineItem(8, new PolyLine(Location.TEST_7, DE_ANZA_AT_280),\n                        Maps.stringMap(HighwayTag.KEY, HighwayTag.MOTORWAY.name().toLowerCase())));\n        this.store.addEdge(\n                new AtlasPrimitiveLineItem(9, new PolyLine(DE_ANZA_AT_280, OUTSIDE_COUNTRY_1),\n                        Maps.stringMap(HighwayTag.KEY, HighwayTag.MOTORWAY.name().toLowerCase())));\n\n        // Add Lines\n        final PolyLine line4 = new Segment(Location.TEST_6, Location.TEST_7);\n        this.store.addLine(new AtlasPrimitiveLineItem(4, line4, Maps.stringMap()));\n        final PolyLine line5 = new Segment(Location.COLOSSEUM, Location.EIFFEL_TOWER);\n        this.store.addLine(new AtlasPrimitiveLineItem(5, line5, Maps.stringMap()));\n        this.store.addLine(\n                new AtlasPrimitiveLineItem(6, line4, Maps.stringMap(\"boundary\", \"administrative\")));\n\n        // Add Relation\n        final RelationBean relationBean = new RelationBean();\n        relationBean.addItem(4L, \"first\", ItemType.LINE);\n        relationBean.addItem(5L, \"second\", ItemType.LINE);\n        this.store.addRelation(new AtlasPrimitiveRelation(123, 123, relationBean, Maps.stringMap(),\n                Rectangle.forLocated(line4, line5)));\n    }\n\n    /**\n     * In some cases, ways can be outside the loading area (still inside the osm PBF) and connected\n     * to some other ways that are inside the loading area, but still inside the country boundary.\n     * That happens at shard boundaries, which do a soft cut.\n     *\n     * @throws IOException\n     *             Exception thrown when {@link OsmosisReaderMock} fails to close\n     */\n    @Test\n    public void testBoundaryNodesForWayOutsideLoadingAreaButInsideCountryBoundary()\n            throws IOException\n    {\n        try (OsmosisReaderMock osmosis = new OsmosisReaderMock(this.store))\n        {\n            // Here, reduce the loading bounds to a small area around Edge 3. In this case\n            // Node 3 is included in the country boundary but not in the loading area. It\n            // should not have any boundary tag.\n            final AtlasLoadingOption loadingOption = AtlasLoadingOption.createOptionWithNoSlicing();\n            Atlas atlas = new RawAtlasGenerator(() -> osmosis, loadingOption, MultiPolygon.MAXIMUM)\n                    .build();\n            atlas = new AtlasSectionProcessor(atlas, loadingOption).run();\n            final Edge edgeIn = atlas.edgesIntersecting(DE_ANZA_AT_280.bounds()).iterator().next();\n            final Node nodeIn = edgeIn.end();\n            Assert.assertNull(nodeIn.tag(SyntheticBoundaryNodeTag.KEY));\n        }\n    }\n\n    @Test\n    public void testDifferentEdgeFilterIncludingAccessNo()\n    {\n        final Resource osmFromJosm = new InputStreamResource(\n                () -> OsmPbfIngestTest.class.getResourceAsStream(\"one_way_roads_in_AIA.osm\"));\n        final WritableResource osmFile = new StringResource();\n        final WritableResource pbfFile = new StringResource();\n        final Resource boundaries = new InputStreamResource(\n                () -> OsmPbfIngestTest.class.getResourceAsStream(\"AIA_boundary.txt\"));\n        new OsmFileParser().update(osmFromJosm, osmFile);\n        new OsmFileToPbf().update(osmFile, pbfFile);\n        final CountryBoundaryMap countryBoundaryMap = CountryBoundaryMap.fromPlainText(boundaries);\n        final MultiPolygon boundary = new JtsPolygonToMultiPolygonConverter()\n                .convert(countryBoundaryMap.countryBoundary(\"AIA\").get(0));\n        final AtlasLoadingOption option = AtlasLoadingOption\n                .createOptionWithAllEnabled(countryBoundaryMap);\n        option.setEdgeFilter(new BridgeConfiguredFilter(\"\", \"edge-filter\",\n                new StandardConfiguration(new InputStreamResource(\n                        () -> OsmPbfIngestTest.class.getResourceAsStream(\"edge-filter.json\")))));\n        Atlas atlas = new RawAtlasGenerator(pbfFile, option, boundary).build();\n        atlas = new RawAtlasSlicer(option, atlas).slice();\n        atlas = new AtlasSectionProcessor(atlas, option).run();\n\n        // Edges with access=no that need to be included\n        Assert.assertNotNull(atlas.edge(205527844000002L));\n        Assert.assertNotNull(atlas.edge(205527844000001L));\n        Assert.assertNotNull(atlas.edge(205527857000000L));\n        // Edge without access=no but that needs to be way-sectioned\n        Assert.assertNotNull(atlas.edge(200693734000003L));\n        Assert.assertNotNull(atlas.edge(200693734000004L));\n    }\n\n    @Test\n    public void testEmptyRelations() throws IOException\n    {\n        try (OsmosisReaderMock osmosis = new OsmosisReaderMock(this.store))\n        {\n            final AtlasLoadingOption loadingOption = AtlasLoadingOption\n                    .createOptionWithAllEnabled(this.countryBoundariesAll)\n                    .setCountryCode(COUNTRY_1_NAME);\n            Atlas atlas = new RawAtlasGenerator(() -> osmosis, loadingOption, MultiPolygon.MAXIMUM)\n                    .build();\n            atlas = new RawAtlasSlicer(loadingOption, atlas).slice();\n            atlas = new AtlasSectionProcessor(atlas, AtlasLoadingOption.createOptionWithNoSlicing())\n                    .run();\n            Assert.assertEquals(2, atlas.numberOfLines());\n            Assert.assertEquals(1, atlas.numberOfRelations());\n            final Relation relation = atlas.relations().iterator().next();\n            Assert.assertEquals(1, relation.members().size());\n            Assert.assertEquals(2, atlas.numberOfEdges());\n        }\n    }\n\n    @Test\n    public void testKeepTags() throws IOException\n    {\n        try (OsmosisReaderMock osmosis = new OsmosisReaderMock(this.store))\n        {\n            // AtlasGenerator from atlas-generator does\n            // 1. PBF -> Raw Atlas\n            // 2. Raw Atlas -> Augmented Atlas\n            // 3. Augmented Atlas -> Sliced Atlas\n            // 4. Sliced Atlas -> Sectioned Atlas\n            // For this test, we only need to check AtlasSectionProcessor and RawAtlasSlicer, as\n            // those are the only steps that modify the metadata at this time.\n            final AtlasLoadingOption loadingOption = AtlasLoadingOption\n                    .createOptionWithAllEnabled(this.countryBoundariesAll).setKeepAll(true)\n                    .setCountryCode(COUNTRY_1_NAME);\n            final List<UnaryOperator<Atlas>> atlasFunctions = Arrays.asList(\n                    atlas -> new RawAtlasGenerator(() -> osmosis, loadingOption,\n                            MultiPolygon.MAXIMUM).build(),\n                    atlas -> new RawAtlasSlicer(loadingOption, atlas).slice(),\n                    atlas -> new AtlasSectionProcessor(atlas, loadingOption).run());\n            Atlas atlas = null;\n            for (final UnaryOperator<Atlas> function : atlasFunctions)\n            {\n                atlas = function.apply(atlas);\n                final Supplier<String> messageSupplier = () -> \"Failed on \"\n                        + atlasFunctions.indexOf(function);\n                assertEquals(4, atlas.metaData().getTags().size(), messageSupplier);\n                assertTrue(\n                        atlas.metaData().getTags()\n                                .containsKey(AtlasMetaData.KEEP_ALL_CONFIGURATION),\n                        messageSupplier.get() + \" \"\n                                + String.join(\",\", atlas.metaData().getTags().keySet()));\n                assertEquals(Boolean.TRUE.toString(),\n                        atlas.metaData().getTags().get(AtlasMetaData.KEEP_ALL_CONFIGURATION),\n                        messageSupplier.get() + \" \" + atlas.metaData().getTags()\n                                .get(AtlasMetaData.KEEP_ALL_CONFIGURATION));\n            }\n        }\n    }\n\n    @Test\n    public void testNoFilter() throws IOException\n    {\n        try (OsmosisReaderMock osmosis = new OsmosisReaderMock(this.store))\n        {\n            final AtlasLoadingOption loadingOption = AtlasLoadingOption.withNoFilter();\n            Atlas atlas = new RawAtlasGenerator(() -> osmosis, loadingOption, MultiPolygon.MAXIMUM)\n                    .build();\n            atlas = new AtlasSectionProcessor(atlas, loadingOption).run();\n            Assert.assertEquals(3, atlas.numberOfLines());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/OsmPbfNodeToAtlasItemTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Tests OSM Node to Atlas {@link Node} and {@link Point} conversion given various use cases.\n *\n * @author mgostintsev\n */\npublic class OsmPbfNodeToAtlasItemTest\n{\n    private static final Location OSM_NODE_LOCATION = new Location(Latitude.degrees(7.014003),\n            Longitude.degrees(-10.5466545));\n\n    @Rule\n    public final OsmPbfNodeToAtlasItemTestRule setup = new OsmPbfNodeToAtlasItemTestRule();\n\n    @Test\n    public void testOsmNodeNotInRelationNoTagsAtIntersection()\n    {\n        // Two OSM Ways intersecting at an OSM Node, that has no tags. We expect two Atlas Nodes\n        // created at the end points of each way and an Atlas Node at the intersection.\n        final Atlas atlas = this.setup.getNoRelationNoTagsAtIntersectionAtlas();\n        final Iterable<Node> nodes = atlas.nodesAt(OSM_NODE_LOCATION);\n        final Iterable<Point> points = atlas.pointsAt(OSM_NODE_LOCATION);\n\n        // Verify a Node was created at the middle location\n        assertEquals(1, Iterables.size(nodes));\n        assertEquals(0, Iterables.size(points));\n\n        // Verify nodes created at both end points and middle\n        assertEquals(5, Iterables.size(atlas.nodes()));\n    }\n\n    @Test\n    public void testOsmNodeNotInRelationNoTagsNotAtIntersection()\n    {\n        // Single OSM Way with an OSM Node in the middle without tags. We expect two Atlas Nodes\n        // created at the end points with a shape point in the middle.\n        final Atlas atlas = this.setup.getNoRelationNoTagsNoIntersectionAtlas();\n        final Iterable<Node> nodes = atlas.nodesAt(OSM_NODE_LOCATION);\n        final Iterable<Point> points = atlas.pointsAt(OSM_NODE_LOCATION);\n\n        // Verify nothing was created at the middle location\n        assertEquals(0, Iterables.size(nodes));\n        assertEquals(0, Iterables.size(points));\n\n        // Verify nodes created at end points\n        assertEquals(2, Iterables.size(atlas.nodes()));\n    }\n\n    @Test\n    public void testOsmNodeNotInRelationWithTagsAtIntersection()\n    {\n        // Two OSM Ways intersecting at an OSM node, that has tags. We expect two Atlas Nodes\n        // created at the end points of each line and both an Atlas Node and an Atlas Point at the\n        // intersection.\n        final Atlas atlas = this.setup.getNoRelationWithTagsAtIntersectionAtlas();\n        final Iterable<Node> nodes = atlas.nodesAt(OSM_NODE_LOCATION);\n        final Iterable<Point> points = atlas.pointsAt(OSM_NODE_LOCATION);\n\n        // Verify a Node was created at the middle location\n        assertEquals(1, Iterables.size(nodes));\n        assertEquals(1, Iterables.size(points));\n\n        // Verify nodes created at both end points and middle\n        assertEquals(5, Iterables.size(atlas.nodes()));\n    }\n\n    @Test\n    public void testOsmNodeNotInRelationWithTagsNotAtIntersection()\n    {\n        // Single OSM Way with an OSM Node in the middle with tags. We expect two Atlas Nodes\n        // created at the end points and an Atlas Point in the middle.\n        final Atlas atlas = this.setup.getNoRelationWithTagsNoIntersectionAtlas();\n        final Iterable<Node> nodes = atlas.nodesAt(OSM_NODE_LOCATION);\n        final Iterable<Point> points = atlas.pointsAt(OSM_NODE_LOCATION);\n\n        // Verify a Point was created at the middle location\n        assertEquals(0, Iterables.size(nodes));\n        assertEquals(1, Iterables.size(points));\n\n        // Verify nodes created at end points\n        assertEquals(2, Iterables.size(atlas.nodes()));\n    }\n\n    @Test\n    public void testOsmNodePartOfRelationNoTagsAtIntersection()\n    {\n        // Two OSM Ways meeting at an OSM node with no tags. The two ways are part of a no u-turn\n        // restriction with the OSM Node as the Via role. We expect two Atlas Nodes\n        // created at the end points and a Node in the middle.\n        final Atlas atlas = this.setup.getPartOfRelationNoTagsAtIntersectionAtlas();\n        final Iterable<Node> nodes = atlas.nodesAt(OSM_NODE_LOCATION);\n        final Iterable<Point> points = atlas.pointsAt(OSM_NODE_LOCATION);\n\n        // Verify a Node was created at the middle location\n        assertEquals(1, Iterables.size(nodes));\n        assertEquals(0, Iterables.size(points));\n\n        // Verify nodes created at end points and middle\n        assertEquals(3, Iterables.size(atlas.nodes()));\n    }\n\n    @Test\n    public void testOsmNodePartOfRelationNoTagsNotAtIntersection()\n    {\n        // Three OSM Nodes are part of a Relation. Two of them have tagging, and the middle one\n        // doesn't. We expect all 3 to show up as Atlas Points.\n        final Atlas atlas = this.setup.getPartOfRelationNoTagsNoIntersectionAtlas();\n        final Iterable<Node> nodes = atlas.nodesAt(OSM_NODE_LOCATION);\n        final Iterable<Point> points = atlas.pointsAt(OSM_NODE_LOCATION);\n\n        // Verify a single Point was created at the untagged location\n        assertEquals(0, Iterables.size(nodes));\n        assertEquals(1, Iterables.size(points));\n\n        // Verify only Points were created\n        assertEquals(3, Iterables.size(atlas.points()));\n        assertEquals(0, Iterables.size(atlas.nodes()));\n    }\n\n    @Test\n    public void testOsmNodePartOfRelationWithTagsAtIntersection()\n    {\n        // Two OSM Ways meeting at an OSM node with barrier tags. The two ways are part of a no\n        // u-turn restriction with the OSM Node as the Via role. We expect two Atlas Nodes\n        // created at the end points and an Atlas Node in the middle.\n        final Atlas atlas = this.setup.getPartOfRelationWithTagsAtIntersectionAtlas();\n        final Iterable<Node> nodes = atlas.nodesAt(OSM_NODE_LOCATION);\n        final Iterable<Point> points = atlas.pointsAt(OSM_NODE_LOCATION);\n\n        // Verify a Node was created at the middle location\n        assertEquals(1, Iterables.size(nodes));\n        assertEquals(1, Iterables.size(points));\n\n        // Verify nodes created at end points and middle\n        assertEquals(3, Iterables.size(atlas.nodes()));\n    }\n\n    @Test\n    public void testOsmNodePartOfRelationWithTagsNotAtIntersection()\n    {\n        // A single OSM Way and a single OSM Node (with tags), with a relation associating the Node\n        // as a house to the Way as the Street. We expect two Atlas Nodes created at the end points\n        // of the Way and an Atlas Point to represent the house.\n        final Atlas atlas = this.setup.getPartOfRelationWithTagsNoIntersectionAtlas();\n        final Iterable<Node> nodes = atlas.nodesAt(OSM_NODE_LOCATION);\n        final Iterable<Point> points = atlas.pointsAt(OSM_NODE_LOCATION);\n\n        // Verify a Point was created at the house location\n        assertEquals(0, Iterables.size(nodes));\n        assertEquals(1, Iterables.size(points));\n\n        // Verify nodes created at end points and middle\n        assertEquals(2, Iterables.size(atlas.nodes()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/OsmPbfNodeToAtlasItemTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * {@link OsmPbfNodeToAtlasItemTest} test cases. This class relies on \"loadFromJosmOsmResource\"\n * methods, instead of text atlases, because it relies on loading from PBF and testing the\n * conversion from OSM PBF to Atlas Item. Loading in any other format, would skip this step.\n *\n * @author mgostintsev\n */\npublic class OsmPbfNodeToAtlasItemTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"noRelationNoTagsNoIntersectionAtlas.osm\")\n    private Atlas atlasWithoutRelationNoTagsNoIntersection;\n\n    @TestAtlas(loadFromJosmOsmResource = \"partOfRelationNoTagsAtIntersectionAtlas.osm\")\n    private Atlas atlasPartOfRelationNoTagsAtIntersection;\n\n    @TestAtlas(loadFromJosmOsmResource = \"partOfRelationWithTagsNoIntersectionAtlas.osm\")\n    private Atlas atlasPartOfRelationWithTagsNoIntersection;\n\n    @TestAtlas(loadFromJosmOsmResource = \"partOfRelationWithTagsAtIntersectionAtlas.osm\")\n    private Atlas atlasPartOfRelationWithTagsAtIntersection;\n\n    @TestAtlas(loadFromJosmOsmResource = \"noRelationWithTagsNoIntersectionAtlas.osm\")\n    private Atlas atlasWithoutRelationWithTagsNoIntersection;\n\n    @TestAtlas(loadFromJosmOsmResource = \"noRelationNoTagsAtIntersectionAtlas.osm\")\n    private Atlas atlasWithoutRelationNoTagsAtIntersection;\n\n    @TestAtlas(loadFromJosmOsmResource = \"noRelationWithTagsAtIntersectionAtlas.osm\")\n    private Atlas atlasWithoutRelationWithTagsAtIntersection;\n\n    @TestAtlas(loadFromJosmOsmResource = \"partOfRelationNoTagsNoIntersectionAtlas.osm\")\n    private Atlas atlasPartOfRelationNoTagsNoIntersection;\n\n    public Atlas getNoRelationNoTagsAtIntersectionAtlas()\n    {\n        return this.atlasWithoutRelationNoTagsAtIntersection;\n    }\n\n    public Atlas getNoRelationNoTagsNoIntersectionAtlas()\n    {\n        return this.atlasWithoutRelationNoTagsNoIntersection;\n    }\n\n    public Atlas getNoRelationWithTagsAtIntersectionAtlas()\n    {\n        return this.atlasWithoutRelationWithTagsAtIntersection;\n    }\n\n    public Atlas getNoRelationWithTagsNoIntersectionAtlas()\n    {\n        return this.atlasWithoutRelationWithTagsNoIntersection;\n    }\n\n    public Atlas getPartOfRelationNoTagsAtIntersectionAtlas()\n    {\n        return this.atlasPartOfRelationNoTagsAtIntersection;\n    }\n\n    public Atlas getPartOfRelationNoTagsNoIntersectionAtlas()\n    {\n        return this.atlasPartOfRelationNoTagsNoIntersection;\n    }\n\n    public Atlas getPartOfRelationWithTagsAtIntersectionAtlas()\n    {\n        return this.atlasPartOfRelationWithTagsAtIntersection;\n    }\n\n    public Atlas getPartOfRelationWithTagsNoIntersectionAtlas()\n    {\n        return this.atlasPartOfRelationWithTagsNoIntersection;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/OsmPbfWayToAtlasEdgeTranslationTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\nimport com.google.common.collect.Iterables;\n\n/**\n * Tests various OSM Way tagging combinations to test whether Atlas edges will be correctly created.\n *\n * @author mgostintsev\n */\npublic class OsmPbfWayToAtlasEdgeTranslationTest\n{\n    @Rule\n    public final OsmPbfWayToAtlasEdgeTranslationTestRule rule = new OsmPbfWayToAtlasEdgeTranslationTestRule();\n\n    @Test\n    public void testLoadingFerryWithMotorVehicleNoTag()\n    {\n        final Atlas atlas = this.rule.getFerryRelation5831018Atlas();\n        Assert.assertEquals(\"Three bi-directional way creates 6 edges\", 6,\n                Iterables.size(atlas.edges()));\n        Assert.assertEquals(\"A single valid relation made up of 3 ways\", 1,\n                Iterables.size(atlas.relations()));\n    }\n\n    @Test\n    public void testLoadingPartialRelationWithComplexTagging()\n    {\n        final Atlas atlas = this.rule.getPartialRelation4451979Atlas();\n        Assert.assertEquals(\n                \"One bi-directional way creates 2 edges, plus an additional one-way edge\", 3,\n                Iterables.size(atlas.edges()));\n        Assert.assertEquals(\"A single valid relation made up of 3 ways\", 1,\n                Iterables.size(atlas.relations()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/OsmPbfWayToAtlasEdgeTranslationTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * {@link OsmPbfWayToAtlasEdgeTranslationTest} test rule.\n *\n * @author mgostintsev\n */\npublic class OsmPbfWayToAtlasEdgeTranslationTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromOsmResource = \"partialRelation4451979.osm\")\n    private Atlas partialRelation4451979;\n\n    @TestAtlas(loadFromOsmResource = \"ferryRelation5831018.osm\")\n    private Atlas relation5831018;\n\n    public Atlas getFerryRelation5831018Atlas()\n    {\n        return this.relation5831018;\n    }\n\n    public Atlas getPartialRelation4451979Atlas()\n    {\n        return this.partialRelation4451979;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/OsmosisReaderMock.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf;\n\nimport java.util.HashMap;\n\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveObjectStore;\nimport org.openstreetmap.atlas.geography.atlas.pbf.converters.AtlasPrimitiveAreaToOsmosisWayConverter;\nimport org.openstreetmap.atlas.geography.atlas.pbf.converters.AtlasPrimitiveLineItemToOsmosisWayConverter;\nimport org.openstreetmap.atlas.geography.atlas.pbf.converters.AtlasPrimitiveLocationItemToOsmosisNodeConverter;\nimport org.openstreetmap.atlas.geography.atlas.pbf.converters.AtlasPrimitiveRelationToOsmosisRelationConverter;\nimport org.openstreetmap.atlas.streaming.StringInputStream;\nimport org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;\nimport org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;\nimport org.openstreetmap.osmosis.core.container.v0_6.WayContainer;\nimport org.openstreetmap.osmosis.core.task.v0_6.Sink;\n\n/**\n * Mock an Osmosis reader to be able to test without any PBF resource. Note: This assumes all PBF\n * Nodes making up the Lines/Edges have been added before-hand.\n *\n * @author matthieun\n * @author mgostintsev\n */\npublic class OsmosisReaderMock extends CloseableOsmosisReader\n{\n    private final AtlasPrimitiveObjectStore source;\n    private Sink sink;\n\n    public OsmosisReaderMock(final AtlasPrimitiveObjectStore source)\n    {\n        super(new StringInputStream(\"\"));\n        this.source = source;\n    }\n\n    @Override\n    public void run()\n    {\n        this.sink.initialize(new HashMap<>());\n        // Call the nodes\n        this.source.getNodes().forEach((identifier, node) -> this.sink.process(new NodeContainer(\n                new AtlasPrimitiveLocationItemToOsmosisNodeConverter().convert(node))));\n        this.source.getPoints().forEach((identifier, point) -> this.sink.process(new NodeContainer(\n                new AtlasPrimitiveLocationItemToOsmosisNodeConverter().convert(point))));\n        // Call the ways\n        this.source.getEdges().forEach((identifier, edge) -> this.sink.process(\n                new WayContainer(new AtlasPrimitiveLineItemToOsmosisWayConverter().convert(edge))));\n        this.source.getLines().forEach((identifier, line) -> this.sink.process(\n                new WayContainer(new AtlasPrimitiveLineItemToOsmosisWayConverter().convert(line))));\n        this.source.getAreas().forEach((identifier, area) -> this.sink.process(\n                new WayContainer(new AtlasPrimitiveAreaToOsmosisWayConverter().convert(area))));\n        // Call the relations\n        this.source.getRelations()\n                .forEach((identifier, relation) -> this.sink.process(new RelationContainer(\n                        new AtlasPrimitiveRelationToOsmosisRelationConverter().convert(relation))));\n        // Close\n        this.sink.close();\n        this.sink.complete();\n    }\n\n    @Override\n    public void setSink(final Sink sink)\n    {\n        this.sink = sink;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/converters/AtlasPrimitiveAreaToOsmosisWayConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.converters;\n\nimport java.util.Date;\n\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveArea;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;\nimport org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Way;\n\n/**\n * @author matthieun\n */\npublic class AtlasPrimitiveAreaToOsmosisWayConverter implements Converter<AtlasPrimitiveArea, Way>\n{\n    private static final LocationIterableToWayNodeListConverter LOCATION_ITERABLE_TO_WAY_NODE_LIST_CONVERTER = new LocationIterableToWayNodeListConverter();\n    private static final TagMapToTagCollectionConverter TAG_MAP_TO_TAG_COLLECTION_CONVERTER = new TagMapToTagCollectionConverter();\n\n    @Override\n    public Way convert(final AtlasPrimitiveArea object)\n    {\n        return new Way(\n                new CommonEntityData(object.getIdentifier(), 0, new Date(), new OsmUser(0, \"osm\"),\n                        0, TAG_MAP_TO_TAG_COLLECTION_CONVERTER.convert(object.getTags())),\n                LOCATION_ITERABLE_TO_WAY_NODE_LIST_CONVERTER\n                        .convert(object.getPolygon().closedLoop()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/converters/AtlasPrimitiveLineItemToOsmosisWayConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.converters;\n\nimport java.util.Date;\n\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveLineItem;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;\nimport org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Way;\n\n/**\n * @author matthieun\n */\npublic class AtlasPrimitiveLineItemToOsmosisWayConverter\n        implements Converter<AtlasPrimitiveLineItem, Way>\n{\n    private static final LocationIterableToWayNodeListConverter LOCATION_ITERABLE_TO_WAY_NODE_LIST_CONVERTER = new LocationIterableToWayNodeListConverter();\n    private static final TagMapToTagCollectionConverter TAG_MAP_TO_TAG_COLLECTION_CONVERTER = new TagMapToTagCollectionConverter();\n\n    @Override\n    public Way convert(final AtlasPrimitiveLineItem object)\n    {\n        return new Way(\n                new CommonEntityData(object.getIdentifier(), 0, new Date(), new OsmUser(0, \"osm\"),\n                        0, TAG_MAP_TO_TAG_COLLECTION_CONVERTER.convert(object.getTags())),\n                LOCATION_ITERABLE_TO_WAY_NODE_LIST_CONVERTER.convert(object.getPolyLine()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/converters/AtlasPrimitiveLocationItemToOsmosisNodeConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.converters;\n\nimport java.util.Date;\n\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveLocationItem;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Node;\nimport org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;\n\n/**\n * @author matthieun\n */\npublic class AtlasPrimitiveLocationItemToOsmosisNodeConverter\n        implements Converter<AtlasPrimitiveLocationItem, Node>\n{\n    private static final TagMapToTagCollectionConverter TAG_MAP_TO_TAG_COLLECTION_CONVERTER = new TagMapToTagCollectionConverter();\n\n    @Override\n    public Node convert(final AtlasPrimitiveLocationItem object)\n    {\n        return new Node(\n                new CommonEntityData(object.getLocation().asConcatenation(), 0, new Date(),\n                        new OsmUser(0, \"osm\"), 0,\n                        TAG_MAP_TO_TAG_COLLECTION_CONVERTER.convert(object.getTags())),\n                object.getLocation().getLatitude().asDegrees(),\n                object.getLocation().getLongitude().asDegrees());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/converters/AtlasPrimitiveRelationToOsmosisRelationConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.converters;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveRelation;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;\nimport org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Relation;\nimport org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;\n\n/**\n * @author matthieun\n */\npublic class AtlasPrimitiveRelationToOsmosisRelationConverter\n        implements Converter<AtlasPrimitiveRelation, Relation>\n{\n    private static final ItemTypeToEntityTypeConverter ITEM_TYPE_TO_ENTITY_TYPE_CONVERTER = new ItemTypeToEntityTypeConverter();\n    private static final TagMapToTagCollectionConverter TAG_MAP_TO_TAG_COLLECTION_CONVERTER = new TagMapToTagCollectionConverter();\n\n    @Override\n    public Relation convert(final AtlasPrimitiveRelation object)\n    {\n        final List<RelationMember> members = new ArrayList<>();\n        final RelationBean bean = object.getRelationBean();\n        for (int index = 0; index < bean.size(); index++)\n        {\n            members.add(new RelationMember(bean.getMemberIdentifiers().get(index),\n                    ITEM_TYPE_TO_ENTITY_TYPE_CONVERTER.convert(bean.getMemberTypes().get(index)),\n                    bean.getMemberRoles().get(index)));\n        }\n        return new Relation(\n                new CommonEntityData(object.getIdentifier(), 0, new Date(), new OsmUser(0, \"osm\"),\n                        0, TAG_MAP_TO_TAG_COLLECTION_CONVERTER.convert(object.getTags())),\n                members);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/converters/ItemTypeToEntityTypeConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.converters;\n\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.osmosis.core.domain.v0_6.EntityType;\n\n/**\n * @author matthieun\n */\npublic class ItemTypeToEntityTypeConverter implements Converter<ItemType, EntityType>\n{\n    @Override\n    public EntityType convert(final ItemType object)\n    {\n        switch (object)\n        {\n            case NODE:\n                return EntityType.Node;\n            case EDGE:\n                return EntityType.Way;\n            case AREA:\n                return EntityType.Way;\n            case LINE:\n                return EntityType.Way;\n            case POINT:\n                return EntityType.Node;\n            case RELATION:\n                return EntityType.Relation;\n            default:\n                throw new CoreException(\"Invalid ItemType: {}\", object);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/converters/LocationIterableToWayNodeListConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.converters;\n\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.conversion.Converter;\nimport org.openstreetmap.osmosis.core.domain.v0_6.WayNode;\n\n/**\n * @author matthieun\n */\npublic class LocationIterableToWayNodeListConverter\n        implements Converter<Iterable<? extends Location>, List<WayNode>>\n{\n    @Override\n    public List<WayNode> convert(final Iterable<? extends Location> object)\n    {\n        return Iterables.stream(object).map(location -> new WayNode(location.asConcatenation()))\n                .collectToList();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/converters/LocationToOsmosisNodeConverter.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.converters;\n\nimport java.util.Date;\n\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.utilities.conversion.TwoWayConverter;\nimport org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;\nimport org.openstreetmap.osmosis.core.domain.v0_6.Node;\nimport org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;\n\n/**\n * @author matthieun\n * @author mgostintsev\n */\npublic class LocationToOsmosisNodeConverter implements TwoWayConverter<Location, Node>\n{\n    @Override\n    public Location backwardConvert(final Node object)\n    {\n        return new Location(Latitude.degrees(object.getLatitude()),\n                Longitude.degrees(object.getLongitude()));\n    }\n\n    @Override\n    public Node convert(final Location object)\n    {\n        return new Node(new CommonEntityData(object.asConcatenation(), 0, new Date(),\n                new OsmUser(0, \"osm\"), 0), object.getLatitude().asDegrees(),\n                object.getLongitude().asDegrees());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/slicing/identifier/ReverseIdentifierFactoryTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * {@link ReverseIdentifierFactory} test.\n *\n * @author mgostintsev\n */\npublic class ReverseIdentifierFactoryTest\n{\n    private static final ReverseIdentifierFactory IDENTIFIER_FACTORY = new ReverseIdentifierFactory();\n\n    @Test\n    public void testMinimumCountryOsmIdentifier()\n    {\n        final long atlasIdentifier = Long.MIN_VALUE;\n        final long countryOsmIdentifier = IDENTIFIER_FACTORY\n                .getCountryOsmIdentifier(atlasIdentifier);\n        Assert.assertTrue(countryOsmIdentifier > 0);\n    }\n\n    @Test\n    public void testMinimumOsmIdentifier()\n    {\n        final long atlasIdentifier = Long.MIN_VALUE;\n        final long osmIdentifier = IDENTIFIER_FACTORY.getOsmIdentifier(atlasIdentifier);\n        Assert.assertTrue(osmIdentifier > 0);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/pbf/store/PbfOneWayTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.pbf.store;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.raw.sectioning.PbfOneWay;\nimport org.openstreetmap.atlas.tags.AccessTag;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.JunctionTag;\nimport org.openstreetmap.atlas.tags.MotorVehicleTag;\nimport org.openstreetmap.atlas.tags.MotorcarTag;\nimport org.openstreetmap.atlas.tags.RouteTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.VehicleTag;\nimport org.openstreetmap.atlas.tags.oneway.OneWayTag;\n\n/**\n * Testing {@link PbfOneWay} functionality.\n *\n * @author mgostintsev\n */\npublic class PbfOneWayTest\n{\n    @Test\n    public void testOneWayCriteria()\n    {\n        final Taggable primary = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase());\n        final Taggable primaryAccessNoMotorVehicleYes = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), AccessTag.KEY,\n                AccessTag.NO.name().toLowerCase(), MotorVehicleTag.KEY,\n                MotorVehicleTag.YES.name().toLowerCase());\n        final Taggable primaryAccessNoMotorCarYes = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), AccessTag.KEY,\n                AccessTag.NO.name().toLowerCase(), MotorcarTag.KEY,\n                MotorcarTag.YES.name().toLowerCase());\n        final Taggable primaryAccessNoVehicleYes = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), AccessTag.KEY,\n                AccessTag.NO.name().toLowerCase(), VehicleTag.KEY,\n                VehicleTag.YES.name().toLowerCase());\n        final Taggable primaryAccessNoMotorVehicleYesOneWayTrue = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), AccessTag.KEY,\n                AccessTag.NO.name().toLowerCase(), MotorVehicleTag.KEY,\n                MotorVehicleTag.YES.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.TRUE.name().toLowerCase());\n        final Taggable primaryAccessNoMotorCarYesOneWayTrue = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), AccessTag.KEY,\n                AccessTag.NO.name().toLowerCase(), MotorcarTag.KEY,\n                MotorcarTag.YES.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.TRUE.name().toLowerCase());\n        final Taggable primaryAccessNoVehicleYesOneWayTrue = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), AccessTag.KEY,\n                AccessTag.NO.name().toLowerCase(), VehicleTag.KEY,\n                VehicleTag.YES.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.TRUE.name().toLowerCase());\n        final Taggable primaryMotorVehicleNo = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), MotorVehicleTag.KEY,\n                MotorVehicleTag.NO.name().toLowerCase());\n        final Taggable primaryAccessYesVehicleNo = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), AccessTag.KEY,\n                AccessTag.YES.name().toLowerCase(), MotorVehicleTag.KEY,\n                MotorVehicleTag.NO.name().toLowerCase());\n        final Taggable primaryAccessDeliveryMotorcarNo = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), AccessTag.KEY,\n                AccessTag.DELIVERY.name().toLowerCase(), MotorcarTag.KEY,\n                MotorcarTag.NO.name().toLowerCase());\n        final Taggable motorway = Taggable.with(HighwayTag.KEY,\n                HighwayTag.MOTORWAY.name().toLowerCase());\n        final Taggable twoWayMotorway = Taggable.with(HighwayTag.KEY,\n                HighwayTag.MOTORWAY.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.NO.name().toLowerCase());\n        final Taggable motorwayLink = Taggable.with(HighwayTag.KEY,\n                HighwayTag.MOTORWAY_LINK.name().toLowerCase());\n        final Taggable roundabout = Taggable.with(JunctionTag.KEY,\n                JunctionTag.ROUNDABOUT.name().toLowerCase());\n        final Taggable explicitlyTwoWay = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.NO.name().toLowerCase());\n        final Taggable explicitlyTwoWayUsingFalse = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.FALSE.name().toLowerCase());\n        final Taggable explicitlyTwoWayUsingZero = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), OneWayTag.KEY, \"0\");\n        final Taggable explicitlyOneWay = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.YES.name().toLowerCase());\n        final Taggable explicitlyOneWayUsingTrue = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.TRUE.name().toLowerCase());\n        final Taggable explicitlyOneWayUsingOne = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), OneWayTag.KEY, \"1\");\n        final Taggable oneWayReversed = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), OneWayTag.KEY,\n                OneWayTag.REVERSE.name().toLowerCase());\n        final Taggable oneWayReversedUsingNegativeOne = Taggable.with(HighwayTag.KEY,\n                HighwayTag.PRIMARY.name().toLowerCase(), OneWayTag.KEY, \"-1\");\n        final Taggable ferryMotorVehicleNo = Taggable.with(RouteTag.KEY,\n                RouteTag.FERRY.name().toLowerCase(), MotorVehicleTag.KEY,\n                MotorVehicleTag.NO.name().toLowerCase());\n\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(primary));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(motorwayLink));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(twoWayMotorway));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(explicitlyTwoWay));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(explicitlyTwoWayUsingZero));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(explicitlyTwoWayUsingFalse));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(primaryAccessNoMotorVehicleYes));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(primaryAccessNoMotorCarYes));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(primaryAccessNoVehicleYes));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(primaryMotorVehicleNo));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(primaryAccessYesVehicleNo));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(primaryAccessDeliveryMotorcarNo));\n        Assert.assertEquals(PbfOneWay.NO, PbfOneWay.forTag(ferryMotorVehicleNo));\n\n        Assert.assertEquals(PbfOneWay.YES, PbfOneWay.forTag(motorway));\n        Assert.assertEquals(PbfOneWay.YES, PbfOneWay.forTag(roundabout));\n        Assert.assertEquals(PbfOneWay.YES, PbfOneWay.forTag(explicitlyOneWay));\n        Assert.assertEquals(PbfOneWay.YES, PbfOneWay.forTag(explicitlyOneWayUsingTrue));\n        Assert.assertEquals(PbfOneWay.YES, PbfOneWay.forTag(explicitlyOneWayUsingOne));\n        Assert.assertEquals(PbfOneWay.YES,\n                PbfOneWay.forTag(primaryAccessNoMotorVehicleYesOneWayTrue));\n        Assert.assertEquals(PbfOneWay.YES, PbfOneWay.forTag(primaryAccessNoMotorCarYesOneWayTrue));\n        Assert.assertEquals(PbfOneWay.YES, PbfOneWay.forTag(primaryAccessNoVehicleYesOneWayTrue));\n\n        Assert.assertEquals(PbfOneWay.REVERSED, PbfOneWay.forTag(oneWayReversed));\n        Assert.assertEquals(PbfOneWay.REVERSED, PbfOneWay.forTag(oneWayReversedUsingNegativeOne));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/raw/RawAtlasTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.creation.RawAtlasGenerator;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileParser;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileToPbf;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n * @author mgostintsev\n */\npublic class RawAtlasTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(RawAtlasTest.class);\n\n    @Test\n    public void testBringInConnectedOutsideWays()\n    {\n        final Resource osmFromJosm = new InputStreamResource(() -> RawAtlasTest.class\n                .getResourceAsStream(\"osmPbfProcessorTest_keepOutsideWaysThatAreConnected.osm\"));\n        final WritableResource osmFile = new StringResource();\n        final WritableResource pbfFile = new StringResource();\n        final Resource boundaries = new InputStreamResource(\n                () -> RawAtlasTest.class.getResourceAsStream(\"DMA_boundary.txt\"));\n        new OsmFileParser().update(osmFromJosm, osmFile);\n        new OsmFileToPbf().update(osmFile, pbfFile);\n        final CountryBoundaryMap countryBoundaryMap = CountryBoundaryMap.fromPlainText(boundaries);\n        final JtsPolygonToMultiPolygonConverter converter = new JtsPolygonToMultiPolygonConverter();\n        final MultiPolygon boundary = converter\n                .convert(countryBoundaryMap.countryBoundary(\"DMA\").get(0));\n        logger.debug(\"Boundary: {}\", boundary.toWkt());\n        final AtlasLoadingOption option = AtlasLoadingOption\n                .createOptionWithAllEnabled(countryBoundaryMap);\n        final RawAtlasGenerator generator = new RawAtlasGenerator(pbfFile, option, boundary);\n        final Atlas rawAtlas = generator.build();\n        logger.debug(\"Raw Atlas: {}\", rawAtlas);\n\n        // Line partially inside, should be included\n        // 38986000000\n        Assert.assertNotNull(rawAtlas.line(38986000000L));\n\n        // Line totally outside, but connected\n        // 39002000000\n        Assert.assertNotNull(rawAtlas.line(39002000000L));\n        // 39019000000\n        Assert.assertNotNull(rawAtlas.line(39019000000L));\n\n        option.setCountryCode(\"DMA\");\n        final Atlas slicedAtlas = new RawAtlasSlicer(option, rawAtlas).slice();\n        logger.debug(\"Sliced Atlas: {}\", slicedAtlas);\n\n        // Line partially inside, should be included\n        // 38986000000\n        Assert.assertNotNull(slicedAtlas.line(38986000000L));\n\n        // Line totally outside, but connected\n        // 39002000000\n        Assert.assertNull(slicedAtlas.line(39002000000L));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/raw/creation/RawAtlasGeneratorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.creation;\n\nimport java.nio.file.FileSystems;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveLineItem;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveLocationItem;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveObjectStore;\nimport org.openstreetmap.atlas.geography.atlas.builder.store.AtlasPrimitiveRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.OsmosisReaderMock;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.tags.SyntheticDuplicateOsmNodeTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * Tests {@link RawAtlasGenerator} Raw Atlas creation. These test functionality for various Relation\n * cases, tests edge cases surrounding corrupt or incomplete PBF files.\n *\n * @author mgostintsev\n */\npublic class RawAtlasGeneratorTest\n{\n    private static final Map<String, String> EMPTY = new HashMap<>();\n\n    @Test\n    public void testLoadingPbfWithPointAsRelationMember()\n    {\n        final AtlasPrimitiveObjectStore store = new AtlasPrimitiveObjectStore();\n\n        // Create 2 nodes\n        store.addPoint(new AtlasPrimitiveLocationItem(1, Location.TEST_1, EMPTY));\n        store.addPoint(new AtlasPrimitiveLocationItem(2, Location.TEST_2, EMPTY));\n\n        // Create line 1\n        final PolyLine line1 = new Segment(Location.TEST_1, Location.TEST_2);\n        store.addLine(new AtlasPrimitiveLineItem(3, line1, EMPTY));\n\n        // Create line 2\n        final PolyLine line2 = new Segment(Location.TEST_2, Location.TEST_1);\n        store.addLine(new AtlasPrimitiveLineItem(4, line2, EMPTY));\n\n        // Create simple U-turn relation\n        final RelationBean relationBean = new RelationBean();\n        relationBean.addItem(3L, \"to\", ItemType.LINE);\n        relationBean.addItem(2L, \"via\", ItemType.POINT);\n        relationBean.addItem(4L, \"from\", ItemType.LINE);\n        store.addRelation(new AtlasPrimitiveRelation(123, 123, relationBean, Maps.stringMap(),\n                Rectangle.forLocated(line1, line2)));\n\n        @SuppressWarnings(\"resource\")\n        final OsmosisReaderMock osmosis = new OsmosisReaderMock(store);\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(() -> osmosis,\n                AtlasLoadingOption.createOptionWithNoSlicing(), MultiPolygon.MAXIMUM);\n        final Atlas atlas = rawAtlasGenerator.build();\n\n        // Verify Atlas Entities\n        assertBasicRawAtlasPrinciples(atlas);\n        Assert.assertEquals(2, atlas.numberOfLines());\n        Assert.assertEquals(2, atlas.numberOfPoints());\n        Assert.assertEquals(1, atlas.numberOfRelations());\n    }\n\n    @Test\n    public void testLoadingPbfWithWayThatReferencesMissingNode()\n    {\n        final AtlasPrimitiveObjectStore store = new AtlasPrimitiveObjectStore();\n\n        // Add Points\n        store.addPoint(new AtlasPrimitiveLocationItem(1, Location.TEST_1, EMPTY));\n        store.addPoint(new AtlasPrimitiveLocationItem(2, Location.TEST_2, EMPTY));\n\n        // Add Lines\n        store.addLine(new AtlasPrimitiveLineItem(3, new Segment(Location.TEST_1, Location.TEST_2),\n                EMPTY));\n        store.addLine(new AtlasPrimitiveLineItem(4, new Segment(Location.TEST_3, Location.TEST_4),\n                EMPTY));\n\n        @SuppressWarnings(\"resource\")\n        final OsmosisReaderMock osmosis = new OsmosisReaderMock(store);\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(() -> osmosis,\n                AtlasLoadingOption.createOptionWithNoSlicing(), MultiPolygon.MAXIMUM);\n\n        // The raw Atlas should not get built, as one of the nodes referenced by the PBF is missing.\n        final Atlas atlas = rawAtlasGenerator.build();\n\n        // Verify Atlas Entities\n        assertBasicRawAtlasPrinciples(atlas);\n        Assert.assertEquals(1, atlas.numberOfLines());\n        Assert.assertEquals(2, atlas.numberOfPoints());\n        Assert.assertEquals(0, atlas.numberOfRelations());\n    }\n\n    @Test\n    public void testNestedSingleRelations()\n    {\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(\n                new InputStreamResource(() -> RawAtlasGeneratorTest.class\n                        .getResourceAsStream(\"nestedSingleRelations.osm.pbf\")));\n        final Atlas atlas = rawAtlasGenerator.build();\n\n        // Verify Atlas Entities\n        assertBasicRawAtlasPrinciples(atlas);\n        // A duplicate point is removed\n        Assert.assertEquals(5, atlas.numberOfPoints());\n        Assert.assertEquals(0, atlas.numberOfLines());\n        Assert.assertEquals(1, atlas.numberOfAreas());\n        Assert.assertEquals(2, atlas.numberOfRelations());\n    }\n\n    @Test\n    public void testNestedSingleRelationsKeepAll()\n    {\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(\n                new InputStreamResource(() -> RawAtlasGeneratorTest.class\n                        .getResourceAsStream(\"nestedSingleRelations.osm.pbf\")),\n                AtlasLoadingOption.withNoFilter().setKeepAll(true), MultiPolygon.MAXIMUM);\n\n        final Atlas atlas = rawAtlasGenerator.build();\n\n        // Verify Atlas Entities\n        assertBasicRawAtlasPrinciples(atlas);\n        // The duplicate point is not removed\n        Assert.assertEquals(6, atlas.numberOfPoints());\n        Assert.assertEquals(0, atlas.numberOfLines());\n        Assert.assertEquals(1, atlas.numberOfAreas());\n        // No relations are dropped (there are four in the source PBF: `parent1`, `parent1-1`,\n        // `parent2`, `parent2-2`).\n        Assert.assertEquals(4, atlas.numberOfRelations());\n    }\n\n    @Test\n    public void testPbfWithIncompleteRelations()\n    {\n        // This PBF has several interesting use cases. 1. It will have relations of relations. 2.\n        // Some of the member relations will be outside of the PBF and will be missing.\n        final String path = RawAtlasGeneratorTest.class.getResource(\"7-105-51.osm.pbf\").getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));\n        final Atlas atlas = rawAtlasGenerator.build();\n\n        // Verify Atlas Entities\n        assertBasicRawAtlasPrinciples(atlas);\n        Assert.assertEquals(467634, atlas.numberOfPoints());\n        Assert.assertEquals(13335, atlas.numberOfAreas());\n        Assert.assertEquals(32768, atlas.numberOfLines());\n        Assert.assertEquals(477, atlas.numberOfRelations());\n        Assert.assertEquals(49, Iterables.size(atlas.points(\n                point -> Validators.hasValuesFor(point, SyntheticDuplicateOsmNodeTag.class))));\n    }\n\n    @Test\n    public void testRawAtlasCreation()\n    {\n        final String path = RawAtlasGeneratorTest.class.getResource(\"9-433-268.osm.pbf\").getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));\n        final Atlas atlas = rawAtlasGenerator.build();\n\n        // Verify Atlas Entities\n        assertBasicRawAtlasPrinciples(atlas);\n        Assert.assertEquals(55250, atlas.numberOfPoints());\n        Assert.assertEquals(5399, atlas.numberOfAreas());\n        Assert.assertEquals(694, atlas.numberOfLines());\n        Assert.assertEquals(11, atlas.numberOfRelations());\n        // Duplicated nodes (same tags all round). These are deduplicated without the keepAll flag\n        // in the atlas loading options.\n        Assert.assertEquals(1, Iterables\n                .size(atlas.points(1070166221000000L, 1070191833000000L, 1070195543000000L)));\n    }\n\n    @Test\n    public void testRawAtlasCreationKeepAll()\n    {\n        final String path = RawAtlasGeneratorTest.class.getResource(\"9-433-268.osm.pbf\").getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(\n                new File(path, FileSystems.getDefault()),\n                AtlasLoadingOption.withNoFilter().setKeepAll(true), MultiPolygon.MAXIMUM);\n        final Atlas atlas = rawAtlasGenerator.build();\n\n        // Verify Atlas Entities\n        assertBasicRawAtlasPrinciples(atlas);\n        Assert.assertEquals(55265, atlas.numberOfPoints());\n        Assert.assertEquals(5399, atlas.numberOfAreas());\n        Assert.assertEquals(694, atlas.numberOfLines());\n        Assert.assertEquals(11, atlas.numberOfRelations());\n        // Duplicated nodes (same tags all round). These are deduplicated without the keepAll flag\n        // in the atlas loading options.\n        Assert.assertEquals(3, Iterables\n                .size(atlas.points(1070166221000000L, 1070191833000000L, 1070195543000000L)));\n    }\n\n    @Test\n    public void testRawAtlasCreationWithBoundingBox()\n    {\n        final String path = RawAtlasGeneratorTest.class.getResource(\"9-433-268.osm.pbf\").getPath();\n        final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path),\n                MultiPolygon.forPolygon(Location.forWkt(\"POINT (124.9721500 -8.9466200)\").bounds()\n                        .expand(Distance.meters(100))));\n        final Atlas atlas = rawAtlasGenerator.build();\n\n        // Verify Atlas Entities\n        assertBasicRawAtlasPrinciples(atlas);\n        Assert.assertEquals(3867, atlas.numberOfPoints());\n        Assert.assertEquals(35, atlas.numberOfLines());\n        Assert.assertEquals(2, atlas.numberOfAreas());\n        Assert.assertEquals(1, atlas.numberOfRelations());\n    }\n\n    private void assertBasicRawAtlasPrinciples(final Atlas atlas)\n    {\n        // The Raw Atlas should never contain Nodes, Edges or Areas\n        Assert.assertEquals(0, atlas.numberOfNodes());\n        Assert.assertEquals(0, atlas.numberOfEdges());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/raw/sectioning/AtlasSectionProcessorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.sectioning;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicerTest;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonConverter;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.tags.BarrierTag;\nimport org.openstreetmap.atlas.tags.SyntheticInvalidWaySectionTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * {@link AtlasSectionProcessor} unit tests\n *\n * @author mgostintsev\n */\npublic class AtlasSectionProcessorTest\n{\n    private static final CountryBoundaryMap COUNTRY_BOUNDARY_MAP;\n\n    static\n    {\n        COUNTRY_BOUNDARY_MAP = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> RawAtlasSlicerTest.class\n                        .getResourceAsStream(\"CIV_GIN_LBR_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n    }\n\n    @Rule\n    public WaySectionProcessorTestRule setup = new WaySectionProcessorTestRule();\n\n    @Test\n    public void testBidirectionalRing()\n    {\n        // Based on https://www.openstreetmap.org/way/317579533\n        final Atlas slicedRawAtlas = this.setup.getBidirectionalRingAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Two edges, each having a reverse counterpart\", 4,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes\", 2, finalAtlas.numberOfNodes());\n\n        // Explicit check for expected identifiers\n        Assert.assertNotNull(finalAtlas.edge(317579533000001L));\n        Assert.assertNotNull(finalAtlas.edge(-317579533000001L));\n        Assert.assertNotNull(finalAtlas.edge(317579533000002L));\n        Assert.assertNotNull(finalAtlas.edge(-317579533000002L));\n    }\n\n    @Test\n    public void testCutAtShardBoundary()\n    {\n        final Atlas slicedRawAtlas = this.setup.getRawAtlasSpanningOutsideBoundary();\n        final Atlas finalAtlas = new AtlasSectionProcessor(SlippyTile.forName(\"8-123-123\"),\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP),\n                new SlippyTileSharding(8), shard -> Optional.of(slicedRawAtlas)).run();\n\n        // Assert the number to make sure the edges outside the shard have been excluded\n        Assert.assertEquals(4, finalAtlas.numberOfNodes());\n        Assert.assertEquals(3, finalAtlas.numberOfEdges());\n\n        // Nodes\n        Assert.assertNotNull(finalAtlas.node(112428000000L));\n        Assert.assertNotNull(finalAtlas.node(112430000000L));\n        Assert.assertNotNull(finalAtlas.node(112441000000L));\n        Assert.assertNotNull(finalAtlas.node(112427000000L));\n\n        // Edges\n        Assert.assertNotNull(finalAtlas.edge(112429000001L));\n        Assert.assertNotNull(finalAtlas.edge(112429000002L));\n        Assert.assertNotNull(finalAtlas.edge(112440000001L));\n    }\n\n    @Test\n    public void testLineWithLessThanTwoNodesDueToRepeatedLocationAtEndOfLine()\n    {\n        // Based on a prior version of https://www.openstreetmap.org/way/488453376\n        final Atlas slicedRawAtlas = this.setup\n                .getLineWithLessThanTwoNodesDueToRepeatedLocationAtEndOfLineAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertNotNull(\"Line has been converted to an Edge\",\n                finalAtlas.edge(488453376000000L));\n        Assert.assertNull(\"Line has not been converted to an Edge\",\n                finalAtlas.line(488453376000000L));\n        final Map<String, String> originalTags = slicedRawAtlas.line(488453376000000L).getTags();\n        final Map<String, String> finalTags = finalAtlas.edge(488453376000000L).getTags();\n        Assert.assertEquals(\"Edge has no other tag changes\", originalTags, finalTags);\n    }\n\n    @Test\n    public void testLineWithLoopAtEnd()\n    {\n        // Based on https://www.openstreetmap.org/way/461101743\n        final Atlas slicedRawAtlas = this.setup.getLineWithLoopAtEndAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Two edges, each having a reverse counterpart\", 4,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes\", 2, finalAtlas.numberOfNodes());\n        Assert.assertEquals(4, finalAtlas.node(4566499618000000L).connectedEdges().size());\n        Assert.assertEquals(2, finalAtlas.node(4566499619000000L).connectedEdges().size());\n\n        // Explicit check for expected identifiers\n        Assert.assertNotNull(finalAtlas.edge(461101743000001L));\n        Assert.assertNotNull(finalAtlas.edge(-461101743000001L));\n        Assert.assertNotNull(finalAtlas.edge(461101743000002L));\n        Assert.assertNotNull(finalAtlas.edge(-461101743000002L));\n    }\n\n    @Test\n    public void testLineWithLoopAtStart()\n    {\n        // Based on https://www.openstreetmap.org/way/460419987\n        final Atlas slicedRawAtlas = this.setup.getLineWithLoopAtStartAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Three edges, each having a reverse counterpart\", 6,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Three nodes\", 3, finalAtlas.numberOfNodes());\n        Assert.assertEquals(4, finalAtlas.node(4560902689000000L).connectedEdges().size());\n        Assert.assertEquals(2, finalAtlas.node(4560902695000000L).connectedEdges().size());\n        Assert.assertEquals(4, finalAtlas.node(4560902693000000L).connectedEdges().size());\n\n        // Explicit check for expected identifiers\n        Assert.assertNotNull(finalAtlas.edge(460419987000001L));\n        Assert.assertNotNull(finalAtlas.edge(-460419987000001L));\n        Assert.assertNotNull(finalAtlas.edge(460419987000002L));\n        Assert.assertNotNull(finalAtlas.edge(-460419987000002L));\n        Assert.assertNotNull(finalAtlas.edge(460419987000003L));\n        Assert.assertNotNull(finalAtlas.edge(-460419987000003L));\n    }\n\n    @Test\n    public void testLineWithLoopInMiddle()\n    {\n        // Loosely based on https://www.openstreetmap.org/way/460419987 - with a new added node\n        final Atlas slicedRawAtlas = this.setup.getLineWithLoopInMiddleAtlas();\n\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Four edges, each having a reverse counterpart\", 8,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Four nodes\", 4, finalAtlas.numberOfNodes());\n        Assert.assertEquals(2, finalAtlas.node(4560902695000000L).connectedEdges().size());\n        Assert.assertEquals(4, finalAtlas.node(4560902693000000L).connectedEdges().size());\n        Assert.assertEquals(2, finalAtlas.node(4560902612000000L).connectedEdges().size());\n        Assert.assertEquals(6, finalAtlas.node(4560902689000000L).connectedEdges().size());\n\n        // Explicit check for expected identifiers\n        Assert.assertNotNull(finalAtlas.edge(460419987000001L));\n        Assert.assertNotNull(finalAtlas.edge(-460419987000001L));\n        Assert.assertNotNull(finalAtlas.edge(460419987000002L));\n        Assert.assertNotNull(finalAtlas.edge(-460419987000003L));\n        Assert.assertNotNull(finalAtlas.edge(460419987000003L));\n        Assert.assertNotNull(finalAtlas.edge(-460419987000003L));\n        Assert.assertNotNull(finalAtlas.edge(460419987000004L));\n        Assert.assertNotNull(finalAtlas.edge(-460419987000004L));\n    }\n\n    @Test\n    public void testLineWithRepeatedLocation()\n    {\n        // Based on a prior version of https://www.openstreetmap.org/way/488453376\n        final Atlas slicedRawAtlas = this.setup.getLineWithRepeatedLocationAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Four edges, each having a reverse counterpart\", 4,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes\", 2, finalAtlas.numberOfNodes());\n        Assert.assertEquals(\"No points\", 0, finalAtlas.numberOfPoints());\n        Assert.assertEquals(\"This way got sectioned 4 times, with reverse edges\", 4,\n                Iterables.size(finalAtlas.edges(edge -> edge.getOsmIdentifier() == 488453376L)));\n    }\n\n    @Test\n    public void testLineWithRepeatedLocationKeepAll()\n    {\n        // Based on a prior version of https://www.openstreetmap.org/way/488453376\n        final Atlas slicedRawAtlas = this.setup.getLineWithRepeatedLocationAtlas();\n        // setKeepAll is not set in createOptionWithAllEnabled since it may break downstream users.\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas, AtlasLoadingOption\n                .createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP).setKeepAll(true)).run();\n\n        Assert.assertEquals(\"Four edges, each having a reverse counterpart\", 4,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes\", 2, finalAtlas.numberOfNodes());\n        Assert.assertEquals(\"Eight points\", 8, finalAtlas.numberOfPoints());\n        Assert.assertEquals(\"This way got sectioned 4 times, with reverse edges\", 4,\n                Iterables.size(finalAtlas.edges(edge -> edge.getOsmIdentifier() == 488453376L)));\n    }\n\n    @Test\n    public void testLoopWithIntersection()\n    {\n        // Based on https://www.openstreetmap.org/way/310540517 and partial excerpt of\n        // https://www.openstreetmap.org/way/310540519\n        final Atlas slicedRawAtlas = this.setup.getLoopingWayWithIntersectionAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Four edges, each having a reverse counterpart\", 8,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Four nodes\", 4, finalAtlas.numberOfNodes());\n        Assert.assertEquals(\"This way got sectioned 3 times, with reverse edges\", 6,\n                Iterables.size(finalAtlas.edges(edge -> edge.getOsmIdentifier() == 310540517L)));\n        Assert.assertEquals(\"This edge got sectioned once, with reverse edges\", 2,\n                Iterables.size(finalAtlas.edges(edge -> edge.getOsmIdentifier() == 310540519L)));\n    }\n\n    @Test\n    public void testLoopWithRepeatedLocation()\n    {\n        // Based on a prior version of https://www.openstreetmap.org/way/488453376 with a piece of\n        // https://www.openstreetmap.org/way/386313688\n        final Atlas slicedRawAtlas = this.setup.getLoopWithRepeatedLocationAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Six edges, each having a reverse counterpart\", 6,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Three nodes\", 3, finalAtlas.numberOfNodes());\n        Assert.assertEquals(\"This way got sectioned twice, with reverse edges\", 4,\n                Iterables.size(finalAtlas.edges(edge -> edge.getOsmIdentifier() == 488453376L)));\n    }\n\n    @Test\n    public void testMalformedPolyLine()\n    {\n        // Based on a prior version of https://www.openstreetmap.org/way/621043891\n        final Atlas slicedRawAtlas = this.setup.getMalformedPolyLineAtlas();\n        final CountryBoundaryMap boundaryMap = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> AtlasSectionProcessorTest.class\n                        .getResourceAsStream(\"malformedPolyLineBoundaryMap.txt\")));\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(boundaryMap)).run();\n\n        Assert.assertEquals(\"Six edges, each having a reverse counterpart\", 12,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Four nodes\", 4, finalAtlas.numberOfNodes());\n    }\n\n    @Test\n    public void testOneWayRing()\n    {\n        // Based on https://www.openstreetmap.org/way/460257372\n        final Atlas slicedRawAtlas = this.setup.getOneWayRingAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Two edges, the ring got sectioned in the middle\", 2,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes\", 2, finalAtlas.numberOfNodes());\n        finalAtlas.edges().forEach(\n                edge -> Assert.assertFalse(\"No edge has a reverse edge\", edge.hasReverseEdge()));\n    }\n\n    @Test\n    public void testOneWaySimpleLine()\n    {\n        // Based on https://www.openstreetmap.org/way/109486264\n        final Atlas slicedRawAtlas = this.setup.getOneWaySimpleLineAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"One edge\", 1, finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes\", 2, finalAtlas.numberOfNodes());\n        finalAtlas.edges().forEach(\n                edge -> Assert.assertFalse(\"No edge has a reverse edge\", edge.hasReverseEdge()));\n    }\n\n    @Test\n    public void testPedestrianRing()\n    {\n        // Based on https://www.openstreetmap.org/way/460257372\n        final Atlas slicedRawAtlas = this.setup.getPedestrianRingAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Two edges, the ring got sectioned in the middle\", 2,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes\", 2, finalAtlas.numberOfNodes());\n        finalAtlas.edges().forEach(\n                edge -> Assert.assertFalse(\"No edge has a reverse edge\", edge.hasReverseEdge()));\n    }\n\n    @Test\n    public void testRelationMemberLocationItemInclusion()\n    {\n        // Based on https://www.openstreetmap.org/relation/578254 - the Node in the Relation gets\n        // created as both an Atlas point and node. Let's verify that the Relation consists of both.\n        final Atlas slicedRawAtlas = this.setup.getNodeAndPointAsRelationMemberAtlas();\n\n        final CountryBoundaryMap boundaryMap = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> AtlasSectionProcessorTest.class\n                        .getResourceAsStream(\"nodeAndPointRelationMemberBoundaryMap.txt\")));\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(boundaryMap)).run();\n\n        final RelationMemberList members = finalAtlas.relation(578254000000L).members();\n        Assert.assertEquals(\"Six members - 2 pairs of reverse edges, 1 node and 1 point\", 6,\n                members.size());\n        Assert.assertEquals(\"Single point\", 1, members.stream()\n                .filter(member -> member.getEntity().getType() == ItemType.POINT).count());\n        Assert.assertEquals(\"Single node\", 1, members.stream()\n                .filter(member -> member.getEntity().getType() == ItemType.NODE).count());\n    }\n\n    @Test\n    public void testReversedOneWayLine()\n    {\n        // Based on https://www.openstreetmap.org/way/333112568\n        final Atlas slicedRawAtlas = this.setup.getReversedOneWayLineAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"One edge\", 1, finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes\", 2, finalAtlas.numberOfNodes());\n        Assert.assertEquals(\"Verify that the direction of the original line was reversed\",\n                slicedRawAtlas.line(333112568000000L).asPolyLine(),\n                finalAtlas.edge(333112568000000L).asPolyLine().reversed());\n    }\n\n    @Test\n    public void testRingWithSingleIntersection()\n    {\n        // Based on https://www.openstreetmap.org/way/460419995 and\n        // https://www.openstreetmap.org/way/460419994\n        final Atlas slicedRawAtlas = this.setup.getRingWithSingleIntersectionAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\n                \"Three edges - one for the one-way ring and two for the bi-directional edge leading to it\",\n                3, finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes - start/end of the edge leading up to the ring\", 2,\n                finalAtlas.numberOfNodes());\n    }\n\n    @Test\n    public void testRoundAbout()\n    {\n        // Based on https://www.openstreetmap.org/way/426558829,\n        // https://www.openstreetmap.org/way/426558827 and\n        // https://www.openstreetmap.org/way/426558831\n        final Atlas slicedRawAtlas = this.setup.getRoundAboutAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\n                \"The roundabout gets split into two edges, each of the respective roads leading into and out of it becomes an edge and a reverse edge\",\n                6, finalAtlas.numberOfEdges());\n        Assert.assertEquals(\n                \"Four nodes - start/end of the two roads leading up to and away from the roundabout\",\n                4, finalAtlas.numberOfNodes());\n    }\n\n    @Test\n    public void testSectioningAtBarrier()\n    {\n        // Based on https://www.openstreetmap.org/way/460419996 - has two barrier, one in the middle\n        // and one at the end node\n        final Atlas slicedRawAtlas = this.setup.getLineWithBarrierAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Two edges, with reverse counterparts\", 4, finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Three nodes, one in the middle at the barrier\", 3,\n                finalAtlas.numberOfNodes());\n        Assert.assertEquals(\"Make sure that the two barriers are also represented as Points\", 2,\n                Iterables.size(finalAtlas.points(point -> BarrierTag.isBarrier(point))));\n    }\n\n    @Test\n    public void testSelfIntersectingLoop()\n    {\n        // Based on https://www.openstreetmap.org/way/373705334 and surrounding edge network\n        final Atlas slicedRawAtlas = this.setup.getSelfIntersectingLoopAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"Ten edges, each having a reverse counterpart\", 20,\n                finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Nine nodes\", 9, finalAtlas.numberOfNodes());\n    }\n\n    @Test\n    public void testSimpleBiDirectionalLine()\n    {\n        // Based on https://www.openstreetmap.org/way/460834514\n        final Atlas slicedRawAtlas = this.setup.getSimpleBiDirectionalLineAtlas();\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(COUNTRY_BOUNDARY_MAP)).run();\n\n        Assert.assertEquals(\"single edge and its counter part\", 2, finalAtlas.numberOfEdges());\n        Assert.assertEquals(\"Two nodes - one at the start and end\", 2, finalAtlas.numberOfNodes());\n    }\n\n    @Test\n    public void testWayExceedingSectioningLimit()\n    {\n        // Based on https://www.openstreetmap.org/way/608903805 and\n        // https://www.openstreetmap.org/way/608901269. These are stacked, duplicated ways that\n        // extend for a long time, causing sectioning to occur more than the allowed 999 times\n        final Atlas slicedRawAtlas = this.setup.getWayExceedingSectioningLimitAtlas();\n\n        // Create a dummy country boundary map that contains these ways and call it Afghanistan\n        final Set<String> countries = new HashSet<>();\n        final String afghanistan = \"AFG\";\n        countries.add(afghanistan);\n        final Map<String, List<org.locationtech.jts.geom.Polygon>> boundaries = new HashMap<>();\n        final Polygon fakePolygon = new Polygon(Location.forString(\"34.15102284294,66.22764518738\"),\n                Location.forString(\"34.1515910819,66.53388908386\"),\n                Location.forString(\"33.99802783162,66.53045585632\"),\n                Location.forString(\"33.99632001003,66.22558525085\"),\n                Location.forString(\"34.15102284294,66.22764518738\"));\n        final List<org.locationtech.jts.geom.Polygon> fakeBoundaries = new ArrayList<>();\n        fakeBoundaries.add(new JtsPolygonConverter().convert(fakePolygon));\n        boundaries.put(afghanistan, fakeBoundaries);\n        final CountryBoundaryMap countryBoundaryMap = CountryBoundaryMap\n                .fromBoundaryMap(boundaries);\n\n        final Atlas finalAtlas = new AtlasSectionProcessor(slicedRawAtlas,\n                AtlasLoadingOption.createOptionWithAllEnabled(countryBoundaryMap)).run();\n\n        // Verify maximum number of sections for each edge\n        Assert.assertEquals(999,\n                Iterables.size(finalAtlas.edges(edge -> edge.getOsmIdentifier() == 608901269)));\n        Assert.assertEquals(999,\n                Iterables.size(finalAtlas.edges(edge -> edge.getOsmIdentifier() == 608903805)));\n\n        // Verify tag presence\n        Assert.assertEquals(SyntheticInvalidWaySectionTag.YES.name(),\n                finalAtlas.edge(608901269000999L).tag(SyntheticInvalidWaySectionTag.KEY));\n        Assert.assertEquals(SyntheticInvalidWaySectionTag.YES.name(),\n                finalAtlas.edge(608903805000999L).tag(SyntheticInvalidWaySectionTag.KEY));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/raw/sectioning/WaySectionProcessorTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.sectioning;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * {@link AtlasSectionProcessorTest} test data.\n *\n * @author mgostintsev\n */\npublic class WaySectionProcessorTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"malformedPolyLine.atlas.txt\")\n    private Atlas malformedPolyLineAtlas;\n\n    @TestAtlas(loadFromTextResource = \"bidirectionalRing.atlas.txt\")\n    private Atlas bidirectioalRingAtlas;\n\n    @TestAtlas(loadFromTextResource = \"loopingWayWithIntersection.atlas.txt\")\n    private Atlas loopingWayWithIntersection;\n\n    @TestAtlas(loadFromTextResource = \"oneWayRing.atlas.txt\")\n    private Atlas oneWayRing;\n\n    @TestAtlas(loadFromTextResource = \"oneWaySimpleLine.atlas.txt\")\n    private Atlas oneWaySimpleLine;\n\n    @TestAtlas(loadFromTextResource = \"reversedOneWayLine.atlas.txt\")\n    private Atlas reversedOneWayLine;\n\n    @TestAtlas(loadFromTextResource = \"lineWithLoopAtEnd.atlas.txt\")\n    private Atlas lineWithLoopAtEnd;\n\n    @TestAtlas(loadFromTextResource = \"lineWithLoopInMiddle.atlas.txt\")\n    private Atlas lineWithLoopInMiddle;\n\n    @TestAtlas(loadFromTextResource = \"lineWithLoopAtStart.atlas.txt\")\n    private Atlas lineWithLoopAtStart;\n\n    @TestAtlas(loadFromTextResource = \"lineWithBarrier.atlas.txt\")\n    private Atlas lineWithBarrier;\n\n    @TestAtlas(loadFromTextResource = \"lineWithInvalidOverlappingGeometry.atlas.txt\")\n    private Atlas lineWithInvalidOverlappingGeometry;\n\n    @TestAtlas(loadFromTextResource = \"simpleBiDirectionalLine.atlas.txt\")\n    private Atlas simpleBiDirectionalLine;\n\n    @TestAtlas(loadFromTextResource = \"ringWithSingleIntersection.atlas.txt\")\n    private Atlas ringWithSingleIntersection;\n\n    @TestAtlas(loadFromTextResource = \"roundAbout.atlas.txt\")\n    private Atlas roundAbout;\n\n    @TestAtlas(loadFromTextResource = \"lineWithRepeatedLocation.atlas.txt\")\n    private Atlas lineWithRepeatedLocation;\n\n    @TestAtlas(loadFromTextResource = \"lineWithLessThanTwoNodesDueToRepeatedLocationAtEndOfLine.atlas.txt\")\n    private Atlas lineWithLessThanTwoNodesDueToRepeatedLocationAtEndOfLine;\n\n    @TestAtlas(loadFromTextResource = \"loopWithRepeatedLocation.atlas.txt\")\n    private Atlas loopWithRepeatedLocation;\n\n    @TestAtlas(loadFromTextResource = \"selfIntersectingLoop.atlas.txt\")\n    private Atlas selfIntersectingLoop;\n\n    @TestAtlas(loadFromTextResource = \"wayExceedingSectioningLimit.atlas.txt\")\n    private Atlas wayExceedingSectioningLimit;\n\n    @TestAtlas(loadFromTextResource = \"rawAtlasSpanningOutsideBoundary.atlas.txt\")\n    private Atlas rawAtlasSpanningOutsideBoundary;\n\n    @TestAtlas(loadFromTextResource = \"nestedRelationRemoval.atlas.txt\")\n    private Atlas nestedRelationRemoval;\n\n    @TestAtlas(loadFromTextResource = \"nodeAndPointAsRelationMember.atlas.txt\")\n    private Atlas nodeAndPointAsRelationMember;\n\n    @TestAtlas(loadFromTextResource = \"pedestrianRing.atlas.txt\")\n    private Atlas pedestrianRing;\n\n    public Atlas getBidirectionalRingAtlas()\n    {\n        return this.bidirectioalRingAtlas;\n    }\n\n    public Atlas getLineWithBarrierAtlas()\n    {\n        return this.lineWithBarrier;\n    }\n\n    public Atlas getLineWithInvalidOverlappingGeometry()\n    {\n        return this.lineWithInvalidOverlappingGeometry;\n    }\n\n    public Atlas getLineWithLessThanTwoNodesDueToRepeatedLocationAtEndOfLineAtlas()\n    {\n        return this.lineWithLessThanTwoNodesDueToRepeatedLocationAtEndOfLine;\n    }\n\n    public Atlas getLineWithLoopAtEndAtlas()\n    {\n        return this.lineWithLoopAtEnd;\n    }\n\n    public Atlas getLineWithLoopAtStartAtlas()\n    {\n        return this.lineWithLoopAtStart;\n    }\n\n    public Atlas getLineWithLoopInMiddleAtlas()\n    {\n        return this.lineWithLoopInMiddle;\n    }\n\n    public Atlas getLineWithRepeatedLocationAtlas()\n    {\n        return this.lineWithRepeatedLocation;\n    }\n\n    public Atlas getLoopWithRepeatedLocationAtlas()\n    {\n        return this.loopWithRepeatedLocation;\n    }\n\n    public Atlas getLoopingWayWithIntersectionAtlas()\n    {\n        return this.loopingWayWithIntersection;\n    }\n\n    public Atlas getMalformedPolyLineAtlas()\n    {\n        return this.malformedPolyLineAtlas;\n    }\n\n    public Atlas getNestedRelationRemovalAtlas()\n    {\n        return this.nestedRelationRemoval;\n    }\n\n    public Atlas getNodeAndPointAsRelationMemberAtlas()\n    {\n        return this.nodeAndPointAsRelationMember;\n    }\n\n    public Atlas getOneWayRingAtlas()\n    {\n        return this.oneWayRing;\n    }\n\n    public Atlas getOneWaySimpleLineAtlas()\n    {\n        return this.oneWaySimpleLine;\n    }\n\n    public Atlas getPedestrianRingAtlas()\n    {\n        return this.pedestrianRing;\n    }\n\n    public Atlas getRawAtlasSpanningOutsideBoundary()\n    {\n        return this.rawAtlasSpanningOutsideBoundary;\n    }\n\n    public Atlas getReversedOneWayLineAtlas()\n    {\n        return this.reversedOneWayLine;\n    }\n\n    public Atlas getRingWithSingleIntersectionAtlas()\n    {\n        return this.ringWithSingleIntersection;\n    }\n\n    public Atlas getRoundAboutAtlas()\n    {\n        return this.roundAbout;\n    }\n\n    public Atlas getSelfIntersectingLoopAtlas()\n    {\n        return this.selfIntersectingLoop;\n    }\n\n    public Atlas getSimpleBiDirectionalLineAtlas()\n    {\n        return this.simpleBiDirectionalLine;\n    }\n\n    public Atlas getWayExceedingSectioningLimitAtlas()\n    {\n        return this.wayExceedingSectioningLimit;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/raw/slicing/RawAtlasSlicerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.slicing;\n\nimport java.util.Iterator;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.Line;\nimport org.openstreetmap.atlas.geography.atlas.items.Point;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.Finder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuilding;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.buildings.ComplexBuildingFinder;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.ComplexWaterEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.complex.water.finder.ComplexWaterEntityFinder;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.pbf.slicing.identifier.CountrySlicingIdentifierFactory;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.tags.SyntheticBoundaryNodeTag;\nimport org.openstreetmap.atlas.tags.SyntheticGeometrySlicedTag;\nimport org.openstreetmap.atlas.tags.SyntheticInvalidGeometryTag;\nimport org.openstreetmap.atlas.tags.SyntheticInvalidMultiPolygonRelationMembersRemovedTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Tests covering the slicing process\n *\n * @author samg\n */\n\npublic class RawAtlasSlicerTest\n{\n    private static final CountryBoundaryMap boundary;\n    static\n    {\n        boundary = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> RawAtlasSlicerTest.class\n                        .getResourceAsStream(\"CIV_GIN_LBR_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n    }\n\n    @Rule\n    public final RawAtlasSlicerTestRule setup = new RawAtlasSlicerTestRule();\n\n    /**\n     * This test uses a simple relation that is tagged as boundary, and has only a very small\n     * percentage crossing the boundary. As a result, it meets both the tagging and percentage\n     * criteria for being consolidated, and only one slice should be generated\n     */\n    @Test\n    public void testBoundaryRelationsConsolidatedSpanningTwoCountries()\n    {\n        final Atlas rawAtlas = this.setup.getSimpleBoundaryRelationConsolidateAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(2, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        Assert.assertTrue(Iterables.stream(civSlicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"CIV\")));\n\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(3, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfRelations());\n        Assert.assertTrue(Iterables.stream(lbrSlicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")));\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertNotNull(civSlicedAtlas.area(line.getIdentifier()));\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(rawArea.getOsmTags(),\n                        civSlicedAtlas.area(line.getIdentifier()).getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n                Assert.assertEquals(0,\n                        civSlicedAtlas.area(line.getIdentifier()).relations().size());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertNotNull(lbrSlicedAtlas.area(line.getIdentifier()));\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(rawArea.getOsmTags(),\n                        lbrSlicedAtlas.area(line.getIdentifier()).getOsmTags());\n                Assert.assertEquals(0, line.relations().size());\n                Assert.assertEquals(0,\n                        lbrSlicedAtlas.area(line.getIdentifier()).relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                1);\n\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(civRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n        Assert.assertTrue(civRelation.asMultiPolygon().get().isValid());\n    }\n\n    /**\n     * This test uses the same simple relation for the multipolygon tests, but is tagged as\n     * boundary. While it meets meets the tagging criteria, it significant pieces on both sides and\n     * thus does not meet the percentage cutoff and should not be consolidated\n     */\n    @Test\n    public void testBoundaryRelationsSpanningTwoCountries()\n    {\n        final Atlas rawAtlas = this.setup.getSimpleBoundaryRelationAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(2, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        Assert.assertTrue(Iterables.stream(civSlicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"CIV\")));\n\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(3, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        Assert.assertTrue(Iterables.stream(lbrSlicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")));\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertNotNull(civSlicedAtlas.area(line.getIdentifier()));\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(rawArea.getOsmTags(),\n                        civSlicedAtlas.area(line.getIdentifier()).getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n                Assert.assertEquals(0,\n                        civSlicedAtlas.area(line.getIdentifier()).relations().size());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertNotNull(lbrSlicedAtlas.area(line.getIdentifier()));\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(rawArea.getOsmTags(),\n                        lbrSlicedAtlas.area(line.getIdentifier()).getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n                Assert.assertEquals(0,\n                        lbrSlicedAtlas.area(line.getIdentifier()).relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                1);\n\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        final Relation lbrRelation = lbrSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(civRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(lbrRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        Assert.assertTrue(civRelation.asMultiPolygon().get().isValid());\n        Assert.assertTrue(lbrRelation.asMultiPolygon().get().isValid());\n    }\n\n    /**\n     * This test examines a number of important behaviors. For a closed edge, it's important that\n     * slicing logic recognizes that it should be sliced linearly, as slicing polygonally would\n     * destroy its ability to be converted to an Edge. Additionally, this test checks that the\n     * line's points are preserved despite no specific tagging, and that synthetic boundary nodes\n     * are present and match the slice location for both sliced lines.\n     */\n    @Test\n    public void testClosedEdgeSpanningTwoCountries()\n    {\n        final Atlas rawAtlas = this.setup.getClosedEdgeSpanningTwoCountriesAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        final Line rawLine = rawAtlas.line(1);\n\n        Assert.assertEquals(1, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfLines());\n\n        final CountrySlicingIdentifierFactory lineIdentifierFactory = new CountrySlicingIdentifierFactory(\n                rawLine.getIdentifier());\n\n        final Line civLine = civSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertFalse(civLine.isClosed());\n\n        final Optional<String> civLineTag = civLine.getTag(ISOCountryTag.KEY);\n        Assert.assertTrue(civLineTag.isPresent());\n        Assert.assertEquals(\"CIV\", civLineTag.get());\n        Assert.assertEquals(rawLine.getOsmTags(), civLine.getOsmTags());\n\n        final Optional<String> civLineSlicedGeometryTag = civLine\n                .getTag(SyntheticGeometrySlicedTag.KEY);\n        Assert.assertTrue(civLineSlicedGeometryTag.isPresent());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civLineSlicedGeometryTag.get());\n\n        final Line lbrLine = lbrSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertFalse(lbrLine.isClosed());\n\n        final Optional<String> lbrLineTag = lbrLine.getTag(ISOCountryTag.KEY);\n        Assert.assertTrue(lbrLineTag.isPresent());\n        Assert.assertEquals(\"LBR\", lbrLineTag.get());\n        Assert.assertEquals(rawLine.getOsmTags(), lbrLine.getOsmTags());\n\n        final Optional<String> lbrLineSlicedGeometryTag = lbrLine\n                .getTag(SyntheticGeometrySlicedTag.KEY);\n        Assert.assertTrue(lbrLineSlicedGeometryTag.isPresent());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrLineSlicedGeometryTag.get());\n\n        // Check Point correctness\n        Assert.assertEquals(4, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(4, lbrSlicedAtlas.numberOfPoints());\n\n        for (final Point point : civSlicedAtlas.points())\n        {\n            Assert.assertTrue(point.getTag(ISOCountryTag.KEY).isPresent());\n            if (point.getIdentifier() == 1 || point.getIdentifier() == 2)\n            {\n                Assert.assertEquals(rawAtlas.point(point.getIdentifier()).getOsmTags(),\n                        point.getOsmTags());\n                Assert.assertEquals(\"CIV\", point.getTag(ISOCountryTag.KEY).get());\n                Assert.assertFalse(point.getTag(SyntheticBoundaryNodeTag.KEY).isPresent());\n            }\n            else\n            {\n                Assert.assertEquals(\"CIV,LBR\", point.getTag(ISOCountryTag.KEY).get());\n                Assert.assertTrue(point.getOsmTags().isEmpty());\n                Assert.assertTrue(point.getTag(SyntheticBoundaryNodeTag.KEY).isPresent());\n                Assert.assertEquals(SyntheticBoundaryNodeTag.YES.toString(),\n                        point.getTag(SyntheticBoundaryNodeTag.KEY).get());\n                civSlicedAtlas.linesContaining(point.getLocation()).forEach(lineContaining ->\n                {\n                    Assert.assertTrue(lineContaining.asPolyLine().last().equals(point.getLocation())\n                            || lineContaining.asPolyLine().first().equals(point.getLocation()));\n                });\n\n                // boundary nodes should be in both Atlases!\n                final Point lbrBoundaryNode = lbrSlicedAtlas.point(point.getIdentifier());\n                Assert.assertEquals(point.getLocation(), lbrBoundaryNode.getLocation());\n                Assert.assertEquals(point.getTags(), lbrBoundaryNode.getTags());\n            }\n        }\n\n        for (final Point point : lbrSlicedAtlas.points())\n        {\n            Assert.assertTrue(point.getTag(ISOCountryTag.KEY).isPresent());\n            if (point.getIdentifier() == 3 || point.getIdentifier() == 4)\n            {\n                Assert.assertEquals(rawAtlas.point(point.getIdentifier()).getOsmTags(),\n                        point.getOsmTags());\n                Assert.assertEquals(\"LBR\", point.getTag(ISOCountryTag.KEY).get());\n                Assert.assertFalse(point.getTag(SyntheticBoundaryNodeTag.KEY).isPresent());\n            }\n            else\n            {\n                Assert.assertEquals(\"CIV,LBR\", point.getTag(ISOCountryTag.KEY).get());\n                Assert.assertTrue(point.getOsmTags().isEmpty());\n                Assert.assertTrue(point.getTag(SyntheticBoundaryNodeTag.KEY).isPresent());\n                Assert.assertEquals(SyntheticBoundaryNodeTag.YES.toString(),\n                        point.getTag(SyntheticBoundaryNodeTag.KEY).get());\n                civSlicedAtlas.linesContaining(point.getLocation()).forEach(lineContaining ->\n                {\n                    Assert.assertTrue(lineContaining.asPolyLine().last().equals(point.getLocation())\n                            || lineContaining.asPolyLine().first().equals(point.getLocation()));\n                });\n\n                // boundary nodes should be in both Atlases!\n                final Point civBoundaryNode = civSlicedAtlas.point(point.getIdentifier());\n                Assert.assertEquals(point.getLocation(), civBoundaryNode.getLocation());\n                Assert.assertEquals(point.getTags(), civBoundaryNode.getTags());\n            }\n        }\n    }\n\n    /**\n     * This is a pretty straightforward case-- just looking to confirm the geometry isn't altered,\n     * the country tag is updated, and the tagless points are removed\n     */\n    @Test\n    public void testClosedLineInsideSingleCountry()\n    {\n        final Atlas rawAtlas = this.setup.getClosedLineFullyInOneCountryAtlas();\n        final Atlas slicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"GIN\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(1, slicedAtlas.numberOfLines());\n        Assert.assertTrue(slicedAtlas.line(1).isClosed());\n        Assert.assertEquals(rawAtlas.line(1).asPolyLine(), slicedAtlas.line(1).asPolyLine());\n        Assert.assertEquals(\"GIN\", slicedAtlas.line(1).getTag(ISOCountryTag.KEY).get());\n        Assert.assertTrue(slicedAtlas.line(1).getTag(SyntheticGeometrySlicedTag.KEY).isEmpty());\n        Assert.assertEquals(0, slicedAtlas.numberOfPoints());\n    }\n\n    /**\n     * This is a pretty straightforward case-- just looking to confirm the geometry isn't altered,\n     * the country tag is updated, and the tagless points are <i>not</i> removed (\"keepAll\" option)\n     */\n    @Test\n    public void testClosedLineInsideSingleCountryKeepAll()\n    {\n        final Atlas rawAtlas = this.setup.getClosedLineFullyInOneCountryAtlas();\n        final Atlas slicedAtlas = new RawAtlasSlicer(AtlasLoadingOption\n                .createOptionWithAllEnabled(boundary).setCountryCode(\"GIN\").setKeepAll(true),\n                rawAtlas).slice();\n\n        Assert.assertEquals(1, slicedAtlas.numberOfLines());\n        Assert.assertTrue(slicedAtlas.line(1).isClosed());\n        Assert.assertEquals(rawAtlas.line(1).asPolyLine(), slicedAtlas.line(1).asPolyLine());\n        Assert.assertEquals(\"GIN\", slicedAtlas.line(1).getTag(ISOCountryTag.KEY).get());\n        Assert.assertTrue(slicedAtlas.line(1).getTag(SyntheticGeometrySlicedTag.KEY).isEmpty());\n        Assert.assertEquals(4, slicedAtlas.numberOfPoints());\n    }\n\n    /**\n     * This is the same geometry as the line in testClosedEdgeSpanningTwoCountries(), but the\n     * tagging here no longer qualifies it as an Edge. The result should be different, accordingly,\n     * as the closed line will now be sliced as a polygon and tagless points will be removed.\n     */\n    @Test\n    public void testClosedLineSpanningTwoCountries()\n    {\n        final Atlas rawAtlas = this.setup.getClosedLineSpanningTwoCountriesAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(1, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfAreas());\n\n        final Area rawArea = rawAtlas.area(1);\n        final CountrySlicingIdentifierFactory lineIdentifierFactory = new CountrySlicingIdentifierFactory(\n                rawArea.getIdentifier());\n\n        final Area civSlicedArea = civSlicedAtlas.area(lineIdentifierFactory.nextIdentifier());\n        Assert.assertEquals(\"CIV\", civSlicedArea.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civSlicedArea.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertEquals(rawArea.getOsmTags(), civSlicedArea.getOsmTags());\n        Assert.assertFalse(civSlicedArea.asPolygon().isClockwise());\n\n        final Area lbrSlicedArea = lbrSlicedAtlas.area(lineIdentifierFactory.nextIdentifier());\n        Assert.assertEquals(\"LBR\", lbrSlicedArea.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrSlicedArea.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertEquals(rawArea.getOsmTags(), lbrSlicedArea.getOsmTags());\n        Assert.assertFalse(lbrSlicedArea.asPolygon().isClockwise());\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n    }\n\n    /**\n     * Test examining the creation of synthetic boundary nodes for existing points. Should slice\n     * line into two pieces, one for CIV and one for LBR. All points should be preserved, and point\n     * 2 should be tagged as a SyntheticBoundaryNode.EXISTING as well as be the first location of\n     * the CIV sliced line and the last location of the LBR sliced line.\n     */\n    @Test\n    public void testCreatingExistingSyntheticBoundaryNode()\n    {\n        final Atlas rawAtlas = this.setup.getRoadAcrossTwoCountriesWithPointOnBorderAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(1, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfLines());\n\n        final Line rawLine = rawAtlas.line(1);\n        final CountrySlicingIdentifierFactory lineIdentifierFactory = new CountrySlicingIdentifierFactory(\n                rawLine.getIdentifier());\n\n        final Line civLine = civSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertEquals(\"CIV\", civLine.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civLine.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertEquals(rawLine.getOsmTags(), civLine.getOsmTags());\n\n        final Line lbrLine = lbrSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertEquals(\"LBR\", lbrLine.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrLine.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertEquals(rawLine.getOsmTags(), lbrLine.getOsmTags());\n\n        Assert.assertEquals(2, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfPoints());\n\n        Assert.assertEquals(\"CIV\", civSlicedAtlas.point(3).getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(\"LBR\", lbrSlicedAtlas.point(1).getTag(ISOCountryTag.KEY).get());\n\n        Assert.assertEquals(SyntheticBoundaryNodeTag.EXISTING.toString(),\n                civSlicedAtlas.point(2).getTag(SyntheticBoundaryNodeTag.KEY).get());\n        Assert.assertEquals(SyntheticBoundaryNodeTag.EXISTING.toString(),\n                lbrSlicedAtlas.point(2).getTag(SyntheticBoundaryNodeTag.KEY).get());\n\n        Assert.assertEquals(\"CIV,LBR\", civSlicedAtlas.point(2).getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(\"CIV,LBR\", lbrSlicedAtlas.point(2).getTag(ISOCountryTag.KEY).get());\n\n        Assert.assertEquals(civSlicedAtlas.point(2).getLocation(), civLine.asPolyLine().first());\n        Assert.assertEquals(lbrSlicedAtlas.point(2).getLocation(), lbrLine.asPolyLine().last());\n    }\n\n    /**\n     * This line is a highway entirely inside a country and should only be updated with an\n     * ISOCountryTag. Points should be preserved since it will be an Edge\n     */\n    @Test\n    public void testEdgeFullyInsideOneCountry()\n    {\n        final Atlas rawAtlas = this.setup.getRoadFullyInOneCountryAtlas();\n        final Atlas slicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(rawAtlas.numberOfLines(), slicedAtlas.numberOfLines());\n        Assert.assertEquals(\"CIV\", slicedAtlas.line(1).getTag(ISOCountryTag.KEY).get());\n        Assert.assertTrue(slicedAtlas.line(1).getTag(SyntheticGeometrySlicedTag.KEY).isEmpty());\n        Assert.assertEquals(rawAtlas.line(1).getOsmTags(), slicedAtlas.line(1).getOsmTags());\n\n        // Check Point correctness\n        Assert.assertEquals(rawAtlas.numberOfPoints(), slicedAtlas.numberOfPoints());\n\n        slicedAtlas.points().forEach(point ->\n        {\n            Assert.assertEquals(\"CIV\", point.getTag(ISOCountryTag.KEY).get());\n            Assert.assertFalse(point.getTag(SyntheticBoundaryNodeTag.KEY).isPresent());\n            Assert.assertEquals(rawAtlas.point(point.getIdentifier()).getOsmTags(),\n                    point.getOsmTags());\n        });\n    }\n\n    /**\n     * This line is an Edge candidate that goes across the boundary multiple times. Expect geometry\n     * to be sliced and new synthetic nodes to be made.\n     */\n    @Test\n    public void testEdgeWeavingAcrossBoundary()\n    {\n        final Atlas rawAtlas = this.setup.getRoadWeavingAlongBoundaryAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(2, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfLines());\n\n        final CountrySlicingIdentifierFactory lineIdentifierFactory = new CountrySlicingIdentifierFactory(\n                1);\n\n        final Line firstCreatedLine = civSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertNotNull(\"Check new way addition\", firstCreatedLine);\n        Assert.assertEquals(\"Expect the first segment to be on the Ivory Coast side\", \"CIV\",\n                firstCreatedLine.getTag(ISOCountryTag.KEY).get());\n\n        final Line secondCreatedLine = civSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertNotNull(\"Check new way addition\", secondCreatedLine);\n        Assert.assertEquals(\"Expect the second segment to be on the Ivory Coast side\", \"CIV\",\n                secondCreatedLine.getTag(ISOCountryTag.KEY).get());\n\n        final Line thirdCreatedLine = lbrSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertNotNull(\"Check new way addition\", thirdCreatedLine);\n        Assert.assertEquals(\"Expect the third segment to be on the Liberia side\", \"LBR\",\n                thirdCreatedLine.getTag(ISOCountryTag.KEY).get());\n\n        final Line fourthCreatedLine = lbrSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertNotNull(\"Check new way addition\", fourthCreatedLine);\n        Assert.assertEquals(\"Expect the fourth segment to be on the Liberia side\", \"LBR\",\n                fourthCreatedLine.getTag(ISOCountryTag.KEY).get());\n\n        Assert.assertEquals(6, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(5, lbrSlicedAtlas.numberOfPoints());\n        for (final Point point : civSlicedAtlas.points())\n        {\n            if (rawAtlas.point(point.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.point(point.getIdentifier()).getOsmTags(),\n                        point.getOsmTags());\n                Assert.assertEquals(\"CIV\", point.getTag(ISOCountryTag.KEY).get());\n            }\n            else\n            {\n                Assert.assertEquals(\"CIV,LBR\", point.getTag(ISOCountryTag.KEY).get());\n                Assert.assertEquals(SyntheticBoundaryNodeTag.YES.toString(),\n                        point.getTag(SyntheticBoundaryNodeTag.KEY).get());\n                Assert.assertNotNull(lbrSlicedAtlas.point(point.getIdentifier()));\n                civSlicedAtlas.linesContaining(point.getLocation()).forEach(lineContaining ->\n                {\n                    Assert.assertTrue(lineContaining.asPolyLine().first()\n                            .equals(point.getLocation())\n                            || lineContaining.asPolyLine().last().equals(point.getLocation()));\n                });\n            }\n        }\n\n        for (final Point point : lbrSlicedAtlas.points())\n        {\n            if (rawAtlas.point(point.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.point(point.getIdentifier()).getOsmTags(),\n                        point.getOsmTags());\n                Assert.assertEquals(\"LBR\", point.getTag(ISOCountryTag.KEY).get());\n            }\n            else\n            {\n                Assert.assertEquals(\"CIV,LBR\", point.getTag(ISOCountryTag.KEY).get());\n                Assert.assertEquals(SyntheticBoundaryNodeTag.YES.toString(),\n                        point.getTag(SyntheticBoundaryNodeTag.KEY).get());\n                Assert.assertNotNull(civSlicedAtlas.point(point.getIdentifier()));\n                civSlicedAtlas.linesContaining(point.getLocation()).forEach(lineContaining ->\n                {\n                    Assert.assertTrue(lineContaining.asPolyLine().first()\n                            .equals(point.getLocation())\n                            || lineContaining.asPolyLine().last().equals(point.getLocation()));\n                });\n            }\n        }\n    }\n\n    /**\n     * This line is an Edge candidate that goes across the boundary multiple times. Expect geometry\n     * to be sliced and new synthetic nodes to be made. The nodes/points should all still be there.\n     */\n    @Test\n    public void testEdgeWeavingAcrossBoundaryKeepAll()\n    {\n        final Atlas rawAtlas = this.setup.getRoadWeavingAlongBoundaryAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(AtlasLoadingOption\n                .createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\").setKeepAll(true),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(AtlasLoadingOption\n                .createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\").setKeepAll(true),\n                rawAtlas).slice();\n\n        final CountrySlicingIdentifierFactory lineIdentifierFactory = new CountrySlicingIdentifierFactory(\n                1);\n\n        final Line firstCreatedLine = civSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertNotNull(\"Check new way addition\", firstCreatedLine);\n        Assert.assertEquals(\"Expect the first segment to be on the Ivory Coast side\", \"CIV\",\n                firstCreatedLine.getTag(ISOCountryTag.KEY).get());\n\n        final Line secondCreatedLine = civSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertNotNull(\"Check new way addition\", secondCreatedLine);\n        Assert.assertEquals(\"Expect the second segment to be on the Ivory Coast side\", \"CIV\",\n                secondCreatedLine.getTag(ISOCountryTag.KEY).get());\n\n        final Line thirdCreatedLine = lbrSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertNotNull(\"Check new way addition\", thirdCreatedLine);\n        Assert.assertEquals(\"Expect the third segment to be on the Liberia side\", \"LBR\",\n                thirdCreatedLine.getTag(ISOCountryTag.KEY).get());\n\n        final Line fourthCreatedLine = lbrSlicedAtlas.line(lineIdentifierFactory.nextIdentifier());\n        Assert.assertNotNull(\"Check new way addition\", fourthCreatedLine);\n        Assert.assertEquals(\"Expect the fourth segment to be on the Liberia side\", \"LBR\",\n                fourthCreatedLine.getTag(ISOCountryTag.KEY).get());\n\n        // We keep all the points\n        Assert.assertEquals(8, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(5, Iterables.stream(civSlicedAtlas.points())\n                .filter(point -> point.getIdentifier() > 0).collectToList().size());\n        Assert.assertEquals(8, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(5, Iterables.stream(lbrSlicedAtlas.points())\n                .filter(point -> point.getIdentifier() > 0).collectToList().size());\n        for (final Point point : civSlicedAtlas.points())\n        {\n            if (rawAtlas.point(point.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.point(point.getIdentifier()).getOsmTags(),\n                        point.getOsmTags());\n                if (\"CIV\".equals(\n                        boundary.getCountryCodeISO3(point.getLocation()).getIso3CountryCode()))\n                {\n                    Assert.assertEquals(\"CIV\", point.getTag(ISOCountryTag.KEY).get());\n                }\n            }\n            else\n            {\n                Assert.assertEquals(\"CIV,LBR\", point.getTag(ISOCountryTag.KEY).get());\n                Assert.assertEquals(SyntheticBoundaryNodeTag.YES.toString(),\n                        point.getTag(SyntheticBoundaryNodeTag.KEY).get());\n                Assert.assertNotNull(lbrSlicedAtlas.point(point.getIdentifier()));\n                civSlicedAtlas.linesContaining(point.getLocation()).forEach(lineContaining ->\n                {\n                    Assert.assertTrue(lineContaining.asPolyLine().first()\n                            .equals(point.getLocation())\n                            || lineContaining.asPolyLine().last().equals(point.getLocation()));\n                });\n            }\n        }\n\n        for (final Point point : lbrSlicedAtlas.points())\n        {\n            if (rawAtlas.point(point.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.point(point.getIdentifier()).getOsmTags(),\n                        point.getOsmTags());\n                if (\"LBR\".equals(\n                        boundary.getCountryCodeISO3(point.getLocation()).getIso3CountryCode()))\n                {\n                    Assert.assertEquals(\"LBR\", point.getTag(ISOCountryTag.KEY).get());\n                }\n                else if (\"CIV\".equals(\n                        boundary.getCountryCodeISO3(point.getLocation()).getIso3CountryCode()))\n                {\n                    Assert.assertEquals(\"CIV\", point.getTag(ISOCountryTag.KEY).get());\n                }\n            }\n            else\n            {\n                Assert.assertEquals(\"CIV,LBR\", point.getTag(ISOCountryTag.KEY).get());\n                Assert.assertEquals(SyntheticBoundaryNodeTag.YES.toString(),\n                        point.getTag(SyntheticBoundaryNodeTag.KEY).get());\n                Assert.assertNotNull(civSlicedAtlas.point(point.getIdentifier()));\n                civSlicedAtlas.linesContaining(point.getLocation()).forEach(lineContaining ->\n                {\n                    Assert.assertTrue(lineContaining.asPolyLine().first()\n                            .equals(point.getLocation())\n                            || lineContaining.asPolyLine().last().equals(point.getLocation()));\n                });\n            }\n        }\n    }\n\n    /**\n     * This tests a relation with two closed lines in LBR that overlap, forming an invalid\n     * multipolygon. Expect only country code assignment, all points removed (since they have no\n     * tags), no geometry slicing, and no relation slicing\n     */\n    @Test\n    public void testInnerIntersectingOuterRelation()\n    {\n        final Atlas rawAtlas = this.setup.getIntersectingInnerAndOuterMembersAtlas();\n        final Atlas slicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n        Assert.assertEquals(0, slicedAtlas.numberOfPoints());\n        Assert.assertEquals(rawAtlas.numberOfLines(), slicedAtlas.numberOfLines());\n        Assert.assertEquals(rawAtlas.numberOfRelations(), slicedAtlas.numberOfRelations());\n\n        Assert.assertTrue(Iterables.stream(slicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")\n                        && entity.getTag(SyntheticGeometrySlicedTag.KEY).isEmpty()));\n    }\n\n    /**\n     * This relation is made up two closed lines, both on the Liberia side. However, the inner and\n     * outer roles are reversed, causing an invalid multipolygon. Expect only country code\n     * assignment, all points removed (since they have no tags), no geometry slicing, and no\n     * relation slicing\n     */\n    @Test\n    public void testInnerOutsideOuterRelation()\n    {\n        final Atlas rawAtlas = this.setup.getInnerOutsideOuterRelationAtlas();\n        final Atlas slicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        // Assert that we cannot build a valid building with this relation\n        new ComplexBuildingFinder().find(slicedAtlas)\n                .forEach(building -> Assert.assertTrue(building.getError().isPresent()));\n\n        // Nothing should have been sliced, verify identical counts\n        Assert.assertEquals(0, slicedAtlas.numberOfPoints());\n        Assert.assertEquals(rawAtlas.numberOfLines(), slicedAtlas.numberOfLines());\n        Assert.assertEquals(rawAtlas.numberOfRelations(), slicedAtlas.numberOfRelations());\n\n        Assert.assertTrue(Iterables.stream(slicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")\n                        && entity.getTag(SyntheticGeometrySlicedTag.KEY).isEmpty()));\n    }\n\n    /**\n     * This relation is made up of a single closed line straddling the CIV/LBR boundary, which is\n     * the sole member in the relation with inner role. This is an invalid multipolygon, since it\n     * doesn't have an outer. Expect only country code assignment, all points removed (since they\n     * have no tags), no geometry slicing, and no relation slicing. Additionally, because\n     * multipolygon relation slicing requires lines sliced linearly, but the line is closed, expect\n     * to see both the linearly sliced line in the atlas Lines, and the polygonally sliced line in\n     * the atlas Areas. The areas should *not* be part of the relation.\n     */\n    @Test\n    public void testInnerWithoutOuterAcrossBoundary()\n    {\n        final Atlas rawAtlas = this.setup.getInnerWithoutOuterAcrossBoundaryAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n\n        // Line was cut into two pieces, and each relation contains the piece as an inner\n        Assert.assertEquals(1, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n\n        Assert.assertEquals(\"CIV\", civSlicedAtlas.relation(1).getTag(ISOCountryTag.KEY).get());\n        Assert.assertTrue(\n                civSlicedAtlas.relation(1).getTag(SyntheticGeometrySlicedTag.KEY).isEmpty());\n        Assert.assertEquals(1, civSlicedAtlas.relation(1).members().size());\n    }\n\n    /**\n     * This relation is made up of a single closed line inside LBR, which is the sole member in the\n     * relation with inner role. This is an invalid multipolygon, since it doesn't have an outer.\n     * Expect only country code assignment, all points removed (since they have no tags), no\n     * geometry slicing, and no relation slicing\n     */\n    @Test\n    public void testInnerWithoutOuterRelationInOneCountry()\n    {\n        final Atlas rawAtlas = this.setup.getInnerWithoutOuterInOneCountryAtlas();\n        final Atlas slicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        // Assert that we cannot build a valid building with this relation\n        new ComplexBuildingFinder().find(slicedAtlas)\n                .forEach(building -> Assert.assertTrue(building.getError().isPresent()));\n\n        // Nothing should have been sliced, verify identical counts\n        Assert.assertEquals(0, slicedAtlas.numberOfPoints());\n        Assert.assertEquals(rawAtlas.numberOfLines(), slicedAtlas.numberOfLines());\n        Assert.assertEquals(rawAtlas.numberOfRelations(), slicedAtlas.numberOfRelations());\n\n        Assert.assertTrue(Iterables.stream(slicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")\n                        && entity.getTag(SyntheticGeometrySlicedTag.KEY).isEmpty()));\n    }\n\n    /**\n     * This relation is made up of three closed lines, each serving as an outer to a multipolygon\n     * relation. Two of the outers span the border of two countries, while one is entirely within a\n     * country.\n     */\n    @Test\n    public void testMultiPolygonRelationSpanningTwoCountries()\n    {\n        final Atlas rawAtlas = this.setup.getSimpleMultiPolygonAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        Assert.assertTrue(Iterables.stream(civSlicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"CIV\")));\n\n        Assert.assertEquals(3, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        Assert.assertTrue(Iterables.stream(lbrSlicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")));\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertNotNull(civSlicedAtlas.area(line.getIdentifier()));\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(rawArea.getOsmTags(),\n                        civSlicedAtlas.area(line.getIdentifier()).getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n                Assert.assertEquals(0,\n                        civSlicedAtlas.area(line.getIdentifier()).relations().size());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertNotNull(lbrSlicedAtlas.area(line.getIdentifier()));\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(rawArea.getOsmTags(),\n                        lbrSlicedAtlas.area(line.getIdentifier()).getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n                Assert.assertEquals(0,\n                        lbrSlicedAtlas.area(line.getIdentifier()).relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                1);\n\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        final Relation lbrRelation = lbrSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(civRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(lbrRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        Assert.assertTrue(civRelation.asMultiPolygon().get().isValid());\n        Assert.assertTrue(lbrRelation.asMultiPolygon().get().isValid());\n    }\n\n    /**\n     * This relation is made up of closed lines, tied together by a relation, to create a\n     * MultiPolygon with the outer spanning two countries and the inner fully inside one country.\n     */\n    @Test\n    public void testMultiPolygonWithClosedLinesSpanningTwoCountries()\n    {\n        final Atlas rawAtlas = this.setup.getComplexMultiPolygonWithHoleUsingClosedLinesAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(1, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        final Iterable<ComplexWaterEntity> civWaterEntities = new ComplexWaterEntityFinder()\n                .find(civSlicedAtlas, Finder::ignore);\n        Assert.assertEquals(1, Iterables.size(civWaterEntities));\n\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        final Iterable<ComplexWaterEntity> lbrWaterEntities = new ComplexWaterEntityFinder()\n                .find(lbrSlicedAtlas, Finder::ignore);\n        Assert.assertEquals(1, Iterables.size(lbrWaterEntities));\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertNotNull(civSlicedAtlas.area(line.getIdentifier()));\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(rawArea.getOsmTags(),\n                        civSlicedAtlas.area(line.getIdentifier()).getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n                Assert.assertEquals(0,\n                        civSlicedAtlas.area(line.getIdentifier()).relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertNotNull(lbrSlicedAtlas.area(line.getIdentifier()));\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(rawArea.getOsmTags(),\n                        lbrSlicedAtlas.area(line.getIdentifier()).getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n                Assert.assertEquals(0,\n                        lbrSlicedAtlas.area(line.getIdentifier()).relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                214805000000L);\n\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        final Relation lbrRelation = lbrSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(civRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(lbrRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n    }\n\n    /**\n     * This test is the same as testSimpleMultiPolygonWithHoleSpanningTwoCountries(), but this\n     * version contains invalid members. Check to see multipolygon relation is still properly sliced\n     * but that invalid members have been filtered out and tag has been updated correctly\n     */\n\n    @Test\n    public void testMultiPolygonWithInvalidMembers()\n    {\n        final Atlas rawAtlas = this.setup.getRelationWithInvalidMultiPolygonMembers();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(1, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(3, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(2, civSlicedAtlas.numberOfRelations());\n        final Iterable<ComplexBuilding> civBuildings = new ComplexBuildingFinder()\n                .find(civSlicedAtlas, Finder::ignore);\n        Assert.assertEquals(1, Iterables.size(civBuildings));\n\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(3, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        final Iterable<ComplexBuilding> lbrBuildings = new ComplexBuildingFinder()\n                .find(lbrSlicedAtlas, Finder::ignore);\n        Assert.assertEquals(1, Iterables.size(lbrBuildings));\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                0L);\n\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        final Relation lbrRelation = lbrSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n    }\n\n    /**\n     * This relation is made up of open lines, tied together by a relation to create a MultiPolygon\n     * with the outer spanning two countries and the inner fully inside one country.\n     */\n    @Test\n    public void testMultiPolygonWithOpenLinesSpanningTwoCountries()\n    {\n\n        final Atlas rawAtlas = this.setup.getComplexMultiPolygonWithHoleUsingOpenLinesAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(2, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        final Iterable<ComplexWaterEntity> civWaterEntities = new ComplexWaterEntityFinder()\n                .find(civSlicedAtlas, Finder::ignore);\n        Assert.assertEquals(1, Iterables.size(civWaterEntities));\n\n        Assert.assertEquals(4, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        final Iterable<ComplexWaterEntity> lbrWaterEntities = new ComplexWaterEntityFinder()\n                .find(lbrSlicedAtlas, Finder::ignore);\n        Assert.assertEquals(1, Iterables.size(lbrWaterEntities));\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Line rawLine = rawAtlas.lines(rawLineCandidate -> rawLineCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawLine.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Line rawLine = rawAtlas.lines(rawLineCandidate -> rawLineCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawLine.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                214805000000L);\n\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        final Relation lbrRelation = lbrSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(civRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(lbrRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n    }\n\n    @Test\n    public void testMultiPolygonWithOverlappingSlicedInners()\n    {\n        final Atlas rawAtlas = this.setup.getMultiPolygonWithOverlappingSlicedInners();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n        lbrSlicedAtlas.relations().forEach(relation ->\n        {\n            Assert.assertTrue(relation.getTag(SyntheticInvalidGeometryTag.KEY).isPresent());\n            Assert.assertFalse(relation.getTag(SyntheticGeometrySlicedTag.KEY).isPresent());\n        });\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        civSlicedAtlas.relations().forEach(relation ->\n        {\n            Assert.assertTrue(relation.getTag(SyntheticInvalidGeometryTag.KEY).isPresent());\n            Assert.assertFalse(relation.getTag(SyntheticGeometrySlicedTag.KEY).isPresent());\n        });\n    }\n\n    @Test\n    public void testMultiPolygonWithOverlappingUnslicedInners()\n    {\n        final Atlas rawAtlas = this.setup.getMultiPolygonWithOverlappingUnslicedInners();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n        lbrSlicedAtlas.relations().forEach(relation ->\n        {\n            Assert.assertFalse(relation.getTag(SyntheticInvalidGeometryTag.KEY).isPresent());\n            Assert.assertTrue(relation.getTag(SyntheticGeometrySlicedTag.KEY).isPresent());\n        });\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        civSlicedAtlas.relations().forEach(relation ->\n        {\n            Assert.assertFalse(relation.getTag(SyntheticInvalidGeometryTag.KEY).isPresent());\n            Assert.assertTrue(relation.getTag(SyntheticGeometrySlicedTag.KEY).isPresent());\n        });\n    }\n\n    /**\n     * This relation is made up of a single open line straddling the country boundary, which is the\n     * sole member in the relation with outer role. This is an invalid multipolygon, since it isn't\n     * closed. We expect slicing to add a new point, on the boundary and create a relation for each\n     * country, holding a piece of the sliced line in each one.\n     */\n    @Test\n    public void testOpenMultiPolygonRelationAcrossBoundary()\n    {\n        final Atlas rawAtlas = this.setup.getOpenMultiPolygonAcrossBoundaryAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        new ComplexBuildingFinder().find(civSlicedAtlas)\n                .forEach(building -> Assert.assertTrue(building.getError().isPresent()));\n\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        new ComplexBuildingFinder().find(lbrSlicedAtlas)\n                .forEach(building -> Assert.assertTrue(building.getError().isPresent()));\n\n        final Relation civRelation = civSlicedAtlas.relation(1);\n        Assert.assertEquals(1, civRelation.members().size());\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n\n        final Relation lbrRelation = lbrSlicedAtlas.relation(1);\n        Assert.assertEquals(1, lbrRelation.members().size());\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n    }\n\n    /**\n     * This relation is made up of a single open line inside LBR, which is the sole member in the\n     * relation with outer role. This is an invalid multipolygon, since it isn't closed. We expect\n     * slicing to add country codes to all points/lines/relations, but leave the geometry and roles\n     * unchanged.\n     */\n    @Test\n    public void testOpenMultiPolygonRelationInOneCountry()\n    {\n\n        final Atlas rawAtlas = this.setup.getOpenMultiPolygonInOneCountryAtlas();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        new ComplexBuildingFinder().find(lbrSlicedAtlas)\n                .forEach(building -> Assert.assertTrue(building.getError().isPresent()));\n\n        Assert.assertTrue(Iterables.stream(lbrSlicedAtlas.entities())\n                .allMatch(entity -> entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")\n                        && entity.getTag(SyntheticGeometrySlicedTag.KEY).isEmpty()));\n    }\n\n    /**\n     * This relation is made up of two lines. The first one is a closed line on the LBR side. The\n     * second is an open line spanning the boundary of LBR and CIV. Both lines are outer members in\n     * a relation. We expect slicing to leave the closed line and to cut the open line as well as\n     * create a new relation on the CIV side with a piece of the outer open line as a single member.\n     */\n    @Test\n    public void testRelationWithOneClosedAndOpenMember()\n    {\n        final Atlas rawAtlas = this.setup.getRelationWithOneClosedAndOneOpenMemberAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        new ComplexBuildingFinder().find(civSlicedAtlas)\n                .forEach(building -> Assert.assertTrue(building.getError().isPresent()));\n\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        new ComplexBuildingFinder().find(lbrSlicedAtlas)\n                .forEach(building -> Assert.assertTrue(building.getError().isPresent()));\n    }\n\n    /**\n     * This relation is made up of two lines. The first is a closed line, with an inner role, fully\n     * on the LBR side. The second is a self-intersecting closed outer, stretching across the\n     * boundary. Since that line is invalid geometry, it should not be sliced and instead be tagged\n     * with both CIV and LBR country tags, and this should propogate up to the unsliced relation\n     * which should have the same country tags.\n     */\n    @Test\n    public void testSelfIntersectingOuterRelationAcrossBoundary()\n    {\n        final Atlas rawAtlas = this.setup\n                .getSelfIntersectingOuterMemberRelationAcrossBoundaryAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        Assert.assertEquals(\"CIV,LBR\",\n                civSlicedAtlas.area(108768000000L).getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(rawAtlas.area(108768000000L).asPolygon(),\n                civSlicedAtlas.area(108768000000L).asPolygon());\n        Assert.assertEquals(\"CIV,LBR\", civSlicedAtlas.relation(1).getTag(ISOCountryTag.KEY).get());\n        Assert.assertTrue(\n                civSlicedAtlas.relation(1).getTag(SyntheticGeometrySlicedTag.KEY).isEmpty());\n        Assert.assertTrue(civSlicedAtlas.relation(1)\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        Assert.assertEquals(\"CIV,LBR\",\n                lbrSlicedAtlas.area(108768000000L).getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(rawAtlas.area(108768000000L).asPolygon(),\n                lbrSlicedAtlas.area(108768000000L).asPolygon());\n        Assert.assertEquals(\"CIV,LBR\", lbrSlicedAtlas.relation(1).getTag(ISOCountryTag.KEY).get());\n        Assert.assertTrue(\n                lbrSlicedAtlas.relation(1).getTag(SyntheticGeometrySlicedTag.KEY).isEmpty());\n        Assert.assertTrue(lbrSlicedAtlas.relation(1)\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n    }\n\n    /**\n     * This relation is made up of two lines. Both lines are on the LBR side. The first is a closed\n     * line, with an inner role. The second is a self-intersecting closed outer. We expect no\n     * slicing or merging to take place, other than country code assignment. The line with invalid\n     * geometry will get tagged as CIV,LBR due to a quirk in geometry polygon handling, but this is\n     * not very concerning as invalid geometry is not expected to be fully supported by slicing--\n     * the country codes here are a best guess.\n     */\n    @Test\n    public void testSelfIntersectingOuterRelationInOneCountry()\n    {\n        final Atlas rawAtlas = this.setup.getSelfIntersectingOuterMemberRelationAtlas();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n        // Assert that we CAN build a valid building with this relation\n        new ComplexBuildingFinder().find(lbrSlicedAtlas)\n                .forEach(building -> Assert.assertFalse(building.getError().isPresent()));\n\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(rawAtlas.numberOfLines(), lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(rawAtlas.numberOfRelations(), lbrSlicedAtlas.numberOfRelations());\n        Assert.assertTrue(Iterables.stream(lbrSlicedAtlas.entities())\n                .allMatch(entity -> (entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")\n                        || entity.getTag(ISOCountryTag.KEY).get().equals(\"CIV,LBR\"))\n                        && entity.getTag(SyntheticGeometrySlicedTag.KEY).isEmpty()));\n    }\n\n    /**\n     * This relation is made up of two closed lines, forming a multi-polygon with one inner and one\n     * outer, both spanning the boundary of two countries.\n     */\n    @Test\n    public void testSimpleMultiPolygonWithHoleSpanningTwoCountries()\n    {\n        final Atlas rawAtlas = this.setup.getSimpleMultiPolygonWithHoleAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(0, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n        final Iterable<ComplexBuilding> civBuildings = new ComplexBuildingFinder()\n                .find(civSlicedAtlas, Finder::ignore);\n        Assert.assertEquals(1, Iterables.size(civBuildings));\n\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n        final Iterable<ComplexBuilding> lbrBuildings = new ComplexBuildingFinder()\n                .find(lbrSlicedAtlas, Finder::ignore);\n        Assert.assertEquals(1, Iterables.size(lbrBuildings));\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Area rawArea = rawAtlas.areas(rawAreaCandidate -> rawAreaCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawArea.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                0L);\n\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        final Relation lbrRelation = lbrSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(civRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(lbrRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n    }\n\n    /**\n     * This simple test covers the case of a line with invalid geometry due to only having one node,\n     * repeated\n     */\n    @Test\n    public void testSingleNodeLine()\n    {\n        final Atlas rawAtlas = this.setup.getSingleNodeLine();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfRelations());\n\n        Assert.assertTrue(Iterables.stream(lbrSlicedAtlas.entities())\n                .allMatch(entity -> (entity.getTag(ISOCountryTag.KEY).get().equals(\"LBR\")\n                        || entity.getTag(ISOCountryTag.KEY).get().equals(\"CIV,LBR\"))\n                        && entity.getTag(SyntheticGeometrySlicedTag.KEY).isEmpty()));\n    }\n\n    /**\n     * This relation is made up of two open lines, both Edges, both crossing the country boundary\n     * and forming a multipolygon with one outer.\n     */\n    @Test\n    public void testSingleOuterMadeOfOpenLinesSpanningTwoCountries()\n    {\n        final Atlas rawAtlas = this.setup.getSingleOuterMadeOfOpenLinesSpanningTwoCountriesAtlas();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(3, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n\n        Assert.assertEquals(8, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Line rawLine = rawAtlas.lines(rawLineCandidate -> rawLineCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawLine.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Line rawLine = rawAtlas.lines(rawLineCandidate -> rawLineCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawLine.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                214805000000L);\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        Assert.assertTrue(civRelation.asMultiPolygon().get().isValid());\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(civRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        final Relation lbrRelation = lbrSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        Assert.assertTrue(lbrRelation.asMultiPolygon().get().isValid());\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(lbrRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n    }\n\n    /**\n     * This relation is made up of two open lines, both crossing the country boundary and forming a\n     * multipolygon with one outer.\n     */\n    @Test\n    public void testSingleOuterMadeOfOpenLinesSpanningTwoCountriesWithDuplicatePoints()\n    {\n\n        final Atlas rawAtlas = this.setup\n                .getSingleOuterMadeOfOpenLinesSpanningTwoCountriesAtlasWithDuplicatePoints();\n        final Atlas civSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"CIV\"),\n                rawAtlas).slice();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n\n        Assert.assertEquals(3, civSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, civSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, civSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, civSlicedAtlas.numberOfRelations());\n\n        Assert.assertEquals(8, lbrSlicedAtlas.numberOfPoints());\n        Assert.assertEquals(2, lbrSlicedAtlas.numberOfLines());\n        Assert.assertEquals(0, lbrSlicedAtlas.numberOfAreas());\n        Assert.assertEquals(1, lbrSlicedAtlas.numberOfRelations());\n\n        civSlicedAtlas.lines().forEach(line ->\n        {\n            final Iterator<Location> lineLocations = line.iterator();\n            Location previous = lineLocations.next();\n            while (lineLocations.hasNext())\n            {\n                final Location current = lineLocations.next();\n                Assert.assertNotEquals(current, previous);\n                previous = current;\n            }\n\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Line rawLine = rawAtlas.lines(rawLineCandidate -> rawLineCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawLine.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        lbrSlicedAtlas.lines().forEach(line ->\n        {\n            final Iterator<Location> lineLocations = line.iterator();\n            Location previous = lineLocations.next();\n            while (lineLocations.hasNext())\n            {\n                final Location current = lineLocations.next();\n                Assert.assertNotEquals(current, previous);\n                previous = current;\n            }\n\n            if (line.getTag(SyntheticGeometrySlicedTag.KEY).isPresent())\n            {\n                final Line rawLine = rawAtlas.lines(rawLineCandidate -> rawLineCandidate\n                        .getOsmIdentifier() == line.getOsmIdentifier()).iterator().next();\n                Assert.assertEquals(rawLine.getOsmTags(), line.getOsmTags());\n                Assert.assertEquals(1, line.relations().size());\n            }\n            else if (rawAtlas.line(line.getIdentifier()) != null)\n            {\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).getOsmTags(),\n                        line.getOsmTags());\n                Assert.assertEquals(rawAtlas.line(line.getIdentifier()).asPolyLine(),\n                        line.asPolyLine());\n                Assert.assertEquals(1, line.relations().size());\n            }\n        });\n\n        final CountrySlicingIdentifierFactory relationIdentifierFactory = new CountrySlicingIdentifierFactory(\n                214805000000L);\n        final Relation civRelation = civSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        Assert.assertTrue(civRelation.asMultiPolygon().get().isValid());\n        Assert.assertEquals(\"CIV\", civRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                civRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(civRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n        final Relation lbrRelation = lbrSlicedAtlas\n                .relation(relationIdentifierFactory.nextIdentifier());\n        Assert.assertTrue(lbrRelation.asMultiPolygon().get().isValid());\n        Assert.assertEquals(\"LBR\", lbrRelation.getTag(ISOCountryTag.KEY).get());\n        Assert.assertEquals(SyntheticGeometrySlicedTag.YES.toString(),\n                lbrRelation.getTag(SyntheticGeometrySlicedTag.KEY).get());\n        Assert.assertTrue(lbrRelation\n                .getTag(SyntheticInvalidMultiPolygonRelationMembersRemovedTag.KEY).isEmpty());\n\n    }\n\n    @Test\n    public void testSlicingOnRelationWithOnlyRelationsAsMembers()\n    {\n        final Atlas rawAtlas = this.setup.getRelationWithOnlyRelationsAsMembers();\n        final Atlas lbrSlicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(boundary).setCountryCode(\"LBR\"),\n                rawAtlas).slice();\n        for (final Relation relation : lbrSlicedAtlas.relations())\n        {\n            Assert.assertEquals(\"LBR\", relation.getTag(ISOCountryTag.KEY).get());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/raw/slicing/RawAtlasSlicerTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.raw.slicing;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * {@link RawAtlasSlicerTest} test data.\n *\n * @author mgostintsev\n */\npublic class RawAtlasSlicerTestRule extends CoreTestRule\n{\n    // Single crossing across boundary\n    private static final String LIBERIA_END = \"7.2,-8.4\";\n    private static final String ON_LIBERIA_AND_IVORY_COAST_BORDER = \"7.2, -8.3174953\";\n    private static final String IVORY_COAST_END = \"7.2,-8.2\";\n\n    // Zig-Zagging Line across boundary\n    private static final String IVORY_COAST_1 = \"6.983833,-8.28705\";\n    private static final String LIBERIA_1 = \"6.98573,-8.29511\";\n    private static final String IVORY_COAST_2 = \"6.98019,-8.29400\";\n    private static final String IVORY_COAST_3 = \"6.98237,-8.29694\";\n    private static final String LIBERIA_2 = \"6.98365,-8.30103\";\n\n    // Vertices for Line outside of boundaries\n    private static final String OUTSIDE_ALL_COUNTRIES_1 = \"14.2,-8.4\";\n    private static final String OUTSIDE_ALL_COUNTRIES_2 = \"14.2,-8.2\";\n\n    // Area spanning Ivory Coast and Liberia\n    private static final String AREA_CIV_SIDE_1 = \"6.92911,-8.30680\";\n    private static final String AREA_CIV_SIDE_2 = \"6.91758,-8.30635\";\n    private static final String AREA_LBR_SIDE_3 = \"6.91758,-8.32742\";\n    private static final String AREA_LBR_SIDE_4 = \"6.92843,-8.32731\";\n\n    // Area fully inside Ivory Coast\n    private static final String CIV_TOP_RIGHT = \"7.8551653,-8.4276384\";\n    private static final String CIV_BOTTOM_RIGHT = \"7.8550903,-8.4276536\";\n    private static final String CIV_BOTTOM_LEFT = \"7.8551026,-8.4277151\";\n    private static final String CIV_TOP_LEFT = \"7.8551775,-8.4276999\";\n\n    private static final String LOCATION_1 = \"6.89143764171,-8.32869925339\";\n\n    private static final String LOCATION_2 = \"6.8834659547,-8.32827100636\";\n\n    private static final String LOCATION_3 = \"6.88378482475,-8.33892365125\";\n\n    private static final String LOCATION_4 = \"6.892181659,-8.33721066313\";\n\n    private static final String LOCATION_5 = \"6.89154392997,-8.32623683297\";\n\n    private static final String LOCATION_6 = \"6.88957759339,-8.31547712632\";\n\n    private static final String LOCATION_7 = \"6.89946233792,-8.31702952181\";\n\n    private static final String LOCATION_8 = \"6.89707088638,-8.32591564769\";\n\n    private static final String LOCATION_9 = \"6.89845261541,-8.33469471182\";\n\n    private static final String LOCATION_10 = \"6.89366969006,-8.34154666432\";\n\n    private static final String LOCATION_11 = \"6.89733660382,-8.34780977714\";\n    private static final String LOCATION_12 = \"6.90440463282,-8.34261728189\";\n    private static final String LOCATION_13 = \"6.94567556723,-8.33813693927\";\n    private static final String LOCATION_14 = \"6.92331354425,-8.33803726312\";\n    private static final String LOCATION_15 = \"6.92321459489,-8.29956226825\";\n    private static final String LOCATION_16 = \"6.94854495346,-8.30125676284\";\n    private static final String LOCATION_17 = \"6.93934306671,-8.32996349477\";\n    private static final String LOCATION_18 = \"6.93093258278,-8.32896673324\";\n    private static final String LOCATION_19 = \"6.93142732129,-8.31341725345\";\n    private static final String LOCATION_20 = \"6.9411240911,-8.31441401497\";\n    private static final String LOCATION_21 = \"6.86563875322,-8.3252389315\";\n    private static final String LOCATION_22 = \"6.86322809383,-8.32322875353\";\n\n    private static final String LOCATION_23 = \"6.8623840025,-8.32698252672\";\n    private static final String LOCATION_24 = \"6.86676181589,-8.32504439815\";\n    private static final String LOCATION_25 = \"6.86541700168,-8.32328639305\";\n    private static final String LOCATION_26 = \"6.86367875216,-8.32594501551\";\n    private static final String LOCATION_27 = \"6.86482327931,-8.32636290197\";\n    private static final String LOCATION_28 = \"6.86431539572,-8.32451123267\";\n    private static final String LOCATION_29 = \"6.86573174578,-8.32812090707\";\n    private static final String LOCATION_30 = \"6.87133198688,-8.33750935353\";\n\n    private static final String LOCATION_31 = \"6.88017051964,-8.33197681515\";\n    private static final String LOCATION_32 = \"6.87441616889,-8.32877230817\";\n    private static final String LOCATION_33 = \"6.8837848,-8.3389237\";\n    private static final String LOCATION_34 = \"6.87142836787,-8.33437857895\";\n    private static final String LOCATION_35 = \"6.87193436775,-8.33149050006\";\n    private static final String LOCATION_36 = \"6.883466,-8.328271\";\n    private static final String LOCATION_37 = \"6.87407883746,-8.34039743242\";\n    private static final String LOCATION_38 = \"6.87822317845,-8.33993631058\";\n    private static final String LOCATION_39 = \"6.876712, -8.331375\";\n\n    private static final String LOCATION_40 = \"6.94567556723,-8.33813693927\";\n    private static final String LOCATION_41 = \"6.92331354425,-8.33803726312\";\n    private static final String LOCATION_42 = \"6.92321459489,-8.29956226825\";\n    private static final String LOCATION_43 = \"6.94854495346,-8.30125676284\";\n    private static final String LOCATION_45 = \"6.9456756,-8.3321111\";\n    private static final String LOCATION_46 = \"6.9232146,-8.3321111\";\n    private static final String LOCATION_47 = \"6.94567556723,-8.33013693927\";\n    private static final String LOCATION_48 = \"6.92331354425,-8.33013693927\";\n    private static final String LOCATION_49 = \"6.9452756,-8.3370369\";\n    private static final String LOCATION_50 = \"6.9452756,-8.3330369\";\n    private static final String LOCATION_51 = \"6.9402756,-8.3330369\";\n    private static final String LOCATION_52 = \"6.9402756,-8.3370369\";\n    private static final String LOCATION_53 = \"6.9233135,-8.3390373\";\n    private static final String LOCATION_54 = \"6.9213135,-8.3390373\";\n    private static final String LOCATION_55 = \"6.9213135,-8.3380373\";\n    private static final String LOCATION_56 = \"6.9456756,-8.3121111\";\n    private static final String LOCATION_57 = \"6.9232146,-8.3121111\";\n    private static final String LOCATION_58 = \"6.9452756,-8.3230369\";\n    private static final String LOCATION_59 = \"6.9402756,-8.3230369\";\n\n    private static final String LOCATION_60 = \"6.9393431,-8.3299635\";\n    private static final String LOCATION_61 = \"6.9393531,-8.3299635\";\n    private static final String LOCATION_62 = \"6.9393531,-8.3299535\";\n    private static final String LOCATION_63 = \"6.9393431,-8.3299535\";\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_41)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_42)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_43)) },\n\n            areas = { @Area(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                    @Loc(value = LOCATION_41), @Loc(value = LOCATION_42), @Loc(value = LOCATION_43),\n                    @Loc(value = LOCATION_40) }, tags = { \"building=yes\" }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"inner\", type = \"area\") })\n\n            })\n    private Atlas innerWithoutOuterAcrossBoundary;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_45)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_46)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_41)) },\n\n            lines = { @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                    @Loc(value = LOCATION_45), @Loc(value = LOCATION_46), @Loc(value = LOCATION_41),\n                    @Loc(value = LOCATION_40) }, tags = { \"building=yes\" }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"inner\", type = \"line\") })\n\n            })\n    private Atlas innerWithoutOuterInOneCountry;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_45)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_46)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_41)) },\n\n            lines = { @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                    @Loc(value = LOCATION_45), @Loc(value = LOCATION_46),\n                    @Loc(value = LOCATION_41) }, tags = { \"building=yes\" }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"line\") })\n\n            })\n    private Atlas openMultiPolygonInOneCountry;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_41)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_42)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_43)) },\n\n            lines = { @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                    @Loc(value = LOCATION_41), @Loc(value = LOCATION_42),\n                    @Loc(value = LOCATION_43) }, tags = { \"building=yes\" }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"line\") })\n\n            })\n    private Atlas openMultiPolygonAcrossBoundary;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_45)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_46)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_41)),\n                    @Point(id = \"108759000000\", coordinates = @Loc(value = LOCATION_43)),\n                    @Point(id = \"108751000000\", coordinates = @Loc(value = LOCATION_42)),\n                    @Point(id = \"108753000000\", coordinates = @Loc(value = LOCATION_47)),\n                    @Point(id = \"108755000000\", coordinates = @Loc(value = LOCATION_48)) },\n\n            lines = { @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                    @Loc(value = LOCATION_45), @Loc(value = LOCATION_46), @Loc(value = LOCATION_41),\n                    @Loc(value = LOCATION_40) }, tags = { \"building=yes\" }),\n                    @Line(id = \"108769000000\", coordinates = { @Loc(value = LOCATION_47),\n                            @Loc(value = LOCATION_48), @Loc(value = LOCATION_42),\n                            @Loc(value = LOCATION_43) }, tags = { \"building=yes\" }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"line\"),\n                            @Member(id = \"108769000000\", role = \"outer\", type = \"line\") })\n\n            })\n    private Atlas closedAndNonClosedMembersRelation;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_45)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_46)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_41)),\n                    @Point(id = \"108751000000\", coordinates = @Loc(value = LOCATION_49)),\n                    @Point(id = \"108753000000\", coordinates = @Loc(value = LOCATION_50)),\n                    @Point(id = \"108755000000\", coordinates = @Loc(value = LOCATION_51)),\n                    @Point(id = \"108757000000\", coordinates = @Loc(value = LOCATION_52)) },\n\n            lines = { @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                    @Loc(value = LOCATION_45), @Loc(value = LOCATION_46), @Loc(value = LOCATION_41),\n                    @Loc(value = LOCATION_40) }, tags = { \"building=yes\" }),\n                    @Line(id = \"108769000000\", coordinates = { @Loc(value = LOCATION_49),\n                            @Loc(value = LOCATION_50), @Loc(value = LOCATION_51),\n                            @Loc(value = LOCATION_52),\n                            @Loc(value = LOCATION_49) }, tags = { \"building=yes\" }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"inner\", type = \"line\"),\n                            @Member(id = \"108769000000\", role = \"outer\", type = \"line\") })\n\n            })\n    private Atlas innerOutsideOuterInOneCountry;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_45)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_46)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_41)),\n                    @Point(id = \"108751000000\", coordinates = @Loc(value = LOCATION_49)),\n                    @Point(id = \"108753000000\", coordinates = @Loc(value = LOCATION_58)),\n                    @Point(id = \"108755000000\", coordinates = @Loc(value = LOCATION_59)),\n                    @Point(id = \"108757000000\", coordinates = @Loc(value = LOCATION_52)) },\n\n            lines = { @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                    @Loc(value = LOCATION_45), @Loc(value = LOCATION_46), @Loc(value = LOCATION_41),\n                    @Loc(value = LOCATION_40) }, tags = { \"building=yes\" }),\n                    @Line(id = \"108769000000\", coordinates = { @Loc(value = LOCATION_49),\n                            @Loc(value = LOCATION_58), @Loc(value = LOCATION_59),\n                            @Loc(value = LOCATION_52),\n                            @Loc(value = LOCATION_49) }, tags = { \"building=yes\" }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"inner\", type = \"line\"),\n                            @Member(id = \"108769000000\", role = \"outer\", type = \"line\") })\n\n            })\n    private Atlas intersectingInnerAndOuterMemberRelation;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_45)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_46)),\n                    @Point(id = \"108751000000\", coordinates = @Loc(value = LOCATION_49)),\n                    @Point(id = \"108753000000\", coordinates = @Loc(value = LOCATION_50)),\n                    @Point(id = \"108755000000\", coordinates = @Loc(value = LOCATION_51)),\n                    @Point(id = \"108757000000\", coordinates = @Loc(value = LOCATION_52)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_53)),\n                    @Point(id = \"108759000000\", coordinates = @Loc(value = LOCATION_54)),\n                    @Point(id = \"108761000000\", coordinates = @Loc(value = LOCATION_55)) },\n\n            lines = { @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                    @Loc(value = LOCATION_45), @Loc(value = LOCATION_46), @Loc(value = LOCATION_53),\n                    @Loc(value = LOCATION_54), @Loc(value = LOCATION_55),\n                    @Loc(value = LOCATION_40) }, tags = { \"building=yes\" }),\n                    @Line(id = \"108769000000\", coordinates = { @Loc(value = LOCATION_49),\n                            @Loc(value = LOCATION_50), @Loc(value = LOCATION_51),\n                            @Loc(value = LOCATION_52),\n                            @Loc(value = LOCATION_49) }, tags = { \"building=yes\" }) },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"line\"),\n                            @Member(id = \"108769000000\", role = \"inner\", type = \"line\") }, wkt = \"MULTIPOLYGON (((-8.3381369 6.9456756, -8.3321111 6.9456756, -8.3321111 6.9232146, -8.3390373 6.9233135, -8.3390373 6.9213135, -8.3380373 6.9213135, -8.3381369 6.9456756, -8.3381369 6.9456756), (-8.3370369 6.9452756, -8.3330369 6.9452756, -8.3330369 6.9402756, -8.3370369 6.9402756, -8.3370369 6.9452756, -8.3370369 6.9452756)))\")\n\n            })\n    private Atlas selfIntersectingOuterMemberRelation;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_40)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_56)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_57)),\n                    @Point(id = \"108751000000\", coordinates = @Loc(value = LOCATION_49)),\n                    @Point(id = \"108753000000\", coordinates = @Loc(value = LOCATION_50)),\n                    @Point(id = \"108755000000\", coordinates = @Loc(value = LOCATION_51)),\n                    @Point(id = \"108757000000\", coordinates = @Loc(value = LOCATION_52)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_53)),\n                    @Point(id = \"108759000000\", coordinates = @Loc(value = LOCATION_54)),\n                    @Point(id = \"108761000000\", coordinates = @Loc(value = LOCATION_55)) },\n\n            areas = {\n                    @Area(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_40),\n                            @Loc(value = LOCATION_56), @Loc(value = LOCATION_57),\n                            @Loc(value = LOCATION_53), @Loc(value = LOCATION_54),\n                            @Loc(value = LOCATION_55), @Loc(value = LOCATION_40) }),\n                    @Area(id = \"108769000000\", coordinates = { @Loc(value = LOCATION_49),\n                            @Loc(value = LOCATION_50), @Loc(value = LOCATION_51),\n                            @Loc(value = LOCATION_52), @Loc(value = LOCATION_49) }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"area\"),\n                            @Member(id = \"108769000000\", role = \"inner\", type = \"area\") })\n\n            })\n    private Atlas selfIntersectingOuterMemberRelationAcrossBoundary;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = CIV_TOP_RIGHT)),\n                    @Point(id = \"2\", coordinates = @Loc(value = CIV_BOTTOM_RIGHT)),\n                    @Point(id = \"3\", coordinates = @Loc(value = CIV_BOTTOM_LEFT)),\n                    @Point(id = \"4\", coordinates = @Loc(value = CIV_TOP_LEFT)) },\n\n            lines = { @Line(id = \"1\", coordinates = { @Loc(value = CIV_TOP_RIGHT),\n                    @Loc(value = CIV_BOTTOM_RIGHT), @Loc(value = CIV_BOTTOM_LEFT),\n                    @Loc(value = CIV_TOP_LEFT),\n                    @Loc(value = CIV_TOP_RIGHT) }, tags = { \"building=yes\" }) })\n    private Atlas closedLineFullyInsideOneCountry;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = AREA_CIV_SIDE_1)),\n                    @Point(id = \"2\", coordinates = @Loc(value = AREA_CIV_SIDE_2)),\n                    @Point(id = \"3\", coordinates = @Loc(value = AREA_LBR_SIDE_3)),\n                    @Point(id = \"4\", coordinates = @Loc(value = AREA_LBR_SIDE_4)) },\n\n            areas = { @Area(id = \"1\", coordinates = { @Loc(value = AREA_CIV_SIDE_1),\n                    @Loc(value = AREA_LBR_SIDE_4), @Loc(value = AREA_LBR_SIDE_3),\n                    @Loc(value = AREA_CIV_SIDE_2),\n                    @Loc(value = AREA_CIV_SIDE_1) }, tags = { \"building=yes\" }) })\n    private Atlas closedLineSpanningTwoCountries;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = AREA_CIV_SIDE_1)),\n                    @Point(id = \"2\", coordinates = @Loc(value = AREA_CIV_SIDE_2)),\n                    @Point(id = \"3\", coordinates = @Loc(value = AREA_LBR_SIDE_3)),\n                    @Point(id = \"4\", coordinates = @Loc(value = AREA_LBR_SIDE_4)) },\n\n            lines = { @Line(id = \"1\", coordinates = { @Loc(value = AREA_CIV_SIDE_1),\n                    @Loc(value = AREA_CIV_SIDE_2), @Loc(value = AREA_LBR_SIDE_3),\n                    @Loc(value = AREA_LBR_SIDE_4),\n                    @Loc(value = AREA_CIV_SIDE_1) }, tags = { \"highway=primary\" }) })\n    private Atlas closedEdgeSpanningTwoCountries;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = OUTSIDE_ALL_COUNTRIES_1)),\n                    @Point(id = \"2\", coordinates = @Loc(value = OUTSIDE_ALL_COUNTRIES_2)) },\n\n            lines = { @Line(id = \"1\", coordinates = { @Loc(value = OUTSIDE_ALL_COUNTRIES_1),\n                    @Loc(value = OUTSIDE_ALL_COUNTRIES_2) }, tags = { \"highway=primary\" }) })\n    private Atlas roadFullyOutsideAllBoundaries;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = IVORY_COAST_END)),\n                    @Point(id = \"2\", coordinates = @Loc(value = ON_LIBERIA_AND_IVORY_COAST_BORDER)) },\n\n            lines = { @Line(id = \"1\", coordinates = { @Loc(value = IVORY_COAST_END),\n                    @Loc(value = ON_LIBERIA_AND_IVORY_COAST_BORDER) }, tags = {\n                            \"highway=primary\" }) })\n    private Atlas roadTouchingBoundary;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = IVORY_COAST_1)),\n                    @Point(id = \"2\", coordinates = @Loc(value = LIBERIA_1)),\n                    @Point(id = \"3\", coordinates = @Loc(value = IVORY_COAST_2)),\n                    @Point(id = \"4\", coordinates = @Loc(value = IVORY_COAST_3)),\n                    @Point(id = \"5\", coordinates = @Loc(value = LIBERIA_2)) },\n\n            lines = { @Line(id = \"1\", coordinates = { @Loc(value = IVORY_COAST_1),\n                    @Loc(value = LIBERIA_1), @Loc(value = IVORY_COAST_2),\n                    @Loc(value = IVORY_COAST_3),\n                    @Loc(value = LIBERIA_2) }, tags = { \"highway=primary\" }) })\n    private Atlas roadWeavingAcrossBoundary;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = LIBERIA_END)),\n                    @Point(id = \"2\", coordinates = @Loc(value = IVORY_COAST_END)) },\n\n            lines = { @Line(id = \"1000\", coordinates = { @Loc(value = LIBERIA_END),\n                    @Loc(value = IVORY_COAST_END) }, tags = { \"highway=primary\" }) })\n    private Atlas roadAcrossTwoCountries;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = IVORY_COAST_3)),\n                    @Point(id = \"2\", coordinates = @Loc(value = IVORY_COAST_2)) },\n\n            lines = { @Line(id = \"1\", coordinates = { @Loc(value = IVORY_COAST_2),\n                    @Loc(value = IVORY_COAST_3) }, tags = { \"highway=primary\" }) })\n    private Atlas roadInsideOneCountry;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"1\", coordinates = @Loc(value = LIBERIA_END)),\n                    @Point(id = \"2\", coordinates = @Loc(value = ON_LIBERIA_AND_IVORY_COAST_BORDER)),\n                    @Point(id = \"3\", coordinates = @Loc(value = IVORY_COAST_END)) },\n\n            lines = { @Line(id = \"1\", coordinates = { @Loc(value = LIBERIA_END),\n                    @Loc(value = ON_LIBERIA_AND_IVORY_COAST_BORDER),\n                    @Loc(value = IVORY_COAST_END) }, tags = { \"highway=primary\" }) })\n    private Atlas roadAcrossTwoCountriesWithPointOnBorder;\n\n    @TestAtlas(\n\n            lines = {\n\n                    @Line(id = \"1\", coordinates = { @Loc(LIBERIA_1), @Loc(LIBERIA_1) }),\n                    @Line(id = \"2\", coordinates = { @Loc(LIBERIA_1), @Loc(LIBERIA_2) })\n\n            }\n\n    )\n    private Atlas singleNodeLine;\n    @TestAtlas(\n\n            points = { @Point(id = \"214776000000\", coordinates = @Loc(value = LOCATION_30)),\n                    @Point(id = \"214775000000\", coordinates = @Loc(value = LOCATION_31)),\n                    @Point(id = \"214774000000\", coordinates = @Loc(value = LOCATION_32)),\n                    @Point(id = \"214773000000\", coordinates = @Loc(value = LOCATION_33)),\n                    @Point(id = \"214772000000\", coordinates = @Loc(value = LOCATION_34)),\n                    @Point(id = \"214771000000\", coordinates = @Loc(value = LOCATION_35)),\n                    @Point(id = \"214770000000\", coordinates = @Loc(value = LOCATION_36)),\n                    @Point(id = \"214769000000\", coordinates = @Loc(value = LOCATION_37)),\n                    @Point(id = \"214768000000\", coordinates = @Loc(value = LOCATION_38)),\n                    @Point(id = \"214708000000\", coordinates = @Loc(value = LOCATION_39)),\n                    @Point(id = \"214718000000\", coordinates = @Loc(value = LOCATION_39)) },\n\n            lines = {\n                    @Line(id = \"214778000000\", coordinates = { @Loc(value = LOCATION_38),\n                            @Loc(value = LOCATION_31),\n                            @Loc(value = LOCATION_32) }, tags = { \"highway=residential\" }),\n                    @Line(id = \"214777000000\", coordinates = { @Loc(value = LOCATION_38),\n                            @Loc(value = LOCATION_37), @Loc(value = LOCATION_30),\n                            @Loc(value = LOCATION_34), @Loc(value = LOCATION_35),\n                            @Loc(value = LOCATION_32) }, tags = { \"highway=primary\" }) },\n\n            relations = {\n\n                    @Relation(id = \"214805000000\", tags = { \"type=multipolygon\",\n                            \"leisure=park\" }, members = {\n                                    @Member(id = \"214777000000\", role = \"outer\", type = \"line\"),\n                                    @Member(id = \"214778000000\", role = \"outer\", type = \"line\") }, wkt = \"MULTIPOLYGON (((-8.3399363 6.8782232, -8.3403974 6.8740788, -8.3375094 6.871332, -8.3343786 6.8714284, -8.3314905 6.8719344, -8.3287723 6.8744162, -8.3319768 6.8801705, -8.3399363 6.8782232)))\") })\n    private Atlas singleOuterMadeOfOpenLinesSpanningTwoCountriesWithDuplicatePoints;\n    @TestAtlas(\n\n            points = { @Point(id = \"214776000000\", coordinates = @Loc(value = LOCATION_30)),\n                    @Point(id = \"214775000000\", coordinates = @Loc(value = LOCATION_31)),\n                    @Point(id = \"214774000000\", coordinates = @Loc(value = LOCATION_32)),\n                    @Point(id = \"214773000000\", coordinates = @Loc(value = LOCATION_33)),\n                    @Point(id = \"214772000000\", coordinates = @Loc(value = LOCATION_34)),\n                    @Point(id = \"214771000000\", coordinates = @Loc(value = LOCATION_35)),\n                    @Point(id = \"214770000000\", coordinates = @Loc(value = LOCATION_36)),\n                    @Point(id = \"214769000000\", coordinates = @Loc(value = LOCATION_37)),\n                    @Point(id = \"214768000000\", coordinates = @Loc(value = LOCATION_38)) },\n\n            lines = {\n                    @Line(id = \"214778000000\", coordinates = { @Loc(value = LOCATION_38),\n                            @Loc(value = LOCATION_31),\n                            @Loc(value = LOCATION_32) }, tags = { \"highway=residential\" }),\n                    @Line(id = \"214777000000\", coordinates = { @Loc(value = LOCATION_38),\n                            @Loc(value = LOCATION_37), @Loc(value = LOCATION_30),\n                            @Loc(value = LOCATION_34), @Loc(value = LOCATION_35),\n                            @Loc(value = LOCATION_32) }, tags = { \"highway=primary\" }) },\n\n            relations = {\n\n                    @Relation(id = \"214805000000\", tags = { \"type=multipolygon\",\n                            \"leisure=park\" }, members = {\n                                    @Member(id = \"214777000000\", role = \"outer\", type = \"line\"),\n                                    @Member(id = \"214778000000\", role = \"outer\", type = \"line\") }, wkt = \"MULTIPOLYGON (((-8.3399363 6.8782232, -8.3403974 6.8740788, -8.3375094 6.871332, -8.3343786 6.8714284, -8.3314905 6.8719344, -8.3287723 6.8744162, -8.3319768 6.8801705, -8.3399363 6.8782232)))\\n\"\n                                            + \"\") })\n    private Atlas singleOuterMadeOfOpenLinesSpanningTwoCountries;\n    @TestAtlas(\n\n            points = { @Point(id = \"214602000000\", coordinates = @Loc(value = LOCATION_1)),\n                    @Point(id = \"214600000000\", coordinates = @Loc(value = LOCATION_2)),\n                    @Point(id = \"214598000000\", coordinates = @Loc(value = LOCATION_3)),\n                    @Point(id = \"214597000000\", coordinates = @Loc(value = LOCATION_4)),\n                    @Point(id = \"214593000000\", coordinates = @Loc(value = LOCATION_5)),\n                    @Point(id = \"214591000000\", coordinates = @Loc(value = LOCATION_6)),\n                    @Point(id = \"214589000000\", coordinates = @Loc(value = LOCATION_7)),\n                    @Point(id = \"214588000000\", coordinates = @Loc(value = LOCATION_8)),\n                    @Point(id = \"214584000000\", coordinates = @Loc(value = LOCATION_9)),\n                    @Point(id = \"214582000000\", coordinates = @Loc(value = LOCATION_10)),\n                    @Point(id = \"214580000000\", coordinates = @Loc(value = LOCATION_11)),\n                    @Point(id = \"214579000000\", coordinates = @Loc(value = LOCATION_12)) },\n\n            areas = { @Area(id = \"214599000000\", coordinates = { @Loc(value = LOCATION_4),\n                    @Loc(value = LOCATION_3), @Loc(value = LOCATION_2), @Loc(value = LOCATION_1),\n                    @Loc(value = LOCATION_4) }, tags = { \"leisure=park\" }),\n                    @Area(id = \"214590000000\", coordinates = { @Loc(value = LOCATION_8),\n                            @Loc(value = LOCATION_7), @Loc(value = LOCATION_6),\n                            @Loc(value = LOCATION_5),\n                            @Loc(value = LOCATION_8) }, tags = { \"leisure=park\" }),\n                    @Area(id = \"214581000000\", coordinates = { @Loc(value = LOCATION_12),\n                            @Loc(value = LOCATION_11), @Loc(value = LOCATION_10),\n                            @Loc(value = LOCATION_9),\n                            @Loc(value = LOCATION_12) }, tags = { \"leisure=park\" }) },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=multipolygon\", \"leisure=park\" }, members = {\n                            @Member(id = \"214599000000\", role = \"outer\", type = \"area\"),\n                            @Member(id = \"214590000000\", role = \"outer\", type = \"area\"),\n                            @Member(id = \"214581000000\", role = \"outer\", type = \"area\") }, wkt = \"MULTIPOLYGON (((-8.3372107 6.8921817, -8.3389237 6.8837848, -8.328271 6.883466, -8.3286993 6.8914376, -8.3372107 6.8921817, -8.3372107 6.8921817)), ((-8.3426173 6.9044046, -8.3478098 6.8973366, -8.3415467 6.8936697, -8.3346947 6.8984526, -8.3426173 6.9044046, -8.3426173 6.9044046)), ((-8.3259156 6.8970709, -8.3170295 6.8994623, -8.3154771 6.8895776, -8.3262368 6.8915439, -8.3259156 6.8970709, -8.3259156 6.8970709)))\")\n\n            })\n    private Atlas simpleMultiPolygon;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"214602000000\", coordinates = @Loc(value = LOCATION_1)),\n                    @Point(id = \"214600000000\", coordinates = @Loc(value = LOCATION_2)),\n                    @Point(id = \"214598000000\", coordinates = @Loc(value = LOCATION_3)),\n                    @Point(id = \"214597000000\", coordinates = @Loc(value = LOCATION_4)),\n                    @Point(id = \"214593000000\", coordinates = @Loc(value = LOCATION_5)),\n                    @Point(id = \"214591000000\", coordinates = @Loc(value = LOCATION_6)),\n                    @Point(id = \"214589000000\", coordinates = @Loc(value = LOCATION_7)),\n                    @Point(id = \"214588000000\", coordinates = @Loc(value = LOCATION_8)),\n                    @Point(id = \"214584000000\", coordinates = @Loc(value = LOCATION_9)),\n                    @Point(id = \"214582000000\", coordinates = @Loc(value = LOCATION_10)),\n                    @Point(id = \"214580000000\", coordinates = @Loc(value = LOCATION_11)),\n                    @Point(id = \"214579000000\", coordinates = @Loc(value = LOCATION_12)) },\n\n            areas = { @Area(id = \"214599000000\", coordinates = { @Loc(value = LOCATION_4),\n                    @Loc(value = LOCATION_3), @Loc(value = LOCATION_2), @Loc(value = LOCATION_1),\n                    @Loc(value = LOCATION_4) }, tags = { \"leisure=park\" }),\n                    @Area(id = \"214590000000\", coordinates = { @Loc(value = LOCATION_8),\n                            @Loc(value = LOCATION_7), @Loc(value = LOCATION_6),\n                            @Loc(value = LOCATION_5),\n                            @Loc(value = LOCATION_8) }, tags = { \"leisure=park\" }),\n                    @Area(id = \"214581000000\", coordinates = { @Loc(value = LOCATION_12),\n                            @Loc(value = LOCATION_11), @Loc(value = LOCATION_10),\n                            @Loc(value = LOCATION_9),\n                            @Loc(value = LOCATION_12) }, tags = { \"leisure=park\" }) },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=boundary\" }, members = {\n                            @Member(id = \"214599000000\", role = \"outer\", type = \"area\"),\n                            @Member(id = \"214590000000\", role = \"outer\", type = \"area\"),\n                            @Member(id = \"214581000000\", role = \"outer\", type = \"area\") }, wkt = \"MULTIPOLYGON (((-8.3372107 6.8921817, -8.3389237 6.8837848, -8.328271 6.883466, -8.3286993 6.8914376, -8.3372107 6.8921817, -8.3372107 6.8921817)), ((-8.3426173 6.9044046, -8.3478098 6.8973366, -8.3415467 6.8936697, -8.3346947 6.8984526, -8.3426173 6.9044046, -8.3426173 6.9044046)), ((-8.3259156 6.8970709, -8.3170295 6.8994623, -8.3154771 6.8895776, -8.3262368 6.8915439, -8.3259156 6.8970709, -8.3259156 6.8970709)))\")\n\n            })\n    private Atlas simpleBoundaryRelation;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"214602000000\", coordinates = @Loc(value = LOCATION_1)),\n                    @Point(id = \"214600000000\", coordinates = @Loc(value = LOCATION_2)),\n                    @Point(id = \"214598000000\", coordinates = @Loc(value = LOCATION_3)),\n                    @Point(id = \"214597000000\", coordinates = @Loc(value = LOCATION_4)),\n                    @Point(id = \"214593000000\", coordinates = @Loc(value = LOCATION_5)),\n                    @Point(id = \"214591000000\", coordinates = @Loc(value = LOCATION_6)),\n                    @Point(id = \"214589000000\", coordinates = @Loc(value = LOCATION_7)),\n                    @Point(id = \"214588000000\", coordinates = @Loc(value = LOCATION_8)),\n                    @Point(id = \"214584000000\", coordinates = @Loc(value = LOCATION_9)),\n                    @Point(id = \"214582000000\", coordinates = @Loc(value = LOCATION_10)),\n                    @Point(id = \"214580000000\", coordinates = @Loc(value = LOCATION_11)),\n                    @Point(id = \"214579000000\", coordinates = @Loc(value = LOCATION_12)) },\n\n            areas = { @Area(id = \"214599000000\", coordinates = { @Loc(value = LOCATION_4),\n                    @Loc(value = LOCATION_3), @Loc(value = LOCATION_2), @Loc(value = LOCATION_1),\n                    @Loc(value = LOCATION_4) }, tags = { \"leisure=park\" }),\n                    @Area(id = \"214590000000\", coordinates = { @Loc(value = LOCATION_8),\n                            @Loc(value = LOCATION_7), @Loc(value = LOCATION_6),\n                            @Loc(value = LOCATION_5),\n                            @Loc(value = LOCATION_8) }, tags = { \"leisure=park\" }),\n                    @Area(id = \"214581000000\", coordinates = { @Loc(value = LOCATION_12),\n                            @Loc(value = LOCATION_11), @Loc(value = LOCATION_10),\n                            @Loc(value = LOCATION_9),\n                            @Loc(value = LOCATION_12) }, tags = { \"leisure=park\" }) },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=boundary\" }, members = {\n                            @Member(id = \"214590000000\", role = \"outer\", type = \"area\") }, wkt = \"MULTIPOLYGON (((-8.3259156 6.8970709, -8.3170295 6.8994623, -8.3154771 6.8895776, -8.3262368 6.8915439, -8.3259156 6.8970709, -8.3259156 6.8970709)))\")\n\n            })\n    private Atlas simpleBoundaryRelationConsolidate;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"106032000000\", coordinates = @Loc(value = LOCATION_21)),\n                    @Point(id = \"106031000000\", coordinates = @Loc(value = LOCATION_22)),\n                    @Point(id = \"106030000000\", coordinates = @Loc(value = LOCATION_23)),\n                    @Point(id = \"106029000000\", coordinates = @Loc(value = LOCATION_24)),\n                    @Point(id = \"106028000000\", coordinates = @Loc(value = LOCATION_25)),\n                    @Point(id = \"106027000000\", coordinates = @Loc(value = LOCATION_26)),\n                    @Point(id = \"106026000000\", coordinates = @Loc(value = LOCATION_27)),\n                    @Point(id = \"106025000000\", coordinates = @Loc(value = LOCATION_28)),\n                    @Point(id = \"106024000000\", coordinates = @Loc(value = LOCATION_29)) },\n\n            lines = {\n                    @Line(id = \"106036000000\", coordinates = { @Loc(value = LOCATION_21),\n                            @Loc(value = LOCATION_27), @Loc(value = LOCATION_26) }),\n                    @Line(id = \"106035000000\", coordinates = { @Loc(value = LOCATION_21),\n                            @Loc(value = LOCATION_28), @Loc(value = LOCATION_26) }),\n                    @Line(id = \"106034000000\", coordinates = { @Loc(value = LOCATION_24),\n                            @Loc(value = LOCATION_25), @Loc(value = LOCATION_22) }),\n                    @Line(id = \"106033000000\", coordinates = { @Loc(value = LOCATION_24),\n                            @Loc(value = LOCATION_29), @Loc(value = LOCATION_23),\n                            @Loc(value = LOCATION_22) }) },\n\n            relations = {\n\n                    @Relation(id = \"214805000000\", tags = { \"type=multipolygon\",\n                            \"natural=water\" }, members = {\n                                    @Member(id = \"106033000000\", role = \"outer\", type = \"line\"),\n                                    @Member(id = \"106034000000\", role = \"outer\", type = \"line\"),\n                                    @Member(id = \"106035000000\", role = \"inner\", type = \"line\"),\n                                    @Member(id = \"106036000000\", role = \"inner\", type = \"line\") }, wkt = \"MULTIPOLYGON (((-8.3250444 6.8667618, -8.3281209 6.8657317, -8.3269825 6.862384, -8.3232288 6.8632281, -8.3232864 6.865417, -8.3250444 6.8667618), (-8.3252389 6.8656388, -8.3245112 6.8643154, -8.325945 6.8636788, -8.3263629 6.8648233, -8.3252389 6.8656388)))\")\n\n            })\n    private Atlas complexMuliPolygonWithHoleUsingOpenLines;\n    @TestAtlas(\n\n            points = { @Point(id = \"106032000000\", coordinates = @Loc(value = LOCATION_21)),\n                    @Point(id = \"106031000000\", coordinates = @Loc(value = LOCATION_22)),\n                    @Point(id = \"106030000000\", coordinates = @Loc(value = LOCATION_23)),\n                    @Point(id = \"106029000000\", coordinates = @Loc(value = LOCATION_24)),\n                    @Point(id = \"106028000000\", coordinates = @Loc(value = LOCATION_25)),\n                    @Point(id = \"106027000000\", coordinates = @Loc(value = LOCATION_26)),\n                    @Point(id = \"106026000000\", coordinates = @Loc(value = LOCATION_27)),\n                    @Point(id = \"106025000000\", coordinates = @Loc(value = LOCATION_28)),\n                    @Point(id = \"106024000000\", coordinates = @Loc(value = LOCATION_29)) },\n\n            areas = { @Area(id = \"106036000000\", coordinates = { @Loc(value = LOCATION_28),\n                    @Loc(value = LOCATION_21), @Loc(value = LOCATION_27), @Loc(value = LOCATION_26),\n                    @Loc(value = LOCATION_28) }, tags = { \"water=natural\" }),\n\n                    @Area(id = \"106034000000\", coordinates = { @Loc(value = LOCATION_23),\n                            @Loc(value = LOCATION_29), @Loc(value = LOCATION_24),\n                            @Loc(value = LOCATION_25), @Loc(value = LOCATION_22),\n                            @Loc(value = LOCATION_23) }, tags = { \"water=natural\" }) },\n\n            relations = {\n\n                    @Relation(id = \"214805000000\", tags = { \"type=multipolygon\",\n                            \"natural=water\" }, members = {\n                                    @Member(id = \"106034000000\", role = \"outer\", type = \"area\"),\n                                    @Member(id = \"106036000000\", role = \"inner\", type = \"area\") }, wkt = \"MULTIPOLYGON (((-8.3269825 6.862384, -8.3281209 6.8657317, -8.3250444 6.8667618, -8.3232864 6.865417, -8.3232288 6.8632281, -8.3269825 6.862384, -8.3269825 6.862384), (-8.3245112 6.8643154, -8.3252389 6.8656388, -8.3263629 6.8648233, -8.325945 6.8636788, -8.3245112 6.8643154, -8.3245112 6.8643154)))\")\n\n            })\n    private Atlas complexMuliPolygonWithHoleUsingClosedLines;\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_13)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_14)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_15)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_16)),\n                    @Point(id = \"108760000000\", coordinates = @Loc(value = LOCATION_17)),\n                    @Point(id = \"108762000000\", coordinates = @Loc(value = LOCATION_18)),\n                    @Point(id = \"108764000000\", coordinates = @Loc(value = LOCATION_19)),\n                    @Point(id = \"108766000000\", coordinates = @Loc(value = LOCATION_20)) },\n\n            areas = {\n                    @Area(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_13),\n                            @Loc(value = LOCATION_14), @Loc(value = LOCATION_15),\n                            @Loc(value = LOCATION_16), @Loc(value = LOCATION_13) }),\n                    @Area(id = \"108770000000\", coordinates = { @Loc(value = LOCATION_17),\n                            @Loc(value = LOCATION_18), @Loc(value = LOCATION_19),\n                            @Loc(value = LOCATION_20), @Loc(value = LOCATION_17) }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"area\"),\n                            @Member(id = \"108770000000\", role = \"inner\", type = \"area\") }, wkt = \"MULTIPOLYGON (((-8.3381369 6.9456756, -8.3380373 6.9233135, -8.2995623 6.9232146, -8.3012568 6.948545, -8.3381369 6.9456756, -8.3381369 6.9456756), (-8.3299635 6.9393431, -8.3289667 6.9309326, -8.3134173 6.9314273, -8.314414 6.9411241, -8.3299635 6.9393431, -8.3299635 6.9393431)))\")\n\n            })\n    private Atlas multiPolygonWithHole;\n    @TestAtlas(points = {\n            @Point(id = \"1\", coordinates = @Loc(value = LOCATION_13), tags = { \"leisure=park\" }),\n            @Point(id = \"2\", coordinates = @Loc(value = LOCATION_14), tags = { \"leisure=park\" })\n\n    }, relations = {\n            @Relation(id = \"3\", tags = { \"bugs=no\" }, members = {\n                    @Member(id = \"1\", role = \"member\", type = \"point\") }),\n            @Relation(id = \"4\", tags = { \"bugs=no\" }, members = {\n                    @Member(id = \"2\", role = \"member\", type = \"point\") }),\n            @Relation(id = \"5\", tags = { \"bugs=no\" }, members = {\n                    @Member(id = \"3\", role = \"member\", type = \"relation\"),\n                    @Member(id = \"4\", role = \"member\", type = \"relation\") }) })\n    private Atlas relationWithOnlyRelationsAsMembers;\n\n    @TestAtlas(points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_13)),\n            @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_14)),\n            @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_15)),\n            @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_16)),\n            @Point(id = \"108760000000\", coordinates = @Loc(value = LOCATION_17)),\n            @Point(id = \"108762000000\", coordinates = @Loc(value = LOCATION_18)),\n            @Point(id = \"108764000000\", coordinates = @Loc(value = LOCATION_19)),\n            @Point(id = \"108766000000\", coordinates = @Loc(value = LOCATION_20)) },\n\n            areas = {\n                    @Area(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_13),\n                            @Loc(value = LOCATION_14), @Loc(value = LOCATION_15),\n                            @Loc(value = LOCATION_16), @Loc(value = LOCATION_13) }),\n                    @Area(id = \"108770000000\", coordinates = { @Loc(value = LOCATION_17),\n                            @Loc(value = LOCATION_18), @Loc(value = LOCATION_19),\n                            @Loc(value = LOCATION_20), @Loc(value = LOCATION_17) }),\n                    @Area(id = \"3\", coordinates = { @Loc(value = LOCATION_17),\n                            @Loc(value = LOCATION_18), @Loc(value = LOCATION_19),\n                            @Loc(value = LOCATION_20), @Loc(value = LOCATION_17) }) },\n\n            relations = {\n                    @Relation(id = \"5\", tags = { \"bugs=no\" }, members = {\n                            @Member(id = \"108756000000\", role = \"member\", type = \"point\") }),\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"area\"),\n                            @Member(id = \"108770000000\", role = \"inner\", type = \"area\"),\n                            @Member(id = \"108752000000\", role = \"badType\", type = \"point\"),\n                            @Member(id = \"5\", role = \"badType\", type = \"relation\"),\n                            @Member(id = \"3\", role = \"badRole\", type = \"area\") }, wkt = \"MULTIPOLYGON (((-8.3381369 6.9456756, -8.3380373 6.9233135, -8.2995623 6.9232146, -8.3012568 6.948545, -8.3381369 6.9456756, -8.3381369 6.9456756), (-8.3299635 6.9393431, -8.3289667 6.9309326, -8.3134173 6.9314273, -8.314414 6.9411241, -8.3299635 6.9393431, -8.3299635 6.9393431)))\") })\n    private Atlas relationWithInvalidMultiPolygonMembers;\n    @TestAtlas(points = { @Point(id = \"214775000000\", coordinates = @Loc(value = LOCATION_31)),\n            @Point(id = \"214774000000\", coordinates = @Loc(value = LOCATION_32)),\n            @Point(id = \"214768000000\", coordinates = @Loc(value = LOCATION_38)) },\n\n            lines = { @Line(id = \"214778000000\", coordinates = { @Loc(value = LOCATION_38),\n                    @Loc(value = LOCATION_31),\n                    @Loc(value = LOCATION_32) }, tags = { \"highway=residential\" }) },\n\n            relations = {\n                    @Relation(id = \"214806000000\", tags = {}, members = {\n                            @Member(id = \"214778000000\", role = \"\", type = \"line\") }),\n                    @Relation(id = \"214805000000\", tags = { \"type=multipolygon\",\n                            \"natural=water\" }, members = {\n                                    @Member(id = \"214778000000\", role = \"outer\", type = \"line\") }) })\n    private Atlas singleOuterMultiPolygonSpanningTwoAtlases1;\n\n    @TestAtlas(points = { @Point(id = \"214776000000\", coordinates = @Loc(value = LOCATION_30)),\n\n            @Point(id = \"214774000000\", coordinates = @Loc(value = LOCATION_32)),\n\n            @Point(id = \"214772000000\", coordinates = @Loc(value = LOCATION_34)),\n            @Point(id = \"214771000000\", coordinates = @Loc(value = LOCATION_35)),\n            @Point(id = \"214769000000\", coordinates = @Loc(value = LOCATION_37)),\n            @Point(id = \"214768000000\", coordinates = @Loc(value = LOCATION_38)) },\n\n            lines = { @Line(id = \"214777000000\", coordinates = { @Loc(value = LOCATION_38),\n                    @Loc(value = LOCATION_37), @Loc(value = LOCATION_30), @Loc(value = LOCATION_34),\n                    @Loc(value = LOCATION_35),\n                    @Loc(value = LOCATION_32) }, tags = { \"highway=primary\" }) },\n\n            relations = {\n                    @Relation(id = \"214806000000\", tags = {}, members = {\n                            @Member(id = \"214777000000\", role = \"\", type = \"line\") }),\n                    @Relation(id = \"214805000000\", tags = { \"type=multipolygon\",\n                            \"natural=water\" }, members = {\n                                    @Member(id = \"214777000000\", role = \"outer\", type = \"line\") }) })\n    private Atlas singleOuterMultiPolygonSpanningTwoAtlases2;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_13)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_14)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_15)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_16)),\n                    @Point(id = \"108760000000\", coordinates = @Loc(value = LOCATION_17)),\n                    @Point(id = \"108762000000\", coordinates = @Loc(value = LOCATION_18)),\n                    @Point(id = \"108764000000\", coordinates = @Loc(value = LOCATION_19)),\n                    @Point(id = \"108766000000\", coordinates = @Loc(value = LOCATION_20)) },\n\n            lines = {\n                    @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_13),\n                            @Loc(value = LOCATION_14), @Loc(value = LOCATION_15),\n                            @Loc(value = LOCATION_16), @Loc(value = LOCATION_13) }),\n                    @Line(id = \"108770000000\", coordinates = { @Loc(value = \"6.9393431,-8.3299635\"),\n                            @Loc(value = \"6.9309326,-8.3299635 \"),\n                            @Loc(value = \"6.9309326,-8.3134173\"),\n                            @Loc(value = \"6.9393431,-8.3134173\"),\n                            @Loc(value = \"6.9393431,-8.3299635\") }),\n                    @Line(id = \"108760000000\", coordinates = { @Loc(value = LOCATION_60),\n                            @Loc(value = LOCATION_61), @Loc(value = LOCATION_62),\n                            @Loc(value = LOCATION_63), @Loc(value = LOCATION_60) }) },\n\n            relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"line\"),\n                            @Member(id = \"108770000000\", role = \"inner\", type = \"line\"),\n                            @Member(id = \"108760000000\", role = \"inner\", type = \"line\") }, wkt = \"MULTIPOLYGON (((-8.3381369 6.9456756, -8.3380373 6.9233135, -8.2995623 6.9232146, -8.3012568 6.948545, -8.3381369 6.9456756, -8.3381369 6.9456756), (-8.3299635 6.9393431, -8.3299635 6.9393531, -8.3299535 6.9393531, -8.3299535 6.9393431, -8.3299635 6.9393431, -8.3299635 6.9393431), (-8.3299635 6.9393431, -8.3299635 6.9309326, -8.3134173 6.9309326, -8.3134173 6.9393431, -8.3299635 6.9393431, -8.3299635 6.9393431)))\")\n\n            })\n    private Atlas multiPolygonWithOverlappingSlicedInners;\n\n    @TestAtlas(\n\n            points = { @Point(id = \"108752000000\", coordinates = @Loc(value = LOCATION_13)),\n                    @Point(id = \"108754000000\", coordinates = @Loc(value = LOCATION_14)),\n                    @Point(id = \"108756000000\", coordinates = @Loc(value = LOCATION_15)),\n                    @Point(id = \"108758000000\", coordinates = @Loc(value = LOCATION_16)),\n                    @Point(id = \"108760000000\", coordinates = @Loc(value = LOCATION_17)),\n                    @Point(id = \"108762000000\", coordinates = @Loc(value = LOCATION_18)),\n                    @Point(id = \"108764000000\", coordinates = @Loc(value = LOCATION_19)),\n                    @Point(id = \"108766000000\", coordinates = @Loc(value = LOCATION_20)) },\n\n            lines = {\n                    @Line(id = \"108768000000\", coordinates = { @Loc(value = LOCATION_13),\n                            @Loc(value = LOCATION_14), @Loc(value = LOCATION_15),\n                            @Loc(value = LOCATION_16), @Loc(value = LOCATION_13) }),\n                    @Line(id = \"108770000000\", coordinates = { @Loc(value = \"6.9393431,-8.3299635\"),\n                            @Loc(value = \"6.9309326,-8.3299635 \"),\n                            @Loc(value = \"6.9309326,-8.3234173\"),\n                            @Loc(value = \"6.9393431,-8.3234173\"),\n                            @Loc(value = \"6.9393431,-8.3299635\") }),\n                    @Line(id = \"108760000000\", coordinates = { @Loc(value = LOCATION_60),\n                            @Loc(value = LOCATION_61), @Loc(value = LOCATION_62),\n                            @Loc(value = LOCATION_63), @Loc(value = LOCATION_60) }) },\n\n            relations = {\n\n                    @Relation(tags = { \"type=multipolygon\", \"building=yes\" }, members = {\n                            @Member(id = \"108768000000\", role = \"outer\", type = \"line\"),\n                            @Member(id = \"108770000000\", role = \"inner\", type = \"line\"),\n                            @Member(id = \"108760000000\", role = \"inner\", type = \"line\") }, wkt = \"MULTIPOLYGON (((-8.3381369 6.9456756, -8.3380373 6.9233135, -8.2995623 6.9232146, -8.3012568 6.948545, -8.3381369 6.9456756, -8.3381369 6.9456756), (-8.3299635 6.9393431, -8.3299635 6.9393531, -8.3299535 6.9393531, -8.3299535 6.9393431, -8.3299635 6.9393431, -8.3299635 6.9393431), (-8.3299635 6.9393431, -8.3299635 6.9309326, -8.3234173 6.9309326, -8.3234173 6.9393431, -8.3299635 6.9393431, -8.3299635 6.9393431)))\")\n\n            })\n    private Atlas multiPolygonWithOverlappingUnslicedInners;\n\n    public Atlas getClosedEdgeSpanningTwoCountriesAtlas()\n    {\n        return this.closedEdgeSpanningTwoCountries;\n    }\n\n    public Atlas getClosedLineFullyInOneCountryAtlas()\n    {\n        return this.closedLineFullyInsideOneCountry;\n    }\n\n    public Atlas getClosedLineSpanningTwoCountriesAtlas()\n    {\n        return this.closedLineSpanningTwoCountries;\n    }\n\n    public Atlas getComplexMultiPolygonWithHoleUsingClosedLinesAtlas()\n    {\n        return this.complexMuliPolygonWithHoleUsingClosedLines;\n    }\n\n    public Atlas getComplexMultiPolygonWithHoleUsingOpenLinesAtlas()\n    {\n        return this.complexMuliPolygonWithHoleUsingOpenLines;\n    }\n\n    public Atlas getInnerOutsideOuterRelationAtlas()\n    {\n        return this.innerOutsideOuterInOneCountry;\n    }\n\n    public Atlas getInnerWithoutOuterAcrossBoundaryAtlas()\n    {\n        return this.innerWithoutOuterAcrossBoundary;\n    }\n\n    public Atlas getInnerWithoutOuterInOneCountryAtlas()\n    {\n        return this.innerWithoutOuterInOneCountry;\n    }\n\n    public Atlas getIntersectingInnerAndOuterMembersAtlas()\n    {\n        return this.intersectingInnerAndOuterMemberRelation;\n    }\n\n    public Atlas getMultiPolygonWithOverlappingSlicedInners()\n    {\n        return this.multiPolygonWithOverlappingSlicedInners;\n    }\n\n    public Atlas getMultiPolygonWithOverlappingUnslicedInners()\n    {\n        return this.multiPolygonWithOverlappingUnslicedInners;\n    }\n\n    public Atlas getOpenMultiPolygonAcrossBoundaryAtlas()\n    {\n        return this.openMultiPolygonAcrossBoundary;\n    }\n\n    public Atlas getOpenMultiPolygonInOneCountryAtlas()\n    {\n        return this.openMultiPolygonInOneCountry;\n    }\n\n    public Atlas getRelationWithInvalidMultiPolygonMembers()\n    {\n        return this.relationWithInvalidMultiPolygonMembers;\n    }\n\n    public Atlas getRelationWithOneClosedAndOneOpenMemberAtlas()\n    {\n        return this.closedAndNonClosedMembersRelation;\n    }\n\n    public Atlas getRelationWithOnlyRelationsAsMembers()\n    {\n        return this.relationWithOnlyRelationsAsMembers;\n    }\n\n    public Atlas getRoadAcrossTwoCountriesAtlas()\n    {\n        return this.roadAcrossTwoCountries;\n    }\n\n    public Atlas getRoadAcrossTwoCountriesWithPointOnBorderAtlas()\n    {\n        return this.roadAcrossTwoCountriesWithPointOnBorder;\n    }\n\n    public Atlas getRoadFullyInOneCountryAtlas()\n    {\n        return this.roadInsideOneCountry;\n    }\n\n    public Atlas getRoadOutsideAllBoundariesAtlas()\n    {\n        return this.roadFullyOutsideAllBoundaries;\n    }\n\n    public Atlas getRoadTouchingBoundaryAtlas()\n    {\n        return this.roadTouchingBoundary;\n    }\n\n    public Atlas getRoadWeavingAlongBoundaryAtlas()\n    {\n        return this.roadWeavingAcrossBoundary;\n    }\n\n    public Atlas getSelfIntersectingOuterMemberRelationAcrossBoundaryAtlas()\n    {\n        return this.selfIntersectingOuterMemberRelationAcrossBoundary;\n    }\n\n    public Atlas getSelfIntersectingOuterMemberRelationAtlas()\n    {\n        return this.selfIntersectingOuterMemberRelation;\n    }\n\n    public Atlas getSimpleBoundaryRelationAtlas()\n    {\n        return this.simpleBoundaryRelation;\n    }\n\n    public Atlas getSimpleBoundaryRelationConsolidateAtlas()\n    {\n        return this.simpleBoundaryRelationConsolidate;\n    }\n\n    public Atlas getSimpleMultiPolygonAtlas()\n    {\n        return this.simpleMultiPolygon;\n    }\n\n    public Atlas getSimpleMultiPolygonWithHoleAtlas()\n    {\n        return this.multiPolygonWithHole;\n    }\n\n    public Atlas getSingleNodeLine()\n    {\n        return this.singleNodeLine;\n    }\n\n    public Atlas getSingleOuterMadeOfOpenLinesSpanningTwoCountriesAtlas()\n    {\n        return this.singleOuterMadeOfOpenLinesSpanningTwoCountries;\n    }\n\n    public Atlas getSingleOuterMadeOfOpenLinesSpanningTwoCountriesAtlasWithDuplicatePoints()\n    {\n        return this.singleOuterMadeOfOpenLinesSpanningTwoCountriesWithDuplicatePoints;\n    }\n\n    public Atlas getSingleOuterMultiPolygonSpanningTwoAtlases1()\n    {\n        return this.singleOuterMultiPolygonSpanningTwoAtlases1;\n    }\n\n    public Atlas getSingleOuterMultiPolygonSpanningTwoAtlases2()\n    {\n        return this.singleOuterMultiPolygonSpanningTwoAtlases2;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/routing/AStarRouterTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Heading;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\nimport org.openstreetmap.atlas.geography.atlas.multi.MultiAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * @author matthieun\n */\npublic class AStarRouterTest\n{\n    @Rule\n    public final AStarRouterTestRule rule = new AStarRouterTestRule();\n    private final Distance threshold = Distance.meters(40);\n\n    private Atlas atlas1;\n    private Atlas atlas2;\n\n    private Atlas multiAtlas;\n\n    @Before\n    public void setup()\n    {\n        this.atlas1 = this.rule.getAtlas1();\n        this.atlas2 = this.rule.getAtlas2();\n        this.multiAtlas = new MultiAtlas(this.atlas1, this.atlas2);\n    }\n\n    @Test\n    public void testAlgorithm()\n    {\n        final Location start = Location.TEST_6.shiftAlongGreatCircle(Heading.NORTH,\n                Distance.ONE_METER);\n        final Location end = Location.TEST_2.shiftAlongGreatCircle(Heading.EAST,\n                Distance.ONE_METER);\n        final Route dijkstraRoute = AStarRouter.dijkstra(this.multiAtlas, this.threshold)\n                .route(start, end);\n        Assert.assertEquals(Route.forEdges(this.multiAtlas.edge(9), this.multiAtlas.edge(-9),\n                this.multiAtlas.edge(5), this.multiAtlas.edge(6)), dijkstraRoute);\n        final Route balancedRoute = AStarRouter.balanced(this.multiAtlas, this.threshold)\n                .route(start, end);\n        Assert.assertEquals(Route.forEdges(this.multiAtlas.edge(9), this.multiAtlas.edge(-9),\n                this.multiAtlas.edge(5), this.multiAtlas.edge(6)), balancedRoute);\n    }\n\n    @Test\n    public void testEdgeCases()\n    {\n        final AtlasBuilder builder = new PackedAtlasBuilder();\n        final Map<String, String> tags = new HashMap<>();\n        final Location location1 = Location.TEST_6;\n        final Location location2 = Location.TEST_2;\n        final Location location3 = Location.TEST_1;\n        builder.addNode(1, location1, tags);\n        builder.addNode(2, location2, tags);\n        builder.addNode(3, location3, tags);\n        builder.addEdge(1, new Segment(location1, location2), tags);\n        builder.addEdge(2, new Segment(location2, location3), tags);\n        final Atlas routeAtlas = builder.get();\n\n        final AStarRouter router = AStarRouter.dijkstra(routeAtlas, this.threshold);\n        // Same edge\n        Assert.assertEquals(Route.forEdge(routeAtlas.edge(1)),\n                router.route(routeAtlas.edge(1), routeAtlas.edge(1)));\n        Assert.assertEquals(Route.forEdges(routeAtlas.edge(1), routeAtlas.edge(2)),\n                router.route(routeAtlas.node(1), routeAtlas.node(3)));\n        Assert.assertEquals(Route.forEdges(routeAtlas.edge(1), routeAtlas.edge(2)),\n                router.route(routeAtlas.edge(1), routeAtlas.edge(2)));\n        try\n        {\n            router.route(routeAtlas.edge(1), null);\n            Assert.fail(\"Did not throw exception\");\n        }\n        catch (final CoreException e)\n        {\n            // Expected\n        }\n        catch (final Exception e)\n        {\n            Assert.fail(\"Did not throw proper exception\");\n        }\n\n        final Location start = location1.shiftAlongGreatCircle(Heading.EAST, Distance.ONE_METER);\n        final Location end = location3.shiftAlongGreatCircle(Heading.WEST, Distance.ONE_METER);\n        Assert.assertEquals(Route.forEdges(routeAtlas.edge(1), routeAtlas.edge(2)),\n                router.route(start, end));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/routing/AStarRouterTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author samg\n */\npublic class AStarRouterTestRule extends CoreTestRule\n{\n    @TestAtlas(nodes = {\n            @Node(id = \"123\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                    \"highway=traffic_signal\" }),\n            @Node(id = \"12345\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                    \"highway=traffic_signal\" }),\n            @Node(id = \"4\", coordinates = @Loc(value = Location.TEST_1_COORDINATES), tags = {\n                    \"highway=traffic_signal\" }), },\n\n            edges = {\n                    @Edge(id = \"5\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge5\", \"surface=concrete\", \"lanes=3\" }),\n                    @Edge(id = \"6\", coordinates = { @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"highway=secondary\", \"name=edge98\", \"bridge=cantilever\",\n                                    \"maxspeed=100\" }) }, relations = {\n                                            @Relation(id = \"1\", members = {\n                                                    @Member(id = \"4\", role = \"notThere\", type = \"node\") }),\n                                            @Relation(id = \"3\", osmId = \"2\", members = {\n                                                    @Member(id = \"5\", role = \"in\", type = \"edge\"),\n                                                    @Member(id = \"12345\", role = \"node\", type = \"node\"),\n                                                    @Member(id = \"6\", role = \"out\", type = \"edge\") }), })\n    private Atlas atlas1;\n\n    @TestAtlas(points = {\n            @Point(id = \"1\", coordinates = @Loc(value = Location.TEST_3_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"2\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"3\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"4\", coordinates = @Loc(value = Location.TEST_7_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"5\", coordinates = @Loc(value = Location.TEST_4_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"6\", coordinates = @Loc(value = Location.TEST_1_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"7\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }), },\n\n            nodes = {\n                    @Node(id = \"123\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"1234\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"12345\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                            \"highway=turning_circle\" }), },\n\n            lines = {\n                    @Line(id = \"32\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES) }, tags = { \"power=line\" }),\n                    @Line(id = \"23\", coordinates = { @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES) }, tags = {\n                                    \"aeroway=runway\" }),\n                    @Line(id = \"24\", coordinates = { @Loc(value = Location.TEST_7_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"natural=coastline\", \"waterway=canal\" }) },\n\n            edges = {\n                    @Edge(id = \"9\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_5_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge9\", \"surface=concrete\", \"lanes=3\" }),\n                    @Edge(id = \"-9\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge_9\", \"surface=fine_gravel\" }),\n                    @Edge(id = \"98\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"highway=secondary\", \"name=edge98\", \"bridge=movable\",\n                                    \"maxspeed=100\" }),\n                    @Edge(id = \"987\", coordinates = { @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"highway=residential\", \"name=edge987\", \"tunnel=culvert\",\n                                    \"maxspeed=50 knots\" }) },\n\n            areas = {\n                    @Area(id = \"45\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"leisure=golf_course\", \"natural=grassland\" }),\n                    @Area(id = \"54\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"leisure=swimming_pool\", \"sport=swimming\" }),\n                    @Area(id = \"4554\", coordinates = { @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES) }, tags = {\n                                    \"hello=world\" }) },\n\n            relations = {\n                    @Relation(id = \"1\", members = { @Member(id = \"9\", role = \"in\", type = \"edge\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\"),\n                            @Member(id = \"-9\", role = \"out\", type = \"edge\") }, tags = {}),\n                    @Relation(id = \"2\", members = {\n                            @Member(id = \"45\", role = \"area\", type = \"area\"),\n                            @Member(id = \"32\", role = \"line\", type = \"line\"),\n                            @Member(id = \"5\", role = \"pt\", type = \"point\"),\n                            @Member(id = \"1\", role = \"rel\", type = \"relation\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\") }, tags = {}) })\n    private Atlas atlas2;\n\n    public Atlas getAtlas1()\n    {\n        return this.atlas1;\n    }\n\n    public Atlas getAtlas2()\n    {\n        return this.atlas2;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/routing/AllPathsRouterTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.function.Predicate;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Route;\n\n/**\n * {@link AllPathsRouter} unit test\n *\n * @author mgostintsev\n */\npublic class AllPathsRouterTest\n{\n    @Rule\n    public RoutingTestRule rule = new RoutingTestRule();\n\n    @Test\n    public void testBidirectionalSimpleRoutes()\n    {\n        final Atlas atlas = this.rule.getBiDirectionalCyclicRouteAtlas();\n        final Set<Route> routes = AllPathsRouter.allRoutes(atlas.edge(315932590),\n                atlas.edge(317932590), Route.ROUTE_COMPARATOR);\n\n        final Set<Route> expectedRoutes = new TreeSet<>(Route.ROUTE_COMPARATOR);\n        expectedRoutes.add(Route.forEdges(atlas.edge(315932590), atlas.edge(316932590),\n                atlas.edge(317932590)));\n\n        Assert.assertEquals(\"Expect one distinct route between start and end\", 1, routes.size());\n        Assert.assertEquals(\"Expect deterministic results from the router\", expectedRoutes, routes);\n    }\n\n    @Test\n    public void testMultipleRoutesFromStartToEnd()\n    {\n        final Atlas atlas = this.rule.getMultipleRoutesAtlas();\n        final Set<Route> routes = AllPathsRouter.allRoutes(atlas.edge(314932590),\n                atlas.edge(319932590), Route.ROUTE_COMPARATOR);\n\n        final Set<Route> expectedRoutes = new TreeSet<>(Route.ROUTE_COMPARATOR);\n        expectedRoutes.add(Route.forEdges(atlas.edge(314932590), atlas.edge(315932590),\n                atlas.edge(316932590), atlas.edge(319932590)));\n        expectedRoutes.add(Route.forEdges(atlas.edge(314932590), atlas.edge(317932590),\n                atlas.edge(318932590), atlas.edge(319932590)));\n\n        Assert.assertEquals(\"Expect two distinct routes between start and end\", 2, routes.size());\n        Assert.assertEquals(\"Expect deterministic results from the router\", expectedRoutes, routes);\n    }\n\n    @Test\n    public void testNoPossibleRouteBetweenStartAndEnd()\n    {\n        final Atlas atlas = this.rule.getNoPossibleRouteAtlas();\n        final Set<Route> routes = AllPathsRouter.allRoutes(atlas.edge(315932590),\n                atlas.edge(318932590), Route.ROUTE_COMPARATOR);\n\n        Assert.assertEquals(\"Expect no possible route between start and end\", 0, routes.size());\n    }\n\n    @Test\n    public void testRoutingAlongCyclicalGraph()\n    {\n        final Atlas atlas = this.rule.getCyclicalRouteAtlas();\n        final Set<Route> routes = AllPathsRouter.allRoutes(atlas.edge(315932590),\n                atlas.edge(317932590), Route.ROUTE_COMPARATOR);\n\n        final Set<Route> expectedRoutes = new TreeSet<>(Route.ROUTE_COMPARATOR);\n        expectedRoutes.add(Route.forEdges(atlas.edge(315932590), atlas.edge(316932590),\n                atlas.edge(317932590)));\n\n        Assert.assertEquals(\"Expect a single route between start and end\", 1, routes.size());\n        Assert.assertEquals(\"Expect deterministic results from the router\", expectedRoutes, routes);\n    }\n\n    @Test\n    public void testRoutingWithFilter()\n    {\n        final Atlas atlas = this.rule.getBiDirectionalCyclicRouteAtlas();\n        final Predicate<Edge> onlyMainEdges = edge -> Edge\n                .isMainEdgeIdentifier(edge.getIdentifier());\n        final Set<Route> routes = AllPathsRouter.allRoutes(atlas.edge(315932590),\n                atlas.edge(317932590), onlyMainEdges, Route.ROUTE_COMPARATOR);\n\n        final Set<Route> expectedRoutes = new TreeSet<>(Route.ROUTE_COMPARATOR);\n        expectedRoutes.add(Route.forEdges(atlas.edge(315932590), atlas.edge(316932590),\n                atlas.edge(317932590)));\n\n        Assert.assertEquals(\n                \"Expect a single distinct route between start and end, since we've filtered out all routes that have a non-main edge\",\n                1, routes.size());\n        Assert.assertEquals(\"Expect deterministic results from the router\", expectedRoutes, routes);\n    }\n\n    @Test\n    public void testRoutingWithFilterAndMaximumAllowedPath()\n    {\n        final Atlas atlas = this.rule.getMultipleRoutesAtlas();\n        final Set<Route> routes = AllPathsRouter.allRoutes(atlas.edge(314932590),\n                atlas.edge(319932590), x -> true, 1);\n\n        final Set<Route> expectedRoutes = new HashSet<>();\n        expectedRoutes.add(Route.forEdges(atlas.edge(314932590), atlas.edge(315932590),\n                atlas.edge(316932590), atlas.edge(319932590)));\n\n        Assert.assertEquals(\"Expect two distinct routes between start and end\", 1, routes.size());\n        Assert.assertEquals(\"Expect deterministic results from the router\", expectedRoutes, routes);\n    }\n\n    @Test\n    public void testSingleRouteFromStartToEnd()\n    {\n        final Atlas atlas = this.rule.getSingleRouteAtlas();\n        final Set<Route> routes = AllPathsRouter.allRoutes(atlas.edge(315932590),\n                atlas.edge(317932590), Route.ROUTE_COMPARATOR);\n\n        final Set<Route> expectedRoutes = new TreeSet<>(Route.ROUTE_COMPARATOR);\n        expectedRoutes.add(Route.forEdges(atlas.edge(315932590), atlas.edge(316932590),\n                atlas.edge(317932590)));\n\n        Assert.assertEquals(\"Expect a single route between start and end\", 1, routes.size());\n        Assert.assertEquals(\"Expect deterministic results from the router\", expectedRoutes, routes);\n        Assert.assertEquals(\n                \"Total number of edges in the Atlas equals the number of edges in the route\",\n                Route.forEdges(atlas.edges()), routes.iterator().next());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/routing/RoutingTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.routing;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * Test data for {@link AllPathsRouterTest}\n *\n * @author mgostintsev\n */\npublic class RoutingTestRule extends CoreTestRule\n{\n    private static final String ONE = \"37.3022071, -121.8505286\";\n    private static final String TWO = \"37.3012035, -121.8469065\";\n    private static final String THREE = \"37.2956185, -121.8513284\";\n    private static final String FOUR = \"37.3009167, -121.8553179\";\n    private static final String FIVE = \"37.3013126, -121.8529148\";\n    private static final String SIX = \"37.3035398, -121.8592693\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            }, edges = {\n\n                    @Edge(id = \"315932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"-315932590\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = ONE) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"316932590\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"-316932590\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = TWO), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"317932590\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"-317932590\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = THREE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"318932590\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = ONE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"-318932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = FOUR), }, tags = { \"highway=primary\" }) })\n    private Atlas biDirectionalCyclicalRouteAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX))\n\n            }, edges = {\n\n                    @Edge(id = \"315932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"316932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = THREE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"317932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = FOUR), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"318932590\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = SIX), }, tags = { \"highway=primary\" }) })\n    private Atlas noPossibleRouteAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            }, edges = {\n\n                    @Edge(id = \"315932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"316932590\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"317932590\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR), }, tags = { \"highway=primary\" }) })\n    private Atlas singleRouteAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX))\n\n            }, edges = {\n                    @Edge(id = \"314932590\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = ONE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"315932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"316932590\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"317932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = FOUR), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"318932590\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = THREE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"319932590\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FIVE), }, tags = { \"highway=primary\" }) })\n    private Atlas multipleRoutesAtlas;\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            }, edges = {\n\n                    @Edge(id = \"315932590\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"316932590\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"317932590\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR), }, tags = { \"highway=primary\" }),\n                    @Edge(id = \"318932590\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = ONE), }, tags = { \"highway=primary\" }) })\n    private Atlas cyclicalRouteAtlas;\n\n    public Atlas getBiDirectionalCyclicRouteAtlas()\n    {\n        return this.biDirectionalCyclicalRouteAtlas;\n    }\n\n    public Atlas getCyclicalRouteAtlas()\n    {\n        return this.cyclicalRouteAtlas;\n    }\n\n    public Atlas getMultipleRoutesAtlas()\n    {\n        return this.multipleRoutesAtlas;\n    }\n\n    public Atlas getNoPossibleRouteAtlas()\n    {\n        return this.noPossibleRouteAtlas;\n    }\n\n    public Atlas getSingleRouteAtlas()\n    {\n        return this.singleRouteAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/statistics/AtlasStatisticsTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.statistics.AtlasStatistics.StatisticKey;\nimport org.openstreetmap.atlas.geography.atlas.statistics.AtlasStatistics.StatisticValue;\n\n/**\n * Test the {@link AtlasStatistics} range of classes.\n *\n * @author matthieun\n */\npublic class AtlasStatisticsTest\n{\n    @Rule\n    public final AtlasStatisticsTestRule rule = new AtlasStatisticsTestRule();\n\n    @Test\n    public void testCSVCompatability()\n    {\n        final StatisticKey key = new StatisticKey(\"\", \"last_edit_user_name\", \"kepta\\\"sds'sds\");\n        // if there is a \" or , or \\n character within a field in a CSV, the CSV field is wrapped in\n        // double quotes and the interior double quote is escaped\n        final String correctlyFormattedCSVKey = \",last_edit_user_name,\\\"kepta\\\"\\\"sds'sds\\\"\";\n        Assert.assertEquals(key.toString(), correctlyFormattedCSVKey);\n    }\n\n    @Test\n    public void testCounting()\n    {\n        final Atlas atlas = this.rule.getPackedAtlas();\n        final Counter counter = new Counter();\n        final AtlasStatistics statistics = counter.processAtlas(atlas);\n        Assert.assertEquals(7.245,\n                statistics.get(new StatisticKey(\"PRIMARY\", \"length_named\", \"true\")).getCount(),\n                0.01);\n    }\n\n    @Test\n    public void testCountingAddresses()\n    {\n        final Atlas atlas = this.rule.getAddressAtlas();\n        final AtlasStatistics statistics = new Counter().processAtlas(atlas);\n\n        // Associated Street\n        Assert.assertEquals(1.0,\n                statistics.get(new StatisticKey(\"\", \"associated_street\", \"true\")).getCount(), 0.01);\n\n        // Address Ranges\n        Assert.assertEquals(1.0,\n                statistics.get(new StatisticKey(\"\", \"address_ranges\", \"true\")).getCount(), 0.01);\n        Assert.assertEquals(4.21,\n                statistics.get(new StatisticKey(\"\", \"address_ranges_distance\", \"true\")).getCount(),\n                0.01);\n\n        // Address Housenumber\n        Assert.assertEquals(6.0,\n                statistics.get(new StatisticKey(\"\", \"address_housenumber\", \"true\")).getCount(),\n                0.01);\n\n        // Address Housename\n        Assert.assertEquals(1.0,\n                statistics.get(new StatisticKey(\"\", \"address_housename\", \"true\")).getCount(), 0.01);\n\n        // Address Street\n        Assert.assertEquals(1.0,\n                statistics.get(new StatisticKey(\"\", \"address_street\", \"true\")).getCount(), 0.01);\n\n        // Address Housenumber and Street\n        Assert.assertEquals(1.0, statistics\n                .get(new StatisticKey(\"\", \"address_housenumber_and_street\", \"true\")).getCount(),\n                0.01);\n\n        // Address Housename and Street\n        Assert.assertEquals(1.0, statistics\n                .get(new StatisticKey(\"\", \"address_housename_and_street\", \"true\")).getCount(),\n                0.01);\n\n        // Address Blocknumber\n        Assert.assertEquals(2.0,\n                statistics.get(new StatisticKey(\"\", \"address_blocknumber\", \"true\")).getCount(),\n                0.01);\n    }\n\n    @Test\n    public void testCountingFerries()\n    {\n        final Atlas atlas = this.rule.getFerryAtlas();\n        final AtlasStatistics statistics = new Counter().processAtlas(atlas);\n\n        // Ferries\n        Assert.assertEquals(2.0,\n                statistics.get(new StatisticKey(\"\", \"ferry_route\", \"true\")).getCount(), 0.01);\n        Assert.assertEquals(14.05,\n                statistics.get(new StatisticKey(\"\", \"ferry_route_distance\", \"true\")).getCount(),\n                0.01);\n    }\n\n    @Test\n    public void testCountingRefs()\n    {\n        final Atlas atlas = this.rule.getRefsAtlas();\n        final AtlasStatistics statistics = new Counter().processAtlas(atlas);\n\n        // Refs\n        Assert.assertEquals(1.0, statistics.get(new StatisticKey(\"\", \"ref\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(0.53,\n                statistics.get(new StatisticKey(\"\", \"ref_distance\", \"true\")).getCount(), 0.01);\n\n        Assert.assertEquals(3.0,\n                statistics.get(new StatisticKey(\"\", \"ref_no_relation\", \"true\")).getCount(), 0.01);\n        Assert.assertEquals(0.16,\n                statistics.get(new StatisticKey(\"\", \"ref_no_relation_distance\", \"true\")).getCount(),\n                0.01);\n\n        Assert.assertEquals(1.0, statistics.get(new StatisticKey(\"\", \"int_ref\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(0.53,\n                statistics.get(new StatisticKey(\"\", \"int_ref_distance\", \"true\")).getCount(), 0.01);\n\n        Assert.assertEquals(2.0,\n                statistics.get(new StatisticKey(\"\", \"int_ref_no_relation\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(0.21, statistics\n                .get(new StatisticKey(\"\", \"int_ref_no_relation_distance\", \"true\")).getCount(),\n                0.01);\n\n        // Lane direction\n        Assert.assertEquals(3.0,\n                statistics.get(new StatisticKey(\"\", \"lane_direction\", \"true\")).getCount(), 0.01);\n        Assert.assertEquals(0.31,\n                statistics.get(new StatisticKey(\"\", \"lane_direction_distance\", \"true\")).getCount(),\n                0.01);\n\n        // Toll booths\n        Assert.assertEquals(1.0,\n                statistics.get(new StatisticKey(\"\", \"toll_booths\", \"true\")).getCount(), 0.01);\n    }\n\n    @Test\n    public void testCountingWater()\n    {\n        final Atlas atlas = this.rule.getWaterAtlas();\n        final AtlasStatistics statistics = new Counter().processAtlas(atlas);\n\n        // Rivers\n        Assert.assertEquals(6.0, statistics.get(new StatisticKey(\"\", \"rivers\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(24.92,\n                statistics.get(new StatisticKey(\"\", \"rivers_distance\", \"true\")).getCount(), 0.01);\n\n        // Wetland\n        Assert.assertEquals(1.0, statistics.get(new StatisticKey(\"\", \"wetland\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(0.84,\n                statistics.get(new StatisticKey(\"\", \"wetland_surface\", \"true\")).getCount(), 0.01);\n\n        // Lakes2\n        Assert.assertEquals(3.0, statistics.get(new StatisticKey(\"\", \"lakes2\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(2.08,\n                statistics.get(new StatisticKey(\"\", \"lakes2_surface\", \"true\")).getCount(), 0.01);\n\n        // Reservoir\n        Assert.assertEquals(1.0,\n                statistics.get(new StatisticKey(\"\", \"reservoir\", \"true\")).getCount(), 0.01);\n        Assert.assertEquals(0.77,\n                statistics.get(new StatisticKey(\"\", \"reservoir_surface\", \"true\")).getCount(), 0.01);\n\n        // Lagoon\n        Assert.assertEquals(1.0, statistics.get(new StatisticKey(\"\", \"lagoon\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(0.35,\n                statistics.get(new StatisticKey(\"\", \"lagoon_surface\", \"true\")).getCount(), 0.01);\n\n        // Pool\n        Assert.assertEquals(1.0, statistics.get(new StatisticKey(\"\", \"pool\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(0.084,\n                statistics.get(new StatisticKey(\"\", \"pool_surface\", \"true\")).getCount(), 0.01);\n\n        // Coastlines\n        Assert.assertEquals(4.803585,\n                statistics.get(new StatisticKey(\"\", \"coastline_distance\", \"true\")).getCount(),\n                0.01);\n\n        // Harbour\n        Assert.assertEquals(3.0, statistics.get(new StatisticKey(\"\", \"harbour\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(0.99,\n                statistics.get(new StatisticKey(\"\", \"harbour_distance\", \"true\")).getCount(), 0.01);\n        Assert.assertEquals(2.39,\n                statistics.get(new StatisticKey(\"\", \"harbour_surface\", \"true\")).getCount(), 0.01);\n\n        // Bay\n        Assert.assertEquals(2.0, statistics.get(new StatisticKey(\"\", \"bay\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(2.39,\n                statistics.get(new StatisticKey(\"\", \"harbour_surface\", \"true\")).getCount(), 0.01);\n\n        // Beach\n        Assert.assertEquals(2.0, statistics.get(new StatisticKey(\"\", \"beach\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(2.44,\n                statistics.get(new StatisticKey(\"\", \"beach_surface\", \"true\")).getCount(), 0.01);\n\n        // Island\n        Assert.assertEquals(2.0, statistics.get(new StatisticKey(\"\", \"island\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(0.25,\n                statistics.get(new StatisticKey(\"\", \"island_surface\", \"true\")).getCount(), 0.01);\n\n        // Piers\n        Assert.assertEquals(3.0, statistics.get(new StatisticKey(\"\", \"pier\", \"true\")).getCount(),\n                0.01);\n        Assert.assertEquals(2.67,\n                statistics.get(new StatisticKey(\"\", \"pier_distance\", \"true\")).getCount(), 0.01);\n        Assert.assertEquals(0.11,\n                statistics.get(new StatisticKey(\"\", \"pier_surface\", \"true\")).getCount(), 0.01);\n    }\n\n    @Test\n    public void testFormatting()\n    {\n        final Atlas atlas = this.rule.getWaterAtlas();\n        final AtlasStatistics statistics = new Counter().processAtlas(atlas);\n\n        Assert.assertEquals(\"6.00,34.00\",\n                statistics.get(new StatisticKey(\"\", \"rivers\", \"true\")).toString());\n        Assert.assertEquals(\"24.92,37.61\",\n                statistics.get(new StatisticKey(\"\", \"rivers_distance\", \"true\")).toString());\n\n    }\n\n    @Test(expected = CoreException.class)\n    public void testFormattingKeyTooHigh()\n    {\n        final double key = 1.0 / 0.0;\n        final double value = 100.0;\n        new StatisticValue(key, value);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testFormattingKeyTooLow()\n    {\n        final double key = -1.0 / 0.0;\n        final double value = 100.0;\n        new StatisticValue(key, value);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testFormattingValueTooHigh()\n    {\n        final double key = 100.0;\n        final double value = Double.MAX_VALUE / 1.5;\n        System.out.println(new StatisticValue(key, value));\n    }\n\n    @Test\n    public void testMerge()\n    {\n        final StatisticKey key = new StatisticKey(\"RESIDENTIAL\", \"length_named\", \"TRUE\");\n        final AtlasStatistics stat1 = new AtlasStatistics();\n        stat1.add(key, new StatisticValue(100.0, 500.0));\n        final AtlasStatistics stat2 = new AtlasStatistics();\n        stat2.add(key, new StatisticValue(200.0, 300.0));\n        final AtlasStatistics merged = AtlasStatistics.merge(stat1, stat2);\n        Assert.assertEquals(300.0, merged.get(key).getCount(), 0.001);\n        Assert.assertEquals(800.0, merged.get(key).getTotalCount(), 0.001);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/statistics/AtlasStatisticsTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * Test rule for {@link AtlasStatisticsTest}\n *\n * @author matthieun\n */\npublic class AtlasStatisticsTestRule extends CoreTestRule\n{\n    @TestAtlas(points = {\n            @Point(id = \"1\", coordinates = @Loc(value = Location.TEST_3_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"2\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"3\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"4\", coordinates = @Loc(value = Location.TEST_7_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"5\", coordinates = @Loc(value = Location.TEST_4_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"6\", coordinates = @Loc(value = Location.TEST_1_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }),\n            @Point(id = \"7\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                    \"addr:city=Cupertino\" }), },\n\n            nodes = {\n                    @Node(id = \"123\", coordinates = @Loc(value = Location.TEST_6_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"1234\", coordinates = @Loc(value = Location.TEST_5_COORDINATES), tags = {\n                            \"highway=turning_circle\" }),\n                    @Node(id = \"12345\", coordinates = @Loc(value = Location.TEST_2_COORDINATES), tags = {\n                            \"highway=turning_circle\" }), },\n\n            lines = {\n                    @Line(id = \"32\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES) }, tags = { \"power=line\" }),\n                    @Line(id = \"23\", coordinates = { @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES) }, tags = {\n                                    \"aeroway=runway\" }),\n                    @Line(id = \"24\", coordinates = { @Loc(value = Location.TEST_7_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"natural=coastline\", \"waterway=canal\" }) },\n\n            edges = {\n                    @Edge(id = \"9\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_5_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge9\", \"surface=concrete\", \"lanes=3\" }),\n                    @Edge(id = \"-9\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = { \"highway=primary\",\n                                    \"name=edge_9\", \"surface=gravel\" }),\n                    @Edge(id = \"98\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"highway=secondary\", \"name=edge98\", \"bridge=movable\",\n                                    \"maxspeed=100\" }),\n                    @Edge(id = \"987\", coordinates = { @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"highway=residential\", \"name=edge987\", \"tunnel=culvert\",\n                                    \"maxspeed=50 knots\" }) },\n\n            areas = {\n                    @Area(id = \"45\", coordinates = { @Loc(value = Location.TEST_6_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES) }, tags = {\n                                    \"leisure=golf_course\", \"natural=grassland\" }),\n                    @Area(id = \"54\", coordinates = { @Loc(value = Location.TEST_5_COORDINATES),\n                            @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_4_COORDINATES),\n                            @Loc(value = Location.TEST_6_COORDINATES) }, tags = {\n                                    \"leisure=swimming_pool\", \"sport=swimming\" }),\n                    @Area(id = \"4554\", coordinates = { @Loc(value = Location.TEST_1_COORDINATES),\n                            @Loc(value = Location.TEST_2_COORDINATES),\n                            @Loc(value = Location.TEST_3_COORDINATES) }, tags = {\n                                    \"hello=world\" }) },\n\n            relations = {\n                    @Relation(id = \"1\", members = { @Member(id = \"9\", role = \"in\", type = \"edge\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\"),\n                            @Member(id = \"-9\", role = \"out\", type = \"edge\") }, tags = {}),\n                    @Relation(id = \"2\", members = {\n                            @Member(id = \"45\", role = \"area\", type = \"area\"),\n                            @Member(id = \"32\", role = \"line\", type = \"line\"),\n                            @Member(id = \"5\", role = \"pt\", type = \"point\"),\n                            @Member(id = \"1\", role = \"rel\", type = \"relation\"),\n                            @Member(id = \"1234\", role = \"node\", type = \"node\") }, tags = {}) })\n    private Atlas packedAtlas;\n\n    @TestAtlas(loadFromJosmOsmResource = \"addressAtlas.josm.osm\")\n    private Atlas addressAtlas;\n\n    @TestAtlas(loadFromJosmOsmResource = \"waterAtlas.josm.osm\")\n    private Atlas waterAtlas;\n\n    @TestAtlas(loadFromJosmOsmResource = \"ferryAtlas.josm.osm\")\n    private Atlas ferryAtlas;\n\n    @TestAtlas(loadFromJosmOsmResource = \"refsAtlas.josm.osm\")\n    private Atlas refsAtlas;\n\n    public Atlas getAddressAtlas()\n    {\n        return this.addressAtlas;\n    }\n\n    public Atlas getFerryAtlas()\n    {\n        return this.ferryAtlas;\n    }\n\n    public Atlas getPackedAtlas()\n    {\n        return this.packedAtlas;\n    }\n\n    public Atlas getRefsAtlas()\n    {\n        return this.refsAtlas;\n    }\n\n    public Atlas getWaterAtlas()\n    {\n        return this.waterAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/statistics/CounterTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.statistics.coverage.Coverage;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTileSharding;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * Test a subset of the counter metrics, to make sure that the logic is sane.\n *\n * @author matthieun\n */\npublic class CounterTest\n{\n    // private static final Logger logger = LoggerFactory.getLogger(CounterTest.class);\n\n    private static final Location LOCATION = Location.TEST_6;\n    private static final Polygon POLYGON = new Polygon(Location.TEST_1, Location.TEST_2,\n            Location.TEST_7);\n    private static final PolyLine POLY_LINE = new Segment(LOCATION, Location.TEST_1);\n\n    private Atlas atlas;\n    private Resource countsDefinition;\n\n    @Test\n    public void countsTest()\n    {\n        final Counter counter = new Counter();\n        counter.setCountsDefinition(this.countsDefinition);\n        final AtlasStatistics atlasStatistics = counter.processAtlas(this.atlas);\n        // logger.info(this.atlas.toString());\n        // logger.info(atlasStatistics.toString());\n        // Nodes\n        Assert.assertEquals(1,\n                atlasStatistics.get(Coverage.NULL_KEY, \"stop_signs\", \"true\").getCount(), 0.01);\n        Assert.assertEquals(1,\n                atlasStatistics.get(Coverage.NULL_KEY, \"stop_lights\", \"true\").getCount(), 0.01);\n\n        // Edges\n        Assert.assertEquals(POLY_LINE.length().asKilometers(),\n                atlasStatistics.get(\"secondary\", \"length_roads_named\", \"true\").getCount(), 0.01);\n        Assert.assertEquals(POLY_LINE.length().asKilometers(),\n                atlasStatistics.get(Coverage.AGGREGATE_KEY, \"length_total\", \"true\").getCount(),\n                0.01);\n\n        // Areas\n        Assert.assertEquals(POLYGON.surface().asKilometerSquared(),\n                atlasStatistics.get(Coverage.NULL_KEY, \"lakes_area\", \"true\").getCount(), 0.01);\n        Assert.assertEquals(POLYGON.surface().asKilometerSquared(),\n                atlasStatistics.get(Coverage.NULL_KEY, \"rivers_area\", \"true\").getCount(), 0.01);\n\n        // Lines\n        Assert.assertEquals(2 * POLY_LINE.length().asKilometers(),\n                atlasStatistics.get(Coverage.NULL_KEY, \"river_length\", \"true\").getCount(), 0.01);\n        Assert.assertEquals(POLY_LINE.length().asKilometers(),\n                atlasStatistics.get(Coverage.NULL_KEY, \"rail_length\", \"true\").getCount(), 0.01);\n\n        // Points\n        Assert.assertEquals(1, atlasStatistics.get(Coverage.NULL_KEY, \"shop\", \"true\").getCount(),\n                0.01);\n\n        // Relations\n        Assert.assertEquals(2 * POLY_LINE.length().asKilometers(),\n                atlasStatistics.get(Coverage.NULL_KEY, \"transit_bus_length\", \"true\").getCount(),\n                0.01);\n    }\n\n    @Test\n    public void countsWithShardBoundariesTest()\n    {\n        final Counter counter = new Counter();\n        counter.setCountsDefinition(this.countsDefinition);\n        counter.withSharding(new SlippyTileSharding(14));\n        final AtlasStatistics atlasStatistics = counter.processAtlas(this.atlas);\n        // logger.info(this.atlas.toString());\n        // logger.info(atlasStatistics.toString());\n        // Nodes\n        Assert.assertEquals(1,\n                atlasStatistics.get(Coverage.NULL_KEY, \"stop_signs\", \"true\").getCount(), 0.01);\n        Assert.assertEquals(1,\n                atlasStatistics.get(Coverage.NULL_KEY, \"stop_lights\", \"true\").getCount(), 0.01);\n\n        // Edges\n        Assert.assertEquals(POLY_LINE.length().asKilometers() / 3,\n                atlasStatistics.get(\"secondary\", \"length_roads_named\", \"true\").getCount(), 0.01);\n        Assert.assertEquals(POLY_LINE.length().asKilometers() / 3,\n                atlasStatistics.get(Coverage.AGGREGATE_KEY, \"length_total\", \"true\").getCount(),\n                0.01);\n\n        // Areas\n        Assert.assertEquals(POLYGON.surface().asKilometerSquared() / 3,\n                atlasStatistics.get(Coverage.NULL_KEY, \"lakes_area\", \"true\").getCount(), 0.01);\n        Assert.assertEquals(POLYGON.surface().asKilometerSquared() / 3,\n                atlasStatistics.get(Coverage.NULL_KEY, \"rivers_area\", \"true\").getCount(), 0.01);\n\n        // Lines\n        Assert.assertEquals(2 * POLY_LINE.length().asKilometers() / 3,\n                atlasStatistics.get(Coverage.NULL_KEY, \"river_length\", \"true\").getCount(), 0.01);\n        Assert.assertEquals(POLY_LINE.length().asKilometers() / 3,\n                atlasStatistics.get(Coverage.NULL_KEY, \"rail_length\", \"true\").getCount(), 0.01);\n\n        // Points\n        Assert.assertEquals(1, atlasStatistics.get(Coverage.NULL_KEY, \"shop\", \"true\").getCount(),\n                0.01);\n\n        // Relations\n        Assert.assertEquals(2 * POLY_LINE.length().asKilometers() / 3,\n                atlasStatistics.get(Coverage.NULL_KEY, \"transit_bus_length\", \"true\").getCount(),\n                0.01);\n    }\n\n    @Before\n    public void getAtlas()\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        // Nodes\n        builder.addNode(0, LOCATION, tags(\"highway=traffic_signals\"));\n        // Mimic the osm pbf reader, and add a point as well.\n        builder.addPoint(1, LOCATION, tags(\"highway=traffic_signals\"));\n        builder.addNode(1, Location.TEST_1, tags(\"highway=stop\"));\n        // Mimic the osm pbf reader, and add a point as well.\n        builder.addPoint(2, Location.TEST_1, tags(\"highway=stop\"));\n\n        // Edges\n        builder.addEdge(0, POLY_LINE, tags(\"highway=primary\", \"name=coco\"));\n        builder.addEdge(1, POLY_LINE, tags(\"highway=secondary\"));\n\n        // Areas\n        builder.addArea(0, POLYGON, tags(\"natural=water\", \"water=lake\"));\n        builder.addArea(1, POLYGON, tags(\"natural=water\", \"water=river\"));\n\n        // Lines\n        builder.addLine(0, POLY_LINE, tags(\"waterway=river\"));\n        builder.addLine(1, POLY_LINE, tags(\"natural=water\", \"water=canal\"));\n        builder.addLine(2, POLY_LINE, tags(\"natural=water\"));\n        builder.addLine(3, POLY_LINE, tags(\"railway=narrow_gauge\"));\n\n        // Points\n        builder.addPoint(0, LOCATION, tags(\"shop=some_shop\"));\n\n        // Relations\n\n        // 0\n        final RelationBean structure0 = new RelationBean();\n        structure0.addItem(1L, \"roadSegment\", ItemType.EDGE);\n        builder.addRelation(0, 0, structure0, tags(\"type=route\", \"route=road\", \"ref=HighwayOne\"));\n\n        // 1\n        final RelationBean structure1 = new RelationBean();\n        structure1.addItem(0L, \"roadSegment\", ItemType.EDGE);\n        structure1.addItem(1L, \"roadSegment\", ItemType.EDGE);\n        builder.addRelation(1, 1, structure1, tags(\"type=route\", \"route=bus\"));\n\n        this.atlas = builder.get();\n        this.countsDefinition = Counter.POI_COUNTS_DEFINITION.getDefault();\n    }\n\n    private Map<String, String> tags(final String... tags)\n    {\n        final Map<String, String> result = new HashMap<>();\n        for (final String tag : tags)\n        {\n            final StringList split = StringList.split(tag, \"=\");\n            result.put(split.get(0), split.get(1));\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/poi/CountCoverageTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.statistics.Counter;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class CountCoverageTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(CountCoverageTest.class);\n\n    @Rule\n    public final CountCoverageTestCaseRule rule = new CountCoverageTestCaseRule();\n\n    @Test\n    public void testPoiCounts()\n    {\n        testCount(\"address_housenumber;\", 3);\n        testCount(\"amenity;\", 3);\n        testCount(\"fixme;\", 3);\n        testCount(\"hospitals;\", 1);\n        testCount(\"lakes;\", 3);\n        testCount(\"rail_stops;\", 2);\n        testCount(\"airports;\", 1);\n    }\n\n    private void testCount(final String type, final int count)\n    {\n        final Atlas atlas = this.rule.getAtlas();\n        final List<SimpleCoverage<AtlasEntity>> coverages = new ArrayList<>();\n        SimpleCoverage.parseSimpleCoverages(atlas, Iterables.filter(\n                Counter.POI_COUNTS_DEFINITION.getDefault().lines(), line -> line.startsWith(type)))\n                .forEach(coverages::add);\n        final SimpleCoverage<AtlasEntity> result = coverages.get(0);\n        result.run();\n        logger.info(\"{}\", result.getStatistic());\n        Assert.assertEquals(count, result.getStatistic().values().iterator().next().getCount(),\n                0.01);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/statistics/coverage/poi/CountCoverageTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.statistics.coverage.poi;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * @author matthieun\n */\npublic class CountCoverageTestCaseRule extends CoreTestRule\n{\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String TWO = \"37.780592, -122.472242\";\n    private static final String THREE = \"37.780724, -122.472249\";\n\n    @TestAtlas(\n\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE))\n\n            }, edges = {\n\n                    @Edge(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"highway=trunk\", \"fixme=please\" }),\n                    @Edge(id = \"1\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" })\n\n            }, areas = {\n                    @Area(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"addr:housenumber=25\" }),\n                    @Area(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"natural=water\", \"water=lake\" }),\n                    @Area(id = \"2\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"natural=water\" }),\n                    @Area(id = \"3\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"amenity=hospital\" }),\n                    @Area(id = \"4\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"amenity=unknown\",\n                                    \"aeroway=aerodrome\" }),\n                    @Area(id = \"5\", coordinates = { @Loc(value = ONE), @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"addr:housenumber=30\",\n                                    \"addr:street=wardell\" })\n\n            }, lines = {\n\n                    @Line(id = \"0\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"railway=station\", \"FIXME=0\" }),\n                    @Line(id = \"1\", coordinates = { @Loc(value = ONE), @Loc(value = TWO) }, tags = {\n                            \"train=yes\", \"public_transport=stop_position\" })\n\n            }, points = {\n\n                    @Point(id = \"0\", coordinates = @Loc(value = ONE), tags = { \"addr:street=coco\",\n                            \"addr:housenumber=25\" }),\n                    @Point(id = \"1\", coordinates = @Loc(value = ONE), tags = {\n                            \"fixme=wrong name\" }),\n                    @Point(id = \"2\", coordinates = @Loc(value = ONE), tags = { \"landuse=basin\" }),\n                    @Point(id = \"3\", coordinates = @Loc(value = ONE), tags = { \"amenity=school\" })\n\n            }, relations = {\n\n                    @Relation(id = \"1\", tags = { \"type=restriction\",\n                            \"restriction=no_left_turn\" }, members = {\n                                    @Member(id = \"0\", role = \"from\", type = \"edge\"),\n                                    @Member(id = \"2\", role = \"via\", type = \"node\"),\n                                    @Member(id = \"1\", role = \"to\", type = \"edge\") })\n\n            })\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasEdgeValidatorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class AtlasEdgeValidatorTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testEdgeToNodeConnectivity()\n    {\n        final Atlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = 2810137078529598434L;\n\n            @Override\n            public Iterable<Edge> edges()\n            {\n                return Iterables.from(new CompleteEdge(123L, null, null, null, null, null)\n                {\n                    private static final long serialVersionUID = 8238381291474515199L;\n\n                    @Override\n                    public Node start()\n                    {\n                        return null;\n                    }\n                });\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"is logically disconnected at its start.\");\n\n        new AtlasEdgeValidator(atlas).validateEdgeToNodeConnectivity();\n    }\n\n    @Test\n    public void testEdgeToNodeLocationAccuracy()\n    {\n        final Atlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = -3111613839268225792L;\n\n            @Override\n            public Iterable<Edge> edges()\n            {\n                return Iterables.from(new CompleteEdge(123L,\n                        new PolyLine(Location.COLOSSEUM, Location.EIFFEL_TOWER), null, null, null,\n                        null)\n                {\n                    private static final long serialVersionUID = -1653164181164046228L;\n\n                    @Override\n                    public Node start()\n                    {\n                        return new CompleteNode(456L, Location.EIFFEL_TOWER, null, null, null,\n                                null);\n                    }\n                });\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"does not match with its start Node\");\n\n        new AtlasEdgeValidator(atlas).validateEdgeToNodeLocationAccuracy();\n\n        final Atlas atlas2 = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = -3557437996775535655L;\n\n            @Override\n            public Iterable<Edge> edges()\n            {\n                return Iterables.from(new CompleteEdge(123L,\n                        new PolyLine(Location.COLOSSEUM, Location.EIFFEL_TOWER), null, null, null,\n                        null)\n                {\n                    private static final long serialVersionUID = 2460452389716155618L;\n\n                    @Override\n                    public Node end()\n                    {\n                        return new CompleteNode(456L, Location.COLOSSEUM, null, null, null, null);\n                    }\n                });\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"does not match with its end Node\");\n\n        new AtlasEdgeValidator(atlas2).validateEdgeToNodeLocationAccuracy();\n    }\n\n    @Test\n    public void testReverseEdgePolyLineUpdated()\n    {\n        final Atlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = -1125897101453459977L;\n\n            @Override\n            public Edge edge(final long identifier)\n            {\n                return new CompleteEdge(-123L, null, null, null, null, null);\n            }\n\n            @Override\n            public Iterable<Edge> edges(final Predicate<Edge> matcher)\n            {\n                return Iterables.from(new CompleteEdge(123L,\n                        new PolyLine(Location.COLOSSEUM, Location.EIFFEL_TOWER), null, null, null,\n                        null)\n                {\n                    private static final long serialVersionUID = -9016561382807198568L;\n\n                    @Override\n                    public Optional<Edge> reversed()\n                    {\n                        return Optional.of(new CompleteEdge(-123L,\n                                new PolyLine(Location.COLOSSEUM, Location.EIFFEL_TOWER), null, null,\n                                null, null));\n                    }\n                });\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"have mismatching PolyLines: Forward =\");\n\n        new AtlasEdgeValidator(atlas).validateReverseEdgePolyLineUpdated();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasLineItemValidatorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteLine;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class AtlasLineItemValidatorTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testMissingPolyLineValidation()\n    {\n        final Atlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = 3421885788588156857L;\n\n            @Override\n            public Iterable<LineItem> lineItems()\n            {\n                return Iterables.from(new CompleteLine(123L, null, null, null));\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"is missing its PolyLine.\");\n\n        new AtlasLineItemValidator(atlas).validate();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasLocationItemValidatorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class AtlasLocationItemValidatorTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testLocationPresent()\n    {\n        final Atlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = -242183195939062159L;\n\n            @Override\n            public Iterable<LocationItem> locationItems()\n            {\n                return Iterables.from(new CompletePoint(123L, null, null, null));\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"is missing a Location.\");\n\n        new AtlasLocationItemValidator(atlas).validateLocationPresent();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasNodeValidatorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class AtlasNodeValidatorTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testNodeToEdgeConnectivity()\n    {\n        final Atlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = 5914210513631166207L;\n\n            @Override\n            public Iterable<Node> nodes()\n            {\n                return Iterables.from(new CompleteNode(123L, null, null, null, null, null)\n                {\n                    private static final long serialVersionUID = 8589044244340528526L;\n\n                    @Override\n                    public SortedSet<Edge> inEdges()\n                    {\n                        // Here supply the comparator so TreeSet accepts a null.\n                        final SortedSet<Edge> result = new TreeSet<>((edge1, edge2) -> 1);\n                        result.add(null);\n                        return result;\n                    }\n                });\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException\n                .expectMessage(\"is logically disconnected from some referenced in edge.\");\n\n        new AtlasNodeValidator(atlas).validateNodeToEdgeConnectivity();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasRelationValidatorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean.RelationBeanItem;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class AtlasRelationValidatorTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testAtlasRelationValidation()\n    {\n        // Make a fake BloatedAtlas which will surface up the only problem the Relation validator\n        // looks for.\n        final EmptyAtlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = -1162255036446588163L;\n\n            @Override\n            public AtlasEntity entity(final long identifier, final ItemType type)\n            {\n                // BloatedRelation uses this method when populating the member entities. Here it\n                // will trigger the BloatedPoint 456 to be null.\n                return null;\n            }\n\n            @Override\n            public Iterable<Relation> relations()\n            {\n                final RelationBean members = new RelationBean();\n                members.addItem(new RelationBeanItem(456L, \"myRole\", ItemType.POINT));\n                return Iterables.from(\n                        new CompleteRelation(123L, null, null, members, null, null, null, null));\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"specifies a member with role\");\n\n        new AtlasRelationValidator(atlas).validate();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/validators/AtlasValidatorTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.validators;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.EmptyAtlas;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class AtlasValidatorTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testParentRelations()\n    {\n        final EmptyAtlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = -1162255036446588163L;\n\n            @Override\n            public Iterable<AtlasEntity> entities()\n            {\n                return Iterables.from(new CompletePoint(456L, null, null, null)\n                {\n                    private static final long serialVersionUID = 3282284682633937718L;\n\n                    @Override\n                    public Set<Relation> relations()\n                    {\n                        final Set<Relation> result = new HashSet<>();\n                        result.add(null);\n                        return result;\n                    }\n                });\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"lists some parent relation that is not present\");\n\n        new AtlasValidator(atlas).validateRelationsPresentAndLinked();\n    }\n\n    @Test\n    public void testTagsPresent()\n    {\n        final EmptyAtlas atlas = new EmptyAtlas()\n        {\n            private static final long serialVersionUID = -2478701330988023398L;\n\n            @Override\n            public Iterable<AtlasEntity> entities()\n            {\n                return Iterables.from(new CompletePoint(456L, null, null, null)\n                {\n                    private static final long serialVersionUID = -3850622600530001863L;\n\n                    @Override\n                    public Map<String, String> getTags()\n                    {\n                        return null;\n                    }\n                });\n            }\n        };\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"is missing tags.\");\n\n        new AtlasValidator(atlas).validateTagsPresent();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/walker/OsmWayWalkerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.walker;\n\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasObject;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.walker.EdgeWalker.EdgeHandler;\n\n/**\n * @author brian_l_davis\n */\npublic class OsmWayWalkerTest\n{\n    /**\n     * @author brian_l_davis\n     */\n    public static class MockEdgeHandler implements EdgeHandler\n    {\n        private long edges = 0;\n        private long boundaries = 0;\n\n        public long getBoundaries()\n        {\n            return this.boundaries;\n        }\n\n        public long getEdges()\n        {\n            return this.edges;\n        }\n\n        @Override\n        public void handleBoundaryEdge(final Edge edge)\n        {\n            this.boundaries++;\n        }\n\n        @Override\n        public void handleEdge(final Edge edge)\n        {\n            this.edges++;\n        }\n    }\n\n    @Rule\n    public final OsmWayWalkerTestRule setup = new OsmWayWalkerTestRule();\n\n    @Test\n    public void testCircularPath()\n    {\n        final Edge startingEdge = this.setup.getRoundAbout().edge(169884263000003L);\n        final Set<Edge> way = new OsmWayWalker(startingEdge).collectEdges();\n\n        Assert.assertEquals(\"Wrong number of edges found\", 4, way.size());\n        Assert.assertArrayEquals(way.stream().map(AtlasObject::getIdentifier).toArray(),\n                new Long[] { 169884263000001L, 169884263000002L, 169884263000003L,\n                        169884263000004L });\n    }\n\n    @Test\n    public void testNonWaySectioned()\n    {\n        final Edge startingEdge = this.setup.getSimpleNetwork().edge(30647522000000L);\n        final Set<Edge> way = new OsmWayWalker(startingEdge).collectEdges();\n\n        Assert.assertEquals(\"Wrong number of edges found\", 1, way.size());\n        Assert.assertArrayEquals(way.stream().map(AtlasObject::getIdentifier).toArray(),\n                new Long[] { 30647522000000L });\n    }\n\n    @Test\n    public void testStatisticsBeginningReverseStart()\n    {\n        final MockEdgeHandler statisticsHandler = new MockEdgeHandler();\n\n        final Edge startingEdge = this.setup.getIsolatedEdge().edge(-1000001L);\n        new OsmWayWalker(startingEdge, statisticsHandler).collectEdges();\n\n        Assert.assertEquals(\"Wrong number of edges counted\", 4, statisticsHandler.getEdges());\n        Assert.assertEquals(\"Wrong number of boundaries counted\", 2,\n                statisticsHandler.getBoundaries());\n    }\n\n    @Test\n    public void testStatisticsBeginningStart()\n    {\n        final MockEdgeHandler statisticsHandler = new MockEdgeHandler();\n\n        final Edge startingEdge = this.setup.getIsolatedEdge().edge(1000001L);\n        new OsmWayWalker(startingEdge, statisticsHandler).collectEdges();\n\n        Assert.assertEquals(\"Wrong number of edges counted\", 4, statisticsHandler.getEdges());\n        Assert.assertEquals(\"Wrong number of boundaries counted\", 2,\n                statisticsHandler.getBoundaries());\n    }\n\n    @Test\n    public void testStatisticsEndStart()\n    {\n        final MockEdgeHandler statisticsHandler = new MockEdgeHandler();\n\n        final Edge startingEdge = this.setup.getIsolatedEdge().edge(1000004L);\n        new OsmWayWalker(startingEdge, statisticsHandler).collectEdges();\n\n        Assert.assertEquals(\"Wrong number of edges counted\", 4, statisticsHandler.getEdges());\n        Assert.assertEquals(\"Wrong number of boundaries counted\", 2,\n                statisticsHandler.getBoundaries());\n    }\n\n    @Test\n    public void testStatisticsMiddleStart()\n    {\n        final MockEdgeHandler statisticsHandler = new MockEdgeHandler();\n\n        final Edge startingEdge = this.setup.getIsolatedEdge().edge(1000003L);\n        new OsmWayWalker(startingEdge, statisticsHandler).collectEdges();\n\n        Assert.assertEquals(\"Wrong number of edges counted\", 4, statisticsHandler.getEdges());\n        Assert.assertEquals(\"Wrong number of boundaries counted\", 2,\n                statisticsHandler.getBoundaries());\n    }\n\n    @Test\n    public void testWaySectioned()\n    {\n        final Edge startingEdge = this.setup.getSimpleNetwork().edge(30647513000003L);\n        final Set<Edge> way = new OsmWayWalker(startingEdge).collectEdges();\n\n        Assert.assertEquals(\"Wrong number of edges found\", 5, way.size());\n        Assert.assertArrayEquals(way.stream().map(AtlasObject::getIdentifier).toArray(),\n                new Long[] { 30647513000001L, 30647513000002L, 30647513000003L, 30647513000004L,\n                        30647513000005L });\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/walker/OsmWayWalkerTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.walker;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\n\n/**\n * @author brian_l_davis\n */\npublic class OsmWayWalkerTestRule extends CoreTestRule\n{\n    private static final String FOUR = \"37.780744, -122.471797\";\n    private static final String ONE = \"37.780574, -122.472852\";\n    private static final String THREE = \"37.780572, -122.472846\";\n    private static final String TWO = \"37.780724, -122.472249\";\n    private static final String FIVE = \"37.780724, -112.472249\";\n\n    @TestAtlas(loadFromTextResource = \"OsmWayWalker-Way30647513.atlas.txt\")\n    private Atlas simpleNetwork;\n\n    @TestAtlas(loadFromTextResource = \"OsmWayWalker-Way169884263.atlas.txt\")\n    private Atlas roundAbout;\n\n    @TestAtlas(\n\n            nodes = { @TestAtlas.Node(id = \"101740465\", coordinates = @Loc(value = ONE)),\n                    @TestAtlas.Node(id = \"102740465\", coordinates = @Loc(value = TWO)),\n                    @TestAtlas.Node(id = \"103740465\", coordinates = @Loc(value = THREE)),\n                    @TestAtlas.Node(id = \"104740465\", coordinates = @Loc(value = FOUR)),\n                    @TestAtlas.Node(id = \"105740465\", coordinates = @Loc(value = FIVE)) },\n\n            edges = {\n                    @Edge(id = \"1000001\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"-1000001\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"1000002\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"1000003\", coordinates = { @Loc(value = THREE),\n                            @Loc(value = FOUR) }, tags = { \"highway=secondary\" }),\n                    @Edge(id = \"1000004\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = FIVE) }, tags = { \"highway=secondary\" }) })\n    private Atlas isolatedEdge;\n\n    public Atlas getIsolatedEdge()\n    {\n        return this.isolatedEdge;\n    }\n\n    public Atlas getRoundAbout()\n    {\n        return this.roundAbout;\n    }\n\n    public Atlas getSimpleNetwork()\n    {\n        return this.simpleNetwork;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/walker/SimpleEdgeWalkerTest.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.walker;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Unit tests for {@link SimpleEdgeWalker}.\n *\n * @author bbreithaupt\n */\npublic class SimpleEdgeWalkerTest\n{\n    private static final long FIRST_EDGE_IDENTIFIER = 1000000001L;\n    @Rule\n    public SimpleEdgeWalkerTestRule setup = new SimpleEdgeWalkerTestRule();\n\n    @Test\n    public void collectAllTest()\n    {\n        Assert.assertEquals(3,\n                new SimpleEdgeWalker(\n                        this.setup.motorwayPrimaryTriangleAtlas().edge(FIRST_EDGE_IDENTIFIER),\n                        edge -> edge.outEdges().stream()).collectEdges().size());\n    }\n\n    @Test\n    public void collectPrimaryQueueAllTest()\n    {\n        Assert.assertEquals(2,\n                new SimpleEdgeWalker(\n                        this.setup.motorwayPrimaryTriangleAtlas().edge(FIRST_EDGE_IDENTIFIER),\n                        connectedEdge -> Validators.isOfType(connectedEdge, HighwayTag.class,\n                                HighwayTag.PRIMARY),\n                        edge -> edge.outEdges().stream()).collectEdges().size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/atlas/walker/SimpleEdgeWalkerTestRule.java",
    "content": "package org.openstreetmap.atlas.geography.atlas.walker;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Unit test rule for {@link SimpleEdgeWalker}.\n *\n * @author bbreithaupt\n */\npublic class SimpleEdgeWalkerTestRule extends CoreTestRule\n{\n    private static final String TEST_1 = \"47.9870531970049,-122.885726828789\";\n    private static final String TEST_2 = \"47.9879410278085,-122.885085090084\";\n    private static final String TEST_3 = \"47.9877865096808,-122.886904654096\";\n\n    @TestAtlas(\n            // nodes\n            nodes = { @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = TEST_1)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = TEST_2)),\n                    @TestAtlas.Node(coordinates = @TestAtlas.Loc(value = TEST_3)) },\n            // edges\n            edges = {\n                    @TestAtlas.Edge(id = \"1000000001\", coordinates = {\n                            @TestAtlas.Loc(value = TEST_1),\n                            @TestAtlas.Loc(value = TEST_2) }, tags = { \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1001000001\", coordinates = {\n                            @TestAtlas.Loc(value = TEST_2),\n                            @TestAtlas.Loc(value = TEST_3) }, tags = { \"highway=primary\" }),\n                    @TestAtlas.Edge(id = \"1002000001\", coordinates = {\n                            @TestAtlas.Loc(value = TEST_3),\n                            @TestAtlas.Loc(value = TEST_1) }, tags = { \"highway=motorway\" }) })\n    private Atlas motorwayPrimaryTriangleAtlas;\n\n    public Atlas motorwayPrimaryTriangleAtlas()\n    {\n        return this.motorwayPrimaryTriangleAtlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/boundary/CountryBoundaryMapTest.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.geom.Polygon;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.text.TextAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.geography.atlas.pbf.AtlasLoadingOption;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.CountryCodeProperties;\nimport org.openstreetmap.atlas.geography.atlas.raw.slicing.RawAtlasSlicer;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPointConverter;\nimport org.openstreetmap.atlas.geography.converters.jts.JtsPolygonToMultiPolygonConverter;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.test.TestUtility;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Tests for {@link CountryBoundaryMap}.\n *\n * @author tony\n * @author Yiqing Jin\n * @author mgostintsev\n */\npublic class CountryBoundaryMapTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(CountryBoundaryMapTest.class);\n    private static final JtsPointConverter JTS_POINT_CONVERTER = new JtsPointConverter();\n\n    @Test\n    public void testAntiMeridian()\n    {\n        final CountryBoundaryMap map = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryBoundaryMapTest.class\n                        .getResourceAsStream(\"HTI_DOM_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n        final LineString lineString = (LineString) TestUtility\n                .createJtsGeometryFromWKT(\"LINESTRING ( -179 18.84927, 179 18.84927 )\");\n\n        // HTI is the closest to the line\n        Assert.assertEquals(\"HTI,DOM\", map.getCountryCodeISO3(lineString).getIso3CountryCode());\n    }\n\n    @Test\n    public void testBorderDeduplication()\n    {\n        final InputStreamResource atlasResource = new InputStreamResource(\n                () -> CountryBoundaryMapTest.class\n                        .getResourceAsStream(\"USA_HTI_overlapping.atlas.txt\"));\n        final Atlas atlas = new TextAtlasBuilder().read(atlasResource);\n        final CountryBoundaryMap map = CountryBoundaryMap.fromAtlas(atlas);\n        final StringResource boundaryHTI = new StringResource(new InputStreamResource(\n                () -> CountryBoundaryMapTest.class.getResourceAsStream(\"HTI_boundary.txt\")));\n        final StringResource boundaryUSA = new StringResource(\n                new InputStreamResource(() -> CountryBoundaryMapTest.class\n                        .getResourceAsStream(\"USA_boundary_reduced.txt\")));\n        final JtsPolygonToMultiPolygonConverter converter = new JtsPolygonToMultiPolygonConverter();\n        // confirm the duplicated border belongs to the USA\n        Assert.assertTrue(MultiPolygon.wkt(boundaryUSA.all())\n                .isSimilarTo(converter.convert(map.countryBoundary(\"USA\").get(0))));\n        // confirm the duplicated border does not belong to HTI\n        Assert.assertTrue(MultiPolygon.wkt(boundaryHTI.all())\n                .isSimilarTo(converter.convert(map.countryBoundary(\"HTI\").get(0))));\n    }\n\n    @Test\n    public void testBoundaryLoading() throws ParseException\n    {\n        final CountryBoundaryMap map = CountryBoundaryMap.fromPlainText(new InputStreamResource(\n                () -> CountryBoundaryMapTest.class.getResourceAsStream(\"CIV_osm_boundaries.txt.gz\"))\n                .withDecompressor(Decompressor.GZIP));\n        Assert.assertEquals(\"CIV\", firstCountryName(map));\n\n        final Location locationInsideInner1 = Location.forString(\"4.5847047, -7.573053\");\n        final Location locationInsideInner2 = Location.forString(\"4.5828105, -7.572514\");\n        final PolyLine polyLineInsideInner = new PolyLine(locationInsideInner1,\n                locationInsideInner2);\n        Assert.assertEquals(1, map.boundaries(locationInsideInner1).size());\n        Assert.assertEquals(1, map.boundaries(polyLineInsideInner).size());\n\n        final Location locationBetweenInnerAndOuter1 = Location.forString(\"4.5842728, -7.57121\");\n        final Location locationBetweenInnerAndOuter2 = Location.forString(\"4.5848686, -7.567427\");\n        final PolyLine polyLineAcrossInner = new PolyLine(locationInsideInner1,\n                locationBetweenInnerAndOuter1);\n        final PolyLine polyLineBetweenInnerAndOuter = new PolyLine(locationBetweenInnerAndOuter2,\n                locationBetweenInnerAndOuter1);\n        Assert.assertEquals(1, map.boundaries(locationBetweenInnerAndOuter1).size());\n        Assert.assertEquals(1, map.boundaries(polyLineAcrossInner).size());\n        Assert.assertEquals(1, map.boundaries(polyLineBetweenInnerAndOuter).size());\n\n        final Location locationOutsideOuter1 = Location.forString(\"4.6002906, -7.5696683\");\n        final Location locationOutsideOuter2 = Location.forString(\"4.610689, -7.5605944\");\n        final PolyLine polyLineAcrossOuter = new PolyLine(locationOutsideOuter1,\n                locationBetweenInnerAndOuter1);\n        final PolyLine polyLineOuter = new PolyLine(locationOutsideOuter1, locationOutsideOuter2);\n        Assert.assertEquals(0, map.boundaries(locationOutsideOuter1).size());\n        Assert.assertEquals(0, map.boundaries(locationOutsideOuter2).size());\n        Assert.assertEquals(1, map.boundaries(polyLineAcrossOuter).size());\n        Assert.assertEquals(0, map.boundaries(polyLineOuter).size());\n    }\n\n    @Test\n    public void testExtensionBoundariesFilter()\n    {\n        final CountryBoundaryMap map = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryBoundaryMapTest.class\n                        .getResourceAsStream(\"MAF_AIA_osm_boundaries_with_grid_index.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n\n        final PolyLine line = PolyLine.wkt(\n                \"LINESTRING(-63.069960775034794 18.20724437315409,-63.056442441599735 18.203616100626693,-63.058416547434696 18.211076399156397)\");\n\n        final MultiMap<String, Polygon> boundaries = map.boundaries(line);\n        Assert.assertEquals(1, boundaries.size());\n\n        Assert.assertEquals(\"POLYGON ((-62.76312 18.1617887\",\n                boundaries.get(\"AIA\").get(0).toText().substring(0, 30));\n    }\n\n    @Test\n    public void testFeatureCrossingCountryBoundary() throws ParseException\n    {\n        final CountryBoundaryMap map = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryBoundaryMapTest.class\n                        .getResourceAsStream(\"HTI_DOM_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n\n        final WKTReader reader = new WKTReader();\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        final org.openstreetmap.atlas.geography.Polygon geometry = new org.openstreetmap.atlas.geography.Polygon(\n                PolyLine.wkt(\n                        \"LINESTRING ( -71.7424191 18.7499411097, -71.730485136 18.749848501, -71.730081575 18.749979671, -71.730142154 18.749575218, -71.730486015 18.7498444, -71.7424191 18.7499411097 )\"));\n        builder.addArea(1L, geometry, new HashMap<String, String>());\n        final Atlas rawAtlas = builder.get();\n        final Atlas slicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(map), rawAtlas).slice();\n        Assert.assertEquals(2, slicedAtlas.numberOfAreas());\n    }\n\n    @Test\n    public void testFeatureRightByCountryBoundary() throws ParseException\n    {\n        // Work on HTI and DOM\n        final Set<String> countries = new HashSet<>();\n        countries.add(\"HTI\");\n        countries.add(\"DOM\");\n\n        // Initialize grid index\n        final CountryBoundaryMap map = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryBoundaryMapTest.class\n                        .getResourceAsStream(\"HTI_DOM_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n\n        // Slice a line along the border\n        final PolyLine geometry = PolyLine.wkt(\n                \"LINESTRING(-71.71119689941406 19.465297438875965,-71.70982360839844 19.425153718960143,-71.72767639160156 19.390181749736552,-71.77093505859375 19.363623938901224,-71.8121337890625 19.32280716454424,-71.78123474121094 19.296886457967965,-71.74896240234375 19.250218840825706,-71.70433044433594 19.22428664772902,-71.66038513183594 19.21391262405755,-71.66862487792969 19.176301302579176,-71.67755126953125 19.143870855908183,-71.73660278320312 19.117921909279115,-71.75033569335938 19.07509724212452,-71.81625366210938 19.03161239237521,-71.88217163085938 19.003048981647012,-71.91925048828125 18.95370063230706,-71.89521789550781 18.923175265301367,-71.80938720703125 18.923175265301367,-71.73934936523438 18.938113908068473,-71.66107177734375 18.94850521929427,-71.60957336425781 18.910184055628548,-71.61026000976562 18.86405711499645,-71.6195297241211 18.813042837757894,-71.64630889892578 18.78249184724649,-71.7242431640625 18.77371553802311,-71.78054809570312 18.745108099985455,-71.83959960937499 18.683975975631473,-71.87118530273438 18.6592567227563)\");\n\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addLine(1000000L, geometry, new HashMap<String, String>());\n        builder.addLine(2000000L, geometry.reversed(), new HashMap<String, String>());\n        final Atlas rawAtlas = builder.get();\n\n        final Atlas slicedAtlas = new RawAtlasSlicer(\n                AtlasLoadingOption.createOptionWithAllEnabled(map), rawAtlas).slice();\n        Assert.assertEquals(6, slicedAtlas.numberOfLines());\n\n        // First piece should be in DOM and rest should be in HTI\n        Assert.assertEquals(\"DOM\", slicedAtlas.line(1001000L).getTag(ISOCountryTag.KEY).get());\n\n        slicedAtlas.lines(\n                line -> line.getOsmIdentifier() == 1000000L && line.getIdentifier() != 1001000L)\n                .forEach(line -> Assert.assertEquals(\"HTI\", line.getTag(ISOCountryTag.KEY).get()));\n\n        // Reverse the line and slice again\n        // Again first piece should be in DOM and rest should be in HTI\n        Assert.assertEquals(\"DOM\", slicedAtlas.line(2001000L).getTag(ISOCountryTag.KEY).get());\n        slicedAtlas.lines(\n                line -> line.getOsmIdentifier() == 2000000L && line.getIdentifier() != 2001000L)\n                .forEach(line -> Assert.assertEquals(\"HTI\", line.getTag(ISOCountryTag.KEY).get()));\n\n        // Returned pieces should be reverse version of each other\n        // First pieces are from DOM, they should have reverse geometry\n        Assert.assertEquals(slicedAtlas.line(1001000L).asPolyLine(),\n                slicedAtlas.line(2001000L).asPolyLine().reversed());\n\n        Assert.assertEquals(slicedAtlas.line(1003000L).asPolyLine(),\n                slicedAtlas.line(2003000L).asPolyLine().reversed());\n        Assert.assertEquals(slicedAtlas.line(1002000L).asPolyLine(),\n                slicedAtlas.line(2002000L).asPolyLine().reversed());\n    }\n\n    @Test\n    public void testGetCountryCode()\n    {\n        final CountryBoundaryMap map = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryBoundaryMapTest.class\n                        .getResourceAsStream(\"HTI_DOM_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n\n        Point point = JTS_POINT_CONVERTER\n                .convert(Location.forString(\"19.068387997775737, -71.7029007844633\"));\n        CountryCodeProperties countryDetails = map.getCountryCodeISO3(point);\n        Assert.assertEquals(\"DOM\", countryDetails.getIso3CountryCode());\n\n        point = JTS_POINT_CONVERTER\n                .convert(Location.forString(\"19.069172931560374, -71.70712929872246\"));\n        countryDetails = map.getCountryCodeISO3(point);\n        Assert.assertEquals(\"HTI\", countryDetails.getIso3CountryCode());\n\n        point = JTS_POINT_CONVERTER.convert(Location.forString(\"19.0681781, -71.7075623\"));\n        countryDetails = map.getCountryCodeISO3(point);\n        Assert.assertEquals(\"HTI,DOM\", countryDetails.getIso3CountryCode());\n    }\n\n    private String firstCountryName(final CountryBoundaryMap map)\n    {\n        return map.boundaries(Rectangle.MAXIMUM).keySet().iterator().next();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/boundary/CountryShardListingTest.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.sharding.CountryShard;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.maps.MultiMapWithSet;\n\n/**\n * @author matthieun\n */\npublic class CountryShardListingTest\n{\n    @Test\n    public void testMultiPolygonBoundary()\n    {\n        final StringList countries = new StringList();\n        countries.add(\"ZAF\");\n        final CountryBoundaryMap boundaries = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryShardListingTest.class\n                        .getResourceAsStream(\"ZAF_osm_boundary.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n        final Sharding sharding = Sharding.forString(\"dynamic@\"\n                + CountryShardListingTest.class.getResource(\"tree-6-14-100000.txt.gz\").getPath());\n        final MultiMapWithSet<String, Shard> shardSetPerCountry = CountryShardListing\n                .countryToShardList(countries, boundaries, sharding);\n        final Resource expectedShardList = new InputStreamResource(\n                () -> CountryShardListingTest.class\n                        .getResourceAsStream(\"ZAF_osm_shards_with_14.txt\"));\n        Assert.assertEquals(Iterables.size(expectedShardList.lines()),\n                shardSetPerCountry.get(countries.get(0)).size());\n        expectedShardList.lines().forEach(line ->\n        {\n            final CountryShard expected = CountryShard.forName(line);\n            Assert.assertTrue(\n                    shardSetPerCountry.get(countries.get(0)).contains(expected.getShard()));\n        });\n    }\n\n    @Test\n    public void testSmallCountry()\n    {\n        final StringList countries = new StringList();\n        countries.add(\"DMA\");\n        final CountryBoundaryMap boundaries = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryShardListingTest.class\n                        .getResourceAsStream(\"DMA_as_world_osm_boundaries.txt.gz\"))\n                        .withDecompressor(Decompressor.GZIP));\n        final Sharding sharding = Sharding.forString(\"dynamic@\"\n                + CountryShardListingTest.class.getResource(\"tree-6-13-100000.txt.gz\").getPath());\n        final MultiMapWithSet<String, Shard> shardSetPerCountry = CountryShardListing\n                .countryToShardList(countries, boundaries, sharding);\n        Assert.assertEquals(4, shardSetPerCountry.get(countries.get(0)).size());\n        StringList.split(\"DMA_9-168-233,DMA_9-169-233,DMA_9-168-234,DMA_10-338-468\", \",\")\n                .forEach(line ->\n                {\n                    final CountryShard expected = CountryShard.forName(line);\n                    Assert.assertTrue(\n                            shardSetPerCountry.get(countries.get(0)).contains(expected.getShard()));\n                });\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/boundary/CountryToShardListCacheTest.java",
    "content": "package org.openstreetmap.atlas.geography.boundary;\n\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.Sharding;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\n\n/**\n * @author james-gage\n */\npublic class CountryToShardListCacheTest\n{\n    private final Resource countryToShardList = new InputStreamResource(\n            () -> CountryToShardListCacheTest.class.getResourceAsStream(\"countryToShardList.txt\"));\n\n    @Test\n    public void testGetShardNamesForCountry()\n    {\n        final CountryToShardListCache cache = new CountryToShardListCache(this.countryToShardList);\n        // test that the right DMA shards are returned\n        final List<Shard> dMAShards = cache.getShardsForCountry(\"DMA\");\n        Assert.assertEquals(\n                \"[[SlippyTile: zoom = 9, x = 168, y = 233], [SlippyTile: zoom = 9, x = 169, y = 233], \"\n                        + \"[SlippyTile: zoom = 9, x = 168, y = 234], [SlippyTile: zoom = 10, x = 338, y = 468]]\",\n                dMAShards.toString());\n        // test that asking for an invalid country code doesn't break anything\n        final List<Shard> noShards = cache.getShardsForCountry(\"XXX\");\n        Assert.assertTrue(noShards.isEmpty());\n    }\n\n    @Test\n    public void testSaveWhenBuildingFromFile()\n    {\n        final CountryToShardListCache cache = new CountryToShardListCache(this.countryToShardList);\n        // test that you get the same file back that was used to initialize the cache\n        final ByteArrayResource output = new ByteArrayResource();\n        cache.save(output);\n        Assert.assertEquals(this.countryToShardList.all(), output.all());\n    }\n\n    @Test\n    public void testSaveWhenBuildingFromScratch()\n    {\n        final StringList countries = new StringList();\n        countries.add(\"DMA\");\n        final CountryBoundaryMap boundaries = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryToShardListCacheTest.class\n                        .getResourceAsStream(\"DMA_boundary.txt\")));\n        final Sharding sharding = Sharding.forString(\"dynamic@\" + CountryToShardListCacheTest.class\n                .getResource(\"tree-6-14-100000.txt.gz\").getPath());\n        // test building the cache from scratch\n        final CountryToShardListCache cache = new CountryToShardListCache(boundaries, countries,\n                sharding);\n        final ByteArrayResource output = new ByteArrayResource();\n        cache.save(output);\n        Assert.assertEquals(\"DMA||9-168-233, 9-169-233, 9-168-234, 10-338-468\", output.all());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/boundary/converters/CountryBoundaryMapGeoJsonConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.boundary.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;\nimport org.openstreetmap.atlas.geography.boundary.CountryBoundaryMapTest;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\n\n/**\n * @author lcram\n */\npublic class CountryBoundaryMapGeoJsonConverterTest\n{\n    @Test\n    public void testConvertToString()\n    {\n        final String expected = new StringResource(() -> CountryBoundaryMapTest.class\n                .getResourceAsStream(\"AAA_boundary.expected.json\")).all();\n        final CountryBoundaryMap mapWithGridIndex = CountryBoundaryMap\n                .fromPlainText(new InputStreamResource(() -> CountryBoundaryMapTest.class\n                        .getResourceAsStream(\"AAA_boundary.txt\")));\n        final String jsonMap = new CountryBoundaryMapGeoJsonConverter().prettyPrint(true)\n                .convertToString(mapWithGridIndex);\n        Assert.assertEquals(expected, jsonMap);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/clipping/GeometryOperationTest.java",
    "content": "package org.openstreetmap.atlas.geography.clipping;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.GeometricObject;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.MultiPolygonTest;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\n\n/**\n * @author matthieun\n */\npublic class GeometryOperationTest\n{\n    private final MultiPolygon multiPolygon1 = MultiPolygon.forOuters(Polygon.wkt(\n            \"POLYGON ((55.4529644 -4.6425404, 55.4527599 -4.6423107, 55.4524513 -4.6423687, 55.452295 -4.6424066,\"\n                    + \" 55.4521828 -4.6425984, 55.4519884 -4.642868, 55.4520144 -4.6430778, 55.4522369 -4.6431817,\"\n                    + \" 55.4525876 -4.6431737, 55.4528842 -4.6430798, 55.4530065 -4.6429939, 55.4528762 -4.6428181,\"\n                    + \" 55.4528862 -4.6426603, 55.4529644 -4.6425404))\"));\n    private final MultiPolygon multiPolygon2 = MultiPolygon.forOuters(Polygon.wkt(\n            \"POLYGON ((55.4704179 -4.6550696, 55.4722205 -4.6581544, 55.4702705 -4.6603804, 55.467663 -4.6590245,\"\n                    + \" 55.4665973 -4.6614991, 55.4665025 -4.6616483, 55.466354 -4.6617547, 55.465372 -4.662019,\"\n                    + \" 55.4643567 -4.6610388, 55.4631883 -4.6606733, 55.4629301 -4.6609095, 55.4626936 -4.6606842,\"\n                    + \" 55.4627858 -4.659018, 55.463193 -4.6585292, 55.4628348 -4.65804, 55.4629354 -4.656064,\"\n                    + \" 55.4621532 -4.654595, 55.4659511 -4.6540752, 55.4661665 -4.6560414, 55.46891 -4.6564482,\"\n                    + \" 55.4691254 -4.6553182, 55.4684792 -4.6544368, 55.4694089 -4.6539848, 55.4704179 -4.6550696))\"));\n    private final MultiPolygon multiPolygon3 = MultiPolygon.forOuters(Polygon.wkt(\n            \"POLYGON ((55.4203385 -4.6274556, 55.4209807 -4.6276984, 55.4211268 -4.6274921, 55.4213636 -4.6271103,\"\n                    + \" 55.4213314 -4.6269499, 55.4209505 -4.6268376, 55.4207682 -4.6270421, 55.4203122 -4.6268858,\"\n                    + \" 55.4203712 -4.6265181, 55.4201352 -4.6261051, 55.4203658 -4.6257736, 55.4200493 -4.6253365,\"\n                    + \" 55.419411 -4.6250624, 55.4188101 -4.6245759, 55.417807 -4.6243847, 55.4172652 -4.6247896,\"\n                    + \" 55.4174315 -4.62495, 55.41844 -4.626383, 55.4192661 -4.6270567, 55.4203385 -4.6274556))\"));\n    private final MultiPolygon multiPolygon4 = MultiPolygon.forOuters(Polygon.wkt(\n            \"POLYGON ((55.4203385 -4.6274556, 55.4199796 -4.6284897, 55.419346 -4.6289794, 55.4195558 -4.6294307,\"\n                    + \" 55.4201512 -4.629666, 55.4204624 -4.6293345, 55.4209344 -4.6283079, 55.4206769 -4.6281207,\"\n                    + \" 55.4209807 -4.6276984, 55.4203385 -4.6274556))\"));\n    private final MultiPolygon multiPolygon5 = MultiPolygon.forOuters(Polygon.wkt(\n            \"POLYGON ((55.455052 -4.6419746, 55.4550627 -4.6425627, 55.4547516 -4.6426162, 55.4547355 -4.6429263,\"\n                    + \" 55.4542978 -4.6429263, 55.4534019 -4.6427926, 55.4530065 -4.6429939, 55.4528762 -4.6428181,\"\n                    + \" 55.4528862 -4.6426603, 55.4529644 -4.6425404, 55.4527599 -4.6423107, 55.4524513 -4.6423687,\"\n                    + \" 55.4521123 -4.6420173, 55.4518462 -4.6417019, 55.4515222 -4.6414934, 55.4516639 -4.6412474,\"\n                    + \" 55.4524503 -4.6415682, 55.4530565 -4.6416484, 55.4534824 -4.6417286, 55.4531906 -4.6421243,\"\n                    + \" 55.4532925 -4.642429, 55.4546121 -4.6423007, 55.4546357 -4.6419264, 55.455052 -4.6419746))\"));\n\n    @Test\n    public void testHuggingPolygons()\n    {\n        final MultiPolygon multiPolygon = MultiPolygonTest.getFrom(\"testHuggingPolygons.josm.osm\",\n                GeometryOperationTest.class);\n        final Optional<GeometricObject> resultOption = GeometryOperation\n                .intersection(multiPolygon.outers());\n        Assert.assertTrue(resultOption.isPresent());\n        final GeometricObject result = resultOption.get();\n        Assert.assertTrue(result instanceof PolyLine && !(result instanceof Polygon));\n        Assert.assertEquals(\"LINESTRING (5.9349989 43.102859, 5.935294 43.1015547)\",\n                ((PolyLine) result).toWkt());\n    }\n\n    @Test\n    public void testMultiplePolygons()\n    {\n        final GeometricSurface result = GeometryOperation\n                .union(this.multiPolygon1, this.multiPolygon2, this.multiPolygon3,\n                        this.multiPolygon4, this.multiPolygon5)\n                .orElseThrow(() -> new CoreException(\"fail\"));\n        Assert.assertEquals(\n                \"MULTIPOLYGON (((55.455052 -4.6419746, 55.4550627 -4.6425627, 55.4547516 -4.6426162, 55.4547355 -4.6429263,\"\n                        + \" 55.4542978 -4.6429263, 55.4534019 -4.6427926, 55.4530065 -4.6429939, 55.4528842 -4.6430798,\"\n                        + \" 55.4525876 -4.6431737, 55.4522369 -4.6431817, 55.4520144 -4.6430778, 55.4519884 -4.642868,\"\n                        + \" 55.4521828 -4.6425984, 55.452295 -4.6424066, 55.4524513 -4.6423687, 55.4521123 -4.6420173,\"\n                        + \" 55.4518462 -4.6417019, 55.4515222 -4.6414934, 55.4516639 -4.6412474, 55.4524503 -4.6415682,\"\n                        + \" 55.4530565 -4.6416484, 55.4534824 -4.6417286, 55.4531906 -4.6421243, 55.4532925 -4.642429,\"\n                        + \" 55.4546121 -4.6423007, 55.4546357 -4.6419264, 55.455052 -4.6419746)),\"\n                        + \" ((55.4704179 -4.6550696, 55.4722205 -4.6581544, 55.4702705 -4.6603804, 55.467663 -4.6590245,\"\n                        + \" 55.4665973 -4.6614991, 55.4665025 -4.6616483, 55.466354 -4.6617547, 55.465372 -4.662019,\"\n                        + \" 55.4643567 -4.6610388, 55.4631883 -4.6606733, 55.4629301 -4.6609095, 55.4626936 -4.6606842,\"\n                        + \" 55.4627858 -4.659018, 55.463193 -4.6585292, 55.4628348 -4.65804, 55.4629354 -4.656064,\"\n                        + \" 55.4621532 -4.654595, 55.4659511 -4.6540752, 55.4661665 -4.6560414, 55.46891 -4.6564482,\"\n                        + \" 55.4691254 -4.6553182, 55.4684792 -4.6544368, 55.4694089 -4.6539848, 55.4704179 -4.6550696)),\"\n                        + \" ((55.4209807 -4.6276984, 55.4206769 -4.6281207, 55.4209344 -4.6283079, 55.4204624 -4.6293345,\"\n                        + \" 55.4201512 -4.629666, 55.4195558 -4.6294307, 55.419346 -4.6289794, 55.4199796 -4.6284897,\"\n                        + \" 55.4203385 -4.6274556, 55.4192661 -4.6270567, 55.41844 -4.626383, 55.4174315 -4.62495,\"\n                        + \" 55.4172652 -4.6247896, 55.417807 -4.6243847, 55.4188101 -4.6245759, 55.419411 -4.6250624,\"\n                        + \" 55.4200493 -4.6253365, 55.4203658 -4.6257736, 55.4201352 -4.6261051, 55.4203712 -4.6265181,\"\n                        + \" 55.4203122 -4.6268858, 55.4207682 -4.6270421, 55.4209505 -4.6268376, 55.4213314 -4.6269499,\"\n                        + \" 55.4213636 -4.6271103, 55.4211268 -4.6274921, 55.4209807 -4.6276984)))\",\n                result.toWkt());\n    }\n\n    @Test\n    public void testOverlappingPolygons()\n    {\n        final GeometricSurface result = GeometryOperation\n                .union(this.multiPolygon1, this.multiPolygon1)\n                .orElseThrow(() -> new CoreException(\"fail\"));\n        Assert.assertEquals(\n                \"POLYGON ((55.4527599 -4.6423107, 55.4529644 -4.6425404, 55.4528862 -4.6426603, 55.4528762 -4.6428181,\"\n                        + \" 55.4530065 -4.6429939, 55.4528842 -4.6430798, 55.4525876 -4.6431737, 55.4522369 -4.6431817,\"\n                        + \" 55.4520144 -4.6430778, 55.4519884 -4.642868, 55.4521828 -4.6425984, 55.452295 -4.6424066,\"\n                        + \" 55.4524513 -4.6423687, 55.4527599 -4.6423107))\",\n                result.toWkt());\n    }\n\n    @Test\n    public void testOverlappingPolygonsToMultiPolygon()\n    {\n        final MultiPolygon multiPolygon = MultiPolygonTest.getFrom(\n                \"testOverlappingPolygonsToMultiPolygon.josm.osm\", GeometryOperationTest.class);\n        final Optional<GeometricObject> resultOption = GeometryOperation\n                .intersection(multiPolygon.outers());\n        Assert.assertTrue(resultOption.isPresent());\n        final GeometricObject result = resultOption.get();\n        Assert.assertTrue(result instanceof MultiPolygon);\n        Assert.assertEquals(\n                \"MULTIPOLYGON (((5.9351685 43.1021092, 5.9352199 43.1018822, 5.9347228 43.1019763, 5.9351685 43.1021092)), \"\n                        + \"((5.9350863 43.1024728, 5.9351358 43.1022541, 5.9344967 43.1023663, 5.9350863 43.1024728)))\",\n                ((MultiPolygon) result).toWkt());\n    }\n\n    @Test\n    public void testOverlappingPolygonsToPolygon()\n    {\n        final MultiPolygon multiPolygon = MultiPolygonTest\n                .getFrom(\"testOverlappingPolygonsToPolygon.josm.osm\", GeometryOperationTest.class);\n        final Optional<GeometricObject> resultOption = GeometryOperation\n                .intersection(multiPolygon.outers());\n        Assert.assertTrue(resultOption.isPresent());\n        final GeometricObject result = resultOption.get();\n        Assert.assertTrue(result instanceof Polygon);\n        Assert.assertEquals(\n                \"POLYGON ((5.9350863 43.1024728, 5.9351896 43.1020163, 5.9344967 43.1023663, 5.9350863 43.1024728))\",\n                ((Polygon) result).toWkt());\n    }\n\n    @Test\n    public void testTouchingPolygons()\n    {\n        final MultiPolygon multiPolygon = MultiPolygonTest.getFrom(\"testTouchingPolygons.josm.osm\",\n                GeometryOperationTest.class);\n        final Optional<GeometricObject> resultOption = GeometryOperation\n                .intersection(multiPolygon.outers());\n        Assert.assertTrue(resultOption.isPresent());\n        final GeometricObject result = resultOption.get();\n        Assert.assertTrue(result instanceof Location);\n        Assert.assertEquals(\"POINT (5.9349989 43.102859)\", ((Location) result).toWkt());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/clipping/MultiPolygonClipperTest.java",
    "content": "package org.openstreetmap.atlas.geography.clipping;\n\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.clipping.Clip.ClipType;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * @author matthieun\n */\npublic class MultiPolygonClipperTest\n{\n    private static final MultiPolygon CLIPPING = MultiPolygon.TEST_MULTI_POLYGON;\n    private static final MultiPolygon SUBJECT_MULTIPOLYGON;\n    private static final Polygon SUBJECT = new Polygon(Location.forString(\"37.329869,-122.054441\"),\n            Location.forString(\"37.331287,-121.995459\"), Location.TEST_3);\n    private static final PolyLine SUBJECT_POLYLINE = new PolyLine(\n            Location.forString(\"37.329869,-122.054441\"),\n            Location.forString(\"37.331287,-121.995459\"), Location.TEST_3);\n\n    static\n    {\n        final MultiMap<Polygon, Polygon> outerToInners = new MultiMap<>();\n        final Polygon outer = new Polygon(Location.CROSSING_85_280, Location.CROSSING_85_17,\n                Location.forString(\"37.363819,-121.928993\"));\n        final Polygon inner = new Polygon(Location.forString(\"37.320691,-122.023155\"),\n                Location.forString(\"37.317882,-122.013252\"),\n                Location.forString(\"37.310775,-122.024402\"));\n        outerToInners.add(outer, inner);\n        SUBJECT_MULTIPOLYGON = new MultiPolygon(outerToInners);\n    }\n\n    @Test\n    public void testEmptyClip()\n    {\n        final Polygon outer = Polygon.wkt(\n                \"POLYGON ((-122.05576 37.332439,  -122.009566 37.36531, -122.031007 37.390535, -122.05576 37.332439))\");\n        final MultiPolygon clipping = MultiPolygon.forPolygon(outer);\n        final MultiPolygon clipped = SUBJECT_MULTIPOLYGON.clip(clipping, ClipType.AND)\n                .getClipMultiPolygon();\n        Assert.assertTrue(clipped.isEmpty());\n    }\n\n    @Test\n    public void testMultiPolygonAnd()\n    {\n        final MultiPolygon clipped = SUBJECT_MULTIPOLYGON.clip(CLIPPING, ClipType.AND)\n                .getClipMultiPolygon();\n        Assert.assertEquals(1, clipped.outers().size());\n        Assert.assertEquals(2, clipped.inners().size());\n    }\n\n    @Test\n    public void testMultiPolygonNot()\n    {\n        final MultiPolygon clipped = SUBJECT_MULTIPOLYGON.clip(CLIPPING, ClipType.NOT)\n                .getClipMultiPolygon();\n        Assert.assertEquals(2, clipped.outers().size());\n        Assert.assertEquals(0, clipped.inners().size());\n    }\n\n    @Test\n    public void testMultiPolygonOr()\n    {\n        final MultiPolygon clipped = SUBJECT_MULTIPOLYGON.clip(CLIPPING, ClipType.OR)\n                .getClipMultiPolygon();\n        Assert.assertEquals(1, clipped.outers().size());\n        Assert.assertEquals(0, clipped.inners().size());\n    }\n\n    @Test\n    public void testMultiPolygonXor()\n    {\n        final MultiPolygon clipped = SUBJECT_MULTIPOLYGON.clip(CLIPPING, ClipType.XOR)\n                .getClipMultiPolygon();\n        Assert.assertEquals(4, clipped.outers().size());\n        Assert.assertEquals(0, clipped.inners().size());\n    }\n\n    @Test\n    public void testPolyLineAnd()\n    {\n        final List<? extends PolyLine> clipped = SUBJECT_POLYLINE.clip(CLIPPING, ClipType.AND)\n                .getClip();\n        Assert.assertEquals(4, clipped.size());\n    }\n\n    @Test\n    public void testPolyLineNot()\n    {\n        final List<? extends PolyLine> clipped = SUBJECT_POLYLINE.clip(CLIPPING, ClipType.NOT)\n                .getClip();\n        Assert.assertEquals(5, clipped.size());\n    }\n\n    @Test\n    public void testPolyLineOr()\n    {\n        final List<? extends PolyLine> clipped = SUBJECT_POLYLINE.clip(CLIPPING, ClipType.OR)\n                .getClip();\n        Assert.assertEquals(5, clipped.size());\n    }\n\n    @Test\n    public void testPolyLineXor()\n    {\n        final List<? extends PolyLine> clipped = SUBJECT_POLYLINE.clip(CLIPPING, ClipType.XOR)\n                .getClip();\n        Assert.assertEquals(5, clipped.size());\n    }\n\n    @Test\n    public void testPolygonAnd()\n    {\n        final MultiPolygon clipped = SUBJECT.clip(CLIPPING, ClipType.AND).getClipMultiPolygon();\n        Assert.assertEquals(2, clipped.outers().size());\n        Assert.assertEquals(0, clipped.inners().size());\n    }\n\n    @Test\n    public void testPolygonNot()\n    {\n        final MultiPolygon clipped = SUBJECT.clip(CLIPPING, ClipType.NOT).getClipMultiPolygon();\n        Assert.assertEquals(3, clipped.outers().size());\n        Assert.assertEquals(0, clipped.inners().size());\n    }\n\n    @Test\n    public void testPolygonOr()\n    {\n        final MultiPolygon clipped = SUBJECT.clip(CLIPPING, ClipType.OR).getClipMultiPolygon();\n        Assert.assertEquals(1, clipped.outers().size());\n        Assert.assertEquals(2, clipped.inners().size());\n    }\n\n    @Test\n    public void testPolygonXor()\n    {\n        final MultiPolygon clipped = SUBJECT.clip(CLIPPING, ClipType.XOR).getClipMultiPolygon();\n        Assert.assertEquals(5, clipped.outers().size());\n        Assert.assertEquals(0, clipped.inners().size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/clipping/PolygonClipperTest.java",
    "content": "package org.openstreetmap.atlas.geography.clipping;\n\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.clipping.Clip.ClipType;\n\n/**\n * @author matthieun\n */\npublic class PolygonClipperTest\n{\n    private static final Polygon CLIPPING = Polygon.SILICON_VALLEY_2;\n    private static final Polygon SUBJECT = new Polygon(Location.CROSSING_85_280, Location.TEST_6,\n            Location.TEST_4, Location.STEVENS_CREEK, Location.CROSSING_85_17);\n    private static final PolyLine SUBJECT_POLYLINE = new PolyLine(Location.CROSSING_85_280,\n            Location.TEST_6, Location.TEST_4, Location.STEVENS_CREEK, Location.CROSSING_85_17);\n\n    @Test\n    public void testPolyLineAnd()\n    {\n        final List<? extends PolyLine> clips = SUBJECT_POLYLINE.clip(CLIPPING, ClipType.AND)\n                .getClip();\n        Assert.assertEquals(2, clips.size());\n    }\n\n    @Test\n    public void testPolyLineNot()\n    {\n        final List<? extends PolyLine> clips = SUBJECT_POLYLINE.clip(CLIPPING, ClipType.NOT)\n                .getClip();\n        Assert.assertEquals(3, clips.size());\n    }\n\n    @Test\n    public void testPolyLineOr()\n    {\n        final List<? extends PolyLine> clips = SUBJECT_POLYLINE.clip(CLIPPING, ClipType.OR)\n                .getClip();\n        Assert.assertEquals(4, clips.size());\n    }\n\n    @Test\n    public void testPolyLineXor()\n    {\n        final List<? extends PolyLine> clips = SUBJECT_POLYLINE.clip(CLIPPING, ClipType.XOR)\n                .getClip();\n        Assert.assertEquals(4, clips.size());\n    }\n\n    @Test\n    public void testPolygonAnd()\n    {\n        final List<? extends PolyLine> clips = SUBJECT.clip(CLIPPING, ClipType.AND).getClip();\n        Assert.assertEquals(2, clips.size());\n    }\n\n    @Test\n    public void testPolygonNot()\n    {\n        final List<? extends PolyLine> clips = SUBJECT.clip(CLIPPING, ClipType.NOT).getClip();\n        Assert.assertEquals(2, clips.size());\n    }\n\n    @Test\n    public void testPolygonOr()\n    {\n        final List<? extends PolyLine> clips = SUBJECT.clip(CLIPPING, ClipType.OR).getClip();\n        Assert.assertEquals(1, clips.size());\n    }\n\n    @Test\n    public void testPolygonXor()\n    {\n        final List<? extends PolyLine> clips = SUBJECT.clip(CLIPPING, ClipType.XOR).getClip();\n        Assert.assertEquals(4, clips.size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/GeodeticEarthCenteredEarthFixedConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.coordinates.EarthCenteredEarthFixedCoordinate;\nimport org.openstreetmap.atlas.geography.coordinates.GeodeticCoordinate;\n\n/**\n * {@link GeodeticEarthCenteredEarthFixedConverter} test.\n *\n * @author mgostintsev\n */\npublic class GeodeticEarthCenteredEarthFixedConverterTest\n{\n    private static final GeodeticEarthCenteredEarthFixedConverter CONVERTER = new GeodeticEarthCenteredEarthFixedConverter();\n\n    @Test\n    public void testConversionToEarthCenteredEarthFixed()\n    {\n        final EarthCenteredEarthFixedCoordinate earthCenteredCoordinates = new EarthCenteredEarthFixedCoordinate(\n                -576793.17, -5376363.47, 3372298.51);\n        final GeodeticCoordinate geodeticCoordinates = CONVERTER\n                .backwardConvert(earthCenteredCoordinates);\n\n        Assert.assertEquals(32.12345, geodeticCoordinates.getLatitude().asDegrees(), 1e-3);\n        Assert.assertEquals(-96.12345, geodeticCoordinates.getLongitude().asDegrees(), 1e-3);\n        Assert.assertEquals(500.0, geodeticCoordinates.getAltitude().asMeters(), 1e-2);\n    }\n\n    @Test\n    public void testConversionToGeodetic()\n    {\n        final GeodeticCoordinate geodeticCoordinates = new GeodeticCoordinate(\n                Latitude.degrees(32.12345), Longitude.degrees(-96.12345), Altitude.meters(500.0));\n        final EarthCenteredEarthFixedCoordinate earthCenteredCoordinates = CONVERTER\n                .convert(geodeticCoordinates);\n\n        Assert.assertEquals(-576793.17, earthCenteredCoordinates.getX(), 1e-2);\n        Assert.assertEquals(-5376363.47, earthCenteredCoordinates.getY(), 1e-2);\n        Assert.assertEquals(3372298.51, earthCenteredCoordinates.getZ(), 1e-2);\n    }\n\n    @Test\n    public void testFullConversionCycle()\n    {\n        final double latitude = -39.664914;\n        final double longitude = 176.881899;\n        final double altitude = 300.0;\n\n        final GeodeticCoordinate geodeticCoordinates = new GeodeticCoordinate(\n                Latitude.degrees(latitude), Longitude.degrees(longitude),\n                Altitude.meters(altitude));\n\n        final EarthCenteredEarthFixedCoordinate earthCenteredCoordinates = CONVERTER\n                .convert(geodeticCoordinates);\n\n        assertEquals(-4909490.91860, earthCenteredCoordinates.getX(), 1e-2);\n        assertEquals(267444.11617, earthCenteredCoordinates.getY(), 1e-2);\n        assertEquals(-4049606.55365, earthCenteredCoordinates.getZ(), 1e-2);\n\n        final GeodeticCoordinate backToGeodetic = CONVERTER\n                .backwardConvert(earthCenteredCoordinates);\n        assertEquals(latitude, backToGeodetic.getLatitude().asDegrees(), 1e-6);\n        assertEquals(longitude, backToGeodetic.getLongitude().asDegrees(), 1e-6);\n        assertEquals(altitude, backToGeodetic.getAltitude().asMeters(), 1e-6);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/MultiPolygonStringConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\n\n/**\n * @author matthieun\n */\npublic class MultiPolygonStringConverterTest\n{\n    private static final MultiPolygonStringConverter CONVERTER = new MultiPolygonStringConverter();\n\n    @Test\n    public void testConversion()\n    {\n        System.out.println(CONVERTER.backwardConvert(MultiPolygon.TEST_MULTI_POLYGON));\n        Assert.assertEquals(MultiPolygon.TEST_MULTI_POLYGON,\n                CONVERTER.convert(CONVERTER.backwardConvert(MultiPolygon.TEST_MULTI_POLYGON)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToMultiPolygonConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation.Ring;\n\n/**\n * @author samg\n */\npublic class MultiplePolyLineToMultiPolygonConverterTest\n{\n    private static final MultiplePolyLineToMultiPolygonConverter CONVERTER = new MultiplePolyLineToMultiPolygonConverter();\n\n    private static final Location INNER_LOCATION_1 = Location.forString(\"24, 68\");\n    private static final Location INNER_LOCATION_2 = Location.forString(\"24, 69\");\n    private static final Location INNER_LOCATION_3 = Location.forString(\"23, 69\");\n    private static final Location INNER_LOCATION_4 = Location.forString(\"23, 68\");\n\n    private static final Location OUTER_LOCATION_1 = Location.forString(\"25, 67\");\n    private static final Location OUTER_LOCATION_2 = Location.forString(\"25, 70\");\n    private static final Location OUTER_LOCATION_3 = Location.forString(\"22, 70\");\n    private static final Location OUTER_LOCATION_4 = Location.forString(\"22, 67\");\n\n    @Test\n    public void testConversion()\n    {\n        final PolyLine inner1 = new PolyLine(INNER_LOCATION_1, INNER_LOCATION_2);\n        final PolyLine inner2 = new PolyLine(INNER_LOCATION_2, INNER_LOCATION_3);\n        final PolyLine inner3 = new PolyLine(INNER_LOCATION_3, INNER_LOCATION_4);\n        final PolyLine inner4 = new PolyLine(INNER_LOCATION_4, INNER_LOCATION_1);\n        final List<PolyLine> inners = new ArrayList<>();\n        inners.add(inner1);\n        inners.add(inner2);\n        inners.add(inner3);\n        inners.add(inner4);\n\n        final PolyLine outer1 = new PolyLine(OUTER_LOCATION_1, OUTER_LOCATION_2);\n        final PolyLine outer2 = new PolyLine(OUTER_LOCATION_2, OUTER_LOCATION_3);\n        final PolyLine outer3 = new PolyLine(OUTER_LOCATION_3, OUTER_LOCATION_4);\n        final PolyLine outer4 = new PolyLine(OUTER_LOCATION_4, OUTER_LOCATION_1);\n        final List<PolyLine> outers = new ArrayList<>();\n        outers.add(outer1);\n        outers.add(outer2);\n        outers.add(outer3);\n        outers.add(outer4);\n\n        final Map<Ring, Iterable<PolyLine>> innersAndOuters = new HashMap<>();\n        innersAndOuters.put(Ring.OUTER, outers);\n        innersAndOuters.put(Ring.INNER, inners);\n\n        final MultiPolygon result = CONVERTER.convert(innersAndOuters);\n        Assert.assertNotNull(result);\n        Assert.assertFalse(result.isEmpty());\n        Assert.assertTrue(result.isSimplePolygon());\n        Assert.assertEquals(1, result.inners().size());\n        Assert.assertEquals(1, result.outers().size());\n    }\n\n    @Test(expected = MultiplePolyLineToPolygonsConverter.OpenPolygonException.class)\n    public void testInnerFailstoClose()\n    {\n        final PolyLine inner1 = new PolyLine(INNER_LOCATION_1, INNER_LOCATION_2);\n        final PolyLine inner2 = new PolyLine(INNER_LOCATION_2, INNER_LOCATION_3);\n        final PolyLine inner3 = new PolyLine(INNER_LOCATION_3, INNER_LOCATION_4);\n        final PolyLine inner4 = new PolyLine(INNER_LOCATION_4, INNER_LOCATION_1);\n        final List<PolyLine> inners = new ArrayList<>();\n        inners.add(inner1);\n        inners.add(inner3);\n        inners.add(inner4);\n\n        final PolyLine outer1 = new PolyLine(OUTER_LOCATION_1, OUTER_LOCATION_2);\n        final PolyLine outer2 = new PolyLine(OUTER_LOCATION_2, OUTER_LOCATION_3);\n        final PolyLine outer3 = new PolyLine(OUTER_LOCATION_3, OUTER_LOCATION_4);\n        final PolyLine outer4 = new PolyLine(OUTER_LOCATION_4, OUTER_LOCATION_1);\n        final List<PolyLine> outers = new ArrayList<>();\n        outers.add(outer1);\n        outers.add(outer2);\n        outers.add(outer3);\n        outers.add(outer4);\n\n        final Map<Ring, Iterable<PolyLine>> innersAndOuters = new HashMap<>();\n        innersAndOuters.put(Ring.OUTER, outers);\n        innersAndOuters.put(Ring.INNER, inners);\n\n        final MultiPolygon result = CONVERTER.convert(innersAndOuters);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testNoOuter()\n    {\n        final PolyLine inner1 = new PolyLine(INNER_LOCATION_1, INNER_LOCATION_2);\n        final PolyLine inner2 = new PolyLine(INNER_LOCATION_2, INNER_LOCATION_3);\n        final PolyLine inner3 = new PolyLine(INNER_LOCATION_3, INNER_LOCATION_4);\n        final PolyLine inner4 = new PolyLine(INNER_LOCATION_4, INNER_LOCATION_1);\n        final List<PolyLine> inners = new ArrayList<>();\n        inners.add(inner1);\n        inners.add(inner2);\n        inners.add(inner3);\n        inners.add(inner4);\n\n        final List<PolyLine> outers = new ArrayList<>();\n\n        final Map<Ring, Iterable<PolyLine>> innersAndOuters = new HashMap<>();\n        innersAndOuters.put(Ring.OUTER, outers);\n        innersAndOuters.put(Ring.INNER, inners);\n\n        final MultiPolygon result = CONVERTER.convert(innersAndOuters);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterCommandTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\n\n/**\n * @author matthieun\n */\npublic class MultiplePolyLineToPolygonsConverterCommandTest\n{\n    @Test\n    public void testCommand()\n    {\n        final Resource input = new StringResource(\n                \"MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10));\"\n                        + \"LINESTRING (10 40, 40 40);LINESTRING (30 10, 10 10)\");\n        final WritableResource output = new StringResource();\n        final String delimiter = \";\";\n        new MultiplePolyLineToPolygonsConverterCommand().translate(input, output, delimiter);\n        final Polygon result = Polygon.wkt(output.all());\n        final Polygon expected = Polygon\n                .wkt(\"POLYGON ((30 10, 10 10, 20 20, 10 40, \" + \"40 40, 30 30, 40 20, 30 10))\");\n        Assert.assertTrue(expected.isSimilarTo(result));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.converters.MultiplePolyLineToPolygonsConverter.OpenPolygonException;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n * @author mgostintsev\n */\npublic class MultiplePolyLineToPolygonsConverterTest\n{\n    private static final MultiplePolyLineToPolygonsConverter CONVERTER = new MultiplePolyLineToPolygonsConverter();\n\n    private static final Location ONE = Location.TEST_6;\n    private static final Location TWO = Location.TEST_2;\n    private static final Location THR = Location.TEST_1;\n    private static final Location FOR = Location.TEST_5;\n    private static final Location FVE = Location.TEST_4;\n\n    private static final Polygon POLYGON_LOOP = new Polygon(ONE, TWO, THR, FOR, FVE);\n    private static final PolyLine POLYLINE_LOOP = new PolyLine(ONE, TWO, THR, FOR, FVE, ONE);\n\n    private static final WktPolyLineConverter WKT_POLY_LINE_CONVERTER = new WktPolyLineConverter();\n    private static final WktPolygonConverter WKT_POLYGON_CONVERTER = new WktPolygonConverter();\n\n    // Following polylines from http://www.openstreetmap.org/relation/409391 version #4\n    private static final PolyLine EDGE1 = new PolyLineStringConverter().convert(\n            \"18.3875057,-74.0412515:\" + \"18.3893694,-74.0470441:\" + \"18.3923348,-74.0490819:\"\n                    + \"18.3936336,-74.0503812:\" + \"18.3957615,-74.0530296:\"\n                    + \"18.3973108,-74.0556245:\" + \"18.399442,-74.0571181:\"\n                    + \"18.4015712,-74.0582142:\" + \"18.403311,-74.0609596:\"\n                    + \"18.4047097,-74.0642088:\" + \"18.4047239,-74.0671055:\"\n                    + \"18.405441,-74.0714471:\" + \"18.405117,-74.0754345:\"\n                    + \"18.4054675,-74.0768812:\" + \"18.4067034,-74.0782947:\"\n                    + \"18.407543,-74.0818403:\" + \"18.4068213,-74.0858583\");\n    private static final PolyLine EDGE2 = new PolyLineStringConverter()\n            .convert(\"18.3875057,-74.0412515:\" + \"18.3931334,-74.0407471:\"\n                    + \"18.3969804,-74.0383875:\" + \"18.4015135,-74.0358821:\"\n                    + \"18.4068233,-74.033268:\" + \"18.4099864,-74.0315273:\"\n                    + \"18.4132471,-74.0311588:\" + \"18.4177298,-74.031219:\"\n                    + \"18.4225155,-74.0304252:\" + \"18.4257431,-74.0289017:\"\n                    + \"18.4282312,-74.0276947:\" + \"18.4307202,-74.0284382:\"\n                    + \"18.4340427,-74.0295842:\" + \"18.433509,-74.0312442:\"\n                    + \"18.4326188,-74.0339003:\" + \"18.4325629,-74.0371106:\"\n                    + \"18.4357384,-74.037803:\" + \"18.4398946,-74.0358198:\"\n                    + \"18.4438752,-74.0330989:\" + \"18.4490069,-74.0328145:\"\n                    + \"18.4528982,-74.0338533:\" + \"18.4531964,-74.0373933:\"\n                    + \"18.4547577,-74.0387577:\" + \"18.4561214,-74.04036:\"\n                    + \"18.4565557,-74.0422515:\" + \"18.4581912,-74.0421951:\"\n                    + \"18.459685,-74.0409368:\" + \"18.4607417,-74.0408078:\"\n                    + \"18.4618023,-74.0414459:\" + \"18.4629325,-74.0415533:\"\n                    + \"18.4640673,-74.0407893:\" + \"18.4651883,-74.0408589:\"\n                    + \"18.4656794,-74.0414717:\" + \"18.4668101,-74.0416738:\"\n                    + \"18.4682445,-74.0411923:\" + \"18.4707062,-74.0419255:\"\n                    + \"18.4720272,-74.041728:\" + \"18.4725118,-74.0413371:\"\n                    + \"18.4734976,-74.0402072:\" + \"18.4742729,-74.0395303:\"\n                    + \"18.4764658,-74.0391494:\" + \"18.4787394,-74.0381316:\"\n                    + \"18.4795547,-74.0367348:\" + \"18.479653,-74.0347454:\"\n                    + \"18.4804524,-74.0337937:\" + \"18.4802471,-74.0307642:\"\n                    + \"18.4807934,-74.0297665:\" + \"18.4818949,-74.0295898:\"\n                    + \"18.4837607,-74.0303845:\" + \"18.4868311,-74.0299409:\"\n                    + \"18.4889283,-74.0300523:\" + \"18.4914733,-74.0311462\");\n    private static final PolyLine EDGE3 = new PolyLineStringConverter()\n            .convert(\"18.5335978,-74.0368872:\" + \"18.53112,-74.036579:\" + \"18.5289457,-74.0372922:\"\n                    + \"18.5273132,-74.034374:\" + \"18.5254488,-74.035616:\"\n                    + \"18.5242828,-74.0337941:\" + \"18.5212489,-74.0307702:\"\n                    + \"18.5181947,-74.0308443:\" + \"18.5176216,-74.03188:\"\n                    + \"18.5176394,-74.0353472:\" + \"18.517026,-74.0373495:\"\n                    + \"18.5137583,-74.0380971:\" + \"18.5115861,-74.039246:\"\n                    + \"18.5096258,-74.0393801:\" + \"18.5054015,-74.038608\");\n    private static final PolyLine EDGE4 = new PolyLineStringConverter().convert(\n            \"18.5230901,-74.0754088:\" + \"18.523199,-74.0718082:\" + \"18.5224555,-74.0694912:\"\n                    + \"18.5213616,-74.0675645:\" + \"18.5226289,-74.0643177:\"\n                    + \"18.5250127,-74.0620784:\" + \"18.5254792,-74.0613843:\"\n                    + \"18.5246008,-74.0592196:\" + \"18.5237511,-74.0573579:\"\n                    + \"18.525581,-74.054629:\" + \"18.524995,-74.0530974:\" + \"18.5261824,-74.0520014:\"\n                    + \"18.5252714,-74.050557:\" + \"18.5265494,-74.0495078:\"\n                    + \"18.5268307,-74.0479241:\" + \"18.5286325,-74.0468152:\"\n                    + \"18.531375,-74.045862:\" + \"18.5326575,-74.0439127:\" + \"18.5332768,-74.041285:\"\n                    + \"18.5341273,-74.039717:\" + \"18.5341949,-74.0387977:\"\n                    + \"18.5335978,-74.0368872\");\n    private static final PolyLine EDGE5 = new PolyLineStringConverter().convert(\n            \"18.4345179,-74.1196672:\" + \"18.4327579,-74.1182084:\" + \"18.4299982,-74.1155429:\"\n                    + \"18.4263207,-74.1157227:\" + \"18.4251045,-74.1146021:\"\n                    + \"18.4222402,-74.11278:\" + \"18.4187323,-74.110573:\" + \"18.4160356,-74.107879:\"\n                    + \"18.4125933,-74.1080767:\" + \"18.403404,-74.106524\");\n    private static final PolyLine EDGE6 = new PolyLineStringConverter()\n            .convert(\"18.5230901,-74.0754088:\" + \"18.520754,-74.0763403:\"\n                    + \"18.5185178,-74.0773754:\" + \"18.5169688,-74.0784637:\"\n                    + \"18.5156572,-74.0800906:\" + \"18.5114384,-74.082273:\"\n                    + \"18.5094846,-74.0837801:\" + \"18.5077898,-74.0846511:\"\n                    + \"18.5048729,-74.0851024:\" + \"18.5022438,-74.087134:\"\n                    + \"18.5007805,-74.0872838:\" + \"18.4992088,-74.0874436:\"\n                    + \"18.4979241,-74.0890323:\" + \"18.4989472,-74.0913097:\"\n                    + \"18.4989661,-74.0919123:\" + \"18.4989665,-74.0919302:\"\n                    + \"18.4989662,-74.0919643:\" + \"18.4990328,-74.0940847:\"\n                    + \"18.4981478,-74.0961165:\" + \"18.4943862,-74.095672:\"\n                    + \"18.4916892,-74.0948621:\" + \"18.4879321,-74.0972687:\"\n                    + \"18.4833534,-74.0998405:\" + \"18.4799753,-74.1021408:\"\n                    + \"18.4780311,-74.1019141:\" + \"18.476033,-74.0998029:\"\n                    + \"18.4729982,-74.1002072:\" + \"18.470605,-74.1005417:\"\n                    + \"18.4678104,-74.098217:\" + \"18.4639369,-74.0970533:\"\n                    + \"18.4608976,-74.0964916:\" + \"18.4567638,-74.0975833:\"\n                    + \"18.4531758,-74.0994771:\" + \"18.4509348,-74.1014301:\"\n                    + \"18.4490696,-74.1045174:\" + \"18.4473182,-74.1068181:\"\n                    + \"18.4466008,-74.107996:\" + \"18.4464353,-74.1081067:\"\n                    + \"18.4463879,-74.1081384:\" + \"18.4440332,-74.1097138:\"\n                    + \"18.4417798,-74.1110132:\" + \"18.4378665,-74.1129176:\"\n                    + \"18.4356025,-74.1157794:\" + \"18.4345179,-74.1196672\");\n    private static final PolyLine EDGE7 = new PolyLineStringConverter()\n            .convert(\"18.4914733,-74.0311462:\" + \"18.4907226,-74.032789:\"\n                    + \"18.4896023,-74.0328426:\" + \"18.4889917,-74.0336038:\"\n                    + \"18.4896319,-74.0351157:\" + \"18.4929014,-74.0364898:\"\n                    + \"18.4938275,-74.0373655:\" + \"18.495212,-74.0377177:\" + \"18.4968,-74.0389875:\"\n                    + \"18.4976593,-74.0409246:\" + \"18.4981439,-74.0412372:\"\n                    + \"18.5009698,-74.0414745:\" + \"18.5009449,-74.0415097:\"\n                    + \"18.5025891,-74.0400445:\" + \"18.503446,-74.0379274:\"\n                    + \"18.5051641,-74.0385303:\" + \"18.5054015,-74.038608\");\n    private static final PolyLine EDGE8 = new PolyLineStringConverter()\n            .convert(\"18.4068213,-74.0858583:\" + \"18.4039542,-74.0948388:\"\n                    + \"18.4026541,-74.1008287:\" + \"18.403404,-74.106524\");\n\n    @Test\n    public void testBoundary()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(EDGE1);\n        list.add(EDGE2);\n        list.add(EDGE3);\n        list.add(EDGE4);\n        list.add(EDGE5);\n        list.add(EDGE6);\n        list.add(EDGE7);\n        list.add(EDGE8);\n        Assert.assertEquals(1, Iterables.size(CONVERTER.convert(list)));\n    }\n\n    @Test(expected = OpenPolygonException.class)\n    public void testHole()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(new PolyLine(ONE, TWO));\n        list.add(new PolyLine(TWO, THR, FOR));\n        list.add(new PolyLine(FVE, ONE));\n        CONVERTER.convert(list);\n    }\n\n    @Test\n    public void testJtsException()\n    {\n        // Based on OSM data taken as of 2020-10-08.\n        // - https://www.openstreetmap.org/relation/3990246 - v8\n        // - outer: https://www.openstreetmap.org/way/300069279 - v30\n        // - inner: https://www.openstreetmap.org/way/358209466 - v6\n        // - inner: https://www.openstreetmap.org/way/304650414 - v9\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(PolyLine.wkt(new InputStreamResource(\n                () -> MultiplePolyLineToPolygonsConverterTest.class.getResourceAsStream(\n                        \"MultiplePolyLineToPolygonsConverterTest_jtsErrorOuter.wkt\"))\n                .all()));\n        list.add(PolyLine.wkt(new InputStreamResource(\n                () -> MultiplePolyLineToPolygonsConverterTest.class.getResourceAsStream(\n                        \"MultiplePolyLineToPolygonsConverterTest_jtsErrorInner1.wkt\"))\n                .all()));\n        list.add(PolyLine.wkt(new InputStreamResource(\n                () -> MultiplePolyLineToPolygonsConverterTest.class.getResourceAsStream(\n                        \"MultiplePolyLineToPolygonsConverterTest_jtsErrorInner2.wkt\"))\n                .all()));\n        Assert.assertEquals(3, Iterables.size(CONVERTER.convert(list)));\n    }\n\n    @Test\n    public void testPolyLinesWithOneSelfIntersection()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(PolyLine.wkt(\"LINESTRING (5 5, 0 10, 10 10)\"));\n        list.add(PolyLine.wkt(\"LINESTRING (10 10, 5 5)\"));\n        list.add(PolyLine.wkt(\"LINESTRING (5 5, 0 0)\"));\n        list.add(PolyLine.wkt(\"LINESTRING (0 0, 10 0, 5 5)\"));\n        Assert.assertEquals(2, Iterables.size(CONVERTER.convert(list)));\n    }\n\n    @Test\n    public void testPolyLinesWithTwoSelfIntersections()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(PolyLine.wkt(\"LINESTRING (5 5, 0 10, 10 10)\"));\n        list.add(PolyLine.wkt(\"LINESTRING (10 10, 5 5)\"));\n        list.add(PolyLine.wkt(\"LINESTRING (5 5, 0 0)\"));\n        list.add(PolyLine.wkt(\"LINESTRING (0 0, 10 0, 5 5)\"));\n        list.add(PolyLine.wkt(\"LINESTRING (10 10, 15 15, 15 10)\"));\n        list.add(PolyLine.wkt(\"LINESTRING (15 10, 10 10)\"));\n        Assert.assertEquals(3, Iterables.size(CONVERTER.convert(list)));\n    }\n\n    @Test\n    public void testRegular()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(new PolyLine(ONE, TWO));\n        list.add(new PolyLine(TWO, THR, FOR));\n        list.add(new PolyLine(FOR, FVE, ONE));\n        final Polygon result = CONVERTER.convert(list).iterator().next();\n        Assert.assertEquals(POLYGON_LOOP.length(), result.length());\n    }\n\n    @Test\n    public void testReversed()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(new PolyLine(ONE, TWO));\n        list.add(new PolyLine(TWO, THR, FOR));\n        // Reversed!\n        list.add(new PolyLine(ONE, FVE, FOR));\n        final Polygon result = CONVERTER.convert(list).iterator().next();\n        Assert.assertEquals(POLYGON_LOOP.length(), result.length());\n    }\n\n    @Test\n    public void testSingleClosedPolyLine()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(POLYLINE_LOOP);\n        Assert.assertEquals(1, Iterables.size(CONVERTER.convert(list)));\n    }\n\n    @Test\n    public void testSingleClosedPolyLineWithinGroup()\n    {\n        final List<PolyLine> input = new InputStreamResource(\n                () -> MultiplePolyLineToPolygonsConverterTest.class.getResourceAsStream(\n                        \"MultiplePolyLineToPolygonsConverterTest_multiplePolyLines.txt\"))\n                .linesList().stream().map(WKT_POLY_LINE_CONVERTER::backwardConvert)\n                .collect(Collectors.toList());\n        final List<Polygon> expected = new InputStreamResource(\n                () -> MultiplePolyLineToPolygonsConverterTest.class.getResourceAsStream(\n                        \"MultiplePolyLineToPolygonsConverterTest_expectedPolygons.txt\"))\n                .linesList().stream().map(WKT_POLYGON_CONVERTER::backwardConvert)\n                .collect(Collectors.toList());\n        Iterables.asList(CONVERTER.convert(input)).forEach(System.out::println);\n        Assert.assertEquals(expected.stream().map(Polygon::length).collect(Collectors.toList()),\n                Iterables.asList(CONVERTER.convert(input)).stream().map(Polygon::length)\n                        .collect(Collectors.toList()));\n    }\n\n    @Test\n    public void testSingleClosedPolygon()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(POLYGON_LOOP);\n        Assert.assertEquals(1, Iterables.size(CONVERTER.convert(list)));\n    }\n\n    @Test\n    public void testSingleOpenPolyLine()\n    {\n        final List<PolyLine> list = new ArrayList<>();\n        list.add(new PolyLine(ONE, TWO));\n        try\n        {\n            CONVERTER.convert(list);\n        }\n        catch (final OpenPolygonException openPolygonException)\n        {\n            Assert.assertEquals(2, openPolygonException.getOpenLocations().size());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/SlippyTileConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.geography.sharding.converters.SlippyTileConverter;\n\n/**\n * @author tony\n */\npublic class SlippyTileConverterTest\n{\n    private static SlippyTileConverter converter = new SlippyTileConverter();\n\n    @Test\n    public void testConversion()\n    {\n        Assert.assertEquals(\"0-0-0\", converter.convert(SlippyTile.ROOT));\n        Assert.assertEquals(SlippyTile.ROOT,\n                converter.backwardConvert(converter.convert(SlippyTile.ROOT)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/WkbMultiPolygonConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\n\n/**\n * Test for {@link WkbMultiPolygonConverter}\n *\n * @author jklamer\n */\npublic class WkbMultiPolygonConverterTest\n{\n    private static WkbMultiPolygonConverter converter = new WkbMultiPolygonConverter();\n\n    @Test\n    public void testConversion()\n    {\n        final MultiPolygon multiPolygonA = MultiPolygon.TEST_MULTI_POLYGON;\n        final byte[] wkb = converter.convert(multiPolygonA);\n        final MultiPolygon multiPolygonB = converter.backwardConvert(wkb);\n        Assert.assertEquals(multiPolygonA, multiPolygonB);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/WkbPolyLineConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.WKBReader;\nimport org.locationtech.jts.io.WKBWriter;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Segment;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Test conversion between Well Known Binary (WKB) and {@link PolyLine}.\n *\n * @author robert_stack\n * @author mgostintsev\n */\npublic class WkbPolyLineConverterTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(WkbPolyLineConverterTest.class);\n\n    @Test\n    public void testSinglePointConversion()\n    {\n        final Location location = Location.TEST_1;\n        final PolyLine polyline = new PolyLine(location);\n        final byte[] wkb = new WkbPolyLineConverter().convert(polyline);\n        final PolyLine polylineConverted = new WkbPolyLineConverter().backwardConvert(wkb);\n        Assert.assertTrue(\"Input/output PolyLine must be the same\",\n                polyline.equals(polylineConverted));\n    }\n\n    @Test\n    public void testWkbConversions()\n    {\n        final List<PolyLine> polyLines = new ArrayList<>();\n        final List<String> strings = new ArrayList<>();\n\n        polyLines.add(new PolyLine(Location.TEST_6, Location.TEST_4, Location.TEST_1));\n        strings.add(\n                \"000000000200000003C05E822C343B70EF4042A9A8049667B6C05E81DA059A73B44042AA8DC11E42E1C05E809CBAB649D44042AAEB702602C9\");\n        polyLines.add(new Segment(Location.TEST_2, Location.TEST_5));\n        strings.add(\n                \"000000000200000002C05E81D25AAB47414042A92B1B36BD2BC05E81FC04C8BC9D4042B1FD0D0678C0\");\n\n        final WkbPolyLineConverter converter = new WkbPolyLineConverter();\n\n        // Forward convert from PolyLine to WKB and verify against prebuilt WKB\n        int stringIndex = 0;\n        for (final PolyLine polyLine : polyLines)\n        {\n            final byte[] convertedWkb = converter.convert(polyLine);\n            final String convertedWkbString = WKBWriter.toHex(convertedWkb);\n            Assert.assertEquals(convertedWkbString, strings.get(stringIndex));\n            logger.trace(convertedWkbString);\n            reportWkt(convertedWkb);\n            stringIndex++;\n        }\n\n        stringIndex = 0;\n        // Backward convert from prebuilt WKB to PolyLine and verify against known Polyline\n        for (final String string : strings)\n        {\n            final byte[] wkb = WKBReader.hexToBytes(string);\n            final PolyLine convertedPolyLine = converter.backwardConvert(wkb);\n            Assert.assertEquals(convertedPolyLine, polyLines.get(stringIndex));\n            logger.trace(convertedPolyLine.toString());\n            reportWkt(wkb);\n            stringIndex++;\n        }\n\n    }\n\n    // This just prints out a little more diagnostic information for any audience who is used to\n    // viewing location information in Postgres LINESTRING format.\n    private void reportWkt(final byte[] edgeWkb)\n    {\n        try\n        {\n            Geometry geom = null;\n            geom = new WKBReader().read(edgeWkb);\n            final String wktString = geom.toText();\n            logger.trace(wktString);\n        }\n        catch (final Exception e)\n        {\n            logger.error(\"Fail convert and parse WKB {}\", edgeWkb);\n            Assert.fail();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/WkbPolygonConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\n\n/**\n * @author Sid\n */\npublic class WkbPolygonConverterTest\n{\n    private static final WkbPolygonConverter CONVERTER = new WkbPolygonConverter();\n\n    @Test\n    public void testConversion()\n    {\n        final Polygon polygonA = new Polygon(Location.CROSSING_85_280, Location.CROSSING_85_17,\n                Location.TEST_1, Location.TEST_5, Location.CROSSING_85_280);\n        final byte[] wkb = CONVERTER.convert(polygonA);\n        final Polygon polygonB = CONVERTER.backwardConvert(wkb);\n        Assert.assertEquals(\"Input/output Polygon must be the same\", polygonA, polygonB);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/WktMultiPolygonConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * @author matthieun\n */\npublic class WktMultiPolygonConverterTest\n{\n    private static final WktMultiPolygonConverter CONVERTER = new WktMultiPolygonConverter();\n\n    private static final Location LOCATION_1 = Location.forString(\"24.2889638, 68.763726\");\n    private static final Location LOCATION_2 = Location.forString(\"24.2847937, 68.7691521\");\n    private static final Location LOCATION_3 = Location.forString(\"24.2726051, 68.7695996\");\n    private static final Location LOCATION_4 = Location.forString(\"24.2558968, 68.7686899\");\n\n    @Test\n    public void testConversionMultiPolygon()\n    {\n        final String wkt = \"POLYGON ((68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, \"\n                + \"68.7686899 24.2558968, 68.763726 24.2889638), \"\n                + \"(68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, 68.763726 24.2889638))\";\n        final MultiPolygon multiPolygon = CONVERTER.backwardConvert(wkt);\n        final Polygon polygon1 = new Polygon(LOCATION_1, LOCATION_2, LOCATION_3, LOCATION_4);\n        final Polygon polygon2 = new Polygon(LOCATION_1, LOCATION_2, LOCATION_3);\n        final MultiMap<Polygon, Polygon> outersToInners = new MultiMap<>();\n        outersToInners.add(polygon1, polygon2);\n        final MultiPolygon truth = new MultiPolygon(outersToInners);\n        Assert.assertEquals(truth, multiPolygon);\n        Assert.assertEquals(wkt, CONVERTER.convert(truth));\n    }\n\n    @Test\n    public void testConversionMultiPolygonWithMultipleOuters()\n    {\n        final String wkt = \"MULTIPOLYGON (\"\n                + \"((68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, \"\n                + \"68.7686899 24.2558968, 68.763726 24.2889638), \"\n                + \"(68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, 68.763726 24.2889638)), \"\n                + \"((68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, 68.763726 24.2889638), \"\n                + \"(68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, 68.763726 24.2889638))\"\n                + \")\";\n        final MultiPolygon multiPolygon = CONVERTER.backwardConvert(wkt);\n        final Polygon polygon1 = new Polygon(LOCATION_1, LOCATION_2, LOCATION_3, LOCATION_4);\n        final Polygon polygon2 = new Polygon(LOCATION_1, LOCATION_2, LOCATION_3);\n        final MultiMap<Polygon, Polygon> outersToInners = new MultiMap<>();\n        outersToInners.add(polygon1, polygon2);\n        outersToInners.add(polygon2, polygon2);\n        final MultiPolygon truth = new MultiPolygon(outersToInners);\n        Assert.assertEquals(truth, multiPolygon);\n        Assert.assertEquals(wkt, CONVERTER.convert(truth));\n    }\n\n    @Test\n    public void testConversionPolygon()\n    {\n        final String wkt = \"POLYGON ((68.763726 24.2889638, 68.7691521 24.2847937, \"\n                + \"68.7695996 24.2726051, 68.7686899 24.2558968, 68.763726 24.2889638))\";\n        final MultiPolygon multiPolygon = CONVERTER.backwardConvert(wkt);\n\n        final MultiPolygon truth = MultiPolygon\n                .forPolygon(new Polygon(LOCATION_1, LOCATION_2, LOCATION_3, LOCATION_4));\n        Assert.assertEquals(truth, multiPolygon);\n        Assert.assertEquals(wkt, CONVERTER.convert(truth));\n    }\n\n    @Test\n    public void testEmptyMultiPolygon()\n    {\n        final MultiMap<Polygon, Polygon> outersToInners = new MultiMap<>();\n        final MultiPolygon multiPolygon = new MultiPolygon(outersToInners);\n        Assert.assertEquals(\"POLYGON EMPTY\", multiPolygon.toWkt());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/converters/WktPolygonConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\n\n/**\n * @author matthieun\n */\npublic class WktPolygonConverterTest\n{\n    private static final WktPolygonConverter CONVERTER = new WktPolygonConverter();\n\n    private static final Location LOCATION_1 = Location.forString(\"24.2889638, 68.763726\");\n    private static final Location LOCATION_2 = Location.forString(\"24.2847937, 68.7691521\");\n    private static final Location LOCATION_3 = Location.forString(\"24.2726051, 68.7695996\");\n    private static final Location LOCATION_4 = Location.forString(\"24.2558968, 68.7686899\");\n\n    @Test\n    public void testConversionMultiPolygonSingleOuter()\n    {\n        final String wkt = \"MULTIPOLYGON (((68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, \"\n                + \"68.7686899 24.2558968, 68.763726 24.2889638)))\";\n        final Polygon polygon = CONVERTER.backwardConvert(wkt);\n        final Polygon truth = new Polygon(LOCATION_1, LOCATION_2, LOCATION_3, LOCATION_4);\n        Assert.assertEquals(truth, polygon);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testConversionMultiPolygonWithMultipleOuters()\n    {\n        final String wkt = \"MULTIPOLYGON (\"\n                + \"((68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, \"\n                + \"68.7686899 24.2558968, 68.763726 24.2889638), \"\n                + \"(68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, 68.763726 24.2889638)), \"\n                + \"((68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, 68.763726 24.2889638), \"\n                + \"(68.763726 24.2889638, 68.7691521 24.2847937, 68.7695996 24.2726051, 68.763726 24.2889638))\"\n                + \")\";\n        CONVERTER.backwardConvert(wkt);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/coordinates/CoordinatesTest.java",
    "content": "package org.openstreetmap.atlas.geography.coordinates;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Altitude;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Longitude;\n\n/**\n * Tests {@link EarthCenteredEarthFixedCoordinate} and {@link GeodeticCoordinate} functionality.\n *\n * @author mgostintsev\n */\npublic class CoordinatesTest\n{\n    @Test\n    public void testEarthCenteredEarthFixedCoordinateEquality()\n    {\n        final EarthCenteredEarthFixedCoordinate coordinate = new EarthCenteredEarthFixedCoordinate(\n                -576793.17, -5376363.47, 3372298.51);\n\n        final EarthCenteredEarthFixedCoordinate identicalCoordinate = new EarthCenteredEarthFixedCoordinate(\n                -576793.17, -5376363.47, 3372298.51);\n\n        final EarthCenteredEarthFixedCoordinate newCoordinate = new EarthCenteredEarthFixedCoordinate(\n                -576123.17, -5376992.47, 3372123.51);\n\n        Assert.assertEquals(coordinate, identicalCoordinate);\n        Assert.assertNotEquals(coordinate, newCoordinate);\n    }\n\n    @Test\n    public void testGeodeticCoordinateEquality()\n    {\n        final GeodeticCoordinate coordinate = new GeodeticCoordinate(Latitude.degrees(32.12345),\n                Longitude.degrees(-96.12345), Altitude.meters(500.0));\n\n        final GeodeticCoordinate identicalCoordinate = new GeodeticCoordinate(\n                Latitude.degrees(32.12345), Longitude.degrees(-96.12345), Altitude.meters(500.0));\n\n        final GeodeticCoordinate newCoordinate = new GeodeticCoordinate(Latitude.degrees(45.24252),\n                Longitude.degrees(-99.14141), Altitude.meters(700.0));\n\n        Assert.assertEquals(coordinate, identicalCoordinate);\n        Assert.assertNotEquals(coordinate, newCoordinate);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/ConcatenateGeoJsonCommandTest.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\n\nimport org.apache.commons.io.FileUtils;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * Test cases for ConcatenateGeoJsonFiles.\n * \n * @author rmegraw\n */\npublic class ConcatenateGeoJsonCommandTest\n{\n    @Rule\n    public TemporaryFolder tempFolder = new TemporaryFolder();\n\n    @Test\n    public void testConcatenateGeoJsonFiles() throws IOException\n    {\n        final String inputPath = new File(this.getClass().getResource(\"test1.geojson\").getPath())\n                .getParent();\n        final String outputPath = this.tempFolder.newFile().getAbsolutePath();\n\n        new ConcatenateGeoJsonCommand().runWithoutQuitting(\"-path=\" + inputPath, \"-mode=FILE\",\n                \"-output=\" + outputPath);\n\n        final File outputFile = new File(outputPath);\n        Assert.assertTrue(outputFile.exists());\n\n        Assert.assertTrue(FileUtils\n                .readFileToString(new File(this.getClass()\n                        .getResource(\"concatenated_geojson_files_expected\").getPath()),\n                        Charset.defaultCharset())\n                .equals(FileUtils.readFileToString(outputFile, Charset.defaultCharset())));\n\n    }\n\n    @Test\n    public void testConcatenateGeoJsonLines() throws IOException\n    {\n        final String inputPath = new File(this.getClass().getResource(\"test1.geojson\").getPath())\n                .getParent();\n        final String outputPath = this.tempFolder.newFile().getAbsolutePath();\n\n        new ConcatenateGeoJsonCommand().runWithoutQuitting(\"-path=\" + inputPath, \"-mode=LINE\",\n                \"-output=\" + outputPath, \"-filePrefix=\" + \"test\");\n\n        final File outputFile = new File(outputPath);\n        Assert.assertTrue(outputFile.exists());\n\n        Assert.assertTrue(FileUtils\n                .readFileToString(new File(this.getClass()\n                        .getResource(\"concatenated_geojson_files_expected\").getPath()),\n                        Charset.defaultCharset())\n                .equals(FileUtils.readFileToString(outputFile, Charset.defaultCharset())));\n\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/GeoJsonBuilderTest.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.GeometryWithProperties;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonBuilder.LocationIterableProperties;\nimport org.openstreetmap.atlas.streaming.readers.GeoJsonReader;\nimport org.openstreetmap.atlas.streaming.readers.json.serializers.PropertiesLocated;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * @author matthieun\n * @author mgostintsev\n * @author rmegraw\n */\n@SuppressWarnings(\"deprecation\")\npublic class GeoJsonBuilderTest\n{\n    @Test\n    public void testConsistency()\n    {\n        final Polygon polygon = new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6);\n        final GeoJsonObject object = new GeoJsonBuilder().create(polygon);\n        final Map<String, String> properties = new HashMap<>();\n        properties.put(\"property\", \"value\");\n        properties.put(\"property2\", \"value2\");\n        object.withNewProperties(properties);\n        // The GeoJsonReader reads Feature collections only\n        object.makeFeatureCollection();\n        final GeoJsonReader reader = new GeoJsonReader(new StringResource(object.toString()));\n        final PropertiesLocated item = reader.next();\n        Assert.assertEquals(polygon, item.getItem());\n    }\n\n    @Test\n    public void testCreateFeatureCollectionFromPropertiesLocated()\n    {\n        final Map<String, Object> properties = new HashMap<>();\n        properties.put(\"prop1\", \"foo\");\n        properties.put(\"prop2\", new Float[] { 1.0F, 2.0F, 3.0F });\n        properties.put(\"prop3\", 0);\n\n        final JsonObject propertiesObject = new JsonObject();\n        final Gson gson = new Gson();\n        properties.forEach((key, value) -> propertiesObject.add(key, gson.toJsonTree(value)));\n        propertiesObject.add(\"properties\", propertiesObject);\n\n        final PropertiesLocated propertiesLocated1 = new PropertiesLocated(PolyLine.TEST_POLYLINE,\n                propertiesObject);\n        final PropertiesLocated propertiesLocated2 = new PropertiesLocated(PolyLine.TEST_POLYLINE,\n                propertiesObject);\n\n        final GeoJsonObject featureCollection = new GeoJsonBuilder()\n                .createFeatureCollectionFromPropertiesLocated(\n                        Iterables.from(propertiesLocated1, propertiesLocated2));\n\n        Assert.assertEquals(\"FeatureCollection\",\n                featureCollection.jsonObject().get(\"type\").getAsString());\n        Assert.assertEquals(2,\n                featureCollection.jsonObject().get(\"features\").getAsJsonArray().size());\n        for (int i = 0; i < 2; i++)\n        {\n            Assert.assertEquals(\"Feature\", featureCollection.jsonObject().get(\"features\")\n                    .getAsJsonArray().get(i).getAsJsonObject().get(\"type\").getAsString());\n            Assert.assertEquals(\"LineString\",\n                    featureCollection.jsonObject().get(\"features\").getAsJsonArray().get(i)\n                            .getAsJsonObject().get(\"geometry\").getAsJsonObject().get(\"type\")\n                            .getAsString());\n            Assert.assertEquals(properties.get(\"prop1\"),\n                    featureCollection.jsonObject().get(\"features\").getAsJsonArray().get(i)\n                            .getAsJsonObject().get(\"properties\").getAsJsonObject().get(\"prop1\")\n                            .getAsString());\n            Assert.assertEquals(((Float[]) properties.get(\"prop2\")).length,\n                    featureCollection.jsonObject().get(\"features\").getAsJsonArray().get(i)\n                            .getAsJsonObject().get(\"properties\").getAsJsonObject().get(\"prop2\")\n                            .getAsJsonArray().size());\n            for (int j = i; j < 3; j++)\n            {\n                Assert.assertEquals(((Float[]) properties.get(\"prop2\"))[j],\n                        featureCollection.jsonObject().get(\"features\").getAsJsonArray().get(i)\n                                .getAsJsonObject().get(\"properties\").getAsJsonObject().get(\"prop2\")\n                                .getAsJsonArray().get(j).getAsFloat(),\n                        0D);\n            }\n            Assert.assertEquals(properties.get(\"prop3\"),\n                    featureCollection.jsonObject().get(\"features\").getAsJsonArray().get(i)\n                            .getAsJsonObject().get(\"properties\").getAsJsonObject().get(\"prop3\")\n                            .getAsInt());\n        }\n    }\n\n    @Test\n    public void testCreateFromGeometries()\n    {\n        final Map<String, Object> properties = new HashMap<>();\n        properties.put(\"property\", \"value\");\n        properties.put(\"property2\", \"value2\");\n        final List<GeometryWithProperties> items = new ArrayList<>();\n        items.add(new GeometryWithProperties(Location.TEST_1, properties));\n        items.add(new GeometryWithProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new GeometryWithProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        final GeoJsonObject object = new GeoJsonBuilder().createFromGeometriesWithProperties(items);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"FeatureCollection\\\",\\\"features\\\":[{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\"\"\n                        + \":[-122.009566,37.33531]},\\\"properties\\\":{\\\"property2\\\":\\\"value2\\\",\\\"property\\\":\\\"value\\\"}},{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\"\"\n                        + \":{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]]},\\\"properties\\\"\"\n                        + \":{\\\"property2\\\":\\\"value2\\\",\\\"property\\\":\\\"value\\\"}},{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\"\"\n                        + \":[[-122.031007,37.390535],[-122.028464,37.321628]]},\\\"properties\\\":{\\\"property2\\\":\\\"value2\\\",\\\"property\\\":\\\"value\\\"}}]}\",\n                object.toString());\n    }\n\n    @Test\n    public void testFeatureCollection()\n    {\n        final Map<String, String> properties = new HashMap<>();\n        properties.put(\"property\", \"value\");\n        properties.put(\"property2\", \"value2\");\n        final List<LocationIterableProperties> items = new ArrayList<>();\n        items.add(new LocationIterableProperties(Location.TEST_1, properties));\n        items.add(new LocationIterableProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new LocationIterableProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        final GeoJsonObject object = new GeoJsonBuilder().create(items);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"FeatureCollection\\\",\\\"features\\\":[{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\"\"\n                        + \":[-122.009566,37.33531]},\\\"properties\\\":{\\\"property2\\\":\\\"value2\\\",\\\"property\\\":\\\"value\\\"}},{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\"\"\n                        + \":{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]]},\\\"properties\\\"\"\n                        + \":{\\\"property2\\\":\\\"value2\\\",\\\"property\\\":\\\"value\\\"}},{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\"\"\n                        + \":[[-122.031007,37.390535],[-122.028464,37.321628]]},\\\"properties\\\":{\\\"property2\\\":\\\"value2\\\",\\\"property\\\":\\\"value\\\"}}]}\",\n                object.toString());\n    }\n\n    @Test\n    public void testGeometryCollectionFeature()\n    {\n        final Map<String, Object> properties = new HashMap<>();\n        final List<GeometryWithProperties> items = new ArrayList<>();\n        items.add(new GeometryWithProperties(Location.TEST_1, properties));\n        items.add(new GeometryWithProperties(Location.TEST_1, properties));\n        items.add(new GeometryWithProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new GeometryWithProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new GeometryWithProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        items.add(new GeometryWithProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        final GeoJsonObject object = new GeoJsonBuilder().createGeometryCollectionFeature(items);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"GeometryCollection\\\",\\\"geometries\\\":[{\\\"type\\\":\\\"MultiPoint\\\",\\\"coordinates\\\":[[-122.009566,37.33531],[-122.009566,37.33531]]},{\\\"type\\\":\\\"MultiPolygon\\\",\\\"coordinates\\\":[[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]],[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]]]},{\\\"type\\\":\\\"MultiLineString\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628]],[[-122.031007,37.390535],[-122.028464,37.321628]]]}]}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testGeometryCollectionFeatureMultipleForm()\n    {\n        final Map<String, Object> properties = new HashMap<>();\n        final List<GeometryWithProperties> items = new ArrayList<>();\n        items.add(new GeometryWithProperties(Location.TEST_1, properties));\n        items.add(new GeometryWithProperties(Location.TEST_1, properties));\n        items.add(new GeometryWithProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new GeometryWithProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new GeometryWithProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        items.add(new GeometryWithProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        final GeoJsonObject object = new GeoJsonBuilder().createGeometryCollectionFeature(items);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"GeometryCollection\\\",\\\"geometries\\\":[{\\\"type\\\":\\\"MultiPoint\\\",\\\"coordinates\\\":[[-122.009566,37.33531],[-122.009566,37.33531]]},{\\\"type\\\":\\\"MultiPolygon\\\",\\\"coordinates\\\":[[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]],[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]]]},{\\\"type\\\":\\\"MultiLineString\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628]],[[-122.031007,37.390535],[-122.028464,37.321628]]]}]}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testGeometryCollectionFeatureSingularForm()\n    {\n        final Map<String, Object> properties = new HashMap<>();\n        final List<GeometryWithProperties> items = new ArrayList<>();\n        items.add(new GeometryWithProperties(Location.TEST_1, properties));\n        items.add(new GeometryWithProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new GeometryWithProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        final GeoJsonObject object = new GeoJsonBuilder().createGeometryCollectionFeature(items);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"GeometryCollection\\\",\\\"geometries\\\":[{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[-122.009566,37.33531]},{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544]]]},{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-122.031007,37.390535],[-122.028464,37.321628]]}]}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testGeometryCollectionMultipleForm()\n    {\n        final Map<String, String> properties = new HashMap<>();\n        final List<LocationIterableProperties> items = new ArrayList<>();\n        items.add(new LocationIterableProperties(Location.TEST_1, properties));\n        items.add(new LocationIterableProperties(Location.TEST_1, properties));\n        items.add(new LocationIterableProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new LocationIterableProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new LocationIterableProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        items.add(new LocationIterableProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        final GeoJsonObject object = new GeoJsonBuilder().createGeometryCollection(items);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"GeometryCollection\\\",\\\"geometries\\\":[{\\\"type\\\":\\\"MultiPoint\\\",\\\"coordinates\\\":[[-122.009566,37.33531],[-122.009566,37.33531]]},{\\\"type\\\":\\\"MultiPolygon\\\",\\\"coordinates\\\":[[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]],[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]]]},{\\\"type\\\":\\\"MultiLineString\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628]],[[-122.031007,37.390535],[-122.028464,37.321628]]]}]}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testGeometryCollectionSingularForm()\n    {\n        final Map<String, String> properties = new HashMap<>();\n        final List<LocationIterableProperties> items = new ArrayList<>();\n        items.add(new LocationIterableProperties(Location.TEST_1, properties));\n        items.add(new LocationIterableProperties(\n                new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6), properties));\n        items.add(new LocationIterableProperties(new PolyLine(Location.TEST_5, Location.TEST_2),\n                properties));\n        final GeoJsonObject object = new GeoJsonBuilder().createGeometryCollection(items);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"GeometryCollection\\\",\\\"geometries\\\":[{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[-122.009566,37.33531]},{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544]]]},{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-122.031007,37.390535],[-122.028464,37.321628]]}]}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testGeometryWithParentMember()\n    {\n        final JsonObject propertiesObject = new JsonObject();\n        final JsonObject parentProperties = new JsonObject();\n        final JsonObject fixSuggestions = new JsonObject();\n        final Map<String, Object> parentMembers = new HashMap<>();\n        final PropertiesLocated propertiesLocated1 = new PropertiesLocated(PolyLine.TEST_POLYLINE,\n                propertiesObject);\n        final PropertiesLocated propertiesLocated2 = new PropertiesLocated(PolyLine.TEST_POLYLINE,\n                propertiesObject);\n\n        parentProperties.addProperty(\"instructions\", \"This feature is no good\");\n        parentProperties.addProperty(\"generator\", \"Atlas Checks\");\n        fixSuggestions.addProperty(\"Edge12345\", \"\");\n        parentMembers.put(\"properties\", parentProperties);\n        parentMembers.put(\"fix_suggestions\", fixSuggestions);\n\n        // Create the FeatureCollection object with extra \"properties\" & \"fix_suggestions\" members\n        final GeoJsonObject featureCollection = new GeoJsonBuilder()\n                .createFeatureCollectionFromPropertiesLocated(\n                        Iterables.from(propertiesLocated1, propertiesLocated2))\n                // Test both functions\n                .withNewParentMember(\"test\", \"value\").withNewParentMembers(parentMembers);\n\n        final JsonObject propertiesMember = featureCollection.jsonObject()\n                .getAsJsonObject(\"properties\");\n        final JsonObject fixSuggestionsMember = featureCollection.jsonObject()\n                .getAsJsonObject(\"fix_suggestions\");\n\n        Assert.assertEquals(\"This feature is no good\",\n                propertiesMember.get(\"instructions\").getAsString());\n        Assert.assertEquals(\"Atlas Checks\", propertiesMember.get(\"generator\").getAsString());\n        Assert.assertEquals(5, featureCollection.jsonObject().entrySet().size());\n        Assert.assertEquals(2, propertiesMember.entrySet().size());\n        Assert.assertEquals(1, fixSuggestionsMember.entrySet().size());\n    }\n\n    @Test\n    public void testLineString()\n    {\n        final GeoJsonObject object = new GeoJsonBuilder()\n                .create(new PolyLine(Location.TEST_5, Location.TEST_2));\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-122.031007,37.390535],[-122.028464,37.321628]]}}\",\n                object.toString());\n\n        final Map<String, String> properties = new HashMap<>();\n        properties.put(\"property\", \"value\");\n        properties.put(\"property2\", \"value2\");\n        object.withNewProperties(properties);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-122.031007,37.390535],[-122.028464,37.321628]]},\\\"properties\\\":{\\\"property2\\\":\\\"value2\\\",\\\"property\\\":\\\"value\\\"}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testLocation()\n    {\n        final GeoJsonObject object = new GeoJsonBuilder().create(Location.TEST_5);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[-122.031007,37.390535]}}\",\n                object.toString());\n\n        object.withNewProperty(\"property\", \"value\");\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[-122.031007,37.390535]},\\\"properties\\\":{\\\"property\\\":\\\"value\\\"}}\",\n                object.toString());\n        object.withNewProperty(\"property2\", \"value2\");\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[-122.031007,37.390535]},\\\"properties\\\":{\\\"property\\\":\\\"value\\\",\\\"property2\\\":\\\"value2\\\"}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testMultiLineString()\n    {\n        final List<PolyLine> polyLines = new ArrayList<>();\n        polyLines.add(new PolyLine(Location.TEST_5, Location.TEST_2));\n        polyLines.add(new PolyLine(Location.COLOSSEUM, Location.EIFFEL_TOWER));\n\n        final GeoJsonObject object = new GeoJsonBuilder().createMultiLineStrings(polyLines);\n\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"MultiLineString\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628]],[[12.49234,41.890224],[2.294495,48.858241]]]}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testMultiPoint()\n    {\n        final List<Location> locations = Arrays.asList(Location.EIFFEL_TOWER, Location.COLOSSEUM);\n        final GeoJsonObject object = new GeoJsonBuilder().create(locations,\n                GeoJsonType.MULTI_POINT);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"MultiPoint\\\",\\\"coordinates\\\":[[2.294495,48.858241],[12.49234,41.890224]]}}\",\n                object.toString());\n\n        object.withNewProperty(\"property\", \"value\");\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"MultiPoint\\\",\\\"coordinates\\\":[[2.294495,48.858241],[12.49234,41.890224]]},\\\"properties\\\":{\\\"property\\\":\\\"value\\\"}}\",\n                object.toString());\n        object.withNewProperty(\"property2\", \"value2\");\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"MultiPoint\\\",\\\"coordinates\\\":[[2.294495,48.858241],[12.49234,41.890224]]},\\\"properties\\\":{\\\"property\\\":\\\"value\\\",\\\"property2\\\":\\\"value2\\\"}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testMultiPolygon()\n    {\n        final List<Polygon> polygons = new ArrayList<>();\n        polygons.add(new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6));\n        polygons.add(new Polygon(Location.COLOSSEUM, Location.EIFFEL_TOWER, Location.TEST_7));\n\n        final GeoJsonObject object = new GeoJsonBuilder().createMultiPolygons(polygons);\n\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"MultiPolygon\\\",\\\"coordinates\\\":[[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]],[[[12.49234,41.890224],[2.294495,48.858241],[-122.0304871,37.3314171],[12.49234,41.890224]]]]}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testPolygon()\n    {\n        final GeoJsonObject object = new GeoJsonBuilder()\n                .create(new Polygon(Location.TEST_5, Location.TEST_2, Location.TEST_6));\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]]}}\",\n                object.toString());\n\n        final Map<String, String> properties = new HashMap<>();\n        properties.put(\"property\", \"value\");\n        properties.put(\"property2\", \"value2\");\n        object.withNewProperties(properties);\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[-122.031007,37.390535],[-122.028464,37.321628],[-122.033948,37.32544],[-122.031007,37.390535]]]},\\\"properties\\\":{\\\"property2\\\":\\\"value2\\\",\\\"property\\\":\\\"value\\\"}}\",\n                object.toString());\n    }\n\n    @Test\n    public void testPropertiesObjects()\n    {\n        final List<Integer> list = Arrays.asList(new Integer[] { 1, 2, 3 });\n        final Map<String, Object> properties = new HashMap<>();\n        properties.put(\"prop1\", 1);\n        properties.put(\"prop2\", list);\n        properties.put(\"prop3\", \"mystring\");\n        final GeometryWithProperties geometryWithProperties = new GeometryWithProperties(\n                PolyLine.TEST_POLYLINE, properties);\n        final JsonObject jsonObject = new GeoJsonBuilder().create(geometryWithProperties);\n        Assert.assertEquals(1,\n                jsonObject.get(\"properties\").getAsJsonObject().get(\"prop1\").getAsInt());\n        final List<Integer> resultList = new ArrayList<>();\n        for (final JsonElement element : jsonObject.get(\"properties\").getAsJsonObject().get(\"prop2\")\n                .getAsJsonArray())\n        {\n            resultList.add(element.getAsInt());\n        }\n        Assert.assertEquals(list, resultList);\n        Assert.assertEquals(\"mystring\",\n                jsonObject.get(\"properties\").getAsJsonObject().get(\"prop3\").getAsString());\n    }\n\n    @Test\n    public void testToGeometryWithProperties()\n    {\n        final Map<String, String> stringProperties = new HashMap<>();\n        stringProperties.put(\"prop1\", \"val1\");\n        stringProperties.put(\"prop2\", \"val2\");\n        final LocationIterableProperties locationIterableProperties = new LocationIterableProperties(\n                PolyLine.TEST_POLYLINE, stringProperties);\n        final GeometryWithProperties geometryWithProperties = GeoJsonBuilder\n                .toGeometryWithProperties(locationIterableProperties);\n        Assert.assertEquals(PolyLine.TEST_POLYLINE, geometryWithProperties.getGeometry());\n        for (final Entry<String, String> stringPropertiesEntry : stringProperties.entrySet())\n        {\n            Assert.assertEquals(stringPropertiesEntry.getValue(),\n                    geometryWithProperties.getProperties().get(stringPropertiesEntry.getKey()));\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/GeoJsonUtilsTest.java",
    "content": "package org.openstreetmap.atlas.geography.geojson;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\n/**\n * Test functions in GeoJsonUtils\n *\n * @author hallahan\n */\npublic class GeoJsonUtilsTest\n{\n    private static final String GEOMETRY = \"{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[-77.7951565,-1.4317173]}\";\n    private static final String PROPERTIES = \"{\\\"last_edit_user_name\\\":\\\"Lover4\\\",\\\"last_edit_changeset\\\":\\\"35090172\\\",\\\"last_edit_time\\\":\\\"1446676342000\\\",\\\"last_edit_user_id\\\":\\\"2789671\\\",\\\"iso_country_code\\\":\\\"ECU\\\",\\\"last_edit_version\\\":\\\"2\\\",\\\"identifier\\\":3390274778000000,\\\"osmIdentifier\\\":3390274778,\\\"itemType\\\":\\\"NODE\\\",\\\"shard\\\":\\\"9-144-256\\\"}\";\n    private static final String FEATURE = \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[-77.7951565,-1.4317173]},\\\"properties\\\":{\\\"last_edit_user_name\\\":\\\"Lover4\\\",\\\"last_edit_changeset\\\":\\\"35090172\\\",\\\"last_edit_time\\\":\\\"1446676342000\\\",\\\"last_edit_user_id\\\":\\\"2789671\\\",\\\"iso_country_code\\\":\\\"ECU\\\",\\\"last_edit_version\\\":\\\"2\\\",\\\"identifier\\\":3390274778000000,\\\"osmIdentifier\\\":3390274778,\\\"itemType\\\":\\\"NODE\\\",\\\"shard\\\":\\\"9-144-256\\\"}}\";\n\n    private static final String BOUNDS_POLYGON_GEOJSON = \"{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[-122.031905,37.328167],[-122.031905,37.330394],[-122.029051,37.330394],[-122.029051,37.328167],[-122.031905,37.328167]]]}\";\n\n    private static final PolyLine EDGE_POLYLINE = PolyLine.wkt(\n            \"LINESTRING(-122.01024413108826 37.33667453131461,-122.00722932815552 37.33667453131461,-122.00610280036926 37.334942852083415,-122.00563073158263 37.33617976989369,-122.00483679771423 37.33639302952647,-122.00469732284544 37.334959913157256,-122.0042359828949 37.33623095226076)\");\n    private static final String FEATURE_COLLECTION_STRING = \"{\\\"type\\\":\\\"FeatureCollection\\\",\\\"features\\\":[{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-122.0102441,37.3366745],[-122.0072293,37.3366745],[-122.0061028,37.3349429],[-122.0056307,37.3361798],[-122.0048368,37.336393],[-122.0046973,37.3349599],[-122.004236,37.336231]]},\\\"properties\\\":{\\\"TheKey\\\":\\\"TheValue\\\",\\\"identifier\\\":1,\\\"osmIdentifier\\\":0,\\\"itemType\\\":\\\"EDGE\\\",\\\"startNode\\\":0,\\\"endNode\\\":1}},{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-122.004236,37.336231],[-122.0046973,37.3349599],[-122.0048368,37.336393],[-122.0056307,37.3361798],[-122.0061028,37.3349429],[-122.0072293,37.3366745],[-122.0102441,37.3366745]]},\\\"properties\\\":{\\\"TheKey\\\":\\\"TheValue\\\",\\\"identifier\\\":-1,\\\"osmIdentifier\\\":0,\\\"itemType\\\":\\\"EDGE\\\",\\\"startNode\\\":1,\\\"endNode\\\":0}}],\\\"properties\\\":{\\\"aProperty\\\":\\\"aValue\\\"}}\";\n    private static final String GEOMETRY_COLLECTION_STRING = \"{\\\"type\\\":\\\"GeometryCollection\\\",\\\"geometries\\\":[{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-122.0102441,37.3366745],[-122.0072293,37.3366745],[-122.0061028,37.3349429],[-122.0056307,37.3361798],[-122.0048368,37.336393],[-122.0046973,37.3349599],[-122.004236,37.336231]]},{\\\"type\\\":\\\"LineString\\\",\\\"coordinates\\\":[[-122.004236,37.336231],[-122.0046973,37.3349599],[-122.0048368,37.336393],[-122.0056307,37.3361798],[-122.0061028,37.3349429],[-122.0072293,37.3366745],[-122.0102441,37.3366745]]}]}\";\n\n    private static final double LONGITUDE = -77.7951565;\n    private static final double LATITUDE = -1.4317173;\n    private static final String COORDINATE = \"[-77.7951565,-1.4317173]\";\n\n    @Test\n    public void testBoundsToPolygonGeometry()\n    {\n        final Rectangle rectangle = Rectangle.TEST_RECTANGLE;\n        final JsonObject polygonGeometry = GeoJsonUtils.boundsToPolygonGeometry(rectangle);\n        Assert.assertEquals(BOUNDS_POLYGON_GEOJSON, polygonGeometry.toString());\n    }\n\n    @Test\n    public void testCoordinate()\n    {\n        final JsonArray coordinate = GeoJsonUtils.coordinate(LONGITUDE, LATITUDE);\n        Assert.assertEquals(COORDINATE, coordinate.toString());\n    }\n\n    @Test\n    public void testFeature()\n    {\n        final JsonParser parser = new JsonParser();\n        final JsonObject geometry = parser.parse(GEOMETRY).getAsJsonObject();\n        final JsonObject properties = parser.parse(PROPERTIES).getAsJsonObject();\n        final JsonObject feature = GeoJsonUtils.feature(geometry, properties);\n        Assert.assertEquals(FEATURE, feature.toString());\n    }\n\n    @Test\n    public void testFeatureCollection()\n    {\n        final Edge edge = new CompleteEdge(1L, EDGE_POLYLINE,\n                Collections.singletonMap(\"TheKey\", \"TheValue\"), 0L, 1L, Collections.emptySet());\n        final Edge reverseEdge = new CompleteEdge(-1L, EDGE_POLYLINE.reversed(),\n                Collections.singletonMap(\"TheKey\", \"TheValue\"), 1L, 0L, Collections.emptySet());\n\n        final JsonObject featureCollection = GeoJsonUtils\n                .featureCollection(new GeoJsonFeatureCollection<Edge>()\n                {\n                    @Override\n                    public Iterable<Edge> getGeoJsonObjects()\n                    {\n                        return Arrays.asList(edge, reverseEdge);\n                    }\n\n                    @Override\n                    public JsonObject getGeoJsonProperties()\n                    {\n                        final JsonObject properties = new JsonObject();\n                        properties.addProperty(\"aProperty\", \"aValue\");\n                        return properties;\n                    }\n                });\n        Assert.assertEquals(FEATURE_COLLECTION_STRING, featureCollection.toString());\n    }\n\n    @Test\n    public void testGeometryCollection()\n    {\n\n        final Edge edge = new CompleteEdge(1L, EDGE_POLYLINE,\n                Collections.singletonMap(\"TheKey\", \"TheValue\"), 0L, 1L, Collections.emptySet());\n        final Edge reverseEdge = new CompleteEdge(-1L, EDGE_POLYLINE.reversed(),\n                Collections.singletonMap(\"TheKey\", \"TheValue\"), 1L, 0L, Collections.emptySet());\n\n        final JsonObject geometryCollection = GeoJsonUtils\n                .geometry(new GeojsonGeometryCollection<PolyLine>()\n                {\n                    @Override\n                    public Iterable<PolyLine> getGeoJsonObjects()\n                    {\n                        return Arrays.asList(EDGE_POLYLINE, EDGE_POLYLINE.reversed());\n                    }\n                });\n        final JsonObject geometryCollectionFromEdges = GeoJsonUtils\n                .geometry(new GeojsonGeometryCollection<Edge>()\n                {\n                    @Override\n                    public Iterable<Edge> getGeoJsonObjects()\n                    {\n                        return Arrays.asList(edge, reverseEdge);\n                    }\n                });\n\n        Assert.assertEquals(GEOMETRY_COLLECTION_STRING, geometryCollection.toString());\n        Assert.assertEquals(GEOMETRY_COLLECTION_STRING, geometryCollectionFromEdges.toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/parser/domain/bbox/DimensionsTest.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.bbox;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Yazad Khambata\n */\npublic class DimensionsTest\n{\n    @Test\n    public void test()\n    {\n        Assert.assertEquals(Dimensions.TWO_DIMENSIONAL.getNumberOfCoordinates(), 4);\n        Assert.assertEquals(Dimensions.THREE_DIMENSIONAL.getNumberOfCoordinates(), 6);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/parser/domain/geometry/coordinate/CoordinatesTest.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.coordinate;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Yazad Khambata\n */\npublic class CoordinatesTest\n{\n\n    private static <T> List<T> list(final T... items)\n    {\n        return Arrays.asList(items);\n    }\n\n    @Test\n    public void forLineString()\n    {\n        final Coordinates<List<Position>> coordinates = Coordinates.forLineString(\n                // Line\n                list(\n                        // Points\n                        list(1d, 2d), list(3d, 4d), list(5d, 6d), list(7d, 8d)));\n\n        Assert.assertTrue(coordinates.getValue() instanceof List);\n        Assert.assertEquals(4, coordinates.getValue().size());\n        Assert.assertEquals((Double) 1d, coordinates.getValue().get(0).getCoordinate1());\n        Assert.assertEquals((Double) 2d, coordinates.getValue().get(0).getCoordinate2());\n\n        Assert.assertEquals((Double) 3d, coordinates.getValue().get(1).getCoordinate1());\n        Assert.assertEquals((Double) 4d, coordinates.getValue().get(1).getCoordinate2());\n\n        Assert.assertEquals((Double) 5d, coordinates.getValue().get(2).getCoordinate1());\n        Assert.assertEquals((Double) 6d, coordinates.getValue().get(2).getCoordinate2());\n\n        Assert.assertEquals((Double) 7d, coordinates.getValue().get(3).getCoordinate1());\n        Assert.assertEquals((Double) 8d, coordinates.getValue().get(3).getCoordinate2());\n    }\n\n    @Test\n    public void forMultiLineString()\n    {\n        final Coordinates<List<List<Position>>> coordinates = Coordinates.forMultiLineString(\n                // Lines\n                list(\n                        // Line1\n                        list(\n                                // Points1\n                                list(1d, 2d), list(3d, 4d), list(5d, 6d), list(7d, 8d)),\n                        // Line2\n                        list(\n                                // Points2\n                                list(11d, 21d), list(31d, 41d), list(51d, 61d), list(71d, 81d)),\n                        // Line3\n                        list(\n                                // Points3\n                                list(12d, 22d), list(32d, 42d), list(52d, 62d), list(72d, 82d)),\n                        // Line4\n                        list(\n                                // Points4\n                                list(13d, 23d), list(33d, 43d), list(53d, 63d), list(73d, 83d))));\n\n        Assert.assertTrue(coordinates.getValue() instanceof List);\n        Assert.assertEquals(4, coordinates.getValue().size());\n        coordinates.getValue().stream().forEach(line -> Assert.assertEquals(4, line.size()));\n\n        Assert.assertEquals((Double) 42d, coordinates.getValue().get(2).get(1).getCoordinate2());\n    }\n\n    @Test\n    public void forMultiPoint()\n    {\n        final Coordinates<List<Position>> coordinates = Coordinates\n                .forMultiPoint(list(list(1d, 2d), list(3d, 4d), list(5d, 6d), list(7d, 8d)));\n        Assert.assertTrue(coordinates.getValue() instanceof List);\n        Assert.assertEquals(4, coordinates.getValue().size());\n        Assert.assertEquals((Double) 1d, coordinates.getValue().get(0).getCoordinate1());\n        Assert.assertEquals((Double) 2d, coordinates.getValue().get(0).getCoordinate2());\n\n        Assert.assertEquals((Double) 3d, coordinates.getValue().get(1).getCoordinate1());\n        Assert.assertEquals((Double) 4d, coordinates.getValue().get(1).getCoordinate2());\n\n        Assert.assertEquals((Double) 5d, coordinates.getValue().get(2).getCoordinate1());\n        Assert.assertEquals((Double) 6d, coordinates.getValue().get(2).getCoordinate2());\n\n        Assert.assertEquals((Double) 7d, coordinates.getValue().get(3).getCoordinate1());\n        Assert.assertEquals((Double) 8d, coordinates.getValue().get(3).getCoordinate2());\n    }\n\n    @Test\n    public void forMultiPolygon()\n    {\n        final Coordinates<List<List<List<Position>>>> coordinates = Coordinates.forMultiPolygon(\n                // Multi-Polygon\n                list(\n                        // Polygons\n                        list(\n                                // Polygon1\n                                list(\n                                        // Points1\n                                        list(1d, 2d), list(3d, 4d), list(5d, 6d), list(7d, 8d)),\n                                // Polygon2\n                                list(\n                                        // Points2\n                                        list(11d, 21d), list(31d, 41d), list(51d, 61d),\n                                        list(71d, 81d)),\n                                // Polygon3\n                                list(\n                                        // Points3\n                                        list(12d, 22d), list(32d, 42d), list(52d, 62d),\n                                        list(72d, 82d)),\n                                // Polygon4\n                                list(\n                                        // Points4\n                                        list(13d, 23d), list(33d, 43d), list(53d, 63d),\n                                        list(73d, 83d)))));\n\n        Assert.assertTrue(coordinates.getValue() instanceof List);\n        Assert.assertEquals(4, coordinates.getValue().get(0).size());\n        coordinates.getValue().get(0).stream().forEach(line -> Assert.assertEquals(4, line.size()));\n\n        Assert.assertEquals((Double) 42d,\n                coordinates.getValue().get(0).get(2).get(1).getCoordinate2());\n    }\n\n    @Test\n    public void forPoint()\n    {\n        final Coordinates<Position> coordinates = Coordinates.forPoint(list(1d, 2d));\n        Assert.assertTrue(coordinates.getValue() instanceof Position);\n        Assert.assertEquals((Double) 1d, coordinates.getValue().getCoordinate1());\n        Assert.assertEquals((Double) 2d, coordinates.getValue().getCoordinate2());\n    }\n\n    @Test\n    public void forPolygon()\n    {\n        final Coordinates<List<Position>> coordinates = Coordinates.forLineString(\n                // Polygon\n                list(\n                        // Points\n                        list(1d, 2d), list(3d, 4d), list(5d, 6d), list(7d, 8d)));\n\n        Assert.assertTrue(coordinates.getValue() instanceof List);\n        Assert.assertEquals(4, coordinates.getValue().size());\n        Assert.assertEquals((Double) 1d, coordinates.getValue().get(0).getCoordinate1());\n        Assert.assertEquals((Double) 2d, coordinates.getValue().get(0).getCoordinate2());\n\n        Assert.assertEquals((Double) 3d, coordinates.getValue().get(1).getCoordinate1());\n        Assert.assertEquals((Double) 4d, coordinates.getValue().get(1).getCoordinate2());\n\n        Assert.assertEquals((Double) 5d, coordinates.getValue().get(2).getCoordinate1());\n        Assert.assertEquals((Double) 6d, coordinates.getValue().get(2).getCoordinate2());\n\n        Assert.assertEquals((Double) 7d, coordinates.getValue().get(3).getCoordinate1());\n        Assert.assertEquals((Double) 8d, coordinates.getValue().get(3).getCoordinate2());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/parser/impl/jackson/AbstractGeoJsonParserJacksonImplTestBase.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.impl.jackson;\n\nimport java.nio.charset.Charset;\n\nimport org.apache.commons.io.IOUtils;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.GeoJsonItem;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Automatically loads the appropriate JSON file in the classpath under\n * {@link AbstractGeoJsonParserJacksonImplTestBase#BASE_CLASSPATH}. It looks for the file that\n * shares the same name as the test method.\n *\n * @author Yazad Khambata\n */\npublic class AbstractGeoJsonParserJacksonImplTestBase\n{\n    public static final String BASE_CLASSPATH = \"org/openstreetmap/atlas/geography/geojson/parser/\";\n    public static final String EXTENSION = \".json\";\n    private static final Logger log = LoggerFactory\n            .getLogger(AbstractGeoJsonParserJacksonImplTestBase.class);\n\n    protected String extractJsonForExtension()\n    {\n        final String callingMethodName = Thread.currentThread().getStackTrace()[2].getMethodName();\n\n        return extractJsonForExtension(callingMethodName);\n    }\n\n    protected GeoJsonItem toGeoJsonItem()\n    {\n        // Do not refactor into another method.\n        final String callingMethodName = Thread.currentThread().getStackTrace()[2].getMethodName();\n\n        log.info(\"calling method: {}.\", callingMethodName);\n\n        final String json = extractJsonForExtension(callingMethodName);\n\n        log.info(\"{} json:: {}.\", callingMethodName, json);\n\n        final GeoJsonItem geoJsonItem = GeoJsonParserJacksonImpl.INSTANCE.deserialize(json);\n        log.info(\"{} geoJsonItem:: {}.\", callingMethodName, geoJsonItem);\n        return geoJsonItem;\n    }\n\n    private String extractJson(final String filePath)\n    {\n        try\n        {\n            return IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream(filePath),\n                    Charset.defaultCharset());\n        }\n        catch (final Exception e)\n        {\n            throw new RuntimeException(filePath, e);\n        }\n    }\n\n    private String extractJsonForExtension(final String callingMethodName)\n    {\n        final String filePath = toFilePath(callingMethodName);\n        return extractJson(filePath);\n    }\n\n    private String toFilePath(final String callingMethodName)\n    {\n        return new StringBuilder(BASE_CLASSPATH).append(callingMethodName).append(EXTENSION)\n                .toString();\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/parser/impl/jackson/GeoJsonParserJacksonImplExtensionsTest.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.impl.jackson;\n\nimport java.io.Serializable;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.jupiter.api.Assertions;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.geojson.parser.GeoJsonParser;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.properties.ext.change.Description;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.properties.ext.change.Descriptor;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.properties.ext.change.FeatureChangeProperties;\nimport org.openstreetmap.atlas.geography.geojson.parser.testdomain.BeanA;\nimport org.openstreetmap.atlas.geography.geojson.parser.testdomain.BeanB;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Yazad Khambata\n */\npublic class GeoJsonParserJacksonImplExtensionsTest extends AbstractGeoJsonParserJacksonImplTestBase\n{\n    private static final Logger log = LoggerFactory\n            .getLogger(GeoJsonParserJacksonImplExtensionsTest.class);\n\n    @Test\n    public void beanA()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final BeanA beanA = geoJsonParser.deserializeExtension(json, BeanA.class);\n        log.info(\"beanA:: {}.\", beanA);\n\n        Assert.assertNotNull(beanA);\n        Assert.assertFalse(beanA.getTags().isEmpty());\n    }\n\n    @Test\n    public void beanBWithArray()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final BeanB beanB = geoJsonParser.deserializeExtension(json, BeanB.class);\n        log.info(\"beanB:: {}.\", beanB);\n\n        Assert.assertNotNull(beanB);\n        Assert.assertFalse(beanB.getBeanA().getTags().isEmpty());\n        Assert.assertEquals(2, beanB.getBeanAs().length);\n    }\n\n    @Test\n    public void beanBWithoutArray()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final BeanB beanB = geoJsonParser.deserializeExtension(json, BeanB.class);\n        log.info(\"beanB:: {}.\", beanB);\n\n        Assert.assertNotNull(beanB);\n        Assert.assertFalse(beanB.getBeanA().getTags().isEmpty());\n    }\n\n    @Test\n    public void description1()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final Description description = geoJsonParser.deserializeExtension(json, Description.class);\n        log.info(\"description:: {}.\", description);\n\n        Assert.assertNotNull(description);\n        Assert.assertEquals(9, description.getDescriptors().length);\n    }\n\n    @Test\n    public void descriptor1()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final Descriptor descriptor = geoJsonParser.deserializeExtension(json, Descriptor.class);\n        log.info(\"descriptor:: {}.\", descriptor);\n\n        Assert.assertNotNull(descriptor);\n        Assert.assertEquals(\"ADD\", descriptor.getType());\n    }\n\n    @Test\n    public void descriptor2()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final Descriptor descriptor = geoJsonParser.deserializeExtension(json, Descriptor.class);\n        log.info(\"descriptor:: {}.\", descriptor);\n\n        Assert.assertNotNull(descriptor);\n        Assert.assertEquals(\"UPDATE\", descriptor.getType());\n    }\n\n    @Test\n    public void descriptor3()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final Descriptor descriptor = geoJsonParser.deserializeExtension(json, Descriptor.class);\n        log.info(\"descriptor:: {}.\", descriptor);\n\n        Assert.assertNotNull(descriptor);\n        Assert.assertEquals(\"b\", descriptor.getKey());\n        Assert.assertEquals(\"2a\", descriptor.getValue());\n    }\n\n    @Test\n    public void featureChangeProperties()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final FeatureChangeProperties featureChangeProperties = geoJsonParser\n                .deserializeExtension(json, FeatureChangeProperties.class);\n        log.info(\"featureChangeProperties:: {}.\", featureChangeProperties);\n\n        Assert.assertEquals(9, featureChangeProperties.getDescription().getDescriptors().length);\n        Assert.assertFalse(featureChangeProperties.getWKT().isEmpty());\n        Assert.assertFalse(featureChangeProperties.getBboxWKT().isEmpty());\n    }\n\n    /**\n     * Ensure that an exception is thrown when a class that requires arguments on instantiation is\n     * given as an argument\n     */\n    @Test\n    public void featureChangePropertiesBad1()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final Exception exception = Assertions.assertThrows(Exception.class,\n                () -> geoJsonParser.deserializeExtension(json,\n                        TestClassWithFieldRequiringInstantiation.class),\n                \"field name `completeEdge` should have caused a failure.\");\n\n        final String message1 = exception.getCause().getMessage();\n        Assertions.assertTrue(message1.startsWith(\"Population failed. propertyDescriptor name\"),\n                message1);\n        final String message2 = exception.getCause().getCause().getCause().getMessage();\n        Assertions.assertEquals(NoSuchMethodException.class,\n                exception.getCause().getCause().getCause().getClass());\n        Assertions.assertEquals(\"java.lang.Class.<init>()\", message2);\n    }\n\n    @Test\n    public void featureChangePropertiesBad2()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        try\n        {\n            final FeatureChangeProperties featureChangeProperties = geoJsonParser\n                    .deserializeExtension(json, FeatureChangeProperties.class);\n        }\n        catch (final Exception e)\n        {\n            final String message1 = e.getCause().getMessage();\n            Assert.assertTrue(\n                    message1.startsWith(\"Population failed. propertyDescriptor name: relations\"));\n            Assert.assertEquals(ClassCastException.class, e.getCause().getCause().getClass());\n            return;\n        }\n\n        Assert.fail(\"data data value should have caused a failure.\");\n    }\n\n    @Test\n    public void featureChangePropertiesRelationMemberDescriptor()\n    {\n        final String json = extractJsonForExtension();\n\n        final GeoJsonParser geoJsonParser = GeoJsonParserJacksonImpl.INSTANCE;\n\n        final FeatureChangeProperties featureChangeProperties = geoJsonParser\n                .deserializeExtension(json, FeatureChangeProperties.class);\n        log.info(\"featureChangeProperties:: {}.\", featureChangeProperties);\n\n        final Descriptor descriptor = featureChangeProperties.getDescription().getDescriptors()[0];\n\n        Assert.assertEquals(402306209000000L, (long) descriptor.getId());\n        Assert.assertEquals(\"NODE\", descriptor.getItemType());\n        Assert.assertEquals(\"via\", descriptor.getRole());\n    }\n\n    /**\n     * Used exclusively for {@link #featureChangePropertiesBad1()}\n     */\n    public static final class TestClassWithFieldRequiringInstantiation implements Serializable\n    {\n        private Class<CompleteEdge> completeEdgeClass;\n\n        public TestClassWithFieldRequiringInstantiation()\n        {\n            // Do nothing -- our mapper expects the constructor to be public\n        }\n\n        public Class<CompleteEdge> getCompleteEdgeClass()\n        {\n            return this.completeEdgeClass;\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/parser/impl/jackson/GeoJsonParserJacksonImplTest.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.impl.jackson;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.base.GeoJsonItem;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.bbox.Bbox2D;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.bbox.Bbox3D;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.feature.Feature;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.feature.FeatureCollection;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.GeometryCollection;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.LineString;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.MultiLineString;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.MultiPoint;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.MultiPolygon;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.Point;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.geometry.Polygon;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.properties.ext.change.Descriptor;\nimport org.openstreetmap.atlas.geography.geojson.parser.domain.properties.ext.change.FeatureChangeProperties;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Some of the examples are taken and modified from wikipedia and geojson spec.\n *\n * @author Yazad Khambata\n */\npublic class GeoJsonParserJacksonImplTest extends AbstractGeoJsonParserJacksonImplTestBase\n{\n    private static final Logger log = LoggerFactory.getLogger(GeoJsonParserJacksonImplTest.class);\n\n    @Test\n    public void feature1()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof Feature);\n        Assert.assertTrue(((Feature) geoJsonItem).getGeometry() instanceof LineString);\n    }\n\n    @Test\n    public void feature2()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof Feature);\n        Assert.assertTrue(((Feature) geoJsonItem).getGeometry() instanceof Polygon);\n\n    }\n\n    @Test\n    public void featureChangePropertiesExample1()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        final FeatureChangeProperties featureChangeProperties = geoJsonItem.getProperties()\n                .asType(FeatureChangeProperties.class);\n\n        Assert.assertNull(featureChangeProperties.getRelations());\n    }\n\n    @Test\n    public void featureChangePropertiesExample2()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        final FeatureChangeProperties featureChangeProperties = geoJsonItem.getProperties()\n                .asType(FeatureChangeProperties.class);\n\n        final Descriptor descriptor = featureChangeProperties.getDescription().getDescriptors()[0];\n        Assert.assertEquals(-1374305525422343588L, (long) descriptor.getId());\n        Assert.assertEquals(\"NODE\", descriptor.getItemType());\n        Assert.assertEquals(\"via\", descriptor.getRole());\n        Assert.assertEquals(9087654321L, (long) descriptor.getBeforeElement());\n        Assert.assertEquals(1234567890L, (long) descriptor.getAfterElement());\n    }\n\n    @Test\n    public void featureCollection1()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof FeatureCollection);\n        Assert.assertEquals(3, ((FeatureCollection) geoJsonItem).getFeatures().size());\n    }\n\n    @Test\n    public void featureWithExtendedProperties()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof Feature);\n        Assert.assertTrue(((Feature) geoJsonItem).getGeometry() instanceof LineString);\n\n        final FeatureChangeProperties featureChangeProperties = geoJsonItem.getProperties()\n                .asType(FeatureChangeProperties.class);\n\n        Assert.assertEquals(9, featureChangeProperties.getDescription().getDescriptors().length);\n        Assert.assertFalse(featureChangeProperties.getWKT().isEmpty());\n\n        log.info(\"featureChangeProperties:: {}.\", featureChangeProperties);\n    }\n\n    @Test\n    public void foreignFieldsNested()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof Point);\n    }\n\n    @Test\n    public void foreignFieldsSimple()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof Point);\n        Assert.assertFalse(geoJsonItem.getForeignFields().asMap().isEmpty());\n    }\n\n    @Test\n    public void geometryCollectionBasic()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof GeometryCollection);\n        Assert.assertFalse(((GeometryCollection) geoJsonItem).getGeometries().isEmpty());\n    }\n\n    @Test\n    public void geometryCollectionChildConversion()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof GeometryCollection);\n        Assert.assertFalse(((GeometryCollection) geoJsonItem).getGeometries().isEmpty());\n    }\n\n    @Test\n    public void geometryCollectionRecursiveNested()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        final int innermostLevelSize = (((GeometryCollection) ((GeometryCollection) ((GeometryCollection) ((GeometryCollection) geoJsonItem)\n                .getGeometries().get(3)).getGeometries().get(3)).getGeometries().get(3)))\n                .getGeometries().size();\n\n        Assert.assertEquals(3, innermostLevelSize);\n    }\n\n    @Test\n    public void lineString()\n    {\n        Assert.assertTrue(toGeoJsonItem() instanceof LineString);\n    }\n\n    @Test\n    public void lineStringConversion()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof LineString);\n        final LineString lineString = (LineString) geoJsonItem;\n        final PolyLine polyLine = lineString.toAtlasGeometry();\n        Assert.assertEquals(3, lineString.getCoordinates().getValue().size());\n        Assert.assertEquals(lineString.getCoordinates().getValue().size(), polyLine.size());\n        final Location first = polyLine.first();\n        Assert.assertEquals((Double) first.getLongitude().asDegrees(),\n                lineString.getCoordinates().getValue().get(0).getCoordinate1());\n        Assert.assertEquals((Double) first.getLatitude().asDegrees(),\n                lineString.getCoordinates().getValue().get(0).getCoordinate2());\n    }\n\n    @Test\n    public void multiLineString()\n    {\n        Assert.assertTrue(toGeoJsonItem() instanceof MultiLineString);\n    }\n\n    @Test\n    public void multiLineStringConversion()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof MultiLineString);\n        final MultiLineString multiLineString = (MultiLineString) geoJsonItem;\n        final List<PolyLine> polyLines = multiLineString.toAtlasGeometry();\n        Assert.assertEquals(2, multiLineString.getCoordinates().getValue().size());\n        Assert.assertEquals(multiLineString.getCoordinates().getValue().size(), polyLines.size());\n        final PolyLine firstPolyLine = polyLines.get(0);\n        Assert.assertEquals(5, multiLineString.getCoordinates().getValue().get(0).size());\n        Assert.assertEquals(multiLineString.getCoordinates().getValue().get(0).size(),\n                firstPolyLine.size());\n    }\n\n    @Test\n    public void multiPoint()\n    {\n        Assert.assertTrue(toGeoJsonItem() instanceof MultiPoint);\n    }\n\n    @Test\n    public void multiPointConversion()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof MultiPoint);\n        final MultiPoint multiPoint = (MultiPoint) geoJsonItem;\n        final List<Location> locations = multiPoint.toAtlasGeometry();\n        Assert.assertEquals(3, multiPoint.getCoordinates().getValue().size());\n        Assert.assertEquals(multiPoint.getCoordinates().getValue().size(), locations.size());\n        final Location first = locations.get(0);\n        Assert.assertEquals((Double) first.getLongitude().asDegrees(),\n                multiPoint.getCoordinates().getValue().get(0).getCoordinate1());\n        Assert.assertEquals((Double) first.getLatitude().asDegrees(),\n                multiPoint.getCoordinates().getValue().get(0).getCoordinate2());\n    }\n\n    @Test\n    public void multiPolygon()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof MultiPolygon);\n        final MultiPolygon multiPolygon = (MultiPolygon) geoJsonItem;\n        final org.openstreetmap.atlas.geography.MultiPolygon atlasPolygons = multiPolygon\n                .toAtlasGeometry();\n        Assert.assertEquals(1, atlasPolygons.outers().size());\n        Assert.assertTrue(atlasPolygons.inners().isEmpty());\n    }\n\n    @Test\n    public void multiPolygonConversion()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof MultiPolygon);\n        final MultiPolygon multiPolygon = (MultiPolygon) geoJsonItem;\n        final org.openstreetmap.atlas.geography.MultiPolygon atlasPolygons = multiPolygon\n                .toAtlasGeometry();\n        Assert.assertEquals(2, multiPolygon.getCoordinates().getValue().size());\n        Assert.assertEquals(multiPolygon.getCoordinates().getValue().size(),\n                atlasPolygons.outers().size());\n        Assert.assertEquals(4, atlasPolygons.inners().size());\n    }\n\n    @Test\n    public void multiPolygonDonut()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof MultiPolygon);\n        final MultiPolygon multiPolygon = (MultiPolygon) geoJsonItem;\n        final org.openstreetmap.atlas.geography.MultiPolygon atlasMultiPolygon = multiPolygon\n                .toAtlasGeometry();\n        Assert.assertEquals(1, multiPolygon.toAtlasGeometry().inners().size());\n    }\n\n    @Test\n    public void point()\n    {\n        Assert.assertTrue(toGeoJsonItem() instanceof Point);\n    }\n\n    @Test\n    public void pointConversion()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof Point);\n\n        final Point point = (Point) geoJsonItem;\n        final Location location = point.toAtlasGeometry();\n        Assert.assertNotNull(location);\n        log.info(\"Location: {}.\", location);\n        Assert.assertEquals((Double) location.getLongitude().asDegrees(),\n                point.getCoordinates().getValue().getCoordinate1());\n        Assert.assertEquals((Double) location.getLatitude().asDegrees(),\n                point.getCoordinates().getValue().getCoordinate2());\n    }\n\n    @Test\n    public void pointWithBbox2D()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof Point);\n        Assert.assertTrue(geoJsonItem.getBbox() instanceof Bbox2D);\n    }\n\n    @Test\n    public void pointWithBbox3D()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertTrue(geoJsonItem instanceof Point);\n        Assert.assertTrue(geoJsonItem.getBbox() instanceof Bbox3D);\n    }\n\n    @Test\n    public void polygon()\n    {\n        Assert.assertTrue(toGeoJsonItem() instanceof Polygon);\n    }\n\n    @Test\n    public void polygonConversion()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertTrue(geoJsonItem instanceof Polygon);\n        final Polygon polygon = (Polygon) geoJsonItem;\n        final org.openstreetmap.atlas.geography.Polygon atlasPolygon = polygon.toAtlasGeometry();\n        log.info(\"Atlas Polygon: {}.\", atlasPolygon);\n        Assert.assertEquals(5, polygon.getCoordinates().getValue().get(0).size());\n        Assert.assertEquals(polygon.getCoordinates().getValue().get(0).size(), atlasPolygon.size());\n    }\n\n    @Test\n    public void propertiesNested()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n        Assert.assertEquals(3,\n                ((Map<String, Object>) geoJsonItem.getProperties().get(\"prop0\")).size());\n    }\n\n    @Test\n    public void propertiesSimple()\n    {\n        final GeoJsonItem geoJsonItem = toGeoJsonItem();\n\n        Assert.assertEquals(2, geoJsonItem.getProperties().asMap().size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/parser/testdomain/BeanA.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.testdomain;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\n\n/**\n * Scalar and scalar arrays. Example,\n *\n * <pre>\n *     {\n *          id: 10,\n *          name: \"Hello\",\n *          score: 10.5,\n *          ids: [1,2,3],\n *          names: [\"hello\", \"hola\", \"bonjour\"],\n *          scores: [1.414, 3.14159, 2.718],\n *          result: true,\n *          results: [true, false, true]\n *      }\n * </pre>\n *\n * @author Yazad Khambata\n */\npublic class BeanA\n{\n    private Long id;\n    private String name;\n    private Double score;\n    private Boolean result;\n    private Long[] ids;\n    private String[] names;\n    private Double[] scores;\n    private Boolean[] results;\n    private Map<String, String> tags;\n\n    public BeanA()\n    {\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    public Long getId()\n    {\n        return this.id;\n    }\n\n    public Long[] getIds()\n    {\n        return this.ids;\n    }\n\n    public String getName()\n    {\n        return this.name;\n    }\n\n    public String[] getNames()\n    {\n        return this.names;\n    }\n\n    public Boolean getResult()\n    {\n        return this.result;\n    }\n\n    public Boolean[] getResults()\n    {\n        return this.results;\n    }\n\n    public Double getScore()\n    {\n        return this.score;\n    }\n\n    public Double[] getScores()\n    {\n        return this.scores;\n    }\n\n    public Map<String, String> getTags()\n    {\n        return this.tags;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    public void setId(final Long id)\n    {\n        this.id = id;\n    }\n\n    public void setIds(final Long[] ids)\n    {\n        this.ids = ids;\n    }\n\n    public void setName(final String name)\n    {\n        this.name = name;\n    }\n\n    public void setNames(final String[] names)\n    {\n        this.names = names;\n    }\n\n    public void setResult(final Boolean result)\n    {\n        this.result = result;\n    }\n\n    public void setResults(final Boolean[] results)\n    {\n        this.results = results;\n    }\n\n    public void setScore(final Double score)\n    {\n        this.score = score;\n    }\n\n    public void setScores(final Double[] scores)\n    {\n        this.scores = scores;\n    }\n\n    public void setTags(final Map<String, String> tags)\n    {\n        this.tags = tags;\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/geojson/parser/testdomain/BeanB.java",
    "content": "package org.openstreetmap.atlas.geography.geojson.parser.testdomain;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\n\n/**\n * @author Yazad Khambata\n */\npublic class BeanB\n{\n    private String name;\n    private BeanA beanA;\n    private BeanA[] beanAs;\n\n    public BeanB()\n    {\n    }\n\n    @Override\n    public boolean equals(final Object that)\n    {\n        return EqualsBuilder.reflectionEquals(this, that);\n    }\n\n    public BeanA getBeanA()\n    {\n        return this.beanA;\n    }\n\n    public BeanA[] getBeanAs()\n    {\n        return this.beanAs;\n    }\n\n    public String getName()\n    {\n        return this.name;\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return HashCodeBuilder.reflectionHashCode(this);\n    }\n\n    public void setBeanA(final BeanA beanA)\n    {\n        this.beanA = beanA;\n    }\n\n    public void setBeanAs(final BeanA[] beanAs)\n    {\n        this.beanAs = beanAs;\n    }\n\n    public void setName(final String name)\n    {\n        this.name = name;\n    }\n\n    @Override\n    public String toString()\n    {\n        return ToStringBuilder.reflectionToString(this);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/index/SpatialIndexTest.java",
    "content": "package org.openstreetmap.atlas.geography.index;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\n\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CounterWithStatistic;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author tony\n */\npublic class SpatialIndexTest\n{\n    private static Logger logger = LoggerFactory.getLogger(SpatialIndexTest.class);\n    private static final int CASE_NUMBER_FOR_EACH_BOUND = 100_000;\n    private static final int CASE_NUMBER_FOR_READ_TEST = CASE_NUMBER_FOR_EACH_BOUND / 100;\n\n    // A bounding box inside (-10, -10) and (10, 10)\n    private final Rectangle right = Rectangle.forLocations(\n            new Location(Latitude.degrees(-10), Longitude.degrees(-10)),\n            new Location(Latitude.degrees(10), Longitude.degrees(10)));\n\n    // A bounding box inside (-30, -10) and (-10, 10)\n    private final Rectangle left = Rectangle.forLocations(\n            new Location(Latitude.degrees(-10), Longitude.degrees(-30)),\n            new Location(Latitude.degrees(10), Longitude.degrees(-10)));\n\n    private List<Location> locationsInsideRight = new ArrayList<>();\n    private List<Location> locationsInsideLeft = new ArrayList<>();\n    private List<Location> locationsOnRightBorder = new ArrayList<>();\n    private List<Rectangle> rectanglesInsideRight = new ArrayList<>();\n    private List<Rectangle> rectanglesInsideLeft = new ArrayList<>();\n    private List<Rectangle> rectanglesOnBorder = new ArrayList<>();\n    private final QuadTree<Location> quadTreeLocation = new QuadTree<>();\n    private final QuadTree<Rectangle> quadTreeRectangle = new QuadTree<>();\n    private final RTree<Location> rTreeLocation = new RTree<>();\n    private final RTree<Rectangle> rTreeRectangle = new RTree<>();\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void testAccuracy()\n    {\n        // generate test case\n        this.locationsOnRightBorder = new ArrayList<>();\n        this.locationsOnRightBorder.add(new Location(Latitude.degrees(-10), Longitude.degrees(0)));\n        this.locationsOnRightBorder.add(new Location(Latitude.degrees(10), Longitude.degrees(5)));\n        // this record is on left right border\n        this.locationsOnRightBorder.add(new Location(Latitude.degrees(0), Longitude.degrees(-10)));\n        this.locationsInsideRight.add(new Location(Latitude.degrees(-9), Longitude.degrees(0)));\n        this.locationsInsideRight.add(new Location(Latitude.degrees(0), Longitude.degrees(0)));\n        this.locationsInsideRight.add(new Location(Latitude.degrees(3), Longitude.degrees(-5)));\n        this.locationsInsideLeft.add(new Location(Latitude.degrees(0), Longitude.degrees(-25)));\n        this.locationsInsideLeft.add(new Location(Latitude.degrees(5), Longitude.degrees(-29)));\n        // this record is very close to left right border\n        this.locationsInsideLeft\n                .add(new Location(Latitude.degrees(0), Longitude.degrees(-10.000001)));\n        assertEquals(3, this.locationsOnRightBorder.size());\n        assertEquals(3, this.locationsInsideRight.size());\n        assertEquals(3, this.locationsInsideLeft.size());\n        this.rectanglesInsideRight = locationToRectangle(this.locationsInsideRight);\n        this.rectanglesInsideLeft = locationToRectangle(this.locationsInsideLeft);\n        this.rectanglesOnBorder = locationToRectangle(this.locationsOnRightBorder);\n\n        // build tree\n        buildLocationTree(this.rTreeLocation, this.locationsInsideLeft, this.locationsInsideRight,\n                this.locationsOnRightBorder);\n        buildRectangleTree(this.quadTreeRectangle, this.rectanglesInsideLeft,\n                this.rectanglesInsideRight, this.rectanglesOnBorder);\n        buildRectangleTree(this.rTreeRectangle, this.rectanglesInsideLeft,\n                this.rectanglesInsideRight, this.rectanglesOnBorder);\n\n        assertEquals(9, this.quadTreeRectangle.size());\n        assertEquals(9, this.rTreeLocation.size());\n        assertEquals(9, this.rTreeRectangle.size());\n\n        // rtree returns exact correct locations\n        assertEquals(6, this.rTreeLocation.get(this.right).size());\n        assertEquals(4, this.rTreeLocation.get(this.left).size());\n\n        // rtree returns one more rectangle since rectangle is 1 km larger bounding box than\n        // location\n        assertEquals(7, this.rTreeRectangle.get(this.right).size());\n        assertEquals(4, this.rTreeRectangle.get(this.left).size());\n\n        // quad tree returns even more rectangles, looks like shouldn't use quad tree for rectangle\n        assertEquals(8, this.quadTreeRectangle.get(this.right).size());\n        assertEquals(7, this.quadTreeRectangle.get(this.left).size());\n    }\n\n    @Test\n    public void testPerformance()\n    {\n        generateRandomTestCase();\n        testBuild();\n        testRead();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void buildLocationTree(final JtsSpatialIndex<Location> index,\n            final List<Location>... lists)\n    {\n        for (final List<Location> list : lists)\n        {\n            list.forEach(location -> index.add(location.bounds(), location));\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void buildRectangleTree(final JtsSpatialIndex<Rectangle> index,\n            final List<Rectangle>... lists)\n    {\n        for (final List<Rectangle> list : lists)\n        {\n            list.forEach(rectangle -> index.add(rectangle.bounds(), rectangle));\n        }\n    }\n\n    private void generateRandomTestCase()\n    {\n        this.locationsInsideRight = randomLocations(this.right, CASE_NUMBER_FOR_EACH_BOUND);\n        this.locationsInsideLeft = randomLocations(this.left, CASE_NUMBER_FOR_EACH_BOUND);\n        this.rectanglesInsideRight = locationToRectangle(this.locationsInsideRight);\n        this.rectanglesInsideLeft = locationToRectangle(this.locationsInsideLeft);\n    }\n\n    /**\n     * @return Rectangle list which expand 1 km bounding box for each location\n     */\n    private List<Rectangle> locationToRectangle(final List<Location> loactions)\n    {\n        return loactions.stream().map(location -> location.boxAround(Distance.kilometers(1)))\n                .collect(Collectors.toList());\n    }\n\n    private List<Location> randomLocations(final Rectangle bound, final int numberToGenerate)\n    {\n        final List<Location> list = new ArrayList<>(numberToGenerate);\n        final List<Double> doubleLatitudes = new Random()\n                .doubles(bound.lowerLeft().getLatitude().asDegrees(),\n                        bound.upperRight().getLatitude().asDegrees())\n                .limit(numberToGenerate).boxed().collect(Collectors.toList());\n        final List<Double> doubleLongitudes = new Random()\n                .doubles(bound.lowerLeft().getLongitude().asDegrees(),\n                        bound.upperRight().getLongitude().asDegrees())\n                .limit(numberToGenerate).boxed().collect(Collectors.toList());\n        for (int i = 0; i < doubleLongitudes.size(); i++)\n        {\n            list.add(new Location(Latitude.degrees(doubleLatitudes.get(i)),\n                    Longitude.degrees(doubleLongitudes.get(i))));\n        }\n        return list;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void testBuild()\n    {\n        logger.info(\"Building QuadTree and RTree indices\");\n\n        // Location QuadTree\n        final CounterWithStatistic counter = new CounterWithStatistic(logger);\n        buildLocationTree(this.quadTreeLocation, this.locationsInsideLeft,\n                this.locationsInsideRight);\n        logger.info(\"Location QuadTree size={}, depth={}, took {} to build\",\n                this.quadTreeLocation.size(), this.quadTreeLocation.depth(), counter.sinceStart());\n        assertEquals(CASE_NUMBER_FOR_EACH_BOUND * 2, this.quadTreeLocation.size());\n\n        this.quadTreeLocation.get(this.right).forEach(location -> counter.increment());\n        logger.info(\"{} locations are within bounds\", counter);\n        assertTrue(counter.count() >= CASE_NUMBER_FOR_EACH_BOUND);\n\n        // Rectangle QuadTree\n        counter.clear();\n        buildRectangleTree(this.quadTreeRectangle, this.rectanglesInsideRight,\n                this.rectanglesInsideLeft);\n        logger.info(\"Rectangle QuadTree size={}, depth={}, took {} to build\",\n                this.quadTreeRectangle.size(), this.quadTreeRectangle.depth(),\n                counter.sinceStart());\n        assertEquals(CASE_NUMBER_FOR_EACH_BOUND * 2, this.quadTreeRectangle.size());\n\n        this.quadTreeRectangle.get(this.right).forEach(location -> counter.increment());\n        logger.info(\"{} rectangles are within bounds\", counter);\n        assertTrue(counter.count() >= CASE_NUMBER_FOR_EACH_BOUND);\n\n        // Location RTree\n        counter.clear();\n        buildLocationTree(this.rTreeLocation, this.locationsInsideLeft, this.locationsInsideRight);\n        logger.info(\"Location RTree size={}, depth={}, took {} to build\", this.rTreeLocation.size(),\n                this.rTreeLocation.depth(), counter.sinceStart());\n        assertEquals(CASE_NUMBER_FOR_EACH_BOUND * 2, this.rTreeLocation.size());\n\n        this.rTreeLocation.get(this.right).forEach(location -> counter.increment());\n        logger.info(\"{} locations are within bounds\", counter);\n        assertEquals(CASE_NUMBER_FOR_EACH_BOUND, counter.count());\n\n        // Rectangle RTree\n        counter.clear();\n        buildRectangleTree(this.rTreeRectangle, this.rectanglesInsideRight,\n                this.rectanglesInsideLeft);\n        logger.info(\"Rectangle RTree size={}, depth={}, took {} to build\",\n                this.rTreeRectangle.size(), this.rTreeRectangle.depth(), counter.sinceStart());\n        assertEquals(CASE_NUMBER_FOR_EACH_BOUND * 2, this.rTreeRectangle.size());\n\n        this.rTreeRectangle.get(this.right).forEach(location -> counter.increment());\n        logger.info(\"{} rectangles are within bounds\", counter);\n        assertTrue(counter.count() >= CASE_NUMBER_FOR_EACH_BOUND);\n    }\n\n    private void testRead()\n    {\n        logger.info(\"Reading QuadTree and RTree indices\");\n\n        final CounterWithStatistic counter = new CounterWithStatistic(logger);\n        this.rectanglesInsideRight.stream().limit(CASE_NUMBER_FOR_READ_TEST)\n                .forEach(rectangle -> assertTrue(\n                        this.quadTreeLocation.get(rectangle.bounds()).iterator().hasNext()));\n        logger.info(\"Location QuadTree read rectangles {} times took {}\", CASE_NUMBER_FOR_READ_TEST,\n                counter.sinceStart());\n\n        counter.clear();\n        this.locationsInsideRight.stream().limit(CASE_NUMBER_FOR_READ_TEST)\n                .forEach(rectangle -> assertTrue(\n                        this.quadTreeLocation.get(rectangle.bounds()).iterator().hasNext()));\n        logger.info(\"Location QuadTree read Locations {} times took {}\", CASE_NUMBER_FOR_READ_TEST,\n                counter.sinceStart());\n\n        counter.clear();\n        this.rectanglesInsideRight.stream().limit(CASE_NUMBER_FOR_READ_TEST)\n                .forEach(rectangle -> assertTrue(\n                        this.quadTreeRectangle.get(rectangle.bounds()).iterator().hasNext()));\n        logger.info(\"Rectangle QuadTree read rectangles {} times took {}\",\n                CASE_NUMBER_FOR_READ_TEST, counter.sinceStart());\n\n        counter.clear();\n        this.rectanglesInsideRight.stream().limit(CASE_NUMBER_FOR_READ_TEST)\n                .forEach(rectangle -> assertTrue(\n                        this.rTreeLocation.get(rectangle.bounds()).iterator().hasNext()));\n        logger.info(\"Location RTree read rectangles {} times took {}\", CASE_NUMBER_FOR_READ_TEST,\n                counter.sinceStart());\n\n        counter.clear();\n        this.rectanglesInsideRight.stream().limit(CASE_NUMBER_FOR_READ_TEST)\n                .forEach(rectangle -> assertTrue(\n                        this.rTreeRectangle.get(rectangle.bounds()).iterator().hasNext()));\n        logger.info(\"Rectangle RTree read rectangles {} times took {}\", CASE_NUMBER_FOR_READ_TEST,\n                counter.sinceStart());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/matching/PolyLineMatchTest.java",
    "content": "package org.openstreetmap.atlas.geography.matching;\n\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class PolyLineMatchTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(PolyLineMatchTest.class);\n\n    @Test\n    public void testMatch()\n    {\n        final PolyLine source = new PolyLine(Location.CROSSING_85_280, Location.TEST_2,\n                Location.TEST_1);\n        final PolyLine candidate1 = new PolyLine(Location.CROSSING_85_280, Location.TEST_2,\n                Location.TEST_5);\n        final PolyLine candidate2 = new PolyLine(Location.CROSSING_85_17, Location.TEST_2,\n                Location.TEST_1);\n        final List<PolyLine> candidates = Iterables\n                .asList(Iterables.iterable(candidate1, candidate2));\n        final PolyLineRoute route = source.costDistanceToOneWay(candidates)\n                .match(Distance.ZERO, Duration.ONE_MINUTE)\n                .orElseThrow(() -> new CoreException(\"Did not find a route!\"));\n        logger.info(\"{}\", route);\n        Assert.assertEquals(3, route.asPolyLine().size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/sharding/CountryShardTest.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class CountryShardTest\n{\n    @Test\n    public void testCountryShard()\n    {\n        final CountryShard countryShard = CountryShard.forName(\"ABC_1-2-3\");\n        final SlippyTile tile = SlippyTile.forName(\"1-2-3\");\n        Assert.assertEquals(tile.bounds(), countryShard.bounds());\n        Assert.assertEquals(\"ABC\", countryShard.getCountry());\n\n        final CountryShard countryShard2 = new CountryShard(\"ABC\", \"4-5-6\");\n        final SlippyTile tile2 = SlippyTile.forName(\"4-5-6\");\n        Assert.assertEquals(tile2.bounds(), countryShard2.bounds());\n        Assert.assertEquals(\"ABC\", countryShard2.getCountry());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/sharding/DynamicTileShardingTest.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\n\n/**\n * Tests the functionality of DynamicTileSharding.\n *\n * @author james-gage\n */\npublic class DynamicTileShardingTest\n{\n    /**\n     * Tests the method that calculates the counts for all SlippyTiles below a certain zoom level.\n     */\n    @Test\n    public void testCalculateTileCountsForAllZoom()\n    {\n        final Map<SlippyTile, Long> countsAtZoom2 = new HashMap<SlippyTile, Long>()\n        {\n            private static final long serialVersionUID = 8166718906410476661L;\n\n            {\n                put(new SlippyTile(0, 0, 2), (long) 5);\n                put(new SlippyTile(1, 0, 2), (long) 3);\n                put(new SlippyTile(2, 0, 2), (long) 7);\n                put(new SlippyTile(3, 0, 2), (long) 2);\n                put(new SlippyTile(0, 1, 2), (long) 4);\n                put(new SlippyTile(1, 1, 2), (long) 1);\n                put(new SlippyTile(2, 1, 2), (long) 2);\n                put(new SlippyTile(3, 1, 2), (long) 5);\n                put(new SlippyTile(0, 2, 2), (long) 6);\n                put(new SlippyTile(1, 2, 2), (long) 7);\n                put(new SlippyTile(2, 2, 2), (long) 4);\n                put(new SlippyTile(3, 2, 2), (long) 3);\n                put(new SlippyTile(0, 3, 2), (long) 8);\n                put(new SlippyTile(1, 3, 2), (long) 6);\n                put(new SlippyTile(2, 3, 2), (long) 5);\n                put(new SlippyTile(3, 3, 2), (long) 4);\n            }\n        };\n        // The HashMap represents this SlippyTile zoom level\n        // -----------------\n        // | 5 | 3 | 7 | 2 |\n        // | 4 | 1 | 2 | 5 |\n        // | 6 | 7 | 4 | 3 |\n        // | 8 | 6 | 5 | 4 |\n        // -----------------\n        final long total = 72;\n        final String root = \"0-0-0\";\n        final StringResource rootString = new StringResource(root);\n        final DynamicTileSharding dynamicTileSharding = new DynamicTileSharding(rootString);\n        final Map<SlippyTile, Long> allCounts = dynamicTileSharding.calculateTileCountsForAllZoom(1,\n                countsAtZoom2);\n        final long zoom0 = allCounts.get(new SlippyTile(0, 0, 0));\n        Assert.assertEquals(\"The summed counts and the total\", zoom0, total);\n    }\n\n    /**\n     * Diff generated between two different trees\n     * <p>\n     * < 9-165-184+ < 10-330-368 < 10-330-369 < 10-331-368 < 10-331-369 --- > 9-165-184\n     * </p>\n     * 1. changed children ordering 2. removed children at deepest level\n     */\n    @Test\n    public void testEquals()\n    {\n        final DynamicTileSharding shardingTreeOriginal = new DynamicTileSharding(\n                new InputStreamResource(() -> DynamicTileShardingTest.class\n                        .getResourceAsStream(\"testDynamicSharding.txt\")));\n        final DynamicTileSharding shardingTreeOriginalCopy = new DynamicTileSharding(\n                new InputStreamResource(() -> DynamicTileShardingTest.class\n                        .getResourceAsStream(\"testDynamicSharding.txt\")));\n        final DynamicTileSharding missingChildren = new DynamicTileSharding(\n                new InputStreamResource(() -> DynamicTileShardingTest.class\n                        .getResourceAsStream(\"testDynamicShardingMissingChildren.txt\")));\n        final DynamicTileSharding differentChildOrdering = new DynamicTileSharding(\n                new InputStreamResource(() -> DynamicTileShardingTest.class\n                        .getResourceAsStream(\"testDynamicShardingChildOrdering.txt\")));\n        // identity\n        Assert.assertEquals(shardingTreeOriginal, shardingTreeOriginal);\n        // copy\n        Assert.assertEquals(shardingTreeOriginal, shardingTreeOriginalCopy);\n        // missing children\n        Assert.assertNotEquals(shardingTreeOriginal, missingChildren);\n        // child order ignore\n        Assert.assertEquals(shardingTreeOriginal, differentChildOrdering);\n    }\n\n    @Test\n    public void testForName()\n    {\n        final DynamicTileSharding shardingTreeOriginal = new DynamicTileSharding(\n                new InputStreamResource(() -> DynamicTileShardingTest.class\n                        .getResourceAsStream(\"testDynamicSharding.txt\")));\n        Assert.assertEquals(SlippyTile.forName(\"8-13-39\"),\n                shardingTreeOriginal.shardForName(\"8-13-39\"));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testForNameError()\n    {\n        final DynamicTileSharding shardingTreeOriginal = new DynamicTileSharding(\n                new InputStreamResource(() -> DynamicTileShardingTest.class\n                        .getResourceAsStream(\"testDynamicSharding.txt\")));\n        shardingTreeOriginal.shardForName(\"7-6-19\");\n    }\n\n    @Test\n    public void testGetName()\n    {\n        final String root = \"0-0-0\";\n        final StringResource rootString = new StringResource(root);\n        final DynamicTileSharding dynamicTileSharding = new DynamicTileSharding(rootString);\n        Assert.assertNotNull(dynamicTileSharding.getName());\n        Assert.assertNotEquals(\"N/A\", dynamicTileSharding.getName());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/sharding/GeoHashShardingTest.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * @author matthieun\n */\npublic class GeoHashShardingTest\n{\n    private final Sharding sharding = Sharding.forString(\"geohash@7\");\n\n    @Test\n    public void testForName()\n    {\n        final Sharding sharding = Sharding.forString(\"geohash@4\");\n        Assert.assertEquals(GeoHashTile.forName(\"gbsf\"), sharding.shardForName(\"gbsf\"));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testForNameError()\n    {\n        final Sharding sharding = Sharding.forString(\"geohash@4\");\n        sharding.shardForName(\"something\");\n    }\n\n    @Test\n    public void testGetName()\n    {\n        final Sharding sharding = Sharding.forString(\"geohash@4\");\n        Assert.assertEquals(\"geohash@4\", sharding.getName());\n    }\n\n    @Test\n    public void testGetPrecision()\n    {\n        Assert.assertEquals(7, ((GeoHashSharding) this.sharding).getPrecision());\n    }\n\n    @Test\n    public void testIntersectionBounds()\n    {\n        final Polygon bounds = new Polygon(Location.forWkt(\"POINT (-122.454 37.739)\"),\n                Location.forWkt(\"POINT (-122.4547 37.739)\"),\n                Location.forWkt(\"POINT (-122.4543 37.74)\"));\n\n        final Iterable<Shard> tiles = this.sharding.shards(bounds);\n        Assert.assertEquals(2, Iterables.size(tiles));\n        Assert.assertTrue(Iterables.equals(Iterables.from(\"9q8ytqp\", \"9q8ytqr\"),\n                Iterables.stream(tiles).map(tile -> (GeoHashTile) tile).map(GeoHashTile::getName)));\n    }\n\n    @Test\n    public void testIntersectionShape()\n    {\n        final PolyLine shape = new Polygon(Location.forWkt(\"POINT (-122.454 37.739)\"),\n                Location.forWkt(\"POINT (-122.4547 37.739)\"),\n                Location.forWkt(\"POINT (-122.4543 37.74)\"));\n\n        final Iterable<Shard> tiles = this.sharding.shardsIntersecting(shape);\n        Assert.assertEquals(2, Iterables.size(tiles));\n        Assert.assertTrue(Iterables.equals(Iterables.from(\"9q8ytqp\", \"9q8ytqr\"),\n                Iterables.stream(tiles).map(tile -> (GeoHashTile) tile).map(GeoHashTile::getName)));\n    }\n\n    @Test\n    public void testLocation()\n    {\n        final Location point = Location.forWkt(\"POINT (-122.454 37.739)\");\n\n        final Iterable<Shard> tiles = this.sharding.shardsCovering(point);\n        Assert.assertEquals(1, Iterables.size(tiles));\n        Assert.assertTrue(Iterables.equals(Iterables.from(\"9q8ytqp\"),\n                Iterables.stream(tiles).map(tile -> (GeoHashTile) tile).map(GeoHashTile::getName)));\n    }\n\n    @Test\n    public void testNeighbors()\n    {\n        final Iterable<Shard> tiles = this.sharding.neighbors(GeoHashTile.forName(\"9q8ytqp\"));\n        Assert.assertEquals(8, Iterables.size(tiles));\n        Assert.assertTrue(Iterables.equals(\n                Iterables.from(\"9q8ytmy\", \"9q8ytmz\", \"9q8ytqn\", \"9q8ytqq\", \"9q8ytqr\", \"9q8yttb\",\n                        \"9q8ytw0\", \"9q8ytw2\"),\n                Iterables.stream(tiles).map(tile -> (GeoHashTile) tile).map(GeoHashTile::getName)));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testNeighborsException()\n    {\n        final Iterable<Shard> tiles = this.sharding.neighbors(SlippyTile.forName(\"1-2-3\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/sharding/GeoHashTileTest.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class GeoHashTileTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(GeoHashTileTest.class);\n\n    @Test\n    public void testAllTiles()\n    {\n        for (int precision = 0; precision <= 4; precision++)\n        {\n            logger.info(\"Starting {}\", precision);\n            final Time start = Time.now();\n\n            Assert.assertEquals(GeoHashTile.numberTilesAtPrecision(precision),\n                    Iterables.size(GeoHashTile.allTiles(precision)));\n            logger.info(\"Finished {} in {}\", precision, start.elapsedSince());\n        }\n    }\n\n    @Test\n    public void testAllTilesBounded()\n    {\n        final Rectangle bounds = Rectangle.forLocations(Location.forWkt(\"POINT (-122.454 37.739)\"),\n                Location.forWkt(\"POINT (-122.4555 37.74)\"));\n        final Iterable<GeoHashTile> tiles = GeoHashTile.allTiles(7, bounds);\n        Assert.assertEquals(4, Iterables.size(tiles));\n        Assert.assertTrue(\n                Iterables.equals(Iterables.from(\"9q8ytqn\", \"9q8ytqp\", \"9q8ytqq\", \"9q8ytqr\"),\n                        Iterables.stream(tiles).map(GeoHashTile::getName)));\n        Assert.assertEquals(GeoHashTile.ROOT, GeoHashTile.allTiles(0, bounds).iterator().next());\n    }\n\n    @Test\n    public void testAllTilesPolygonBounded()\n    {\n        final Polygon bounds = new Polygon(Location.forWkt(\"POINT (-122.454 37.739)\"),\n                Location.forWkt(\"POINT (-122.4547 37.739)\"),\n                Location.forWkt(\"POINT (-122.4543 37.74)\"));\n        final Iterable<GeoHashTile> tiles = GeoHashTile.allTiles(7, bounds);\n        Assert.assertEquals(2, Iterables.size(tiles));\n        Assert.assertTrue(Iterables.equals(Iterables.from(\"9q8ytqp\", \"9q8ytqr\"),\n                Iterables.stream(tiles).map(GeoHashTile::getName)));\n    }\n\n    @Test\n    public void testBoundingBox()\n    {\n        final GeoHashTile tile = new GeoHashTile(\"9q8ytqp\");\n        Assert.assertEquals(\n                Rectangle.wkt(\"POLYGON ((-122.4549866 37.7380371, -122.4549866 37.7394104, \"\n                        + \"-122.4536133 37.7394104, -122.4536133 37.7380371, \"\n                        + \"-122.4549866 37.7380371))\"),\n                tile.bounds());\n        Assert.assertEquals(Rectangle.MAXIMUM, GeoHashTile.ROOT.bounds());\n    }\n\n    @Test\n    public void testEncoding()\n    {\n        final Location point = Location.forWkt(\"POINT (-122.454 37.739)\");\n\n        final GeoHashTile tile0 = GeoHashTile.covering(point, 0);\n        Assert.assertEquals(GeoHashTile.ROOT, tile0);\n        Assert.assertEquals(\"\", tile0.getName());\n\n        final GeoHashTile tile3 = GeoHashTile.covering(point, 3);\n        Assert.assertEquals(\"9q8\", tile3.getName());\n\n        final GeoHashTile tile7 = GeoHashTile.covering(point, 7);\n        Assert.assertEquals(\"9q8ytqp\", tile7.getName());\n\n        final GeoHashTile tile12 = GeoHashTile.covering(point, 12);\n        Assert.assertTrue(tile12.getName().startsWith(\"9q8ytqp\"));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testPrecisionTooHigh()\n    {\n        GeoHashTile.allTiles(-1);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testPrecisionTooLow()\n    {\n        GeoHashTile.allTiles(GeoHashTile.MAXIMUM_PRECISION + 1);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/sharding/SlippyTileShardingTest.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic class SlippyTileShardingTest\n{\n    @Test\n    public void testForName()\n    {\n        final Sharding sharding = Sharding.forString(\"slippy@11\");\n        Assert.assertEquals(SlippyTile.forName(\"11-998-709\"), sharding.shardForName(\"11-998-709\"));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testForNameError()\n    {\n        final Sharding sharding = Sharding.forString(\"slippy@11\");\n        sharding.shardForName(\"10-498-354\");\n    }\n\n    @Test\n    public void testGetName()\n    {\n        final Sharding sharding = Sharding.forString(\"slippy@11\");\n        Assert.assertEquals(\"slippy@11\", sharding.getName());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/sharding/SlippyTileTest.java",
    "content": "package org.openstreetmap.atlas.geography.sharding;\n\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.runtime.Command;\nimport org.openstreetmap.atlas.utilities.runtime.CommandMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link SlippyTile} unit tests.\n *\n * @author matthieun\n * @author mgostintsev\n */\npublic class SlippyTileTest extends Command\n{\n    private static final Logger logger = LoggerFactory.getLogger(SlippyTileTest.class);\n\n    private static final Switch<Rectangle> BOUNDS = new Switch<>(\"bounds\",\n            \"The bounds to get all the tiles from\", bounds -> Rectangle.forString(bounds),\n            Optionality.REQUIRED);\n    private static final Switch<Integer> ZOOM = new Switch<>(\"zoom\", \"The zoom level for the tiles\",\n            zoom -> Integer.valueOf(zoom), Optionality.REQUIRED);\n\n    public static void main(final String[] args)\n    {\n        new SlippyTileTest().run(args);\n    }\n\n    @Test\n    public void testAll()\n    {\n        Assert.assertEquals(1_024, Iterables.size(SlippyTile.allTiles(5, Rectangle.MAXIMUM)));\n        Assert.assertEquals(1, Iterables.size(SlippyTile.allTiles(17, Rectangle.TEST_RECTANGLE_2)));\n    }\n\n    @Test\n    public void testCompareTo()\n    {\n        final SlippyTile tile = new SlippyTile(10, 10, 17);\n        final SlippyTile identical = new SlippyTile(10, 10, 17);\n        final SlippyTile sameZ = new SlippyTile(5, 5, 17);\n        final SlippyTile sameZAndX = new SlippyTile(10, 5, 17);\n        final SlippyTile sameZAndY = new SlippyTile(5, 10, 17);\n\n        Assert.assertTrue(\"Identical tiles\", tile.compareTo(identical) == 0);\n        Assert.assertTrue(\"Same z levels, original tile should be preferred with higher X-value\",\n                tile.compareTo(sameZ) == 1);\n        Assert.assertTrue(\"Same z levels, original tile should be preferred with higher y value\",\n                tile.compareTo(sameZAndX) == 1);\n        Assert.assertTrue(\"Same z levels, original tile should be preferred with higher x value\",\n                tile.compareTo(sameZAndY) == 1);\n    }\n\n    @Test\n    public void testMultiPolygonShards()\n    {\n        final MultiPolygon multiPolygon = MultiPolygon\n                .wkt(\"MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),\"\n                        + \"((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),\"\n                        + \"(30 20, 20 15, 20 25, 30 20)))\");\n        final Set<Shard> coveredShards = StreamSupport\n                .stream(new SlippyTileSharding(8).shards(multiPolygon).spliterator(), false)\n                .collect(Collectors.toSet());\n        Assert.assertFalse(coveredShards.contains(SlippyTile.forName(\"8-145-113\")));\n        Assert.assertTrue(coveredShards.contains(SlippyTile.forName(\"8-146-111\")));\n        Assert.assertFalse(coveredShards.contains(SlippyTile.forName(\"8-167-108\")));\n        Assert.assertTrue(coveredShards.contains(SlippyTile.forName(\"8-152-97\")));\n    }\n\n    @Test\n    public void testNeighbors()\n    {\n        Assert.assertEquals(8, new SlippyTile(659, 1588, 12).neighbors().size());\n    }\n\n    @Test\n    public void testParent()\n    {\n        final SlippyTile tile = new SlippyTile(659, 1588, 12);\n        final SlippyTile parent = tile.parent();\n        logger.info(\"Tile: {}, Parent: {}\", tile, parent);\n        Assert.assertEquals(329, parent.getX());\n        Assert.assertEquals(794, parent.getY());\n        Assert.assertEquals(11, parent.getZoom());\n    }\n\n    @Test\n    public void testSplit()\n    {\n        final SlippyTile tile = new SlippyTile(659, 1588, 12);\n        final Set<SlippyTile> children = Iterables.asSet(tile.split());\n        logger.info(\"Tile: {}, Children: {}\", tile, children);\n        Assert.assertEquals(4, children.size());\n        Assert.assertTrue(children.contains(new SlippyTile(1318, 3176, 13)));\n        Assert.assertTrue(children.contains(new SlippyTile(1318, 3177, 13)));\n        Assert.assertTrue(children.contains(new SlippyTile(1319, 3176, 13)));\n        Assert.assertTrue(children.contains(new SlippyTile(1319, 3177, 13)));\n    }\n\n    @Test\n    public void testSplitZoomIn()\n    {\n        final SlippyTile tile = new SlippyTile(659, 1588, 12);\n        final Set<SlippyTile> children = Iterables.asSet(tile.split(14));\n        logger.info(\"Tile: {}, Children: {}\", tile, children);\n        Assert.assertEquals(16, children.size());\n        Assert.assertTrue(children.contains(new SlippyTile(2638, 6354, 14)));\n        Assert.assertTrue(children.contains(new SlippyTile(2638, 6355, 14)));\n        Assert.assertTrue(children.contains(new SlippyTile(2639, 6354, 14)));\n        Assert.assertTrue(children.contains(new SlippyTile(2639, 6355, 14)));\n    }\n\n    @Test\n    public void testSplitZoomOut()\n    {\n        final SlippyTile tile = new SlippyTile(659, 1588, 12);\n        final Set<SlippyTile> children = Iterables.asSet(tile.split(10));\n        logger.info(\"Tile: {}, Children: {}\", tile, children);\n        Assert.assertEquals(1, children.size());\n        Assert.assertTrue(children.contains(new SlippyTile(164, 397, 10)));\n    }\n\n    @Test\n    public void testTile()\n    {\n        // http://c.tile.openstreetmap.org/17/21104/50868.png\n        final SlippyTile tile = new SlippyTile(Location.TEST_6, 17);\n        final SlippyTile same = new SlippyTile(21104, 50868, 17);\n        Assert.assertEquals(same, tile);\n    }\n\n    @Override\n    protected int onRun(final CommandMap command)\n    {\n        final Rectangle bounds = (Rectangle) command.get(BOUNDS.getName());\n        final int zoom = (int) command.get(ZOOM.getName());\n        for (final SlippyTile tile : SlippyTile.allTiles(zoom, bounds))\n        {\n            System.out.println(tile);\n        }\n        return 0;\n    }\n\n    @Override\n    protected SwitchList switches()\n    {\n        return new SwitchList().with(BOUNDS, ZOOM);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/sharding/converters/RectangleToSpatial4JRectangleConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.sharding.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.locationtech.spatial4j.context.SpatialContext;\nimport org.locationtech.spatial4j.shape.impl.RectangleImpl;\nimport org.openstreetmap.atlas.geography.Rectangle;\n\n/**\n * @author matthieun\n */\npublic class RectangleToSpatial4JRectangleConverterTest\n{\n    @Test\n    public void testConversion()\n    {\n        final Rectangle atlasRectangle = Rectangle.TEST_RECTANGLE;\n        final org.locationtech.spatial4j.shape.Rectangle spatial4JRectangle = new RectangleImpl(\n                -122.031905, -122.029051, 37.328167, 37.330394, SpatialContext.GEO);\n        final RectangleToSpatial4JRectangleConverter converter = new RectangleToSpatial4JRectangleConverter();\n        Assert.assertEquals(atlasRectangle, converter.convert(spatial4JRectangle));\n        Assert.assertEquals(spatial4JRectangle, converter.backwardConvert(atlasRectangle));\n    }\n\n    @Test\n    public void testLimits()\n    {\n        final Rectangle atlasRectangle = Rectangle.MAXIMUM;\n        final org.locationtech.spatial4j.shape.Rectangle spatial4JRectangle = new RectangleImpl(\n                -180.0, 180.0, -90.0, 90.0, SpatialContext.GEO);\n        final RectangleToSpatial4JRectangleConverter converter = new RectangleToSpatial4JRectangleConverter();\n        Assert.assertEquals(atlasRectangle, converter.convert(spatial4JRectangle));\n        Assert.assertEquals(spatial4JRectangle, converter.backwardConvert(atlasRectangle));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/geography/sharding/converters/StringToShardConverterTest.java",
    "content": "package org.openstreetmap.atlas.geography.sharding.converters;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.sharding.CountryShard;\nimport org.openstreetmap.atlas.geography.sharding.GeoHashTile;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.utilities.tuples.Tuple;\n\n/**\n * @author lcram\n */\npublic class StringToShardConverterTest\n{\n    @Test\n    public void testConverter()\n    {\n        final Shard shard1 = new StringToShardConverter().convert(\"ABC_1-2-3\");\n        Assert.assertTrue(shard1 instanceof CountryShard);\n        Assert.assertEquals(\"ABC\", ((CountryShard) shard1).getCountry());\n        Assert.assertTrue(((CountryShard) shard1).getShard() instanceof SlippyTile);\n        Assert.assertEquals(\"[SlippyTile: zoom = 1, x = 2, y = 3]\",\n                ((CountryShard) shard1).getShard().toString());\n    }\n\n    @Test\n    public void testConverterWithMetadata()\n    {\n        final Tuple<Shard, Optional<String>> tuple1 = new StringToShardConverter()\n                .convertWithMetadata(\"ABC_1-2-3\");\n        final Tuple<Shard, Optional<String>> tuple2 = new StringToShardConverter()\n                .convertWithMetadata(\"ABC_bb54tp9\");\n        final Tuple<Shard, Optional<String>> tuple3 = new StringToShardConverter()\n                .convertWithMetadata(\"ABC_1-2-3_zz/xx/yy\");\n        final Tuple<Shard, Optional<String>> tuple4 = new StringToShardConverter()\n                .convertWithMetadata(\"1-2-3\");\n        final Tuple<Shard, Optional<String>> tuple5 = new StringToShardConverter()\n                .convertWithMetadata(\"1-2-3_zz/_moremetadata\");\n        final Tuple<Shard, Optional<String>> tuple6 = new StringToShardConverter()\n                .convertWithMetadata(\"ABC_DEF_1-2-3_zz/xx/yy\");\n\n        Assert.assertTrue(tuple1.getFirst() instanceof CountryShard);\n        Assert.assertEquals(\"ABC\", ((CountryShard) tuple1.getFirst()).getCountry());\n        Assert.assertTrue(((CountryShard) tuple1.getFirst()).getShard() instanceof SlippyTile);\n        Assert.assertEquals(\"[SlippyTile: zoom = 1, x = 2, y = 3]\",\n                ((CountryShard) tuple1.getFirst()).getShard().toString());\n\n        Assert.assertTrue(tuple2.getFirst() instanceof CountryShard);\n        Assert.assertEquals(\"ABC\", ((CountryShard) tuple2.getFirst()).getCountry());\n        Assert.assertTrue(((CountryShard) tuple2.getFirst()).getShard() instanceof GeoHashTile);\n        Assert.assertEquals(\"[GeoHashTile: value = bb54tp9]\",\n                ((CountryShard) tuple2.getFirst()).getShard().toString());\n\n        Assert.assertTrue(tuple3.getFirst() instanceof CountryShard);\n        Assert.assertEquals(\"ABC\", ((CountryShard) tuple3.getFirst()).getCountry());\n        Assert.assertTrue(((CountryShard) tuple3.getFirst()).getShard() instanceof SlippyTile);\n        Assert.assertEquals(\"[SlippyTile: zoom = 1, x = 2, y = 3]\",\n                ((CountryShard) tuple3.getFirst()).getShard().toString());\n        Assert.assertTrue(tuple3.getSecond().isPresent());\n        Assert.assertEquals(\"zz/xx/yy\", tuple3.getSecond().get());\n\n        Assert.assertTrue(tuple4.getFirst() instanceof SlippyTile);\n        Assert.assertEquals(\"[SlippyTile: zoom = 1, x = 2, y = 3]\", tuple4.getFirst().toString());\n        Assert.assertFalse(tuple4.getSecond().isPresent());\n\n        Assert.assertTrue(tuple5.getFirst() instanceof SlippyTile);\n        Assert.assertEquals(\"[SlippyTile: zoom = 1, x = 2, y = 3]\", tuple5.getFirst().toString());\n        Assert.assertTrue(tuple5.getSecond().isPresent());\n        Assert.assertEquals(\"zz/_moremetadata\", tuple5.getSecond().get());\n\n        Assert.assertTrue(tuple6.getFirst() instanceof CountryShard);\n        Assert.assertEquals(\"ABC\", ((CountryShard) tuple6.getFirst()).getCountry());\n        Assert.assertTrue(((CountryShard) tuple6.getFirst()).getShard() instanceof CountryShard);\n        Assert.assertEquals(\"DEF\",\n                ((CountryShard) ((CountryShard) tuple6.getFirst()).getShard()).getCountry());\n        Assert.assertTrue(((CountryShard) ((CountryShard) tuple6.getFirst()).getShard())\n                .getShard() instanceof SlippyTile);\n        Assert.assertEquals(\"[SlippyTile: zoom = 1, x = 2, y = 3]\",\n                ((CountryShard) ((CountryShard) tuple6.getFirst()).getShard()).getShard()\n                        .toString());\n        Assert.assertTrue(tuple6.getSecond().isPresent());\n        Assert.assertEquals(\"zz/xx/yy\", tuple6.getSecond().get());\n    }\n\n    @Test\n    public void testIfGetNameIsParsable()\n    {\n        final StringToShardConverter converter = new StringToShardConverter();\n\n        final SlippyTile slippyTile = new SlippyTile(9, 12, 8);\n        final GeoHashTile geoHashTile = new GeoHashTile(\"3ghv\");\n        final CountryShard countryShard1 = new CountryShard(\"DMA\", slippyTile);\n        final CountryShard countryShard2 = new CountryShard(\"AIA\", geoHashTile);\n\n        Assert.assertEquals(slippyTile, converter.convert(slippyTile.getName()));\n        Assert.assertEquals(slippyTile.toString(),\n                converter.convert(slippyTile.getName()).toString());\n        Assert.assertEquals(geoHashTile, converter.convert(geoHashTile.getName()));\n        Assert.assertEquals(geoHashTile.toString(),\n                converter.convert(geoHashTile.getName()).toString());\n        Assert.assertEquals(countryShard1, converter.convert(countryShard1.getName()));\n        Assert.assertEquals(countryShard1.toString(),\n                converter.convert(countryShard1.getName()).toString());\n        Assert.assertEquals(countryShard2, converter.convert(countryShard2.getName()));\n        Assert.assertEquals(countryShard2.toString(),\n                converter.convert(countryShard2.getName()).toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/locale/IsoCountryFuzzyMatcherTest.java",
    "content": "package org.openstreetmap.atlas.locale;\n\nimport java.util.Arrays;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author lcram\n */\npublic class IsoCountryFuzzyMatcherTest\n{\n    @Test\n    public void testFuzzyMatching()\n    {\n        Assert.assertEquals(Arrays.asList(\"Sweden\", \"British Indian Ocean Territory\", \"Cape Verde\"),\n                IsoCountryFuzzyMatcher.forDisplayCountryTopMatches(3, \"abcdefg\").stream()\n                        .map(IsoCountry::getDisplayCountry).collect(Collectors.toList()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/locale/IsoCountryTest.java",
    "content": "package org.openstreetmap.atlas.locale;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Test case for IsoCountry\n *\n * @author mcuthbert\n * @author robert_stack\n */\npublic class IsoCountryTest\n{\n    @Test\n    public void testIsoCountry()\n    {\n        Assert.assertTrue(IsoCountry.isValidCountryCode(\"US\"));\n        Assert.assertFalse(IsoCountry.isValidCountryCode(\"ZZ\"));\n        Assert.assertFalse(IsoCountry.isValidCountryCode(null));\n        Assert.assertTrue(IsoCountry.isValidCountryCode(\"USA\"));\n        Assert.assertFalse(IsoCountry.isValidCountryCode(\"ZZZ\"));\n        Assert.assertEquals(\"USA\", IsoCountry.iso3ForIso2(\"US\").get());\n        Assert.assertEquals(\"US\", IsoCountry.iso2ForIso3(\"USA\").get());\n        Assert.assertEquals(\"United States\", IsoCountry.displayCountry(\"US\").get());\n        Assert.assertEquals(\"United States\", IsoCountry.displayCountry(\"USA\").get());\n\n        Assert.assertEquals(\"South Africa\", IsoCountry.displayCountry(\"ZA\").get());\n        Assert.assertEquals(\"South Africa\", IsoCountry.displayCountry(\"ZAF\").get());\n        Assert.assertEquals(\"ZAF\", IsoCountry.iso3ForIso2(\"ZA\").get());\n        Assert.assertEquals(\"ZA\", IsoCountry.iso2ForIso3(\"ZAF\").get());\n\n        Assert.assertEquals(Optional.empty(), IsoCountry.displayCountry(null));\n        Assert.assertEquals(Optional.empty(), IsoCountry.iso3ForIso2(null));\n        Assert.assertEquals(Optional.empty(), IsoCountry.iso2ForIso3(null));\n        Assert.assertEquals(Optional.empty(), IsoCountry.displayCountry(\"ZZZ\"));\n        Assert.assertEquals(Optional.empty(), IsoCountry.iso3ForIso2(\"ZZZ\"));\n        Assert.assertEquals(Optional.empty(), IsoCountry.iso2ForIso3(\"ZZZ\"));\n\n        Assert.assertEquals(Optional.empty(), IsoCountry.displayCountry(\"Z\"));\n        Assert.assertEquals(Optional.empty(), IsoCountry.displayCountry(\"ZZ\"));\n        Assert.assertEquals(Optional.empty(), IsoCountry.displayCountry(\"ZZZ\"));\n        Assert.assertEquals(Optional.empty(), IsoCountry.displayCountry(\"ZZZZ\"));\n\n        final Optional<IsoCountry> isoCountry1 = IsoCountry.forCountryCode(\"US\");\n        Assert.assertEquals(\"US\", isoCountry1.get().getCountryCode());\n        Assert.assertEquals(\"USA\", isoCountry1.get().getIso3CountryCode());\n        Assert.assertEquals(\"United States\", isoCountry1.get().getDisplayCountry());\n        final Optional<IsoCountry> isoCountry2 = IsoCountry.forCountryCode(\"USA\");\n        Assert.assertEquals(\"US\", isoCountry2.get().getCountryCode());\n        Assert.assertEquals(\"USA\", isoCountry2.get().getIso3CountryCode());\n        Assert.assertEquals(\"United States\", isoCountry2.get().getDisplayCountry());\n        final Optional<IsoCountry> isoCountry3 = IsoCountry.forCountryCode(\"ZZ\");\n        Assert.assertEquals(Optional.empty(), isoCountry3);\n        final Optional<IsoCountry> isoCountry4 = IsoCountry.forCountryCode(\"ZZZ\");\n        Assert.assertEquals(Optional.empty(), isoCountry4);\n        final Optional<IsoCountry> isoCountry5 = IsoCountry.forCountryCode(null);\n        Assert.assertEquals(Optional.empty(), isoCountry5);\n\n        Assert.assertTrue(IsoCountry.allCountryCodes().contains(\"DZ\"));\n\n        Assert.assertEquals(\"USA\",\n                IsoCountry.forDisplayCountry(\"United States\").get().getIso3CountryCode());\n        Assert.assertTrue(IsoCountry.forDisplayCountry(\"united states\").isPresent());\n        Assert.assertFalse(IsoCountry.forDisplayCountry(\"ZZzz zzzzz\").isPresent());\n\n        Assert.assertTrue(IsoCountry.allDisplayCountries().contains(\"United States\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/locale/IsoLanguageTest.java",
    "content": "package org.openstreetmap.atlas.locale;\n\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.SerializationUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Test case for IsoLanguage\n *\n * @author robert_stack\n */\npublic class IsoLanguageTest\n{\n    @Test\n    public void testEquals()\n    {\n        final Set<IsoLanguage> isoLanguages = IsoLanguage.all().collect(Collectors.toSet());\n        IsoLanguage.forLanguageCode(\"en\").ifPresent(isoLanguage ->\n        {\n            final IsoLanguage clone = SerializationUtils\n                    .deserialize(SerializationUtils.serialize(isoLanguage));\n            Assert.assertTrue(isoLanguages.contains(clone));\n        });\n    }\n\n    @Test\n    public void testLanguage()\n    {\n        Assert.assertEquals(Optional.empty(), IsoLanguage.displayLanguageForLanguageCode(null));\n        Assert.assertEquals(Optional.empty(), IsoLanguage.displayLanguageForLanguageCode(\"zz\"));\n\n        Assert.assertFalse(IsoLanguage.isValidLanguageCode(null));\n        Assert.assertTrue(IsoLanguage.isValidLanguageCode(\"es\"));\n        Assert.assertFalse(IsoLanguage.isValidLanguageCode(\"zz\"));\n        Assert.assertEquals(\"Spanish\", IsoLanguage.displayLanguageForLanguageCode(\"es\").get());\n\n        Assert.assertFalse(IsoLanguage.isValidDisplayLanguage(null));\n        Assert.assertFalse(IsoLanguage.isValidDisplayLanguage(\"NonexistentCountry\"));\n        Assert.assertTrue(IsoLanguage.isValidDisplayLanguage(\"Spanish\"));\n\n        final Optional<IsoLanguage> isoLanguage1 = IsoLanguage.forLanguageCode(\"es\");\n        Assert.assertEquals(\"es\", isoLanguage1.get().getLanguageCode());\n        final Optional<IsoLanguage> isoLanguage2 = IsoLanguage.forLanguageCode(\"zz\");\n        Assert.assertEquals(Optional.empty(), isoLanguage2);\n        final Optional<IsoLanguage> isoLanguage3 = IsoLanguage.forLanguageCode(\"es\");\n        Assert.assertEquals(\"Spanish\", isoLanguage3.get().getDisplayLanguage());\n\n        Assert.assertTrue(IsoLanguage.allLanguageCodes().contains(\"ar\"));\n    }\n\n    @Test\n    public void testSerialization()\n    {\n        final IsoLanguage spanish = IsoLanguage.forLanguageCode(\"es\")\n                .orElseThrow(() -> new CoreException(\"es should correspond to Spanish.\"));\n        final IsoLanguage roundtrip = SerializationUtils\n                .deserialize(SerializationUtils.serialize(spanish));\n        Assert.assertEquals(spanish.getLanguageCode(), roundtrip.getLanguageCode());\n        Assert.assertEquals(spanish.getDisplayLanguage(), roundtrip.getDisplayLanguage());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/FullProtoSuiteTest.java",
    "content": "package org.openstreetmap.atlas.proto;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.SortedSet;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize.AtlasSizeBuilder;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.delta.AtlasDelta;\nimport org.openstreetmap.atlas.geography.atlas.delta.Diff;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas.AtlasSerializationFormat;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.random.RandomTagsSupplier;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class FullProtoSuiteTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(FullProtoSuiteTest.class);\n\n    private static final int BYTES_PER_KIBIBYTE = 1024;\n    private static final int LARGE_DEFAULT_SIZE = 1024 * 1024 * 24;\n\n    // Point parameters\n    private static final int NUMBER_OF_POINTS = 1000;\n    private static final int TAGS_PER_POINT = 5;\n\n    // Line parameters\n    private static final int NUMBER_OF_LINES = 1000;\n    private static final int TAGS_PER_LINE = 6;\n    private static final int MAXIMUM_LINE_SIZE = 10;\n    private static final int MINIMUM_LINE_SIZE = 2;\n\n    // Area parameters\n    private static final int NUMBER_OF_AREAS = 1000;\n    private static final int TAGS_PER_AREA = 3;\n    private static final int MAXIMUM_AREA_SIZE = 12;\n    private static final int MINIMUM_AREA_SIZE = 3;\n\n    // Node parameters\n    private static final int TAGS_PER_NODE = 2;\n\n    // Edge parameters\n    private static final int NUMBER_OF_EDGES = 1000;\n    private static final int TAGS_PER_EDGE = 4;\n\n    // Relation parameters\n    private static final int NUMBER_OF_RELATIONS = 1000;\n    private static final int TAGS_PER_RELATION = 7;\n\n    private static int nextIdentifier = 0;\n\n    private PackedAtlas cachedAtlas = null;\n\n    @Test\n    public void testConsistency()\n    {\n        final PackedAtlas atlas = getNewAtlas();\n\n        final ByteArrayResource javaResource = new ByteArrayResource(LARGE_DEFAULT_SIZE);\n        final ByteArrayResource protoResource = new ByteArrayResource(LARGE_DEFAULT_SIZE);\n\n        logger.info(\"Starting serialization process...\");\n\n        Time javaTime = Time.now();\n        atlas.setSaveSerializationFormat(AtlasSerializationFormat.JAVA);\n        atlas.save(javaResource);\n        logger.info(\"Java serialization time: {}\", javaTime.elapsedSince());\n\n        Time protoTime = Time.now();\n        atlas.setSaveSerializationFormat(AtlasSerializationFormat.PROTOBUF);\n        atlas.save(protoResource);\n        logger.info(\"Proto serialization time: {}\", protoTime.elapsedSince());\n\n        logger.info(\"Java resource size: {} bytes ({} KiB)\", javaResource.length(),\n                javaResource.length() / BYTES_PER_KIBIBYTE);\n        logger.info(\"Proto resource size: {} bytes ({} KiB)\", protoResource.length(),\n                protoResource.length() / BYTES_PER_KIBIBYTE);\n\n        javaTime = Time.now();\n        final PackedAtlas loadedFromJava = PackedAtlas.load(javaResource);\n        logger.info(\"Java deserialization time: {}\", javaTime.elapsedSince());\n\n        protoTime = Time.now();\n        final PackedAtlas loadedFromProto = PackedAtlas.load(protoResource);\n        logger.info(\"Proto deserialization time: {}\", protoTime.elapsedSince());\n\n        Assert.assertEquals(loadedFromJava, loadedFromProto);\n\n        final SortedSet<Diff> diff = new AtlasDelta(loadedFromJava, loadedFromProto, true)\n                .generate().getDifferences();\n        Assert.assertTrue(diff.isEmpty());\n    }\n\n    @Test\n    public void testEmptyTags()\n    {\n        final HashMap<String, String> map = new HashMap<>();\n\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addPoint(1, new Location(Latitude.ZERO, Longitude.ZERO), map);\n\n        final ByteArrayResource protoResource = new ByteArrayResource(LARGE_DEFAULT_SIZE);\n\n        final PackedAtlas atlas = (PackedAtlas) builder.get();\n\n        logger.info(\"Starting serialization process...\");\n\n        Time protoTime = Time.now();\n        atlas.setSaveSerializationFormat(AtlasSerializationFormat.PROTOBUF);\n        atlas.save(protoResource);\n        logger.info(\"Proto serialization time: {}\", protoTime.elapsedSince());\n\n        logger.info(\"Proto resource size: {} bytes ({} KiB)\", protoResource.length(),\n                protoResource.length() / BYTES_PER_KIBIBYTE);\n\n        protoTime = Time.now();\n        final PackedAtlas loadedFromProto = PackedAtlas.load(protoResource);\n        logger.info(\"Proto deserialization time: {}\", protoTime.elapsedSince());\n\n        Assert.assertEquals(atlas, loadedFromProto);\n    }\n\n    private PackedAtlas getCachedAtlas()\n    {\n        if (this.cachedAtlas == null)\n        {\n            this.cachedAtlas = setupAtlas1();\n        }\n        return this.cachedAtlas;\n    }\n\n    private PackedAtlas getFileAtlas(final String string)\n    {\n        return PackedAtlas.load(new File(string));\n    }\n\n    private PackedAtlas getNewAtlas()\n    {\n        return setupAtlas1();\n    }\n\n    private int getNextIdentifier()\n    {\n        nextIdentifier++;\n        return nextIdentifier;\n    }\n\n    private PackedAtlas setupAtlas1()\n    {\n        final Random random = new Random();\n        final AtlasSize sizeEstimates = new AtlasSizeBuilder().withPointEstimate(NUMBER_OF_POINTS)\n                .withLineEstimate(NUMBER_OF_LINES).withAreaEstimate(NUMBER_OF_AREAS)\n                .withEdgeEstimate(NUMBER_OF_EDGES).withRelationEstimate(NUMBER_OF_RELATIONS)\n                .build();\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder()\n                .withSizeEstimates(sizeEstimates);\n\n        // add some points\n        for (int index = 0; index < NUMBER_OF_POINTS; index++)\n        {\n            final Map<String, String> randomTags = RandomTagsSupplier.randomTags(TAGS_PER_POINT);\n            builder.addPoint(getNextIdentifier(), Location.random(Rectangle.MAXIMUM), randomTags);\n        }\n\n        // add some lines\n        for (int index = 0; index < NUMBER_OF_LINES; index++)\n        {\n            final Map<String, String> randomTags = RandomTagsSupplier.randomTags(TAGS_PER_LINE);\n            final PolyLine geometry = PolyLine.random(\n                    random.nextInt(MAXIMUM_LINE_SIZE - MINIMUM_LINE_SIZE) + MINIMUM_LINE_SIZE,\n                    Rectangle.TEST_RECTANGLE);\n            builder.addLine(getNextIdentifier(), geometry, randomTags);\n        }\n\n        // add some areas\n        for (int index = 0; index < NUMBER_OF_AREAS; index++)\n        {\n            final Map<String, String> randomTags = RandomTagsSupplier.randomTags(TAGS_PER_AREA);\n            final Polygon geometry = Polygon.random(\n                    random.nextInt(MAXIMUM_AREA_SIZE - MINIMUM_AREA_SIZE) + MINIMUM_AREA_SIZE,\n                    Rectangle.TEST_RECTANGLE);\n            builder.addArea(getNextIdentifier(), geometry, randomTags);\n        }\n\n        // create some nodes\n        builder.addNode(getNextIdentifier(), Location.TEST_1,\n                RandomTagsSupplier.randomTags(TAGS_PER_NODE));\n        builder.addNode(getNextIdentifier(), Location.TEST_2,\n                RandomTagsSupplier.randomTags(TAGS_PER_NODE));\n        builder.addNode(getNextIdentifier(), Location.TEST_3,\n                RandomTagsSupplier.randomTags(TAGS_PER_NODE));\n        builder.addNode(getNextIdentifier(), Location.TEST_4,\n                RandomTagsSupplier.randomTags(TAGS_PER_NODE));\n        builder.addNode(getNextIdentifier(), Location.TEST_5,\n                RandomTagsSupplier.randomTags(TAGS_PER_NODE));\n\n        // connect the nodes into some edges\n        for (int index = 0; index < NUMBER_OF_EDGES; index++)\n        {\n            builder.addEdge(\n                    getNextIdentifier(), new PolyLine(Location.TEST_1, Location.TEST_2,\n                            Location.TEST_3, Location.TEST_4, Location.TEST_5),\n                    RandomTagsSupplier.randomTags(TAGS_PER_EDGE));\n        }\n\n        // add some relations\n        final RelationBean structure1 = new RelationBean();\n        structure1.addItem(1L, \"p1\", ItemType.POINT);\n        structure1.addItem(2L, \"p2\", ItemType.POINT);\n        structure1.addItem(3L, \"p3\", ItemType.POINT);\n        for (int index = 0; index < NUMBER_OF_RELATIONS; index++)\n        {\n            builder.addRelation(getNextIdentifier(), nextIdentifier, structure1,\n                    RandomTagsSupplier.randomTags(TAGS_PER_RELATION));\n        }\n\n        return (PackedAtlas) builder.get();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoAtlasMetaDataAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoAtlasMetaDataAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoAtlasMetaDataAdapterTest.class);\n    private final ProtoAtlasMetaDataAdapter adapter = new ProtoAtlasMetaDataAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final AtlasSize size = new AtlasSize(1, 1, 1, 1, 1, 1);\n        final Map<String, String> tags = Maps.hashMap(\"key1\", \"value1\", \"key2\", \"value2\");\n        final AtlasMetaData metaData = new AtlasMetaData(size, true, \"test\", \"test\", \"test\", \"test\",\n                tags);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(metaData);\n        logger.info(\"Took {} to serialize AtlasMetaData\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final AtlasMetaData parsedFrom = (AtlasMetaData) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize AtlasMetaData from bytestream\",\n                startTime.elapsedSince());\n\n        Assert.assertEquals(metaData, parsedFrom);\n    }\n\n    @Test\n    public void testNullFields()\n    {\n        final AtlasMetaData nullified = new AtlasMetaData(null, false, null, null, null, null,\n                null);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(nullified);\n        logger.info(\"Took {} to serialize AtlasMetaData\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final AtlasMetaData parsedFrom = (AtlasMetaData) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize AtlasMetaData from bytestream\",\n                startTime.elapsedSince());\n\n        Assert.assertEquals(nullified, parsedFrom);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoByteArrayOfArraysAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.arrays.ByteArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoByteArrayOfArraysAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoByteArrayOfArraysAdapterTest.class);\n    private static final int TEST_ARRAY_SIZE = 5000;\n    private static final String TEST_NAME = \"testarray\";\n    private final ProtoByteArrayOfArraysAdapter adapter = new ProtoByteArrayOfArraysAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final ByteArrayOfArrays byteArrayOfArrays = new ByteArrayOfArrays(TEST_ARRAY_SIZE,\n                TEST_ARRAY_SIZE, TEST_ARRAY_SIZE);\n        for (int index = 0; index < TEST_ARRAY_SIZE; index++)\n        {\n            final byte[] items = new byte[TEST_ARRAY_SIZE];\n            for (int subIndex = 0; subIndex < TEST_ARRAY_SIZE; subIndex++)\n            {\n                items[subIndex] = 1;\n            }\n            byteArrayOfArrays.add(items);\n        }\n        byteArrayOfArrays.setName(TEST_NAME);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(byteArrayOfArrays);\n        logger.info(\"Took {} to serialize ByteArrayOfArrays\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final ByteArrayOfArrays parsedFrom = (ByteArrayOfArrays) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize ByteArrayOfArrays from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(byteArrayOfArrays, parsedFrom);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testNullFields()\n    {\n        final ByteArrayOfArrays byteArrayOfArrays = new ByteArrayOfArrays(10);\n\n        Assert.assertTrue(byteArrayOfArrays.getName() == null);\n\n        Time startTime = Time.now();\n        byte[] contents = this.adapter.serialize(byteArrayOfArrays);\n        logger.info(\"Took {} to serialize ByteArrayOfArrays\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final ByteArrayOfArrays parsedFrom = (ByteArrayOfArrays) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize ByteArrayOfArrays from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(byteArrayOfArrays, parsedFrom);\n\n        logger.info(\"Testing proper handling of null elements...\");\n        byteArrayOfArrays.add(new byte[10]);\n        byteArrayOfArrays.add(null);\n        contents = this.adapter.serialize(byteArrayOfArrays);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoIntegerArrayOfArraysAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoIntegerArrayOfArraysAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoIntegerArrayOfArraysAdapterTest.class);\n    private static final int TEST_ARRAY_SIZE = 200;\n    private static final int TEST_SUBARRAY_SIZE = 10_000;\n    private static final String TEST_NAME = \"testarray\";\n    private final ProtoIntegerArrayOfArraysAdapter adapter = new ProtoIntegerArrayOfArraysAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final IntegerArrayOfArrays integerArrayOfArrays = new IntegerArrayOfArrays(TEST_ARRAY_SIZE,\n                TEST_SUBARRAY_SIZE, TEST_SUBARRAY_SIZE);\n        for (int index = 0; index < TEST_ARRAY_SIZE; index++)\n        {\n            final int[] items = new int[TEST_SUBARRAY_SIZE];\n            for (int subIndex = 0; subIndex < TEST_SUBARRAY_SIZE; subIndex++)\n            {\n                items[subIndex] = subIndex;\n            }\n            integerArrayOfArrays.add(items);\n        }\n        integerArrayOfArrays.setName(TEST_NAME);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(integerArrayOfArrays);\n        logger.info(\"Took {} to serialize IntegerArrayOfArrays\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final IntegerArrayOfArrays parsedFrom = (IntegerArrayOfArrays) this.adapter\n                .deserialize(contents);\n        logger.info(\"Took {} to deserialize IntegerArrayOfArrays from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(integerArrayOfArrays, parsedFrom);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testNullFields()\n    {\n        final IntegerArrayOfArrays integerArrayOfArrays = new IntegerArrayOfArrays(10);\n\n        Assert.assertTrue(integerArrayOfArrays.getName() == null);\n\n        Time startTime = Time.now();\n        byte[] contents = this.adapter.serialize(integerArrayOfArrays);\n        logger.info(\"Took {} to serialize IntegerArrayOfArrays\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final IntegerArrayOfArrays parsedFrom = (IntegerArrayOfArrays) this.adapter\n                .deserialize(contents);\n        logger.info(\"Took {} to deserialize IntegerArrayOfArrays from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(integerArrayOfArrays, parsedFrom);\n\n        logger.info(\"Testing proper handling of null elements...\");\n        integerArrayOfArrays.add(new int[10]);\n        integerArrayOfArrays.add(null);\n        contents = this.adapter.serialize(integerArrayOfArrays);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoIntegerStringDictionaryAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.compression.IntegerDictionary;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoIntegerStringDictionaryAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoIntegerStringDictionaryAdapterTest.class);\n    private static final int NUMBER_OF_ENTRIES = 100_000;\n    private final ProtoIntegerStringDictionaryAdapter adapter = new ProtoIntegerStringDictionaryAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final IntegerDictionary<String> dictionary = new IntegerDictionary<>();\n        for (int index = 0; index < NUMBER_OF_ENTRIES; index++)\n        {\n            dictionary.add(\"testword\" + index);\n        }\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(dictionary);\n        logger.info(\"Took {} to serialize IntegerDictionary<String>\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        @SuppressWarnings(\"unchecked\")\n        final IntegerDictionary<String> parsedFrom = (IntegerDictionary<String>) this.adapter\n                .deserialize(contents);\n        logger.info(\"Took {} to deserialize IntegerDictionary<String> from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(dictionary, parsedFrom);\n    }\n\n    @Test\n    public void testContainsNullElements()\n    {\n        final IntegerDictionary<String> dictionary = new IntegerDictionary<>();\n\n        dictionary.add(\"testword1\");\n        dictionary.add(null);\n        dictionary.add(\"testword2\");\n\n        final byte[] contents = this.adapter.serialize(dictionary);\n        @SuppressWarnings(\"unchecked\")\n        final IntegerDictionary<String> parsedFrom = (IntegerDictionary<String>) this.adapter\n                .deserialize(contents);\n\n        Assert.assertEquals(dictionary, parsedFrom);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testExceptionOnInvalidOwnerTypeParameter()\n    {\n        // Create an IntegerDictionary parameterized with a type other than String\n        final IntegerDictionary<Object> dictionary = new IntegerDictionary<>();\n        dictionary.add(new Object());\n\n        // this line will throw a CoreException when attempting to serialize the dictionary\n        @SuppressWarnings(\"unused\")\n        final byte[] contents = dictionary.getProtoAdapter().serialize(dictionary);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoLongArrayAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.arrays.LongArray;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoLongArrayAdapterTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(ProtoLongArrayAdapterTest.class);\n    private static final int TEST_SIZE = 500_000;\n    private static final String TEST_NAME = \"testarray\";\n    private final ProtoLongArrayAdapter adapter = new ProtoLongArrayAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final LongArray longArray = new LongArray(TEST_SIZE, TEST_SIZE, TEST_SIZE);\n        for (int index = 0; index < TEST_SIZE; index++)\n        {\n            final long value = index;\n            longArray.add(value);\n        }\n        longArray.setName(TEST_NAME);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(longArray);\n        logger.info(\"Took {} to serialize LongArray\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final LongArray parsedFrom = (LongArray) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize LongArray from bytestream\", startTime.elapsedSince());\n\n        logger.info(\"Testing array equality...\");\n        Assert.assertEquals(longArray, parsedFrom);\n    }\n\n    @Test\n    public void testNullFields()\n    {\n        final LongArray longArray = new LongArray(10);\n\n        Assert.assertTrue(longArray.getName() == null);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(longArray);\n        logger.info(\"Took {} to serialize LongArray\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final LongArray parsedFrom = (LongArray) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize LongArray from bytestream\", startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(longArray, parsedFrom);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoLongArrayOfArraysAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.arrays.LongArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoLongArrayOfArraysAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoLongArrayOfArraysAdapterTest.class);\n    private static final int TEST_ARRAY_SIZE = 200;\n    private static final int TEST_SUBARRAY_SIZE = 10_000;\n    private static final String TEST_NAME = \"testarray\";\n    private final ProtoLongArrayOfArraysAdapter adapter = new ProtoLongArrayOfArraysAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final LongArrayOfArrays longArrayOfArrays = new LongArrayOfArrays(TEST_ARRAY_SIZE);\n        for (int index = 0; index < TEST_ARRAY_SIZE; index++)\n        {\n            final long[] items = new long[TEST_SUBARRAY_SIZE];\n            for (int subIndex = 0; subIndex < TEST_SUBARRAY_SIZE; subIndex++)\n            {\n                items[subIndex] = subIndex;\n            }\n            longArrayOfArrays.add(items);\n        }\n        longArrayOfArrays.setName(TEST_NAME);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(longArrayOfArrays);\n        logger.info(\"Took {} to serialize LongArrayOfArrays\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final LongArrayOfArrays parsedFrom = (LongArrayOfArrays) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize LongArrayOfArrays from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(longArrayOfArrays, parsedFrom);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testNullFields()\n    {\n        final LongArrayOfArrays longArrayOfArrays = new LongArrayOfArrays(10);\n\n        Assert.assertTrue(longArrayOfArrays.getName() == null);\n\n        Time startTime = Time.now();\n        byte[] contents = this.adapter.serialize(longArrayOfArrays);\n        logger.info(\"Took {} to serialize LongArrayOfArrays\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final LongArrayOfArrays parsedFrom = (LongArrayOfArrays) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize LongArrayOfArrays from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(longArrayOfArrays, parsedFrom);\n\n        logger.info(\"Testing proper handling of null elements...\");\n        longArrayOfArrays.add(new long[10]);\n        longArrayOfArrays.add(null);\n        contents = this.adapter.serialize(longArrayOfArrays);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoLongToLongMapAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.maps.LongToLongMap;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoLongToLongMapAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoLongToLongMapAdapterTest.class);\n    private static final int TEST_SIZE = 500_000;\n    private static final String TEST_NAME = \"testmap\";\n    private final ProtoLongToLongMapAdapter adapter = new ProtoLongToLongMapAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final LongToLongMap longMap = new LongToLongMap(TEST_NAME, TEST_SIZE, TEST_SIZE, TEST_SIZE,\n                TEST_SIZE, TEST_SIZE, TEST_SIZE);\n        for (int index = 0; index < TEST_SIZE; index++)\n        {\n            final long value = index;\n            longMap.put(value, value);\n        }\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(longMap);\n        logger.info(\"Took {} to serialize LongToLongMap\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final LongToLongMap parsedFrom = (LongToLongMap) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize LongToLongMap from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(longMap, parsedFrom);\n    }\n\n    @Test\n    public void testNullFields()\n    {\n        final LongToLongMap longMap = new LongToLongMap(null, 10);\n\n        Assert.assertTrue(longMap.getName() == null);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(longMap);\n        logger.info(\"Took {} to serialize LongToLongMap\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final LongToLongMap parsedFrom = (LongToLongMap) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize LongToLongMap from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(longMap, parsedFrom);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoLongToLongMultiMapAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.maps.LongToLongMultiMap;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoLongToLongMultiMapAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoLongToLongMultiMapAdapterTest.class);\n    private static final int TEST_SIZE = 1000;\n    private static final int TEST_SUBARRAY_SIZE = 1000;\n    private static final String TEST_NAME = \"testmap\";\n    private final ProtoLongToLongMultiMapAdapter adapter = new ProtoLongToLongMultiMapAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final LongToLongMultiMap longMultiMap = new LongToLongMultiMap(TEST_NAME, TEST_SIZE);\n        for (int index = 0; index < TEST_SIZE; index++)\n        {\n            final long key = index;\n            final long[] values = new long[TEST_SUBARRAY_SIZE];\n            for (int subIndex = 0; subIndex < values.length; subIndex++)\n            {\n                values[subIndex] = subIndex;\n            }\n            longMultiMap.put(key, values);\n        }\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(longMultiMap);\n        logger.info(\"Took {} to serialize LongToLongMultiMap\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final LongToLongMultiMap parsedFrom = (LongToLongMultiMap) this.adapter\n                .deserialize(contents);\n        logger.info(\"Took {} to deserialize LongToLongMultiMap from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(longMultiMap, parsedFrom);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testNullElements()\n    {\n        final LongToLongMultiMap longMultiMap = new LongToLongMultiMap(TEST_SIZE);\n\n        Time startTime = Time.now();\n        byte[] contents = this.adapter.serialize(longMultiMap);\n        logger.info(\"Took {} to serialize LongToLongMultiMap\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final LongToLongMultiMap parsedFrom = (LongToLongMultiMap) this.adapter\n                .deserialize(contents);\n        logger.info(\"Took {} to deserialize LongToLongMultiMap from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(longMultiMap, parsedFrom);\n\n        logger.info(\"Testing proper handling of null elements...\");\n        longMultiMap.put(3L, new long[10]);\n        longMultiMap.put(4L, null);\n        contents = this.adapter.serialize(longMultiMap);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoPackedTagStoreAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedTagStore;\nimport org.openstreetmap.atlas.utilities.compression.IntegerDictionary;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoPackedTagStoreAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoPackedTagStoreAdapterTest.class);\n    private static final int TEST_NUMBER_FEATURES = 10_000;\n    private static final int BLOCK_SIZE = 4096;\n    private static final int TEST_TAGSPERFEATURE_SIZE = 10;\n    private final ProtoPackedTagStoreAdapter adapter = new ProtoPackedTagStoreAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final IntegerDictionary<String> dictionary = new IntegerDictionary<String>();\n\n        final PackedTagStore store = new PackedTagStore(TEST_NUMBER_FEATURES, BLOCK_SIZE,\n                TEST_TAGSPERFEATURE_SIZE, dictionary);\n\n        for (int index = 0; index < TEST_NUMBER_FEATURES; index++)\n        {\n            for (int subIndex = 0; subIndex < TEST_TAGSPERFEATURE_SIZE; subIndex++)\n            {\n                store.add(index, \"key\" + index + \"_\" + subIndex, \"value\" + index + \"_\" + subIndex);\n            }\n        }\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(store);\n        logger.info(\"Took {} to serialize PackedTagStore\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final PackedTagStore parsedFrom = (PackedTagStore) this.adapter.deserialize(contents);\n        parsedFrom.setDictionary(dictionary);\n        logger.info(\"Took {} to deserialize PackedTagStore from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(store, parsedFrom);\n    }\n\n    @Test\n    public void testContainsNullElements()\n    {\n        final IntegerDictionary<String> dictionary = new IntegerDictionary<String>();\n\n        final PackedTagStore store = new PackedTagStore(TEST_NUMBER_FEATURES, BLOCK_SIZE,\n                TEST_TAGSPERFEATURE_SIZE, dictionary);\n\n        store.add(0, null, null);\n        store.add(1, null, null);\n        store.add(2, \"key1\", \"value1\");\n        store.add(2, \"key2\", \"value2\");\n        store.add(3, null, \"value3\");\n        store.add(4, \"key4\", null);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(store);\n        logger.info(\"Took {} to serialize PackedTagStore\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final PackedTagStore parsedFrom = (PackedTagStore) this.adapter.deserialize(contents);\n        parsedFrom.setDictionary(dictionary);\n        logger.info(\"Took {} to deserialize PackedTagStore from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(store, parsedFrom);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoPolyLineArrayAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.utilities.arrays.PolyLineArray;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoPolyLineArrayAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoPolyLineArrayAdapterTest.class);\n    private static final int TEST_SIZE = 10_000;\n    private static final String TEST_NAME = \"testarray\";\n    private final ProtoPolyLineArrayAdapter adapter = new ProtoPolyLineArrayAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final List<Location> locationList = new ArrayList<>();\n        locationList.add(new Location(Latitude.degrees(1), Longitude.degrees(1)));\n        locationList.add(new Location(Latitude.degrees(2), Longitude.degrees(2)));\n        locationList.add(new Location(Latitude.degrees(3), Longitude.degrees(3)));\n        locationList.add(new Location(Latitude.degrees(4), Longitude.degrees(4)));\n        locationList.add(new Location(Latitude.degrees(5), Longitude.degrees(5)));\n\n        final PolyLine testLine = new PolyLine(locationList);\n\n        final PolyLineArray polyLineArray = new PolyLineArray(TEST_SIZE, TEST_SIZE, TEST_SIZE);\n        for (int index = 0; index < TEST_SIZE; index++)\n        {\n            polyLineArray.add(testLine);\n        }\n        polyLineArray.setName(TEST_NAME);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(polyLineArray);\n        logger.info(\"Took {} to serialize PolyLineArray\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final PolyLineArray parsedFrom = (PolyLineArray) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize PolyLineArray from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(polyLineArray, parsedFrom);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testNullElements()\n    {\n        final PolyLineArray polyLineArray = new PolyLineArray(10);\n\n        Assert.assertTrue(polyLineArray.getName() == null);\n\n        Time startTime = Time.now();\n        byte[] contents = this.adapter.serialize(polyLineArray);\n        logger.info(\"Took {} to serialize PolyLineArray\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final PolyLineArray parsedFrom = (PolyLineArray) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize PolyLineArray from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(polyLineArray, parsedFrom);\n\n        logger.info(\"Testing proper handling of null elements...\");\n        polyLineArray.add(new PolyLine());\n        polyLineArray.add(null);\n        contents = this.adapter.serialize(polyLineArray);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/adapters/ProtoPolygonArrayAdapterTest.java",
    "content": "package org.openstreetmap.atlas.proto.adapters;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.utilities.arrays.PolygonArray;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoPolygonArrayAdapterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoPolygonArrayAdapterTest.class);\n    private static final int TEST_SIZE = 10_000;\n    private static final String TEST_NAME = \"testarray\";\n    private final ProtoPolygonArrayAdapter adapter = new ProtoPolygonArrayAdapter();\n\n    @Test\n    public void testConsistency()\n    {\n        final List<Location> locationList = new ArrayList<>();\n        locationList.add(new Location(Latitude.degrees(1), Longitude.degrees(1)));\n        locationList.add(new Location(Latitude.degrees(2), Longitude.degrees(2)));\n        locationList.add(new Location(Latitude.degrees(3), Longitude.degrees(3)));\n        locationList.add(new Location(Latitude.degrees(4), Longitude.degrees(4)));\n        locationList.add(new Location(Latitude.degrees(5), Longitude.degrees(5)));\n        locationList.add(new Location(Latitude.degrees(1), Longitude.degrees(1)));\n\n        final Polygon testPolygon = new Polygon(locationList);\n\n        final PolygonArray polygonArray = new PolygonArray(TEST_SIZE, TEST_SIZE, TEST_SIZE);\n        for (int index = 0; index < TEST_SIZE; index++)\n        {\n            polygonArray.add(testPolygon);\n        }\n        polygonArray.setName(TEST_NAME);\n\n        Time startTime = Time.now();\n        final byte[] contents = this.adapter.serialize(polygonArray);\n        logger.info(\"Took {} to serialize PolygonArray\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final PolygonArray parsedFrom = (PolygonArray) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize PolygonArray from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(polygonArray, parsedFrom);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testNullElements()\n    {\n        final PolygonArray polygonArray = new PolygonArray(10);\n\n        Assert.assertTrue(polygonArray.getName() == null);\n\n        Time startTime = Time.now();\n        byte[] contents = this.adapter.serialize(polygonArray);\n        logger.info(\"Took {} to serialize PolygonArray\", startTime.elapsedSince());\n\n        startTime = Time.now();\n        final PolygonArray parsedFrom = (PolygonArray) this.adapter.deserialize(contents);\n        logger.info(\"Took {} to deserialize PolygonArray from bytestream\",\n                startTime.elapsedSince());\n\n        logger.info(\"Testing equality...\");\n        Assert.assertEquals(polygonArray, parsedFrom);\n\n        logger.info(\"Testing proper handling of null elements...\");\n        polygonArray.add(new Polygon());\n        polygonArray.add(null);\n        contents = this.adapter.serialize(polygonArray);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/builder/ProtoAtlasBuilderTest.java",
    "content": "package org.openstreetmap.atlas.proto.builder;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoAtlasBuilderTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(ProtoAtlasBuilderTest.class);\n\n    private static long idCounter = 0L;\n\n    private static long getNextId()\n    {\n        idCounter++;\n        return idCounter;\n    }\n\n    @Test\n    public void testReadWriteConsistency()\n    {\n        final WritableResource resource = new ByteArrayResource();\n        final ProtoAtlasBuilder protoAtlasBuilder = new ProtoAtlasBuilder();\n        final PackedAtlasBuilder packedAtlasBuilder = setUpTestAtlasBuilder();\n\n        // make sure the atlases are the same\n        final Atlas outAtlas = packedAtlasBuilder.get();\n        protoAtlasBuilder.write(outAtlas, resource);\n        final Atlas inAtlas = protoAtlasBuilder.read(resource);\n\n        logger.debug(\"ATLAS THAT WAS WRITTEN OUT:\\n{}\\n\", outAtlas);\n        logger.debug(\"ATLAS THAT WAS READ BACK:\\n{}\\n\", inAtlas);\n\n        Assert.assertEquals(outAtlas, inAtlas);\n    }\n\n    private PackedAtlasBuilder setUpTestAtlasBuilder()\n    {\n        final PackedAtlasBuilder packedAtlasBuilder = new PackedAtlasBuilder();\n        final Map<String, String> tags = new HashMap<>();\n        final List<Location> shapePoints = new ArrayList<>();\n\n        final Map<String, String> metaTags = new HashMap<>();\n        metaTags.put(\"key1\", \"val1\");\n        metaTags.put(\"key2\", \"val2\");\n        final AtlasMetaData metaData = new AtlasMetaData(AtlasSize.DEFAULT, true, \"testCodeVersion\",\n                ProtoAtlasBuilder.PROTOATLAS_DATA_VERSION, \"testCountry\", \"testShardName\",\n                metaTags);\n\n        // add points\n        tags.put(\"building\", \"yes\");\n        tags.put(\"name\", \"eiffel_tower\");\n        packedAtlasBuilder.addPoint(getNextId(), Location.EIFFEL_TOWER, tags);\n        tags.clear();\n        tags.put(\"building\", \"yes\");\n        tags.put(\"name\", \"colosseum\");\n        packedAtlasBuilder.addPoint(getNextId(), Location.COLOSSEUM, tags);\n\n        // add lines\n        tags.clear();\n        tags.put(\"path\", \"yes\");\n        shapePoints.add(Location.EIFFEL_TOWER);\n        shapePoints.add(Location.COLOSSEUM);\n        packedAtlasBuilder.addLine(getNextId(), new PolyLine(shapePoints), tags);\n\n        // add areas\n        tags.clear();\n        tags.put(\"triangle\", \"yes\");\n        tags.put(\"size\", \"stupidbig\");\n        shapePoints.clear();\n        shapePoints.add(Location.EIFFEL_TOWER);\n        shapePoints.add(Location.COLOSSEUM);\n        shapePoints.add(Location.STEVENS_CREEK);\n        packedAtlasBuilder.addArea(getNextId(), new Polygon(shapePoints), tags);\n\n        // add nodes\n        tags.clear();\n        tags.put(\"sometag:namespace\", \"somevalue\");\n        packedAtlasBuilder.addNode(getNextId(), Location.forString(\"48.3406719,10.5563445\"), tags);\n        tags.clear();\n        packedAtlasBuilder.addNode(getNextId(), Location.forString(\"48.34204,10.55844\"), tags);\n\n        // add edges\n        tags.clear();\n        tags.put(\"edge\", \"yes\");\n        shapePoints.clear();\n        shapePoints.add(Location.forString(\"48.3406719,10.5563445\"));\n        shapePoints.add(Location.forString(\"48.34204,10.55844\"));\n        packedAtlasBuilder.addEdge(getNextId(), new PolyLine(shapePoints), tags);\n\n        // add relations\n        tags.clear();\n        tags.put(\"relationtag\", \"somevalue\");\n        RelationBean bean = new RelationBean();\n        bean.addItem(1L, \"This is the Eiffel Tower Point\", ItemType.POINT);\n        packedAtlasBuilder.addRelation(getNextId(), idCounter, bean, tags);\n\n        tags.clear();\n        tags.put(\"name\", \"coolstuff\");\n        tags.put(\"has_subrelation\", \"yes\");\n        bean = new RelationBean();\n        bean.addItem(1L, \"Eiffel Tower\", ItemType.POINT);\n        bean.addItem(2L, \"Colosseum\", ItemType.POINT);\n        bean.addItem(8L, \"subrelation\", ItemType.RELATION);\n        packedAtlasBuilder.addRelation(getNextId(), idCounter, bean, tags);\n\n        return packedAtlasBuilder;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/converters/ProtoIntegerArrayOfArraysConverterTest.java",
    "content": "package org.openstreetmap.atlas.proto.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.proto.ProtoIntegerArray;\nimport org.openstreetmap.atlas.proto.ProtoIntegerArrayOfArrays;\nimport org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoIntegerArrayOfArraysConverterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(ProtoIntegerArrayOfArraysConverterTest.class);\n\n    private static final int TEST_SIZE = 10;\n    private static final int TEST_SUBARRAY_SIZE = 10;\n\n    private static final String TEST_NAME = \"test_name\";\n\n    @Test\n    public void testArrayToProtoArray()\n    {\n        final ProtoIntegerArrayOfArraysConverter converter = new ProtoIntegerArrayOfArraysConverter();\n        final IntegerArrayOfArrays array = new IntegerArrayOfArrays(TEST_SIZE);\n        final ProtoIntegerArrayOfArrays.Builder builder = ProtoIntegerArrayOfArrays.newBuilder();\n\n        for (int index = 0; index < TEST_SIZE; index++)\n        {\n            final int[] subarray = new int[TEST_SUBARRAY_SIZE];\n            final ProtoIntegerArray.Builder subBuilder = ProtoIntegerArray.newBuilder();\n            for (int subIndex = 0; subIndex < TEST_SUBARRAY_SIZE; subIndex++)\n            {\n                subarray[subIndex] = subIndex;\n                subBuilder.addElements(subIndex);\n            }\n            builder.addArrays(subBuilder);\n            array.add(subarray);\n        }\n\n        array.setName(TEST_NAME);\n        builder.setName(TEST_NAME);\n\n        final ProtoIntegerArrayOfArrays protoArray = builder.build();\n        final ProtoIntegerArrayOfArrays convertedFrom = converter.backwardConvert(array);\n\n        Assert.assertEquals(protoArray, convertedFrom);\n    }\n\n    @Test\n    public void testProtoArrayToArray()\n    {\n        final ProtoIntegerArrayOfArraysConverter converter = new ProtoIntegerArrayOfArraysConverter();\n        final IntegerArrayOfArrays array = new IntegerArrayOfArrays(TEST_SIZE);\n        final ProtoIntegerArrayOfArrays.Builder builder = ProtoIntegerArrayOfArrays.newBuilder();\n\n        for (int index = 0; index < TEST_SIZE; index++)\n        {\n            final int[] subarray = new int[TEST_SUBARRAY_SIZE];\n            final ProtoIntegerArray.Builder subBuilder = ProtoIntegerArray.newBuilder();\n            for (int subIndex = 0; subIndex < TEST_SUBARRAY_SIZE; subIndex++)\n            {\n                subarray[subIndex] = subIndex;\n                subBuilder.addElements(subIndex);\n            }\n            builder.addArrays(subBuilder);\n            array.add(subarray);\n        }\n\n        array.setName(TEST_NAME);\n        builder.setName(TEST_NAME);\n\n        final ProtoIntegerArrayOfArrays protoArray = builder.build();\n        final IntegerArrayOfArrays convertedFrom = converter.convert(protoArray);\n\n        Assert.assertEquals(array, convertedFrom);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/converters/ProtoLocationConverterTest.java",
    "content": "package org.openstreetmap.atlas.proto.converters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.proto.ProtoLocation;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class ProtoLocationConverterTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(ProtoLocationConverterTest.class);\n\n    @Test\n    public void testLocationToProtoLocation()\n    {\n        final ProtoLocationConverter converter = new ProtoLocationConverter();\n        final Location location = new Location(Latitude.dm7(1), Longitude.dm7(1));\n\n        final ProtoLocation protoLocation = ProtoLocation.newBuilder().setLatitude(1)\n                .setLongitude(1).build();\n        final ProtoLocation convertedFromLocation = converter.backwardConvert(location);\n\n        logger.info(\"{}\", protoLocation);\n        logger.info(\"{}\", convertedFromLocation);\n        Assert.assertEquals(protoLocation, convertedFromLocation);\n    }\n\n    @Test\n    public void testProtoLocationToLocation()\n    {\n        final ProtoLocationConverter converter = new ProtoLocationConverter();\n        final ProtoLocation protoLocation = ProtoLocation.newBuilder().setLatitude(1)\n                .setLongitude(1).build();\n\n        final Location location = new Location(Latitude.dm7(1), Longitude.dm7(1));\n        final Location convertedFromProtoLocation = converter.convert(protoLocation);\n\n        logger.info(\"{}\", location);\n        logger.info(\"{}\", convertedFromProtoLocation);\n        Assert.assertEquals(location, convertedFromProtoLocation);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/proto/converters/ProtoTagListConverterTest.java",
    "content": "package org.openstreetmap.atlas.proto.converters;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.proto.ProtoTag;\n\n/**\n * @author lcram\n */\npublic class ProtoTagListConverterTest\n{\n    @Test\n    public void testEmptyConversion()\n    {\n        final ProtoTagListConverter converter = new ProtoTagListConverter();\n        final Map<String, String> osmTagMap = new HashMap<>();\n        final List<ProtoTag> protoTagList = new ArrayList<>();\n\n        final List<ProtoTag> listFromMap = converter.backwardConvert(osmTagMap);\n        Assert.assertEquals(protoTagList, listFromMap);\n\n        final Map<String, String> mapFromList = converter.convert(protoTagList);\n        Assert.assertEquals(osmTagMap, mapFromList);\n    }\n\n    @Test\n    public void testOSMToProtoTagList()\n    {\n        final ProtoTagListConverter converter = new ProtoTagListConverter();\n        final Map<String, String> osmTagMap = new HashMap<>();\n        osmTagMap.put(\"key1\", \"value1\");\n        osmTagMap.put(\"key2\", \"value2\");\n        final List<ProtoTag> protoTagList = new ArrayList<>();\n        protoTagList.add(ProtoTag.newBuilder().setKey(\"key1\").setValue(\"value1\").build());\n        protoTagList.add(ProtoTag.newBuilder().setKey(\"key2\").setValue(\"value2\").build());\n\n        final List<ProtoTag> listFromMap = converter.backwardConvert(osmTagMap);\n        Assert.assertEquals(protoTagList, listFromMap);\n    }\n\n    @Test\n    public void testProtoTagListToOSM()\n    {\n        final ProtoTagListConverter converter = new ProtoTagListConverter();\n        final List<ProtoTag> protoTagList = new ArrayList<>();\n        protoTagList.add(ProtoTag.newBuilder().setKey(\"key1\").setValue(\"value1\").build());\n        protoTagList.add(ProtoTag.newBuilder().setKey(\"key2\").setValue(\"value2\").build());\n        final Map<String, String> osmTagMap = new HashMap<>();\n        osmTagMap.put(\"key1\", \"value1\");\n        osmTagMap.put(\"key2\", \"value2\");\n\n        final Map<String, String> mapFromList = converter.convert(protoTagList);\n        Assert.assertEquals(osmTagMap, mapFromList);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/SplittableInputStreamTest.java",
    "content": "package org.openstreetmap.atlas.streaming;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.random.RandomTextGenerator;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.threads.Pool;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class SplittableInputStreamTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(SplittableInputStreamTest.class);\n\n    public static void main(final String[] args)\n    {\n        new SplittableInputStreamTest().largerTest();\n    }\n\n    public void largerTest()\n    {\n        final RandomTextGenerator text = new RandomTextGenerator();\n        try (SplittableInputStream split = new SplittableInputStream(text.infiniteStream());\n                Pool pool = new Pool(2, \"testSplitStream\", Duration.ONE_HOUR))\n        {\n            final InputStream in2 = split.split();\n            logger.info(\"Starting!\");\n            pool.queue(() -> new InputStreamResource(() -> split).lines()\n                    .forEach(line -> logger.info(\"IN-1: {}\", line)));\n            pool.queue(() -> new InputStreamResource(() -> in2).lines()\n                    .forEach(line -> logger.info(\"IN-2: {}\", line)));\n            Duration.ONE_HOUR.sleep();\n        }\n        catch (final IOException e)\n        {\n            throw new CoreException(\"Failed split stream\", e);\n        }\n    }\n\n    @Test\n    public void splitTest()\n    {\n        final InputStream input = new StringInputStream(\"line1: blah\\nline2: haha\");\n        final SplittableInputStream split = new SplittableInputStream(input);\n        final InputStream in2 = split.split();\n        logger.info(\"{}\", Iterables.asList(new InputStreamResource(() -> split).lines()));\n        logger.info(\"{}\", Iterables.asList(new InputStreamResource(() -> in2).lines()));\n        Streams.close(split);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/readers/CsvReaderTest.java",
    "content": "package org.openstreetmap.atlas.streaming.readers;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.AbstractResource;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.conversion.StringConverter;\nimport org.openstreetmap.atlas.utilities.scalars.Counter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Test the {@link CsvReader}\n *\n * @author matthieun\n */\npublic class CsvReaderTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(CsvReaderTest.class);\n\n    private static final StringConverter<String> CONVERTER1 = StringConverter.IDENTITY;\n    private static final StringConverter<Long> CONVERTER2 = string -> Long.valueOf(string);\n    private static final StringConverter<Double> CONVERTER3 = string -> Double.valueOf(string);\n\n    private AbstractResource resource;\n    private AbstractResource wrongResource;\n    private CsvSchema schema;\n\n    @Before\n    public void init()\n    {\n        this.resource = new InputStreamResource(\n                () -> CsvReaderTest.class.getResourceAsStream(\"data.csv\"));\n        this.wrongResource = new InputStreamResource(\n                () -> CsvReaderTest.class.getResourceAsStream(\"wrongData.csv\"));\n        // Create the schema with 3 converters in order.\n        this.schema = new CsvSchema(CONVERTER1, CONVERTER2, CONVERTER3);\n    }\n\n    @Test\n    public void testReader()\n    {\n        final CsvReader reader = new CsvReader(this.schema, this.resource);\n        final Counter counter = new Counter();\n        reader.forEachRemaining(csvLine ->\n        {\n            logger.info(csvLine.toString());\n            if (counter.getValue() == 0)\n            {\n                Assert.assertEquals(\"Hello,world\", csvLine.get(0));\n            }\n            if (counter.getValue() == 1)\n            {\n                Assert.assertEquals(36.3, csvLine.get(2));\n                Assert.assertEquals(true, csvLine.get(1) instanceof Long);\n            }\n            counter.increment();\n        });\n        try\n        {\n            new CsvReader(this.schema, this.wrongResource).next();\n            // Malformed lines are just ignored here.\n            // Assert.fail(\"The wrong resource has to fail.\");\n        }\n        catch (final Exception e)\n        {\n            logger.info(\"Wrong resource: \" + e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/readers/GeoJsonReaderTest.java",
    "content": "package org.openstreetmap.atlas.streaming.readers;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.streaming.readers.json.serializers.PropertiesLocated;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class GeoJsonReaderTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(GeoJsonReaderTest.class);\n\n    @Test\n    public void testReadingMulti()\n    {\n        final GeoJsonReader reader = new GeoJsonReader(new InputStreamResource(\n                () -> GeoJsonReaderTest.class.getResourceAsStream(\"geojson-sample.json\")));\n        reader.forEachRemaining(located -> logger.info(located.toString()));\n    }\n\n    @Test\n    public void testReadingMultiPolygon()\n    {\n        final GeoJsonReader reader = new GeoJsonReader(new InputStreamResource(\n                () -> GeoJsonReaderTest.class.getResourceAsStream(\"geojson-multipolygon.json\")));\n\n        Assert.assertTrue(reader.hasNext());\n        final PropertiesLocated located = reader.next();\n        final MultiPolygon multiPolygon = (MultiPolygon) located.getItem();\n        Assert.assertEquals(2, multiPolygon.outers().size());\n        Assert.assertEquals(1, multiPolygon.inners().size());\n\n        // test non-duplicated first point\n        for (final Polygon polygon : multiPolygon)\n        {\n            Assert.assertTrue(polygon.finalHeading().isPresent());\n        }\n        logger.info(located.toString());\n    }\n\n    @Test\n    public void testReadingPoint()\n    {\n        final GeoJsonReader reader = new GeoJsonReader(new InputStreamResource(\n                () -> GeoJsonReaderTest.class.getResourceAsStream(\"geojson-point.json\")));\n        reader.forEachRemaining(located -> logger.info(located.toString()));\n    }\n\n    @Test\n    public void testReadingPolygon()\n    {\n        final GeoJsonReader reader = new GeoJsonReader(new InputStreamResource(\n                () -> GeoJsonReaderTest.class.getResourceAsStream(\"geojson-polygon.json\")));\n\n        Assert.assertTrue(reader.hasNext());\n        final PropertiesLocated located = reader.next();\n        final Polygon polygon = (Polygon) located.getItem();\n        Assert.assertEquals(4, polygon.segments().size());\n        Assert.assertTrue(polygon.finalHeading().isPresent());\n        Assert.assertTrue(polygon.isApproximatelyNSided(4, Angle.NONE));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/resource/ByteArrayOutputStreamExceptional.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\n\n/**\n * The whole purpose of this class is to throw when closed (which ByteArrayOutputStream does not).\n * This should never be used in production, and should be used only in tests.\n * \n * @author Taylor Smock\n */\npublic class ByteArrayOutputStreamExceptional extends ByteArrayOutputStream\n{\n    private boolean isClosed;\n    private boolean throwOnClose;\n\n    @Override\n    public void close() throws IOException\n    {\n        if (this.throwOnClose)\n        {\n            throw new IOException(\"This stream is closed\");\n        }\n        this.isClosed = true;\n    }\n\n    /**\n     * Check if this resource is closed\n     * \n     * @return {@code true} if this resource has been closed\n     */\n    public boolean isClosed()\n    {\n        return this.isClosed;\n    }\n\n    /**\n     * Use to force this OutputStream to throw an exception on close\n     * \n     * @param throwOnClose\n     *            {@code true} to force a throw on close.\n     */\n    public void setThrowOnClose(final boolean throwOnClose)\n    {\n        this.throwOnClose = throwOnClose;\n    }\n\n    @Override\n    public byte[] toByteArray() throws UncheckedIOException\n    {\n        this.checkClosed();\n        return super.toByteArray();\n    }\n\n    @Override\n    public void write(final int byteWrite)\n    {\n        this.checkClosed();\n        super.write(byteWrite);\n    }\n\n    @Override\n    public synchronized void write(final byte[] bytes, final int off, final int len)\n    {\n        this.checkClosed();\n        super.write(bytes, off, len);\n    }\n\n    @Override\n    public void writeBytes(final byte[] bytes) throws UncheckedIOException\n    {\n        this.checkClosed();\n        super.writeBytes(bytes);\n    }\n\n    private void checkClosed()\n    {\n        if (this.isClosed)\n        {\n            throw new UncheckedIOException(new IOException(\"This stream is closed\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/resource/FileSuffixTestCase.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Test Cases for new File Detection in the File Suffix Enum\n *\n * @author Jack\n */\npublic class FileSuffixTestCase\n{\n    @Test\n    public void testDoesntExistNoMatch()\n    {\n        Assert.assertFalse(FileSuffix.CSV.matches(new FileSuffixTestCaseResource(\"test.fizzbuzz\")));\n    }\n\n    @Test\n    public void testExistsButNoMatch()\n    {\n        Assert.assertFalse(FileSuffix.CSV.matches(new FileSuffixTestCaseResource(\"test.csv.ext\")));\n    }\n\n    @Test\n    public void testMatches()\n    {\n        Assert.assertTrue(FileSuffix.CSV.matches(new FileSuffixTestCaseResource(\"test.csv\")));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/resource/FileSuffixTestCaseResource.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.InputStream;\n\n/**\n * This is for testing the FileSuffix classes, so we only need the getName method\n *\n * @author Jack\n */\nclass FileSuffixTestCaseResource implements Resource\n{\n    private final String name;\n\n    FileSuffixTestCaseResource(final String name)\n    {\n        this.name = name;\n    }\n\n    @Override\n    public String getName()\n    {\n        return this.name;\n    }\n\n    @Override\n    public long length()\n    {\n        return 0;\n    }\n\n    @Override\n    public InputStream read()\n    {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/resource/FileTest.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.FileSystem;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class FileTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testChild()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path homePath = filesystem.getPath(\"/Users/foobar\");\n            final File home = new File(homePath);\n\n            final File directChild = home.child(\"child\");\n            directChild.writeAndClose(\"foo\");\n\n            final File testDirectChildContents = new File(\n                    filesystem.getPath(\"/Users/foobar/child\"));\n            Assert.assertEquals(\"foo\", testDirectChildContents.all());\n\n            // Now create a symlink to foobar's home folder\n            Files.createDirectory(filesystem.getPath(\"/tmp\"));\n            final Path linkPath = filesystem.getPath(\"/tmp/link\");\n            Files.createSymbolicLink(linkPath, homePath);\n            final File linkChild = home.child(\"child\");\n            linkChild.writeAndClose(\"foo\");\n\n            final File testLinkChildContents = new File(filesystem.getPath(\"/tmp/link/child\"));\n            Assert.assertEquals(\"foo\", testLinkChildContents.all());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testChildFailDueToParentIsAFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/bazbat/file\");\n            final File file = new File(filePath, true);\n            file.writeAndClose(\"foobar\");\n\n            this.expectedException.expect(CoreException.class);\n            this.expectedException\n                    .expectMessage(\"Could not create directories for path /Users/bazbat/file\");\n            file.child(\"subdir\");\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testChildFailDueToParentIsSymlinkToFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/bazbat/file\");\n            final File file = new File(filePath, true);\n            file.writeAndClose(\"foobar\");\n            final Path linkPath = filesystem.getPath(\"/Users/bazbat/link\");\n            Files.createSymbolicLink(linkPath, filePath);\n            final File linkFile = new File(linkPath);\n\n            this.expectedException.expect(CoreException.class);\n            this.expectedException.expectMessage(\n                    \"Cannot create the child of file /Users/bazbat/link since it did not resolve to a directory\");\n            linkFile.child(\"subdir\");\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testConstructors()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file.gz\");\n            final File file = new File(filePath, true);\n            file.writeAndClose(\"foobar\");\n            Assert.assertEquals(\"foobar\", file.all());\n\n            final Path file2Path = filesystem.getPath(\"/Users/bazbat/file\");\n            final File file2 = new File(file2Path, false);\n            Assert.assertFalse(file2.exists());\n            final File dir = new File(\"/Users/bazbat\", filesystem, false);\n            Assert.assertFalse(dir.exists());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testDelete()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file\");\n            final File file = new File(filePath);\n            file.writeAndClose(\"foobar\");\n            Assert.assertTrue(file.exists());\n            Assert.assertEquals(\"foobar\", file.all());\n            file.delete();\n            Assert.assertFalse(file.exists());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testDeleteFailDueToNonExistentFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file\");\n            final File file = new File(filePath);\n            Assert.assertFalse(file.exists());\n\n            this.expectedException.expect(CoreException.class);\n            this.expectedException.expectMessage(\"Cannot delete file /Users/foobar/file\");\n            file.delete();\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testDeleteRecursive()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path homePath = filesystem.getPath(\"/Users/foobar\");\n            final File home = new File(homePath);\n            final File child1 = home.child(\"child1\");\n            child1.writeAndClose(\"foobar\");\n            final File child2 = home.child(\"child2\");\n            final File child3 = child2.child(\"child3\");\n            child3.writeAndClose(\"foobar\");\n\n            Assert.assertTrue(home.exists());\n            Assert.assertTrue(home.isDirectory());\n            home.deleteRecursively();\n            Assert.assertFalse(home.exists());\n            Assert.assertTrue(new File(filesystem.getPath(\"/Users\")).exists());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testDeleteRecursiveFailDueToNonExistentFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file\");\n            final File file = new File(filePath);\n            Assert.assertFalse(file.exists());\n\n            this.expectedException.expect(CoreException.class);\n            this.expectedException\n                    .expectMessage(\"Cannot delete folder /Users/foobar/file recursively\");\n            file.deleteRecursively();\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testFileComparison()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path file1Path = filesystem.getPath(\"/Users/foobar/file1\");\n            final File file1 = new File(file1Path);\n            final Path file2Path = filesystem.getPath(\"/Users/foobar/file2\");\n            final File file2 = new File(file2Path);\n            file1.writeAndClose(\"foobar\\n\");\n            file2.writeAndClose(\"foobar\\n\");\n\n            Assert.assertTrue(file1.compareTo(file2) < 0);\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testFileEqualityAndHashcodeContracts()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path file1Path = filesystem.getPath(\"/Users/foobar/file1\");\n            final File file1 = new File(file1Path);\n            final Path file2Path = filesystem.getPath(\"/Users/foobar/file2\");\n            final File file2 = new File(file2Path);\n\n            final Path file1APath = filesystem.getPath(\"/Users/foobar/file1\");\n            final File file1A = new File(file1APath);\n            final Path file2APath = filesystem.getPath(\"/Users/foobar/file2\");\n            final File file2A = new File(file2APath);\n\n            Assert.assertEquals(file1, file1);\n            Assert.assertEquals(file1, file1A);\n            Assert.assertEquals(file1A, file1A);\n            Assert.assertEquals(file1.hashCode(), file1.hashCode());\n            Assert.assertEquals(file1.hashCode(), file1A.hashCode());\n            Assert.assertEquals(file1A.hashCode(), file1A.hashCode());\n\n            Assert.assertEquals(file2, file2);\n            Assert.assertEquals(file2, file2A);\n            Assert.assertEquals(file2A, file2A);\n            Assert.assertEquals(file2.hashCode(), file2.hashCode());\n            Assert.assertEquals(file2.hashCode(), file2A.hashCode());\n            Assert.assertEquals(file2A.hashCode(), file2A.hashCode());\n\n            Assert.assertNotEquals(file1, file2);\n            Assert.assertNotEquals(file1, file2A);\n            Assert.assertNotEquals(file1A, file2);\n            Assert.assertNotEquals(file1A, file2A);\n            Assert.assertNotEquals(file1.hashCode(), file2.hashCode());\n            Assert.assertNotEquals(file1.hashCode(), file2A.hashCode());\n            Assert.assertNotEquals(file1A.hashCode(), file2.hashCode());\n            Assert.assertNotEquals(file1A.hashCode(), file2A.hashCode());\n\n            Assert.assertNotEquals(file1, \"somerandomthing\");\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testFileReadWrite()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path directoryPath = filesystem.getPath(\"/tmp\");\n            Files.createDirectory(directoryPath);\n\n            final Path filePath = filesystem.getPath(\"/tmp/foo\");\n            Files.createFile(filePath);\n            final List<String> lines = Arrays.asList(\"one\", \"two\", \"three\");\n            Files.write(filePath, lines, StandardCharsets.UTF_8);\n\n            final File file = new File(filePath);\n            Assert.assertEquals(\"one\\ntwo\\nthree\", file.all());\n\n            // Now we read by following a symlink\n            final Path symlinkPath = filesystem.getPath(\"/tmp/bar\");\n            Files.createSymbolicLink(symlinkPath, filePath);\n            final File symlink = new File(symlinkPath);\n            Assert.assertEquals(\"one\\ntwo\\nthree\", symlink.all());\n\n            // Now let's try writing/reading with compression\n            final Path compressedPath = filesystem.getPath(\"/tmp/compressed.gz\");\n            final File compressedFile = new File(compressedPath).withCompressor(Compressor.GZIP)\n                    .withDecompressor(Decompressor.GZIP);\n            compressedFile.writeAndClose(\"one\\ntwo\\nthree\");\n            Assert.assertTrue(compressedFile.isGzipped());\n            Assert.assertEquals(\"one\\ntwo\\nthree\", compressedFile.all());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testLength()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file\");\n            final File file = new File(filePath);\n            file.writeAndClose(\"foobar\\n\");\n\n            Assert.assertEquals(7L, file.length());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testLengthFailDueToNonexistentFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file\");\n            final File file = new File(filePath);\n\n            this.expectedException.expect(CoreException.class);\n            this.expectedException.expectMessage(\"Could not get length of file /Users/foobar/file\");\n            file.length();\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testListFiles()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final File home = new File(filesystem.getPath(\"/Users/foobar\"));\n            final File file1 = home.child(\"file1\");\n            file1.writeAndClose(\"foobar\\n\");\n            final File file2 = home.child(\"file2\");\n            file2.writeAndClose(\"bazbat\\n\");\n            final File dir1 = home.child(\"dir1\");\n            final File file3 = dir1.child(\"file3\");\n            file3.writeAndClose(\"fred\\n\");\n\n            Assert.assertEquals(Sets.hashSet(file1, file2, dir1),\n                    new HashSet<>(home.listFiles(true)));\n            Assert.assertEquals(Sets.hashSet(file1, file2), new HashSet<>(home.listFiles()));\n            Assert.assertEquals(Sets.hashSet(file1), new HashSet<>(file1.listFiles()));\n\n            Assert.assertTrue(\n                    new HashSet<>(new File(filesystem.getPath(\"/foo/bar\")).listFiles()).isEmpty());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testListFilesRecursively()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final File home = new File(filesystem.getPath(\"/Users/foobar\"));\n            final File file1 = home.child(\"file1\");\n            file1.writeAndClose(\"foobar\\n\");\n            final File file2 = home.child(\"file2\");\n            file2.writeAndClose(\"bazbat\\n\");\n            final File dir1 = home.child(\"dir1\");\n            final File file3 = dir1.child(\"file3\");\n            file3.writeAndClose(\"fred\\n\");\n            final File dir2 = dir1.child(\"dir2\");\n            final File file4 = dir2.child(\"file4\");\n            file4.writeAndClose(\"ned\\n\");\n\n            Assert.assertEquals(Sets.hashSet(file1, file2, file3, file4, dir1, dir2),\n                    new HashSet<>(home.listFilesRecursively(true)));\n            Assert.assertEquals(Sets.hashSet(file1, file2, file3, file4),\n                    new HashSet<>(home.listFilesRecursively()));\n            Assert.assertEquals(Sets.hashSet(file1), new HashSet<>(file1.listFilesRecursively()));\n            Assert.assertTrue(\n                    new HashSet<>(new File(filesystem.getPath(\"/foo/bar\")).listFilesRecursively())\n                            .isEmpty());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testMkdirs()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path homeFolderPath = filesystem.getPath(\"/Users/foobar\");\n            final File homeFolder = new File(homeFolderPath, false);\n            Assert.assertFalse(Files.exists(filesystem.getPath(\"/Users\")));\n            Assert.assertFalse(Files.exists(homeFolder.toPath()));\n            homeFolder.mkdirs();\n            Assert.assertTrue(Files.exists(filesystem.getPath(\"/Users\")));\n            Assert.assertTrue(Files.exists(homeFolder.toPath()));\n            Assert.assertTrue(Files.isDirectory(homeFolder.toPath()));\n\n            final File homeChildFolder = homeFolder.child(\"subdir\");\n            Assert.assertFalse(Files.exists(homeChildFolder.toPath()));\n            homeChildFolder.mkdirs();\n            Assert.assertTrue(Files.exists(homeChildFolder.toPath()));\n            Assert.assertTrue(Files.isDirectory(homeChildFolder.toPath()));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testParent()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file\");\n            final File file = new File(filePath);\n            file.writeAndClose(\"foobar\\n\");\n\n            Assert.assertEquals(\"/Users/foobar\", file.parent().getAbsolutePathString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testProperSymlinkHandling()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path homeDirectoryPath = filesystem.getPath(\"/Users/foobar\");\n            Files.createDirectories(homeDirectoryPath);\n\n            final Path desktopPath = homeDirectoryPath.resolve(\"Desktop\");\n            Files.createDirectory(desktopPath);\n\n            final Path directoryOnDesktopPath = desktopPath.resolve(\"folder_on_desktop\");\n            Files.createDirectory(directoryOnDesktopPath);\n\n            final Path fileOnDesktopPath = desktopPath.resolve(\"file_on_desktop\");\n            Files.createFile(fileOnDesktopPath);\n            final List<String> lines = Arrays.asList(\"foo\", \"bar\");\n            Files.write(fileOnDesktopPath, lines, StandardCharsets.UTF_8);\n\n            final Path tmpDirectoryPath = filesystem.getPath(\"/tmp\");\n            Files.createDirectory(tmpDirectoryPath);\n\n            final Path symlinkToDesktopPath = tmpDirectoryPath.resolve(\"symlink_to_desktop\");\n            Files.createSymbolicLink(symlinkToDesktopPath, desktopPath);\n\n            final File desktopFolder = new File(desktopPath);\n\n            Assert.assertTrue(Files.exists(desktopFolder.toPath()));\n            Assert.assertTrue(Files.isDirectory(desktopFolder.toPath()));\n\n            final File symlinkToDesktop = new File(symlinkToDesktopPath);\n            final File fileOnDesktopThruSymlink = symlinkToDesktop.child(\"file_on_desktop\");\n            final File nonexistentFileOnDesktopThruSymlink = symlinkToDesktop\n                    .child(\"nonexistant_file_on_desktop\");\n\n            Assert.assertTrue(fileOnDesktopThruSymlink.exists());\n            Assert.assertFalse(nonexistentFileOnDesktopThruSymlink.exists());\n            nonexistentFileOnDesktopThruSymlink.writeAndClose(\"foobar\");\n            Assert.assertTrue(nonexistentFileOnDesktopThruSymlink.exists());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testReadFailDueToNonexistentFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file\");\n            final File file = new File(filePath);\n            Assert.assertFalse(file.exists());\n\n            this.expectedException.expect(CoreException.class);\n            this.expectedException.expectMessage(\"Cannot read file /Users/foobar/file\");\n            file.all();\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testStringMethods()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/file\");\n            final File file = new File(filePath);\n            file.writeAndClose(\"foobar\");\n\n            Assert.assertEquals(\"file\", file.basename());\n            Assert.assertEquals(\"file\", file.getName());\n            file.withName(\"myFile\");\n            Assert.assertEquals(\"myFile\", file.getName());\n            Assert.assertEquals(\"/Users/foobar/file\", file.getAbsolutePathString());\n            Assert.assertEquals(\"/Users/foobar/file\", file.toString());\n            Assert.assertEquals(\"/Users/foobar/file\", file.getPathString());\n            Assert.assertEquals(\"/Users/foobar\", file.getParentPathString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testTemporaryFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final TemporaryFile file = File.temporary(filesystem);\n            try (file)\n            {\n                file.writeAndClose(\"foobar\");\n                Assert.assertEquals(\"foobar\", file.all());\n            }\n            Assert.assertFalse(file.exists());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testTemporaryFolder()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final File file;\n            final TemporaryFile folder = File.temporaryFolder(filesystem);\n            try (folder)\n            {\n                file = folder.child(\"child\");\n                file.writeAndClose(\"foobar\");\n                Assert.assertEquals(\"foobar\", file.all());\n            }\n            Assert.assertFalse(file.exists());\n            Assert.assertFalse(folder.exists());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testWriteFailDueToFileIsDirectory()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final Path filePath = filesystem.getPath(\"/Users/foobar/dir\");\n            final File file = new File(filePath);\n            file.mkdirs();\n            Assert.assertTrue(file.isDirectory());\n\n            this.expectedException.expect(CoreException.class);\n            this.expectedException.expectMessage(\"Could not write to /Users/foobar/dir\");\n            file.writeAndClose(\"foobar\");\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/resource/InputStreamResourceCloseableTest.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\n\nimport org.junit.jupiter.api.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Test class for {@link InputStreamResourceCloseable}\n * \n * @author Taylor Smock\n */\nclass InputStreamResourceCloseableTest\n{\n    /** An arbitrary string to use for input/output streams */\n    private static final String HELLO_WORLD = \"Hello World\";\n\n    /**\n     * Test that the class properly closes a resource\n     * \n     * @throws Exception\n     *             If an untested exception is thrown\n     */\n    @Test\n    void testClosingResource() throws Exception\n    {\n        final ByteArrayOutputStream outputStream = new ByteArrayOutputStreamExceptional();\n        outputStream.writeBytes(HELLO_WORLD.getBytes());\n        try (InputStreamResourceCloseable inputStreamResource = new InputStreamResourceCloseable(\n                () -> new ByteArrayInputStream(outputStream.toByteArray()), outputStream))\n        {\n            // Check twice for open/close of ByteArrayInputStream\n            assertEquals(HELLO_WORLD, assertDoesNotThrow(() -> inputStreamResource.firstLine()));\n            assertEquals(HELLO_WORLD, assertDoesNotThrow(() -> inputStreamResource.firstLine()));\n            outputStream.close();\n            final UncheckedIOException uncheckedIOException = assertThrows(\n                    UncheckedIOException.class, () -> inputStreamResource.firstLine());\n            assertTrue(uncheckedIOException.getCause() instanceof IOException);\n        }\n    }\n\n    /**\n     * Test that a thrown exception is passed back\n     * \n     * @throws Exception\n     *             If an untested exception is thrown\n     */\n    @Test\n    void testExceptionOnClosing() throws Exception\n    {\n        final ByteArrayOutputStreamExceptional outputStream = new ByteArrayOutputStreamExceptional();\n        outputStream.write(HELLO_WORLD.getBytes());\n        final InputStreamResourceCloseable inputStreamResource = new InputStreamResourceCloseable(\n                () -> new ByteArrayInputStream(outputStream.toByteArray()), outputStream);\n        assertDoesNotThrow(() -> outputStream.close());\n        outputStream.setThrowOnClose(true);\n\n        assertThrows(CoreException.class, () -> inputStreamResource.close());\n    }\n\n    /**\n     * Check that this class fails when improperly used\n     * \n     * @throws Exception\n     *             If an untested exception is thrown\n     */\n    @Test\n    void testTryWithResources() throws Exception\n    {\n        final InputStreamResourceCloseable inputStreamResource;\n        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStreamExceptional())\n        {\n            outputStream.write(HELLO_WORLD.getBytes());\n            inputStreamResource = new InputStreamResourceCloseable(\n                    () -> new ByteArrayInputStream(outputStream.toByteArray()),\n                    (AutoCloseable[]) null);\n            assertEquals(HELLO_WORLD, assertDoesNotThrow(() -> inputStreamResource.firstLine()));\n        }\n        final UncheckedIOException uncheckedIOException = assertThrows(UncheckedIOException.class,\n                () -> inputStreamResource.firstLine());\n        assertTrue(uncheckedIOException.getCause() instanceof IOException);\n        inputStreamResource.close();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/resource/OutputStreamWritableResourceCloseableTest.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.OutputStream;\nimport java.io.UncheckedIOException;\n\nimport org.junit.jupiter.api.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * Test class for {@link OutputStreamWritableResourceCloseable}\n * \n * @author Taylor Smock\n */\nclass OutputStreamWritableResourceCloseableTest\n{\n    private static final String HELLO_WORLD = \"Hello World\";\n    private static final byte[] HELLO_WORLD_BYTES = HELLO_WORLD.getBytes();\n\n    @Test\n    void testClosingResource() throws Exception\n    {\n        final ByteArrayOutputStreamExceptional outputStream = new ByteArrayOutputStreamExceptional();\n        final OutputStreamWritableResourceCloseable resource = new OutputStreamWritableResourceCloseable(\n                outputStream, (AutoCloseable[]) null);\n        assertDoesNotThrow(() -> resource.write().write(HELLO_WORLD_BYTES));\n        assertEquals(HELLO_WORLD, new String(outputStream.toByteArray()));\n\n        outputStream.close();\n        assertTrue(outputStream.isClosed());\n        final OutputStream writer = resource.write();\n        assertThrows(UncheckedIOException.class, () -> writer.write(HELLO_WORLD_BYTES));\n        resource.close();\n    }\n\n    @Test\n    void testCorrectImplementation() throws Exception\n    {\n        final ByteArrayOutputStreamExceptional outputStream = new ByteArrayOutputStreamExceptional();\n        final OutputStreamWritableResourceCloseable resource = new OutputStreamWritableResourceCloseable(\n                outputStream, outputStream);\n        assertDoesNotThrow(() -> resource.write().write(HELLO_WORLD_BYTES));\n        assertEquals(HELLO_WORLD, new String(outputStream.toByteArray()));\n\n        resource.close();\n        assertTrue(outputStream.isClosed());\n    }\n\n    @Test\n    void testTryWithResources() throws Exception\n    {\n        final ByteArrayOutputStreamExceptional outputStream = new ByteArrayOutputStreamExceptional();\n        final OutputStreamWritableResourceCloseable resource = new OutputStreamWritableResourceCloseable(\n                outputStream, outputStream);\n\n        try (ByteArrayOutputStreamExceptional temp = outputStream)\n        {\n            assertDoesNotThrow(() -> resource.write().write(HELLO_WORLD_BYTES));\n            assertEquals(HELLO_WORLD, new String(outputStream.toByteArray()));\n        }\n\n        final OutputStream writer = resource.write();\n        assertThrows(UncheckedIOException.class, () -> writer.write(HELLO_WORLD_BYTES));\n        outputStream.setThrowOnClose(true);\n        assertThrows(CoreException.class, () -> resource.close());\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/resource/ResourceTest.java",
    "content": "package org.openstreetmap.atlas.streaming.resource;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.readers.CsvReaderTest;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class ResourceTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(ResourceTest.class);\n\n    @Test\n    public void testRead()\n    {\n        AbstractResource resource = new InputStreamResource(\n                () -> CsvReaderTest.class.getResourceAsStream(\"data.csv\"));\n        resource.lines().forEach(logger::info);\n        resource = new InputStreamResource(\n                () -> CsvReaderTest.class.getResourceAsStream(\"data.csv\"));\n        Assert.assertEquals(3, Iterables.size(resource.lines()));\n    }\n\n    // Examples of how the HttpResource works\n    /*\n     * @Test public void testHttpResource() { try { final HttpResource get = new GetResource( new\n     * URIBuilder(\"http://localhost:5000/\").build()); Assert.assertEquals(200, get.getStatusCode());\n     * // just check the first line Assert.assertEquals(\"<!DOCTYPE html>\",\n     * get.lines().iterator().next()); get.close(); // not authenticated final HttpResource delete =\n     * new DeleteResource( new\n     * URIBuilder(\"http://localhost:5000/api/admin/challenge/22263\").build());\n     * Assert.assertEquals(401, delete.getStatusCode()); delete.setAuth(\"testuser\", \"password\");\n     * delete.reExecute(); //Assert.assertEquals(200, delete.getStatusCode()); delete.close(); final\n     * byte[] body =\n     * \"{\\\"title\\\":\\\"HttpResource22263\\\",\\\"description\\\":\\\"Testing out the HttpResource\\\",\\\"blurb\\\":\\\"Testing out the HttpResource\\\",\\\"help\\\":\\\"Testing out the HttpResource\\\",\\\"difficulty\\\":1,\\\"active\\\":true}\"\n     * .getBytes(); final HttpResource post = new PostResource(new\n     * URIBuilder(\"http://localhost:5000/api/admin/challenge/22263\").build(), body);\n     * post.setHeader(HttpHeaders.CONTENT_TYPE, \"application/json\"); post.setAuth(\"testuser\",\n     * \"password\"); Assert.assertEquals(201, post.getStatusCode()); } catch (Exception e) { throw\n     * new CoreException(\"failed\", e); } }\n     */\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/resource/zip/ZipResourceTest.java",
    "content": "package org.openstreetmap.atlas.streaming.resource.zip;\n\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.nio.file.FileSystem;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.random.RandomTextGenerator;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author matthieun\n */\npublic class ZipResourceTest\n{\n    /**\n     * @author matthieun\n     */\n    private static class TwoStringObject implements Serializable\n    {\n        private static final long serialVersionUID = 2404840862177380282L;\n        private final String one;\n        private final String two;\n\n        TwoStringObject(final long size)\n        {\n            this.one = new RandomTextGenerator().generate(size);\n            this.two = new RandomTextGenerator().generate(size);\n        }\n\n        public String getOne()\n        {\n            return this.one;\n        }\n\n        public String getTwo()\n        {\n            return this.two;\n        }\n    }\n\n    public static final String NAME_1 = \"entry1.txt\";\n    public static final String CONTENTS_1 = \"I am entry 1.\";\n    public static final String NAME_2 = \"entry2.txt\";\n    public static final String CONTENTS_2 = \"I am entry 2.\";\n    private static final Logger logger = LoggerFactory.getLogger(ZipResourceTest.class);\n\n    public static void main(final String[] args)\n    {\n        try\n        {\n            new ZipResourceTest().testSizes();\n        }\n        catch (final Exception e)\n        {\n            e.printStackTrace();\n        }\n        // This is used to re-create the \"test.zip\" in the resources package.\n        // final ZipWritableResource writable = new ZipWritableResource(new File(args[0]));\n        // writable.writeAndClose(new StringResource(CONTENTS_1).withName(NAME_1),\n        // new StringResource(CONTENTS_2).withName(NAME_2));\n    }\n\n    @Test\n    public void testHighCompressionLevel() throws IOException\n    {\n        final File source = File.temporary();\n        logger.info(\"testCompressionLevel using {}\", source);\n        try\n        {\n            final ZipFileWritableResource zipFile = new ZipFileWritableResource(source);\n            zipFile.writeAndClose(\n                    new StringResource(\"HereIsSomeTextThatRepeatsHereIsSomeTextThatRepeats\")\n                            .withName(NAME_1),\n                    new StringResource(\"HereIsSomeTextThatDoesn'tRepeat\").withName(NAME_2));\n            final ZipFile file = new ZipFile(source.getFile());\n            final ZipEntry name1 = file.getEntry(NAME_1);\n            Assert.assertNotEquals(-1, name1.getCompressedSize());\n            Assert.assertTrue(name1.getCompressedSize() < name1.getSize());\n            final ZipEntry name2 = file.getEntry(NAME_2);\n            Assert.assertNotEquals(-1, name2.getCompressedSize());\n            Assert.assertTrue(name2.getCompressedSize() >= name2.getSize());\n            file.close();\n        }\n        finally\n        {\n            source.delete();\n            logger.info(\"testZipFile deleted {}\", source);\n        }\n    }\n\n    @Test\n    public void testNoCompressionLevel() throws IOException\n    {\n        final File source = File.temporary();\n        logger.info(\"testCompressionLevel using {}\", source);\n        try\n        {\n            final ZipFileWritableResource zipFile = new ZipFileWritableResource(source);\n            zipFile.setWriteCompression(false);\n            zipFile.writeAndClose(\n                    new StringResource(\"HereIsSomeTextThatRepeatsHereIsSomeTextThatRepeats\")\n                            .withName(NAME_1),\n                    new StringResource(\"HereIsSomeTextThatDoesn'tRepeat\").withName(NAME_2));\n            final ZipFile file = new ZipFile(source.getFile());\n            final ZipEntry name1 = file.getEntry(NAME_1);\n            Assert.assertNotEquals(-1, name1.getCompressedSize());\n            Assert.assertTrue(name1.getCompressedSize() >= name1.getSize());\n            final ZipEntry name2 = file.getEntry(NAME_2);\n            Assert.assertNotEquals(-1, name2.getCompressedSize());\n            Assert.assertTrue(name2.getCompressedSize() >= name2.getSize());\n            file.close();\n        }\n        finally\n        {\n            source.delete();\n            logger.info(\"testZipFile deleted {}\", source);\n        }\n    }\n\n    public void testSizes() throws Exception\n    {\n        final long size = 100000;\n        final ByteArrayResource javaOutput = new ByteArrayResource().withName(\"Java\");\n        final ByteArrayResource zippedOutput = new ByteArrayResource().withName(\"Zipped\");\n        javaOutput.setCompressor(Compressor.GZIP);\n        zippedOutput.setCompressor(Compressor.GZIP);\n        final TwoStringObject object = new TwoStringObject(size);\n        final Resource oneResource = new StringResource(object.getOne()).withName(\"one\");\n        final Resource twoResource = new StringResource(object.getTwo()).withName(\"two\");\n\n        final ObjectOutputStream out = new ObjectOutputStream(javaOutput.write());\n        out.writeObject(object);\n        Streams.close(out);\n\n        new ZipWritableResource(zippedOutput).writeAndClose(oneResource, twoResource);\n\n        System.out.println(javaOutput.getName() + \" \" + javaOutput.length());\n        System.out.println(zippedOutput.getName() + \" \" + zippedOutput.length());\n    }\n\n    @Test\n    public void testWithJimfs()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ZipFileWritableResource zipFile = new ZipFileWritableResource(\n                    new File(\"/Users/foo/file.zip\", filesystem));\n            final Resource entry2 = zipFile.entryForName(NAME_2);\n            final String name2 = entry2.getName();\n            final String contents2 = entry2.all();\n            logger.info(name2 + \" -> \" + contents2);\n            Assert.assertEquals(NAME_2, name2);\n            Assert.assertEquals(CONTENTS_2, contents2);\n            // check contents again to test Resource re-read\n            final String contents2Again = entry2.all();\n            Assert.assertEquals(CONTENTS_2, contents2Again);\n\n            final ZipFileWritableResource zipFile2 = new ZipFileWritableResource(\n                    new File(\"/Users/foo/file.zip\", filesystem));\n            final List<Resource> entries = new ArrayList<Resource>();\n            zipFile2.entries().forEach(entries::add);\n            Assert.assertEquals(NAME_1, entries.get(0).getName());\n            Assert.assertEquals(CONTENTS_1, entries.get(0).all());\n            Assert.assertEquals(NAME_2, entries.get(1).getName());\n            Assert.assertEquals(CONTENTS_2, entries.get(1).all());\n            // check contents again to test Resource re-read\n            Assert.assertEquals(CONTENTS_1, entries.get(0).all());\n            Assert.assertEquals(CONTENTS_2, entries.get(1).all());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testZipFile()\n    {\n        final File source = File.temporary();\n        logger.info(\"testZipFile using {}\", source);\n        try\n        {\n            final ZipFileWritableResource zipFile = new ZipFileWritableResource(source);\n            zipFile.writeAndClose(new StringResource(CONTENTS_1).withName(NAME_1),\n                    new StringResource(CONTENTS_2).withName(NAME_2));\n            // Try to access entries randomly\n            final Resource entry2 = zipFile.entryForName(NAME_2);\n            final String name2 = entry2.getName();\n            final String contents2 = entry2.all();\n            logger.info(name2 + \" -> \" + contents2);\n            Assert.assertEquals(NAME_2, name2);\n            Assert.assertEquals(CONTENTS_2, contents2);\n            final Resource entry1 = zipFile.entryForName(NAME_1);\n            final String name1 = entry1.getName();\n            final String contents1 = entry1.all();\n            logger.info(name1 + \" -> \" + contents1);\n            Assert.assertEquals(NAME_1, name1);\n            Assert.assertEquals(CONTENTS_1, contents1);\n        }\n        finally\n        {\n            source.delete();\n            logger.info(\"testZipFile deleted {}\", source);\n        }\n    }\n\n    @Test\n    public void testZipFileEntries()\n    {\n        final File source = File.temporary();\n        logger.info(\"testZipFileEntries using {}\", source);\n        try\n        {\n            final ZipFileWritableResource zipFile = new ZipFileWritableResource(source);\n            zipFile.writeAndClose(new StringResource(CONTENTS_1).withName(NAME_1),\n                    new StringResource(CONTENTS_2).withName(NAME_2));\n            int counter = 0;\n            for (final Resource entry : zipFile.entries())\n            {\n                final String name = entry.getName();\n                final String contents = entry.all();\n                logger.info(name + \" -> \" + contents);\n                if (counter == 0)\n                {\n                    Assert.assertEquals(NAME_1, name);\n                    Assert.assertEquals(CONTENTS_1, contents);\n                }\n                else\n                {\n                    Assert.assertEquals(NAME_2, name);\n                    Assert.assertEquals(CONTENTS_2, contents);\n                }\n                counter++;\n            }\n        }\n        finally\n        {\n            source.delete();\n            logger.info(\"testZipFileEntries deleted {}\", source);\n        }\n    }\n\n    @Test\n    public void testZipResource()\n    {\n        final ZipResource resource = new ZipResource(new InputStreamResource(\n                () -> ZipResourceTest.class.getResourceAsStream(\"test.zip\")));\n        int counter = 0;\n        for (final Resource entry : resource.entries())\n        {\n            final String name = entry.getName();\n            final String contents = entry.all();\n            logger.info(name + \" -> \" + contents);\n            if (counter == 0)\n            {\n                Assert.assertEquals(NAME_1, name);\n                Assert.assertEquals(CONTENTS_1, contents);\n            }\n            else\n            {\n                Assert.assertEquals(NAME_2, name);\n                Assert.assertEquals(CONTENTS_2, contents);\n            }\n            counter++;\n        }\n    }\n\n    @Test\n    public void testZipResourceStopBefore()\n    {\n        final ZipResource resource = new ZipResource(new InputStreamResource(\n                () -> ZipResourceTest.class.getResourceAsStream(\"test.zip\")));\n        final Iterator<Resource> entryIterator = resource.entries().iterator();\n        entryIterator.next();\n        final String failMessage = \"Should not have been able to print the contents of \"\n                + \"the second entry after not reading the first.\";\n        try\n        {\n            System.out.println(entryIterator.next());\n            Assert.fail(failMessage);\n        }\n        catch (final CoreException e)\n        {\n            if (!ZipResource.PREMATURE_READ_ERROR_MESSAGE.equals(e.getMessage()))\n            {\n                throw e;\n            }\n        }\n    }\n\n    @Test\n    public void testZipWritableResource()\n    {\n        final ByteArrayResource source = new ByteArrayResource();\n        final ZipWritableResource writable = new ZipWritableResource(source);\n        writable.writeAndClose(new StringResource(\"byte! \" + CONTENTS_1).withName(NAME_1),\n                new StringResource(\"byte! \" + CONTENTS_2).withName(NAME_2));\n        final ZipResource resource = new ZipResource(source);\n        int counter = 0;\n        for (final Resource entry : resource.entries())\n        {\n            final String name = entry.getName();\n            final String contents = entry.all();\n            logger.info(name + \" -> \" + contents);\n            if (counter == 0)\n            {\n                Assert.assertEquals(NAME_1, name);\n                Assert.assertEquals(\"byte! \" + CONTENTS_1, contents);\n            }\n            else\n            {\n                Assert.assertEquals(NAME_2, name);\n                Assert.assertEquals(\"byte! \" + CONTENTS_2, contents);\n            }\n            counter++;\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final ZipFileWritableResource zipFile = new ZipFileWritableResource(\n                new File(\"/Users/foo/file.zip\", filesystem));\n        zipFile.writeAndClose(new StringResource(CONTENTS_1).withName(NAME_1),\n                new StringResource(CONTENTS_2).withName(NAME_2));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/streaming/writers/JsonWriterTest.java",
    "content": "package org.openstreetmap.atlas.streaming.writers;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\n\n/**\n * @author matthieun\n */\npublic class JsonWriterTest\n{\n    @Test\n    public void testWrite()\n    {\n        final StringResource resource = new StringResource();\n        final JsonWriter writer = new JsonWriter(resource);\n        final PolyLine polyLine = new PolyLine(Location.TEST_6, Location.TEST_2, Location.TEST_2);\n        writer.write(polyLine.asGeoJson());\n        writer.close();\n        Assert.assertEquals(polyLine.asGeoJson().toString(), resource.writtenString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/AbstractNameFinderTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\n\nimport org.openstreetmap.atlas.tags.names.NameFinder;\n\n/**\n * Base class that provides the freezeDry method for verifying serialization works or not.\n *\n * @author cstaylor\n */\nabstract class AbstractNameFinderTestCase\n{\n    protected NameFinder freezeDry(final NameFinder finder) throws Exception\n    {\n        final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        final ObjectOutputStream oos = new ObjectOutputStream(baos);\n        oos.writeObject(finder);\n        oos.close();\n        final ObjectInputStream ois = new ObjectInputStream(\n                new ByteArrayInputStream(baos.toByteArray()));\n        return (NameFinder) ois.readObject();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/BarrierTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\n\n/**\n * Test for {@link org.openstreetmap.atlas.tags.BarrierTag}\n *\n * @author alexhsieh\n */\npublic class BarrierTagTestCase\n{\n    @Test\n    public void busTrapIsBlockingTestCase()\n    {\n        final Taggable taggable = Taggable.with(\"barrier\", \"bus_trap\");\n        Assert.assertTrue(BarrierTag.isBarrier(taggable));\n    }\n\n    @Test\n    public void busTrapTestCase()\n    {\n        final Taggable taggable = Taggable.with(\"barrier\", \"bus_trap\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, BarrierTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/BulkNameFinderForcedLocalizableTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.Taggable.TagSearchOption;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.tags.names.BulkNameFinder;\nimport org.openstreetmap.atlas.tags.names.BulkNameFinder.BulkFindResults;\nimport org.openstreetmap.atlas.tags.names.InternationallyKnownAsTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test case verifying that we can optionally force a tag value search to localize a normally\n * non-localizable tag\n *\n * @author cstaylor\n */\npublic class BulkNameFinderForcedLocalizableTestCase\n{\n    private static final Optional<IsoLanguage> RUSSIAN = IsoLanguage.forLanguageCode(\"ru\");\n\n    private Taggable localizedDataForNonLocalizableKey;\n\n    @Test\n    public void forceLocalized()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet()\n                .withLanguage(RUSSIAN.get()).forceLocalized()\n                .findIn(this.localizedDataForNonLocalizableKey);\n        Assert.assertTrue(results.valueFor(RUSSIAN, InternationallyKnownAsTag.class).isPresent());\n        Assert.assertEquals(\"nyet\",\n                results.valueFor(RUSSIAN, InternationallyKnownAsTag.class).get());\n    }\n\n    @Test\n    public void forceLocalizedWithoutExplicitLanguageList()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet().findInWithMyLanguages(\n                this.localizedDataForNonLocalizableKey, TagSearchOption.FORCE_ALL_LOCALIZED_ONLY);\n        Assert.assertTrue(results.valueFor(RUSSIAN, InternationallyKnownAsTag.class).isPresent());\n        Assert.assertEquals(\"nyet\",\n                results.valueFor(RUSSIAN, InternationallyKnownAsTag.class).get());\n    }\n\n    @Test\n    public void nonLocalized()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet()\n                .withLanguage(RUSSIAN.get()).findIn(this.localizedDataForNonLocalizableKey);\n        Assert.assertFalse(results.valueFor(RUSSIAN, InternationallyKnownAsTag.class).isPresent());\n    }\n\n    @Test\n    public void nonLocalizedWithoutExplicitLanguageList()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet()\n                .findInWithMyLanguages(this.localizedDataForNonLocalizableKey);\n        Assert.assertFalse(results.valueFor(RUSSIAN, InternationallyKnownAsTag.class).isPresent());\n    }\n\n    @Before\n    public void setUp()\n    {\n        final Map<String, String> data = Maps\n                .hashMap(Validators.localizeKeyName(InternationallyKnownAsTag.class, RUSSIAN,\n                        TagSearchOption.FORCE_ALL_LOCALIZED_ONLY).get(), \"nyet\");\n        this.localizedDataForNonLocalizableKey = new TestTaggable(data);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/BulkNameFinderTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.tags.names.AlternativeNameTag;\nimport org.openstreetmap.atlas.tags.names.BulkNameFinder;\nimport org.openstreetmap.atlas.tags.names.BulkNameFinder.BulkFindResults;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test case for the BulkNameFinder\n *\n * @author cstaylor\n */\npublic class BulkNameFinderTestCase\n{\n    private Taggable taggable;\n\n    @Test\n    public void allLanguagesTest()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet().allLanguages()\n                .findIn(this.taggable);\n        Assert.assertTrue(results.valueFor(Optional.empty(), AlternativeNameTag.class).isPresent());\n        Assert.assertEquals(\"Real Test\",\n                results.valueFor(Optional.empty(), AlternativeNameTag.class).get());\n        Assert.assertTrue(results\n                .valueFor(Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()), NameTag.class)\n                .isPresent());\n        Assert.assertEquals(\"nyet\", results\n                .valueFor(Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()), NameTag.class)\n                .get());\n    }\n\n    @Test\n    public void allValuesForCountryWithDataTest()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet().allLanguages()\n                .findIn(this.taggable);\n        final Optional<Map<Class<?>, String>> russianValues = results\n                .allValuesFor(Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()));\n        Assert.assertTrue(russianValues.isPresent());\n\n        final Optional<Map<Class<?>, String>> japaneseValues = results\n                .allValuesFor(Optional.of(IsoLanguage.forLanguageCode(\"ja\").get()));\n        Assert.assertTrue(japaneseValues.isPresent());\n        Assert.assertEquals(0, japaneseValues.get().size());\n\n        russianValues.ifPresent(map ->\n        {\n            Assert.assertNotNull(map.get(NameTag.class));\n            Assert.assertEquals(\"nyet\", map.get(NameTag.class));\n        });\n    }\n\n    @Test\n    public void allValuesForDefaultWithDataTest()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet().allLanguages()\n                .findIn(this.taggable);\n        final Optional<Map<Class<?>, String>> defaultValues = results\n                .allValuesFor(Optional.empty());\n        Assert.assertTrue(defaultValues.isPresent());\n\n        defaultValues.ifPresent(map ->\n        {\n            Assert.assertNotNull(map.get(AlternativeNameTag.class));\n            Assert.assertEquals(\"Real Test\", map.get(AlternativeNameTag.class));\n        });\n    }\n\n    @Test\n    public void allValuesNotPresentForCountryNotRequested()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet()\n                .withLanguage(IsoLanguage.forLanguageCode(\"ru\").get()).findIn(this.taggable);\n        final Optional<Map<Class<?>, String>> japaneseValues = results\n                .allValuesFor(Optional.of(IsoLanguage.forLanguageCode(\"ja\").get()));\n        Assert.assertFalse(japaneseValues.isPresent());\n    }\n\n    @Test\n    public void allValuesPresentForCountryRequestedWithNoData()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet().allLanguages()\n                .findIn(this.taggable);\n        final Optional<Map<Class<?>, String>> japaneseValues = results\n                .allValuesFor(Optional.of(IsoLanguage.forLanguageCode(\"ja\").get()));\n        Assert.assertTrue(japaneseValues.isPresent());\n        Assert.assertEquals(0, japaneseValues.get().size());\n    }\n\n    @Test\n    public void flattenTest()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet()\n                .withLanguage(IsoLanguage.forLanguageCode(\"ru\").get()).findIn(this.taggable);\n        final Map<String, String> flattenedResults = results.flatten();\n        Assert.assertEquals(3, flattenedResults.size());\n    }\n\n    @Before\n    public void setUp()\n    {\n        final Map<String, String> testData = Maps\n                .hashMap(NameTag.KEY, \"Test\", AlternativeNameTag.KEY, \"Real Test\",\n                        Validators.localizeKeyName(NameTag.class,\n                                Optional.of(IsoLanguage.forLanguageCode(\"ru\").get())).get(),\n                        \"nyet\");\n        this.taggable = new TestTaggable(testData);\n    }\n\n    @Test\n    public void standardTestInRussian()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet()\n                .withLanguage(IsoLanguage.forLanguageCode(\"ru\").get()).findIn(this.taggable);\n        Assert.assertTrue(results.valueFor(Optional.empty(), AlternativeNameTag.class).isPresent());\n        Assert.assertEquals(\"Real Test\",\n                results.valueFor(Optional.empty(), AlternativeNameTag.class).get());\n        Assert.assertTrue(results\n                .valueFor(Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()), NameTag.class)\n                .isPresent());\n        Assert.assertEquals(\"nyet\", results\n                .valueFor(Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()), NameTag.class)\n                .get());\n    }\n\n    @Test\n    public void standardTestNoLanguages()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet().findIn(this.taggable);\n        Assert.assertTrue(results.valueFor(Optional.empty(), AlternativeNameTag.class).isPresent());\n        Assert.assertEquals(\"Real Test\",\n                results.valueFor(Optional.empty(), AlternativeNameTag.class).get());\n    }\n\n    @Test\n    public void testLanguagesInValues()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet()\n                .findInWithMyLanguages(this.taggable);\n        Assert.assertTrue(results.valueFor(Optional.empty(), AlternativeNameTag.class).isPresent());\n        Assert.assertEquals(\"Real Test\",\n                results.valueFor(Optional.empty(), AlternativeNameTag.class).get());\n        Assert.assertTrue(results\n                .valueFor(Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()), NameTag.class)\n                .isPresent());\n        Assert.assertEquals(\"nyet\", results\n                .valueFor(Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()), NameTag.class)\n                .get());\n    }\n\n    @Test\n    public void testLanguagesInValuesNoLanguages()\n    {\n        final BulkFindResults results = BulkNameFinder.createStandardSet()\n                .findInWithMyLanguages(this.taggable);\n        Assert.assertTrue(results.valueFor(Optional.empty(), AlternativeNameTag.class).isPresent());\n        Assert.assertEquals(\"Real Test\",\n                results.valueFor(Optional.empty(), AlternativeNameTag.class).get());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/CheckDateTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for @{CheckDateTag}\n *\n * @author brianjor\n */\npublic class CheckDateTagTestCase\n{\n    @Test\n    public void testCheckDateTag()\n    {\n        final TestTaggable taggable = new TestTaggable(CheckDateTag.KEY, \"2020\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, CheckDateTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/ConstructionDateTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for @{ConstructionDateTag}\n *\n * @author brianjor\n */\npublic class ConstructionDateTagTestCase\n{\n    @Test\n    public void testConstructionDateTag()\n    {\n        final TestTaggable taggable = new TestTaggable(ConstructionDateTag.KEY, \"2020\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, ConstructionDateTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/DestinationTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Tests\n * for @{DestinationTag}, @{DestinationRefTag}, @{DestinationStreetTag}, @{DestinationRefToTag}\n * and @{DestinationIntRefTag}\n * \n * @author sbhalekar\n */\npublic class DestinationTagTestCase\n{\n    @Test\n    public void testDestinatioTag()\n    {\n        final TestTaggable taggable = new TestTaggable(DestinationTag.KEY, \"San Jose\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, DestinationTag.class));\n    }\n\n    @Test\n    public void testDestinationIntRefTag()\n    {\n        final TestTaggable taggable = new TestTaggable(DestinationIntRefTag.KEY, \"E_94\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, DestinationIntRefTag.class));\n    }\n\n    @Test\n    public void testDestinationRefTag()\n    {\n        final TestTaggable taggable = new TestTaggable(DestinationRefTag.KEY, \"KPE\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, DestinationRefTag.class));\n    }\n\n    @Test\n    public void testDestinationStreetTag()\n    {\n        final TestTaggable taggable = new TestTaggable(DestinationStreetTag.KEY, \"Bendemeer Road\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, DestinationStreetTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/DisusedRailwayTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author vlemberg\n */\n\npublic class DisusedRailwayTagTestCase\n{\n    private final Taggable taggable1 = Taggable.with(\"disused:railway\", \"level_crossing\");\n    private final Taggable taggable2 = Taggable.with(\"disused:railway\", \"crossing\");\n    private final Taggable taggable3 = Taggable.with(\"railway\", \"level_crossing\");\n\n    @Test\n    public void isDisuseRailwayCrossing()\n    {\n        Assert.assertTrue(DisusedRailwayTag.isDisusedRailwayCrossing(this.taggable1));\n        Assert.assertTrue(DisusedRailwayTag.isDisusedRailwayCrossing(this.taggable2));\n        Assert.assertFalse(DisusedRailwayTag.isDisusedRailwayCrossing(this.taggable3));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/EstimatedWidthTagTest.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Unit tests for {@link EstimatedWidthTag}.\n *\n * @author bbreithaupt\n */\npublic class EstimatedWidthTagTest\n{\n    @Test\n    public void testInvalidValue()\n    {\n        Assert.assertFalse(EstimatedWidthTag.get(Taggable.with(\"est_width\", \"1;2\")).isPresent());\n    }\n\n    @Test\n    public void testMissingValue()\n    {\n        Assert.assertFalse(EstimatedWidthTag.get(Taggable.with(\"width\", \"1.2\")).isPresent());\n    }\n\n    @Test\n    public void testValidValue()\n    {\n        Assert.assertTrue(EstimatedWidthTag.get(Taggable.with(\"est_width\", \"1.2\")).isPresent());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/FerryTagTest.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class FerryTagTest\n{\n    private final Taggable taggable1 = Taggable.with(\"ferry\", \"service\");\n    private final Taggable taggable2 = Taggable.with(\"ferry\", \"footway\");\n\n    @Test\n    public void isCarNavigableFerryTest()\n    {\n        Assert.assertTrue(FerryTag.isCarNavigableFerry(this.taggable1));\n        Assert.assertFalse(FerryTag.isCarNavigableFerry(this.taggable2));\n    }\n\n    @Test\n    public void isPedestrianNavigableFerryTest()\n    {\n        Assert.assertFalse(FerryTag.isPedestrianNavigableFerry(this.taggable1));\n        Assert.assertTrue(FerryTag.isPedestrianNavigableFerry(this.taggable2));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/GetTagsTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test case for listing explicitly defined languages in a localized tag\n *\n * @author cstaylor\n */\npublic class GetTagsTestCase\n{\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Test\n    public void hasAdditionalTagsToo()\n    {\n        final TestTaggable taggable = new TestTaggable(\n                Maps.hashMap(\"name\", \"Some Name\", \"car\", \"123\"));\n        final Optional<Set<IsoLanguage>> possibleLanguages = taggable.languagesFor(NameTag.class);\n        Assert.assertTrue(possibleLanguages.isPresent());\n        Assert.assertTrue(possibleLanguages.get().isEmpty());\n    }\n\n    @Test\n    public void hasNoLanguagesForName()\n    {\n        final TestTaggable taggable = new TestTaggable(Maps.hashMap(\"name\", \"Some Name\"));\n        final Optional<Set<IsoLanguage>> possibleLanguages = taggable.languagesFor(NameTag.class);\n        Assert.assertTrue(possibleLanguages.isPresent());\n        Assert.assertTrue(possibleLanguages.get().isEmpty());\n    }\n\n    @Test\n    public void hasNoRussianForName()\n    {\n        final TestTaggable taggable = new TestTaggable(\n                Maps.hashMap(\"name\", \"Some Name\", \"name:en\", \"Nyet!\"));\n        final Optional<Set<IsoLanguage>> possibleLanguages = taggable.languagesFor(NameTag.class);\n        Assert.assertTrue(possibleLanguages.isPresent());\n        Assert.assertEquals(1, possibleLanguages.get().size());\n        Assert.assertFalse(\n                possibleLanguages.get().contains(IsoLanguage.forLanguageCode(\"ru\").get()));\n    }\n\n    @Test\n    public void hasRussianForName()\n    {\n        final TestTaggable taggable = new TestTaggable(\n                Maps.hashMap(\"name\", \"Some Name\", \"name:ru\", \"Nyet!\"));\n        final Optional<Set<IsoLanguage>> possibleLanguages = taggable.languagesFor(NameTag.class);\n        Assert.assertTrue(possibleLanguages.isPresent());\n        Assert.assertEquals(1, possibleLanguages.get().size());\n        Assert.assertTrue(\n                possibleLanguages.get().contains(IsoLanguage.forLanguageCode(\"ru\").get()));\n    }\n\n    @Test\n    public void hasWeirdLangauge()\n    {\n        final TestTaggable taggable = new TestTaggable(\n                Maps.hashMap(\"name\", \"Some Name\", \"name:klingon\", \"Nyet!\"));\n        final Optional<Set<IsoLanguage>> possibleLanguages = taggable.languagesFor(NameTag.class);\n        Assert.assertTrue(possibleLanguages.isPresent());\n        Assert.assertTrue(possibleLanguages.get().isEmpty());\n    }\n\n    @Test\n    public void nonLocalizableTag()\n    {\n        this.thrown.expect(CoreException.class);\n        final TestTaggable taggable = new TestTaggable(\n                Maps.hashMap(\"name\", \"Some Name\", \"name:ru\", \"Nyet!\"));\n        @SuppressWarnings(\"unused\")\n        final Optional<Set<IsoLanguage>> possibleLanguages = taggable\n                .languagesFor(MilitaryTag.class);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/HeightTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.tags.annotations.validation.BaseTagTestCase;\n\n/**\n * Test case for verifying both the height tag and the length validator\n *\n * @author cstaylor\n */\npublic class HeightTagTestCase extends BaseTagTestCase\n{\n    @Rule\n    public final ExpectedException expected = ExpectedException.none();\n\n    @Test\n    public void testEmptyHeight()\n    {\n        Assert.assertFalse(validators().isValidFor(HeightTag.KEY, \"\"));\n    }\n\n    @Test\n    public void testEnglish()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \"12'5\\\"\"));\n    }\n\n    @Test\n    public void testEnglishBlank()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \"12'5\\\"  \"));\n    }\n\n    @Test\n    public void testEnglishPrecedingBlank()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \" 12'5\\\"\"));\n    }\n\n    @Test\n    public void testEnglishWrong()\n    {\n        Assert.assertFalse(validators().isValidFor(HeightTag.KEY, \"12'5\\\"a\"));\n    }\n\n    @Test\n    public void testFeetOnly()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \"12'\"));\n    }\n\n    @Test\n    public void testFeetOnlyBlank()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \"12' \"));\n    }\n\n    @Test\n    public void testFeetWrong()\n    {\n        Assert.assertFalse(validators().isValidFor(HeightTag.KEY, \"12'a\"));\n    }\n\n    @Test\n    public void testInchesOnly()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \"5\\\"\"));\n    }\n\n    @Test\n    public void testMeters()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \"12.5 m\"));\n    }\n\n    @Test\n    public void testNonsenseValue()\n    {\n        Assert.assertFalse(validators().isValidFor(HeightTag.KEY, \"PPAP\"));\n    }\n\n    @Test\n    public void testNonsenseValue2()\n    {\n        Assert.assertFalse(\n                validators().isValidFor(HeightTag.KEY, \"Estacion de Servicio \\\"Los Arrayanes\\\"\"));\n    }\n\n    @Test\n    public void testNullHeight()\n    {\n        this.expected.expect(NullPointerException.class);\n        Assert.assertFalse(validators().isValidFor(HeightTag.KEY, null));\n    }\n\n    @Test\n    public void testNumericOnly()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \"12.5\"));\n    }\n\n    @Test\n    public void testNumericOnlyWithBlanks()\n    {\n        Assert.assertTrue(validators().isValidFor(HeightTag.KEY, \"  12.5  \"));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/HighwayTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for {@link org.openstreetmap.atlas.tags.HighwayTag}\n *\n * @author v-garei\n */\npublic class HighwayTagTestCase\n{\n\n    @Test\n    public void highwayTagTests()\n    {\n        final Taggable emergencyBayTaggable = Taggable.with(HighwayTag.KEY,\n                HighwayTag.EMERGENCY_BAY.toString());\n        Assert.assertTrue(Validators.hasValuesFor(emergencyBayTaggable, HighwayTag.class));\n\n        final TestTaggable falseNodeOnlyTag = new TestTaggable(HighwayTag.KEY,\n                HighwayTag.TRUNK.toString());\n        Assert.assertFalse(HighwayTag.isNodeOnlyTag(falseNodeOnlyTag));\n\n        final TestTaggable falseWayOnlyTag = new TestTaggable(HighwayTag.KEY,\n                HighwayTag.BUS_STOP.toString());\n        Assert.assertFalse(HighwayTag.isWayOnlyTag(falseWayOnlyTag));\n\n        final TestTaggable trueNodeOnlyTag = new TestTaggable(HighwayTag.KEY,\n                HighwayTag.TRAFFIC_MIRROR.toString());\n        Assert.assertTrue(HighwayTag.isNodeOnlyTag(trueNodeOnlyTag));\n\n        final TestTaggable trueWayOnlyTag = new TestTaggable(HighwayTag.KEY,\n                HighwayTag.PRIMARY.toString());\n        Assert.assertTrue(HighwayTag.isWayOnlyTag(trueWayOnlyTag));\n\n        final Taggable trafficMirrorTaggable = Taggable.with(HighwayTag.KEY,\n                HighwayTag.TRAFFIC_MIRROR.toString());\n        Assert.assertTrue(Validators.hasValuesFor(trafficMirrorTaggable, HighwayTag.class));\n\n        final Taggable trailheadTaggable = Taggable.with(HighwayTag.KEY,\n                HighwayTag.TRAILHEAD.toString());\n        Assert.assertTrue(Validators.hasValuesFor(trailheadTaggable, HighwayTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/ISOCountryTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\nimport com.google.common.collect.Lists;\n\n/**\n * Test case for verifying if ISOCountryTag's from method works\n *\n * @author cstaylor\n * @author ahsieh\n */\npublic class ISOCountryTagTestCase\n{\n    @Test\n    public void testAll()\n    {\n        final TestTaggable testable = new TestTaggable(ISOCountryTag.KEY, \"PRK,KOR,USA\");\n\n        final List<String> countries = Lists.newArrayList(ISOCountryTag.all(testable));\n        Assert.assertTrue(countries.size() == 3);\n        Assert.assertTrue(countries.contains(\"PRK\"));\n        Assert.assertTrue(countries.contains(\"KOR\"));\n        Assert.assertTrue(countries.contains(\"USA\"));\n    }\n\n    @Test\n    public void testFilterAllIn()\n    {\n        final Collection<String> countries = new HashSet<>();\n        countries.add(\"KOR\");\n        countries.add(\"USA\");\n        final Predicate<Taggable> checkMe = ISOCountryTag.allIn(countries);\n\n        Assert.assertFalse(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"PRK,KOR,USA,RUS\")));\n        Assert.assertFalse(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"PRK,KOR,USA\")));\n        Assert.assertTrue(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"KOR,USA\")));\n        Assert.assertTrue(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"USA,KOR\")));\n        Assert.assertTrue(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"KOR\")));\n        Assert.assertFalse(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"PRK\")));\n        Assert.assertFalse(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"PRK,RUS\")));\n    }\n\n    @Test\n    public void testFilterIsIn()\n    {\n        final Predicate<Taggable> checkMe = ISOCountryTag.isIn(\"KOR\");\n        Assert.assertTrue(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"KOR\")));\n        Assert.assertFalse(checkMe.test(new TestTaggable(ISOCountryTag.KEY, \"PRK\")));\n    }\n\n    @Test\n    public void testFromOrdering()\n    {\n        // We expect PRK to be the country code because it's the first in the list\n        final TestTaggable testable = new TestTaggable(ISOCountryTag.KEY, \"PRK,KOR,USA\");\n\n        final Iterable<String> possibleCountry = ISOCountryTag.all(testable);\n        Assert.assertTrue(Iterables.size(possibleCountry) > 0);\n        Assert.assertEquals(\"PRK\", possibleCountry.iterator().next());\n    }\n\n    @Test\n    public void testKorea()\n    {\n        final TestTaggable testable = new TestTaggable(ISOCountryTag.KEY, \"KOR\");\n\n        final List<String> countries = Lists.newArrayList(ISOCountryTag.all(testable));\n        Assert.assertTrue(countries.size() == 1);\n        Assert.assertTrue(countries.contains(\"KOR\"));\n\n        final Iterable<String> possibleCountry = ISOCountryTag.all(testable);\n        Assert.assertTrue(Iterables.size(possibleCountry) > 0);\n        final String country = possibleCountry.iterator().next();\n        Assert.assertEquals(\"KOR\", country);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/LayerTagTest.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Unit tests for {@link LayerTag}.\n *\n * @author bbreithaupt\n */\npublic class LayerTagTest\n{\n    @Test\n    public void validBridgeTest()\n    {\n        Assert.assertEquals((Long) 1L,\n                LayerTag.getTaggedOrImpliedValue(Taggable.with(\"bridge\", \"yes\"), LayerTag.ZERO));\n    }\n\n    @Test\n    public void validDefaultTest()\n    {\n        Assert.assertEquals((Long) 0L,\n                LayerTag.getTaggedOrImpliedValue(Taggable.with(), LayerTag.ZERO));\n    }\n\n    @Test\n    public void validLayerBridgeTunnelTest()\n    {\n        Assert.assertEquals((Long) 3L, LayerTag.getTaggedOrImpliedValue(\n                Taggable.with(\"layer\", \"3\", \"bridge\", \"yes\", \"tunnel\", \"yes\"), LayerTag.ZERO));\n    }\n\n    @Test\n    public void validLayerTest()\n    {\n        Assert.assertEquals((Long) 3L,\n                LayerTag.getTaggedOrImpliedValue(Taggable.with(\"layer\", \"3\"), LayerTag.ZERO));\n    }\n\n    @Test\n    public void validTunnelTest()\n    {\n        Assert.assertEquals((Long) (-1L),\n                LayerTag.getTaggedOrImpliedValue(Taggable.with(\"tunnel\", \"yes\"), LayerTag.ZERO));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/LocalizedTagNameWithOptionalDateTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\n\n/**\n * Test cases for the LocalizedTagNameWithOptionalDate class\n *\n * @author cstaylor\n */\npublic class LocalizedTagNameWithOptionalDateTestCase\n{\n    @Test\n    public void singleItemName()\n    {\n        final LocalizedTagNameWithOptionalDate name = new LocalizedTagNameWithOptionalDate(\"name\");\n        Assert.assertEquals(\"name\", name.getName());\n        Assert.assertFalse(name.getLanguage().isPresent());\n        Assert.assertFalse(name.getDateRange().isPresent());\n    }\n\n    @Test\n    public void threePartItemName()\n    {\n        final LocalizedTagNameWithOptionalDate name = new LocalizedTagNameWithOptionalDate(\n                \"name:maybe:not\");\n        Assert.assertEquals(\"name:maybe:not\", name.getName());\n        Assert.assertFalse(name.getLanguage().isPresent());\n        Assert.assertFalse(name.getDateRange().isPresent());\n    }\n\n    @Test\n    public void threePartItemNameInRussian()\n    {\n        final LocalizedTagNameWithOptionalDate name = new LocalizedTagNameWithOptionalDate(\n                \"name:maybe:not:ru\");\n        Assert.assertEquals(\"name:maybe:not\", name.getName());\n        Assert.assertTrue(name.getLanguage().isPresent());\n        Assert.assertEquals(IsoLanguage.forLanguageCode(\"ru\").get().getLanguageCode(),\n                name.getLanguage().get().getLanguageCode());\n        Assert.assertFalse(name.getDateRange().isPresent());\n    }\n\n    @Test\n    public void threePartItemNameInRussianWithDateRange()\n    {\n        final LocalizedTagNameWithOptionalDate name = new LocalizedTagNameWithOptionalDate(\n                \"name:maybe:not:ru:1939-1945\");\n        Assert.assertEquals(\"name:maybe:not\", name.getName());\n        Assert.assertTrue(name.getLanguage().isPresent());\n        Assert.assertEquals(IsoLanguage.forLanguageCode(\"ru\").get().getLanguageCode(),\n                name.getLanguage().get().getLanguageCode());\n        Assert.assertTrue(name.getDateRange().isPresent());\n    }\n\n    @Test\n    public void threePartItemNameWithDateRange()\n    {\n        final LocalizedTagNameWithOptionalDate name = new LocalizedTagNameWithOptionalDate(\n                \"name:maybe:not:1939-1945\");\n        Assert.assertEquals(\"name:maybe:not\", name.getName());\n        Assert.assertFalse(name.getLanguage().isPresent());\n        Assert.assertTrue(name.getDateRange().isPresent());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/LocalizedTaggableTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test cases for fetching localized tag values\n *\n * @author cstaylor\n */\npublic class LocalizedTaggableTestCase\n{\n    @Rule\n    public final ExpectedException exception = ExpectedException.none();\n\n    @Test\n    public void failOnNonTagClass()\n    {\n        this.exception.expect(IllegalArgumentException.class);\n        new TestTaggable(NameTag.KEY, \"privet\").getTag(String.class,\n                Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()));\n        Assert.fail(\"Shouldn't have gotten here\");\n    }\n\n    @Test\n    public void failOnNullTagClass()\n    {\n        this.exception.expect(IllegalArgumentException.class);\n        new TestTaggable(NameTag.KEY, \"dah\").getTag(null,\n                Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()));\n        Assert.fail(\"Shouldn't have gotten here\");\n    }\n\n    @Test\n    public void findDefaultNameFromRussianAndDefaults()\n    {\n        final Optional<String> value = new TestTaggable(\n                Maps.hashMap(NameTag.KEY + \":ru\", \"dah\", NameTag.KEY, \"nyet\")).getTag(NameTag.KEY);\n        Assert.assertTrue(value.isPresent());\n        Assert.assertEquals(\"nyet\", value.get());\n    }\n\n    @Test\n    public void findDefaultNameWhenRequestingRussianName()\n    {\n        final Optional<String> value = new TestTaggable(NameTag.KEY, \"karta\").getTag(NameTag.class,\n                Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()));\n        Assert.assertTrue(value.isPresent());\n        Assert.assertEquals(\"karta\", value.get());\n    }\n\n    @Test\n    public void findNoNameWhenRequestingDefaultName()\n    {\n        final Optional<String> value = new TestTaggable(NameTag.KEY + \":ru\", \"babushka\")\n                .getTag(NameTag.KEY);\n        Assert.assertFalse(value.isPresent());\n    }\n\n    @Test\n    public void findRussianName()\n    {\n        final Optional<String> value = new TestTaggable(NameTag.KEY + \":ru\", \"kak dela?\")\n                .getTag(NameTag.class, Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()));\n        Assert.assertTrue(value.isPresent());\n        Assert.assertEquals(\"kak dela?\", value.get());\n    }\n\n    @Test\n    public void findRussianNameFromRussianAndDefaults()\n    {\n        final Optional<String> value = new TestTaggable(\n                Maps.hashMap(NameTag.KEY + \":ru\", \"dah\", NameTag.KEY, \"nyet\"))\n                .getTag(NameTag.class, Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()));\n        Assert.assertTrue(value.isPresent());\n        Assert.assertEquals(\"dah\", value.get());\n    }\n\n    @Test\n    public void ignoreLocalizationOnNonLocalizableTagClass()\n    {\n        final Optional<String> value = new TestTaggable(AmenityTag.BANK).getTag(AmenityTag.class,\n                Optional.of(IsoLanguage.forLanguageCode(\"ru\").get()));\n        Assert.assertTrue(value.isPresent());\n        Assert.assertEquals(\"bank\", value.get());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/NameFinderTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.tags.names.AlternativeNameTag;\nimport org.openstreetmap.atlas.tags.names.InternationallyKnownAsTag;\nimport org.openstreetmap.atlas.tags.names.NameFinder;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.testing.FreezeDryFunction;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test class for the NameFinder\n *\n * @author cstaylor\n */\npublic class NameFinderTestCase\n{\n    private static final FreezeDryFunction<NameFinder> FREEZE_DRY = new FreezeDryFunction<>();\n\n    private Taggable taggable;\n\n    @Before\n    public void setUp()\n    {\n        final Map<String, String> testData = Maps\n                .hashMap(NameTag.KEY, \"Test\", AlternativeNameTag.KEY, \"Real Test\",\n                        Validators.localizeKeyName(NameTag.class,\n                                Optional.of(IsoLanguage.forLanguageCode(\"ru\").get())).get(),\n                        \"nyet\");\n        this.taggable = new TestTaggable(testData);\n    }\n\n    @Test\n    public void testAllEnglish() throws Exception\n    {\n        final Map<Class<?>, String> all = FREEZE_DRY\n                .apply(new NameFinder().withTags(NameTag.class, AlternativeNameTag.class)\n                        .inLanguage(IsoLanguage.forLanguageCode(\"en\").get()))\n                .all(this.taggable);\n        Assert.assertEquals(2, all.size());\n        Assert.assertEquals(\"Test\", all.get(NameTag.class));\n        Assert.assertEquals(\"Real Test\", all.get(AlternativeNameTag.class));\n        Assert.assertFalse(all.containsKey(InternationallyKnownAsTag.class));\n    }\n\n    @Test\n    public void testAllRussian() throws Exception\n    {\n        final Map<Class<?>, String> all = FREEZE_DRY\n                .apply(new NameFinder().withTags(NameTag.class, AlternativeNameTag.class)\n                        .inLanguage(IsoLanguage.forLanguageCode(\"ru\").get()))\n                .all(this.taggable);\n        Assert.assertEquals(2, all.size());\n        Assert.assertEquals(\"nyet\", all.get(NameTag.class));\n        Assert.assertEquals(\"Real Test\", all.get(AlternativeNameTag.class));\n        Assert.assertFalse(all.containsKey(InternationallyKnownAsTag.class));\n    }\n\n    @Test\n    public void testBestEnglishName() throws Exception\n    {\n        final Optional<String> value = FREEZE_DRY.apply(new NameFinder().withTags(NameTag.class)\n                .inLanguage(IsoLanguage.forLanguageCode(\"en\").get())).best(this.taggable);\n        Assert.assertTrue(value.isPresent());\n        Assert.assertEquals(\"Test\", value.get());\n    }\n\n    @Test\n    public void testBestName() throws Exception\n    {\n        final Optional<String> value = FREEZE_DRY.apply(new NameFinder().withTags(NameTag.class))\n                .best(this.taggable);\n        Assert.assertTrue(value.isPresent());\n        Assert.assertEquals(\"Test\", value.get());\n    }\n\n    @Test\n    public void testBestRussianName() throws Exception\n    {\n        final Optional<String> value = FREEZE_DRY.apply(new NameFinder().withTags(NameTag.class)\n                .inLanguage(IsoLanguage.forLanguageCode(\"ru\").get())).best(this.taggable);\n        Assert.assertTrue(value.isPresent());\n        Assert.assertEquals(\"nyet\", value.get());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/OpenDateTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for @{OpenDateTag}\n *\n * @author brianjor\n */\npublic class OpenDateTagTestCase\n{\n    @Test\n    public void testOpenDateTag()\n    {\n        final TestTaggable taggable = new TestTaggable(OpenDateTag.KEY, \"2020\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, OpenDateTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/OpeningDateTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for #{OpeningDateTag}\n *\n * @author brianjor\n */\npublic class OpeningDateTagTestCase\n{\n    @Test\n    public void testOpeningDateTag()\n    {\n        final TestTaggable taggable = new TestTaggable(OpeningDateTag.KEY, \"2020\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, OpeningDateTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/ProtectClassTagTest.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Unit tests for {@link ProtectClassTag}.\n *\n * @author bbreithaupt\n */\npublic class ProtectClassTagTest\n{\n\n    @Test\n    public void getValueNotNumberTest()\n    {\n        final Optional<Integer> tagValue = ProtectClassTag\n                .getValue(Taggable.with(\"protect_class\", \"bad\"));\n        Assert.assertFalse(tagValue.isPresent());\n    }\n\n    @Test\n    public void getValueTest()\n    {\n        final Optional<Integer> tagValue = ProtectClassTag\n                .getValue(Taggable.with(\"protect_class\", \"1\"));\n        Assert.assertTrue(tagValue.isPresent());\n        Assert.assertEquals((Integer) 1, tagValue.get());\n    }\n\n    @Test\n    public void getValueWrongTagTest()\n    {\n        final Optional<Integer> tagValue = ProtectClassTag\n                .getValue(Taggable.with(\"highway\", \"primary\"));\n        Assert.assertFalse(tagValue.isPresent());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/RailwayTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author vlemberg\n */\n\npublic class RailwayTagTestCase\n{\n    private final Taggable taggable1 = Taggable.with(\"railway\", \"level_crossing\");\n    private final Taggable taggable2 = Taggable.with(\"railway\", \"tram_level_crossing\");\n    private final Taggable taggable3 = Taggable.with(\"disused:railway\", \"level_crossing\");\n\n    @Test\n    public void isRailwayCrossing()\n    {\n        Assert.assertTrue(RailwayTag.isRailwayCrossing(this.taggable1));\n        Assert.assertTrue(RailwayTag.isRailwayCrossing(this.taggable2));\n        Assert.assertFalse(RailwayTag.isRailwayCrossing(this.taggable3));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/SmoothnessTagTest.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Unit tests for {@link SmoothnessTag}.\n *\n * @author bbreithaupt\n */\npublic class SmoothnessTagTest\n{\n\n    @Test\n    public void isLessImportantThanOrEqualToTest()\n    {\n        Assert.assertTrue(SmoothnessTag.BAD.isLessImportantThanOrEqualTo(SmoothnessTag.BAD));\n        Assert.assertTrue(SmoothnessTag.BAD.isLessImportantThanOrEqualTo(SmoothnessTag.GOOD));\n        Assert.assertFalse(SmoothnessTag.GOOD.isLessImportantThanOrEqualTo(SmoothnessTag.BAD));\n    }\n\n    @Test\n    public void isLessImportantThanTest()\n    {\n        Assert.assertTrue(SmoothnessTag.BAD.isLessImportantThan(SmoothnessTag.GOOD));\n        Assert.assertFalse(SmoothnessTag.GOOD.isLessImportantThan(SmoothnessTag.BAD));\n    }\n\n    @Test\n    public void isMoreImportantThanOrEqualToTest()\n    {\n        Assert.assertTrue(SmoothnessTag.BAD.isMoreImportantThanOrEqualTo(SmoothnessTag.BAD));\n        Assert.assertFalse(SmoothnessTag.BAD.isMoreImportantThanOrEqualTo(SmoothnessTag.GOOD));\n        Assert.assertTrue(SmoothnessTag.GOOD.isMoreImportantThanOrEqualTo(SmoothnessTag.BAD));\n    }\n\n    @Test\n    public void isMoreImportantThanTest()\n    {\n        Assert.assertFalse(SmoothnessTag.BAD.isMoreImportantThan(SmoothnessTag.GOOD));\n        Assert.assertTrue(SmoothnessTag.GOOD.isMoreImportantThan(SmoothnessTag.BAD));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/StandardNameFinderTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.locale.IsoLanguage;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.tags.names.AlternativeNameTag;\nimport org.openstreetmap.atlas.tags.names.InternationallyKnownAsTag;\nimport org.openstreetmap.atlas.tags.names.NameFinder;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.testing.FreezeDryFunction;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test cases for the standard set of names\n *\n * @author cstaylor\n */\npublic class StandardNameFinderTestCase\n{\n    private static final FreezeDryFunction<NameFinder> FREEZE_DRY = new FreezeDryFunction<>();\n\n    private Taggable taggable;\n\n    @Test\n    public void serializedStandardAll() throws Exception\n    {\n        final Map<Class<?>, String> all = FREEZE_DRY\n                .apply(NameFinder.createStandardSet(IsoLanguage.forLanguageCode(\"en\").get()))\n                .all(this.taggable);\n        Assert.assertEquals(2, all.size());\n        Assert.assertEquals(\"Test\", all.get(NameTag.class));\n        Assert.assertEquals(\"Real Test\", all.get(AlternativeNameTag.class));\n        Assert.assertFalse(all.containsKey(InternationallyKnownAsTag.class));\n    }\n\n    @Before\n    public void setUp()\n    {\n        final Map<String, String> testData = Maps\n                .hashMap(NameTag.KEY, \"Test\", AlternativeNameTag.KEY, \"Real Test\",\n                        Validators.localizeKeyName(NameTag.class,\n                                Optional.of(IsoLanguage.forLanguageCode(\"ru\").get())).get(),\n                        \"nyet\");\n        this.taggable = new TestTaggable(testData);\n    }\n\n    @Test\n    public void standardAll()\n    {\n        final Map<Class<?>, String> all = NameFinder\n                .createStandardSet(IsoLanguage.forLanguageCode(\"en\").get()).all(this.taggable);\n        Assert.assertEquals(2, all.size());\n        Assert.assertEquals(\"Test\", all.get(NameTag.class));\n        Assert.assertEquals(\"Real Test\", all.get(AlternativeNameTag.class));\n        Assert.assertFalse(all.containsKey(InternationallyKnownAsTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/SyntheticTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators.TagKeySearchResults;\nimport org.openstreetmap.atlas.tags.names.NameTag;\n\n/**\n * Test case for checking if the synthetic attribute works\n *\n * @author cstaylor\n */\npublic class SyntheticTagTestCase\n{\n    @Test\n    public void isNotSynthetic()\n    {\n        final TagKeySearchResults results = Validators.TagKeySearch.findTagKeyIn(NameTag.class)\n                .orElseThrow(() -> new IllegalArgumentException(\"Not a tag\"));\n        Assert.assertFalse(results.getTag().synthetic());\n    }\n\n    @Test\n    public void isSynthetic()\n    {\n        final TagKeySearchResults results = Validators.TagKeySearch\n                .findTagKeyIn(TestSyntheticTag.class)\n                .orElseThrow(() -> new IllegalArgumentException(\"Not a tag\"));\n        Assert.assertTrue(results.getTag().synthetic());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/TagTestSuite.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.openstreetmap.atlas.tags.annotations.validation.TagValidationTestSuite;\n\n/**\n * This is a JUnit TestSuite for running multiple tests in one run, so when I use eclemma I can see\n * the coverage results. At the moment gradle doesn't run these, so these are primarily for code\n * coverage checks within Eclipse\n *\n * @author cstaylor\n */\n@RunWith(Suite.class)\n@Suite.SuiteClasses({ LocalizedTagNameWithOptionalDateTestCase.class,\n        LocalizedTaggableTestCase.class, TagValidationTestSuite.class, NameFinderTestCase.class,\n        BulkNameFinderTestCase.class, ISOCountryTagTestCase.class })\npublic class TagTestSuite\n{\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/TaggableTest.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * @author matthieun\n */\npublic class TaggableTest\n{\n    @Test\n    public void hasAtLeastOneOfTest()\n    {\n        final Taggable taggable = Taggable.with(\"highway\", \"primary\", \"boundary\", \"administrative\");\n        Assert.assertTrue(taggable.hasAtLeastOneOf(Maps.hashMap(\"highway\", \"*\")));\n        Assert.assertTrue(taggable.hasAtLeastOneOf(Maps.hashMap(\"highway\", \"primary\")));\n        Assert.assertFalse(taggable.hasAtLeastOneOf(Maps.hashMap(\"highway\", \"secondary\")));\n        Assert.assertTrue(taggable\n                .hasAtLeastOneOf(Maps.hashMap(\"highway\", \"primary\", \"boundary\", \"administrative\")));\n        Assert.assertTrue(taggable\n                .hasAtLeastOneOf(Maps.hashMap(\"highway\", \"primary\", \"boundary\", \"unknown\")));\n        Assert.assertFalse(taggable.hasAtLeastOneOf(Maps.hashMap()));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/TemporaryDateOnTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.validation.Validators;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for @{TemporaryDateOnTag}\n *\n * @author brianjor\n */\npublic class TemporaryDateOnTagTestCase\n{\n    @Test\n    public void testTemporaryDateOnTag()\n    {\n        final TestTaggable taggable = new TestTaggable(TemporaryDateOnTag.KEY, \"2020\");\n        Assert.assertTrue(Validators.hasValuesFor(taggable, TemporaryDateOnTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/TestSyntheticTag.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Tag used for testing the synthetic attribute\n *\n * @author cstaylor\n */\n@Tag(synthetic = true)\npublic interface TestSyntheticTag\n{\n    @TagKey\n    String KEY = \"replicant\";\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/TurnLaneBackwardTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for {@link org.openstreetmap.atlas.tags.TurnLanesBackwardTag}\n *\n * @author brian_l_davis\n */\npublic class TurnLaneBackwardTagTestCase\n{\n    @Test\n    public void testBackwardTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesBackwardTag.KEY,\n                \"left|through|right\");\n        Assert.assertTrue(TurnLanesBackwardTag.getBackwardTurnLanes(taggable).isPresent());\n        Assert.assertTrue(\n                TurnLanesBackwardTag.hasBackwardTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n    }\n\n    @Test\n    public void testMalformedTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesBackwardTag.KEY,\n                \"sdf|sldkf;jsdl|sldkfj\");\n        Assert.assertTrue(TurnLanesBackwardTag.hasBackwardTurnLane(taggable));\n        Assert.assertFalse(TurnLanesBackwardTag.hasBackwardTurnLane(taggable,\n                TurnLanesBackwardTag.TurnType.THROUGH));\n    }\n\n    @Test\n    public void testMultipleTypeTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesBackwardTag.KEY,\n                \"left;through|right\");\n        Assert.assertTrue(TurnLanesBackwardTag.hasBackwardTurnLane(taggable,\n                TurnLanesBackwardTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesBackwardTag.hasBackwardTurnLane(taggable));\n    }\n\n    @Test\n    public void testSingleTypeTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesBackwardTag.KEY,\n                \"left|through|right\");\n        Assert.assertTrue(TurnLanesBackwardTag.hasBackwardTurnLane(taggable,\n                TurnLanesBackwardTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesBackwardTag.hasBackwardTurnLane(taggable));\n    }\n\n    @Test\n    public void testUntagged()\n    {\n        final TestTaggable taggable = new TestTaggable(HighwayTag.KEY, \"no\");\n        Assert.assertFalse(TurnLanesBackwardTag.getBackwardTurnLanes(taggable).isPresent());\n        Assert.assertFalse(TurnLanesBackwardTag.hasBackwardTurnLane(taggable));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/TurnLaneForwardTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for {@link org.openstreetmap.atlas.tags.TurnLanesForwardTag}\n *\n * @author brian_l_davis\n */\npublic class TurnLaneForwardTagTestCase\n{\n    @Test\n    public void testForwardTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesForwardTag.KEY,\n                \"left|through|right\");\n        Assert.assertTrue(TurnLanesForwardTag.getForwardTurnLanes(taggable).isPresent());\n        Assert.assertTrue(\n                TurnLanesForwardTag.hasForwardTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n    }\n\n    @Test\n    public void testMalformedTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesForwardTag.KEY,\n                \"sdf|sldkf;jsdl|sldkfj\");\n        Assert.assertTrue(TurnLanesForwardTag.hasForwardTurnLane(taggable));\n        Assert.assertFalse(TurnLanesForwardTag.hasForwardTurnLane(taggable,\n                TurnLanesForwardTag.TurnType.THROUGH));\n    }\n\n    @Test\n    public void testMultipleTypeTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesForwardTag.KEY,\n                \"left;through|right\");\n        Assert.assertTrue(TurnLanesForwardTag.hasForwardTurnLane(taggable,\n                TurnLanesForwardTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesForwardTag.hasForwardTurnLane(taggable));\n    }\n\n    @Test\n    public void testSingleTypeTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesForwardTag.KEY,\n                \"left|through|right\");\n        Assert.assertTrue(TurnLanesForwardTag.hasForwardTurnLane(taggable,\n                TurnLanesForwardTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesForwardTag.hasForwardTurnLane(taggable));\n    }\n\n    @Test\n    public void testUntagged()\n    {\n        final TestTaggable taggable = new TestTaggable(HighwayTag.KEY, \"no\");\n        Assert.assertFalse(TurnLanesForwardTag.getForwardTurnLanes(taggable).isPresent());\n        Assert.assertFalse(TurnLanesForwardTag.hasForwardTurnLane(taggable));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/TurnLaneTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for {@link org.openstreetmap.atlas.tags.TurnLanesTag}\n *\n * @author brian_l_davis\n */\npublic class TurnLaneTagTestCase\n{\n    @Test\n    public void testBackwardTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesBackwardTag.KEY,\n                \"left|through|right\");\n        Assert.assertTrue(TurnLanesBackwardTag.getBackwardTurnLanes(taggable).isPresent());\n        Assert.assertTrue(\n                TurnLanesBackwardTag.hasBackwardTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesTag.hasTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n    }\n\n    @Test\n    public void testForwardTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesForwardTag.KEY,\n                \"left|through|right\");\n        Assert.assertTrue(TurnLanesForwardTag.getForwardTurnLanes(taggable).isPresent());\n        Assert.assertTrue(\n                TurnLanesForwardTag.hasForwardTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesTag.hasTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesTag.hasTurnLane(taggable));\n    }\n\n    @Test\n    public void testMalformedTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesTag.KEY, \"sdf|sldkf;jsdl|sldkfj\");\n        Assert.assertTrue(TurnLanesTag.hasTurnLane(taggable));\n        Assert.assertFalse(TurnLanesTag.hasTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n    }\n\n    @Test\n    public void testMatchSpecificTurnLaneTags()\n    {\n        final TestTaggable forwardTurnLaneTagged = new TestTaggable(TurnLanesForwardTag.KEY,\n                \"right\");\n        final TestTaggable backwardTurnLaneTagged = new TestTaggable(TurnLanesBackwardTag.KEY,\n                \"left\");\n\n        Assert.assertTrue(TurnTag.hasTurn(forwardTurnLaneTagged));\n        Assert.assertTrue(TurnTag.hasTurn(forwardTurnLaneTagged, TurnTag.TurnType.RIGHT));\n\n        Assert.assertTrue(TurnTag.hasTurn(backwardTurnLaneTagged));\n        Assert.assertTrue(TurnTag.hasTurn(backwardTurnLaneTagged, TurnTag.TurnType.LEFT));\n    }\n\n    @Test\n    public void testMultipleTypeTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesTag.KEY, \"left;through|right\");\n        Assert.assertTrue(TurnLanesTag.hasTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesTag.hasTurnLane(taggable));\n    }\n\n    @Test\n    public void testSingleTypeTurnLane()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnLanesTag.KEY, \"left|through|right\");\n        Assert.assertTrue(TurnLanesTag.hasTurnLane(taggable, TurnLanesTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnLanesTag.hasTurnLane(taggable));\n    }\n\n    @Test\n    public void testUntagged()\n    {\n        final TestTaggable taggable = new TestTaggable(HighwayTag.KEY, \"no\");\n        Assert.assertFalse(TurnLanesTag.getTurnLanes(taggable).isPresent());\n        Assert.assertFalse(TurnLanesTag.hasTurnLane(taggable));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/TurnTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test for {@link org.openstreetmap.atlas.tags.TurnTag}\n *\n * @author brian_l_davis\n */\npublic class TurnTagTestCase\n{\n    @Test\n    public void testMalformedTurn()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnTag.KEY, \"sdf|sldkf;jsdl|sldkfj\");\n        Assert.assertTrue(TurnTag.hasTurn(taggable));\n        Assert.assertFalse(TurnTag.hasTurn(taggable, TurnTag.TurnType.THROUGH));\n    }\n\n    @Test\n    public void testMatchSpecificTurnTags()\n    {\n        final TestTaggable forwardTurnLaneTagged = new TestTaggable(TurnLanesForwardTag.KEY,\n                \"right\");\n        final TestTaggable backwardTurnLaneTagged = new TestTaggable(TurnLanesBackwardTag.KEY,\n                \"left\");\n        final TestTaggable turnLanedTagged = new TestTaggable(TurnLanesBackwardTag.KEY, \"through\");\n\n        Assert.assertTrue(TurnTag.hasTurn(forwardTurnLaneTagged));\n        Assert.assertTrue(TurnTag.hasTurn(forwardTurnLaneTagged, TurnTag.TurnType.RIGHT));\n\n        Assert.assertTrue(TurnTag.hasTurn(backwardTurnLaneTagged));\n        Assert.assertTrue(TurnTag.hasTurn(backwardTurnLaneTagged, TurnTag.TurnType.LEFT));\n\n        Assert.assertTrue(TurnTag.hasTurn(turnLanedTagged));\n        Assert.assertTrue(TurnTag.hasTurn(turnLanedTagged, TurnTag.TurnType.THROUGH));\n    }\n\n    @Test\n    public void testMultipleTypeTurn()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnTag.KEY, \"left;through|right\");\n        Assert.assertTrue(TurnTag.hasTurn(taggable, TurnTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnTag.hasTurn(taggable));\n    }\n\n    @Test\n    public void testSingleTypeTurn()\n    {\n        final TestTaggable taggable = new TestTaggable(TurnTag.KEY, \"left|through|right\");\n        Assert.assertTrue(TurnTag.hasTurn(taggable, TurnTag.TurnType.THROUGH));\n        Assert.assertTrue(TurnTag.hasTurn(taggable));\n    }\n\n    @Test\n    public void testUntagged()\n    {\n        final TestTaggable taggable = new TestTaggable(HighwayTag.KEY, \"no\");\n        Assert.assertFalse(TurnTag.getTurns(taggable).isPresent());\n        Assert.assertFalse(TurnTag.hasTurn(taggable));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/extraction/AltitudeExtractorTest.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Altitude;\n\n/**\n * Unit test for {@link AltitudeExtractor}.\n *\n * @author bbreithaupt\n */\npublic class AltitudeExtractorTest\n{\n    @Test\n    public void validMetersTest()\n    {\n        Assert.assertEquals(Optional.of(Altitude.meters(20)),\n                AltitudeExtractor.validateAndExtract(\"20 m\"));\n    }\n\n    @Test\n    public void validNegativeMetersCapsTest()\n    {\n        Assert.assertEquals(Optional.of(Altitude.meters(-20)),\n                AltitudeExtractor.validateAndExtract(\"-20 M\"));\n    }\n\n    @Test\n    public void validNegativeMetersTest()\n    {\n        Assert.assertEquals(Optional.of(Altitude.meters(-20)),\n                AltitudeExtractor.validateAndExtract(\"-20 m\"));\n    }\n\n    @Test\n    public void validNegativeNumberTest()\n    {\n        Assert.assertEquals(Optional.of(Altitude.meters(-20.5)),\n                AltitudeExtractor.validateAndExtract(\"-20.5\"));\n    }\n\n    @Test\n    public void validNumberTest()\n    {\n        Assert.assertEquals(Optional.of(Altitude.meters(20.5)),\n                AltitudeExtractor.validateAndExtract(\"20.5\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/extraction/LengthExtractorTest.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * Unit test for {@link LengthExtractor}.\n *\n * @author bbreithaupt\n */\npublic class LengthExtractorTest\n{\n    @Test\n    public void invalidMetersTest()\n    {\n        Assert.assertEquals(Optional.empty(), LengthExtractor.validateAndExtract(\"20m\"));\n    }\n\n    @Test\n    public void invalidNumberTest()\n    {\n        Assert.assertEquals(Optional.empty(), LengthExtractor.validateAndExtract(\"20.s\"));\n    }\n\n    @Test\n    public void validFeetInchesTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.feetAndInches(20, 5)),\n                LengthExtractor.validateAndExtract(\"20'5\\\"\"));\n    }\n\n    @Test\n    public void validFeetTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.feet(20)),\n                LengthExtractor.validateAndExtract(\"20'\"));\n    }\n\n    @Test\n    public void validInchesTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.inches(5)),\n                LengthExtractor.validateAndExtract(\"5\\\"\"));\n    }\n\n    @Test\n    public void validKilometersTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.kilometers(20.5)),\n                LengthExtractor.validateAndExtract(\"20.5 km\"));\n    }\n\n    @Test\n    public void validMetersCapsTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.meters(20)),\n                LengthExtractor.validateAndExtract(\"20 M\"));\n    }\n\n    @Test\n    public void validMetersTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.meters(20)),\n                LengthExtractor.validateAndExtract(\"20 m\"));\n    }\n\n    @Test\n    public void validMilesTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.miles(20.54)),\n                LengthExtractor.validateAndExtract(\"20.54 mi\"));\n    }\n\n    @Test\n    public void validMilesWithCommaDecimalSeparatorTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.miles(20.54)),\n                LengthExtractor.validateAndExtract(\"20,54 mi\"));\n    }\n\n    @Test\n    public void validNauticalMilesMixedCapsTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.nauticalMiles(20.543)),\n                LengthExtractor.validateAndExtract(\"20.543 nMI\"));\n    }\n\n    @Test\n    public void validNauticalMilesTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.nauticalMiles(20.543)),\n                LengthExtractor.validateAndExtract(\"20.543 nmi\"));\n    }\n\n    @Test\n    public void validNumberTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.meters(20.5)),\n                LengthExtractor.validateAndExtract(\"20.5\"));\n    }\n\n    @Test\n    public void validNumberWithCommaDecimalSeparatorTest()\n    {\n        Assert.assertEquals(Optional.of(Distance.meters(20.5)),\n                LengthExtractor.validateAndExtract(\"20,5\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/extraction/LongExtractorTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\n\n/**\n * Test case for {@link LongExtractor}\n *\n * @author brian_l_davis\n */\npublic class LongExtractorTestCase\n{\n    /**\n     * @author brian_l_davis\n     */\n    @Tag(value = Tag.Validation.LONG, range = @Tag.Range(min = 1, max = 10, exclude = { 5 }))\n    private interface TestTag\n    {\n    }\n\n    private static final Tag tag = TestTag.class.getDeclaredAnnotation(Tag.class);\n\n    @Test\n    public void testExtractorInRange()\n    {\n        final LongExtractor extractor = new LongExtractor();\n        Assert.assertTrue(extractor.validateAndExtract(\"1\", tag).isPresent());\n        Assert.assertTrue(extractor.validateAndExtract(\"3\", tag).isPresent());\n        Assert.assertTrue(extractor.validateAndExtract(\"10\", tag).isPresent());\n    }\n\n    @Test\n    public void testExtractorNotInRange()\n    {\n        final LongExtractor extractor = new LongExtractor();\n        Assert.assertFalse(extractor.validateAndExtract(\"0\", tag).isPresent());\n        Assert.assertFalse(extractor.validateAndExtract(\"5\", tag).isPresent());\n        Assert.assertFalse(extractor.validateAndExtract(\"11\", tag).isPresent());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/extraction/OrdinalExtractorTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Range;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\n\n/**\n * Test case for {@link OrdinalExtractor}\n *\n * @author brian_l_davis\n */\npublic class OrdinalExtractorTestCase\n{\n    /**\n     * @author brian_l_davis\n     */\n    @Tag(value = Validation.ORDINAL, range = @Range(min = 1, max = 10, exclude = { 5 }))\n    private interface TestTag\n    {\n    }\n\n    private static final Tag tag = TestTag.class.getDeclaredAnnotation(Tag.class);\n\n    @Test\n    public void testExtractorInRange()\n    {\n        final OrdinalExtractor extractor = new OrdinalExtractor();\n        Assert.assertTrue(extractor.validateAndExtract(\"1\", tag).isPresent());\n        Assert.assertTrue(extractor.validateAndExtract(\"3\", tag).isPresent());\n        Assert.assertTrue(extractor.validateAndExtract(\"10\", tag).isPresent());\n    }\n\n    @Test\n    public void testExtractorNotInRange()\n    {\n        final OrdinalExtractor extractor = new OrdinalExtractor();\n        Assert.assertFalse(extractor.validateAndExtract(\"0\", tag).isPresent());\n        Assert.assertFalse(extractor.validateAndExtract(\"5\", tag).isPresent());\n        Assert.assertFalse(extractor.validateAndExtract(\"11\", tag).isPresent());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/extraction/SpeedExtractorTest.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.extraction;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Speed;\n\n/**\n * Unit tests for {@link SpeedExtractor}.\n *\n * @author bbreithaupt\n */\npublic class SpeedExtractorTest\n{\n    @Test\n    public void invalidImplicitDelimitedTest()\n    {\n        Assert.assertEquals(Optional.empty(),\n                SpeedExtractor.validateAndExtract(\"RO:urban; RO:rural\"));\n    }\n\n    @Test\n    public void invalidNoneTest()\n    {\n        Assert.assertEquals(Optional.empty(), SpeedExtractor.validateAndExtract(\"none\"));\n    }\n\n    @Test\n    public void invalidNumberDelimitedTest()\n    {\n        Assert.assertEquals(Optional.empty(), SpeedExtractor.validateAndExtract(\"60; 50\"));\n    }\n\n    @Test\n    public void invalidNumberMPHTest()\n    {\n        Assert.assertEquals(Optional.empty(), SpeedExtractor.validateAndExtract(\"60mph\"));\n    }\n\n    @Test\n    public void validImplicitTest()\n    {\n        Assert.assertEquals(Optional.of(Speed.kilometersPerHour(50)),\n                SpeedExtractor.validateAndExtract(\"RO:urban\"));\n    }\n\n    @Test\n    public void validNumberKPHTest()\n    {\n        Assert.assertEquals(Optional.of(Speed.kilometersPerHour(60)),\n                SpeedExtractor.validateAndExtract(\"60 kph\"));\n    }\n\n    @Test\n    public void validNumberKnotsTest()\n    {\n        Assert.assertEquals(Optional.of(Speed.knots(60)),\n                SpeedExtractor.validateAndExtract(\"60 knots\"));\n    }\n\n    @Test\n    public void validNumberMPHTest()\n    {\n        Assert.assertEquals(Optional.of(Speed.milesPerHour(60)),\n                SpeedExtractor.validateAndExtract(\"60 mph\"));\n    }\n\n    @Test\n    public void validNumberTest()\n    {\n        Assert.assertEquals(Optional.of(Speed.kilometersPerHour(60)),\n                SpeedExtractor.validateAndExtract(\"60\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/AddressFlatsTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.AddressFlatsTag;\n\n/**\n * Test case verifying that Address Flats Tag can be properly validated.\n *\n * @author mgostintsev\n */\npublic class AddressFlatsTagTestCase extends BaseTagTestCase\n{\n    @Test\n    public void testInvalidValue()\n    {\n        Assert.assertTrue(validators().canValidate(AddressFlatsTag.KEY));\n        Assert.assertFalse(validators().getValidatorFor(AddressFlatsTag.KEY).isValid(\" \"));\n    }\n\n    @Test\n    public void testValidValues()\n    {\n        Assert.assertTrue(validators().canValidate(AddressFlatsTag.KEY));\n        Assert.assertTrue(validators().getValidatorFor(AddressFlatsTag.KEY).isValid(\"7\"));\n        Assert.assertTrue(validators().getValidatorFor(AddressFlatsTag.KEY).isValid(\"1-20\"));\n        Assert.assertTrue(\n                validators().getValidatorFor(AddressFlatsTag.KEY).isValid(\"3-7;10;14;16-18\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/BaseTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.BeforeClass;\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * All tag tests are subclasses of this so we can consolidate how validators are initialized\n *\n * @author cstaylor\n */\npublic abstract class BaseTagTestCase\n{\n    private static Validators validators;\n\n    @BeforeClass\n    public static void setupValidators()\n    {\n        validators = new Validators(Taggable.class);\n    }\n\n    protected static Validators validators()\n    {\n        return BaseTagTestCase.validators;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/BuildingTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.BuildingTag;\n\n/**\n * Simple test case verifying that BuildTag.isBuilding works\n *\n * @author cstaylor\n */\npublic class BuildingTagTestCase\n{\n    @Test\n    public void testIsBuilding()\n    {\n        Assert.assertTrue(BuildingTag.isBuilding(\"ComMERcial\"));\n    }\n\n    @Test\n    public void testIsNotBuilding()\n    {\n        Assert.assertFalse(BuildingTag.isBuilding(\"Pyramids\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/DisusedShopTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.DisusedShopTag;\nimport org.openstreetmap.atlas.tags.ShopTag;\n\n/**\n * Testing the with annotation feature with the disused shop tag\n *\n * @author cstaylor\n */\npublic class DisusedShopTagTestCase extends BaseTagTestCase\n{\n\n    @Test\n    public void testInvalidValue()\n    {\n        Assert.assertTrue(validators().canValidate(DisusedShopTag.KEY));\n        Assert.assertFalse(\n                validators().getValidatorFor(DisusedShopTag.KEY).isValid(\"Crisps and Chips\"));\n    }\n\n    @Test\n    public void testValidValues()\n    {\n        Assert.assertTrue(validators().canValidate(DisusedShopTag.KEY));\n        for (final ShopTag tag : ShopTag.values())\n        {\n            Assert.assertTrue(validators().getValidatorFor(DisusedShopTag.KEY)\n                    .isValid(tag.name().toLowerCase()));\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/FromEnumTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.BuildingTag;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test case for verifying reflections-based enum value parsing\n *\n * @author cstaylor\n */\npublic class FromEnumTestCase\n{\n    /**\n     * Private tag used for with=enum testing\n     *\n     * @author cstaylor\n     */\n    @Tag(with = { EightBall.class })\n    public interface DisusedEightBall\n    {\n        @TagKey\n        String KEY = \"disused:eightball\";\n    }\n\n    /**\n     * Private tag used for test case\n     *\n     * @author cstaylor\n     */\n    @Tag\n    public enum EightBall\n    {\n        YES,\n        NO,\n        MAYBE;\n\n        @TagKey\n        public static final String KEY = \"magic-eight-ball\";\n    }\n\n    @Test\n    public void testAnnotationExists()\n    {\n        final TestTaggable testing = new TestTaggable(EightBall.KEY, \"maYbE\");\n        final Optional<EightBall> found = Validators.fromAnnotation(EightBall.class, testing);\n        Assert.assertTrue(found.isPresent());\n        Assert.assertEquals(EightBall.MAYBE, found.get());\n    }\n\n    @Test\n    public void testAnnotationIllegalValue()\n    {\n        final TestTaggable testing = new TestTaggable(EightBall.KEY, \"Nope\");\n        final Optional<EightBall> found = Validators.fromAnnotation(EightBall.class, testing);\n        Assert.assertFalse(found.isPresent());\n    }\n\n    @Test\n    public void testAnnotationMissingValue()\n    {\n        final TestTaggable testing = new TestTaggable(BuildingTag.KEY, \"Nope\");\n        final Optional<EightBall> found = Validators.fromAnnotation(EightBall.class, testing);\n        Assert.assertFalse(found.isPresent());\n    }\n\n    @Test\n    public void testExists()\n    {\n        final TestTaggable testing = new TestTaggable(EightBall.KEY, \"maYbE\");\n        final Optional<EightBall> found = Validators.from(EightBall.class, testing);\n        Assert.assertTrue(found.isPresent());\n        Assert.assertEquals(EightBall.MAYBE, found.get());\n    }\n\n    @Test\n    public void testIllegalValue()\n    {\n        final TestTaggable testing = new TestTaggable(EightBall.KEY, \"Nope\");\n        final Optional<EightBall> found = Validators.from(EightBall.class, testing);\n        Assert.assertFalse(found.isPresent());\n    }\n\n    @Test\n    public void testMissingValue()\n    {\n        final TestTaggable testing = new TestTaggable(BuildingTag.KEY, \"Nope\");\n        final Optional<EightBall> found = Validators.from(EightBall.class, testing);\n        Assert.assertFalse(found.isPresent());\n    }\n\n    @Test\n    public void testWith()\n    {\n        final TestTaggable testing = new TestTaggable(DisusedEightBall.KEY, \"maYbE\");\n        final Optional<EightBall> found = Validators.from(DisusedEightBall.class, EightBall.class,\n                testing);\n        Assert.assertTrue(found.isPresent());\n        Assert.assertEquals(EightBall.MAYBE, found.get());\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/HighwayTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.AreaTag;\nimport org.openstreetmap.atlas.tags.BuildingTag;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Simple test case verifying String to enum value conversion using reflection\n *\n * @author cstaylor\n */\npublic class HighwayTagTestCase\n{\n    @Test\n    public void testClassificationEquality()\n    {\n        Assert.assertTrue(HighwayTag.MOTORWAY.isOfEqualClassification(HighwayTag.MOTORWAY));\n        Assert.assertTrue(HighwayTag.PRIMARY.isOfEqualClassification(HighwayTag.PRIMARY_LINK));\n        Assert.assertFalse(HighwayTag.PRIMARY.isOfEqualClassification(HighwayTag.SECONDARY));\n    }\n\n    @Test\n    public void testClassificationMatch()\n    {\n        Assert.assertTrue(HighwayTag.MOTORWAY.isIdenticalClassification(HighwayTag.MOTORWAY));\n        Assert.assertFalse(HighwayTag.PRIMARY.isIdenticalClassification(HighwayTag.SECONDARY));\n    }\n\n    @Test\n    public void testHighwayFromLink()\n    {\n        Assert.assertEquals(HighwayTag.PRIMARY, HighwayTag.PRIMARY_LINK.getHighwayFromLink().get());\n        Assert.assertEquals(HighwayTag.SECONDARY,\n                HighwayTag.SECONDARY_LINK.getHighwayFromLink().get());\n        Assert.assertEquals(HighwayTag.TERTIARY,\n                HighwayTag.TERTIARY_LINK.getHighwayFromLink().get());\n        Assert.assertEquals(HighwayTag.TRUNK, HighwayTag.TRUNK_LINK.getHighwayFromLink().get());\n        Assert.assertEquals(HighwayTag.MOTORWAY,\n                HighwayTag.MOTORWAY_LINK.getHighwayFromLink().get());\n        Assert.assertEquals(Optional.empty(), HighwayTag.BRIDLEWAY.getHighwayFromLink());\n    }\n\n    @Test\n    public void testIsCarNavigableHighway()\n    {\n        for (final String test : Arrays.asList(\"MOTOrWAY\", \"TRUNK\", \"PRIMARY\", \"SECONDARY\",\n                \"TERTIARY\", \"UNCLASSIFIED\", \"RESIDENTIAL\", \"SERVICE\", \"MOTORWAY_LINK\", \"TRUNK_LINK\",\n                \"PRIMARY_LINK\", \"SECONDARY_LINK\", \"TERTIARY_LINK\", \"LIvING_STREET\", \"TRACK\",\n                \"ROAD\"))\n        {\n            Assert.assertTrue(\n                    HighwayTag.isCarNavigableHighway(new TestTaggable(HighwayTag.KEY, test)));\n\n        }\n    }\n\n    @Test\n    public void testIsCarNotNavigableHighway()\n    {\n        Assert.assertFalse(\n                HighwayTag.isCarNavigableHighway(new TestTaggable(HighwayTag.KEY, \"dog\")));\n    }\n\n    @Test\n    public void testIsHighwayArea()\n    {\n        final Map<String, String> tags = new HashMap<>();\n        tags.put(HighwayTag.KEY, HighwayTag.PEDESTRIAN.getTagValue());\n        tags.put(BuildingTag.KEY, BuildingTag.YES.name().toLowerCase());\n        Assert.assertTrue(HighwayTag.isHighwayArea(new TestTaggable(tags)));\n\n        tags.put(HighwayTag.KEY, HighwayTag.FOOTWAY.getTagValue());\n        Assert.assertTrue(HighwayTag.isHighwayArea(new TestTaggable(tags)));\n\n        tags.remove(BuildingTag.KEY);\n        Assert.assertFalse(HighwayTag.isHighwayArea(new TestTaggable(tags)));\n\n        tags.put(AreaTag.KEY, AreaTag.YES.name().toLowerCase());\n        Assert.assertTrue(HighwayTag.isHighwayArea(new TestTaggable(tags)));\n\n        tags.put(HighwayTag.KEY, HighwayTag.TRACK.getTagValue());\n        Assert.assertFalse(HighwayTag.isHighwayArea(new TestTaggable(tags)));\n    }\n\n    @Test\n    public void testLessImportant()\n    {\n        Assert.assertTrue(HighwayTag.SECONDARY.isLessImportantThan(HighwayTag.PRIMARY));\n        Assert.assertTrue(HighwayTag.TERTIARY.isLessImportantThan(HighwayTag.TRUNK));\n\n        // Testing equivalence\n        Assert.assertTrue(HighwayTag.PRIMARY.isLessImportantThanOrEqualTo(HighwayTag.PRIMARY));\n    }\n\n    @Test\n    public void testLinkFromHighway()\n    {\n        Assert.assertEquals(HighwayTag.PRIMARY_LINK, HighwayTag.PRIMARY.getLinkFromHighway().get());\n        Assert.assertEquals(HighwayTag.SECONDARY_LINK,\n                HighwayTag.SECONDARY.getLinkFromHighway().get());\n        Assert.assertEquals(HighwayTag.TERTIARY_LINK,\n                HighwayTag.TERTIARY.getLinkFromHighway().get());\n        Assert.assertEquals(HighwayTag.TRUNK_LINK, HighwayTag.TRUNK.getLinkFromHighway().get());\n        Assert.assertEquals(HighwayTag.MOTORWAY_LINK,\n                HighwayTag.MOTORWAY.getLinkFromHighway().get());\n        Assert.assertEquals(Optional.empty(), HighwayTag.BRIDLEWAY.getLinkFromHighway());\n    }\n\n    @Test\n    public void testMoreImportant()\n    {\n        Assert.assertTrue(HighwayTag.MOTORWAY.isMoreImportantThan(HighwayTag.TERTIARY));\n        Assert.assertTrue(HighwayTag.PRIMARY.isMoreImportantThan(HighwayTag.SECONDARY));\n\n        // Testing equivalence\n        Assert.assertTrue(HighwayTag.PRIMARY.isMoreImportantThanOrEqualTo(HighwayTag.PRIMARY));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/ISOCountryTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\n\n/**\n * Simple test case for verifying tags using the ValidatorFactory\n *\n * @author cstaylor\n */\npublic class ISOCountryTagTestCase extends BaseTagTestCase\n{\n    @Test\n    public void testBadCountry()\n    {\n        Assert.assertTrue(validators().canValidate(ISOCountryTag.KEY));\n        Assert.assertFalse(validators().getValidatorFor(ISOCountryTag.KEY).isValid(\"Timbuktu\"));\n    }\n\n    @Test\n    public void testJapan()\n    {\n        Assert.assertTrue(validators().canValidate(ISOCountryTag.KEY));\n        Assert.assertTrue(validators().getValidatorFor(ISOCountryTag.KEY).isValid(\"JPN\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/LastEditUserIdentifierTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.LastEditUserIdentifierTag;\n\n/**\n * Test case for LastEditUserIdentifier tag\n *\n * @author cstaylor\n */\npublic class LastEditUserIdentifierTestCase extends BaseTagTestCase\n{\n    @Test\n    public void bad()\n    {\n        Assert.assertFalse(validators().isValidFor(LastEditUserIdentifierTag.KEY, \"Nope\"));\n    }\n\n    @Test\n    public void good()\n    {\n        Assert.assertTrue(validators().isValidFor(LastEditUserIdentifierTag.KEY, \"1846410\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/LayerTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.LayerTag;\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * Test case for the LayerTag class\n *\n * @author cstaylor\n */\npublic class LayerTagTestCase extends BaseTagTestCase\n{\n    @Test\n    public void layerMaxValue()\n    {\n        Assert.assertEquals(LayerTag.getMaxValue(), 5);\n        Assert.assertEquals(LayerTag.getMinValue(), -5);\n    }\n\n    @Test\n    public void layerNotZero()\n    {\n        Assert.assertFalse(validators().isValidFor(LayerTag.KEY, \"0\"));\n    }\n\n    @Test\n    public void layerTooHigh()\n    {\n        Assert.assertFalse(validators().isValidFor(LayerTag.KEY, \"6\"));\n    }\n\n    @Test\n    public void layerTooLow()\n    {\n        Assert.assertFalse(validators().isValidFor(LayerTag.KEY, \"-6\"));\n    }\n\n    @Test\n    public void layersJustRight()\n    {\n        for (int loop = -5; loop < 0; loop++)\n        {\n            Assert.assertTrue(validators().isValidFor(LayerTag.KEY, String.valueOf(loop)));\n        }\n\n        for (int loop = 1; loop <= 5; loop++)\n        {\n            Assert.assertTrue(validators().isValidFor(LayerTag.KEY, String.valueOf(loop)));\n        }\n    }\n\n    @Test\n    public void taggablesOnDifferentLayer()\n    {\n        final Taggable taggableOne = Taggable.with(\"layer\", \"1\");\n        final Taggable taggableTwo = Taggable.with(\"layer\", \"2\");\n        Assert.assertFalse(LayerTag.areOnSameLayer(taggableOne, taggableTwo));\n    }\n\n    @Test\n    public void taggablesOnSameLayer()\n    {\n        final Taggable taggableOne = Taggable.with(\"layer\", \"1\");\n        final Taggable taggableTwo = Taggable.with(\"layer\", \"2\");\n        final Taggable taggableThree = Taggable.with(\"layer\", \"0\");\n        final Taggable taggableFour = Taggable.with(\"highway\", \"primary\");\n        Assert.assertFalse(LayerTag.areOnSameLayer(taggableOne, taggableTwo));\n        Assert.assertTrue(LayerTag.areOnSameLayer(taggableThree, taggableFour));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/LengthValidatorTest.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Unit test for {@link LengthValidator}.\n *\n * @author bbreithaupt\n */\npublic class LengthValidatorTest\n{\n    private static final LengthValidator VALIDATOR = new LengthValidator();\n\n    @Test\n    public void invalidFeetInchesTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"12'5\\\"\"));\n    }\n\n    @Test\n    public void invalidFeetTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"12'\"));\n    }\n\n    @Test\n    public void invalidInchesTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"5\\\"\"));\n    }\n\n    @Test\n    public void invalidMetersTest()\n    {\n        Assert.assertFalse(VALIDATOR.isValid(\"123m\"));\n    }\n\n    @Test\n    public void invalidNumberTest()\n    {\n        Assert.assertFalse(VALIDATOR.isValid(\"123s m\"));\n    }\n\n    @Test\n    public void validKilometersTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"123.5 km\"));\n    }\n\n    @Test\n    public void validMetersCapsTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"123 M\"));\n    }\n\n    @Test\n    public void validMetersTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"123 m\"));\n    }\n\n    @Test\n    public void validMilesTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"123.54 mi\"));\n    }\n\n    @Test\n    public void validNauticalMilesMixedCapsTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"123.543 nMI\"));\n    }\n\n    @Test\n    public void validNauticalMilesTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"123.543 nmi\"));\n    }\n\n    @Test\n    public void validNumberTest()\n    {\n        Assert.assertTrue(VALIDATOR.isValid(\"123.5\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/LevelTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.LevelTag;\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * Test case for the {@link LevelTag} class\n *\n * @author sayas01\n */\npublic class LevelTagTestCase extends BaseTagTestCase\n{\n    @Test\n    public void taggablesOnDifferentLevel()\n    {\n        final Taggable taggableOne = Taggable.with(\"level\", \"1\");\n        final Taggable taggableTwo = Taggable.with(\"level\", \"2\");\n        final Taggable taggableThree = Taggable.with(\"level\", \"0\");\n        final Taggable taggableFour = Taggable.with(\"highway\", \"primary\");\n        Assert.assertFalse(LevelTag.areOnSameLevel(taggableOne, taggableTwo));\n        Assert.assertFalse(LevelTag.areOnSameLevel(taggableThree, taggableFour));\n    }\n\n    @Test\n    public void taggablesOnSameLevel()\n    {\n        final Taggable taggableOne = Taggable.with(\"level\", \"1\");\n        final Taggable taggableTwo = Taggable.with(\"level\", \"1\");\n        Assert.assertTrue(LevelTag.areOnSameLevel(taggableOne, taggableTwo));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/MaxHeightTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.MaxHeightTag;\n\n/**\n * Test cases for the maxheight tag\n *\n * @author cstaylor\n */\npublic class MaxHeightTagTestCase extends BaseTagTestCase\n{\n    @Test\n    public void englishSystem()\n    {\n        Assert.assertTrue(validators().isValidFor(MaxHeightTag.KEY, \"5\\'9\\\"\"));\n    }\n\n    @Test\n    public void garbage()\n    {\n        Assert.assertFalse(validators().isValidFor(MaxHeightTag.KEY, \"nope\"));\n    }\n\n    @Test\n    public void justAFractionalNumber()\n    {\n        Assert.assertTrue(validators().isValidFor(MaxHeightTag.KEY, \"3\"));\n    }\n\n    @Test\n    public void justANumber()\n    {\n        Assert.assertTrue(validators().isValidFor(MaxHeightTag.KEY, \"3.5\"));\n    }\n\n    @Test\n    public void withMeters()\n    {\n        Assert.assertTrue(validators().isValidFor(MaxHeightTag.KEY, \"3.5 m\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/MaxWidthTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.MaxWidthTag;\n\n/**\n * Test cases for the maxwidth tag\n *\n * @author cstaylor\n */\npublic class MaxWidthTagTestCase extends BaseTagTestCase\n{\n    @Test\n    public void englishSystem()\n    {\n        Assert.assertTrue(validators().isValidFor(MaxWidthTag.KEY, \"5\\'9\\\"\"));\n    }\n\n    @Test\n    public void garbage()\n    {\n        Assert.assertFalse(validators().isValidFor(MaxWidthTag.KEY, \"nope\"));\n    }\n\n    @Test\n    public void justAFractionalNumber()\n    {\n        Assert.assertTrue(validators().isValidFor(MaxWidthTag.KEY, \"3\"));\n    }\n\n    @Test\n    public void justANumber()\n    {\n        Assert.assertTrue(validators().isValidFor(MaxWidthTag.KEY, \"3.5\"));\n    }\n\n    @Test\n    public void withMeters()\n    {\n        Assert.assertTrue(validators().isValidFor(MaxWidthTag.KEY, \"3.5 m\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/OpeningHoursTagTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.OpeningHoursTag;\n\n/**\n * Testing various inputs for the OpeningHoursTag, since it uses regular expressions\n *\n * @author cstaylor\n */\npublic class OpeningHoursTagTestCase extends BaseTagTestCase\n{\n    @Test\n    public void testDateAndTime()\n    {\n        Assert.assertTrue(validators().canValidate(OpeningHoursTag.KEY));\n        Assert.assertTrue(\n                validators().getValidatorFor(OpeningHoursTag.KEY).isValid(\"Mo-Fr 08:00-22:00\"));\n    }\n\n    @Test\n    public void testIllegalValue()\n    {\n        Assert.assertTrue(validators().canValidate(OpeningHoursTag.KEY));\n        Assert.assertFalse(validators().getValidatorFor(OpeningHoursTag.KEY).isValid(\"Garbage\"));\n    }\n\n    @Test\n    public void testTimeOnly()\n    {\n        Assert.assertTrue(validators().canValidate(OpeningHoursTag.KEY));\n        Assert.assertTrue(validators().getValidatorFor(OpeningHoursTag.KEY).isValid(\"08:00-22:00\"));\n    }\n\n    @Test\n    public void testTwentyFourSeven()\n    {\n        Assert.assertTrue(validators().canValidate(OpeningHoursTag.KEY));\n        Assert.assertTrue(validators().getValidatorFor(OpeningHoursTag.KEY).isValid(\"24/7\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/SpeedTagsTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.MaxSpeedBackwardTag;\nimport org.openstreetmap.atlas.tags.MaxSpeedForwardTag;\nimport org.openstreetmap.atlas.tags.MaxSpeedTag;\nimport org.openstreetmap.atlas.tags.MinSpeedTag;\n\n/**\n * Test case verifying that speed tags can be properly validated.\n *\n * @author mgostintsev\n */\npublic class SpeedTagsTestCase extends BaseTagTestCase\n{\n    @Test\n    public void testInvalidValue()\n    {\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\" \"));\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"mph\"));\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"-60 kph\"));\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"-60\"));\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"60 km/h\"));\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"sixty\"));\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"sixty kph\"));\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"sixty mph\"));\n        Assert.assertFalse(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"sixty knots\"));\n    }\n\n    @Test\n    public void testValidValues()\n    {\n        Assert.assertTrue(validators().canValidate(MaxSpeedTag.KEY));\n        Assert.assertTrue(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"60\"));\n        Assert.assertTrue(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"60 kph\"));\n        Assert.assertTrue(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"60kph\"));\n        Assert.assertTrue(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"60 knots\"));\n        Assert.assertTrue(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"60mph\"));\n        Assert.assertTrue(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"60 mph\"));\n        Assert.assertTrue(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"none\"));\n        Assert.assertTrue(validators().getValidatorFor(MaxSpeedTag.KEY).isValid(\"RO:urban\"));\n        Assert.assertTrue(validators().canValidate(MinSpeedTag.KEY));\n        Assert.assertTrue(validators().getValidatorFor(MinSpeedTag.KEY).isValid(\"60\"));\n        Assert.assertTrue(validators().getValidatorFor(MinSpeedTag.KEY).isValid(\"60 kph\"));\n        Assert.assertTrue(validators().getValidatorFor(MinSpeedTag.KEY).isValid(\"60kph\"));\n        Assert.assertTrue(validators().getValidatorFor(MinSpeedTag.KEY).isValid(\"60 knots\"));\n        Assert.assertTrue(validators().getValidatorFor(MinSpeedTag.KEY).isValid(\"60mph\"));\n        Assert.assertTrue(validators().getValidatorFor(MinSpeedTag.KEY).isValid(\"60 mph\"));\n        Assert.assertTrue(validators().getValidatorFor(MinSpeedTag.KEY).isValid(\"none\"));\n        Assert.assertTrue(validators().getValidatorFor(MinSpeedTag.KEY).isValid(\"RO:urban\"));\n        Assert.assertTrue(validators().canValidate(MaxSpeedBackwardTag.KEY));\n        Assert.assertTrue(validators().canValidate(MaxSpeedForwardTag.KEY));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/TagValidationTestSuite.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\n\n/**\n * This is a JUnit TestSuite for running multiple tests in one run, so when I use eclemma I can see\n * the coverage results. At the moment gradle doesn't run these, so these are primarily for code\n * coverage checks within Eclipse\n *\n * @author cstaylor\n */\n@RunWith(Suite.class)\n@Suite.SuiteClasses({ DisusedShopTagTestCase.class, ISOCountryTagTestCase.class,\n        OpeningHoursTagTestCase.class, ValidatorsTestCase.class, BuildingTagTestCase.class,\n        FromEnumTestCase.class, HighwayTagTestCase.class, ValidatorsHasValuesForTestCase.class,\n        LastEditUserIdentifierTestCase.class, SpeedTagsTestCase.class })\npublic class TagValidationTestSuite\n{\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/TagValueAsTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.ParkingTag;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test case for verifying if TagAsValue works when converting tag values to enums\n *\n * @author cstaylor\n */\npublic class TagValueAsTestCase extends BaseTagTestCase\n{\n    @Test\n    public void simpleEnum()\n    {\n        Assert.assertEquals(Optional.of(HighwayTag.MOTORWAY),\n                Validators.from(HighwayTag.class, new TestTaggable(HighwayTag.KEY, \"motorway\")));\n    }\n\n    @Test\n    public void stringToEnum()\n    {\n        Assert.assertEquals(Optional.of(ParkingTag.MULTI_STOREY), Validators.from(ParkingTag.class,\n                new TestTaggable(ParkingTag.KEY, \"multi-storey\")));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/ValidatorsHasValuesForTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.function.Predicate;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.LastEditTimeTag;\nimport org.openstreetmap.atlas.tags.NaturalTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.WaterTag;\nimport org.openstreetmap.atlas.tags.WaterwayTag;\nimport org.openstreetmap.atlas.tags.names.NameTag;\nimport org.openstreetmap.atlas.utilities.testing.TestTaggable;\n\n/**\n * Test cases for hasValuesFor Validators convenience method\n *\n * @author cstaylor\n * @author mgostintsev\n */\npublic class ValidatorsHasValuesForTestCase\n{\n    @Test\n    public void degenerateCase()\n    {\n        Assert.assertTrue(Validators.hasValuesFor(new TestTaggable(NameTag.KEY, \"yes!\")));\n    }\n\n    @Test\n    public void keyPresentDifferentValues()\n    {\n        Assert.assertFalse(Validators.isOfSameType(new TestTaggable(HighwayTag.BUS_STOP),\n                new TestTaggable(HighwayTag.CYCLEWAY), HighwayTag.class));\n    }\n\n    @Test\n    public void keyPresentSameValue()\n    {\n        Assert.assertTrue(Validators.isOfSameType(new TestTaggable(WaterTag.LAKE),\n                new TestTaggable(WaterTag.LAKE), WaterTag.class));\n    }\n\n    @Test\n    public void oneEnumeratedTagOneNonEnumeratedTag()\n    {\n        Assert.assertFalse(Validators.isOfSameType(new TestTaggable(WaterwayTag.CANAL),\n                new TestTaggable(LastEditTimeTag.KEY, \"invalid\"), WaterTag.class));\n    }\n\n    @Test\n    public void oneMissingKey()\n    {\n        Assert.assertFalse(Validators.isOfSameType(new TestTaggable(WaterTag.CANAL),\n                new TestTaggable(WaterwayTag.CANAL), WaterTag.class));\n    }\n\n    @Test\n    public void oneOfOne()\n    {\n        Assert.assertTrue(\n                Validators.hasValuesFor(new TestTaggable(NameTag.KEY, \"yes!\"), NameTag.class));\n    }\n\n    @Test\n    public void oneOfOneEnum()\n    {\n        Assert.assertTrue(\n                Validators.hasValuesFor(new TestTaggable(WaterTag.CANAL), WaterTag.class));\n    }\n\n    @Test\n    public void testIsNotOfType()\n    {\n        Assert.assertTrue(Validators.isNotOfType(new TestTaggable(NaturalTag.BEACH),\n                NaturalTag.class, NaturalTag.BAY));\n    }\n\n    @Test\n    public void testIsOfType()\n    {\n        Assert.assertTrue(Validators.isOfType(new TestTaggable(NaturalTag.BEACH), NaturalTag.class,\n                NaturalTag.BEACH));\n    }\n\n    @Test\n    public void testManyOfManyFilter()\n    {\n        final Predicate<Taggable> checkMe = Validators.hasValuesFor(WaterTag.class,\n                NaturalTag.class);\n        Assert.assertTrue(checkMe\n                .test(new TestTaggable(WaterTag.CANAL, NaturalTag.BAY, HighwayTag.BUS_STOP)));\n        Assert.assertFalse(checkMe.test(new TestTaggable(HighwayTag.BUS_STOP, NaturalTag.BAY)));\n    }\n\n    @Test\n    public void testOneOfOneFilter()\n    {\n        final Predicate<Taggable> checkMe = Validators.hasValuesFor(NameTag.class);\n        Assert.assertTrue(checkMe.test(new TestTaggable(NameTag.KEY, \"someName\")));\n        Assert.assertFalse(checkMe.test(new TestTaggable(WaterTag.CANAL)));\n    }\n\n    @Test\n    public void testUnionOfFilters()\n    {\n        final Predicate<Taggable> unionTocheck = Validators.hasValuesFor(WaterTag.class)\n                .and(Validators.hasValuesFor(NaturalTag.class));\n        Assert.assertTrue(unionTocheck\n                .test(new TestTaggable(WaterTag.CANAL, NaturalTag.BAY, HighwayTag.BUS_STOP)));\n        Assert.assertFalse(\n                unionTocheck.test(new TestTaggable(HighwayTag.BUS_STOP, NaturalTag.BAY)));\n    }\n\n    @Test\n    public void threeOfThreeEnums()\n    {\n        Assert.assertTrue(Validators.hasValuesFor(\n                new TestTaggable(WaterTag.CANAL, NaturalTag.WATER, HighwayTag.ESCAPE),\n                NaturalTag.class, HighwayTag.class, WaterTag.class));\n    }\n\n    @Test\n    public void threeOfTwoEnums()\n    {\n        Assert.assertTrue(Validators.hasValuesFor(\n                new TestTaggable(WaterTag.CANAL, NaturalTag.WATER, HighwayTag.ESCAPE),\n                NaturalTag.class, WaterTag.class));\n    }\n\n    @Test\n    public void twoMissingKeys()\n    {\n        Assert.assertFalse(Validators.isOfSameType(new TestTaggable(WaterwayTag.CANAL),\n                new TestTaggable(WaterwayTag.CANAL), WaterTag.class));\n    }\n\n    @Test\n    public void twoNonEnumeratedTagsSameKeyDifferentValues()\n    {\n        Assert.assertFalse(Validators.isOfSameType(new TestTaggable(LastEditTimeTag.KEY, \"01Apr19\"),\n                new TestTaggable(LastEditTimeTag.KEY, \"06June19\"), LastEditTimeTag.class));\n    }\n\n    @Test\n    public void twoNonEnumeratedTagsSameKeySameValue()\n    {\n        Assert.assertTrue(Validators.isOfSameType(new TestTaggable(LastEditTimeTag.KEY, \"06June19\"),\n                new TestTaggable(LastEditTimeTag.KEY, \"06June19\"), LastEditTimeTag.class));\n    }\n\n    @Test\n    public void twoOfThreeEnums()\n    {\n        Assert.assertFalse(\n                Validators.hasValuesFor(new TestTaggable(WaterTag.CANAL, NaturalTag.WATER),\n                        NaturalTag.class, HighwayTag.class, WaterTag.class));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/annotations/validation/ValidatorsTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.annotations.validation;\n\nimport java.util.EnumMap;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.tags.annotations.Tag;\nimport org.openstreetmap.atlas.tags.annotations.Tag.Validation;\nimport org.openstreetmap.atlas.tags.annotations.TagKey;\n\n/**\n * Comprehensive test case for checking the code coverage within the Validators class\n *\n * @author cstaylor\n */\npublic class ValidatorsTestCase\n{\n    /**\n     * Sample tag with double validation\n     *\n     * @author cstaylor\n     */\n    @Tag(Validation.DOUBLE)\n    public interface ValidatorsTestTag\n    {\n        @TagKey\n        String KEY = \"abc\";\n    }\n\n    /**\n     * Illegal validator implementation because it has a private constructor\n     *\n     * @author cstaylor\n     */\n    private static final class IllegalValidator implements TagValidator\n    {\n        private IllegalValidator()\n        {\n\n        }\n\n        @Override\n        public boolean isValid(final String value)\n        {\n            return true;\n        }\n\n    }\n\n    /**\n     * Validators implementation assigning the illegal double validator listed above so we can test\n     * one of the exception code paths\n     *\n     * @author cstaylor\n     */\n    private static final class TestingIllegalValidators extends Validators\n    {\n        TestingIllegalValidators(final Class<?> packageRepresentative)\n        {\n            super(packageRepresentative);\n        }\n\n        @Override\n        protected void fillValidatorTypes(\n                final EnumMap<Validation, Class<? extends TagValidator>> validatorTypes)\n        {\n            super.fillValidatorTypes(validatorTypes);\n            validatorTypes.put(Validation.DOUBLE, IllegalValidator.class);\n        }\n    }\n\n    /**\n     * Validators implementation that removes the DOUBLE validator so we can test the missing\n     * validator code path within Validators\n     *\n     * @author cstaylor\n     */\n    private static final class TestingNullValidators extends Validators\n    {\n        TestingNullValidators(final Class<?> packageRepresentative)\n        {\n            super(packageRepresentative);\n        }\n\n        @Override\n        protected void fillValidatorTypes(\n                final EnumMap<Validation, Class<? extends TagValidator>> validatorTypes)\n        {\n            super.fillValidatorTypes(validatorTypes);\n            validatorTypes.remove(Validation.DOUBLE);\n        }\n    }\n\n    @Rule\n    public final ExpectedException expected = ExpectedException.none();\n\n    @Test\n    public void illegalValidator()\n    {\n        this.expected.expect(IllegalArgumentException.class);\n        new TestingIllegalValidators(ValidatorsTestCase.class);\n    }\n\n    @Test\n    public void nullValidator()\n    {\n        this.expected.expect(IllegalArgumentException.class);\n        new TestingNullValidators(ValidatorsTestCase.class);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/cache/TaggerTestCase.java",
    "content": "package org.openstreetmap.atlas.tags.cache;\n\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.oneway.OneWayTag;\n\n/**\n * Tagger tests.\n *\n * @author gpogulsky\n */\npublic class TaggerTestCase\n{\n    @Rule\n    public final TaggerTestRule rule = new TaggerTestRule();\n\n    @Test\n    public void testTag()\n    {\n        final Tagger<HighwayTag> highwayTagger = new Tagger<>(HighwayTag.class);\n        final Tagger<OneWayTag> onewayTagger = new Tagger<>(OneWayTag.class);\n\n        for (final Edge edge : this.rule.getAtlas().edges())\n        {\n            final Optional<HighwayTag> tag = highwayTagger.getTag(edge);\n            Assert.assertTrue(tag.isPresent());\n            Assert.assertEquals(HighwayTag.SECONDARY, tag.get());\n\n            final Optional<OneWayTag> otag = onewayTagger.getTag(edge);\n            if (edge.getIdentifier() == 12000000)\n            {\n                Assert.assertFalse(otag.isPresent());\n            }\n            else\n            {\n                Assert.assertTrue(otag.isPresent());\n                Assert.assertEquals(OneWayTag.YES, otag.get());\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/cache/TaggerTestRule.java",
    "content": "package org.openstreetmap.atlas.tags.cache;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * Test atlas for Tagger\n * \n * @author gpogulsky\n */\npublic class TaggerTestRule extends CoreTestRule\n{\n    private static final String ONE = \"34.1444317, -118.7909766\";\n    private static final String TWO_CONNECTED = \"34.144713, -118.7924548\";\n    private static final String THREE = \"34.1448283, -118.7937022\";\n    private static final String FOUR = \"34.1447281, -118.7936994\";\n\n    @TestAtlas(\n            // nodes\n            nodes = {\n\n                    @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO_CONNECTED)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR))\n\n            },\n            // edges\n            edges = {\n                    @Edge(id = \"12000000\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO_CONNECTED) }, tags = { \"highway=secondary\",\n                                    \"name=Agoura Road\" }),\n                    @Edge(id = \"23000000\", coordinates = { @Loc(value = TWO_CONNECTED),\n                            @Loc(value = THREE) }, tags = { \"highway=secondary\", \"name=Agoura Road\",\n                                    \"oneway=yes\" }),\n                    @Edge(id = \"24000000\", coordinates = { @Loc(value = FOUR),\n                            @Loc(value = TWO_CONNECTED) }, tags = { \"highway=secondary\",\n                                    \"name=Agoura Road\", \"oneway=yes\" })\n\n            })\n\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/ConfiguredTaggableFilterTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.configuration.Configuration;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\n\n/**\n * @author matthieun\n * @author mgostintsev\n */\npublic class ConfiguredTaggableFilterTest\n{\n    private ConfiguredTaggableFilter filter;\n\n    @Before\n    public void prepare()\n    {\n        final Resource resource = new InputStreamResource(() -> ConfiguredTaggableFilterTest.class\n                .getResourceAsStream(\"test-filtering.json\"));\n        final Configuration configuration = new StandardConfiguration(resource);\n        this.filter = new ConfiguredTaggableFilter(configuration);\n    }\n\n    @Test\n    public void testConfigured()\n    {\n        final Taggable valid1 = Taggable.with(\"barrier\", \"yes\");\n        final Taggable valid2 = Taggable.with(\"highway\", \"motorway_junction\");\n        final Taggable valid3 = Taggable.with(\"railway\", \"level_crossing\", \"public_transport\",\n                \"platform\");\n        final Taggable valid4 = Taggable.with(\"barrier\", \"yes\", \"oneway\", \"yes\");\n\n        final Taggable invalid1 = Taggable.with(\"barrier\", \"yes\", \"noexit\", \"yes\");\n        final Taggable invalid2 = Taggable.with(\"highway\", \"motorway_junction\", \"noexit\", \"yes\");\n        final Taggable invalid3 = Taggable.with(\"highway\", \"pedestrian\");\n        final Taggable invalid4 = Taggable.with();\n        final Taggable invalid5 = Taggable.with(\"barrier\", \"yes\", \"oneway\", \"reversed\");\n\n        Assert.assertTrue(this.filter.test(valid1));\n        Assert.assertTrue(this.filter.test(valid2));\n        Assert.assertTrue(this.filter.test(valid3));\n        Assert.assertTrue(this.filter.test(valid4));\n\n        Assert.assertFalse(this.filter.test(invalid1));\n        Assert.assertFalse(this.filter.test(invalid2));\n        Assert.assertFalse(this.filter.test(invalid3));\n        Assert.assertFalse(this.filter.test(invalid4));\n        Assert.assertFalse(this.filter.test(invalid5));\n    }\n\n    @Test\n    public void testDefaultOsmosisWayConfiguration()\n    {\n        final Resource wayResource = new InputStreamResource(\n                () -> getClass().getClassLoader().getResourceAsStream(\n                        \"org/openstreetmap/atlas/geography/atlas/pbf/osm-pbf-way.json\"));\n        final Configuration wayConfiguration = new StandardConfiguration(wayResource);\n        final ConfiguredTaggableFilter wayFilter = new ConfiguredTaggableFilter(wayConfiguration);\n\n        final Taggable boundary = Taggable.with(\"boundary\", \"administrative\");\n        final Taggable hirez = Taggable.with(\"hires\", \"yes\");\n\n        Assert.assertTrue(wayFilter.test(boundary));\n        Assert.assertFalse(wayFilter.test(hirez));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/RegexTaggableFilterTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.Taggable;\n\n/**\n * @author mm-ciub on 14/09/2020.\n */\npublic class RegexTaggableFilterTest\n{\n    @Test\n    public void testCreationFromString()\n    {\n        final String definition = \"source,highway|secondary,illegal\";\n        final RegexTaggableFilter filter = new RegexTaggableFilter(definition);\n        Assert.assertFalse(filter.test(Taggable.with()));\n        Assert.assertTrue(\n                filter.test(Taggable.with(\"source\", \"local knowledge\", \"highway\", \"secondary\")));\n        Assert.assertTrue(filter.test(Taggable.with(\"source\", \"illegal\", \"highway\", \"primary\")));\n    }\n\n    @Test\n    public void testException()\n    {\n        final Set<String> tagNames = new HashSet<>(Arrays.asList(\"source\", \"highway\"));\n        final Set<String> regex = new HashSet<>(\n                Arrays.asList(\".*(?i)\\\\bmap\\\\b.*\", \".*(?i)\\\\bsecondary\\\\b.*\"));\n        final HashMap<String, Set<String>> exceptions = new HashMap<>(\n                Map.of(\"source\", Set.of(\"personal map\", \"public map\")));\n        final RegexTaggableFilter filter = new RegexTaggableFilter(tagNames, regex, exceptions);\n\n        Assert.assertFalse(filter.test(Taggable.with()));\n        Assert.assertFalse(filter.test(Taggable.with(\"highway\", \"primary\")));\n        Assert.assertTrue(\n                filter.test(Taggable.with(\"source\", \"local knowledge\", \"highway\", \"secondary\")));\n        Assert.assertTrue(\n                filter.test(Taggable.with(\"source\", \"illegal map\", \"highway\", \"secondary\")));\n        Assert.assertFalse(\n                filter.test(Taggable.with(\"source\", \"public map\", \"highway\", \"primary\")));\n\n    }\n\n    @Test\n    public void testMultipleOccurrence()\n    {\n        final Set<String> tagNames = new HashSet<>(Arrays.asList(\"source\", \"highway\"));\n        final Set<String> regex = new HashSet<>(\n                Arrays.asList(\".*(?i)\\\\bmap\\\\b.*\", \".*(?i)\\\\bsecondary\\\\b.*\"));\n        final RegexTaggableFilter filter = new RegexTaggableFilter(tagNames, regex, null);\n\n        Assert.assertFalse(filter.test(Taggable.with()));\n        Assert.assertFalse(filter.test(Taggable.with(\"highway\", \"primary\")));\n        Assert.assertTrue(\n                filter.test(Taggable.with(\"source\", \"local knowledge\", \"highway\", \"secondary\")));\n        Assert.assertTrue(\n                filter.test(Taggable.with(\"source\", \"illegal map\", \"highway\", \"secondary\")));\n    }\n\n    @Test\n    public void testReturnedTags()\n    {\n        final Set<String> tagNames = new HashSet<>(Arrays.asList(\"source\", \"highway\"));\n        final Set<String> regex = new HashSet<>(\n                Arrays.asList(\".*(?i)\\\\bmap\\\\b.*\", \".*(?i)\\\\bsecondary\\\\b.*\"));\n        final HashMap<String, Set<String>> exceptions = new HashMap<>(\n                Map.of(\"source\", Set.of(\"personal map\", \"public map\")));\n        final RegexTaggableFilter filter = new RegexTaggableFilter(tagNames, regex, exceptions);\n        final Taggable taggable = Taggable.with(\"source\", \"illegal map\", \"highway\", \"secondary\");\n\n        Assert.assertTrue(filter.test(taggable));\n        Assert.assertEquals(\"source,highway\", filter.getMatchedTags(taggable));\n    }\n\n    @Test\n    public void testSimpleCase()\n    {\n        final Set<String> tagNames = new HashSet<>(Collections.singletonList(\"source\"));\n        final Set<String> regex = new HashSet<>(Collections.singletonList(\".*(?i)\\\\bmap\\\\b.*\"));\n        final RegexTaggableFilter filter = new RegexTaggableFilter(tagNames, regex, null);\n\n        Assert.assertFalse(filter.test(Taggable.with()));\n        Assert.assertFalse(filter.test(Taggable.with(\"highway\", \"primary\")));\n        Assert.assertFalse(filter.test(Taggable.with(\"source\", \"local knowledge\")));\n        Assert.assertTrue(filter.test(Taggable.with(\"source\", \"illegal map\")));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/TaggableFilterTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.testing.FreezeDryFunction;\n\n/**\n * @author matthieun\n */\npublic class TaggableFilterTest\n{\n    @Test\n    public void testAllValidComplex()\n    {\n        // In this case the OR(|) is ignored here.\n        final String definition = \"bus->*|\";\n        final TaggableFilter filter = TaggableFilter.forDefinition(definition);\n\n        Assert.assertFalse(filter.test(Taggable.with()));\n        Assert.assertFalse(filter.test(Taggable.with(\"highway\", \"primary\")));\n        Assert.assertTrue(filter.test(Taggable.with(\"bus\", \"lane\")));\n    }\n\n    @Test\n    public void testAllValidSimple()\n    {\n        final String definition = \"\";\n        final TaggableFilter filter = TaggableFilter.forDefinition(definition);\n\n        Assert.assertTrue(filter.test(Taggable.with()));\n        Assert.assertTrue(filter.test(Taggable.with(\"highway\", \"primary\")));\n        Assert.assertTrue(filter.test(Taggable.with(\"bus\", \"lane\")));\n    }\n\n    @Test\n    public void testBackwardsCompatibility()\n    {\n        final String definition = \"bus->*|water->*&bus->*^water->!canal\";\n        final TaggableFilter filter = TaggableFilter.forDefinition(definition);\n\n        Assert.assertTrue(filter.test(Taggable.with(\"water\", \"pond\")));\n        Assert.assertFalse(filter.test(Taggable.with(\"water\", \"canal\")));\n        Assert.assertTrue(filter.test(Taggable.with(\"water\", \"canal\", \"bus\", \"stop\")));\n\n        Assert.assertEquals(definition.replace(\"^\", \"||\"), filter.toString());\n    }\n\n    @Test\n    public void testDepth()\n    {\n        final String definition = \"bus->*|water->*&bus->*||water->!canal&&bus->!|||water->pond,canal&&&bus->*\";\n        final TaggableFilter filter = TaggableFilter.forDefinition(definition);\n\n        Assert.assertTrue(filter.test(Taggable.with(\"water\", \"pond\")));\n        Assert.assertFalse(filter.test(Taggable.with(\"water\", \"canal\")));\n        Assert.assertTrue(filter.test(Taggable.with(\"water\", \"canal\", \"bus\", \"stop\")));\n\n        Assert.assertEquals(definition, filter.toString());\n    }\n\n    @Test\n    public void testDoubleNegative()\n    {\n        final String definition = \"water->!pond,!lake\";\n        final TaggableFilter filter = TaggableFilter.forDefinition(definition);\n\n        Assert.assertTrue(filter.test(Taggable.with()));\n        // Pond, but not lake -> true\n        Assert.assertTrue(filter.test(Taggable.with(\"water\", \"pond\")));\n        // Lake, but not pond -> true\n        Assert.assertTrue(filter.test(Taggable.with(\"water\", \"lake\")));\n        // Not ponf, not lake. -> true\n        Assert.assertTrue(filter.test(Taggable.with(\"water\", \"sea\")));\n    }\n\n    @Test\n    public void testParsing()\n    {\n        // amenity=bus_station OR highway=bus_stop OR ( (bus=* OR trolleybus=*) AND\n        // public_transport=[stop_position OR platform OR station] )\n        final String definition = \"amenity->bus_station|highway->BUS_STOP|bus->*||trolleybus->*&public_transport->stop_position,platform,station\";\n        final TaggableFilter filter = TaggableFilter.forDefinition(definition);\n\n        final Taggable valid1 = Taggable.with(\"amenity\", \"bus_station\");\n        final Taggable valid2 = Taggable.with(\"highway\", \"bus_stop\");\n        final Taggable valid3 = Taggable.with(\"trolleybus\", \"yes\", \"public_transport\", \"platform\");\n        final Taggable valid4 = Taggable.with(\"bus\", \"hello\", \"public_transport\", \"station\");\n        final Taggable valid5 = Taggable.with(\"trolleybus\", \"bye\", \"public_transport\",\n                \"stop_position\");\n        final Taggable valid6 = Taggable.with(\"amenity\", \"bus_stop\", \"trolleybus\", \"bye\",\n                \"public_transport\", \"stop_position\");\n\n        final Taggable invalid1 = Taggable.with(\"amenity\", \"bus_stop\");\n        final Taggable invalid2 = Taggable.with(\"highway\", \"bus_station\");\n        final Taggable invalid3 = Taggable.with(\"trolleybus\", \"yes\");\n        final Taggable invalid4 = Taggable.with(\"bus\", \"hello\", \"public_transport\", \"terminal\");\n        final Taggable invalid5 = Taggable.with();\n\n        Assert.assertTrue(filter.test(valid1));\n        Assert.assertTrue(filter.test(valid2));\n        Assert.assertTrue(filter.test(valid3));\n        Assert.assertTrue(filter.test(valid4));\n        Assert.assertTrue(filter.test(valid5));\n        Assert.assertTrue(filter.test(valid6));\n\n        Assert.assertFalse(filter.test(invalid1));\n        Assert.assertFalse(filter.test(invalid2));\n        Assert.assertFalse(filter.test(invalid3));\n        Assert.assertFalse(filter.test(invalid4));\n        Assert.assertFalse(filter.test(invalid5));\n    }\n\n    @Test\n    public void testSerialization()\n    {\n        final String definition1 = \"amenity->bus_station|highway->BUS_STOP|bus->*||trolleybus->*&public_transport->stop_position,platform,station\";\n        final TaggableFilter filter1 = TaggableFilter.forDefinition(definition1);\n\n        final String definition2 = \"\";\n        final TaggableFilter filter2 = TaggableFilter.forDefinition(definition2);\n\n        final TaggableFilter filter12 = (TaggableFilter) new FreezeDryFunction<>().apply(filter1);\n        Assert.assertEquals(filter1.getDefinition(), filter12.getDefinition());\n\n        final TaggableFilter filter22 = (TaggableFilter) new FreezeDryFunction<>().apply(filter2);\n        Assert.assertEquals(filter2.getDefinition(), filter22.getDefinition());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/TaggableFilterToMatcherConverterTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.matcher.TaggableMatcher;\n\n/**\n * @author lcram\n */\npublic class TaggableFilterToMatcherConverterTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void basicTest()\n    {\n        final TaggableFilterToMatcherConverter converter = new TaggableFilterToMatcherConverter();\n        final TaggableFilter filter = TaggableFilter.forDefinition(\n                \"highway->service&cycleway->lane||cycleway:lane->*&&cycleway->!|highway->!service\");\n        final TaggableMatcher equivalentMatcher = converter.convert(filter);\n\n        Assert.assertEquals(\n                \"(highway=service & (cycleway=lane | (cycleway:lane & !cycleway))) | (highway!=service | !highway)\",\n                equivalentMatcher.getDefinition());\n\n        final Taggable taggable1 = Taggable.with(\"highway\", \"primary\");\n        final Taggable taggable2 = Taggable.with(\"highway\", \"service\", \"cycleway\", \"lane\");\n        final Taggable taggable3 = Taggable.with(\"highway\", \"service\", \"cycleway\", \"lane\",\n                \"cycleway:lane\", \"left\");\n        final Taggable taggable4 = Taggable.with(\"highway\", \"service\", \"cycleway:lane\", \"left\");\n        final Taggable taggable5 = Taggable.with(\"cycleway\", \"lane\");\n        final Taggable taggable6 = Taggable.with(\"natural\", \"lake\");\n\n        Assert.assertEquals(filter.test(taggable1), equivalentMatcher.test(taggable1));\n        Assert.assertEquals(filter.test(taggable2), equivalentMatcher.test(taggable2));\n        Assert.assertEquals(filter.test(taggable3), equivalentMatcher.test(taggable3));\n        Assert.assertEquals(filter.test(taggable4), equivalentMatcher.test(taggable4));\n        Assert.assertEquals(filter.test(taggable5), equivalentMatcher.test(taggable5));\n        Assert.assertEquals(filter.test(taggable6), equivalentMatcher.test(taggable6));\n    }\n\n    @Test\n    public void parenthesesTest()\n    {\n        final TaggableFilterToMatcherConverter converter = new TaggableFilterToMatcherConverter();\n        final TaggableFilter filter = TaggableFilter.forDefinition(\"foo->bar&baz->bat\");\n        final TaggableMatcher equivalentMatcher = converter.convert(filter);\n        Assert.assertEquals(\"(foo=bar & baz=bat)\", equivalentMatcher.getDefinition());\n    }\n\n    @Test\n    public void regexTest()\n    {\n        final TaggableFilterToMatcherConverter converter = new TaggableFilterToMatcherConverter();\n        final TaggableFilter filter = TaggableFilter.forDefinition(\"foo->bar*|baz->*bat\");\n        final TaggableMatcher equivalentMatcher = converter.convert(filter);\n\n        Assert.assertEquals(\"foo=/bar.*/ | baz=/.*bat/\", equivalentMatcher.getDefinition());\n\n        final Taggable taggable1 = Taggable.with(\"foo\", \"bar2\");\n        final Taggable taggable2 = Taggable.with(\"foo\", \"2bar\", \"baz\", \"222bat\");\n        final Taggable taggable3 = Taggable.with(\"foo\", \"2bar\", \"baz\", \"bat2\");\n\n        Assert.assertEquals(filter.test(taggable1), equivalentMatcher.test(taggable1));\n        Assert.assertEquals(filter.test(taggable2), equivalentMatcher.test(taggable2));\n        Assert.assertEquals(filter.test(taggable3), equivalentMatcher.test(taggable3));\n    }\n\n    @Test\n    public void testBangFailure()\n    {\n        final TaggableFilterToMatcherConverter converter = new TaggableFilterToMatcherConverter();\n        final TaggableFilter filter = TaggableFilter.forDefinition(\"highway->primary,!,secondary\");\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"Cannot transpile `highway->primary,!,secondary' since composite value `primary,!,secondary' contains a lone `!' operator.\");\n        converter.convert(filter);\n    }\n\n    @Test\n    public void testMultipleValues()\n    {\n        final TaggableFilterToMatcherConverter converter = new TaggableFilterToMatcherConverter();\n        final TaggableFilter filter = TaggableFilter\n                .forDefinition(\"highway->primary,!service,secondary\");\n        final TaggableMatcher equivalentMatcher = converter.convert(filter);\n\n        Assert.assertEquals(\"highway=(primary | !service | secondary)\",\n                equivalentMatcher.getDefinition());\n\n        final Taggable taggable1 = Taggable.with(\"highway\", \"primary\");\n        final Taggable taggable2 = Taggable.with(\"highway\", \"service\");\n        final Taggable taggable3 = Taggable.with(\"natural\", \"lake\");\n\n        Assert.assertEquals(filter.test(taggable1), equivalentMatcher.test(taggable1));\n        Assert.assertEquals(filter.test(taggable2), equivalentMatcher.test(taggable2));\n        Assert.assertEquals(filter.test(taggable3), equivalentMatcher.test(taggable3));\n    }\n\n    @Test\n    public void testRegexFailure1()\n    {\n        final TaggableFilterToMatcherConverter converter = new TaggableFilterToMatcherConverter();\n        final TaggableFilter filter = TaggableFilter.forDefinition(\"highway->*(primary)\");\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"Cannot transpile `highway->*(primary)' since new value `(primary)' contains a regex control character.\");\n        converter.convert(filter);\n    }\n\n    @Test\n    public void testRegexFailure2()\n    {\n        final TaggableFilterToMatcherConverter converter = new TaggableFilterToMatcherConverter();\n        final TaggableFilter filter = TaggableFilter.forDefinition(\"highway->(primary)*\");\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"Cannot transpile `highway->(primary)*' since new value `(primary)' contains a regex control character.\");\n        converter.convert(filter);\n    }\n\n    @Test\n    public void testStarFailure()\n    {\n        final TaggableFilterToMatcherConverter converter = new TaggableFilterToMatcherConverter();\n        final TaggableFilter filter = TaggableFilter.forDefinition(\"highway->primary,*,secondary\");\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"Cannot transpile `highway->primary,*,secondary' since composite value `primary,*,secondary' contains a lone `*' operator.\");\n        converter.convert(filter);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/matcher/ConfiguredTaggableMatcherTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.utilities.configuration.Configuration;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\n\n/**\n * @author lcram\n */\npublic class ConfiguredTaggableMatcherTest\n{\n    private ConfiguredTaggableMatcher matcher;\n\n    @Before\n    public void prepare()\n    {\n        final Resource resource = new InputStreamResource(() -> ConfiguredTaggableMatcherTest.class\n                .getResourceAsStream(\"test-matching.json\"));\n        final Configuration configuration = new StandardConfiguration(resource);\n        this.matcher = new ConfiguredTaggableMatcher(configuration);\n    }\n\n    @Test\n    public void test()\n    {\n        final Taggable valid1 = Taggable.with(\"foo\", \"bar\", \"hello\", \"world2\");\n        final Taggable valid2 = Taggable.with(\"foo\", \"bar1\", \"baz\", \"bat\", \"hello\", \"world2\");\n\n        final Taggable invalid1 = Taggable.with(\"barrier\", \"yes\", \"noexit\", \"yes\");\n        final Taggable invalid2 = Taggable.with(\"foo\", \"bar\", \"baz\", \"bat\");\n\n        Assert.assertTrue(this.matcher.test(valid1));\n        Assert.assertTrue(this.matcher.test(valid2));\n\n        Assert.assertFalse(this.matcher.test(invalid1));\n        Assert.assertFalse(this.matcher.test(invalid2));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/matcher/TaggableMatcherTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.filters.TaggableFilter;\n\n/**\n * @author lcram\n */\npublic class TaggableMatcherTest\n{\n    @Test\n    public void basicTests()\n    {\n        final Taggable taggable1 = Taggable.with(\"foo\", \"bar\", \"baz\", \"bat\");\n        Assert.assertTrue(TaggableMatcher.from(\"!(foo=bar ^ baz=bat)\").test(taggable1));\n        Assert.assertFalse(TaggableMatcher.from(\"foo=bar ^ baz=bat\").test(taggable1));\n        Assert.assertTrue(TaggableMatcher.from(\"foo = bar | baz = bat\").test(taggable1));\n        Assert.assertFalse(TaggableMatcher.from(\"!(foo=bar | baz=bat)\").test(taggable1));\n        Assert.assertTrue(TaggableMatcher.from(\"foo=bar & baz=bat\").test(taggable1));\n        Assert.assertFalse(TaggableMatcher.from(\"!(foo=bar & baz=bat)\").test(taggable1));\n        Assert.assertFalse(TaggableMatcher.from(\"foo=bar & baz=\\\" bat\\\"\").test(taggable1));\n        Assert.assertTrue(TaggableMatcher.from(\"foo=bar & baz!=\\\" bat\\\"\").test(taggable1));\n    }\n\n    @Test\n    public void basicTests2()\n    {\n        final Taggable taggable1 = Taggable.with(\"foo\", \"bar\", \"baz\", \"bat\");\n        Assert.assertTrue(TaggableMatcher.from(\"foo=/b.*/ & baz=/b.*/\").test(taggable1));\n        Assert.assertTrue(TaggableMatcher.from(\"foo = bar & !mat\").test(taggable1));\n        Assert.assertFalse(TaggableMatcher.from(\"foo=bar & mat!=hat\").test(taggable1));\n        Assert.assertTrue(TaggableMatcher.from(\"foo=bar & baz!=hat\").test(taggable1));\n        Assert.assertTrue(TaggableMatcher.from(\"foo=bar & baz=!hat\").test(taggable1));\n        Assert.assertEquals(TaggableMatcher.from(\"baz = (!hat & !cat)\").test(taggable1),\n                TaggableMatcher.from(\"baz = !(hat | cat)\").test(taggable1));\n        Assert.assertTrue(TaggableMatcher.from(\"  foo    =bar & baz= \\\"bat\\\"  \").test(taggable1));\n    }\n\n    @Test\n    public void complexTests()\n    {\n        /*\n         * Test a complex expression filter.\n         */\n        final Taggable taggable = Taggable.with(\"name\", \"Main Street\", \"highway\", \"secondary\",\n                \"restricted\", \"no\");\n        Assert.assertTrue(TaggableMatcher.from(\n                \"name=/^.*(s|S)treet$/ & highway!=primary & (!restricted | restricted != (yes | sometimes))\")\n                .test(taggable));\n\n        /*\n         * Test matching against variable keys.\n         */\n        final Taggable oldStyleLakes = Taggable.with(\"natural\", \"lake\");\n        final Taggable newStyleLakes = Taggable.with(\"natural\", \"water\", \"water\", \"lake\");\n        /*\n         * These filters should match both old style and new style lake tagging\n         */\n        final TaggableMatcher lakeMatcher = TaggableMatcher.from(\"(natural | water)=lake\");\n        Assert.assertTrue(lakeMatcher.test(oldStyleLakes));\n        Assert.assertTrue(lakeMatcher.test(newStyleLakes));\n    }\n\n    @Test\n    public void testEmptyMatcher()\n    {\n        final Taggable taggable1 = Taggable.with(\"name\", \"hello\");\n\n        final TaggableMatcher emptyMatcher = TaggableMatcher.from(\"\");\n        Assert.assertEquals(0L, emptyMatcher.lengthOfLongestLineForPrintedTree());\n        Assert.assertEquals(\"\", emptyMatcher.prettyPrintTree());\n        Assert.assertTrue(emptyMatcher.test(taggable1));\n    }\n\n    @Test\n    public void testFilterVsMatcher()\n    {\n        final Taggable primaryHighway = Taggable.with(\"highway\", \"primary\", \"name\", \"280\");\n        final Taggable secondaryHighway = Taggable.with(\"highway\", \"secondary\", \"name\", \"De Anza\");\n        final Taggable someLake = Taggable.with(\"water\", \"lake\", \"name\", \"Loch Ness\");\n\n        final TaggableMatcher allFeaturesNotPrimaryHighway = TaggableMatcher\n                .from(\"highway != primary | !highway\");\n        final TaggableFilter allFeaturesNotPrimaryHighwayFilter = TaggableFilter\n                .forDefinition(\"highway->!primary\");\n\n        final TaggableMatcher allHighwaysNotPrimaryHighway = TaggableMatcher\n                .from(\"highway != primary\");\n        final TaggableFilter allHighwaysNotPrimaryHighwayFilter = TaggableFilter\n                .forDefinition(\"highway->!primary&highway->*\");\n\n        Assert.assertFalse(allFeaturesNotPrimaryHighway.test(primaryHighway));\n        Assert.assertTrue(allFeaturesNotPrimaryHighway.test(secondaryHighway));\n        Assert.assertTrue(allFeaturesNotPrimaryHighway.test(someLake));\n\n        Assert.assertFalse(allFeaturesNotPrimaryHighwayFilter.test(primaryHighway));\n        Assert.assertTrue(allFeaturesNotPrimaryHighwayFilter.test(secondaryHighway));\n        Assert.assertTrue(allFeaturesNotPrimaryHighwayFilter.test(someLake));\n\n        Assert.assertFalse(allHighwaysNotPrimaryHighway.test(primaryHighway));\n        Assert.assertTrue(allHighwaysNotPrimaryHighway.test(secondaryHighway));\n        Assert.assertFalse(allHighwaysNotPrimaryHighway.test(someLake));\n\n        Assert.assertFalse(allHighwaysNotPrimaryHighwayFilter.test(primaryHighway));\n        Assert.assertTrue(allHighwaysNotPrimaryHighwayFilter.test(secondaryHighway));\n        Assert.assertFalse(allHighwaysNotPrimaryHighwayFilter.test(someLake));\n    }\n\n    @Test\n    public void testPrettyPrintTree()\n    {\n        Assert.assertEquals(\"        =       \\n\" + \"    ┌───┴───┐   \\n\" + \"   foo     bar  \\n\",\n                TaggableMatcher.from(\"foo = bar\").prettyPrintTree());\n    }\n\n    @Test\n    public void testPrettyTreeLength()\n    {\n        Assert.assertEquals(16L,\n                TaggableMatcher.from(\"foo = bar\").lengthOfLongestLineForPrintedTree());\n        Assert.assertEquals(32L,\n                TaggableMatcher.from(\"foo = bar | baz = bat\").lengthOfLongestLineForPrintedTree());\n    }\n\n    @Test\n    public void testQuotes()\n    {\n        final Taggable taggable1 = Taggable.with(\"name\", \"John's \\\"Coffee\\\" Shop\");\n        Assert.assertTrue(\n                TaggableMatcher.from(\"name = 'John\\\\'s \\\"Coffee\\\" Shop'\").test(taggable1));\n        Assert.assertTrue(\n                TaggableMatcher.from(\"name = \\\"John's \\\\\\\"Coffee\\\\\\\" Shop\\\"\").test(taggable1));\n    }\n\n    @Test\n    public void testToString()\n    {\n        Assert.assertEquals(\"TaggableMatcher(foo = bar)\",\n                TaggableMatcher.from(\"foo = bar\").toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/LexerTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author lcram\n */\npublic class LexerTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testFailEscape()\n    {\n        final Lexer lexer = new Lexer();\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"syntax error: unexpected EOF after '\\\\'\\n\" + \"foo=bar\\\\\\n\" + \"~~~~~~~~^\");\n        lexer.lex(\"foo=bar\\\\\");\n    }\n\n    @Test\n    public void testFailMixedQuotes()\n    {\n        final Lexer lexer = new Lexer();\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"syntax error: unexpected EOF after `\\\"'\\n\" + \"foo=\\\"bar'\\n\" + \"~~~~~~~~~^\");\n        lexer.lex(\"foo=\\\"bar'\");\n    }\n\n    @Test\n    public void testFailQuote()\n    {\n        final Lexer lexer = new Lexer();\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"syntax error: unexpected EOF after `\\\"'\\n\"\n                + \"foo=\\\"bar\\\"baz\\\"\\n\" + \"~~~~~~~~~~~~~^\");\n        lexer.lex(\"foo=\\\"bar\\\"baz\\\"\");\n    }\n\n    @Test\n    public void testFailRegex()\n    {\n        final Lexer lexer = new Lexer();\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"syntax error: unexpected EOF after '/'\\n\" + \"foo=/bar\\n\" + \"~~~~~~~~^\");\n        lexer.lex(\"foo=/bar\");\n    }\n\n    @Test\n    public void testQuotes()\n    {\n        final Lexer lexer = new Lexer();\n\n        Assert.assertEquals(\n                \"(LITERAL, foo), (EQUAL, =), (LITERAL, \\\"bar\\\"), (AND, &), (LITERAL, baz), (EQUAL, =), (LITERAL, 'bat'), \",\n                Lexer.debugString(lexer.lex(\"foo='\\\"bar\\\"'&baz=\\\"'bat'\\\"\")));\n    }\n\n    @Test\n    public void testSuccessfulLex()\n    {\n        final Lexer lexer = new Lexer();\n\n        Assert.assertEquals(\n                \"(PAREN_OPEN, (), (LITERAL, foo), (EQUAL, =), (LITERAL, bar), (OR, |), (LITERAL, baz), (EQUAL, =), (LITERAL, bat), (PAREN_CLOSE, )), \",\n                Lexer.debugString(lexer.lex(\"(foo=bar|baz=bat)\")));\n\n        Assert.assertEquals(\n                \"(LITERAL, foo), (EQUAL, =), (LITERAL, bar), (XOR, ^), (LITERAL, baz), (EQUAL, =), (LITERAL, bat), \",\n                Lexer.debugString(lexer.lex(\"foo=bar^baz=bat\")));\n\n        /*\n         * This one has a syntax error that would manifest at the parse step (\"world\" literal\n         * followed by \"foo\" literal). It should lex OK.\n         */\n        Assert.assertEquals(\n                \"(LITERAL, hello), (EQUAL, =), (LITERAL, world), (LITERAL, foo), (OR, |), (LITERAL, a), (EQUAL, =), (BANG, !), (LITERAL, b), \",\n                Lexer.debugString(lexer.lex(\"hello=world foo|a=!b\")));\n\n        Assert.assertEquals(\"(LITERAL, a), (EQUAL, =), (LITERAL, b=c), \",\n                Lexer.debugString(lexer.lex(\"a=b\\\\=c\")));\n\n        Assert.assertEquals(\"(LITERAL, a), (EQUAL, =), (LITERAL, =b), \",\n                Lexer.debugString(lexer.lex(\"a=\\\\=b\")));\n\n        Assert.assertEquals(\"(LITERAL, message), (EQUAL, =), (LITERAL, Hello World), \",\n                Lexer.debugString(lexer.lex(\"message=Hello\\\\ World\")));\n\n        Assert.assertEquals(\n                \"(LITERAL, foo), (BANG_EQUAL, !=), (LITERAL, bar), (AND, &), (BANG, !), (LITERAL, baz), \",\n                Lexer.debugString(lexer.lex(\"foo!=bar&!baz\")));\n\n        Assert.assertEquals(\"(LITERAL, foo), (EQUAL, =), (REGEX, bar), \",\n                Lexer.debugString(lexer.lex(\"foo=/bar/\")));\n\n        Assert.assertEquals(\"(LITERAL, math), (EQUAL, =), (LITERAL, 2+2=4), \",\n                Lexer.debugString(lexer.lex(\"math=\\\"2+2=4\\\"\")));\n\n        Assert.assertEquals(\"(LITERAL, foo), (EQUAL, =), (REGEX, bar\\\\/baz\\\\.), \",\n                Lexer.debugString(lexer.lex(\"foo=/bar\\\\/baz\\\\./\")));\n\n        Assert.assertEquals(\"(LITERAL, foo), (EQUAL, =), (LITERAL, bar \\\"baz   bat), \",\n                Lexer.debugString(lexer.lex(\"foo=\\\"bar \\\\\\\"baz   bat\\\"\")));\n\n        Assert.assertEquals(\n                \"(LITERAL, foo), (EQUAL, =), (LITERAL, bar), (AND, &), (PAREN_OPEN, (), (LITERAL, a), (EQUAL, =), (LITERAL, b), (OR, |), (LITERAL, c), (EQUAL, =), (LITERAL, d), (PAREN_CLOSE, )), \",\n                Lexer.debugString(lexer.lex(\"    foo = bar & (   a=b | c = d)  \")));\n\n        Assert.assertEquals(\"(LITERAL, foo), (EQUAL, =), (LITERAL, ), \",\n                Lexer.debugString(lexer.lex(\"foo=\\\"\\\"\")));\n    }\n\n    @Test\n    public void testTokenEquals()\n    {\n        final Token token1 = new Token(Token.TokenType.LITERAL, \"foo\", 0);\n        final Token token2 = new Token(Token.TokenType.LITERAL, \"foo\", 0);\n        final Token token3 = new Token(Token.TokenType.LITERAL, \"bar\", 4);\n\n        Assert.assertEquals(token1, token2);\n        Assert.assertNotEquals(token1, token3);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/ParserTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author lcram\n */\npublic class ParserTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testBasic()\n    {\n        final String input1 = \"!name | name=/.*[s|S]treet/\";\n        final String expected1 = \"OR_1\\n\" + \"OR_1 left: BANG_0\\n\" + \"OR_1 right: EQ_0\\n\"\n                + \"BANG_0\\n\" + \"BANG_0 child: name_0\\n\" + \"name_0\\n\" + \"EQ_0\\n\"\n                + \"EQ_0 left: name_1\\n\" + \"EQ_0 right: .*[s|S]treet_2\\n\" + \"name_1\\n\"\n                + \".*[s|S]treet_2\\n\";\n        Assert.assertEquals(expected1,\n                new Parser(new Lexer().lex(input1), input1).parse().debugPrintTree());\n\n        final String input2 = \"foo | bar | baz\";\n        final String expected2 = \"OR_1\\n\" + \"OR_1 left: foo_0\\n\" + \"OR_1 right: OR_0\\n\" + \"foo_0\\n\"\n                + \"OR_0\\n\" + \"OR_0 left: bar_1\\n\" + \"OR_0 right: baz_2\\n\" + \"bar_1\\n\" + \"baz_2\\n\";\n        Assert.assertEquals(expected2,\n                new Parser(new Lexer().lex(input2), input2).parse().debugPrintTree());\n\n        final String input3 = \"foo = bar = baz != bat\";\n        final String expected3 = \"EQ_2\\n\" + \"EQ_2 left: foo_0\\n\" + \"EQ_2 right: EQ_1\\n\" + \"foo_0\\n\"\n                + \"EQ_1\\n\" + \"EQ_1 left: bar_1\\n\" + \"EQ_1 right: BANGEQ_0\\n\" + \"bar_1\\n\"\n                + \"BANGEQ_0\\n\" + \"BANGEQ_0 left: baz_2\\n\" + \"BANGEQ_0 right: bat_3\\n\" + \"baz_2\\n\"\n                + \"bat_3\\n\";\n        Assert.assertEquals(expected3,\n                new Parser(new Lexer().lex(input3), input3).parse().debugPrintTree());\n\n        final String input4 = \"foo != bar = baz != bat != hello\";\n        final String expected4 = \"BANGEQ_3\\n\" + \"BANGEQ_3 left: foo_0\\n\" + \"BANGEQ_3 right: EQ_2\\n\"\n                + \"foo_0\\n\" + \"EQ_2\\n\" + \"EQ_2 left: bar_1\\n\" + \"EQ_2 right: BANGEQ_1\\n\" + \"bar_1\\n\"\n                + \"BANGEQ_1\\n\" + \"BANGEQ_1 left: baz_2\\n\" + \"BANGEQ_1 right: BANGEQ_0\\n\" + \"baz_2\\n\"\n                + \"BANGEQ_0\\n\" + \"BANGEQ_0 left: bat_3\\n\" + \"BANGEQ_0 right: hello_4\\n\" + \"bat_3\\n\"\n                + \"hello_4\\n\";\n        Assert.assertEquals(expected4,\n                new Parser(new Lexer().lex(input4), input4).parse().debugPrintTree());\n\n        final String input5 = \"foo = bar\";\n        final String expected5 = \"EQ_0\\n\" + \"EQ_0 left: foo_0\\n\" + \"EQ_0 right: bar_1\\n\" + \"foo_0\\n\"\n                + \"bar_1\\n\";\n        Assert.assertEquals(expected5,\n                new Parser(new Lexer().lex(input5), input5).parse().debugPrintTree());\n\n        final String input6 = \"foo\";\n        final String expected6 = \"foo_0\\n\";\n        Assert.assertEquals(expected6,\n                new Parser(new Lexer().lex(input6), input6).parse().debugPrintTree());\n\n        final String input7 = \"foo & bar & baz\";\n        final String expected7 = \"AND_1\\n\" + \"AND_1 left: foo_0\\n\" + \"AND_1 right: AND_0\\n\"\n                + \"foo_0\\n\" + \"AND_0\\n\" + \"AND_0 left: bar_1\\n\" + \"AND_0 right: baz_2\\n\" + \"bar_1\\n\"\n                + \"baz_2\\n\";\n        Assert.assertEquals(expected7,\n                new Parser(new Lexer().lex(input7), input7).parse().debugPrintTree());\n\n        final String input8 = \"foo=bar ^ baz=bat\";\n        final String expected8 = \"XOR_2\\n\" + \"XOR_2 left: EQ_0\\n\" + \"XOR_2 right: EQ_1\\n\" + \"EQ_0\\n\"\n                + \"EQ_0 left: foo_0\\n\" + \"EQ_0 right: bar_1\\n\" + \"foo_0\\n\" + \"bar_1\\n\" + \"EQ_1\\n\"\n                + \"EQ_1 left: baz_2\\n\" + \"EQ_1 right: bat_3\\n\" + \"baz_2\\n\" + \"bat_3\\n\";\n        Assert.assertEquals(expected8,\n                new Parser(new Lexer().lex(input8), input8).parse().debugPrintTree());\n\n        final String input9 = \"foo ^ bar ^ baz\";\n        final String expected9 = \"XOR_1\\n\" + \"XOR_1 left: foo_0\\n\" + \"XOR_1 right: XOR_0\\n\"\n                + \"foo_0\\n\" + \"XOR_0\\n\" + \"XOR_0 left: bar_1\\n\" + \"XOR_0 right: baz_2\\n\" + \"bar_1\\n\"\n                + \"baz_2\\n\";\n        Assert.assertEquals(expected9,\n                new Parser(new Lexer().lex(input9), input9).parse().debugPrintTree());\n    }\n\n    @Test\n    public void testExceptionDoubleAnd()\n    {\n        final String input = \"(foo = bar & baz = bat) && (hello = city | world)\";\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"syntax error: unexpected token AND(&)\\n\"\n                + \"(foo = bar & baz = bat) && (hello = city | world)\\n\"\n                + \"~~~~~~~~~~~~~~~~~~~~~~~~~^\");\n        new Parser(new Lexer().lex(input), input).parse().debugPrintTree();\n    }\n\n    @Test\n    public void testExceptionDoubleEqual()\n    {\n        final String input = \"foo == bar\";\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"syntax error: unexpected token EQUAL(=)\\n\" + \"foo == bar\\n\" + \"~~~~~^\");\n        new Parser(new Lexer().lex(input), input).parse().debugPrintTree();\n    }\n\n    @Test\n    public void testExceptionDoubleLiteral()\n    {\n        final String input = \"foo baz = bar\";\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"syntax error: unexpected token LITERAL(baz)\\n\" + \"foo baz = bar\\n\" + \"~~~~^\");\n        new Parser(new Lexer().lex(input), input).parse().debugPrintTree();\n    }\n\n    @Test\n    public void testExceptionExpectedParenthesis()\n    {\n        final String input = \"(foo = bar\";\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException\n                .expectMessage(\"syntax error: expected PAREN_CLOSE, but saw EOF(null)\\n\"\n                        + \"(foo = bar\\n\" + \"~~~~~~~~~~^\");\n        new Parser(new Lexer().lex(input), input).parse().debugPrintTree();\n    }\n\n    @Test\n    public void testExceptionLeadingOp()\n    {\n        final String input = \"& foo = bar\";\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException\n                .expectMessage(\"syntax error: unexpected token AND(&)\\n\" + \"& foo = bar\\n\" + \"^\");\n        new Parser(new Lexer().lex(input), input).parse().debugPrintTree();\n    }\n\n    @Test\n    public void testParentheticalChanges()\n    {\n        final String input1 = \"foo = bar | (baz = bat & hello = world)\";\n        final String expected1 = \"OR_4\\n\" + \"OR_4 left: EQ_0\\n\" + \"OR_4 right: AND_3\\n\" + \"EQ_0\\n\"\n                + \"EQ_0 left: foo_0\\n\" + \"EQ_0 right: bar_1\\n\" + \"foo_0\\n\" + \"bar_1\\n\" + \"AND_3\\n\"\n                + \"AND_3 left: EQ_1\\n\" + \"AND_3 right: EQ_2\\n\" + \"EQ_1\\n\" + \"EQ_1 left: baz_2\\n\"\n                + \"EQ_1 right: bat_3\\n\" + \"baz_2\\n\" + \"bat_3\\n\" + \"EQ_2\\n\" + \"EQ_2 left: hello_4\\n\"\n                + \"EQ_2 right: world_5\\n\" + \"hello_4\\n\" + \"world_5\\n\";\n        Assert.assertEquals(expected1,\n                new Parser(new Lexer().lex(input1), input1).parse().debugPrintTree());\n\n        final String input2 = \"(foo = bar | baz = bat) & hello = world\";\n        final String expected2 = \"AND_4\\n\" + \"AND_4 left: OR_2\\n\" + \"AND_4 right: EQ_3\\n\" + \"OR_2\\n\"\n                + \"OR_2 left: EQ_0\\n\" + \"OR_2 right: EQ_1\\n\" + \"EQ_0\\n\" + \"EQ_0 left: foo_0\\n\"\n                + \"EQ_0 right: bar_1\\n\" + \"foo_0\\n\" + \"bar_1\\n\" + \"EQ_1\\n\" + \"EQ_1 left: baz_2\\n\"\n                + \"EQ_1 right: bat_3\\n\" + \"baz_2\\n\" + \"bat_3\\n\" + \"EQ_3\\n\" + \"EQ_3 left: hello_4\\n\"\n                + \"EQ_3 right: world_5\\n\" + \"hello_4\\n\" + \"world_5\\n\";\n        Assert.assertEquals(expected2,\n                new Parser(new Lexer().lex(input2), input2).parse().debugPrintTree());\n\n        final String inputA = \"foo = bar | (baz = bat & hello = world)\";\n        final String inputB = \"foo = bar | baz = bat & hello = world\";\n        Assert.assertEquals(new Parser(new Lexer().lex(inputA), inputA).parse().debugPrintTree(),\n                new Parser(new Lexer().lex(inputB), inputB).parse().debugPrintTree());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/SemanticCheckerTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author lcram\n */\npublic class SemanticCheckerTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void test()\n    {\n        // These will never fail\n\n        final String input = \"foo=bar\";\n        new SemanticChecker().check(new Parser(new Lexer().lex(input), input).parse());\n\n        final String input2 = \"foo = bar | baz = bat\";\n        new SemanticChecker().check(new Parser(new Lexer().lex(input2), input2).parse());\n\n        final String input3 = \"((foo | bar) = baz | hello = world) & a=b\";\n        new SemanticChecker().check(new Parser(new Lexer().lex(input3), input3).parse());\n    }\n\n    @Test\n    public void testFail1()\n    {\n        final String input = \"!(foo = bar) = baz\";\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"semantic error: invalid nested equality operators\");\n        new SemanticChecker().check(new Parser(new Lexer().lex(input), input).parse());\n    }\n\n    @Test\n    public void testFail2()\n    {\n        final String input = \"foo = bar = baz = !(bat = cat = mat)\";\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"semantic error: invalid nested equality operators\");\n        new SemanticChecker().check(new Parser(new Lexer().lex(input), input).parse());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/ASTNodeTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Token;\n\n/**\n * @author lcram\n */\npublic class ASTNodeTest\n{\n    @Test\n    public void testPrint()\n    {\n        BinaryOperator.clearIdentifierCounter();\n        UnaryOperator.clearIdentifierCounter();\n        Operand.clearIdentifierCounter();\n\n        final ASTNode foo = new LiteralOperand(new Token(Token.TokenType.LITERAL, \"foo\", 0));\n        final ASTNode bar = new LiteralOperand(new Token(Token.TokenType.LITERAL, \"bar\", 0));\n        final ASTNode mat = new LiteralOperand(new Token(Token.TokenType.LITERAL, \"mat\", 0));\n        final ASTNode baz = new LiteralOperand(new Token(Token.TokenType.LITERAL, \"baz\", 0));\n        final ASTNode bat = new RegexOperand(new Token(Token.TokenType.LITERAL, \"bat.*\", 0));\n        final ASTNode hat = new LiteralOperand(new Token(Token.TokenType.LITERAL, \"hat\", 0));\n\n        final ASTNode not = new BangOperator(bar);\n        final ASTNode equals = new EqualsOperator(foo, not, false);\n        final ASTNode or1 = new OrOperator(baz, bat);\n        final ASTNode bangEquals = new EqualsOperator(mat, or1, true);\n        final ASTNode and = new AndOperator(equals, bangEquals);\n        final ASTNode xor = new XorOperator(and, hat);\n\n        final String expected = \"XOR_4\\n\" + \"XOR_4 left: AND_3\\n\" + \"XOR_4 right: hat_5\\n\"\n                + \"AND_3\\n\" + \"AND_3 left: EQ_0\\n\" + \"AND_3 right: BANGEQ_2\\n\" + \"EQ_0\\n\"\n                + \"EQ_0 left: foo_0\\n\" + \"EQ_0 right: BANG_0\\n\" + \"foo_0\\n\" + \"BANG_0\\n\"\n                + \"BANG_0 child: bar_1\\n\" + \"bar_1\\n\" + \"BANGEQ_2\\n\" + \"BANGEQ_2 left: mat_2\\n\"\n                + \"BANGEQ_2 right: OR_1\\n\" + \"mat_2\\n\" + \"OR_1\\n\" + \"OR_1 left: baz_3\\n\"\n                + \"OR_1 right: bat.*_4\\n\" + \"baz_3\\n\" + \"bat.*_4\\n\" + \"hat_5\\n\";\n        Assert.assertEquals(expected, xor.debugPrintTree());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/filters/matcher/parsing/tree/TreePrinterTest.java",
    "content": "package org.openstreetmap.atlas.tags.filters.matcher.parsing.tree;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Lexer;\nimport org.openstreetmap.atlas.tags.filters.matcher.parsing.Parser;\n\n/**\n * @author lcram\n */\npublic class TreePrinterTest\n{\n    @Test\n    public void test()\n    {\n        final String input1 = \"foo=bar | !(baz=bat)\";\n        ASTNode root = new Parser(new Lexer().lex(input1), input1).parse();\n        final String tree1 = TreePrinter.print(root);\n        Assert.assertEquals(\n                \"                                |                               \\n\"\n                        + \"                ┌───────────────┴───────────────┐               \\n\"\n                        + \"                =                               !               \\n\"\n                        + \"        ┌───────┴───────┐               ┌───────┘               \\n\"\n                        + \"       foo             bar              =                       \\n\"\n                        + \"                                    ┌───┴───┐                   \\n\"\n                        + \"                                   baz     bat                  \\n\",\n                tree1);\n\n        final String input2 = \"foo=bar | baz=bat\";\n        root = new Parser(new Lexer().lex(input2), input2).parse();\n        final String tree2 = TreePrinter.print(root);\n        Assert.assertEquals(\"                |               \\n\"\n                + \"        ┌───────┴───────┐       \\n\" + \"        =               =       \\n\"\n                + \"    ┌───┴───┐       ┌───┴───┐   \\n\" + \"   foo     bar     baz     bat  \\n\",\n                tree2);\n\n        final String input3 = \"foo=bar | baz=bat & cat=mat\";\n        root = new Parser(new Lexer().lex(input3), input3).parse();\n        final String tree3 = TreePrinter.print(root);\n        Assert.assertEquals(\n                \"                                |                               \\n\"\n                        + \"                ┌───────────────┴───────────────┐               \\n\"\n                        + \"                =                               &               \\n\"\n                        + \"        ┌───────┴───────┐               ┌───────┴───────┐       \\n\"\n                        + \"       foo             bar              =               =       \\n\"\n                        + \"                                    ┌───┴───┐       ┌───┴───┐   \\n\"\n                        + \"                                   baz     bat     cat     mat  \\n\",\n                tree3);\n\n        final String input4 = \"(foo=bar | baz=bat) & cat=mat\";\n        root = new Parser(new Lexer().lex(input4), input4).parse();\n        final String tree4 = TreePrinter.print(root);\n        Assert.assertEquals(\n                \"                                &                               \\n\"\n                        + \"                ┌───────────────┴───────────────┐               \\n\"\n                        + \"                |                               =               \\n\"\n                        + \"        ┌───────┴───────┐               ┌───────┴───────┐       \\n\"\n                        + \"        =               =              cat             mat      \\n\"\n                        + \"    ┌───┴───┐       ┌───┴───┐                                   \\n\"\n                        + \"   foo     bar     baz     bat                                  \\n\",\n                tree4);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/tags/oneway/OneWayTagTest.java",
    "content": "package org.openstreetmap.atlas.tags.oneway;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.tags.Taggable;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.BicycleOneWayTag;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.CyclewayLeftOneWayTag;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.CyclewayOneWayTag;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.CyclewayRightOneWayTag;\nimport org.openstreetmap.atlas.tags.oneway.bicycle.OneWayBicycleTag;\nimport org.openstreetmap.atlas.tags.oneway.motor.OneWayMotorVehicleTag;\nimport org.openstreetmap.atlas.tags.oneway.motor.OneWayMotorcarTag;\nimport org.openstreetmap.atlas.tags.oneway.motor.OneWayVehicleTag;\n\n/**\n * @author matthieun\n */\npublic class OneWayTagTest\n{\n    // Un-tagged\n    public static final Taggable UNTAGGED = Taggable.with(HighwayTag.KEY,\n            HighwayTag.MOTORWAY.toString());\n\n    // OneWay tags\n    public static final Taggable ONE_WAY_YES = Taggable.with(OneWayTag.KEY,\n            OneWayTag.YES.toString());\n    public static final Taggable ONE_WAY_NO = Taggable.with(OneWayTag.KEY, OneWayTag.NO.toString());\n    public static final Taggable ONE_WAY_REVERSIBLE = Taggable.with(OneWayTag.KEY,\n            OneWayTag.REVERSIBLE.toString());\n    public static final Taggable ONE_WAY_TRUE = Taggable.with(OneWayTag.KEY,\n            OneWayTag.TRUE.toString());\n    public static final Taggable ONE_WAY_FALSE = Taggable.with(OneWayTag.KEY,\n            OneWayTag.FALSE.toString());\n    public static final Taggable ONE_WAY_ONE = Taggable.with(OneWayTag.KEY,\n            OneWayTag.ONE.toString());\n    public static final Taggable ONE_WAY_ZERO = Taggable.with(OneWayTag.KEY,\n            OneWayTag.ZERO.toString());\n    public static final Taggable ONE_WAY_MINUS_ONE = Taggable.with(OneWayTag.KEY,\n            OneWayTag.MINUS_1.toString());\n    public static final Taggable ONE_WAY_REVERSE = Taggable.with(OneWayTag.KEY,\n            OneWayTag.REVERSE.toString());\n\n    // OneWayMotorVehicle\n    public static final Taggable ONE_WAY_MOTOR_VEHICLE_YES = Taggable\n            .with(OneWayMotorVehicleTag.KEY, OneWayMotorVehicleTag.YES.toString());\n    public static final Taggable ONE_WAY_MOTOR_VEHICLE_NO = Taggable.with(OneWayMotorVehicleTag.KEY,\n            OneWayMotorVehicleTag.NO.toString());\n    public static final Taggable ONE_WAY_MOTOR_VEHICLE_MINUS_ONE = Taggable\n            .with(OneWayMotorVehicleTag.KEY, OneWayMotorVehicleTag.MINUS_1.toString());\n\n    // OneWayVehicle\n    public static final Taggable ONE_WAY_VEHICLE_YES = Taggable.with(OneWayVehicleTag.KEY,\n            OneWayVehicleTag.YES.toString());\n    public static final Taggable ONE_WAY_VEHICLE_NO = Taggable.with(OneWayVehicleTag.KEY,\n            OneWayVehicleTag.NO.toString());\n    public static final Taggable ONE_WAY_VEHICLE_MINUS_ONE = Taggable.with(OneWayVehicleTag.KEY,\n            OneWayVehicleTag.MINUS_1.toString());\n\n    // OneWayMotorcar\n    public static final Taggable ONE_WAY_MOTORCAR_YES = Taggable.with(OneWayMotorcarTag.KEY,\n            OneWayMotorcarTag.YES.toString());\n    public static final Taggable ONE_WAY_MOTORCAR_NO = Taggable.with(OneWayMotorcarTag.KEY,\n            OneWayMotorcarTag.NO.toString());\n    public static final Taggable ONE_WAY_MOTORCAR_MINUS_ONE = Taggable.with(OneWayMotorcarTag.KEY,\n            OneWayMotorcarTag.MINUS_1.toString());\n\n    // BicycleOneWay\n    public static final Taggable BICYCLE_ONE_WAY_YES = Taggable.with(BicycleOneWayTag.KEY,\n            BicycleOneWayTag.YES.toString());\n    public static final Taggable BICYCLE_ONE_WAY_NO = Taggable.with(BicycleOneWayTag.KEY,\n            BicycleOneWayTag.NO.toString());\n    public static final Taggable BICYCLE_ONE_WAY_MINUS_ONE = Taggable.with(BicycleOneWayTag.KEY,\n            BicycleOneWayTag.MINUS_1.toString());\n\n    // OneWayBicycle\n    public static final Taggable ONE_WAY_BICYCLE_YES = Taggable.with(OneWayBicycleTag.KEY,\n            OneWayBicycleTag.YES.toString());\n    public static final Taggable ONE_WAY_BICYCLE_NO = Taggable.with(OneWayBicycleTag.KEY,\n            OneWayBicycleTag.NO.toString());\n    public static final Taggable ONE_WAY_BICYCLE_ONE = Taggable.with(OneWayBicycleTag.KEY,\n            OneWayBicycleTag.ONE.toString());\n    public static final Taggable ONE_WAY_BICYCLE_MINUS_ONE = Taggable.with(OneWayBicycleTag.KEY,\n            OneWayBicycleTag.MINUS_1.toString());\n    public static final Taggable ONE_WAY_BICYCLE_OPPOSITE = Taggable.with(OneWayBicycleTag.KEY,\n            OneWayBicycleTag.OPPOSITE.toString());\n\n    // CyclewayOneWay\n    public static final Taggable CYCLEWAY_ONE_WAY_YES = Taggable.with(CyclewayOneWayTag.KEY,\n            CyclewayOneWayTag.YES.toString());\n    public static final Taggable CYCLEWAY_ONE_WAY_NO = Taggable.with(CyclewayOneWayTag.KEY,\n            CyclewayOneWayTag.NO.toString());\n    public static final Taggable CYCLEWAY_ONE_WAY_MINUS_ONE = Taggable.with(CyclewayOneWayTag.KEY,\n            CyclewayOneWayTag.MINUS_1.toString());\n\n    // CyclewayRightOneWay\n    public static final Taggable CYCLEWAY_RIGHT_ONE_WAY_YES = Taggable\n            .with(CyclewayRightOneWayTag.KEY, CyclewayRightOneWayTag.YES.toString());\n    public static final Taggable CYCLEWAY_RIGHT_ONE_WAY_NO = Taggable\n            .with(CyclewayRightOneWayTag.KEY, CyclewayRightOneWayTag.NO.toString());\n    public static final Taggable CYCLEWAY_RIGHT_ONE_WAY_ONE = Taggable\n            .with(CyclewayRightOneWayTag.KEY, CyclewayRightOneWayTag.ONE.toString());\n    public static final Taggable CYCLEWAY_RIGHT_ONE_WAY_MINUS_ONE = Taggable\n            .with(CyclewayRightOneWayTag.KEY, CyclewayRightOneWayTag.MINUS_1.toString());\n    public static final Taggable CYCLEWAY_RIGHT_ONE_WAY_LANE = Taggable\n            .with(CyclewayRightOneWayTag.KEY, CyclewayRightOneWayTag.LANE.toString());\n    public static final Taggable CYCLEWAY_RIGHT_ONE_WAY_DESIGNATED = Taggable\n            .with(CyclewayRightOneWayTag.KEY, CyclewayRightOneWayTag.DESIGNATED.toString());\n\n    // CyclewayLeftOneWay\n    public static final Taggable CYCLEWAY_LEFT_ONE_WAY_YES = Taggable\n            .with(CyclewayLeftOneWayTag.KEY, CyclewayLeftOneWayTag.YES.toString());\n    public static final Taggable CYCLEWAY_LEFT_ONE_WAY_NO = Taggable.with(CyclewayLeftOneWayTag.KEY,\n            CyclewayLeftOneWayTag.NO.toString());\n    public static final Taggable CYCLEWAY_LEFT_ONE_WAY_ONE = Taggable\n            .with(CyclewayLeftOneWayTag.KEY, CyclewayLeftOneWayTag.ONE.toString());\n    public static final Taggable CYCLEWAY_LEFT_ONE_WAY_MINUS_ONE = Taggable\n            .with(CyclewayLeftOneWayTag.KEY, CyclewayLeftOneWayTag.MINUS_1.toString());\n    public static final Taggable CYCLEWAY_LEFT_ONE_WAY_LANE = Taggable\n            .with(CyclewayLeftOneWayTag.KEY, CyclewayLeftOneWayTag.LANE.toString());\n    public static final Taggable CYCLEWAY_LEFT_ONE_WAY_OPPOSITE = Taggable\n            .with(CyclewayLeftOneWayTag.KEY, CyclewayLeftOneWayTag.OPPOSITE.toString());\n    public static final Taggable CYCLEWAY_LEFT_ONE_WAY_FALSE = Taggable\n            .with(CyclewayLeftOneWayTag.KEY, CyclewayLeftOneWayTag.FALSE.toString());\n\n    @Test\n    public void testOneWayBicycleForwardFalse()\n    {\n        // Splitting between true and false methods for Sonar\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(UNTAGGED));\n\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(BICYCLE_ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(BICYCLE_ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(ONE_WAY_BICYCLE_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(ONE_WAY_BICYCLE_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(ONE_WAY_BICYCLE_OPPOSITE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(CYCLEWAY_ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(CYCLEWAY_ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(CYCLEWAY_RIGHT_ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(CYCLEWAY_RIGHT_ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(CYCLEWAY_LEFT_ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(CYCLEWAY_LEFT_ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(CYCLEWAY_LEFT_ONE_WAY_OPPOSITE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayForward(CYCLEWAY_LEFT_ONE_WAY_FALSE));\n    }\n\n    @Test\n    public void testOneWayBicycleForwardTrue()\n    {\n        // Splitting between true and false methods for Sonar\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(ONE_WAY_YES));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(ONE_WAY_TRUE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(ONE_WAY_ONE));\n\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(BICYCLE_ONE_WAY_YES));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(ONE_WAY_BICYCLE_YES));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(ONE_WAY_BICYCLE_ONE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(CYCLEWAY_ONE_WAY_YES));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(CYCLEWAY_RIGHT_ONE_WAY_YES));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(CYCLEWAY_RIGHT_ONE_WAY_ONE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(CYCLEWAY_RIGHT_ONE_WAY_LANE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(CYCLEWAY_RIGHT_ONE_WAY_DESIGNATED));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(CYCLEWAY_LEFT_ONE_WAY_YES));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(CYCLEWAY_LEFT_ONE_WAY_ONE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayForward(CYCLEWAY_LEFT_ONE_WAY_LANE));\n    }\n\n    @Test\n    public void testOneWayBicycleReverseFalse()\n    {\n        // Splitting between true and false methods for Sonar\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(UNTAGGED));\n\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(BICYCLE_ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(BICYCLE_ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(ONE_WAY_BICYCLE_YES));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(ONE_WAY_BICYCLE_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(ONE_WAY_BICYCLE_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_RIGHT_ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_RIGHT_ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_RIGHT_ONE_WAY_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_RIGHT_ONE_WAY_LANE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_RIGHT_ONE_WAY_DESIGNATED));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_LEFT_ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_LEFT_ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_LEFT_ONE_WAY_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_LEFT_ONE_WAY_LANE));\n        Assert.assertFalse(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_LEFT_ONE_WAY_FALSE));\n    }\n\n    @Test\n    public void testOneWayBicycleReverseTrue()\n    {\n        // Splitting between true and false methods for Sonar\n        Assert.assertTrue(OneWayTag.isBicycleOneWayReversed(BICYCLE_ONE_WAY_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayReversed(ONE_WAY_BICYCLE_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayReversed(ONE_WAY_BICYCLE_OPPOSITE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_ONE_WAY_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_RIGHT_ONE_WAY_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_LEFT_ONE_WAY_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isBicycleOneWayReversed(CYCLEWAY_LEFT_ONE_WAY_OPPOSITE));\n    }\n\n    @Test\n    public void testOneWayBicycleTwoWay()\n    {\n        Assert.assertTrue(OneWayTag.isBicycleTwoWay(UNTAGGED));\n\n        Assert.assertTrue(OneWayTag.isBicycleTwoWay(BICYCLE_ONE_WAY_NO));\n        Assert.assertTrue(OneWayTag.isBicycleTwoWay(ONE_WAY_BICYCLE_NO));\n        Assert.assertTrue(OneWayTag.isBicycleTwoWay(CYCLEWAY_ONE_WAY_NO));\n        Assert.assertTrue(OneWayTag.isBicycleTwoWay(CYCLEWAY_RIGHT_ONE_WAY_NO));\n        Assert.assertTrue(OneWayTag.isBicycleTwoWay(CYCLEWAY_LEFT_ONE_WAY_NO));\n        Assert.assertTrue(OneWayTag.isBicycleTwoWay(CYCLEWAY_LEFT_ONE_WAY_FALSE));\n\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(BICYCLE_ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(BICYCLE_ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(ONE_WAY_BICYCLE_YES));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(ONE_WAY_BICYCLE_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(ONE_WAY_BICYCLE_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(ONE_WAY_BICYCLE_OPPOSITE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_RIGHT_ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_RIGHT_ONE_WAY_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_RIGHT_ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_RIGHT_ONE_WAY_LANE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_RIGHT_ONE_WAY_DESIGNATED));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_LEFT_ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_LEFT_ONE_WAY_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_LEFT_ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_LEFT_ONE_WAY_LANE));\n        Assert.assertFalse(OneWayTag.isBicycleTwoWay(CYCLEWAY_LEFT_ONE_WAY_OPPOSITE));\n    }\n\n    @Test\n    public void testOneWayMotorTagsForward()\n    {\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayForward(UNTAGGED));\n\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_YES));\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_TRUE));\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_ONE));\n\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_MOTOR_VEHICLE_YES));\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_VEHICLE_YES));\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_MOTORCAR_YES));\n\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_MOTOR_VEHICLE_NO));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_MOTOR_VEHICLE_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_VEHICLE_NO));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_VEHICLE_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_MOTORCAR_NO));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayForward(ONE_WAY_MOTORCAR_MINUS_ONE));\n    }\n\n    @Test\n    public void testOneWayMotorTagsReverse()\n    {\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayReversed(UNTAGGED));\n\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_REVERSE));\n\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_MOTOR_VEHICLE_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_VEHICLE_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_MOTORCAR_MINUS_ONE));\n\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_MOTOR_VEHICLE_NO));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_MOTOR_VEHICLE_YES));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_VEHICLE_NO));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_VEHICLE_YES));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_MOTORCAR_NO));\n        Assert.assertFalse(OneWayTag.isMotorVehicleOneWayReversed(ONE_WAY_MOTORCAR_YES));\n    }\n\n    @Test\n    public void testOneWayMotorTagsTwoWay()\n    {\n        Assert.assertTrue(OneWayTag.isMotorVehicleTwoWay(UNTAGGED));\n\n        Assert.assertTrue(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_MOTOR_VEHICLE_NO));\n        Assert.assertTrue(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_VEHICLE_NO));\n        Assert.assertTrue(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_MOTORCAR_NO));\n\n        Assert.assertFalse(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_MOTOR_VEHICLE_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_MOTOR_VEHICLE_YES));\n        Assert.assertFalse(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_VEHICLE_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_VEHICLE_YES));\n        Assert.assertFalse(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_MOTORCAR_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isMotorVehicleTwoWay(ONE_WAY_MOTORCAR_YES));\n    }\n\n    @Test\n    public void testOneWayRegularForward()\n    {\n        Assert.assertFalse(OneWayTag.isOneWayForward(UNTAGGED));\n\n        Assert.assertTrue(OneWayTag.isOneWayForward(ONE_WAY_YES));\n        Assert.assertTrue(OneWayTag.isOneWayForward(ONE_WAY_TRUE));\n        Assert.assertTrue(OneWayTag.isOneWayForward(ONE_WAY_ONE));\n\n        Assert.assertFalse(OneWayTag.isOneWayForward(ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isOneWayForward(ONE_WAY_REVERSIBLE));\n        Assert.assertFalse(OneWayTag.isOneWayForward(ONE_WAY_FALSE));\n        Assert.assertFalse(OneWayTag.isOneWayForward(ONE_WAY_ZERO));\n        Assert.assertFalse(OneWayTag.isOneWayForward(ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isOneWayForward(ONE_WAY_REVERSE));\n\n    }\n\n    @Test\n    public void testOneWayRegularReverse()\n    {\n        Assert.assertFalse(OneWayTag.isOneWayReversed(UNTAGGED));\n\n        Assert.assertTrue(OneWayTag.isOneWayReversed(ONE_WAY_MINUS_ONE));\n        Assert.assertTrue(OneWayTag.isOneWayReversed(ONE_WAY_REVERSE));\n\n        Assert.assertFalse(OneWayTag.isOneWayReversed(ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isOneWayReversed(ONE_WAY_TRUE));\n        Assert.assertFalse(OneWayTag.isOneWayReversed(ONE_WAY_ONE));\n        Assert.assertFalse(OneWayTag.isOneWayReversed(ONE_WAY_NO));\n        Assert.assertFalse(OneWayTag.isOneWayReversed(ONE_WAY_REVERSIBLE));\n        Assert.assertFalse(OneWayTag.isOneWayReversed(ONE_WAY_FALSE));\n        Assert.assertFalse(OneWayTag.isOneWayReversed(ONE_WAY_ZERO));\n    }\n\n    @Test\n    public void testOneWayRegularTwoWay()\n    {\n        Assert.assertTrue(OneWayTag.isTwoWay(UNTAGGED));\n\n        Assert.assertTrue(OneWayTag.isTwoWay(ONE_WAY_NO));\n        Assert.assertTrue(OneWayTag.isTwoWay(ONE_WAY_FALSE));\n        Assert.assertTrue(OneWayTag.isTwoWay(ONE_WAY_ZERO));\n\n        Assert.assertFalse(OneWayTag.isTwoWay(ONE_WAY_YES));\n        Assert.assertFalse(OneWayTag.isTwoWay(ONE_WAY_TRUE));\n        Assert.assertFalse(OneWayTag.isTwoWay(ONE_WAY_ONE));\n        Assert.assertFalse(OneWayTag.isTwoWay(ONE_WAY_REVERSIBLE));\n        Assert.assertFalse(OneWayTag.isTwoWay(ONE_WAY_MINUS_ONE));\n        Assert.assertFalse(OneWayTag.isTwoWay(ONE_WAY_REVERSE));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/test/TestUtility.java",
    "content": "package org.openstreetmap.atlas.test;\n\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.ParseException;\nimport org.locationtech.jts.io.WKTReader;\n\n/**\n * @author Yiqing Jin\n */\npublic final class TestUtility\n{\n    private static WKTReader reader;\n\n    static\n    {\n        reader = new WKTReader();\n    }\n\n    public static Geometry createJtsGeometryFromWKT(final String wktString)\n    {\n        Geometry geometry = null;\n        try\n        {\n            geometry = reader.read(wktString);\n        }\n        catch (final ParseException e)\n        {\n            e.printStackTrace();\n        }\n        return geometry;\n    };\n\n    private TestUtility()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/archive/ExtractorTest.java",
    "content": "package org.openstreetmap.atlas.utilities.archive;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.apache.commons.compress.archivers.ArchiveException;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\n/**\n * @author matthieun\n */\npublic class ExtractorTest\n{\n    @Test\n    public void testExtract() throws IOException, ArchiveException\n    {\n        File temporaryFile = null;\n        File outputDirectory = null;\n        try\n        {\n            final String name = \"testExtractor\";\n            temporaryFile = File.createTempFile(name, \".zip\");\n            outputDirectory = File.createTempFile(name + \"_output\", \"\");\n            outputDirectory.delete();\n            outputDirectory.mkdirs();\n            final Resource testData = new InputStreamResource(\n                    () -> ExtractorTest.class.getResourceAsStream(\"testExtractor.zip\"));\n            testData.copyTo(new org.openstreetmap.atlas.streaming.resource.File(temporaryFile));\n            final Extractor extractor = Extractor.extractZipArchive(outputDirectory);\n            extractor.extract(temporaryFile);\n            final String expectedOutput = \"file1\";\n            final String actualOutput = new org.openstreetmap.atlas.streaming.resource.File(\n                    new File(outputDirectory, \"file1.txt\")).all();\n            Assert.assertEquals(expectedOutput, actualOutput);\n        }\n        finally\n        {\n            temporaryFile.delete();\n            new org.openstreetmap.atlas.streaming.resource.File(outputDirectory)\n                    .listFilesRecursively()\n                    .forEach(org.openstreetmap.atlas.streaming.resource.File::delete);\n            outputDirectory.delete();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/arrays/LargeArrayTest.java",
    "content": "package org.openstreetmap.atlas.utilities.arrays;\n\nimport java.io.ObjectOutputStream;\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class LargeArrayTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(LargeArrayTest.class);\n    private LongArray array;\n\n    public static void main(final String[] args)\n    {\n        new LargeArrayTest().largeSerialization();\n    }\n\n    @Before\n    public void init()\n    {\n        this.array = (LongArray) new LongArray(100, 2, 15).withName(\"testArray\");\n        for (int i = 0; i < 100; i++)\n        {\n            this.array.add((long) i);\n        }\n    }\n\n    public void largeSerialization()\n    {\n        final int size = 500_000_000;\n        final IntegerArray array = new IntegerArray(size, size, size);\n        final File writableResourceJava = new File(\n                System.getProperty(\"user.home\") + \"/projects/data/unitTest/IntegerArrayJava.dat\");\n        for (int i = 0; i < size; i++)\n        {\n            array.add(new Random().nextInt());\n        }\n        final Time start = Time.now();\n        try (ObjectOutputStream out = new ObjectOutputStream(writableResourceJava.write()))\n        {\n            out.writeObject(array);\n        }\n        catch (final Exception e)\n        {\n            throw new CoreException(\"Could not save to {}\", e, writableResourceJava);\n        }\n        logger.info(\"Java Serialization: {}\", start.elapsedSince());\n    }\n\n    @Test\n    public void testArray()\n    {\n        try\n        {\n            this.array.add(0L);\n            Assert.fail(\"Cannot go over array size\");\n        }\n        catch (final CoreException e)\n        {\n            // OK\n        }\n        Assert.assertEquals(100, this.array.size());\n        this.array.forEach(value -> System.out.print(value + \" \"));\n        System.out.println();\n        Assert.assertEquals(new Long(53L), this.array.get(53));\n        Assert.assertEquals(new Long(97L), this.array.get(97));\n    }\n\n    @Test\n    public void testTrimming()\n    {\n        Assert.assertEquals(15,\n                this.array.getArrays().get(this.array.getArrays().size() - 1).size());\n        this.array.trimIfLessFilledThan(Ratio.HALF);\n        Assert.assertEquals(15,\n                this.array.getArrays().get(this.array.getArrays().size() - 1).size());\n        this.array.trim();\n        Assert.assertEquals(10,\n                this.array.getArrays().get(this.array.getArrays().size() - 1).size());\n    }\n\n    /*\n     * A NOTE ABOUT THIS TEST: Historically, LargeArray would mishandle trim() in the case that the\n     * rightmost subarray was completely full. The trim() function has now been fixed, so this\n     * should no longer happen. If this test ever fails, that means the bug has been re-introduced.\n     */\n    @Test\n    public void trimArrayWithFullRightmostSubArray()\n    {\n        final int maxSize = 100;\n        final int subArraySize = 10;\n        final int blockSize = subArraySize;\n\n        final LongArray array = new LongArray(maxSize, blockSize, subArraySize);\n\n        // completely fill up three subarrays\n        for (int i = 0; i < subArraySize * 3; i++)\n        {\n            array.add(2L);\n        }\n\n        // Now, we trim the array. This should only affect the rightmost* subarray\n        // * Here, rightmost indicates it is the last subarray in LargeArray's list of subarrays\n        array.trim();\n\n        // this is fine, since the first subarray is not corrupted by trim()\n        Assert.assertEquals(2L, array.get(2).longValue());\n\n        // this is also fine, the second subarray is not corrupted\n        Assert.assertEquals(2L, array.get(12).longValue());\n\n        // this should be fine as well, now that the bug is fixed\n        // NOTE that the bug would have caused this line to throw ArrayIndexOutOfBoundsException\n        Assert.assertEquals(2L, array.get(23).longValue());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/caching/ConcurrentResourceCacheTest.java",
    "content": "package org.openstreetmap.atlas.utilities.caching;\n\nimport java.net.URI;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.caching.strategies.CachingStrategy;\n\n/**\n * @author lcram\n */\npublic class ConcurrentResourceCacheTest\n{\n    /**\n     * @author lcram\n     */\n    private static class DebugCachingStrategy implements CachingStrategy\n    {\n        static final String NAME = \"DebugCachingStrategy\";\n\n        private final Set<URI> cacheContains = Collections\n                .newSetFromMap(new ConcurrentHashMap<URI, Boolean>());\n\n        @Override\n        public Optional<Resource> attemptFetch(final URI resourceURI,\n                final Function<URI, Optional<Resource>> defaultFetcher)\n        {\n            if (this.cacheContains.contains(resourceURI))\n            {\n                return Optional.of(new StringResource(RESOURCE_CONTENTS));\n            }\n            this.cacheContains.add(resourceURI);\n            return Optional.empty();\n        }\n\n        public boolean cacheContains(final URI uri)\n        {\n            return this.cacheContains.contains(uri);\n        }\n\n        @Override\n        public String getName()\n        {\n            return NAME;\n        }\n\n        @Override\n        public void invalidate()\n        {\n            this.cacheContains.clear();\n        }\n\n        @Override\n        public void invalidate(final URI resourceURI)\n        {\n            this.cacheContains.remove(resourceURI);\n        }\n    }\n\n    private static final String RESOURCE_CONTENTS = \"hello world\";\n\n    @Test\n    public void testCache()\n    {\n        final Function<URI, Optional<Resource>> fetcher = uri -> Optional\n                .of(new StringResource(RESOURCE_CONTENTS));\n\n        final DebugCachingStrategy strategy = new DebugCachingStrategy();\n        Assert.assertEquals(DebugCachingStrategy.NAME, strategy.getName());\n        final ConcurrentResourceCache cache = new ConcurrentResourceCache(strategy, fetcher);\n        Assert.assertEquals(cache.getStrategyName(), strategy.getName());\n        Assert.assertNotNull(cache.getCacheID());\n\n        final URI foo = URI.create(\"scheme://foo\");\n        Assert.assertFalse(strategy.cacheContains(foo));\n        Optional<Resource> fromCache = cache.get(foo);\n        Assert.assertTrue(strategy.cacheContains(foo));\n        Assert.assertTrue(fromCache.isPresent());\n        Assert.assertEquals(\"hello world\", fromCache.get().all());\n\n        final URI bar = URI.create(\"scheme://bar\");\n        Assert.assertFalse(strategy.cacheContains(bar));\n        fromCache = cache.get(bar);\n        Assert.assertTrue(strategy.cacheContains(bar));\n        Assert.assertTrue(fromCache.isPresent());\n        Assert.assertEquals(\"hello world\", fromCache.get().all());\n\n        cache.invalidate(bar);\n        Assert.assertFalse(strategy.cacheContains(bar));\n\n        cache.invalidate();\n        Assert.assertFalse(strategy.cacheContains(foo));\n        Assert.assertFalse(strategy.cacheContains(bar));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/caching/LocalFileInMemoryCacheTest.java",
    "content": "package org.openstreetmap.atlas.utilities.caching;\n\nimport java.io.IOException;\nimport java.nio.file.FileSystem;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class LocalFileInMemoryCacheTest\n{\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n\n            final LocalFileInMemoryCache cache = new LocalFileInMemoryCache(filesystem);\n\n            final Optional<Resource> text1 = cache.get(\"/Users/foo/text1.txt\");\n            Assert.assertTrue(text1.isPresent());\n            Assert.assertEquals(\"hello world\", text1.get().all());\n            final Optional<Resource> text2 = cache.get(\"/Users/foo/text2.txt\");\n            Assert.assertTrue(text2.isPresent());\n            Assert.assertEquals(\"foo bar\", text2.get().all());\n            final Optional<Resource> text3 = cache.get(\"/Users/foo/text3.txt\");\n            Assert.assertTrue(text3.isEmpty());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final File textFile1 = new File(\"/Users/foo/text1.txt\", filesystem);\n        textFile1.writeAndClose(\"hello world\");\n        final File textFile2 = new File(\"/Users/foo/text2.txt\", filesystem);\n        textFile2.writeAndClose(\"foo bar\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/caching/ResourceCacheTest.java",
    "content": "package org.openstreetmap.atlas.utilities.caching;\n\nimport java.net.URI;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\n\n/**\n * @author lcram\n */\npublic class ResourceCacheTest\n{\n    /**\n     * @author lcram\n     */\n    private static class TestCache implements ResourceCache\n    {\n        @Override\n        public Optional<Resource> get(final URI resourceURI)\n        {\n            return Optional.of(new StringResource(RESOURCE_CONTENTS));\n        }\n\n        @Override\n        public void invalidate()\n        {\n            // NOOP\n        }\n\n        @Override\n        public void invalidate(final URI resourceURI)\n        {\n            // NOOP\n        }\n    }\n\n    private static final String RESOURCE_CONTENTS = \"hello world\";\n\n    @Test\n    public void test()\n    {\n        final ResourceCache cache = new TestCache();\n        final Optional<Resource> fromCache = cache.get(\"scheme://foo\");\n        Assert.assertTrue(fromCache.isPresent());\n        Assert.assertEquals(RESOURCE_CONTENTS, fromCache.get().all());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/caching/strategies/ByteArrayCachingStrategyTest.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.net.URI;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\n\n/**\n * @author lcram\n */\npublic class ByteArrayCachingStrategyTest\n{\n    private static final String RESOURCE_CONTENTS = \"hello world\";\n    private static final URI DOES_NOT_EXIST = URI.create(\"scheme://DNE\");\n    private static final int NEW_ARRAY_SIZE = 1024;\n\n    @Test\n    public void test()\n    {\n        final Function<URI, Optional<Resource>> fetcher = uri ->\n        {\n            if (DOES_NOT_EXIST.toString().equals(uri.toString()))\n            {\n                return Optional.empty();\n            }\n            return Optional.of(new StringResource(RESOURCE_CONTENTS));\n        };\n\n        final ByteArrayCachingStrategy strategy = new ByteArrayCachingStrategy();\n        Assert.assertEquals(strategy.getName(), strategy.getName());\n\n        // the cache map should be empty here\n        Assert.assertTrue(strategy.getResourceCache().isEmpty());\n        final URI foo = URI.create(\"scheme://foo\");\n        final Optional<Resource> resource = strategy.attemptFetch(foo, fetcher);\n        Assert.assertTrue(resource.isPresent());\n        Assert.assertEquals(RESOURCE_CONTENTS, resource.get().all());\n        // the cache map should now have a single entry\n        Assert.assertEquals(1, strategy.getResourceCache().size());\n\n        // now use the exact resource size\n        strategy.useExactResourceSize();\n        final URI bar = URI.create(\"scheme://bar\");\n        final Optional<Resource> anotherResource = strategy.attemptFetch(bar, fetcher);\n        Assert.assertTrue(anotherResource.isPresent());\n        Assert.assertEquals(RESOURCE_CONTENTS, anotherResource.get().all());\n        // the cache map should now have two entries\n        Assert.assertEquals(2, strategy.getResourceCache().size());\n\n        // remove foo from cache\n        strategy.invalidate(foo);\n        Assert.assertEquals(1, strategy.getResourceCache().size());\n\n        // add foo back with a set array size\n        strategy.withInitialArraySize(NEW_ARRAY_SIZE);\n        final Optional<Resource> fooAgain = strategy.attemptFetch(foo, fetcher);\n        Assert.assertTrue(fooAgain.isPresent());\n        Assert.assertEquals(RESOURCE_CONTENTS, fooAgain.get().all());\n        // the cache map should now have two entries again\n        Assert.assertEquals(2, strategy.getResourceCache().size());\n\n        // clear cache\n        strategy.invalidate();\n        Assert.assertTrue(strategy.getResourceCache().isEmpty());\n\n        // fetch a non-existent resource\n        final Optional<Resource> shouldBeEmpty = strategy.attemptFetch(DOES_NOT_EXIST, fetcher);\n        Assert.assertTrue(shouldBeEmpty.isEmpty());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/caching/strategies/NamespaceCachingStrategiesTest.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.nio.file.FileSystem;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.compression.Decompressor;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * Test both the {@link NamespaceCachingStrategy} and the {@link GlobalNamespaceCachingStrategy}.\n * \n * @author lcram\n */\npublic class NamespaceCachingStrategiesTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testGlobalStrategy()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final int[] fetcherCount = new int[1];\n            final Function<URI, Optional<Resource>> fetcher = uri ->\n            {\n                fetcherCount[0]++;\n                final String path = uri.getPath();\n                return Optional.of(new File(path, filesystem));\n            };\n\n            final GlobalNamespaceCachingStrategy strategy = new GlobalNamespaceCachingStrategy(\n                    filesystem);\n            final File atlasFile = new File(\"/Users/foo/test.atlas.txt\", filesystem);\n            final File cacheDirectory = new File(strategy.getStorageDirectory());\n\n            // the cache directory should be empty here, with no fetcher calls\n            Assert.assertTrue(cacheDirectory.listFiles().isEmpty());\n            Assert.assertEquals(0, fetcherCount[0]);\n            final Optional<Resource> atlasResource = strategy\n                    .attemptFetch(atlasFile.toAbsolutePath().toUri(), fetcher);\n            Assert.assertTrue(atlasResource.isPresent());\n            Assert.assertNotNull(\n                    new AtlasResourceLoader().load(atlasResource.get()).point(1000000L));\n            Assert.assertTrue(atlasResource.get().getName().endsWith(FileSuffix.TEXT.toString()));\n            // the cache directory should now have a single file, with one fetcher call\n            Assert.assertEquals(1, cacheDirectory.listFiles().size());\n            Assert.assertEquals(1, fetcherCount[0]);\n\n            final Optional<Resource> atlasResourceAgain = strategy\n                    .attemptFetch(atlasFile.toAbsolutePath().toUri(), fetcher);\n            Assert.assertTrue(atlasResourceAgain.isPresent());\n            Assert.assertTrue(\n                    atlasResourceAgain.get().getName().endsWith(FileSuffix.TEXT.toString()));\n            Assert.assertNotNull(\n                    new AtlasResourceLoader().load(atlasResourceAgain.get()).point(1000000L));\n            // fetcher calls should still be at one since we used a cached version of the file\n            Assert.assertEquals(1, fetcherCount[0]);\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testInvalidate()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final Function<URI, Optional<Resource>> fetcher = uri ->\n            {\n                final String path = uri.getPath();\n                return Optional.of(new File(path, filesystem));\n            };\n\n            final GlobalNamespaceCachingStrategy strategy = new GlobalNamespaceCachingStrategy(\n                    filesystem);\n            final File atlasFile = new File(\"/Users/foo/test.atlas.txt\", filesystem);\n            final File textFile = new File(\"/Users/foo/text.txt\", filesystem);\n            final File cacheDirectory = new File(strategy.getStorageDirectory());\n\n            // the cache directory should be empty here\n            Assert.assertTrue(cacheDirectory.listFiles().isEmpty());\n            strategy.attemptFetch(atlasFile.toAbsolutePath().toUri(), fetcher);\n            // the cache directory should now have a single file\n            Assert.assertEquals(1, cacheDirectory.listFiles().size());\n\n            strategy.attemptFetch(textFile.toAbsolutePath().toUri(), fetcher);\n            // the cache directory should now have two files\n            Assert.assertEquals(2, cacheDirectory.listFiles().size());\n\n            strategy.invalidate(atlasFile.toAbsolutePath().toUri());\n            // the cache directory should now have one file again\n            Assert.assertEquals(1, cacheDirectory.listFiles().size());\n\n            strategy.attemptFetch(atlasFile.toAbsolutePath().toUri(), fetcher);\n            // the cache directory should now have two files again\n            Assert.assertEquals(2, cacheDirectory.listFiles().size());\n\n            // this final invalidate will delete the cache directory\n            strategy.invalidate();\n            Assert.assertFalse(cacheDirectory.exists());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testStrategyWithGzippedFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final Function<URI, Optional<Resource>> fetcher = uri ->\n            {\n                final String path = uri.getPath();\n                return Optional.of(new File(path, filesystem));\n            };\n\n            final GlobalNamespaceCachingStrategy strategy = new GlobalNamespaceCachingStrategy(\n                    filesystem);\n            final File gzippedFile = new File(\"/Users/foo/hello.txt.gz\", filesystem);\n            final File cacheDirectory = new File(strategy.getStorageDirectory());\n\n            Assert.assertTrue(cacheDirectory.listFiles().isEmpty());\n            final Optional<Resource> gzippedResource = strategy\n                    .attemptFetch(gzippedFile.toAbsolutePath().toUri(), fetcher);\n            Assert.assertTrue(gzippedResource.isPresent());\n            Assert.assertEquals(\"hello world\", gzippedResource.get().all());\n            Assert.assertEquals(1, cacheDirectory.listFiles().size());\n            Assert.assertTrue(gzippedResource.get().getName().endsWith(FileSuffix.GZIP.toString()));\n\n            final Optional<Resource> gzippedResourceAgain = strategy\n                    .attemptFetch(gzippedFile.toAbsolutePath().toUri(), fetcher);\n            Assert.assertTrue(gzippedResourceAgain.isPresent());\n            Assert.assertEquals(\"hello world\", gzippedResourceAgain.get().all());\n            Assert.assertTrue(\n                    gzippedResourceAgain.get().getName().endsWith(FileSuffix.GZIP.toString()));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testStrategyWithIllegalCharacterInNamespace()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n\n            this.expectedException.expect(IllegalArgumentException.class);\n            this.expectedException\n                    .expectMessage(\"The namespace cannot contain characters '\\\\' or '/'\");\n            new NamespaceCachingStrategy(\"foo/bar\", filesystem);\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testStrategyWithoutExtensionPreservation()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final int[] fetcherCount = new int[1];\n            final Function<URI, Optional<Resource>> fetcher = uri ->\n            {\n                fetcherCount[0]++;\n                final String path = uri.getPath();\n                return Optional.of(new File(path, filesystem));\n            };\n\n            final NamespaceCachingStrategy strategy = new NamespaceCachingStrategy(\"foo\",\n                    filesystem).withFileExtensionPreservation(false);\n            final File textFile = new File(\"/Users/foo/text.txt\", filesystem);\n            final File cacheDirectory = new File(strategy.getStorageDirectory());\n\n            // the cache directory should be empty here with no fetcher calls\n            Assert.assertEquals(0, fetcherCount[0]);\n            Assert.assertTrue(cacheDirectory.listFiles().isEmpty());\n            final Optional<Resource> textResource = strategy\n                    .attemptFetch(textFile.toAbsolutePath().toUri(), fetcher);\n            Assert.assertTrue(textResource.isPresent());\n            Assert.assertEquals(\"hello world\", textResource.get().all());\n            Assert.assertFalse(textResource.get().getName().endsWith(FileSuffix.TEXT.toString()));\n            // the cache directory should now have a single file, with one fetcher call\n            Assert.assertEquals(1, cacheDirectory.listFiles().size());\n            Assert.assertEquals(1, fetcherCount[0]);\n\n            final Optional<Resource> textResourceAgain = strategy\n                    .attemptFetch(textFile.toAbsolutePath().toUri(), fetcher);\n            Assert.assertTrue(textResourceAgain.isPresent());\n            Assert.assertEquals(\"hello world\", textResource.get().all());\n            Assert.assertFalse(\n                    textResourceAgain.get().getName().endsWith(FileSuffix.TEXT.toString()));\n            // fetcher calls should still be at one since we used a cached version of the file\n            Assert.assertEquals(1, fetcherCount[0]);\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n\n        builder.addPoint(1000000L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n\n        final Atlas atlas = builder.get();\n        final File atlasFile = new File(\"/Users/foo/test.atlas.txt\", filesystem);\n        assert atlas != null;\n        atlas.saveAsText(atlasFile);\n\n        final File textFile = new File(\"/Users/foo/text.txt\", filesystem);\n        textFile.writeAndClose(\"hello world\\n\");\n\n        final File gzippedFile = new File(\"/Users/foo/hello.txt.gz\", filesystem);\n        /*\n         * The 'File' class by default enables gzip compression on all writes when it sees a file\n         * name with a '.gz' extension. So when we call 'gzippedFile.copyFrom' with a 'File' that\n         * has a '.gz' in the name, the 'copyFrom' method of 'File' will automatically apply gzip\n         * compression to the byte stream it writes into the filesystem. Since the\n         * InputStreamResource we are copying is sourced from gzipped data, we want to explicitly\n         * enable gzip decompression on the InputStreamResource so that the 'File' class does not\n         * re-compress data that is already compressed.\n         */\n        gzippedFile.copyFrom(new InputStreamResource(\n                () -> NamespaceCachingStrategiesTest.class.getResourceAsStream(\"hello.txt.gz\"))\n                .withDecompressor(Decompressor.GZIP));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/caching/strategies/NoCachingStrategyTest.java",
    "content": "package org.openstreetmap.atlas.utilities.caching.strategies;\n\nimport java.net.URI;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\n\n/**\n * @author lcram\n */\npublic class NoCachingStrategyTest\n{\n    private static final String RESOURCE_CONTENTS = \"hello world\";\n\n    @Test\n    public void test()\n    {\n        final Function<URI, Optional<Resource>> fetcher = uri -> Optional\n                .of(new StringResource(RESOURCE_CONTENTS));\n\n        final NoCachingStrategy strategy = new NoCachingStrategy();\n        Assert.assertEquals(strategy.getName(), strategy.getName());\n\n        final URI foo = URI.create(\"scheme://foo\");\n        final Optional<Resource> resource = strategy.attemptFetch(foo, fetcher);\n        Assert.assertTrue(resource.isEmpty());\n\n        // NOOPs\n        strategy.invalidate(foo);\n        strategy.invalidate();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckTest.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\nimport org.junit.Test;\n\nimport com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;\nimport com.puppycrawl.tools.checkstyle.DefaultConfiguration;\n\n/**\n * @author matthieun\n */\npublic class ArrangementCheckTest extends AbstractModuleTestSupport\n{\n    @Test\n    public void testRight() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckRight.java\"));\n    }\n\n    @Test\n    public void testWrongField0() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongField0.java\"),\n                \"12: Invalid order, method, field\");\n    }\n\n    @Test\n    public void testWrongField1() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongField1.java\"),\n                \"9: Invalid order, field, FIELD\");\n    }\n\n    @Test\n    public void testWrongField2() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongField2.java\"),\n                \"9: Invalid order, fieldA, fieldB\");\n    }\n\n    @Test\n    public void testWrongInitializerBlock() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongInitializerBlock.java\"),\n                \"12: Invalid order, method, initializer_block\");\n    }\n\n    @Test\n    public void testWrongInitializerStaticBlock() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongInitializerStaticBlock.java\"),\n                \"12: Invalid order, method, static_initializer_block\");\n    }\n\n    @Test\n    public void testWrongMethodModifier() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongMethodModifier.java\"),\n                \"12: Invalid order, methodA, methodB\");\n    }\n\n    @Test\n    public void testWrongMethodName() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongMethodName.java\"),\n                \"12: Invalid order, methodB, methodA\");\n    }\n\n    @Test\n    public void testWrongMethodVisibility1() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongMethodVisibility1.java\"),\n                \"12: Invalid order, methodA, methodB\");\n    }\n\n    @Test\n    public void testWrongMethodVisibility2() throws Exception\n    {\n        verify(configuration(), getPath(\"ArrangementCheckWrongMethodVisibility2.java\"),\n                \"12: Invalid order, methodA, methodB\");\n    }\n\n    @Override\n    protected String getPackageLocation()\n    {\n        return \"org/openstreetmap/atlas/utilities/checkstyle\";\n    }\n\n    private DefaultConfiguration configuration()\n    {\n        final DefaultConfiguration result = createModuleConfig(ArrangementCheck.class);\n        // Make sure to test with the configurable path!\n        result.addProperty(\"arrangementDefinition\",\n                ArrangementCheck.class.getResource(\"arrangement.txt\").getPath());\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/EnumSetCollectorTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.EnumSet;\nimport java.util.stream.Stream;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * JUnit test case for showing how we can use an EnumSetCollector with a stream of strings. This\n * includes how the ALL tag works, that we're case-insensitive, exceptions are thrown when illegal\n * values are added, and how to declare a subclass of EnumSetCollector\n *\n * @author cstaylor\n */\npublic class EnumSetCollectorTestCase\n{\n    /**\n     * Example enum for testing purposes\n     *\n     * @author cstaylor\n     */\n    private enum Testing\n    {\n        ONE,\n        TWO,\n        THREE;\n    }\n\n    /**\n     * This is how we must declare subclasses of EnumSetCollector, because we need the class for\n     * introspection over the declared enum values.\n     *\n     * @author cstaylor\n     */\n    private static final class TestingEnumSetCollector extends EnumSetCollector<Testing>\n    {\n    }\n\n    @Rule\n    public final ExpectedException exception = ExpectedException.none();\n\n    @Test\n    public void testAllEnumSet()\n    {\n        final EnumSet<Testing> setItems = Stream.of(\"all\").collect(new TestingEnumSetCollector());\n        Assert.assertTrue(setItems.contains(Testing.ONE));\n        Assert.assertTrue(setItems.contains(Testing.TWO));\n        Assert.assertTrue(setItems.contains(Testing.THREE));\n    }\n\n    /**\n     * Anonymous classes require less typing, but you _must_ remember to add those curly braces\n     * after the instantiation. Just ot make sure I've marked EnumSetCollector as abstract so you\n     * can't mistakenly create an instance of EnumSetCollector directly, since the type parameters\n     * are associated with the subclass and not with the instance of the parameterized class.\n     */\n    @Test\n    public void testAnonymousClassAllEnumSet()\n    {\n        final EnumSet<Testing> setItems = Stream.of(\"all\").collect(new EnumSetCollector<Testing>()\n        {\n\n        });\n        Assert.assertTrue(setItems.contains(Testing.ONE));\n        Assert.assertTrue(setItems.contains(Testing.TWO));\n        Assert.assertTrue(setItems.contains(Testing.THREE));\n    }\n\n    @Test\n    public void testBadValueEnumSet()\n    {\n        this.exception.expect(CoreException.class);\n        Stream.of(\"doesntexist\").collect(new TestingEnumSetCollector());\n    }\n\n    @Test\n    public void testPartialInOrderEnumSet()\n    {\n        final EnumSet<Testing> setItems = Stream.of(\"OnE\", \"ThReE\")\n                .collect(new TestingEnumSetCollector());\n        Assert.assertTrue(setItems.contains(Testing.ONE));\n        Assert.assertFalse(setItems.contains(Testing.TWO));\n        Assert.assertTrue(setItems.contains(Testing.THREE));\n    }\n\n    @Test\n    public void testPartialOutOfOrderEnumSet()\n    {\n        final EnumSet<Testing> setItems = Stream.of(\"ThReE\", \"OnE\")\n                .collect(new TestingEnumSetCollector());\n        Assert.assertTrue(setItems.contains(Testing.ONE));\n        Assert.assertFalse(setItems.contains(Testing.TWO));\n        Assert.assertTrue(setItems.contains(Testing.THREE));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/FilteredIterableTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Sam Gass\n */\npublic class FilteredIterableTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(FilteredIterableTest.class);\n\n    @Test\n    public void testComplexIdentifier() throws MalformedURLException\n    {\n        final Function<URL, String> identifier = (final URL urlToIdentify) ->\n        {\n            return urlToIdentify.getProtocol();\n        };\n        final List<URL> urls = new ArrayList<>();\n        urls.add(new URL(\"https://github.com\"));\n        urls.add(new URL(\"http://github.com\"));\n        urls.add(new URL(\"https://test.com\"));\n        urls.add(new URL(\"http://test.com\"));\n        final FilteredIterable<URL, String> filteredIterable = Iterables\n                .filter(Iterables.asIterable(urls), new HashSet<String>(), identifier);\n\n        // Non-destructive streaming filter\n        final Iterable<URL> filtered1 = Iterables.stream(Iterables.asIterable(urls))\n                .filter(Iterables.asSet(Iterables.from(\"http\")), identifier).collect();\n        logger.info(\"{}\", Iterables.asList(filtered1));\n        Assert.assertEquals(2, Iterables.count(filtered1, i -> 1L));\n\n        // No filtering\n        final List<URL> unfiltered = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", unfiltered);\n        Assert.assertEquals(4, unfiltered.size());\n\n        // filter HTTP -- note that this is the result of choosing such a generic identifier method,\n        // even though we invoke addToFilteredSet() on the much more specific \"http://github.com\"\n        filteredIterable.addToFilteredSet(new URL(\"http://github.com\"));\n        final List<URL> filtered2 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered2);\n        Assert.assertEquals(2, filtered2.size());\n\n        // filter HTTPS -- note that this is the result of choosing such a generic identifier\n        // method, even though we invoke addToFilteredSet() on the much more specific\n        // \"https://github.com\"\n        filteredIterable.addToFilteredSet(new URL(\"https://github.com\"));\n        final List<URL> filtered3 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered3);\n        Assert.assertEquals(0, filtered3.size());\n    }\n\n    @Test\n    public void testFilteringSimple()\n    {\n        final List<Integer> values = new ArrayList<>();\n        final Function<Integer, Integer> identifier = (final Integer integer) ->\n        {\n            return integer;\n        };\n        for (int index = 0; index < 10; index++)\n        {\n            values.add(index);\n        }\n\n        // No filtering\n        FilteredIterable<Integer, Integer> filteredIterable = new FilteredIterable<>(\n                Iterables.asIterable(values), new HashSet<Integer>(), identifier);\n        final List<Integer> unfiltered = Iterables.stream(filteredIterable).collectToList();\n\n        logger.info(\"{}\", unfiltered);\n        Assert.assertEquals(10, unfiltered.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)),\n                unfiltered);\n\n        // Filter 5 and 6\n        filteredIterable.addToFilteredSet(new Integer(5));\n        filteredIterable.addToFilteredSet(new Integer(6));\n        final List<Integer> filtered1 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered1);\n        Assert.assertEquals(8, filtered1.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(0, 1, 2, 3, 4, 7, 8, 9)), filtered1);\n\n        // Filter duplicate\n        filteredIterable.addToFilteredSet(new Integer(5));\n        filteredIterable.addToFilteredSet(new Integer(5));\n        final List<Integer> filtered2 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered2);\n        Assert.assertEquals(8, filtered2.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(0, 1, 2, 3, 4, 7, 8, 9)), filtered2);\n\n        // Filter non-existent entries\n        filteredIterable.addToFilteredSet(new Integer(11));\n        filteredIterable.addToFilteredSet(new Integer(12));\n        final List<Integer> filtered3 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered3);\n        Assert.assertEquals(8, filtered3.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(0, 1, 2, 3, 4, 7, 8, 9)), filtered3);\n\n        // Filter down to one\n        filteredIterable.addToFilteredSet(new Integer(0));\n        filteredIterable.addToFilteredSet(new Integer(1));\n        filteredIterable.addToFilteredSet(new Integer(2));\n        filteredIterable.addToFilteredSet(new Integer(3));\n        filteredIterable.addToFilteredSet(new Integer(4));\n        filteredIterable.addToFilteredSet(new Integer(7));\n        filteredIterable.addToFilteredSet(new Integer(9));\n        final List<Integer> filtered4 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered4);\n        Assert.assertEquals(1, filtered4.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(8)), filtered4);\n\n        // Filter all\n        filteredIterable.addToFilteredSet(new Integer(8));\n        final List<Integer> filtered5 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered5);\n        Assert.assertEquals(0, filtered5.size());\n        Assert.assertEquals(Iterables.asList(Iterables.emptyIterable(Integer.class)), filtered5);\n\n        // New Iterable, testing filter of the first value (edge case)\n        filteredIterable = new FilteredIterable<Integer, Integer>(Iterables.asIterable(values),\n                new HashSet<Integer>(), identifier);\n        filteredIterable.addToFilteredSet(new Integer(0));\n        final List<Integer> filtered6 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered6);\n        Assert.assertEquals(9, filtered6.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(1, 2, 3, 4, 5, 6, 7, 8, 9)), filtered6);\n\n        // New Iterable, testing filter of the last value (edge case)\n        filteredIterable = new FilteredIterable<Integer, Integer>(Iterables.asIterable(values),\n                new HashSet<Integer>(), identifier);\n        filteredIterable.addToFilteredSet(new Integer(9));\n        final List<Integer> filtered7 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered7);\n        Assert.assertEquals(9, filtered7.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(0, 1, 2, 3, 4, 5, 6, 7, 8)), filtered7);\n\n        // Empty Iterable (edge case)\n        // New Iterable, testing filter of the first value (edge case)\n        filteredIterable = new FilteredIterable<Integer, Integer>(Iterables.from(0),\n                new HashSet<Integer>(), identifier);\n        filteredIterable.addToFilteredSet(new Integer(0));\n        final List<Integer> filtered8 = Iterables.stream(filteredIterable).collectToList();\n        logger.info(\"{}\", filtered8);\n        Assert.assertEquals(0, filtered8.size());\n        Assert.assertEquals(Iterables.asList(Iterables.emptyIterable(Integer.class)), filtered8);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/FixedSizePriorityQueueTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Comparator;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author tony\n */\npublic class FixedSizePriorityQueueTest\n{\n    @Test\n    public void testComparable()\n    {\n        final Comparator<Integer> reversed = (first, second) -> first >= second ? -1 : 1;\n        final FixedSizePriorityQueue<Integer> queue = new FixedSizePriorityQueue<>(1, reversed);\n\n        queue.add(-1);\n        queue.add(3);\n        Assert.assertEquals(\"The size of queue should be 1\", 1, queue.size());\n        Assert.assertTrue(\"The queue should contain -1\", queue.contains(-1));\n\n        queue.add(-3);\n        Assert.assertEquals(\"The size of queue should be 1\", 1, queue.size());\n        Assert.assertTrue(\"The queue should contain -3\", queue.contains(-3));\n    }\n\n    @Test\n    public void testSize()\n    {\n        final FixedSizePriorityQueue<Integer> queue = new FixedSizePriorityQueue<>(3);\n\n        queue.add(5);\n        queue.add(1);\n        queue.add(2);\n        Assert.assertEquals(\"The size of queue should be 3\", 3, queue.size());\n        Assert.assertTrue(\"The queue should contain 1\", queue.contains(1));\n\n        queue.add(6);\n        Assert.assertEquals(\"The size of queue should still be 3\", 3, queue.size());\n        Assert.assertFalse(\"The queue should not contain 1\", queue.contains(1));\n        Assert.assertTrue(\"The queue should contain 6\", queue.contains(6));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/IterablesAddAllTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Test cases for the {@link Iterables#addAll(java.util.Collection, Iterable)} method\n *\n * @author cstaylor\n */\npublic class IterablesAddAllTestCase\n{\n    @Test\n    public void testAddNone()\n    {\n        Assert.assertFalse(Iterables.addAll(new ArrayList<>(), Iterables.from()));\n    }\n\n    @Test\n    public void testAddOne()\n    {\n        Assert.assertTrue(Iterables.addAll(new ArrayList<>(), Iterables.from(\"Test\")));\n    }\n\n    @Test\n    public void testSameToList()\n    {\n        final List<String> items = Iterables.asList(new String[] { \"Test\" });\n        Assert.assertTrue(Iterables.addAll(items, Iterables.from(\"Test\")));\n    }\n\n    @Test\n    public void testSameToSet()\n    {\n        final Set<String> items = Iterables.asSet(new String[] { \"Test\" });\n        Assert.assertFalse(Iterables.addAll(items, Iterables.from(\"Test\")));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/IterablesTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.NoSuchElementException;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class IterablesTest\n{\n    @Test\n    public void testCount()\n    {\n        final Iterable<Integer> iterable = () -> new Iterator<Integer>()\n        {\n            private int index = 0;\n\n            @Override\n            public boolean hasNext()\n            {\n                return this.index < 100;\n            }\n\n            @Override\n            public Integer next()\n            {\n                return this.index++;\n            }\n        };\n        Assert.assertEquals(200, Iterables.count(iterable, integer -> 2L));\n\n        final List<Integer> list = Iterables.asList(iterable);\n        Assert.assertEquals(300, Iterables.count(list, integer -> 3L));\n    }\n\n    @Test\n    public void testFilter()\n    {\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n        input.add(null);\n        input.add(3);\n        final List<Integer> outputNonNull = Iterables\n                .asList(Iterables.filter(input, in -> in != null));\n        Assert.assertEquals(2, outputNonNull.size());\n        final List<Integer> outputNull = Iterables\n                .asList(Iterables.filter(input, in -> in == null));\n        Assert.assertEquals(1, outputNull.size());\n        final List<Integer> outputNotThree = Iterables\n                .asList(Iterables.filter(input, in -> !new Integer(3).equals(in)));\n        Assert.assertEquals(2, outputNotThree.size());\n    }\n\n    @Test\n    public void testFirst()\n    {\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n        input.add(2);\n        input.add(3);\n\n        Assert.assertTrue(Iterables.first(input).isPresent());\n        Assert.assertEquals(input.get(0), Iterables.first(input).get());\n        Assert.assertFalse(Iterables.first(new ArrayList<>()).isPresent());\n    }\n\n    @Test\n    public void testFirstMatching()\n    {\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n        input.add(2);\n        input.add(3);\n        final Optional<Integer> firstMatching = Iterables.firstMatching(input,\n                integer -> integer == 2);\n\n        Assert.assertTrue(firstMatching.isPresent());\n        Assert.assertEquals(input.get(1), firstMatching.get());\n        Assert.assertFalse(Iterables.firstMatching(new ArrayList<>(), value -> true).isPresent());\n    }\n\n    @Test\n    public void testHead()\n    {\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n        input.add(2);\n        input.add(3);\n\n        Assert.assertNotNull(Iterables.head(input));\n        Assert.assertEquals(input.get(0), Iterables.head(input));\n        Assert.assertNull(Iterables.head(Collections.emptyList()));\n    }\n\n    @Test\n    public void testIndexBased()\n    {\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n        input.add(2);\n        input.add(3);\n\n        final Iterable<Integer> indexBased = Iterables.indexBasedIterable(input.size(),\n                index -> input.get((int) index));\n\n        Assert.assertEquals(3, Iterables.size(indexBased));\n    }\n\n    @Test(expected = NoSuchElementException.class)\n    public void testIndexBasedError()\n    {\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n\n        final Iterable<Integer> indexBased = Iterables.indexBasedIterable(input.size(),\n                index -> input.get((int) index));\n        final Iterator<Integer> iterator = indexBased.iterator();\n        final int initialValue = iterator.next();\n        Assert.assertEquals(1L, initialValue);\n        iterator.next();\n    }\n\n    @Test\n    public void testIsEmpty()\n    {\n        final List<Integer> input = new ArrayList<>();\n        Assert.assertTrue(\"List is empty to start\", Iterables.isEmpty(input));\n        input.add(1);\n        Assert.assertFalse(\"List is not empty\", Iterables.isEmpty(input));\n        input.remove(0);\n        Assert.assertTrue(\"List is empty to end\", Iterables.isEmpty(input));\n    }\n\n    @Test\n    public void testJoin()\n    {\n        final List<Integer> rebuilt = Iterables.asList(\n                Iterables.join(1, Iterables.join(2, Iterables.join(3, Collections.emptyList()))));\n\n        Assert.assertArrayEquals(new Integer[] { 1, 2, 3 }, rebuilt.toArray());\n    }\n\n    @Test\n    public void testLast()\n    {\n        // As list\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n        input.add(2);\n        input.add(3);\n\n        Assert.assertTrue(Iterables.last(input).isPresent());\n        Assert.assertEquals(input.get(2), Iterables.last(input).get());\n        Assert.assertFalse(Iterables.last(new ArrayList<>()).isPresent());\n\n        // As iterable\n        final Iterable<Integer> input2 = Iterables.stream(input);\n        Assert.assertTrue(Iterables.last(input2).isPresent());\n        Assert.assertEquals(input.get(2), Iterables.last(input2).get());\n        Assert.assertFalse(Iterables.last(Collections.emptyList()).isPresent());\n    }\n\n    @Test\n    public void testLastMatching()\n    {\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n        input.add(2);\n        input.add(3);\n        final Optional<Integer> lastMatching = Iterables.lastMatching(input,\n                integer -> integer == 2);\n\n        Assert.assertTrue(lastMatching.isPresent());\n        Assert.assertEquals(input.get(1), lastMatching.get());\n        Assert.assertFalse(Iterables.lastMatching(new ArrayList<>(), value -> true).isPresent());\n    }\n\n    @Test\n    public void testSize()\n    {\n        final Iterable<Integer> iterable = () -> new Iterator<Integer>()\n        {\n            private int index = 0;\n\n            @Override\n            public boolean hasNext()\n            {\n                return this.index < 100;\n            }\n\n            @Override\n            public Integer next()\n            {\n                return this.index++;\n            }\n        };\n        Assert.assertEquals(100, Iterables.size(iterable));\n        final List<Integer> list = Iterables.asList(iterable);\n        Assert.assertEquals(100, Iterables.size(list));\n    }\n\n    @Test\n    public void testTail()\n    {\n        final List<Integer> input = new ArrayList<>();\n        input.add(1);\n        input.add(2);\n        input.add(3);\n\n        final List<Integer> tailOfTwo = Iterables.asList(Iterables.tail(input));\n        Assert.assertArrayEquals(new Integer[] { 2, 3 }, tailOfTwo.toArray());\n\n        final List<Integer> tailOfOne = Iterables.asList(Iterables.tail(tailOfTwo));\n        Assert.assertArrayEquals(new Integer[] { 3 }, tailOfOne.toArray());\n\n        final List<Integer> tailOfNone = Iterables.asList(Iterables.tail(tailOfOne));\n        Assert.assertTrue(tailOfNone.isEmpty());\n\n        final List<Integer> pastTail = Iterables.asList(Iterables.tail(tailOfNone));\n        Assert.assertTrue(pastTail.isEmpty());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/MapsTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic class MapsTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testWithMaps()\n    {\n        final Map<String, String> map1 = Maps.hashMap(\"key1\", \"value1\");\n        final Map<String, String> map2 = Maps.hashMap(\"key2\", \"value2\");\n        final Map<String, String> result = Maps.hashMap(\"key1\", \"value1\", \"key2\", \"value2\");\n\n        Assert.assertEquals(result, Maps.withMaps(true, map1, map2));\n    }\n\n    @Test\n    public void testWithMapsCollision()\n    {\n        final Map<String, String> map1 = Maps.hashMap(\"key\", \"value1\");\n        final Map<String, String> map2 = Maps.hashMap(\"key\", \"value2\");\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Cannot merge maps! Collision on key.\");\n\n        Maps.withMaps(true, map1, map2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/MultiIterableTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class MultiIterableTest\n{\n    @Test\n    public void multiIterableTest()\n    {\n        final List<Integer> list1 = new ArrayList<>();\n        final List<Integer> list2 = new ArrayList<>();\n        list1.add(1);\n        list1.add(2);\n        list2.add(3);\n        list2.add(4);\n        list2.add(5);\n\n        final MultiIterable<Integer> multi = new MultiIterable<>(list1, list2);\n        final Iterator<Integer> iterator = multi.iterator();\n        Assert.assertEquals(Integer.valueOf(1), iterator.next());\n        Assert.assertEquals(Integer.valueOf(2), iterator.next());\n        Assert.assertEquals(Integer.valueOf(3), iterator.next());\n        Assert.assertEquals(Integer.valueOf(4), iterator.next());\n        Assert.assertEquals(Integer.valueOf(5), iterator.next());\n        Assert.assertFalse(iterator.hasNext());\n        Assert.assertNull(iterator.next());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/OptionalIterableTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author cuthbertm\n */\npublic class OptionalIterableTest\n{\n    @Test\n    public void iterationTest()\n    {\n        final List<Optional<Integer>> list = new ArrayList<>();\n        list.add(Optional.of(1));\n        list.add(Optional.of(2));\n        list.add(Optional.empty());\n        list.add(Optional.of(3));\n        list.add(Optional.empty());\n        list.add(Optional.empty());\n        list.add(Optional.of(4));\n        list.add(Optional.empty());\n\n        final OptionalIterable<Integer> optionalIterable = new OptionalIterable<>(list);\n        final Iterator<Integer> iterator = optionalIterable.iterator();\n        Assert.assertTrue(iterator.hasNext());\n        Assert.assertEquals(Integer.valueOf(1), iterator.next());\n        Assert.assertTrue(iterator.hasNext());\n        Assert.assertTrue(iterator.hasNext());\n        Assert.assertTrue(iterator.hasNext());\n        Assert.assertEquals(Integer.valueOf(2), iterator.next());\n        Assert.assertTrue(iterator.hasNext());\n        Assert.assertEquals(Integer.valueOf(3), iterator.next());\n        Assert.assertEquals(Integer.valueOf(4), iterator.next());\n        Assert.assertFalse(iterator.hasNext());\n        Assert.assertNull(iterator.next());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/ParallelIterableTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author cuthbertm\n */\npublic class ParallelIterableTest\n{\n    @Test\n    public void testDifferentObjectTypes()\n    {\n        final List<Long> firstThree = Arrays.asList(1L, 2L, 3L);\n        final List<String> secondThree = Arrays.asList(\"test1\", \"test2\", \"test3\");\n        final List<Integer> thirdThree = Arrays.asList(7, 8, 9);\n        final ParallelIterable parallel = new ParallelIterable(firstThree, secondThree, thirdThree);\n        final Iterator<JoinedCollection> iterator = parallel.iterator();\n        int indexCounter = 0;\n        while (iterator.hasNext())\n        {\n            final JoinedCollection joined = iterator.next();\n            Assert.assertEquals(firstThree.get(indexCounter), joined.get(0));\n            Assert.assertEquals(secondThree.get(indexCounter), joined.get(1));\n            Assert.assertEquals(thirdThree.get(indexCounter++), joined.get(2));\n        }\n    }\n\n    @Test\n    public void testMultipleLongTypes()\n    {\n        final List<Long> firstThree = Arrays.asList(1L, 2L, 3L);\n        final List<Long> secondThree = Arrays.asList(4L, 5L, 6L);\n        final List<Long> thirdThree = Arrays.asList(7L, 8L, 9L);\n        final ParallelIterable parallel = new ParallelIterable(firstThree, secondThree, thirdThree);\n        final Iterator<JoinedCollection> iterator = parallel.iterator();\n        int indexCounter = 0;\n        while (iterator.hasNext())\n        {\n            final JoinedCollection joined = iterator.next();\n            Assert.assertEquals(firstThree.get(indexCounter), joined.get(0));\n            Assert.assertEquals(secondThree.get(indexCounter), joined.get(1));\n            Assert.assertEquals(thirdThree.get(indexCounter++), joined.get(2));\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Test\n    public void testUnevenLists()\n    {\n        final List<Long> firstThree = Arrays.asList(1L, 2L, 3L, 12L, 45L);\n        final List<Long> secondThree = Arrays.asList(4L, 5L, 6L);\n        final List<Long> thirdThree = Arrays.asList(7L, 8L, 9L, 10L);\n        final ParallelIterable parallel = new ParallelIterable(firstThree, secondThree, thirdThree);\n        final Iterator<JoinedCollection> iterator = parallel.iterator();\n        int indexCounter = 0;\n        while (iterator.hasNext())\n        {\n            final JoinedCollection joined = iterator.next();\n            Assert.assertEquals(Optional.of(firstThree.get(indexCounter)), joined.getOption(0));\n            if (indexCounter > 2)\n            {\n                Assert.assertEquals(Optional.empty(), joined.getOption(1));\n            }\n            else\n            {\n                Assert.assertEquals(Optional.of(secondThree.get(indexCounter)),\n                        joined.getOption(1));\n            }\n            if (indexCounter > 3)\n            {\n                Assert.assertEquals(null, joined.get(2));\n            }\n            else\n            {\n                Assert.assertEquals(Optional.of(thirdThree.get(indexCounter++)),\n                        joined.getOption(2));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/SetsTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * @author matthieun\n */\npublic class SetsTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testWithSets()\n    {\n        final Set<String> set1 = Sets.hashSet(\"key1\");\n        final Set<String> set2 = Sets.hashSet(\"key2\");\n        final Set<String> result = Sets.hashSet(\"key1\", \"key2\");\n\n        Assert.assertEquals(result, Sets.withSets(true, set1, set2));\n    }\n\n    @Test\n    public void testWithSetsCollision()\n    {\n        final Set<String> set1 = Sets.hashSet(\"key\");\n        final Set<String> set2 = Sets.hashSet(\"key\");\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Cannot merge sets! Collision on element.\");\n\n        Sets.withSets(true, set1, set2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/ShardBucketCollectionTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\nimport org.openstreetmap.atlas.utilities.testing.FreezeDryFunction;\n\n/**\n * Test for example {@link ShardBucketCollection}s\n *\n * @author jklamer\n */\npublic class ShardBucketCollectionTest\n{\n\n    @Test\n    public void testMultiPolygonSort()\n    {\n        final Rectangle maxBounds = SlippyTile.forName(\"1-0-0\").bounds()\n                .contract(Distance.ONE_METER);\n\n        ShardBucketCollectionTestClasses.MultiPolygonSort multiPolygonSort = new ShardBucketCollectionTestClasses.MultiPolygonSort(\n                maxBounds, 4);\n\n        final MultiPolygon firstShardMultiPolygon = this\n                .multiPolygonForShard(SlippyTile.forName(\"4-0-0\"));\n        final MultiPolygon secondShardMultiPolygon = this\n                .multiPolygonForShard(SlippyTile.forName(\"4-1-0\"));\n        final MultiPolygon combinedMultiPolygon = firstShardMultiPolygon\n                .merge(secondShardMultiPolygon);\n\n        // the combined multipolygon will be sorted into the two different buckets as the original\n        // multipolygons\n        multiPolygonSort.add(combinedMultiPolygon);\n        Assert.assertTrue(multiPolygonSort.contains(firstShardMultiPolygon));\n        Assert.assertTrue(multiPolygonSort.contains(secondShardMultiPolygon));\n\n        // test serialization\n        multiPolygonSort = new FreezeDryFunction<ShardBucketCollectionTestClasses.MultiPolygonSort>()\n                .apply(multiPolygonSort);\n        Assert.assertTrue(multiPolygonSort.contains(firstShardMultiPolygon));\n        Assert.assertTrue(multiPolygonSort.contains(secondShardMultiPolygon));\n\n        // make a multipolygon with an outer for every shard and ensure they all get sorted into\n        // their proper buckets\n        multiPolygonSort.clear();\n        multiPolygonSort.add(this.multiPolygonForShards(SlippyTile.allTiles(4, maxBounds)));\n        Assert.assertEquals(64, multiPolygonSort.size());\n        for (final Shard shard : SlippyTile.allTiles(4, maxBounds))\n        {\n            Assert.assertTrue(multiPolygonSort.getBucketCollectionForShard(shard).map(List::stream)\n                    .map(multiPolygonStream -> multiPolygonStream.allMatch(\n                            multiPolygon -> multiPolygon.equals(this.multiPolygonForShard(shard))))\n                    .orElse(false));\n        }\n        Assert.assertEquals(64L,\n                multiPolygonSort.getAllShardBucketCollectionPairs().entrySet().size());\n\n        // make some big multipolygons that will go in multiple buckets but still touch all the\n        // buckets\n        multiPolygonSort.clear();\n        multiPolygonSort.add(this.multiPolygonForShards(SlippyTile.allTiles(2, maxBounds)));\n        Assert.assertEquals(64, multiPolygonSort.size());\n        Assert.assertEquals(4, multiPolygonSort.distinctStream().count());\n    }\n\n    @Test\n    public void testShardResolution()\n    {\n        final Rectangle maxBounds = SlippyTile.forName(\"1-0-0\").bounds()\n                .contract(Distance.ONE_METER);\n\n        // collection with implemented shard resolution strategy\n        final ShardBucketCollectionTestClasses.PolyLineBucketedAtStartLocation startLocationBuckets = new ShardBucketCollectionTestClasses.PolyLineBucketedAtStartLocation(\n                maxBounds, 3);\n        final Iterable<SlippyTile> shardsWithinBounds = SlippyTile.allTiles(3, maxBounds);\n\n        // a polyline that will cover all 16 buckets but go into the first one\n        final PolyLine allBucketsPolyline = this.polylineForShards(shardsWithinBounds);\n\n        startLocationBuckets.add(allBucketsPolyline);\n        Assert.assertEquals(1, startLocationBuckets.size());\n        Assert.assertTrue(\n                startLocationBuckets.getBucketCollectionForShard(SlippyTile.forName(\"3-0-0\"))\n                        .map(collection -> collection.contains(allBucketsPolyline)).orElse(false));\n        startLocationBuckets.clear();\n\n        // adding reversed polyline changes bucket it is stored in\n        final PolyLine testLineAccrossBuckets = this.polylineForShards(\"3-0-0,3-3-3\");\n        startLocationBuckets.add(testLineAccrossBuckets);\n        Assert.assertTrue(startLocationBuckets\n                .getBucketCollectionForShard(SlippyTile.forName(\"3-0-0\"))\n                .map(collection -> collection.contains(testLineAccrossBuckets)).orElse(false));\n        startLocationBuckets.add(testLineAccrossBuckets.reversed());\n        Assert.assertTrue(\n                startLocationBuckets.getBucketCollectionForShard(SlippyTile.forName(\"3-3-3\"))\n                        .map(collection -> collection.contains(testLineAccrossBuckets.reversed()))\n                        .orElse(false));\n        startLocationBuckets.clear();\n\n        // add a polyline whose start location intersects with 4 shards and gets resolved to the\n        // first as decided by the resolveShard method and slippy tile comparator\n        final ArrayList<Location> locations = new ArrayList<>();\n        locations.addAll(SlippyTile.forName(\"3-0-0\").bounds());\n        locations.retainAll(SlippyTile.forName(\"3-1-1\").bounds());\n        // at the corner of both shards\n        final Location startLocation = locations.get(0);\n        locations.clear();\n        locations.addAll(SlippyTile.forName(\"3-1-1\").bounds());\n        locations.retainAll(SlippyTile.forName(\"3-2-0\").bounds());\n        // across to another corner\n        final Location endLocation = locations.get(0);\n\n        final PolyLine onTheEdges = new PolyLine(Arrays.asList(startLocation, endLocation));\n        startLocationBuckets.add(onTheEdges);\n        Assert.assertEquals(1, startLocationBuckets.size());\n        Assert.assertTrue(\n                startLocationBuckets.getBucketCollectionForShard(SlippyTile.forName(\"3-0-0\"))\n                        .map(collection -> collection.contains(onTheEdges)).orElse(false));\n\n    }\n\n    @Test\n    public void testSimplePolylineCollectionFunctionality()\n    {\n        final Rectangle maxBounds = SlippyTile.forName(\"1-0-0\").bounds()\n                .contract(Distance.ONE_METER);\n\n        final Iterable<SlippyTile> shardsWithinBounds = SlippyTile.allTiles(3, maxBounds);\n        // a polyline that will go into all 16 buckets\n        final PolyLine allBucketsPolyline = this.polylineForShards(shardsWithinBounds);\n        // create Zoom level three buckets within the bounds of the first zoom = 1 slippy tile\n        final ShardBucketCollectionTestClasses.PolylineBuckets polylineBuckets1 = new ShardBucketCollectionTestClasses.PolylineBuckets(\n                maxBounds, 3);\n\n        // test add, contains, size, stream\n        polylineBuckets1.add(allBucketsPolyline);\n        Assert.assertTrue(polylineBuckets1.contains(allBucketsPolyline));\n        Assert.assertFalse(polylineBuckets1.contains(new Integer(0)));\n        Assert.assertFalse(polylineBuckets1.contains(null));\n        Assert.assertTrue(polylineBuckets1.getAllBucketCollections()\n                .allMatch(bucket -> bucket.contains(allBucketsPolyline)));\n        Assert.assertEquals(16L, polylineBuckets1.stream().count());\n        Assert.assertTrue(polylineBuckets1.stream().allMatch(allBucketsPolyline::equals));\n        Assert.assertEquals(16L, polylineBuckets1.size());\n        Assert.assertEquals(polylineBuckets1.size(), polylineBuckets1.toArray().length);\n        Assert.assertEquals(1L, polylineBuckets1.distinctStream().count());\n\n        polylineBuckets1.clear();\n        Assert.assertEquals(0L, polylineBuckets1.size());\n        Assert.assertFalse(polylineBuckets1.iterator().hasNext());\n\n        polylineBuckets1.add(allBucketsPolyline);\n        polylineBuckets1.remove(allBucketsPolyline);\n        Assert.assertTrue(polylineBuckets1.isEmpty());\n\n        // Test with bucket unique items\n        // reinitialize\n        final ShardBucketCollectionTestClasses.PolylineBuckets polylineBuckets2 = new ShardBucketCollectionTestClasses.PolylineBuckets(\n                maxBounds, 3);\n\n        final PolyLine firstBucketPolyline = this\n                .polylineForShards(Iterables.first(shardsWithinBounds).get().split(4));\n        final PolyLine lastBucketPolyline = this\n                .polylineForShards(Iterables.last(shardsWithinBounds).get().split(4));\n\n        // test getting buckets\n        polylineBuckets2.add(firstBucketPolyline);\n        polylineBuckets2.add(lastBucketPolyline);\n        Assert.assertFalse(polylineBuckets2.isEmpty());\n        Assert.assertEquals(2, polylineBuckets2.size());\n        Assert.assertTrue(\"Bucket does not exist or doesn't contain polyline\",\n                polylineBuckets2.getBucketCollectionForShard(SlippyTile.forName(\"3-0-0\"))\n                        .map(collection -> collection.contains(firstBucketPolyline)).orElse(false));\n        Assert.assertTrue(\n                polylineBuckets2.getBucketCollectionsForBounds(lastBucketPolyline.bounds())\n                        .allMatch(collection -> collection.contains(lastBucketPolyline)));\n\n        // outside of bounds shard\n        Assert.assertFalse(polylineBuckets2.getBucketCollectionForShard(SlippyTile.forName(\"3-4-0\"))\n                .isPresent());\n        // wrong zoom shard\n        Assert.assertFalse(polylineBuckets2.getBucketCollectionForShard(SlippyTile.forName(\"4-0-0\"))\n                .isPresent());\n\n        // test remove\n        Assert.assertTrue(polylineBuckets2\n                .containsAll(Arrays.asList(firstBucketPolyline, lastBucketPolyline)));\n        Assert.assertTrue(\n                polylineBuckets2.removeAll(Arrays.asList(firstBucketPolyline, lastBucketPolyline)));\n        Assert.assertFalse(polylineBuckets2.remove(firstBucketPolyline));\n        Assert.assertFalse(polylineBuckets2.remove(new Integer(0)));\n        Assert.assertFalse(polylineBuckets2\n                .containsAll(Arrays.asList(firstBucketPolyline, lastBucketPolyline)));\n\n        // test retain\n        Assert.assertTrue(\n                polylineBuckets2.addAll(Arrays.asList(firstBucketPolyline, lastBucketPolyline)));\n        Assert.assertFalse(\n                polylineBuckets2.retainAll(Arrays.asList(firstBucketPolyline, lastBucketPolyline)));\n        Assert.assertTrue(polylineBuckets2\n                .containsAll(Arrays.asList(firstBucketPolyline, lastBucketPolyline)));\n        Assert.assertTrue(polylineBuckets2.retainAll(Arrays.asList(firstBucketPolyline)));\n        Assert.assertTrue(polylineBuckets2.containsAll(Arrays.asList(firstBucketPolyline)));\n\n        // test array transfer\n        polylineBuckets1.clear();\n        polylineBuckets2.clear();\n        polylineBuckets1.add(firstBucketPolyline);\n        polylineBuckets2.addAll(Arrays.asList(firstBucketPolyline, lastBucketPolyline));\n        final Object[] firstCollectionArray = polylineBuckets1.toArray();\n        final PolyLine[] array1 = new PolyLine[] { firstBucketPolyline };\n        Assert.assertArrayEquals(array1, firstCollectionArray);\n        final PolyLine[] array2 = new PolyLine[] { firstBucketPolyline, firstBucketPolyline,\n                lastBucketPolyline };\n        final PolyLine[] allArray = polylineBuckets2.toArray(array1);\n        Assert.assertArrayEquals(array2, allArray);\n\n        // get code coverage\n        Assert.assertEquals(maxBounds, polylineBuckets1.getMaximumBounds());\n    }\n\n    @Test(expected = UnsupportedOperationException.class)\n    public void testUnimplemented()\n    {\n        final Rectangle maxBounds = SlippyTile.forName(\"1-0-0\").bounds()\n                .contract(Distance.ONE_METER);\n\n        // collection without implemented resolve shard strategy\n        final ShardBucketCollectionTestClasses.UnsupportedOperation unsupportedOperation = new ShardBucketCollectionTestClasses.UnsupportedOperation(\n                maxBounds, 3);\n        // a polyline that will intersect with multiple buckets\n        final PolyLine testPolyLine1 = this.polylineForShards(SlippyTile.allTiles(3, maxBounds));\n        unsupportedOperation.add(testPolyLine1);\n    }\n\n    /**\n     * Multipolygon for shard with outer smaller than the bounds\n     */\n    private MultiPolygon multiPolygonForShard(final Shard shard)\n    {\n        final Rectangle bounds = shard.bounds();\n        final Rectangle outer = bounds.contract(Distance.ONE_METER);\n        final Rectangle inner = outer.contract(Distance.ONE_METER);\n        final MultiMap<Polygon, Polygon> multiMap = new MultiMap<>();\n        multiMap.put(outer, Arrays.asList(inner));\n        return new MultiPolygon(multiMap);\n    }\n\n    /**\n     * multipolygon that resides in all shards. With an outer in each shard\n     */\n    private MultiPolygon multiPolygonForShards(final Iterable<? extends Shard> shards)\n    {\n        MultiPolygon toReturn = this.multiPolygonForShard(Iterables.head(shards));\n        for (final Shard shard : Iterables.tail(shards))\n        {\n            toReturn = toReturn.merge(this.multiPolygonForShard(shard));\n        }\n        return toReturn;\n    }\n\n    /**\n     * Polyline for csv SlippyTile names\n     */\n    private PolyLine polylineForShards(final String names)\n    {\n        return polylineForShards(Arrays.stream(names.split(\",\")).map(SlippyTile::forName)\n                .collect(Collectors.toList()));\n    }\n\n    /**\n     * Construct a polyline from the centers of the provided shards\n     */\n    private PolyLine polylineForShards(final Iterable<? extends Shard> shards)\n    {\n        return new PolyLine(\n                new StreamIterable<>(shards).map(tile -> tile.bounds().center()).collectToList());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/ShardBucketCollectionTestClasses.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\n\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.sharding.Shard;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\n\n/**\n * Class container for test classes\n *\n * @author jklamer\n */\npublic class ShardBucketCollectionTestClasses\n{\n    /**\n     * Collection that will handle splitting multipolygons into buckets by overriding the add\n     * function\n     */\n    public static class MultiPolygonSort\n            extends ShardBucketCollection<MultiPolygon, ArrayList<MultiPolygon>>\n    {\n        public MultiPolygonSort(final Rectangle maxBounds, final Integer zoomLevel)\n        {\n            super(maxBounds, zoomLevel);\n        }\n\n        /**\n         * This function only puts the part of the multipolygon with outers intersecting with the\n         * shard\n         *\n         * @param item\n         *            to add\n         * @param collection\n         *            to add to\n         * @param shard\n         *            shard associated with the collection\n         * @return whether it was added successfully\n         */\n        @Override\n        protected boolean addFunction(final MultiPolygon item,\n                final ArrayList<MultiPolygon> collection, final Shard shard)\n        {\n            final Rectangle shardBounds = shard.bounds();\n            final MultiMap<Polygon, Polygon> newMultiPolygon = new MultiMap<>();\n            item.outers().forEach(outer ->\n            {\n                if (outer.overlaps(shardBounds))\n                {\n                    newMultiPolygon.put(outer, item.innersOf(outer));\n                }\n            });\n            return collection.add(new MultiPolygon(newMultiPolygon));\n        }\n\n        @Override\n        protected boolean allowMultipleBucketInsertion()\n        {\n            return true;\n        }\n\n        @Override\n        protected ArrayList<MultiPolygon> initializeBucketCollection()\n        {\n            return new ArrayList<>();\n        }\n    }\n\n    /**\n     * Class where the polylines are put at the first bucket whose bounds overlap with the start\n     */\n    public static class PolyLineBucketedAtStartLocation\n            extends ShardBucketCollection<PolyLine, HashSet<PolyLine>>\n    {\n        public PolyLineBucketedAtStartLocation(final Rectangle maxBounds, final Integer zoomLevel)\n        {\n            super(maxBounds, zoomLevel);\n        }\n\n        @Override\n        protected boolean allowMultipleBucketInsertion()\n        {\n            return false;\n        }\n\n        @Override\n        protected HashSet<PolyLine> initializeBucketCollection()\n        {\n            return new HashSet<>();\n        }\n\n        /**\n         * Choose the first shard that overlaps the start point of the PolyLine as sorted by the\n         * SlippyTile class\n         *\n         * @param item\n         *            located item to insert\n         * @param possibleBuckets\n         *            possible buckets for the item to work into\n         * @return\n         */\n        @Override\n        protected Shard resolveShard(final PolyLine item,\n                final List<? extends Shard> possibleBuckets)\n        {\n            final Location start = Iterables.head(item);\n            return possibleBuckets.stream().map(shard -> (SlippyTile) shard)\n                    .filter(shard -> shard.bounds().fullyGeometricallyEncloses(start)).sorted()\n                    .findFirst().get();\n        }\n    }\n\n    /**\n     * Basic implementation for testing core functionality\n     */\n    public static class PolylineBuckets extends ShardBucketCollection<PolyLine, HashSet<PolyLine>>\n    {\n        public PolylineBuckets(final Rectangle maxBounds, final Integer zoomLevel)\n        {\n            super(maxBounds, zoomLevel);\n        }\n\n        @Override\n        protected boolean allowMultipleBucketInsertion()\n        {\n            return true;\n        }\n\n        @Override\n        protected HashSet<PolyLine> initializeBucketCollection()\n        {\n            return new HashSet<>();\n        }\n    }\n\n    /**\n     * Unimplemented resolve shard\n     */\n    public static class UnsupportedOperation\n            extends ShardBucketCollection<PolyLine, HashSet<PolyLine>>\n    {\n        public UnsupportedOperation(final Rectangle maxBounds, final Integer zoomLevel)\n        {\n            super(maxBounds, zoomLevel);\n        }\n\n        @Override\n        protected boolean allowMultipleBucketInsertion()\n        {\n            return false;\n        }\n\n        @Override\n        protected HashSet<PolyLine> initializeBucketCollection()\n        {\n            return new HashSet<>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/StreamIterableTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport static java.util.Arrays.asList;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.IntStream;\n\nimport org.junit.Assert;\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.time.Time;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Sagar Rohankar\n * @author mgostintsev\n */\npublic class StreamIterableTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(StreamIterableTest.class);\n\n    @Test\n    public void testAllMatch() throws Exception\n    {\n        final StreamIterable<Integer> streamIterable = new StreamIterable<>(asList(1, 2, 3, 4));\n\n        // true -> all numbers are less than 5\n        Assert.assertTrue(streamIterable.allMatch(n -> n < 5));\n        // false -> all numbers are even\n        Assert.assertFalse(streamIterable.allMatch(n -> n % 2 == 0));\n    }\n\n    @Test\n    public void testAllMatchParallel() throws Exception\n    {\n        final StreamIterable<Integer> streamIterable = new StreamIterable<>(asList(1, 2, 3, 4),\n                true);\n\n        // true -> all numbers are less than 5\n        Assert.assertTrue(streamIterable.allMatch(n -> n < 5));\n        // false -> all numbers are even\n        Assert.assertFalse(streamIterable.allMatch(n -> n % 2 == 0));\n    }\n\n    @Test\n    public void testAnyMatch()\n    {\n        final StreamIterable<Integer> streamIterable = new StreamIterable<>(asList(6, 12, 18));\n\n        // true -> any number that is divisible by 9\n        Assert.assertTrue(streamIterable.anyMatch(n -> n % 9 == 0));\n        // false -> any number that is divisible by 5\n        Assert.assertFalse(streamIterable.allMatch(n -> n % 5 == 0));\n    }\n\n    @Test\n    public void testAnyMatchParallel()\n    {\n        final StreamIterable<Integer> streamIterable = new StreamIterable<>(asList(6, 12, 18),\n                true);\n\n        // true -> any number that is divisible by 9\n        Assert.assertTrue(streamIterable.anyMatch(n -> n % 9 == 0));\n        // false -> any number that is divisible by 5\n        Assert.assertFalse(streamIterable.allMatch(n -> n % 5 == 0));\n    }\n\n    @Ignore\n    @Test\n    public void testParallelPerformance()\n    {\n        final List<Integer> numbers = new ArrayList<>();\n        IntStream.range(0, 10000000).forEach(number ->\n        {\n            numbers.add(number);\n        });\n        final StreamIterable<Integer> streamIterable = new StreamIterable<>(numbers)\n                .disableParallelization();\n        Time currentTime = Time.now();\n        streamIterable.anyMatch(n -> n > 100000);\n        final Duration sequentialDuration = currentTime.elapsedSince();\n        logger.debug(\"Sequential duration was {} ms\", sequentialDuration.asMilliseconds());\n\n        currentTime = Time.now();\n        streamIterable.enableParallelization();\n        streamIterable.anyMatch(n -> n > 100000);\n        final Duration parallelDuration = currentTime.elapsedSince();\n        logger.debug(\"Parallel duration was {} ms\", parallelDuration.asMilliseconds());\n        Assert.assertTrue(parallelDuration.isLessThan(sequentialDuration));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/StringListTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author tony\n * @author mgostintsev\n */\npublic class StringListTest\n{\n    @Test\n    public void testSplit()\n    {\n        final String withTwoEquals = \"key1=value1&key2=value2\";\n        final StringList fullySplit = StringList.split(withTwoEquals, \"=\");\n        final StringList partialSplit1 = StringList.split(withTwoEquals, \"=\", 1);\n        final StringList partialSplit2 = StringList.split(withTwoEquals, \"=\", 2);\n        final StringList partialSplit3 = StringList.split(withTwoEquals, \"=\", 3);\n        final StringList partialSplit4 = StringList.split(withTwoEquals, \"=\", 4);\n\n        Assert.assertEquals(3, fullySplit.size());\n        Assert.assertEquals(1, partialSplit1.size());\n        Assert.assertEquals(withTwoEquals, partialSplit1.get(0));\n        Assert.assertEquals(2, partialSplit2.size());\n        Assert.assertEquals(\"value1&key2=value2\", partialSplit2.get(1));\n        Assert.assertEquals(3, partialSplit3.size());\n        Assert.assertEquals(3, partialSplit4.size());\n    }\n\n    @Test\n    public void testSplittingWhiteSpace()\n    {\n        final String withSpaces = \"separated by spaces\";\n        final String withTabs = \"separated  by  tabs\";\n\n        final StringList spacesSplitByRegex = StringList.splitByRegex(withSpaces, \"\\\\s\", 0);\n        final StringList spacesFullySplit = StringList.split(withSpaces, \"\\\\s\", 0);\n        final StringList tabsSplitByRegex = StringList.splitByRegex(withTabs, \"\\\\s+\", 0);\n        final StringList tabsFullySplit = StringList.split(withTabs, \"\\\\s+\", 0);\n\n        Assert.assertEquals(3, spacesSplitByRegex.size());\n        Assert.assertEquals(1, spacesFullySplit.size());\n        Assert.assertEquals(3, tabsSplitByRegex.size());\n        Assert.assertEquals(1, tabsFullySplit.size());\n    }\n\n    @Test\n    public void testToArray()\n    {\n        final StringList test = new StringList();\n        test.add(\"1\");\n        test.add(\"2\");\n        test.add(\"3\");\n        final String[] array = test.toArray();\n        Assert.assertEquals(3, array.length);\n        Assert.assertArrayEquals(new String[] { \"1\", \"2\", \"3\" }, array);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/collections/SubIterableTest.java",
    "content": "package org.openstreetmap.atlas.utilities.collections;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class SubIterableTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(SubIterableTest.class);\n\n    @Test\n    public void testTruncation()\n    {\n        final List<Integer> values = new ArrayList<>();\n        for (int index = 0; index < 10; index++)\n        {\n            values.add(index);\n        }\n\n        // Middle\n        final List<Integer> truncated1 = Iterables.stream(values).truncate(2, 4).collectToList();\n        logger.info(\"{}\", truncated1);\n        Assert.assertEquals(4, truncated1.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(2, 3, 4, 5)), truncated1);\n\n        // 0, 0\n        final List<Integer> truncated2 = Iterables.stream(values).truncate(0, 0).collectToList();\n        logger.info(\"{}\", truncated2);\n        Assert.assertEquals(10, truncated2.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)),\n                truncated2);\n\n        // 0, middle\n        final List<Integer> truncated3 = Iterables.stream(values).truncate(0, 7).collectToList();\n        logger.info(\"{}\", truncated3);\n        Assert.assertEquals(3, truncated3.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(0, 1, 2)), truncated3);\n\n        // middle, 0\n        final List<Integer> truncated4 = Iterables.stream(values).truncate(7, 0).collectToList();\n        logger.info(\"{}\", truncated4);\n        Assert.assertEquals(3, truncated4.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(7, 8, 9)), truncated4);\n\n        // negative\n        final List<Integer> truncated5 = Iterables.stream(values).truncate(-5, -1).collectToList();\n        logger.info(\"{}\", truncated5);\n        Assert.assertEquals(10, truncated5.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)),\n                truncated5);\n\n        // crossing\n        final List<Integer> truncated6 = Iterables.stream(values).truncate(7, 7).collectToList();\n        logger.info(\"{}\", truncated6);\n        Assert.assertEquals(0, truncated6.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from()), truncated6);\n\n        // Only one left\n        final List<Integer> truncated7 = Iterables.stream(values).truncate(5, 4).collectToList();\n        logger.info(\"{}\", truncated7);\n        Assert.assertEquals(1, truncated7.size());\n        Assert.assertEquals(Iterables.asList(Iterables.from(5)), truncated7);\n\n        // Bigger than size\n        final List<Integer> truncated8 = Iterables.stream(values).truncate(20, 15).collectToList();\n        logger.info(\"{}\", truncated8);\n        Assert.assertEquals(0, truncated8.size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/SimpleOptionAndArgumentParserTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentArity;\nimport org.openstreetmap.atlas.utilities.command.parsing.ArgumentOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;\nimport org.openstreetmap.atlas.utilities.command.parsing.SimpleOptionAndArgumentParser;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.AmbiguousAbbreviationException;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.UnknownOptionException;\nimport org.openstreetmap.atlas.utilities.command.parsing.exceptions.UnparsableContextException;\n\n/**\n * @author lcram\n */\npublic class SimpleOptionAndArgumentParserTest\n{\n    @Test(expected = UnparsableContextException.class)\n    public void testFailOnInvalidShortOptionAbbrev() throws UnparsableContextException\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"opt1\", 'a', \"the 1st option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOptionWithRequiredArgument(\"opt2\", 'b', \"the 2nd option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerOption(\"opt3\", 'c', \"the 3rd option\", OptionOptionality.OPTIONAL, 1);\n\n        final List<String> arguments = Arrays.asList(\"-abc\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (final AmbiguousAbbreviationException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n        catch (final UnknownOptionException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n    }\n\n    @Test\n    public void testIgnoreUnknownOptions()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.ignoreUnknownOptions(true);\n        parser.registerOption(\"opt1\", \"the 1st option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt2\", \"the 2nd option\", OptionOptionality.OPTIONAL, 1);\n\n        final List<String> arguments = Arrays.asList(\"--opt1\", \"--opt3\", \"--opt2\");\n\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertTrue(parser.hasOption(\"opt1\"));\n        Assert.assertTrue(parser.hasOption(\"opt2\"));\n        Assert.assertFalse(parser.hasOption(\"opt3\"));\n\n        final SimpleOptionAndArgumentParser parser2 = new SimpleOptionAndArgumentParser();\n        parser2.ignoreUnknownOptions(true);\n        parser2.registerOption(\"opt1\", 'o', \"the 1st option\", OptionOptionality.OPTIONAL, 1);\n\n        final List<String> arguments2 = Arrays.asList(\"-z\", \"-o\");\n\n        try\n        {\n            parser2.parse(arguments2);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertTrue(parser2.hasOption(\"opt1\"));\n    }\n\n    @Test(expected = CoreException.class)\n    public void testInvalidArgumentDeclarationOrder()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        // This should fail. You cannot register another argument after an optional argument.\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.OPTIONAL, 1);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n\n        final List<String> arguments = Arrays.asList(\"arg1\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n    }\n\n    @Test(expected = UnparsableContextException.class)\n    public void testMultipleParseContextUnparsable() throws UnparsableContextException\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"opt1\", 'a', \"an option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt2\", 'b', \"an option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt3\", 'c', \"an option\", OptionOptionality.OPTIONAL, 2);\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 2);\n        parser.registerOptionWithRequiredArgument(\"opt4\", 'd', \"an option\",\n                OptionOptionality.OPTIONAL, \"hint\", 3);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.OPTIONAL, 3);\n\n        final List<String> arguments = Arrays.asList(\"--opt2\", \"--opt3\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n    }\n\n    @Test\n    public void testMultipleParseContexts()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"opt1\", 'a', \"an option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt2\", 'b', \"an option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt3\", 'c', \"an option\", OptionOptionality.OPTIONAL, 2);\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 2);\n        parser.registerOptionWithRequiredArgument(\"opt4\", 'd', \"an option\",\n                OptionOptionality.OPTIONAL, \"hint\", 3);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.OPTIONAL, 3);\n\n        List<String> arguments = Arrays.asList(\"--opt2\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n        Assert.assertEquals(1, parser.getContext());\n        Assert.assertFalse(parser.hasOption(\"opt1\"));\n        Assert.assertTrue(parser.hasOption(\"opt2\"));\n        Assert.assertFalse(parser.hasOption(\"opt3\"));\n        Assert.assertFalse(parser.hasOption(\"opt4\"));\n\n        arguments = Arrays.asList(\"--opt3\", \"arg1\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n        Assert.assertEquals(2, parser.getContext());\n        Assert.assertFalse(parser.hasOption(\"opt1\"));\n        Assert.assertFalse(parser.hasOption(\"opt2\"));\n        Assert.assertTrue(parser.hasOption(\"opt3\"));\n        Assert.assertFalse(parser.hasOption(\"opt4\"));\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n\n        arguments = Arrays.asList(\"--opt4\", \"optarg\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n        Assert.assertEquals(3, parser.getContext());\n        Assert.assertFalse(parser.hasOption(\"opt1\"));\n        Assert.assertFalse(parser.hasOption(\"opt2\"));\n        Assert.assertFalse(parser.hasOption(\"opt3\"));\n        Assert.assertTrue(parser.hasOption(\"opt4\"));\n        Assert.assertEquals(\"optarg\", parser.getOptionArgument(\"opt4\").get());\n        Assert.assertFalse(parser.getUnaryArgument(\"single1\").isPresent());\n        Assert.assertFalse(parser.getUnaryArgument(\"single2\").isPresent());\n    }\n\n    @Test\n    public void testMultipleShortFormArgumentShorthand()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"opt1\", 'a', \"a short form\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt2\", 'b', \"a short form\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt3\", 'c', \"a short form\", OptionOptionality.OPTIONAL, 1);\n\n        List<String> arguments = Arrays.asList(\"-abc\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(true, parser.hasOption(\"opt1\"));\n        Assert.assertEquals(true, parser.hasOption(\"opt2\"));\n        Assert.assertEquals(true, parser.hasOption(\"opt3\"));\n\n        // Swap the order and try again\n        arguments = Arrays.asList(\"-cba\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(true, parser.hasOption(\"opt1\"));\n        Assert.assertEquals(true, parser.hasOption(\"opt2\"));\n        Assert.assertEquals(true, parser.hasOption(\"opt3\"));\n    }\n\n    @Test\n    public void testOfMixedOptionsAndArguments()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"opt1\", \"the 1st option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt2\", \"the 2nd option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOptionWithRequiredArgument(\"opt3\", \"the 3rd option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerOption(\"opt4\", 'o', \"a short form option (4th)\", OptionOptionality.OPTIONAL,\n                1);\n        parser.registerOptionWithOptionalArgument(\"opt5\", \"the 5th option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerOptionWithRequiredArgument(\"opt6\", \"the 6th option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerOptionWithRequiredArgument(\"opt7\", \"the 7th option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"multi1\", ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED, 1);\n\n        final List<String> arguments = Arrays.asList(\"--opt1\", \"--opt3=value3\", \"arg1\", \"--opt2\",\n                \"arg2\", \"arg3\", \"-o\", \"--opt5\", \"arg4\", \"--opt6\", \"value6\", \"arg5\", \"--opt7\",\n                \"value7\");\n\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(true, parser.hasOption(\"opt1\"));\n        Assert.assertEquals(true, parser.hasOption(\"opt2\"));\n        Assert.assertEquals(\"value3\", parser.getOptionArgument(\"opt3\").get());\n\n        /*\n         * hasOption(longForm) will return true even if only the shortForm was actually present on\n         * the command line\n         */\n        Assert.assertEquals(true, parser.hasOption(\"opt4\"));\n\n        Assert.assertEquals(true, parser.hasOption(\"opt5\"));\n        Assert.assertFalse(parser.getOptionArgument(\"opt5\").isPresent());\n        Assert.assertEquals(\"value6\", parser.getOptionArgument(\"opt6\").get());\n        Assert.assertEquals(\"value7\", parser.getOptionArgument(\"opt7\").get());\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertEquals(\"arg2\", parser.getUnaryArgument(\"single2\").get());\n        Assert.assertEquals(Arrays.asList(\"arg3\", \"arg4\", \"arg5\"),\n                parser.getVariadicArgument(\"multi1\"));\n    }\n\n    @Test\n    public void testOptionArgumentConversion()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOptionWithRequiredArgument(\"two\", \"the number two\",\n                OptionOptionality.OPTIONAL, \"value\", 1);\n        parser.registerOptionWithRequiredArgument(\"myList\", \"a list of numbers\",\n                OptionOptionality.OPTIONAL, \"value\", 1);\n        parser.registerOptionWithRequiredArgument(\"three\", \"the number three\",\n                OptionOptionality.OPTIONAL, \"value\", 1);\n        parser.registerOptionWithRequiredArgument(\"someOption\", 'o', \"another option\",\n                OptionOptionality.OPTIONAL, \"value\", 1);\n        parser.registerOptionWithRequiredArgument(\"someOption2\", 'p', \"another option2\",\n                OptionOptionality.OPTIONAL, \"value\", 1);\n\n        final List<String> arguments = Arrays.asList(\"--two=2\", \"--myList=1:2:3\", \"-p\", \"3.14\",\n                \"--three=foo\", \"-ofalse\");\n\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(new Integer(2),\n                parser.getOptionArgument(\"two\", optionArgument -> Integer.parseInt(optionArgument))\n                        .get());\n\n        Assert.assertEquals(Arrays.asList(1, 2, 3),\n                parser.getOptionArgument(\"myList\", optionArgument ->\n                {\n                    final List<Integer> myList = new ArrayList<>();\n                    final String[] split = optionArgument.split(\":\");\n                    for (final String string : split)\n                    {\n                        myList.add(Integer.parseInt(string));\n                    }\n                    return myList;\n                }).get());\n\n        Assert.assertEquals(new Double(3.14),\n                parser.getOptionArgument(\"someOption2\", optionArgument ->\n                {\n                    try\n                    {\n                        return Double.parseDouble(optionArgument);\n                    }\n                    catch (final Exception e)\n                    {\n                        // return null on error, causing getOptionArgument to return an empty\n                        // optional\n                        return null;\n                    }\n                }).get());\n\n        Assert.assertEquals(new Boolean(false),\n                parser.getOptionArgument(\"someOption\", optionArgument ->\n                {\n                    try\n                    {\n                        return Boolean.parseBoolean(optionArgument);\n                    }\n                    catch (final Exception e)\n                    {\n                        // return null on error, causing getOptionArgument to return an empty\n                        // optional\n                        return null;\n                    }\n                }).get());\n\n        // this conversion will fail, but then we will fall back on a default value\n        Assert.assertEquals(new Integer(3), parser.getOptionArgument(\"three\", optionArgument ->\n        {\n            try\n            {\n                return Integer.parseInt(optionArgument);\n            }\n            catch (final Exception e)\n            {\n                // return null on error, causing getOptionArgument to return an empty optional\n                return null;\n            }\n        }).orElse(new Integer(3)));\n    }\n\n    public void testOptionArgumentValueOverwrite()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOptionWithOptionalArgument(\"opt1\", \"an option\", OptionOptionality.OPTIONAL,\n                \"ARG\", 1);\n        parser.registerOptionWithOptionalArgument(\"opt2\", \"an option\", OptionOptionality.OPTIONAL,\n                \"ARG\", 1);\n\n        final List<String> arguments = Arrays.asList(\"--opt1=optarg1\", \"--opt2=optarg2\",\n                \"--opt1=newArg\", \"--opt2\");\n\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"newArg\", parser.getOptionArgument(\"opt1\").get());\n        Assert.assertFalse(parser.getOptionArgument(\"opt2\").isPresent());\n    }\n\n    @Test\n    public void testOptionMap()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"opt1\", 'a', \"the 1st option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOptionWithRequiredArgument(\"opt2\", 'b', \"the 2nd option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerOption(\"opt3\", 'c', \"the 3rd option\", OptionOptionality.OPTIONAL, 1);\n\n        Assert.assertEquals(\"the 1st option\",\n                parser.getOptionNameToRegisteredOption().get(\"opt1\").getDescription());\n        Assert.assertEquals(\"the 2nd option\",\n                parser.getOptionNameToRegisteredOption().get(\"opt2\").getDescription());\n        Assert.assertEquals(\"the 3rd option\",\n                parser.getOptionNameToRegisteredOption().get(\"opt3\").getDescription());\n    }\n\n    @Test\n    public void testOptionWithDefaultValue()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOptionWithOptionalArgument(\"opt1\", \"an opt with an optional arg\",\n                OptionOptionality.OPTIONAL, \"optarg\", 1);\n\n        final List<String> arguments = Arrays.asList(\"--opt1\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"defaultValue\",\n                parser.getOptionArgument(\"opt1\").orElse(\"defaultValue\"));\n    }\n\n    @Test\n    public void testPrefixAbbreviation()\n    {\n        final SimpleOptionAndArgumentParser parser1 = new SimpleOptionAndArgumentParser();\n        parser1.registerOption(\"opt1\", \"option1\", OptionOptionality.OPTIONAL, 1);\n        parser1.registerOption(\"anotherOpt\", \"option2\", OptionOptionality.OPTIONAL, 1);\n        parser1.registerOption(\"option\", \"option3\", OptionOptionality.OPTIONAL, 1);\n        parser1.registerOption(\"optionSuffix\", \"option4\", OptionOptionality.OPTIONAL, 1);\n\n        final List<String> arguments = Arrays.asList(\"--opt1\", \"--an\", \"--option\", \"--optionSuf\");\n        try\n        {\n            parser1.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertTrue(parser1.hasOption(\"opt1\"));\n        Assert.assertTrue(parser1.hasOption(\"anotherOpt\"));\n        Assert.assertTrue(parser1.hasOption(\"option\"));\n        Assert.assertTrue(parser1.hasOption(\"optionSuffix\"));\n    }\n\n    @Test(expected = AmbiguousAbbreviationException.class)\n    public void testPrefixAmbiguous() throws AmbiguousAbbreviationException\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"option\", \"option1\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"optionSuffix\", \"option2\", OptionOptionality.OPTIONAL, 1);\n\n        final List<String> arguments2 = Arrays.asList(\"--opt\");\n        try\n        {\n            parser.parse(arguments2);\n        }\n        catch (UnknownOptionException | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n    }\n\n    @Test(expected = UnparsableContextException.class)\n    public void testRequiredOptionsMissing() throws UnparsableContextException\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"option1\", \"option1\", OptionOptionality.REQUIRED, 1);\n        parser.registerOption(\"option2\", \"option2\", OptionOptionality.OPTIONAL, 1);\n\n        final List<String> arguments = Arrays.asList(\"--option2\");\n\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n    }\n\n    @Test\n    public void testShortFormOptions()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerOption(\"opt1\", 'a', \"the 1st option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt2\", 'b', \"the 2nd option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOption(\"opt3\", 'c', \"the 3rd option\", OptionOptionality.OPTIONAL, 1);\n        parser.registerOptionWithRequiredArgument(\"opt4\", 'd', \"the 4th option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerOptionWithRequiredArgument(\"opt5\", 'e', \"the 5th option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerOptionWithOptionalArgument(\"opt6\", 'f', \"the 6th option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerOptionWithOptionalArgument(\"opt7\", 'g', \"the 7th option\",\n                OptionOptionality.OPTIONAL, \"ARG\", 1);\n        parser.registerArgument(\"hint1\", ArgumentArity.UNARY, ArgumentOptionality.OPTIONAL, 1);\n\n        final List<String> arguments = Arrays.asList(\"-abc\", \"-doptarg1\", \"-e\", \"optarg2\",\n                \"-foptarg3\", \"-g\", \"arg\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(true, parser.hasOption(\"opt1\"));\n        Assert.assertEquals(true, parser.hasOption(\"opt2\"));\n        Assert.assertEquals(true, parser.hasOption(\"opt3\"));\n        Assert.assertEquals(\"optarg1\", parser.getOptionArgument(\"opt4\").get());\n        Assert.assertEquals(\"optarg2\", parser.getOptionArgument(\"opt5\").get());\n        Assert.assertEquals(\"optarg3\", parser.getOptionArgument(\"opt6\").get());\n        Assert.assertFalse(parser.getOptionArgument(\"opt7\").isPresent());\n        Assert.assertEquals(\"arg\", parser.getUnaryArgument(\"hint1\").get());\n    }\n\n    @Test\n    public void testSingleOptionalArgument()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.OPTIONAL, 1);\n\n        List<String> arguments = new ArrayList<>();\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(1, parser.getContext());\n\n        arguments = Arrays.asList(\"arg1\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n        Assert.assertEquals(1, parser.getContext());\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n    }\n\n    @Test\n    public void testUnaryArgumentOptionality()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.OPTIONAL, 1);\n\n        List<String> arguments = Arrays.asList(\"arg1\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertFalse(parser.getUnaryArgument(\"single2\").isPresent());\n\n        arguments = Arrays.asList(\"arg1\", \"arg2\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertEquals(\"arg2\", parser.getUnaryArgument(\"single2\").get());\n    }\n\n    @Test(expected = UnknownOptionException.class)\n    public void testUnknownOption() throws UnknownOptionException\n    {\n        final SimpleOptionAndArgumentParser parser1 = new SimpleOptionAndArgumentParser();\n        parser1.registerOption(\"opt1\", \"option1\", OptionOptionality.OPTIONAL, 1);\n\n        final List<String> arguments = Arrays.asList(\"--opt2\");\n        try\n        {\n            parser1.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n    }\n\n    @Test\n    public void testUnsuppliedOptionalArgument()\n    {\n        SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.OPTIONAL, 1);\n\n        List<String> arguments = Arrays.asList(\"arg1\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertFalse(parser.getUnaryArgument(\"single2\").isPresent());\n\n        parser = new SimpleOptionAndArgumentParser();\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"multi1\", ArgumentArity.VARIADIC, ArgumentOptionality.OPTIONAL, 1);\n\n        arguments = Arrays.asList(\"arg1\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertTrue(parser.getVariadicArgument(\"multi1\").isEmpty());\n    }\n\n    @Test\n    public void testVariadicArgumentInAllPositions()\n    {\n        // First\n        SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerArgument(\"multi1\", ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n\n        final List<String> arguments = Arrays.asList(\"arg1\", \"arg2\", \"arg3\", \"arg4\", \"arg5\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg4\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertEquals(\"arg5\", parser.getUnaryArgument(\"single2\").get());\n        Assert.assertEquals(Arrays.asList(\"arg1\", \"arg2\", \"arg3\"),\n                parser.getVariadicArgument(\"multi1\"));\n\n        // Middle\n        parser = new SimpleOptionAndArgumentParser();\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"multi1\", ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertEquals(\"arg5\", parser.getUnaryArgument(\"single2\").get());\n        Assert.assertEquals(Arrays.asList(\"arg2\", \"arg3\", \"arg4\"),\n                parser.getVariadicArgument(\"multi1\"));\n\n        // Last\n        parser = new SimpleOptionAndArgumentParser();\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"single2\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"multi1\", ArgumentArity.VARIADIC, ArgumentOptionality.REQUIRED, 1);\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertEquals(\"arg2\", parser.getUnaryArgument(\"single2\").get());\n        Assert.assertEquals(Arrays.asList(\"arg3\", \"arg4\", \"arg5\"),\n                parser.getVariadicArgument(\"multi1\"));\n    }\n\n    @Test\n    public void testVariadicArgumentOptionality()\n    {\n        final SimpleOptionAndArgumentParser parser = new SimpleOptionAndArgumentParser();\n        parser.registerArgument(\"single1\", ArgumentArity.UNARY, ArgumentOptionality.REQUIRED, 1);\n        parser.registerArgument(\"multi1\", ArgumentArity.VARIADIC, ArgumentOptionality.OPTIONAL, 1);\n\n        List<String> arguments = Arrays.asList(\"arg1\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n\n        arguments = Arrays.asList(\"arg1\", \"arg2\", \"arg3\");\n        try\n        {\n            parser.parse(arguments);\n        }\n        catch (AmbiguousAbbreviationException | UnknownOptionException\n                | UnparsableContextException e)\n        {\n            Assert.fail(e.getMessage());\n        }\n\n        Assert.assertEquals(\"arg1\", parser.getUnaryArgument(\"single1\").get());\n        Assert.assertEquals(Arrays.asList(\"arg2\", \"arg3\"), parser.getVariadicArgument(\"multi1\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/AnyToGeoJsonCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class AnyToGeoJsonCommandTest\n{\n    @Test\n    public void testAtlasConversion()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AnyToGeoJsonCommand command = new AnyToGeoJsonCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"--atlas=/Users/foo/test.atlas\", \"--verbose\",\n                    \"--output=/Users/foo\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\n                    \"any2geojson: writing the atlas geojson file to /Users/foo/output-atlas.geojson\\n\",\n                    errContent.toString());\n            Assert.assertTrue(new File(\"/Users/foo/output-atlas.geojson\", filesystem).exists());\n            Assert.assertEquals(\n                    \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[1.0,1.0]},\\\"properties\\\":{\\\"foo\\\":\\\"bar\\\",\\\"identifier\\\":1000000,\\\"osmIdentifier\\\":1,\\\"itemType\\\":\\\"POINT\\\"}}\",\n                    new File(\"/Users/foo/output-atlas.geojson\", filesystem).readAndClose());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testBoundaryConversion()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            AnyToGeoJsonCommand command = new AnyToGeoJsonCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"--country-boundary=/Users/foo/boundary.txt\", \"--verbose\",\n                    \"--output=/Users/foo\", \"--countries=AIA\", \"--countries-deny=MAF\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\"any2geojson: loading country boundary map...\\n\"\n                    + \"any2geojson: loaded boundary map\\n\"\n                    + \"any2geojson: converting boundary file to GeoJSON...\\n\"\n                    + \"any2geojson: writing the boundary geojson file to /Users/foo/output-country-boundary.geojson\\n\",\n                    errContent.toString());\n            Assert.assertTrue(\n                    new File(\"/Users/foo/output-country-boundary.geojson\", filesystem).exists());\n            Assert.assertEquals(\n                    new String(AnyToGeoJsonCommandTest.class\n                            .getResourceAsStream(\"test_boundary_expected.geojson\").readAllBytes()),\n                    new File(\"/Users/foo/output-country-boundary.geojson\", filesystem)\n                            .readAndClose() + \"\\n\");\n\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n            command = new AnyToGeoJsonCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.setNewErrStream(new PrintStream(errContent2));\n\n            command.runSubcommand(\"--country-boundary=/Users/foo/boundary.txt\", \"--verbose\",\n                    \"--output=/Users/foo\", \"--countries-deny=MAF\");\n\n            Assert.assertTrue(outContent2.toString().isEmpty());\n            Assert.assertEquals(\"any2geojson: loading country boundary map...\\n\"\n                    + \"any2geojson: loaded boundary map\\n\"\n                    + \"any2geojson: converting boundary file to GeoJSON...\\n\"\n                    + \"any2geojson: writing the boundary geojson file to /Users/foo/output-country-boundary.geojson\\n\",\n                    errContent2.toString());\n            Assert.assertTrue(\n                    new File(\"/Users/foo/output-country-boundary.geojson\", filesystem).exists());\n            Assert.assertEquals(\n                    new String(AnyToGeoJsonCommandTest.class\n                            .getResourceAsStream(\"test_boundary_expected.geojson\").readAllBytes()),\n                    new File(\"/Users/foo/output-country-boundary.geojson\", filesystem)\n                            .readAndClose() + \"\\n\");\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testShardingConversion()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AnyToGeoJsonCommand command = new AnyToGeoJsonCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"--sharding=dynamic@/Users/foo/sharding.txt\", \"--verbose\",\n                    \"--output=/Users/foo\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\n                    \"any2geojson: writing the sharding geojson file to /Users/foo/output-sharding.geojson\\n\",\n                    errContent.toString());\n            Assert.assertTrue(new File(\"/Users/foo/output-sharding.geojson\", filesystem).exists());\n            Assert.assertEquals(new String(AnyToGeoJsonCommandTest.class\n                    .getResourceAsStream(\"sharding-tree-1-expected.geojson\").readAllBytes()),\n                    new File(\"/Users/foo/output-sharding.geojson\", filesystem).readAndClose()\n                            + \"\\n\");\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem) throws IOException\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n\n        builder.addPoint(1000000L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n\n        final Atlas atlas = builder.get();\n        final File atlasFile = new File(\"/Users/foo/test.atlas\", filesystem);\n        assert atlas != null;\n        atlas.save(atlasFile);\n\n        final File boundaryFile = new File(filesystem.getPath(\"/Users/foo\", \"boundary.txt\"));\n        boundaryFile.writeAndClose(AnyToGeoJsonCommandTest.class\n                .getResourceAsStream(\"test_boundary.txt\").readAllBytes());\n\n        final File shardingFile = new File(filesystem.getPath(\"/Users/foo\", \"sharding.txt\"));\n        shardingFile.writeAndClose(AnyToGeoJsonCommandTest.class\n                .getResourceAsStream(\"sharding-tree-1.txt\").readAllBytes());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.change.diff.AtlasDiff;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author matthieun\n */\npublic class AtlasDiffCommandTest\n{\n    private static final String ATLAS_A = \"/test/atlasA.atlas\";\n    private static final String ATLAS_B = \"/test/atlasB.atlas\";\n\n    private static final String ATLAS_BEFORE = \"/test/before\";\n    private static final String ATLAS_BEFORE_A = ATLAS_BEFORE + \"/atlasA.atlas\";\n    private static final String ATLAS_BEFORE_B = ATLAS_BEFORE + \"/atlasB.atlas\";\n    private static final String ATLAS_BEFORE_C = ATLAS_BEFORE + \"/x/atlasC.atlas\";\n    private static final String ATLAS_BEFORE_D = ATLAS_BEFORE + \"/x/atlasD.atlas\";\n\n    private static final String ATLAS_BEFORE2 = \"/test/before2\";\n    private static final String ATLAS_BEFORE2_A = ATLAS_BEFORE2 + \"/atlasA.atlas\";\n    private static final String ATLAS_BEFORE2_B = ATLAS_BEFORE2 + \"/atlasB.atlas\";\n\n    private static final String ATLAS_AFTER = \"/test/after\";\n    private static final String ATLAS_AFTER_A = ATLAS_AFTER + \"/atlasA.atlas\";\n    private static final String ATLAS_AFTER_B = ATLAS_AFTER + \"/atlasB.atlas\";\n    private static final String ATLAS_AFTER_C = ATLAS_AFTER + \"/x/atlasC.atlas\";\n    private static final String ATLAS_AFTER_D = ATLAS_AFTER + \"/x/atlasD.atlas\";\n\n    private static final String ATLAS_AFTER2 = \"/test/after2\";\n    private static final String ATLAS_AFTER2_B = ATLAS_AFTER2 + \"/atlasB.atlas\";\n    private static final String ATLAS_AFTER2_C = ATLAS_AFTER2 + \"/atlasC.atlas\";\n    private static final String ATLAS_AFTER2_D = ATLAS_AFTER2 + \"/atlasD.atlas\";\n\n    @Rule\n    public AtlasDiffCommandTestRule rule = new AtlasDiffCommandTestRule();\n\n    private FileSystem memoryFileSystem;\n\n    private Atlas atlas1;\n    private Atlas atlas2;\n    private Atlas atlas3;\n    private Atlas atlas4;\n\n    private String atlas1to2Diff;\n\n    @After\n    public void cleanup()\n    {\n        Streams.close(this.memoryFileSystem);\n    }\n\n    @Before\n    public void setup()\n    {\n        this.atlas1 = this.rule.getAtlas1();\n        this.atlas2 = this.rule.getAtlas2();\n        this.atlas3 = this.rule.getAtlas3();\n        this.atlas4 = this.rule.getAtlas4();\n\n        this.atlas1to2Diff = new AtlasDiff(this.atlas1, this.atlas2).saveAllGeometries(false)\n                .generateChange().orElseThrow(() -> new CoreException(\"There is no change\"))\n                .toLineDelimitedFeatureChanges(true);\n\n        this.memoryFileSystem = Jimfs.newFileSystem(Configuration.unix());\n\n        this.atlas1.save(new File(ATLAS_A, this.memoryFileSystem));\n        this.atlas2.save(new File(ATLAS_B, this.memoryFileSystem));\n\n        this.atlas1.save(new File(ATLAS_BEFORE_A, this.memoryFileSystem));\n        this.atlas2.save(new File(ATLAS_BEFORE_B, this.memoryFileSystem));\n        this.atlas3.save(new File(ATLAS_BEFORE_C, this.memoryFileSystem));\n        this.atlas4.save(new File(ATLAS_BEFORE_D, this.memoryFileSystem));\n\n        this.atlas2.save(new File(ATLAS_BEFORE2_A, this.memoryFileSystem));\n        this.atlas1.save(new File(ATLAS_BEFORE2_B, this.memoryFileSystem));\n\n        this.atlas2.save(new File(ATLAS_AFTER_A, this.memoryFileSystem));\n        this.atlas2.save(new File(ATLAS_AFTER_B, this.memoryFileSystem));\n        this.atlas3.save(new File(ATLAS_AFTER_C, this.memoryFileSystem));\n        this.atlas4.save(new File(ATLAS_AFTER_D, this.memoryFileSystem));\n\n        this.atlas2.save(new File(ATLAS_AFTER2_B, this.memoryFileSystem));\n        this.atlas3.save(new File(ATLAS_AFTER2_C, this.memoryFileSystem));\n        this.atlas4.save(new File(ATLAS_AFTER2_D, this.memoryFileSystem));\n    }\n\n    @Test\n    public void testFolderDiffDifferentFileList()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final AtlasDiffCommand command = new AtlasDiffCommand();\n        command.setNewFileSystem(this.memoryFileSystem);\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n        command.runSubcommand(ATLAS_BEFORE2, ATLAS_AFTER2, \"--ldgeojson\");\n        Assert.assertEquals(\"atlasB.atlas\\n\" + this.atlas1to2Diff + \"\\n\", outContent.toString());\n        Assert.assertEquals(\n                \"atlas-diff: warn: Files only in Before Atlas folder:\\n\"\n                        + \"atlas-diff: warn: atlasA.atlas\\n\"\n                        + \"atlas-diff: warn: Files only in After Atlas folder:\\n\"\n                        + \"atlas-diff: warn: atlasC.atlas\\n\" + \"atlas-diff: warn: atlasD.atlas\\n\",\n                errContent.toString());\n    }\n\n    @Test\n    public void testFolderDiffSameFileList()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final AtlasDiffCommand command = new AtlasDiffCommand();\n        command.setNewFileSystem(this.memoryFileSystem);\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n        command.runSubcommand(ATLAS_BEFORE, ATLAS_AFTER, \"--ldgeojson\", \"--recursive\", \"--verbose\");\n        Assert.assertEquals(\"atlasA.atlas\\n\" + this.atlas1to2Diff + \"\\n\" + \"atlasB.atlas\\n\"\n                + \"x/atlasC.atlas\\n\" + \"x/atlasD.atlas\\n\", outContent.toString());\n        Assert.assertEquals(\"atlas-diff: atlases are effectively identical\\n\"\n                + \"atlas-diff: atlases are effectively identical\\n\"\n                + \"atlas-diff: atlases are effectively identical\\n\", errContent.toString());\n    }\n\n    @Test\n    public void testSimpleDiff()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final AtlasDiffCommand command = new AtlasDiffCommand();\n        command.setNewFileSystem(this.memoryFileSystem);\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n        command.runSubcommand(ATLAS_A, ATLAS_B, \"--ldgeojson\");\n        Assert.assertEquals(this.atlas1to2Diff + \"\\n\", outContent.toString());\n        Assert.assertEquals(\"\", errContent.toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommandTestRule.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * @author matthieun\n */\npublic class AtlasDiffCommandTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"AtlasDiffCommandAtlas1.josm.osm\")\n    private Atlas atlas1;\n\n    @TestAtlas(loadFromJosmOsmResource = \"AtlasDiffCommandAtlas2.josm.osm\")\n    private Atlas atlas2;\n\n    @TestAtlas(loadFromJosmOsmResource = \"AtlasDiffCommandAtlas3.josm.osm\")\n    private Atlas atlas3;\n\n    @TestAtlas(loadFromJosmOsmResource = \"AtlasDiffCommandAtlas4.josm.osm\")\n    private Atlas atlas4;\n\n    public Atlas getAtlas1()\n    {\n        return this.atlas1;\n    }\n\n    public Atlas getAtlas2()\n    {\n        return this.atlas2;\n    }\n\n    public Atlas getAtlas3()\n    {\n        return this.atlas3;\n    }\n\n    public Atlas getAtlas4()\n    {\n        return this.atlas4;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasMetadataReaderCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasMetaData;\nimport org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class AtlasMetadataReaderCommandTest\n{\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasMetadataReaderCommand command = new AtlasMetadataReaderCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\");\n\n            Assert.assertEquals(\"/Users/foo/test.atlas metadata:\\n\" + \"Size: \\n\" + \"\\tNodes: 0\\n\"\n                    + \"\\tEdges: 0\\n\" + \"\\tAreas: 0\\n\" + \"\\tLines: 0\\n\" + \"\\tPoints: 1\\n\"\n                    + \"\\tRelations: 0\\n\" + \"Original: false\\n\" + \"Code Version: codeVersion\\n\"\n                    + \"Data Version: dataVersion\\n\" + \"Country: countryName\\n\"\n                    + \"Shard: shardName\\n\" + \"Tags:\\n\\t\" + \"baz -> bat\\n\\t\" + \"foo -> bar\" + \"\\n\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\"\", errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testOptions()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasMetadataReaderCommand command = new AtlasMetadataReaderCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--size\", \"--original\", \"--code-version\",\n                    \"--data-version\", \"--country\", \"--shard\", \"--tags\");\n\n            Assert.assertEquals(\"/Users/foo/test.atlas metadata:\\n\" + \"Size: \\n\" + \"\\tNodes: 0\\n\"\n                    + \"\\tEdges: 0\\n\" + \"\\tAreas: 0\\n\" + \"\\tLines: 0\\n\" + \"\\tPoints: 1\\n\"\n                    + \"\\tRelations: 0\\n\" + \"Original: false\\n\" + \"Code Version: codeVersion\\n\"\n                    + \"Data Version: dataVersion\\n\" + \"Country: countryName\\n\"\n                    + \"Shard: shardName\\n\" + \"Tags:\\n\\t\" + \"baz -> bat\\n\\t\" + \"foo -> bar\" + \"\\n\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\"\", errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addPoint(1000000L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n        final AtlasMetaData metaData = new AtlasMetaData(new AtlasSize(0L, 0L, 0L, 0L, 1L, 0L),\n                false, \"codeVersion\", \"dataVersion\", \"countryName\", \"shardName\",\n                Maps.hashMap(\"foo\", \"bar\", \"baz\", \"bat\"));\n        builder.setMetaData(metaData);\n\n        final Atlas atlas = builder.get();\n        final File atlasFile = new File(\"/Users/foo/test.atlas\", filesystem);\n        assert atlas != null;\n        atlas.save(atlasFile);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasSearchCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class AtlasSearchCommandTest\n{\n    @Test\n    public void testBoundingPolygonSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--bounding-polygon=POLYGON((0 0, 0 2.8, 2.8 2.8, 2.8 0, 0 0))\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompletePoint [\\n\" + \"identifier: 1000000, \\n\" + \"geometry: POINT (1 1), \\n\"\n                    + \"tags: {foo=bar}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((1 1, 1 1, 1 1, 1 1, 1 1)), \\n\" + \"]\\n\" + \"\\n\"\n                    + \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompletePoint [\\n\" + \"identifier: 2000000, \\n\" + \"geometry: POINT (2 2), \\n\"\n                    + \"tags: {baz=bat}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((2 2, 2 2, 2 2, 2 2, 2 2)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testCollectSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--geometry=POINT (1 1)\",\n                    \"--collect-matching\", \"--output=/Users/foo\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompletePoint [\\n\" + \"identifier: 1000000, \\n\" + \"geometry: POINT (1 1), \\n\"\n                    + \"tags: {foo=bar}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((1 1, 1 1, 1 1, 1 1, 1 1)), \\n\" + \"]\\n\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\"\n                            + \"find: saved to /Users/foo/collected-multi.atlas\\n\",\n                    errContent.toString());\n            Assert.assertTrue(new File(\"/Users/foo/collected-multi.atlas\", filesystem).exists());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testFailSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--type=ASD\");\n\n            Assert.assertEquals(\"\", outContent.toString());\n            Assert.assertEquals(\n                    \"find: error: could not parse ItemType 'ASD'\\n\"\n                            + \"find: error: no filtering objects were successfully constructed\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testGeometrySearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--geometry=POINT (1 1)\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompletePoint [\\n\" + \"identifier: 1000000, \\n\" + \"geometry: POINT (1 1), \\n\"\n                    + \"tags: {foo=bar}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((1 1, 1 1, 1 1, 1 1, 1 1)), \\n\" + \"]\\n\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.setNewErrStream(new PrintStream(errContent2));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--geometry=LINESTRING (10 10, 11 11, 12 12)\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteLine [\\n\" + \"identifier: 4000000, \\n\"\n                    + \"polyLine: LINESTRING (10 10, 11 11, 12 12), \\n\"\n                    + \"tags: {this_is=a_line}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((10 10, 10 12, 12 12, 12 10, 10 10)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent2.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent2.toString());\n\n            final ByteArrayOutputStream outContent3 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent3 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent3));\n            command.setNewErrStream(new PrintStream(errContent3));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--geometry=POLYGON ((20 20, 21 20, 21 21, 20 20))\");\n\n            Assert.assertEquals(\n                    \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                            + \"CompleteArea [\\n\" + \"identifier: 7000000, \\n\"\n                            + \"geometry: POLYGON ((20 20, 21 20, 21 21, 20 20)), \\n\"\n                            + \"tags: {baz=bat}, \\n\" + \"parentRelations: [12000000], \\n\"\n                            + \"bounds: POLYGON ((20 20, 20 21, 21 21, 21 20, 20 20)), \\n\" + \"]\\n\\n\",\n                    outContent3.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent3.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testIdSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--id=1000000\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompletePoint [\\n\" + \"identifier: 1000000, \\n\" + \"geometry: POINT (1 1), \\n\"\n                    + \"tags: {foo=bar}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((1 1, 1 1, 1 1, 1 1, 1 1)), \\n\" + \"]\\n\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testInOutEdgeSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--out-edge=11000001\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteNode [\\n\" + \"identifier: 9000000, \\n\" + \"geometry: POINT (32 32), \\n\"\n                    + \"tags: {baz=bat}, \\n\" + \"inEdges: [11000000], \\n\" + \"outEdges: [11000001], \\n\"\n                    + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((32 32, 32 32, 32 32, 32 32, 32 32)), \\n\" + \"]\\n\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.setNewErrStream(new PrintStream(errContent2));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--in-edge=11000000\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteNode [\\n\" + \"identifier: 9000000, \\n\" + \"geometry: POINT (32 32), \\n\"\n                    + \"tags: {baz=bat}, \\n\" + \"inEdges: [11000000], \\n\" + \"outEdges: [11000001], \\n\"\n                    + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((32 32, 32 32, 32 32, 32 32, 32 32)), \\n\" + \"]\\n\\n\",\n                    outContent2.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent2.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testJsonOption()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--all\", \"--json\");\n\n            final StringResource expected = new StringResource();\n            expected.writeAndClose(AtlasSearchCommandTest.class\n                    .getResourceAsStream(\"expected-json-features.txt\").readAllBytes());\n            Assert.assertEquals(expected.all(), outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testOsmIdSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--osmid=11\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteEdge [\\n\" + \"identifier: 11000000, \\n\"\n                    + \"geometry: LINESTRING (30 30, 31 30, 32 32), \\n\" + \"startNode: 8000000, \\n\"\n                    + \"endNode: 9000000, \\n\" + \"tags: {foo=bar}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((30 30, 30 32, 32 32, 32 30, 30 30)), \\n\" + \"]\\n\" + \"\\n\"\n                    + \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteEdge [\\n\" + \"identifier: 11000001, \\n\"\n                    + \"geometry: LINESTRING (32 32, 32 33, 34 34), \\n\" + \"startNode: 9000000, \\n\"\n                    + \"endNode: 10000000, \\n\" + \"tags: {baz=bat}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((32 32, 32 34, 34 34, 34 32, 32 32)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testParentRelationSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--parent-relations=12000000\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteEdge [\\n\" + \"identifier: 11000000, \\n\"\n                    + \"geometry: LINESTRING (30 30, 31 30, 32 32), \\n\" + \"startNode: 8000000, \\n\"\n                    + \"endNode: 9000000, \\n\" + \"tags: {foo=bar}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((30 30, 30 32, 32 32, 32 30, 30 30)), \\n\" + \"]\\n\" + \"\\n\"\n                    + \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteEdge [\\n\" + \"identifier: 11000001, \\n\"\n                    + \"geometry: LINESTRING (32 32, 32 33, 34 34), \\n\" + \"startNode: 9000000, \\n\"\n                    + \"endNode: 10000000, \\n\" + \"tags: {baz=bat}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((32 32, 32 34, 34 34, 34 32, 32 32)), \\n\" + \"]\\n\" + \"\\n\"\n                    + \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteArea [\\n\" + \"identifier: 7000000, \\n\"\n                    + \"geometry: POLYGON ((20 20, 21 20, 21 21, 20 20)), \\n\" + \"tags: {baz=bat}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((20 20, 20 21, 21 21, 21 20, 20 20)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testPredicateSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--predicate=!e.relations().isEmpty() && e.relations().collect { it.getIdentifier() }.containsAll(Sets.hashSet(12000000L))\",\n                    \"--imports=org.openstreetmap.atlas.utilities.command.parsing\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteEdge [\\n\" + \"identifier: 11000000, \\n\"\n                    + \"geometry: LINESTRING (30 30, 31 30, 32 32), \\n\" + \"startNode: 8000000, \\n\"\n                    + \"endNode: 9000000, \\n\" + \"tags: {foo=bar}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((30 30, 30 32, 32 32, 32 30, 30 30)), \\n\" + \"]\\n\" + \"\\n\"\n                    + \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteEdge [\\n\" + \"identifier: 11000001, \\n\"\n                    + \"geometry: LINESTRING (32 32, 32 33, 34 34), \\n\" + \"startNode: 9000000, \\n\"\n                    + \"endNode: 10000000, \\n\" + \"tags: {baz=bat}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((32 32, 32 34, 34 34, 34 32, 32 32)), \\n\" + \"]\\n\" + \"\\n\"\n                    + \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteArea [\\n\" + \"identifier: 7000000, \\n\"\n                    + \"geometry: POLYGON ((20 20, 21 20, 21 21, 20 20)), \\n\" + \"tags: {baz=bat}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((20 20, 20 21, 21 21, 21 20, 20 20)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testRelationMemberAND()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--and-relation-members=EDGE,11000000,role0;EDGE,11000001,role1\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteRelation [\\n\" + \"identifier: 12000000, \\n\" + \"tags: {route=bike}, \\n\"\n                    + \"members: RelationBean [[[EDGE, 11000000, role0], [EDGE, 11000001, role1], [AREA, 7000000, role2]]], \\n\"\n                    + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((20 20, 20 34, 34 34, 34 20, 20 20)), \\n\" + \"\\n\" + \"]\\n\"\n                    + \"\\n\", outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testRelationMemberANDParseError()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.setNewErrStream(new PrintStream(errContent2));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--and-relation-members=EDGE,asd,*\");\n\n            Assert.assertEquals(\"\", outContent2.toString());\n            Assert.assertEquals(\n                    \"find: error: could not parse ID `asd' from member `EDGE,asd,*'\\n\"\n                            + \"find: error: no filtering objects were successfully constructed\\n\",\n                    errContent2.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testRelationMemberOR()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--relation-members=AREA,*,*;*,11000001,*\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteRelation [\\n\" + \"identifier: 12000000, \\n\" + \"tags: {route=bike}, \\n\"\n                    + \"members: RelationBean [[[EDGE, 11000000, role0], [EDGE, 11000001, role1], [AREA, 7000000, role2]]], \\n\"\n                    + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((20 20, 20 34, 34 34, 34 20, 20 20)), \\n\" + \"\\n\" + \"]\\n\"\n                    + \"\\n\", outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.setNewErrStream(new PrintStream(errContent2));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--relation-members=POINT,*,\\\\*\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteRelation [\\n\" + \"identifier: 1300000, \\n\"\n                    + \"tags: {some=relation}, \\n\"\n                    + \"members: RelationBean [[[POINT, 4000000, *]]], \\n\"\n                    + \"parentRelations: [], \\n\" + \"bounds: POLYGON ((4 5, 4 5, 4 5, 4 5, 4 5)), \\n\"\n                    + \"\\n\" + \"]\\n\" + \"\\n\", outContent2.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent2.toString());\n\n            final ByteArrayOutputStream outContent3 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent3 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent3));\n            command.setNewErrStream(new PrintStream(errContent3));\n\n            // Find relations with an emoji role\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--relation-members=*,*,💯\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteRelation [\\n\" + \"identifier: 1400000, \\n\"\n                    + \"tags: {some2=relation2}, \\n\"\n                    + \"members: RelationBean [[[POINT, 5000000, 💯]]], \\n\"\n                    + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((30 30, 30 30, 30 30, 30 30, 30 30)), \\n\" + \"\\n\" + \"]\\n\"\n                    + \"\\n\", outContent3.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent3.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testRelationMemberORParseError()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            AtlasSearchCommand command = new AtlasSearchCommand();\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.setNewErrStream(new PrintStream(errContent2));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--relation-members=asd,*,*\");\n\n            Assert.assertEquals(\"\", outContent2.toString());\n            Assert.assertEquals(\n                    \"find: error: could not parse ItemType `asd' from member `asd,*,*'\\n\"\n                            + \"find: error: no filtering objects were successfully constructed\\n\",\n                    errContent2.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testShowAll()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test2.atlas\", \"--verbose\", \"--all\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test2.atlas:\\n\"\n                    + \"CompletePoint [\\n\" + \"identifier: 1000000, \\n\" + \"geometry: POINT (1 1), \\n\"\n                    + \"tags: {foo=bar}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((1 1, 1 1, 1 1, 1 1, 1 1)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test2.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test2.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testStartEndNodeSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--start-node=8000000\",\n                    \"--end-node=9000000\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteEdge [\\n\" + \"identifier: 11000000, \\n\"\n                    + \"geometry: LINESTRING (30 30, 31 30, 32 32), \\n\" + \"startNode: 8000000, \\n\"\n                    + \"endNode: 9000000, \\n\" + \"tags: {foo=bar}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((30 30, 30 32, 32 32, 32 30, 30 30)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testSubGeometrySearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--sub-geometry=POINT (1 1)\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompletePoint [\\n\" + \"identifier: 1000000, \\n\" + \"geometry: POINT (1 1), \\n\"\n                    + \"tags: {foo=bar}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((1 1, 1 1, 1 1, 1 1, 1 1)), \\n\" + \"]\\n\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.setNewErrStream(new PrintStream(errContent2));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--sub-geometry=POINT (10 10)\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteLine [\\n\" + \"identifier: 4000000, \\n\"\n                    + \"polyLine: LINESTRING (10 10, 11 11, 12 12), \\n\"\n                    + \"tags: {this_is=a_line}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((10 10, 10 12, 12 12, 12 10, 10 10)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent2.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent2.toString());\n\n            final ByteArrayOutputStream outContent3 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent3 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent3));\n            command.setNewErrStream(new PrintStream(errContent3));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--sub-geometry=LINESTRING (10 10, 11 11)\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteLine [\\n\" + \"identifier: 4000000, \\n\"\n                    + \"polyLine: LINESTRING (10 10, 11 11, 12 12), \\n\"\n                    + \"tags: {this_is=a_line}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((10 10, 10 12, 12 12, 12 10, 10 10)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent3.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent3.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testTagSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--tag-filter=another->*\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteNode [\\n\" + \"identifier: 10000000, \\n\"\n                    + \"geometry: POINT (34 34), \\n\" + \"tags: {another=tag2}, \\n\"\n                    + \"inEdges: [11000001], \\n\" + \"outEdges: [], \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((34 34, 34 34, 34 34, 34 34, 34 34)), \\n\" + \"]\\n\" + \"\\n\"\n                    + \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteLine [\\n\" + \"identifier: 6000000, \\n\"\n                    + \"polyLine: LINESTRING (16 16, 17 17, 18 18), \\n\"\n                    + \"tags: {another=tag1, hello=world}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((16 16, 16 18, 18 18, 18 16, 16 16)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n            command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.setNewErrStream(new PrintStream(errContent2));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--tag-matcher=another=/tag.*/\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteNode [\\n\" + \"identifier: 10000000, \\n\"\n                    + \"geometry: POINT (34 34), \\n\" + \"tags: {another=tag2}, \\n\"\n                    + \"inEdges: [11000001], \\n\" + \"outEdges: [], \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((34 34, 34 34, 34 34, 34 34, 34 34)), \\n\" + \"]\\n\" + \"\\n\"\n                    + \"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteLine [\\n\" + \"identifier: 6000000, \\n\"\n                    + \"polyLine: LINESTRING (16 16, 17 17, 18 18), \\n\"\n                    + \"tags: {another=tag1, hello=world}, \\n\" + \"parentRelations: [], \\n\"\n                    + \"bounds: POLYGON ((16 16, 16 18, 18 18, 18 16, 16 16)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent2.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent2.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testTypeSearch()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasSearchCommand command = new AtlasSearchCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--type=AREA\");\n\n            Assert.assertEquals(\"Found entity matching criteria in /Users/foo/test.atlas:\\n\"\n                    + \"CompleteArea [\\n\" + \"identifier: 7000000, \\n\"\n                    + \"geometry: POLYGON ((20 20, 21 20, 21 21, 20 20)), \\n\" + \"tags: {baz=bat}, \\n\"\n                    + \"parentRelations: [12000000], \\n\"\n                    + \"bounds: POLYGON ((20 20, 20 21, 21 21, 21 20, 20 20)), \\n\" + \"]\\n\" + \"\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"find: loading /Users/foo/test.atlas\\n\"\n                            + \"find: processing atlas /Users/foo/test.atlas (1/1)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n\n        builder.addPoint(1000000L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n        builder.addPoint(2000000L, Location.forWkt(\"POINT(2 2)\"), Maps.hashMap(\"baz\", \"bat\"));\n        builder.addPoint(3000000L, Location.forWkt(\"POINT(3 3)\"),\n                Maps.hashMap(\"foo\", \"bar\", \"baz\", \"bat\"));\n        builder.addPoint(4000000L, Location.forWkt(\"POINT(4 5)\"), Maps.hashMap(\"a\", \"b\"));\n        builder.addPoint(5000000L, Location.forWkt(\"POINT(30 30)\"), Maps.hashMap(\"a\", \"b\"));\n\n        builder.addLine(4000000L, PolyLine.wkt(\"LINESTRING(10 10, 11 11, 12 12)\"),\n                Maps.hashMap(\"this_is\", \"a_line\"));\n        builder.addLine(5000000L, PolyLine.wkt(\"LINESTRING(13 13, 14 14, 15 15)\"),\n                Maps.hashMap(\"this_is\", \"a_line\", \"foo\", \"bar\"));\n        builder.addLine(6000000L, PolyLine.wkt(\"LINESTRING(16 16, 17 17, 18 18)\"),\n                Maps.hashMap(\"hello\", \"world\", \"another\", \"tag1\"));\n\n        builder.addArea(7000000L, Polygon.wkt(\"POLYGON((20 20, 21 20, 21 21, 20 20))\"),\n                Maps.hashMap(\"baz\", \"bat\"));\n\n        builder.addNode(8000000L, Location.forWkt(\"POINT(30 30)\"), Maps.hashMap(\"foo\", \"bar\"));\n        builder.addNode(9000000L, Location.forWkt(\"POINT(32 32)\"), Maps.hashMap(\"baz\", \"bat\"));\n        builder.addNode(10000000L, Location.forWkt(\"POINT(34 34)\"),\n                Maps.hashMap(\"another\", \"tag2\"));\n\n        builder.addEdge(11000000L, PolyLine.wkt(\"LINESTRING(30 30, 31 30, 32 32)\"),\n                Maps.hashMap(\"foo\", \"bar\"));\n        builder.addEdge(11000001L, PolyLine.wkt(\"LINESTRING(32 32, 32 33, 34 34)\"),\n                Maps.hashMap(\"baz\", \"bat\"));\n\n        final RelationBean bean = new RelationBean();\n        bean.addItem(11000000L, \"role0\", ItemType.EDGE);\n        bean.addItem(11000001L, \"role1\", ItemType.EDGE);\n        bean.addItem(7000000L, \"role2\", ItemType.AREA);\n        builder.addRelation(12000000L, 12L, bean, Maps.hashMap(\"route\", \"bike\"));\n\n        final RelationBean bean2 = new RelationBean();\n        bean2.addItem(4000000L, \"*\", ItemType.POINT);\n        builder.addRelation(1300000L, 13L, bean2, Maps.hashMap(\"some\", \"relation\"));\n\n        final RelationBean bean3 = new RelationBean();\n        bean3.addItem(5000000L, \"💯\", ItemType.POINT);\n        builder.addRelation(1400000L, 14L, bean3, Maps.hashMap(\"some2\", \"relation2\"));\n\n        final Atlas atlas = builder.get();\n        final File atlasFile = new File(\"/Users/foo/test.atlas\", filesystem);\n        assert atlas != null;\n        atlas.save(atlasFile);\n\n        final PackedAtlasBuilder builder2 = new PackedAtlasBuilder();\n        builder2.addPoint(1000000L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n        final Atlas atlas2 = builder2.get();\n        final File atlasFile2 = new File(\"/Users/foo/test2.atlas\", filesystem);\n        assert atlas2 != null;\n        atlas2.save(atlasFile2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShardingConverterCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.sub.AtlasCutType;\nimport org.openstreetmap.atlas.geography.sharding.SlippyTile;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileParser;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileToPbf;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author matthieun\n */\npublic class AtlasShardingConverterCommandTest\n{\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final File input = new File(\"/Users/foo/input\", filesystem);\n            final File output = new File(\"/Users/foo/output\", filesystem);\n            final StringList arguments = new StringList();\n            arguments.add(\"--input=\" + input.getAbsolutePathString());\n            arguments.add(\"--inputSharding=slippy@11\");\n            arguments.add(\"--output=\" + output.getAbsolutePathString());\n            arguments.add(\"--outputSharding=geohash@4\");\n\n            final String[] args = new String[arguments.size()];\n            for (int index = 0; index < arguments.size(); index++)\n            {\n                args[index] = arguments.get(index);\n            }\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasShardingConverterCommand command = new AtlasShardingConverterCommand();\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n            command.setNewFileSystem(filesystem);\n            command.runSubcommand(args);\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\n                    \"sharding-converter: Found input files: [/Users/foo/input/FRA_11-998-708.atlas, /Users/foo/input/FRA_11-998-709.atlas]\\n\"\n                            + \"sharding-converter: Found 2 input shards: [[SlippyTile: zoom = 11, x = 998, y = 709], [SlippyTile: zoom = 11, x = 998, y = 708]]\\n\"\n                            + \"sharding-converter: Found country: FRA\\n\"\n                            + \"sharding-converter: Found 4 output shards: [[GeoHashTile: value = gbsd], [GeoHashTile: value = gbse], [GeoHashTile: value = gbsf], [GeoHashTile: value = gbsg]]\\n\"\n                            + \"sharding-converter: Processing output shard [GeoHashTile: value = gbsd]\\n\"\n                            + \"sharding-converter: Loading Atlas with [/Users/foo/input/FRA_11-998-709.atlas]\\n\"\n                            + \"sharding-converter: Saving Atlas to /Users/foo/output/FRA_gbsd.atlas\\n\"\n                            + \"sharding-converter: Processing output shard [GeoHashTile: value = gbse]\\n\"\n                            + \"sharding-converter: Loading Atlas with [/Users/foo/input/FRA_11-998-708.atlas, /Users/foo/input/FRA_11-998-709.atlas]\\n\"\n                            + \"sharding-converter: Saving Atlas to /Users/foo/output/FRA_gbse.atlas\\n\"\n                            + \"sharding-converter: Processing output shard [GeoHashTile: value = gbsf]\\n\"\n                            + \"sharding-converter: Loading Atlas with [/Users/foo/input/FRA_11-998-709.atlas]\\n\"\n                            + \"sharding-converter: Saving Atlas to /Users/foo/output/FRA_gbsf.atlas\\n\"\n                            + \"sharding-converter: Processing output shard [GeoHashTile: value = gbsg]\\n\"\n                            + \"sharding-converter: Loading Atlas with [/Users/foo/input/FRA_11-998-708.atlas, /Users/foo/input/FRA_11-998-709.atlas]\\n\"\n                            + \"sharding-converter: Saving Atlas to /Users/foo/output/FRA_gbsg.atlas\\n\",\n                    errContent.toString());\n            final File franceAtlas1 = output.child(\"FRA_gbsf.atlas\");\n            final File franceAtlas2 = output.child(\"FRA_gbsg.atlas\");\n            Assert.assertEquals(2, output.listFilesRecursively().size());\n            Assert.assertEquals(5, new AtlasResourceLoader().load(franceAtlas1).numberOfEdges());\n            Assert.assertEquals(8, new AtlasResourceLoader().load(franceAtlas2).numberOfEdges());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem) throws IOException\n    {\n        final Resource resource = new InputStreamResource(\n                () -> AtlasShardingConverterCommandTest.class\n                        .getResourceAsStream(\"shardingConverter.josm.osm\"));\n        final File pbfFile = new File(\"/Users/foo/shardingConverter.pbf\", filesystem);\n        final StringResource osmFile = new StringResource();\n        new OsmFileParser().update(resource, osmFile);\n        new OsmFileToPbf().update(osmFile, pbfFile);\n\n        final StringList arguments = new StringList();\n        arguments.add(pbfFile.getAbsolutePathString());\n        arguments.add(\"--output=/Users/foo\");\n        arguments.add(\"--countryName=FRA\");\n\n        final String[] args = new String[arguments.size()];\n        for (int index = 0; index < arguments.size(); index++)\n        {\n            args[index] = arguments.get(index);\n        }\n        final PbfToAtlasCommand command = new PbfToAtlasCommand();\n        command.setNewFileSystem(filesystem);\n        command.runSubcommand(args);\n\n        final File outputAtlasFile = new File(\"/Users/foo/FRA_shardingConverter.atlas\", filesystem);\n        final Atlas outputAtlas = new AtlasResourceLoader().load(outputAtlasFile);\n        final File input = new File(\"/Users/foo/input\", filesystem);\n        input.mkdirs();\n        final SlippyTile tile1 = SlippyTile.forName(\"11-998-708\");\n        outputAtlas.subAtlas(tile1.bounds(), AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"Should be there.\"))\n                .saveAsText(input.child(\"FRA_\" + tile1.getName() + FileSuffix.ATLAS));\n        final SlippyTile tile2 = SlippyTile.forName(\"11-998-709\");\n        outputAtlas.subAtlas(tile2.bounds(), AtlasCutType.SOFT_CUT)\n                .orElseThrow(() -> new CoreException(\"Should be there.\"))\n                .saveAsText(input.child(\"FRA_\" + tile2.getName() + FileSuffix.ATLAS));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/AtlasShellToolsDemoCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\nimport java.io.PrintStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\n/**\n * @author lcram\n */\npublic class AtlasShellToolsDemoCommandTest\n{\n    @Test\n    public void testCommand()\n    {\n        final ByteArrayOutputStream outContent1 = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent1 = new ByteArrayOutputStream();\n        final InputStream inStream = new StringResource(\"hello!\").read();\n\n        final AtlasShellToolsDemoCommand command = new AtlasShellToolsDemoCommand();\n        command.setNewOutStream(new PrintStream(outContent1));\n        command.setNewErrStream(new PrintStream(errContent1));\n        command.setNewInStream(inStream);\n        command.setNewEnvironment(Maps.hashMap(\"user.home\", \"/Users/foo\"));\n        command.runSubcommand(\"--breakfast\", \"waffles\");\n\n        Assert.assertEquals(\n                \"Using special breakfast mode:\\nwaffles\\nNow say something!\\n> You said: hello!\\n\",\n                outContent1.toString());\n        Assert.assertEquals(\"ast-demo: value of HOME environment variable: /Users/foo\\n\",\n                errContent1.toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/ConcatenateAtlasCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class ConcatenateAtlasCommandTest\n{\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final ConcatenateAtlasCommand command = new ConcatenateAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"/Users/foo/test2.atlas\", \"--verbose\",\n                    \"--output=/Users/foo\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\n                    \"fatlas: loading /Users/foo/test.atlas\\n\"\n                            + \"fatlas: loading /Users/foo/test2.atlas\\n\"\n                            + \"fatlas: processing atlas /Users/foo/test.atlas (1/2)\\n\"\n                            + \"fatlas: processing atlas /Users/foo/test2.atlas (2/2)\\n\"\n                            + \"fatlas: cloning...\\n\" + \"fatlas: saved to /Users/foo/output.atlas\\n\",\n                    errContent.toString());\n            final File outputAtlasFile = new File(\"/Users/foo/output.atlas\", filesystem);\n            final Atlas outputAtlas = new AtlasResourceLoader().load(outputAtlasFile);\n            Assert.assertEquals(2, outputAtlas.numberOfPoints());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem) throws IOException\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addPoint(1000000L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n        final Atlas atlas = builder.get();\n        final File atlasFile = new File(\"/Users/foo/test.atlas\", filesystem);\n        assert atlas != null;\n        atlas.save(atlasFile);\n\n        final PackedAtlasBuilder builder2 = new PackedAtlasBuilder();\n        builder2.addPoint(2000000L, Location.forWkt(\"POINT(2 2)\"), Maps.hashMap(\"baz\", \"bat\"));\n        final Atlas atlas2 = builder2.get();\n        final File atlasFile2 = new File(\"/Users/foo/test2.atlas\", filesystem);\n        assert atlas2 != null;\n        atlas2.save(atlasFile2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/CountryBoundaryMapPrinterCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.compression.Compressor;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author matthieun\n */\npublic class CountryBoundaryMapPrinterCommandTest\n{\n    @Test\n    public void testCommandWithInputFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            final File inputText = new File(filesystem.getPath(\"/Users/foo\", \"boundary.txt.gz\"))\n                    .withCompressor(Compressor.GZIP);\n            inputText.writeAndClose(new InputStreamResource(\n                    () -> CountryBoundaryMapPrinterCommandTest.class.getResourceAsStream(\n                            \"CountryBoundaryMapPrinterCommandTestBoundaries.txt\"))\n                    .all());\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final CountryBoundaryMapPrinterCommand command = new CountryBoundaryMapPrinterCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewErrStream(new PrintStream(outContent));\n            command.runSubcommand(\"--\" + CountryBoundaryMapPrinterCommand.BOUNDARY_OPTION_LONG\n                    + \"=/Users/foo/boundary.txt.gz\", \"--verbose\");\n            Assert.assertTrue(outContent.toString().contains(\"boundary-itemizer: Saved XYZ in\"));\n            final File outputFolder = new File(\"/Users/foo\", filesystem);\n            final File outputWkt = outputFolder.child(\"boundary-wkt\");\n            final File outputGeojson = outputFolder.child(\"boundary-geojson\");\n            Assert.assertEquals(3, outputGeojson.listFiles().size());\n            Assert.assertEquals(3, outputWkt.listFiles().size());\n            Assert.assertEquals(\"MULTIPOLYGON (((4.2038896 38.8273784, 4.2132564 38.8330031, \"\n                    + \"4.2308843 38.8337631, 4.2451948 38.8205877, 4.2422676 38.811769, \"\n                    + \"4.226396 38.8072578, 4.2102643 38.8082716, 4.2021333 38.8176483, \"\n                    + \"4.2038896 38.8273784)), ((9.1437517 41.3421313, 9.1863237 41.3591412, \"\n                    + \"9.2144762 41.338007, 9.19937 41.3173817, 9.1739641 41.3359448, \"\n                    + \"9.1616045 41.3194445, 9.1437517 41.3421313)))\",\n                    outputWkt.child(\"ABC.wkt\").all());\n            Assert.assertEquals(\n                    \"{\\\"type\\\":\\\"MultiPolygon\\\",\\\"coordinates\\\":[[[[9.1437517,41.3421313],\"\n                            + \"[9.1863237,41.3591412],[9.2144762,41.338007],[9.19937,41.3173817],\"\n                            + \"[9.1739641,41.3359448],[9.1616045,41.3194445],[9.1437517,41.3421313]]],\"\n                            + \"[[[4.2038896,38.8273784],[4.2132564,38.8330031],[4.2308843,38.8337631],\"\n                            + \"[4.2451948,38.8205877],[4.2422676,38.811769],[4.226396,38.8072578],\"\n                            + \"[4.2102643,38.8082716],[4.2021333,38.8176483],[4.2038896,38.8273784]]]]}\",\n                    outputGeojson.child(\"ABC.geojson\").all());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/CountryShardToBoundsCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class CountryShardToBoundsCommandTest\n{\n    @Test\n    public void testCountry()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final CountryShardToBoundsCommand command = new CountryShardToBoundsCommand();\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewFileSystem(filesystem);\n\n            command.runSubcommand(\"--country-boundary=/Users/foo/boundary.txt\", \"AIA\");\n\n            final String expected = \"AIA boundary:\\n\"\n                    + \"POLYGON ((-62.76312 18.1617887, -62.8763889 18.1902778,\";\n            Assert.assertEquals(expected, outContent.toString().substring(0, expected.length()));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testShard()\n    {\n        final ByteArrayOutputStream outContent1a = new ByteArrayOutputStream();\n        CountryShardToBoundsCommand command = new CountryShardToBoundsCommand();\n        command.setNewOutStream(new PrintStream(outContent1a));\n        command.runSubcommand(\"9-168-233\");\n        Assert.assertEquals(\"9-168-233 bounds:\\n\"\n                + \"POLYGON ((-61.875 15.2841851, -61.875 15.9613291, -61.171875 15.9613291, -61.171875 15.2841851, -61.875 15.2841851))\\n\",\n                outContent1a.toString());\n\n        final ByteArrayOutputStream outContent2a = new ByteArrayOutputStream();\n        command = new CountryShardToBoundsCommand();\n        command.setNewOutStream(new PrintStream(outContent2a));\n        command.runSubcommand(\"--reverse\",\n                \"POLYGON ((-61.875 15.2841851, -61.875 15.9613291, -61.171875 15.9613291, -61.171875 15.2841851, -61.875 15.2841851))\");\n        Assert.assertEquals(\n                \"POLYGON ((-61.875 15.2841851, -61.875 15.9613291, -61.171875 15.9613291, -61.171875 15.2841851, -61.875 15.2841851)) exactly matched shard:\\n\"\n                        + \"[SlippyTile: zoom = 9, x = 168, y = 233]\\n\",\n                outContent2a.toString());\n\n        final ByteArrayOutputStream outContent3a = new ByteArrayOutputStream();\n        command = new CountryShardToBoundsCommand();\n        command.setNewOutStream(new PrintStream(outContent3a));\n        command.runSubcommand(\"--reverse\",\n                \"POLYGON ((-0.3515625 -0.1757812, -0.3515625 0, 0 0, 0 -0.1757812, -0.3515625 -0.1757812))\");\n        Assert.assertEquals(\n                \"POLYGON ((-0.3515625 -0.1757812, -0.3515625 0, 0 0, 0 -0.1757812, -0.3515625 -0.1757812)) exactly matched shard:\\n\"\n                        + \"[GeoHashTile: value = 7zzz]\\n\",\n                outContent3a.toString());\n    }\n\n    private void setupFilesystem(final FileSystem filesystem) throws IOException\n    {\n        final File boundaryFile = new File(filesystem.getPath(\"/Users/foo\", \"boundary.txt\"));\n        boundaryFile.writeAndClose(CountryShardToBoundsCommandTest.class\n                .getResourceAsStream(\"MAF_AIA_osm_boundaries_with_grid_index.txt\").readAllBytes());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/HelloWorldCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author lcram\n */\npublic class HelloWorldCommandTest\n{\n    @Test\n    public void test()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        HelloWorldCommand command = new HelloWorldCommand();\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n\n        command.runSubcommand(\"--verbose\");\n\n        Assert.assertEquals(\"Hello, world!\\n\", outContent.toString());\n        Assert.assertTrue(errContent.toString().isEmpty());\n\n        final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n        command = new HelloWorldCommand();\n        command.setNewOutStream(new PrintStream(outContent2));\n        command.setNewErrStream(new PrintStream(errContent2));\n\n        command.runSubcommand(\"--verbose\", \"--name=foo\");\n\n        Assert.assertEquals(\"Hello, foo!\\n\", outContent2.toString());\n        Assert.assertTrue(errContent2.toString().isEmpty());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/IsoCountryCodeCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.locale.IsoCountry;\nimport org.openstreetmap.atlas.locale.IsoCountryFuzzyMatcher;\n\n/**\n * @author lcram\n */\npublic class IsoCountryCodeCommandTest\n{\n    @Test\n    public void test()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        IsoCountryCodeCommand command = new IsoCountryCodeCommand();\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n        command.runSubcommand(\"USA\");\n        Assert.assertEquals(\"ISO code 'USA' matched: \\n\" + \"US   USA   United States\\n\",\n                outContent.toString());\n        Assert.assertEquals(\"\", errContent.toString());\n\n        final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent2 = new ByteArrayOutputStream();\n        command = new IsoCountryCodeCommand();\n        command.setNewOutStream(new PrintStream(outContent2));\n        command.setNewErrStream(new PrintStream(errContent2));\n        command.runSubcommand(\"FOO\");\n        Assert.assertEquals(\n                \"Display country name 'FOO' had no exact matches. 10 closest matches are:\\n\"\n                        + \"ST   STP   São Tomé & Príncipe\\n\" + \"TG   TGO   Togo\\n\"\n                        + \"IM   IMN   Isle of Man\\n\" + \"CK   COK   Cook Islands\\n\"\n                        + \"BF   BFA   Burkina Faso\\n\" + \"PG   PNG   Papua New Guinea\\n\"\n                        + \"AG   ATG   Antigua & Barbuda\\n\" + \"PM   SPM   St. Pierre & Miquelon\\n\"\n                        + \"LA   LAO   Laos\\n\" + \"FO   FRO   Faroe Islands\\n\",\n                outContent2.toString());\n        Assert.assertEquals(\"\", errContent2.toString());\n\n        final ByteArrayOutputStream outContent3 = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent3 = new ByteArrayOutputStream();\n        command = new IsoCountryCodeCommand();\n        command.setNewOutStream(new PrintStream(outContent3));\n        command.setNewErrStream(new PrintStream(errContent3));\n        command.runSubcommand(\"US\");\n        Assert.assertEquals(\"ISO code 'US' matched: \\n\" + \"US   USA   United States\\n\",\n                outContent3.toString());\n        Assert.assertEquals(\"\", errContent3.toString());\n\n        final ByteArrayOutputStream outContent4 = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent4 = new ByteArrayOutputStream();\n        command = new IsoCountryCodeCommand();\n        command.setNewOutStream(new PrintStream(outContent4));\n        command.setNewErrStream(new PrintStream(errContent4));\n        command.runSubcommand(\"United States\");\n        Assert.assertEquals(\n                \"Display country name 'United States' matched: \\n\" + \"US   USA   United States\\n\",\n                outContent4.toString());\n        Assert.assertEquals(\"\", errContent4.toString());\n\n        final ByteArrayOutputStream outContent5 = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent5 = new ByteArrayOutputStream();\n        command = new IsoCountryCodeCommand();\n        command.setNewOutStream(new PrintStream(outContent5));\n        command.setNewErrStream(new PrintStream(errContent5));\n        command.runSubcommand(\"Martin\");\n        Assert.assertEquals(\n                \"Display country name 'Martin' had no exact matches. 10 closest matches are:\\n\"\n                        + \"MF   MAF   St. Martin\\n\" + \"SM   SMR   San Marino\\n\"\n                        + \"SX   SXM   Sint Maarten\\n\" + \"ML   MLI   Mali\\n\" + \"BH   BHR   Bahrain\\n\"\n                        + \"MP   MNP   Northern Mariana Islands\\n\"\n                        + \"SJ   SJM   Svalbard & Jan Mayen\\n\" + \"HT   HTI   Haiti\\n\"\n                        + \"VG   VGB   British Virgin Islands\\n\" + \"IM   IMN   Isle of Man\\n\",\n                outContent5.toString());\n        Assert.assertEquals(\"\", errContent5.toString());\n\n        final ByteArrayOutputStream outContent6 = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent6 = new ByteArrayOutputStream();\n        command = new IsoCountryCodeCommand();\n        command.setNewOutStream(new PrintStream(outContent6));\n        command.setNewErrStream(new PrintStream(errContent6));\n        command.runSubcommand(\"Martin\", \"USA\", \"United Kingdom\", \"-n3\");\n        Assert.assertEquals(\n                \"Display country name 'Martin' had no exact matches. 3 closest matches are:\\n\"\n                        + \"MF   MAF   St. Martin\\n\" + \"SM   SMR   San Marino\\n\"\n                        + \"SX   SXM   Sint Maarten\\n\" + \"\\n\" + \"ISO code 'USA' matched: \\n\"\n                        + \"US   USA   United States\\n\" + \"\\n\"\n                        + \"Display country name 'United Kingdom' matched: \\n\"\n                        + \"GB   GBR   United Kingdom\\n\",\n                outContent6.toString());\n        Assert.assertEquals(\"\", errContent6.toString());\n\n        final ByteArrayOutputStream outContent7 = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent7 = new ByteArrayOutputStream();\n        command = new IsoCountryCodeCommand();\n        command.setNewOutStream(new PrintStream(outContent7));\n        command.setNewErrStream(new PrintStream(errContent7));\n        command.runSubcommand(\"usA\", \"-n3\");\n        Assert.assertEquals(\n                \"Display country name 'usA' had no exact matches. 3 closest matches are:\\n\"\n                        + \"UM   UMI   U.S. Outlying Islands\\n\" + \"CU   CUB   Cuba\\n\"\n                        + \"SM   SMR   San Marino\\n\",\n                outContent7.toString());\n        Assert.assertEquals(\"iso-country-code: warn: did you mean case-sensitive ISO code 'USA'?\\n\",\n                errContent7.toString());\n    }\n\n    @Test\n    public void testAllCount()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final IsoCountryCodeCommand command = new IsoCountryCodeCommand();\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n        command.runSubcommand(\"--all\");\n        Assert.assertEquals(250L, outContent.toString().lines().count());\n    }\n\n    @Test\n    public void testFuzzyMatcher()\n    {\n        final List<IsoCountry> results = IsoCountryFuzzyMatcher.forDisplayCountryTopMatches(1,\n                \"United States\");\n        Assert.assertEquals(1, results.size());\n        Assert.assertEquals(\"US\", results.get(0).getCountryCode());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/JavaToProtoSerializationCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class JavaToProtoSerializationCommandTest\n{\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final JavaToProtoSerializationCommand command = new JavaToProtoSerializationCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/java-atlas.atlas\", \"--verbose\");\n\n            Assert.assertEquals(\"Saved to /work/java-atlas.atlas\\n\", outContent.toString());\n            Assert.assertEquals(\n                    \"java2proto: loading /Users/foo/java-atlas.atlas\\n\"\n                            + \"java2proto: processing atlas /Users/foo/java-atlas.atlas (1/1)\\n\",\n                    errContent.toString());\n            Assert.assertEquals(PackedAtlas.AtlasSerializationFormat.PROTOBUF,\n                    ((PackedAtlas) new AtlasResourceLoader()\n                            .load(new File(\"/work/java-atlas.atlas\", filesystem)))\n                            .getSerializationFormat());\n\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testCheck()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final JavaToProtoSerializationCommand command = new JavaToProtoSerializationCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/proto-atlas.atlas\", \"/Users/foo/java-atlas.atlas\",\n                    \"--check\", \"--verbose\");\n\n            Assert.assertEquals(\n                    \"atlas /Users/foo/proto-atlas.atlas format: PROTOBUF\\n\"\n                            + \"atlas /Users/foo/java-atlas.atlas format: JAVA\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"java2proto: loading /Users/foo/proto-atlas.atlas\\n\"\n                            + \"java2proto: loading /Users/foo/java-atlas.atlas\\n\"\n                            + \"java2proto: processing atlas /Users/foo/proto-atlas.atlas (1/2)\\n\"\n                            + \"java2proto: processing atlas /Users/foo/java-atlas.atlas (2/2)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testReverse()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final JavaToProtoSerializationCommand command = new JavaToProtoSerializationCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/proto-atlas.atlas\", \"--reverse\", \"--verbose\");\n\n            Assert.assertEquals(\"Saved to /work/proto-atlas.atlas\\n\", outContent.toString());\n            Assert.assertEquals(\n                    \"java2proto: loading /Users/foo/proto-atlas.atlas\\n\"\n                            + \"java2proto: processing atlas /Users/foo/proto-atlas.atlas (1/1)\\n\",\n                    errContent.toString());\n            Assert.assertEquals(PackedAtlas.AtlasSerializationFormat.JAVA,\n                    ((PackedAtlas) new AtlasResourceLoader()\n                            .load(new File(\"/work/proto-atlas.atlas\", filesystem)))\n                            .getSerializationFormat());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n\n        builder.addPoint(1L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n        builder.addPoint(2L, Location.forWkt(\"POINT(2 2)\"), Maps.hashMap(\"baz\", \"bat\"));\n        builder.addPoint(3L, Location.forWkt(\"POINT(3 3)\"),\n                Maps.hashMap(\"foo\", \"bar\", \"baz\", \"bat\"));\n\n        final Atlas atlas = builder.get();\n        assert atlas != null;\n\n        final File protoAtlasFile = new File(\"/Users/foo/proto-atlas.atlas\", filesystem);\n        final File javaAtlasFile = new File(\"/Users/foo/java-atlas.atlas\", filesystem);\n\n        ((PackedAtlas) atlas)\n                .setSaveSerializationFormat(PackedAtlas.AtlasSerializationFormat.PROTOBUF);\n        atlas.save(protoAtlasFile);\n\n        ((PackedAtlas) atlas).setSaveSerializationFormat(PackedAtlas.AtlasSerializationFormat.JAVA);\n        atlas.save(javaAtlasFile);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/OsmFileParserCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class OsmFileParserCommandTest\n{\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final OsmFileParserCommand command = new OsmFileParserCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"--verbose\", \"/Users/foo/test.josm.osm\", \"/Users/foo/test.osm\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\"\", errContent.toString());\n            Assert.assertTrue(new File(\"/Users/foo/test.osm\", filesystem).exists());\n            Assert.assertEquals(\n                    new String(OsmFileParserCommandTest.class\n                            .getResourceAsStream(\"MultiPolygonTest.osm\").readAllBytes()),\n                    new File(\"/Users/foo/test.osm\", filesystem).readAndClose() + \"\\n\");\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem) throws IOException\n    {\n        final File josmOsmFile = new File(filesystem.getPath(\"/Users/foo\", \"test.josm.osm\"));\n        josmOsmFile.writeAndClose(OsmFileParserCommandTest.class\n                .getResourceAsStream(\"MultiPolygonTest.josm.osm\").readAllBytes());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/OsmToAtlasCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class OsmToAtlasCommandTest\n{\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final OsmToAtlasCommand command = new OsmToAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"--verbose\", \"/Users/foo/test.josm.osm\", \"/Users/foo/test.atlas\",\n                    \"--josm\", \"--country=DMA\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertTrue(errContent.toString().isEmpty());\n            final File outputAtlasFile = new File(\"/Users/foo/test.atlas\", filesystem);\n            Assert.assertTrue(outputAtlasFile.exists());\n            final Atlas outputAtlas = new AtlasResourceLoader()\n                    .load(new InputStreamResource(outputAtlasFile::read)\n                            .withName(outputAtlasFile.getAbsolutePathString()));\n            Assert.assertEquals(4, outputAtlas.numberOfAreas());\n            Assert.assertEquals(1, outputAtlas.numberOfRelations());\n            Assert.assertEquals(\"DMA\",\n                    outputAtlas.area(102506000000L).getTag(ISOCountryTag.KEY).orElse(\"\"));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem) throws IOException\n    {\n        final File josmOsmFile = new File(filesystem.getPath(\"/Users/foo\", \"test.josm.osm\"));\n        josmOsmFile.writeAndClose(OsmFileParserCommandTest.class\n                .getResourceAsStream(\"MultiPolygonTest.josm.osm\").readAllBytes());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/PackedToTextAtlasCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class PackedToTextAtlasCommandTest\n{\n    @Test\n    public void testReverse()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final PackedToTextAtlasCommand command = new PackedToTextAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/text.atlas.txt\", \"--verbose\", \"--output=/Users/foo\",\n                    \"--reverse\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\n                    \"packed2text: loading /Users/foo/text.atlas.txt\\n\"\n                            + \"packed2text: processing atlas /Users/foo/text.atlas.txt (1/1)\\n\"\n                            + \"packed2text: converting /Users/foo/text.atlas.txt...\\n\"\n                            + \"packed2text: saved to /Users/foo/text.atlas\\n\",\n                    errContent.toString());\n            final File outputAtlasFile = new File(\"/Users/foo/text.atlas\", filesystem);\n            final Atlas outputAtlas = new AtlasResourceLoader()\n                    .load(new InputStreamResource(outputAtlasFile::read)\n                            .withName(outputAtlasFile.getAbsolutePathString()));\n            Assert.assertEquals(1, outputAtlas.numberOfPoints());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testToGeoJson()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final PackedToTextAtlasCommand command = new PackedToTextAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/text.atlas.txt\", \"--verbose\", \"--output=/Users/foo\",\n                    \"--geojson\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\n                    \"packed2text: loading /Users/foo/text.atlas.txt\\n\"\n                            + \"packed2text: processing atlas /Users/foo/text.atlas.txt (1/1)\\n\"\n                            + \"packed2text: converting /Users/foo/text.atlas.txt...\\n\"\n                            + \"packed2text: saved to /Users/foo/text.geojson\\n\",\n                    errContent.toString());\n            final File outputFile = new File(\"/Users/foo/text.geojson\", filesystem);\n            Assert.assertEquals(\n                    \"{\\\"type\\\":\\\"FeatureCollection\\\",\\\"features\\\":[{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[1.0,1.0]},\"\n                            + \"\\\"properties\\\":{\\\"foo\\\":\\\"bar\\\",\\\"identifier\\\":1000000,\\\"osmIdentifier\\\":1,\\\"itemType\\\":\\\"POINT\\\"}}],\"\n                            + \"\\\"properties\\\":{\\\"size\\\":{\\\"Number of Edges\\\":0,\\\"Number of Nodes\\\":0,\\\"Number of Areas\\\":0,\\\"Number of Lines\\\":0,\\\"Number of Points\\\":1,\\\"Number of Relations\\\":0},\"\n                            + \"\\\"original\\\":true,\\\"Code Version\\\":\\\"unknown\\\",\\\"Data Version\\\":\\\"TextAtlas\\\",\\\"Country\\\":\\\"unknown\\\",\\\"Shard Name\\\":\\\"unknown\\\",\\\"name\\\":\\\"text.atlas.txt\\\",\\\"Entity filter used\\\":true}}\",\n                    outputFile.readAndClose());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testToLDGeoJson()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final PackedToTextAtlasCommand command = new PackedToTextAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/binary.atlas\", \"--verbose\", \"--output=/Users/foo\",\n                    \"--ldgeojson\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\n                    \"packed2text: loading /Users/foo/binary.atlas\\n\"\n                            + \"packed2text: processing atlas /Users/foo/binary.atlas (1/1)\\n\"\n                            + \"packed2text: converting /Users/foo/binary.atlas...\\n\"\n                            + \"packed2text: saved to /Users/foo/binary.geojson\\n\",\n                    errContent.toString());\n            final File outputFile = new File(\"/Users/foo/binary.geojson\", filesystem);\n            Assert.assertEquals(\n                    \"{\\\"type\\\":\\\"Feature\\\",\\\"geometry\\\":{\\\"type\\\":\\\"Point\\\",\\\"coordinates\\\":[1.0,1.0]},\"\n                            + \"\\\"properties\\\":{\\\"foo\\\":\\\"bar\\\",\\\"identifier\\\":1000000,\\\"osmIdentifier\\\":1,\\\"itemType\\\":\\\"POINT\\\"}}\",\n                    outputFile.readAndClose());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testToText()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final PackedToTextAtlasCommand command = new PackedToTextAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/binary.atlas\", \"--verbose\", \"--output=/Users/foo\");\n\n            Assert.assertTrue(outContent.toString().isEmpty());\n            Assert.assertEquals(\n                    \"packed2text: loading /Users/foo/binary.atlas\\n\"\n                            + \"packed2text: processing atlas /Users/foo/binary.atlas (1/1)\\n\"\n                            + \"packed2text: converting /Users/foo/binary.atlas...\\n\"\n                            + \"packed2text: saved to /Users/foo/binary.atlas.txt\\n\",\n                    errContent.toString());\n            final File outputAtlasFile = new File(\"/Users/foo/binary.atlas.txt\", filesystem);\n            Assert.assertEquals(\n                    \"# Nodes\\n\" + \"# Edges\\n\" + \"# Areas\\n\" + \"# Lines\\n\" + \"# Points\\n\"\n                            + \"1000000 && 1.0,1.0 && foo -> bar\\n\" + \"# Relations\",\n                    outputAtlasFile.readAndClose());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n        builder.addPoint(1000000L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n        final Atlas atlas = builder.get();\n        final File atlasTextFile = new File(\"/Users/foo/text.atlas.txt\", filesystem);\n        final File atlasBinaryFile = new File(\"/Users/foo/binary.atlas\", filesystem);\n        assert atlas != null;\n        atlas.saveAsText(atlasTextFile);\n        atlas.save(atlasBinaryFile);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/PbfToAtlasCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileParser;\nimport org.openstreetmap.atlas.utilities.testing.OsmFileToPbf;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author matthieun\n */\npublic class PbfToAtlasCommandTest\n{\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final PbfToAtlasCommand command = new PbfToAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"--verbose\", \"--output=/Users/foo\", \"--countryName=FRA\",\n                    \"/Users/foo/PbfToAtlasCommandTest.pbf\");\n\n            Assert.assertEquals(\"/Users/foo/FRA_PbfToAtlasCommandTest.atlas\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\"pbf2atlas: loading /Users/foo/PbfToAtlasCommandTest.pbf\\n\",\n                    errContent.toString());\n            final File outputAtlasFile = new File(\"/Users/foo/FRA_PbfToAtlasCommandTest.atlas\",\n                    filesystem);\n            final Atlas outputAtlas = new AtlasResourceLoader()\n                    .load(new InputStreamResource(outputAtlasFile::read)\n                            .withName(outputAtlasFile.getAbsolutePathString()));\n            Assert.assertEquals(5, outputAtlas.numberOfNodes());\n            Assert.assertEquals(4, outputAtlas.numberOfEdges());\n            Assert.assertEquals(1, outputAtlas.numberOfAreas());\n            Assert.assertEquals(1, outputAtlas.numberOfLines());\n            Assert.assertEquals(1, outputAtlas.numberOfRelations());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem) throws IOException\n    {\n        final File pbfFile = new File(\"/Users/foo/PbfToAtlasCommandTest.pbf\", filesystem);\n        final Resource resource = new InputStreamResource(\n                () -> PbfToAtlasCommandTest.class.getResourceAsStream(\"testPbf2Atlas.josm.osm\"));\n        final StringResource osmFile = new StringResource();\n        new OsmFileParser().update(resource, osmFile);\n        new OsmFileToPbf().update(osmFile, pbfFile);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/SubAtlasCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class SubAtlasCommandTest\n{\n    @Test\n    public void testOtherOptions()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final SubAtlasCommand command = new SubAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\", \"--imports=java.util\",\n                    \"--predicate=!e.relations().isEmpty()\",\n                    \"--polygon=POLYGON((33 33, 35 33, 35 35, 33 35, 33 33))\", \"--slice-first\",\n                    \"--cut-type=SOFT_CUT\", \"--output=/Users/foo\", \"--verbose\");\n\n            Assert.assertEquals(\"\", outContent.toString());\n            Assert.assertEquals(\n                    \"subatlas: loading /Users/foo/test.atlas\\n\"\n                            + \"subatlas: processing atlas /Users/foo/test.atlas (1/1)\\n\"\n                            + \"subatlas: saved to /Users/foo/sub_test.atlas\\n\",\n                    errContent.toString());\n\n            final File subAtlasFile = new File(\"/Users/foo/sub_test.atlas\", filesystem);\n            final Atlas subAtlas = new AtlasResourceLoader()\n                    .load(new InputStreamResource(subAtlasFile::read)\n                            .withName(subAtlasFile.getAbsolutePathString()));\n            Assert.assertEquals(3, Iterables.size(subAtlas.entities()));\n            Assert.assertNotNull(subAtlas.node(9000000L));\n            Assert.assertNotNull(subAtlas.node(10000000L));\n            Assert.assertNotNull(subAtlas.edge(11000001L));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testPolygon()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final SubAtlasCommand command = new SubAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--polygon=POLYGON((0 0, 4 0, 4 4, 0 4, 0 0))\", \"--output=/Users/foo\",\n                    \"--verbose\");\n\n            Assert.assertEquals(\"\", outContent.toString());\n            Assert.assertEquals(\n                    \"subatlas: loading /Users/foo/test.atlas\\n\"\n                            + \"subatlas: processing atlas /Users/foo/test.atlas (1/1)\\n\"\n                            + \"subatlas: saved to /Users/foo/sub_test.atlas\\n\",\n                    errContent.toString());\n\n            final File subAtlasFile = new File(\"/Users/foo/sub_test.atlas\", filesystem);\n            final Atlas subAtlas = new AtlasResourceLoader()\n                    .load(new InputStreamResource(subAtlasFile::read)\n                            .withName(subAtlasFile.getAbsolutePathString()));\n            Assert.assertEquals(3, Iterables.size(subAtlas.entities()));\n            Assert.assertNotNull(subAtlas.point(1000000L));\n            Assert.assertNotNull(subAtlas.point(2000000L));\n            Assert.assertNotNull(subAtlas.point(3000000L));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testPredicate()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final SubAtlasCommand command = new SubAtlasCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/test.atlas\", \"--verbose\",\n                    \"--predicate=!e.relations().isEmpty()\", \"--output=/Users/foo\", \"--verbose\");\n\n            Assert.assertEquals(\"\", outContent.toString());\n            Assert.assertEquals(\n                    \"subatlas: loading /Users/foo/test.atlas\\n\"\n                            + \"subatlas: processing atlas /Users/foo/test.atlas (1/1)\\n\"\n                            + \"subatlas: saved to /Users/foo/sub_test.atlas\\n\",\n                    errContent.toString());\n\n            final File subAtlasFile = new File(\"/Users/foo/sub_test.atlas\", filesystem);\n            final Atlas subAtlas = new AtlasResourceLoader()\n                    .load(new InputStreamResource(subAtlasFile::read)\n                            .withName(subAtlasFile.getAbsolutePathString()));\n            Assert.assertEquals(5, Iterables.size(subAtlas.entities()));\n            Assert.assertNotNull(subAtlas.node(8000000L));\n            Assert.assertNotNull(subAtlas.node(9000000L));\n            Assert.assertNotNull(subAtlas.node(10000000L));\n            Assert.assertNotNull(subAtlas.edge(11000000L));\n            Assert.assertNotNull(subAtlas.edge(11000001L));\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n\n        builder.addPoint(1000000L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n        builder.addPoint(2000000L, Location.forWkt(\"POINT(2 2)\"), Maps.hashMap(\"baz\", \"bat\"));\n        builder.addPoint(3000000L, Location.forWkt(\"POINT(3 3)\"),\n                Maps.hashMap(\"foo\", \"bar\", \"baz\", \"bat\"));\n\n        builder.addLine(4000000L, PolyLine.wkt(\"LINESTRING(10 10, 11 11, 12 12)\"),\n                Maps.hashMap(\"this_is\", \"a_line\"));\n        builder.addLine(5000000L, PolyLine.wkt(\"LINESTRING(13 13, 14 14, 15 15)\"),\n                Maps.hashMap(\"this_is\", \"a_line\", \"foo\", \"bar\"));\n        builder.addLine(6000000L, PolyLine.wkt(\"LINESTRING(16 16, 17 17, 18 18)\"),\n                Maps.hashMap(\"hello\", \"world\", \"another\", \"tag1\"));\n\n        builder.addArea(7000000L, Polygon.wkt(\"POLYGON((20 20, 21 20, 21 21, 20 20))\"),\n                Maps.hashMap(\"baz\", \"bat\"));\n\n        builder.addNode(8000000L, Location.forWkt(\"POINT(30 30)\"), Maps.hashMap(\"foo\", \"bar\"));\n        builder.addNode(9000000L, Location.forWkt(\"POINT(32 32)\"), Maps.hashMap(\"baz\", \"bat\"));\n        builder.addNode(10000000L, Location.forWkt(\"POINT(34 34)\"),\n                Maps.hashMap(\"another\", \"tag2\"));\n\n        builder.addEdge(11000000L, PolyLine.wkt(\"LINESTRING(30 30, 31 30, 32 32)\"),\n                Maps.hashMap(\"foo\", \"bar\"));\n        builder.addEdge(11000001L, PolyLine.wkt(\"LINESTRING(32 32, 32 33, 34 34)\"),\n                Maps.hashMap(\"baz\", \"bat\"));\n\n        final RelationBean bean = new RelationBean();\n        bean.addItem(11000000L, \"role0\", ItemType.EDGE);\n        bean.addItem(11000001L, \"role1\", ItemType.EDGE);\n        builder.addRelation(12000000L, 12L, bean, Maps.hashMap(\"route\", \"bike\"));\n\n        final Atlas atlas = builder.get();\n        final File atlasFile = new File(\"/Users/foo/test.atlas\", filesystem);\n        assert atlas != null;\n        atlas.save(atlasFile);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/TaggableMatcherPrinterCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author lcram\n */\npublic class TaggableMatcherPrinterCommandTest\n{\n    @Test\n    public void test()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final TaggableMatcherPrinterCommand command = new TaggableMatcherPrinterCommand();\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n\n        command.runSubcommand(\"foo=bar\", \"baz=bat\", \"--verbose\");\n\n        Assert.assertEquals(\n                \"        =       \\n\" + \"    ┌───┴───┐   \\n\" + \"   foo     bar  \\n\" + \"\\n\"\n                        + \"        =       \\n\" + \"    ┌───┴───┐   \\n\" + \"   baz     bat  \\n\",\n                outContent.toString());\n        Assert.assertEquals(\"\", errContent.toString());\n    }\n\n    @Test\n    public void testFail()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final TaggableMatcherPrinterCommand command = new TaggableMatcherPrinterCommand();\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n\n        command.runSubcommand(\"foo=bar=baz\", \"--verbose\");\n\n        Assert.assertEquals(\"\", outContent.toString());\n        Assert.assertEquals(\n                \"print-matcher: error: definition `foo=bar=baz' contained nested equality operators\\n\",\n                errContent.toString());\n    }\n\n    @Test\n    public void testReverse()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final TaggableMatcherPrinterCommand command = new TaggableMatcherPrinterCommand();\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n\n        command.runSubcommand(\"--reverse\", \"foo->bar\", \"baz->bat\", \"--verbose\");\n\n        Assert.assertEquals(\"foo=bar\\n\" + \"baz=bat\\n\", outContent.toString());\n        Assert.assertEquals(\"\", errContent.toString());\n    }\n\n    @Test\n    public void testReverseFail()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final TaggableMatcherPrinterCommand command = new TaggableMatcherPrinterCommand();\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n\n        command.runSubcommand(\"--reverse\", \"foo->bar,!,bat\", \"--verbose\");\n\n        Assert.assertEquals(\"\", outContent.toString());\n        Assert.assertEquals(\n                \"print-matcher: error: Cannot transpile `foo->bar,!,bat' since composite value `bar,!,bat' contains a lone `!' operator.\\n\"\n                        + \"Expression `foo->bar,!,bat' is ambiguous and order dependent, please rewrite your TaggableFilter to remove it.\\n\",\n                errContent.toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/TemplateTestCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author lcram\n */\npublic class TemplateTestCommandTest\n{\n    @Test\n    public void test()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final TemplateTestCommand command = new TemplateTestCommand();\n\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n        command.runSubcommand(\"--list-of-numbers\", \"1,2,3\");\n\n        Assert.assertEquals(\"[1, 2, 3]\\n\", outContent.toString());\n        Assert.assertEquals(\"\", errContent.toString());\n    }\n\n    @Test\n    public void testFail()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final TemplateTestCommand command = new TemplateTestCommand();\n\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n        command.runSubcommand(\"--list-of-numbers\", \"1,foo,3\");\n\n        Assert.assertEquals(\"\", outContent.toString());\n        Assert.assertEquals(\n                \"template-test: error: could not parse number 'foo'\\n\"\n                        + \"template-test: error: failed to parse number list!\\n\",\n                errContent.toString());\n    }\n\n    @Test\n    public void testReverseContext()\n    {\n        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n        final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n        final TemplateTestCommand command = new TemplateTestCommand();\n\n        command.setNewOutStream(new PrintStream(outContent));\n        command.setNewErrStream(new PrintStream(errContent));\n        command.runSubcommand(\"--reverse\");\n\n        Assert.assertEquals(\"Using reverse context!\\n\", outContent.toString());\n        Assert.assertEquals(\"\", errContent.toString());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/WKTShardCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class WKTShardCommandTest\n{\n    @Test\n    public void testCommand()\n    {\n        final ByteArrayOutputStream outContent1a = new ByteArrayOutputStream();\n        WKTShardCommand command = new WKTShardCommand();\n        command.setNewOutStream(new PrintStream(outContent1a));\n        command.runSubcommand(\"--sharding=slippy@10\", \"POINT (0 0)\");\n        Assert.assertEquals(\n                \"POINT (0 0) covered by:\\n\" + \"[SlippyTile: zoom = 10, x = 512, y = 512]\\n\",\n                outContent1a.toString());\n\n        final ByteArrayOutputStream outContent2a = new ByteArrayOutputStream();\n        command = new WKTShardCommand();\n        command.setNewOutStream(new PrintStream(outContent2a));\n        command.runSubcommand(\"--sharding=slippy@1\", \"0-0-0\");\n        Assert.assertEquals(\n                \"0-0-0 contains or intersects:\\n\" + \"[SlippyTile: zoom = 1, x = 0, y = 0]\\n\"\n                        + \"[SlippyTile: zoom = 1, x = 1, y = 0]\\n\"\n                        + \"[SlippyTile: zoom = 1, x = 0, y = 1]\\n\"\n                        + \"[SlippyTile: zoom = 1, x = 1, y = 1]\\n\",\n                outContent2a.toString());\n\n        final ByteArrayOutputStream outContent3a = new ByteArrayOutputStream();\n        command = new WKTShardCommand();\n        command.setNewOutStream(new PrintStream(outContent3a));\n        command.runSubcommand(\"--sharding=slippy@1\", \"LINESTRING (1 1, 2 2)\");\n        Assert.assertEquals(\n                \"LINESTRING (1 1, 2 2) intersects:\\n\" + \"[SlippyTile: zoom = 1, x = 1, y = 0]\\n\",\n                outContent3a.toString());\n\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent2 = new ByteArrayOutputStream();\n            command = new WKTShardCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2));\n            command.runSubcommand(\"--sharding=dynamic@/Users/foo/sharding.txt\", \"POINT (1 1)\");\n            Assert.assertEquals(\n                    \"POINT (1 1) covered by:\\n\" + \"[SlippyTile: zoom = 1, x = 1, y = 0]\\n\",\n                    outContent2.toString());\n\n            final ByteArrayOutputStream outContent1b = new ByteArrayOutputStream();\n            command = new WKTShardCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent1b));\n            final String polygonString = \"POLYGON ((-63.07390759923378 18.20801927106241,-63.05279324986854 18.20801927106241,-63.05279324986854 18.1971750442678,-63.07390759923378 18.1971750442678,-63.07390759923378 18.20801927106241))\";\n            command.runSubcommand(\"--country-boundary=/Users/foo/boundaries.txt\", polygonString);\n            Assert.assertEquals(polygonString + \" contains or intersects:\\n\" + \"AIA\\n\",\n                    outContent1b.toString());\n\n            final ByteArrayOutputStream outContent2b = new ByteArrayOutputStream();\n            command = new WKTShardCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent2b));\n            final String pointString = \"POINT (-63.07390759923378 18.20801927106241)\";\n            command.runSubcommand(\"--country-boundary=/Users/foo/boundaries.txt\", pointString);\n            Assert.assertEquals(pointString + \" covered by:\\n\" + \"AIA\\n\", outContent2b.toString());\n\n            final ByteArrayOutputStream outContent3b = new ByteArrayOutputStream();\n            command = new WKTShardCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent3b));\n            final String lineString = \"LINESTRING (-63.07390759923378 18.20801927106241,-63.05279324986854 18.20801927106241)\";\n            command.runSubcommand(\"--country-boundary=/Users/foo/boundaries.txt\", lineString);\n            Assert.assertEquals(lineString + \" intersects:\\n\" + \"AIA\\n\", outContent3b.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testCommandWithInputFile()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent1a = new ByteArrayOutputStream();\n            final WKTShardCommand command = new WKTShardCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent1a));\n            command.runSubcommand(\"--sharding=slippy@10\", \"--input=/Users/foo/input.txt\");\n            Assert.assertEquals(\n                    \"POINT (0 0) covered by:\\n\" + \"[SlippyTile: zoom = 10, x = 512, y = 512]\\n\"\n                            + \"\\n\" + \"POINT (1 1) covered by:\\n\"\n                            + \"[SlippyTile: zoom = 10, x = 514, y = 509]\\n\",\n                    outContent1a.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testUnparseableInput()\n    {\n        final ByteArrayOutputStream errContent1a = new ByteArrayOutputStream();\n        final WKTShardCommand command = new WKTShardCommand();\n        command.setNewErrStream(new PrintStream(errContent1a));\n        command.runSubcommand(\"--sharding=slippy@10\", \"POINT (((\");\n        Assert.assertEquals(\n                \"wkt-shard: error: unable to parse 'POINT (((' as WKT or shard string\\n\",\n                errContent1a.toString());\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem) throws IOException\n    {\n        final File inputText = new File(filesystem.getPath(\"/Users/foo\", \"input.txt\"));\n        inputText.writeAndClose(\"POINT (0 0)\\nPOINT (1 1)\\n\");\n        final File shardingTree = new File(filesystem.getPath(\"/Users/foo\", \"sharding.txt\"));\n        shardingTree.writeAndClose(WKTShardCommandTest.class\n                .getResourceAsStream(\"sharding-tree-1.txt\").readAllBytes());\n        final File boundaryMap = new File(filesystem.getPath(\"/Users/foo\", \"boundaries.txt\"));\n        boundaryMap.writeAndClose(WKTShardCommandTest.class\n                .getResourceAsStream(\"MAF_AIA_osm_boundaries_with_grid_index.txt\").readAllBytes());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/AtlasLoaderCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class AtlasLoaderCommandTest\n{\n    /**\n     * @author lcram\n     */\n    private static class TestImplementationCommand extends AtlasLoaderCommand\n    {\n        @Override\n        public String getCommandName()\n        {\n            return \"test\";\n        }\n\n        @Override\n        public String getSimpleDescription()\n        {\n            return \"test\";\n        }\n\n        @Override\n        public void processAtlas(final Atlas atlas, final String atlasFileName,\n                final File atlasResource)\n        {\n            this.getCommandOutputDelegate().printlnCommandMessage(atlas.point(1L).toWkt());\n            this.getCommandOutputDelegate().printlnCommandMessage(atlas.point(2L).toWkt());\n        }\n    }\n\n    @Test\n    public void testCombine()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n            final TestImplementationCommand command = new TestImplementationCommand();\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/atlas1.atlas.txt\", \"/Users/foo/atlas2.atlas.txt\",\n                    \"--combine\");\n\n            final StringBuilder errExpected = new StringBuilder();\n            errExpected.append(\"test: POINT (1 1)\\n\").append(\"test: POINT (2 2)\\n\");\n            Assert.assertEquals(errExpected.toString(), errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testCommand()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n            final TestImplementationCommand command = new TestImplementationCommand();\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/atlas1.atlas.txt\", \"/Users/foo/atlas2.atlas.txt\",\n                    \"/Users/foo\", \"--verbose\");\n\n            final StringBuilder errExpected = new StringBuilder();\n            errExpected.append(\"test: loading /Users/foo/atlas1.atlas.txt\\n\")\n                    .append(\"test: loading /Users/foo/atlas2.atlas.txt\\n\")\n                    .append(\"test: warn: skipping directory: /Users/foo\\n\")\n                    .append(\"test: processing atlas /Users/foo/atlas1.atlas.txt (1/2)\\n\")\n                    .append(\"test: POINT (1 1)\\n\").append(\"test: POINT (2 2)\\n\")\n                    .append(\"test: processing atlas /Users/foo/atlas2.atlas.txt (2/2)\\n\")\n                    .append(\"test: POINT (1 1)\\n\").append(\"test: POINT (2 2)\\n\");\n            Assert.assertEquals(errExpected.toString(), errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testMalformedAtlas()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n\n            final TestImplementationCommand command = new TestImplementationCommand();\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/atlas_malformed.atlas.txt\");\n\n            final StringBuilder errExpected = new StringBuilder();\n            errExpected.append(\"test: warn: could not load: /Users/foo/atlas_malformed.atlas.txt\\n\")\n                    .append(\"test: error: no atlas files were loaded\\n\");\n            Assert.assertEquals(errExpected.toString(), errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testStrict()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n            final TestImplementationCommand command = new TestImplementationCommand();\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/atlas1.atlas.txt\", \"/Users/foo/atlas2.atlas.txt\",\n                    \"/Users/foo/atlas3.atlas.txt\", \"--strict\");\n\n            final StringBuilder errExpected = new StringBuilder();\n            errExpected.append(\"test: warn: file not found: /Users/foo/atlas3.atlas.txt\\n\")\n                    .append(\"test: error: strict load is missing some atlas(es)\\n\")\n                    .append(\"test: error: no atlas files were loaded\\n\");\n            Assert.assertEquals(errExpected.toString(), errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem(final FileSystem filesystem) throws IOException\n    {\n        final File atlas1File = new File(filesystem.getPath(\"/Users/foo\", \"atlas1.atlas.txt\"));\n        atlas1File.writeAndClose(AtlasLoaderCommandTest.class\n                .getResourceAsStream(\"atlas1.atlas.txt\").readAllBytes());\n        final File atlas2File = new File(filesystem.getPath(\"/Users/foo\", \"atlas2.atlas.txt\"));\n        atlas2File.writeAndClose(AtlasLoaderCommandTest.class\n                .getResourceAsStream(\"atlas1.atlas.txt\").readAllBytes());\n        final File atlasMalformedFile = new File(\n                filesystem.getPath(\"/Users/foo\", \"atlas_malformed.atlas.txt\"));\n        atlasMalformedFile.writeAndClose(AtlasLoaderCommandTest.class\n                .getResourceAsStream(\"atlas_malformed.atlas.txt\").readAllBytes());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/AtlasLoaderTemplateTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.packed.PackedAtlasBuilder;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class AtlasLoaderTemplateTest\n{\n    /**\n     * @author lcram\n     */\n    private static class TestCommand extends AbstractAtlasShellToolsCommand\n    {\n        @Override\n        public int execute()\n        {\n            AtlasLoaderTemplate.execute(this, this::start, this::processAtlas, this::finish);\n            return 0;\n        }\n\n        @Override\n        public String getCommandName()\n        {\n            return \"test-command\";\n        }\n\n        @Override\n        public String getSimpleDescription()\n        {\n            return \"unit test command\";\n        }\n\n        @Override\n        public void registerManualPageSections()\n        {\n\n        }\n\n        @Override\n        public void registerOptionsAndArguments()\n        {\n            registerOptionsAndArgumentsFromTemplate(new AtlasLoaderTemplate());\n            super.registerOptionsAndArguments();\n        }\n\n        private int finish()\n        {\n            return 0;\n        }\n\n        private void processAtlas(final Atlas atlas, final String atlasFileName,\n                final File atlasResource)\n        {\n            this.getCommandOutputDelegate().printlnStdout(atlasFileName);\n            this.getCommandOutputDelegate().printlnStdout(atlas.metaData().getSize().toString());\n        }\n\n        private int start()\n        {\n            return 0;\n        }\n    }\n\n    @Test\n    public void test()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasLoaderTemplateTest.TestCommand command = new AtlasLoaderTemplateTest.TestCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/atlas1.atlas\", \"/Users/foo/atlas2.atlas\",\n                    \"--verbose\");\n\n            Assert.assertEquals(\"atlas1.atlas\\n\"\n                    + \"[AtlasSize: edgeNumber=0, nodeNumber=0, areaNumber=0, lineNumber=0, pointNumber=3, relationNumber=0]\\n\"\n                    + \"atlas2.atlas\\n\"\n                    + \"[AtlasSize: edgeNumber=0, nodeNumber=0, areaNumber=0, lineNumber=0, pointNumber=3, relationNumber=0]\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"test-command: loading /Users/foo/atlas1.atlas\\n\"\n                            + \"test-command: loading /Users/foo/atlas2.atlas\\n\"\n                            + \"test-command: processing atlas /Users/foo/atlas1.atlas (1/2)\\n\"\n                            + \"test-command: processing atlas /Users/foo/atlas2.atlas (2/2)\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testCombine()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasLoaderTemplateTest.TestCommand command = new AtlasLoaderTemplateTest.TestCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/atlas1.atlas\", \"/Users/foo/atlas2.atlas\", \"--combine\",\n                    \"--verbose\");\n\n            Assert.assertEquals(\"combined.atlas\\n\"\n                    + \"[AtlasSize: edgeNumber=0, nodeNumber=0, areaNumber=0, lineNumber=0, pointNumber=3, relationNumber=0]\\n\",\n                    outContent.toString());\n            Assert.assertEquals(\n                    \"test-command: loading /Users/foo/atlas1.atlas\\n\"\n                            + \"test-command: loading /Users/foo/atlas2.atlas\\n\"\n                            + \"test-command: processing all atlases as one multiatlas...\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testStrictFail()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final AtlasLoaderTemplateTest.TestCommand command = new AtlasLoaderTemplateTest.TestCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"/Users/foo/atlas1.atlas\", \"/Users/foo/atlas2.atlas\",\n                    \"/Users/foo/atlas3.atlas\", \"--strict\", \"--verbose\");\n\n            Assert.assertEquals(\"\", outContent.toString());\n            Assert.assertEquals(\n                    \"test-command: loading /Users/foo/atlas1.atlas\\n\"\n                            + \"test-command: loading /Users/foo/atlas2.atlas\\n\"\n                            + \"test-command: warn: file not found: /Users/foo/atlas3.atlas\\n\"\n                            + \"test-command: error: strict load is missing some atlas(es)\\n\"\n                            + \"test-command: error: no atlas files were loaded\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final PackedAtlasBuilder builder = new PackedAtlasBuilder();\n\n        builder.addPoint(1L, Location.forWkt(\"POINT(1 1)\"), Maps.hashMap(\"foo\", \"bar\"));\n        builder.addPoint(2L, Location.forWkt(\"POINT(2 2)\"), Maps.hashMap(\"baz\", \"bat\"));\n        builder.addPoint(3L, Location.forWkt(\"POINT(3 3)\"),\n                Maps.hashMap(\"foo\", \"bar\", \"baz\", \"bat\"));\n\n        final Atlas atlas = builder.get();\n        assert atlas != null;\n\n        final File atlasFile1 = new File(\"/Users/foo/atlas1.atlas\", filesystem);\n        final File atlasFile2 = new File(\"/Users/foo/atlas2.atlas\", filesystem);\n        atlas.save(atlasFile1);\n        atlas.save(atlasFile2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/MultipleOutputCommandTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class MultipleOutputCommandTest\n{\n    /**\n     * @author lcram\n     */\n    private static class TestImplementationCommand extends MultipleOutputCommand\n    {\n        @Override\n        public int execute()\n        {\n            // set up the output path from the parent class\n            final int code = super.execute();\n            if (code != 0)\n            {\n                return code;\n            }\n\n            // Write two files to the output path\n            final File file1 = new File(this.getFileSystem()\n                    .getPath(this.getOutputPath().toAbsolutePath().toString(), \"file1.txt\"));\n            file1.writeAndClose(\"file1\");\n            final File file2 = new File(this.getFileSystem()\n                    .getPath(this.getOutputPath().toAbsolutePath().toString(), \"file2.txt\"));\n            file2.writeAndClose(\"file2\");\n\n            return 0;\n        }\n\n        @Override\n        public String getCommandName()\n        {\n            return \"test\";\n        }\n\n        @Override\n        public String getSimpleDescription()\n        {\n            return \"test\";\n        }\n    }\n\n    @Test\n    public void testCommand()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n            final TestImplementationCommand command = new TestImplementationCommand();\n            command.setNewFileSystem(filesystem);\n            command.runSubcommand(\"--output=/Users/foo/output\");\n\n            final File file1 = new File(\n                    command.getFileSystem().getPath(\"/Users/foo/output\", \"file1.txt\"));\n            Assert.assertEquals(\"file1\", file1.all());\n\n            final File file2 = new File(\n                    command.getFileSystem().getPath(\"/Users/foo/output\", \"file2.txt\"));\n            Assert.assertEquals(\"file2\", file2.all());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testFailures()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem(filesystem);\n            final TestImplementationCommand command = new TestImplementationCommand();\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n            command.runSubcommand(\"--output=/Users/foo/bar\");\n            final StringBuilder errExpected = new StringBuilder();\n            errExpected.append(\"test: error: /Users/foo/bar already exists and is a file\\n\")\n                    .append(\"test: error: invalid output path\\n\");\n            Assert.assertEquals(errExpected.toString(), errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem(final FileSystem filesystem)\n    {\n        new File(filesystem.getPath(\"/Users/foo\")).mkdirs();\n        new File(filesystem.getPath(\"/Users/foo/bar\")).writeAndClose(\"bar\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/command/subcommands/templates/OutputDirectoryTemplateTest.java",
    "content": "package org.openstreetmap.atlas.utilities.command.subcommands.templates;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.FileSystem;\nimport java.nio.file.Path;\nimport java.util.Optional;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.resource.File;\nimport org.openstreetmap.atlas.utilities.command.abstractcommand.AbstractAtlasShellToolsCommand;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\n/**\n * @author lcram\n */\npublic class OutputDirectoryTemplateTest\n{\n    /**\n     * @author lcram\n     */\n    private static class TestCommand extends AbstractAtlasShellToolsCommand\n    {\n        @Override\n        public int execute()\n        {\n            final Optional<Path> output = OutputDirectoryTemplate.getOutputPath(this);\n            if (output.isEmpty())\n            {\n                this.getCommandOutputDelegate().printlnErrorMessage(\"could not get output path\");\n                return 1;\n            }\n            this.getCommandOutputDelegate().printlnCommandMessage(\n                    \"output path is \" + output.get().toAbsolutePath().toString());\n            return 0;\n        }\n\n        @Override\n        public String getCommandName()\n        {\n            return \"test-command\";\n        }\n\n        @Override\n        public String getSimpleDescription()\n        {\n            return \"unit test command\";\n        }\n\n        @Override\n        public void registerManualPageSections()\n        {\n\n        }\n\n        @Override\n        public void registerOptionsAndArguments()\n        {\n            registerOptionsAndArgumentsFromTemplate(new OutputDirectoryTemplate());\n            super.registerOptionsAndArguments();\n        }\n    }\n\n    @Test\n    public void testNoOption()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final TestCommand command = new TestCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"--verbose\");\n\n            Assert.assertEquals(\"\", outContent.toString());\n            Assert.assertEquals(\"test-command: output path is /work\\n\", errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    @Test\n    public void testOption()\n    {\n        try (FileSystem filesystem = Jimfs.newFileSystem(Configuration.osX()))\n        {\n            setupFilesystem1(filesystem);\n            final ByteArrayOutputStream outContent = new ByteArrayOutputStream();\n            final ByteArrayOutputStream errContent = new ByteArrayOutputStream();\n            final TestCommand command = new TestCommand();\n            command.setNewFileSystem(filesystem);\n            command.setNewOutStream(new PrintStream(outContent));\n            command.setNewErrStream(new PrintStream(errContent));\n\n            command.runSubcommand(\"--verbose\", \"--output-directory=/Users/foo/bar\");\n\n            Assert.assertEquals(\"\", outContent.toString());\n            Assert.assertEquals(\"test-command: output path is /Users/foo/bar\\n\",\n                    errContent.toString());\n        }\n        catch (final IOException exception)\n        {\n            throw new CoreException(\"FileSystem operation failed\", exception);\n        }\n    }\n\n    private void setupFilesystem1(final FileSystem filesystem)\n    {\n        final File helloText = new File(\"/Users/foo/hello.txt\", filesystem);\n        helloText.writeAndClose(\"Hello world!\");\n        final File helloText2 = new File(\"/Users/foo/bar/hello2.txt\", filesystem);\n        helloText2.writeAndClose(\"Hello world again!\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/configuration/ConfiguredFilterTest.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.MultiPolygonTest;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteNode;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\nimport org.openstreetmap.atlas.geography.atlas.items.Node;\nimport org.openstreetmap.atlas.streaming.resource.FileSuffix;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.tags.HighwayTag;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlasHandler;\n\nimport com.google.gson.GsonBuilder;\n\n/**\n * @author lcram\n * @author matthieun\n */\npublic class ConfiguredFilterTest\n{\n    private static final String NAMESAKE_JSON = ConfiguredFilterTest.class.getSimpleName()\n            + FileSuffix.JSON;\n\n    public Atlas getAtlasFrom(final String name, final Class<?> clazz)\n    {\n        return TestAtlasHandler.getAtlasFromJosmOsmResource(true,\n                new InputStreamResource(() -> clazz.getResourceAsStream(name)), name);\n    }\n\n    @Test\n    public void testFilterAndMatcher()\n    {\n        final ConfiguredFilter junctionRoundaboutFilter = get(\"junctionRoundaboutFilter\");\n        final ConfiguredFilter dummyFilter = get(\"dummyFilter\");\n        final ConfiguredFilter tagFilterOnly = get(\"tagFilterOnly\");\n        final ConfiguredFilter defaultFilter = get(\"I am not there\");\n        final ConfiguredFilter nothingGoesThroughFilter = get(\"nothingGoesThroughFilter\");\n        final ConfiguredFilter regexFilterOnly = get(\"regexFilterOnly\");\n        final ConfiguredFilter taggableMatcherOnly = get(\"taggableMatcherOnly\");\n        final ConfiguredFilter unsafePredicateFilter = get(\"unsafePredicateFilter\");\n        final ConfiguredFilter taggableMatcherAndFilter = get(\"taggableMatcherAndFilter\");\n\n        final Edge edge = new CompleteEdge(123L, null,\n                Maps.hashMap(\"junction\", \"roundabout\", \"source\", \"illegal source\"), null, null,\n                null);\n        final Node node = new CompleteNode(124L, null, Maps.hashMap(\"source\", \"local knowledge\"),\n                null, null, null);\n\n        Assert.assertTrue(junctionRoundaboutFilter.test(edge));\n        Assert.assertFalse(dummyFilter.test(edge));\n        Assert.assertTrue(tagFilterOnly.test(edge));\n        Assert.assertTrue(defaultFilter.test(edge));\n        Assert.assertFalse(nothingGoesThroughFilter.test(edge));\n        Assert.assertTrue(regexFilterOnly.test(edge));\n        Assert.assertFalse(regexFilterOnly.test(node));\n        Assert.assertTrue(unsafePredicateFilter.test(edge));\n        Assert.assertFalse(taggableMatcherOnly.test(edge));\n        Assert.assertTrue(taggableMatcherOnly.test(node));\n        Assert.assertTrue(taggableMatcherAndFilter.test(edge));\n    }\n\n    @Test\n    public void testGeometryFilter1()\n    {\n        final ConfiguredFilter geometryFilter = get(\"geometryFilter1\");\n        final Atlas atlas = getAtlasFrom(\"geometryFilter.josm.osm\");\n\n        // Make sure the MultiPolygon is the same in the Atlas Relation (origin of the test WKT and\n        // WKB # 2, and in the filter).\n        final MultiPolygon multiPolygon = getGeometryFrom(\"geometryFilter.josm.osm\");\n        Assert.assertEquals(multiPolygon, geometryFilter.getGeometryBasedFilters().get(1));\n\n        final Edge motorway = atlas.edges(entity -> HighwayTag.MOTORWAY.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertTrue(geometryFilter.test(motorway));\n        final Edge trunk = atlas.edges(entity -> HighwayTag.TRUNK.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertFalse(geometryFilter.test(trunk));\n        final Edge primary = atlas.edges(entity -> HighwayTag.PRIMARY.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertTrue(geometryFilter.test(primary));\n        final Edge secondary = atlas\n                .edges(entity -> HighwayTag.SECONDARY.equals(entity.highwayTag())).iterator()\n                .next();\n        Assert.assertTrue(geometryFilter.test(secondary));\n    }\n\n    @Test\n    public void testGeometryFilter2()\n    {\n        final ConfiguredFilter geometryFilter = get(\"geometryFilter2\");\n        final Atlas atlas = getAtlasFrom(\"geometryFilter.josm.osm\");\n\n        final Edge motorway = atlas.edges(entity -> HighwayTag.MOTORWAY.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertTrue(geometryFilter.test(motorway));\n        final Edge trunk = atlas.edges(entity -> HighwayTag.TRUNK.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertFalse(geometryFilter.test(trunk));\n        final Edge primary = atlas.edges(entity -> HighwayTag.PRIMARY.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertTrue(geometryFilter.test(primary));\n        final Edge secondary = atlas\n                .edges(entity -> HighwayTag.SECONDARY.equals(entity.highwayTag())).iterator()\n                .next();\n        Assert.assertTrue(geometryFilter.test(secondary));\n    }\n\n    @Test\n    public void testGeometryFilter3()\n    {\n        final ConfiguredFilter geometryFilter = get(\"my.root\", \"geometryFilter3\",\n                \"ConfiguredFilterTestOtherRoot.json\");\n        final Atlas atlas = getAtlasFrom(\"geometryFilter.josm.osm\");\n\n        final Edge motorway = atlas.edges(entity -> HighwayTag.MOTORWAY.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertTrue(geometryFilter.test(motorway));\n        final Edge trunk = atlas.edges(entity -> HighwayTag.TRUNK.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertFalse(geometryFilter.test(trunk));\n        final Edge primary = atlas.edges(entity -> HighwayTag.PRIMARY.equals(entity.highwayTag()))\n                .iterator().next();\n        Assert.assertTrue(geometryFilter.test(primary));\n        final Edge secondary = atlas\n                .edges(entity -> HighwayTag.SECONDARY.equals(entity.highwayTag())).iterator()\n                .next();\n        Assert.assertFalse(geometryFilter.test(secondary));\n    }\n\n    @Test\n    public void testIsNoExpansion()\n    {\n        Assert.assertTrue(get(\"nothingGoesThroughFilter\").isNoExpansion());\n    }\n\n    @Test\n    public void testToJson()\n    {\n        final ConfiguredFilter tagFilterOnly = get(\"tagFilterOnly\");\n        final String jsonString1 = new GsonBuilder().disableHtmlEscaping().create()\n                .toJson(tagFilterOnly.toJson());\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"_filter\\\",\\\"name\\\":\\\"tagFilterOnly\\\",\\\"taggableFilter\\\":\\\"junction->roundabout\\\",\\\"noExpansion\\\":false}\",\n                jsonString1);\n\n        final ConfiguredFilter unsafePredicateFilter = get(\"unsafePredicateFilter\");\n        final String jsonString2 = new GsonBuilder().disableHtmlEscaping().create()\n                .toJson(unsafePredicateFilter.toJson());\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"_filter\\\",\\\"name\\\":\\\"unsafePredicateFilter\\\",\\\"unsafePredicate\\\":\\\"e instanceof Edge\\\",\\\"imports\\\":[\\\"org.openstreetmap.atlas.geography.atlas.items\\\"],\\\"noExpansion\\\":false}\",\n                jsonString2);\n\n        final ConfiguredFilter dummyFilter = get(\"dummyFilter\");\n        final String jsonString3 = new GsonBuilder().disableHtmlEscaping().create()\n                .toJson(dummyFilter.toJson());\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"_filter\\\",\\\"name\\\":\\\"dummyFilter\\\",\\\"predicate\\\":\\\"\\\\\\\"yes\\\\\\\".equals(e.getTag(\\\\\\\"dummy\\\\\\\"))\\\",\\\"imports\\\":[\\\"org.openstreetmap.atlas.geography.atlas.items\\\"],\\\"noExpansion\\\":false}\",\n                jsonString3);\n\n        final ConfiguredFilter regexFilterOnly = get(\"regexFilterOnly\");\n        final String jsonString4 = new GsonBuilder().disableHtmlEscaping().create()\n                .toJson(regexFilterOnly.toJson());\n        Assert.assertEquals(\n                \"{\\\"type\\\":\\\"_filter\\\",\\\"name\\\":\\\"regexFilterOnly\\\",\\\"regexTaggableFilter\\\":\\\"source,highway|.*illegal.*,.*secondary.*\\\",\\\"noExpansion\\\":false}\",\n                jsonString4);\n    }\n\n    private ConfiguredFilter get(final String name)\n    {\n        return get(ConfiguredFilter.CONFIGURATION_ROOT, name, NAMESAKE_JSON);\n    }\n\n    private ConfiguredFilter get(final String root, final String name, final String resourceName)\n    {\n        final Configuration configuration = new StandardConfiguration(new InputStreamResource(\n                () -> ConfiguredFilterTest.class.getResourceAsStream(resourceName)));\n        return ConfiguredFilter.from(root, name, configuration);\n    }\n\n    private Atlas getAtlasFrom(final String name)\n    {\n        return getAtlasFrom(name, ConfiguredFilterTest.class);\n    }\n\n    private MultiPolygon getGeometryFrom(final String name)\n    {\n        return MultiPolygonTest.getFrom(name, ConfiguredFilterTest.class);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/configuration/MergedConfigurationTest.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport java.io.InputStream;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.StringInputStream;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\n\n/**\n * Test Cases for MergedConfiguration implementations\n *\n * @author brian_l_davis\n * @author jklamer\n * @author cameron_frenette\n */\npublic class MergedConfigurationTest\n{\n    private static final String BASE_CONFIGURATION_JSON = StandardConfigurationTest.JSON_CONFIGURATION;\n    private static final String BASE_CONFIGURATION_YAML = StandardConfigurationTest.YAML_CONFIGURATION;\n\n    private static final String KEYWORD_OVERRIDDEN_CONFIGURATION_JSON = StandardConfigurationTest.JSON_KEYWORD_OVERRIDDEN_CONFIGURATION;\n    private static final String KEYWORD_OVERRIDDEN_CONFIGURATION_YAML = StandardConfigurationTest.YAML_KEYWORD_OVERRIDDEN_CONFIGURATION;\n\n    private static final String KEYWORD_OVERRIDDEN_DEV_CONFIGURATION_JSON = \"developmentOverriding.json\";\n    private static final String KEYWORD_OVERRIDDEN_DEV_CONFIGURATION_YAML = \"developmentOverriding.yml\";\n\n    private static final String OVERRIDE_CONFIGURATION_JSON = \"development.json\";\n    private static final String OVERRIDE_CONFIGURATION_YAML = \"development.yml\";\n\n    private static final String PARTIAL_CONFIGURATION_JSON = \"feature.json\";\n    private static final String PARTIAL_CONFIGURATION_YAML = \"feature.yml\";\n\n    private static final String CONFIGURATION_FEATURE_RANGE = \"feature.%s.range\";\n\n    @Test\n    public void testConfigurationDataKeySetAllJson()\n    {\n        testConfigurationDataKeySet(getResourceInputStreamSupplier(BASE_CONFIGURATION_JSON),\n                getResourceInputStreamSupplier(OVERRIDE_CONFIGURATION_JSON));\n    }\n\n    @Test\n    public void testConfigurationDataKeySetAllYaml()\n    {\n        testConfigurationDataKeySet(getResourceInputStreamSupplier(BASE_CONFIGURATION_YAML),\n                getResourceInputStreamSupplier(OVERRIDE_CONFIGURATION_YAML));\n    }\n\n    @Test\n    public void testDotFormat()\n    {\n        final String expandedSource = \"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":0,\\\"d\\\":1}}}\";\n        final String expandedOverrideSource = \"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":2}}}\";\n        final String flattenedSource = \"{\\\"a.b.c\\\": 0, \\\"a.b.d\\\": 1}\";\n        final String flattenedOverrideSource = \"{\\\"a.b.c\\\": 2}\";\n\n        validateSame(expandedSource, expandedOverrideSource, flattenedSource,\n                flattenedOverrideSource);\n        validateSame(expandedSource, flattenedOverrideSource, flattenedSource,\n                expandedOverrideSource);\n    }\n\n    @Test\n    public void testLayeredJson()\n    {\n        testLayered(getResourceInputStreamSupplier(BASE_CONFIGURATION_JSON),\n                getResourceInputStreamSupplier(OVERRIDE_CONFIGURATION_JSON));\n    }\n\n    @Test\n    public void testLayeredJsonOnYaml()\n    {\n        testLayered(getResourceInputStreamSupplier(BASE_CONFIGURATION_YAML),\n                getResourceInputStreamSupplier(OVERRIDE_CONFIGURATION_JSON));\n    }\n\n    @Test\n    public void testLayeredYaml()\n    {\n        testLayered(getResourceInputStreamSupplier(BASE_CONFIGURATION_YAML),\n                getResourceInputStreamSupplier(OVERRIDE_CONFIGURATION_YAML));\n    }\n\n    @Test\n    public void testLayeredYamlOnJson()\n    {\n        testLayered(getResourceInputStreamSupplier(BASE_CONFIGURATION_JSON),\n                getResourceInputStreamSupplier(OVERRIDE_CONFIGURATION_YAML));\n    }\n\n    @Test\n    public void testMergedOverriddenConfigurationsJson()\n    {\n        testMergedOverriddenConfigurations(\n                getResourceInputStreamSupplier(KEYWORD_OVERRIDDEN_CONFIGURATION_JSON),\n                getResourceInputStreamSupplier(KEYWORD_OVERRIDDEN_DEV_CONFIGURATION_JSON));\n    }\n\n    @Test\n    public void testMergedOverriddenConfigurationsYaml()\n    {\n        testMergedOverriddenConfigurations(\n                getResourceInputStreamSupplier(KEYWORD_OVERRIDDEN_CONFIGURATION_YAML),\n                getResourceInputStreamSupplier(KEYWORD_OVERRIDDEN_DEV_CONFIGURATION_YAML));\n    }\n\n    @Test\n    public void testMergedOverriddenConfigurationsYamlOnJson()\n    {\n        testMergedOverriddenConfigurations(\n                getResourceInputStreamSupplier(KEYWORD_OVERRIDDEN_CONFIGURATION_JSON),\n                getResourceInputStreamSupplier(KEYWORD_OVERRIDDEN_DEV_CONFIGURATION_YAML));\n    }\n\n    @Test\n    public void testPartialJson()\n    {\n        testPartial(getResourceInputStreamSupplier(BASE_CONFIGURATION_JSON),\n                getResourceInputStreamSupplier(PARTIAL_CONFIGURATION_JSON));\n    }\n\n    @Test\n    public void testPartialYaml()\n    {\n        testPartial(getResourceInputStreamSupplier(BASE_CONFIGURATION_YAML),\n                getResourceInputStreamSupplier(PARTIAL_CONFIGURATION_YAML));\n    }\n\n    @Test\n    public void testPartialYamlOnJson()\n    {\n        testPartial(getResourceInputStreamSupplier(BASE_CONFIGURATION_JSON),\n                getResourceInputStreamSupplier(PARTIAL_CONFIGURATION_YAML));\n    }\n\n    @Test\n    public void testSubConfiguration()\n    {\n        final Configuration configurationA = new StandardConfiguration(\n                new StringResource(\"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":\\\"d\\\"}}}\"));\n        final Configuration configurationB = new StandardConfiguration(\n                new StringResource(\"{\\\"a\\\":{\\\"e\\\":{\\\"f\\\":\\\"g\\\"}}}\"));\n        final Configuration configuration = new MergedConfiguration(configurationA, configurationB);\n        final Configuration subConfiguration = configuration.subConfiguration(\"a\").get();\n        Assert.assertTrue(subConfiguration.get(\"b\").value() instanceof Map);\n        Assert.assertEquals(\"{c=d}\", ((Map) subConfiguration.get(\"b\").value()).toString());\n        Assert.assertEquals(\"d\", (String) subConfiguration.get(\"b.c\").value());\n        // Merged configuration will take the first only for key \"a\", ignoring others.\n        Assert.assertNull(subConfiguration.get(\"e\").value());\n    }\n\n    /**\n     * Create a Supplier to return an input stream of a named resource.\n     *\n     * @param name\n     *            the name of the resource local to the class.\n     * @return a Supplier that will get an InputStream of the resource.\n     */\n    private Supplier<InputStream> getResourceInputStreamSupplier(final String name)\n    {\n        return () -> MergedConfigurationTest.class.getResourceAsStream(name);\n    }\n\n    /**\n     * Create a Supplier to return an Input Stream of a string\n     *\n     * @param value\n     *            the string value\n     * @return a Supplier that will return an InputStream of the string\n     */\n    private Supplier<InputStream> getStringInputStreamsSupplier(final String value)\n    {\n        return () -> new StringInputStream(value);\n    }\n\n    private void testConfigurationDataKeySet(final Supplier<InputStream> base,\n            final Supplier<InputStream> override)\n    {\n\n        final Configuration configuration = new MergedConfiguration(new InputStreamResource(base),\n                new InputStreamResource(override));\n\n        final Set<String> compareTo = new HashSet<>();\n        compareTo.add(\"feature\");\n        Assert.assertEquals(configuration.configurationDataKeySet(), compareTo);\n\n    }\n\n    private void testLayered(final Supplier<InputStream> base, final Supplier<InputStream> override)\n    {\n\n        final Configuration configuration = new MergedConfiguration(new InputStreamResource(base),\n                new InputStreamResource(override));\n\n        final String keyword = \"ABC\";\n        final String key = String.format(CONFIGURATION_FEATURE_RANGE, keyword);\n\n        final Optional<Map<String, Double>> rangeOption = configuration.get(key).valueOption();\n\n        Assert.assertTrue(rangeOption.isPresent());\n        rangeOption.ifPresent(range ->\n        {\n            Assert.assertEquals(5.0, range.get(\"min\"), 0.1);\n            Assert.assertEquals(30.0, range.get(\"max\"), 0.1);\n        });\n\n        final String baseKeyword = \"ABC\";\n        final String baseKey = String.format(CONFIGURATION_FEATURE_RANGE, baseKeyword);\n        final Double max = configuration.get(baseKey + \".min\", Double.NaN).value();\n        Assert.assertNotEquals(Double.NaN, max);\n\n        Assert.assertFalse(configuration.get(\"foo\").valueOption().isPresent());\n    }\n\n    private void testMergedOverriddenConfigurations(\n            final Supplier<InputStream> keywordOverriddenBaseConfiguration,\n            final Supplier<InputStream> developmentConfiguration)\n    {\n\n        final Configuration configuration = new MergedConfiguration(\n                new InputStreamResource(keywordOverriddenBaseConfiguration),\n                new InputStreamResource(developmentConfiguration));\n\n        final String keyword1 = \"ABC\";\n        final String keyword2 = \"XYZ\";\n        final String keyword3 = \"ZZZ\";\n        final Configuration configuration1 = configuration.configurationForKeyword(keyword1);\n        final Configuration configuration2 = configuration.configurationForKeyword(keyword2);\n\n        final String minKey = \"feature.range.min\";\n        final String maxKey = \"feature.range.max\";\n\n        // Assert that configuration not changed for not overriding keywords.\n        Assert.assertEquals(configuration, configuration.configurationForKeyword(keyword3));\n        // value derived from default of higher layer development configuration instead of\n        // keyword specific view of base configuration\n        Assert.assertEquals(Angle.degrees(35.0),\n                configuration2.get(maxKey, Angle::degrees).value());\n        // value derived from keyword specific view of development configuration\n        Assert.assertEquals(Angle.degrees(70.0),\n                configuration1.get(maxKey, Angle::degrees).value());\n        // value derived from default of base configuration\n        Assert.assertEquals(Angle.degrees(10.0),\n                configuration1.get(minKey, Angle::degrees).value());\n        Assert.assertEquals(Angle.degrees(10.0),\n                configuration2.get(minKey, Angle::degrees).value());\n\n    }\n\n    private void testPartial(final Supplier<InputStream> base, final Supplier<InputStream> partial)\n    {\n        final Configuration configuration = new MergedConfiguration(new InputStreamResource(base),\n                new InputStreamResource(partial));\n\n        final String keyword = \"XYZ\";\n        final String key = String.format(CONFIGURATION_FEATURE_RANGE, keyword);\n\n        final Optional<Map<String, Double>> rangeOption = configuration.get(key).valueOption();\n\n        Assert.assertTrue(rangeOption.isPresent());\n        rangeOption.ifPresent(range ->\n        {\n            Assert.assertEquals(20.0, range.get(\"min\"), 0.1);\n            Assert.assertEquals(40.0, range.get(\"max\"), 0.1);\n        });\n        Assert.assertFalse(configuration.get(\"foo\").valueOption().isPresent());\n\n    }\n\n    private void validateSame(final String leftBase, final String leftOverride,\n            final String rightBase, final String rightOverride)\n    {\n        final Configuration left = new MergedConfiguration(\n                new InputStreamResource(getStringInputStreamsSupplier(leftBase)),\n                new InputStreamResource(getStringInputStreamsSupplier(leftOverride)));\n\n        final Configuration right = new MergedConfiguration(\n                new InputStreamResource(getStringInputStreamsSupplier(rightBase)),\n                new InputStreamResource(getStringInputStreamsSupplier(rightOverride)));\n\n        Assert.assertEquals(2L, (long) left.get(StandardConfigurationTest.ABC).value());\n        Assert.assertEquals(1L, (long) left.get(StandardConfigurationTest.ABD).value());\n        Assert.assertEquals((long) left.get(StandardConfigurationTest.ABC).value(),\n                (long) right.get(StandardConfigurationTest.ABC).value());\n        Assert.assertEquals((long) left.get(StandardConfigurationTest.ABD).value(),\n                (long) right.get(StandardConfigurationTest.ABD).value());\n\n        final Map<String, Object> leftMap = left.get(\"a.b\").value();\n        final Map<String, Object> rightMap = right.get(\"a.b\").value();\n\n        Assert.assertEquals(2L, leftMap.get(\"c\"));\n        Assert.assertEquals(2L, rightMap.get(\"c\"));\n        Assert.assertEquals(1L, leftMap.get(\"d\"));\n        Assert.assertEquals(1L, rightMap.get(\"d\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/configuration/StandardConfigurationTest.java",
    "content": "package org.openstreetmap.atlas.utilities.configuration;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.scalars.Angle;\n\n/**\n * Test case for the StandardConfiguration implementation\n *\n * @author cstaylor\n * @author brian_l_davis\n * @author jklamer\n * @author cameron_frenette\n */\npublic class StandardConfigurationTest\n{\n    public static final String JSON_CONFIGURATION = \"application.json\";\n    public static final String YAML_CONFIGURATION = \"application.yml\";\n\n    public static final String YAML_DOT_EXPANDED = \"yaml_dot_expanded.yml\";\n    public static final String YAML_DOT_COMPRESSED = \"yaml_dot_compressed.yml\";\n\n    public static final String JSON_KEYWORD_OVERRIDDEN_CONFIGURATION = \"keywordOverridingApplication.json\";\n    public static final String YAML_KEYWORD_OVERRIDDEN_CONFIGURATION = \"keywordOverridingApplication.yml\";\n\n    protected static final String ABC = \"a.b.c\";\n    protected static final String ABD = \"a.b.d\";\n\n    @Test\n    public void testConfigurationDataKeySetJson() throws IOException\n    {\n        try (InputStream stream = StandardConfigurationTest.class\n                .getResourceAsStream(JSON_CONFIGURATION))\n        {\n            testConfigurationDataKeySet(stream);\n        }\n    }\n\n    @Test\n    public void testConfigurationDataKeySetYaml() throws IOException\n    {\n        try (InputStream stream = StandardConfigurationTest.class\n                .getResourceAsStream(YAML_CONFIGURATION))\n        {\n            testConfigurationDataKeySet(stream);\n        }\n    }\n\n    @Test\n    public void testDefaults() throws IOException\n    {\n        try (InputStream stream = new ByteArrayInputStream(\"{}\".getBytes(StandardCharsets.UTF_8)))\n        {\n            final StandardConfiguration configuration = new StandardConfiguration(\n                    new InputStreamResource(() -> stream));\n            final String minKey = \"missing.range.min\";\n            final String maxKey = \"missing.range.max\";\n\n            Assert.assertEquals(Double.valueOf(1.0), configuration.get(minKey, 1.0).value());\n            Assert.assertEquals(Double.valueOf(100.0), configuration.get(maxKey, 100.0).value());\n            Assert.assertEquals(Angle.degrees(1.0),\n                    configuration.get(minKey, 1.0, Angle::degrees).value());\n            Assert.assertEquals(Angle.degrees(100.0),\n                    configuration.get(maxKey, 100.0, Angle::degrees).value());\n        }\n    }\n\n    @Test\n    public void testDotFormat() throws IOException\n    {\n        try (InputStream expandedSource = new ByteArrayInputStream(\n                \"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":0,\\\"d\\\":1}}}\".getBytes(StandardCharsets.UTF_8));\n                InputStream flattenedSource = new ByteArrayInputStream(\n                        \"{\\\"a.b.c\\\": 0, \\\"a.b.d\\\": 1}\".getBytes(StandardCharsets.UTF_8)))\n        {\n            final StandardConfiguration expanded = new StandardConfiguration(\n                    new InputStreamResource(() -> expandedSource));\n            final StandardConfiguration flattened = new StandardConfiguration(\n                    new InputStreamResource(() -> flattenedSource));\n\n            Assert.assertEquals((long) expanded.get(ABC).value(),\n                    (long) flattened.get(ABC).value());\n            Assert.assertEquals((long) expanded.get(ABD).value(),\n                    (long) flattened.get(ABD).value());\n        }\n    }\n\n    @Test\n    public void testDotFormatYaml() throws IOException\n    {\n        try (InputStream expandedSource = StandardConfigurationTest.class\n                .getResourceAsStream(YAML_DOT_EXPANDED);\n                InputStream flattenedSource = StandardConfigurationTest.class\n                        .getResourceAsStream(YAML_DOT_COMPRESSED))\n        {\n            final StandardConfiguration expanded = new StandardConfiguration(\n                    new InputStreamResource(() -> expandedSource));\n            final StandardConfiguration flattened = new StandardConfiguration(\n                    new InputStreamResource(() -> flattenedSource));\n\n            Assert.assertEquals(0, (long) expanded.get(ABC).value());\n            Assert.assertEquals(1, (long) expanded.get(ABD).value());\n            Assert.assertEquals((long) expanded.get(ABC).value(),\n                    (long) flattened.get(ABC).value());\n            Assert.assertEquals((long) expanded.get(ABD).value(),\n                    (long) flattened.get(ABD).value());\n        }\n    }\n\n    @Test\n    public void testInFileOverrideJson() throws IOException\n    {\n        try (InputStream stream = StandardConfigurationTest.class\n                .getResourceAsStream(JSON_KEYWORD_OVERRIDDEN_CONFIGURATION))\n        {\n            testInFileOverride(stream);\n        }\n    }\n\n    @Test\n    public void testInFileOverrideYaml() throws IOException\n    {\n        try (InputStream stream = StandardConfigurationTest.class\n                .getResourceAsStream(YAML_KEYWORD_OVERRIDDEN_CONFIGURATION))\n        {\n            testInFileOverride(stream);\n        }\n    }\n\n    @Test\n    public void testMissing() throws IOException\n    {\n        try (InputStream stream = new ByteArrayInputStream(\"{}\".getBytes(StandardCharsets.UTF_8)))\n        {\n            final StandardConfiguration configuration = new StandardConfiguration(\n                    new InputStreamResource(() -> stream));\n            final String minKey = \"missing.range.min\";\n            final String maxKey = \"missing.range.max\";\n\n            Assert.assertNull(configuration.get(minKey).value());\n            Assert.assertNull(configuration.get(maxKey).value());\n            Assert.assertNull(configuration.get(minKey, Angle::degrees).value());\n            Assert.assertNull(configuration.get(maxKey, Angle::degrees).value());\n        }\n    }\n\n    @Test\n    public void testSpecificFormatLoad() throws IOException\n    {\n        try (InputStream expandedSource1 = StandardConfigurationTest.class\n                .getResourceAsStream(YAML_DOT_EXPANDED);\n                InputStream flattenedSource1 = StandardConfigurationTest.class\n                        .getResourceAsStream(YAML_DOT_COMPRESSED);\n                InputStream expandedSource2 = StandardConfigurationTest.class\n                        .getResourceAsStream(YAML_DOT_EXPANDED);\n                InputStream flattenedSource2 = StandardConfigurationTest.class\n                        .getResourceAsStream(YAML_DOT_COMPRESSED))\n        {\n            final StandardConfiguration expanded1 = new StandardConfiguration(\n                    new InputStreamResource(() -> expandedSource1));\n            final StandardConfiguration flattened1 = new StandardConfiguration(\n                    new InputStreamResource(() -> flattenedSource1));\n\n            final StandardConfiguration expanded2 = new StandardConfiguration(\n                    new InputStreamResource(() -> expandedSource2),\n                    StandardConfiguration.ConfigurationFormat.YAML);\n            final StandardConfiguration flattened2 = new StandardConfiguration(\n                    new InputStreamResource(() -> flattenedSource2),\n                    StandardConfiguration.ConfigurationFormat.YAML);\n\n            Assert.assertEquals(0, (long) expanded1.get(ABC).value());\n            Assert.assertEquals(1, (long) expanded1.get(ABD).value());\n            Assert.assertEquals((long) expanded1.get(ABC).value(),\n                    (long) flattened1.get(ABC).value());\n            Assert.assertEquals((long) expanded1.get(ABD).value(),\n                    (long) flattened1.get(ABD).value());\n\n            Assert.assertEquals(0, (long) expanded2.get(ABC).value());\n            Assert.assertEquals(1, (long) expanded2.get(ABD).value());\n            Assert.assertEquals((long) expanded2.get(ABC).value(),\n                    (long) flattened2.get(ABC).value());\n            Assert.assertEquals((long) expanded2.get(ABD).value(),\n                    (long) flattened2.get(ABD).value());\n        }\n    }\n\n    @Test\n    public void testStandardConfigurationJson() throws IOException\n    {\n        try (InputStream stream = StandardConfigurationTest.class\n                .getResourceAsStream(JSON_CONFIGURATION))\n        {\n            testStandardConfiguration(stream);\n        }\n    }\n\n    @Test\n    public void testStandardConfigurationYaml() throws IOException\n    {\n        try (InputStream stream = StandardConfigurationTest.class\n                .getResourceAsStream(YAML_CONFIGURATION))\n        {\n            testStandardConfiguration(stream);\n        }\n    }\n\n    @Test\n    public void testSubConfiguration()\n    {\n        final Configuration configuration = new StandardConfiguration(\n                new StringResource(\"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":\\\"d\\\"},\\\"e\\\":{\\\"f\\\":\\\"g\\\"}}}\"));\n        final Configuration subConfiguration = configuration.subConfiguration(\"a\").get();\n        Assert.assertTrue(subConfiguration.get(\"b\").value() instanceof Map);\n        Assert.assertEquals(\"{c=d}\", ((Map) subConfiguration.get(\"b\").value()).toString());\n        Assert.assertEquals(\"d\", (String) subConfiguration.get(\"b.c\").value());\n        Assert.assertTrue(subConfiguration.get(\"e\").value() instanceof Map);\n        Assert.assertEquals(\"{f=g}\", ((Map) subConfiguration.get(\"e\").value()).toString());\n        Assert.assertEquals(\"g\", (String) subConfiguration.get(\"e.f\").value());\n        Assert.assertTrue(configuration.subConfiguration(\"z\").isEmpty());\n    }\n\n    @Test\n    public void testTransformationJson() throws IOException\n    {\n        try (InputStream stream = StandardConfigurationTest.class\n                .getResourceAsStream(JSON_CONFIGURATION))\n        {\n            testTransformation(stream);\n        }\n    }\n\n    @Test\n    public void testTransformationYaml() throws IOException\n    {\n        try (InputStream stream = StandardConfigurationTest.class\n                .getResourceAsStream(YAML_CONFIGURATION))\n        {\n            testTransformation(stream);\n        }\n    }\n\n    private void testConfigurationDataKeySet(final InputStream stream)\n    {\n        final StandardConfiguration configuration = new StandardConfiguration(\n                new InputStreamResource(() -> stream));\n\n        final Set<String> compareTo = new HashSet<>();\n        compareTo.add(\"feature\");\n        Assert.assertEquals(configuration.configurationDataKeySet(), compareTo);\n    }\n\n    private void testInFileOverride(final InputStream stream)\n    {\n        final String keyword1 = \"ABC\";\n        final String keyword2 = \"XYZ\";\n        final String notAnOverRiddenKeyword = \"ZZZ\";\n        final StandardConfiguration configuration = new StandardConfiguration(\n                new InputStreamResource(() -> stream));\n        final Configuration overriddenConfiguration1 = configuration\n                .configurationForKeyword(keyword1);\n        final Configuration overriddenConfiguration2 = configuration\n                .configurationForKeyword(keyword2);\n        final Configuration sameConfiguration = configuration\n                .configurationForKeyword(notAnOverRiddenKeyword);\n        final String overriddenKey = \"feature.range.max\";\n        final String notOverriddenKey = \"feature.range.min\";\n        final String overriddenListKey = \"feature.list\";\n\n        // assert configuration is same for non-overridden keyword\n        Assert.assertEquals(configuration, sameConfiguration);\n        // assert override works for objects\n        Assert.assertEquals(Arrays.asList(\"nodes\"),\n                overriddenConfiguration1.get(overriddenListKey).value());\n        // assert different values for same key in different configurations based on keyword\n        Assert.assertEquals(Angle.degrees(20.0),\n                overriddenConfiguration1.get(overriddenKey, Angle::degrees).value());\n        Assert.assertEquals(Angle.degrees(30.0),\n                overriddenConfiguration2.get(overriddenKey, Angle::degrees).value());\n        // assert same value for same key in different configurations based on keyword\n        Assert.assertEquals(Angle.degrees(10.0),\n                overriddenConfiguration1.get(notOverriddenKey, Angle::degrees).value());\n        Assert.assertEquals(Angle.degrees(10.0),\n                overriddenConfiguration2.get(notOverriddenKey, Angle::degrees).value());\n    }\n\n    private void testStandardConfiguration(final InputStream stream)\n    {\n        final StandardConfiguration configuration = new StandardConfiguration(\n                new InputStreamResource(() -> stream));\n        final String keyword = \"ABC\";\n        final String key = String.format(\"feature.%s.range\", keyword);\n\n        final Optional<Map<String, Double>> range = configuration.get(key).valueOption();\n\n        Assert.assertTrue(range.isPresent());\n        range.ifPresent(enforcements ->\n        {\n            Assert.assertEquals(10.0, enforcements.get(\"min\"), 0.1);\n            Assert.assertEquals(20.0, enforcements.get(\"max\"), 0.1);\n        });\n        Assert.assertFalse(configuration.get(\"foo\").valueOption().isPresent());\n    }\n\n    private void testTransformation(final InputStream stream)\n    {\n        final StandardConfiguration configuration = new StandardConfiguration(\n                new InputStreamResource(() -> stream));\n        final String keyword = \"ABC\";\n        final String minKey = String.format(\"feature.%s.range.min\", keyword);\n        final String maxKey = String.format(\"feature.%s.range.max\", keyword);\n\n        Assert.assertEquals(Angle.degrees(10.0), configuration.get(minKey, Angle::degrees).value());\n        Assert.assertEquals(Angle.degrees(20.0), configuration.get(maxKey, Angle::degrees).value());\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/conversion/HexStringByteArrayConverterTest.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class HexStringByteArrayConverterTest\n{\n    private static final HexStringByteArrayConverter HEX_STRING_BYTE_ARRAY_CONVERTER = new HexStringByteArrayConverter();\n\n    @Test\n    public void testConversion()\n    {\n        final String hex = \"0103000000020000000600000036A094FF7FBA\";\n        final byte[] bytes2 = HEX_STRING_BYTE_ARRAY_CONVERTER.convert(hex);\n        final String hex2 = HEX_STRING_BYTE_ARRAY_CONVERTER.backwardConvert(bytes2);\n        Assert.assertEquals(hex, hex2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/conversion/StringToPredicateConverterTest.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.codehaus.groovy.control.MultipleCompilationErrorsException;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author lcram\n */\npublic class StringToPredicateConverterTest\n{\n    private static final Logger logger = LoggerFactory\n            .getLogger(StringToPredicateConverterTest.class);\n\n    @Rule\n    public StringToPredicateConverterTestRule rule = new StringToPredicateConverterTestRule();\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void checkSecurityForConvert()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Unable to parse\");\n        new StringToPredicateConverter<Integer>().convert(\n                \"e.intValue() == org.openstreetmap.atlas.utilities.random.RandomTagsSupplier.randomTags(5).size()\");\n    }\n\n    @Test\n    public void checkSecurityForConvertUnsafe()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Unable to parse\");\n        new StringToPredicateConverter<Integer>().convertUnsafe(\n                \"e.intValue() == org.openstreetmap.atlas.utilities.random.RandomTagsSupplier.randomTags(5).size()\");\n    }\n\n    @Test\n    public void testComplexExpression()\n    {\n        /*\n         * This complex expression will always return true.\n         */\n        final String complexExpression = \"Boolean val = false; if (!val) { val = true; }; val\";\n        final Predicate<String> predicate = new StringToPredicateConverter<String>()\n                .convert(complexExpression);\n        Assert.assertTrue(predicate.test(\"ignoredValue\"));\n    }\n\n    @Test\n    public void testComplexExpressionFail()\n    {\n        /*\n         * This complex expression will always return true. However, it cannot be converted using\n         * the unsafe version of the method. Complex expressions are only supported by the regular\n         * converter.\n         */\n        final String complexExpression = \"Boolean val = false; if (!val) { val = true; }; val\";\n        this.expectedException.expect(MultipleCompilationErrorsException.class);\n        new StringToPredicateConverter<String>().convertUnsafe(complexExpression);\n    }\n\n    @Test\n    public void testComplexExpressionFail2()\n    {\n        /*\n         * Another complex expression which should fail.\n         */\n        final String complexExpression = \"e.equals(\\\"foo\\\"); e.equals(\\\"foo\\\")\";\n        this.expectedException.expect(MultipleCompilationErrorsException.class);\n        new StringToPredicateConverter<String>().convertUnsafe(complexExpression);\n    }\n\n    @Test\n    public void testConvert()\n    {\n        final Predicate<String> predicate1 = new StringToPredicateConverter<String>()\n                .convert(\"e.equals(\\\"foo\\\")\");\n        final Predicate<Map<String, String>> predicate2 = new StringToPredicateConverter<Map<String, String>>()\n                .convert(\"e.containsKey(\\\"foo\\\")\");\n        final Predicate<Integer> predicate3 = new StringToPredicateConverter<Integer>()\n                .convert(\"e == 3\");\n        final Predicate<AtlasEntity> predicate4 = new StringToPredicateConverter<AtlasEntity>()\n                .convert(\"e.getTags().containsKey(\\\"mat\\\")\");\n        final Predicate<Integer> predicate5 = new StringToPredicateConverter<Integer>()\n                .withAddedStarImportPackages(\"org.openstreetmap.atlas.utilities.random\")\n                .convert(\"e.intValue() == RandomTagsSupplier.randomTags(5).size()\");\n        final Predicate<Integer> predicate6 = new StringToPredicateConverter<Integer>()\n                .withAddedStarImportPackages(\n                        Collections.singletonList(\"org.openstreetmap.atlas.utilities.random\"))\n                .convert(\"e.intValue() == RandomTagsSupplier.randomTags(5).size()\");\n\n        Assert.assertTrue(predicate1.test(\"foo\"));\n        Assert.assertFalse(predicate1.test(\"bar\"));\n\n        Assert.assertTrue(predicate2.test(Maps.hashMap(\"foo\", \"bar\", \"baz\", \"bat\")));\n        Assert.assertFalse(predicate2.test(Maps.hashMap(\"baz\", \"bat\", \"bar\", \"mat\")));\n\n        Assert.assertTrue(predicate3.test(3));\n        Assert.assertFalse(predicate3.test(10));\n\n        Assert.assertTrue(predicate4.test(this.rule.getAtlas().point(3)));\n        Assert.assertFalse(predicate4.test(this.rule.getAtlas().point(1)));\n\n        Assert.assertTrue(predicate5.test(5));\n        Assert.assertTrue(predicate6.test(5));\n    }\n\n    @Test\n    public void testConvertUnsafe()\n    {\n        final Predicate<String> predicate1 = new StringToPredicateConverter<String>()\n                .convertUnsafe(\"e.equals(\\\"foo\\\")\");\n        final Predicate<Map<String, String>> predicate2 = new StringToPredicateConverter<Map<String, String>>()\n                .convertUnsafe(\"e.containsKey(\\\"foo\\\")\");\n        final Predicate<Integer> predicate3 = new StringToPredicateConverter<Integer>()\n                .convertUnsafe(\"e == 3\");\n        final Predicate<AtlasEntity> predicate4 = new StringToPredicateConverter<AtlasEntity>()\n                .convertUnsafe(\"e.getTags().containsKey(\\\"mat\\\")\");\n        final Predicate<Integer> predicate5 = new StringToPredicateConverter<Integer>()\n                .withAddedStarImportPackages(\"org.openstreetmap.atlas.utilities.random\")\n                .convertUnsafe(\"e.intValue() == RandomTagsSupplier.randomTags(5).size()\");\n        final Predicate<Integer> predicate6 = new StringToPredicateConverter<Integer>()\n                .withAddedStarImportPackages(\n                        Collections.singletonList(\"org.openstreetmap.atlas.utilities.random\"))\n                .convertUnsafe(\"e.intValue() == RandomTagsSupplier.randomTags(5).size()\");\n\n        Assert.assertTrue(predicate1.test(\"foo\"));\n        Assert.assertFalse(predicate1.test(\"bar\"));\n\n        Assert.assertTrue(predicate2.test(Maps.hashMap(\"foo\", \"bar\", \"baz\", \"bat\")));\n        Assert.assertFalse(predicate2.test(Maps.hashMap(\"baz\", \"bat\", \"bar\", \"mat\")));\n\n        Assert.assertTrue(predicate3.test(3));\n        Assert.assertFalse(predicate3.test(10));\n\n        Assert.assertTrue(predicate4.test(this.rule.getAtlas().point(3)));\n        Assert.assertFalse(predicate4.test(this.rule.getAtlas().point(1)));\n\n        Assert.assertTrue(predicate5.test(5));\n        Assert.assertTrue(predicate6.test(5));\n    }\n\n    @Test\n    public void testImportInjectionProtection1()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Invalid import\");\n        new StringToPredicateConverter<Integer>()\n                .withAddedStarImportPackages(Collections.singletonList(\n                        \"org.openstreetmap.atlas.utilities.random.*;System.out.println(\\\"INJECTED\\\");import org.openstreetmap.atlas\"))\n                .convertUnsafe(\"e.intValue() == RandomTagsSupplier.randomTags(5).size()\");\n    }\n\n    @Test\n    public void testImportInjectionProtection2()\n    {\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Invalid import\");\n        new StringToPredicateConverter<Integer>()\n                .withAddedStarImportPackages(Collections.singletonList(\"System.out.println()\"))\n                .convertUnsafe(\"e.intValue() == RandomTagsSupplier.randomTags(5).size()\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/conversion/StringToPredicateConverterTestRule.java",
    "content": "package org.openstreetmap.atlas.utilities.conversion;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\n\n/**\n * @author lcram\n */\npublic class StringToPredicateConverterTestRule extends CoreTestRule\n{\n    private static final String ONE = \"15.420563,-61.336198\";\n    private static final String TWO = \"15.429499,-61.332850\";\n    private static final String THREE = \"15.4855,-61.3041\";\n\n    @TestAtlas(points = {\n\n            @Point(id = \"1\", coordinates = @Loc(value = ONE), tags = { \"foo=bar\" }),\n            @Point(id = \"2\", coordinates = @Loc(value = TWO), tags = { \"baz=bat\" }),\n            @Point(id = \"3\", coordinates = @Loc(value = THREE), tags = { \"foo=bar\", \"baz=bat\",\n                    \"mat=fat\" })\n\n    })\n    private Atlas atlas1;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas1;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/direction/EdgeDirectionComparatorTest.java",
    "content": "package org.openstreetmap.atlas.utilities.direction;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.items.Edge;\n\n/**\n * @author Sid\n */\npublic class EdgeDirectionComparatorTest\n{\n    @Rule\n    public EdgeDirectionComparatorTestCaseRule setup = new EdgeDirectionComparatorTestCaseRule();\n\n    @Test\n    public void testOppositeDirection()\n    {\n        final EdgeDirectionComparator edgeDirectionComparator = new EdgeDirectionComparator();\n        final Map<Integer, Integer> oppositeTurnMap = new HashMap<>();\n        oppositeTurnMap.put(1314, 1415);\n        oppositeTurnMap.put(56, 78);\n        oppositeTurnMap.put(56, 65);\n        oppositeTurnMap.forEach((from, too) ->\n        {\n            final String entry = from + \" -> \" + too;\n            final Edge fromOppositeTurn = this.setup.getAtlas().edge(from);\n            final Edge toOppositeTurn = this.setup.getAtlas().edge(too);\n            Assert.assertTrue(\"Should be a U Turn : \" + entry,\n                    edgeDirectionComparator.isOppositeDirection(fromOppositeTurn.asPolyLine(),\n                            toOppositeTurn.asPolyLine(), false));\n            Assert.assertFalse(\"Should be only U Turn : \" + entry, edgeDirectionComparator\n                    .isSameDirection(fromOppositeTurn, toOppositeTurn, false));\n        });\n    }\n\n    @Test\n    public void testSameDirection()\n    {\n        final EdgeDirectionComparator edgeDirectionComparator = new EdgeDirectionComparator();\n        final Map<Integer, Integer> sameDirectionMap = new HashMap<>();\n        sameDirectionMap.put(12, 12);\n        sameDirectionMap.put(1617, 1718);\n        sameDirectionMap.forEach((from, too) ->\n        {\n            final String entry = from + \" -> \" + too;\n            final Edge fromSameDirection = this.setup.getAtlas().edge(from);\n            final Edge toSameDirection = this.setup.getAtlas().edge(too);\n            Assert.assertTrue(\"Should be Same Direction : \" + entry, edgeDirectionComparator\n                    .isSameDirection(fromSameDirection, toSameDirection, false));\n            Assert.assertFalse(\"Should be only Same Direction : \" + entry,\n                    edgeDirectionComparator.isOppositeDirection(fromSameDirection.asPolyLine(),\n                            toSameDirection.asPolyLine(), false));\n        });\n\n        // Segment Vs Overall Heading\n        final Map<Integer, Integer> sameDirectionEdgeCasesMap = new HashMap<>();\n        sameDirectionEdgeCasesMap.put(1920, 202122);\n        sameDirectionEdgeCasesMap.forEach((from, too) ->\n        {\n            final String entry = from + \" -> \" + too;\n            final Edge fromSameDirection = this.setup.getAtlas().edge(from);\n            final Edge toSameDirection = this.setup.getAtlas().edge(too);\n            Assert.assertTrue(\"Should be Same Direction if we use segment Heading: \" + entry,\n                    edgeDirectionComparator.isSameDirection(fromSameDirection, toSameDirection,\n                            false));\n            Assert.assertFalse(\"Should NOT be in Same Direction if we use overallHeading: \" + entry,\n                    edgeDirectionComparator.isSameDirection(fromSameDirection, toSameDirection,\n                            true));\n            Assert.assertFalse(\"Should be only Same Direction : \" + entry,\n                    edgeDirectionComparator.isOppositeDirection(fromSameDirection.asPolyLine(),\n                            toSameDirection.asPolyLine(), false));\n        });\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/direction/EdgeDirectionComparatorTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.utilities.direction;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\n\n/**\n * @author Sid\n */\npublic class EdgeDirectionComparatorTestCaseRule extends CoreTestRule\n{\n    private static final String ONE = \"-34.870399,-56.1752752\";\n    private static final String TWO = \"-34.870359, -56.174562\";\n    private static final String THREE = \"-34.8704619,-56.1747235\";\n    private static final String FOUR = \"-34.869687, -56.174080\";\n    private static final String FIVE = \"-31.7180368,-55.9959477\";\n    private static final String SIX = \"-31.718552, -55.996589\";\n    private static final String SEVEN = \"-31.718639, -55.996470\";\n    private static final String EIGHT = \"-31.7181469,-55.9958428\";\n    private static final String NINE = \"19.0546006,-70.446448\";\n    private static final String TEN = \"19.0495406,-70.446176\";\n    private static final String ELEVEN = \"19.0502,-70.4514893\";\n    private static final String TWELVE = \"19.049056, -70.445427\";\n    private static final String THIRTEEN = \"-34.7761251,-55.3534846\";\n    private static final String FOURTEEN = \"-34.774595,-55.3565624\";\n    private static final String FIFTEEN = \"-34.7756381,-55.3548773\";\n\n    /*\n     * Same Direction\n     */\n    private static final String SIXTEEN = \"-34.897118, -56.157317\";\n    private static final String SEVENTEEN = \"-34.897149, -56.157169\";\n    private static final String EIGHTEEN = \"-34.897165, -56.157087\";\n\n    /*\n     * Same Direction - Segment Heading vs OverallHeading\n     */\n    private static final String NINETEEN = \"18.4220149,-70.7864727\";\n    private static final String TWENTY = \"18.4220965,-70.7869184\";\n    private static final String TWENTYONE = \"18.4221578,-70.7872535\";\n    private static final String TWENTYTWO = \"18.42245,-70.78779\";\n\n    @TestAtlas(\n\n            nodes = { @Node(id = \"1\", coordinates = @Loc(value = ONE)),\n                    @Node(id = \"2\", coordinates = @Loc(value = TWO)),\n                    @Node(id = \"3\", coordinates = @Loc(value = THREE)),\n                    @Node(id = \"4\", coordinates = @Loc(value = FOUR)),\n                    @Node(id = \"5\", coordinates = @Loc(value = FIVE)),\n                    @Node(id = \"6\", coordinates = @Loc(value = SIX)),\n                    @Node(id = \"7\", coordinates = @Loc(value = SEVEN)),\n                    @Node(id = \"8\", coordinates = @Loc(value = EIGHT)),\n                    @Node(id = \"9\", coordinates = @Loc(value = NINE)),\n                    @Node(id = \"10\", coordinates = @Loc(value = TEN)),\n                    @Node(id = \"11\", coordinates = @Loc(value = ELEVEN)),\n                    @Node(id = \"12\", coordinates = @Loc(value = TWELVE)),\n                    @Node(id = \"13\", coordinates = @Loc(value = THIRTEEN)),\n                    @Node(id = \"14\", coordinates = @Loc(value = FOURTEEN)),\n                    @Node(id = \"15\", coordinates = @Loc(value = FIFTEEN)),\n                    @Node(id = \"16\", coordinates = @Loc(value = SIXTEEN)),\n                    @Node(id = \"17\", coordinates = @Loc(value = SEVENTEEN)),\n                    @Node(id = \"18\", coordinates = @Loc(value = EIGHTEEN)),\n                    @Node(id = \"19\", coordinates = @Loc(value = NINETEEN)),\n                    @Node(id = \"20\", coordinates = @Loc(value = TWENTY)),\n                    @Node(id = \"21\", coordinates = @Loc(value = TWENTYONE)),\n                    @Node(id = \"22\", coordinates = @Loc(value = TWENTYTWO)), },\n\n            edges = {\n                    @Edge(id = \"12\", coordinates = { @Loc(value = ONE),\n                            @Loc(value = TWO) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"23\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = THREE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"24\", coordinates = { @Loc(value = TWO),\n                            @Loc(value = FOUR) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"56\", coordinates = { @Loc(value = FIVE),\n                            @Loc(value = SIX) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"67\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = SEVEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"78\", coordinates = { @Loc(value = SEVEN),\n                            @Loc(value = EIGHT) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"65\", coordinates = { @Loc(value = SIX),\n                            @Loc(value = FIVE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"910\", coordinates = { @Loc(value = NINE),\n                            @Loc(value = TEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1011\", coordinates = { @Loc(value = TEN),\n                            @Loc(value = ELEVEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1012\", coordinates = { @Loc(value = TEN),\n                            @Loc(value = TWELVE) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1314\", coordinates = { @Loc(value = THIRTEEN),\n                            @Loc(value = FOURTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1415\", coordinates = { @Loc(value = FOURTEEN),\n                            @Loc(value = FIFTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1617\", coordinates = { @Loc(value = SIXTEEN),\n                            @Loc(value = SEVENTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1718\", coordinates = { @Loc(value = SEVENTEEN),\n                            @Loc(value = EIGHTEEN) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"1920\", coordinates = { @Loc(value = NINETEEN),\n                            @Loc(value = TWENTY) }, tags = { \"highway=trunk\" }),\n                    @Edge(id = \"202122\", coordinates = { @Loc(value = TWENTY),\n                            @Loc(value = TWENTYONE),\n                            @Loc(value = TWENTYTWO) }, tags = { \"highway=trunk\" }), })\n\n    private Atlas atlas;\n\n    public Atlas getAtlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/filters/AtlasEntityPolygonsFilterTest.java",
    "content": "package org.openstreetmap.atlas.utilities.filters;\n\nimport static org.openstreetmap.atlas.utilities.filters.IntersectionPolicy.DEFAULT_INTERSECTION_POLICY;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.StreamSupport;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.locationtech.jts.io.WKBWriter;\nimport org.openstreetmap.atlas.geography.GeometricSurface;\nimport org.openstreetmap.atlas.geography.MultiPolygon;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;\nimport org.openstreetmap.atlas.geography.atlas.items.LineItem;\nimport org.openstreetmap.atlas.geography.atlas.items.LocationItem;\nimport org.openstreetmap.atlas.geography.atlas.items.Relation;\nimport org.openstreetmap.atlas.geography.atlas.items.RelationMember;\nimport org.openstreetmap.atlas.geography.converters.MultiPolygonStringConverter;\nimport org.openstreetmap.atlas.geography.converters.PolygonStringConverter;\nimport org.openstreetmap.atlas.geography.converters.WkbMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.WkbPolygonConverter;\nimport org.openstreetmap.atlas.geography.converters.WktMultiPolygonConverter;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonFeature;\nimport org.openstreetmap.atlas.geography.geojson.GeoJsonUtils;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.configuration.StandardConfiguration;\nimport org.openstreetmap.atlas.utilities.maps.MultiMap;\nimport org.openstreetmap.atlas.utilities.testing.FreezeDryFunction;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Tests for {@link AtlasEntityPolygonsFilter}\n *\n * @author jklamer\n */\npublic class AtlasEntityPolygonsFilterTest\n{\n    private static final IntersectionPolicy FULL_GEOMETRIC_ENCLOSING = new IntersectionPolicy()\n    {\n        private static final long serialVersionUID = -2116753295106517381L;\n\n        // note this is the only one now used by the filter\n        @Override\n        public boolean geometricSurfaceEntityIntersecting(final GeometricSurface geometricSurface,\n                final AtlasEntity entity)\n        {\n            if (entity instanceof LineItem)\n            {\n                return geometricSurface\n                        .fullyGeometricallyEncloses(((LineItem) entity).asPolyLine());\n            }\n            if (entity instanceof LocationItem)\n            {\n                return geometricSurface\n                        .fullyGeometricallyEncloses(((LocationItem) entity).getLocation());\n            }\n            if (entity instanceof Area)\n            {\n                return geometricSurface.fullyGeometricallyEncloses(((Area) entity).asPolygon());\n            }\n            if (entity instanceof Relation)\n            {\n                return ((Relation) entity).members().stream().map(RelationMember::getEntity)\n                        .anyMatch(relationEntity -> this.geometricSurfaceEntityIntersecting(\n                                geometricSurface, relationEntity));\n            }\n            else\n            {\n                return false;\n            }\n        }\n\n        @Override\n        public boolean multiPolygonEntityIntersecting(final MultiPolygon multiPolygon,\n                final AtlasEntity entity)\n        {\n\n            if (entity instanceof LineItem)\n            {\n                return multiPolygon.fullyGeometricallyEncloses(((LineItem) entity).asPolyLine());\n            }\n            if (entity instanceof LocationItem)\n            {\n                return multiPolygon\n                        .fullyGeometricallyEncloses(((LocationItem) entity).getLocation());\n            }\n            if (entity instanceof Area)\n            {\n                return multiPolygon.fullyGeometricallyEncloses(((Area) entity).asPolygon());\n            }\n            if (entity instanceof Relation)\n            {\n                return ((Relation) entity).members().stream().map(RelationMember::getEntity)\n                        .anyMatch(relationEntity -> this\n                                .multiPolygonEntityIntersecting(multiPolygon, relationEntity));\n            }\n            else\n            {\n                return false;\n            }\n        }\n\n        @Override\n        public boolean polygonEntityIntersecting(final Polygon polygon, final AtlasEntity entity)\n        {\n\n            if (entity instanceof LineItem)\n            {\n                return polygon.fullyGeometricallyEncloses(((LineItem) entity).asPolyLine());\n            }\n            if (entity instanceof LocationItem)\n            {\n                return polygon.fullyGeometricallyEncloses(((LocationItem) entity).getLocation());\n            }\n            if (entity instanceof Area)\n            {\n                return polygon.fullyGeometricallyEncloses(((Area) entity).asPolygon());\n            }\n            if (entity instanceof Relation)\n            {\n                return ((Relation) entity).members().stream().map(RelationMember::getEntity)\n                        .anyMatch(relationEntity -> this.polygonEntityIntersecting(polygon,\n                                relationEntity));\n            }\n            else\n            {\n                return false;\n            }\n        }\n    };\n\n    @Rule\n    public final AtlasEntityPolygonsFilterTestRule setup = new AtlasEntityPolygonsFilterTestRule();\n\n    /**\n     * Test Standard use cases by validating counts and asserting symmetry of include vs. exclude\n     */\n    @Test\n    public void testCounts()\n    {\n        final Atlas testCountsAtlas = this.setup.getTestCounts();\n        final String includeConfigurationFormat = \"{\\\"filter.polygons\\\":{\\\"include.wkt\\\":[\\\"%s\\\"]}}\";\n        final String excludeConfigurationFormat = \"{\\\"filter.polygons\\\":{\\\"exclude.wkt\\\":[\\\"%s\\\"]}}\";\n        final Polygon polygon1 = this.getPolygonWithName(testCountsAtlas, \"polygon1\");\n        final Polygon polygon2 = this.getPolygonWithName(testCountsAtlas, \"polygon2\");\n\n        final long totalPointCount = testCountsAtlas.numberOfPoints();\n        final long totalLineCount = testCountsAtlas.numberOfLines();\n        final long totalAreaCount = testCountsAtlas.numberOfAreas();\n        final long totalRelationCount = testCountsAtlas.numberOfRelations();\n        final AtlasEntityPolygonsFilter includePolygon1 = this\n                .constructConfiguredFilter(includeConfigurationFormat, polygon1.toWkt());\n        final AtlasEntityPolygonsFilter includePolygon2 = this\n                .constructConfiguredFilter(includeConfigurationFormat, polygon2.toWkt());\n        final AtlasEntityPolygonsFilter includePolygon1And2 = this.constructConfiguredFilter(\n                includeConfigurationFormat,\n                String.join(\"\\\",\\\"\", polygon1.toWkt(), polygon2.toWkt()));\n        final AtlasEntityPolygonsFilter excludePolygon1 = this\n                .constructConfiguredFilter(excludeConfigurationFormat, polygon1.toWkt());\n        final AtlasEntityPolygonsFilter excludePolygon2 = this\n                .constructConfiguredFilter(excludeConfigurationFormat, polygon2.toWkt());\n        final AtlasEntityPolygonsFilter excludePolygon1And2 = this.constructConfiguredFilter(\n                excludeConfigurationFormat,\n                String.join(\"\\\",\\\"\", polygon1.toWkt(), polygon2.toWkt()));\n\n        // testing polygon1\n        this.assertCounts(testCountsAtlas, includePolygon1, 2L, 3L, 1L, 0L);\n        this.assertCounts(testCountsAtlas, excludePolygon1, totalPointCount - 2L,\n                totalLineCount - 3L, totalAreaCount - 1L, totalRelationCount);\n        this.assertCounts(testCountsAtlas, includePolygon1.negate(), totalPointCount - 2L,\n                totalLineCount - 3L, totalAreaCount - 1L, totalRelationCount);\n\n        // testing polygon2\n        this.assertCounts(testCountsAtlas, includePolygon2, 0L, 2L, 1L, 1L);\n        this.assertCounts(testCountsAtlas, excludePolygon2, totalPointCount, totalLineCount - 2L,\n                totalAreaCount - 1L, totalRelationCount - 1L);\n        this.assertCounts(testCountsAtlas, excludePolygon2.negate(), 0L, 2L, 1L, 1L);\n\n        // testing tandem filter\n        this.assertCounts(testCountsAtlas, includePolygon1And2, totalPointCount - 1L,\n                totalLineCount - 2L, totalAreaCount, totalRelationCount);\n        this.assertCounts(testCountsAtlas, excludePolygon1And2, 1L, 2L, 0L, 0L);\n        this.assertCounts(testCountsAtlas, includePolygon1And2.negate(), 1L, 2L, 0L, 0L);\n\n        // testing simple filter construction\n        this.assertCounts(testCountsAtlas,\n                AtlasEntityPolygonsFilter.Type.INCLUDE.polygons(Collections.singleton(polygon1)),\n                2L, 3L, 1L, 0L);\n        this.assertCounts(testCountsAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE\n                .geometricSurfaces(Collections.singleton(polygon1)), 2L, 3L, 1L, 0L);\n        this.assertCounts(testCountsAtlas,\n                AtlasEntityPolygonsFilter.Type.EXCLUDE.polygons(Collections.singleton(polygon1)),\n                totalPointCount - 2L, totalLineCount - 3L, totalAreaCount - 1L, totalRelationCount);\n        this.assertCounts(testCountsAtlas,\n                AtlasEntityPolygonsFilter.Type.EXCLUDE\n                        .geometricSurfaces(Collections.singleton(polygon1)),\n                totalPointCount - 2L, totalLineCount - 3L, totalAreaCount - 1L, totalRelationCount);\n        this.assertCounts(testCountsAtlas,\n                AtlasEntityPolygonsFilter.Type.INCLUDE.polygons(Arrays.asList(polygon1, polygon2)),\n                totalPointCount - 1L, totalLineCount - 2L, totalAreaCount, totalRelationCount);\n    }\n\n    /**\n     * Tests every polygon input form supported by the filter\n     */\n    @Test\n    public void testForms()\n    {\n        final Atlas testFormsAtlas = this.setup.getTestForm();\n        final String wktConfigurationStringFormat = \"{\\\"filter.polygons\\\":{\\\"include.wkt\\\":[\\\"%s\\\"]}}\";\n        final String wkbConfigurationStringFormat = \"{\\\"filter.polygons\\\":{\\\"include.wkb\\\":[\\\"%s\\\"]}}\";\n        final String atlasConfigurationStringFormat = \"{\\\"filter.polygons\\\":{\\\"include.atlas\\\":[\\\"%s\\\"]}}\";\n        final String geojsonConfigurationStringFormat = \"{\\\"filter.polygons\\\":{\\\"include.geojson\\\":[\\\"%s\\\"]}}\";\n        final Polygon includeBoundary = this.getPolygonWithName(testFormsAtlas, \"include\");\n\n        this.assertCounts(this.setup.getTestForm(), this.constructConfiguredFilter(\n                wktConfigurationStringFormat, includeBoundary.toWkt()), 2, 2, 2, 0);\n        this.assertCounts(this.setup.getTestForm(),\n                this.constructConfiguredFilter(wkbConfigurationStringFormat,\n                        WKBWriter.toHex(new WkbPolygonConverter().convert(includeBoundary))),\n                2, 2, 2, 0);\n        this.assertCounts(this.setup.getTestForm(),\n                this.constructConfiguredFilter(atlasConfigurationStringFormat,\n                        new PolygonStringConverter().backwardConvert(includeBoundary)),\n                2, 2, 2, 0);\n        this.assertCounts(this.setup.getTestForm(), this.constructConfiguredFilter(\n                geojsonConfigurationStringFormat,\n                GeoJsonUtils.featureCollection(Collections.singletonList(new GeoJsonFeature()\n                {\n                    @Override\n                    public JsonObject asGeoJsonGeometry()\n                    {\n                        return includeBoundary.asGeoJson();\n                    }\n\n                    @Override\n                    public JsonObject getGeoJsonProperties()\n                    {\n                        return new JsonObject();\n                    }\n                }), new JsonObject()).toString().replaceAll(\"\\\"\", \"\\\\\\\\\\\"\")), 2, 2, 2, 0);\n    }\n\n    /**\n     * Tests every arrangement of 1 include and 1 exclude polygon in a configuration. Each situation\n     * the include dominance of the configured filter means that exclude polygon is ignored. Each\n     * situation designed to have same count result.\n     */\n    @Test\n    public void testIncludeDominant()\n    {\n        final int numberOfCases = 4;\n        final Atlas includeExcludeArrangements = this.setup.getIncludeExcludeArrangements();\n        final String configurationStringFormat = \"{\\\"filter.polygons\\\":{\\\"include.wkt\\\":[\\\"%s\\\"],\\\"exclude.wkt\\\":[\\\"%s\\\"]}}\";\n        final Polygon includePolygon = this.getPolygonWithName(includeExcludeArrangements,\n                \"include\");\n\n        for (int caseNumber = 1; caseNumber <= numberOfCases; caseNumber++)\n        {\n            final String caseString = String.format(\"case_%d\", caseNumber);\n            final String excludePolygonName = \"exclude_\".concat(caseString);\n            final Polygon excludePolygon = this.getPolygonWithName(includeExcludeArrangements,\n                    excludePolygonName);\n            final AtlasEntityPolygonsFilter filter = this.constructConfiguredFilter(\n                    configurationStringFormat, includePolygon.toWkt(), excludePolygon.toWkt());\n\n            Assert.assertEquals(2L, StreamSupport\n                    .stream(includeExcludeArrangements\n                            .lines(line -> line.getTag(\"name\")\n                                    .filter(value -> value.contains(caseString)).isPresent())\n                            .spliterator(), false)\n                    .filter(filter).map(line -> line.getTag(\"name\")).filter(Optional::isPresent)\n                    .map(Optional::get).peek(name -> Assert.assertTrue(name.contains(\"included\")))\n                    .count());\n\n            Assert.assertEquals(1L, StreamSupport\n                    .stream(includeExcludeArrangements\n                            .lines(line -> line.getTag(\"name\")\n                                    .filter(value -> value.contains(caseString)).isPresent())\n                            .spliterator(), false)\n                    .filter(filter.negate()).map(line -> line.getTag(\"name\"))\n                    .filter(Optional::isPresent).map(Optional::get)\n                    .peek(name -> Assert.assertTrue(name.contains(\"excluded\"))).count());\n        }\n    }\n\n    @Test\n    public void testIntersectionPolicy()\n    {\n\n        final Atlas testCountsAtlas = this.setup.getTestCounts();\n        final Polygon polygon1 = this.getPolygonWithName(testCountsAtlas, \"polygon1\");\n        final Polygon polygon2 = this.getPolygonWithName(testCountsAtlas, \"polygon2\");\n        final long totalPointCount = testCountsAtlas.numberOfPoints();\n        final long totalLineCount = testCountsAtlas.numberOfLines();\n        final long totalAreaCount = testCountsAtlas.numberOfAreas();\n        final long totalRelationCount = testCountsAtlas.numberOfRelations();\n        final IntersectionPolicy dudIntersectionPolicy = new IntersectionPolicy()\n        {\n            private static final long serialVersionUID = 7127929164076173373L;\n            // uses all false defaults\n        };\n\n        // Test all three policies on the first polygon\n        this.assertCounts(\n                testCountsAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE\n                        .polygons(DEFAULT_INTERSECTION_POLICY, Collections.singleton(polygon1)),\n                2L, 3L, 1L, 0L);\n        this.assertCounts(testCountsAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE\n                .polygons(dudIntersectionPolicy, Collections.singleton(polygon1)), 0L, 0L, 0L, 0L);\n        this.assertCounts(testCountsAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE.polygons(\n                FULL_GEOMETRIC_ENCLOSING, Collections.singleton(polygon1)), 2L, 1L, 1L, 0L);\n        this.assertCounts(testCountsAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE.geometricSurfaces(\n                FULL_GEOMETRIC_ENCLOSING, Collections.singleton(polygon1)), 2L, 1L, 1L, 0L);\n\n        // Test all three decision makers on two polygon filter\n        this.assertCounts(testCountsAtlas,\n                AtlasEntityPolygonsFilter.Type.INCLUDE.polygons(DEFAULT_INTERSECTION_POLICY,\n                        Arrays.asList(polygon1, polygon2)),\n                totalPointCount - 1L, totalLineCount - 2L, totalAreaCount, totalRelationCount);\n        this.assertCounts(testCountsAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE\n                .polygons(dudIntersectionPolicy, Arrays.asList(polygon1, polygon2)), 0, 0, 0, 0);\n        this.assertCounts(testCountsAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE\n                .polygons(FULL_GEOMETRIC_ENCLOSING, Arrays.asList(polygon1, polygon2)), 2, 1, 2, 0);\n\n        // Test Serializability\n        this.assertCounts(testCountsAtlas,\n                new FreezeDryFunction<AtlasEntityPolygonsFilter>().apply(\n                        AtlasEntityPolygonsFilter.Type.INCLUDE.polygons(FULL_GEOMETRIC_ENCLOSING,\n                                Arrays.asList(polygon1, polygon2))),\n                2, 1, 2, 0);\n    }\n\n    /**\n     * Test Multipolygon Functionality\n     */\n    @Test\n    public void testMultiPolygon()\n    {\n\n        final Atlas testMultiPolygonFilterAtlas = this.setup.getMultiPolygons();\n        final long totalPointCount = testMultiPolygonFilterAtlas.numberOfPoints();\n        final long totalLineCount = testMultiPolygonFilterAtlas.numberOfLines();\n        final long totalAreaCount = testMultiPolygonFilterAtlas.numberOfAreas();\n        final long totalRelationCount = testMultiPolygonFilterAtlas.numberOfRelations();\n        final Polygon multiPolygon1Outer1 = this.getPolygonWithName(testMultiPolygonFilterAtlas,\n                \"multipolygon1-outer1\");\n        final Polygon multiPolygon1Inner1 = this.getPolygonWithName(testMultiPolygonFilterAtlas,\n                \"multipolygon1-inner1\");\n        final Polygon multiPolygon1Inner2 = this.getPolygonWithName(testMultiPolygonFilterAtlas,\n                \"multipolygon1-inner2\");\n        final Polygon multiPolygon2Outer1 = this.getPolygonWithName(testMultiPolygonFilterAtlas,\n                \"multipolygon2-outer1\");\n        final Polygon multiPolygon2Inner1 = this.getPolygonWithName(testMultiPolygonFilterAtlas,\n                \"multipolygon2-inner1\");\n        final MultiMap<Polygon, Polygon> multiPolygonMap1 = new MultiMap<>();\n        multiPolygonMap1.add(multiPolygon1Outer1, multiPolygon1Inner1);\n        multiPolygonMap1.add(multiPolygon1Outer1, multiPolygon1Inner2);\n        final MultiMap<Polygon, Polygon> multiPolygonMap2 = new MultiMap<>();\n        multiPolygonMap2.add(multiPolygon2Outer1, multiPolygon2Inner1);\n        final MultiPolygon multiPolygon1 = new MultiPolygon(multiPolygonMap1);\n        final MultiPolygon multiPolygon2 = new MultiPolygon(multiPolygonMap2);\n        final Polygon polygon1 = this.getPolygonWithName(testMultiPolygonFilterAtlas, \"polygon1\");\n\n        // Multipolygon 1 and 2 include counts\n        this.assertCounts(testMultiPolygonFilterAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE\n                .multiPolygons(Collections.singleton(multiPolygon1)), 3, 1, 5, 1);\n        this.assertCounts(testMultiPolygonFilterAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE\n                .multiPolygons(Collections.singleton(multiPolygon2)), 2, 1, 3, 1);\n\n        // Multipolygon 1 and 2 exclude counts\n        this.assertCounts(testMultiPolygonFilterAtlas,\n                AtlasEntityPolygonsFilter.Type.EXCLUDE\n                        .multiPolygons(Collections.singleton(multiPolygon1)),\n                totalPointCount - 3, totalLineCount - 1, totalAreaCount - 5,\n                totalRelationCount - 1);\n        this.assertCounts(testMultiPolygonFilterAtlas,\n                AtlasEntityPolygonsFilter.Type.EXCLUDE\n                        .multiPolygons(Collections.singleton(multiPolygon2)),\n                totalPointCount - 2, totalLineCount - 1, totalAreaCount - 3,\n                totalRelationCount - 1);\n\n        // Merged MultiPolygon include is sum of above (except relation is the same between the two)\n        this.assertCounts(testMultiPolygonFilterAtlas, AtlasEntityPolygonsFilter.Type.INCLUDE\n                .multiPolygons(Arrays.asList(multiPolygon1, multiPolygon2)), 3 + 2, 1 + 1, 5 + 3,\n                1);\n        this.assertCounts(testMultiPolygonFilterAtlas,\n                AtlasEntityPolygonsFilter.Type.INCLUDE\n                        .multiPolygons(Collections.singleton(multiPolygon1.merge(multiPolygon2))),\n                3 + 2, 1 + 1, 5 + 3, 1);\n\n        // both polygons and multipolygons\n        this.assertCounts(testMultiPolygonFilterAtlas,\n                AtlasEntityPolygonsFilter.Type.INCLUDE.polygonsAndMultiPolygons(\n                        Collections.singleton(polygon1),\n                        Arrays.asList(multiPolygon1, multiPolygon2)),\n                totalPointCount - 2, totalLineCount - 2, totalAreaCount - 1, totalRelationCount);\n\n    }\n\n    /**\n     * Tests the various forms of configurable multipolygons\n     */\n    @Test\n    public void testMultiPolygonForms()\n    {\n        final Atlas testMultiPolygonFormsAtlas = this.setup.getMultiPolygons();\n        final Polygon outerPolygon = this.getPolygonWithName(testMultiPolygonFormsAtlas,\n                \"multipolygon1-outer1\");\n        final Polygon innerPolygon1 = this.getPolygonWithName(testMultiPolygonFormsAtlas,\n                \"multipolygon1-inner1\");\n        final Polygon innerPolygon2 = this.getPolygonWithName(testMultiPolygonFormsAtlas,\n                \"multipolygon1-inner2\");\n        final MultiMap<Polygon, Polygon> multiPolygonMap = new MultiMap<>();\n        multiPolygonMap.add(outerPolygon, innerPolygon1);\n        multiPolygonMap.add(outerPolygon, innerPolygon2);\n        final MultiPolygon multiPolygon1 = new MultiPolygon(multiPolygonMap);\n\n        final String wktConfigurationStringFormat = \"{\\\"filter.multipolygons\\\":{\\\"include.wkt\\\":[\\\"%s\\\"]}}\";\n        final String wkbConfigurationStringFormat = \"{\\\"filter.multipolygons\\\":{\\\"include.wkb\\\":[\\\"%s\\\"]}}\";\n        final String atlasConfigurationStringFormat = \"{\\\"filter.multipolygons\\\":{\\\"include.atlas\\\":[\\\"%s\\\"]}}\";\n        final String geojsonConfigurationStringFormat = \"{\\\"filter.multipolygons\\\":{\\\"include.geojson\\\":[\\\"%s\\\"]}}\";\n\n        this.assertCounts(testMultiPolygonFormsAtlas,\n                this.constructConfiguredFilter(wkbConfigurationStringFormat,\n                        WKBWriter.toHex(new WkbMultiPolygonConverter().convert(multiPolygon1))),\n                3, 1, -1, -1);\n        this.assertCounts(testMultiPolygonFormsAtlas,\n                this.constructConfiguredFilter(wktConfigurationStringFormat,\n                        new WktMultiPolygonConverter().convert(multiPolygon1)),\n                3, 1, -1, -1);\n        this.assertCounts(testMultiPolygonFormsAtlas,\n                this.constructConfiguredFilter(atlasConfigurationStringFormat,\n                        new MultiPolygonStringConverter().backwardConvert(multiPolygon1)),\n                3, 1, -1, -1);\n        this.assertCounts(testMultiPolygonFormsAtlas,\n                this.constructConfiguredFilter(geojsonConfigurationStringFormat, multiPolygon1\n                        .asGeoJsonFeatureCollection().toString().replaceAll(\"\\\"\", \"\\\\\\\\\\\"\")),\n                3, 1, -1, -1);\n    }\n\n    private void assertCounts(final Atlas atlas, final Predicate<AtlasEntity> filter,\n            final long expectedPointCount, final long expectedLineCount,\n            final long expectedAreaCount, final long expectedRelationCount)\n    {\n        if (expectedPointCount >= 0)\n        {\n            Assert.assertEquals(expectedPointCount, Iterables.size(atlas.points(filter::test)));\n        }\n        if (expectedLineCount >= 0)\n        {\n            Assert.assertEquals(expectedLineCount, Iterables.size(atlas.lines(filter::test)));\n        }\n        if (expectedAreaCount >= 0)\n        {\n            Assert.assertEquals(expectedAreaCount, Iterables.size(atlas.areas(filter::test)));\n        }\n        if (expectedRelationCount >= 0)\n        {\n            Assert.assertEquals(expectedRelationCount,\n                    Iterables.size(atlas.relations(filter::test)));\n        }\n    }\n\n    private AtlasEntityPolygonsFilter constructConfiguredFilter(final String format,\n            final Object... polygonStrings)\n    {\n        return AtlasEntityPolygonsFilter.forConfiguration(new StandardConfiguration(\n                new StringResource(String.format(format, polygonStrings))));\n    }\n\n    private Polygon getPolygonWithName(final Atlas atlas, final String name)\n    {\n        return StreamSupport\n                .stream(atlas.areas(area -> area.getTag(\"name\")\n                        .filter(string -> string.equals(name)).isPresent()).spliterator(), false)\n                .findFirst().get().asPolygon();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/filters/AtlasEntityPolygonsFilterTestRule.java",
    "content": "package org.openstreetmap.atlas.utilities.filters;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.CoreTestRule;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas;\n\n/**\n * Test Rule for {@link AtlasEntityPolygonsFilterTest}\n *\n * @author jklamer\n */\npublic class AtlasEntityPolygonsFilterTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"includeExcludePolygonArrangements.osm\")\n    private Atlas includeExcludeArrangements;\n    @TestAtlas(loadFromJosmOsmResource = \"multiPolygons.osm\")\n    private Atlas multiPolygons;\n    @TestAtlas(loadFromJosmOsmResource = \"testCounts.osm\")\n    private Atlas testCounts;\n    @TestAtlas(loadFromJosmOsmResource = \"testForms.osm\")\n    private Atlas testForm;\n\n    public Atlas getIncludeExcludeArrangements()\n    {\n        return this.includeExcludeArrangements;\n    }\n\n    public Atlas getMultiPolygons()\n    {\n        return this.multiPolygons;\n    }\n\n    public Atlas getTestCounts()\n    {\n        return this.testCounts;\n    }\n\n    public Atlas getTestForm()\n    {\n        return this.testForm;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/graphs/DirectedAcyclicGraphTest.java",
    "content": "package org.openstreetmap.atlas.utilities.graphs;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.Stack;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * Basic validation of DAG functionality\n *\n * @author cuthbertm\n * @author mgostintsev\n */\npublic class DirectedAcyclicGraphTest\n{\n    private static final String PLATINUM = \"Platinum\";\n    private static final String GOLD = \"Gold\";\n    private static final String COPPER = \"Copper\";\n    private static final String PRECIOUS = \"Precious\";\n    private static final String COINAGE = \"Coinage\";\n    private static final String METAL = \"Metal\";\n\n    private final DirectedAcyclicGraph<String> graph;\n\n    public DirectedAcyclicGraphTest()\n    {\n        this.graph = new DirectedAcyclicGraph<>();\n    }\n\n    @Before\n    public void setup()\n    {\n        // Vertices\n        this.graph.addVertex(PLATINUM);\n        this.graph.addVertex(GOLD);\n        this.graph.addVertex(COPPER);\n        this.graph.addVertex(PRECIOUS);\n        this.graph.addVertex(COINAGE);\n        this.graph.addVertex(METAL);\n\n        // Edges\n        this.graph.addEdge(METAL, COINAGE);\n        this.graph.addEdge(METAL, PRECIOUS);\n        this.graph.addEdge(PRECIOUS, PLATINUM);\n        this.graph.addEdge(PRECIOUS, GOLD);\n        this.graph.addEdge(COINAGE, GOLD);\n        this.graph.addEdge(COINAGE, COPPER);\n    }\n\n    @Test\n    public void testDeepestLevel()\n    {\n        assertEquals(1, this.graph.getDeepestLevel(METAL));\n        assertEquals(3, this.graph.getDeepestLevel(GOLD));\n    }\n\n    @Test\n    public void testPaths()\n    {\n        assertTrue(this.graph.hasPath(METAL, COPPER));\n        assertTrue(this.graph.hasPath(METAL, PLATINUM));\n        assertFalse(this.graph.hasPath(COINAGE, PLATINUM));\n\n        // Adding this edge will cause a cycle, which is not allowed in a DAG\n        Assert.assertTrue(!this.graph.addEdge(COPPER, METAL));\n    }\n\n    @Test\n    public void testProcessGroups()\n    {\n        final List<Set<String>> processGroups = this.graph.processGroups();\n        final List<Set<String>> expected = new ArrayList<>();\n        final Set<String> first = Sets.hashSet(METAL);\n        final Set<String> second = Sets.hashSet(PRECIOUS, COINAGE);\n        final Set<String> third = Sets.hashSet(GOLD, PLATINUM, COPPER);\n        expected.add(first);\n        expected.add(second);\n        expected.add(third);\n        Assert.assertEquals(expected, processGroups);\n    }\n\n    @Test\n    public void testProcessGroupsWithSourceSinks()\n    {\n        final DirectedAcyclicGraph<String> dag = new DirectedAcyclicGraph<>();\n        dag.addVertex(\"a\");\n        dag.addVertex(\"b\");\n        dag.addVertex(\"c\");\n        final List<Set<String>> processGroups = dag.processGroups();\n        Assert.assertEquals(1, processGroups.size());\n    }\n\n    @Test\n    public void testSinks()\n    {\n        final Set<String> expectedSinks = new HashSet<>(Arrays.asList(PLATINUM, GOLD, COPPER));\n        assertTrue(this.graph.getSinks().stream().allMatch(expectedSinks::remove)\n                && expectedSinks.isEmpty());\n    }\n\n    @Test\n    public void testSources()\n    {\n        final Set<String> expectedSources = new HashSet<>(Arrays.asList(METAL));\n        assertTrue(this.graph.getSources().stream().allMatch(expectedSources::remove)\n                && expectedSources.isEmpty());\n    }\n\n    @Test\n    public void testTopologicalSort()\n    {\n        final Stack<String> topologicalSort = this.graph.getTopologicalSortedList();\n        Assert.assertEquals(METAL, topologicalSort.pop());\n        Assert.assertEquals(COINAGE, topologicalSort.pop());\n        Assert.assertEquals(PRECIOUS, topologicalSort.pop());\n        Assert.assertEquals(COPPER, topologicalSort.pop());\n        Assert.assertEquals(GOLD, topologicalSort.pop());\n        Assert.assertEquals(PLATINUM, topologicalSort.pop());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/identifiers/EntityIdentifierGeneratorTest.java",
    "content": "package org.openstreetmap.atlas.utilities.identifiers;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.PolyLine;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.geography.atlas.builder.RelationBean;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompletePoint;\nimport org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation;\nimport org.openstreetmap.atlas.geography.atlas.items.ItemType;\nimport org.openstreetmap.atlas.utilities.collections.Maps;\nimport org.openstreetmap.atlas.utilities.collections.Sets;\n\n/**\n * @author lcram\n */\npublic class EntityIdentifierGeneratorTest\n{\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void testEmptyConfigException()\n    {\n        final CompletePoint point = new CompletePoint(1L, Location.CENTER,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Sets.hashSet());\n\n        Assert.assertTrue((new EntityIdentifierGenerator.Configuration().getGenerator()\n                .getBasicPropertyString(point)\n                + new EntityIdentifierGenerator.Configuration().getGenerator()\n                        .getTypeSpecificPropertyString(point))\n                .isEmpty());\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"EntityIdentifierGenerator.Configuration was empty! Please set at least one of geometry, tags, or relation members.\");\n        new EntityIdentifierGenerator.Configuration().getGenerator().generateIdentifier(point);\n    }\n\n    @Test\n    public void testForcePositiveIDForEdge()\n    {\n        final CompleteEdge edge = new CompleteEdge(1L, PolyLine.SIMPLE_POLYLINE,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), 2L, 3L, Sets.hashSet());\n\n        this.expectedException.expect(IllegalArgumentException.class);\n        this.expectedException\n                .expectMessage(\"For type EDGE, please use generatePositiveIdentifierForEdge\");\n        new EntityIdentifierGenerator.Configuration().useDefaults().getGenerator()\n                .generateIdentifier(edge);\n    }\n\n    @Test\n    public void testGetFullPropertyString()\n    {\n        final CompleteEdge edge = new CompleteEdge(1L, PolyLine.SIMPLE_POLYLINE,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), 2L, 3L, Sets.hashSet());\n\n        final String goldenPropertyStringAll = \"LINESTRING (1 1, 2 2);a=b,c=d\";\n        Assert.assertEquals(goldenPropertyStringAll,\n                new EntityIdentifierGenerator.Configuration().useDefaults().getGenerator()\n                        .getBasicPropertyString(edge)\n                        + new EntityIdentifierGenerator().getTypeSpecificPropertyString(edge));\n\n        final String goldenPropertyStringGeometryOnly = \"LINESTRING (1 1, 2 2)\";\n        Assert.assertEquals(goldenPropertyStringGeometryOnly,\n                new EntityIdentifierGenerator.Configuration().useGeometry().getGenerator()\n                        .getBasicPropertyString(edge)\n                        + new EntityIdentifierGenerator().getTypeSpecificPropertyString(edge));\n\n        final String goldenPropertyStringTagsOnly = \";a=b,c=d\";\n        Assert.assertEquals(goldenPropertyStringTagsOnly,\n                new EntityIdentifierGenerator.Configuration().useTags().getGenerator()\n                        .getBasicPropertyString(edge)\n                        + new EntityIdentifierGenerator().getTypeSpecificPropertyString(edge));\n    }\n\n    @Test\n    public void testGetPropertyString()\n    {\n        final CompletePoint point = new CompletePoint(1L, Location.CENTER,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Sets.hashSet());\n\n        final String goldenPropertyString = \"POINT (0 0);a=b,c=d\";\n        Assert.assertEquals(goldenPropertyString,\n                new EntityIdentifierGenerator().getBasicPropertyString(point));\n    }\n\n    @Test\n    public void testGetTypeSpecificPropertyStringForRelation()\n    {\n        final RelationBean bean1 = new RelationBean();\n        bean1.addItem(1L, \"role\", ItemType.POINT);\n        bean1.addItem(10L, \"role\", ItemType.AREA);\n\n        final RelationBean bean2 = new RelationBean();\n        bean2.addItem(10L, \"role\", ItemType.AREA);\n        bean2.addItem(1L, \"role\", ItemType.POINT);\n\n        final CompleteRelation relation1 = new CompleteRelation(1L,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Rectangle.MINIMUM, bean1, null, null, null,\n                Sets.hashSet());\n        final CompleteRelation relation2 = new CompleteRelation(1L,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Rectangle.MINIMUM, bean2, null, null, null,\n                Sets.hashSet());\n\n        final String goldenPropertyString = \";RelationBean[(AREA,10,role)(POINT,1,role)]\";\n        Assert.assertEquals(goldenPropertyString,\n                new EntityIdentifierGenerator().getTypeSpecificPropertyString(relation1));\n        Assert.assertEquals(goldenPropertyString,\n                new EntityIdentifierGenerator().getTypeSpecificPropertyString(relation2));\n        Assert.assertTrue(\n                new EntityIdentifierGenerator.Configuration().useDefaults().excludeRelationMembers()\n                        .getGenerator().getTypeSpecificPropertyString(relation1).isEmpty());\n        Assert.assertEquals(goldenPropertyString, new EntityIdentifierGenerator.Configuration()\n                .useRelationMembers().getGenerator().getTypeSpecificPropertyString(relation2));\n    }\n\n    @Test\n    public void testHashes()\n    {\n        final CompleteEdge edge = new CompleteEdge(1L, PolyLine.SIMPLE_POLYLINE,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), 2L, 3L, Sets.hashSet());\n\n        final long goldenHash1 = 6463671242943641314L;\n        Assert.assertEquals(goldenHash1,\n                new EntityIdentifierGenerator().generatePositiveIdentifierForEdge(edge));\n\n        final CompletePoint point = new CompletePoint(1L, Location.CENTER,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Sets.hashSet());\n\n        final long goldenHash2 = 4334702026426103264L;\n        Assert.assertEquals(goldenHash2, new EntityIdentifierGenerator().generateIdentifier(point));\n        Assert.assertEquals(goldenHash2, new EntityIdentifierGenerator.Configuration().useDefaults()\n                .getGenerator().generateIdentifier(point));\n\n        final long goldenHash3 = -2934213421148195810L;\n        Assert.assertEquals(goldenHash3, new EntityIdentifierGenerator.Configuration().useGeometry()\n                .getGenerator().generateIdentifier(point));\n        Assert.assertEquals(goldenHash3, new EntityIdentifierGenerator.Configuration().useDefaults()\n                .excludeTags().excludeRelationMembers().getGenerator().generateIdentifier(point));\n\n        final long goldenHash4 = 3739460904040018219L;\n        Assert.assertEquals(goldenHash4, new EntityIdentifierGenerator.Configuration().useTags()\n                .getGenerator().generateIdentifier(point));\n        Assert.assertEquals(goldenHash4,\n                new EntityIdentifierGenerator.Configuration().useDefaults().excludeGeometry()\n                        .excludeRelationMembers().getGenerator().generateIdentifier(point));\n    }\n\n    @Test\n    public void testNonRelationInvariantConfigException()\n    {\n        final EntityIdentifierGenerator nonRelationInvariantGenerator = new EntityIdentifierGenerator.Configuration()\n                .useRelationMembers().getGenerator();\n\n        final RelationBean bean1 = new RelationBean();\n        bean1.addItem(1L, \"role\", ItemType.POINT);\n        bean1.addItem(10L, \"role\", ItemType.AREA);\n        final CompleteRelation relation1 = new CompleteRelation(1L,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Rectangle.MINIMUM, bean1, null, null, null,\n                Sets.hashSet());\n        Assert.assertEquals(6707509058043000459L,\n                nonRelationInvariantGenerator.generateIdentifier(relation1));\n\n        final CompletePoint point = new CompletePoint(1L, Location.CENTER,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Sets.hashSet());\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\n                \"EntityIdentifierGenerator.Configuration was non-relation invariant! Please set at least one of geometry or tags to generate IDs for non-relation type entities.\");\n        nonRelationInvariantGenerator.generateIdentifier(point);\n    }\n\n    @Test\n    public void testUnsetGeometryException()\n    {\n        final CompletePoint pointNoGeometry = new CompletePoint(1L, null,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Sets.hashSet());\n        final CompletePoint pointNoTags = new CompletePoint(1L, Location.CENTER, null,\n                Sets.hashSet());\n\n        new EntityIdentifierGenerator.Configuration().useTags().getGenerator()\n                .generateIdentifier(pointNoGeometry);\n        new EntityIdentifierGenerator.Configuration().useGeometry().getGenerator()\n                .generateIdentifier(pointNoTags);\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Geometry must be set for entity\");\n        new EntityIdentifierGenerator().generateIdentifier(pointNoGeometry);\n    }\n\n    @Test\n    public void testUnsetRelationMembersException()\n    {\n        final RelationBean bean1 = new RelationBean();\n        bean1.addItem(1L, \"role\", ItemType.POINT);\n        bean1.addItem(10L, \"role\", ItemType.AREA);\n        final CompleteRelation relationWithMembers = new CompleteRelation(1L,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Rectangle.MINIMUM, bean1, null, null, null,\n                Sets.hashSet());\n        final CompleteRelation relationNoMembers = new CompleteRelation(1L,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Rectangle.MINIMUM, null, null, null, null,\n                Sets.hashSet());\n\n        new EntityIdentifierGenerator.Configuration().useDefaults().getGenerator()\n                .generateIdentifier(relationWithMembers);\n        new EntityIdentifierGenerator.Configuration().useDefaults().excludeRelationMembers()\n                .getGenerator().generateIdentifier(relationNoMembers);\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Relation members must be set for entity\");\n        new EntityIdentifierGenerator().generateIdentifier(relationNoMembers);\n    }\n\n    @Test\n    public void testUnsetTagsException()\n    {\n        final CompletePoint pointNoGeometry = new CompletePoint(1L, null,\n                Maps.hashMap(\"a\", \"b\", \"c\", \"d\"), Sets.hashSet());\n        final CompletePoint pointNoTags = new CompletePoint(1L, Location.CENTER, null,\n                Sets.hashSet());\n\n        new EntityIdentifierGenerator.Configuration().useTags().getGenerator()\n                .generateIdentifier(pointNoGeometry);\n        new EntityIdentifierGenerator.Configuration().useGeometry().getGenerator()\n                .generateIdentifier(pointNoTags);\n\n        this.expectedException.expect(CoreException.class);\n        this.expectedException.expectMessage(\"Tags must be set for entity\");\n        new EntityIdentifierGenerator().generateIdentifier(pointNoTags);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/jsoncompare/ArraysRegularExpressionJSONComparatorTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.jsoncompare;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.skyscreamer.jsonassert.JSONCompare;\nimport org.skyscreamer.jsonassert.JSONCompareMode;\nimport org.skyscreamer.jsonassert.JSONCompareResult;\nimport org.skyscreamer.jsonassert.JSONParser;\n\n/**\n * JSONArray-specific Test cases for the {@link RegularExpressionJSONComparator}\n *\n * @author cstaylor\n */\npublic class ArraysRegularExpressionJSONComparatorTestCase\n{\n    @Test\n    public void arrayCompare() throws JSONException\n    {\n        final JSONCompareResult results = JSONCompare.compareJSON(\"[\\\"abc\\\"]\", \"[\\\"abc\\\"]\",\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void extensibleNotReallyIfAnArrayTest() throws JSONException\n    {\n        final JSONCompareResult results = JSONCompare.compareJSON(\"[\\\"abc\\\"]\", \"[\\\"abc\\\", \\\"def\\\"]\",\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT_ORDER));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void lenientArrayOfJSONObjectsWithSameKeysButDifferentValues() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser\n                .parseJSON(\"[ { foo:\\\"123\\\" }, { bar:\\\"456\\\" } ]\");\n        final JSONArray array2 = (JSONArray) JSONParser\n                .parseJSON(\"[ { bar:\\\"456\\\" }, { foo:\\\"123\\\" } ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void lenientArraysContainingDifferentKeys() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser\n                .parseJSON(\"[ { foo:\\\"123\\\" }, { foo:\\\"456\\\" }, { foo:\\\"BBB\\\" } ]\");\n        final JSONArray array2 = (JSONArray) JSONParser\n                .parseJSON(\"[ { foo:\\\"456\\\" }, { foo:\\\"123\\\" }, { foo:\\\"AAA\\\" } ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void lenientOutOfOrderArrays() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser.parseJSON(\"[\\\"123\\\", \\\"456\\\"]\");\n        final JSONArray array2 = (JSONArray) JSONParser.parseJSON(\"[\\\"456\\\", \\\"123\\\"]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void lenientOutOfOrderArraysContainingArrays() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser.parseJSON(\"[ [\\\"123\\\" ], [\\\"456\\\" ] ]\");\n        final JSONArray array2 = (JSONArray) JSONParser.parseJSON(\"[ [\\\"456\\\" ], [\\\"123\\\" ] ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void lenientOutOfOrderArraysContainingDifferentObjects() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser\n                .parseJSON(\"[ { foo:\\\"123\\\" }, { foo:\\\"456\\\" }, { foo:\\\"BBB\\\" } ]\");\n        final JSONArray array2 = (JSONArray) JSONParser\n                .parseJSON(\"[ { bar:\\\"456\\\" }, { bar:\\\"123\\\" }, { bar:\\\"AAA\\\" } ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void lenientOutOfOrderArraysContainingMixedItems() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser.parseJSON(\"[ { foo:\\\"123\\\" }, 456 ]\");\n        final JSONArray array2 = (JSONArray) JSONParser.parseJSON(\"[ 456, { foo:\\\"123\\\" } ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void lenientOutOfOrderArraysContainingObjects() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser\n                .parseJSON(\"[ { foo:\\\"123\\\" }, { foo:\\\"456\\\" } ]\");\n        final JSONArray array2 = (JSONArray) JSONParser\n                .parseJSON(\"[ { foo:\\\"456\\\" }, { foo:\\\"123\\\" } ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void lenientOutOfOrderArraysInsufficientItems() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser.parseJSON(\"[\\\"123\\\", \\\"123\\\"]\");\n        final JSONArray array2 = (JSONArray) JSONParser.parseJSON(\"[\\\"789\\\", \\\"123\\\"]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void lenientOutOfOrderArraysMissingItems() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser.parseJSON(\"[\\\"123\\\", \\\"456\\\"]\");\n        final JSONArray array2 = (JSONArray) JSONParser.parseJSON(\"[\\\"789\\\", \\\"123\\\"]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void lenientOutOfOrderArraysMixedAndMissingItems() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser.parseJSON(\"[ { foo:\\\"123\\\" }, 789 ]\");\n        final JSONArray array2 = (JSONArray) JSONParser.parseJSON(\"[ 456, { foo:\\\"123\\\" } ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void strictOutOfOrderArrays() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser.parseJSON(\"[\\\"123\\\", \\\"456\\\"]\");\n        final JSONArray array2 = (JSONArray) JSONParser.parseJSON(\"[\\\"456\\\", \\\"123\\\"]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT_ORDER));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void strictOutOfOrderArraysContainingArrays() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser.parseJSON(\"[ [\\\"123\\\" ], [\\\"456\\\" ] ]\");\n        final JSONArray array2 = (JSONArray) JSONParser.parseJSON(\"[ [\\\"456\\\" ], [\\\"123\\\" ] ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT_ORDER));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void strictOutOfOrderArraysContainingObjects() throws JSONException\n    {\n        final JSONArray array1 = (JSONArray) JSONParser\n                .parseJSON(\"[ { foo:\\\"123\\\" }, { bar:\\\"456\\\" } ]\");\n        final JSONArray array2 = (JSONArray) JSONParser\n                .parseJSON(\"[ { bar:\\\"456\\\" }, { foo:\\\"123\\\" } ]\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(array1, array2,\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT_ORDER));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void structMissingItemsInArray() throws JSONException\n    {\n        final JSONObject object1 = (JSONObject) JSONParser.parseJSON(\"{ \\\"a\\\": [ 1, 2 ] }\");\n        final JSONObject object2 = (JSONObject) JSONParser.parseJSON(\"{ \\\"a\\\": [ 1 ] }\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(object1, object2,\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT));\n        Assert.assertTrue(results.failed());\n        Assert.assertEquals(1, results.getFieldMissing().size());\n        Assert.assertEquals(0, results.getFieldUnexpected().size());\n    }\n\n    @Test\n    public void structUnexpectedItemsInArray() throws JSONException\n    {\n        final JSONObject object1 = (JSONObject) JSONParser.parseJSON(\"{ \\\"a\\\": [ 1 ] }\");\n        final JSONObject object2 = (JSONObject) JSONParser.parseJSON(\"{ \\\"a\\\": [ 1,2 ] }\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(object1, object2,\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT));\n        Assert.assertTrue(results.failed());\n        Assert.assertEquals(0, results.getFieldMissing().size());\n        Assert.assertEquals(1, results.getFieldUnexpected().size());\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/jsoncompare/DegenerateRegularExpressionJSONComparatorTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.jsoncompare;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.skyscreamer.jsonassert.JSONCompare;\nimport org.skyscreamer.jsonassert.JSONCompareMode;\nimport org.skyscreamer.jsonassert.JSONCompareResult;\nimport org.skyscreamer.jsonassert.comparator.JSONComparator;\n\n/**\n * Edge-case Test cases for the {@link RegularExpressionJSONComparator}\n *\n * @author cstaylor\n */\npublic class DegenerateRegularExpressionJSONComparatorTestCase\n{\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Test\n    public void allArraysNull() throws JSONException\n    {\n        this.thrown.expect(NullPointerException.class);\n        JSONCompare.compareJSON((JSONArray) null, (JSONArray) null, (JSONComparator) null);\n    }\n\n    @Test\n    public void allObjectsNull() throws JSONException\n    {\n        this.thrown.expect(NullPointerException.class);\n        JSONCompare.compareJSON((JSONObject) null, (JSONObject) null, (JSONComparator) null);\n    }\n\n    @Test\n    public void allStringsNull() throws JSONException\n    {\n        this.thrown.expect(NullPointerException.class);\n        JSONCompare.compareJSON((String) null, (String) null, (JSONComparator) null);\n    }\n\n    @Test\n    public void degenerateStringsTest() throws JSONException\n    {\n        this.thrown.expect(JSONException.class);\n        JSONCompare.compareJSON(\"\", \"\", (JSONComparator) null);\n    }\n\n    @Test\n    public void emptyArrays() throws JSONException\n    {\n        final JSONCompareResult results = JSONCompare.compareJSON(\"[]\", \"[]\",\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void equalFieldsEmptyComparator() throws JSONException\n    {\n        final JSONCompareResult results = JSONCompare.compareJSON(\"{}\", \"{}\",\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void equalFieldsNullComparator() throws JSONException\n    {\n        this.thrown.expect(NullPointerException.class);\n        JSONCompare.compareJSON(\"{}\", \"{}\", (JSONComparator) null);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/jsoncompare/MatchingRegularExpressionJSONComparatorTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.jsoncompare;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.skyscreamer.jsonassert.JSONCompare;\nimport org.skyscreamer.jsonassert.JSONCompareMode;\nimport org.skyscreamer.jsonassert.JSONCompareResult;\nimport org.skyscreamer.jsonassert.JSONParser;\nimport org.skyscreamer.jsonassert.comparator.JSONComparator;\n\n/**\n * Regular Expression tests for the {@link RegularExpressionJSONComparator}\n *\n * @author cstaylor\n */\npublic class MatchingRegularExpressionJSONComparatorTestCase\n{\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Test\n    public void startsWith() throws JSONException\n    {\n        final JSONObject object1 = (JSONObject) JSONParser\n                .parseJSON(\"{ mixedAtBirth:{ child:[123] }, dnaChecksOut: { child:[555] } }\");\n        final JSONObject object2 = (JSONObject) JSONParser\n                .parseJSON(\"{ mixedAtBirth:{ child:[456] }, dnaChecksOut: { child:[555] } }\");\n        final RegularExpressionJSONComparator comparator = new RegularExpressionJSONComparator(\n                JSONCompareMode.STRICT);\n        comparator.startsWith(\"mixedAtBirth.child\");\n        final JSONCompareResult results = JSONCompare.compareJSON(object1, object2, comparator);\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void testRoadNodes() throws JSONException\n    {\n        final JSONCompareResult results = JSONCompare.compareJSON(\n                \"{\\\"FeatureProto\\\":{\\\"bounding_box\\\":{\\\"max\\\":{\\\"latitude\\\":184761037,\\\"longitude\\\":-699138300},\\\"min\\\":{\\\"latitude\\\":184761037,\\\"longitude\\\":-699138300}},\\\"vendor_info\\\":{\\\"vendor_version\\\":\\\"-20160509-235548\\\",\\\"vendor_feature_id\\\":\\\"OSM_W_410348025_000000\\\",\\\"vendor_id\\\":6},\\\"protocol_version\\\":\\\"0.28.1\\\",\\\"is_active\\\":true,\\\"road_node\\\":{\\\"road_segment\\\":[{\\\"feature\\\":\\\"148693917074718963\\\",\\\"end\\\":\\\"FROM\\\"},{\\\"feature\\\":\\\"144190317701110516\\\",\\\"end\\\":\\\"TO\\\"}]},\\\"geo_type_id\\\":18,\\\"point\\\":[{\\\"latitude\\\":184761037,\\\"longitude\\\":-699138300}],\\\"feature_id\\\":\\\"148693917074718965\\\",\\\"representative_point\\\":{\\\"latitude\\\":184761037,\\\"longitude\\\":-699138300},\\\"candidate\\\":true,\\\"geo_ontology_id\\\":11966,\\\"feature_type\\\":[\\\"ROAD_NODE\\\"],\\\"iso_country_code\\\":\\\"DOM\\\",\\\"history\\\":[{\\\"source_id\\\":1745,\\\"edit_type\\\":\\\"CREATION\\\",\\\"source_key\\\":\\\"OSM_W_410348025_000000\\\",\\\"source\\\":\\\"1745/6/OSM_W_410348025_000000\\\",\\\"vendor_id\\\":6,\\\"timestamp\\\":1462887123}],\\\"time_zone_reference\\\":[{\\\"tz_database_name\\\":[\\\"Atlantic Standard Time\\\"],\\\"time_zone\\\":{\\\"feature\\\":\\\"144190317670694970\\\"}}],\\\"version\\\":\\\"a364fc02-16b3-11e6-800c-56847afe9799\\\"}}\",\n                \"{\\\"FeatureProto\\\":{\\\"bounding_box\\\":{\\\"max\\\":{\\\"latitude\\\":184761037,\\\"longitude\\\":-699138300},\\\"min\\\":{\\\"latitude\\\":184761037,\\\"longitude\\\":-699138300}},\\\"is_active\\\":true,\\\"vendor_info\\\":{\\\"vendor_version\\\":\\\"-20160512-184524\\\",\\\"vendor_id\\\":6,\\\"vendor_feature_id\\\":\\\"OSM_W_410348025_000000\\\"},\\\"protocol_version\\\":\\\"0.28.1\\\",\\\"road_node\\\":{\\\"road_segment\\\":[{\\\"feature\\\":\\\"148694183946749497\\\",\\\"end\\\":\\\"FROM\\\"},{\\\"feature\\\":\\\"144190584576286789\\\",\\\"end\\\":\\\"TO\\\"}]},\\\"geo_type_id\\\":18,\\\"feature_id\\\":\\\"148694183946749499\\\",\\\"point\\\":[{\\\"latitude\\\":184761037,\\\"longitude\\\":-699138300}],\\\"representative_point\\\":{\\\"latitude\\\":184761037,\\\"longitude\\\":-699138300},\\\"candidate\\\":true,\\\"geo_ontology_id\\\":11966,\\\"feature_type\\\":[\\\"ROAD_NODE\\\"],\\\"iso_country_code\\\":\\\"DOM\\\",\\\"history\\\":[{\\\"source_id\\\":1745,\\\"edit_type\\\":\\\"CREATION\\\",\\\"source_key\\\":\\\"OSM_W_410348025_000000\\\",\\\"source\\\":\\\"1745/6/OSM_W_410348025_000000\\\",\\\"vendor_id\\\":6,\\\"timestamp\\\":1463141634}],\\\"time_zone_reference\\\":[{\\\"tz_database_name\\\":[\\\"Atlantic Standard Time\\\"],\\\"time_zone\\\":{\\\"feature\\\":\\\"144190584546918458\\\"}}],\\\"version\\\":\\\"36902a8b-1904-11e6-854c-56847afe9799\\\"}}\",\n                createRoadsComparator());\n        Assert.assertFalse(results.failed());\n    }\n\n    private JSONComparator createRoadsComparator()\n    {\n        final RegularExpressionJSONComparator returnValue = new RegularExpressionJSONComparator(\n                JSONCompareMode.STRICT);\n        returnValue.endsWith(\".timestamp\");\n        returnValue.endsWith(\".feature\");\n        returnValue.endsWith(\".feature_id\");\n        returnValue.exact(\"FeatureProto.vendor_info.vendor_version\");\n        returnValue.exact(\"FeatureProto.version\");\n        return returnValue;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/jsoncompare/ObjectsRegularExpressionJSONComparatorTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.jsoncompare;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.skyscreamer.jsonassert.JSONCompare;\nimport org.skyscreamer.jsonassert.JSONCompareMode;\nimport org.skyscreamer.jsonassert.JSONCompareResult;\nimport org.skyscreamer.jsonassert.JSONParser;\n\n/**\n * JSONObject-specific Test cases for the {@link RegularExpressionJSONComparator}\n *\n * @author cstaylor\n */\npublic class ObjectsRegularExpressionJSONComparatorTestCase\n{\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Test\n    public void extensibleObjectTest() throws JSONException\n    {\n        final JSONObject obj1 = (JSONObject) JSONParser.parseJSON(\"{ foo:\\\"abc\\\" }\");\n        final JSONObject obj2 = (JSONObject) JSONParser.parseJSON(\"{ foo:\\\"abc\\\", bar: \\\"def\\\" }\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(obj1, obj2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertFalse(results.failed());\n    }\n\n    @Test\n    public void nonExtensibleObjectTest() throws JSONException\n    {\n        final JSONObject obj1 = (JSONObject) JSONParser.parseJSON(\"{ foo:\\\"abc\\\" }\");\n        final JSONObject obj2 = (JSONObject) JSONParser.parseJSON(\"{ foo:\\\"abc\\\", bar: \\\"def\\\" }\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(obj1, obj2,\n                new RegularExpressionJSONComparator(JSONCompareMode.STRICT));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void objectsDifferentTypes() throws JSONException\n    {\n        final JSONObject obj1 = (JSONObject) JSONParser.parseJSON(\"{ foo:\\\"123\\\" }\");\n        final JSONObject obj2 = (JSONObject) JSONParser.parseJSON(\"{ foo:123 }\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(obj1, obj2,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertTrue(results.failed());\n    }\n\n    @Test\n    public void reverseObjectsDifferentTypes() throws JSONException\n    {\n        final JSONObject obj1 = (JSONObject) JSONParser.parseJSON(\"{ foo:\\\"123\\\" }\");\n        final JSONObject obj2 = (JSONObject) JSONParser.parseJSON(\"{ foo:123 }\");\n\n        final JSONCompareResult results = JSONCompare.compareJSON(obj2, obj1,\n                new RegularExpressionJSONComparator(JSONCompareMode.LENIENT));\n        Assert.assertTrue(results.failed());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/jsoncompare/RegularExpressionJSONComparatorTestSuite.java",
    "content": "package org.openstreetmap.atlas.utilities.jsoncompare;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * Test Suite for all {@link RegularExpressionJSONComparator} Test Cases: makes coverage checks\n * easier to do\n *\n * @author cstaylor\n */\n@RunWith(Suite.class)\n@SuiteClasses({ ArraysRegularExpressionJSONComparatorTestCase.class,\n        DegenerateRegularExpressionJSONComparatorTestCase.class,\n        MatchingRegularExpressionJSONComparatorTestCase.class,\n        ObjectsRegularExpressionJSONComparatorTestCase.class, })\npublic class RegularExpressionJSONComparatorTestSuite\n{\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/maps/LargeMapTest.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport java.io.ObjectOutputStream;\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.File;\n\n/**\n * @author matthieun\n */\npublic class LargeMapTest\n{\n    public static void main(final String[] args)\n    {\n        new LargeMapTest().largeSerialization();\n    }\n\n    public void largeSerialization()\n    {\n        final int size = 300_000_000;\n        final LongToLongMap map = new LongToLongMap(\"testMap\", size, size / 10, size, size, size,\n                size);\n        final File writableResource = new File(\n                System.getProperty(\"user.home\") + \"/projects/data/unitTest/LongToLongMap.dat\");\n        final Random random = new Random();\n        for (int i = 0; i < size; i++)\n        {\n            map.put((long) i, random.nextLong());\n        }\n        ObjectOutputStream out = null;\n        try\n        {\n            out = new ObjectOutputStream(writableResource.write());\n            out.writeObject(map);\n            Streams.close(out);\n        }\n        catch (final Exception e)\n        {\n            Streams.close(out);\n            throw new CoreException(\"Could not save to {}\", e, writableResource);\n        }\n    }\n\n    @Test\n    public void testMap()\n    {\n        final LongToLongMap map = new LongToLongMap(\"TestMap\", 100, 10, 2, 15, 2, 15);\n        for (int i = 0; i < 100; i++)\n        {\n            map.put((long) i, i * 1000L);\n        }\n        try\n        {\n            map.put(101L, 0L);\n            Assert.fail(\"Cannot go over array size\");\n        }\n        catch (final CoreException e)\n        {\n            // OK\n        }\n        // This works as it is just replacing an item\n        map.put(0L, 9999999999L);\n        map.forEach(key -> System.out.print(key + \"->\" + map.get(key) + \", \"));\n        System.out.println();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/maps/MultiMapTest.java",
    "content": "package org.openstreetmap.atlas.utilities.maps;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.Rectangle;\nimport org.openstreetmap.atlas.streaming.Streams;\nimport org.openstreetmap.atlas.streaming.resource.ByteArrayResource;\nimport org.openstreetmap.atlas.streaming.resource.WritableResource;\n\n/**\n * @author matthieun\n */\npublic class MultiMapTest\n{\n    public static final MultiMap<Polygon, Polygon> getMultiMap()\n    {\n        final MultiMap<Polygon, Polygon> map = new MultiMap<>();\n        map.add(Polygon.SILICON_VALLEY, Rectangle.TEST_RECTANGLE_2);\n        map.add(Polygon.SILICON_VALLEY, Rectangle.TEST_RECTANGLE);\n        return map;\n    }\n\n    @Test\n    public void testSerialization() throws IOException, ClassNotFoundException\n    {\n        final MultiMap<Polygon, Polygon> map = getMultiMap();\n        final WritableResource out = new ByteArrayResource();\n        final ObjectOutputStream outStream = new ObjectOutputStream(out.write());\n        outStream.writeObject(map);\n        Streams.close(outStream);\n\n        try (ObjectInputStream inStream = new ObjectInputStream(out.read()))\n        {\n            @SuppressWarnings(\"unchecked\")\n            final MultiMap<Polygon, Polygon> result = (MultiMap<Polygon, Polygon>) inStream\n                    .readObject();\n            Assert.assertEquals(map, result);\n        }\n        catch (final Exception e)\n        {\n            throw e;\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/matching/NameMatcherTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.matching;\n\nimport java.util.stream.Stream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Tests {@link NameMatcher}\n *\n * @author brian_l_davis\n */\npublic class NameMatcherTestCase\n{\n    private final String[] data = new String[] { \"Main Street\", \"main street\", \"Rain Street\",\n            \"Second Street\", \"Second Avenue\", null };\n    private final String source = \"Main Street\";\n\n    @Test\n    public void testExactMatch()\n    {\n        final String[] results = Stream.of(this.data)\n                .filter(new NameMatcher(this.source).matchExactly()).toArray(String[]::new);\n        Assert.assertArrayEquals(new String[] { \"Main Street\" }, results);\n    }\n\n    @Test\n    public void testExactMatchWithNulls()\n    {\n        final String[] results = Stream.of(this.data)\n                .filter(new NameMatcher(this.source).matchExactly().matchNulls())\n                .toArray(String[]::new);\n        Assert.assertArrayEquals(new String[] { \"Main Street\", null }, results);\n    }\n\n    @Test\n    public void testFuzzyMatch()\n    {\n        final String[] results = Stream.of(this.data)\n                .filter(new NameMatcher(this.source).matchSimilar()).toArray(String[]::new);\n        Assert.assertArrayEquals(new String[] { \"Main Street\", \"main street\", \"Rain Street\" },\n                results);\n    }\n\n    @Test\n    public void testFuzzyMatchWithNulls()\n    {\n        final String[] results = Stream.of(this.data)\n                .filter(new NameMatcher(this.source).matchSimilar().matchNulls())\n                .toArray(String[]::new);\n        Assert.assertArrayEquals(new String[] { \"Main Street\", \"main street\", \"Rain Street\", null },\n                results);\n    }\n\n    @Test\n    public void testReallyFuzzyMatch()\n    {\n        final String[] results = Stream.of(this.data)\n                .filter(new NameMatcher(this.source).matchSimilar(10)).toArray(String[]::new);\n        Assert.assertArrayEquals(\n                new String[] { \"Main Street\", \"main street\", \"Rain Street\", \"Second Street\" },\n                results);\n    }\n\n    @Test\n    public void testUncasedMatch()\n    {\n        final String[] results = Stream.of(this.data).filter(new NameMatcher(this.source))\n                .toArray(String[]::new);\n        Assert.assertArrayEquals(new String[] { \"Main Street\", \"main street\" }, results);\n    }\n\n    @Test\n    public void testUncasedMatchWithNulls()\n    {\n        final String[] results = Stream.of(this.data)\n                .filter(new NameMatcher(this.source).matchNulls()).toArray(String[]::new);\n        Assert.assertArrayEquals(new String[] { \"Main Street\", \"main street\", null }, results);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/random/RandomTextGeneratorTest.java",
    "content": "package org.openstreetmap.atlas.utilities.random;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.nio.charset.Charset;\n\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class RandomTextGeneratorTest\n{\n    @Test\n    public void testReader() throws IOException\n    {\n        final RandomTextGenerator generator = new RandomTextGenerator();\n        final BufferedReader reader = generator.infiniteReader(\"\\n\");\n        String line = \"\";\n        int counter = 0;\n        while ((line = reader.readLine()) != null && counter < 10)\n        {\n            counter++;\n            System.out.println(line);\n        }\n        reader.close();\n        try\n        {\n            reader.readLine();\n        }\n        catch (final IOException e)\n        {\n            // Test passes :)\n            return;\n        }\n        throw new RuntimeException(\"Reader was closed but kept reading.\");\n    }\n\n    @Test\n    public void testStream() throws IOException\n    {\n        final RandomTextGenerator generator = new RandomTextGenerator();\n        final BufferedReader reader = new BufferedReader(\n                new InputStreamReader(generator.infiniteStream(), Charset.forName(\"UTF-8\")));\n        String line = \"\";\n        int counter = 0;\n        while ((line = reader.readLine()) != null && counter < 10)\n        {\n            counter++;\n            System.out.println(line);\n        }\n        reader.close();\n        try\n        {\n            reader.readLine();\n        }\n        catch (final IOException e)\n        {\n            // Test passes :)\n            return;\n        }\n        throw new RuntimeException(\"Reader was closed but kept reading.\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/runtime/BooleanFlagTest.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.exception.CoreException;\n\n/**\n * JUnit test case for testing the following situations:\n * <ul>\n * <li>testBooleanOptionalFlagFound - flag is optional and specified by the user (TRUE)</li>\n * <li>testBooleanOptionalFlagMissing - flag is optional and not specified by the user (FALSE)</li>\n * <li>testBooleanRequiredFlagFound - flag is required and specified by the user (TRUE)</li>\n * <li>testBooleanRequiredFlagFound - flag is required and not specified by the user (COREEXCEPTION)\n * </li>\n * </ul>\n *\n * @author cstaylor\n */\npublic class BooleanFlagTest\n{\n    /**\n     * Command object for verifying the optional flag tests\n     *\n     * @author cstaylor\n     */\n    static final class OptionalBooleanFlags extends Command\n    {\n        private static final Flag VERBOSE_FLAG = new Flag(\"v\", \"The Verbose Flag\");\n\n        private boolean success;\n\n        public boolean isSuccessful()\n        {\n            return this.success;\n        }\n\n        @Override\n        protected int onRun(final CommandMap command)\n        {\n            this.success = (Boolean) command.get(VERBOSE_FLAG);\n            return 0;\n        }\n\n        @Override\n        protected SwitchList switches()\n        {\n            return new SwitchList().with(VERBOSE_FLAG);\n        }\n    }\n\n    /**\n     * Command object for verifying the required flag tests\n     *\n     * @author cstaylor\n     */\n\n    static final class RequiredBooleanFlags extends Command\n    {\n        private static final Flag VERBOSE_FLAG = new Flag(\"v\", \"The Verbose Flag\",\n                Optionality.REQUIRED);\n\n        private boolean success;\n\n        public boolean isSuccessful()\n        {\n            return this.success;\n        }\n\n        @Override\n        protected int onRun(final CommandMap command)\n        {\n            this.success = (Boolean) command.get(VERBOSE_FLAG);\n            return 0;\n        }\n\n        @Override\n        protected SwitchList switches()\n        {\n            return new SwitchList().with(VERBOSE_FLAG);\n        }\n    }\n\n    @Rule\n    public ExpectedException expected = ExpectedException.none();\n\n    @Test\n    public void testBooleanOptionalFlagFound()\n    {\n        final OptionalBooleanFlags flags = new OptionalBooleanFlags();\n        flags.runWithoutQuitting(\"-v\");\n        Assert.assertTrue(flags.isSuccessful());\n    }\n\n    @Test\n    public void testBooleanOptionalFlagMissing()\n    {\n        final OptionalBooleanFlags flags = new OptionalBooleanFlags();\n        flags.runWithoutQuitting(\"\");\n        Assert.assertFalse(flags.isSuccessful());\n    }\n\n    @Test\n    public void testBooleanRequiredFlagFound()\n    {\n        final RequiredBooleanFlags flags = new RequiredBooleanFlags();\n        flags.runWithoutQuitting(\"-v\");\n        Assert.assertTrue(flags.isSuccessful());\n    }\n\n    @Test\n    public void testBooleanRequiredFlagMissing()\n    {\n        this.expected.expect(CoreException.class);\n        final RequiredBooleanFlags flags = new RequiredBooleanFlags();\n        flags.runWithoutQuitting(\"\");\n        Assert.fail(\"Shouldn't get here\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/runtime/FlagNameParsingRegressionTest.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Optionality;\nimport org.openstreetmap.atlas.utilities.runtime.Command.Switch;\n\n/**\n * This test case verifies the switch name parsing bug CST introduced on 2015-12-11.\n *\n * @author cstaylor\n */\npublic class FlagNameParsingRegressionTest\n{\n    /**\n     * Sample command implementation for verifying unknown switches and their names\n     *\n     * @author cstaylor\n     */\n    static final class VerifySwitchNames extends Command\n    {\n        private boolean success;\n\n        public boolean isSuccessful()\n        {\n            return this.success;\n        }\n\n        @Override\n        protected int onRun(final CommandMap command)\n        {\n            this.success = command.containsKey(\"theunknownswitch\");\n            this.success = this.success && \"yup\".equals(command.get(KNOWN_SWITCH));\n            return 0;\n        }\n\n        @Override\n        protected SwitchList switches()\n        {\n            return new SwitchList().with(KNOWN_SWITCH);\n        }\n    }\n\n    private static final Switch<String> KNOWN_SWITCH = new Switch<>(\"theknownswitch\",\n            \"We should find this switch\", path -> path, Optionality.OPTIONAL);\n\n    @Test\n    public void testUnknownFlag()\n    {\n        final VerifySwitchNames flags = new VerifySwitchNames();\n        flags.runWithoutQuitting(\"-theunknownswitch=nope\", \"-theknownswitch=yup\");\n        Assert.assertTrue(flags.isSuccessful());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/runtime/RetryTest.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Test for {@link Retry}\n *\n * @author matthieun\n */\npublic class RetryTest\n{\n    /**\n     * @author matthieun\n     */\n    private static class NeedsAction implements Runnable\n    {\n        private boolean blocked = true;\n\n        public boolean isBlocked()\n        {\n            return this.blocked;\n        }\n\n        @Override\n        public void run()\n        {\n            if (this.blocked)\n            {\n                throw new RuntimeException(\"Blocked\");\n            }\n            else\n            {\n                logger.info(\"Success, I have been unblocked!\");\n            }\n        }\n\n        public void unBlock()\n        {\n            this.blocked = false;\n        }\n    }\n\n    /**\n     * @author matthieun\n     */\n    private static class WithCounter implements Runnable\n    {\n        private int counter = 0;\n\n        public int getCounter()\n        {\n            return this.counter;\n        }\n\n        @Override\n        public void run()\n        {\n            if (this.counter++ < 4)\n            {\n                throw new RuntimeException();\n            }\n            else\n            {\n                logger.info(\"Success\");\n            }\n        }\n    }\n\n    private static final Logger logger = LoggerFactory.getLogger(RetryTest.class);\n\n    private final NeedsAction needAction = new NeedsAction();\n    private final WithCounter withCounter = new WithCounter();\n\n    @Test\n    public void testHandleException()\n    {\n        final Retry retry = new Retry(5, Duration.milliseconds(1)).withQuadratic(true);\n        Assert.assertTrue(retry.isQuadratic());\n        retry.run(this.withCounter);\n        Assert.assertEquals(5, this.withCounter.getCounter());\n    }\n\n    @Test\n    public void testRunBeforeRetry()\n    {\n        final Retry retry = new Retry(1, Duration.milliseconds(1)).withQuiet(true);\n        Assert.assertTrue(retry.isQuiet());\n        retry.run(this.needAction, this.needAction::unBlock);\n        Assert.assertFalse(this.needAction.isBlocked());\n    }\n\n    @Test(expected = RuntimeException.class)\n    public void testThrowException()\n    {\n        final Retry retry = new Retry(2, Duration.milliseconds(1));\n        retry.run(this.withCounter);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/runtime/RunScriptTest.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.openstreetmap.atlas.utilities.collections.StringList;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class RunScriptTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(RunScriptTest.class);\n    private static final Supplier<SingleLineMonitor> JAVA_VERSION_MONITOR_CREATOR = () ->\n    {\n        return new SingleLineMonitor()\n        {\n            @Override\n            protected Optional<String> parseResult(final String line)\n            {\n                if (line.contains(\" version\"))\n                {\n                    final StringList spaceSplit = StringList.split(line, \" \");\n                    for (final String item : spaceSplit)\n                    {\n                        if (item.startsWith(\"\\\"\"))\n                        {\n                            return Optional.of(item.replace(\"\\\"\", \"\"));\n                        }\n                    }\n                }\n                return Optional.empty();\n            }\n        };\n    };\n\n    @Test\n    public void testRunScript()\n    {\n        final SingleLineMonitor javaVersionMonitor = JAVA_VERSION_MONITOR_CREATOR.get();\n        RunScript.run(new String[] { \"java\", \"-version\" }, Iterables.toList(javaVersionMonitor));\n        // RunScript.run(\"java -version\");\n        logger.info(\"Parsed Java Version: {}\", javaVersionMonitor.getResult());\n        Assert.assertTrue(javaVersionMonitor.getResult().isPresent());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/runtime/system/SystemInfoTest.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime.system;\n\nimport org.junit.Test;\n\n/**\n * @author matthieun\n */\npublic class SystemInfoTest\n{\n    @Test\n    public void testPrint()\n    {\n        SystemInfo.printSystemInfo();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/runtime/system/memory/MemoryTest.java",
    "content": "package org.openstreetmap.atlas.utilities.runtime.system.memory;\n\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\n/**\n * @author matthieun\n */\npublic class MemoryTest\n{\n    @Rule\n    public final ExpectedException exception = ExpectedException.none();\n\n    @Test\n    public void testConversion()\n    {\n        for (int counter = 0; counter < 100; counter++)\n        {\n            long bytes = new Random().nextLong();\n            if (bytes < 0)\n            {\n                bytes = -bytes;\n            }\n            runConversionTest(bytes);\n        }\n    }\n\n    @Test\n    public void testPrint()\n    {\n        Memory.printCurrentMemory();\n    }\n\n    private void assertConversion(final long bytes, final double kiloBytes, final double megaBytes,\n            final double gigaBytes, final double teraBytes, final Memory memory)\n    {\n        Assert.assertEquals(0.0, Math.abs(bytes - memory.asBytes()) / bytes, 5);\n        Assert.assertEquals(kiloBytes, memory.asKiloBytes(), 5);\n        Assert.assertEquals(megaBytes, memory.asMegaBytes(), 5);\n        Assert.assertEquals(gigaBytes, memory.asGigaBytes(), 5);\n        Assert.assertEquals(teraBytes, memory.asTeraBytes(), 5);\n    }\n\n    private void runConversionTest(final long bytes)\n    {\n        final double kiloBytes = bytes / 1024.0;\n        final double megaBytes = kiloBytes / 1024.0;\n        final double gigaBytes = megaBytes / 1024.0;\n        final double teraBytes = gigaBytes / 1024.0;\n\n        Memory memory = Memory.bytes(bytes);\n        assertConversion(bytes, kiloBytes, megaBytes, gigaBytes, teraBytes, memory);\n\n        memory = Memory.kiloBytes(kiloBytes);\n        assertConversion(bytes, kiloBytes, megaBytes, gigaBytes, teraBytes, memory);\n\n        memory = Memory.megaBytes(megaBytes);\n        assertConversion(bytes, kiloBytes, megaBytes, gigaBytes, teraBytes, memory);\n\n        memory = Memory.gigaBytes(gigaBytes);\n        assertConversion(bytes, kiloBytes, megaBytes, gigaBytes, teraBytes, memory);\n\n        memory = Memory.teraBytes(teraBytes);\n        assertConversion(bytes, kiloBytes, megaBytes, gigaBytes, teraBytes, memory);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/scalars/AngleTest.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Test {@link Angle}s\n *\n * @author matthieun\n * @author mgostintsev\n */\npublic class AngleTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(AngleTest.class);\n\n    @Test\n    public void testAddSubtract()\n    {\n        logger.info(\"Small: \" + Angle.dm7(2320));\n        final Angle ninety = Angle.degrees(90);\n        logger.info(\"Ninety degrees: \" + ninety);\n        final Angle oneEighty = Angle.degrees(180);\n        logger.info(\"One hundred and eighty degrees: \" + oneEighty);\n        final Angle twoSeventy = Angle.degrees(270);\n        logger.info(\"Two hundred and seventy degrees: \" + twoSeventy);\n        Assert.assertEquals(ninety, twoSeventy.subtract(oneEighty));\n        Assert.assertEquals(oneEighty, twoSeventy.subtract(ninety));\n        Assert.assertEquals(twoSeventy, ninety.subtract(oneEighty));\n        Assert.assertEquals(ninety, twoSeventy.add(oneEighty));\n        Assert.assertEquals(twoSeventy, ninety.add(oneEighty));\n    }\n\n    @Test\n    public void testConversion()\n    {\n        final Angle value = Angle.dm7(325_000_020);\n        Assert.assertEquals(32.500002, value.asDegrees(), 0);\n        Assert.assertEquals(0.5672320418047421, value.asRadians(), 1e-10);\n    }\n\n    @Test\n    public void testDifference()\n    {\n        Assert.assertEquals(Angle.MAXIMUM, Angle.degrees(-180).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(-180).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(-180).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(-180).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(-180).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.degrees(0), Angle.degrees(-180).difference(Angle.degrees(180)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(-180).difference(Angle.degrees(200)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(-180).difference(Angle.degrees(250)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(-180).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(-180).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(-180).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.MAXIMUM, Angle.degrees(-180).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(-180).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(-90).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(-90).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(-90).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.MAXIMUM, Angle.degrees(-90).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(-90).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(-90).difference(Angle.degrees(180)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(-90).difference(Angle.degrees(200)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(-90).difference(Angle.degrees(250)));\n        Assert.assertEquals(Angle.degrees(0), Angle.degrees(-90).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(-90).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(-90).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(-90).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(-90).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.degrees(0), Angle.degrees(0).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(0).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(0).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(0).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(0).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.MAXIMUM, Angle.degrees(0).difference(Angle.degrees(180)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(0).difference(Angle.degrees(200)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(0).difference(Angle.degrees(250)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(0).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(0).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(0).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.degrees(0), Angle.degrees(0).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(0).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(90).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(90).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(90).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.degrees(0), Angle.degrees(90).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(90).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(90).difference(Angle.degrees(180)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(90).difference(Angle.degrees(200)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(90).difference(Angle.degrees(250)));\n        Assert.assertEquals(Angle.MAXIMUM, Angle.degrees(90).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(90).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(90).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(90).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(90).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.MAXIMUM, Angle.degrees(180).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(180).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(180).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(180).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(180).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.degrees(0), Angle.degrees(180).difference(Angle.degrees(180)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(180).difference(Angle.degrees(200)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(180).difference(Angle.degrees(250)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(180).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(180).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(180).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.MAXIMUM, Angle.degrees(180).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(180).difference(Angle.degrees(380)));\n\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(270).difference(Angle.degrees(0)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(270).difference(Angle.degrees(20)));\n        Assert.assertEquals(Angle.degrees(160), Angle.degrees(270).difference(Angle.degrees(70)));\n        Assert.assertEquals(Angle.MAXIMUM, Angle.degrees(270).difference(Angle.degrees(90)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(270).difference(Angle.degrees(160)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(270).difference(Angle.degrees(180)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(270).difference(Angle.degrees(200)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(270).difference(Angle.degrees(250)));\n        Assert.assertEquals(Angle.degrees(0), Angle.degrees(270).difference(Angle.degrees(270)));\n        Assert.assertEquals(Angle.degrees(20), Angle.degrees(270).difference(Angle.degrees(290)));\n        Assert.assertEquals(Angle.degrees(70), Angle.degrees(270).difference(Angle.degrees(340)));\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(270).difference(Angle.degrees(360)));\n        Assert.assertEquals(Angle.degrees(110), Angle.degrees(270).difference(Angle.degrees(380)));\n    }\n\n    @Test\n    public void testDistanceOnEarth()\n    {\n        final Angle value = Angle.dm7(Short.MAX_VALUE);\n        Assert.assertEquals(364.352, value.onEarth().asMeters(), 1e-3);\n    }\n\n    @Test\n    public void testPositiveAngle()\n    {\n        final Angle zero = Angle.degrees(0);\n        Assert.assertEquals(zero, zero.asPositiveAngle());\n        Assert.assertEquals(Angle.degrees(10), Angle.degrees(-10).asPositiveAngle());\n        Assert.assertEquals(Angle.MAXIMUM, Angle.MINIMUM.asPositiveAngle());\n        Assert.assertEquals(Angle.MAXIMUM, Angle.MAXIMUM.asPositiveAngle());\n    }\n\n    @Test\n    public void testPositiveRadians()\n    {\n        final Angle value = Angle.MINIMUM;\n        Assert.assertEquals(-Math.PI, value.asRadians(), 1e-9);\n        Assert.assertEquals(Math.PI, value.asPositiveRadians(), 1e-9);\n    }\n\n    @Test\n    public void testReverse()\n    {\n        System.out.println(Angle.MAXIMUM);\n        Assert.assertEquals(Angle.degrees(0), Angle.MINIMUM.reverse());\n        Assert.assertEquals(Angle.degrees((double) -1 / Angle.DM7_PER_DEGREE),\n                Angle.MAXIMUM.reverse());\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(-90).reverse());\n        Assert.assertEquals(Angle.degrees(-90), Angle.degrees(90).reverse());\n        Assert.assertEquals(Angle.degrees(90), Angle.degrees(-450).reverse());\n    }\n\n    @Test\n    public void testRollingValue()\n    {\n        final Angle value = Angle.dm7(3_600_000_050L);\n        Assert.assertEquals(value, Angle.dm7(50));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/scalars/DistanceTest.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\n\n/**\n * @author tony\n * @author matthieun\n */\npublic class DistanceTest\n{\n    @Test\n    public void testAdd()\n    {\n        Assert.assertEquals(Distance.miles(10), Distance.miles(2).add(Distance.miles(8)));\n        Assert.assertEquals(Distance.meters(100),\n                Distance.millimeters(5000).add(Distance.meters(95)));\n    }\n\n    @Test\n    public void testConversion()\n    {\n        final Distance tenMiles = Distance.miles(10);\n        Assert.assertEquals(16093440.0, tenMiles.asMillimeters(), 5);\n        Assert.assertEquals(52800.0, tenMiles.asFeet(), 0);\n        Assert.assertEquals(16.09344, tenMiles.asKilometers(), 0);\n        Assert.assertEquals(8.6897624, tenMiles.asNauticalMiles(), 1e-7);\n\n        Assert.assertEquals(Distance.meters(tenMiles.asMeters()), tenMiles);\n        Assert.assertEquals(Distance.miles(tenMiles.asMiles()), tenMiles);\n        Assert.assertEquals(Distance.millimeters(tenMiles.asMillimeters()), tenMiles);\n        Assert.assertEquals(Distance.nauticalMiles(tenMiles.asNauticalMiles()), tenMiles);\n    }\n\n    @Test\n    public void testDistanceBetweenLocations()\n    {\n        final Location location1 = Location.forString(\"37.336900,-122.005414\");\n        final Location location2 = Location.forString(\"37.332758,-122.005409\");\n\n        final Distance approximation1 = location1.equirectangularDistanceTo(location2);\n        final Distance approximation2 = location1.haversineDistanceTo(location2);\n\n        Assert.assertTrue(Distance.FIFTEEN_HUNDRED_FEET.difference(approximation1)\n                .isLessThan(Distance.meters(10)));\n        Assert.assertTrue(Distance.FIFTEEN_HUNDRED_FEET.difference(approximation2)\n                .isLessThan(Distance.meters(10)));\n\n        // So the two methods have basically no difference, should choose equirectangular\n        // approximation for most cases\n        Assert.assertTrue(approximation1.difference(approximation2).isLessThan(Distance.meters(1)));\n    }\n\n    @Test\n    public void testDistancePerDegreeLongitudeAt()\n    {\n        Assert.assertEquals(111195L, Math.round(\n                Distance.distancePerDegreeLongitudeAt(Location.forWkt(\"POINT(0 0)\")).asMeters()));\n        Assert.assertEquals(1941L, Math.round(\n                Distance.distancePerDegreeLongitudeAt(Location.forWkt(\"POINT(0 -89)\")).asMeters()));\n        Assert.assertEquals(1941L, Math.round(\n                Distance.distancePerDegreeLongitudeAt(Location.forWkt(\"POINT(0 89)\")).asMeters()));\n        Assert.assertEquals(0L, Math.round(\n                Distance.distancePerDegreeLongitudeAt(Location.forWkt(\"POINT(0 -90)\")).asMeters()));\n        Assert.assertEquals(0L, Math.round(\n                Distance.distancePerDegreeLongitudeAt(Location.forWkt(\"POINT(0 90)\")).asMeters()));\n    }\n\n    @Test\n    public void testEquals()\n    {\n        Assert.assertEquals(Distance.meters(1000.001), Distance.meters(1000.001));\n        Assert.assertNotEquals(Distance.meters(1000.001), Distance.meters(1000.002));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/scalars/DoubleCounterTest.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Test class for double counter\n *\n * @author jklamer\n */\npublic class DoubleCounterTest\n{\n    @Test\n    public void testDoubleCounter()\n    {\n        final DoubleCounter myCount = new DoubleCounter(43.2);\n\n        myCount.add(76.5);\n        Assert.assertEquals(119.7, myCount.getValue(), 0);\n\n        myCount.reset();\n        final DoubleCounter otherCount = new DoubleCounter();\n\n        Assert.assertEquals(myCount.getValue(), otherCount.getValue(), 0);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/scalars/DurationTest.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.time.Time;\n\n/**\n * @author matthieun\n */\npublic class DurationTest\n{\n    @Test\n    public void testConversion()\n    {\n        Duration duration = Duration.seconds(60);\n        Assert.assertEquals(1.0, duration.asMinutes(), 2);\n\n        duration = Duration.minutes(2);\n        Assert.assertEquals(120000L, duration.asMilliseconds());\n\n        duration = Duration.minutes(2);\n        Assert.assertEquals(120000L, duration.asMilliseconds());\n    }\n\n    @Test\n    public void testEquals()\n    {\n        Assert.assertEquals(Duration.seconds(3600), Duration.hours(1));\n        Assert.assertNotEquals(Duration.seconds(1.001), Duration.seconds(1.002));\n    }\n\n    @Test\n    public void testLowestAndHighest()\n    {\n        final Duration lowest = Duration.seconds(5);\n        final Duration highest = Duration.seconds(10);\n        Assert.assertEquals(lowest, lowest.lowest(highest));\n        Assert.assertEquals(lowest, highest.lowest(lowest));\n        Assert.assertEquals(highest, highest.lowest(null));\n        Assert.assertEquals(highest, lowest.highest(highest));\n        Assert.assertEquals(highest, highest.highest(lowest));\n        Assert.assertEquals(lowest, lowest.highest(null));\n    }\n\n    @Test\n    public void testSleep()\n    {\n        final Duration sleepTime = Duration.milliseconds(10);\n        final Time start = Time.now();\n        sleepTime.sleep();\n        final Duration elapsed = start.elapsedSince();\n        Assert.assertTrue(elapsed.isMoreThanOrEqualsTo(sleepTime));\n    }\n\n    @Test\n    public void testSort()\n    {\n        final Duration duration1 = Duration.hours(2.9999999);\n        final Duration duration2 = Duration.hours(3);\n        final Duration duration3 = Duration.ONE_DAY;\n\n        final List<Duration> durationSorted = Arrays.asList(duration1, duration2, duration3);\n        final List<Duration> durationUnsorted = Arrays.asList(duration1, duration3, duration2);\n\n        // get max min\n        Assert.assertEquals(duration3, durationUnsorted.stream().max(Duration::compareTo).get());\n        Assert.assertEquals(duration1, durationUnsorted.stream().min(Duration::compareTo).get());\n\n        // sort and test\n        durationUnsorted.sort(Duration::compareTo);\n        Assert.assertEquals(durationSorted, durationUnsorted);\n    }\n\n    @Test\n    public void testTimes()\n    {\n        final Duration duration = Duration.ONE_SECOND;\n        Assert.assertEquals(2.0, duration.times(2.0).asSeconds(), 3);\n        Assert.assertEquals(5000.0, duration.times(5000.0).asSeconds(), 3);\n    }\n\n    @Test(expected = CoreException.class)\n    public void testTimesException()\n    {\n        Duration.ONE_SECOND.times(-2.0);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/scalars/RatioTest.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\nimport org.junit.Test;\n\n/**\n * @author tony\n * @author matthieun\n */\npublic class RatioTest\n{\n    private static final double DELTA = 0.00001;\n\n    @Test\n    public void testPercentage()\n    {\n        final Ratio ratio = Ratio.percentage(15.51236632);\n        assertTrue(ratio.isGreaterThan(Ratio.percentage(15)));\n        assertEquals(\"15.51 %\", ratio.toString());\n        assertEquals(\"15.51237 %\", ratio.toString(5));\n        assertEquals(15.51236632, ratio.asPercentage(), DELTA);\n        assertEquals(0.1551236632, ratio.asRatio(), DELTA);\n\n        try\n        {\n            Ratio.percentage(101);\n            fail();\n        }\n        catch (final Exception e)\n        {\n            // Expected\n        }\n\n        try\n        {\n            Ratio.percentage(-0.00001);\n            fail();\n        }\n        catch (final Exception e)\n        {\n            // Expected\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/scalars/SpeedTest.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author tony\n */\npublic class SpeedTest\n{\n    @Test\n    public void testCompare()\n    {\n        Assert.assertTrue(Speed.milesPerHour(10).isFasterThan(Speed.milesPerHour(9.999)));\n        Assert.assertTrue(Speed.kilometersPerHour(100).isSlowerThan(Speed.kilometersPerHour(101)));\n        Assert.assertTrue(Speed.kilometersPerHour(1.00008)\n                .isFasterThanOrEqualTo(Speed.kilometersPerHour(1.00008)));\n        Assert.assertTrue(Speed.kilometersPerHour(1.00009)\n                .isFasterThanOrEqualTo(Speed.kilometersPerHour(1.00008)));\n        Assert.assertTrue(Speed.kilometersPerHour(1.00008)\n                .isSlowerThanOrEqualTo(Speed.kilometersPerHour(1.00008)));\n        Assert.assertTrue(Speed.kilometersPerHour(1.00008)\n                .isSlowerThanOrEqualTo(Speed.kilometersPerHour(1.00009)));\n        Assert.assertFalse(Speed.kilometersPerHour(1.00009)\n                .isSlowerThanOrEqualTo(Speed.kilometersPerHour(1.00008)));\n        Assert.assertFalse(Speed.kilometersPerHour(1.00008)\n                .isFasterThanOrEqualTo(Speed.kilometersPerHour(1.00009)));\n\n        // This comparison will only track a few digits after decimal, depending on the\n        // initialization methods you choose\n        Assert.assertFalse(\n                Speed.kilometersPerHour(1.000009).isSlowerThan(Speed.kilometersPerHour(1.000008)));\n        Assert.assertFalse(Speed.kilometersPerHour(1.000009)\n                .isSlowerThanOrEqualTo(Speed.kilometersPerHour(1.000008)));\n    }\n\n    @Test\n    public void testConversion()\n    {\n        Assert.assertEquals(Speed.kilometersPerHour(10),\n                Speed.distancePerDuration(Distance.kilometers(100), Duration.hours(10)));\n        Assert.assertTrue(Speed.milesPerHour(1).asKilometersPerHour() - 1.6093440 < 0.0001);\n    }\n\n    @Test\n    public void testDuration()\n    {\n        final Speed speed = Speed.metersPerSecond(3.0);\n        final Distance distance = Distance.meters(6.0);\n        Assert.assertEquals(Duration.seconds(2.0), speed.asDuration(distance));\n    }\n\n    @Test\n    public void testEquals()\n    {\n        Assert.assertEquals(Speed.kilometersPerHour(9.999999), Speed.kilometersPerHour(9.999999));\n        Assert.assertNotEquals(Speed.kilometersPerHour(9.99999), Speed.kilometersPerHour(9.99998));\n        Assert.assertNotEquals(Speed.kilometersPerHour(9.9999999),\n                Speed.kilometersPerHour(9.9999998));\n    }\n\n    @Test\n    public void testOperations()\n    {\n        Assert.assertEquals(Speed.kilometersPerHour(100).difference(Speed.kilometersPerHour(50)),\n                Speed.kilometersPerHour(50));\n        Assert.assertEquals(Speed.kilometersPerHour(50).add(Speed.kilometersPerHour(50)),\n                Speed.kilometersPerHour(100));\n        Assert.assertEquals(Speed.kilometersPerHour(100).subtract(Speed.kilometersPerHour(50)),\n                Speed.kilometersPerHour(50));\n        Assert.assertEquals(Speed.kilometersPerHour(50).subtract(Speed.kilometersPerHour(100)),\n                Speed.kilometersPerHour(0));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/scalars/SurfaceTest.java",
    "content": "package org.openstreetmap.atlas.utilities.scalars;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.openstreetmap.atlas.utilities.testing.FreezeDryFunction;\n\n/**\n * Unit test for {@link Surface} scale function\n *\n * @author cstaylor\n */\npublic class SurfaceTest\n{\n    private static final FreezeDryFunction<Surface> FREEZE_DRY = new FreezeDryFunction<>();\n\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Test\n    public void failNegativeScale()\n    {\n        this.thrown.expect(IllegalArgumentException.class);\n        Surface.UNIT_METER_SQUARED_ON_EARTH_SURFACE.scaleBy(-1);\n    }\n\n    @Test\n    public void identityTest()\n    {\n        Assert.assertEquals(FREEZE_DRY.apply(Surface.UNIT_METER_SQUARED_ON_EARTH_SURFACE),\n                FREEZE_DRY.apply(Surface.UNIT_METER_SQUARED_ON_EARTH_SURFACE.scaleBy(1)));\n    }\n\n    @Test\n    public void passZeroScale()\n    {\n        Assert.assertEquals(FREEZE_DRY.apply(Surface.forDm7Squared(0)),\n                FREEZE_DRY.apply(Surface.UNIT_METER_SQUARED_ON_EARTH_SURFACE.scaleBy(0)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/statistic/CounterWithStatisticTest.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CounterWithStatistic;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author tony\n */\npublic class CounterWithStatisticTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(CounterWithStatisticTest.class);\n\n    @Test\n    public void testPause() throws InterruptedException\n    {\n        final CounterWithStatistic nonPaused = new CounterWithStatistic(logger);\n        final CounterWithStatistic paused = new CounterWithStatistic(logger);\n        final Duration sleepTime1 = Duration.milliseconds(100);\n        Thread.sleep(sleepTime1.asMilliseconds());\n        nonPaused.increment();\n        paused.increment();\n        paused.pause();\n\n        final Duration sleepTime2 = Duration.milliseconds(200);\n        Thread.sleep(sleepTime2.asMilliseconds());\n\n        nonPaused.increment();\n        paused.increment();\n\n        Assert.assertEquals(2, nonPaused.count());\n        Assert.assertEquals(2, paused.count());\n\n        final Duration totalSleepTime = sleepTime1.add(sleepTime2);\n        logger.info(\"sleep time 1 is {}, sleep time 2 is {}, total is {}\", sleepTime1, sleepTime2,\n                totalSleepTime);\n        logger.info(\"non paused counter since start is {}\", nonPaused.sinceStart());\n        logger.info(\"paused counter since start is {}\", paused.sinceStart());\n        logger.info(\"paused counter accurate time spent is {}\", paused.accurateTimeSpent());\n\n        final Duration safe = Duration.milliseconds(100);\n        Assert.assertTrue(nonPaused.sinceStart().isCloseTo(totalSleepTime, safe));\n        Assert.assertTrue(nonPaused.sinceStart().isLessThan(totalSleepTime.add(safe)));\n        Assert.assertTrue(paused.sinceStart().isCloseTo(totalSleepTime, safe));\n        Assert.assertTrue(paused.sinceStart().isLessThan(totalSleepTime.add(safe)));\n\n        Assert.assertTrue(paused.accurateTimeSpent().isCloseTo(sleepTime1, safe));\n        Assert.assertTrue(paused.accurateTimeSpent().isLessThan(sleepTime1.add(safe)));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/statistic/CustomizedStatisticTest.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CounterWithStatistic;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.CustomizedStatistic;\nimport org.openstreetmap.atlas.utilities.statistic.storeless.StatisticType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author tony\n */\npublic class CustomizedStatisticTest\n{\n    private static final double DELTA = 0.00001;\n    private final List<Double> testData = new ArrayList<>();\n    private final Logger logger = LoggerFactory.getLogger(CustomizedStatisticTest.class);\n\n    @Before\n    public void loadData()\n    {\n        this.testData.add(-5d);\n        this.testData.add(-1d);\n        this.testData.add(0d);\n        this.testData.add(5d);\n        this.testData.add(5d);\n        this.testData.add(5.1);\n        this.testData.add(100d);\n    }\n\n    @Test\n    public void testCounter()\n    {\n        final CounterWithStatistic counter = new CounterWithStatistic(this.logger);\n        assertEquals(0, counter.count());\n        this.testData.forEach(value -> counter.increment());\n        assertEquals(7, counter.count());\n        counter.summary();\n    }\n\n    @Test\n    public void testCustomizedStorelessStatistic()\n    {\n        final CustomizedStatistic stat = new CustomizedStatistic(this.logger, StatisticType.Max,\n                StatisticType.Min, StatisticType.Sum, StatisticType.SumOfSquares);\n        this.testData.forEach(value -> stat.increment(value));\n        assertEquals(7, stat.count());\n        assertEquals(100, stat.getMax(), DELTA);\n        assertEquals(-5, stat.getMin(), DELTA);\n        assertEquals(109.1, stat.getSum(), DELTA);\n        assertEquals(10102.01, stat.getSumOfSquares(), DELTA);\n        try\n        {\n            stat.getGeometricMean();\n            fail();\n        }\n        catch (final CoreException e)\n        {\n            // Expected\n        }\n        stat.summary();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/statistic/StatisticUtilsTest.java",
    "content": "package org.openstreetmap.atlas.utilities.statistic;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.DoubleSummaryStatistics;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport java.util.stream.DoubleStream;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Ratio;\n\n/**\n * @author tony\n */\npublic class StatisticUtilsTest\n{\n    private static final double DELTA = 0.1;\n    private static final double STRICT_DELTA = 0.00000001;\n    private List<Double> randomBetween1And99;\n\n    @Before\n    public void loadData()\n    {\n        final DoubleStream doubleStream = new Random().doubles(1, 99);\n        this.randomBetween1And99 = doubleStream.limit(1_000_000).boxed()\n                .collect(Collectors.toList());\n    }\n\n    @Test\n    public void testStatisticUtils()\n    {\n        // Java 8 SummaryStatistics\n        final DoubleSummaryStatistics stats1 = StatisticUtils\n                .summarizingDouble(this.randomBetween1And99);\n        assertEquals(this.randomBetween1And99.size(), stats1.getCount());\n        assertEquals(1, stats1.getMin(), DELTA);\n        assertEquals(99, stats1.getMax(), DELTA);\n        assertEquals(50, stats1.getAverage(), 0.1);\n\n        // Customized operation\n        final Optional<Double> min = StatisticUtils.summarizing(this.randomBetween1And99,\n                (left, right) -> left > right ? right : left);\n        assertEquals(stats1.getMin(), min.get().doubleValue(), STRICT_DELTA);\n\n        // Apache percentile\n        assertEquals(50, StatisticUtils.percentile(this.randomBetween1And99, Ratio.percentage(50)),\n                1);\n    }\n\n    double[] toDoubleArray(final List<Double> list)\n    {\n        final double[] ret = new double[list.size()];\n        for (int i = 0; i < ret.length; i++)\n        {\n            ret[i] = list.get(i);\n        }\n        return ret;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/AtlasFromTextTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Verifies that we can create a TestAtlas from an input file\n *\n * @author cstaylor\n */\npublic class AtlasFromTextTestCase\n{\n    @Rule\n    public AtlasFromTextTestCaseRule setup = new AtlasFromTextTestCaseRule();\n\n    @Test\n    public void verify()\n    {\n        Assert.assertNotNull(this.setup.atlas());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/AtlasFromTextTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * Test fixture data for AtlasFromTextTestCase\n *\n * @author cstaylor\n */\npublic class AtlasFromTextTestCaseRule extends CoreTestRule\n{\n    @TestAtlas(loadFromTextResource = \"test.txt\")\n    private Atlas atlas;\n\n    public Atlas atlas()\n    {\n        return this.atlas;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/AtlasTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Polygon;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.geography.atlas.items.Area;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\n\n/**\n * Example test case illustrating how to use custom rules, and {@link TestAtlasHandler} unit tests.\n *\n * @author cstaylor\n * @author bbreithaupt\n */\npublic class AtlasTestCase\n{\n    @Rule\n    public AtlasTestCaseRule setup = new AtlasTestCaseRule();\n\n    @Test\n    public void allAnnotationsISOOverrideTest()\n    {\n        final Atlas atlas = this.setup.allAnnotationsISOOverrideAtlas();\n        Assert.assertEquals(9, Iterables.asList(atlas.entities(\n                atlasEntity -> atlasEntity.getTag(ISOCountryTag.KEY).orElse(\"\").equals(\"DEU\")))\n                .size());\n        Assert.assertEquals(1, Iterables.asList(atlas.entities(\n                atlasEntity -> atlasEntity.getTag(ISOCountryTag.KEY).orElse(\"\").equals(\"BBH\")))\n                .size());\n    }\n\n    @Test\n    public void allAnnotationsISOTest()\n    {\n        Assert.assertEquals(10, Iterables.asList(this.setup.allAnnotationsISOAtlas().entities(\n                atlasEntity -> atlasEntity.getTag(ISOCountryTag.KEY).orElse(\"\").equals(\"DEU\")))\n                .size());\n    }\n\n    @Test\n    public void allAnnotationsTest()\n    {\n        Assert.assertEquals(10,\n                Iterables.asList(this.setup.allAnnotationsAtlas().entities(\n                        atlasEntity -> atlasEntity.getTag(ISOCountryTag.KEY).orElse(\"\").equals(\"\")))\n                        .size());\n    }\n\n    @Test\n    public void verify()\n    {\n        Assert.assertNotNull(this.setup.atlas());\n        Assert.assertNotNull(this.setup.atlas2());\n        Assert.assertNotNull(this.setup.atlas3());\n        final Area area = this.setup.atlas3().area(1L);\n        Assert.assertNotNull(area);\n        Assert.assertEquals(new Polygon(Location.TEST_1), area.getRawGeometry());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/AtlasTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Area;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Building;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Line;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Node;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Point;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation;\nimport org.openstreetmap.atlas.utilities.testing.TestAtlas.Relation.Member;\n\n/**\n * Example test case rule showing how annotation processing can simplify test code, and\n * {@link TestAtlasHandler} unit test rule.\n *\n * @author cstaylor\n * @author bbreithaupt\n */\npublic class AtlasTestCaseRule extends CoreTestRule\n{\n    private static final String TEST_1 = \"50.2722020136163,7.64883186874144\";\n    private static final String TEST_2 = \"50.2718530548889,7.64920738356862\";\n    private static final String TEST_3 = \"50.2722620153118,7.64926173439887\";\n\n    @TestAtlas(areas = { @Area(id = \"1234\", tags = { \"name=17\", \"building=apartments\",\n            \"addr:street=Expreso V Centenario\" }, coordinates = { @Loc(\"18.4762695,-69.9118829\"),\n                    @Loc(lon = -69.3320129, lat = 19.2025913) }) })\n    private Atlas atlas;\n\n    @TestAtlas(areas = { @Area(tags = { \"name=17\", \"building=apartments\",\n            \"addr:street=Expreso V Centenario\" }, coordinates = {\n                    @Loc(lon = -69.9118829, lat = 18.4762695),\n                    @Loc(lon = -69.3320129, lat = 19.2025913) }) })\n    private Atlas atlas2;\n\n    /**\n     * A quick way for testing non-geometry related information about an area\n     */\n    @TestAtlas(areas = { @Area(tags = { \"name=42\" }) })\n    private Atlas atlas3;\n\n    @TestAtlas(\n            // Points\n            points = { @Point(coordinates = @Loc(TEST_2), tags = { \"name=Marksburg\",\n                    \"historic=castle\" }) },\n            // Nodes\n            nodes = { @Node(coordinates = @Loc(TEST_1)), @Node(coordinates = @Loc(TEST_3)) },\n            // Edges\n            edges = { @Edge(coordinates = { @Loc(TEST_1), @Loc(TEST_3) }, tags = {\n                    \"highway=footway\" }) },\n            // Lines\n            lines = { @Line(coordinates = { @Loc(TEST_1), @Loc(TEST_3) }, tags = {\n                    \"barrier=retaining_wall\" }) },\n            // Areas\n            areas = { @Area(id = \"1000000\", coordinates = { @Loc(TEST_1), @Loc(TEST_2),\n                    @Loc(TEST_3) }, tags = { \"building=yes\" }) },\n            // Relations\n            relations = { @Relation(members = {\n                    @Member(id = \"1000000\", type = \"area\", role = \"outer\") }, tags = {\n                            \"type=multipolygon\", \"building=yes\" }) },\n            // Buildings\n            buildings = { @Building(outer = @Area(coordinates = { @Loc(TEST_1), @Loc(TEST_2),\n                    @Loc(TEST_3) }), tags = \"building=yes\") })\n    private Atlas allAnnotationsAtlas;\n\n    @TestAtlas(\n            // Points\n            points = { @Point(coordinates = @Loc(TEST_2), tags = { \"name=Marksburg\",\n                    \"historic=castle\" }) },\n            // Nodes\n            nodes = { @Node(coordinates = @Loc(TEST_1)), @Node(coordinates = @Loc(TEST_3)) },\n            // Edges\n            edges = { @Edge(coordinates = { @Loc(TEST_1), @Loc(TEST_3) }, tags = {\n                    \"highway=footway\" }) },\n            // Lines\n            lines = { @Line(coordinates = { @Loc(TEST_1), @Loc(TEST_3) }, tags = {\n                    \"barrier=retaining_wall\" }) },\n            // Areas\n            areas = { @Area(id = \"1000000\", coordinates = { @Loc(TEST_1), @Loc(TEST_2),\n                    @Loc(TEST_3) }, tags = { \"building=yes\" }) },\n            // Relations\n            relations = { @Relation(members = {\n                    @Member(id = \"1000000\", type = \"area\", role = \"outer\") }, tags = {\n                            \"type=multipolygon\", \"building=yes\" }) },\n            // Buildings\n            buildings = { @Building(outer = @Area(coordinates = { @Loc(TEST_1), @Loc(TEST_2),\n                    @Loc(TEST_3) }), tags = \"building=yes\") },\n            // ISO code\n            iso = \"DEU\")\n    private Atlas allAnnotationsISOAtlas;\n\n    @TestAtlas(\n            // Points\n            points = { @Point(coordinates = @Loc(TEST_2), tags = { \"name=Marksburg\",\n                    \"historic=castle\", \"iso_country_code=BBH\" }) },\n            // Nodes\n            nodes = { @Node(coordinates = @Loc(TEST_1)), @Node(coordinates = @Loc(TEST_3)) },\n            // Edges\n            edges = { @Edge(coordinates = { @Loc(TEST_1), @Loc(TEST_3) }, tags = {\n                    \"highway=footway\" }) },\n            // Lines\n            lines = { @Line(coordinates = { @Loc(TEST_1), @Loc(TEST_3) }, tags = {\n                    \"barrier=retaining_wall\" }) },\n            // Areas\n            areas = { @Area(id = \"1000000\", coordinates = { @Loc(TEST_1), @Loc(TEST_2),\n                    @Loc(TEST_3) }, tags = { \"building=yes\" }) },\n            // Relations\n            relations = { @Relation(members = {\n                    @Member(id = \"1000000\", type = \"area\", role = \"outer\") }, tags = {\n                            \"type=multipolygon\", \"building=yes\" }) },\n            // Buildings\n            buildings = { @Building(outer = @Area(coordinates = { @Loc(TEST_1), @Loc(TEST_2),\n                    @Loc(TEST_3) }), tags = \"building=yes\") },\n            // ISO code\n            iso = \"DEU\")\n    private Atlas allAnnotationsISOOverrideAtlas;\n\n    public Atlas allAnnotationsAtlas()\n    {\n        return this.allAnnotationsAtlas;\n    }\n\n    public Atlas allAnnotationsISOAtlas()\n    {\n        return this.allAnnotationsISOAtlas;\n    }\n\n    public Atlas allAnnotationsISOOverrideAtlas()\n    {\n        return this.allAnnotationsISOOverrideAtlas;\n    }\n\n    public Atlas atlas()\n    {\n        return this.atlas;\n    }\n\n    public Atlas atlas2()\n    {\n        return this.atlas2;\n    }\n\n    public Atlas atlas3()\n    {\n        return this.atlas3;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/BeanTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Test case demonstrating how to use Bean annotations for testing\n *\n * @author cstaylor\n */\npublic class BeanTestCase\n{\n    @Rule\n    public BeanTestCaseRule setup = new BeanTestCaseRule();\n\n    @Test\n    public void verify()\n    {\n        Assert.assertEquals(\"Christopher Taylor\", this.setup.bean().getName());\n        Assert.assertEquals(\"123 Main Street\", this.setup.bean().getStreet());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/BeanTestCaseRule.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\n/**\n * Example of how to use the Bean annotation in a test case\n *\n * @author cstaylor\n */\npublic class BeanTestCaseRule extends CoreTestRule\n{\n    @Bean({ \"name=Christopher Taylor\", \"street=123 Main Street\" })\n    private SomeTestBean bean;\n\n    SomeTestBean bean()\n    {\n        return this.bean;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/CoreTestExtension.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\n\n/**\n * A JUnit 4 to JUnit 5 shim for {@link CoreTestRule}. It is still compatible with JUnit 4.\n *\n * @author Taylor Smock\n */\npublic class CoreTestExtension extends CoreTestRule implements BeforeEachCallback\n{\n    @Override\n    public void beforeEach(final ExtensionContext context) throws Exception\n    {\n        final Statement temporaryStatement = new EmptyStatement();\n        try\n        {\n            this.apply(temporaryStatement, Description.createTestDescription(this.getClass(),\n                    \"CoreTestRule JUnit5 Compatibility\")).evaluate();\n        }\n        catch (final Throwable exception)\n        {\n            throw new Exception(exception);\n        }\n    }\n\n    /**\n     * This exists solely to provide a base statement for JUnit 4 -> 5 transitioning\n     */\n    private static class EmptyStatement extends Statement\n    {\n        @Override\n        public void evaluate() throws Throwable\n        {\n            // do nothing\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/OsmFileParserTest.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.streaming.resource.InputStreamResource;\nimport org.openstreetmap.atlas.streaming.resource.Resource;\nimport org.openstreetmap.atlas.streaming.resource.StringResource;\n\n/**\n * @author matthieun\n */\npublic class OsmFileParserTest\n{\n    @Test\n    public void testReplacement()\n    {\n        final Resource josmOsmFile = new InputStreamResource(\n                () -> OsmFileParserTest.class.getResourceAsStream(\"josmOsmFile.osm\"));\n        final Resource osmFile = new InputStreamResource(\n                () -> OsmFileParserTest.class.getResourceAsStream(\"osmFile.osm\"));\n        final StringResource result = new StringResource();\n        new OsmFileParser().update(josmOsmFile, result);\n        Assert.assertEquals(osmFile.all(), result.all());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/SomeTestBean.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\n/**\n * Sample object used in the BeanTestCaseRule\n *\n * @author cstaylor\n */\nclass SomeTestBean\n{\n    private String name;\n    private String street;\n\n    public String getName()\n    {\n        return this.name;\n    }\n\n    public String getStreet()\n    {\n        return this.street;\n    }\n\n    public void setName(final String name)\n    {\n        this.name = name;\n    }\n\n    public void setStreet(final String street)\n    {\n        this.street = street;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/TestAtlasTest.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\nimport org.openstreetmap.atlas.tags.ISOCountryTag;\nimport org.openstreetmap.atlas.utilities.collections.Iterables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Tests {@link Atlas} creation from both JOSM and Osmosis XML files.\n *\n * @author matthieun\n * @author bbreithaupt\n */\npublic class TestAtlasTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(TestAtlasTest.class);\n    private static final String USA = \"USA\";\n\n    @Rule\n    public final TestAtlasTestRule rule = new TestAtlasTestRule();\n\n    @Test\n    public void loadFromJosmOsmResourceISOTest()\n    {\n        final Atlas atlas = this.rule.getAtlasFromJosmOsmResourceISO();\n        assertAtlasCreation(atlas);\n        Assert.assertEquals(14,\n                Iterables\n                        .asList(atlas.entities(\n                                entity -> entity.getTag(ISOCountryTag.KEY).orElse(\"\").equals(USA)))\n                        .size());\n    }\n\n    @Test\n    public void loadFromJosmOsmResourceTest()\n    {\n        final Atlas atlas = this.rule.getAtlasFromJosmOsmResource();\n        assertAtlasCreation(atlas);\n    }\n\n    @Test\n    public void loadFromOsmResourceISOTest()\n    {\n        final Atlas atlas = this.rule.getAtlasFromOsmResourceISO();\n        assertAtlasCreation(atlas);\n        Assert.assertEquals(14,\n                Iterables\n                        .asList(atlas.entities(\n                                entity -> entity.getTag(ISOCountryTag.KEY).orElse(\"\").equals(USA)))\n                        .size());\n    }\n\n    @Test\n    public void loadFromOsmResourceTest()\n    {\n        final Atlas atlas = this.rule.getAtlasFromOsmResource();\n        assertAtlasCreation(atlas);\n    }\n\n    private void assertAtlasCreation(final Atlas atlas)\n    {\n        logger.info(\"{}\", atlas);\n        Assert.assertEquals(4, atlas.numberOfNodes());\n        Assert.assertEquals(4, atlas.numberOfEdges());\n        Assert.assertEquals(3, atlas.numberOfAreas());\n        Assert.assertEquals(1, atlas.numberOfLines());\n        Assert.assertEquals(1, atlas.numberOfPoints());\n        Assert.assertEquals(1, atlas.numberOfRelations());\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/testing/TestAtlasTestRule.java",
    "content": "package org.openstreetmap.atlas.utilities.testing;\n\nimport org.openstreetmap.atlas.geography.atlas.Atlas;\n\n/**\n * {@link TestAtlasTest} test data\n *\n * @author matthieun\n * @author bbreithaupt\n */\npublic class TestAtlasTestRule extends CoreTestRule\n{\n    @TestAtlas(loadFromJosmOsmResource = \"josmOsmFile.osm\")\n    private Atlas atlasFromJosmOsmResource;\n\n    @TestAtlas(loadFromOsmResource = \"osmFile.osm\")\n    private Atlas atlasFromOsmResource;\n\n    @TestAtlas(loadFromJosmOsmResource = \"josmOsmFile.osm\", iso = \"USA\")\n    private Atlas atlasFromJosmOsmResourceISO;\n\n    @TestAtlas(loadFromOsmResource = \"osmFile.osm\", iso = \"USA\")\n    private Atlas atlasFromOsmResourceISO;\n\n    public Atlas getAtlasFromJosmOsmResource()\n    {\n        return this.atlasFromJosmOsmResource;\n    }\n\n    public Atlas getAtlasFromJosmOsmResourceISO()\n    {\n        return this.atlasFromJosmOsmResourceISO;\n    }\n\n    public Atlas getAtlasFromOsmResource()\n    {\n        return this.atlasFromOsmResource;\n    }\n\n    public Atlas getAtlasFromOsmResourceISO()\n    {\n        return this.atlasFromOsmResourceISO;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/threads/PoolTest.java",
    "content": "package org.openstreetmap.atlas.utilities.threads;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.TimeoutException;\nimport java.util.stream.IntStream;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.exception.CoreException;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class PoolTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(PoolTest.class);\n\n    @Test\n    public void testCallable()\n    {\n        final int size = 1_000_000;\n        final Callable<Integer> callable = () -> new Random().nextInt();\n        final List<Callable<Integer>> callables = new ArrayList<>();\n        IntStream.range(0, size).forEach(value -> callables.add(callable));\n        final Pool pool = new Pool(10, \"testPoolCallable\", Duration.milliseconds(100));\n        final List<Integer> values = pool.queueAll(callables, value -> value, Duration.ONE_SECOND);\n        Assert.assertEquals(size, values.size());\n        pool.close();\n    }\n\n    @Test(expected = CoreException.class)\n    public void testFailureCaught()\n    {\n        try (Pool pool = new Pool(2, \"Failure\", Duration.ONE_DAY))\n        {\n            // Cast to Runnable otherwise it assumes it is a Callable.\n            pool.queue((Runnable) () ->\n            {\n                Duration.milliseconds(100).sleep();\n                logger.info(\"Thread 1\");\n                throw new CoreException(\"Fail thread 1\");\n            });\n            pool.queue((Runnable) () ->\n            {\n                Duration.milliseconds(100).sleep();\n                logger.info(\"Thread 2\");\n                throw new CoreException(\"Fail thread 2\");\n            });\n        }\n    }\n\n    @Test\n    public void testQueue()\n    {\n        runWithTimer(Duration.seconds(5), () ->\n        {\n            try (Pool pool = new Pool(2, \"testPoolQueue\", Duration.ONE_SECOND))\n            {\n                pool.queue(() -> System.out.println(\"1\"));\n                pool.queue(() -> System.out.println(\"2\"));\n                pool.queue(() -> System.out.println(\"3\"));\n                pool.queue(() -> System.out.println(\"4\"));\n                pool.close();\n            }\n        });\n    }\n\n    @Test\n    public void testTickerCallable()\n    {\n        runWithTimer(Duration.seconds(5), () ->\n        {\n            try (Pool pool = new Pool(2, \"testTickerCallable\", Duration.seconds(10)))\n            {\n                final List<Result<Boolean>> results = new ArrayList<>();\n                for (int index = 0; index < 5; index++)\n                {\n                    final int idx = index;\n                    final Callable<Boolean> callable = () ->\n                    {\n                        Duration.milliseconds(100).sleep();\n                        System.out.println(\"Thread \" + idx + \" done.\");\n                        return true;\n                    };\n                    results.add(pool.queue(callable,\n                            new LogTicker(\"Ticker \" + idx, Duration.milliseconds(50))));\n                }\n                System.out.println(\"All submitted to pool!\");\n                for (final Result<Boolean> result : results)\n                {\n                    Assert.assertTrue(result.get());\n                }\n            }\n            System.out.println(\"Pool Ended.\");\n        });\n    }\n\n    @Test(expected = CoreException.class)\n    public void testTickerCallableWithErrors()\n    {\n        runWithTimer(Duration.seconds(500000), () ->\n        {\n            try (Pool pool = new Pool(2, \"testTickerCallableWithErrors\", Duration.seconds(10)))\n            {\n                final List<Result<Boolean>> results = new ArrayList<>();\n                for (int index = 0; index < 5; index++)\n                {\n                    final int idx = index;\n                    final Callable<Boolean> callable = () ->\n                    {\n                        Duration.milliseconds(100).sleep();\n                        if (idx != 3)\n                        {\n                            System.out.println(\"Thread \" + idx + \" done.\");\n                        }\n                        else\n                        {\n                            throw new CoreException(\"Failing task {} on purpose.\", idx);\n                        }\n                        return true;\n                    };\n                    results.add(pool.queue(callable,\n                            new LogTicker(\"Ticker \" + idx, Duration.milliseconds(50))));\n                }\n                System.out.println(\"All submitted to pool!\");\n                for (final Result<Boolean> result : results)\n                {\n                    Assert.assertTrue(result.get());\n                }\n            }\n            System.out.println(\"Pool Ended.\");\n        });\n    }\n\n    @Test\n    public void testTickerFailureHandling()\n    {\n        runWithTimer(Duration.seconds(5), () ->\n        {\n            try (Pool pool = new Pool(2, \"testTickerCallable\", Duration.seconds(10)))\n            {\n                final List<Result<Boolean>> results = new ArrayList<>();\n                for (int index = 0; index < 5; index++)\n                {\n                    final int idx = index;\n                    final Callable<Boolean> callable = () ->\n                    {\n                        Duration.milliseconds(100).sleep();\n                        System.out.println(\"Thread \" + idx + \" done.\");\n                        return true;\n                    };\n                    final Ticker rogueTicker = new Ticker(\"Ticker \" + idx,\n                            Duration.milliseconds(50))\n                    {\n                        @Override\n                        protected void tickAction(final Duration sinceStart)\n                        {\n                            logger.info(\"{}: {}\", getName(), sinceStart);\n                            if (\"Ticker 3\".equals(getName()))\n                            {\n                                throw new CoreException(\"I am rogue Ticker 3\");\n                            }\n                        }\n                    };\n                    results.add(pool.queue(callable, rogueTicker));\n                }\n                System.out.println(\"All submitted to pool!\");\n                for (final Result<Boolean> result : results)\n                {\n                    Assert.assertTrue(result.get());\n                }\n            }\n            System.out.println(\"Pool Ended.\");\n        });\n    }\n\n    @Test\n    public void testTickerRunnable()\n    {\n        runWithTimer(Duration.seconds(5), () ->\n        {\n            try (Pool pool = new Pool(2, \"testTickerRunnable\", Duration.seconds(10)))\n            {\n                for (int index = 0; index < 5; index++)\n                {\n                    final int idx = index;\n                    final Runnable runnable = () ->\n                    {\n                        Duration.milliseconds(100).sleep();\n                        System.out.println(\"Thread \" + idx + \" done.\");\n                    };\n                    pool.queue(runnable, new LogTicker(\"Ticker \" + idx, Duration.milliseconds(50)));\n                }\n                System.out.println(\"All submitted to pool!\");\n            }\n            System.out.println(\"Pool Ended.\");\n        });\n    }\n\n    @Test(expected = CoreException.class)\n    public void testTickerRunnableWithErrors()\n    {\n        runWithTimer(Duration.seconds(5), () ->\n        {\n            try (Pool pool = new Pool(2, \"testTickerRunnableWithErrors\", Duration.seconds(10)))\n            {\n                for (int index = 0; index < 5; index++)\n                {\n                    final int idx = index;\n                    final Runnable runnable = () ->\n                    {\n                        Duration.milliseconds(100).sleep();\n                        if (idx != 3)\n                        {\n                            System.out.println(\"Thread \" + idx + \" done.\");\n                        }\n                        else\n                        {\n                            throw new CoreException(\"Failing task {} on purpose.\", idx);\n                        }\n                    };\n                    pool.queue(runnable, new LogTicker(\"Ticker \" + idx, Duration.milliseconds(50)));\n                }\n                System.out.println(\"All submitted to pool!\");\n            }\n            System.out.println(\"Pool Ended.\");\n        });\n    }\n\n    @Test(expected = TimeoutException.class)\n    public void testTimeout() throws TimeoutException\n    {\n        try (Pool pool = new Pool(1, \"testPoolTimeout\", Duration.ONE_SECOND))\n        {\n            final Result<Integer> result = pool.queue(() ->\n            {\n                Duration.seconds(10).sleep();\n                return 10;\n            });\n            result.get(Duration.ONE_SECOND);\n            Assert.fail(\"Did not throw TimeoutException\");\n        }\n    }\n\n    @Test\n    public void testZeroThreads()\n    {\n        runWithTimer(Duration.seconds(5), () ->\n        {\n            final List<Integer> accumulator = new ArrayList<>();\n            try (Pool pool = new Pool(0, \"testZeroThreads\", Duration.seconds(10)))\n            {\n                pool.queue(() -> accumulator.add(1));\n            }\n            Assert.assertEquals(1, accumulator.size());\n        });\n    }\n\n    private void runWithTimer(final Duration maximum, final Runnable test)\n    {\n        try (Pool pool = new Pool(1, \"RunWithTimer\", maximum))\n        {\n            pool.queue(() ->\n            {\n                test.run();\n                return true;\n            }).get(maximum);\n        }\n        catch (final TimeoutException e)\n        {\n            throw new RuntimeException(\n                    \"Timeout while running unit test (Max allowed time was \" + maximum + \").\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/time/LocalTimeTest.java",
    "content": "package org.openstreetmap.atlas.utilities.time;\n\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\n\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.scalars.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author matthieun\n */\npublic class LocalTimeTest\n{\n    private static final Logger logger = LoggerFactory.getLogger(LocalTimeTest.class);\n\n    @Test\n    public void formatTest()\n    {\n        final Time time = new Time(Duration.milliseconds(1443561351635L));\n        logger.info(time.format(DateTimeFormatter.BASIC_ISO_DATE));\n        logger.info(time.format(DateTimeFormatter.ISO_DATE_TIME));\n        logger.info(time.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));\n        logger.info(time.format(\"yyyy-MM-dd--HH-mm-ss-SSS\"));\n\n        final LocalTime localTime = LocalTime.now(ZoneId.of(\"America/Los_Angeles\"));\n        logger.info(localTime.format(DateTimeFormatter.BASIC_ISO_DATE));\n        logger.info(localTime.format(DateTimeFormatter.ISO_DATE_TIME));\n        logger.info(localTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));\n        // This still fails...\n        // logger.info(time.format(\"yyyy-MM-dd--HH-mm-ss-SSS-zzz\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/timezone/TimeZoneTest.java",
    "content": "package org.openstreetmap.atlas.utilities.timezone;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.geography.Heading;\nimport org.openstreetmap.atlas.geography.Latitude;\nimport org.openstreetmap.atlas.geography.Location;\nimport org.openstreetmap.atlas.geography.Longitude;\nimport org.openstreetmap.atlas.utilities.scalars.Distance;\n\n/**\n * @author tony\n */\npublic class TimeZoneTest\n{\n    private static Location BERMUDA_INSIDE = new Location(Latitude.degrees(32.3009619),\n            Longitude.degrees(-64.770893));\n    private static Location BERMUDA_OUTSIDE_CLOSE = BERMUDA_INSIDE\n            .shiftAlongGreatCircle(Heading.EAST, Distance.SEA_TERRITORY_ZONE);\n    private static Location BERMUDA_OUTSIDE_FARAWAY = BERMUDA_INSIDE\n            .shiftAlongGreatCircle(Heading.EAST, Distance.SEA_TERRITORY_ZONE.scaleBy(3));\n\n    /**\n     * This is a case where there is a large timezone -- America/Denver -- and then a smaller\n     * timezone --America/Phoenix -- contained within that large timezone -- and then a yet smaller\n     * timezone (in this case the same as the large timezone -- America/Denver -- ) contained within\n     * that second timezone\n     */\n    private static Location ARIZONA_MULTI_POLYGON_LEVEL_3 = new Location(\n            Latitude.degrees(35.7468571), Longitude.degrees(-110.14448));\n    private static Location ARIZONA_MULTI_POLYGON_LEVEL_2 = ARIZONA_MULTI_POLYGON_LEVEL_3\n            .shiftAlongGreatCircle(Heading.WEST, Distance.miles(10));\n    private static Location ARIZONA_MULTI_POLYGON_LEVEL_1 = ARIZONA_MULTI_POLYGON_LEVEL_3\n            .shiftAlongGreatCircle(Heading.EAST, Distance.miles(10));\n\n    private static Location LOCATION_IN_NEVADA = new Location(Latitude.degrees(41.8834138),\n            Longitude.degrees(-114.19474));\n    private static Location LOCATION_IN_IDAHO = LOCATION_IN_NEVADA\n            .shiftAlongGreatCircle(Heading.NORTH, Distance.miles(10));\n    private static Location LOCATION_IN_UTAH = LOCATION_IN_NEVADA\n            .shiftAlongGreatCircle(Heading.EAST, Distance.miles(10));\n\n    @Test\n    public void testArizonaMultiPolygon()\n    {\n        final TimeZoneMap map = new TimeZoneMap(\n                ARIZONA_MULTI_POLYGON_LEVEL_3.bounds().expand(Distance.miles(50)));\n        Assert.assertEquals(\"America/Denver\", map.timeZone(ARIZONA_MULTI_POLYGON_LEVEL_3).getID());\n        Assert.assertEquals(\"America/Phoenix\", map.timeZone(ARIZONA_MULTI_POLYGON_LEVEL_2).getID());\n        Assert.assertEquals(\"America/Denver\", map.timeZone(ARIZONA_MULTI_POLYGON_LEVEL_1).getID());\n    }\n\n    @Test\n    public void testBoundary()\n    {\n        final TimeZoneMap map = new TimeZoneMap(\n                LOCATION_IN_NEVADA.bounds().expand(Distance.miles(50)));\n        Assert.assertEquals(\"America/Los_Angeles\", map.timeZone(LOCATION_IN_NEVADA).getID());\n        Assert.assertEquals(\"America/Boise\", map.timeZone(LOCATION_IN_IDAHO).getID());\n        Assert.assertEquals(\"America/Denver\", map.timeZone(LOCATION_IN_UTAH).getID());\n    }\n\n    @Test\n    public void testIsland()\n    {\n        final TimeZoneMap map = new TimeZoneMap(BERMUDA_INSIDE.bounds().expand(Distance.miles(50)));\n        Assert.assertEquals(\"Atlantic/Bermuda\", map.timeZone(BERMUDA_INSIDE).getID());\n        Assert.assertEquals(\"Atlantic/Bermuda\", map.timeZone(BERMUDA_OUTSIDE_CLOSE).getID());\n        // in the middle of the sea\n        Assert.assertEquals(\"GMT-04:00\", map.timeZone(BERMUDA_OUTSIDE_FARAWAY).getID());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/tuples/EitherTest.java",
    "content": "package org.openstreetmap.atlas.utilities.tuples;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Test the functionality of the {@link Either} class\n *\n * @author cuthbertm\n */\npublic class EitherTest\n{\n    @Test\n    public void leftTest()\n    {\n        final Either<String, Integer> either = Either.left(\"Test\");\n        either.apply(left -> Assert.assertTrue(true), right -> Assert.assertTrue(false));\n    }\n\n    @Test\n    public void mappingTest()\n    {\n        final List<Either<String, Integer>> eitherList = new ArrayList<>();\n        new Random().ints(25, 0, 2).forEach(value ->\n        {\n            if (value == 0)\n            {\n                eitherList.add(Either.left(\"Test\"));\n            }\n            else\n            {\n                eitherList.add(Either.right(54));\n            }\n        });\n\n        eitherList.stream().filter(Either::isLeft).forEach(either -> either\n                .apply(left -> Assert.assertTrue(true), right -> Assert.assertTrue(false)));\n        eitherList.stream().filter(Either::isRight).forEach(either -> either\n                .apply(left -> Assert.assertTrue(false), right -> Assert.assertTrue(true)));\n    }\n\n    @Test\n    public void rightTest()\n    {\n        final Either<String, Integer> either = Either.right(54);\n        either.apply(left -> Assert.assertTrue(false), right -> Assert.assertTrue(true));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/tuples/TupleTest.java",
    "content": "package org.openstreetmap.atlas.utilities.tuples;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author mgostintsev\n */\npublic class TupleTest\n{\n    public static final Tuple<Integer, Integer> getIntegerTuple()\n    {\n        final Tuple<Integer, Integer> tuple = Tuple.createTuple(1, 2);\n        return tuple;\n    }\n\n    public static final Tuple<String, String> getStringTuple()\n    {\n        final Tuple<String, String> tuple = Tuple.createTuple(\"s1\", \"s2\");\n        return tuple;\n    }\n\n    @Test(expected = ClassCastException.class)\n    public void testCastFailure()\n    {\n        final Tuple<?, ?> tuple = getIntegerTuple();\n        Tuple.cast(tuple, String.class, String.class);\n    }\n\n    @Test\n    public void testInstanceOf()\n    {\n        final Tuple<String, String> tuple = getStringTuple();\n        Assert.assertTrue(tuple.isInstanceOf(String.class, String.class));\n    }\n\n    @Test\n    public void testSuccessfulCast()\n    {\n        @SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n        final Tuple<?, ?> tuple = new Tuple(\"s1\", \"s2\");\n\n        final Tuple<String, String> castTuple = Tuple.cast(tuple, String.class, String.class);\n        Assert.assertTrue(castTuple.isInstanceOf(String.class, String.class));\n    }\n\n    @Test\n    public void testTuple()\n    {\n        final Tuple<String, String> tuple = getStringTuple();\n        Assert.assertEquals(tuple.getFirst(), \"s1\");\n        Assert.assertEquals(tuple.getSecond(), \"s2\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/unicode/LoadingClassifierTestCase.java",
    "content": "package org.openstreetmap.atlas.utilities.unicode;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.openstreetmap.atlas.utilities.unicode.Classification.CodeBlock;\n\n/**\n * Test case for the loading classifier\n *\n * @author cstaylor\n */\npublic class LoadingClassifierTestCase\n{\n    @Test\n    public void koreanAndEnglish()\n    {\n        final Classification classification = new LoadingClassifier()\n                .classify(\"영일만대로 (Yeongilman-daero)\");\n        Assert.assertTrue(classification.getClassificationCount() == 2);\n        Assert.assertTrue(classification.has(CodeBlock.LATIN));\n        Assert.assertTrue(classification.has(CodeBlock.HANGUL));\n    }\n\n    @Test\n    public void testDefaults()\n    {\n        final Classification classification = new LoadingClassifier().classify(\"ABC\");\n        Assert.assertEquals(1, classification.getClassificationCount());\n        Assert.assertTrue(classification.has(CodeBlock.LATIN));\n    }\n\n    @Test\n    public void testIgnore()\n    {\n        final Classification classification = new LoadingClassifier().classify(\"|\");\n        Assert.assertEquals(0, classification.getClassificationCount());\n        Assert.assertFalse(classification.has(CodeBlock.LATIN));\n    }\n\n    @Test\n    public void testSquareBracketsAndBackSlash()\n    {\n        final Classification classification = new LoadingClassifier().classify(\"[]\\\\\");\n        Assert.assertEquals(0, classification.getClassificationCount());\n        Assert.assertFalse(classification.has(CodeBlock.LATIN));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/openstreetmap/atlas/utilities/vectortiles/MinimumZoomTest.java",
    "content": "package org.openstreetmap.atlas.utilities.vectortiles;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * Basic unit tests for MinimumZoom. The main thing here is that we want to make sure the object\n * instantiates without any exceptions being thrown. If your minimum-zooms.json is bogus, you'll get\n * an exception, and then these tests will fail.\n *\n * @author hallahan\n */\npublic class MinimumZoomTest\n{\n    @Test\n    public void testGettingAMinimumZoomForTags()\n    {\n        final Map<String, String> tags = new HashMap<>();\n        tags.put(\"weird\", \"tag\");\n        final int zoom = MinimumZoom.INSTANCE.get(tags);\n        Assert.assertEquals(14, zoom);\n    }\n\n    @Test\n    public void testLoadingMinimumZoomConfig()\n    {\n        final MinimumZoom minimumZoom = MinimumZoom.INSTANCE;\n        Assert.assertNotNull(minimumZoom);\n    }\n}\n"
  },
  {
    "path": "src/test/resources/aql-files/test.aql",
    "content": "select node._ from atlas.node"
  },
  {
    "path": "src/test/resources/aql-files/test1/test.aql",
    "content": "select node._ from atlas.node"
  },
  {
    "path": "src/test/resources/aql-files/test2/test.aql",
    "content": "select node._ from atlas.node"
  },
  {
    "path": "src/test/resources/aql-files/test2/testA/test.aql",
    "content": "select node._ from atlas.node"
  },
  {
    "path": "src/test/resources/aql-files/test2/testA/test.aql.sig",
    "content": "abcd"
  },
  {
    "path": "src/test/resources/aql-files/test2/testB/test.aql",
    "content": "select node._ from atlas.node"
  },
  {
    "path": "src/test/resources/aql-files/test2/testB/test.aql.sig",
    "content": "abcd"
  },
  {
    "path": "src/test/resources/aql-files/test2/testB/test2.aql",
    "content": "select node._ from atlas.node"
  },
  {
    "path": "src/test/resources/aql-files/test2/testB/test2.aql.sig",
    "content": "abcd"
  },
  {
    "path": "src/test/resources/data/Alcatraz/Alcatraz.atlas.txt",
    "content": "# Nodes\n307351652000000 && 37.8251293,-122.421207 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755472000 || last_edit_user_id -> 14293 || last_edit_version -> 5\n307429818000000 && 37.8257351,-122.4221372 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664594000 || last_edit_user_id -> 27824 || last_edit_version -> 5\n307459464000000 && 37.8275768,-122.4230619 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958432000 || last_edit_user_id -> 35195 || last_edit_version -> 8\n1417681452000000 && 37.8267031,-122.4210044 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697600000 || last_edit_user_id -> 14293 || last_edit_version -> 2\n307446864000000 && 37.8273227,-122.4225384 && last_edit_user_name -> will l || last_edit_changeset -> 761842 || last_edit_time -> 1226936035000 || last_edit_user_id -> 35195 || last_edit_version -> 11\n3202364308000000 && 37.8270774,-122.4243445 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || last_edit_version -> 2\n307459624000000 && 37.8276242,-122.4239505 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958476000 || last_edit_user_id -> 35195 || last_edit_version -> 3\n307446872000000 && 37.827285,-122.4227049 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664595000 || last_edit_user_id -> 27824 || last_edit_version -> 7\n307446838000000 && 37.8270347,-122.4244748 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || last_edit_version -> 2\n5939835722000000 && 37.8263319,-122.4221365 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || last_edit_version -> 1\n1417681468000000 && 37.8280205,-122.4241771 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664589000 || last_edit_user_id -> 27824 || last_edit_version -> 1\n307446867000000 && 37.8271132,-122.42221 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224953895000 || last_edit_user_id -> 35195 || last_edit_version -> 1 || created_by -> JOSM\n1526523417000000 && 37.8268181,-122.421603 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || last_edit_time -> 1322753281000 || last_edit_user_id -> 381909 || last_edit_version -> 1\n307447189000000 && 37.8268745,-122.4222271 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958438000 || last_edit_user_id -> 35195 || last_edit_version -> 2\n307447029000000 && 37.8272039,-122.4229806 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958623000 || last_edit_user_id -> 35195 || last_edit_version -> 7\n1417681433000000 && 37.8269443,-122.4217188 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664588000 || last_edit_user_id -> 27824 || last_edit_version -> 1\n307459622000000 && 37.8279428,-122.4243196 && last_edit_user_name -> Walter Schlögl || last_edit_changeset -> 6060751 || access -> private || barrier -> gate || last_edit_time -> 1287257739000 || last_edit_user_id -> 78656 || last_edit_version -> 5\n5939834263000000 && 37.826102,-122.4222069 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050340 || last_edit_time -> 1538261966000 || last_edit_user_id -> 9446 || last_edit_version -> 1\n265561249000000 && 37.8270274,-122.4214142 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697600000 || last_edit_user_id -> 14293 || last_edit_version -> 12\n307447121000000 && 37.8268597,-122.4222351 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224956005000 || last_edit_user_id -> 35195 || last_edit_version -> 2\n307430033000000 && 37.8260568,-122.4221661 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050340 || last_edit_time -> 1538261966000 || last_edit_user_id -> 9446 || last_edit_version -> 6\n307459824000000 && 37.8279582,-122.4246659 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958420000 || last_edit_user_id -> 35195 || last_edit_version -> 3\n307430025000000 && 37.8265962,-122.4236215 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755472000 || last_edit_user_id -> 14293 || last_edit_version -> 9\n3202364307000000 && 37.8270479,-122.4242927 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755472000 || last_edit_user_id -> 14293 || last_edit_version -> 2\n5939835720000000 && 37.8263806,-122.4221839 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || last_edit_version -> 1\n307447150000000 && 37.8268464,-122.4222076 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224956005000 || last_edit_user_id -> 35195 || last_edit_version -> 2\n307459677000000 && 37.8279922,-122.4243622 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958420000 || last_edit_user_id -> 35195 || last_edit_version -> 6\n307446378000000 && 37.8255776,-122.4222696 && last_edit_user_name -> delphiN || last_edit_changeset -> 4749900 || last_edit_time -> 1274289694000 || last_edit_user_id -> 13192 || last_edit_version -> 6\n307430636000000 && 37.8265542,-122.4222529 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958456000 || last_edit_user_id -> 35195 || last_edit_version -> 8\n307456159000000 && 37.8267903,-122.4225171 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958456000 || last_edit_user_id -> 35195 || last_edit_version -> 5\n307446868000000 && 37.8269888,-122.422082 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958426000 || last_edit_user_id -> 35195 || last_edit_version -> 3\n1526523486000000 && 37.8266704,-122.4208218 && last_edit_user_name -> eduaddad || name:pt -> Terminal de Balsa de Alcatraz || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || amenity -> ferry_terminal || ferry -> yes || name -> Alcatraz Ferry Terminal || public_transport -> stop_position || last_edit_version -> 7 || operator -> Alcatraz Cruises, LLC\n307430019000000 && 37.8261152,-122.4234561 && last_edit_user_name -> RAW || last_edit_changeset -> 10745510 || last_edit_time -> 1329774929000 || last_edit_user_id -> 63807 || last_edit_version -> 7\n1417681435000000 && 37.8271206,-122.4239856 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048128 || barrier -> gate || last_edit_time -> 1546697857000 || last_edit_user_id -> 14293 || last_edit_version -> 2\n1641119524000000 && 37.8269543,-122.4241045 && last_edit_user_name -> RAW || last_edit_changeset -> 10745510 || last_edit_time -> 1329774926000 || last_edit_user_id -> 63807 || last_edit_version -> 1\n3202364309000000 && 37.8270878,-122.4243583 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697600000 || last_edit_user_id -> 14293 || entrance -> yes || last_edit_version -> 2\n303176761000000 && 37.8271993,-122.4216941 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || last_edit_version -> 4\n307459822000000 && 37.8278683,-122.4248946 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224957167000 || last_edit_user_id -> 35195 || last_edit_version -> 1\n307430035000000 && 37.8261526,-122.4219539 && last_edit_user_name -> Nautic || last_edit_changeset -> 17223234 || last_edit_time -> 1375682659000 || last_edit_user_id -> 449569 || last_edit_version -> 9\n4553243991000000 && 37.8258018,-122.4209095 && last_edit_user_name -> FTA || last_edit_changeset -> 44388316 || last_edit_time -> 1481686082000 || last_edit_user_id -> 652301 || last_edit_version -> 1\n307446170000000 && 37.8256105,-122.4207728 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || last_edit_version -> 5\n307465040000000 && 37.8271677,-122.423325 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664596000 || last_edit_user_id -> 27824 || last_edit_version -> 2\n307429837000000 && 37.8263646,-122.421571 && last_edit_user_name -> delphiN || last_edit_changeset -> 4749900 || last_edit_time -> 1274289689000 || last_edit_user_id -> 13192 || last_edit_version -> 3\n307430638000000 && 37.8262987,-122.4221054 && last_edit_user_name -> Nautic || last_edit_changeset -> 17223234 || last_edit_time -> 1375682660000 || last_edit_user_id -> 449569 || last_edit_version -> 9\n307457176000000 && 37.8270154,-122.4240917 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048128 || last_edit_time -> 1546697857000 || last_edit_user_id -> 14293 || last_edit_version -> 4\n1526523425000000 && 37.8264021,-122.4208428 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || last_edit_version -> 3\n307453263000000 && 37.8261859,-122.4222843 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050340 || last_edit_time -> 1538261966000 || last_edit_user_id -> 9446 || last_edit_version -> 6\n3202359193000000 && 37.8070389,-122.4041405 && wheelchair -> yes || last_edit_changeset -> 69833614 || addr:state -> CA || amenity -> ferry_terminal || addr:postcode -> 94111 || operator -> Alcatraz Cruises, LLC || addr:city -> San Francisco || name:ca -> Ferry Alcatraz || last_edit_user_name -> gpesquero || toilets:wheelchair -> yes || addr:housenumber -> Pier 33 || last_edit_time -> 1556873987000 || last_edit_user_id -> 3584 || name -> Alcatraz Cruises, LLC || name:en -> Alcatraz Cruises, LLC || addr:street -> Alcatraz Landing || last_edit_version -> 7 || name:zh -> 恶魔岛渡轮\n2213794399000000 && 37.8271609,-122.4222329 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697600000 || last_edit_user_id -> 14293 || last_edit_version -> 2\n307446836000000 && 37.827774,-122.4249791 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> private || barrier -> gate || last_edit_time -> 1546755472000 || last_edit_user_id -> 14293 || last_edit_version -> 3\n1417681458000000 && 37.8270905,-122.4219723 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664589000 || last_edit_user_id -> 27824 || last_edit_version -> 1\n1417681432000000 && 37.8266908,-122.4209852 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697600000 || last_edit_user_id -> 14293 || last_edit_version -> 3\n307446865000000 && 37.8271629,-122.4222296 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697601000 || last_edit_user_id -> 14293 || last_edit_version -> 5\n2027966328000000 && 37.8270681,-122.4222388 && last_edit_user_name -> achims311 || last_edit_changeset -> 13998200 || last_edit_time -> 1353674169000 || last_edit_user_id -> 52898 || last_edit_version -> 1\n1417681474000000 && 37.8270442,-122.4220131 && last_edit_user_name -> nithinkamath || last_edit_changeset -> 12663035 || last_edit_time -> 1344465800000 || last_edit_user_id -> 573196 || last_edit_version -> 2\n307446487000000 && 37.8277604,-122.4235646 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224958420000 || last_edit_user_id -> 35195 || last_edit_version -> 10\n# Edges\n27989500000000 && 37.8070389,-122.4041405:37.8074686,-122.4039428:37.808076,-122.4035932:37.8084458,-122.4034591:37.8087629,-122.4034225:37.809084,-122.4034509:37.8094213,-122.4035282:37.8097059,-122.4036339:37.8101326,-122.4038737:37.8108196,-122.4043411:37.811474,-122.4048817:37.8121365,-122.4055158:37.8137484,-122.4073261:37.8246229,-122.417873:37.8256667,-122.4188075:37.8262371,-122.4195964:37.8265614,-122.4203732:37.826636,-122.4206135:37.8266723,-122.4207726:37.8266704,-122.4208218 && payment:clipper -> no || last_edit_changeset -> 73085718 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> no || operator -> Alcatraz Cruises, LLC || vehicle -> no || duration -> 00:15:00 || last_edit_user_name -> clay_c || route -> ferry || last_edit_time -> 1565127911000 || last_edit_user_id -> 119881 || name -> Alcatraz - Pier 33 Alcatraz Landing || last_edit_version -> 12 || foot -> yes\n-27989500000000 && 37.8266704,-122.4208218:37.8266723,-122.4207726:37.826636,-122.4206135:37.8265614,-122.4203732:37.8262371,-122.4195964:37.8256667,-122.4188075:37.8246229,-122.417873:37.8137484,-122.4073261:37.8121365,-122.4055158:37.811474,-122.4048817:37.8108196,-122.4043411:37.8101326,-122.4038737:37.8097059,-122.4036339:37.8094213,-122.4035282:37.809084,-122.4034509:37.8087629,-122.4034225:37.8084458,-122.4034591:37.808076,-122.4035932:37.8074686,-122.4039428:37.8070389,-122.4041405 && payment:clipper -> no || last_edit_changeset -> 73085718 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> no || operator -> Alcatraz Cruises, LLC || vehicle -> no || duration -> 00:15:00 || last_edit_user_name -> clay_c || route -> ferry || last_edit_time -> 1565127911000 || last_edit_user_id -> 119881 || name -> Alcatraz - Pier 33 Alcatraz Landing || last_edit_version -> 12 || foot -> yes\n27996878000000 && 37.8255776,-122.4222696:37.8256159,-122.4223636:37.8257081,-122.4225136:37.8257651,-122.4226203:37.8258245,-122.4227291:37.8259294,-122.4227869:37.8259939,-122.4229635:37.8259722,-122.4230662:37.82597,-122.4231203:37.8259974,-122.4231939:37.8260251,-122.4232676:37.8260526,-122.4233438:37.8260709,-122.4233933:37.8260901,-122.4234293:37.8261152,-122.4234561 && last_edit_user_name -> RAW || last_edit_changeset -> 10745510 || access -> private || last_edit_time -> 1329774930000 || last_edit_user_id -> 63807 || highway -> footway || last_edit_version -> 5\n-27996878000000 && 37.8261152,-122.4234561:37.8260901,-122.4234293:37.8260709,-122.4233933:37.8260526,-122.4233438:37.8260251,-122.4232676:37.8259974,-122.4231939:37.82597,-122.4231203:37.8259722,-122.4230662:37.8259939,-122.4229635:37.8259294,-122.4227869:37.8258245,-122.4227291:37.8257651,-122.4226203:37.8257081,-122.4225136:37.8256159,-122.4223636:37.8255776,-122.4222696 && last_edit_user_name -> RAW || last_edit_changeset -> 10745510 || access -> private || last_edit_time -> 1329774930000 || last_edit_user_id -> 63807 || highway -> footway || last_edit_version -> 5\n27998996000001 && 37.8265542,-122.4222529:37.8264525,-122.4221499:37.826426,-122.4221443:37.8263254,-122.4220706:37.8262987,-122.4221054 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || source -> survey || highway -> footway || last_edit_version -> 6\n-27998996000001 && 37.8262987,-122.4221054:37.8263254,-122.4220706:37.826426,-122.4221443:37.8264525,-122.4221499:37.8265542,-122.4222529 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || source -> survey || highway -> footway || last_edit_version -> 6\n27998996000002 && 37.8262987,-122.4221054:37.8261526,-122.4219539 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || source -> survey || highway -> footway || last_edit_version -> 6\n-27998996000002 && 37.8261526,-122.4219539:37.8262987,-122.4221054 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || source -> survey || highway -> footway || last_edit_version -> 6\n459479815000000 && 37.8258018,-122.4209095:37.8256778,-122.4207891:37.8256396,-122.4207475:37.8256105,-122.4207728 && last_edit_user_name -> FTA || last_edit_changeset -> 44388316 || last_edit_time -> 1481686082000 || last_edit_user_id -> 652301 || highway -> footway || last_edit_version -> 1\n-459479815000000 && 37.8256105,-122.4207728:37.8256396,-122.4207475:37.8256778,-122.4207891:37.8258018,-122.4209095 && last_edit_user_name -> FTA || last_edit_changeset -> 44388316 || last_edit_time -> 1481686082000 || last_edit_user_id -> 652301 || highway -> footway || last_edit_version -> 1\n27999692000001 && 37.8263806,-122.4221839:37.8262122,-122.4224379:37.8261859,-122.4222843 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || source -> survey || highway -> footway || last_edit_version -> 5\n-27999692000001 && 37.8261859,-122.4222843:37.8262122,-122.4224379:37.8263806,-122.4221839 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || source -> survey || highway -> footway || last_edit_version -> 5\n27999692000002 && 37.8261859,-122.4222843:37.8262987,-122.4221054 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || source -> survey || highway -> footway || last_edit_version -> 5\n-27999692000002 && 37.8262987,-122.4221054:37.8261859,-122.4222843 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || source -> survey || highway -> footway || last_edit_version -> 5\n27999864000001 && 37.8266908,-122.4209852:37.8267423,-122.4209305:37.8266704,-122.4208218 && area -> yes || last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 70368429 || man_made -> pier || last_edit_time -> 1558116233000 || last_edit_user_id -> 119881 || last_edit_version -> 8\n-27999864000001 && 37.8266704,-122.4208218:37.8267423,-122.4209305:37.8266908,-122.4209852 && area -> yes || last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 70368429 || man_made -> pier || last_edit_time -> 1558116233000 || last_edit_user_id -> 119881 || last_edit_version -> 8\n27999864000002 && 37.8266704,-122.4208218:37.8266033,-122.4207207:37.8265198,-122.4208095:37.8266589,-122.4210191:37.8266908,-122.4209852 && area -> yes || last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 70368429 || man_made -> pier || last_edit_time -> 1558116233000 || last_edit_user_id -> 119881 || last_edit_version -> 8\n-27999864000002 && 37.8266908,-122.4209852:37.8266589,-122.4210191:37.8265198,-122.4208095:37.8266033,-122.4207207:37.8266704,-122.4208218 && area -> yes || last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 70368429 || man_made -> pier || last_edit_time -> 1558116233000 || last_edit_user_id -> 119881 || last_edit_version -> 8\n27999973000001 && 37.8268597,-122.4222351:37.8268464,-122.4222076 && last_edit_user_name -> delphiN || last_edit_changeset -> 4749900 || access -> private || last_edit_time -> 1274289692000 || last_edit_user_id -> 13192 || highway -> steps || last_edit_version -> 3\n-27999973000001 && 37.8268464,-122.4222076:37.8268597,-122.4222351 && last_edit_user_name -> delphiN || last_edit_changeset -> 4749900 || access -> private || last_edit_time -> 1274289692000 || last_edit_user_id -> 13192 || highway -> steps || last_edit_version -> 3\n27999973000002 && 37.8268464,-122.4222076:37.8268304,-122.4222147:37.8268702,-122.4223:37.8268474,-122.4223125:37.8268077,-122.4222252:37.8267768,-122.4222596:37.8268717,-122.4224313:37.8267903,-122.4225171 && last_edit_user_name -> delphiN || last_edit_changeset -> 4749900 || access -> private || last_edit_time -> 1274289692000 || last_edit_user_id -> 13192 || highway -> steps || last_edit_version -> 3\n-27999973000002 && 37.8267903,-122.4225171:37.8268717,-122.4224313:37.8267768,-122.4222596:37.8268077,-122.4222252:37.8268474,-122.4223125:37.8268702,-122.4223:37.8268304,-122.4222147:37.8268464,-122.4222076 && last_edit_user_name -> delphiN || last_edit_changeset -> 4749900 || access -> private || last_edit_time -> 1274289692000 || last_edit_user_id -> 13192 || highway -> steps || last_edit_version -> 3\n629120567000000 && 37.826102,-122.4222069:37.8260568,-122.4221661 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050340 || last_edit_time -> 1538261966000 || last_edit_user_id -> 9446 || highway -> footway || last_edit_version -> 1\n-629120567000000 && 37.8260568,-122.4221661:37.826102,-122.4222069 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050340 || last_edit_time -> 1538261966000 || last_edit_user_id -> 9446 || highway -> footway || last_edit_version -> 1\n27999701000001 && 37.8272039,-122.4229806:37.8271429,-122.4230235:37.8270073,-122.4228089:37.8268988,-122.4226544:37.8267903,-122.4225171 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> permissive || last_edit_time -> 1224958456000 || last_edit_user_id -> 35195 || source -> survey || highway -> service || last_edit_version -> 4 || created_by -> Potlatch 0.10e\n-27999701000001 && 37.8267903,-122.4225171:37.8268988,-122.4226544:37.8270073,-122.4228089:37.8271429,-122.4230235:37.8272039,-122.4229806 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> permissive || last_edit_time -> 1224958456000 || last_edit_user_id -> 35195 || source -> survey || highway -> service || last_edit_version -> 4 || created_by -> Potlatch 0.10e\n27999701000002 && 37.8267903,-122.4225171:37.8267361,-122.4224399:37.8265542,-122.4222529 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> permissive || last_edit_time -> 1224958456000 || last_edit_user_id -> 35195 || source -> survey || highway -> service || last_edit_version -> 4 || created_by -> Potlatch 0.10e\n-27999701000002 && 37.8265542,-122.4222529:37.8267361,-122.4224399:37.8267903,-122.4225171 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> permissive || last_edit_time -> 1224958456000 || last_edit_user_id -> 35195 || source -> survey || highway -> service || last_edit_version -> 4 || created_by -> Potlatch 0.10e\n28000456000000 && 37.8278683,-122.4248946:37.8279157,-122.4247487:37.8279582,-122.4246659 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> permissive || last_edit_time -> 1224957167000 || last_edit_user_id -> 35195 || highway -> service || last_edit_version -> 1 || created_by -> Potlatch 0.10e\n-28000456000000 && 37.8279582,-122.4246659:37.8279157,-122.4247487:37.8278683,-122.4248946 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> permissive || last_edit_time -> 1224957167000 || last_edit_user_id -> 35195 || highway -> service || last_edit_version -> 1 || created_by -> Potlatch 0.10e\n27998971000000 && 37.8255776,-122.4222696:37.8254863,-122.4221485:37.8253937,-122.4219604:37.8253699,-122.4219295:37.8253371,-122.421906:37.825312,-122.4218736:37.8252821,-122.4218064:37.8252571,-122.4217389:37.8252466,-122.4217013:37.825241,-122.4216635:37.8252425,-122.4216273:37.8252513,-122.4215468:37.8252669,-122.4214067:37.8252755,-122.4213487:37.825285,-122.4212942:37.8253016,-122.4212326:37.8253232,-122.4211635:37.8253462,-122.4211056:37.8253745,-122.4210509:37.825412,-122.4209931:37.8254547,-122.4209381:37.8254977,-122.4208866:37.8255531,-122.4208283:37.8256105,-122.4207728 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755474000 || last_edit_user_id -> 14293 || highway -> footway || last_edit_version -> 6\n-27998971000000 && 37.8256105,-122.4207728:37.8255531,-122.4208283:37.8254977,-122.4208866:37.8254547,-122.4209381:37.825412,-122.4209931:37.8253745,-122.4210509:37.8253462,-122.4211056:37.8253232,-122.4211635:37.8253016,-122.4212326:37.825285,-122.4212942:37.8252755,-122.4213487:37.8252669,-122.4214067:37.8252513,-122.4215468:37.8252425,-122.4216273:37.825241,-122.4216635:37.8252466,-122.4217013:37.8252571,-122.4217389:37.8252821,-122.4218064:37.825312,-122.4218736:37.8253371,-122.421906:37.8253699,-122.4219295:37.8253937,-122.4219604:37.8254863,-122.4221485:37.8255776,-122.4222696 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755474000 || last_edit_user_id -> 14293 || highway -> footway || last_edit_version -> 6\n27996944000001 && 37.8271677,-122.423325:37.8269103,-122.4229565:37.8269421,-122.4229216:37.8270828,-122.4231364:37.8271874,-122.4232905:37.8272688,-122.4234124:37.8272952,-122.4234433:37.8273118,-122.4234534:37.8273378,-122.4234535:37.8273597,-122.4234474:37.8273791,-122.4234309:37.8273905,-122.4234101:37.8273969,-122.4233843:37.8273955,-122.4233642:37.8273921,-122.4233363:37.8273827,-122.4233086:37.8273607,-122.4232626:37.8272944,-122.4231264:37.8272039,-122.4229806 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59183284 || access -> permissive || last_edit_time -> 1527002255000 || last_edit_user_id -> 7134350 || source -> survey || highway -> service || last_edit_version -> 7\n-27996944000001 && 37.8272039,-122.4229806:37.8272944,-122.4231264:37.8273607,-122.4232626:37.8273827,-122.4233086:37.8273921,-122.4233363:37.8273955,-122.4233642:37.8273969,-122.4233843:37.8273905,-122.4234101:37.8273791,-122.4234309:37.8273597,-122.4234474:37.8273378,-122.4234535:37.8273118,-122.4234534:37.8272952,-122.4234433:37.8272688,-122.4234124:37.8271874,-122.4232905:37.8270828,-122.4231364:37.8269421,-122.4229216:37.8269103,-122.4229565:37.8271677,-122.423325 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59183284 || access -> permissive || last_edit_time -> 1527002255000 || last_edit_user_id -> 7134350 || source -> survey || highway -> service || last_edit_version -> 7\n27996944000002 && 37.8272039,-122.4229806:37.8270498,-122.4226474:37.8269409,-122.4223799:37.8268745,-122.4222271 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59183284 || access -> permissive || last_edit_time -> 1527002255000 || last_edit_user_id -> 7134350 || source -> survey || highway -> service || last_edit_version -> 7\n-27996944000002 && 37.8268745,-122.4222271:37.8269409,-122.4223799:37.8270498,-122.4226474:37.8272039,-122.4229806 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59183284 || access -> permissive || last_edit_time -> 1527002255000 || last_edit_user_id -> 7134350 || source -> survey || highway -> service || last_edit_version -> 7\n27998974000000 && 37.827774,-122.4249791:37.8275858,-122.4248466:37.8271828,-122.4245778:37.8270347,-122.4244748 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> private || last_edit_time -> 1546755474000 || last_edit_user_id -> 14293 || highway -> footway || last_edit_version -> 2\n-27998974000000 && 37.8270347,-122.4244748:37.8271828,-122.4245778:37.8275858,-122.4248466:37.827774,-122.4249791 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> private || last_edit_time -> 1546755474000 || last_edit_user_id -> 14293 || highway -> footway || last_edit_version -> 2\n28000400000001 && 37.8275768,-122.4230619:37.8274276,-122.4229291:37.8273191,-122.4227832:37.827285,-122.4227049 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || access -> permissive || last_edit_time -> 1314664597000 || last_edit_user_id -> 27824 || source -> survey || highway -> service || last_edit_version -> 4\n-28000400000001 && 37.827285,-122.4227049:37.8273191,-122.4227832:37.8274276,-122.4229291:37.8275768,-122.4230619 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || access -> permissive || last_edit_time -> 1314664597000 || last_edit_user_id -> 27824 || source -> survey || highway -> service || last_edit_version -> 4\n28000400000002 && 37.827285,-122.4227049:37.8272008,-122.422614:37.827145,-122.4225347:37.8270892,-122.4224434:37.826971,-122.4222182:37.8268745,-122.4222271 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || access -> permissive || last_edit_time -> 1314664597000 || last_edit_user_id -> 27824 || source -> survey || highway -> service || last_edit_version -> 4\n-28000400000002 && 37.8268745,-122.4222271:37.826971,-122.4222182:37.8270892,-122.4224434:37.827145,-122.4225347:37.8272008,-122.422614:37.827285,-122.4227049 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || access -> permissive || last_edit_time -> 1314664597000 || last_edit_user_id -> 27824 || source -> survey || highway -> service || last_edit_version -> 4\n27998999000000 && 37.8268745,-122.4222271:37.8268597,-122.4222351 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> private || last_edit_time -> 1224954023000 || last_edit_user_id -> 35195 || highway -> footway || last_edit_version -> 1 || created_by -> JOSM\n-27998999000000 && 37.8268597,-122.4222351:37.8268745,-122.4222271 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> private || last_edit_time -> 1224954023000 || last_edit_user_id -> 35195 || highway -> footway || last_edit_version -> 1 || created_by -> JOSM\n27999800000001 && 37.8261152,-122.4234561:37.826172,-122.4234946:37.8262353,-122.4235155:37.826368,-122.4235658:37.8264185,-122.4235906:37.8264695,-122.4236169:37.8265259,-122.4236413:37.8265462,-122.4236473:37.8265662,-122.4236481:37.8265794,-122.4236449:37.8265897,-122.4236358:37.8265962,-122.4236215 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || name -> West Road || highway -> service || last_edit_version -> 5\n-27999800000001 && 37.8265962,-122.4236215:37.8265897,-122.4236358:37.8265794,-122.4236449:37.8265662,-122.4236481:37.8265462,-122.4236473:37.8265259,-122.4236413:37.8264695,-122.4236169:37.8264185,-122.4235906:37.826368,-122.4235658:37.8262353,-122.4235155:37.826172,-122.4234946:37.8261152,-122.4234561 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || name -> West Road || highway -> service || last_edit_version -> 5\n27999800000002 && 37.8265962,-122.4236215:37.8265981,-122.423603:37.8265934,-122.4235809:37.8265839,-122.4235623:37.8265732,-122.4235467:37.8263884,-122.4233246:37.8263441,-122.4232657:37.8263238,-122.4232313:37.8263034,-122.4231884:37.8262899,-122.4231439:37.8262707,-122.4230778:37.8262188,-122.4229102:37.8261717,-122.4227524:37.8261517,-122.4226823:37.8261423,-122.422641:37.8261172,-122.4225077:37.8260838,-122.4223215:37.8260651,-122.4222218:37.8260568,-122.4221661 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || name -> West Road || highway -> service || last_edit_version -> 5\n-27999800000002 && 37.8260568,-122.4221661:37.8260651,-122.4222218:37.8260838,-122.4223215:37.8261172,-122.4225077:37.8261423,-122.422641:37.8261517,-122.4226823:37.8261717,-122.4227524:37.8262188,-122.4229102:37.8262707,-122.4230778:37.8262899,-122.4231439:37.8263034,-122.4231884:37.8263238,-122.4232313:37.8263441,-122.4232657:37.8263884,-122.4233246:37.8265732,-122.4235467:37.8265839,-122.4235623:37.8265934,-122.4235809:37.8265981,-122.423603:37.8265962,-122.4236215 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || name -> West Road || highway -> service || last_edit_version -> 5\n27999800000003 && 37.8260568,-122.4221661:37.8260557,-122.4221316:37.826064,-122.4220996:37.8260813,-122.4220557:37.8261526,-122.4219539 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || name -> West Road || highway -> service || last_edit_version -> 5\n-27999800000003 && 37.8261526,-122.4219539:37.8260813,-122.4220557:37.826064,-122.4220996:37.8260557,-122.4221316:37.8260568,-122.4221661 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || name -> West Road || highway -> service || last_edit_version -> 5\n27998983000001 && 37.8269888,-122.422082:37.8270681,-122.4222388 && last_edit_user_name -> achims311 || last_edit_changeset -> 13998200 || access -> permissive || last_edit_time -> 1353674169000 || last_edit_user_id -> 52898 || source -> survey || highway -> service || last_edit_version -> 4\n-27998983000001 && 37.8270681,-122.4222388:37.8269888,-122.422082 && last_edit_user_name -> achims311 || last_edit_changeset -> 13998200 || access -> permissive || last_edit_time -> 1353674169000 || last_edit_user_id -> 52898 || source -> survey || highway -> service || last_edit_version -> 4\n27998983000002 && 37.8270681,-122.4222388:37.8271463,-122.4224117:37.8272161,-122.4225662:37.827285,-122.4227049 && last_edit_user_name -> achims311 || last_edit_changeset -> 13998200 || access -> permissive || last_edit_time -> 1353674169000 || last_edit_user_id -> 52898 || source -> survey || highway -> service || last_edit_version -> 4\n-27998983000002 && 37.827285,-122.4227049:37.8272161,-122.4225662:37.8271463,-122.4224117:37.8270681,-122.4222388 && last_edit_user_name -> achims311 || last_edit_changeset -> 13998200 || access -> permissive || last_edit_time -> 1353674169000 || last_edit_user_id -> 52898 || source -> survey || highway -> service || last_edit_version -> 4\n128245370000000 && 37.8266908,-122.4209852:37.8267031,-122.4210044 && last_edit_user_name -> monena41 || last_edit_changeset -> 58579435 || man_made -> pier || last_edit_time -> 1525167595000 || last_edit_user_id -> 4633898 || amenity -> ferry_terminal || name -> Alcatraz Island Ferry Terminal || cargo -> passengers || last_edit_version -> 2\n-128245370000000 && 37.8267031,-122.4210044:37.8266908,-122.4209852 && last_edit_user_name -> monena41 || last_edit_changeset -> 58579435 || man_made -> pier || last_edit_time -> 1525167595000 || last_edit_user_id -> 4633898 || amenity -> ferry_terminal || name -> Alcatraz Island Ferry Terminal || cargo -> passengers || last_edit_version -> 2\n59621740000000 && 37.8255776,-122.4222696:37.8257351,-122.4221372 && last_edit_user_name -> dpaschich || last_edit_changeset -> 50700405 || last_edit_time -> 1501440870000 || last_edit_user_id -> 621202 || highway -> footway || last_edit_version -> 2\n-59621740000000 && 37.8257351,-122.4221372:37.8255776,-122.4222696 && last_edit_user_name -> dpaschich || last_edit_changeset -> 50700405 || last_edit_time -> 1501440870000 || last_edit_user_id -> 621202 || highway -> footway || last_edit_version -> 2\n128245366000000 && 37.8270905,-122.4219723:37.8269443,-122.4217188 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664590000 || last_edit_user_id -> 27824 || highway -> footway || last_edit_version -> 1\n-128245366000000 && 37.8269443,-122.4217188:37.8270905,-122.4219723 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664590000 || last_edit_user_id -> 27824 || highway -> footway || last_edit_version -> 1\n314192471000000 && 37.8270878,-122.4243583:37.8270774,-122.4243445 && last_edit_user_name -> dchiles || last_edit_changeset -> 26984131 || last_edit_time -> 1416781373000 || last_edit_user_id -> 153669 || highway -> footway || last_edit_version -> 1\n-314192471000000 && 37.8270774,-122.4243445:37.8270878,-122.4243583 && last_edit_user_name -> dchiles || last_edit_changeset -> 26984131 || last_edit_time -> 1416781373000 || last_edit_user_id -> 153669 || highway -> footway || last_edit_version -> 1\n27998977000000 && 37.8271629,-122.4222296:37.827168,-122.422239:37.8272859,-122.4224685:37.8273227,-122.4225384 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753285000 || last_edit_user_id -> 381909 || name -> East Road || highway -> service || last_edit_version -> 3 || layer -> -1 || tunnel -> yes\n-27998977000000 && 37.8273227,-122.4225384:37.8272859,-122.4224685:37.827168,-122.422239:37.8271629,-122.4222296 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753285000 || last_edit_user_id -> 381909 || name -> East Road || highway -> service || last_edit_version -> 3 || layer -> -1 || tunnel -> yes\n27998998000001 && 37.8268745,-122.4222271:37.8268464,-122.4222076 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59183284 || access -> permissive || last_edit_time -> 1527002255000 || last_edit_user_id -> 7134350 || source -> survey || highway -> service || last_edit_version -> 4\n-27998998000001 && 37.8268464,-122.4222076:37.8268745,-122.4222271 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59183284 || access -> permissive || last_edit_time -> 1527002255000 || last_edit_user_id -> 7134350 || source -> survey || highway -> service || last_edit_version -> 4\n27998998000002 && 37.8268464,-122.4222076:37.826809,-122.4221806:37.8266226,-122.4219063:37.8265103,-122.4217541:37.8264333,-122.4216514:37.8263646,-122.421571 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59183284 || access -> permissive || last_edit_time -> 1527002255000 || last_edit_user_id -> 7134350 || source -> survey || highway -> service || last_edit_version -> 4\n-27998998000002 && 37.8263646,-122.421571:37.8264333,-122.4216514:37.8265103,-122.4217541:37.8266226,-122.4219063:37.826809,-122.4221806:37.8268464,-122.4222076 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59183284 || access -> permissive || last_edit_time -> 1527002255000 || last_edit_user_id -> 7134350 || source -> survey || highway -> service || last_edit_version -> 4\n629121014000000 && 37.8262987,-122.4221054:37.8263319,-122.4221365 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || highway -> steps || last_edit_version -> 1\n-629121014000000 && 37.8263319,-122.4221365:37.8262987,-122.4221054 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || highway -> steps || last_edit_version -> 1\n27996842000001 && 37.8257351,-122.4221372:37.8257192,-122.4219714:37.8257069,-122.4219113:37.8256076,-122.4217186:37.8256695,-122.4216631:37.8256361,-122.4215908:37.825583,-122.4216015:37.8255055,-122.4214552:37.8254333,-122.4214099:37.8253431,-122.4214434:37.8252912,-122.4215249:37.8253312,-122.4212359:37.8253762,-122.4212779:37.8255871,-122.4213427:37.8256336,-122.4212818:37.8256581,-122.4212001:37.8257184,-122.4211315:37.8257309,-122.4210036:37.8256911,-122.4209166:37.8258018,-122.4209095 && area -> yes || last_edit_user_name -> Azlux || last_edit_changeset -> 47452285 || surface -> concrete || last_edit_time -> 1491332262000 || last_edit_user_id -> 5586237 || name -> Parade Ground || highway -> pedestrian || last_edit_version -> 7\n-27996842000001 && 37.8258018,-122.4209095:37.8256911,-122.4209166:37.8257309,-122.4210036:37.8257184,-122.4211315:37.8256581,-122.4212001:37.8256336,-122.4212818:37.8255871,-122.4213427:37.8253762,-122.4212779:37.8253312,-122.4212359:37.8252912,-122.4215249:37.8253431,-122.4214434:37.8254333,-122.4214099:37.8255055,-122.4214552:37.825583,-122.4216015:37.8256361,-122.4215908:37.8256695,-122.4216631:37.8256076,-122.4217186:37.8257069,-122.4219113:37.8257192,-122.4219714:37.8257351,-122.4221372 && area -> yes || last_edit_user_name -> Azlux || last_edit_changeset -> 47452285 || surface -> concrete || last_edit_time -> 1491332262000 || last_edit_user_id -> 5586237 || name -> Parade Ground || highway -> pedestrian || last_edit_version -> 7\n27996842000002 && 37.8258018,-122.4209095:37.8258258,-122.420908:37.8259144,-122.421014:37.8260075,-122.421124:37.8263461,-122.4213991:37.8263899,-122.4215455:37.8263646,-122.421571 && area -> yes || last_edit_user_name -> Azlux || last_edit_changeset -> 47452285 || surface -> concrete || last_edit_time -> 1491332262000 || last_edit_user_id -> 5586237 || name -> Parade Ground || highway -> pedestrian || last_edit_version -> 7\n-27996842000002 && 37.8263646,-122.421571:37.8263899,-122.4215455:37.8263461,-122.4213991:37.8260075,-122.421124:37.8259144,-122.421014:37.8258258,-122.420908:37.8258018,-122.4209095 && area -> yes || last_edit_user_name -> Azlux || last_edit_changeset -> 47452285 || surface -> concrete || last_edit_time -> 1491332262000 || last_edit_user_id -> 5586237 || name -> Parade Ground || highway -> pedestrian || last_edit_version -> 7\n27996842000003 && 37.8263646,-122.421571:37.8263469,-122.4215926:37.8261538,-122.4217471:37.8261191,-122.4217666:37.8260852,-122.4217843:37.8260352,-122.4218334:37.8260103,-122.4218633:37.82597,-122.4219276:37.8257351,-122.4221372 && area -> yes || last_edit_user_name -> Azlux || last_edit_changeset -> 47452285 || surface -> concrete || last_edit_time -> 1491332262000 || last_edit_user_id -> 5586237 || name -> Parade Ground || highway -> pedestrian || last_edit_version -> 7\n-27996842000003 && 37.8257351,-122.4221372:37.82597,-122.4219276:37.8260103,-122.4218633:37.8260352,-122.4218334:37.8260852,-122.4217843:37.8261191,-122.4217666:37.8261538,-122.4217471:37.8263469,-122.4215926:37.8263646,-122.421571 && area -> yes || last_edit_user_name -> Azlux || last_edit_changeset -> 47452285 || surface -> concrete || last_edit_time -> 1491332262000 || last_edit_user_id -> 5586237 || name -> Parade Ground || highway -> pedestrian || last_edit_version -> 7\n27989502000000 && 37.8251293,-122.421207:37.8251708,-122.4210867:37.8251972,-122.4210324:37.8252111,-122.4210075:37.8252266,-122.4209838:37.8252472,-122.4209575:37.8253152,-122.420884:37.8253488,-122.420853:37.8253689,-122.4208404:37.8253963,-122.4208266:37.8254275,-122.4208081:37.8254607,-122.4207908:37.8255612,-122.4207442:37.8256105,-122.4207728 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || name -> The Agave Trail || highway -> steps || last_edit_version -> 6\n-27989502000000 && 37.8256105,-122.4207728:37.8255612,-122.4207442:37.8254607,-122.4207908:37.8254275,-122.4208081:37.8253963,-122.4208266:37.8253689,-122.4208404:37.8253488,-122.420853:37.8253152,-122.420884:37.8252472,-122.4209575:37.8252266,-122.4209838:37.8252111,-122.4210075:37.8251972,-122.4210324:37.8251708,-122.4210867:37.8251293,-122.421207 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || name -> The Agave Trail || highway -> steps || last_edit_version -> 6\n27609572000000 && 37.8270274,-122.4214142:37.8271993,-122.4216941 && last_edit_user_name -> GreyCat || last_edit_changeset -> 172031 || man_made -> pier || last_edit_time -> 1223501888000 || last_edit_user_id -> 45062 || last_edit_version -> 2 || created_by -> Potlatch 0.10d\n-27609572000000 && 37.8271993,-122.4216941:37.8270274,-122.4214142 && last_edit_user_name -> GreyCat || last_edit_changeset -> 172031 || man_made -> pier || last_edit_time -> 1223501888000 || last_edit_user_id -> 45062 || last_edit_version -> 2 || created_by -> Potlatch 0.10d\n28000429000000 && 37.8279428,-122.4243196:37.8276242,-122.4239505 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> private || last_edit_time -> 1224958477000 || last_edit_user_id -> 35195 || source -> yahoo || highway -> footway || last_edit_version -> 2 || created_by -> Potlatch 0.10e\n-28000429000000 && 37.8276242,-122.4239505:37.8279428,-122.4243196 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || access -> private || last_edit_time -> 1224958477000 || last_edit_user_id -> 35195 || source -> yahoo || highway -> footway || last_edit_version -> 2 || created_by -> Potlatch 0.10e\n128245371000000 && 37.8270442,-122.4220131:37.8270905,-122.4219723 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664591000 || last_edit_user_id -> 27824 || bridge -> yes || highway -> footway || last_edit_version -> 1\n-128245371000000 && 37.8270905,-122.4219723:37.8270442,-122.4220131 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664591000 || last_edit_user_id -> 27824 || bridge -> yes || highway -> footway || last_edit_version -> 1\n128245363000000 && 37.8280205,-122.4241771:37.8277896,-122.423827:37.8277604,-122.4235646 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || access -> permissive || last_edit_time -> 1314664590000 || last_edit_user_id -> 27824 || source -> survey || highway -> service || last_edit_version -> 1\n-128245363000000 && 37.8277604,-122.4235646:37.8277896,-122.423827:37.8280205,-122.4241771 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || access -> permissive || last_edit_time -> 1314664590000 || last_edit_user_id -> 27824 || source -> survey || highway -> service || last_edit_version -> 1\n28000416000000 && 37.8279922,-122.4243622:37.8279428,-122.4243196 && last_edit_user_name -> dpaschich || last_edit_changeset -> 50700405 || last_edit_time -> 1501440869000 || last_edit_user_id -> 621202 || highway -> footway || last_edit_version -> 4\n-28000416000000 && 37.8279428,-122.4243196:37.8279922,-122.4243622 && last_edit_user_name -> dpaschich || last_edit_changeset -> 50700405 || last_edit_time -> 1501440869000 || last_edit_user_id -> 621202 || highway -> footway || last_edit_version -> 4\n28000072000001 && 37.8270154,-122.4240917:37.8269877,-122.4240829:37.8269669,-122.4240835:37.8269543,-122.4241045 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || highway -> service || last_edit_version -> 6\n-28000072000001 && 37.8269543,-122.4241045:37.8269669,-122.4240835:37.8269877,-122.4240829:37.8270154,-122.4240917 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || highway -> service || last_edit_version -> 6\n28000072000002 && 37.8269543,-122.4241045:37.8269295,-122.4240948:37.8269102,-122.4240852:37.8268929,-122.4240745:37.8268792,-122.4240632:37.8268685,-122.424052:37.8268558,-122.4240366:37.8267221,-122.4238051:37.8266966,-122.4237621:37.8266691,-122.4237203:37.8265962,-122.4236215 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || highway -> service || last_edit_version -> 6\n-28000072000002 && 37.8265962,-122.4236215:37.8266691,-122.4237203:37.8266966,-122.4237621:37.8267221,-122.4238051:37.8268558,-122.4240366:37.8268685,-122.424052:37.8268792,-122.4240632:37.8268929,-122.4240745:37.8269102,-122.4240852:37.8269295,-122.4240948:37.8269543,-122.4241045 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> permissive || last_edit_time -> 1546755475000 || last_edit_user_id -> 14293 || highway -> service || last_edit_version -> 6\n314192472000000 && 37.8270774,-122.4243445:37.8270479,-122.4242927 && last_edit_user_name -> dchiles || last_edit_changeset -> 26984131 || last_edit_time -> 1416781373000 || last_edit_user_id -> 153669 || bridge -> yes || highway -> footway || last_edit_version -> 1\n-314192472000000 && 37.8270479,-122.4242927:37.8270774,-122.4243445 && last_edit_user_name -> dchiles || last_edit_changeset -> 26984131 || last_edit_time -> 1416781373000 || last_edit_user_id -> 153669 || bridge -> yes || highway -> footway || last_edit_version -> 1\n27998972000001 && 37.8268181,-122.421603:37.8268908,-122.4217373:37.8270224,-122.4219712:37.8270442,-122.4220131 && last_edit_user_name -> nithinkamath || last_edit_changeset -> 12663035 || access -> permissive || last_edit_time -> 1344465780000 || last_edit_user_id -> 573196 || name -> East Road || highway -> service || last_edit_version -> 3\n-27998972000001 && 37.8270442,-122.4220131:37.8270224,-122.4219712:37.8268908,-122.4217373:37.8268181,-122.421603 && last_edit_user_name -> nithinkamath || last_edit_changeset -> 12663035 || access -> permissive || last_edit_time -> 1344465780000 || last_edit_user_id -> 573196 || name -> East Road || highway -> service || last_edit_version -> 3\n27998972000002 && 37.8270442,-122.4220131:37.8271046,-122.4221259:37.8271629,-122.4222296 && last_edit_user_name -> nithinkamath || last_edit_changeset -> 12663035 || access -> permissive || last_edit_time -> 1344465780000 || last_edit_user_id -> 573196 || name -> East Road || highway -> service || last_edit_version -> 3\n-27998972000002 && 37.8271629,-122.4222296:37.8271046,-122.4221259:37.8270442,-122.4220131 && last_edit_user_name -> nithinkamath || last_edit_changeset -> 12663035 || access -> permissive || last_edit_time -> 1344465780000 || last_edit_user_id -> 573196 || name -> East Road || highway -> service || last_edit_version -> 3\n629121015000000 && 37.8263319,-122.4221365:37.8263806,-122.4221839 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || highway -> footway || last_edit_version -> 1\n-629121015000000 && 37.8263806,-122.4221839:37.8263319,-122.4221365 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || highway -> footway || last_edit_version -> 1\n27998976000001 && 37.8273227,-122.4225384:37.8274359,-122.4227538:37.827516,-122.4229151:37.8275768,-122.4230619 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n-27998976000001 && 37.8275768,-122.4230619:37.827516,-122.4229151:37.8274359,-122.4227538:37.8273227,-122.4225384 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n27998976000002 && 37.8275768,-122.4230619:37.8275941,-122.4231038:37.8276627,-122.423267:37.8277451,-122.42347:37.8277604,-122.4235646 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n-27998976000002 && 37.8277604,-122.4235646:37.8277451,-122.42347:37.8276627,-122.423267:37.8275941,-122.4231038:37.8275768,-122.4230619 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n27998976000003 && 37.8277604,-122.4235646:37.8277276,-122.4236738:37.82772,-122.4237782:37.8277536,-122.4238891:37.8278635,-122.4240707:37.827942,-122.4242253:37.8279893,-122.424326:37.8279922,-122.4243622 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n-27998976000003 && 37.8279922,-122.4243622:37.8279893,-122.424326:37.827942,-122.4242253:37.8278635,-122.4240707:37.8277536,-122.4238891:37.82772,-122.4237782:37.8277276,-122.4236738:37.8277604,-122.4235646 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n27998976000004 && 37.8279922,-122.4243622:37.8279971,-122.4244226:37.8279971,-122.4245256:37.82797,-122.4246114:37.8279582,-122.4246659 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n-27998976000004 && 37.8279582,-122.4246659:37.82797,-122.4246114:37.8279971,-122.4245256:37.8279971,-122.4244226:37.8279922,-122.4243622 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n27998976000005 && 37.8279582,-122.4246659:37.8279545,-122.4247254:37.8279432,-122.4248882:37.8278803,-122.4249867:37.827774,-122.4249791 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n-27998976000005 && 37.827774,-122.4249791:37.8278803,-122.4249867:37.8279432,-122.4248882:37.8279545,-122.4247254:37.8279582,-122.4246659 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || access -> permissive || last_edit_time -> 1322753284000 || last_edit_user_id -> 381909 || name -> East Road || source -> survey || highway -> service || last_edit_version -> 11\n27998980000000 && 37.8271609,-122.4222329:37.8271132,-122.42221 && last_edit_user_name -> RAW || last_edit_changeset -> 15448995 || last_edit_time -> 1363905028000 || last_edit_user_id -> 63807 || bridge -> yes || highway -> footway || last_edit_version -> 4 || layer -> 2\n-27998980000000 && 37.8271132,-122.42221:37.8271609,-122.4222329 && last_edit_user_name -> RAW || last_edit_changeset -> 15448995 || last_edit_time -> 1363905028000 || last_edit_user_id -> 63807 || bridge -> yes || highway -> footway || last_edit_version -> 4 || layer -> 2\n27989501000000 && 37.8264021,-122.4208428:37.8262588,-122.4206588:37.8261186,-122.4204998:37.8257539,-122.4204073:37.8257395,-122.420403:37.8256169,-122.4204726:37.8255235,-122.4205092:37.8254354,-122.4205599:37.8254083,-122.4205836:37.8252464,-122.4207352:37.8252356,-122.4207497:37.8251927,-122.4208583:37.8251794,-122.4208966:37.8251061,-122.42115:37.8251022,-122.4211649:37.8251293,-122.421207 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755474000 || last_edit_user_id -> 14293 || name -> The Agave Trail (Seasonal) || source -> survey || highway -> footway || last_edit_version -> 7\n-27989501000000 && 37.8251293,-122.421207:37.8251022,-122.4211649:37.8251061,-122.42115:37.8251794,-122.4208966:37.8251927,-122.4208583:37.8252356,-122.4207497:37.8252464,-122.4207352:37.8254083,-122.4205836:37.8254354,-122.4205599:37.8255235,-122.4205092:37.8256169,-122.4204726:37.8257395,-122.420403:37.8257539,-122.4204073:37.8261186,-122.4204998:37.8262588,-122.4206588:37.8264021,-122.4208428 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755474000 || last_edit_user_id -> 14293 || name -> The Agave Trail (Seasonal) || source -> survey || highway -> footway || last_edit_version -> 7\n629120566000000 && 37.8261859,-122.4222843:37.826102,-122.4222069 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050340 || last_edit_time -> 1538261966000 || last_edit_user_id -> 9446 || highway -> steps || last_edit_version -> 1\n-629120566000000 && 37.826102,-122.4222069:37.8261859,-122.4222843 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050340 || last_edit_time -> 1538261966000 || last_edit_user_id -> 9446 || highway -> steps || last_edit_version -> 1\n28000214000001 && 37.8268181,-122.421603:37.826957,-122.421478:37.8270274,-122.4214142 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n-28000214000001 && 37.8270274,-122.4214142:37.826957,-122.421478:37.8268181,-122.421603 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n28000214000002 && 37.8270274,-122.4214142:37.8270426,-122.4214009:37.8267594,-122.4209676:37.8267317,-122.420966:37.8267031,-122.4210044 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n-28000214000002 && 37.8267031,-122.4210044:37.8267317,-122.420966:37.8267594,-122.4209676:37.8270426,-122.4214009:37.8270274,-122.4214142 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n28000214000003 && 37.8267031,-122.4210044:37.8266576,-122.4210652:37.8265016,-122.420877:37.8264889,-122.4208486:37.8264623,-122.4208766:37.826416,-122.420818:37.8264021,-122.4208428 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n-28000214000003 && 37.8264021,-122.4208428:37.826416,-122.420818:37.8264623,-122.4208766:37.8264889,-122.4208486:37.8265016,-122.420877:37.8266576,-122.4210652:37.8267031,-122.4210044 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n28000214000004 && 37.8264021,-122.4208428:37.8263926,-122.4208604:37.8262666,-122.4211063:37.8263971,-122.421303:37.8264185,-122.4213353:37.8266246,-122.4212888:37.8266819,-122.4213756:37.8268112,-122.4216098:37.8268181,-122.421603 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n-28000214000004 && 37.8268181,-122.421603:37.8268112,-122.4216098:37.8266819,-122.4213756:37.8266246,-122.4212888:37.8264185,-122.4213353:37.8263971,-122.421303:37.8262666,-122.4211063:37.8263926,-122.4208604:37.8264021,-122.4208428 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n192237295000000 && 37.8271132,-122.42221:37.8270681,-122.4222388 && last_edit_user_name -> achims311 || last_edit_changeset -> 13998200 || last_edit_time -> 1353674169000 || last_edit_user_id -> 52898 || source -> bing || highway -> footway || last_edit_version -> 1\n-192237295000000 && 37.8270681,-122.4222388:37.8271132,-122.42221 && last_edit_user_name -> achims311 || last_edit_changeset -> 13998200 || last_edit_time -> 1353674169000 || last_edit_user_id -> 52898 || source -> bing || highway -> footway || last_edit_version -> 1\n128245364000000 && 37.8271206,-122.4239856:37.8270154,-122.4240917 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664590000 || last_edit_user_id -> 27824 || highway -> steps || last_edit_version -> 1\n-128245364000000 && 37.8270154,-122.4240917:37.8271206,-122.4239856 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664590000 || last_edit_user_id -> 27824 || highway -> steps || last_edit_version -> 1\n314192473000000 && 37.8270479,-122.4242927:37.827025,-122.4242758:37.8269274,-122.4241733:37.8269514,-122.4241288:37.8269543,-122.4241045 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || highway -> footway || last_edit_version -> 2\n-314192473000000 && 37.8269543,-122.4241045:37.8269514,-122.4241288:37.8269274,-122.4241733:37.827025,-122.4242758:37.8270479,-122.4242927 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || highway -> footway || last_edit_version -> 2\n# Areas\n24433389000000 && 37.8281718,-122.4253998:37.8280361,-122.4255291:37.8279852,-122.4254435:37.8279176,-122.425508:37.8278166,-122.4253382:37.8278507,-122.4253057:37.8277749,-122.425178:37.8278432,-122.4251129:37.82797,-122.4253263:37.828071,-122.4252301 && last_edit_user_name -> Edward || last_edit_changeset -> 61569480 || old_name -> Military Industries and Workshops Building || last_edit_time -> 1534005386000 || last_edit_user_id -> 364 || name -> Model Industries Building || disused -> yes || last_edit_version -> 4 || wikidata -> Q1142654 || building -> yes\n660870452000000 && 37.8275819,-122.4238976:37.827596,-122.4238812:37.8276045,-122.4238624:37.8276087,-122.4238396:37.8276074,-122.4238178:37.8276007,-122.4237958:37.8275904,-122.4237791:37.8275771,-122.4237669:37.8275621,-122.4237596:37.8275423,-122.4237581:37.8275248,-122.4237637:37.8275114,-122.4237738:37.8274985,-122.4237911:37.827491,-122.4238102:37.8274881,-122.4238312:37.82749,-122.4238535:37.8274965,-122.423873:37.8275066,-122.4238893:37.8275191,-122.4239009:37.8275327,-122.4239078:37.8275488,-122.4239106:37.827567,-122.4239068 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66062793 || man_made -> water_tower || last_edit_time -> 1546753099000 || last_edit_user_id -> 14293 || name -> Water Tower || disused -> yes || last_edit_version -> 1 || building -> yes\n27999864000000 && 37.8266033,-122.4207207:37.8265198,-122.4208095:37.8266589,-122.4210191:37.8266908,-122.4209852:37.8267423,-122.4209305:37.8266704,-122.4208218 && area -> yes || last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 70368429 || man_made -> pier || last_edit_time -> 1558116233000 || last_edit_user_id -> 119881 || last_edit_version -> 8\n128245373000000 && 37.8267327,-122.4231328:37.8266988,-122.4231768:37.8266479,-122.4232435:37.8263371,-122.4228632:37.8264063,-122.4227727:37.8262972,-122.4226392:37.8263178,-122.4226124:37.8265359,-122.422327:37.8268456,-122.4227066:37.8267808,-122.4227913:37.8268908,-122.422926:37.8268581,-122.4229687 && last_edit_user_name -> eduaddad || name:pt -> Prisão principal || last_edit_changeset -> 73895096 || alt_name -> Cellhouse || historic -> yes || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Main Prison || last_edit_version -> 4 || building -> yes\n232326056000000 && 37.8276009,-122.4235219:37.8275865,-122.4234812:37.8275419,-122.4235064:37.8275562,-122.4235471 && last_edit_user_name -> Nautic || last_edit_changeset -> 17214523 || last_edit_time -> 1375620456000 || last_edit_user_id -> 449569 || last_edit_version -> 1 || building -> yes\n99202294000000 && 37.8262455,-122.4221663:37.826249,-122.4221588:37.8262502,-122.4221506:37.8262499,-122.4221461:37.8262475,-122.4221382:37.8262415,-122.4221304:37.8262357,-122.4221273:37.8262285,-122.4221268:37.8262226,-122.4221292:37.8262178,-122.4221337:37.826214,-122.4221406:37.8262124,-122.4221487:37.826213,-122.4221568:37.8262163,-122.4221652:37.8262213,-122.4221709:37.8262297,-122.4221744:37.8262366,-122.4221735:37.8262429,-122.4221694 && last_edit_user_name -> Edward || last_edit_changeset -> 49588642 || alt_name -> United States Coast Guard Lighthouse || man_made -> lighthouse || last_edit_time -> 1497612241000 || last_edit_user_id -> 364 || name -> Alcatraz Island Lighthouse || last_edit_version -> 5 || wikidata -> Q4712967 || building -> yes || start_date -> 1909\n28000041000000 && 37.8272682,-122.4236113:37.827237,-122.4235831:37.8272679,-122.4235284:37.827299,-122.4235567 && last_edit_user_name -> Manu1400 || last_edit_changeset -> 15874039 || last_edit_time -> 1366994044000 || last_edit_user_id -> 181135 || amenity -> mortuary || name -> Morgue || disused -> yes || last_edit_version -> 4 || building -> yes\n27996723000000 && 37.828194,-122.4241906:37.8281081,-122.4242756:37.8279241,-122.4239776:37.82801,-122.4238926 && last_edit_user_name -> eduaddad || name:pt -> Quarto principal || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Quartermaster || last_edit_version -> 4 || building -> yes\n59621863000000 && 37.8256032,-122.4222034:37.8256995,-122.4221046:37.8257007,-122.4219819:37.8256934,-122.4219179:37.8255862,-122.4217157:37.8256532,-122.4216594:37.82563,-122.4216108:37.8255825,-122.4216208:37.8255002,-122.421468:37.825432,-122.421424:37.8253491,-122.4214665:37.8252948,-122.4215568:37.8252887,-122.4217273:37.8253722,-122.4218716:37.8255551,-122.4221903 && last_edit_user_name -> nvk || last_edit_changeset -> 33170040 || historic -> ruins || natural -> scree || last_edit_time -> 1438919870000 || last_edit_user_id -> 131996 || name -> Rubble Pile || last_edit_version -> 2\n151291054000000 && 37.827092,-122.423515:37.8271835,-122.4235957:37.8274396,-122.423822:37.827279,-122.4241222:37.8271206,-122.4239856:37.8266955,-122.4236195:37.8268332,-122.4233348:37.8266988,-122.4231768:37.8267327,-122.4231328:37.8270223,-122.4234875:37.8270451,-122.4234575 && area -> yes || wheelchair -> no || last_edit_changeset -> 73895096 || source -> yahoo || disused -> yes || last_edit_user_name -> eduaddad || name:pt -> Quintal de recreação || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Recreation Yard || last_edit_version -> 4 || leisure -> pitch || sport -> multi\n27996759000000 && 37.8271238,-122.4222642:37.8272374,-122.4224816:37.8272245,-122.4224936:37.8272731,-122.4225776:37.8273895,-122.4224703:37.8272872,-122.4222745:37.8272515,-122.4223041:37.8271965,-122.422198:37.8271676,-122.4222218:37.8271629,-122.4222296:37.8271609,-122.4222329 && last_edit_user_name -> eduaddad || name:pt -> Porto de Sally || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Sally Port || last_edit_version -> 9 || building -> yes || layer -> 1\n27996743000000 && 37.8269854,-122.421387:37.826924,-122.4214379:37.8268678,-122.4213292:37.8269292,-122.4212783 && last_edit_user_name -> wheelmap_visitor || toilets:wheelchair -> yes || wheelchair -> yes || last_edit_changeset -> 36107842 || last_edit_time -> 1450802771000 || last_edit_user_id -> 290680 || amenity -> toilets || name -> Restrooms || last_edit_version -> 5 || building -> yes\n59621862000000 && 37.8255826,-122.4213123:37.8256174,-122.4212683:37.8256432,-122.4211879:37.8257016,-122.421124:37.8257145,-122.4210042:37.8256668,-122.4209:37.8258338,-122.4208844:37.8259249,-122.420994:37.8260155,-122.4211065:37.826353,-122.4213814:37.8263971,-122.421303:37.8262666,-122.4211063:37.8263926,-122.4208604:37.8261154,-122.4205159:37.8257483,-122.4204259:37.825604,-122.4204938:37.8255239,-122.4205283:37.8255273,-122.4205376:37.8254534,-122.4205801:37.8254509,-122.4205729:37.8254152,-122.4205947:37.8252491,-122.4207537:37.8252416,-122.4207709:37.8252492,-122.4207754:37.8252237,-122.4208387:37.8252179,-122.4208352:37.8251895,-122.4209076:37.8251206,-122.4211489:37.8251199,-122.4211532:37.8251206,-122.4211573:37.8251234,-122.4211603:37.8251274,-122.421161:37.8251308,-122.4211587:37.825133,-122.4211545:37.8251455,-122.4211147:37.8251654,-122.4210783:37.8251959,-122.4210144:37.8252167,-122.4209844:37.8252405,-122.4209543:37.825309,-122.4208745:37.82534,-122.4208431:37.8254566,-122.4207839:37.8255579,-122.420737:37.8255848,-122.4207253:37.8256559,-122.4207024:37.8256668,-122.4207837:37.8256273,-122.420809:37.8255835,-122.4208507:37.8255278,-122.4209076:37.8254786,-122.4209673:37.8254408,-122.4210174:37.8254061,-122.4210676:37.8253765,-122.4211244:37.825355,-122.4211833:37.8253845,-122.4212585 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || historic -> ruins || natural -> scree || last_edit_time -> 1546755474000 || last_edit_user_id -> 14293 || name -> Rubble Pile || last_edit_version -> 4\n27996789000000 && 37.826337,-122.4220318:37.8262225,-122.4219119:37.8262519,-122.4218669:37.8262392,-122.4218535:37.8262752,-122.4217985:37.8263609,-122.4218884:37.8263689,-122.4218761:37.8264103,-122.4219195 && last_edit_user_name -> Edward || last_edit_changeset -> 49588642 || historic -> ruins || last_edit_time -> 1497612222000 || last_edit_user_id -> 364 || name -> Warden's House || disused -> yes || last_edit_version -> 6 || wikidata -> Q1142648 || building -> yes || start_date -> 1934\n28021912000000 && 37.8270247,-122.4241767:37.8270394,-122.4241437:37.826976,-122.4240988:37.8269614,-122.4241318 && last_edit_user_name -> will l || last_edit_changeset -> 585867 || last_edit_time -> 1225033203000 || last_edit_user_id -> 35195 || last_edit_version -> 3 || created_by -> Potlatch 0.10e || building -> yes\n27996792000000 && 37.8264665,-122.4221448:37.8264081,-122.4220876:37.8264253,-122.4220595:37.8264461,-122.4220798:37.8264516,-122.4220708:37.8264892,-122.4221076 && last_edit_user_name -> wheelmap_visitor || toilets:wheelchair -> yes || wheelchair -> yes || last_edit_changeset -> 36107842 || last_edit_time -> 1450802731000 || last_edit_user_id -> 290680 || amenity -> toilets || fee -> no || name -> Restrooms || last_edit_version -> 9 || building -> yes\n27996775000000 && 37.8272677,-122.4226159:37.8273186,-122.4225736:37.8274149,-122.4227594:37.8273641,-122.4228016 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224954042000 || last_edit_user_id -> 35195 || name -> Electric Shop || last_edit_version -> 3 || created_by -> Potlatch 0.10e || building -> yes\n27996842000000 && 37.8257351,-122.4221372:37.8257192,-122.4219714:37.8257069,-122.4219113:37.8256076,-122.4217186:37.8256695,-122.4216631:37.8256361,-122.4215908:37.825583,-122.4216015:37.8255055,-122.4214552:37.8254333,-122.4214099:37.8253431,-122.4214434:37.8252912,-122.4215249:37.8253312,-122.4212359:37.8253762,-122.4212779:37.8255871,-122.4213427:37.8256336,-122.4212818:37.8256581,-122.4212001:37.8257184,-122.4211315:37.8257309,-122.4210036:37.8256911,-122.4209166:37.8258018,-122.4209095:37.8258258,-122.420908:37.8259144,-122.421014:37.8260075,-122.421124:37.8263461,-122.4213991:37.8263899,-122.4215455:37.8263646,-122.421571:37.8263469,-122.4215926:37.8261538,-122.4217471:37.8261191,-122.4217666:37.8260852,-122.4217843:37.8260352,-122.4218334:37.8260103,-122.4218633:37.82597,-122.4219276 && area -> yes || last_edit_user_name -> Azlux || last_edit_changeset -> 47452285 || surface -> concrete || last_edit_time -> 1491332262000 || last_edit_user_id -> 5586237 || name -> Parade Ground || highway -> pedestrian || last_edit_version -> 7\n128245367000000 && 37.8263178,-122.4226124:37.8262224,-122.4224956:37.8264405,-122.4222102:37.8265359,-122.422327 && last_edit_user_name -> eduaddad || name:pt -> Bloco de Administração || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Administration Block || last_edit_version -> 4 || building -> yes\n27996721000000 && 37.8280874,-122.4246186:37.8280944,-122.4243261:37.8281802,-122.4242915:37.8282267,-122.4242933:37.8282228,-122.4244599:37.828317,-122.4244635:37.828315,-122.4245438:37.8282302,-122.4245405:37.8282282,-122.4246255:37.828209,-122.4246248:37.828205,-122.4247923:37.8280942,-122.424788:37.8280983,-122.424619 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697601000 || last_edit_user_id -> 14293 || name -> Powerhouse || last_edit_version -> 5 || wikidata -> Q1142612 || building -> yes\n24433395000000 && 37.8277813,-122.4249597:37.8278188,-122.4248746:37.8278604,-122.4247795:37.8278732,-122.4247885:37.8278827,-122.4247668:37.827125,-122.4242353:37.8271159,-122.424256:37.8271287,-122.424265:37.8270878,-122.4243583:37.8270503,-122.4244436 && last_edit_user_name -> eduaddad || name:pt -> Construção de novas indústrias || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> New Industries Building || disused -> yes || last_edit_version -> 7 || building -> yes\n24433437000000 && 37.8267327,-122.4231328:37.8268581,-122.4229687:37.827148,-122.4233237:37.8271271,-122.4233509:37.8271737,-122.423408:37.827092,-122.423515:37.8270451,-122.4234575:37.8270223,-122.4234875 && last_edit_user_name -> eduaddad || name:pt -> Sala de jantar || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Dining Hall || last_edit_version -> 10 || building -> yes\n396629347000000 && 37.8276814,-122.4244759:37.8275329,-122.4242608:37.8276187,-122.4241659:37.8277671,-122.424381 && area -> yes || last_edit_user_name -> Johnny Mapperseed || last_edit_changeset -> 37071222 || surface -> paved || last_edit_time -> 1454890655000 || last_edit_user_id -> 1723055 || last_edit_version -> 1\n27996732000000 && 37.8278343,-122.4232984:37.8277424,-122.4233639:37.8276054,-122.4230558:37.8276973,-122.4229903 && last_edit_user_name -> eduaddad || name:pt -> Post Exchange & Officers' Club || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Post Exchange & Officers' Club || disused -> yes || last_edit_version -> 7 || building -> yes\n24433344000000 && 37.8258434,-122.4230873:37.8258231,-122.4229929:37.8257756,-122.4229328:37.8257214,-122.4227869:37.8256265,-122.4227526:37.8255092,-122.4227072:37.8254955,-122.4226802:37.8254881,-122.4226473:37.8254888,-122.4225911:37.8254677,-122.4225276:37.8254698,-122.4225087:37.8254502,-122.4224522:37.8253078,-122.4221432:37.8251465,-122.4218792:37.82511,-122.421809:37.8250854,-122.4217247:37.8250726,-122.4216129:37.8250787,-122.421493:37.8251055,-122.4211995:37.8250846,-122.421176:37.8251705,-122.4208744:37.8251842,-122.4208369:37.8252309,-122.4207283:37.8252773,-122.4206896:37.8253961,-122.4205712:37.8254269,-122.4205455:37.8254308,-122.4205432:37.8254639,-122.4205238:37.8256533,-122.4204309:37.8257327,-122.4203839:37.8258285,-122.4204073:37.825891,-122.4204324:37.8261174,-122.4204776:37.8261966,-122.4205613:37.8262384,-122.420595:37.8262743,-122.4206387:37.826416,-122.420818:37.8264623,-122.4208766:37.8264889,-122.4208486:37.8265016,-122.420877:37.8266576,-122.4210652:37.8267031,-122.4210044:37.8267317,-122.420966:37.8267594,-122.4209676:37.8270426,-122.4214009:37.8270274,-122.4214142:37.826957,-122.421478:37.8271293,-122.4218:37.8270678,-122.4218551:37.827098,-122.4219104:37.8271993,-122.4219715:37.8272468,-122.4220831:37.8272875,-122.4222119:37.827295,-122.4222585:37.8274079,-122.4224738:37.8274786,-122.4226689:37.8277624,-122.422991:37.8278503,-122.4231184:37.8279927,-122.423248:37.8280748,-122.4234665:37.8280889,-122.4235093:37.828102,-122.423556:37.8281056,-122.4236155:37.8281077,-122.4236952:37.8281054,-122.4238064:37.8281905,-122.4239048:37.8282311,-122.4239992:37.8283112,-122.4240915:37.8283586,-122.4241774:37.8283593,-122.424289:37.8283107,-122.4244188:37.8283586,-122.4246237:37.8283518,-122.4247439:37.8283247,-122.4248039:37.8283722,-122.4248898:37.8283586,-122.4249756:37.8283654,-122.4250357:37.8283857,-122.4250958:37.8283247,-122.4252073:37.8283654,-122.425276:37.8283247,-122.4254477:37.8282513,-122.4255674:37.828174,-122.4256104:37.8281195,-122.4256233:37.8280807,-122.4255997:37.8280425,-122.4256157:37.827954,-122.4255445:37.827884,-122.425561:37.8277791,-122.4253253:37.8278093,-122.425264:37.8277688,-122.4251902:37.8277078,-122.4251387:37.8276739,-122.4251215:37.827579,-122.4250357:37.8275443,-122.4250204:37.8275161,-122.4250375:37.827341,-122.4249618:37.8272552,-122.4249189:37.8271703,-122.4248737:37.8271174,-122.4248392:37.8270945,-122.4248205:37.8269523,-122.4246717:37.8268793,-122.424465:37.8267926,-122.4244949:37.8267315,-122.4245207:37.8267112,-122.424555:37.8266999,-122.4245634:37.8266871,-122.4245682:37.8266755,-122.4245682:37.8266638,-122.4245647:37.8266339,-122.4245303:37.8266318,-122.4245057:37.8266027,-122.4244864:37.8265529,-122.4243945:37.826575,-122.4243214:37.826565,-122.4243015:37.8265699,-122.4242874:37.826585,-122.4242512:37.8265692,-122.4242024:37.8265494,-122.4241953:37.8264875,-122.4240229:37.8265183,-122.4239573:37.82653,-122.4238988:37.8265283,-122.423846:37.8264904,-122.4237927:37.8264491,-122.4237511:37.8264056,-122.42373:37.8262488,-122.4237363:37.8261621,-122.4237654:37.8260875,-122.4237053:37.8260265,-122.4235336:37.8259519,-122.4233534:37.8258841,-122.4232762:37.8258705,-122.4231903 && boundary -> national_park || last_edit_changeset -> 71376374 || natural -> coastline || source -> yahoo || gnis:county_id -> 075 || last_edit_user_name -> KaL3288 || gnis:created -> 01/19/1981 || alt_name -> Alcatraz || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Alcatraz Island || gnis:feature_id -> 218080 || gnis:state_id -> 06 || place -> islet || wikipedia -> en:Alcatraz Island || last_edit_version -> 18 || wikidata -> Q131354 || ele -> 37\n232326055000000 && 37.8262097,-122.4221137:37.8261656,-122.422067:37.8261393,-122.4221067:37.8261834,-122.4221535 && last_edit_user_name -> Nautic || last_edit_changeset -> 17214523 || last_edit_time -> 1375620456000 || last_edit_user_id -> 449569 || last_edit_version -> 1 || building -> yes\n28000214000000 && 37.8262666,-122.4211063:37.8263971,-122.421303:37.8264185,-122.4213353:37.8266246,-122.4212888:37.8266819,-122.4213756:37.8268112,-122.4216098:37.8268181,-122.421603:37.826957,-122.421478:37.8270274,-122.4214142:37.8270426,-122.4214009:37.8267594,-122.4209676:37.8267317,-122.420966:37.8267031,-122.4210044:37.8266576,-122.4210652:37.8265016,-122.420877:37.8264889,-122.4208486:37.8264623,-122.4208766:37.826416,-122.420818:37.8264021,-122.4208428:37.8263926,-122.4208604 && area -> yes || last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || surface -> concrete || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || name -> Dock || highway -> pedestrian || last_edit_version -> 9\n698078900000000 && 37.8257327,-122.4203839:37.8258285,-122.4204073:37.825891,-122.4204324:37.8261174,-122.4204776:37.8261966,-122.4205613:37.8262384,-122.420595:37.8264623,-122.4208766:37.8264889,-122.4208486:37.8265016,-122.420877:37.8266576,-122.4210652:37.8267317,-122.420966:37.8267594,-122.4209676:37.8270426,-122.4214009:37.8270274,-122.4214142:37.826957,-122.421478:37.8271293,-122.4218:37.8270678,-122.4218551:37.827098,-122.4219104:37.8271993,-122.4219715:37.8272468,-122.4220831:37.8272875,-122.4222119:37.827295,-122.4222585:37.8274079,-122.4224738:37.8274786,-122.4226689:37.8277624,-122.422991:37.8278503,-122.4231184:37.8279927,-122.423248:37.8280748,-122.4234665:37.8280889,-122.4235093:37.828102,-122.423556:37.8281056,-122.4236155:37.8281077,-122.4236952:37.8281054,-122.4238064:37.8281905,-122.4239048:37.8282311,-122.4239992:37.8283112,-122.4240915:37.8283586,-122.4241774:37.8283593,-122.424289:37.8283107,-122.4244188:37.8283586,-122.4246237:37.8283518,-122.4247439:37.8283247,-122.4248039:37.8283722,-122.4248898:37.8283586,-122.4249756:37.8283654,-122.4250357:37.8283857,-122.4250958:37.8283247,-122.4252073:37.8283654,-122.425276:37.8283247,-122.4254477:37.8282513,-122.4255674:37.828174,-122.4256104:37.8281195,-122.4256233:37.8280807,-122.4255997:37.8280425,-122.4256157:37.827954,-122.4255445:37.827884,-122.425561:37.8277791,-122.4253253:37.8278093,-122.425264:37.8277688,-122.4251902:37.8277078,-122.4251387:37.8276739,-122.4251215:37.827579,-122.4250357:37.8275443,-122.4250204:37.8275161,-122.4250375:37.827341,-122.4249618:37.8271703,-122.4248737:37.8271174,-122.4248392:37.8270945,-122.4248205:37.8269523,-122.4246717:37.8268793,-122.424465:37.8267926,-122.4244949:37.8267315,-122.4245207:37.8267112,-122.424555:37.8266999,-122.4245634:37.8266871,-122.4245682:37.8266755,-122.4245682:37.8266638,-122.4245647:37.8266339,-122.4245303:37.8266318,-122.4245057:37.8266027,-122.4244864:37.8265529,-122.4243945:37.826575,-122.4243214:37.826565,-122.4243015:37.8265699,-122.4242874:37.826585,-122.4242512:37.8265692,-122.4242024:37.8265494,-122.4241953:37.8264875,-122.4240229:37.8265183,-122.4239573:37.82653,-122.4238988:37.8265283,-122.423846:37.8264904,-122.4237927:37.8264491,-122.4237511:37.8264056,-122.42373:37.8262488,-122.4237363:37.8261621,-122.4237654:37.8260875,-122.4237053:37.8260265,-122.4235336:37.8259519,-122.4233534:37.8258841,-122.4232762:37.8258705,-122.4231903:37.8258434,-122.4230873:37.8258231,-122.4229929:37.8257756,-122.4229328:37.8257214,-122.4227869:37.8256265,-122.4227526:37.8255092,-122.4227072:37.8254955,-122.4226802:37.8254881,-122.4226473:37.8254888,-122.4225911:37.8254677,-122.4225276:37.8254698,-122.4225087:37.8254502,-122.4224522:37.8253078,-122.4221432:37.8251465,-122.4218792:37.82511,-122.421809:37.8250854,-122.4217247:37.8250726,-122.4216129:37.8250787,-122.421493:37.8251055,-122.4211995:37.8250846,-122.421176:37.8251705,-122.4208744:37.8251842,-122.4208369:37.8252309,-122.4207283:37.8252773,-122.4206896:37.8253961,-122.4205712:37.8254308,-122.4205432:37.8254639,-122.4205238:37.8256533,-122.4204309 && last_edit_user_name -> eduaddad || name:pt -> Ilha de Alcatraz || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Alcatraz Island || last_edit_version -> 3 || leisure -> park || name:es -> Isla de Alcatraz\n128245368000000 && 37.8264062,-122.422042:37.8264517,-122.4219629:37.8265746,-122.4220782:37.8266339,-122.422144:37.8266509,-122.4221225:37.8268299,-122.4223357:37.8268987,-122.4224323:37.826904,-122.4224631:37.8268426,-122.4225275:37.8266731,-122.4223478:37.8264665,-122.4221448:37.8264892,-122.4221076:37.8264516,-122.4220708:37.8264461,-122.4220798:37.8264253,-122.4220595 && last_edit_user_name -> Nautic || last_edit_changeset -> 17223234 || last_edit_time -> 1375682658000 || last_edit_user_id -> 449569 || last_edit_version -> 2 || leisure -> garden\n232403925000000 && 37.8263359,-122.422198:37.8263275,-122.4221888:37.8263342,-122.4221796:37.8262984,-122.4221378:37.8262544,-122.4222046:37.8262486,-122.4221987:37.8261921,-122.4222817:37.8262055,-122.4223525:37.8262139,-122.4223532 && last_edit_user_name -> Nautic || last_edit_changeset -> 17223234 || last_edit_time -> 1375682658000 || last_edit_user_id -> 449569 || landuse -> grass || last_edit_version -> 1\n295140461000000 && 37.8271993,-122.4219715:37.827098,-122.4219104:37.8270678,-122.4218551:37.8271293,-122.4218:37.826957,-122.421478:37.8270274,-122.4214142:37.8270426,-122.4214009:37.8267594,-122.4209676:37.8267317,-122.420966:37.8267031,-122.4210044:37.8266576,-122.4210652:37.8265016,-122.420877:37.8264889,-122.4208486:37.8264623,-122.4208766:37.826416,-122.420818:37.8262743,-122.4206387:37.8262384,-122.420595:37.8261966,-122.4205613:37.8261174,-122.4204776:37.825891,-122.4204324:37.8258285,-122.4204073:37.8257327,-122.4203839:37.8256533,-122.4204309:37.8254639,-122.4205238:37.8254269,-122.4205455:37.8253961,-122.4205712:37.8252773,-122.4206896:37.8252309,-122.4207283:37.8251842,-122.4208369:37.8251711,-122.4208726:37.8251705,-122.4208744:37.8250846,-122.421176:37.8251055,-122.4211995:37.8250787,-122.421493:37.8250726,-122.4216129:37.8250854,-122.4217247:37.82511,-122.421809:37.8251465,-122.4218792:37.8253078,-122.4221432:37.8254502,-122.4224522:37.8254698,-122.4225087:37.8254677,-122.4225276:37.8254888,-122.4225911:37.8254881,-122.4226473:37.8254955,-122.4226802:37.8255092,-122.4227072:37.8256265,-122.4227526:37.8257214,-122.4227869:37.8257756,-122.4229328:37.8258231,-122.4229929:37.8258434,-122.4230873:37.8258705,-122.4231903:37.8258841,-122.4232762:37.8259519,-122.4233534:37.8260265,-122.4235336:37.8260875,-122.4237053:37.8261621,-122.4237654:37.8262488,-122.4237363:37.8264056,-122.42373:37.8264491,-122.4237511:37.8264904,-122.4237927:37.8265283,-122.423846:37.82653,-122.4238988:37.8265183,-122.4239573:37.8264875,-122.4240229:37.8265494,-122.4241953:37.8265692,-122.4242024:37.826585,-122.4242512:37.8265699,-122.4242874:37.826565,-122.4243015:37.826575,-122.4243214:37.8265529,-122.4243945:37.8266027,-122.4244864:37.8266318,-122.4245057:37.8266339,-122.4245303:37.8266638,-122.4245647:37.8266755,-122.4245682:37.8266871,-122.4245682:37.8266999,-122.4245634:37.8267112,-122.424555:37.8267315,-122.4245207:37.8267926,-122.4244949:37.8268793,-122.424465:37.8269523,-122.4246717:37.8270945,-122.4248205:37.8271174,-122.4248392:37.8271703,-122.4248737:37.8272552,-122.4249189:37.827341,-122.4249618:37.8275161,-122.4250375:37.8275443,-122.4250204:37.827579,-122.4250357:37.8276739,-122.4251215:37.8277078,-122.4251387:37.8277688,-122.4251902:37.8278093,-122.425264:37.8277791,-122.4253253:37.827884,-122.425561:37.827954,-122.4255445:37.8280425,-122.4256157:37.8280807,-122.4255997:37.8281195,-122.4256233:37.828174,-122.4256104:37.8282513,-122.4255674:37.8283247,-122.4254477:37.8283654,-122.425276:37.8283247,-122.4252073:37.8283857,-122.4250958:37.8283654,-122.4250357:37.8283586,-122.4249756:37.8283722,-122.4248898:37.8283247,-122.4248039:37.8283518,-122.4247439:37.8283586,-122.4246237:37.8283107,-122.4244188:37.8283593,-122.424289:37.8283586,-122.4241774:37.8283112,-122.4240915:37.8282311,-122.4239992:37.8281905,-122.4239048:37.8281054,-122.4238064:37.8281077,-122.4236952:37.8281056,-122.4236155:37.828102,-122.423556:37.8280889,-122.4235093:37.8280748,-122.4234665:37.8279927,-122.423248:37.8278503,-122.4231184:37.8277624,-122.422991:37.8274786,-122.4226689:37.8274079,-122.4224738:37.827295,-122.4222585:37.8272875,-122.4222119:37.8272468,-122.4220831 && boundary -> national_park || last_edit_user_name -> KaL3288 || name:ko -> 앨커트래즈 섬 || last_edit_changeset -> 71376374 || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || admin_level -> 1 || name -> Alcatraz Island (GGNRA) || last_edit_version -> 6 || leisure -> common || park:type -> national_recreational_area || wikidata -> Q131354\n24617219000000 && 37.8270206,-122.4220063:37.8268901,-122.4221255:37.826553,-122.421542:37.826439,-122.4215701:37.826407,-122.421369:37.8266219,-122.4213147 && last_edit_user_name -> Edward || last_edit_changeset -> 49588498 || last_edit_time -> 1497611818000 || last_edit_user_id -> 364 || name -> Building 64 || last_edit_version -> 4 || wikidata -> Q1142634 || building -> yes\n# Lines\n99202295000000 && 37.8266988,-122.4231768:37.8268332,-122.4233348:37.8266955,-122.4236195:37.8271206,-122.4239856:37.827279,-122.4241222:37.8274396,-122.423822:37.8271835,-122.4235957:37.8272745,-122.42347:37.8270849,-122.4231766:37.8270545,-122.4231082:37.8269623,-122.4229781 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || barrier -> wall || last_edit_time -> 1314664599000 || last_edit_user_id -> 27824 || last_edit_version -> 2\n128245365000000 && 37.8274697,-122.4234167:37.8273627,-122.4231471:37.8272208,-122.4229661:37.8269189,-122.4222928 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || barrier -> wall || last_edit_time -> 1314664590000 || last_edit_user_id -> 27824 || last_edit_version -> 1\n128245369000000 && 37.8272398,-122.4226402:37.8271487,-122.4224752:37.826992,-122.4221507:37.8268924,-122.4221547 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || barrier -> wall || last_edit_time -> 1314664590000 || last_edit_user_id -> 27824 || last_edit_version -> 1\n128245372000000 && 37.8265445,-122.423597:37.8265163,-122.4236856 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || barrier -> fence || last_edit_time -> 1314664591000 || last_edit_user_id -> 27824 || last_edit_version -> 1\n232403927000000 && 37.8277451,-122.4236998:37.8277597,-122.4238259:37.8280206,-122.4242526 && last_edit_user_name -> Nautic || last_edit_changeset -> 17223234 || barrier -> wall || last_edit_time -> 1375682658000 || last_edit_user_id -> 449569 || last_edit_version -> 1\n232403928000000 && 37.8261322,-122.4218812:37.8260594,-122.4220336:37.8260332,-122.4221509:37.8261251,-122.422657 && last_edit_user_name -> Nautic || last_edit_changeset -> 17223234 || barrier -> wall || last_edit_time -> 1375682658000 || last_edit_user_id -> 449569 || last_edit_version -> 1\n232403930000000 && 37.8261435,-122.4220086:37.8260847,-122.4220927:37.8260791,-122.4221136:37.8260777,-122.4221391:37.8260831,-122.4221639:37.8260918,-122.4221829:37.8261314,-122.422226 && last_edit_user_name -> Nautic || last_edit_changeset -> 17223234 || barrier -> wall || last_edit_time -> 1375682658000 || last_edit_user_id -> 449569 || last_edit_version -> 1\n629121016000000 && 37.8263291,-122.4221048:37.8263835,-122.4221473:37.8264374,-122.422186:37.8264945,-122.4222404:37.8265266,-122.4222765 && last_edit_user_name -> Michiel M || last_edit_changeset -> 63050387 || barrier -> fence || last_edit_time -> 1538262194000 || last_edit_user_id -> 9446 || last_edit_version -> 1\n660872606000000 && 37.825136,-122.4212307:37.8251055,-122.4211995:37.8250846,-122.421176:37.8251711,-122.4208726:37.8251842,-122.4208369:37.8252309,-122.4207283:37.8252773,-122.4206896:37.8253961,-122.4205712:37.8254308,-122.4205432:37.8254639,-122.4205238:37.8256533,-122.4204309:37.8257327,-122.4203839:37.8258285,-122.4204073:37.825891,-122.4204324:37.8261174,-122.4204776:37.8261966,-122.4205613:37.8262384,-122.420595:37.8262743,-122.4206387:37.826416,-122.420818:37.8264623,-122.4208766 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 71376374 || barrier -> wall || last_edit_time -> 1560882267000 || last_edit_user_id -> 7134350 || last_edit_version -> 2\n660872945000000 && 37.8280585,-122.4246222:37.8280558,-122.4247123:37.827991,-122.4247105:37.8279795,-122.4247304:37.8279757,-122.4250288:37.8277915,-122.4250161:37.8275772,-122.4248659:37.8271931,-122.4246216:37.8270237,-122.4245035 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063123 || barrier -> fence || last_edit_time -> 1546755783000 || last_edit_user_id -> 14293 || last_edit_version -> 1\n# Points\n4553243887000000 && 37.825229,-122.4208047 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755473000 || last_edit_user_id -> 14293 || amenity -> bench || last_edit_version -> 2\n2407548249000000 && 37.8275485,-122.4238341 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66062793 || seamark:landmark:category -> tower || last_edit_time -> 1546753099000 || last_edit_user_id -> 14293 || seamark:name -> Water tower || last_edit_version -> 2 || seamark:type -> landmark\n3054493437000000 && 37.8268591,-122.4227493 && gnis:county_name -> San Francisco || wheelchair -> yes || last_edit_changeset -> 46480561 || addr:state -> CA || amenity -> toilets || gnis:reviewed -> no || source -> USGS Geonames || building -> yes || last_edit_user_name -> BizV || toilets:wheelchair -> yes || last_edit_time -> 1488323986000 || last_edit_user_id -> 5404994 || gnis:import_uuid -> 57871b70-0100-4405-bb30-88b2e001a944 || name -> Alcatraz || gnis:feature_id -> 1657175 || last_edit_version -> 4 || ele -> 32\n1526523486000000 && 37.8266704,-122.4208218 && last_edit_user_name -> eduaddad || name:pt -> Terminal de Balsa de Alcatraz || last_edit_changeset -> 73895096 || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || amenity -> ferry_terminal || ferry -> yes || name -> Alcatraz Ferry Terminal || public_transport -> stop_position || last_edit_version -> 7 || operator -> Alcatraz Cruises, LLC\n1526523481000000 && 37.8266151,-122.4213351 && last_edit_user_name -> eduaddad || name:pt -> Loja de Presentes Alcatraz || last_edit_changeset -> 73895096 || shop -> gift || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Alcatraz Giftshop || source -> Bing || last_edit_version -> 2\n1417681435000000 && 37.8271206,-122.4239856 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048128 || barrier -> gate || last_edit_time -> 1546697857000 || last_edit_user_id -> 14293 || last_edit_version -> 2\n2113145715000000 && 37.8280442,-122.4255358 && last_edit_changeset -> 27604782 || seamark:light:character -> F || seamark:light:colour -> amber || seamark:light:exhibition -> night || noaa:geohash -> 468dc0f8411d || seamark:fog_signal:period -> 30 || seamark:fog_signal:sequence -> 02.0+(02.0)+02.0+(24.0) || seamark:fog_signal:generation -> automatic || seamark:type -> landmark || last_edit_user_name -> malcolmh || seamark:status -> permanent || last_edit_time -> 1419154709000 || last_edit_user_id -> 128186 || noaa:taghash -> 4577c29d2bcf9219ae4f037344d3ad80dcb37af2 || noaa:lnam -> 13fb0fb7 || seamark:fog_signal:category -> horn || seamark:landmark:conspicuity -> conspicuous || last_edit_version -> 7 || seamark:fog_signal:group -> 2 || seamark:light:category -> floodlight\n2407548242000000 && 37.8263372,-122.4221889 && last_edit_user_name -> Nautic || last_edit_changeset -> 17223234 || last_edit_time -> 1375682657000 || last_edit_user_id -> 449569 || fire_hydrant:position -> sidewalk || emergency -> fire_hydrant || fire_hydrant:type -> pillar || last_edit_version -> 1\n5784941541000000 && 37.8265179,-122.4209758 && last_edit_user_name -> randoogle || last_edit_changeset -> 61065995 || last_edit_time -> 1532546136000 || last_edit_user_id -> 245874 || tourism -> picnic_site || last_edit_version -> 1\n307446867000000 && 37.8271132,-122.42221 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224953895000 || last_edit_user_id -> 35195 || last_edit_version -> 1 || created_by -> JOSM\n2407548229000000 && 37.8262306,-122.4221503 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755471000 || last_edit_user_id -> 14293 || seamark:name -> Lighthouse || last_edit_version -> 3 || seamark:type -> landmark\n3202364309000000 && 37.8270878,-122.4243583 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697600000 || last_edit_user_id -> 14293 || entrance -> yes || last_edit_version -> 2\n1526523442000000 && 37.8253259,-122.4213619 && last_edit_user_name -> Dedus D D || wheelchair -> limited || last_edit_changeset -> 52209203 || last_edit_time -> 1505909499000 || last_edit_user_id -> 4509564 || name -> Alcatraz || tourism -> viewpoint || last_edit_version -> 3\n307446854000000 && 37.8271046,-122.4221259 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224953892000 || last_edit_user_id -> 35195 || last_edit_version -> 1 || created_by -> JOSM\n3202364310000000 && 37.8278188,-122.4248746 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66048024 || last_edit_time -> 1546697600000 || last_edit_user_id -> 14293 || entrance -> main || last_edit_version -> 2\n1526527959000000 && 37.8268449,-122.4229523 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 10008969 || last_edit_time -> 1322753403000 || last_edit_user_id -> 381909 || tourism -> information || last_edit_version -> 1\n3202359193000000 && 37.8070389,-122.4041405 && wheelchair -> yes || last_edit_changeset -> 69833614 || addr:state -> CA || amenity -> ferry_terminal || addr:postcode -> 94111 || operator -> Alcatraz Cruises, LLC || addr:city -> San Francisco || name:ca -> Ferry Alcatraz || last_edit_user_name -> gpesquero || toilets:wheelchair -> yes || addr:housenumber -> Pier 33 || last_edit_time -> 1556873987000 || last_edit_user_id -> 3584 || name -> Alcatraz Cruises, LLC || name:en -> Alcatraz Cruises, LLC || addr:street -> Alcatraz Landing || last_edit_version -> 7 || name:zh -> 恶魔岛渡轮\n307446857000000 && 37.8270224,-122.4219712 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224953892000 || last_edit_user_id -> 35195 || last_edit_version -> 1 || created_by -> JOSM\n307446836000000 && 37.827774,-122.4249791 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || access -> private || barrier -> gate || last_edit_time -> 1546755472000 || last_edit_user_id -> 14293 || last_edit_version -> 3\n307459622000000 && 37.8279428,-122.4243196 && last_edit_user_name -> Walter Schlögl || last_edit_changeset -> 6060751 || access -> private || barrier -> gate || last_edit_time -> 1287257739000 || last_edit_user_id -> 78656 || last_edit_version -> 5\n1526523433000000 && 37.8262052,-122.4224616 && last_edit_user_name -> wheelmap_visitor || toilets:wheelchair -> yes || wheelchair -> limited || last_edit_changeset -> 58254331 || last_edit_time -> 1524212918000 || last_edit_user_id -> 290680 || tourism -> viewpoint || last_edit_version -> 3\n265562462000000 && 37.827056,-122.4230597 && last_edit_user_name -> eduaddad || name:pt -> Ilha de Alcatraz || wheelchair -> yes || last_edit_changeset -> 73895096 || historic -> yes || last_edit_time -> 1567102630000 || last_edit_user_id -> 7489282 || name -> Alcatraz Island || tourism -> attraction || last_edit_version -> 9 || name:zh -> 恶魔岛，夺命岛\n4553243888000000 && 37.8254858,-122.4205524 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 66063078 || last_edit_time -> 1546755472000 || last_edit_user_id -> 14293 || amenity -> bench || last_edit_version -> 2\n1417681445000000 && 37.826779,-122.4214171 && last_edit_user_name -> phut || last_edit_changeset -> 9163280 || last_edit_time -> 1314664588000 || last_edit_user_id -> 27824 || tourism -> information || last_edit_version -> 1\n307431811000000 && 37.827076,-122.421781 && last_edit_user_name -> nvk || last_edit_changeset -> 33170078 || man_made -> tower || last_edit_time -> 1438920317000 || last_edit_user_id -> 131996 || name -> Guard Tower || last_edit_version -> 3\n307446860000000 && 37.8268908,-122.4217373 && last_edit_user_name -> will l || last_edit_changeset -> 571017 || last_edit_time -> 1224953893000 || last_edit_user_id -> 35195 || last_edit_version -> 1 || created_by -> JOSM\n# Relations\n9451753000000 && 24433344000000 -> inner -> A && last_edit_user_name -> StenSoft || last_edit_changeset -> 69717377 || name:de -> Bucht von San Francisco || natural -> bay || last_edit_time -> 1556592508000 || last_edit_user_id -> 255936 || name -> San Francisco Bay || wikipedia -> en:San Francisco Bay || type -> multipolygon || last_edit_version -> 2 || name:cs -> Sanfranciský záliv || wikidata -> Q232264\n"
  },
  {
    "path": "src/test/resources/data/Alcatraz/Alcatraz.osm",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<osm version=\"0.6\" generator=\"CGImap 0.7.5 (11246 thorn-03.openstreetmap.org)\" copyright=\"OpenStreetMap and contributors\" attribution=\"http://www.openstreetmap.org/copyright\" license=\"http://opendatacommons.org/licenses/odbl/1-0/\">\n <bounds minlat=\"37.8250000\" minlon=\"-122.4265700\" maxlat=\"37.8284600\" maxlon=\"-122.4194300\"/>\n <node id=\"265561221\" visible=\"true\" version=\"15\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:25Z\" user=\"will l\" uid=\"35195\" lat=\"37.8258434\" lon=\"-122.4230873\"/>\n <node id=\"265561222\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8258231\" lon=\"-122.4229929\"/>\n <node id=\"265561223\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8257756\" lon=\"-122.4229328\"/>\n <node id=\"265561224\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8257214\" lon=\"-122.4227869\"/>\n <node id=\"265561225\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8256265\" lon=\"-122.4227526\"/>\n <node id=\"265561226\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8255092\" lon=\"-122.4227072\"/>\n <node id=\"265561227\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8254881\" lon=\"-122.4226473\"/>\n <node id=\"265561228\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8254698\" lon=\"-122.4225087\"/>\n <node id=\"265561229\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8254502\" lon=\"-122.4224522\"/>\n <node id=\"265561230\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8253078\" lon=\"-122.4221432\"/>\n <node id=\"265561231\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8251465\" lon=\"-122.4218792\"/>\n <node id=\"265561232\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:13Z\" user=\"will l\" uid=\"35195\" lat=\"37.8250787\" lon=\"-122.4214930\"/>\n <node id=\"265561233\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251055\" lon=\"-122.4211995\"/>\n <node id=\"265561234\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8250846\" lon=\"-122.4211760\"/>\n <node id=\"265561235\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251705\" lon=\"-122.4208744\"/>\n <node id=\"265561236\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252309\" lon=\"-122.4207283\"/>\n <node id=\"265561237\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253961\" lon=\"-122.4205712\"/>\n <node id=\"265561238\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8256533\" lon=\"-122.4204309\"/>\n <node id=\"265561240\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8258285\" lon=\"-122.4204073\"/>\n <node id=\"265561241\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8258910\" lon=\"-122.4204324\"/>\n <node id=\"265561242\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8261174\" lon=\"-122.4204776\"/>\n <node id=\"265561243\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262743\" lon=\"-122.4206387\"/>\n <node id=\"265561244\" visible=\"true\" version=\"10\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8264889\" lon=\"-122.4208486\"/>\n <node id=\"265561245\" visible=\"true\" version=\"12\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8265016\" lon=\"-122.4208770\"/>\n <node id=\"265561246\" visible=\"true\" version=\"11\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266576\" lon=\"-122.4210652\"/>\n <node id=\"265561247\" visible=\"true\" version=\"10\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8267317\" lon=\"-122.4209660\"/>\n <node id=\"265561248\" visible=\"true\" version=\"10\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8267594\" lon=\"-122.4209676\"/>\n <node id=\"265561249\" visible=\"true\" version=\"12\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270274\" lon=\"-122.4214142\"/>\n <node id=\"265561252\" visible=\"true\" version=\"10\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269570\" lon=\"-122.4214780\"/>\n <node id=\"265561253\" visible=\"true\" version=\"9\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271293\" lon=\"-122.4218000\"/>\n <node id=\"265561254\" visible=\"true\" version=\"9\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:32Z\" user=\"phut\" uid=\"27824\" lat=\"37.8270678\" lon=\"-122.4218551\"/>\n <node id=\"265561256\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270980\" lon=\"-122.4219104\"/>\n <node id=\"265561257\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:14Z\" user=\"will l\" uid=\"35195\" lat=\"37.8271993\" lon=\"-122.4219715\"/>\n <node id=\"265561258\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:14Z\" user=\"will l\" uid=\"35195\" lat=\"37.8272468\" lon=\"-122.4220831\"/>\n <node id=\"265561259\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:14Z\" user=\"will l\" uid=\"35195\" lat=\"37.8272875\" lon=\"-122.4222119\"/>\n <node id=\"265561260\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272950\" lon=\"-122.4222585\"/>\n <node id=\"265561261\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8274079\" lon=\"-122.4224738\"/>\n <node id=\"265561262\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:15Z\" user=\"will l\" uid=\"35195\" lat=\"37.8274786\" lon=\"-122.4226689\"/>\n <node id=\"265561263\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8277624\" lon=\"-122.4229910\"/>\n <node id=\"265561264\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278503\" lon=\"-122.4231184\"/>\n <node id=\"265561265\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279927\" lon=\"-122.4232480\"/>\n <node id=\"265561427\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280748\" lon=\"-122.4234665\"/>\n <node id=\"265561428\" visible=\"true\" version=\"10\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8281020\" lon=\"-122.4235560\"/>\n <node id=\"265561429\" visible=\"true\" version=\"10\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8281054\" lon=\"-122.4238064\"/>\n <node id=\"265561430\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:15Z\" user=\"will l\" uid=\"35195\" lat=\"37.8281905\" lon=\"-122.4239048\"/>\n <node id=\"265561431\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8282311\" lon=\"-122.4239992\"/>\n <node id=\"265561432\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283112\" lon=\"-122.4240915\"/>\n <node id=\"265561433\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283586\" lon=\"-122.4241774\"/>\n <node id=\"265561434\" visible=\"true\" version=\"9\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:32Z\" user=\"phut\" uid=\"27824\" lat=\"37.8283593\" lon=\"-122.4242890\"/>\n <node id=\"265561436\" visible=\"true\" version=\"9\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:32Z\" user=\"phut\" uid=\"27824\" lat=\"37.8283107\" lon=\"-122.4244188\"/>\n <node id=\"265561437\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283586\" lon=\"-122.4246237\"/>\n <node id=\"265561439\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283518\" lon=\"-122.4247439\"/>\n <node id=\"265561440\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283247\" lon=\"-122.4248039\"/>\n <node id=\"265561441\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283722\" lon=\"-122.4248898\"/>\n <node id=\"265561442\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283586\" lon=\"-122.4249756\"/>\n <node id=\"265561443\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:17Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283654\" lon=\"-122.4250357\"/>\n <node id=\"265561444\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:17Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283857\" lon=\"-122.4250958\"/>\n <node id=\"265561445\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:17Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283247\" lon=\"-122.4252073\"/>\n <node id=\"265561446\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:17Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283654\" lon=\"-122.4252760\"/>\n <node id=\"265561447\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:17Z\" user=\"will l\" uid=\"35195\" lat=\"37.8283247\" lon=\"-122.4254477\"/>\n <node id=\"265561448\" visible=\"true\" version=\"9\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:32Z\" user=\"phut\" uid=\"27824\" lat=\"37.8282513\" lon=\"-122.4255674\"/>\n <node id=\"265561449\" visible=\"true\" version=\"9\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:32Z\" user=\"phut\" uid=\"27824\" lat=\"37.8280807\" lon=\"-122.4255997\"/>\n <node id=\"265561450\" visible=\"true\" version=\"9\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:32Z\" user=\"phut\" uid=\"27824\" lat=\"37.8280425\" lon=\"-122.4256157\"/>\n <node id=\"265561451\" visible=\"true\" version=\"10\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:32Z\" user=\"phut\" uid=\"27824\" lat=\"37.8279540\" lon=\"-122.4255445\"/>\n <node id=\"265561452\" visible=\"true\" version=\"9\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:32Z\" user=\"phut\" uid=\"27824\" lat=\"37.8278840\" lon=\"-122.4255610\"/>\n <node id=\"265561453\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8277791\" lon=\"-122.4253253\"/>\n <node id=\"265561454\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8278093\" lon=\"-122.4252640\"/>\n <node id=\"265561455\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:18Z\" user=\"will l\" uid=\"35195\" lat=\"37.8277688\" lon=\"-122.4251902\"/>\n <node id=\"265561456\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:19Z\" user=\"will l\" uid=\"35195\" lat=\"37.8277078\" lon=\"-122.4251387\"/>\n <node id=\"265561457\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:19Z\" user=\"will l\" uid=\"35195\" lat=\"37.8276739\" lon=\"-122.4251215\"/>\n <node id=\"265561458\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:19Z\" user=\"will l\" uid=\"35195\" lat=\"37.8275790\" lon=\"-122.4250357\"/>\n <node id=\"265561459\" visible=\"true\" version=\"10\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8275443\" lon=\"-122.4250204\"/>\n <node id=\"265561460\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275161\" lon=\"-122.4250375\"/>\n <node id=\"265561461\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271703\" lon=\"-122.4248737\"/>\n <node id=\"265561463\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270945\" lon=\"-122.4248205\"/>\n <node id=\"265561464\" visible=\"true\" version=\"9\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269523\" lon=\"-122.4246717\"/>\n <node id=\"265561465\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8268793\" lon=\"-122.4244650\"/>\n <node id=\"265561467\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:20Z\" user=\"will l\" uid=\"35195\" lat=\"37.8267926\" lon=\"-122.4244949\"/>\n <node id=\"265561468\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:20Z\" user=\"will l\" uid=\"35195\" lat=\"37.8267315\" lon=\"-122.4245207\"/>\n <node id=\"265561469\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:20Z\" user=\"will l\" uid=\"35195\" lat=\"37.8267112\" lon=\"-122.4245550\"/>\n <node id=\"265561470\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8266638\" lon=\"-122.4245647\"/>\n <node id=\"265561471\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:21Z\" user=\"will l\" uid=\"35195\" lat=\"37.8266027\" lon=\"-122.4244864\"/>\n <node id=\"265561472\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8265529\" lon=\"-122.4243945\"/>\n <node id=\"265561473\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8265750\" lon=\"-122.4243214\"/>\n <node id=\"265561474\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8265850\" lon=\"-122.4242512\"/>\n <node id=\"265561475\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8265494\" lon=\"-122.4241953\"/>\n <node id=\"265561477\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:22Z\" user=\"will l\" uid=\"35195\" lat=\"37.8264875\" lon=\"-122.4240229\"/>\n <node id=\"265561478\" visible=\"true\" version=\"9\" changeset=\"49057363\" timestamp=\"2017-05-29T00:31:53Z\" user=\"flockfinder\" uid=\"1616228\" lat=\"37.8265183\" lon=\"-122.4239573\"/>\n <node id=\"265561479\" visible=\"true\" version=\"9\" changeset=\"49057363\" timestamp=\"2017-05-29T00:31:53Z\" user=\"flockfinder\" uid=\"1616228\" lat=\"37.8265300\" lon=\"-122.4238988\"/>\n <node id=\"265561480\" visible=\"true\" version=\"9\" changeset=\"49057363\" timestamp=\"2017-05-29T00:31:53Z\" user=\"flockfinder\" uid=\"1616228\" lat=\"37.8265283\" lon=\"-122.4238460\"/>\n <node id=\"265561481\" visible=\"true\" version=\"9\" changeset=\"49057363\" timestamp=\"2017-05-29T00:31:53Z\" user=\"flockfinder\" uid=\"1616228\" lat=\"37.8264491\" lon=\"-122.4237511\"/>\n <node id=\"265561482\" visible=\"true\" version=\"9\" changeset=\"49057363\" timestamp=\"2017-05-29T00:31:53Z\" user=\"flockfinder\" uid=\"1616228\" lat=\"37.8264056\" lon=\"-122.4237300\"/>\n <node id=\"265561483\" visible=\"true\" version=\"9\" changeset=\"49057363\" timestamp=\"2017-05-29T00:31:53Z\" user=\"flockfinder\" uid=\"1616228\" lat=\"37.8262488\" lon=\"-122.4237363\"/>\n <node id=\"265561484\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:23Z\" user=\"will l\" uid=\"35195\" lat=\"37.8261621\" lon=\"-122.4237654\"/>\n <node id=\"265561485\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:23Z\" user=\"will l\" uid=\"35195\" lat=\"37.8260875\" lon=\"-122.4237053\"/>\n <node id=\"265561487\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:24Z\" user=\"will l\" uid=\"35195\" lat=\"37.8260265\" lon=\"-122.4235336\"/>\n <node id=\"265561488\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:24Z\" user=\"will l\" uid=\"35195\" lat=\"37.8259519\" lon=\"-122.4233534\"/>\n <node id=\"265561489\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:24Z\" user=\"will l\" uid=\"35195\" lat=\"37.8258841\" lon=\"-122.4232762\"/>\n <node id=\"265561490\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:24Z\" user=\"will l\" uid=\"35195\" lat=\"37.8258705\" lon=\"-122.4231903\"/>\n <node id=\"265561608\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8281718\" lon=\"-122.4253998\"/>\n <node id=\"265561609\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280361\" lon=\"-122.4255291\"/>\n <node id=\"265561610\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279852\" lon=\"-122.4254435\"/>\n <node id=\"265561611\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279176\" lon=\"-122.4255080\"/>\n <node id=\"265561612\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278166\" lon=\"-122.4253382\"/>\n <node id=\"265561613\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278507\" lon=\"-122.4253057\"/>\n <node id=\"265561614\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8277749\" lon=\"-122.4251780\"/>\n <node id=\"265561615\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278432\" lon=\"-122.4251129\"/>\n <node id=\"265561616\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279700\" lon=\"-122.4253263\"/>\n <node id=\"265561617\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280710\" lon=\"-122.4252301\"/>\n <node id=\"265561699\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8277813\" lon=\"-122.4249597\"/>\n <node id=\"265561700\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278604\" lon=\"-122.4247795\"/>\n <node id=\"265561701\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278732\" lon=\"-122.4247885\"/>\n <node id=\"265561702\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278827\" lon=\"-122.4247668\"/>\n <node id=\"265561705\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271250\" lon=\"-122.4242353\"/>\n <node id=\"265561706\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271159\" lon=\"-122.4242560\"/>\n <node id=\"265561707\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271287\" lon=\"-122.4242650\"/>\n <node id=\"265561708\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270503\" lon=\"-122.4244436\"/>\n <node id=\"265562145\" visible=\"true\" version=\"9\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270451\" lon=\"-122.4234575\"/>\n <node id=\"265562146\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270223\" lon=\"-122.4234875\"/>\n <node id=\"265562149\" visible=\"true\" version=\"6\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8267327\" lon=\"-122.4231328\"/>\n <node id=\"265562150\" visible=\"true\" version=\"6\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266479\" lon=\"-122.4232435\"/>\n <node id=\"265562151\" visible=\"true\" version=\"6\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8263371\" lon=\"-122.4228632\"/>\n <node id=\"265562152\" visible=\"true\" version=\"6\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8264063\" lon=\"-122.4227727\"/>\n <node id=\"265562153\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262972\" lon=\"-122.4226392\"/>\n <node id=\"265562154\" visible=\"true\" version=\"7\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8263178\" lon=\"-122.4226124\"/>\n <node id=\"265562155\" visible=\"true\" version=\"7\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262224\" lon=\"-122.4224956\"/>\n <node id=\"265562159\" visible=\"true\" version=\"7\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8264405\" lon=\"-122.4222102\"/>\n <node id=\"265562161\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268456\" lon=\"-122.4227066\"/>\n <node id=\"265562162\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8267808\" lon=\"-122.4227913\"/>\n <node id=\"265562163\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268581\" lon=\"-122.4229687\"/>\n <node id=\"265562164\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271480\" lon=\"-122.4233237\"/>\n <node id=\"265562165\" visible=\"true\" version=\"6\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271271\" lon=\"-122.4233509\"/>\n <node id=\"265562462\" visible=\"true\" version=\"9\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\" lat=\"37.8270560\" lon=\"-122.4230597\">\n  <tag k=\"historic\" v=\"yes\"/><tag k=\"name\" v=\"Alcatraz Island\"/><tag k=\"name:pt\" v=\"Ilha de Alcatraz\"/><tag k=\"name:zh\" v=\"恶魔岛，夺命岛\"/><tag k=\"tourism\" v=\"attraction\"/><tag k=\"wheelchair\" v=\"yes\"/>\n </node>\n <node id=\"267604015\" visible=\"true\" version=\"3\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270206\" lon=\"-122.4220063\"/>\n <node id=\"267604016\" visible=\"true\" version=\"3\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268901\" lon=\"-122.4221255\"/>\n <node id=\"267604017\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265530\" lon=\"-122.4215420\"/>\n <node id=\"267604018\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8264390\" lon=\"-122.4215701\"/>\n <node id=\"267604019\" visible=\"true\" version=\"3\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8264070\" lon=\"-122.4213690\"/>\n <node id=\"267604023\" visible=\"true\" version=\"3\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266219\" lon=\"-122.4213147\"/>\n <node id=\"303176761\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271993\" lon=\"-122.4216941\"/>\n <node id=\"303666382\" visible=\"true\" version=\"6\" changeset=\"571017\" timestamp=\"2008-10-25T18:11:13Z\" user=\"will l\" uid=\"35195\" lat=\"37.8250854\" lon=\"-122.4217247\"/>\n <node id=\"303666473\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270426\" lon=\"-122.4214009\"/>\n <node id=\"307351589\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:40Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8074686\" lon=\"-122.4039428\"/>\n <node id=\"307351590\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:40Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8080760\" lon=\"-122.4035932\"/>\n <node id=\"307351591\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8084458\" lon=\"-122.4034591\"/>\n <node id=\"307351592\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8087629\" lon=\"-122.4034225\"/>\n <node id=\"307351593\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8090840\" lon=\"-122.4034509\"/>\n <node id=\"307351594\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8094213\" lon=\"-122.4035282\"/>\n <node id=\"307351595\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8097059\" lon=\"-122.4036339\"/>\n <node id=\"307351596\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8101326\" lon=\"-122.4038737\"/>\n <node id=\"307351597\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8108196\" lon=\"-122.4043411\"/>\n <node id=\"307351598\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8114740\" lon=\"-122.4048817\"/>\n <node id=\"307351599\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8121365\" lon=\"-122.4055158\"/>\n <node id=\"307351600\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8137484\" lon=\"-122.4073261\"/>\n <node id=\"307351607\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8246229\" lon=\"-122.4178730\"/>\n <node id=\"307351608\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8256667\" lon=\"-122.4188075\"/>\n <node id=\"307351609\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8262371\" lon=\"-122.4195964\"/>\n <node id=\"307351610\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8265614\" lon=\"-122.4203732\"/>\n <node id=\"307351611\" visible=\"true\" version=\"5\" changeset=\"20683290\" timestamp=\"2014-02-20T21:09:41Z\" user=\"wvdp\" uid=\"436419\" lat=\"37.8266360\" lon=\"-122.4206135\"/>\n <node id=\"307351633\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262588\" lon=\"-122.4206588\"/>\n <node id=\"307351636\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8261186\" lon=\"-122.4204998\"/>\n <node id=\"307351641\" visible=\"true\" version=\"3\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:09Z\" user=\"will l\" uid=\"35195\" lat=\"37.8257539\" lon=\"-122.4204073\"/>\n <node id=\"307351642\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8257395\" lon=\"-122.4204030\"/>\n <node id=\"307351643\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8256169\" lon=\"-122.4204726\"/>\n <node id=\"307351644\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255235\" lon=\"-122.4205092\"/>\n <node id=\"307351645\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254354\" lon=\"-122.4205599\"/>\n <node id=\"307351646\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254083\" lon=\"-122.4205836\"/>\n <node id=\"307351647\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252464\" lon=\"-122.4207352\"/>\n <node id=\"307351648\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252356\" lon=\"-122.4207497\"/>\n <node id=\"307351649\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251794\" lon=\"-122.4208966\"/>\n <node id=\"307351650\" visible=\"true\" version=\"6\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251061\" lon=\"-122.4211500\"/>\n <node id=\"307351651\" visible=\"true\" version=\"6\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251022\" lon=\"-122.4211649\"/>\n <node id=\"307351652\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251293\" lon=\"-122.4212070\"/>\n <node id=\"307351653\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251708\" lon=\"-122.4210867\"/>\n <node id=\"307351654\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252266\" lon=\"-122.4209838\"/>\n <node id=\"307351655\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252472\" lon=\"-122.4209575\"/>\n <node id=\"307351658\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253689\" lon=\"-122.4208404\"/>\n <node id=\"307351661\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253963\" lon=\"-122.4208266\"/>\n <node id=\"307428498\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280874\" lon=\"-122.4246186\"/>\n <node id=\"307428500\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8282267\" lon=\"-122.4242933\"/>\n <node id=\"307428501\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8282090\" lon=\"-122.4246248\"/>\n <node id=\"307428502\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8282050\" lon=\"-122.4247923\"/>\n <node id=\"307428503\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280942\" lon=\"-122.4247880\"/>\n <node id=\"307428613\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8281940\" lon=\"-122.4241906\"/>\n <node id=\"307428615\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8281081\" lon=\"-122.4242756\"/>\n <node id=\"307428616\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279241\" lon=\"-122.4239776\"/>\n <node id=\"307428617\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280100\" lon=\"-122.4238926\"/>\n <node id=\"307428691\" visible=\"true\" version=\"10\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278343\" lon=\"-122.4232984\"/>\n <node id=\"307428692\" visible=\"true\" version=\"7\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8277424\" lon=\"-122.4233639\"/>\n <node id=\"307428693\" visible=\"true\" version=\"7\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276054\" lon=\"-122.4230558\"/>\n <node id=\"307428694\" visible=\"true\" version=\"7\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276973\" lon=\"-122.4229903\"/>\n <node id=\"307428865\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269854\" lon=\"-122.4213870\"/>\n <node id=\"307428867\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269240\" lon=\"-122.4214379\"/>\n <node id=\"307428869\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268678\" lon=\"-122.4213292\"/>\n <node id=\"307428871\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269292\" lon=\"-122.4212783\"/>\n <node id=\"307429152\" visible=\"true\" version=\"6\" changeset=\"15448995\" timestamp=\"2013-03-21T22:30:28Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8271238\" lon=\"-122.4222642\"/>\n <node id=\"307429153\" visible=\"true\" version=\"4\" changeset=\"15448995\" timestamp=\"2013-03-21T22:30:28Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8272731\" lon=\"-122.4225776\"/>\n <node id=\"307429155\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8273895\" lon=\"-122.4224703\"/>\n <node id=\"307429156\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272872\" lon=\"-122.4222745\"/>\n <node id=\"307429157\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272515\" lon=\"-122.4223041\"/>\n <node id=\"307429159\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271965\" lon=\"-122.4221980\"/>\n <node id=\"307429230\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272677\" lon=\"-122.4226159\"/>\n <node id=\"307429231\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8273186\" lon=\"-122.4225736\"/>\n <node id=\"307429232\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8274149\" lon=\"-122.4227594\"/>\n <node id=\"307429233\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8273641\" lon=\"-122.4228016\"/>\n <node id=\"307429407\" visible=\"true\" version=\"9\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8263370\" lon=\"-122.4220318\"/>\n <node id=\"307429408\" visible=\"true\" version=\"8\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262225\" lon=\"-122.4219119\"/>\n <node id=\"307429409\" visible=\"true\" version=\"9\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262752\" lon=\"-122.4217985\"/>\n <node id=\"307429410\" visible=\"true\" version=\"8\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8263609\" lon=\"-122.4218884\"/>\n <node id=\"307429411\" visible=\"true\" version=\"8\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8263689\" lon=\"-122.4218761\"/>\n <node id=\"307429412\" visible=\"true\" version=\"8\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8264103\" lon=\"-122.4219195\"/>\n <node id=\"307429450\" visible=\"true\" version=\"6\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:19Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8264665\" lon=\"-122.4221448\"/>\n <node id=\"307429451\" visible=\"true\" version=\"5\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:19Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8264081\" lon=\"-122.4220876\"/>\n <node id=\"307429452\" visible=\"true\" version=\"5\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:19Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8264253\" lon=\"-122.4220595\"/>\n <node id=\"307429453\" visible=\"true\" version=\"6\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:19Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8264892\" lon=\"-122.4221076\"/>\n <node id=\"307429818\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8257351\" lon=\"-122.4221372\"/>\n <node id=\"307429819\" visible=\"true\" version=\"4\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8257192\" lon=\"-122.4219714\"/>\n <node id=\"307429820\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:31Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8257069\" lon=\"-122.4219113\"/>\n <node id=\"307429821\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255830\" lon=\"-122.4216015\"/>\n <node id=\"307429822\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:30Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255055\" lon=\"-122.4214552\"/>\n <node id=\"307429823\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:31Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8252912\" lon=\"-122.4215249\"/>\n <node id=\"307429827\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:29Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8253312\" lon=\"-122.4212359\"/>\n <node id=\"307429828\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:25Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255871\" lon=\"-122.4213427\"/>\n <node id=\"307429829\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:27Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256336\" lon=\"-122.4212818\"/>\n <node id=\"307429830\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8257184\" lon=\"-122.4211315\"/>\n <node id=\"307429831\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8257309\" lon=\"-122.4210036\"/>\n <node id=\"307429832\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:31Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256911\" lon=\"-122.4209166\"/>\n <node id=\"307429833\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:32Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8258258\" lon=\"-122.4209080\"/>\n <node id=\"307429834\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:30Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8259144\" lon=\"-122.4210140\"/>\n <node id=\"307429835\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:31Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8260075\" lon=\"-122.4211240\"/>\n <node id=\"307429836\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:28Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8263461\" lon=\"-122.4213991\"/>\n <node id=\"307429837\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:29Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8263646\" lon=\"-122.4215710\"/>\n <node id=\"307430019\" visible=\"true\" version=\"7\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:29Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8261152\" lon=\"-122.4234561\"/>\n <node id=\"307430020\" visible=\"true\" version=\"5\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:29Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8262353\" lon=\"-122.4235155\"/>\n <node id=\"307430021\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8264185\" lon=\"-122.4235906\"/>\n <node id=\"307430022\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265259\" lon=\"-122.4236413\"/>\n <node id=\"307430023\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265462\" lon=\"-122.4236473\"/>\n <node id=\"307430024\" visible=\"true\" version=\"6\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265662\" lon=\"-122.4236481\"/>\n <node id=\"307430025\" visible=\"true\" version=\"9\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265962\" lon=\"-122.4236215\"/>\n <node id=\"307430026\" visible=\"true\" version=\"6\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265839\" lon=\"-122.4235623\"/>\n <node id=\"307430027\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8263884\" lon=\"-122.4233246\"/>\n <node id=\"307430028\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T17:38:36Z\" user=\"will l\" uid=\"35195\" lat=\"37.8263441\" lon=\"-122.4232657\"/>\n <node id=\"307430029\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T17:38:36Z\" user=\"will l\" uid=\"35195\" lat=\"37.8263034\" lon=\"-122.4231884\"/>\n <node id=\"307430030\" visible=\"true\" version=\"5\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:29Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8262707\" lon=\"-122.4230778\"/>\n <node id=\"307430031\" visible=\"true\" version=\"5\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:29Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8261423\" lon=\"-122.4226410\"/>\n <node id=\"307430032\" visible=\"true\" version=\"5\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:29Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260651\" lon=\"-122.4222218\"/>\n <node id=\"307430033\" visible=\"true\" version=\"6\" changeset=\"63050340\" timestamp=\"2018-09-29T22:59:26Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8260568\" lon=\"-122.4221661\"/>\n <node id=\"307430034\" visible=\"true\" version=\"5\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:28Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260557\" lon=\"-122.4221316\"/>\n <node id=\"307430035\" visible=\"true\" version=\"9\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:19Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8261526\" lon=\"-122.4219539\"/>\n <node id=\"307430635\" visible=\"true\" version=\"5\" changeset=\"571017\" timestamp=\"2008-10-25T18:17:03Z\" user=\"will l\" uid=\"35195\" lat=\"37.8269409\" lon=\"-122.4223799\"/>\n <node id=\"307430636\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8265542\" lon=\"-122.4222529\"/>\n <node id=\"307430637\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:05Z\" user=\"will l\" uid=\"35195\" lat=\"37.8264525\" lon=\"-122.4221499\"/>\n <node id=\"307430638\" visible=\"true\" version=\"9\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:20Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8262987\" lon=\"-122.4221054\"/>\n <node id=\"307431021\" visible=\"true\" version=\"5\" changeset=\"43722604\" timestamp=\"2016-11-17T09:28:47Z\" user=\"Юкатан\" uid=\"3885962\" lat=\"37.8266723\" lon=\"-122.4207726\"/>\n <node id=\"307431811\" visible=\"true\" version=\"3\" changeset=\"33170078\" timestamp=\"2015-08-07T04:05:17Z\" user=\"nvk\" uid=\"131996\" lat=\"37.8270760\" lon=\"-122.4217810\">\n  <tag k=\"man_made\" v=\"tower\"/><tag k=\"name\" v=\"Guard Tower\"/>\n </node>\n <node id=\"307446157\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254275\" lon=\"-122.4208081\"/>\n <node id=\"307446163\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254607\" lon=\"-122.4207908\"/>\n <node id=\"307446167\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255612\" lon=\"-122.4207442\"/>\n <node id=\"307446170\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8256105\" lon=\"-122.4207728\"/>\n <node id=\"307446172\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254120\" lon=\"-122.4209931\"/>\n <node id=\"307446174\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255531\" lon=\"-122.4208283\"/>\n <node id=\"307446184\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253745\" lon=\"-122.4210509\"/>\n <node id=\"307446189\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253016\" lon=\"-122.4212326\"/>\n <node id=\"307446235\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252513\" lon=\"-122.4215468\"/>\n <node id=\"307446313\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252410\" lon=\"-122.4216635\"/>\n <node id=\"307446325\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252571\" lon=\"-122.4217389\"/>\n <node id=\"307446349\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252821\" lon=\"-122.4218064\"/>\n <node id=\"307446378\" visible=\"true\" version=\"6\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255776\" lon=\"-122.4222696\"/>\n <node id=\"307446432\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8275941\" lon=\"-122.4231038\"/>\n <node id=\"307446477\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8276627\" lon=\"-122.4232670\"/>\n <node id=\"307446480\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8277451\" lon=\"-122.4234700\"/>\n <node id=\"307446487\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8277604\" lon=\"-122.4235646\"/>\n <node id=\"307446527\" visible=\"true\" version=\"11\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:33Z\" user=\"phut\" uid=\"27824\" lat=\"37.8277276\" lon=\"-122.4236738\"/>\n <node id=\"307446815\" visible=\"true\" version=\"11\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:37Z\" user=\"phut\" uid=\"27824\" lat=\"37.8277200\" lon=\"-122.4237782\"/>\n <node id=\"307446816\" visible=\"true\" version=\"11\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:37Z\" user=\"phut\" uid=\"27824\" lat=\"37.8277536\" lon=\"-122.4238891\"/>\n <node id=\"307446821\" visible=\"true\" version=\"11\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8278635\" lon=\"-122.4240707\"/>\n <node id=\"307446822\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8279420\" lon=\"-122.4242253\"/>\n <node id=\"307446823\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8279893\" lon=\"-122.4243260\"/>\n <node id=\"307446824\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8279971\" lon=\"-122.4244226\"/>\n <node id=\"307446827\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8279971\" lon=\"-122.4245256\"/>\n <node id=\"307446829\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8279700\" lon=\"-122.4246114\"/>\n <node id=\"307446833\" visible=\"true\" version=\"11\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279545\" lon=\"-122.4247254\"/>\n <node id=\"307446834\" visible=\"true\" version=\"11\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279432\" lon=\"-122.4248882\"/>\n <node id=\"307446835\" visible=\"true\" version=\"11\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278803\" lon=\"-122.4249867\"/>\n <node id=\"307446836\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8277740\" lon=\"-122.4249791\">\n  <tag k=\"access\" v=\"private\"/><tag k=\"barrier\" v=\"gate\"/>\n </node>\n <node id=\"307446838\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270347\" lon=\"-122.4244748\"/>\n <node id=\"307446839\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8275160\" lon=\"-122.4229151\"/>\n <node id=\"307446842\" visible=\"true\" version=\"10\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8274359\" lon=\"-122.4227538\"/>\n <node id=\"307446843\" visible=\"true\" version=\"2\" changeset=\"761842\" timestamp=\"2008-11-17T15:33:55Z\" user=\"will l\" uid=\"35195\" lat=\"37.8272859\" lon=\"-122.4224685\"/>\n <node id=\"307446845\" visible=\"true\" version=\"3\" changeset=\"15448995\" timestamp=\"2013-03-21T22:30:28Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8271680\" lon=\"-122.4222390\"/>\n <node id=\"307446854\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T16:58:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8271046\" lon=\"-122.4221259\">\n  <tag k=\"created_by\" v=\"JOSM\"/>\n </node>\n <node id=\"307446857\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T16:58:12Z\" user=\"will l\" uid=\"35195\" lat=\"37.8270224\" lon=\"-122.4219712\">\n  <tag k=\"created_by\" v=\"JOSM\"/>\n </node>\n <node id=\"307446860\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T16:58:13Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268908\" lon=\"-122.4217373\">\n  <tag k=\"created_by\" v=\"JOSM\"/>\n </node>\n <node id=\"307446864\" visible=\"true\" version=\"11\" changeset=\"761842\" timestamp=\"2008-11-17T15:33:55Z\" user=\"will l\" uid=\"35195\" lat=\"37.8273227\" lon=\"-122.4225384\"/>\n <node id=\"307446865\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271629\" lon=\"-122.4222296\"/>\n <node id=\"307446867\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T16:58:15Z\" user=\"will l\" uid=\"35195\" lat=\"37.8271132\" lon=\"-122.4222100\">\n  <tag k=\"created_by\" v=\"JOSM\"/>\n </node>\n <node id=\"307446868\" visible=\"true\" version=\"3\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:46Z\" user=\"will l\" uid=\"35195\" lat=\"37.8269888\" lon=\"-122.4220820\"/>\n <node id=\"307446870\" visible=\"true\" version=\"3\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:46Z\" user=\"will l\" uid=\"35195\" lat=\"37.8271463\" lon=\"-122.4224117\"/>\n <node id=\"307446871\" visible=\"true\" version=\"3\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:46Z\" user=\"will l\" uid=\"35195\" lat=\"37.8272161\" lon=\"-122.4225662\"/>\n <node id=\"307446872\" visible=\"true\" version=\"7\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:35Z\" user=\"phut\" uid=\"27824\" lat=\"37.8272850\" lon=\"-122.4227049\"/>\n <node id=\"307446873\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:35Z\" user=\"phut\" uid=\"27824\" lat=\"37.8272008\" lon=\"-122.4226140\"/>\n <node id=\"307446874\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8271450\" lon=\"-122.4225347\"/>\n <node id=\"307446875\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:35Z\" user=\"phut\" uid=\"27824\" lat=\"37.8270892\" lon=\"-122.4224434\"/>\n <node id=\"307446882\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8269710\" lon=\"-122.4222182\"/>\n <node id=\"307446900\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:17:03Z\" user=\"will l\" uid=\"35195\" lat=\"37.8270498\" lon=\"-122.4226474\"/>\n <node id=\"307447029\" visible=\"true\" version=\"7\" changeset=\"571017\" timestamp=\"2008-10-25T18:17:03Z\" user=\"will l\" uid=\"35195\" lat=\"37.8272039\" lon=\"-122.4229806\"/>\n <node id=\"307447041\" visible=\"true\" version=\"5\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8272944\" lon=\"-122.4231264\"/>\n <node id=\"307447042\" visible=\"true\" version=\"5\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8273607\" lon=\"-122.4232626\"/>\n <node id=\"307447044\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:17:02Z\" user=\"will l\" uid=\"35195\" lat=\"37.8273827\" lon=\"-122.4233086\"/>\n <node id=\"307447052\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:36Z\" user=\"phut\" uid=\"27824\" lat=\"37.8273955\" lon=\"-122.4233642\"/>\n <node id=\"307447053\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:35Z\" user=\"phut\" uid=\"27824\" lat=\"37.8273905\" lon=\"-122.4234101\"/>\n <node id=\"307447054\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8273597\" lon=\"-122.4234474\"/>\n <node id=\"307447057\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8273118\" lon=\"-122.4234534\"/>\n <node id=\"307447058\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:36Z\" user=\"phut\" uid=\"27824\" lat=\"37.8272688\" lon=\"-122.4234124\"/>\n <node id=\"307447065\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8271874\" lon=\"-122.4232905\"/>\n <node id=\"307447072\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:17:01Z\" user=\"will l\" uid=\"35195\" lat=\"37.8270828\" lon=\"-122.4231364\"/>\n <node id=\"307447083\" visible=\"true\" version=\"5\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:36Z\" user=\"phut\" uid=\"27824\" lat=\"37.8269421\" lon=\"-122.4229216\"/>\n <node id=\"307447104\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268908\" lon=\"-122.4229260\"/>\n <node id=\"307447107\" visible=\"true\" version=\"6\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:33Z\" user=\"phut\" uid=\"27824\" lat=\"37.8269103\" lon=\"-122.4229565\"/>\n <node id=\"307447109\" visible=\"true\" version=\"4\" changeset=\"59183284\" timestamp=\"2018-05-22T15:17:34Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8268090\" lon=\"-122.4221806\"/>\n <node id=\"307447117\" visible=\"true\" version=\"3\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8266226\" lon=\"-122.4219063\"/>\n <node id=\"307447118\" visible=\"true\" version=\"3\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:35Z\" user=\"phut\" uid=\"27824\" lat=\"37.8265103\" lon=\"-122.4217541\"/>\n <node id=\"307447119\" visible=\"true\" version=\"3\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8264333\" lon=\"-122.4216514\"/>\n <node id=\"307447121\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T17:33:25Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268597\" lon=\"-122.4222351\"/>\n <node id=\"307447150\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T17:33:25Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268464\" lon=\"-122.4222076\"/>\n <node id=\"307447179\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T17:33:25Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268304\" lon=\"-122.4222147\"/>\n <node id=\"307447180\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T17:33:25Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268702\" lon=\"-122.4223000\"/>\n <node id=\"307447187\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T17:33:25Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268474\" lon=\"-122.4223125\"/>\n <node id=\"307447188\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T17:33:35Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268077\" lon=\"-122.4222252\"/>\n <node id=\"307447189\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:58Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268745\" lon=\"-122.4222271\"/>\n <node id=\"307453258\" visible=\"true\" version=\"7\" changeset=\"7256631\" timestamp=\"2011-02-11T16:15:47Z\" user=\"Yarl\" uid=\"91056\" lat=\"37.8264260\" lon=\"-122.4221443\"/>\n <node id=\"307453262\" visible=\"true\" version=\"5\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:20Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8262122\" lon=\"-122.4224379\"/>\n <node id=\"307453263\" visible=\"true\" version=\"6\" changeset=\"63050340\" timestamp=\"2018-09-29T22:59:26Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8261859\" lon=\"-122.4222843\"/>\n <node id=\"307453379\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8271429\" lon=\"-122.4230235\"/>\n <node id=\"307453382\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8270073\" lon=\"-122.4228089\"/>\n <node id=\"307453384\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268988\" lon=\"-122.4226544\"/>\n <node id=\"307453386\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8267361\" lon=\"-122.4224399\"/>\n <node id=\"307454851\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8258245\" lon=\"-122.4227291\"/>\n <node id=\"307454852\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8259294\" lon=\"-122.4227869\"/>\n <node id=\"307454854\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8259939\" lon=\"-122.4229635\"/>\n <node id=\"307454856\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8259700\" lon=\"-122.4231203\"/>\n <node id=\"307454858\" visible=\"true\" version=\"3\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:28Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260901\" lon=\"-122.4234293\"/>\n <node id=\"307455182\" visible=\"true\" version=\"12\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266033\" lon=\"-122.4207207\"/>\n <node id=\"307455183\" visible=\"true\" version=\"9\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265198\" lon=\"-122.4208095\"/>\n <node id=\"307455184\" visible=\"true\" version=\"9\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266589\" lon=\"-122.4210191\"/>\n <node id=\"307455185\" visible=\"true\" version=\"9\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8267423\" lon=\"-122.4209305\"/>\n <node id=\"307456157\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T17:33:36Z\" user=\"will l\" uid=\"35195\" lat=\"37.8267768\" lon=\"-122.4222596\"/>\n <node id=\"307456158\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T17:33:39Z\" user=\"will l\" uid=\"35195\" lat=\"37.8268717\" lon=\"-122.4224313\"/>\n <node id=\"307456159\" visible=\"true\" version=\"5\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:16Z\" user=\"will l\" uid=\"35195\" lat=\"37.8267903\" lon=\"-122.4225171\"/>\n <node id=\"307456636\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271737\" lon=\"-122.4234080\"/>\n <node id=\"307456732\" visible=\"true\" version=\"10\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272682\" lon=\"-122.4236113\"/>\n <node id=\"307456734\" visible=\"true\" version=\"8\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272370\" lon=\"-122.4235831\"/>\n <node id=\"307456736\" visible=\"true\" version=\"8\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272679\" lon=\"-122.4235284\"/>\n <node id=\"307456737\" visible=\"true\" version=\"8\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272990\" lon=\"-122.4235567\"/>\n <node id=\"307457176\" visible=\"true\" version=\"4\" changeset=\"66048128\" timestamp=\"2019-01-05T14:17:37Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270154\" lon=\"-122.4240917\"/>\n <node id=\"307457179\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268558\" lon=\"-122.4240366\"/>\n <node id=\"307457182\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266966\" lon=\"-122.4237621\"/>\n <node id=\"307458264\" visible=\"true\" version=\"2\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:35Z\" user=\"phut\" uid=\"27824\" lat=\"37.8262666\" lon=\"-122.4211063\"/>\n <node id=\"307458267\" visible=\"true\" version=\"2\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:35Z\" user=\"phut\" uid=\"27824\" lat=\"37.8264185\" lon=\"-122.4213353\"/>\n <node id=\"307458269\" visible=\"true\" version=\"2\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:34Z\" user=\"phut\" uid=\"27824\" lat=\"37.8266246\" lon=\"-122.4212888\"/>\n <node id=\"307458273\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T17:42:35Z\" user=\"will l\" uid=\"35195\" lat=\"37.8266819\" lon=\"-122.4213756\"/>\n <node id=\"307458274\" visible=\"true\" version=\"2\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:33Z\" user=\"phut\" uid=\"27824\" lat=\"37.8268112\" lon=\"-122.4216098\"/>\n <node id=\"307458622\" visible=\"true\" version=\"3\" changeset=\"66048128\" timestamp=\"2019-01-05T14:17:37Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272790\" lon=\"-122.4241222\"/>\n <node id=\"307458623\" visible=\"true\" version=\"2\" changeset=\"66048128\" timestamp=\"2019-01-05T14:17:37Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266955\" lon=\"-122.4236195\"/>\n <node id=\"307458624\" visible=\"true\" version=\"4\" changeset=\"66048128\" timestamp=\"2019-01-05T14:17:37Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268332\" lon=\"-122.4233348\"/>\n <node id=\"307458627\" visible=\"true\" version=\"3\" changeset=\"66048128\" timestamp=\"2019-01-05T14:17:37Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8274396\" lon=\"-122.4238220\"/>\n <node id=\"307459464\" visible=\"true\" version=\"8\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:52Z\" user=\"will l\" uid=\"35195\" lat=\"37.8275768\" lon=\"-122.4230619\"/>\n <node id=\"307459465\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:52Z\" user=\"will l\" uid=\"35195\" lat=\"37.8274276\" lon=\"-122.4229291\"/>\n <node id=\"307459466\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:52Z\" user=\"will l\" uid=\"35195\" lat=\"37.8273191\" lon=\"-122.4227832\"/>\n <node id=\"307459622\" visible=\"true\" version=\"5\" changeset=\"6060751\" timestamp=\"2010-10-16T19:35:39Z\" user=\"Walter Schlögl\" uid=\"78656\" lat=\"37.8279428\" lon=\"-122.4243196\">\n  <tag k=\"access\" v=\"private\"/><tag k=\"barrier\" v=\"gate\"/>\n </node>\n <node id=\"307459624\" visible=\"true\" version=\"3\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:36Z\" user=\"will l\" uid=\"35195\" lat=\"37.8276242\" lon=\"-122.4239505\"/>\n <node id=\"307459677\" visible=\"true\" version=\"6\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8279922\" lon=\"-122.4243622\"/>\n <node id=\"307459822\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T17:52:47Z\" user=\"will l\" uid=\"35195\" lat=\"37.8278683\" lon=\"-122.4248946\"/>\n <node id=\"307459823\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T17:52:47Z\" user=\"will l\" uid=\"35195\" lat=\"37.8279157\" lon=\"-122.4247487\"/>\n <node id=\"307459824\" visible=\"true\" version=\"3\" changeset=\"571017\" timestamp=\"2008-10-25T18:13:40Z\" user=\"will l\" uid=\"35195\" lat=\"37.8279582\" lon=\"-122.4246659\"/>\n <node id=\"307465040\" visible=\"true\" version=\"2\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:36Z\" user=\"phut\" uid=\"27824\" lat=\"37.8271677\" lon=\"-122.4233250\"/>\n <node id=\"307697056\" visible=\"true\" version=\"9\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270247\" lon=\"-122.4241767\"/>\n <node id=\"307697058\" visible=\"true\" version=\"7\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270394\" lon=\"-122.4241437\"/>\n <node id=\"307697131\" visible=\"true\" version=\"6\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269760\" lon=\"-122.4240988\"/>\n <node id=\"307697132\" visible=\"true\" version=\"6\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269614\" lon=\"-122.4241318\"/>\n <node id=\"739598009\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:12:21Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8253762\" lon=\"-122.4212779\"/>\n <node id=\"739598012\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253550\" lon=\"-122.4211833\"/>\n <node id=\"739598015\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254152\" lon=\"-122.4205947\"/>\n <node id=\"739598018\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8257007\" lon=\"-122.4219819\"/>\n <node id=\"739598020\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256668\" lon=\"-122.4209000\"/>\n <node id=\"739598025\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8263530\" lon=\"-122.4213814\"/>\n <node id=\"739598029\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255002\" lon=\"-122.4214680\"/>\n <node id=\"739598031\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253462\" lon=\"-122.4211056\"/>\n <node id=\"739598033\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255551\" lon=\"-122.4221903\"/>\n <node id=\"739598035\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8253431\" lon=\"-122.4214434\"/>\n <node id=\"739598038\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:33Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8257145\" lon=\"-122.4210042\"/>\n <node id=\"739598042\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256174\" lon=\"-122.4212683\"/>\n <node id=\"739598045\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254566\" lon=\"-122.4207839\"/>\n <node id=\"739598049\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256934\" lon=\"-122.4219179\"/>\n <node id=\"739598051\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8259249\" lon=\"-122.4209940\"/>\n <node id=\"739598055\" visible=\"true\" version=\"2\" changeset=\"59183284\" timestamp=\"2018-05-22T15:17:34Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8263971\" lon=\"-122.4213030\"/>\n <node id=\"739598059\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8254320\" lon=\"-122.4214240\"/>\n <node id=\"739598063\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256581\" lon=\"-122.4212001\"/>\n <node id=\"739598066\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255862\" lon=\"-122.4217157\"/>\n <node id=\"739598071\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8254333\" lon=\"-122.4214099\"/>\n <node id=\"739598074\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:34Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8258338\" lon=\"-122.4208844\"/>\n <node id=\"739598076\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8261154\" lon=\"-122.4205159\"/>\n <node id=\"739598080\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251206\" lon=\"-122.4211489\"/>\n <node id=\"739598085\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8256559\" lon=\"-122.4207024\"/>\n <node id=\"739598088\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:35Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8253491\" lon=\"-122.4214665\"/>\n <node id=\"739598089\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:35Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256532\" lon=\"-122.4216594\"/>\n <node id=\"739598091\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:35Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256695\" lon=\"-122.4216631\"/>\n <node id=\"739598095\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:35Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256032\" lon=\"-122.4222034\"/>\n <node id=\"739598097\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:35Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8260155\" lon=\"-122.4211065\"/>\n <node id=\"739598100\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:36Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8257483\" lon=\"-122.4204259\"/>\n <node id=\"739598101\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253090\" lon=\"-122.4208745\"/>\n <node id=\"739598103\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255835\" lon=\"-122.4208507\"/>\n <node id=\"739598106\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:36Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255826\" lon=\"-122.4213123\"/>\n <node id=\"739598108\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:36Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8252948\" lon=\"-122.4215568\"/>\n <node id=\"739598110\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:36Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256300\" lon=\"-122.4216108\"/>\n <node id=\"739598111\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:36Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256361\" lon=\"-122.4215908\"/>\n <node id=\"739598114\" visible=\"true\" version=\"2\" changeset=\"59183284\" timestamp=\"2018-05-22T15:17:33Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8263926\" lon=\"-122.4208604\"/>\n <node id=\"739598116\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:36Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8252887\" lon=\"-122.4217273\"/>\n <node id=\"739598118\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8256668\" lon=\"-122.4207837\"/>\n <node id=\"739598120\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252491\" lon=\"-122.4207537\"/>\n <node id=\"739598122\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:36Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256995\" lon=\"-122.4221046\"/>\n <node id=\"739598126\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254061\" lon=\"-122.4210676\"/>\n <node id=\"739598127\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:37Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8257016\" lon=\"-122.4211240\"/>\n <node id=\"739598129\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8256040\" lon=\"-122.4204938\"/>\n <node id=\"739598130\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:37Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8255825\" lon=\"-122.4216208\"/>\n <node id=\"739598131\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:37Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8253845\" lon=\"-122.4212585\"/>\n <node id=\"739598133\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:37Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256076\" lon=\"-122.4217186\"/>\n <node id=\"739598134\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:37Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8253722\" lon=\"-122.4218716\"/>\n <node id=\"739598135\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254786\" lon=\"-122.4209673\"/>\n <node id=\"739598137\" visible=\"true\" version=\"1\" changeset=\"4749900\" timestamp=\"2010-05-19T17:13:37Z\" user=\"delphiN\" uid=\"13192\" lat=\"37.8256432\" lon=\"-122.4211879\"/>\n <node id=\"1147514103\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262163\" lon=\"-122.4221652\"/>\n <node id=\"1147514104\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262455\" lon=\"-122.4221663\"/>\n <node id=\"1147514105\" visible=\"true\" version=\"3\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266988\" lon=\"-122.4231768\"/>\n <node id=\"1147514106\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262297\" lon=\"-122.4221744\"/>\n <node id=\"1147514108\" visible=\"true\" version=\"3\" changeset=\"66048128\" timestamp=\"2019-01-05T14:17:37Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271835\" lon=\"-122.4235957\"/>\n <node id=\"1147514110\" visible=\"true\" version=\"1\" changeset=\"7256631\" timestamp=\"2011-02-11T16:15:46Z\" user=\"Yarl\" uid=\"91056\" lat=\"37.8270849\" lon=\"-122.4231766\"/>\n <node id=\"1147514113\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262415\" lon=\"-122.4221304\"/>\n <node id=\"1147514115\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262499\" lon=\"-122.4221461\"/>\n <node id=\"1147514117\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262124\" lon=\"-122.4221487\"/>\n <node id=\"1147514119\" visible=\"true\" version=\"1\" changeset=\"7256631\" timestamp=\"2011-02-11T16:15:46Z\" user=\"Yarl\" uid=\"91056\" lat=\"37.8272745\" lon=\"-122.4234700\"/>\n <node id=\"1147514120\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262178\" lon=\"-122.4221337\"/>\n <node id=\"1147514121\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262285\" lon=\"-122.4221268\"/>\n <node id=\"1417681432\" visible=\"true\" version=\"3\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266908\" lon=\"-122.4209852\"/>\n <node id=\"1417681433\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8269443\" lon=\"-122.4217188\"/>\n <node id=\"1417681435\" visible=\"true\" version=\"2\" changeset=\"66048128\" timestamp=\"2019-01-05T14:17:37Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271206\" lon=\"-122.4239856\">\n  <tag k=\"barrier\" v=\"gate\"/>\n </node>\n <node id=\"1417681436\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8269189\" lon=\"-122.4222928\"/>\n <node id=\"1417681437\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8277896\" lon=\"-122.4238270\"/>\n <node id=\"1417681438\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8269623\" lon=\"-122.4229781\"/>\n <node id=\"1417681439\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265163\" lon=\"-122.4236856\"/>\n <node id=\"1417681444\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8266731\" lon=\"-122.4223478\"/>\n <node id=\"1417681445\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8267790\" lon=\"-122.4214171\">\n  <tag k=\"tourism\" v=\"information\"/>\n </node>\n <node id=\"1417681447\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8272398\" lon=\"-122.4226402\"/>\n <node id=\"1417681448\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8264062\" lon=\"-122.4220420\"/>\n <node id=\"1417681449\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8266509\" lon=\"-122.4221225\"/>\n <node id=\"1417681450\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:28Z\" user=\"phut\" uid=\"27824\" lat=\"37.8272208\" lon=\"-122.4229661\"/>\n <node id=\"1417681452\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8267031\" lon=\"-122.4210044\"/>\n <node id=\"1417681453\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8270545\" lon=\"-122.4231082\"/>\n <node id=\"1417681454\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265445\" lon=\"-122.4235970\"/>\n <node id=\"1417681456\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8268987\" lon=\"-122.4224323\"/>\n <node id=\"1417681457\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8264517\" lon=\"-122.4219629\"/>\n <node id=\"1417681458\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8270905\" lon=\"-122.4219723\"/>\n <node id=\"1417681459\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8281740\" lon=\"-122.4256104\"/>\n <node id=\"1417681460\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8268426\" lon=\"-122.4225275\"/>\n <node id=\"1417681461\" visible=\"true\" version=\"4\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265359\" lon=\"-122.4223270\"/>\n <node id=\"1417681462\" visible=\"true\" version=\"4\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8264623\" lon=\"-122.4208766\"/>\n <node id=\"1417681463\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8269040\" lon=\"-122.4224631\"/>\n <node id=\"1417681464\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8268299\" lon=\"-122.4223357\"/>\n <node id=\"1417681465\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8271487\" lon=\"-122.4224752\"/>\n <node id=\"1417681466\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8266339\" lon=\"-122.4221440\"/>\n <node id=\"1417681468\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8280205\" lon=\"-122.4241771\"/>\n <node id=\"1417681469\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8274697\" lon=\"-122.4234167\"/>\n <node id=\"1417681470\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8265746\" lon=\"-122.4220782\"/>\n <node id=\"1417681472\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8273627\" lon=\"-122.4231471\"/>\n <node id=\"1417681473\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:29Z\" user=\"phut\" uid=\"27824\" lat=\"37.8268924\" lon=\"-122.4221547\"/>\n <node id=\"1417681474\" visible=\"true\" version=\"2\" changeset=\"12663035\" timestamp=\"2012-08-08T22:43:20Z\" user=\"nithinkamath\" uid=\"573196\" lat=\"37.8270442\" lon=\"-122.4220131\"/>\n <node id=\"1417681475\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:30Z\" user=\"phut\" uid=\"27824\" lat=\"37.8269920\" lon=\"-122.4221507\"/>\n <node id=\"1417681476\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:30Z\" user=\"phut\" uid=\"27824\" lat=\"37.8281195\" lon=\"-122.4256233\"/>\n <node id=\"1526523417\" visible=\"true\" version=\"1\" changeset=\"10008969\" timestamp=\"2011-12-01T15:28:01Z\" user=\"JessAk71\" uid=\"381909\" lat=\"37.8268181\" lon=\"-122.4216030\"/>\n <node id=\"1526523420\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262140\" lon=\"-122.4221406\"/>\n <node id=\"1526523423\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262475\" lon=\"-122.4221382\"/>\n <node id=\"1526523425\" visible=\"true\" version=\"3\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8264021\" lon=\"-122.4208428\"/>\n <node id=\"1526523430\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262357\" lon=\"-122.4221273\"/>\n <node id=\"1526523433\" visible=\"true\" version=\"3\" changeset=\"58254331\" timestamp=\"2018-04-20T08:28:38Z\" user=\"wheelmap_visitor\" uid=\"290680\" lat=\"37.8262052\" lon=\"-122.4224616\">\n  <tag k=\"toilets:wheelchair\" v=\"yes\"/><tag k=\"tourism\" v=\"viewpoint\"/><tag k=\"wheelchair\" v=\"limited\"/>\n </node>\n <node id=\"1526523436\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262130\" lon=\"-122.4221568\"/>\n <node id=\"1526523439\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262429\" lon=\"-122.4221694\"/>\n <node id=\"1526523442\" visible=\"true\" version=\"3\" changeset=\"52209203\" timestamp=\"2017-09-20T12:11:39Z\" user=\"Dedus D D\" uid=\"4509564\" lat=\"37.8253259\" lon=\"-122.4213619\">\n  <tag k=\"name\" v=\"Alcatraz\"/><tag k=\"tourism\" v=\"viewpoint\"/><tag k=\"wheelchair\" v=\"limited\"/>\n </node>\n <node id=\"1526523446\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262490\" lon=\"-122.4221588\"/>\n <node id=\"1526523448\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262366\" lon=\"-122.4221735\"/>\n <node id=\"1526523481\" visible=\"true\" version=\"2\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\" lat=\"37.8266151\" lon=\"-122.4213351\">\n  <tag k=\"name\" v=\"Alcatraz Giftshop\"/><tag k=\"name:pt\" v=\"Loja de Presentes Alcatraz\"/><tag k=\"shop\" v=\"gift\"/><tag k=\"source\" v=\"Bing\"/>\n </node>\n <node id=\"1526523484\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262213\" lon=\"-122.4221709\"/>\n <node id=\"1526523486\" visible=\"true\" version=\"7\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\" lat=\"37.8266704\" lon=\"-122.4208218\">\n  <tag k=\"amenity\" v=\"ferry_terminal\"/><tag k=\"ferry\" v=\"yes\"/><tag k=\"name\" v=\"Alcatraz Ferry Terminal\"/><tag k=\"name:pt\" v=\"Terminal de Balsa de Alcatraz\"/><tag k=\"operator\" v=\"Alcatraz Cruises, LLC\"/><tag k=\"public_transport\" v=\"stop_position\"/>\n </node>\n <node id=\"1526523490\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262226\" lon=\"-122.4221292\"/>\n <node id=\"1526527959\" visible=\"true\" version=\"1\" changeset=\"10008969\" timestamp=\"2011-12-01T15:30:03Z\" user=\"JessAk71\" uid=\"381909\" lat=\"37.8268449\" lon=\"-122.4229523\">\n  <tag k=\"tourism\" v=\"information\"/>\n </node>\n <node id=\"1641119437\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:24Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8262188\" lon=\"-122.4229102\"/>\n <node id=\"1641119448\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265794\" lon=\"-122.4236449\"/>\n <node id=\"1641119453\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:24Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8261172\" lon=\"-122.4225077\"/>\n <node id=\"1641119458\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:24Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260251\" lon=\"-122.4232676\"/>\n <node id=\"1641119461\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:24Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8261517\" lon=\"-122.4226823\"/>\n <node id=\"1641119466\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:24Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260640\" lon=\"-122.4220996\"/>\n <node id=\"1641119470\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265897\" lon=\"-122.4236358\"/>\n <node id=\"1641119476\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:25Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8257081\" lon=\"-122.4225136\"/>\n <node id=\"1641119477\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:25Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8257651\" lon=\"-122.4226203\"/>\n <node id=\"1641119478\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265732\" lon=\"-122.4235467\"/>\n <node id=\"1641119479\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265981\" lon=\"-122.4236030\"/>\n <node id=\"1641119481\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268685\" lon=\"-122.4240520\"/>\n <node id=\"1641119483\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270920\" lon=\"-122.4235150\"/>\n <node id=\"1641119484\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8265934\" lon=\"-122.4235809\"/>\n <node id=\"1641119486\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:25Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8262899\" lon=\"-122.4231439\"/>\n <node id=\"1641119488\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:25Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260709\" lon=\"-122.4233933\"/>\n <node id=\"1641119490\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:25Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8256159\" lon=\"-122.4223636\"/>\n <node id=\"1641119501\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8263680\" lon=\"-122.4235658\"/>\n <node id=\"1641119504\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8259722\" lon=\"-122.4230662\"/>\n <node id=\"1641119508\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8259974\" lon=\"-122.4231939\"/>\n <node id=\"1641119511\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268792\" lon=\"-122.4240632\"/>\n <node id=\"1641119513\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:25Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8269669\" lon=\"-122.4240835\"/>\n <node id=\"1641119516\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:26Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8261720\" lon=\"-122.4234946\"/>\n <node id=\"1641119520\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262502\" lon=\"-122.4221506\"/>\n <node id=\"1641119524\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:26Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8269543\" lon=\"-122.4241045\"/>\n <node id=\"1641119527\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:26Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260526\" lon=\"-122.4233438\"/>\n <node id=\"1641119531\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:26Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8263238\" lon=\"-122.4232313\"/>\n <node id=\"1641119546\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:26Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260813\" lon=\"-122.4220557\"/>\n <node id=\"1641119548\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:26Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260838\" lon=\"-122.4223215\"/>\n <node id=\"1641119551\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:26Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8269877\" lon=\"-122.4240829\"/>\n <node id=\"1641119553\" visible=\"true\" version=\"1\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:26Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8261717\" lon=\"-122.4227524\"/>\n <node id=\"2027966328\" visible=\"true\" version=\"1\" changeset=\"13998200\" timestamp=\"2012-11-23T12:36:09Z\" user=\"achims311\" uid=\"52898\" lat=\"37.8270681\" lon=\"-122.4222388\"/>\n <node id=\"2113145715\" visible=\"true\" version=\"7\" changeset=\"27604782\" timestamp=\"2014-12-21T09:38:29Z\" user=\"malcolmh\" uid=\"128186\" lat=\"37.8280442\" lon=\"-122.4255358\">\n  <tag k=\"noaa:geohash\" v=\"468dc0f8411d\"/><tag k=\"noaa:lnam\" v=\"13fb0fb7\"/><tag k=\"noaa:taghash\" v=\"4577c29d2bcf9219ae4f037344d3ad80dcb37af2\"/><tag k=\"seamark:fog_signal:category\" v=\"horn\"/><tag k=\"seamark:fog_signal:generation\" v=\"automatic\"/><tag k=\"seamark:fog_signal:group\" v=\"2\"/><tag k=\"seamark:fog_signal:period\" v=\"30\"/><tag k=\"seamark:fog_signal:sequence\" v=\"02.0+(02.0)+02.0+(24.0)\"/><tag k=\"seamark:landmark:conspicuity\" v=\"conspicuous\"/><tag k=\"seamark:light:category\" v=\"floodlight\"/><tag k=\"seamark:light:character\" v=\"F\"/><tag k=\"seamark:light:colour\" v=\"amber\"/><tag k=\"seamark:light:exhibition\" v=\"night\"/><tag k=\"seamark:status\" v=\"permanent\"/><tag k=\"seamark:type\" v=\"landmark\"/>\n </node>\n <node id=\"2213560687\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8261538\" lon=\"-122.4217471\"/>\n <node id=\"2213560690\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8273378\" lon=\"-122.4234535\"/>\n <node id=\"2213560692\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260352\" lon=\"-122.4218334\"/>\n <node id=\"2213560700\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260852\" lon=\"-122.4217843\"/>\n <node id=\"2213560702\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8263469\" lon=\"-122.4215926\"/>\n <node id=\"2213560704\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8259700\" lon=\"-122.4219276\"/>\n <node id=\"2213560706\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8272952\" lon=\"-122.4234433\"/>\n <node id=\"2213560707\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262392\" lon=\"-122.4218535\"/>\n <node id=\"2213560709\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8263899\" lon=\"-122.4215455\"/>\n <node id=\"2213560711\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8260103\" lon=\"-122.4218633\"/>\n <node id=\"2213560713\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8273969\" lon=\"-122.4233843\"/>\n <node id=\"2213560715\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8273921\" lon=\"-122.4233363\"/>\n <node id=\"2213560717\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262519\" lon=\"-122.4218669\"/>\n <node id=\"2213560719\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8273791\" lon=\"-122.4234309\"/>\n <node id=\"2213560720\" visible=\"true\" version=\"1\" changeset=\"15447404\" timestamp=\"2013-03-21T20:06:59Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8261191\" lon=\"-122.4217666\"/>\n <node id=\"2213794398\" visible=\"true\" version=\"1\" changeset=\"15448995\" timestamp=\"2013-03-21T22:30:27Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8272374\" lon=\"-122.4224816\"/>\n <node id=\"2213794399\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271609\" lon=\"-122.4222329\"/>\n <node id=\"2213794400\" visible=\"true\" version=\"1\" changeset=\"15448995\" timestamp=\"2013-03-21T22:30:27Z\" user=\"RAW\" uid=\"63807\" lat=\"37.8272245\" lon=\"-122.4224936\"/>\n <node id=\"2406851985\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8261393\" lon=\"-122.4221067\"/>\n <node id=\"2406851986\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8261656\" lon=\"-122.4220670\"/>\n <node id=\"2406851987\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8261834\" lon=\"-122.4221535\"/>\n <node id=\"2406851988\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262097\" lon=\"-122.4221137\"/>\n <node id=\"2406851989\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275419\" lon=\"-122.4235064\"/>\n <node id=\"2406851990\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275562\" lon=\"-122.4235471\"/>\n <node id=\"2406851991\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275865\" lon=\"-122.4234812\"/>\n <node id=\"2406851992\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276009\" lon=\"-122.4235219\"/>\n <node id=\"2407548213\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8260332\" lon=\"-122.4221509\"/>\n <node id=\"2407548214\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8260594\" lon=\"-122.4220336\"/>\n <node id=\"2407548215\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8260777\" lon=\"-122.4221391\"/>\n <node id=\"2407548216\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8260791\" lon=\"-122.4221136\"/>\n <node id=\"2407548217\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8260831\" lon=\"-122.4221639\"/>\n <node id=\"2407548218\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8260847\" lon=\"-122.4220927\"/>\n <node id=\"2407548219\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8260918\" lon=\"-122.4221829\"/>\n <node id=\"2407548220\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8261251\" lon=\"-122.4226570\"/>\n <node id=\"2407548221\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8261314\" lon=\"-122.4222260\"/>\n <node id=\"2407548222\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8261322\" lon=\"-122.4218812\"/>\n <node id=\"2407548223\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8261435\" lon=\"-122.4220086\"/>\n <node id=\"2407548225\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8261921\" lon=\"-122.4222817\"/>\n <node id=\"2407548226\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8262055\" lon=\"-122.4223525\"/>\n <node id=\"2407548227\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8262139\" lon=\"-122.4223532\"/>\n <node id=\"2407548229\" visible=\"true\" version=\"3\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262306\" lon=\"-122.4221503\">\n  <tag k=\"seamark:name\" v=\"Lighthouse\"/><tag k=\"seamark:type\" v=\"landmark\"/>\n </node>\n <node id=\"2407548232\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8262486\" lon=\"-122.4221987\"/>\n <node id=\"2407548233\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8262544\" lon=\"-122.4222046\"/>\n <node id=\"2407548238\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8262984\" lon=\"-122.4221378\"/>\n <node id=\"2407548239\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8263275\" lon=\"-122.4221888\"/>\n <node id=\"2407548240\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8263342\" lon=\"-122.4221796\"/>\n <node id=\"2407548241\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8263359\" lon=\"-122.4221980\"/>\n <node id=\"2407548242\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:17Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8263372\" lon=\"-122.4221889\">\n  <tag k=\"emergency\" v=\"fire_hydrant\"/><tag k=\"fire_hydrant:position\" v=\"sidewalk\"/><tag k=\"fire_hydrant:type\" v=\"pillar\"/>\n </node>\n <node id=\"2407548247\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8264461\" lon=\"-122.4220798\"/>\n <node id=\"2407548248\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8264516\" lon=\"-122.4220708\"/>\n <node id=\"2407548249\" visible=\"true\" version=\"2\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275485\" lon=\"-122.4238341\">\n  <tag k=\"seamark:landmark:category\" v=\"tower\"/><tag k=\"seamark:name\" v=\"Water tower\"/><tag k=\"seamark:type\" v=\"landmark\"/>\n </node>\n <node id=\"2407548250\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8277451\" lon=\"-122.4236998\"/>\n <node id=\"2407548251\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8277597\" lon=\"-122.4238259\"/>\n <node id=\"2407548252\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\" lat=\"37.8280206\" lon=\"-122.4242526\"/>\n <node id=\"3054493437\" visible=\"true\" version=\"4\" changeset=\"46480561\" timestamp=\"2017-02-28T23:19:46Z\" user=\"BizV\" uid=\"5404994\" lat=\"37.8268591\" lon=\"-122.4227493\">\n  <tag k=\"addr:state\" v=\"CA\"/><tag k=\"amenity\" v=\"toilets\"/><tag k=\"building\" v=\"yes\"/><tag k=\"ele\" v=\"32\"/><tag k=\"gnis:county_name\" v=\"San Francisco\"/><tag k=\"gnis:feature_id\" v=\"1657175\"/><tag k=\"gnis:import_uuid\" v=\"57871b70-0100-4405-bb30-88b2e001a944\"/><tag k=\"gnis:reviewed\" v=\"no\"/><tag k=\"name\" v=\"Alcatraz\"/><tag k=\"source\" v=\"USGS Geonames\"/><tag k=\"toilets:wheelchair\" v=\"yes\"/><tag k=\"wheelchair\" v=\"yes\"/>\n </node>\n <node id=\"3202359193\" visible=\"true\" version=\"7\" changeset=\"69833614\" timestamp=\"2019-05-03T08:59:47Z\" user=\"gpesquero\" uid=\"3584\" lat=\"37.8070389\" lon=\"-122.4041405\">\n  <tag k=\"addr:city\" v=\"San Francisco\"/><tag k=\"addr:housenumber\" v=\"Pier 33\"/><tag k=\"addr:postcode\" v=\"94111\"/><tag k=\"addr:state\" v=\"CA\"/><tag k=\"addr:street\" v=\"Alcatraz Landing\"/><tag k=\"amenity\" v=\"ferry_terminal\"/><tag k=\"name\" v=\"Alcatraz Cruises, LLC\"/><tag k=\"name:ca\" v=\"Ferry Alcatraz\"/><tag k=\"name:en\" v=\"Alcatraz Cruises, LLC\"/><tag k=\"name:zh\" v=\"恶魔岛渡轮\"/><tag k=\"operator\" v=\"Alcatraz Cruises, LLC\"/><tag k=\"toilets:wheelchair\" v=\"yes\"/><tag k=\"wheelchair\" v=\"yes\"/>\n </node>\n <node id=\"3202364305\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269274\" lon=\"-122.4241733\"/>\n <node id=\"3202364306\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269514\" lon=\"-122.4241288\"/>\n <node id=\"3202364307\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270479\" lon=\"-122.4242927\"/>\n <node id=\"3202364308\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270774\" lon=\"-122.4243445\"/>\n <node id=\"3202364309\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270878\" lon=\"-122.4243583\">\n  <tag k=\"entrance\" v=\"yes\"/>\n </node>\n <node id=\"3202364310\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8278188\" lon=\"-122.4248746\">\n  <tag k=\"entrance\" v=\"main\"/>\n </node>\n <node id=\"3994498157\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276814\" lon=\"-122.4244759\"/>\n <node id=\"3994498158\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:20Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275329\" lon=\"-122.4242608\"/>\n <node id=\"3994498159\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276187\" lon=\"-122.4241659\"/>\n <node id=\"3994498160\" visible=\"true\" version=\"2\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8277671\" lon=\"-122.4243810\"/>\n <node id=\"4553243887\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252290\" lon=\"-122.4208047\">\n  <tag k=\"amenity\" v=\"bench\"/>\n </node>\n <node id=\"4553243888\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:52Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254858\" lon=\"-122.4205524\">\n  <tag k=\"amenity\" v=\"bench\"/>\n </node>\n <node id=\"4553243989\" visible=\"true\" version=\"1\" changeset=\"44388316\" timestamp=\"2016-12-14T03:28:02Z\" user=\"FTA\" uid=\"652301\" lat=\"37.8256396\" lon=\"-122.4207475\"/>\n <node id=\"4553243990\" visible=\"true\" version=\"1\" changeset=\"44388316\" timestamp=\"2016-12-14T03:28:02Z\" user=\"FTA\" uid=\"652301\" lat=\"37.8256778\" lon=\"-122.4207891\"/>\n <node id=\"4553243991\" visible=\"true\" version=\"1\" changeset=\"44388316\" timestamp=\"2016-12-14T03:28:02Z\" user=\"FTA\" uid=\"652301\" lat=\"37.8258018\" lon=\"-122.4209095\"/>\n <node id=\"5784941541\" visible=\"true\" version=\"1\" changeset=\"61065995\" timestamp=\"2018-07-25T19:15:36Z\" user=\"randoogle\" uid=\"245874\" lat=\"37.8265179\" lon=\"-122.4209758\">\n  <tag k=\"tourism\" v=\"picnic_site\"/>\n </node>\n <node id=\"5939834263\" visible=\"true\" version=\"1\" changeset=\"63050340\" timestamp=\"2018-09-29T22:59:26Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8261020\" lon=\"-122.4222069\"/>\n <node id=\"5939835720\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8263806\" lon=\"-122.4221839\"/>\n <node id=\"5939835721\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8263254\" lon=\"-122.4220706\"/>\n <node id=\"5939835722\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8263319\" lon=\"-122.4221365\"/>\n <node id=\"5939835723\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8263291\" lon=\"-122.4221048\"/>\n <node id=\"5939835724\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8263835\" lon=\"-122.4221473\"/>\n <node id=\"5939835725\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8264374\" lon=\"-122.4221860\"/>\n <node id=\"5939835726\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8264945\" lon=\"-122.4222404\"/>\n <node id=\"5939835727\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\" lat=\"37.8265266\" lon=\"-122.4222765\"/>\n <node id=\"6185816189\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:18Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271676\" lon=\"-122.4222218\"/>\n <node id=\"6185816191\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:18Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280944\" lon=\"-122.4243261\"/>\n <node id=\"6185816192\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:18Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8281802\" lon=\"-122.4242915\"/>\n <node id=\"6185816193\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8282228\" lon=\"-122.4244599\"/>\n <node id=\"6185816194\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8283170\" lon=\"-122.4244635\"/>\n <node id=\"6185816196\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8283150\" lon=\"-122.4245438\"/>\n <node id=\"6185816198\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8282302\" lon=\"-122.4245405\"/>\n <node id=\"6185816200\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8282282\" lon=\"-122.4246255\"/>\n <node id=\"6185816202\" visible=\"true\" version=\"1\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280983\" lon=\"-122.4246190\"/>\n <node id=\"6187321698\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8274881\" lon=\"-122.4238312\"/>\n <node id=\"6187321699\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275191\" lon=\"-122.4239009\"/>\n <node id=\"6187321700\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8274910\" lon=\"-122.4238102\"/>\n <node id=\"6187321701\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276045\" lon=\"-122.4238624\"/>\n <node id=\"6187321702\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275423\" lon=\"-122.4237581\"/>\n <node id=\"6187321703\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275248\" lon=\"-122.4237637\"/>\n <node id=\"6187321704\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8274985\" lon=\"-122.4237911\"/>\n <node id=\"6187321705\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275066\" lon=\"-122.4238893\"/>\n <node id=\"6187321706\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275327\" lon=\"-122.4239078\"/>\n <node id=\"6187321707\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8274900\" lon=\"-122.4238535\"/>\n <node id=\"6187321708\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275488\" lon=\"-122.4239106\"/>\n <node id=\"6187321709\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275819\" lon=\"-122.4238976\"/>\n <node id=\"6187321710\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276007\" lon=\"-122.4237958\"/>\n <node id=\"6187321711\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275114\" lon=\"-122.4237738\"/>\n <node id=\"6187321712\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276074\" lon=\"-122.4238178\"/>\n <node id=\"6187321713\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275621\" lon=\"-122.4237596\"/>\n <node id=\"6187321714\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8276087\" lon=\"-122.4238396\"/>\n <node id=\"6187321715\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275771\" lon=\"-122.4237669\"/>\n <node id=\"6187321716\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275960\" lon=\"-122.4238812\"/>\n <node id=\"6187321717\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275670\" lon=\"-122.4239068\"/>\n <node id=\"6187321718\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8274965\" lon=\"-122.4238730\"/>\n <node id=\"6187321719\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275904\" lon=\"-122.4237791\"/>\n <node id=\"6187339867\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252416\" lon=\"-122.4207709\"/>\n <node id=\"6187339868\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254547\" lon=\"-122.4209381\"/>\n <node id=\"6187339869\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251927\" lon=\"-122.4208583\"/>\n <node id=\"6187339870\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269102\" lon=\"-122.4240852\"/>\n <node id=\"6187339871\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253937\" lon=\"-122.4219604\"/>\n <node id=\"6187339872\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254509\" lon=\"-122.4205729\"/>\n <node id=\"6187339873\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275858\" lon=\"-122.4248466\"/>\n <node id=\"6187339874\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253371\" lon=\"-122.4219060\"/>\n <node id=\"6187339875\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254639\" lon=\"-122.4205238\"/>\n <node id=\"6187339876\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253488\" lon=\"-122.4208530\"/>\n <node id=\"6187339877\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255278\" lon=\"-122.4209076\"/>\n <node id=\"6187339878\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:50Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251842\" lon=\"-122.4208369\"/>\n <node id=\"6187339879\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251711\" lon=\"-122.4208726\"/>\n <node id=\"6187339880\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255579\" lon=\"-122.4207370\"/>\n <node id=\"6187339881\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252167\" lon=\"-122.4209844\"/>\n <node id=\"6187339882\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251455\" lon=\"-122.4211147\"/>\n <node id=\"6187339883\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8262384\" lon=\"-122.4205950\"/>\n <node id=\"6187339884\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251199\" lon=\"-122.4211532\"/>\n <node id=\"6187341085\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252425\" lon=\"-122.4216273\"/>\n <node id=\"6187341086\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251234\" lon=\"-122.4211603\"/>\n <node id=\"6187341087\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8267221\" lon=\"-122.4238051\"/>\n <node id=\"6187341088\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253232\" lon=\"-122.4211635\"/>\n <node id=\"6187341089\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252492\" lon=\"-122.4207754\"/>\n <node id=\"6187341090\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8266691\" lon=\"-122.4237203\"/>\n <node id=\"6187341091\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252466\" lon=\"-122.4217013\"/>\n <node id=\"6187341092\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252773\" lon=\"-122.4206896\"/>\n <node id=\"6187341093\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8269295\" lon=\"-122.4240948\"/>\n <node id=\"6187341094\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254863\" lon=\"-122.4221485\"/>\n <node id=\"6187341095\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255273\" lon=\"-122.4205376\"/>\n <node id=\"6187341096\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271828\" lon=\"-122.4245778\"/>\n <node id=\"6187341097\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253765\" lon=\"-122.4211244\"/>\n <node id=\"6187341098\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254269\" lon=\"-122.4205455\"/>\n <node id=\"6187341099\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252111\" lon=\"-122.4210075\"/>\n <node id=\"6187341100\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8256273\" lon=\"-122.4208090\"/>\n <node id=\"6187341101\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252179\" lon=\"-122.4208352\"/>\n <node id=\"6187341102\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253400\" lon=\"-122.4208431\"/>\n <node id=\"6187341103\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251959\" lon=\"-122.4210144\"/>\n <node id=\"6187341104\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251330\" lon=\"-122.4211545\"/>\n <node id=\"6187341105\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8261966\" lon=\"-122.4205613\"/>\n <node id=\"6187341106\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251274\" lon=\"-122.4211610\"/>\n <node id=\"6187341107\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252850\" lon=\"-122.4212942\"/>\n <node id=\"6187341108\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251895\" lon=\"-122.4209076\"/>\n <node id=\"6187341109\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254977\" lon=\"-122.4208866\"/>\n <node id=\"6187341110\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252237\" lon=\"-122.4208387\"/>\n <node id=\"6187341111\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8268929\" lon=\"-122.4240745\"/>\n <node id=\"6187341112\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253120\" lon=\"-122.4218736\"/>\n <node id=\"6187341113\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255239\" lon=\"-122.4205283\"/>\n <node id=\"6187341114\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270250\" lon=\"-122.4242758\"/>\n <node id=\"6187341115\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253699\" lon=\"-122.4219295\"/>\n <node id=\"6187341116\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254534\" lon=\"-122.4205801\"/>\n <node id=\"6187341117\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8253152\" lon=\"-122.4208840\"/>\n <node id=\"6187341118\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254408\" lon=\"-122.4210174\"/>\n <node id=\"6187341119\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251360\" lon=\"-122.4212307\"/>\n <node id=\"6187341120\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251972\" lon=\"-122.4210324\"/>\n <node id=\"6187341121\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8255848\" lon=\"-122.4207253\"/>\n <node id=\"6187341122\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252405\" lon=\"-122.4209543\"/>\n <node id=\"6187341123\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8254308\" lon=\"-122.4205432\"/>\n <node id=\"6187341124\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251654\" lon=\"-122.4210783\"/>\n <node id=\"6187341125\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8257327\" lon=\"-122.4203839\"/>\n <node id=\"6187341126\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251308\" lon=\"-122.4211587\"/>\n <node id=\"6187341127\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252755\" lon=\"-122.4213487\"/>\n <node id=\"6187341128\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8251206\" lon=\"-122.4211573\"/>\n <node id=\"6187341129\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8264695\" lon=\"-122.4236169\"/>\n <node id=\"6187341130\" visible=\"true\" version=\"1\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:51Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8252669\" lon=\"-122.4214067\"/>\n <node id=\"6187341750\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279795\" lon=\"-122.4247304\"/>\n <node id=\"6187341751\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280585\" lon=\"-122.4246222\"/>\n <node id=\"6187341752\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280889\" lon=\"-122.4235093\"/>\n <node id=\"6187341753\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8272552\" lon=\"-122.4249189\"/>\n <node id=\"6187341754\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279757\" lon=\"-122.4250288\"/>\n <node id=\"6187341755\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8275772\" lon=\"-122.4248659\"/>\n <node id=\"6187341756\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8270237\" lon=\"-122.4245035\"/>\n <node id=\"6187341757\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8279910\" lon=\"-122.4247105\"/>\n <node id=\"6187341758\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8281056\" lon=\"-122.4236155\"/>\n <node id=\"6187341759\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8273410\" lon=\"-122.4249618\"/>\n <node id=\"6187341760\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271931\" lon=\"-122.4246216\"/>\n <node id=\"6187341761\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8280558\" lon=\"-122.4247123\"/>\n <node id=\"6187341762\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8281077\" lon=\"-122.4236952\"/>\n <node id=\"6187341763\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8271174\" lon=\"-122.4248392\"/>\n <node id=\"6187341764\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\" lat=\"37.8277915\" lon=\"-122.4250161\"/>\n <node id=\"6555988180\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8264160\" lon=\"-122.4208180\"/>\n <node id=\"6555988181\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8250726\" lon=\"-122.4216129\"/>\n <node id=\"6555988182\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8251100\" lon=\"-122.4218090\"/>\n <node id=\"6555988183\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8254677\" lon=\"-122.4225276\"/>\n <node id=\"6555988184\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8254888\" lon=\"-122.4225911\"/>\n <node id=\"6556000285\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8254955\" lon=\"-122.4226802\"/>\n <node id=\"6556000286\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8264904\" lon=\"-122.4237927\"/>\n <node id=\"6556000287\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8265692\" lon=\"-122.4242024\"/>\n <node id=\"6556000288\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8265699\" lon=\"-122.4242874\"/>\n <node id=\"6556000289\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8265650\" lon=\"-122.4243015\"/>\n <node id=\"6556000290\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8266318\" lon=\"-122.4245057\"/>\n <node id=\"6556000291\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8266339\" lon=\"-122.4245303\"/>\n <node id=\"6556000292\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8266871\" lon=\"-122.4245682\"/>\n <node id=\"6556000293\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8266755\" lon=\"-122.4245682\"/>\n <node id=\"6556000294\" visible=\"true\" version=\"1\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\" lat=\"37.8266999\" lon=\"-122.4245634\"/>\n <way id=\"24433344\" visible=\"true\" version=\"18\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\">\n  <nd ref=\"265561221\"/><nd ref=\"265561222\"/><nd ref=\"265561223\"/><nd ref=\"265561224\"/><nd ref=\"265561225\"/><nd ref=\"265561226\"/><nd ref=\"6556000285\"/><nd ref=\"265561227\"/><nd ref=\"6555988184\"/><nd ref=\"6555988183\"/><nd ref=\"265561228\"/><nd ref=\"265561229\"/><nd ref=\"265561230\"/><nd ref=\"265561231\"/><nd ref=\"6555988182\"/><nd ref=\"303666382\"/><nd ref=\"6555988181\"/><nd ref=\"265561232\"/><nd ref=\"265561233\"/><nd ref=\"265561234\"/><nd ref=\"265561235\"/><nd ref=\"6187339878\"/><nd ref=\"265561236\"/><nd ref=\"6187341092\"/><nd ref=\"265561237\"/><nd ref=\"6187341098\"/><nd ref=\"6187341123\"/><nd ref=\"6187339875\"/><nd ref=\"265561238\"/><nd ref=\"6187341125\"/><nd ref=\"265561240\"/><nd ref=\"265561241\"/><nd ref=\"265561242\"/><nd ref=\"6187341105\"/><nd ref=\"6187339883\"/><nd ref=\"265561243\"/><nd ref=\"6555988180\"/><nd ref=\"1417681462\"/><nd ref=\"265561244\"/><nd ref=\"265561245\"/><nd ref=\"265561246\"/><nd ref=\"1417681452\"/><nd ref=\"265561247\"/><nd ref=\"265561248\"/><nd ref=\"303666473\"/><nd ref=\"265561249\"/><nd ref=\"265561252\"/><nd ref=\"265561253\"/><nd ref=\"265561254\"/><nd ref=\"265561256\"/><nd ref=\"265561257\"/><nd ref=\"265561258\"/><nd ref=\"265561259\"/><nd ref=\"265561260\"/><nd ref=\"265561261\"/><nd ref=\"265561262\"/><nd ref=\"265561263\"/><nd ref=\"265561264\"/><nd ref=\"265561265\"/><nd ref=\"265561427\"/><nd ref=\"6187341752\"/><nd ref=\"265561428\"/><nd ref=\"6187341758\"/><nd ref=\"6187341762\"/><nd ref=\"265561429\"/><nd ref=\"265561430\"/><nd ref=\"265561431\"/><nd ref=\"265561432\"/><nd ref=\"265561433\"/><nd ref=\"265561434\"/><nd ref=\"265561436\"/><nd ref=\"265561437\"/><nd ref=\"265561439\"/><nd ref=\"265561440\"/><nd ref=\"265561441\"/><nd ref=\"265561442\"/><nd ref=\"265561443\"/><nd ref=\"265561444\"/><nd ref=\"265561445\"/><nd ref=\"265561446\"/><nd ref=\"265561447\"/><nd ref=\"265561448\"/><nd ref=\"1417681459\"/><nd ref=\"1417681476\"/><nd ref=\"265561449\"/><nd ref=\"265561450\"/><nd ref=\"265561451\"/><nd ref=\"265561452\"/><nd ref=\"265561453\"/><nd ref=\"265561454\"/><nd ref=\"265561455\"/><nd ref=\"265561456\"/><nd ref=\"265561457\"/><nd ref=\"265561458\"/><nd ref=\"265561459\"/><nd ref=\"265561460\"/><nd ref=\"6187341759\"/><nd ref=\"6187341753\"/><nd ref=\"265561461\"/><nd ref=\"6187341763\"/><nd ref=\"265561463\"/><nd ref=\"265561464\"/><nd ref=\"265561465\"/><nd ref=\"265561467\"/><nd ref=\"265561468\"/><nd ref=\"265561469\"/><nd ref=\"6556000294\"/><nd ref=\"6556000292\"/><nd ref=\"6556000293\"/><nd ref=\"265561470\"/><nd ref=\"6556000291\"/><nd ref=\"6556000290\"/><nd ref=\"265561471\"/><nd ref=\"265561472\"/><nd ref=\"265561473\"/><nd ref=\"6556000289\"/><nd ref=\"6556000288\"/><nd ref=\"265561474\"/><nd ref=\"6556000287\"/><nd ref=\"265561475\"/><nd ref=\"265561477\"/><nd ref=\"265561478\"/><nd ref=\"265561479\"/><nd ref=\"265561480\"/><nd ref=\"6556000286\"/><nd ref=\"265561481\"/><nd ref=\"265561482\"/><nd ref=\"265561483\"/><nd ref=\"265561484\"/><nd ref=\"265561485\"/><nd ref=\"265561487\"/><nd ref=\"265561488\"/><nd ref=\"265561489\"/><nd ref=\"265561490\"/><nd ref=\"265561221\"/><tag k=\"alt_name\" v=\"Alcatraz\"/><tag k=\"boundary\" v=\"national_park\"/><tag k=\"ele\" v=\"37\"/><tag k=\"gnis:county_id\" v=\"075\"/><tag k=\"gnis:created\" v=\"01/19/1981\"/><tag k=\"gnis:feature_id\" v=\"218080\"/><tag k=\"gnis:state_id\" v=\"06\"/><tag k=\"name\" v=\"Alcatraz Island\"/><tag k=\"natural\" v=\"coastline\"/><tag k=\"place\" v=\"islet\"/><tag k=\"source\" v=\"yahoo\"/><tag k=\"wikidata\" v=\"Q131354\"/><tag k=\"wikipedia\" v=\"en:Alcatraz Island\"/>\n </way>\n <way id=\"24433389\" visible=\"true\" version=\"4\" changeset=\"61569480\" timestamp=\"2018-08-11T16:36:26Z\" user=\"Edward\" uid=\"364\">\n  <nd ref=\"265561608\"/><nd ref=\"265561609\"/><nd ref=\"265561610\"/><nd ref=\"265561611\"/><nd ref=\"265561612\"/><nd ref=\"265561613\"/><nd ref=\"265561614\"/><nd ref=\"265561615\"/><nd ref=\"265561616\"/><nd ref=\"265561617\"/><nd ref=\"265561608\"/><tag k=\"building\" v=\"yes\"/><tag k=\"disused\" v=\"yes\"/><tag k=\"name\" v=\"Model Industries Building\"/><tag k=\"old_name\" v=\"Military Industries and Workshops Building\"/><tag k=\"wikidata\" v=\"Q1142654\"/>\n </way>\n <way id=\"24433395\" visible=\"true\" version=\"7\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"265561699\"/><nd ref=\"3202364310\"/><nd ref=\"265561700\"/><nd ref=\"265561701\"/><nd ref=\"265561702\"/><nd ref=\"265561705\"/><nd ref=\"265561706\"/><nd ref=\"265561707\"/><nd ref=\"3202364309\"/><nd ref=\"265561708\"/><nd ref=\"265561699\"/><tag k=\"building\" v=\"yes\"/><tag k=\"disused\" v=\"yes\"/><tag k=\"name\" v=\"New Industries Building\"/><tag k=\"name:pt\" v=\"Construção de novas indústrias\"/>\n </way>\n <way id=\"24433437\" visible=\"true\" version=\"10\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"265562149\"/><nd ref=\"265562163\"/><nd ref=\"265562164\"/><nd ref=\"265562165\"/><nd ref=\"307456636\"/><nd ref=\"1641119483\"/><nd ref=\"265562145\"/><nd ref=\"265562146\"/><nd ref=\"265562149\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Dining Hall\"/><tag k=\"name:pt\" v=\"Sala de jantar\"/>\n </way>\n <way id=\"24617219\" visible=\"true\" version=\"4\" changeset=\"49588498\" timestamp=\"2017-06-16T11:16:58Z\" user=\"Edward\" uid=\"364\">\n  <nd ref=\"267604015\"/><nd ref=\"267604016\"/><nd ref=\"267604017\"/><nd ref=\"267604018\"/><nd ref=\"267604019\"/><nd ref=\"267604023\"/><nd ref=\"267604015\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Building 64\"/><tag k=\"wikidata\" v=\"Q1142634\"/>\n </way>\n <way id=\"27609572\" visible=\"true\" version=\"2\" changeset=\"172031\" timestamp=\"2008-10-08T21:38:08Z\" user=\"GreyCat\" uid=\"45062\">\n  <nd ref=\"265561249\"/><nd ref=\"303176761\"/><tag k=\"created_by\" v=\"Potlatch 0.10d\"/><tag k=\"man_made\" v=\"pier\"/>\n </way>\n <way id=\"27989500\" visible=\"true\" version=\"12\" changeset=\"73085718\" timestamp=\"2019-08-06T21:45:11Z\" user=\"clay_c\" uid=\"119881\">\n  <nd ref=\"3202359193\"/><nd ref=\"307351589\"/><nd ref=\"307351590\"/><nd ref=\"307351591\"/><nd ref=\"307351592\"/><nd ref=\"307351593\"/><nd ref=\"307351594\"/><nd ref=\"307351595\"/><nd ref=\"307351596\"/><nd ref=\"307351597\"/><nd ref=\"307351598\"/><nd ref=\"307351599\"/><nd ref=\"307351600\"/><nd ref=\"307351607\"/><nd ref=\"307351608\"/><nd ref=\"307351609\"/><nd ref=\"307351610\"/><nd ref=\"307351611\"/><nd ref=\"307431021\"/><nd ref=\"1526523486\"/><tag k=\"bicycle\" v=\"yes\"/><tag k=\"duration\" v=\"00:15:00\"/><tag k=\"fee\" v=\"yes\"/><tag k=\"foot\" v=\"yes\"/><tag k=\"name\" v=\"Alcatraz - Pier 33 Alcatraz Landing\"/><tag k=\"operator\" v=\"Alcatraz Cruises, LLC\"/><tag k=\"payment:cash\" v=\"no\"/><tag k=\"payment:clipper\" v=\"no\"/><tag k=\"payment:prepaid_ticket\" v=\"yes\"/><tag k=\"route\" v=\"ferry\"/><tag k=\"vehicle\" v=\"no\"/>\n </way>\n <way id=\"27989501\" visible=\"true\" version=\"7\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:54Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"1526523425\"/><nd ref=\"307351633\"/><nd ref=\"307351636\"/><nd ref=\"307351641\"/><nd ref=\"307351642\"/><nd ref=\"307351643\"/><nd ref=\"307351644\"/><nd ref=\"307351645\"/><nd ref=\"307351646\"/><nd ref=\"307351647\"/><nd ref=\"307351648\"/><nd ref=\"6187339869\"/><nd ref=\"307351649\"/><nd ref=\"307351650\"/><nd ref=\"307351651\"/><nd ref=\"307351652\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"name\" v=\"The Agave Trail (Seasonal)\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"27989502\" visible=\"true\" version=\"6\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"307351652\"/><nd ref=\"307351653\"/><nd ref=\"6187341120\"/><nd ref=\"6187341099\"/><nd ref=\"307351654\"/><nd ref=\"307351655\"/><nd ref=\"6187341117\"/><nd ref=\"6187339876\"/><nd ref=\"307351658\"/><nd ref=\"307351661\"/><nd ref=\"307446157\"/><nd ref=\"307446163\"/><nd ref=\"307446167\"/><nd ref=\"307446170\"/><tag k=\"highway\" v=\"steps\"/><tag k=\"name\" v=\"The Agave Trail\"/>\n </way>\n <way id=\"27996721\" visible=\"true\" version=\"5\" changeset=\"66048024\" timestamp=\"2019-01-05T14:13:21Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"307428498\"/><nd ref=\"6185816191\"/><nd ref=\"6185816192\"/><nd ref=\"307428500\"/><nd ref=\"6185816193\"/><nd ref=\"6185816194\"/><nd ref=\"6185816196\"/><nd ref=\"6185816198\"/><nd ref=\"6185816200\"/><nd ref=\"307428501\"/><nd ref=\"307428502\"/><nd ref=\"307428503\"/><nd ref=\"6185816202\"/><nd ref=\"307428498\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Powerhouse\"/><tag k=\"wikidata\" v=\"Q1142612\"/>\n </way>\n <way id=\"27996723\" visible=\"true\" version=\"4\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"307428613\"/><nd ref=\"307428615\"/><nd ref=\"307428616\"/><nd ref=\"307428617\"/><nd ref=\"307428613\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Quartermaster\"/><tag k=\"name:pt\" v=\"Quarto principal\"/>\n </way>\n <way id=\"27996732\" visible=\"true\" version=\"7\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"307428691\"/><nd ref=\"307428692\"/><nd ref=\"307428693\"/><nd ref=\"307428694\"/><nd ref=\"307428691\"/><tag k=\"building\" v=\"yes\"/><tag k=\"disused\" v=\"yes\"/><tag k=\"name\" v=\"Post Exchange &amp; Officers' Club\"/><tag k=\"name:pt\" v=\"Post Exchange &amp; Officers' Club\"/>\n </way>\n <way id=\"27996743\" visible=\"true\" version=\"5\" changeset=\"36107842\" timestamp=\"2015-12-22T16:46:11Z\" user=\"wheelmap_visitor\" uid=\"290680\">\n  <nd ref=\"307428865\"/><nd ref=\"307428867\"/><nd ref=\"307428869\"/><nd ref=\"307428871\"/><nd ref=\"307428865\"/><tag k=\"amenity\" v=\"toilets\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Restrooms\"/><tag k=\"toilets:wheelchair\" v=\"yes\"/><tag k=\"wheelchair\" v=\"yes\"/>\n </way>\n <way id=\"27996759\" visible=\"true\" version=\"9\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"307429152\"/><nd ref=\"2213794398\"/><nd ref=\"2213794400\"/><nd ref=\"307429153\"/><nd ref=\"307429155\"/><nd ref=\"307429156\"/><nd ref=\"307429157\"/><nd ref=\"307429159\"/><nd ref=\"6185816189\"/><nd ref=\"307446865\"/><nd ref=\"2213794399\"/><nd ref=\"307429152\"/><tag k=\"building\" v=\"yes\"/><tag k=\"layer\" v=\"1\"/><tag k=\"name\" v=\"Sally Port\"/><tag k=\"name:pt\" v=\"Porto de Sally\"/>\n </way>\n <way id=\"27996775\" visible=\"true\" version=\"3\" changeset=\"571017\" timestamp=\"2008-10-25T17:00:42Z\" user=\"will l\" uid=\"35195\">\n  <nd ref=\"307429230\"/><nd ref=\"307429231\"/><nd ref=\"307429232\"/><nd ref=\"307429233\"/><nd ref=\"307429230\"/><tag k=\"building\" v=\"yes\"/><tag k=\"created_by\" v=\"Potlatch 0.10e\"/><tag k=\"name\" v=\"Electric Shop\"/>\n </way>\n <way id=\"27996789\" visible=\"true\" version=\"6\" changeset=\"49588642\" timestamp=\"2017-06-16T11:23:42Z\" user=\"Edward\" uid=\"364\">\n  <nd ref=\"307429407\"/><nd ref=\"307429408\"/><nd ref=\"2213560717\"/><nd ref=\"2213560707\"/><nd ref=\"307429409\"/><nd ref=\"307429410\"/><nd ref=\"307429411\"/><nd ref=\"307429412\"/><nd ref=\"307429407\"/><tag k=\"building\" v=\"yes\"/><tag k=\"disused\" v=\"yes\"/><tag k=\"historic\" v=\"ruins\"/><tag k=\"name\" v=\"Warden's House\"/><tag k=\"start_date\" v=\"1934\"/><tag k=\"wikidata\" v=\"Q1142648\"/>\n </way>\n <way id=\"27996792\" visible=\"true\" version=\"9\" changeset=\"36107842\" timestamp=\"2015-12-22T16:45:31Z\" user=\"wheelmap_visitor\" uid=\"290680\">\n  <nd ref=\"307429450\"/><nd ref=\"307429451\"/><nd ref=\"307429452\"/><nd ref=\"2407548247\"/><nd ref=\"2407548248\"/><nd ref=\"307429453\"/><nd ref=\"307429450\"/><tag k=\"amenity\" v=\"toilets\"/><tag k=\"building\" v=\"yes\"/><tag k=\"fee\" v=\"no\"/><tag k=\"name\" v=\"Restrooms\"/><tag k=\"toilets:wheelchair\" v=\"yes\"/><tag k=\"wheelchair\" v=\"yes\"/>\n </way>\n <way id=\"27996842\" visible=\"true\" version=\"7\" changeset=\"47452285\" timestamp=\"2017-04-04T18:57:42Z\" user=\"Azlux\" uid=\"5586237\">\n  <nd ref=\"307429818\"/><nd ref=\"307429819\"/><nd ref=\"307429820\"/><nd ref=\"739598133\"/><nd ref=\"739598091\"/><nd ref=\"739598111\"/><nd ref=\"307429821\"/><nd ref=\"307429822\"/><nd ref=\"739598071\"/><nd ref=\"739598035\"/><nd ref=\"307429823\"/><nd ref=\"307429827\"/><nd ref=\"739598009\"/><nd ref=\"307429828\"/><nd ref=\"307429829\"/><nd ref=\"739598063\"/><nd ref=\"307429830\"/><nd ref=\"307429831\"/><nd ref=\"307429832\"/><nd ref=\"4553243991\"/><nd ref=\"307429833\"/><nd ref=\"307429834\"/><nd ref=\"307429835\"/><nd ref=\"307429836\"/><nd ref=\"2213560709\"/><nd ref=\"307429837\"/><nd ref=\"2213560702\"/><nd ref=\"2213560687\"/><nd ref=\"2213560720\"/><nd ref=\"2213560700\"/><nd ref=\"2213560692\"/><nd ref=\"2213560711\"/><nd ref=\"2213560704\"/><nd ref=\"307429818\"/><tag k=\"area\" v=\"yes\"/><tag k=\"highway\" v=\"pedestrian\"/><tag k=\"name\" v=\"Parade Ground\"/><tag k=\"surface\" v=\"concrete\"/>\n </way>\n <way id=\"27996878\" visible=\"true\" version=\"5\" changeset=\"10745510\" timestamp=\"2012-02-20T21:55:30Z\" user=\"RAW\" uid=\"63807\">\n  <nd ref=\"307446378\"/><nd ref=\"1641119490\"/><nd ref=\"1641119476\"/><nd ref=\"1641119477\"/><nd ref=\"307454851\"/><nd ref=\"307454852\"/><nd ref=\"307454854\"/><nd ref=\"1641119504\"/><nd ref=\"307454856\"/><nd ref=\"1641119508\"/><nd ref=\"1641119458\"/><nd ref=\"1641119527\"/><nd ref=\"1641119488\"/><nd ref=\"307454858\"/><nd ref=\"307430019\"/><tag k=\"access\" v=\"private\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"27996944\" visible=\"true\" version=\"7\" changeset=\"59183284\" timestamp=\"2018-05-22T15:17:35Z\" user=\"KaL3288\" uid=\"7134350\">\n  <nd ref=\"307465040\"/><nd ref=\"307447107\"/><nd ref=\"307447083\"/><nd ref=\"307447072\"/><nd ref=\"307447065\"/><nd ref=\"307447058\"/><nd ref=\"2213560706\"/><nd ref=\"307447057\"/><nd ref=\"2213560690\"/><nd ref=\"307447054\"/><nd ref=\"2213560719\"/><nd ref=\"307447053\"/><nd ref=\"2213560713\"/><nd ref=\"307447052\"/><nd ref=\"2213560715\"/><nd ref=\"307447044\"/><nd ref=\"307447042\"/><nd ref=\"307447041\"/><nd ref=\"307447029\"/><nd ref=\"307446900\"/><nd ref=\"307430635\"/><nd ref=\"307447189\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"27998971\" visible=\"true\" version=\"6\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:54Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"307446378\"/><nd ref=\"6187341094\"/><nd ref=\"6187339871\"/><nd ref=\"6187341115\"/><nd ref=\"6187339874\"/><nd ref=\"6187341112\"/><nd ref=\"307446349\"/><nd ref=\"307446325\"/><nd ref=\"6187341091\"/><nd ref=\"307446313\"/><nd ref=\"6187341085\"/><nd ref=\"307446235\"/><nd ref=\"6187341130\"/><nd ref=\"6187341127\"/><nd ref=\"6187341107\"/><nd ref=\"307446189\"/><nd ref=\"6187341088\"/><nd ref=\"739598031\"/><nd ref=\"307446184\"/><nd ref=\"307446172\"/><nd ref=\"6187339868\"/><nd ref=\"6187341109\"/><nd ref=\"307446174\"/><nd ref=\"307446170\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"27998972\" visible=\"true\" version=\"3\" changeset=\"12663035\" timestamp=\"2012-08-08T22:43:00Z\" user=\"nithinkamath\" uid=\"573196\">\n  <nd ref=\"1526523417\"/><nd ref=\"307446860\"/><nd ref=\"307446857\"/><nd ref=\"1417681474\"/><nd ref=\"307446854\"/><nd ref=\"307446865\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"name\" v=\"East Road\"/>\n </way>\n <way id=\"27998974\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:54Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"307446836\"/><nd ref=\"6187339873\"/><nd ref=\"6187341096\"/><nd ref=\"307446838\"/><tag k=\"access\" v=\"private\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"27998976\" visible=\"true\" version=\"11\" changeset=\"10008969\" timestamp=\"2011-12-01T15:28:04Z\" user=\"JessAk71\" uid=\"381909\">\n  <nd ref=\"307446864\"/><nd ref=\"307446842\"/><nd ref=\"307446839\"/><nd ref=\"307459464\"/><nd ref=\"307446432\"/><nd ref=\"307446477\"/><nd ref=\"307446480\"/><nd ref=\"307446487\"/><nd ref=\"307446527\"/><nd ref=\"307446815\"/><nd ref=\"307446816\"/><nd ref=\"307446821\"/><nd ref=\"307446822\"/><nd ref=\"307446823\"/><nd ref=\"307459677\"/><nd ref=\"307446824\"/><nd ref=\"307446827\"/><nd ref=\"307446829\"/><nd ref=\"307459824\"/><nd ref=\"307446833\"/><nd ref=\"307446834\"/><nd ref=\"307446835\"/><nd ref=\"307446836\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"name\" v=\"East Road\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"27998977\" visible=\"true\" version=\"3\" changeset=\"10008969\" timestamp=\"2011-12-01T15:28:05Z\" user=\"JessAk71\" uid=\"381909\">\n  <nd ref=\"307446865\"/><nd ref=\"307446845\"/><nd ref=\"307446843\"/><nd ref=\"307446864\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"layer\" v=\"-1\"/><tag k=\"name\" v=\"East Road\"/><tag k=\"tunnel\" v=\"yes\"/>\n </way>\n <way id=\"27998980\" visible=\"true\" version=\"4\" changeset=\"15448995\" timestamp=\"2013-03-21T22:30:28Z\" user=\"RAW\" uid=\"63807\">\n  <nd ref=\"2213794399\"/><nd ref=\"307446867\"/><tag k=\"bridge\" v=\"yes\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"layer\" v=\"2\"/>\n </way>\n <way id=\"27998983\" visible=\"true\" version=\"4\" changeset=\"13998200\" timestamp=\"2012-11-23T12:36:09Z\" user=\"achims311\" uid=\"52898\">\n  <nd ref=\"307446868\"/><nd ref=\"2027966328\"/><nd ref=\"307446870\"/><nd ref=\"307446871\"/><nd ref=\"307446872\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"27998996\" visible=\"true\" version=\"6\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\">\n  <nd ref=\"307430636\"/><nd ref=\"307430637\"/><nd ref=\"307453258\"/><nd ref=\"5939835721\"/><nd ref=\"307430638\"/><nd ref=\"307430035\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"27998998\" visible=\"true\" version=\"4\" changeset=\"59183284\" timestamp=\"2018-05-22T15:17:35Z\" user=\"KaL3288\" uid=\"7134350\">\n  <nd ref=\"307447189\"/><nd ref=\"307447150\"/><nd ref=\"307447109\"/><nd ref=\"307447117\"/><nd ref=\"307447118\"/><nd ref=\"307447119\"/><nd ref=\"307429837\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"27998999\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T17:00:23Z\" user=\"will l\" uid=\"35195\">\n  <nd ref=\"307447189\"/><nd ref=\"307447121\"/><tag k=\"access\" v=\"private\"/><tag k=\"created_by\" v=\"JOSM\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"27999692\" visible=\"true\" version=\"5\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\">\n  <nd ref=\"5939835720\"/><nd ref=\"307453262\"/><nd ref=\"307453263\"/><nd ref=\"307430638\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"27999701\" visible=\"true\" version=\"4\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:16Z\" user=\"will l\" uid=\"35195\">\n  <nd ref=\"307447029\"/><nd ref=\"307453379\"/><nd ref=\"307453382\"/><nd ref=\"307453384\"/><nd ref=\"307456159\"/><nd ref=\"307453386\"/><nd ref=\"307430636\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"created_by\" v=\"Potlatch 0.10e\"/><tag k=\"highway\" v=\"service\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"27999800\" visible=\"true\" version=\"5\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:55Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"307430019\"/><nd ref=\"1641119516\"/><nd ref=\"307430020\"/><nd ref=\"1641119501\"/><nd ref=\"307430021\"/><nd ref=\"6187341129\"/><nd ref=\"307430022\"/><nd ref=\"307430023\"/><nd ref=\"307430024\"/><nd ref=\"1641119448\"/><nd ref=\"1641119470\"/><nd ref=\"307430025\"/><nd ref=\"1641119479\"/><nd ref=\"1641119484\"/><nd ref=\"307430026\"/><nd ref=\"1641119478\"/><nd ref=\"307430027\"/><nd ref=\"307430028\"/><nd ref=\"1641119531\"/><nd ref=\"307430029\"/><nd ref=\"1641119486\"/><nd ref=\"307430030\"/><nd ref=\"1641119437\"/><nd ref=\"1641119553\"/><nd ref=\"1641119461\"/><nd ref=\"307430031\"/><nd ref=\"1641119453\"/><nd ref=\"1641119548\"/><nd ref=\"307430032\"/><nd ref=\"307430033\"/><nd ref=\"307430034\"/><nd ref=\"1641119466\"/><nd ref=\"1641119546\"/><nd ref=\"307430035\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"name\" v=\"West Road\"/>\n </way>\n <way id=\"27999864\" visible=\"true\" version=\"8\" changeset=\"70368429\" timestamp=\"2019-05-17T18:03:53Z\" user=\"clay_c\" uid=\"119881\">\n  <nd ref=\"307455182\"/><nd ref=\"307455183\"/><nd ref=\"307455184\"/><nd ref=\"1417681432\"/><nd ref=\"307455185\"/><nd ref=\"1526523486\"/><nd ref=\"307455182\"/><tag k=\"area\" v=\"yes\"/><tag k=\"man_made\" v=\"pier\"/><tag k=\"wheelchair\" v=\"yes\"/>\n </way>\n <way id=\"27999973\" visible=\"true\" version=\"3\" changeset=\"4749900\" timestamp=\"2010-05-19T17:21:32Z\" user=\"delphiN\" uid=\"13192\">\n  <nd ref=\"307447121\"/><nd ref=\"307447150\"/><nd ref=\"307447179\"/><nd ref=\"307447180\"/><nd ref=\"307447187\"/><nd ref=\"307447188\"/><nd ref=\"307456157\"/><nd ref=\"307456158\"/><nd ref=\"307456159\"/><tag k=\"access\" v=\"private\"/><tag k=\"highway\" v=\"steps\"/>\n </way>\n <way id=\"28000041\" visible=\"true\" version=\"4\" changeset=\"15874039\" timestamp=\"2013-04-26T16:34:04Z\" user=\"Manu1400\" uid=\"181135\">\n  <nd ref=\"307456732\"/><nd ref=\"307456734\"/><nd ref=\"307456736\"/><nd ref=\"307456737\"/><nd ref=\"307456732\"/><tag k=\"amenity\" v=\"mortuary\"/><tag k=\"building\" v=\"yes\"/><tag k=\"disused\" v=\"yes\"/><tag k=\"name\" v=\"Morgue\"/>\n </way>\n <way id=\"28000072\" visible=\"true\" version=\"6\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:55Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"307457176\"/><nd ref=\"1641119551\"/><nd ref=\"1641119513\"/><nd ref=\"1641119524\"/><nd ref=\"6187341093\"/><nd ref=\"6187339870\"/><nd ref=\"6187341111\"/><nd ref=\"1641119511\"/><nd ref=\"1641119481\"/><nd ref=\"307457179\"/><nd ref=\"6187341087\"/><nd ref=\"307457182\"/><nd ref=\"6187341090\"/><nd ref=\"307430025\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/>\n </way>\n <way id=\"28000214\" visible=\"true\" version=\"9\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\">\n  <nd ref=\"307458264\"/><nd ref=\"739598055\"/><nd ref=\"307458267\"/><nd ref=\"307458269\"/><nd ref=\"307458273\"/><nd ref=\"307458274\"/><nd ref=\"1526523417\"/><nd ref=\"265561252\"/><nd ref=\"265561249\"/><nd ref=\"303666473\"/><nd ref=\"265561248\"/><nd ref=\"265561247\"/><nd ref=\"1417681452\"/><nd ref=\"265561246\"/><nd ref=\"265561245\"/><nd ref=\"265561244\"/><nd ref=\"1417681462\"/><nd ref=\"6555988180\"/><nd ref=\"1526523425\"/><nd ref=\"739598114\"/><nd ref=\"307458264\"/><tag k=\"area\" v=\"yes\"/><tag k=\"highway\" v=\"pedestrian\"/><tag k=\"name\" v=\"Dock\"/><tag k=\"surface\" v=\"concrete\"/>\n </way>\n <way id=\"28000400\" visible=\"true\" version=\"4\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:37Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"307459464\"/><nd ref=\"307459465\"/><nd ref=\"307459466\"/><nd ref=\"307446872\"/><nd ref=\"307446873\"/><nd ref=\"307446874\"/><nd ref=\"307446875\"/><nd ref=\"307446882\"/><nd ref=\"307447189\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"28000416\" visible=\"true\" version=\"4\" changeset=\"50700405\" timestamp=\"2017-07-30T18:54:29Z\" user=\"dpaschich\" uid=\"621202\">\n  <nd ref=\"307459677\"/><nd ref=\"307459622\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"28000429\" visible=\"true\" version=\"2\" changeset=\"571017\" timestamp=\"2008-10-25T18:14:37Z\" user=\"will l\" uid=\"35195\">\n  <nd ref=\"307459622\"/><nd ref=\"307459624\"/><tag k=\"access\" v=\"private\"/><tag k=\"created_by\" v=\"Potlatch 0.10e\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"source\" v=\"yahoo\"/>\n </way>\n <way id=\"28000456\" visible=\"true\" version=\"1\" changeset=\"571017\" timestamp=\"2008-10-25T17:52:47Z\" user=\"will l\" uid=\"35195\">\n  <nd ref=\"307459822\"/><nd ref=\"307459823\"/><nd ref=\"307459824\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"created_by\" v=\"Potlatch 0.10e\"/><tag k=\"highway\" v=\"service\"/>\n </way>\n <way id=\"28021912\" visible=\"true\" version=\"3\" changeset=\"585867\" timestamp=\"2008-10-26T15:00:03Z\" user=\"will l\" uid=\"35195\">\n  <nd ref=\"307697056\"/><nd ref=\"307697058\"/><nd ref=\"307697131\"/><nd ref=\"307697132\"/><nd ref=\"307697056\"/><tag k=\"building\" v=\"yes\"/><tag k=\"created_by\" v=\"Potlatch 0.10e\"/>\n </way>\n <way id=\"59621740\" visible=\"true\" version=\"2\" changeset=\"50700405\" timestamp=\"2017-07-30T18:54:30Z\" user=\"dpaschich\" uid=\"621202\">\n  <nd ref=\"307446378\"/><nd ref=\"307429818\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"59621862\" visible=\"true\" version=\"4\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:54Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"739598106\"/><nd ref=\"739598042\"/><nd ref=\"739598137\"/><nd ref=\"739598127\"/><nd ref=\"739598038\"/><nd ref=\"739598020\"/><nd ref=\"739598074\"/><nd ref=\"739598051\"/><nd ref=\"739598097\"/><nd ref=\"739598025\"/><nd ref=\"739598055\"/><nd ref=\"307458264\"/><nd ref=\"739598114\"/><nd ref=\"739598076\"/><nd ref=\"739598100\"/><nd ref=\"739598129\"/><nd ref=\"6187341113\"/><nd ref=\"6187341095\"/><nd ref=\"6187341116\"/><nd ref=\"6187339872\"/><nd ref=\"739598015\"/><nd ref=\"739598120\"/><nd ref=\"6187339867\"/><nd ref=\"6187341089\"/><nd ref=\"6187341110\"/><nd ref=\"6187341101\"/><nd ref=\"6187341108\"/><nd ref=\"739598080\"/><nd ref=\"6187339884\"/><nd ref=\"6187341128\"/><nd ref=\"6187341086\"/><nd ref=\"6187341106\"/><nd ref=\"6187341126\"/><nd ref=\"6187341104\"/><nd ref=\"6187339882\"/><nd ref=\"6187341124\"/><nd ref=\"6187341103\"/><nd ref=\"6187339881\"/><nd ref=\"6187341122\"/><nd ref=\"739598101\"/><nd ref=\"6187341102\"/><nd ref=\"739598045\"/><nd ref=\"6187339880\"/><nd ref=\"6187341121\"/><nd ref=\"739598085\"/><nd ref=\"739598118\"/><nd ref=\"6187341100\"/><nd ref=\"739598103\"/><nd ref=\"6187339877\"/><nd ref=\"739598135\"/><nd ref=\"6187341118\"/><nd ref=\"739598126\"/><nd ref=\"6187341097\"/><nd ref=\"739598012\"/><nd ref=\"739598131\"/><nd ref=\"739598106\"/><tag k=\"historic\" v=\"ruins\"/><tag k=\"name\" v=\"Rubble Pile\"/><tag k=\"natural\" v=\"scree\"/>\n </way>\n <way id=\"59621863\" visible=\"true\" version=\"2\" changeset=\"33170040\" timestamp=\"2015-08-07T03:57:50Z\" user=\"nvk\" uid=\"131996\">\n  <nd ref=\"739598095\"/><nd ref=\"739598122\"/><nd ref=\"739598018\"/><nd ref=\"739598049\"/><nd ref=\"739598066\"/><nd ref=\"739598089\"/><nd ref=\"739598110\"/><nd ref=\"739598130\"/><nd ref=\"739598029\"/><nd ref=\"739598059\"/><nd ref=\"739598088\"/><nd ref=\"739598108\"/><nd ref=\"739598116\"/><nd ref=\"739598134\"/><nd ref=\"739598033\"/><nd ref=\"739598095\"/><tag k=\"historic\" v=\"ruins\"/><tag k=\"name\" v=\"Rubble Pile\"/><tag k=\"natural\" v=\"scree\"/>\n </way>\n <way id=\"99202294\" visible=\"true\" version=\"5\" changeset=\"49588642\" timestamp=\"2017-06-16T11:24:01Z\" user=\"Edward\" uid=\"364\">\n  <nd ref=\"1147514104\"/><nd ref=\"1526523446\"/><nd ref=\"1641119520\"/><nd ref=\"1147514115\"/><nd ref=\"1526523423\"/><nd ref=\"1147514113\"/><nd ref=\"1526523430\"/><nd ref=\"1147514121\"/><nd ref=\"1526523490\"/><nd ref=\"1147514120\"/><nd ref=\"1526523420\"/><nd ref=\"1147514117\"/><nd ref=\"1526523436\"/><nd ref=\"1147514103\"/><nd ref=\"1526523484\"/><nd ref=\"1147514106\"/><nd ref=\"1526523448\"/><nd ref=\"1526523439\"/><nd ref=\"1147514104\"/><tag k=\"alt_name\" v=\"United States Coast Guard Lighthouse\"/><tag k=\"building\" v=\"yes\"/><tag k=\"man_made\" v=\"lighthouse\"/><tag k=\"name\" v=\"Alcatraz Island Lighthouse\"/><tag k=\"start_date\" v=\"1909\"/><tag k=\"wikidata\" v=\"Q4712967\"/>\n </way>\n <way id=\"99202295\" visible=\"true\" version=\"2\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:39Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"1147514105\"/><nd ref=\"307458624\"/><nd ref=\"307458623\"/><nd ref=\"1417681435\"/><nd ref=\"307458622\"/><nd ref=\"307458627\"/><nd ref=\"1147514108\"/><nd ref=\"1147514119\"/><nd ref=\"1147514110\"/><nd ref=\"1417681453\"/><nd ref=\"1417681438\"/><tag k=\"barrier\" v=\"wall\"/>\n </way>\n <way id=\"128245363\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:30Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"1417681468\"/><nd ref=\"1417681437\"/><nd ref=\"307446487\"/><tag k=\"access\" v=\"permissive\"/><tag k=\"highway\" v=\"service\"/><tag k=\"source\" v=\"survey\"/>\n </way>\n <way id=\"128245364\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:30Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"1417681435\"/><nd ref=\"307457176\"/><tag k=\"highway\" v=\"steps\"/>\n </way>\n <way id=\"128245365\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:30Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"1417681469\"/><nd ref=\"1417681472\"/><nd ref=\"1417681450\"/><nd ref=\"1417681436\"/><tag k=\"barrier\" v=\"wall\"/>\n </way>\n <way id=\"128245366\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:30Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"1417681458\"/><nd ref=\"1417681433\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"128245367\" visible=\"true\" version=\"4\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"265562154\"/><nd ref=\"265562155\"/><nd ref=\"265562159\"/><nd ref=\"1417681461\"/><nd ref=\"265562154\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Administration Block\"/><tag k=\"name:pt\" v=\"Bloco de Administração\"/>\n </way>\n <way id=\"128245368\" visible=\"true\" version=\"2\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\">\n  <nd ref=\"1417681448\"/><nd ref=\"1417681457\"/><nd ref=\"1417681470\"/><nd ref=\"1417681466\"/><nd ref=\"1417681449\"/><nd ref=\"1417681464\"/><nd ref=\"1417681456\"/><nd ref=\"1417681463\"/><nd ref=\"1417681460\"/><nd ref=\"1417681444\"/><nd ref=\"307429450\"/><nd ref=\"307429453\"/><nd ref=\"2407548248\"/><nd ref=\"2407548247\"/><nd ref=\"307429452\"/><nd ref=\"1417681448\"/><tag k=\"leisure\" v=\"garden\"/>\n </way>\n <way id=\"128245369\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:30Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"1417681447\"/><nd ref=\"1417681465\"/><nd ref=\"1417681475\"/><nd ref=\"1417681473\"/><tag k=\"barrier\" v=\"wall\"/>\n </way>\n <way id=\"128245370\" visible=\"true\" version=\"2\" changeset=\"58579435\" timestamp=\"2018-05-01T09:39:55Z\" user=\"monena41\" uid=\"4633898\">\n  <nd ref=\"1417681432\"/><nd ref=\"1417681452\"/><tag k=\"amenity\" v=\"ferry_terminal\"/><tag k=\"cargo\" v=\"passengers\"/><tag k=\"man_made\" v=\"pier\"/><tag k=\"name\" v=\"Alcatraz Island Ferry Terminal\"/>\n </way>\n <way id=\"128245371\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:31Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"1417681474\"/><nd ref=\"1417681458\"/><tag k=\"bridge\" v=\"yes\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"128245372\" visible=\"true\" version=\"1\" changeset=\"9163280\" timestamp=\"2011-08-30T00:36:31Z\" user=\"phut\" uid=\"27824\">\n  <nd ref=\"1417681454\"/><nd ref=\"1417681439\"/><tag k=\"barrier\" v=\"fence\"/>\n </way>\n <way id=\"128245373\" visible=\"true\" version=\"4\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"265562149\"/><nd ref=\"1147514105\"/><nd ref=\"265562150\"/><nd ref=\"265562151\"/><nd ref=\"265562152\"/><nd ref=\"265562153\"/><nd ref=\"265562154\"/><nd ref=\"1417681461\"/><nd ref=\"265562161\"/><nd ref=\"265562162\"/><nd ref=\"307447104\"/><nd ref=\"265562163\"/><nd ref=\"265562149\"/><tag k=\"alt_name\" v=\"Cellhouse\"/><tag k=\"building\" v=\"yes\"/><tag k=\"historic\" v=\"yes\"/><tag k=\"name\" v=\"Main Prison\"/><tag k=\"name:pt\" v=\"Prisão principal\"/>\n </way>\n <way id=\"151291054\" visible=\"true\" version=\"4\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"1641119483\"/><nd ref=\"1147514108\"/><nd ref=\"307458627\"/><nd ref=\"307458622\"/><nd ref=\"1417681435\"/><nd ref=\"307458623\"/><nd ref=\"307458624\"/><nd ref=\"1147514105\"/><nd ref=\"265562149\"/><nd ref=\"265562146\"/><nd ref=\"265562145\"/><nd ref=\"1641119483\"/><tag k=\"area\" v=\"yes\"/><tag k=\"disused\" v=\"yes\"/><tag k=\"leisure\" v=\"pitch\"/><tag k=\"name\" v=\"Recreation Yard\"/><tag k=\"name:pt\" v=\"Quintal de recreação\"/><tag k=\"source\" v=\"yahoo\"/><tag k=\"sport\" v=\"multi\"/><tag k=\"wheelchair\" v=\"no\"/>\n </way>\n <way id=\"192237295\" visible=\"true\" version=\"1\" changeset=\"13998200\" timestamp=\"2012-11-23T12:36:09Z\" user=\"achims311\" uid=\"52898\">\n  <nd ref=\"307446867\"/><nd ref=\"2027966328\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"source\" v=\"bing\"/>\n </way>\n <way id=\"232326055\" visible=\"true\" version=\"1\" changeset=\"17214523\" timestamp=\"2013-08-04T12:47:36Z\" user=\"Nautic\" uid=\"449569\">\n  <nd ref=\"2406851988\"/><nd ref=\"2406851986\"/><nd ref=\"2406851985\"/><nd ref=\"2406851987\"/><nd ref=\"2406851988\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"232326056\" visible=\"true\" version=\"1\" changeset=\"17214523\" timestamp=\"2013-08-04T12:47:36Z\" user=\"Nautic\" uid=\"449569\">\n  <nd ref=\"2406851992\"/><nd ref=\"2406851991\"/><nd ref=\"2406851989\"/><nd ref=\"2406851990\"/><nd ref=\"2406851992\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"232403925\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\">\n  <nd ref=\"2407548241\"/><nd ref=\"2407548239\"/><nd ref=\"2407548240\"/><nd ref=\"2407548238\"/><nd ref=\"2407548233\"/><nd ref=\"2407548232\"/><nd ref=\"2407548225\"/><nd ref=\"2407548226\"/><nd ref=\"2407548227\"/><nd ref=\"2407548241\"/><tag k=\"landuse\" v=\"grass\"/>\n </way>\n <way id=\"232403927\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\">\n  <nd ref=\"2407548250\"/><nd ref=\"2407548251\"/><nd ref=\"2407548252\"/><tag k=\"barrier\" v=\"wall\"/>\n </way>\n <way id=\"232403928\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\">\n  <nd ref=\"2407548222\"/><nd ref=\"2407548214\"/><nd ref=\"2407548213\"/><nd ref=\"2407548220\"/><tag k=\"barrier\" v=\"wall\"/>\n </way>\n <way id=\"232403930\" visible=\"true\" version=\"1\" changeset=\"17223234\" timestamp=\"2013-08-05T06:04:18Z\" user=\"Nautic\" uid=\"449569\">\n  <nd ref=\"2407548223\"/><nd ref=\"2407548218\"/><nd ref=\"2407548216\"/><nd ref=\"2407548215\"/><nd ref=\"2407548217\"/><nd ref=\"2407548219\"/><nd ref=\"2407548221\"/><tag k=\"barrier\" v=\"wall\"/>\n </way>\n <way id=\"295140461\" visible=\"true\" version=\"6\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\">\n  <nd ref=\"265561257\"/><nd ref=\"265561256\"/><nd ref=\"265561254\"/><nd ref=\"265561253\"/><nd ref=\"265561252\"/><nd ref=\"265561249\"/><nd ref=\"303666473\"/><nd ref=\"265561248\"/><nd ref=\"265561247\"/><nd ref=\"1417681452\"/><nd ref=\"265561246\"/><nd ref=\"265561245\"/><nd ref=\"265561244\"/><nd ref=\"1417681462\"/><nd ref=\"6555988180\"/><nd ref=\"265561243\"/><nd ref=\"6187339883\"/><nd ref=\"6187341105\"/><nd ref=\"265561242\"/><nd ref=\"265561241\"/><nd ref=\"265561240\"/><nd ref=\"6187341125\"/><nd ref=\"265561238\"/><nd ref=\"6187339875\"/><nd ref=\"6187341098\"/><nd ref=\"265561237\"/><nd ref=\"6187341092\"/><nd ref=\"265561236\"/><nd ref=\"6187339878\"/><nd ref=\"6187339879\"/><nd ref=\"265561235\"/><nd ref=\"265561234\"/><nd ref=\"265561233\"/><nd ref=\"265561232\"/><nd ref=\"6555988181\"/><nd ref=\"303666382\"/><nd ref=\"6555988182\"/><nd ref=\"265561231\"/><nd ref=\"265561230\"/><nd ref=\"265561229\"/><nd ref=\"265561228\"/><nd ref=\"6555988183\"/><nd ref=\"6555988184\"/><nd ref=\"265561227\"/><nd ref=\"6556000285\"/><nd ref=\"265561226\"/><nd ref=\"265561225\"/><nd ref=\"265561224\"/><nd ref=\"265561223\"/><nd ref=\"265561222\"/><nd ref=\"265561221\"/><nd ref=\"265561490\"/><nd ref=\"265561489\"/><nd ref=\"265561488\"/><nd ref=\"265561487\"/><nd ref=\"265561485\"/><nd ref=\"265561484\"/><nd ref=\"265561483\"/><nd ref=\"265561482\"/><nd ref=\"265561481\"/><nd ref=\"6556000286\"/><nd ref=\"265561480\"/><nd ref=\"265561479\"/><nd ref=\"265561478\"/><nd ref=\"265561477\"/><nd ref=\"265561475\"/><nd ref=\"6556000287\"/><nd ref=\"265561474\"/><nd ref=\"6556000288\"/><nd ref=\"6556000289\"/><nd ref=\"265561473\"/><nd ref=\"265561472\"/><nd ref=\"265561471\"/><nd ref=\"6556000290\"/><nd ref=\"6556000291\"/><nd ref=\"265561470\"/><nd ref=\"6556000293\"/><nd ref=\"6556000292\"/><nd ref=\"6556000294\"/><nd ref=\"265561469\"/><nd ref=\"265561468\"/><nd ref=\"265561467\"/><nd ref=\"265561465\"/><nd ref=\"265561464\"/><nd ref=\"265561463\"/><nd ref=\"6187341763\"/><nd ref=\"265561461\"/><nd ref=\"6187341753\"/><nd ref=\"6187341759\"/><nd ref=\"265561460\"/><nd ref=\"265561459\"/><nd ref=\"265561458\"/><nd ref=\"265561457\"/><nd ref=\"265561456\"/><nd ref=\"265561455\"/><nd ref=\"265561454\"/><nd ref=\"265561453\"/><nd ref=\"265561452\"/><nd ref=\"265561451\"/><nd ref=\"265561450\"/><nd ref=\"265561449\"/><nd ref=\"1417681476\"/><nd ref=\"1417681459\"/><nd ref=\"265561448\"/><nd ref=\"265561447\"/><nd ref=\"265561446\"/><nd ref=\"265561445\"/><nd ref=\"265561444\"/><nd ref=\"265561443\"/><nd ref=\"265561442\"/><nd ref=\"265561441\"/><nd ref=\"265561440\"/><nd ref=\"265561439\"/><nd ref=\"265561437\"/><nd ref=\"265561436\"/><nd ref=\"265561434\"/><nd ref=\"265561433\"/><nd ref=\"265561432\"/><nd ref=\"265561431\"/><nd ref=\"265561430\"/><nd ref=\"265561429\"/><nd ref=\"6187341762\"/><nd ref=\"6187341758\"/><nd ref=\"265561428\"/><nd ref=\"6187341752\"/><nd ref=\"265561427\"/><nd ref=\"265561265\"/><nd ref=\"265561264\"/><nd ref=\"265561263\"/><nd ref=\"265561262\"/><nd ref=\"265561261\"/><nd ref=\"265561260\"/><nd ref=\"265561259\"/><nd ref=\"265561258\"/><nd ref=\"265561257\"/><tag k=\"admin_level\" v=\"1\"/><tag k=\"boundary\" v=\"national_park\"/><tag k=\"leisure\" v=\"common\"/><tag k=\"name\" v=\"Alcatraz Island (GGNRA)\"/><tag k=\"name:ko\" v=\"앨커트래즈 섬\"/><tag k=\"park:type\" v=\"national_recreational_area\"/><tag k=\"wikidata\" v=\"Q131354\"/>\n </way>\n <way id=\"314192471\" visible=\"true\" version=\"1\" changeset=\"26984131\" timestamp=\"2014-11-23T22:22:53Z\" user=\"dchiles\" uid=\"153669\">\n  <nd ref=\"3202364309\"/><nd ref=\"3202364308\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"314192472\" visible=\"true\" version=\"1\" changeset=\"26984131\" timestamp=\"2014-11-23T22:22:53Z\" user=\"dchiles\" uid=\"153669\">\n  <nd ref=\"3202364308\"/><nd ref=\"3202364307\"/><tag k=\"bridge\" v=\"yes\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"314192473\" visible=\"true\" version=\"2\" changeset=\"66063078\" timestamp=\"2019-01-06T06:17:53Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"3202364307\"/><nd ref=\"6187341114\"/><nd ref=\"3202364305\"/><nd ref=\"3202364306\"/><nd ref=\"1641119524\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"396629347\" visible=\"true\" version=\"1\" changeset=\"37071222\" timestamp=\"2016-02-08T00:17:35Z\" user=\"Johnny Mapperseed\" uid=\"1723055\">\n  <nd ref=\"3994498157\"/><nd ref=\"3994498158\"/><nd ref=\"3994498159\"/><nd ref=\"3994498160\"/><nd ref=\"3994498157\"/><tag k=\"area\" v=\"yes\"/><tag k=\"surface\" v=\"paved\"/>\n </way>\n <way id=\"459479815\" visible=\"true\" version=\"1\" changeset=\"44388316\" timestamp=\"2016-12-14T03:28:02Z\" user=\"FTA\" uid=\"652301\">\n  <nd ref=\"4553243991\"/><nd ref=\"4553243990\"/><nd ref=\"4553243989\"/><nd ref=\"307446170\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"629120566\" visible=\"true\" version=\"1\" changeset=\"63050340\" timestamp=\"2018-09-29T22:59:26Z\" user=\"Michiel M\" uid=\"9446\">\n  <nd ref=\"307453263\"/><nd ref=\"5939834263\"/><tag k=\"highway\" v=\"steps\"/>\n </way>\n <way id=\"629120567\" visible=\"true\" version=\"1\" changeset=\"63050340\" timestamp=\"2018-09-29T22:59:26Z\" user=\"Michiel M\" uid=\"9446\">\n  <nd ref=\"5939834263\"/><nd ref=\"307430033\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"629121014\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\">\n  <nd ref=\"307430638\"/><nd ref=\"5939835722\"/><tag k=\"highway\" v=\"steps\"/>\n </way>\n <way id=\"629121015\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\">\n  <nd ref=\"5939835722\"/><nd ref=\"5939835720\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"629121016\" visible=\"true\" version=\"1\" changeset=\"63050387\" timestamp=\"2018-09-29T23:03:14Z\" user=\"Michiel M\" uid=\"9446\">\n  <nd ref=\"5939835723\"/><nd ref=\"5939835724\"/><nd ref=\"5939835725\"/><nd ref=\"5939835726\"/><nd ref=\"5939835727\"/><tag k=\"barrier\" v=\"fence\"/>\n </way>\n <way id=\"660870452\" visible=\"true\" version=\"1\" changeset=\"66062793\" timestamp=\"2019-01-06T05:38:19Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"6187321709\"/><nd ref=\"6187321716\"/><nd ref=\"6187321701\"/><nd ref=\"6187321714\"/><nd ref=\"6187321712\"/><nd ref=\"6187321710\"/><nd ref=\"6187321719\"/><nd ref=\"6187321715\"/><nd ref=\"6187321713\"/><nd ref=\"6187321702\"/><nd ref=\"6187321703\"/><nd ref=\"6187321711\"/><nd ref=\"6187321704\"/><nd ref=\"6187321700\"/><nd ref=\"6187321698\"/><nd ref=\"6187321707\"/><nd ref=\"6187321718\"/><nd ref=\"6187321705\"/><nd ref=\"6187321699\"/><nd ref=\"6187321706\"/><nd ref=\"6187321708\"/><nd ref=\"6187321717\"/><nd ref=\"6187321709\"/><tag k=\"building\" v=\"yes\"/><tag k=\"disused\" v=\"yes\"/><tag k=\"man_made\" v=\"water_tower\"/><tag k=\"name\" v=\"Water Tower\"/>\n </way>\n <way id=\"660872606\" visible=\"true\" version=\"2\" changeset=\"71376374\" timestamp=\"2019-06-18T18:24:27Z\" user=\"KaL3288\" uid=\"7134350\">\n  <nd ref=\"6187341119\"/><nd ref=\"265561233\"/><nd ref=\"265561234\"/><nd ref=\"6187339879\"/><nd ref=\"6187339878\"/><nd ref=\"265561236\"/><nd ref=\"6187341092\"/><nd ref=\"265561237\"/><nd ref=\"6187341123\"/><nd ref=\"6187339875\"/><nd ref=\"265561238\"/><nd ref=\"6187341125\"/><nd ref=\"265561240\"/><nd ref=\"265561241\"/><nd ref=\"265561242\"/><nd ref=\"6187341105\"/><nd ref=\"6187339883\"/><nd ref=\"265561243\"/><nd ref=\"6555988180\"/><nd ref=\"1417681462\"/><tag k=\"barrier\" v=\"wall\"/>\n </way>\n <way id=\"660872945\" visible=\"true\" version=\"1\" changeset=\"66063123\" timestamp=\"2019-01-06T06:23:03Z\" user=\"KindredCoda\" uid=\"14293\">\n  <nd ref=\"6187341751\"/><nd ref=\"6187341761\"/><nd ref=\"6187341757\"/><nd ref=\"6187341750\"/><nd ref=\"6187341754\"/><nd ref=\"6187341764\"/><nd ref=\"6187341755\"/><nd ref=\"6187341760\"/><nd ref=\"6187341756\"/><tag k=\"barrier\" v=\"fence\"/>\n </way>\n <way id=\"698078900\" visible=\"true\" version=\"3\" changeset=\"73895096\" timestamp=\"2019-08-29T18:17:10Z\" user=\"eduaddad\" uid=\"7489282\">\n  <nd ref=\"6187341125\"/><nd ref=\"265561240\"/><nd ref=\"265561241\"/><nd ref=\"265561242\"/><nd ref=\"6187341105\"/><nd ref=\"6187339883\"/><nd ref=\"1417681462\"/><nd ref=\"265561244\"/><nd ref=\"265561245\"/><nd ref=\"265561246\"/><nd ref=\"265561247\"/><nd ref=\"265561248\"/><nd ref=\"303666473\"/><nd ref=\"265561249\"/><nd ref=\"265561252\"/><nd ref=\"265561253\"/><nd ref=\"265561254\"/><nd ref=\"265561256\"/><nd ref=\"265561257\"/><nd ref=\"265561258\"/><nd ref=\"265561259\"/><nd ref=\"265561260\"/><nd ref=\"265561261\"/><nd ref=\"265561262\"/><nd ref=\"265561263\"/><nd ref=\"265561264\"/><nd ref=\"265561265\"/><nd ref=\"265561427\"/><nd ref=\"6187341752\"/><nd ref=\"265561428\"/><nd ref=\"6187341758\"/><nd ref=\"6187341762\"/><nd ref=\"265561429\"/><nd ref=\"265561430\"/><nd ref=\"265561431\"/><nd ref=\"265561432\"/><nd ref=\"265561433\"/><nd ref=\"265561434\"/><nd ref=\"265561436\"/><nd ref=\"265561437\"/><nd ref=\"265561439\"/><nd ref=\"265561440\"/><nd ref=\"265561441\"/><nd ref=\"265561442\"/><nd ref=\"265561443\"/><nd ref=\"265561444\"/><nd ref=\"265561445\"/><nd ref=\"265561446\"/><nd ref=\"265561447\"/><nd ref=\"265561448\"/><nd ref=\"1417681459\"/><nd ref=\"1417681476\"/><nd ref=\"265561449\"/><nd ref=\"265561450\"/><nd ref=\"265561451\"/><nd ref=\"265561452\"/><nd ref=\"265561453\"/><nd ref=\"265561454\"/><nd ref=\"265561455\"/><nd ref=\"265561456\"/><nd ref=\"265561457\"/><nd ref=\"265561458\"/><nd ref=\"265561459\"/><nd ref=\"265561460\"/><nd ref=\"6187341759\"/><nd ref=\"265561461\"/><nd ref=\"6187341763\"/><nd ref=\"265561463\"/><nd ref=\"265561464\"/><nd ref=\"265561465\"/><nd ref=\"265561467\"/><nd ref=\"265561468\"/><nd ref=\"265561469\"/><nd ref=\"6556000294\"/><nd ref=\"6556000292\"/><nd ref=\"6556000293\"/><nd ref=\"265561470\"/><nd ref=\"6556000291\"/><nd ref=\"6556000290\"/><nd ref=\"265561471\"/><nd ref=\"265561472\"/><nd ref=\"265561473\"/><nd ref=\"6556000289\"/><nd ref=\"6556000288\"/><nd ref=\"265561474\"/><nd ref=\"6556000287\"/><nd ref=\"265561475\"/><nd ref=\"265561477\"/><nd ref=\"265561478\"/><nd ref=\"265561479\"/><nd ref=\"265561480\"/><nd ref=\"6556000286\"/><nd ref=\"265561481\"/><nd ref=\"265561482\"/><nd ref=\"265561483\"/><nd ref=\"265561484\"/><nd ref=\"265561485\"/><nd ref=\"265561487\"/><nd ref=\"265561488\"/><nd ref=\"265561489\"/><nd ref=\"265561490\"/><nd ref=\"265561221\"/><nd ref=\"265561222\"/><nd ref=\"265561223\"/><nd ref=\"265561224\"/><nd ref=\"265561225\"/><nd ref=\"265561226\"/><nd ref=\"6556000285\"/><nd ref=\"265561227\"/><nd ref=\"6555988184\"/><nd ref=\"6555988183\"/><nd ref=\"265561228\"/><nd ref=\"265561229\"/><nd ref=\"265561230\"/><nd ref=\"265561231\"/><nd ref=\"6555988182\"/><nd ref=\"303666382\"/><nd ref=\"6555988181\"/><nd ref=\"265561232\"/><nd ref=\"265561233\"/><nd ref=\"265561234\"/><nd ref=\"265561235\"/><nd ref=\"6187339878\"/><nd ref=\"265561236\"/><nd ref=\"6187341092\"/><nd ref=\"265561237\"/><nd ref=\"6187341123\"/><nd ref=\"6187339875\"/><nd ref=\"265561238\"/><nd ref=\"6187341125\"/><tag k=\"leisure\" v=\"park\"/><tag k=\"name\" v=\"Alcatraz Island\"/><tag k=\"name:es\" v=\"Isla de Alcatraz\"/><tag k=\"name:pt\" v=\"Ilha de Alcatraz\"/>\n </way>\n <relation id=\"9451753\" visible=\"true\" version=\"2\" changeset=\"69717377\" timestamp=\"2019-04-30T02:48:28Z\" user=\"StenSoft\" uid=\"255936\">\n  <member type=\"way\" ref=\"161075093\" role=\"inner\"/><member type=\"way\" ref=\"157449982\" role=\"inner\"/><member type=\"way\" ref=\"157429145\" role=\"inner\"/><member type=\"way\" ref=\"24433344\" role=\"inner\"/><member type=\"way\" ref=\"668082533\" role=\"outer\"/><member type=\"way\" ref=\"29399800\" role=\"inner\"/><tag k=\"name\" v=\"San Francisco Bay\"/><tag k=\"name:cs\" v=\"Sanfranciský záliv\"/><tag k=\"name:de\" v=\"Bucht von San Francisco\"/><tag k=\"natural\" v=\"bay\"/><tag k=\"type\" v=\"multipolygon\"/><tag k=\"wikidata\" v=\"Q232264\"/><tag k=\"wikipedia\" v=\"en:San Francisco Bay\"/>\n </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/data/ButterflyPark/ButterflyPark.atlas.txt",
    "content": "# Nodes\n5138971723000000 && 1.2556477,103.8175322 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707359000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n5133972846000000 && 1.2557524,103.817218 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707357000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n5175392808000000 && 1.2560357,103.8160113 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n4733403532000000 && 1.2542395,103.8179569 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n5209288659000000 && 1.2563089,103.8160091 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 2\n4731977864000000 && 1.2535375,103.8173137 && last_edit_user_name -> FlyTy || last_edit_changeset -> 54417250 || last_edit_time -> 1512603533000 || last_edit_user_id -> 6095805 || last_edit_version -> 2\n5138650779000000 && 1.2551525,103.8170011 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304368000 || last_edit_user_id -> 6346054 || last_edit_version -> 8\n4731913776000000 && 1.2552127,103.816772 && last_edit_user_name -> pushian || last_edit_changeset -> 46831836 || last_edit_time -> 1489476091000 || last_edit_user_id -> 4989626 || last_edit_version -> 2\n1717556458000000 && 1.2555488,103.8161391 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707350000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n5145931993000000 && 1.2560356,103.8161935 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707362000 || last_edit_user_id -> 7671613 || last_edit_version -> 8\n5138650784000000 && 1.2551718,103.8163177 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304368000 || last_edit_user_id -> 6346054 || last_edit_version -> 7\n5146232330000000 && 1.2554904,103.8160242 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707362000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 7\n5175384349000000 && 1.2552509,103.8187571 && last_edit_user_name -> CapAhab || last_edit_changeset -> 53052207 || last_edit_time -> 1508359102000 || last_edit_user_id -> 6172866 || last_edit_version -> 1\n5209288656000000 && 1.2551718,103.8167514 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || last_edit_version -> 1\n4568857132000000 && 1.2555951,103.8166206 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707355000 || last_edit_user_id -> 7671613 || last_edit_version -> 4\n5145931994000000 && 1.2558988,103.8160258 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707362000 || last_edit_user_id -> 7671613 || last_edit_version -> 8\n5138672444000000 && 1.2552652,103.8169729 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n1867526773000000 && 1.2556997,103.8160146 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707351000 || last_edit_user_id -> 7671613 || last_edit_version -> 8\n2558083041000000 && 1.2550414,103.8188556 && last_edit_user_name -> khatulistiwa || last_edit_changeset -> 19189134 || last_edit_time -> 1385784269000 || last_edit_user_id -> 94431 || last_edit_version -> 1\n471491305000000 && 1.256038,103.8164078 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707350000 || last_edit_user_id -> 7671613 || last_edit_version -> 10\n4566570125000000 && 1.2550724,103.8173982 && last_edit_user_name -> pushian || last_edit_changeset -> 52501454 || last_edit_time -> 1506756672000 || last_edit_user_id -> 4989626 || last_edit_version -> 2\n5138710145000000 && 1.2543873,103.819763 && last_edit_user_name -> pushian || last_edit_changeset -> 52497277 || last_edit_time -> 1506732398000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n2558468525000000 && 1.2558972,103.8186614 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707354000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n5138672449000000 && 1.2554976,103.8160891 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707358000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n4279963875000000 && 1.2555597,103.815744 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707355000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n2362722970000000 && 1.2545466,103.8177089 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n5138682822000000 && 1.2551056,103.8185825 && last_edit_user_name -> pushian || last_edit_changeset -> 52497277 || last_edit_time -> 1506732399000 || last_edit_user_id -> 4989626 || last_edit_version -> 2\n4566570124000000 && 1.2550877,103.8174563 && last_edit_user_name -> pushian || last_edit_changeset -> 44586417 || last_edit_time -> 1482399260000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n5137340959000000 && 1.255278,103.8171476 && last_edit_user_name -> pushian || last_edit_changeset -> 52472107 || last_edit_time -> 1506684670000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n5138971722000000 && 1.2557114,103.8172062 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707359000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n4731977868000000 && 1.2536168,103.8168848 && last_edit_user_name -> FlyTy || last_edit_changeset -> 54417250 || last_edit_time -> 1512603533000 || last_edit_user_id -> 6095805 || last_edit_version -> 2\n2663353454000000 && 1.2531882,103.8159215 && last_edit_user_name -> andi9876 || last_edit_changeset -> 20467454 || last_edit_time -> 1391961920000 || last_edit_user_id -> 1256497 || last_edit_version -> 1\n5138693281000000 && 1.2565672,103.8159474 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707358000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n2103671644000000 && 1.2542217,103.8170064 && last_edit_user_name -> alexrudd || last_edit_changeset -> 14593518 || last_edit_time -> 1357787367000 || last_edit_user_id -> 5611 || last_edit_version -> 1\n5138710151000000 && 1.2549677,103.8187549 && last_edit_user_name -> pushian || last_edit_changeset -> 52497277 || last_edit_time -> 1506732398000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n4733403539000000 && 1.2544035,103.8177439 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n5209288647000000 && 1.2551103,103.8188232 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749607000 || last_edit_user_id -> 6172527 || highway -> crossing || last_edit_version -> 1\n1717556688000000 && 1.2553055,103.8167848 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334481120000 || last_edit_user_id -> 257555 || last_edit_version -> 1\n5138638004000000 && 1.2561024,103.8160087 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707358000 || last_edit_user_id -> 7671613 || last_edit_version -> 11\n2558468523000000 && 1.255212,103.8189822 && last_edit_user_name -> khatulistiwa || last_edit_changeset -> 19195140 || last_edit_time -> 1385821267000 || last_edit_user_id -> 94431 || last_edit_version -> 1\n471491310000000 && 1.255184,103.8163823 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304366000 || last_edit_user_id -> 6346054 || last_edit_version -> 8\n5139421331000000 && 1.2565929,103.8119988 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52685203 || last_edit_time -> 1507300654000 || last_edit_user_id -> 6346054 || last_edit_version -> 3\n6728983057000000 && 1.2565055,103.8183895 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || last_edit_version -> 1\n5209288652000000 && 1.2586232,103.8137176 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || highway -> crossing || last_edit_version -> 1\n5138650785000000 && 1.2552148,103.8161948 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304368000 || last_edit_user_id -> 6346054 || last_edit_version -> 7\n5138672448000000 && 1.2554871,103.816002 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707358000 || last_edit_user_id -> 7671613 || last_edit_version -> 9\n4733403769000000 && 1.2542972,103.8177678 && last_edit_user_name -> pushian || last_edit_changeset -> 46830399 || last_edit_time -> 1489469670000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n5145848424000000 && 1.2557383,103.8188748 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69415695 || last_edit_time -> 1555818629000 || last_edit_user_id -> 741163 || last_edit_version -> 3\n5209288657000000 && 1.2552052,103.8167682 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || last_edit_version -> 1\n5278186712000000 && 1.255276,103.8169656 && last_edit_user_name -> happy-camper || last_edit_changeset -> 54547742 || last_edit_time -> 1513018302000 || last_edit_user_id -> 6346054 || last_edit_version -> 1\n5137340960000000 && 1.2553364,103.8162287 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || last_edit_version -> 2\n4568857133000000 && 1.2558069,103.8175439 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707355000 || last_edit_user_id -> 7671613 || last_edit_version -> 4\n4733403533000000 && 1.254327,103.8176708 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n5138672445000000 && 1.2552404,103.8170098 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304368000 || last_edit_user_id -> 6346054 || last_edit_version -> 8\n5133972841000000 && 1.2555783,103.8165643 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707357000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n5175066692000000 && 1.2560358,103.8159433 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707363000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n5139019659000000 && 1.2563128,103.8159095 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707359000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n4752344968000000 && 1.2561001,103.8159494 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707355000 || last_edit_user_id -> 7671613 || last_edit_version -> 7\n5209288654000000 && 1.2580669,103.8149089 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 2\n1717541875000000 && 1.2553003,103.816949 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304366000 || last_edit_user_id -> 6346054 || name -> Imbiah Lookout || highway -> bus_stop || last_edit_version -> 11\n4731913768000000 && 1.2543716,103.8175247 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n4566570120000000 && 1.2550171,103.8175161 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n4731913779000000 && 1.255064,103.817368 && last_edit_user_name -> pushian || last_edit_changeset -> 52243602 || last_edit_time -> 1505999486000 || last_edit_user_id -> 4989626 || entrance -> yes || last_edit_version -> 2\n1717541891000000 && 1.255115,103.8173869 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334480146000 || last_edit_user_id -> 257555 || last_edit_version -> 1\n5286594356000000 && 1.2552897,103.8171688 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || last_edit_version -> 1\n5138638008000000 && 1.256104,103.8166767 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707358000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 9\n5139015613000000 && 1.2561417,103.8166742 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707359000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n2558083054000000 && 1.2566936,103.8183352 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707352000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n4279963872000000 && 1.2559298,103.8171479 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707354000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n5139019660000000 && 1.2563075,103.8160443 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707359000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n4752207927000000 && 1.2539855,103.8192619 && last_edit_user_name -> Evandering || last_edit_changeset -> 53115984 || last_edit_time -> 1508541322000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n5175384350000000 && 1.2549476,103.8188959 && last_edit_user_name -> CapAhab || last_edit_changeset -> 53052207 || last_edit_time -> 1508359102000 || last_edit_user_id -> 6172866 || last_edit_version -> 1\n5138693319000000 && 1.2581363,103.8153039 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707359000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n4733410887000000 && 1.2552998,103.8170992 && last_edit_user_name -> pushian || last_edit_changeset -> 46831836 || last_edit_time -> 1489476091000 || last_edit_user_id -> 4989626 || last_edit_version -> 2\n1739490136000000 && 1.2547974,103.817368 && last_edit_user_name -> pushian || last_edit_changeset -> 52501454 || last_edit_time -> 1506756672000 || last_edit_user_id -> 4989626 || last_edit_version -> 2\n4752282593000000 && 1.255332,103.8161785 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304367000 || last_edit_user_id -> 6346054 || last_edit_version -> 7\n5286594351000000 && 1.2552784,103.8173867 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372481000 || last_edit_user_id -> 6095772 || last_edit_version -> 1\n5119743804000000 && 1.2555443,103.815627 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707357000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n4752344963000000 && 1.2553069,103.8160742 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507303876000 || last_edit_user_id -> 6346054 || last_edit_version -> 6\n1422900685000000 && 1.2548287,103.8172656 && last_edit_user_name -> twut || last_edit_changeset -> 10877772 || last_edit_time -> 1330945638000 || last_edit_user_id -> 522960 || last_edit_version -> 2\n4736068646000000 && 1.2545648,103.8177049 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n5209288658000000 && 1.2563109,103.8159577 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 2\n5209288650000000 && 1.2579967,103.8124881 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || highway -> crossing || last_edit_version -> 1\n4752344943000000 && 1.2580641,103.8152654 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707355000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n5138672446000000 && 1.2551583,103.8167451 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749609000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n4731913772000000 && 1.2550794,103.8176549 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || last_edit_time -> 1489396888000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n5146232329000000 && 1.2554965,103.8160775 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707362000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 8\n4733403528000000 && 1.2544761,103.8177277 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n4568857131000000 && 1.2558354,103.8165649 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707355000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n5209288660000000 && 1.2560381,103.8166812 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 2\n5119744356000000 && 1.257109,103.8152189 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707357000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n2362722968000000 && 1.2548562,103.8176669 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n2558083058000000 && 1.2567434,103.8184641 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707352000 || last_edit_user_id -> 7671613 || last_edit_version -> 3\n4733074345000000 && 1.2543494,103.8175973 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n1717541894000000 && 1.2551852,103.8171392 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304367000 || last_edit_user_id -> 6346054 || last_edit_version -> 8\n5138693286000000 && 1.2577373,103.8156241 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707358000 || last_edit_user_id -> 7671613 || last_edit_version -> 2\n603563258000000 && 1.25429,103.8170499 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334479744000 || last_edit_user_id -> 257555 || last_edit_version -> 2\n4733403538000000 && 1.2542634,103.8178787 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n1717541881000000 && 1.255232,103.8170032 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304367000 || last_edit_user_id -> 6346054 || last_edit_version -> 8\n4752282589000000 && 1.2552681,103.8162152 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304367000 || last_edit_user_id -> 6346054 || last_edit_version -> 7\n1422900678000000 && 1.2521213,103.8168274 && last_edit_user_name -> twut || last_edit_changeset -> 10877772 || last_edit_time -> 1330945639000 || last_edit_user_id -> 522960 || last_edit_version -> 2\n4731913778000000 && 1.2551596,103.8173956 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || last_edit_time -> 1489396888000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n2362722969000000 && 1.2542172,103.8180295 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n4731770686000000 && 1.255176,103.8167019 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69405864 || last_edit_time -> 1555776545000 || last_edit_user_id -> 741163 || last_edit_version -> 2\n613981020000000 && 1.2578449,103.8122184 && last_edit_user_name -> CapAhab || last_edit_changeset -> 53046295 || last_edit_time -> 1508347696000 || last_edit_user_id -> 6172866 || last_edit_version -> 4\n4736068642000000 && 1.2543692,103.8174987 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060528000 || last_edit_user_id -> 6172527 || last_edit_version -> 2\n5139015612000000 && 1.2560081,103.8166832 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707359000 || last_edit_user_id -> 7671613 || last_edit_version -> 8\n6596068720000000 && 1.2564458,103.8160077 && last_edit_user_name -> div006 || last_edit_changeset -> 72006528 || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || last_edit_version -> 1\n4733410886000000 && 1.255316,103.8170979 && last_edit_user_name -> pushian || last_edit_changeset -> 46830546 || last_edit_time -> 1489470416000 || last_edit_user_id -> 4989626 || last_edit_version -> 1\n5286594350000000 && 1.2552553,103.8174222 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372481000 || last_edit_user_id -> 6095772 || last_edit_version -> 1\n5145931987000000 && 1.2550605,103.8176481 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || last_edit_time -> 1507060526000 || last_edit_user_id -> 6172527 || last_edit_version -> 1\n# Edges\n480175917000001 && 1.2548562,103.8176669:1.2545648,103.8177049 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n-480175917000001 && 1.2545648,103.8177049:1.2548562,103.8176669 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n480175917000002 && 1.2545648,103.8177049:1.2545466,103.8177089 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n-480175917000002 && 1.2545466,103.8177089:1.2545648,103.8177049 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n480632927000001 && 1.2543692,103.8174987:1.2542873,103.8177619:1.2542012,103.8180387:1.2542135,103.8180424:1.2543104,103.8180521:1.2544048,103.8180283:1.2544855,103.817974:1.2545431,103.8178955:1.2545706,103.8178021:1.2545648,103.8177049 && area -> yes || last_edit_user_name -> KingRichardV || last_edit_changeset -> 66408527 || last_edit_time -> 1547760796000 || last_edit_user_id -> 9004577 || highway -> pedestrian || last_edit_version -> 3\n-480632927000001 && 1.2545648,103.8177049:1.2545706,103.8178021:1.2545431,103.8178955:1.2544855,103.817974:1.2544048,103.8180283:1.2543104,103.8180521:1.2542135,103.8180424:1.2542012,103.8180387:1.2542873,103.8177619:1.2543692,103.8174987 && area -> yes || last_edit_user_name -> KingRichardV || last_edit_changeset -> 66408527 || last_edit_time -> 1547760796000 || last_edit_user_id -> 9004577 || highway -> pedestrian || last_edit_version -> 3\n480632927000002 && 1.2545648,103.8177049:1.2545383,103.8176355:1.2544948,103.8175754:1.2544371,103.8175287:1.2543692,103.8174987 && area -> yes || last_edit_user_name -> KingRichardV || last_edit_changeset -> 66408527 || last_edit_time -> 1547760796000 || last_edit_user_id -> 9004577 || highway -> pedestrian || last_edit_version -> 3\n-480632927000002 && 1.2543692,103.8174987:1.2544371,103.8175287:1.2544948,103.8175754:1.2545383,103.8176355:1.2545648,103.8177049 && area -> yes || last_edit_user_name -> KingRichardV || last_edit_changeset -> 66408527 || last_edit_time -> 1547760796000 || last_edit_user_id -> 9004577 || highway -> pedestrian || last_edit_version -> 3\n528898916000000 && 1.2551718,103.8163177:1.2552148,103.8161948 && last_edit_user_name -> rudroju || last_edit_changeset -> 65163560 || psv -> yes || access -> private || last_edit_time -> 1543935529000 || last_edit_user_id -> 7804792 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 4 || foot -> no || oneway -> yes\n528240337000000 && 1.2554976,103.8160891:1.2555488,103.8161391 && last_edit_user_name -> pushian || last_edit_changeset -> 52496567 || last_edit_time -> 1506727734000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n-528240337000000 && 1.2555488,103.8161391:1.2554976,103.8160891 && last_edit_user_name -> pushian || last_edit_changeset -> 52496567 || last_edit_time -> 1506727734000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n482407211000001 && 1.2556997,103.8160146:1.2554965,103.8160775 && last_edit_changeset -> 72006528 || surface -> asphalt || oneway -> yes || last_edit_user_name -> div006 || maxheight -> 4.5 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 17 || foot -> no\n482407211000002 && 1.2554965,103.8160775:1.2554177,103.8161207:1.255332,103.8161785 && last_edit_changeset -> 72006528 || surface -> asphalt || oneway -> yes || last_edit_user_name -> div006 || maxheight -> 4.5 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 17 || foot -> no\n528906080000000 && 1.2551056,103.8185825:1.2550281,103.8186729:1.2549677,103.8187549 && last_edit_user_name -> pushian || last_edit_changeset -> 52497277 || last_edit_time -> 1506732398000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-528906080000000 && 1.2549677,103.8187549:1.2550281,103.8186729:1.2551056,103.8185825 && last_edit_user_name -> pushian || last_edit_changeset -> 52497277 || last_edit_time -> 1506732398000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n730092327000000 && 1.2567434,103.8184641:1.2565883,103.8184816:1.2558972,103.8186614 && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || highway -> service || last_edit_version -> 1\n-730092327000000 && 1.2558972,103.8186614:1.2565883,103.8184816:1.2567434,103.8184641 && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || highway -> service || last_edit_version -> 1\n480175920000001 && 1.2552127,103.816772:1.2552052,103.8167682 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749610000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n-480175920000001 && 1.2552052,103.8167682:1.2552127,103.816772 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749610000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n480175920000002 && 1.2552052,103.8167682:1.2551718,103.8167514 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749610000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n-480175920000002 && 1.2551718,103.8167514:1.2552052,103.8167682 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749610000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n480175920000003 && 1.2551718,103.8167514:1.2551583,103.8167451 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749610000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n-480175920000003 && 1.2551583,103.8167451:1.2551718,103.8167514 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749610000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n526495379000001 && 1.257109,103.8152189:1.2571849,103.8152445:1.2572649,103.8152494:1.2573433,103.815233:1.2574045,103.8152095:1.2574567,103.8151699:1.2574959,103.8151174:1.2575189,103.815056:1.2575493,103.814908:1.257528,103.81475:1.257518,103.814611:1.257577,103.814375:1.25759,103.814216:1.2576283,103.8140861:1.2577044,103.8139742:1.2577764,103.8139282:1.2578377,103.8138905:1.2578863,103.8138374:1.2579182,103.8137728:1.257931,103.813702:1.2578569,103.8134666:1.2577795,103.8133392:1.2576869,103.8132896:1.2575829,103.8132746:1.25748,103.813296:1.257349,103.813479:1.2570988,103.8137332:1.256836,103.813937:1.256651,103.81398:1.256477,103.814122:1.2559355,103.8149113:1.2559119,103.8149495:1.2559024,103.8149935:1.2559081,103.8150381:1.2559236,103.8152187:1.255918,103.8152487:1.255904,103.815276:1.2558828,103.815298:1.2558562,103.815313:1.2558264,103.8153197:1.2555443,103.815627 && last_edit_user_name -> Evandering || last_edit_changeset -> 52610584 || last_edit_time -> 1507071692000 || last_edit_user_id -> 6172527 || name -> Imbiah Trail || highway -> footway || last_edit_version -> 6\n-526495379000001 && 1.2555443,103.815627:1.2558264,103.8153197:1.2558562,103.815313:1.2558828,103.815298:1.255904,103.815276:1.255918,103.8152487:1.2559236,103.8152187:1.2559081,103.8150381:1.2559024,103.8149935:1.2559119,103.8149495:1.2559355,103.8149113:1.256477,103.814122:1.256651,103.81398:1.256836,103.813937:1.2570988,103.8137332:1.257349,103.813479:1.25748,103.813296:1.2575829,103.8132746:1.2576869,103.8132896:1.2577795,103.8133392:1.2578569,103.8134666:1.257931,103.813702:1.2579182,103.8137728:1.2578863,103.8138374:1.2578377,103.8138905:1.2577764,103.8139282:1.2577044,103.8139742:1.2576283,103.8140861:1.25759,103.814216:1.257577,103.814375:1.257518,103.814611:1.257528,103.81475:1.2575493,103.814908:1.2575189,103.815056:1.2574959,103.8151174:1.2574567,103.8151699:1.2574045,103.8152095:1.2573433,103.815233:1.2572649,103.8152494:1.2571849,103.8152445:1.257109,103.8152189 && last_edit_user_name -> Evandering || last_edit_changeset -> 52610584 || last_edit_time -> 1507071692000 || last_edit_user_id -> 6172527 || name -> Imbiah Trail || highway -> footway || last_edit_version -> 6\n526495379000002 && 1.2555443,103.815627:1.2555597,103.815744 && last_edit_user_name -> Evandering || last_edit_changeset -> 52610584 || last_edit_time -> 1507071692000 || last_edit_user_id -> 6172527 || name -> Imbiah Trail || highway -> footway || last_edit_version -> 6\n-526495379000002 && 1.2555597,103.815744:1.2555443,103.815627 && last_edit_user_name -> Evandering || last_edit_changeset -> 52610584 || last_edit_time -> 1507071692000 || last_edit_user_id -> 6172527 || name -> Imbiah Trail || highway -> footway || last_edit_version -> 6\n533417988000000 && 1.2561001,103.8159494:1.2561024,103.8160087 && last_edit_changeset -> 53046295 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> CapAhab || psv -> yes || last_edit_time -> 1508347695000 || last_edit_user_id -> 6172866 || lanes -> 2 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 1 || foot -> no\n260774257000000 && 1.25429,103.8170499:1.2542373,103.8169698:1.2541108,103.8168991:1.2540962,103.8168321:1.2541521,103.8167856:1.2542598,103.8168117:1.2543901,103.8168937:1.2544876,103.816974:1.2545722,103.8170407:1.2546311,103.8170698:1.2547068,103.8169988:1.2547503,103.8168416:1.2547254,103.816778:1.2546819,103.8167156:1.2546806,103.8166255:1.2546301,103.8165553:1.2545483,103.8165437:1.2544863,103.8165667:1.2543994,103.8166001:1.2543239,103.8166277:1.2542436,103.8166373:1.2541914,103.8165958:1.2541752,103.8165262:1.2541992,103.8163935:1.2541619,103.8163022:1.254096,103.8162606:1.2540049,103.8162514:1.2539338,103.8162285:1.2538887,103.8161712:1.2538656,103.816074:1.2538415,103.8160059:1.2537812,103.8159439:1.2536669,103.815895:1.253576,103.8159081:1.2534981,103.8159041:1.253402,103.8158711:1.2532796,103.815885:1.2531882,103.8159215 && last_edit_user_name -> andi9876 || last_edit_changeset -> 20467454 || surface -> asphalt || last_edit_time -> 1391962038000 || last_edit_user_id -> 1256497 || cutting -> no || name -> Luge Dragon Trail || highway -> track || last_edit_version -> 3 || oneway -> yes\n639335710000000 && 1.2560357,103.8160113:1.2558988,103.8160258 && last_edit_changeset -> 64005613 || surface -> asphalt || oneway -> yes || last_edit_user_name -> Ramanyam || maxheight -> 4.5 || psv -> yes || last_edit_time -> 1540893405000 || last_edit_user_id -> 7671600 || lanes -> 2 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 1 || foot -> no\n526499229000000 && 1.257109,103.8152189:1.2569769,103.8153199:1.2568383,103.815426:1.2565773,103.815507:1.2561883,103.81557:1.2558913,103.815556:1.2557193,103.815654:1.2555597,103.815744 && last_edit_user_name -> tommystyle84 || last_edit_changeset -> 71136817 || last_edit_time -> 1560251836000 || last_edit_user_id -> 438632 || highway -> footway || last_edit_version -> 2\n-526499229000000 && 1.2555597,103.815744:1.2557193,103.815654:1.2558913,103.815556:1.2561883,103.81557:1.2565773,103.815507:1.2568383,103.815426:1.2569769,103.8153199:1.257109,103.8152189 && last_edit_user_name -> tommystyle84 || last_edit_changeset -> 71136817 || last_edit_time -> 1560251836000 || last_edit_user_id -> 438632 || highway -> footway || last_edit_version -> 2\n480175915000001 && 1.2548562,103.8176669:1.2548422,103.8176272:1.2548436,103.817585:1.2548605,103.8175463:1.2548903,103.8175165:1.2549289,103.8174996:1.2549711,103.8174982:1.2550171,103.8175161 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 2\n-480175915000001 && 1.2550171,103.8175161:1.2549711,103.8174982:1.2549289,103.8174996:1.2548903,103.8175165:1.2548605,103.8175463:1.2548436,103.817585:1.2548422,103.8176272:1.2548562,103.8176669 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 2\n480175915000002 && 1.2550171,103.8175161:1.2550511,103.817552:1.2550665,103.817599:1.2550605,103.8176481 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 2\n-480175915000002 && 1.2550605,103.8176481:1.2550665,103.817599:1.2550511,103.817552:1.2550171,103.8175161 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 2\n480175915000003 && 1.2550605,103.8176481:1.2550341,103.8176899:1.2550002,103.8177133:1.2549602,103.817723:1.2549193,103.8177177:1.2548831,103.8176982:1.2548562,103.8176669 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 2\n-480175915000003 && 1.2548562,103.8176669:1.2548831,103.8176982:1.2549193,103.8177177:1.2549602,103.817723:1.2550002,103.8177133:1.2550341,103.8176899:1.2550605,103.8176481 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 2\n528929961000000 && 1.2557114,103.8172062:1.2557291,103.8173139:1.2556477,103.8175322 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058170000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 2\n-528929961000000 && 1.2556477,103.8175322:1.2557291,103.8173139:1.2557114,103.8172062 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058170000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 2\n480339638000000 && 1.2552998,103.8170992:1.255316,103.8170979 && last_edit_user_name -> pushian || last_edit_changeset -> 46830546 || shelter -> yes || last_edit_time -> 1489470416000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480339638000000 && 1.255316,103.8170979:1.2552998,103.8170992 && last_edit_user_name -> pushian || last_edit_changeset -> 46830546 || shelter -> yes || last_edit_time -> 1489470416000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n533417989000000 && 1.256038,103.8164078:1.2560356,103.8161935 && last_edit_changeset -> 66635979 || surface -> asphalt || oneway -> yes || turn:lanes -> left;right || last_edit_user_name -> sravan7 || psv -> yes || last_edit_time -> 1548431890000 || last_edit_user_id -> 8911008 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 4 || foot -> no\n528934982000001 && 1.2552784,103.8173867:1.255197,103.8173761:1.2552897,103.8171688 && area -> yes || last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> pedestrian || last_edit_version -> 2\n-528934982000001 && 1.2552897,103.8171688:1.255197,103.8173761:1.2552784,103.8173867 && area -> yes || last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> pedestrian || last_edit_version -> 2\n528934982000002 && 1.2552897,103.8171688:1.2552975,103.8171514:1.2553713,103.8171498:1.2553556,103.8172299:1.2554409,103.8172409:1.2553875,103.8174012:1.2552784,103.8173867 && area -> yes || last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> pedestrian || last_edit_version -> 2\n-528934982000002 && 1.2552784,103.8173867:1.2553875,103.8174012:1.2554409,103.8172409:1.2553556,103.8172299:1.2553713,103.8171498:1.2552975,103.8171514:1.2552897,103.8171688 && area -> yes || last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> pedestrian || last_edit_version -> 2\n716057445000000 && 1.2565055,103.8183895:1.2566936,103.8183352 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || turn:lanes:forward -> left || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || turn:lanes:backward -> through || highway -> service || last_edit_version -> 1\n-716057445000000 && 1.2566936,103.8183352:1.2565055,103.8183895 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || turn:lanes:forward -> left || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || turn:lanes:backward -> through || highway -> service || last_edit_version -> 1\n128869879000000 && 1.2521213,103.8168274:1.2548287,103.8172656 && last_edit_user_name -> sgmapperhh || last_edit_changeset -> 75596691 || last_edit_time -> 1570875303000 || last_edit_user_id -> 10373200 || aerialway -> chair_lift || name -> SkyRide || highway -> raceway || last_edit_version -> 3 || layer -> 2 || oneway -> no\n-128869879000000 && 1.2548287,103.8172656:1.2521213,103.8168274 && last_edit_user_name -> sgmapperhh || last_edit_changeset -> 75596691 || last_edit_time -> 1570875303000 || last_edit_user_id -> 10373200 || aerialway -> chair_lift || name -> SkyRide || highway -> raceway || last_edit_version -> 3 || layer -> 2 || oneway -> no\n528906082000001 && 1.2553364,103.8162287:1.2553921,103.8161783:1.2554976,103.8160891 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n-528906082000001 && 1.2554976,103.8160891:1.2553921,103.8161783:1.2553364,103.8162287 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n528906082000002 && 1.2554976,103.8160891:1.2557117,103.8160541:1.2558,103.8160758:1.2558767,103.8161247:1.2559336,103.8161957:1.2560102,103.8164384:1.2560081,103.8166832 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n-528906082000002 && 1.2560081,103.8166832:1.2560102,103.8164384:1.2559336,103.8161957:1.2558767,103.8161247:1.2558,103.8160758:1.2557117,103.8160541:1.2554976,103.8160891 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n528906082000003 && 1.2560081,103.8166832:1.2560081,103.8167623:1.256025,103.8168277:1.2561164,103.8169633:1.2562553,103.8171746:1.2562869,103.8172734:1.2562827,103.8173771:1.2562433,103.8174731:1.2560003,103.8179546:1.2558878,103.818134:1.2558222,103.8182134:1.2557207,103.8183124:1.2556211,103.8183751:1.2552244,103.8185142:1.2551056,103.8185825 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n-528906082000003 && 1.2551056,103.8185825:1.2552244,103.8185142:1.2556211,103.8183751:1.2557207,103.8183124:1.2558222,103.8182134:1.2558878,103.818134:1.2560003,103.8179546:1.2562433,103.8174731:1.2562827,103.8173771:1.2562869,103.8172734:1.2562553,103.8171746:1.2561164,103.8169633:1.256025,103.8168277:1.2560081,103.8167623:1.2560081,103.8166832 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n650732280000000 && 1.2558988,103.8160258:1.2558092,103.8160176:1.2556997,103.8160146 && last_edit_user_name -> daram2 || last_edit_changeset -> 67086910 || psv -> yes || surface -> asphalt || last_edit_time -> 1549854189000 || last_edit_user_id -> 8967880 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 2 || foot -> no || oneway -> yes\n461380737000001 && 1.2558069,103.8175439:1.2557265,103.8175493:1.2556477,103.8175322 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || shelter -> yes || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 7\n-461380737000001 && 1.2556477,103.8175322:1.2557265,103.8175493:1.2558069,103.8175439 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || shelter -> yes || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 7\n461380737000002 && 1.2556477,103.8175322:1.2552553,103.8174222 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || shelter -> yes || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 7\n-461380737000002 && 1.2552553,103.8174222:1.2556477,103.8175322 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || shelter -> yes || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 7\n461380737000003 && 1.2552553,103.8174222:1.2551596,103.8173956 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || shelter -> yes || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 7\n-461380737000003 && 1.2551596,103.8173956:1.2552553,103.8174222 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || shelter -> yes || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 7\n461380737000004 && 1.2551596,103.8173956:1.255115,103.8173869 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || shelter -> yes || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 7\n-461380737000004 && 1.255115,103.8173869:1.2551596,103.8173956 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || shelter -> yes || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 7\n538105767000000 && 1.2553055,103.8167848:1.2552127,103.816772 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 1\n-538105767000000 && 1.2552127,103.816772:1.2553055,103.8167848 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 1\n526481397000000 && 1.255064,103.817368:1.2550134,103.8173354:1.2549636,103.8173111:1.2549087,103.8172896:1.2548287,103.8172656 && last_edit_user_name -> pushian || last_edit_changeset -> 52243602 || last_edit_time -> 1505999485000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-526481397000000 && 1.2548287,103.8172656:1.2549087,103.8172896:1.2549636,103.8173111:1.2550134,103.8173354:1.255064,103.817368 && last_edit_user_name -> pushian || last_edit_changeset -> 52243602 || last_edit_time -> 1505999485000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n480175918000001 && 1.2543716,103.8175247:1.2544315,103.8175525:1.2544827,103.8175944:1.2545219,103.8176476:1.2545466,103.8177089 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-480175918000001 && 1.2545466,103.8177089:1.2545219,103.8176476:1.2544827,103.8175944:1.2544315,103.8175525:1.2543716,103.8175247 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n480175918000002 && 1.2545466,103.8177089:1.2545542,103.8178011:1.2545295,103.8178903:1.2544755,103.8179654:1.2543989,103.8180173:1.2543091,103.8180396:1.2542172,103.8180295 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-480175918000002 && 1.2542172,103.8180295:1.2543091,103.8180396:1.2543989,103.8180173:1.2544755,103.8179654:1.2545295,103.8178903:1.2545542,103.8178011:1.2545466,103.8177089 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n480338714000001 && 1.2543494,103.8175973:1.2544295,103.8176461:1.2544761,103.8177277 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480338714000001 && 1.2544761,103.8177277:1.2544295,103.8176461:1.2543494,103.8175973 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n480338714000002 && 1.2544761,103.8177277:1.254476,103.8178269:1.2544255,103.8179122:1.2543386,103.81796:1.2542395,103.8179569 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480338714000002 && 1.2542395,103.8179569:1.2543386,103.81796:1.2544255,103.8179122:1.254476,103.8178269:1.2544761,103.8177277 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n260777732000000 && 1.2560356,103.8161935:1.256002,103.8161264:1.2559571,103.8160674:1.2558988,103.8160258 && last_edit_user_name -> naik4 || last_edit_changeset -> 65020111 || psv -> yes || surface -> asphalt || last_edit_time -> 1543515811000 || last_edit_user_id -> 7671613 || lanes -> 1 || noname -> yes || highway -> unclassified || last_edit_version -> 8 || foot -> no || oneway -> yes\n428880136000001 && 1.2559298,103.8171479:1.2558354,103.8165649 && horse -> no || last_edit_user_name -> MyWayOrNoHighway || last_edit_changeset -> 45230182 || bicycle -> no || surface -> metal || last_edit_time -> 1484608695000 || last_edit_user_id -> 4351550 || bridge -> yes || highway -> path || last_edit_version -> 3 || layer -> 1\n-428880136000001 && 1.2558354,103.8165649:1.2559298,103.8171479 && horse -> no || last_edit_user_name -> MyWayOrNoHighway || last_edit_changeset -> 45230182 || bicycle -> no || surface -> metal || last_edit_time -> 1484608695000 || last_edit_user_id -> 4351550 || bridge -> yes || highway -> path || last_edit_version -> 3 || layer -> 1\n428880136000002 && 1.2558354,103.8165649:1.2558247,103.8165046:1.2556903,103.8161419:1.2555597,103.815744 && horse -> no || last_edit_user_name -> MyWayOrNoHighway || last_edit_changeset -> 45230182 || bicycle -> no || surface -> metal || last_edit_time -> 1484608695000 || last_edit_user_id -> 4351550 || bridge -> yes || highway -> path || last_edit_version -> 3 || layer -> 1\n-428880136000002 && 1.2555597,103.815744:1.2556903,103.8161419:1.2558247,103.8165046:1.2558354,103.8165649 && horse -> no || last_edit_user_name -> MyWayOrNoHighway || last_edit_changeset -> 45230182 || bicycle -> no || surface -> metal || last_edit_time -> 1484608695000 || last_edit_user_id -> 4351550 || bridge -> yes || highway -> path || last_edit_version -> 3 || layer -> 1\n546165469000000 && 1.2553003,103.816949:1.255276,103.8169656 && last_edit_user_name -> happy-camper || last_edit_changeset -> 54547742 || last_edit_time -> 1513018302000 || last_edit_user_id -> 6346054 || covered -> yes || highway -> footway || last_edit_version -> 1\n-546165469000000 && 1.255276,103.8169656:1.2553003,103.816949 && last_edit_user_name -> happy-camper || last_edit_changeset -> 54547742 || last_edit_time -> 1513018302000 || last_edit_user_id -> 6346054 || covered -> yes || highway -> footway || last_edit_version -> 1\n528935623000001 && 1.2563128,103.8159095:1.2563109,103.8159577 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-528935623000001 && 1.2563109,103.8159577:1.2563128,103.8159095 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n528935623000002 && 1.2563109,103.8159577:1.2563089,103.8160091 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-528935623000002 && 1.2563089,103.8160091:1.2563109,103.8159577 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n528935623000003 && 1.2563089,103.8160091:1.2563075,103.8160443 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-528935623000003 && 1.2563075,103.8160443:1.2563089,103.8160091 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n528240340000001 && 1.2552148,103.8161948:1.2552008,103.8161773:1.2551925,103.8161565:1.2551907,103.8161341:1.2551953,103.8161123:1.2552062,103.8160927:1.2552222,103.8160771:1.2552421,103.8160668:1.2552641,103.8160628:1.2552864,103.8160653:1.2553069,103.8160742 && junction -> roundabout || last_edit_user_name -> harishpuppala || last_edit_changeset -> 66895408 || psv -> yes || surface -> asphalt || last_edit_time -> 1549266975000 || last_edit_user_id -> 7446646 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 11 || foot -> no\n528240340000002 && 1.2553069,103.8160742:1.2553223,103.8160867:1.2553338,103.8161027:1.2553409,103.8161212:1.255343,103.8161408:1.25534,103.8161604:1.255332,103.8161785 && junction -> roundabout || last_edit_user_name -> harishpuppala || last_edit_changeset -> 66895408 || psv -> yes || surface -> asphalt || last_edit_time -> 1549266975000 || last_edit_user_id -> 7446646 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 11 || foot -> no\n528240340000003 && 1.255332,103.8161785:1.2553153,103.8161978:1.2552932,103.8162105:1.2552681,103.8162152 && junction -> roundabout || last_edit_user_name -> harishpuppala || last_edit_changeset -> 66895408 || psv -> yes || surface -> asphalt || last_edit_time -> 1549266975000 || last_edit_user_id -> 7446646 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 11 || foot -> no\n528240340000004 && 1.2552681,103.8162152:1.2552487,103.8162131:1.2552306,103.8162061:1.2552148,103.8161948 && junction -> roundabout || last_edit_user_name -> harishpuppala || last_edit_changeset -> 66895408 || psv -> yes || surface -> asphalt || last_edit_time -> 1549266975000 || last_edit_user_id -> 7446646 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 11 || foot -> no\n547131784000001 && 1.255278,103.8171476:1.2552897,103.8171688 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 1\n-547131784000001 && 1.2552897,103.8171688:1.255278,103.8171476 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 1\n547131784000002 && 1.2552897,103.8171688:1.2553015,103.8172019:1.2553088,103.8172509:1.2553055,103.8173052:1.2552954,103.8173488:1.2552784,103.8173867 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 1\n-547131784000002 && 1.2552784,103.8173867:1.2552954,103.8173488:1.2553055,103.8173052:1.2553088,103.8172509:1.2553015,103.8172019:1.2552897,103.8171688 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 1\n547131784000003 && 1.2552784,103.8173867:1.2552553,103.8174222 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 1\n-547131784000003 && 1.2552553,103.8174222:1.2552784,103.8173867 && last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> footway || last_edit_version -> 1\n526495380000000 && 1.2555597,103.815744:1.2554913,103.8158824:1.2554871,103.816002 && last_edit_user_name -> Evandering || last_edit_changeset -> 52610966 || surface -> asphalt || last_edit_time -> 1507074076000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-526495380000000 && 1.2554871,103.816002:1.2554913,103.8158824:1.2555597,103.815744 && last_edit_user_name -> Evandering || last_edit_changeset -> 52610966 || surface -> asphalt || last_edit_time -> 1507074076000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n538105768000001 && 1.2561024,103.8160087:1.256104,103.8166767 && last_edit_changeset -> 64183540 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> Neela1 || psv -> yes || last_edit_time -> 1541386415000 || last_edit_user_id -> 7795604 || lanes -> 2 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 2 || foot -> no\n538105768000002 && 1.256104,103.8166767:1.2561072,103.8167292:1.256126,103.8167828:1.2562006,103.8168992:1.2563315,103.8171142:1.2563714,103.8172177:1.2563864,103.8173204:1.2563581,103.8174425:1.256075,103.8179538:1.2559849,103.8181156:1.2559018,103.8182311:1.2558086,103.8183251:1.2556682,103.8184334:1.2552383,103.8185969:1.2551858,103.8186349:1.2550941,103.8187449:1.2550414,103.8188556 && last_edit_changeset -> 64183540 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> Neela1 || psv -> yes || last_edit_time -> 1541386415000 || last_edit_user_id -> 7795604 || lanes -> 2 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 2 || foot -> no\n702400958000001 && 1.2564458,103.8160077:1.2563089,103.8160091 && last_edit_changeset -> 72006528 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || turn:lanes -> left;through || last_edit_user_name -> div006 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 1 || foot -> no\n702400958000002 && 1.2563089,103.8160091:1.2561024,103.8160087 && last_edit_changeset -> 72006528 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || turn:lanes -> left;through || last_edit_user_name -> div006 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 1 || foot -> no\n530020436000001 && 1.2551525,103.8170011:1.2551718,103.816993:1.2551926,103.8169906:1.2552132,103.8169941:1.255232,103.8170032 && junction -> roundabout || last_edit_user_name -> udaykiran3 || last_edit_changeset -> 73290653 || psv -> yes || access -> private || surface -> asphalt || last_edit_time -> 1565674130000 || last_edit_user_id -> 7671560 || lanes -> 1 || highway -> unclassified || last_edit_version -> 9 || foot -> no\n530020436000002 && 1.255232,103.8170032:1.2552404,103.8170098 && junction -> roundabout || last_edit_user_name -> udaykiran3 || last_edit_changeset -> 73290653 || psv -> yes || access -> private || surface -> asphalt || last_edit_time -> 1565674130000 || last_edit_user_id -> 7671560 || lanes -> 1 || highway -> unclassified || last_edit_version -> 9 || foot -> no\n530020436000003 && 1.2552404,103.8170098:1.2552549,103.8170276:1.2552632,103.8170491:1.2552646,103.817072:1.255259,103.8170942:1.2552468,103.8171137:1.2552293,103.8171286:1.2552081,103.8171373:1.2551852,103.8171392 && junction -> roundabout || last_edit_user_name -> udaykiran3 || last_edit_changeset -> 73290653 || psv -> yes || access -> private || surface -> asphalt || last_edit_time -> 1565674130000 || last_edit_user_id -> 7671560 || lanes -> 1 || highway -> unclassified || last_edit_version -> 9 || foot -> no\n530020436000004 && 1.2551852,103.8171392:1.2551593,103.8171325:1.2551374,103.817117:1.2551225,103.8170949:1.2551163,103.8170688:1.2551198,103.8170423:1.2551324,103.8170187:1.2551525,103.8170011 && junction -> roundabout || last_edit_user_name -> udaykiran3 || last_edit_changeset -> 73290653 || psv -> yes || access -> private || surface -> asphalt || last_edit_time -> 1565674130000 || last_edit_user_id -> 7671560 || lanes -> 1 || highway -> unclassified || last_edit_version -> 9 || foot -> no\n528901320000001 && 1.255276,103.8169656:1.2552652,103.8169729 && last_edit_user_name -> happy-camper || last_edit_changeset -> 54547742 || last_edit_time -> 1513018304000 || last_edit_user_id -> 6346054 || highway -> footway || last_edit_version -> 2\n-528901320000001 && 1.2552652,103.8169729:1.255276,103.8169656 && last_edit_user_name -> happy-camper || last_edit_changeset -> 54547742 || last_edit_time -> 1513018304000 || last_edit_user_id -> 6346054 || highway -> footway || last_edit_version -> 2\n528901320000002 && 1.2552652,103.8169729:1.2552404,103.8170098 && last_edit_user_name -> happy-camper || last_edit_changeset -> 54547742 || last_edit_time -> 1513018304000 || last_edit_user_id -> 6346054 || highway -> footway || last_edit_version -> 2\n-528901320000002 && 1.2552404,103.8170098:1.2552652,103.8169729 && last_edit_user_name -> happy-camper || last_edit_changeset -> 54547742 || last_edit_time -> 1513018304000 || last_edit_user_id -> 6346054 || highway -> footway || last_edit_version -> 2\n639335779000000 && 1.2560357,103.8160113:1.2560358,103.8159433 && last_edit_changeset -> 65016111 || surface -> asphalt || noname -> yes || oneway -> yes || last_edit_user_name -> sai_sri || psv -> yes || last_edit_time -> 1543508722000 || last_edit_user_id -> 8911951 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 2 || foot -> no\n478164185000001 && 1.2549476,103.8188959:1.2550546,103.8187039:1.255092,103.818644:1.2551506,103.8185862:1.2552384,103.8185377:1.255653,103.818386:1.2557393,103.818332:1.2558563,103.8182142:1.2559241,103.8181233:1.2560288,103.8179454:1.2563107,103.817427:1.2563238,103.8173145:1.2563115,103.8172383:1.256273,103.8171451:1.2561336,103.8169394:1.256075,103.8168593:1.2560489,103.8168185:1.2560381,103.8167676:1.2560381,103.8166812 && last_edit_changeset -> 64183540 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> Neela1 || psv -> yes || last_edit_time -> 1541386415000 || last_edit_user_id -> 7795604 || lanes -> 2 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 13 || foot -> no\n478164185000002 && 1.2560381,103.8166812:1.256038,103.8164078 && last_edit_changeset -> 64183540 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> Neela1 || psv -> yes || last_edit_time -> 1541386415000 || last_edit_user_id -> 7795604 || lanes -> 2 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 13 || foot -> no\n482416616000001 && 1.2561001,103.8159494:1.2563109,103.8159577 && last_edit_changeset -> 72006528 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> div006 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 6 || foot -> no\n482416616000002 && 1.2563109,103.8159577:1.2566241,103.8159701:1.2567884,103.8159634:1.256968,103.815956:1.2572858,103.8158836:1.2574293,103.8158226:1.2577725,103.8156348:1.257855,103.8155832:1.2579602,103.8154578:1.2580641,103.8152654 && last_edit_changeset -> 72006528 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> div006 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 6 || foot -> no\n639335711000000 && 1.2560358,103.8159433:1.2561001,103.8159494 && last_edit_changeset -> 72006528 || surface -> asphalt || oneway -> yes || last_edit_user_name -> div006 || maxheight -> 4.5 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 2 || foot -> no\n480175926000000 && 1.255064,103.817368:1.2550724,103.8173982 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || last_edit_time -> 1489396889000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480175926000000 && 1.2550724,103.8173982:1.255064,103.817368 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || last_edit_time -> 1489396889000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n162005451000000 && 1.2547974,103.817368:1.2548287,103.8172656 && last_edit_user_name -> cboothroyd || last_edit_changeset -> 11491444 || last_edit_time -> 1336070510000 || last_edit_user_id -> 255802 || highway -> path || last_edit_version -> 1\n-162005451000000 && 1.2548287,103.8172656:1.2547974,103.817368 && last_edit_user_name -> cboothroyd || last_edit_changeset -> 11491444 || last_edit_time -> 1336070510000 || last_edit_user_id -> 255802 || highway -> path || last_edit_version -> 1\n480338771000000 && 1.2544035,103.8177439:1.2542972,103.8177678 && last_edit_user_name -> pushian || last_edit_changeset -> 46830399 || last_edit_time -> 1489469670000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480338771000000 && 1.2542972,103.8177678:1.2544035,103.8177439 && last_edit_user_name -> pushian || last_edit_changeset -> 46830399 || last_edit_time -> 1489469670000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n480183445000001 && 1.2543716,103.8175247:1.2543692,103.8174987 && last_edit_user_name -> pushian || last_edit_changeset -> 46868015 || last_edit_time -> 1489581874000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n-480183445000001 && 1.2543692,103.8174987:1.2543716,103.8175247 && last_edit_user_name -> pushian || last_edit_changeset -> 46868015 || last_edit_time -> 1489581874000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n480183445000002 && 1.2543692,103.8174987:1.2543526,103.8174381:1.2537653,103.8172369:1.25365,103.8173683:1.2535375,103.8173137 && last_edit_user_name -> pushian || last_edit_changeset -> 46868015 || last_edit_time -> 1489581874000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n-480183445000002 && 1.2535375,103.8173137:1.25365,103.8173683:1.2537653,103.8172369:1.2543526,103.8174381:1.2543692,103.8174987 && last_edit_user_name -> pushian || last_edit_changeset -> 46868015 || last_edit_time -> 1489581874000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n529787039000000 && 1.2550794,103.8176549:1.2550605,103.8176481 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060527000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 1\n-529787039000000 && 1.2550605,103.8176481:1.2550794,103.8176549 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060527000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 1\n480338716000001 && 1.2545466,103.8177089:1.2544761,103.8177277 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480338716000001 && 1.2544761,103.8177277:1.2545466,103.8177089 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n480338716000002 && 1.2544761,103.8177277:1.2544035,103.8177439 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480338716000002 && 1.2544035,103.8177439:1.2544761,103.8177277 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n480175924000000 && 1.2550877,103.8174563:1.2551596,103.8173956 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || shelter -> yes || last_edit_time -> 1489396889000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480175924000000 && 1.2551596,103.8173956:1.2550877,103.8174563 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || shelter -> yes || last_edit_time -> 1489396889000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n528898917000001 && 1.2551525,103.8170011:1.2551719,103.8169196:1.2551763,103.8168342:1.2551718,103.8167514 && last_edit_changeset -> 65163560 || access -> private || surface -> asphalt || oneway -> yes || last_edit_user_name -> rudroju || psv -> yes || last_edit_time -> 1543935529000 || last_edit_user_id -> 7804792 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 10 || foot -> no\n528898917000002 && 1.2551718,103.8167514:1.255171,103.8167364:1.2551466,103.8165992:1.2551218,103.8164965:1.2551198,103.8164596:1.2551273,103.8164194:1.2551718,103.8163177 && last_edit_changeset -> 65163560 || access -> private || surface -> asphalt || oneway -> yes || last_edit_user_name -> rudroju || psv -> yes || last_edit_time -> 1543935529000 || last_edit_user_id -> 7804792 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 10 || foot -> no\n528904857000000 && 1.2577373,103.8156241:1.2573982,103.8158064:1.2572742,103.8158574:1.2569612,103.8159354:1.2567292,103.8159524:1.2565672,103.8159474 && last_edit_user_name -> pushian || last_edit_changeset -> 52497070 || last_edit_time -> 1506731022000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-528904857000000 && 1.2565672,103.8159474:1.2567292,103.8159524:1.2569612,103.8159354:1.2572742,103.8158574:1.2573982,103.8158064:1.2577373,103.8156241 && last_edit_user_name -> pushian || last_edit_changeset -> 52497070 || last_edit_time -> 1506731022000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n528900905000001 && 1.2551852,103.8171392:1.2551768,103.8172073:1.2551546,103.8172737:1.255115,103.8173869 && last_edit_changeset -> 73567273 || surface -> asphalt || turn:lanes:forward -> through || lanes:forward -> 1 || lanes:backward -> 1 || last_edit_user_name -> pk_ravi || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || lanes -> 2 || name -> Imbiah Road || turn:lanes:backward -> left || highway -> service || last_edit_version -> 3 || foot -> no\n-528900905000001 && 1.255115,103.8173869:1.2551546,103.8172737:1.2551768,103.8172073:1.2551852,103.8171392 && last_edit_changeset -> 73567273 || surface -> asphalt || turn:lanes:forward -> through || lanes:forward -> 1 || lanes:backward -> 1 || last_edit_user_name -> pk_ravi || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || lanes -> 2 || name -> Imbiah Road || turn:lanes:backward -> left || highway -> service || last_edit_version -> 3 || foot -> no\n528900905000002 && 1.255115,103.8173869:1.2550724,103.8173982 && last_edit_changeset -> 73567273 || surface -> asphalt || turn:lanes:forward -> through || lanes:forward -> 1 || lanes:backward -> 1 || last_edit_user_name -> pk_ravi || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || lanes -> 2 || name -> Imbiah Road || turn:lanes:backward -> left || highway -> service || last_edit_version -> 3 || foot -> no\n-528900905000002 && 1.2550724,103.8173982:1.255115,103.8173869 && last_edit_changeset -> 73567273 || surface -> asphalt || turn:lanes:forward -> through || lanes:forward -> 1 || lanes:backward -> 1 || last_edit_user_name -> pk_ravi || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || lanes -> 2 || name -> Imbiah Road || turn:lanes:backward -> left || highway -> service || last_edit_version -> 3 || foot -> no\n528902956000000 && 1.2552509,103.8187571:1.2553881,103.818717:1.2557658,103.8186065:1.2560674,103.8185183:1.2563658,103.8184311:1.2565055,103.8183895 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || highway -> service || last_edit_version -> 9\n-528902956000000 && 1.2565055,103.8183895:1.2563658,103.8184311:1.2560674,103.8185183:1.2557658,103.8186065:1.2553881,103.818717:1.2552509,103.8187571 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || highway -> service || last_edit_version -> 9\n528240338000001 && 1.2555783,103.8165643:1.2555951,103.8166206 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058169000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n-528240338000001 && 1.2555951,103.8166206:1.2555783,103.8165643 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058169000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n528240338000002 && 1.2555951,103.8166206:1.2555981,103.816645:1.2556071,103.816852:1.2556842,103.8171013:1.2557114,103.8172062 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058169000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n-528240338000002 && 1.2557114,103.8172062:1.2556842,103.8171013:1.2556071,103.816852:1.2555981,103.816645:1.2555951,103.8166206 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058169000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n528240338000003 && 1.2557114,103.8172062:1.2557524,103.817218 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058169000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n-528240338000003 && 1.2557524,103.817218:1.2557114,103.8172062 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058169000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n461110977000001 && 1.2550171,103.8175161:1.2550877,103.8174563 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || shelter -> yes || last_edit_time -> 1489396889000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n-461110977000001 && 1.2550877,103.8174563:1.2550171,103.8175161 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || shelter -> yes || last_edit_time -> 1489396889000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n461110977000002 && 1.2550877,103.8174563:1.2550724,103.8173982 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || shelter -> yes || last_edit_time -> 1489396889000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n-461110977000002 && 1.2550724,103.8173982:1.2550877,103.8174563 && last_edit_user_name -> pushian || last_edit_changeset -> 46806385 || shelter -> yes || last_edit_time -> 1489396889000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n482407210000000 && 1.2552681,103.8162152:1.255184,103.8163823 && last_edit_user_name -> anu_smita || last_edit_changeset -> 67122864 || psv -> yes || access -> private || last_edit_time -> 1549957185000 || last_edit_user_id -> 8967885 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 7 || foot -> no || oneway -> yes\n128869880000001 && 1.2547974,103.817368:1.2546697,103.8173603:1.2545321,103.8173245:1.2543927,103.8171623:1.25429,103.8170499 && last_edit_user_name -> pushian || last_edit_changeset -> 52501454 || surface -> asphalt || last_edit_time -> 1506756672000 || last_edit_user_id -> 4989626 || cutting -> no || name -> Luge Trail || highway -> track || last_edit_version -> 5 || oneway -> yes\n128869880000002 && 1.25429,103.8170499:1.2542217,103.8170064 && last_edit_user_name -> pushian || last_edit_changeset -> 52501454 || surface -> asphalt || last_edit_time -> 1506756672000 || last_edit_user_id -> 4989626 || cutting -> no || name -> Luge Trail || highway -> track || last_edit_version -> 5 || oneway -> yes\n528246258000000 && 1.2539855,103.8192619:1.2541076,103.8192021:1.2543857,103.8190694:1.254434,103.8190868:1.2545768,103.8190547:1.2546206,103.8190266:1.2547068,103.8189715:1.2548362,103.8188213:1.2549616,103.8186758:1.2550313,103.8186302:1.2551056,103.8185825 && last_edit_user_name -> Evandering || last_edit_changeset -> 53115984 || last_edit_time -> 1508541322000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n-528246258000000 && 1.2551056,103.8185825:1.2550313,103.8186302:1.2549616,103.8186758:1.2548362,103.8188213:1.2547068,103.8189715:1.2546206,103.8190266:1.2545768,103.8190547:1.254434,103.8190868:1.2543857,103.8190694:1.2541076,103.8192021:1.2539855,103.8192619 && last_edit_user_name -> Evandering || last_edit_changeset -> 53115984 || last_edit_time -> 1508541322000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 3\n646425420000000 && 1.2550724,103.8173982:1.2549776,103.8174195:1.2547564,103.8174721:1.2545781,103.8174628:1.2544446,103.8173782:1.2542019,103.8171228:1.2540528,103.8170529:1.253656,103.8168978:1.2536168,103.8168848 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || surface -> asphalt || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || lanes -> 2 || lanes:forward -> 1 || name -> Imbiah Road || lanes:backward -> 1 || highway -> service || last_edit_version -> 2\n-646425420000000 && 1.2536168,103.8168848:1.253656,103.8168978:1.2540528,103.8170529:1.2542019,103.8171228:1.2544446,103.8173782:1.2545781,103.8174628:1.2547564,103.8174721:1.2549776,103.8174195:1.2550724,103.8173982 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || surface -> asphalt || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || lanes -> 2 || lanes:forward -> 1 || name -> Imbiah Road || lanes:backward -> 1 || highway -> service || last_edit_version -> 2\n528901322000001 && 1.2554871,103.816002:1.2554904,103.8160242 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749612000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n-528901322000001 && 1.2554904,103.8160242:1.2554871,103.816002 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749612000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n528901322000002 && 1.2554904,103.8160242:1.2554965,103.8160775 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749612000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n-528901322000002 && 1.2554965,103.8160775:1.2554904,103.8160242 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749612000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n528901322000003 && 1.2554965,103.8160775:1.2554976,103.8160891 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749612000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n-528901322000003 && 1.2554976,103.8160891:1.2554965,103.8160775 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749612000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 3\n249144132000000 && 1.2558972,103.8186614:1.2556102,103.8187361:1.2554232,103.8187864:1.255335,103.8188441:1.2552442,103.8189425:1.255212,103.8189822 && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || highway -> service || last_edit_version -> 6\n-249144132000000 && 1.255212,103.8189822:1.2552442,103.8189425:1.255335,103.8188441:1.2554232,103.8187864:1.2556102,103.8187361:1.2558972,103.8186614 && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || highway -> service || last_edit_version -> 6\n480338715000001 && 1.254327,103.8176708:1.2543748,103.8176974:1.2544035,103.8177439 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480338715000001 && 1.2544035,103.8177439:1.2543748,103.8176974:1.254327,103.8176708 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n480338715000002 && 1.2544035,103.8177439:1.2544049,103.8178033:1.254375,103.8178545:1.2543226,103.8178824:1.2542634,103.8178787 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n-480338715000002 && 1.2542634,103.8178787:1.2543226,103.8178824:1.254375,103.8178545:1.2544049,103.8178033:1.2544035,103.8177439 && last_edit_user_name -> pushian || last_edit_changeset -> 46830384 || last_edit_time -> 1489469612000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 1\n482416618000001 && 1.2553069,103.8160742:1.2553621,103.8160732:1.2554045,103.8160631:1.2554904,103.8160242 && last_edit_changeset -> 72006528 || surface -> asphalt || oneway -> yes || turn:lanes -> through;right || last_edit_user_name -> div006 || maxheight -> 4.5 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 13 || foot -> no\n482416618000002 && 1.2554904,103.8160242:1.2556619,103.815964:1.2557628,103.8159365:1.2558724,103.8159277:1.2560358,103.8159433 && last_edit_changeset -> 72006528 || surface -> asphalt || oneway -> yes || turn:lanes -> through;right || last_edit_user_name -> div006 || maxheight -> 4.5 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 13 || foot -> no\n533417990000000 && 1.2560356,103.8161935:1.2560357,103.8160113 && last_edit_user_name -> naik4 || last_edit_changeset -> 65010043 || psv -> yes || surface -> asphalt || last_edit_time -> 1543497734000 || last_edit_user_id -> 7671613 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 4 || foot -> no || oneway -> yes\n480324970000001 && 1.2543716,103.8175247:1.2543494,103.8175973 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-480324970000001 && 1.2543494,103.8175973:1.2543716,103.8175247 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n480324970000002 && 1.2543494,103.8175973:1.254327,103.8176708 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-480324970000002 && 1.254327,103.8176708:1.2543494,103.8175973 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n480324970000003 && 1.254327,103.8176708:1.2542972,103.8177678 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-480324970000003 && 1.2542972,103.8177678:1.254327,103.8176708 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n480324970000004 && 1.2542972,103.8177678:1.2542634,103.8178787 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-480324970000004 && 1.2542634,103.8178787:1.2542972,103.8177678 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n480324970000005 && 1.2542634,103.8178787:1.254256,103.8179027:1.2542395,103.8179569 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-480324970000005 && 1.2542395,103.8179569:1.254256,103.8179027:1.2542634,103.8178787 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n480324970000006 && 1.2542395,103.8179569:1.2542347,103.8179725:1.2542172,103.8180295 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n-480324970000006 && 1.2542172,103.8180295:1.2542347,103.8179725:1.2542395,103.8179569 && last_edit_user_name -> Evandering || last_edit_changeset -> 52607303 || shelter -> yes || last_edit_time -> 1507060530000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 5\n528904849000001 && 1.2565672,103.8159474:1.2563128,103.8159095 && last_edit_user_name -> pushian || last_edit_changeset -> 52501454 || last_edit_time -> 1506756672000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n-528904849000001 && 1.2563128,103.8159095:1.2565672,103.8159474 && last_edit_user_name -> pushian || last_edit_changeset -> 52501454 || last_edit_time -> 1506756672000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n528904849000002 && 1.2563128,103.8159095:1.2561254,103.8158815:1.2559171,103.8158742:1.2556825,103.815909:1.2554871,103.816002 && last_edit_user_name -> pushian || last_edit_changeset -> 52501454 || last_edit_time -> 1506756672000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n-528904849000002 && 1.2554871,103.816002:1.2556825,103.815909:1.2559171,103.8158742:1.2561254,103.8158815:1.2563128,103.8159095 && last_edit_user_name -> pushian || last_edit_changeset -> 52501454 || last_edit_time -> 1506756672000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 2\n538105766000000 && 1.2551583,103.8167451:1.255176,103.8167019 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 1\n-538105766000000 && 1.255176,103.8167019:1.2551583,103.8167451 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || shelter -> yes || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 1\n533462931000001 && 1.2550414,103.8188556:1.2551103,103.8188232 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || turn:lanes:forward -> through || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || turn:lanes:backward -> left || highway -> service || last_edit_version -> 4\n-533462931000001 && 1.2551103,103.8188232:1.2550414,103.8188556 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || turn:lanes:forward -> through || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || turn:lanes:backward -> left || highway -> service || last_edit_version -> 4\n533462931000002 && 1.2551103,103.8188232:1.2552509,103.8187571 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || turn:lanes:forward -> through || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || turn:lanes:backward -> left || highway -> service || last_edit_version -> 4\n-533462931000002 && 1.2552509,103.8187571:1.2551103,103.8188232 && last_edit_user_name -> pk_ravi || last_edit_changeset -> 73567273 || last_edit_time -> 1566373604000 || last_edit_user_id -> 9393824 || turn:lanes:forward -> through || lanes -> 2 || lanes:forward -> 1 || lanes:backward -> 1 || turn:lanes:backward -> left || highway -> service || last_edit_version -> 4\n478164981000001 && 1.255184,103.8163823:1.2551637,103.816438:1.2551587,103.8164771:1.2551617,103.816509:1.2551803,103.8165855:1.2552024,103.8167435:1.2552052,103.8167682 && last_edit_changeset -> 65147309 || access -> private || surface -> asphalt || oneway -> yes || turn:lanes -> through || last_edit_user_name -> poornima1 || psv -> yes || last_edit_time -> 1543901775000 || last_edit_user_id -> 7868172 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 14 || foot -> no\n478164981000002 && 1.2552052,103.8167682:1.255232,103.8170032 && last_edit_changeset -> 65147309 || access -> private || surface -> asphalt || oneway -> yes || turn:lanes -> through || last_edit_user_name -> poornima1 || psv -> yes || last_edit_time -> 1543901775000 || last_edit_user_id -> 7868172 || lanes -> 1 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 14 || foot -> no\n480175922000001 && 1.2553364,103.8162287:1.2552734,103.8163156:1.2552264,103.8164529:1.2552006,103.8165879:1.2551979,103.8166889:1.2552127,103.816772 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n-480175922000001 && 1.2552127,103.816772:1.2551979,103.8166889:1.2552006,103.8165879:1.2552264,103.8164529:1.2552734,103.8163156:1.2553364,103.8162287 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n480175922000002 && 1.2552127,103.816772:1.2552288,103.8168632:1.2552359,103.8169194:1.2552652,103.8169729 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n-480175922000002 && 1.2552652,103.8169729:1.2552359,103.8169194:1.2552288,103.8168632:1.2552127,103.816772 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n480175922000003 && 1.2552652,103.8169729:1.255274,103.8169958:1.2552958,103.8170416:1.2552998,103.8170992 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n-480175922000003 && 1.2552998,103.8170992:1.2552958,103.8170416:1.255274,103.8169958:1.2552652,103.8169729 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n480175922000004 && 1.2552998,103.8170992:1.255278,103.8171476 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n-480175922000004 && 1.255278,103.8171476:1.2552998,103.8170992 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n480175922000005 && 1.255278,103.8171476:1.2552663,103.8171703:1.2552113,103.8172763:1.2551596,103.8173956 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n-480175922000005 && 1.2551596,103.8173956:1.2552113,103.8172763:1.2552663,103.8171703:1.255278,103.8171476 && last_edit_user_name -> pushian || last_edit_changeset -> 52496545 || shelter -> yes || last_edit_time -> 1506727568000 || last_edit_user_id -> 4989626 || highway -> footway || last_edit_version -> 6\n528906088000001 && 1.2581363,103.8153039:1.2580323,103.8154909:1.2579273,103.8156129:1.2578443,103.8156819:1.2575033,103.8158789:1.2573793,103.8159299:1.2570663,103.8160079:1.2568343,103.8160249:1.2566723,103.8160199:1.2563075,103.8160443 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n-528906088000001 && 1.2563075,103.8160443:1.2566723,103.8160199:1.2568343,103.8160249:1.2570663,103.8160079:1.2573793,103.8159299:1.2575033,103.8158789:1.2578443,103.8156819:1.2579273,103.8156129:1.2580323,103.8154909:1.2581363,103.8153039 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n528906088000002 && 1.2563075,103.8160443:1.2562599,103.8160475:1.2562158,103.8160623:1.2561788,103.8160906:1.256153,103.8161293:1.2561411,103.8161743:1.2561279,103.8163784:1.2561297,103.8164499:1.2561417,103.8166742 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n-528906088000002 && 1.2561417,103.8166742:1.2561297,103.8164499:1.2561279,103.8163784:1.2561411,103.8161743:1.256153,103.8161293:1.2561788,103.8160906:1.2562158,103.8160623:1.2562599,103.8160475:1.2563075,103.8160443 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n528906088000003 && 1.2561417,103.8166742:1.2561437,103.8167119:1.2561583,103.8167845:1.2562216,103.8168838:1.256358,103.8170893:1.2563947,103.8171803:1.2564185,103.81729:1.2564107,103.817402:1.2563721,103.8175074:1.2560932,103.8179758:1.2559895,103.8181625:1.2559195,103.8182565:1.2558005,103.8183775:1.2556965,103.8184475:1.255211,103.8186698:1.2551131,103.8187931:1.2551103,103.8188232 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n-528906088000003 && 1.2551103,103.8188232:1.2551131,103.8187931:1.255211,103.8186698:1.2556965,103.8184475:1.2558005,103.8183775:1.2559195,103.8182565:1.2559895,103.8181625:1.2560932,103.8179758:1.2563721,103.8175074:1.2564107,103.817402:1.2564185,103.81729:1.2563947,103.8171803:1.256358,103.8170893:1.2562216,103.8168838:1.2561583,103.8167845:1.2561437,103.8167119:1.2561417,103.8166742 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n528906088000004 && 1.2551103,103.8188232:1.2551043,103.8188876:1.2550354,103.8190007:1.2548672,103.8192216:1.2543873,103.819763 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n-528906088000004 && 1.2543873,103.819763:1.2548672,103.8192216:1.2550354,103.8190007:1.2551043,103.8188876:1.2551103,103.8188232 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || highway -> footway || last_edit_version -> 4\n528934981000001 && 1.2560081,103.8166832:1.2560381,103.8166812 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-528934981000001 && 1.2560381,103.8166812:1.2560081,103.8166832 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n528934981000002 && 1.2560381,103.8166812:1.256104,103.8166767 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-528934981000002 && 1.256104,103.8166767:1.2560381,103.8166812 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n528934981000003 && 1.256104,103.8166767:1.2561417,103.8166742 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-528934981000003 && 1.2561417,103.8166742:1.256104,103.8166767 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749613000 || last_edit_user_id -> 6172527 || footway -> crossing || highway -> footway || last_edit_version -> 2\n528897519000001 && 1.2578449,103.8122184:1.25792,103.8123632:1.2579967,103.8124881 && last_edit_changeset -> 72006528 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> div006 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 16 || foot -> no\n528897519000002 && 1.2579967,103.8124881:1.2582418,103.8128876:1.2584389,103.813247:1.2586232,103.8137176 && last_edit_changeset -> 72006528 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> div006 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 16 || foot -> no\n528897519000003 && 1.2586232,103.8137176:1.2586548,103.8137982:1.2587017,103.813937:1.2587211,103.8140476:1.258701,103.8141449:1.2586192,103.8142314:1.2585388,103.8142723:1.258288,103.8143279:1.258164,103.8143655:1.258101,103.8144165:1.2580393,103.8145137:1.2580072,103.8146377:1.2580125,103.8147222:1.2580669,103.8149089 && last_edit_changeset -> 72006528 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> div006 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 16 || foot -> no\n528897519000004 && 1.2580669,103.8149089:1.2581204,103.8150837:1.2581258,103.8151816:1.2581137,103.8152882:1.2580078,103.815484:1.257916,103.8156006:1.2578281,103.8156711:1.2574708,103.8158648:1.2573381,103.8159232:1.2569881,103.8159956:1.256791,103.816009:1.2566255,103.8160097:1.2564458,103.8160077 && last_edit_changeset -> 72006528 || surface -> asphalt || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> div006 || psv -> yes || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || lanes -> 1 || name -> Siloso Road || highway -> unclassified || last_edit_version -> 16 || foot -> no\n533463813000000 && 1.2561024,103.8160087:1.2560357,103.8160113 && last_edit_changeset -> 64005613 || surface -> asphalt || oneway -> yes || last_edit_user_name -> Ramanyam || maxheight -> 4.5 || psv -> yes || last_edit_time -> 1540893405000 || last_edit_user_id -> 7671600 || lanes -> 2 || name -> Imbiah Road || highway -> unclassified || last_edit_version -> 2 || foot -> no\n249198509000000 && 1.2558972,103.8186614:1.2557991,103.8188466:1.2557383,103.8188748 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606037 || last_edit_time -> 1507058093000 || last_edit_user_id -> 6172527 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> yes\n461380736000000 && 1.2558354,103.8165649:1.2555951,103.8166206 && last_edit_user_name -> MyWayOrNoHighway || last_edit_changeset -> 45230182 || surface -> metal || last_edit_time -> 1484608695000 || last_edit_user_id -> 4351550 || bridge -> yes || highway -> path || last_edit_version -> 2 || layer -> 1\n-461380736000000 && 1.2555951,103.8166206:1.2558354,103.8165649 && last_edit_user_name -> MyWayOrNoHighway || last_edit_changeset -> 45230182 || surface -> metal || last_edit_time -> 1484608695000 || last_edit_user_id -> 4351550 || bridge -> yes || highway -> path || last_edit_version -> 2 || layer -> 1\n100985094000000 && 1.2555443,103.815627:1.2553517,103.8150997:1.2553216,103.8150299:1.2553168,103.8149541:1.255338,103.814881:1.2553799,103.8148195:1.2555488,103.8146183:1.2558706,103.814216:1.2559564,103.8140014:1.2559832,103.813827:1.2559896,103.8137491:1.2559564,103.8136259:1.2558119,103.813448:1.2558462,103.8133009:1.2560128,103.8130116:1.2563903,103.8126047:1.2565324,103.8123252:1.2565927,103.8120234:1.2565929,103.8119988 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749609000 || last_edit_user_id -> 6172527 || name -> Imbiah Trail || highway -> footway || last_edit_version -> 5\n-100985094000000 && 1.2565929,103.8119988:1.2565927,103.8120234:1.2565324,103.8123252:1.2563903,103.8126047:1.2560128,103.8130116:1.2558462,103.8133009:1.2558119,103.813448:1.2559564,103.8136259:1.2559896,103.8137491:1.2559832,103.813827:1.2559564,103.8140014:1.2558706,103.814216:1.2555488,103.8146183:1.2553799,103.8148195:1.255338,103.814881:1.2553168,103.8149541:1.2553216,103.8150299:1.2553517,103.8150997:1.2555443,103.815627 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749609000 || last_edit_user_id -> 6172527 || name -> Imbiah Trail || highway -> footway || last_edit_version -> 5\n# Areas\n159637730000000 && 1.2559725,103.8172264:1.2560168,103.8172368:1.2560047,103.8172817:1.2559616,103.8172721 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334481789000 || last_edit_user_id -> 257555 || last_edit_version -> 2 || building -> yes\n480632927000000 && 1.2543692,103.8174987:1.2542873,103.8177619:1.2542012,103.8180387:1.2542135,103.8180424:1.2543104,103.8180521:1.2544048,103.8180283:1.2544855,103.817974:1.2545431,103.8178955:1.2545706,103.8178021:1.2545648,103.8177049:1.2545383,103.8176355:1.2544948,103.8175754:1.2544371,103.8175287 && area -> yes || last_edit_user_name -> KingRichardV || last_edit_changeset -> 66408527 || last_edit_time -> 1547760796000 || last_edit_user_id -> 9004577 || highway -> pedestrian || last_edit_version -> 3\n159637726000000 && 1.2554288,103.8163349:1.2554917,103.8163369:1.2555495,103.8163704:1.2555978,103.8164348:1.2555394,103.8164516:1.2555334,103.8164294:1.2555071,103.8164388:1.2554932,103.8163926:1.2554422,103.8164053:1.2554516,103.8164187:1.2554228,103.8164261:1.2554054,103.8164335:1.255402,103.8164154:1.2552544,103.8164757:1.2553007,103.8165669:1.2553127,103.8166795:1.2553457,103.8166963:1.2553336,103.8166728:1.2554227,103.8166319:1.2554181,103.8166219:1.2555401,103.8165736:1.2555433,103.8165823:1.2555783,103.8165643:1.2556023,103.8165541:1.2555824,103.8164878:1.2556479,103.8164616:1.2556434,103.8164408:1.2557258,103.8163798:1.2556796,103.8163121:1.2556385,103.8163349:1.2556345,103.8163268:1.2556514,103.8162886:1.2556279,103.8162779:1.2556655,103.8161887:1.2555488,103.8161391:1.2555327,103.8161854:1.2555146,103.8161753:1.2554684,103.8162665:1.2554335,103.8162437:1.2553658,103.8162973:1.2554066,103.8163375 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69415093 || addr:housenumber -> 51B || last_edit_time -> 1555813895000 || last_edit_user_id -> 741163 || addr:country -> SG || building:levels -> 2 || addr:street -> Imbiah Road || last_edit_version -> 5 || addr:postcode -> 099708 || building -> yes || addr:city -> Singapore\n159637722000000 && 1.2559812,103.8173695:1.2559725,103.8174138:1.2559417,103.8174078:1.2559397,103.8174178:1.2559591,103.8174239:1.255941,103.8175063:1.2559065,103.8175014:1.2559404,103.81736 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334481786000 || last_edit_user_id -> 257555 || last_edit_version -> 2 || building -> yes\n159637735000000 && 1.2552361,103.8169116:1.255276,103.8169656:1.2552994,103.8169974:1.2553472,103.8169621:1.255284,103.8168763 && last_edit_user_name -> happy-camper || last_edit_changeset -> 54547742 || last_edit_time -> 1513018303000 || last_edit_user_id -> 6346054 || last_edit_version -> 3 || building -> yes\n171984330000000 && 1.2557616,103.8189672:1.2557187,103.818978:1.2557801,103.8192057:1.2563692,103.8190475:1.2565433,103.8196956:1.2565683,103.819789:1.256798,103.8197273:1.2565864,103.8189392:1.2565354,103.8187493 && image -> https://images.mapillary.com/H00qB2807YW4bLqbUqLbxw/thumb-2048.jpg || last_edit_changeset -> 70113476 || website -> https://www.rwsentosa.com/ || addr:country -> SG || building:levels -> 7 || roof:colour -> #b4b7b1 || tourism -> hotel || addr:postcode -> 098271 || roof:material -> metal || building -> yes || operator -> Resorts World || addr:city -> Singapore || last_edit_user_name -> km2bp || addr:housenumber -> 12 || last_edit_time -> 1557500576000 || last_edit_user_id -> 3610826 || name -> Festive Hotel || name:en -> Festive Hotel || addr:street -> Sentosa Gateway || mapillary -> https://www.mapillary.com/map/im/H00qB2807YW4bLqbUqLbxw || last_edit_version -> 12 || name:zh -> 节庆酒店\n159637731000000 && 1.2555354,103.816707:1.2554629,103.8167385:1.2554227,103.8166319:1.2553336,103.8166728:1.2553457,103.8166963:1.2553664,103.816756:1.2553055,103.8167848:1.2553135,103.8168049:1.2552948,103.8168103:1.2553356,103.8169116:1.2553758,103.8168948:1.2555795,103.8168096:1.255559,103.8167618 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69415093 || addr:housenumber -> 51A || last_edit_time -> 1555813895000 || last_edit_user_id -> 741163 || addr:country -> SG || addr:street -> Imbiah Road || last_edit_version -> 3 || addr:postcode -> 099703 || building -> yes || addr:city -> Singapore\n159637729000000 && 1.2557808,103.8174621:1.2557882,103.8174312:1.2558132,103.817438:1.2558058,103.8174688 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334481788000 || last_edit_user_id -> 257555 || last_edit_version -> 2 || building -> yes\n159638484000000 && 1.2539915,103.8149151:1.2542215,103.8151963:1.2543327,103.8150448:1.2543756,103.8153653:1.2548422,103.8153251:1.2548489,103.8151534:1.2548717,103.8148503:1.2549736,103.814625:1.2550313,103.8146599:1.2553692,103.8142361:1.2554067,103.8142669:1.2554496,103.8142307:1.2554925,103.8142937:1.2554054,103.8144104:1.2553598,103.8144211:1.2552914,103.8144681:1.2552646,103.8145486:1.2552632,103.8146116:1.2553571,103.8147135:1.255451,103.8146974:1.2555059,103.8146491:1.2555247,103.8145727:1.2555099,103.8144721:1.2555475,103.8144131:1.2556682,103.8142441:1.2558036,103.8141194:1.2558934,103.8139236:1.2561026,103.8137372:1.2562434,103.8135427:1.2563332,103.8133402:1.2564767,103.8132007:1.2568816,103.8128789:1.2570894,103.8126013:1.2572007,103.8124859:1.2573468,103.8124028:1.2578013,103.8123424:1.257863,103.8124658:1.2581821,103.8129312:1.2583605,103.8132665:1.2584235,103.8134448:1.2585468,103.8138203:1.2585736,103.8139719:1.2585656,103.8140604:1.2585307,103.8141328:1.2584329,103.8141771:1.2582532,103.8142012:1.2580735,103.8142803:1.2579555,103.8144024:1.2579073,103.8145807:1.2579046,103.8147417:1.2580266,103.8150555:1.2580279,103.8151842:1.257973,103.8153532:1.2578617,103.8155115:1.2576981,103.8156174:1.2573643,103.8158025:1.2572288,103.8158508:1.2569473,103.8159151:1.2567274,103.8159192:1.2562232,103.8159057:1.2560959,103.8158789:1.2557902,103.815887:1.2555435,103.815946:1.2555073,103.8158347:1.2554375,103.8158628:1.2554563,103.8159688:1.2554013,103.816005:1.2553638,103.8159956:1.2553196,103.8159406:1.2552244,103.8159057:1.2551305,103.8159205:1.2550527,103.8159674:1.2550085,103.8160868:1.2550581,103.816241:1.2550192,103.8163322:1.2548597,103.8168673:1.2550487,103.8170993:1.2551024,103.8171838:1.2550822,103.8172764:1.2549803,103.8172569:1.2548959,103.8173756:1.2547893,103.8174125:1.2546264,103.8174366:1.2545426,103.8174011:1.2544816,103.8173394:1.2542469,103.8171215:1.2541296,103.817037:1.2537937,103.8169008:1.2535893,103.8168546:1.253472,103.8168418:1.2532849,103.8168713:1.2531334,103.8169451:1.2529993,103.8170323:1.2528887,103.8171416:1.2527841,103.8173193:1.2527245,103.8175111:1.2527198,103.8176297:1.2527392,103.8178369:1.2528197,103.8180153:1.2531193,103.8184183:1.2532829,103.8186872:1.2532615,103.8187006:1.2522063,103.818073:1.2522524,103.817963:1.2520977,103.8178637:1.2523559,103.8173261:1.2521122,103.8171665:1.2520548,103.8172844:1.2519046,103.817287:1.2519314,103.8170537:1.2520735,103.8168847:1.2525608,103.8170215:1.2525911,103.8168257:1.2522076,103.8165789:1.2524007,103.8164368:1.2526078,103.8162155:1.252823,103.8159292:1.2531106,103.815712:1.2538568,103.8150233 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69405864 || last_edit_time -> 1555776556000 || last_edit_user_id -> 741163 || landuse -> forest || last_edit_version -> 15\n684974293000000 && 1.2549783,103.8187925:1.2549314,103.8187549:1.2546505,103.8190969:1.2545211,103.8191378:1.2544541,103.8191378:1.2543508,103.8191915:1.2544025,103.8193054:1.2543428,103.8193423:1.2544809,103.8194013:1.2548235,103.8190459:1.2548972,103.8189467 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69405864 || natural -> wood || last_edit_time -> 1555776516000 || last_edit_user_id -> 741163 || last_edit_version -> 1\n159637725000000 && 1.2552805,103.8175453:1.2552755,103.817593:1.2557367,103.8176415:1.2557345,103.8176595:1.2557616,103.8176623:1.2557531,103.8177462:1.2557238,103.8177431:1.2557169,103.8178091:1.2552624,103.8177609:1.2552524,103.8178034:1.2552282,103.8178365:1.2551883,103.8178444:1.2550922,103.8178342:1.255097,103.817788:1.2551855,103.8177968:1.255211,103.8175384 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69415093 || addr:housenumber -> 50 || last_edit_time -> 1555813895000 || last_edit_user_id -> 741163 || addr:country -> SG || name -> Singapore Cable Car Station || addr:street -> Imbiah Road || last_edit_version -> 5 || addr:postcode -> 099706 || building -> yes || addr:city -> Singapore\n176839461000000 && 1.2544868,103.8152068:1.2544937,103.8152978:1.2544089,103.8153042:1.2543811,103.8149459:1.25492,103.8143029:1.254988,103.8143596:1.2549783,103.8143713:1.2551279,103.8145045:1.2550161,103.8146302:1.2548706,103.8145008:1.2544699,103.8149823:1.2544843,103.8151726:1.2545205,103.8151702:1.2545172,103.8151231:1.2545566,103.8151203:1.2545642,103.8152289:1.2545248,103.8152317:1.2545229,103.8152044 && last_edit_changeset -> 59646385 || website -> www.silosobeachresort.com || addr:country -> SG || tourism -> hotel || source -> Bing || addr:postcode -> 099538 || building -> yes || addr:city -> Singapore || last_edit_user_name -> Mapier || addr:housenumber -> 51 || last_edit_time -> 1528398175000 || last_edit_user_id -> 616884 || name -> Siloso Beach Resort || name:en -> Siloso Beach Resort || addr:street -> Imbiah Walk || last_edit_version -> 2 || name:zh -> 喜乐圣淘沙度假旅馆\n684974066000000 && 1.2552068,103.8187353:1.2552733,103.8186248:1.2553537,103.8185866:1.2557701,103.8184109:1.2559001,103.818301:1.2559859,103.8182031:1.25612,103.8179764:1.2564056,103.8174628:1.2564364,103.8173085:1.256415,103.8172106:1.2563694,103.8170779:1.2561696,103.8167801:1.2561643,103.8167252:1.2561763,103.8161954:1.2562112,103.8161096:1.2562742,103.8160788:1.2564337,103.8160506:1.2571162,103.8160305:1.2574112,103.8159554:1.2576565,103.8158374:1.2579341,103.815655:1.2580668,103.815494:1.2581593,103.8152862:1.2581687,103.8151427:1.2581406,103.8149817:1.2580869,103.8148342:1.2580561,103.8147202:1.2580708,103.8145781:1.2580963,103.8144909:1.258162,103.8144386:1.2582398,103.8144024:1.2585589,103.8143326:1.2586608,103.8142857:1.2587439,103.8141784:1.2587801,103.814055:1.2587573,103.8139424:1.2586594,103.8136554:1.2584691,103.8131914:1.2579435,103.8123183:1.2576552,103.8117389:1.2576539,103.8115807:1.2577343,103.8113862:1.2578697,103.8114117:1.2579341,103.811354:1.2581942,103.8118382:1.2580199,103.8119562:1.2583122,103.8124014:1.2590416,103.8121292:1.2589196,103.8127555:1.2589209,103.813308:1.2589732,103.8137519:1.2589303,103.8141046:1.2587573,103.8146988:1.2585066,103.8152701:1.2583846,103.8154954:1.2583202,103.8157247:1.2581365,103.8161378:1.2576203,103.8164556:1.2571229,103.816579:1.2569446,103.8168123:1.2568132,103.8170604:1.2567622,103.817279:1.2567354,103.8179335:1.2567448,103.8180716:1.2567354,103.8181548:1.2566349,103.8182808:1.2565973,103.8183117:1.2555529,103.8185853 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69405864 || natural -> wood || last_edit_time -> 1555776509000 || last_edit_user_id -> 741163 || last_edit_version -> 1\n689159154000000 && 1.2566324,103.8187761:1.2566542,103.8187736:1.2566741,103.8187642:1.2566899,103.8187489:1.2567,103.8187294:1.2567033,103.8187077:1.2566993,103.8186854:1.2566881,103.8186656:1.2566711,103.8186507:1.25665,103.8186422:1.2566273,103.8186411:1.2566056,103.8186476:1.2565872,103.8186609:1.2565742,103.8186795:1.2565682,103.8187014:1.2565695,103.8187233:1.2565777,103.8187437:1.256592,103.8187603:1.2566109,103.8187715 && last_edit_user_name -> km2bp || image -> https://images.mapillary.com/H00qB2807YW4bLqbUqLbxw/thumb-2048.jpg || shelter_type -> lean_to || last_edit_changeset -> 70113476 || last_edit_time -> 1557500573000 || last_edit_user_id -> 3610826 || amenity -> shelter || mapillary -> https://www.mapillary.com/map/im/H00qB2807YW4bLqbUqLbxw || last_edit_version -> 1 || building -> yes\n478355767000000 && 1.2551122,103.8173239:1.2550335,103.8173117:1.2550217,103.8173401:1.2550134,103.8173354:1.254989,103.8173861:1.255064,103.817368:1.2550903,103.8173616 && last_edit_changeset -> 60730660 || website -> http://www.skylineluge.sg || tourism -> theme_park || source -> Kaart Ground Survey 2017 || building -> yes || addr:city -> Singapore || last_edit_user_name -> gunsno || last_edit_time -> 1531657336000 || last_edit_user_id -> 7277094 || name -> Skyline Luge Sentosa || opening_hours -> Mo-Su 10:00-21:30 || addr:street -> Imbiah Road || last_edit_version -> 5\n159637728000000 && 1.2553593,103.8175531:1.2553667,103.8174787:1.2552949,103.8174715:1.255297,103.8174497:1.2551355,103.8174337:1.2551178,103.8176126:1.2550839,103.8176093:1.2550794,103.8176549:1.2550749,103.8177001:1.2551054,103.8177031:1.255097,103.817788:1.2551855,103.8177968:1.255211,103.8175384:1.2552805,103.8175453 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69415093 || addr:housenumber -> 42 || last_edit_time -> 1555813895000 || last_edit_user_id -> 741163 || addr:country -> SG || building:levels -> 2 || addr:street -> Imbiah Road || last_edit_version -> 6 || addr:postcode -> 099701 || building -> yes || addr:city -> Singapore\n528934982000000 && 1.2554409,103.8172409:1.2553875,103.8174012:1.2552784,103.8173867:1.255197,103.8173761:1.2552897,103.8171688:1.2552975,103.8171514:1.2553713,103.8171498:1.2553556,103.8172299 && area -> yes || last_edit_user_name -> cartogram || last_edit_changeset -> 54665653 || last_edit_time -> 1513372482000 || last_edit_user_id -> 6095772 || highway -> pedestrian || last_edit_version -> 2\n159637741000000 && 1.2555079,103.8172851:1.255507,103.8172833:1.255544,103.8173541:1.2555358,103.8173834:1.2554677,103.8174198:1.2554235,103.8173353 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334481791000 || last_edit_user_id -> 257555 || last_edit_version -> 2 || building -> yes\n480159876000000 && 1.254893,103.8168369:1.255041,103.8164779:1.255176,103.8167019:1.255035,103.8170579 && image -> https://images.mapillary.com/48gXOPD0CLfUnPMiflSVbQ/thumb-2048.jpg || last_edit_changeset -> 70104334 || addr:country -> SG || building:levels -> 2 || addr:postcode -> 098466 || building -> yes || addr:city -> Singapore || last_edit_user_name -> km2bp || addr:housenumber -> 60 || last_edit_time -> 1557485049000 || last_edit_user_id -> 3610826 || addr:street -> Imbiah Road || mapillary -> https://www.mapillary.com/map/im/48gXOPD0CLfUnPMiflSVbQ || last_edit_version -> 4\n159637737000000 && 1.255994,103.8172985:1.2559806,103.8173555:1.2559434,103.8173479:1.2559573,103.8172898 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334481790000 || last_edit_user_id -> 257555 || last_edit_version -> 2 || building -> yes\n159638790000000 && 1.2549438,103.8184377:1.2550108,103.8185184:1.255088,103.8184543:1.255021,103.8183736 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69415093 || last_edit_time -> 1555813896000 || last_edit_user_id -> 741163 || roof:shape -> pyramidal || building:levels -> 1 || roof:levels -> 1 || last_edit_version -> 2 || roof:material -> roof_tiles || building -> yes\n159637733000000 && 1.2559633,103.8171961:1.2559744,103.8171563:1.2559298,103.8171479:1.2558941,103.8171402:1.2558853,103.8171773 && last_edit_user_name -> Evandering || last_edit_changeset -> 52606119 || last_edit_time -> 1507058169000 || last_edit_user_id -> 6172527 || last_edit_version -> 3 || building -> yes\n260774258000000 && 1.2548596,103.8171658:1.25481,103.817147:1.254743,103.8171322:1.2547027,103.8171269:1.2546773,103.8172637:1.2547014,103.8172851:1.2547389,103.8172878:1.254747,103.8172409:1.2548422,103.8172529:1.2548757,103.8172167:1.2548395,103.8172087 && last_edit_changeset -> 69415093 || addr:country -> SG || addr:postcode -> 099003 || building -> yes || addr:city -> Singapore || last_edit_user_name -> JaLooNz || addr:housenumber -> 45 || last_edit_time -> 1555813897000 || last_edit_user_id -> 741163 || name -> Skyline Luge Office || addr:street -> Siloso Beach Walk || last_edit_version -> 4 || name:zh -> 斜坡滑車&空中吊椅\n159638788000000 && 1.2547207,103.8186175:1.2547695,103.818676:1.2548041,103.8186472:1.2547553,103.8185886 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334481988000 || last_edit_user_id -> 257555 || last_edit_version -> 1 || building -> yes\n684974295000000 && 1.2543636,103.8185987:1.2546492,103.8177699:1.254674,103.8177276:1.2548362,103.8177035:1.2549187,103.8177504:1.2549508,103.8177565:1.254924,103.8178899:1.255211,103.8179281:1.2552565,103.8178651:1.2552847,103.8178021:1.2557278,103.8178517:1.2557372,103.8177632:1.2557701,103.8177659:1.2557929,103.8176036:1.2559216,103.8176163:1.2560375,103.8172207:1.255994,103.8172106:1.2559967,103.8171503:1.255941,103.8171362:1.2558358,103.816467:1.2556829,103.8160821:1.255764,103.8160519:1.2558927,103.816062:1.255945,103.8160908:1.2559893,103.8161425:1.2560201,103.8162176:1.2560194,103.8167741:1.2560496,103.8168633:1.2562434,103.8171771:1.2562655,103.8173092:1.2562494,103.8174427:1.2558827,103.8181166:1.2557855,103.8182158:1.2556963,103.8182909:1.255585,103.8183513:1.2554898,103.8183747:1.2554959,103.8182064:1.2550541,103.8182071:1.2546894,103.8185249:1.2547028,103.8187663:1.2545808,103.8187999 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69405864 || natural -> wood || last_edit_time -> 1555776517000 || last_edit_user_id -> 741163 || last_edit_version -> 1\n159637727000000 && 1.2557965,103.8171709:1.2558684,103.8171882:1.2558718,103.8171741:1.2558853,103.8171773:1.2559633,103.8171961:1.2559789,103.8171998:1.2559725,103.8172264:1.2559616,103.8172721:1.2559573,103.8172898:1.2559434,103.8173479:1.2559404,103.81736:1.2559065,103.8175014:1.2559014,103.8175227:1.2558826,103.8175182:1.2558209,103.8175033:1.2557988,103.817498:1.2558058,103.8174688:1.2558132,103.817438:1.2558515,103.8172785:1.2557751,103.8172602:1.2557524,103.817218 && last_edit_changeset -> 69415093 || website -> https://www.sentosa.com.sg/Explore/Sentosa-Nature-Discovery-Walking-Trails/Sentosa-Nature-Discovery || addr:country -> SG || tourism -> museum || addr:postcode -> 098968 || building -> yes || addr:city -> Singapore || last_edit_user_name -> JaLooNz || addr:housenumber -> 21 || last_edit_time -> 1555813895000 || last_edit_user_id -> 741163 || name -> Sentosa Nature Discovery || addr:street -> Siloso Road || last_edit_version -> 5\n159637723000000 && 1.2553886,103.8171275:1.2553157,103.8171245:1.255316,103.8170979:1.2553166,103.8170328:1.2553669,103.8170333:1.2553457,103.8169753:1.2553758,103.8168948:1.2555795,103.8168096:1.255559,103.8167618:1.2555981,103.816645:1.2556326,103.8167312:1.2556499,103.8167942:1.2556472,103.8169156:1.2556071,103.816852:1.2556025,103.8171281:1.2556011,103.8171657:1.2555601,103.8172488:1.2553873,103.8172073 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69415093 || addr:housenumber -> 51 || last_edit_time -> 1555813894000 || last_edit_user_id -> 741163 || addr:country -> SG || addr:street -> Imbiah Road || last_edit_version -> 5 || addr:postcode -> 099702 || building -> yes || addr:city -> Singapore\n159637740000000 && 1.2556973,103.8172189:1.2556751,103.8172988:1.2557082,103.817308:1.2556841,103.8173947:1.2556463,103.8173842:1.2556238,103.8174653:1.255521,103.8174367:1.2555358,103.8173834:1.255544,103.8173541:1.2555898,103.8171891 && last_edit_user_name -> rene78 || last_edit_changeset -> 11307160 || last_edit_time -> 1334481790000 || last_edit_user_id -> 257555 || last_edit_version -> 2 || building -> yes\n176839464000000 && 1.2549498,103.8175571:1.2549714,103.8175585:1.2549908,103.817568:1.255005,103.8175842:1.255012,103.8176047:1.2550106,103.8176262:1.2550011,103.8176456:1.2549849,103.8176599:1.2549645,103.8176668:1.254943,103.8176655:1.2549236,103.817656:1.2549093,103.8176398:1.2549023,103.8176193:1.2549037,103.8175978:1.2549132,103.8175784:1.2549294,103.8175641 && image -> https://images.mapillary.com/Xo7nB4YlPWSyZBmXuSOPzQ/thumb-2048.jpg || last_edit_changeset -> 70108984 || website -> http://www.skytower.com.sg/ || man_made -> tower || tourism -> attraction || building -> yes || tower:type -> observation || last_edit_user_name -> km2bp || old_name -> Carlsberg Sky Tower || last_edit_time -> 1557493286000 || last_edit_user_id -> 3610826 || name -> Tiger Sky Tower || opening_hours -> 09:00-21:00 || mapillary -> https://www.mapillary.com/map/im/Xo7nB4YlPWSyZBmXuSOPzQ || wikipedia -> en:Tiger Sky Tower || last_edit_version -> 5 || height -> 110 || name:zh -> Tiger 摩天塔\n159637736000000 && 1.2558673,103.8175848:1.2558049,103.8175707:1.2558069,103.8175439:1.2558209,103.8175033:1.2558826,103.8175182 && last_edit_user_name -> pushian || last_edit_changeset -> 46816490 || last_edit_time -> 1489420121000 || last_edit_user_id -> 4989626 || last_edit_version -> 3 || building -> yes\n159638789000000 && 1.2548368,103.8185325:1.2549,103.8186104:1.254979,103.8185462:1.2549158,103.8184683 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 69415093 || last_edit_time -> 1555813895000 || last_edit_user_id -> 741163 || roof:shape -> pyramidal || building:levels -> 1 || roof:levels -> 1 || last_edit_version -> 2 || roof:material -> roof_tiles || building -> yes\n# Lines\n28755868000000 && 1.2555875,103.8176818:1.2586261,103.818041 && last_edit_user_name -> km2bp || last_edit_changeset -> 70113476 || last_edit_time -> 1557500575000 || last_edit_user_id -> 3610826 || aerialway -> gondola || name -> Singapore-Sentosa Cable Car || last_edit_version -> 10 || layer -> 2\n688904126000000 && 1.2567636,103.8181642:1.2567019,103.8182446:1.2566282,103.8183197:1.2564901,103.8183519:1.2555581,103.8185985:1.2552041,103.8187534:1.2551238,103.8188784:1.2549321,103.8191861:1.25435,103.8198425:1.2536597,103.8207042:1.2533647,103.8211012:1.2529558,103.8216765:1.2528445,103.8218643:1.2527654,103.8220789:1.2527573,103.8221888:1.2527372,103.8228366:1.2526688,103.8232389:1.2526112,103.8234964:1.2525146,103.8237244:1.2523149,103.824006:1.2518429,103.8245988:1.2518054,103.8246873:1.2518027,103.8247356:1.2518335,103.8247986:1.2520561,103.8249689:1.2522639,103.8251688:1.2528284,103.8257535:1.2529504,103.8258608:1.2530483,103.825905:1.2531408,103.8259278:1.2532628,103.8259292:1.253366,103.8259144:1.2534787,103.8258688:1.2536248,103.8257562:1.253775,103.8255389:1.2538621,103.8254517:1.2559336,103.8237673:1.2562219,103.8236278:1.2564606,103.8235849:1.2567091,103.8235736:1.2567098,103.823498:1.2567564,103.8234498:1.256828,103.8234197:1.2574154,103.8232975:1.2574578,103.8232702:1.2574909,103.8232246:1.2575146,103.8231721:1.2575731,103.8230422:1.2575991,103.8229848 && last_edit_user_name -> km2bp || last_edit_changeset -> 70073507 || last_edit_time -> 1557408324000 || last_edit_user_id -> 3610826 || last_edit_version -> 1\n689102082000000 && 1.2543463,103.8182899:1.2544518,103.8180627 && last_edit_user_name -> km2bp || last_edit_changeset -> 70104334 || last_edit_time -> 1557485045000 || last_edit_user_id -> 3610826 || aerialway -> cable_car || name -> Sentosa Line || last_edit_version -> 1\n689102084000000 && 1.2545761,103.8177946:1.2547353,103.817453 && last_edit_user_name -> km2bp || last_edit_changeset -> 70104334 || last_edit_time -> 1557485045000 || last_edit_user_id -> 3610826 || aerialway -> cable_car || name -> Sentosa Line || last_edit_version -> 1\n689102085000000 && 1.2544518,103.8180627:1.2545761,103.8177946 && last_edit_user_name -> km2bp || image -> https://images.mapillary.com/tM5pTW4qQVgbrk7FB2O0ow/thumb-2048.jpg || last_edit_changeset -> 70104334 || last_edit_time -> 1557485045000 || last_edit_user_id -> 3610826 || aerialway -> cable_car || name -> Sentosa Line || mapillary -> https://www.mapillary.com/map/im/tM5pTW4qQVgbrk7FB2O0ow || last_edit_version -> 1\n689102086000000 && 1.2547353,103.817453:1.2549477,103.816994 && last_edit_user_name -> km2bp || image -> https://images.mapillary.com/48gXOPD0CLfUnPMiflSVbQ/thumb-2048.jpg || last_edit_changeset -> 70104334 || last_edit_time -> 1557485045000 || last_edit_user_id -> 3610826 || aerialway -> cable_car || name -> Sentosa Line || mapillary -> https://www.mapillary.com/map/im/48gXOPD0CLfUnPMiflSVbQ || last_edit_version -> 1\n689102087000000 && 1.2549477,103.816994:1.2550487,103.8167759:1.2553994,103.8159077 && last_edit_user_name -> km2bp || last_edit_changeset -> 70104334 || last_edit_time -> 1557485045000 || last_edit_user_id -> 3610826 || aerialway -> cable_car || name -> Sentosa Line || last_edit_version -> 1\n689102088000000 && 1.2553994,103.8159077:1.2558502,103.8148119 && last_edit_user_name -> km2bp || image -> https://images.mapillary.com/K6QdH5wB__71miZmpGihNA/thumb-2048.jpg || last_edit_changeset -> 70104334 || last_edit_time -> 1557485046000 || last_edit_user_id -> 3610826 || aerialway -> cable_car || name -> Sentosa Line || mapillary -> https://www.mapillary.com/map/im/K6QdH5wB__71miZmpGihNA || last_edit_version -> 1\n# Points\n6464873707000000 && 1.2553279,103.8173871 && last_edit_user_name -> km2bp || image -> https://images.mapillary.com/-3LjtvvOeOm6KqBpMPBaNg/thumb-2048.jpg || last_edit_changeset -> 70113476 || last_edit_time -> 1557500572000 || last_edit_user_id -> 3610826 || amenity -> fountain || mapillary -> https://www.mapillary.com/map/im/-3LjtvvOeOm6KqBpMPBaNg || last_edit_version -> 1\n5209288654000000 && 1.2580669,103.8149089 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 2\n4485958594000000 && 1.2555099,103.8174809 && last_edit_user_name -> gps-newcomer || last_edit_changeset -> 43450576 || last_edit_time -> 1478464761000 || last_edit_user_id -> 94213 || amenity -> toilets || fee -> no || last_edit_version -> 1\n5209288650000000 && 1.2579967,103.8124881 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || highway -> crossing || last_edit_version -> 1\n1717541875000000 && 1.2553003,103.816949 && last_edit_user_name -> happy-camper || last_edit_changeset -> 52686350 || last_edit_time -> 1507304366000 || last_edit_user_id -> 6346054 || name -> Imbiah Lookout || highway -> bus_stop || last_edit_version -> 11\n4893137468000000 && 1.2552108,103.8182922 && last_edit_user_name -> DAJIBA || last_edit_changeset -> 49211674 || last_edit_time -> 1496462062000 || last_edit_user_id -> 360397 || name -> Former Mini Golf || tourism -> attraction || last_edit_version -> 1\n5146232329000000 && 1.2554965,103.8160775 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707362000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 8\n5209288659000000 && 1.2563089,103.8160091 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 2\n4731913779000000 && 1.255064,103.817368 && last_edit_user_name -> pushian || last_edit_changeset -> 52243602 || last_edit_time -> 1505999486000 || last_edit_user_id -> 4989626 || entrance -> yes || last_edit_version -> 2\n6464873706000000 && 1.2553539,103.817251 && last_edit_user_name -> km2bp || image -> https://images.mapillary.com/sDvht2V9qXhRVmoSJmWh_w/thumb-2048.jpg || last_edit_changeset -> 70113476 || last_edit_time -> 1557500572000 || last_edit_user_id -> 3610826 || tourism -> artwork || artwork_type -> sculpture || mapillary -> https://www.mapillary.com/map/im/sDvht2V9qXhRVmoSJmWh_w || last_edit_version -> 1\n3686388539000000 && 1.2550487,103.8167759 && last_edit_user_name -> km2bp || last_edit_changeset -> 70104334 || last_edit_time -> 1557485048000 || last_edit_user_id -> 3610826 || aerialway -> station || name -> Imbiah Lookout || last_edit_version -> 9 || wikidata -> Q6003536\n2248167729000000 && 1.2555712,103.8177397 && last_edit_user_name -> Ulmon Community || last_edit_changeset -> 15606850 || last_edit_time -> 1365076619000 || last_edit_user_id -> 1313848 || name -> Southern Ridges || tourism -> viewpoint || last_edit_version -> 1\n4613238712000000 && 1.2552278,103.8175128 && last_edit_user_name -> Canyonsrcool || last_edit_changeset -> 45231662 || last_edit_time -> 1484616741000 || last_edit_user_id -> 126508 || amenity -> fast_food || name -> Subway || cuisine -> sandwich || last_edit_version -> 1\n5209288647000000 && 1.2551103,103.8188232 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749607000 || last_edit_user_id -> 6172527 || highway -> crossing || last_edit_version -> 1\n5138638008000000 && 1.256104,103.8166767 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707358000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 9\n5146232330000000 && 1.2554904,103.8160242 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707362000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 7\n5209288660000000 && 1.2560381,103.8166812 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 2\n5586638524000000 && 1.2550597,103.8176013 && last_edit_user_name -> Wiple || last_edit_changeset -> 58534348 || name:de -> Tiger Sky Tower || last_edit_time -> 1525026129000 || last_edit_user_id -> 7355545 || tourism -> attraction || last_edit_version -> 1\n5209288652000000 && 1.2586232,103.8137176 && last_edit_user_name -> Evandering || last_edit_changeset -> 53491829 || last_edit_time -> 1509749608000 || last_edit_user_id -> 6172527 || highway -> crossing || last_edit_version -> 1\n4613238713000000 && 1.2551627,103.8175716 && last_edit_user_name -> Canyonsrcool || last_edit_changeset -> 45231662 || last_edit_time -> 1484616741000 || last_edit_user_id -> 126508 || amenity -> cafe || name -> Starbucks || last_edit_version -> 1\n5761606753000000 && 1.2554079,103.8165019 && last_edit_user_name -> gunsno || last_edit_changeset -> 60730626 || last_edit_time -> 1531657248000 || last_edit_user_id -> 7277094 || name -> 4D Adventureland || opening_hours -> Mo-Su 09:00-21:00 || tourism -> attraction || last_edit_version -> 1\n6464873708000000 && 1.2553653,103.8174089 && last_edit_user_name -> km2bp || last_edit_changeset -> 70113476 || last_edit_time -> 1557500572000 || last_edit_user_id -> 3610826 || name -> Butterfly Park & Insect Kigdom || tourism -> artwork || mapillary -> https://www.mapillary.com/map/im/GlnBJBlPZOptpdPiudAvFA || last_edit_version -> 1\n4217410090000000 && 1.2554744,103.8169904 && name:ru -> Парк бабочек и царство насекомых || last_edit_user_name -> rene78 || last_edit_changeset -> 39697306 || website -> http://www.jungle.com.sg/ || last_edit_time -> 1464725794000 || last_edit_user_id -> 257555 || name -> Butterfly Park & Insect Kingdom || tourism -> zoo || last_edit_version -> 2\n316161140000000 && 1.2555875,103.8176818 && last_edit_user_name -> JaLooNz || last_edit_changeset -> 12738977 || last_edit_time -> 1345037425000 || last_edit_user_id -> 741163 || aerialway -> station || name -> Sentosa || last_edit_version -> 3\n5209288658000000 && 1.2563109,103.8159577 && last_edit_user_name -> naik4 || last_edit_changeset -> 59733650 || last_edit_time -> 1528707364000 || last_edit_user_id -> 7671613 || highway -> crossing || last_edit_version -> 2\n# Relations\n2353599000000 && 28755868000000 ->  -> L || 316161140000000 -> stop -> P && last_edit_changeset -> 70181245 || description -> Singapore Cable Car || type -> route || operator -> Singapore Cable Car || last_edit_user_name -> km2bp || ref -> Singapore Cable Car || route -> aerialway || last_edit_time -> 1557735493000 || last_edit_user_id -> 3610826 || name -> Singapore Cable Car || from -> Mount Faber || to -> Imbian || last_edit_version -> 5\n2581000000000 && 1717541875000000 -> platform -> N || 1717541875000000 -> platform -> P && last_edit_changeset -> 71305177 || fee -> no || type -> route || operator -> Sentosa || network -> Sentosa || last_edit_user_name -> mueschel || ref -> Bus A || roundtrip -> yes || route -> bus || public_transport:version -> 2 || last_edit_time -> 1560703589000 || last_edit_user_id -> 616774 || name -> Sentosa Bus A || opening_hours -> Mo-SU 7:00-00:10 || interval -> 00:10:00 || last_edit_version -> 48\n7616954000000 && 260777732000000 ->  -> E || 478164185000001 ->  -> E || 478164185000002 ->  -> E || 478164981000001 ->  -> E || 478164981000002 ->  -> E || 482407210000000 ->  -> E || 482407211000001 ->  -> E || 482407211000002 ->  -> E || 482416616000001 ->  -> E || 482416616000002 ->  -> E || 482416618000001 ->  -> E || 482416618000002 ->  -> E || 528240340000001 ->  -> E || 528240340000002 ->  -> E || 528240340000003 ->  -> E || 528240340000004 ->  -> E || 528897519000001 ->  -> E || 528897519000002 ->  -> E || 528897519000003 ->  -> E || 528897519000004 ->  -> E || 528898916000000 ->  -> E || 528898917000001 ->  -> E || 528898917000002 ->  -> E || 530020436000001 ->  -> E || 530020436000002 ->  -> E || 530020436000003 ->  -> E || 530020436000004 ->  -> E || 533417989000000 ->  -> E || 538105768000001 ->  -> E || 538105768000002 ->  -> E || 639335711000000 ->  -> E || 650732280000000 ->  -> E || 702400958000001 ->  -> E || 702400958000002 ->  -> E && last_edit_user_name -> aj_34 || last_edit_changeset -> 74099421 || route -> bus || last_edit_time -> 1567618893000 || last_edit_user_id -> 8911947 || name -> Sentosa Bus A || type -> route || last_edit_version -> 51\n8865315000000 && 533417988000000 -> via -> E || 533463813000000 -> to -> E || 639335711000000 -> from -> E && last_edit_user_name -> Ramanyam || last_edit_changeset -> 64005613 || last_edit_time -> 1540893405000 || last_edit_user_id -> 7671600 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n8865318000000 && 533417988000000 -> to -> E || 639335711000000 -> via -> E || 639335779000000 -> from -> E && last_edit_user_name -> Ramanyam || last_edit_changeset -> 64005642 || last_edit_time -> 1540893493000 || last_edit_user_id -> 7671600 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n8865326000000 && 533463813000000 -> from -> E || 639335711000000 -> to -> E || 639335779000000 -> via -> E && last_edit_user_name -> Ramanyam || last_edit_changeset -> 64005668 || last_edit_time -> 1540893552000 || last_edit_user_id -> 7671600 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n8870287000000 && 533417988000000 -> from -> E || 533463813000000 -> via -> E || 639335779000000 -> to -> E && last_edit_user_name -> mutpuri || last_edit_changeset -> 64038126 || last_edit_time -> 1540975886000 || last_edit_user_id -> 7795551 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n8883047000000 && 528897519000001 -> to -> E || 528897519000002 -> to -> E || 528897519000003 -> to -> E || 528897519000004 -> to -> E && last_edit_user_name -> div006 || last_edit_changeset -> 72006528 || last_edit_time -> 1562581439000 || last_edit_user_id -> 8911933 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 2\n8923582000000 && 1717541894000000 -> via -> N || -528900905000002 -> from -> E || -528900905000002 -> to -> E || -528900905000001 -> from -> E || -528900905000001 -> to -> E || 528900905000001 -> from -> E || 528900905000001 -> to -> E || 528900905000002 -> from -> E || 528900905000002 -> to -> E && last_edit_user_name -> nadigatla || last_edit_changeset -> 64439376 || last_edit_time -> 1542110062000 || last_edit_user_id -> 8995721 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n8924581000000 && 613981020000000 -> via -> N && last_edit_user_name -> kumar3 || last_edit_changeset -> 64442215 || last_edit_time -> 1542115071000 || last_edit_user_id -> 8924971 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n8993526000000 && 4731977868000000 -> via -> N || -646425420000000 -> from -> E || -646425420000000 -> to -> E || 646425420000000 -> from -> E || 646425420000000 -> to -> E && last_edit_user_name -> aj_34 || last_edit_changeset -> 64661757 || last_edit_time -> 1542641735000 || last_edit_user_id -> 8911947 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n8993527000000 && 4731977868000000 -> via -> N && last_edit_user_name -> aj_34 || last_edit_changeset -> 64661757 || last_edit_time -> 1542641735000 || last_edit_user_id -> 8911947 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n9033062000000 && 5175392808000000 -> via -> N || 533417990000000 -> from -> E || 639335710000000 -> to -> E && last_edit_user_name -> pavani_03 || last_edit_changeset -> 64726693 || last_edit_time -> 1542784300000 || last_edit_user_id -> 8911925 || restriction -> no_left_turn || type -> restriction || last_edit_version -> 1\n9574726000000 && 688904126000000 -> outer -> L && last_edit_changeset -> 70073507 || website -> https://www.rwsentosa.com/ || addr:country -> SG || tourism -> attraction || type -> multipolygon || addr:postcode -> 098269 || addr:city -> Singapore || last_edit_user_name -> km2bp || addr:housenumber -> 8 || last_edit_time -> 1557408326000 || last_edit_user_id -> 3610826 || landuse -> retail || name -> Resorts World Sentosa || name:en -> Resorts World Sentosa || addr:street -> Sentosa Gateway || wikipedia -> en:Resorts World Sentosa || last_edit_version -> 1 || wikidata -> Q5955134 || email -> enquiries@rwsentosa.com || name:zh -> 圣淘沙名胜世界\n10100363000000 && 2558468523000000 -> via -> N && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n10100364000000 && 2558468523000000 -> via -> N || -249144132000000 -> from -> E || -249144132000000 -> to -> E || 249144132000000 -> from -> E || 249144132000000 -> to -> E && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n10100365000000 && 2558468525000000 -> via -> N || -249144132000000 -> from -> E || -249144132000000 -> to -> E || 249144132000000 -> from -> E || 249144132000000 -> to -> E && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n10100366000000 && 2558468525000000 -> via -> N || -730092327000000 -> from -> E || -730092327000000 -> to -> E || 730092327000000 -> from -> E || 730092327000000 -> to -> E && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n10100367000000 && 2558083058000000 -> via -> N || -730092327000000 -> from -> E || -730092327000000 -> to -> E || 730092327000000 -> from -> E || 730092327000000 -> to -> E && last_edit_user_name -> div006 || last_edit_changeset -> 75129398 || last_edit_time -> 1569910184000 || last_edit_user_id -> 8911933 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n10101134000000 && 2558083041000000 -> via -> N || -533462931000002 -> from -> E || -533462931000002 -> to -> E || -533462931000001 -> from -> E || -533462931000001 -> to -> E || 533462931000001 -> from -> E || 533462931000001 -> to -> E || 533462931000002 -> from -> E || 533462931000002 -> to -> E && last_edit_user_name -> div006 || last_edit_changeset -> 75137528 || last_edit_time -> 1569920612000 || last_edit_user_id -> 8911933 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n2353586000000 && 2581000000000 ->  -> R && last_edit_user_name -> JaLooNz || last_edit_changeset -> 61784221 || last_edit_time -> 1534652650000 || last_edit_user_id -> 741163 || name -> Sentosa Public Transport Services || type -> operator || last_edit_version -> 4 || network -> Sentosa\n"
  },
  {
    "path": "src/test/resources/data/ButterflyPark/ButterflyPark.osm",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<osm version=\"0.6\" generator=\"CGImap 0.7.5 (8536 thorn-03.openstreetmap.org)\" copyright=\"OpenStreetMap and contributors\" attribution=\"http://www.openstreetmap.org/copyright\" license=\"http://opendatacommons.org/licenses/odbl/1-0/\">\n <bounds minlat=\"1.2543700\" minlon=\"103.8152100\" maxlat=\"1.2565700\" maxlon=\"103.8187700\"/>\n <node id=\"134940391\" visible=\"true\" version=\"8\" changeset=\"11302963\" timestamp=\"2012-04-14T20:33:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2574909\" lon=\"103.8232246\"/>\n <node id=\"134940413\" visible=\"true\" version=\"7\" changeset=\"11302963\" timestamp=\"2012-04-14T20:33:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2568280\" lon=\"103.8234197\"/>\n <node id=\"134940446\" visible=\"true\" version=\"7\" changeset=\"1189930\" timestamp=\"2009-05-14T17:52:09Z\" user=\"Pel\" uid=\"101815\" lat=\"1.2567098\" lon=\"103.8234980\"/>\n <node id=\"247182122\" visible=\"true\" version=\"7\" changeset=\"70241839\" timestamp=\"2019-05-14T15:47:49Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2567091\" lon=\"103.8235736\"/>\n <node id=\"316161140\" visible=\"true\" version=\"3\" changeset=\"12738977\" timestamp=\"2012-08-15T13:30:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555875\" lon=\"103.8176818\">\n  <tag k=\"aerialway\" v=\"station\"/><tag k=\"name\" v=\"Sentosa\"/>\n </node>\n <node id=\"471491299\" visible=\"true\" version=\"11\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558563\" lon=\"103.8182142\"/>\n <node id=\"471491300\" visible=\"true\" version=\"11\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559241\" lon=\"103.8181233\"/>\n <node id=\"471491301\" visible=\"true\" version=\"11\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563107\" lon=\"103.8174270\"/>\n <node id=\"471491302\" visible=\"true\" version=\"11\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563115\" lon=\"103.8172383\"/>\n <node id=\"471491303\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560381\" lon=\"103.8167676\"/>\n <node id=\"471491305\" visible=\"true\" version=\"10\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560380\" lon=\"103.8164078\"/>\n <node id=\"471491310\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:26Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551840\" lon=\"103.8163823\"/>\n <node id=\"471491312\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:26Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552024\" lon=\"103.8167435\"/>\n <node id=\"471491338\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2566255\" lon=\"103.8160097\"/>\n <node id=\"471491339\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2569881\" lon=\"103.8159956\"/>\n <node id=\"471491340\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2573381\" lon=\"103.8159232\"/>\n <node id=\"471491341\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2574708\" lon=\"103.8158648\"/>\n <node id=\"471491342\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2578281\" lon=\"103.8156711\"/>\n <node id=\"471491343\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2579160\" lon=\"103.8156006\"/>\n <node id=\"471491344\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2581137\" lon=\"103.8152882\"/>\n <node id=\"471491345\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2581204\" lon=\"103.8150837\"/>\n <node id=\"471491346\" visible=\"true\" version=\"3\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2580125\" lon=\"103.8147222\"/>\n <node id=\"471491347\" visible=\"true\" version=\"4\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2580393\" lon=\"103.8145137\"/>\n <node id=\"471491348\" visible=\"true\" version=\"4\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2581010\" lon=\"103.8144165\"/>\n <node id=\"471491349\" visible=\"true\" version=\"4\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2582880\" lon=\"103.8143279\"/>\n <node id=\"471491350\" visible=\"true\" version=\"3\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2585388\" lon=\"103.8142723\"/>\n <node id=\"471491351\" visible=\"true\" version=\"4\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2586192\" lon=\"103.8142314\"/>\n <node id=\"471491352\" visible=\"true\" version=\"4\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2587211\" lon=\"103.8140476\"/>\n <node id=\"471491353\" visible=\"true\" version=\"4\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2587017\" lon=\"103.8139370\"/>\n <node id=\"471491355\" visible=\"true\" version=\"4\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2582418\" lon=\"103.8128876\"/>\n <node id=\"603563257\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T08:55:48Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2543927\" lon=\"103.8171623\"/>\n <node id=\"603563258\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T08:49:04Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2542900\" lon=\"103.8170499\"/>\n <node id=\"603563344\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554913\" lon=\"103.8158824\"/>\n <node id=\"613981020\" visible=\"true\" version=\"4\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2578449\" lon=\"103.8122184\"/>\n <node id=\"1166463593\" visible=\"true\" version=\"1\" changeset=\"7363914\" timestamp=\"2011-02-22T14:30:11Z\" user=\"Fraggle\" uid=\"44005\" lat=\"1.2558462\" lon=\"103.8133009\"/>\n <node id=\"1166463599\" visible=\"true\" version=\"4\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:34Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2559896\" lon=\"103.8137491\"/>\n <node id=\"1166463611\" visible=\"true\" version=\"1\" changeset=\"7363914\" timestamp=\"2011-02-22T14:30:12Z\" user=\"Fraggle\" uid=\"44005\" lat=\"1.2560128\" lon=\"103.8130116\"/>\n <node id=\"1166463618\" visible=\"true\" version=\"1\" changeset=\"7363914\" timestamp=\"2011-02-22T14:30:12Z\" user=\"Fraggle\" uid=\"44005\" lat=\"1.2563903\" lon=\"103.8126047\"/>\n <node id=\"1166463638\" visible=\"true\" version=\"1\" changeset=\"7363914\" timestamp=\"2011-02-22T14:30:13Z\" user=\"Fraggle\" uid=\"44005\" lat=\"1.2558119\" lon=\"103.8134480\"/>\n <node id=\"1166463643\" visible=\"true\" version=\"1\" changeset=\"7363914\" timestamp=\"2011-02-22T14:30:14Z\" user=\"Fraggle\" uid=\"44005\" lat=\"1.2565324\" lon=\"103.8123252\"/>\n <node id=\"1166463644\" visible=\"true\" version=\"3\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:34Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2559832\" lon=\"103.8138270\"/>\n <node id=\"1316095871\" visible=\"true\" version=\"2\" changeset=\"19195140\" timestamp=\"2013-11-30T14:21:12Z\" user=\"khatulistiwa\" uid=\"94431\" lat=\"1.2555581\" lon=\"103.8185985\"/>\n <node id=\"1316095876\" visible=\"true\" version=\"6\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557801\" lon=\"103.8192057\"/>\n <node id=\"1316095915\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2518335\" lon=\"103.8247986\"/>\n <node id=\"1316096285\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2527372\" lon=\"103.8228366\"/>\n <node id=\"1316096954\" visible=\"true\" version=\"3\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:45Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2567636\" lon=\"103.8181642\"/>\n <node id=\"1316097185\" visible=\"true\" version=\"3\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:45Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2564901\" lon=\"103.8183519\"/>\n <node id=\"1316097339\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2533647\" lon=\"103.8211012\"/>\n <node id=\"1316097408\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2518054\" lon=\"103.8246873\"/>\n <node id=\"1316097553\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2530483\" lon=\"103.8259050\"/>\n <node id=\"1316097655\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2538621\" lon=\"103.8254517\"/>\n <node id=\"1316097718\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2528445\" lon=\"103.8218643\"/>\n <node id=\"1316097943\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2518429\" lon=\"103.8245988\"/>\n <node id=\"1316097987\" visible=\"true\" version=\"4\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2528284\" lon=\"103.8257535\"/>\n <node id=\"1316098039\" visible=\"true\" version=\"4\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:23Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2526112\" lon=\"103.8234964\"/>\n <node id=\"1316098058\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:24Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2527654\" lon=\"103.8220789\"/>\n <node id=\"1316098225\" visible=\"true\" version=\"2\" changeset=\"12275424\" timestamp=\"2012-07-18T11:47:57Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551238\" lon=\"103.8188784\"/>\n <node id=\"1316098229\" visible=\"true\" version=\"4\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:24Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2522639\" lon=\"103.8251688\"/>\n <node id=\"1316098277\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:24Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2529558\" lon=\"103.8216765\"/>\n <node id=\"1316098695\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:24Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2537750\" lon=\"103.8255389\"/>\n <node id=\"1316098722\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:24Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2532628\" lon=\"103.8259292\"/>\n <node id=\"1316098955\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:24Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2531408\" lon=\"103.8259278\"/>\n <node id=\"1380555963\" visible=\"true\" version=\"2\" changeset=\"12746266\" timestamp=\"2012-08-16T04:25:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550546\" lon=\"103.8187039\"/>\n <node id=\"1422900635\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2547068\" lon=\"103.8169988\"/>\n <node id=\"1422900637\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2541752\" lon=\"103.8165262\"/>\n <node id=\"1422900639\" visible=\"true\" version=\"2\" changeset=\"11491444\" timestamp=\"2012-05-03T18:41:51Z\" user=\"cboothroyd\" uid=\"255802\" lat=\"1.2545321\" lon=\"103.8173245\"/>\n <node id=\"1422900640\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2539338\" lon=\"103.8162285\"/>\n <node id=\"1422900641\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2532796\" lon=\"103.8158850\"/>\n <node id=\"1422900642\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2545483\" lon=\"103.8165437\"/>\n <node id=\"1422900645\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T08:49:07Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2543994\" lon=\"103.8166001\"/>\n <node id=\"1422900647\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2543901\" lon=\"103.8168937\"/>\n <node id=\"1422900653\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2534981\" lon=\"103.8159041\"/>\n <node id=\"1422900654\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2547503\" lon=\"103.8168416\"/>\n <node id=\"1422900655\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2541619\" lon=\"103.8163022\"/>\n <node id=\"1422900657\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2538415\" lon=\"103.8160059\"/>\n <node id=\"1422900660\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2542436\" lon=\"103.8166373\"/>\n <node id=\"1422900662\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2540960\" lon=\"103.8162606\"/>\n <node id=\"1422900664\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2545722\" lon=\"103.8170407\"/>\n <node id=\"1422900666\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2534020\" lon=\"103.8158711\"/>\n <node id=\"1422900673\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2546301\" lon=\"103.8165553\"/>\n <node id=\"1422900676\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2535760\" lon=\"103.8159081\"/>\n <node id=\"1422900678\" visible=\"true\" version=\"2\" changeset=\"10877772\" timestamp=\"2012-03-05T11:07:19Z\" user=\"twut\" uid=\"522960\" lat=\"1.2521213\" lon=\"103.8168274\"/>\n <node id=\"1422900680\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2541521\" lon=\"103.8167856\"/>\n <node id=\"1422900685\" visible=\"true\" version=\"2\" changeset=\"10877772\" timestamp=\"2012-03-05T11:07:18Z\" user=\"twut\" uid=\"522960\" lat=\"1.2548287\" lon=\"103.8172656\"/>\n <node id=\"1716790299\" visible=\"true\" version=\"1\" changeset=\"11302963\" timestamp=\"2012-04-14T20:33:29Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2574154\" lon=\"103.8232975\"/>\n <node id=\"1716790736\" visible=\"true\" version=\"1\" changeset=\"11302963\" timestamp=\"2012-04-14T20:33:40Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2574578\" lon=\"103.8232702\"/>\n <node id=\"1716790799\" visible=\"true\" version=\"1\" changeset=\"11302963\" timestamp=\"2012-04-14T20:33:41Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2567564\" lon=\"103.8234498\"/>\n <node id=\"1717537324\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2544863\" lon=\"103.8165667\"/>\n <node id=\"1717537325\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2547254\" lon=\"103.8167780\"/>\n <node id=\"1717537326\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2538887\" lon=\"103.8161712\"/>\n <node id=\"1717537329\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:42Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2541108\" lon=\"103.8168991\"/>\n <node id=\"1717537330\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:42Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2541914\" lon=\"103.8165958\"/>\n <node id=\"1717537332\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:42Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2546806\" lon=\"103.8166255\"/>\n <node id=\"1717537333\" visible=\"true\" version=\"3\" changeset=\"11491444\" timestamp=\"2012-05-03T18:41:51Z\" user=\"cboothroyd\" uid=\"255802\" lat=\"1.2540528\" lon=\"103.8170529\"/>\n <node id=\"1717537337\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2538656\" lon=\"103.8160740\"/>\n <node id=\"1717537353\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:42Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2540049\" lon=\"103.8162514\"/>\n <node id=\"1717537357\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2544876\" lon=\"103.8169740\"/>\n <node id=\"1717537363\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2536669\" lon=\"103.8158950\"/>\n <node id=\"1717537391\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T08:48:52Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2543239\" lon=\"103.8166277\"/>\n <node id=\"1717537411\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2537812\" lon=\"103.8159439\"/>\n <node id=\"1717537412\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2540962\" lon=\"103.8168321\"/>\n <node id=\"1717537415\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:42Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2542598\" lon=\"103.8168117\"/>\n <node id=\"1717537417\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:42Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2546311\" lon=\"103.8170698\"/>\n <node id=\"1717537429\" visible=\"true\" version=\"3\" changeset=\"11491444\" timestamp=\"2012-05-03T18:41:51Z\" user=\"cboothroyd\" uid=\"255802\" lat=\"1.2536560\" lon=\"103.8168978\"/>\n <node id=\"1717537432\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:42Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2542373\" lon=\"103.8169698\"/>\n <node id=\"1717537438\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:43Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2546819\" lon=\"103.8167156\"/>\n <node id=\"1717537490\" visible=\"true\" version=\"2\" changeset=\"20467454\" timestamp=\"2014-02-09T15:57:42Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2541992\" lon=\"103.8163935\"/>\n <node id=\"1717541873\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:26Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552468\" lon=\"103.8171137\"/>\n <node id=\"1717541874\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:26Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551225\" lon=\"103.8170949\"/>\n <node id=\"1717541875\" visible=\"true\" version=\"11\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:26Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553003\" lon=\"103.8169490\">\n  <tag k=\"highway\" v=\"bus_stop\"/><tag k=\"name\" v=\"Imbiah Lookout\"/>\n </node>\n <node id=\"1717541876\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552632\" lon=\"103.8170491\"/>\n <node id=\"1717541877\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552549\" lon=\"103.8170276\"/>\n <node id=\"1717541878\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551163\" lon=\"103.8170688\"/>\n <node id=\"1717541879\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552646\" lon=\"103.8170720\"/>\n <node id=\"1717541881\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552320\" lon=\"103.8170032\"/>\n <node id=\"1717541883\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551324\" lon=\"103.8170187\"/>\n <node id=\"1717541884\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T08:55:45Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2544446\" lon=\"103.8173782\"/>\n <node id=\"1717541885\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T08:55:45Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2542019\" lon=\"103.8171228\"/>\n <node id=\"1717541888\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551593\" lon=\"103.8171325\"/>\n <node id=\"1717541889\" visible=\"true\" version=\"2\" changeset=\"11491444\" timestamp=\"2012-05-03T18:41:51Z\" user=\"cboothroyd\" uid=\"255802\" lat=\"1.2549776\" lon=\"103.8174195\"/>\n <node id=\"1717541890\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552293\" lon=\"103.8171286\"/>\n <node id=\"1717541891\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T08:55:46Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2551150\" lon=\"103.8173869\"/>\n <node id=\"1717541892\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551374\" lon=\"103.8171170\"/>\n <node id=\"1717541893\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T08:55:46Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2545781\" lon=\"103.8174628\"/>\n <node id=\"1717541894\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551852\" lon=\"103.8171392\"/>\n <node id=\"1717541895\" visible=\"true\" version=\"3\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2551768\" lon=\"103.8172073\"/>\n <node id=\"1717541896\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551198\" lon=\"103.8170423\"/>\n <node id=\"1717541897\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552081\" lon=\"103.8171373\"/>\n <node id=\"1717541898\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T08:55:46Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2547564\" lon=\"103.8174721\"/>\n <node id=\"1717541902\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552590\" lon=\"103.8170942\"/>\n <node id=\"1717541904\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551617\" lon=\"103.8165090\"/>\n <node id=\"1717556418\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556499\" lon=\"103.8167942\"/>\n <node id=\"1717556420\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556796\" lon=\"103.8163121\"/>\n <node id=\"1717556422\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:35Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557988\" lon=\"103.8174980\"/>\n <node id=\"1717556423\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555590\" lon=\"103.8167618\"/>\n <node id=\"1717556424\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2560047\" lon=\"103.8172817\"/>\n <node id=\"1717556425\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559812\" lon=\"103.8173695\"/>\n <node id=\"1717556426\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2557258\" lon=\"103.8163798\"/>\n <node id=\"1717556427\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554235\" lon=\"103.8173353\"/>\n <node id=\"1717556428\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556434\" lon=\"103.8164408\"/>\n <node id=\"1717556430\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:35Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550749\" lon=\"103.8177001\"/>\n <node id=\"1717556431\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553669\" lon=\"103.8170333\"/>\n <node id=\"1717556432\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:35Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552949\" lon=\"103.8174715\"/>\n <node id=\"1717556433\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554181\" lon=\"103.8166219\"/>\n <node id=\"1717556434\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555070\" lon=\"103.8172833\"/>\n <node id=\"1717556435\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:35Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2553667\" lon=\"103.8174787\"/>\n <node id=\"1717556436\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556345\" lon=\"103.8163268\"/>\n <node id=\"1717556437\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:51Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553758\" lon=\"103.8168948\"/>\n <node id=\"1717556438\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:52Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2558673\" lon=\"103.8175848\"/>\n <node id=\"1717556439\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:52Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553166\" lon=\"103.8170328\"/>\n <node id=\"1717556440\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:52Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553336\" lon=\"103.8166728\"/>\n <node id=\"1717556441\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:35Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559434\" lon=\"103.8173479\"/>\n <node id=\"1717556442\" visible=\"true\" version=\"8\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:42Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552840\" lon=\"103.8168763\"/>\n <node id=\"1717556443\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:52Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554288\" lon=\"103.8163349\"/>\n <node id=\"1717556444\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:52Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555433\" lon=\"103.8165823\"/>\n <node id=\"1717556445\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:52Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554227\" lon=\"103.8166319\"/>\n <node id=\"1717556446\" visible=\"true\" version=\"8\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:42Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552994\" lon=\"103.8169974\"/>\n <node id=\"1717556447\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558209\" lon=\"103.8175033\"/>\n <node id=\"1717556448\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551855\" lon=\"103.8177968\"/>\n <node id=\"1717556449\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558132\" lon=\"103.8174380\"/>\n <node id=\"1717556450\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559014\" lon=\"103.8175227\"/>\n <node id=\"1717556451\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559633\" lon=\"103.8171961\"/>\n <node id=\"1717556452\" visible=\"true\" version=\"2\" changeset=\"46830546\" timestamp=\"2017-03-14T05:46:56Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2553886\" lon=\"103.8171275\"/>\n <node id=\"1717556453\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557169\" lon=\"103.8178091\"/>\n <node id=\"1717556454\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557531\" lon=\"103.8177462\"/>\n <node id=\"1717556455\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552755\" lon=\"103.8175930\"/>\n <node id=\"1717556456\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553157\" lon=\"103.8171245\"/>\n <node id=\"1717556457\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557082\" lon=\"103.8173080\"/>\n <node id=\"1717556458\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:50Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2555488\" lon=\"103.8161391\"/>\n <node id=\"1717556459\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559725\" lon=\"103.8172264\"/>\n <node id=\"1717556461\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555146\" lon=\"103.8161753\"/>\n <node id=\"1717556462\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553135\" lon=\"103.8168049\"/>\n <node id=\"1717556463\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556472\" lon=\"103.8169156\"/>\n <node id=\"1717556464\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559744\" lon=\"103.8171563\"/>\n <node id=\"1717556466\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2556973\" lon=\"103.8172189\"/>\n <node id=\"1717556468\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554054\" lon=\"103.8164335\"/>\n <node id=\"1717556472\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555334\" lon=\"103.8164294\"/>\n <node id=\"1717556474\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557751\" lon=\"103.8172602\"/>\n <node id=\"1717556476\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554422\" lon=\"103.8164053\"/>\n <node id=\"1717556481\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554516\" lon=\"103.8164187\"/>\n <node id=\"1717556482\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553873\" lon=\"103.8172073\"/>\n <node id=\"1717556484\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555071\" lon=\"103.8164388\"/>\n <node id=\"1717556486\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554917\" lon=\"103.8163369\"/>\n <node id=\"1717556488\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556326\" lon=\"103.8167312\"/>\n <node id=\"1717556491\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555978\" lon=\"103.8164348\"/>\n <node id=\"1717556493\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554677\" lon=\"103.8174198\"/>\n <node id=\"1717556495\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553127\" lon=\"103.8166795\"/>\n <node id=\"1717556497\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2558941\" lon=\"103.8171402\"/>\n <node id=\"1717556499\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559410\" lon=\"103.8175063\"/>\n <node id=\"1717556503\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2558049\" lon=\"103.8175707\"/>\n <node id=\"1717556505\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:55Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555354\" lon=\"103.8167070\"/>\n <node id=\"1717556509\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558853\" lon=\"103.8171773\"/>\n <node id=\"1717556512\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557345\" lon=\"103.8176595\"/>\n <node id=\"1717556514\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2553593\" lon=\"103.8175531\"/>\n <node id=\"1717556516\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:55Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555401\" lon=\"103.8165736\"/>\n <node id=\"1717556518\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:55Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2552544\" lon=\"103.8164757\"/>\n <node id=\"1717556519\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:55Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553356\" lon=\"103.8169116\"/>\n <node id=\"1717556520\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:36Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558684\" lon=\"103.8171882\"/>\n <node id=\"1717556521\" visible=\"true\" version=\"8\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:42Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553472\" lon=\"103.8169621\"/>\n <node id=\"1717556527\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558515\" lon=\"103.8172785\"/>\n <node id=\"1717556529\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:55Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555824\" lon=\"103.8164878\"/>\n <node id=\"1717556532\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:55Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554335\" lon=\"103.8162437\"/>\n <node id=\"1717556535\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559404\" lon=\"103.8173600\"/>\n <node id=\"1717556538\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:55Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553457\" lon=\"103.8169753\"/>\n <node id=\"1717556540\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555440\" lon=\"103.8173541\"/>\n <node id=\"1717556542\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557616\" lon=\"103.8176623\"/>\n <node id=\"1717556543\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:56Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556479\" lon=\"103.8164616\"/>\n <node id=\"1717556545\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559573\" lon=\"103.8172898\"/>\n <node id=\"1717556547\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:56Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555394\" lon=\"103.8164516\"/>\n <node id=\"1717556560\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:56Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554684\" lon=\"103.8162665\"/>\n <node id=\"1717556561\" visible=\"true\" version=\"8\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:42Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552361\" lon=\"103.8169116\"/>\n <node id=\"1717556564\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2556238\" lon=\"103.8174653\"/>\n <node id=\"1717556566\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:56Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556279\" lon=\"103.8162779\"/>\n <node id=\"1717556568\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558826\" lon=\"103.8175182\"/>\n <node id=\"1717556570\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558718\" lon=\"103.8171741\"/>\n <node id=\"1717556573\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:56Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556514\" lon=\"103.8162886\"/>\n <node id=\"1717556577\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2556841\" lon=\"103.8173947\"/>\n <node id=\"1717556580\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559616\" lon=\"103.8172721\"/>\n <node id=\"1717556591\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555795\" lon=\"103.8168096\"/>\n <node id=\"1717556592\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555601\" lon=\"103.8172488\"/>\n <node id=\"1717556594\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556011\" lon=\"103.8171657\"/>\n <node id=\"1717556596\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554228\" lon=\"103.8164261\"/>\n <node id=\"1717556599\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559725\" lon=\"103.8174138\"/>\n <node id=\"1717556603\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554932\" lon=\"103.8163926\"/>\n <node id=\"1717556606\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559065\" lon=\"103.8175014\"/>\n <node id=\"1717556610\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558058\" lon=\"103.8174688\"/>\n <node id=\"1717556613\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553664\" lon=\"103.8167560\"/>\n <node id=\"1717556617\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554629\" lon=\"103.8167385\"/>\n <node id=\"1717556626\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559806\" lon=\"103.8173555\"/>\n <node id=\"1717556628\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551355\" lon=\"103.8174337\"/>\n <node id=\"1717556630\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550970\" lon=\"103.8177880\"/>\n <node id=\"1717556633\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556023\" lon=\"103.8165541\"/>\n <node id=\"1717556634\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555327\" lon=\"103.8161854\"/>\n <node id=\"1717556635\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553658\" lon=\"103.8162973\"/>\n <node id=\"1717556636\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555358\" lon=\"103.8173834\"/>\n <node id=\"1717556637\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559417\" lon=\"103.8174078\"/>\n <node id=\"1717556638\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:37Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552624\" lon=\"103.8177609\"/>\n <node id=\"1717556639\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555495\" lon=\"103.8163704\"/>\n <node id=\"1717556650\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2555079\" lon=\"103.8172851\"/>\n <node id=\"1717556651\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550839\" lon=\"103.8176093\"/>\n <node id=\"1717556652\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555898\" lon=\"103.8171891\"/>\n <node id=\"1717556653\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555210\" lon=\"103.8174367\"/>\n <node id=\"1717556654\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:59Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2552948\" lon=\"103.8168103\"/>\n <node id=\"1717556655\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557965\" lon=\"103.8171709\"/>\n <node id=\"1717556656\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:59Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554020\" lon=\"103.8164154\"/>\n <node id=\"1717556660\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2556751\" lon=\"103.8172988\"/>\n <node id=\"1717556663\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:59Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556025\" lon=\"103.8171281\"/>\n <node id=\"1717556669\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552110\" lon=\"103.8175384\"/>\n <node id=\"1717556670\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:59Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556385\" lon=\"103.8163349\"/>\n <node id=\"1717556671\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559789\" lon=\"103.8171998\"/>\n <node id=\"1717556672\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:59Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2557882\" lon=\"103.8174312\"/>\n <node id=\"1717556673\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:59Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553457\" lon=\"103.8166963\"/>\n <node id=\"1717556674\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557238\" lon=\"103.8177431\"/>\n <node id=\"1717556675\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:11:59Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559397\" lon=\"103.8174178\"/>\n <node id=\"1717556676\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:12:00Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559591\" lon=\"103.8174239\"/>\n <node id=\"1717556677\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:12:00Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2559940\" lon=\"103.8172985\"/>\n <node id=\"1717556678\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551178\" lon=\"103.8176126\"/>\n <node id=\"1717556679\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552805\" lon=\"103.8175453\"/>\n <node id=\"1717556680\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:12:00Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553007\" lon=\"103.8165669\"/>\n <node id=\"1717556681\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:12:00Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2560168\" lon=\"103.8172368\"/>\n <node id=\"1717556682\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557367\" lon=\"103.8176415\"/>\n <node id=\"1717556683\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2556463\" lon=\"103.8173842\"/>\n <node id=\"1717556684\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552282\" lon=\"103.8178365\"/>\n <node id=\"1717556685\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:12:00Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2557808\" lon=\"103.8174621\"/>\n <node id=\"1717556686\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551054\" lon=\"103.8177031\"/>\n <node id=\"1717556687\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552970\" lon=\"103.8174497\"/>\n <node id=\"1717556688\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:12:00Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2553055\" lon=\"103.8167848\"/>\n <node id=\"1717556689\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550922\" lon=\"103.8178342\"/>\n <node id=\"1717556691\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:12:01Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2556655\" lon=\"103.8161887\"/>\n <node id=\"1717556692\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:12:01Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554066\" lon=\"103.8163375\"/>\n <node id=\"1717564065\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2523559\" lon=\"103.8173261\"/>\n <node id=\"1717564066\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2519046\" lon=\"103.8172870\"/>\n <node id=\"1717564070\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2520735\" lon=\"103.8168847\"/>\n <node id=\"1717564072\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:53Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2525608\" lon=\"103.8170215\"/>\n <node id=\"1717564086\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:54Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2520977\" lon=\"103.8178637\"/>\n <node id=\"1717564091\" visible=\"true\" version=\"2\" changeset=\"53047171\" timestamp=\"2017-10-18T17:57:46Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2538568\" lon=\"103.8150233\"/>\n <node id=\"1717564095\" visible=\"true\" version=\"3\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:54Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552041\" lon=\"103.8187534\"/>\n <node id=\"1717564113\" visible=\"true\" version=\"2\" changeset=\"12786976\" timestamp=\"2012-08-19T18:22:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2521122\" lon=\"103.8171665\"/>\n <node id=\"1717564124\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2525911\" lon=\"103.8168257\"/>\n <node id=\"1717564125\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2522524\" lon=\"103.8179630\"/>\n <node id=\"1717564126\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:57Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2520548\" lon=\"103.8172844\"/>\n <node id=\"1717564129\" visible=\"true\" version=\"2\" changeset=\"53047171\" timestamp=\"2017-10-18T17:57:46Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2528230\" lon=\"103.8159292\"/>\n <node id=\"1717564136\" visible=\"true\" version=\"2\" changeset=\"53047171\" timestamp=\"2017-10-18T17:57:46Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2526078\" lon=\"103.8162155\"/>\n <node id=\"1717564147\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2519314\" lon=\"103.8170537\"/>\n <node id=\"1717564152\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:22:58Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2522076\" lon=\"103.8165789\"/>\n <node id=\"1717566343\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549000\" lon=\"103.8186104\"/>\n <node id=\"1717566344\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:26:28Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2547207\" lon=\"103.8186175\"/>\n <node id=\"1717566345\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548368\" lon=\"103.8185325\"/>\n <node id=\"1717566346\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:26:28Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2547695\" lon=\"103.8186760\"/>\n <node id=\"1717566347\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550880\" lon=\"103.8184543\"/>\n <node id=\"1717566348\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:26:28Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2547553\" lon=\"103.8185886\"/>\n <node id=\"1717566349\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550108\" lon=\"103.8185184\"/>\n <node id=\"1717566350\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549438\" lon=\"103.8184377\"/>\n <node id=\"1717566351\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:26:28Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2548041\" lon=\"103.8186472\"/>\n <node id=\"1717566352\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550210\" lon=\"103.8183736\"/>\n <node id=\"1717566353\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549158\" lon=\"103.8184683\"/>\n <node id=\"1717566354\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:25Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549790\" lon=\"103.8185462\"/>\n <node id=\"1739490135\" visible=\"true\" version=\"1\" changeset=\"11491444\" timestamp=\"2012-05-03T18:41:50Z\" user=\"cboothroyd\" uid=\"255802\" lat=\"1.2546697\" lon=\"103.8173603\"/>\n <node id=\"1739490136\" visible=\"true\" version=\"2\" changeset=\"52501454\" timestamp=\"2017-09-30T07:31:12Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2547974\" lon=\"103.8173680\"/>\n <node id=\"1739490140\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2551546\" lon=\"103.8172737\"/>\n <node id=\"1829420417\" visible=\"true\" version=\"4\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2565354\" lon=\"103.8187493\"/>\n <node id=\"1829420421\" visible=\"true\" version=\"4\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2567980\" lon=\"103.8197273\"/>\n <node id=\"1829420424\" visible=\"true\" version=\"6\" changeset=\"69415695\" timestamp=\"2019-04-21T03:50:21Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557616\" lon=\"103.8189672\"/>\n <node id=\"1829420432\" visible=\"true\" version=\"4\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2565683\" lon=\"103.8197890\"/>\n <node id=\"1829420444\" visible=\"true\" version=\"5\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2563692\" lon=\"103.8190475\"/>\n <node id=\"1829606801\" visible=\"true\" version=\"6\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2565433\" lon=\"103.8196956\"/>\n <node id=\"1829631576\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2536597\" lon=\"103.8207042\"/>\n <node id=\"1829631579\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2525146\" lon=\"103.8237244\"/>\n <node id=\"1829639090\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2562219\" lon=\"103.8236278\"/>\n <node id=\"1829639092\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2564606\" lon=\"103.8235849\"/>\n <node id=\"1829639098\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559336\" lon=\"103.8237673\"/>\n <node id=\"1829639101\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2534787\" lon=\"103.8258688\"/>\n <node id=\"1829639103\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2536248\" lon=\"103.8257562\"/>\n <node id=\"1829639105\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:26Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2533660\" lon=\"103.8259144\"/>\n <node id=\"1867272119\" visible=\"true\" version=\"3\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2580072\" lon=\"103.8146377\"/>\n <node id=\"1867272120\" visible=\"true\" version=\"3\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2584389\" lon=\"103.8132470\"/>\n <node id=\"1867272121\" visible=\"true\" version=\"3\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2587010\" lon=\"103.8141449\"/>\n <node id=\"1867526773\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556997\" lon=\"103.8160146\"/>\n <node id=\"1868203191\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2550941\" lon=\"103.8187449\"/>\n <node id=\"1868203196\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551506\" lon=\"103.8185862\"/>\n <node id=\"1868203197\" visible=\"true\" version=\"10\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556530\" lon=\"103.8183860\"/>\n <node id=\"1868203199\" visible=\"true\" version=\"10\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557393\" lon=\"103.8183320\"/>\n <node id=\"1868203201\" visible=\"true\" version=\"10\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560288\" lon=\"103.8179454\"/>\n <node id=\"1868203212\" visible=\"true\" version=\"10\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563238\" lon=\"103.8173145\"/>\n <node id=\"1868203213\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561336\" lon=\"103.8169394\"/>\n <node id=\"1868203214\" visible=\"true\" version=\"10\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562730\" lon=\"103.8171451\"/>\n <node id=\"1868204948\" visible=\"true\" version=\"1\" changeset=\"12746266\" timestamp=\"2012-08-16T04:32:20Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2575731\" lon=\"103.8230422\"/>\n <node id=\"1873234299\" visible=\"true\" version=\"3\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:14Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2541076\" lon=\"103.8192021\"/>\n <node id=\"1873234418\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:47Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2543811\" lon=\"103.8149459\"/>\n <node id=\"1873234432\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544089\" lon=\"103.8153042\"/>\n <node id=\"1873234443\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544699\" lon=\"103.8149823\"/>\n <node id=\"1873234455\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544843\" lon=\"103.8151726\"/>\n <node id=\"1873234456\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544868\" lon=\"103.8152068\"/>\n <node id=\"1873234457\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544937\" lon=\"103.8152978\"/>\n <node id=\"1873234463\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545172\" lon=\"103.8151231\"/>\n <node id=\"1873234464\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545205\" lon=\"103.8151702\"/>\n <node id=\"1873234468\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545229\" lon=\"103.8152044\"/>\n <node id=\"1873234470\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:48Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545248\" lon=\"103.8152317\"/>\n <node id=\"1873234500\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:49Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545566\" lon=\"103.8151203\"/>\n <node id=\"1873234506\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:49Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545642\" lon=\"103.8152289\"/>\n <node id=\"1873234732\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:53Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548706\" lon=\"103.8145008\"/>\n <node id=\"1873234777\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:54Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549200\" lon=\"103.8143029\"/>\n <node id=\"1873234794\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549023\" lon=\"103.8176193\"/>\n <node id=\"1873234799\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549132\" lon=\"103.8175784\"/>\n <node id=\"1873234803\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549236\" lon=\"103.8176560\"/>\n <node id=\"1873234814\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:55Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549783\" lon=\"103.8143713\"/>\n <node id=\"1873234821\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549498\" lon=\"103.8175571\"/>\n <node id=\"1873234822\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:55Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549880\" lon=\"103.8143596\"/>\n <node id=\"1873234828\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549645\" lon=\"103.8176668\"/>\n <node id=\"1873234835\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:56Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550161\" lon=\"103.8146302\"/>\n <node id=\"1873234836\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549908\" lon=\"103.8175680\"/>\n <node id=\"1873234841\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550011\" lon=\"103.8176456\"/>\n <node id=\"1873234844\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550120\" lon=\"103.8176047\"/>\n <node id=\"1873234859\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:57Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551279\" lon=\"103.8145045\"/>\n <node id=\"1873234866\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:57Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551883\" lon=\"103.8178444\"/>\n <node id=\"1873234879\" visible=\"true\" version=\"1\" changeset=\"12786976\" timestamp=\"2012-08-19T18:20:57Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552524\" lon=\"103.8178034\"/>\n <node id=\"1873234962\" visible=\"true\" version=\"5\" changeset=\"69415695\" timestamp=\"2019-04-21T03:50:21Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557187\" lon=\"103.8189780\"/>\n <node id=\"2103671644\" visible=\"true\" version=\"1\" changeset=\"14593518\" timestamp=\"2013-01-10T03:09:27Z\" user=\"alexrudd\" uid=\"5611\" lat=\"1.2542217\" lon=\"103.8170064\"/>\n <node id=\"2248167729\" visible=\"true\" version=\"1\" changeset=\"15606850\" timestamp=\"2013-04-04T11:56:59Z\" user=\"Ulmon Community\" uid=\"1313848\" lat=\"1.2555712\" lon=\"103.8177397\">\n  <tag k=\"name\" v=\"Southern Ridges\"/><tag k=\"tourism\" v=\"viewpoint\"/>\n </node>\n <node id=\"2362722961\" visible=\"true\" version=\"3\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545295\" lon=\"103.8178903\"/>\n <node id=\"2362722963\" visible=\"true\" version=\"3\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544755\" lon=\"103.8179654\"/>\n <node id=\"2362722964\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543989\" lon=\"103.8180173\"/>\n <node id=\"2362722966\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549602\" lon=\"103.8177230\"/>\n <node id=\"2362722967\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543091\" lon=\"103.8180396\"/>\n <node id=\"2362722968\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2548562\" lon=\"103.8176669\"/>\n <node id=\"2362722969\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2542172\" lon=\"103.8180295\"/>\n <node id=\"2362722970\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545466\" lon=\"103.8177089\"/>\n <node id=\"2362722971\" visible=\"true\" version=\"3\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545542\" lon=\"103.8178011\"/>\n <node id=\"2558083041\" visible=\"true\" version=\"1\" changeset=\"19189134\" timestamp=\"2013-11-30T04:04:29Z\" user=\"khatulistiwa\" uid=\"94431\" lat=\"1.2550414\" lon=\"103.8188556\"/>\n <node id=\"2558083044\" visible=\"true\" version=\"1\" changeset=\"19189134\" timestamp=\"2013-11-30T04:04:29Z\" user=\"khatulistiwa\" uid=\"94431\" lat=\"1.2552442\" lon=\"103.8189425\"/>\n <node id=\"2558083045\" visible=\"true\" version=\"1\" changeset=\"19189134\" timestamp=\"2013-11-30T04:04:29Z\" user=\"khatulistiwa\" uid=\"94431\" lat=\"1.2553350\" lon=\"103.8188441\"/>\n <node id=\"2558083046\" visible=\"true\" version=\"3\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\" lat=\"1.2553881\" lon=\"103.8187170\"/>\n <node id=\"2558083048\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554232\" lon=\"103.8187864\"/>\n <node id=\"2558083049\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:51Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556102\" lon=\"103.8187361\"/>\n <node id=\"2558083050\" visible=\"true\" version=\"3\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\" lat=\"1.2557658\" lon=\"103.8186065\"/>\n <node id=\"2558083051\" visible=\"true\" version=\"3\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\" lat=\"1.2560674\" lon=\"103.8185183\"/>\n <node id=\"2558083052\" visible=\"true\" version=\"3\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\" lat=\"1.2563658\" lon=\"103.8184311\"/>\n <node id=\"2558083053\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:52Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2565883\" lon=\"103.8184816\"/>\n <node id=\"2558083054\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:52Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2566936\" lon=\"103.8183352\"/>\n <node id=\"2558083058\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:52Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2567434\" lon=\"103.8184641\"/>\n <node id=\"2558468523\" visible=\"true\" version=\"1\" changeset=\"19195140\" timestamp=\"2013-11-30T14:21:07Z\" user=\"khatulistiwa\" uid=\"94431\" lat=\"1.2552120\" lon=\"103.8189822\"/>\n <node id=\"2558468524\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557991\" lon=\"103.8188466\"/>\n <node id=\"2558468525\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558972\" lon=\"103.8186614\"/>\n <node id=\"2558468526\" visible=\"true\" version=\"2\" changeset=\"69405864\" timestamp=\"2019-04-20T16:09:03Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2566282\" lon=\"103.8183197\"/>\n <node id=\"2558468527\" visible=\"true\" version=\"2\" changeset=\"69405864\" timestamp=\"2019-04-20T16:09:03Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2567019\" lon=\"103.8182446\"/>\n <node id=\"2663348085\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2548422\" lon=\"103.8172529\"/>\n <node id=\"2663348086\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2548395\" lon=\"103.8172087\"/>\n <node id=\"2663348087\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2547027\" lon=\"103.8171269\"/>\n <node id=\"2663348088\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2546773\" lon=\"103.8172637\"/>\n <node id=\"2663348089\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2547470\" lon=\"103.8172409\"/>\n <node id=\"2663348090\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2548100\" lon=\"103.8171470\"/>\n <node id=\"2663348091\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2548596\" lon=\"103.8171658\"/>\n <node id=\"2663348092\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2547430\" lon=\"103.8171322\"/>\n <node id=\"2663348093\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2548757\" lon=\"103.8172167\"/>\n <node id=\"2663348094\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2547389\" lon=\"103.8172878\"/>\n <node id=\"2663348095\" visible=\"true\" version=\"2\" changeset=\"54546640\" timestamp=\"2017-12-11T17:57:30Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2547014\" lon=\"103.8172851\"/>\n <node id=\"2663353454\" visible=\"true\" version=\"1\" changeset=\"20467454\" timestamp=\"2014-02-09T16:05:20Z\" user=\"andi9876\" uid=\"1256497\" lat=\"1.2531882\" lon=\"103.8159215\"/>\n <node id=\"2663373909\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560020\" lon=\"103.8161264\"/>\n <node id=\"2663373914\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558092\" lon=\"103.8160176\"/>\n <node id=\"2663373916\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559571\" lon=\"103.8160674\"/>\n <node id=\"3686388539\" visible=\"true\" version=\"9\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:08Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2550487\" lon=\"103.8167759\">\n  <tag k=\"aerialway\" v=\"station\"/><tag k=\"name\" v=\"Imbiah Lookout\"/><tag k=\"wikidata\" v=\"Q6003536\"/>\n </node>\n <node id=\"4217410090\" visible=\"true\" version=\"2\" changeset=\"39697306\" timestamp=\"2016-05-31T20:16:34Z\" user=\"rene78\" uid=\"257555\" lat=\"1.2554744\" lon=\"103.8169904\">\n  <tag k=\"name\" v=\"Butterfly Park &amp; Insect Kingdom\"/><tag k=\"name:ru\" v=\"Парк бабочек и царство насекомых\"/><tag k=\"tourism\" v=\"zoo\"/><tag k=\"website\" v=\"http://www.jungle.com.sg/\"/>\n </node>\n <node id=\"4230810140\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2567910\" lon=\"103.8160090\"/>\n <node id=\"4230810141\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2580078\" lon=\"103.8154840\"/>\n <node id=\"4230810142\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2581258\" lon=\"103.8151816\"/>\n <node id=\"4230810143\" visible=\"true\" version=\"2\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2581640\" lon=\"103.8143655\"/>\n <node id=\"4230810144\" visible=\"true\" version=\"2\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:16Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2586548\" lon=\"103.8137982\"/>\n <node id=\"4279963872\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:54Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559298\" lon=\"103.8171479\"/>\n <node id=\"4279963873\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558247\" lon=\"103.8165046\"/>\n <node id=\"4279963874\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556903\" lon=\"103.8161419\"/>\n <node id=\"4279963875\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2555597\" lon=\"103.8157440\"/>\n <node id=\"4485958594\" visible=\"true\" version=\"1\" changeset=\"43450576\" timestamp=\"2016-11-06T20:39:21Z\" user=\"gps-newcomer\" uid=\"94213\" lat=\"1.2555099\" lon=\"103.8174809\">\n  <tag k=\"amenity\" v=\"toilets\"/><tag k=\"fee\" v=\"no\"/>\n </node>\n <node id=\"4566570118\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550341\" lon=\"103.8176899\"/>\n <node id=\"4566570119\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550665\" lon=\"103.8175990\"/>\n <node id=\"4566570120\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550171\" lon=\"103.8175161\"/>\n <node id=\"4566570121\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549711\" lon=\"103.8174982\"/>\n <node id=\"4566570122\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2548903\" lon=\"103.8175165\"/>\n <node id=\"4566570123\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2548436\" lon=\"103.8175850\"/>\n <node id=\"4566570124\" visible=\"true\" version=\"1\" changeset=\"44586417\" timestamp=\"2016-12-22T09:34:20Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2550877\" lon=\"103.8174563\"/>\n <node id=\"4566570125\" visible=\"true\" version=\"2\" changeset=\"52501454\" timestamp=\"2017-09-30T07:31:12Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2550724\" lon=\"103.8173982\"/>\n <node id=\"4568857131\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558354\" lon=\"103.8165649\"/>\n <node id=\"4568857132\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2555951\" lon=\"103.8166206\"/>\n <node id=\"4568857133\" visible=\"true\" version=\"4\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558069\" lon=\"103.8175439\"/>\n <node id=\"4613238712\" visible=\"true\" version=\"1\" changeset=\"45231662\" timestamp=\"2017-01-17T01:32:21Z\" user=\"Canyonsrcool\" uid=\"126508\" lat=\"1.2552278\" lon=\"103.8175128\">\n  <tag k=\"amenity\" v=\"fast_food\"/><tag k=\"cuisine\" v=\"sandwich\"/><tag k=\"name\" v=\"Subway\"/>\n </node>\n <node id=\"4613238713\" visible=\"true\" version=\"1\" changeset=\"45231662\" timestamp=\"2017-01-17T01:32:21Z\" user=\"Canyonsrcool\" uid=\"126508\" lat=\"1.2551627\" lon=\"103.8175716\">\n  <tag k=\"amenity\" v=\"cafe\"/><tag k=\"name\" v=\"Starbucks\"/>\n </node>\n <node id=\"4715825892\" visible=\"true\" version=\"1\" changeset=\"46561180\" timestamp=\"2017-03-03T23:01:40Z\" user=\"Te-Ika\" uid=\"3742941\" lat=\"1.2551122\" lon=\"103.8173239\"/>\n <node id=\"4715825893\" visible=\"true\" version=\"1\" changeset=\"46561180\" timestamp=\"2017-03-03T23:01:40Z\" user=\"Te-Ika\" uid=\"3742941\" lat=\"1.2550335\" lon=\"103.8173117\"/>\n <node id=\"4715825894\" visible=\"true\" version=\"1\" changeset=\"46561180\" timestamp=\"2017-03-03T23:01:40Z\" user=\"Te-Ika\" uid=\"3742941\" lat=\"1.2550217\" lon=\"103.8173401\"/>\n <node id=\"4715825895\" visible=\"true\" version=\"1\" changeset=\"46561180\" timestamp=\"2017-03-03T23:01:40Z\" user=\"Te-Ika\" uid=\"3742941\" lat=\"1.2550134\" lon=\"103.8173354\"/>\n <node id=\"4715825896\" visible=\"true\" version=\"1\" changeset=\"46561180\" timestamp=\"2017-03-03T23:01:40Z\" user=\"Te-Ika\" uid=\"3742941\" lat=\"1.2549890\" lon=\"103.8173861\"/>\n <node id=\"4715825897\" visible=\"true\" version=\"1\" changeset=\"46561180\" timestamp=\"2017-03-03T23:01:40Z\" user=\"Te-Ika\" uid=\"3742941\" lat=\"1.2550903\" lon=\"103.8173616\"/>\n <node id=\"4731770684\" visible=\"true\" version=\"2\" changeset=\"69405864\" timestamp=\"2019-04-20T16:09:05Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548930\" lon=\"103.8168369\"/>\n <node id=\"4731770685\" visible=\"true\" version=\"2\" changeset=\"69405864\" timestamp=\"2019-04-20T16:09:05Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550350\" lon=\"103.8170579\"/>\n <node id=\"4731770686\" visible=\"true\" version=\"2\" changeset=\"69405864\" timestamp=\"2019-04-20T16:09:05Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551760\" lon=\"103.8167019\"/>\n <node id=\"4731770687\" visible=\"true\" version=\"2\" changeset=\"69405864\" timestamp=\"2019-04-20T16:09:05Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550410\" lon=\"103.8164779\"/>\n <node id=\"4731913768\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543716\" lon=\"103.8175247\"/>\n <node id=\"4731913769\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544315\" lon=\"103.8175525\"/>\n <node id=\"4731913770\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544827\" lon=\"103.8175944\"/>\n <node id=\"4731913771\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545219\" lon=\"103.8176476\"/>\n <node id=\"4731913772\" visible=\"true\" version=\"1\" changeset=\"46806385\" timestamp=\"2017-03-13T09:21:28Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2550794\" lon=\"103.8176549\"/>\n <node id=\"4731913775\" visible=\"true\" version=\"3\" changeset=\"47118033\" timestamp=\"2017-03-24T08:20:01Z\" user=\"zhangosm\" uid=\"5541546\" lat=\"1.2552006\" lon=\"103.8165879\"/>\n <node id=\"4731913776\" visible=\"true\" version=\"2\" changeset=\"46831836\" timestamp=\"2017-03-14T07:21:31Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552127\" lon=\"103.8167720\"/>\n <node id=\"4731913777\" visible=\"true\" version=\"2\" changeset=\"46831836\" timestamp=\"2017-03-14T07:21:31Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552663\" lon=\"103.8171703\"/>\n <node id=\"4731913778\" visible=\"true\" version=\"1\" changeset=\"46806385\" timestamp=\"2017-03-13T09:21:28Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2551596\" lon=\"103.8173956\"/>\n <node id=\"4731913779\" visible=\"true\" version=\"2\" changeset=\"52243602\" timestamp=\"2017-09-21T13:11:26Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2550640\" lon=\"103.8173680\">\n  <tag k=\"entrance\" v=\"yes\"/>\n </node>\n <node id=\"4731977864\" visible=\"true\" version=\"2\" changeset=\"54417250\" timestamp=\"2017-12-06T23:38:53Z\" user=\"FlyTy\" uid=\"6095805\" lat=\"1.2535375\" lon=\"103.8173137\"/>\n <node id=\"4731977865\" visible=\"true\" version=\"1\" changeset=\"46807810\" timestamp=\"2017-03-13T10:07:47Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2536500\" lon=\"103.8173683\"/>\n <node id=\"4731977866\" visible=\"true\" version=\"1\" changeset=\"46807810\" timestamp=\"2017-03-13T10:07:47Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2537653\" lon=\"103.8172369\"/>\n <node id=\"4731977867\" visible=\"true\" version=\"1\" changeset=\"46807810\" timestamp=\"2017-03-13T10:07:47Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2543526\" lon=\"103.8174381\"/>\n <node id=\"4731977868\" visible=\"true\" version=\"2\" changeset=\"54417250\" timestamp=\"2017-12-06T23:38:53Z\" user=\"FlyTy\" uid=\"6095805\" lat=\"1.2536168\" lon=\"103.8168848\"/>\n <node id=\"4733074344\" visible=\"true\" version=\"1\" changeset=\"46826714\" timestamp=\"2017-03-13T23:38:46Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2542560\" lon=\"103.8179027\"/>\n <node id=\"4733074345\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543494\" lon=\"103.8175973\"/>\n <node id=\"4733074353\" visible=\"true\" version=\"1\" changeset=\"46826714\" timestamp=\"2017-03-13T23:38:46Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2542347\" lon=\"103.8179725\"/>\n <node id=\"4733403527\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544295\" lon=\"103.8176461\"/>\n <node id=\"4733403528\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544761\" lon=\"103.8177277\"/>\n <node id=\"4733403529\" visible=\"true\" version=\"3\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544760\" lon=\"103.8178269\"/>\n <node id=\"4733403530\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544255\" lon=\"103.8179122\"/>\n <node id=\"4733403531\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543386\" lon=\"103.8179600\"/>\n <node id=\"4733403532\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2542395\" lon=\"103.8179569\"/>\n <node id=\"4733403533\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543270\" lon=\"103.8176708\"/>\n <node id=\"4733403534\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543748\" lon=\"103.8176974\"/>\n <node id=\"4733403535\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544049\" lon=\"103.8178033\"/>\n <node id=\"4733403536\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543750\" lon=\"103.8178545\"/>\n <node id=\"4733403537\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543226\" lon=\"103.8178824\"/>\n <node id=\"4733403538\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2542634\" lon=\"103.8178787\"/>\n <node id=\"4733403539\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544035\" lon=\"103.8177439\"/>\n <node id=\"4733403769\" visible=\"true\" version=\"1\" changeset=\"46830399\" timestamp=\"2017-03-14T05:34:30Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2542972\" lon=\"103.8177678\"/>\n <node id=\"4733410886\" visible=\"true\" version=\"1\" changeset=\"46830546\" timestamp=\"2017-03-14T05:46:56Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2553160\" lon=\"103.8170979\"/>\n <node id=\"4733410887\" visible=\"true\" version=\"2\" changeset=\"46831836\" timestamp=\"2017-03-14T07:21:31Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552998\" lon=\"103.8170992\"/>\n <node id=\"4733474809\" visible=\"true\" version=\"1\" changeset=\"46831836\" timestamp=\"2017-03-14T07:21:31Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552113\" lon=\"103.8172763\"/>\n <node id=\"4733474810\" visible=\"true\" version=\"1\" changeset=\"46831836\" timestamp=\"2017-03-14T07:21:31Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552958\" lon=\"103.8170416\"/>\n <node id=\"4733474811\" visible=\"true\" version=\"1\" changeset=\"46831836\" timestamp=\"2017-03-14T07:21:31Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552740\" lon=\"103.8169958\"/>\n <node id=\"4733474812\" visible=\"true\" version=\"2\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:43Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552359\" lon=\"103.8169194\"/>\n <node id=\"4733474813\" visible=\"true\" version=\"1\" changeset=\"46831836\" timestamp=\"2017-03-14T07:21:31Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552288\" lon=\"103.8168632\"/>\n <node id=\"4733474814\" visible=\"true\" version=\"1\" changeset=\"46831836\" timestamp=\"2017-03-14T07:21:31Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2551979\" lon=\"103.8166889\"/>\n <node id=\"4733474815\" visible=\"true\" version=\"2\" changeset=\"47118033\" timestamp=\"2017-03-24T08:20:01Z\" user=\"zhangosm\" uid=\"5541546\" lat=\"1.2552264\" lon=\"103.8164529\"/>\n <node id=\"4733474816\" visible=\"true\" version=\"3\" changeset=\"52496545\" timestamp=\"2017-09-29T23:26:08Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552734\" lon=\"103.8163156\"/>\n <node id=\"4736068641\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2542012\" lon=\"103.8180387\"/>\n <node id=\"4736068642\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543692\" lon=\"103.8174987\"/>\n <node id=\"4736068643\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544371\" lon=\"103.8175287\"/>\n <node id=\"4736068644\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544948\" lon=\"103.8175754\"/>\n <node id=\"4736068645\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545383\" lon=\"103.8176355\"/>\n <node id=\"4736068646\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545648\" lon=\"103.8177049\"/>\n <node id=\"4736068647\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545706\" lon=\"103.8178021\"/>\n <node id=\"4736068648\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545431\" lon=\"103.8178955\"/>\n <node id=\"4736068649\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544855\" lon=\"103.8179740\"/>\n <node id=\"4736068650\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544048\" lon=\"103.8180283\"/>\n <node id=\"4736068651\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543104\" lon=\"103.8180521\"/>\n <node id=\"4736068652\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2542135\" lon=\"103.8180424\"/>\n <node id=\"4752207927\" visible=\"true\" version=\"2\" changeset=\"53115984\" timestamp=\"2017-10-20T23:15:22Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2539855\" lon=\"103.8192619\"/>\n <node id=\"4752282585\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554177\" lon=\"103.8161207\"/>\n <node id=\"4752282588\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552008\" lon=\"103.8161773\"/>\n <node id=\"4752282589\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552681\" lon=\"103.8162152\"/>\n <node id=\"4752282590\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552932\" lon=\"103.8162105\"/>\n <node id=\"4752282591\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553153\" lon=\"103.8161978\"/>\n <node id=\"4752282593\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553320\" lon=\"103.8161785\"/>\n <node id=\"4752282597\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552864\" lon=\"103.8160653\"/>\n <node id=\"4752282598\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552641\" lon=\"103.8160628\"/>\n <node id=\"4752282599\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552421\" lon=\"103.8160668\"/>\n <node id=\"4752282600\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552222\" lon=\"103.8160771\"/>\n <node id=\"4752282601\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552062\" lon=\"103.8160927\"/>\n <node id=\"4752282602\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551953\" lon=\"103.8161123\"/>\n <node id=\"4752282603\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551907\" lon=\"103.8161341\"/>\n <node id=\"4752282604\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:27Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551925\" lon=\"103.8161565\"/>\n <node id=\"4752344935\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2566241\" lon=\"103.8159701\"/>\n <node id=\"4752344936\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2567884\" lon=\"103.8159634\"/>\n <node id=\"4752344937\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2569680\" lon=\"103.8159560\"/>\n <node id=\"4752344938\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2572858\" lon=\"103.8158836\"/>\n <node id=\"4752344939\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2574293\" lon=\"103.8158226\"/>\n <node id=\"4752344940\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2577725\" lon=\"103.8156348\"/>\n <node id=\"4752344941\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2578550\" lon=\"103.8155832\"/>\n <node id=\"4752344942\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2579602\" lon=\"103.8154578\"/>\n <node id=\"4752344943\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2580641\" lon=\"103.8152654\"/>\n <node id=\"4752344963\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:31:16Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553069\" lon=\"103.8160742\"/>\n <node id=\"4752344966\" visible=\"true\" version=\"7\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556619\" lon=\"103.8159640\"/>\n <node id=\"4752344967\" visible=\"true\" version=\"7\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558724\" lon=\"103.8159277\"/>\n <node id=\"4752344968\" visible=\"true\" version=\"7\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:55Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561001\" lon=\"103.8159494\"/>\n <node id=\"4893137468\" visible=\"true\" version=\"1\" changeset=\"49211674\" timestamp=\"2017-06-03T03:54:22Z\" user=\"DAJIBA\" uid=\"360397\" lat=\"1.2552108\" lon=\"103.8182922\">\n  <tag k=\"name\" v=\"Former Mini Golf\"/><tag k=\"tourism\" v=\"attraction\"/>\n </node>\n <node id=\"5119642628\" visible=\"true\" version=\"1\" changeset=\"52243602\" timestamp=\"2017-09-21T13:11:25Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2549636\" lon=\"103.8173111\"/>\n <node id=\"5119642629\" visible=\"true\" version=\"1\" changeset=\"52243602\" timestamp=\"2017-09-21T13:11:25Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2549087\" lon=\"103.8172896\"/>\n <node id=\"5119743800\" visible=\"true\" version=\"2\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:35Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2559564\" lon=\"103.8140014\"/>\n <node id=\"5119743801\" visible=\"true\" version=\"2\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:35Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2558706\" lon=\"103.8142160\"/>\n <node id=\"5119743802\" visible=\"true\" version=\"2\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:35Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2555488\" lon=\"103.8146183\"/>\n <node id=\"5119743803\" visible=\"true\" version=\"2\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:35Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2553168\" lon=\"103.8149541\"/>\n <node id=\"5119743804\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2555443\" lon=\"103.8156270\"/>\n <node id=\"5119743805\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559040\" lon=\"103.8152760\"/>\n <node id=\"5119743806\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558264\" lon=\"103.8153197\"/>\n <node id=\"5119743807\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559119\" lon=\"103.8149495\"/>\n <node id=\"5119743808\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559355\" lon=\"103.8149113\"/>\n <node id=\"5119743809\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2564770\" lon=\"103.8141220\"/>\n <node id=\"5119743810\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2566510\" lon=\"103.8139800\"/>\n <node id=\"5119743811\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2568360\" lon=\"103.8139370\"/>\n <node id=\"5119743812\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2573490\" lon=\"103.8134790\"/>\n <node id=\"5119743813\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2574800\" lon=\"103.8132960\"/>\n <node id=\"5119743814\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:29Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2575829\" lon=\"103.8132746\"/>\n <node id=\"5119743815\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:29Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2577795\" lon=\"103.8133392\"/>\n <node id=\"5119743816\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2571849\" lon=\"103.8152445\"/>\n <node id=\"5119743817\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2573433\" lon=\"103.8152330\"/>\n <node id=\"5119743818\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2574567\" lon=\"103.8151699\"/>\n <node id=\"5119743819\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2575493\" lon=\"103.8149080\"/>\n <node id=\"5119743820\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2575280\" lon=\"103.8147500\"/>\n <node id=\"5119744321\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2575180\" lon=\"103.8146110\"/>\n <node id=\"5119744322\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2575770\" lon=\"103.8143750\"/>\n <node id=\"5119744323\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2575900\" lon=\"103.8142160\"/>\n <node id=\"5119744325\" visible=\"true\" version=\"2\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:35Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2577044\" lon=\"103.8139742\"/>\n <node id=\"5119744326\" visible=\"true\" version=\"2\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:35Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2577764\" lon=\"103.8139282\"/>\n <node id=\"5119744327\" visible=\"true\" version=\"2\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:35Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2578863\" lon=\"103.8138374\"/>\n <node id=\"5119744328\" visible=\"true\" version=\"1\" changeset=\"52244831\" timestamp=\"2017-09-21T14:03:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2579310\" lon=\"103.8137020\"/>\n <node id=\"5119744329\" visible=\"true\" version=\"2\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:35Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2578569\" lon=\"103.8134666\"/>\n <node id=\"5119744356\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2571090\" lon=\"103.8152189\"/>\n <node id=\"5119744357\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2568383\" lon=\"103.8154260\"/>\n <node id=\"5119744358\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2565773\" lon=\"103.8155070\"/>\n <node id=\"5119744359\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561883\" lon=\"103.8155700\"/>\n <node id=\"5119744360\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558913\" lon=\"103.8155560\"/>\n <node id=\"5119744361\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557193\" lon=\"103.8156540\"/>\n <node id=\"5119775043\" visible=\"true\" version=\"2\" changeset=\"52685203\" timestamp=\"2017-10-06T14:37:34Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2565927\" lon=\"103.8120234\"/>\n <node id=\"5133972841\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2555783\" lon=\"103.8165643\"/>\n <node id=\"5133972842\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2555981\" lon=\"103.8166450\"/>\n <node id=\"5133972843\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556071\" lon=\"103.8168520\"/>\n <node id=\"5133972844\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556842\" lon=\"103.8171013\"/>\n <node id=\"5133972846\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557524\" lon=\"103.8172180\"/>\n <node id=\"5134024625\" visible=\"true\" version=\"1\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:13Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2543857\" lon=\"103.8190694\"/>\n <node id=\"5134024627\" visible=\"true\" version=\"1\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:13Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550313\" lon=\"103.8186302\"/>\n <node id=\"5134024634\" visible=\"true\" version=\"1\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:13Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549616\" lon=\"103.8186758\"/>\n <node id=\"5134024636\" visible=\"true\" version=\"1\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:13Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2547068\" lon=\"103.8189715\"/>\n <node id=\"5134024639\" visible=\"true\" version=\"1\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:13Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2548362\" lon=\"103.8188213\"/>\n <node id=\"5134024640\" visible=\"true\" version=\"1\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:13Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2546206\" lon=\"103.8190266\"/>\n <node id=\"5134024641\" visible=\"true\" version=\"1\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:13Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2545768\" lon=\"103.8190547\"/>\n <node id=\"5134024644\" visible=\"true\" version=\"1\" changeset=\"52427672\" timestamp=\"2017-09-27T22:44:13Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2544340\" lon=\"103.8190868\"/>\n <node id=\"5134029801\" visible=\"true\" version=\"1\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:30Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2553517\" lon=\"103.8150997\"/>\n <node id=\"5134029805\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559081\" lon=\"103.8150381\"/>\n <node id=\"5134029815\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2574045\" lon=\"103.8152095\"/>\n <node id=\"5134029927\" visible=\"true\" version=\"1\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:30Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2553216\" lon=\"103.8150299\"/>\n <node id=\"5134029936\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2574959\" lon=\"103.8151174\"/>\n <node id=\"5134029942\" visible=\"true\" version=\"1\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:31Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2576283\" lon=\"103.8140861\"/>\n <node id=\"5134029943\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559236\" lon=\"103.8152187\"/>\n <node id=\"5134029949\" visible=\"true\" version=\"1\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:31Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2553380\" lon=\"103.8148810\"/>\n <node id=\"5134029954\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2575189\" lon=\"103.8150560\"/>\n <node id=\"5134029963\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559180\" lon=\"103.8152487\"/>\n <node id=\"5134029979\" visible=\"true\" version=\"1\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:31Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2559564\" lon=\"103.8136259\"/>\n <node id=\"5134029984\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:57Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558828\" lon=\"103.8152980\"/>\n <node id=\"5134030001\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558562\" lon=\"103.8153130\"/>\n <node id=\"5134030005\" visible=\"true\" version=\"1\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:31Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2578377\" lon=\"103.8138905\"/>\n <node id=\"5134030015\" visible=\"true\" version=\"1\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:32Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2570988\" lon=\"103.8137332\"/>\n <node id=\"5134030022\" visible=\"true\" version=\"1\" changeset=\"52427585\" timestamp=\"2017-09-27T22:41:32Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2579182\" lon=\"103.8137728\"/>\n <node id=\"5134030028\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559024\" lon=\"103.8149935\"/>\n <node id=\"5134030041\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2553799\" lon=\"103.8148195\"/>\n <node id=\"5134030051\" visible=\"true\" version=\"3\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:17Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2579200\" lon=\"103.8123632\"/>\n <node id=\"5137340959\" visible=\"true\" version=\"1\" changeset=\"52472107\" timestamp=\"2017-09-29T11:31:10Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552780\" lon=\"103.8171476\"/>\n <node id=\"5137340960\" visible=\"true\" version=\"2\" changeset=\"52496545\" timestamp=\"2017-09-29T23:26:08Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2553364\" lon=\"103.8162287\"/>\n <node id=\"5138638004\" visible=\"true\" version=\"11\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561024\" lon=\"103.8160087\"/>\n <node id=\"5138638008\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561040\" lon=\"103.8166767\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5138638009\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561072\" lon=\"103.8167292\"/>\n <node id=\"5138638010\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562006\" lon=\"103.8168992\"/>\n <node id=\"5138638011\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563315\" lon=\"103.8171142\"/>\n <node id=\"5138638012\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563714\" lon=\"103.8172177\"/>\n <node id=\"5138638013\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563864\" lon=\"103.8173204\"/>\n <node id=\"5138638014\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563581\" lon=\"103.8174425\"/>\n <node id=\"5138638015\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560750\" lon=\"103.8179538\"/>\n <node id=\"5138638016\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559849\" lon=\"103.8181156\"/>\n <node id=\"5138638017\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559018\" lon=\"103.8182311\"/>\n <node id=\"5138638018\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558086\" lon=\"103.8183251\"/>\n <node id=\"5138638019\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556682\" lon=\"103.8184334\"/>\n <node id=\"5138638020\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551858\" lon=\"103.8186349\"/>\n <node id=\"5138650779\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551525\" lon=\"103.8170011\"/>\n <node id=\"5138650780\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551719\" lon=\"103.8169196\"/>\n <node id=\"5138650781\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551710\" lon=\"103.8167364\"/>\n <node id=\"5138650782\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551466\" lon=\"103.8165992\"/>\n <node id=\"5138650783\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551273\" lon=\"103.8164194\"/>\n <node id=\"5138650784\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551718\" lon=\"103.8163177\"/>\n <node id=\"5138650785\" visible=\"true\" version=\"7\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552148\" lon=\"103.8161948\"/>\n <node id=\"5138672444\" visible=\"true\" version=\"1\" changeset=\"52496545\" timestamp=\"2017-09-29T23:26:08Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552652\" lon=\"103.8169729\"/>\n <node id=\"5138672445\" visible=\"true\" version=\"8\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552404\" lon=\"103.8170098\"/>\n <node id=\"5138672446\" visible=\"true\" version=\"2\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:29Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2551583\" lon=\"103.8167451\"/>\n <node id=\"5138672447\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2553921\" lon=\"103.8161783\"/>\n <node id=\"5138672448\" visible=\"true\" version=\"9\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554871\" lon=\"103.8160020\"/>\n <node id=\"5138672449\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554976\" lon=\"103.8160891\"/>\n <node id=\"5138682822\" visible=\"true\" version=\"2\" changeset=\"52497277\" timestamp=\"2017-09-30T00:46:39Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2551056\" lon=\"103.8185825\"/>\n <node id=\"5138693270\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561297\" lon=\"103.8164499\"/>\n <node id=\"5138693271\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561279\" lon=\"103.8163784\"/>\n <node id=\"5138693273\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561788\" lon=\"103.8160906\"/>\n <node id=\"5138693274\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560102\" lon=\"103.8164384\"/>\n <node id=\"5138693276\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558767\" lon=\"103.8161247\"/>\n <node id=\"5138693277\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557117\" lon=\"103.8160541\"/>\n <node id=\"5138693278\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556825\" lon=\"103.8159090\"/>\n <node id=\"5138693279\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559171\" lon=\"103.8158742\"/>\n <node id=\"5138693280\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561254\" lon=\"103.8158815\"/>\n <node id=\"5138693281\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2565672\" lon=\"103.8159474\"/>\n <node id=\"5138693282\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2567292\" lon=\"103.8159524\"/>\n <node id=\"5138693283\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2569612\" lon=\"103.8159354\"/>\n <node id=\"5138693284\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2572742\" lon=\"103.8158574\"/>\n <node id=\"5138693285\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2573982\" lon=\"103.8158064\"/>\n <node id=\"5138693286\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2577373\" lon=\"103.8156241\"/>\n <node id=\"5138693310\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562599\" lon=\"103.8160475\"/>\n <node id=\"5138693311\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2566723\" lon=\"103.8160199\"/>\n <node id=\"5138693312\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2568343\" lon=\"103.8160249\"/>\n <node id=\"5138693313\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:58Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2570663\" lon=\"103.8160079\"/>\n <node id=\"5138693314\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2573793\" lon=\"103.8159299\"/>\n <node id=\"5138693315\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2575033\" lon=\"103.8158789\"/>\n <node id=\"5138693316\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2578443\" lon=\"103.8156819\"/>\n <node id=\"5138693317\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2579273\" lon=\"103.8156129\"/>\n <node id=\"5138693318\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2580323\" lon=\"103.8154909\"/>\n <node id=\"5138693319\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2581363\" lon=\"103.8153039\"/>\n <node id=\"5138710145\" visible=\"true\" version=\"1\" changeset=\"52497277\" timestamp=\"2017-09-30T00:46:38Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2543873\" lon=\"103.8197630\"/>\n <node id=\"5138710146\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:30Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2543500\" lon=\"103.8198425\"/>\n <node id=\"5138710147\" visible=\"true\" version=\"1\" changeset=\"52497277\" timestamp=\"2017-09-30T00:46:38Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2548672\" lon=\"103.8192216\"/>\n <node id=\"5138710148\" visible=\"true\" version=\"1\" changeset=\"52497277\" timestamp=\"2017-09-30T00:46:38Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2550354\" lon=\"103.8190007\"/>\n <node id=\"5138710149\" visible=\"true\" version=\"2\" changeset=\"52501387\" timestamp=\"2017-09-30T07:27:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2551043\" lon=\"103.8188876\"/>\n <node id=\"5138710150\" visible=\"true\" version=\"3\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:29Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2551131\" lon=\"103.8187931\"/>\n <node id=\"5138710151\" visible=\"true\" version=\"1\" changeset=\"52497277\" timestamp=\"2017-09-30T00:46:38Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2549677\" lon=\"103.8187549\"/>\n <node id=\"5138710152\" visible=\"true\" version=\"1\" changeset=\"52497277\" timestamp=\"2017-09-30T00:46:38Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2550281\" lon=\"103.8186729\"/>\n <node id=\"5138710153\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556211\" lon=\"103.8183751\"/>\n <node id=\"5138710154\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557207\" lon=\"103.8183124\"/>\n <node id=\"5138710155\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558222\" lon=\"103.8182134\"/>\n <node id=\"5138710156\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558878\" lon=\"103.8181340\"/>\n <node id=\"5138710157\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560003\" lon=\"103.8179546\"/>\n <node id=\"5138710158\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562433\" lon=\"103.8174731\"/>\n <node id=\"5138710160\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562869\" lon=\"103.8172734\"/>\n <node id=\"5138710161\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562553\" lon=\"103.8171746\"/>\n <node id=\"5138710162\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561164\" lon=\"103.8169633\"/>\n <node id=\"5138710163\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560250\" lon=\"103.8168277\"/>\n <node id=\"5138710164\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560081\" lon=\"103.8167623\"/>\n <node id=\"5138710165\" visible=\"true\" version=\"2\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:29Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2552110\" lon=\"103.8186698\"/>\n <node id=\"5138710166\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556965\" lon=\"103.8184475\"/>\n <node id=\"5138710167\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558005\" lon=\"103.8183775\"/>\n <node id=\"5138710168\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559195\" lon=\"103.8182565\"/>\n <node id=\"5138710169\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559895\" lon=\"103.8181625\"/>\n <node id=\"5138710170\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560932\" lon=\"103.8179758\"/>\n <node id=\"5138710171\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563721\" lon=\"103.8175074\"/>\n <node id=\"5138710172\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2564185\" lon=\"103.8172900\"/>\n <node id=\"5138710174\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563947\" lon=\"103.8171803\"/>\n <node id=\"5138710175\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562216\" lon=\"103.8168838\"/>\n <node id=\"5138710176\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561583\" lon=\"103.8167845\"/>\n <node id=\"5138710177\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561437\" lon=\"103.8167119\"/>\n <node id=\"5138971722\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557114\" lon=\"103.8172062\"/>\n <node id=\"5138971723\" visible=\"true\" version=\"3\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2556477\" lon=\"103.8175322\"/>\n <node id=\"5139015612\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560081\" lon=\"103.8166832\"/>\n <node id=\"5139015613\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561417\" lon=\"103.8166742\"/>\n <node id=\"5139015614\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554409\" lon=\"103.8172409\"/>\n <node id=\"5139015615\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2553875\" lon=\"103.8174012\"/>\n <node id=\"5139015616\" visible=\"true\" version=\"1\" changeset=\"52501387\" timestamp=\"2017-09-30T07:27:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2551970\" lon=\"103.8173761\"/>\n <node id=\"5139015617\" visible=\"true\" version=\"1\" changeset=\"52501387\" timestamp=\"2017-09-30T07:27:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2552975\" lon=\"103.8171514\"/>\n <node id=\"5139015618\" visible=\"true\" version=\"1\" changeset=\"52501387\" timestamp=\"2017-09-30T07:27:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2553713\" lon=\"103.8171498\"/>\n <node id=\"5139015619\" visible=\"true\" version=\"1\" changeset=\"52501387\" timestamp=\"2017-09-30T07:27:16Z\" user=\"pushian\" uid=\"4989626\" lat=\"1.2553556\" lon=\"103.8172299\"/>\n <node id=\"5139019659\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563128\" lon=\"103.8159095\"/>\n <node id=\"5139019660\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:55:59Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563075\" lon=\"103.8160443\"/>\n <node id=\"5139421331\" visible=\"true\" version=\"3\" changeset=\"52685203\" timestamp=\"2017-10-06T14:37:34Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2565929\" lon=\"103.8119988\"/>\n <node id=\"5145848424\" visible=\"true\" version=\"3\" changeset=\"69415695\" timestamp=\"2019-04-21T03:50:29Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557383\" lon=\"103.8188748\"/>\n <node id=\"5145848430\" visible=\"true\" version=\"4\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:30Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2565864\" lon=\"103.8189392\"/>\n <node id=\"5145849313\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557291\" lon=\"103.8173139\"/>\n <node id=\"5145849314\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557265\" lon=\"103.8175493\"/>\n <node id=\"5145931938\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2559336\" lon=\"103.8161957\"/>\n <node id=\"5145931939\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558000\" lon=\"103.8160758\"/>\n <node id=\"5145931940\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561411\" lon=\"103.8161743\"/>\n <node id=\"5145931941\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561530\" lon=\"103.8161293\"/>\n <node id=\"5145931942\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562158\" lon=\"103.8160623\"/>\n <node id=\"5145931971\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2576869\" lon=\"103.8132896\"/>\n <node id=\"5145931972\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2562827\" lon=\"103.8173771\"/>\n <node id=\"5145931973\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2564107\" lon=\"103.8174020\"/>\n <node id=\"5145931974\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563580\" lon=\"103.8170893\"/>\n <node id=\"5145931975\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549714\" lon=\"103.8175585\"/>\n <node id=\"5145931976\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550050\" lon=\"103.8175842\"/>\n <node id=\"5145931977\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550106\" lon=\"103.8176262\"/>\n <node id=\"5145931978\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549849\" lon=\"103.8176599\"/>\n <node id=\"5145931979\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549430\" lon=\"103.8176655\"/>\n <node id=\"5145931980\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549093\" lon=\"103.8176398\"/>\n <node id=\"5145931981\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549037\" lon=\"103.8175978\"/>\n <node id=\"5145931982\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549294\" lon=\"103.8175641\"/>\n <node id=\"5145931983\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2548422\" lon=\"103.8176272\"/>\n <node id=\"5145931984\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2548605\" lon=\"103.8175463\"/>\n <node id=\"5145931985\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549289\" lon=\"103.8174996\"/>\n <node id=\"5145931986\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550511\" lon=\"103.8175520\"/>\n <node id=\"5145931987\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550605\" lon=\"103.8176481\"/>\n <node id=\"5145931988\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2550002\" lon=\"103.8177133\"/>\n <node id=\"5145931989\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2549193\" lon=\"103.8177177\"/>\n <node id=\"5145931990\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:26Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2548831\" lon=\"103.8176982\"/>\n <node id=\"5145931993\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560356\" lon=\"103.8161935\"/>\n <node id=\"5145931994\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2558988\" lon=\"103.8160258\"/>\n <node id=\"5146201911\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2572649\" lon=\"103.8152494\"/>\n <node id=\"5146232329\" visible=\"true\" version=\"8\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554965\" lon=\"103.8160775\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5146232330\" visible=\"true\" version=\"7\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:02Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554904\" lon=\"103.8160242\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5151991075\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551587\" lon=\"103.8164771\"/>\n <node id=\"5151991076\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551198\" lon=\"103.8164596\"/>\n <node id=\"5151991077\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551763\" lon=\"103.8168342\"/>\n <node id=\"5151991078\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551218\" lon=\"103.8164965\"/>\n <node id=\"5151991079\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551803\" lon=\"103.8165855\"/>\n <node id=\"5151991080\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551637\" lon=\"103.8164380\"/>\n <node id=\"5151991081\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552487\" lon=\"103.8162131\"/>\n <node id=\"5151991082\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552306\" lon=\"103.8162061\"/>\n <node id=\"5151991083\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551718\" lon=\"103.8169930\"/>\n <node id=\"5151991084\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2551926\" lon=\"103.8169906\"/>\n <node id=\"5151991085\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552132\" lon=\"103.8169941\"/>\n <node id=\"5151991086\" visible=\"true\" version=\"6\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:03Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2554045\" lon=\"103.8160631\"/>\n <node id=\"5151991087\" visible=\"true\" version=\"5\" changeset=\"52686350\" timestamp=\"2017-10-06T15:31:17Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553621\" lon=\"103.8160732\"/>\n <node id=\"5151991088\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553223\" lon=\"103.8160867\"/>\n <node id=\"5151991089\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553409\" lon=\"103.8161212\"/>\n <node id=\"5151991090\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553338\" lon=\"103.8161027\"/>\n <node id=\"5151991091\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553400\" lon=\"103.8161604\"/>\n <node id=\"5151991092\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2553430\" lon=\"103.8161408\"/>\n <node id=\"5151991093\" visible=\"true\" version=\"6\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:03Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2557628\" lon=\"103.8159365\"/>\n <node id=\"5151991094\" visible=\"true\" version=\"7\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:03Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2561260\" lon=\"103.8167828\"/>\n <node id=\"5151991095\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552383\" lon=\"103.8185969\"/>\n <node id=\"5151991097\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2550920\" lon=\"103.8186440\"/>\n <node id=\"5151991098\" visible=\"true\" version=\"6\" changeset=\"52686350\" timestamp=\"2017-10-06T15:39:28Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552384\" lon=\"103.8185377\"/>\n <node id=\"5151991099\" visible=\"true\" version=\"7\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:03Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560489\" lon=\"103.8168185\"/>\n <node id=\"5151991100\" visible=\"true\" version=\"7\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:03Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560750\" lon=\"103.8168593\"/>\n <node id=\"5152568179\" visible=\"true\" version=\"1\" changeset=\"52695264\" timestamp=\"2017-10-06T21:30:54Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2575146\" lon=\"103.8231721\"/>\n <node id=\"5175066692\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:03Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560358\" lon=\"103.8159433\"/>\n <node id=\"5175108863\" visible=\"true\" version=\"1\" changeset=\"53047171\" timestamp=\"2017-10-18T17:57:45Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2524007\" lon=\"103.8164368\"/>\n <node id=\"5175108868\" visible=\"true\" version=\"1\" changeset=\"53047171\" timestamp=\"2017-10-18T17:57:45Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2531106\" lon=\"103.8157120\"/>\n <node id=\"5175384349\" visible=\"true\" version=\"1\" changeset=\"53052207\" timestamp=\"2017-10-18T20:38:22Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2552509\" lon=\"103.8187571\"/>\n <node id=\"5175384350\" visible=\"true\" version=\"1\" changeset=\"53052207\" timestamp=\"2017-10-18T20:38:22Z\" user=\"CapAhab\" uid=\"6172866\" lat=\"1.2549476\" lon=\"103.8188959\"/>\n <node id=\"5175392808\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:04Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560357\" lon=\"103.8160113\"/>\n <node id=\"5209288645\" visible=\"true\" version=\"1\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2552244\" lon=\"103.8185142\"/>\n <node id=\"5209288647\" visible=\"true\" version=\"1\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:27Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2551103\" lon=\"103.8188232\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5209288650\" visible=\"true\" version=\"1\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2579967\" lon=\"103.8124881\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5209288652\" visible=\"true\" version=\"1\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2586232\" lon=\"103.8137176\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5209288654\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:04Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2580669\" lon=\"103.8149089\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5209288656\" visible=\"true\" version=\"1\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2551718\" lon=\"103.8167514\"/>\n <node id=\"5209288657\" visible=\"true\" version=\"1\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:28Z\" user=\"Evandering\" uid=\"6172527\" lat=\"1.2552052\" lon=\"103.8167682\"/>\n <node id=\"5209288658\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:04Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563109\" lon=\"103.8159577\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5209288659\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:04Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2563089\" lon=\"103.8160091\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5209288660\" visible=\"true\" version=\"2\" changeset=\"59733650\" timestamp=\"2018-06-11T08:56:04Z\" user=\"naik4\" uid=\"7671613\" lat=\"1.2560381\" lon=\"103.8166812\">\n  <tag k=\"highway\" v=\"crossing\"/>\n </node>\n <node id=\"5278186712\" visible=\"true\" version=\"1\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:42Z\" user=\"happy-camper\" uid=\"6346054\" lat=\"1.2552760\" lon=\"103.8169656\"/>\n <node id=\"5286594350\" visible=\"true\" version=\"1\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:41Z\" user=\"cartogram\" uid=\"6095772\" lat=\"1.2552553\" lon=\"103.8174222\"/>\n <node id=\"5286594351\" visible=\"true\" version=\"1\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:41Z\" user=\"cartogram\" uid=\"6095772\" lat=\"1.2552784\" lon=\"103.8173867\"/>\n <node id=\"5286594352\" visible=\"true\" version=\"1\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:41Z\" user=\"cartogram\" uid=\"6095772\" lat=\"1.2552954\" lon=\"103.8173488\"/>\n <node id=\"5286594353\" visible=\"true\" version=\"1\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:42Z\" user=\"cartogram\" uid=\"6095772\" lat=\"1.2553055\" lon=\"103.8173052\"/>\n <node id=\"5286594354\" visible=\"true\" version=\"1\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:42Z\" user=\"cartogram\" uid=\"6095772\" lat=\"1.2553088\" lon=\"103.8172509\"/>\n <node id=\"5286594355\" visible=\"true\" version=\"1\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:42Z\" user=\"cartogram\" uid=\"6095772\" lat=\"1.2553015\" lon=\"103.8172019\"/>\n <node id=\"5286594356\" visible=\"true\" version=\"1\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:42Z\" user=\"cartogram\" uid=\"6095772\" lat=\"1.2552897\" lon=\"103.8171688\"/>\n <node id=\"5586638524\" visible=\"true\" version=\"1\" changeset=\"58534348\" timestamp=\"2018-04-29T18:22:09Z\" user=\"Wiple\" uid=\"7355545\" lat=\"1.2550597\" lon=\"103.8176013\">\n  <tag k=\"name:de\" v=\"Tiger Sky Tower\"/><tag k=\"tourism\" v=\"attraction\"/>\n </node>\n <node id=\"5761606753\" visible=\"true\" version=\"1\" changeset=\"60730626\" timestamp=\"2018-07-15T12:20:48Z\" user=\"gunsno\" uid=\"7277094\" lat=\"1.2554079\" lon=\"103.8165019\">\n  <tag k=\"name\" v=\"4D Adventureland\"/><tag k=\"opening_hours\" v=\"Mo-Su 09:00-21:00\"/><tag k=\"tourism\" v=\"attraction\"/>\n </node>\n <node id=\"6217274958\" visible=\"true\" version=\"1\" changeset=\"66408527\" timestamp=\"2019-01-17T21:33:16Z\" user=\"KingRichardV\" uid=\"9004577\" lat=\"1.2542873\" lon=\"103.8177619\"/>\n <node id=\"6418091522\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552068\" lon=\"103.8187353\"/>\n <node id=\"6418091523\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:38Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555529\" lon=\"103.8185853\"/>\n <node id=\"6418091524\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2565973\" lon=\"103.8183117\"/>\n <node id=\"6418091525\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2566349\" lon=\"103.8182808\"/>\n <node id=\"6418091526\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2567354\" lon=\"103.8181548\"/>\n <node id=\"6418091527\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2567448\" lon=\"103.8180716\"/>\n <node id=\"6418091528\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2567354\" lon=\"103.8179335\"/>\n <node id=\"6418091529\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2567622\" lon=\"103.8172790\"/>\n <node id=\"6418091530\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2568132\" lon=\"103.8170604\"/>\n <node id=\"6418091531\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2569446\" lon=\"103.8168123\"/>\n <node id=\"6418091532\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2571229\" lon=\"103.8165790\"/>\n <node id=\"6418091533\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2576203\" lon=\"103.8164556\"/>\n <node id=\"6418091534\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2581365\" lon=\"103.8161378\"/>\n <node id=\"6418091535\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2583202\" lon=\"103.8157247\"/>\n <node id=\"6418091536\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2583846\" lon=\"103.8154954\"/>\n <node id=\"6418091537\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2585066\" lon=\"103.8152701\"/>\n <node id=\"6418091538\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2587573\" lon=\"103.8146988\"/>\n <node id=\"6418091539\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2589303\" lon=\"103.8141046\"/>\n <node id=\"6418091540\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2589732\" lon=\"103.8137519\"/>\n <node id=\"6418091541\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2589209\" lon=\"103.8133080\"/>\n <node id=\"6418091542\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2589196\" lon=\"103.8127555\"/>\n <node id=\"6418091543\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2590416\" lon=\"103.8121292\"/>\n <node id=\"6418091544\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2583122\" lon=\"103.8124014\"/>\n <node id=\"6418091545\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580199\" lon=\"103.8119562\"/>\n <node id=\"6418091546\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2581942\" lon=\"103.8118382\"/>\n <node id=\"6418091547\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2579341\" lon=\"103.8113540\"/>\n <node id=\"6418091548\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2578697\" lon=\"103.8114117\"/>\n <node id=\"6418091549\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2577343\" lon=\"103.8113862\"/>\n <node id=\"6418091550\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2576539\" lon=\"103.8115807\"/>\n <node id=\"6418091551\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2576552\" lon=\"103.8117389\"/>\n <node id=\"6418091552\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2579435\" lon=\"103.8123183\"/>\n <node id=\"6418091553\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2584691\" lon=\"103.8131914\"/>\n <node id=\"6418091554\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2586594\" lon=\"103.8136554\"/>\n <node id=\"6418091555\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2587573\" lon=\"103.8139424\"/>\n <node id=\"6418091556\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2587801\" lon=\"103.8140550\"/>\n <node id=\"6418091557\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2587439\" lon=\"103.8141784\"/>\n <node id=\"6418091558\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2586608\" lon=\"103.8142857\"/>\n <node id=\"6418091559\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2585589\" lon=\"103.8143326\"/>\n <node id=\"6418091560\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2582398\" lon=\"103.8144024\"/>\n <node id=\"6418091561\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2581620\" lon=\"103.8144386\"/>\n <node id=\"6418091562\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580963\" lon=\"103.8144909\"/>\n <node id=\"6418091563\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580708\" lon=\"103.8145781\"/>\n <node id=\"6418091564\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580561\" lon=\"103.8147202\"/>\n <node id=\"6418091565\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580869\" lon=\"103.8148342\"/>\n <node id=\"6418091566\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2581406\" lon=\"103.8149817\"/>\n <node id=\"6418091567\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2581687\" lon=\"103.8151427\"/>\n <node id=\"6418091568\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2581593\" lon=\"103.8152862\"/>\n <node id=\"6418091569\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580668\" lon=\"103.8154940\"/>\n <node id=\"6418091570\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2579341\" lon=\"103.8156550\"/>\n <node id=\"6418091571\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2576565\" lon=\"103.8158374\"/>\n <node id=\"6418091572\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2574112\" lon=\"103.8159554\"/>\n <node id=\"6418091573\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2571162\" lon=\"103.8160305\"/>\n <node id=\"6418091574\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2564337\" lon=\"103.8160506\"/>\n <node id=\"6418091575\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2562742\" lon=\"103.8160788\"/>\n <node id=\"6418091576\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2562112\" lon=\"103.8161096\"/>\n <node id=\"6418091577\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2561763\" lon=\"103.8161954\"/>\n <node id=\"6418091578\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2561643\" lon=\"103.8167252\"/>\n <node id=\"6418091579\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2561696\" lon=\"103.8167801\"/>\n <node id=\"6418091580\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2563694\" lon=\"103.8170779\"/>\n <node id=\"6418091581\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2564150\" lon=\"103.8172106\"/>\n <node id=\"6418091582\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2564364\" lon=\"103.8173085\"/>\n <node id=\"6418091583\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2564056\" lon=\"103.8174628\"/>\n <node id=\"6418091584\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2561200\" lon=\"103.8179764\"/>\n <node id=\"6418092485\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559859\" lon=\"103.8182031\"/>\n <node id=\"6418092486\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559001\" lon=\"103.8183010\"/>\n <node id=\"6418092487\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557701\" lon=\"103.8184109\"/>\n <node id=\"6418092488\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2553537\" lon=\"103.8185866\"/>\n <node id=\"6418092489\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:39Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552733\" lon=\"103.8186248\"/>\n <node id=\"6418094127\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:06Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2539915\" lon=\"103.8149151\"/>\n <node id=\"6418094128\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:06Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2542215\" lon=\"103.8151963\"/>\n <node id=\"6418094129\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:06Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2543327\" lon=\"103.8150448\"/>\n <node id=\"6418094130\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:06Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2543756\" lon=\"103.8153653\"/>\n <node id=\"6418094131\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:06Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548422\" lon=\"103.8153251\"/>\n <node id=\"6418094132\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:06Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548489\" lon=\"103.8151534\"/>\n <node id=\"6418094133\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:06Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548717\" lon=\"103.8148503\"/>\n <node id=\"6418094134\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549736\" lon=\"103.8146250\"/>\n <node id=\"6418094135\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550313\" lon=\"103.8146599\"/>\n <node id=\"6418094136\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2553692\" lon=\"103.8142361\"/>\n <node id=\"6418094137\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554067\" lon=\"103.8142669\"/>\n <node id=\"6418094138\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554496\" lon=\"103.8142307\"/>\n <node id=\"6418094139\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554925\" lon=\"103.8142937\"/>\n <node id=\"6418094140\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554054\" lon=\"103.8144104\"/>\n <node id=\"6418094141\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2553598\" lon=\"103.8144211\"/>\n <node id=\"6418094142\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552914\" lon=\"103.8144681\"/>\n <node id=\"6418094143\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552646\" lon=\"103.8145486\"/>\n <node id=\"6418094144\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552632\" lon=\"103.8146116\"/>\n <node id=\"6418094145\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2553571\" lon=\"103.8147135\"/>\n <node id=\"6418094146\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554510\" lon=\"103.8146974\"/>\n <node id=\"6418094147\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555059\" lon=\"103.8146491\"/>\n <node id=\"6418094148\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555247\" lon=\"103.8145727\"/>\n <node id=\"6418094149\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555099\" lon=\"103.8144721\"/>\n <node id=\"6418094150\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555475\" lon=\"103.8144131\"/>\n <node id=\"6418094151\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2556682\" lon=\"103.8142441\"/>\n <node id=\"6418094152\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558036\" lon=\"103.8141194\"/>\n <node id=\"6418094153\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558934\" lon=\"103.8139236\"/>\n <node id=\"6418094154\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2561026\" lon=\"103.8137372\"/>\n <node id=\"6418094155\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2562434\" lon=\"103.8135427\"/>\n <node id=\"6418094156\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2563332\" lon=\"103.8133402\"/>\n <node id=\"6418094157\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2564767\" lon=\"103.8132007\"/>\n <node id=\"6418094158\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2568816\" lon=\"103.8128789\"/>\n <node id=\"6418094159\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2570894\" lon=\"103.8126013\"/>\n <node id=\"6418094160\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2572007\" lon=\"103.8124859\"/>\n <node id=\"6418094161\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2573468\" lon=\"103.8124028\"/>\n <node id=\"6418094162\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2578013\" lon=\"103.8123424\"/>\n <node id=\"6418094163\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2578630\" lon=\"103.8124658\"/>\n <node id=\"6418094164\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2581821\" lon=\"103.8129312\"/>\n <node id=\"6418094165\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2583605\" lon=\"103.8132665\"/>\n <node id=\"6418094166\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2584235\" lon=\"103.8134448\"/>\n <node id=\"6418094167\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2585468\" lon=\"103.8138203\"/>\n <node id=\"6418094168\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2585736\" lon=\"103.8139719\"/>\n <node id=\"6418094169\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2585656\" lon=\"103.8140604\"/>\n <node id=\"6418094170\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2585307\" lon=\"103.8141328\"/>\n <node id=\"6418094171\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2584329\" lon=\"103.8141771\"/>\n <node id=\"6418094172\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2582532\" lon=\"103.8142012\"/>\n <node id=\"6418094173\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580735\" lon=\"103.8142803\"/>\n <node id=\"6418094174\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2579555\" lon=\"103.8144024\"/>\n <node id=\"6418094175\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2579073\" lon=\"103.8145807\"/>\n <node id=\"6418094176\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2579046\" lon=\"103.8147417\"/>\n <node id=\"6418094177\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580266\" lon=\"103.8150555\"/>\n <node id=\"6418094178\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2580279\" lon=\"103.8151842\"/>\n <node id=\"6418094179\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2579730\" lon=\"103.8153532\"/>\n <node id=\"6418094180\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2578617\" lon=\"103.8155115\"/>\n <node id=\"6418094181\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2576981\" lon=\"103.8156174\"/>\n <node id=\"6418094182\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2573643\" lon=\"103.8158025\"/>\n <node id=\"6418094183\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2572288\" lon=\"103.8158508\"/>\n <node id=\"6418094184\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2569473\" lon=\"103.8159151\"/>\n <node id=\"6418095005\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549803\" lon=\"103.8172569\"/>\n <node id=\"6418095006\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548959\" lon=\"103.8173756\"/>\n <node id=\"6418095007\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2547893\" lon=\"103.8174125\"/>\n <node id=\"6418095008\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2546264\" lon=\"103.8174366\"/>\n <node id=\"6418095009\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545426\" lon=\"103.8174011\"/>\n <node id=\"6418095010\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544816\" lon=\"103.8173394\"/>\n <node id=\"6418095011\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2542469\" lon=\"103.8171215\"/>\n <node id=\"6418095012\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2541296\" lon=\"103.8170370\"/>\n <node id=\"6418095013\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2537937\" lon=\"103.8169008\"/>\n <node id=\"6418095014\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2535893\" lon=\"103.8168546\"/>\n <node id=\"6418095015\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2534720\" lon=\"103.8168418\"/>\n <node id=\"6418095016\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2532849\" lon=\"103.8168713\"/>\n <node id=\"6418095017\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2531334\" lon=\"103.8169451\"/>\n <node id=\"6418095018\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2529993\" lon=\"103.8170323\"/>\n <node id=\"6418095019\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2528887\" lon=\"103.8171416\"/>\n <node id=\"6418095020\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2527841\" lon=\"103.8173193\"/>\n <node id=\"6418095021\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2527245\" lon=\"103.8175111\"/>\n <node id=\"6418095022\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2527198\" lon=\"103.8176297\"/>\n <node id=\"6418095023\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2527392\" lon=\"103.8178369\"/>\n <node id=\"6418095024\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2528197\" lon=\"103.8180153\"/>\n <node id=\"6418095025\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2531193\" lon=\"103.8184183\"/>\n <node id=\"6418095026\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2532829\" lon=\"103.8186872\"/>\n <node id=\"6418095028\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:09Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2532615\" lon=\"103.8187006\"/>\n <node id=\"6418095329\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548972\" lon=\"103.8189467\"/>\n <node id=\"6418095330\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548235\" lon=\"103.8190459\"/>\n <node id=\"6418095331\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544809\" lon=\"103.8194013\"/>\n <node id=\"6418095332\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2543428\" lon=\"103.8193423\"/>\n <node id=\"6418095333\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544025\" lon=\"103.8193054\"/>\n <node id=\"6418095334\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2543508\" lon=\"103.8191915\"/>\n <node id=\"6418095335\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2544541\" lon=\"103.8191378\"/>\n <node id=\"6418095336\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545211\" lon=\"103.8191378\"/>\n <node id=\"6418095337\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2546505\" lon=\"103.8190969\"/>\n <node id=\"6418095338\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549314\" lon=\"103.8187549\"/>\n <node id=\"6418095339\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549783\" lon=\"103.8187925\"/>\n <node id=\"6418095340\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2545808\" lon=\"103.8187999\"/>\n <node id=\"6418095341\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2547028\" lon=\"103.8187663\"/>\n <node id=\"6418095342\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2546894\" lon=\"103.8185249\"/>\n <node id=\"6418095343\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550541\" lon=\"103.8182071\"/>\n <node id=\"6418095344\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554959\" lon=\"103.8182064\"/>\n <node id=\"6418095345\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554898\" lon=\"103.8183747\"/>\n <node id=\"6418095346\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555850\" lon=\"103.8183513\"/>\n <node id=\"6418095347\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2556963\" lon=\"103.8182909\"/>\n <node id=\"6418095348\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557855\" lon=\"103.8182158\"/>\n <node id=\"6418095349\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:58Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558827\" lon=\"103.8181166\"/>\n <node id=\"6418095350\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:59Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2562494\" lon=\"103.8174427\"/>\n <node id=\"6418095351\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:59Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2562655\" lon=\"103.8173092\"/>\n <node id=\"6418095352\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:07:59Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2562434\" lon=\"103.8171771\"/>\n <node id=\"6418095353\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2560496\" lon=\"103.8168633\"/>\n <node id=\"6418095354\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2560194\" lon=\"103.8167741\"/>\n <node id=\"6418095355\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2560201\" lon=\"103.8162176\"/>\n <node id=\"6418095356\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559893\" lon=\"103.8161425\"/>\n <node id=\"6418095357\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559450\" lon=\"103.8160908\"/>\n <node id=\"6418095358\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558927\" lon=\"103.8160620\"/>\n <node id=\"6418095359\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557640\" lon=\"103.8160519\"/>\n <node id=\"6418095360\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2556829\" lon=\"103.8160821\"/>\n <node id=\"6418095361\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2558358\" lon=\"103.8164670\"/>\n <node id=\"6418095362\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559410\" lon=\"103.8171362\"/>\n <node id=\"6418095363\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559967\" lon=\"103.8171503\"/>\n <node id=\"6418095364\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559940\" lon=\"103.8172106\"/>\n <node id=\"6418095365\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2560375\" lon=\"103.8172207\"/>\n <node id=\"6418095366\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2559216\" lon=\"103.8176163\"/>\n <node id=\"6418095367\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557929\" lon=\"103.8176036\"/>\n <node id=\"6418095368\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557701\" lon=\"103.8177659\"/>\n <node id=\"6418095369\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557372\" lon=\"103.8177632\"/>\n <node id=\"6418095370\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557278\" lon=\"103.8178517\"/>\n <node id=\"6418095371\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552847\" lon=\"103.8178021\"/>\n <node id=\"6418095372\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552565\" lon=\"103.8178651\"/>\n <node id=\"6418095373\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552110\" lon=\"103.8179281\"/>\n <node id=\"6418095374\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549240\" lon=\"103.8178899\"/>\n <node id=\"6418095375\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549508\" lon=\"103.8177565\"/>\n <node id=\"6418095376\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549187\" lon=\"103.8177504\"/>\n <node id=\"6418095377\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548362\" lon=\"103.8177035\"/>\n <node id=\"6418095378\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2546740\" lon=\"103.8177276\"/>\n <node id=\"6418095379\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2546492\" lon=\"103.8177699\"/>\n <node id=\"6418095380\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:00Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2543636\" lon=\"103.8185987\"/>\n <node id=\"6418096085\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2567274\" lon=\"103.8159192\"/>\n <node id=\"6418096086\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2562232\" lon=\"103.8159057\"/>\n <node id=\"6418096087\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2560959\" lon=\"103.8158789\"/>\n <node id=\"6418096088\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2557902\" lon=\"103.8158870\"/>\n <node id=\"6418096089\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555435\" lon=\"103.8159460\"/>\n <node id=\"6418096090\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2555073\" lon=\"103.8158347\"/>\n <node id=\"6418096091\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554375\" lon=\"103.8158628\"/>\n <node id=\"6418096092\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554563\" lon=\"103.8159688\"/>\n <node id=\"6418096093\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2554013\" lon=\"103.8160050\"/>\n <node id=\"6418096094\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2553638\" lon=\"103.8159956\"/>\n <node id=\"6418096095\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2553196\" lon=\"103.8159406\"/>\n <node id=\"6418096096\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2552244\" lon=\"103.8159057\"/>\n <node id=\"6418096097\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551305\" lon=\"103.8159205\"/>\n <node id=\"6418096098\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550527\" lon=\"103.8159674\"/>\n <node id=\"6418096099\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550085\" lon=\"103.8160868\"/>\n <node id=\"6418096100\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550581\" lon=\"103.8162410\"/>\n <node id=\"6418096101\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:07Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550192\" lon=\"103.8163322\"/>\n <node id=\"6418096102\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:08Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2548597\" lon=\"103.8168673\"/>\n <node id=\"6418096103\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:08Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550487\" lon=\"103.8170993\"/>\n <node id=\"6418096104\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:08Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2551024\" lon=\"103.8171838\"/>\n <node id=\"6418096105\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:08Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2550822\" lon=\"103.8172764\"/>\n <node id=\"6418096391\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:10Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2522063\" lon=\"103.8180730\"/>\n <node id=\"6418683199\" visible=\"true\" version=\"1\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:20Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2549321\" lon=\"103.8191861\"/>\n <node id=\"6418683200\" visible=\"true\" version=\"1\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:20Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2518027\" lon=\"103.8247356\"/>\n <node id=\"6418683201\" visible=\"true\" version=\"1\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:20Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2527573\" lon=\"103.8221888\"/>\n <node id=\"6418683202\" visible=\"true\" version=\"1\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:20Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2526688\" lon=\"103.8232389\"/>\n <node id=\"6418683203\" visible=\"true\" version=\"1\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:20Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2520561\" lon=\"103.8249689\"/>\n <node id=\"6418683204\" visible=\"true\" version=\"1\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:20Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2523149\" lon=\"103.8240060\"/>\n <node id=\"6418683205\" visible=\"true\" version=\"1\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:20Z\" user=\"JaLooNz\" uid=\"741163\" lat=\"1.2529504\" lon=\"103.8258608\"/>\n <node id=\"6461869572\" visible=\"true\" version=\"1\" changeset=\"70073507\" timestamp=\"2019-05-09T13:25:24Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2575991\" lon=\"103.8229848\"/>\n <node id=\"6464184258\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:03Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2543463\" lon=\"103.8182899\"/>\n <node id=\"6464184260\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:03Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2545761\" lon=\"103.8177946\"/>\n <node id=\"6464184261\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:03Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2544518\" lon=\"103.8180627\"/>\n <node id=\"6464184262\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:03Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2547353\" lon=\"103.8174530\"/>\n <node id=\"6464184263\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:03Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2549477\" lon=\"103.8169940\"/>\n <node id=\"6464184264\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:03Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2553994\" lon=\"103.8159077\"/>\n <node id=\"6464184265\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:03Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2558502\" lon=\"103.8148119\"/>\n <node id=\"6464873706\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:52Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2553539\" lon=\"103.8172510\">\n  <tag k=\"artwork_type\" v=\"sculpture\"/><tag k=\"image\" v=\"https://images.mapillary.com/sDvht2V9qXhRVmoSJmWh_w/thumb-2048.jpg\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/sDvht2V9qXhRVmoSJmWh_w\"/><tag k=\"tourism\" v=\"artwork\"/>\n </node>\n <node id=\"6464873707\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:52Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2553279\" lon=\"103.8173871\">\n  <tag k=\"amenity\" v=\"fountain\"/><tag k=\"image\" v=\"https://images.mapillary.com/-3LjtvvOeOm6KqBpMPBaNg/thumb-2048.jpg\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/-3LjtvvOeOm6KqBpMPBaNg\"/>\n </node>\n <node id=\"6464873708\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:52Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2553653\" lon=\"103.8174089\">\n  <tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/GlnBJBlPZOptpdPiudAvFA\"/><tag k=\"name\" v=\"Butterfly Park &amp; Insect Kigdom\"/><tag k=\"tourism\" v=\"artwork\"/>\n </node>\n <node id=\"6464873709\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:52Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2586261\" lon=\"103.8180410\"/>\n <node id=\"6464873749\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566324\" lon=\"103.8187761\"/>\n <node id=\"6464873750\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566542\" lon=\"103.8187736\"/>\n <node id=\"6464873751\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566741\" lon=\"103.8187642\"/>\n <node id=\"6464873752\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566899\" lon=\"103.8187489\"/>\n <node id=\"6464873753\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2567000\" lon=\"103.8187294\"/>\n <node id=\"6464873754\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2567033\" lon=\"103.8187077\"/>\n <node id=\"6464873755\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566993\" lon=\"103.8186854\"/>\n <node id=\"6464873756\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566881\" lon=\"103.8186656\"/>\n <node id=\"6464873757\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566711\" lon=\"103.8186507\"/>\n <node id=\"6464873758\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566500\" lon=\"103.8186422\"/>\n <node id=\"6464873759\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566273\" lon=\"103.8186411\"/>\n <node id=\"6464873760\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566056\" lon=\"103.8186476\"/>\n <node id=\"6464873761\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2565872\" lon=\"103.8186609\"/>\n <node id=\"6464873762\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2565742\" lon=\"103.8186795\"/>\n <node id=\"6464873763\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2565682\" lon=\"103.8187014\"/>\n <node id=\"6464873764\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2565695\" lon=\"103.8187233\"/>\n <node id=\"6464873765\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2565777\" lon=\"103.8187437\"/>\n <node id=\"6464873766\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2565920\" lon=\"103.8187603\"/>\n <node id=\"6464873767\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\" lat=\"1.2566109\" lon=\"103.8187715\"/>\n <node id=\"6539353057\" visible=\"true\" version=\"1\" changeset=\"71136817\" timestamp=\"2019-06-11T11:17:16Z\" user=\"tommystyle84\" uid=\"438632\" lat=\"1.2569769\" lon=\"103.8153199\"/>\n <node id=\"6596068720\" visible=\"true\" version=\"1\" changeset=\"72006528\" timestamp=\"2019-07-08T10:23:59Z\" user=\"div006\" uid=\"8911933\" lat=\"1.2564458\" lon=\"103.8160077\"/>\n <node id=\"6728983057\" visible=\"true\" version=\"1\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\" lat=\"1.2565055\" lon=\"103.8183895\"/>\n <way id=\"28755868\" visible=\"true\" version=\"10\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:55Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"316161140\"/><nd ref=\"6464873709\"/><tag k=\"aerialway\" v=\"gondola\"/><tag k=\"layer\" v=\"2\"/><tag k=\"name\" v=\"Singapore-Sentosa Cable Car\"/>\n </way>\n <way id=\"100985094\" visible=\"true\" version=\"5\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:29Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5119743804\"/><nd ref=\"5134029801\"/><nd ref=\"5134029927\"/><nd ref=\"5119743803\"/><nd ref=\"5134029949\"/><nd ref=\"5134030041\"/><nd ref=\"5119743802\"/><nd ref=\"5119743801\"/><nd ref=\"5119743800\"/><nd ref=\"1166463644\"/><nd ref=\"1166463599\"/><nd ref=\"5134029979\"/><nd ref=\"1166463638\"/><nd ref=\"1166463593\"/><nd ref=\"1166463611\"/><nd ref=\"1166463618\"/><nd ref=\"1166463643\"/><nd ref=\"5119775043\"/><nd ref=\"5139421331\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"name\" v=\"Imbiah Trail\"/>\n </way>\n <way id=\"128869879\" visible=\"true\" version=\"3\" changeset=\"75596691\" timestamp=\"2019-10-12T10:15:03Z\" user=\"sgmapperhh\" uid=\"10373200\">\n  <nd ref=\"1422900678\"/><nd ref=\"1422900685\"/><tag k=\"aerialway\" v=\"chair_lift\"/><tag k=\"highway\" v=\"raceway\"/><tag k=\"layer\" v=\"2\"/><tag k=\"name\" v=\"SkyRide\"/><tag k=\"oneway\" v=\"no\"/>\n </way>\n <way id=\"128869880\" visible=\"true\" version=\"5\" changeset=\"52501454\" timestamp=\"2017-09-30T07:31:12Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"1739490136\"/><nd ref=\"1739490135\"/><nd ref=\"1422900639\"/><nd ref=\"603563257\"/><nd ref=\"603563258\"/><nd ref=\"2103671644\"/><tag k=\"cutting\" v=\"no\"/><tag k=\"highway\" v=\"track\"/><tag k=\"name\" v=\"Luge Trail\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"159637722\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T09:23:06Z\" user=\"rene78\" uid=\"257555\">\n  <nd ref=\"1717556425\"/><nd ref=\"1717556599\"/><nd ref=\"1717556637\"/><nd ref=\"1717556675\"/><nd ref=\"1717556676\"/><nd ref=\"1717556499\"/><nd ref=\"1717556606\"/><nd ref=\"1717556535\"/><nd ref=\"1717556425\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637723\" visible=\"true\" version=\"5\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:34Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"1717556452\"/><nd ref=\"1717556456\"/><nd ref=\"4733410886\"/><nd ref=\"1717556439\"/><nd ref=\"1717556431\"/><nd ref=\"1717556538\"/><nd ref=\"1717556437\"/><nd ref=\"1717556591\"/><nd ref=\"1717556423\"/><nd ref=\"5133972842\"/><nd ref=\"1717556488\"/><nd ref=\"1717556418\"/><nd ref=\"1717556463\"/><nd ref=\"5133972843\"/><nd ref=\"1717556663\"/><nd ref=\"1717556594\"/><nd ref=\"1717556592\"/><nd ref=\"1717556482\"/><nd ref=\"1717556452\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"51\"/><tag k=\"addr:postcode\" v=\"099702\"/><tag k=\"addr:street\" v=\"Imbiah Road\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637725\" visible=\"true\" version=\"5\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:35Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"1717556679\"/><nd ref=\"1717556455\"/><nd ref=\"1717556682\"/><nd ref=\"1717556512\"/><nd ref=\"1717556542\"/><nd ref=\"1717556454\"/><nd ref=\"1717556674\"/><nd ref=\"1717556453\"/><nd ref=\"1717556638\"/><nd ref=\"1873234879\"/><nd ref=\"1717556684\"/><nd ref=\"1873234866\"/><nd ref=\"1717556689\"/><nd ref=\"1717556630\"/><nd ref=\"1717556448\"/><nd ref=\"1717556669\"/><nd ref=\"1717556679\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"50\"/><tag k=\"addr:postcode\" v=\"099706\"/><tag k=\"addr:street\" v=\"Imbiah Road\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Singapore Cable Car Station\"/>\n </way>\n <way id=\"159637726\" visible=\"true\" version=\"5\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:35Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"1717556443\"/><nd ref=\"1717556486\"/><nd ref=\"1717556639\"/><nd ref=\"1717556491\"/><nd ref=\"1717556547\"/><nd ref=\"1717556472\"/><nd ref=\"1717556484\"/><nd ref=\"1717556603\"/><nd ref=\"1717556476\"/><nd ref=\"1717556481\"/><nd ref=\"1717556596\"/><nd ref=\"1717556468\"/><nd ref=\"1717556656\"/><nd ref=\"1717556518\"/><nd ref=\"1717556680\"/><nd ref=\"1717556495\"/><nd ref=\"1717556673\"/><nd ref=\"1717556440\"/><nd ref=\"1717556445\"/><nd ref=\"1717556433\"/><nd ref=\"1717556516\"/><nd ref=\"1717556444\"/><nd ref=\"5133972841\"/><nd ref=\"1717556633\"/><nd ref=\"1717556529\"/><nd ref=\"1717556543\"/><nd ref=\"1717556428\"/><nd ref=\"1717556426\"/><nd ref=\"1717556420\"/><nd ref=\"1717556670\"/><nd ref=\"1717556436\"/><nd ref=\"1717556573\"/><nd ref=\"1717556566\"/><nd ref=\"1717556691\"/><nd ref=\"1717556458\"/><nd ref=\"1717556634\"/><nd ref=\"1717556461\"/><nd ref=\"1717556560\"/><nd ref=\"1717556532\"/><nd ref=\"1717556635\"/><nd ref=\"1717556692\"/><nd ref=\"1717556443\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"51B\"/><tag k=\"addr:postcode\" v=\"099708\"/><tag k=\"addr:street\" v=\"Imbiah Road\"/><tag k=\"building\" v=\"yes\"/><tag k=\"building:levels\" v=\"2\"/>\n </way>\n <way id=\"159637727\" visible=\"true\" version=\"5\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:35Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"1717556655\"/><nd ref=\"1717556520\"/><nd ref=\"1717556570\"/><nd ref=\"1717556509\"/><nd ref=\"1717556451\"/><nd ref=\"1717556671\"/><nd ref=\"1717556459\"/><nd ref=\"1717556580\"/><nd ref=\"1717556545\"/><nd ref=\"1717556441\"/><nd ref=\"1717556535\"/><nd ref=\"1717556606\"/><nd ref=\"1717556450\"/><nd ref=\"1717556568\"/><nd ref=\"1717556447\"/><nd ref=\"1717556422\"/><nd ref=\"1717556610\"/><nd ref=\"1717556449\"/><nd ref=\"1717556527\"/><nd ref=\"1717556474\"/><nd ref=\"5133972846\"/><nd ref=\"1717556655\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"21\"/><tag k=\"addr:postcode\" v=\"098968\"/><tag k=\"addr:street\" v=\"Siloso Road\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Sentosa Nature Discovery\"/><tag k=\"tourism\" v=\"museum\"/><tag k=\"website\" v=\"https://www.sentosa.com.sg/Explore/Sentosa-Nature-Discovery-Walking-Trails/Sentosa-Nature-Discovery\"/>\n </way>\n <way id=\"159637728\" visible=\"true\" version=\"6\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:35Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"1717556514\"/><nd ref=\"1717556435\"/><nd ref=\"1717556432\"/><nd ref=\"1717556687\"/><nd ref=\"1717556628\"/><nd ref=\"1717556678\"/><nd ref=\"1717556651\"/><nd ref=\"4731913772\"/><nd ref=\"1717556430\"/><nd ref=\"1717556686\"/><nd ref=\"1717556630\"/><nd ref=\"1717556448\"/><nd ref=\"1717556669\"/><nd ref=\"1717556679\"/><nd ref=\"1717556514\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"42\"/><tag k=\"addr:postcode\" v=\"099701\"/><tag k=\"addr:street\" v=\"Imbiah Road\"/><tag k=\"building\" v=\"yes\"/><tag k=\"building:levels\" v=\"2\"/>\n </way>\n <way id=\"159637729\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T09:23:08Z\" user=\"rene78\" uid=\"257555\">\n  <nd ref=\"1717556685\"/><nd ref=\"1717556672\"/><nd ref=\"1717556449\"/><nd ref=\"1717556610\"/><nd ref=\"1717556685\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637730\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T09:23:09Z\" user=\"rene78\" uid=\"257555\">\n  <nd ref=\"1717556459\"/><nd ref=\"1717556681\"/><nd ref=\"1717556424\"/><nd ref=\"1717556580\"/><nd ref=\"1717556459\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637731\" visible=\"true\" version=\"3\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:35Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"1717556505\"/><nd ref=\"1717556617\"/><nd ref=\"1717556445\"/><nd ref=\"1717556440\"/><nd ref=\"1717556673\"/><nd ref=\"1717556613\"/><nd ref=\"1717556688\"/><nd ref=\"1717556462\"/><nd ref=\"1717556654\"/><nd ref=\"1717556519\"/><nd ref=\"1717556437\"/><nd ref=\"1717556591\"/><nd ref=\"1717556423\"/><nd ref=\"1717556505\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"51A\"/><tag k=\"addr:postcode\" v=\"099703\"/><tag k=\"addr:street\" v=\"Imbiah Road\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637733\" visible=\"true\" version=\"3\" changeset=\"52606119\" timestamp=\"2017-10-03T19:16:09Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"1717556451\"/><nd ref=\"1717556464\"/><nd ref=\"4279963872\"/><nd ref=\"1717556497\"/><nd ref=\"1717556509\"/><nd ref=\"1717556451\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637735\" visible=\"true\" version=\"3\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:43Z\" user=\"happy-camper\" uid=\"6346054\">\n  <nd ref=\"1717556561\"/><nd ref=\"5278186712\"/><nd ref=\"1717556446\"/><nd ref=\"1717556521\"/><nd ref=\"1717556442\"/><nd ref=\"1717556561\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637736\" visible=\"true\" version=\"3\" changeset=\"46816490\" timestamp=\"2017-03-13T15:48:41Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"1717556438\"/><nd ref=\"1717556503\"/><nd ref=\"4568857133\"/><nd ref=\"1717556447\"/><nd ref=\"1717556568\"/><nd ref=\"1717556438\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637737\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T09:23:10Z\" user=\"rene78\" uid=\"257555\">\n  <nd ref=\"1717556677\"/><nd ref=\"1717556626\"/><nd ref=\"1717556441\"/><nd ref=\"1717556545\"/><nd ref=\"1717556677\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637740\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T09:23:10Z\" user=\"rene78\" uid=\"257555\">\n  <nd ref=\"1717556466\"/><nd ref=\"1717556660\"/><nd ref=\"1717556457\"/><nd ref=\"1717556577\"/><nd ref=\"1717556683\"/><nd ref=\"1717556564\"/><nd ref=\"1717556653\"/><nd ref=\"1717556636\"/><nd ref=\"1717556540\"/><nd ref=\"1717556652\"/><nd ref=\"1717556466\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159637741\" visible=\"true\" version=\"2\" changeset=\"11307160\" timestamp=\"2012-04-15T09:23:11Z\" user=\"rene78\" uid=\"257555\">\n  <nd ref=\"1717556650\"/><nd ref=\"1717556434\"/><nd ref=\"1717556540\"/><nd ref=\"1717556636\"/><nd ref=\"1717556493\"/><nd ref=\"1717556427\"/><nd ref=\"1717556650\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159638484\" visible=\"true\" version=\"15\" changeset=\"69405864\" timestamp=\"2019-04-20T16:09:16Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"6418094127\"/><nd ref=\"6418094128\"/><nd ref=\"6418094129\"/><nd ref=\"6418094130\"/><nd ref=\"6418094131\"/><nd ref=\"6418094132\"/><nd ref=\"6418094133\"/><nd ref=\"6418094134\"/><nd ref=\"6418094135\"/><nd ref=\"6418094136\"/><nd ref=\"6418094137\"/><nd ref=\"6418094138\"/><nd ref=\"6418094139\"/><nd ref=\"6418094140\"/><nd ref=\"6418094141\"/><nd ref=\"6418094142\"/><nd ref=\"6418094143\"/><nd ref=\"6418094144\"/><nd ref=\"6418094145\"/><nd ref=\"6418094146\"/><nd ref=\"6418094147\"/><nd ref=\"6418094148\"/><nd ref=\"6418094149\"/><nd ref=\"6418094150\"/><nd ref=\"6418094151\"/><nd ref=\"6418094152\"/><nd ref=\"6418094153\"/><nd ref=\"6418094154\"/><nd ref=\"6418094155\"/><nd ref=\"6418094156\"/><nd ref=\"6418094157\"/><nd ref=\"6418094158\"/><nd ref=\"6418094159\"/><nd ref=\"6418094160\"/><nd ref=\"6418094161\"/><nd ref=\"6418094162\"/><nd ref=\"6418094163\"/><nd ref=\"6418094164\"/><nd ref=\"6418094165\"/><nd ref=\"6418094166\"/><nd ref=\"6418094167\"/><nd ref=\"6418094168\"/><nd ref=\"6418094169\"/><nd ref=\"6418094170\"/><nd ref=\"6418094171\"/><nd ref=\"6418094172\"/><nd ref=\"6418094173\"/><nd ref=\"6418094174\"/><nd ref=\"6418094175\"/><nd ref=\"6418094176\"/><nd ref=\"6418094177\"/><nd ref=\"6418094178\"/><nd ref=\"6418094179\"/><nd ref=\"6418094180\"/><nd ref=\"6418094181\"/><nd ref=\"6418094182\"/><nd ref=\"6418094183\"/><nd ref=\"6418094184\"/><nd ref=\"6418096085\"/><nd ref=\"6418096086\"/><nd ref=\"6418096087\"/><nd ref=\"6418096088\"/><nd ref=\"6418096089\"/><nd ref=\"6418096090\"/><nd ref=\"6418096091\"/><nd ref=\"6418096092\"/><nd ref=\"6418096093\"/><nd ref=\"6418096094\"/><nd ref=\"6418096095\"/><nd ref=\"6418096096\"/><nd ref=\"6418096097\"/><nd ref=\"6418096098\"/><nd ref=\"6418096099\"/><nd ref=\"6418096100\"/><nd ref=\"6418096101\"/><nd ref=\"6418096102\"/><nd ref=\"6418096103\"/><nd ref=\"6418096104\"/><nd ref=\"6418096105\"/><nd ref=\"6418095005\"/><nd ref=\"6418095006\"/><nd ref=\"6418095007\"/><nd ref=\"6418095008\"/><nd ref=\"6418095009\"/><nd ref=\"6418095010\"/><nd ref=\"6418095011\"/><nd ref=\"6418095012\"/><nd ref=\"6418095013\"/><nd ref=\"6418095014\"/><nd ref=\"6418095015\"/><nd ref=\"6418095016\"/><nd ref=\"6418095017\"/><nd ref=\"6418095018\"/><nd ref=\"6418095019\"/><nd ref=\"6418095020\"/><nd ref=\"6418095021\"/><nd ref=\"6418095022\"/><nd ref=\"6418095023\"/><nd ref=\"6418095024\"/><nd ref=\"6418095025\"/><nd ref=\"6418095026\"/><nd ref=\"6418095028\"/><nd ref=\"6418096391\"/><nd ref=\"1717564125\"/><nd ref=\"1717564086\"/><nd ref=\"1717564065\"/><nd ref=\"1717564113\"/><nd ref=\"1717564126\"/><nd ref=\"1717564066\"/><nd ref=\"1717564147\"/><nd ref=\"1717564070\"/><nd ref=\"1717564072\"/><nd ref=\"1717564124\"/><nd ref=\"1717564152\"/><nd ref=\"5175108863\"/><nd ref=\"1717564136\"/><nd ref=\"1717564129\"/><nd ref=\"5175108868\"/><nd ref=\"1717564091\"/><nd ref=\"6418094127\"/><tag k=\"landuse\" v=\"forest\"/>\n </way>\n <way id=\"159638788\" visible=\"true\" version=\"1\" changeset=\"11307160\" timestamp=\"2012-04-15T09:26:28Z\" user=\"rene78\" uid=\"257555\">\n  <nd ref=\"1717566344\"/><nd ref=\"1717566346\"/><nd ref=\"1717566351\"/><nd ref=\"1717566348\"/><nd ref=\"1717566344\"/><tag k=\"building\" v=\"yes\"/>\n </way>\n <way id=\"159638789\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:35Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"1717566345\"/><nd ref=\"1717566343\"/><nd ref=\"1717566354\"/><nd ref=\"1717566353\"/><nd ref=\"1717566345\"/><tag k=\"building\" v=\"yes\"/><tag k=\"building:levels\" v=\"1\"/><tag k=\"roof:levels\" v=\"1\"/><tag k=\"roof:material\" v=\"roof_tiles\"/><tag k=\"roof:shape\" v=\"pyramidal\"/>\n </way>\n <way id=\"159638790\" visible=\"true\" version=\"2\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:36Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"1717566350\"/><nd ref=\"1717566349\"/><nd ref=\"1717566347\"/><nd ref=\"1717566352\"/><nd ref=\"1717566350\"/><tag k=\"building\" v=\"yes\"/><tag k=\"building:levels\" v=\"1\"/><tag k=\"roof:levels\" v=\"1\"/><tag k=\"roof:material\" v=\"roof_tiles\"/><tag k=\"roof:shape\" v=\"pyramidal\"/>\n </way>\n <way id=\"162005451\" visible=\"true\" version=\"1\" changeset=\"11491444\" timestamp=\"2012-05-03T18:41:50Z\" user=\"cboothroyd\" uid=\"255802\">\n  <nd ref=\"1739490136\"/><nd ref=\"1422900685\"/><tag k=\"highway\" v=\"path\"/>\n </way>\n <way id=\"171984330\" visible=\"true\" version=\"12\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:56Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"1829420424\"/><nd ref=\"1873234962\"/><nd ref=\"1316095876\"/><nd ref=\"1829420444\"/><nd ref=\"1829606801\"/><nd ref=\"1829420432\"/><nd ref=\"1829420421\"/><nd ref=\"5145848430\"/><nd ref=\"1829420417\"/><nd ref=\"1829420424\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"12\"/><tag k=\"addr:postcode\" v=\"098271\"/><tag k=\"addr:street\" v=\"Sentosa Gateway\"/><tag k=\"building\" v=\"yes\"/><tag k=\"building:levels\" v=\"7\"/><tag k=\"image\" v=\"https://images.mapillary.com/H00qB2807YW4bLqbUqLbxw/thumb-2048.jpg\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/H00qB2807YW4bLqbUqLbxw\"/><tag k=\"name\" v=\"Festive Hotel\"/><tag k=\"name:en\" v=\"Festive Hotel\"/><tag k=\"name:zh\" v=\"节庆酒店\"/><tag k=\"operator\" v=\"Resorts World\"/><tag k=\"roof:colour\" v=\"#b4b7b1\"/><tag k=\"roof:material\" v=\"metal\"/><tag k=\"tourism\" v=\"hotel\"/><tag k=\"website\" v=\"https://www.rwsentosa.com/\"/>\n </way>\n <way id=\"176839461\" visible=\"true\" version=\"2\" changeset=\"59646385\" timestamp=\"2018-06-07T19:02:55Z\" user=\"Mapier\" uid=\"616884\">\n  <nd ref=\"1873234456\"/><nd ref=\"1873234457\"/><nd ref=\"1873234432\"/><nd ref=\"1873234418\"/><nd ref=\"1873234777\"/><nd ref=\"1873234822\"/><nd ref=\"1873234814\"/><nd ref=\"1873234859\"/><nd ref=\"1873234835\"/><nd ref=\"1873234732\"/><nd ref=\"1873234443\"/><nd ref=\"1873234455\"/><nd ref=\"1873234464\"/><nd ref=\"1873234463\"/><nd ref=\"1873234500\"/><nd ref=\"1873234506\"/><nd ref=\"1873234470\"/><nd ref=\"1873234468\"/><nd ref=\"1873234456\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"51\"/><tag k=\"addr:postcode\" v=\"099538\"/><tag k=\"addr:street\" v=\"Imbiah Walk\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Siloso Beach Resort\"/><tag k=\"name:en\" v=\"Siloso Beach Resort\"/><tag k=\"name:zh\" v=\"喜乐圣淘沙度假旅馆\"/><tag k=\"source\" v=\"Bing\"/><tag k=\"tourism\" v=\"hotel\"/><tag k=\"website\" v=\"www.silosobeachresort.com\"/>\n </way>\n <way id=\"176839464\" visible=\"true\" version=\"5\" changeset=\"70108984\" timestamp=\"2019-05-10T13:01:26Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"1873234821\"/><nd ref=\"5145931975\"/><nd ref=\"1873234836\"/><nd ref=\"5145931976\"/><nd ref=\"1873234844\"/><nd ref=\"5145931977\"/><nd ref=\"1873234841\"/><nd ref=\"5145931978\"/><nd ref=\"1873234828\"/><nd ref=\"5145931979\"/><nd ref=\"1873234803\"/><nd ref=\"5145931980\"/><nd ref=\"1873234794\"/><nd ref=\"5145931981\"/><nd ref=\"1873234799\"/><nd ref=\"5145931982\"/><nd ref=\"1873234821\"/><tag k=\"building\" v=\"yes\"/><tag k=\"height\" v=\"110\"/><tag k=\"image\" v=\"https://images.mapillary.com/Xo7nB4YlPWSyZBmXuSOPzQ/thumb-2048.jpg\"/><tag k=\"man_made\" v=\"tower\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/Xo7nB4YlPWSyZBmXuSOPzQ\"/><tag k=\"name\" v=\"Tiger Sky Tower\"/><tag k=\"name:zh\" v=\"Tiger 摩天塔\"/><tag k=\"old_name\" v=\"Carlsberg Sky Tower\"/><tag k=\"opening_hours\" v=\"09:00-21:00\"/><tag k=\"tourism\" v=\"attraction\"/><tag k=\"tower:type\" v=\"observation\"/><tag k=\"website\" v=\"http://www.skytower.com.sg/\"/><tag k=\"wikipedia\" v=\"en:Tiger Sky Tower\"/>\n </way>\n <way id=\"249144132\" visible=\"true\" version=\"6\" changeset=\"75129398\" timestamp=\"2019-10-01T06:09:44Z\" user=\"div006\" uid=\"8911933\">\n  <nd ref=\"2558468525\"/><nd ref=\"2558083049\"/><nd ref=\"2558083048\"/><nd ref=\"2558083045\"/><nd ref=\"2558083044\"/><nd ref=\"2558468523\"/><tag k=\"highway\" v=\"service\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"lanes:backward\" v=\"1\"/><tag k=\"lanes:forward\" v=\"1\"/>\n </way>\n <way id=\"249198509\" visible=\"true\" version=\"2\" changeset=\"52606037\" timestamp=\"2017-10-03T19:14:53Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"2558468525\"/><nd ref=\"2558468524\"/><nd ref=\"5145848424\"/><tag k=\"highway\" v=\"service\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"service\" v=\"parking_aisle\"/>\n </way>\n <way id=\"260774257\" visible=\"true\" version=\"3\" changeset=\"20467454\" timestamp=\"2014-02-09T16:07:18Z\" user=\"andi9876\" uid=\"1256497\">\n  <nd ref=\"603563258\"/><nd ref=\"1717537432\"/><nd ref=\"1717537329\"/><nd ref=\"1717537412\"/><nd ref=\"1422900680\"/><nd ref=\"1717537415\"/><nd ref=\"1422900647\"/><nd ref=\"1717537357\"/><nd ref=\"1422900664\"/><nd ref=\"1717537417\"/><nd ref=\"1422900635\"/><nd ref=\"1422900654\"/><nd ref=\"1717537325\"/><nd ref=\"1717537438\"/><nd ref=\"1717537332\"/><nd ref=\"1422900673\"/><nd ref=\"1422900642\"/><nd ref=\"1717537324\"/><nd ref=\"1422900645\"/><nd ref=\"1717537391\"/><nd ref=\"1422900660\"/><nd ref=\"1717537330\"/><nd ref=\"1422900637\"/><nd ref=\"1717537490\"/><nd ref=\"1422900655\"/><nd ref=\"1422900662\"/><nd ref=\"1717537353\"/><nd ref=\"1422900640\"/><nd ref=\"1717537326\"/><nd ref=\"1717537337\"/><nd ref=\"1422900657\"/><nd ref=\"1717537411\"/><nd ref=\"1717537363\"/><nd ref=\"1422900676\"/><nd ref=\"1422900653\"/><nd ref=\"1422900666\"/><nd ref=\"1422900641\"/><nd ref=\"2663353454\"/><tag k=\"cutting\" v=\"no\"/><tag k=\"highway\" v=\"track\"/><tag k=\"name\" v=\"Luge Dragon Trail\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"260774258\" visible=\"true\" version=\"4\" changeset=\"69415093\" timestamp=\"2019-04-21T02:31:37Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"2663348091\"/><nd ref=\"2663348090\"/><nd ref=\"2663348092\"/><nd ref=\"2663348087\"/><nd ref=\"2663348088\"/><nd ref=\"2663348095\"/><nd ref=\"2663348094\"/><nd ref=\"2663348089\"/><nd ref=\"2663348085\"/><nd ref=\"2663348093\"/><nd ref=\"2663348086\"/><nd ref=\"2663348091\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"45\"/><tag k=\"addr:postcode\" v=\"099003\"/><tag k=\"addr:street\" v=\"Siloso Beach Walk\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Skyline Luge Office\"/><tag k=\"name:zh\" v=\"斜坡滑車&amp;空中吊椅\"/>\n </way>\n <way id=\"260777732\" visible=\"true\" version=\"8\" changeset=\"65020111\" timestamp=\"2018-11-29T18:23:31Z\" user=\"naik4\" uid=\"7671613\">\n  <nd ref=\"5145931993\"/><nd ref=\"2663373909\"/><nd ref=\"2663373916\"/><nd ref=\"5145931994\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"noname\" v=\"yes\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"428880136\" visible=\"true\" version=\"3\" changeset=\"45230182\" timestamp=\"2017-01-16T23:18:15Z\" user=\"MyWayOrNoHighway\" uid=\"4351550\">\n  <nd ref=\"4279963872\"/><nd ref=\"4568857131\"/><nd ref=\"4279963873\"/><nd ref=\"4279963874\"/><nd ref=\"4279963875\"/><tag k=\"bicycle\" v=\"no\"/><tag k=\"bridge\" v=\"yes\"/><tag k=\"highway\" v=\"path\"/><tag k=\"horse\" v=\"no\"/><tag k=\"layer\" v=\"1\"/><tag k=\"surface\" v=\"metal\"/>\n </way>\n <way id=\"461110977\" visible=\"true\" version=\"2\" changeset=\"46806385\" timestamp=\"2017-03-13T09:21:29Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4566570120\"/><nd ref=\"4566570124\"/><nd ref=\"4566570125\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"461380736\" visible=\"true\" version=\"2\" changeset=\"45230182\" timestamp=\"2017-01-16T23:18:15Z\" user=\"MyWayOrNoHighway\" uid=\"4351550\">\n  <nd ref=\"4568857131\"/><nd ref=\"4568857132\"/><tag k=\"bridge\" v=\"yes\"/><tag k=\"highway\" v=\"path\"/><tag k=\"layer\" v=\"1\"/><tag k=\"surface\" v=\"metal\"/>\n </way>\n <way id=\"461380737\" visible=\"true\" version=\"7\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:42Z\" user=\"cartogram\" uid=\"6095772\">\n  <nd ref=\"4568857133\"/><nd ref=\"5145849314\"/><nd ref=\"5138971723\"/><nd ref=\"5286594350\"/><nd ref=\"4731913778\"/><nd ref=\"1717541891\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"478164185\" visible=\"true\" version=\"13\" changeset=\"64183540\" timestamp=\"2018-11-05T02:53:35Z\" user=\"Neela1\" uid=\"7795604\">\n  <nd ref=\"5175384350\"/><nd ref=\"1380555963\"/><nd ref=\"5151991097\"/><nd ref=\"1868203196\"/><nd ref=\"5151991098\"/><nd ref=\"1868203197\"/><nd ref=\"1868203199\"/><nd ref=\"471491299\"/><nd ref=\"471491300\"/><nd ref=\"1868203201\"/><nd ref=\"471491301\"/><nd ref=\"1868203212\"/><nd ref=\"471491302\"/><nd ref=\"1868203214\"/><nd ref=\"1868203213\"/><nd ref=\"5151991100\"/><nd ref=\"5151991099\"/><nd ref=\"471491303\"/><nd ref=\"5209288660\"/><nd ref=\"471491305\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"maxspeed\" v=\"40\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"478164981\" visible=\"true\" version=\"14\" changeset=\"65147309\" timestamp=\"2018-12-04T05:36:15Z\" user=\"poornima1\" uid=\"7868172\">\n  <nd ref=\"471491310\"/><nd ref=\"5151991080\"/><nd ref=\"5151991075\"/><nd ref=\"1717541904\"/><nd ref=\"5151991079\"/><nd ref=\"471491312\"/><nd ref=\"5209288657\"/><nd ref=\"1717541881\"/><tag k=\"access\" v=\"private\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/><tag k=\"turn:lanes\" v=\"through\"/>\n </way>\n <way id=\"478355767\" visible=\"true\" version=\"5\" changeset=\"60730660\" timestamp=\"2018-07-15T12:22:16Z\" user=\"gunsno\" uid=\"7277094\">\n  <nd ref=\"4715825892\"/><nd ref=\"4715825893\"/><nd ref=\"4715825894\"/><nd ref=\"4715825895\"/><nd ref=\"4715825896\"/><nd ref=\"4731913779\"/><nd ref=\"4715825897\"/><nd ref=\"4715825892\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:street\" v=\"Imbiah Road\"/><tag k=\"building\" v=\"yes\"/><tag k=\"name\" v=\"Skyline Luge Sentosa\"/><tag k=\"opening_hours\" v=\"Mo-Su 10:00-21:30\"/><tag k=\"source\" v=\"Kaart Ground Survey 2017\"/><tag k=\"tourism\" v=\"theme_park\"/><tag k=\"website\" v=\"http://www.skylineluge.sg\"/>\n </way>\n <way id=\"480159876\" visible=\"true\" version=\"4\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:09Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"4731770684\"/><nd ref=\"4731770687\"/><nd ref=\"4731770686\"/><nd ref=\"4731770685\"/><nd ref=\"4731770684\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"60\"/><tag k=\"addr:postcode\" v=\"098466\"/><tag k=\"addr:street\" v=\"Imbiah Road\"/><tag k=\"building\" v=\"yes\"/><tag k=\"building:levels\" v=\"2\"/><tag k=\"image\" v=\"https://images.mapillary.com/48gXOPD0CLfUnPMiflSVbQ/thumb-2048.jpg\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/48gXOPD0CLfUnPMiflSVbQ\"/>\n </way>\n <way id=\"480175915\" visible=\"true\" version=\"2\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:30Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"2362722968\"/><nd ref=\"5145931983\"/><nd ref=\"4566570123\"/><nd ref=\"5145931984\"/><nd ref=\"4566570122\"/><nd ref=\"5145931985\"/><nd ref=\"4566570121\"/><nd ref=\"4566570120\"/><nd ref=\"5145931986\"/><nd ref=\"4566570119\"/><nd ref=\"5145931987\"/><nd ref=\"4566570118\"/><nd ref=\"5145931988\"/><nd ref=\"2362722966\"/><nd ref=\"5145931989\"/><nd ref=\"5145931990\"/><nd ref=\"2362722968\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"480175917\" visible=\"true\" version=\"3\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:30Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"2362722968\"/><nd ref=\"4736068646\"/><nd ref=\"2362722970\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"480175918\" visible=\"true\" version=\"5\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:30Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"4731913768\"/><nd ref=\"4731913769\"/><nd ref=\"4731913770\"/><nd ref=\"4731913771\"/><nd ref=\"2362722970\"/><nd ref=\"2362722971\"/><nd ref=\"2362722961\"/><nd ref=\"2362722963\"/><nd ref=\"2362722964\"/><nd ref=\"2362722967\"/><nd ref=\"2362722969\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"480175920\" visible=\"true\" version=\"3\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:30Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"4731913776\"/><nd ref=\"5209288657\"/><nd ref=\"5209288656\"/><nd ref=\"5138672446\"/><tag k=\"footway\" v=\"crossing\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"480175922\" visible=\"true\" version=\"6\" changeset=\"52496545\" timestamp=\"2017-09-29T23:26:08Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"5137340960\"/><nd ref=\"4733474816\"/><nd ref=\"4733474815\"/><nd ref=\"4731913775\"/><nd ref=\"4733474814\"/><nd ref=\"4731913776\"/><nd ref=\"4733474813\"/><nd ref=\"4733474812\"/><nd ref=\"5138672444\"/><nd ref=\"4733474811\"/><nd ref=\"4733474810\"/><nd ref=\"4733410887\"/><nd ref=\"5137340959\"/><nd ref=\"4731913777\"/><nd ref=\"4733474809\"/><nd ref=\"4731913778\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"480175924\" visible=\"true\" version=\"1\" changeset=\"46806385\" timestamp=\"2017-03-13T09:21:29Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4566570124\"/><nd ref=\"4731913778\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"480175926\" visible=\"true\" version=\"1\" changeset=\"46806385\" timestamp=\"2017-03-13T09:21:29Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4731913779\"/><nd ref=\"4566570125\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"480183445\" visible=\"true\" version=\"2\" changeset=\"46868015\" timestamp=\"2017-03-15T12:44:34Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4731913768\"/><nd ref=\"4736068642\"/><nd ref=\"4731977867\"/><nd ref=\"4731977866\"/><nd ref=\"4731977865\"/><nd ref=\"4731977864\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"480324970\" visible=\"true\" version=\"5\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:30Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"4731913768\"/><nd ref=\"4733074345\"/><nd ref=\"4733403533\"/><nd ref=\"4733403769\"/><nd ref=\"4733403538\"/><nd ref=\"4733074344\"/><nd ref=\"4733403532\"/><nd ref=\"4733074353\"/><nd ref=\"2362722969\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"480338714\" visible=\"true\" version=\"1\" changeset=\"46830384\" timestamp=\"2017-03-14T05:33:32Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4733074345\"/><nd ref=\"4733403527\"/><nd ref=\"4733403528\"/><nd ref=\"4733403529\"/><nd ref=\"4733403530\"/><nd ref=\"4733403531\"/><nd ref=\"4733403532\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"480338715\" visible=\"true\" version=\"1\" changeset=\"46830384\" timestamp=\"2017-03-14T05:33:32Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4733403533\"/><nd ref=\"4733403534\"/><nd ref=\"4733403539\"/><nd ref=\"4733403535\"/><nd ref=\"4733403536\"/><nd ref=\"4733403537\"/><nd ref=\"4733403538\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"480338716\" visible=\"true\" version=\"1\" changeset=\"46830384\" timestamp=\"2017-03-14T05:33:32Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"2362722970\"/><nd ref=\"4733403528\"/><nd ref=\"4733403539\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"480338771\" visible=\"true\" version=\"1\" changeset=\"46830399\" timestamp=\"2017-03-14T05:34:30Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4733403539\"/><nd ref=\"4733403769\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"480339638\" visible=\"true\" version=\"1\" changeset=\"46830546\" timestamp=\"2017-03-14T05:46:56Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4733410887\"/><nd ref=\"4733410886\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"480632927\" visible=\"true\" version=\"3\" changeset=\"66408527\" timestamp=\"2019-01-17T21:33:16Z\" user=\"KingRichardV\" uid=\"9004577\">\n  <nd ref=\"4736068642\"/><nd ref=\"6217274958\"/><nd ref=\"4736068641\"/><nd ref=\"4736068652\"/><nd ref=\"4736068651\"/><nd ref=\"4736068650\"/><nd ref=\"4736068649\"/><nd ref=\"4736068648\"/><nd ref=\"4736068647\"/><nd ref=\"4736068646\"/><nd ref=\"4736068645\"/><nd ref=\"4736068644\"/><nd ref=\"4736068643\"/><nd ref=\"4736068642\"/><tag k=\"area\" v=\"yes\"/><tag k=\"highway\" v=\"pedestrian\"/>\n </way>\n <way id=\"482407210\" visible=\"true\" version=\"7\" changeset=\"67122864\" timestamp=\"2019-02-12T07:39:45Z\" user=\"anu_smita\" uid=\"8967885\">\n  <nd ref=\"4752282589\"/><nd ref=\"471491310\"/><tag k=\"access\" v=\"private\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/>\n </way>\n <way id=\"482407211\" visible=\"true\" version=\"17\" changeset=\"72006528\" timestamp=\"2019-07-08T10:23:59Z\" user=\"div006\" uid=\"8911933\">\n  <nd ref=\"1867526773\"/><nd ref=\"5146232329\"/><nd ref=\"4752282585\"/><nd ref=\"4752282593\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"maxheight\" v=\"4.5\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"482416616\" visible=\"true\" version=\"6\" changeset=\"72006528\" timestamp=\"2019-07-08T10:23:59Z\" user=\"div006\" uid=\"8911933\">\n  <nd ref=\"4752344968\"/><nd ref=\"5209288658\"/><nd ref=\"4752344935\"/><nd ref=\"4752344936\"/><nd ref=\"4752344937\"/><nd ref=\"4752344938\"/><nd ref=\"4752344939\"/><nd ref=\"4752344940\"/><nd ref=\"4752344941\"/><nd ref=\"4752344942\"/><nd ref=\"4752344943\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"maxspeed\" v=\"40\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"482416618\" visible=\"true\" version=\"13\" changeset=\"72006528\" timestamp=\"2019-07-08T10:23:59Z\" user=\"div006\" uid=\"8911933\">\n  <nd ref=\"4752344963\"/><nd ref=\"5151991087\"/><nd ref=\"5151991086\"/><nd ref=\"5146232330\"/><nd ref=\"4752344966\"/><nd ref=\"5151991093\"/><nd ref=\"4752344967\"/><nd ref=\"5175066692\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"maxheight\" v=\"4.5\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/><tag k=\"turn:lanes\" v=\"through;right\"/>\n </way>\n <way id=\"526481397\" visible=\"true\" version=\"1\" changeset=\"52243602\" timestamp=\"2017-09-21T13:11:25Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"4731913779\"/><nd ref=\"4715825895\"/><nd ref=\"5119642628\"/><nd ref=\"5119642629\"/><nd ref=\"1422900685\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"526495379\" visible=\"true\" version=\"6\" changeset=\"52610584\" timestamp=\"2017-10-03T23:01:32Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5119744356\"/><nd ref=\"5119743816\"/><nd ref=\"5146201911\"/><nd ref=\"5119743817\"/><nd ref=\"5134029815\"/><nd ref=\"5119743818\"/><nd ref=\"5134029936\"/><nd ref=\"5134029954\"/><nd ref=\"5119743819\"/><nd ref=\"5119743820\"/><nd ref=\"5119744321\"/><nd ref=\"5119744322\"/><nd ref=\"5119744323\"/><nd ref=\"5134029942\"/><nd ref=\"5119744325\"/><nd ref=\"5119744326\"/><nd ref=\"5134030005\"/><nd ref=\"5119744327\"/><nd ref=\"5134030022\"/><nd ref=\"5119744328\"/><nd ref=\"5119744329\"/><nd ref=\"5119743815\"/><nd ref=\"5145931971\"/><nd ref=\"5119743814\"/><nd ref=\"5119743813\"/><nd ref=\"5119743812\"/><nd ref=\"5134030015\"/><nd ref=\"5119743811\"/><nd ref=\"5119743810\"/><nd ref=\"5119743809\"/><nd ref=\"5119743808\"/><nd ref=\"5119743807\"/><nd ref=\"5134030028\"/><nd ref=\"5134029805\"/><nd ref=\"5134029943\"/><nd ref=\"5134029963\"/><nd ref=\"5119743805\"/><nd ref=\"5134029984\"/><nd ref=\"5134030001\"/><nd ref=\"5119743806\"/><nd ref=\"5119743804\"/><nd ref=\"4279963875\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"name\" v=\"Imbiah Trail\"/>\n </way>\n <way id=\"526495380\" visible=\"true\" version=\"5\" changeset=\"52610966\" timestamp=\"2017-10-03T23:41:16Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"4279963875\"/><nd ref=\"603563344\"/><nd ref=\"5138672448\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"526499229\" visible=\"true\" version=\"2\" changeset=\"71136817\" timestamp=\"2019-06-11T11:17:16Z\" user=\"tommystyle84\" uid=\"438632\">\n  <nd ref=\"5119744356\"/><nd ref=\"6539353057\"/><nd ref=\"5119744357\"/><nd ref=\"5119744358\"/><nd ref=\"5119744359\"/><nd ref=\"5119744360\"/><nd ref=\"5119744361\"/><nd ref=\"4279963875\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528240337\" visible=\"true\" version=\"2\" changeset=\"52496567\" timestamp=\"2017-09-29T23:28:54Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"5138672449\"/><nd ref=\"1717556458\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528240338\" visible=\"true\" version=\"3\" changeset=\"52606119\" timestamp=\"2017-10-03T19:16:09Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5133972841\"/><nd ref=\"4568857132\"/><nd ref=\"5133972842\"/><nd ref=\"5133972843\"/><nd ref=\"5133972844\"/><nd ref=\"5138971722\"/><nd ref=\"5133972846\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528240340\" visible=\"true\" version=\"11\" changeset=\"66895408\" timestamp=\"2019-02-04T07:56:15Z\" user=\"harishpuppala\" uid=\"7446646\">\n  <nd ref=\"5138650785\"/><nd ref=\"4752282588\"/><nd ref=\"4752282604\"/><nd ref=\"4752282603\"/><nd ref=\"4752282602\"/><nd ref=\"4752282601\"/><nd ref=\"4752282600\"/><nd ref=\"4752282599\"/><nd ref=\"4752282598\"/><nd ref=\"4752282597\"/><nd ref=\"4752344963\"/><nd ref=\"5151991088\"/><nd ref=\"5151991090\"/><nd ref=\"5151991089\"/><nd ref=\"5151991092\"/><nd ref=\"5151991091\"/><nd ref=\"4752282593\"/><nd ref=\"4752282591\"/><nd ref=\"4752282590\"/><nd ref=\"4752282589\"/><nd ref=\"5151991081\"/><nd ref=\"5151991082\"/><nd ref=\"5138650785\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"junction\" v=\"roundabout\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"528246258\" visible=\"true\" version=\"3\" changeset=\"53115984\" timestamp=\"2017-10-20T23:15:22Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"4752207927\"/><nd ref=\"1873234299\"/><nd ref=\"5134024625\"/><nd ref=\"5134024644\"/><nd ref=\"5134024641\"/><nd ref=\"5134024640\"/><nd ref=\"5134024636\"/><nd ref=\"5134024639\"/><nd ref=\"5134024634\"/><nd ref=\"5134024627\"/><nd ref=\"5138682822\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528897519\" visible=\"true\" version=\"16\" changeset=\"72006528\" timestamp=\"2019-07-08T10:23:59Z\" user=\"div006\" uid=\"8911933\">\n  <nd ref=\"613981020\"/><nd ref=\"5134030051\"/><nd ref=\"5209288650\"/><nd ref=\"471491355\"/><nd ref=\"1867272120\"/><nd ref=\"5209288652\"/><nd ref=\"4230810144\"/><nd ref=\"471491353\"/><nd ref=\"471491352\"/><nd ref=\"1867272121\"/><nd ref=\"471491351\"/><nd ref=\"471491350\"/><nd ref=\"471491349\"/><nd ref=\"4230810143\"/><nd ref=\"471491348\"/><nd ref=\"471491347\"/><nd ref=\"1867272119\"/><nd ref=\"471491346\"/><nd ref=\"5209288654\"/><nd ref=\"471491345\"/><nd ref=\"4230810142\"/><nd ref=\"471491344\"/><nd ref=\"4230810141\"/><nd ref=\"471491343\"/><nd ref=\"471491342\"/><nd ref=\"471491341\"/><nd ref=\"471491340\"/><nd ref=\"471491339\"/><nd ref=\"4230810140\"/><nd ref=\"471491338\"/><nd ref=\"6596068720\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"maxspeed\" v=\"40\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"528898916\" visible=\"true\" version=\"4\" changeset=\"65163560\" timestamp=\"2018-12-04T14:58:49Z\" user=\"rudroju\" uid=\"7804792\">\n  <nd ref=\"5138650784\"/><nd ref=\"5138650785\"/><tag k=\"access\" v=\"private\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/>\n </way>\n <way id=\"528898917\" visible=\"true\" version=\"10\" changeset=\"65163560\" timestamp=\"2018-12-04T14:58:49Z\" user=\"rudroju\" uid=\"7804792\">\n  <nd ref=\"5138650779\"/><nd ref=\"5138650780\"/><nd ref=\"5151991077\"/><nd ref=\"5209288656\"/><nd ref=\"5138650781\"/><nd ref=\"5138650782\"/><nd ref=\"5151991078\"/><nd ref=\"5151991076\"/><nd ref=\"5138650783\"/><nd ref=\"5138650784\"/><tag k=\"access\" v=\"private\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"528900905\" visible=\"true\" version=\"3\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\">\n  <nd ref=\"1717541894\"/><nd ref=\"1717541895\"/><nd ref=\"1739490140\"/><nd ref=\"1717541891\"/><nd ref=\"4566570125\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"service\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"lanes:backward\" v=\"1\"/><tag k=\"lanes:forward\" v=\"1\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"surface\" v=\"asphalt\"/><tag k=\"turn:lanes:backward\" v=\"left\"/><tag k=\"turn:lanes:forward\" v=\"through\"/>\n </way>\n <way id=\"528901320\" visible=\"true\" version=\"2\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:44Z\" user=\"happy-camper\" uid=\"6346054\">\n  <nd ref=\"5278186712\"/><nd ref=\"5138672444\"/><nd ref=\"5138672445\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528901322\" visible=\"true\" version=\"3\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:32Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5138672448\"/><nd ref=\"5146232330\"/><nd ref=\"5146232329\"/><nd ref=\"5138672449\"/><tag k=\"footway\" v=\"crossing\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528902956\" visible=\"true\" version=\"9\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\">\n  <nd ref=\"5175384349\"/><nd ref=\"2558083046\"/><nd ref=\"2558083050\"/><nd ref=\"2558083051\"/><nd ref=\"2558083052\"/><nd ref=\"6728983057\"/><tag k=\"highway\" v=\"service\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"lanes:backward\" v=\"1\"/><tag k=\"lanes:forward\" v=\"1\"/>\n </way>\n <way id=\"528904849\" visible=\"true\" version=\"2\" changeset=\"52501454\" timestamp=\"2017-09-30T07:31:12Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"5138693281\"/><nd ref=\"5139019659\"/><nd ref=\"5138693280\"/><nd ref=\"5138693279\"/><nd ref=\"5138693278\"/><nd ref=\"5138672448\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528904857\" visible=\"true\" version=\"1\" changeset=\"52497070\" timestamp=\"2017-09-30T00:23:42Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"5138693286\"/><nd ref=\"5138693285\"/><nd ref=\"5138693284\"/><nd ref=\"5138693283\"/><nd ref=\"5138693282\"/><nd ref=\"5138693281\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528906080\" visible=\"true\" version=\"1\" changeset=\"52497277\" timestamp=\"2017-09-30T00:46:38Z\" user=\"pushian\" uid=\"4989626\">\n  <nd ref=\"5138682822\"/><nd ref=\"5138710152\"/><nd ref=\"5138710151\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528906082\" visible=\"true\" version=\"4\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:33Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5137340960\"/><nd ref=\"5138672447\"/><nd ref=\"5138672449\"/><nd ref=\"5138693277\"/><nd ref=\"5145931939\"/><nd ref=\"5138693276\"/><nd ref=\"5145931938\"/><nd ref=\"5138693274\"/><nd ref=\"5139015612\"/><nd ref=\"5138710164\"/><nd ref=\"5138710163\"/><nd ref=\"5138710162\"/><nd ref=\"5138710161\"/><nd ref=\"5138710160\"/><nd ref=\"5145931972\"/><nd ref=\"5138710158\"/><nd ref=\"5138710157\"/><nd ref=\"5138710156\"/><nd ref=\"5138710155\"/><nd ref=\"5138710154\"/><nd ref=\"5138710153\"/><nd ref=\"5209288645\"/><nd ref=\"5138682822\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528906088\" visible=\"true\" version=\"4\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:33Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5138693319\"/><nd ref=\"5138693318\"/><nd ref=\"5138693317\"/><nd ref=\"5138693316\"/><nd ref=\"5138693315\"/><nd ref=\"5138693314\"/><nd ref=\"5138693313\"/><nd ref=\"5138693312\"/><nd ref=\"5138693311\"/><nd ref=\"5139019660\"/><nd ref=\"5138693310\"/><nd ref=\"5145931942\"/><nd ref=\"5138693273\"/><nd ref=\"5145931941\"/><nd ref=\"5145931940\"/><nd ref=\"5138693271\"/><nd ref=\"5138693270\"/><nd ref=\"5139015613\"/><nd ref=\"5138710177\"/><nd ref=\"5138710176\"/><nd ref=\"5138710175\"/><nd ref=\"5145931974\"/><nd ref=\"5138710174\"/><nd ref=\"5138710172\"/><nd ref=\"5145931973\"/><nd ref=\"5138710171\"/><nd ref=\"5138710170\"/><nd ref=\"5138710169\"/><nd ref=\"5138710168\"/><nd ref=\"5138710167\"/><nd ref=\"5138710166\"/><nd ref=\"5138710165\"/><nd ref=\"5138710150\"/><nd ref=\"5209288647\"/><nd ref=\"5138710149\"/><nd ref=\"5138710148\"/><nd ref=\"5138710147\"/><nd ref=\"5138710145\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528929961\" visible=\"true\" version=\"2\" changeset=\"52606119\" timestamp=\"2017-10-03T19:16:10Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5138971722\"/><nd ref=\"5145849313\"/><nd ref=\"5138971723\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528934981\" visible=\"true\" version=\"2\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:33Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5139015612\"/><nd ref=\"5209288660\"/><nd ref=\"5138638008\"/><nd ref=\"5139015613\"/><tag k=\"footway\" v=\"crossing\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"528934982\" visible=\"true\" version=\"2\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:42Z\" user=\"cartogram\" uid=\"6095772\">\n  <nd ref=\"5139015614\"/><nd ref=\"5139015615\"/><nd ref=\"5286594351\"/><nd ref=\"5139015616\"/><nd ref=\"5286594356\"/><nd ref=\"5139015617\"/><nd ref=\"5139015618\"/><nd ref=\"5139015619\"/><nd ref=\"5139015614\"/><tag k=\"area\" v=\"yes\"/><tag k=\"highway\" v=\"pedestrian\"/>\n </way>\n <way id=\"528935623\" visible=\"true\" version=\"2\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:33Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5139019659\"/><nd ref=\"5209288658\"/><nd ref=\"5209288659\"/><nd ref=\"5139019660\"/><tag k=\"footway\" v=\"crossing\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"529787039\" visible=\"true\" version=\"1\" changeset=\"52607303\" timestamp=\"2017-10-03T19:55:27Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"4731913772\"/><nd ref=\"5145931987\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"530020436\" visible=\"true\" version=\"9\" changeset=\"73290653\" timestamp=\"2019-08-13T05:28:50Z\" user=\"udaykiran3\" uid=\"7671560\">\n  <nd ref=\"5138650779\"/><nd ref=\"5151991083\"/><nd ref=\"5151991084\"/><nd ref=\"5151991085\"/><nd ref=\"1717541881\"/><nd ref=\"5138672445\"/><nd ref=\"1717541877\"/><nd ref=\"1717541876\"/><nd ref=\"1717541879\"/><nd ref=\"1717541902\"/><nd ref=\"1717541873\"/><nd ref=\"1717541890\"/><nd ref=\"1717541897\"/><nd ref=\"1717541894\"/><nd ref=\"1717541888\"/><nd ref=\"1717541892\"/><nd ref=\"1717541874\"/><nd ref=\"1717541878\"/><nd ref=\"1717541896\"/><nd ref=\"1717541883\"/><nd ref=\"5138650779\"/><tag k=\"access\" v=\"private\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"junction\" v=\"roundabout\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"533417988\" visible=\"true\" version=\"1\" changeset=\"53046295\" timestamp=\"2017-10-18T17:28:15Z\" user=\"CapAhab\" uid=\"6172866\">\n  <nd ref=\"4752344968\"/><nd ref=\"5138638004\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"maxspeed\" v=\"40\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"533417989\" visible=\"true\" version=\"4\" changeset=\"66635979\" timestamp=\"2019-01-25T15:58:10Z\" user=\"sravan7\" uid=\"8911008\">\n  <nd ref=\"471491305\"/><nd ref=\"5145931993\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/><tag k=\"turn:lanes\" v=\"left;right\"/>\n </way>\n <way id=\"533417990\" visible=\"true\" version=\"4\" changeset=\"65010043\" timestamp=\"2018-11-29T13:22:14Z\" user=\"naik4\" uid=\"7671613\">\n  <nd ref=\"5145931993\"/><nd ref=\"5175392808\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"533462931\" visible=\"true\" version=\"4\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\">\n  <nd ref=\"2558083041\"/><nd ref=\"5209288647\"/><nd ref=\"5175384349\"/><tag k=\"highway\" v=\"service\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"lanes:backward\" v=\"1\"/><tag k=\"lanes:forward\" v=\"1\"/><tag k=\"turn:lanes:backward\" v=\"left\"/><tag k=\"turn:lanes:forward\" v=\"through\"/>\n </way>\n <way id=\"533463813\" visible=\"true\" version=\"2\" changeset=\"64005613\" timestamp=\"2018-10-30T09:56:45Z\" user=\"Ramanyam\" uid=\"7671600\">\n  <nd ref=\"5138638004\"/><nd ref=\"5175392808\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"maxheight\" v=\"4.5\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"538105766\" visible=\"true\" version=\"1\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:28Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"5138672446\"/><nd ref=\"4731770686\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"538105767\" visible=\"true\" version=\"1\" changeset=\"53491829\" timestamp=\"2017-11-03T22:53:28Z\" user=\"Evandering\" uid=\"6172527\">\n  <nd ref=\"1717556688\"/><nd ref=\"4731913776\"/><tag k=\"highway\" v=\"footway\"/><tag k=\"shelter\" v=\"yes\"/>\n </way>\n <way id=\"538105768\" visible=\"true\" version=\"2\" changeset=\"64183540\" timestamp=\"2018-11-05T02:53:35Z\" user=\"Neela1\" uid=\"7795604\">\n  <nd ref=\"5138638004\"/><nd ref=\"5138638008\"/><nd ref=\"5138638009\"/><nd ref=\"5151991094\"/><nd ref=\"5138638010\"/><nd ref=\"5138638011\"/><nd ref=\"5138638012\"/><nd ref=\"5138638013\"/><nd ref=\"5138638014\"/><nd ref=\"5138638015\"/><nd ref=\"5138638016\"/><nd ref=\"5138638017\"/><nd ref=\"5138638018\"/><nd ref=\"5138638019\"/><nd ref=\"5151991095\"/><nd ref=\"5138638020\"/><nd ref=\"1868203191\"/><nd ref=\"2558083041\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"maxspeed\" v=\"40\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"546165469\" visible=\"true\" version=\"1\" changeset=\"54547742\" timestamp=\"2017-12-11T18:51:42Z\" user=\"happy-camper\" uid=\"6346054\">\n  <nd ref=\"1717541875\"/><nd ref=\"5278186712\"/><tag k=\"covered\" v=\"yes\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"547131784\" visible=\"true\" version=\"1\" changeset=\"54665653\" timestamp=\"2017-12-15T21:14:42Z\" user=\"cartogram\" uid=\"6095772\">\n  <nd ref=\"5137340959\"/><nd ref=\"5286594356\"/><nd ref=\"5286594355\"/><nd ref=\"5286594354\"/><nd ref=\"5286594353\"/><nd ref=\"5286594352\"/><nd ref=\"5286594351\"/><nd ref=\"5286594350\"/><tag k=\"highway\" v=\"footway\"/>\n </way>\n <way id=\"639335710\" visible=\"true\" version=\"1\" changeset=\"64005613\" timestamp=\"2018-10-30T09:56:45Z\" user=\"Ramanyam\" uid=\"7671600\">\n  <nd ref=\"5175392808\"/><nd ref=\"5145931994\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"maxheight\" v=\"4.5\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"639335711\" visible=\"true\" version=\"2\" changeset=\"72006528\" timestamp=\"2019-07-08T10:23:59Z\" user=\"div006\" uid=\"8911933\">\n  <nd ref=\"5175066692\"/><nd ref=\"4752344968\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"maxheight\" v=\"4.5\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"639335779\" visible=\"true\" version=\"2\" changeset=\"65016111\" timestamp=\"2018-11-29T16:25:22Z\" user=\"sai_sri\" uid=\"8911951\">\n  <nd ref=\"5175392808\"/><nd ref=\"5175066692\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"noname\" v=\"yes\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"646425420\" visible=\"true\" version=\"2\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\">\n  <nd ref=\"4566570125\"/><nd ref=\"1717541889\"/><nd ref=\"1717541898\"/><nd ref=\"1717541893\"/><nd ref=\"1717541884\"/><nd ref=\"1717541885\"/><nd ref=\"1717537333\"/><nd ref=\"1717537429\"/><nd ref=\"4731977868\"/><tag k=\"highway\" v=\"service\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"lanes:backward\" v=\"1\"/><tag k=\"lanes:forward\" v=\"1\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"650732280\" visible=\"true\" version=\"2\" changeset=\"67086910\" timestamp=\"2019-02-11T03:03:09Z\" user=\"daram2\" uid=\"8967880\">\n  <nd ref=\"5145931994\"/><nd ref=\"2663373914\"/><nd ref=\"1867526773\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"name\" v=\"Imbiah Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/>\n </way>\n <way id=\"684974066\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:29Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"6418091522\"/><nd ref=\"6418092489\"/><nd ref=\"6418092488\"/><nd ref=\"6418092487\"/><nd ref=\"6418092486\"/><nd ref=\"6418092485\"/><nd ref=\"6418091584\"/><nd ref=\"6418091583\"/><nd ref=\"6418091582\"/><nd ref=\"6418091581\"/><nd ref=\"6418091580\"/><nd ref=\"6418091579\"/><nd ref=\"6418091578\"/><nd ref=\"6418091577\"/><nd ref=\"6418091576\"/><nd ref=\"6418091575\"/><nd ref=\"6418091574\"/><nd ref=\"6418091573\"/><nd ref=\"6418091572\"/><nd ref=\"6418091571\"/><nd ref=\"6418091570\"/><nd ref=\"6418091569\"/><nd ref=\"6418091568\"/><nd ref=\"6418091567\"/><nd ref=\"6418091566\"/><nd ref=\"6418091565\"/><nd ref=\"6418091564\"/><nd ref=\"6418091563\"/><nd ref=\"6418091562\"/><nd ref=\"6418091561\"/><nd ref=\"6418091560\"/><nd ref=\"6418091559\"/><nd ref=\"6418091558\"/><nd ref=\"6418091557\"/><nd ref=\"6418091556\"/><nd ref=\"6418091555\"/><nd ref=\"6418091554\"/><nd ref=\"6418091553\"/><nd ref=\"6418091552\"/><nd ref=\"6418091551\"/><nd ref=\"6418091550\"/><nd ref=\"6418091549\"/><nd ref=\"6418091548\"/><nd ref=\"6418091547\"/><nd ref=\"6418091546\"/><nd ref=\"6418091545\"/><nd ref=\"6418091544\"/><nd ref=\"6418091543\"/><nd ref=\"6418091542\"/><nd ref=\"6418091541\"/><nd ref=\"6418091540\"/><nd ref=\"6418091539\"/><nd ref=\"6418091538\"/><nd ref=\"6418091537\"/><nd ref=\"6418091536\"/><nd ref=\"6418091535\"/><nd ref=\"6418091534\"/><nd ref=\"6418091533\"/><nd ref=\"6418091532\"/><nd ref=\"6418091531\"/><nd ref=\"6418091530\"/><nd ref=\"6418091529\"/><nd ref=\"6418091528\"/><nd ref=\"6418091527\"/><nd ref=\"6418091526\"/><nd ref=\"6418091525\"/><nd ref=\"6418091524\"/><nd ref=\"6418091523\"/><nd ref=\"6418091522\"/><tag k=\"natural\" v=\"wood\"/>\n </way>\n <way id=\"684974293\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:36Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"6418095339\"/><nd ref=\"6418095338\"/><nd ref=\"6418095337\"/><nd ref=\"6418095336\"/><nd ref=\"6418095335\"/><nd ref=\"6418095334\"/><nd ref=\"6418095333\"/><nd ref=\"6418095332\"/><nd ref=\"6418095331\"/><nd ref=\"6418095330\"/><nd ref=\"6418095329\"/><nd ref=\"6418095339\"/><tag k=\"natural\" v=\"wood\"/>\n </way>\n <way id=\"684974295\" visible=\"true\" version=\"1\" changeset=\"69405864\" timestamp=\"2019-04-20T16:08:37Z\" user=\"JaLooNz\" uid=\"741163\">\n  <nd ref=\"6418095380\"/><nd ref=\"6418095379\"/><nd ref=\"6418095378\"/><nd ref=\"6418095377\"/><nd ref=\"6418095376\"/><nd ref=\"6418095375\"/><nd ref=\"6418095374\"/><nd ref=\"6418095373\"/><nd ref=\"6418095372\"/><nd ref=\"6418095371\"/><nd ref=\"6418095370\"/><nd ref=\"6418095369\"/><nd ref=\"6418095368\"/><nd ref=\"6418095367\"/><nd ref=\"6418095366\"/><nd ref=\"6418095365\"/><nd ref=\"6418095364\"/><nd ref=\"6418095363\"/><nd ref=\"6418095362\"/><nd ref=\"6418095361\"/><nd ref=\"6418095360\"/><nd ref=\"6418095359\"/><nd ref=\"6418095358\"/><nd ref=\"6418095357\"/><nd ref=\"6418095356\"/><nd ref=\"6418095355\"/><nd ref=\"6418095354\"/><nd ref=\"6418095353\"/><nd ref=\"6418095352\"/><nd ref=\"6418095351\"/><nd ref=\"6418095350\"/><nd ref=\"6418095349\"/><nd ref=\"6418095348\"/><nd ref=\"6418095347\"/><nd ref=\"6418095346\"/><nd ref=\"6418095345\"/><nd ref=\"6418095344\"/><nd ref=\"6418095343\"/><nd ref=\"6418095342\"/><nd ref=\"6418095341\"/><nd ref=\"6418095340\"/><nd ref=\"6418095380\"/><tag k=\"natural\" v=\"wood\"/>\n </way>\n <way id=\"688904126\" visible=\"true\" version=\"1\" changeset=\"70073507\" timestamp=\"2019-05-09T13:25:24Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"1316096954\"/><nd ref=\"2558468527\"/><nd ref=\"2558468526\"/><nd ref=\"1316097185\"/><nd ref=\"1316095871\"/><nd ref=\"1717564095\"/><nd ref=\"1316098225\"/><nd ref=\"6418683199\"/><nd ref=\"5138710146\"/><nd ref=\"1829631576\"/><nd ref=\"1316097339\"/><nd ref=\"1316098277\"/><nd ref=\"1316097718\"/><nd ref=\"1316098058\"/><nd ref=\"6418683201\"/><nd ref=\"1316096285\"/><nd ref=\"6418683202\"/><nd ref=\"1316098039\"/><nd ref=\"1829631579\"/><nd ref=\"6418683204\"/><nd ref=\"1316097943\"/><nd ref=\"1316097408\"/><nd ref=\"6418683200\"/><nd ref=\"1316095915\"/><nd ref=\"6418683203\"/><nd ref=\"1316098229\"/><nd ref=\"1316097987\"/><nd ref=\"6418683205\"/><nd ref=\"1316097553\"/><nd ref=\"1316098955\"/><nd ref=\"1316098722\"/><nd ref=\"1829639105\"/><nd ref=\"1829639101\"/><nd ref=\"1829639103\"/><nd ref=\"1316098695\"/><nd ref=\"1316097655\"/><nd ref=\"1829639098\"/><nd ref=\"1829639090\"/><nd ref=\"1829639092\"/><nd ref=\"247182122\"/><nd ref=\"134940446\"/><nd ref=\"1716790799\"/><nd ref=\"134940413\"/><nd ref=\"1716790299\"/><nd ref=\"1716790736\"/><nd ref=\"134940391\"/><nd ref=\"5152568179\"/><nd ref=\"1868204948\"/><nd ref=\"6461869572\"/>\n </way>\n <way id=\"689102082\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:05Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"6464184258\"/><nd ref=\"6464184261\"/><tag k=\"aerialway\" v=\"cable_car\"/><tag k=\"name\" v=\"Sentosa Line\"/>\n </way>\n <way id=\"689102084\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:05Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"6464184260\"/><nd ref=\"6464184262\"/><tag k=\"aerialway\" v=\"cable_car\"/><tag k=\"name\" v=\"Sentosa Line\"/>\n </way>\n <way id=\"689102085\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:05Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"6464184261\"/><nd ref=\"6464184260\"/><tag k=\"aerialway\" v=\"cable_car\"/><tag k=\"image\" v=\"https://images.mapillary.com/tM5pTW4qQVgbrk7FB2O0ow/thumb-2048.jpg\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/tM5pTW4qQVgbrk7FB2O0ow\"/><tag k=\"name\" v=\"Sentosa Line\"/>\n </way>\n <way id=\"689102086\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:05Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"6464184262\"/><nd ref=\"6464184263\"/><tag k=\"aerialway\" v=\"cable_car\"/><tag k=\"image\" v=\"https://images.mapillary.com/48gXOPD0CLfUnPMiflSVbQ/thumb-2048.jpg\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/48gXOPD0CLfUnPMiflSVbQ\"/><tag k=\"name\" v=\"Sentosa Line\"/>\n </way>\n <way id=\"689102087\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:05Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"6464184263\"/><nd ref=\"3686388539\"/><nd ref=\"6464184264\"/><tag k=\"aerialway\" v=\"cable_car\"/><tag k=\"name\" v=\"Sentosa Line\"/>\n </way>\n <way id=\"689102088\" visible=\"true\" version=\"1\" changeset=\"70104334\" timestamp=\"2019-05-10T10:44:06Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"6464184264\"/><nd ref=\"6464184265\"/><tag k=\"aerialway\" v=\"cable_car\"/><tag k=\"image\" v=\"https://images.mapillary.com/K6QdH5wB__71miZmpGihNA/thumb-2048.jpg\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/K6QdH5wB__71miZmpGihNA\"/><tag k=\"name\" v=\"Sentosa Line\"/>\n </way>\n <way id=\"689159154\" visible=\"true\" version=\"1\" changeset=\"70113476\" timestamp=\"2019-05-10T15:02:53Z\" user=\"km2bp\" uid=\"3610826\">\n  <nd ref=\"6464873749\"/><nd ref=\"6464873750\"/><nd ref=\"6464873751\"/><nd ref=\"6464873752\"/><nd ref=\"6464873753\"/><nd ref=\"6464873754\"/><nd ref=\"6464873755\"/><nd ref=\"6464873756\"/><nd ref=\"6464873757\"/><nd ref=\"6464873758\"/><nd ref=\"6464873759\"/><nd ref=\"6464873760\"/><nd ref=\"6464873761\"/><nd ref=\"6464873762\"/><nd ref=\"6464873763\"/><nd ref=\"6464873764\"/><nd ref=\"6464873765\"/><nd ref=\"6464873766\"/><nd ref=\"6464873767\"/><nd ref=\"6464873749\"/><tag k=\"amenity\" v=\"shelter\"/><tag k=\"building\" v=\"yes\"/><tag k=\"image\" v=\"https://images.mapillary.com/H00qB2807YW4bLqbUqLbxw/thumb-2048.jpg\"/><tag k=\"mapillary\" v=\"https://www.mapillary.com/map/im/H00qB2807YW4bLqbUqLbxw\"/><tag k=\"shelter_type\" v=\"lean_to\"/>\n </way>\n <way id=\"702400958\" visible=\"true\" version=\"1\" changeset=\"72006528\" timestamp=\"2019-07-08T10:23:59Z\" user=\"div006\" uid=\"8911933\">\n  <nd ref=\"6596068720\"/><nd ref=\"5209288659\"/><nd ref=\"5138638004\"/><tag k=\"foot\" v=\"no\"/><tag k=\"highway\" v=\"unclassified\"/><tag k=\"lanes\" v=\"1\"/><tag k=\"maxspeed\" v=\"40\"/><tag k=\"name\" v=\"Siloso Road\"/><tag k=\"oneway\" v=\"yes\"/><tag k=\"psv\" v=\"yes\"/><tag k=\"surface\" v=\"asphalt\"/><tag k=\"turn:lanes\" v=\"left;through\"/>\n </way>\n <way id=\"716057445\" visible=\"true\" version=\"1\" changeset=\"73567273\" timestamp=\"2019-08-21T07:46:44Z\" user=\"pk_ravi\" uid=\"9393824\">\n  <nd ref=\"6728983057\"/><nd ref=\"2558083054\"/><tag k=\"highway\" v=\"service\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"lanes:backward\" v=\"1\"/><tag k=\"lanes:forward\" v=\"1\"/><tag k=\"turn:lanes:backward\" v=\"through\"/><tag k=\"turn:lanes:forward\" v=\"left\"/>\n </way>\n <way id=\"730092327\" visible=\"true\" version=\"1\" changeset=\"75129398\" timestamp=\"2019-10-01T06:09:44Z\" user=\"div006\" uid=\"8911933\">\n  <nd ref=\"2558083058\"/><nd ref=\"2558083053\"/><nd ref=\"2558468525\"/><tag k=\"highway\" v=\"service\"/><tag k=\"lanes\" v=\"2\"/><tag k=\"lanes:backward\" v=\"1\"/><tag k=\"lanes:forward\" v=\"1\"/>\n </way>\n <relation id=\"2353586\" visible=\"true\" version=\"4\" changeset=\"61784221\" timestamp=\"2018-08-19T04:24:10Z\" user=\"JaLooNz\" uid=\"741163\">\n  <member type=\"relation\" ref=\"2353581\" role=\"\"/>\n  <member type=\"relation\" ref=\"2353584\" role=\"\"/>\n  <member type=\"relation\" ref=\"2581000\" role=\"\"/>\n  <member type=\"relation\" ref=\"2581001\" role=\"\"/><tag k=\"name\" v=\"Sentosa Public Transport Services\"/><tag k=\"network\" v=\"Sentosa\"/><tag k=\"type\" v=\"operator\"/>\n </relation>\n <relation id=\"2353599\" visible=\"true\" version=\"5\" changeset=\"70181245\" timestamp=\"2019-05-13T08:18:13Z\" user=\"km2bp\" uid=\"3610826\">\n  <member type=\"node\" ref=\"471491369\" role=\"stop\"/>\n  <member type=\"node\" ref=\"316161138\" role=\"stop\"/>\n  <member type=\"node\" ref=\"316161140\" role=\"stop\"/>\n  <member type=\"way\" ref=\"28755868\" role=\"\"/>\n  <member type=\"way\" ref=\"689159148\" role=\"\"/>\n  <member type=\"way\" ref=\"689159149\" role=\"\"/>\n  <member type=\"way\" ref=\"689159150\" role=\"\"/>\n  <member type=\"way\" ref=\"689612593\" role=\"\"/>\n  <member type=\"way\" ref=\"688910216\" role=\"\"/>\n  <member type=\"way\" ref=\"689612592\" role=\"\"/>\n  <member type=\"way\" ref=\"689612594\" role=\"\"/>\n  <member type=\"way\" ref=\"688904133\" role=\"\"/>\n  <member type=\"way\" ref=\"689612595\" role=\"\"/>\n  <member type=\"way\" ref=\"689612601\" role=\"\"/>\n  <member type=\"way\" ref=\"689612596\" role=\"\"/>\n  <member type=\"way\" ref=\"689612597\" role=\"\"/>\n  <member type=\"way\" ref=\"689612602\" role=\"\"/><tag k=\"description\" v=\"Singapore Cable Car\"/><tag k=\"from\" v=\"Mount Faber\"/><tag k=\"name\" v=\"Singapore Cable Car\"/><tag k=\"operator\" v=\"Singapore Cable Car\"/><tag k=\"ref\" v=\"Singapore Cable Car\"/><tag k=\"route\" v=\"aerialway\"/><tag k=\"to\" v=\"Imbian\"/><tag k=\"type\" v=\"route\"/>\n </relation>\n <relation id=\"2581000\" visible=\"true\" version=\"48\" changeset=\"71305177\" timestamp=\"2019-06-16T16:46:29Z\" user=\"mueschel\" uid=\"616774\">\n  <member type=\"node\" ref=\"1717541875\" role=\"platform\"/>\n  <member type=\"node\" ref=\"5137340962\" role=\"platform\"/>\n  <member type=\"node\" ref=\"1868203030\" role=\"platform\"/>\n  <member type=\"node\" ref=\"1868203032\" role=\"platform\"/>\n  <member type=\"node\" ref=\"5137340958\" role=\"platform\"/>\n  <member type=\"node\" ref=\"5284959086\" role=\"platform\"/>\n  <member type=\"node\" ref=\"5284959085\" role=\"platform\"/>\n  <member type=\"node\" ref=\"1868203038\" role=\"platform\"/><tag k=\"fee\" v=\"no\"/><tag k=\"interval\" v=\"00:10:00\"/><tag k=\"name\" v=\"Sentosa Bus A\"/><tag k=\"network\" v=\"Sentosa\"/><tag k=\"opening_hours\" v=\"Mo-SU 7:00-00:10\"/><tag k=\"operator\" v=\"Sentosa\"/><tag k=\"public_transport:version\" v=\"2\"/><tag k=\"ref\" v=\"Bus A\"/><tag k=\"roundtrip\" v=\"yes\"/><tag k=\"route\" v=\"bus\"/><tag k=\"type\" v=\"route\"/>\n </relation>\n <relation id=\"7616954\" visible=\"true\" version=\"51\" changeset=\"74099421\" timestamp=\"2019-09-04T17:41:33Z\" user=\"aj_34\" uid=\"8911947\">\n  <member type=\"way\" ref=\"100985095\" role=\"\"/>\n  <member type=\"way\" ref=\"641062216\" role=\"\"/>\n  <member type=\"way\" ref=\"528897520\" role=\"\"/>\n  <member type=\"way\" ref=\"530440710\" role=\"\"/>\n  <member type=\"way\" ref=\"530440710\" role=\"\"/>\n  <member type=\"way\" ref=\"530440710\" role=\"\"/>\n  <member type=\"way\" ref=\"530440709\" role=\"\"/>\n  <member type=\"way\" ref=\"641062218\" role=\"\"/>\n  <member type=\"way\" ref=\"528896902\" role=\"\"/>\n  <member type=\"way\" ref=\"260777732\" role=\"\"/>\n  <member type=\"way\" ref=\"650732280\" role=\"\"/>\n  <member type=\"way\" ref=\"482407211\" role=\"\"/>\n  <member type=\"way\" ref=\"39351056\" role=\"\"/>\n  <member type=\"way\" ref=\"478164188\" role=\"\"/>\n  <member type=\"way\" ref=\"641071782\" role=\"\"/>\n  <member type=\"way\" ref=\"478164185\" role=\"\"/>\n  <member type=\"way\" ref=\"533417989\" role=\"\"/>\n  <member type=\"way\" ref=\"482407210\" role=\"\"/>\n  <member type=\"way\" ref=\"478164981\" role=\"\"/>\n  <member type=\"way\" ref=\"528240340\" role=\"\"/>\n  <member type=\"way\" ref=\"528240340\" role=\"\"/>\n  <member type=\"way\" ref=\"528898916\" role=\"\"/>\n  <member type=\"way\" ref=\"528898917\" role=\"\"/>\n  <member type=\"way\" ref=\"530020436\" role=\"\"/>\n  <member type=\"way\" ref=\"482416618\" role=\"\"/>\n  <member type=\"way\" ref=\"639335711\" role=\"\"/>\n  <member type=\"way\" ref=\"482416616\" role=\"\"/>\n  <member type=\"way\" ref=\"482416617\" role=\"\"/>\n  <member type=\"way\" ref=\"640177800\" role=\"\"/>\n  <member type=\"way\" ref=\"483329212\" role=\"\"/>\n  <member type=\"way\" ref=\"696374488\" role=\"\"/>\n  <member type=\"way\" ref=\"640177801\" role=\"\"/>\n  <member type=\"way\" ref=\"528897519\" role=\"\"/>\n  <member type=\"way\" ref=\"702400958\" role=\"\"/>\n  <member type=\"way\" ref=\"538105768\" role=\"\"/>\n  <member type=\"way\" ref=\"717472124\" role=\"\"/>\n  <member type=\"way\" ref=\"641071779\" role=\"\"/>\n  <member type=\"way\" ref=\"478164186\" role=\"\"/>\n  <member type=\"way\" ref=\"478164187\" role=\"\"/>\n  <member type=\"way\" ref=\"638062871\" role=\"\"/>\n  <member type=\"way\" ref=\"639566222\" role=\"\"/>\n  <member type=\"way\" ref=\"528900132\" role=\"\"/>\n  <member type=\"way\" ref=\"639566220\" role=\"\"/>\n  <member type=\"way\" ref=\"639566219\" role=\"\"/>\n  <member type=\"way\" ref=\"46705592\" role=\"\"/>\n  <member type=\"way\" ref=\"721980716\" role=\"\"/>\n  <member type=\"way\" ref=\"123939459\" role=\"\"/>\n  <member type=\"way\" ref=\"176273839\" role=\"\"/>\n  <member type=\"way\" ref=\"478097302\" role=\"\"/>\n  <member type=\"way\" ref=\"190539565\" role=\"\"/>\n  <member type=\"way\" ref=\"176907142\" role=\"\"/>\n  <member type=\"way\" ref=\"528903929\" role=\"\"/>\n  <member type=\"way\" ref=\"190539566\" role=\"\"/>\n  <member type=\"way\" ref=\"123939457\" role=\"\"/>\n  <member type=\"way\" ref=\"654421246\" role=\"\"/>\n  <member type=\"way\" ref=\"176273824\" role=\"\"/>\n  <member type=\"way\" ref=\"428878461\" role=\"\"/>\n  <member type=\"way\" ref=\"685026490\" role=\"\"/>\n  <member type=\"way\" ref=\"685026489\" role=\"\"/>\n  <member type=\"way\" ref=\"722003955\" role=\"\"/>\n  <member type=\"way\" ref=\"477973143\" role=\"\"/>\n  <member type=\"way\" ref=\"528900131\" role=\"\"/>\n  <member type=\"way\" ref=\"528900131\" role=\"\"/>\n  <member type=\"way\" ref=\"528900129\" role=\"\"/>\n  <member type=\"way\" ref=\"652296697\" role=\"\"/>\n  <member type=\"way\" ref=\"530440704\" role=\"\"/>\n  <member type=\"way\" ref=\"639566224\" role=\"\"/>\n  <member type=\"way\" ref=\"639566218\" role=\"\"/>\n  <member type=\"way\" ref=\"673079184\" role=\"\"/>\n  <member type=\"way\" ref=\"639566223\" role=\"\"/>\n  <member type=\"way\" ref=\"711651986\" role=\"\"/>\n  <member type=\"way\" ref=\"530440708\" role=\"\"/>\n  <member type=\"way\" ref=\"693168172\" role=\"\"/>\n  <member type=\"way\" ref=\"528757007\" role=\"\"/><tag k=\"name\" v=\"Sentosa Bus A\"/><tag k=\"route\" v=\"bus\"/><tag k=\"type\" v=\"route\"/>\n </relation>\n <relation id=\"8865315\" visible=\"true\" version=\"1\" changeset=\"64005613\" timestamp=\"2018-10-30T09:56:45Z\" user=\"Ramanyam\" uid=\"7671600\">\n  <member type=\"way\" ref=\"639335711\" role=\"from\"/>\n  <member type=\"way\" ref=\"533417988\" role=\"via\"/>\n  <member type=\"way\" ref=\"533463813\" role=\"to\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"8865318\" visible=\"true\" version=\"1\" changeset=\"64005642\" timestamp=\"2018-10-30T09:58:13Z\" user=\"Ramanyam\" uid=\"7671600\">\n  <member type=\"way\" ref=\"639335779\" role=\"from\"/>\n  <member type=\"way\" ref=\"639335711\" role=\"via\"/>\n  <member type=\"way\" ref=\"533417988\" role=\"to\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"8865326\" visible=\"true\" version=\"1\" changeset=\"64005668\" timestamp=\"2018-10-30T09:59:12Z\" user=\"Ramanyam\" uid=\"7671600\">\n  <member type=\"way\" ref=\"533463813\" role=\"from\"/>\n  <member type=\"way\" ref=\"639335779\" role=\"via\"/>\n  <member type=\"way\" ref=\"639335711\" role=\"to\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"8870287\" visible=\"true\" version=\"1\" changeset=\"64038126\" timestamp=\"2018-10-31T08:51:26Z\" user=\"mutpuri\" uid=\"7795551\">\n  <member type=\"way\" ref=\"533417988\" role=\"from\"/>\n  <member type=\"way\" ref=\"533463813\" role=\"via\"/>\n  <member type=\"way\" ref=\"639335779\" role=\"to\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"8883047\" visible=\"true\" version=\"2\" changeset=\"72006528\" timestamp=\"2019-07-08T10:23:59Z\" user=\"div006\" uid=\"8911933\">\n  <member type=\"way\" ref=\"482416617\" role=\"from\"/>\n  <member type=\"way\" ref=\"640177799\" role=\"via\"/>\n  <member type=\"way\" ref=\"528897519\" role=\"to\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"8923582\" visible=\"true\" version=\"1\" changeset=\"64439376\" timestamp=\"2018-11-13T11:54:22Z\" user=\"nadigatla\" uid=\"8995721\">\n  <member type=\"node\" ref=\"1717541894\" role=\"via\"/>\n  <member type=\"way\" ref=\"528900905\" role=\"to\"/>\n  <member type=\"way\" ref=\"528900905\" role=\"from\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"8924581\" visible=\"true\" version=\"1\" changeset=\"64442215\" timestamp=\"2018-11-13T13:17:51Z\" user=\"kumar3\" uid=\"8924971\">\n  <member type=\"node\" ref=\"613981020\" role=\"via\"/>\n  <member type=\"way\" ref=\"640177799\" role=\"to\"/>\n  <member type=\"way\" ref=\"640177799\" role=\"from\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"8993526\" visible=\"true\" version=\"1\" changeset=\"64661757\" timestamp=\"2018-11-19T15:35:35Z\" user=\"aj_34\" uid=\"8911947\">\n  <member type=\"way\" ref=\"646425420\" role=\"from\"/>\n  <member type=\"node\" ref=\"4731977868\" role=\"via\"/>\n  <member type=\"way\" ref=\"646425420\" role=\"to\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"8993527\" visible=\"true\" version=\"1\" changeset=\"64661757\" timestamp=\"2018-11-19T15:35:35Z\" user=\"aj_34\" uid=\"8911947\">\n  <member type=\"way\" ref=\"480183449\" role=\"from\"/>\n  <member type=\"node\" ref=\"4731977868\" role=\"via\"/>\n  <member type=\"way\" ref=\"480183449\" role=\"to\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"9033062\" visible=\"true\" version=\"1\" changeset=\"64726693\" timestamp=\"2018-11-21T07:11:40Z\" user=\"pavani_03\" uid=\"8911925\">\n  <member type=\"way\" ref=\"533417990\" role=\"from\"/>\n  <member type=\"node\" ref=\"5175392808\" role=\"via\"/>\n  <member type=\"way\" ref=\"639335710\" role=\"to\"/><tag k=\"restriction\" v=\"no_left_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"9574726\" visible=\"true\" version=\"1\" changeset=\"70073507\" timestamp=\"2019-05-09T13:25:26Z\" user=\"km2bp\" uid=\"3610826\">\n  <member type=\"way\" ref=\"116818077\" role=\"outer\"/>\n  <member type=\"way\" ref=\"688904126\" role=\"outer\"/>\n  <member type=\"way\" ref=\"688904150\" role=\"outer\"/>\n  <member type=\"way\" ref=\"688904137\" role=\"outer\"/>\n  <member type=\"way\" ref=\"688904135\" role=\"outer\"/>\n  <member type=\"way\" ref=\"688904140\" role=\"outer\"/>\n  <member type=\"way\" ref=\"688904144\" role=\"outer\"/><tag k=\"addr:city\" v=\"Singapore\"/><tag k=\"addr:country\" v=\"SG\"/><tag k=\"addr:housenumber\" v=\"8\"/><tag k=\"addr:postcode\" v=\"098269\"/><tag k=\"addr:street\" v=\"Sentosa Gateway\"/><tag k=\"email\" v=\"enquiries@rwsentosa.com\"/><tag k=\"landuse\" v=\"retail\"/><tag k=\"name\" v=\"Resorts World Sentosa\"/><tag k=\"name:en\" v=\"Resorts World Sentosa\"/><tag k=\"name:zh\" v=\"圣淘沙名胜世界\"/><tag k=\"tourism\" v=\"attraction\"/><tag k=\"type\" v=\"multipolygon\"/><tag k=\"website\" v=\"https://www.rwsentosa.com/\"/><tag k=\"wikidata\" v=\"Q5955134\"/><tag k=\"wikipedia\" v=\"en:Resorts World Sentosa\"/>\n </relation>\n <relation id=\"10100363\" visible=\"true\" version=\"1\" changeset=\"75129398\" timestamp=\"2019-10-01T06:09:44Z\" user=\"div006\" uid=\"8911933\">\n  <member type=\"way\" ref=\"730092326\" role=\"from\"/>\n  <member type=\"way\" ref=\"730092326\" role=\"to\"/>\n  <member type=\"node\" ref=\"2558468523\" role=\"via\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"10100364\" visible=\"true\" version=\"1\" changeset=\"75129398\" timestamp=\"2019-10-01T06:09:44Z\" user=\"div006\" uid=\"8911933\">\n  <member type=\"way\" ref=\"249144132\" role=\"from\"/>\n  <member type=\"way\" ref=\"249144132\" role=\"to\"/>\n  <member type=\"node\" ref=\"2558468523\" role=\"via\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"10100365\" visible=\"true\" version=\"1\" changeset=\"75129398\" timestamp=\"2019-10-01T06:09:44Z\" user=\"div006\" uid=\"8911933\">\n  <member type=\"way\" ref=\"249144132\" role=\"from\"/>\n  <member type=\"way\" ref=\"249144132\" role=\"to\"/>\n  <member type=\"node\" ref=\"2558468525\" role=\"via\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"10100366\" visible=\"true\" version=\"1\" changeset=\"75129398\" timestamp=\"2019-10-01T06:09:44Z\" user=\"div006\" uid=\"8911933\">\n  <member type=\"way\" ref=\"730092327\" role=\"from\"/>\n  <member type=\"way\" ref=\"730092327\" role=\"to\"/>\n  <member type=\"node\" ref=\"2558468525\" role=\"via\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"10100367\" visible=\"true\" version=\"1\" changeset=\"75129398\" timestamp=\"2019-10-01T06:09:44Z\" user=\"div006\" uid=\"8911933\">\n  <member type=\"way\" ref=\"730092327\" role=\"from\"/>\n  <member type=\"way\" ref=\"730092327\" role=\"to\"/>\n  <member type=\"node\" ref=\"2558083058\" role=\"via\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n <relation id=\"10101134\" visible=\"true\" version=\"1\" changeset=\"75137528\" timestamp=\"2019-10-01T09:03:32Z\" user=\"div006\" uid=\"8911933\">\n  <member type=\"way\" ref=\"533462931\" role=\"from\"/>\n  <member type=\"way\" ref=\"533462931\" role=\"to\"/>\n  <member type=\"node\" ref=\"2558083041\" role=\"via\"/><tag k=\"restriction\" v=\"no_u_turn\"/><tag k=\"type\" v=\"restriction\"/>\n </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/data/polygon/jsonl/samples.jsonl",
    "content": "[[113.57867490000001,22.1617608],[113.5787899,22.1617241],[113.5788852,22.1615436],[113.58502050000001,22.1635048],[113.5848607,22.1639116],[113.5847,22.1645271],[113.5848301,22.16492],[113.58499850000001,22.165171],[113.58519380000001,22.1653409],[113.5858101,22.1655501],[113.58638560000001,22.16573],[113.5867355,22.1657165],[113.58719470000001,22.165483],[113.58740010000001,22.1653146],[113.58751760000001,22.1650176],[113.587962,22.1637974],[113.5919202,22.1529645],[113.5982532,22.1359751],[113.5983546,22.13555],[113.5982529,22.1352573],[113.59810250000001,22.1349825],[113.59787770000001,22.134755],[113.59584810000001,22.1340829],[113.5955243,22.1340282],[113.59521,22.134029],[113.5947813,22.1342792],[113.5944831,22.1345515],[113.5943677,22.1349493],[113.5908334,22.144539],[113.5904614,22.1455896],[113.590239,22.1460792],[113.5900867,22.146118],[113.5899435,22.146128],[113.58994860000001,22.1460879],[113.58987780000001,22.1460474],[113.58976910000001,22.1461969],[113.58964440000001,22.1463103],[113.5893676,22.1472007],[113.5894167,22.1473249],[113.58958050000001,22.1475323],[113.5767795,22.1554512],[113.5767596,22.1554508],[113.5766004,22.1553861],[113.5766518,22.1553386],[113.5766367,22.1552071],[113.575556,22.1542885],[113.5710616,22.1523376],[113.5704741,22.1533031],[113.569752,22.1544897],[113.56972180000001,22.1548813],[113.5697522,22.1550823],[113.56981850000001,22.1552505],[113.5699057,22.1554302],[113.5700359,22.1556198],[113.5701742,22.1557569],[113.57035,22.1558939],[113.5736248,22.1574451],[113.5738848,22.1575983],[113.5741076,22.1577823],[113.574296,22.1579898],[113.5744435,22.1582094],[113.5745862,22.1585032],[113.57464580000001,22.1586628],[113.5746813,22.1587732],[113.574954,22.1596271],[113.5749476,22.1598031],[113.57488780000001,22.1601868],[113.57445220000001,22.1608593],[113.57409770000001,22.1612627],[113.57393110000001,22.1616227],[113.57363,22.1622396],[113.5736099,22.162423],[113.573614,22.1625198],[113.57363,22.1626045],[113.5736766,22.1626672],[113.57375,22.1627199],[113.5738512,22.1627473],[113.5739659,22.1627333],[113.574725,22.1625465],[113.5749168,22.1625754],[113.5750619,22.1625981],[113.57867490000001,22.1617608]]\n[[-122.4222554295,37.8248723624],[-122.4237360089,37.825397785],[-122.4246586888,37.8257452398],[-122.4255813687,37.826448619],[-122.4251307575,37.8271096198],[-122.4240149586,37.8283214392],[-122.4211181729,37.8279824707],[-122.4206353752,37.8266689532],[-122.4216224281,37.825397785],[-122.4222554295,37.8248723624]]\n[[104.0012689,1.3298339],[104.0023393,1.3293889],[104.0006106,1.3253055],[104.00137570000001,1.3249994],[104.0017675,1.3258912],[104.0030763,1.3253328],[104.0034219,1.32607],[104.0036036,1.3265069],[104.00409250000001,1.3263057],[104.0039942,1.3260757],[104.0040443,1.3259904],[104.004137,1.3259533],[104.0040294,1.3256473],[104.00419640000001,1.3255768],[104.0039237,1.3249444],[104.007315,1.323511],[104.0077227,1.323629],[104.00801240000001,1.3238757],[104.01095210000001,1.330751],[104.01310860000001,1.3358673],[104.01498620000001,1.3404258],[104.0176469,1.3466575],[104.0199965,1.3521492],[104.0215307,1.3558496],[104.0215307,1.3563215],[104.0213269,1.356772],[104.02099430000001,1.3570402],[104.0204353,1.3572565],[104.0193313,1.3576837],[104.0183121,1.3580913],[104.0158287,1.3590646],[104.0175298,1.3631728],[104.0167078,1.363513],[104.0150066,1.3594048],[104.013946,1.3598853],[104.0012689,1.3298339]]\n[[103.9713552,1.3413031],[103.972305,1.3405202],[103.9749743,1.3394137],[103.9784163,1.337997],[103.9794011,1.3381183],[103.9800878,1.3388782],[103.9813761,1.3384824],[103.9817473,1.3377078],[103.981902,1.33701],[103.9814785,1.3358926],[103.9834757,1.335044],[103.9835767,1.3342784],[103.98076990000001,1.3275081],[103.9788494,1.322635],[103.9804982,1.323031],[103.9811172,1.3227241],[103.9811578,1.3223531],[103.9807667,1.3212517],[103.9824174,1.3204245],[103.98295750000001,1.3203138],[103.98337330000001,1.3203384],[103.98385610000001,1.3204865],[103.98423810000001,1.3207518],[103.9844994,1.3210849],[104.0023106,1.3637289],[104.0039464,1.3674551],[104.003957,1.3678025],[104.0038059,1.368311],[104.00352930000001,1.3688136],[104.0031199,1.3691444],[104.0003518,1.3703536],[104.0001418,1.3705751],[103.9998915,1.3710473],[103.99972070000001,1.3715334],[103.9996581,1.3720635],[103.9996883,1.3734455],[103.9977972,1.3742601],[103.9955209,1.368784],[103.991663,1.3704769],[103.998084,1.385966],[103.9965914,1.3866055],[103.99680230000001,1.387099],[103.99599850000001,1.3874475],[103.9968906,1.3894406],[103.99412190000001,1.3897295],[103.9931568,1.3874164],[103.9919835,1.3879203],[103.9916641,1.3871306],[103.9908817,1.3874363],[103.9879901,1.3805408],[103.9874338,1.3772738],[103.9713552,1.3413031]]\n[[103.8618108,1.4103426],[103.8612068,1.4093894],[103.8607667,1.4088788],[103.8606449,1.4084926],[103.8606857,1.4078665],[103.860675,1.4075965],[103.860616,1.4073266],[103.860321,1.4068349],[103.8600881,1.4064186],[103.8601096,1.4062331],[103.8628984,1.4043086],[103.8640385,1.406202],[103.8680919,1.4122578],[103.8695671,1.4113094],[103.8699513,1.4118973],[103.8692741,1.4123534],[103.8697729,1.4131512],[103.871453,1.4156424],[103.8709427,1.4159833],[103.8734169,1.4197729],[103.8737339,1.4195427],[103.8745809,1.4208349],[103.874584,1.421049],[103.8741932,1.4216462],[103.8738063,1.4220361],[103.8734881,1.4219623],[103.8730552,1.4220908],[103.8731026,1.4223908],[103.8729595,1.4227112],[103.8730112,1.4229981],[103.8728557,1.4231683],[103.8727174,1.4234246],[103.8730275,1.4239577],[103.873705,1.4232741],[103.8747738,1.4248319],[103.8738714,1.4254075],[103.8754989,1.4279216],[103.8744483,1.4286122],[103.8721701,1.4252403],[103.8719102,1.4247713],[103.8715338,1.4241805],[103.8713168,1.4241859],[103.86986750000001,1.4245786],[103.8691425,1.424517],[103.868598,1.4245542],[103.8673568,1.4245616],[103.8671265,1.4243387],[103.8674272,1.4240344],[103.8679261,1.4237443],[103.866421,1.4214335],[103.86571540000001,1.4219151],[103.8641323,1.4194202],[103.86369130000001,1.4184195],[103.8631901,1.4177118],[103.8625806,1.416788],[103.862529,1.4166122],[103.8625064,1.4163251],[103.86245120000001,1.4162245],[103.86197530000001,1.4153855],[103.86224630000001,1.4141277],[103.8623754,1.4128183],[103.8623007,1.4118791],[103.8620339,1.410912],[103.8618108,1.4103426]]\n[[113.89361140000001,22.3110822],[113.8932252,22.3109233],[113.892839,22.3106454],[113.8925995,22.3102756],[113.8924956,22.3098514],[113.8924956,22.3094146],[113.892513,22.3091865],[113.8926244,22.3088588],[113.8928819,22.3085015],[113.8932681,22.3083029],[113.8937831,22.3080647],[113.89570990000001,22.3076301],[113.8959188,22.3075057],[113.8960869,22.3073522],[113.896193,22.3072104],[113.8963568,22.3067777],[113.8969313,22.3052605],[113.8967004,22.3051816],[113.8970489,22.3042791],[113.89735630000001,22.3043775],[113.89812280000001,22.3023661],[113.8980253,22.3022728],[113.8980849,22.3020425],[113.8980982,22.3017759],[113.8980317,22.3014453],[113.8944268,22.2968679],[113.894154,22.2964714],[113.8939773,22.2961292],[113.8938751,22.2957451],[113.8938689,22.2952796],[113.89391680000001,22.2949052],[113.8940835,22.2946443],[113.894341,22.2943267],[113.8947272,22.294009],[113.89528050000001,22.2937002],[113.8960576,22.2936119],[113.8968301,22.2936119],[113.89760260000001,22.2935834],[113.89908930000001,22.293632],[113.9003095,22.2939546],[113.9012008,22.294282],[113.9023232,22.2945339],[113.9035794,22.294762],[113.904984,22.2949335],[113.90631440000001,22.2950526],[113.9079903,22.2951888],[113.9106955,22.2951945],[113.9116259,22.2951888],[113.9144851,22.2953306],[113.9150661,22.2953306],[113.9157227,22.2953306],[113.9159585,22.29531],[113.91609910000001,22.2952114],[113.9163059,22.2951967],[113.9165558,22.2951789],[113.9168915,22.2950938],[113.9171741,22.2950148],[113.917483,22.2949489],[113.9178713,22.2948455],[113.9182717,22.2947195],[113.9194426,22.2943414],[113.9199703,22.2941585],[113.9205833,22.2939737],[113.9212495,22.2937729],[113.92253640000001,22.2933849],[113.9244002,22.292848],[113.9272084,22.2920391],[113.927618,22.291899],[113.9280031,22.291785],[113.928436,22.2916353],[113.9287749,22.2915227],[113.9289428,22.2914871],[113.9290383,22.2915213],[113.929078,22.2916132],[113.9313551,22.2909542],[113.931996,22.2931665],[113.9328771,22.2946032],[113.933284,22.2955042],[113.9396112,22.3111174],[113.943222,22.3158458],[113.9443305,22.3194954],[113.9449129,22.3203413],[113.9458058,22.3208891],[113.9456745,22.3230817],[113.9449535,22.3238158],[113.9447377,22.3238203],[113.9445526,22.3235556],[113.94420860000001,22.3232021],[113.9436936,22.3226463],[113.9395894,22.3218646],[113.9392319,22.3217854],[113.939026,22.3217191],[113.9360386,22.3207566],[113.9358255,22.3207739],[113.93566310000001,22.3208721],[113.9346461,22.3234694],[113.9337197,22.3239464],[113.9278739,22.3221448],[113.9208644,22.3198762],[113.89361140000001,22.3110822]]\n[[103.6992448,1.3832097],[103.6991025,1.3829774],[103.6976545,1.3808068],[103.6976208,1.3807517],[103.6976637,1.3807257],[103.6974768,1.3804333],[103.6971726,1.3799252],[103.6969507,1.3794709],[103.6968577,1.3792205],[103.6967718,1.3789486],[103.696804,1.3789128],[103.6967718,1.3787947],[103.6963102,1.37726],[103.695929,1.3759434],[103.6956228,1.3748325],[103.695451,1.3739524],[103.6975991,1.3728511],[103.6979064,1.3727517],[103.6980749,1.3727271],[103.6991843,1.3726965],[103.7013769,1.3725678],[103.7020698,1.372538],[103.7020702,1.3725588],[103.7021297,1.3725567],[103.7021296,1.3725351],[103.7037545,1.3724862],[103.7037545,1.3725122],[103.7038154,1.3725111],[103.7038154,1.3724876],[103.7042544,1.3724325],[103.7042958,1.3724065],[103.7044475,1.3723758],[103.7045103,1.3723743],[103.7045961,1.3723651],[103.7046589,1.372287],[103.7047784,1.3722196],[103.7049102,1.3721644],[103.7051791,1.3719959],[103.7058748,1.3715318],[103.7060602,1.3714018],[103.7061802,1.3712938],[103.7066983,1.3709265],[103.7068467,1.3708441],[103.7070867,1.3707212],[103.7073296,1.3706357],[103.7075515,1.3705832],[103.7077277,1.370557],[103.7078657,1.3705548],[103.7081735,1.3705772],[103.7082565,1.3705997],[103.7094993,1.3713948],[103.7101975,1.3718394],[103.711056,1.3724316],[103.7121604,1.3732126],[103.7129004,1.373373],[103.7131628,1.373418],[103.713627,1.3735305],[103.713585,1.3736669],[103.7137413,1.37383],[103.7138159,1.3739052],[103.7141368,1.3739697],[103.7144457,1.3744899],[103.7147426,1.3745499],[103.7148111,1.3745652],[103.7148679,1.3745778],[103.715027,1.3752075],[103.7151648,1.3757417],[103.7154628,1.3769947],[103.7156133,1.3777283],[103.7157088,1.3780688],[103.7157716,1.3782945],[103.7158256,1.3784323],[103.7159168,1.3786348],[103.7159705,1.3789287],[103.7162296,1.3803478],[103.7162519,1.3804231],[103.7163055,1.3805047],[103.7163479,1.3805514],[103.7164115,1.3806097],[103.7164613,1.3806383],[103.7165207,1.3806648],[103.7165928,1.3806764],[103.718212,1.3808464],[103.7183882,1.3808786],[103.7187473,1.3809888],[103.7189483,1.3810675],[103.7190997,1.3811559],[103.7191815,1.3812219],[103.7192459,1.3813253],[103.71931,1.3814745],[103.7193363,1.3815757],[103.7193498,1.3817203],[103.7193438,1.3818388],[103.719331,1.3819257],[103.719286,1.3820614],[103.7192317,1.3821686],[103.7191447,1.3822938],[103.7190247,1.3824114],[103.71809,1.3833444],[103.718037,1.3834154],[103.7180083,1.3835002],[103.718002,1.3835786],[103.7180115,1.3836783],[103.7180507,1.383673],[103.7180963,1.383762],[103.7181536,1.3838299],[103.7186944,1.3843175],[103.7188471,1.3845019],[103.7196467,1.3857045],[103.7197509,1.3859114],[103.7197994,1.3860449],[103.7198449,1.3862136],[103.7198727,1.3864572],[103.7198698,1.3865438],[103.7198316,1.3865482],[103.7198272,1.3867228],[103.7198096,1.3868915],[103.7197627,1.3870661],[103.7197018,1.3872363],[103.7195946,1.3874534],[103.7193407,1.3877923],[103.7190714,1.3881092],[103.7197832,1.3887166],[103.7198023,1.3892873],[103.7197509,1.3897025],[103.7197216,1.3900635],[103.7200591,1.3911917],[103.7200444,1.3914705],[103.7199901,1.3914763],[103.7195808,1.3916072],[103.7195452,1.3916185],[103.7195099,1.3916298],[103.7194897,1.3916363],[103.7193635,1.3914998],[103.7189496,1.3916201],[103.7189082,1.3914809],[103.7187031,1.3907912],[103.7180016,1.3908073],[103.7177235,1.3911418],[103.7172744,1.3910523],[103.7169134,1.3908102],[103.7167585,1.3905462],[103.7165149,1.3901691],[103.7155544,1.3899329],[103.7153717,1.3894135],[103.7152469,1.3885772],[103.7147443,1.3876383],[103.7146775,1.3875414],[103.7146158,1.3874871],[103.7145557,1.3874637],[103.7144353,1.3874343],[103.7140288,1.3875649],[103.7138439,1.3876529],[103.7136693,1.3877747],[103.713524,1.3879111],[103.7130874,1.3884467],[103.7149417,1.3916348],[103.7157767,1.3921952],[103.7159132,1.3923111],[103.7161304,1.3925811],[103.7162163,1.3927219],[103.7162808,1.3928701],[103.7163425,1.3930491],[103.7163836,1.3932149],[103.7164012,1.3933675],[103.7164012,1.3935362],[103.7163806,1.3937724],[103.716341,1.3939661],[103.7162639,1.3941612],[103.716148,1.3943842],[103.7159998,1.394575],[103.7158428,1.3947349],[103.7143231,1.3961111],[103.7142864,1.3961903],[103.7139342,1.3964602],[103.7139826,1.396585],[103.7154898,1.3977543],[103.7155588,1.3977396],[103.7176002,1.3950855],[103.7175297,1.3950298],[103.7184052,1.3939808],[103.7189548,1.3935802],[103.719712,1.3933396],[103.7197759,1.3933264],[103.7196996,1.3930799],[103.7198757,1.3929846],[103.7198581,1.3928599],[103.7198966,1.3928426],[103.719927,1.392829],[103.7199755,1.3929376],[103.7201868,1.3929816],[103.7203071,1.3931841],[103.720426,1.3931724],[103.7205948,1.3931724],[103.7209191,1.3934027],[103.7216265,1.394192],[103.7216683,1.3944194],[103.7217314,1.3945148],[103.7221248,1.3947921],[103.722204,1.3948302],[103.7222847,1.3948361],[103.7226487,1.3948038],[103.7229334,1.3949491],[103.7231256,1.3950899],[103.7233839,1.3950767],[103.723718,1.3954798],[103.7238259,1.3958242],[103.7239266,1.396069],[103.7240381,1.3962838],[103.7243039,1.3966156],[103.7243588,1.3966842],[103.7244117,1.396844],[103.7244315,1.3969374],[103.7244387,1.3970256],[103.7244283,1.3972548],[103.7244086,1.3973274],[103.7243796,1.3974021],[103.724309,1.3975318],[103.7242187,1.3976553],[103.7240428,1.3978489],[103.7239048,1.3979956],[103.7237933,1.3980602],[103.7236494,1.3981042],[103.7234381,1.398113],[103.7231152,1.3980749],[103.7228951,1.3980103],[103.7223286,1.3976025],[103.7220028,1.3975056],[103.7214114,1.3974352],[103.7209124,1.3975907],[103.720839,1.398559],[103.72023,1.3993513],[103.7200157,1.3994423],[103.7198836,1.399765],[103.7195637,1.3998883],[103.7191087,1.4000115],[103.7188182,1.400123],[103.7187389,1.4001788],[103.7178936,1.4010708],[103.7177439,1.4012615],[103.7175091,1.4017662],[103.7174386,1.4019892],[103.7173623,1.4022739],[103.717195,1.403028],[103.7150964,1.402224],[103.7136427,1.4013072],[103.713537,1.4012236],[103.7135091,1.4012661],[103.7132542,1.4014265],[103.7132278,1.4013832],[103.713164,1.4014206],[103.7131654,1.4014625],[103.7130766,1.4014623],[103.7129956,1.4015764],[103.7117057,1.4022694],[103.7114276,1.4025703],[103.7103907,1.4030703],[103.7098442,1.4039327],[103.7097743,1.4040026],[103.7097075,1.4040527],[103.7096392,1.4040967],[103.7095708,1.4041225],[103.7094843,1.4041392],[103.7094281,1.4041407],[103.7093507,1.4041286],[103.7092656,1.4041043],[103.7090728,1.4040177],[103.7083454,1.403702],[103.7082512,1.4037111],[103.7081389,1.4037308],[103.7079263,1.4037961],[103.7077288,1.4038675],[103.7076271,1.4038902],[103.7075026,1.4038933],[103.7073583,1.4038933],[103.7071898,1.4038781],[103.7070379,1.4038356],[103.7069073,1.4037764],[103.7068405,1.4037278],[103.7067843,1.4036443],[103.7067175,1.4035046],[103.7066841,1.4034242],[103.7066446,1.4032906],[103.7066127,1.4031266],[103.706555,1.4030598],[103.7064806,1.4029839],[103.706391,1.4029338],[103.7062726,1.4028974],[103.7061951,1.4028883],[103.7056583,1.4029429],[103.7047431,1.4030534],[103.7044511,1.4030887],[103.7040312,1.4029338],[103.7038581,1.4029278],[103.7037381,1.4029187],[103.7036379,1.4028959],[103.7029325,1.4026393],[103.7022811,1.4023979],[103.7020616,1.4023296],[103.701679,1.4022165],[103.7013468,1.402085],[103.7007414,1.4020944],[103.7006961,1.401911],[103.7006637,1.401725],[103.7006528,1.4014517],[103.7007085,1.4014517],[103.7007091,1.4014112],[103.7006562,1.401408],[103.7006757,1.4010657],[103.7007061,1.4010075],[103.7007592,1.4009569],[103.7008301,1.4009241],[103.7008098,1.4008608],[103.7007617,1.4008077],[103.7006706,1.4007318],[103.7006856,1.4003267],[103.7008703,1.4001775],[103.7008444,1.4000555],[103.7007752,1.3997518],[103.7005576,1.3997409],[103.7004009,1.399767],[103.7003177,1.3997358],[103.7002693,1.3996401],[103.7001362,1.398921],[103.6999162,1.3978394],[103.6978431,1.3853736],[103.6977528,1.3848306],[103.6979914,1.3845907],[103.6981778,1.3844629],[103.6983013,1.3843242],[103.6983858,1.3841509],[103.6991009,1.3833602],[103.6992448,1.3832097]]\n[[104.0012689,1.3298339],[104.0023393,1.3293889],[104.0006106,1.3253055],[104.0013757,1.3249994],[104.0017675,1.3258912],[104.0030763,1.3253328],[104.0034219,1.32607],[104.0036036,1.3265069],[104.0040925,1.3263057],[104.0039942,1.3260757],[104.0040443,1.3259904],[104.004137,1.3259533],[104.0040294,1.3256473],[104.0041964,1.3255768],[104.0039237,1.3249444],[104.007315,1.323511],[104.0077227,1.323629],[104.0080124,1.3238757],[104.0109521,1.330751],[104.0131086,1.3358673],[104.0149862,1.3404258],[104.0176469,1.3466575],[104.0199965,1.3521492],[104.0215307,1.3558496],[104.0215307,1.3563215],[104.0213269,1.356772],[104.0209943,1.3570402],[104.0204353,1.3572565],[104.0193313,1.3576837],[104.0183121,1.3580913],[104.0158287,1.3590646],[104.0175298,1.3631728],[104.0167078,1.363513],[104.0150066,1.3594048],[104.013946,1.3598853],[104.0012689,1.3298339]]\n[[103.8128607,1.443512],[103.8142125,1.4428899],[103.8171308,1.4412167],[103.8184741,1.4401489],[103.8197388,1.4389957],[103.8211004,1.4375701],[103.8226889,1.435528],[103.8234024,1.4351293],[103.8239856,1.434943],[103.8231911,1.434315],[103.822492,1.4346891],[103.8223664,1.4344168],[103.822871,1.4340571],[103.8230623,1.4340304],[103.8241408,1.4349022],[103.8243845,1.434891],[103.8244402,1.4348951],[103.8255361,1.4349742],[103.8258043,1.4348298],[103.8261725,1.4335319],[103.8263792,1.4319641],[103.8264188,1.4312537],[103.8264526,1.4305984],[103.8263935,1.4305306],[103.8263295,1.4304013],[103.8263553,1.4303012],[103.8264482,1.4301868],[103.8266743,1.4287401],[103.8251601,1.4291726],[103.8240831,1.4287704],[103.8244618,1.4283663],[103.82428,1.4275824],[103.8238795,1.4275223],[103.823706,1.426758],[103.8235994,1.4262163],[103.8237057,1.4260447],[103.8231166,1.4244516],[103.8251858,1.4238417],[103.8249169,1.4225223],[103.8247317,1.4222102],[103.8245428,1.422038],[103.8245225,1.4219032],[103.8246031,1.4217943],[103.8245844,1.42124],[103.8246905,1.4210765],[103.8244027,1.4194585],[103.8226617,1.4194529],[103.8226066,1.4190893],[103.8225907,1.4190161],[103.8225611,1.4189593],[103.8224339,1.4188273],[103.8217946,1.4183259],[103.8216711,1.4182435],[103.820845,1.4177962],[103.8197528,1.4172473],[103.8196942,1.417243],[103.8195436,1.4175566],[103.8194848,1.4177404],[103.8194486,1.4179335],[103.8192544,1.4179097],[103.8190348,1.4179474],[103.8186849,1.417984],[103.8184958,1.4180278],[103.8175801,1.4188651],[103.8175496,1.4189292],[103.8175466,1.4189881],[103.8175659,1.419047],[103.8176411,1.4191222],[103.8175453,1.4192342],[103.8173264,1.4190523],[103.8170498,1.4192464],[103.8167783,1.4193],[103.8161714,1.4194158],[103.816143,1.4194616],[103.8160586,1.4194799],[103.8160037,1.4194494],[103.8154799,1.4195422],[103.8154732,1.4195124],[103.8154683,1.4194905],[103.8155037,1.4194318],[103.8154025,1.4189568],[103.8155016,1.4185592],[103.8153717,1.418247],[103.8153019,1.4179657],[103.8152502,1.4178047],[103.8147282,1.4176486],[103.8146441,1.417727],[103.814111,1.4178132],[103.8140169,1.4173287],[103.8135099,1.4173862],[103.8131241,1.4169121],[103.8123632,1.4168731],[103.8113214,1.4181371],[103.8107033,1.4185785],[103.8099424,1.418858],[103.8091744,1.418115],[103.8092487,1.4167847],[103.8088594,1.4159143],[103.8088481,1.4156471],[103.8064448,1.4146604],[103.8059513,1.4147891],[103.805608,1.415068],[103.8003938,1.4197014],[103.7962938,1.4224298],[103.796171,1.4229032],[103.7961784,1.423393],[103.7994496,1.4271235],[103.8105647,1.4401227],[103.811702,1.4422464],[103.8120453,1.4437265],[103.8128607,1.443512]]\n[[103.9713552,1.3413031],[103.972305,1.3405202],[103.9749743,1.3394137],[103.9784163,1.337997],[103.9794011,1.3381183],[103.9800878,1.3388782],[103.9813761,1.3384824],[103.9817473,1.3377078],[103.981902,1.33701],[103.9814785,1.3358926],[103.9834757,1.335044],[103.9835767,1.3342784],[103.9807699,1.3275081],[103.9788494,1.322635],[103.9804982,1.323031],[103.9811172,1.3227241],[103.9811578,1.3223531],[103.9807667,1.3212517],[103.9824174,1.3204245],[103.9829575,1.3203138],[103.9833733,1.3203384],[103.9838561,1.3204865],[103.9842381,1.3207518],[103.9844994,1.3210849],[104.0023106,1.3637289],[104.0039464,1.3674551],[104.003957,1.3678025],[104.0038059,1.368311],[104.0035293,1.3688136],[104.0031199,1.3691444],[104.0003518,1.3703536],[104.0001418,1.3705751],[103.9998915,1.3710473],[103.9997207,1.3715334],[103.9996581,1.3720635],[103.9996883,1.3734455],[103.9977972,1.3742601],[103.9955209,1.368784],[103.991663,1.3704769],[103.998084,1.385966],[103.9965914,1.3866055],[103.9968023,1.387099],[103.9959985,1.3874475],[103.9968906,1.3894406],[103.9941219,1.3897295],[103.9931568,1.3874164],[103.9919835,1.3879203],[103.9916641,1.3871306],[103.9908817,1.3874363],[103.9879901,1.3805408],[103.9874338,1.3772738],[103.9713552,1.3413031]]\n[[103.9875214,1.3873926],[103.9886479,1.3875213],[103.98912,1.3878967],[103.9907669,1.3873229],[103.9853681,1.3730217],[103.9843623,1.3733419],[103.9826704,1.3713238],[103.9823485,1.3733081],[103.9819483,1.3739104],[103.9814119,1.3743608],[103.9815438,1.3754747],[103.9811329,1.375959],[103.9778746,1.3765473],[103.9785473,1.3773211],[103.978794,1.3782757],[103.9788644,1.3787205],[103.9792009,1.3791361],[103.978768,1.3792456],[103.9785795,1.3841213],[103.9787511,1.3851295],[103.9792795,1.3860771],[103.9806737,1.3868863],[103.9807475,1.3871328],[103.9809446,1.3870738],[103.9812885,1.387169],[103.9816423,1.3873052],[103.9815851,1.387532],[103.9824394,1.3877752],[103.9833132,1.3877417],[103.9833144,1.3876163],[103.9834485,1.3876118],[103.983418,1.3869649],[103.9832657,1.3865745],[103.983335,1.3854545],[103.9821731,1.3834911],[103.9822745,1.3821743],[103.9845506,1.3805383],[103.9849572,1.3809904],[103.9845227,1.3818399],[103.9851812,1.3827709],[103.9859204,1.3833844],[103.9861672,1.3831444],[103.986455,1.3832822],[103.986411,1.38558],[103.9875413,1.3856212],[103.9875214,1.3873926]]\n[[103.9020233,1.3388959],[103.9028379,1.3410049],[103.9067845,1.3409146],[103.9079985,1.3409244],[103.9084277,1.3410478],[103.9100584,1.3422598],[103.9115819,1.3433538],[103.9134537,1.3418311],[103.9139499,1.3425731],[103.9140408,1.3426463],[103.9153107,1.3417224],[103.9159457,1.3429042],[103.9158532,1.3429776],[103.9140941,1.3443743],[103.9140654,1.3444446],[103.9140566,1.3445179],[103.9140787,1.3445835],[103.9141146,1.3446537],[103.9144502,1.3448907],[103.9148587,1.3460027],[103.9154376,1.3464915],[103.9158196,1.3466307],[103.9162082,1.3468868],[103.9161936,1.3470546],[103.9162659,1.3472296],[103.9163946,1.347664],[103.9177671,1.3508888],[103.9194845,1.3550112],[103.9229231,1.36312],[103.9231645,1.3633559],[103.9250367,1.3677052],[103.9239155,1.3688583],[103.9210126,1.3719972],[103.9203045,1.3731073],[103.9199673,1.3741353],[103.9198861,1.3759764],[103.9198471,1.3766193],[103.9196447,1.3782503],[103.9193282,1.3790225],[103.9189151,1.3795481],[103.9183787,1.3798859],[103.9178154,1.3799825],[103.9171725,1.3798361],[103.9155202,1.3790263],[103.9148658,1.3785543],[103.9144259,1.3779752],[103.9139216,1.3767256],[103.9104401,1.3684882],[103.9089166,1.364761],[103.9077898,1.3637099],[103.9071783,1.3630556],[103.9062663,1.3628625],[103.9052627,1.3609013],[103.9036319,1.3569328],[103.9017602,1.356234],[103.9001192,1.3527481],[103.9002158,1.3523941],[103.8998982,1.3521491],[103.898997,1.3498323],[103.8987889,1.3491871],[103.8985249,1.3481054],[103.898643,1.3471401],[103.8990195,1.3456905],[103.8991912,1.3450952],[103.8992878,1.3443497],[103.8992985,1.3437118],[103.8994272,1.3432613],[103.8993682,1.3427358],[103.8985421,1.340789],[103.8984509,1.3405477],[103.8985582,1.3403117],[103.8999798,1.3395555],[103.9012509,1.3390514],[103.9020233,1.3388959]]\n[[103.8618108,1.4103426],[103.8612068,1.4093894],[103.8607667,1.4088788],[103.8606449,1.4084926],[103.8606857,1.4078665],[103.860675,1.4075965],[103.860616,1.4073266],[103.860321,1.4068349],[103.8600881,1.4064186],[103.8601096,1.4062331],[103.8628984,1.4043086],[103.8640385,1.406202],[103.8680919,1.4122578],[103.8695671,1.4113094],[103.8699513,1.4118973],[103.8692741,1.4123534],[103.8697729,1.4131512],[103.8706255,1.4144104],[103.8717721,1.4136798],[103.8724065,1.4146464],[103.8713014,1.4153811],[103.871453,1.4156424],[103.8709427,1.4159833],[103.8734169,1.4197729],[103.8737339,1.4195427],[103.8745809,1.4208349],[103.874584,1.421049],[103.8741932,1.4216462],[103.8738063,1.4220361],[103.8734881,1.4219623],[103.8730552,1.4220908],[103.8731026,1.4223908],[103.873028,1.4225579],[103.8729595,1.4227112],[103.8730112,1.4229981],[103.8728557,1.4231683],[103.8727174,1.4234246],[103.8730275,1.4239577],[103.873705,1.4232741],[103.8747738,1.4248319],[103.8738714,1.4254075],[103.8754989,1.4279216],[103.8744483,1.4286122],[103.8721701,1.4252403],[103.8719102,1.4247713],[103.8715338,1.4241805],[103.8713168,1.4241859],[103.8698675,1.4245786],[103.8691425,1.424517],[103.868598,1.4245542],[103.8673568,1.4245616],[103.8671265,1.4243387],[103.8674272,1.4240344],[103.8679261,1.4237443],[103.866421,1.4214335],[103.8657154,1.4219151],[103.8641323,1.4194202],[103.8636913,1.4184195],[103.8631901,1.4177118],[103.8625806,1.416788],[103.862529,1.4166122],[103.8625064,1.4163251],[103.8624512,1.4162245],[103.8619753,1.4153855],[103.8622463,1.4141277],[103.8623754,1.4128183],[103.8623007,1.4118791],[103.8620339,1.410912],[103.8618108,1.4103426]]\n[[113.5786749,22.1617608],[113.5750619,22.1625981],[113.5749168,22.1625754],[113.574725,22.1625465],[113.5739659,22.1627333],[113.5738512,22.1627473],[113.57375,22.1627199],[113.5736766,22.1626672],[113.57363,22.1626045],[113.573614,22.1625198],[113.5736099,22.162423],[113.57363,22.1622396],[113.5739311,22.1616227],[113.5740977,22.1612627],[113.5744522,22.1608593],[113.5748878,22.1601868],[113.5749476,22.1598031],[113.574954,22.1596271],[113.5746813,22.1587732],[113.5746755,22.1587552],[113.5746458,22.1586628],[113.5746039,22.1585507],[113.5745862,22.1585032],[113.5745219,22.1583708],[113.5744435,22.1582094],[113.574296,22.1579898],[113.5742727,22.1579642],[113.5741076,22.1577823],[113.5738848,22.1575983],[113.5738772,22.1575938],[113.5736248,22.1574451],[113.5732693,22.1572767],[113.57035,22.1558939],[113.5701742,22.1557569],[113.5700359,22.1556198],[113.5699057,22.1554302],[113.5698185,22.1552505],[113.5697522,22.1550823],[113.5697218,22.1548813],[113.569752,22.1544897],[113.5704667,22.1533025],[113.5710616,22.1523376],[113.575556,22.1542885],[113.5766367,22.1552071],[113.5766518,22.1553386],[113.5766004,22.1553861],[113.576744,22.1554431],[113.576759,22.1554339],[113.5895686,22.1475204],[113.5894167,22.1473249],[113.5893676,22.1472007],[113.5896444,22.1463103],[113.5897691,22.1461969],[113.5898778,22.1460474],[113.5899486,22.1460879],[113.5899435,22.146128],[113.5900867,22.146118],[113.590239,22.1460792],[113.5904614,22.1455896],[113.5908334,22.144539],[113.5943677,22.1349493],[113.5944831,22.1345515],[113.5947813,22.1342792],[113.59521,22.134029],[113.5955243,22.1340282],[113.5958481,22.1340829],[113.5978777,22.134755],[113.5981025,22.1349825],[113.5982529,22.1352573],[113.5983546,22.13555],[113.5982532,22.1359751],[113.5919202,22.1529645],[113.587962,22.1637974],[113.5875176,22.1650176],[113.5874001,22.1653146],[113.5871947,22.165483],[113.5867355,22.1657165],[113.5863856,22.16573],[113.5858101,22.1655501],[113.5851938,22.1653409],[113.5849985,22.165171],[113.5848301,22.16492],[113.5847,22.1645271],[113.5848314,22.16401],[113.5848607,22.1639116],[113.5850322,22.163465],[113.5788975,22.1615078],[113.5787899,22.1617241],[113.5786749,22.1617608]]\n[[113.8936114,22.3110822],[113.8932252,22.3109233],[113.892839,22.3106454],[113.8925995,22.3102756],[113.8924956,22.3098514],[113.8924956,22.3094146],[113.892513,22.3091865],[113.8926244,22.3088588],[113.8928819,22.3085015],[113.8932681,22.3083029],[113.8937831,22.3080647],[113.8957099,22.3076301],[113.8959188,22.3075057],[113.8960869,22.3073522],[113.896193,22.3072104],[113.8963568,22.3067777],[113.8969313,22.3052605],[113.8967004,22.3051816],[113.8970489,22.3042791],[113.8973563,22.3043775],[113.8981228,22.3023661],[113.8980253,22.3022728],[113.8980849,22.3020425],[113.8980982,22.3017759],[113.8980317,22.3014453],[113.8944268,22.2968679],[113.894154,22.2964714],[113.8939773,22.2961292],[113.8938751,22.2957451],[113.8938689,22.2952796],[113.8939168,22.2949052],[113.8940835,22.2946443],[113.894341,22.2943267],[113.8947272,22.294009],[113.8952805,22.2937002],[113.8960576,22.2936119],[113.8968301,22.2936119],[113.8976026,22.2935834],[113.8990893,22.293632],[113.9003095,22.2939546],[113.9012008,22.294282],[113.9023232,22.2945339],[113.9035794,22.294762],[113.904984,22.2949335],[113.9063144,22.2950526],[113.9079903,22.2951888],[113.9106955,22.2951945],[113.9116259,22.2951888],[113.9144851,22.2953306],[113.9150661,22.2953306],[113.9157227,22.2953306],[113.9159585,22.29531],[113.9160991,22.2952114],[113.9163059,22.2951967],[113.9165558,22.2951789],[113.9168915,22.2950938],[113.9171741,22.2950148],[113.917483,22.2949489],[113.9178713,22.2948455],[113.9182717,22.2947195],[113.9194426,22.2943414],[113.9199703,22.2941585],[113.9205833,22.2939737],[113.9212495,22.2937729],[113.9225364,22.2933849],[113.9244002,22.292848],[113.9272084,22.2920391],[113.927618,22.291899],[113.9280031,22.291785],[113.928436,22.2916353],[113.9287749,22.2915227],[113.9289428,22.2914871],[113.9290383,22.2915213],[113.929078,22.2916132],[113.9313551,22.2909542],[113.931996,22.2931665],[113.9328771,22.2946032],[113.933284,22.2955042],[113.9396112,22.3111174],[113.943222,22.3158458],[113.9443305,22.3194954],[113.9449129,22.3203413],[113.9458058,22.3208891],[113.9459933,22.3228069],[113.9458835,22.3228166],[113.9457611,22.3229167],[113.9457789,22.3231954],[113.9456664,22.3233406],[113.945174,22.3235324],[113.9451198,22.3236117],[113.945057,22.3236522],[113.9449778,22.3236801],[113.9449856,22.3237533],[113.9447461,22.3237739],[113.9447278,22.3236687],[113.9445526,22.3235556],[113.9442086,22.3232021],[113.9436936,22.3226463],[113.9395894,22.3218646],[113.9392319,22.3217854],[113.939026,22.3217191],[113.9360386,22.3207566],[113.9358255,22.3207739],[113.9356631,22.3208721],[113.9346461,22.3234694],[113.9337197,22.3239464],[113.9278739,22.3221448],[113.9208644,22.3198762],[113.8936114,22.3110822]]\n"
  },
  {
    "path": "src/test/resources/log4j.properties",
    "content": "log4j.rootLogger=INFO, stdout\n\n# Direct log messages to stdout\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] %c{1}:%L - %m%n\n\n# Remove unused logging\nlog4j.logger.org.reflections=WARN\n\n# Show our logs\nlog4j.logger.org.openstreetmap.atlas=DEBUG\nlog4j.logger.org.openstreetmap.atlas.utilities=INFO\nlog4j.logger.org.openstreetmap.atlas.streaming.resource=INFO\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.items.complex.bignode=DEBUG\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.items.TurnRestriction=DEBUG\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.pbf=INFO\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.packed=INFO\nlog4j.logger.org.openstreetmap.atlas.geography.atlas.delta=INFO\nlog4j.logger.org.openstreetmap.atlas.tags=INFO\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom1.wkt",
    "content": "MULTIPOLYGON(((4.284240019864911 43.13294029312797,7.624083769864911 43.10086194353044,7.646056426114911 41.4755361063697,4.306212676114911 41.52490557389508,4.284240019864911 43.13294029312797),(4.811583769864911 42.16328065593403,6.745177519864911 42.61766955007799,7.162657988614911 41.83670446074862,4.965392363614911 42.77915379405722,4.811583769864911 42.16328065593403)))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom10.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104838' action='modify' visible='true' lat='43.10248790611' lon='5.93329533407' />\n  <node id='-104853' action='modify' visible='true' lat='43.1022980005' lon='5.93287680911' />\n  <node id='-104854' action='modify' visible='true' lat='43.10198712901' lon='5.93331176428' />\n  <node id='-104857' action='modify' visible='true' lat='43.10250424812' lon='5.93422966731' />\n  <node id='-104858' action='modify' visible='true' lat='43.10200347115' lon='5.93424609751' />\n  <node id='-104862' action='modify' visible='true' lat='43.10239526312' lon='5.93454751396' />\n  <node id='-104863' action='modify' visible='true' lat='43.10228246626' lon='5.93397834835' />\n  <node id='-104864' action='modify' visible='true' lat='43.10207765037' lon='5.93447027005' />\n  <node id='-104865' action='modify' visible='true' lat='43.10217857423' lon='5.93471826364' />\n  <node id='-104866' action='modify' visible='true' lat='43.10233292803' lon='5.93469387083' />\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-102192' action='modify' visible='true'>\n    <nd ref='-104853' />\n    <nd ref='-104838' />\n    <nd ref='-104854' />\n    <nd ref='-104853' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-102477' action='modify' visible='true'>\n    <nd ref='-104857' />\n    <nd ref='-104838' />\n    <nd ref='-104854' />\n    <nd ref='-104858' />\n    <nd ref='-104857' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='-102506' action='modify' visible='true'>\n    <nd ref='-104862' />\n    <nd ref='-104863' />\n    <nd ref='-104864' />\n    <nd ref='-104865' />\n    <nd ref='-104866' />\n    <nd ref='-104862' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-102506' role='inner' />\n    <member type='way' ref='-102477' role='inner' />\n    <member type='way' ref='-102192' role='inner' />\n    <member type='way' ref='-101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom2.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104778' action='modify' visible='true' lat='43.10207958347' lon='5.93281030118' />\n  <node id='-104779' action='modify' visible='true' lat='43.10256696324' lon='5.93339685865' />\n  <node id='-104780' action='modify' visible='true' lat='43.10188534214' lon='5.93338796766' />\n  <node id='-104781' action='modify' visible='true' lat='43.10255974628' lon='5.93443472831' />\n  <node id='-104782' action='modify' visible='true' lat='43.10187812509' lon='5.93442583732' />\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <way id='-101786' action='modify' visible='true'>\n    <nd ref='-104778' />\n    <nd ref='-104779' />\n    <nd ref='-104780' />\n    <nd ref='-104778' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-101789' action='modify' visible='true'>\n    <nd ref='-104779' />\n    <nd ref='-104781' />\n    <nd ref='-104782' />\n    <nd ref='-104780' />\n    <nd ref='-104779' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-101793' role='outer' />\n    <member type='way' ref='-101789' role='inner' />\n    <member type='way' ref='-101786' role='inner' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom3.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104778' action='modify' visible='true' lat='43.10207958347' lon='5.93281030118' />\n  <node id='-104779' action='modify' visible='true' lat='43.10256696324' lon='5.93339685865' />\n  <node id='-104780' action='modify' visible='true' lat='43.10188534214' lon='5.93338796766' />\n  <node id='-104781' action='modify' visible='true' lat='43.10255974628' lon='5.93443472831' />\n  <node id='-104782' action='modify' visible='true' lat='43.10187812509' lon='5.93442583732' />\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104789' action='modify' visible='true' lat='43.10179061902' lon='5.93321268098' />\n  <node id='-104790' action='modify' visible='true' lat='43.10164570421' lon='5.93349288081' />\n  <node id='-104791' action='modify' visible='true' lat='43.10165422862' lon='5.93447124521' />\n  <node id='-104792' action='modify' visible='true' lat='43.10177357023' lon='5.93468840007' />\n  <way id='-101786' action='modify' visible='true'>\n    <nd ref='-104778' />\n    <nd ref='-104779' />\n    <nd ref='-104780' />\n    <nd ref='-104778' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-101789' action='modify' visible='true'>\n    <nd ref='-104779' />\n    <nd ref='-104781' />\n    <nd ref='-104782' />\n    <nd ref='-104780' />\n    <nd ref='-104779' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-101917' action='modify' visible='true'>\n    <nd ref='-104780' />\n    <nd ref='-104789' />\n    <nd ref='-104790' />\n    <nd ref='-104791' />\n    <nd ref='-104792' />\n    <nd ref='-104782' />\n    <nd ref='-104780' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-101917' role='inner' />\n    <member type='way' ref='-101793' role='outer' />\n    <member type='way' ref='-101789' role='inner' />\n    <member type='way' ref='-101786' role='inner' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom4.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104778' action='modify' visible='true' lat='43.10207958347' lon='5.93281030118' />\n  <node id='-104779' action='modify' visible='true' lat='43.10256696324' lon='5.93339685865' />\n  <node id='-104780' action='modify' visible='true' lat='43.10188534214' lon='5.93338796766' />\n  <node id='-104781' action='modify' visible='true' lat='43.10255974628' lon='5.93443472831' />\n  <node id='-104782' action='modify' visible='true' lat='43.10187812509' lon='5.93442583732' />\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104792' action='modify' visible='true' lat='43.10177357023' lon='5.93468840007' />\n  <node id='-104795' action='modify' visible='true' lat='43.10267715087' lon='5.93460900779' />\n  <node id='-104796' action='modify' visible='true' lat='43.10254417216' lon='5.93480981766' />\n  <node id='-104797' action='modify' visible='true' lat='43.10197133752' lon='5.93487052763' />\n  <way id='-101786' action='modify' visible='true'>\n    <nd ref='-104778' />\n    <nd ref='-104779' />\n    <nd ref='-104780' />\n    <nd ref='-104778' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-101789' action='modify' visible='true'>\n    <nd ref='-104779' />\n    <nd ref='-104781' />\n    <nd ref='-104782' />\n    <nd ref='-104780' />\n    <nd ref='-104779' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-101976' action='modify' visible='true'>\n    <nd ref='-104781' />\n    <nd ref='-104795' />\n    <nd ref='-104796' />\n    <nd ref='-104797' />\n    <nd ref='-104792' />\n    <nd ref='-104782' />\n    <nd ref='-104781' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-101976' role='inner' />\n    <member type='way' ref='-101793' role='outer' />\n    <member type='way' ref='-101789' role='inner' />\n    <member type='way' ref='-101786' role='inner' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom5.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104778' action='modify' visible='true' lat='43.10207958347' lon='5.93281030118' />\n  <node id='-104779' action='modify' visible='true' lat='43.10256696324' lon='5.93339685865' />\n  <node id='-104780' action='modify' visible='true' lat='43.10188534214' lon='5.93338796766' />\n  <node id='-104781' action='modify' visible='true' lat='43.10255974628' lon='5.93443472831' />\n  <node id='-104782' action='modify' visible='true' lat='43.10187812509' lon='5.93442583732' />\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104792' action='modify' visible='true' lat='43.10177357023' lon='5.93468840007' />\n  <node id='-104795' action='modify' visible='true' lat='43.10267715087' lon='5.93460900779' />\n  <node id='-104796' action='modify' visible='true' lat='43.10257144987' lon='5.93545894727' />\n  <node id='-104797' action='modify' visible='true' lat='43.10197133752' lon='5.93487052763' />\n  <way id='-101786' action='modify' visible='true'>\n    <nd ref='-104778' />\n    <nd ref='-104779' />\n    <nd ref='-104780' />\n    <nd ref='-104778' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-101789' action='modify' visible='true'>\n    <nd ref='-104779' />\n    <nd ref='-104781' />\n    <nd ref='-104782' />\n    <nd ref='-104780' />\n    <nd ref='-104779' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-101976' action='modify' visible='true'>\n    <nd ref='-104781' />\n    <nd ref='-104795' />\n    <nd ref='-104796' />\n    <nd ref='-104797' />\n    <nd ref='-104792' />\n    <nd ref='-104782' />\n    <nd ref='-104781' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-101976' role='inner' />\n    <member type='way' ref='-101793' role='outer' />\n    <member type='way' ref='-101789' role='inner' />\n    <member type='way' ref='-101786' role='inner' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom6.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104778' action='modify' visible='true' lat='43.10218869505' lon='5.93359019071' />\n  <node id='-104779' action='modify' visible='true' lat='43.10242716486' lon='5.93403197826' />\n  <node id='-104780' action='modify' visible='true' lat='43.10203196125' lon='5.93412115721' />\n  <node id='-104781' action='modify' visible='true' lat='43.10255974628' lon='5.93443472831' />\n  <node id='-104782' action='modify' visible='true' lat='43.10187812509' lon='5.93442583732' />\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104815' action='modify' visible='true' lat='43.10256696324' lon='5.93339685865' />\n  <node id='-104816' action='modify' visible='true' lat='43.10188534214' lon='5.93338796766' />\n  <way id='-101786' action='modify' visible='true'>\n    <nd ref='-104778' />\n    <nd ref='-104779' />\n    <nd ref='-104780' />\n    <nd ref='-104778' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-101789' action='modify' visible='true'>\n    <nd ref='-104815' />\n    <nd ref='-104781' />\n    <nd ref='-104782' />\n    <nd ref='-104816' />\n    <nd ref='-104815' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-101793' role='outer' />\n    <member type='way' ref='-101789' role='inner' />\n    <member type='way' ref='-101786' role='inner' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom7.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104778' action='modify' visible='true' lat='43.10218869505' lon='5.93359019071' />\n  <node id='-104779' action='modify' visible='true' lat='43.10242716486' lon='5.93403197826' />\n  <node id='-104780' action='modify' visible='true' lat='43.10203196125' lon='5.93412115721' />\n  <node id='-104781' action='modify' visible='true' lat='43.10255974628' lon='5.93443472831' />\n  <node id='-104782' action='modify' visible='true' lat='43.10187812509' lon='5.93442583732' />\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104815' action='modify' visible='true' lat='43.10256696324' lon='5.93339685865' />\n  <node id='-104816' action='modify' visible='true' lat='43.10188534214' lon='5.93338796766' />\n  <way id='-101786' action='modify' visible='true'>\n    <nd ref='-104778' />\n    <nd ref='-104779' />\n    <nd ref='-104780' />\n    <nd ref='-104778' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-101789' action='modify' visible='true'>\n    <nd ref='-104815' />\n    <nd ref='-104781' />\n    <nd ref='-104782' />\n    <nd ref='-104816' />\n    <nd ref='-104815' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-101793' role='outer' />\n    <member type='way' ref='-101789' role='outer' />\n    <member type='way' ref='-101786' role='inner' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom8.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104837' action='modify' visible='true' lat='43.10218530014' lon='5.93353285416' />\n  <node id='-104838' action='modify' visible='true' lat='43.10242376996' lon='5.93397464172' />\n  <node id='-104839' action='modify' visible='true' lat='43.10202856633' lon='5.93406382067' />\n  <node id='-104840' action='modify' visible='true' lat='43.10218530014' lon='5.93353285416' />\n  <node id='-104841' action='modify' visible='true' lat='43.10242376996' lon='5.93397464172' />\n  <node id='-104842' action='modify' visible='true' lat='43.10202856633' lon='5.93406382067' />\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-102192' action='modify' visible='true'>\n    <nd ref='-104837' />\n    <nd ref='-104838' />\n    <nd ref='-104839' />\n    <nd ref='-104837' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-102204' action='modify' visible='true'>\n    <nd ref='-104840' />\n    <nd ref='-104841' />\n    <nd ref='-104842' />\n    <nd ref='-104840' />\n    <tag k='natural' v='beach' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-102192' role='inner' />\n    <member type='way' ref='-102204' role='inner' />\n    <member type='way' ref='-101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/MultiPolygonTestGeom9.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104838' action='modify' visible='true' lat='43.10260483809' lon='5.9357146623' />\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-102192' action='modify' visible='true'>\n    <nd ref='-104786' />\n    <nd ref='-104838' />\n    <nd ref='-104787' />\n    <nd ref='-104786' />\n    <tag k='natural' v='beach' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-102192' role='inner' />\n    <member type='way' ref='-101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/builder/overpass-turbo.geojson",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/25026709\",\n      \"properties\": {\n        \"@id\": \"way/25026709\",\n        \"amenity\": \"parking\",\n        \"building\": \"yes\",\n        \"parking\": \"multi-storey\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0793742,\n              37.3951243\n            ],\n            [\n              -122.0797481,\n              37.394521\n            ],\n            [\n              -122.0793565,\n              37.3943665\n            ],\n            [\n              -122.0789896,\n              37.3949702\n            ],\n            [\n              -122.0793742,\n              37.3951243\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/25795737\",\n      \"properties\": {\n        \"@id\": \"way/25795737\",\n        \"amenity\": \"parking\",\n        \"capacity\": \"340\",\n        \"capacity:bike_locke\": \"116\",\n        \"capacity:bike_rack\": \"23\",\n        \"capacity:compact\": \"0\",\n        \"capacity:disabled\": \"10\",\n        \"capacity:disabled_v\": \"0\",\n        \"capacity:kiss_n_rid\": \"0\",\n        \"capacity:loading_zn\": \"Yes\",\n        \"capacity:motorcycle\": \"0\",\n        \"capacity:standard\": \"328\",\n        \"fee\": \"yes\",\n        \"name\": \"Mountain View\",\n        \"park_ride\": \"yes\",\n        \"vta:id\": \"22a0d4e8cc8a426eb0c202feb11f0ce9\",\n        \"vta:owner\": \"Caltrain\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.073351,\n              37.3932839\n            ],\n            [\n              -122.0746818,\n              37.393813\n            ],\n            [\n              -122.0747954,\n              37.3938582\n            ],\n            [\n              -122.0748169,\n              37.393822\n            ],\n            [\n              -122.0760413,\n              37.3943036\n            ],\n            [\n              -122.0760131,\n              37.3943505\n            ],\n            [\n              -122.0764919,\n              37.3945433\n            ],\n            [\n              -122.0765201,\n              37.3944965\n            ],\n            [\n              -122.0765724,\n              37.3945156\n            ],\n            [\n              -122.0767306,\n              37.3942578\n            ],\n            [\n              -122.0760746,\n              37.3939966\n            ],\n            [\n              -122.0742506,\n              37.3932702\n            ],\n            [\n              -122.0735307,\n              37.3929835\n            ],\n            [\n              -122.073351,\n              37.3932839\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/30860595\",\n      \"properties\": {\n        \"@id\": \"way/30860595\",\n        \"area\": \"yes\",\n        \"leisure\": \"common\",\n        \"name\": \"Centennial Plaza\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0780194,\n              37.3947255\n            ],\n            [\n              -122.0776748,\n              37.3945913\n            ],\n            [\n              -122.077542,\n              37.3948076\n            ],\n            [\n              -122.0779617,\n              37.3949727\n            ],\n            [\n              -122.0780596,\n              37.394814\n            ],\n            [\n              -122.0780194,\n              37.3947255\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/133164446\",\n      \"properties\": {\n        \"@id\": \"way/133164446\",\n        \"area\": \"yes\",\n        \"highway\": \"pedestrian\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0776158,\n              37.3945369\n            ],\n            [\n              -122.0775283,\n              37.3946877\n            ],\n            [\n              -122.0774701,\n              37.3947271\n            ],\n            [\n              -122.0774221,\n              37.3947596\n            ],\n            [\n              -122.0772627,\n              37.3948144\n            ],\n            [\n              -122.0770817,\n              37.3947927\n            ],\n            [\n              -122.0769726,\n              37.3947505\n            ],\n            [\n              -122.0768663,\n              37.3946797\n            ],\n            [\n              -122.0768332,\n              37.3946535\n            ],\n            [\n              -122.076783,\n              37.3946227\n            ],\n            [\n              -122.0767284,\n              37.3944937\n            ],\n            [\n              -122.0767537,\n              37.3943832\n            ],\n            [\n              -122.0768396,\n              37.3942365\n            ],\n            [\n              -122.0767623,\n              37.3942091\n            ],\n            [\n              -122.0767291,\n              37.3942665\n            ],\n            [\n              -122.0765503,\n              37.3945759\n            ],\n            [\n              -122.0766207,\n              37.3946044\n            ],\n            [\n              -122.0766092,\n              37.3946238\n            ],\n            [\n              -122.0765966,\n              37.3946439\n            ],\n            [\n              -122.076707,\n              37.3946864\n            ],\n            [\n              -122.0767393,\n              37.3946987\n            ],\n            [\n              -122.0774137,\n              37.3949577\n            ],\n            [\n              -122.0776765,\n              37.3945623\n            ],\n            [\n              -122.0776158,\n              37.3945369\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/148259786\",\n      \"properties\": {\n        \"@id\": \"way/148259786\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0773335,\n              37.3940225\n            ],\n            [\n              -122.0768952,\n              37.3938447\n            ],\n            [\n              -122.0768002,\n              37.3939926\n            ],\n            [\n              -122.0772384,\n              37.3941704\n            ],\n            [\n              -122.0773335,\n              37.3940225\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/148259831\",\n      \"properties\": {\n        \"@id\": \"way/148259831\",\n        \"amenity\": \"parking\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0773435,\n              37.3940114\n            ],\n            [\n              -122.0775833,\n              37.393622\n            ],\n            [\n              -122.0771274,\n              37.3934517\n            ],\n            [\n              -122.0768838,\n              37.3938246\n            ],\n            [\n              -122.0773435,\n              37.3940114\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/149851681\",\n      \"properties\": {\n        \"@id\": \"way/149851681\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0780504,\n              37.3943787\n            ],\n            [\n              -122.0778399,\n              37.3942939\n            ],\n            [\n              -122.0778549,\n              37.3942702\n            ],\n            [\n              -122.0775247,\n              37.3941372\n            ],\n            [\n              -122.0774445,\n              37.3942628\n            ],\n            [\n              -122.077654,\n              37.3943472\n            ],\n            [\n              -122.0776407,\n              37.394368\n            ],\n            [\n              -122.0779721,\n              37.3945015\n            ],\n            [\n              -122.0780504,\n              37.3943787\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/152945040\",\n      \"properties\": {\n        \"@id\": \"way/152945040\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0779284,\n              37.3949753\n            ],\n            [\n              -122.0775262,\n              37.3948168\n            ],\n            [\n              -122.0774709,\n              37.3949054\n            ],\n            [\n              -122.0778731,\n              37.3950639\n            ],\n            [\n              -122.0779284,\n              37.3949753\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/176608622\",\n      \"properties\": {\n        \"@id\": \"way/176608622\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0790188,\n              37.3946698\n            ],\n            [\n              -122.0790355,\n              37.3946469\n            ],\n            [\n              -122.0789844,\n              37.3946167\n            ],\n            [\n              -122.0790352,\n              37.3945356\n            ],\n            [\n              -122.0787302,\n              37.3944161\n            ],\n            [\n              -122.0786158,\n              37.3946195\n            ],\n            [\n              -122.0787015,\n              37.394653\n            ],\n            [\n              -122.0787045,\n              37.3946406\n            ],\n            [\n              -122.0788214,\n              37.3946862\n            ],\n            [\n              -122.0788495,\n              37.3946903\n            ],\n            [\n              -122.0789367,\n              37.3947233\n            ],\n            [\n              -122.0789713,\n              37.3946525\n            ],\n            [\n              -122.0790188,\n              37.3946698\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/176608625\",\n      \"properties\": {\n        \"@id\": \"way/176608625\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0791204,\n              37.394539\n            ],\n            [\n              -122.079158,\n              37.3944814\n            ],\n            [\n              -122.0787986,\n              37.3943344\n            ],\n            [\n              -122.0787543,\n              37.3944015\n            ],\n            [\n              -122.0791204,\n              37.394539\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/176608629\",\n      \"properties\": {\n        \"@id\": \"way/176608629\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.079152,\n              37.3941552\n            ],\n            [\n              -122.0792291,\n              37.3940383\n            ],\n            [\n              -122.0790129,\n              37.3939538\n            ],\n            [\n              -122.0789428,\n              37.3940777\n            ],\n            [\n              -122.079152,\n              37.3941552\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799842\",\n      \"properties\": {\n        \"@id\": \"way/205799842\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0785346,\n              37.3942365\n            ],\n            [\n              -122.0783039,\n              37.3941384\n            ],\n            [\n              -122.0782032,\n              37.394301\n            ],\n            [\n              -122.0784388,\n              37.3943875\n            ],\n            [\n              -122.0785346,\n              37.3942365\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799843\",\n      \"properties\": {\n        \"@id\": \"way/205799843\",\n        \"amenity\": \"restaurant\",\n        \"building\": \"yes\",\n        \"cuisine\": \"vietnamese\",\n        \"name\": \"Xanh\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0787215,\n              37.3950445\n            ],\n            [\n              -122.0787775,\n              37.3949269\n            ],\n            [\n              -122.078499,\n              37.3948162\n            ],\n            [\n              -122.0784268,\n              37.3949358\n            ],\n            [\n              -122.0787215,\n              37.3950445\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799844\",\n      \"properties\": {\n        \"@id\": \"way/205799844\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0792666,\n              37.3942858\n            ],\n            [\n              -122.0789299,\n              37.3941504\n            ],\n            [\n              -122.0789019,\n              37.3941966\n            ],\n            [\n              -122.0792252,\n              37.3943243\n            ],\n            [\n              -122.0792666,\n              37.3942858\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799845\",\n      \"properties\": {\n        \"@id\": \"way/205799845\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.078403,\n              37.3944482\n            ],\n            [\n              -122.0784388,\n              37.3943875\n            ],\n            [\n              -122.0782032,\n              37.394301\n            ],\n            [\n              -122.0781691,\n              37.3943591\n            ],\n            [\n              -122.078403,\n              37.3944482\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799847\",\n      \"properties\": {\n        \"@id\": \"way/205799847\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0783478,\n              37.3945295\n            ],\n            [\n              -122.078403,\n              37.3944482\n            ],\n            [\n              -122.0781691,\n              37.3943591\n            ],\n            [\n              -122.0781236,\n              37.394443\n            ],\n            [\n              -122.0783478,\n              37.3945295\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799848\",\n      \"properties\": {\n        \"@id\": \"way/205799848\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0786272,\n              37.3940828\n            ],\n            [\n              -122.0784056,\n              37.3939949\n            ],\n            [\n              -122.0783413,\n              37.3940932\n            ],\n            [\n              -122.0785688,\n              37.3941835\n            ],\n            [\n              -122.0786272,\n              37.3940828\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799849\",\n      \"properties\": {\n        \"@id\": \"way/205799849\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0788109,\n              37.3949349\n            ],\n            [\n              -122.0788922,\n              37.3948056\n            ],\n            [\n              -122.0785786,\n              37.3946858\n            ],\n            [\n              -122.078499,\n              37.3948162\n            ],\n            [\n              -122.0787775,\n              37.3949269\n            ],\n            [\n              -122.0788109,\n              37.3949349\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799850\",\n      \"properties\": {\n        \"@id\": \"way/205799850\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0786272,\n              37.3940828\n            ],\n            [\n              -122.078712,\n              37.3939366\n            ],\n            [\n              -122.0784841,\n              37.3938453\n            ],\n            [\n              -122.0784729,\n              37.3938618\n            ],\n            [\n              -122.0783871,\n              37.3939876\n            ],\n            [\n              -122.0784056,\n              37.3939949\n            ],\n            [\n              -122.0786272,\n              37.3940828\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799851\",\n      \"properties\": {\n        \"@id\": \"way/205799851\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0786056,\n              37.3946244\n            ],\n            [\n              -122.0785786,\n              37.3946858\n            ],\n            [\n              -122.0788922,\n              37.3948056\n            ],\n            [\n              -122.0789223,\n              37.3948315\n            ],\n            [\n              -122.078969,\n              37.3947589\n            ],\n            [\n              -122.0786056,\n              37.3946244\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799852\",\n      \"properties\": {\n        \"@id\": \"way/205799852\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0789118,\n              37.3941189\n            ],\n            [\n              -122.0792895,\n              37.3942636\n            ],\n            [\n              -122.0793144,\n              37.3942153\n            ],\n            [\n              -122.079152,\n              37.3941552\n            ],\n            [\n              -122.0789428,\n              37.3940777\n            ],\n            [\n              -122.0789118,\n              37.3941189\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/205799853\",\n      \"properties\": {\n        \"@id\": \"way/205799853\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:housenumber\": \"174\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"building\": \"yes\",\n        \"cuisine\": \"thai\",\n        \"name\": \"Amarin Thai Cuisine\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.078871,\n              37.3941927\n            ],\n            [\n              -122.0787992,\n              37.3943142\n            ],\n            [\n              -122.0790173,\n              37.3943966\n            ],\n            [\n              -122.079053,\n              37.3943308\n            ],\n            [\n              -122.0792008,\n              37.3943837\n            ],\n            [\n              -122.0792366,\n              37.394336\n            ],\n            [\n              -122.078871,\n              37.3941927\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/231426015\",\n      \"properties\": {\n        \"@id\": \"way/231426015\",\n        \"amenity\": \"parking\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0781256,\n              37.394392\n            ],\n            [\n              -122.078445,\n              37.393874\n            ],\n            [\n              -122.0778389,\n              37.3936289\n            ],\n            [\n              -122.0775275,\n              37.3941215\n            ],\n            [\n              -122.0781256,\n              37.394392\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/310754186\",\n      \"properties\": {\n        \"@id\": \"way/310754186\",\n        \"addr:housenumber\": \"102\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"building\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0786738,\n              37.3951686\n            ],\n            [\n              -122.0787105,\n              37.3951105\n            ],\n            [\n              -122.0783938,\n              37.3949912\n            ],\n            [\n              -122.078358,\n              37.3950521\n            ],\n            [\n              -122.0786738,\n              37.3951686\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/310754187\",\n      \"properties\": {\n        \"@id\": \"way/310754187\",\n        \"addr:housenumber\": \"108\",\n        \"addr:postcode\": \"94041\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"building\": \"yes\",\n        \"cuisine\": \"italian\",\n        \"email\": \"vasoazzurro@gmail.com\",\n        \"name\": \"Vaso Azzurro\",\n        \"phone\": \"+1 650 940-1717\",\n        \"website\": \"www.vasoazzurro.com\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0787105,\n              37.3951105\n            ],\n            [\n              -122.0787436,\n              37.3950551\n            ],\n            [\n              -122.0784268,\n              37.3949358\n            ],\n            [\n              -122.0783938,\n              37.3949912\n            ],\n            [\n              -122.0787105,\n              37.3951105\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/8945233\",\n      \"properties\": {\n        \"@id\": \"way/8945233\",\n        \"highway\": \"unclassified\",\n        \"name\": \"West Evelyn Avenue\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Evelyn\",\n        \"tiger:name_direction_prefix\": \"W\",\n        \"tiger:name_type\": \"Ave\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0782277,\n            37.3951044\n          ],\n          [\n            -122.0786891,\n            37.3952862\n          ],\n          [\n            -122.0792844,\n            37.3955209\n          ],\n          [\n            -122.080424,\n            37.39597\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/8946639\",\n      \"properties\": {\n        \"@id\": \"way/8946639\",\n        \"highway\": \"residential\",\n        \"name\": \"Santa Clara Avenue\",\n        \"tiger:cfcc\": \"A41\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Santa Clara\",\n        \"tiger:name_type\": \"Ave\",\n        \"tiger:reviewed\": \"no\",\n        \"tiger:separated\": \"no\",\n        \"tiger:source\": \"tiger_import_dch_v0.6_20070809\",\n        \"tiger:tlid\": \"122955240\",\n        \"tiger:zip_left\": \"94043\",\n        \"tiger:zip_right\": \"94043\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0763498,\n            37.3952637\n          ],\n          [\n            -122.0759421,\n            37.3959051\n          ],\n          [\n            -122.0759447,\n            37.395952\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/8948572\",\n      \"properties\": {\n        \"@id\": \"way/8948572\",\n        \"highway\": \"residential\",\n        \"name\": \"Willowgate Street\",\n        \"name_1\": \"Willow Gate Gardens\",\n        \"tiger:cfcc\": \"A41\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Willowgate\",\n        \"tiger:name_base_1\": \"Willow Gate Gardens\",\n        \"tiger:name_type\": \"St\",\n        \"tiger:zip_left\": \"94043\",\n        \"tiger:zip_right\": \"94043\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0717256,\n            37.3934609\n          ],\n          [\n            -122.0718007,\n            37.3934556\n          ],\n          [\n            -122.0748987,\n            37.3946884\n          ],\n          [\n            -122.0755639,\n            37.3949483\n          ],\n          [\n            -122.0763498,\n            37.3952637\n          ],\n          [\n            -122.0769881,\n            37.3955088\n          ],\n          [\n            -122.077023,\n            37.3955493\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/8965859\",\n      \"properties\": {\n        \"@id\": \"way/8965859\",\n        \"highway\": \"secondary\",\n        \"maxspeed\": \"25 mph\",\n        \"name\": \"Castro Street\",\n        \"oneway\": \"yes\",\n        \"source:maxspeed\": \"sign\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Castro:Moffett\",\n        \"tiger:name_type\": \"Blvd:St\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0778716,\n            37.3955527\n          ],\n          [\n            -122.078066,\n            37.3953351\n          ],\n          [\n            -122.0780917,\n            37.3953089\n          ],\n          [\n            -122.0781272,\n            37.3952677\n          ],\n          [\n            -122.0782277,\n            37.3951044\n          ],\n          [\n            -122.0784154,\n            37.3947552\n          ],\n          [\n            -122.0784435,\n            37.3946923\n          ],\n          [\n            -122.0784644,\n            37.3946251\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/8968653\",\n      \"properties\": {\n        \"@id\": \"way/8968653\",\n        \"highway\": \"service\",\n        \"name\": \"Cherry Lane\",\n        \"oneway\": \"yes\",\n        \"service\": \"parking_aisle\",\n        \"tiger:cfcc\": \"A41\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Cherry\",\n        \"tiger:name_type\": \"Ln\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0786891,\n            37.3952862\n          ],\n          [\n            -122.0789434,\n            37.39488\n          ],\n          [\n            -122.0791971,\n            37.3944814\n          ],\n          [\n            -122.0794437,\n            37.3940895\n          ],\n          [\n            -122.0795123,\n            37.3940346\n          ],\n          [\n            -122.0796971,\n            37.3937357\n          ],\n          [\n            -122.0797539,\n            37.3937608\n          ],\n          [\n            -122.0798833,\n            37.3935405\n          ],\n          [\n            -122.080065,\n            37.3932532\n          ],\n          [\n            -122.0801977,\n            37.3933071\n          ],\n          [\n            -122.0802438,\n            37.3932874\n          ],\n          [\n            -122.0804127,\n            37.3930163\n          ],\n          [\n            -122.0805128,\n            37.3928591\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/8969746\",\n      \"properties\": {\n        \"@id\": \"way/8969746\",\n        \"highway\": \"residential\",\n        \"name\": \"Hope Street\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Hope\",\n        \"tiger:name_type\": \"St\",\n        \"tiger:zip_left\": \"94041\",\n        \"tiger:zip_right\": \"94041\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0772566,\n            37.3943074\n          ],\n          [\n            -122.077434,\n            37.394003\n          ],\n          [\n            -122.07745,\n            37.3939751\n          ],\n          [\n            -122.0776933,\n            37.3935902\n          ],\n          [\n            -122.0778335,\n            37.3933684\n          ],\n          [\n            -122.0780466,\n            37.3930261\n          ],\n          [\n            -122.0782693,\n            37.3926645\n          ],\n          [\n            -122.0782934,\n            37.3926254\n          ],\n          [\n            -122.0783457,\n            37.3925413\n          ],\n          [\n            -122.0786185,\n            37.3921023\n          ],\n          [\n            -122.0794094,\n            37.3908458\n          ],\n          [\n            -122.0796422,\n            37.3904772\n          ],\n          [\n            -122.0799294,\n            37.3900144\n          ],\n          [\n            -122.0801816,\n            37.3896078\n          ],\n          [\n            -122.0807578,\n            37.388684\n          ],\n          [\n            -122.0807731,\n            37.388659\n          ],\n          [\n            -122.080814,\n            37.388592\n          ],\n          [\n            -122.0808636,\n            37.3885108\n          ],\n          [\n            -122.08107,\n            37.388173\n          ],\n          [\n            -122.0812444,\n            37.3878868\n          ],\n          [\n            -122.0812507,\n            37.3878774\n          ],\n          [\n            -122.0812864,\n            37.3878191\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/8970380\",\n      \"properties\": {\n        \"@id\": \"way/8970380\",\n        \"highway\": \"service\",\n        \"name\": \"Blossom Lane\",\n        \"oneway\": \"yes\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Blossom\",\n        \"tiger:name_type\": \"Ln\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0779388,\n            37.3945734\n          ],\n          [\n            -122.0780851,\n            37.3943367\n          ],\n          [\n            -122.0781105,\n            37.3942696\n          ],\n          [\n            -122.0782049,\n            37.3941227\n          ],\n          [\n            -122.0782259,\n            37.39409\n          ],\n          [\n            -122.0782624,\n            37.3940331\n          ],\n          [\n            -122.0782648,\n            37.3940011\n          ],\n          [\n            -122.0782373,\n            37.3939535\n          ],\n          [\n            -122.0781897,\n            37.3938711\n          ],\n          [\n            -122.0781883,\n            37.3938434\n          ],\n          [\n            -122.0782187,\n            37.3937958\n          ],\n          [\n            -122.0783148,\n            37.3936451\n          ],\n          [\n            -122.0783349,\n            37.3935674\n          ],\n          [\n            -122.0785187,\n            37.3932732\n          ],\n          [\n            -122.0785737,\n            37.3932317\n          ],\n          [\n            -122.0786568,\n            37.3931006\n          ],\n          [\n            -122.0787046,\n            37.3930549\n          ],\n          [\n            -122.0787561,\n            37.3929738\n          ],\n          [\n            -122.0788112,\n            37.392893\n          ],\n          [\n            -122.0788446,\n            37.3928439\n          ],\n          [\n            -122.079186,\n            37.3923323\n          ],\n          [\n            -122.0792156,\n            37.3922899\n          ],\n          [\n            -122.0793846,\n            37.392015\n          ],\n          [\n            -122.0794743,\n            37.3917808\n          ],\n          [\n            -122.079479,\n            37.3917687\n          ],\n          [\n            -122.0795028,\n            37.3917065\n          ],\n          [\n            -122.0794429,\n            37.3915072\n          ],\n          [\n            -122.0795415,\n            37.3913586\n          ],\n          [\n            -122.0796461,\n            37.3912094\n          ],\n          [\n            -122.0797856,\n            37.390991\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/25795741\",\n      \"properties\": {\n        \"@id\": \"way/25795741\",\n        \"access\": \"no\",\n        \"bus\": \"yes\",\n        \"car\": \"no\",\n        \"highway\": \"service\",\n        \"oneway\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0775635,\n            37.3944271\n          ],\n          [\n            -122.0774457,\n            37.3946192\n          ],\n          [\n            -122.0774008,\n            37.3946654\n          ],\n          [\n            -122.0773527,\n            37.3946907\n          ],\n          [\n            -122.0772968,\n            37.3947106\n          ],\n          [\n            -122.0772323,\n            37.3947222\n          ],\n          [\n            -122.0771647,\n            37.3947251\n          ],\n          [\n            -122.0770986,\n            37.3947181\n          ],\n          [\n            -122.0770314,\n            37.394699\n          ],\n          [\n            -122.0769701,\n            37.3946669\n          ],\n          [\n            -122.0769204,\n            37.3946273\n          ],\n          [\n            -122.0768842,\n            37.3945779\n          ],\n          [\n            -122.0768643,\n            37.3945358\n          ],\n          [\n            -122.0768563,\n            37.3944886\n          ],\n          [\n            -122.0768566,\n            37.3944488\n          ],\n          [\n            -122.076872,\n            37.3943967\n          ],\n          [\n            -122.0769896,\n            37.3942033\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/30860591\",\n      \"properties\": {\n        \"@id\": \"way/30860591\",\n        \"access\": \"no\",\n        \"bus\": \"yes\",\n        \"car\": \"no\",\n        \"highway\": \"service\",\n        \"oneway\": \"yes\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0771447,\n            37.3942638\n          ],\n          [\n            -122.0770994,\n            37.3943313\n          ],\n          [\n            -122.077086,\n            37.3943622\n          ],\n          [\n            -122.0770645,\n            37.3944528\n          ],\n          [\n            -122.0770632,\n            37.3944847\n          ],\n          [\n            -122.0770873,\n            37.3945156\n          ],\n          [\n            -122.0771745,\n            37.3945508\n          ],\n          [\n            -122.0772268,\n            37.3945529\n          ],\n          [\n            -122.0772684,\n            37.3945316\n          ],\n          [\n            -122.0773301,\n            37.3944741\n          ],\n          [\n            -122.0773583,\n            37.3944336\n          ],\n          [\n            -122.0774031,\n            37.3943645\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/30906778\",\n      \"properties\": {\n        \"@id\": \"way/30906778\",\n        \"highway\": \"secondary\",\n        \"maxspeed\": \"25 mph\",\n        \"name\": \"Castro Street\",\n        \"oneway\": \"yes\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Castro\",\n        \"tiger:name_type\": \"St\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0784644,\n            37.3946251\n          ],\n          [\n            -122.07841,\n            37.3946731\n          ],\n          [\n            -122.0783821,\n            37.3946945\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/30906781\",\n      \"properties\": {\n        \"@id\": \"way/30906781\",\n        \"HFCS\": \"Minor Arterial\",\n        \"highway\": \"secondary\",\n        \"maxspeed\": \"25 mph\",\n        \"name\": \"Castro Street\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Castro\",\n        \"tiger:name_type\": \"St\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0784644,\n            37.3946251\n          ],\n          [\n            -122.0786777,\n            37.3942782\n          ],\n          [\n            -122.0789615,\n            37.393816\n          ],\n          [\n            -122.0792758,\n            37.3933044\n          ],\n          [\n            -122.0793864,\n            37.3931245\n          ],\n          [\n            -122.0797378,\n            37.3925524\n          ],\n          [\n            -122.0800586,\n            37.3920303\n          ],\n          [\n            -122.080078,\n            37.3919987\n          ],\n          [\n            -122.0800785,\n            37.3919979\n          ],\n          [\n            -122.0801216,\n            37.3919412\n          ],\n          [\n            -122.0803319,\n            37.3915855\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/91933519\",\n      \"properties\": {\n        \"@id\": \"way/91933519\",\n        \"highway\": \"service\",\n        \"service\": \"parking_aisle\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0760299,\n            37.3940675\n          ],\n          [\n            -122.0765961,\n            37.3942927\n          ],\n          [\n            -122.0764979,\n            37.3944485\n          ],\n          [\n            -122.0759317,\n            37.3942233\n          ],\n          [\n            -122.0741051,\n            37.3934966\n          ],\n          [\n            -122.0734953,\n            37.393254\n          ],\n          [\n            -122.0735935,\n            37.3930982\n          ],\n          [\n            -122.0742033,\n            37.3933408\n          ],\n          [\n            -122.0760299,\n            37.3940675\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/133164443\",\n      \"properties\": {\n        \"@id\": \"way/133164443\",\n        \"highway\": \"footway\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0774722,\n            37.3943915\n          ],\n          [\n            -122.0774192,\n            37.3944743\n          ],\n          [\n            -122.0773129,\n            37.3946021\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/133164447\",\n      \"properties\": {\n        \"@id\": \"way/133164447\",\n        \"highway\": \"footway\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0770329,\n            37.3945827\n          ],\n          [\n            -122.0769668,\n            37.3944572\n          ],\n          [\n            -122.0770329,\n            37.3943306\n          ],\n          [\n            -122.0770856,\n            37.3942407\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/133164448\",\n      \"properties\": {\n        \"@id\": \"way/133164448\",\n        \"highway\": \"footway\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.076707,\n            37.3946864\n          ],\n          [\n            -122.0766992,\n            37.3946987\n          ],\n          [\n            -122.0766711,\n            37.3947433\n          ],\n          [\n            -122.0766422,\n            37.3947904\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/144330038\",\n      \"properties\": {\n        \"@id\": \"way/144330038\",\n        \"highway\": \"footway\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0786777,\n            37.3942782\n          ],\n          [\n            -122.0791971,\n            37.3944814\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/144330046\",\n      \"properties\": {\n        \"@id\": \"way/144330046\",\n        \"highway\": \"footway\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0786777,\n            37.3942782\n          ],\n          [\n            -122.0782259,\n            37.39409\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/149436519\",\n      \"properties\": {\n        \"@id\": \"way/149436519\",\n        \"cycleway\": \"lane\",\n        \"highway\": \"secondary\",\n        \"name\": \"West Evelyn Avenue\",\n        \"tiger:county\": \"Santa Clara, CA\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0779388,\n            37.3945734\n          ],\n          [\n            -122.0775635,\n            37.3944271\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/149436522\",\n      \"properties\": {\n        \"@id\": \"way/149436522\",\n        \"cycleway\": \"lane\",\n        \"highway\": \"secondary\",\n        \"name\": \"West Evelyn Avenue\",\n        \"oneway\": \"yes\",\n        \"tiger:county\": \"Santa Clara, CA\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0779388,\n            37.3945734\n          ],\n          [\n            -122.0780141,\n            37.39462\n          ],\n          [\n            -122.0781683,\n            37.3946957\n          ],\n          [\n            -122.0782021,\n            37.3947397\n          ],\n          [\n            -122.078218,\n            37.3947767\n          ],\n          [\n            -122.0782257,\n            37.3948618\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/152943929\",\n      \"properties\": {\n        \"@id\": \"way/152943929\",\n        \"highway\": \"footway\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0765834,\n            37.3947505\n          ],\n          [\n            -122.0766422,\n            37.3947904\n          ],\n          [\n            -122.0767083,\n            37.3948144\n          ],\n          [\n            -122.0766799,\n            37.3948577\n          ],\n          [\n            -122.0766726,\n            37.3948749\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/152945039\",\n      \"properties\": {\n        \"@id\": \"way/152945039\",\n        \"highway\": \"footway\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0774701,\n            37.3947271\n          ],\n          [\n            -122.0774008,\n            37.3946654\n          ],\n          [\n            -122.0773508,\n            37.3946207\n          ],\n          [\n            -122.0773129,\n            37.3946021\n          ],\n          [\n            -122.0771693,\n            37.3946409\n          ],\n          [\n            -122.0770329,\n            37.3945827\n          ],\n          [\n            -122.0770041,\n            37.3946021\n          ],\n          [\n            -122.0769204,\n            37.3946273\n          ],\n          [\n            -122.0768332,\n            37.3946535\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/186521557\",\n      \"properties\": {\n        \"@id\": \"way/186521557\",\n        \"cycleway\": \"lane\",\n        \"highway\": \"secondary\",\n        \"name\": \"West Evelyn Avenue\",\n        \"oneway\": \"yes\",\n        \"tiger:county\": \"Santa Clara, CA\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0783821,\n            37.3946945\n          ],\n          [\n            -122.0783278,\n            37.3946917\n          ],\n          [\n            -122.0782848,\n            37.3946844\n          ],\n          [\n            -122.0781683,\n            37.3946424\n          ],\n          [\n            -122.0780262,\n            37.3945955\n          ],\n          [\n            -122.0779388,\n            37.3945734\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/188696880\",\n      \"properties\": {\n        \"@id\": \"way/188696880\",\n        \"highway\": \"secondary\",\n        \"maxspeed\": \"25 mph\",\n        \"name\": \"Castro Street\",\n        \"oneway\": \"yes\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Castro\",\n        \"tiger:name_type\": \"St\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0782257,\n            37.3948618\n          ],\n          [\n            -122.0779827,\n            37.3952078\n          ],\n          [\n            -122.0779506,\n            37.3952535\n          ],\n          [\n            -122.0778618,\n            37.39538\n          ],\n          [\n            -122.0778468,\n            37.3954014\n          ],\n          [\n            -122.0777715,\n            37.3955086\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/188696885\",\n      \"properties\": {\n        \"@id\": \"way/188696885\",\n        \"highway\": \"trunk\",\n        \"lanes\": \"2\",\n        \"maxspeed\": \"45 mph\",\n        \"name\": \"Central Expressway\",\n        \"oneway\": \"yes\",\n        \"ref\": \"G6\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0777715,\n            37.3955086\n          ],\n          [\n            -122.0751746,\n            37.3944765\n          ],\n          [\n            -122.0740699,\n            37.3940156\n          ],\n          [\n            -122.0732585,\n            37.3936747\n          ],\n          [\n            -122.0714748,\n            37.3929597\n          ],\n          [\n            -122.071149,\n            37.3928234\n          ],\n          [\n            -122.0708029,\n            37.3926678\n          ],\n          [\n            -122.0704516,\n            37.3925069\n          ],\n          [\n            -122.0695999,\n            37.3921209\n          ],\n          [\n            -122.0692221,\n            37.3919733\n          ],\n          [\n            -122.0690251,\n            37.3918963\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/207418066\",\n      \"properties\": {\n        \"@id\": \"way/207418066\",\n        \"highway\": \"service\",\n        \"service\": \"parking_aisle\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0777597,\n            37.3939495\n          ],\n          [\n            -122.0782049,\n            37.3941227\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/207418068\",\n      \"properties\": {\n        \"@id\": \"way/207418068\",\n        \"highway\": \"service\",\n        \"service\": \"parking_aisle\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.077434,\n            37.394003\n          ],\n          [\n            -122.0776669,\n            37.3940948\n          ],\n          [\n            -122.0781105,\n            37.3942696\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/207418070\",\n      \"properties\": {\n        \"@id\": \"way/207418070\",\n        \"highway\": \"service\",\n        \"service\": \"parking_aisle\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0776669,\n            37.3940948\n          ],\n          [\n            -122.0777597,\n            37.3939495\n          ],\n          [\n            -122.0778506,\n            37.3938072\n          ],\n          [\n            -122.0782373,\n            37.3939535\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/231369969\",\n      \"properties\": {\n        \"@id\": \"way/231369969\",\n        \"highway\": \"secondary\",\n        \"maxspeed\": \"25 mph\",\n        \"name\": \"Castro Street\",\n        \"oneway\": \"yes\",\n        \"tiger:county\": \"Santa Clara, CA\",\n        \"tiger:name_base\": \"Castro\",\n        \"tiger:name_type\": \"St\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0783821,\n            37.3946945\n          ],\n          [\n            -122.078345,\n            37.3947228\n          ],\n          [\n            -122.0783038,\n            37.3947604\n          ],\n          [\n            -122.0782654,\n            37.3948025\n          ],\n          [\n            -122.0782257,\n            37.3948618\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/240577403\",\n      \"properties\": {\n        \"@id\": \"way/240577403\",\n        \"cycleway\": \"lane\",\n        \"highway\": \"secondary\",\n        \"name\": \"West Evelyn Avenue\",\n        \"tiger:county\": \"Santa Clara, CA\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0769896,\n            37.3942033\n          ],\n          [\n            -122.0761506,\n            37.3938762\n          ],\n          [\n            -122.0751427,\n            37.3934832\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/240577405\",\n      \"properties\": {\n        \"@id\": \"way/240577405\",\n        \"cycleway\": \"lane\",\n        \"highway\": \"secondary\",\n        \"name\": \"West Evelyn Avenue\",\n        \"tiger:county\": \"Santa Clara, CA\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0775635,\n            37.3944271\n          ],\n          [\n            -122.0774722,\n            37.3943915\n          ],\n          [\n            -122.0774031,\n            37.3943645\n          ],\n          [\n            -122.0772566,\n            37.3943074\n          ],\n          [\n            -122.0771447,\n            37.3942638\n          ],\n          [\n            -122.0770856,\n            37.3942407\n          ],\n          [\n            -122.0769896,\n            37.3942033\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"way/316719473\",\n      \"properties\": {\n        \"@id\": \"way/316719473\",\n        \"highway\": \"trunk\",\n        \"lanes\": \"2\",\n        \"maxspeed\": \"45 mph\",\n        \"name\": \"Central Expressway\",\n        \"oneway\": \"yes\",\n        \"ref\": \"G6\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.0674461,\n            37.3914603\n          ],\n          [\n            -122.0682952,\n            37.3917914\n          ],\n          [\n            -122.0689747,\n            37.3920736\n          ],\n          [\n            -122.0690527,\n            37.392106\n          ],\n          [\n            -122.0694926,\n            37.3923021\n          ],\n          [\n            -122.0704704,\n            37.3927381\n          ],\n          [\n            -122.0707587,\n            37.3928702\n          ],\n          [\n            -122.0710457,\n            37.3929938\n          ],\n          [\n            -122.0713407,\n            37.3931142\n          ],\n          [\n            -122.0750744,\n            37.3946038\n          ],\n          [\n            -122.0764396,\n            37.3951429\n          ],\n          [\n            -122.0773538,\n            37.3955059\n          ],\n          [\n            -122.0775995,\n            37.3956036\n          ],\n          [\n            -122.0776715,\n            37.3956322\n          ],\n          [\n            -122.0777716,\n            37.3956763\n          ],\n          [\n            -122.0781524,\n            37.3958284\n          ],\n          [\n            -122.0791183,\n            37.3962141\n          ],\n          [\n            -122.0797347,\n            37.396463\n          ],\n          [\n            -122.0804897,\n            37.3967794\n          ],\n          [\n            -122.0809499,\n            37.3969719\n          ],\n          [\n            -122.0809967,\n            37.3969915\n          ],\n          [\n            -122.0811208,\n            37.3970465\n          ],\n          [\n            -122.0816243,\n            37.3972504\n          ],\n          [\n            -122.0819837,\n            37.3973931\n          ],\n          [\n            -122.0826658,\n            37.3976646\n          ],\n          [\n            -122.0827105,\n            37.3976824\n          ],\n          [\n            -122.0828098,\n            37.3977224\n          ],\n          [\n            -122.0833287,\n            37.3979294\n          ],\n          [\n            -122.0835166,\n            37.3979972\n          ],\n          [\n            -122.08707,\n            37.3992789\n          ],\n          [\n            -122.0904975,\n            37.4006304\n          ],\n          [\n            -122.0906526,\n            37.4006916\n          ],\n          [\n            -122.0933007,\n            37.4017733\n          ],\n          [\n            -122.0958076,\n            37.4027972\n          ],\n          [\n            -122.097159,\n            37.4032212\n          ],\n          [\n            -122.0972812,\n            37.4032623\n          ],\n          [\n            -122.0974034,\n            37.4033034\n          ],\n          [\n            -122.0980062,\n            37.4035374\n          ],\n          [\n            -122.0981819,\n            37.4036056\n          ],\n          [\n            -122.098503,\n            37.4037303\n          ],\n          [\n            -122.101296,\n            37.4048145\n          ],\n          [\n            -122.1041719,\n            37.4060206\n          ],\n          [\n            -122.1046188,\n            37.4062537\n          ],\n          [\n            -122.1056145,\n            37.4068849\n          ],\n          [\n            -122.1057376,\n            37.4069565\n          ],\n          [\n            -122.1060773,\n            37.4071799\n          ],\n          [\n            -122.1064676,\n            37.4074362\n          ],\n          [\n            -122.1072437,\n            37.4079316\n          ],\n          [\n            -122.1077966,\n            37.4083017\n          ],\n          [\n            -122.1080281,\n            37.4084461\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/65531994\",\n      \"properties\": {\n        \"@id\": \"node/65531994\",\n        \"highway\": \"traffic_signals\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0772566,\n          37.3943074\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/281367666\",\n      \"properties\": {\n        \"@id\": \"node/281367666\",\n        \"highway\": \"traffic_signals\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0769896,\n          37.3942033\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/343244527\",\n      \"properties\": {\n        \"@id\": \"node/343244527\",\n        \"amenity\": \"atm\",\n        \"covered\": \"no\",\n        \"drive_through\": \"no\",\n        \"fee\": \"no\",\n        \"name\": \"Bank of America\",\n        \"opening_hours\": \"24/7\",\n        \"operator\": \"Bank of America\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.077769,\n          37.394911\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/343244536\",\n      \"properties\": {\n        \"@id\": \"node/343244536\",\n        \"amenity\": \"bicycle_parking\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0777035,\n          37.3948881\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/343266174\",\n      \"properties\": {\n        \"@id\": \"node/343266174\",\n        \"highway\": \"traffic_signals\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0774031,\n          37.3943645\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/343272363\",\n      \"properties\": {\n        \"@id\": \"node/343272363\",\n        \"amenity\": \"fast_food\",\n        \"name\": \"Subway\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0775272,\n          37.3942457\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/343588500\",\n      \"properties\": {\n        \"@id\": \"node/343588500\",\n        \"highway\": \"crossing\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0784644,\n          37.3946251\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/761885172\",\n      \"properties\": {\n        \"@id\": \"node/761885172\",\n        \"amenity\": \"bus_station\",\n        \"name\": \"Mountain View Transit Center\",\n        \"network\": \"VTA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0771889,\n          37.3944304\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1071547747\",\n      \"properties\": {\n        \"@id\": \"node/1071547747\",\n        \"highway\": \"crossing\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0786777,\n          37.3942782\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1306273777\",\n      \"properties\": {\n        \"@id\": \"node/1306273777\",\n        \"addr:housenumber\": \"135\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"cafe\",\n        \"cuisine\": \"mediterranean\",\n        \"email\": \"caffeolympus@gmail.com\",\n        \"name\": \"Olympus Caffè & Bakery\",\n        \"phone\": \"+1 (650) 336-7613\",\n        \"website\": \"http://cafeolympus.com/\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0783274,\n          37.3944999\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1306274949\",\n      \"properties\": {\n        \"@id\": \"node/1306274949\",\n        \"addr:housenumber\": \"124\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"regional\",\n        \"name\": \"Shell Shock\",\n        \"website\": \"http://www.shellshockrestaurant.com/\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0785389,\n          37.3947886\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1330277742\",\n      \"properties\": {\n        \"@id\": \"node/1330277742\",\n        \"addr:housenumber\": \"134\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"chinese\",\n        \"name\": \"Hangen Szechuan Chinese restaurant\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0786195,\n          37.3946702\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1576227438\",\n      \"properties\": {\n        \"@id\": \"node/1576227438\",\n        \"amenity\": \"pub\",\n        \"name\": \"Savvy Cellars\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0778444,\n          37.3949747\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1722140170\",\n      \"properties\": {\n        \"@id\": \"node/1722140170\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:housenumber\": \"186\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"mediterranean\",\n        \"name\": \"La Fontaine\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0789667,\n          37.394113\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1722141153\",\n      \"properties\": {\n        \"@id\": \"node/1722141153\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:housenumber\": \"180\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"japanese\",\n        \"name\": \"Shabuway\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0789423,\n          37.3941842\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1722142269\",\n      \"properties\": {\n        \"@id\": \"node/1722142269\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:housenumber\": \"160\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"pizza\",\n        \"name\": \"Doppio Zero\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0787958,\n          37.3943806\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1972437250\",\n      \"properties\": {\n        \"@id\": \"node/1972437250\",\n        \"crossing\": \"uncontrolled\",\n        \"highway\": \"crossing\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0782848,\n          37.3946844\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1972437252\",\n      \"properties\": {\n        \"@id\": \"node/1972437252\",\n        \"crossing\": \"uncontrolled\",\n        \"highway\": \"crossing\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0782021,\n          37.3947397\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2157827669\",\n      \"properties\": {\n        \"@id\": \"node/2157827669\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:housenumber\": \"198\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"mexican\",\n        \"name\": \"Agave\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0790172,\n          37.3940131\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2157827670\",\n      \"properties\": {\n        \"@id\": \"node/2157827670\",\n        \"addr:housenumber\": \"156\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"japanese\",\n        \"name\": \"Bushido\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0787507,\n          37.3944469\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2157827672\",\n      \"properties\": {\n        \"@id\": \"node/2157827672\",\n        \"addr:housenumber\": \"143\",\n        \"name\": \"Fotron\",\n        \"shop\": \"camera\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0783587,\n          37.3944675\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2157827673\",\n      \"properties\": {\n        \"@id\": \"node/2157827673\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:country\": \"US\",\n        \"addr:housenumber\": \"153\",\n        \"addr:postcode\": \"94041\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"chinese\",\n        \"name\": \"Fu Lam Mum\",\n        \"phone\": \"+1-650-967-1689\",\n        \"website\": \"http://www.flmchineserestaurant.com/\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0784664,\n          37.3943113\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2157827674\",\n      \"properties\": {\n        \"@id\": \"node/2157827674\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:country\": \"US\",\n        \"addr:housenumber\": \"147\",\n        \"addr:postcode\": \"94041\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"chinese\",\n        \"name\": \"Hong Kong Bistro\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0784014,\n          37.3944133\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2157827689\",\n      \"properties\": {\n        \"@id\": \"node/2157827689\",\n        \"addr:housenumber\": \"171\",\n        \"name\": \"Mountain View Lock Service\",\n        \"phone\": \"+1 650 691 1186\",\n        \"shop\": \"locksmith\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0785716,\n          37.3941407\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2176747620\",\n      \"properties\": {\n        \"@id\": \"node/2176747620\",\n        \"amenity\": \"bicycle_parking\",\n        \"bicycle_parking\": \"stands\",\n        \"capacity\": \"2\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0781355,\n          37.3945085\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2187335117\",\n      \"properties\": {\n        \"@id\": \"node/2187335117\",\n        \"addr:housenumber\": \"142\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"asian\",\n        \"name\": \"Asian Box\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0786733,\n          37.3945837\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2199873550\",\n      \"properties\": {\n        \"@id\": \"node/2199873550\",\n        \"addr:housenumber\": \"747\",\n        \"name\": \"Mountain View Hair & Nails\",\n        \"shop\": \"hairdresser\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0779412,\n          37.3944533\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/2626075058\",\n      \"properties\": {\n        \"@id\": \"node/2626075058\",\n        \"amenity\": \"bicycle_rental\",\n        \"capacity\": \"23\",\n        \"name\": \"Mountain View Caltrain Station\",\n        \"network\": \"Bay Area Bike Share\",\n        \"operator\": \"Bay Area Bike Share\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0767847,\n          37.3942775\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/3282721066\",\n      \"properties\": {\n        \"@id\": \"node/3282721066\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:housenumber\": \"152\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"japanese\",\n        \"name\": \"Ramen Izakaya\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0787297,\n          37.3944741\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/3282721067\",\n      \"properties\": {\n        \"@id\": \"node/3282721067\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:housenumber\": \"146\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"cuisine\": \"pizza\",\n        \"name\": \"Blue Line Pizza\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0787094,\n          37.3945143\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/3282721068\",\n      \"properties\": {\n        \"@id\": \"node/3282721068\",\n        \"addr:city\": \"Mountain View\",\n        \"addr:housenumber\": \"126\",\n        \"addr:street\": \"Castro Street\",\n        \"amenity\": \"restaurant\",\n        \"loc_name\": \"Bullshito\",\n        \"name\": \"Oren's Hummus Shop\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0785798,\n          37.3947246\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/343588500\",\n      \"properties\": {\n        \"@id\": \"node/343588500\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0784644,\n          37.3946251\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1972437250\",\n      \"properties\": {\n        \"@id\": \"node/1972437250\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0782848,\n          37.3946844\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1972437252\",\n      \"properties\": {\n        \"@id\": \"node/1972437252\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0782021,\n          37.3947397\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/65531994\",\n      \"properties\": {\n        \"@id\": \"node/65531994\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0772566,\n          37.3943074\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/281367666\",\n      \"properties\": {\n        \"@id\": \"node/281367666\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0769896,\n          37.3942033\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/343266174\",\n      \"properties\": {\n        \"@id\": \"node/343266174\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0774031,\n          37.3943645\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": \"node/1071547747\",\n      \"properties\": {\n        \"@id\": \"node/1071547747\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          -122.0786777,\n          37.3942782\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/ChangeAtlasTest.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38978' action='modify' lat='37.59269293962' lon='-122.24502370724' />\n  <node id='-38980' action='modify' lat='37.5930666147' lon='-122.24410491133' />\n  <node id='-38982' action='modify' lat='37.59269938231' lon='-122.24295844917' />\n  <node id='-38984' action='modify' lat='37.5931374839' lon='-122.24195021295' />\n  <node id='-38986' action='modify' lat='37.59206799606' lon='-122.24292592542' />\n  <node id='-38988' action='modify' lat='37.5932470089' lon='-122.24284461605' />\n  <node id='-38990' action='modify' lat='37.59387838514' lon='-122.2433568651'>\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='-38992' action='modify' lat='37.59344673053' lon='-122.24189329639' />\n  <node id='-38994' action='modify' lat='37.59354336984' lon='-122.24146235671' />\n  <node id='-38996' action='modify' lat='37.59334364847' lon='-122.2413078689' />\n  <node id='-38998' action='modify' lat='37.59304084407' lon='-122.24127534515' />\n  <node id='-39000' action='modify' lat='37.59296353211' lon='-122.24160871358' />\n  <node id='-41769' action='modify' lat='37.59333720261' lon='-122.24404392523' />\n  <node id='-41770' action='modify' lat='37.59333398129' lon='-122.2437756043' />\n  <node id='-41772' action='modify' lat='37.59320834981' lon='-122.24376747336' />\n  <node id='-41774' action='modify' lat='37.59319546452' lon='-122.24341784306' />\n  <node id='-41776' action='modify' lat='37.5932824402' lon='-122.24327148619' />\n  <node id='-41778' action='modify' lat='37.59308916078' lon='-122.24317798041' />\n  <node id='-41793' action='modify' lat='37.59273803521' lon='-122.24378780071' />\n  <node id='-41794' action='modify' lat='37.59269293639' lon='-122.24397481226' />\n  <node id='-41796' action='modify' lat='37.59256086113' lon='-122.24393009211' />\n  <node id='-41798' action='modify' lat='37.59248032732' lon='-122.24371462227' />\n  <node id='-41800' action='modify' lat='37.59258985328' lon='-122.24351134884' />\n  <node id='-41802' action='modify' lat='37.59270904312' lon='-122.24357639634' />\n  <node id='-41822' action='modify' lat='37.59261884542' lon='-122.24322270057'>\n    <tag k='natural' v='tree' />\n  </node>\n  <way id='-39002' action='modify'>\n    <nd ref='-38978' />\n    <nd ref='-38980' />\n    <nd ref='-38982' />\n    <nd ref='-38984' />\n    <tag k='highway' v='primary' />\n    <tag k='name' v='primary' />\n  </way>\n  <way id='-39004' action='modify'>\n    <nd ref='-38986' />\n    <nd ref='-38982' />\n    <nd ref='-38988' />\n    <nd ref='-38990' />\n    <tag k='highway' v='motorway' />\n    <tag k='name' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-39006' action='modify'>\n    <nd ref='-38992' />\n    <nd ref='-38994' />\n    <nd ref='-38996' />\n    <nd ref='-38998' />\n    <nd ref='-39000' />\n    <nd ref='-38984' />\n    <nd ref='-38992' />\n    <tag k='highway' v='secondary' />\n    <tag k='junction' v='roundabout' />\n    <tag k='name' v='secondary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-41771' action='modify'>\n    <nd ref='-41769' />\n    <nd ref='-41770' />\n    <nd ref='-41772' />\n    <nd ref='-41774' />\n    <nd ref='-41776' />\n    <nd ref='-41778' />\n    <tag k='waterway' v='canal' />\n  </way>\n  <way id='-41795' action='modify'>\n    <nd ref='-41793' />\n    <nd ref='-41794' />\n    <nd ref='-41796' />\n    <nd ref='-41798' />\n    <nd ref='-41800' />\n    <nd ref='-41802' />\n    <nd ref='-41793' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <relation id='-39008' action='modify'>\n    <member type='way' ref='-39002' role='role1' />\n    <member type='way' ref='-39006' role='role2' />\n    <tag k='name' v='route1' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='-39010' action='modify'>\n    <member type='node' ref='-38990' role='endNode' />\n    <member type='way' ref='-39004' role='roleA' />\n    <member type='way' ref='-39006' role='roleB' />\n    <tag k='name' v='route2' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='-41834' action='modify'>\n    <member type='node' ref='-41822' role='tree' />\n    <member type='way' ref='-41795' role='pond' />\n    <member type='way' ref='-41771' role='river' />\n    <tag k='name' v='disconnected features' />\n    <tag k='type' v='group' />\n  </relation>\n  <relation id='-41860' action='modify'>\n    <member type='relation' ref='-39008' role='child2' />\n    <member type='relation' ref='-41834' role='child1' />\n    <tag k='name' v='parent' />\n    <tag k='type' v='parent' />\n  </relation>\n  <relation id='-41861' action='modify'>\n    <member type='relation' ref='-41860' role='child1' />\n    <tag k='name' v='super-parent' />\n    <tag k='type' v='super-parent' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/ChangeAtlasTestEdge.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38999' action='modify' lat='37.59269293962' lon='-122.24502370724' />\n  <node id='-39000' action='modify' lat='37.5930666147' lon='-122.24410491133' />\n  <node id='-39002' action='modify' lat='37.59269938231' lon='-122.24295844917' />\n  <node id='-39004' action='modify' lat='37.5931374839' lon='-122.24195021295' />\n  <node id='-39013' action='modify' lat='37.59206799606' lon='-122.24292592542' />\n  <node id='-39015' action='modify' lat='37.5932470089' lon='-122.24284461605' />\n  <node id='-39017' action='modify' lat='37.59387838514' lon='-122.2433568651' />\n  <node id='-39032' action='modify' lat='37.59344673053' lon='-122.24189329639' />\n  <node id='-39033' action='modify' lat='37.59354336984' lon='-122.24146235671' />\n  <node id='-39035' action='modify' lat='37.59334364847' lon='-122.2413078689' />\n  <node id='-39037' action='modify' lat='37.59304084407' lon='-122.24127534515' />\n  <node id='-39039' action='modify' lat='37.59296353211' lon='-122.24160871358' />\n  <way id='-39001' action='modify'>\n    <nd ref='-38999' />\n    <nd ref='-39000' />\n    <nd ref='-39002' />\n    <nd ref='-39004' />\n    <tag k='highway' v='primary' />\n    <tag k='name' v='primary' />\n  </way>\n  <way id='-39014' action='modify'>\n    <nd ref='-39013' />\n    <nd ref='-39002' />\n    <nd ref='-39015' />\n    <nd ref='-39017' />\n    <tag k='highway' v='motorway' />\n    <tag k='name' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-39034' action='modify'>\n    <nd ref='-39032' />\n    <nd ref='-39033' />\n    <nd ref='-39035' />\n    <nd ref='-39037' />\n    <nd ref='-39039' />\n    <nd ref='-39004' />\n    <nd ref='-39032' />\n    <tag k='highway' v='secondary' />\n    <tag k='junction' v='roundabout' />\n    <tag k='name' v='secondary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-39069' action='modify'>\n    <member type='way' ref='-39001' role='role1' />\n    <member type='way' ref='-39034' role='role2' />\n    <tag k='name' v='route1' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='-39079' action='modify'>\n    <member type='way' ref='-39014' role='roleA' />\n    <member type='way' ref='-39034' role='roleB' />\n    <tag k='name' v='route2' />\n    <tag k='type' v='route' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/MultipleChangeAtlasTest.atlas.txt",
    "content": "# Nodes\n5987265746000000 && 37.7693499,-122.4036904 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65320168000000 && 37.7689632,-122.4019854 && last_edit_user_name -> woodpeck_fixbot || last_edit_changeset -> 2102778 || last_edit_time -> 1249941564000 || last_edit_user_id -> 147510 || last_edit_version -> 3\n5438253600000000 && 37.7684088,-122.4070237 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n65303886000000 && 37.7684788,-122.4058441 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 11004366 || last_edit_time -> 1331955216000 || last_edit_user_id -> 14293 || last_edit_version -> 8\n4925013310000000 && 37.7705038,-122.4029296 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n5987119388000000 && 37.7686952,-122.4037649 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65315504000000 && 37.77007,-122.4069977 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195454000 || last_edit_user_id -> 33757 || highway -> traffic_signals || last_edit_version -> 7\n4941620852000000 && 37.7677676,-122.4039575 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n5987137963000000 && 37.7690443,-122.4031076 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4941620716000000 && 37.7698091,-122.4005449 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65303899000000 && 37.7683061,-122.408752 && last_edit_user_name -> oba510 || last_edit_changeset -> 47257631 || last_edit_time -> 1490787846000 || last_edit_user_id -> 933797 || last_edit_version -> 7\n1461189690000000 && 37.7715623,-122.4074212 && last_edit_user_name -> skela || last_edit_changeset -> 9521974 || last_edit_time -> 1318253588000 || last_edit_user_id -> 93285 || last_edit_version -> 1\n2304626336000000 && 37.7700557,-122.403919 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n5438253568000000 && 37.7686401,-122.4014282 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5438253589000000 && 37.7695354,-122.405646 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n4925013313000000 && 37.770111,-122.4025138 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n295219625000000 && 37.7693309,-122.4062061 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924469000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 6 || crossing -> zebra\n4925013320000000 && 37.7703252,-122.4020359 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || access -> no || barrier -> gate || last_edit_time -> 1522356645000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n4941620845000000 && 37.7683159,-122.4040717 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n539993902000000 && 37.768488,-122.4056892 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 11004366 || last_edit_time -> 1331955217000 || last_edit_user_id -> 14293 || last_edit_version -> 3\n65303721000000 && 37.7718329,-122.4016818 && last_edit_user_name -> bhalperin28 || last_edit_changeset -> 33172900 || last_edit_time -> 1438933354000 || last_edit_user_id -> 3130496 || highway -> traffic_signals || last_edit_version -> 10\n5438253599000000 && 37.7685929,-122.4070582 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5515121096000000 && 37.7698908,-122.4006353 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || access -> no || barrier -> gate || last_edit_time -> 1522356643000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4941620706000000 && 37.7696133,-122.3992382 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65294603000000 && 37.767373,-122.4028264 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || stop -> all || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> stop || last_edit_version -> 5\n5987137970000000 && 37.769043,-122.4031335 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || barrier -> gate || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5991623594000000 && 37.769006,-122.4037645 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725653000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 1\n4941620717000000 && 37.7688723,-122.4017139 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n5449464493000000 && 37.7691009,-122.4028607 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 56836576 || access -> permissive || barrier -> gate || last_edit_time -> 1520040596000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4941620822000000 && 37.7699518,-122.4007125 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n308712276000000 && 37.7713916,-122.406081 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 4692674 || last_edit_time -> 1273819520000 || last_edit_user_id -> 14293 || last_edit_version -> 22\n5438253588000000 && 37.7695328,-122.4056825 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n423775500000000 && 37.7703055,-122.3990512 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783391000 || last_edit_user_id -> 53073 || last_edit_version -> 4\n1461189670000000 && 37.7706311,-122.4063017 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 24109539 || last_edit_time -> 1405201543000 || last_edit_user_id -> 371121 || last_edit_version -> 3\n5438253559000000 && 37.7696467,-122.4000006 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n65340076000000 && 37.7672565,-122.4047579 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 6\n5987137908000000 && 37.7690142,-122.4036131 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641693000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 1\n5438253598000000 && 37.7687132,-122.4075933 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n4941620707000000 && 37.7691935,-122.3997679 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65294602000000 && 37.7686504,-122.4029523 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || last_edit_version -> 6\n1485472609000000 && 37.7701025,-122.4005184 && last_edit_user_name -> Telecas || last_edit_changeset -> 9690834 || last_edit_time -> 1319958914000 || last_edit_user_id -> 202256 || last_edit_version -> 1\n65282185000000 && 37.768536,-122.4048807 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 8\n4880860061000000 && 37.770003,-122.406914 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n5438253569000000 && 37.7681397,-122.4013813 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5987119105000000 && 37.7694627,-122.4035721 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5487354242000000 && 37.7702125,-122.4039102 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4941620697000000 && 37.7687623,-122.4006667 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n4941620823000000 && 37.7690946,-122.4029961 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || last_edit_version -> 3\n2304626334000000 && 37.7697586,-122.4043663 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n5449464503000000 && 37.7695056,-122.4036409 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || last_edit_time -> 1539630504000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 2\n5438253566000000 && 37.7697927,-122.402515 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n4925013311000000 && 37.7701009,-122.402596 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n4941620847000000 && 37.7684132,-122.4042781 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n5987119396000000 && 37.7690044,-122.4037977 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119099000000 && 37.7693645,-122.4034135 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4880860063000000 && 37.7701463,-122.4070931 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n4941620705000000 && 37.7693995,-122.3999967 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n1266064662000000 && 37.7712328,-122.4053572 && last_edit_user_name -> oba510 || last_edit_changeset -> 39324426 || last_edit_time -> 1463290425000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 4 || crossing -> traffic_signals\n5487354240000000 && 37.7699471,-122.4035915 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n65303723000000 && 37.7711307,-122.4008049 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || stop -> minor || last_edit_time -> 1522090886000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 7\n4941620821000000 && 37.7695094,-122.4001931 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65303878000000 && 37.7685958,-122.4038927 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || stop -> all || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> stop || last_edit_version -> 8\n3532718888000000 && 37.769884,-122.4042061 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 4 || crossing -> zebra\n5438253597000000 && 37.7688653,-122.4059504 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5438253576000000 && 37.7686496,-122.4053349 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n65339116000000 && 37.7694529,-122.4062261 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || source -> Yahoo || highway -> stop || last_edit_version -> 7\n4941620698000000 && 37.7686369,-122.4005136 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n2304626312000000 && 37.7697023,-122.4040067 && last_edit_user_name -> chachafish || last_edit_changeset -> 63559070 || last_edit_time -> 1539650461000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 5 || crossing -> uncontrolled\n1344032216000000 && 37.7694516,-122.3984773 && last_edit_user_name -> oldtopos || last_edit_changeset -> 8586501 || last_edit_time -> 1309399164000 || last_edit_user_id -> 169004 || railway -> level_crossing || last_edit_version -> 1\n314079148000000 && 37.7684257,-122.4050967 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 4663164 || last_edit_time -> 1273522822000 || last_edit_user_id -> 14293 || last_edit_version -> 20\n5987119390000000 && 37.7697239,-122.403851 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987265744000000 && 37.7694109,-122.4036338 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4925013315000000 && 37.770243,-122.4026003 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n5449464494000000 && 37.7691415,-122.4021759 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 56836576 || access -> permissive || barrier -> gate || last_edit_time -> 1520040596000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n65355343000000 && 37.7698702,-122.4023944 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || last_edit_time -> 1433631821000 || last_edit_user_id -> 371121 || last_edit_version -> 5\n5438253565000000 && 37.76977,-122.4028995 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n2304626359000000 && 37.7697727,-122.403881 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n5987119183000000 && 37.7693596,-122.4038258 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4880860062000000 && 37.7701423,-122.4069081 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n5438253591000000 && 37.7696728,-122.4057841 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n1344032637000000 && 37.7695087,-122.3984035 && last_edit_user_name -> maggot27 || last_edit_changeset -> 32570723 || last_edit_time -> 1436648775000 || last_edit_user_id -> 118021 || usage -> main || railway -> level_crossing || last_edit_version -> 3\n5987119098000000 && 37.7694224,-122.4034195 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119182000000 && 37.7693586,-122.4038462 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5438253596000000 && 37.768832,-122.4061382 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5987137969000000 && 37.7690411,-122.4032399 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || last_edit_time -> 1539646682000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 2\n3532719204000000 && 37.7712243,-122.405549 && last_edit_user_name -> oba510 || last_edit_changeset -> 39324426 || last_edit_time -> 1463290426000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n4941620699000000 && 37.7692088,-122.3997849 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65283737000000 && 37.7713009,-122.4085367 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195454000 || last_edit_user_id -> 33757 || highway -> traffic_signals || last_edit_version -> 9\n5487354231000000 && 37.7700015,-122.4020862 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || barrier -> bollard || last_edit_time -> 1522356645000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n4925013309000000 && 37.7704091,-122.40281 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n4941620704000000 && 37.7697893,-122.3994823 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n314079692000000 && 37.7672605,-122.4054727 && last_edit_user_name -> Cato_d_Ae || last_edit_changeset -> 21087627 || last_edit_time -> 1394737433000 || last_edit_user_id -> 1679807 || last_edit_version -> 7\n5987265745000000 && 37.7694294,-122.4032851 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5438253564000000 && 37.7697984,-122.4024073 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n65283717000000 && 37.7690324,-122.4069177 && last_edit_user_name -> Stephen214 || last_edit_changeset -> 56741589 || ref -> 433C || last_edit_time -> 1519788914000 || last_edit_user_id -> 4018842 || ref:right -> 433C || source -> http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf || highway -> motorway_junction || last_edit_version -> 16\n65356544000000 && 37.7694762,-122.3984446 && last_edit_user_name -> maggot27 || last_edit_changeset -> 32570723 || last_edit_time -> 1436648775000 || last_edit_user_id -> 118021 || usage -> main || railway -> level_crossing || last_edit_version -> 13\n4925013308000000 && 37.7706746,-122.4024774 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n5438253590000000 && 37.7695444,-122.4057841 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n2274244022000000 && 37.7698904,-122.4036185 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 6 || crossing -> zebra\n1344032359000000 && 37.7695295,-122.3985735 && last_edit_user_name -> oldtopos || last_edit_changeset -> 8586501 || last_edit_time -> 1309399167000 || last_edit_user_id -> 169004 || railway -> level_crossing || last_edit_version -> 1\n3532718890000000 && 37.7695218,-122.406127 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 3 || crossing -> zebra\n5987119397000000 && 37.7690045,-122.4037953 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n4941620710000000 && 37.7701304,-122.4004824 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65303885000000 && 37.7685143,-122.4053147 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672111000 || last_edit_user_id -> 5288591 || last_edit_version -> 7\n65340073000000 && 37.7697218,-122.4049953 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 16134749 || last_edit_time -> 1368600966000 || last_edit_user_id -> 14293 || last_edit_version -> 7\n5987119087000000 && 37.7697339,-122.4036687 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4941620700000000 && 37.768709,-122.4007362 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65374334000000 && 37.7697239,-122.4092051 && last_edit_user_name -> andygol || last_edit_changeset -> 50914033 || last_edit_time -> 1502104378000 || last_edit_user_id -> 94578 || last_edit_version -> 10\n1461189674000000 && 37.7707059,-122.4063969 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 19084727 || last_edit_time -> 1385260239000 || last_edit_user_id -> 371121 || last_edit_version -> 2\n5987137902000000 && 37.7690148,-122.4036033 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641693000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5038477632000000 && 37.7710243,-122.405102 && last_edit_user_name -> andygol || last_edit_changeset -> 51162893 || last_edit_time -> 1502872593000 || last_edit_user_id -> 94578 || last_edit_version -> 1\n5438253563000000 && 37.7699085,-122.4004492 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n4880860065000000 && 37.7697092,-122.4065564 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || last_edit_version -> 1\n5987137905000000 && 37.7690202,-122.4035015 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641693000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4179515466000000 && 37.7698128,-122.3991828 && last_edit_user_name -> karitotp || last_edit_changeset -> 39253310 || last_edit_time -> 1463001992000 || last_edit_user_id -> 2748195 || last_edit_version -> 1\n65371540000000 && 37.7660475,-122.4052442 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 4692674 || last_edit_time -> 1273819300000 || last_edit_user_id -> 14293 || last_edit_version -> 5\n5987119088000000 && 37.7697062,-122.4038918 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65371530000000 && 37.7717012,-122.4064466 && last_edit_user_name -> manoharuss || last_edit_changeset -> 50402608 || ref -> 1A;1B || ref:left -> 1A || last_edit_time -> 1500463872000 || last_edit_user_id -> 3769434 || ref:right -> 1B || source -> http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/80.pdf || highway -> motorway_junction || last_edit_version -> 12\n65283776000000 && 37.7697226,-122.4081672 && last_edit_user_name -> Cato_d_Ae || last_edit_changeset -> 21087627 || last_edit_time -> 1394737433000 || last_edit_user_id -> 1679807 || last_edit_version -> 8\n2304626320000000 && 37.7701309,-122.4040106 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 5 || crossing -> zebra\n5438253594000000 && 37.7686586,-122.4063058 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5438253573000000 && 37.7691482,-122.4020061 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5987119101000000 && 37.7694144,-122.403568 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5506470752000000 && 37.7692693,-122.3985025 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090881000 || last_edit_user_id -> 53073 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n4941620703000000 && 37.769298,-122.399884 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n703048830000000 && 37.7697377,-122.3981097 && last_edit_user_name -> StellanL || last_edit_changeset -> 19417081 || last_edit_time -> 1386867817000 || last_edit_user_id -> 28775 || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n984720636000000 && 37.7697271,-122.3983363 && last_edit_user_name -> yurasi || last_edit_changeset -> 44455726 || last_edit_time -> 1481918729000 || last_edit_user_id -> 3526564 || highway -> traffic_signals || last_edit_version -> 3\n4941620848000000 && 37.7678722,-122.4040486 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n3532719196000000 && 37.7695536,-122.4063509 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 3 || crossing -> zebra\n5487354238000000 && 37.7699296,-122.404258 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5987118542000000 && 37.7694049,-122.4032794 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641007000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3611937229000000 && 37.772489,-122.4025095 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 32152237 || last_edit_time -> 1435032266000 || last_edit_user_id -> 371121 || last_edit_version -> 1\n4925013307000000 && 37.770347,-122.4027316 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n3532718886000000 && 37.7697762,-122.4036728 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57466350 || last_edit_time -> 1521830620000 || last_edit_user_id -> 53073 || highway -> crossing || last_edit_version -> 3 || crossing -> zebra\n5487354233000000 && 37.7698849,-122.4021666 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n2304626330000000 && 37.7701231,-122.4038029 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 6 || crossing -> zebra\n2304626301000000 && 37.7700503,-122.4038576 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n5438253562000000 && 37.770056,-122.3994841 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5487354239000000 && 37.7696978,-122.4041318 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4941620701000000 && 37.7693329,-122.3999227 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n4179515467000000 && 37.7701236,-122.3995674 && last_edit_user_name -> karitotp || last_edit_changeset -> 39253310 || last_edit_time -> 1463001992000 || last_edit_user_id -> 2748195 || last_edit_version -> 1\n4925013317000000 && 37.7709337,-122.4027983 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n5987119395000000 && 37.7690036,-122.4038144 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4925013296000000 && 37.7702202,-122.4030315 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n4941620712000000 && 37.7702366,-122.4000335 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65308756000000 && 37.7692412,-122.4079015 && last_edit_user_name -> oba510 || last_edit_changeset -> 47257631 || last_edit_time -> 1490787847000 || last_edit_user_id -> 933797 || source -> Yahoo || highway -> traffic_signals || last_edit_version -> 8\n3532718892000000 && 37.7696585,-122.4058816 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195451000 || last_edit_user_id -> 33757 || last_edit_version -> 1\n3985226613000000 && 37.7697825,-122.4039838 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n4880860064000000 && 37.7699957,-122.4070864 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n5184349833000000 && 37.7717956,-122.3983947 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || highway -> crossing || last_edit_version -> 1\n708463538000000 && 37.770979,-122.3992503 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || barrier -> gate || last_edit_time -> 1523656387000 || last_edit_user_id -> 53073 || last_edit_version -> 5\n65310195000000 && 37.7672005,-122.4057234 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 5\n5987119394000000 && 37.769003,-122.4038265 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4925013316000000 && 37.7707063,-122.4025165 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n5438253585000000 && 37.7693069,-122.4057982 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5020763504000000 && 37.7690793,-122.3982601 && last_edit_user_name -> yurasi || last_edit_changeset -> 50926093 || last_edit_time -> 1502133983000 || last_edit_user_id -> 3526564 || last_edit_version -> 1\n5987119184000000 && 37.7694098,-122.4038217 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3532718887000000 && 37.7697728,-122.40414 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 4 || crossing -> zebra\n3611937228000000 && 37.7707359,-122.4047498 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n5487354232000000 && 37.769811,-122.4022176 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5987118543000000 && 37.7694122,-122.4031408 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641007000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4761685552000000 && 37.7676115,-122.4067451 && last_edit_user_name -> oba510 || last_edit_changeset -> 47257631 || last_edit_time -> 1490787816000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n65358380000000 && 37.769371,-122.406398 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924469000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 6 || crossing -> zebra\n65328379000000 && 37.7673142,-122.403785 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 5\n5987119100000000 && 37.7693566,-122.4035639 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4941620702000000 && 37.7697017,-122.3993608 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65303726000000 && 37.7704904,-122.400019 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || stop -> minor || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 7\n4925013318000000 && 37.7708579,-122.4027036 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || access -> no || barrier -> gate || last_edit_time -> 1522356645000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n65303894000000 && 37.7684205,-122.406827 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 11004366 || last_edit_time -> 1331955216000 || last_edit_user_id -> 14293 || last_edit_version -> 8\n5438253587000000 && 37.7693408,-122.4056286 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5487354235000000 && 37.7698517,-122.4027816 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n2304626360000000 && 37.7700073,-122.4040485 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n3532718885000000 && 37.7717718,-122.4017594 && last_edit_user_name -> oba510 || last_edit_changeset -> 39324426 || last_edit_time -> 1463290426000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n258877153000000 && 37.7709091,-122.3995214 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 4950654 || last_edit_time -> 1276133850000 || last_edit_user_id -> 14293 || last_edit_version -> 16\n5184349771000000 && 37.7703635,-122.3989802 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || barrier -> gate || last_edit_time -> 1523656388000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n5438253592000000 && 37.7688065,-122.4064989 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n295221650000000 && 37.7689731,-122.4086279 && last_edit_user_name -> clay_c || last_edit_changeset -> 61761408 || last_edit_time -> 1534562832000 || last_edit_user_id -> 119881 || last_edit_version -> 12\n984720713000000 && 37.7695543,-122.3985416 && last_edit_user_name -> stevea || last_edit_changeset -> 27413049 || last_edit_time -> 1418351083000 || last_edit_user_id -> 123633 || railway -> level_crossing || last_edit_version -> 5\n666571226000000 && 37.77573,-122.3933992 && last_edit_user_name -> StellanL || last_edit_changeset -> 38624789 || last_edit_time -> 1460817832000 || last_edit_user_id -> 28775 || highway -> traffic_signals || last_edit_version -> 4\n2548141867000000 && 37.7713038,-122.4054511 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195454000 || last_edit_user_id -> 33757 || highway -> traffic_signals || last_edit_version -> 3\n65320166000000 && 37.769885,-122.402073 && last_edit_user_name -> woodpeck_fixbot || last_edit_changeset -> 2102778 || last_edit_time -> 1249941564000 || last_edit_user_id -> 147510 || last_edit_version -> 3\n5438253560000000 && 37.7695592,-122.4000965 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n1344032342000000 && 37.7695848,-122.3985037 && last_edit_user_name -> stevea || last_edit_changeset -> 27413049 || last_edit_time -> 1418351083000 || last_edit_user_id -> 123633 || railway -> level_crossing || last_edit_version -> 2\n5184864687000000 && 37.769458,-122.4032877 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || last_edit_time -> 1539630504000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 2\n65296271000000 && 37.7698379,-122.4030693 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || last_edit_time -> 1433631821000 || last_edit_user_id -> 371121 || last_edit_version -> 6\n4925013312000000 && 37.7701571,-122.4024582 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n5987137965000000 && 37.7690936,-122.4031507 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987265747000000 && 37.7687092,-122.4035163 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4941620714000000 && 37.7691861,-122.4013223 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65320179000000 && 37.7661396,-122.4017216 && last_edit_user_name -> Nathan K || last_edit_changeset -> 18541703 || last_edit_time -> 1382729483000 || last_edit_user_id -> 732991 || highway -> traffic_signals || last_edit_version -> 6\n300444895000000 && 37.7698177,-122.403392 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 16134749 || last_edit_time -> 1368600967000 || last_edit_user_id -> 14293 || last_edit_version -> 7\n539993905000000 && 37.7693733,-122.4060831 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924469000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 4 || crossing -> zebra\n5184349835000000 && 37.7710444,-122.3993496 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5987137964000000 && 37.7690424,-122.4031466 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n1677798439000000 && 37.7687093,-122.4019618 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 11004366 || last_edit_time -> 1331955214000 || last_edit_user_id -> 14293 || last_edit_version -> 1\n5184349783000000 && 37.771683,-122.3985411 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || highway -> crossing || last_edit_version -> 1\n4941620709000000 && 37.7700035,-122.4003313 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n5987119103000000 && 37.7693519,-122.403653 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5438253567000000 && 37.7698641,-122.4025219 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n295222216000000 && 37.7643851,-122.4048876 && last_edit_user_name -> saikabhi || last_edit_changeset -> 50401838 || ref -> 433C;433B || ref:left -> 433C || last_edit_time -> 1500461881000 || last_edit_user_id -> 3029661 || ref:right -> 433B || source -> http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf || highway -> motorway_junction || last_edit_version -> 12\n5449464496000000 && 37.7687364,-122.4035185 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || last_edit_time -> 1539646682000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 3\n257670327000000 && 37.768741,-122.406751 && last_edit_user_name -> Cato_d_Ae || last_edit_changeset -> 21087627 || last_edit_time -> 1394737432000 || last_edit_user_id -> 1679807 || last_edit_version -> 8\n5184864686000000 && 37.7693307,-122.4036894 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || last_edit_time -> 1539646683000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 3\n5438253593000000 && 37.7688898,-122.4061596 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n733619113000000 && 37.7690681,-122.4092372 && noref -> yes || last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 63022562 || last_edit_time -> 1538167768000 || last_edit_user_id -> 33757 || source -> http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf || highway -> motorway_junction || last_edit_version -> 8\n65303732000000 && 37.7693828,-122.3986477 && last_edit_user_name -> nikh1ll || last_edit_changeset -> 51232075 || last_edit_time -> 1503058696000 || last_edit_user_id -> 2835928 || highway -> traffic_signals || last_edit_version -> 8\n3532718884000000 && 37.7717342,-122.4015592 && last_edit_user_name -> oba510 || last_edit_changeset -> 39324426 || last_edit_time -> 1463290426000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n65283721000000 && 37.7701419,-122.4083742 && last_edit_user_name -> woodpeck_fixbot || last_edit_changeset -> 3156706 || last_edit_time -> 1258601627000 || last_edit_user_id -> 147510 || last_edit_version -> 5\n5987119387000000 && 37.7693149,-122.4038257 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987137901000000 && 37.7690212,-122.4034821 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || last_edit_time -> 1539646682000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 2\n4941620715000000 && 37.7689121,-122.4009596 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719598000 || last_edit_user_id -> 5659851 || last_edit_version -> 1\n65283822000000 && 37.7700689,-122.4058533 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 4692759 || last_edit_time -> 1273821155000 || last_edit_user_id -> 14293 || last_edit_version -> 6\n4925027421000000 && 37.7702367,-122.4019313 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n4179515465000000 && 37.7695009,-122.3987942 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57720035 || last_edit_time -> 1522608840000 || last_edit_user_id -> 53073 || last_edit_version -> 3\n5987119386000000 && 37.7693601,-122.4038174 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5438253561000000 && 37.7699822,-122.3995842 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || access -> no || barrier -> gate || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n4925013319000000 && 37.7704995,-122.4022604 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || last_edit_version -> 1\n5487354234000000 && 37.7697775,-122.4027728 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n2304626355000000 && 37.7699907,-122.4037591 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || last_edit_version -> 2\n5438253574000000 && 37.7692147,-122.4052517 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672102000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n295221942000000 && 37.7690689,-122.4086034 && last_edit_user_name -> clay_c || last_edit_changeset -> 61761408 || last_edit_time -> 1534562832000 || last_edit_user_id -> 119881 || last_edit_version -> 13\n5987118546000000 && 37.7693579,-122.4038585 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641007000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n# Edges\n84823308000001 && 37.7700503,-122.4038576:37.7700872,-122.4038348:37.7701231,-122.4038029 && lcn_ref -> 23;36 || sidewalk -> both || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090892000 || last_edit_user_id -> 53073 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 15 || cycleway -> lane\n-84823308000001 && 37.7701231,-122.4038029:37.7700872,-122.4038348:37.7700503,-122.4038576 && lcn_ref -> 23;36 || sidewalk -> both || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090892000 || last_edit_user_id -> 53073 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 15 || cycleway -> lane\n84823308000002 && 37.7701231,-122.4038029:37.7709337,-122.4027983 && lcn_ref -> 23;36 || sidewalk -> both || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090892000 || last_edit_user_id -> 53073 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 15 || cycleway -> lane\n-84823308000002 && 37.7709337,-122.4027983:37.7701231,-122.4038029 && lcn_ref -> 23;36 || sidewalk -> both || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090892000 || last_edit_user_id -> 53073 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 15 || cycleway -> lane\n84823308000003 && 37.7709337,-122.4027983:37.7717718,-122.4017594 && lcn_ref -> 23;36 || sidewalk -> both || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090892000 || last_edit_user_id -> 53073 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 15 || cycleway -> lane\n-84823308000003 && 37.7717718,-122.4017594:37.7709337,-122.4027983 && lcn_ref -> 23;36 || sidewalk -> both || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090892000 || last_edit_user_id -> 53073 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 15 || cycleway -> lane\n84823308000004 && 37.7717718,-122.4017594:37.7718329,-122.4016818 && lcn_ref -> 23;36 || sidewalk -> both || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090892000 || last_edit_user_id -> 53073 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 15 || cycleway -> lane\n-84823308000004 && 37.7718329,-122.4016818:37.7717718,-122.4017594 && lcn_ref -> 23;36 || sidewalk -> both || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090892000 || last_edit_user_id -> 53073 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 15 || cycleway -> lane\n564435406000000 && 37.7688653,-122.4059504:37.768832,-122.4061382 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n-564435406000000 && 37.768832,-122.4061382:37.7688653,-122.4059504 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n8917113000001 && 37.7694529,-122.4062261:37.7693309,-122.4062061 && sidewalk -> right || last_edit_changeset -> 56699939 || tiger:name_type -> Ave || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Irina Karachevtseva || tiger:cfcc -> A41 || last_edit_time -> 1519672112000 || last_edit_user_id -> 5288591 || name -> San Bruno Avenue || tiger:name_base -> San Bruno || highway -> residential || last_edit_version -> 14\n8917113000002 && 37.7693309,-122.4062061:37.769068,-122.406023:37.7688653,-122.4059504 && sidewalk -> right || last_edit_changeset -> 56699939 || tiger:name_type -> Ave || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Irina Karachevtseva || tiger:cfcc -> A41 || last_edit_time -> 1519672112000 || last_edit_user_id -> 5288591 || name -> San Bruno Avenue || tiger:name_base -> San Bruno || highway -> residential || last_edit_version -> 14\n8917113000003 && 37.7688653,-122.4059504:37.7687084,-122.4058942:37.7684788,-122.4058441 && sidewalk -> right || last_edit_changeset -> 56699939 || tiger:name_type -> Ave || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Irina Karachevtseva || tiger:cfcc -> A41 || last_edit_time -> 1519672112000 || last_edit_user_id -> 5288591 || name -> San Bruno Avenue || tiger:name_base -> San Bruno || highway -> residential || last_edit_version -> 14\n8917113000004 && 37.7684788,-122.4058441:37.7672005,-122.4057234 && sidewalk -> right || last_edit_changeset -> 56699939 || tiger:name_type -> Ave || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Irina Karachevtseva || tiger:cfcc -> A41 || last_edit_time -> 1519672112000 || last_edit_user_id -> 5288591 || name -> San Bruno Avenue || tiger:name_base -> San Bruno || highway -> residential || last_edit_version -> 14\n162931810000001 && 37.77007,-122.4069977:37.7701463,-122.4070931 && last_edit_changeset -> 56663149 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Irina Karachevtseva || tiger:cfcc -> A41 || last_edit_time -> 1519571118000 || last_edit_user_id -> 5288591 || lanes -> 4 || name -> 9th Street || tiger:name_base -> 9th || highway -> primary || last_edit_version -> 9\n162931810000002 && 37.7701463,-122.4070931:37.7704698,-122.4074984:37.7713009,-122.4085367 && last_edit_changeset -> 56663149 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Irina Karachevtseva || tiger:cfcc -> A41 || last_edit_time -> 1519571118000 || last_edit_user_id -> 5288591 || lanes -> 4 || name -> 9th Street || tiger:name_base -> 9th || highway -> primary || last_edit_version -> 9\n496374147000000 && 37.7697092,-122.4065564:37.7697502,-122.4059698:37.7696585,-122.4058816 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-496374147000000 && 37.7696585,-122.4058816:37.7697502,-122.4059698:37.7697092,-122.4065564 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n43101607000001 && 37.7672005,-122.4057234:37.7673311,-122.4056711:37.7679417,-122.4056539:37.768488,-122.4056892 && sidewalk -> right || last_edit_changeset -> 49322246 || tiger:name_type -> Ave || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798778000 || last_edit_user_id -> 1330847 || name -> San Bruno Avenue || tiger:name_base -> San Bruno || highway -> residential || last_edit_version -> 8\n43101607000002 && 37.768488,-122.4056892:37.7688441,-122.4057741:37.7691291,-122.4058942:37.7693733,-122.4060831 && sidewalk -> right || last_edit_changeset -> 49322246 || tiger:name_type -> Ave || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798778000 || last_edit_user_id -> 1330847 || name -> San Bruno Avenue || tiger:name_base -> San Bruno || highway -> residential || last_edit_version -> 8\n43101607000003 && 37.7693733,-122.4060831:37.7694529,-122.4062261 && sidewalk -> right || last_edit_changeset -> 49322246 || tiger:name_type -> Ave || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798778000 || last_edit_user_id -> 1330847 || name -> San Bruno Avenue || tiger:name_base -> San Bruno || highway -> residential || last_edit_version -> 8\n634415566000000 && 37.769006,-122.4037645:37.7690142,-122.4036131 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725654000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2 || tunnel -> building_passage\n-634415566000000 && 37.7690142,-122.4036131:37.769006,-122.4037645 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725654000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2 || tunnel -> building_passage\n8916798000001 && 37.7687093,-122.4019618:37.7686589,-122.4028115:37.7686504,-122.4029523 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000001 && 37.7686504,-122.4029523:37.7686589,-122.4028115:37.7687093,-122.4019618 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n8916798000002 && 37.7686504,-122.4029523:37.768642,-122.4030951:37.7686174,-122.40351:37.7686072,-122.4036825:37.7685958,-122.4038927 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000002 && 37.7685958,-122.4038927:37.7686072,-122.4036825:37.7686174,-122.40351:37.768642,-122.4030951:37.7686504,-122.4029523 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n8916798000003 && 37.7685958,-122.4038927:37.768536,-122.4048807 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000003 && 37.768536,-122.4048807:37.7685958,-122.4038927 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n8916798000004 && 37.768536,-122.4048807:37.7685143,-122.4053147 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000004 && 37.7685143,-122.4053147:37.768536,-122.4048807 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n8916798000005 && 37.7685143,-122.4053147:37.768488,-122.4056892 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000005 && 37.768488,-122.4056892:37.7685143,-122.4053147 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n8916798000006 && 37.768488,-122.4056892:37.7684788,-122.4058441 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000006 && 37.7684788,-122.4058441:37.768488,-122.4056892 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n8916798000007 && 37.7684788,-122.4058441:37.7684205,-122.406827 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000007 && 37.7684205,-122.406827:37.7684788,-122.4058441 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n8916798000008 && 37.7684205,-122.406827:37.7684088,-122.4070237 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000008 && 37.7684088,-122.4070237:37.7684205,-122.406827 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n8916798000009 && 37.7684088,-122.4070237:37.7683619,-122.4078137:37.7683061,-122.408752 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n-8916798000009 && 37.7683061,-122.408752:37.7683619,-122.4078137:37.7684088,-122.4070237 && sidewalk -> both || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Alameda Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 21 || tiger:county -> San Francisco, CA\n634444999000000 && 37.7697023,-122.4040067:37.7697825,-122.4039838 && lcn_ref -> 123 || last_edit_changeset -> 63559070 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_time -> 1539650460000 || last_edit_user_id -> 2237750 || name -> Henry Adams Street || tiger:name_base -> Henry Adams || highway -> residential || last_edit_version -> 1 || cycleway -> shared_lane\n-634444999000000 && 37.7697825,-122.4039838:37.7697023,-122.4040067 && lcn_ref -> 123 || last_edit_changeset -> 63559070 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_time -> 1539650460000 || last_edit_user_id -> 2237750 || name -> Henry Adams Street || tiger:name_base -> Henry Adams || highway -> residential || last_edit_version -> 1 || cycleway -> shared_lane\n634412055000001 && 37.7693596,-122.4038258:37.7693601,-122.4038174 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412055000001 && 37.7693601,-122.4038174:37.7693596,-122.4038258 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412055000002 && 37.7693601,-122.4038174:37.7693612,-122.4037958:37.7693444,-122.4037944:37.7693499,-122.4036904 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412055000002 && 37.7693499,-122.4036904:37.7693444,-122.4037944:37.7693612,-122.4037958:37.7693601,-122.4038174 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412055000003 && 37.7693499,-122.4036904:37.7693519,-122.403653 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412055000003 && 37.7693519,-122.403653:37.7693499,-122.4036904 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412055000004 && 37.7693519,-122.403653:37.7693566,-122.4035639 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412055000004 && 37.7693566,-122.4035639:37.7693519,-122.403653 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412055000005 && 37.7693566,-122.4035639:37.7693645,-122.4034135 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412055000005 && 37.7693645,-122.4034135:37.7693566,-122.4035639 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412055000006 && 37.7693645,-122.4034135:37.7693718,-122.4032766:37.7694049,-122.4032794 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412055000006 && 37.7694049,-122.4032794:37.7693718,-122.4032766:37.7693645,-122.4034135 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412055000007 && 37.7694049,-122.4032794:37.7694122,-122.4031408 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412055000007 && 37.7694122,-122.4031408:37.7694049,-122.4032794 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n501802415000000 && 37.7707063,-122.4025165:37.7708579,-122.4027036 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> steps || last_edit_version -> 1\n-501802415000000 && 37.7708579,-122.4027036:37.7707063,-122.4025165 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> steps || last_edit_version -> 1\n564435409000000 && 37.7685929,-122.4070582:37.7687132,-122.4075933 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435409000000 && 37.7687132,-122.4075933:37.7685929,-122.4070582 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n504300644000000 && 37.7702366,-122.4000335:37.7699324,-122.3996513:37.7699822,-122.3995842 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n-504300644000000 && 37.7699822,-122.3995842:37.7699324,-122.3996513:37.7702366,-122.4000335 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n503918578000001 && 37.7677676,-122.4039575:37.7678722,-122.4040486 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n-503918578000001 && 37.7678722,-122.4040486:37.7677676,-122.4039575 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n503918578000002 && 37.7678722,-122.4040486:37.7683994,-122.4044953:37.7684111,-122.4044672:37.7684132,-122.4042781 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n-503918578000002 && 37.7684132,-122.4042781:37.7684111,-122.4044672:37.7683994,-122.4044953:37.7678722,-122.4040486 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n503918578000003 && 37.7684132,-122.4042781:37.7684153,-122.4040769:37.7683159,-122.4040717 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n-503918578000003 && 37.7683159,-122.4040717:37.7684153,-122.4040769:37.7684132,-122.4042781 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n503918578000004 && 37.7683159,-122.4040717:37.7678722,-122.4040486 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n-503918578000004 && 37.7678722,-122.4040486:37.7683159,-122.4040717 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 3\n221434102000001 && 37.7697218,-122.4049953:37.7696787,-122.4057206:37.7696728,-122.4057841 && lcn_ref -> 36 || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || cycleway -> lane || tiger:county -> San Francisco, CA\n-221434102000001 && 37.7696728,-122.4057841:37.7696787,-122.4057206:37.7697218,-122.4049953 && lcn_ref -> 36 || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || cycleway -> lane || tiger:county -> San Francisco, CA\n221434102000002 && 37.7696728,-122.4057841:37.7696712,-122.4058011:37.7696585,-122.4058816 && lcn_ref -> 36 || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || cycleway -> lane || tiger:county -> San Francisco, CA\n-221434102000002 && 37.7696585,-122.4058816:37.7696712,-122.4058011:37.7696728,-122.4057841 && lcn_ref -> 36 || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || cycleway -> lane || tiger:county -> San Francisco, CA\n221434102000003 && 37.7696585,-122.4058816:37.7696299,-122.4059593:37.7695917,-122.4060344:37.7695218,-122.406127 && lcn_ref -> 36 || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || cycleway -> lane || tiger:county -> San Francisco, CA\n-221434102000003 && 37.7695218,-122.406127:37.7695917,-122.4060344:37.7696299,-122.4059593:37.7696585,-122.4058816 && lcn_ref -> 36 || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || cycleway -> lane || tiger:county -> San Francisco, CA\n221434102000004 && 37.7695218,-122.406127:37.7694529,-122.4062261 && lcn_ref -> 36 || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || cycleway -> lane || tiger:county -> San Francisco, CA\n-221434102000004 && 37.7694529,-122.4062261:37.7695218,-122.406127 && lcn_ref -> 36 || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || cycleway -> lane || tiger:county -> San Francisco, CA\n634433864000000 && 37.7693307,-122.4036894:37.7693499,-122.4036904 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634433864000000 && 37.7693499,-122.4036904:37.7693307,-122.4036894 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n26944068000000 && 37.768741,-122.406751:37.7682279,-122.4060483:37.7680837,-122.4058806:37.7679904,-122.4057868:37.767878,-122.4057009:37.7677646,-122.4056312:37.7676374,-122.4055668:37.7675547,-122.405536:37.7674709,-122.4055092:37.7673155,-122.4054677:37.7672605,-122.4054727 && last_edit_changeset -> 36885656 || bicycle -> no || layer -> 1 || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> andygol || tiger:cfcc -> A63 || last_edit_time -> 1454094211000 || last_edit_user_id -> 94578 || lanes -> 1 || bridge -> yes || highway -> motorway_link || last_edit_version -> 15\n564435383000000 && 37.7699822,-122.3995842:37.770056,-122.3994841 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672106000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n-564435383000000 && 37.770056,-122.3994841:37.7699822,-122.3995842 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672106000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n634412042000000 && 37.7694224,-122.4034195:37.7694707,-122.4034236:37.7694627,-122.4035721 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412042000000 && 37.7694627,-122.4035721:37.7694707,-122.4034236:37.7694224,-122.4034195 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n26943758000001 && 37.77007,-122.4069977:37.7701423,-122.4069081 && last_edit_changeset -> 49322246 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798668000 || last_edit_user_id -> 1330847 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || highway -> tertiary || last_edit_version -> 27\n-26943758000001 && 37.7701423,-122.4069081:37.77007,-122.4069977 && last_edit_changeset -> 49322246 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798668000 || last_edit_user_id -> 1330847 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || highway -> tertiary || last_edit_version -> 27\n26943758000002 && 37.7701423,-122.4069081:37.7706311,-122.4063017 && last_edit_changeset -> 49322246 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798668000 || last_edit_user_id -> 1330847 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || highway -> tertiary || last_edit_version -> 27\n-26943758000002 && 37.7706311,-122.4063017:37.7701423,-122.4069081 && last_edit_changeset -> 49322246 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798668000 || last_edit_user_id -> 1330847 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || highway -> tertiary || last_edit_version -> 27\n26943758000003 && 37.7706311,-122.4063017:37.7712243,-122.405549 && last_edit_changeset -> 49322246 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798668000 || last_edit_user_id -> 1330847 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || highway -> tertiary || last_edit_version -> 27\n-26943758000003 && 37.7712243,-122.405549:37.7706311,-122.4063017 && last_edit_changeset -> 49322246 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798668000 || last_edit_user_id -> 1330847 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || highway -> tertiary || last_edit_version -> 27\n26943758000004 && 37.7712243,-122.405549:37.7713038,-122.4054511 && last_edit_changeset -> 49322246 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798668000 || last_edit_user_id -> 1330847 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || highway -> tertiary || last_edit_version -> 27\n-26943758000004 && 37.7713038,-122.4054511:37.7712243,-122.405549 && last_edit_changeset -> 49322246 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> TheDutchMan13 || tiger:cfcc -> A41 || last_edit_time -> 1496798668000 || last_edit_user_id -> 1330847 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || highway -> tertiary || last_edit_version -> 27\n503918565000001 && 37.7698091,-122.4005449:37.7691861,-122.4013223 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n-503918565000001 && 37.7691861,-122.4013223:37.7698091,-122.4005449 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n503918565000002 && 37.7691861,-122.4013223:37.7688723,-122.4017139 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n-503918565000002 && 37.7688723,-122.4017139:37.7691861,-122.4013223 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n501802408000000 && 37.7704091,-122.40281:37.7706746,-122.4024774 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802408000000 && 37.7706746,-122.4024774:37.7704091,-122.40281 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n564435387000000 && 37.7681397,-122.4013813:37.7686401,-122.4014282 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672106000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435387000000 && 37.7686401,-122.4014282:37.7681397,-122.4013813 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672106000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n417392962000000 && 37.7695009,-122.3987942:37.7693828,-122.3986477 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || turn:lanes:forward -> left| || lanes:forward -> 2 || maxspeed -> 25 mph || lanes:backward -> 2 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || lanes -> 4 || name -> 7th Street || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n-417392962000000 && 37.7693828,-122.3986477:37.7695009,-122.3987942 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || turn:lanes:forward -> left| || lanes:forward -> 2 || maxspeed -> 25 mph || lanes:backward -> 2 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || lanes -> 4 || name -> 7th Street || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n504300645000000 && 37.7693995,-122.3999967:37.7697893,-122.3994823 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854222000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-504300645000000 && 37.7697893,-122.3994823:37.7693995,-122.3999967 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854222000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n564435403000000 && 37.7695444,-122.4057841:37.7694686,-122.4058566:37.7693069,-122.4057982 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435403000000 && 37.7693069,-122.4057982:37.7694686,-122.4058566:37.7695444,-122.4057841 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n503918562000000 && 37.7701304,-122.4004824:37.7700035,-122.4003313 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n-503918562000000 && 37.7700035,-122.4003313:37.7701304,-122.4004824 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n8919954000000 && 37.7673142,-122.403785:37.7677669,-122.4038266:37.7685958,-122.4038927 && lcn_ref -> 123 || last_edit_changeset -> 63557245 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || name -> Henry Adams Street || tiger:name_base -> Henry Adams || highway -> residential || last_edit_version -> 16 || cycleway -> shared_lane\n-8919954000000 && 37.7685958,-122.4038927:37.7677669,-122.4038266:37.7673142,-122.403785 && lcn_ref -> 123 || last_edit_changeset -> 63557245 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || name -> Henry Adams Street || tiger:name_base -> Henry Adams || highway -> residential || last_edit_version -> 16 || cycleway -> shared_lane\n634415572000000 && 37.7690936,-122.4031507:37.7690925,-122.4031681:37.7689918,-122.4031601:37.7689907,-122.4031798:37.7690411,-122.4032399 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634415572000000 && 37.7690411,-122.4032399:37.7689907,-122.4031798:37.7689918,-122.4031601:37.7690925,-122.4031681:37.7690936,-122.4031507 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n634412061000000 && 37.769003,-122.4038265:37.7690036,-122.4038144 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412061000000 && 37.7690036,-122.4038144:37.769003,-122.4038265 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n186398845000001 && 37.7672565,-122.4047579:37.7680309,-122.4048322:37.768536,-122.4048807 && last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Vermont Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 7 || tiger:county -> San Francisco, CA\n-186398845000001 && 37.768536,-122.4048807:37.7680309,-122.4048322:37.7672565,-122.4047579 && last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Vermont Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 7 || tiger:county -> San Francisco, CA\n186398845000002 && 37.768536,-122.4048807:37.7697218,-122.4049953 && last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Vermont Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 7 || tiger:county -> San Francisco, CA\n-186398845000002 && 37.7697218,-122.4049953:37.768536,-122.4048807 && last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493068000 || last_edit_user_id -> 8107451 || name -> Vermont Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 7 || tiger:county -> San Francisco, CA\n503918559000000 && 37.769298,-122.399884:37.7697017,-122.3993608 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854223000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n-503918559000000 && 37.7697017,-122.3993608:37.769298,-122.399884 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854223000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n394691481000000 && 37.7697226,-122.4081672:37.7693622,-122.4076107:37.7691693,-122.407329:37.768741,-122.406751 && last_edit_changeset -> 36885656 || bicycle -> no || layer -> 1 || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> andygol || tiger:cfcc -> A63 || last_edit_time -> 1454094208000 || last_edit_user_id -> 94578 || lanes -> 2 || bridge -> yes || highway -> motorway_link || last_edit_version -> 1\n501802414000001 && 37.7704995,-122.4022604:37.7706746,-122.4024774 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802414000001 && 37.7706746,-122.4024774:37.7704995,-122.4022604 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n501802414000002 && 37.7706746,-122.4024774:37.7707063,-122.4025165 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802414000002 && 37.7707063,-122.4025165:37.7706746,-122.4024774 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n135243873000000 && 37.7700015,-122.4020862:37.7701025,-122.4005184 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || highway -> footway || last_edit_version -> 2 || foot -> yes\n-135243873000000 && 37.7701025,-122.4005184:37.7700015,-122.4020862 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || highway -> footway || last_edit_version -> 2 || foot -> yes\n218146865000001 && 37.7697727,-122.403881:37.7697733,-122.4037992:37.769773,-122.4037545:37.7697762,-122.4036728 && sidewalk -> right || last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || name -> Division Street || highway -> tertiary || last_edit_version -> 8 || oneway -> yes\n218146865000002 && 37.7697762,-122.4036728:37.7697777,-122.4036517:37.769784,-122.4035594:37.769792,-122.4035068:37.7698079,-122.4034486:37.7698177,-122.403392 && sidewalk -> right || last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || name -> Division Street || highway -> tertiary || last_edit_version -> 8 || oneway -> yes\n504300638000000 && 37.7686369,-122.4005136:37.7687623,-122.4006667 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854221000 || last_edit_user_id -> 2219338 || highway -> service || last_edit_version -> 1\n-504300638000000 && 37.7687623,-122.4006667:37.7686369,-122.4005136 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854221000 || last_edit_user_id -> 2219338 || highway -> service || last_edit_version -> 1\n634412043000000 && 37.7694627,-122.4035721:37.7694144,-122.403568 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412043000000 && 37.7694144,-122.403568:37.7694627,-122.4035721 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n397250673000001 && 37.7693828,-122.3986477:37.7694084,-122.3985346:37.7694516,-122.3984773 && sidewalk -> right || last_edit_changeset -> 37144817 || bicycle -> yes || surface -> paved || oneway -> yes || turn:lanes -> through|through || last_edit_user_name -> Jothirnadh || last_edit_time -> 1455194554000 || last_edit_user_id -> 2015224 || lanes -> 2 || name -> Mission Bay Drive || highway -> tertiary || last_edit_version -> 1 || cycleway -> lane\n397250673000002 && 37.7694516,-122.3984773:37.7694762,-122.3984446 && sidewalk -> right || last_edit_changeset -> 37144817 || bicycle -> yes || surface -> paved || oneway -> yes || turn:lanes -> through|through || last_edit_user_name -> Jothirnadh || last_edit_time -> 1455194554000 || last_edit_user_id -> 2015224 || lanes -> 2 || name -> Mission Bay Drive || highway -> tertiary || last_edit_version -> 1 || cycleway -> lane\n397250673000003 && 37.7694762,-122.3984446:37.7695087,-122.3984035 && sidewalk -> right || last_edit_changeset -> 37144817 || bicycle -> yes || surface -> paved || oneway -> yes || turn:lanes -> through|through || last_edit_user_name -> Jothirnadh || last_edit_time -> 1455194554000 || last_edit_user_id -> 2015224 || lanes -> 2 || name -> Mission Bay Drive || highway -> tertiary || last_edit_version -> 1 || cycleway -> lane\n397250673000004 && 37.7695087,-122.3984035:37.7696802,-122.3982012:37.7697377,-122.3981097 && sidewalk -> right || last_edit_changeset -> 37144817 || bicycle -> yes || surface -> paved || oneway -> yes || turn:lanes -> through|through || last_edit_user_name -> Jothirnadh || last_edit_time -> 1455194554000 || last_edit_user_id -> 2015224 || lanes -> 2 || name -> Mission Bay Drive || highway -> tertiary || last_edit_version -> 1 || cycleway -> lane\n570786829000001 && 37.7699471,-122.4035915:37.7701231,-122.4038029 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n-570786829000001 && 37.7701231,-122.4038029:37.7699471,-122.4035915 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n570786829000002 && 37.7701231,-122.4038029:37.7702125,-122.4039102 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n-570786829000002 && 37.7702125,-122.4039102:37.7701231,-122.4038029 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n503918564000000 && 37.7689121,-122.4009596:37.7691861,-122.4013223 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n-503918564000000 && 37.7691861,-122.4013223:37.7689121,-122.4009596 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n119237698000000 && 37.7690681,-122.4092372:37.7689762,-122.4090901:37.7689309,-122.4088943:37.7688905,-122.4086922:37.7688529,-122.4084711:37.7688286,-122.4082914:37.7688074,-122.4080956:37.7687893,-122.407885:37.7687671,-122.4076718:37.7687342,-122.4074613:37.7686907,-122.4072534:37.7686346,-122.4070536:37.768572,-122.4068765:37.7685137,-122.4067344:37.7684512,-122.4065962:37.7683759,-122.4064447:37.7683314,-122.4063629:37.7682836,-122.4062811:37.7682285,-122.4061912:37.7681776,-122.4061148:37.7681119,-122.4060236:37.7680377,-122.405927:37.7679539,-122.4058318:37.7678585,-122.4057406:37.7677525,-122.4056602:37.7676306,-122.4055891:37.7675532,-122.4055529:37.7674652,-122.4055207:37.767321,-122.4054831:37.7672605,-122.4054727 && last_edit_changeset -> 50401651 || destination:ref -> US 101 South || bicycle -> no || destination -> San Jose || maxspeed -> 50 mph || tiger:name_base_1 -> Central || layer -> 1 || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> nammala || tiger:cfcc -> A25 || ref -> US 101 || last_edit_time -> 1500461345000 || last_edit_user_id -> 3479270 || NHS -> STRAHNET || lanes -> 2 || name -> Central Freeway || tiger:name_base -> United States Highway 101 || tiger:name_type_1 -> Fwy || bridge -> yes || highway -> motorway || last_edit_version -> 12\n224384015000001 && 37.7698379,-122.4030693:37.7698517,-122.4027816 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n-224384015000001 && 37.7698517,-122.4027816:37.7698379,-122.4030693 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n224384015000002 && 37.7698517,-122.4027816:37.7698641,-122.4025219 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n-224384015000002 && 37.7698641,-122.4025219:37.7698517,-122.4027816 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n224384015000003 && 37.7698641,-122.4025219:37.7698702,-122.4023944 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n-224384015000003 && 37.7698702,-122.4023944:37.7698641,-122.4025219 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n224384015000004 && 37.7698702,-122.4023944:37.7698849,-122.4021666 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n-224384015000004 && 37.7698849,-122.4021666:37.7698702,-122.4023944 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n224384015000005 && 37.7698849,-122.4021666:37.769885,-122.402073 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n-224384015000005 && 37.769885,-122.402073:37.7698849,-122.4021666 && sidewalk -> left || last_edit_changeset -> 57545421 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n132813867000000 && 37.7707059,-122.4063969:37.7706311,-122.4063017 && last_edit_user_name -> skela || last_edit_changeset -> 9521974 || surface -> paved || last_edit_time -> 1318253589000 || last_edit_user_id -> 93285 || service -> driveway || source -> survey || highway -> service || last_edit_version -> 1\n-132813867000000 && 37.7706311,-122.4063017:37.7707059,-122.4063969 && last_edit_user_name -> skela || last_edit_changeset -> 9521974 || surface -> paved || last_edit_time -> 1318253589000 || last_edit_user_id -> 93285 || service -> driveway || source -> survey || highway -> service || last_edit_version -> 1\n634412038000001 && 37.7702125,-122.4039102:37.7701309,-122.4040106 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-634412038000001 && 37.7701309,-122.4040106:37.7702125,-122.4039102 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n634412038000002 && 37.7701309,-122.4040106:37.7699296,-122.404258 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-634412038000002 && 37.7699296,-122.404258:37.7701309,-122.4040106 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n564435391000001 && 37.7690946,-122.4029961:37.7691009,-122.4028607 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 56836576 || access -> permissive || last_edit_time -> 1520040596000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> yes\n564435391000002 && 37.7691009,-122.4028607:37.7691415,-122.4021759 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 56836576 || access -> permissive || last_edit_time -> 1520040596000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> yes\n564435391000003 && 37.7691415,-122.4021759:37.7691482,-122.4020061 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 56836576 || access -> permissive || last_edit_time -> 1520040596000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> yes\n37177689000001 && 37.7709091,-122.3995214:37.7710444,-122.3993496 && sidewalk -> both || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493064000 || last_edit_user_id -> 8107451 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 27 || tiger:county -> San Francisco, CA\n-37177689000001 && 37.7710444,-122.3993496:37.7709091,-122.3995214 && sidewalk -> both || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493064000 || last_edit_user_id -> 8107451 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 27 || tiger:county -> San Francisco, CA\n37177689000002 && 37.7710444,-122.3993496:37.7710798,-122.3993047:37.7713747,-122.3989302:37.7715757,-122.3986749:37.771683,-122.3985411 && sidewalk -> both || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493064000 || last_edit_user_id -> 8107451 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 27 || tiger:county -> San Francisco, CA\n-37177689000002 && 37.771683,-122.3985411:37.7715757,-122.3986749:37.7713747,-122.3989302:37.7710798,-122.3993047:37.7710444,-122.3993496 && sidewalk -> both || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493064000 || last_edit_user_id -> 8107451 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 27 || tiger:county -> San Francisco, CA\n37177689000003 && 37.771683,-122.3985411:37.7717378,-122.3984691:37.7717956,-122.3983947 && sidewalk -> both || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493064000 || last_edit_user_id -> 8107451 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 27 || tiger:county -> San Francisco, CA\n-37177689000003 && 37.7717956,-122.3983947:37.7717378,-122.3984691:37.771683,-122.3985411 && sidewalk -> both || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493064000 || last_edit_user_id -> 8107451 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 27 || tiger:county -> San Francisco, CA\n37177689000004 && 37.7717956,-122.3983947:37.7722396,-122.3978318:37.772369,-122.3976675:37.7733782,-122.3964146:37.7739551,-122.3956834:37.7740429,-122.395587:37.7741197,-122.3954843:37.7746805,-122.394732:37.7748858,-122.3944713:37.7751905,-122.3940843:37.7754359,-122.3937727:37.77573,-122.3933992 && sidewalk -> both || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493064000 || last_edit_user_id -> 8107451 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 27 || tiger:county -> San Francisco, CA\n-37177689000004 && 37.77573,-122.3933992:37.7754359,-122.3937727:37.7751905,-122.3940843:37.7748858,-122.3944713:37.7746805,-122.394732:37.7741197,-122.3954843:37.7740429,-122.395587:37.7739551,-122.3956834:37.7733782,-122.3964146:37.772369,-122.3976675:37.7722396,-122.3978318:37.7717956,-122.3983947 && sidewalk -> both || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_changeset -> 62086023 || last_edit_time -> 1535493064000 || last_edit_user_id -> 8107451 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 27 || tiger:county -> San Francisco, CA\n634412054000000 && 37.7693586,-122.4038462:37.7693596,-122.4038258 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || incline -> up || highway -> steps || last_edit_version -> 1\n-634412054000000 && 37.7693596,-122.4038258:37.7693586,-122.4038462 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || incline -> up || highway -> steps || last_edit_version -> 1\n28518571000001 && 37.7718329,-122.4016818:37.7717342,-122.4015592 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493063000 || last_edit_user_id -> 8107451 || name -> 7th Street || tiger:name_base -> 7th || highway -> secondary || last_edit_version -> 11 || cycleway -> lane\n-28518571000001 && 37.7717342,-122.4015592:37.7718329,-122.4016818 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493063000 || last_edit_user_id -> 8107451 || name -> 7th Street || tiger:name_base -> 7th || highway -> secondary || last_edit_version -> 11 || cycleway -> lane\n28518571000002 && 37.7717342,-122.4015592:37.7711307,-122.4008049 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493063000 || last_edit_user_id -> 8107451 || name -> 7th Street || tiger:name_base -> 7th || highway -> secondary || last_edit_version -> 11 || cycleway -> lane\n-28518571000002 && 37.7711307,-122.4008049:37.7717342,-122.4015592 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493063000 || last_edit_user_id -> 8107451 || name -> 7th Street || tiger:name_base -> 7th || highway -> secondary || last_edit_version -> 11 || cycleway -> lane\n28518571000003 && 37.7711307,-122.4008049:37.7704904,-122.400019 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493063000 || last_edit_user_id -> 8107451 || name -> 7th Street || tiger:name_base -> 7th || highway -> secondary || last_edit_version -> 11 || cycleway -> lane\n-28518571000003 && 37.7704904,-122.400019:37.7711307,-122.4008049 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493063000 || last_edit_user_id -> 8107451 || name -> 7th Street || tiger:name_base -> 7th || highway -> secondary || last_edit_version -> 11 || cycleway -> lane\n417392966000000 && 37.7704904,-122.400019:37.7701236,-122.3995674 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || name -> 7th Street || highway -> secondary || last_edit_version -> 2 || cycleway -> lane\n-417392966000000 && 37.7701236,-122.3995674:37.7704904,-122.400019 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || name -> 7th Street || highway -> secondary || last_edit_version -> 2 || cycleway -> lane\n221434099000001 && 37.7697586,-122.4043663:37.7697728,-122.40414 && lcn_ref -> 36 || sidewalk -> right || last_edit_changeset -> 63557245 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n221434099000002 && 37.7697728,-122.40414:37.7697825,-122.4039838 && lcn_ref -> 36 || sidewalk -> right || last_edit_changeset -> 63557245 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || name -> Division Street || highway -> tertiary || last_edit_version -> 9\n503918558000001 && 37.7693329,-122.3999227:37.7687623,-122.4006667 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n-503918558000001 && 37.7687623,-122.4006667:37.7693329,-122.3999227 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n503918558000002 && 37.7687623,-122.4006667:37.768709,-122.4007362 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n-503918558000002 && 37.768709,-122.4007362:37.7687623,-122.4006667 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49908842 || last_edit_time -> 1498719603000 || last_edit_user_id -> 5659851 || highway -> service || last_edit_version -> 1\n417392953000000 && 37.7698128,-122.3991828:37.7696275,-122.3989519:37.7695009,-122.3987942 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || turn:lanes:forward -> left| || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || name -> 7th Street || turn:lanes:backward -> |merge_to_left || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n-417392953000000 && 37.7695009,-122.3987942:37.7696275,-122.3989519:37.7698128,-122.3991828 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || turn:lanes:forward -> left| || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || name -> 7th Street || turn:lanes:backward -> |merge_to_left || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n564435386000000 && 37.7698641,-122.4025219:37.7697927,-122.402515 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || service -> driveway || highway -> service || last_edit_version -> 2\n-564435386000000 && 37.7697927,-122.402515:37.7698641,-122.4025219 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || service -> driveway || highway -> service || last_edit_version -> 2\n564435407000001 && 37.7688065,-122.4064989:37.768832,-122.4061382 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435407000001 && 37.768832,-122.4061382:37.7688065,-122.4064989 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n564435407000002 && 37.768832,-122.4061382:37.7688898,-122.4061596 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435407000002 && 37.7688898,-122.4061596:37.768832,-122.4061382 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n501802409000001 && 37.770243,-122.4026003:37.770347,-122.4027316 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802409000001 && 37.770347,-122.4027316:37.770243,-122.4026003 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n501802409000002 && 37.770347,-122.4027316:37.7704091,-122.40281 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802409000002 && 37.7704091,-122.40281:37.770347,-122.4027316 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n501802409000003 && 37.7704091,-122.40281:37.7705038,-122.4029296 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802409000003 && 37.7705038,-122.4029296:37.7704091,-122.40281 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n221434104000001 && 37.7697727,-122.403881:37.7697828,-122.4038385:37.7698008,-122.4038003:37.7698257,-122.4037689:37.7698558,-122.403746:37.7698895,-122.4037332:37.7699246,-122.4037311:37.769959,-122.4037399:37.7699907,-122.4037591 && junction -> roundabout || sidewalk -> right || last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> tertiary || last_edit_version -> 8 || cycleway -> shared_lane\n221434104000002 && 37.7699907,-122.4037591:37.7700165,-122.4037857:37.7700367,-122.4038191:37.7700503,-122.4038576 && junction -> roundabout || sidewalk -> right || last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> tertiary || last_edit_version -> 8 || cycleway -> shared_lane\n221434104000003 && 37.7700503,-122.4038576:37.7700564,-122.4038982:37.7700557,-122.403919 && junction -> roundabout || sidewalk -> right || last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> tertiary || last_edit_version -> 8 || cycleway -> shared_lane\n221434104000004 && 37.7700557,-122.403919:37.7700551,-122.4039396:37.7700461,-122.4039803:37.7700298,-122.4040173:37.7700073,-122.4040485 && junction -> roundabout || sidewalk -> right || last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> tertiary || last_edit_version -> 8 || cycleway -> shared_lane\n221434104000005 && 37.7700073,-122.4040485:37.7699796,-122.4040723:37.7699483,-122.4040872:37.7699152,-122.4040926:37.7698819,-122.4040881:37.7698504,-122.404074:37.7698224,-122.404051:37.7697993,-122.4040204:37.7697825,-122.4039838 && junction -> roundabout || sidewalk -> right || last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> tertiary || last_edit_version -> 8 || cycleway -> shared_lane\n221434104000006 && 37.7697825,-122.4039838:37.769774,-122.4039506:37.7697706,-122.4039159:37.7697727,-122.403881 && junction -> roundabout || sidewalk -> right || last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> tertiary || last_edit_version -> 8 || cycleway -> shared_lane\n634415563000000 && 37.7690148,-122.4036033:37.769039,-122.4035775:37.7690412,-122.4035265:37.7690202,-122.4035015 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634415563000000 && 37.7690202,-122.4035015:37.7690412,-122.4035265:37.769039,-122.4035775:37.7690148,-122.4036033 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n564435402000000 && 37.7696728,-122.4057841:37.7695444,-122.4057841 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n-564435402000000 && 37.7695444,-122.4057841:37.7696728,-122.4057841 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n634433862000000 && 37.769458,-122.4032877:37.7694294,-122.4032851 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634433862000000 && 37.7694294,-122.4032851:37.769458,-122.4032877 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n513699413000001 && 37.7693828,-122.3986477:37.7692693,-122.3985025 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || lanes:forward -> 1 || maxspeed -> 25 mph || lanes:backward -> 3 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493073000 || last_edit_user_id -> 8107451 || lanes -> 4 || name -> 7th Street || turn:lanes:backward -> ||right || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n-513699413000001 && 37.7692693,-122.3985025:37.7693828,-122.3986477 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || lanes:forward -> 1 || maxspeed -> 25 mph || lanes:backward -> 3 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493073000 || last_edit_user_id -> 8107451 || lanes -> 4 || name -> 7th Street || turn:lanes:backward -> ||right || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n513699413000002 && 37.7692693,-122.3985025:37.7692219,-122.3984419:37.7690793,-122.3982601 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || lanes:forward -> 1 || maxspeed -> 25 mph || lanes:backward -> 3 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493073000 || last_edit_user_id -> 8107451 || lanes -> 4 || name -> 7th Street || turn:lanes:backward -> ||right || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n-513699413000002 && 37.7690793,-122.3982601:37.7692219,-122.3984419:37.7692693,-122.3985025 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || lanes:forward -> 1 || maxspeed -> 25 mph || lanes:backward -> 3 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493073000 || last_edit_user_id -> 8107451 || lanes -> 4 || name -> 7th Street || turn:lanes:backward -> ||right || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n133735125000001 && 37.7698177,-122.403392:37.7698283,-122.4034575:37.7698431,-122.4035145:37.7698646,-122.4035684:37.7698904,-122.4036185 && sidewalk -> right || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || oneway -> yes || tiger:county -> San Francisco, CA\n133735125000002 && 37.7698904,-122.4036185:37.7699206,-122.4036628:37.7699907,-122.4037591 && sidewalk -> right || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 8 || oneway -> yes || tiger:county -> San Francisco, CA\n503918563000000 && 37.7700035,-122.4003313:37.7702366,-122.4000335 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 4\n-503918563000000 && 37.7702366,-122.4000335:37.7700035,-122.4003313 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 4\n346766971000001 && 37.7699296,-122.404258:37.769884,-122.4042061 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n-346766971000001 && 37.769884,-122.4042061:37.7699296,-122.404258 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n346766971000002 && 37.769884,-122.4042061:37.7697783,-122.4041395:37.7697728,-122.40414 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n-346766971000002 && 37.7697728,-122.40414:37.7697783,-122.4041395:37.769884,-122.4042061 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n346766971000003 && 37.7697728,-122.40414:37.7696978,-122.4041318 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n-346766971000003 && 37.7696978,-122.4041318:37.7697728,-122.40414 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n8919269000001 && 37.7694529,-122.4062261:37.7695536,-122.4063509 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || name -> 9th Street || tiger:name_base -> 9th || tiger:name_type -> St || highway -> primary || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n-8919269000001 && 37.7695536,-122.4063509:37.7694529,-122.4062261 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || name -> 9th Street || tiger:name_base -> 9th || tiger:name_type -> St || highway -> primary || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n8919269000002 && 37.7695536,-122.4063509:37.7696603,-122.4064855:37.7697092,-122.4065564 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || name -> 9th Street || tiger:name_base -> 9th || tiger:name_type -> St || highway -> primary || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n-8919269000002 && 37.7697092,-122.4065564:37.7696603,-122.4064855:37.7695536,-122.4063509 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || name -> 9th Street || tiger:name_base -> 9th || tiger:name_type -> St || highway -> primary || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n8919269000003 && 37.7697092,-122.4065564:37.770003,-122.406914 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || name -> 9th Street || tiger:name_base -> 9th || tiger:name_type -> St || highway -> primary || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n-8919269000003 && 37.770003,-122.406914:37.7697092,-122.4065564 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || name -> 9th Street || tiger:name_base -> 9th || tiger:name_type -> St || highway -> primary || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n8919269000004 && 37.770003,-122.406914:37.77007,-122.4069977 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || name -> 9th Street || tiger:name_base -> 9th || tiger:name_type -> St || highway -> primary || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n-8919269000004 && 37.77007,-122.4069977:37.770003,-122.406914 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || name -> 9th Street || tiger:name_base -> 9th || tiger:name_type -> St || highway -> primary || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n634415571000000 && 37.7690424,-122.4031466:37.7690936,-122.4031507 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || incline -> up || highway -> steps || last_edit_version -> 1\n-634415571000000 && 37.7690936,-122.4031507:37.7690424,-122.4031466 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || incline -> up || highway -> steps || last_edit_version -> 1\n564435385000001 && 37.76977,-122.4028995:37.7697775,-122.4027728 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n-564435385000001 && 37.7697775,-122.4027728:37.76977,-122.4028995 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n564435385000002 && 37.7697775,-122.4027728:37.7697927,-122.402515 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n-564435385000002 && 37.7697927,-122.402515:37.7697775,-122.4027728 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n564435385000003 && 37.7697927,-122.402515:37.7697984,-122.4024073 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n-564435385000003 && 37.7697984,-122.4024073:37.7697927,-122.402515 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411165000 || last_edit_user_id -> 53073 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n501802410000000 && 37.7704995,-122.4022604:37.770243,-122.4026003 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802410000000 && 37.770243,-122.4026003:37.7704995,-122.4022604 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n634412060000000 && 37.7697239,-122.403851:37.7696444,-122.4038443:37.7696338,-122.4038644:37.7694085,-122.4038443:37.7694098,-122.4038217 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412060000000 && 37.7694098,-122.4038217:37.7694085,-122.4038443:37.7696338,-122.4038644:37.7696444,-122.4038443:37.7697239,-122.403851 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n8915288000000 && 37.7643851,-122.4048876:37.765253,-122.4049182:37.7671246,-122.4051449:37.7673112,-122.4051771:37.7674194,-122.4052053:37.7675233,-122.4052428:37.7676399,-122.4052978:37.7677703,-122.4053769:37.7678869,-122.4054735:37.7679844,-122.405566:37.7680671,-122.4056613:37.7681434,-122.4057605:37.7690324,-122.4069177 && last_edit_changeset -> 61782308 || bicycle -> no || maxspeed -> 50 mph || junction:ref -> 433C || tiger:name_base_1 -> 13th || layer -> 2 || oneway -> yes || tiger:county -> San Francisco, CA;San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A41;A25 || ref -> US 101 || last_edit_time -> 1534636295000 || last_edit_user_id -> 119881 || NHS -> STRAHNET || lanes -> 2 || name -> Central Freeway || tiger:name_base -> United States Highway 101 || tiger:name_type_1 -> St || bridge -> yes || highway -> motorway || last_edit_version -> 57\n110800913000001 && 37.769885,-122.402073:37.7691482,-122.4020061 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090890000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> De Haro Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 8 || oneway -> no || tiger:county -> San Francisco, CA\n-110800913000001 && 37.7691482,-122.4020061:37.769885,-122.402073 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090890000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> De Haro Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 8 || oneway -> no || tiger:county -> San Francisco, CA\n110800913000002 && 37.7691482,-122.4020061:37.7689632,-122.4019854 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090890000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> De Haro Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 8 || oneway -> no || tiger:county -> San Francisco, CA\n-110800913000002 && 37.7689632,-122.4019854:37.7691482,-122.4020061 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090890000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> De Haro Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 8 || oneway -> no || tiger:county -> San Francisco, CA\n110800913000003 && 37.7689632,-122.4019854:37.7687093,-122.4019618 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090890000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> De Haro Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 8 || oneway -> no || tiger:county -> San Francisco, CA\n-110800913000003 && 37.7687093,-122.4019618:37.7689632,-122.4019854 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090890000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> De Haro Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 8 || oneway -> no || tiger:county -> San Francisco, CA\n110800913000004 && 37.7687093,-122.4019618:37.7681083,-122.4019059:37.767431,-122.401843:37.7661396,-122.4017216 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090890000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> De Haro Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 8 || oneway -> no || tiger:county -> San Francisco, CA\n-110800913000004 && 37.7661396,-122.4017216:37.767431,-122.401843:37.7681083,-122.4019059:37.7687093,-122.4019618 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090890000 || last_edit_user_id -> 53073 || lanes -> 2 || name -> De Haro Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 8 || oneway -> no || tiger:county -> San Francisco, CA\n503918560000001 && 37.7697893,-122.3994823:37.7697017,-122.3993608 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854223000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n-503918560000001 && 37.7697017,-122.3993608:37.7697893,-122.3994823 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854223000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n503918560000002 && 37.7697017,-122.3993608:37.7696133,-122.3992382 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854223000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n-503918560000002 && 37.7696133,-122.3992382:37.7697017,-122.3993608 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854223000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n218146840000000 && 37.7698177,-122.403392:37.7698379,-122.4030693 && sidewalk -> both || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 4 || tiger:county -> San Francisco, CA\n-218146840000000 && 37.7698379,-122.4030693:37.7698177,-122.403392 && sidewalk -> both || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090888000 || last_edit_user_id -> 53073 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 4 || tiger:county -> San Francisco, CA\n570786825000001 && 37.769811,-122.4022176:37.7698849,-122.4021666 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || footway -> crossing || highway -> footway || last_edit_version -> 1 || crossing -> zebra\n-570786825000001 && 37.7698849,-122.4021666:37.769811,-122.4022176 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || footway -> crossing || highway -> footway || last_edit_version -> 1 || crossing -> zebra\n570786825000002 && 37.7698849,-122.4021666:37.7700015,-122.4020862 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || footway -> crossing || highway -> footway || last_edit_version -> 1 || crossing -> zebra\n-570786825000002 && 37.7700015,-122.4020862:37.7698849,-122.4021666 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || footway -> crossing || highway -> footway || last_edit_version -> 1 || crossing -> zebra\n179235236000000 && 37.7689731,-122.4086279:37.7689514,-122.4084532:37.7689243,-122.4082072:37.7689006,-122.4079186:37.7688837,-122.4076758:37.768872,-122.4074478:37.7688635,-122.4072185:37.7688593,-122.4068068:37.7688614,-122.4066888:37.7688667,-122.4065721:37.7688741,-122.4064675:37.768889,-122.4063548:37.768907,-122.4062502:37.7689314,-122.4061416:37.7689643,-122.406041:37.7690067,-122.4059431:37.7690522,-122.4058546:37.7691042,-122.4057728:37.7691636,-122.405699:37.7692303,-122.4056347:37.7693109,-122.405569:37.7693968,-122.4055126:37.7694752,-122.4054724:37.7695653,-122.4054416:37.7696671,-122.4054228:37.7697604,-122.4054121:37.7698389,-122.4054094:37.7699205,-122.4054134:37.7699989,-122.4054201:37.7700784,-122.4054308:37.7701548,-122.4054456:37.7702353,-122.4054684:37.7704113,-122.4055327:37.7705799,-122.4056079:37.7707537,-122.4056923:37.7708979,-122.4057688:37.7710039,-122.4058318:37.7710993,-122.4058962:37.7713916,-122.406081 && last_edit_changeset -> 61782308 || destination:ref -> I 80 East || bicycle -> no || tiger:reviewed -> no || destination -> Bay Bridge;Oakland || layer -> 3 || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A63 || last_edit_time -> 1534636296000 || last_edit_user_id -> 119881 || lanes -> 2 || bridge -> yes || highway -> motorway_link || last_edit_version -> 6\n516061750000001 && 37.7710243,-122.405102:37.7707359,-122.4047498 && lcn_ref -> 23 || last_edit_changeset -> 63557245 || bicycle -> designated || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || name -> 8th Street || tiger:name_base -> 8th || highway -> tertiary || last_edit_version -> 4\n-516061750000001 && 37.7707359,-122.4047498:37.7710243,-122.405102 && lcn_ref -> 23 || last_edit_changeset -> 63557245 || bicycle -> designated || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || name -> 8th Street || tiger:name_base -> 8th || highway -> tertiary || last_edit_version -> 4\n516061750000002 && 37.7707359,-122.4047498:37.7701309,-122.4040106 && lcn_ref -> 23 || last_edit_changeset -> 63557245 || bicycle -> designated || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || name -> 8th Street || tiger:name_base -> 8th || highway -> tertiary || last_edit_version -> 4\n-516061750000002 && 37.7701309,-122.4040106:37.7707359,-122.4047498 && lcn_ref -> 23 || last_edit_changeset -> 63557245 || bicycle -> designated || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || name -> 8th Street || tiger:name_base -> 8th || highway -> tertiary || last_edit_version -> 4\n516061750000003 && 37.7701309,-122.4040106:37.7700557,-122.403919 && lcn_ref -> 23 || last_edit_changeset -> 63557245 || bicycle -> designated || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || name -> 8th Street || tiger:name_base -> 8th || highway -> tertiary || last_edit_version -> 4\n-516061750000003 && 37.7700557,-122.403919:37.7701309,-122.4040106 && lcn_ref -> 23 || last_edit_changeset -> 63557245 || bicycle -> designated || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || name -> 8th Street || tiger:name_base -> 8th || highway -> tertiary || last_edit_version -> 4\n634412034000001 && 37.7697062,-122.4038918:37.7693988,-122.403862:37.7693579,-122.4038585 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000001 && 37.7693579,-122.4038585:37.7693988,-122.403862:37.7697062,-122.4038918 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n634412034000002 && 37.7693579,-122.4038585:37.769003,-122.4038265 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000002 && 37.769003,-122.4038265:37.7693579,-122.4038585 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n634412034000003 && 37.769003,-122.4038265:37.7686934,-122.4037986:37.7686952,-122.4037649 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000003 && 37.7686952,-122.4037649:37.7686934,-122.4037986:37.769003,-122.4038265 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n634412034000004 && 37.7686952,-122.4037649:37.7687092,-122.4035163 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000004 && 37.7687092,-122.4035163:37.7686952,-122.4037649 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n634412034000005 && 37.7687092,-122.4035163:37.7687338,-122.4030796:37.7690443,-122.4031076 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000005 && 37.7690443,-122.4031076:37.7687338,-122.4030796:37.7687092,-122.4035163 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n634412034000006 && 37.7690443,-122.4031076:37.7694122,-122.4031408 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000006 && 37.7694122,-122.4031408:37.7690443,-122.4031076 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n634412034000007 && 37.7694122,-122.4031408:37.7694368,-122.4031429:37.7697619,-122.4031722:37.7697339,-122.4036687 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000007 && 37.7697339,-122.4036687:37.7697619,-122.4031722:37.7694368,-122.4031429:37.7694122,-122.4031408 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n634412034000008 && 37.7697339,-122.4036687:37.7697239,-122.403851 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000008 && 37.7697239,-122.403851:37.7697339,-122.4036687 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n634412034000009 && 37.7697239,-122.403851:37.7697216,-122.4038932:37.7697062,-122.4038918 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n-634412034000009 && 37.7697062,-122.4038918:37.7697216,-122.4038932:37.7697239,-122.403851 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || footway -> sidewalk || highway -> footway || last_edit_version -> 3\n501802407000000 && 37.770347,-122.4027316:37.7702928,-122.4027479:37.7702716,-122.4027653:37.7702547,-122.4027928:37.770252,-122.4028263:37.7702562,-122.4028525:37.7702615,-122.4028974:37.77026,-122.4029249:37.7702472,-122.402951:37.7702282,-122.4029839:37.7702207,-122.403008:37.7702202,-122.4030315 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> path || last_edit_version -> 1\n-501802407000000 && 37.7702202,-122.4030315:37.7702207,-122.403008:37.7702282,-122.4029839:37.7702472,-122.402951:37.77026,-122.4029249:37.7702615,-122.4028974:37.7702562,-122.4028525:37.770252,-122.4028263:37.7702547,-122.4027928:37.7702716,-122.4027653:37.7702928,-122.4027479:37.770347,-122.4027316 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> path || last_edit_version -> 1\n617895234000000 && 37.7643851,-122.4048876:37.7647312,-122.4048389:37.765047,-122.4048392:37.7653661,-122.4048419:37.765681,-122.4048526:37.7658379,-122.4048593:37.7660722,-122.4048754:37.7671313,-122.4049679:37.7684257,-122.4050967 && note -> not officially part of the Interstate Highway System (west of unbuilt I-280) || destination:ref -> I 80 East || bicycle -> no || destination -> Bay Bridge;Oakland || maxspeed -> 50 mph || junction:ref -> 433B || tiger:name_type -> Fwy || layer -> 2 || last_edit_time -> 1534636295000 || last_edit_user_id -> 119881 || hgv -> designated || NHS -> STRAHNET || lanes -> 3 || highway -> motorway || last_edit_version -> 1 || last_edit_changeset -> 61782308 || source:hgv:national_network -> Title 23: Highways Part 658 http://ecfr.gpoaccess.gov/cgi/t/text/text-idx?c=ecfr&rgn=div5&view=text&node=23:1.0.1.7.33&idno=23 || hgv:national_network -> yes || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A15 || name -> James Lick Freeway || tiger:name_base -> James Lick || bridge -> yes\n634412063000001 && 37.7690044,-122.4037977:37.7690045,-122.4037953 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725654000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412063000001 && 37.7690045,-122.4037953:37.7690044,-122.4037977 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725654000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412063000002 && 37.7690045,-122.4037953:37.769006,-122.4037645 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725654000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412063000002 && 37.769006,-122.4037645:37.7690045,-122.4037953 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725654000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n31129765000000 && 37.7684257,-122.4050967:37.769595,-122.405204:37.7698081,-122.4052348:37.7700382,-122.4052817:37.7702584,-122.4053541:37.7704386,-122.4054279:37.7705987,-122.4055097:37.7707842,-122.4056224:37.7709255,-122.4057197:37.7711213,-122.4058718:37.7713916,-122.406081 && note -> not officially part of the Interstate Highway System (west of unbuilt I-280) || destination:ref -> I 80 East || bicycle -> no || destination -> Bay Bridge;Oakland || maxspeed -> 50 mph || junction:ref -> 433B || tiger:name_type -> Fwy || layer -> 3 || last_edit_time -> 1534636296000 || last_edit_user_id -> 119881 || hgv -> designated || NHS -> STRAHNET || lanes -> 3 || highway -> motorway || last_edit_version -> 27 || last_edit_changeset -> 61782308 || source:hgv:national_network -> Title 23: Highways Part 658 http://ecfr.gpoaccess.gov/cgi/t/text/text-idx?c=ecfr&rgn=div5&view=text&node=23:1.0.1.7.33&idno=23 || hgv:national_network -> yes || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A15 || name -> James Lick Freeway || tiger:name_base -> James Lick || bridge -> yes\n564435404000000 && 37.7695328,-122.4056825:37.7693408,-122.4056286 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435404000000 && 37.7693408,-122.4056286:37.7695328,-122.4056825 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n503918557000000 && 37.7692088,-122.3997849:37.7686369,-122.4005136 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854223000 || last_edit_user_id -> 2219338 || highway -> service || last_edit_version -> 2\n-503918557000000 && 37.7686369,-122.4005136:37.7692088,-122.3997849 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854223000 || last_edit_user_id -> 2219338 || highway -> service || last_edit_version -> 2\n27167768000000 && 37.7690324,-122.4069177:37.7692578,-122.4072485:37.7694743,-122.4075789:37.7696209,-122.407811:37.7697107,-122.4079587:37.7697799,-122.4080774:37.769842,-122.4082053:37.7698846,-122.4083501:37.769909,-122.4084933:37.7699138,-122.4086443:37.7698919,-122.4087984:37.7698627,-122.4089154:37.7698286,-122.4090048:37.7697823,-122.4091111:37.7697239,-122.4092051 && last_edit_changeset -> 61761408 || destination:ref -> US 101 North || bicycle -> no || destination -> Golden Gate Bridge || maxspeed -> 50 mph || tiger:name_base_1 -> 13th || layer -> 2 || oneway -> yes || tiger:county -> San Francisco, CA;San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A41;A25 || ref -> US 101 || last_edit_time -> 1534562833000 || last_edit_user_id -> 119881 || NHS -> STRAHNET || lanes -> 2 || name -> Central Freeway || tiger:name_base -> United States Highway 101 || tiger:name_type_1 -> St || bridge -> yes || highway -> motorway || last_edit_version -> 33\n501802416000000 && 37.7708579,-122.4027036:37.7709337,-122.4027983 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802416000000 && 37.7709337,-122.4027983:37.7708579,-122.4027036 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n570786826000000 && 37.7697775,-122.4027728:37.7698517,-122.4027816 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || service -> driveway || highway -> service || last_edit_version -> 1\n-570786826000000 && 37.7698517,-122.4027816:37.7697775,-122.4027728 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || service -> driveway || highway -> service || last_edit_version -> 1\n564435408000000 && 37.768832,-122.4061382:37.7686698,-122.4061053:37.7686586,-122.4063058 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435408000000 && 37.7686586,-122.4063058:37.7686698,-122.4061053:37.768832,-122.4061382 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n132813868000000 && 37.7715623,-122.4074212:37.771489,-122.4073866:37.7707059,-122.4063969 && last_edit_user_name -> skela || last_edit_changeset -> 9521974 || surface -> paved || last_edit_time -> 1318253589000 || last_edit_user_id -> 93285 || service -> driveway || source -> survey || highway -> service || last_edit_version -> 1 || tunnel -> yes\n-132813868000000 && 37.7707059,-122.4063969:37.771489,-122.4073866:37.7715623,-122.4074212 && last_edit_user_name -> skela || last_edit_changeset -> 9521974 || surface -> paved || last_edit_time -> 1318253589000 || last_edit_user_id -> 93285 || service -> driveway || source -> survey || highway -> service || last_edit_version -> 1 || tunnel -> yes\n634412037000000 && 37.7685958,-122.4038927:37.7696757,-122.404004:37.7697023,-122.4040067 && lcn_ref -> 123 || last_edit_changeset -> 63559070 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_time -> 1539650461000 || last_edit_user_id -> 2237750 || name -> Henry Adams Street || tiger:name_base -> Henry Adams || highway -> residential || last_edit_version -> 2 || cycleway -> shared_lane\n-634412037000000 && 37.7697023,-122.4040067:37.7696757,-122.404004:37.7685958,-122.4038927 && lcn_ref -> 123 || last_edit_changeset -> 63559070 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_time -> 1539650461000 || last_edit_user_id -> 2237750 || name -> Henry Adams Street || tiger:name_base -> Henry Adams || highway -> residential || last_edit_version -> 2 || cycleway -> shared_lane\n634415564000000 && 37.7690148,-122.4036033:37.7689972,-122.4035752:37.7689995,-122.4035224:37.7690202,-122.4035015 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634415564000000 && 37.7690202,-122.4035015:37.7689995,-122.4035224:37.7689972,-122.4035752:37.7690148,-122.4036033 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n417392970000001 && 37.7701236,-122.3995674:37.770056,-122.3994841 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || name -> 7th Street || turn:lanes:backward -> |merge_to_left || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n-417392970000001 && 37.770056,-122.3994841:37.7701236,-122.3995674 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || name -> 7th Street || turn:lanes:backward -> |merge_to_left || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n417392970000002 && 37.770056,-122.3994841:37.7699381,-122.399339:37.7698128,-122.3991828 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || name -> 7th Street || turn:lanes:backward -> |merge_to_left || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n-417392970000002 && 37.7698128,-122.3991828:37.7699381,-122.399339:37.770056,-122.3994841 && lcn_ref -> 23 || last_edit_changeset -> 62086023 || maxspeed -> 25 mph || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> njtbusfan || tiger:cfcc -> A41 || last_edit_time -> 1535493072000 || last_edit_user_id -> 8107451 || name -> 7th Street || turn:lanes:backward -> |merge_to_left || highway -> secondary || last_edit_version -> 3 || cycleway -> lane\n564435382000000 && 37.7696467,-122.4000006:37.7695592,-122.4000965 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672106000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435382000000 && 37.7695592,-122.4000965:37.7696467,-122.4000006 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672106000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n564435395000000 && 37.7686496,-122.4053349:37.7691755,-122.40541:37.7692147,-122.4052517 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435395000000 && 37.7692147,-122.4052517:37.7691755,-122.40541:37.7686496,-122.4053349 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n501802413000000 && 37.7703252,-122.4020359:37.7704995,-122.4022604 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> steps || last_edit_version -> 1\n-501802413000000 && 37.7704995,-122.4022604:37.7703252,-122.4020359 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> steps || last_edit_version -> 1\n620728093000000 && 37.7697586,-122.4043663:37.7697218,-122.4049953 && lcn_ref -> 36 || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 2 || cycleway -> lane || tiger:county -> San Francisco, CA\n-620728093000000 && 37.7697218,-122.4049953:37.7697586,-122.4043663 && lcn_ref -> 36 || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || name -> Division Street || tiger:name_type -> St || highway -> tertiary || last_edit_version -> 2 || cycleway -> lane || tiger:county -> San Francisco, CA\n8916338000001 && 37.7698379,-122.4030693:37.7690946,-122.4029961 && last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Rhode Island Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n-8916338000001 && 37.7690946,-122.4029961:37.7698379,-122.4030693 && last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Rhode Island Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n8916338000002 && 37.7690946,-122.4029961:37.7686504,-122.4029523 && last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Rhode Island Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n-8916338000002 && 37.7686504,-122.4029523:37.7690946,-122.4029961 && last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Rhode Island Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n8916338000003 && 37.7686504,-122.4029523:37.767373,-122.4028264 && last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Rhode Island Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n-8916338000003 && 37.767373,-122.4028264:37.7686504,-122.4029523 && last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || lanes -> 2 || name -> Rhode Island Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 20 || oneway -> no || tiger:county -> San Francisco, CA\n634412040000001 && 37.7694049,-122.4032794:37.7694295,-122.4032822:37.7694294,-122.4032851 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412040000001 && 37.7694294,-122.4032851:37.7694295,-122.4032822:37.7694049,-122.4032794 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412040000002 && 37.7694294,-122.4032851:37.7694224,-122.4034195 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412040000002 && 37.7694224,-122.4034195:37.7694294,-122.4032851 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412040000003 && 37.7694224,-122.4034195:37.7693645,-122.4034135 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412040000003 && 37.7693645,-122.4034135:37.7694224,-122.4034195 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634433866000000 && 37.7687364,-122.4035185:37.7687092,-122.4035163 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634433866000000 && 37.7687092,-122.4035163:37.7687364,-122.4035185 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n215524529000001 && 37.7697271,-122.3983363:37.7701979,-122.3989385:37.7703055,-122.3990512 && sidewalk -> right || last_edit_user_name -> StellanL || last_edit_changeset -> 25467657 || last_edit_time -> 1410819230000 || last_edit_user_id -> 28775 || name -> Berry Street || highway -> residential || last_edit_version -> 2\n-215524529000001 && 37.7703055,-122.3990512:37.7701979,-122.3989385:37.7697271,-122.3983363 && sidewalk -> right || last_edit_user_name -> StellanL || last_edit_changeset -> 25467657 || last_edit_time -> 1410819230000 || last_edit_user_id -> 28775 || name -> Berry Street || highway -> residential || last_edit_version -> 2\n215524529000002 && 37.7703055,-122.3990512:37.77066,-122.3994876:37.7707067,-122.3995342:37.770748,-122.399559:37.7707999,-122.3995643:37.7708519,-122.3995523:37.7709091,-122.3995214 && sidewalk -> right || last_edit_user_name -> StellanL || last_edit_changeset -> 25467657 || last_edit_time -> 1410819230000 || last_edit_user_id -> 28775 || name -> Berry Street || highway -> residential || last_edit_version -> 2\n-215524529000002 && 37.7709091,-122.3995214:37.7708519,-122.3995523:37.7707999,-122.3995643:37.770748,-122.399559:37.7707067,-122.3995342:37.77066,-122.3994876:37.7703055,-122.3990512 && sidewalk -> right || last_edit_user_name -> StellanL || last_edit_changeset -> 25467657 || last_edit_time -> 1410819230000 || last_edit_user_id -> 28775 || name -> Berry Street || highway -> residential || last_edit_version -> 2\n221434100000001 && 37.7700073,-122.4040485:37.769884,-122.4042061 && lcn_ref -> 36 || sidewalk -> right || last_edit_changeset -> 63557245 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || name -> Division Street || highway -> tertiary || last_edit_version -> 7 || cycleway -> lane\n221434100000002 && 37.769884,-122.4042061:37.7697586,-122.4043663 && lcn_ref -> 36 || sidewalk -> right || last_edit_changeset -> 63557245 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> chachafish || tiger:cfcc -> A41 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || name -> Division Street || highway -> tertiary || last_edit_version -> 7 || cycleway -> lane\n23883760000001 && 37.7704904,-122.400019:37.7701304,-122.4004824 && last_edit_user_name -> marthaleena || tiger:cfcc -> A41 || last_edit_changeset -> 49908842 || last_edit_time -> 1498719606000 || last_edit_user_id -> 5659851 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 10 || oneway -> no || tiger:county -> San Francisco, CA\n-23883760000001 && 37.7701304,-122.4004824:37.7704904,-122.400019 && last_edit_user_name -> marthaleena || tiger:cfcc -> A41 || last_edit_changeset -> 49908842 || last_edit_time -> 1498719606000 || last_edit_user_id -> 5659851 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 10 || oneway -> no || tiger:county -> San Francisco, CA\n23883760000002 && 37.7701304,-122.4004824:37.7701025,-122.4005184 && last_edit_user_name -> marthaleena || tiger:cfcc -> A41 || last_edit_changeset -> 49908842 || last_edit_time -> 1498719606000 || last_edit_user_id -> 5659851 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 10 || oneway -> no || tiger:county -> San Francisco, CA\n-23883760000002 && 37.7701025,-122.4005184:37.7701304,-122.4004824 && last_edit_user_name -> marthaleena || tiger:cfcc -> A41 || last_edit_changeset -> 49908842 || last_edit_time -> 1498719606000 || last_edit_user_id -> 5659851 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 10 || oneway -> no || tiger:county -> San Francisco, CA\n23883760000003 && 37.7701025,-122.4005184:37.7699518,-122.4007125 && last_edit_user_name -> marthaleena || tiger:cfcc -> A41 || last_edit_changeset -> 49908842 || last_edit_time -> 1498719606000 || last_edit_user_id -> 5659851 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 10 || oneway -> no || tiger:county -> San Francisco, CA\n-23883760000003 && 37.7699518,-122.4007125:37.7701025,-122.4005184 && last_edit_user_name -> marthaleena || tiger:cfcc -> A41 || last_edit_changeset -> 49908842 || last_edit_time -> 1498719606000 || last_edit_user_id -> 5659851 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 10 || oneway -> no || tiger:county -> San Francisco, CA\n23883760000004 && 37.7699518,-122.4007125:37.7689632,-122.4019854 && last_edit_user_name -> marthaleena || tiger:cfcc -> A41 || last_edit_changeset -> 49908842 || last_edit_time -> 1498719606000 || last_edit_user_id -> 5659851 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 10 || oneway -> no || tiger:county -> San Francisco, CA\n-23883760000004 && 37.7689632,-122.4019854:37.7699518,-122.4007125 && last_edit_user_name -> marthaleena || tiger:cfcc -> A41 || last_edit_changeset -> 49908842 || last_edit_time -> 1498719606000 || last_edit_user_id -> 5659851 || name -> Berry Street || tiger:name_base -> Berry || tiger:name_type -> St || highway -> residential || last_edit_version -> 10 || oneway -> no || tiger:county -> San Francisco, CA\n634415567000000 && 37.7690142,-122.4036131:37.7690148,-122.4036033 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634415567000000 && 37.7690148,-122.4036033:37.7690142,-122.4036131 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n394691476000000 && 37.7690324,-122.4069177:37.7701419,-122.4083742 && last_edit_changeset -> 61761408 || bicycle -> no || destination -> Civic Center || junction:ref -> 433C || layer -> 2 || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A63 || last_edit_time -> 1534562838000 || last_edit_user_id -> 119881 || lanes -> 1 || bridge -> yes || destination:street -> 9th Street || highway -> motorway_link || last_edit_version -> 9\n634412035000000 && 37.7697762,-122.4036728:37.7697339,-122.4036687 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412035000000 && 37.7697339,-122.4036687:37.7697762,-122.4036728 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n634412056000001 && 37.7694098,-122.4038217:37.7693601,-122.4038174 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412056000001 && 37.7693601,-122.4038174:37.7694098,-122.4038217 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n634412056000002 && 37.7693601,-122.4038174:37.7693157,-122.4038136:37.7693149,-122.4038257 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412056000002 && 37.7693149,-122.4038257:37.7693157,-122.4038136:37.7693601,-122.4038174 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n27167756000000 && 37.7700689,-122.4058533:37.7699915,-122.4058412:37.7699088,-122.4058358:37.7698251,-122.4058345:37.7697403,-122.4058399:37.7696268,-122.4058573:37.7695187,-122.4058948:37.7694159,-122.4059565:37.7693353,-122.4060223:37.76926,-122.4060947:37.7691858,-122.4061899:37.769119,-122.4062972:37.7690713,-122.4063964:37.7690321,-122.4065024:37.7690003,-122.4066164:37.7689812,-122.4067317:37.7689579,-122.4069825:37.76896,-122.4072145:37.7689685,-122.4074438:37.7689791,-122.4076691:37.768995,-122.4079092:37.7690194,-122.4081922:37.7690459,-122.4084336:37.7690689,-122.4086034 && last_edit_changeset -> 61782308 || destination:ref -> US 101 North || bicycle -> no || tiger:reviewed -> no || destination -> Golden Gate Bridge || junction:ref -> 1B || layer -> 3 || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A63 || maxspeed:advisory -> 40 mph || last_edit_time -> 1534636296000 || last_edit_user_id -> 119881 || lanes -> 2 || bridge -> yes || highway -> motorway_link || last_edit_version -> 21\n564435410000000 && 37.7684088,-122.4070237:37.7685929,-122.4070582 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n-564435410000000 && 37.7685929,-122.4070582:37.7684088,-122.4070237 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n154966358000000 && 37.7717012,-122.4064466:37.7715269,-122.4063594:37.7710977,-122.4061189:37.7709491,-122.4060418:37.7708008,-122.405969:37.7706789,-122.405914:37.7705199,-122.4058451:37.7703612,-122.405783:37.770194,-122.4057289:37.7699919,-122.4056709:37.7698111,-122.4056275:37.7696225,-122.4055869:37.7694571,-122.4055602:37.7692514,-122.4055331:37.7666598,-122.4052777:37.7660475,-122.4052442 && note -> not officially part of the Interstate Highway System (west of unbuilt I-280) || last_edit_changeset -> 50466458 || destination:ref -> US 101 South || bicycle -> no || source:hgv:national_network -> Title 23: Highways Part 658 http://ecfr.gpoaccess.gov/cgi/t/text/text-idx?c=ecfr&rgn=div5&view=text&node=23:1.0.1.7.33&idno=23 || hgv:national_network -> yes || destination -> San Jose || maxspeed -> 45 mph || junction:ref -> 1A || layer -> 1 || oneway -> yes || last_edit_user_name -> srividya_c || last_edit_time -> 1500663664000 || last_edit_user_id -> 2115749 || hgv -> designated || NHS -> STRAHNET || lanes -> 3 || name -> James Lick Freeway || bridge -> yes || highway -> motorway || last_edit_version -> 12\n503918577000000 && 37.7684132,-122.4042781:37.7683189,-122.4042124:37.7683159,-122.4040717 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n-503918577000000 && 37.7683159,-122.4040717:37.7683189,-122.4042124:37.7684132,-122.4042781 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 2\n564435405000001 && 37.7695444,-122.4057841:37.7695328,-122.4056825 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435405000001 && 37.7695328,-122.4056825:37.7695444,-122.4057841 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n564435405000002 && 37.7695328,-122.4056825:37.7695354,-122.405646 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435405000002 && 37.7695354,-122.405646:37.7695328,-122.4056825 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n634415570000001 && 37.7690443,-122.4031076:37.769043,-122.4031335 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634415570000001 && 37.769043,-122.4031335:37.7690443,-122.4031076 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n634415570000002 && 37.769043,-122.4031335:37.7690424,-122.4031466 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634415570000002 && 37.7690424,-122.4031466:37.769043,-122.4031335 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n8915263000000 && 37.7717012,-122.4064466:37.7706912,-122.4060236:37.7705746,-122.405982:37.7704071,-122.4059311:37.7703286,-122.4059096:37.7702396,-122.4058881:37.7701548,-122.4058694:37.7700689,-122.4058533 && last_edit_changeset -> 61782308 || destination:ref -> US 101 North || bicycle -> no || tiger:reviewed -> no || destination -> Golden Gate Bridge || junction:ref -> 1B || layer -> 1 || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A63 || maxspeed:advisory -> 40 mph || last_edit_time -> 1534636295000 || last_edit_user_id -> 119881 || lanes -> 2 || bridge -> yes || highway -> motorway_link || last_edit_version -> 30\n503918566000001 && 37.7695592,-122.4000965:37.7695094,-122.4001931 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 3\n-503918566000001 && 37.7695094,-122.4001931:37.7695592,-122.4000965 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 3\n503918566000002 && 37.7695094,-122.4001931:37.7689121,-122.4009596 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 3\n-503918566000002 && 37.7689121,-122.4009596:37.7695094,-122.4001931 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 3\n503918566000003 && 37.7689121,-122.4009596:37.7687811,-122.4011278:37.7687323,-122.401384:37.7687175,-122.4015838:37.7688723,-122.4017139 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 3\n-503918566000003 && 37.7688723,-122.4017139:37.7687175,-122.4015838:37.7687323,-122.401384:37.7687811,-122.4011278:37.7689121,-122.4009596 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672117000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 3\n564435384000000 && 37.7700035,-122.4003313:37.7699085,-122.4004492 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672106000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564435384000000 && 37.7699085,-122.4004492:37.7700035,-122.4003313 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672106000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n27167281000001 && 37.7713038,-122.4054511:37.7712328,-122.4053572 && lcn_ref -> 23 || last_edit_changeset -> 59617626 || bicycle -> designated || lanes:forward -> 1 || maxspeed -> 30 mph || lanes:backward -> 3 || tiger:name_type -> St || cycleway:left -> lane || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Asumu Takikawa || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1528332469000 || last_edit_user_id -> 1744893 || lanes -> 4 || name -> 8th Street || tiger:name_base -> 8th || placement:forward -> left_of:1 || turn:lanes:backward -> left|left|right || highway -> tertiary || last_edit_version -> 25\n-27167281000001 && 37.7712328,-122.4053572:37.7713038,-122.4054511 && lcn_ref -> 23 || last_edit_changeset -> 59617626 || bicycle -> designated || lanes:forward -> 1 || maxspeed -> 30 mph || lanes:backward -> 3 || tiger:name_type -> St || cycleway:left -> lane || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Asumu Takikawa || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1528332469000 || last_edit_user_id -> 1744893 || lanes -> 4 || name -> 8th Street || tiger:name_base -> 8th || placement:forward -> left_of:1 || turn:lanes:backward -> left|left|right || highway -> tertiary || last_edit_version -> 25\n27167281000002 && 37.7712328,-122.4053572:37.7710243,-122.405102 && lcn_ref -> 23 || last_edit_changeset -> 59617626 || bicycle -> designated || lanes:forward -> 1 || maxspeed -> 30 mph || lanes:backward -> 3 || tiger:name_type -> St || cycleway:left -> lane || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Asumu Takikawa || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1528332469000 || last_edit_user_id -> 1744893 || lanes -> 4 || name -> 8th Street || tiger:name_base -> 8th || placement:forward -> left_of:1 || turn:lanes:backward -> left|left|right || highway -> tertiary || last_edit_version -> 25\n-27167281000002 && 37.7710243,-122.405102:37.7712328,-122.4053572 && lcn_ref -> 23 || last_edit_changeset -> 59617626 || bicycle -> designated || lanes:forward -> 1 || maxspeed -> 30 mph || lanes:backward -> 3 || tiger:name_type -> St || cycleway:left -> lane || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Asumu Takikawa || tiger:cfcc -> A41 || cycleway:right -> track || last_edit_time -> 1528332469000 || last_edit_user_id -> 1744893 || lanes -> 4 || name -> 8th Street || tiger:name_base -> 8th || placement:forward -> left_of:1 || turn:lanes:backward -> left|left|right || highway -> tertiary || last_edit_version -> 25\n501802411000001 && 37.7701571,-122.4024582:37.770111,-122.4025138 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802411000001 && 37.770111,-122.4025138:37.7701571,-122.4024582 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n501802411000002 && 37.770111,-122.4025138:37.7701009,-122.402596 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802411000002 && 37.7701009,-122.402596:37.770111,-122.4025138 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n634415565000000 && 37.7690202,-122.4035015:37.7690212,-122.4034821 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634415565000000 && 37.7690212,-122.4034821:37.7690202,-122.4035015 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n634433860000000 && 37.7695056,-122.4036409:37.7694109,-122.4036338 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634433860000000 && 37.7694109,-122.4036338:37.7695056,-122.4036409 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n503918561000001 && 37.7699518,-122.4007125:37.7698908,-122.4006353 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n-503918561000001 && 37.7698908,-122.4006353:37.7699518,-122.4007125 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n503918561000002 && 37.7698908,-122.4006353:37.7698091,-122.4005449 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n-503918561000002 && 37.7698091,-122.4005449:37.7698908,-122.4006353 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n503918561000003 && 37.7698091,-122.4005449:37.7695094,-122.4001931 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n-503918561000003 && 37.7695094,-122.4001931:37.7698091,-122.4005449 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n503918561000004 && 37.7695094,-122.4001931:37.7694797,-122.4000858:37.7693995,-122.3999967 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n-503918561000004 && 37.7693995,-122.3999967:37.7694797,-122.4000858:37.7695094,-122.4001931 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n503918561000005 && 37.7693995,-122.3999967:37.7693329,-122.3999227 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n-503918561000005 && 37.7693329,-122.3999227:37.7693995,-122.3999967 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n503918561000006 && 37.7693329,-122.3999227:37.769298,-122.399884 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n-503918561000006 && 37.769298,-122.399884:37.7693329,-122.3999227 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n503918561000007 && 37.769298,-122.399884:37.7692088,-122.3997849 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n-503918561000007 && 37.7692088,-122.3997849:37.769298,-122.399884 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n503918561000008 && 37.7692088,-122.3997849:37.7691935,-122.3997679 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n-503918561000008 && 37.7691935,-122.3997679:37.7692088,-122.3997849 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || highway -> service || last_edit_version -> 4\n534621727000001 && 37.7703055,-122.3990512:37.7703635,-122.3989802 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || highway -> footway || last_edit_version -> 1\n-534621727000001 && 37.7703635,-122.3989802:37.7703055,-122.3990512 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || highway -> footway || last_edit_version -> 1\n534621727000002 && 37.7703635,-122.3989802:37.7703979,-122.3989356:37.770421,-122.3989117:37.7704476,-122.3989049:37.7704778,-122.3989259:37.7705127,-122.39897:37.7705499,-122.3990336:37.770631,-122.3991421:37.77067,-122.3991758:37.7707067,-122.3991967:37.7707486,-122.3992049:37.7707912,-122.3992004:37.7708397,-122.3991877:37.7708776,-122.399178:37.7709048,-122.3991817:37.7709184,-122.3991915:37.7709355,-122.3992102:37.770979,-122.3992503 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || highway -> footway || last_edit_version -> 1\n-534621727000002 && 37.770979,-122.3992503:37.7709355,-122.3992102:37.7709184,-122.3991915:37.7709048,-122.3991817:37.7708776,-122.399178:37.7708397,-122.3991877:37.7707912,-122.3992004:37.7707486,-122.3992049:37.7707067,-122.3991967:37.77067,-122.3991758:37.770631,-122.3991421:37.7705499,-122.3990336:37.7705127,-122.39897:37.7704778,-122.3989259:37.7704476,-122.3989049:37.770421,-122.3989117:37.7703979,-122.3989356:37.7703635,-122.3989802 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || highway -> footway || last_edit_version -> 1\n534621727000003 && 37.770979,-122.3992503:37.7710444,-122.3993496 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || highway -> footway || last_edit_version -> 1\n-534621727000003 && 37.7710444,-122.3993496:37.770979,-122.3992503 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || highway -> footway || last_edit_version -> 1\n501802412000000 && 37.770243,-122.4026003:37.7702091,-122.4025648:37.770111,-122.4025138 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802412000000 && 37.770111,-122.4025138:37.7702091,-122.4025648:37.770243,-122.4026003 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || access -> private || last_edit_time -> 1497903638000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n634412041000001 && 37.7693566,-122.4035639:37.7694144,-122.403568 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412041000001 && 37.7694144,-122.403568:37.7693566,-122.4035639 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412041000002 && 37.7694144,-122.403568:37.7694109,-122.4036338 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412041000002 && 37.7694109,-122.4036338:37.7694144,-122.403568 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412041000003 && 37.7694109,-122.4036338:37.7694096,-122.4036587:37.7693519,-122.403653 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n-634412041000003 && 37.7693519,-122.403653:37.7694096,-122.4036587:37.7694109,-122.4036338 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558513 || last_edit_time -> 1539646884000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 2\n634412062000000 && 37.7690036,-122.4038144:37.7690044,-122.4037977 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || incline -> up || highway -> steps || last_edit_version -> 1\n-634412062000000 && 37.7690044,-122.4037977:37.7690036,-122.4038144 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || incline -> up || highway -> steps || last_edit_version -> 1\n8917337000001 && 37.7692412,-122.4079015:37.7693864,-122.4077747:37.7694831,-122.4076852:37.7695306,-122.4076423:37.7699957,-122.4070864 && last_edit_changeset -> 57545421 || lanes:forward -> 2 || lanes:backward -> 2 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || turn:lanes:backward -> left;through;right|right || highway -> tertiary || last_edit_version -> 15\n-8917337000001 && 37.7699957,-122.4070864:37.7695306,-122.4076423:37.7694831,-122.4076852:37.7693864,-122.4077747:37.7692412,-122.4079015 && last_edit_changeset -> 57545421 || lanes:forward -> 2 || lanes:backward -> 2 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || turn:lanes:backward -> left;through;right|right || highway -> tertiary || last_edit_version -> 15\n8917337000002 && 37.7699957,-122.4070864:37.77007,-122.4069977 && last_edit_changeset -> 57545421 || lanes:forward -> 2 || lanes:backward -> 2 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || turn:lanes:backward -> left;through;right|right || highway -> tertiary || last_edit_version -> 15\n-8917337000002 && 37.77007,-122.4069977:37.7699957,-122.4070864 && last_edit_changeset -> 57545421 || lanes:forward -> 2 || lanes:backward -> 2 || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_time -> 1522090896000 || last_edit_user_id -> 53073 || lanes -> 4 || name -> Brannan Street || tiger:name_base -> Brannan || turn:lanes:backward -> left;through;right|right || highway -> tertiary || last_edit_version -> 15\n634411999000000 && 37.7693579,-122.4038585:37.7693586,-122.4038462 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634411999000000 && 37.7693586,-122.4038462:37.7693579,-122.4038585 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n215528940000001 && 37.7697271,-122.3983363:37.7695848,-122.3985037 && sidewalk -> none || turn:lanes -> left|right|right || last_edit_user_name -> ridixcr || last_edit_changeset -> 51015043 || last_edit_time -> 1502399149000 || last_edit_user_id -> 2508151 || lanes -> 3 || name -> Mission Bay Drive || highway -> tertiary || last_edit_version -> 3 || oneway -> yes\n215528940000002 && 37.7695848,-122.3985037:37.7695543,-122.3985416 && sidewalk -> none || turn:lanes -> left|right|right || last_edit_user_name -> ridixcr || last_edit_changeset -> 51015043 || last_edit_time -> 1502399149000 || last_edit_user_id -> 2508151 || lanes -> 3 || name -> Mission Bay Drive || highway -> tertiary || last_edit_version -> 3 || oneway -> yes\n215528940000003 && 37.7695543,-122.3985416:37.7695295,-122.3985735 && sidewalk -> none || turn:lanes -> left|right|right || last_edit_user_name -> ridixcr || last_edit_changeset -> 51015043 || last_edit_time -> 1502399149000 || last_edit_user_id -> 2508151 || lanes -> 3 || name -> Mission Bay Drive || highway -> tertiary || last_edit_version -> 3 || oneway -> yes\n215528940000004 && 37.7695295,-122.3985735:37.7694846,-122.398631:37.7693828,-122.3986477 && sidewalk -> none || turn:lanes -> left|right|right || last_edit_user_name -> ridixcr || last_edit_changeset -> 51015043 || last_edit_time -> 1502399149000 || last_edit_user_id -> 2508151 || lanes -> 3 || name -> Mission Bay Drive || highway -> tertiary || last_edit_version -> 3 || oneway -> yes\n23883761000001 && 37.7711307,-122.4008049:37.7702367,-122.4019313 && last_edit_changeset -> 49676208 || tiger:reviewed -> no || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> PlaneMad || tiger:cfcc -> A41 || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || name -> King Street || tiger:name_base -> King || highway -> residential || last_edit_version -> 10\n-23883761000001 && 37.7702367,-122.4019313:37.7711307,-122.4008049 && last_edit_changeset -> 49676208 || tiger:reviewed -> no || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> PlaneMad || tiger:cfcc -> A41 || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || name -> King Street || tiger:name_base -> King || highway -> residential || last_edit_version -> 10\n23883761000002 && 37.7702367,-122.4019313:37.7699961,-122.4022345:37.7698702,-122.4023944 && last_edit_changeset -> 49676208 || tiger:reviewed -> no || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> PlaneMad || tiger:cfcc -> A41 || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || name -> King Street || tiger:name_base -> King || highway -> residential || last_edit_version -> 10\n-23883761000002 && 37.7698702,-122.4023944:37.7699961,-122.4022345:37.7702367,-122.4019313 && last_edit_changeset -> 49676208 || tiger:reviewed -> no || tiger:name_type -> St || oneway -> no || tiger:county -> San Francisco, CA || last_edit_user_name -> PlaneMad || tiger:cfcc -> A41 || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || name -> King Street || tiger:name_base -> King || highway -> residential || last_edit_version -> 10\n634412036000001 && 37.7696978,-122.4041318:37.7697023,-122.4040067 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412036000001 && 37.7697023,-122.4040067:37.7696978,-122.4041318 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n634412036000002 && 37.7697023,-122.4040067:37.7697062,-122.4038918 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412036000002 && 37.7697062,-122.4038918:37.7697023,-122.4040067 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n634412057000001 && 37.7693149,-122.4038257:37.7690045,-122.4037953 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412057000001 && 37.7690045,-122.4037953:37.7693149,-122.4038257 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n634412057000002 && 37.7690045,-122.4037953:37.7686952,-122.4037649 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n-634412057000002 && 37.7686952,-122.4037649:37.7690045,-122.4037953 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 1\n346766970000001 && 37.7697762,-122.4036728:37.7698904,-122.4036185 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n-346766970000001 && 37.7698904,-122.4036185:37.7697762,-122.4036728 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n346766970000002 && 37.7698904,-122.4036185:37.7699471,-122.4035915 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n-346766970000002 && 37.7699471,-122.4035915:37.7698904,-122.4036185 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> footway || last_edit_version -> 4\n501802417000000 && 37.7702367,-122.4019313:37.7703252,-122.4020359 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n-501802417000000 && 37.7703252,-122.4020359:37.7702367,-122.4019313 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676208 || last_edit_time -> 1497903639000 || last_edit_user_id -> 1306 || highway -> footway || last_edit_version -> 1\n564435394000000 && 37.7685143,-122.4053147:37.7686496,-122.4053349 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n-564435394000000 && 37.7686496,-122.4053349:37.7685143,-122.4053147 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56699939 || last_edit_time -> 1519672107000 || last_edit_user_id -> 5288591 || highway -> service || last_edit_version -> 1\n483432855000000 && 37.7684205,-122.406827:37.7676115,-122.4067451 && sidewalk -> right || last_edit_user_name -> oba510 || tiger:cfcc -> A41 || last_edit_changeset -> 47257631 || last_edit_time -> 1490787818000 || last_edit_user_id -> 933797 || name -> Utah Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 1 || tiger:county -> San Francisco, CA\n-483432855000000 && 37.7676115,-122.4067451:37.7684205,-122.406827 && sidewalk -> right || last_edit_user_name -> oba510 || tiger:cfcc -> A41 || last_edit_changeset -> 47257631 || last_edit_time -> 1490787818000 || last_edit_user_id -> 933797 || name -> Utah Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 1 || tiger:county -> San Francisco, CA\n504300646000000 && 37.7691935,-122.3997679:37.7696133,-122.3992382 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854222000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-504300646000000 && 37.7696133,-122.3992382:37.7691935,-122.3997679 && last_edit_user_name -> RichRico || last_edit_changeset -> 49953223 || last_edit_time -> 1498854222000 || last_edit_user_id -> 2219338 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n355589869000000 && 37.7707359,-122.4047498:37.7714113,-122.4038867:37.771489,-122.4037874:37.7720411,-122.4030819:37.7723485,-122.4026891:37.772489,-122.4025095 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57301060 || last_edit_time -> 1521407168000 || last_edit_user_id -> 53073 || service -> alley || name -> Bluxome Alley || highway -> service || last_edit_version -> 3\n-355589869000000 && 37.772489,-122.4025095:37.7723485,-122.4026891:37.7720411,-122.4030819:37.771489,-122.4037874:37.7714113,-122.4038867:37.7707359,-122.4047498 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57301060 || last_edit_time -> 1521407168000 || last_edit_user_id -> 53073 || service -> alley || name -> Bluxome Alley || highway -> service || last_edit_version -> 3\n224373746000001 && 37.7692412,-122.4079015:37.769271,-122.407461:37.769299,-122.406882:37.769307,-122.406725:37.7693341,-122.4065441:37.769371,-122.406398 && lcn_ref -> 36 || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090887000 || last_edit_user_id -> 53073 || name -> Division Street || tiger:name_type -> St || highway -> secondary || last_edit_version -> 6 || cycleway -> track || tiger:county -> San Francisco, CA\n-224373746000001 && 37.769371,-122.406398:37.7693341,-122.4065441:37.769307,-122.406725:37.769299,-122.406882:37.769271,-122.407461:37.7692412,-122.4079015 && lcn_ref -> 36 || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090887000 || last_edit_user_id -> 53073 || name -> Division Street || tiger:name_type -> St || highway -> secondary || last_edit_version -> 6 || cycleway -> track || tiger:county -> San Francisco, CA\n224373746000002 && 37.769371,-122.406398:37.7694529,-122.4062261 && lcn_ref -> 36 || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090887000 || last_edit_user_id -> 53073 || name -> Division Street || tiger:name_type -> St || highway -> secondary || last_edit_version -> 6 || cycleway -> track || tiger:county -> San Francisco, CA\n-224373746000002 && 37.7694529,-122.4062261:37.769371,-122.406398 && lcn_ref -> 36 || last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 57545421 || last_edit_time -> 1522090887000 || last_edit_user_id -> 53073 || name -> Division Street || tiger:name_type -> St || highway -> secondary || last_edit_version -> 6 || cycleway -> track || tiger:county -> San Francisco, CA\n# Areas\n634359207000000 && 37.7695234,-122.4036052:37.7695363,-122.4033725:37.7694627,-122.4033659:37.769467,-122.4032885:37.769458,-122.4032877:37.7694648,-122.4031641:37.7695689,-122.4031733:37.7695685,-122.4031815:37.7696484,-122.4031886:37.7696488,-122.4031814:37.7697437,-122.4031898:37.7697433,-122.4031968:37.7697403,-122.4031965:37.7697352,-122.4032892:37.7697399,-122.4032896:37.7697372,-122.4033391:37.7697272,-122.4033382:37.7697091,-122.4033368:37.769708,-122.4033592:37.7696843,-122.4033573:37.7696828,-122.4033861:37.7696513,-122.4033835:37.7696556,-122.4033001:37.7695505,-122.4032915:37.7695454,-122.4033912:37.7695384,-122.4033906:37.7695274,-122.4036048:37.7695726,-122.4036085:37.7695685,-122.4036887:37.7696653,-122.4036967:37.7697135,-122.4037008:37.7697125,-122.4037183:37.7697064,-122.4037177:37.769701,-122.4038189:37.7697086,-122.4038196:37.7697082,-122.4038269:37.7696145,-122.4038188:37.7696149,-122.4038104:37.7695315,-122.4038032:37.769531,-122.4038126:37.7694271,-122.4038036:37.7694342,-122.4036716:37.7695037,-122.4036776:37.7695056,-122.4036409:37.7695077,-122.4036038 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567870 || min_height -> 18 || last_edit_time -> 1539678771000 || last_edit_user_id -> 2237750 || building:part -> yes || last_edit_version -> 2 || building:material -> glass || height -> 21\n634359291000000 && 37.7697033,-122.4033521:37.7697045,-122.4033272:37.7697079,-122.4033275:37.7697064,-122.4033578:37.7696836,-122.403356:37.7696821,-122.4033845:37.7696783,-122.4033842:37.76968,-122.4033503 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630504000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412010000000 && 37.7689119,-122.4038398:37.7689128,-122.4038255:37.7688879,-122.4038231:37.768887,-122.4038374 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634411997000000 && 37.7689692,-122.403654:37.7689705,-122.4036154:37.7689905,-122.4036165:37.7689902,-122.4036259:37.7689783,-122.4036253:37.7689773,-122.4036544 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412068000000 && 37.7687112,-122.4031534:37.7687095,-122.4031826:37.7687175,-122.4031833:37.7687192,-122.4031541 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433297000000 && 37.7688189,-122.4031604:37.7688212,-122.4031606:37.7688217,-122.4031516:37.7688194,-122.4031514 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433318000000 && 37.7688061,-122.4033039:37.7688061,-122.4033079:37.768797,-122.4033078:37.768797,-122.4033038 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433276000000 && 37.768848,-122.4036548:37.7688457,-122.4036548:37.7688457,-122.4036458:37.768848,-122.4036457 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359252000000 && 37.7695568,-122.4036676:37.7695633,-122.4036678:37.769563,-122.4036825:37.7695565,-122.4036822 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433444000000 && 37.769522,-122.4033071:37.7695296,-122.4033074:37.7695295,-122.4033111:37.7695219,-122.4033107 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646681000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359265000000 && 37.7697112,-122.4035457:37.7697111,-122.4035553:37.7697081,-122.4035552:37.7697082,-122.4035456 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634359278000000 && 37.7696803,-122.4033306:37.769688,-122.4033307:37.7696879,-122.4033345:37.7696803,-122.4033343 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634415569000000 && 37.7691004,-122.403177:37.769002,-122.4031679:37.7690038,-122.4031755:37.7690109,-122.4031881:37.7690195,-122.4031961:37.7690296,-122.4032005:37.7690403,-122.4032011:37.7690506,-122.4031977:37.7690609,-122.4031893:37.769075,-122.4031849:37.7690816,-122.4031841:37.7690937,-122.4031906:37.7690978,-122.4031972:37.7691,-122.403205:37.7691001,-122.4032133:37.769098,-122.4032213:37.7690941,-122.4032279:37.7690886,-122.4032326:37.7690823,-122.4032348:37.7690757,-122.4032344:37.7690666,-122.403233:37.7690602,-122.4032337:37.7690528,-122.4032377:37.7690973,-122.4032417:37.7691116,-122.4032428:37.7691102,-122.4032718:37.7691442,-122.4032753:37.7691484,-122.4032004:37.7691169,-122.4031975:37.7691183,-122.403167:37.7691268,-122.4031579:37.7691508,-122.4031592:37.7691523,-122.4031317:37.7691027,-122.4031277 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433181000000 && 37.7693105,-122.4035261:37.76931,-122.4035341:37.7691989,-122.4035247:37.7691993,-122.4035167 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646661000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433202000000 && 37.7693011,-122.4034835:37.7693008,-122.4034915:37.7692605,-122.403489:37.7692608,-122.4034809 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433223000000 && 37.769346,-122.4035769:37.7693458,-122.4035791:37.7692761,-122.4035733:37.7692763,-122.4035711 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646665000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433370000000 && 37.7690558,-122.4033046:37.7690535,-122.4033046:37.7690535,-122.4032955:37.7690558,-122.4032955 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433391000000 && 37.7688925,-122.4036077:37.7688924,-122.4036122:37.7688718,-122.4036116:37.7688719,-122.4036071 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433328000000 && 37.7687832,-122.4036378:37.7687809,-122.4036378:37.7687809,-122.4036288:37.7687832,-122.4036288 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412003000000 && 37.7694129,-122.4037764:37.7694181,-122.4036796:37.7693572,-122.4036744:37.7693536,-122.403741:37.7693979,-122.4037448:37.7693963,-122.403775 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359271000000 && 37.7697158,-122.4034717:37.7697157,-122.4034814:37.7697127,-122.4034813:37.7697129,-122.4034717 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n534677316000000 && 37.7693247,-122.4037935:37.7691204,-122.4037749:37.7691213,-122.4037594:37.7691264,-122.4036671:37.7691334,-122.4036677:37.76914,-122.4035471:37.7691535,-122.4035483:37.7691581,-122.4034657:37.7690891,-122.4034597:37.7690875,-122.4034879:37.7690212,-122.4034821:37.7689893,-122.4034793:37.7689909,-122.403449:37.7689197,-122.4034428:37.7689151,-122.4035283:37.7689283,-122.4035295:37.7689219,-122.4036472:37.7689285,-122.4036478:37.7689233,-122.4037429:37.7689225,-122.4037567:37.7687275,-122.4037392:37.7687336,-122.4036314:37.7687477,-122.4036327:37.7687523,-122.4035516:37.7687347,-122.40355:37.7687364,-122.4035185:37.7687384,-122.4034851:37.7687518,-122.4034864:37.7687636,-122.4032871:37.7687704,-122.4032877:37.768776,-122.4031937:37.7687585,-122.4031921:37.7687641,-122.4030983:37.768869,-122.4031082:37.7688696,-122.4030972:37.7689349,-122.4031034:37.7689345,-122.4031105:37.7689596,-122.4031129:37.7689536,-122.403216:37.7689387,-122.4032146:37.7689362,-122.4032572:37.768996,-122.4032629:37.7689976,-122.4032358:37.7690411,-122.4032399:37.7691097,-122.4032461:37.7691081,-122.4032748:37.7691673,-122.4032802:37.7691695,-122.4032417:37.7691489,-122.4032398:37.7691554,-122.4031265:37.7691829,-122.403129:37.7691832,-122.4031245:37.7692562,-122.4031311:37.7692558,-122.403137:37.7693627,-122.4031467:37.7693564,-122.4032567:37.7693366,-122.4032549:37.7693321,-122.4033352:37.7693394,-122.4033359:37.7693278,-122.403539:37.7693401,-122.4035401:37.7693383,-122.4035726:37.7693481,-122.4035735:37.7693459,-122.4036125:37.769316,-122.4036098:37.7693116,-122.4036877:37.7693307,-122.4036894 && last_edit_changeset -> 63567870 || addr:state -> CA || building:levels -> 6 || roof:colour -> #f5deb3 || addr:postcode -> 94103 || building -> apartments || addr:city -> San Francisco || last_edit_user_name -> chachafish || addr:housenumber -> 1 || last_edit_time -> 1539678770000 || last_edit_user_id -> 2237750 || addr:street -> Henry Adams Street || building:colour -> #CD853F || last_edit_version -> 7 || building:material -> glass || height -> 24\n634433275000000 && 37.7688544,-122.4036387:37.7688521,-122.4036387:37.7688521,-122.4036297:37.7688544,-122.4036297 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433254000000 && 37.76886,-122.4033103:37.7688493,-122.4033098:37.7688508,-122.4032518:37.7688539,-122.4032519:37.7688553,-122.4031979:37.7688857,-122.4031991:37.7688851,-122.4032219:37.7688791,-122.4032217:37.7688795,-122.4032073:37.7688628,-122.4032066 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567737 || min_height -> 21 || last_edit_time -> 1539678513000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> grey || building:colour -> grey || last_edit_version -> 2 || height -> 22\n634433212000000 && 37.7692458,-122.4031946:37.7692454,-122.4032026:37.7692052,-122.4032001:37.7692055,-122.403192 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433233000000 && 37.7692505,-122.4032926:37.7692428,-122.4032923:37.7692436,-122.4032594:37.7692306,-122.403259:37.7692307,-122.4032542:37.7692446,-122.4032547:37.7692448,-122.4032462:37.7692287,-122.4032457:37.7692284,-122.403256:37.7692222,-122.4032558:37.7692227,-122.4032356:37.7692517,-122.4032366 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567737 || min_height -> 21 || last_edit_time -> 1539678512000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> grey || building:colour -> grey || last_edit_version -> 2 || height -> 22\n634433380000000 && 37.7690659,-122.403404:37.7690636,-122.403404:37.7690636,-122.4033949:37.7690659,-122.4033949 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412048000000 && 37.7690159,-122.4038162:37.7690168,-122.4037997:37.7690379,-122.4038014:37.769037,-122.4038179 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433183000000 && 37.7692382,-122.4034482:37.7692318,-122.4034477:37.7692353,-122.4033786:37.7692417,-122.4033791 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646661000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359258000000 && 37.7696897,-122.4036422:37.7696896,-122.4036519:37.7696866,-122.4036518:37.7696867,-122.4036422 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634359227000000 && 37.7694788,-122.4037136:37.7694889,-122.4037144:37.7694887,-122.4037182:37.7694787,-122.4037173 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433264000000 && 37.7688932,-122.4034483:37.7688932,-122.4034523:37.7688841,-122.4034522:37.7688841,-122.4034482 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412017000000 && 37.76946,-122.4038911:37.7694608,-122.4038768:37.7694359,-122.4038744:37.7694351,-122.4038887 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433306000000 && 37.7687883,-122.403379:37.7687883,-122.403383:37.7687792,-122.4033829:37.7687792,-122.4033789 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433432000000 && 37.7695335,-122.4032429:37.7695334,-122.4032526:37.7695305,-122.4032525:37.7695306,-122.4032429 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433235000000 && 37.7691623,-122.4033759:37.7691622,-122.4033783:37.7689272,-122.4033579:37.7689274,-122.4033556 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646666000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433193000000 && 37.7692039,-122.4034821:37.7692036,-122.4034901:37.7691847,-122.4034893:37.7691849,-122.4034812 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359232000000 && 37.7695758,-122.4037294:37.7695751,-122.4037506:37.7695722,-122.4037504:37.7695729,-122.4037293 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433403000000 && 37.7689057,-122.403211:37.7689056,-122.403215:37.7688881,-122.4032144:37.7688882,-122.4032103 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646679000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359201000000 && 37.7694271,-122.4038036:37.769531,-122.4038126:37.7695315,-122.4038032:37.7696149,-122.4038104:37.7696145,-122.4038188:37.7697082,-122.4038269:37.7697086,-122.4038196:37.769701,-122.4038189:37.7697064,-122.4037177:37.7697125,-122.4037183:37.7697135,-122.4037008:37.7697146,-122.4036806:37.7697085,-122.4036801:37.7697272,-122.4033382:37.7697372,-122.4033391:37.7697399,-122.4032896:37.7697352,-122.4032892:37.7697403,-122.4031965:37.7697433,-122.4031968:37.7697437,-122.4031898:37.7696488,-122.4031814:37.7696484,-122.4031886:37.7695685,-122.4031815:37.7695689,-122.4031733:37.7694648,-122.4031641:37.769458,-122.4032877:37.769467,-122.4032885:37.7694627,-122.4033659:37.7695363,-122.4033725:37.7695234,-122.4036052:37.7695077,-122.4036038:37.7695056,-122.4036409:37.7695037,-122.4036776:37.7694342,-122.4036716 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || last_edit_time -> 1539630495000 || last_edit_user_id -> 2237750 || building:part -> yes || last_edit_version -> 1 || height -> 18\n634359285000000 && 37.7696593,-122.4033437:37.7696599,-122.4033255:37.7696629,-122.4033256:37.7696623,-122.4033438 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146855000000 && 37.7686344,-122.4017653:37.7686441,-122.4015893:37.7681527,-122.4015459:37.768143,-122.4017218 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768316000 || last_edit_user_id -> 3817650 || last_edit_version -> 3 || building -> yes || height -> 7\n634412028000000 && 37.7689025,-122.4030821:37.7689033,-122.4030677:37.7688784,-122.4030653:37.7688776,-122.4030796 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433190000000 && 37.7690482,-122.4033072:37.7690478,-122.4033157:37.7689341,-122.4033071:37.7689345,-122.4032986 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433316000000 && 37.7688323,-122.4033052:37.7688323,-122.4033092:37.7688232,-122.4033092:37.7688232,-122.4033051 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433358000000 && 37.7690856,-122.4034232:37.7690832,-122.4034232:37.7690832,-122.4034142:37.7690855,-122.4034142 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433245000000 && 37.7688732,-122.4035601:37.7688663,-122.4035594:37.7688706,-122.4034869:37.7688775,-122.4034875 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646667000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433287000000 && 37.7688821,-122.403297:37.7688798,-122.403297:37.7688798,-122.4032879:37.7688821,-122.4032879 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n25371830000000 && 37.7695724,-122.4073956:37.769423,-122.4071939:37.7694642,-122.4064892:37.7694877,-122.406455:37.7698979,-122.4069777 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55665623 || website -> http://fitnesssf.com/location/soma/ || last_edit_time -> 1516652473000 || last_edit_user_id -> 3817650 || name -> Fitness SF SOMA || last_edit_version -> 5 || brand -> Fitness SF || leisure -> fitness_centre || building -> yes || operator -> Fitness SF || height -> 8\n634412023000000 && 37.7693742,-122.4031303:37.7693751,-122.403116:37.7693502,-122.4031136:37.7693493,-122.4031279 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359238000000 && 37.7696391,-122.403735:37.7696579,-122.4037355:37.7696578,-122.4037393:37.769639,-122.4037387 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n256898639000000 && 37.770792,-122.4052779:37.7707593,-122.4053161:37.770885,-122.4054754:37.7710372,-122.4055808:37.7711019,-122.4054978:37.7708982,-122.4052435:37.7708335,-122.4053265 && last_edit_user_name -> Rub21 || last_edit_changeset -> 20040699 || last_edit_time -> 1389905071000 || last_edit_user_id -> 510836 || last_edit_version -> 1 || building -> yes\n634433200000000 && 37.7692262,-122.4033442:37.7692198,-122.403344:37.7692204,-122.4033078:37.7692269,-122.403308 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433221000000 && 37.7691795,-122.4036103:37.7691731,-122.4036099:37.769174,-122.403586:37.7691803,-122.4035863 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433368000000 && 37.769076,-122.4033069:37.7690737,-122.4033069:37.7690736,-122.4032979:37.769076,-122.4032979 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433389000000 && 37.7689732,-122.4033453:37.7689708,-122.4033453:37.7689708,-122.4033362:37.7689732,-122.4033362 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n565995476000000 && 37.768163,-122.4001799:37.7692802,-122.3987642:37.7694742,-122.3990103:37.7683592,-122.4004246 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 59447024 || last_edit_time -> 1527815892000 || last_edit_user_id -> 53073 || building:levels -> 4 || last_edit_version -> 3 || building -> yes\n634359212000000 && 37.7696005,-122.4036059:37.7696008,-122.4035997:37.7696632,-122.4036053:37.7696661,-122.4035537:37.7696346,-122.4035509:37.7696351,-122.4035424:37.7696666,-122.4035452:37.7696695,-122.4034929:37.7696377,-122.4034901:37.769638,-122.4034835:37.7696698,-122.4034863:37.7696727,-122.4034358:37.7696414,-122.4034331:37.7696419,-122.4034254:37.7696731,-122.4034282:37.7696753,-122.4033881:37.7696803,-122.4033886:37.7696679,-122.4036119 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 18 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #4c4c4c || building:colour -> #4c4c4c || last_edit_version -> 1 || height -> 18.5\n634359254000000 && 37.7696943,-122.4036593:37.7696942,-122.403669:37.7696913,-122.4036689:37.7696914,-122.4036593 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433226000000 && 37.769222,-122.4035667:37.7692203,-122.4035666:37.7692214,-122.4035313:37.7692231,-122.4035313 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646665000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433247000000 && 37.7688853,-122.4035893:37.7687707,-122.403579:37.7687713,-122.4035697:37.7688858,-122.4035799 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646667000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412021000000 && 37.7696287,-122.4031551:37.7696295,-122.4031408:37.7696046,-122.4031384:37.7696038,-122.4031527 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412008000000 && 37.7693352,-122.4033164:37.7693507,-122.4033176:37.7693492,-122.4033483:37.7693635,-122.4033494:37.7693674,-122.4032687:37.7693899,-122.4032704:37.7693937,-122.4031939:37.7693722,-122.4031922:37.7693735,-122.4031663:37.7693634,-122.4031655:37.7693588,-122.4032589:37.7693381,-122.4032573 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412050000000 && 37.7691502,-122.4038288:37.7691837,-122.4038315:37.7691841,-122.4038229:37.7691506,-122.4038202 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433252000000 && 37.7688646,-122.403561:37.7687359,-122.4035486:37.7687396,-122.4034867:37.7688574,-122.4034981:37.7688553,-122.4035329:37.7688662,-122.4035339 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646668000 || last_edit_user_id -> 2237750 || building:part -> yes || last_edit_version -> 1 || height -> 23\n634433273000000 && 37.7688863,-122.4034214:37.7688863,-122.4034255:37.7688771,-122.4034254:37.7688772,-122.4034214 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359205000000 && 37.7691204,-122.4037749:37.7690853,-122.4037717:37.7690862,-122.4037562:37.7691213,-122.4037594 && last_edit_user_name -> chachafish || last_edit_changeset -> 63568221 || last_edit_time -> 1539679425000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> dark_grey || building:colour -> #89a86f || last_edit_version -> 2 || building:material -> glass || height -> 5\n634359247000000 && 37.7695327,-122.4036778:37.7695411,-122.403678:37.7695411,-122.4036818:37.7695326,-122.4036816 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146846000000 && 37.7674797,-122.4027025:37.7685731,-122.4028024:37.7685999,-122.4023173:37.7684978,-122.4023083:37.76851,-122.4020868:37.7675184,-122.4020016 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 32105235 || last_edit_time -> 1434835033000 || last_edit_user_id -> 371121 || name -> Showplace East || last_edit_version -> 5 || building -> yes\n634359289000000 && 37.7696828,-122.4032628:37.7696826,-122.4032725:37.7696797,-122.4032724:37.7696798,-122.4032628 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433278000000 && 37.7688207,-122.4036221:37.7688184,-122.4036221:37.7688184,-122.4036131:37.7688207,-122.4036131 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433299000000 && 37.7688014,-122.4031608:37.7688037,-122.403161:37.7688042,-122.4031519:37.7688019,-122.4031517 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146859000000 && 37.7690702,-122.4021645:37.768798,-122.4021366:37.7687585,-122.4028227:37.7690291,-122.4028439 && last_edit_changeset -> 55702219 || addr:state -> CA || building:levels -> 3 || addr:postcode -> 94103 || building -> yes || addr:city -> San Francisco || last_edit_user_name -> bdon_import || addr:housenumber -> 99 || last_edit_time -> 1516768317000 || last_edit_user_id -> 3817650 || addr:street -> Rhode Island Street || last_edit_version -> 4 || height -> 14\n574610487000000 && 37.7700312,-122.4021091:37.7700875,-122.401941:37.7700779,-122.4019173:37.7700789,-122.4018802:37.7700961,-122.4017889:37.7700703,-122.4017802:37.7700875,-122.4016954:37.7701236,-122.4016345:37.7701631,-122.4015845:37.7702389,-122.401482:37.7701135,-122.4013329:37.7701777,-122.4012465:37.7701986,-122.4012224:37.7701017,-122.4011073:37.7701743,-122.4010096:37.7701617,-122.4007889:37.7701332,-122.400753:37.7700416,-122.4008569:37.7700331,-122.4010321:37.7700055,-122.4011863:37.7699625,-122.4019761:37.7699602,-122.402072 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356644000 || last_edit_user_id -> 53073 || last_edit_version -> 1 || leisure -> park\n634433446000000 && 37.7695069,-122.4033041:37.7695145,-122.4033044:37.7695144,-122.4033081:37.7695068,-122.4033078 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646681000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433304000000 && 37.7688069,-122.4034351:37.7688069,-122.4034391:37.7687978,-122.4034391:37.7687978,-122.4034351 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433325000000 && 37.7688457,-122.403315:37.7688396,-122.4033146:37.7688404,-122.4032967:37.7688314,-122.4032961:37.7688361,-122.4031852:37.7688427,-122.4031857:37.7688381,-122.4032912:37.7688467,-122.4032918 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359234000000 && 37.7696207,-122.4037474:37.7696203,-122.4037602:37.7696174,-122.4037601:37.7696177,-122.4037473 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359276000000 && 37.7697073,-122.4034278:37.7697072,-122.4034375:37.7697043,-122.4034374:37.7697044,-122.4034278 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n132813870000000 && 37.7706616,-122.4064522:37.7707059,-122.4063969:37.7707298,-122.4063675:37.7715623,-122.4074212:37.7715181,-122.4074615:37.7715504,-122.4074992:37.7712604,-122.4078614:37.7706171,-122.407037:37.7708708,-122.4067202 && parking -> multi-storey || last_edit_user_name -> AndrewSnow || last_edit_changeset -> 19084727 || access -> customers || last_edit_time -> 1385260237000 || last_edit_user_id -> 371121 || amenity -> parking || fee -> no || capacity:disabled -> yes || source -> survey || last_edit_version -> 2\n634433401000000 && 37.768885,-122.4032566:37.768885,-122.4032606:37.7688674,-122.4032603:37.7688674,-122.4032563 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646679000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359203000000 && 37.7691204,-122.4037749:37.7691213,-122.4037594:37.7690862,-122.4037562:37.769091,-122.4036722:37.7690824,-122.4036714:37.7690817,-122.4036826:37.7690682,-122.4036813:37.7690708,-122.4036371:37.7690568,-122.4036358:37.7690579,-122.4036171:37.7690142,-122.4036131:37.768995,-122.4036113:37.768994,-122.4036287:37.768981,-122.4036275:37.7689784,-122.4036723:37.7689595,-122.4036706:37.7689603,-122.4036579:37.7689535,-122.4036573:37.7689484,-122.4037452:37.7689233,-122.4037429:37.7689225,-122.4037567:37.7689476,-122.403759:37.7689741,-122.4037617:37.7689726,-122.40379:37.7690489,-122.4037965:37.7690504,-122.4037683:37.7690853,-122.4037717 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725653000 || last_edit_user_id -> 2237750 || building:levels -> 2 || last_edit_version -> 4 || building -> yes || building:material -> glass || height -> 7.5\n634359245000000 && 37.7695445,-122.4036783:37.769553,-122.4036785:37.769553,-122.4036822:37.7695445,-122.4036821 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433330000000 && 37.7687671,-122.4036368:37.7687648,-122.4036368:37.7687647,-122.4036278:37.7687671,-122.4036277 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359287000000 && 37.7696945,-122.4032638:37.7696944,-122.4032735:37.7696915,-122.4032734:37.7696916,-122.4032638 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433259000000 && 37.7688969,-122.4036705:37.7688968,-122.4036745:37.7688877,-122.4036745:37.7688877,-122.4036705 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646668000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433188000000 && 37.7692188,-122.4033583:37.7692185,-122.4033664:37.7691645,-122.4033627:37.7691649,-122.4033547 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n170183647000000 && 37.7707016,-122.4025173:37.7708529,-122.4027058:37.7706018,-122.4030284:37.7705861,-122.4030083:37.7705227,-122.4029287:37.7704505,-122.4028399 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55660512 || addr:housenumber -> 655 || last_edit_time -> 1516640369000 || last_edit_user_id -> 3817650 || name -> A || addr:street -> Townsend Street || last_edit_version -> 5 || building -> yes || height -> 15\n634433285000000 && 37.7689135,-122.4032981:37.7689112,-122.4032981:37.7689111,-122.403289:37.7689135,-122.403289 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433382000000 && 37.7690527,-122.403403:37.7690504,-122.403403:37.7690503,-122.4033939:37.7690527,-122.4033939 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433311000000 && 37.7688011,-122.403388:37.768801,-122.403392:37.7687919,-122.403392:37.7687919,-122.403388 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433240000000 && 37.7689661,-122.4032606:37.7689675,-122.4032607:37.7689664,-122.4032922:37.7689313,-122.4032899:37.7689313,-122.4032884:37.768965,-122.4032907 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646666000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433408000000 && 37.768835,-122.4035053:37.768834,-122.4035366:37.7688318,-122.4035365:37.7688327,-122.4035084:37.7688065,-122.4035071:37.7688066,-122.4035039 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646679000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359214000000 && 37.7694973,-122.4036799:37.7695002,-122.4036801:37.7694953,-122.4037944:37.7694924,-122.4037942 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359256000000 && 37.7697073,-122.4036429:37.7697072,-122.4036525:37.7697043,-122.4036525:37.7697044,-122.4036428 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433337000000 && 37.7688184,-122.4035453:37.7688172,-122.4035449:37.7688163,-122.403544:37.7688155,-122.4035428:37.7688152,-122.4035414:37.7688152,-122.4035399:37.7688155,-122.4035385:37.7688162,-122.4035372:37.7688172,-122.4035364:37.7688184,-122.403536:37.7688195,-122.403536:37.7688207,-122.4035365:37.7688216,-122.4035376:37.7688223,-122.403539:37.7688225,-122.4035406:37.7688223,-122.4035422:37.7688217,-122.4035436:37.7688207,-122.4035447:37.7688196,-122.4035452 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 22\n634359267000000 && 37.7697035,-122.4034873:37.7697034,-122.403497:37.7697004,-122.4034969:37.7697005,-122.4034873 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634359225000000 && 37.7694786,-122.403728:37.7694887,-122.4037289:37.7694885,-122.4037326:37.7694784,-122.4037317 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433266000000 && 37.7688928,-122.4034621:37.7688928,-122.4034661:37.7688836,-122.4034661:37.7688837,-122.403462 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433434000000 && 37.7695398,-122.4032438:37.7695397,-122.4032534:37.7695367,-122.4032533:37.7695368,-122.4032437 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433195000000 && 37.7691975,-122.4035855:37.7691971,-122.4035935:37.7691727,-122.4035917:37.7691731,-122.4035837 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412052000000 && 37.7692764,-122.4038398:37.7693414,-122.403845:37.7693424,-122.4038258:37.769322,-122.4038242:37.7693215,-122.403835:37.7692768,-122.4038314 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n356479005000000 && 37.7708277,-122.4020532:37.7705934,-122.402359:37.7703297,-122.4020296:37.7705661,-122.4017266 && parking -> multi-storey || last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 32220993 || access -> unknown || last_edit_time -> 1435308311000 || last_edit_user_id -> 33757 || amenity -> parking || last_edit_version -> 1\n634433334000000 && 37.7688191,-122.4035305:37.768818,-122.4035301:37.768817,-122.4035293:37.7688163,-122.4035281:37.7688159,-122.4035267:37.7688159,-122.4035252:37.7688163,-122.4035237:37.768817,-122.4035225:37.768818,-122.4035217:37.7688191,-122.4035212:37.7688203,-122.4035213:37.7688214,-122.4035218:37.7688224,-122.4035229:37.768823,-122.4035243:37.7688233,-122.4035259:37.7688231,-122.4035275:37.7688224,-122.4035289:37.7688214,-122.4035299:37.7688203,-122.4035305 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 22\n634433292000000 && 37.7688941,-122.4031679:37.7688917,-122.4031678:37.768892,-122.4031588:37.7688943,-122.4031588 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433418000000 && 37.7696389,-122.4032689:37.7696388,-122.4032785:37.7696358,-122.4032784:37.7696359,-122.4032688 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359262000000 && 37.76971,-122.4035629:37.7697099,-122.4035726:37.7697069,-122.4035725:37.769707,-122.4035629 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634412026000000 && 37.7690975,-122.4031028:37.7690984,-122.4030885:37.7690735,-122.4030861:37.7690726,-122.4031004 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433197000000 && 37.7691956,-122.4033645:37.7691892,-122.4033641:37.7691905,-122.4033332:37.7691969,-122.4033337 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433281000000 && 37.7688203,-122.4036386:37.7688179,-122.4036386:37.7688179,-122.4036295:37.7688202,-122.4036295 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n56645386000000 && 37.7711493,-122.398578:37.7709002,-122.398267:37.7704695,-122.3988459:37.7706915,-122.3991232:37.7707575,-122.3990386:37.7706686,-122.3989276:37.7708239,-122.3987286:37.7709292,-122.39886 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || last_edit_time -> 1523656390000 || last_edit_user_id -> 53073 || name -> Channel Westwater Pump Station || type -> pumping_station || last_edit_version -> 3 || building -> yes\n634433323000000 && 37.7688085,-122.4032539:37.7688085,-122.4032579:37.7687993,-122.4032579:37.7687994,-122.4032538 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433239000000 && 37.7689315,-122.4032891:37.7689329,-122.4032892:37.7689341,-122.4032573:37.7689326,-122.4032573 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646666000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634412000000000 && 37.7694857,-122.4036278:37.7694289,-122.4036225:37.7694309,-122.4035887:37.7694876,-122.4035939 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359236000000 && 37.7696589,-122.4037217:37.7696586,-122.4037392:37.7696557,-122.4037391:37.7696559,-122.4037217 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433365000000 && 37.7690765,-122.4032912:37.7690742,-122.4032912:37.7690742,-122.4032821:37.7690765,-122.4032821 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n256024188000000 && 37.771211,-122.4005675:37.771093,-122.400423:37.7710766,-122.4004441:37.7710383,-122.4004944:37.7711563,-122.400639 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 32509995 || alt_name -> Building H || ref -> H || last_edit_time -> 1436414591000 || last_edit_user_id -> 33757 || name -> Parsons || last_edit_version -> 3 || building -> yes\n218146858000000 && 37.7704488,-122.4027212:37.7706012,-122.402525:37.7704639,-122.4023544:37.7703115,-122.4025506 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768317000 || last_edit_user_id -> 3817650 || last_edit_version -> 3 || building -> yes || height -> 15\n634433449000000 && 37.7694931,-122.4032958:37.7695007,-122.4032962:37.7695006,-122.4033:37.769493,-122.4032996 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646681000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n170183648000000 && 37.77164,-122.4034654:37.7716519,-122.4034387:37.7716547,-122.4033865:37.7716324,-122.4033416:37.7715719,-122.4032626:37.7712725,-122.4028837:37.7711837,-122.4027778:37.7711435,-122.4027724:37.7710989,-122.4028019:37.7703039,-122.4038185:37.7702911,-122.4038721:37.7702996,-122.4039285:37.7707385,-122.404481:37.7707809,-122.4045132:37.7707973,-122.4045148:37.7708144,-122.4045092 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55660452 || addr:housenumber -> 650 || last_edit_time -> 1516640252000 || last_edit_user_id -> 3817650 || name -> Zynga || addr:street -> Townsend Street || last_edit_version -> 7 || building -> yes || height -> 20\n634412045000000 && 37.7688145,-122.4037972:37.7688505,-122.4038001:37.7688509,-122.4037914:37.7688149,-122.4037885 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359281000000 && 37.7696651,-122.4033393:37.7696727,-122.4033395:37.7696727,-122.4033432:37.769665,-122.403343 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n56656116000000 && 37.7719325,-122.3971938:37.7721721,-122.3968768:37.7724198,-122.3965706:37.7726284,-122.3963064:37.7729267,-122.3959286:37.7730018,-122.3958301:37.7734388,-122.3952753:37.7737947,-122.3957277:37.774141,-122.3952797:37.7737789,-122.394842:37.7742299,-122.3942703:37.7743195,-122.3941654:37.7747518,-122.3936209:37.7748349,-122.3935066:37.7748231,-122.3934881:37.7748856,-122.3934117:37.7750062,-122.3932679:37.7750629,-122.3931949:37.7752879,-122.3929125:37.7751587,-122.3927586:37.7751282,-122.3927816:37.7751019,-122.3928091:37.7750791,-122.3928519:37.7749936,-122.3930862:37.7749597,-122.3931457:37.7749071,-122.3932158:37.7746826,-122.3934308:37.7736818,-122.3947519:37.7735995,-122.3947949:37.7735452,-122.3948364:37.7735022,-122.3948793:37.7734518,-122.3949551:37.7734661,-122.3949736:37.7733952,-122.3950972:37.7732908,-122.3952282:37.7725192,-122.3962309:37.7718488,-122.3970866:37.7718078,-122.3970405:37.7713509,-122.3976384:37.7708851,-122.3982015:37.7708762,-122.3982123:37.7705013,-122.398671:37.770411,-122.3985591:37.7703183,-122.398518:37.770225,-122.3985212:37.7701394,-122.3985647:37.7700861,-122.3986374:37.7703517,-122.398967:37.7703635,-122.3989802:37.7707158,-122.3994192:37.7707667,-122.39944:37.770825,-122.399426:37.7708674,-122.3993944:37.770979,-122.3992503:37.7710076,-122.3992458:37.7716675,-122.3984132:37.7722832,-122.3976389 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || last_edit_time -> 1523656391000 || last_edit_user_id -> 53073 || name -> Mission Creek Park || last_edit_version -> 5 || leisure -> park\n579720319000000 && 37.7766871,-122.3903453:37.7765169,-122.3902035:37.7762619,-122.3901812:37.7759474,-122.3905758:37.7748697,-122.3919861:37.7749154,-122.3920548:37.7747568,-122.3922591:37.7746885,-122.3921924:37.7731188,-122.394124:37.7730306,-122.3943143:37.7729282,-122.3944211:37.772651,-122.3948105:37.7719846,-122.3957814:37.7712321,-122.3968349:37.7706081,-122.397668:37.7701568,-122.3981743:37.7701591,-122.3982084:37.7700241,-122.3983854:37.7700239,-122.3984197:37.7701394,-122.3985647:37.770225,-122.3985212:37.7703183,-122.398518:37.770411,-122.3985591:37.7705013,-122.398671:37.7708762,-122.3982123:37.7708851,-122.3982015:37.7713509,-122.3976384:37.7718078,-122.3970405:37.7718488,-122.3970866:37.7725192,-122.3962309:37.7732908,-122.3952282:37.7733952,-122.3950972:37.7734661,-122.3949736:37.7734518,-122.3949551:37.7735022,-122.3948793:37.7735452,-122.3948364:37.7735995,-122.3947949:37.7736818,-122.3947519:37.7746826,-122.3934308:37.7749071,-122.3932158:37.7749597,-122.3931457:37.7749936,-122.3930862:37.7750791,-122.3928519:37.7751019,-122.3928091:37.7751282,-122.3927816:37.7751587,-122.3927586:37.7751265,-122.3927194:37.7751333,-122.3927118:37.7750643,-122.392623:37.7752224,-122.392424:37.7752748,-122.3924899:37.775437,-122.3922851:37.7754445,-122.3922946:37.7754707,-122.3922728:37.7754981,-122.3922511:37.7755242,-122.3922253:37.775549,-122.3921963:37.7755699,-122.3921668:37.7755896,-122.3921339:37.7756081,-122.3920984:37.7756283,-122.3920625:37.7756208,-122.392053:37.7759816,-122.3915975:37.7759891,-122.391607:37.7760147,-122.3915844:37.7760427,-122.3915605:37.7760718,-122.391533:37.776096,-122.3915042:37.7761181,-122.3914729:37.7761357,-122.3914424:37.776156,-122.3914047:37.776174,-122.3913735:37.7761665,-122.391364:37.776485,-122.3909617:37.7764925,-122.3909712:37.7765177,-122.3909478:37.7765446,-122.3909263:37.7765723,-122.3909005:37.776598,-122.3908711:37.7766231,-122.3908371:37.7766436,-122.3908018:37.7766604,-122.3907692:37.7766787,-122.3907361:37.7766712,-122.3907266:37.7768563,-122.3904928:37.7768474,-122.3904858 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || natural -> water || last_edit_time -> 1523656386000 || last_edit_user_id -> 53073 || name -> Mission Creek || wikipedia -> en:Mission Creek || last_edit_version -> 1 || wikidata -> Q6878574 || name:zh -> 米慎溪\n634433186000000 && 37.7691977,-122.4037197:37.7691904,-122.4037191:37.7691954,-122.4036165:37.7692027,-122.4036171 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359210000000 && 37.7695481,-122.4033894:37.7695519,-122.4033149:37.7696371,-122.4033218:37.7696333,-122.4033962 && last_edit_user_name -> chachafish || last_edit_changeset -> 63568124 || min_height -> 18 || last_edit_time -> 1539679255000 || last_edit_user_id -> 2237750 || building:part -> yes || last_edit_version -> 3 || height -> 21\n27369705000000 && 37.7730306,-122.3943143:37.7731188,-122.394124:37.7746885,-122.3921924:37.7743723,-122.3918094:37.7728026,-122.3937652:37.7713126,-122.3956507:37.7711897,-122.395868:37.7708698,-122.3960942:37.7708223,-122.3961141:37.7706243,-122.396337:37.7705801,-122.3963991:37.7705377,-122.3965359:37.77049,-122.3967176:37.7704202,-122.3968605:37.7703145,-122.3970611:37.7702494,-122.3972252:37.7702069,-122.3974438:37.7702001,-122.3975632:37.7701879,-122.3977737:37.7701545,-122.3979065:37.7701073,-122.3980091:37.7699274,-122.3982574:37.7698614,-122.3983604:37.7700861,-122.3986374:37.7701394,-122.3985647:37.7700239,-122.3984197:37.7700241,-122.3983854:37.7701591,-122.3982084:37.7701568,-122.3981743:37.7706081,-122.397668:37.7712321,-122.3968349:37.7719846,-122.3957814:37.772651,-122.3948105:37.7729282,-122.3944211 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || last_edit_time -> 1523656389000 || last_edit_user_id -> 53073 || name -> Mission Creek Park || last_edit_version -> 4 || leisure -> park\n634433270000000 && 37.7688975,-122.4034077:37.7688975,-122.4034117:37.7688883,-122.4034117:37.7688884,-122.4034076 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433228000000 && 37.7692085,-122.4035994:37.7692068,-122.4035993:37.7692078,-122.4035639:37.7692096,-122.403564 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646665000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634359229000000 && 37.7695339,-122.4037241:37.769544,-122.403725:37.7695438,-122.4037287:37.7695337,-122.4037278 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433396000000 && 37.7688975,-122.4035256:37.7688975,-122.4035301:37.7688768,-122.4035294:37.7688769,-122.403525 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646679000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433312000000 && 37.7687878,-122.4033867:37.7687878,-122.4033907:37.7687787,-122.4033906:37.7687787,-122.4033866 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412019000000 && 37.7695705,-122.4039022:37.7695714,-122.4038878:37.7695465,-122.4038854:37.7695456,-122.4038997 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359255000000 && 37.7697061,-122.4036607:37.769706,-122.4036703:37.7697031,-122.4036702:37.7697032,-122.4036606 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n28642121000000 && 37.7658684,-122.4077252:37.7659197,-122.4066613:37.7683472,-122.4068956:37.7681026,-122.4106123:37.7672673,-122.4105185:37.767375,-122.4088771:37.7671969,-122.4088637:37.7669679,-122.4085767:37.7669957,-122.4078399 && last_edit_user_name -> StellanL || last_edit_changeset -> 24263865 || last_edit_time -> 1405899335000 || last_edit_user_id -> 28775 || landuse -> retail || last_edit_version -> 3\n56645380000000 && 37.771249,-122.3988693:37.7710222,-122.399154:37.770899,-122.3989969:37.7711257,-122.3987122 && last_edit_user_name -> Gregory Arenius || last_edit_changeset -> 4539232 || surface -> paved || last_edit_time -> 1272358652000 || last_edit_user_id -> 116029 || last_edit_version -> 1 || leisure -> pitch || sport -> tennis\n634412064000000 && 37.7686822,-122.4036296:37.7686805,-122.4036588:37.7686886,-122.4036595:37.7686903,-122.4036304 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359269000000 && 37.7697146,-122.4035012:37.7697145,-122.4035109:37.7697116,-122.4035108:37.7697117,-122.4035012 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634359248000000 && 37.7695329,-122.4036694:37.7695413,-122.4036695:37.7695413,-122.4036733:37.7695328,-122.4036731 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146849000000 && 37.7693946,-122.4009673:37.7696695,-122.4006152:37.7694567,-122.4003493:37.7691818,-122.4007015 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768316000 || last_edit_user_id -> 3817650 || last_edit_version -> 3 || building -> yes || height -> 8\n634359274000000 && 37.7697092,-122.4033933:37.7697091,-122.4034029:37.7697061,-122.4034029:37.7697062,-122.4033932 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433248000000 && 37.7688847,-122.4035889:37.7688778,-122.4035883:37.76888,-122.403552:37.7688868,-122.4035527 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646667000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412012000000 && 37.7690765,-122.4038552:37.7690773,-122.4038409:37.7690524,-122.4038385:37.7690516,-122.4038528 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433206000000 && 37.7693139,-122.4032975:37.7693136,-122.4033055:37.7692733,-122.403303:37.7692736,-122.4032949 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433332000000 && 37.7687734,-122.4036376:37.7687711,-122.4036376:37.7687711,-122.4036286:37.7687734,-122.4036285 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433290000000 && 37.7688761,-122.4031665:37.7688738,-122.4031664:37.768874,-122.4031573:37.7688763,-122.4031574 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433374000000 && 37.7691049,-122.4033983:37.7691026,-122.4033983:37.7691025,-122.4033892:37.7691049,-122.4033892 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433416000000 && 37.7696326,-122.403268:37.7696325,-122.4032777:37.7696296,-122.4032776:37.7696297,-122.403268 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646679000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359222000000 && 37.7694767,-122.4037652:37.7694868,-122.4037661:37.7694866,-122.4037698:37.7694766,-122.4037689 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359243000000 && 37.7695367,-122.4037311:37.7695364,-122.4037439:37.7695334,-122.4037438:37.7695338,-122.403731 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433261000000 && 37.7688866,-122.4036863:37.7688834,-122.4036863:37.7688836,-122.4036708:37.7688867,-122.4036708 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433219000000 && 37.7692492,-122.4037272:37.769249,-122.4037336:37.7691998,-122.4037307:37.7692,-122.4037243 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433303000000 && 37.7688201,-122.4034358:37.7688201,-122.4034398:37.768811,-122.4034398:37.768811,-122.4034357 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433429000000 && 37.7696403,-122.4032334:37.7696402,-122.403243:37.7696372,-122.4032429:37.7696373,-122.4032333 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433387000000 && 37.7689663,-122.4033952:37.768964,-122.4033953:37.7689639,-122.4033862:37.7689663,-122.4033862 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412007000000 && 37.7693375,-122.4034072:37.7693397,-122.4033669:37.7693611,-122.4033687:37.769359,-122.4034091 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359217000000 && 37.7696262,-122.4037034:37.7696292,-122.4037036:37.7696243,-122.403818:37.7696213,-122.4038178 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433263000000 && 37.7688824,-122.4034479:37.7688823,-122.403452:37.7688732,-122.4034519:37.7688732,-122.4034479 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359241000000 && 37.7695439,-122.4037315:37.7695435,-122.4037442:37.7695406,-122.4037441:37.7695409,-122.4037314 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359283000000 && 37.769654,-122.4033659:37.7696684,-122.403367:37.7696682,-122.4033707:37.7696539,-122.4033696 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433347000000 && 37.7690357,-122.4034199:37.7690334,-122.4034199:37.7690334,-122.4034108:37.7690357,-122.4034108 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412005000000 && 37.7694463,-122.4032735:37.7694494,-122.4031978:37.7694311,-122.4031967:37.7694281,-122.4032723 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412047000000 && 37.7689632,-122.4038109:37.7689641,-122.4037944:37.7689852,-122.4037962:37.7689844,-122.4038127 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433184000000 && 37.7692149,-122.4035182:37.7692083,-122.4035176:37.7692125,-122.4034384:37.7692191,-122.403439 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433268000000 && 37.7688763,-122.4034232:37.7688732,-122.4034231:37.7688733,-122.4034076:37.7688765,-122.4034077 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433294000000 && 37.7688445,-122.4031617:37.7688468,-122.4031619:37.7688473,-122.4031528:37.768845,-122.4031526 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n219783480000000 && 37.771207,-122.4068676:37.7713487,-122.4066911:37.7710588,-122.4063275:37.7709508,-122.4062743:37.7708766,-122.406242:37.7708239,-122.4062553:37.7707716,-122.4063203 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55660452 || last_edit_time -> 1516640255000 || last_edit_user_id -> 3817650 || source -> Bing || last_edit_version -> 4 || building -> yes || height -> 10\n634411995000000 && 37.7689264,-122.4035853:37.7689286,-122.4035337:37.7689452,-122.4035348:37.7689469,-122.4034951:37.7689185,-122.4034931:37.7689198,-122.4034625:37.768963,-122.4034654:37.7689623,-122.4034811:37.768973,-122.4034818:37.7689685,-122.4035882 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412024000000 && 37.7693239,-122.4031263:37.7693247,-122.403112:37.7692998,-122.4031096:37.769299,-122.4031239 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412066000000 && 37.7686984,-122.4033603:37.7686967,-122.4033894:37.7687048,-122.4033902:37.7687065,-122.403361 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359260000000 && 37.7696984,-122.4035799:37.7696983,-122.4035895:37.7696954,-122.4035894:37.7696955,-122.4035798 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433257000000 && 37.7689073,-122.4036847:37.7689072,-122.4036887:37.7688981,-122.4036886:37.7688981,-122.4036846 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646668000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433341000000 && 37.7688177,-122.403556:37.7688166,-122.4035556:37.7688156,-122.4035547:37.7688149,-122.4035536:37.7688145,-122.4035521:37.7688145,-122.4035506:37.7688148,-122.4035492:37.7688156,-122.403548:37.7688165,-122.4035471:37.7688177,-122.4035467:37.7688189,-122.4035468:37.76882,-122.4035473:37.768821,-122.4035483:37.7688216,-122.4035497:37.7688218,-122.4035513:37.7688216,-122.4035529:37.768821,-122.4035543:37.76882,-122.4035554:37.7688189,-122.4035559 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 22\n634359231000000 && 37.7695644,-122.4037463:37.7695745,-122.4037471:37.7695743,-122.4037509:37.7695642,-122.40375 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359208000000 && 37.7690504,-122.4037683:37.7690489,-122.4037965:37.7689726,-122.40379:37.7689741,-122.4037617:37.769006,-122.4037645 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || min_height -> 5.9 || last_edit_time -> 1539725654000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #4c4c4c || building:colour -> #4c4c4c || last_edit_version -> 3 || height -> 6\n211118823000000 && 37.7711412,-122.4023299:37.7716652,-122.4016414:37.7711333,-122.4009935:37.7706093,-122.4016819 && last_edit_changeset -> 56378669 || office -> company || addr:postcode -> 94103 || building -> yes || last_edit_user_name -> Edward || addr:housenumber -> 601 || old_name -> Baker & Hamilton || last_edit_time -> 1518687711000 || last_edit_user_id -> 364 || name -> Adobe Systems || addr:street -> Townsend Street || last_edit_version -> 8 || wikidata -> Q19865448 || height -> 17\n634359250000000 && 37.7695217,-122.4036691:37.7695302,-122.4036693:37.7695302,-122.403673:37.7695217,-122.4036729 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433199000000 && 37.7692269,-122.4033054:37.7692267,-122.4033138:37.7691804,-122.4033115:37.7691806,-122.4033031 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433283000000 && 37.7688955,-122.4032974:37.7688932,-122.4032974:37.7688932,-122.4032884:37.7688955,-122.4032883 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433367000000 && 37.7690829,-122.4032914:37.7690806,-122.4032915:37.7690806,-122.4032824:37.7690829,-122.4032824 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n288672372000000 && 37.772196,-122.3988728:37.7721839,-122.3988261:37.7721882,-122.3988207:37.7721527,-122.3987764:37.7721173,-122.3988217:37.772117,-122.3988243:37.7721014,-122.39882:37.7721187,-122.3987273:37.772068,-122.3987124:37.772069,-122.3987072:37.7720018,-122.3986879:37.7720008,-122.3986932:37.7719443,-122.3986765:37.7719456,-122.3986692:37.7718785,-122.3986499:37.7718784,-122.3986499:37.7718772,-122.3986567:37.771828,-122.3986421:37.7718092,-122.3987404:37.7717748,-122.398731:37.7717697,-122.3987374:37.7717134,-122.3986674:37.7716778,-122.3987127:37.7716734,-122.3987075:37.771647,-122.3987416:37.77165,-122.3987452:37.7716023,-122.3988058:37.7715993,-122.3988021:37.77156,-122.3988505:37.7715637,-122.3988553:37.7715636,-122.3988553:37.7715219,-122.3989084:37.7715185,-122.3989043:37.7714923,-122.398937:37.7715151,-122.3989644:37.7714627,-122.399031:37.7714433,-122.3990066:37.7714432,-122.3990066:37.7713941,-122.3990692:37.7713917,-122.3990662:37.7713523,-122.3991146:37.7713548,-122.3991177:37.7713144,-122.3991685:37.7713122,-122.3991658:37.7712857,-122.3991999:37.7713098,-122.3992291:37.771312,-122.3992263:37.7713474,-122.3992683:37.7713239,-122.399299:37.7712901,-122.3992572:37.7712913,-122.3992557:37.7712652,-122.3992243:37.7712387,-122.3992585:37.7712418,-122.3992623:37.7712027,-122.3993118:37.7711988,-122.399307:37.7711987,-122.399307:37.7711537,-122.3993649:37.7711566,-122.3993684:37.7711369,-122.3993934:37.7711337,-122.3993895:37.7711072,-122.3994237:37.7711101,-122.3994272:37.7710741,-122.3994727:37.7711354,-122.3995491:37.7711821,-122.39949:37.7711914,-122.3995016:37.7711744,-122.3995217:37.7711756,-122.3995233:37.7711639,-122.3995368:37.7711846,-122.399564:37.7711958,-122.3995511:37.7712154,-122.3995782:37.7712155,-122.3995782:37.7712348,-122.3995554:37.7712518,-122.3995765:37.7712519,-122.3995765:37.7712901,-122.3995299:37.7712838,-122.3995211:37.7712934,-122.3995098:37.771344,-122.3995783:37.7714058,-122.3995083:37.7713653,-122.3994533:37.7713762,-122.3994404:37.7713356,-122.399386:37.771325,-122.3993986:37.7713037,-122.3993697:37.7713767,-122.3992787:37.7713951,-122.3993082:37.7713832,-122.3993202:37.7714205,-122.399379:37.7714321,-122.3993674:37.7714537,-122.3994019:37.7714412,-122.3994139:37.7714773,-122.3994742:37.7714908,-122.3994613:37.771528,-122.3995208:37.7715281,-122.3995208:37.7715754,-122.3994731:37.7715918,-122.3994997:37.7715751,-122.3995148:37.7715889,-122.3995416:37.7716343,-122.3994976:37.771641,-122.3995083:37.7717057,-122.3994447:37.7716691,-122.3993862:37.7716803,-122.3993754:37.7716441,-122.3993153:37.7716319,-122.3993269:37.7716104,-122.3992926:37.7716222,-122.3992813:37.771586,-122.3992213:37.7715859,-122.3992213:37.7715733,-122.3992333:37.7715527,-122.3992004:37.7715619,-122.3991916:37.7715457,-122.3991617:37.7715456,-122.3991617:37.7715349,-122.399172:37.7715336,-122.3991698:37.7715335,-122.3991698:37.7715274,-122.3991758:37.7715007,-122.3991311:37.7715719,-122.3990447:37.7715981,-122.399071:37.7715982,-122.3990711:37.7716079,-122.399055:37.7717518,-122.3991916:37.7717565,-122.3991841:37.7717795,-122.3992064:37.7717796,-122.3992064:37.7717877,-122.3991934:37.7718164,-122.3992204:37.7718075,-122.3992359:37.7718567,-122.3992809:37.7718652,-122.3992662:37.7719106,-122.3993089:37.7719556,-122.399231:37.7719741,-122.3992472:37.7719742,-122.3992472:37.7720081,-122.3991924:37.7719899,-122.3991733:37.7720135,-122.3991337:37.771989,-122.3991104:37.7719992,-122.3990934:37.7719519,-122.3990486:37.7719518,-122.3990486:37.7719418,-122.3990655:37.7718923,-122.3990184:37.7719032,-122.3990001:37.771856,-122.3989553:37.7718559,-122.3989553:37.7718451,-122.3989735:37.7718211,-122.3989507:37.771832,-122.398931:37.7718074,-122.3989095:37.7718073,-122.3989095:37.7717972,-122.3989279:37.771728,-122.3988621:37.7717251,-122.3988586:37.7717144,-122.3988469:37.7717835,-122.3987591:37.7718431,-122.3987768:37.7718258,-122.398877:37.7718825,-122.3988928:37.771879,-122.3989101:37.7719362,-122.3989283:37.7719401,-122.398909:37.7719733,-122.3989183:37.7719702,-122.3989336:37.7720274,-122.3989517:37.7720309,-122.3989344:37.7720921,-122.3989515:37.772088,-122.3989719:37.7721451,-122.39899:37.7721452,-122.39899:37.7721497,-122.3989676:37.7722065,-122.3989836:37.7722066,-122.3989836:37.7722249,-122.3988809 && last_edit_changeset -> 55660512 || addr:state -> CA || building:levels -> 4 || addr:postcode -> 94158 || building -> apartments || addr:city -> San Francisco || last_edit_user_name -> bdon_import || addr:housenumber -> 420 || last_edit_time -> 1516640374000 || last_edit_user_id -> 3817650 || name -> Crescent Cove || addr:street -> Berry Street || last_edit_version -> 4 || height -> 13\n634433451000000 && 37.7695025,-122.4033432:37.7694911,-122.4033428:37.7694916,-122.4033226:37.769503,-122.4033231 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646681000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #4e5964 || building:colour -> #4e5964 || last_edit_version -> 1 || height -> 21.5\n256024190000000 && 37.7710766,-122.4004441:37.7709529,-122.4002957:37.7709085,-122.4003549:37.7710322,-122.4005033:37.7710383,-122.4004944 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 32509995 || ref -> G || last_edit_time -> 1436414591000 || last_edit_user_id -> 33757 || name -> Building G || last_edit_version -> 3 || building -> yes\n634412069000000 && 37.7687069,-122.4032226:37.7687039,-122.4032684:37.768712,-122.4032692:37.7687149,-122.4032234 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412014000000 && 37.7692413,-122.4038703:37.7692422,-122.403856:37.7692173,-122.4038536:37.7692164,-122.4038679 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433288000000 && 37.7688627,-122.4031655:37.7688604,-122.4031654:37.7688606,-122.4031563:37.768863,-122.4031564 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433204000000 && 37.7693012,-122.4033451:37.7693008,-122.4033531:37.7692606,-122.4033506:37.7692609,-122.4033426 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433372000000 && 37.7690574,-122.4032882:37.7690551,-122.4032882:37.7690551,-122.4032791:37.7690574,-122.4032791 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634411993000000 && 37.7690752,-122.4035985:37.7690796,-122.4035038:37.7690474,-122.4035013:37.7690467,-122.403518:37.7690688,-122.4035197:37.7690652,-122.4035978 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433217000000 && 37.7692196,-122.4037822:37.7692155,-122.4037818:37.7692244,-122.4036179:37.7692285,-122.4036182 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433385000000 && 37.7689697,-122.4033849:37.7689674,-122.4033849:37.7689674,-122.4033758:37.7689697,-122.4033758 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433301000000 && 37.7688069,-122.4034428:37.7688069,-122.4034469:37.7687978,-122.4034468:37.7687978,-122.4034428 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433230000000 && 37.769256,-122.4035873:37.7692548,-122.4035869:37.7692538,-122.4035861:37.7692531,-122.4035849:37.7692527,-122.4035834:37.7692527,-122.4035819:37.7692531,-122.4035805:37.7692538,-122.4035793:37.7692548,-122.4035784:37.7692559,-122.403578:37.7692571,-122.4035781:37.7692582,-122.4035786:37.7692592,-122.4035796:37.7692599,-122.403581:37.7692601,-122.4035826:37.7692599,-122.4035842:37.7692592,-122.4035856:37.7692583,-122.4035867:37.7692571,-122.4035872 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646665000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 22\n634433314000000 && 37.7688193,-122.4032975:37.7688193,-122.4033015:37.7688102,-122.4033014:37.7688102,-122.4032974 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n215531275000000 && 37.7690217,-122.3984241:37.7680298,-122.3996746:37.7678867,-122.3998354:37.768163,-122.4001799:37.7683592,-122.4004246:37.7694742,-122.3990103 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 59447024 || website -> http://www.100hooper.com/ || last_edit_time -> 1527815892000 || last_edit_user_id -> 53073 || landuse -> commercial || name -> 100 Hooper || last_edit_version -> 6\n634433398000000 && 37.7688938,-122.4033911:37.7688938,-122.4033951:37.7688847,-122.403395:37.7688847,-122.403391 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646679000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433243000000 && 37.7689088,-122.4033008:37.7689161,-122.4033013:37.7689148,-122.4033309:37.7688381,-122.4033254:37.7688385,-122.4033168:37.7689079,-122.4033218 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646667000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433327000000 && 37.7688514,-122.4033386:37.7688512,-122.4033423:37.7687622,-122.4033355:37.7687624,-122.4033318 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.2\n634359219000000 && 37.7696566,-122.4031837:37.7696595,-122.4031839:37.7696546,-122.4032982:37.7696516,-122.403298 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433411000000 && 37.7695876,-122.4033777:37.7695763,-122.4033771:37.7695781,-122.4033188:37.7695897,-122.4032999:37.7695905,-122.4032826:37.7696012,-122.4032834:37.7696005,-122.4032977:37.7695895,-122.4033194 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567672 || min_height -> 18 || last_edit_time -> 1539678375000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> grey || building:colour -> grey || last_edit_version -> 2 || height -> 21.5\n56645382000000 && 37.7710076,-122.3992458:37.770979,-122.3992503:37.7708674,-122.3993944:37.770825,-122.399426:37.7707667,-122.39944:37.7707158,-122.3994192:37.7703635,-122.3989802:37.7703517,-122.398967:37.7704477,-122.3988433:37.7707008,-122.3991586:37.7707839,-122.3990554:37.7708986,-122.3991099 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || barrier -> fence || last_edit_time -> 1523656390000 || last_edit_user_id -> 53073 || name -> Mission Bay Dog Park || opening_hours -> 07:00-22:00 || last_edit_version -> 7 || leisure -> dog_park\n634359293000000 && 37.7695275,-122.4032836:37.7695357,-122.4032838:37.7695748,-122.4032117:37.769567,-122.4032112 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630504000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359272000000 && 37.7697186,-122.4034117:37.7697185,-122.4034214:37.7697155,-122.4034213:37.7697156,-122.4034117 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n218146842000000 && 37.7696562,-122.4016051:37.7695563,-122.4014754:37.7693518,-122.4017275:37.769353,-122.4017291:37.7692599,-122.4018506:37.7692949,-122.4018927:37.7698929,-122.4019492:37.7699028,-122.4017811:37.7696862,-122.4017606:37.7699428,-122.4014587:37.7698608,-122.4013471 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || addr:housenumber -> 552 || last_edit_time -> 1433631820000 || last_edit_user_id -> 371121 || addr:state -> CA || name -> San Francisco Gravel Company || addr:street -> Berry Street || last_edit_version -> 3 || addr:postcode -> 94103 || building -> yes || addr:city -> San Francisco\n634433224000000 && 37.7693299,-122.4035403:37.7693298,-122.4035424:37.7692217,-122.4035334:37.7692219,-122.4035312 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646665000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433308000000 && 37.7688143,-122.403381:37.7688143,-122.403385:37.7688052,-122.4033849:37.7688052,-122.4033809 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433237000000 && 37.7689227,-122.4034424:37.7689213,-122.4034423:37.7689223,-122.4034176:37.7689574,-122.4034199:37.7689574,-122.4034214:37.7689237,-122.4034191 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646666000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433321000000 && 37.7688217,-122.4032469:37.7688217,-122.4032509:37.7688126,-122.4032508:37.7688126,-122.4032468 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433250000000 && 37.7688339,-122.403748:37.7688304,-122.4037477:37.7688392,-122.403584:37.7688426,-122.4035843 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646668000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359220000000 && 37.7695891,-122.4037028:37.76961,-122.4037042:37.7696094,-122.4037178:37.7696092,-122.4037213:37.7695883,-122.4037197:37.769589,-122.4037042 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #FFF8DC || building:colour -> #FFF8DC || last_edit_version -> 1 || height -> 22\n634433234000000 && 37.7691586,-122.4034649:37.7691567,-122.4034648:37.7691661,-122.4032817:37.7691679,-122.4032819 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646666000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433192000000 && 37.7692061,-122.4034422:37.7692059,-122.4034502:37.7691869,-122.4034494:37.7691871,-122.4034414 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433255000000 && 37.7688688,-122.4033861:37.7688574,-122.4033856:37.7688585,-122.403342:37.7688916,-122.4033433:37.7688915,-122.4033505:37.7689005,-122.4033509:37.7689007,-122.4033428:37.7689079,-122.4033431:37.7689076,-122.4033582:37.7688695,-122.4033567 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567737 || min_height -> 21 || last_edit_time -> 1539678513000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> grey || building:colour -> grey || last_edit_version -> 2 || height -> 22\n634359233000000 && 37.7696219,-122.4037318:37.7696215,-122.4037446:37.7696186,-122.4037445:37.7696189,-122.4037317 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433360000000 && 37.7690797,-122.4034052:37.7690774,-122.4034052:37.7690774,-122.4033961:37.7690797,-122.4033961 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146845000000 && 37.7700781,-122.4023524:37.7700725,-122.4023646:37.7700726,-122.4023699:37.770071,-122.4023765:37.7700631,-122.4023821:37.7700143,-122.4024258:37.7699899,-122.4024719:37.7699827,-122.4025092:37.7699821,-122.4025332:37.7699849,-122.4025548:37.7699893,-122.4025748:37.769988,-122.402584:37.7699776,-122.4025842:37.7699493,-122.4031009:37.7701727,-122.4031227:37.7702133,-122.402607:37.7701009,-122.402596:37.7700753,-122.4025935:37.7700777,-122.402548:37.7700884,-122.4025276:37.770109,-122.4024863:37.7701479,-122.4024373 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768316000 || last_edit_user_id -> 3817650 || name -> D || last_edit_version -> 5 || building -> yes || height -> 13\n634412029000000 && 37.7688479,-122.4030787:37.7688487,-122.4030644:37.7688238,-122.403062:37.768823,-122.4030763 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359226000000 && 37.769479,-122.4037211:37.7694891,-122.403722:37.7694889,-122.4037257:37.7694788,-122.4037248 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433286000000 && 37.7688891,-122.4032971:37.7688868,-122.4032971:37.7688868,-122.4032881:37.7688891,-122.4032881 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433307000000 && 37.7688011,-122.4033803:37.768801,-122.4033843:37.7687919,-122.4033843:37.7687919,-122.4033802 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433244000000 && 37.7688477,-122.4034881:37.7688408,-122.4034875:37.7688502,-122.4033239:37.7688571,-122.4033245 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646667000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433265000000 && 37.7688968,-122.4034643:37.7688937,-122.4034643:37.7688937,-122.4034528:37.7688969,-122.4034528 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412016000000 && 37.7694064,-122.4038874:37.7694073,-122.4038731:37.7693824,-122.4038707:37.7693815,-122.403885 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359239000000 && 37.7696375,-122.4037209:37.7696372,-122.4037384:37.7696343,-122.4037383:37.7696345,-122.4037208 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n351964814000000 && 37.7697082,-122.4038269:37.7696145,-122.4038188:37.7696149,-122.4038104:37.7695315,-122.4038032:37.769531,-122.4038126:37.7694271,-122.4038036:37.7694342,-122.4036716:37.7695037,-122.4036776:37.7695056,-122.4036409:37.7695077,-122.4036038:37.7695234,-122.4036052:37.7695363,-122.4033725:37.7694627,-122.4033659:37.769467,-122.4032885:37.769458,-122.4032877:37.7694648,-122.4031641:37.7695689,-122.4031733:37.7695685,-122.4031815:37.7696484,-122.4031886:37.7696488,-122.4031814:37.7697437,-122.4031898:37.7697433,-122.4031968:37.7697403,-122.4031965:37.7697352,-122.4032892:37.7697399,-122.4032896:37.7697372,-122.4033391:37.7697272,-122.4033382:37.7697085,-122.4036801:37.7697146,-122.4036806:37.7697135,-122.4037008:37.7697125,-122.4037183:37.7697064,-122.4037177:37.769701,-122.4038189:37.7697086,-122.4038196 && last_edit_changeset -> 63567870 || addr:state -> CA || building:levels -> 6 || roof:colour -> #f5deb3 || addr:postcode -> 94103 || building -> apartments || addr:city -> San Francisco || last_edit_user_name -> chachafish || addr:housenumber -> 1 || last_edit_time -> 1539678771000 || last_edit_user_id -> 2237750 || addr:street -> Henry Adams Street || building:colour -> #676d5f || last_edit_version -> 9 || building:material -> glass || height -> 24.5\n634359284000000 && 37.7696525,-122.4033824:37.7696535,-122.4033642:37.7696565,-122.4033645:37.7696554,-122.4033826 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433191000000 && 37.7692382,-122.4034399:37.7692378,-122.4034479:37.7692134,-122.4034461:37.7692138,-122.403438 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433296000000 && 37.768831,-122.403161:37.7688334,-122.4031612:37.7688339,-122.4031521:37.7688315,-122.4031519 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359213000000 && 37.7695734,-122.4036:37.7695343,-122.403597:37.769537,-122.4035424:37.7695685,-122.4035448:37.7695689,-122.4035365:37.7695373,-122.4035341:37.7695398,-122.4034816:37.7695717,-122.4034841:37.769572,-122.4034776:37.7695402,-122.4034752:37.7695426,-122.4034245:37.7695738,-122.4034269:37.7695742,-122.4034193:37.769543,-122.403417:37.7695441,-122.4033927:37.7695387,-122.4033923:37.7695287,-122.4036033:37.7695731,-122.4036067 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 18 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #4c4c4c || building:colour -> #4c4c4c || last_edit_version -> 1 || height -> 18.5\n634433317000000 && 37.7688193,-122.4033052:37.7688193,-122.4033092:37.7688102,-122.4033092:37.7688102,-122.4033051 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433267000000 && 37.768882,-122.403462:37.768882,-122.403466:37.7688729,-122.403466:37.7688729,-122.4034619 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433225000000 && 37.7692783,-122.4035724:37.7692766,-122.4035723:37.7692784,-122.403537:37.7692801,-122.4035371 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646665000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433393000000 && 37.7688981,-122.4035115:37.768898,-122.403516:37.7688774,-122.4035154:37.7688775,-122.4035109 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646679000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n170183650000000 && 37.7700659,-122.4031773:37.7699503,-122.4031666:37.7699291,-122.4034844:37.7699387,-122.4035354:37.7699853,-122.4035944:37.7700341,-122.4036521:37.7700828,-122.4036628:37.7701348,-122.4036319:37.7703341,-122.403387:37.7702387,-122.4032752:37.7700808,-122.4034795:37.770104,-122.4035018:37.7700818,-122.403522:37.7700521,-122.4035166:37.7700298,-122.4034911:37.770032,-122.4034495:37.7700521,-122.4034469 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768315000 || last_edit_user_id -> 3817650 || name -> C || last_edit_version -> 4 || building -> yes || height -> 11\n634412022000000 && 37.7694288,-122.403135:37.7694297,-122.4031207:37.7694048,-122.4031183:37.7694039,-122.4031326 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359279000000 && 37.7696945,-122.4033312:37.7697021,-122.4033314:37.7697021,-122.4033351:37.7696945,-122.403335 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359206000000 && 37.7689225,-122.4037567:37.7689476,-122.403759:37.7689484,-122.4037452:37.7689233,-122.4037429 && last_edit_user_name -> chachafish || last_edit_changeset -> 63568221 || last_edit_time -> 1539679425000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> dark_grey || building:colour -> #89a86f || last_edit_version -> 2 || building:material -> glass || height -> 5\n634433180000000 && 37.7692822,-122.4036173:37.7692818,-122.4036253:37.7691706,-122.4036159:37.7691711,-122.4036078 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646661000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359290000000 && 37.7697166,-122.4032343:37.7697134,-122.4033178:37.769705,-122.4033173:37.7697079,-122.4032429:37.7696583,-122.4032399:37.7696595,-122.403208:37.7696669,-122.4032084:37.769666,-122.4032311 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634411996000000 && 37.7689279,-122.4036953:37.76893,-122.4036457:37.7689236,-122.4036453:37.7689242,-122.4036304:37.7689435,-122.4036317:37.7689407,-122.4036962 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433222000000 && 37.7693147,-122.4036072:37.7693146,-122.4036094:37.7692065,-122.4036004:37.7692066,-122.4035982 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433277000000 && 37.7688485,-122.4036384:37.7688461,-122.4036384:37.7688461,-122.4036293:37.7688484,-122.4036293 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433319000000 && 37.7687958,-122.4032449:37.7687957,-122.4032489:37.7687866,-122.4032488:37.7687866,-122.4032448 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433445000000 && 37.7695228,-122.4032991:37.7695304,-122.4032995:37.7695303,-122.4033033:37.7695227,-122.4033029 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646681000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359253000000 && 37.7696887,-122.4036583:37.7696886,-122.4036679:37.7696857,-122.4036679:37.7696858,-122.4036582 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634411991000000 && 37.7691196,-122.4036025:37.7691218,-122.4035508:37.7691052,-122.4035497:37.7691069,-122.40351:37.7691353,-122.4035119:37.7691366,-122.4034812:37.7690934,-122.4034783:37.7690927,-122.403494:37.7690821,-122.4034932:37.7690775,-122.4035996 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n205644329000000 && 37.7680575,-122.4003046:37.7682545,-122.4005502:37.7683539,-122.4004198:37.7678867,-122.3998354:37.7677624,-122.3999933:37.7676293,-122.4001635:37.767989,-122.4006101:37.7680892,-122.4004831:37.7680023,-122.4003704:37.7677637,-122.4000607:37.7677898,-122.400028:37.7678127,-122.3999993 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 62713402 || last_edit_time -> 1537317501000 || last_edit_user_id -> 53073 || last_edit_version -> 5\n634359264000000 && 37.7697004,-122.4035448:37.7697003,-122.4035545:37.7696974,-122.4035544:37.7696975,-122.4035448 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433232000000 && 37.7692307,-122.4034213:37.7692193,-122.4034208:37.7692208,-122.4033737:37.7692322,-122.4033738 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567737 || min_height -> 21 || last_edit_time -> 1539678512000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> grey || building:colour -> grey || last_edit_version -> 2 || height -> 22\n634412049000000 && 37.7690688,-122.4038211:37.7691204,-122.4038258:37.7691209,-122.4038173:37.7690693,-122.4038126 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634415568000000 && 37.7689935,-122.4032593:37.7689375,-122.4032547:37.7689395,-122.4032165:37.7689554,-122.4032178:37.7689608,-122.4031134:37.7689998,-122.4031166:37.7689996,-122.4031221:37.7690373,-122.4031251:37.7690356,-122.4031569:37.7689848,-122.4031528:37.7689833,-122.4031796:37.7689788,-122.4031837:37.7689746,-122.4031912:37.7689724,-122.4032:37.7689723,-122.4032092:37.7689745,-122.4032179:37.7689787,-122.4032255:37.7689844,-122.4032311:37.7689921,-122.4032344:37.7689953,-122.4032352 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433274000000 && 37.7688544,-122.4036556:37.768852,-122.4036556:37.768852,-122.4036466:37.7688544,-122.4036466 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433442000000 && 37.769534,-122.4032067:37.7695339,-122.4032164:37.769531,-122.4032163:37.7695311,-122.4032067 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433203000000 && 37.7693059,-122.4034313:37.7693056,-122.4034393:37.7692653,-122.4034368:37.7692657,-122.4034287 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433329000000 && 37.7687896,-122.4036386:37.7687873,-122.4036386:37.7687873,-122.4036296:37.7687896,-122.4036296 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433371000000 && 37.7690622,-122.4033049:37.7690599,-122.4033049:37.7690599,-122.4032958:37.7690622,-122.4032958 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412002000000 && 37.7693851,-122.403548:37.7693909,-122.4034337:37.7694359,-122.4034374:37.7694301,-122.4035516 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n25371872000000 && 37.7686356,-122.4047621:37.7694373,-122.4048332:37.7696594,-122.4043001:37.7696662,-122.4041628:37.7686831,-122.4040754 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57302156 || addr:housenumber -> 2 || last_edit_time -> 1521411166000 || last_edit_user_id -> 53073 || addr:state -> CA || addr:street -> Henry Adams Street || last_edit_version -> 6 || addr:postcode -> 94103 || building -> yes || addr:city -> San Francisco || height -> 2\n634359259000000 && 37.7696969,-122.4036248:37.7696967,-122.4036344:37.7696938,-122.4036344:37.7696939,-122.4036247 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n256897616000000 && 37.7695634,-122.4048387:37.7695669,-122.4047281:37.769533,-122.4047264:37.7695325,-122.4047418:37.7695295,-122.404837 && last_edit_user_name -> ediyes || last_edit_changeset -> 20040540 || last_edit_time -> 1389904371000 || last_edit_user_id -> 1240849 || last_edit_version -> 1 || building -> yes\n170183646000000 && 37.7705539,-122.4030956:37.7705364,-122.4030717:37.7703987,-122.4028987:37.7701986,-122.4031516:37.7703545,-122.4033484 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768315000 || last_edit_user_id -> 3817650 || name -> B || last_edit_version -> 5 || building -> yes || height -> 12\n634412018000000 && 37.7695143,-122.4038961:37.7695152,-122.4038818:37.7694903,-122.4038794:37.7694894,-122.4038937 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433305000000 && 37.7687942,-122.4034338:37.7687941,-122.4034378:37.768785,-122.4034377:37.768785,-122.4034337 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433284000000 && 37.7689252,-122.4032997:37.7689228,-122.4032997:37.7689228,-122.4032907:37.7689251,-122.4032907 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n351964812000000 && 37.7693445,-122.4021862:37.7692107,-122.4021722:37.7691739,-122.4028067:37.7693137,-122.4028221 && last_edit_changeset -> 55702219 || shop -> carpet || addr:state -> CA || building:levels -> 2 || addr:postcode -> 94103 || building -> yes || addr:city -> San Francisco || last_edit_user_name -> bdon_import || addr:housenumber -> 25 || last_edit_time -> 1516768317000 || last_edit_user_id -> 3817650 || name -> floordesign || addr:street -> Rhode Island Street || last_edit_version -> 5 || height -> 6\n634433310000000 && 37.768814,-122.403388:37.768814,-122.403392:37.7688049,-122.403392:37.7688049,-122.403388 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359202000000 && 37.7693247,-122.4037935:37.7691204,-122.4037749:37.7691213,-122.4037594:37.7691264,-122.4036671:37.7691334,-122.4036677:37.76914,-122.4035471:37.7691535,-122.4035483:37.7691581,-122.4034657:37.7690891,-122.4034597:37.7690875,-122.4034879:37.7690212,-122.4034821:37.7689893,-122.4034793:37.7689909,-122.403449:37.7689197,-122.4034428:37.7689151,-122.4035283:37.7689283,-122.4035295:37.7689219,-122.4036472:37.7689285,-122.4036478:37.7689233,-122.4037429:37.7689225,-122.4037567:37.7687275,-122.4037392:37.7687336,-122.4036314:37.7687477,-122.4036327:37.7687523,-122.4035516:37.7687347,-122.40355:37.7687364,-122.4035185:37.7687384,-122.4034851:37.7687518,-122.4034864:37.7687636,-122.4032871:37.7687704,-122.4032877:37.768776,-122.4031937:37.7687585,-122.4031921:37.7687641,-122.4030983:37.768869,-122.4031082:37.7688696,-122.4030972:37.7689349,-122.4031034:37.7689345,-122.4031105:37.7689596,-122.4031129:37.7689536,-122.403216:37.7689387,-122.4032146:37.7689362,-122.4032572:37.768996,-122.4032629:37.7689976,-122.4032358:37.7690411,-122.4032399:37.7691097,-122.4032461:37.7691081,-122.4032748:37.7691673,-122.4032802:37.7691695,-122.4032417:37.7691489,-122.4032398:37.7691554,-122.4031265:37.7691829,-122.403129:37.7691832,-122.4031245:37.7692562,-122.4031311:37.7692558,-122.403137:37.7693627,-122.4031467:37.7693564,-122.4032567:37.7693366,-122.4032549:37.7693321,-122.4033352:37.7693394,-122.4033359:37.7693278,-122.403539:37.7693401,-122.4035401:37.7693383,-122.4035726:37.7693481,-122.4035735:37.7693459,-122.4036125:37.769316,-122.4036098:37.7693116,-122.4036877:37.7693307,-122.4036894 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567870 || last_edit_time -> 1539678771000 || last_edit_user_id -> 2237750 || building:part -> yes || last_edit_version -> 3 || building:material -> glass || height -> 21\n634359244000000 && 37.7695362,-122.4037462:37.7695359,-122.403759:37.7695329,-122.4037589:37.7695333,-122.4037461 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359286000000 && 37.7696761,-122.4032616:37.769676,-122.4032713:37.7696731,-122.4032712:37.7696732,-122.4032616 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359215000000 && 37.7694362,-122.403789:37.7694364,-122.4037853:37.7694948,-122.4037906:37.7694946,-122.4037943 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359257000000 && 37.7696951,-122.4036424:37.769695,-122.403652:37.7696921,-122.403652:37.7696922,-122.4036423 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433189000000 && 37.7691622,-122.4033298:37.7691618,-122.4033384:37.769048,-122.4033298:37.7690484,-122.4033212 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433194000000 && 37.769198,-122.403553:37.7691976,-122.403561:37.7691732,-122.4035592:37.7691736,-122.4035511 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433215000000 && 37.7693019,-122.4036774:37.7693016,-122.4036854:37.7692614,-122.4036829:37.7692617,-122.4036748 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433362000000 && 37.7690861,-122.4034054:37.7690838,-122.4034054:37.7690837,-122.4033964:37.7690861,-122.4033964 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412011000000 && 37.7690211,-122.4038499:37.7690219,-122.4038355:37.768997,-122.4038331:37.7689962,-122.4038474 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412053000000 && 37.7693726,-122.403848:37.769393,-122.4038496:37.7693939,-122.40383:37.7693735,-122.4038285 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634411998000000 && 37.7690793,-122.4036646:37.7690812,-122.4036261:37.7690612,-122.4036245:37.7690607,-122.403634:37.7690726,-122.4036349:37.7690712,-122.403664 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433220000000 && 37.7691657,-122.4037008:37.7691654,-122.4037089:37.7691466,-122.4037078:37.7691469,-122.4036997 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433241000000 && 37.7689272,-122.4034172:37.7689239,-122.4034168:37.7689312,-122.4032902:37.7689346,-122.4032905 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646666000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.2\n351315297000000 && 37.7705364,-122.4030717:37.7705861,-122.4030083:37.7705227,-122.4029287:37.770505,-122.4029512:37.7704308,-122.4028579:37.7703987,-122.4028987 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31708207 || last_edit_time -> 1433396636000 || last_edit_user_id -> 33757 || last_edit_version -> 1 || building -> yes\n547181915000000 && 37.7712734,-122.4052538:37.7715688,-122.4048816:37.7715487,-122.4048561:37.7716226,-122.404763:37.771286,-122.4043353:37.7713576,-122.4042451:37.7716995,-122.4046795:37.7717684,-122.4045928:37.7717863,-122.4046156:37.7721193,-122.4041964:37.7720501,-122.4041085:37.7720341,-122.4041299:37.7719493,-122.4042365:37.7719212,-122.4042719:37.7717987,-122.4044276:37.7717749,-122.4044571:37.7717497,-122.4044245:37.7716931,-122.4044949:37.7714001,-122.4041177:37.7714416,-122.4040661:37.7714292,-122.4040501:37.77139,-122.4040989:37.7713572,-122.4040567:37.7713328,-122.404087:37.7713201,-122.4040706:37.7708531,-122.4046509:37.7708446,-122.4046764:37.7708446,-122.4047173:37.7708594,-122.4047488:37.7709745,-122.4048755:37.7709973,-122.4049225:37.7710238,-122.4049419:37.7710693,-122.4050123:37.771107,-122.4050438:37.7711775,-122.4051345:37.7711936,-122.4051142:37.7712141,-122.4051403:37.771226,-122.4051253:37.7712472,-122.4051522:37.7712202,-122.4051862 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57301060 || level -> 1 || last_edit_time -> 1521407168000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n634433246000000 && 37.7688754,-122.403495:37.768768,-122.4034831:37.7687686,-122.4034744:37.768876,-122.4034863 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646667000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359224000000 && 37.7694775,-122.4037436:37.7694876,-122.4037445:37.7694874,-122.4037482:37.7694774,-122.4037473 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359266000000 && 37.7697026,-122.4035004:37.7697024,-122.40351:37.7696995,-122.40351:37.7696996,-122.4035003 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433414000000 && 37.7696536,-122.4033035:37.7696528,-122.4033185:37.7695532,-122.4033106:37.769554,-122.4032955 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567672 || min_height -> 18 || last_edit_time -> 1539678375000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> grey || building:colour -> grey || last_edit_version -> 2 || height -> 20\n634412051000000 && 37.7692185,-122.4038349:37.769252,-122.4038375:37.7692524,-122.403829:37.7692189,-122.4038263 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412009000000 && 37.7687978,-122.4038288:37.7687987,-122.4038145:37.7687738,-122.4038121:37.7687729,-122.4038264 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n28689469000000 && 37.7766061,-122.3945805:37.77674,-122.3947513:37.776876,-122.3949266:37.7767241,-122.3951154:37.776672,-122.395176:37.7766548,-122.3951543:37.7765431,-122.3952959:37.7765961,-122.3953616:37.776347,-122.3956835:37.7763629,-122.3957009:37.7758721,-122.3963366:37.7754969,-122.3968033:37.7754057,-122.3969361:37.775199,-122.397207:37.7750389,-122.3973867:37.7743648,-122.3982397:37.7734796,-122.3993675:37.7734224,-122.3994239:37.7723475,-122.4007904:37.7722234,-122.4009728:37.7721672,-122.4009286:37.7720888,-122.4010922:37.7718874,-122.4013604:37.7718513,-122.4013859:37.771775,-122.4014114:37.7706365,-122.400026:37.7704849,-122.3998624:37.7686766,-122.3975794:37.7691311,-122.3978454:37.7693483,-122.3981115:37.7700403,-122.3989698:37.7704584,-122.3994301:37.7708588,-122.3997026:37.7709317,-122.3997779:37.7711881,-122.3998537:37.7713867,-122.3998923:37.7716347,-122.3998453:37.7718793,-122.3997345:37.7720621,-122.3995449:37.7724385,-122.3991939:37.7728466,-122.3988349:37.7733045,-122.3982668:37.7736564,-122.3978937:37.7738396,-122.3977124:37.774068,-122.3974352:37.7745316,-122.3966781:37.7761926,-122.3945703:37.7762203,-122.3946066:37.7762738,-122.3945377:37.7763178,-122.3945935:37.7763665,-122.3946553:37.7764216,-122.394725:37.7764564,-122.3947688:37.7764871,-122.3947298 && last_edit_user_name -> StellanL || last_edit_changeset -> 38624789 || last_edit_time -> 1460817832000 || last_edit_user_id -> 28775 || landuse -> railway || name -> Caltrain || last_edit_version -> 13\n634433272000000 && 37.768897,-122.4034215:37.768897,-122.4034255:37.7688879,-122.4034255:37.7688879,-122.4034215 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433201000000 && 37.7692431,-122.4032058:37.7692427,-122.4032139:37.7691888,-122.4032102:37.7691891,-122.4032022 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433369000000 && 37.7690824,-122.4033072:37.7690801,-122.4033072:37.76908,-122.4032982:37.7690824,-122.4032982 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433298000000 && 37.7688131,-122.4031601:37.7688154,-122.4031603:37.7688159,-122.4031513:37.7688136,-122.4031511 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n221434106000000 && 37.7700111,-122.4039044:37.7700101,-122.403931:37.7700045,-122.4039566:37.7699945,-122.40398:37.7699806,-122.404:37.7699635,-122.4040155:37.7699441,-122.4040258:37.7699234,-122.4040304:37.7699024,-122.404029:37.7698822,-122.4040217:37.7698638,-122.4040089:37.7698481,-122.4039912:37.7698359,-122.4039695:37.7698279,-122.4039449:37.7698243,-122.403917:37.7698261,-122.4038888:37.7698331,-122.403862:37.769845,-122.403838:37.769861,-122.4038182:37.7698803,-122.4038039:37.7699017,-122.4037957:37.769924,-122.4037942:37.769946,-122.4037994:37.7699663,-122.4038111:37.7699839,-122.4038286:37.7699977,-122.4038509:37.770007,-122.4038766 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 16134749 || last_edit_time -> 1368600966000 || last_edit_user_id -> 14293 || landuse -> grass || last_edit_version -> 1\n634433227000000 && 37.7692214,-122.4035637:37.7692213,-122.4035659:37.7692079,-122.4035651:37.769208,-122.4035629 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646665000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n215531272000000 && 37.7692927,-122.4019062:37.7699625,-122.4019761:37.7700055,-122.4011863:37.7700331,-122.4010321:37.7700416,-122.4008569:37.7692547,-122.4018566 && last_edit_user_name -> oba510 || last_edit_changeset -> 15616228 || last_edit_time -> 1365149359000 || last_edit_user_id -> 933797 || landuse -> industrial || last_edit_version -> 1\n573287806000000 && 37.7697141,-122.4038387:37.7687212,-122.4037441:37.7687603,-122.4030868:37.7697533,-122.4031814 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || addr:housenumber -> 1 || last_edit_time -> 1539630504000 || last_edit_user_id -> 2237750 || addr:state -> CA || landuse -> residential || name -> One Henry Adams || addr:street -> Henry Adams Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n634433324000000 && 37.7687952,-122.4032526:37.7687952,-122.4032566:37.7687861,-122.4032565:37.7687861,-122.4032525 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412020000000 && 37.7696232,-122.4039075:37.7696241,-122.4038932:37.7695992,-122.4038908:37.7695983,-122.4039051 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359235000000 && 37.7696198,-122.4037637:37.7696194,-122.4037764:37.7696165,-122.4037763:37.7696168,-122.4037636 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433253000000 && 37.7687957,-122.4035235:37.7687394,-122.4035185:37.768741,-122.403489:37.7687973,-122.4034939 && last_edit_user_name -> chachafish || last_edit_changeset -> 63568069 || min_height -> 23 || last_edit_time -> 1539679151000 || last_edit_user_id -> 2237750 || building:part -> yes || last_edit_version -> 2 || height -> 24\n634359277000000 && 37.7697032,-122.4033921:37.7697031,-122.4034017:37.7697002,-122.4034017:37.7697003,-122.403392 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n132813869000000 && 37.7717627,-122.4077712:37.7715504,-122.4074992:37.7712604,-122.4078614:37.7706171,-122.407037:37.7708708,-122.4067202:37.7706616,-122.4064522:37.7702353,-122.4069846:37.7713,-122.4083491 && last_edit_user_name -> skela || last_edit_changeset -> 9521974 || shop -> mall || last_edit_time -> 1318253589000 || last_edit_user_id -> 93285 || building:levels -> 2 || source -> Bing || last_edit_version -> 1 || building -> yes\n634433421000000 && 37.7696334,-122.4032503:37.7696333,-122.40326:37.7696304,-122.4032599:37.7696305,-122.4032503 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359204000000 && 37.769091,-122.4036722:37.7690824,-122.4036714:37.7690817,-122.4036826:37.7690682,-122.4036813:37.7690708,-122.4036371:37.7690568,-122.4036358:37.7690579,-122.4036171:37.7690142,-122.4036131:37.768995,-122.4036113:37.768994,-122.4036287:37.768981,-122.4036275:37.7689784,-122.4036723:37.7689595,-122.4036706:37.7689603,-122.4036579:37.7689535,-122.4036573:37.7689484,-122.4037452:37.7689476,-122.403759:37.7689741,-122.4037617:37.769006,-122.4037645:37.7690504,-122.4037683:37.7690853,-122.4037717:37.7690862,-122.4037562 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725653000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #89a86f || building:colour -> #A0522D || last_edit_version -> 4 || building:material -> glass || height -> 7.5\n634433182000000 && 37.7691857,-122.403312:37.7691794,-122.4033115:37.7691853,-122.4032001:37.7691916,-122.4032006 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646661000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359246000000 && 37.7695446,-122.4036693:37.7695531,-122.4036695:37.769553,-122.4036732:37.7695445,-122.403673 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433350000000 && 37.7690304,-122.4034052:37.7690281,-122.4034052:37.7690281,-122.4033961:37.7690304,-122.4033961 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359288000000 && 37.7697008,-122.4032647:37.7697007,-122.4032743:37.7696977,-122.4032742:37.7696978,-122.4032646 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146852000000 && 37.7686585,-122.4013284:37.7686688,-122.4011414:37.7682512,-122.4011045:37.7682409,-122.4012915 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768316000 || last_edit_user_id -> 3817650 || last_edit_version -> 3 || building -> yes || height -> 7\n288667880000000 && 37.7683456,-122.4045554:37.7680774,-122.4043175:37.7680583,-122.4043519:37.7680406,-122.4043363:37.7680137,-122.4043848:37.7680314,-122.4044005:37.7680132,-122.4044333:37.7682815,-122.4046711 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55665623 || last_edit_time -> 1516652476000 || last_edit_user_id -> 3817650 || last_edit_version -> 3 || building -> yes || height -> 7\n634433279000000 && 37.7688267,-122.4036225:37.7688244,-122.4036225:37.7688243,-122.4036134:37.7688267,-122.4036134 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433447000000 && 37.7695077,-122.4032961:37.7695153,-122.4032965:37.7695152,-122.4033003:37.7695076,-122.4032999 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646681000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n635049069000000 && 37.7690504,-122.4037683:37.7690489,-122.4037965:37.7689726,-122.40379:37.7689741,-122.4037617:37.769006,-122.4037645 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || min_height -> 2.9 || last_edit_time -> 1539725653000 || last_edit_user_id -> 2237750 || building:part -> yes || last_edit_version -> 1 || roof:material -> glass || building:material -> glass || height -> 3\n634433313000000 && 37.7688066,-122.4032962:37.7688066,-122.4033002:37.7687975,-122.4033001:37.7687975,-122.4032961 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433271000000 && 37.7689011,-122.4034238:37.7688979,-122.4034237:37.768898,-122.4034122:37.7689012,-122.4034122 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646670000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359249000000 && 37.7695215,-122.4036773:37.7695299,-122.4036774:37.7695299,-122.4036812:37.7695214,-122.403681 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433439000000 && 37.7695406,-122.403226:37.7695405,-122.4032356:37.7695375,-122.4032355:37.7695376,-122.4032259 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433355000000 && 37.7690792,-122.4034229:37.7690768,-122.4034229:37.7690768,-122.4034139:37.7690792,-122.4034139 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646677000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146848000000 && 37.7708663,-122.4027036:37.7711041,-122.4024023:37.7708277,-122.4020532:37.7705661,-122.4017266:37.7703297,-122.4020296:37.7705934,-122.402359 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 32220993 || last_edit_time -> 1435308313000 || last_edit_user_id -> 33757 || name -> Adobe Systems || office -> company || last_edit_version -> 3 || building -> yes\n634412039000000 && 37.769496,-122.4033795:37.7694948,-122.4034074:37.7694405,-122.4034037:37.7694417,-122.4033758 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n288673594000000 && 37.7723967,-122.3991508:37.7723721,-122.3991088:37.772354,-122.3991247:37.772353,-122.3991231:37.7723283,-122.3991469:37.7723323,-122.3991534:37.7722879,-122.3991937:37.7722823,-122.399184:37.7722822,-122.399184:37.7722245,-122.3992362:37.7722302,-122.3992464:37.7721924,-122.3992787:37.7721869,-122.39927:37.7721269,-122.3993248:37.7721328,-122.399334:37.7720951,-122.3993705:37.7720871,-122.3993584:37.772087,-122.3993584:37.7720283,-122.3994116:37.772034,-122.3994227:37.7719987,-122.399457:37.7719917,-122.3994448:37.7719302,-122.3994994:37.7719364,-122.3995122:37.7719004,-122.3995439:37.771894,-122.3995329:37.7718657,-122.3995571:37.7718327,-122.3995804:37.7718375,-122.3995913:37.7717993,-122.3996197:37.7717931,-122.3996075:37.771793,-122.3996075:37.7717611,-122.3996307:37.7717699,-122.3996539:37.771753,-122.3996632:37.7717449,-122.3996399:37.7717448,-122.3996399:37.7717096,-122.3996551:37.7717138,-122.3996699:37.7716707,-122.3996911:37.7716652,-122.3996771:37.7716335,-122.3996924:37.7715973,-122.399705:37.7716007,-122.3997176:37.7715566,-122.3997336:37.7715531,-122.3997188:37.771553,-122.3997188:37.7715194,-122.3997292:37.7714816,-122.3997359:37.7714829,-122.399751:37.7714384,-122.3997569:37.771437,-122.399744:37.7714369,-122.399744:37.7714004,-122.3997494:37.7713636,-122.3997485:37.771363,-122.3997633:37.7713179,-122.3997639:37.7713178,-122.39975:37.771283,-122.399751:37.7712444,-122.3997452:37.771244,-122.3997583:37.7711989,-122.3997529:37.7711997,-122.3997398:37.7711632,-122.3997362:37.7711278,-122.3997263:37.7711278,-122.3997262:37.7711254,-122.3997374:37.7710803,-122.3997255:37.771082,-122.3997124:37.771047,-122.399702:37.7710116,-122.3996854:37.7710088,-122.399698:37.7709681,-122.3996808:37.7709709,-122.3996706:37.7709359,-122.399654:37.7709332,-122.3996636:37.7709153,-122.3996856:37.7709184,-122.3996893:37.7708895,-122.3997279:37.7708905,-122.3997291:37.7708897,-122.39973:37.7708937,-122.3997347:37.7708999,-122.3997373:37.7709077,-122.399741:37.7709075,-122.3997416:37.7709163,-122.3997453:37.7709202,-122.3997474:37.7709202,-122.399747:37.7710254,-122.3997912:37.7711504,-122.3998265:37.7712769,-122.3998436:37.771277,-122.3998436:37.7714045,-122.3998422:37.7715297,-122.3998215:37.7716543,-122.3997831:37.7717693,-122.3997284:37.7717696,-122.3997294:37.7717697,-122.3997294:37.7717936,-122.3997162:37.7717932,-122.3997149:37.7719079,-122.3996387:37.7723949,-122.3991962:37.772394,-122.3991947:37.7724255,-122.3991674:37.7724103,-122.3991389 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55660512 || last_edit_time -> 1516640376000 || last_edit_user_id -> 3817650 || last_edit_version -> 3 || building -> yes || height -> 9\n634359275000000 && 37.7697085,-122.403409:37.7697084,-122.4034187:37.7697055,-122.4034186:37.7697056,-122.403409 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634412013000000 && 37.769187,-122.4038659:37.7691879,-122.4038516:37.769163,-122.4038492:37.7691621,-122.4038635 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359223000000 && 37.769477,-122.4037518:37.7694871,-122.4037527:37.7694869,-122.4037564:37.7694768,-122.4037555 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433302000000 && 37.7687936,-122.4034415:37.7687936,-122.4034455:37.7687845,-122.4034455:37.7687845,-122.4034414 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433260000000 && 37.7689113,-122.4036869:37.7689081,-122.4036869:37.7689082,-122.4036754:37.7689114,-122.4036754 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433344000000 && 37.7690293,-122.4034196:37.769027,-122.4034196:37.769027,-122.4034105:37.7690293,-122.4034105 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359268000000 && 37.7697045,-122.4034722:37.7697044,-122.4034819:37.7697015,-122.4034818:37.7697016,-122.4034722 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433291000000 && 37.7688883,-122.4031675:37.768886,-122.4031674:37.7688862,-122.4031583:37.7688885,-122.4031584 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433249000000 && 37.7688848,-122.4035613:37.7688665,-122.4035594:37.7688671,-122.4035507:37.7688854,-122.4035526 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646668000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359242000000 && 37.7695436,-122.4037466:37.7695433,-122.4037593:37.7695403,-122.4037592:37.7695407,-122.4037464 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n565995475000000 && 37.7682175,-122.3999129:37.7692095,-122.3986624:37.7690217,-122.3984241:37.7680298,-122.3996746 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57301575 || last_edit_time -> 1521408918000 || last_edit_user_id -> 53073 || building:levels -> 4 || last_edit_version -> 2 || building -> yes\n634412006000000 && 37.769356,-122.403506:37.7693596,-122.4034339:37.7693358,-122.4034319:37.7693321,-122.4035041 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433280000000 && 37.7688266,-122.4036394:37.7688243,-122.4036394:37.7688243,-122.4036303:37.7688266,-122.4036303 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412001000000 && 37.7693946,-122.4036418:37.7693971,-122.4035878:37.7693613,-122.4035851:37.7693588,-122.4036391 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433196000000 && 37.7691725,-122.4033615:37.7691661,-122.4033611:37.7691675,-122.4033302:37.7691738,-122.4033306 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433238000000 && 37.7689571,-122.4034212:37.7689556,-122.4034211:37.7689547,-122.4034457:37.7689562,-122.4034458 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646666000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634359216000000 && 37.7694388,-122.4038044:37.7694358,-122.4038042:37.7694367,-122.403786:37.7694396,-122.4037863 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359237000000 && 37.7696395,-122.4037213:37.7696583,-122.4037219:37.7696582,-122.4037256:37.7696394,-122.4037251 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630498000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433322000000 && 37.7688215,-122.4032539:37.7688214,-122.4032579:37.7688123,-122.4032579:37.7688123,-122.4032538 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433448000000 && 37.7694923,-122.4033037:37.7694999,-122.4033041:37.7694998,-122.4033078:37.7694922,-122.4033074 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646681000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433406000000 && 37.7688758,-122.4031768:37.7688757,-122.4031808:37.7688361,-122.4031792:37.7688362,-122.4031751 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646679000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433209000000 && 37.7693054,-122.4032016:37.7693051,-122.4032096:37.7692648,-122.4032071:37.7692651,-122.4031991 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646664000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433377000000 && 37.7690612,-122.4034224:37.7690588,-122.4034224:37.7690588,-122.4034134:37.7690611,-122.4034134 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433293000000 && 37.7689057,-122.40317:37.7689033,-122.4031699:37.7689036,-122.4031609:37.7689059,-122.403161 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359211000000 && 37.7696653,-122.4036967:37.7696828,-122.4033861:37.7696843,-122.4033573:37.769708,-122.4033592:37.7697091,-122.4033368:37.7697272,-122.4033382:37.7697085,-122.4036801:37.7697146,-122.4036806:37.7697135,-122.4037008 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 18 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || last_edit_version -> 1 || height -> 19\n634412070000000 && 37.7696648,-122.4038744:37.7696657,-122.4038553:37.7696468,-122.403854:37.769646,-122.4038731 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n256897615000000 && 37.7695295,-122.404837:37.7695325,-122.4047418:37.7695035,-122.4047403:37.7695004,-122.4048355 && last_edit_user_name -> ediyes || last_edit_changeset -> 20040540 || last_edit_time -> 1389904371000 || last_edit_user_id -> 1240849 || last_edit_version -> 1 || building -> yes\n170183649000000 && 37.7685163,-122.4037742:37.7685544,-122.4031273:37.76852,-122.4031239:37.7685196,-122.4031218:37.7682963,-122.4031007:37.7682915,-122.4031122:37.7682778,-122.4031126:37.7682465,-122.4036799:37.7682424,-122.4037476 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768315000 || last_edit_user_id -> 3817650 || last_edit_version -> 5 || building -> yes || height -> 18\n634412044000000 && 37.7687575,-122.4037915:37.7687837,-122.4037935:37.7687841,-122.4037861:37.7687578,-122.4037841 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412065000000 && 37.7686921,-122.4034619:37.7686904,-122.403491:37.7686985,-122.4034918:37.7687002,-122.4034626 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359280000000 && 37.7696794,-122.4033391:37.769687,-122.4033393:37.769687,-122.403343:37.7696794,-122.4033428 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433258000000 && 37.7689077,-122.4036709:37.7689077,-122.4036749:37.7688986,-122.4036748:37.7688986,-122.4036708 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646668000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433300000000 && 37.7688199,-122.4034428:37.7688199,-122.4034469:37.7688107,-122.4034468:37.7688108,-122.4034428 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646673000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433426000000 && 37.769634,-122.4032325:37.7696339,-122.4032422:37.769631,-122.4032421:37.7696311,-122.4032325 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146860000000 && 37.7701829,-122.401703:37.7702571,-122.4017912:37.7705472,-122.4014008:37.7704922,-122.4013355:37.7705472,-122.4012595:37.7706004,-122.4013356:37.7707671,-122.4011139:37.7707413,-122.4010813:37.7707774,-122.4010357:37.7707998,-122.4010639:37.7709733,-122.4008422:37.7706039,-122.4003835:37.7705867,-122.4004053:37.7705558,-122.400364:37.7705747,-122.4003422:37.7705008,-122.4002574:37.7704458,-122.400327:37.7704145,-122.4003695:37.7703427,-122.4004661:37.7707431,-122.4009422:37.7706675,-122.40104:37.7702686,-122.4005657:37.770207,-122.4006487:37.7706058,-122.401123:37.7705872,-122.4011454:37.7702207,-122.4007096:37.7701617,-122.4007889:37.7705172,-122.4012116:37.7704376,-122.4013227:37.7701743,-122.4010096:37.7701017,-122.4011073:37.7701986,-122.4012224:37.7703409,-122.4013917:37.7703244,-122.4014209:37.7701777,-122.4012465:37.7701135,-122.4013329:37.7702389,-122.401482:37.7702602,-122.4015074:37.7701803,-122.4015976:37.7701631,-122.4015845:37.7701236,-122.4016345:37.7701597,-122.4016845:37.7701219,-122.4017323:37.7700875,-122.4016954:37.7700703,-122.4017802:37.7700961,-122.4017889:37.7700789,-122.4018802:37.7700779,-122.4019173:37.7700875,-122.401941:37.7701014,-122.401945:37.770122,-122.401937:37.770138,-122.4019159:37.7701509,-122.4018882:37.7701597,-122.4018584:37.7701786,-122.4017845:37.7701494,-122.4017476 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || last_edit_time -> 1522356646000 || last_edit_user_id -> 53073 || last_edit_version -> 5 || building -> yes || height -> 19\n634433229000000 && 37.7692567,-122.4035726:37.7692556,-122.4035722:37.7692546,-122.4035713:37.7692539,-122.4035701:37.7692535,-122.4035687:37.7692535,-122.4035672:37.7692538,-122.4035658:37.7692546,-122.4035646:37.7692555,-122.4035637:37.7692567,-122.4035633:37.7692579,-122.4035633:37.769259,-122.4035639:37.76926,-122.4035649:37.7692606,-122.4035663:37.7692608,-122.4035679:37.7692606,-122.4035695:37.76926,-122.4035709:37.769259,-122.403572:37.7692579,-122.4035725 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646665000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 22\n634433187000000 && 37.7692704,-122.4035232:37.7692646,-122.4035227:37.7692837,-122.4031472:37.7692895,-122.4031477 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n215531274000000 && 37.7681289,-122.4010788:37.768673,-122.4011112:37.7686793,-122.4009919:37.7683941,-122.4006249:37.769608,-122.3990862:37.7703554,-122.4000308:37.7689274,-122.4018426:37.7678057,-122.4017536:37.7678062,-122.4017478:37.7678313,-122.4014015:37.768107,-122.401423:37.7681206,-122.4012078 && last_edit_user_name -> oba510 || last_edit_changeset -> 15616228 || last_edit_time -> 1365149360000 || last_edit_user_id -> 933797 || landuse -> industrial || last_edit_version -> 1\n634359228000000 && 37.7695355,-122.4037171:37.7695456,-122.403718:37.7695454,-122.4037217:37.7695353,-122.4037208 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359270000000 && 37.7697151,-122.4034875:37.769715,-122.4034971:37.7697121,-122.4034971:37.7697122,-122.4034874 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634433242000000 && 37.7691338,-122.4032776:37.7691324,-122.4032775:37.769131,-122.4033095:37.769166,-122.403312:37.769166,-122.4033105:37.7691325,-122.4033081 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646667000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433326000000 && 37.7688142,-122.403478:37.7688112,-122.4034778:37.7688319,-122.4031054:37.7688347,-122.4031056 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646675000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.2\n634359273000000 && 37.7697206,-122.4033939:37.7697205,-122.4034036:37.7697175,-122.4034035:37.7697176,-122.4033939 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630502000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634411992000000 && 37.7691085,-122.4037177:37.7691106,-122.4036682:37.769117,-122.4036686:37.7691177,-122.4036537:37.7690984,-122.4036524:37.7690957,-122.4037168 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433289000000 && 37.7688697,-122.4031659:37.7688674,-122.4031658:37.7688676,-122.4031568:37.76887,-122.4031569 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433373000000 && 37.7690638,-122.4032884:37.7690615,-122.4032884:37.7690615,-122.4032794:37.7690638,-122.4032794 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646678000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433315000000 && 37.7688326,-122.4032982:37.7688326,-122.4033022:37.7688235,-122.4033021:37.7688235,-122.4032981 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433231000000 && 37.769249,-122.4033498:37.7692376,-122.4033492:37.7692394,-122.4032909:37.7692508,-122.4032914 && last_edit_user_name -> chachafish || last_edit_changeset -> 63567737 || min_height -> 21 || last_edit_time -> 1539678512000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> grey || building:colour -> grey || last_edit_version -> 2 || height -> 22\n634359218000000 && 37.7696558,-122.403319:37.769656,-122.4033153:37.7697352,-122.4033218:37.769735,-122.4033255 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433236000000 && 37.7691239,-122.4034622:37.7691225,-122.4034621:37.7691235,-122.4034373:37.7691586,-122.4034397:37.7691586,-122.4034411:37.7691249,-122.4034389 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646666000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> black || building:colour -> black || last_edit_version -> 1 || height -> 21.3\n634433320000000 && 37.7688085,-122.4032462:37.7688085,-122.4032502:37.7687993,-122.4032502:37.7687994,-122.4032461 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646674000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359292000000 && 37.7696527,-122.4032083:37.7696521,-122.4032167:37.7695685,-122.4032085:37.7695731,-122.4032005 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630504000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n218146843000000 && 37.7699627,-122.3998986:37.7697541,-122.3996379:37.7694675,-122.4000033:37.7695142,-122.4000616:37.769617,-122.39993:37.7697093,-122.4000454:37.7696066,-122.400177:37.7697909,-122.4004073:37.7700405,-122.4000876:37.7699264,-122.3999451 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768316000 || last_edit_user_id -> 3817650 || last_edit_version -> 4 || building -> yes || height -> 5\n634433262000000 && 37.7688721,-122.4034637:37.768869,-122.4034637:37.7688691,-122.4034482:37.7688722,-122.4034482 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359221000000 && 37.7696123,-122.403696:37.7696119,-122.4037049:37.7696169,-122.4037053:37.7696168,-122.4037092:37.7696245,-122.4037097:37.7696241,-122.4037188:37.7696094,-122.4037178:37.76961,-122.4037042:37.7695891,-122.4037028:37.769589,-122.4037042:37.7695084,-122.4036987:37.7695078,-122.4037135:37.7694999,-122.403713:37.7695009,-122.4036885 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630497000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359263000000 && 37.7696994,-122.4035607:37.7696993,-122.4035704:37.7696963,-122.4035703:37.7696964,-122.4035607 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n219783490000000 && 37.7704744,-122.4060749:37.7705091,-122.4061067:37.7705239,-122.4061561:37.7705239,-122.4062011:37.7705048,-122.4062406:37.77011,-122.4067445:37.7700649,-122.4067533:37.7700163,-122.4067357:37.7697682,-122.4064108:37.7697578,-122.4063811:37.7697543,-122.4063438:37.769776,-122.4060123:37.7697951,-122.4059782:37.7698263,-122.4059618:37.7699729,-122.4059651:37.7702506,-122.4060145:37.7703998,-122.4060496 && parking -> multi-storey || last_edit_changeset -> 55665623 || office -> company || building -> commercial || addr:city -> San Francisco || last_edit_user_name -> bdon_import || addr:housenumber -> 999 || old_name -> Dolby Laboratories || last_edit_time -> 1516652473000 || last_edit_user_id -> 3817650 || addr:street -> Brannan Street || last_edit_version -> 6 || height -> 13\n634412027000000 && 37.7690504,-122.4030981:37.7690512,-122.4030838:37.7690263,-122.4030814:37.7690255,-122.4030957 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433443000000 && 37.7695403,-122.4032076:37.7695402,-122.4032172:37.7695372,-122.4032171:37.7695373,-122.4032075 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n501802406000000 && 37.7701479,-122.4024373:37.770232,-122.4025398:37.7704718,-122.4022248:37.7703179,-122.4020373:37.7700781,-122.4023524 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || addr:housenumber -> 660 || last_edit_time -> 1516768317000 || last_edit_user_id -> 3817650 || name -> E || addr:street -> King Street || last_edit_version -> 2 || building -> yes || height -> 13\n570785600000000 && 37.7712757,-122.4052821:37.7725627,-122.4036566:37.772039,-122.4029995:37.7707351,-122.4046276 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57301995 || addr:housenumber -> 855 || last_edit_time -> 1521410466000 || last_edit_user_id -> 53073 || addr:state -> CA || landuse -> residential || name -> 855 Brennan || addr:street -> Brannan Street || last_edit_version -> 1 || addr:postcode -> 94103 || addr:city -> San Francisco\n218146862000000 && 37.7693137,-122.4028221:37.7693119,-122.4028671:37.7696994,-122.4029118:37.769732,-122.4022268:37.7693445,-122.4021862 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55702219 || last_edit_time -> 1516768317000 || last_edit_user_id -> 3817650 || building:levels -> 2 || last_edit_version -> 6 || building -> yes || height -> 6\n634412025000000 && 37.7692115,-122.4031136:37.7692124,-122.4030993:37.7691875,-122.4030968:37.7691866,-122.4031112 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412046000000 && 37.7688813,-122.4038028:37.7689328,-122.4038081:37.7689334,-122.4037996:37.7688818,-122.4037943 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641016000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359240000000 && 37.7696417,-122.4036961:37.7696415,-122.403709:37.7696386,-122.4037089:37.7696388,-122.403696 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630499000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634359261000000 && 37.7697093,-122.4035805:37.7697092,-122.4035902:37.7697062,-122.4035901:37.7697064,-122.4035805 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 19 || last_edit_time -> 1539630501000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 19.3\n634359282000000 && 37.7696655,-122.4033287:37.7696731,-122.4033289:37.7696731,-122.4033326:37.7696654,-122.4033324 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630503000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412067000000 && 37.7687027,-122.4032929:37.768701,-122.4033221:37.7687091,-122.4033228:37.7687108,-122.4032936 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634433256000000 && 37.7688965,-122.4036846:37.7688965,-122.4036886:37.7688874,-122.4036886:37.7688874,-122.4036845 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646668000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433424000000 && 37.7696397,-122.4032512:37.7696396,-122.4032608:37.7696366,-122.4032607:37.7696367,-122.4032511 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634412004000000 && 37.7694086,-122.4033979:37.7694116,-122.4033222:37.7693934,-122.403321:37.7693903,-122.4033967 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359251000000 && 37.7695311,-122.403634:37.7695473,-122.4036346:37.769547,-122.4036492:37.7695308,-122.4036487 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 21 || last_edit_time -> 1539630500000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433269000000 && 37.7688866,-122.4034074:37.7688866,-122.4034114:37.7688775,-122.4034113:37.7688775,-122.4034073 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646669000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433185000000 && 37.7692021,-122.4035965:37.7691955,-122.4035959:37.7691997,-122.4035167:37.7692063,-122.4035173 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646662000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433353000000 && 37.7690368,-122.4034054:37.7690345,-122.4034054:37.7690345,-122.4033964:37.7690368,-122.4033964 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646676000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433437000000 && 37.7695343,-122.4032251:37.7695342,-122.4032348:37.7695313,-122.4032347:37.7695314,-122.4032251 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646680000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433198000000 && 37.7692257,-122.4033349:37.7692256,-122.4033443:37.7691918,-122.4033435:37.7691919,-122.4033341 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646663000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433282000000 && 37.7689077,-122.4032979:37.7689054,-122.4032979:37.7689054,-122.4032889:37.7689077,-122.4032889 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646671000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n634433450000000 && 37.7694893,-122.4032692:37.7695165,-122.4032699:37.7695166,-122.40326:37.7694998,-122.4032596:37.7694999,-122.4032568:37.7695194,-122.4032574:37.7695191,-122.4032738:37.7694892,-122.4032731 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646681000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n184540572000000 && 37.7678057,-122.4017536:37.7678062,-122.4017478:37.7678313,-122.4014015:37.768107,-122.401423:37.7681206,-122.4012078:37.7681289,-122.4010788:37.7681378,-122.4009393:37.7681451,-122.4008215:37.7680867,-122.4007656:37.7683592,-122.4004246:37.768163,-122.4001799:37.7678867,-122.3998354:37.7677544,-122.3996667:37.7680872,-122.399248:37.7689084,-122.398215:37.7684377,-122.3976303:37.7676352,-122.3986704:37.766984,-122.3994857:37.7668529,-122.3996499:37.7670171,-122.3998597:37.767146,-122.4000245:37.7673049,-122.4002276:37.7676119,-122.4006234:37.7675811,-122.4010399:37.7675765,-122.4011:37.7675736,-122.4011377:37.767572,-122.4011588:37.7675567,-122.4013658:37.7675303,-122.4017227 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 59447024 || alt_name -> CCA || old_name -> California College of Arts and Crafts;CCAC || last_edit_time -> 1527815892000 || last_edit_user_id -> 53073 || amenity -> university || name -> California College of the Arts || last_edit_version -> 9 || wikidata -> Q5020382\n634433295000000 && 37.7688374,-122.4031612:37.7688398,-122.4031614:37.7688403,-122.4031524:37.7688379,-122.4031522 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || min_height -> 21 || last_edit_time -> 1539646672000 || last_edit_user_id -> 2237750 || building:part -> yes || roof:colour -> #708090 || building:colour -> #708090 || last_edit_version -> 1 || height -> 21.3\n243465059000000 && 37.7707107,-122.4053764:37.7703652,-122.4052085:37.7701129,-122.40512:37.7698945,-122.4050798:37.7699175,-122.4046583:37.7698621,-122.4046533:37.7698747,-122.4043573:37.770077,-122.4041061:37.7708973,-122.4051457:37.770792,-122.4052779:37.7707593,-122.4053161 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31640735 || last_edit_time -> 1433177116000 || last_edit_user_id -> 33757 || name -> Sobel Building || last_edit_version -> 3 || building -> retail\n634411994000000 && 37.7689693,-122.4035882:37.7689737,-122.4034935:37.7690059,-122.4034959:37.7690052,-122.4035126:37.768983,-122.4035109:37.7689793,-122.403589 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634412015000000 && 37.7692964,-122.403875:37.7692973,-122.4038607:37.7692724,-122.4038583:37.7692716,-122.4038726 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641014000 || last_edit_user_id -> 2237750 || landuse -> grass || last_edit_version -> 1\n634359209000000 && 37.7695909,-122.4036838:37.7695947,-122.403608:37.7696624,-122.4036133:37.7696587,-122.4036891 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || min_height -> 18 || last_edit_time -> 1539630496000 || last_edit_user_id -> 2237750 || building:part -> yes || building:colour -> #A0522D || last_edit_version -> 1 || height -> 24\n# Lines\n8917256000000 && 37.7696275,-122.3989519:37.7695525,-122.399048:37.768339,-122.4006035:37.7683051,-122.4008267:37.7682169,-122.4009125:37.7681378,-122.4009393 && last_edit_user_name -> Aaron Lidman || tiger:cfcc -> A41 || last_edit_changeset -> 62050947 || access -> no || surface -> paved || last_edit_time -> 1535401344000 || last_edit_user_id -> 53073 || name -> Channel Street || tiger:name_type -> St || highway -> residential || last_edit_version -> 23 || tiger:county -> San Francisco, CA\n8920472000000 && 37.7704732,-122.3997657:37.7700669,-122.3992489:37.7695295,-122.3985735:37.7694516,-122.3984773:37.7692733,-122.3982538:37.7688321,-122.3977008:37.7678902,-122.3965211:37.7675178,-122.3960609:37.7671425,-122.3955727:37.7666615,-122.394936:37.7666122,-122.3948708:37.7665023,-122.3947384:37.7661255,-122.3942618:37.7657493,-122.393892:37.7653454,-122.3935799:37.7648874,-122.3932929:37.7644182,-122.3930577:37.7639947,-122.3928986:37.7636045,-122.3928021:37.7631974,-122.3927377 && gauge -> 1435 || owner -> Peninsula Corridor Joint Powers Board || last_edit_changeset -> 64205910 || railway:traffic_mode -> passenger || usage -> main || maxspeed -> 40 mph || history -> Retrieved from v10 || maxspeed:freight -> 10 mph || tiger:county -> San Francisco, CA || railway:track_ref -> 2 || last_edit_user_name -> clay_c || tiger:cfcc -> B11:B21 || electrified -> no || last_edit_time -> 1541439893000 || last_edit_user_id -> 119881 || name -> Peninsula Subdivision Main 2 || tiger:name_base -> Union Pacific Railroad || railway -> rail || last_edit_version -> 33\n36331135000000 && 37.7709091,-122.3995214:37.7709918,-122.399604:37.7711194,-122.3996683:37.7712431,-122.3996866:37.7713653,-122.399693:37.7714776,-122.3996854:37.7715929,-122.3996552:37.7716873,-122.3996048:37.7717828,-122.3995548:37.7718729,-122.3994901:37.7724061,-122.3990056 && last_edit_user_name -> StellanL || last_edit_changeset -> 38624789 || access -> no || last_edit_time -> 1460817834000 || last_edit_user_id -> 28775 || highway -> residential || last_edit_version -> 7 || oneway -> yes\n110353426000000 && 37.7725605,-122.3997929:37.7723909,-122.3999655:37.7722349,-122.4000833:37.7720735,-122.4001743:37.7718676,-122.4002525:37.7716766,-122.4002893:37.7714721,-122.4002893:37.7712686,-122.4002573:37.7710631,-122.400185:37.7708752,-122.4000823:37.7706846,-122.3999297:37.7704988,-122.399727 && gauge -> 1435 || owner -> Peninsula Corridor Joint Powers Board || last_edit_changeset -> 64205910 || railway:traffic_mode -> passenger || usage -> main || maxspeed -> 25 mph || history -> Retrieved from v10 || maxspeed:freight -> 10 mph || tiger:county -> San Francisco, CA || railway:track_ref -> 1 || last_edit_user_name -> clay_c || tiger:cfcc -> B21 || electrified -> no || last_edit_time -> 1541440010000 || last_edit_user_id -> 119881 || name -> Peninsula Subdivision Main 1 || tiger:name_base -> Union Pacific Railroad || railway -> rail || last_edit_version -> 14\n119709582000000 && 37.7725952,-122.3996803:37.7724927,-122.3997883:37.7723846,-122.3998846:37.7722441,-122.3999902:37.7720827,-122.400086:37.7718816,-122.4001631:37.7716916,-122.4001998:37.7714881,-122.4002023:37.7712865,-122.4001716:37.7710878,-122.4001078:37.7708979,-122.4000099:37.7707024,-122.3998634:37.7705196,-122.3996754 && gauge -> 1435 || owner -> Peninsula Corridor Joint Powers Board || last_edit_changeset -> 64205910 || railway:traffic_mode -> passenger || usage -> main || maxspeed -> 25 mph || history -> Retrieved from v10 || maxspeed:freight -> 10 mph || tiger:county -> San Francisco, CA || railway:track_ref -> 3 || last_edit_user_name -> clay_c || tiger:cfcc -> B21 || electrified -> no || last_edit_time -> 1541440031000 || last_edit_user_id -> 119881 || name -> Peninsula Subdivision Main 3 || tiger:name_base -> Union Pacific Railroad || railway -> rail || last_edit_version -> 11\n119709592000000 && 37.7704732,-122.3997657:37.7705653,-122.3998776:37.7706647,-122.3999887:37.7707627,-122.4000768:37.7708679,-122.4001567:37.7709919,-122.4002347:37.7711037,-122.4002879:37.7712096,-122.4003283:37.7713431,-122.4003641 && gauge -> 1435 || last_edit_changeset -> 64205910 || railway:traffic_mode -> passenger || usage -> main || maxspeed -> 25 mph || maxspeed:freight -> 10 mph || railway:track_ref -> 2 || last_edit_user_name -> clay_c || electrified -> no || last_edit_time -> 1541440032000 || last_edit_user_id -> 119881 || name -> Peninsula Subdivision Main 2 || railway -> rail || last_edit_version -> 5\n148018328000000 && 37.771933,-122.4000456:37.7718299,-122.400097:37.771777,-122.4001181:37.7717247,-122.4001291:37.771642,-122.400142:37.7715666,-122.4001447:37.771473,-122.4001374:37.7712959,-122.4001071:37.7711697,-122.4000713:37.7710522,-122.4000291:37.7709542,-122.3999749:37.7708512,-122.3999061:37.7707482,-122.3998253:37.7706502,-122.3997317:37.7705864,-122.3996619:37.7704434,-122.3995114:37.7703324,-122.3993985:37.7701151,-122.3991677 && gauge -> 1435 || last_edit_user_name -> flierfy || last_edit_changeset -> 32034365 || electrified -> no || railway:traffic_mode -> passenger || last_edit_time -> 1434562811000 || last_edit_user_id -> 445671 || service -> siding || railway -> rail || last_edit_version -> 2\n237055031000000 && 37.7679098,-122.3853462:37.7687659,-122.3850346:37.7698747,-122.3855501:37.7701577,-122.3855948:37.7702142,-122.3856126:37.7693373,-122.3834036:37.7697199,-122.3831784:37.7706551,-122.3855754:37.7706046,-122.3857261:37.7706126,-122.3857458:37.7706527,-122.3857361:37.770673,-122.3858007:37.7706873,-122.385885:37.7707093,-122.3859091:37.7707748,-122.3859255:37.7707925,-122.3859074:37.7708225,-122.3859098:37.7708368,-122.3859379:37.7708588,-122.3859621:37.7708736,-122.3859624:37.7708742,-122.3859728:37.7710202,-122.3859833:37.7710807,-122.3860208:37.7711676,-122.3860748:37.7711675,-122.3861564:37.7712137,-122.3861929:37.7712513,-122.386222:37.7714839,-122.3863871:37.7715751,-122.3863:37.7715904,-122.3863153:37.7716145,-122.3863393:37.7716386,-122.3863616:37.771669,-122.3863895:37.771757,-122.3864701:37.771956,-122.3864542:37.7719574,-122.3865477:37.7720222,-122.3865579:37.7722381,-122.3866086:37.772306,-122.3866297:37.7723784,-122.3866651:37.7723744,-122.3866785:37.7723968,-122.3866851:37.7723927,-122.386708:37.7724218,-122.3867187:37.7724494,-122.3867288:37.7724619,-122.3866785:37.7724793,-122.3866668:37.7725007,-122.3866629:37.7726887,-122.3867018:37.7727843,-122.3867367:37.7728259,-122.3865806:37.7732761,-122.3867755:37.7732861,-122.3867388:37.7734,-122.3849082:37.7733522,-122.3846464:37.7719452,-122.3815399:37.7719401,-122.3815127:37.7719454,-122.381492:37.7719585,-122.3814808:37.7719791,-122.3814771:37.7739552,-122.38174:37.7740825,-122.3814581:37.774451,-122.3818152:37.7745987,-122.3818349:37.7746142,-122.3818372:37.7746281,-122.381849:37.7746363,-122.3818701:37.7746379,-122.3818932:37.7743538,-122.3869477:37.7750788,-122.3870139:37.7751052,-122.3869525:37.7753395,-122.384905:37.7754234,-122.3849196:37.7754306,-122.3848487:37.776339,-122.3850142:37.7763567,-122.3850442:37.7761169,-122.3871096:37.7763414,-122.387128:37.7763346,-122.387284:37.7766122,-122.3873143:37.7767722,-122.3875505:37.7766133,-122.3898455:37.7766466,-122.3898729:37.776629,-122.3899071:37.7766762,-122.3899459:37.7766556,-122.3899859:37.7766357,-122.3899694:37.7765169,-122.3902035:37.7762619,-122.3901812:37.7759474,-122.3905758:37.7748697,-122.3919861:37.7749154,-122.3920548:37.7747568,-122.3922591:37.7746885,-122.3921924:37.7731188,-122.394124:37.7730306,-122.3943143:37.7729282,-122.3944211:37.772651,-122.3948105:37.7719846,-122.3957814:37.7712321,-122.3968349:37.7706081,-122.397668:37.7701568,-122.3981743:37.7701591,-122.3982084:37.7700241,-122.3983854:37.7700239,-122.3984197:37.7701394,-122.3985647:37.770225,-122.3985212:37.7703183,-122.398518:37.770411,-122.3985591:37.7705013,-122.398671:37.7708762,-122.3982123:37.7708851,-122.3982015:37.7713509,-122.3976384:37.7718078,-122.3970405:37.7718488,-122.3970866:37.7725192,-122.3962309:37.7732908,-122.3952282:37.7733952,-122.3950972:37.7734661,-122.3949736:37.7734518,-122.3949551:37.7735022,-122.3948793:37.7735452,-122.3948364:37.7735995,-122.3947949:37.7736818,-122.3947519:37.7746826,-122.3934308:37.7749071,-122.3932158:37.7749597,-122.3931457:37.7749936,-122.3930862:37.7750791,-122.3928519:37.7751019,-122.3928091:37.7751282,-122.3927816:37.7751587,-122.3927586:37.7751265,-122.3927194:37.7751333,-122.3927118:37.7750643,-122.392623:37.7752224,-122.392424:37.7752748,-122.3924899:37.775437,-122.3922851:37.7754445,-122.3922946:37.7754707,-122.3922728:37.7754981,-122.3922511:37.7755242,-122.3922253:37.775549,-122.3921963:37.7755699,-122.3921668:37.7755896,-122.3921339:37.7756081,-122.3920984:37.7756283,-122.3920625:37.7756208,-122.392053:37.7759816,-122.3915975:37.7759891,-122.391607:37.7760147,-122.3915844:37.7760427,-122.3915605:37.7760718,-122.391533:37.776096,-122.3915042:37.7761181,-122.3914729:37.7761357,-122.3914424:37.776156,-122.3914047:37.776174,-122.3913735:37.7761665,-122.391364:37.776485,-122.3909617:37.7764925,-122.3909712:37.7765177,-122.3909478:37.7765446,-122.3909263:37.7765723,-122.3909005:37.776598,-122.3908711:37.7766231,-122.3908371:37.7766436,-122.3908018:37.7766604,-122.3907692:37.7766787,-122.3907361:37.7766712,-122.3907266:37.7768563,-122.3904928:37.7768474,-122.3904858:37.776969,-122.3902496:37.7770483,-122.3903205:37.777082,-122.3902678:37.777114,-122.3902912:37.7771373,-122.3902444:37.7772127,-122.3902934:37.7773718,-122.3898719:37.7773495,-122.3898645:37.7774212,-122.3897295:37.7774047,-122.3897209:37.7774765,-122.3895946:37.77746,-122.3895811:37.7775269,-122.3894547:37.7775105,-122.3894424:37.7775793,-122.389321:37.7775609,-122.3893099:37.7776296,-122.3891804:37.7782445,-122.3875871:37.7782892,-122.3874511:37.7783469,-122.3874609:37.7810992,-122.3876858:37.7814384,-122.3876944:37.7814669,-122.3872304:37.7815326,-122.38617:37.781574,-122.3855023 && last_edit_user_name -> rkuris || last_edit_changeset -> 61874700 || natural -> coastline || last_edit_time -> 1534907201000 || last_edit_user_id -> 501715 || source -> PGS || last_edit_version -> 14\n349439374000000 && 37.7724255,-122.3991674:37.772394,-122.3991947:37.7723949,-122.3991962:37.7719079,-122.3996387:37.7717932,-122.3997149:37.7717936,-122.3997162:37.7717697,-122.3997294:37.7717693,-122.3997284:37.7716543,-122.3997831:37.7715297,-122.3998215:37.7714045,-122.3998422:37.771277,-122.3998436:37.7711504,-122.3998265:37.7710254,-122.3997912:37.7709202,-122.399747:37.7709202,-122.3997474:37.7709163,-122.3997453:37.7709075,-122.3997416:37.7709077,-122.399741:37.7708999,-122.3997373:37.7708937,-122.3997347:37.7708897,-122.39973 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31521737 || website -> http://sfmural.com/ || barrier -> wall || last_edit_time -> 1432795319000 || last_edit_user_id -> 33757 || name -> Systems Mural || tourism -> artwork || artwork_type -> mural || last_edit_version -> 2\n534675090000000 && 37.7709151,-122.3990729:37.7710798,-122.3993047 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53193135 || access -> no || last_edit_time -> 1508798739000 || last_edit_user_id -> 53073 || service -> driveway || highway -> service || last_edit_version -> 1\n634412030000000 && 37.7693422,-122.4038479:37.7690146,-122.4038172 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || barrier -> wall || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || width -> .2 || last_edit_version -> 1 || height -> .3\n634412031000000 && 37.768986,-122.4038145:37.7687246,-122.4037901 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || barrier -> wall || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || width -> .2 || last_edit_version -> 1 || height -> .3\n634412032000000 && 37.7693422,-122.4038479:37.7690146,-122.4038172 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || barrier -> fence || min_height -> .3 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || last_edit_version -> 1 || height -> .6\n634412033000000 && 37.768986,-122.4038145:37.7687246,-122.4037901 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || barrier -> fence || min_height -> .3 || last_edit_time -> 1539641015000 || last_edit_user_id -> 2237750 || last_edit_version -> 1 || height -> .6\n634412058000000 && 37.7697058,-122.4038819:37.7693702,-122.4038505 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || barrier -> wall || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || width -> .2 || last_edit_version -> 1 || height -> .3\n634412059000000 && 37.7697058,-122.4038819:37.7693702,-122.4038505 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || barrier -> fence || min_height -> .3 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || last_edit_version -> 1 || height -> .6\n639288274000000 && 37.7705196,-122.3996754:37.770321,-122.3994255:37.7701151,-122.3991677:37.7695848,-122.3985037:37.7695087,-122.3984035:37.7675775,-122.3959716:37.7671688,-122.3955319 && gauge -> 1435 || owner -> Peninsula Corridor Joint Powers Board || last_edit_changeset -> 64205910 || railway:traffic_mode -> passenger || usage -> main || maxspeed -> 35 mph || history -> Retrieved from v10 || maxspeed:freight -> 10 mph || tiger:county -> San Francisco, CA || railway:track_ref -> 3 || last_edit_user_name -> clay_c || tiger:cfcc -> B21 || electrified -> no || last_edit_time -> 1541440194000 || last_edit_user_id -> 119881 || name -> Peninsula Subdivision Main 3 || tiger:name_base -> Union Pacific Railroad || railway -> rail || last_edit_version -> 3\n639288278000000 && 37.7704988,-122.399727:37.7702973,-122.3994673:37.7695543,-122.3985416:37.7694762,-122.3984446:37.768771,-122.3975512:37.7684835,-122.3971898:37.7675443,-122.3960132:37.7671688,-122.3955319 && gauge -> 1435 || owner -> Peninsula Corridor Joint Powers Board || last_edit_changeset -> 64205910 || railway:traffic_mode -> passenger || usage -> main || maxspeed -> 40 mph || history -> Retrieved from v10 || maxspeed:freight -> 10 mph || tiger:county -> San Francisco, CA || railway:track_ref -> 1 || last_edit_user_name -> clay_c || tiger:cfcc -> B21 || electrified -> no || last_edit_time -> 1541440195000 || last_edit_user_id -> 119881 || name -> Peninsula Subdivision Main 1 || tiger:name_base -> Union Pacific Railroad || railway -> rail || last_edit_version -> 3\n# Points\n5987119136000000 && 37.7690845,-122.4030945 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5184540127000000 && 37.7704503,-122.3988089 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53188507 || natural -> tree || last_edit_time -> 1508786397000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5987119107000000 && 37.7690842,-122.4035367 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65315504000000 && 37.77007,-122.4069977 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195454000 || last_edit_user_id -> 33757 || highway -> traffic_signals || last_edit_version -> 7\n5987119430000000 && 37.7696866,-122.4039358 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n708463429000000 && 37.7707261,-122.399166 && last_edit_user_name -> Gregory Arenius || wheelchair -> yes || last_edit_changeset -> 4539232 || last_edit_time -> 1272358651000 || last_edit_user_id -> 116029 || amenity -> drinking_water || last_edit_version -> 1 || dog -> yes\n281652584000000 && 37.7683433,-122.4066637 && last_edit_user_name -> will l || last_edit_changeset -> 611272 || last_edit_time -> 1217218891000 || last_edit_user_id -> 35195 || amenity -> post_box || last_edit_version -> 1 || created_by -> JOSM\n2156202815000000 && 37.7677624,-122.3999933 && last_edit_user_name -> oba510 || last_edit_changeset -> 15041764 || last_edit_time -> 1360944265000 || last_edit_user_id -> 933797 || entrance -> main_entrance || last_edit_version -> 1\n5987119139000000 && 37.7688356,-122.4030731 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119462000000 && 37.7694,-122.4033675 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5184349774000000 && 37.7708853,-122.3991533 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n3570918495000000 && 37.7706265,-122.4029447 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 55392521 || addr:housenumber -> 669 || website -> http://omakasesf.com/ || last_edit_time -> 1515791007000 || last_edit_user_id -> 371121 || amenity -> restaurant || name -> Omakase || cuisine -> sushi || addr:street -> Townsend Street || last_edit_version -> 4\n5987119433000000 && 37.7697618,-122.4034215 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n295219625000000 && 37.7693309,-122.4062061 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924469000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 6 || crossing -> zebra\n3532718857000000 && 37.7763665,-122.3946553 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || ref -> D || last_edit_time -> 1432195449000 || last_edit_user_id -> 33757 || name -> Exit D || entrance -> yes || last_edit_version -> 1\n4925013320000000 && 37.7703252,-122.4020359 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || access -> no || barrier -> gate || last_edit_time -> 1522356645000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n5780936416000000 && 37.7695525,-122.399048 && last_edit_user_name -> Adamant1 || last_edit_changeset -> 61008584 || barrier -> gate || last_edit_time -> 1532414802000 || last_edit_user_id -> 4540953 || last_edit_version -> 1\n5987138390000000 && 37.7690315,-122.4031811 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557427 || natural -> tree || last_edit_time -> 1539641730000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3619170071000000 && 37.7705055,-122.4047286 && last_edit_changeset -> 57301114 || website -> hardwoodsf.com || microbrewery -> yes || addr:state -> CA || amenity -> restaurant || cuisine -> barbeque || addr:postcode -> 94103 || addr:city -> San Francisco || last_edit_user_name -> Aaron Lidman || addr:housenumber -> 680 || last_edit_time -> 1521407336000 || last_edit_user_id -> 53073 || name -> Hardwood Bar & Smokery || addr:street -> 8th Street || outdoor_seating -> no || last_edit_version -> 2\n5987119117000000 && 37.7691732,-122.4038573 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119398000000 && 37.7688994,-122.4038309 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119440000000 && 37.7687134,-122.4031459 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5987119453000000 && 37.7693335,-122.4038355 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5376562309000000 && 37.7694156,-122.4042196 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 55946628 || last_edit_time -> 1517432749000 || last_edit_user_id -> 371121 || amenity -> cafe || name -> Caffe Pazzo || cuisine -> sandwich || outdoor_seating -> yes || last_edit_version -> 1 || takeaway -> yes\n5987138393000000 && 37.7689748,-122.4031355 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557427 || natural -> tree || last_edit_time -> 1539641730000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65303721000000 && 37.7718329,-122.4016818 && last_edit_user_name -> bhalperin28 || last_edit_changeset -> 33172900 || last_edit_time -> 1438933354000 || last_edit_user_id -> 3130496 || highway -> traffic_signals || last_edit_version -> 10\n5987119443000000 && 37.7687038,-122.4038406 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5515121096000000 && 37.7698908,-122.4006353 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || access -> no || barrier -> gate || last_edit_time -> 1522356643000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n65294603000000 && 37.767373,-122.4028264 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || stop -> all || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> stop || last_edit_version -> 5\n5987137970000000 && 37.769043,-122.4031335 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || barrier -> gate || last_edit_time -> 1539641694000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5991623594000000 && 37.769006,-122.4037645 && last_edit_user_name -> chachafish || last_edit_changeset -> 63591679 || last_edit_time -> 1539725653000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 1\n65320171000000 && 37.767431,-122.401843 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 4\n6024177029000000 && 37.7666615,-122.394936 && last_edit_user_name -> clay_c || last_edit_changeset -> 64000226 || last_edit_time -> 1540879765000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 1\n3607887693000000 && 37.76833,-122.402747 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 32105635 || addr:housenumber -> 111 || last_edit_time -> 1434836681000 || last_edit_user_id -> 371121 || addr:state -> CA || addr:street -> Rhode Island Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n2156202856000000 && 37.767146,-122.4000245 && last_edit_user_name -> marthaleena || last_edit_changeset -> 49909094 || last_edit_time -> 1498720302000 || last_edit_user_id -> 5659851 || entrance -> main_entrance || last_edit_version -> 2\n5449464493000000 && 37.7691009,-122.4028607 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 56836576 || access -> permissive || barrier -> gate || last_edit_time -> 1520040596000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5987138392000000 && 37.7689684,-122.4031757 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557427 || natural -> tree || last_edit_time -> 1539641730000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119442000000 && 37.7686736,-122.4037655 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5987119463000000 && 37.7693507,-122.40329 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5184349775000000 && 37.7708439,-122.3993172 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || last_edit_version -> 1 || leisure -> picnic_table\n6024177034000000 && 37.7684835,-122.3971898 && last_edit_user_name -> clay_c || last_edit_changeset -> 64000226 || last_edit_time -> 1540879765000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 1\n1409407310000000 && 37.77035,-122.40037 && last_edit_user_name -> Bryce C Nesbitt || last_edit_changeset -> 28179793 || ref -> 229 || website -> http://www.citycarshare.org/ || last_edit_time -> 1421391153000 || last_edit_user_id -> 355242 || amenity -> car_sharing || source:pkey -> 229 || name -> Berry & 7th || source -> osmsync:ccs || last_edit_version -> 3 || operator -> City CarShare\n3567489498000000 && 37.7708073,-122.4033554 && last_edit_user_name -> nyuriks || last_edit_changeset -> 52339423 || website -> http://www.practicefusion.com/ || last_edit_time -> 1506287284000 || last_edit_user_id -> 339581 || name -> Practice Fusion || office -> company || wikipedia -> en:Practice Fusion || last_edit_version -> 2 || wikidata -> Q7237353\n5184540126000000 && 37.7709008,-122.3994112 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53188507 || natural -> tree || last_edit_time -> 1508786397000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n3577596694000000 && 37.7701609,-122.4023148 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || addr:housenumber -> 690 || shop -> massage || last_edit_time -> 1433631821000 || last_edit_user_id -> 371121 || addr:state -> CA || name -> Suchada || addr:street -> King Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n5987119106000000 && 37.7690821,-122.4035797 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119127000000 && 37.7697404,-122.403457 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5184540125000000 && 37.7709484,-122.3993443 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53188507 || natural -> tree || last_edit_time -> 1508786397000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5376554067000000 && 37.7700255,-122.4042737 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 56162152 || addr:housenumber -> 680 || last_edit_time -> 1518039815000 || last_edit_user_id -> 371121 || craft -> cabinet_maker || name -> studiobecker || addr:street -> Division Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n65340076000000 && 37.7672565,-122.4047579 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 6\n5987137908000000 && 37.7690142,-122.4036131 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557411 || last_edit_time -> 1539641693000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 1\n3570918494000000 && 37.7702887,-122.4050927 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31708207 || shop -> flooring || last_edit_time -> 1433396636000 || last_edit_user_id -> 33757 || name -> Galleher || last_edit_version -> 1\n65282185000000 && 37.768536,-122.4048807 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 8\n4880860061000000 && 37.770003,-122.406914 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n5987119126000000 && 37.7697386,-122.4035083 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65315506000000 && 37.7695306,-122.4076423 && last_edit_user_name -> yurasi || last_edit_changeset -> 44455305 || last_edit_time -> 1481917382000 || last_edit_user_id -> 3526564 || highway -> traffic_signals || last_edit_version -> 5\n3532718856000000 && 37.7764216,-122.394725 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || ref -> C || last_edit_time -> 1432195449000 || last_edit_user_id -> 33757 || name -> Exit C || entrance -> yes || last_edit_version -> 1\n3577586294000000 && 37.7695213,-122.4028105 && last_edit_changeset -> 31778944 || website -> http://www.boconcept.com/en-us/stores/find-your-local-store/usa/california/boconcept-san-francisco || shop -> interior_decoration || addr:state -> CA || addr:postcode -> 94103 || addr:city -> San Francisco || last_edit_user_name -> AndrewSnow || addr:housenumber -> 1 || last_edit_time -> 1433631820000 || last_edit_user_id -> 371121 || name -> BoConcept || addr:street -> Rhode Island Street || last_edit_version -> 2\n5987119137000000 && 37.7690368,-122.4030905 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3587199694000000 && 37.7696079,-122.4067729 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49085218 || website -> http://www.somaphysio.com/ || last_edit_time -> 1496091476000 || last_edit_user_id -> 33757 || amenity -> doctors || name -> SOMA Sport & Physio || last_edit_version -> 2\n5449464503000000 && 37.7695056,-122.4036409 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || last_edit_time -> 1539630504000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 2\n5987119431000000 && 37.7697422,-122.4038566 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5987119389000000 && 37.7696757,-122.404004 && last_edit_user_name -> chachafish || last_edit_changeset -> 63559070 || last_edit_time -> 1539650461000 || last_edit_user_id -> 2237750 || highway -> stop || last_edit_version -> 2 || direction -> forward\n3607887493000000 && 37.7685586,-122.4025598 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 32105635 || addr:housenumber -> 1755 || last_edit_time -> 1434836682000 || last_edit_user_id -> 371121 || addr:state -> CA || addr:street -> Alameda Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n5987119452000000 && 37.7693839,-122.4038429 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987138388000000 && 37.7691084,-122.4031771 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557427 || natural -> tree || last_edit_time -> 1539641730000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3577596693000000 && 37.7703162,-122.4020946 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || addr:housenumber -> 660 || last_edit_time -> 1433631821000 || last_edit_user_id -> 371121 || addr:state -> CA || addr:street -> King Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n4880860063000000 && 37.7701463,-122.4070931 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n1266064662000000 && 37.7712328,-122.4053572 && last_edit_user_name -> oba510 || last_edit_changeset -> 39324426 || last_edit_time -> 1463290425000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 4 || crossing -> traffic_signals\n65303723000000 && 37.7711307,-122.4008049 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || stop -> minor || last_edit_time -> 1522090886000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 7\n5987119131000000 && 37.7696182,-122.4031478 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n4884299090000000 && 37.7695428,-122.4066627 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49085218 || last_edit_time -> 1496091476000 || last_edit_user_id -> 33757 || amenity -> cafe || name -> Power Up Cafe || cuisine -> juice || last_edit_version -> 1\n5987119118000000 && 37.76923,-122.403862 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65303878000000 && 37.7685958,-122.4038927 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || stop -> all || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> stop || last_edit_version -> 8\n5987119441000000 && 37.7686943,-122.4034456 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n3532718888000000 && 37.769884,-122.4042061 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 4 || crossing -> zebra\n2156214835000000 && 37.7687493,-122.4030378 && last_edit_user_name -> oba510 || last_edit_changeset -> 15041764 || ticker -> no || shelter -> no || last_edit_time -> 1360944863000 || last_edit_user_id -> 933797 || highway -> bus_stop || last_edit_version -> 1\n3532718859000000 && 37.7764871,-122.3947298 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195449000 || last_edit_user_id -> 33757 || entrance -> yes || last_edit_version -> 1\n3577596493000000 && 37.7702061,-122.4022532 && last_edit_changeset -> 31778944 || addr:state -> CA || amenity -> fast_food || addr:postcode -> 94103 || addr:city -> San Francisco || last_edit_user_name -> AndrewSnow || addr:housenumber -> 684 || last_edit_time -> 1433631820000 || last_edit_user_id -> 371121 || name -> CrepeCone || addr:street -> King Street || outdoor_seating -> yes || last_edit_version -> 2\n65339116000000 && 37.7694529,-122.4062261 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || source -> Yahoo || highway -> stop || last_edit_version -> 7\n3577586293000000 && 37.7700224,-122.4024575 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || addr:housenumber -> 2 || shop -> massage || last_edit_time -> 1433631821000 || last_edit_user_id -> 371121 || addr:state -> CA || name -> Suchada || addr:street -> Division Street || last_edit_version -> 3 || addr:postcode -> 94103 || addr:city -> San Francisco\n5987119461000000 && 37.7693478,-122.4033923 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119138000000 && 37.7688873,-122.4030751 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n2304626312000000 && 37.7697023,-122.4040067 && last_edit_user_name -> chachafish || last_edit_changeset -> 63559070 || last_edit_time -> 1539650461000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 5 || crossing -> uncontrolled\n1344032216000000 && 37.7694516,-122.3984773 && last_edit_user_name -> oldtopos || last_edit_changeset -> 8586501 || last_edit_time -> 1309399164000 || last_edit_user_id -> 169004 || railway -> level_crossing || last_edit_version -> 1\n5449464494000000 && 37.7691415,-122.4021759 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 56836576 || access -> permissive || barrier -> gate || last_edit_time -> 1520040596000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n65303897000000 && 37.7683619,-122.4078137 && last_edit_user_name -> yurasi || last_edit_changeset -> 44455215 || last_edit_time -> 1481917110000 || last_edit_user_id -> 3526564 || highway -> traffic_signals || last_edit_version -> 7\n1880211977000000 && 37.7699091,-122.4055266 && last_edit_user_name -> nmixter || last_edit_changeset -> 12839844 || last_edit_time -> 1345770246000 || last_edit_user_id -> 55774 || amenity -> parking || last_edit_version -> 1\n5987119112000000 && 37.7689551,-122.403531 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119435000000 && 37.7697041,-122.4031445 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5987119125000000 && 37.7697346,-122.4035753 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3570918493000000 && 37.7699643,-122.4050283 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31708207 || website -> http://www.kurtadler-sf.com/ || shop -> christmas || last_edit_time -> 1433396636000 || last_edit_user_id -> 33757 || name -> Kurt S. Adler || last_edit_version -> 1\n5184349772000000 && 37.7705878,-122.399059 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n6024177033000000 && 37.7678902,-122.3965211 && last_edit_user_name -> clay_c || last_edit_changeset -> 64000226 || last_edit_time -> 1540879765000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 1\n4880860062000000 && 37.7701423,-122.4069081 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n1344032637000000 && 37.7695087,-122.3984035 && last_edit_user_name -> maggot27 || last_edit_changeset -> 32570723 || last_edit_time -> 1436648775000 || last_edit_user_id -> 118021 || usage -> main || railway -> level_crossing || last_edit_version -> 3\n5987119434000000 && 37.7697698,-122.4032639 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5987119119000000 && 37.7692835,-122.403867 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119124000000 && 37.7696121,-122.4038992 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3279466347000000 && 37.7685653,-122.4022079 && payment:litecoin -> yes || last_edit_changeset -> 57302156 || website -> http://skoolsf.com || payment:bitcoin -> yes || addr:state -> CA || amenity -> restaurant || cuisine -> Seafood,_Japanese,_Asian_Fusion || addr:postcode -> 94103 || addr:city -> San Francisco || last_edit_user_name -> Aaron Lidman || addr:housenumber -> 1725 || phone -> (415) 255-8800 || last_edit_time -> 1521411164000 || last_edit_user_id -> 53073 || name -> Skool SF || addr:street -> Alameda Street || last_edit_version -> 3\n5987137969000000 && 37.7690411,-122.4032399 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || last_edit_time -> 1539646682000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 2\n1880208429000000 && 37.7704352,-122.403736 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31640735 || last_edit_time -> 1433177115000 || last_edit_user_id -> 33757 || amenity -> bank || name -> Union Bank || last_edit_version -> 2\n3532719204000000 && 37.7712243,-122.405549 && last_edit_user_name -> oba510 || last_edit_changeset -> 39324426 || last_edit_time -> 1463290426000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n65283737000000 && 37.7713009,-122.4085367 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195454000 || last_edit_user_id -> 33757 || highway -> traffic_signals || last_edit_version -> 9\n4430188891000000 && 37.7701378,-122.404123 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 42619743 || last_edit_time -> 1475555743000 || last_edit_user_id -> 1306 || highway -> bus_stop || last_edit_version -> 1\n5487354231000000 && 37.7700015,-122.4020862 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || barrier -> bollard || last_edit_time -> 1522356645000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n2671868148000000 && 37.771058,-122.402717 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 50511534 || last_edit_time -> 1500843950000 || last_edit_user_id -> 33757 || amenity -> bicycle_rental || operator:wikidata -> Q4735954 || name -> Townsend at 7th || network:wikidata -> Q16971391 || last_edit_version -> 2 || operator -> Motivate || capacity -> 15 || network -> Ford GoBike\n1880211756000000 && 37.7700958,-122.4048271 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31708207 || shop -> furniture || last_edit_time -> 1433396637000 || last_edit_user_id -> 33757 || name -> Pak Oriental Rugs || last_edit_version -> 3\n5987119429000000 && 37.7687145,-122.40317 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119113000000 && 37.7689549,-122.4034888 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65283717000000 && 37.7690324,-122.4069177 && last_edit_user_name -> Stephen214 || last_edit_changeset -> 56741589 || ref -> 433C || last_edit_time -> 1519788914000 || last_edit_user_id -> 4018842 || ref:right -> 433C || source -> http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf || highway -> motorway_junction || last_edit_version -> 16\n5987138394000000 && 37.7690103,-122.4031449 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557427 || natural -> tree || last_edit_time -> 1539641730000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n2657227481000000 && 37.7699755,-122.4030009 && last_edit_changeset -> 63546947 || website -> http://grandpubah.com/ || disused:amenity -> restaurant || addr:state -> CA || amenity -> restaurant || cuisine -> thai || addr:postcode -> 94103 || addr:city -> San Francisco || last_edit_user_name -> chachafish || addr:housenumber -> 88 || phone -> (415) 255-8188 || last_edit_time -> 1539618322000 || last_edit_user_id -> 2237750 || name -> Grand Pu Bah || addr:street -> Division Street || last_edit_version -> 4\n65356544000000 && 37.7694762,-122.3984446 && last_edit_user_name -> maggot27 || last_edit_changeset -> 32570723 || last_edit_time -> 1436648775000 || last_edit_user_id -> 118021 || usage -> main || railway -> level_crossing || last_edit_version -> 13\n5184349773000000 && 37.7707717,-122.3991795 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n3532718858000000 && 37.7763178,-122.3945935 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || ref -> E || last_edit_time -> 1432195449000 || last_edit_user_id -> 33757 || name -> Exit E || entrance -> yes || last_edit_version -> 1\n5987119428000000 && 37.7687076,-122.4032508 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n2274244022000000 && 37.7698904,-122.4036185 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641017000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 6 || crossing -> zebra\n5987119460000000 && 37.769347,-122.4034724 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n1344032359000000 && 37.7695295,-122.3985735 && last_edit_user_name -> oldtopos || last_edit_changeset -> 8586501 || last_edit_time -> 1309399167000 || last_edit_user_id -> 169004 || railway -> level_crossing || last_edit_version -> 1\n1880207577000000 && 37.7706214,-122.4026406 && last_edit_user_name -> nmixter || last_edit_changeset -> 12839765 || last_edit_time -> 1345769709000 || last_edit_user_id -> 55774 || amenity -> parking || last_edit_version -> 1\n5987138389000000 && 37.7691243,-122.40322 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557427 || natural -> tree || last_edit_time -> 1539641730000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3532718890000000 && 37.7695218,-122.406127 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 3 || crossing -> zebra\n5184540023000000 && 37.7721721,-122.3968768 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53188507 || last_edit_time -> 1508786394000 || last_edit_user_id -> 53073 || entrance -> yes || last_edit_version -> 1\n2156214854000000 && 37.7685627,-122.4028608 && last_edit_user_name -> oba510 || last_edit_changeset -> 15041764 || ticker -> no || shelter -> no || last_edit_time -> 1360944863000 || last_edit_user_id -> 933797 || highway -> bus_stop || last_edit_version -> 1\n5987119459000000 && 37.7694148,-122.4034566 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n2156214848000000 && 37.7698985,-122.4034375 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 42533955 || ticker -> yes || shelter -> yes || last_edit_time -> 1475214971000 || last_edit_user_id -> 1306 || name -> Division St & Camden St || name:en -> Division St & Camden St || highway -> bus_stop || last_edit_version -> 3 || network -> Muni\n5184349779000000 && 37.7707634,-122.3992715 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || natural -> tree || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n423775811000000 && 37.7709918,-122.399604 && last_edit_user_name -> mk408 || last_edit_changeset -> 4058253 || barrier -> gate || last_edit_time -> 1267951280000 || last_edit_user_id -> 201724 || last_edit_version -> 2\n3456791114000000 && 37.7815326,-122.38617 && last_edit_user_name -> malcolmh || last_edit_changeset -> 61942337 || last_edit_time -> 1535093811000 || last_edit_user_id -> 128186 || fee -> no || seamark:small_craft_facility:category -> pump-out || last_edit_version -> 4 || seamark:type -> small_craft_facility\n5184864685000000 && 37.7696641,-122.4037858 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53193519 || last_edit_time -> 1508800407000 || last_edit_user_id -> 53073 || amenity -> restaurant || name -> The Grove || last_edit_version -> 1\n356618598000000 && 37.7705267,-122.4030173 && parking -> underground || last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 32220993 || last_edit_time -> 1435308312000 || last_edit_user_id -> 33757 || amenity -> parking || fee -> yes || name -> Metro @ Showplace Square Garage || last_edit_version -> 2\n1880208351000000 && 37.7701928,-122.4034984 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195453000 || last_edit_user_id -> 33757 || amenity -> cafe || name -> Starbucks || cuisine -> coffee_shop || last_edit_version -> 2\n3562892896000000 && 37.7706133,-122.4035643 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31640735 || website -> http://www.poggenpohl.com/en/find-a-studio/usa/poggenpohl-san-francisco/ || shop -> kitchen || last_edit_time -> 1433177115000 || last_edit_user_id -> 33757 || name -> Poggenpohl Kitchens || last_edit_version -> 2\n3532718854000000 && 37.7767241,-122.3951154 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || ref -> A || last_edit_time -> 1432195449000 || last_edit_user_id -> 33757 || name -> Exit A || entrance -> yes || last_edit_version -> 1\n5987119123000000 && 37.769561,-122.4038938 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119446000000 && 37.7693383,-122.4038828 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n3613001946000000 && 37.7692029,-122.4041915 && last_edit_user_name -> nereocystis || last_edit_changeset -> 32163790 || website -> http://www.annsacks.com/find-a-showroom/ann-sacks-san-francisco || shop -> trade || last_edit_time -> 1435072247000 || last_edit_user_id -> 1835512 || name -> Ann Sacks || last_edit_version -> 1\n65371530000000 && 37.7717012,-122.4064466 && last_edit_user_name -> manoharuss || last_edit_changeset -> 50402608 || ref -> 1A;1B || ref:left -> 1A || last_edit_time -> 1500463872000 || last_edit_user_id -> 3769434 || ref:right -> 1B || source -> http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/80.pdf || highway -> motorway_junction || last_edit_version -> 12\n2304626320000000 && 37.7701309,-122.4040106 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 5 || crossing -> zebra\n3577577196000000 && 37.7696698,-122.4024929 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || addr:housenumber -> 25 || last_edit_time -> 1433631821000 || last_edit_user_id -> 371121 || addr:state -> CA || addr:street -> Division Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n5184540024000000 && 37.7724198,-122.3965706 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53188507 || last_edit_time -> 1508786394000 || last_edit_user_id -> 53073 || entrance -> yes || last_edit_version -> 1\n5987119466000000 && 37.7694652,-122.4033926 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3563920174000000 && 37.7703525,-122.40328 && last_edit_user_name -> AndrewSnow || wheelchair -> yes || last_edit_changeset -> 63915437 || website -> http://saffron685-sf.com/ || last_edit_time -> 1540589885000 || last_edit_user_id -> 371121 || amenity -> restaurant || name -> Safron 685 || opening_hours -> Mo-Sa 11:00-21:00 || cuisine -> mediterranean || last_edit_version -> 2 || takeaway -> yes\n5506470752000000 && 37.7692693,-122.3985025 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090881000 || last_edit_user_id -> 53073 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n5987119424000000 && 37.7686838,-122.4036444 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n984720636000000 && 37.7697271,-122.3983363 && last_edit_user_name -> yurasi || last_edit_changeset -> 44455726 || last_edit_time -> 1481918729000 || last_edit_user_id -> 3526564 || highway -> traffic_signals || last_edit_version -> 3\n703048830000000 && 37.7697377,-122.3981097 && last_edit_user_name -> StellanL || last_edit_changeset -> 19417081 || last_edit_time -> 1386867817000 || last_edit_user_id -> 28775 || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5987119130000000 && 37.76975,-122.4032642 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119456000000 && 37.7693883,-122.4036974 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119133000000 && 37.7693625,-122.4031227 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3532719196000000 && 37.7695536,-122.4063509 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 3 || crossing -> zebra\n5987119120000000 && 37.7693908,-122.4038771 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5184349780000000 && 37.7708728,-122.3991354 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || natural -> tree || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n3563920177000000 && 37.7694599,-122.4046345 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31640735 || website -> http://www.countryfloors.com/san-francisco-country-floors/ || shop -> flooring || last_edit_time -> 1433177115000 || last_edit_user_id -> 33757 || name -> Country Floors || last_edit_version -> 1\n5987119427000000 && 37.7687052,-122.4033098 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3532718886000000 && 37.7697762,-122.4036728 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57466350 || last_edit_time -> 1521830620000 || last_edit_user_id -> 53073 || highway -> crossing || last_edit_version -> 3 || crossing -> zebra\n2304626330000000 && 37.7701231,-122.4038029 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641019000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 6 || crossing -> zebra\n3532718855000000 && 37.77674,-122.3947513 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || ref -> B || last_edit_time -> 1432195449000 || last_edit_user_id -> 33757 || entrance -> yes || last_edit_version -> 1\n5987119132000000 && 37.7694195,-122.403127 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119090000000 && 37.7686589,-122.4028115 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || highway -> stop || last_edit_version -> 1 || direction -> forward\n5987119111000000 && 37.7689533,-122.4035689 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119426000000 && 37.7687015,-122.4033789 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n1880212762000000 && 37.7704179,-122.4069268 && last_edit_user_name -> nmixter || last_edit_changeset -> 12839844 || shop -> clothes || last_edit_time -> 1345770485000 || last_edit_user_id -> 55774 || name -> Nordstrom Rack || last_edit_version -> 1\n5987119437000000 && 37.7692535,-122.4031063 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5987119458000000 && 37.7694077,-122.4035227 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n1880208573000000 && 37.7703366,-122.4038355 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31681487 || last_edit_time -> 1433297175000 || last_edit_user_id -> 33757 || amenity -> atm || name -> Bank of America || last_edit_version -> 2\n65308756000000 && 37.7692412,-122.4079015 && last_edit_user_name -> oba510 || last_edit_changeset -> 47257631 || last_edit_time -> 1490787847000 || last_edit_user_id -> 933797 || source -> Yahoo || highway -> traffic_signals || last_edit_version -> 8\n5987138387000000 && 37.7691105,-122.4031556 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557427 || natural -> tree || last_edit_time -> 1539641730000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5497302690000000 && 37.7692586,-122.4069619 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57432960 || payment:credit_cards -> yes || payment:electronic_purses -> yes || last_edit_time -> 1521743607000 || last_edit_user_id -> 53073 || amenity -> bicycle_rental || last_edit_version -> 1 || network -> Ford GoBike\n5184349778000000 && 37.770809,-122.3993486 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || natural -> tree || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4880860064000000 && 37.7699957,-122.4070864 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924468000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 1 || crossing -> zebra\n3562892897000000 && 37.769516,-122.404299 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31627405 || website -> http://www.sfdesigncenter.com/ || shop -> furniture || last_edit_time -> 1433145794000 || last_edit_user_id -> 33757 || name -> San Francisco Design Center || short_name -> SFDC || last_edit_version -> 1\n708463538000000 && 37.770979,-122.3992503 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || barrier -> gate || last_edit_time -> 1523656387000 || last_edit_user_id -> 53073 || last_edit_version -> 5\n5184349833000000 && 37.7717956,-122.3983947 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783390000 || last_edit_user_id -> 53073 || highway -> crossing || last_edit_version -> 1\n65310195000000 && 37.7672005,-122.4057234 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 5\n767817443000000 && 37.7666122,-122.3948708 && last_edit_user_name -> clay_c || last_edit_changeset -> 64000226 || last_edit_time -> 1540879780000 || last_edit_user_id -> 119881 || usage -> main || railway -> level_crossing || last_edit_version -> 8\n5987119436000000 && 37.7694544,-122.4031244 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5987119457000000 && 37.7693817,-122.4036149 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5184349781000000 && 37.7707871,-122.3991571 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || natural -> tree || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n3532718887000000 && 37.7697728,-122.40414 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641018000 || last_edit_user_id -> 2237750 || highway -> crossing || last_edit_version -> 4 || crossing -> zebra\n1880206745000000 && 37.7707657,-122.4027645 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 55392380 || addr:housenumber -> 655 || last_edit_time -> 1515790759000 || last_edit_user_id -> 371121 || amenity -> cafe || name -> Patisserie Bonjour || addr:street -> Townsend Street || last_edit_version -> 4\n65358380000000 && 37.769371,-122.406398 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924469000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 6 || crossing -> zebra\n65328379000000 && 37.7673142,-122.403785 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 5\n5987119121000000 && 37.7694499,-122.4038834 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n65303726000000 && 37.7704904,-122.400019 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || stop -> minor || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || highway -> stop || last_edit_version -> 7\n5987119438000000 && 37.7690219,-122.4030822 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n4925013318000000 && 37.7708579,-122.4027036 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57645807 || access -> no || barrier -> gate || last_edit_time -> 1522356645000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n3577577294000000 && 37.7696757,-122.402331 && delivery -> yes || last_edit_changeset -> 56836576 || addr:state -> CA || amenity -> restaurant || addr:postcode -> 94103 || addr:city -> San Francisco || last_edit_user_name -> Aaron Lidman || addr:housenumber -> 11 || last_edit_time -> 1520040596000 || last_edit_user_id -> 53073 || smoking -> no || name -> Dumpling Time || addr:street -> Division Street || outdoor_seating -> yes || last_edit_version -> 4\n3532718885000000 && 37.7717718,-122.4017594 && last_edit_user_name -> oba510 || last_edit_changeset -> 39324426 || last_edit_time -> 1463290426000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5184349771000000 && 37.7703635,-122.3989802 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 58077286 || barrier -> gate || last_edit_time -> 1523656388000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n3563920172000000 && 37.7710733,-122.4029823 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31640735 || website -> http://www.schawk.com/ || last_edit_time -> 1433177115000 || last_edit_user_id -> 33757 || name -> Schawk! || office -> company || last_edit_version -> 1\n5987119128000000 && 37.7697417,-122.4034016 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119451000000 && 37.7696534,-122.4038654 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n1880209756000000 && 37.7699814,-122.4044817 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31737345 || website -> http://www.mieleusa.com/usa/design-centers/design-centers.asp?id=4 || shop -> furniture || old_name -> Kreis || last_edit_time -> 1433485928000 || last_edit_user_id -> 33757 || name -> Miele Center || last_edit_version -> 3\n5987119089000000 && 37.768642,-122.4030951 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641009000 || last_edit_user_id -> 2237750 || highway -> stop || last_edit_version -> 1 || direction -> backward\n5184349782000000 && 37.7707415,-122.3991571 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || natural -> tree || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5987119399000000 && 37.7687886,-122.4038202 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641011000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987138391000000 && 37.7689658,-122.4032327 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557427 || natural -> tree || last_edit_time -> 1539641730000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n984720713000000 && 37.7695543,-122.3985416 && last_edit_user_name -> stevea || last_edit_changeset -> 27413049 || last_edit_time -> 1418351083000 || last_edit_user_id -> 123633 || railway -> level_crossing || last_edit_version -> 5\n666571226000000 && 37.77573,-122.3933992 && last_edit_user_name -> StellanL || last_edit_changeset -> 38624789 || last_edit_time -> 1460817832000 || last_edit_user_id -> 28775 || highway -> traffic_signals || last_edit_version -> 4\n5987119425000000 && 37.7686957,-122.4034775 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3553171994000000 && 37.7700749,-122.4035915 && last_edit_user_name -> wheelmap_visitor || toilets:wheelchair -> yes || wheelchair -> yes || last_edit_changeset -> 41518910 || website -> http://www.roche-bobois.com/ || shop -> furniture || last_edit_time -> 1471465258000 || last_edit_user_id -> 290680 || name -> Roche Bobois || last_edit_version -> 4\n2548141867000000 && 37.7713038,-122.4054511 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31336567 || last_edit_time -> 1432195454000 || last_edit_user_id -> 33757 || highway -> traffic_signals || last_edit_version -> 3\n1344032342000000 && 37.7695848,-122.3985037 && last_edit_user_name -> stevea || last_edit_changeset -> 27413049 || last_edit_time -> 1418351083000 || last_edit_user_id -> 123633 || railway -> level_crossing || last_edit_version -> 2\n2922128564000000 && 37.7665023,-122.3947384 && last_edit_user_name -> clay_c || last_edit_changeset -> 64000226 || last_edit_time -> 1540879783000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 3\n5184349777000000 && 37.7708391,-122.399276 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || natural -> tree || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184864687000000 && 37.769458,-122.4032877 && last_edit_user_name -> chachafish || last_edit_changeset -> 63553269 || last_edit_time -> 1539630504000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 2\n5987119445000000 && 37.7691321,-122.4038687 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n6024177036000000 && 37.768771,-122.3975512 && last_edit_user_name -> clay_c || last_edit_changeset -> 64000226 || last_edit_time -> 1540879765000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 1\n5184540129000000 && 37.7701735,-122.398684 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53188507 || natural -> tree || last_edit_time -> 1508786397000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5987119122000000 && 37.7695011,-122.4038891 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3553171993000000 && 37.7704712,-122.4031218 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31640735 || shop -> furniture || last_edit_time -> 1433177115000 || last_edit_user_id -> 33757 || name -> California Closets || last_edit_version -> 2\n5987119109000000 && 37.7691157,-122.4034955 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119432000000 && 37.7697586,-122.4035938 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5987119464000000 && 37.7693737,-122.4032323 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n2960272791000000 && 37.7708144,-122.4062846 && last_edit_user_name -> dbaron || last_edit_changeset -> 31950831 || addr:housenumber -> 934 || last_edit_time -> 1434228583000 || last_edit_user_id -> 481533 || addr:country -> US || addr:street -> Brannan Street || last_edit_version -> 3 || addr:postcode -> 94103 || addr:city -> San Francisco\n65320179000000 && 37.7661396,-122.4017216 && last_edit_user_name -> Nathan K || last_edit_changeset -> 18541703 || last_edit_time -> 1382729483000 || last_edit_user_id -> 732991 || highway -> traffic_signals || last_edit_version -> 6\n3577590195000000 && 37.7702648,-122.4021666 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || addr:housenumber -> 680 || last_edit_time -> 1433631821000 || last_edit_user_id -> 371121 || addr:state -> CA || addr:street -> King Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n1880206851000000 && 37.7707211,-122.4028316 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 55392380 || addr:housenumber -> 659 || last_edit_time -> 1515790759000 || last_edit_user_id -> 371121 || amenity -> fast_food || name -> Holy Grill || cuisine -> burger || addr:street -> Townsend Street || last_edit_version -> 5\n539993905000000 && 37.7693733,-122.4060831 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 49034504 || last_edit_time -> 1495924469000 || last_edit_user_id -> 33757 || highway -> crossing || last_edit_version -> 4 || crossing -> zebra\n5184349783000000 && 37.771683,-122.3985411 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || highway -> crossing || last_edit_version -> 1\n2156214857000000 && 37.7697585,-122.4032336 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 32220993 || shelter -> yes || last_edit_time -> 1435308312000 || last_edit_user_id -> 33757 || highway -> bus_stop || last_edit_version -> 2 || network -> Muni\n5987119135000000 && 37.7692011,-122.4031063 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n295222216000000 && 37.7643851,-122.4048876 && last_edit_user_name -> saikabhi || last_edit_changeset -> 50401838 || ref -> 433C;433B || ref:left -> 433C || last_edit_time -> 1500461881000 || last_edit_user_id -> 3029661 || ref:right -> 433B || source -> http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf || highway -> motorway_junction || last_edit_version -> 12\n5449464496000000 && 37.7687364,-122.4035185 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || last_edit_time -> 1539646682000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 3\n5987119114000000 && 37.7689281,-122.4034777 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n3577577295000000 && 37.7694628,-122.4022939 && last_edit_user_name -> AndrewSnow || last_edit_changeset -> 31778944 || addr:housenumber -> 50 || last_edit_time -> 1433631820000 || last_edit_user_id -> 371121 || addr:state -> CA || craft -> furniture || name -> CustomFurnitureDesign. com || addr:street -> De Haro Street || last_edit_version -> 2 || addr:postcode -> 94103 || addr:city -> San Francisco\n5184864686000000 && 37.7693307,-122.4036894 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || last_edit_time -> 1539646683000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 3\n733619113000000 && 37.7690681,-122.4092372 && noref -> yes || last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 63022562 || last_edit_time -> 1538167768000 || last_edit_user_id -> 33757 || source -> http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf || highway -> motorway_junction || last_edit_version -> 8\n65303732000000 && 37.7693828,-122.3986477 && last_edit_user_name -> nikh1ll || last_edit_changeset -> 51232075 || last_edit_time -> 1503058696000 || last_edit_user_id -> 2835928 || highway -> traffic_signals || last_edit_version -> 8\n5515207393000000 && 37.7689486,-122.4027466 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57646984 || last_edit_time -> 1522359792000 || last_edit_user_id -> 53073 || name -> Jawbone Health || office -> company || last_edit_version -> 1\n3532718884000000 && 37.7717342,-122.4015592 && last_edit_user_name -> oba510 || last_edit_changeset -> 39324426 || last_edit_time -> 1463290426000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5987137901000000 && 37.7690212,-122.4034821 && last_edit_user_name -> chachafish || last_edit_changeset -> 63558461 || last_edit_time -> 1539646682000 || last_edit_user_id -> 2237750 || entrance -> yes || last_edit_version -> 2\n5987119134000000 && 37.7693132,-122.4031177 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n6024177069000000 && 37.7671688,-122.3955319 && last_edit_user_name -> clay_c || last_edit_changeset -> 64000226 || last_edit_time -> 1540879766000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 1\n5987119465000000 && 37.7694395,-122.403239 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641013000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5184837261000000 && 37.7709151,-122.3990729 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53193135 || access -> private || barrier -> gate || last_edit_time -> 1508798738000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5438253561000000 && 37.7699822,-122.3995842 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57545421 || access -> no || barrier -> gate || last_edit_time -> 1522090885000 || last_edit_user_id -> 53073 || last_edit_version -> 2\n5987119439000000 && 37.7687674,-122.4030614 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n6024177037000000 && 37.7692733,-122.3982538 && last_edit_user_name -> clay_c || last_edit_changeset -> 64000226 || last_edit_time -> 1540879765000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 1\n3563920173000000 && 37.7704182,-122.4032049 && last_edit_user_name -> Minh Nguyen || last_edit_changeset -> 31681487 || shop -> beauty || last_edit_time -> 1433297175000 || last_edit_user_id -> 33757 || name -> Mademoiselle Nails || last_edit_version -> 2\n5184540128000000 && 37.770415,-122.3987944 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53188507 || natural -> tree || last_edit_time -> 1508786397000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5987119444000000 && 37.7689413,-122.4038452 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || last_edit_time -> 1539641012000 || last_edit_user_id -> 2237750 || highway -> street_lamp || last_edit_version -> 1\n5184349776000000 && 37.7707203,-122.399288 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53186943 || last_edit_time -> 1508783389000 || last_edit_user_id -> 53073 || last_edit_version -> 1 || leisure -> picnic_table\n5987119108000000 && 37.7690876,-122.4035022 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n5987119129000000 && 37.7697439,-122.403347 && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || natural -> tree || last_edit_time -> 1539641010000 || last_edit_user_id -> 2237750 || last_edit_version -> 1\n# Relations\n71162000000 && 119237698000000 -> forward -> E && symbol -> https://upload.wikimedia.org/wikipedia/commons/5/5b/US_101_(CA).svg || last_edit_changeset -> 63022562 || is_in:state -> CA || type -> route || network -> US:US || last_edit_user_name -> Minh Nguyen || ref -> 101 || route -> road || last_edit_time -> 1538167769000 || last_edit_user_id -> 33757 || NHS -> STRAHNET || name -> US 101 (CA southbound, south of Hopland) || wikipedia -> en:U.S. Route 101 || last_edit_version -> 652 || wikidata -> Q410892 || direction -> south\n108619000000 && 8915288000000 -> forward -> E || 27167768000000 -> forward -> E && symbol -> https://upload.wikimedia.org/wikipedia/commons/5/5b/US_101_(CA).svg || last_edit_changeset -> 62027969 || is_in:state -> CA || type -> route || network -> US:US || last_edit_user_name -> njtbusfan || ref -> 101 || route -> road || last_edit_time -> 1535351731000 || last_edit_user_id -> 8107451 || NHS -> STRAHNET || name -> US 101 (CA northbound, south of Hopland) || wikipedia -> en:U.S. Route 101 || last_edit_version -> 510 || wikidata -> Q410892 || direction -> north\n1539483000000 && -620728093000000 ->  -> E || -224373746000002 ->  -> E || -224373746000001 ->  -> E || -221434102000004 ->  -> E || -221434102000003 ->  -> E || -221434102000002 ->  -> E || -221434102000001 ->  -> E || -84823308000004 ->  -> E || -84823308000003 ->  -> E || -84823308000002 ->  -> E || -84823308000001 ->  -> E || 84823308000001 ->  -> E || 84823308000002 ->  -> E || 84823308000003 ->  -> E || 84823308000004 ->  -> E || 221434099000001 -> forward -> E || 221434099000002 -> forward -> E || 221434100000001 -> forward -> E || 221434100000002 -> forward -> E || 221434102000001 ->  -> E || 221434102000002 ->  -> E || 221434102000003 ->  -> E || 221434102000004 ->  -> E || 221434104000001 -> forward -> E || 221434104000002 -> forward -> E || 221434104000003 -> forward -> E || 221434104000004 -> forward -> E || 221434104000005 -> forward -> E || 221434104000006 -> forward -> E || 224373746000001 ->  -> E || 224373746000002 ->  -> E || 620728093000000 ->  -> E && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || ref -> 36 || route -> bicycle || cycle_network -> US:CA:SF || last_edit_time -> 1539641020000 || last_edit_user_id -> 2237750 || type -> route || last_edit_version -> 21 || network -> lcn\n1797343000000 && -634444999000000 ->  -> E || -634412037000000 ->  -> E || -8919954000000 ->  -> E || 8919954000000 ->  -> E || 634412037000000 ->  -> E || 634444999000000 ->  -> E && last_edit_user_name -> chachafish || last_edit_changeset -> 63559070 || ref -> 123 || route -> bicycle || cycle_network -> US:CA:SF || last_edit_time -> 1539650461000 || last_edit_user_id -> 2237750 || type -> route || last_edit_version -> 3 || network -> lcn\n1797344000000 && -516061750000003 -> forward -> E || -516061750000002 -> forward -> E || -516061750000001 -> forward -> E || -513699413000002 ->  -> E || -513699413000001 ->  -> E || -417392970000002 ->  -> E || -417392970000001 ->  -> E || -417392966000000 ->  -> E || -417392962000000 ->  -> E || -417392953000000 ->  -> E || -84823308000004 -> forward -> E || -84823308000003 -> forward -> E || -84823308000002 -> forward -> E || -84823308000001 -> forward -> E || -28518571000003 ->  -> E || -28518571000002 ->  -> E || -28518571000001 ->  -> E || -27167281000002 -> forward -> E || -27167281000001 -> forward -> E || 27167281000001 -> forward -> E || 27167281000002 -> forward -> E || 28518571000001 ->  -> E || 28518571000002 ->  -> E || 28518571000003 ->  -> E || 84823308000001 -> forward -> E || 84823308000002 -> forward -> E || 84823308000003 -> forward -> E || 84823308000004 -> forward -> E || 221434104000001 -> forward -> E || 221434104000002 -> forward -> E || 221434104000003 -> forward -> E || 221434104000004 -> forward -> E || 221434104000005 -> forward -> E || 221434104000006 -> forward -> E || 417392953000000 ->  -> E || 417392962000000 ->  -> E || 417392966000000 ->  -> E || 417392970000001 ->  -> E || 417392970000002 ->  -> E || 513699413000001 ->  -> E || 513699413000002 ->  -> E || 516061750000001 -> forward -> E || 516061750000002 -> forward -> E || 516061750000003 -> forward -> E && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || ref -> 23 || route -> bicycle || cycle_network -> US:CA:SF || last_edit_time -> 1539641021000 || last_edit_user_id -> 2237750 || type -> route || last_edit_version -> 35 || network -> lcn\n1889266000000 && -26943758000004 -> street -> E || -26943758000003 -> street -> E || -26943758000002 -> street -> E || -26943758000001 -> street -> E || 26943758000001 -> street -> E || 26943758000002 -> street -> E || 26943758000003 -> street -> E || 26943758000004 -> street -> E && last_edit_user_name -> nereocystis || last_edit_changeset -> 54244365 || last_edit_time -> 1512150882000 || last_edit_user_id -> 1835512 || name -> Brannan Street || type -> associatedStreet || last_edit_version -> 14\n2320405000000 && 31129765000000 -> forward -> E || 154966358000000 -> forward -> E || 617895234000000 -> forward -> E && last_edit_user_name -> clay_c || note -> STRAHNET route: signed as I-80 but not officially an Interstate || last_edit_changeset -> 61782308 || route -> road || last_edit_time -> 1534636299000 || last_edit_user_id -> 119881 || NHS -> STRAHNET || type -> route || last_edit_version -> 10\n2716239000000 && 8915263000000 ->  -> E || 27167756000000 ->  -> E && last_edit_changeset -> 62000684 || type -> route || operator -> Alameda-Contra Costa Transit District || network -> AC Transit || via -> Oakland || last_edit_user_name -> njtbusfan || ref -> 800 || route -> bus || last_edit_time -> 1535269999000 || last_edit_user_id -> 8107451 || name -> AC Transit 800 (to San Francisco) || from -> Richmond BART || to -> San Francisco || last_edit_version -> 134\n2768040000000 && 8920472000000 -> forward -> L || 110353426000000 -> forward -> L || 119709582000000 -> backward -> L || 639288274000000 -> backward -> L || 639288278000000 -> forward -> L && last_edit_changeset -> 64205910 || operator:wikidata -> Q166817 || operator:wikipedia -> en:Caltrain || type -> route || operator -> TransitAmerica Services || network -> Caltrain || last_edit_user_name -> clay_c || colour -> red || ref -> 305-386 || route -> train || public_transport:version -> 1 || passenger -> suburban || last_edit_time -> 1541440268000 || last_edit_user_id -> 119881 || name -> Baby Bullet || last_edit_version -> 46\n2768041000000 && 8920472000000 -> forward -> L || 110353426000000 -> forward -> L || 119709582000000 -> backward -> L || 639288274000000 -> backward -> L || 639288278000000 -> forward -> L && last_edit_changeset -> 64205910 || operator:wikidata -> Q166817 || operator:wikipedia -> en:Caltrain || type -> route || operator -> TransitAmerica Services || network -> Caltrain || last_edit_user_name -> clay_c || colour -> white || ref -> 100s || route -> train || public_transport:version -> 1 || passenger -> suburban || last_edit_time -> 1541440269000 || last_edit_user_id -> 119881 || name -> Local || last_edit_version -> 48\n2981333000000 && -218146840000000 ->  -> E || -84823308000004 ->  -> E || -84823308000003 ->  -> E || -84823308000002 ->  -> E || -84823308000001 ->  -> E || -8916338000003 ->  -> E || -8916338000002 ->  -> E || -8916338000001 ->  -> E || 8916338000001 ->  -> E || 8916338000002 ->  -> E || 8916338000003 ->  -> E || 84823308000001 ->  -> E || 84823308000002 ->  -> E || 84823308000003 ->  -> E || 84823308000004 ->  -> E || 133735125000001 ->  -> E || 133735125000002 ->  -> E || 218146840000000 ->  -> E || 221434104000001 ->  -> E || 221434104000002 ->  -> E || 221434104000003 ->  -> E || 221434104000004 ->  -> E || 221434104000005 ->  -> E || 221434104000006 ->  -> E && last_edit_changeset -> 63557245 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> chachafish || ref -> 10 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1539641022000 || last_edit_user_id -> 2237750 || name -> 10-Townsend: Inbound to Pacific Heights (normal weekday) || from -> 24th Street & Potrero Avenue || to -> Jackson St & Webster St || last_edit_version -> 39\n2981334000000 && -218146840000000 ->  -> E || -84823308000004 ->  -> E || -84823308000003 ->  -> E || -84823308000002 ->  -> E || -84823308000001 ->  -> E || -8916338000003 ->  -> E || -8916338000002 ->  -> E || -8916338000001 ->  -> E || 8916338000001 ->  -> E || 8916338000002 ->  -> E || 8916338000003 ->  -> E || 84823308000001 ->  -> E || 84823308000002 ->  -> E || 84823308000003 ->  -> E || 84823308000004 ->  -> E || 218146840000000 ->  -> E || 218146865000001 ->  -> E || 218146865000002 ->  -> E || 221434104000001 ->  -> E || 221434104000002 ->  -> E || 221434104000003 ->  -> E || 221434104000004 ->  -> E || 221434104000005 ->  -> E || 221434104000006 ->  -> E && last_edit_changeset -> 63557245 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> chachafish || ref -> 10 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1539641021000 || last_edit_user_id -> 2237750 || name -> 10-Townsend: Outbound to San Francisco General Hospital (normal weekday) || from -> Jackson St & Fillmore St || to -> 23rd St & Utah St || last_edit_version -> 44\n3001321000000 && -218146840000000 ->  -> E || -84823308000004 ->  -> E || -84823308000003 ->  -> E || -84823308000002 ->  -> E || -84823308000001 ->  -> E || -8916338000003 ->  -> E || -8916338000002 ->  -> E || -8916338000001 ->  -> E || 8916338000001 ->  -> E || 8916338000002 ->  -> E || 8916338000003 ->  -> E || 84823308000001 ->  -> E || 84823308000002 ->  -> E || 84823308000003 ->  -> E || 84823308000004 ->  -> E || 133735125000001 ->  -> E || 133735125000002 ->  -> E || 218146840000000 ->  -> E || 221434104000001 ->  -> E || 221434104000002 ->  -> E || 221434104000003 ->  -> E || 221434104000004 ->  -> E || 221434104000005 ->  -> E || 221434104000006 ->  -> E && last_edit_changeset -> 63557245 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> chachafish || ref -> 19 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1539641023000 || last_edit_user_id -> 2237750 || name -> 19-Polk: Inbound to Fisherman's Wharf || from -> Galvez Ave & Horne Ave || to -> Beach St & Polk St || last_edit_version -> 36\n3001322000000 && -516061750000003 ->  -> E || -516061750000002 ->  -> E || -516061750000001 ->  -> E || -218146840000000 ->  -> E || -27167281000002 ->  -> E || -27167281000001 ->  -> E || -8916338000003 ->  -> E || -8916338000002 ->  -> E || -8916338000001 ->  -> E || 8916338000001 ->  -> E || 8916338000002 ->  -> E || 8916338000003 ->  -> E || 27167281000001 ->  -> E || 27167281000002 ->  -> E || 218146840000000 ->  -> E || 218146865000001 ->  -> E || 218146865000002 ->  -> E || 221434104000001 ->  -> E || 221434104000002 ->  -> E || 221434104000003 ->  -> E || 221434104000004 ->  -> E || 221434104000005 ->  -> E || 221434104000006 ->  -> E || 516061750000001 ->  -> E || 516061750000002 ->  -> E || 516061750000003 ->  -> E && last_edit_changeset -> 63557245 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> chachafish || ref -> 19 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1539641021000 || last_edit_user_id -> 2237750 || name -> 19-Polk: Outbound to Hunters Point || from -> Beach St & Polk St || to -> Galvez Ave & Horne Ave || last_edit_version -> 40\n3341655000000 && 2548141867000000 -> via -> N || 2548141867000000 -> via -> P && last_edit_user_name -> andygol || last_edit_changeset -> 51710383 || last_edit_time -> 1504512582000 || last_edit_user_id -> 94578 || name -> Only left from left side of 8th at Brannan || restriction -> only_left_turn || type -> restriction || last_edit_version -> 6\n3341694000000 && 2548141867000000 -> via -> N || 2548141867000000 -> via -> P && last_edit_user_name -> andygol || last_edit_changeset -> 51162893 || last_edit_time -> 1502872595000 || last_edit_user_id -> 94578 || name -> no left from west half of 8th to Brannan || restriction -> no_left_turn || type -> restriction || last_edit_version -> 6\n3406515000000 && 31129765000000 ->  -> E || 617895234000000 ->  -> E && last_edit_changeset -> 62016715 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> njtbusfan || ref -> 8 || route -> bus || public_transport:version -> 2 || old_name -> 8X Bayshore Express || last_edit_time -> 1535319561000 || last_edit_user_id -> 8107451 || old_ref -> 8X || name -> 8-Bayshore: Inbound to Fisherman's Wharf || from -> Phelan Loop || to -> Kearny St & North Point St || last_edit_version -> 70\n3406516000000 && 154966358000000 ->  -> E && last_edit_changeset -> 61974510 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> njtbusfan || ref -> 8 || route -> bus || public_transport:version -> 2 || old_name -> 8X Bayshore Express || last_edit_time -> 1535158826000 || last_edit_user_id -> 8107451 || old_ref -> 8X || name -> 8-Bayshore: Outbound to City College || from -> Kearny St & North Point St || to -> Phelan Loop || last_edit_version -> 69\n3406710000000 && 154966358000000 ->  -> E && last_edit_changeset -> 61968343 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> njtbusfan || FIXME -> verify exit at Crescent (not 280 to Alemany) || ref -> 14X || route -> bus || public_transport:version -> 2 || last_edit_time -> 1535137718000 || last_edit_user_id -> 8107451 || name -> 14X-Mission Express: Outbound to Daly City || from -> Steuart St & Mission St || to -> Mission St & Flournoy St || last_edit_version -> 47\n3406808000000 && 31129765000000 ->  -> E || 617895234000000 ->  -> E && last_edit_changeset -> 62016715 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> njtbusfan || ref -> 8AX || route -> bus || public_transport:version -> 2 || last_edit_time -> 1535319562000 || last_edit_user_id -> 8107451 || name -> 8AX-Bayshore A Express: Inbound to North Beach || from -> Schwerin St & Geneva Ave || to -> Kearny St & Broadway St || last_edit_version -> 41\n3406809000000 && 154966358000000 ->  -> E && last_edit_changeset -> 61968343 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> njtbusfan || ref -> 8AX || route -> bus || public_transport:version -> 2 || last_edit_time -> 1535137719000 || last_edit_user_id -> 8107451 || name -> 8AX-Bayshore A Express: Outbound to Visitacion Valley || from -> Kearny St & Broadway St || to -> Schwerin St & Geneva Ave || last_edit_version -> 44\n3406811000000 && 154966358000000 ->  -> E && last_edit_changeset -> 61971818 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> njtbusfan || ref -> 8BX || route -> bus || public_transport:version -> 2 || last_edit_time -> 1535145597000 || last_edit_user_id -> 8107451 || name -> 8BX-Bayshore B Express: Outbound to City College || from -> Kearny St & North Point St || to -> Phelan Loop || last_edit_version -> 62\n3418923000000 && -620728093000000 ->  -> E || -221434102000004 ->  -> E || -221434102000003 ->  -> E || -221434102000002 ->  -> E || -221434102000001 ->  -> E || -84823308000004 ->  -> E || -84823308000003 ->  -> E || -84823308000002 ->  -> E || -84823308000001 ->  -> E || -8919269000004 ->  -> E || -8919269000003 ->  -> E || -8919269000002 ->  -> E || -8919269000001 ->  -> E || 8919269000001 ->  -> E || 8919269000002 ->  -> E || 8919269000003 ->  -> E || 8919269000004 ->  -> E || 84823308000001 ->  -> E || 84823308000002 ->  -> E || 84823308000003 ->  -> E || 84823308000004 ->  -> E || 162931810000001 ->  -> E || 162931810000002 ->  -> E || 221434100000001 ->  -> E || 221434100000002 ->  -> E || 221434102000001 ->  -> E || 221434102000002 ->  -> E || 221434102000003 ->  -> E || 221434102000004 ->  -> E || 221434104000001 ->  -> E || 221434104000002 ->  -> E || 221434104000003 ->  -> E || 221434104000004 ->  -> E || 221434104000005 ->  -> E || 221434104000006 ->  -> E || 620728093000000 ->  -> E && last_edit_changeset -> 63557245 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> chachafish || ref -> 83X || route -> bus || public_transport:version -> 2 || last_edit_time -> 1539641022000 || last_edit_user_id -> 2237750 || name -> 83X-Mid-Market Express: Inbound to Levi Plaza || from -> 4th St & Townsend St || to -> 9th St & Market St || last_edit_version -> 10\n3435871000000 && -8917337000002 ->  -> E || -8917337000001 ->  -> E || 8917337000001 ->  -> E || 8917337000002 ->  -> E || 162931810000001 ->  -> E || 162931810000002 ->  -> E && last_edit_changeset -> 64171818 || type -> route || operator -> San Mateo County Transit District || network -> SamTrans || last_edit_user_name -> Telecas || ref -> 292 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1541350853000 || last_edit_user_id -> 202256 || name -> SamTrans 292 (northbound) || from -> Hillsdale Shopping Center || to -> Transbay Temporary Terminal || last_edit_version -> 144\n6088583000000 && 8920472000000 -> forward -> L || 110353426000000 -> forward -> L || 119709582000000 -> backward -> L || 639288274000000 -> backward -> L || 639288278000000 -> forward -> L && last_edit_changeset -> 64205910 || operator:wikidata -> Q166817 || operator:wikipedia -> en:Caltrain || type -> route || operator -> TransitAmerica Services || network -> Caltrain || last_edit_user_name -> clay_c || colour -> yellow || ref -> 206-289 || route -> train || public_transport:version -> 1 || passenger -> suburban || last_edit_time -> 1541440270000 || last_edit_user_id -> 119881 || name -> Limited || last_edit_version -> 20\n6435331000000 && -8919269000004 -> street -> E || -8919269000003 -> street -> E || -8919269000002 -> street -> E || -8919269000001 -> street -> E || 8919269000001 -> street -> E || 8919269000002 -> street -> E || 8919269000003 -> street -> E || 8919269000004 -> street -> E && last_edit_user_name -> njtbusfan || last_edit_changeset -> 61971818 || last_edit_time -> 1535145621000 || last_edit_user_id -> 8107451 || name -> 9th Street || type -> associatedStreet || last_edit_version -> 5\n6703053000000 && 8920472000000 ->  -> L || 110353426000000 ->  -> L || 119709582000000 ->  -> L || 639288274000000 ->  -> L || 639288278000000 ->  -> L && last_edit_user_name -> clay_c || last_edit_changeset -> 64205910 || route -> railway || last_edit_time -> 1541440271000 || last_edit_user_id -> 119881 || name -> Caltrain || type -> route || last_edit_version -> 15 || wikidata -> Q166817\n7461515000000 && 65303732000000 -> via -> N || 215528940000001 -> from -> E || 215528940000002 -> from -> E || 215528940000003 -> from -> E || 215528940000004 -> from -> E || 397250673000001 -> to -> E || 397250673000002 -> to -> E || 397250673000003 -> to -> E || 397250673000004 -> to -> E || 65303732000000 -> via -> P && last_edit_user_name -> ridixcr || last_edit_changeset -> 51015043 || last_edit_time -> 1502399149000 || last_edit_user_id -> 2508151 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n7739486000000 && 8920472000000 ->  -> L || 110353426000000 ->  -> L || 119709582000000 ->  -> L || 639288274000000 ->  -> L || 639288278000000 ->  -> L && last_edit_user_name -> clay_c || FIXME -> Needs to fully reflect all bridges, cuttings, embankments, etc. Needs to delete station members. || note -> Must be route:railway to describe the infrastructure. Including stations with role stop in this relation is incorrect, however we might temporarily allow it so that another route:train relation can use these elements at beginning of passenger service. || last_edit_changeset -> 64000226 || route -> railway || last_edit_time -> 1540879866000 || last_edit_user_id -> 119881 || name -> California High Speed Rail || type -> route || last_edit_version -> 22 || operator -> California High Speed Rail Authority\n7814535000000 && 547181915000000 -> outer -> A && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 57301995 || last_edit_time -> 1521410466000 || last_edit_user_id -> 53073 || type -> multipolygon || last_edit_version -> 2 || building -> yes\n7916804000000 && 8920472000000 ->  -> L || 110353426000000 ->  -> L || 119709582000000 ->  -> L || 119709592000000 ->  -> L || 639288274000000 ->  -> L || 639288278000000 ->  -> L && owner -> Peninsula Corridor Joint Powers Board || last_edit_user_name -> clay_c || last_edit_changeset -> 64186583 || route -> railway || last_edit_time -> 1541400223000 || last_edit_user_id -> 119881 || name -> Peninsula Subdivision || type -> route || last_edit_version -> 8 || operator -> Caltrain\n7940074000000 && 3532718854000000 ->  -> P || 3532718855000000 ->  -> P || 3532718856000000 ->  -> P || 3532718857000000 ->  -> P || 3532718858000000 ->  -> P || 3532718859000000 ->  -> P && last_edit_user_name -> oba510 || last_edit_changeset -> 55801562 || last_edit_time -> 1517055030000 || last_edit_user_id -> 933797 || name -> San Francisco 4th & King Street Station || public_transport -> stop_area || type -> public_transport || last_edit_version -> 1 || operator -> Caltrain || network -> Caltrain\n8135631000000 && 65303732000000 -> via -> N || -513699413000002 -> from -> E || -513699413000002 -> to -> E || -513699413000001 -> from -> E || -513699413000001 -> to -> E || 513699413000001 -> from -> E || 513699413000001 -> to -> E || 513699413000002 -> from -> E || 513699413000002 -> to -> E || 65303732000000 -> via -> P && last_edit_user_name -> upendrakarukonda || last_edit_changeset -> 57415811 || last_edit_time -> 1521712426000 || last_edit_user_id -> 4007051 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 1\n8588025000000 && 8915288000000 -> forward -> E || 26944068000000 -> forward -> E || 394691476000000 -> forward -> E || 394691481000000 -> forward -> E && last_edit_changeset -> 61938661 || type -> route || operator -> San Mateo County Transit District || network -> SamTrans || last_edit_user_name -> njtbusfan || FIXME -> verify route at San Bruno BART || ref -> 398 || route -> bus || public_transport:version -> 1 || last_edit_time -> 1535083053000 || last_edit_user_id -> 8107451 || old_ref -> KX || name -> SamTrans 398 || last_edit_version -> 4\n8588026000000 && -8917337000002 -> forward -> E || -8917337000001 -> forward -> E || 8917337000001 -> forward -> E || 8917337000002 -> forward -> E || 162931810000001 -> forward -> E || 162931810000002 -> forward -> E && last_edit_changeset -> 64545844 || type -> route || operator -> San Mateo County Transit District || network -> SamTrans || last_edit_user_name -> Vernon_Langley || FIXME -> does it somehow use Howard in SF? || ref -> 397 || route -> bus || public_transport:version -> 1 || last_edit_time -> 1542318739000 || last_edit_user_id -> 8899783 || name -> SamTrans 397 || last_edit_version -> 10\n8588115000000 && 65315504000000 -> via -> N || -26943758000004 -> from -> E || -26943758000003 -> from -> E || -26943758000002 -> from -> E || -26943758000001 -> from -> E || -8919269000004 -> to -> E || -8919269000003 -> to -> E || -8919269000002 -> to -> E || -8919269000001 -> to -> E || 8919269000001 -> to -> E || 8919269000002 -> to -> E || 8919269000003 -> to -> E || 8919269000004 -> to -> E || 26943758000001 -> from -> E || 26943758000002 -> from -> E || 26943758000003 -> from -> E || 26943758000004 -> from -> E || 65315504000000 -> via -> P && last_edit_user_name -> jho1 || last_edit_changeset -> 61930455 || last_edit_time -> 1535049830000 || last_edit_user_id -> 7231011 || restriction -> no_left_turn || type -> restriction || last_edit_version -> 1\n8592257000000 && -218146840000000 ->  -> E || -84823308000004 ->  -> E || -84823308000003 ->  -> E || -84823308000002 ->  -> E || -84823308000001 ->  -> E || -8916338000003 ->  -> E || -8916338000002 ->  -> E || -8916338000001 ->  -> E || 8916338000001 ->  -> E || 8916338000002 ->  -> E || 8916338000003 ->  -> E || 84823308000001 ->  -> E || 84823308000002 ->  -> E || 84823308000003 ->  -> E || 84823308000004 ->  -> E || 133735125000001 ->  -> E || 133735125000002 ->  -> E || 218146840000000 ->  -> E || 221434104000001 ->  -> E || 221434104000002 ->  -> E || 221434104000003 ->  -> E || 221434104000004 ->  -> E || 221434104000005 ->  -> E || 221434104000006 ->  -> E && last_edit_changeset -> 63557245 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> chachafish || ref -> 10 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1539641023000 || last_edit_user_id -> 2237750 || name -> 10-Townsend: Inbound to Pacific Heights (other times) || from -> 24th Street & Potrero Avenue || to -> Jackson St & Webster St || last_edit_version -> 7\n8592258000000 && -218146840000000 ->  -> E || -84823308000004 ->  -> E || -84823308000003 ->  -> E || -84823308000002 ->  -> E || -84823308000001 ->  -> E || -8916338000003 ->  -> E || -8916338000002 ->  -> E || -8916338000001 ->  -> E || 8916338000001 ->  -> E || 8916338000002 ->  -> E || 8916338000003 ->  -> E || 84823308000001 ->  -> E || 84823308000002 ->  -> E || 84823308000003 ->  -> E || 84823308000004 ->  -> E || 218146840000000 ->  -> E || 218146865000001 ->  -> E || 218146865000002 ->  -> E || 221434104000001 ->  -> E || 221434104000002 ->  -> E || 221434104000003 ->  -> E || 221434104000004 ->  -> E || 221434104000005 ->  -> E || 221434104000006 ->  -> E && last_edit_changeset -> 63557245 || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> chachafish || ref -> 10 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1539641022000 || last_edit_user_id -> 2237750 || name -> 10-Townsend: Outbound to San Francisco General Hospital (other times) || from -> Jackson St & Fillmore St || to -> 23rd St & Utah St || last_edit_version -> 6\n8610460000000 && -215524529000002 -> forward -> E || -215524529000001 -> forward -> E || -37177689000004 -> forward -> E || -37177689000003 -> forward -> E || -37177689000002 -> forward -> E || -37177689000001 -> forward -> E || 37177689000001 -> forward -> E || 37177689000002 -> forward -> E || 37177689000003 -> forward -> E || 37177689000004 -> forward -> E || 215524529000001 -> forward -> E || 215524529000002 -> forward -> E && last_edit_user_name -> njtbusfan || last_edit_changeset -> 62086023 || ref -> TransBay/Caltrain || route -> bus || public_transport:version -> 1 || last_edit_time -> 1535493051000 || last_edit_user_id -> 8107451 || name -> Mission Bay TransBay/Caltrain Route (PM) || type -> route || last_edit_version -> 1 || operator -> Mission Bay Transportation Management Association || network -> Mission Bay\n8610461000000 && -215524529000002 -> forward -> E || -215524529000001 -> forward -> E || -37177689000004 -> forward -> E || -37177689000003 -> forward -> E || -37177689000002 -> forward -> E || -37177689000001 -> forward -> E || 37177689000001 -> forward -> E || 37177689000002 -> forward -> E || 37177689000003 -> forward -> E || 37177689000004 -> forward -> E || 215524529000001 -> forward -> E || 215524529000002 -> forward -> E && last_edit_user_name -> bhalperin28 || last_edit_changeset -> 63499215 || ref -> TransBay/Caltrain || route -> bus || public_transport:version -> 1 || last_edit_time -> 1539491822000 || last_edit_user_id -> 3130496 || name -> Mission Bay TransBay/Caltrain Route (AM) || type -> route || last_edit_version -> 2 || operator -> Mission Bay Transportation Management Association || network -> Mission Bay\n8610462000000 && -513699413000002 -> forward -> E || -513699413000001 -> forward -> E || -417392970000002 -> forward -> E || -417392970000001 -> forward -> E || -417392966000000 -> forward -> E || -417392962000000 -> forward -> E || -417392953000000 -> forward -> E || -221434102000004 -> forward -> E || -221434102000003 -> forward -> E || -221434102000002 -> forward -> E || -221434102000001 -> forward -> E || -186398845000002 -> forward -> E || -186398845000001 -> forward -> E || -28518571000003 -> forward -> E || -28518571000002 -> forward -> E || -28518571000001 -> forward -> E || -8919269000004 -> forward -> E || -8919269000003 -> forward -> E || -8919269000002 -> forward -> E || -8919269000001 -> forward -> E || 8919269000001 -> forward -> E || 8919269000002 -> forward -> E || 8919269000003 -> forward -> E || 8919269000004 -> forward -> E || 28518571000001 -> forward -> E || 28518571000002 -> forward -> E || 28518571000003 -> forward -> E || 162931810000001 -> forward -> E || 162931810000002 -> forward -> E || 186398845000001 -> forward -> E || 186398845000002 -> forward -> E || 221434102000001 -> forward -> E || 221434102000002 -> forward -> E || 221434102000003 -> forward -> E || 221434102000004 -> forward -> E || 417392953000000 -> forward -> E || 417392962000000 -> forward -> E || 417392966000000 -> forward -> E || 417392970000001 -> forward -> E || 417392970000002 -> forward -> E || 513699413000001 -> forward -> E || 513699413000002 -> forward -> E && last_edit_user_name -> njtbusfan || last_edit_changeset -> 62086023 || ref -> South || route -> bus || public_transport:version -> 1 || last_edit_time -> 1535493052000 || last_edit_user_id -> 8107451 || name -> Mission Bay South Route || type -> route || last_edit_version -> 1 || operator -> Mission Bay Transportation Management Association || network -> Mission Bay\n8610463000000 && -513699413000002 -> backward -> E || -513699413000001 -> backward -> E || -417392970000002 ->  -> E || -417392970000001 ->  -> E || -417392966000000 ->  -> E || -417392962000000 ->  -> E || -417392953000000 ->  -> E || -215524529000002 -> forward -> E || -215524529000001 -> forward -> E || -37177689000004 -> forward -> E || -37177689000003 -> forward -> E || -37177689000002 -> forward -> E || -37177689000001 -> forward -> E || -28518571000003 ->  -> E || -28518571000002 ->  -> E || -28518571000001 ->  -> E || 28518571000001 ->  -> E || 28518571000002 ->  -> E || 28518571000003 ->  -> E || 37177689000001 -> forward -> E || 37177689000002 -> forward -> E || 37177689000003 -> forward -> E || 37177689000004 -> forward -> E || 215524529000001 -> forward -> E || 215524529000002 -> forward -> E || 397250673000001 -> forward -> E || 397250673000002 -> forward -> E || 397250673000003 -> forward -> E || 397250673000004 -> forward -> E || 417392953000000 ->  -> E || 417392962000000 ->  -> E || 417392966000000 ->  -> E || 417392970000001 ->  -> E || 417392970000002 ->  -> E || 513699413000001 -> backward -> E || 513699413000002 -> backward -> E && last_edit_user_name -> njtbusfan || last_edit_changeset -> 62086023 || ref -> West || route -> bus || public_transport:version -> 1 || last_edit_time -> 1535493053000 || last_edit_user_id -> 8107451 || name -> Mission Bay West Route || type -> route || last_edit_version -> 1 || operator -> Mission Bay Transportation Management Association || network -> Mission Bay\n8610464000000 && -516061750000003 -> forward -> E || -516061750000002 -> forward -> E || -516061750000001 -> forward -> E || -221434102000004 -> forward -> E || -221434102000003 -> forward -> E || -221434102000002 -> forward -> E || -221434102000001 -> forward -> E || -218146840000000 -> forward -> E || -186398845000002 -> forward -> E || -186398845000001 -> forward -> E || -27167281000002 -> forward -> E || -27167281000001 -> forward -> E || -8919269000004 -> forward -> E || -8919269000003 -> forward -> E || -8919269000002 -> forward -> E || -8919269000001 -> forward -> E || -8916338000003 -> forward -> E || -8916338000002 -> forward -> E || -8916338000001 -> forward -> E || 8916338000001 -> forward -> E || 8916338000002 -> forward -> E || 8916338000003 -> forward -> E || 8919269000001 -> forward -> E || 8919269000002 -> forward -> E || 8919269000003 -> forward -> E || 8919269000004 -> forward -> E || 27167281000001 -> forward -> E || 27167281000002 -> forward -> E || 162931810000001 -> forward -> E || 162931810000002 -> forward -> E || 186398845000001 -> forward -> E || 186398845000002 -> forward -> E || 218146840000000 -> forward -> E || 218146865000001 -> forward -> E || 218146865000002 -> forward -> E || 221434102000001 -> forward -> E || 221434102000002 -> forward -> E || 221434102000003 -> forward -> E || 221434102000004 -> forward -> E || 221434104000001 -> forward -> E || 221434104000002 -> forward -> E || 221434104000003 -> forward -> E || 221434104000004 -> forward -> E || 221434104000005 -> forward -> E || 221434104000006 -> forward -> E || 516061750000001 -> forward -> E || 516061750000002 -> forward -> E || 516061750000003 -> forward -> E && last_edit_user_name -> chachafish || last_edit_changeset -> 63557245 || ref -> CCA || route -> bus || public_transport:version -> 1 || last_edit_time -> 1539641022000 || last_edit_user_id -> 2237750 || name -> Mission Bay CCA Route || type -> route || last_edit_version -> 2 || operator -> Mission Bay Transportation Management Association || network -> Mission Bay\n8610465000000 && -634444999000000 -> forward -> E || -634412037000000 -> forward -> E || -620728093000000 -> forward -> E || -513699413000002 -> forward -> E || -513699413000001 -> forward -> E || -417392970000002 -> forward -> E || -417392970000001 -> forward -> E || -417392966000000 -> forward -> E || -417392962000000 -> forward -> E || -417392953000000 -> forward -> E || -221434102000004 -> forward -> E || -221434102000003 -> forward -> E || -221434102000002 -> forward -> E || -221434102000001 -> forward -> E || -28518571000003 -> forward -> E || -28518571000002 -> forward -> E || -28518571000001 -> forward -> E || -8919954000000 -> forward -> E || -8919269000004 -> forward -> E || -8919269000003 -> forward -> E || -8919269000002 -> forward -> E || -8919269000001 -> forward -> E || 8919269000001 -> forward -> E || 8919269000002 -> forward -> E || 8919269000003 -> forward -> E || 8919269000004 -> forward -> E || 8919954000000 -> forward -> E || 28518571000001 -> forward -> E || 28518571000002 -> forward -> E || 28518571000003 -> forward -> E || 162931810000001 -> forward -> E || 162931810000002 -> forward -> E || 221434100000001 -> forward -> E || 221434100000002 -> forward -> E || 221434102000001 -> forward -> E || 221434102000002 -> forward -> E || 221434102000003 -> forward -> E || 221434102000004 -> forward -> E || 221434104000001 -> forward -> E || 221434104000002 -> forward -> E || 221434104000003 -> forward -> E || 221434104000004 -> forward -> E || 221434104000005 -> forward -> E || 221434104000006 -> forward -> E || 417392953000000 -> forward -> E || 417392962000000 -> forward -> E || 417392966000000 -> forward -> E || 417392970000001 -> forward -> E || 417392970000002 -> forward -> E || 513699413000001 -> forward -> E || 513699413000002 -> forward -> E || 620728093000000 -> forward -> E || 634412037000000 -> forward -> E || 634444999000000 -> forward -> E && last_edit_user_name -> chachafish || last_edit_changeset -> 63559070 || ref -> East || route -> bus || public_transport:version -> 1 || last_edit_time -> 1539650461000 || last_edit_user_id -> 2237750 || name -> Mission Bay East Route || type -> route || last_edit_version -> 3 || operator -> Mission Bay Transportation Management Association || network -> Mission Bay\n8610466000000 && -513699413000002 ->  -> E || -513699413000001 ->  -> E || -417392970000002 ->  -> E || -417392970000001 ->  -> E || -417392966000000 ->  -> E || -417392962000000 ->  -> E || -417392953000000 ->  -> E || -28518571000003 ->  -> E || -28518571000002 ->  -> E || -28518571000001 ->  -> E || 28518571000001 ->  -> E || 28518571000002 ->  -> E || 28518571000003 ->  -> E || 417392953000000 ->  -> E || 417392962000000 ->  -> E || 417392966000000 ->  -> E || 417392970000001 ->  -> E || 417392970000002 ->  -> E || 513699413000001 ->  -> E || 513699413000002 ->  -> E && last_edit_user_name -> njtbusfan || last_edit_changeset -> 62086023 || ref -> Owens/Illinois || route -> bus || public_transport:version -> 1 || last_edit_time -> 1535493055000 || last_edit_user_id -> 8107451 || name -> Mission Bay Owens/Illinois Route || type -> route || last_edit_version -> 1 || operator -> Mission Bay Transportation Management Association || network -> Mission Bay\n8713570000000 && 205644329000000 -> outer -> A && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 62713402 || addr:housenumber -> 184 || last_edit_time -> 1537317501000 || last_edit_user_id -> 53073 || name -> Graduate Center || addr:street -> Hooper Street || type -> multipolygon || last_edit_version -> 1 || building -> university || operator -> California College of the Arts\n2981335000000 && 2981333000000 ->  -> R || 2981334000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 19726354 || ref -> 10 || route_master -> bus || last_edit_time -> 1388460449000 || last_edit_user_id -> 119881 || nextbus:route -> 10 || name -> 10-Townsend || nextbus:agency -> sf-muni || type -> route_master || last_edit_version -> 2 || operator -> San Francisco Municipal Railway\n3001323000000 && 3001321000000 ->  -> R || 3001322000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 19726354 || ref -> 19 || route_master -> bus || last_edit_time -> 1388460450000 || last_edit_user_id -> 119881 || nextbus:route -> 19 || name -> 19-Polk || nextbus:agency -> sf-muni || type -> route_master || last_edit_version -> 2 || operator -> San Francisco Municipal Railway\n3406517000000 && 3406515000000 ->  -> R || 3406516000000 ->  -> R && last_edit_user_name -> Martin Atkins || last_edit_changeset -> 31835136 || ref -> 8 || route_master -> bus || last_edit_time -> 1433831958000 || last_edit_user_id -> 6585 || nextbus:route -> 8 || name -> 8-Bayshore || nextbus:agency -> sf-muni || type -> route_master || last_edit_version -> 3\n3406812000000 && 3406808000000 ->  -> R || 3406809000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 19815429 || ref -> 8AX || route_master -> bus || last_edit_time -> 1388878866000 || last_edit_user_id -> 119881 || nextbus:route -> 8AX || name -> 8AX-Bayshore A Express || nextbus:agency -> sf-muni || type -> route_master || last_edit_version -> 2\n443197000000 && 71162000000 -> south -> R || 108619000000 -> north -> R && symbol -> https://upload.wikimedia.org/wikipedia/commons/7/79/US_101.svg || last_edit_changeset -> 62741327 || is_in:state -> CA;OR;WA || type -> route || network -> US:US || last_edit_user_name -> 5XWarrior || ref -> 101 || route -> road || last_edit_time -> 1537384791000 || last_edit_user_id -> 8724499 || name -> US 101 (super) || wikipedia -> en:U.S. Route 101 || last_edit_version -> 10 || wikidata -> Q410892\n2716240000000 && 2716239000000 ->  -> R && last_edit_user_name -> dchiles || last_edit_changeset -> 19487292 || ref -> 800 || route_master -> bus || last_edit_time -> 1387221693000 || last_edit_user_id -> 153669 || name -> Line 800 || type -> route_master || last_edit_version -> 2 || operator -> AC Transit\n3406711000000 && 3406710000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 19726354 || ref -> 14X || route_master -> bus || last_edit_time -> 1388460449000 || last_edit_user_id -> 119881 || nextbus:route -> 14X || name -> 14X-Mission Express || nextbus:agency -> sf-muni || type -> route_master || last_edit_version -> 2 || operator -> San Francisco Municipal Railway\n3406813000000 && 3406811000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 19815429 || ref -> 8BX || route_master -> bus || last_edit_time -> 1388878866000 || last_edit_user_id -> 119881 || nextbus:route -> 8BX || name -> 8BX-Bayshore B Express || nextbus:agency -> sf-muni || type -> route_master || last_edit_version -> 2\n3418929000000 && 3418923000000 ->  -> R && last_edit_user_name -> dpaschich || last_edit_changeset -> 52688409 || ref -> 83X || route_master -> bus || last_edit_time -> 1507308522000 || last_edit_user_id -> 621202 || nextbus:route -> 83X || name -> 83X-Mid-Market Express || nextbus:agency -> sf-muni || type -> route_master || last_edit_version -> 3\n3435880000000 && 3435871000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 20006303 || ref -> 292 || route_master -> bus || last_edit_time -> 1389757451000 || last_edit_user_id -> 119881 || name -> SamTrans 292 || type -> route_master || last_edit_version -> 2 || operator -> San Mateo County Transportation District\n7940075000000 && 7940074000000 ->  -> R && last_edit_user_name -> oba510 || last_edit_changeset -> 55801562 || last_edit_time -> 1517055031000 || last_edit_user_id -> 933797 || name -> Fourth & King Street Stations || public_transport -> stop_area_group || type -> public_transport || last_edit_version -> 1\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/MultipleChangeAtlasTest.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <bounds minlat='37.7683231' minlon='-122.4071145' maxlat='37.7710879' maxlon='-122.3985314' origin='CGImap 0.6.1 (13160 thorn-03.openstreetmap.org)' />\n  <node id='30676466' timestamp='2018-10-30T06:09:39Z' uid='119881' user='clay_c' version='14' changeset='64000226' lat='37.7725605' lon='-122.3997929' />\n  <node id='30676467' timestamp='2011-06-30T01:59:54Z' uid='169004' user='oldtopos' version='13' changeset='8586501' lat='37.7720735' lon='-122.4001743' />\n  <node id='30676468' timestamp='2011-06-30T01:59:49Z' uid='169004' user='oldtopos' version='13' changeset='8586501' lat='37.7716766' lon='-122.4002893' />\n  <node id='30676469' timestamp='2011-06-30T01:59:53Z' uid='169004' user='oldtopos' version='13' changeset='8586501' lat='37.7712686' lon='-122.4002573' />\n  <node id='31789507' timestamp='2013-09-08T00:09:25Z' uid='14293' user='KindredCoda' version='8' changeset='17725564' lat='37.777114' lon='-122.3902912' />\n  <node id='31794393' timestamp='2012-07-18T17:50:44Z' uid='722137' user='OSMF Redaction Account' version='5' changeset='12291161' lat='37.7754234' lon='-122.3849196' />\n  <node id='31821521' timestamp='2012-07-18T17:50:45Z' uid='722137' user='OSMF Redaction Account' version='5' changeset='12291161' lat='37.7814384' lon='-122.3876944' />\n  <node id='31823084' timestamp='2012-07-18T17:50:44Z' uid='722137' user='OSMF Redaction Account' version='7' changeset='12291161' lat='37.7733522' lon='-122.3846464' />\n  <node id='31826027' timestamp='2018-02-05T10:11:52Z' uid='933797' user='oba510' version='6' changeset='56080356' lat='37.7708742' lon='-122.3859728' />\n  <node id='31828120' timestamp='2012-07-18T17:50:44Z' uid='722137' user='OSMF Redaction Account' version='7' changeset='12291161' lat='37.7732761' lon='-122.3867755' />\n  <node id='31832893' timestamp='2018-03-30T03:23:33Z' uid='53073' user='Aaron Lidman' version='7' changeset='57650677' lat='37.7743538' lon='-122.3869477' />\n  <node id='31837618' timestamp='2012-07-18T17:50:44Z' uid='722137' user='OSMF Redaction Account' version='4' changeset='12291161' lat='37.7759474' lon='-122.3905758' />\n  <node id='31839614' timestamp='2017-02-16T15:40:33Z' uid='4518035' user='BenoitMenez' version='6' changeset='46140328' lat='37.7782892' lon='-122.3874511' />\n  <node id='31840458' timestamp='2012-07-18T17:50:45Z' uid='722137' user='OSMF Redaction Account' version='5' changeset='12291161' lat='37.781574' lon='-122.3855023' />\n  <node id='31844169' timestamp='2012-07-18T17:50:43Z' uid='722137' user='OSMF Redaction Account' version='5' changeset='12291161' lat='37.7706551' lon='-122.3855754' />\n  <node id='31846028' timestamp='2013-09-08T00:09:25Z' uid='14293' user='KindredCoda' version='7' changeset='17725564' lat='37.7768563' lon='-122.3904928' />\n  <node id='31847860' timestamp='2018-05-24T18:55:36Z' uid='7134350' user='KaL fvr' version='6' changeset='59250365' lat='37.7746281' lon='-122.381849' />\n  <node id='31850605' timestamp='2012-07-18T17:50:44Z' uid='722137' user='OSMF Redaction Account' version='6' changeset='12291161' lat='37.7719585' lon='-122.3814808' />\n  <node id='31857224' timestamp='2018-02-05T10:11:52Z' uid='933797' user='oba510' version='6' changeset='56080356' lat='37.7706527' lon='-122.3857361' />\n  <node id='31858878' timestamp='2012-07-18T17:50:44Z' uid='722137' user='OSMF Redaction Account' version='7' changeset='12291161' lat='37.7761169' lon='-122.3871096' />\n  <node id='31860538' timestamp='2012-07-18T17:50:43Z' uid='722137' user='OSMF Redaction Account' version='9' changeset='12291161' lat='37.7705013' lon='-122.398671' />\n  <node id='31861668' timestamp='2012-07-18T17:50:44Z' uid='722137' user='OSMF Redaction Account' version='7' changeset='12291161' lat='37.7750788' lon='-122.3870139' />\n  <node id='31862513' timestamp='2014-01-07T10:40:59Z' uid='28775' user='StellanL' version='5' changeset='19860253' lat='37.771757' lon='-122.3864701' />\n  <node id='31864303' timestamp='2012-07-18T17:50:44Z' uid='722137' user='OSMF Redaction Account' version='5' changeset='12291161' lat='37.7762619' lon='-122.3901812' />\n  <node id='65282185' timestamp='2018-03-26T19:01:25Z' uid='53073' user='Aaron Lidman' version='8' changeset='57545421' lat='37.768536' lon='-122.4048807'>\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='65283553' timestamp='2015-05-21T08:04:14Z' uid='33757' user='Minh Nguyen' version='5' changeset='31336567' lat='37.7696787' lon='-122.4057206' />\n  <node id='65283717' timestamp='2018-02-28T03:35:14Z' uid='4018842' user='Stephen214' version='16' changeset='56741589' lat='37.7690324' lon='-122.4069177'>\n    <tag k='highway' v='motorway_junction' />\n    <tag k='ref' v='433C' />\n    <tag k='ref:right' v='433C' />\n    <tag k='source' v='http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf' />\n  </node>\n  <node id='65283721' timestamp='2009-11-19T03:33:47Z' uid='147510' user='woodpeck_fixbot' version='5' changeset='3156706' lat='37.7701419' lon='-122.4083742' />\n  <node id='65283737' timestamp='2015-05-21T08:04:14Z' uid='33757' user='Minh Nguyen' version='9' changeset='31336567' lat='37.7713009' lon='-122.4085367'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='65283756' timestamp='2013-05-22T06:53:47Z' uid='14293' user='KindredCoda' version='8' changeset='16233758' lat='37.7689243' lon='-122.4082072' />\n  <node id='65283776' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='8' changeset='21087627' lat='37.7697226' lon='-122.4081672' />\n  <node id='65283778' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='7' changeset='21087627' lat='37.7693622' lon='-122.4076107' />\n  <node id='65283780' timestamp='2009-11-19T03:33:48Z' uid='147510' user='woodpeck_fixbot' version='3' changeset='3156706' lat='37.769271' lon='-122.407461' />\n  <node id='65283790' timestamp='2010-05-14T07:10:03Z' uid='14293' user='KindredCoda' version='8' changeset='4692759' lat='37.7688667' lon='-122.4065721' />\n  <node id='65283798' timestamp='2010-05-14T07:08:57Z' uid='14293' user='KindredCoda' version='8' changeset='4692759' lat='37.7689314' lon='-122.4061416' />\n  <node id='65283800' timestamp='2010-05-14T07:08:58Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7692303' lon='-122.4056347' />\n  <node id='65283804' timestamp='2010-05-14T07:09:47Z' uid='14293' user='KindredCoda' version='8' changeset='4692759' lat='37.7693968' lon='-122.4055126' />\n  <node id='65283808' timestamp='2010-05-14T07:08:58Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7697604' lon='-122.4054121' />\n  <node id='65283811' timestamp='2010-05-14T06:45:19Z' uid='14293' user='KindredCoda' version='12' changeset='4692674' lat='37.7705987' lon='-122.4055097' />\n  <node id='65283822' timestamp='2010-05-14T07:12:35Z' uid='14293' user='KindredCoda' version='6' changeset='4692759' lat='37.7700689' lon='-122.4058533' />\n  <node id='65283827' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='6' changeset='4692759' lat='37.76926' lon='-122.4060947' />\n  <node id='65283829' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7691858' lon='-122.4061899' />\n  <node id='65283831' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7689812' lon='-122.4067317' />\n  <node id='65283833' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='8' changeset='4692759' lat='37.7689579' lon='-122.4069825' />\n  <node id='65286490' timestamp='2010-05-14T07:16:12Z' uid='14293' user='KindredCoda' version='8' changeset='4692759' lat='37.7682836' lon='-122.4062811' />\n  <node id='65294602' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='6' changeset='63557245' lat='37.7686504' lon='-122.4029523' />\n  <node id='65294603' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='5' changeset='63557245' lat='37.767373' lon='-122.4028264'>\n    <tag k='highway' v='stop' />\n    <tag k='stop' v='all' />\n  </node>\n  <node id='65296271' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='6' changeset='31778944' lat='37.7698379' lon='-122.4030693' />\n  <node id='65303721' timestamp='2015-08-07T07:42:34Z' uid='3130496' user='bhalperin28' version='10' changeset='33172900' lat='37.7718329' lon='-122.4016818'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='65303723' timestamp='2018-03-26T19:01:26Z' uid='53073' user='Aaron Lidman' version='7' changeset='57545421' lat='37.7711307' lon='-122.4008049'>\n    <tag k='highway' v='stop' />\n    <tag k='stop' v='minor' />\n  </node>\n  <node id='65303726' timestamp='2018-03-26T19:01:25Z' uid='53073' user='Aaron Lidman' version='7' changeset='57545421' lat='37.7704904' lon='-122.400019'>\n    <tag k='highway' v='stop' />\n    <tag k='stop' v='minor' />\n  </node>\n  <node id='65303730' timestamp='2010-06-10T01:36:30Z' uid='14293' user='KindredCoda' version='4' changeset='4950654' lat='37.7699381' lon='-122.399339' />\n  <node id='65303732' timestamp='2017-08-18T12:18:16Z' uid='2835928' user='nikh1ll' version='8' changeset='51232075' lat='37.7693828' lon='-122.3986477'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='65303878' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='8' changeset='63557245' lat='37.7685958' lon='-122.4038927'>\n    <tag k='highway' v='stop' />\n    <tag k='stop' v='all' />\n  </node>\n  <node id='65303885' timestamp='2018-02-26T19:08:31Z' uid='5288591' user='Irina Karachevtseva' version='7' changeset='56699939' lat='37.7685143' lon='-122.4053147' />\n  <node id='65303886' timestamp='2012-03-17T03:33:36Z' uid='14293' user='KindredCoda' version='8' changeset='11004366' lat='37.7684788' lon='-122.4058441' />\n  <node id='65303894' timestamp='2012-03-17T03:33:36Z' uid='14293' user='KindredCoda' version='8' changeset='11004366' lat='37.7684205' lon='-122.406827' />\n  <node id='65303897' timestamp='2016-12-16T19:38:30Z' uid='3526564' user='yurasi' version='7' changeset='44455215' lat='37.7683619' lon='-122.4078137'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='65303899' timestamp='2017-03-29T11:44:06Z' uid='933797' user='oba510' version='7' changeset='47257631' lat='37.7683061' lon='-122.408752' />\n  <node id='65308756' timestamp='2017-03-29T11:44:07Z' uid='933797' user='oba510' version='8' changeset='47257631' lat='37.7692412' lon='-122.4079015'>\n    <tag k='highway' v='traffic_signals' />\n    <tag k='source' v='Yahoo' />\n  </node>\n  <node id='65310189' timestamp='2015-05-21T08:04:14Z' uid='33757' user='Minh Nguyen' version='4' changeset='31336567' lat='37.7696299' lon='-122.4059593' />\n  <node id='65310195' timestamp='2018-03-26T19:01:25Z' uid='53073' user='Aaron Lidman' version='5' changeset='57545421' lat='37.7672005' lon='-122.4057234'>\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='65313423' timestamp='2018-03-29T00:58:56Z' uid='53073' user='Aaron Lidman' version='5' changeset='57617363' lat='37.7696275' lon='-122.3989519' />\n  <node id='65313426' timestamp='2009-08-10T21:58:02Z' uid='147510' user='woodpeck_fixbot' version='4' changeset='2102778' lat='37.768339' lon='-122.4006035' />\n  <node id='65313427' timestamp='2009-08-10T21:58:02Z' uid='147510' user='woodpeck_fixbot' version='3' changeset='2102778' lat='37.7683051' lon='-122.4008267' />\n  <node id='65313429' timestamp='2009-08-10T21:58:02Z' uid='147510' user='woodpeck_fixbot' version='3' changeset='2102778' lat='37.7682169' lon='-122.4009125' />\n  <node id='65315504' timestamp='2015-05-21T08:04:14Z' uid='33757' user='Minh Nguyen' version='7' changeset='31336567' lat='37.77007' lon='-122.4069977'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='65315506' timestamp='2016-12-16T19:43:02Z' uid='3526564' user='yurasi' version='5' changeset='44455305' lat='37.7695306' lon='-122.4076423'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='65320166' timestamp='2009-08-10T21:59:24Z' uid='147510' user='woodpeck_fixbot' version='3' changeset='2102778' lat='37.769885' lon='-122.402073' />\n  <node id='65320168' timestamp='2009-08-10T21:59:24Z' uid='147510' user='woodpeck_fixbot' version='3' changeset='2102778' lat='37.7689632' lon='-122.4019854' />\n  <node id='65320171' timestamp='2018-03-26T19:01:25Z' uid='53073' user='Aaron Lidman' version='4' changeset='57545421' lat='37.767431' lon='-122.401843'>\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='65320179' timestamp='2013-10-25T19:31:23Z' uid='732991' user='Nathan K' version='6' changeset='18541703' lat='37.7661396' lon='-122.4017216'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='65325067' timestamp='2017-10-23T19:20:05Z' uid='53073' user='Aaron Lidman' version='12' changeset='53188507' lat='37.7740429' lon='-122.395587' />\n  <node id='65328379' timestamp='2018-03-26T19:01:25Z' uid='53073' user='Aaron Lidman' version='5' changeset='57545421' lat='37.7673142' lon='-122.403785'>\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='65339116' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='7' changeset='49034504' lat='37.7694529' lon='-122.4062261'>\n    <tag k='highway' v='stop' />\n    <tag k='source' v='Yahoo' />\n  </node>\n  <node id='65340073' timestamp='2013-05-15T06:56:06Z' uid='14293' user='KindredCoda' version='7' changeset='16134749' lat='37.7697218' lon='-122.4049953' />\n  <node id='65340076' timestamp='2018-03-26T19:01:25Z' uid='53073' user='Aaron Lidman' version='6' changeset='57545421' lat='37.7672565' lon='-122.4047579'>\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='65345893' timestamp='2009-08-10T22:07:15Z' uid='147510' user='woodpeck_fixbot' version='3' changeset='2102778' lat='37.769299' lon='-122.406882' />\n  <node id='65355339' timestamp='2011-10-30T07:15:15Z' uid='202256' user='Telecas' version='4' changeset='9690834' lat='37.7699961' lon='-122.4022345' />\n  <node id='65355343' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='5' changeset='31778944' lat='37.7698702' lon='-122.4023944' />\n  <node id='65356544' timestamp='2015-07-11T21:06:15Z' uid='118021' user='maggot27' version='13' changeset='32570723' lat='37.7694762' lon='-122.3984446'>\n    <tag k='railway' v='level_crossing' />\n    <tag k='usage' v='main' />\n  </node>\n  <node id='65356577' timestamp='2018-10-30T06:09:39Z' uid='119881' user='clay_c' version='5' changeset='64000226' lat='37.7644182' lon='-122.3930577' />\n  <node id='65358378' timestamp='2009-08-10T22:10:19Z' uid='147510' user='woodpeck_fixbot' version='3' changeset='2102778' lat='37.769307' lon='-122.406725' />\n  <node id='65358380' timestamp='2017-05-27T22:34:29Z' uid='33757' user='Minh Nguyen' version='6' changeset='49034504' lat='37.769371' lon='-122.406398'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='65371530' timestamp='2017-07-19T11:31:12Z' uid='3769434' user='manoharuss' version='12' changeset='50402608' lat='37.7717012' lon='-122.4064466'>\n    <tag k='highway' v='motorway_junction' />\n    <tag k='ref' v='1A;1B' />\n    <tag k='ref:left' v='1A' />\n    <tag k='ref:right' v='1B' />\n    <tag k='source' v='http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/80.pdf' />\n  </node>\n  <node id='65371540' timestamp='2010-05-14T06:41:40Z' uid='14293' user='KindredCoda' version='5' changeset='4692674' lat='37.7660475' lon='-122.4052442' />\n  <node id='65371930' timestamp='2010-05-10T20:20:19Z' uid='14293' user='KindredCoda' version='6' changeset='4663164' lat='37.765681' lon='-122.4048526' />\n  <node id='65371933' timestamp='2010-05-10T20:20:22Z' uid='14293' user='KindredCoda' version='7' changeset='4663164' lat='37.7698081' lon='-122.4052348' />\n  <node id='65371935' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='9' changeset='16233828' lat='37.7709255' lon='-122.4057197' />\n  <node id='65374259' timestamp='2010-05-14T07:13:41Z' uid='14293' user='KindredCoda' version='6' changeset='4692759' lat='37.7688529' lon='-122.4084711' />\n  <node id='65374260' timestamp='2010-05-14T07:13:42Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7687342' lon='-122.4074613' />\n  <node id='65374264' timestamp='2010-05-14T07:13:42Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7686346' lon='-122.4070536' />\n  <node id='65374267' timestamp='2010-05-14T07:13:43Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7685137' lon='-122.4067344' />\n  <node id='65374268' timestamp='2010-05-14T07:13:43Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7683759' lon='-122.4064447' />\n  <node id='65374269' timestamp='2010-05-14T07:13:43Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7681776' lon='-122.4061148' />\n  <node id='65374270' timestamp='2010-05-14T07:16:13Z' uid='14293' user='KindredCoda' version='5' changeset='4692759' lat='37.7678585' lon='-122.4057406' />\n  <node id='65374273' timestamp='2010-05-14T07:16:13Z' uid='14293' user='KindredCoda' version='5' changeset='4692759' lat='37.7676306' lon='-122.4055891' />\n  <node id='65374276' timestamp='2010-05-14T07:16:13Z' uid='14293' user='KindredCoda' version='5' changeset='4692759' lat='37.7674652' lon='-122.4055207' />\n  <node id='65374334' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='10' changeset='50914033' lat='37.7697239' lon='-122.4092051' />\n  <node id='65374337' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='9' changeset='50914033' lat='37.7698286' lon='-122.4090048' />\n  <node id='65374339' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='10' changeset='50914033' lat='37.769842' lon='-122.4082053' />\n  <node id='65374342' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='6' changeset='4692674' lat='37.7681434' lon='-122.4057605' />\n  <node id='65374344' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='5' changeset='4692674' lat='37.7677703' lon='-122.4053769' />\n  <node id='65374346' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='5' changeset='4692674' lat='37.7675233' lon='-122.4052428' />\n  <node id='65374349' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='7' changeset='4692674' lat='37.7671246' lon='-122.4051449' />\n  <node id='255649963' timestamp='2017-02-16T15:40:33Z' uid='4518035' user='BenoitMenez' version='31' changeset='46140328' lat='37.7783469' lon='-122.3874609' />\n  <node id='255897294' timestamp='2018-02-05T10:11:51Z' uid='933797' user='oba510' version='23' changeset='56080356' lat='37.7710202' lon='-122.3859833' />\n  <node id='255897295' timestamp='2011-04-26T01:06:12Z' uid='14293' user='KindredCoda' version='22' changeset='7970273' lat='37.7727843' lon='-122.3867367' />\n  <node id='255897296' timestamp='2011-04-26T01:06:12Z' uid='14293' user='KindredCoda' version='22' changeset='7970273' lat='37.7728259' lon='-122.3865806' />\n  <node id='257670318' timestamp='2010-05-14T07:08:57Z' uid='14293' user='KindredCoda' version='9' changeset='4692759' lat='37.7689006' lon='-122.4079186' />\n  <node id='257670327' timestamp='2014-03-13T19:03:52Z' uid='1679807' user='Cato_d_Ae' version='8' changeset='21087627' lat='37.768741' lon='-122.406751' />\n  <node id='258877153' timestamp='2010-06-10T01:37:30Z' uid='14293' user='KindredCoda' version='16' changeset='4950654' lat='37.7709091' lon='-122.3995214' />\n  <node id='262828279' timestamp='2011-06-30T01:59:48Z' uid='169004' user='oldtopos' version='11' changeset='8586501' lat='37.7708752' lon='-122.4000823' />\n  <node id='268235129' timestamp='2008-11-20T06:11:15Z' uid='35195' user='will l' version='3' changeset='773289' lat='37.7701577' lon='-122.3855948' />\n  <node id='268235131' timestamp='2014-01-07T10:40:59Z' uid='28775' user='StellanL' version='4' changeset='19860253' lat='37.7698747' lon='-122.3855501' />\n  <node id='268235132' timestamp='2014-01-07T10:40:59Z' uid='28775' user='StellanL' version='4' changeset='19860253' lat='37.7687659' lon='-122.3850346' />\n  <node id='268235134' timestamp='2008-11-20T06:11:15Z' uid='35195' user='will l' version='3' changeset='773289' lat='37.7679098' lon='-122.3853462' />\n  <node id='268235225' timestamp='2014-09-15T22:13:47Z' uid='28775' user='StellanL' version='2' changeset='25467657' lat='37.7697199' lon='-122.3831784' />\n  <node id='268235226' timestamp='2014-09-15T22:13:47Z' uid='28775' user='StellanL' version='2' changeset='25467657' lat='37.7693373' lon='-122.3834036' />\n  <node id='268235227' timestamp='2008-11-20T06:11:15Z' uid='35195' user='will l' version='3' changeset='773289' lat='37.7702142' lon='-122.3856126' />\n  <node id='276545628' timestamp='2014-06-19T13:49:17Z' uid='510836' user='Rub21' version='3' changeset='23024899' lat='37.7695724' lon='-122.4073956' />\n  <node id='276545629' timestamp='2014-06-19T13:49:17Z' uid='510836' user='Rub21' version='3' changeset='23024899' lat='37.769423' lon='-122.4071939' />\n  <node id='276545630' timestamp='2014-06-19T13:49:17Z' uid='510836' user='Rub21' version='3' changeset='23024899' lat='37.7694877' lon='-122.406455' />\n  <node id='276545631' timestamp='2014-06-19T13:49:17Z' uid='510836' user='Rub21' version='3' changeset='23024899' lat='37.7698979' lon='-122.4069777' />\n  <node id='276545980' timestamp='2014-01-16T20:32:53Z' uid='1240849' user='ediyes' version='4' changeset='20040540' lat='37.7686356' lon='-122.4047621' />\n  <node id='276545981' timestamp='2014-01-16T20:32:53Z' uid='1240849' user='ediyes' version='3' changeset='20040540' lat='37.7694373' lon='-122.4048332' />\n  <node id='276545982' timestamp='2014-01-16T20:32:53Z' uid='1240849' user='ediyes' version='3' changeset='20040540' lat='37.7696594' lon='-122.4043001' />\n  <node id='276545983' timestamp='2014-01-16T20:32:53Z' uid='1240849' user='ediyes' version='3' changeset='20040540' lat='37.7696662' lon='-122.4041628' />\n  <node id='276545984' timestamp='2014-01-16T20:32:53Z' uid='1240849' user='ediyes' version='3' changeset='20040540' lat='37.7686831' lon='-122.4040754' />\n  <node id='281652584' timestamp='2008-07-28T04:21:31Z' uid='35195' user='will l' version='1' changeset='611272' lat='37.7683433' lon='-122.4066637'>\n    <tag k='amenity' v='post_box' />\n    <tag k='created_by' v='JOSM' />\n  </node>\n  <node id='295219625' timestamp='2017-05-27T22:34:29Z' uid='33757' user='Minh Nguyen' version='6' changeset='49034504' lat='37.7693309' lon='-122.4062061'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='295219626' timestamp='2009-10-25T03:42:47Z' uid='28775' user='StellanL' version='3' changeset='2942982' lat='37.769068' lon='-122.406023' />\n  <node id='295219627' timestamp='2009-10-25T03:42:47Z' uid='28775' user='StellanL' version='4' changeset='2942982' lat='37.7687084' lon='-122.4058942' />\n  <node id='295219756' timestamp='2017-03-29T11:44:06Z' uid='933797' user='oba510' version='2' changeset='47257631' lat='37.7693864' lon='-122.4077747' />\n  <node id='295219758' timestamp='2008-09-09T00:27:30Z' uid='24452' user='Speight' version='1' changeset='585228' lat='37.7694831' lon='-122.4076852' />\n  <node id='295220015' timestamp='2010-05-14T07:10:25Z' uid='14293' user='KindredCoda' version='5' changeset='4692759' lat='37.768995' lon='-122.4079092' />\n  <node id='295220168' timestamp='2010-05-14T07:13:42Z' uid='14293' user='KindredCoda' version='10' changeset='4692759' lat='37.7688074' lon='-122.4080956' />\n  <node id='295221650' timestamp='2018-08-18T03:27:12Z' uid='119881' user='clay_c' version='12' changeset='61761408' lat='37.7689731' lon='-122.4086279' />\n  <node id='295221752' timestamp='2010-05-14T07:10:17Z' uid='14293' user='KindredCoda' version='8' changeset='4692759' lat='37.7688593' lon='-122.4068068' />\n  <node id='295221767' timestamp='2010-05-14T07:08:57Z' uid='14293' user='KindredCoda' version='8' changeset='4692759' lat='37.768889' lon='-122.4063548' />\n  <node id='295221939' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='8' changeset='4692759' lat='37.7695187' lon='-122.4058948' />\n  <node id='295221940' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='5' changeset='4692759' lat='37.7694159' lon='-122.4059565' />\n  <node id='295221941' timestamp='2010-05-14T07:11:10Z' uid='14293' user='KindredCoda' version='5' changeset='4692759' lat='37.76896' lon='-122.4072145' />\n  <node id='295221942' timestamp='2018-08-18T03:27:12Z' uid='119881' user='clay_c' version='13' changeset='61761408' lat='37.7690689' lon='-122.4086034' />\n  <node id='295222204' timestamp='2010-05-10T20:20:20Z' uid='14293' user='KindredCoda' version='23' changeset='4663164' lat='37.7660722' lon='-122.4048754' />\n  <node id='295222216' timestamp='2017-07-19T10:58:01Z' uid='3029661' user='saikabhi' version='12' changeset='50401838' lat='37.7643851' lon='-122.4048876'>\n    <tag k='highway' v='motorway_junction' />\n    <tag k='ref' v='433C;433B' />\n    <tag k='ref:left' v='433C' />\n    <tag k='ref:right' v='433B' />\n    <tag k='source' v='http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf' />\n  </node>\n  <node id='295223359' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='12' changeset='50914033' lat='37.7697799' lon='-122.4080774' />\n  <node id='295223386' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='11' changeset='50914033' lat='37.7698919' lon='-122.4087984' />\n  <node id='298081840' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7699205' lon='-122.4054134' />\n  <node id='298081944' timestamp='2010-05-14T06:46:50Z' uid='14293' user='KindredCoda' version='25' changeset='4692674' lat='37.7702584' lon='-122.4053541' />\n  <node id='299388079' timestamp='2010-05-14T07:16:13Z' uid='14293' user='KindredCoda' version='10' changeset='4692759' lat='37.7677525' lon='-122.4056602' />\n  <node id='299388080' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='2' changeset='4692674' lat='37.7673112' lon='-122.4051771' />\n  <node id='300444895' timestamp='2013-05-15T06:56:07Z' uid='14293' user='KindredCoda' version='7' changeset='16134749' lat='37.7698177' lon='-122.403392' />\n  <node id='300459902' timestamp='2014-01-07T10:41:00Z' uid='28775' user='StellanL' version='3' changeset='19860253' lat='37.7712513' lon='-122.386222' />\n  <node id='300460635' timestamp='2014-01-07T11:57:27Z' uid='28775' user='StellanL' version='3' changeset='19861380' lat='37.7730306' lon='-122.3943143' />\n  <node id='300460638' timestamp='2014-01-07T11:57:27Z' uid='28775' user='StellanL' version='2' changeset='19861380' lat='37.7743723' lon='-122.3918094' />\n  <node id='300460639' timestamp='2014-01-07T11:57:27Z' uid='28775' user='StellanL' version='2' changeset='19861380' lat='37.7728026' lon='-122.3937652' />\n  <node id='300460643' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='4' changeset='58077286' lat='37.7729282' lon='-122.3944211' />\n  <node id='300662244' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='15' changeset='38624789' lat='37.7717378' lon='-122.3984691' />\n  <node id='300757485' timestamp='2010-05-14T07:12:35Z' uid='14293' user='KindredCoda' version='6' changeset='4692759' lat='37.7702396' lon='-122.4058881' />\n  <node id='300757630' timestamp='2010-05-14T07:08:58Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7690067' lon='-122.4059431' />\n  <node id='300757631' timestamp='2010-05-14T07:08:58Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7691042' lon='-122.4057728' />\n  <node id='300757632' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='5' changeset='4692759' lat='37.7702353' lon='-122.4054684' />\n  <node id='300757646' timestamp='2010-05-10T20:20:22Z' uid='14293' user='KindredCoda' version='23' changeset='4663164' lat='37.769595' lon='-122.405204' />\n  <node id='300757647' timestamp='2010-05-10T20:20:23Z' uid='14293' user='KindredCoda' version='23' changeset='4663164' lat='37.7700382' lon='-122.4052817' />\n  <node id='302034098' timestamp='2014-01-07T10:41:00Z' uid='28775' user='StellanL' version='2' changeset='19860253' lat='37.7716386' lon='-122.3863616' />\n  <node id='302493286' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='15' changeset='38624789' lat='37.7715757' lon='-122.3986749' />\n  <node id='303754358' timestamp='2012-04-20T06:56:34Z' uid='14293' user='KindredCoda' version='22' changeset='11360818' lat='37.7765169' lon='-122.3902035' />\n  <node id='303754367' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='3' changeset='58074219' lat='37.7763414' lon='-122.387128' />\n  <node id='303754368' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='3' changeset='58074219' lat='37.7763346' lon='-122.387284' />\n  <node id='303754369' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='3' changeset='58074219' lat='37.7766122' lon='-122.3873143' />\n  <node id='303754370' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='3' changeset='58074219' lat='37.7767722' lon='-122.3875505' />\n  <node id='303754371' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='4' changeset='58074219' lat='37.7766133' lon='-122.3898455' />\n  <node id='303785344' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='20' changeset='53186943' lat='37.7713509' lon='-122.3976384' />\n  <node id='303785350' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='20' changeset='53186943' lat='37.7725192' lon='-122.3962309' />\n  <node id='303785455' timestamp='2009-03-11T12:31:33Z' uid='28775' user='StellanL' version='17' changeset='788629' lat='37.7746826' lon='-122.3934308' />\n  <node id='303785511' timestamp='2012-04-20T06:40:00Z' uid='14293' user='KindredCoda' version='17' changeset='11360720' lat='37.7749597' lon='-122.3931457' />\n  <node id='303785512' timestamp='2013-09-08T00:09:25Z' uid='14293' user='KindredCoda' version='18' changeset='17725564' lat='37.7752748' lon='-122.3924899' />\n  <node id='306735671' timestamp='2011-06-30T01:59:51Z' uid='169004' user='oldtopos' version='8' changeset='8586501' lat='37.7722349' lon='-122.4000833' />\n  <node id='306735672' timestamp='2011-06-30T01:59:46Z' uid='169004' user='oldtopos' version='8' changeset='8586501' lat='37.7718676' lon='-122.4002525' />\n  <node id='306735684' timestamp='2011-06-30T01:59:51Z' uid='169004' user='oldtopos' version='7' changeset='8586501' lat='37.7714721' lon='-122.4002893' />\n  <node id='306735795' timestamp='2011-06-30T01:59:56Z' uid='169004' user='oldtopos' version='5' changeset='8586501' lat='37.7710631' lon='-122.400185' />\n  <node id='306735822' timestamp='2011-06-30T01:59:48Z' uid='169004' user='oldtopos' version='3' changeset='8586501' lat='37.7723909' lon='-122.3999655' />\n  <node id='306735889' timestamp='2011-04-26T04:11:04Z' uid='14293' user='KindredCoda' version='3' changeset='7970700' lat='37.7704988' lon='-122.399727' />\n  <node id='308712276' timestamp='2010-05-14T06:45:20Z' uid='14293' user='KindredCoda' version='22' changeset='4692674' lat='37.7713916' lon='-122.406081' />\n  <node id='310729129' timestamp='2015-05-21T08:04:12Z' uid='33757' user='Minh Nguyen' version='12' changeset='31336567' lat='37.776876' lon='-122.3949266' />\n  <node id='310729130' timestamp='2016-04-04T07:08:53Z' uid='33757' user='Minh Nguyen' version='11' changeset='38282511' lat='37.7766548' lon='-122.3951543' />\n  <node id='310729131' timestamp='2016-04-04T07:08:53Z' uid='33757' user='Minh Nguyen' version='11' changeset='38282511' lat='37.7762203' lon='-122.3946066' />\n  <node id='310729132' timestamp='2015-05-21T08:04:12Z' uid='33757' user='Minh Nguyen' version='10' changeset='31336567' lat='37.7762738' lon='-122.3945377' />\n  <node id='310729133' timestamp='2015-05-21T08:04:12Z' uid='33757' user='Minh Nguyen' version='10' changeset='31336567' lat='37.7764564' lon='-122.3947688' />\n  <node id='310729134' timestamp='2015-05-21T08:04:12Z' uid='33757' user='Minh Nguyen' version='10' changeset='31336567' lat='37.7766061' lon='-122.3945805' />\n  <node id='314078882' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='4' changeset='4692759' lat='37.7690321' lon='-122.4065024' />\n  <node id='314078883' timestamp='2010-05-14T07:11:17Z' uid='14293' user='KindredCoda' version='5' changeset='4692759' lat='37.7689791' lon='-122.4076691' />\n  <node id='314078887' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='3' changeset='4692759' lat='37.769119' lon='-122.4062972' />\n  <node id='314078897' timestamp='2010-05-14T07:12:35Z' uid='14293' user='KindredCoda' version='4' changeset='4692759' lat='37.7699088' lon='-122.4058358' />\n  <node id='314078898' timestamp='2010-05-14T07:12:35Z' uid='14293' user='KindredCoda' version='4' changeset='4692759' lat='37.7697403' lon='-122.4058399' />\n  <node id='314078983' timestamp='2010-05-14T07:08:57Z' uid='14293' user='KindredCoda' version='6' changeset='4692759' lat='37.7688837' lon='-122.4076758' />\n  <node id='314078984' timestamp='2010-05-14T07:08:57Z' uid='14293' user='KindredCoda' version='6' changeset='4692759' lat='37.7688635' lon='-122.4072185' />\n  <node id='314079060' timestamp='2010-05-14T07:08:58Z' uid='14293' user='KindredCoda' version='2' changeset='4692759' lat='37.7695653' lon='-122.4054416' />\n  <node id='314079061' timestamp='2010-05-14T07:08:58Z' uid='14293' user='KindredCoda' version='2' changeset='4692759' lat='37.7696671' lon='-122.4054228' />\n  <node id='314079066' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='4' changeset='4692759' lat='37.7704113' lon='-122.4055327' />\n  <node id='314079075' timestamp='2010-05-14T07:12:34Z' uid='14293' user='KindredCoda' version='3' changeset='4692759' lat='37.7704071' lon='-122.4059311' />\n  <node id='314079148' timestamp='2010-05-10T20:20:22Z' uid='14293' user='KindredCoda' version='20' changeset='4663164' lat='37.7684257' lon='-122.4050967' />\n  <node id='314079200' timestamp='2010-05-14T06:46:50Z' uid='14293' user='KindredCoda' version='20' changeset='4692674' lat='37.7704386' lon='-122.4054279' />\n  <node id='314079201' timestamp='2010-05-10T20:28:05Z' uid='14293' user='KindredCoda' version='19' changeset='4663164' lat='37.7671313' lon='-122.4049679' />\n  <node id='314079203' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='3' changeset='4692759' lat='37.7705799' lon='-122.4056079' />\n  <node id='314079644' timestamp='2014-03-13T19:03:52Z' uid='1679807' user='Cato_d_Ae' version='7' changeset='21087627' lat='37.7691693' lon='-122.407329' />\n  <node id='314079646' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='5' changeset='21087627' lat='37.7682279' lon='-122.4060483' />\n  <node id='314079678' timestamp='2010-05-14T07:13:42Z' uid='14293' user='KindredCoda' version='7' changeset='4692759' lat='37.7687893' lon='-122.407885' />\n  <node id='314079692' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='7' changeset='21087627' lat='37.7672605' lon='-122.4054727' />\n  <node id='314079708' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='4' changeset='21087627' lat='37.7680837' lon='-122.4058806' />\n  <node id='314079709' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='4' changeset='21087627' lat='37.7679904' lon='-122.4057868' />\n  <node id='314079710' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='5' changeset='21087627' lat='37.767878' lon='-122.4057009' />\n  <node id='314079711' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='5' changeset='21087627' lat='37.7677646' lon='-122.4056312' />\n  <node id='314079712' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='5' changeset='21087627' lat='37.7676374' lon='-122.4055668' />\n  <node id='314079713' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='4' changeset='21087627' lat='37.7674709' lon='-122.4055092' />\n  <node id='314079729' timestamp='2013-05-22T06:53:48Z' uid='14293' user='KindredCoda' version='4' changeset='16233758' lat='37.7688905' lon='-122.4086922' />\n  <node id='314139236' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='5' changeset='50914033' lat='37.7698846' lon='-122.4083501' />\n  <node id='314139237' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='5' changeset='50914033' lat='37.7699138' lon='-122.4086443' />\n  <node id='314810083' timestamp='2014-07-20T23:35:34Z' uid='28775' user='StellanL' version='6' changeset='24263865' lat='37.7659197' lon='-122.4066613' />\n  <node id='314810084' timestamp='2014-07-20T23:35:34Z' uid='28775' user='StellanL' version='5' changeset='24263865' lat='37.7683472' lon='-122.4068956' />\n  <node id='314810085' timestamp='2014-07-20T23:35:35Z' uid='28775' user='StellanL' version='5' changeset='24263865' lat='37.7681026' lon='-122.4106123' />\n  <node id='315342911' timestamp='2008-11-28T19:43:47Z' uid='35195' user='will l' version='1' changeset='805304' lat='37.7691311' lon='-122.3978454' />\n  <node id='315342912' timestamp='2008-11-28T19:43:47Z' uid='35195' user='will l' version='1' changeset='805304' lat='37.7693483' lon='-122.3981115' />\n  <node id='315342913' timestamp='2008-11-28T19:43:47Z' uid='35195' user='will l' version='1' changeset='805304' lat='37.7700403' lon='-122.3989698' />\n  <node id='315342914' timestamp='2013-04-05T07:01:11Z' uid='933797' user='oba510' version='3' changeset='15616228' lat='37.7704584' lon='-122.3994301' />\n  <node id='315342915' timestamp='2012-10-26T22:39:47Z' uid='28775' user='StellanL' version='2' changeset='13644272' lat='37.7708588' lon='-122.3997026' />\n  <node id='315342916' timestamp='2012-10-26T22:39:47Z' uid='28775' user='StellanL' version='2' changeset='13644272' lat='37.7709317' lon='-122.3997779' />\n  <node id='315342917' timestamp='2012-10-26T22:39:47Z' uid='28775' user='StellanL' version='2' changeset='13644272' lat='37.7711881' lon='-122.3998537' />\n  <node id='315342918' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='2' changeset='38624789' lat='37.7713867' lon='-122.3998923' />\n  <node id='315342919' timestamp='2008-11-28T19:43:48Z' uid='35195' user='will l' version='1' changeset='805304' lat='37.7716347' lon='-122.3998453' />\n  <node id='315342920' timestamp='2008-11-28T19:43:48Z' uid='35195' user='will l' version='1' changeset='805304' lat='37.7720621' lon='-122.3995449' />\n  <node id='315342921' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='2' changeset='38624789' lat='37.7728466' lon='-122.3988349' />\n  <node id='315342922' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='2' changeset='38624789' lat='37.7733045' lon='-122.3982668' />\n  <node id='315342923' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='2' changeset='38624789' lat='37.7736564' lon='-122.3978937' />\n  <node id='315342924' timestamp='2016-04-16T14:43:51Z' uid='28775' user='StellanL' version='2' changeset='38624789' lat='37.7738396' lon='-122.3977124' />\n  <node id='315342925' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='2' changeset='38624789' lat='37.774068' lon='-122.3974352' />\n  <node id='315342926' timestamp='2008-11-28T19:43:48Z' uid='35195' user='will l' version='1' changeset='805304' lat='37.7745316' lon='-122.3966781' />\n  <node id='315342927' timestamp='2016-04-04T07:08:53Z' uid='33757' user='Minh Nguyen' version='3' changeset='38282511' lat='37.7761926' lon='-122.3945703' />\n  <node id='315342928' timestamp='2010-06-10T02:11:50Z' uid='14293' user='KindredCoda' version='4' changeset='4950723' lat='37.7765431' lon='-122.3952959' />\n  <node id='315342930' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='2' changeset='4950723' lat='37.7765961' lon='-122.3953616' />\n  <node id='315342931' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='2' changeset='4950723' lat='37.7754969' lon='-122.3968033' />\n  <node id='315342932' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='2' changeset='4950723' lat='37.7734224' lon='-122.3994239' />\n  <node id='315342933' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='2' changeset='4950723' lat='37.771775' lon='-122.4014114' />\n  <node id='315342934' timestamp='2008-11-28T19:43:48Z' uid='35195' user='will l' version='1' changeset='805304' lat='37.7686766' lon='-122.3975794' />\n  <node id='317102068' timestamp='2009-03-11T12:31:34Z' uid='28775' user='StellanL' version='13' changeset='788629' lat='37.7810992' lon='-122.3876858' />\n  <node id='317102418' timestamp='2009-03-11T12:31:34Z' uid='28775' user='StellanL' version='14' changeset='788629' lat='37.7814669' lon='-122.3872304' />\n  <node id='318260226' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='2' changeset='4692759' lat='37.7707537' lon='-122.4056923' />\n  <node id='318260227' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='2' changeset='4692759' lat='37.7708979' lon='-122.4057688' />\n  <node id='356618598' timestamp='2015-06-26T08:45:12Z' uid='33757' user='Minh Nguyen' version='2' changeset='32220993' lat='37.7705267' lon='-122.4030173'>\n    <tag k='amenity' v='parking' />\n    <tag k='fee' v='yes' />\n    <tag k='name' v='Metro @ Showplace Square Garage' />\n    <tag k='parking' v='underground' />\n  </node>\n  <node id='358946108' timestamp='2009-03-11T12:31:32Z' uid='28775' user='StellanL' version='1' changeset='788629' lat='37.7739552' lon='-122.38174' />\n  <node id='358946109' timestamp='2009-03-11T12:31:32Z' uid='28775' user='StellanL' version='1' changeset='788629' lat='37.7740825' lon='-122.3814581' />\n  <node id='358946110' timestamp='2018-05-24T18:55:36Z' uid='7134350' user='KaL fvr' version='2' changeset='59250365' lat='37.774451' lon='-122.3818152' />\n  <node id='423775491' timestamp='2013-12-12T17:03:37Z' uid='28775' user='StellanL' version='5' changeset='19417081' lat='37.7696802' lon='-122.3982012' />\n  <node id='423775499' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='4' changeset='53186943' lat='37.7701979' lon='-122.3989385' />\n  <node id='423775500' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='4' changeset='53186943' lat='37.7703055' lon='-122.3990512' />\n  <node id='423775501' timestamp='2013-04-05T07:01:14Z' uid='933797' user='oba510' version='3' changeset='15616228' lat='37.77066' lon='-122.3994876' />\n  <node id='423775502' timestamp='2010-06-10T01:37:49Z' uid='14293' user='KindredCoda' version='3' changeset='4950654' lat='37.7707999' lon='-122.3995643' />\n  <node id='423775811' timestamp='2010-03-07T08:41:20Z' uid='201724' user='mk408' version='2' changeset='4058253' lat='37.7709918' lon='-122.399604'>\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='423775812' timestamp='2010-01-20T02:20:21Z' uid='28775' user='StellanL' version='2' changeset='3663930' lat='37.7712431' lon='-122.3996866' />\n  <node id='423775813' timestamp='2012-04-10T07:33:10Z' uid='116029' user='Gregory Arenius' version='3' changeset='11248664' lat='37.7714776' lon='-122.3996854' />\n  <node id='423775814' timestamp='2010-01-20T02:20:21Z' uid='28775' user='StellanL' version='2' changeset='3663930' lat='37.7716873' lon='-122.3996048' />\n  <node id='423775815' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='5' changeset='38624789' lat='37.7724061' lon='-122.3990056' />\n  <node id='466244863' timestamp='2010-06-09T09:04:23Z' uid='14293' user='KindredCoda' version='2' changeset='4943544' lat='37.7631974' lon='-122.3927377' />\n  <node id='480642605' timestamp='2012-10-07T07:00:09Z' uid='933797' user='oba510' version='2' changeset='13393870' lat='37.7673049' lon='-122.4002276' />\n  <node id='480642606' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='2' changeset='13393870' lat='37.7668529' lon='-122.3996499' />\n  <node id='480642607' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='2' changeset='13393870' lat='37.7676352' lon='-122.3986704' />\n  <node id='480642608' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='2' changeset='13393870' lat='37.7680872' lon='-122.399248' />\n  <node id='496536677' timestamp='2010-06-09T09:04:22Z' uid='14293' user='KindredCoda' version='2' changeset='4943544' lat='37.7639947' lon='-122.3928986' />\n  <node id='496536678' timestamp='2010-06-09T09:04:22Z' uid='14293' user='KindredCoda' version='2' changeset='4943544' lat='37.7636045' lon='-122.3928021' />\n  <node id='539993900' timestamp='2009-10-25T03:42:44Z' uid='28775' user='StellanL' version='1' changeset='2942982' lat='37.7673311' lon='-122.4056711' />\n  <node id='539993901' timestamp='2009-10-25T03:42:44Z' uid='28775' user='StellanL' version='1' changeset='2942982' lat='37.7679417' lon='-122.4056539' />\n  <node id='539993902' timestamp='2012-03-17T03:33:37Z' uid='14293' user='KindredCoda' version='3' changeset='11004366' lat='37.768488' lon='-122.4056892' />\n  <node id='539993903' timestamp='2009-10-25T03:42:44Z' uid='28775' user='StellanL' version='1' changeset='2942982' lat='37.7688441' lon='-122.4057741' />\n  <node id='539993904' timestamp='2009-10-25T03:42:44Z' uid='28775' user='StellanL' version='1' changeset='2942982' lat='37.7691291' lon='-122.4058942' />\n  <node id='539993905' timestamp='2017-05-27T22:34:29Z' uid='33757' user='Minh Nguyen' version='4' changeset='49034504' lat='37.7693733' lon='-122.4060831'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='590637975' timestamp='2017-10-23T19:20:05Z' uid='53073' user='Aaron Lidman' version='3' changeset='53188507' lat='37.7743195' lon='-122.3941654' />\n  <node id='590637977' timestamp='2017-10-23T19:20:05Z' uid='53073' user='Aaron Lidman' version='3' changeset='53188507' lat='37.7747518' lon='-122.3936209' />\n  <node id='590638118' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='8' changeset='58077286' lat='37.7722832' lon='-122.3976389' />\n  <node id='590638123' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='6' changeset='53186943' lat='37.7719325' lon='-122.3971938' />\n  <node id='590638148' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='4' changeset='53186943' lat='37.7734388' lon='-122.3952753' />\n  <node id='590638153' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='5' changeset='53186943' lat='37.7730018' lon='-122.3958301' />\n  <node id='590986641' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='4' changeset='38624789' lat='37.7748858' lon='-122.3944713' />\n  <node id='590986656' timestamp='2017-10-23T19:20:05Z' uid='53073' user='Aaron Lidman' version='5' changeset='53188507' lat='37.7741197' lon='-122.3954843' />\n  <node id='590986678' timestamp='2017-10-23T19:20:05Z' uid='53073' user='Aaron Lidman' version='5' changeset='53188507' lat='37.7739551' lon='-122.3956834' />\n  <node id='590986685' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='3' changeset='53186943' lat='37.7737947' lon='-122.3957277' />\n  <node id='590986687' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='3' changeset='53186943' lat='37.774141' lon='-122.3952797' />\n  <node id='590986689' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='4' changeset='58077286' lat='37.7737789' lon='-122.394842' />\n  <node id='590986802' timestamp='2012-01-13T23:05:41Z' uid='103253' user='gormur' version='2' changeset='10383705' lat='37.7782445' lon='-122.3875871' />\n  <node id='595774117' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='4' changeset='38624789' lat='37.7722396' lon='-122.3978318' />\n  <node id='616936988' timestamp='2012-04-10T07:33:10Z' uid='116029' user='Gregory Arenius' version='2' changeset='11248664' lat='37.7718729' lon='-122.3994901' />\n  <node id='666571226' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='4' changeset='38624789' lat='37.77573' lon='-122.3933992'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='666571379' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58074219' lat='37.7748697' lon='-122.3919861' />\n  <node id='666571381' timestamp='2010-03-15T02:49:29Z' uid='28775' user='StellanL' version='1' changeset='4131610' lat='37.7749154' lon='-122.3920548' />\n  <node id='666571382' timestamp='2010-03-15T02:49:29Z' uid='28775' user='StellanL' version='1' changeset='4131610' lat='37.7747568' lon='-122.3922591' />\n  <node id='666571384' timestamp='2014-01-07T11:57:27Z' uid='28775' user='StellanL' version='2' changeset='19861380' lat='37.7746885' lon='-122.3921924' />\n  <node id='703048830' timestamp='2013-12-12T17:03:37Z' uid='28775' user='StellanL' version='3' changeset='19417081' lat='37.7697377' lon='-122.3981097'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='708463429' timestamp='2010-04-27T08:57:31Z' uid='116029' user='Gregory Arenius' version='1' changeset='4539232' lat='37.7707261' lon='-122.399166'>\n    <tag k='amenity' v='drinking_water' />\n    <tag k='dog' v='yes' />\n    <tag k='wheelchair' v='yes' />\n  </node>\n  <node id='708463475' timestamp='2013-04-05T07:01:10Z' uid='933797' user='oba510' version='7' changeset='15616228' lat='37.771249' lon='-122.3988693' />\n  <node id='708463477' timestamp='2013-04-05T07:01:10Z' uid='933797' user='oba510' version='7' changeset='15616228' lat='37.7710222' lon='-122.399154' />\n  <node id='708463482' timestamp='2013-04-05T07:01:10Z' uid='933797' user='oba510' version='7' changeset='15616228' lat='37.770899' lon='-122.3989969' />\n  <node id='708463485' timestamp='2013-04-05T07:01:10Z' uid='933797' user='oba510' version='7' changeset='15616228' lat='37.7711257' lon='-122.3987122' />\n  <node id='708463538' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='5' changeset='58077286' lat='37.770979' lon='-122.3992503'>\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='708463541' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286' lat='37.7708674' lon='-122.3993944' />\n  <node id='708463542' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286' lat='37.770825' lon='-122.399426' />\n  <node id='708463543' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286' lat='37.7707667' lon='-122.39944' />\n  <node id='708463545' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286' lat='37.7707158' lon='-122.3994192' />\n  <node id='708463547' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286' lat='37.7703517' lon='-122.398967' />\n  <node id='708463548' timestamp='2013-04-05T07:01:13Z' uid='933797' user='oba510' version='4' changeset='15616228' lat='37.7704477' lon='-122.3988433' />\n  <node id='708463550' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='2' changeset='53186943' lat='37.7707008' lon='-122.3991586' />\n  <node id='708463568' timestamp='2017-10-23T22:45:39Z' uid='53073' user='Aaron Lidman' version='3' changeset='53193135' lat='37.7707839' lon='-122.3990554' />\n  <node id='708463788' timestamp='2011-05-27T01:31:07Z' uid='14293' user='KindredCoda' version='4' changeset='8258584' lat='37.7711493' lon='-122.398578' />\n  <node id='708463792' timestamp='2011-05-27T01:31:07Z' uid='14293' user='KindredCoda' version='4' changeset='8258584' lat='37.7709002' lon='-122.398267' />\n  <node id='708463833' timestamp='2011-05-27T01:31:08Z' uid='14293' user='KindredCoda' version='4' changeset='8258584' lat='37.7704695' lon='-122.3988459' />\n  <node id='708463838' timestamp='2011-05-27T01:31:08Z' uid='14293' user='KindredCoda' version='4' changeset='8258584' lat='37.7706915' lon='-122.3991232' />\n  <node id='708463842' timestamp='2011-05-27T01:31:08Z' uid='14293' user='KindredCoda' version='4' changeset='8258584' lat='37.7707575' lon='-122.3990386' />\n  <node id='708463846' timestamp='2011-05-27T01:31:08Z' uid='14293' user='KindredCoda' version='4' changeset='8258584' lat='37.7706686' lon='-122.3989276' />\n  <node id='708463852' timestamp='2011-05-27T01:31:08Z' uid='14293' user='KindredCoda' version='4' changeset='8258584' lat='37.7708239' lon='-122.3987286' />\n  <node id='708463869' timestamp='2011-05-27T01:31:08Z' uid='14293' user='KindredCoda' version='4' changeset='8258584' lat='37.7709292' lon='-122.39886' />\n  <node id='708533541' timestamp='2017-10-23T19:20:05Z' uid='53073' user='Aaron Lidman' version='5' changeset='53188507' lat='37.7733782' lon='-122.3964146' />\n  <node id='708533549' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='6' changeset='53186943' lat='37.7708762' lon='-122.3982123' />\n  <node id='708533561' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='4' changeset='53186943' lat='37.7729267' lon='-122.3959286' />\n  <node id='708533568' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='5' changeset='53186943' lat='37.7718078' lon='-122.3970405' />\n  <node id='708533580' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='5' changeset='38624789' lat='37.7746805' lon='-122.394732' />\n  <node id='708533582' timestamp='2018-04-13T21:53:07Z' uid='53073' user='Aaron Lidman' version='8' changeset='58077286' lat='37.7710076' lon='-122.3992458' />\n  <node id='708533587' timestamp='2017-10-23T18:29:51Z' uid='53073' user='Aaron Lidman' version='4' changeset='53186943' lat='37.7726284' lon='-122.3963064' />\n  <node id='728909725' timestamp='2010-05-10T20:20:19Z' uid='14293' user='KindredCoda' version='1' changeset='4663164' lat='37.7653661' lon='-122.4048419' />\n  <node id='728909726' timestamp='2010-05-10T20:20:20Z' uid='14293' user='KindredCoda' version='1' changeset='4663164' lat='37.7658379' lon='-122.4048593' />\n  <node id='728909734' timestamp='2010-05-14T06:45:19Z' uid='14293' user='KindredCoda' version='2' changeset='4692674' lat='37.7707842' lon='-122.4056224' />\n  <node id='728909736' timestamp='2010-05-14T06:45:20Z' uid='14293' user='KindredCoda' version='2' changeset='4692674' lat='37.7711213' lon='-122.4058718' />\n  <node id='728917817' timestamp='2012-03-14T03:30:43Z' uid='462373' user='Davidazus' version='2' changeset='10974121' lat='37.7647312' lon='-122.4048389' />\n  <node id='728917836' timestamp='2010-05-10T20:28:04Z' uid='14293' user='KindredCoda' version='1' changeset='4663164' lat='37.765047' lon='-122.4048392' />\n  <node id='733582844' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='1' changeset='4692674' lat='37.7674194' lon='-122.4052053' />\n  <node id='733582849' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='1' changeset='4692674' lat='37.7676399' lon='-122.4052978' />\n  <node id='733582853' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='1' changeset='4692674' lat='37.7678869' lon='-122.4054735' />\n  <node id='733582855' timestamp='2010-05-14T06:42:54Z' uid='14293' user='KindredCoda' version='1' changeset='4692674' lat='37.7679844' lon='-122.405566' />\n  <node id='733583392' timestamp='2010-05-14T06:43:07Z' uid='14293' user='KindredCoda' version='1' changeset='4692674' lat='37.7680671' lon='-122.4056613' />\n  <node id='733619113' timestamp='2018-09-28T20:49:28Z' uid='33757' user='Minh Nguyen' version='8' changeset='63022562' lat='37.7690681' lon='-122.4092372'>\n    <tag k='highway' v='motorway_junction' />\n    <tag k='noref' v='yes' />\n    <tag k='source' v='http://www.dot.ca.gov/hq/traffops/signtech/calnexus/pdf/101.pdf' />\n  </node>\n  <node id='733619123' timestamp='2013-05-22T06:53:49Z' uid='14293' user='KindredCoda' version='2' changeset='16233758' lat='37.7689309' lon='-122.4088943' />\n  <node id='733622843' timestamp='2010-05-14T07:08:57Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.768872' lon='-122.4074478' />\n  <node id='733624608' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7700784' lon='-122.4054308' />\n  <node id='733624612' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7701548' lon='-122.4054456' />\n  <node id='733624629' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7710039' lon='-122.4058318' />\n  <node id='733624631' timestamp='2010-05-14T07:09:26Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7710993' lon='-122.4058962' />\n  <node id='733624873' timestamp='2010-05-14T07:09:32Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7698389' lon='-122.4054094' />\n  <node id='733624877' timestamp='2010-05-14T07:09:32Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7699989' lon='-122.4054201' />\n  <node id='733625764' timestamp='2010-05-14T07:09:47Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7691636' lon='-122.405699' />\n  <node id='733625766' timestamp='2010-05-14T07:09:47Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7693109' lon='-122.405569' />\n  <node id='733625769' timestamp='2010-05-14T07:09:47Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7694752' lon='-122.4054724' />\n  <node id='733626661' timestamp='2010-05-14T07:10:03Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7688741' lon='-122.4064675' />\n  <node id='733626665' timestamp='2010-05-14T07:10:03Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.768907' lon='-122.4062502' />\n  <node id='733626671' timestamp='2010-05-14T07:10:03Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7689643' lon='-122.406041' />\n  <node id='733626677' timestamp='2010-05-14T07:10:03Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7690522' lon='-122.4058546' />\n  <node id='733627034' timestamp='2010-05-14T07:10:09Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7688614' lon='-122.4066888' />\n  <node id='733628924' timestamp='2013-05-22T06:53:48Z' uid='14293' user='KindredCoda' version='2' changeset='16233758' lat='37.7689514' lon='-122.4084532' />\n  <node id='733630089' timestamp='2010-05-14T07:11:04Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7690194' lon='-122.4081922' />\n  <node id='733630094' timestamp='2010-05-14T07:11:04Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7690459' lon='-122.4084336' />\n  <node id='733630737' timestamp='2010-05-14T07:11:17Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7689685' lon='-122.4074438' />\n  <node id='733632258' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7693353' lon='-122.4060223' />\n  <node id='733632260' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7690713' lon='-122.4063964' />\n  <node id='733632261' timestamp='2010-05-14T07:11:52Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7690003' lon='-122.4066164' />\n  <node id='733634101' timestamp='2010-05-14T07:12:34Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7706912' lon='-122.4060236' />\n  <node id='733634117' timestamp='2010-05-14T07:12:34Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7705746' lon='-122.405982' />\n  <node id='733634124' timestamp='2010-05-14T07:12:34Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7703286' lon='-122.4059096' />\n  <node id='733634133' timestamp='2010-05-14T07:12:35Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7701548' lon='-122.4058694' />\n  <node id='733634142' timestamp='2010-05-14T07:12:35Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7699915' lon='-122.4058412' />\n  <node id='733634149' timestamp='2010-05-14T07:12:35Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7698251' lon='-122.4058345' />\n  <node id='733634154' timestamp='2010-05-14T07:12:35Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7696268' lon='-122.4058573' />\n  <node id='733637405' timestamp='2010-05-14T07:13:41Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7688286' lon='-122.4082914' />\n  <node id='733637423' timestamp='2010-05-14T07:13:42Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7687671' lon='-122.4076718' />\n  <node id='733637435' timestamp='2010-05-14T07:13:42Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7686907' lon='-122.4072534' />\n  <node id='733637444' timestamp='2010-05-14T07:13:43Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.768572' lon='-122.4068765' />\n  <node id='733637451' timestamp='2010-05-14T07:13:43Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7684512' lon='-122.4065962' />\n  <node id='733638758' timestamp='2010-05-14T07:16:12Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7683314' lon='-122.4063629' />\n  <node id='733638760' timestamp='2010-05-14T07:16:12Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7682285' lon='-122.4061912' />\n  <node id='733638763' timestamp='2010-05-14T07:16:12Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7681119' lon='-122.4060236' />\n  <node id='733638765' timestamp='2010-05-14T07:16:12Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7680377' lon='-122.405927' />\n  <node id='733638767' timestamp='2010-05-14T07:16:13Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7679539' lon='-122.4058318' />\n  <node id='733638785' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='2' changeset='21087627' lat='37.7673155' lon='-122.4054677' />\n  <node id='733638792' timestamp='2010-05-14T07:16:29Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.7675532' lon='-122.4055529' />\n  <node id='733638793' timestamp='2010-05-14T07:16:29Z' uid='14293' user='KindredCoda' version='1' changeset='4692759' lat='37.767321' lon='-122.4054831' />\n  <node id='733638795' timestamp='2014-03-13T19:03:53Z' uid='1679807' user='Cato_d_Ae' version='2' changeset='21087627' lat='37.7675547' lon='-122.405536' />\n  <node id='733639375' timestamp='2013-05-22T07:04:52Z' uid='14293' user='KindredCoda' version='2' changeset='16233828' lat='37.7696209' lon='-122.407811' />\n  <node id='733639377' timestamp='2013-05-22T07:04:52Z' uid='14293' user='KindredCoda' version='2' changeset='16233828' lat='37.7697107' lon='-122.4079587' />\n  <node id='733639385' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='2' changeset='50914033' lat='37.769909' lon='-122.4084933' />\n  <node id='733639389' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='2' changeset='50914033' lat='37.7698627' lon='-122.4089154' />\n  <node id='733639392' timestamp='2017-08-07T11:12:58Z' uid='94578' user='andygol' version='2' changeset='50914033' lat='37.7697823' lon='-122.4091111' />\n  <node id='767817443' timestamp='2018-10-30T06:09:40Z' uid='119881' user='clay_c' version='8' changeset='64000226' lat='37.7666122' lon='-122.3948708'>\n    <tag k='railway' v='level_crossing' />\n    <tag k='usage' v='main' />\n  </node>\n  <node id='767817444' timestamp='2018-10-30T06:09:40Z' uid='119881' user='clay_c' version='3' changeset='64000226' lat='37.7661255' lon='-122.3942618' />\n  <node id='767817445' timestamp='2018-10-30T06:09:40Z' uid='119881' user='clay_c' version='2' changeset='64000226' lat='37.7657493' lon='-122.393892' />\n  <node id='767817446' timestamp='2018-10-30T06:09:40Z' uid='119881' user='clay_c' version='2' changeset='64000226' lat='37.7653454' lon='-122.3935799' />\n  <node id='767817447' timestamp='2018-10-30T06:09:40Z' uid='119881' user='clay_c' version='2' changeset='64000226' lat='37.7648874' lon='-122.3932929' />\n  <node id='768243466' timestamp='2011-06-30T01:59:55Z' uid='169004' user='oldtopos' version='2' changeset='8586501' lat='37.7706846' lon='-122.3999297' />\n  <node id='768243534' timestamp='2013-04-05T07:01:13Z' uid='933797' user='oba510' version='2' changeset='15616228' lat='37.7707067' lon='-122.3995342' />\n  <node id='768243535' timestamp='2010-06-10T01:37:49Z' uid='14293' user='KindredCoda' version='1' changeset='4950654' lat='37.770748' lon='-122.399559' />\n  <node id='768243536' timestamp='2010-06-10T01:37:49Z' uid='14293' user='KindredCoda' version='1' changeset='4950654' lat='37.7708519' lon='-122.3995523' />\n  <node id='768247772' timestamp='2010-06-10T02:12:26Z' uid='14293' user='KindredCoda' version='2' changeset='4950723' lat='37.775199' lon='-122.397207' />\n  <node id='768247773' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7743648' lon='-122.3982397' />\n  <node id='768247774' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7734796' lon='-122.3993675' />\n  <node id='768247775' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7723475' lon='-122.4007904' />\n  <node id='768247776' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7722234' lon='-122.4009728' />\n  <node id='768247777' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7721672' lon='-122.4009286' />\n  <node id='768247778' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7720888' lon='-122.4010922' />\n  <node id='768247779' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7718874' lon='-122.4013604' />\n  <node id='768247780' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7718513' lon='-122.4013859' />\n  <node id='768247781' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7706365' lon='-122.400026' />\n  <node id='768247782' timestamp='2010-06-10T02:11:51Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7704849' lon='-122.3998624' />\n  <node id='768247783' timestamp='2010-06-10T02:12:15Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.776347' lon='-122.3956835' />\n  <node id='768247784' timestamp='2010-06-10T02:12:16Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7763629' lon='-122.3957009' />\n  <node id='768247785' timestamp='2010-06-10T02:12:16Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7758721' lon='-122.3963366' />\n  <node id='768247787' timestamp='2010-06-10T02:12:26Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7754057' lon='-122.3969361' />\n  <node id='768247788' timestamp='2010-06-10T02:12:30Z' uid='14293' user='KindredCoda' version='1' changeset='4950723' lat='37.7750389' lon='-122.3973867' />\n  <node id='984720636' timestamp='2016-12-16T20:05:29Z' uid='3526564' user='yurasi' version='3' changeset='44455726' lat='37.7697271' lon='-122.3983363'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='984720713' timestamp='2014-12-12T02:24:43Z' uid='123633' user='stevea' version='5' changeset='27413049' lat='37.7695543' lon='-122.3985416'>\n    <tag k='railway' v='level_crossing' />\n  </node>\n  <node id='984720787' timestamp='2018-04-01T18:54:00Z' uid='53073' user='Aaron Lidman' version='3' changeset='57720035' lat='37.7694846' lon='-122.398631' />\n  <node id='984720797' timestamp='2018-04-01T18:54:00Z' uid='53073' user='Aaron Lidman' version='3' changeset='57720035' lat='37.7694084' lon='-122.3985346' />\n  <node id='1216643467' timestamp='2018-02-05T10:11:51Z' uid='933797' user='oba510' version='2' changeset='56080356' lat='37.770673' lon='-122.3858007' />\n  <node id='1260356820' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7763567' lon='-122.3850442' />\n  <node id='1260356822' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7734' lon='-122.3849082' />\n  <node id='1260356824' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7723744' lon='-122.3866785' />\n  <node id='1260356825' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7719452' lon='-122.3815399' />\n  <node id='1260356826' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7724218' lon='-122.3867187' />\n  <node id='1260356827' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7719454' lon='-122.381492' />\n  <node id='1260356828' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7720222' lon='-122.3865579' />\n  <node id='1260356830' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7723784' lon='-122.3866651' />\n  <node id='1260356832' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.771956' lon='-122.3864542' />\n  <node id='1260356833' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7754306' lon='-122.3848487' />\n  <node id='1260356834' timestamp='2011-04-26T01:06:10Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7724494' lon='-122.3867288' />\n  <node id='1260356835' timestamp='2014-01-07T10:41:01Z' uid='28775' user='StellanL' version='2' changeset='19860253' lat='37.7715751' lon='-122.3863' />\n  <node id='1260356837' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.776339' lon='-122.3850142' />\n  <node id='1260356839' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7723968' lon='-122.3866851' />\n  <node id='1260356841' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.772306' lon='-122.3866297' />\n  <node id='1260356842' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7723927' lon='-122.386708' />\n  <node id='1260356843' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7719574' lon='-122.3865477' />\n  <node id='1260356844' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7724619' lon='-122.3866785' />\n  <node id='1260356846' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7724793' lon='-122.3866668' />\n  <node id='1260356848' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7751052' lon='-122.3869525' />\n  <node id='1260356852' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7725007' lon='-122.3866629' />\n  <node id='1260356854' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7722381' lon='-122.3866086' />\n  <node id='1260356855' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7719401' lon='-122.3815127' />\n  <node id='1260356857' timestamp='2011-04-26T01:06:11Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7753395' lon='-122.384905' />\n  <node id='1260356858' timestamp='2011-04-26T01:06:12Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7719791' lon='-122.3814771' />\n  <node id='1260356861' timestamp='2011-04-26T01:06:12Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7726887' lon='-122.3867018' />\n  <node id='1260356862' timestamp='2011-04-26T01:06:12Z' uid='14293' user='KindredCoda' version='1' changeset='7970273' lat='37.7732861' lon='-122.3867388' />\n  <node id='1260419894' timestamp='2011-06-30T01:59:55Z' uid='169004' user='oldtopos' version='2' changeset='8586501' lat='37.7702973' lon='-122.3994673' />\n  <node id='1266064662' timestamp='2016-05-15T05:33:45Z' uid='933797' user='oba510' version='4' changeset='39324426' lat='37.7712328' lon='-122.4053572'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='1344032198' timestamp='2011-06-30T01:59:23Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7707482' lon='-122.3998253' />\n  <node id='1344032216' timestamp='2011-06-30T01:59:24Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7694516' lon='-122.3984773'>\n    <tag k='railway' v='level_crossing' />\n  </node>\n  <node id='1344032218' timestamp='2011-06-30T01:59:24Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7712865' lon='-122.4001716' />\n  <node id='1344032231' timestamp='2011-06-30T01:59:24Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7709919' lon='-122.4002347' />\n  <node id='1344032260' timestamp='2011-06-30T01:59:25Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7705196' lon='-122.3996754' />\n  <node id='1344032279' timestamp='2018-10-30T06:09:42Z' uid='119881' user='clay_c' version='2' changeset='64000226' lat='37.7671425' lon='-122.3955727' />\n  <node id='1344032280' timestamp='2011-06-30T01:59:25Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7722441' lon='-122.3999902' />\n  <node id='1344032282' timestamp='2011-06-30T01:59:25Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7706502' lon='-122.3997317' />\n  <node id='1344032305' timestamp='2011-06-30T01:59:26Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7708979' lon='-122.4000099' />\n  <node id='1344032313' timestamp='2011-06-30T01:59:26Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7711037' lon='-122.4002879' />\n  <node id='1344032323' timestamp='2011-06-30T01:59:26Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7725952' lon='-122.3996803' />\n  <node id='1344032324' timestamp='2011-06-30T01:59:26Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.771642' lon='-122.400142' />\n  <node id='1344032326' timestamp='2011-06-30T01:59:26Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7704434' lon='-122.3995114' />\n  <node id='1344032342' timestamp='2014-12-12T02:24:43Z' uid='123633' user='stevea' version='2' changeset='27413049' lat='37.7695848' lon='-122.3985037'>\n    <tag k='railway' v='level_crossing' />\n  </node>\n  <node id='1344032357' timestamp='2011-06-30T01:59:27Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7713431' lon='-122.4003641' />\n  <node id='1344032359' timestamp='2011-06-30T01:59:27Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7695295' lon='-122.3985735'>\n    <tag k='railway' v='level_crossing' />\n  </node>\n  <node id='1344032365' timestamp='2011-06-30T01:59:27Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7718816' lon='-122.4001631' />\n  <node id='1344032367' timestamp='2011-06-30T01:59:27Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7705864' lon='-122.3996619' />\n  <node id='1344032394' timestamp='2011-06-30T01:59:28Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.771933' lon='-122.4000456' />\n  <node id='1344032401' timestamp='2011-06-30T01:59:28Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7712096' lon='-122.4003283' />\n  <node id='1344032404' timestamp='2011-06-30T01:59:28Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7715666' lon='-122.4001447' />\n  <node id='1344032411' timestamp='2011-06-30T01:59:29Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7703324' lon='-122.3993985' />\n  <node id='1344032445' timestamp='2011-06-30T01:59:29Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7714881' lon='-122.4002023' />\n  <node id='1344032449' timestamp='2011-06-30T01:59:30Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7712959' lon='-122.4001071' />\n  <node id='1344032462' timestamp='2011-06-30T01:59:30Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7717247' lon='-122.4001291' />\n  <node id='1344032471' timestamp='2011-06-30T01:59:30Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7718299' lon='-122.400097' />\n  <node id='1344032476' timestamp='2011-06-30T01:59:30Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7704732' lon='-122.3997657' />\n  <node id='1344032485' timestamp='2011-06-30T01:59:30Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.771473' lon='-122.4001374' />\n  <node id='1344032489' timestamp='2011-06-30T01:59:31Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7707024' lon='-122.3998634' />\n  <node id='1344032490' timestamp='2011-06-30T01:59:31Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7701151' lon='-122.3991677' />\n  <node id='1344032526' timestamp='2011-06-30T01:59:32Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7710878' lon='-122.4001078' />\n  <node id='1344032530' timestamp='2011-06-30T01:59:32Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7711697' lon='-122.4000713' />\n  <node id='1344032557' timestamp='2011-06-30T01:59:32Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.771777' lon='-122.4001181' />\n  <node id='1344032561' timestamp='2011-06-30T01:59:32Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7705653' lon='-122.3998776' />\n  <node id='1344032572' timestamp='2011-06-30T01:59:33Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.770321' lon='-122.3994255' />\n  <node id='1344032579' timestamp='2011-06-30T01:59:33Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7720827' lon='-122.400086' />\n  <node id='1344032591' timestamp='2011-06-30T01:59:33Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7688321' lon='-122.3977008' />\n  <node id='1344032592' timestamp='2011-06-30T01:59:33Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7710522' lon='-122.4000291' />\n  <node id='1344032615' timestamp='2011-06-30T01:59:34Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7723846' lon='-122.3998846' />\n  <node id='1344032625' timestamp='2011-06-30T01:59:34Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7706647' lon='-122.3999887' />\n  <node id='1344032635' timestamp='2018-10-30T06:09:42Z' uid='119881' user='clay_c' version='2' changeset='64000226' lat='37.7675178' lon='-122.3960609' />\n  <node id='1344032637' timestamp='2015-07-11T21:06:15Z' uid='118021' user='maggot27' version='3' changeset='32570723' lat='37.7695087' lon='-122.3984035'>\n    <tag k='railway' v='level_crossing' />\n    <tag k='usage' v='main' />\n  </node>\n  <node id='1344032639' timestamp='2011-06-30T01:59:34Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7708512' lon='-122.3999061' />\n  <node id='1344032657' timestamp='2011-06-30T01:59:35Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7716916' lon='-122.4001998' />\n  <node id='1344032672' timestamp='2011-06-30T01:59:35Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7708679' lon='-122.4001567' />\n  <node id='1344032673' timestamp='2011-06-30T01:59:35Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7709542' lon='-122.3999749' />\n  <node id='1344032691' timestamp='2011-06-30T01:59:36Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7700669' lon='-122.3992489' />\n  <node id='1344032709' timestamp='2011-06-30T01:59:36Z' uid='169004' user='oldtopos' version='1' changeset='8586501' lat='37.7707627' lon='-122.4000768' />\n  <node id='1409407310' timestamp='2015-01-16T06:52:33Z' uid='355242' user='Bryce C Nesbitt' version='3' changeset='28179793' lat='37.77035' lon='-122.40037'>\n    <tag k='amenity' v='car_sharing' />\n    <tag k='name' v='Berry &amp; 7th' />\n    <tag k='operator' v='City CarShare' />\n    <tag k='ref' v='229' />\n    <tag k='source' v='osmsync:ccs' />\n    <tag k='source:pkey' v='229' />\n    <tag k='website' v='http://www.citycarshare.org/' />\n  </node>\n  <node id='1461189663' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7702353' lon='-122.4069846' />\n  <node id='1461189670' timestamp='2014-07-12T21:45:43Z' uid='371121' user='AndrewSnow' version='3' changeset='24109539' lat='37.7706311' lon='-122.4063017' />\n  <node id='1461189671' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7706171' lon='-122.407037' />\n  <node id='1461189673' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7706616' lon='-122.4064522' />\n  <node id='1461189674' timestamp='2013-11-24T02:30:39Z' uid='371121' user='AndrewSnow' version='2' changeset='19084727' lat='37.7707059' lon='-122.4063969' />\n  <node id='1461189675' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7707298' lon='-122.4063675' />\n  <node id='1461189676' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7708708' lon='-122.4067202' />\n  <node id='1461189677' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7712604' lon='-122.4078614' />\n  <node id='1461189679' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7713' lon='-122.4083491' />\n  <node id='1461189685' timestamp='2013-11-24T02:30:39Z' uid='371121' user='AndrewSnow' version='2' changeset='19084727' lat='37.771489' lon='-122.4073866' />\n  <node id='1461189687' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7715181' lon='-122.4074615' />\n  <node id='1461189689' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7715504' lon='-122.4074992' />\n  <node id='1461189690' timestamp='2011-10-10T13:33:08Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7715623' lon='-122.4074212' />\n  <node id='1461189692' timestamp='2011-10-10T13:33:09Z' uid='93285' user='skela' version='1' changeset='9521974' lat='37.7717627' lon='-122.4077712' />\n  <node id='1485472609' timestamp='2011-10-30T07:15:14Z' uid='202256' user='Telecas' version='1' changeset='9690834' lat='37.7701025' lon='-122.4005184' />\n  <node id='1588951789' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7771373' lon='-122.3902444' />\n  <node id='1588951791' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7772127' lon='-122.3902934' />\n  <node id='1588951793' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7773495' lon='-122.3898645' />\n  <node id='1588951795' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7773718' lon='-122.3898719' />\n  <node id='1588951797' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7774047' lon='-122.3897209' />\n  <node id='1588951799' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7774212' lon='-122.3897295' />\n  <node id='1588951801' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.77746' lon='-122.3895811' />\n  <node id='1588951802' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7774765' lon='-122.3895946' />\n  <node id='1588951804' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7775105' lon='-122.3894424' />\n  <node id='1588951806' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7775269' lon='-122.3894547' />\n  <node id='1588951808' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7775609' lon='-122.3893099' />\n  <node id='1588951810' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7775793' lon='-122.389321' />\n  <node id='1588951812' timestamp='2012-01-13T23:05:23Z' uid='103253' user='gormur' version='1' changeset='10383705' lat='37.7776296' lon='-122.3891804' />\n  <node id='1674115432' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='2' changeset='16233828' lat='37.7692514' lon='-122.4055331' />\n  <node id='1674115433' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='2' changeset='16233828' lat='37.7698111' lon='-122.4056275' />\n  <node id='1674115435' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='2' changeset='16233828' lat='37.7666598' lon='-122.4052777' />\n  <node id='1674115436' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='2' changeset='16233828' lat='37.7708008' lon='-122.405969' />\n  <node id='1674117133' timestamp='2012-10-17T22:33:14Z' uid='207745' user='NE2' version='2' changeset='13538213' lat='37.765253' lon='-122.4049182' />\n  <node id='1677798439' timestamp='2012-03-17T03:33:34Z' uid='14293' user='KindredCoda' version='1' changeset='11004366' lat='37.7687093' lon='-122.4019618' />\n  <node id='1709638627' timestamp='2013-12-12T17:03:38Z' uid='28775' user='StellanL' version='2' changeset='19417081' lat='37.7700241' lon='-122.3983854' />\n  <node id='1709638629' timestamp='2013-12-12T17:03:38Z' uid='28775' user='StellanL' version='2' changeset='19417081' lat='37.7700239' lon='-122.3984197' />\n  <node id='1709638631' timestamp='2013-12-12T17:03:38Z' uid='28775' user='StellanL' version='2' changeset='19417081' lat='37.7701591' lon='-122.3982084' />\n  <node id='1709638633' timestamp='2012-04-10T07:32:57Z' uid='116029' user='Gregory Arenius' version='1' changeset='11248664' lat='37.7701394' lon='-122.3985647' />\n  <node id='1709638634' timestamp='2012-04-10T07:32:57Z' uid='116029' user='Gregory Arenius' version='1' changeset='11248664' lat='37.770225' lon='-122.3985212' />\n  <node id='1709638635' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7703183' lon='-122.398518' />\n  <node id='1709638637' timestamp='2012-04-10T07:32:57Z' uid='116029' user='Gregory Arenius' version='1' changeset='11248664' lat='37.770411' lon='-122.3985591' />\n  <node id='1709638639' timestamp='2012-04-10T07:32:57Z' uid='116029' user='Gregory Arenius' version='1' changeset='11248664' lat='37.7711194' lon='-122.3996683' />\n  <node id='1709638655' timestamp='2012-04-10T07:32:58Z' uid='116029' user='Gregory Arenius' version='1' changeset='11248664' lat='37.7713653' lon='-122.399693' />\n  <node id='1709638657' timestamp='2012-04-10T07:32:58Z' uid='116029' user='Gregory Arenius' version='1' changeset='11248664' lat='37.7715929' lon='-122.3996552' />\n  <node id='1709638660' timestamp='2012-04-10T07:32:58Z' uid='116029' user='Gregory Arenius' version='1' changeset='11248664' lat='37.7717828' lon='-122.3995548' />\n  <node id='1709638662' timestamp='2012-04-10T07:32:58Z' uid='116029' user='Gregory Arenius' version='1' changeset='11248664' lat='37.7718793' lon='-122.3997345' />\n  <node id='1723639055' timestamp='2012-04-20T06:39:55Z' uid='14293' user='KindredCoda' version='1' changeset='11360720' lat='37.7750791' lon='-122.3928519' />\n  <node id='1723639059' timestamp='2013-09-08T00:09:25Z' uid='14293' user='KindredCoda' version='2' changeset='17725564' lat='37.7751019' lon='-122.3928091' />\n  <node id='1723639081' timestamp='2012-04-20T06:39:56Z' uid='14293' user='KindredCoda' version='1' changeset='11360720' lat='37.7749071' lon='-122.3932158' />\n  <node id='1723639090' timestamp='2013-09-08T00:09:25Z' uid='14293' user='KindredCoda' version='2' changeset='17725564' lat='37.7751587' lon='-122.3927586' />\n  <node id='1723639091' timestamp='2012-04-20T06:39:56Z' uid='14293' user='KindredCoda' version='1' changeset='11360720' lat='37.7749936' lon='-122.3930862' />\n  <node id='1723646833' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='3' changeset='58074219' lat='37.7766556' lon='-122.3899859' />\n  <node id='1813413813' timestamp='2015-06-06T23:12:07Z' uid='371121' user='AndrewSnow' version='3' changeset='31779035' lat='37.77164' lon='-122.4034654' />\n  <node id='1813413814' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7711435' lon='-122.4027724' />\n  <node id='1813413815' timestamp='2015-06-20T21:44:42Z' uid='371121' user='AndrewSnow' version='4' changeset='32105635' lat='37.7685544' lon='-122.4031273' />\n  <node id='1813413816' timestamp='2015-06-20T21:44:42Z' uid='371121' user='AndrewSnow' version='4' changeset='32105635' lat='37.7682778' lon='-122.4031126' />\n  <node id='1813413817' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7699291' lon='-122.4034844' />\n  <node id='1813413819' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7701348' lon='-122.4036319' />\n  <node id='1813413820' timestamp='2015-06-20T21:44:42Z' uid='371121' user='AndrewSnow' version='4' changeset='32105635' lat='37.7685163' lon='-122.4037742' />\n  <node id='1813413821' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7710989' lon='-122.4028019' />\n  <node id='1813413823' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='3' changeset='20406949' lat='37.7705539' lon='-122.4030956' />\n  <node id='1813413824' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='3' changeset='20406949' lat='37.7706018' lon='-122.4030284' />\n  <node id='1813413825' timestamp='2015-06-06T23:12:07Z' uid='371121' user='AndrewSnow' version='3' changeset='31779035' lat='37.7716547' lon='-122.4033865' />\n  <node id='1813413826' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='3' changeset='20406949' lat='37.7707016' lon='-122.4025173' />\n  <node id='1813413827' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7699503' lon='-122.4031666' />\n  <node id='1813413828' timestamp='2014-02-06T07:32:24Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7707809' lon='-122.4045132' />\n  <node id='1813413829' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='3' changeset='20406949' lat='37.7701986' lon='-122.4031516' />\n  <node id='1813413830' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700341' lon='-122.4036521' />\n  <node id='1813413831' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700818' lon='-122.403522' />\n  <node id='1813413832' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.770032' lon='-122.4034495' />\n  <node id='1813413833' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='3' changeset='20406949' lat='37.7708529' lon='-122.4027058' />\n  <node id='1813413834' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7707385' lon='-122.404481' />\n  <node id='1813413835' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700659' lon='-122.4031773' />\n  <node id='1813413836' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700808' lon='-122.4034795' />\n  <node id='1813413837' timestamp='2015-06-06T23:12:07Z' uid='371121' user='AndrewSnow' version='3' changeset='31779035' lat='37.7708144' lon='-122.4045092' />\n  <node id='1813413838' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='3' changeset='20406949' lat='37.7703545' lon='-122.4033484' />\n  <node id='1813413839' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7702911' lon='-122.4038721' />\n  <node id='1813413840' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.770104' lon='-122.4035018' />\n  <node id='1813413841' timestamp='2015-06-06T23:12:07Z' uid='371121' user='AndrewSnow' version='3' changeset='31779035' lat='37.7716324' lon='-122.4033416' />\n  <node id='1813413842' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700521' lon='-122.4034469' />\n  <node id='1813413843' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7699853' lon='-122.4035944' />\n  <node id='1813413844' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700298' lon='-122.4034911' />\n  <node id='1813413845' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7702996' lon='-122.4039285' />\n  <node id='1813413846' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='3' changeset='20406949' lat='37.7704505' lon='-122.4028399' />\n  <node id='1813413847' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7699387' lon='-122.4035354' />\n  <node id='1813413848' timestamp='2015-06-20T21:44:42Z' uid='371121' user='AndrewSnow' version='4' changeset='32105635' lat='37.7682424' lon='-122.4037476' />\n  <node id='1813413849' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7711837' lon='-122.4027778' />\n  <node id='1813413850' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7703341' lon='-122.403387' />\n  <node id='1813413851' timestamp='2015-06-04T05:43:57Z' uid='33757' user='Minh Nguyen' version='4' changeset='31708207' lat='37.7703987' lon='-122.4028987' />\n  <node id='1813413852' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700828' lon='-122.4036628' />\n  <node id='1813413853' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7702387' lon='-122.4032752' />\n  <node id='1813413854' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700521' lon='-122.4035166' />\n  <node id='1813413855' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7703039' lon='-122.4038185' />\n  <node id='1880206745' timestamp='2018-01-12T20:59:19Z' uid='371121' user='AndrewSnow' version='4' changeset='55392380' lat='37.7707657' lon='-122.4027645'>\n    <tag k='addr:housenumber' v='655' />\n    <tag k='addr:street' v='Townsend Street' />\n    <tag k='amenity' v='cafe' />\n    <tag k='name' v='Patisserie Bonjour' />\n  </node>\n  <node id='1880206851' timestamp='2018-01-12T20:59:19Z' uid='371121' user='AndrewSnow' version='5' changeset='55392380' lat='37.7707211' lon='-122.4028316'>\n    <tag k='addr:housenumber' v='659' />\n    <tag k='addr:street' v='Townsend Street' />\n    <tag k='amenity' v='fast_food' />\n    <tag k='cuisine' v='burger' />\n    <tag k='name' v='Holy Grill' />\n  </node>\n  <node id='1880207577' timestamp='2012-08-24T00:55:09Z' uid='55774' user='nmixter' version='1' changeset='12839765' lat='37.7706214' lon='-122.4026406'>\n    <tag k='amenity' v='parking' />\n  </node>\n  <node id='1880208351' timestamp='2015-05-21T08:04:13Z' uid='33757' user='Minh Nguyen' version='2' changeset='31336567' lat='37.7701928' lon='-122.4034984'>\n    <tag k='amenity' v='cafe' />\n    <tag k='cuisine' v='coffee_shop' />\n    <tag k='name' v='Starbucks' />\n  </node>\n  <node id='1880208429' timestamp='2015-06-01T16:45:15Z' uid='33757' user='Minh Nguyen' version='2' changeset='31640735' lat='37.7704352' lon='-122.403736'>\n    <tag k='amenity' v='bank' />\n    <tag k='name' v='Union Bank' />\n  </node>\n  <node id='1880208573' timestamp='2015-06-03T02:06:15Z' uid='33757' user='Minh Nguyen' version='2' changeset='31681487' lat='37.7703366' lon='-122.4038355'>\n    <tag k='amenity' v='atm' />\n    <tag k='name' v='Bank of America' />\n  </node>\n  <node id='1880209756' timestamp='2015-06-05T06:32:08Z' uid='33757' user='Minh Nguyen' version='3' changeset='31737345' lat='37.7699814' lon='-122.4044817'>\n    <tag k='name' v='Miele Center' />\n    <tag k='old_name' v='Kreis' />\n    <tag k='shop' v='furniture' />\n    <tag k='website' v='http://www.mieleusa.com/usa/design-centers/design-centers.asp?id=4' />\n  </node>\n  <node id='1880211756' timestamp='2015-06-04T05:43:57Z' uid='33757' user='Minh Nguyen' version='3' changeset='31708207' lat='37.7700958' lon='-122.4048271'>\n    <tag k='name' v='Pak Oriental Rugs' />\n    <tag k='shop' v='furniture' />\n  </node>\n  <node id='1880211977' timestamp='2012-08-24T01:04:06Z' uid='55774' user='nmixter' version='1' changeset='12839844' lat='37.7699091' lon='-122.4055266'>\n    <tag k='amenity' v='parking' />\n  </node>\n  <node id='1880212762' timestamp='2012-08-24T01:08:05Z' uid='55774' user='nmixter' version='1' changeset='12839844' lat='37.7704179' lon='-122.4069268'>\n    <tag k='name' v='Nordstrom Rack' />\n    <tag k='shop' v='clothes' />\n  </node>\n  <node id='1941018589' timestamp='2018-04-01T18:54:00Z' uid='53073' user='Aaron Lidman' version='4' changeset='57720035' lat='37.7681378' lon='-122.4009393' />\n  <node id='1950347608' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='1' changeset='13393870' lat='37.7678313' lon='-122.4014015' />\n  <node id='1950347609' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='1' changeset='13393870' lat='37.7684377' lon='-122.3976303' />\n  <node id='1950347610' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='1' changeset='13393870' lat='37.768107' lon='-122.401423' />\n  <node id='1950347611' timestamp='2013-02-15T16:04:28Z' uid='933797' user='oba510' version='2' changeset='15041764' lat='37.7683592' lon='-122.4004246' />\n  <node id='1950347612' timestamp='2013-02-15T16:04:28Z' uid='933797' user='oba510' version='2' changeset='15041764' lat='37.7675303' lon='-122.4017227' />\n  <node id='1950347613' timestamp='2013-02-15T16:04:27Z' uid='933797' user='oba510' version='2' changeset='15041764' lat='37.7678057' lon='-122.4017536' />\n  <node id='1950347614' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='1' changeset='13393870' lat='37.7681451' lon='-122.4008215' />\n  <node id='1950347615' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='1' changeset='13393870' lat='37.7680867' lon='-122.4007656' />\n  <node id='1950347616' timestamp='2012-10-07T07:14:40Z' uid='933797' user='oba510' version='2' changeset='13393968' lat='37.7676119' lon='-122.4006234' />\n  <node id='1950347617' timestamp='2013-02-15T16:04:27Z' uid='933797' user='oba510' version='2' changeset='15041764' lat='37.7677544' lon='-122.3996667' />\n  <node id='1950347618' timestamp='2012-10-07T07:00:08Z' uid='933797' user='oba510' version='1' changeset='13393870' lat='37.7689084' lon='-122.398215' />\n  <node id='2101121534' timestamp='2016-04-04T07:08:53Z' uid='33757' user='Minh Nguyen' version='4' changeset='38282511' lat='37.776672' lon='-122.395176' />\n  <node id='2156202572' timestamp='2013-02-15T16:04:24Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7677637' lon='-122.4000607' />\n  <node id='2156202578' timestamp='2013-02-15T16:04:24Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7683539' lon='-122.4004198' />\n  <node id='2156202622' timestamp='2013-02-15T16:04:24Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7681206' lon='-122.4012078' />\n  <node id='2156202672' timestamp='2013-02-15T16:04:24Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7680023' lon='-122.4003704' />\n  <node id='2156202674' timestamp='2013-02-15T16:04:24Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7678127' lon='-122.3999993' />\n  <node id='2156202688' timestamp='2013-02-15T16:04:24Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7680892' lon='-122.4004831' />\n  <node id='2156202707' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7675811' lon='-122.4010399' />\n  <node id='2156202740' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7675765' lon='-122.4011' />\n  <node id='2156202749' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7680575' lon='-122.4003046' />\n  <node id='2156202770' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7677898' lon='-122.400028' />\n  <node id='2156202808' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7678867' lon='-122.3998354' />\n  <node id='2156202811' timestamp='2018-09-19T00:38:21Z' uid='53073' user='Aaron Lidman' version='2' changeset='62713402' lat='37.767989' lon='-122.4006101' />\n  <node id='2156202815' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7677624' lon='-122.3999933'>\n    <tag k='entrance' v='main_entrance' />\n  </node>\n  <node id='2156202823' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7675736' lon='-122.4011377' />\n  <node id='2156202832' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.767572' lon='-122.4011588' />\n  <node id='2156202856' timestamp='2017-06-29T07:11:42Z' uid='5659851' user='marthaleena' version='2' changeset='49909094' lat='37.767146' lon='-122.4000245'>\n    <tag k='entrance' v='main_entrance' />\n  </node>\n  <node id='2156202859' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7678062' lon='-122.4017478' />\n  <node id='2156202873' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7675567' lon='-122.4013658' />\n  <node id='2156202901' timestamp='2013-02-15T16:04:25Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7682545' lon='-122.4005502' />\n  <node id='2156214835' timestamp='2013-02-15T16:14:23Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7687493' lon='-122.4030378'>\n    <tag k='highway' v='bus_stop' />\n    <tag k='shelter' v='no' />\n    <tag k='ticker' v='no' />\n  </node>\n  <node id='2156214848' timestamp='2016-09-30T05:56:11Z' uid='1306' user='PlaneMad' version='3' changeset='42533955' lat='37.7698985' lon='-122.4034375'>\n    <tag k='highway' v='bus_stop' />\n    <tag k='name' v='Division St &amp; Camden St' />\n    <tag k='name:en' v='Division St &amp; Camden St' />\n    <tag k='network' v='Muni' />\n    <tag k='shelter' v='yes' />\n    <tag k='ticker' v='yes' />\n  </node>\n  <node id='2156214854' timestamp='2013-02-15T16:14:23Z' uid='933797' user='oba510' version='1' changeset='15041764' lat='37.7685627' lon='-122.4028608'>\n    <tag k='highway' v='bus_stop' />\n    <tag k='shelter' v='no' />\n    <tag k='ticker' v='no' />\n  </node>\n  <node id='2156214857' timestamp='2015-06-26T08:45:12Z' uid='33757' user='Minh Nguyen' version='2' changeset='32220993' lat='37.7697585' lon='-122.4032336'>\n    <tag k='highway' v='bus_stop' />\n    <tag k='network' v='Muni' />\n    <tag k='shelter' v='yes' />\n  </node>\n  <node id='2211243717' timestamp='2013-04-20T20:42:58Z' uid='589596' user='lxbarth' version='2' changeset='15803441' lat='37.7706093' lon='-122.4016819' />\n  <node id='2211243718' timestamp='2013-04-20T20:42:58Z' uid='589596' user='lxbarth' version='2' changeset='15803441' lat='37.7711412' lon='-122.4023299' />\n  <node id='2211243719' timestamp='2013-04-20T20:42:58Z' uid='589596' user='lxbarth' version='2' changeset='15803441' lat='37.7711333' lon='-122.4009935' />\n  <node id='2211243720' timestamp='2013-04-20T20:42:58Z' uid='589596' user='lxbarth' version='2' changeset='15803441' lat='37.7716652' lon='-122.4016414' />\n  <node id='2249879663' timestamp='2014-01-07T10:41:00Z' uid='28775' user='StellanL' version='2' changeset='19860253' lat='37.7716145' lon='-122.3863393' />\n  <node id='2249911751' timestamp='2013-04-05T08:09:18Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.769608' lon='-122.3990862' />\n  <node id='2249911759' timestamp='2013-04-05T08:09:18Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7700416' lon='-122.4008569' />\n  <node id='2249911762' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7689274' lon='-122.4018426' />\n  <node id='2249911764' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7692927' lon='-122.4019062' />\n  <node id='2249911776' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7681289' lon='-122.4010788' />\n  <node id='2249911788' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7699625' lon='-122.4019761' />\n  <node id='2249911791' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7692547' lon='-122.4018566' />\n  <node id='2249911793' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7686793' lon='-122.4009919' />\n  <node id='2249911795' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7683941' lon='-122.4006249' />\n  <node id='2249911801' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7700331' lon='-122.4010321' />\n  <node id='2249911804' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7700055' lon='-122.4011863' />\n  <node id='2249911806' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.768673' lon='-122.4011112' />\n  <node id='2249911813' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228' lat='37.7703554' lon='-122.4000308' />\n  <node id='2274243872' timestamp='2013-04-20T20:42:48Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7674797' lon='-122.4027025' />\n  <node id='2274243877' timestamp='2013-04-20T20:42:48Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7675184' lon='-122.4020016' />\n  <node id='2274243947' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.768143' lon='-122.4017218' />\n  <node id='2274243948' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7681527' lon='-122.4015459' />\n  <node id='2274243954' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7682409' lon='-122.4012915' />\n  <node id='2274243955' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7682512' lon='-122.4011045' />\n  <node id='2274243956' timestamp='2015-06-20T21:44:43Z' uid='371121' user='AndrewSnow' version='3' changeset='32105635' lat='37.7682465' lon='-122.4036799' />\n  <node id='2274243961' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7684978' lon='-122.4023083' />\n  <node id='2274243962' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.76851' lon='-122.4020868' />\n  <node id='2274243963' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7685731' lon='-122.4028024' />\n  <node id='2274243964' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7685999' lon='-122.4023173' />\n  <node id='2274243965' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7686344' lon='-122.4017653' />\n  <node id='2274243966' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7686441' lon='-122.4015893' />\n  <node id='2274243967' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7686585' lon='-122.4013284' />\n  <node id='2274243968' timestamp='2013-04-20T20:42:50Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7686688' lon='-122.4011414' />\n  <node id='2274243971' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7687585' lon='-122.4028227' />\n  <node id='2274243972' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.768798' lon='-122.4021366' />\n  <node id='2274243973' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7690291' lon='-122.4028439' />\n  <node id='2274243976' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7690702' lon='-122.4021645' />\n  <node id='2274243980' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7691739' lon='-122.4028067' />\n  <node id='2274243981' timestamp='2014-01-16T20:35:12Z' uid='1240849' user='ediyes' version='2' changeset='20040567' lat='37.7691818' lon='-122.4007015' />\n  <node id='2274243982' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7692107' lon='-122.4021722' />\n  <node id='2274243983' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7692599' lon='-122.4018506' />\n  <node id='2274243984' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7692949' lon='-122.4018927' />\n  <node id='2274243985' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7693119' lon='-122.4028671' />\n  <node id='2274243986' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7693137' lon='-122.4028221' />\n  <node id='2274243987' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7693518' lon='-122.4017275' />\n  <node id='2274243988' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.769353' lon='-122.4017291' />\n  <node id='2274243989' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7693445' lon='-122.4021862' />\n  <node id='2274243993' timestamp='2014-01-16T20:35:12Z' uid='1240849' user='ediyes' version='2' changeset='20040567' lat='37.7693946' lon='-122.4009673' />\n  <node id='2274243996' timestamp='2014-01-16T20:35:12Z' uid='1240849' user='ediyes' version='2' changeset='20040567' lat='37.7694567' lon='-122.4003493' />\n  <node id='2274243997' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7694675' lon='-122.4000033' />\n  <node id='2274243998' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7695142' lon='-122.4000616' />\n  <node id='2274243999' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7695563' lon='-122.4014754' />\n  <node id='2274244002' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7696066' lon='-122.400177' />\n  <node id='2274244003' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.769617' lon='-122.39993' />\n  <node id='2274244004' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7696562' lon='-122.4016051' />\n  <node id='2274244005' timestamp='2014-01-16T20:35:12Z' uid='1240849' user='ediyes' version='2' changeset='20040567' lat='37.7696695' lon='-122.4006152' />\n  <node id='2274244006' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7696862' lon='-122.4017606' />\n  <node id='2274244008' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7696994' lon='-122.4029118' />\n  <node id='2274244009' timestamp='2013-04-20T20:42:51Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7697093' lon='-122.4000454' />\n  <node id='2274244011' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.769732' lon='-122.4022268' />\n  <node id='2274244012' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7697541' lon='-122.3996379' />\n  <node id='2274244013' timestamp='2013-05-15T06:56:07Z' uid='14293' user='KindredCoda' version='2' changeset='16134749' lat='37.769773' lon='-122.4037545' />\n  <node id='2274244014' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7697909' lon='-122.4004073' />\n  <node id='2274244022' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='6' changeset='63557245' lat='37.7698904' lon='-122.4036185'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='2274244023' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7698608' lon='-122.4013471' />\n  <node id='2274244026' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7698929' lon='-122.4019492' />\n  <node id='2274244028' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7699028' lon='-122.4017811' />\n  <node id='2274244029' timestamp='2013-05-15T06:56:07Z' uid='14293' user='KindredCoda' version='2' changeset='16134749' lat='37.7699206' lon='-122.4036628' />\n  <node id='2274244031' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7699264' lon='-122.3999451' />\n  <node id='2274244034' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7699428' lon='-122.4014587' />\n  <node id='2274244035' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7699627' lon='-122.3998986' />\n  <node id='2274244039' timestamp='2014-02-06T07:32:22Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7699493' lon='-122.4031009' />\n  <node id='2274244044' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='3' changeset='31778944' lat='37.7699776' lon='-122.4025842' />\n  <node id='2274244045' timestamp='2014-02-06T07:32:22Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7699827' lon='-122.4025092' />\n  <node id='2274244046' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='3' changeset='31778944' lat='37.7699899' lon='-122.4024719' />\n  <node id='2274244047' timestamp='2013-04-20T20:42:52Z' uid='589596' user='lxbarth' version='1' changeset='15803441' lat='37.7700405' lon='-122.4000876' />\n  <node id='2274244048' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='3' changeset='31778944' lat='37.7700143' lon='-122.4024258' />\n  <node id='2274244049' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7700703' lon='-122.4017802' />\n  <node id='2274244050' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7700789' lon='-122.4018802' />\n  <node id='2274244051' timestamp='2014-02-06T07:32:22Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700781' lon='-122.4023524' />\n  <node id='2274244052' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7700875' lon='-122.4016954' />\n  <node id='2274244053' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7700875' lon='-122.401941' />\n  <node id='2274244054' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='3' changeset='31778944' lat='37.7700753' lon='-122.4025935' />\n  <node id='2274244055' timestamp='2014-02-06T07:32:22Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7700884' lon='-122.4025276' />\n  <node id='2274244056' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7700961' lon='-122.4017889' />\n  <node id='2274244057' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701017' lon='-122.4011073' />\n  <node id='2274244058' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701135' lon='-122.4013329' />\n  <node id='2274244059' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.770109' lon='-122.4024863' />\n  <node id='2274244060' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='3' changeset='57645807' lat='37.770122' lon='-122.401937' />\n  <node id='2274244061' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701219' lon='-122.4017323' />\n  <node id='2274244062' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701236' lon='-122.4016345' />\n  <node id='2274244063' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701494' lon='-122.4017476' />\n  <node id='2274244064' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7701479' lon='-122.4024373' />\n  <node id='2274244065' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701597' lon='-122.4016845' />\n  <node id='2274244066' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701597' lon='-122.4018584' />\n  <node id='2274244067' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701617' lon='-122.4007889' />\n  <node id='2274244068' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701631' lon='-122.4015845' />\n  <node id='2274244069' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701743' lon='-122.4010096' />\n  <node id='2274244070' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701777' lon='-122.4012465' />\n  <node id='2274244071' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701786' lon='-122.4017845' />\n  <node id='2274244072' timestamp='2014-02-06T07:32:22Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7701727' lon='-122.4031227' />\n  <node id='2274244073' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701803' lon='-122.4015976' />\n  <node id='2274244074' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701829' lon='-122.401703' />\n  <node id='2274244075' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.770207' lon='-122.4006487' />\n  <node id='2274244076' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7702207' lon='-122.4007096' />\n  <node id='2274244077' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='3' changeset='31778944' lat='37.7702133' lon='-122.402607' />\n  <node id='2274244078' timestamp='2014-02-06T07:32:23Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.770232' lon='-122.4025398' />\n  <node id='2274244079' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7702571' lon='-122.4017912' />\n  <node id='2274244080' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7702602' lon='-122.4015074' />\n  <node id='2274244081' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7702686' lon='-122.4005657' />\n  <node id='2274244082' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7703115' lon='-122.4025506' />\n  <node id='2274244083' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7703244' lon='-122.4014209' />\n  <node id='2274244084' timestamp='2014-02-06T07:32:22Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7703179' lon='-122.4020373' />\n  <node id='2274244085' timestamp='2015-06-26T08:45:12Z' uid='33757' user='Minh Nguyen' version='3' changeset='32220993' lat='37.7703297' lon='-122.4020296' />\n  <node id='2274244086' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7703409' lon='-122.4013917' />\n  <node id='2274244087' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7703427' lon='-122.4004661' />\n  <node id='2274244088' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7704145' lon='-122.4003695' />\n  <node id='2274244089' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7704488' lon='-122.4027212' />\n  <node id='2274244090' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7704639' lon='-122.4023544' />\n  <node id='2274244091' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7704376' lon='-122.4013227' />\n  <node id='2274244092' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7704458' lon='-122.400327' />\n  <node id='2274244093' timestamp='2014-02-06T07:32:22Z' uid='1917271' user='Adam Alpern' version='2' changeset='20406949' lat='37.7704718' lon='-122.4022248' />\n  <node id='2274244094' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7704922' lon='-122.4013355' />\n  <node id='2274244095' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7705008' lon='-122.4002574' />\n  <node id='2274244096' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7705172' lon='-122.4012116' />\n  <node id='2274244097' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7705472' lon='-122.4012595' />\n  <node id='2274244098' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7705472' lon='-122.4014008' />\n  <node id='2274244099' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7705558' lon='-122.400364' />\n  <node id='2274244100' timestamp='2015-06-26T08:45:12Z' uid='33757' user='Minh Nguyen' version='3' changeset='32220993' lat='37.7705661' lon='-122.4017266' />\n  <node id='2274244101' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7706012' lon='-122.402525' />\n  <node id='2274244102' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7705747' lon='-122.4003422' />\n  <node id='2274244103' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7705867' lon='-122.4004053' />\n  <node id='2274244104' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7705872' lon='-122.4011454' />\n  <node id='2274244105' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7706004' lon='-122.4013356' />\n  <node id='2274244106' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7706039' lon='-122.4003835' />\n  <node id='2274244107' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7706058' lon='-122.401123' />\n  <node id='2274244108' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7706675' lon='-122.40104' />\n  <node id='2274244109' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7707413' lon='-122.4010813' />\n  <node id='2274244110' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7707431' lon='-122.4009422' />\n  <node id='2274244111' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7707671' lon='-122.4011139' />\n  <node id='2274244112' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7707774' lon='-122.4010357' />\n  <node id='2274244113' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7707998' lon='-122.4010639' />\n  <node id='2274244114' timestamp='2015-06-26T08:45:12Z' uid='33757' user='Minh Nguyen' version='3' changeset='32220993' lat='37.7708663' lon='-122.4027036' />\n  <node id='2274244115' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7709733' lon='-122.4008422' />\n  <node id='2274244116' timestamp='2015-06-26T08:45:12Z' uid='33757' user='Minh Nguyen' version='3' changeset='32220993' lat='37.7711041' lon='-122.4024023' />\n  <node id='2282804185' timestamp='2014-06-18T18:01:20Z' uid='510836' user='Rub21' version='2' changeset='23010210' lat='37.7672673' lon='-122.4105185' />\n  <node id='2282804186' timestamp='2013-04-27T06:51:18Z' uid='416346' user='Brian@Brea' version='1' changeset='15879940' lat='37.7669957' lon='-122.4078399' />\n  <node id='2282804187' timestamp='2013-04-27T06:51:18Z' uid='416346' user='Brian@Brea' version='1' changeset='15879940' lat='37.7669679' lon='-122.4085767' />\n  <node id='2282804188' timestamp='2014-07-20T23:35:35Z' uid='28775' user='StellanL' version='2' changeset='24263865' lat='37.7658684' lon='-122.4077252' />\n  <node id='2282804189' timestamp='2013-04-27T06:51:18Z' uid='416346' user='Brian@Brea' version='1' changeset='15879940' lat='37.767375' lon='-122.4088771' />\n  <node id='2282804191' timestamp='2013-04-27T06:51:18Z' uid='416346' user='Brian@Brea' version='1' changeset='15879940' lat='37.7671969' lon='-122.4088637' />\n  <node id='2289187320' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7697543' lon='-122.4063438' />\n  <node id='2289187323' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7697578' lon='-122.4063811' />\n  <node id='2289187324' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7697682' lon='-122.4064108' />\n  <node id='2289187326' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.769776' lon='-122.4060123' />\n  <node id='2289187328' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7697951' lon='-122.4059782' />\n  <node id='2289187330' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7698263' lon='-122.4059618' />\n  <node id='2289187332' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7699729' lon='-122.4059651' />\n  <node id='2289187334' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7700163' lon='-122.4067357' />\n  <node id='2289187335' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7700649' lon='-122.4067533' />\n  <node id='2289187337' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.77011' lon='-122.4067445' />\n  <node id='2289187341' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7702506' lon='-122.4060145' />\n  <node id='2289187350' timestamp='2013-05-02T14:17:42Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7703998' lon='-122.4060496' />\n  <node id='2289187353' timestamp='2013-05-02T14:17:43Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7704744' lon='-122.4060749' />\n  <node id='2289187357' timestamp='2013-05-02T14:17:43Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7705048' lon='-122.4062406' />\n  <node id='2289187360' timestamp='2013-05-02T14:17:43Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7705091' lon='-122.4061067' />\n  <node id='2289187362' timestamp='2013-05-02T14:17:43Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7705239' lon='-122.4061561' />\n  <node id='2289187363' timestamp='2013-05-02T14:17:43Z' uid='214969' user='AndrewBuck' version='1' changeset='15946957' lat='37.7705239' lon='-122.4062011' />\n  <node id='2289187385' timestamp='2014-07-12T21:45:43Z' uid='371121' user='AndrewSnow' version='2' changeset='24109539' lat='37.7707716' lon='-122.4063203' />\n  <node id='2289187390' timestamp='2014-07-12T21:45:43Z' uid='371121' user='AndrewSnow' version='2' changeset='24109539' lat='37.7708239' lon='-122.4062553' />\n  <node id='2289187400' timestamp='2014-07-12T21:45:43Z' uid='371121' user='AndrewSnow' version='2' changeset='24109539' lat='37.7708766' lon='-122.406242' />\n  <node id='2289187412' timestamp='2014-07-12T21:45:43Z' uid='371121' user='AndrewSnow' version='2' changeset='24109539' lat='37.7709508' lon='-122.4062743' />\n  <node id='2289187429' timestamp='2014-07-12T21:45:43Z' uid='371121' user='AndrewSnow' version='2' changeset='24109539' lat='37.7710588' lon='-122.4063275' />\n  <node id='2289187464' timestamp='2014-07-12T21:45:43Z' uid='371121' user='AndrewSnow' version='2' changeset='24109539' lat='37.771207' lon='-122.4068676' />\n  <node id='2289187496' timestamp='2014-07-12T21:45:43Z' uid='371121' user='AndrewSnow' version='2' changeset='24109539' lat='37.7713487' lon='-122.4066911' />\n  <node id='2304626285' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699441' lon='-122.4040258' />\n  <node id='2304626286' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698895' lon='-122.4037332' />\n  <node id='2304626287' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='3' changeset='63557245' lat='37.7697993' lon='-122.4040204' />\n  <node id='2304626288' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700461' lon='-122.4039803' />\n  <node id='2304626289' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700111' lon='-122.4039044' />\n  <node id='2304626292' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699152' lon='-122.4040926' />\n  <node id='2304626294' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698243' lon='-122.403917' />\n  <node id='2304626295' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699796' lon='-122.4040723' />\n  <node id='2304626296' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698638' lon='-122.4040089' />\n  <node id='2304626298' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699483' lon='-122.4040872' />\n  <node id='2304626299' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698008' lon='-122.4038003' />\n  <node id='2304626300' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698822' lon='-122.4040217' />\n  <node id='2304626301' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700503' lon='-122.4038576' />\n  <node id='2304626302' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699234' lon='-122.4040304' />\n  <node id='2304626303' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699024' lon='-122.404029' />\n  <node id='2304626304' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700045' lon='-122.4039566' />\n  <node id='2304626305' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698359' lon='-122.4039695' />\n  <node id='2304626306' timestamp='2013-05-15T06:56:05Z' uid='14293' user='KindredCoda' version='1' changeset='16134749' lat='37.7698079' lon='-122.4034486' />\n  <node id='2304626307' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700165' lon='-122.4037857' />\n  <node id='2304626308' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698224' lon='-122.404051' />\n  <node id='2304626309' timestamp='2013-05-15T06:56:05Z' uid='14293' user='KindredCoda' version='1' changeset='16134749' lat='37.769792' lon='-122.4035068' />\n  <node id='2304626310' timestamp='2013-05-15T06:56:05Z' uid='14293' user='KindredCoda' version='1' changeset='16134749' lat='37.7698283' lon='-122.4034575' />\n  <node id='2304626311' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698819' lon='-122.4040881' />\n  <node id='2304626312' timestamp='2018-10-16T00:41:01Z' uid='2237750' user='chachafish' version='5' changeset='63559070' lat='37.7697023' lon='-122.4040067'>\n    <tag k='crossing' v='uncontrolled' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='2304626313' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.769959' lon='-122.4037399' />\n  <node id='2304626314' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698279' lon='-122.4039449' />\n  <node id='2304626316' timestamp='2013-05-15T06:56:05Z' uid='14293' user='KindredCoda' version='1' changeset='16134749' lat='37.769784' lon='-122.4035594' />\n  <node id='2304626317' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699663' lon='-122.4038111' />\n  <node id='2304626318' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698331' lon='-122.403862' />\n  <node id='2304626319' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700367' lon='-122.4038191' />\n  <node id='2304626320' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='5' changeset='63557245' lat='37.7701309' lon='-122.4040106'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='2304626321' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699246' lon='-122.4037311' />\n  <node id='2304626322' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698257' lon='-122.4037689' />\n  <node id='2304626323' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700101' lon='-122.403931' />\n  <node id='2304626324' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698803' lon='-122.4038039' />\n  <node id='2304626325' timestamp='2013-05-15T06:56:05Z' uid='14293' user='KindredCoda' version='1' changeset='16134749' lat='37.7698646' lon='-122.4035684' />\n  <node id='2304626327' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.769924' lon='-122.4037942' />\n  <node id='2304626329' timestamp='2013-05-15T06:56:05Z' uid='14293' user='KindredCoda' version='1' changeset='16134749' lat='37.7698431' lon='-122.4035145' />\n  <node id='2304626330' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='6' changeset='63557245' lat='37.7701231' lon='-122.4038029'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='2304626333' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698481' lon='-122.4039912' />\n  <node id='2304626334' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7697586' lon='-122.4043663' />\n  <node id='2304626335' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699839' lon='-122.4038286' />\n  <node id='2304626336' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700557' lon='-122.403919' />\n  <node id='2304626337' timestamp='2013-05-15T06:56:05Z' uid='14293' user='KindredCoda' version='1' changeset='16134749' lat='37.7700872' lon='-122.4038348' />\n  <node id='2304626338' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699977' lon='-122.4038509' />\n  <node id='2304626340' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700551' lon='-122.4039396' />\n  <node id='2304626342' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700564' lon='-122.4038982' />\n  <node id='2304626343' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.769946' lon='-122.4037994' />\n  <node id='2304626345' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699635' lon='-122.4040155' />\n  <node id='2304626346' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.769845' lon='-122.403838' />\n  <node id='2304626347' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699945' lon='-122.40398' />\n  <node id='2304626348' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.770007' lon='-122.4038766' />\n  <node id='2304626349' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699806' lon='-122.404' />\n  <node id='2304626350' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699017' lon='-122.4037957' />\n  <node id='2304626352' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698504' lon='-122.404074' />\n  <node id='2304626355' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7699907' lon='-122.4037591' />\n  <node id='2304626356' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698558' lon='-122.403746' />\n  <node id='2304626357' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7698261' lon='-122.4038888' />\n  <node id='2304626358' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7697706' lon='-122.4039159' />\n  <node id='2304626359' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7697727' lon='-122.403881' />\n  <node id='2304626360' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700073' lon='-122.4040485' />\n  <node id='2304626361' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7697828' lon='-122.4038385' />\n  <node id='2304626363' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7700298' lon='-122.4040173' />\n  <node id='2304626364' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.769861' lon='-122.4038182' />\n  <node id='2315555372' timestamp='2013-05-22T06:53:45Z' uid='14293' user='KindredCoda' version='1' changeset='16233758' lat='37.7689762' lon='-122.4090901' />\n  <node id='2315558265' timestamp='2013-05-22T07:03:52Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7706789' lon='-122.405914' />\n  <node id='2315558266' timestamp='2013-05-22T07:03:52Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7715269' lon='-122.4063594' />\n  <node id='2315558267' timestamp='2013-05-22T07:03:52Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7705199' lon='-122.4058451' />\n  <node id='2315558268' timestamp='2013-05-22T07:03:52Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7696225' lon='-122.4055869' />\n  <node id='2315558269' timestamp='2013-05-22T07:03:52Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7710977' lon='-122.4061189' />\n  <node id='2315558270' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.770194' lon='-122.4057289' />\n  <node id='2315558271' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7703612' lon='-122.405783' />\n  <node id='2315558272' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7699919' lon='-122.4056709' />\n  <node id='2315558273' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7709491' lon='-122.4060418' />\n  <node id='2315558274' timestamp='2013-05-22T07:03:53Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7694571' lon='-122.4055602' />\n  <node id='2315558589' timestamp='2013-05-22T07:04:52Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7692578' lon='-122.4072485' />\n  <node id='2315558590' timestamp='2013-05-22T07:04:52Z' uid='14293' user='KindredCoda' version='1' changeset='16233828' lat='37.7694743' lon='-122.4075789' />\n  <node id='2450032769' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7766787' lon='-122.3907361' />\n  <node id='2450032771' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7756283' lon='-122.3920625' />\n  <node id='2450032772' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.776485' lon='-122.3909617' />\n  <node id='2450032773' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7766436' lon='-122.3908018' />\n  <node id='2450032774' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58074219' lat='37.7766466' lon='-122.3898729' />\n  <node id='2450032775' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7750643' lon='-122.392623' />\n  <node id='2450032777' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7754445' lon='-122.3922946' />\n  <node id='2450032779' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7761181' lon='-122.3914729' />\n  <node id='2450032784' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.777082' lon='-122.3902678' />\n  <node id='2450032788' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7764925' lon='-122.3909712' />\n  <node id='2450032789' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58074219' lat='37.776629' lon='-122.3899071' />\n  <node id='2450032790' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7752224' lon='-122.392424' />\n  <node id='2450032791' timestamp='2014-05-08T22:23:02Z' uid='153669' user='dchiles' version='2' changeset='22220359' lat='37.776969' lon='-122.3902496' />\n  <node id='2450032793' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7756208' lon='-122.392053' />\n  <node id='2450032794' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7751282' lon='-122.3927816' />\n  <node id='2450032795' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7755896' lon='-122.3921339' />\n  <node id='2450032796' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7754981' lon='-122.3922511' />\n  <node id='2450032797' timestamp='2013-09-08T00:09:22Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.775549' lon='-122.3921963' />\n  <node id='2450032798' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58074219' lat='37.7766357' lon='-122.3899694' />\n  <node id='2450032799' timestamp='2018-04-13T19:37:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58074219' lat='37.7766762' lon='-122.3899459' />\n  <node id='2450032802' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.775437' lon='-122.3922851' />\n  <node id='2450032803' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7759816' lon='-122.3915975' />\n  <node id='2450032804' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7751265' lon='-122.3927194' />\n  <node id='2450032806' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7755699' lon='-122.3921668' />\n  <node id='2450032807' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7765446' lon='-122.3909263' />\n  <node id='2450032808' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7760147' lon='-122.3915844' />\n  <node id='2450032813' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7755242' lon='-122.3922253' />\n  <node id='2450032814' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7760427' lon='-122.3915605' />\n  <node id='2450032819' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7768474' lon='-122.3904858' />\n  <node id='2450032821' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7766712' lon='-122.3907266' />\n  <node id='2450032826' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7751333' lon='-122.3927118' />\n  <node id='2450032827' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7765723' lon='-122.3909005' />\n  <node id='2450032828' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.776598' lon='-122.3908711' />\n  <node id='2450032829' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7759891' lon='-122.391607' />\n  <node id='2450032830' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7761665' lon='-122.391364' />\n  <node id='2450032831' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.776156' lon='-122.3914047' />\n  <node id='2450032833' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.776174' lon='-122.3913735' />\n  <node id='2450032834' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7766231' lon='-122.3908371' />\n  <node id='2450032835' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.776096' lon='-122.3915042' />\n  <node id='2450032837' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7766604' lon='-122.3907692' />\n  <node id='2450032838' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7765177' lon='-122.3909478' />\n  <node id='2450032839' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7760718' lon='-122.391533' />\n  <node id='2450032842' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7754707' lon='-122.3922728' />\n  <node id='2450032843' timestamp='2013-09-08T00:09:23Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7761357' lon='-122.3914424' />\n  <node id='2450032846' timestamp='2014-05-08T22:23:02Z' uid='153669' user='dchiles' version='2' changeset='22220359' lat='37.7770483' lon='-122.3903205' />\n  <node id='2450032849' timestamp='2013-09-08T00:09:24Z' uid='14293' user='KindredCoda' version='1' changeset='17725564' lat='37.7756081' lon='-122.3920984' />\n  <node id='2508652379' timestamp='2013-10-25T18:23:12Z' uid='1775756' user='ets_2016' version='1' changeset='18540384' lat='37.7708973' lon='-122.4051457' />\n  <node id='2508652383' timestamp='2014-01-16T20:44:32Z' uid='510836' user='Rub21' version='2' changeset='20040699' lat='37.7699175' lon='-122.4046583' />\n  <node id='2508652394' timestamp='2014-01-16T20:44:32Z' uid='510836' user='Rub21' version='2' changeset='20040699' lat='37.770077' lon='-122.4041061' />\n  <node id='2508652398' timestamp='2014-01-16T20:44:32Z' uid='510836' user='Rub21' version='2' changeset='20040699' lat='37.7698747' lon='-122.4043573' />\n  <node id='2508652407' timestamp='2015-06-01T16:45:16Z' uid='33757' user='Minh Nguyen' version='2' changeset='31640735' lat='37.7698945' lon='-122.4050798' />\n  <node id='2508652431' timestamp='2014-01-16T20:44:32Z' uid='510836' user='Rub21' version='2' changeset='20040699' lat='37.7698621' lon='-122.4046533' />\n  <node id='2508652457' timestamp='2013-10-25T18:23:14Z' uid='1775756' user='ets_2016' version='1' changeset='18540384' lat='37.7707107' lon='-122.4053764' />\n  <node id='2548141867' timestamp='2015-05-21T08:04:14Z' uid='33757' user='Minh Nguyen' version='3' changeset='31336567' lat='37.7713038' lon='-122.4054511'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='2613221255' timestamp='2014-01-07T10:40:58Z' uid='28775' user='StellanL' version='1' changeset='19860253' lat='37.771669' lon='-122.3863895' />\n  <node id='2613221266' timestamp='2014-01-07T10:40:59Z' uid='28775' user='StellanL' version='1' changeset='19860253' lat='37.7712137' lon='-122.3861929' />\n  <node id='2613221271' timestamp='2018-02-05T10:11:51Z' uid='933797' user='oba510' version='2' changeset='56080356' lat='37.7711676' lon='-122.3860748' />\n  <node id='2613221282' timestamp='2014-01-07T10:40:59Z' uid='28775' user='StellanL' version='1' changeset='19860253' lat='37.7715904' lon='-122.3863153' />\n  <node id='2613221288' timestamp='2018-02-05T10:11:51Z' uid='933797' user='oba510' version='2' changeset='56080356' lat='37.7711675' lon='-122.3861564' />\n  <node id='2613221289' timestamp='2014-01-07T10:40:59Z' uid='28775' user='StellanL' version='1' changeset='19860253' lat='37.7714839' lon='-122.3863871' />\n  <node id='2613280503' timestamp='2014-01-07T11:57:27Z' uid='28775' user='StellanL' version='1' changeset='19861380' lat='37.7731188' lon='-122.394124' />\n  <node id='2617273740' timestamp='2014-01-10T13:33:03Z' uid='589596' user='lxbarth' version='2' changeset='19917439' lat='37.7709085' lon='-122.4003549' />\n  <node id='2617273741' timestamp='2014-01-10T13:33:04Z' uid='589596' user='lxbarth' version='2' changeset='19917439' lat='37.7709529' lon='-122.4002957' />\n  <node id='2617273742' timestamp='2014-01-10T13:33:04Z' uid='589596' user='lxbarth' version='2' changeset='19917439' lat='37.7710322' lon='-122.4005033' />\n  <node id='2617273743' timestamp='2014-01-10T13:33:04Z' uid='589596' user='lxbarth' version='2' changeset='19917439' lat='37.7710383' lon='-122.4004944' />\n  <node id='2617273744' timestamp='2014-01-10T13:33:04Z' uid='589596' user='lxbarth' version='2' changeset='19917439' lat='37.7710766' lon='-122.4004441' />\n  <node id='2617273746' timestamp='2014-01-10T13:33:04Z' uid='589596' user='lxbarth' version='2' changeset='19917439' lat='37.771093' lon='-122.400423' />\n  <node id='2617273748' timestamp='2014-01-10T13:33:04Z' uid='589596' user='lxbarth' version='2' changeset='19917439' lat='37.7711563' lon='-122.400639' />\n  <node id='2617273752' timestamp='2014-01-10T13:33:04Z' uid='589596' user='lxbarth' version='2' changeset='19917439' lat='37.771211' lon='-122.4005675' />\n  <node id='2625194640' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540' lat='37.7695004' lon='-122.4048355' />\n  <node id='2625194641' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540' lat='37.7695035' lon='-122.4047403' />\n  <node id='2625194642' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540' lat='37.7695295' lon='-122.404837' />\n  <node id='2625194643' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540' lat='37.7695325' lon='-122.4047418' />\n  <node id='2625194644' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540' lat='37.769533' lon='-122.4047264' />\n  <node id='2625194645' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540' lat='37.7695634' lon='-122.4048387' />\n  <node id='2625194646' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540' lat='37.7695669' lon='-122.4047281' />\n  <node id='2625206735' timestamp='2014-01-16T20:44:31Z' uid='510836' user='Rub21' version='1' changeset='20040699' lat='37.7707593' lon='-122.4053161' />\n  <node id='2625206736' timestamp='2014-01-16T20:44:31Z' uid='510836' user='Rub21' version='1' changeset='20040699' lat='37.770792' lon='-122.4052779' />\n  <node id='2625206737' timestamp='2014-01-16T20:44:31Z' uid='510836' user='Rub21' version='1' changeset='20040699' lat='37.7708335' lon='-122.4053265' />\n  <node id='2625206738' timestamp='2014-01-16T20:44:31Z' uid='510836' user='Rub21' version='1' changeset='20040699' lat='37.770885' lon='-122.4054754' />\n  <node id='2625206739' timestamp='2014-01-16T20:44:31Z' uid='510836' user='Rub21' version='1' changeset='20040699' lat='37.7708982' lon='-122.4052435' />\n  <node id='2625206740' timestamp='2014-01-16T20:44:31Z' uid='510836' user='Rub21' version='1' changeset='20040699' lat='37.7710372' lon='-122.4055808' />\n  <node id='2625206741' timestamp='2014-01-16T20:44:31Z' uid='510836' user='Rub21' version='1' changeset='20040699' lat='37.7711019' lon='-122.4054978' />\n  <node id='2657227481' timestamp='2018-10-15T15:45:22Z' uid='2237750' user='chachafish' version='4' changeset='63546947' lat='37.7699755' lon='-122.4030009'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='88' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Division Street' />\n    <tag k='amenity' v='restaurant' />\n    <tag k='cuisine' v='thai' />\n    <tag k='disused:amenity' v='restaurant' />\n    <tag k='name' v='Grand Pu Bah' />\n    <tag k='phone' v='(415) 255-8188' />\n    <tag k='website' v='http://grandpubah.com/' />\n  </node>\n  <node id='2671868148' timestamp='2017-07-23T21:05:50Z' uid='33757' user='Minh Nguyen' version='2' changeset='50511534' lat='37.771058' lon='-122.402717'>\n    <tag k='amenity' v='bicycle_rental' />\n    <tag k='capacity' v='15' />\n    <tag k='name' v='Townsend at 7th' />\n    <tag k='network' v='Ford GoBike' />\n    <tag k='network:wikidata' v='Q16971391' />\n    <tag k='operator' v='Motivate' />\n    <tag k='operator:wikidata' v='Q4735954' />\n  </node>\n  <node id='2922027486' timestamp='2014-06-18T15:15:30Z' uid='510836' user='Rub21' version='1' changeset='23007262' lat='37.7748231' lon='-122.3934881' />\n  <node id='2922027488' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7748349' lon='-122.3935066' />\n  <node id='2922027489' timestamp='2014-06-18T15:15:31Z' uid='510836' user='Rub21' version='1' changeset='23007262' lat='37.7748856' lon='-122.3934117' />\n  <node id='2922027500' timestamp='2014-06-18T15:15:31Z' uid='510836' user='Rub21' version='1' changeset='23007262' lat='37.7750062' lon='-122.3932679' />\n  <node id='2922028312' timestamp='2014-06-18T15:15:31Z' uid='510836' user='Rub21' version='1' changeset='23007262' lat='37.7750629' lon='-122.3931949' />\n  <node id='2922028340' timestamp='2014-06-18T15:15:31Z' uid='510836' user='Rub21' version='1' changeset='23007262' lat='37.7752879' lon='-122.3929125' />\n  <node id='2922128564' timestamp='2018-10-30T06:09:43Z' uid='119881' user='clay_c' version='3' changeset='64000226' lat='37.7665023' lon='-122.3947384'>\n    <tag k='railway' v='level_crossing' />\n  </node>\n  <node id='2922201661' timestamp='2014-06-18T18:01:18Z' uid='510836' user='Rub21' version='1' changeset='23010210' lat='37.7680132' lon='-122.4044333' />\n  <node id='2922201662' timestamp='2014-06-18T18:01:18Z' uid='510836' user='Rub21' version='1' changeset='23010210' lat='37.7680137' lon='-122.4043848' />\n  <node id='2922201663' timestamp='2014-06-18T18:01:18Z' uid='510836' user='Rub21' version='1' changeset='23010210' lat='37.7680314' lon='-122.4044005' />\n  <node id='2922201664' timestamp='2014-06-18T18:01:18Z' uid='510836' user='Rub21' version='1' changeset='23010210' lat='37.7680406' lon='-122.4043363' />\n  <node id='2922201667' timestamp='2014-06-18T18:01:18Z' uid='510836' user='Rub21' version='1' changeset='23010210' lat='37.7680583' lon='-122.4043519' />\n  <node id='2922201668' timestamp='2014-06-18T18:01:18Z' uid='510836' user='Rub21' version='1' changeset='23010210' lat='37.7680774' lon='-122.4043175' />\n  <node id='2922201679' timestamp='2014-06-18T18:01:18Z' uid='510836' user='Rub21' version='1' changeset='23010210' lat='37.7682815' lon='-122.4046711' />\n  <node id='2922201680' timestamp='2014-06-18T18:01:18Z' uid='510836' user='Rub21' version='1' changeset='23010210' lat='37.7683456' lon='-122.4045554' />\n  <node id='2922218278' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286' lat='37.7719846' lon='-122.3957814' />\n  <node id='2922219017' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.772651' lon='-122.3948105' />\n  <node id='2922227248' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7742299' lon='-122.3942703' />\n  <node id='2922256881' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7710741' lon='-122.3994727' />\n  <node id='2922256882' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711072' lon='-122.3994237' />\n  <node id='2922256883' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711101' lon='-122.3994272' />\n  <node id='2922256884' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711337' lon='-122.3993895' />\n  <node id='2922256885' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711354' lon='-122.3995491' />\n  <node id='2922256886' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711369' lon='-122.3993934' />\n  <node id='2922256887' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711537' lon='-122.3993649' />\n  <node id='2922256888' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711566' lon='-122.3993684' />\n  <node id='2922256889' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711639' lon='-122.3995368' />\n  <node id='2922256890' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711744' lon='-122.3995217' />\n  <node id='2922256891' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711756' lon='-122.3995233' />\n  <node id='2922256892' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711821' lon='-122.39949' />\n  <node id='2922256893' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711846' lon='-122.399564' />\n  <node id='2922256894' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711914' lon='-122.3995016' />\n  <node id='2922256895' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711958' lon='-122.3995511' />\n  <node id='2922256896' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711987' lon='-122.399307' />\n  <node id='2922256897' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7711988' lon='-122.399307' />\n  <node id='2922256898' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712027' lon='-122.3993118' />\n  <node id='2922256899' timestamp='2014-06-18T18:40:03Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712154' lon='-122.3995782' />\n  <node id='2922256900' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712155' lon='-122.3995782' />\n  <node id='2922257001' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712348' lon='-122.3995554' />\n  <node id='2922257002' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712387' lon='-122.3992585' />\n  <node id='2922257003' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712418' lon='-122.3992623' />\n  <node id='2922257004' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712518' lon='-122.3995765' />\n  <node id='2922257005' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712519' lon='-122.3995765' />\n  <node id='2922257006' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712652' lon='-122.3992243' />\n  <node id='2922257007' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712838' lon='-122.3995211' />\n  <node id='2922257008' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712857' lon='-122.3991999' />\n  <node id='2922257009' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712901' lon='-122.3992572' />\n  <node id='2922257010' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712901' lon='-122.3995299' />\n  <node id='2922257011' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712913' lon='-122.3992557' />\n  <node id='2922257012' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7712934' lon='-122.3995098' />\n  <node id='2922257013' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713037' lon='-122.3993697' />\n  <node id='2922257014' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713098' lon='-122.3992291' />\n  <node id='2922257015' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771312' lon='-122.3992263' />\n  <node id='2922257016' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713122' lon='-122.3991658' />\n  <node id='2922257017' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713144' lon='-122.3991685' />\n  <node id='2922257018' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713239' lon='-122.399299' />\n  <node id='2922257019' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771325' lon='-122.3993986' />\n  <node id='2922257020' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713356' lon='-122.399386' />\n  <node id='2922257021' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771344' lon='-122.3995783' />\n  <node id='2922257022' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713474' lon='-122.3992683' />\n  <node id='2922257023' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713523' lon='-122.3991146' />\n  <node id='2922257024' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713548' lon='-122.3991177' />\n  <node id='2922257025' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713653' lon='-122.3994533' />\n  <node id='2922257026' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713762' lon='-122.3994404' />\n  <node id='2922257027' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713767' lon='-122.3992787' />\n  <node id='2922257028' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713832' lon='-122.3993202' />\n  <node id='2922257029' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713917' lon='-122.3990662' />\n  <node id='2922257030' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713941' lon='-122.3990692' />\n  <node id='2922257031' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7713951' lon='-122.3993082' />\n  <node id='2922257032' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714058' lon='-122.3995083' />\n  <node id='2922257033' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714205' lon='-122.399379' />\n  <node id='2922257034' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714321' lon='-122.3993674' />\n  <node id='2922257035' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714412' lon='-122.3994139' />\n  <node id='2922257036' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714432' lon='-122.3990066' />\n  <node id='2922257037' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714433' lon='-122.3990066' />\n  <node id='2922257038' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714537' lon='-122.3994019' />\n  <node id='2922257039' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714627' lon='-122.399031' />\n  <node id='2922257040' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714773' lon='-122.3994742' />\n  <node id='2922257041' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714908' lon='-122.3994613' />\n  <node id='2922257042' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7714923' lon='-122.398937' />\n  <node id='2922257043' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715007' lon='-122.3991311' />\n  <node id='2922257044' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715151' lon='-122.3989644' />\n  <node id='2922257045' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715185' lon='-122.3989043' />\n  <node id='2922257046' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715219' lon='-122.3989084' />\n  <node id='2922257047' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715274' lon='-122.3991758' />\n  <node id='2922257048' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771528' lon='-122.3995208' />\n  <node id='2922257049' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715281' lon='-122.3995208' />\n  <node id='2922257050' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715335' lon='-122.3991698' />\n  <node id='2922257051' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715336' lon='-122.3991698' />\n  <node id='2922257052' timestamp='2014-06-18T18:40:04Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715349' lon='-122.399172' />\n  <node id='2922257053' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715456' lon='-122.3991617' />\n  <node id='2922257054' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715457' lon='-122.3991617' />\n  <node id='2922257055' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715527' lon='-122.3992004' />\n  <node id='2922257056' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.77156' lon='-122.3988505' />\n  <node id='2922257057' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715619' lon='-122.3991916' />\n  <node id='2922257058' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715636' lon='-122.3988553' />\n  <node id='2922257059' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715637' lon='-122.3988553' />\n  <node id='2922257060' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715719' lon='-122.3990447' />\n  <node id='2922257061' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715733' lon='-122.3992333' />\n  <node id='2922257062' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715751' lon='-122.3995148' />\n  <node id='2922257063' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715754' lon='-122.3994731' />\n  <node id='2922257064' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715859' lon='-122.3992213' />\n  <node id='2922257065' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771586' lon='-122.3992213' />\n  <node id='2922257066' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715889' lon='-122.3995416' />\n  <node id='2922257067' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715918' lon='-122.3994997' />\n  <node id='2922257068' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715981' lon='-122.399071' />\n  <node id='2922257069' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715982' lon='-122.3990711' />\n  <node id='2922257070' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7715993' lon='-122.3988021' />\n  <node id='2922257071' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716023' lon='-122.3988058' />\n  <node id='2922257072' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716079' lon='-122.399055' />\n  <node id='2922257073' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716104' lon='-122.3992926' />\n  <node id='2922257074' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716222' lon='-122.3992813' />\n  <node id='2922257075' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716319' lon='-122.3993269' />\n  <node id='2922257076' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716343' lon='-122.3994976' />\n  <node id='2922257077' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771641' lon='-122.3995083' />\n  <node id='2922257078' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716441' lon='-122.3993153' />\n  <node id='2922257079' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771647' lon='-122.3987416' />\n  <node id='2922257080' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.77165' lon='-122.3987452' />\n  <node id='2922257081' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716691' lon='-122.3993862' />\n  <node id='2922257082' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716734' lon='-122.3987075' />\n  <node id='2922257083' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716778' lon='-122.3987127' />\n  <node id='2922257084' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7716803' lon='-122.3993754' />\n  <node id='2922257085' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717057' lon='-122.3994447' />\n  <node id='2922257086' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717134' lon='-122.3986674' />\n  <node id='2922257087' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717144' lon='-122.3988469' />\n  <node id='2922257088' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717251' lon='-122.3988586' />\n  <node id='2922257089' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771728' lon='-122.3988621' />\n  <node id='2922257090' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717518' lon='-122.3991916' />\n  <node id='2922257091' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717565' lon='-122.3991841' />\n  <node id='2922257092' timestamp='2014-06-18T18:40:05Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717697' lon='-122.3987374' />\n  <node id='2922257093' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717748' lon='-122.398731' />\n  <node id='2922257094' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717795' lon='-122.3992064' />\n  <node id='2922257095' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717796' lon='-122.3992064' />\n  <node id='2922257096' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717835' lon='-122.3987591' />\n  <node id='2922257097' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717877' lon='-122.3991934' />\n  <node id='2922257098' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7717972' lon='-122.3989279' />\n  <node id='2922257099' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718073' lon='-122.3989095' />\n  <node id='2922257100' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718074' lon='-122.3989095' />\n  <node id='2922257101' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718075' lon='-122.3992359' />\n  <node id='2922257102' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718092' lon='-122.3987404' />\n  <node id='2922257103' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718164' lon='-122.3992204' />\n  <node id='2922257104' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718211' lon='-122.3989507' />\n  <node id='2922257105' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718258' lon='-122.398877' />\n  <node id='2922257106' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771828' lon='-122.3986421' />\n  <node id='2922257107' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771832' lon='-122.398931' />\n  <node id='2922257108' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718431' lon='-122.3987768' />\n  <node id='2922257109' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718451' lon='-122.3989735' />\n  <node id='2922257110' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718559' lon='-122.3989553' />\n  <node id='2922257111' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771856' lon='-122.3989553' />\n  <node id='2922257112' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718567' lon='-122.3992809' />\n  <node id='2922257113' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718652' lon='-122.3992662' />\n  <node id='2922257114' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718772' lon='-122.3986567' />\n  <node id='2922257115' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718784' lon='-122.3986499' />\n  <node id='2922257116' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718785' lon='-122.3986499' />\n  <node id='2922257117' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771879' lon='-122.3989101' />\n  <node id='2922257118' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718825' lon='-122.3988928' />\n  <node id='2922257119' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7718923' lon='-122.3990184' />\n  <node id='2922257120' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719032' lon='-122.3990001' />\n  <node id='2922257121' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719106' lon='-122.3993089' />\n  <node id='2922257122' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719362' lon='-122.3989283' />\n  <node id='2922257123' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719401' lon='-122.398909' />\n  <node id='2922257124' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719418' lon='-122.3990655' />\n  <node id='2922257125' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719443' lon='-122.3986765' />\n  <node id='2922257126' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719456' lon='-122.3986692' />\n  <node id='2922257127' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719518' lon='-122.3990486' />\n  <node id='2922257128' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719519' lon='-122.3990486' />\n  <node id='2922257129' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719556' lon='-122.399231' />\n  <node id='2922257130' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719702' lon='-122.3989336' />\n  <node id='2922257131' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719733' lon='-122.3989183' />\n  <node id='2922257132' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719741' lon='-122.3992472' />\n  <node id='2922257133' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719742' lon='-122.3992472' />\n  <node id='2922257134' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.771989' lon='-122.3991104' />\n  <node id='2922257135' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719899' lon='-122.3991733' />\n  <node id='2922257136' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7719992' lon='-122.3990934' />\n  <node id='2922257137' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7720008' lon='-122.3986932' />\n  <node id='2922257138' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7720018' lon='-122.3986879' />\n  <node id='2922257139' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7720081' lon='-122.3991924' />\n  <node id='2922257140' timestamp='2014-06-18T18:40:06Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7720135' lon='-122.3991337' />\n  <node id='2922257141' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7720274' lon='-122.3989517' />\n  <node id='2922257142' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7720309' lon='-122.3989344' />\n  <node id='2922257143' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.772068' lon='-122.3987124' />\n  <node id='2922257144' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.772069' lon='-122.3987072' />\n  <node id='2922257145' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.772088' lon='-122.3989719' />\n  <node id='2922257146' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7720921' lon='-122.3989515' />\n  <node id='2922257147' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721014' lon='-122.39882' />\n  <node id='2922257148' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.772117' lon='-122.3988243' />\n  <node id='2922257149' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721173' lon='-122.3988217' />\n  <node id='2922257150' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721187' lon='-122.3987273' />\n  <node id='2922257151' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721451' lon='-122.39899' />\n  <node id='2922257152' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721452' lon='-122.39899' />\n  <node id='2922257153' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721497' lon='-122.3989676' />\n  <node id='2922257154' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721527' lon='-122.3987764' />\n  <node id='2922257155' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721839' lon='-122.3988261' />\n  <node id='2922257156' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7721882' lon='-122.3988207' />\n  <node id='2922257157' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.772196' lon='-122.3988728' />\n  <node id='2922257158' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7722065' lon='-122.3989836' />\n  <node id='2922257159' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7722066' lon='-122.3989836' />\n  <node id='2922257160' timestamp='2014-06-18T18:40:07Z' uid='510836' user='Rub21' version='1' changeset='23010843' lat='37.7722249' lon='-122.3988809' />\n  <node id='2922262472' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7708895' lon='-122.3997279' />\n  <node id='2922262473' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7708897' lon='-122.39973' />\n  <node id='2922262474' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7708905' lon='-122.3997291' />\n  <node id='2922262475' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7708937' lon='-122.3997347' />\n  <node id='2922262476' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7708999' lon='-122.3997373' />\n  <node id='2922262477' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709075' lon='-122.3997416' />\n  <node id='2922262478' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709077' lon='-122.399741' />\n  <node id='2922262479' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709153' lon='-122.3996856' />\n  <node id='2922262480' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709163' lon='-122.3997453' />\n  <node id='2922262481' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709184' lon='-122.3996893' />\n  <node id='2922262482' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709202' lon='-122.399747' />\n  <node id='2922262483' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709202' lon='-122.3997474' />\n  <node id='2922262484' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709332' lon='-122.3996636' />\n  <node id='2922262485' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709359' lon='-122.399654' />\n  <node id='2922262486' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709681' lon='-122.3996808' />\n  <node id='2922262487' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7709709' lon='-122.3996706' />\n  <node id='2922262488' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7710088' lon='-122.399698' />\n  <node id='2922262489' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7710116' lon='-122.3996854' />\n  <node id='2922262490' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7710254' lon='-122.3997912' />\n  <node id='2922262491' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771047' lon='-122.399702' />\n  <node id='2922262492' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7710803' lon='-122.3997255' />\n  <node id='2922262493' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771082' lon='-122.3997124' />\n  <node id='2922262494' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7711254' lon='-122.3997374' />\n  <node id='2922262495' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7711278' lon='-122.3997262' />\n  <node id='2922262496' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7711278' lon='-122.3997263' />\n  <node id='2922262497' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7711504' lon='-122.3998265' />\n  <node id='2922262498' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7711632' lon='-122.3997362' />\n  <node id='2922262499' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7711989' lon='-122.3997529' />\n  <node id='2922262500' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7711997' lon='-122.3997398' />\n  <node id='2922265101' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771244' lon='-122.3997583' />\n  <node id='2922265102' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7712444' lon='-122.3997452' />\n  <node id='2922265103' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7712769' lon='-122.3998436' />\n  <node id='2922265104' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771277' lon='-122.3998436' />\n  <node id='2922265105' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771283' lon='-122.399751' />\n  <node id='2922265106' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7713178' lon='-122.39975' />\n  <node id='2922265107' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7713179' lon='-122.3997639' />\n  <node id='2922265108' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771363' lon='-122.3997633' />\n  <node id='2922265109' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7713636' lon='-122.3997485' />\n  <node id='2922265110' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7714004' lon='-122.3997494' />\n  <node id='2922265111' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7714045' lon='-122.3998422' />\n  <node id='2922265112' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7714369' lon='-122.399744' />\n  <node id='2922265113' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771437' lon='-122.399744' />\n  <node id='2922265114' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7714384' lon='-122.3997569' />\n  <node id='2922265115' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7714816' lon='-122.3997359' />\n  <node id='2922265116' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7714829' lon='-122.399751' />\n  <node id='2922265117' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7715194' lon='-122.3997292' />\n  <node id='2922265118' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7715297' lon='-122.3998215' />\n  <node id='2922265119' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771553' lon='-122.3997188' />\n  <node id='2922265120' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7715531' lon='-122.3997188' />\n  <node id='2922265121' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7715566' lon='-122.3997336' />\n  <node id='2922265122' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7715973' lon='-122.399705' />\n  <node id='2922265123' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7716007' lon='-122.3997176' />\n  <node id='2922265124' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7716335' lon='-122.3996924' />\n  <node id='2922265125' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7716543' lon='-122.3997831' />\n  <node id='2922265126' timestamp='2014-06-18T18:43:33Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7716652' lon='-122.3996771' />\n  <node id='2922265127' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7716707' lon='-122.3996911' />\n  <node id='2922265128' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717096' lon='-122.3996551' />\n  <node id='2922265129' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717138' lon='-122.3996699' />\n  <node id='2922265130' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717448' lon='-122.3996399' />\n  <node id='2922265131' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717449' lon='-122.3996399' />\n  <node id='2922265132' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771753' lon='-122.3996632' />\n  <node id='2922265133' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717611' lon='-122.3996307' />\n  <node id='2922265134' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717693' lon='-122.3997284' />\n  <node id='2922265135' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717696' lon='-122.3997294' />\n  <node id='2922265136' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717697' lon='-122.3997294' />\n  <node id='2922265137' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717699' lon='-122.3996539' />\n  <node id='2922265138' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771793' lon='-122.3996075' />\n  <node id='2922265139' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717931' lon='-122.3996075' />\n  <node id='2922265140' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717932' lon='-122.3997149' />\n  <node id='2922265141' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717936' lon='-122.3997162' />\n  <node id='2922265142' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7717993' lon='-122.3996197' />\n  <node id='2922265143' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7718327' lon='-122.3995804' />\n  <node id='2922265144' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7718375' lon='-122.3995913' />\n  <node id='2922265145' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7718657' lon='-122.3995571' />\n  <node id='2922265146' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.771894' lon='-122.3995329' />\n  <node id='2922265147' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7719004' lon='-122.3995439' />\n  <node id='2922265148' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7719079' lon='-122.3996387' />\n  <node id='2922265149' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7719302' lon='-122.3994994' />\n  <node id='2922265150' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7719364' lon='-122.3995122' />\n  <node id='2922265151' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7719917' lon='-122.3994448' />\n  <node id='2922265152' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7719987' lon='-122.399457' />\n  <node id='2922265153' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7720283' lon='-122.3994116' />\n  <node id='2922265154' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.772034' lon='-122.3994227' />\n  <node id='2922265155' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.772087' lon='-122.3993584' />\n  <node id='2922265156' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7720871' lon='-122.3993584' />\n  <node id='2922265157' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7720951' lon='-122.3993705' />\n  <node id='2922265158' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7721269' lon='-122.3993248' />\n  <node id='2922265159' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7721328' lon='-122.399334' />\n  <node id='2922265160' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7721869' lon='-122.39927' />\n  <node id='2922265161' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7721924' lon='-122.3992787' />\n  <node id='2922265162' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7722245' lon='-122.3992362' />\n  <node id='2922265163' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7722302' lon='-122.3992464' />\n  <node id='2922265164' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7722822' lon='-122.399184' />\n  <node id='2922265165' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7722823' lon='-122.399184' />\n  <node id='2922265166' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7722879' lon='-122.3991937' />\n  <node id='2922265167' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7723283' lon='-122.3991469' />\n  <node id='2922265168' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7723323' lon='-122.3991534' />\n  <node id='2922265169' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.772353' lon='-122.3991231' />\n  <node id='2922265170' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.772354' lon='-122.3991247' />\n  <node id='2922265171' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7723721' lon='-122.3991088' />\n  <node id='2922265172' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.772394' lon='-122.3991947' />\n  <node id='2922265173' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7723949' lon='-122.3991962' />\n  <node id='2922265174' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7723967' lon='-122.3991508' />\n  <node id='2922265175' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7724103' lon='-122.3991389' />\n  <node id='2922265176' timestamp='2014-06-18T18:43:34Z' uid='510836' user='Rub21' version='1' changeset='23010915' lat='37.7724255' lon='-122.3991674' />\n  <node id='2923489922' timestamp='2014-06-19T13:49:13Z' uid='510836' user='Rub21' version='1' changeset='23024899' lat='37.7694642' lon='-122.4064892' />\n  <node id='2960272791' timestamp='2015-06-13T20:49:43Z' uid='481533' user='dbaron' version='3' changeset='31950831' lat='37.7708144' lon='-122.4062846'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:country' v='US' />\n    <tag k='addr:housenumber' v='934' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:street' v='Brannan Street' />\n  </node>\n  <node id='3279466347' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='3' changeset='57302156' lat='37.7685653' lon='-122.4022079'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='1725' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Alameda Street' />\n    <tag k='amenity' v='restaurant' />\n    <tag k='cuisine' v='Seafood,_Japanese,_Asian_Fusion' />\n    <tag k='name' v='Skool SF' />\n    <tag k='payment:bitcoin' v='yes' />\n    <tag k='payment:litecoin' v='yes' />\n    <tag k='phone' v='(415) 255-8800' />\n    <tag k='website' v='http://skoolsf.com' />\n  </node>\n  <node id='3456791114' timestamp='2018-08-24T06:56:51Z' uid='128186' user='malcolmh' version='4' changeset='61942337' lat='37.7815326' lon='-122.38617'>\n    <tag k='fee' v='no' />\n    <tag k='seamark:small_craft_facility:category' v='pump-out' />\n    <tag k='seamark:type' v='small_craft_facility' />\n  </node>\n  <node id='3532718854' timestamp='2015-05-21T08:04:09Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7767241' lon='-122.3951154'>\n    <tag k='entrance' v='yes' />\n    <tag k='name' v='Exit A' />\n    <tag k='ref' v='A' />\n  </node>\n  <node id='3532718855' timestamp='2015-05-21T08:04:09Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.77674' lon='-122.3947513'>\n    <tag k='entrance' v='yes' />\n    <tag k='ref' v='B' />\n  </node>\n  <node id='3532718856' timestamp='2015-05-21T08:04:09Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7764216' lon='-122.394725'>\n    <tag k='entrance' v='yes' />\n    <tag k='name' v='Exit C' />\n    <tag k='ref' v='C' />\n  </node>\n  <node id='3532718857' timestamp='2015-05-21T08:04:09Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7763665' lon='-122.3946553'>\n    <tag k='entrance' v='yes' />\n    <tag k='name' v='Exit D' />\n    <tag k='ref' v='D' />\n  </node>\n  <node id='3532718858' timestamp='2015-05-21T08:04:09Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7763178' lon='-122.3945935'>\n    <tag k='entrance' v='yes' />\n    <tag k='name' v='Exit E' />\n    <tag k='ref' v='E' />\n  </node>\n  <node id='3532718859' timestamp='2015-05-21T08:04:09Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7764871' lon='-122.3947298'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='3532718884' timestamp='2016-05-15T05:33:46Z' uid='933797' user='oba510' version='2' changeset='39324426' lat='37.7717342' lon='-122.4015592'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3532718885' timestamp='2016-05-15T05:33:46Z' uid='933797' user='oba510' version='3' changeset='39324426' lat='37.7717718' lon='-122.4017594'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3532718886' timestamp='2018-03-23T18:43:40Z' uid='53073' user='Aaron Lidman' version='3' changeset='57466350' lat='37.7697762' lon='-122.4036728'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3532718887' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='4' changeset='63557245' lat='37.7697728' lon='-122.40414'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3532718888' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='4' changeset='63557245' lat='37.769884' lon='-122.4042061'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3532718889' timestamp='2015-05-21T08:04:11Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7697783' lon='-122.4041395' />\n  <node id='3532718890' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='3' changeset='49034504' lat='37.7695218' lon='-122.406127'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3532718891' timestamp='2015-05-21T08:04:11Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7695917' lon='-122.4060344' />\n  <node id='3532718892' timestamp='2015-05-21T08:04:11Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7696585' lon='-122.4058816' />\n  <node id='3532719193' timestamp='2015-05-21T08:04:11Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7696712' lon='-122.4058011' />\n  <node id='3532719195' timestamp='2015-05-21T08:04:11Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7696603' lon='-122.4064855' />\n  <node id='3532719196' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='3' changeset='49034504' lat='37.7695536' lon='-122.4063509'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3532719197' timestamp='2015-05-21T08:04:11Z' uid='33757' user='Minh Nguyen' version='1' changeset='31336567' lat='37.7693341' lon='-122.4065441' />\n  <node id='3532719204' timestamp='2016-05-15T05:33:46Z' uid='933797' user='oba510' version='2' changeset='39324426' lat='37.7712243' lon='-122.405549'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3553171993' timestamp='2015-06-01T16:45:15Z' uid='33757' user='Minh Nguyen' version='2' changeset='31640735' lat='37.7704712' lon='-122.4031218'>\n    <tag k='name' v='California Closets' />\n    <tag k='shop' v='furniture' />\n  </node>\n  <node id='3553171994' timestamp='2016-08-17T20:20:58Z' uid='290680' user='wheelmap_visitor' version='4' changeset='41518910' lat='37.7700749' lon='-122.4035915'>\n    <tag k='name' v='Roche Bobois' />\n    <tag k='shop' v='furniture' />\n    <tag k='toilets:wheelchair' v='yes' />\n    <tag k='website' v='http://www.roche-bobois.com/' />\n    <tag k='wheelchair' v='yes' />\n  </node>\n  <node id='3562892896' timestamp='2015-06-01T16:45:15Z' uid='33757' user='Minh Nguyen' version='2' changeset='31640735' lat='37.7706133' lon='-122.4035643'>\n    <tag k='name' v='Poggenpohl Kitchens' />\n    <tag k='shop' v='kitchen' />\n    <tag k='website' v='http://www.poggenpohl.com/en/find-a-studio/usa/poggenpohl-san-francisco/' />\n  </node>\n  <node id='3562892897' timestamp='2015-06-01T08:03:14Z' uid='33757' user='Minh Nguyen' version='1' changeset='31627405' lat='37.769516' lon='-122.404299'>\n    <tag k='name' v='San Francisco Design Center' />\n    <tag k='shop' v='furniture' />\n    <tag k='short_name' v='SFDC' />\n    <tag k='website' v='http://www.sfdesigncenter.com/' />\n  </node>\n  <node id='3563920172' timestamp='2015-06-01T16:45:15Z' uid='33757' user='Minh Nguyen' version='1' changeset='31640735' lat='37.7710733' lon='-122.4029823'>\n    <tag k='name' v='Schawk!' />\n    <tag k='office' v='company' />\n    <tag k='website' v='http://www.schawk.com/' />\n  </node>\n  <node id='3563920173' timestamp='2015-06-03T02:06:15Z' uid='33757' user='Minh Nguyen' version='2' changeset='31681487' lat='37.7704182' lon='-122.4032049'>\n    <tag k='name' v='Mademoiselle Nails' />\n    <tag k='shop' v='beauty' />\n  </node>\n  <node id='3563920174' timestamp='2018-10-26T21:38:05Z' uid='371121' user='AndrewSnow' version='2' changeset='63915437' lat='37.7703525' lon='-122.40328'>\n    <tag k='amenity' v='restaurant' />\n    <tag k='cuisine' v='mediterranean' />\n    <tag k='name' v='Safron 685' />\n    <tag k='opening_hours' v='Mo-Sa 11:00-21:00' />\n    <tag k='takeaway' v='yes' />\n    <tag k='website' v='http://saffron685-sf.com/' />\n    <tag k='wheelchair' v='yes' />\n  </node>\n  <node id='3563920175' timestamp='2015-06-01T16:45:15Z' uid='33757' user='Minh Nguyen' version='1' changeset='31640735' lat='37.7701129' lon='-122.40512' />\n  <node id='3563920176' timestamp='2015-06-01T16:45:15Z' uid='33757' user='Minh Nguyen' version='1' changeset='31640735' lat='37.7703652' lon='-122.4052085' />\n  <node id='3563920177' timestamp='2015-06-01T16:45:15Z' uid='33757' user='Minh Nguyen' version='1' changeset='31640735' lat='37.7694599' lon='-122.4046345'>\n    <tag k='name' v='Country Floors' />\n    <tag k='shop' v='flooring' />\n    <tag k='website' v='http://www.countryfloors.com/san-francisco-country-floors/' />\n  </node>\n  <node id='3567489498' timestamp='2017-09-24T21:08:04Z' uid='339581' user='nyuriks' version='2' changeset='52339423' lat='37.7708073' lon='-122.4033554'>\n    <tag k='name' v='Practice Fusion' />\n    <tag k='office' v='company' />\n    <tag k='website' v='http://www.practicefusion.com/' />\n    <tag k='wikidata' v='Q7237353' />\n    <tag k='wikipedia' v='en:Practice Fusion' />\n  </node>\n  <node id='3570918493' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.7699643' lon='-122.4050283'>\n    <tag k='name' v='Kurt S. Adler' />\n    <tag k='shop' v='christmas' />\n    <tag k='website' v='http://www.kurtadler-sf.com/' />\n  </node>\n  <node id='3570918494' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.7702887' lon='-122.4050927'>\n    <tag k='name' v='Galleher' />\n    <tag k='shop' v='flooring' />\n  </node>\n  <node id='3570918495' timestamp='2018-01-12T21:03:27Z' uid='371121' user='AndrewSnow' version='4' changeset='55392521' lat='37.7706265' lon='-122.4029447'>\n    <tag k='addr:housenumber' v='669' />\n    <tag k='addr:street' v='Townsend Street' />\n    <tag k='amenity' v='restaurant' />\n    <tag k='cuisine' v='sushi' />\n    <tag k='name' v='Omakase' />\n    <tag k='website' v='http://omakasesf.com/' />\n  </node>\n  <node id='3570918496' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.7705364' lon='-122.4030717' />\n  <node id='3570918497' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.7705861' lon='-122.4030083' />\n  <node id='3570918498' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.7705227' lon='-122.4029287' />\n  <node id='3570918499' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.770505' lon='-122.4029512' />\n  <node id='3570918500' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.7704308' lon='-122.4028579' />\n  <node id='3570918501' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.7712725' lon='-122.4028837' />\n  <node id='3570918504' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207' lat='37.7715719' lon='-122.4032626' />\n  <node id='3577577196' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7696698' lon='-122.4024929'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='25' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Division Street' />\n  </node>\n  <node id='3577577294' timestamp='2018-03-03T01:29:56Z' uid='53073' user='Aaron Lidman' version='4' changeset='56836576' lat='37.7696757' lon='-122.402331'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='11' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Division Street' />\n    <tag k='amenity' v='restaurant' />\n    <tag k='delivery' v='yes' />\n    <tag k='name' v='Dumpling Time' />\n    <tag k='outdoor_seating' v='yes' />\n    <tag k='smoking' v='no' />\n  </node>\n  <node id='3577577295' timestamp='2015-06-06T23:03:40Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7694628' lon='-122.4022939'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='50' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='De Haro Street' />\n    <tag k='craft' v='furniture' />\n    <tag k='name' v='CustomFurnitureDesign. com' />\n  </node>\n  <node id='3577586293' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='3' changeset='31778944' lat='37.7700224' lon='-122.4024575'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='2' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Division Street' />\n    <tag k='name' v='Suchada' />\n    <tag k='shop' v='massage' />\n  </node>\n  <node id='3577586294' timestamp='2015-06-06T23:03:40Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7695213' lon='-122.4028105'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='1' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Rhode Island Street' />\n    <tag k='name' v='BoConcept' />\n    <tag k='shop' v='interior_decoration' />\n    <tag k='website' v='http://www.boconcept.com/en-us/stores/find-your-local-store/usa/california/boconcept-san-francisco' />\n  </node>\n  <node id='3577590195' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7702648' lon='-122.4021666'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='680' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='King Street' />\n  </node>\n  <node id='3577596493' timestamp='2015-06-06T23:03:40Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7702061' lon='-122.4022532'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='684' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='King Street' />\n    <tag k='amenity' v='fast_food' />\n    <tag k='name' v='CrepeCone' />\n    <tag k='outdoor_seating' v='yes' />\n  </node>\n  <node id='3577596693' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7703162' lon='-122.4020946'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='660' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='King Street' />\n  </node>\n  <node id='3577596694' timestamp='2015-06-06T23:03:41Z' uid='371121' user='AndrewSnow' version='2' changeset='31778944' lat='37.7701609' lon='-122.4023148'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='690' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='King Street' />\n    <tag k='name' v='Suchada' />\n    <tag k='shop' v='massage' />\n  </node>\n  <node id='3577621761' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='3' changeset='63553269' lat='37.7694271' lon='-122.4038036' />\n  <node id='3577621762' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='3' changeset='63553269' lat='37.7694648' lon='-122.4031641' />\n  <node id='3577621763' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='3' changeset='63553269' lat='37.7697082' lon='-122.4038269' />\n  <node id='3577621764' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='3' changeset='63553269' lat='37.7697437' lon='-122.4031898' />\n  <node id='3577621765' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.7699821' lon='-122.4025332' />\n  <node id='3577621766' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.7699849' lon='-122.4025548' />\n  <node id='3577621767' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.769988' lon='-122.402584' />\n  <node id='3577621768' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.7699893' lon='-122.4025748' />\n  <node id='3577621769' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.7700631' lon='-122.4023821' />\n  <node id='3577621770' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.770071' lon='-122.4023765' />\n  <node id='3577621771' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.7700725' lon='-122.4023646' />\n  <node id='3577621772' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.7700726' lon='-122.4023699' />\n  <node id='3577621773' timestamp='2015-06-06T23:03:38Z' uid='371121' user='AndrewSnow' version='1' changeset='31778944' lat='37.7700777' lon='-122.402548' />\n  <node id='3577623862' timestamp='2015-06-06T23:12:06Z' uid='371121' user='AndrewSnow' version='1' changeset='31779035' lat='37.7707973' lon='-122.4045148' />\n  <node id='3577623863' timestamp='2015-06-06T23:12:06Z' uid='371121' user='AndrewSnow' version='1' changeset='31779035' lat='37.7716519' lon='-122.4034387' />\n  <node id='3587199694' timestamp='2017-05-29T20:57:56Z' uid='33757' user='Minh Nguyen' version='2' changeset='49085218' lat='37.7696079' lon='-122.4067729'>\n    <tag k='amenity' v='doctors' />\n    <tag k='name' v='SOMA Sport &amp; Physio' />\n    <tag k='website' v='http://www.somaphysio.com/' />\n  </node>\n  <node id='3607887493' timestamp='2015-06-20T21:44:42Z' uid='371121' user='AndrewSnow' version='2' changeset='32105635' lat='37.7685586' lon='-122.4025598'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='1755' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Alameda Street' />\n  </node>\n  <node id='3607887693' timestamp='2015-06-20T21:44:41Z' uid='371121' user='AndrewSnow' version='2' changeset='32105635' lat='37.76833' lon='-122.402747'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='111' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Rhode Island Street' />\n  </node>\n  <node id='3607930410' timestamp='2015-06-20T21:44:41Z' uid='371121' user='AndrewSnow' version='1' changeset='32105635' lat='37.7682915' lon='-122.4031122' />\n  <node id='3607930411' timestamp='2015-06-20T21:44:41Z' uid='371121' user='AndrewSnow' version='1' changeset='32105635' lat='37.7682963' lon='-122.4031007' />\n  <node id='3607930412' timestamp='2015-06-20T21:44:41Z' uid='371121' user='AndrewSnow' version='1' changeset='32105635' lat='37.7685196' lon='-122.4031218' />\n  <node id='3607930413' timestamp='2015-06-20T21:44:41Z' uid='371121' user='AndrewSnow' version='1' changeset='32105635' lat='37.76852' lon='-122.4031239' />\n  <node id='3611937228' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7707359' lon='-122.4047498' />\n  <node id='3611937229' timestamp='2015-06-23T04:04:26Z' uid='371121' user='AndrewSnow' version='1' changeset='32152237' lat='37.772489' lon='-122.4025095' />\n  <node id='3613001946' timestamp='2015-06-23T15:10:47Z' uid='1835512' user='nereocystis' version='1' changeset='32163790' lat='37.7692029' lon='-122.4041915'>\n    <tag k='name' v='Ann Sacks' />\n    <tag k='shop' v='trade' />\n    <tag k='website' v='http://www.annsacks.com/find-a-showroom/ann-sacks-san-francisco' />\n  </node>\n  <node id='3619170069' timestamp='2015-06-26T08:45:10Z' uid='33757' user='Minh Nguyen' version='1' changeset='32220993' lat='37.7708277' lon='-122.4020532' />\n  <node id='3619170070' timestamp='2015-06-26T08:45:10Z' uid='33757' user='Minh Nguyen' version='1' changeset='32220993' lat='37.7705934' lon='-122.402359' />\n  <node id='3619170071' timestamp='2018-03-18T21:08:56Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301114' lat='37.7705055' lon='-122.4047286'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='680' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='8th Street' />\n    <tag k='amenity' v='restaurant' />\n    <tag k='cuisine' v='barbeque' />\n    <tag k='microbrewery' v='yes' />\n    <tag k='name' v='Hardwood Bar &amp; Smokery' />\n    <tag k='outdoor_seating' v='no' />\n    <tag k='website' v='hardwoodsf.com' />\n  </node>\n  <node id='3856374589' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='2' changeset='38624789' lat='37.7751905' lon='-122.3940843' />\n  <node id='3985226613' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7697825' lon='-122.4039838' />\n  <node id='3985226614' timestamp='2018-10-15T22:03:38Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.769774' lon='-122.4039506' />\n  <node id='4072351913' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701509' lon='-122.4018882' />\n  <node id='4072351914' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.770138' lon='-122.4019159' />\n  <node id='4072351915' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7701014' lon='-122.401945' />\n  <node id='4072351916' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7700779' lon='-122.4019173' />\n  <node id='4126311691' timestamp='2016-04-16T14:43:50Z' uid='28775' user='StellanL' version='1' changeset='38624789' lat='37.772369' lon='-122.3976675' />\n  <node id='4126311698' timestamp='2016-04-16T14:43:51Z' uid='28775' user='StellanL' version='1' changeset='38624789' lat='37.7724385' lon='-122.3991939' />\n  <node id='4179515465' timestamp='2018-04-01T18:54:00Z' uid='53073' user='Aaron Lidman' version='3' changeset='57720035' lat='37.7695009' lon='-122.3987942' />\n  <node id='4179515466' timestamp='2016-05-11T21:26:32Z' uid='2748195' user='karitotp' version='1' changeset='39253310' lat='37.7698128' lon='-122.3991828' />\n  <node id='4179515467' timestamp='2016-05-11T21:26:32Z' uid='2748195' user='karitotp' version='1' changeset='39253310' lat='37.7701236' lon='-122.3995674' />\n  <node id='4430188891' timestamp='2016-10-04T04:35:43Z' uid='1306' user='PlaneMad' version='1' changeset='42619743' lat='37.7701378' lon='-122.404123'>\n    <tag k='highway' v='bus_stop' />\n  </node>\n  <node id='4761685552' timestamp='2017-03-29T11:43:36Z' uid='933797' user='oba510' version='1' changeset='47257631' lat='37.7676115' lon='-122.4067451' />\n  <node id='4880860061' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='1' changeset='49034504' lat='37.770003' lon='-122.406914'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='4880860062' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='1' changeset='49034504' lat='37.7701423' lon='-122.4069081'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='4880860063' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='1' changeset='49034504' lat='37.7701463' lon='-122.4070931'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='4880860064' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='1' changeset='49034504' lat='37.7699957' lon='-122.4070864'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='4880860065' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='1' changeset='49034504' lat='37.7697092' lon='-122.4065564' />\n  <node id='4880860066' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='1' changeset='49034504' lat='37.7697502' lon='-122.4059698' />\n  <node id='4884299090' timestamp='2017-05-29T20:57:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='49085218' lat='37.7695428' lon='-122.4066627'>\n    <tag k='amenity' v='cafe' />\n    <tag k='cuisine' v='juice' />\n    <tag k='name' v='Power Up Cafe' />\n  </node>\n  <node id='4925013296' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702202' lon='-122.4030315' />\n  <node id='4925013297' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702207' lon='-122.403008' />\n  <node id='4925013298' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702282' lon='-122.4029839' />\n  <node id='4925013299' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702472' lon='-122.402951' />\n  <node id='4925013300' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.77026' lon='-122.4029249' />\n  <node id='4925013301' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702615' lon='-122.4028974' />\n  <node id='4925013302' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702562' lon='-122.4028525' />\n  <node id='4925013303' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.770252' lon='-122.4028263' />\n  <node id='4925013304' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702547' lon='-122.4027928' />\n  <node id='4925013305' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702716' lon='-122.4027653' />\n  <node id='4925013306' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702928' lon='-122.4027479' />\n  <node id='4925013307' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.770347' lon='-122.4027316' />\n  <node id='4925013308' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7706746' lon='-122.4024774' />\n  <node id='4925013309' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7704091' lon='-122.40281' />\n  <node id='4925013310' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7705038' lon='-122.4029296' />\n  <node id='4925013311' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7701009' lon='-122.402596' />\n  <node id='4925013312' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7701571' lon='-122.4024582' />\n  <node id='4925013313' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.770111' lon='-122.4025138' />\n  <node id='4925013314' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702091' lon='-122.4025648' />\n  <node id='4925013315' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.770243' lon='-122.4026003' />\n  <node id='4925013316' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7707063' lon='-122.4025165' />\n  <node id='4925013317' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7709337' lon='-122.4027983' />\n  <node id='4925013318' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7708579' lon='-122.4027036'>\n    <tag k='access' v='no' />\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='4925013319' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7704995' lon='-122.4022604' />\n  <node id='4925013320' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7703252' lon='-122.4020359'>\n    <tag k='access' v='no' />\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='4925027421' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208' lat='37.7702367' lon='-122.4019313' />\n  <node id='4941620696' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7692219' lon='-122.3984419' />\n  <node id='4941620697' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7687623' lon='-122.4006667' />\n  <node id='4941620698' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7686369' lon='-122.4005136' />\n  <node id='4941620699' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7692088' lon='-122.3997849' />\n  <node id='4941620700' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.768709' lon='-122.4007362' />\n  <node id='4941620701' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7693329' lon='-122.3999227' />\n  <node id='4941620702' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7697017' lon='-122.3993608' />\n  <node id='4941620703' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.769298' lon='-122.399884' />\n  <node id='4941620704' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7697893' lon='-122.3994823' />\n  <node id='4941620705' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7693995' lon='-122.3999967' />\n  <node id='4941620706' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7696133' lon='-122.3992382' />\n  <node id='4941620707' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7691935' lon='-122.3997679' />\n  <node id='4941620708' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7694797' lon='-122.4000858' />\n  <node id='4941620709' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7700035' lon='-122.4003313' />\n  <node id='4941620710' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7701304' lon='-122.4004824' />\n  <node id='4941620711' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7699324' lon='-122.3996513' />\n  <node id='4941620712' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7702366' lon='-122.4000335' />\n  <node id='4941620714' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7691861' lon='-122.4013223' />\n  <node id='4941620715' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7689121' lon='-122.4009596' />\n  <node id='4941620716' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7698091' lon='-122.4005449' />\n  <node id='4941620717' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7688723' lon='-122.4017139' />\n  <node id='4941620718' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7687175' lon='-122.4015838' />\n  <node id='4941620719' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7687323' lon='-122.401384' />\n  <node id='4941620720' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7687811' lon='-122.4011278' />\n  <node id='4941620821' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7695094' lon='-122.4001931' />\n  <node id='4941620822' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7699518' lon='-122.4007125' />\n  <node id='4941620823' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='3' changeset='63557245' lat='37.7690946' lon='-122.4029961' />\n  <node id='4941620828' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7686174' lon='-122.40351' />\n  <node id='4941620831' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7686072' lon='-122.4036825' />\n  <node id='4941620833' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7697777' lon='-122.4036517' />\n  <node id='4941620836' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7697733' lon='-122.4037992' />\n  <node id='4941620845' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7683159' lon='-122.4040717' />\n  <node id='4941620846' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7683189' lon='-122.4042124' />\n  <node id='4941620847' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7684132' lon='-122.4042781' />\n  <node id='4941620848' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7678722' lon='-122.4040486' />\n  <node id='4941620849' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7684153' lon='-122.4040769' />\n  <node id='4941620850' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7684111' lon='-122.4044672' />\n  <node id='4941620851' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7683994' lon='-122.4044953' />\n  <node id='4941620852' timestamp='2017-06-29T06:59:58Z' uid='5659851' user='marthaleena' version='1' changeset='49908842' lat='37.7677676' lon='-122.4039575' />\n  <node id='4941620853' timestamp='2017-10-23T23:13:28Z' uid='53073' user='Aaron Lidman' version='2' changeset='53193519' lat='37.7677669' lon='-122.4038266' />\n  <node id='5020763504' timestamp='2017-08-07T19:26:23Z' uid='3526564' user='yurasi' version='1' changeset='50926093' lat='37.7690793' lon='-122.3982601' />\n  <node id='5038477632' timestamp='2017-08-16T08:36:33Z' uid='94578' user='andygol' version='1' changeset='51162893' lat='37.7710243' lon='-122.405102' />\n  <node id='5090306509' timestamp='2017-09-08T21:53:52Z' uid='116769' user='probabble' version='1' changeset='51865111' lat='37.7670171' lon='-122.3998597' />\n  <node id='5090306511' timestamp='2017-09-08T21:53:52Z' uid='116769' user='probabble' version='1' changeset='51865111' lat='37.766984' lon='-122.3994857' />\n  <node id='5184288019' timestamp='2017-10-23T17:45:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53185819' lat='37.7736818' lon='-122.3947519' />\n  <node id='5184288020' timestamp='2017-10-23T17:45:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53185819' lat='37.7735995' lon='-122.3947949' />\n  <node id='5184288021' timestamp='2017-10-23T17:45:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53185819' lat='37.7735452' lon='-122.3948364' />\n  <node id='5184288022' timestamp='2017-10-23T17:45:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53185819' lat='37.7735022' lon='-122.3948793' />\n  <node id='5184288023' timestamp='2017-10-23T17:45:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53185819' lat='37.7734518' lon='-122.3949551' />\n  <node id='5184288024' timestamp='2017-10-23T17:45:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53185819' lat='37.7734661' lon='-122.3949736' />\n  <node id='5184288025' timestamp='2017-10-23T17:45:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53185819' lat='37.7732908' lon='-122.3952282' />\n  <node id='5184288026' timestamp='2017-10-23T17:45:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53185819' lat='37.7733952' lon='-122.3950972' />\n  <node id='5184349755' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7709355' lon='-122.3992102' />\n  <node id='5184349756' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7709184' lon='-122.3991915' />\n  <node id='5184349757' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7709048' lon='-122.3991817' />\n  <node id='5184349758' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7708776' lon='-122.399178' />\n  <node id='5184349759' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7708397' lon='-122.3991877' />\n  <node id='5184349760' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7707912' lon='-122.3992004' />\n  <node id='5184349761' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7707486' lon='-122.3992049' />\n  <node id='5184349762' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7707067' lon='-122.3991967' />\n  <node id='5184349763' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.77067' lon='-122.3991758' />\n  <node id='5184349764' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.770631' lon='-122.3991421' />\n  <node id='5184349765' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7705499' lon='-122.3990336' />\n  <node id='5184349766' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7705127' lon='-122.39897' />\n  <node id='5184349767' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7704778' lon='-122.3989259' />\n  <node id='5184349768' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7704476' lon='-122.3989049' />\n  <node id='5184349769' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.770421' lon='-122.3989117' />\n  <node id='5184349770' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7703979' lon='-122.3989356' />\n  <node id='5184349771' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7703635' lon='-122.3989802'>\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='5184349772' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7705878' lon='-122.399059'>\n    <tag k='amenity' v='bench' />\n  </node>\n  <node id='5184349773' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7707717' lon='-122.3991795'>\n    <tag k='amenity' v='bench' />\n  </node>\n  <node id='5184349774' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7708853' lon='-122.3991533'>\n    <tag k='amenity' v='bench' />\n  </node>\n  <node id='5184349775' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7708439' lon='-122.3993172'>\n    <tag k='leisure' v='picnic_table' />\n  </node>\n  <node id='5184349776' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7707203' lon='-122.399288'>\n    <tag k='leisure' v='picnic_table' />\n  </node>\n  <node id='5184349777' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7708391' lon='-122.399276'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184349778' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.770809' lon='-122.3993486'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184349779' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7707634' lon='-122.3992715'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184349780' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7708728' lon='-122.3991354'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184349781' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7707871' lon='-122.3991571'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184349782' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7707415' lon='-122.3991571'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184349783' timestamp='2017-10-23T18:29:49Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.771683' lon='-122.3985411'>\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='5184349785' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286' lat='37.7708223' lon='-122.3961141' />\n  <node id='5184349789' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286' lat='37.7712321' lon='-122.3968349' />\n  <node id='5184349791' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7699274' lon='-122.3982574' />\n  <node id='5184349792' timestamp='2018-04-13T21:53:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7701073' lon='-122.3980091' />\n  <node id='5184349793' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7701545' lon='-122.3979065' />\n  <node id='5184349794' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7701879' lon='-122.3977737' />\n  <node id='5184349795' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7702001' lon='-122.3975632' />\n  <node id='5184349796' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7702069' lon='-122.3974438' />\n  <node id='5184349797' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7702494' lon='-122.3972252' />\n  <node id='5184349798' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7703145' lon='-122.3970611' />\n  <node id='5184349799' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7704202' lon='-122.3968605' />\n  <node id='5184349800' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.77049' lon='-122.3967176' />\n  <node id='5184349801' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7705377' lon='-122.3965359' />\n  <node id='5184349802' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7705801' lon='-122.3963991' />\n  <node id='5184349803' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7706243' lon='-122.396337' />\n  <node id='5184349813' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7718488' lon='-122.3970866' />\n  <node id='5184349818' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7706081' lon='-122.397668' />\n  <node id='5184349819' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7701568' lon='-122.3981743' />\n  <node id='5184349820' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7708851' lon='-122.3982015' />\n  <node id='5184349832' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7716675' lon='-122.3984132' />\n  <node id='5184349833' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7717956' lon='-122.3983947'>\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='5184349835' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943' lat='37.7710444' lon='-122.3993496' />\n  <node id='5184540023' timestamp='2017-10-23T19:19:54Z' uid='53073' user='Aaron Lidman' version='1' changeset='53188507' lat='37.7721721' lon='-122.3968768'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5184540024' timestamp='2017-10-23T19:19:54Z' uid='53073' user='Aaron Lidman' version='1' changeset='53188507' lat='37.7724198' lon='-122.3965706'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5184540125' timestamp='2017-10-23T19:19:57Z' uid='53073' user='Aaron Lidman' version='1' changeset='53188507' lat='37.7709484' lon='-122.3993443'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184540126' timestamp='2017-10-23T19:19:57Z' uid='53073' user='Aaron Lidman' version='1' changeset='53188507' lat='37.7709008' lon='-122.3994112'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184540127' timestamp='2017-10-23T19:19:57Z' uid='53073' user='Aaron Lidman' version='1' changeset='53188507' lat='37.7704503' lon='-122.3988089'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184540128' timestamp='2017-10-23T19:19:57Z' uid='53073' user='Aaron Lidman' version='1' changeset='53188507' lat='37.770415' lon='-122.3987944'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184540129' timestamp='2017-10-23T19:19:57Z' uid='53073' user='Aaron Lidman' version='1' changeset='53188507' lat='37.7701735' lon='-122.398684'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5184837261' timestamp='2017-10-23T22:45:38Z' uid='53073' user='Aaron Lidman' version='1' changeset='53193135' lat='37.7709151' lon='-122.3990729'>\n    <tag k='access' v='private' />\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='5184837262' timestamp='2017-10-23T22:45:38Z' uid='53073' user='Aaron Lidman' version='1' changeset='53193135' lat='37.7710798' lon='-122.3993047' />\n  <node id='5184837264' timestamp='2017-10-23T22:45:38Z' uid='53073' user='Aaron Lidman' version='1' changeset='53193135' lat='37.7713747' lon='-122.3989302' />\n  <node id='5184837266' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='2' changeset='58077286' lat='37.7708986' lon='-122.3991099' />\n  <node id='5184864680' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='3' changeset='63558461' lat='37.7693247' lon='-122.4037935' />\n  <node id='5184864681' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='3' changeset='63558461' lat='37.7687275' lon='-122.4037392' />\n  <node id='5184864682' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='3' changeset='63558461' lat='37.7687641' lon='-122.4030983' />\n  <node id='5184864683' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='3' changeset='63558461' lat='37.7693627' lon='-122.4031467' />\n  <node id='5184864685' timestamp='2017-10-23T23:13:27Z' uid='53073' user='Aaron Lidman' version='1' changeset='53193519' lat='37.7696641' lon='-122.4037858'>\n    <tag k='amenity' v='restaurant' />\n    <tag k='name' v='The Grove' />\n  </node>\n  <node id='5184864686' timestamp='2018-10-15T23:38:03Z' uid='2237750' user='chachafish' version='3' changeset='63558461' lat='37.7693307' lon='-122.4036894'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5184864687' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269' lat='37.769458' lon='-122.4032877'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5287019804' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7720341' lon='-122.4041299' />\n  <node id='5287019805' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7719493' lon='-122.4042365' />\n  <node id='5287019811' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7711936' lon='-122.4051142' />\n  <node id='5287019812' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7712141' lon='-122.4051403' />\n  <node id='5287019813' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.771226' lon='-122.4051253' />\n  <node id='5287019814' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7712472' lon='-122.4051522' />\n  <node id='5287019815' timestamp='2018-03-18T21:06:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7712202' lon='-122.4051862' />\n  <node id='5287019816' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7711775' lon='-122.4051345' />\n  <node id='5287019818' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.771107' lon='-122.4050438' />\n  <node id='5287019820' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.7710693' lon='-122.4050123' />\n  <node id='5287020423' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.7710238' lon='-122.4049419' />\n  <node id='5287020424' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.7709973' lon='-122.4049225' />\n  <node id='5287020426' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.7709745' lon='-122.4048755' />\n  <node id='5287020431' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.7708594' lon='-122.4047488' />\n  <node id='5287020432' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.7708446' lon='-122.4047173' />\n  <node id='5287020433' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.7708446' lon='-122.4046764' />\n  <node id='5287020434' timestamp='2017-12-16T06:32:28Z' uid='371121' user='AndrewSnow' version='1' changeset='54672173' lat='37.7708531' lon='-122.4046509' />\n  <node id='5287020437' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7713201' lon='-122.4040706' />\n  <node id='5287020438' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7713328' lon='-122.404087' />\n  <node id='5287020439' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7713572' lon='-122.4040567' />\n  <node id='5287020440' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.77139' lon='-122.4040989' />\n  <node id='5287020441' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7714292' lon='-122.4040501' />\n  <node id='5287020442' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7714416' lon='-122.4040661' />\n  <node id='5287020443' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7714001' lon='-122.4041177' />\n  <node id='5287020444' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7716931' lon='-122.4044949' />\n  <node id='5287020445' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7717497' lon='-122.4044245' />\n  <node id='5287020446' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7717749' lon='-122.4044571' />\n  <node id='5287020447' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7720501' lon='-122.4041085' />\n  <node id='5287020448' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7721193' lon='-122.4041964' />\n  <node id='5287020449' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7717863' lon='-122.4046156' />\n  <node id='5287020450' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7717684' lon='-122.4045928' />\n  <node id='5287020451' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7716995' lon='-122.4046795' />\n  <node id='5287020454' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7713576' lon='-122.4042451' />\n  <node id='5287020455' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.771286' lon='-122.4043353' />\n  <node id='5287020458' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7716226' lon='-122.404763' />\n  <node id='5287020459' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7715487' lon='-122.4048561' />\n  <node id='5287020460' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7715688' lon='-122.4048816' />\n  <node id='5287020461' timestamp='2018-03-18T21:06:07Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060' lat='37.7712734' lon='-122.4052538' />\n  <node id='5376554067' timestamp='2018-02-07T21:43:35Z' uid='371121' user='AndrewSnow' version='2' changeset='56162152' lat='37.7700255' lon='-122.4042737'>\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='680' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:street' v='Division Street' />\n    <tag k='craft' v='cabinet_maker' />\n    <tag k='name' v='studiobecker' />\n  </node>\n  <node id='5376562309' timestamp='2018-01-31T21:05:49Z' uid='371121' user='AndrewSnow' version='1' changeset='55946628' lat='37.7694156' lon='-122.4042196'>\n    <tag k='amenity' v='cafe' />\n    <tag k='cuisine' v='sandwich' />\n    <tag k='name' v='Caffe Pazzo' />\n    <tag k='outdoor_seating' v='yes' />\n    <tag k='takeaway' v='yes' />\n  </node>\n  <node id='5389465206' timestamp='2018-02-05T10:11:48Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7706046' lon='-122.3857261' />\n  <node id='5389465207' timestamp='2018-02-05T10:11:48Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7706126' lon='-122.3857458' />\n  <node id='5389465208' timestamp='2018-02-05T10:11:48Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7706873' lon='-122.385885' />\n  <node id='5389465209' timestamp='2018-02-05T10:11:48Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7707093' lon='-122.3859091' />\n  <node id='5389465210' timestamp='2018-02-05T10:11:48Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7707748' lon='-122.3859255' />\n  <node id='5389465211' timestamp='2018-02-05T10:11:48Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7707925' lon='-122.3859074' />\n  <node id='5389465212' timestamp='2018-02-05T10:11:48Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7708225' lon='-122.3859098' />\n  <node id='5389465213' timestamp='2018-02-05T10:11:48Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7708368' lon='-122.3859379' />\n  <node id='5389465215' timestamp='2018-02-05T10:11:49Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7708588' lon='-122.3859621' />\n  <node id='5389465216' timestamp='2018-02-05T10:11:49Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7708736' lon='-122.3859624' />\n  <node id='5389466122' timestamp='2018-02-05T10:11:49Z' uid='933797' user='oba510' version='1' changeset='56080356' lat='37.7710807' lon='-122.3860208' />\n  <node id='5435465932' timestamp='2018-07-12T12:49:28Z' uid='8369524' user='swappa' version='2' changeset='60651560' lat='37.7704698' lon='-122.4074984' />\n  <node id='5435465957' timestamp='2018-02-25T15:05:00Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56663149' lat='37.7723485' lon='-122.4026891' />\n  <node id='5435466127' timestamp='2018-02-25T15:05:01Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56663149' lat='37.7754359' lon='-122.3937727' />\n  <node id='5438253559' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7696467' lon='-122.4000006' />\n  <node id='5438253560' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7695592' lon='-122.4000965' />\n  <node id='5438253561' timestamp='2018-03-26T19:01:25Z' uid='53073' user='Aaron Lidman' version='2' changeset='57545421' lat='37.7699822' lon='-122.3995842'>\n    <tag k='access' v='no' />\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='5438253562' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.770056' lon='-122.3994841' />\n  <node id='5438253563' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7699085' lon='-122.4004492' />\n  <node id='5438253564' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='2' changeset='57302156' lat='37.7697984' lon='-122.4024073' />\n  <node id='5438253565' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.76977' lon='-122.4028995' />\n  <node id='5438253566' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7697927' lon='-122.402515' />\n  <node id='5438253567' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7698641' lon='-122.4025219' />\n  <node id='5438253568' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7686401' lon='-122.4014282' />\n  <node id='5438253569' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7681397' lon='-122.4013813' />\n  <node id='5438253570' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7681083' lon='-122.4019059' />\n  <node id='5438253573' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7691482' lon='-122.4020061' />\n  <node id='5438253574' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7692147' lon='-122.4052517' />\n  <node id='5438253575' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7691755' lon='-122.40541' />\n  <node id='5438253576' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7686496' lon='-122.4053349' />\n  <node id='5438253584' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7680309' lon='-122.4048322' />\n  <node id='5438253585' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7693069' lon='-122.4057982' />\n  <node id='5438253586' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7694686' lon='-122.4058566' />\n  <node id='5438253587' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7693408' lon='-122.4056286' />\n  <node id='5438253588' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7695328' lon='-122.4056825' />\n  <node id='5438253589' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7695354' lon='-122.405646' />\n  <node id='5438253590' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7695444' lon='-122.4057841' />\n  <node id='5438253591' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7696728' lon='-122.4057841' />\n  <node id='5438253592' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7688065' lon='-122.4064989' />\n  <node id='5438253593' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7688898' lon='-122.4061596' />\n  <node id='5438253594' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7686586' lon='-122.4063058' />\n  <node id='5438253595' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7686698' lon='-122.4061053' />\n  <node id='5438253596' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.768832' lon='-122.4061382' />\n  <node id='5438253597' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7688653' lon='-122.4059504' />\n  <node id='5438253598' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7687132' lon='-122.4075933' />\n  <node id='5438253599' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7685929' lon='-122.4070582' />\n  <node id='5438253600' timestamp='2018-02-26T19:08:22Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939' lat='37.7684088' lon='-122.4070237' />\n  <node id='5449464493' timestamp='2018-03-03T01:29:56Z' uid='53073' user='Aaron Lidman' version='1' changeset='56836576' lat='37.7691009' lon='-122.4028607'>\n    <tag k='access' v='permissive' />\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='5449464494' timestamp='2018-03-03T01:29:56Z' uid='53073' user='Aaron Lidman' version='1' changeset='56836576' lat='37.7691415' lon='-122.4021759'>\n    <tag k='access' v='permissive' />\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='5449464496' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='3' changeset='63558461' lat='37.7687364' lon='-122.4035185'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5449464497' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269' lat='37.7694342' lon='-122.4036716' />\n  <node id='5449464498' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269' lat='37.7694627' lon='-122.4033659' />\n  <node id='5449464499' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269' lat='37.7695037' lon='-122.4036776' />\n  <node id='5449464500' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269' lat='37.7695363' lon='-122.4033725' />\n  <node id='5449464501' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269' lat='37.7695234' lon='-122.4036052' />\n  <node id='5449464502' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269' lat='37.7695077' lon='-122.4036038' />\n  <node id='5449464503' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269' lat='37.7695056' lon='-122.4036409'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5449466839' timestamp='2018-03-03T01:35:20Z' uid='53073' user='Aaron Lidman' version='1' changeset='56836615' lat='37.7682175' lon='-122.3999129' />\n  <node id='5449466840' timestamp='2018-03-03T01:35:20Z' uid='53073' user='Aaron Lidman' version='1' changeset='56836615' lat='37.7692095' lon='-122.3986624' />\n  <node id='5449466841' timestamp='2018-03-03T01:35:20Z' uid='53073' user='Aaron Lidman' version='1' changeset='56836615' lat='37.7690217' lon='-122.3984241' />\n  <node id='5449466842' timestamp='2018-03-03T01:35:20Z' uid='53073' user='Aaron Lidman' version='1' changeset='56836615' lat='37.7680298' lon='-122.3996746' />\n  <node id='5449466843' timestamp='2018-06-01T01:18:12Z' uid='53073' user='Aaron Lidman' version='2' changeset='59447024' lat='37.768163' lon='-122.4001799' />\n  <node id='5449466844' timestamp='2018-03-03T01:35:20Z' uid='53073' user='Aaron Lidman' version='1' changeset='56836615' lat='37.7692802' lon='-122.3987642' />\n  <node id='5449466845' timestamp='2018-03-03T01:35:20Z' uid='53073' user='Aaron Lidman' version='1' changeset='56836615' lat='37.7694742' lon='-122.3990103' />\n  <node id='5487246111' timestamp='2018-03-18T21:06:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301060' lat='37.7719212' lon='-122.4042719' />\n  <node id='5487246112' timestamp='2018-03-18T21:06:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301060' lat='37.7717987' lon='-122.4044276' />\n  <node id='5487246114' timestamp='2018-03-18T21:06:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301060' lat='37.7720411' lon='-122.4030819' />\n  <node id='5487246116' timestamp='2018-03-18T21:06:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301060' lat='37.771489' lon='-122.4037874' />\n  <node id='5487246119' timestamp='2018-03-18T21:06:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301060' lat='37.7714113' lon='-122.4038867' />\n  <node id='5487350515' timestamp='2018-03-18T22:01:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301995' lat='37.7712757' lon='-122.4052821' />\n  <node id='5487350516' timestamp='2018-03-18T22:01:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301995' lat='37.7725627' lon='-122.4036566' />\n  <node id='5487350517' timestamp='2018-03-18T22:01:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301995' lat='37.772039' lon='-122.4029995' />\n  <node id='5487350518' timestamp='2018-03-18T22:01:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301995' lat='37.7707351' lon='-122.4046276' />\n  <node id='5487354231' timestamp='2018-03-29T20:50:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57645807' lat='37.7700015' lon='-122.4020862'>\n    <tag k='barrier' v='bollard' />\n  </node>\n  <node id='5487354232' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156' lat='37.769811' lon='-122.4022176' />\n  <node id='5487354233' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156' lat='37.7698849' lon='-122.4021666' />\n  <node id='5487354234' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156' lat='37.7697775' lon='-122.4027728' />\n  <node id='5487354235' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156' lat='37.7698517' lon='-122.4027816' />\n  <node id='5487354238' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156' lat='37.7699296' lon='-122.404258' />\n  <node id='5487354239' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156' lat='37.7696978' lon='-122.4041318' />\n  <node id='5487354240' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156' lat='37.7699471' lon='-122.4035915' />\n  <node id='5487354242' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156' lat='37.7702125' lon='-122.4039102' />\n  <node id='5497302690' timestamp='2018-03-22T18:33:27Z' uid='53073' user='Aaron Lidman' version='1' changeset='57432960' lat='37.7692586' lon='-122.4069619'>\n    <tag k='amenity' v='bicycle_rental' />\n    <tag k='network' v='Ford GoBike' />\n    <tag k='payment:credit_cards' v='yes' />\n    <tag k='payment:electronic_purses' v='yes' />\n  </node>\n  <node id='5506466408' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7697141' lon='-122.4038387' />\n  <node id='5506466409' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7687212' lon='-122.4037441' />\n  <node id='5506466410' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7687603' lon='-122.4030868' />\n  <node id='5506466411' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='2' changeset='63557245' lat='37.7697533' lon='-122.4031814' />\n  <node id='5506470737' timestamp='2018-03-26T19:01:20Z' uid='53073' user='Aaron Lidman' version='1' changeset='57545421' lat='37.7708698' lon='-122.3960942' />\n  <node id='5506470752' timestamp='2018-03-26T19:01:21Z' uid='53073' user='Aaron Lidman' version='1' changeset='57545421' lat='37.7692693' lon='-122.3985025'>\n    <tag k='crossing' v='zebra' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='5515121096' timestamp='2018-03-29T20:50:43Z' uid='53073' user='Aaron Lidman' version='1' changeset='57645807' lat='37.7698908' lon='-122.4006353'>\n    <tag k='access' v='no' />\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='5515121097' timestamp='2018-03-29T20:50:43Z' uid='53073' user='Aaron Lidman' version='1' changeset='57645807' lat='37.7700312' lon='-122.4021091' />\n  <node id='5515121098' timestamp='2018-03-29T20:50:43Z' uid='53073' user='Aaron Lidman' version='1' changeset='57645807' lat='37.7702389' lon='-122.401482' />\n  <node id='5515121099' timestamp='2018-03-29T20:50:43Z' uid='53073' user='Aaron Lidman' version='1' changeset='57645807' lat='37.7701986' lon='-122.4012224' />\n  <node id='5515121100' timestamp='2018-03-29T20:50:43Z' uid='53073' user='Aaron Lidman' version='1' changeset='57645807' lat='37.7701332' lon='-122.400753' />\n  <node id='5515121101' timestamp='2018-03-29T20:50:43Z' uid='53073' user='Aaron Lidman' version='1' changeset='57645807' lat='37.7699602' lon='-122.402072' />\n  <node id='5515207393' timestamp='2018-03-29T21:43:12Z' uid='53073' user='Aaron Lidman' version='1' changeset='57646984' lat='37.7689486' lon='-122.4027466'>\n    <tag k='name' v='Jawbone Health' />\n    <tag k='office' v='company' />\n  </node>\n  <node id='5551421960' timestamp='2018-04-13T21:53:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='58077286' lat='37.7700861' lon='-122.3986374' />\n  <node id='5551421961' timestamp='2018-04-13T21:53:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='58077286' lat='37.7766871' lon='-122.3903453' />\n  <node id='5551421966' timestamp='2018-04-13T21:53:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='58077286' lat='37.7698614' lon='-122.3983604' />\n  <node id='5551421967' timestamp='2018-04-13T21:53:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='58077286' lat='37.7711897' lon='-122.395868' />\n  <node id='5551421968' timestamp='2018-04-13T21:53:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='58077286' lat='37.7713126' lon='-122.3956507' />\n  <node id='5640337866' timestamp='2018-05-24T18:55:27Z' uid='7134350' user='KaL fvr' version='1' changeset='59250365' lat='37.7745987' lon='-122.3818349' />\n  <node id='5640337867' timestamp='2018-05-24T18:55:27Z' uid='7134350' user='KaL fvr' version='1' changeset='59250365' lat='37.7746379' lon='-122.3818932' />\n  <node id='5640337868' timestamp='2018-05-24T18:55:27Z' uid='7134350' user='KaL fvr' version='1' changeset='59250365' lat='37.7746363' lon='-122.3818701' />\n  <node id='5640337869' timestamp='2018-05-24T18:55:27Z' uid='7134350' user='KaL fvr' version='1' changeset='59250365' lat='37.7746142' lon='-122.3818372' />\n  <node id='5780936416' timestamp='2018-07-24T06:46:42Z' uid='4540953' user='Adamant1' version='1' changeset='61008584' lat='37.7695525' lon='-122.399048'>\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='5914927381' timestamp='2018-09-19T00:38:21Z' uid='53073' user='Aaron Lidman' version='1' changeset='62713402' lat='37.7676293' lon='-122.4001635' />\n  <node id='5986733764' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697125' lon='-122.4037183' />\n  <node id='5986733765' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697086' lon='-122.4038196' />\n  <node id='5986733766' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769701' lon='-122.4038189' />\n  <node id='5986733767' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697064' lon='-122.4037177' />\n  <node id='5986733768' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693116' lon='-122.4036877' />\n  <node id='5986733769' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.769316' lon='-122.4036098' />\n  <node id='5986733770' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693459' lon='-122.4036125' />\n  <node id='5986733771' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693481' lon='-122.4035735' />\n  <node id='5986733772' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693383' lon='-122.4035726' />\n  <node id='5986733773' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693401' lon='-122.4035401' />\n  <node id='5986733774' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693278' lon='-122.403539' />\n  <node id='5986733775' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693394' lon='-122.4033359' />\n  <node id='5986733776' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693321' lon='-122.4033352' />\n  <node id='5986733777' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693366' lon='-122.4032549' />\n  <node id='5986733778' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7693564' lon='-122.4032567' />\n  <node id='5986733779' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691554' lon='-122.4031265' />\n  <node id='5986733780' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691489' lon='-122.4032398' />\n  <node id='5986733781' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691673' lon='-122.4032802' />\n  <node id='5986733782' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691695' lon='-122.4032417' />\n  <node id='5986733783' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691081' lon='-122.4032748' />\n  <node id='5986733784' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691097' lon='-122.4032461' />\n  <node id='5986734185' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689976' lon='-122.4032358' />\n  <node id='5986734186' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.768996' lon='-122.4032629' />\n  <node id='5986734187' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689362' lon='-122.4032572' />\n  <node id='5986734188' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689387' lon='-122.4032146' />\n  <node id='5986734189' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689596' lon='-122.4031129' />\n  <node id='5986734190' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689536' lon='-122.403216' />\n  <node id='5986734191' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687336' lon='-122.4036314' />\n  <node id='5986734192' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687347' lon='-122.40355' />\n  <node id='5986734193' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687477' lon='-122.4036327' />\n  <node id='5986734194' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687523' lon='-122.4035516' />\n  <node id='5986734195' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687384' lon='-122.4034851' />\n  <node id='5986734196' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687518' lon='-122.4034864' />\n  <node id='5986734197' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687636' lon='-122.4032871' />\n  <node id='5986734198' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687704' lon='-122.4032877' />\n  <node id='5986734199' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7687585' lon='-122.4031921' />\n  <node id='5986734200' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.768776' lon='-122.4031937' />\n  <node id='5986734201' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689285' lon='-122.4036478' />\n  <node id='5986734202' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691264' lon='-122.4036671' />\n  <node id='5986734203' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.76914' lon='-122.4035471' />\n  <node id='5986734204' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691334' lon='-122.4036677' />\n  <node id='5986734205' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691581' lon='-122.4034657' />\n  <node id='5986734206' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691535' lon='-122.4035483' />\n  <node id='5986734207' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689219' lon='-122.4036472' />\n  <node id='5986734208' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689283' lon='-122.4035295' />\n  <node id='5986734209' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689197' lon='-122.4034428' />\n  <node id='5986734210' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689151' lon='-122.4035283' />\n  <node id='5986734211' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689909' lon='-122.403449' />\n  <node id='5986734212' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7690891' lon='-122.4034597' />\n  <node id='5986734213' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7690875' lon='-122.4034879' />\n  <node id='5986734214' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689893' lon='-122.4034793' />\n  <node id='5986734215' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689345' lon='-122.4031105' />\n  <node id='5986734216' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.768869' lon='-122.4031082' />\n  <node id='5986734217' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689349' lon='-122.4031034' />\n  <node id='5986734218' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7688696' lon='-122.4030972' />\n  <node id='5986734219' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7692558' lon='-122.403137' />\n  <node id='5986734220' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691829' lon='-122.403129' />\n  <node id='5986734221' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7692562' lon='-122.4031311' />\n  <node id='5986734222' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691832' lon='-122.4031245' />\n  <node id='5986734223' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769531' lon='-122.4038126' />\n  <node id='5986734224' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696145' lon='-122.4038188' />\n  <node id='5986734225' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696149' lon='-122.4038104' />\n  <node id='5986734226' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695315' lon='-122.4038032' />\n  <node id='5986734227' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690824' lon='-122.4036714' />\n  <node id='5986734228' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690817' lon='-122.4036826' />\n  <node id='5986734229' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690682' lon='-122.4036813' />\n  <node id='5986734230' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690708' lon='-122.4036371' />\n  <node id='5986734231' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690568' lon='-122.4036358' />\n  <node id='5986734232' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690579' lon='-122.4036171' />\n  <node id='5986734233' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.768995' lon='-122.4036113' />\n  <node id='5986734234' timestamp='2018-10-15T19:08:08Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.768994' lon='-122.4036287' />\n  <node id='5986734235' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.768981' lon='-122.4036275' />\n  <node id='5986734236' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7689784' lon='-122.4036723' />\n  <node id='5986734237' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7689595' lon='-122.4036706' />\n  <node id='5986734238' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7689603' lon='-122.4036579' />\n  <node id='5986734239' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7689535' lon='-122.4036573' />\n  <node id='5986734240' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7689484' lon='-122.4037452' />\n  <node id='5986734241' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689233' lon='-122.4037429' />\n  <node id='5986734242' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690853' lon='-122.4037717' />\n  <node id='5986734243' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769091' lon='-122.4036722' />\n  <node id='5986734244' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690862' lon='-122.4037562' />\n  <node id='5986734245' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691213' lon='-122.4037594' />\n  <node id='5986734246' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7691204' lon='-122.4037749' />\n  <node id='5986734247' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7689225' lon='-122.4037567' />\n  <node id='5986734248' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7689476' lon='-122.403759' />\n  <node id='5986734249' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695726' lon='-122.4036085' />\n  <node id='5986734250' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690504' lon='-122.4037683' />\n  <node id='5986734251' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7690489' lon='-122.4037965' />\n  <node id='5986734252' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7689726' lon='-122.40379' />\n  <node id='5986734253' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7689741' lon='-122.4037617' />\n  <node id='5986734254' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695685' lon='-122.4036887' />\n  <node id='5986734255' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695909' lon='-122.4036838' />\n  <node id='5986734256' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695947' lon='-122.403608' />\n  <node id='5986734257' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695274' lon='-122.4036048' />\n  <node id='5986734258' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695384' lon='-122.4033906' />\n  <node id='5986734259' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695454' lon='-122.4033912' />\n  <node id='5986734260' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695505' lon='-122.4032915' />\n  <node id='5986734261' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769467' lon='-122.4032885' />\n  <node id='5986734262' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695689' lon='-122.4031733' />\n  <node id='5986734263' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695685' lon='-122.4031815' />\n  <node id='5986734264' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696556' lon='-122.4033001' />\n  <node id='5986734265' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696484' lon='-122.4031886' />\n  <node id='5986734266' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697352' lon='-122.4032892' />\n  <node id='5986734267' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696488' lon='-122.4031814' />\n  <node id='5986734268' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697433' lon='-122.4031968' />\n  <node id='5986734269' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697403' lon='-122.4031965' />\n  <node id='5986734270' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697372' lon='-122.4033391' />\n  <node id='5986734271' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697399' lon='-122.4032896' />\n  <node id='5986734272' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696513' lon='-122.4033835' />\n  <node id='5986734273' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696624' lon='-122.4036133' />\n  <node id='5986734274' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696587' lon='-122.4036891' />\n  <node id='5986734275' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695481' lon='-122.4033894' />\n  <node id='5986734276' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695519' lon='-122.4033149' />\n  <node id='5986734277' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696371' lon='-122.4033218' />\n  <node id='5986734278' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696333' lon='-122.4033962' />\n  <node id='5986734279' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696653' lon='-122.4036967' />\n  <node id='5986734280' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697272' lon='-122.4033382' />\n  <node id='5986734281' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697085' lon='-122.4036801' />\n  <node id='5986734282' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697135' lon='-122.4037008' />\n  <node id='5986734283' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697146' lon='-122.4036806' />\n  <node id='5986734284' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769708' lon='-122.4033592' />\n  <node id='5986734285' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697091' lon='-122.4033368' />\n  <node id='5986734286' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696843' lon='-122.4033573' />\n  <node id='5986734287' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696828' lon='-122.4033861' />\n  <node id='5986734288' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696005' lon='-122.4036059' />\n  <node id='5986734289' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696008' lon='-122.4035997' />\n  <node id='5986734290' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696803' lon='-122.4033886' />\n  <node id='5986734291' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696679' lon='-122.4036119' />\n  <node id='5986734292' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696753' lon='-122.4033881' />\n  <node id='5986734293' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696632' lon='-122.4036053' />\n  <node id='5986734294' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696661' lon='-122.4035537' />\n  <node id='5986734295' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696666' lon='-122.4035452' />\n  <node id='5986734296' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696346' lon='-122.4035509' />\n  <node id='5986734297' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696351' lon='-122.4035424' />\n  <node id='5986734298' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696695' lon='-122.4034929' />\n  <node id='5986734299' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696698' lon='-122.4034863' />\n  <node id='5986734300' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696377' lon='-122.4034901' />\n  <node id='5986734301' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769638' lon='-122.4034835' />\n  <node id='5986734302' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696727' lon='-122.4034358' />\n  <node id='5986734303' timestamp='2018-10-15T19:08:09Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696731' lon='-122.4034282' />\n  <node id='5986734304' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696414' lon='-122.4034331' />\n  <node id='5986734305' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696419' lon='-122.4034254' />\n  <node id='5986734306' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695734' lon='-122.4036' />\n  <node id='5986734307' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695343' lon='-122.403597' />\n  <node id='5986734308' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769537' lon='-122.4035424' />\n  <node id='5986734309' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695685' lon='-122.4035448' />\n  <node id='5986734310' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695689' lon='-122.4035365' />\n  <node id='5986734311' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695373' lon='-122.4035341' />\n  <node id='5986734312' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695398' lon='-122.4034816' />\n  <node id='5986734313' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695717' lon='-122.4034841' />\n  <node id='5986734314' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769572' lon='-122.4034776' />\n  <node id='5986734315' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695402' lon='-122.4034752' />\n  <node id='5986734316' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695426' lon='-122.4034245' />\n  <node id='5986734317' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695738' lon='-122.4034269' />\n  <node id='5986734318' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695742' lon='-122.4034193' />\n  <node id='5986734319' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769543' lon='-122.403417' />\n  <node id='5986734320' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695441' lon='-122.4033927' />\n  <node id='5986734321' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695387' lon='-122.4033923' />\n  <node id='5986734322' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695287' lon='-122.4036033' />\n  <node id='5986734323' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695731' lon='-122.4036067' />\n  <node id='5986734324' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694973' lon='-122.4036799' />\n  <node id='5986734325' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695002' lon='-122.4036801' />\n  <node id='5986734326' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694953' lon='-122.4037944' />\n  <node id='5986734327' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694924' lon='-122.4037942' />\n  <node id='5986734328' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694362' lon='-122.403789' />\n  <node id='5986734329' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694364' lon='-122.4037853' />\n  <node id='5986734330' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694948' lon='-122.4037906' />\n  <node id='5986734331' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694946' lon='-122.4037943' />\n  <node id='5986734332' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694388' lon='-122.4038044' />\n  <node id='5986734333' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694358' lon='-122.4038042' />\n  <node id='5986734334' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694367' lon='-122.403786' />\n  <node id='5986734335' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694396' lon='-122.4037863' />\n  <node id='5986734336' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696262' lon='-122.4037034' />\n  <node id='5986734337' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696292' lon='-122.4037036' />\n  <node id='5986734338' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696243' lon='-122.403818' />\n  <node id='5986734339' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696213' lon='-122.4038178' />\n  <node id='5986734340' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696558' lon='-122.403319' />\n  <node id='5986734341' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769656' lon='-122.4033153' />\n  <node id='5986734342' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697352' lon='-122.4033218' />\n  <node id='5986734343' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769735' lon='-122.4033255' />\n  <node id='5986734344' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696566' lon='-122.4031837' />\n  <node id='5986734345' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696595' lon='-122.4031839' />\n  <node id='5986734346' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696546' lon='-122.4032982' />\n  <node id='5986734347' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696516' lon='-122.403298' />\n  <node id='5986734348' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696092' lon='-122.4037213' />\n  <node id='5986734349' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695883' lon='-122.4037197' />\n  <node id='5986734350' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696123' lon='-122.403696' />\n  <node id='5986734351' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769589' lon='-122.4037042' />\n  <node id='5986734352' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695084' lon='-122.4036987' />\n  <node id='5986734353' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695009' lon='-122.4036885' />\n  <node id='5986734354' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694999' lon='-122.403713' />\n  <node id='5986734355' timestamp='2018-10-15T19:08:10Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695078' lon='-122.4037135' />\n  <node id='5986734356' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695891' lon='-122.4037028' />\n  <node id='5986734357' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.76961' lon='-122.4037042' />\n  <node id='5986734358' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696169' lon='-122.4037053' />\n  <node id='5986734359' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696094' lon='-122.4037178' />\n  <node id='5986734360' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696241' lon='-122.4037188' />\n  <node id='5986734361' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696245' lon='-122.4037097' />\n  <node id='5986734362' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696168' lon='-122.4037092' />\n  <node id='5986734363' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696119' lon='-122.4037049' />\n  <node id='5986734364' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694868' lon='-122.4037661' />\n  <node id='5986734365' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694866' lon='-122.4037698' />\n  <node id='5986734366' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694767' lon='-122.4037652' />\n  <node id='5986734367' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694766' lon='-122.4037689' />\n  <node id='5986734368' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769477' lon='-122.4037518' />\n  <node id='5986734369' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694871' lon='-122.4037527' />\n  <node id='5986734370' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694869' lon='-122.4037564' />\n  <node id='5986734371' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694768' lon='-122.4037555' />\n  <node id='5986734372' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694775' lon='-122.4037436' />\n  <node id='5986734373' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694876' lon='-122.4037445' />\n  <node id='5986734374' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694874' lon='-122.4037482' />\n  <node id='5986734375' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694774' lon='-122.4037473' />\n  <node id='5986734376' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694786' lon='-122.403728' />\n  <node id='5986734377' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694887' lon='-122.4037289' />\n  <node id='5986734378' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694885' lon='-122.4037326' />\n  <node id='5986734379' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694784' lon='-122.4037317' />\n  <node id='5986734380' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769479' lon='-122.4037211' />\n  <node id='5986734381' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694891' lon='-122.403722' />\n  <node id='5986734382' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694889' lon='-122.4037257' />\n  <node id='5986734383' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694788' lon='-122.4037248' />\n  <node id='5986734384' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694788' lon='-122.4037136' />\n  <node id='5986734385' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694889' lon='-122.4037144' />\n  <node id='5986734386' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694887' lon='-122.4037182' />\n  <node id='5986734387' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7694787' lon='-122.4037173' />\n  <node id='5986734388' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695355' lon='-122.4037171' />\n  <node id='5986734389' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695456' lon='-122.403718' />\n  <node id='5986734390' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695454' lon='-122.4037217' />\n  <node id='5986734391' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695353' lon='-122.4037208' />\n  <node id='5986734392' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695339' lon='-122.4037241' />\n  <node id='5986734393' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769544' lon='-122.403725' />\n  <node id='5986734394' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695438' lon='-122.4037287' />\n  <node id='5986734395' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695337' lon='-122.4037278' />\n  <node id='5986734396' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695644' lon='-122.4037463' />\n  <node id='5986734397' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695745' lon='-122.4037471' />\n  <node id='5986734398' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695743' lon='-122.4037509' />\n  <node id='5986734399' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695642' lon='-122.40375' />\n  <node id='5986734400' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695758' lon='-122.4037294' />\n  <node id='5986734401' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695751' lon='-122.4037506' />\n  <node id='5986734402' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695722' lon='-122.4037504' />\n  <node id='5986734403' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695729' lon='-122.4037293' />\n  <node id='5986734404' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696219' lon='-122.4037318' />\n  <node id='5986734405' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696215' lon='-122.4037446' />\n  <node id='5986734406' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696186' lon='-122.4037445' />\n  <node id='5986734407' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696189' lon='-122.4037317' />\n  <node id='5986734408' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696207' lon='-122.4037474' />\n  <node id='5986734409' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696203' lon='-122.4037602' />\n  <node id='5986734410' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696174' lon='-122.4037601' />\n  <node id='5986734411' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696177' lon='-122.4037473' />\n  <node id='5986734412' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696198' lon='-122.4037637' />\n  <node id='5986734413' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696194' lon='-122.4037764' />\n  <node id='5986734414' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696165' lon='-122.4037763' />\n  <node id='5986734415' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696168' lon='-122.4037636' />\n  <node id='5986734416' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696589' lon='-122.4037217' />\n  <node id='5986734417' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696586' lon='-122.4037392' />\n  <node id='5986734418' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696557' lon='-122.4037391' />\n  <node id='5986734419' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696559' lon='-122.4037217' />\n  <node id='5986734420' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696395' lon='-122.4037213' />\n  <node id='5986734421' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696583' lon='-122.4037219' />\n  <node id='5986734422' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696582' lon='-122.4037256' />\n  <node id='5986734423' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696394' lon='-122.4037251' />\n  <node id='5986734424' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696391' lon='-122.403735' />\n  <node id='5986734425' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696579' lon='-122.4037355' />\n  <node id='5986734426' timestamp='2018-10-15T19:08:11Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696578' lon='-122.4037393' />\n  <node id='5986734427' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769639' lon='-122.4037387' />\n  <node id='5986734428' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696375' lon='-122.4037209' />\n  <node id='5986734429' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696372' lon='-122.4037384' />\n  <node id='5986734430' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696343' lon='-122.4037383' />\n  <node id='5986734431' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696345' lon='-122.4037208' />\n  <node id='5986734432' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696417' lon='-122.4036961' />\n  <node id='5986734433' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696415' lon='-122.403709' />\n  <node id='5986734434' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696386' lon='-122.4037089' />\n  <node id='5986734435' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696388' lon='-122.403696' />\n  <node id='5986734436' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695439' lon='-122.4037315' />\n  <node id='5986734437' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695435' lon='-122.4037442' />\n  <node id='5986734438' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695406' lon='-122.4037441' />\n  <node id='5986734439' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695409' lon='-122.4037314' />\n  <node id='5986734440' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695436' lon='-122.4037466' />\n  <node id='5986734441' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695433' lon='-122.4037593' />\n  <node id='5986734442' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695403' lon='-122.4037592' />\n  <node id='5986734443' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695407' lon='-122.4037464' />\n  <node id='5986734444' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695367' lon='-122.4037311' />\n  <node id='5986734445' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695364' lon='-122.4037439' />\n  <node id='5986734446' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695334' lon='-122.4037438' />\n  <node id='5986734447' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695338' lon='-122.403731' />\n  <node id='5986734448' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695362' lon='-122.4037462' />\n  <node id='5986734449' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695359' lon='-122.403759' />\n  <node id='5986734450' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695329' lon='-122.4037589' />\n  <node id='5986734451' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695333' lon='-122.4037461' />\n  <node id='5986734452' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695445' lon='-122.4036783' />\n  <node id='5986734453' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769553' lon='-122.4036785' />\n  <node id='5986734454' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769553' lon='-122.4036822' />\n  <node id='5986734455' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695445' lon='-122.4036821' />\n  <node id='5986734456' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695446' lon='-122.4036693' />\n  <node id='5986734457' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695531' lon='-122.4036695' />\n  <node id='5986734458' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769553' lon='-122.4036732' />\n  <node id='5986734459' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695445' lon='-122.403673' />\n  <node id='5986734460' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695327' lon='-122.4036778' />\n  <node id='5986734461' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695411' lon='-122.403678' />\n  <node id='5986734462' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695411' lon='-122.4036818' />\n  <node id='5986734463' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695326' lon='-122.4036816' />\n  <node id='5986734464' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695329' lon='-122.4036694' />\n  <node id='5986734465' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695413' lon='-122.4036695' />\n  <node id='5986734466' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695413' lon='-122.4036733' />\n  <node id='5986734467' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695328' lon='-122.4036731' />\n  <node id='5986734468' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695215' lon='-122.4036773' />\n  <node id='5986734469' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695299' lon='-122.4036774' />\n  <node id='5986734470' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695299' lon='-122.4036812' />\n  <node id='5986734471' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695214' lon='-122.403681' />\n  <node id='5986734472' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695217' lon='-122.4036691' />\n  <node id='5986734473' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695302' lon='-122.4036693' />\n  <node id='5986734474' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695302' lon='-122.403673' />\n  <node id='5986734475' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695217' lon='-122.4036729' />\n  <node id='5986734476' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695311' lon='-122.403634' />\n  <node id='5986734477' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695473' lon='-122.4036346' />\n  <node id='5986734478' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769547' lon='-122.4036492' />\n  <node id='5986734479' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695308' lon='-122.4036487' />\n  <node id='5986734480' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695568' lon='-122.4036676' />\n  <node id='5986734481' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695633' lon='-122.4036678' />\n  <node id='5986734482' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769563' lon='-122.4036825' />\n  <node id='5986734483' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695565' lon='-122.4036822' />\n  <node id='5986734484' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696887' lon='-122.4036583' />\n  <node id='5986734485' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696886' lon='-122.4036679' />\n  <node id='5986734486' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696857' lon='-122.4036679' />\n  <node id='5986734487' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696858' lon='-122.4036582' />\n  <node id='5986734488' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696943' lon='-122.4036593' />\n  <node id='5986734489' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696942' lon='-122.403669' />\n  <node id='5986734490' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696913' lon='-122.4036689' />\n  <node id='5986734491' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696914' lon='-122.4036593' />\n  <node id='5986734492' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697061' lon='-122.4036607' />\n  <node id='5986734493' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769706' lon='-122.4036703' />\n  <node id='5986734494' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697031' lon='-122.4036702' />\n  <node id='5986734495' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697032' lon='-122.4036606' />\n  <node id='5986734496' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697073' lon='-122.4036429' />\n  <node id='5986734497' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697072' lon='-122.4036525' />\n  <node id='5986734498' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697043' lon='-122.4036525' />\n  <node id='5986734499' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697044' lon='-122.4036428' />\n  <node id='5986734500' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696951' lon='-122.4036424' />\n  <node id='5986734501' timestamp='2018-10-15T19:08:12Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769695' lon='-122.403652' />\n  <node id='5986734502' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696921' lon='-122.403652' />\n  <node id='5986734503' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696922' lon='-122.4036423' />\n  <node id='5986734504' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696897' lon='-122.4036422' />\n  <node id='5986734505' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696896' lon='-122.4036519' />\n  <node id='5986734506' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696866' lon='-122.4036518' />\n  <node id='5986734507' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696867' lon='-122.4036422' />\n  <node id='5986734508' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696969' lon='-122.4036248' />\n  <node id='5986734509' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696967' lon='-122.4036344' />\n  <node id='5986734510' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696938' lon='-122.4036344' />\n  <node id='5986734511' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696939' lon='-122.4036247' />\n  <node id='5986734512' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696984' lon='-122.4035799' />\n  <node id='5986734513' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696983' lon='-122.4035895' />\n  <node id='5986734514' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696954' lon='-122.4035894' />\n  <node id='5986734515' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696955' lon='-122.4035798' />\n  <node id='5986734516' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697093' lon='-122.4035805' />\n  <node id='5986734517' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697092' lon='-122.4035902' />\n  <node id='5986734518' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697062' lon='-122.4035901' />\n  <node id='5986734519' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697064' lon='-122.4035805' />\n  <node id='5986734520' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.76971' lon='-122.4035629' />\n  <node id='5986734521' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697099' lon='-122.4035726' />\n  <node id='5986734522' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697069' lon='-122.4035725' />\n  <node id='5986734523' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769707' lon='-122.4035629' />\n  <node id='5986734524' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696994' lon='-122.4035607' />\n  <node id='5986734525' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696993' lon='-122.4035704' />\n  <node id='5986734526' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696963' lon='-122.4035703' />\n  <node id='5986734527' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696964' lon='-122.4035607' />\n  <node id='5986734528' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697004' lon='-122.4035448' />\n  <node id='5986734529' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697003' lon='-122.4035545' />\n  <node id='5986734530' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696974' lon='-122.4035544' />\n  <node id='5986734531' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696975' lon='-122.4035448' />\n  <node id='5986734532' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697112' lon='-122.4035457' />\n  <node id='5986734533' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697111' lon='-122.4035553' />\n  <node id='5986734534' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697081' lon='-122.4035552' />\n  <node id='5986734535' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697082' lon='-122.4035456' />\n  <node id='5986734536' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697026' lon='-122.4035004' />\n  <node id='5986734537' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697024' lon='-122.40351' />\n  <node id='5986734538' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696995' lon='-122.40351' />\n  <node id='5986734539' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696996' lon='-122.4035003' />\n  <node id='5986734540' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697035' lon='-122.4034873' />\n  <node id='5986734541' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697034' lon='-122.403497' />\n  <node id='5986734542' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697004' lon='-122.4034969' />\n  <node id='5986734543' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697005' lon='-122.4034873' />\n  <node id='5986734544' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697045' lon='-122.4034722' />\n  <node id='5986734545' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697044' lon='-122.4034819' />\n  <node id='5986734546' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697015' lon='-122.4034818' />\n  <node id='5986734547' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697016' lon='-122.4034722' />\n  <node id='5986734548' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697146' lon='-122.4035012' />\n  <node id='5986734549' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697145' lon='-122.4035109' />\n  <node id='5986734550' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697116' lon='-122.4035108' />\n  <node id='5986734551' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697117' lon='-122.4035012' />\n  <node id='5986734552' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697151' lon='-122.4034875' />\n  <node id='5986734553' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769715' lon='-122.4034971' />\n  <node id='5986734554' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697121' lon='-122.4034971' />\n  <node id='5986734555' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697122' lon='-122.4034874' />\n  <node id='5986734556' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697158' lon='-122.4034717' />\n  <node id='5986734557' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697157' lon='-122.4034814' />\n  <node id='5986734558' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697127' lon='-122.4034813' />\n  <node id='5986734559' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697129' lon='-122.4034717' />\n  <node id='5986734560' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697186' lon='-122.4034117' />\n  <node id='5986734561' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697185' lon='-122.4034214' />\n  <node id='5986734562' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697155' lon='-122.4034213' />\n  <node id='5986734563' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697156' lon='-122.4034117' />\n  <node id='5986734564' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697206' lon='-122.4033939' />\n  <node id='5986734565' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697205' lon='-122.4034036' />\n  <node id='5986734566' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697175' lon='-122.4034035' />\n  <node id='5986734567' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697176' lon='-122.4033939' />\n  <node id='5986734568' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697092' lon='-122.4033933' />\n  <node id='5986734569' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697091' lon='-122.4034029' />\n  <node id='5986734570' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697061' lon='-122.4034029' />\n  <node id='5986734571' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697062' lon='-122.4033932' />\n  <node id='5986734572' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697085' lon='-122.403409' />\n  <node id='5986734573' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697084' lon='-122.4034187' />\n  <node id='5986734574' timestamp='2018-10-15T19:08:13Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697055' lon='-122.4034186' />\n  <node id='5986734575' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697056' lon='-122.403409' />\n  <node id='5986734576' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697073' lon='-122.4034278' />\n  <node id='5986734577' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697072' lon='-122.4034375' />\n  <node id='5986734578' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697043' lon='-122.4034374' />\n  <node id='5986734579' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697044' lon='-122.4034278' />\n  <node id='5986734580' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697032' lon='-122.4033921' />\n  <node id='5986734581' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697031' lon='-122.4034017' />\n  <node id='5986734582' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697002' lon='-122.4034017' />\n  <node id='5986734583' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697003' lon='-122.403392' />\n  <node id='5986734584' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696803' lon='-122.4033306' />\n  <node id='5986734585' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769688' lon='-122.4033307' />\n  <node id='5986734586' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696879' lon='-122.4033345' />\n  <node id='5986734587' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696803' lon='-122.4033343' />\n  <node id='5986734588' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696945' lon='-122.4033312' />\n  <node id='5986734589' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697021' lon='-122.4033314' />\n  <node id='5986734590' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697021' lon='-122.4033351' />\n  <node id='5986734591' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696945' lon='-122.403335' />\n  <node id='5986734592' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696794' lon='-122.4033391' />\n  <node id='5986734593' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769687' lon='-122.4033393' />\n  <node id='5986734594' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769687' lon='-122.403343' />\n  <node id='5986734595' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696794' lon='-122.4033428' />\n  <node id='5986734596' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696651' lon='-122.4033393' />\n  <node id='5986734597' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696727' lon='-122.4033395' />\n  <node id='5986734598' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696727' lon='-122.4033432' />\n  <node id='5986734599' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769665' lon='-122.403343' />\n  <node id='5986734600' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696655' lon='-122.4033287' />\n  <node id='5986734601' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696731' lon='-122.4033289' />\n  <node id='5986734602' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696731' lon='-122.4033326' />\n  <node id='5986734603' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696654' lon='-122.4033324' />\n  <node id='5986734604' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769654' lon='-122.4033659' />\n  <node id='5986734605' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696684' lon='-122.403367' />\n  <node id='5986734606' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696682' lon='-122.4033707' />\n  <node id='5986734607' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696539' lon='-122.4033696' />\n  <node id='5986734608' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696525' lon='-122.4033824' />\n  <node id='5986734609' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696535' lon='-122.4033642' />\n  <node id='5986734610' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696565' lon='-122.4033645' />\n  <node id='5986734611' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696554' lon='-122.4033826' />\n  <node id='5986734612' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696593' lon='-122.4033437' />\n  <node id='5986734613' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696599' lon='-122.4033255' />\n  <node id='5986734614' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696629' lon='-122.4033256' />\n  <node id='5986734615' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696623' lon='-122.4033438' />\n  <node id='5986734616' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696761' lon='-122.4032616' />\n  <node id='5986734617' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769676' lon='-122.4032713' />\n  <node id='5986734618' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696731' lon='-122.4032712' />\n  <node id='5986734619' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696732' lon='-122.4032616' />\n  <node id='5986734620' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696945' lon='-122.4032638' />\n  <node id='5986734621' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696944' lon='-122.4032735' />\n  <node id='5986734622' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696915' lon='-122.4032734' />\n  <node id='5986734623' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696916' lon='-122.4032638' />\n  <node id='5986734624' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697008' lon='-122.4032647' />\n  <node id='5986734625' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697007' lon='-122.4032743' />\n  <node id='5986734626' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696977' lon='-122.4032742' />\n  <node id='5986734627' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696978' lon='-122.4032646' />\n  <node id='5986734628' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696828' lon='-122.4032628' />\n  <node id='5986734629' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696826' lon='-122.4032725' />\n  <node id='5986734630' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696797' lon='-122.4032724' />\n  <node id='5986734631' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696798' lon='-122.4032628' />\n  <node id='5986734632' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697166' lon='-122.4032343' />\n  <node id='5986734633' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697134' lon='-122.4033178' />\n  <node id='5986734634' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769705' lon='-122.4033173' />\n  <node id='5986734635' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697079' lon='-122.4032429' />\n  <node id='5986734636' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769666' lon='-122.4032311' />\n  <node id='5986734637' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696583' lon='-122.4032399' />\n  <node id='5986734638' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696669' lon='-122.4032084' />\n  <node id='5986734639' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696595' lon='-122.403208' />\n  <node id='5986734640' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697033' lon='-122.4033521' />\n  <node id='5986734641' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696821' lon='-122.4033845' />\n  <node id='5986734642' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696783' lon='-122.4033842' />\n  <node id='5986734643' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.76968' lon='-122.4033503' />\n  <node id='5986734644' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696836' lon='-122.403356' />\n  <node id='5986734645' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697064' lon='-122.4033578' />\n  <node id='5986734646' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697079' lon='-122.4033275' />\n  <node id='5986734647' timestamp='2018-10-15T19:08:14Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7697045' lon='-122.4033272' />\n  <node id='5986734648' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696527' lon='-122.4032083' />\n  <node id='5986734649' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7696521' lon='-122.4032167' />\n  <node id='5986734650' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695685' lon='-122.4032085' />\n  <node id='5986734651' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695731' lon='-122.4032005' />\n  <node id='5986734652' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695275' lon='-122.4032836' />\n  <node id='5986734653' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695357' lon='-122.4032838' />\n  <node id='5986734654' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.7695748' lon='-122.4032117' />\n  <node id='5986734655' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269' lat='37.769567' lon='-122.4032112' />\n  <node id='5987118485' timestamp='2018-10-15T22:03:26Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691196' lon='-122.4036025' />\n  <node id='5987118486' timestamp='2018-10-15T22:03:26Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691218' lon='-122.4035508' />\n  <node id='5987118487' timestamp='2018-10-15T22:03:26Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691052' lon='-122.4035497' />\n  <node id='5987118488' timestamp='2018-10-15T22:03:26Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691069' lon='-122.40351' />\n  <node id='5987118489' timestamp='2018-10-15T22:03:26Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691353' lon='-122.4035119' />\n  <node id='5987118490' timestamp='2018-10-15T22:03:26Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691366' lon='-122.4034812' />\n  <node id='5987118491' timestamp='2018-10-15T22:03:26Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690934' lon='-122.4034783' />\n  <node id='5987118492' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690927' lon='-122.403494' />\n  <node id='5987118493' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690821' lon='-122.4034932' />\n  <node id='5987118494' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690775' lon='-122.4035996' />\n  <node id='5987118495' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691085' lon='-122.4037177' />\n  <node id='5987118496' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691106' lon='-122.4036682' />\n  <node id='5987118497' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769117' lon='-122.4036686' />\n  <node id='5987118498' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691177' lon='-122.4036537' />\n  <node id='5987118499' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690984' lon='-122.4036524' />\n  <node id='5987118500' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690957' lon='-122.4037168' />\n  <node id='5987118501' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690752' lon='-122.4035985' />\n  <node id='5987118502' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690796' lon='-122.4035038' />\n  <node id='5987118503' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690474' lon='-122.4035013' />\n  <node id='5987118504' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690467' lon='-122.403518' />\n  <node id='5987118505' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690688' lon='-122.4035197' />\n  <node id='5987118506' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690652' lon='-122.4035978' />\n  <node id='5987118507' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689693' lon='-122.4035882' />\n  <node id='5987118508' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689737' lon='-122.4034935' />\n  <node id='5987118509' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690059' lon='-122.4034959' />\n  <node id='5987118510' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690052' lon='-122.4035126' />\n  <node id='5987118511' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768983' lon='-122.4035109' />\n  <node id='5987118512' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689793' lon='-122.403589' />\n  <node id='5987118513' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689264' lon='-122.4035853' />\n  <node id='5987118514' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689286' lon='-122.4035337' />\n  <node id='5987118515' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689452' lon='-122.4035348' />\n  <node id='5987118516' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689469' lon='-122.4034951' />\n  <node id='5987118517' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689185' lon='-122.4034931' />\n  <node id='5987118518' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689198' lon='-122.4034625' />\n  <node id='5987118519' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768963' lon='-122.4034654' />\n  <node id='5987118520' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689623' lon='-122.4034811' />\n  <node id='5987118521' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768973' lon='-122.4034818' />\n  <node id='5987118522' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689685' lon='-122.4035882' />\n  <node id='5987118523' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689279' lon='-122.4036953' />\n  <node id='5987118524' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.76893' lon='-122.4036457' />\n  <node id='5987118525' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689236' lon='-122.4036453' />\n  <node id='5987118526' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689242' lon='-122.4036304' />\n  <node id='5987118527' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689435' lon='-122.4036317' />\n  <node id='5987118528' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689407' lon='-122.4036962' />\n  <node id='5987118529' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689692' lon='-122.403654' />\n  <node id='5987118530' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689705' lon='-122.4036154' />\n  <node id='5987118531' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689905' lon='-122.4036165' />\n  <node id='5987118532' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689902' lon='-122.4036259' />\n  <node id='5987118533' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689783' lon='-122.4036253' />\n  <node id='5987118534' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689773' lon='-122.4036544' />\n  <node id='5987118535' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690793' lon='-122.4036646' />\n  <node id='5987118536' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690812' lon='-122.4036261' />\n  <node id='5987118537' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690612' lon='-122.4036245' />\n  <node id='5987118538' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690607' lon='-122.403634' />\n  <node id='5987118539' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690726' lon='-122.4036349' />\n  <node id='5987118540' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690712' lon='-122.403664' />\n  <node id='5987118541' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693718' lon='-122.4032766' />\n  <node id='5987118542' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694049' lon='-122.4032794' />\n  <node id='5987118543' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694122' lon='-122.4031408' />\n  <node id='5987118544' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693612' lon='-122.4037958' />\n  <node id='5987118545' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693444' lon='-122.4037944' />\n  <node id='5987118546' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693579' lon='-122.4038585' />\n  <node id='5987118547' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694857' lon='-122.4036278' />\n  <node id='5987118548' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694289' lon='-122.4036225' />\n  <node id='5987118549' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694309' lon='-122.4035887' />\n  <node id='5987118550' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694876' lon='-122.4035939' />\n  <node id='5987118551' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693946' lon='-122.4036418' />\n  <node id='5987118552' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693971' lon='-122.4035878' />\n  <node id='5987118553' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693613' lon='-122.4035851' />\n  <node id='5987118554' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693588' lon='-122.4036391' />\n  <node id='5987118555' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693851' lon='-122.403548' />\n  <node id='5987118556' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693909' lon='-122.4034337' />\n  <node id='5987118557' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694359' lon='-122.4034374' />\n  <node id='5987118558' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694301' lon='-122.4035516' />\n  <node id='5987118559' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694129' lon='-122.4037764' />\n  <node id='5987118560' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694181' lon='-122.4036796' />\n  <node id='5987118561' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693572' lon='-122.4036744' />\n  <node id='5987118562' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693536' lon='-122.403741' />\n  <node id='5987118563' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693979' lon='-122.4037448' />\n  <node id='5987118564' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693963' lon='-122.403775' />\n  <node id='5987118565' timestamp='2018-10-15T22:03:27Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694086' lon='-122.4033979' />\n  <node id='5987118566' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694116' lon='-122.4033222' />\n  <node id='5987118567' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693934' lon='-122.403321' />\n  <node id='5987118568' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693903' lon='-122.4033967' />\n  <node id='5987118569' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694463' lon='-122.4032735' />\n  <node id='5987118570' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694494' lon='-122.4031978' />\n  <node id='5987118571' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694311' lon='-122.4031967' />\n  <node id='5987118572' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694281' lon='-122.4032723' />\n  <node id='5987118573' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769356' lon='-122.403506' />\n  <node id='5987118574' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693596' lon='-122.4034339' />\n  <node id='5987118575' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693358' lon='-122.4034319' />\n  <node id='5987118576' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693321' lon='-122.4035041' />\n  <node id='5987118577' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693375' lon='-122.4034072' />\n  <node id='5987118578' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693397' lon='-122.4033669' />\n  <node id='5987118579' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693611' lon='-122.4033687' />\n  <node id='5987118580' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769359' lon='-122.4034091' />\n  <node id='5987118581' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693352' lon='-122.4033164' />\n  <node id='5987118582' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693507' lon='-122.4033176' />\n  <node id='5987118583' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693492' lon='-122.4033483' />\n  <node id='5987118584' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693635' lon='-122.4033494' />\n  <node id='5987118785' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693674' lon='-122.4032687' />\n  <node id='5987118786' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693899' lon='-122.4032704' />\n  <node id='5987118787' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693937' lon='-122.4031939' />\n  <node id='5987118788' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693722' lon='-122.4031922' />\n  <node id='5987118789' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693735' lon='-122.4031663' />\n  <node id='5987118790' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693634' lon='-122.4031655' />\n  <node id='5987118791' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693588' lon='-122.4032589' />\n  <node id='5987118792' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693381' lon='-122.4032573' />\n  <node id='5987118793' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687738' lon='-122.4038121' />\n  <node id='5987118794' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687729' lon='-122.4038264' />\n  <node id='5987118795' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687978' lon='-122.4038288' />\n  <node id='5987118796' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687987' lon='-122.4038145' />\n  <node id='5987118797' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689119' lon='-122.4038398' />\n  <node id='5987118798' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689128' lon='-122.4038255' />\n  <node id='5987118799' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688879' lon='-122.4038231' />\n  <node id='5987118800' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768887' lon='-122.4038374' />\n  <node id='5987118801' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690211' lon='-122.4038499' />\n  <node id='5987118802' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690219' lon='-122.4038355' />\n  <node id='5987118803' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768997' lon='-122.4038331' />\n  <node id='5987118804' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689962' lon='-122.4038474' />\n  <node id='5987118805' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690765' lon='-122.4038552' />\n  <node id='5987118806' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690773' lon='-122.4038409' />\n  <node id='5987118807' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690524' lon='-122.4038385' />\n  <node id='5987118808' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690516' lon='-122.4038528' />\n  <node id='5987118809' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769187' lon='-122.4038659' />\n  <node id='5987118810' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691879' lon='-122.4038516' />\n  <node id='5987118811' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769163' lon='-122.4038492' />\n  <node id='5987118812' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691621' lon='-122.4038635' />\n  <node id='5987118813' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692413' lon='-122.4038703' />\n  <node id='5987118814' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692422' lon='-122.403856' />\n  <node id='5987118815' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692173' lon='-122.4038536' />\n  <node id='5987118816' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692164' lon='-122.4038679' />\n  <node id='5987118817' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692964' lon='-122.403875' />\n  <node id='5987118818' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692973' lon='-122.4038607' />\n  <node id='5987118819' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692724' lon='-122.4038583' />\n  <node id='5987118820' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692716' lon='-122.4038726' />\n  <node id='5987118821' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694064' lon='-122.4038874' />\n  <node id='5987118822' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694073' lon='-122.4038731' />\n  <node id='5987118823' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693824' lon='-122.4038707' />\n  <node id='5987118824' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693815' lon='-122.403885' />\n  <node id='5987118825' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.76946' lon='-122.4038911' />\n  <node id='5987118826' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694608' lon='-122.4038768' />\n  <node id='5987118827' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694359' lon='-122.4038744' />\n  <node id='5987118828' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694351' lon='-122.4038887' />\n  <node id='5987118829' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695143' lon='-122.4038961' />\n  <node id='5987118830' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695152' lon='-122.4038818' />\n  <node id='5987118831' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694903' lon='-122.4038794' />\n  <node id='5987118832' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694894' lon='-122.4038937' />\n  <node id='5987118833' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695705' lon='-122.4039022' />\n  <node id='5987118834' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695714' lon='-122.4038878' />\n  <node id='5987118835' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695465' lon='-122.4038854' />\n  <node id='5987118836' timestamp='2018-10-15T22:03:28Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695456' lon='-122.4038997' />\n  <node id='5987118837' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696232' lon='-122.4039075' />\n  <node id='5987118838' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696241' lon='-122.4038932' />\n  <node id='5987118839' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695992' lon='-122.4038908' />\n  <node id='5987118840' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695983' lon='-122.4039051' />\n  <node id='5987118841' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696287' lon='-122.4031551' />\n  <node id='5987118842' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696295' lon='-122.4031408' />\n  <node id='5987118843' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696046' lon='-122.4031384' />\n  <node id='5987118844' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696038' lon='-122.4031527' />\n  <node id='5987118845' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694288' lon='-122.403135' />\n  <node id='5987118846' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694297' lon='-122.4031207' />\n  <node id='5987118847' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694048' lon='-122.4031183' />\n  <node id='5987118848' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694039' lon='-122.4031326' />\n  <node id='5987118849' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693742' lon='-122.4031303' />\n  <node id='5987118850' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693751' lon='-122.403116' />\n  <node id='5987118851' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693502' lon='-122.4031136' />\n  <node id='5987118852' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693493' lon='-122.4031279' />\n  <node id='5987118853' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693239' lon='-122.4031263' />\n  <node id='5987118854' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693247' lon='-122.403112' />\n  <node id='5987118855' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692998' lon='-122.4031096' />\n  <node id='5987118856' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769299' lon='-122.4031239' />\n  <node id='5987118857' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692115' lon='-122.4031136' />\n  <node id='5987118858' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692124' lon='-122.4030993' />\n  <node id='5987118859' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691875' lon='-122.4030968' />\n  <node id='5987118860' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691866' lon='-122.4031112' />\n  <node id='5987118861' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690975' lon='-122.4031028' />\n  <node id='5987118862' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690984' lon='-122.4030885' />\n  <node id='5987118863' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690735' lon='-122.4030861' />\n  <node id='5987118864' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690726' lon='-122.4031004' />\n  <node id='5987118865' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690504' lon='-122.4030981' />\n  <node id='5987118866' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690512' lon='-122.4030838' />\n  <node id='5987118867' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690263' lon='-122.4030814' />\n  <node id='5987118868' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690255' lon='-122.4030957' />\n  <node id='5987118869' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689025' lon='-122.4030821' />\n  <node id='5987118870' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689033' lon='-122.4030677' />\n  <node id='5987118871' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688784' lon='-122.4030653' />\n  <node id='5987118872' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688776' lon='-122.4030796' />\n  <node id='5987118873' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688479' lon='-122.4030787' />\n  <node id='5987118874' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688487' lon='-122.4030644' />\n  <node id='5987118875' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688238' lon='-122.403062' />\n  <node id='5987118876' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768823' lon='-122.4030763' />\n  <node id='5987118877' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697058' lon='-122.4038819' />\n  <node id='5987118878' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687246' lon='-122.4037901' />\n  <node id='5987118879' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693702' lon='-122.4038505' />\n  <node id='5987118880' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693422' lon='-122.4038479' />\n  <node id='5987118881' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690146' lon='-122.4038172' />\n  <node id='5987118882' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768986' lon='-122.4038145' />\n  <node id='5987118883' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697216' lon='-122.4038932' />\n  <node id='5987118884' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686934' lon='-122.4037986' />\n  <node id='5987119085' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687338' lon='-122.4030796' />\n  <node id='5987119086' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697619' lon='-122.4031722' />\n  <node id='5987119087' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697339' lon='-122.4036687' />\n  <node id='5987119088' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697062' lon='-122.4038918' />\n  <node id='5987119089' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768642' lon='-122.4030951'>\n    <tag k='direction' v='backward' />\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='5987119090' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686589' lon='-122.4028115'>\n    <tag k='direction' v='forward' />\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='5987119091' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769496' lon='-122.4033795' />\n  <node id='5987119092' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694948' lon='-122.4034074' />\n  <node id='5987119093' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694405' lon='-122.4034037' />\n  <node id='5987119094' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694417' lon='-122.4033758' />\n  <node id='5987119095' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693988' lon='-122.403862' />\n  <node id='5987119096' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694368' lon='-122.4031429' />\n  <node id='5987119097' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694295' lon='-122.4032822' />\n  <node id='5987119098' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694224' lon='-122.4034195' />\n  <node id='5987119099' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693645' lon='-122.4034135' />\n  <node id='5987119100' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693566' lon='-122.4035639' />\n  <node id='5987119101' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694144' lon='-122.403568' />\n  <node id='5987119102' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694096' lon='-122.4036587' />\n  <node id='5987119103' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693519' lon='-122.403653' />\n  <node id='5987119104' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694707' lon='-122.4034236' />\n  <node id='5987119105' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694627' lon='-122.4035721' />\n  <node id='5987119106' timestamp='2018-10-15T22:03:29Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690821' lon='-122.4035797'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119107' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690842' lon='-122.4035367'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119108' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690876' lon='-122.4035022'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119109' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691157' lon='-122.4034955'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119111' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689533' lon='-122.4035689'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119112' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689551' lon='-122.403531'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119113' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689549' lon='-122.4034888'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119114' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689281' lon='-122.4034777'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119117' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691732' lon='-122.4038573'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119118' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.76923' lon='-122.403862'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119119' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692835' lon='-122.403867'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119120' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693908' lon='-122.4038771'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119121' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694499' lon='-122.4038834'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119122' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7695011' lon='-122.4038891'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119123' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769561' lon='-122.4038938'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119124' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696121' lon='-122.4038992'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119125' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697346' lon='-122.4035753'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119126' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697386' lon='-122.4035083'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119127' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697404' lon='-122.403457'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119128' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697417' lon='-122.4034016'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119129' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697439' lon='-122.403347'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119130' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.76975' lon='-122.4032642'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119131' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696182' lon='-122.4031478'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119132' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694195' lon='-122.403127'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119133' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693625' lon='-122.4031227'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119134' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693132' lon='-122.4031177'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119135' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692011' lon='-122.4031063'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119136' timestamp='2018-10-15T22:03:30Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690845' lon='-122.4030945'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119137' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690368' lon='-122.4030905'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119138' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688873' lon='-122.4030751'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119139' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688356' lon='-122.4030731'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119140' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687575' lon='-122.4037915' />\n  <node id='5987119141' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687837' lon='-122.4037935' />\n  <node id='5987119142' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687841' lon='-122.4037861' />\n  <node id='5987119143' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687578' lon='-122.4037841' />\n  <node id='5987119144' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688145' lon='-122.4037972' />\n  <node id='5987119145' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688505' lon='-122.4038001' />\n  <node id='5987119146' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688509' lon='-122.4037914' />\n  <node id='5987119147' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688149' lon='-122.4037885' />\n  <node id='5987119148' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688813' lon='-122.4038028' />\n  <node id='5987119149' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689328' lon='-122.4038081' />\n  <node id='5987119150' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689334' lon='-122.4037996' />\n  <node id='5987119151' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688818' lon='-122.4037943' />\n  <node id='5987119152' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689632' lon='-122.4038109' />\n  <node id='5987119153' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689641' lon='-122.4037944' />\n  <node id='5987119154' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689852' lon='-122.4037962' />\n  <node id='5987119155' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689844' lon='-122.4038127' />\n  <node id='5987119156' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690159' lon='-122.4038162' />\n  <node id='5987119157' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690168' lon='-122.4037997' />\n  <node id='5987119158' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690379' lon='-122.4038014' />\n  <node id='5987119159' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769037' lon='-122.4038179' />\n  <node id='5987119160' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690688' lon='-122.4038211' />\n  <node id='5987119161' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691204' lon='-122.4038258' />\n  <node id='5987119162' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691209' lon='-122.4038173' />\n  <node id='5987119163' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690693' lon='-122.4038126' />\n  <node id='5987119164' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691502' lon='-122.4038288' />\n  <node id='5987119165' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691841' lon='-122.4038229' />\n  <node id='5987119166' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691506' lon='-122.4038202' />\n  <node id='5987119167' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691837' lon='-122.4038315' />\n  <node id='5987119168' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692185' lon='-122.4038349' />\n  <node id='5987119169' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769252' lon='-122.4038375' />\n  <node id='5987119170' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692524' lon='-122.403829' />\n  <node id='5987119171' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692189' lon='-122.4038263' />\n  <node id='5987119172' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692764' lon='-122.4038398' />\n  <node id='5987119173' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693414' lon='-122.403845' />\n  <node id='5987119174' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693215' lon='-122.403835' />\n  <node id='5987119175' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692768' lon='-122.4038314' />\n  <node id='5987119176' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693424' lon='-122.4038258' />\n  <node id='5987119177' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769322' lon='-122.4038242' />\n  <node id='5987119178' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693726' lon='-122.403848' />\n  <node id='5987119179' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769393' lon='-122.4038496' />\n  <node id='5987119180' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693939' lon='-122.40383' />\n  <node id='5987119181' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693735' lon='-122.4038285' />\n  <node id='5987119182' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693586' lon='-122.4038462' />\n  <node id='5987119183' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693596' lon='-122.4038258' />\n  <node id='5987119184' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694098' lon='-122.4038217' />\n  <node id='5987119385' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693157' lon='-122.4038136' />\n  <node id='5987119386' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693601' lon='-122.4038174' />\n  <node id='5987119387' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693149' lon='-122.4038257' />\n  <node id='5987119388' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686952' lon='-122.4037649' />\n  <node id='5987119389' timestamp='2018-10-16T00:41:01Z' uid='2237750' user='chachafish' version='2' changeset='63559070' lat='37.7696757' lon='-122.404004'>\n    <tag k='direction' v='forward' />\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='5987119390' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697239' lon='-122.403851' />\n  <node id='5987119391' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696444' lon='-122.4038443' />\n  <node id='5987119392' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696338' lon='-122.4038644' />\n  <node id='5987119393' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694085' lon='-122.4038443' />\n  <node id='5987119394' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769003' lon='-122.4038265' />\n  <node id='5987119395' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690036' lon='-122.4038144' />\n  <node id='5987119396' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690044' lon='-122.4037977' />\n  <node id='5987119397' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='2' changeset='63557411' lat='37.7690045' lon='-122.4037953' />\n  <node id='5987119398' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7688994' lon='-122.4038309'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119399' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687886' lon='-122.4038202'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119400' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686822' lon='-122.4036296' />\n  <node id='5987119401' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686805' lon='-122.4036588' />\n  <node id='5987119402' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686886' lon='-122.4036595' />\n  <node id='5987119403' timestamp='2018-10-15T22:03:31Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686903' lon='-122.4036304' />\n  <node id='5987119404' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686921' lon='-122.4034619' />\n  <node id='5987119405' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686904' lon='-122.403491' />\n  <node id='5987119406' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686985' lon='-122.4034918' />\n  <node id='5987119407' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687002' lon='-122.4034626' />\n  <node id='5987119408' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686984' lon='-122.4033603' />\n  <node id='5987119409' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686967' lon='-122.4033894' />\n  <node id='5987119410' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687048' lon='-122.4033902' />\n  <node id='5987119411' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687065' lon='-122.403361' />\n  <node id='5987119412' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687027' lon='-122.4032929' />\n  <node id='5987119413' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768701' lon='-122.4033221' />\n  <node id='5987119414' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687091' lon='-122.4033228' />\n  <node id='5987119415' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687108' lon='-122.4032936' />\n  <node id='5987119416' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687112' lon='-122.4031534' />\n  <node id='5987119417' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687095' lon='-122.4031826' />\n  <node id='5987119418' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687175' lon='-122.4031833' />\n  <node id='5987119419' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687192' lon='-122.4031541' />\n  <node id='5987119420' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687069' lon='-122.4032226' />\n  <node id='5987119421' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687039' lon='-122.4032684' />\n  <node id='5987119422' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.768712' lon='-122.4032692' />\n  <node id='5987119423' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687149' lon='-122.4032234' />\n  <node id='5987119424' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686838' lon='-122.4036444'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119425' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686957' lon='-122.4034775'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119426' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687015' lon='-122.4033789'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119427' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687052' lon='-122.4033098'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119428' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687076' lon='-122.4032508'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119429' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687145' lon='-122.40317'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119430' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696866' lon='-122.4039358'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119431' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697422' lon='-122.4038566'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119432' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697586' lon='-122.4035938'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119433' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697618' lon='-122.4034215'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119434' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697698' lon='-122.4032639'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119435' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7697041' lon='-122.4031445'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119436' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694544' lon='-122.4031244'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119437' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7692535' lon='-122.4031063'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119438' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7690219' lon='-122.4030822'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119439' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687674' lon='-122.4030614'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119440' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687134' lon='-122.4031459'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119441' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686943' lon='-122.4034456'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119442' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7686736' lon='-122.4037655'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119443' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7687038' lon='-122.4038406'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119444' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7689413' lon='-122.4038452'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119445' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7691321' lon='-122.4038687'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119446' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693383' lon='-122.4038828'>\n    <tag k='highway' v='street_lamp' />\n  </node>\n  <node id='5987119447' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696648' lon='-122.4038744' />\n  <node id='5987119448' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696657' lon='-122.4038553' />\n  <node id='5987119449' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696468' lon='-122.403854' />\n  <node id='5987119450' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769646' lon='-122.4038731' />\n  <node id='5987119451' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7696534' lon='-122.4038654'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119452' timestamp='2018-10-15T22:03:32Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693839' lon='-122.4038429'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119453' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693335' lon='-122.4038355'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119456' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693883' lon='-122.4036974'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119457' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693817' lon='-122.4036149'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119458' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694077' lon='-122.4035227'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119459' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694148' lon='-122.4034566'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119460' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.769347' lon='-122.4034724'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119461' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693478' lon='-122.4033923'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119462' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694' lon='-122.4033675'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119463' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693507' lon='-122.40329'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119464' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7693737' lon='-122.4032323'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119465' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694395' lon='-122.403239'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987119466' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245' lat='37.7694652' lon='-122.4033926'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987137901' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7690212' lon='-122.4034821'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5987137902' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690148' lon='-122.4036033' />\n  <node id='5987137903' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.769039' lon='-122.4035775' />\n  <node id='5987137904' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690412' lon='-122.4035265' />\n  <node id='5987137905' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690202' lon='-122.4035015' />\n  <node id='5987137906' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689972' lon='-122.4035752' />\n  <node id='5987137907' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689995' lon='-122.4035224' />\n  <node id='5987137908' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690142' lon='-122.4036131'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5987137909' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689935' lon='-122.4032593' />\n  <node id='5987137910' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689375' lon='-122.4032547' />\n  <node id='5987137911' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689395' lon='-122.4032165' />\n  <node id='5987137912' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689554' lon='-122.4032178' />\n  <node id='5987137913' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689608' lon='-122.4031134' />\n  <node id='5987137914' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689998' lon='-122.4031166' />\n  <node id='5987137915' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689996' lon='-122.4031221' />\n  <node id='5987137916' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690373' lon='-122.4031251' />\n  <node id='5987137917' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690356' lon='-122.4031569' />\n  <node id='5987137918' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689848' lon='-122.4031528' />\n  <node id='5987137919' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689787' lon='-122.4032255' />\n  <node id='5987137920' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689953' lon='-122.4032352' />\n  <node id='5987137921' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691442' lon='-122.4032753' />\n  <node id='5987137922' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691484' lon='-122.4032004' />\n  <node id='5987137923' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691169' lon='-122.4031975' />\n  <node id='5987137924' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691508' lon='-122.4031592' />\n  <node id='5987137925' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691523' lon='-122.4031317' />\n  <node id='5987137926' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691027' lon='-122.4031277' />\n  <node id='5987137927' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690973' lon='-122.4032417' />\n  <node id='5987137928' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691116' lon='-122.4032428' />\n  <node id='5987137929' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691102' lon='-122.4032718' />\n  <node id='5987137930' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690757' lon='-122.4032344' />\n  <node id='5987137931' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690823' lon='-122.4032348' />\n  <node id='5987137932' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690886' lon='-122.4032326' />\n  <node id='5987137933' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690937' lon='-122.4031906' />\n  <node id='5987137934' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690816' lon='-122.4031841' />\n  <node id='5987137935' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690941' lon='-122.4032279' />\n  <node id='5987137936' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.769098' lon='-122.4032213' />\n  <node id='5987137937' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691001' lon='-122.4032133' />\n  <node id='5987137938' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691' lon='-122.403205' />\n  <node id='5987137939' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690978' lon='-122.4031972' />\n  <node id='5987137940' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.769075' lon='-122.4031849' />\n  <node id='5987137941' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690506' lon='-122.4031977' />\n  <node id='5987137942' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690403' lon='-122.4032011' />\n  <node id='5987137943' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690109' lon='-122.4031881' />\n  <node id='5987137944' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690038' lon='-122.4031755' />\n  <node id='5987137945' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690296' lon='-122.4032005' />\n  <node id='5987137946' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690195' lon='-122.4031961' />\n  <node id='5987137947' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690609' lon='-122.4031893' />\n  <node id='5987137948' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691004' lon='-122.403177' />\n  <node id='5987137949' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.769002' lon='-122.4031679' />\n  <node id='5987137950' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690666' lon='-122.403233' />\n  <node id='5987137951' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690602' lon='-122.4032337' />\n  <node id='5987137952' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690528' lon='-122.4032377' />\n  <node id='5987137953' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691183' lon='-122.403167' />\n  <node id='5987137954' timestamp='2018-10-15T22:14:53Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7691268' lon='-122.4031579' />\n  <node id='5987137955' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689844' lon='-122.4032311' />\n  <node id='5987137956' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689833' lon='-122.4031796' />\n  <node id='5987137957' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689921' lon='-122.4032344' />\n  <node id='5987137958' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689745' lon='-122.4032179' />\n  <node id='5987137959' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689723' lon='-122.4032092' />\n  <node id='5987137960' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689788' lon='-122.4031837' />\n  <node id='5987137961' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689724' lon='-122.4032' />\n  <node id='5987137962' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689746' lon='-122.4031912' />\n  <node id='5987137963' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690443' lon='-122.4031076' />\n  <node id='5987137964' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690424' lon='-122.4031466' />\n  <node id='5987137965' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690936' lon='-122.4031507' />\n  <node id='5987137966' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7690925' lon='-122.4031681' />\n  <node id='5987137967' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689918' lon='-122.4031601' />\n  <node id='5987137968' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.7689907' lon='-122.4031798' />\n  <node id='5987137969' timestamp='2018-10-15T23:38:02Z' uid='2237750' user='chachafish' version='2' changeset='63558461' lat='37.7690411' lon='-122.4032399'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='5987137970' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411' lat='37.769043' lon='-122.4031335'>\n    <tag k='barrier' v='gate' />\n  </node>\n  <node id='5987138387' timestamp='2018-10-15T22:15:30Z' uid='2237750' user='chachafish' version='1' changeset='63557427' lat='37.7691105' lon='-122.4031556'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987138388' timestamp='2018-10-15T22:15:30Z' uid='2237750' user='chachafish' version='1' changeset='63557427' lat='37.7691084' lon='-122.4031771'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987138389' timestamp='2018-10-15T22:15:30Z' uid='2237750' user='chachafish' version='1' changeset='63557427' lat='37.7691243' lon='-122.40322'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987138390' timestamp='2018-10-15T22:15:30Z' uid='2237750' user='chachafish' version='1' changeset='63557427' lat='37.7690315' lon='-122.4031811'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987138391' timestamp='2018-10-15T22:15:30Z' uid='2237750' user='chachafish' version='1' changeset='63557427' lat='37.7689658' lon='-122.4032327'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987138392' timestamp='2018-10-15T22:15:30Z' uid='2237750' user='chachafish' version='1' changeset='63557427' lat='37.7689684' lon='-122.4031757'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987138393' timestamp='2018-10-15T22:15:30Z' uid='2237750' user='chachafish' version='1' changeset='63557427' lat='37.7689748' lon='-122.4031355'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987138394' timestamp='2018-10-15T22:15:30Z' uid='2237750' user='chachafish' version='1' changeset='63557427' lat='37.7690103' lon='-122.4031449'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='5987263171' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692822' lon='-122.4036173' />\n  <node id='5987263172' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692818' lon='-122.4036253' />\n  <node id='5987263173' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691706' lon='-122.4036159' />\n  <node id='5987263174' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691711' lon='-122.4036078' />\n  <node id='5987263175' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693105' lon='-122.4035261' />\n  <node id='5987263176' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76931' lon='-122.4035341' />\n  <node id='5987263177' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691989' lon='-122.4035247' />\n  <node id='5987263178' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691993' lon='-122.4035167' />\n  <node id='5987263179' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691857' lon='-122.403312' />\n  <node id='5987263180' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691794' lon='-122.4033115' />\n  <node id='5987263181' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691853' lon='-122.4032001' />\n  <node id='5987263182' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691916' lon='-122.4032006' />\n  <node id='5987263183' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692382' lon='-122.4034482' />\n  <node id='5987263184' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692318' lon='-122.4034477' />\n  <node id='5987264285' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692417' lon='-122.4033791' />\n  <node id='5987264286' timestamp='2018-10-15T23:37:28Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692353' lon='-122.4033786' />\n  <node id='5987264287' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692149' lon='-122.4035182' />\n  <node id='5987264288' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692083' lon='-122.4035176' />\n  <node id='5987264289' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692125' lon='-122.4034384' />\n  <node id='5987264290' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692191' lon='-122.403439' />\n  <node id='5987264291' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692021' lon='-122.4035965' />\n  <node id='5987264292' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691955' lon='-122.4035959' />\n  <node id='5987264293' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691997' lon='-122.4035167' />\n  <node id='5987264294' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692063' lon='-122.4035173' />\n  <node id='5987264295' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691977' lon='-122.4037197' />\n  <node id='5987264296' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691904' lon='-122.4037191' />\n  <node id='5987264297' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691954' lon='-122.4036165' />\n  <node id='5987264298' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692027' lon='-122.4036171' />\n  <node id='5987264299' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692704' lon='-122.4035232' />\n  <node id='5987264300' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692646' lon='-122.4035227' />\n  <node id='5987264301' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692837' lon='-122.4031472' />\n  <node id='5987264302' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692895' lon='-122.4031477' />\n  <node id='5987264303' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692188' lon='-122.4033583' />\n  <node id='5987264304' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691645' lon='-122.4033627' />\n  <node id='5987264305' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691649' lon='-122.4033547' />\n  <node id='5987264306' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692185' lon='-122.4033664' />\n  <node id='5987264307' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691622' lon='-122.4033298' />\n  <node id='5987264308' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691618' lon='-122.4033384' />\n  <node id='5987264309' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769048' lon='-122.4033298' />\n  <node id='5987264310' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690484' lon='-122.4033212' />\n  <node id='5987264311' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690482' lon='-122.4033072' />\n  <node id='5987264312' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690478' lon='-122.4033157' />\n  <node id='5987264313' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689341' lon='-122.4033071' />\n  <node id='5987264314' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689345' lon='-122.4032986' />\n  <node id='5987264315' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692382' lon='-122.4034399' />\n  <node id='5987264316' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692378' lon='-122.4034479' />\n  <node id='5987264317' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692134' lon='-122.4034461' />\n  <node id='5987264318' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692138' lon='-122.403438' />\n  <node id='5987264319' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692061' lon='-122.4034422' />\n  <node id='5987264320' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692059' lon='-122.4034502' />\n  <node id='5987264321' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691869' lon='-122.4034494' />\n  <node id='5987264322' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691871' lon='-122.4034414' />\n  <node id='5987264323' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692039' lon='-122.4034821' />\n  <node id='5987264324' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692036' lon='-122.4034901' />\n  <node id='5987264325' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691847' lon='-122.4034893' />\n  <node id='5987264326' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691849' lon='-122.4034812' />\n  <node id='5987264327' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769198' lon='-122.403553' />\n  <node id='5987264328' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691976' lon='-122.403561' />\n  <node id='5987264329' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691732' lon='-122.4035592' />\n  <node id='5987264330' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691736' lon='-122.4035511' />\n  <node id='5987264331' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691975' lon='-122.4035855' />\n  <node id='5987264332' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691971' lon='-122.4035935' />\n  <node id='5987264333' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691727' lon='-122.4035917' />\n  <node id='5987264334' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691731' lon='-122.4035837' />\n  <node id='5987264335' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691725' lon='-122.4033615' />\n  <node id='5987264336' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691661' lon='-122.4033611' />\n  <node id='5987264337' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691675' lon='-122.4033302' />\n  <node id='5987264338' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691738' lon='-122.4033306' />\n  <node id='5987264339' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691956' lon='-122.4033645' />\n  <node id='5987264340' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691892' lon='-122.4033641' />\n  <node id='5987264341' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691905' lon='-122.4033332' />\n  <node id='5987264342' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691969' lon='-122.4033337' />\n  <node id='5987264343' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692257' lon='-122.4033349' />\n  <node id='5987264344' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692256' lon='-122.4033443' />\n  <node id='5987264345' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691918' lon='-122.4033435' />\n  <node id='5987264346' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691919' lon='-122.4033341' />\n  <node id='5987264347' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692269' lon='-122.4033054' />\n  <node id='5987264348' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692267' lon='-122.4033138' />\n  <node id='5987264349' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691804' lon='-122.4033115' />\n  <node id='5987264350' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691806' lon='-122.4033031' />\n  <node id='5987264351' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692262' lon='-122.4033442' />\n  <node id='5987264352' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692198' lon='-122.403344' />\n  <node id='5987264353' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692204' lon='-122.4033078' />\n  <node id='5987264354' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692269' lon='-122.403308' />\n  <node id='5987264355' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692431' lon='-122.4032058' />\n  <node id='5987264356' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692427' lon='-122.4032139' />\n  <node id='5987264357' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691888' lon='-122.4032102' />\n  <node id='5987264358' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691891' lon='-122.4032022' />\n  <node id='5987264359' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693011' lon='-122.4034835' />\n  <node id='5987264360' timestamp='2018-10-15T23:37:29Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693008' lon='-122.4034915' />\n  <node id='5987264361' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692605' lon='-122.403489' />\n  <node id='5987264362' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692608' lon='-122.4034809' />\n  <node id='5987264363' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693059' lon='-122.4034313' />\n  <node id='5987264364' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693056' lon='-122.4034393' />\n  <node id='5987264365' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692653' lon='-122.4034368' />\n  <node id='5987264366' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692657' lon='-122.4034287' />\n  <node id='5987264367' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693012' lon='-122.4033451' />\n  <node id='5987264368' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693008' lon='-122.4033531' />\n  <node id='5987264369' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692606' lon='-122.4033506' />\n  <node id='5987264370' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692609' lon='-122.4033426' />\n  <node id='5987264371' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693139' lon='-122.4032975' />\n  <node id='5987264372' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693136' lon='-122.4033055' />\n  <node id='5987264373' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692733' lon='-122.403303' />\n  <node id='5987264374' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692736' lon='-122.4032949' />\n  <node id='5987264375' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693054' lon='-122.4032016' />\n  <node id='5987264376' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693051' lon='-122.4032096' />\n  <node id='5987264377' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692648' lon='-122.4032071' />\n  <node id='5987264378' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692651' lon='-122.4031991' />\n  <node id='5987264379' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692458' lon='-122.4031946' />\n  <node id='5987264380' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692454' lon='-122.4032026' />\n  <node id='5987264381' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692052' lon='-122.4032001' />\n  <node id='5987264382' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692055' lon='-122.403192' />\n  <node id='5987264383' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693019' lon='-122.4036774' />\n  <node id='5987264384' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693016' lon='-122.4036854' />\n  <node id='5987264385' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692614' lon='-122.4036829' />\n  <node id='5987264386' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692617' lon='-122.4036748' />\n  <node id='5987264387' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692196' lon='-122.4037822' />\n  <node id='5987264388' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692155' lon='-122.4037818' />\n  <node id='5987264389' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692244' lon='-122.4036179' />\n  <node id='5987264390' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692285' lon='-122.4036182' />\n  <node id='5987264391' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692492' lon='-122.4037272' />\n  <node id='5987264392' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769249' lon='-122.4037336' />\n  <node id='5987264393' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691998' lon='-122.4037307' />\n  <node id='5987264394' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692' lon='-122.4037243' />\n  <node id='5987264395' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691657' lon='-122.4037008' />\n  <node id='5987264396' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691654' lon='-122.4037089' />\n  <node id='5987264397' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691466' lon='-122.4037078' />\n  <node id='5987264398' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691469' lon='-122.4036997' />\n  <node id='5987264399' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691795' lon='-122.4036103' />\n  <node id='5987264400' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691731' lon='-122.4036099' />\n  <node id='5987264401' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769174' lon='-122.403586' />\n  <node id='5987264402' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691803' lon='-122.4035863' />\n  <node id='5987264403' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693147' lon='-122.4036072' />\n  <node id='5987264404' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693146' lon='-122.4036094' />\n  <node id='5987264405' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692065' lon='-122.4036004' />\n  <node id='5987264406' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692066' lon='-122.4035982' />\n  <node id='5987264407' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769346' lon='-122.4035769' />\n  <node id='5987264408' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693458' lon='-122.4035791' />\n  <node id='5987264409' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692763' lon='-122.4035711' />\n  <node id='5987264410' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692761' lon='-122.4035733' />\n  <node id='5987264411' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693299' lon='-122.4035403' />\n  <node id='5987264412' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7693298' lon='-122.4035424' />\n  <node id='5987264413' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692217' lon='-122.4035334' />\n  <node id='5987264414' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692219' lon='-122.4035312' />\n  <node id='5987264415' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692783' lon='-122.4035724' />\n  <node id='5987264416' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692766' lon='-122.4035723' />\n  <node id='5987264417' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692801' lon='-122.4035371' />\n  <node id='5987264418' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692784' lon='-122.403537' />\n  <node id='5987264419' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769222' lon='-122.4035667' />\n  <node id='5987264420' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692203' lon='-122.4035666' />\n  <node id='5987264421' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692214' lon='-122.4035313' />\n  <node id='5987264422' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692231' lon='-122.4035313' />\n  <node id='5987264423' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692214' lon='-122.4035637' />\n  <node id='5987264424' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692079' lon='-122.4035651' />\n  <node id='5987264425' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769208' lon='-122.4035629' />\n  <node id='5987264426' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692085' lon='-122.4035994' />\n  <node id='5987264427' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692068' lon='-122.4035993' />\n  <node id='5987264428' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692078' lon='-122.4035639' />\n  <node id='5987264429' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692096' lon='-122.403564' />\n  <node id='5987264430' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692213' lon='-122.4035659' />\n  <node id='5987264431' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769259' lon='-122.4035639' />\n  <node id='5987264432' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769259' lon='-122.403572' />\n  <node id='5987264433' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692579' lon='-122.4035725' />\n  <node id='5987264434' timestamp='2018-10-15T23:37:30Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76926' lon='-122.4035649' />\n  <node id='5987264435' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692606' lon='-122.4035663' />\n  <node id='5987264436' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692608' lon='-122.4035679' />\n  <node id='5987264437' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692606' lon='-122.4035695' />\n  <node id='5987264438' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76926' lon='-122.4035709' />\n  <node id='5987264439' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692567' lon='-122.4035726' />\n  <node id='5987264440' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692556' lon='-122.4035722' />\n  <node id='5987264441' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692546' lon='-122.4035713' />\n  <node id='5987264442' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692539' lon='-122.4035701' />\n  <node id='5987264443' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692535' lon='-122.4035687' />\n  <node id='5987264444' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692535' lon='-122.4035672' />\n  <node id='5987264445' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692538' lon='-122.4035658' />\n  <node id='5987264446' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692546' lon='-122.4035646' />\n  <node id='5987264447' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692555' lon='-122.4035637' />\n  <node id='5987264448' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692567' lon='-122.4035633' />\n  <node id='5987264449' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692579' lon='-122.4035633' />\n  <node id='5987264450' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769256' lon='-122.4035873' />\n  <node id='5987264451' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692548' lon='-122.4035869' />\n  <node id='5987264452' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692538' lon='-122.4035861' />\n  <node id='5987264453' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692531' lon='-122.4035849' />\n  <node id='5987264454' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692527' lon='-122.4035834' />\n  <node id='5987264455' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692527' lon='-122.4035819' />\n  <node id='5987264456' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692531' lon='-122.4035805' />\n  <node id='5987264457' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692538' lon='-122.4035793' />\n  <node id='5987264458' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692548' lon='-122.4035784' />\n  <node id='5987264459' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692559' lon='-122.403578' />\n  <node id='5987264460' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692571' lon='-122.4035781' />\n  <node id='5987264461' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692582' lon='-122.4035786' />\n  <node id='5987264462' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692592' lon='-122.4035796' />\n  <node id='5987264463' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692599' lon='-122.403581' />\n  <node id='5987264464' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692601' lon='-122.4035826' />\n  <node id='5987264465' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692599' lon='-122.4035842' />\n  <node id='5987264466' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692592' lon='-122.4035856' />\n  <node id='5987264467' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692583' lon='-122.4035867' />\n  <node id='5987264468' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692571' lon='-122.4035872' />\n  <node id='5987264469' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769249' lon='-122.4033498' />\n  <node id='5987264470' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692376' lon='-122.4033492' />\n  <node id='5987264471' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692394' lon='-122.4032909' />\n  <node id='5987264472' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692508' lon='-122.4032914' />\n  <node id='5987264473' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692307' lon='-122.4034213' />\n  <node id='5987264474' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692193' lon='-122.4034208' />\n  <node id='5987264475' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692208' lon='-122.4033737' />\n  <node id='5987264476' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692322' lon='-122.4033738' />\n  <node id='5987264477' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692505' lon='-122.4032926' />\n  <node id='5987264478' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692428' lon='-122.4032923' />\n  <node id='5987264479' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692517' lon='-122.4032366' />\n  <node id='5987264480' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692436' lon='-122.4032594' />\n  <node id='5987264481' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692307' lon='-122.4032542' />\n  <node id='5987264482' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692227' lon='-122.4032356' />\n  <node id='5987264483' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692306' lon='-122.403259' />\n  <node id='5987264484' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692222' lon='-122.4032558' />\n  <node id='5987264485' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692448' lon='-122.4032462' />\n  <node id='5987264486' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692446' lon='-122.4032547' />\n  <node id='5987264487' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692287' lon='-122.4032457' />\n  <node id='5987264488' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7692284' lon='-122.403256' />\n  <node id='5987264489' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691586' lon='-122.4034649' />\n  <node id='5987264490' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691567' lon='-122.4034648' />\n  <node id='5987264491' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691661' lon='-122.4032817' />\n  <node id='5987264492' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691679' lon='-122.4032819' />\n  <node id='5987264493' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691623' lon='-122.4033759' />\n  <node id='5987264494' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691622' lon='-122.4033783' />\n  <node id='5987264495' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689272' lon='-122.4033579' />\n  <node id='5987264496' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689274' lon='-122.4033556' />\n  <node id='5987264497' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691239' lon='-122.4034622' />\n  <node id='5987264498' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691225' lon='-122.4034621' />\n  <node id='5987264499' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691235' lon='-122.4034373' />\n  <node id='5987264500' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691249' lon='-122.4034389' />\n  <node id='5987264501' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691586' lon='-122.4034397' />\n  <node id='5987264502' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691586' lon='-122.4034411' />\n  <node id='5987264503' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689227' lon='-122.4034424' />\n  <node id='5987264504' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689213' lon='-122.4034423' />\n  <node id='5987264505' timestamp='2018-10-15T23:37:31Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689223' lon='-122.4034176' />\n  <node id='5987264506' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689574' lon='-122.4034199' />\n  <node id='5987264507' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689574' lon='-122.4034214' />\n  <node id='5987264508' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689237' lon='-122.4034191' />\n  <node id='5987264509' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689571' lon='-122.4034212' />\n  <node id='5987264510' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689556' lon='-122.4034211' />\n  <node id='5987264511' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689547' lon='-122.4034457' />\n  <node id='5987264512' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689562' lon='-122.4034458' />\n  <node id='5987264513' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689315' lon='-122.4032891' />\n  <node id='5987264514' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689329' lon='-122.4032892' />\n  <node id='5987264515' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689341' lon='-122.4032573' />\n  <node id='5987264516' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689326' lon='-122.4032573' />\n  <node id='5987264517' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689661' lon='-122.4032606' />\n  <node id='5987264518' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689675' lon='-122.4032607' />\n  <node id='5987264519' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689664' lon='-122.4032922' />\n  <node id='5987264520' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689313' lon='-122.4032899' />\n  <node id='5987264521' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689313' lon='-122.4032884' />\n  <node id='5987264522' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768965' lon='-122.4032907' />\n  <node id='5987264523' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689272' lon='-122.4034172' />\n  <node id='5987264524' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689312' lon='-122.4032902' />\n  <node id='5987264525' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689346' lon='-122.4032905' />\n  <node id='5987264526' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689239' lon='-122.4034168' />\n  <node id='5987264527' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691338' lon='-122.4032776' />\n  <node id='5987264528' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691324' lon='-122.4032775' />\n  <node id='5987264529' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769131' lon='-122.4033095' />\n  <node id='5987264530' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769166' lon='-122.403312' />\n  <node id='5987264531' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769166' lon='-122.4033105' />\n  <node id='5987264532' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691325' lon='-122.4033081' />\n  <node id='5987264533' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689088' lon='-122.4033008' />\n  <node id='5987264534' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689161' lon='-122.4033013' />\n  <node id='5987264535' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688381' lon='-122.4033254' />\n  <node id='5987264536' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688385' lon='-122.4033168' />\n  <node id='5987264537' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689079' lon='-122.4033218' />\n  <node id='5987264538' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689148' lon='-122.4033309' />\n  <node id='5987264539' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688477' lon='-122.4034881' />\n  <node id='5987264540' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688408' lon='-122.4034875' />\n  <node id='5987264541' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688502' lon='-122.4033239' />\n  <node id='5987264542' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688571' lon='-122.4033245' />\n  <node id='5987264543' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688732' lon='-122.4035601' />\n  <node id='5987264544' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688663' lon='-122.4035594' />\n  <node id='5987264545' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688775' lon='-122.4034875' />\n  <node id='5987264546' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688706' lon='-122.4034869' />\n  <node id='5987264547' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768768' lon='-122.4034831' />\n  <node id='5987264548' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687686' lon='-122.4034744' />\n  <node id='5987264549' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768876' lon='-122.4034863' />\n  <node id='5987264550' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688754' lon='-122.403495' />\n  <node id='5987264551' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688853' lon='-122.4035893' />\n  <node id='5987264552' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687707' lon='-122.403579' />\n  <node id='5987264553' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687713' lon='-122.4035697' />\n  <node id='5987264554' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688858' lon='-122.4035799' />\n  <node id='5987264555' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688847' lon='-122.4035889' />\n  <node id='5987264556' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688778' lon='-122.4035883' />\n  <node id='5987264557' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688868' lon='-122.4035527' />\n  <node id='5987264558' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76888' lon='-122.403552' />\n  <node id='5987264559' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688665' lon='-122.4035594' />\n  <node id='5987264560' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688671' lon='-122.4035507' />\n  <node id='5987264561' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688848' lon='-122.4035613' />\n  <node id='5987264562' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688854' lon='-122.4035526' />\n  <node id='5987264563' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688339' lon='-122.403748' />\n  <node id='5987264564' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688304' lon='-122.4037477' />\n  <node id='5987264565' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688392' lon='-122.403584' />\n  <node id='5987264566' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688426' lon='-122.4035843' />\n  <node id='5987264567' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688646' lon='-122.403561' />\n  <node id='5987264568' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687359' lon='-122.4035486' />\n  <node id='5987264569' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688553' lon='-122.4035329' />\n  <node id='5987264570' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688662' lon='-122.4035339' />\n  <node id='5987264571' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688574' lon='-122.4034981' />\n  <node id='5987264572' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687396' lon='-122.4034867' />\n  <node id='5987264573' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687957' lon='-122.4035235' />\n  <node id='5987264574' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687394' lon='-122.4035185' />\n  <node id='5987264575' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768741' lon='-122.403489' />\n  <node id='5987264576' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687973' lon='-122.4034939' />\n  <node id='5987264577' timestamp='2018-10-15T23:37:32Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76886' lon='-122.4033103' />\n  <node id='5987264578' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688493' lon='-122.4033098' />\n  <node id='5987264579' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688508' lon='-122.4032518' />\n  <node id='5987264580' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688628' lon='-122.4032066' />\n  <node id='5987264581' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688539' lon='-122.4032519' />\n  <node id='5987264582' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688553' lon='-122.4031979' />\n  <node id='5987264583' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688857' lon='-122.4031991' />\n  <node id='5987264584' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688795' lon='-122.4032073' />\n  <node id='5987264585' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688851' lon='-122.4032219' />\n  <node id='5987264586' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688791' lon='-122.4032217' />\n  <node id='5987264587' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688688' lon='-122.4033861' />\n  <node id='5987264588' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688574' lon='-122.4033856' />\n  <node id='5987264589' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688916' lon='-122.4033433' />\n  <node id='5987264590' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689076' lon='-122.4033582' />\n  <node id='5987264591' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688695' lon='-122.4033567' />\n  <node id='5987264592' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688585' lon='-122.403342' />\n  <node id='5987264593' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688915' lon='-122.4033505' />\n  <node id='5987264594' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689005' lon='-122.4033509' />\n  <node id='5987264595' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689079' lon='-122.4033431' />\n  <node id='5987264596' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689007' lon='-122.4033428' />\n  <node id='5987264597' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688965' lon='-122.4036846' />\n  <node id='5987264598' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688965' lon='-122.4036886' />\n  <node id='5987264599' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688874' lon='-122.4036886' />\n  <node id='5987264600' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688874' lon='-122.4036845' />\n  <node id='5987264601' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689073' lon='-122.4036847' />\n  <node id='5987264602' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689072' lon='-122.4036887' />\n  <node id='5987264603' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688981' lon='-122.4036886' />\n  <node id='5987264604' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688981' lon='-122.4036846' />\n  <node id='5987264605' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689077' lon='-122.4036709' />\n  <node id='5987264606' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689077' lon='-122.4036749' />\n  <node id='5987264607' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688986' lon='-122.4036748' />\n  <node id='5987264608' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688986' lon='-122.4036708' />\n  <node id='5987264609' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688969' lon='-122.4036705' />\n  <node id='5987264610' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688968' lon='-122.4036745' />\n  <node id='5987264611' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688877' lon='-122.4036745' />\n  <node id='5987264612' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688877' lon='-122.4036705' />\n  <node id='5987264613' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689113' lon='-122.4036869' />\n  <node id='5987264614' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689081' lon='-122.4036869' />\n  <node id='5987264615' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689082' lon='-122.4036754' />\n  <node id='5987264616' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689114' lon='-122.4036754' />\n  <node id='5987264617' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688866' lon='-122.4036863' />\n  <node id='5987264618' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688834' lon='-122.4036863' />\n  <node id='5987264619' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688836' lon='-122.4036708' />\n  <node id='5987264620' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688867' lon='-122.4036708' />\n  <node id='5987264621' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688721' lon='-122.4034637' />\n  <node id='5987264622' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768869' lon='-122.4034637' />\n  <node id='5987264623' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688691' lon='-122.4034482' />\n  <node id='5987264624' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688722' lon='-122.4034482' />\n  <node id='5987264625' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688824' lon='-122.4034479' />\n  <node id='5987264626' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688823' lon='-122.403452' />\n  <node id='5987264627' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688732' lon='-122.4034519' />\n  <node id='5987264628' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688732' lon='-122.4034479' />\n  <node id='5987264629' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688932' lon='-122.4034483' />\n  <node id='5987264630' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688932' lon='-122.4034523' />\n  <node id='5987264631' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688841' lon='-122.4034522' />\n  <node id='5987264632' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688841' lon='-122.4034482' />\n  <node id='5987264633' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688968' lon='-122.4034643' />\n  <node id='5987264634' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688937' lon='-122.4034643' />\n  <node id='5987264635' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688937' lon='-122.4034528' />\n  <node id='5987264636' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688969' lon='-122.4034528' />\n  <node id='5987264637' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688928' lon='-122.4034621' />\n  <node id='5987264638' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688928' lon='-122.4034661' />\n  <node id='5987264639' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688836' lon='-122.4034661' />\n  <node id='5987264640' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688837' lon='-122.403462' />\n  <node id='5987264641' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768882' lon='-122.403462' />\n  <node id='5987264642' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768882' lon='-122.403466' />\n  <node id='5987264643' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688729' lon='-122.403466' />\n  <node id='5987264644' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688729' lon='-122.4034619' />\n  <node id='5987264645' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688763' lon='-122.4034232' />\n  <node id='5987264646' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688732' lon='-122.4034231' />\n  <node id='5987264647' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688733' lon='-122.4034076' />\n  <node id='5987264648' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688765' lon='-122.4034077' />\n  <node id='5987264649' timestamp='2018-10-15T23:37:33Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688866' lon='-122.4034074' />\n  <node id='5987264650' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688866' lon='-122.4034114' />\n  <node id='5987264651' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688775' lon='-122.4034113' />\n  <node id='5987264652' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688775' lon='-122.4034073' />\n  <node id='5987264653' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688975' lon='-122.4034077' />\n  <node id='5987264654' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688975' lon='-122.4034117' />\n  <node id='5987264655' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688883' lon='-122.4034117' />\n  <node id='5987264656' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688884' lon='-122.4034076' />\n  <node id='5987264657' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689011' lon='-122.4034238' />\n  <node id='5987264658' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688979' lon='-122.4034237' />\n  <node id='5987264659' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768898' lon='-122.4034122' />\n  <node id='5987264660' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689012' lon='-122.4034122' />\n  <node id='5987264661' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768897' lon='-122.4034215' />\n  <node id='5987264662' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768897' lon='-122.4034255' />\n  <node id='5987264663' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688879' lon='-122.4034255' />\n  <node id='5987264664' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688879' lon='-122.4034215' />\n  <node id='5987264665' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688863' lon='-122.4034214' />\n  <node id='5987264666' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688863' lon='-122.4034255' />\n  <node id='5987264667' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688771' lon='-122.4034254' />\n  <node id='5987264668' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688772' lon='-122.4034214' />\n  <node id='5987264669' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688544' lon='-122.4036556' />\n  <node id='5987264670' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768852' lon='-122.4036556' />\n  <node id='5987264671' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768852' lon='-122.4036466' />\n  <node id='5987264672' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688544' lon='-122.4036466' />\n  <node id='5987264673' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688544' lon='-122.4036387' />\n  <node id='5987264674' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688521' lon='-122.4036387' />\n  <node id='5987264675' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688521' lon='-122.4036297' />\n  <node id='5987264676' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688544' lon='-122.4036297' />\n  <node id='5987264677' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768848' lon='-122.4036548' />\n  <node id='5987264678' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688457' lon='-122.4036548' />\n  <node id='5987264679' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688457' lon='-122.4036458' />\n  <node id='5987264680' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768848' lon='-122.4036457' />\n  <node id='5987264681' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688485' lon='-122.4036384' />\n  <node id='5987264682' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688461' lon='-122.4036384' />\n  <node id='5987264683' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688461' lon='-122.4036293' />\n  <node id='5987264684' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688484' lon='-122.4036293' />\n  <node id='5987264685' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688207' lon='-122.4036221' />\n  <node id='5987264686' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688184' lon='-122.4036221' />\n  <node id='5987264687' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688184' lon='-122.4036131' />\n  <node id='5987264688' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688207' lon='-122.4036131' />\n  <node id='5987264689' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688267' lon='-122.4036225' />\n  <node id='5987264690' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688244' lon='-122.4036225' />\n  <node id='5987264691' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688243' lon='-122.4036134' />\n  <node id='5987264692' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688267' lon='-122.4036134' />\n  <node id='5987264693' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688266' lon='-122.4036394' />\n  <node id='5987264694' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688243' lon='-122.4036394' />\n  <node id='5987264695' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688243' lon='-122.4036303' />\n  <node id='5987264696' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688266' lon='-122.4036303' />\n  <node id='5987264697' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688203' lon='-122.4036386' />\n  <node id='5987264698' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688179' lon='-122.4036386' />\n  <node id='5987264699' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688179' lon='-122.4036295' />\n  <node id='5987264700' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688202' lon='-122.4036295' />\n  <node id='5987264701' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689077' lon='-122.4032979' />\n  <node id='5987264702' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689054' lon='-122.4032979' />\n  <node id='5987264703' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689054' lon='-122.4032889' />\n  <node id='5987264704' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689077' lon='-122.4032889' />\n  <node id='5987264705' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688955' lon='-122.4032974' />\n  <node id='5987264706' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688932' lon='-122.4032974' />\n  <node id='5987264707' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688932' lon='-122.4032884' />\n  <node id='5987264708' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688955' lon='-122.4032883' />\n  <node id='5987264709' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689252' lon='-122.4032997' />\n  <node id='5987264710' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689228' lon='-122.4032997' />\n  <node id='5987264711' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689228' lon='-122.4032907' />\n  <node id='5987264712' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689251' lon='-122.4032907' />\n  <node id='5987264713' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689135' lon='-122.4032981' />\n  <node id='5987264714' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689112' lon='-122.4032981' />\n  <node id='5987264715' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689111' lon='-122.403289' />\n  <node id='5987264716' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689135' lon='-122.403289' />\n  <node id='5987264717' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688891' lon='-122.4032971' />\n  <node id='5987264718' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688868' lon='-122.4032971' />\n  <node id='5987264719' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688868' lon='-122.4032881' />\n  <node id='5987264720' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688891' lon='-122.4032881' />\n  <node id='5987264721' timestamp='2018-10-15T23:37:34Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688821' lon='-122.403297' />\n  <node id='5987264722' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688798' lon='-122.403297' />\n  <node id='5987264723' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688798' lon='-122.4032879' />\n  <node id='5987264724' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688821' lon='-122.4032879' />\n  <node id='5987264725' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688627' lon='-122.4031655' />\n  <node id='5987264726' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688604' lon='-122.4031654' />\n  <node id='5987264727' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688606' lon='-122.4031563' />\n  <node id='5987264728' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768863' lon='-122.4031564' />\n  <node id='5987264729' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688697' lon='-122.4031659' />\n  <node id='5987264730' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688674' lon='-122.4031658' />\n  <node id='5987264731' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688676' lon='-122.4031568' />\n  <node id='5987264732' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76887' lon='-122.4031569' />\n  <node id='5987264733' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688761' lon='-122.4031665' />\n  <node id='5987264734' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688738' lon='-122.4031664' />\n  <node id='5987264735' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768874' lon='-122.4031573' />\n  <node id='5987264736' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688763' lon='-122.4031574' />\n  <node id='5987264737' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688883' lon='-122.4031675' />\n  <node id='5987264738' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768886' lon='-122.4031674' />\n  <node id='5987264739' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688862' lon='-122.4031583' />\n  <node id='5987264740' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688885' lon='-122.4031584' />\n  <node id='5987264741' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688941' lon='-122.4031679' />\n  <node id='5987264742' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688917' lon='-122.4031678' />\n  <node id='5987264743' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768892' lon='-122.4031588' />\n  <node id='5987264744' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688943' lon='-122.4031588' />\n  <node id='5987264745' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689057' lon='-122.40317' />\n  <node id='5987264746' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689033' lon='-122.4031699' />\n  <node id='5987264747' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689036' lon='-122.4031609' />\n  <node id='5987264748' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689059' lon='-122.403161' />\n  <node id='5987264749' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688445' lon='-122.4031617' />\n  <node id='5987264750' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688468' lon='-122.4031619' />\n  <node id='5987264751' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688473' lon='-122.4031528' />\n  <node id='5987264752' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768845' lon='-122.4031526' />\n  <node id='5987264753' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688374' lon='-122.4031612' />\n  <node id='5987264754' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688398' lon='-122.4031614' />\n  <node id='5987264755' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688403' lon='-122.4031524' />\n  <node id='5987264756' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688379' lon='-122.4031522' />\n  <node id='5987264757' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768831' lon='-122.403161' />\n  <node id='5987264758' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688334' lon='-122.4031612' />\n  <node id='5987264759' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688339' lon='-122.4031521' />\n  <node id='5987264760' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688315' lon='-122.4031519' />\n  <node id='5987264761' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688189' lon='-122.4031604' />\n  <node id='5987264762' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688212' lon='-122.4031606' />\n  <node id='5987264763' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688217' lon='-122.4031516' />\n  <node id='5987264764' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688194' lon='-122.4031514' />\n  <node id='5987264765' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688131' lon='-122.4031601' />\n  <node id='5987264766' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688154' lon='-122.4031603' />\n  <node id='5987264767' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688159' lon='-122.4031513' />\n  <node id='5987264768' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688136' lon='-122.4031511' />\n  <node id='5987264769' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688014' lon='-122.4031608' />\n  <node id='5987264770' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688037' lon='-122.403161' />\n  <node id='5987264771' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688042' lon='-122.4031519' />\n  <node id='5987264772' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688019' lon='-122.4031517' />\n  <node id='5987264773' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688199' lon='-122.4034428' />\n  <node id='5987264774' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688199' lon='-122.4034469' />\n  <node id='5987264775' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688107' lon='-122.4034468' />\n  <node id='5987264776' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688108' lon='-122.4034428' />\n  <node id='5987264777' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688069' lon='-122.4034428' />\n  <node id='5987264778' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688069' lon='-122.4034469' />\n  <node id='5987264779' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687978' lon='-122.4034468' />\n  <node id='5987264780' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687978' lon='-122.4034428' />\n  <node id='5987264781' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687936' lon='-122.4034415' />\n  <node id='5987264782' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687936' lon='-122.4034455' />\n  <node id='5987264783' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687845' lon='-122.4034455' />\n  <node id='5987264784' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687845' lon='-122.4034414' />\n  <node id='5987264785' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688201' lon='-122.4034358' />\n  <node id='5987264786' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688201' lon='-122.4034398' />\n  <node id='5987264787' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768811' lon='-122.4034398' />\n  <node id='5987264788' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768811' lon='-122.4034357' />\n  <node id='5987264789' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688069' lon='-122.4034351' />\n  <node id='5987264790' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688069' lon='-122.4034391' />\n  <node id='5987264791' timestamp='2018-10-15T23:37:35Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687978' lon='-122.4034391' />\n  <node id='5987264792' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687978' lon='-122.4034351' />\n  <node id='5987264793' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687942' lon='-122.4034338' />\n  <node id='5987264794' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687941' lon='-122.4034378' />\n  <node id='5987264795' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768785' lon='-122.4034377' />\n  <node id='5987264796' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768785' lon='-122.4034337' />\n  <node id='5987264797' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687883' lon='-122.403379' />\n  <node id='5987264798' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687883' lon='-122.403383' />\n  <node id='5987264799' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687792' lon='-122.4033829' />\n  <node id='5987264800' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687792' lon='-122.4033789' />\n  <node id='5987264801' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688011' lon='-122.4033803' />\n  <node id='5987264802' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768801' lon='-122.4033843' />\n  <node id='5987264803' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687919' lon='-122.4033843' />\n  <node id='5987264804' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687919' lon='-122.4033802' />\n  <node id='5987264805' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688143' lon='-122.403381' />\n  <node id='5987264806' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688143' lon='-122.403385' />\n  <node id='5987264807' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688052' lon='-122.4033849' />\n  <node id='5987264808' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688052' lon='-122.4033809' />\n  <node id='5987264809' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768814' lon='-122.403388' />\n  <node id='5987264810' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768814' lon='-122.403392' />\n  <node id='5987264811' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688049' lon='-122.403392' />\n  <node id='5987264812' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688049' lon='-122.403388' />\n  <node id='5987264813' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688011' lon='-122.403388' />\n  <node id='5987264814' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768801' lon='-122.403392' />\n  <node id='5987264815' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687919' lon='-122.403392' />\n  <node id='5987264816' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687919' lon='-122.403388' />\n  <node id='5987264817' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687878' lon='-122.4033867' />\n  <node id='5987264818' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687878' lon='-122.4033907' />\n  <node id='5987264819' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687787' lon='-122.4033906' />\n  <node id='5987264820' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687787' lon='-122.4033866' />\n  <node id='5987264821' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688066' lon='-122.4032962' />\n  <node id='5987264822' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688066' lon='-122.4033002' />\n  <node id='5987264823' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687975' lon='-122.4033001' />\n  <node id='5987264824' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687975' lon='-122.4032961' />\n  <node id='5987264825' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688193' lon='-122.4032975' />\n  <node id='5987264826' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688193' lon='-122.4033015' />\n  <node id='5987264827' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688102' lon='-122.4033014' />\n  <node id='5987264828' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688102' lon='-122.4032974' />\n  <node id='5987264829' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688326' lon='-122.4032982' />\n  <node id='5987264830' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688326' lon='-122.4033022' />\n  <node id='5987264831' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688235' lon='-122.4033021' />\n  <node id='5987264832' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688235' lon='-122.4032981' />\n  <node id='5987264833' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688323' lon='-122.4033052' />\n  <node id='5987264834' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688323' lon='-122.4033092' />\n  <node id='5987264835' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688232' lon='-122.4033092' />\n  <node id='5987264836' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688232' lon='-122.4033051' />\n  <node id='5987264837' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688193' lon='-122.4033052' />\n  <node id='5987264838' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688193' lon='-122.4033092' />\n  <node id='5987264839' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688102' lon='-122.4033092' />\n  <node id='5987264840' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688102' lon='-122.4033051' />\n  <node id='5987264841' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688061' lon='-122.4033039' />\n  <node id='5987264842' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688061' lon='-122.4033079' />\n  <node id='5987264843' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768797' lon='-122.4033078' />\n  <node id='5987264844' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768797' lon='-122.4033038' />\n  <node id='5987264845' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687958' lon='-122.4032449' />\n  <node id='5987264846' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687957' lon='-122.4032489' />\n  <node id='5987264847' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687866' lon='-122.4032488' />\n  <node id='5987264848' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687866' lon='-122.4032448' />\n  <node id='5987264849' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688085' lon='-122.4032462' />\n  <node id='5987264850' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688085' lon='-122.4032502' />\n  <node id='5987264851' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687993' lon='-122.4032502' />\n  <node id='5987264852' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687994' lon='-122.4032461' />\n  <node id='5987264853' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688217' lon='-122.4032469' />\n  <node id='5987264854' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688217' lon='-122.4032509' />\n  <node id='5987264855' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688126' lon='-122.4032508' />\n  <node id='5987264856' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688126' lon='-122.4032468' />\n  <node id='5987264857' timestamp='2018-10-15T23:37:36Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688215' lon='-122.4032539' />\n  <node id='5987264858' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688214' lon='-122.4032579' />\n  <node id='5987264859' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688123' lon='-122.4032579' />\n  <node id='5987264860' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688123' lon='-122.4032538' />\n  <node id='5987264861' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688085' lon='-122.4032539' />\n  <node id='5987264862' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688085' lon='-122.4032579' />\n  <node id='5987264863' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687993' lon='-122.4032579' />\n  <node id='5987264864' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687994' lon='-122.4032538' />\n  <node id='5987264865' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687952' lon='-122.4032526' />\n  <node id='5987264866' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687952' lon='-122.4032566' />\n  <node id='5987264867' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687861' lon='-122.4032565' />\n  <node id='5987264868' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687861' lon='-122.4032525' />\n  <node id='5987264869' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688457' lon='-122.403315' />\n  <node id='5987264870' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688404' lon='-122.4032967' />\n  <node id='5987264871' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688314' lon='-122.4032961' />\n  <node id='5987264872' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688361' lon='-122.4031852' />\n  <node id='5987264873' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688427' lon='-122.4031857' />\n  <node id='5987264874' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688381' lon='-122.4032912' />\n  <node id='5987264875' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688467' lon='-122.4032918' />\n  <node id='5987264876' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688396' lon='-122.4033146' />\n  <node id='5987264877' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688142' lon='-122.403478' />\n  <node id='5987264878' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688112' lon='-122.4034778' />\n  <node id='5987264879' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688319' lon='-122.4031054' />\n  <node id='5987264880' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688347' lon='-122.4031056' />\n  <node id='5987264881' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688514' lon='-122.4033386' />\n  <node id='5987264882' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688512' lon='-122.4033423' />\n  <node id='5987264883' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687624' lon='-122.4033318' />\n  <node id='5987264884' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687622' lon='-122.4033355' />\n  <node id='5987264885' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687832' lon='-122.4036378' />\n  <node id='5987264886' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687809' lon='-122.4036378' />\n  <node id='5987264887' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687809' lon='-122.4036288' />\n  <node id='5987264888' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687832' lon='-122.4036288' />\n  <node id='5987264889' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687896' lon='-122.4036386' />\n  <node id='5987264890' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687873' lon='-122.4036386' />\n  <node id='5987264891' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687873' lon='-122.4036296' />\n  <node id='5987264892' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687896' lon='-122.4036296' />\n  <node id='5987264893' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687671' lon='-122.4036368' />\n  <node id='5987264894' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687648' lon='-122.4036368' />\n  <node id='5987264895' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687647' lon='-122.4036278' />\n  <node id='5987264896' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687671' lon='-122.4036277' />\n  <node id='5987264897' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687734' lon='-122.4036376' />\n  <node id='5987264898' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687711' lon='-122.4036376' />\n  <node id='5987264899' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687711' lon='-122.4036286' />\n  <node id='5987264900' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7687734' lon='-122.4036285' />\n  <node id='5987264901' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688191' lon='-122.4035305' />\n  <node id='5987264902' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768818' lon='-122.4035301' />\n  <node id='5987264903' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768817' lon='-122.4035293' />\n  <node id='5987264904' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688163' lon='-122.4035281' />\n  <node id='5987264905' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688159' lon='-122.4035267' />\n  <node id='5987264906' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688159' lon='-122.4035252' />\n  <node id='5987264907' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688163' lon='-122.4035237' />\n  <node id='5987264908' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768817' lon='-122.4035225' />\n  <node id='5987264909' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768818' lon='-122.4035217' />\n  <node id='5987264910' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688191' lon='-122.4035212' />\n  <node id='5987264911' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688203' lon='-122.4035213' />\n  <node id='5987264912' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688214' lon='-122.4035218' />\n  <node id='5987264913' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688224' lon='-122.4035229' />\n  <node id='5987264914' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768823' lon='-122.4035243' />\n  <node id='5987264915' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688233' lon='-122.4035259' />\n  <node id='5987264916' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688231' lon='-122.4035275' />\n  <node id='5987264917' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688224' lon='-122.4035289' />\n  <node id='5987264918' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688214' lon='-122.4035299' />\n  <node id='5987264919' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688203' lon='-122.4035305' />\n  <node id='5987264920' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688184' lon='-122.4035453' />\n  <node id='5987264921' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688172' lon='-122.4035449' />\n  <node id='5987264922' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688163' lon='-122.403544' />\n  <node id='5987264923' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688155' lon='-122.4035428' />\n  <node id='5987264924' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688152' lon='-122.4035414' />\n  <node id='5987264925' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688152' lon='-122.4035399' />\n  <node id='5987264926' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688155' lon='-122.4035385' />\n  <node id='5987264927' timestamp='2018-10-15T23:37:37Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688162' lon='-122.4035372' />\n  <node id='5987264928' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688172' lon='-122.4035364' />\n  <node id='5987264929' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688184' lon='-122.403536' />\n  <node id='5987264930' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688195' lon='-122.403536' />\n  <node id='5987264931' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688207' lon='-122.4035365' />\n  <node id='5987264932' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688216' lon='-122.4035376' />\n  <node id='5987264933' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688223' lon='-122.403539' />\n  <node id='5987264934' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688225' lon='-122.4035406' />\n  <node id='5987264935' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688223' lon='-122.4035422' />\n  <node id='5987264936' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688217' lon='-122.4035436' />\n  <node id='5987264937' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688207' lon='-122.4035447' />\n  <node id='5987264938' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688196' lon='-122.4035452' />\n  <node id='5987264939' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688177' lon='-122.403556' />\n  <node id='5987264940' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688166' lon='-122.4035556' />\n  <node id='5987264941' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688156' lon='-122.4035547' />\n  <node id='5987264942' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688149' lon='-122.4035536' />\n  <node id='5987264943' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688145' lon='-122.4035521' />\n  <node id='5987264944' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688145' lon='-122.4035506' />\n  <node id='5987264945' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688148' lon='-122.4035492' />\n  <node id='5987264946' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688156' lon='-122.403548' />\n  <node id='5987264947' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688165' lon='-122.4035471' />\n  <node id='5987264948' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688177' lon='-122.4035467' />\n  <node id='5987264949' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688189' lon='-122.4035468' />\n  <node id='5987264950' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76882' lon='-122.4035473' />\n  <node id='5987264951' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768821' lon='-122.4035483' />\n  <node id='5987264952' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688216' lon='-122.4035497' />\n  <node id='5987264953' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688218' lon='-122.4035513' />\n  <node id='5987264954' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688216' lon='-122.4035529' />\n  <node id='5987264955' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768821' lon='-122.4035543' />\n  <node id='5987264956' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76882' lon='-122.4035554' />\n  <node id='5987264957' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688189' lon='-122.4035559' />\n  <node id='5987264958' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690293' lon='-122.4034196' />\n  <node id='5987264959' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769027' lon='-122.4034196' />\n  <node id='5987264960' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769027' lon='-122.4034105' />\n  <node id='5987264961' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690293' lon='-122.4034105' />\n  <node id='5987264962' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690357' lon='-122.4034199' />\n  <node id='5987264963' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690334' lon='-122.4034199' />\n  <node id='5987264964' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690334' lon='-122.4034108' />\n  <node id='5987264965' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690357' lon='-122.4034108' />\n  <node id='5987264966' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690304' lon='-122.4034052' />\n  <node id='5987264967' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690281' lon='-122.4034052' />\n  <node id='5987264968' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690281' lon='-122.4033961' />\n  <node id='5987264969' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690304' lon='-122.4033961' />\n  <node id='5987264970' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690368' lon='-122.4034054' />\n  <node id='5987264971' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690345' lon='-122.4034054' />\n  <node id='5987264972' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690345' lon='-122.4033964' />\n  <node id='5987264973' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690368' lon='-122.4033964' />\n  <node id='5987264974' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690792' lon='-122.4034229' />\n  <node id='5987264975' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690768' lon='-122.4034229' />\n  <node id='5987264976' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690768' lon='-122.4034139' />\n  <node id='5987264977' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690792' lon='-122.4034139' />\n  <node id='5987264978' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690856' lon='-122.4034232' />\n  <node id='5987264979' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690832' lon='-122.4034232' />\n  <node id='5987264980' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690832' lon='-122.4034142' />\n  <node id='5987264981' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690855' lon='-122.4034142' />\n  <node id='5987264982' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690797' lon='-122.4034052' />\n  <node id='5987264983' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690774' lon='-122.4034052' />\n  <node id='5987264984' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690774' lon='-122.4033961' />\n  <node id='5987264985' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690797' lon='-122.4033961' />\n  <node id='5987264986' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690861' lon='-122.4034054' />\n  <node id='5987264987' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690838' lon='-122.4034054' />\n  <node id='5987264988' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690837' lon='-122.4033964' />\n  <node id='5987264989' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690861' lon='-122.4033964' />\n  <node id='5987264990' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690765' lon='-122.4032912' />\n  <node id='5987264991' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690742' lon='-122.4032912' />\n  <node id='5987264992' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690742' lon='-122.4032821' />\n  <node id='5987264993' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690765' lon='-122.4032821' />\n  <node id='5987264994' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690829' lon='-122.4032914' />\n  <node id='5987264995' timestamp='2018-10-15T23:37:38Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690806' lon='-122.4032915' />\n  <node id='5987264996' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690806' lon='-122.4032824' />\n  <node id='5987264997' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690829' lon='-122.4032824' />\n  <node id='5987264998' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769076' lon='-122.4033069' />\n  <node id='5987264999' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690737' lon='-122.4033069' />\n  <node id='5987265000' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690736' lon='-122.4032979' />\n  <node id='5987265001' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769076' lon='-122.4032979' />\n  <node id='5987265002' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690824' lon='-122.4033072' />\n  <node id='5987265003' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690801' lon='-122.4033072' />\n  <node id='5987265004' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.76908' lon='-122.4032982' />\n  <node id='5987265005' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690824' lon='-122.4032982' />\n  <node id='5987265006' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690558' lon='-122.4033046' />\n  <node id='5987265007' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690535' lon='-122.4033046' />\n  <node id='5987265008' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690535' lon='-122.4032955' />\n  <node id='5987265009' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690558' lon='-122.4032955' />\n  <node id='5987265010' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690622' lon='-122.4033049' />\n  <node id='5987265011' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690599' lon='-122.4033049' />\n  <node id='5987265012' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690599' lon='-122.4032958' />\n  <node id='5987265013' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690622' lon='-122.4032958' />\n  <node id='5987265014' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690574' lon='-122.4032882' />\n  <node id='5987265015' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690551' lon='-122.4032882' />\n  <node id='5987265016' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690551' lon='-122.4032791' />\n  <node id='5987265017' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690574' lon='-122.4032791' />\n  <node id='5987265018' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690638' lon='-122.4032884' />\n  <node id='5987265019' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690615' lon='-122.4032884' />\n  <node id='5987265020' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690615' lon='-122.4032794' />\n  <node id='5987265021' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690638' lon='-122.4032794' />\n  <node id='5987265022' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691049' lon='-122.4033983' />\n  <node id='5987265023' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691026' lon='-122.4033983' />\n  <node id='5987265024' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691025' lon='-122.4033892' />\n  <node id='5987265025' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7691049' lon='-122.4033892' />\n  <node id='5987265026' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690612' lon='-122.4034224' />\n  <node id='5987265027' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690588' lon='-122.4034224' />\n  <node id='5987265028' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690588' lon='-122.4034134' />\n  <node id='5987265029' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690611' lon='-122.4034134' />\n  <node id='5987265030' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690659' lon='-122.403404' />\n  <node id='5987265031' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690636' lon='-122.403404' />\n  <node id='5987265032' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690636' lon='-122.4033949' />\n  <node id='5987265033' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690659' lon='-122.4033949' />\n  <node id='5987265034' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690527' lon='-122.403403' />\n  <node id='5987265035' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690504' lon='-122.403403' />\n  <node id='5987265036' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690503' lon='-122.4033939' />\n  <node id='5987265037' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7690527' lon='-122.4033939' />\n  <node id='5987265038' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689697' lon='-122.4033849' />\n  <node id='5987265039' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689674' lon='-122.4033849' />\n  <node id='5987265040' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689674' lon='-122.4033758' />\n  <node id='5987265041' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689697' lon='-122.4033758' />\n  <node id='5987265042' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689663' lon='-122.4033952' />\n  <node id='5987265043' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768964' lon='-122.4033953' />\n  <node id='5987265044' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689639' lon='-122.4033862' />\n  <node id='5987265045' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689663' lon='-122.4033862' />\n  <node id='5987265046' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689732' lon='-122.4033453' />\n  <node id='5987265047' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689708' lon='-122.4033453' />\n  <node id='5987265048' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689708' lon='-122.4033362' />\n  <node id='5987265049' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689732' lon='-122.4033362' />\n  <node id='5987265050' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688925' lon='-122.4036077' />\n  <node id='5987265051' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688924' lon='-122.4036122' />\n  <node id='5987265052' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688718' lon='-122.4036116' />\n  <node id='5987265053' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688719' lon='-122.4036071' />\n  <node id='5987265054' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688981' lon='-122.4035115' />\n  <node id='5987265055' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768898' lon='-122.403516' />\n  <node id='5987265056' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688774' lon='-122.4035154' />\n  <node id='5987265057' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688775' lon='-122.4035109' />\n  <node id='5987265058' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688975' lon='-122.4035256' />\n  <node id='5987265059' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688975' lon='-122.4035301' />\n  <node id='5987265060' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688768' lon='-122.4035294' />\n  <node id='5987265061' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688769' lon='-122.403525' />\n  <node id='5987265062' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688938' lon='-122.4033911' />\n  <node id='5987265063' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688938' lon='-122.4033951' />\n  <node id='5987265064' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688847' lon='-122.403395' />\n  <node id='5987265065' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688847' lon='-122.403391' />\n  <node id='5987265066' timestamp='2018-10-15T23:37:39Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768885' lon='-122.4032566' />\n  <node id='5987265067' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768885' lon='-122.4032606' />\n  <node id='5987265068' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688674' lon='-122.4032603' />\n  <node id='5987265069' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688674' lon='-122.4032563' />\n  <node id='5987265070' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689057' lon='-122.403211' />\n  <node id='5987265071' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7689056' lon='-122.403215' />\n  <node id='5987265072' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688881' lon='-122.4032144' />\n  <node id='5987265073' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688882' lon='-122.4032103' />\n  <node id='5987265074' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688758' lon='-122.4031768' />\n  <node id='5987265075' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688757' lon='-122.4031808' />\n  <node id='5987265076' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688361' lon='-122.4031792' />\n  <node id='5987265077' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688362' lon='-122.4031751' />\n  <node id='5987265078' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768835' lon='-122.4035053' />\n  <node id='5987265079' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688327' lon='-122.4035084' />\n  <node id='5987265080' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688065' lon='-122.4035071' />\n  <node id='5987265081' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688066' lon='-122.4035039' />\n  <node id='5987265082' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.768834' lon='-122.4035366' />\n  <node id='5987265083' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7688318' lon='-122.4035365' />\n  <node id='5987265084' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695876' lon='-122.4033777' />\n  <node id='5987265085' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695763' lon='-122.4033771' />\n  <node id='5987265086' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695781' lon='-122.4033188' />\n  <node id='5987265087' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695895' lon='-122.4033194' />\n  <node id='5987265088' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696005' lon='-122.4032977' />\n  <node id='5987265089' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695897' lon='-122.4032999' />\n  <node id='5987265090' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696012' lon='-122.4032834' />\n  <node id='5987265091' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695905' lon='-122.4032826' />\n  <node id='5987265092' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696536' lon='-122.4033035' />\n  <node id='5987265093' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696528' lon='-122.4033185' />\n  <node id='5987265094' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695532' lon='-122.4033106' />\n  <node id='5987265095' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769554' lon='-122.4032955' />\n  <node id='5987265096' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696326' lon='-122.403268' />\n  <node id='5987265097' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696325' lon='-122.4032777' />\n  <node id='5987265098' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696296' lon='-122.4032776' />\n  <node id='5987265099' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696297' lon='-122.403268' />\n  <node id='5987265100' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696389' lon='-122.4032689' />\n  <node id='5987265101' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696388' lon='-122.4032785' />\n  <node id='5987265102' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696358' lon='-122.4032784' />\n  <node id='5987265103' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696359' lon='-122.4032688' />\n  <node id='5987265104' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696334' lon='-122.4032503' />\n  <node id='5987265105' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696333' lon='-122.40326' />\n  <node id='5987265106' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696304' lon='-122.4032599' />\n  <node id='5987265107' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696305' lon='-122.4032503' />\n  <node id='5987265108' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696397' lon='-122.4032512' />\n  <node id='5987265109' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696396' lon='-122.4032608' />\n  <node id='5987265110' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696366' lon='-122.4032607' />\n  <node id='5987265111' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696367' lon='-122.4032511' />\n  <node id='5987265112' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769634' lon='-122.4032325' />\n  <node id='5987265113' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696339' lon='-122.4032422' />\n  <node id='5987265114' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769631' lon='-122.4032421' />\n  <node id='5987265115' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696311' lon='-122.4032325' />\n  <node id='5987265116' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696403' lon='-122.4032334' />\n  <node id='5987265117' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696402' lon='-122.403243' />\n  <node id='5987265118' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696372' lon='-122.4032429' />\n  <node id='5987265119' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7696373' lon='-122.4032333' />\n  <node id='5987265120' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695335' lon='-122.4032429' />\n  <node id='5987265121' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695334' lon='-122.4032526' />\n  <node id='5987265122' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695305' lon='-122.4032525' />\n  <node id='5987265123' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695306' lon='-122.4032429' />\n  <node id='5987265124' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695398' lon='-122.4032438' />\n  <node id='5987265125' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695397' lon='-122.4032534' />\n  <node id='5987265126' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695367' lon='-122.4032533' />\n  <node id='5987265127' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695368' lon='-122.4032437' />\n  <node id='5987265128' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695343' lon='-122.4032251' />\n  <node id='5987265129' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695342' lon='-122.4032348' />\n  <node id='5987265130' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695313' lon='-122.4032347' />\n  <node id='5987265131' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695314' lon='-122.4032251' />\n  <node id='5987265132' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695406' lon='-122.403226' />\n  <node id='5987265133' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695405' lon='-122.4032356' />\n  <node id='5987265134' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695375' lon='-122.4032355' />\n  <node id='5987265135' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695376' lon='-122.4032259' />\n  <node id='5987265136' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769534' lon='-122.4032067' />\n  <node id='5987265137' timestamp='2018-10-15T23:37:40Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695339' lon='-122.4032164' />\n  <node id='5987265138' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769531' lon='-122.4032163' />\n  <node id='5987265139' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695311' lon='-122.4032067' />\n  <node id='5987265140' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695403' lon='-122.4032076' />\n  <node id='5987265141' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695402' lon='-122.4032172' />\n  <node id='5987265142' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695372' lon='-122.4032171' />\n  <node id='5987265143' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695373' lon='-122.4032075' />\n  <node id='5987265144' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769522' lon='-122.4033071' />\n  <node id='5987265145' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695296' lon='-122.4033074' />\n  <node id='5987265146' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695295' lon='-122.4033111' />\n  <node id='5987265147' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695219' lon='-122.4033107' />\n  <node id='5987265148' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695228' lon='-122.4032991' />\n  <node id='5987265149' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695304' lon='-122.4032995' />\n  <node id='5987265150' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695303' lon='-122.4033033' />\n  <node id='5987265151' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695227' lon='-122.4033029' />\n  <node id='5987265152' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695069' lon='-122.4033041' />\n  <node id='5987265153' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695145' lon='-122.4033044' />\n  <node id='5987265154' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695144' lon='-122.4033081' />\n  <node id='5987265155' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695068' lon='-122.4033078' />\n  <node id='5987265156' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695077' lon='-122.4032961' />\n  <node id='5987265157' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695153' lon='-122.4032965' />\n  <node id='5987265158' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695152' lon='-122.4033003' />\n  <node id='5987265159' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695076' lon='-122.4032999' />\n  <node id='5987265160' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694923' lon='-122.4033037' />\n  <node id='5987265161' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694999' lon='-122.4033041' />\n  <node id='5987265162' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694998' lon='-122.4033078' />\n  <node id='5987265163' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694922' lon='-122.4033074' />\n  <node id='5987265164' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694931' lon='-122.4032958' />\n  <node id='5987265165' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695007' lon='-122.4032962' />\n  <node id='5987265166' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695006' lon='-122.4033' />\n  <node id='5987265167' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769493' lon='-122.4032996' />\n  <node id='5987265168' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694893' lon='-122.4032692' />\n  <node id='5987265169' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695165' lon='-122.4032699' />\n  <node id='5987265170' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695191' lon='-122.4032738' />\n  <node id='5987265171' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694892' lon='-122.4032731' />\n  <node id='5987265172' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695194' lon='-122.4032574' />\n  <node id='5987265173' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695166' lon='-122.40326' />\n  <node id='5987265174' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694999' lon='-122.4032568' />\n  <node id='5987265175' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694998' lon='-122.4032596' />\n  <node id='5987265176' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7695025' lon='-122.4033432' />\n  <node id='5987265177' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694911' lon='-122.4033428' />\n  <node id='5987265178' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.7694916' lon='-122.4033226' />\n  <node id='5987265179' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461' lat='37.769503' lon='-122.4033231' />\n  <node id='5987265744' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='1' changeset='63558513' lat='37.7694109' lon='-122.4036338' />\n  <node id='5987265745' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='1' changeset='63558513' lat='37.7694294' lon='-122.4032851' />\n  <node id='5987265746' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='1' changeset='63558513' lat='37.7693499' lon='-122.4036904' />\n  <node id='5987265747' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='1' changeset='63558513' lat='37.7687092' lon='-122.4035163' />\n  <node id='5991623594' timestamp='2018-10-16T21:34:13Z' uid='2237750' user='chachafish' version='1' changeset='63591679' lat='37.769006' lon='-122.4037645'>\n    <tag k='entrance' v='yes' />\n  </node>\n  <node id='6024177029' timestamp='2018-10-30T06:09:25Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.7666615' lon='-122.394936'>\n    <tag k='railway' v='switch' />\n  </node>\n  <node id='6024177032' timestamp='2018-10-30T06:09:25Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.7675775' lon='-122.3959716' />\n  <node id='6024177033' timestamp='2018-10-30T06:09:25Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.7678902' lon='-122.3965211'>\n    <tag k='railway' v='switch' />\n  </node>\n  <node id='6024177034' timestamp='2018-10-30T06:09:25Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.7684835' lon='-122.3971898'>\n    <tag k='railway' v='switch' />\n  </node>\n  <node id='6024177035' timestamp='2018-10-30T06:09:25Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.7675443' lon='-122.3960132' />\n  <node id='6024177036' timestamp='2018-10-30T06:09:25Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.768771' lon='-122.3975512'>\n    <tag k='railway' v='switch' />\n  </node>\n  <node id='6024177037' timestamp='2018-10-30T06:09:25Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.7692733' lon='-122.3982538'>\n    <tag k='railway' v='switch' />\n  </node>\n  <node id='6024177038' timestamp='2018-10-30T06:09:25Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.7724927' lon='-122.3997883' />\n  <node id='6024177069' timestamp='2018-10-30T06:09:26Z' uid='119881' user='clay_c' version='1' changeset='64000226' lat='37.7671688' lon='-122.3955319'>\n    <tag k='railway' v='switch' />\n  </node>\n  <way id='8915263' timestamp='2018-08-18T23:51:35Z' uid='119881' user='clay_c' version='30' changeset='61782308'>\n    <nd ref='65371530' />\n    <nd ref='733634101' />\n    <nd ref='733634117' />\n    <nd ref='314079075' />\n    <nd ref='733634124' />\n    <nd ref='300757485' />\n    <nd ref='733634133' />\n    <nd ref='65283822' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='Golden Gate Bridge' />\n    <tag k='destination:ref' v='US 101 North' />\n    <tag k='highway' v='motorway_link' />\n    <tag k='junction:ref' v='1B' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='1' />\n    <tag k='maxspeed:advisory' v='40 mph' />\n    <tag k='oneway' v='yes' />\n    <tag k='tiger:cfcc' v='A63' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:reviewed' v='no' />\n  </way>\n  <way id='8915288' timestamp='2018-08-18T23:51:35Z' uid='119881' user='clay_c' version='57' changeset='61782308'>\n    <nd ref='295222216' />\n    <nd ref='1674117133' />\n    <nd ref='65374349' />\n    <nd ref='299388080' />\n    <nd ref='733582844' />\n    <nd ref='65374346' />\n    <nd ref='733582849' />\n    <nd ref='65374344' />\n    <nd ref='733582853' />\n    <nd ref='733582855' />\n    <nd ref='733583392' />\n    <nd ref='65374342' />\n    <nd ref='65283717' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='highway' v='motorway' />\n    <tag k='junction:ref' v='433C' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='maxspeed' v='50 mph' />\n    <tag k='name' v='Central Freeway' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='US 101' />\n    <tag k='tiger:cfcc' v='A41;A25' />\n    <tag k='tiger:county' v='San Francisco, CA;San Francisco, CA' />\n    <tag k='tiger:name_base' v='United States Highway 101' />\n    <tag k='tiger:name_base_1' v='13th' />\n    <tag k='tiger:name_type_1' v='St' />\n  </way>\n  <way id='8916338' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='20' changeset='63557245'>\n    <nd ref='65296271' />\n    <nd ref='4941620823' />\n    <nd ref='65294602' />\n    <nd ref='65294603' />\n    <tag k='highway' v='residential' />\n    <tag k='lanes' v='2' />\n    <tag k='name' v='Rhode Island Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='8916798' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='21' changeset='63557245'>\n    <nd ref='1677798439' />\n    <nd ref='5987119090' />\n    <nd ref='65294602' />\n    <nd ref='5987119089' />\n    <nd ref='4941620828' />\n    <nd ref='4941620831' />\n    <nd ref='65303878' />\n    <nd ref='65282185' />\n    <nd ref='65303885' />\n    <nd ref='539993902' />\n    <nd ref='65303886' />\n    <nd ref='65303894' />\n    <nd ref='5438253600' />\n    <nd ref='65303897' />\n    <nd ref='65303899' />\n    <tag k='highway' v='residential' />\n    <tag k='lanes' v='2' />\n    <tag k='name' v='Alameda Street' />\n    <tag k='sidewalk' v='both' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='8917113' timestamp='2018-02-26T19:08:32Z' uid='5288591' user='Irina Karachevtseva' version='14' changeset='56699939'>\n    <nd ref='65339116' />\n    <nd ref='295219625' />\n    <nd ref='295219626' />\n    <nd ref='5438253597' />\n    <nd ref='295219627' />\n    <nd ref='65303886' />\n    <nd ref='65310195' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='San Bruno Avenue' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='San Bruno' />\n    <tag k='tiger:name_type' v='Ave' />\n  </way>\n  <way id='8917256' timestamp='2018-08-27T20:22:24Z' uid='53073' user='Aaron Lidman' version='23' changeset='62050947'>\n    <nd ref='65313423' />\n    <nd ref='5780936416' />\n    <nd ref='65313426' />\n    <nd ref='65313427' />\n    <nd ref='65313429' />\n    <nd ref='1941018589' />\n    <tag k='access' v='no' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='Channel Street' />\n    <tag k='surface' v='paved' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='8917337' timestamp='2018-03-26T19:01:36Z' uid='53073' user='Aaron Lidman' version='15' changeset='57545421'>\n    <nd ref='65308756' />\n    <nd ref='295219756' />\n    <nd ref='295219758' />\n    <nd ref='65315506' />\n    <nd ref='4880860064' />\n    <nd ref='65315504' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='4' />\n    <tag k='lanes:backward' v='2' />\n    <tag k='lanes:forward' v='2' />\n    <tag k='name' v='Brannan Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Brannan' />\n    <tag k='tiger:name_type' v='St' />\n    <tag k='turn:lanes:backward' v='left;through;right|right' />\n  </way>\n  <way id='8919269' timestamp='2018-03-26T19:01:36Z' uid='53073' user='Aaron Lidman' version='20' changeset='57545421'>\n    <nd ref='65339116' />\n    <nd ref='3532719196' />\n    <nd ref='3532719195' />\n    <nd ref='4880860065' />\n    <nd ref='4880860061' />\n    <nd ref='65315504' />\n    <tag k='highway' v='primary' />\n    <tag k='name' v='9th Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='9th' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='8919954' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='16' changeset='63557245'>\n    <nd ref='65328379' />\n    <nd ref='4941620853' />\n    <nd ref='65303878' />\n    <tag k='cycleway' v='shared_lane' />\n    <tag k='highway' v='residential' />\n    <tag k='lcn_ref' v='123' />\n    <tag k='name' v='Henry Adams Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Henry Adams' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='8920472' timestamp='2018-11-05T17:44:53Z' uid='119881' user='clay_c' version='33' changeset='64205910'>\n    <nd ref='1344032476' />\n    <nd ref='1344032691' />\n    <nd ref='1344032359' />\n    <nd ref='1344032216' />\n    <nd ref='6024177037' />\n    <nd ref='1344032591' />\n    <nd ref='6024177033' />\n    <nd ref='1344032635' />\n    <nd ref='1344032279' />\n    <nd ref='6024177029' />\n    <nd ref='767817443' />\n    <nd ref='2922128564' />\n    <nd ref='767817444' />\n    <nd ref='767817445' />\n    <nd ref='767817446' />\n    <nd ref='767817447' />\n    <nd ref='65356577' />\n    <nd ref='496536677' />\n    <nd ref='496536678' />\n    <nd ref='466244863' />\n    <tag k='electrified' v='no' />\n    <tag k='gauge' v='1435' />\n    <tag k='history' v='Retrieved from v10' />\n    <tag k='maxspeed' v='40 mph' />\n    <tag k='maxspeed:freight' v='10 mph' />\n    <tag k='name' v='Peninsula Subdivision Main 2' />\n    <tag k='owner' v='Peninsula Corridor Joint Powers Board' />\n    <tag k='railway' v='rail' />\n    <tag k='railway:track_ref' v='2' />\n    <tag k='railway:traffic_mode' v='passenger' />\n    <tag k='tiger:cfcc' v='B11:B21' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Union Pacific Railroad' />\n    <tag k='usage' v='main' />\n  </way>\n  <way id='23883760' timestamp='2017-06-29T07:00:06Z' uid='5659851' user='marthaleena' version='10' changeset='49908842'>\n    <nd ref='65303726' />\n    <nd ref='4941620710' />\n    <nd ref='1485472609' />\n    <nd ref='4941620822' />\n    <nd ref='65320168' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='Berry Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Berry' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='23883761' timestamp='2017-06-19T20:20:39Z' uid='1306' user='PlaneMad' version='10' changeset='49676208'>\n    <nd ref='65303723' />\n    <nd ref='4925027421' />\n    <nd ref='65355339' />\n    <nd ref='65355343' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='King Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='King' />\n    <tag k='tiger:name_type' v='St' />\n    <tag k='tiger:reviewed' v='no' />\n  </way>\n  <way id='25371830' timestamp='2018-01-22T20:21:13Z' uid='3817650' user='bdon_import' version='5' changeset='55665623'>\n    <nd ref='276545628' />\n    <nd ref='276545629' />\n    <nd ref='2923489922' />\n    <nd ref='276545630' />\n    <nd ref='276545631' />\n    <nd ref='276545628' />\n    <tag k='brand' v='Fitness SF' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='8' />\n    <tag k='leisure' v='fitness_centre' />\n    <tag k='name' v='Fitness SF SOMA' />\n    <tag k='operator' v='Fitness SF' />\n    <tag k='website' v='http://fitnesssf.com/location/soma/' />\n  </way>\n  <way id='25371872' timestamp='2018-03-18T22:12:46Z' uid='53073' user='Aaron Lidman' version='6' changeset='57302156'>\n    <nd ref='276545980' />\n    <nd ref='276545981' />\n    <nd ref='276545982' />\n    <nd ref='276545983' />\n    <nd ref='276545984' />\n    <nd ref='276545980' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='2' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Henry Adams Street' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='2' />\n  </way>\n  <way id='26943758' timestamp='2017-06-07T01:24:28Z' uid='1330847' user='TheDutchMan13' version='27' changeset='49322246'>\n    <nd ref='65315504' />\n    <nd ref='4880860062' />\n    <nd ref='1461189670' />\n    <nd ref='3532719204' />\n    <nd ref='2548141867' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='4' />\n    <tag k='name' v='Brannan Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Brannan' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='26944068' timestamp='2016-01-29T19:03:31Z' uid='94578' user='andygol' version='15' changeset='36885656'>\n    <nd ref='257670327' />\n    <nd ref='314079646' />\n    <nd ref='314079708' />\n    <nd ref='314079709' />\n    <nd ref='314079710' />\n    <nd ref='314079711' />\n    <nd ref='314079712' />\n    <nd ref='733638795' />\n    <nd ref='314079713' />\n    <nd ref='733638785' />\n    <nd ref='314079692' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='highway' v='motorway_link' />\n    <tag k='lanes' v='1' />\n    <tag k='layer' v='1' />\n    <tag k='oneway' v='yes' />\n    <tag k='tiger:cfcc' v='A63' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n  </way>\n  <way id='27167281' timestamp='2018-06-07T00:47:49Z' uid='1744893' user='Asumu Takikawa' version='25' changeset='59617626'>\n    <nd ref='2548141867' />\n    <nd ref='1266064662' />\n    <nd ref='5038477632' />\n    <tag k='bicycle' v='designated' />\n    <tag k='cycleway:left' v='lane' />\n    <tag k='cycleway:right' v='track' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='4' />\n    <tag k='lanes:backward' v='3' />\n    <tag k='lanes:forward' v='1' />\n    <tag k='lcn_ref' v='23' />\n    <tag k='maxspeed' v='30 mph' />\n    <tag k='name' v='8th Street' />\n    <tag k='oneway' v='no' />\n    <tag k='placement:forward' v='left_of:1' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='8th' />\n    <tag k='tiger:name_type' v='St' />\n    <tag k='turn:lanes:backward' v='left|left|right' />\n  </way>\n  <way id='27167756' timestamp='2018-08-18T23:51:36Z' uid='119881' user='clay_c' version='21' changeset='61782308'>\n    <nd ref='65283822' />\n    <nd ref='733634142' />\n    <nd ref='314078897' />\n    <nd ref='733634149' />\n    <nd ref='314078898' />\n    <nd ref='733634154' />\n    <nd ref='295221939' />\n    <nd ref='295221940' />\n    <nd ref='733632258' />\n    <nd ref='65283827' />\n    <nd ref='65283829' />\n    <nd ref='314078887' />\n    <nd ref='733632260' />\n    <nd ref='314078882' />\n    <nd ref='733632261' />\n    <nd ref='65283831' />\n    <nd ref='65283833' />\n    <nd ref='295221941' />\n    <nd ref='733630737' />\n    <nd ref='314078883' />\n    <nd ref='295220015' />\n    <nd ref='733630089' />\n    <nd ref='733630094' />\n    <nd ref='295221942' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='Golden Gate Bridge' />\n    <tag k='destination:ref' v='US 101 North' />\n    <tag k='highway' v='motorway_link' />\n    <tag k='junction:ref' v='1B' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='3' />\n    <tag k='maxspeed:advisory' v='40 mph' />\n    <tag k='oneway' v='yes' />\n    <tag k='tiger:cfcc' v='A63' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:reviewed' v='no' />\n  </way>\n  <way id='27167768' timestamp='2018-08-18T03:27:13Z' uid='119881' user='clay_c' version='33' changeset='61761408'>\n    <nd ref='65283717' />\n    <nd ref='2315558589' />\n    <nd ref='2315558590' />\n    <nd ref='733639375' />\n    <nd ref='733639377' />\n    <nd ref='295223359' />\n    <nd ref='65374339' />\n    <nd ref='314139236' />\n    <nd ref='733639385' />\n    <nd ref='314139237' />\n    <nd ref='295223386' />\n    <nd ref='733639389' />\n    <nd ref='65374337' />\n    <nd ref='733639392' />\n    <nd ref='65374334' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='Golden Gate Bridge' />\n    <tag k='destination:ref' v='US 101 North' />\n    <tag k='highway' v='motorway' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='maxspeed' v='50 mph' />\n    <tag k='name' v='Central Freeway' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='US 101' />\n    <tag k='tiger:cfcc' v='A41;A25' />\n    <tag k='tiger:county' v='San Francisco, CA;San Francisco, CA' />\n    <tag k='tiger:name_base' v='United States Highway 101' />\n    <tag k='tiger:name_base_1' v='13th' />\n    <tag k='tiger:name_type_1' v='St' />\n  </way>\n  <way id='27369705' timestamp='2018-04-13T21:53:09Z' uid='53073' user='Aaron Lidman' version='4' changeset='58077286'>\n    <nd ref='300460635' />\n    <nd ref='2613280503' />\n    <nd ref='666571384' />\n    <nd ref='300460638' />\n    <nd ref='300460639' />\n    <nd ref='5551421968' />\n    <nd ref='5551421967' />\n    <nd ref='5506470737' />\n    <nd ref='5184349785' />\n    <nd ref='5184349803' />\n    <nd ref='5184349802' />\n    <nd ref='5184349801' />\n    <nd ref='5184349800' />\n    <nd ref='5184349799' />\n    <nd ref='5184349798' />\n    <nd ref='5184349797' />\n    <nd ref='5184349796' />\n    <nd ref='5184349795' />\n    <nd ref='5184349794' />\n    <nd ref='5184349793' />\n    <nd ref='5184349792' />\n    <nd ref='5184349791' />\n    <nd ref='5551421966' />\n    <nd ref='5551421960' />\n    <nd ref='1709638633' />\n    <nd ref='1709638629' />\n    <nd ref='1709638627' />\n    <nd ref='1709638631' />\n    <nd ref='5184349819' />\n    <nd ref='5184349818' />\n    <nd ref='5184349789' />\n    <nd ref='2922218278' />\n    <nd ref='2922219017' />\n    <nd ref='300460643' />\n    <nd ref='300460635' />\n    <tag k='leisure' v='park' />\n    <tag k='name' v='Mission Creek Park' />\n  </way>\n  <way id='28518571' timestamp='2018-08-28T21:51:03Z' uid='8107451' user='njtbusfan' version='11' changeset='62086023'>\n    <nd ref='65303721' />\n    <nd ref='3532718884' />\n    <nd ref='65303723' />\n    <nd ref='65303726' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='secondary' />\n    <tag k='lcn_ref' v='23' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='name' v='7th Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='7th' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='28642121' timestamp='2014-07-20T23:35:35Z' uid='28775' user='StellanL' version='3' changeset='24263865'>\n    <nd ref='2282804188' />\n    <nd ref='314810083' />\n    <nd ref='314810084' />\n    <nd ref='314810085' />\n    <nd ref='2282804185' />\n    <nd ref='2282804189' />\n    <nd ref='2282804191' />\n    <nd ref='2282804187' />\n    <nd ref='2282804186' />\n    <nd ref='2282804188' />\n    <tag k='landuse' v='retail' />\n  </way>\n  <way id='28689469' timestamp='2016-04-16T14:43:52Z' uid='28775' user='StellanL' version='13' changeset='38624789'>\n    <nd ref='310729134' />\n    <nd ref='3532718855' />\n    <nd ref='310729129' />\n    <nd ref='3532718854' />\n    <nd ref='2101121534' />\n    <nd ref='310729130' />\n    <nd ref='315342928' />\n    <nd ref='315342930' />\n    <nd ref='768247783' />\n    <nd ref='768247784' />\n    <nd ref='768247785' />\n    <nd ref='315342931' />\n    <nd ref='768247787' />\n    <nd ref='768247772' />\n    <nd ref='768247788' />\n    <nd ref='768247773' />\n    <nd ref='768247774' />\n    <nd ref='315342932' />\n    <nd ref='768247775' />\n    <nd ref='768247776' />\n    <nd ref='768247777' />\n    <nd ref='768247778' />\n    <nd ref='768247779' />\n    <nd ref='768247780' />\n    <nd ref='315342933' />\n    <nd ref='768247781' />\n    <nd ref='768247782' />\n    <nd ref='315342934' />\n    <nd ref='315342911' />\n    <nd ref='315342912' />\n    <nd ref='315342913' />\n    <nd ref='315342914' />\n    <nd ref='315342915' />\n    <nd ref='315342916' />\n    <nd ref='315342917' />\n    <nd ref='315342918' />\n    <nd ref='315342919' />\n    <nd ref='1709638662' />\n    <nd ref='315342920' />\n    <nd ref='4126311698' />\n    <nd ref='315342921' />\n    <nd ref='315342922' />\n    <nd ref='315342923' />\n    <nd ref='315342924' />\n    <nd ref='315342925' />\n    <nd ref='315342926' />\n    <nd ref='315342927' />\n    <nd ref='310729131' />\n    <nd ref='310729132' />\n    <nd ref='3532718858' />\n    <nd ref='3532718857' />\n    <nd ref='3532718856' />\n    <nd ref='310729133' />\n    <nd ref='3532718859' />\n    <nd ref='310729134' />\n    <tag k='landuse' v='railway' />\n    <tag k='name' v='Caltrain' />\n  </way>\n  <way id='31129765' timestamp='2018-08-18T23:51:36Z' uid='119881' user='clay_c' version='27' changeset='61782308'>\n    <nd ref='314079148' />\n    <nd ref='300757646' />\n    <nd ref='65371933' />\n    <nd ref='300757647' />\n    <nd ref='298081944' />\n    <nd ref='314079200' />\n    <nd ref='65283811' />\n    <nd ref='728909734' />\n    <nd ref='65371935' />\n    <nd ref='728909736' />\n    <nd ref='308712276' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='Bay Bridge;Oakland' />\n    <tag k='destination:ref' v='I 80 East' />\n    <tag k='hgv' v='designated' />\n    <tag k='hgv:national_network' v='yes' />\n    <tag k='highway' v='motorway' />\n    <tag k='junction:ref' v='433B' />\n    <tag k='lanes' v='3' />\n    <tag k='layer' v='3' />\n    <tag k='maxspeed' v='50 mph' />\n    <tag k='name' v='James Lick Freeway' />\n    <tag k='note' v='not officially part of the Interstate Highway System (west of unbuilt I-280)' />\n    <tag k='oneway' v='yes' />\n    <tag k='source:hgv:national_network' v='Title 23: Highways Part 658 http://ecfr.gpoaccess.gov/cgi/t/text/text-idx?c=ecfr&amp;rgn=div5&amp;view=text&amp;node=23:1.0.1.7.33&amp;idno=23' />\n    <tag k='tiger:cfcc' v='A15' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='James Lick' />\n    <tag k='tiger:name_type' v='Fwy' />\n  </way>\n  <way id='36331135' timestamp='2016-04-16T14:43:54Z' uid='28775' user='StellanL' version='7' changeset='38624789'>\n    <nd ref='258877153' />\n    <nd ref='423775811' />\n    <nd ref='1709638639' />\n    <nd ref='423775812' />\n    <nd ref='1709638655' />\n    <nd ref='423775813' />\n    <nd ref='1709638657' />\n    <nd ref='423775814' />\n    <nd ref='1709638660' />\n    <nd ref='616936988' />\n    <nd ref='423775815' />\n    <tag k='access' v='no' />\n    <tag k='highway' v='residential' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='37177689' timestamp='2018-08-28T21:51:04Z' uid='8107451' user='njtbusfan' version='27' changeset='62086023'>\n    <nd ref='258877153' />\n    <nd ref='5184349835' />\n    <nd ref='5184837262' />\n    <nd ref='5184837264' />\n    <nd ref='302493286' />\n    <nd ref='5184349783' />\n    <nd ref='300662244' />\n    <nd ref='5184349833' />\n    <nd ref='595774117' />\n    <nd ref='4126311691' />\n    <nd ref='708533541' />\n    <nd ref='590986678' />\n    <nd ref='65325067' />\n    <nd ref='590986656' />\n    <nd ref='708533580' />\n    <nd ref='590986641' />\n    <nd ref='3856374589' />\n    <nd ref='5435466127' />\n    <nd ref='666571226' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='Berry Street' />\n    <tag k='sidewalk' v='both' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Berry' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='43101607' timestamp='2017-06-07T01:26:18Z' uid='1330847' user='TheDutchMan13' version='8' changeset='49322246'>\n    <nd ref='65310195' />\n    <nd ref='539993900' />\n    <nd ref='539993901' />\n    <nd ref='539993902' />\n    <nd ref='539993903' />\n    <nd ref='539993904' />\n    <nd ref='539993905' />\n    <nd ref='65339116' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='San Bruno Avenue' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='San Bruno' />\n    <tag k='tiger:name_type' v='Ave' />\n  </way>\n  <way id='56645380' timestamp='2010-04-27T08:57:32Z' uid='116029' user='Gregory Arenius' version='1' changeset='4539232'>\n    <nd ref='708463475' />\n    <nd ref='708463477' />\n    <nd ref='708463482' />\n    <nd ref='708463485' />\n    <nd ref='708463475' />\n    <tag k='leisure' v='pitch' />\n    <tag k='sport' v='tennis' />\n    <tag k='surface' v='paved' />\n  </way>\n  <way id='56645382' timestamp='2018-04-13T21:53:10Z' uid='53073' user='Aaron Lidman' version='7' changeset='58077286'>\n    <nd ref='708533582' />\n    <nd ref='708463538' />\n    <nd ref='708463541' />\n    <nd ref='708463542' />\n    <nd ref='708463543' />\n    <nd ref='708463545' />\n    <nd ref='5184349771' />\n    <nd ref='708463547' />\n    <nd ref='708463548' />\n    <nd ref='708463550' />\n    <nd ref='708463568' />\n    <nd ref='5184837266' />\n    <nd ref='708533582' />\n    <tag k='barrier' v='fence' />\n    <tag k='leisure' v='dog_park' />\n    <tag k='name' v='Mission Bay Dog Park' />\n    <tag k='opening_hours' v='07:00-22:00' />\n  </way>\n  <way id='56645386' timestamp='2018-04-13T21:53:10Z' uid='53073' user='Aaron Lidman' version='3' changeset='58077286'>\n    <nd ref='708463788' />\n    <nd ref='708463792' />\n    <nd ref='708463833' />\n    <nd ref='708463838' />\n    <nd ref='708463842' />\n    <nd ref='708463846' />\n    <nd ref='708463852' />\n    <nd ref='708463869' />\n    <nd ref='708463788' />\n    <tag k='building' v='yes' />\n    <tag k='name' v='Channel Westwater Pump Station' />\n    <tag k='type' v='pumping_station' />\n  </way>\n  <way id='56656116' timestamp='2018-04-13T21:53:11Z' uid='53073' user='Aaron Lidman' version='5' changeset='58077286'>\n    <nd ref='590638123' />\n    <nd ref='5184540023' />\n    <nd ref='5184540024' />\n    <nd ref='708533587' />\n    <nd ref='708533561' />\n    <nd ref='590638153' />\n    <nd ref='590638148' />\n    <nd ref='590986685' />\n    <nd ref='590986687' />\n    <nd ref='590986689' />\n    <nd ref='2922227248' />\n    <nd ref='590637975' />\n    <nd ref='590637977' />\n    <nd ref='2922027488' />\n    <nd ref='2922027486' />\n    <nd ref='2922027489' />\n    <nd ref='2922027500' />\n    <nd ref='2922028312' />\n    <nd ref='2922028340' />\n    <nd ref='1723639090' />\n    <nd ref='2450032794' />\n    <nd ref='1723639059' />\n    <nd ref='1723639055' />\n    <nd ref='1723639091' />\n    <nd ref='303785511' />\n    <nd ref='1723639081' />\n    <nd ref='303785455' />\n    <nd ref='5184288019' />\n    <nd ref='5184288020' />\n    <nd ref='5184288021' />\n    <nd ref='5184288022' />\n    <nd ref='5184288023' />\n    <nd ref='5184288024' />\n    <nd ref='5184288026' />\n    <nd ref='5184288025' />\n    <nd ref='303785350' />\n    <nd ref='5184349813' />\n    <nd ref='708533568' />\n    <nd ref='303785344' />\n    <nd ref='5184349820' />\n    <nd ref='708533549' />\n    <nd ref='31860538' />\n    <nd ref='1709638637' />\n    <nd ref='1709638635' />\n    <nd ref='1709638634' />\n    <nd ref='1709638633' />\n    <nd ref='5551421960' />\n    <nd ref='708463547' />\n    <nd ref='5184349771' />\n    <nd ref='708463545' />\n    <nd ref='708463543' />\n    <nd ref='708463542' />\n    <nd ref='708463541' />\n    <nd ref='708463538' />\n    <nd ref='708533582' />\n    <nd ref='5184349832' />\n    <nd ref='590638118' />\n    <nd ref='590638123' />\n    <tag k='leisure' v='park' />\n    <tag k='name' v='Mission Creek Park' />\n  </way>\n  <way id='84823308' timestamp='2018-03-26T19:01:32Z' uid='53073' user='Aaron Lidman' version='15' changeset='57545421'>\n    <nd ref='2304626301' />\n    <nd ref='2304626337' />\n    <nd ref='2304626330' />\n    <nd ref='4925013317' />\n    <nd ref='3532718885' />\n    <nd ref='65303721' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lcn_ref' v='23;36' />\n    <tag k='name' v='Townsend Street' />\n    <tag k='oneway' v='no' />\n    <tag k='sidewalk' v='both' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Townsend' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='110353426' timestamp='2018-11-05T17:46:50Z' uid='119881' user='clay_c' version='14' changeset='64205910'>\n    <nd ref='30676466' />\n    <nd ref='306735822' />\n    <nd ref='306735671' />\n    <nd ref='30676467' />\n    <nd ref='306735672' />\n    <nd ref='30676468' />\n    <nd ref='306735684' />\n    <nd ref='30676469' />\n    <nd ref='306735795' />\n    <nd ref='262828279' />\n    <nd ref='768243466' />\n    <nd ref='306735889' />\n    <tag k='electrified' v='no' />\n    <tag k='gauge' v='1435' />\n    <tag k='history' v='Retrieved from v10' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='maxspeed:freight' v='10 mph' />\n    <tag k='name' v='Peninsula Subdivision Main 1' />\n    <tag k='owner' v='Peninsula Corridor Joint Powers Board' />\n    <tag k='railway' v='rail' />\n    <tag k='railway:track_ref' v='1' />\n    <tag k='railway:traffic_mode' v='passenger' />\n    <tag k='tiger:cfcc' v='B21' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Union Pacific Railroad' />\n    <tag k='usage' v='main' />\n  </way>\n  <way id='110800913' timestamp='2018-03-26T19:01:30Z' uid='53073' user='Aaron Lidman' version='8' changeset='57545421'>\n    <nd ref='65320166' />\n    <nd ref='5438253573' />\n    <nd ref='65320168' />\n    <nd ref='1677798439' />\n    <nd ref='5438253570' />\n    <nd ref='65320171' />\n    <nd ref='65320179' />\n    <tag k='highway' v='residential' />\n    <tag k='lanes' v='2' />\n    <tag k='name' v='De Haro Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='119237698' timestamp='2017-07-19T10:49:05Z' uid='3479270' user='nammala' version='12' changeset='50401651'>\n    <nd ref='733619113' />\n    <nd ref='2315555372' />\n    <nd ref='733619123' />\n    <nd ref='314079729' />\n    <nd ref='65374259' />\n    <nd ref='733637405' />\n    <nd ref='295220168' />\n    <nd ref='314079678' />\n    <nd ref='733637423' />\n    <nd ref='65374260' />\n    <nd ref='733637435' />\n    <nd ref='65374264' />\n    <nd ref='733637444' />\n    <nd ref='65374267' />\n    <nd ref='733637451' />\n    <nd ref='65374268' />\n    <nd ref='733638758' />\n    <nd ref='65286490' />\n    <nd ref='733638760' />\n    <nd ref='65374269' />\n    <nd ref='733638763' />\n    <nd ref='733638765' />\n    <nd ref='733638767' />\n    <nd ref='65374270' />\n    <nd ref='299388079' />\n    <nd ref='65374273' />\n    <nd ref='733638792' />\n    <nd ref='65374276' />\n    <nd ref='733638793' />\n    <nd ref='314079692' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='San Jose' />\n    <tag k='destination:ref' v='US 101 South' />\n    <tag k='highway' v='motorway' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='1' />\n    <tag k='maxspeed' v='50 mph' />\n    <tag k='name' v='Central Freeway' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='US 101' />\n    <tag k='tiger:cfcc' v='A25' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='United States Highway 101' />\n    <tag k='tiger:name_base_1' v='Central' />\n    <tag k='tiger:name_type_1' v='Fwy' />\n  </way>\n  <way id='119709582' timestamp='2018-11-05T17:47:11Z' uid='119881' user='clay_c' version='11' changeset='64205910'>\n    <nd ref='1344032323' />\n    <nd ref='6024177038' />\n    <nd ref='1344032615' />\n    <nd ref='1344032280' />\n    <nd ref='1344032579' />\n    <nd ref='1344032365' />\n    <nd ref='1344032657' />\n    <nd ref='1344032445' />\n    <nd ref='1344032218' />\n    <nd ref='1344032526' />\n    <nd ref='1344032305' />\n    <nd ref='1344032489' />\n    <nd ref='1344032260' />\n    <tag k='electrified' v='no' />\n    <tag k='gauge' v='1435' />\n    <tag k='history' v='Retrieved from v10' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='maxspeed:freight' v='10 mph' />\n    <tag k='name' v='Peninsula Subdivision Main 3' />\n    <tag k='owner' v='Peninsula Corridor Joint Powers Board' />\n    <tag k='railway' v='rail' />\n    <tag k='railway:track_ref' v='3' />\n    <tag k='railway:traffic_mode' v='passenger' />\n    <tag k='tiger:cfcc' v='B21' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Union Pacific Railroad' />\n    <tag k='usage' v='main' />\n  </way>\n  <way id='119709592' timestamp='2018-11-05T17:47:12Z' uid='119881' user='clay_c' version='5' changeset='64205910'>\n    <nd ref='1344032476' />\n    <nd ref='1344032561' />\n    <nd ref='1344032625' />\n    <nd ref='1344032709' />\n    <nd ref='1344032672' />\n    <nd ref='1344032231' />\n    <nd ref='1344032313' />\n    <nd ref='1344032401' />\n    <nd ref='1344032357' />\n    <tag k='electrified' v='no' />\n    <tag k='gauge' v='1435' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='maxspeed:freight' v='10 mph' />\n    <tag k='name' v='Peninsula Subdivision Main 2' />\n    <tag k='railway' v='rail' />\n    <tag k='railway:track_ref' v='2' />\n    <tag k='railway:traffic_mode' v='passenger' />\n    <tag k='usage' v='main' />\n  </way>\n  <way id='132813867' timestamp='2011-10-10T13:33:09Z' uid='93285' user='skela' version='1' changeset='9521974'>\n    <nd ref='1461189674' />\n    <nd ref='1461189670' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='driveway' />\n    <tag k='source' v='survey' />\n    <tag k='surface' v='paved' />\n  </way>\n  <way id='132813868' timestamp='2011-10-10T13:33:09Z' uid='93285' user='skela' version='1' changeset='9521974'>\n    <nd ref='1461189690' />\n    <nd ref='1461189685' />\n    <nd ref='1461189674' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='driveway' />\n    <tag k='source' v='survey' />\n    <tag k='surface' v='paved' />\n    <tag k='tunnel' v='yes' />\n  </way>\n  <way id='132813869' timestamp='2011-10-10T13:33:09Z' uid='93285' user='skela' version='1' changeset='9521974'>\n    <nd ref='1461189692' />\n    <nd ref='1461189689' />\n    <nd ref='1461189677' />\n    <nd ref='1461189671' />\n    <nd ref='1461189676' />\n    <nd ref='1461189673' />\n    <nd ref='1461189663' />\n    <nd ref='1461189679' />\n    <nd ref='1461189692' />\n    <tag k='building' v='yes' />\n    <tag k='building:levels' v='2' />\n    <tag k='shop' v='mall' />\n    <tag k='source' v='Bing' />\n  </way>\n  <way id='132813870' timestamp='2013-11-24T02:30:37Z' uid='371121' user='AndrewSnow' version='2' changeset='19084727'>\n    <nd ref='1461189673' />\n    <nd ref='1461189674' />\n    <nd ref='1461189675' />\n    <nd ref='1461189690' />\n    <nd ref='1461189687' />\n    <nd ref='1461189689' />\n    <nd ref='1461189677' />\n    <nd ref='1461189671' />\n    <nd ref='1461189676' />\n    <nd ref='1461189673' />\n    <tag k='access' v='customers' />\n    <tag k='amenity' v='parking' />\n    <tag k='capacity:disabled' v='yes' />\n    <tag k='fee' v='no' />\n    <tag k='parking' v='multi-storey' />\n    <tag k='source' v='survey' />\n  </way>\n  <way id='133735125' timestamp='2018-03-26T19:01:28Z' uid='53073' user='Aaron Lidman' version='8' changeset='57545421'>\n    <nd ref='300444895' />\n    <nd ref='2304626310' />\n    <nd ref='2304626329' />\n    <nd ref='2304626325' />\n    <nd ref='2274244022' />\n    <nd ref='2274244029' />\n    <nd ref='2304626355' />\n    <tag k='highway' v='tertiary' />\n    <tag k='name' v='Division Street' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='135243873' timestamp='2018-03-18T22:12:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57302156'>\n    <nd ref='5487354231' />\n    <nd ref='1485472609' />\n    <tag k='foot' v='yes' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='148018328' timestamp='2015-06-17T17:40:11Z' uid='445671' user='flierfy' version='2' changeset='32034365'>\n    <nd ref='1344032394' />\n    <nd ref='1344032471' />\n    <nd ref='1344032557' />\n    <nd ref='1344032462' />\n    <nd ref='1344032324' />\n    <nd ref='1344032404' />\n    <nd ref='1344032485' />\n    <nd ref='1344032449' />\n    <nd ref='1344032530' />\n    <nd ref='1344032592' />\n    <nd ref='1344032673' />\n    <nd ref='1344032639' />\n    <nd ref='1344032198' />\n    <nd ref='1344032282' />\n    <nd ref='1344032367' />\n    <nd ref='1344032326' />\n    <nd ref='1344032411' />\n    <nd ref='1344032490' />\n    <tag k='electrified' v='no' />\n    <tag k='gauge' v='1435' />\n    <tag k='railway' v='rail' />\n    <tag k='railway:traffic_mode' v='passenger' />\n    <tag k='service' v='siding' />\n  </way>\n  <way id='154966358' timestamp='2017-07-21T19:01:04Z' uid='2115749' user='srividya_c' version='12' changeset='50466458'>\n    <nd ref='65371530' />\n    <nd ref='2315558266' />\n    <nd ref='2315558269' />\n    <nd ref='2315558273' />\n    <nd ref='1674115436' />\n    <nd ref='2315558265' />\n    <nd ref='2315558267' />\n    <nd ref='2315558271' />\n    <nd ref='2315558270' />\n    <nd ref='2315558272' />\n    <nd ref='1674115433' />\n    <nd ref='2315558268' />\n    <nd ref='2315558274' />\n    <nd ref='1674115432' />\n    <nd ref='1674115435' />\n    <nd ref='65371540' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='San Jose' />\n    <tag k='destination:ref' v='US 101 South' />\n    <tag k='hgv' v='designated' />\n    <tag k='hgv:national_network' v='yes' />\n    <tag k='highway' v='motorway' />\n    <tag k='junction:ref' v='1A' />\n    <tag k='lanes' v='3' />\n    <tag k='layer' v='1' />\n    <tag k='maxspeed' v='45 mph' />\n    <tag k='name' v='James Lick Freeway' />\n    <tag k='note' v='not officially part of the Interstate Highway System (west of unbuilt I-280)' />\n    <tag k='oneway' v='yes' />\n    <tag k='source:hgv:national_network' v='Title 23: Highways Part 658 http://ecfr.gpoaccess.gov/cgi/t/text/text-idx?c=ecfr&amp;rgn=div5&amp;view=text&amp;node=23:1.0.1.7.33&amp;idno=23' />\n  </way>\n  <way id='162931810' timestamp='2018-02-25T15:05:18Z' uid='5288591' user='Irina Karachevtseva' version='9' changeset='56663149'>\n    <nd ref='65315504' />\n    <nd ref='4880860063' />\n    <nd ref='5435465932' />\n    <nd ref='65283737' />\n    <tag k='highway' v='primary' />\n    <tag k='lanes' v='4' />\n    <tag k='name' v='9th Street' />\n    <tag k='oneway' v='yes' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='9th' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='170183646' timestamp='2018-01-24T04:31:55Z' uid='3817650' user='bdon_import' version='5' changeset='55702219'>\n    <nd ref='1813413823' />\n    <nd ref='3570918496' />\n    <nd ref='1813413851' />\n    <nd ref='1813413829' />\n    <nd ref='1813413838' />\n    <nd ref='1813413823' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='12' />\n    <tag k='name' v='B' />\n  </way>\n  <way id='170183647' timestamp='2018-01-22T16:59:29Z' uid='3817650' user='bdon_import' version='5' changeset='55660512'>\n    <nd ref='1813413826' />\n    <nd ref='1813413833' />\n    <nd ref='1813413824' />\n    <nd ref='3570918497' />\n    <nd ref='3570918498' />\n    <nd ref='1813413846' />\n    <nd ref='1813413826' />\n    <tag k='addr:housenumber' v='655' />\n    <tag k='addr:street' v='Townsend Street' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='15' />\n    <tag k='name' v='A' />\n  </way>\n  <way id='170183648' timestamp='2018-01-22T16:57:32Z' uid='3817650' user='bdon_import' version='7' changeset='55660452'>\n    <nd ref='1813413813' />\n    <nd ref='3577623863' />\n    <nd ref='1813413825' />\n    <nd ref='1813413841' />\n    <nd ref='3570918504' />\n    <nd ref='3570918501' />\n    <nd ref='1813413849' />\n    <nd ref='1813413814' />\n    <nd ref='1813413821' />\n    <nd ref='1813413855' />\n    <nd ref='1813413839' />\n    <nd ref='1813413845' />\n    <nd ref='1813413834' />\n    <nd ref='1813413828' />\n    <nd ref='3577623862' />\n    <nd ref='1813413837' />\n    <nd ref='1813413813' />\n    <tag k='addr:housenumber' v='650' />\n    <tag k='addr:street' v='Townsend Street' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='20' />\n    <tag k='name' v='Zynga' />\n  </way>\n  <way id='170183649' timestamp='2018-01-24T04:31:55Z' uid='3817650' user='bdon_import' version='5' changeset='55702219'>\n    <nd ref='1813413820' />\n    <nd ref='1813413815' />\n    <nd ref='3607930413' />\n    <nd ref='3607930412' />\n    <nd ref='3607930411' />\n    <nd ref='3607930410' />\n    <nd ref='1813413816' />\n    <nd ref='2274243956' />\n    <nd ref='1813413848' />\n    <nd ref='1813413820' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='18' />\n  </way>\n  <way id='170183650' timestamp='2018-01-24T04:31:55Z' uid='3817650' user='bdon_import' version='4' changeset='55702219'>\n    <nd ref='1813413835' />\n    <nd ref='1813413827' />\n    <nd ref='1813413817' />\n    <nd ref='1813413847' />\n    <nd ref='1813413843' />\n    <nd ref='1813413830' />\n    <nd ref='1813413852' />\n    <nd ref='1813413819' />\n    <nd ref='1813413850' />\n    <nd ref='1813413853' />\n    <nd ref='1813413836' />\n    <nd ref='1813413840' />\n    <nd ref='1813413831' />\n    <nd ref='1813413854' />\n    <nd ref='1813413844' />\n    <nd ref='1813413832' />\n    <nd ref='1813413842' />\n    <nd ref='1813413835' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='11' />\n    <tag k='name' v='C' />\n  </way>\n  <way id='179235236' timestamp='2018-08-18T23:51:36Z' uid='119881' user='clay_c' version='6' changeset='61782308'>\n    <nd ref='295221650' />\n    <nd ref='733628924' />\n    <nd ref='65283756' />\n    <nd ref='257670318' />\n    <nd ref='314078983' />\n    <nd ref='733622843' />\n    <nd ref='314078984' />\n    <nd ref='295221752' />\n    <nd ref='733627034' />\n    <nd ref='65283790' />\n    <nd ref='733626661' />\n    <nd ref='295221767' />\n    <nd ref='733626665' />\n    <nd ref='65283798' />\n    <nd ref='733626671' />\n    <nd ref='300757630' />\n    <nd ref='733626677' />\n    <nd ref='300757631' />\n    <nd ref='733625764' />\n    <nd ref='65283800' />\n    <nd ref='733625766' />\n    <nd ref='65283804' />\n    <nd ref='733625769' />\n    <nd ref='314079060' />\n    <nd ref='314079061' />\n    <nd ref='65283808' />\n    <nd ref='733624873' />\n    <nd ref='298081840' />\n    <nd ref='733624877' />\n    <nd ref='733624608' />\n    <nd ref='733624612' />\n    <nd ref='300757632' />\n    <nd ref='314079066' />\n    <nd ref='314079203' />\n    <nd ref='318260226' />\n    <nd ref='318260227' />\n    <nd ref='733624629' />\n    <nd ref='733624631' />\n    <nd ref='308712276' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='Bay Bridge;Oakland' />\n    <tag k='destination:ref' v='I 80 East' />\n    <tag k='highway' v='motorway_link' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='3' />\n    <tag k='oneway' v='yes' />\n    <tag k='tiger:cfcc' v='A63' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:reviewed' v='no' />\n  </way>\n  <way id='184540572' timestamp='2018-06-01T01:18:12Z' uid='53073' user='Aaron Lidman' version='9' changeset='59447024'>\n    <nd ref='1950347613' />\n    <nd ref='2156202859' />\n    <nd ref='1950347608' />\n    <nd ref='1950347610' />\n    <nd ref='2156202622' />\n    <nd ref='2249911776' />\n    <nd ref='1941018589' />\n    <nd ref='1950347614' />\n    <nd ref='1950347615' />\n    <nd ref='1950347611' />\n    <nd ref='5449466843' />\n    <nd ref='2156202808' />\n    <nd ref='1950347617' />\n    <nd ref='480642608' />\n    <nd ref='1950347618' />\n    <nd ref='1950347609' />\n    <nd ref='480642607' />\n    <nd ref='5090306511' />\n    <nd ref='480642606' />\n    <nd ref='5090306509' />\n    <nd ref='2156202856' />\n    <nd ref='480642605' />\n    <nd ref='1950347616' />\n    <nd ref='2156202707' />\n    <nd ref='2156202740' />\n    <nd ref='2156202823' />\n    <nd ref='2156202832' />\n    <nd ref='2156202873' />\n    <nd ref='1950347612' />\n    <nd ref='1950347613' />\n    <tag k='alt_name' v='CCA' />\n    <tag k='amenity' v='university' />\n    <tag k='name' v='California College of the Arts' />\n    <tag k='old_name' v='California College of Arts and Crafts;CCAC' />\n    <tag k='wikidata' v='Q5020382' />\n  </way>\n  <way id='186398845' timestamp='2018-08-28T21:51:08Z' uid='8107451' user='njtbusfan' version='7' changeset='62086023'>\n    <nd ref='65340076' />\n    <nd ref='5438253584' />\n    <nd ref='65282185' />\n    <nd ref='65340073' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='Vermont Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='205644329' timestamp='2018-09-19T00:38:21Z' uid='53073' user='Aaron Lidman' version='5' changeset='62713402'>\n    <nd ref='2156202749' />\n    <nd ref='2156202901' />\n    <nd ref='2156202578' />\n    <nd ref='2156202808' />\n    <nd ref='2156202815' />\n    <nd ref='5914927381' />\n    <nd ref='2156202811' />\n    <nd ref='2156202688' />\n    <nd ref='2156202672' />\n    <nd ref='2156202572' />\n    <nd ref='2156202770' />\n    <nd ref='2156202674' />\n    <nd ref='2156202749' />\n  </way>\n  <way id='211118823' timestamp='2018-02-15T09:41:51Z' uid='364' user='Edward' version='8' changeset='56378669'>\n    <nd ref='2211243718' />\n    <nd ref='2211243720' />\n    <nd ref='2211243719' />\n    <nd ref='2211243717' />\n    <nd ref='2211243718' />\n    <tag k='addr:housenumber' v='601' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:street' v='Townsend Street' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='17' />\n    <tag k='name' v='Adobe Systems' />\n    <tag k='office' v='company' />\n    <tag k='old_name' v='Baker &amp; Hamilton' />\n    <tag k='wikidata' v='Q19865448' />\n  </way>\n  <way id='215524529' timestamp='2014-09-15T22:13:50Z' uid='28775' user='StellanL' version='2' changeset='25467657'>\n    <nd ref='984720636' />\n    <nd ref='423775499' />\n    <nd ref='423775500' />\n    <nd ref='423775501' />\n    <nd ref='768243534' />\n    <nd ref='768243535' />\n    <nd ref='423775502' />\n    <nd ref='768243536' />\n    <nd ref='258877153' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='Berry Street' />\n    <tag k='sidewalk' v='right' />\n  </way>\n  <way id='215528940' timestamp='2017-08-10T21:05:49Z' uid='2508151' user='ridixcr' version='3' changeset='51015043'>\n    <nd ref='984720636' />\n    <nd ref='1344032342' />\n    <nd ref='984720713' />\n    <nd ref='1344032359' />\n    <nd ref='984720787' />\n    <nd ref='65303732' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='3' />\n    <tag k='name' v='Mission Bay Drive' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='none' />\n    <tag k='turn:lanes' v='left|right|right' />\n  </way>\n  <way id='215531272' timestamp='2013-04-05T08:09:19Z' uid='933797' user='oba510' version='1' changeset='15616228'>\n    <nd ref='2249911764' />\n    <nd ref='2249911788' />\n    <nd ref='2249911804' />\n    <nd ref='2249911801' />\n    <nd ref='2249911759' />\n    <nd ref='2249911791' />\n    <nd ref='2249911764' />\n    <tag k='landuse' v='industrial' />\n  </way>\n  <way id='215531274' timestamp='2013-04-05T08:09:20Z' uid='933797' user='oba510' version='1' changeset='15616228'>\n    <nd ref='2249911776' />\n    <nd ref='2249911806' />\n    <nd ref='2249911793' />\n    <nd ref='2249911795' />\n    <nd ref='2249911751' />\n    <nd ref='2249911813' />\n    <nd ref='2249911762' />\n    <nd ref='1950347613' />\n    <nd ref='2156202859' />\n    <nd ref='1950347608' />\n    <nd ref='1950347610' />\n    <nd ref='2156202622' />\n    <nd ref='2249911776' />\n    <tag k='landuse' v='industrial' />\n  </way>\n  <way id='215531275' timestamp='2018-06-01T01:18:12Z' uid='53073' user='Aaron Lidman' version='6' changeset='59447024'>\n    <nd ref='5449466841' />\n    <nd ref='5449466842' />\n    <nd ref='2156202808' />\n    <nd ref='5449466843' />\n    <nd ref='1950347611' />\n    <nd ref='5449466845' />\n    <nd ref='5449466841' />\n    <tag k='landuse' v='commercial' />\n    <tag k='name' v='100 Hooper' />\n    <tag k='website' v='http://www.100hooper.com/' />\n  </way>\n  <way id='218146840' timestamp='2018-03-26T19:01:28Z' uid='53073' user='Aaron Lidman' version='4' changeset='57545421'>\n    <nd ref='300444895' />\n    <nd ref='65296271' />\n    <tag k='highway' v='tertiary' />\n    <tag k='name' v='Division Street' />\n    <tag k='sidewalk' v='both' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='218146842' timestamp='2015-06-06T23:03:40Z' uid='371121' user='AndrewSnow' version='3' changeset='31778944'>\n    <nd ref='2274244004' />\n    <nd ref='2274243999' />\n    <nd ref='2274243987' />\n    <nd ref='2274243988' />\n    <nd ref='2274243983' />\n    <nd ref='2274243984' />\n    <nd ref='2274244026' />\n    <nd ref='2274244028' />\n    <nd ref='2274244006' />\n    <nd ref='2274244034' />\n    <nd ref='2274244023' />\n    <nd ref='2274244004' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='552' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Berry Street' />\n    <tag k='building' v='yes' />\n    <tag k='name' v='San Francisco Gravel Company' />\n  </way>\n  <way id='218146843' timestamp='2018-01-24T04:31:56Z' uid='3817650' user='bdon_import' version='4' changeset='55702219'>\n    <nd ref='2274244035' />\n    <nd ref='2274244012' />\n    <nd ref='2274243997' />\n    <nd ref='2274243998' />\n    <nd ref='2274244003' />\n    <nd ref='2274244009' />\n    <nd ref='2274244002' />\n    <nd ref='2274244014' />\n    <nd ref='2274244047' />\n    <nd ref='2274244031' />\n    <nd ref='2274244035' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='5' />\n  </way>\n  <way id='218146845' timestamp='2018-01-24T04:31:56Z' uid='3817650' user='bdon_import' version='5' changeset='55702219'>\n    <nd ref='2274244051' />\n    <nd ref='3577621771' />\n    <nd ref='3577621772' />\n    <nd ref='3577621770' />\n    <nd ref='3577621769' />\n    <nd ref='2274244048' />\n    <nd ref='2274244046' />\n    <nd ref='2274244045' />\n    <nd ref='3577621765' />\n    <nd ref='3577621766' />\n    <nd ref='3577621768' />\n    <nd ref='3577621767' />\n    <nd ref='2274244044' />\n    <nd ref='2274244039' />\n    <nd ref='2274244072' />\n    <nd ref='2274244077' />\n    <nd ref='4925013311' />\n    <nd ref='2274244054' />\n    <nd ref='3577621773' />\n    <nd ref='2274244055' />\n    <nd ref='2274244059' />\n    <nd ref='2274244064' />\n    <nd ref='2274244051' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='13' />\n    <tag k='name' v='D' />\n  </way>\n  <way id='218146846' timestamp='2015-06-20T21:17:13Z' uid='371121' user='AndrewSnow' version='5' changeset='32105235'>\n    <nd ref='2274243872' />\n    <nd ref='2274243963' />\n    <nd ref='2274243964' />\n    <nd ref='2274243961' />\n    <nd ref='2274243962' />\n    <nd ref='2274243877' />\n    <nd ref='2274243872' />\n    <tag k='building' v='yes' />\n    <tag k='name' v='Showplace East' />\n  </way>\n  <way id='218146848' timestamp='2015-06-26T08:45:13Z' uid='33757' user='Minh Nguyen' version='3' changeset='32220993'>\n    <nd ref='2274244114' />\n    <nd ref='2274244116' />\n    <nd ref='3619170069' />\n    <nd ref='2274244100' />\n    <nd ref='2274244085' />\n    <nd ref='3619170070' />\n    <nd ref='2274244114' />\n    <tag k='building' v='yes' />\n    <tag k='name' v='Adobe Systems' />\n    <tag k='office' v='company' />\n  </way>\n  <way id='218146849' timestamp='2018-01-24T04:31:56Z' uid='3817650' user='bdon_import' version='3' changeset='55702219'>\n    <nd ref='2274243993' />\n    <nd ref='2274244005' />\n    <nd ref='2274243996' />\n    <nd ref='2274243981' />\n    <nd ref='2274243993' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='8' />\n  </way>\n  <way id='218146852' timestamp='2018-01-24T04:31:56Z' uid='3817650' user='bdon_import' version='3' changeset='55702219'>\n    <nd ref='2274243967' />\n    <nd ref='2274243968' />\n    <nd ref='2274243955' />\n    <nd ref='2274243954' />\n    <nd ref='2274243967' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='7' />\n  </way>\n  <way id='218146855' timestamp='2018-01-24T04:31:56Z' uid='3817650' user='bdon_import' version='3' changeset='55702219'>\n    <nd ref='2274243965' />\n    <nd ref='2274243966' />\n    <nd ref='2274243948' />\n    <nd ref='2274243947' />\n    <nd ref='2274243965' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='7' />\n  </way>\n  <way id='218146858' timestamp='2018-01-24T04:31:57Z' uid='3817650' user='bdon_import' version='3' changeset='55702219'>\n    <nd ref='2274244089' />\n    <nd ref='2274244101' />\n    <nd ref='2274244090' />\n    <nd ref='2274244082' />\n    <nd ref='2274244089' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='15' />\n  </way>\n  <way id='218146859' timestamp='2018-01-24T04:31:57Z' uid='3817650' user='bdon_import' version='4' changeset='55702219'>\n    <nd ref='2274243976' />\n    <nd ref='2274243972' />\n    <nd ref='2274243971' />\n    <nd ref='2274243973' />\n    <nd ref='2274243976' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='99' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Rhode Island Street' />\n    <tag k='building' v='yes' />\n    <tag k='building:levels' v='3' />\n    <tag k='height' v='14' />\n  </way>\n  <way id='218146860' timestamp='2018-03-29T20:50:46Z' uid='53073' user='Aaron Lidman' version='5' changeset='57645807'>\n    <nd ref='2274244074' />\n    <nd ref='2274244079' />\n    <nd ref='2274244098' />\n    <nd ref='2274244094' />\n    <nd ref='2274244097' />\n    <nd ref='2274244105' />\n    <nd ref='2274244111' />\n    <nd ref='2274244109' />\n    <nd ref='2274244112' />\n    <nd ref='2274244113' />\n    <nd ref='2274244115' />\n    <nd ref='2274244106' />\n    <nd ref='2274244103' />\n    <nd ref='2274244099' />\n    <nd ref='2274244102' />\n    <nd ref='2274244095' />\n    <nd ref='2274244092' />\n    <nd ref='2274244088' />\n    <nd ref='2274244087' />\n    <nd ref='2274244110' />\n    <nd ref='2274244108' />\n    <nd ref='2274244081' />\n    <nd ref='2274244075' />\n    <nd ref='2274244107' />\n    <nd ref='2274244104' />\n    <nd ref='2274244076' />\n    <nd ref='2274244067' />\n    <nd ref='2274244096' />\n    <nd ref='2274244091' />\n    <nd ref='2274244069' />\n    <nd ref='2274244057' />\n    <nd ref='5515121099' />\n    <nd ref='2274244086' />\n    <nd ref='2274244083' />\n    <nd ref='2274244070' />\n    <nd ref='2274244058' />\n    <nd ref='5515121098' />\n    <nd ref='2274244080' />\n    <nd ref='2274244073' />\n    <nd ref='2274244068' />\n    <nd ref='2274244062' />\n    <nd ref='2274244065' />\n    <nd ref='2274244061' />\n    <nd ref='2274244052' />\n    <nd ref='2274244049' />\n    <nd ref='2274244056' />\n    <nd ref='2274244050' />\n    <nd ref='4072351916' />\n    <nd ref='2274244053' />\n    <nd ref='4072351915' />\n    <nd ref='2274244060' />\n    <nd ref='4072351914' />\n    <nd ref='4072351913' />\n    <nd ref='2274244066' />\n    <nd ref='2274244071' />\n    <nd ref='2274244063' />\n    <nd ref='2274244074' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='19' />\n  </way>\n  <way id='218146862' timestamp='2018-01-24T04:31:57Z' uid='3817650' user='bdon_import' version='6' changeset='55702219'>\n    <nd ref='2274243986' />\n    <nd ref='2274243985' />\n    <nd ref='2274244008' />\n    <nd ref='2274244011' />\n    <nd ref='2274243989' />\n    <nd ref='2274243986' />\n    <tag k='building' v='yes' />\n    <tag k='building:levels' v='2' />\n    <tag k='height' v='6' />\n  </way>\n  <way id='218146865' timestamp='2018-03-26T19:01:28Z' uid='53073' user='Aaron Lidman' version='8' changeset='57545421'>\n    <nd ref='2304626359' />\n    <nd ref='4941620836' />\n    <nd ref='2274244013' />\n    <nd ref='3532718886' />\n    <nd ref='4941620833' />\n    <nd ref='2304626316' />\n    <nd ref='2304626309' />\n    <nd ref='2304626306' />\n    <nd ref='300444895' />\n    <tag k='highway' v='tertiary' />\n    <tag k='name' v='Division Street' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n  </way>\n  <way id='219783480' timestamp='2018-01-22T16:57:35Z' uid='3817650' user='bdon_import' version='4' changeset='55660452'>\n    <nd ref='2289187464' />\n    <nd ref='2289187496' />\n    <nd ref='2289187429' />\n    <nd ref='2289187412' />\n    <nd ref='2289187400' />\n    <nd ref='2289187390' />\n    <nd ref='2289187385' />\n    <nd ref='2289187464' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='10' />\n    <tag k='source' v='Bing' />\n  </way>\n  <way id='219783490' timestamp='2018-01-22T20:21:13Z' uid='3817650' user='bdon_import' version='6' changeset='55665623'>\n    <nd ref='2289187353' />\n    <nd ref='2289187360' />\n    <nd ref='2289187362' />\n    <nd ref='2289187363' />\n    <nd ref='2289187357' />\n    <nd ref='2289187337' />\n    <nd ref='2289187335' />\n    <nd ref='2289187334' />\n    <nd ref='2289187324' />\n    <nd ref='2289187323' />\n    <nd ref='2289187320' />\n    <nd ref='2289187326' />\n    <nd ref='2289187328' />\n    <nd ref='2289187330' />\n    <nd ref='2289187332' />\n    <nd ref='2289187341' />\n    <nd ref='2289187350' />\n    <nd ref='2289187353' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='999' />\n    <tag k='addr:street' v='Brannan Street' />\n    <tag k='building' v='commercial' />\n    <tag k='height' v='13' />\n    <tag k='office' v='company' />\n    <tag k='old_name' v='Dolby Laboratories' />\n    <tag k='parking' v='multi-storey' />\n  </way>\n  <way id='221434099' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='9' changeset='63557245'>\n    <nd ref='2304626334' />\n    <nd ref='3532718887' />\n    <nd ref='3985226613' />\n    <tag k='cycleway:right' v='lane' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lcn_ref' v='36' />\n    <tag k='name' v='Division Street' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='221434100' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='7' changeset='63557245'>\n    <nd ref='2304626360' />\n    <nd ref='3532718888' />\n    <nd ref='2304626334' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lcn_ref' v='36' />\n    <tag k='name' v='Division Street' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='221434102' timestamp='2018-08-28T21:51:08Z' uid='8107451' user='njtbusfan' version='8' changeset='62086023'>\n    <nd ref='65340073' />\n    <nd ref='65283553' />\n    <nd ref='5438253591' />\n    <nd ref='3532719193' />\n    <nd ref='3532718892' />\n    <nd ref='65310189' />\n    <nd ref='3532718891' />\n    <nd ref='3532718890' />\n    <nd ref='65339116' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lcn_ref' v='36' />\n    <tag k='name' v='Division Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='221434104' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='8' changeset='63557245'>\n    <nd ref='3985226614' />\n    <nd ref='2304626358' />\n    <nd ref='2304626359' />\n    <nd ref='2304626361' />\n    <nd ref='2304626299' />\n    <nd ref='2304626322' />\n    <nd ref='2304626356' />\n    <nd ref='2304626286' />\n    <nd ref='2304626321' />\n    <nd ref='2304626313' />\n    <nd ref='2304626355' />\n    <nd ref='2304626307' />\n    <nd ref='2304626319' />\n    <nd ref='2304626301' />\n    <nd ref='2304626342' />\n    <nd ref='2304626336' />\n    <nd ref='2304626340' />\n    <nd ref='2304626288' />\n    <nd ref='2304626363' />\n    <nd ref='2304626360' />\n    <nd ref='2304626295' />\n    <nd ref='2304626298' />\n    <nd ref='2304626292' />\n    <nd ref='2304626311' />\n    <nd ref='2304626352' />\n    <nd ref='2304626308' />\n    <nd ref='2304626287' />\n    <nd ref='3985226613' />\n    <nd ref='3985226614' />\n    <tag k='cycleway' v='shared_lane' />\n    <tag k='highway' v='tertiary' />\n    <tag k='junction' v='roundabout' />\n    <tag k='sidewalk' v='right' />\n  </way>\n  <way id='221434106' timestamp='2013-05-15T06:56:06Z' uid='14293' user='KindredCoda' version='1' changeset='16134749'>\n    <nd ref='2304626289' />\n    <nd ref='2304626323' />\n    <nd ref='2304626304' />\n    <nd ref='2304626347' />\n    <nd ref='2304626349' />\n    <nd ref='2304626345' />\n    <nd ref='2304626285' />\n    <nd ref='2304626302' />\n    <nd ref='2304626303' />\n    <nd ref='2304626300' />\n    <nd ref='2304626296' />\n    <nd ref='2304626333' />\n    <nd ref='2304626305' />\n    <nd ref='2304626314' />\n    <nd ref='2304626294' />\n    <nd ref='2304626357' />\n    <nd ref='2304626318' />\n    <nd ref='2304626346' />\n    <nd ref='2304626364' />\n    <nd ref='2304626324' />\n    <nd ref='2304626350' />\n    <nd ref='2304626327' />\n    <nd ref='2304626343' />\n    <nd ref='2304626317' />\n    <nd ref='2304626335' />\n    <nd ref='2304626338' />\n    <nd ref='2304626348' />\n    <nd ref='2304626289' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='224373746' timestamp='2018-03-26T19:01:27Z' uid='53073' user='Aaron Lidman' version='6' changeset='57545421'>\n    <nd ref='65308756' />\n    <nd ref='65283780' />\n    <nd ref='65345893' />\n    <nd ref='65358378' />\n    <nd ref='3532719197' />\n    <nd ref='65358380' />\n    <nd ref='65339116' />\n    <tag k='cycleway' v='track' />\n    <tag k='highway' v='secondary' />\n    <tag k='lcn_ref' v='36' />\n    <tag k='name' v='Division Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='224384015' timestamp='2018-03-26T19:01:28Z' uid='53073' user='Aaron Lidman' version='9' changeset='57545421'>\n    <nd ref='65296271' />\n    <nd ref='5487354235' />\n    <nd ref='5438253567' />\n    <nd ref='65355343' />\n    <nd ref='5487354233' />\n    <nd ref='65320166' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='2' />\n    <tag k='name' v='Division Street' />\n    <tag k='oneway' v='no' />\n    <tag k='sidewalk' v='left' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='237055031' timestamp='2018-08-22T03:06:41Z' uid='501715' user='rkuris' version='14' changeset='61874700'>\n    <nd ref='268235134' />\n    <nd ref='268235132' />\n    <nd ref='268235131' />\n    <nd ref='268235129' />\n    <nd ref='268235227' />\n    <nd ref='268235226' />\n    <nd ref='268235225' />\n    <nd ref='31844169' />\n    <nd ref='5389465206' />\n    <nd ref='5389465207' />\n    <nd ref='31857224' />\n    <nd ref='1216643467' />\n    <nd ref='5389465208' />\n    <nd ref='5389465209' />\n    <nd ref='5389465210' />\n    <nd ref='5389465211' />\n    <nd ref='5389465212' />\n    <nd ref='5389465213' />\n    <nd ref='5389465215' />\n    <nd ref='5389465216' />\n    <nd ref='31826027' />\n    <nd ref='255897294' />\n    <nd ref='5389466122' />\n    <nd ref='2613221271' />\n    <nd ref='2613221288' />\n    <nd ref='2613221266' />\n    <nd ref='300459902' />\n    <nd ref='2613221289' />\n    <nd ref='1260356835' />\n    <nd ref='2613221282' />\n    <nd ref='2249879663' />\n    <nd ref='302034098' />\n    <nd ref='2613221255' />\n    <nd ref='31862513' />\n    <nd ref='1260356832' />\n    <nd ref='1260356843' />\n    <nd ref='1260356828' />\n    <nd ref='1260356854' />\n    <nd ref='1260356841' />\n    <nd ref='1260356830' />\n    <nd ref='1260356824' />\n    <nd ref='1260356839' />\n    <nd ref='1260356842' />\n    <nd ref='1260356826' />\n    <nd ref='1260356834' />\n    <nd ref='1260356844' />\n    <nd ref='1260356846' />\n    <nd ref='1260356852' />\n    <nd ref='1260356861' />\n    <nd ref='255897295' />\n    <nd ref='255897296' />\n    <nd ref='31828120' />\n    <nd ref='1260356862' />\n    <nd ref='1260356822' />\n    <nd ref='31823084' />\n    <nd ref='1260356825' />\n    <nd ref='1260356855' />\n    <nd ref='1260356827' />\n    <nd ref='31850605' />\n    <nd ref='1260356858' />\n    <nd ref='358946108' />\n    <nd ref='358946109' />\n    <nd ref='358946110' />\n    <nd ref='5640337866' />\n    <nd ref='5640337869' />\n    <nd ref='31847860' />\n    <nd ref='5640337868' />\n    <nd ref='5640337867' />\n    <nd ref='31832893' />\n    <nd ref='31861668' />\n    <nd ref='1260356848' />\n    <nd ref='1260356857' />\n    <nd ref='31794393' />\n    <nd ref='1260356833' />\n    <nd ref='1260356837' />\n    <nd ref='1260356820' />\n    <nd ref='31858878' />\n    <nd ref='303754367' />\n    <nd ref='303754368' />\n    <nd ref='303754369' />\n    <nd ref='303754370' />\n    <nd ref='303754371' />\n    <nd ref='2450032774' />\n    <nd ref='2450032789' />\n    <nd ref='2450032799' />\n    <nd ref='1723646833' />\n    <nd ref='2450032798' />\n    <nd ref='303754358' />\n    <nd ref='31864303' />\n    <nd ref='31837618' />\n    <nd ref='666571379' />\n    <nd ref='666571381' />\n    <nd ref='666571382' />\n    <nd ref='666571384' />\n    <nd ref='2613280503' />\n    <nd ref='300460635' />\n    <nd ref='300460643' />\n    <nd ref='2922219017' />\n    <nd ref='2922218278' />\n    <nd ref='5184349789' />\n    <nd ref='5184349818' />\n    <nd ref='5184349819' />\n    <nd ref='1709638631' />\n    <nd ref='1709638627' />\n    <nd ref='1709638629' />\n    <nd ref='1709638633' />\n    <nd ref='1709638634' />\n    <nd ref='1709638635' />\n    <nd ref='1709638637' />\n    <nd ref='31860538' />\n    <nd ref='708533549' />\n    <nd ref='5184349820' />\n    <nd ref='303785344' />\n    <nd ref='708533568' />\n    <nd ref='5184349813' />\n    <nd ref='303785350' />\n    <nd ref='5184288025' />\n    <nd ref='5184288026' />\n    <nd ref='5184288024' />\n    <nd ref='5184288023' />\n    <nd ref='5184288022' />\n    <nd ref='5184288021' />\n    <nd ref='5184288020' />\n    <nd ref='5184288019' />\n    <nd ref='303785455' />\n    <nd ref='1723639081' />\n    <nd ref='303785511' />\n    <nd ref='1723639091' />\n    <nd ref='1723639055' />\n    <nd ref='1723639059' />\n    <nd ref='2450032794' />\n    <nd ref='1723639090' />\n    <nd ref='2450032804' />\n    <nd ref='2450032826' />\n    <nd ref='2450032775' />\n    <nd ref='2450032790' />\n    <nd ref='303785512' />\n    <nd ref='2450032802' />\n    <nd ref='2450032777' />\n    <nd ref='2450032842' />\n    <nd ref='2450032796' />\n    <nd ref='2450032813' />\n    <nd ref='2450032797' />\n    <nd ref='2450032806' />\n    <nd ref='2450032795' />\n    <nd ref='2450032849' />\n    <nd ref='2450032771' />\n    <nd ref='2450032793' />\n    <nd ref='2450032803' />\n    <nd ref='2450032829' />\n    <nd ref='2450032808' />\n    <nd ref='2450032814' />\n    <nd ref='2450032839' />\n    <nd ref='2450032835' />\n    <nd ref='2450032779' />\n    <nd ref='2450032843' />\n    <nd ref='2450032831' />\n    <nd ref='2450032833' />\n    <nd ref='2450032830' />\n    <nd ref='2450032772' />\n    <nd ref='2450032788' />\n    <nd ref='2450032838' />\n    <nd ref='2450032807' />\n    <nd ref='2450032827' />\n    <nd ref='2450032828' />\n    <nd ref='2450032834' />\n    <nd ref='2450032773' />\n    <nd ref='2450032837' />\n    <nd ref='2450032769' />\n    <nd ref='2450032821' />\n    <nd ref='31846028' />\n    <nd ref='2450032819' />\n    <nd ref='2450032791' />\n    <nd ref='2450032846' />\n    <nd ref='2450032784' />\n    <nd ref='31789507' />\n    <nd ref='1588951789' />\n    <nd ref='1588951791' />\n    <nd ref='1588951795' />\n    <nd ref='1588951793' />\n    <nd ref='1588951799' />\n    <nd ref='1588951797' />\n    <nd ref='1588951802' />\n    <nd ref='1588951801' />\n    <nd ref='1588951806' />\n    <nd ref='1588951804' />\n    <nd ref='1588951810' />\n    <nd ref='1588951808' />\n    <nd ref='1588951812' />\n    <nd ref='590986802' />\n    <nd ref='31839614' />\n    <nd ref='255649963' />\n    <nd ref='317102068' />\n    <nd ref='31821521' />\n    <nd ref='317102418' />\n    <nd ref='3456791114' />\n    <nd ref='31840458' />\n    <tag k='natural' v='coastline' />\n    <tag k='source' v='PGS' />\n  </way>\n  <way id='243465059' timestamp='2015-06-01T16:45:16Z' uid='33757' user='Minh Nguyen' version='3' changeset='31640735'>\n    <nd ref='2508652457' />\n    <nd ref='3563920176' />\n    <nd ref='3563920175' />\n    <nd ref='2508652407' />\n    <nd ref='2508652383' />\n    <nd ref='2508652431' />\n    <nd ref='2508652398' />\n    <nd ref='2508652394' />\n    <nd ref='2508652379' />\n    <nd ref='2625206736' />\n    <nd ref='2625206735' />\n    <nd ref='2508652457' />\n    <tag k='building' v='retail' />\n    <tag k='name' v='Sobel Building' />\n  </way>\n  <way id='256024188' timestamp='2015-07-09T04:03:11Z' uid='33757' user='Minh Nguyen' version='3' changeset='32509995'>\n    <nd ref='2617273752' />\n    <nd ref='2617273746' />\n    <nd ref='2617273744' />\n    <nd ref='2617273743' />\n    <nd ref='2617273748' />\n    <nd ref='2617273752' />\n    <tag k='alt_name' v='Building H' />\n    <tag k='building' v='yes' />\n    <tag k='name' v='Parsons' />\n    <tag k='ref' v='H' />\n  </way>\n  <way id='256024190' timestamp='2015-07-09T04:03:11Z' uid='33757' user='Minh Nguyen' version='3' changeset='32509995'>\n    <nd ref='2617273744' />\n    <nd ref='2617273741' />\n    <nd ref='2617273740' />\n    <nd ref='2617273742' />\n    <nd ref='2617273743' />\n    <nd ref='2617273744' />\n    <tag k='building' v='yes' />\n    <tag k='name' v='Building G' />\n    <tag k='ref' v='G' />\n  </way>\n  <way id='256897615' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540'>\n    <nd ref='2625194642' />\n    <nd ref='2625194643' />\n    <nd ref='2625194641' />\n    <nd ref='2625194640' />\n    <nd ref='2625194642' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='256897616' timestamp='2014-01-16T20:32:51Z' uid='1240849' user='ediyes' version='1' changeset='20040540'>\n    <nd ref='2625194645' />\n    <nd ref='2625194646' />\n    <nd ref='2625194644' />\n    <nd ref='2625194643' />\n    <nd ref='2625194642' />\n    <nd ref='2625194645' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='256898639' timestamp='2014-01-16T20:44:31Z' uid='510836' user='Rub21' version='1' changeset='20040699'>\n    <nd ref='2625206736' />\n    <nd ref='2625206735' />\n    <nd ref='2625206738' />\n    <nd ref='2625206740' />\n    <nd ref='2625206741' />\n    <nd ref='2625206739' />\n    <nd ref='2625206737' />\n    <nd ref='2625206736' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='288667880' timestamp='2018-01-22T20:21:16Z' uid='3817650' user='bdon_import' version='3' changeset='55665623'>\n    <nd ref='2922201680' />\n    <nd ref='2922201668' />\n    <nd ref='2922201667' />\n    <nd ref='2922201664' />\n    <nd ref='2922201662' />\n    <nd ref='2922201663' />\n    <nd ref='2922201661' />\n    <nd ref='2922201679' />\n    <nd ref='2922201680' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='7' />\n  </way>\n  <way id='288672372' timestamp='2018-01-22T16:59:34Z' uid='3817650' user='bdon_import' version='4' changeset='55660512'>\n    <nd ref='2922257157' />\n    <nd ref='2922257155' />\n    <nd ref='2922257156' />\n    <nd ref='2922257154' />\n    <nd ref='2922257149' />\n    <nd ref='2922257148' />\n    <nd ref='2922257147' />\n    <nd ref='2922257150' />\n    <nd ref='2922257143' />\n    <nd ref='2922257144' />\n    <nd ref='2922257138' />\n    <nd ref='2922257137' />\n    <nd ref='2922257125' />\n    <nd ref='2922257126' />\n    <nd ref='2922257116' />\n    <nd ref='2922257115' />\n    <nd ref='2922257114' />\n    <nd ref='2922257106' />\n    <nd ref='2922257102' />\n    <nd ref='2922257093' />\n    <nd ref='2922257092' />\n    <nd ref='2922257086' />\n    <nd ref='2922257083' />\n    <nd ref='2922257082' />\n    <nd ref='2922257079' />\n    <nd ref='2922257080' />\n    <nd ref='2922257071' />\n    <nd ref='2922257070' />\n    <nd ref='2922257056' />\n    <nd ref='2922257059' />\n    <nd ref='2922257058' />\n    <nd ref='2922257046' />\n    <nd ref='2922257045' />\n    <nd ref='2922257042' />\n    <nd ref='2922257044' />\n    <nd ref='2922257039' />\n    <nd ref='2922257037' />\n    <nd ref='2922257036' />\n    <nd ref='2922257030' />\n    <nd ref='2922257029' />\n    <nd ref='2922257023' />\n    <nd ref='2922257024' />\n    <nd ref='2922257017' />\n    <nd ref='2922257016' />\n    <nd ref='2922257008' />\n    <nd ref='2922257014' />\n    <nd ref='2922257015' />\n    <nd ref='2922257022' />\n    <nd ref='2922257018' />\n    <nd ref='2922257009' />\n    <nd ref='2922257011' />\n    <nd ref='2922257006' />\n    <nd ref='2922257002' />\n    <nd ref='2922257003' />\n    <nd ref='2922256898' />\n    <nd ref='2922256897' />\n    <nd ref='2922256896' />\n    <nd ref='2922256887' />\n    <nd ref='2922256888' />\n    <nd ref='2922256886' />\n    <nd ref='2922256884' />\n    <nd ref='2922256882' />\n    <nd ref='2922256883' />\n    <nd ref='2922256881' />\n    <nd ref='2922256885' />\n    <nd ref='2922256892' />\n    <nd ref='2922256894' />\n    <nd ref='2922256890' />\n    <nd ref='2922256891' />\n    <nd ref='2922256889' />\n    <nd ref='2922256893' />\n    <nd ref='2922256895' />\n    <nd ref='2922256899' />\n    <nd ref='2922256900' />\n    <nd ref='2922257001' />\n    <nd ref='2922257004' />\n    <nd ref='2922257005' />\n    <nd ref='2922257010' />\n    <nd ref='2922257007' />\n    <nd ref='2922257012' />\n    <nd ref='2922257021' />\n    <nd ref='2922257032' />\n    <nd ref='2922257025' />\n    <nd ref='2922257026' />\n    <nd ref='2922257020' />\n    <nd ref='2922257019' />\n    <nd ref='2922257013' />\n    <nd ref='2922257027' />\n    <nd ref='2922257031' />\n    <nd ref='2922257028' />\n    <nd ref='2922257033' />\n    <nd ref='2922257034' />\n    <nd ref='2922257038' />\n    <nd ref='2922257035' />\n    <nd ref='2922257040' />\n    <nd ref='2922257041' />\n    <nd ref='2922257048' />\n    <nd ref='2922257049' />\n    <nd ref='2922257063' />\n    <nd ref='2922257067' />\n    <nd ref='2922257062' />\n    <nd ref='2922257066' />\n    <nd ref='2922257076' />\n    <nd ref='2922257077' />\n    <nd ref='2922257085' />\n    <nd ref='2922257081' />\n    <nd ref='2922257084' />\n    <nd ref='2922257078' />\n    <nd ref='2922257075' />\n    <nd ref='2922257073' />\n    <nd ref='2922257074' />\n    <nd ref='2922257065' />\n    <nd ref='2922257064' />\n    <nd ref='2922257061' />\n    <nd ref='2922257055' />\n    <nd ref='2922257057' />\n    <nd ref='2922257054' />\n    <nd ref='2922257053' />\n    <nd ref='2922257052' />\n    <nd ref='2922257051' />\n    <nd ref='2922257050' />\n    <nd ref='2922257047' />\n    <nd ref='2922257043' />\n    <nd ref='2922257060' />\n    <nd ref='2922257068' />\n    <nd ref='2922257069' />\n    <nd ref='2922257072' />\n    <nd ref='2922257090' />\n    <nd ref='2922257091' />\n    <nd ref='2922257094' />\n    <nd ref='2922257095' />\n    <nd ref='2922257097' />\n    <nd ref='2922257103' />\n    <nd ref='2922257101' />\n    <nd ref='2922257112' />\n    <nd ref='2922257113' />\n    <nd ref='2922257121' />\n    <nd ref='2922257129' />\n    <nd ref='2922257132' />\n    <nd ref='2922257133' />\n    <nd ref='2922257139' />\n    <nd ref='2922257135' />\n    <nd ref='2922257140' />\n    <nd ref='2922257134' />\n    <nd ref='2922257136' />\n    <nd ref='2922257128' />\n    <nd ref='2922257127' />\n    <nd ref='2922257124' />\n    <nd ref='2922257119' />\n    <nd ref='2922257120' />\n    <nd ref='2922257111' />\n    <nd ref='2922257110' />\n    <nd ref='2922257109' />\n    <nd ref='2922257104' />\n    <nd ref='2922257107' />\n    <nd ref='2922257100' />\n    <nd ref='2922257099' />\n    <nd ref='2922257098' />\n    <nd ref='2922257089' />\n    <nd ref='2922257088' />\n    <nd ref='2922257087' />\n    <nd ref='2922257096' />\n    <nd ref='2922257108' />\n    <nd ref='2922257105' />\n    <nd ref='2922257118' />\n    <nd ref='2922257117' />\n    <nd ref='2922257122' />\n    <nd ref='2922257123' />\n    <nd ref='2922257131' />\n    <nd ref='2922257130' />\n    <nd ref='2922257141' />\n    <nd ref='2922257142' />\n    <nd ref='2922257146' />\n    <nd ref='2922257145' />\n    <nd ref='2922257151' />\n    <nd ref='2922257152' />\n    <nd ref='2922257153' />\n    <nd ref='2922257158' />\n    <nd ref='2922257159' />\n    <nd ref='2922257160' />\n    <nd ref='2922257157' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='420' />\n    <tag k='addr:postcode' v='94158' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Berry Street' />\n    <tag k='building' v='apartments' />\n    <tag k='building:levels' v='4' />\n    <tag k='height' v='13' />\n    <tag k='name' v='Crescent Cove' />\n  </way>\n  <way id='288673594' timestamp='2018-01-22T16:59:36Z' uid='3817650' user='bdon_import' version='3' changeset='55660512'>\n    <nd ref='2922265174' />\n    <nd ref='2922265171' />\n    <nd ref='2922265170' />\n    <nd ref='2922265169' />\n    <nd ref='2922265167' />\n    <nd ref='2922265168' />\n    <nd ref='2922265166' />\n    <nd ref='2922265165' />\n    <nd ref='2922265164' />\n    <nd ref='2922265162' />\n    <nd ref='2922265163' />\n    <nd ref='2922265161' />\n    <nd ref='2922265160' />\n    <nd ref='2922265158' />\n    <nd ref='2922265159' />\n    <nd ref='2922265157' />\n    <nd ref='2922265156' />\n    <nd ref='2922265155' />\n    <nd ref='2922265153' />\n    <nd ref='2922265154' />\n    <nd ref='2922265152' />\n    <nd ref='2922265151' />\n    <nd ref='2922265149' />\n    <nd ref='2922265150' />\n    <nd ref='2922265147' />\n    <nd ref='2922265146' />\n    <nd ref='2922265145' />\n    <nd ref='2922265143' />\n    <nd ref='2922265144' />\n    <nd ref='2922265142' />\n    <nd ref='2922265139' />\n    <nd ref='2922265138' />\n    <nd ref='2922265133' />\n    <nd ref='2922265137' />\n    <nd ref='2922265132' />\n    <nd ref='2922265131' />\n    <nd ref='2922265130' />\n    <nd ref='2922265128' />\n    <nd ref='2922265129' />\n    <nd ref='2922265127' />\n    <nd ref='2922265126' />\n    <nd ref='2922265124' />\n    <nd ref='2922265122' />\n    <nd ref='2922265123' />\n    <nd ref='2922265121' />\n    <nd ref='2922265120' />\n    <nd ref='2922265119' />\n    <nd ref='2922265117' />\n    <nd ref='2922265115' />\n    <nd ref='2922265116' />\n    <nd ref='2922265114' />\n    <nd ref='2922265113' />\n    <nd ref='2922265112' />\n    <nd ref='2922265110' />\n    <nd ref='2922265109' />\n    <nd ref='2922265108' />\n    <nd ref='2922265107' />\n    <nd ref='2922265106' />\n    <nd ref='2922265105' />\n    <nd ref='2922265102' />\n    <nd ref='2922265101' />\n    <nd ref='2922262499' />\n    <nd ref='2922262500' />\n    <nd ref='2922262498' />\n    <nd ref='2922262496' />\n    <nd ref='2922262495' />\n    <nd ref='2922262494' />\n    <nd ref='2922262492' />\n    <nd ref='2922262493' />\n    <nd ref='2922262491' />\n    <nd ref='2922262489' />\n    <nd ref='2922262488' />\n    <nd ref='2922262486' />\n    <nd ref='2922262487' />\n    <nd ref='2922262485' />\n    <nd ref='2922262484' />\n    <nd ref='2922262479' />\n    <nd ref='2922262481' />\n    <nd ref='2922262472' />\n    <nd ref='2922262474' />\n    <nd ref='2922262473' />\n    <nd ref='2922262475' />\n    <nd ref='2922262476' />\n    <nd ref='2922262478' />\n    <nd ref='2922262477' />\n    <nd ref='2922262480' />\n    <nd ref='2922262483' />\n    <nd ref='2922262482' />\n    <nd ref='2922262490' />\n    <nd ref='2922262497' />\n    <nd ref='2922265103' />\n    <nd ref='2922265104' />\n    <nd ref='2922265111' />\n    <nd ref='2922265118' />\n    <nd ref='2922265125' />\n    <nd ref='2922265134' />\n    <nd ref='2922265135' />\n    <nd ref='2922265136' />\n    <nd ref='2922265141' />\n    <nd ref='2922265140' />\n    <nd ref='2922265148' />\n    <nd ref='2922265173' />\n    <nd ref='2922265172' />\n    <nd ref='2922265176' />\n    <nd ref='2922265175' />\n    <nd ref='2922265174' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='9' />\n  </way>\n  <way id='346766970' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='4' changeset='63557245'>\n    <nd ref='3532718886' />\n    <nd ref='2274244022' />\n    <nd ref='5487354240' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='346766971' timestamp='2018-10-15T22:03:39Z' uid='2237750' user='chachafish' version='4' changeset='63557245'>\n    <nd ref='5487354238' />\n    <nd ref='3532718888' />\n    <nd ref='3532718889' />\n    <nd ref='3532718887' />\n    <nd ref='5487354239' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='349439374' timestamp='2015-05-28T06:41:59Z' uid='33757' user='Minh Nguyen' version='2' changeset='31521737'>\n    <nd ref='2922265176' />\n    <nd ref='2922265172' />\n    <nd ref='2922265173' />\n    <nd ref='2922265148' />\n    <nd ref='2922265140' />\n    <nd ref='2922265141' />\n    <nd ref='2922265136' />\n    <nd ref='2922265134' />\n    <nd ref='2922265125' />\n    <nd ref='2922265118' />\n    <nd ref='2922265111' />\n    <nd ref='2922265104' />\n    <nd ref='2922262497' />\n    <nd ref='2922262490' />\n    <nd ref='2922262482' />\n    <nd ref='2922262483' />\n    <nd ref='2922262480' />\n    <nd ref='2922262477' />\n    <nd ref='2922262478' />\n    <nd ref='2922262476' />\n    <nd ref='2922262475' />\n    <nd ref='2922262473' />\n    <tag k='artwork_type' v='mural' />\n    <tag k='barrier' v='wall' />\n    <tag k='name' v='Systems Mural' />\n    <tag k='tourism' v='artwork' />\n    <tag k='website' v='http://sfmural.com/' />\n  </way>\n  <way id='351315297' timestamp='2015-06-04T05:43:56Z' uid='33757' user='Minh Nguyen' version='1' changeset='31708207'>\n    <nd ref='3570918496' />\n    <nd ref='3570918497' />\n    <nd ref='3570918498' />\n    <nd ref='3570918499' />\n    <nd ref='3570918500' />\n    <nd ref='1813413851' />\n    <nd ref='3570918496' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='351964812' timestamp='2018-01-24T04:31:57Z' uid='3817650' user='bdon_import' version='5' changeset='55702219'>\n    <nd ref='2274243989' />\n    <nd ref='2274243982' />\n    <nd ref='2274243980' />\n    <nd ref='2274243986' />\n    <nd ref='2274243989' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='25' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Rhode Island Street' />\n    <tag k='building' v='yes' />\n    <tag k='building:levels' v='2' />\n    <tag k='height' v='6' />\n    <tag k='name' v='floordesign' />\n    <tag k='shop' v='carpet' />\n  </way>\n  <way id='351964814' timestamp='2018-10-16T08:32:51Z' uid='2237750' user='chachafish' version='9' changeset='63567870'>\n    <nd ref='3577621763' />\n    <nd ref='5986734224' />\n    <nd ref='5986734225' />\n    <nd ref='5986734226' />\n    <nd ref='5986734223' />\n    <nd ref='3577621761' />\n    <nd ref='5449464497' />\n    <nd ref='5449464499' />\n    <nd ref='5449464503' />\n    <nd ref='5449464502' />\n    <nd ref='5449464501' />\n    <nd ref='5449464500' />\n    <nd ref='5449464498' />\n    <nd ref='5986734261' />\n    <nd ref='5184864687' />\n    <nd ref='3577621762' />\n    <nd ref='5986734262' />\n    <nd ref='5986734263' />\n    <nd ref='5986734265' />\n    <nd ref='5986734267' />\n    <nd ref='3577621764' />\n    <nd ref='5986734268' />\n    <nd ref='5986734269' />\n    <nd ref='5986734266' />\n    <nd ref='5986734271' />\n    <nd ref='5986734270' />\n    <nd ref='5986734280' />\n    <nd ref='5986734281' />\n    <nd ref='5986734283' />\n    <nd ref='5986734282' />\n    <nd ref='5986733764' />\n    <nd ref='5986733767' />\n    <nd ref='5986733766' />\n    <nd ref='5986733765' />\n    <nd ref='3577621763' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='1' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Henry Adams Street' />\n    <tag k='building' v='apartments' />\n    <tag k='building:colour' v='#676d5f' />\n    <tag k='building:levels' v='6' />\n    <tag k='building:material' v='glass' />\n    <tag k='height' v='24.5' />\n    <tag k='roof:colour' v='#f5deb3' />\n  </way>\n  <way id='355589869' timestamp='2018-03-18T21:06:08Z' uid='53073' user='Aaron Lidman' version='3' changeset='57301060'>\n    <nd ref='3611937228' />\n    <nd ref='5487246119' />\n    <nd ref='5487246116' />\n    <nd ref='5487246114' />\n    <nd ref='5435465957' />\n    <nd ref='3611937229' />\n    <tag k='highway' v='service' />\n    <tag k='name' v='Bluxome Alley' />\n    <tag k='service' v='alley' />\n  </way>\n  <way id='356479005' timestamp='2015-06-26T08:45:11Z' uid='33757' user='Minh Nguyen' version='1' changeset='32220993'>\n    <nd ref='3619170069' />\n    <nd ref='3619170070' />\n    <nd ref='2274244085' />\n    <nd ref='2274244100' />\n    <nd ref='3619170069' />\n    <tag k='access' v='unknown' />\n    <tag k='amenity' v='parking' />\n    <tag k='parking' v='multi-storey' />\n  </way>\n  <way id='394691476' timestamp='2018-08-18T03:27:18Z' uid='119881' user='clay_c' version='9' changeset='61761408'>\n    <nd ref='65283717' />\n    <nd ref='65283721' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='Civic Center' />\n    <tag k='destination:street' v='9th Street' />\n    <tag k='highway' v='motorway_link' />\n    <tag k='junction:ref' v='433C' />\n    <tag k='lanes' v='1' />\n    <tag k='layer' v='2' />\n    <tag k='oneway' v='yes' />\n    <tag k='tiger:cfcc' v='A63' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n  </way>\n  <way id='394691481' timestamp='2016-01-29T19:03:28Z' uid='94578' user='andygol' version='1' changeset='36885656'>\n    <nd ref='65283776' />\n    <nd ref='65283778' />\n    <nd ref='314079644' />\n    <nd ref='257670327' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='highway' v='motorway_link' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='1' />\n    <tag k='oneway' v='yes' />\n    <tag k='tiger:cfcc' v='A63' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n  </way>\n  <way id='397250673' timestamp='2016-02-11T12:42:34Z' uid='2015224' user='Jothirnadh' version='1' changeset='37144817'>\n    <nd ref='65303732' />\n    <nd ref='984720797' />\n    <nd ref='1344032216' />\n    <nd ref='65356544' />\n    <nd ref='1344032637' />\n    <nd ref='423775491' />\n    <nd ref='703048830' />\n    <tag k='bicycle' v='yes' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='2' />\n    <tag k='name' v='Mission Bay Drive' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='surface' v='paved' />\n    <tag k='turn:lanes' v='through|through' />\n  </way>\n  <way id='417392953' timestamp='2018-08-28T21:51:12Z' uid='8107451' user='njtbusfan' version='3' changeset='62086023'>\n    <nd ref='4179515466' />\n    <nd ref='65313423' />\n    <nd ref='4179515465' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='secondary' />\n    <tag k='lcn_ref' v='23' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='name' v='7th Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n    <tag k='turn:lanes:backward' v='|merge_to_left' />\n    <tag k='turn:lanes:forward' v='left|' />\n  </way>\n  <way id='417392962' timestamp='2018-08-28T21:51:12Z' uid='8107451' user='njtbusfan' version='3' changeset='62086023'>\n    <nd ref='4179515465' />\n    <nd ref='65303732' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='secondary' />\n    <tag k='lanes' v='4' />\n    <tag k='lanes:backward' v='2' />\n    <tag k='lanes:forward' v='2' />\n    <tag k='lcn_ref' v='23' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='name' v='7th Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n    <tag k='turn:lanes:forward' v='left|' />\n  </way>\n  <way id='417392966' timestamp='2018-08-28T21:51:12Z' uid='8107451' user='njtbusfan' version='2' changeset='62086023'>\n    <nd ref='65303726' />\n    <nd ref='4179515467' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='secondary' />\n    <tag k='lcn_ref' v='23' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='name' v='7th Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='417392970' timestamp='2018-08-28T21:51:12Z' uid='8107451' user='njtbusfan' version='3' changeset='62086023'>\n    <nd ref='4179515467' />\n    <nd ref='5438253562' />\n    <nd ref='65303730' />\n    <nd ref='4179515466' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='secondary' />\n    <tag k='lcn_ref' v='23' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='name' v='7th Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n    <tag k='turn:lanes:backward' v='|merge_to_left' />\n  </way>\n  <way id='483432855' timestamp='2017-03-29T11:43:38Z' uid='933797' user='oba510' version='1' changeset='47257631'>\n    <nd ref='65303894' />\n    <nd ref='4761685552' />\n    <tag k='highway' v='residential' />\n    <tag k='name' v='Utah Street' />\n    <tag k='sidewalk' v='right' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='496374147' timestamp='2017-05-27T22:34:28Z' uid='33757' user='Minh Nguyen' version='1' changeset='49034504'>\n    <nd ref='4880860065' />\n    <nd ref='4880860066' />\n    <nd ref='3532718892' />\n    <tag k='footway' v='sidewalk' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='501802406' timestamp='2018-01-24T04:31:57Z' uid='3817650' user='bdon_import' version='2' changeset='55702219'>\n    <nd ref='2274244064' />\n    <nd ref='2274244078' />\n    <nd ref='2274244093' />\n    <nd ref='2274244084' />\n    <nd ref='2274244051' />\n    <nd ref='2274244064' />\n    <tag k='addr:housenumber' v='660' />\n    <tag k='addr:street' v='King Street' />\n    <tag k='building' v='yes' />\n    <tag k='height' v='13' />\n    <tag k='name' v='E' />\n  </way>\n  <way id='501802407' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013307' />\n    <nd ref='4925013306' />\n    <nd ref='4925013305' />\n    <nd ref='4925013304' />\n    <nd ref='4925013303' />\n    <nd ref='4925013302' />\n    <nd ref='4925013301' />\n    <nd ref='4925013300' />\n    <nd ref='4925013299' />\n    <nd ref='4925013298' />\n    <nd ref='4925013297' />\n    <nd ref='4925013296' />\n    <tag k='highway' v='path' />\n  </way>\n  <way id='501802408' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013309' />\n    <nd ref='4925013308' />\n    <tag k='access' v='private' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='501802409' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013315' />\n    <nd ref='4925013307' />\n    <nd ref='4925013309' />\n    <nd ref='4925013310' />\n    <tag k='access' v='private' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='501802410' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013319' />\n    <nd ref='4925013315' />\n    <tag k='access' v='private' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='501802411' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013312' />\n    <nd ref='4925013313' />\n    <nd ref='4925013311' />\n    <tag k='access' v='private' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='501802412' timestamp='2017-06-19T20:20:38Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013315' />\n    <nd ref='4925013314' />\n    <nd ref='4925013313' />\n    <tag k='access' v='private' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='501802413' timestamp='2017-06-19T20:20:39Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013320' />\n    <nd ref='4925013319' />\n    <tag k='access' v='private' />\n    <tag k='highway' v='steps' />\n  </way>\n  <way id='501802414' timestamp='2017-06-19T20:20:39Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013319' />\n    <nd ref='4925013308' />\n    <nd ref='4925013316' />\n    <tag k='access' v='private' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='501802415' timestamp='2017-06-19T20:20:39Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013316' />\n    <nd ref='4925013318' />\n    <tag k='access' v='private' />\n    <tag k='highway' v='steps' />\n  </way>\n  <way id='501802416' timestamp='2017-06-19T20:20:39Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925013318' />\n    <nd ref='4925013317' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='501802417' timestamp='2017-06-19T20:20:39Z' uid='1306' user='PlaneMad' version='1' changeset='49676208'>\n    <nd ref='4925027421' />\n    <nd ref='4925013320' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='503918557' timestamp='2017-06-30T20:23:43Z' uid='2219338' user='RichRico' version='2' changeset='49953223'>\n    <nd ref='4941620699' />\n    <nd ref='4941620698' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='503918558' timestamp='2017-06-29T07:00:03Z' uid='5659851' user='marthaleena' version='1' changeset='49908842'>\n    <nd ref='4941620701' />\n    <nd ref='4941620697' />\n    <nd ref='4941620700' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='503918559' timestamp='2017-06-30T20:23:43Z' uid='2219338' user='RichRico' version='2' changeset='49953223'>\n    <nd ref='4941620703' />\n    <nd ref='4941620702' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='503918560' timestamp='2017-06-30T20:23:43Z' uid='2219338' user='RichRico' version='2' changeset='49953223'>\n    <nd ref='4941620704' />\n    <nd ref='4941620702' />\n    <nd ref='4941620706' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='503918561' timestamp='2018-03-29T20:50:46Z' uid='53073' user='Aaron Lidman' version='4' changeset='57645807'>\n    <nd ref='4941620822' />\n    <nd ref='5515121096' />\n    <nd ref='4941620716' />\n    <nd ref='4941620821' />\n    <nd ref='4941620708' />\n    <nd ref='4941620705' />\n    <nd ref='4941620701' />\n    <nd ref='4941620703' />\n    <nd ref='4941620699' />\n    <nd ref='4941620707' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='503918562' timestamp='2017-06-29T07:00:03Z' uid='5659851' user='marthaleena' version='1' changeset='49908842'>\n    <nd ref='4941620710' />\n    <nd ref='4941620709' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='503918563' timestamp='2018-02-26T19:08:37Z' uid='5288591' user='Irina Karachevtseva' version='4' changeset='56699939'>\n    <nd ref='4941620709' />\n    <nd ref='4941620712' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='503918564' timestamp='2017-06-29T07:00:03Z' uid='5659851' user='marthaleena' version='1' changeset='49908842'>\n    <nd ref='4941620715' />\n    <nd ref='4941620714' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='503918565' timestamp='2017-06-29T07:00:03Z' uid='5659851' user='marthaleena' version='1' changeset='49908842'>\n    <nd ref='4941620716' />\n    <nd ref='4941620714' />\n    <nd ref='4941620717' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='503918566' timestamp='2018-02-26T19:08:37Z' uid='5288591' user='Irina Karachevtseva' version='3' changeset='56699939'>\n    <nd ref='5438253560' />\n    <nd ref='4941620821' />\n    <nd ref='4941620715' />\n    <nd ref='4941620720' />\n    <nd ref='4941620719' />\n    <nd ref='4941620718' />\n    <nd ref='4941620717' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='503918577' timestamp='2018-02-26T19:08:37Z' uid='5288591' user='Irina Karachevtseva' version='2' changeset='56699939'>\n    <nd ref='4941620847' />\n    <nd ref='4941620846' />\n    <nd ref='4941620845' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='503918578' timestamp='2018-02-26T19:08:37Z' uid='5288591' user='Irina Karachevtseva' version='3' changeset='56699939'>\n    <nd ref='4941620852' />\n    <nd ref='4941620848' />\n    <nd ref='4941620851' />\n    <nd ref='4941620850' />\n    <nd ref='4941620847' />\n    <nd ref='4941620849' />\n    <nd ref='4941620845' />\n    <nd ref='4941620848' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='504300638' timestamp='2017-06-30T20:23:41Z' uid='2219338' user='RichRico' version='1' changeset='49953223'>\n    <nd ref='4941620698' />\n    <nd ref='4941620697' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='504300644' timestamp='2018-02-26T19:08:37Z' uid='5288591' user='Irina Karachevtseva' version='3' changeset='56699939'>\n    <nd ref='4941620712' />\n    <nd ref='4941620711' />\n    <nd ref='5438253561' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='504300645' timestamp='2017-06-30T20:23:42Z' uid='2219338' user='RichRico' version='1' changeset='49953223'>\n    <nd ref='4941620705' />\n    <nd ref='4941620704' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='504300646' timestamp='2017-06-30T20:23:42Z' uid='2219338' user='RichRico' version='1' changeset='49953223'>\n    <nd ref='4941620707' />\n    <nd ref='4941620706' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='513699413' timestamp='2018-08-28T21:51:13Z' uid='8107451' user='njtbusfan' version='3' changeset='62086023'>\n    <nd ref='65303732' />\n    <nd ref='5506470752' />\n    <nd ref='4941620696' />\n    <nd ref='5020763504' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='secondary' />\n    <tag k='lanes' v='4' />\n    <tag k='lanes:backward' v='3' />\n    <tag k='lanes:forward' v='1' />\n    <tag k='lcn_ref' v='23' />\n    <tag k='maxspeed' v='25 mph' />\n    <tag k='name' v='7th Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n    <tag k='turn:lanes:backward' v='||right' />\n  </way>\n  <way id='516061750' timestamp='2018-10-15T22:03:40Z' uid='2237750' user='chachafish' version='4' changeset='63557245'>\n    <nd ref='5038477632' />\n    <nd ref='3611937228' />\n    <nd ref='2304626320' />\n    <nd ref='2304626336' />\n    <tag k='bicycle' v='designated' />\n    <tag k='cycleway:right' v='track' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lcn_ref' v='23' />\n    <tag k='maxspeed' v='30 mph' />\n    <tag k='name' v='8th Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='8th' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='534621727' timestamp='2017-10-23T18:29:50Z' uid='53073' user='Aaron Lidman' version='1' changeset='53186943'>\n    <nd ref='423775500' />\n    <nd ref='5184349771' />\n    <nd ref='5184349770' />\n    <nd ref='5184349769' />\n    <nd ref='5184349768' />\n    <nd ref='5184349767' />\n    <nd ref='5184349766' />\n    <nd ref='5184349765' />\n    <nd ref='5184349764' />\n    <nd ref='5184349763' />\n    <nd ref='5184349762' />\n    <nd ref='5184349761' />\n    <nd ref='5184349760' />\n    <nd ref='5184349759' />\n    <nd ref='5184349758' />\n    <nd ref='5184349757' />\n    <nd ref='5184349756' />\n    <nd ref='5184349755' />\n    <nd ref='708463538' />\n    <nd ref='5184349835' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='534675090' timestamp='2017-10-23T22:45:39Z' uid='53073' user='Aaron Lidman' version='1' changeset='53193135'>\n    <nd ref='5184837261' />\n    <nd ref='5184837262' />\n    <tag k='access' v='no' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='driveway' />\n  </way>\n  <way id='534677316' timestamp='2018-10-16T08:32:50Z' uid='2237750' user='chachafish' version='7' changeset='63567870'>\n    <nd ref='5184864680' />\n    <nd ref='5986734246' />\n    <nd ref='5986734245' />\n    <nd ref='5986734202' />\n    <nd ref='5986734204' />\n    <nd ref='5986734203' />\n    <nd ref='5986734206' />\n    <nd ref='5986734205' />\n    <nd ref='5986734212' />\n    <nd ref='5986734213' />\n    <nd ref='5987137901' />\n    <nd ref='5986734214' />\n    <nd ref='5986734211' />\n    <nd ref='5986734209' />\n    <nd ref='5986734210' />\n    <nd ref='5986734208' />\n    <nd ref='5986734207' />\n    <nd ref='5986734201' />\n    <nd ref='5986734241' />\n    <nd ref='5986734247' />\n    <nd ref='5184864681' />\n    <nd ref='5986734191' />\n    <nd ref='5986734193' />\n    <nd ref='5986734194' />\n    <nd ref='5986734192' />\n    <nd ref='5449464496' />\n    <nd ref='5986734195' />\n    <nd ref='5986734196' />\n    <nd ref='5986734197' />\n    <nd ref='5986734198' />\n    <nd ref='5986734200' />\n    <nd ref='5986734199' />\n    <nd ref='5184864682' />\n    <nd ref='5986734216' />\n    <nd ref='5986734218' />\n    <nd ref='5986734217' />\n    <nd ref='5986734215' />\n    <nd ref='5986734189' />\n    <nd ref='5986734190' />\n    <nd ref='5986734188' />\n    <nd ref='5986734187' />\n    <nd ref='5986734186' />\n    <nd ref='5986734185' />\n    <nd ref='5987137969' />\n    <nd ref='5986733784' />\n    <nd ref='5986733783' />\n    <nd ref='5986733781' />\n    <nd ref='5986733782' />\n    <nd ref='5986733780' />\n    <nd ref='5986733779' />\n    <nd ref='5986734220' />\n    <nd ref='5986734222' />\n    <nd ref='5986734221' />\n    <nd ref='5986734219' />\n    <nd ref='5184864683' />\n    <nd ref='5986733778' />\n    <nd ref='5986733777' />\n    <nd ref='5986733776' />\n    <nd ref='5986733775' />\n    <nd ref='5986733774' />\n    <nd ref='5986733773' />\n    <nd ref='5986733772' />\n    <nd ref='5986733771' />\n    <nd ref='5986733770' />\n    <nd ref='5986733769' />\n    <nd ref='5986733768' />\n    <nd ref='5184864686' />\n    <nd ref='5184864680' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='1' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Henry Adams Street' />\n    <tag k='building' v='apartments' />\n    <tag k='building:colour' v='#CD853F' />\n    <tag k='building:levels' v='6' />\n    <tag k='building:material' v='glass' />\n    <tag k='height' v='24' />\n    <tag k='roof:colour' v='#f5deb3' />\n  </way>\n  <way id='547181915' timestamp='2018-03-18T21:06:08Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301060'>\n    <nd ref='5287020461' />\n    <nd ref='5287020460' />\n    <nd ref='5287020459' />\n    <nd ref='5287020458' />\n    <nd ref='5287020455' />\n    <nd ref='5287020454' />\n    <nd ref='5287020451' />\n    <nd ref='5287020450' />\n    <nd ref='5287020449' />\n    <nd ref='5287020448' />\n    <nd ref='5287020447' />\n    <nd ref='5287019804' />\n    <nd ref='5287019805' />\n    <nd ref='5487246111' />\n    <nd ref='5487246112' />\n    <nd ref='5287020446' />\n    <nd ref='5287020445' />\n    <nd ref='5287020444' />\n    <nd ref='5287020443' />\n    <nd ref='5287020442' />\n    <nd ref='5287020441' />\n    <nd ref='5287020440' />\n    <nd ref='5287020439' />\n    <nd ref='5287020438' />\n    <nd ref='5287020437' />\n    <nd ref='5287020434' />\n    <nd ref='5287020433' />\n    <nd ref='5287020432' />\n    <nd ref='5287020431' />\n    <nd ref='5287020426' />\n    <nd ref='5287020424' />\n    <nd ref='5287020423' />\n    <nd ref='5287019820' />\n    <nd ref='5287019818' />\n    <nd ref='5287019816' />\n    <nd ref='5287019811' />\n    <nd ref='5287019812' />\n    <nd ref='5287019813' />\n    <nd ref='5287019814' />\n    <nd ref='5287019815' />\n    <nd ref='5287020461' />\n    <tag k='level' v='1' />\n  </way>\n  <way id='564435382' timestamp='2018-02-26T19:08:26Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253559' />\n    <nd ref='5438253560' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435383' timestamp='2018-02-26T19:08:26Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253561' />\n    <nd ref='5438253562' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='564435384' timestamp='2018-02-26T19:08:26Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='4941620709' />\n    <nd ref='5438253563' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435385' timestamp='2018-03-18T22:12:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57302156'>\n    <nd ref='5438253565' />\n    <nd ref='5487354234' />\n    <nd ref='5438253566' />\n    <nd ref='5438253564' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435386' timestamp='2018-03-18T22:12:45Z' uid='53073' user='Aaron Lidman' version='2' changeset='57302156'>\n    <nd ref='5438253567' />\n    <nd ref='5438253566' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='driveway' />\n  </way>\n  <way id='564435387' timestamp='2018-02-26T19:08:26Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253569' />\n    <nd ref='5438253568' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435391' timestamp='2018-03-03T01:29:56Z' uid='53073' user='Aaron Lidman' version='2' changeset='56836576'>\n    <nd ref='4941620823' />\n    <nd ref='5449464493' />\n    <nd ref='5449464494' />\n    <nd ref='5438253573' />\n    <tag k='access' v='permissive' />\n    <tag k='highway' v='service' />\n    <tag k='oneway' v='yes' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435394' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='65303885' />\n    <nd ref='5438253576' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='564435395' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253576' />\n    <nd ref='5438253575' />\n    <nd ref='5438253574' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435402' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253591' />\n    <nd ref='5438253590' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='564435403' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253590' />\n    <nd ref='5438253586' />\n    <nd ref='5438253585' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435404' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253588' />\n    <nd ref='5438253587' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435405' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253590' />\n    <nd ref='5438253588' />\n    <nd ref='5438253589' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435406' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253597' />\n    <nd ref='5438253596' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='564435407' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253592' />\n    <nd ref='5438253596' />\n    <nd ref='5438253593' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435408' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253596' />\n    <nd ref='5438253595' />\n    <nd ref='5438253594' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435409' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253599' />\n    <nd ref='5438253598' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='564435410' timestamp='2018-02-26T19:08:27Z' uid='5288591' user='Irina Karachevtseva' version='1' changeset='56699939'>\n    <nd ref='5438253600' />\n    <nd ref='5438253599' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='565995475' timestamp='2018-03-18T21:35:18Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301575'>\n    <nd ref='5449466839' />\n    <nd ref='5449466840' />\n    <nd ref='5449466841' />\n    <nd ref='5449466842' />\n    <nd ref='5449466839' />\n    <tag k='building' v='yes' />\n    <tag k='building:levels' v='4' />\n  </way>\n  <way id='565995476' timestamp='2018-06-01T01:18:12Z' uid='53073' user='Aaron Lidman' version='3' changeset='59447024'>\n    <nd ref='5449466843' />\n    <nd ref='5449466844' />\n    <nd ref='5449466845' />\n    <nd ref='1950347611' />\n    <nd ref='5449466843' />\n    <tag k='building' v='yes' />\n    <tag k='building:levels' v='4' />\n  </way>\n  <way id='570785600' timestamp='2018-03-18T22:01:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='57301995'>\n    <nd ref='5487350515' />\n    <nd ref='5487350516' />\n    <nd ref='5487350517' />\n    <nd ref='5487350518' />\n    <nd ref='5487350515' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='855' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Brannan Street' />\n    <tag k='landuse' v='residential' />\n    <tag k='name' v='855 Brennan' />\n  </way>\n  <way id='570786825' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156'>\n    <nd ref='5487354232' />\n    <nd ref='5487354233' />\n    <nd ref='5487354231' />\n    <tag k='crossing' v='zebra' />\n    <tag k='footway' v='crossing' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='570786826' timestamp='2018-03-18T22:12:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57302156'>\n    <nd ref='5487354234' />\n    <nd ref='5487354235' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='driveway' />\n  </way>\n  <way id='570786829' timestamp='2018-10-15T22:03:40Z' uid='2237750' user='chachafish' version='2' changeset='63557245'>\n    <nd ref='5487354240' />\n    <nd ref='2304626330' />\n    <nd ref='5487354242' />\n    <tag k='footway' v='sidewalk' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='573287806' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='2' changeset='63553269'>\n    <nd ref='5506466408' />\n    <nd ref='5506466409' />\n    <nd ref='5506466410' />\n    <nd ref='5506466411' />\n    <nd ref='5506466408' />\n    <tag k='addr:city' v='San Francisco' />\n    <tag k='addr:housenumber' v='1' />\n    <tag k='addr:postcode' v='94103' />\n    <tag k='addr:state' v='CA' />\n    <tag k='addr:street' v='Henry Adams Street' />\n    <tag k='landuse' v='residential' />\n    <tag k='name' v='One Henry Adams' />\n  </way>\n  <way id='574610487' timestamp='2018-03-29T20:50:44Z' uid='53073' user='Aaron Lidman' version='1' changeset='57645807'>\n    <nd ref='5515121097' />\n    <nd ref='2274244053' />\n    <nd ref='4072351916' />\n    <nd ref='2274244050' />\n    <nd ref='2274244056' />\n    <nd ref='2274244049' />\n    <nd ref='2274244052' />\n    <nd ref='2274244062' />\n    <nd ref='2274244068' />\n    <nd ref='5515121098' />\n    <nd ref='2274244058' />\n    <nd ref='2274244070' />\n    <nd ref='5515121099' />\n    <nd ref='2274244057' />\n    <nd ref='2274244069' />\n    <nd ref='2274244067' />\n    <nd ref='5515121100' />\n    <nd ref='2249911759' />\n    <nd ref='2249911801' />\n    <nd ref='2249911804' />\n    <nd ref='2249911788' />\n    <nd ref='5515121101' />\n    <nd ref='5515121097' />\n    <tag k='leisure' v='park' />\n  </way>\n  <way id='579720319' timestamp='2018-04-13T21:53:06Z' uid='53073' user='Aaron Lidman' version='1' changeset='58077286'>\n    <nd ref='5551421961' />\n    <nd ref='303754358' />\n    <nd ref='31864303' />\n    <nd ref='31837618' />\n    <nd ref='666571379' />\n    <nd ref='666571381' />\n    <nd ref='666571382' />\n    <nd ref='666571384' />\n    <nd ref='2613280503' />\n    <nd ref='300460635' />\n    <nd ref='300460643' />\n    <nd ref='2922219017' />\n    <nd ref='2922218278' />\n    <nd ref='5184349789' />\n    <nd ref='5184349818' />\n    <nd ref='5184349819' />\n    <nd ref='1709638631' />\n    <nd ref='1709638627' />\n    <nd ref='1709638629' />\n    <nd ref='1709638633' />\n    <nd ref='1709638634' />\n    <nd ref='1709638635' />\n    <nd ref='1709638637' />\n    <nd ref='31860538' />\n    <nd ref='708533549' />\n    <nd ref='5184349820' />\n    <nd ref='303785344' />\n    <nd ref='708533568' />\n    <nd ref='5184349813' />\n    <nd ref='303785350' />\n    <nd ref='5184288025' />\n    <nd ref='5184288026' />\n    <nd ref='5184288024' />\n    <nd ref='5184288023' />\n    <nd ref='5184288022' />\n    <nd ref='5184288021' />\n    <nd ref='5184288020' />\n    <nd ref='5184288019' />\n    <nd ref='303785455' />\n    <nd ref='1723639081' />\n    <nd ref='303785511' />\n    <nd ref='1723639091' />\n    <nd ref='1723639055' />\n    <nd ref='1723639059' />\n    <nd ref='2450032794' />\n    <nd ref='1723639090' />\n    <nd ref='2450032804' />\n    <nd ref='2450032826' />\n    <nd ref='2450032775' />\n    <nd ref='2450032790' />\n    <nd ref='303785512' />\n    <nd ref='2450032802' />\n    <nd ref='2450032777' />\n    <nd ref='2450032842' />\n    <nd ref='2450032796' />\n    <nd ref='2450032813' />\n    <nd ref='2450032797' />\n    <nd ref='2450032806' />\n    <nd ref='2450032795' />\n    <nd ref='2450032849' />\n    <nd ref='2450032771' />\n    <nd ref='2450032793' />\n    <nd ref='2450032803' />\n    <nd ref='2450032829' />\n    <nd ref='2450032808' />\n    <nd ref='2450032814' />\n    <nd ref='2450032839' />\n    <nd ref='2450032835' />\n    <nd ref='2450032779' />\n    <nd ref='2450032843' />\n    <nd ref='2450032831' />\n    <nd ref='2450032833' />\n    <nd ref='2450032830' />\n    <nd ref='2450032772' />\n    <nd ref='2450032788' />\n    <nd ref='2450032838' />\n    <nd ref='2450032807' />\n    <nd ref='2450032827' />\n    <nd ref='2450032828' />\n    <nd ref='2450032834' />\n    <nd ref='2450032773' />\n    <nd ref='2450032837' />\n    <nd ref='2450032769' />\n    <nd ref='2450032821' />\n    <nd ref='31846028' />\n    <nd ref='2450032819' />\n    <nd ref='5551421961' />\n    <tag k='name' v='Mission Creek' />\n    <tag k='name:zh' v='米慎溪' />\n    <tag k='natural' v='water' />\n    <tag k='wikidata' v='Q6878574' />\n    <tag k='wikipedia' v='en:Mission Creek' />\n  </way>\n  <way id='617895234' timestamp='2018-08-18T23:51:35Z' uid='119881' user='clay_c' version='1' changeset='61782308'>\n    <nd ref='295222216' />\n    <nd ref='728917817' />\n    <nd ref='728917836' />\n    <nd ref='728909725' />\n    <nd ref='65371930' />\n    <nd ref='728909726' />\n    <nd ref='295222204' />\n    <nd ref='314079201' />\n    <nd ref='314079148' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='bicycle' v='no' />\n    <tag k='bridge' v='yes' />\n    <tag k='destination' v='Bay Bridge;Oakland' />\n    <tag k='destination:ref' v='I 80 East' />\n    <tag k='hgv' v='designated' />\n    <tag k='hgv:national_network' v='yes' />\n    <tag k='highway' v='motorway' />\n    <tag k='junction:ref' v='433B' />\n    <tag k='lanes' v='3' />\n    <tag k='layer' v='2' />\n    <tag k='maxspeed' v='50 mph' />\n    <tag k='name' v='James Lick Freeway' />\n    <tag k='note' v='not officially part of the Interstate Highway System (west of unbuilt I-280)' />\n    <tag k='oneway' v='yes' />\n    <tag k='source:hgv:national_network' v='Title 23: Highways Part 658 http://ecfr.gpoaccess.gov/cgi/t/text/text-idx?c=ecfr&amp;rgn=div5&amp;view=text&amp;node=23:1.0.1.7.33&amp;idno=23' />\n    <tag k='tiger:cfcc' v='A15' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='James Lick' />\n    <tag k='tiger:name_type' v='Fwy' />\n  </way>\n  <way id='620728093' timestamp='2018-10-15T22:03:40Z' uid='2237750' user='chachafish' version='2' changeset='63557245'>\n    <nd ref='2304626334' />\n    <nd ref='65340073' />\n    <tag k='cycleway' v='lane' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lcn_ref' v='36' />\n    <tag k='name' v='Division Street' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='634359201' timestamp='2018-10-15T19:08:15Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='3577621761' />\n    <nd ref='5986734223' />\n    <nd ref='5986734226' />\n    <nd ref='5986734225' />\n    <nd ref='5986734224' />\n    <nd ref='3577621763' />\n    <nd ref='5986733765' />\n    <nd ref='5986733766' />\n    <nd ref='5986733767' />\n    <nd ref='5986733764' />\n    <nd ref='5986734282' />\n    <nd ref='5986734283' />\n    <nd ref='5986734281' />\n    <nd ref='5986734280' />\n    <nd ref='5986734270' />\n    <nd ref='5986734271' />\n    <nd ref='5986734266' />\n    <nd ref='5986734269' />\n    <nd ref='5986734268' />\n    <nd ref='3577621764' />\n    <nd ref='5986734267' />\n    <nd ref='5986734265' />\n    <nd ref='5986734263' />\n    <nd ref='5986734262' />\n    <nd ref='3577621762' />\n    <nd ref='5184864687' />\n    <nd ref='5986734261' />\n    <nd ref='5449464498' />\n    <nd ref='5449464500' />\n    <nd ref='5449464501' />\n    <nd ref='5449464502' />\n    <nd ref='5449464503' />\n    <nd ref='5449464499' />\n    <nd ref='5449464497' />\n    <nd ref='3577621761' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='18' />\n  </way>\n  <way id='634359202' timestamp='2018-10-16T08:32:51Z' uid='2237750' user='chachafish' version='3' changeset='63567870'>\n    <nd ref='5184864680' />\n    <nd ref='5986734246' />\n    <nd ref='5986734245' />\n    <nd ref='5986734202' />\n    <nd ref='5986734204' />\n    <nd ref='5986734203' />\n    <nd ref='5986734206' />\n    <nd ref='5986734205' />\n    <nd ref='5986734212' />\n    <nd ref='5986734213' />\n    <nd ref='5987137901' />\n    <nd ref='5986734214' />\n    <nd ref='5986734211' />\n    <nd ref='5986734209' />\n    <nd ref='5986734210' />\n    <nd ref='5986734208' />\n    <nd ref='5986734207' />\n    <nd ref='5986734201' />\n    <nd ref='5986734241' />\n    <nd ref='5986734247' />\n    <nd ref='5184864681' />\n    <nd ref='5986734191' />\n    <nd ref='5986734193' />\n    <nd ref='5986734194' />\n    <nd ref='5986734192' />\n    <nd ref='5449464496' />\n    <nd ref='5986734195' />\n    <nd ref='5986734196' />\n    <nd ref='5986734197' />\n    <nd ref='5986734198' />\n    <nd ref='5986734200' />\n    <nd ref='5986734199' />\n    <nd ref='5184864682' />\n    <nd ref='5986734216' />\n    <nd ref='5986734218' />\n    <nd ref='5986734217' />\n    <nd ref='5986734215' />\n    <nd ref='5986734189' />\n    <nd ref='5986734190' />\n    <nd ref='5986734188' />\n    <nd ref='5986734187' />\n    <nd ref='5986734186' />\n    <nd ref='5986734185' />\n    <nd ref='5987137969' />\n    <nd ref='5986733784' />\n    <nd ref='5986733783' />\n    <nd ref='5986733781' />\n    <nd ref='5986733782' />\n    <nd ref='5986733780' />\n    <nd ref='5986733779' />\n    <nd ref='5986734220' />\n    <nd ref='5986734222' />\n    <nd ref='5986734221' />\n    <nd ref='5986734219' />\n    <nd ref='5184864683' />\n    <nd ref='5986733778' />\n    <nd ref='5986733777' />\n    <nd ref='5986733776' />\n    <nd ref='5986733775' />\n    <nd ref='5986733774' />\n    <nd ref='5986733773' />\n    <nd ref='5986733772' />\n    <nd ref='5986733771' />\n    <nd ref='5986733770' />\n    <nd ref='5986733769' />\n    <nd ref='5986733768' />\n    <nd ref='5184864686' />\n    <nd ref='5184864680' />\n    <tag k='building:material' v='glass' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21' />\n  </way>\n  <way id='634359203' timestamp='2018-10-16T21:34:13Z' uid='2237750' user='chachafish' version='4' changeset='63591679'>\n    <nd ref='5986734246' />\n    <nd ref='5986734245' />\n    <nd ref='5986734244' />\n    <nd ref='5986734243' />\n    <nd ref='5986734227' />\n    <nd ref='5986734228' />\n    <nd ref='5986734229' />\n    <nd ref='5986734230' />\n    <nd ref='5986734231' />\n    <nd ref='5986734232' />\n    <nd ref='5987137908' />\n    <nd ref='5986734233' />\n    <nd ref='5986734234' />\n    <nd ref='5986734235' />\n    <nd ref='5986734236' />\n    <nd ref='5986734237' />\n    <nd ref='5986734238' />\n    <nd ref='5986734239' />\n    <nd ref='5986734240' />\n    <nd ref='5986734241' />\n    <nd ref='5986734247' />\n    <nd ref='5986734248' />\n    <nd ref='5986734253' />\n    <nd ref='5986734252' />\n    <nd ref='5986734251' />\n    <nd ref='5986734250' />\n    <nd ref='5986734242' />\n    <nd ref='5986734246' />\n    <tag k='building' v='yes' />\n    <tag k='building:levels' v='2' />\n    <tag k='building:material' v='glass' />\n    <tag k='height' v='7.5' />\n  </way>\n  <way id='634359204' timestamp='2018-10-16T21:34:13Z' uid='2237750' user='chachafish' version='4' changeset='63591679'>\n    <nd ref='5986734243' />\n    <nd ref='5986734227' />\n    <nd ref='5986734228' />\n    <nd ref='5986734229' />\n    <nd ref='5986734230' />\n    <nd ref='5986734231' />\n    <nd ref='5986734232' />\n    <nd ref='5987137908' />\n    <nd ref='5986734233' />\n    <nd ref='5986734234' />\n    <nd ref='5986734235' />\n    <nd ref='5986734236' />\n    <nd ref='5986734237' />\n    <nd ref='5986734238' />\n    <nd ref='5986734239' />\n    <nd ref='5986734240' />\n    <nd ref='5986734248' />\n    <nd ref='5986734253' />\n    <nd ref='5991623594' />\n    <nd ref='5986734250' />\n    <nd ref='5986734242' />\n    <nd ref='5986734244' />\n    <nd ref='5986734243' />\n    <tag k='building:colour' v='#A0522D' />\n    <tag k='building:material' v='glass' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='7.5' />\n    <tag k='roof:colour' v='#89a86f' />\n  </way>\n  <way id='634359205' timestamp='2018-10-16T08:43:45Z' uid='2237750' user='chachafish' version='2' changeset='63568221'>\n    <nd ref='5986734246' />\n    <nd ref='5986734242' />\n    <nd ref='5986734244' />\n    <nd ref='5986734245' />\n    <nd ref='5986734246' />\n    <tag k='building:colour' v='#89a86f' />\n    <tag k='building:material' v='glass' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='5' />\n    <tag k='roof:colour' v='dark_grey' />\n  </way>\n  <way id='634359206' timestamp='2018-10-16T08:43:45Z' uid='2237750' user='chachafish' version='2' changeset='63568221'>\n    <nd ref='5986734247' />\n    <nd ref='5986734248' />\n    <nd ref='5986734240' />\n    <nd ref='5986734241' />\n    <nd ref='5986734247' />\n    <tag k='building:colour' v='#89a86f' />\n    <tag k='building:material' v='glass' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='5' />\n    <tag k='roof:colour' v='dark_grey' />\n  </way>\n  <way id='634359207' timestamp='2018-10-16T08:32:51Z' uid='2237750' user='chachafish' version='2' changeset='63567870'>\n    <nd ref='5449464501' />\n    <nd ref='5449464500' />\n    <nd ref='5449464498' />\n    <nd ref='5986734261' />\n    <nd ref='5184864687' />\n    <nd ref='3577621762' />\n    <nd ref='5986734262' />\n    <nd ref='5986734263' />\n    <nd ref='5986734265' />\n    <nd ref='5986734267' />\n    <nd ref='3577621764' />\n    <nd ref='5986734268' />\n    <nd ref='5986734269' />\n    <nd ref='5986734266' />\n    <nd ref='5986734271' />\n    <nd ref='5986734270' />\n    <nd ref='5986734280' />\n    <nd ref='5986734285' />\n    <nd ref='5986734284' />\n    <nd ref='5986734286' />\n    <nd ref='5986734287' />\n    <nd ref='5986734272' />\n    <nd ref='5986734264' />\n    <nd ref='5986734260' />\n    <nd ref='5986734259' />\n    <nd ref='5986734258' />\n    <nd ref='5986734257' />\n    <nd ref='5986734249' />\n    <nd ref='5986734254' />\n    <nd ref='5986734279' />\n    <nd ref='5986734282' />\n    <nd ref='5986733764' />\n    <nd ref='5986733767' />\n    <nd ref='5986733766' />\n    <nd ref='5986733765' />\n    <nd ref='3577621763' />\n    <nd ref='5986734224' />\n    <nd ref='5986734225' />\n    <nd ref='5986734226' />\n    <nd ref='5986734223' />\n    <nd ref='3577621761' />\n    <nd ref='5449464497' />\n    <nd ref='5449464499' />\n    <nd ref='5449464503' />\n    <nd ref='5449464502' />\n    <nd ref='5449464501' />\n    <tag k='building:material' v='glass' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21' />\n    <tag k='min_height' v='18' />\n  </way>\n  <way id='634359208' timestamp='2018-10-16T21:34:14Z' uid='2237750' user='chachafish' version='3' changeset='63591679'>\n    <nd ref='5986734250' />\n    <nd ref='5986734251' />\n    <nd ref='5986734252' />\n    <nd ref='5986734253' />\n    <nd ref='5991623594' />\n    <nd ref='5986734250' />\n    <tag k='building:colour' v='#4c4c4c' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='6' />\n    <tag k='min_height' v='5.9' />\n    <tag k='roof:colour' v='#4c4c4c' />\n  </way>\n  <way id='634359209' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734255' />\n    <nd ref='5986734256' />\n    <nd ref='5986734273' />\n    <nd ref='5986734274' />\n    <nd ref='5986734255' />\n    <tag k='building:colour' v='#A0522D' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='24' />\n    <tag k='min_height' v='18' />\n  </way>\n  <way id='634359210' timestamp='2018-10-16T08:40:55Z' uid='2237750' user='chachafish' version='3' changeset='63568124'>\n    <nd ref='5986734275' />\n    <nd ref='5986734276' />\n    <nd ref='5986734277' />\n    <nd ref='5986734278' />\n    <nd ref='5986734275' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21' />\n    <tag k='min_height' v='18' />\n  </way>\n  <way id='634359211' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734279' />\n    <nd ref='5986734287' />\n    <nd ref='5986734286' />\n    <nd ref='5986734284' />\n    <nd ref='5986734285' />\n    <nd ref='5986734280' />\n    <nd ref='5986734281' />\n    <nd ref='5986734283' />\n    <nd ref='5986734282' />\n    <nd ref='5986734279' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19' />\n    <tag k='min_height' v='18' />\n  </way>\n  <way id='634359212' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734288' />\n    <nd ref='5986734289' />\n    <nd ref='5986734293' />\n    <nd ref='5986734294' />\n    <nd ref='5986734296' />\n    <nd ref='5986734297' />\n    <nd ref='5986734295' />\n    <nd ref='5986734298' />\n    <nd ref='5986734300' />\n    <nd ref='5986734301' />\n    <nd ref='5986734299' />\n    <nd ref='5986734302' />\n    <nd ref='5986734304' />\n    <nd ref='5986734305' />\n    <nd ref='5986734303' />\n    <nd ref='5986734292' />\n    <nd ref='5986734290' />\n    <nd ref='5986734291' />\n    <nd ref='5986734288' />\n    <tag k='building:colour' v='#4c4c4c' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='18.5' />\n    <tag k='min_height' v='18' />\n    <tag k='roof:colour' v='#4c4c4c' />\n  </way>\n  <way id='634359213' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734306' />\n    <nd ref='5986734307' />\n    <nd ref='5986734308' />\n    <nd ref='5986734309' />\n    <nd ref='5986734310' />\n    <nd ref='5986734311' />\n    <nd ref='5986734312' />\n    <nd ref='5986734313' />\n    <nd ref='5986734314' />\n    <nd ref='5986734315' />\n    <nd ref='5986734316' />\n    <nd ref='5986734317' />\n    <nd ref='5986734318' />\n    <nd ref='5986734319' />\n    <nd ref='5986734320' />\n    <nd ref='5986734321' />\n    <nd ref='5986734322' />\n    <nd ref='5986734323' />\n    <nd ref='5986734306' />\n    <tag k='building:colour' v='#4c4c4c' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='18.5' />\n    <tag k='min_height' v='18' />\n    <tag k='roof:colour' v='#4c4c4c' />\n  </way>\n  <way id='634359214' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734324' />\n    <nd ref='5986734325' />\n    <nd ref='5986734326' />\n    <nd ref='5986734327' />\n    <nd ref='5986734324' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359215' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734328' />\n    <nd ref='5986734329' />\n    <nd ref='5986734330' />\n    <nd ref='5986734331' />\n    <nd ref='5986734328' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359216' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734332' />\n    <nd ref='5986734333' />\n    <nd ref='5986734334' />\n    <nd ref='5986734335' />\n    <nd ref='5986734332' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359217' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734336' />\n    <nd ref='5986734337' />\n    <nd ref='5986734338' />\n    <nd ref='5986734339' />\n    <nd ref='5986734336' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359218' timestamp='2018-10-15T19:08:16Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734340' />\n    <nd ref='5986734341' />\n    <nd ref='5986734342' />\n    <nd ref='5986734343' />\n    <nd ref='5986734340' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359219' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734344' />\n    <nd ref='5986734345' />\n    <nd ref='5986734346' />\n    <nd ref='5986734347' />\n    <nd ref='5986734344' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359220' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734356' />\n    <nd ref='5986734357' />\n    <nd ref='5986734359' />\n    <nd ref='5986734348' />\n    <nd ref='5986734349' />\n    <nd ref='5986734351' />\n    <nd ref='5986734356' />\n    <tag k='building:colour' v='#FFF8DC' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#FFF8DC' />\n  </way>\n  <way id='634359221' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734350' />\n    <nd ref='5986734363' />\n    <nd ref='5986734358' />\n    <nd ref='5986734362' />\n    <nd ref='5986734361' />\n    <nd ref='5986734360' />\n    <nd ref='5986734359' />\n    <nd ref='5986734357' />\n    <nd ref='5986734356' />\n    <nd ref='5986734351' />\n    <nd ref='5986734352' />\n    <nd ref='5986734355' />\n    <nd ref='5986734354' />\n    <nd ref='5986734353' />\n    <nd ref='5986734350' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359222' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734366' />\n    <nd ref='5986734364' />\n    <nd ref='5986734365' />\n    <nd ref='5986734367' />\n    <nd ref='5986734366' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359223' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734368' />\n    <nd ref='5986734369' />\n    <nd ref='5986734370' />\n    <nd ref='5986734371' />\n    <nd ref='5986734368' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359224' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734372' />\n    <nd ref='5986734373' />\n    <nd ref='5986734374' />\n    <nd ref='5986734375' />\n    <nd ref='5986734372' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359225' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734376' />\n    <nd ref='5986734377' />\n    <nd ref='5986734378' />\n    <nd ref='5986734379' />\n    <nd ref='5986734376' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359226' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734380' />\n    <nd ref='5986734381' />\n    <nd ref='5986734382' />\n    <nd ref='5986734383' />\n    <nd ref='5986734380' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359227' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734384' />\n    <nd ref='5986734385' />\n    <nd ref='5986734386' />\n    <nd ref='5986734387' />\n    <nd ref='5986734384' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359228' timestamp='2018-10-15T19:08:17Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734388' />\n    <nd ref='5986734389' />\n    <nd ref='5986734390' />\n    <nd ref='5986734391' />\n    <nd ref='5986734388' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359229' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734392' />\n    <nd ref='5986734393' />\n    <nd ref='5986734394' />\n    <nd ref='5986734395' />\n    <nd ref='5986734392' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359231' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734396' />\n    <nd ref='5986734397' />\n    <nd ref='5986734398' />\n    <nd ref='5986734399' />\n    <nd ref='5986734396' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359232' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734400' />\n    <nd ref='5986734401' />\n    <nd ref='5986734402' />\n    <nd ref='5986734403' />\n    <nd ref='5986734400' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359233' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734404' />\n    <nd ref='5986734405' />\n    <nd ref='5986734406' />\n    <nd ref='5986734407' />\n    <nd ref='5986734404' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359234' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734408' />\n    <nd ref='5986734409' />\n    <nd ref='5986734410' />\n    <nd ref='5986734411' />\n    <nd ref='5986734408' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359235' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734412' />\n    <nd ref='5986734413' />\n    <nd ref='5986734414' />\n    <nd ref='5986734415' />\n    <nd ref='5986734412' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359236' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734416' />\n    <nd ref='5986734417' />\n    <nd ref='5986734418' />\n    <nd ref='5986734419' />\n    <nd ref='5986734416' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359237' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734420' />\n    <nd ref='5986734421' />\n    <nd ref='5986734422' />\n    <nd ref='5986734423' />\n    <nd ref='5986734420' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359238' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734424' />\n    <nd ref='5986734425' />\n    <nd ref='5986734426' />\n    <nd ref='5986734427' />\n    <nd ref='5986734424' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359239' timestamp='2018-10-15T19:08:18Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734428' />\n    <nd ref='5986734429' />\n    <nd ref='5986734430' />\n    <nd ref='5986734431' />\n    <nd ref='5986734428' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359240' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734432' />\n    <nd ref='5986734433' />\n    <nd ref='5986734434' />\n    <nd ref='5986734435' />\n    <nd ref='5986734432' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359241' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734436' />\n    <nd ref='5986734437' />\n    <nd ref='5986734438' />\n    <nd ref='5986734439' />\n    <nd ref='5986734436' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359242' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734440' />\n    <nd ref='5986734441' />\n    <nd ref='5986734442' />\n    <nd ref='5986734443' />\n    <nd ref='5986734440' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359243' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734444' />\n    <nd ref='5986734445' />\n    <nd ref='5986734446' />\n    <nd ref='5986734447' />\n    <nd ref='5986734444' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359244' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734448' />\n    <nd ref='5986734449' />\n    <nd ref='5986734450' />\n    <nd ref='5986734451' />\n    <nd ref='5986734448' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359245' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734452' />\n    <nd ref='5986734453' />\n    <nd ref='5986734454' />\n    <nd ref='5986734455' />\n    <nd ref='5986734452' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359246' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734456' />\n    <nd ref='5986734457' />\n    <nd ref='5986734458' />\n    <nd ref='5986734459' />\n    <nd ref='5986734456' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359247' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734460' />\n    <nd ref='5986734461' />\n    <nd ref='5986734462' />\n    <nd ref='5986734463' />\n    <nd ref='5986734460' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359248' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734464' />\n    <nd ref='5986734465' />\n    <nd ref='5986734466' />\n    <nd ref='5986734467' />\n    <nd ref='5986734464' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359249' timestamp='2018-10-15T19:08:19Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734468' />\n    <nd ref='5986734469' />\n    <nd ref='5986734470' />\n    <nd ref='5986734471' />\n    <nd ref='5986734468' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359250' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734472' />\n    <nd ref='5986734473' />\n    <nd ref='5986734474' />\n    <nd ref='5986734475' />\n    <nd ref='5986734472' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359251' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734476' />\n    <nd ref='5986734477' />\n    <nd ref='5986734478' />\n    <nd ref='5986734479' />\n    <nd ref='5986734476' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359252' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734480' />\n    <nd ref='5986734481' />\n    <nd ref='5986734482' />\n    <nd ref='5986734483' />\n    <nd ref='5986734480' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359253' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734484' />\n    <nd ref='5986734485' />\n    <nd ref='5986734486' />\n    <nd ref='5986734487' />\n    <nd ref='5986734484' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359254' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734488' />\n    <nd ref='5986734489' />\n    <nd ref='5986734490' />\n    <nd ref='5986734491' />\n    <nd ref='5986734488' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359255' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734492' />\n    <nd ref='5986734493' />\n    <nd ref='5986734494' />\n    <nd ref='5986734495' />\n    <nd ref='5986734492' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359256' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734496' />\n    <nd ref='5986734497' />\n    <nd ref='5986734498' />\n    <nd ref='5986734499' />\n    <nd ref='5986734496' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359257' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734500' />\n    <nd ref='5986734501' />\n    <nd ref='5986734502' />\n    <nd ref='5986734503' />\n    <nd ref='5986734500' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359258' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734504' />\n    <nd ref='5986734505' />\n    <nd ref='5986734506' />\n    <nd ref='5986734507' />\n    <nd ref='5986734504' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359259' timestamp='2018-10-15T19:08:20Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734508' />\n    <nd ref='5986734509' />\n    <nd ref='5986734510' />\n    <nd ref='5986734511' />\n    <nd ref='5986734508' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359260' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734512' />\n    <nd ref='5986734513' />\n    <nd ref='5986734514' />\n    <nd ref='5986734515' />\n    <nd ref='5986734512' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359261' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734516' />\n    <nd ref='5986734517' />\n    <nd ref='5986734518' />\n    <nd ref='5986734519' />\n    <nd ref='5986734516' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359262' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734520' />\n    <nd ref='5986734521' />\n    <nd ref='5986734522' />\n    <nd ref='5986734523' />\n    <nd ref='5986734520' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359263' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734524' />\n    <nd ref='5986734525' />\n    <nd ref='5986734526' />\n    <nd ref='5986734527' />\n    <nd ref='5986734524' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359264' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734528' />\n    <nd ref='5986734529' />\n    <nd ref='5986734530' />\n    <nd ref='5986734531' />\n    <nd ref='5986734528' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359265' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734532' />\n    <nd ref='5986734533' />\n    <nd ref='5986734534' />\n    <nd ref='5986734535' />\n    <nd ref='5986734532' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359266' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734536' />\n    <nd ref='5986734537' />\n    <nd ref='5986734538' />\n    <nd ref='5986734539' />\n    <nd ref='5986734536' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359267' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734540' />\n    <nd ref='5986734541' />\n    <nd ref='5986734542' />\n    <nd ref='5986734543' />\n    <nd ref='5986734540' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359268' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734544' />\n    <nd ref='5986734545' />\n    <nd ref='5986734546' />\n    <nd ref='5986734547' />\n    <nd ref='5986734544' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359269' timestamp='2018-10-15T19:08:21Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734548' />\n    <nd ref='5986734549' />\n    <nd ref='5986734550' />\n    <nd ref='5986734551' />\n    <nd ref='5986734548' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359270' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734552' />\n    <nd ref='5986734553' />\n    <nd ref='5986734554' />\n    <nd ref='5986734555' />\n    <nd ref='5986734552' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359271' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734556' />\n    <nd ref='5986734557' />\n    <nd ref='5986734558' />\n    <nd ref='5986734559' />\n    <nd ref='5986734556' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359272' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734560' />\n    <nd ref='5986734561' />\n    <nd ref='5986734562' />\n    <nd ref='5986734563' />\n    <nd ref='5986734560' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359273' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734564' />\n    <nd ref='5986734565' />\n    <nd ref='5986734566' />\n    <nd ref='5986734567' />\n    <nd ref='5986734564' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359274' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734568' />\n    <nd ref='5986734569' />\n    <nd ref='5986734570' />\n    <nd ref='5986734571' />\n    <nd ref='5986734568' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359275' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734572' />\n    <nd ref='5986734573' />\n    <nd ref='5986734574' />\n    <nd ref='5986734575' />\n    <nd ref='5986734572' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359276' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734576' />\n    <nd ref='5986734577' />\n    <nd ref='5986734578' />\n    <nd ref='5986734579' />\n    <nd ref='5986734576' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359277' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734580' />\n    <nd ref='5986734581' />\n    <nd ref='5986734582' />\n    <nd ref='5986734583' />\n    <nd ref='5986734580' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='19.3' />\n    <tag k='min_height' v='19' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359278' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734584' />\n    <nd ref='5986734585' />\n    <nd ref='5986734586' />\n    <nd ref='5986734587' />\n    <nd ref='5986734584' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359279' timestamp='2018-10-15T19:08:22Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734588' />\n    <nd ref='5986734589' />\n    <nd ref='5986734590' />\n    <nd ref='5986734591' />\n    <nd ref='5986734588' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359280' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734592' />\n    <nd ref='5986734593' />\n    <nd ref='5986734594' />\n    <nd ref='5986734595' />\n    <nd ref='5986734592' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359281' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734596' />\n    <nd ref='5986734597' />\n    <nd ref='5986734598' />\n    <nd ref='5986734599' />\n    <nd ref='5986734596' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359282' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734600' />\n    <nd ref='5986734601' />\n    <nd ref='5986734602' />\n    <nd ref='5986734603' />\n    <nd ref='5986734600' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359283' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734604' />\n    <nd ref='5986734605' />\n    <nd ref='5986734606' />\n    <nd ref='5986734607' />\n    <nd ref='5986734604' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359284' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734608' />\n    <nd ref='5986734609' />\n    <nd ref='5986734610' />\n    <nd ref='5986734611' />\n    <nd ref='5986734608' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359285' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734612' />\n    <nd ref='5986734613' />\n    <nd ref='5986734614' />\n    <nd ref='5986734615' />\n    <nd ref='5986734612' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359286' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734616' />\n    <nd ref='5986734617' />\n    <nd ref='5986734618' />\n    <nd ref='5986734619' />\n    <nd ref='5986734616' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359287' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734620' />\n    <nd ref='5986734621' />\n    <nd ref='5986734622' />\n    <nd ref='5986734623' />\n    <nd ref='5986734620' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359288' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734624' />\n    <nd ref='5986734625' />\n    <nd ref='5986734626' />\n    <nd ref='5986734627' />\n    <nd ref='5986734624' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359289' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734628' />\n    <nd ref='5986734629' />\n    <nd ref='5986734630' />\n    <nd ref='5986734631' />\n    <nd ref='5986734628' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359290' timestamp='2018-10-15T19:08:23Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734632' />\n    <nd ref='5986734633' />\n    <nd ref='5986734634' />\n    <nd ref='5986734635' />\n    <nd ref='5986734637' />\n    <nd ref='5986734639' />\n    <nd ref='5986734638' />\n    <nd ref='5986734636' />\n    <nd ref='5986734632' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359291' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734640' />\n    <nd ref='5986734647' />\n    <nd ref='5986734646' />\n    <nd ref='5986734645' />\n    <nd ref='5986734644' />\n    <nd ref='5986734641' />\n    <nd ref='5986734642' />\n    <nd ref='5986734643' />\n    <nd ref='5986734640' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359292' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734648' />\n    <nd ref='5986734649' />\n    <nd ref='5986734650' />\n    <nd ref='5986734651' />\n    <nd ref='5986734648' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634359293' timestamp='2018-10-15T19:08:24Z' uid='2237750' user='chachafish' version='1' changeset='63553269'>\n    <nd ref='5986734652' />\n    <nd ref='5986734653' />\n    <nd ref='5986734654' />\n    <nd ref='5986734655' />\n    <nd ref='5986734652' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634411991' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118485' />\n    <nd ref='5987118486' />\n    <nd ref='5987118487' />\n    <nd ref='5987118488' />\n    <nd ref='5987118489' />\n    <nd ref='5987118490' />\n    <nd ref='5987118491' />\n    <nd ref='5987118492' />\n    <nd ref='5987118493' />\n    <nd ref='5987118494' />\n    <nd ref='5987118485' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634411992' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118495' />\n    <nd ref='5987118496' />\n    <nd ref='5987118497' />\n    <nd ref='5987118498' />\n    <nd ref='5987118499' />\n    <nd ref='5987118500' />\n    <nd ref='5987118495' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634411993' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118501' />\n    <nd ref='5987118502' />\n    <nd ref='5987118503' />\n    <nd ref='5987118504' />\n    <nd ref='5987118505' />\n    <nd ref='5987118506' />\n    <nd ref='5987118501' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634411994' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118507' />\n    <nd ref='5987118508' />\n    <nd ref='5987118509' />\n    <nd ref='5987118510' />\n    <nd ref='5987118511' />\n    <nd ref='5987118512' />\n    <nd ref='5987118507' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634411995' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118513' />\n    <nd ref='5987118514' />\n    <nd ref='5987118515' />\n    <nd ref='5987118516' />\n    <nd ref='5987118517' />\n    <nd ref='5987118518' />\n    <nd ref='5987118519' />\n    <nd ref='5987118520' />\n    <nd ref='5987118521' />\n    <nd ref='5987118522' />\n    <nd ref='5987118513' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634411996' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118523' />\n    <nd ref='5987118524' />\n    <nd ref='5987118525' />\n    <nd ref='5987118526' />\n    <nd ref='5987118527' />\n    <nd ref='5987118528' />\n    <nd ref='5987118523' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634411997' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118529' />\n    <nd ref='5987118530' />\n    <nd ref='5987118531' />\n    <nd ref='5987118532' />\n    <nd ref='5987118533' />\n    <nd ref='5987118534' />\n    <nd ref='5987118529' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634411998' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118535' />\n    <nd ref='5987118536' />\n    <nd ref='5987118537' />\n    <nd ref='5987118538' />\n    <nd ref='5987118539' />\n    <nd ref='5987118540' />\n    <nd ref='5987118535' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634411999' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118546' />\n    <nd ref='5987119182' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412000' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118547' />\n    <nd ref='5987118548' />\n    <nd ref='5987118549' />\n    <nd ref='5987118550' />\n    <nd ref='5987118547' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412001' timestamp='2018-10-15T22:03:33Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118551' />\n    <nd ref='5987118552' />\n    <nd ref='5987118553' />\n    <nd ref='5987118554' />\n    <nd ref='5987118551' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412002' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118555' />\n    <nd ref='5987118556' />\n    <nd ref='5987118557' />\n    <nd ref='5987118558' />\n    <nd ref='5987118555' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412003' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118559' />\n    <nd ref='5987118560' />\n    <nd ref='5987118561' />\n    <nd ref='5987118562' />\n    <nd ref='5987118563' />\n    <nd ref='5987118564' />\n    <nd ref='5987118559' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412004' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118565' />\n    <nd ref='5987118566' />\n    <nd ref='5987118567' />\n    <nd ref='5987118568' />\n    <nd ref='5987118565' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412005' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118569' />\n    <nd ref='5987118570' />\n    <nd ref='5987118571' />\n    <nd ref='5987118572' />\n    <nd ref='5987118569' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412006' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118573' />\n    <nd ref='5987118574' />\n    <nd ref='5987118575' />\n    <nd ref='5987118576' />\n    <nd ref='5987118573' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412007' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118577' />\n    <nd ref='5987118578' />\n    <nd ref='5987118579' />\n    <nd ref='5987118580' />\n    <nd ref='5987118577' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412008' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118581' />\n    <nd ref='5987118582' />\n    <nd ref='5987118583' />\n    <nd ref='5987118584' />\n    <nd ref='5987118785' />\n    <nd ref='5987118786' />\n    <nd ref='5987118787' />\n    <nd ref='5987118788' />\n    <nd ref='5987118789' />\n    <nd ref='5987118790' />\n    <nd ref='5987118791' />\n    <nd ref='5987118792' />\n    <nd ref='5987118581' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412009' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118795' />\n    <nd ref='5987118796' />\n    <nd ref='5987118793' />\n    <nd ref='5987118794' />\n    <nd ref='5987118795' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412010' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118797' />\n    <nd ref='5987118798' />\n    <nd ref='5987118799' />\n    <nd ref='5987118800' />\n    <nd ref='5987118797' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412011' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118801' />\n    <nd ref='5987118802' />\n    <nd ref='5987118803' />\n    <nd ref='5987118804' />\n    <nd ref='5987118801' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412012' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118805' />\n    <nd ref='5987118806' />\n    <nd ref='5987118807' />\n    <nd ref='5987118808' />\n    <nd ref='5987118805' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412013' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118809' />\n    <nd ref='5987118810' />\n    <nd ref='5987118811' />\n    <nd ref='5987118812' />\n    <nd ref='5987118809' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412014' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118813' />\n    <nd ref='5987118814' />\n    <nd ref='5987118815' />\n    <nd ref='5987118816' />\n    <nd ref='5987118813' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412015' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118817' />\n    <nd ref='5987118818' />\n    <nd ref='5987118819' />\n    <nd ref='5987118820' />\n    <nd ref='5987118817' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412016' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118821' />\n    <nd ref='5987118822' />\n    <nd ref='5987118823' />\n    <nd ref='5987118824' />\n    <nd ref='5987118821' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412017' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118825' />\n    <nd ref='5987118826' />\n    <nd ref='5987118827' />\n    <nd ref='5987118828' />\n    <nd ref='5987118825' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412018' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118829' />\n    <nd ref='5987118830' />\n    <nd ref='5987118831' />\n    <nd ref='5987118832' />\n    <nd ref='5987118829' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412019' timestamp='2018-10-15T22:03:34Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118833' />\n    <nd ref='5987118834' />\n    <nd ref='5987118835' />\n    <nd ref='5987118836' />\n    <nd ref='5987118833' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412020' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118837' />\n    <nd ref='5987118838' />\n    <nd ref='5987118839' />\n    <nd ref='5987118840' />\n    <nd ref='5987118837' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412021' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118841' />\n    <nd ref='5987118842' />\n    <nd ref='5987118843' />\n    <nd ref='5987118844' />\n    <nd ref='5987118841' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412022' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118845' />\n    <nd ref='5987118846' />\n    <nd ref='5987118847' />\n    <nd ref='5987118848' />\n    <nd ref='5987118845' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412023' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118849' />\n    <nd ref='5987118850' />\n    <nd ref='5987118851' />\n    <nd ref='5987118852' />\n    <nd ref='5987118849' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412024' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118853' />\n    <nd ref='5987118854' />\n    <nd ref='5987118855' />\n    <nd ref='5987118856' />\n    <nd ref='5987118853' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412025' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118857' />\n    <nd ref='5987118858' />\n    <nd ref='5987118859' />\n    <nd ref='5987118860' />\n    <nd ref='5987118857' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412026' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118861' />\n    <nd ref='5987118862' />\n    <nd ref='5987118863' />\n    <nd ref='5987118864' />\n    <nd ref='5987118861' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412027' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118865' />\n    <nd ref='5987118866' />\n    <nd ref='5987118867' />\n    <nd ref='5987118868' />\n    <nd ref='5987118865' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412028' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118869' />\n    <nd ref='5987118870' />\n    <nd ref='5987118871' />\n    <nd ref='5987118872' />\n    <nd ref='5987118869' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412029' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118873' />\n    <nd ref='5987118874' />\n    <nd ref='5987118875' />\n    <nd ref='5987118876' />\n    <nd ref='5987118873' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412030' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118880' />\n    <nd ref='5987118881' />\n    <tag k='barrier' v='wall' />\n    <tag k='height' v='.3' />\n    <tag k='width' v='.2' />\n  </way>\n  <way id='634412031' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118882' />\n    <nd ref='5987118878' />\n    <tag k='barrier' v='wall' />\n    <tag k='height' v='.3' />\n    <tag k='width' v='.2' />\n  </way>\n  <way id='634412032' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118880' />\n    <nd ref='5987118881' />\n    <tag k='barrier' v='fence' />\n    <tag k='height' v='.6' />\n    <tag k='min_height' v='.3' />\n  </way>\n  <way id='634412033' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118882' />\n    <nd ref='5987118878' />\n    <tag k='barrier' v='fence' />\n    <tag k='height' v='.6' />\n    <tag k='min_height' v='.3' />\n  </way>\n  <way id='634412034' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='3' changeset='63558513'>\n    <nd ref='5987118883' />\n    <nd ref='5987119088' />\n    <nd ref='5987119095' />\n    <nd ref='5987118546' />\n    <nd ref='5987119394' />\n    <nd ref='5987118884' />\n    <nd ref='5987119388' />\n    <nd ref='5987265747' />\n    <nd ref='5987119085' />\n    <nd ref='5987137963' />\n    <nd ref='5987118543' />\n    <nd ref='5987119096' />\n    <nd ref='5987119086' />\n    <nd ref='5987119087' />\n    <nd ref='5987119390' />\n    <nd ref='5987118883' />\n    <tag k='footway' v='sidewalk' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412035' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='3532718886' />\n    <nd ref='5987119087' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412036' timestamp='2018-10-15T22:03:35Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5487354239' />\n    <nd ref='2304626312' />\n    <nd ref='5987119088' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412037' timestamp='2018-10-16T00:41:01Z' uid='2237750' user='chachafish' version='2' changeset='63559070'>\n    <nd ref='65303878' />\n    <nd ref='5987119389' />\n    <nd ref='2304626312' />\n    <tag k='cycleway' v='shared_lane' />\n    <tag k='highway' v='residential' />\n    <tag k='lcn_ref' v='123' />\n    <tag k='name' v='Henry Adams Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Henry Adams' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='634412038' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5487354242' />\n    <nd ref='2304626320' />\n    <nd ref='5487354238' />\n    <tag k='footway' v='sidewalk' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412039' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119091' />\n    <nd ref='5987119092' />\n    <nd ref='5987119093' />\n    <nd ref='5987119094' />\n    <nd ref='5987119091' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412040' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='2' changeset='63558513'>\n    <nd ref='5987118542' />\n    <nd ref='5987119097' />\n    <nd ref='5987265745' />\n    <nd ref='5987119098' />\n    <nd ref='5987119099' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412041' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='2' changeset='63558513'>\n    <nd ref='5987119100' />\n    <nd ref='5987119101' />\n    <nd ref='5987265744' />\n    <nd ref='5987119102' />\n    <nd ref='5987119103' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412042' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119098' />\n    <nd ref='5987119104' />\n    <nd ref='5987119105' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412043' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119105' />\n    <nd ref='5987119101' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412044' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119140' />\n    <nd ref='5987119141' />\n    <nd ref='5987119142' />\n    <nd ref='5987119143' />\n    <nd ref='5987119140' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412045' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119144' />\n    <nd ref='5987119145' />\n    <nd ref='5987119146' />\n    <nd ref='5987119147' />\n    <nd ref='5987119144' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412046' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119148' />\n    <nd ref='5987119149' />\n    <nd ref='5987119150' />\n    <nd ref='5987119151' />\n    <nd ref='5987119148' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412047' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119152' />\n    <nd ref='5987119153' />\n    <nd ref='5987119154' />\n    <nd ref='5987119155' />\n    <nd ref='5987119152' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412048' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119156' />\n    <nd ref='5987119157' />\n    <nd ref='5987119158' />\n    <nd ref='5987119159' />\n    <nd ref='5987119156' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412049' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119160' />\n    <nd ref='5987119161' />\n    <nd ref='5987119162' />\n    <nd ref='5987119163' />\n    <nd ref='5987119160' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412050' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119164' />\n    <nd ref='5987119167' />\n    <nd ref='5987119165' />\n    <nd ref='5987119166' />\n    <nd ref='5987119164' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412051' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119168' />\n    <nd ref='5987119169' />\n    <nd ref='5987119170' />\n    <nd ref='5987119171' />\n    <nd ref='5987119168' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412052' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119172' />\n    <nd ref='5987119173' />\n    <nd ref='5987119176' />\n    <nd ref='5987119177' />\n    <nd ref='5987119174' />\n    <nd ref='5987119175' />\n    <nd ref='5987119172' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412053' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119178' />\n    <nd ref='5987119179' />\n    <nd ref='5987119180' />\n    <nd ref='5987119181' />\n    <nd ref='5987119178' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412054' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119182' />\n    <nd ref='5987119183' />\n    <tag k='highway' v='steps' />\n    <tag k='incline' v='up' />\n  </way>\n  <way id='634412055' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='2' changeset='63558513'>\n    <nd ref='5987119183' />\n    <nd ref='5987119386' />\n    <nd ref='5987118544' />\n    <nd ref='5987118545' />\n    <nd ref='5987265746' />\n    <nd ref='5987119103' />\n    <nd ref='5987119100' />\n    <nd ref='5987119099' />\n    <nd ref='5987118541' />\n    <nd ref='5987118542' />\n    <nd ref='5987118543' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412056' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119184' />\n    <nd ref='5987119386' />\n    <nd ref='5987119385' />\n    <nd ref='5987119387' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412057' timestamp='2018-10-15T22:03:36Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119387' />\n    <nd ref='5987119397' />\n    <nd ref='5987119388' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412058' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118877' />\n    <nd ref='5987118879' />\n    <tag k='barrier' v='wall' />\n    <tag k='height' v='.3' />\n    <tag k='width' v='.2' />\n  </way>\n  <way id='634412059' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987118877' />\n    <nd ref='5987118879' />\n    <tag k='barrier' v='fence' />\n    <tag k='height' v='.6' />\n    <tag k='min_height' v='.3' />\n  </way>\n  <way id='634412060' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119390' />\n    <nd ref='5987119391' />\n    <nd ref='5987119392' />\n    <nd ref='5987119393' />\n    <nd ref='5987119184' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412061' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119394' />\n    <nd ref='5987119395' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412062' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119395' />\n    <nd ref='5987119396' />\n    <tag k='highway' v='steps' />\n    <tag k='incline' v='up' />\n  </way>\n  <way id='634412063' timestamp='2018-10-16T21:34:14Z' uid='2237750' user='chachafish' version='2' changeset='63591679'>\n    <nd ref='5987119396' />\n    <nd ref='5987119397' />\n    <nd ref='5991623594' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634412064' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119400' />\n    <nd ref='5987119401' />\n    <nd ref='5987119402' />\n    <nd ref='5987119403' />\n    <nd ref='5987119400' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412065' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119404' />\n    <nd ref='5987119405' />\n    <nd ref='5987119406' />\n    <nd ref='5987119407' />\n    <nd ref='5987119404' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412066' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119408' />\n    <nd ref='5987119409' />\n    <nd ref='5987119410' />\n    <nd ref='5987119411' />\n    <nd ref='5987119408' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412067' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119412' />\n    <nd ref='5987119413' />\n    <nd ref='5987119414' />\n    <nd ref='5987119415' />\n    <nd ref='5987119412' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412068' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119416' />\n    <nd ref='5987119417' />\n    <nd ref='5987119418' />\n    <nd ref='5987119419' />\n    <nd ref='5987119416' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412069' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119420' />\n    <nd ref='5987119421' />\n    <nd ref='5987119422' />\n    <nd ref='5987119423' />\n    <nd ref='5987119420' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634412070' timestamp='2018-10-15T22:03:37Z' uid='2237750' user='chachafish' version='1' changeset='63557245'>\n    <nd ref='5987119447' />\n    <nd ref='5987119448' />\n    <nd ref='5987119449' />\n    <nd ref='5987119450' />\n    <nd ref='5987119447' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634415563' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137902' />\n    <nd ref='5987137903' />\n    <nd ref='5987137904' />\n    <nd ref='5987137905' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634415564' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137902' />\n    <nd ref='5987137906' />\n    <nd ref='5987137907' />\n    <nd ref='5987137905' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634415565' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137905' />\n    <nd ref='5987137901' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634415566' timestamp='2018-10-16T21:34:14Z' uid='2237750' user='chachafish' version='2' changeset='63591679'>\n    <nd ref='5991623594' />\n    <nd ref='5987137908' />\n    <tag k='highway' v='footway' />\n    <tag k='tunnel' v='building_passage' />\n  </way>\n  <way id='634415567' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137908' />\n    <nd ref='5987137902' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634415568' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137909' />\n    <nd ref='5987137910' />\n    <nd ref='5987137911' />\n    <nd ref='5987137912' />\n    <nd ref='5987137913' />\n    <nd ref='5987137914' />\n    <nd ref='5987137915' />\n    <nd ref='5987137916' />\n    <nd ref='5987137917' />\n    <nd ref='5987137918' />\n    <nd ref='5987137956' />\n    <nd ref='5987137960' />\n    <nd ref='5987137962' />\n    <nd ref='5987137961' />\n    <nd ref='5987137959' />\n    <nd ref='5987137958' />\n    <nd ref='5987137919' />\n    <nd ref='5987137955' />\n    <nd ref='5987137957' />\n    <nd ref='5987137920' />\n    <nd ref='5987137909' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634415569' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137948' />\n    <nd ref='5987137949' />\n    <nd ref='5987137944' />\n    <nd ref='5987137943' />\n    <nd ref='5987137946' />\n    <nd ref='5987137945' />\n    <nd ref='5987137942' />\n    <nd ref='5987137941' />\n    <nd ref='5987137947' />\n    <nd ref='5987137940' />\n    <nd ref='5987137934' />\n    <nd ref='5987137933' />\n    <nd ref='5987137939' />\n    <nd ref='5987137938' />\n    <nd ref='5987137937' />\n    <nd ref='5987137936' />\n    <nd ref='5987137935' />\n    <nd ref='5987137932' />\n    <nd ref='5987137931' />\n    <nd ref='5987137930' />\n    <nd ref='5987137950' />\n    <nd ref='5987137951' />\n    <nd ref='5987137952' />\n    <nd ref='5987137927' />\n    <nd ref='5987137928' />\n    <nd ref='5987137929' />\n    <nd ref='5987137921' />\n    <nd ref='5987137922' />\n    <nd ref='5987137923' />\n    <nd ref='5987137953' />\n    <nd ref='5987137954' />\n    <nd ref='5987137924' />\n    <nd ref='5987137925' />\n    <nd ref='5987137926' />\n    <nd ref='5987137948' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='634415570' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137963' />\n    <nd ref='5987137970' />\n    <nd ref='5987137964' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634415571' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137964' />\n    <nd ref='5987137965' />\n    <tag k='highway' v='steps' />\n    <tag k='incline' v='up' />\n  </way>\n  <way id='634415572' timestamp='2018-10-15T22:14:54Z' uid='2237750' user='chachafish' version='1' changeset='63557411'>\n    <nd ref='5987137965' />\n    <nd ref='5987137966' />\n    <nd ref='5987137967' />\n    <nd ref='5987137968' />\n    <nd ref='5987137969' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634433180' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987263171' />\n    <nd ref='5987263172' />\n    <nd ref='5987263173' />\n    <nd ref='5987263174' />\n    <nd ref='5987263171' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433181' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987263175' />\n    <nd ref='5987263176' />\n    <nd ref='5987263177' />\n    <nd ref='5987263178' />\n    <nd ref='5987263175' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433182' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987263179' />\n    <nd ref='5987263180' />\n    <nd ref='5987263181' />\n    <nd ref='5987263182' />\n    <nd ref='5987263179' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433183' timestamp='2018-10-15T23:37:41Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987263183' />\n    <nd ref='5987263184' />\n    <nd ref='5987264286' />\n    <nd ref='5987264285' />\n    <nd ref='5987263183' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433184' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264287' />\n    <nd ref='5987264288' />\n    <nd ref='5987264289' />\n    <nd ref='5987264290' />\n    <nd ref='5987264287' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433185' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264291' />\n    <nd ref='5987264292' />\n    <nd ref='5987264293' />\n    <nd ref='5987264294' />\n    <nd ref='5987264291' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433186' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264295' />\n    <nd ref='5987264296' />\n    <nd ref='5987264297' />\n    <nd ref='5987264298' />\n    <nd ref='5987264295' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433187' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264299' />\n    <nd ref='5987264300' />\n    <nd ref='5987264301' />\n    <nd ref='5987264302' />\n    <nd ref='5987264299' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433188' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264303' />\n    <nd ref='5987264306' />\n    <nd ref='5987264304' />\n    <nd ref='5987264305' />\n    <nd ref='5987264303' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433189' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264307' />\n    <nd ref='5987264308' />\n    <nd ref='5987264309' />\n    <nd ref='5987264310' />\n    <nd ref='5987264307' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433190' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264311' />\n    <nd ref='5987264312' />\n    <nd ref='5987264313' />\n    <nd ref='5987264314' />\n    <nd ref='5987264311' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433191' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264315' />\n    <nd ref='5987264316' />\n    <nd ref='5987264317' />\n    <nd ref='5987264318' />\n    <nd ref='5987264315' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433192' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264319' />\n    <nd ref='5987264320' />\n    <nd ref='5987264321' />\n    <nd ref='5987264322' />\n    <nd ref='5987264319' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433193' timestamp='2018-10-15T23:37:42Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264323' />\n    <nd ref='5987264324' />\n    <nd ref='5987264325' />\n    <nd ref='5987264326' />\n    <nd ref='5987264323' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433194' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264327' />\n    <nd ref='5987264328' />\n    <nd ref='5987264329' />\n    <nd ref='5987264330' />\n    <nd ref='5987264327' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433195' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264331' />\n    <nd ref='5987264332' />\n    <nd ref='5987264333' />\n    <nd ref='5987264334' />\n    <nd ref='5987264331' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433196' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264335' />\n    <nd ref='5987264336' />\n    <nd ref='5987264337' />\n    <nd ref='5987264338' />\n    <nd ref='5987264335' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433197' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264339' />\n    <nd ref='5987264340' />\n    <nd ref='5987264341' />\n    <nd ref='5987264342' />\n    <nd ref='5987264339' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433198' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264343' />\n    <nd ref='5987264344' />\n    <nd ref='5987264345' />\n    <nd ref='5987264346' />\n    <nd ref='5987264343' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433199' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264347' />\n    <nd ref='5987264348' />\n    <nd ref='5987264349' />\n    <nd ref='5987264350' />\n    <nd ref='5987264347' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433200' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264351' />\n    <nd ref='5987264352' />\n    <nd ref='5987264353' />\n    <nd ref='5987264354' />\n    <nd ref='5987264351' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433201' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264355' />\n    <nd ref='5987264356' />\n    <nd ref='5987264357' />\n    <nd ref='5987264358' />\n    <nd ref='5987264355' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433202' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264359' />\n    <nd ref='5987264360' />\n    <nd ref='5987264361' />\n    <nd ref='5987264362' />\n    <nd ref='5987264359' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433203' timestamp='2018-10-15T23:37:43Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264363' />\n    <nd ref='5987264364' />\n    <nd ref='5987264365' />\n    <nd ref='5987264366' />\n    <nd ref='5987264363' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433204' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264367' />\n    <nd ref='5987264368' />\n    <nd ref='5987264369' />\n    <nd ref='5987264370' />\n    <nd ref='5987264367' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433206' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264371' />\n    <nd ref='5987264372' />\n    <nd ref='5987264373' />\n    <nd ref='5987264374' />\n    <nd ref='5987264371' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433209' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264375' />\n    <nd ref='5987264376' />\n    <nd ref='5987264377' />\n    <nd ref='5987264378' />\n    <nd ref='5987264375' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433212' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264379' />\n    <nd ref='5987264380' />\n    <nd ref='5987264381' />\n    <nd ref='5987264382' />\n    <nd ref='5987264379' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433215' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264383' />\n    <nd ref='5987264384' />\n    <nd ref='5987264385' />\n    <nd ref='5987264386' />\n    <nd ref='5987264383' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433217' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264387' />\n    <nd ref='5987264388' />\n    <nd ref='5987264389' />\n    <nd ref='5987264390' />\n    <nd ref='5987264387' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433219' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264391' />\n    <nd ref='5987264392' />\n    <nd ref='5987264393' />\n    <nd ref='5987264394' />\n    <nd ref='5987264391' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433220' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264395' />\n    <nd ref='5987264396' />\n    <nd ref='5987264397' />\n    <nd ref='5987264398' />\n    <nd ref='5987264395' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433221' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264399' />\n    <nd ref='5987264400' />\n    <nd ref='5987264401' />\n    <nd ref='5987264402' />\n    <nd ref='5987264399' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433222' timestamp='2018-10-15T23:37:44Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264403' />\n    <nd ref='5987264404' />\n    <nd ref='5987264405' />\n    <nd ref='5987264406' />\n    <nd ref='5987264403' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433223' timestamp='2018-10-15T23:37:45Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264407' />\n    <nd ref='5987264408' />\n    <nd ref='5987264410' />\n    <nd ref='5987264409' />\n    <nd ref='5987264407' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433224' timestamp='2018-10-15T23:37:45Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264411' />\n    <nd ref='5987264412' />\n    <nd ref='5987264413' />\n    <nd ref='5987264414' />\n    <nd ref='5987264411' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433225' timestamp='2018-10-15T23:37:45Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264415' />\n    <nd ref='5987264416' />\n    <nd ref='5987264418' />\n    <nd ref='5987264417' />\n    <nd ref='5987264415' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433226' timestamp='2018-10-15T23:37:45Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264419' />\n    <nd ref='5987264420' />\n    <nd ref='5987264421' />\n    <nd ref='5987264422' />\n    <nd ref='5987264419' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433227' timestamp='2018-10-15T23:37:45Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264423' />\n    <nd ref='5987264430' />\n    <nd ref='5987264424' />\n    <nd ref='5987264425' />\n    <nd ref='5987264423' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433228' timestamp='2018-10-15T23:37:45Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264426' />\n    <nd ref='5987264427' />\n    <nd ref='5987264428' />\n    <nd ref='5987264429' />\n    <nd ref='5987264426' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433229' timestamp='2018-10-15T23:37:45Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264439' />\n    <nd ref='5987264440' />\n    <nd ref='5987264441' />\n    <nd ref='5987264442' />\n    <nd ref='5987264443' />\n    <nd ref='5987264444' />\n    <nd ref='5987264445' />\n    <nd ref='5987264446' />\n    <nd ref='5987264447' />\n    <nd ref='5987264448' />\n    <nd ref='5987264449' />\n    <nd ref='5987264431' />\n    <nd ref='5987264434' />\n    <nd ref='5987264435' />\n    <nd ref='5987264436' />\n    <nd ref='5987264437' />\n    <nd ref='5987264438' />\n    <nd ref='5987264432' />\n    <nd ref='5987264433' />\n    <nd ref='5987264439' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433230' timestamp='2018-10-15T23:37:45Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264450' />\n    <nd ref='5987264451' />\n    <nd ref='5987264452' />\n    <nd ref='5987264453' />\n    <nd ref='5987264454' />\n    <nd ref='5987264455' />\n    <nd ref='5987264456' />\n    <nd ref='5987264457' />\n    <nd ref='5987264458' />\n    <nd ref='5987264459' />\n    <nd ref='5987264460' />\n    <nd ref='5987264461' />\n    <nd ref='5987264462' />\n    <nd ref='5987264463' />\n    <nd ref='5987264464' />\n    <nd ref='5987264465' />\n    <nd ref='5987264466' />\n    <nd ref='5987264467' />\n    <nd ref='5987264468' />\n    <nd ref='5987264450' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433231' timestamp='2018-10-16T08:28:32Z' uid='2237750' user='chachafish' version='2' changeset='63567737'>\n    <nd ref='5987264469' />\n    <nd ref='5987264470' />\n    <nd ref='5987264471' />\n    <nd ref='5987264472' />\n    <nd ref='5987264469' />\n    <tag k='building:colour' v='grey' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='grey' />\n  </way>\n  <way id='634433232' timestamp='2018-10-16T08:28:32Z' uid='2237750' user='chachafish' version='2' changeset='63567737'>\n    <nd ref='5987264473' />\n    <nd ref='5987264474' />\n    <nd ref='5987264475' />\n    <nd ref='5987264476' />\n    <nd ref='5987264473' />\n    <tag k='building:colour' v='grey' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='grey' />\n  </way>\n  <way id='634433233' timestamp='2018-10-16T08:28:32Z' uid='2237750' user='chachafish' version='2' changeset='63567737'>\n    <nd ref='5987264477' />\n    <nd ref='5987264478' />\n    <nd ref='5987264480' />\n    <nd ref='5987264483' />\n    <nd ref='5987264481' />\n    <nd ref='5987264486' />\n    <nd ref='5987264485' />\n    <nd ref='5987264487' />\n    <nd ref='5987264488' />\n    <nd ref='5987264484' />\n    <nd ref='5987264482' />\n    <nd ref='5987264479' />\n    <nd ref='5987264477' />\n    <tag k='building:colour' v='grey' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='grey' />\n  </way>\n  <way id='634433234' timestamp='2018-10-15T23:37:46Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264489' />\n    <nd ref='5987264490' />\n    <nd ref='5987264491' />\n    <nd ref='5987264492' />\n    <nd ref='5987264489' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433235' timestamp='2018-10-15T23:37:46Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264493' />\n    <nd ref='5987264494' />\n    <nd ref='5987264495' />\n    <nd ref='5987264496' />\n    <nd ref='5987264493' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433236' timestamp='2018-10-15T23:37:46Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264497' />\n    <nd ref='5987264498' />\n    <nd ref='5987264499' />\n    <nd ref='5987264501' />\n    <nd ref='5987264502' />\n    <nd ref='5987264500' />\n    <nd ref='5987264497' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433237' timestamp='2018-10-15T23:37:46Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264503' />\n    <nd ref='5987264504' />\n    <nd ref='5987264505' />\n    <nd ref='5987264506' />\n    <nd ref='5987264507' />\n    <nd ref='5987264508' />\n    <nd ref='5987264503' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433238' timestamp='2018-10-15T23:37:46Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264509' />\n    <nd ref='5987264510' />\n    <nd ref='5987264511' />\n    <nd ref='5987264512' />\n    <nd ref='5987264509' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433239' timestamp='2018-10-15T23:37:46Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264513' />\n    <nd ref='5987264514' />\n    <nd ref='5987264515' />\n    <nd ref='5987264516' />\n    <nd ref='5987264513' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433240' timestamp='2018-10-15T23:37:46Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264517' />\n    <nd ref='5987264518' />\n    <nd ref='5987264519' />\n    <nd ref='5987264520' />\n    <nd ref='5987264521' />\n    <nd ref='5987264522' />\n    <nd ref='5987264517' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433241' timestamp='2018-10-15T23:37:46Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264523' />\n    <nd ref='5987264526' />\n    <nd ref='5987264524' />\n    <nd ref='5987264525' />\n    <nd ref='5987264523' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.2' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433242' timestamp='2018-10-15T23:37:47Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264527' />\n    <nd ref='5987264528' />\n    <nd ref='5987264529' />\n    <nd ref='5987264530' />\n    <nd ref='5987264531' />\n    <nd ref='5987264532' />\n    <nd ref='5987264527' />\n    <tag k='building:colour' v='black' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='black' />\n  </way>\n  <way id='634433243' timestamp='2018-10-15T23:37:47Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264533' />\n    <nd ref='5987264534' />\n    <nd ref='5987264538' />\n    <nd ref='5987264535' />\n    <nd ref='5987264536' />\n    <nd ref='5987264537' />\n    <nd ref='5987264533' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433244' timestamp='2018-10-15T23:37:47Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264539' />\n    <nd ref='5987264540' />\n    <nd ref='5987264541' />\n    <nd ref='5987264542' />\n    <nd ref='5987264539' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433245' timestamp='2018-10-15T23:37:47Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264543' />\n    <nd ref='5987264544' />\n    <nd ref='5987264546' />\n    <nd ref='5987264545' />\n    <nd ref='5987264543' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433246' timestamp='2018-10-15T23:37:47Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264550' />\n    <nd ref='5987264547' />\n    <nd ref='5987264548' />\n    <nd ref='5987264549' />\n    <nd ref='5987264550' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433247' timestamp='2018-10-15T23:37:47Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264551' />\n    <nd ref='5987264552' />\n    <nd ref='5987264553' />\n    <nd ref='5987264554' />\n    <nd ref='5987264551' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433248' timestamp='2018-10-15T23:37:47Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264555' />\n    <nd ref='5987264556' />\n    <nd ref='5987264558' />\n    <nd ref='5987264557' />\n    <nd ref='5987264555' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433249' timestamp='2018-10-15T23:37:48Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264561' />\n    <nd ref='5987264559' />\n    <nd ref='5987264560' />\n    <nd ref='5987264562' />\n    <nd ref='5987264561' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433250' timestamp='2018-10-15T23:37:48Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264563' />\n    <nd ref='5987264564' />\n    <nd ref='5987264565' />\n    <nd ref='5987264566' />\n    <nd ref='5987264563' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433252' timestamp='2018-10-15T23:37:48Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264567' />\n    <nd ref='5987264568' />\n    <nd ref='5987264572' />\n    <nd ref='5987264571' />\n    <nd ref='5987264569' />\n    <nd ref='5987264570' />\n    <nd ref='5987264567' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='23' />\n    <tag k='min_height' v='21' />\n  </way>\n  <way id='634433253' timestamp='2018-10-16T08:39:11Z' uid='2237750' user='chachafish' version='2' changeset='63568069'>\n    <nd ref='5987264573' />\n    <nd ref='5987264574' />\n    <nd ref='5987264575' />\n    <nd ref='5987264576' />\n    <nd ref='5987264573' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='24' />\n    <tag k='min_height' v='23' />\n  </way>\n  <way id='634433254' timestamp='2018-10-16T08:28:33Z' uid='2237750' user='chachafish' version='2' changeset='63567737'>\n    <nd ref='5987264577' />\n    <nd ref='5987264578' />\n    <nd ref='5987264579' />\n    <nd ref='5987264581' />\n    <nd ref='5987264582' />\n    <nd ref='5987264583' />\n    <nd ref='5987264585' />\n    <nd ref='5987264586' />\n    <nd ref='5987264584' />\n    <nd ref='5987264580' />\n    <nd ref='5987264577' />\n    <tag k='building:colour' v='grey' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='grey' />\n  </way>\n  <way id='634433255' timestamp='2018-10-16T08:28:33Z' uid='2237750' user='chachafish' version='2' changeset='63567737'>\n    <nd ref='5987264587' />\n    <nd ref='5987264588' />\n    <nd ref='5987264592' />\n    <nd ref='5987264589' />\n    <nd ref='5987264593' />\n    <nd ref='5987264594' />\n    <nd ref='5987264596' />\n    <nd ref='5987264595' />\n    <nd ref='5987264590' />\n    <nd ref='5987264591' />\n    <nd ref='5987264587' />\n    <tag k='building:colour' v='grey' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='grey' />\n  </way>\n  <way id='634433256' timestamp='2018-10-15T23:37:48Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264597' />\n    <nd ref='5987264598' />\n    <nd ref='5987264599' />\n    <nd ref='5987264600' />\n    <nd ref='5987264597' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433257' timestamp='2018-10-15T23:37:48Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264601' />\n    <nd ref='5987264602' />\n    <nd ref='5987264603' />\n    <nd ref='5987264604' />\n    <nd ref='5987264601' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433258' timestamp='2018-10-15T23:37:48Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264605' />\n    <nd ref='5987264606' />\n    <nd ref='5987264607' />\n    <nd ref='5987264608' />\n    <nd ref='5987264605' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433259' timestamp='2018-10-15T23:37:48Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264609' />\n    <nd ref='5987264610' />\n    <nd ref='5987264611' />\n    <nd ref='5987264612' />\n    <nd ref='5987264609' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433260' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264613' />\n    <nd ref='5987264614' />\n    <nd ref='5987264615' />\n    <nd ref='5987264616' />\n    <nd ref='5987264613' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433261' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264617' />\n    <nd ref='5987264618' />\n    <nd ref='5987264619' />\n    <nd ref='5987264620' />\n    <nd ref='5987264617' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433262' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264621' />\n    <nd ref='5987264622' />\n    <nd ref='5987264623' />\n    <nd ref='5987264624' />\n    <nd ref='5987264621' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433263' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264625' />\n    <nd ref='5987264626' />\n    <nd ref='5987264627' />\n    <nd ref='5987264628' />\n    <nd ref='5987264625' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433264' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264629' />\n    <nd ref='5987264630' />\n    <nd ref='5987264631' />\n    <nd ref='5987264632' />\n    <nd ref='5987264629' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433265' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264633' />\n    <nd ref='5987264634' />\n    <nd ref='5987264635' />\n    <nd ref='5987264636' />\n    <nd ref='5987264633' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433266' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264637' />\n    <nd ref='5987264638' />\n    <nd ref='5987264639' />\n    <nd ref='5987264640' />\n    <nd ref='5987264637' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433267' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264641' />\n    <nd ref='5987264642' />\n    <nd ref='5987264643' />\n    <nd ref='5987264644' />\n    <nd ref='5987264641' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433268' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264645' />\n    <nd ref='5987264646' />\n    <nd ref='5987264647' />\n    <nd ref='5987264648' />\n    <nd ref='5987264645' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433269' timestamp='2018-10-15T23:37:49Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264649' />\n    <nd ref='5987264650' />\n    <nd ref='5987264651' />\n    <nd ref='5987264652' />\n    <nd ref='5987264649' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433270' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264653' />\n    <nd ref='5987264654' />\n    <nd ref='5987264655' />\n    <nd ref='5987264656' />\n    <nd ref='5987264653' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433271' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264657' />\n    <nd ref='5987264658' />\n    <nd ref='5987264659' />\n    <nd ref='5987264660' />\n    <nd ref='5987264657' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433272' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264661' />\n    <nd ref='5987264662' />\n    <nd ref='5987264663' />\n    <nd ref='5987264664' />\n    <nd ref='5987264661' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433273' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264665' />\n    <nd ref='5987264666' />\n    <nd ref='5987264667' />\n    <nd ref='5987264668' />\n    <nd ref='5987264665' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433274' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264669' />\n    <nd ref='5987264670' />\n    <nd ref='5987264671' />\n    <nd ref='5987264672' />\n    <nd ref='5987264669' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433275' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264673' />\n    <nd ref='5987264674' />\n    <nd ref='5987264675' />\n    <nd ref='5987264676' />\n    <nd ref='5987264673' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433276' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264677' />\n    <nd ref='5987264678' />\n    <nd ref='5987264679' />\n    <nd ref='5987264680' />\n    <nd ref='5987264677' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433277' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264681' />\n    <nd ref='5987264682' />\n    <nd ref='5987264683' />\n    <nd ref='5987264684' />\n    <nd ref='5987264681' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433278' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264685' />\n    <nd ref='5987264686' />\n    <nd ref='5987264687' />\n    <nd ref='5987264688' />\n    <nd ref='5987264685' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433279' timestamp='2018-10-15T23:37:50Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264689' />\n    <nd ref='5987264690' />\n    <nd ref='5987264691' />\n    <nd ref='5987264692' />\n    <nd ref='5987264689' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433280' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264693' />\n    <nd ref='5987264694' />\n    <nd ref='5987264695' />\n    <nd ref='5987264696' />\n    <nd ref='5987264693' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433281' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264697' />\n    <nd ref='5987264698' />\n    <nd ref='5987264699' />\n    <nd ref='5987264700' />\n    <nd ref='5987264697' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433282' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264701' />\n    <nd ref='5987264702' />\n    <nd ref='5987264703' />\n    <nd ref='5987264704' />\n    <nd ref='5987264701' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433283' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264705' />\n    <nd ref='5987264706' />\n    <nd ref='5987264707' />\n    <nd ref='5987264708' />\n    <nd ref='5987264705' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433284' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264709' />\n    <nd ref='5987264710' />\n    <nd ref='5987264711' />\n    <nd ref='5987264712' />\n    <nd ref='5987264709' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433285' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264713' />\n    <nd ref='5987264714' />\n    <nd ref='5987264715' />\n    <nd ref='5987264716' />\n    <nd ref='5987264713' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433286' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264717' />\n    <nd ref='5987264718' />\n    <nd ref='5987264719' />\n    <nd ref='5987264720' />\n    <nd ref='5987264717' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433287' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264721' />\n    <nd ref='5987264722' />\n    <nd ref='5987264723' />\n    <nd ref='5987264724' />\n    <nd ref='5987264721' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433288' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264725' />\n    <nd ref='5987264726' />\n    <nd ref='5987264727' />\n    <nd ref='5987264728' />\n    <nd ref='5987264725' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433289' timestamp='2018-10-15T23:37:51Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264729' />\n    <nd ref='5987264730' />\n    <nd ref='5987264731' />\n    <nd ref='5987264732' />\n    <nd ref='5987264729' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433290' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264733' />\n    <nd ref='5987264734' />\n    <nd ref='5987264735' />\n    <nd ref='5987264736' />\n    <nd ref='5987264733' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433291' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264737' />\n    <nd ref='5987264738' />\n    <nd ref='5987264739' />\n    <nd ref='5987264740' />\n    <nd ref='5987264737' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433292' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264741' />\n    <nd ref='5987264742' />\n    <nd ref='5987264743' />\n    <nd ref='5987264744' />\n    <nd ref='5987264741' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433293' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264745' />\n    <nd ref='5987264746' />\n    <nd ref='5987264747' />\n    <nd ref='5987264748' />\n    <nd ref='5987264745' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433294' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264749' />\n    <nd ref='5987264750' />\n    <nd ref='5987264751' />\n    <nd ref='5987264752' />\n    <nd ref='5987264749' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433295' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264753' />\n    <nd ref='5987264754' />\n    <nd ref='5987264755' />\n    <nd ref='5987264756' />\n    <nd ref='5987264753' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433296' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264757' />\n    <nd ref='5987264758' />\n    <nd ref='5987264759' />\n    <nd ref='5987264760' />\n    <nd ref='5987264757' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433297' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264761' />\n    <nd ref='5987264762' />\n    <nd ref='5987264763' />\n    <nd ref='5987264764' />\n    <nd ref='5987264761' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433298' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264765' />\n    <nd ref='5987264766' />\n    <nd ref='5987264767' />\n    <nd ref='5987264768' />\n    <nd ref='5987264765' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433299' timestamp='2018-10-15T23:37:52Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264769' />\n    <nd ref='5987264770' />\n    <nd ref='5987264771' />\n    <nd ref='5987264772' />\n    <nd ref='5987264769' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433300' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264773' />\n    <nd ref='5987264774' />\n    <nd ref='5987264775' />\n    <nd ref='5987264776' />\n    <nd ref='5987264773' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433301' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264777' />\n    <nd ref='5987264778' />\n    <nd ref='5987264779' />\n    <nd ref='5987264780' />\n    <nd ref='5987264777' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433302' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264781' />\n    <nd ref='5987264782' />\n    <nd ref='5987264783' />\n    <nd ref='5987264784' />\n    <nd ref='5987264781' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433303' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264785' />\n    <nd ref='5987264786' />\n    <nd ref='5987264787' />\n    <nd ref='5987264788' />\n    <nd ref='5987264785' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433304' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264789' />\n    <nd ref='5987264790' />\n    <nd ref='5987264791' />\n    <nd ref='5987264792' />\n    <nd ref='5987264789' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433305' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264793' />\n    <nd ref='5987264794' />\n    <nd ref='5987264795' />\n    <nd ref='5987264796' />\n    <nd ref='5987264793' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433306' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264797' />\n    <nd ref='5987264798' />\n    <nd ref='5987264799' />\n    <nd ref='5987264800' />\n    <nd ref='5987264797' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433307' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264801' />\n    <nd ref='5987264802' />\n    <nd ref='5987264803' />\n    <nd ref='5987264804' />\n    <nd ref='5987264801' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433308' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264805' />\n    <nd ref='5987264806' />\n    <nd ref='5987264807' />\n    <nd ref='5987264808' />\n    <nd ref='5987264805' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433310' timestamp='2018-10-15T23:37:53Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264809' />\n    <nd ref='5987264810' />\n    <nd ref='5987264811' />\n    <nd ref='5987264812' />\n    <nd ref='5987264809' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433311' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264813' />\n    <nd ref='5987264814' />\n    <nd ref='5987264815' />\n    <nd ref='5987264816' />\n    <nd ref='5987264813' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433312' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264817' />\n    <nd ref='5987264818' />\n    <nd ref='5987264819' />\n    <nd ref='5987264820' />\n    <nd ref='5987264817' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433313' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264821' />\n    <nd ref='5987264822' />\n    <nd ref='5987264823' />\n    <nd ref='5987264824' />\n    <nd ref='5987264821' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433314' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264825' />\n    <nd ref='5987264826' />\n    <nd ref='5987264827' />\n    <nd ref='5987264828' />\n    <nd ref='5987264825' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433315' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264829' />\n    <nd ref='5987264830' />\n    <nd ref='5987264831' />\n    <nd ref='5987264832' />\n    <nd ref='5987264829' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433316' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264833' />\n    <nd ref='5987264834' />\n    <nd ref='5987264835' />\n    <nd ref='5987264836' />\n    <nd ref='5987264833' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433317' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264837' />\n    <nd ref='5987264838' />\n    <nd ref='5987264839' />\n    <nd ref='5987264840' />\n    <nd ref='5987264837' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433318' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264841' />\n    <nd ref='5987264842' />\n    <nd ref='5987264843' />\n    <nd ref='5987264844' />\n    <nd ref='5987264841' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433319' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264845' />\n    <nd ref='5987264846' />\n    <nd ref='5987264847' />\n    <nd ref='5987264848' />\n    <nd ref='5987264845' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433320' timestamp='2018-10-15T23:37:54Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264849' />\n    <nd ref='5987264850' />\n    <nd ref='5987264851' />\n    <nd ref='5987264852' />\n    <nd ref='5987264849' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433321' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264853' />\n    <nd ref='5987264854' />\n    <nd ref='5987264855' />\n    <nd ref='5987264856' />\n    <nd ref='5987264853' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433322' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264857' />\n    <nd ref='5987264858' />\n    <nd ref='5987264859' />\n    <nd ref='5987264860' />\n    <nd ref='5987264857' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433323' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264861' />\n    <nd ref='5987264862' />\n    <nd ref='5987264863' />\n    <nd ref='5987264864' />\n    <nd ref='5987264861' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433324' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264865' />\n    <nd ref='5987264866' />\n    <nd ref='5987264867' />\n    <nd ref='5987264868' />\n    <nd ref='5987264865' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433325' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264869' />\n    <nd ref='5987264876' />\n    <nd ref='5987264870' />\n    <nd ref='5987264871' />\n    <nd ref='5987264872' />\n    <nd ref='5987264873' />\n    <nd ref='5987264874' />\n    <nd ref='5987264875' />\n    <nd ref='5987264869' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433326' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264877' />\n    <nd ref='5987264878' />\n    <nd ref='5987264879' />\n    <nd ref='5987264880' />\n    <nd ref='5987264877' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.2' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433327' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264881' />\n    <nd ref='5987264882' />\n    <nd ref='5987264884' />\n    <nd ref='5987264883' />\n    <nd ref='5987264881' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.2' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433328' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264885' />\n    <nd ref='5987264886' />\n    <nd ref='5987264887' />\n    <nd ref='5987264888' />\n    <nd ref='5987264885' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433329' timestamp='2018-10-15T23:37:55Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264889' />\n    <nd ref='5987264890' />\n    <nd ref='5987264891' />\n    <nd ref='5987264892' />\n    <nd ref='5987264889' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433330' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264893' />\n    <nd ref='5987264894' />\n    <nd ref='5987264895' />\n    <nd ref='5987264896' />\n    <nd ref='5987264893' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433332' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264897' />\n    <nd ref='5987264898' />\n    <nd ref='5987264899' />\n    <nd ref='5987264900' />\n    <nd ref='5987264897' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433334' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264901' />\n    <nd ref='5987264902' />\n    <nd ref='5987264903' />\n    <nd ref='5987264904' />\n    <nd ref='5987264905' />\n    <nd ref='5987264906' />\n    <nd ref='5987264907' />\n    <nd ref='5987264908' />\n    <nd ref='5987264909' />\n    <nd ref='5987264910' />\n    <nd ref='5987264911' />\n    <nd ref='5987264912' />\n    <nd ref='5987264913' />\n    <nd ref='5987264914' />\n    <nd ref='5987264915' />\n    <nd ref='5987264916' />\n    <nd ref='5987264917' />\n    <nd ref='5987264918' />\n    <nd ref='5987264919' />\n    <nd ref='5987264901' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433337' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264920' />\n    <nd ref='5987264921' />\n    <nd ref='5987264922' />\n    <nd ref='5987264923' />\n    <nd ref='5987264924' />\n    <nd ref='5987264925' />\n    <nd ref='5987264926' />\n    <nd ref='5987264927' />\n    <nd ref='5987264928' />\n    <nd ref='5987264929' />\n    <nd ref='5987264930' />\n    <nd ref='5987264931' />\n    <nd ref='5987264932' />\n    <nd ref='5987264933' />\n    <nd ref='5987264934' />\n    <nd ref='5987264935' />\n    <nd ref='5987264936' />\n    <nd ref='5987264937' />\n    <nd ref='5987264938' />\n    <nd ref='5987264920' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433341' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264939' />\n    <nd ref='5987264940' />\n    <nd ref='5987264941' />\n    <nd ref='5987264942' />\n    <nd ref='5987264943' />\n    <nd ref='5987264944' />\n    <nd ref='5987264945' />\n    <nd ref='5987264946' />\n    <nd ref='5987264947' />\n    <nd ref='5987264948' />\n    <nd ref='5987264949' />\n    <nd ref='5987264950' />\n    <nd ref='5987264951' />\n    <nd ref='5987264952' />\n    <nd ref='5987264953' />\n    <nd ref='5987264954' />\n    <nd ref='5987264955' />\n    <nd ref='5987264956' />\n    <nd ref='5987264957' />\n    <nd ref='5987264939' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='22' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433344' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264958' />\n    <nd ref='5987264959' />\n    <nd ref='5987264960' />\n    <nd ref='5987264961' />\n    <nd ref='5987264958' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433347' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264962' />\n    <nd ref='5987264963' />\n    <nd ref='5987264964' />\n    <nd ref='5987264965' />\n    <nd ref='5987264962' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433350' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264966' />\n    <nd ref='5987264967' />\n    <nd ref='5987264968' />\n    <nd ref='5987264969' />\n    <nd ref='5987264966' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433353' timestamp='2018-10-15T23:37:56Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264970' />\n    <nd ref='5987264971' />\n    <nd ref='5987264972' />\n    <nd ref='5987264973' />\n    <nd ref='5987264970' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433355' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264974' />\n    <nd ref='5987264975' />\n    <nd ref='5987264976' />\n    <nd ref='5987264977' />\n    <nd ref='5987264974' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433358' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264978' />\n    <nd ref='5987264979' />\n    <nd ref='5987264980' />\n    <nd ref='5987264981' />\n    <nd ref='5987264978' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433360' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264982' />\n    <nd ref='5987264983' />\n    <nd ref='5987264984' />\n    <nd ref='5987264985' />\n    <nd ref='5987264982' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433362' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264986' />\n    <nd ref='5987264987' />\n    <nd ref='5987264988' />\n    <nd ref='5987264989' />\n    <nd ref='5987264986' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433365' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264990' />\n    <nd ref='5987264991' />\n    <nd ref='5987264992' />\n    <nd ref='5987264993' />\n    <nd ref='5987264990' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433367' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264994' />\n    <nd ref='5987264995' />\n    <nd ref='5987264996' />\n    <nd ref='5987264997' />\n    <nd ref='5987264994' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433368' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987264998' />\n    <nd ref='5987264999' />\n    <nd ref='5987265000' />\n    <nd ref='5987265001' />\n    <nd ref='5987264998' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433369' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265002' />\n    <nd ref='5987265003' />\n    <nd ref='5987265004' />\n    <nd ref='5987265005' />\n    <nd ref='5987265002' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433370' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265006' />\n    <nd ref='5987265007' />\n    <nd ref='5987265008' />\n    <nd ref='5987265009' />\n    <nd ref='5987265006' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433371' timestamp='2018-10-15T23:37:57Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265010' />\n    <nd ref='5987265011' />\n    <nd ref='5987265012' />\n    <nd ref='5987265013' />\n    <nd ref='5987265010' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433372' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265014' />\n    <nd ref='5987265015' />\n    <nd ref='5987265016' />\n    <nd ref='5987265017' />\n    <nd ref='5987265014' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433373' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265018' />\n    <nd ref='5987265019' />\n    <nd ref='5987265020' />\n    <nd ref='5987265021' />\n    <nd ref='5987265018' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433374' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265022' />\n    <nd ref='5987265023' />\n    <nd ref='5987265024' />\n    <nd ref='5987265025' />\n    <nd ref='5987265022' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433377' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265026' />\n    <nd ref='5987265027' />\n    <nd ref='5987265028' />\n    <nd ref='5987265029' />\n    <nd ref='5987265026' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433380' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265030' />\n    <nd ref='5987265031' />\n    <nd ref='5987265032' />\n    <nd ref='5987265033' />\n    <nd ref='5987265030' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433382' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265034' />\n    <nd ref='5987265035' />\n    <nd ref='5987265036' />\n    <nd ref='5987265037' />\n    <nd ref='5987265034' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433385' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265038' />\n    <nd ref='5987265039' />\n    <nd ref='5987265040' />\n    <nd ref='5987265041' />\n    <nd ref='5987265038' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433387' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265042' />\n    <nd ref='5987265043' />\n    <nd ref='5987265044' />\n    <nd ref='5987265045' />\n    <nd ref='5987265042' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433389' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265046' />\n    <nd ref='5987265047' />\n    <nd ref='5987265048' />\n    <nd ref='5987265049' />\n    <nd ref='5987265046' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433391' timestamp='2018-10-15T23:37:58Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265050' />\n    <nd ref='5987265051' />\n    <nd ref='5987265052' />\n    <nd ref='5987265053' />\n    <nd ref='5987265050' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433393' timestamp='2018-10-15T23:37:59Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265054' />\n    <nd ref='5987265055' />\n    <nd ref='5987265056' />\n    <nd ref='5987265057' />\n    <nd ref='5987265054' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433396' timestamp='2018-10-15T23:37:59Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265058' />\n    <nd ref='5987265059' />\n    <nd ref='5987265060' />\n    <nd ref='5987265061' />\n    <nd ref='5987265058' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433398' timestamp='2018-10-15T23:37:59Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265062' />\n    <nd ref='5987265063' />\n    <nd ref='5987265064' />\n    <nd ref='5987265065' />\n    <nd ref='5987265062' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433401' timestamp='2018-10-15T23:37:59Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265066' />\n    <nd ref='5987265067' />\n    <nd ref='5987265068' />\n    <nd ref='5987265069' />\n    <nd ref='5987265066' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433403' timestamp='2018-10-15T23:37:59Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265070' />\n    <nd ref='5987265071' />\n    <nd ref='5987265072' />\n    <nd ref='5987265073' />\n    <nd ref='5987265070' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433406' timestamp='2018-10-15T23:37:59Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265074' />\n    <nd ref='5987265075' />\n    <nd ref='5987265076' />\n    <nd ref='5987265077' />\n    <nd ref='5987265074' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433408' timestamp='2018-10-15T23:37:59Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265078' />\n    <nd ref='5987265082' />\n    <nd ref='5987265083' />\n    <nd ref='5987265079' />\n    <nd ref='5987265080' />\n    <nd ref='5987265081' />\n    <nd ref='5987265078' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433411' timestamp='2018-10-16T08:26:15Z' uid='2237750' user='chachafish' version='2' changeset='63567672'>\n    <nd ref='5987265084' />\n    <nd ref='5987265085' />\n    <nd ref='5987265086' />\n    <nd ref='5987265089' />\n    <nd ref='5987265091' />\n    <nd ref='5987265090' />\n    <nd ref='5987265088' />\n    <nd ref='5987265087' />\n    <nd ref='5987265084' />\n    <tag k='building:colour' v='grey' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.5' />\n    <tag k='min_height' v='18' />\n    <tag k='roof:colour' v='grey' />\n  </way>\n  <way id='634433414' timestamp='2018-10-16T08:26:15Z' uid='2237750' user='chachafish' version='2' changeset='63567672'>\n    <nd ref='5987265092' />\n    <nd ref='5987265093' />\n    <nd ref='5987265094' />\n    <nd ref='5987265095' />\n    <nd ref='5987265092' />\n    <tag k='building:colour' v='grey' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='20' />\n    <tag k='min_height' v='18' />\n    <tag k='roof:colour' v='grey' />\n  </way>\n  <way id='634433416' timestamp='2018-10-15T23:37:59Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265096' />\n    <nd ref='5987265097' />\n    <nd ref='5987265098' />\n    <nd ref='5987265099' />\n    <nd ref='5987265096' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433418' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265100' />\n    <nd ref='5987265101' />\n    <nd ref='5987265102' />\n    <nd ref='5987265103' />\n    <nd ref='5987265100' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433421' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265104' />\n    <nd ref='5987265105' />\n    <nd ref='5987265106' />\n    <nd ref='5987265107' />\n    <nd ref='5987265104' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433424' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265108' />\n    <nd ref='5987265109' />\n    <nd ref='5987265110' />\n    <nd ref='5987265111' />\n    <nd ref='5987265108' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433426' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265112' />\n    <nd ref='5987265113' />\n    <nd ref='5987265114' />\n    <nd ref='5987265115' />\n    <nd ref='5987265112' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433429' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265116' />\n    <nd ref='5987265117' />\n    <nd ref='5987265118' />\n    <nd ref='5987265119' />\n    <nd ref='5987265116' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433432' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265120' />\n    <nd ref='5987265121' />\n    <nd ref='5987265122' />\n    <nd ref='5987265123' />\n    <nd ref='5987265120' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433434' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265124' />\n    <nd ref='5987265125' />\n    <nd ref='5987265126' />\n    <nd ref='5987265127' />\n    <nd ref='5987265124' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433437' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265128' />\n    <nd ref='5987265129' />\n    <nd ref='5987265130' />\n    <nd ref='5987265131' />\n    <nd ref='5987265128' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433439' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265132' />\n    <nd ref='5987265133' />\n    <nd ref='5987265134' />\n    <nd ref='5987265135' />\n    <nd ref='5987265132' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433442' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265136' />\n    <nd ref='5987265137' />\n    <nd ref='5987265138' />\n    <nd ref='5987265139' />\n    <nd ref='5987265136' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433443' timestamp='2018-10-15T23:38:00Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265140' />\n    <nd ref='5987265141' />\n    <nd ref='5987265142' />\n    <nd ref='5987265143' />\n    <nd ref='5987265140' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433444' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265144' />\n    <nd ref='5987265145' />\n    <nd ref='5987265146' />\n    <nd ref='5987265147' />\n    <nd ref='5987265144' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433445' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265148' />\n    <nd ref='5987265149' />\n    <nd ref='5987265150' />\n    <nd ref='5987265151' />\n    <nd ref='5987265148' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433446' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265152' />\n    <nd ref='5987265153' />\n    <nd ref='5987265154' />\n    <nd ref='5987265155' />\n    <nd ref='5987265152' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433447' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265156' />\n    <nd ref='5987265157' />\n    <nd ref='5987265158' />\n    <nd ref='5987265159' />\n    <nd ref='5987265156' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433448' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265160' />\n    <nd ref='5987265161' />\n    <nd ref='5987265162' />\n    <nd ref='5987265163' />\n    <nd ref='5987265160' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433449' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265164' />\n    <nd ref='5987265165' />\n    <nd ref='5987265166' />\n    <nd ref='5987265167' />\n    <nd ref='5987265164' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433450' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265168' />\n    <nd ref='5987265169' />\n    <nd ref='5987265173' />\n    <nd ref='5987265175' />\n    <nd ref='5987265174' />\n    <nd ref='5987265172' />\n    <nd ref='5987265170' />\n    <nd ref='5987265171' />\n    <nd ref='5987265168' />\n    <tag k='building:colour' v='#708090' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.3' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#708090' />\n  </way>\n  <way id='634433451' timestamp='2018-10-15T23:38:01Z' uid='2237750' user='chachafish' version='1' changeset='63558461'>\n    <nd ref='5987265176' />\n    <nd ref='5987265177' />\n    <nd ref='5987265178' />\n    <nd ref='5987265179' />\n    <nd ref='5987265176' />\n    <tag k='building:colour' v='#4e5964' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='21.5' />\n    <tag k='min_height' v='21' />\n    <tag k='roof:colour' v='#4e5964' />\n  </way>\n  <way id='634433860' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='1' changeset='63558513'>\n    <nd ref='5449464503' />\n    <nd ref='5987265744' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634433862' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='1' changeset='63558513'>\n    <nd ref='5184864687' />\n    <nd ref='5987265745' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634433864' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='1' changeset='63558513'>\n    <nd ref='5184864686' />\n    <nd ref='5987265746' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634433866' timestamp='2018-10-15T23:41:24Z' uid='2237750' user='chachafish' version='1' changeset='63558513'>\n    <nd ref='5449464496' />\n    <nd ref='5987265747' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='634444999' timestamp='2018-10-16T00:41:00Z' uid='2237750' user='chachafish' version='1' changeset='63559070'>\n    <nd ref='2304626312' />\n    <nd ref='3985226613' />\n    <tag k='cycleway' v='shared_lane' />\n    <tag k='highway' v='residential' />\n    <tag k='lcn_ref' v='123' />\n    <tag k='name' v='Henry Adams Street' />\n    <tag k='oneway' v='no' />\n    <tag k='tiger:cfcc' v='A41' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Henry Adams' />\n    <tag k='tiger:name_type' v='St' />\n  </way>\n  <way id='635049069' timestamp='2018-10-16T21:34:13Z' uid='2237750' user='chachafish' version='1' changeset='63591679'>\n    <nd ref='5986734250' />\n    <nd ref='5986734251' />\n    <nd ref='5986734252' />\n    <nd ref='5986734253' />\n    <nd ref='5991623594' />\n    <nd ref='5986734250' />\n    <tag k='building:material' v='glass' />\n    <tag k='building:part' v='yes' />\n    <tag k='height' v='3' />\n    <tag k='min_height' v='2.9' />\n    <tag k='roof:material' v='glass' />\n  </way>\n  <way id='639288274' timestamp='2018-11-05T17:49:54Z' uid='119881' user='clay_c' version='3' changeset='64205910'>\n    <nd ref='1344032260' />\n    <nd ref='1344032572' />\n    <nd ref='1344032490' />\n    <nd ref='1344032342' />\n    <nd ref='1344032637' />\n    <nd ref='6024177032' />\n    <nd ref='6024177069' />\n    <tag k='electrified' v='no' />\n    <tag k='gauge' v='1435' />\n    <tag k='history' v='Retrieved from v10' />\n    <tag k='maxspeed' v='35 mph' />\n    <tag k='maxspeed:freight' v='10 mph' />\n    <tag k='name' v='Peninsula Subdivision Main 3' />\n    <tag k='owner' v='Peninsula Corridor Joint Powers Board' />\n    <tag k='railway' v='rail' />\n    <tag k='railway:track_ref' v='3' />\n    <tag k='railway:traffic_mode' v='passenger' />\n    <tag k='tiger:cfcc' v='B21' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Union Pacific Railroad' />\n    <tag k='usage' v='main' />\n  </way>\n  <way id='639288278' timestamp='2018-11-05T17:49:55Z' uid='119881' user='clay_c' version='3' changeset='64205910'>\n    <nd ref='306735889' />\n    <nd ref='1260419894' />\n    <nd ref='984720713' />\n    <nd ref='65356544' />\n    <nd ref='6024177036' />\n    <nd ref='6024177034' />\n    <nd ref='6024177035' />\n    <nd ref='6024177069' />\n    <tag k='electrified' v='no' />\n    <tag k='gauge' v='1435' />\n    <tag k='history' v='Retrieved from v10' />\n    <tag k='maxspeed' v='40 mph' />\n    <tag k='maxspeed:freight' v='10 mph' />\n    <tag k='name' v='Peninsula Subdivision Main 1' />\n    <tag k='owner' v='Peninsula Corridor Joint Powers Board' />\n    <tag k='railway' v='rail' />\n    <tag k='railway:track_ref' v='1' />\n    <tag k='railway:traffic_mode' v='passenger' />\n    <tag k='tiger:cfcc' v='B21' />\n    <tag k='tiger:county' v='San Francisco, CA' />\n    <tag k='tiger:name_base' v='Union Pacific Railroad' />\n    <tag k='usage' v='main' />\n  </way>\n  <relation id='71162' timestamp='2018-09-28T20:49:29Z' uid='33757' user='Minh Nguyen' version='652' changeset='63022562'>\n    <member type='way' ref='26467753' role='forward' />\n    <member type='way' ref='117809223' role='forward' />\n    <member type='way' ref='32867942' role='forward' />\n    <member type='way' ref='32867941' role='forward' />\n    <member type='way' ref='25287752' role='forward' />\n    <member type='way' ref='10318894' role='forward' />\n    <member type='way' ref='184029841' role='forward' />\n    <member type='way' ref='164755807' role='forward' />\n    <member type='way' ref='164755815' role='forward' />\n    <member type='way' ref='7707845' role='forward' />\n    <member type='way' ref='172723226' role='forward' />\n    <member type='way' ref='172725823' role='forward' />\n    <member type='way' ref='186409240' role='forward' />\n    <member type='way' ref='23780740' role='forward' />\n    <member type='way' ref='48046780' role='forward' />\n    <member type='way' ref='48046769' role='forward' />\n    <member type='way' ref='48046774' role='forward' />\n    <member type='way' ref='48046770' role='forward' />\n    <member type='way' ref='48046776' role='forward' />\n    <member type='way' ref='48046759' role='forward' />\n    <member type='way' ref='48046764' role='forward' />\n    <member type='way' ref='48046761' role='forward' />\n    <member type='way' ref='48046767' role='forward' />\n    <member type='way' ref='48046765' role='forward' />\n    <member type='way' ref='30678074' role='forward' />\n    <member type='way' ref='31125315' role='forward' />\n    <member type='way' ref='31125316' role='forward' />\n    <member type='way' ref='31125310' role='forward' />\n    <member type='way' ref='31125311' role='forward' />\n    <member type='way' ref='31125317' role='forward' />\n    <member type='way' ref='31125318' role='forward' />\n    <member type='way' ref='31125328' role='forward' />\n    <member type='way' ref='31125325' role='forward' />\n    <member type='way' ref='31125660' role='forward' />\n    <member type='way' ref='31781656' role='forward' />\n    <member type='way' ref='48046737' role='forward' />\n    <member type='way' ref='48046728' role='forward' />\n    <member type='way' ref='48046733' role='forward' />\n    <member type='way' ref='48046729' role='forward' />\n    <member type='way' ref='48046732' role='forward' />\n    <member type='way' ref='48046726' role='forward' />\n    <member type='way' ref='48046758' role='forward' />\n    <member type='way' ref='48046753' role='forward' />\n    <member type='way' ref='186410486' role='forward' />\n    <member type='way' ref='186410488' role='forward' />\n    <member type='way' ref='48046754' role='forward' />\n    <member type='way' ref='48046755' role='forward' />\n    <member type='way' ref='48046750' role='forward' />\n    <member type='way' ref='48046723' role='forward' />\n    <member type='way' ref='48046740' role='forward' />\n    <member type='way' ref='48046742' role='forward' />\n    <member type='way' ref='48046745' role='forward' />\n    <member type='way' ref='48046743' role='forward' />\n    <member type='way' ref='48046738' role='forward' />\n    <member type='way' ref='48046748' role='forward' />\n    <member type='way' ref='48046702' role='forward' />\n    <member type='way' ref='620247529' role='forward' />\n    <member type='way' ref='48046722' role='forward' />\n    <member type='way' ref='48046704' role='forward' />\n    <member type='way' ref='48046718' role='forward' />\n    <member type='way' ref='48046716' role='forward' />\n    <member type='way' ref='48046720' role='forward' />\n    <member type='way' ref='48046706' role='forward' />\n    <member type='way' ref='48046713' role='forward' />\n    <member type='way' ref='48046714' role='forward' />\n    <member type='way' ref='48046708' role='forward' />\n    <member type='way' ref='48046710' role='forward' />\n    <member type='way' ref='31781386' role='forward' />\n    <member type='way' ref='620247407' role='forward' />\n    <member type='way' ref='620247520' role='forward' />\n    <member type='way' ref='31781384' role='forward' />\n    <member type='way' ref='620247413' role='forward' />\n    <member type='way' ref='124952165' role='forward' />\n    <member type='way' ref='31763418' role='forward' />\n    <member type='way' ref='31763419' role='forward' />\n    <member type='way' ref='119237656' role='forward' />\n    <member type='way' ref='282925078' role='forward' />\n    <member type='way' ref='620247411' role='forward' />\n    <member type='way' ref='620247500' role='forward' />\n    <member type='way' ref='123741514' role='forward' />\n    <member type='way' ref='620247511' role='forward' />\n    <member type='way' ref='620247416' role='forward' />\n    <member type='way' ref='31450988' role='forward' />\n    <member type='way' ref='31450990' role='forward' />\n    <member type='way' ref='159938772' role='forward' />\n    <member type='way' ref='31450983' role='forward' />\n    <member type='way' ref='620247417' role='forward' />\n    <member type='way' ref='31450984' role='forward' />\n    <member type='way' ref='48046696' role='forward' />\n    <member type='way' ref='48046695' role='forward' />\n    <member type='way' ref='620247422' role='forward' />\n    <member type='way' ref='91360728' role='forward' />\n    <member type='way' ref='91360723' role='forward' />\n    <member type='way' ref='186410477' role='forward' />\n    <member type='way' ref='620247366' role='forward' />\n    <member type='way' ref='186410476' role='forward' />\n    <member type='way' ref='159938684' role='forward' />\n    <member type='way' ref='159938678' role='forward' />\n    <member type='way' ref='159938682' role='forward' />\n    <member type='way' ref='159938679' role='forward' />\n    <member type='way' ref='48046699' role='forward' />\n    <member type='way' ref='48046698' role='forward' />\n    <member type='way' ref='186409235' role='forward' />\n    <member type='way' ref='186409238' role='forward' />\n    <member type='way' ref='186409231' role='forward' />\n    <member type='way' ref='620247429' role='forward' />\n    <member type='way' ref='507018105' role='forward' />\n    <member type='way' ref='117809244' role='forward' />\n    <member type='way' ref='31395184' role='forward' />\n    <member type='way' ref='31124175' role='forward' />\n    <member type='way' ref='123741505' role='forward' />\n    <member type='way' ref='31394371' role='forward' />\n    <member type='way' ref='31394374' role='forward' />\n    <member type='way' ref='186406407' role='forward' />\n    <member type='way' ref='620215998' role='forward' />\n    <member type='way' ref='30678067' role='forward' />\n    <member type='way' ref='30678069' role='forward' />\n    <member type='way' ref='620215982' role='forward' />\n    <member type='way' ref='31386135' role='forward' />\n    <member type='way' ref='31386137' role='forward' />\n    <member type='way' ref='30678061' role='forward' />\n    <member type='way' ref='23797567' role='forward' />\n    <member type='way' ref='620215999' role='forward' />\n    <member type='way' ref='123741488' role='forward' />\n    <member type='way' ref='394411110' role='forward' />\n    <member type='way' ref='184505664' role='forward' />\n    <member type='way' ref='394427802' role='forward' />\n    <member type='way' ref='394427804' role='forward' />\n    <member type='way' ref='394427801' role='forward' />\n    <member type='way' ref='620216010' role='forward' />\n    <member type='way' ref='394430232' role='forward' />\n    <member type='way' ref='184505667' role='forward' />\n    <member type='way' ref='620216032' role='forward' />\n    <member type='way' ref='394433888' role='forward' />\n    <member type='way' ref='394433893' role='forward' />\n    <member type='way' ref='284602476' role='forward' />\n    <member type='way' ref='394434757' role='forward' />\n    <member type='way' ref='620216023' role='forward' />\n    <member type='way' ref='284602514' role='forward' />\n    <member type='way' ref='620216037' role='forward' />\n    <member type='way' ref='48675483' role='forward' />\n    <member type='way' ref='48675484' role='forward' />\n    <member type='way' ref='620216020' role='forward' />\n    <member type='way' ref='48040771' role='forward' />\n    <member type='way' ref='25025886' role='forward' />\n    <member type='way' ref='25025887' role='forward' />\n    <member type='way' ref='620525339' role='forward' />\n    <member type='way' ref='25025825' role='forward' />\n    <member type='way' ref='25025826' role='forward' />\n    <member type='way' ref='620247559' role='forward' />\n    <member type='way' ref='25025835' role='forward' />\n    <member type='way' ref='394442790' role='forward' />\n    <member type='way' ref='394446737' role='forward' />\n    <member type='way' ref='394446735' role='forward' />\n    <member type='way' ref='25025836' role='forward' />\n    <member type='way' ref='24316573' role='forward' />\n    <member type='way' ref='24316574' role='forward' />\n    <member type='way' ref='620200070' role='forward' />\n    <member type='way' ref='25025179' role='forward' />\n    <member type='way' ref='394451347' role='forward' />\n    <member type='way' ref='394451349' role='forward' />\n    <member type='way' ref='394451348' role='forward' />\n    <member type='way' ref='394451353' role='forward' />\n    <member type='way' ref='394451346' role='forward' />\n    <member type='way' ref='25025180' role='forward' />\n    <member type='way' ref='25025639' role='forward' />\n    <member type='way' ref='394451351' role='forward' />\n    <member type='way' ref='394451350' role='forward' />\n    <member type='way' ref='394150398' role='forward' />\n    <member type='way' ref='620200101' role='forward' />\n    <member type='way' ref='394457529' role='forward' />\n    <member type='way' ref='25025640' role='forward' />\n    <member type='way' ref='394457527' role='forward' />\n    <member type='way' ref='394150428' role='forward' />\n    <member type='way' ref='620169749' role='forward' />\n    <member type='way' ref='394458911' role='forward' />\n    <member type='way' ref='394150403' role='forward' />\n    <member type='way' ref='394458910' role='forward' />\n    <member type='way' ref='394150412' role='forward' />\n    <member type='way' ref='394465532' role='forward' />\n    <member type='way' ref='124952166' role='forward' />\n    <member type='way' ref='394465526' role='forward' />\n    <member type='way' ref='30682448' role='forward' />\n    <member type='way' ref='513522368' role='forward' />\n    <member type='way' ref='30682438' role='forward' />\n    <member type='way' ref='35869652' role='forward' />\n    <member type='way' ref='620247561' role='forward' />\n    <member type='way' ref='513522358' role='forward' />\n    <member type='way' ref='620169760' role='forward' />\n    <member type='way' ref='620169763' role='forward' />\n    <member type='way' ref='513534048' role='forward' />\n    <member type='way' ref='24326107' role='forward' />\n    <member type='way' ref='90536421' role='forward' />\n    <member type='way' ref='255330055' role='forward' />\n    <member type='way' ref='537838948' role='forward' />\n    <member type='way' ref='26968357' role='forward' />\n    <member type='way' ref='620247384' role='forward' />\n    <member type='way' ref='397086249' role='forward' />\n    <member type='way' ref='26765971' role='forward' />\n    <member type='way' ref='28412293' role='forward' />\n    <member type='way' ref='155000227' role='forward' />\n    <member type='way' ref='28412412' role='forward' />\n    <member type='way' ref='49922864' role='forward' />\n    <member type='way' ref='161493574' role='forward' />\n    <member type='way' ref='619568399' role='forward' />\n    <member type='way' ref='155000230' role='forward' />\n    <member type='way' ref='251506305' role='forward' />\n    <member type='way' ref='155000229' role='forward' />\n    <member type='way' ref='167952609' role='forward' />\n    <member type='way' ref='167952621' role='forward' />\n    <member type='way' ref='364469663' role='forward' />\n    <member type='way' ref='349663309' role='forward' />\n    <member type='way' ref='197885220' role='forward' />\n    <member type='way' ref='85562576' role='forward' />\n    <member type='way' ref='474751287' role='forward' />\n    <member type='way' ref='397028728' role='forward' />\n    <member type='way' ref='85562496' role='forward' />\n    <member type='way' ref='498532263' role='forward' />\n    <member type='way' ref='85562513' role='forward' />\n    <member type='way' ref='85562600' role='forward' />\n    <member type='way' ref='8922429' role='forward' />\n    <member type='way' ref='85562522' role='forward' />\n    <member type='way' ref='85562572' role='forward' />\n    <member type='way' ref='85562507' role='forward' />\n    <member type='way' ref='514527342' role='forward' />\n    <member type='way' ref='85562596' role='forward' />\n    <member type='way' ref='85562583' role='forward' />\n    <member type='way' ref='85562503' role='forward' />\n    <member type='way' ref='85562585' role='forward' />\n    <member type='way' ref='85562604' role='forward' />\n    <member type='way' ref='85562541' role='forward' />\n    <member type='way' ref='85562490' role='forward' />\n    <member type='way' ref='85562554' role='forward' />\n    <member type='way' ref='85562500' role='forward' />\n    <member type='way' ref='620205316' role='forward' />\n    <member type='way' ref='417091875' role='forward' />\n    <member type='way' ref='527829990' role='forward' />\n    <member type='way' ref='527829989' role='forward' />\n    <member type='way' ref='527829987' role='forward' />\n    <member type='way' ref='527829985' role='forward' />\n    <member type='way' ref='527829981' role='forward' />\n    <member type='way' ref='85644911' role='forward' />\n    <member type='way' ref='417094183' role='forward' />\n    <member type='way' ref='527857394' role='forward' />\n    <member type='way' ref='85644961' role='forward' />\n    <member type='way' ref='527829975' role='forward' />\n    <member type='way' ref='527829973' role='forward' />\n    <member type='way' ref='477214641' role='forward' />\n    <member type='way' ref='414960395' role='forward' />\n    <member type='way' ref='527829963' role='forward' />\n    <member type='way' ref='527829956' role='forward' />\n    <member type='way' ref='527829953' role='forward' />\n    <member type='way' ref='513870421' role='forward' />\n    <member type='way' ref='527829944' role='forward' />\n    <member type='way' ref='527829942' role='forward' />\n    <member type='way' ref='527829940' role='forward' />\n    <member type='way' ref='527829938' role='forward' />\n    <member type='way' ref='417087664' role='forward' />\n    <member type='way' ref='417087653' role='forward' />\n    <member type='way' ref='507188657' role='forward' />\n    <member type='way' ref='26943482' role='forward' />\n    <member type='way' ref='143666216' role='forward' />\n    <member type='way' ref='417087550' role='forward' />\n    <member type='way' ref='511666057' role='forward' />\n    <member type='way' ref='417087568' role='forward' />\n    <member type='way' ref='511666056' role='forward' />\n    <member type='way' ref='143666213' role='forward' />\n    <member type='way' ref='254759955' role='forward' />\n    <member type='way' ref='198565351' role='forward' />\n    <member type='way' ref='198565350' role='forward' />\n    <member type='way' ref='198565352' role='forward' />\n    <member type='way' ref='8915677' role='forward' />\n    <member type='way' ref='385184173' role='forward' />\n    <member type='way' ref='27167746' role='forward' />\n    <member type='way' ref='198651498' role='forward' />\n    <member type='way' ref='33111467' role='forward' />\n    <member type='way' ref='628897455' role='forward' />\n    <member type='way' ref='119237698' role='forward' />\n    <member type='way' ref='256787734' role='forward' />\n    <member type='way' ref='26943964' role='forward' />\n    <member type='way' ref='58841187' role='forward' />\n    <member type='way' ref='27183366' role='forward' />\n    <member type='way' ref='397267120' role='forward' />\n    <member type='way' ref='123741422' role='forward' />\n    <member type='way' ref='28326944' role='forward' />\n    <member type='way' ref='28326941' role='forward' />\n    <member type='way' ref='48037053' role='forward' />\n    <member type='way' ref='48037051' role='forward' />\n    <member type='way' ref='48036602' role='forward' />\n    <member type='way' ref='48036600' role='forward' />\n    <member type='way' ref='48582245' role='forward' />\n    <member type='way' ref='48037096' role='forward' />\n    <member type='way' ref='123741461' role='forward' />\n    <member type='way' ref='48191417' role='forward' />\n    <member type='way' ref='48101177' role='forward' />\n    <member type='way' ref='254308550' role='forward' />\n    <member type='way' ref='48101182' role='forward' />\n    <member type='way' ref='48101173' role='forward' />\n    <member type='way' ref='123741446' role='forward' />\n    <member type='way' ref='513983770' role='forward' />\n    <member type='way' ref='513983771' role='forward' />\n    <member type='way' ref='124019876' role='forward' />\n    <member type='way' ref='124019874' role='forward' />\n    <member type='way' ref='124019870' role='forward' />\n    <member type='way' ref='124019871' role='forward' />\n    <member type='way' ref='217582796' role='forward' />\n    <member type='way' ref='123692699' role='forward' />\n    <member type='way' ref='216932394' role='forward' />\n    <member type='way' ref='4970206' role='forward' />\n    <member type='way' ref='28947845' role='forward' />\n    <member type='way' ref='216932393' role='forward' />\n    <member type='way' ref='216932391' role='forward' />\n    <member type='way' ref='216932392' role='forward' />\n    <member type='way' ref='216932396' role='forward' />\n    <member type='way' ref='216932378' role='forward' />\n    <member type='way' ref='395577081' role='forward' />\n    <member type='way' ref='395577080' role='forward' />\n    <member type='way' ref='216932399' role='forward' />\n    <member type='way' ref='256787721' role='forward' />\n    <member type='way' ref='394222192' role='forward' />\n    <member type='way' ref='394222191' role='forward' />\n    <member type='way' ref='394222193' role='forward' />\n    <member type='way' ref='394222195' role='forward' />\n    <member type='way' ref='394222194' role='forward' />\n    <member type='way' ref='396939368' role='forward' />\n    <member type='way' ref='396936290' role='forward' />\n    <member type='way' ref='396936301' role='forward' />\n    <member type='way' ref='256787726' role='forward' />\n    <member type='way' ref='256787714' role='forward' />\n    <member type='way' ref='256787727' role='forward' />\n    <member type='way' ref='24054674' role='forward' />\n    <member type='way' ref='24054675' role='forward' />\n    <member type='way' ref='256787712' role='forward' />\n    <member type='way' ref='425562119' role='forward' />\n    <member type='way' ref='425562118' role='forward' />\n    <member type='way' ref='23720613' role='forward' />\n    <member type='way' ref='583791567' role='forward' />\n    <member type='way' ref='583791566' role='forward' />\n    <member type='way' ref='124952141' role='forward' />\n    <member type='way' ref='124952140' role='forward' />\n    <member type='way' ref='514310892' role='forward' />\n    <member type='way' ref='514310893' role='forward' />\n    <member type='way' ref='44427457' role='forward' />\n    <member type='way' ref='38151145' role='forward' />\n    <member type='way' ref='514310891' role='forward' />\n    <member type='way' ref='127284802' role='forward' />\n    <member type='way' ref='127284801' role='forward' />\n    <member type='way' ref='42380380' role='forward' />\n    <member type='way' ref='42380382' role='forward' />\n    <member type='way' ref='35837501' role='forward' />\n    <member type='way' ref='35837502' role='forward' />\n    <member type='way' ref='395436919' role='forward' />\n    <member type='way' ref='395436915' role='forward' />\n    <member type='way' ref='395446376' role='forward' />\n    <member type='way' ref='395446375' role='forward' />\n    <member type='way' ref='396010339' role='forward' />\n    <member type='way' ref='22942542' role='forward' />\n    <member type='way' ref='442943042' role='forward' />\n    <member type='way' ref='396010340' role='forward' />\n    <member type='way' ref='619117737' role='forward' />\n    <member type='way' ref='13104958' role='forward' />\n    <member type='way' ref='609836294' role='forward' />\n    <member type='way' ref='609836295' role='forward' />\n    <member type='way' ref='617235580' role='forward' />\n    <member type='way' ref='515733343' role='forward' />\n    <member type='way' ref='509747378' role='forward' />\n    <member type='way' ref='509747390' role='forward' />\n    <member type='way' ref='509747381' role='forward' />\n    <member type='way' ref='617259178' role='forward' />\n    <member type='way' ref='27628572' role='forward' />\n    <member type='way' ref='27628574' role='forward' />\n    <member type='way' ref='509747348' role='forward' />\n    <member type='way' ref='509747331' role='forward' />\n    <member type='way' ref='22372324' role='forward' />\n    <member type='way' ref='509747324' role='forward' />\n    <member type='way' ref='509747295' role='forward' />\n    <member type='way' ref='509747288' role='forward' />\n    <member type='way' ref='479773097' role='forward' />\n    <member type='way' ref='479773096' role='forward' />\n    <member type='way' ref='8954447' role='forward' />\n    <member type='way' ref='617259144' role='forward' />\n    <member type='way' ref='479773094' role='forward' />\n    <member type='way' ref='479773093' role='forward' />\n    <member type='way' ref='479773095' role='forward' />\n    <member type='way' ref='617259148' role='forward' />\n    <member type='way' ref='395318356' role='forward' />\n    <member type='way' ref='28485312' role='forward' />\n    <member type='way' ref='28485303' role='forward' />\n    <member type='way' ref='509747233' role='forward' />\n    <member type='way' ref='509721009' role='forward' />\n    <member type='way' ref='509721008' role='forward' />\n    <member type='way' ref='509721012' role='forward' />\n    <member type='way' ref='28414596' role='forward' />\n    <member type='way' ref='28414597' role='forward' />\n    <member type='way' ref='544300073' role='forward' />\n    <member type='way' ref='509721013' role='forward' />\n    <member type='way' ref='28414375' role='forward' />\n    <member type='way' ref='28414374' role='forward' />\n    <member type='way' ref='28414346' role='forward' />\n    <member type='way' ref='28414348' role='forward' />\n    <member type='way' ref='157607524' role='forward' />\n    <member type='way' ref='157607517' role='forward' />\n    <member type='way' ref='508479360' role='forward' />\n    <member type='way' ref='508479365' role='forward' />\n    <member type='way' ref='544301010' role='forward' />\n    <member type='way' ref='508479370' role='forward' />\n    <member type='way' ref='157607522' role='forward' />\n    <member type='way' ref='28414032' role='forward' />\n    <member type='way' ref='28414034' role='forward' />\n    <member type='way' ref='157806284' role='forward' />\n    <member type='way' ref='544296215' role='forward' />\n    <member type='way' ref='544296214' role='forward' />\n    <member type='way' ref='508137802' role='forward' />\n    <member type='way' ref='394449799' role='forward' />\n    <member type='way' ref='619042912' role='forward' />\n    <member type='way' ref='619042916' role='forward' />\n    <member type='way' ref='498017747' role='forward' />\n    <member type='way' ref='395368981' role='forward' />\n    <member type='way' ref='395368977' role='forward' />\n    <member type='way' ref='157806285' role='forward' />\n    <member type='way' ref='395387091' role='forward' />\n    <member type='way' ref='395368986' role='forward' />\n    <member type='way' ref='395387096' role='forward' />\n    <member type='way' ref='395394468' role='forward' />\n    <member type='way' ref='8157025' role='forward' />\n    <member type='way' ref='28399274' role='forward' />\n    <member type='way' ref='28399272' role='forward' />\n    <member type='way' ref='52306304' role='forward' />\n    <member type='way' ref='52306306' role='forward' />\n    <member type='way' ref='52305996' role='forward' />\n    <member type='way' ref='52305981' role='forward' />\n    <member type='way' ref='516535357' role='forward' />\n    <member type='way' ref='516535356' role='forward' />\n    <member type='way' ref='30583337' role='forward' />\n    <member type='way' ref='23807074' role='forward' />\n    <member type='way' ref='23807076' role='forward' />\n    <member type='way' ref='28437207' role='forward' />\n    <member type='way' ref='394385109' role='forward' />\n    <member type='way' ref='394385106' role='forward' />\n    <member type='way' ref='394385111' role='forward' />\n    <member type='way' ref='394385110' role='forward' />\n    <member type='way' ref='394385107' role='forward' />\n    <member type='way' ref='394381704' role='forward' />\n    <member type='way' ref='394381703' role='forward' />\n    <member type='way' ref='28437209' role='forward' />\n    <member type='way' ref='93539057' role='forward' />\n    <member type='way' ref='93539060' role='forward' />\n    <member type='way' ref='93539061' role='forward' />\n    <member type='way' ref='93539063' role='forward' />\n    <member type='way' ref='25463675' role='forward' />\n    <member type='way' ref='25463676' role='forward' />\n    <member type='way' ref='124952170' role='forward' />\n    <member type='way' ref='499352726' role='forward' />\n    <member type='way' ref='499352717' role='forward' />\n    <member type='way' ref='123741536' role='forward' />\n    <member type='way' ref='164553615' role='forward' />\n    <member type='way' ref='36020207' role='forward' />\n    <member type='way' ref='157807077' role='forward' />\n    <member type='way' ref='157807080' role='forward' />\n    <member type='way' ref='207380307' role='forward' />\n    <member type='way' ref='207380310' role='forward' />\n    <member type='way' ref='157807076' role='forward' />\n    <member type='way' ref='36020830' role='forward' />\n    <member type='way' ref='36020829' role='forward' />\n    <member type='way' ref='36020964' role='forward' />\n    <member type='way' ref='36020965' role='forward' />\n    <member type='way' ref='118632648' role='forward' />\n    <member type='way' ref='118632670' role='forward' />\n    <member type='way' ref='184081684' role='forward' />\n    <member type='way' ref='123741533' role='forward' />\n    <member type='way' ref='164778988' role='forward' />\n    <member type='way' ref='164778989' role='forward' />\n    <member type='way' ref='117809217' role='forward' />\n    <member type='way' ref='36021785' role='forward' />\n    <member type='way' ref='36021848' role='forward' />\n    <member type='way' ref='117809034' role='forward' />\n    <member type='way' ref='8941999' role='forward' />\n    <member type='way' ref='10526106' role='forward' />\n    <member type='way' ref='36022288' role='forward' />\n    <member type='way' ref='36022345' role='forward' />\n    <member type='way' ref='184081605' role='forward' />\n    <member type='way' ref='239214503' role='forward' />\n    <member type='way' ref='36022479' role='forward' />\n    <member type='way' ref='36022474' role='forward' />\n    <member type='way' ref='239214508' role='forward' />\n    <member type='way' ref='26902078' role='forward' />\n    <member type='way' ref='254292681' role='forward' />\n    <member type='way' ref='117809270' role='forward' />\n    <member type='way' ref='254292682' role='forward' />\n    <member type='way' ref='111059131' role='forward' />\n    <member type='way' ref='311825343' role='forward' />\n    <member type='way' ref='499352679' role='forward' />\n    <member type='way' ref='313638858' role='forward' />\n    <member type='way' ref='157326408' role='forward' />\n    <member type='way' ref='111059126' role='forward' />\n    <member type='way' ref='313637077' role='forward' />\n    <member type='way' ref='436399720' role='forward' />\n    <member type='way' ref='27214479' role='forward' />\n    <member type='way' ref='436399724' role='forward' />\n    <member type='way' ref='436399727' role='forward' />\n    <member type='way' ref='184081650' role='forward' />\n    <member type='way' ref='48143367' role='forward' />\n    <member type='way' ref='48143364' role='forward' />\n    <member type='way' ref='118632724' role='forward' />\n    <member type='way' ref='118632662' role='forward' />\n    <member type='way' ref='48143370' role='forward' />\n    <member type='way' ref='109831785' role='forward' />\n    <member type='way' ref='184081595' role='forward' />\n    <member type='way' ref='10462166' role='forward' />\n    <member type='way' ref='186263560' role='forward' />\n    <member type='way' ref='186263568' role='forward' />\n    <member type='way' ref='27213533' role='forward' />\n    <member type='way' ref='184081667' role='forward' />\n    <member type='way' ref='184081621' role='forward' />\n    <member type='way' ref='184081623' role='forward' />\n    <member type='way' ref='184081594' role='forward' />\n    <member type='way' ref='184081602' role='forward' />\n    <member type='way' ref='47391864' role='forward' />\n    <member type='way' ref='184081645' role='forward' />\n    <member type='way' ref='186263566' role='forward' />\n    <member type='way' ref='10475770' role='forward' />\n    <member type='way' ref='10462173' role='forward' />\n    <member type='way' ref='117809010' role='forward' />\n    <member type='way' ref='27212749' role='forward' />\n    <member type='way' ref='184081642' role='forward' />\n    <member type='way' ref='184081601' role='forward' />\n    <member type='way' ref='117808977' role='forward' />\n    <member type='way' ref='27212392' role='forward' />\n    <member type='way' ref='47391818' role='forward' />\n    <member type='way' ref='47391820' role='forward' />\n    <member type='way' ref='47391813' role='forward' />\n    <member type='way' ref='47391824' role='forward' />\n    <member type='way' ref='184081639' role='forward' />\n    <member type='way' ref='47391825' role='forward' />\n    <member type='way' ref='47391817' role='forward' />\n    <member type='way' ref='47391832' role='forward' />\n    <member type='way' ref='47391829' role='forward' />\n    <member type='way' ref='47391834' role='forward' />\n    <member type='way' ref='47391810' role='forward' />\n    <member type='way' ref='254292684' role='forward' />\n    <member type='way' ref='47391849' role='forward' />\n    <member type='way' ref='160057231' role='forward' />\n    <member type='way' ref='186264547' role='forward' />\n    <member type='way' ref='186264542' role='forward' />\n    <member type='way' ref='118632683' role='forward' />\n    <member type='way' ref='118632721' role='forward' />\n    <member type='way' ref='47391845' role='forward' />\n    <member type='way' ref='47391835' role='forward' />\n    <member type='way' ref='47391841' role='forward' />\n    <member type='way' ref='47391807' role='forward' />\n    <member type='way' ref='47391879' role='forward' />\n    <member type='way' ref='47391880' role='forward' />\n    <member type='way' ref='10462114' role='forward' />\n    <member type='way' ref='64377314' role='forward' />\n    <member type='way' ref='64377313' role='forward' />\n    <member type='way' ref='435230398' role='forward' />\n    <member type='way' ref='186263563' role='forward' />\n    <member type='way' ref='186263564' role='forward' />\n    <member type='way' ref='118632729' role='forward' />\n    <member type='way' ref='118632713' role='forward' />\n    <member type='way' ref='208616319' role='forward' />\n    <member type='way' ref='208616320' role='forward' />\n    <member type='way' ref='435230396' role='forward' />\n    <member type='way' ref='27211404' role='forward' />\n    <member type='way' ref='64377307' role='forward' />\n    <member type='way' ref='64377304' role='forward' />\n    <member type='way' ref='184081644' role='forward' />\n    <member type='way' ref='118632655' role='forward' />\n    <member type='way' ref='118632695' role='forward' />\n    <member type='way' ref='254292688' role='forward' />\n    <member type='way' ref='64377297' role='forward' />\n    <member type='way' ref='64377296' role='forward' />\n    <member type='way' ref='184081597' role='forward' />\n    <member type='way' ref='117809194' role='forward' />\n    <member type='way' ref='363409354' role='forward' />\n    <member type='way' ref='363409355' role='forward' />\n    <member type='way' ref='117809110' role='forward' />\n    <member type='way' ref='186263570' role='forward' />\n    <member type='way' ref='118632674' role='forward' />\n    <member type='way' ref='118632690' role='forward' />\n    <member type='way' ref='172477246' role='forward' />\n    <member type='way' ref='172477248' role='forward' />\n    <member type='way' ref='184549516' role='forward' />\n    <member type='way' ref='60920245' role='forward' />\n    <member type='way' ref='60920183' role='forward' />\n    <member type='way' ref='184549524' role='forward' />\n    <member type='way' ref='30613302' role='forward' />\n    <member type='way' ref='30613306' role='forward' />\n    <member type='way' ref='164754805' role='forward' />\n    <member type='way' ref='164754804' role='forward' />\n    <member type='way' ref='607816008' role='forward' />\n    <member type='way' ref='344628967' role='forward' />\n    <member type='way' ref='344628969' role='forward' />\n    <member type='way' ref='275350448' role='forward' />\n    <member type='way' ref='184081669' role='forward' />\n    <member type='way' ref='64378426' role='forward' />\n    <member type='way' ref='64378425' role='forward' />\n    <member type='way' ref='64378423' role='forward' />\n    <member type='way' ref='64380381' role='forward' />\n    <member type='way' ref='184081606' role='forward' />\n    <member type='way' ref='117809057' role='forward' />\n    <member type='way' ref='345758126' role='forward' />\n    <member type='way' ref='345758127' role='forward' />\n    <member type='way' ref='322883975' role='forward' />\n    <member type='way' ref='377625542' role='forward' />\n    <member type='way' ref='322883974' role='forward' />\n    <member type='way' ref='64380422' role='forward' />\n    <member type='way' ref='184081641' role='forward' />\n    <member type='way' ref='64384117' role='forward' />\n    <member type='way' ref='64384118' role='forward' />\n    <member type='way' ref='10564855' role='forward' />\n    <member type='way' ref='184247606' role='forward' />\n    <member type='way' ref='60419296' role='forward' />\n    <member type='way' ref='60419298' role='forward' />\n    <member type='way' ref='60419243' role='forward' />\n    <member type='way' ref='60419224' role='forward' />\n    <member type='way' ref='186263541' role='forward' />\n    <member type='way' ref='186263543' role='forward' />\n    <member type='way' ref='344973987' role='forward' />\n    <member type='way' ref='344973966' role='forward' />\n    <member type='way' ref='164754167' role='forward' />\n    <member type='way' ref='164754169' role='forward' />\n    <member type='way' ref='60418512' role='forward' />\n    <member type='way' ref='60615707' role='forward' />\n    <member type='way' ref='60418460' role='forward' />\n    <member type='way' ref='60418461' role='forward' />\n    <member type='way' ref='10561000' role='forward' />\n    <member type='way' ref='32869990' role='forward' />\n    <member type='way' ref='32869991' role='forward' />\n    <member type='way' ref='32870279' role='forward' />\n    <member type='way' ref='32870280' role='forward' />\n    <member type='way' ref='60418167' role='forward' />\n    <member type='way' ref='60418168' role='forward' />\n    <member type='way' ref='60418056' role='forward' />\n    <member type='way' ref='60418055' role='forward' />\n    <member type='way' ref='435094863' role='forward' />\n    <member type='way' ref='435094864' role='forward' />\n    <member type='way' ref='184081612' role='forward' />\n    <member type='way' ref='435094865' role='forward' />\n    <member type='way' ref='435094869' role='forward' />\n    <member type='way' ref='119237652' role='forward' />\n    <member type='way' ref='60417191' role='forward' />\n    <member type='way' ref='435094873' role='forward' />\n    <member type='way' ref='435094874' role='forward' />\n    <member type='way' ref='184081657' role='forward' />\n    <member type='way' ref='60417031' role='forward' />\n    <member type='way' ref='60417029' role='forward' />\n    <member type='way' ref='188720938' role='forward' />\n    <member type='way' ref='188720933' role='forward' />\n    <member type='way' ref='253128478' role='forward' />\n    <member type='way' ref='253128477' role='forward' />\n    <member type='way' ref='172211745' role='forward' />\n    <member type='way' ref='172211755' role='forward' />\n    <member type='way' ref='119524714' role='forward' />\n    <member type='way' ref='106447193' role='forward' />\n    <member type='way' ref='106447194' role='forward' />\n    <member type='way' ref='170080277' role='forward' />\n    <member type='way' ref='185142210' role='forward' />\n    <member type='way' ref='172050955' role='forward' />\n    <member type='way' ref='172050957' role='forward' />\n    <member type='way' ref='106706500' role='forward' />\n    <member type='way' ref='106706503' role='forward' />\n    <member type='way' ref='106801242' role='forward' />\n    <member type='way' ref='106801230' role='forward' />\n    <member type='way' ref='184081694' role='forward' />\n    <member type='way' ref='119237655' role='forward' />\n    <member type='way' ref='186248362' role='forward' />\n    <member type='way' ref='42046406' role='forward' />\n    <member type='way' ref='42046402' role='forward' />\n    <member type='way' ref='184081688' role='forward' />\n    <member type='way' ref='157484385' role='forward' />\n    <member type='way' ref='184081687' role='forward' />\n    <member type='way' ref='26946072' role='forward' />\n    <member type='way' ref='26946070' role='forward' />\n    <member type='way' ref='184081686' role='forward' />\n    <member type='way' ref='416771255' role='forward' />\n    <member type='way' ref='416771259' role='forward' />\n    <member type='way' ref='184255672' role='forward' />\n    <member type='way' ref='416771279' role='forward' />\n    <member type='way' ref='416771282' role='forward' />\n    <member type='way' ref='416771295' role='forward' />\n    <member type='way' ref='416771301' role='forward' />\n    <member type='way' ref='416771318' role='forward' />\n    <member type='way' ref='416771319' role='forward' />\n    <member type='way' ref='416771327' role='forward' />\n    <member type='way' ref='416771328' role='forward' />\n    <member type='way' ref='574427455' role='forward' />\n    <member type='way' ref='574427462' role='forward' />\n    <member type='way' ref='574427466' role='forward' />\n    <member type='way' ref='184255673' role='forward' />\n    <member type='way' ref='30723967' role='forward' />\n    <member type='way' ref='30723969' role='forward' />\n    <member type='way' ref='346096035' role='forward' />\n    <member type='way' ref='346096034' role='forward' />\n    <member type='way' ref='416771337' role='forward' />\n    <member type='way' ref='99417083' role='forward' />\n    <member type='way' ref='99417107' role='forward' />\n    <member type='way' ref='186248365' role='forward' />\n    <member type='way' ref='98963568' role='forward' />\n    <member type='way' ref='98963524' role='forward' />\n    <member type='way' ref='65066562' role='forward' />\n    <member type='way' ref='98963554' role='forward' />\n    <member type='way' ref='98963523' role='forward' />\n    <member type='way' ref='65066563' role='forward' />\n    <member type='way' ref='16253462' role='forward' />\n    <member type='way' ref='186248337' role='forward' />\n    <member type='way' ref='98963539' role='forward' />\n    <member type='way' ref='98963572' role='forward' />\n    <member type='way' ref='119237661' role='forward' />\n    <member type='way' ref='65066574' role='forward' />\n    <member type='way' ref='133349512' role='forward' />\n    <member type='way' ref='561820398' role='forward' />\n    <member type='way' ref='561820397' role='forward' />\n    <member type='way' ref='69760178' role='forward' />\n    <member type='way' ref='119237662' role='forward' />\n    <member type='way' ref='437355091' role='forward' />\n    <member type='way' ref='437355090' role='forward' />\n    <member type='way' ref='69755059' role='forward' />\n    <member type='way' ref='191004146' role='forward' />\n    <member type='way' ref='191004148' role='forward' />\n    <member type='way' ref='39422996' role='forward' />\n    <member type='way' ref='69727560' role='forward' />\n    <member type='way' ref='30615296' role='forward' />\n    <member type='way' ref='30615291' role='forward' />\n    <member type='way' ref='69727559' role='forward' />\n    <member type='way' ref='30615285' role='forward' />\n    <member type='way' ref='30615284' role='forward' />\n    <member type='way' ref='69760161' role='forward' />\n    <member type='way' ref='30615279' role='forward' />\n    <member type='way' ref='30615281' role='forward' />\n    <member type='way' ref='30615274' role='forward' />\n    <member type='way' ref='30615273' role='forward' />\n    <member type='way' ref='37803659' role='forward' />\n    <member type='way' ref='37803660' role='forward' />\n    <member type='way' ref='70331150' role='forward' />\n    <member type='way' ref='30615272' role='forward' />\n    <member type='way' ref='30615268' role='forward' />\n    <member type='way' ref='30615266' role='forward' />\n    <member type='way' ref='30615267' role='forward' />\n    <member type='way' ref='30615261' role='forward' />\n    <member type='way' ref='30615260' role='forward' />\n    <member type='way' ref='295048535' role='forward' />\n    <member type='way' ref='295048531' role='forward' />\n    <member type='way' ref='142545830' role='forward' />\n    <member type='way' ref='142545832' role='forward' />\n    <member type='way' ref='30615258' role='forward' />\n    <member type='way' ref='30615259' role='forward' />\n    <member type='way' ref='70331110' role='forward' />\n    <member type='way' ref='70331157' role='forward' />\n    <member type='way' ref='37394199' role='forward' />\n    <member type='way' ref='37394200' role='forward' />\n    <member type='way' ref='70331117' role='forward' />\n    <member type='way' ref='37392239' role='forward' />\n    <member type='way' ref='37392237' role='forward' />\n    <member type='way' ref='37392233' role='forward' />\n    <member type='way' ref='37392234' role='forward' />\n    <member type='way' ref='37392226' role='forward' />\n    <member type='way' ref='37392227' role='forward' />\n    <member type='way' ref='37392215' role='forward' />\n    <member type='way' ref='37392216' role='forward' />\n    <member type='way' ref='184081603' role='forward' />\n    <member type='way' ref='37219978' role='forward' />\n    <member type='way' ref='37219979' role='forward' />\n    <member type='way' ref='10743444' role='forward' />\n    <member type='way' ref='37219974' role='forward' />\n    <member type='way' ref='122900717' role='forward' />\n    <member type='way' ref='122900721' role='forward' />\n    <member type='way' ref='122900700' role='forward' />\n    <member type='way' ref='122900710' role='forward' />\n    <member type='way' ref='37219962' role='forward' />\n    <member type='way' ref='37219961' role='forward' />\n    <member type='way' ref='122900698' role='forward' />\n    <member type='way' ref='37219954' role='forward' />\n    <member type='way' ref='37219955' role='forward' />\n    <member type='way' ref='37219953' role='forward' />\n    <member type='way' ref='37219952' role='forward' />\n    <member type='way' ref='10733315' role='forward' />\n    <member type='way' ref='10743455' role='forward' />\n    <member type='way' ref='37214834' role='forward' />\n    <member type='way' ref='37214833' role='forward' />\n    <member type='way' ref='37214831' role='forward' />\n    <member type='way' ref='37214832' role='forward' />\n    <member type='way' ref='432346715' role='forward' />\n    <member type='way' ref='432346714' role='forward' />\n    <member type='way' ref='227623523' role='forward' />\n    <member type='way' ref='37214825' role='forward' />\n    <member type='way' ref='37214826' role='forward' />\n    <member type='way' ref='37058470' role='forward' />\n    <member type='way' ref='122900711' role='forward' />\n    <member type='way' ref='37058472' role='forward' />\n    <member type='way' ref='37058481' role='forward' />\n    <member type='way' ref='37058482' role='forward' />\n    <member type='way' ref='37058467' role='forward' />\n    <member type='way' ref='37058468' role='forward' />\n    <member type='way' ref='122900704' role='forward' />\n    <member type='way' ref='37058490' role='forward' />\n    <member type='way' ref='37058489' role='forward' />\n    <member type='way' ref='37058494' role='forward' />\n    <member type='way' ref='37058493' role='forward' />\n    <member type='way' ref='37058498' role='forward' />\n    <member type='way' ref='37058497' role='forward' />\n    <member type='way' ref='37058708' role='forward' />\n    <member type='way' ref='37058707' role='forward' />\n    <member type='way' ref='122900725' role='forward' />\n    <member type='way' ref='37058698' role='forward' />\n    <member type='way' ref='37058699' role='forward' />\n    <member type='way' ref='37058705' role='forward' />\n    <member type='way' ref='37058704' role='forward' />\n    <member type='way' ref='122900702' role='forward' />\n    <member type='way' ref='37058738' role='forward' />\n    <member type='way' ref='37058737' role='forward' />\n    <member type='way' ref='37058743' role='forward' />\n    <member type='way' ref='37058744' role='forward' />\n    <member type='way' ref='37058748' role='forward' />\n    <member type='way' ref='37058747' role='forward' />\n    <member type='way' ref='122900744' role='forward' />\n    <member type='way' ref='32551737' role='forward' />\n    <member type='way' ref='32551738' role='forward' />\n    <member type='way' ref='227624352' role='forward' />\n    <member type='way' ref='10743452' role='forward' />\n    <member type='way' ref='122900716' role='forward' />\n    <member type='way' ref='10733313' role='forward' />\n    <member type='way' ref='10742767' role='forward' />\n    <member type='way' ref='37058816' role='forward' />\n    <member type='way' ref='10743449' role='forward' />\n    <member type='way' ref='10733322' role='forward' />\n    <member type='way' ref='30624374' role='forward' />\n    <member type='way' ref='30624376' role='forward' />\n    <member type='way' ref='37048284' role='forward' />\n    <member type='way' ref='37048285' role='forward' />\n    <member type='way' ref='130624069' role='forward' />\n    <member type='way' ref='130624068' role='forward' />\n    <member type='way' ref='122900673' role='forward' />\n    <member type='way' ref='40097769' role='forward' />\n    <member type='way' ref='10743457' role='forward' />\n    <member type='way' ref='122900728' role='forward' />\n    <member type='way' ref='40047532' role='forward' />\n    <member type='way' ref='40047533' role='forward' />\n    <member type='way' ref='122900703' role='forward' />\n    <member type='way' ref='387003163' role='forward' />\n    <member type='way' ref='184482690' role='forward' />\n    <member type='way' ref='184482696' role='forward' />\n    <member type='way' ref='40399088' role='forward' />\n    <member type='way' ref='607788619' role='forward' />\n    <member type='way' ref='40399089' role='forward' />\n    <member type='way' ref='23891381' role='forward' />\n    <member type='way' ref='40033983' role='forward' />\n    <member type='way' ref='40033984' role='forward' />\n    <member type='way' ref='607788602' role='forward' />\n    <member type='way' ref='40033987' role='forward' />\n    <member type='way' ref='40033988' role='forward' />\n    <member type='way' ref='43720426' role='forward' />\n    <member type='way' ref='159240662' role='forward' />\n    <member type='way' ref='607788494' role='forward' />\n    <member type='way' ref='607788613' role='forward' />\n    <member type='way' ref='159243037' role='forward' />\n    <member type='way' ref='55468516' role='forward' />\n    <member type='way' ref='607788616' role='forward' />\n    <member type='way' ref='606514885' role='forward' />\n    <member type='way' ref='55468517' role='forward' />\n    <member type='way' ref='606514883' role='forward' />\n    <member type='way' ref='55468353' role='forward' />\n    <member type='way' ref='55468128' role='forward' />\n    <member type='way' ref='607788499' role='forward' />\n    <member type='way' ref='172235377' role='forward' />\n    <member type='way' ref='55468129' role='forward' />\n    <member type='way' ref='55467849' role='forward' />\n    <member type='way' ref='55467850' role='forward' />\n    <member type='way' ref='55467644' role='forward' />\n    <member type='way' ref='26959195' role='forward' />\n    <member type='way' ref='122900715' role='forward' />\n    <member type='way' ref='55467607' role='forward' />\n    <member type='way' ref='607788500' role='forward' />\n    <member type='way' ref='55467608' role='forward' />\n    <member type='way' ref='172205062' role='forward' />\n    <member type='way' ref='55467556' role='forward' />\n    <member type='way' ref='32491724' role='forward' />\n    <member type='way' ref='32491722' role='forward' />\n    <member type='way' ref='55467508' role='forward' />\n    <member type='way' ref='55467509' role='forward' />\n    <member type='way' ref='55467477' role='forward' />\n    <member type='way' ref='55467478' role='forward' />\n    <member type='way' ref='55467452' role='forward' />\n    <member type='way' ref='607788504' role='forward' />\n    <member type='way' ref='55467453' role='forward' />\n    <member type='way' ref='26960092' role='forward' />\n    <member type='way' ref='26960094' role='forward' />\n    <member type='way' ref='26960099' role='forward' />\n    <member type='way' ref='26960102' role='forward' />\n    <member type='way' ref='607788502' role='forward' />\n    <member type='way' ref='26959412' role='forward' />\n    <member type='way' ref='55467426' role='forward' />\n    <member type='way' ref='55467390' role='forward' />\n    <member type='way' ref='55467391' role='forward' />\n    <member type='way' ref='55467113' role='forward' />\n    <member type='way' ref='55467114' role='forward' />\n    <member type='way' ref='55466953' role='forward' />\n    <member type='way' ref='55466952' role='forward' />\n    <member type='way' ref='122900701' role='forward' />\n    <member type='way' ref='24813078' role='forward' />\n    <member type='way' ref='24813080' role='forward' />\n    <member type='way' ref='122900706' role='forward' />\n    <member type='way' ref='24813102' role='forward' />\n    <member type='way' ref='24813103' role='forward' />\n    <member type='way' ref='24813105' role='forward' />\n    <member type='way' ref='24813107' role='forward' />\n    <member type='way' ref='24813109' role='forward' />\n    <member type='way' ref='27827549' role='forward' />\n    <member type='way' ref='55900249' role='forward' />\n    <member type='way' ref='55900250' role='forward' />\n    <member type='way' ref='122900724' role='forward' />\n    <member type='way' ref='32864874' role='forward' />\n    <member type='way' ref='607788505' role='forward' />\n    <member type='way' ref='32864871' role='forward' />\n    <member type='way' ref='169789556' role='forward' />\n    <member type='way' ref='169789555' role='forward' />\n    <member type='way' ref='27827550' role='forward' />\n    <member type='way' ref='27287058' role='forward' />\n    <member type='way' ref='55900237' role='forward' />\n    <member type='way' ref='55900242' role='forward' />\n    <member type='way' ref='44335127' role='forward' />\n    <member type='way' ref='44335126' role='forward' />\n    <member type='way' ref='66519378' role='forward' />\n    <member type='way' ref='66519379' role='forward' />\n    <member type='way' ref='66519376' role='forward' />\n    <member type='way' ref='66519375' role='forward' />\n    <member type='way' ref='27287060' role='forward' />\n    <member type='way' ref='27286947' role='forward' />\n    <member type='way' ref='27286946' role='forward' />\n    <member type='way' ref='27286722' role='forward' />\n    <member type='way' ref='27286725' role='forward' />\n    <member type='way' ref='24813112' role='forward' />\n    <member type='way' ref='172076623' role='forward' />\n    <member type='way' ref='299078428' role='forward' />\n    <member type='way' ref='32233218' role='forward' />\n    <member type='way' ref='32233202' role='forward' />\n    <member type='way' ref='55904292' role='forward' />\n    <member type='way' ref='172777685' role='forward' />\n    <member type='way' ref='172777690' role='forward' />\n    <member type='way' ref='122900683' role='forward' />\n    <member type='way' ref='25003400' role='forward' />\n    <member type='way' ref='25003401' role='forward' />\n    <member type='way' ref='25003394' role='forward' />\n    <member type='way' ref='24794617' role='forward' />\n    <member type='way' ref='24794606' role='forward' />\n    <member type='way' ref='24794598' role='forward' />\n    <member type='way' ref='122900675' role='forward' />\n    <member type='way' ref='31353386' role='forward' />\n    <member type='way' ref='31353366' role='forward' />\n    <member type='way' ref='226663235' role='forward' />\n    <member type='way' ref='226663234' role='forward' />\n    <member type='way' ref='25710104' role='forward' />\n    <member type='way' ref='226666943' role='forward' />\n    <member type='way' ref='607788508' role='forward' />\n    <member type='way' ref='607788506' role='forward' />\n    <member type='way' ref='148249780' role='forward' />\n    <member type='way' ref='25886743' role='forward' />\n    <member type='way' ref='25886744' role='forward' />\n    <member type='way' ref='31377238' role='forward' />\n    <member type='way' ref='31377243' role='forward' />\n    <member type='way' ref='31377399' role='forward' />\n    <member type='way' ref='607788513' role='forward' />\n    <member type='way' ref='31377403' role='forward' />\n    <member type='way' ref='607788512' role='forward' />\n    <member type='way' ref='24865476' role='forward' />\n    <member type='way' ref='24865470' role='forward' />\n    <member type='way' ref='31588768' role='forward' />\n    <member type='way' ref='31588766' role='forward' />\n    <member type='way' ref='31588510' role='forward' />\n    <member type='way' ref='226643927' role='forward' />\n    <member type='way' ref='31588562' role='forward' />\n    <member type='way' ref='226643929' role='forward' />\n    <member type='way' ref='31590080' role='forward' />\n    <member type='way' ref='148257356' role='forward' />\n    <member type='way' ref='226640533' role='forward' />\n    <member type='way' ref='31590109' role='forward' />\n    <member type='way' ref='607788519' role='forward' />\n    <member type='way' ref='31590110' role='forward' />\n    <member type='way' ref='72148097' role='forward' />\n    <member type='way' ref='226640532' role='forward' />\n    <member type='way' ref='122900682' role='forward' />\n    <member type='way' ref='24716925' role='forward' />\n    <member type='way' ref='24716927' role='forward' />\n    <member type='way' ref='148252743' role='forward' />\n    <member type='way' ref='148252747' role='forward' />\n    <member type='way' ref='226245457' role='forward' />\n    <member type='way' ref='148252755' role='forward' />\n    <member type='way' ref='607788622' role='forward' />\n    <member type='way' ref='122900676' role='forward' />\n    <member type='way' ref='120111293' role='forward' />\n    <member type='way' ref='120111294' role='forward' />\n    <member type='way' ref='122900680' role='forward' />\n    <member type='way' ref='120111291' role='forward' />\n    <member type='way' ref='120111286' role='forward' />\n    <member type='way' ref='122900677' role='forward' />\n    <member type='way' ref='148238646' role='forward' />\n    <member type='way' ref='148238634' role='forward' />\n    <member type='way' ref='608204519' role='forward' />\n    <member type='way' ref='28897213' role='forward' />\n    <member type='way' ref='122900696' role='forward' />\n    <member type='way' ref='148257370' role='forward' />\n    <member type='way' ref='28897231' role='forward' />\n    <member type='way' ref='122900695' role='forward' />\n    <member type='way' ref='148257375' role='forward' />\n    <member type='way' ref='28897240' role='forward' />\n    <member type='way' ref='148257379' role='forward' />\n    <member type='way' ref='28897245' role='forward' />\n    <member type='way' ref='122900692' role='forward' />\n    <member type='way' ref='148257371' role='forward' />\n    <member type='way' ref='28897250' role='forward' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='direction' v='south' />\n    <tag k='is_in:state' v='CA' />\n    <tag k='name' v='US 101 (CA southbound, south of Hopland)' />\n    <tag k='network' v='US:US' />\n    <tag k='ref' v='101' />\n    <tag k='route' v='road' />\n    <tag k='symbol' v='https://upload.wikimedia.org/wikipedia/commons/5/5b/US_101_(CA).svg' />\n    <tag k='type' v='route' />\n    <tag k='wikidata' v='Q410892' />\n    <tag k='wikipedia' v='en:U.S. Route 101' />\n  </relation>\n  <relation id='108619' timestamp='2018-08-27T06:35:31Z' uid='8107451' user='njtbusfan' version='510' changeset='62027969'>\n    <member type='way' ref='139639785' role='forward' />\n    <member type='way' ref='148257382' role='forward' />\n    <member type='way' ref='28897361' role='forward' />\n    <member type='way' ref='148257373' role='forward' />\n    <member type='way' ref='28897363' role='forward' />\n    <member type='way' ref='148257386' role='forward' />\n    <member type='way' ref='28897365' role='forward' />\n    <member type='way' ref='148257372' role='forward' />\n    <member type='way' ref='28897401' role='forward' />\n    <member type='way' ref='148238640' role='forward' />\n    <member type='way' ref='608204521' role='forward' />\n    <member type='way' ref='148238642' role='forward' />\n    <member type='way' ref='148238648' role='forward' />\n    <member type='way' ref='122900687' role='forward' />\n    <member type='way' ref='24706779' role='forward' />\n    <member type='way' ref='24706780' role='forward' />\n    <member type='way' ref='122900694' role='forward' />\n    <member type='way' ref='122900693' role='forward' />\n    <member type='way' ref='148257387' role='forward' />\n    <member type='way' ref='581408759' role='forward' />\n    <member type='way' ref='581407415' role='forward' />\n    <member type='way' ref='581407418' role='forward' />\n    <member type='way' ref='581407417' role='forward' />\n    <member type='way' ref='120111282' role='forward' />\n    <member type='way' ref='607788623' role='forward' />\n    <member type='way' ref='122900685' role='forward' />\n    <member type='way' ref='24716577' role='forward' />\n    <member type='way' ref='148252741' role='forward' />\n    <member type='way' ref='148252753' role='forward' />\n    <member type='way' ref='24716918' role='forward' />\n    <member type='way' ref='24716916' role='forward' />\n    <member type='way' ref='148252751' role='forward' />\n    <member type='way' ref='122900674' role='forward' />\n    <member type='way' ref='32528297' role='forward' />\n    <member type='way' ref='32528296' role='forward' />\n    <member type='way' ref='607788520' role='forward' />\n    <member type='way' ref='31590106' role='forward' />\n    <member type='way' ref='148252631' role='forward' />\n    <member type='way' ref='148252632' role='forward' />\n    <member type='way' ref='148252627' role='forward' />\n    <member type='way' ref='31590081' role='forward' />\n    <member type='way' ref='31588567' role='forward' />\n    <member type='way' ref='31588578' role='forward' />\n    <member type='way' ref='31588500' role='forward' />\n    <member type='way' ref='31588519' role='forward' />\n    <member type='way' ref='31588761' role='forward' />\n    <member type='way' ref='31588755' role='forward' />\n    <member type='way' ref='31378081' role='forward' />\n    <member type='way' ref='607788516' role='forward' />\n    <member type='way' ref='120230887' role='forward' />\n    <member type='way' ref='607788514' role='forward' />\n    <member type='way' ref='31378200' role='forward' />\n    <member type='way' ref='31378204' role='forward' />\n    <member type='way' ref='31377228' role='forward' />\n    <member type='way' ref='31377232' role='forward' />\n    <member type='way' ref='25886745' role='forward' />\n    <member type='way' ref='148238628' role='forward' />\n    <member type='way' ref='607788511' role='forward' />\n    <member type='way' ref='607788510' role='forward' />\n    <member type='way' ref='148238623' role='forward' />\n    <member type='way' ref='606788906' role='forward' />\n    <member type='way' ref='606788905' role='forward' />\n    <member type='way' ref='58583369' role='forward' />\n    <member type='way' ref='58583367' role='forward' />\n    <member type='way' ref='58583370' role='forward' />\n    <member type='way' ref='58583336' role='forward' />\n    <member type='way' ref='31353398' role='forward' />\n    <member type='way' ref='31353391' role='forward' />\n    <member type='way' ref='226328501' role='forward' />\n    <member type='way' ref='226328500' role='forward' />\n    <member type='way' ref='30636675' role='forward' />\n    <member type='way' ref='24794667' role='forward' />\n    <member type='way' ref='25003395' role='forward' />\n    <member type='way' ref='24794581' role='forward' />\n    <member type='way' ref='25003398' role='forward' />\n    <member type='way' ref='25003399' role='forward' />\n    <member type='way' ref='172076192' role='forward' />\n    <member type='way' ref='172076190' role='forward' />\n    <member type='way' ref='55904299' role='forward' />\n    <member type='way' ref='519777708' role='forward' />\n    <member type='way' ref='172076417' role='forward' />\n    <member type='way' ref='32233343' role='forward' />\n    <member type='way' ref='122900713' role='forward' />\n    <member type='way' ref='172076622' role='forward' />\n    <member type='way' ref='27286724' role='forward' />\n    <member type='way' ref='27286723' role='forward' />\n    <member type='way' ref='27286945' role='forward' />\n    <member type='way' ref='27286944' role='forward' />\n    <member type='way' ref='27287063' role='forward' />\n    <member type='way' ref='27287065' role='forward' />\n    <member type='way' ref='66519380' role='forward' />\n    <member type='way' ref='66519374' role='forward' />\n    <member type='way' ref='66519377' role='forward' />\n    <member type='way' ref='66519384' role='forward' />\n    <member type='way' ref='44335129' role='forward' />\n    <member type='way' ref='44335128' role='forward' />\n    <member type='way' ref='55900239' role='forward' />\n    <member type='way' ref='55900238' role='forward' />\n    <member type='way' ref='27827528' role='forward' />\n    <member type='way' ref='27827529' role='forward' />\n    <member type='way' ref='169789553' role='forward' />\n    <member type='way' ref='169789554' role='forward' />\n    <member type='way' ref='607788521' role='forward' />\n    <member type='way' ref='32864872' role='forward' />\n    <member type='way' ref='32864873' role='forward' />\n    <member type='way' ref='122900722' role='forward' />\n    <member type='way' ref='55900251' role='forward' />\n    <member type='way' ref='55900246' role='forward' />\n    <member type='way' ref='122900737' role='forward' />\n    <member type='way' ref='24813402' role='forward' />\n    <member type='way' ref='24813405' role='forward' />\n    <member type='way' ref='24813412' role='forward' />\n    <member type='way' ref='24813416' role='forward' />\n    <member type='way' ref='24813417' role='forward' />\n    <member type='way' ref='24813421' role='forward' />\n    <member type='way' ref='169645785' role='forward' />\n    <member type='way' ref='24813380' role='forward' />\n    <member type='way' ref='4252626' role='forward' />\n    <member type='way' ref='169645783' role='forward' />\n    <member type='way' ref='122900712' role='forward' />\n    <member type='way' ref='55466955' role='forward' />\n    <member type='way' ref='55466954' role='forward' />\n    <member type='way' ref='55467118' role='forward' />\n    <member type='way' ref='55467116' role='forward' />\n    <member type='way' ref='607788618' role='forward' />\n    <member type='way' ref='55467388' role='forward' />\n    <member type='way' ref='55467389' role='forward' />\n    <member type='way' ref='55467425' role='forward' />\n    <member type='way' ref='55467424' role='forward' />\n    <member type='way' ref='26960101' role='forward' />\n    <member type='way' ref='26960100' role='forward' />\n    <member type='way' ref='26960095' role='forward' />\n    <member type='way' ref='26960090' role='forward' />\n    <member type='way' ref='607788503' role='forward' />\n    <member type='way' ref='55467451' role='forward' />\n    <member type='way' ref='55467450' role='forward' />\n    <member type='way' ref='55467475' role='forward' />\n    <member type='way' ref='55467476' role='forward' />\n    <member type='way' ref='55467511' role='forward' />\n    <member type='way' ref='55467510' role='forward' />\n    <member type='way' ref='32491725' role='forward' />\n    <member type='way' ref='32491727' role='forward' />\n    <member type='way' ref='55467559' role='forward' />\n    <member type='way' ref='55467558' role='forward' />\n    <member type='way' ref='607788501' role='forward' />\n    <member type='way' ref='55467606' role='forward' />\n    <member type='way' ref='55467604' role='forward' />\n    <member type='way' ref='122900705' role='forward' />\n    <member type='way' ref='55467645' role='forward' />\n    <member type='way' ref='26959197' role='forward' />\n    <member type='way' ref='15239052' role='forward' />\n    <member type='way' ref='55467851' role='forward' />\n    <member type='way' ref='55467852' role='forward' />\n    <member type='way' ref='55468126' role='forward' />\n    <member type='way' ref='55468127' role='forward' />\n    <member type='way' ref='55468355' role='forward' />\n    <member type='way' ref='607788498' role='forward' />\n    <member type='way' ref='606514882' role='forward' />\n    <member type='way' ref='172235378' role='forward' />\n    <member type='way' ref='606514884' role='forward' />\n    <member type='way' ref='607788615' role='forward' />\n    <member type='way' ref='55468515' role='forward' />\n    <member type='way' ref='607788612' role='forward' />\n    <member type='way' ref='607788490' role='forward' />\n    <member type='way' ref='55468514' role='forward' />\n    <member type='way' ref='159240676' role='forward' />\n    <member type='way' ref='43720330' role='forward' />\n    <member type='way' ref='40033985' role='forward' />\n    <member type='way' ref='607788603' role='forward' />\n    <member type='way' ref='40033986' role='forward' />\n    <member type='way' ref='40033981' role='forward' />\n    <member type='way' ref='40033982' role='forward' />\n    <member type='way' ref='43720392' role='forward' />\n    <member type='way' ref='387003039' role='forward' />\n    <member type='way' ref='40399086' role='forward' />\n    <member type='way' ref='40399087' role='forward' />\n    <member type='way' ref='184482694' role='forward' />\n    <member type='way' ref='184482698' role='forward' />\n    <member type='way' ref='10743464' role='forward' />\n    <member type='way' ref='40047530' role='forward' />\n    <member type='way' ref='10733323' role='forward' />\n    <member type='way' ref='40097767' role='forward' />\n    <member type='way' ref='40097766' role='forward' />\n    <member type='way' ref='130624063' role='forward' />\n    <member type='way' ref='130624067' role='forward' />\n    <member type='way' ref='37048282' role='forward' />\n    <member type='way' ref='37048283' role='forward' />\n    <member type='way' ref='10743463' role='forward' />\n    <member type='way' ref='30624372' role='forward' />\n    <member type='way' ref='10743459' role='forward' />\n    <member type='way' ref='10742768' role='forward' />\n    <member type='way' ref='37058815' role='forward' />\n    <member type='way' ref='10733327' role='forward' />\n    <member type='way' ref='10743450' role='forward' />\n    <member type='way' ref='122900697' role='forward' />\n    <member type='way' ref='10733324' role='forward' />\n    <member type='way' ref='227624354' role='forward' />\n    <member type='way' ref='32551739' role='forward' />\n    <member type='way' ref='32551741' role='forward' />\n    <member type='way' ref='122900723' role='forward' />\n    <member type='way' ref='37058746' role='forward' />\n    <member type='way' ref='37058745' role='forward' />\n    <member type='way' ref='37058741' role='forward' />\n    <member type='way' ref='37058742' role='forward' />\n    <member type='way' ref='37058739' role='forward' />\n    <member type='way' ref='37058740' role='forward' />\n    <member type='way' ref='37058703' role='forward' />\n    <member type='way' ref='37058702' role='forward' />\n    <member type='way' ref='122900708' role='forward' />\n    <member type='way' ref='37058695' role='forward' />\n    <member type='way' ref='37058697' role='forward' />\n    <member type='way' ref='37058706' role='forward' />\n    <member type='way' ref='37058709' role='forward' />\n    <member type='way' ref='37058495' role='forward' />\n    <member type='way' ref='37058496' role='forward' />\n    <member type='way' ref='37058491' role='forward' />\n    <member type='way' ref='37058492' role='forward' />\n    <member type='way' ref='37058488' role='forward' />\n    <member type='way' ref='37058487' role='forward' />\n    <member type='way' ref='37058466' role='forward' />\n    <member type='way' ref='37058464' role='forward' />\n    <member type='way' ref='122900707' role='forward' />\n    <member type='way' ref='37058480' role='forward' />\n    <member type='way' ref='37058478' role='forward' />\n    <member type='way' ref='37058471' role='forward' />\n    <member type='way' ref='122900714' role='forward' />\n    <member type='way' ref='37058469' role='forward' />\n    <member type='way' ref='37214823' role='forward' />\n    <member type='way' ref='37214824' role='forward' />\n    <member type='way' ref='227623524' role='forward' />\n    <member type='way' ref='432346712' role='forward' />\n    <member type='way' ref='432346713' role='forward' />\n    <member type='way' ref='37214830' role='forward' />\n    <member type='way' ref='37214829' role='forward' />\n    <member type='way' ref='37214836' role='forward' />\n    <member type='way' ref='37214835' role='forward' />\n    <member type='way' ref='10733335' role='forward' />\n    <member type='way' ref='10743465' role='forward' />\n    <member type='way' ref='37219950' role='forward' />\n    <member type='way' ref='37219951' role='forward' />\n    <member type='way' ref='37219957' role='forward' />\n    <member type='way' ref='37219958' role='forward' />\n    <member type='way' ref='122900741' role='forward' />\n    <member type='way' ref='37219960' role='forward' />\n    <member type='way' ref='37219959' role='forward' />\n    <member type='way' ref='122900709' role='forward' />\n    <member type='way' ref='186430831' role='forward' />\n    <member type='way' ref='122900719' role='forward' />\n    <member type='way' ref='122900720' role='forward' />\n    <member type='way' ref='122900699' role='forward' />\n    <member type='way' ref='37219973' role='forward' />\n    <member type='way' ref='16253445' role='forward' />\n    <member type='way' ref='37219977' role='forward' />\n    <member type='way' ref='37219975' role='forward' />\n    <member type='way' ref='184081629' role='forward' />\n    <member type='way' ref='37392217' role='forward' />\n    <member type='way' ref='37392218' role='forward' />\n    <member type='way' ref='37392228' role='forward' />\n    <member type='way' ref='16253472' role='forward' />\n    <member type='way' ref='37392231' role='forward' />\n    <member type='way' ref='37392232' role='forward' />\n    <member type='way' ref='37392236' role='forward' />\n    <member type='way' ref='37392238' role='forward' />\n    <member type='way' ref='70331140' role='forward' />\n    <member type='way' ref='37394198' role='forward' />\n    <member type='way' ref='37394197' role='forward' />\n    <member type='way' ref='70331125' role='forward' />\n    <member type='way' ref='37394203' role='forward' />\n    <member type='way' ref='37394204' role='forward' />\n    <member type='way' ref='70331120' role='forward' />\n    <member type='way' ref='30615257' role='forward' />\n    <member type='way' ref='30615256' role='forward' />\n    <member type='way' ref='142545828' role='forward' />\n    <member type='way' ref='142545833' role='forward' />\n    <member type='way' ref='295048529' role='forward' />\n    <member type='way' ref='295048526' role='forward' />\n    <member type='way' ref='30615263' role='forward' />\n    <member type='way' ref='30615262' role='forward' />\n    <member type='way' ref='30615264' role='forward' />\n    <member type='way' ref='30615265' role='forward' />\n    <member type='way' ref='30615269' role='forward' />\n    <member type='way' ref='30615271' role='forward' />\n    <member type='way' ref='70331153' role='forward' />\n    <member type='way' ref='37803661' role='forward' />\n    <member type='way' ref='37803658' role='forward' />\n    <member type='way' ref='30615275' role='forward' />\n    <member type='way' ref='30615276' role='forward' />\n    <member type='way' ref='30615278' role='forward' />\n    <member type='way' ref='30615277' role='forward' />\n    <member type='way' ref='69760168' role='forward' />\n    <member type='way' ref='30615283' role='forward' />\n    <member type='way' ref='30615282' role='forward' />\n    <member type='way' ref='69727469' role='forward' />\n    <member type='way' ref='69755060' role='forward' />\n    <member type='way' ref='30615293' role='forward' />\n    <member type='way' ref='39422997' role='forward' />\n    <member type='way' ref='69727440' role='forward' />\n    <member type='way' ref='16253470' role='forward' />\n    <member type='way' ref='69727567' role='forward' />\n    <member type='way' ref='69727496' role='forward' />\n    <member type='way' ref='191004145' role='forward' />\n    <member type='way' ref='191004147' role='forward' />\n    <member type='way' ref='69755064' role='forward' />\n    <member type='way' ref='437355093' role='forward' />\n    <member type='way' ref='437355092' role='forward' />\n    <member type='way' ref='119237693' role='forward' />\n    <member type='way' ref='69760188' role='forward' />\n    <member type='way' ref='65066575' role='forward' />\n    <member type='way' ref='119237660' role='forward' />\n    <member type='way' ref='98963540' role='forward' />\n    <member type='way' ref='98963562' role='forward' />\n    <member type='way' ref='186248338' role='forward' />\n    <member type='way' ref='16264068' role='forward' />\n    <member type='way' ref='98963565' role='forward' />\n    <member type='way' ref='98963559' role='forward' />\n    <member type='way' ref='65066566' role='forward' />\n    <member type='way' ref='98963545' role='forward' />\n    <member type='way' ref='98963563' role='forward' />\n    <member type='way' ref='186248361' role='forward' />\n    <member type='way' ref='366698869' role='forward' />\n    <member type='way' ref='366698867' role='forward' />\n    <member type='way' ref='366698865' role='forward' />\n    <member type='way' ref='366698876' role='forward' />\n    <member type='way' ref='366698863' role='forward' />\n    <member type='way' ref='366698870' role='forward' />\n    <member type='way' ref='366698884' role='forward' />\n    <member type='way' ref='366698866' role='forward' />\n    <member type='way' ref='366698871' role='forward' />\n    <member type='way' ref='366698880' role='forward' />\n    <member type='way' ref='366698868' role='forward' />\n    <member type='way' ref='366698872' role='forward' />\n    <member type='way' ref='30723958' role='forward' />\n    <member type='way' ref='30723959' role='forward' />\n    <member type='way' ref='65066469' role='forward' />\n    <member type='way' ref='574427469' role='forward' />\n    <member type='way' ref='366705823' role='forward' />\n    <member type='way' ref='574427458' role='forward' />\n    <member type='way' ref='366705824' role='forward' />\n    <member type='way' ref='366705821' role='forward' />\n    <member type='way' ref='366705822' role='forward' />\n    <member type='way' ref='366705820' role='forward' />\n    <member type='way' ref='416771323' role='forward' />\n    <member type='way' ref='416771321' role='forward' />\n    <member type='way' ref='416771316' role='forward' />\n    <member type='way' ref='416771309' role='forward' />\n    <member type='way' ref='416771291' role='forward' />\n    <member type='way' ref='416771286' role='forward' />\n    <member type='way' ref='416771275' role='forward' />\n    <member type='way' ref='416771265' role='forward' />\n    <member type='way' ref='157484412' role='forward' />\n    <member type='way' ref='416771253' role='forward' />\n    <member type='way' ref='416771249' role='forward' />\n    <member type='way' ref='184081696' role='forward' />\n    <member type='way' ref='26946068' role='forward' />\n    <member type='way' ref='16264051' role='forward' />\n    <member type='way' ref='184081695' role='forward' />\n    <member type='way' ref='157484371' role='forward' />\n    <member type='way' ref='184081692' role='forward' />\n    <member type='way' ref='42046404' role='forward' />\n    <member type='way' ref='42046405' role='forward' />\n    <member type='way' ref='184081700' role='forward' />\n    <member type='way' ref='119237654' role='forward' />\n    <member type='way' ref='184081698' role='forward' />\n    <member type='way' ref='106801329' role='forward' />\n    <member type='way' ref='106735170' role='forward' />\n    <member type='way' ref='106706502' role='forward' />\n    <member type='way' ref='106706501' role='forward' />\n    <member type='way' ref='172050956' role='forward' />\n    <member type='way' ref='45536127' role='forward' />\n    <member type='way' ref='185142209' role='forward' />\n    <member type='way' ref='170080273' role='forward' />\n    <member type='way' ref='184081618' role='forward' />\n    <member type='way' ref='106447196' role='forward' />\n    <member type='way' ref='106447192' role='forward' />\n    <member type='way' ref='60417032' role='forward' />\n    <member type='way' ref='172211751' role='forward' />\n    <member type='way' ref='172211747' role='forward' />\n    <member type='way' ref='253128479' role='forward' />\n    <member type='way' ref='253128480' role='forward' />\n    <member type='way' ref='188720941' role='forward' />\n    <member type='way' ref='188720945' role='forward' />\n    <member type='way' ref='172211742' role='forward' />\n    <member type='way' ref='66165269' role='forward' />\n    <member type='way' ref='184081598' role='forward' />\n    <member type='way' ref='435094875' role='forward' />\n    <member type='way' ref='435094876' role='forward' />\n    <member type='way' ref='119237663' role='forward' />\n    <member type='way' ref='60417498' role='forward' />\n    <member type='way' ref='184081651' role='forward' />\n    <member type='way' ref='60417598' role='forward' />\n    <member type='way' ref='60417602' role='forward' />\n    <member type='way' ref='435094867' role='forward' />\n    <member type='way' ref='435094866' role='forward' />\n    <member type='way' ref='60615868' role='forward' />\n    <member type='way' ref='60418057' role='forward' />\n    <member type='way' ref='435094862' role='forward' />\n    <member type='way' ref='435094861' role='forward' />\n    <member type='way' ref='60418175' role='forward' />\n    <member type='way' ref='60418170' role='forward' />\n    <member type='way' ref='32870276' role='forward' />\n    <member type='way' ref='32870277' role='forward' />\n    <member type='way' ref='32869992' role='forward' />\n    <member type='way' ref='32869993' role='forward' />\n    <member type='way' ref='10564867' role='forward' />\n    <member type='way' ref='346547493' role='forward' />\n    <member type='way' ref='60418479' role='forward' />\n    <member type='way' ref='60615708' role='forward' />\n    <member type='way' ref='184247605' role='forward' />\n    <member type='way' ref='60615705' role='forward' />\n    <member type='way' ref='60418513' role='forward' />\n    <member type='way' ref='164754168' role='forward' />\n    <member type='way' ref='164754166' role='forward' />\n    <member type='way' ref='344973972' role='forward' />\n    <member type='way' ref='344973977' role='forward' />\n    <member type='way' ref='186263542' role='forward' />\n    <member type='way' ref='186263545' role='forward' />\n    <member type='way' ref='60419218' role='forward' />\n    <member type='way' ref='60419221' role='forward' />\n    <member type='way' ref='60419289' role='forward' />\n    <member type='way' ref='60419288' role='forward' />\n    <member type='way' ref='10561022' role='forward' />\n    <member type='way' ref='184247607' role='forward' />\n    <member type='way' ref='64384119' role='forward' />\n    <member type='way' ref='64384114' role='forward' />\n    <member type='way' ref='184081608' role='forward' />\n    <member type='way' ref='64380406' role='forward' />\n    <member type='way' ref='345758125' role='forward' />\n    <member type='way' ref='344502053' role='forward' />\n    <member type='way' ref='186263556' role='forward' />\n    <member type='way' ref='184081596' role='forward' />\n    <member type='way' ref='64378429' role='forward' />\n    <member type='way' ref='172777678' role='forward' />\n    <member type='way' ref='64378435' role='forward' />\n    <member type='way' ref='184081615' role='forward' />\n    <member type='way' ref='607816010' role='forward' />\n    <member type='way' ref='275350445' role='forward' />\n    <member type='way' ref='275350451' role='forward' />\n    <member type='way' ref='344628973' role='forward' />\n    <member type='way' ref='344628971' role='forward' />\n    <member type='way' ref='607816011' role='forward' />\n    <member type='way' ref='164754806' role='forward' />\n    <member type='way' ref='164754807' role='forward' />\n    <member type='way' ref='30613316' role='forward' />\n    <member type='way' ref='30613315' role='forward' />\n    <member type='way' ref='60920256' role='forward' />\n    <member type='way' ref='232438352' role='forward' />\n    <member type='way' ref='60920112' role='forward' />\n    <member type='way' ref='172477250' role='forward' />\n    <member type='way' ref='172477247' role='forward' />\n    <member type='way' ref='172477245' role='forward' />\n    <member type='way' ref='118632725' role='forward' />\n    <member type='way' ref='118632698' role='forward' />\n    <member type='way' ref='184081619' role='forward' />\n    <member type='way' ref='117809304' role='forward' />\n    <member type='way' ref='363409352' role='forward' />\n    <member type='way' ref='363409353' role='forward' />\n    <member type='way' ref='27211497' role='forward' />\n    <member type='way' ref='184081646' role='forward' />\n    <member type='way' ref='118632735' role='forward' />\n    <member type='way' ref='118632688' role='forward' />\n    <member type='way' ref='64377295' role='forward' />\n    <member type='way' ref='64377302' role='forward' />\n    <member type='way' ref='254292686' role='forward' />\n    <member type='way' ref='118632667' role='forward' />\n    <member type='way' ref='118632730' role='forward' />\n    <member type='way' ref='64377306' role='forward' />\n    <member type='way' ref='64377305' role='forward' />\n    <member type='way' ref='184081600' role='forward' />\n    <member type='way' ref='10475778' role='forward' />\n    <member type='way' ref='435230395' role='forward' />\n    <member type='way' ref='208616318' role='forward' />\n    <member type='way' ref='208616321' role='forward' />\n    <member type='way' ref='118632680' role='forward' />\n    <member type='way' ref='118632652' role='forward' />\n    <member type='way' ref='186263562' role='forward' />\n    <member type='way' ref='186263557' role='forward' />\n    <member type='way' ref='435230399' role='forward' />\n    <member type='way' ref='64377312' role='forward' />\n    <member type='way' ref='64377321' role='forward' />\n    <member type='way' ref='27212402' role='forward' />\n    <member type='way' ref='47391881' role='forward' />\n    <member type='way' ref='47391808' role='forward' />\n    <member type='way' ref='47391840' role='forward' />\n    <member type='way' ref='47391839' role='forward' />\n    <member type='way' ref='47391846' role='forward' />\n    <member type='way' ref='47391847' role='forward' />\n    <member type='way' ref='118632723' role='forward' />\n    <member type='way' ref='160057244' role='forward' />\n    <member type='way' ref='186264548' role='forward' />\n    <member type='way' ref='186264552' role='forward' />\n    <member type='way' ref='47391848' role='forward' />\n    <member type='way' ref='47391809' role='forward' />\n    <member type='way' ref='254292683' role='forward' />\n    <member type='way' ref='47391830' role='forward' />\n    <member type='way' ref='47391833' role='forward' />\n    <member type='way' ref='47391831' role='forward' />\n    <member type='way' ref='47391816' role='forward' />\n    <member type='way' ref='47391826' role='forward' />\n    <member type='way' ref='47391827' role='forward' />\n    <member type='way' ref='184081663' role='forward' />\n    <member type='way' ref='47391814' role='forward' />\n    <member type='way' ref='47391821' role='forward' />\n    <member type='way' ref='47391819' role='forward' />\n    <member type='way' ref='47391812' role='forward' />\n    <member type='way' ref='117809164' role='forward' />\n    <member type='way' ref='27212521' role='forward' />\n    <member type='way' ref='184081647' role='forward' />\n    <member type='way' ref='184081660' role='forward' />\n    <member type='way' ref='117809179' role='forward' />\n    <member type='way' ref='27212952' role='forward' />\n    <member type='way' ref='10475766' role='forward' />\n    <member type='way' ref='27213519' role='forward' />\n    <member type='way' ref='184081624' role='forward' />\n    <member type='way' ref='47391865' role='forward' />\n    <member type='way' ref='47391853' role='forward' />\n    <member type='way' ref='184081604' role='forward' />\n    <member type='way' ref='184081627' role='forward' />\n    <member type='way' ref='184081613' role='forward' />\n    <member type='way' ref='184081666' role='forward' />\n    <member type='way' ref='184081592' role='forward' />\n    <member type='way' ref='27213733' role='forward' />\n    <member type='way' ref='10462153' role='forward' />\n    <member type='way' ref='184081599' role='forward' />\n    <member type='way' ref='48143369' role='forward' />\n    <member type='way' ref='48143363' role='forward' />\n    <member type='way' ref='118632693' role='forward' />\n    <member type='way' ref='118632731' role='forward' />\n    <member type='way' ref='48143366' role='forward' />\n    <member type='way' ref='48143365' role='forward' />\n    <member type='way' ref='436399726' role='forward' />\n    <member type='way' ref='436399722' role='forward' />\n    <member type='way' ref='254292687' role='forward' />\n    <member type='way' ref='436399723' role='forward' />\n    <member type='way' ref='117809163' role='forward' />\n    <member type='way' ref='313637078' role='forward' />\n    <member type='way' ref='111059135' role='forward' />\n    <member type='way' ref='313638857' role='forward' />\n    <member type='way' ref='499352626' role='forward' />\n    <member type='way' ref='460460217' role='forward' />\n    <member type='way' ref='311825342' role='forward' />\n    <member type='way' ref='314043236' role='forward' />\n    <member type='way' ref='27216370' role='forward' />\n    <member type='way' ref='254292685' role='forward' />\n    <member type='way' ref='24024650' role='forward' />\n    <member type='way' ref='157327195' role='forward' />\n    <member type='way' ref='36022471' role='forward' />\n    <member type='way' ref='36022468' role='forward' />\n    <member type='way' ref='184081683' role='forward' />\n    <member type='way' ref='36022189' role='forward' />\n    <member type='way' ref='36022187' role='forward' />\n    <member type='way' ref='26902141' role='forward' />\n    <member type='way' ref='26902140' role='forward' />\n    <member type='way' ref='117809302' role='forward' />\n    <member type='way' ref='36021851' role='forward' />\n    <member type='way' ref='36021850' role='forward' />\n    <member type='way' ref='27216559' role='forward' />\n    <member type='way' ref='164778987' role='forward' />\n    <member type='way' ref='164778990' role='forward' />\n    <member type='way' ref='123741526' role='forward' />\n    <member type='way' ref='184081682' role='forward' />\n    <member type='way' ref='36021095' role='forward' />\n    <member type='way' ref='36021094' role='forward' />\n    <member type='way' ref='36020901' role='forward' />\n    <member type='way' ref='36020871' role='forward' />\n    <member type='way' ref='36020828' role='forward' />\n    <member type='way' ref='36020827' role='forward' />\n    <member type='way' ref='157807075' role='forward' />\n    <member type='way' ref='499352624' role='forward' />\n    <member type='way' ref='157807079' role='forward' />\n    <member type='way' ref='207380308' role='forward' />\n    <member type='way' ref='207380309' role='forward' />\n    <member type='way' ref='157807078' role='forward' />\n    <member type='way' ref='36020208' role='forward' />\n    <member type='way' ref='164553620' role='forward' />\n    <member type='way' ref='123741531' role='forward' />\n    <member type='way' ref='25463673' role='forward' />\n    <member type='way' ref='25463674' role='forward' />\n    <member type='way' ref='93539065' role='forward' />\n    <member type='way' ref='93539055' role='forward' />\n    <member type='way' ref='93539067' role='forward' />\n    <member type='way' ref='93539056' role='forward' />\n    <member type='way' ref='394385112' role='forward' />\n    <member type='way' ref='394385113' role='forward' />\n    <member type='way' ref='394385108' role='forward' />\n    <member type='way' ref='394385114' role='forward' />\n    <member type='way' ref='28437191' role='forward' />\n    <member type='way' ref='28437190' role='forward' />\n    <member type='way' ref='23807653' role='forward' />\n    <member type='way' ref='23820853' role='forward' />\n    <member type='way' ref='23807652' role='forward' />\n    <member type='way' ref='52305998' role='forward' />\n    <member type='way' ref='52305987' role='forward' />\n    <member type='way' ref='52306305' role='forward' />\n    <member type='way' ref='52306303' role='forward' />\n    <member type='way' ref='28399276' role='forward' />\n    <member type='way' ref='394449801' role='forward' />\n    <member type='way' ref='617259143' role='forward' />\n    <member type='way' ref='395394473' role='forward' />\n    <member type='way' ref='395394478' role='forward' />\n    <member type='way' ref='394449803' role='forward' />\n    <member type='way' ref='394449802' role='forward' />\n    <member type='way' ref='28399282' role='forward' />\n    <member type='way' ref='619042896' role='forward' />\n    <member type='way' ref='619042901' role='forward' />\n    <member type='way' ref='498017755' role='forward' />\n    <member type='way' ref='619042918' role='forward' />\n    <member type='way' ref='619042919' role='forward' />\n    <member type='way' ref='619042924' role='forward' />\n    <member type='way' ref='619042925' role='forward' />\n    <member type='way' ref='544296220' role='forward' />\n    <member type='way' ref='544296219' role='forward' />\n    <member type='way' ref='544296218' role='forward' />\n    <member type='way' ref='28414031' role='forward' />\n    <member type='way' ref='28414030' role='forward' />\n    <member type='way' ref='157607513' role='forward' />\n    <member type='way' ref='508479350' role='forward' />\n    <member type='way' ref='508479355' role='forward' />\n    <member type='way' ref='157607510' role='forward' />\n    <member type='way' ref='28414350' role='forward' />\n    <member type='way' ref='28414349' role='forward' />\n    <member type='way' ref='28414367' role='forward' />\n    <member type='way' ref='28414370' role='forward' />\n    <member type='way' ref='28414588' role='forward' />\n    <member type='way' ref='28414589' role='forward' />\n    <member type='way' ref='517603522' role='forward' />\n    <member type='way' ref='360948375' role='forward' />\n    <member type='way' ref='509721010' role='forward' />\n    <member type='way' ref='509747240' role='forward' />\n    <member type='way' ref='28485320' role='forward' />\n    <member type='way' ref='395310468' role='forward' />\n    <member type='way' ref='395354964' role='forward' />\n    <member type='way' ref='479773091' role='forward' />\n    <member type='way' ref='479773092' role='forward' />\n    <member type='way' ref='395354963' role='forward' />\n    <member type='way' ref='28485321' role='forward' />\n    <member type='way' ref='509747302' role='forward' />\n    <member type='way' ref='509747310' role='forward' />\n    <member type='way' ref='509747317' role='forward' />\n    <member type='way' ref='23722215' role='forward' />\n    <member type='way' ref='509747346' role='forward' />\n    <member type='way' ref='509747347' role='forward' />\n    <member type='way' ref='27628575' role='forward' />\n    <member type='way' ref='27628577' role='forward' />\n    <member type='way' ref='617335302' role='forward' />\n    <member type='way' ref='361615811' role='forward' />\n    <member type='way' ref='509747406' role='forward' />\n    <member type='way' ref='617235581' role='forward' />\n    <member type='way' ref='509747399' role='forward' />\n    <member type='way' ref='392017177' role='forward' />\n    <member type='way' ref='394368089' role='forward' />\n    <member type='way' ref='394368088' role='forward' />\n    <member type='way' ref='609836293' role='forward' />\n    <member type='way' ref='609836292' role='forward' />\n    <member type='way' ref='361615812' role='forward' />\n    <member type='way' ref='619117738' role='forward' />\n    <member type='way' ref='396010341' role='forward' />\n    <member type='way' ref='442943039' role='forward' />\n    <member type='way' ref='442943035' role='forward' />\n    <member type='way' ref='396010338' role='forward' />\n    <member type='way' ref='395446377' role='forward' />\n    <member type='way' ref='395446378' role='forward' />\n    <member type='way' ref='395436918' role='forward' />\n    <member type='way' ref='395436903' role='forward' />\n    <member type='way' ref='35837496' role='forward' />\n    <member type='way' ref='35837497' role='forward' />\n    <member type='way' ref='42380379' role='forward' />\n    <member type='way' ref='42380378' role='forward' />\n    <member type='way' ref='127284803' role='forward' />\n    <member type='way' ref='392017178' role='forward' />\n    <member type='way' ref='127284804' role='forward' />\n    <member type='way' ref='47885091' role='forward' />\n    <member type='way' ref='38151150' role='forward' />\n    <member type='way' ref='514310894' role='forward' />\n    <member type='way' ref='514284027' role='forward' />\n    <member type='way' ref='514284028' role='forward' />\n    <member type='way' ref='124952142' role='forward' />\n    <member type='way' ref='514284031' role='forward' />\n    <member type='way' ref='23727601' role='forward' />\n    <member type='way' ref='583791565' role='forward' />\n    <member type='way' ref='583791564' role='forward' />\n    <member type='way' ref='425562120' role='forward' />\n    <member type='way' ref='425562121' role='forward' />\n    <member type='way' ref='256787711' role='forward' />\n    <member type='way' ref='514264487' role='forward' />\n    <member type='way' ref='514264488' role='forward' />\n    <member type='way' ref='24054676' role='forward' />\n    <member type='way' ref='216932380' role='forward' />\n    <member type='way' ref='256787717' role='forward' />\n    <member type='way' ref='510235871' role='forward' />\n    <member type='way' ref='435668946' role='forward' />\n    <member type='way' ref='435668945' role='forward' />\n    <member type='way' ref='256787725' role='forward' />\n    <member type='way' ref='436570209' role='forward' />\n    <member type='way' ref='396936294' role='forward' />\n    <member type='way' ref='396936299' role='forward' />\n    <member type='way' ref='396939369' role='forward' />\n    <member type='way' ref='392505477' role='forward' />\n    <member type='way' ref='256787724' role='forward' />\n    <member type='way' ref='392505474' role='forward' />\n    <member type='way' ref='396949693' role='forward' />\n    <member type='way' ref='396949694' role='forward' />\n    <member type='way' ref='392505469' role='forward' />\n    <member type='way' ref='256787710' role='forward' />\n    <member type='way' ref='395575865' role='forward' />\n    <member type='way' ref='395575864' role='forward' />\n    <member type='way' ref='395575863' role='forward' />\n    <member type='way' ref='216881166' role='forward' />\n    <member type='way' ref='216881160' role='forward' />\n    <member type='way' ref='216881163' role='forward' />\n    <member type='way' ref='216881162' role='forward' />\n    <member type='way' ref='216881164' role='forward' />\n    <member type='way' ref='216881165' role='forward' />\n    <member type='way' ref='5071579' role='forward' />\n    <member type='way' ref='4311275' role='forward' />\n    <member type='way' ref='305729174' role='forward' />\n    <member type='way' ref='217582797' role='forward' />\n    <member type='way' ref='513704142' role='forward' />\n    <member type='way' ref='513704144' role='forward' />\n    <member type='way' ref='30593519' role='forward' />\n    <member type='way' ref='124019872' role='forward' />\n    <member type='way' ref='124019859' role='forward' />\n    <member type='way' ref='124019875' role='forward' />\n    <member type='way' ref='124019873' role='forward' />\n    <member type='way' ref='123741436' role='forward' />\n    <member type='way' ref='5071582' role='forward' />\n    <member type='way' ref='254308549' role='forward' />\n    <member type='way' ref='48101183' role='forward' />\n    <member type='way' ref='48101184' role='forward' />\n    <member type='way' ref='8921980' role='forward' />\n    <member type='way' ref='48101169' role='forward' />\n    <member type='way' ref='48191415' role='forward' />\n    <member type='way' ref='123741462' role='forward' />\n    <member type='way' ref='48582237' role='forward' />\n    <member type='way' ref='48037097' role='forward' />\n    <member type='way' ref='48582224' role='forward' />\n    <member type='way' ref='48037052' role='forward' />\n    <member type='way' ref='48037054' role='forward' />\n    <member type='way' ref='48037050' role='forward' />\n    <member type='way' ref='254299123' role='forward' />\n    <member type='way' ref='28326948' role='forward' />\n    <member type='way' ref='33645895' role='forward' />\n    <member type='way' ref='123741441' role='forward' />\n    <member type='way' ref='27183377' role='forward' />\n    <member type='way' ref='27183379' role='forward' />\n    <member type='way' ref='478345678' role='forward' />\n    <member type='way' ref='8915288' role='forward' />\n    <member type='way' ref='27167768' role='forward' />\n    <member type='way' ref='617730082' role='forward' />\n    <member type='way' ref='119237653' role='forward' />\n    <member type='way' ref='25337748' role='forward' />\n    <member type='way' ref='395566672' role='forward' />\n    <member type='way' ref='27167749' role='forward' />\n    <member type='way' ref='119237657' role='forward' />\n    <member type='way' ref='26943484' role='forward' />\n    <member type='way' ref='417087474' role='forward' />\n    <member type='way' ref='417087489' role='forward' />\n    <member type='way' ref='143666210' role='forward' />\n    <member type='way' ref='8919006' role='forward' />\n    <member type='way' ref='34128773' role='forward' />\n    <member type='way' ref='513963921' role='forward' />\n    <member type='way' ref='84654228' role='forward' />\n    <member type='way' ref='397116935' role='forward' />\n    <member type='way' ref='527857425' role='forward' />\n    <member type='way' ref='502795544' role='forward' />\n    <member type='way' ref='502795946' role='forward' />\n    <member type='way' ref='502795943' role='forward' />\n    <member type='way' ref='527857414' role='forward' />\n    <member type='way' ref='527857410' role='forward' />\n    <member type='way' ref='527857407' role='forward' />\n    <member type='way' ref='527857404' role='forward' />\n    <member type='way' ref='527829966' role='forward' />\n    <member type='way' ref='502795938' role='forward' />\n    <member type='way' ref='86303732' role='forward' />\n    <member type='way' ref='527857399' role='forward' />\n    <member type='way' ref='527857397' role='forward' />\n    <member type='way' ref='527829976' role='forward' />\n    <member type='way' ref='527857393' role='forward' />\n    <member type='way' ref='527857392' role='forward' />\n    <member type='way' ref='527857390' role='forward' />\n    <member type='way' ref='527857388' role='forward' />\n    <member type='way' ref='85644931' role='forward' />\n    <member type='way' ref='417091905' role='forward' />\n    <member type='way' ref='23876482' role='forward' />\n    <member type='way' ref='85644942' role='forward' />\n    <member type='way' ref='85562534' role='forward' />\n    <member type='way' ref='85562592' role='forward' />\n    <member type='way' ref='85562514' role='forward' />\n    <member type='way' ref='85562528' role='forward' />\n    <member type='way' ref='85562580' role='forward' />\n    <member type='way' ref='85562588' role='forward' />\n    <member type='way' ref='85562608' role='forward' />\n    <member type='way' ref='85562498' role='forward' />\n    <member type='way' ref='514527343' role='forward' />\n    <member type='way' ref='85562505' role='forward' />\n    <member type='way' ref='85562564' role='forward' />\n    <member type='way' ref='398467248' role='forward' />\n    <member type='way' ref='85562562' role='forward' />\n    <member type='way' ref='8916973' role='forward' />\n    <member type='way' ref='85562492' role='forward' />\n    <member type='way' ref='437384203' role='forward' />\n    <member type='way' ref='437384204' role='forward' />\n    <member type='way' ref='514412999' role='forward' />\n    <member type='way' ref='85562484' role='forward' />\n    <member type='way' ref='516129277' role='forward' />\n    <member type='way' ref='417104573' role='forward' />\n    <member type='way' ref='397028729' role='forward' />\n    <member type='way' ref='167952615' role='forward' />\n    <member type='way' ref='349663310' role='forward' />\n    <member type='way' ref='349663308' role='forward' />\n    <member type='way' ref='167952611' role='forward' />\n    <member type='way' ref='167952610' role='forward' />\n    <member type='way' ref='167952618' role='forward' />\n    <member type='way' ref='619568400' role='forward' />\n    <member type='way' ref='167952620' role='forward' />\n    <member type='way' ref='167952623' role='forward' />\n    <member type='way' ref='26765956' role='forward' />\n    <member type='way' ref='28412298' role='forward' />\n    <member type='way' ref='397086247' role='forward' />\n    <member type='way' ref='90536393' role='forward' />\n    <member type='way' ref='620247385' role='forward' />\n    <member type='way' ref='26968360' role='forward' />\n    <member type='way' ref='595194543' role='forward' />\n    <member type='way' ref='26968355' role='forward' />\n    <member type='way' ref='90536369' role='forward' />\n    <member type='way' ref='255330054' role='forward' />\n    <member type='way' ref='27092098' role='forward' />\n    <member type='way' ref='620169759' role='forward' />\n    <member type='way' ref='620169758' role='forward' />\n    <member type='way' ref='12190847' role='forward' />\n    <member type='way' ref='620247563' role='forward' />\n    <member type='way' ref='129230851' role='forward' />\n    <member type='way' ref='35869653' role='forward' />\n    <member type='way' ref='123741517' role='forward' />\n    <member type='way' ref='30682440' role='forward' />\n    <member type='way' ref='394150406' role='forward' />\n    <member type='way' ref='394150417' role='forward' />\n    <member type='way' ref='394458913' role='forward' />\n    <member type='way' ref='394458912' role='forward' />\n    <member type='way' ref='620169747' role='forward' />\n    <member type='way' ref='394150423' role='forward' />\n    <member type='way' ref='23797526' role='forward' />\n    <member type='way' ref='394457530' role='forward' />\n    <member type='way' ref='394457528' role='forward' />\n    <member type='way' ref='394150392' role='forward' />\n    <member type='way' ref='25025641' role='forward' />\n    <member type='way' ref='394451354' role='forward' />\n    <member type='way' ref='23797525' role='forward' />\n    <member type='way' ref='620200109' role='forward' />\n    <member type='way' ref='25025181' role='forward' />\n    <member type='way' ref='496981807' role='forward' />\n    <member type='way' ref='496981808' role='forward' />\n    <member type='way' ref='24316576' role='forward' />\n    <member type='way' ref='24316577' role='forward' />\n    <member type='way' ref='394446736' role='forward' />\n    <member type='way' ref='394446738' role='forward' />\n    <member type='way' ref='25025833' role='forward' />\n    <member type='way' ref='394442791' role='forward' />\n    <member type='way' ref='25025831' role='forward' />\n    <member type='way' ref='25025827' role='forward' />\n    <member type='way' ref='25025828' role='forward' />\n    <member type='way' ref='186406405' role='forward' />\n    <member type='way' ref='186406404' role='forward' />\n    <member type='way' ref='25025888' role='forward' />\n    <member type='way' ref='25025889' role='forward' />\n    <member type='way' ref='620216019' role='forward' />\n    <member type='way' ref='28949366' role='forward' />\n    <member type='way' ref='48675480' role='forward' />\n    <member type='way' ref='394430890' role='forward' />\n    <member type='way' ref='394430891' role='forward' />\n    <member type='way' ref='48675482' role='forward' />\n    <member type='way' ref='394433889' role='forward' />\n    <member type='way' ref='394430892' role='forward' />\n    <member type='way' ref='284602482' role='forward' />\n    <member type='way' ref='394430225' role='forward' />\n    <member type='way' ref='394430236' role='forward' />\n    <member type='way' ref='284602477' role='forward' />\n    <member type='way' ref='394430240' role='forward' />\n    <member type='way' ref='394427797' role='forward' />\n    <member type='way' ref='394427798' role='forward' />\n    <member type='way' ref='394427803' role='forward' />\n    <member type='way' ref='394427800' role='forward' />\n    <member type='way' ref='394427799' role='forward' />\n    <member type='way' ref='394411111' role='forward' />\n    <member type='way' ref='124952164' role='forward' />\n    <member type='way' ref='394408678' role='forward' />\n    <member type='way' ref='394408681' role='forward' />\n    <member type='way' ref='123741501' role='forward' />\n    <member type='way' ref='620216000' role='forward' />\n    <member type='way' ref='30678054' role='forward' />\n    <member type='way' ref='30678056' role='forward' />\n    <member type='way' ref='620215986' role='forward' />\n    <member type='way' ref='31386140' role='forward' />\n    <member type='way' ref='31386138' role='forward' />\n    <member type='way' ref='30678065' role='forward' />\n    <member type='way' ref='12177115' role='forward' />\n    <member type='way' ref='620215997' role='forward' />\n    <member type='way' ref='186406403' role='forward' />\n    <member type='way' ref='31394369' role='forward' />\n    <member type='way' ref='31124174' role='forward' />\n    <member type='way' ref='117809227' role='forward' />\n    <member type='way' ref='186406406' role='forward' />\n    <member type='way' ref='31395186' role='forward' />\n    <member type='way' ref='507018103' role='forward' />\n    <member type='way' ref='31123954' role='forward' />\n    <member type='way' ref='620247432' role='forward' />\n    <member type='way' ref='186409232' role='forward' />\n    <member type='way' ref='186409234' role='forward' />\n    <member type='way' ref='186409241' role='forward' />\n    <member type='way' ref='48046700' role='forward' />\n    <member type='way' ref='48046701' role='forward' />\n    <member type='way' ref='159938680' role='forward' />\n    <member type='way' ref='159938686' role='forward' />\n    <member type='way' ref='620247406' role='forward' />\n    <member type='way' ref='159938677' role='forward' />\n    <member type='way' ref='620247361' role='forward' />\n    <member type='way' ref='159938681' role='forward' />\n    <member type='way' ref='159938685' role='forward' />\n    <member type='way' ref='91360729' role='forward' />\n    <member type='way' ref='620247419' role='forward' />\n    <member type='way' ref='91360724' role='forward' />\n    <member type='way' ref='48046697' role='forward' />\n    <member type='way' ref='48046694' role='forward' />\n    <member type='way' ref='620247418' role='forward' />\n    <member type='way' ref='31450981' role='forward' />\n    <member type='way' ref='31450980' role='forward' />\n    <member type='way' ref='159938771' role='forward' />\n    <member type='way' ref='31450992' role='forward' />\n    <member type='way' ref='119237658' role='forward' />\n    <member type='way' ref='620247415' role='forward' />\n    <member type='way' ref='620247508' role='forward' />\n    <member type='way' ref='620247501' role='forward' />\n    <member type='way' ref='123741496' role='forward' />\n    <member type='way' ref='620247412' role='forward' />\n    <member type='way' ref='186409236' role='forward' />\n    <member type='way' ref='186409237' role='forward' />\n    <member type='way' ref='31763423' role='forward' />\n    <member type='way' ref='31763422' role='forward' />\n    <member type='way' ref='620247414' role='forward' />\n    <member type='way' ref='123741508' role='forward' />\n    <member type='way' ref='620247523' role='forward' />\n    <member type='way' ref='620247410' role='forward' />\n    <member type='way' ref='7707846' role='forward' />\n    <member type='way' ref='31781380' role='forward' />\n    <member type='way' ref='48046707' role='forward' />\n    <member type='way' ref='620247519' role='forward' />\n    <member type='way' ref='48046711' role='forward' />\n    <member type='way' ref='48046712' role='forward' />\n    <member type='way' ref='48046705' role='forward' />\n    <member type='way' ref='48046719' role='forward' />\n    <member type='way' ref='48046715' role='forward' />\n    <member type='way' ref='48046717' role='forward' />\n    <member type='way' ref='48046703' role='forward' />\n    <member type='way' ref='48046721' role='forward' />\n    <member type='way' ref='620247528' role='forward' />\n    <member type='way' ref='7707838' role='forward' />\n    <member type='way' ref='48046749' role='forward' />\n    <member type='way' ref='48046739' role='forward' />\n    <member type='way' ref='124952167' role='forward' />\n    <member type='way' ref='48046744' role='forward' />\n    <member type='way' ref='48046746' role='forward' />\n    <member type='way' ref='48046741' role='forward' />\n    <member type='way' ref='48046747' role='forward' />\n    <member type='way' ref='48046724' role='forward' />\n    <member type='way' ref='48046751' role='forward' />\n    <member type='way' ref='48046756' role='forward' />\n    <member type='way' ref='48046752' role='forward' />\n    <member type='way' ref='7707859' role='forward' />\n    <member type='way' ref='186410487' role='forward' />\n    <member type='way' ref='186410489' role='forward' />\n    <member type='way' ref='48046757' role='forward' />\n    <member type='way' ref='48046725' role='forward' />\n    <member type='way' ref='48046731' role='forward' />\n    <member type='way' ref='48046730' role='forward' />\n    <member type='way' ref='48046734' role='forward' />\n    <member type='way' ref='48046727' role='forward' />\n    <member type='way' ref='48046736' role='forward' />\n    <member type='way' ref='48046735' role='forward' />\n    <member type='way' ref='31125662' role='forward' />\n    <member type='way' ref='31125665' role='forward' />\n    <member type='way' ref='31125322' role='forward' />\n    <member type='way' ref='31125323' role='forward' />\n    <member type='way' ref='31125319' role='forward' />\n    <member type='way' ref='31125321' role='forward' />\n    <member type='way' ref='31125308' role='forward' />\n    <member type='way' ref='31125309' role='forward' />\n    <member type='way' ref='31125313' role='forward' />\n    <member type='way' ref='31125314' role='forward' />\n    <member type='way' ref='28949361' role='forward' />\n    <member type='way' ref='48046766' role='forward' />\n    <member type='way' ref='48046762' role='forward' />\n    <member type='way' ref='48046763' role='forward' />\n    <member type='way' ref='48046760' role='forward' />\n    <member type='way' ref='48046777' role='forward' />\n    <member type='way' ref='25287957' role='forward' />\n    <member type='way' ref='48046775' role='forward' />\n    <member type='way' ref='48046768' role='forward' />\n    <member type='way' ref='48046779' role='forward' />\n    <member type='way' ref='48046778' role='forward' />\n    <member type='way' ref='35089534' role='forward' />\n    <member type='way' ref='172725811' role='forward' />\n    <member type='way' ref='172725822' role='forward' />\n    <member type='way' ref='186409239' role='forward' />\n    <member type='way' ref='10318902' role='forward' />\n    <member type='way' ref='164755808' role='forward' />\n    <member type='way' ref='164755810' role='forward' />\n    <member type='way' ref='186409233' role='forward' />\n    <member type='way' ref='25287746' role='forward' />\n    <member type='way' ref='35089442' role='forward' />\n    <member type='way' ref='32867940' role='forward' />\n    <member type='way' ref='32867943' role='forward' />\n    <member type='way' ref='117809276' role='forward' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='direction' v='north' />\n    <tag k='is_in:state' v='CA' />\n    <tag k='name' v='US 101 (CA northbound, south of Hopland)' />\n    <tag k='network' v='US:US' />\n    <tag k='ref' v='101' />\n    <tag k='route' v='road' />\n    <tag k='symbol' v='https://upload.wikimedia.org/wikipedia/commons/5/5b/US_101_(CA).svg' />\n    <tag k='type' v='route' />\n    <tag k='wikidata' v='Q410892' />\n    <tag k='wikipedia' v='en:U.S. Route 101' />\n  </relation>\n  <relation id='443197' timestamp='2018-09-19T19:19:51Z' uid='8724499' user='5XWarrior' version='10' changeset='62741327'>\n    <member type='relation' ref='108619' role='north' />\n    <member type='relation' ref='71162' role='south' />\n    <member type='relation' ref='2306823' role='' />\n    <member type='relation' ref='2306824' role='' />\n    <member type='relation' ref='2306831' role='' />\n    <tag k='is_in:state' v='CA;OR;WA' />\n    <tag k='name' v='US 101 (super)' />\n    <tag k='network' v='US:US' />\n    <tag k='ref' v='101' />\n    <tag k='route' v='road' />\n    <tag k='symbol' v='https://upload.wikimedia.org/wikipedia/commons/7/79/US_101.svg' />\n    <tag k='type' v='route' />\n    <tag k='wikidata' v='Q410892' />\n    <tag k='wikipedia' v='en:U.S. Route 101' />\n  </relation>\n  <relation id='1539483' timestamp='2018-10-15T22:03:40Z' uid='2237750' user='chachafish' version='21' changeset='63557245'>\n    <member type='way' ref='26297408' role='' />\n    <member type='way' ref='25821947' role='' />\n    <member type='way' ref='133735128' role='forward' />\n    <member type='way' ref='513595718' role='forward' />\n    <member type='way' ref='513595719' role='forward' />\n    <member type='way' ref='8920632' role='forward' />\n    <member type='way' ref='458779334' role='forward' />\n    <member type='way' ref='27374826' role='forward' />\n    <member type='way' ref='254299376' role='' />\n    <member type='way' ref='224373746' role='' />\n    <member type='way' ref='221434102' role='' />\n    <member type='way' ref='620728093' role='' />\n    <member type='way' ref='221434099' role='forward' />\n    <member type='way' ref='221434100' role='forward' />\n    <member type='way' ref='221434104' role='forward' />\n    <member type='way' ref='84823308' role='' />\n    <member type='way' ref='133755923' role='' />\n    <member type='way' ref='442572410' role='' />\n    <member type='way' ref='442572411' role='' />\n    <member type='way' ref='442572412' role='' />\n    <member type='way' ref='442572413' role='' />\n    <member type='way' ref='143617587' role='' />\n    <member type='way' ref='619568404' role='' />\n    <member type='way' ref='525564024' role='' />\n    <member type='way' ref='143617589' role='' />\n    <member type='way' ref='111550911' role='' />\n    <tag k='cycle_network' v='US:CA:SF' />\n    <tag k='network' v='lcn' />\n    <tag k='ref' v='36' />\n    <tag k='route' v='bicycle' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='1797343' timestamp='2018-10-16T00:41:01Z' uid='2237750' user='chachafish' version='3' changeset='63559070'>\n    <member type='way' ref='634444999' role='' />\n    <member type='way' ref='634412037' role='' />\n    <member type='way' ref='8919954' role='' />\n    <member type='way' ref='8918238' role='' />\n    <tag k='cycle_network' v='US:CA:SF' />\n    <tag k='network' v='lcn' />\n    <tag k='ref' v='123' />\n    <tag k='route' v='bicycle' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='1797344' timestamp='2018-10-15T22:03:41Z' uid='2237750' user='chachafish' version='35' changeset='63557245'>\n    <member type='way' ref='396965551' role='' />\n    <member type='way' ref='516940742' role='' />\n    <member type='way' ref='397250671' role='' />\n    <member type='way' ref='8916191' role='' />\n    <member type='way' ref='489670144' role='' />\n    <member type='way' ref='27239381' role='' />\n    <member type='way' ref='623399444' role='' />\n    <member type='way' ref='544431983' role='' />\n    <member type='way' ref='544431982' role='' />\n    <member type='way' ref='396965550' role='' />\n    <member type='way' ref='396965549' role='' />\n    <member type='way' ref='396965553' role='' />\n    <member type='way' ref='8920802' role='' />\n    <member type='way' ref='417392949' role='' />\n    <member type='way' ref='8916792' role='' />\n    <member type='way' ref='513699413' role='' />\n    <member type='way' ref='417392962' role='' />\n    <member type='way' ref='417392953' role='' />\n    <member type='way' ref='417392970' role='' />\n    <member type='way' ref='417392966' role='' />\n    <member type='way' ref='28518571' role='' />\n    <member type='way' ref='84823308' role='forward' />\n    <member type='way' ref='157812260' role='backward' />\n    <member type='way' ref='428511530' role='forward' />\n    <member type='way' ref='539195910' role='forward' />\n    <member type='way' ref='539195908' role='forward' />\n    <member type='way' ref='620728096' role='forward' />\n    <member type='way' ref='488350036' role='forward' />\n    <member type='way' ref='493107463' role='forward' />\n    <member type='way' ref='417087280' role='forward' />\n    <member type='way' ref='417087295' role='forward' />\n    <member type='way' ref='88572922' role='forward' />\n    <member type='way' ref='493107464' role='forward' />\n    <member type='way' ref='417087285' role='forward' />\n    <member type='way' ref='415520894' role='forward' />\n    <member type='way' ref='493107465' role='forward' />\n    <member type='way' ref='417087290' role='forward' />\n    <member type='way' ref='417269458' role='forward' />\n    <member type='way' ref='221434104' role='forward' />\n    <member type='way' ref='516061750' role='forward' />\n    <member type='way' ref='27167281' role='forward' />\n    <member type='way' ref='516061744' role='forward' />\n    <member type='way' ref='573287808' role='forward' />\n    <member type='way' ref='394558807' role='forward' />\n    <member type='way' ref='525662693' role='forward' />\n    <member type='way' ref='419633752' role='forward' />\n    <member type='way' ref='493107462' role='forward' />\n    <member type='way' ref='620200099' role='forward' />\n    <member type='way' ref='111050389' role='forward' />\n    <member type='way' ref='417087300' role='forward' />\n    <member type='way' ref='417087312' role='forward' />\n    <member type='way' ref='397119705' role='forward' />\n    <member type='way' ref='8921141' role='forward' />\n    <member type='way' ref='417087306' role='forward' />\n    <member type='way' ref='415517886' role='forward' />\n    <member type='way' ref='415491501' role='forward' />\n    <tag k='cycle_network' v='US:CA:SF' />\n    <tag k='network' v='lcn' />\n    <tag k='ref' v='23' />\n    <tag k='route' v='bicycle' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='1889266' timestamp='2017-12-01T17:54:42Z' uid='1835512' user='nereocystis' version='14' changeset='54244365'>\n    <member type='way' ref='26943758' role='street' />\n    <member type='way' ref='417400705' role='street' />\n    <member type='way' ref='417400701' role='street' />\n    <member type='way' ref='417400697' role='street' />\n    <member type='way' ref='417400703' role='street' />\n    <member type='way' ref='247966218' role='street' />\n    <member type='way' ref='417400699' role='street' />\n    <member type='way' ref='141688972' role='street' />\n    <member type='way' ref='397127923' role='street' />\n    <member type='way' ref='397127924' role='street' />\n    <member type='way' ref='396951398' role='street' />\n    <member type='way' ref='396951397' role='street' />\n    <member type='way' ref='396952210' role='street' />\n    <member type='way' ref='397097609' role='street' />\n    <member type='way' ref='533779280' role='street' />\n    <member type='way' ref='255330036' role='street' />\n    <member type='way' ref='255330037' role='street' />\n    <member type='way' ref='113094318' role='house' />\n    <member type='way' ref='346093777' role='house' />\n    <member type='node' ref='5258103408' role='house' />\n    <tag k='name' v='Brannan Street' />\n    <tag k='type' v='associatedStreet' />\n  </relation>\n  <relation id='2320405' timestamp='2018-08-18T23:51:39Z' uid='119881' user='clay_c' version='10' changeset='61782308'>\n    <member type='way' ref='120813417' role='forward' />\n    <member type='way' ref='202485364' role='forward' />\n    <member type='way' ref='617730080' role='forward' />\n    <member type='way' ref='510583844' role='forward' />\n    <member type='way' ref='31129732' role='forward' />\n    <member type='way' ref='254299122' role='forward' />\n    <member type='way' ref='154966358' role='forward' />\n    <member type='way' ref='617895234' role='forward' />\n    <member type='way' ref='31129765' role='forward' />\n    <member type='way' ref='617730081' role='forward' />\n    <member type='way' ref='123741465' role='forward' />\n    <member type='way' ref='123741454' role='forward' />\n    <member type='way' ref='394443188' role='forward' />\n    <member type='way' ref='394443190' role='forward' />\n    <member type='way' ref='394443187' role='forward' />\n    <member type='way' ref='394443189' role='forward' />\n    <member type='way' ref='513706009' role='forward' />\n    <member type='way' ref='31129771' role='forward' />\n    <member type='way' ref='123741452' role='forward' />\n    <tag k='NHS' v='STRAHNET' />\n    <tag k='note' v='STRAHNET route: signed as I-80 but not officially an Interstate' />\n    <tag k='route' v='road' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='2716239' timestamp='2018-08-26T07:53:19Z' uid='8107451' user='njtbusfan' version='134' changeset='62000684'>\n    <member type='node' ref='5845168206' role='stop' />\n    <member type='node' ref='5845183335' role='stop' />\n    <member type='way' ref='520155508' role='' />\n    <member type='way' ref='596920658' role='' />\n    <member type='way' ref='120710556' role='' />\n    <member type='way' ref='417537897' role='' />\n    <member type='way' ref='417537730' role='' />\n    <member type='way' ref='481593697' role='' />\n    <member type='way' ref='202015019' role='' />\n    <member type='way' ref='481593696' role='' />\n    <member type='way' ref='202034972' role='' />\n    <member type='way' ref='517848116' role='' />\n    <member type='way' ref='417537746' role='' />\n    <member type='way' ref='202034974' role='' />\n    <member type='way' ref='574281083' role='' />\n    <member type='way' ref='575457852' role='' />\n    <member type='way' ref='575457853' role='' />\n    <member type='way' ref='575489895' role='' />\n    <member type='way' ref='575455248' role='' />\n    <member type='way' ref='575455247' role='' />\n    <member type='way' ref='575455837' role='' />\n    <member type='way' ref='575456948' role='' />\n    <member type='way' ref='575456949' role='' />\n    <member type='way' ref='416087653' role='' />\n    <member type='way' ref='575489371' role='' />\n    <member type='way' ref='416087652' role='' />\n    <member type='way' ref='416087650' role='' />\n    <member type='way' ref='416087651' role='' />\n    <member type='way' ref='416087654' role='' />\n    <member type='way' ref='416061125' role='' />\n    <member type='way' ref='416061116' role='' />\n    <member type='way' ref='416061127' role='' />\n    <member type='way' ref='416061132' role='' />\n    <member type='way' ref='416083248' role='' />\n    <member type='way' ref='416083240' role='' />\n    <member type='way' ref='416083233' role='' />\n    <member type='way' ref='24206455' role='' />\n    <member type='way' ref='24206472' role='' />\n    <member type='way' ref='416122255' role='' />\n    <member type='way' ref='416122261' role='' />\n    <member type='way' ref='416122259' role='' />\n    <member type='way' ref='416122260' role='' />\n    <member type='way' ref='415786602' role='' />\n    <member type='way' ref='196750855' role='' />\n    <member type='way' ref='24430320' role='' />\n    <member type='way' ref='196750851' role='' />\n    <member type='way' ref='196750852' role='' />\n    <member type='way' ref='24208138' role='' />\n    <member type='way' ref='167541519' role='' />\n    <member type='way' ref='417703553' role='' />\n    <member type='way' ref='179235226' role='' />\n    <member type='way' ref='415797597' role='' />\n    <member type='way' ref='7866640' role='' />\n    <member type='way' ref='415787681' role='' />\n    <member type='way' ref='517644694' role='' />\n    <member type='way' ref='415787780' role='' />\n    <member type='way' ref='415788055' role='' />\n    <member type='way' ref='415788052' role='' />\n    <member type='way' ref='415788373' role='' />\n    <member type='way' ref='415788370' role='' />\n    <member type='way' ref='415788551' role='' />\n    <member type='way' ref='415788550' role='' />\n    <member type='way' ref='415788660' role='' />\n    <member type='way' ref='415788659' role='' />\n    <member type='way' ref='574384855' role='' />\n    <member type='way' ref='415788688' role='' />\n    <member type='way' ref='415788687' role='' />\n    <member type='way' ref='415788740' role='' />\n    <member type='way' ref='415788741' role='' />\n    <member type='way' ref='416138243' role='' />\n    <member type='way' ref='416138242' role='' />\n    <member type='way' ref='416139196' role='' />\n    <member type='way' ref='416139194' role='' />\n    <member type='way' ref='167541521' role='' />\n    <member type='way' ref='416139988' role='' />\n    <member type='way' ref='416139989' role='' />\n    <member type='way' ref='416130764' role='' />\n    <member type='way' ref='416130763' role='' />\n    <member type='way' ref='202007364' role='' />\n    <member type='way' ref='416135850' role='' />\n    <member type='way' ref='575456997' role='' />\n    <member type='way' ref='416143868' role='' />\n    <member type='way' ref='575456995' role='' />\n    <member type='way' ref='575456996' role='' />\n    <member type='way' ref='416143872' role='' />\n    <member type='way' ref='202007365' role='' />\n    <member type='way' ref='24206457' role='' />\n    <member type='way' ref='416146107' role='' />\n    <member type='way' ref='575448035' role='' />\n    <member type='way' ref='416146124' role='' />\n    <member type='way' ref='416146113' role='' />\n    <member type='way' ref='416146102' role='' />\n    <member type='way' ref='575449149' role='' />\n    <member type='way' ref='416146129' role='' />\n    <member type='way' ref='575449156' role='' />\n    <member type='way' ref='575449164' role='' />\n    <member type='way' ref='416146119' role='' />\n    <member type='way' ref='417705210' role='' />\n    <member type='way' ref='195604795' role='' />\n    <member type='way' ref='417705204' role='' />\n    <member type='way' ref='417705221' role='' />\n    <member type='way' ref='417712247' role='' />\n    <member type='way' ref='415872344' role='' />\n    <member type='way' ref='415872358' role='' />\n    <member type='way' ref='415803797' role='' />\n    <member type='way' ref='415803790' role='' />\n    <member type='way' ref='139944613' role='' />\n    <member type='way' ref='415803796' role='' />\n    <member type='way' ref='415803791' role='' />\n    <member type='way' ref='515886870' role='' />\n    <member type='way' ref='42639699' role='' />\n    <member type='way' ref='195433880' role='' />\n    <member type='way' ref='415803788' role='' />\n    <member type='way' ref='415803776' role='' />\n    <member type='way' ref='415803783' role='' />\n    <member type='way' ref='22718354' role='' />\n    <member type='way' ref='22718353' role='' />\n    <member type='way' ref='415803782' role='' />\n    <member type='way' ref='415803798' role='' />\n    <member type='way' ref='415803784' role='' />\n    <member type='way' ref='415803792' role='' />\n    <member type='way' ref='415803786' role='' />\n    <member type='way' ref='415803785' role='' />\n    <member type='way' ref='203164334' role='' />\n    <member type='way' ref='415870573' role='' />\n    <member type='way' ref='415870539' role='' />\n    <member type='way' ref='195433881' role='' />\n    <member type='way' ref='449859959' role='' />\n    <member type='way' ref='415870540' role='' />\n    <member type='way' ref='415870551' role='' />\n    <member type='way' ref='415870561' role='' />\n    <member type='way' ref='203193434' role='' />\n    <member type='way' ref='203193435' role='' />\n    <member type='way' ref='415870538' role='' />\n    <member type='way' ref='203394105' role='' />\n    <member type='way' ref='202317719' role='' />\n    <member type='way' ref='415870603' role='' />\n    <member type='way' ref='415870601' role='' />\n    <member type='way' ref='203394107' role='' />\n    <member type='way' ref='458138566' role='' />\n    <member type='way' ref='517601167' role='' />\n    <member type='way' ref='458138567' role='' />\n    <member type='way' ref='415870585' role='' />\n    <member type='way' ref='415870615' role='' />\n    <member type='way' ref='415870598' role='' />\n    <member type='way' ref='195433884' role='' />\n    <member type='way' ref='415870606' role='' />\n    <member type='way' ref='415870608' role='' />\n    <member type='way' ref='415870612' role='' />\n    <member type='way' ref='415870589' role='' />\n    <member type='way' ref='196599908' role='' />\n    <member type='way' ref='415876829' role='' />\n    <member type='way' ref='22372597' role='' />\n    <member type='way' ref='415876826' role='' />\n    <member type='way' ref='415876827' role='' />\n    <member type='way' ref='25738782' role='' />\n    <member type='way' ref='292218768' role='' />\n    <member type='way' ref='26379240' role='' />\n    <member type='way' ref='288515137' role='' />\n    <member type='way' ref='415876825' role='' />\n    <member type='way' ref='415876816' role='' />\n    <member type='way' ref='415876810' role='' />\n    <member type='way' ref='415876820' role='' />\n    <member type='way' ref='196599906' role='' />\n    <member type='way' ref='202485373' role='' />\n    <member type='way' ref='415876812' role='' />\n    <member type='way' ref='415876823' role='' />\n    <member type='way' ref='415876821' role='' />\n    <member type='way' ref='195430728' role='' />\n    <member type='way' ref='415876815' role='' />\n    <member type='way' ref='415876819' role='' />\n    <member type='way' ref='415876814' role='' />\n    <member type='way' ref='415876818' role='' />\n    <member type='way' ref='415876817' role='' />\n    <member type='way' ref='415876822' role='' />\n    <member type='way' ref='415876808' role='' />\n    <member type='way' ref='415876813' role='' />\n    <member type='way' ref='201989375' role='' />\n    <member type='way' ref='415876806' role='' />\n    <member type='way' ref='517593875' role='' />\n    <member type='way' ref='34815518' role='' />\n    <member type='way' ref='6351923' role='' />\n    <member type='way' ref='165864142' role='' />\n    <member type='way' ref='416111296' role='' />\n    <member type='way' ref='416102054' role='' />\n    <member type='way' ref='202317703' role='' />\n    <member type='way' ref='416102058' role='' />\n    <member type='way' ref='202317700' role='' />\n    <member type='way' ref='516589462' role='' />\n    <member type='way' ref='516589461' role='' />\n    <member type='way' ref='416102057' role='' />\n    <member type='way' ref='416105053' role='' />\n    <member type='way' ref='416105051' role='' />\n    <member type='way' ref='202317717' role='' />\n    <member type='way' ref='416105082' role='' />\n    <member type='way' ref='416105077' role='' />\n    <member type='way' ref='416105073' role='' />\n    <member type='way' ref='28436962' role='' />\n    <member type='way' ref='481521360' role='' />\n    <member type='way' ref='481521361' role='' />\n    <member type='way' ref='403341428' role='' />\n    <member type='way' ref='328902340' role='' />\n    <member type='way' ref='86357783' role='' />\n    <member type='way' ref='86356713' role='' />\n    <member type='way' ref='416105064' role='' />\n    <member type='way' ref='393902212' role='' />\n    <member type='way' ref='416164986' role='' />\n    <member type='way' ref='393902210' role='' />\n    <member type='way' ref='196739967' role='' />\n    <member type='way' ref='403341429' role='' />\n    <member type='way' ref='416164983' role='' />\n    <member type='way' ref='196739963' role='' />\n    <member type='way' ref='416164980' role='' />\n    <member type='way' ref='416164947' role='' />\n    <member type='way' ref='416164956' role='' />\n    <member type='way' ref='416164985' role='' />\n    <member type='way' ref='416164976' role='' />\n    <member type='way' ref='416164977' role='' />\n    <member type='way' ref='416164965' role='' />\n    <member type='way' ref='416164946' role='' />\n    <member type='way' ref='416164959' role='' />\n    <member type='way' ref='416164961' role='' />\n    <member type='way' ref='416164958' role='' />\n    <member type='way' ref='416164982' role='' />\n    <member type='way' ref='416164991' role='' />\n    <member type='way' ref='417724567' role='' />\n    <member type='way' ref='417724575' role='' />\n    <member type='way' ref='417724561' role='' />\n    <member type='way' ref='416164969' role='' />\n    <member type='way' ref='416164975' role='' />\n    <member type='way' ref='416164971' role='' />\n    <member type='way' ref='416164973' role='' />\n    <member type='way' ref='107014160' role='' />\n    <member type='way' ref='416164945' role='' />\n    <member type='way' ref='416164972' role='' />\n    <member type='way' ref='416164979' role='' />\n    <member type='way' ref='416164970' role='' />\n    <member type='way' ref='416164962' role='' />\n    <member type='way' ref='416164966' role='' />\n    <member type='way' ref='202459224' role='' />\n    <member type='way' ref='416164960' role='' />\n    <member type='way' ref='416164974' role='' />\n    <member type='way' ref='416164953' role='' />\n    <member type='way' ref='416164981' role='' />\n    <member type='way' ref='416164964' role='' />\n    <member type='way' ref='416164967' role='' />\n    <member type='way' ref='416164984' role='' />\n    <member type='way' ref='416164963' role='' />\n    <member type='way' ref='202459223' role='' />\n    <member type='way' ref='416164950' role='' />\n    <member type='way' ref='416164989' role='' />\n    <member type='way' ref='416164990' role='' />\n    <member type='way' ref='416164968' role='' />\n    <member type='way' ref='518495991' role='' />\n    <member type='way' ref='416164987' role='' />\n    <member type='way' ref='518495990' role='' />\n    <member type='way' ref='416154094' role='' />\n    <member type='way' ref='416153764' role='' />\n    <member type='way' ref='416153765' role='' />\n    <member type='way' ref='416153415' role='' />\n    <member type='way' ref='416153414' role='' />\n    <member type='way' ref='416153328' role='' />\n    <member type='way' ref='416153329' role='' />\n    <member type='way' ref='417922201' role='' />\n    <member type='way' ref='417922205' role='' />\n    <member type='way' ref='416153308' role='' />\n    <member type='way' ref='416153307' role='' />\n    <member type='way' ref='416153133' role='' />\n    <member type='way' ref='416151979' role='' />\n    <member type='way' ref='416145334' role='' />\n    <member type='way' ref='416151873' role='' />\n    <member type='way' ref='416151872' role='' />\n    <member type='way' ref='416151641' role='' />\n    <member type='way' ref='416151647' role='' />\n    <member type='way' ref='416145331' role='' />\n    <member type='way' ref='416150865' role='' />\n    <member type='way' ref='416150866' role='' />\n    <member type='way' ref='417922197' role='' />\n    <member type='way' ref='417922204' role='' />\n    <member type='way' ref='417922198' role='' />\n    <member type='way' ref='417922202' role='' />\n    <member type='way' ref='417922199' role='' />\n    <member type='way' ref='417922209' role='' />\n    <member type='way' ref='458957047' role='' />\n    <member type='way' ref='417922207' role='' />\n    <member type='way' ref='107014156' role='' />\n    <member type='way' ref='416149494' role='' />\n    <member type='way' ref='416149495' role='' />\n    <member type='way' ref='416148263' role='' />\n    <member type='way' ref='417922206' role='' />\n    <member type='way' ref='416148182' role='' />\n    <member type='way' ref='416148183' role='' />\n    <member type='way' ref='416148130' role='' />\n    <member type='way' ref='416148131' role='' />\n    <member type='way' ref='458957046' role='' />\n    <member type='way' ref='416148070' role='' />\n    <member type='way' ref='416147987' role='' />\n    <member type='way' ref='416147986' role='' />\n    <member type='way' ref='416147906' role='' />\n    <member type='way' ref='195604806' role='' />\n    <member type='way' ref='230391158' role='' />\n    <member type='way' ref='230391151' role='' />\n    <member type='way' ref='231610637' role='' />\n    <member type='way' ref='202317705' role='' />\n    <member type='way' ref='416161538' role='' />\n    <member type='way' ref='416161537' role='' />\n    <member type='way' ref='195604782' role='' />\n    <member type='way' ref='25533417' role='' />\n    <member type='way' ref='618433958' role='' />\n    <member type='way' ref='617930747' role='' />\n    <member type='way' ref='202460854' role='' />\n    <member type='way' ref='4758023' role='' />\n    <member type='way' ref='28412732' role='' />\n    <member type='way' ref='28412730' role='' />\n    <member type='way' ref='393883536' role='' />\n    <member type='way' ref='57169171' role='' />\n    <member type='way' ref='123867341' role='' />\n    <member type='way' ref='28183675' role='' />\n    <member type='way' ref='28183685' role='' />\n    <member type='way' ref='28183688' role='' />\n    <member type='way' ref='28183698' role='' />\n    <member type='way' ref='76789991' role='' />\n    <member type='way' ref='10713932' role='' />\n    <member type='way' ref='183343421' role='' />\n    <member type='way' ref='183343418' role='' />\n    <member type='way' ref='28183739' role='' />\n    <member type='way' ref='28183738' role='' />\n    <member type='way' ref='10718888' role='' />\n    <member type='way' ref='395576867' role='' />\n    <member type='way' ref='10718726' role='' />\n    <member type='way' ref='10718727' role='' />\n    <member type='way' ref='119058985' role='' />\n    <member type='way' ref='119058973' role='' />\n    <member type='way' ref='123867427' role='' />\n    <member type='way' ref='11162455' role='' />\n    <member type='way' ref='119058995' role='' />\n    <member type='way' ref='123867429' role='' />\n    <member type='way' ref='119058986' role='' />\n    <member type='way' ref='28411014' role='' />\n    <member type='way' ref='6391210' role='' />\n    <member type='way' ref='119058993' role='' />\n    <member type='way' ref='28413679' role='' />\n    <member type='way' ref='11197898' role='' />\n    <member type='way' ref='123741311' role='' />\n    <member type='way' ref='123741303' role='' />\n    <member type='way' ref='90077193' role='' />\n    <member type='way' ref='340686922' role='' />\n    <member type='way' ref='236348366' role='' />\n    <member type='way' ref='562568903' role='' />\n    <member type='way' ref='340686911' role='' />\n    <member type='way' ref='236348361' role='' />\n    <member type='way' ref='236348360' role='' />\n    <member type='way' ref='497579295' role='' />\n    <member type='way' ref='11415208' role='' />\n    <member type='way' ref='23874736' role='' />\n    <member type='way' ref='394443191' role='' />\n    <member type='way' ref='90077241' role='' />\n    <member type='way' ref='8921938' role='' />\n    <member type='way' ref='120813417' role='' />\n    <member type='way' ref='202485364' role='' />\n    <member type='way' ref='617730080' role='' />\n    <member type='way' ref='510583844' role='' />\n    <member type='way' ref='31129732' role='' />\n    <member type='way' ref='254299122' role='' />\n    <member type='way' ref='8915263' role='' />\n    <member type='way' ref='27167756' role='' />\n    <member type='way' ref='617730084' role='' />\n    <member type='way' ref='119237653' role='' />\n    <member type='way' ref='222603227' role='' />\n    <member type='way' ref='8922462' role='' />\n    <member type='way' ref='28596014' role='' />\n    <member type='way' ref='202317710' role='' />\n    <member type='way' ref='204964839' role='' />\n    <member type='way' ref='361622905' role='' />\n    <member type='way' ref='228339825' role='' />\n    <member type='way' ref='417087433' role='' />\n    <member type='way' ref='417087426' role='' />\n    <member type='way' ref='224197569' role='' />\n    <member type='way' ref='514906781' role='' />\n    <member type='way' ref='228228520' role='' />\n    <member type='way' ref='212214143' role='' />\n    <member type='way' ref='224373752' role='' />\n    <member type='way' ref='8917791' role='' />\n    <member type='way' ref='417087270' role='' />\n    <member type='way' ref='417087260' role='' />\n    <member type='way' ref='24335441' role='' />\n    <member type='way' ref='397118327' role='' />\n    <member type='way' ref='34128766' role='' />\n    <member type='way' ref='143666216' role='' />\n    <member type='way' ref='417087550' role='' />\n    <member type='way' ref='511666057' role='' />\n    <member type='way' ref='417087568' role='' />\n    <member type='way' ref='511666056' role='' />\n    <member type='way' ref='143666213' role='' />\n    <member type='way' ref='254759955' role='' />\n    <member type='way' ref='198565351' role='' />\n    <member type='way' ref='198565350' role='' />\n    <member type='way' ref='198565352' role='' />\n    <member type='way' ref='8919009' role='' />\n    <member type='way' ref='198565345' role='' />\n    <member type='way' ref='414156935' role='' />\n    <member type='way' ref='514581439' role='' />\n    <member type='way' ref='23878997' role='' />\n    <member type='way' ref='254299728' role='' />\n    <member type='way' ref='619568411' role='' />\n    <tag k='from' v='Richmond BART' />\n    <tag k='name' v='AC Transit 800 (to San Francisco)' />\n    <tag k='network' v='AC Transit' />\n    <tag k='operator' v='Alameda-Contra Costa Transit District' />\n    <tag k='ref' v='800' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='San Francisco' />\n    <tag k='type' v='route' />\n    <tag k='via' v='Oakland' />\n  </relation>\n  <relation id='2716240' timestamp='2013-12-16T19:21:33Z' uid='153669' user='dchiles' version='2' changeset='19487292'>\n    <member type='relation' ref='2716238' role='' />\n    <member type='relation' ref='2716239' role='' />\n    <tag k='name' v='Line 800' />\n    <tag k='operator' v='AC Transit' />\n    <tag k='ref' v='800' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='2768040' timestamp='2018-11-05T17:51:08Z' uid='119881' user='clay_c' version='46' changeset='64205910'>\n    <member type='node' ref='2150077200' role='stop' />\n    <member type='node' ref='2160213358' role='stop' />\n    <member type='node' ref='564663926' role='stop' />\n    <member type='node' ref='2160213354' role='stop' />\n    <member type='node' ref='30236612' role='stop' />\n    <member type='node' ref='647651427' role='stop' />\n    <member type='node' ref='3646391919' role='stop' />\n    <member type='node' ref='2160213362' role='stop' />\n    <member type='node' ref='266947168' role='stop' />\n    <member type='node' ref='3646391941' role='stop' />\n    <member type='node' ref='3646391943' role='stop' />\n    <member type='node' ref='3646391944' role='stop' />\n    <member type='node' ref='3646391945' role='stop' />\n    <member type='node' ref='312418973' role='stop_position' />\n    <member type='node' ref='3721863783' role='stop_position' />\n    <member type='way' ref='206031838' role='forward' />\n    <member type='way' ref='639288281' role='backward' />\n    <member type='way' ref='206031845' role='backward' />\n    <member type='way' ref='119407994' role='backward' />\n    <member type='way' ref='206031837' role='backward' />\n    <member type='way' ref='639288284' role='forward' />\n    <member type='way' ref='206031846' role='forward' />\n    <member type='way' ref='206031840' role='backward' />\n    <member type='way' ref='119407555' role='backward' />\n    <member type='way' ref='608569202' role='forward' />\n    <member type='way' ref='639288279' role='forward' />\n    <member type='way' ref='639288277' role='forward' />\n    <member type='way' ref='110353426' role='forward' />\n    <member type='way' ref='639288278' role='forward' />\n    <member type='way' ref='8920472' role='forward' />\n    <member type='way' ref='125738663' role='forward' />\n    <member type='way' ref='125738664' role='forward' />\n    <member type='way' ref='26780771' role='forward' />\n    <member type='way' ref='23937685' role='forward' />\n    <member type='way' ref='27614456' role='forward' />\n    <member type='way' ref='27614455' role='forward' />\n    <member type='way' ref='61601587' role='forward' />\n    <member type='way' ref='61601588' role='forward' />\n    <member type='way' ref='27614412' role='forward' />\n    <member type='way' ref='27614413' role='forward' />\n    <member type='way' ref='39070563' role='forward' />\n    <member type='way' ref='39070561' role='forward' />\n    <member type='way' ref='27614350' role='forward' />\n    <member type='way' ref='27614351' role='forward' />\n    <member type='way' ref='8920487' role='forward' />\n    <member type='way' ref='23937630' role='forward' />\n    <member type='way' ref='389800282' role='forward' />\n    <member type='way' ref='389800280' role='forward' />\n    <member type='way' ref='8920480' role='forward' />\n    <member type='way' ref='8920316' role='forward' />\n    <member type='way' ref='32199046' role='backward' />\n    <member type='way' ref='639288308' role='backward' />\n    <member type='way' ref='639288299' role='backward' />\n    <member type='way' ref='8926837' role='backward' />\n    <member type='way' ref='23946554' role='forward' />\n    <member type='way' ref='639288336' role='forward' />\n    <member type='way' ref='639288334' role='forward' />\n    <member type='way' ref='259275843' role='forward' />\n    <member type='way' ref='259275849' role='forward' />\n    <member type='way' ref='259275845' role='forward' />\n    <member type='way' ref='259275851' role='forward' />\n    <member type='way' ref='639288340' role='forward' />\n    <member type='way' ref='639288338' role='forward' />\n    <member type='way' ref='385950999' role='forward' />\n    <member type='way' ref='385951001' role='forward' />\n    <member type='way' ref='24446834' role='forward' />\n    <member type='way' ref='24446833' role='forward' />\n    <member type='way' ref='24446837' role='forward' />\n    <member type='way' ref='24446838' role='forward' />\n    <member type='way' ref='24446848' role='forward' />\n    <member type='way' ref='24446847' role='forward' />\n    <member type='way' ref='24446861' role='forward' />\n    <member type='way' ref='639288344' role='forward' />\n    <member type='way' ref='24446860' role='forward' />\n    <member type='way' ref='119527504' role='forward' />\n    <member type='way' ref='119527510' role='forward' />\n    <member type='way' ref='44421264' role='forward' />\n    <member type='way' ref='44421265' role='forward' />\n    <member type='way' ref='43835697' role='forward' />\n    <member type='way' ref='43835695' role='forward' />\n    <member type='way' ref='639288349' role='forward' />\n    <member type='way' ref='43835699' role='forward' />\n    <member type='way' ref='59329491' role='forward' />\n    <member type='way' ref='59329477' role='forward' />\n    <member type='way' ref='43835705' role='forward' />\n    <member type='way' ref='59329481' role='forward' />\n    <member type='way' ref='59329483' role='forward' />\n    <member type='way' ref='59329465' role='forward' />\n    <member type='way' ref='59329462' role='forward' />\n    <member type='way' ref='59329469' role='forward' />\n    <member type='way' ref='59329452' role='forward' />\n    <member type='way' ref='59329454' role='forward' />\n    <member type='way' ref='59329456' role='forward' />\n    <member type='way' ref='47688463' role='forward' />\n    <member type='way' ref='506797115' role='forward' />\n    <member type='way' ref='639288351' role='forward' />\n    <member type='way' ref='506797116' role='forward' />\n    <member type='way' ref='34396089' role='forward' />\n    <member type='way' ref='34396090' role='forward' />\n    <member type='way' ref='8926810' role='forward' />\n    <member type='way' ref='44129793' role='backward' />\n    <member type='way' ref='44129792' role='backward' />\n    <member type='way' ref='8926825' role='backward' />\n    <member type='way' ref='8936043' role='forward' />\n    <member type='way' ref='66209396' role='forward' />\n    <member type='way' ref='368345084' role='forward' />\n    <member type='way' ref='368345083' role='forward' />\n    <member type='way' ref='27422567' role='forward' />\n    <member type='way' ref='32315631' role='backward' />\n    <member type='way' ref='367132252' role='backward' />\n    <member type='way' ref='367132251' role='backward' />\n    <member type='way' ref='32315626' role='backward' />\n    <member type='way' ref='32315575' role='backward' />\n    <member type='way' ref='8936124' role='forward' />\n    <member type='way' ref='51324837' role='backward' />\n    <member type='way' ref='639288357' role='backward' />\n    <member type='way' ref='639288355' role='forward' />\n    <member type='way' ref='18409986' role='forward' />\n    <member type='way' ref='38883727' role='backward' />\n    <member type='way' ref='38883735' role='backward' />\n    <member type='way' ref='28485280' role='backward' />\n    <member type='way' ref='28485281' role='backward' />\n    <member type='way' ref='38883723' role='backward' />\n    <member type='way' ref='38883722' role='backward' />\n    <member type='way' ref='8936023' role='backward' />\n    <member type='way' ref='459757519' role='backward' />\n    <member type='way' ref='119420072' role='forward' />\n    <member type='way' ref='639288362' role='forward' />\n    <member type='way' ref='8936148' role='forward' />\n    <member type='way' ref='42477721' role='forward' />\n    <member type='way' ref='540107871' role='forward' />\n    <member type='way' ref='540107870' role='forward' />\n    <member type='way' ref='206031824' role='forward' />\n    <member type='way' ref='206031842' role='forward' />\n    <member type='way' ref='37311641' role='backward' />\n    <member type='way' ref='206031832' role='backward' />\n    <member type='way' ref='608569201' role='backward' />\n    <member type='way' ref='639288276' role='backward' />\n    <member type='way' ref='119709582' role='backward' />\n    <member type='way' ref='639288274' role='backward' />\n    <member type='way' ref='119709558' role='backward' />\n    <member type='way' ref='125738662' role='backward' />\n    <member type='way' ref='125738665' role='backward' />\n    <member type='way' ref='119709579' role='backward' />\n    <member type='way' ref='119709567' role='backward' />\n    <member type='way' ref='119709589' role='backward' />\n    <member type='way' ref='119709584' role='backward' />\n    <member type='way' ref='119709562' role='backward' />\n    <member type='way' ref='119709561' role='backward' />\n    <member type='way' ref='119709573' role='backward' />\n    <member type='way' ref='119709557' role='backward' />\n    <member type='way' ref='119709578' role='backward' />\n    <member type='way' ref='119709566' role='backward' />\n    <member type='way' ref='119709587' role='backward' />\n    <member type='way' ref='119709569' role='backward' />\n    <member type='way' ref='119709596' role='backward' />\n    <member type='way' ref='119709575' role='backward' />\n    <member type='way' ref='389800281' role='backward' />\n    <member type='way' ref='389800279' role='backward' />\n    <member type='way' ref='119627699' role='backward' />\n    <member type='way' ref='119627709' role='backward' />\n    <member type='way' ref='119539640' role='forward' />\n    <member type='way' ref='639288304' role='forward' />\n    <member type='way' ref='639288293' role='forward' />\n    <member type='way' ref='119538866' role='forward' />\n    <member type='way' ref='259275850' role='backward' />\n    <member type='way' ref='639288335' role='backward' />\n    <member type='way' ref='639288333' role='backward' />\n    <member type='way' ref='259275842' role='backward' />\n    <member type='way' ref='259275846' role='backward' />\n    <member type='way' ref='259275848' role='backward' />\n    <member type='way' ref='385951002' role='backward' />\n    <member type='way' ref='385951000' role='backward' />\n    <member type='way' ref='639288339' role='backward' />\n    <member type='way' ref='639288341' role='backward' />\n    <member type='way' ref='119527502' role='backward' />\n    <member type='way' ref='119527498' role='backward' />\n    <member type='way' ref='119527512' role='backward' />\n    <member type='way' ref='119527503' role='backward' />\n    <member type='way' ref='119527499' role='backward' />\n    <member type='way' ref='119527511' role='backward' />\n    <member type='way' ref='119527505' role='backward' />\n    <member type='way' ref='119527514' role='backward' />\n    <member type='way' ref='639288347' role='forward' />\n    <member type='way' ref='119527500' role='forward' />\n    <member type='way' ref='119527508' role='forward' />\n    <member type='way' ref='119527513' role='forward' />\n    <member type='way' ref='119527506' role='backward' />\n    <member type='way' ref='119525091' role='backward' />\n    <member type='way' ref='119525085' role='backward' />\n    <member type='way' ref='119525076' role='backward' />\n    <member type='way' ref='639288350' role='backward' />\n    <member type='way' ref='119525081' role='backward' />\n    <member type='way' ref='119525088' role='backward' />\n    <member type='way' ref='119525084' role='backward' />\n    <member type='way' ref='119525079' role='backward' />\n    <member type='way' ref='119525093' role='backward' />\n    <member type='way' ref='119525092' role='backward' />\n    <member type='way' ref='119525086' role='backward' />\n    <member type='way' ref='119525080' role='backward' />\n    <member type='way' ref='119525077' role='backward' />\n    <member type='way' ref='119525075' role='backward' />\n    <member type='way' ref='119525078' role='backward' />\n    <member type='way' ref='119525087' role='backward' />\n    <member type='way' ref='119525074' role='backward' />\n    <member type='way' ref='506797114' role='backward' />\n    <member type='way' ref='639288353' role='backward' />\n    <member type='way' ref='639288352' role='backward' />\n    <member type='way' ref='506797113' role='backward' />\n    <member type='way' ref='119525082' role='backward' />\n    <member type='way' ref='119525083' role='backward' />\n    <member type='way' ref='119274434' role='forward' />\n    <member type='way' ref='119274427' role='forward' />\n    <member type='way' ref='119274430' role='forward' />\n    <member type='way' ref='119352145' role='forward' />\n    <member type='way' ref='119352143' role='backward' />\n    <member type='way' ref='119352142' role='backward' />\n    <member type='way' ref='119391946' role='backward' />\n    <member type='way' ref='368345086' role='backward' />\n    <member type='way' ref='368345085' role='backward' />\n    <member type='way' ref='119391944' role='backward' />\n    <member type='way' ref='206031847' role='forward' />\n    <member type='way' ref='367132250' role='forward' />\n    <member type='way' ref='367132249' role='forward' />\n    <member type='way' ref='206031829' role='forward' />\n    <member type='way' ref='119391941' role='forward' />\n    <member type='way' ref='119408003' role='backward' />\n    <member type='way' ref='119408002' role='forward' />\n    <member type='way' ref='639288356' role='forward' />\n    <member type='way' ref='639288354' role='backward' />\n    <member type='way' ref='119408004' role='backward' />\n    <member type='way' ref='30694882' role='forward' />\n    <member type='way' ref='206031822' role='backward' />\n    <member type='way' ref='119420076' role='forward' />\n    <member type='way' ref='119420067' role='forward' />\n    <member type='way' ref='119420084' role='forward' />\n    <member type='way' ref='119420071' role='forward' />\n    <member type='way' ref='119420077' role='forward' />\n    <member type='way' ref='119420081' role='forward' />\n    <member type='way' ref='459757523' role='forward' />\n    <member type='way' ref='353731500' role='forward' />\n    <member type='way' ref='119420088' role='forward' />\n    <member type='way' ref='639288363' role='forward' />\n    <member type='way' ref='213371621' role='forward' />\n    <member type='way' ref='540107872' role='forward' />\n    <member type='way' ref='540107873' role='forward' />\n    <member type='way' ref='42479145' role='forward' />\n    <member type='way' ref='639288368' role='forward' />\n    <member type='way' ref='42479151' role='' />\n    <member type='way' ref='411194873' role='' />\n    <member type='way' ref='213371623' role='' />\n    <member type='way' ref='316902613' role='' />\n    <member type='way' ref='354329683' role='' />\n    <member type='way' ref='639288386' role='' />\n    <member type='way' ref='28431980' role='' />\n    <member type='way' ref='119514255' role='' />\n    <member type='way' ref='119514252' role='' />\n    <member type='way' ref='119514264' role='' />\n    <member type='way' ref='206031844' role='' />\n    <member type='way' ref='206031825' role='' />\n    <member type='way' ref='206031831' role='' />\n    <member type='way' ref='206031848' role='' />\n    <member type='way' ref='206031843' role='' />\n    <member type='way' ref='28433401' role='' />\n    <member type='way' ref='28433400' role='' />\n    <member type='way' ref='175042624' role='' />\n    <member type='way' ref='175042623' role='' />\n    <member type='way' ref='28433438' role='' />\n    <member type='way' ref='28433442' role='' />\n    <member type='way' ref='206031833' role='' />\n    <member type='way' ref='206031853' role='backward' />\n    <member type='way' ref='641310738' role='backward' />\n    <member type='way' ref='639288379' role='backward' />\n    <member type='way' ref='206031836' role='forward' />\n    <member type='way' ref='206031834' role='' />\n    <member type='way' ref='206031839' role='' />\n    <member type='way' ref='26275242' role='' />\n    <member type='way' ref='639288371' role='' />\n    <member type='way' ref='316893345' role='' />\n    <member type='way' ref='639288370' role='' />\n    <member type='way' ref='316893356' role='' />\n    <member type='way' ref='639218776' role='' />\n    <member type='way' ref='406323588' role='' />\n    <member type='way' ref='316893349' role='' />\n    <member type='way' ref='639288381' role='' />\n    <member type='way' ref='554029486' role='' />\n    <member type='way' ref='316893363' role='' />\n    <member type='way' ref='283523737' role='' />\n    <member type='way' ref='283523740' role='' />\n    <member type='way' ref='639288373' role='' />\n    <member type='way' ref='324897089' role='' />\n    <member type='way' ref='324897091' role='' />\n    <member type='way' ref='639288374' role='' />\n    <member type='way' ref='639288382' role='' />\n    <member type='way' ref='120259635' role='' />\n    <member type='way' ref='28295229' role='stop' />\n    <member type='way' ref='641125035' role='stop' />\n    <member type='way' ref='188661968' role='stop' />\n    <member type='way' ref='354329698' role='' />\n    <member type='way' ref='316902614' role='' />\n    <member type='way' ref='301707409' role='stop' />\n    <tag k='colour' v='red' />\n    <tag k='name' v='Baby Bullet' />\n    <tag k='network' v='Caltrain' />\n    <tag k='operator' v='TransitAmerica Services' />\n    <tag k='operator:wikidata' v='Q166817' />\n    <tag k='operator:wikipedia' v='en:Caltrain' />\n    <tag k='passenger' v='suburban' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='305-386' />\n    <tag k='route' v='train' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='2768041' timestamp='2018-11-05T17:51:09Z' uid='119881' user='clay_c' version='48' changeset='64205910'>\n    <member type='node' ref='266947168' role='stop' />\n    <member type='node' ref='252024546' role='stop' />\n    <member type='node' ref='266944543' role='stop' />\n    <member type='node' ref='266910712' role='stop' />\n    <member type='node' ref='266910711' role='stop' />\n    <member type='node' ref='2160213362' role='stop' />\n    <member type='node' ref='2160213350' role='stop' />\n    <member type='node' ref='2160213359' role='stop' />\n    <member type='node' ref='2160213352' role='stop' />\n    <member type='node' ref='647651427' role='stop' />\n    <member type='node' ref='30236612' role='stop' />\n    <member type='node' ref='30236493' role='stop' />\n    <member type='node' ref='2160213349' role='stop' />\n    <member type='node' ref='2160213361' role='stop' />\n    <member type='node' ref='2160213354' role='stop' />\n    <member type='node' ref='2160213353' role='stop' />\n    <member type='node' ref='2160213344' role='stop' />\n    <member type='node' ref='2160213357' role='stop' />\n    <member type='node' ref='2160213346' role='stop' />\n    <member type='node' ref='564663926' role='stop' />\n    <member type='node' ref='2160213351' role='stop' />\n    <member type='node' ref='2160213358' role='stop' />\n    <member type='node' ref='2160213348' role='stop' />\n    <member type='node' ref='2160213347' role='stop' />\n    <member type='node' ref='2150077200' role='stop' />\n    <member type='node' ref='2160213360' role='stop' />\n    <member type='node' ref='2160213345' role='stop' />\n    <member type='node' ref='2160213364' role='stop' />\n    <member type='node' ref='3646391919' role='stop' />\n    <member type='node' ref='3646391941' role='stop' />\n    <member type='node' ref='3646391942' role='stop' />\n    <member type='node' ref='3646391943' role='stop' />\n    <member type='node' ref='3646391944' role='stop' />\n    <member type='node' ref='3646391945' role='stop' />\n    <member type='node' ref='3646391946' role='stop' />\n    <member type='node' ref='3721808996' role='stop' />\n    <member type='node' ref='3721808997' role='stop' />\n    <member type='node' ref='312418973' role='stop_position' />\n    <member type='node' ref='3721863783' role='stop_position' />\n    <member type='node' ref='3721890341' role='stop_position' />\n    <member type='node' ref='3721890342' role='stop_position' />\n    <member type='way' ref='119527498' role='backward' />\n    <member type='way' ref='119527512' role='backward' />\n    <member type='way' ref='119527503' role='backward' />\n    <member type='way' ref='119527499' role='backward' />\n    <member type='way' ref='119527511' role='backward' />\n    <member type='way' ref='119527505' role='backward' />\n    <member type='way' ref='119527514' role='backward' />\n    <member type='way' ref='639288347' role='forward' />\n    <member type='way' ref='119527500' role='forward' />\n    <member type='way' ref='119527508' role='forward' />\n    <member type='way' ref='119527513' role='forward' />\n    <member type='way' ref='119527506' role='backward' />\n    <member type='way' ref='119525091' role='backward' />\n    <member type='way' ref='119525085' role='backward' />\n    <member type='way' ref='119525076' role='backward' />\n    <member type='way' ref='639288350' role='backward' />\n    <member type='way' ref='119525081' role='backward' />\n    <member type='way' ref='119525088' role='backward' />\n    <member type='way' ref='119525084' role='backward' />\n    <member type='way' ref='119525079' role='backward' />\n    <member type='way' ref='119525093' role='backward' />\n    <member type='way' ref='119525092' role='backward' />\n    <member type='way' ref='119525086' role='backward' />\n    <member type='way' ref='119525080' role='backward' />\n    <member type='way' ref='119525077' role='backward' />\n    <member type='way' ref='119525075' role='backward' />\n    <member type='way' ref='119525078' role='backward' />\n    <member type='way' ref='119525087' role='backward' />\n    <member type='way' ref='119525074' role='backward' />\n    <member type='way' ref='506797114' role='backward' />\n    <member type='way' ref='639288353' role='backward' />\n    <member type='way' ref='639288352' role='backward' />\n    <member type='way' ref='506797113' role='backward' />\n    <member type='way' ref='119525082' role='backward' />\n    <member type='way' ref='119525083' role='backward' />\n    <member type='way' ref='119274434' role='forward' />\n    <member type='way' ref='119274427' role='forward' />\n    <member type='way' ref='119274430' role='forward' />\n    <member type='way' ref='119352145' role='forward' />\n    <member type='way' ref='119352143' role='backward' />\n    <member type='way' ref='119352142' role='backward' />\n    <member type='way' ref='119391946' role='backward' />\n    <member type='way' ref='368345086' role='backward' />\n    <member type='way' ref='368345085' role='backward' />\n    <member type='way' ref='119391944' role='backward' />\n    <member type='way' ref='206031847' role='forward' />\n    <member type='way' ref='367132250' role='forward' />\n    <member type='way' ref='367132249' role='forward' />\n    <member type='way' ref='206031829' role='forward' />\n    <member type='way' ref='119391941' role='forward' />\n    <member type='way' ref='119408003' role='backward' />\n    <member type='way' ref='119408002' role='forward' />\n    <member type='way' ref='639288356' role='forward' />\n    <member type='way' ref='639288354' role='backward' />\n    <member type='way' ref='119408004' role='backward' />\n    <member type='way' ref='639288359' role='forward' />\n    <member type='way' ref='119407561' role='forward' />\n    <member type='way' ref='639288361' role='forward' />\n    <member type='way' ref='119407995' role='forward' />\n    <member type='way' ref='206031822' role='backward' />\n    <member type='way' ref='119420076' role='forward' />\n    <member type='way' ref='119420067' role='forward' />\n    <member type='way' ref='119420084' role='forward' />\n    <member type='way' ref='119420071' role='forward' />\n    <member type='way' ref='119420077' role='forward' />\n    <member type='way' ref='119420081' role='forward' />\n    <member type='way' ref='459757523' role='forward' />\n    <member type='way' ref='353731500' role='forward' />\n    <member type='way' ref='119420088' role='forward' />\n    <member type='way' ref='639288363' role='forward' />\n    <member type='way' ref='213371621' role='forward' />\n    <member type='way' ref='540107872' role='forward' />\n    <member type='way' ref='540107873' role='forward' />\n    <member type='way' ref='42479145' role='forward' />\n    <member type='way' ref='639288368' role='forward' />\n    <member type='way' ref='42479151' role='' />\n    <member type='way' ref='411194873' role='' />\n    <member type='way' ref='213371623' role='' />\n    <member type='way' ref='316902613' role='' />\n    <member type='way' ref='354329683' role='' />\n    <member type='way' ref='639288386' role='' />\n    <member type='way' ref='28431980' role='' />\n    <member type='way' ref='119514255' role='' />\n    <member type='way' ref='119514252' role='' />\n    <member type='way' ref='119514264' role='' />\n    <member type='way' ref='206031844' role='' />\n    <member type='way' ref='206031825' role='' />\n    <member type='way' ref='206031831' role='' />\n    <member type='way' ref='206031848' role='' />\n    <member type='way' ref='206031843' role='' />\n    <member type='way' ref='28433401' role='' />\n    <member type='way' ref='28433400' role='' />\n    <member type='way' ref='175042624' role='' />\n    <member type='way' ref='175042623' role='' />\n    <member type='way' ref='28433438' role='' />\n    <member type='way' ref='28433442' role='' />\n    <member type='way' ref='206031833' role='' />\n    <member type='way' ref='206031853' role='backward' />\n    <member type='way' ref='641310738' role='backward' />\n    <member type='way' ref='639288379' role='backward' />\n    <member type='way' ref='206031836' role='forward' />\n    <member type='way' ref='206031834' role='' />\n    <member type='way' ref='206031839' role='' />\n    <member type='way' ref='26275242' role='' />\n    <member type='way' ref='639288371' role='' />\n    <member type='way' ref='316893345' role='' />\n    <member type='way' ref='639288370' role='' />\n    <member type='way' ref='316893356' role='' />\n    <member type='way' ref='639218776' role='' />\n    <member type='way' ref='406323588' role='' />\n    <member type='way' ref='316893349' role='' />\n    <member type='way' ref='639288381' role='' />\n    <member type='way' ref='554029486' role='' />\n    <member type='way' ref='316893363' role='' />\n    <member type='way' ref='283523737' role='' />\n    <member type='way' ref='283523740' role='' />\n    <member type='way' ref='639288373' role='' />\n    <member type='way' ref='324897089' role='' />\n    <member type='way' ref='324897091' role='' />\n    <member type='way' ref='639288374' role='' />\n    <member type='way' ref='639288382' role='' />\n    <member type='way' ref='120259635' role='' />\n    <member type='way' ref='608569202' role='forward' />\n    <member type='way' ref='639288279' role='forward' />\n    <member type='way' ref='639288277' role='forward' />\n    <member type='way' ref='110353426' role='forward' />\n    <member type='way' ref='639288278' role='forward' />\n    <member type='way' ref='8920472' role='forward' />\n    <member type='way' ref='125738663' role='forward' />\n    <member type='way' ref='125738664' role='forward' />\n    <member type='way' ref='26780771' role='forward' />\n    <member type='way' ref='23937685' role='forward' />\n    <member type='way' ref='27614456' role='forward' />\n    <member type='way' ref='27614455' role='forward' />\n    <member type='way' ref='61601587' role='forward' />\n    <member type='way' ref='61601588' role='forward' />\n    <member type='way' ref='27614412' role='forward' />\n    <member type='way' ref='27614413' role='forward' />\n    <member type='way' ref='39070563' role='forward' />\n    <member type='way' ref='39070561' role='forward' />\n    <member type='way' ref='27614350' role='forward' />\n    <member type='way' ref='27614351' role='forward' />\n    <member type='way' ref='8920487' role='forward' />\n    <member type='way' ref='23937630' role='forward' />\n    <member type='way' ref='389800282' role='forward' />\n    <member type='way' ref='389800280' role='forward' />\n    <member type='way' ref='8920480' role='forward' />\n    <member type='way' ref='8920316' role='forward' />\n    <member type='way' ref='639288288' role='backward' />\n    <member type='way' ref='119627694' role='backward' />\n    <member type='way' ref='639288326' role='backward' />\n    <member type='way' ref='32199046' role='backward' />\n    <member type='way' ref='639288308' role='backward' />\n    <member type='way' ref='639288299' role='backward' />\n    <member type='way' ref='8926837' role='backward' />\n    <member type='way' ref='23946554' role='forward' />\n    <member type='way' ref='639288336' role='forward' />\n    <member type='way' ref='639288334' role='forward' />\n    <member type='way' ref='259275843' role='forward' />\n    <member type='way' ref='259275849' role='forward' />\n    <member type='way' ref='259275845' role='forward' />\n    <member type='way' ref='259275851' role='forward' />\n    <member type='way' ref='639288340' role='forward' />\n    <member type='way' ref='639288338' role='forward' />\n    <member type='way' ref='385950999' role='forward' />\n    <member type='way' ref='385951001' role='forward' />\n    <member type='way' ref='24446834' role='forward' />\n    <member type='way' ref='24446833' role='forward' />\n    <member type='way' ref='24446837' role='forward' />\n    <member type='way' ref='24446838' role='forward' />\n    <member type='way' ref='24446848' role='forward' />\n    <member type='way' ref='24446847' role='forward' />\n    <member type='way' ref='24446861' role='forward' />\n    <member type='way' ref='639288344' role='forward' />\n    <member type='way' ref='24446860' role='forward' />\n    <member type='way' ref='119527504' role='forward' />\n    <member type='way' ref='119527510' role='forward' />\n    <member type='way' ref='44421264' role='forward' />\n    <member type='way' ref='44421265' role='forward' />\n    <member type='way' ref='43835697' role='forward' />\n    <member type='way' ref='43835695' role='forward' />\n    <member type='way' ref='639288349' role='forward' />\n    <member type='way' ref='43835699' role='forward' />\n    <member type='way' ref='59329491' role='forward' />\n    <member type='way' ref='59329477' role='forward' />\n    <member type='way' ref='43835705' role='forward' />\n    <member type='way' ref='59329481' role='forward' />\n    <member type='way' ref='59329483' role='forward' />\n    <member type='way' ref='59329465' role='forward' />\n    <member type='way' ref='59329462' role='forward' />\n    <member type='way' ref='59329469' role='forward' />\n    <member type='way' ref='59329452' role='forward' />\n    <member type='way' ref='59329454' role='forward' />\n    <member type='way' ref='59329456' role='forward' />\n    <member type='way' ref='47688463' role='forward' />\n    <member type='way' ref='506797115' role='forward' />\n    <member type='way' ref='639288351' role='forward' />\n    <member type='way' ref='506797116' role='forward' />\n    <member type='way' ref='34396089' role='forward' />\n    <member type='way' ref='34396090' role='forward' />\n    <member type='way' ref='8926810' role='forward' />\n    <member type='way' ref='44129793' role='backward' />\n    <member type='way' ref='44129792' role='backward' />\n    <member type='way' ref='8926825' role='backward' />\n    <member type='way' ref='8936043' role='forward' />\n    <member type='way' ref='66209396' role='forward' />\n    <member type='way' ref='368345084' role='forward' />\n    <member type='way' ref='368345083' role='forward' />\n    <member type='way' ref='27422567' role='forward' />\n    <member type='way' ref='32315631' role='backward' />\n    <member type='way' ref='367132252' role='backward' />\n    <member type='way' ref='367132251' role='backward' />\n    <member type='way' ref='32315626' role='backward' />\n    <member type='way' ref='32315575' role='backward' />\n    <member type='way' ref='8936124' role='forward' />\n    <member type='way' ref='51324837' role='backward' />\n    <member type='way' ref='639288357' role='backward' />\n    <member type='way' ref='639288355' role='forward' />\n    <member type='way' ref='18409986' role='forward' />\n    <member type='way' ref='639288358' role='forward' />\n    <member type='way' ref='206031841' role='forward' />\n    <member type='way' ref='206031826' role='backward' />\n    <member type='way' ref='639288360' role='backward' />\n    <member type='way' ref='38883727' role='backward' />\n    <member type='way' ref='38883735' role='backward' />\n    <member type='way' ref='28485280' role='backward' />\n    <member type='way' ref='28485281' role='backward' />\n    <member type='way' ref='38883723' role='backward' />\n    <member type='way' ref='38883722' role='backward' />\n    <member type='way' ref='8936023' role='backward' />\n    <member type='way' ref='459757519' role='backward' />\n    <member type='way' ref='119420072' role='forward' />\n    <member type='way' ref='639288362' role='forward' />\n    <member type='way' ref='8936148' role='forward' />\n    <member type='way' ref='42477721' role='forward' />\n    <member type='way' ref='540107871' role='forward' />\n    <member type='way' ref='540107870' role='forward' />\n    <member type='way' ref='206031824' role='forward' />\n    <member type='way' ref='206031842' role='forward' />\n    <member type='way' ref='37311641' role='backward' />\n    <member type='way' ref='206031832' role='backward' />\n    <member type='way' ref='608569201' role='backward' />\n    <member type='way' ref='639288276' role='backward' />\n    <member type='way' ref='119709582' role='backward' />\n    <member type='way' ref='639288274' role='backward' />\n    <member type='way' ref='119709558' role='backward' />\n    <member type='way' ref='125738662' role='backward' />\n    <member type='way' ref='125738665' role='backward' />\n    <member type='way' ref='119709579' role='backward' />\n    <member type='way' ref='119709567' role='backward' />\n    <member type='way' ref='119709589' role='backward' />\n    <member type='way' ref='119709584' role='backward' />\n    <member type='way' ref='119709562' role='backward' />\n    <member type='way' ref='119709561' role='backward' />\n    <member type='way' ref='119709573' role='backward' />\n    <member type='way' ref='119709557' role='backward' />\n    <member type='way' ref='119709578' role='backward' />\n    <member type='way' ref='119709566' role='backward' />\n    <member type='way' ref='119709587' role='backward' />\n    <member type='way' ref='119709569' role='backward' />\n    <member type='way' ref='119709596' role='backward' />\n    <member type='way' ref='119709575' role='backward' />\n    <member type='way' ref='389800281' role='backward' />\n    <member type='way' ref='389800279' role='backward' />\n    <member type='way' ref='119627699' role='backward' />\n    <member type='way' ref='119627709' role='backward' />\n    <member type='way' ref='639288280' role='forward' />\n    <member type='way' ref='119627702' role='forward' />\n    <member type='way' ref='639288330' role='forward' />\n    <member type='way' ref='119539640' role='forward' />\n    <member type='way' ref='639288304' role='forward' />\n    <member type='way' ref='639288293' role='forward' />\n    <member type='way' ref='119538866' role='forward' />\n    <member type='way' ref='259275850' role='backward' />\n    <member type='way' ref='639288335' role='backward' />\n    <member type='way' ref='639288333' role='backward' />\n    <member type='way' ref='259275842' role='backward' />\n    <member type='way' ref='259275846' role='backward' />\n    <member type='way' ref='259275848' role='backward' />\n    <member type='way' ref='385951002' role='backward' />\n    <member type='way' ref='385951000' role='backward' />\n    <member type='way' ref='639288339' role='backward' />\n    <member type='way' ref='639288341' role='backward' />\n    <member type='way' ref='119527502' role='backward' />\n    <member type='way' ref='30694882' role='forward' />\n    <member type='way' ref='28295229' role='stop' />\n    <member type='way' ref='213371654' role='' />\n    <member type='way' ref='641125035' role='stop' />\n    <member type='way' ref='188661968' role='stop' />\n    <member type='way' ref='119507157' role='' />\n    <member type='way' ref='354329695' role='' />\n    <member type='way' ref='540035964' role='' />\n    <member type='way' ref='540035965' role='platform' />\n    <member type='way' ref='301707409' role='stop' />\n    <tag k='colour' v='white' />\n    <tag k='name' v='Local' />\n    <tag k='network' v='Caltrain' />\n    <tag k='operator' v='TransitAmerica Services' />\n    <tag k='operator:wikidata' v='Q166817' />\n    <tag k='operator:wikipedia' v='en:Caltrain' />\n    <tag k='passenger' v='suburban' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='100s' />\n    <tag k='route' v='train' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='2981333' timestamp='2018-10-15T22:03:42Z' uid='2237750' user='chachafish' version='39' changeset='63557245'>\n    <member type='way' ref='224384023' role='' />\n    <member type='way' ref='397125841' role='' />\n    <member type='way' ref='389359771' role='' />\n    <member type='way' ref='224384048' role='' />\n    <member type='way' ref='389359766' role='' />\n    <member type='way' ref='389359770' role='' />\n    <member type='way' ref='389359767' role='' />\n    <member type='way' ref='24547841' role='' />\n    <member type='way' ref='24547840' role='' />\n    <member type='way' ref='133781849' role='' />\n    <member type='way' ref='224384049' role='' />\n    <member type='way' ref='224384026' role='' />\n    <member type='way' ref='278713839' role='' />\n    <member type='way' ref='224384058' role='' />\n    <member type='way' ref='224384036' role='' />\n    <member type='way' ref='225847539' role='' />\n    <member type='way' ref='224384055' role='' />\n    <member type='way' ref='8917776' role='' />\n    <member type='way' ref='8920710' role='' />\n    <member type='way' ref='224384051' role='' />\n    <member type='way' ref='224384038' role='' />\n    <member type='way' ref='224384046' role='' />\n    <member type='way' ref='224384047' role='' />\n    <member type='way' ref='29482568' role='' />\n    <member type='way' ref='29482562' role='' />\n    <member type='way' ref='29482560' role='' />\n    <member type='way' ref='29482558' role='' />\n    <member type='way' ref='224384012' role='' />\n    <member type='way' ref='224384042' role='' />\n    <member type='way' ref='620728090' role='' />\n    <member type='way' ref='8916338' role='' />\n    <member type='way' ref='218146840' role='' />\n    <member type='way' ref='133735125' role='' />\n    <member type='way' ref='221434104' role='' />\n    <member type='way' ref='84823308' role='' />\n    <member type='way' ref='133755923' role='' />\n    <member type='way' ref='442572410' role='' />\n    <member type='way' ref='442572411' role='' />\n    <member type='way' ref='442572412' role='' />\n    <member type='way' ref='442572413' role='' />\n    <member type='way' ref='143617587' role='' />\n    <member type='way' ref='619568404' role='' />\n    <member type='way' ref='525564024' role='' />\n    <member type='way' ref='143617589' role='' />\n    <member type='way' ref='111550911' role='' />\n    <member type='way' ref='417396747' role='' />\n    <member type='way' ref='88559680' role='' />\n    <member type='way' ref='417396753' role='' />\n    <member type='way' ref='397094803' role='' />\n    <member type='way' ref='88559661' role='' />\n    <member type='way' ref='397094802' role='' />\n    <member type='way' ref='225518512' role='' />\n    <member type='way' ref='467979192' role='' />\n    <member type='way' ref='88559691' role='' />\n    <member type='way' ref='633791726' role='' />\n    <member type='way' ref='88597577' role='' />\n    <member type='way' ref='417101702' role='' />\n    <member type='way' ref='619979102' role='' />\n    <member type='way' ref='224384018' role='' />\n    <member type='way' ref='224384027' role='' />\n    <member type='way' ref='550652206' role='' />\n    <member type='way' ref='8920734' role='' />\n    <member type='way' ref='224316527' role='' />\n    <member type='way' ref='254971068' role='' />\n    <member type='way' ref='254396719' role='' />\n    <member type='way' ref='437935727' role='' />\n    <member type='way' ref='254756526' role='' />\n    <member type='way' ref='254971069' role='' />\n    <member type='way' ref='25372358' role='' />\n    <member type='way' ref='425516652' role='' />\n    <member type='way' ref='598020858' role='' />\n    <member type='way' ref='224384020' role='' />\n    <member type='way' ref='514675661' role='' />\n    <member type='way' ref='416878321' role='' />\n    <member type='way' ref='24335485' role='' />\n    <member type='way' ref='399142440' role='' />\n    <member type='way' ref='398849856' role='' />\n    <member type='way' ref='27050841' role='' />\n    <member type='way' ref='224384021' role='' />\n    <member type='way' ref='85644961' role='' />\n    <member type='way' ref='224384016' role='' />\n    <member type='way' ref='30832455' role='' />\n    <member type='way' ref='224941795' role='' />\n    <member type='way' ref='225516710' role='' />\n    <member type='way' ref='619535759' role='' />\n    <tag k='from' v='24th Street &amp; Potrero Avenue' />\n    <tag k='name' v='10-Townsend: Inbound to Pacific Heights (normal weekday)' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='10' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Jackson St &amp; Webster St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='2981334' timestamp='2018-10-15T22:03:41Z' uid='2237750' user='chachafish' version='44' changeset='63557245'>\n    <member type='way' ref='224316519' role='' />\n    <member type='way' ref='224384029' role='' />\n    <member type='way' ref='225516722' role='' />\n    <member type='way' ref='225516723' role='' />\n    <member type='way' ref='47108401' role='' />\n    <member type='way' ref='30035688' role='' />\n    <member type='way' ref='254758136' role='' />\n    <member type='way' ref='527857399' role='' />\n    <member type='way' ref='527857397' role='' />\n    <member type='way' ref='27050841' role='' />\n    <member type='way' ref='398849856' role='' />\n    <member type='way' ref='399142440' role='' />\n    <member type='way' ref='24335485' role='' />\n    <member type='way' ref='397264925' role='' />\n    <member type='way' ref='24335486' role='' />\n    <member type='way' ref='514671064' role='' />\n    <member type='way' ref='514671065' role='' />\n    <member type='way' ref='514671063' role='' />\n    <member type='way' ref='87376660' role='' />\n    <member type='way' ref='588724745' role='' />\n    <member type='way' ref='87376695' role='' />\n    <member type='way' ref='87376682' role='' />\n    <member type='way' ref='87376679' role='' />\n    <member type='way' ref='406580281' role='' />\n    <member type='way' ref='514675663' role='' />\n    <member type='way' ref='87376703' role='' />\n    <member type='way' ref='598020853' role='' />\n    <member type='way' ref='263819574' role='' />\n    <member type='way' ref='598020854' role='' />\n    <member type='way' ref='598020855' role='' />\n    <member type='way' ref='598020852' role='' />\n    <member type='way' ref='87376675' role='' />\n    <member type='way' ref='397094228' role='' />\n    <member type='way' ref='224384028' role='' />\n    <member type='way' ref='598020858' role='' />\n    <member type='way' ref='425516652' role='' />\n    <member type='way' ref='25372358' role='' />\n    <member type='way' ref='254971069' role='' />\n    <member type='way' ref='254756526' role='' />\n    <member type='way' ref='437935727' role='' />\n    <member type='way' ref='254396719' role='' />\n    <member type='way' ref='254971068' role='' />\n    <member type='way' ref='224316527' role='' />\n    <member type='way' ref='8920734' role='' />\n    <member type='way' ref='550652206' role='' />\n    <member type='way' ref='224384027' role='' />\n    <member type='way' ref='224384018' role='' />\n    <member type='way' ref='619979102' role='' />\n    <member type='way' ref='417101702' role='' />\n    <member type='way' ref='88597577' role='' />\n    <member type='way' ref='633791726' role='' />\n    <member type='way' ref='88559691' role='' />\n    <member type='way' ref='467979192' role='' />\n    <member type='way' ref='225518512' role='' />\n    <member type='way' ref='397094802' role='' />\n    <member type='way' ref='88559661' role='' />\n    <member type='way' ref='397094803' role='' />\n    <member type='way' ref='417396753' role='' />\n    <member type='way' ref='88559680' role='' />\n    <member type='way' ref='417396747' role='' />\n    <member type='way' ref='111550911' role='' />\n    <member type='way' ref='143617589' role='' />\n    <member type='way' ref='525564024' role='' />\n    <member type='way' ref='619568404' role='' />\n    <member type='way' ref='143617587' role='' />\n    <member type='way' ref='442572413' role='' />\n    <member type='way' ref='442572412' role='' />\n    <member type='way' ref='442572411' role='' />\n    <member type='way' ref='442572410' role='' />\n    <member type='way' ref='133755923' role='' />\n    <member type='way' ref='84823308' role='' />\n    <member type='way' ref='221434104' role='' />\n    <member type='way' ref='218146865' role='' />\n    <member type='way' ref='218146840' role='' />\n    <member type='way' ref='8916338' role='' />\n    <member type='way' ref='620728090' role='' />\n    <member type='way' ref='224384025' role='' />\n    <member type='way' ref='224384044' role='' />\n    <member type='way' ref='29482558' role='' />\n    <member type='way' ref='29482560' role='' />\n    <member type='way' ref='29482562' role='' />\n    <member type='way' ref='29482568' role='' />\n    <member type='way' ref='224384047' role='' />\n    <member type='way' ref='224384009' role='' />\n    <member type='way' ref='8920710' role='' />\n    <member type='way' ref='8917776' role='' />\n    <member type='way' ref='224384055' role='' />\n    <member type='way' ref='225847539' role='' />\n    <member type='way' ref='224384036' role='' />\n    <member type='way' ref='224384058' role='' />\n    <member type='way' ref='224384057' role='' />\n    <member type='way' ref='283080370' role='' />\n    <member type='way' ref='110472619' role='' />\n    <member type='way' ref='133781849' role='' />\n    <member type='way' ref='24547840' role='' />\n    <member type='way' ref='24547841' role='' />\n    <member type='way' ref='389359767' role='' />\n    <member type='way' ref='389359770' role='' />\n    <member type='way' ref='389359766' role='' />\n    <member type='way' ref='8919650' role='' />\n    <member type='way' ref='224384054' role='' />\n    <tag k='from' v='Jackson St &amp; Fillmore St' />\n    <tag k='name' v='10-Townsend: Outbound to San Francisco General Hospital (normal weekday)' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='10' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='23rd St &amp; Utah St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='2981335' timestamp='2013-12-31T03:27:29Z' uid='119881' user='clay_c' version='2' changeset='19726354'>\n    <member type='relation' ref='2981333' role='' />\n    <member type='relation' ref='2981334' role='' />\n    <tag k='name' v='10-Townsend' />\n    <tag k='nextbus:agency' v='sf-muni' />\n    <tag k='nextbus:route' v='10' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='ref' v='10' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='3001321' timestamp='2018-10-15T22:03:43Z' uid='2237750' user='chachafish' version='36' changeset='63557245'>\n    <member type='node' ref='4631688565' role='stop' />\n    <member type='way' ref='225847521' role='' />\n    <member type='way' ref='225847534' role='' />\n    <member type='way' ref='225847516' role='' />\n    <member type='way' ref='69598564' role='' />\n    <member type='way' ref='69598565' role='' />\n    <member type='way' ref='8919099' role='' />\n    <member type='way' ref='27023200' role='' />\n    <member type='way' ref='8916919' role='' />\n    <member type='way' ref='155965636' role='' />\n    <member type='way' ref='437552470' role='' />\n    <member type='way' ref='513725768' role='' />\n    <member type='way' ref='255351715' role='' />\n    <member type='way' ref='396979375' role='' />\n    <member type='way' ref='396979384' role='' />\n    <member type='way' ref='396979372' role='' />\n    <member type='way' ref='255166627' role='' />\n    <member type='way' ref='397084138' role='' />\n    <member type='way' ref='417385476' role='' />\n    <member type='way' ref='30516559' role='' />\n    <member type='way' ref='488986464' role='' />\n    <member type='way' ref='396972638' role='' />\n    <member type='way' ref='417385474' role='' />\n    <member type='way' ref='283080371' role='' />\n    <member type='way' ref='283080372' role='' />\n    <member type='way' ref='417385444' role='' />\n    <member type='way' ref='225847506' role='' />\n    <member type='way' ref='8920510' role='' />\n    <member type='way' ref='225847508' role='' />\n    <member type='way' ref='225847536' role='' />\n    <member type='way' ref='224384036' role='' />\n    <member type='way' ref='224384058' role='' />\n    <member type='way' ref='224384057' role='' />\n    <member type='way' ref='283080370' role='' />\n    <member type='way' ref='110472619' role='' />\n    <member type='way' ref='224384049' role='' />\n    <member type='way' ref='225847537' role='' />\n    <member type='way' ref='224384013' role='' />\n    <member type='way' ref='224384012' role='' />\n    <member type='way' ref='224384042' role='' />\n    <member type='way' ref='620728090' role='' />\n    <member type='way' ref='8916338' role='' />\n    <member type='way' ref='218146840' role='' />\n    <member type='way' ref='133735125' role='' />\n    <member type='way' ref='221434104' role='' />\n    <member type='way' ref='84823308' role='' />\n    <member type='way' ref='157812260' role='' />\n    <member type='way' ref='428511530' role='' />\n    <member type='way' ref='539195910' role='' />\n    <member type='way' ref='539195908' role='' />\n    <member type='way' ref='620728096' role='' />\n    <member type='way' ref='488350036' role='' />\n    <member type='way' ref='493107463' role='' />\n    <member type='way' ref='417087280' role='' />\n    <member type='way' ref='417087295' role='' />\n    <member type='way' ref='88572922' role='' />\n    <member type='way' ref='493107464' role='' />\n    <member type='way' ref='417087285' role='' />\n    <member type='way' ref='415520894' role='' />\n    <member type='way' ref='493107465' role='' />\n    <member type='way' ref='417087290' role='' />\n    <member type='way' ref='417269458' role='' />\n    <member type='way' ref='417269470' role='' />\n    <member type='way' ref='403880757' role='' />\n    <member type='way' ref='403880756' role='' />\n    <member type='way' ref='225847523' role='' />\n    <member type='way' ref='26988030' role='' />\n    <member type='way' ref='27167302' role='' />\n    <member type='way' ref='399138123' role='' />\n    <member type='way' ref='254759936' role='' />\n    <member type='way' ref='254448248' role='' />\n    <member type='way' ref='398675749' role='' />\n    <member type='way' ref='91181767' role='' />\n    <member type='way' ref='513704711' role='' />\n    <member type='way' ref='225847531' role='' />\n    <member type='way' ref='23866015' role='' />\n    <member type='way' ref='212214144' role='' />\n    <member type='way' ref='225518506' role='' />\n    <member type='way' ref='27050837' role='' />\n    <member type='way' ref='514667728' role='' />\n    <member type='way' ref='27050839' role='' />\n    <member type='way' ref='28738271' role='' />\n    <member type='way' ref='28738276' role='' />\n    <member type='way' ref='87297165' role='' />\n    <member type='way' ref='225847527' role='' />\n    <member type='way' ref='225847524' role='' />\n    <member type='way' ref='225847502' role='' />\n    <tag k='from' v='Galvez Ave &amp; Horne Ave' />\n    <tag k='name' v='19-Polk: Inbound to Fisherman&apos;s Wharf' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='19' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Beach St &amp; Polk St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3001322' timestamp='2018-10-15T22:03:41Z' uid='2237750' user='chachafish' version='40' changeset='63557245'>\n    <member type='way' ref='425516688' role='' />\n    <member type='way' ref='133781783' role='' />\n    <member type='way' ref='87297165' role='' />\n    <member type='way' ref='28738276' role='' />\n    <member type='way' ref='28738271' role='' />\n    <member type='way' ref='27050839' role='' />\n    <member type='way' ref='514667728' role='' />\n    <member type='way' ref='27050837' role='' />\n    <member type='way' ref='225518506' role='' />\n    <member type='way' ref='212214144' role='' />\n    <member type='way' ref='23866015' role='' />\n    <member type='way' ref='225847531' role='' />\n    <member type='way' ref='297445459' role='' />\n    <member type='way' ref='515595963' role='' />\n    <member type='way' ref='277451471' role='' />\n    <member type='way' ref='225847532' role='' />\n    <member type='way' ref='225847513' role='' />\n    <member type='way' ref='30832489' role='' />\n    <member type='way' ref='225847522' role='' />\n    <member type='way' ref='473325693' role='' />\n    <member type='way' ref='254759933' role='' />\n    <member type='way' ref='530033991' role='' />\n    <member type='way' ref='415491501' role='' />\n    <member type='way' ref='415517886' role='' />\n    <member type='way' ref='417087306' role='' />\n    <member type='way' ref='8921141' role='' />\n    <member type='way' ref='397119705' role='' />\n    <member type='way' ref='417087312' role='' />\n    <member type='way' ref='417087300' role='' />\n    <member type='way' ref='111050389' role='' />\n    <member type='way' ref='620200099' role='' />\n    <member type='way' ref='493107462' role='' />\n    <member type='way' ref='419633752' role='' />\n    <member type='way' ref='525662693' role='' />\n    <member type='way' ref='394558807' role='' />\n    <member type='way' ref='573287808' role='' />\n    <member type='way' ref='516061744' role='' />\n    <member type='way' ref='27167281' role='' />\n    <member type='way' ref='516061750' role='' />\n    <member type='way' ref='221434104' role='' />\n    <member type='way' ref='218146865' role='' />\n    <member type='way' ref='218146840' role='' />\n    <member type='way' ref='8916338' role='' />\n    <member type='way' ref='620728090' role='' />\n    <member type='way' ref='224384025' role='' />\n    <member type='way' ref='224384024' role='' />\n    <member type='way' ref='84805680' role='' />\n    <member type='way' ref='224384026' role='' />\n    <member type='way' ref='278713839' role='' />\n    <member type='way' ref='224384058' role='' />\n    <member type='way' ref='224384036' role='' />\n    <member type='way' ref='225847539' role='' />\n    <member type='way' ref='8920510' role='' />\n    <member type='way' ref='225847506' role='' />\n    <member type='way' ref='417385444' role='' />\n    <member type='way' ref='283080372' role='' />\n    <member type='way' ref='283080371' role='' />\n    <member type='way' ref='417385474' role='' />\n    <member type='way' ref='396972638' role='' />\n    <member type='way' ref='488986464' role='' />\n    <member type='way' ref='30516559' role='' />\n    <member type='way' ref='417385476' role='' />\n    <member type='way' ref='397084138' role='' />\n    <member type='way' ref='155965653' role='' />\n    <member type='way' ref='396979382' role='' />\n    <member type='way' ref='255166626' role='' />\n    <member type='way' ref='396979383' role='' />\n    <member type='way' ref='396979377' role='' />\n    <member type='way' ref='255351716' role='' />\n    <member type='way' ref='397077939' role='' />\n    <member type='way' ref='397077938' role='' />\n    <member type='way' ref='396979380' role='' />\n    <member type='way' ref='396979370' role='' />\n    <member type='way' ref='396979381' role='' />\n    <member type='way' ref='32395824' role='' />\n    <member type='way' ref='8916919' role='' />\n    <member type='way' ref='27023200' role='' />\n    <member type='way' ref='8919099' role='' />\n    <member type='way' ref='69598565' role='' />\n    <member type='way' ref='69598564' role='' />\n    <member type='way' ref='225847516' role='' />\n    <member type='way' ref='8920088' role='' />\n    <tag k='from' v='Beach St &amp; Polk St' />\n    <tag k='name' v='19-Polk: Outbound to Hunters Point' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='19' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Galvez Ave &amp; Horne Ave' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3001323' timestamp='2013-12-31T03:27:30Z' uid='119881' user='clay_c' version='2' changeset='19726354'>\n    <member type='relation' ref='3001321' role='' />\n    <member type='relation' ref='3001322' role='' />\n    <tag k='name' v='19-Polk' />\n    <tag k='nextbus:agency' v='sf-muni' />\n    <tag k='nextbus:route' v='19' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='ref' v='19' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='3341655' timestamp='2017-09-04T08:09:42Z' uid='94578' user='andygol' version='6' changeset='51710383'>\n    <member type='way' ref='519082484' role='from' />\n    <member type='node' ref='2548141867' role='via' />\n    <member type='way' ref='417400705' role='to' />\n    <tag k='name' v='Only left from left side of 8th at Brannan' />\n    <tag k='restriction' v='only_left_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='3341694' timestamp='2017-08-16T08:36:35Z' uid='94578' user='andygol' version='6' changeset='51162893'>\n    <member type='way' ref='516061744' role='from' />\n    <member type='node' ref='2548141867' role='via' />\n    <member type='way' ref='417400705' role='to' />\n    <tag k='name' v='no left from west half of 8th to Brannan' />\n    <tag k='restriction' v='no_left_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='3406515' timestamp='2018-08-26T21:39:21Z' uid='8107451' user='njtbusfan' version='70' changeset='62016715'>\n    <member type='node' ref='5791157081' role='stop' />\n    <member type='way' ref='240301451' role='' />\n    <member type='way' ref='240301452' role='' />\n    <member type='way' ref='159154465' role='' />\n    <member type='way' ref='247329525' role='' />\n    <member type='way' ref='442153004' role='' />\n    <member type='way' ref='255178079' role='' />\n    <member type='way' ref='264872663' role='' />\n    <member type='way' ref='30642929' role='' />\n    <member type='way' ref='619535699' role='' />\n    <member type='way' ref='159766611' role='' />\n    <member type='way' ref='619568401' role='' />\n    <member type='way' ref='417390893' role='' />\n    <member type='way' ref='255351718' role='' />\n    <member type='way' ref='214514823' role='' />\n    <member type='way' ref='243834647' role='' />\n    <member type='way' ref='514550021' role='' />\n    <member type='way' ref='514550014' role='' />\n    <member type='way' ref='417389860' role='' />\n    <member type='way' ref='286436511' role='' />\n    <member type='way' ref='408610939' role='' />\n    <member type='way' ref='243834646' role='' />\n    <member type='way' ref='30757789' role='' />\n    <member type='way' ref='397105106' role='' />\n    <member type='way' ref='106963935' role='' />\n    <member type='way' ref='464926130' role='' />\n    <member type='way' ref='283537864' role='' />\n    <member type='way' ref='419643712' role='' />\n    <member type='way' ref='419643711' role='' />\n    <member type='way' ref='397105266' role='' />\n    <member type='way' ref='254984065' role='' />\n    <member type='way' ref='255178077' role='' />\n    <member type='way' ref='419643708' role='' />\n    <member type='way' ref='619568434' role='' />\n    <member type='way' ref='254984066' role='' />\n    <member type='way' ref='30758170' role='' />\n    <member type='way' ref='417389716' role='' />\n    <member type='way' ref='8918118' role='' />\n    <member type='way' ref='417958440' role='' />\n    <member type='way' ref='123660737' role='' />\n    <member type='way' ref='417958437' role='' />\n    <member type='way' ref='513694378' role='' />\n    <member type='way' ref='417958443' role='' />\n    <member type='way' ref='513694377' role='' />\n    <member type='way' ref='464936238' role='' />\n    <member type='way' ref='8922884' role='' />\n    <member type='way' ref='254299137' role='' />\n    <member type='way' ref='254299119' role='' />\n    <member type='way' ref='255178156' role='' />\n    <member type='way' ref='255351736' role='' />\n    <member type='way' ref='286471135' role='' />\n    <member type='way' ref='254396723' role='' />\n    <member type='way' ref='254299138' role='' />\n    <member type='way' ref='254299140' role='' />\n    <member type='way' ref='254299114' role='' />\n    <member type='way' ref='397093352' role='' />\n    <member type='way' ref='255178045' role='' />\n    <member type='way' ref='397093355' role='' />\n    <member type='way' ref='8917741' role='' />\n    <member type='way' ref='485458382' role='' />\n    <member type='way' ref='515593377' role='' />\n    <member type='way' ref='255178137' role='' />\n    <member type='way' ref='397166909' role='' />\n    <member type='way' ref='397166908' role='' />\n    <member type='way' ref='254968964' role='' />\n    <member type='way' ref='514904203' role='' />\n    <member type='way' ref='153227162' role='' />\n    <member type='way' ref='27041779' role='' />\n    <member type='way' ref='417389062' role='' />\n    <member type='way' ref='397120485' role='' />\n    <member type='way' ref='254299130' role='' />\n    <member type='way' ref='515583838' role='' />\n    <member type='way' ref='417353053' role='' />\n    <member type='way' ref='224373754' role='' />\n    <member type='way' ref='417353044' role='' />\n    <member type='way' ref='417353049' role='' />\n    <member type='way' ref='48201560' role='' />\n    <member type='way' ref='254299134' role='' />\n    <member type='way' ref='418522265' role='' />\n    <member type='way' ref='254299111' role='' />\n    <member type='way' ref='8915024' role='' />\n    <member type='way' ref='514615513' role='' />\n    <member type='way' ref='183198843' role='' />\n    <member type='way' ref='48190018' role='' />\n    <member type='way' ref='48190017' role='' />\n    <member type='way' ref='254299123' role='' />\n    <member type='way' ref='28326948' role='' />\n    <member type='way' ref='33645895' role='' />\n    <member type='way' ref='123741441' role='' />\n    <member type='way' ref='27183377' role='' />\n    <member type='way' ref='27183379' role='' />\n    <member type='way' ref='478345678' role='' />\n    <member type='way' ref='617895234' role='' />\n    <member type='way' ref='31129765' role='' />\n    <member type='way' ref='617730081' role='' />\n    <member type='way' ref='123741465' role='' />\n    <member type='way' ref='8915117' role='' />\n    <member type='way' ref='8915115' role='' />\n    <member type='way' ref='254299118' role='' />\n    <member type='way' ref='254308548' role='' />\n    <member type='way' ref='513705407' role='' />\n    <member type='way' ref='186400365' role='' />\n    <member type='way' ref='143666207' role='' />\n    <member type='way' ref='417396772' role='' />\n    <member type='way' ref='620169772' role='' />\n    <member type='way' ref='417396768' role='' />\n    <member type='way' ref='254299142' role='' />\n    <member type='way' ref='397104880' role='' />\n    <member type='way' ref='397144264' role='' />\n    <member type='way' ref='397144265' role='' />\n    <member type='way' ref='397144001' role='' />\n    <member type='way' ref='111552058' role='' />\n    <member type='way' ref='255330068' role='' />\n    <member type='way' ref='88597544' role='' />\n    <member type='way' ref='417396858' role='' />\n    <member type='way' ref='417166168' role='' />\n    <member type='way' ref='224316520' role='' />\n    <member type='way' ref='406944778' role='' />\n    <member type='way' ref='254297759' role='' />\n    <member type='way' ref='397269729' role='' />\n    <member type='way' ref='408575054' role='' />\n    <member type='way' ref='397269730' role='' />\n    <member type='way' ref='406944779' role='' />\n    <member type='way' ref='224316521' role='' />\n    <member type='way' ref='425516647' role='' />\n    <member type='way' ref='406944780' role='' />\n    <member type='way' ref='91182062' role='' />\n    <member type='way' ref='417099335' role='' />\n    <member type='way' ref='417099340' role='' />\n    <member type='way' ref='397094227' role='' />\n    <member type='way' ref='408572568' role='' />\n    <member type='way' ref='91185877' role='' />\n    <member type='way' ref='416878315' role='' />\n    <member type='way' ref='30030101' role='' />\n    <member type='way' ref='87376669' role='' />\n    <member type='way' ref='416877246' role='' />\n    <member type='way' ref='517260065' role='' />\n    <member type='way' ref='48211487' role='' />\n    <member type='way' ref='587388472' role='' />\n    <member type='way' ref='254756518' role='' />\n    <member type='way' ref='587388483' role='' />\n    <member type='way' ref='148874363' role='' />\n    <member type='way' ref='30030433' role='' />\n    <member type='way' ref='30030485' role='' />\n    <member type='way' ref='254299112' role='' />\n    <member type='way' ref='619535770' role='' />\n    <tag k='from' v='Phelan Loop' />\n    <tag k='name' v='8-Bayshore: Inbound to Fisherman&apos;s Wharf' />\n    <tag k='network' v='Muni' />\n    <tag k='old_name' v='8X Bayshore Express' />\n    <tag k='old_ref' v='8X' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='8' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Kearny St &amp; North Point St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3406516' timestamp='2018-08-25T01:00:26Z' uid='8107451' user='njtbusfan' version='69' changeset='61974510'>\n    <member type='node' ref='5790972437' role='stop' />\n    <member type='way' ref='254299124' role='' />\n    <member type='way' ref='399163465' role='' />\n    <member type='way' ref='254982299' role='' />\n    <member type='way' ref='254299128' role='' />\n    <member type='way' ref='30030485' role='' />\n    <member type='way' ref='148846998' role='' />\n    <member type='way' ref='415591483' role='' />\n    <member type='way' ref='148846995' role='' />\n    <member type='way' ref='415197400' role='' />\n    <member type='way' ref='148846996' role='' />\n    <member type='way' ref='206629822' role='' />\n    <member type='way' ref='28841114' role='' />\n    <member type='way' ref='406580282' role='' />\n    <member type='way' ref='397146714' role='' />\n    <member type='way' ref='397146709' role='' />\n    <member type='way' ref='397146710' role='' />\n    <member type='way' ref='397146715' role='' />\n    <member type='way' ref='397146708' role='' />\n    <member type='way' ref='28843615' role='' />\n    <member type='way' ref='397146712' role='' />\n    <member type='way' ref='397146711' role='' />\n    <member type='way' ref='417099351' role='' />\n    <member type='way' ref='417099350' role='' />\n    <member type='way' ref='8921866' role='' />\n    <member type='way' ref='417099347' role='' />\n    <member type='way' ref='28841121' role='' />\n    <member type='way' ref='415726782' role='' />\n    <member type='way' ref='417094961' role='' />\n    <member type='way' ref='254297761' role='' />\n    <member type='way' ref='254758128' role='' />\n    <member type='way' ref='254448247' role='' />\n    <member type='way' ref='85789165' role='' />\n    <member type='way' ref='85789166' role='' />\n    <member type='way' ref='88572895' role='' />\n    <member type='way' ref='417396816' role='' />\n    <member type='way' ref='397127922' role='' />\n    <member type='way' ref='397127921' role='' />\n    <member type='way' ref='408591443' role='' />\n    <member type='way' ref='214545530' role='' />\n    <member type='way' ref='513704346' role='' />\n    <member type='way' ref='513704345' role='' />\n    <member type='way' ref='143617585' role='' />\n    <member type='way' ref='513704344' role='' />\n    <member type='way' ref='254305033' role='' />\n    <member type='way' ref='539195909' role='' />\n    <member type='way' ref='8915588' role='' />\n    <member type='way' ref='27397440' role='' />\n    <member type='way' ref='394443199' role='' />\n    <member type='way' ref='254299122' role='' />\n    <member type='way' ref='154966358' role='' />\n    <member type='way' ref='26943964' role='' />\n    <member type='way' ref='58841187' role='' />\n    <member type='way' ref='27183366' role='' />\n    <member type='way' ref='397267120' role='' />\n    <member type='way' ref='123741422' role='' />\n    <member type='way' ref='28326944' role='' />\n    <member type='way' ref='28326941' role='' />\n    <member type='way' ref='48037053' role='' />\n    <member type='way' ref='48037051' role='' />\n    <member type='way' ref='395418632' role='' />\n    <member type='way' ref='23925474' role='' />\n    <member type='way' ref='515583838' role='' />\n    <member type='way' ref='254299130' role='' />\n    <member type='way' ref='397120485' role='' />\n    <member type='way' ref='417389062' role='' />\n    <member type='way' ref='27041779' role='' />\n    <member type='way' ref='153227162' role='' />\n    <member type='way' ref='514904203' role='' />\n    <member type='way' ref='254968964' role='' />\n    <member type='way' ref='397166908' role='' />\n    <member type='way' ref='397166909' role='' />\n    <member type='way' ref='255178137' role='' />\n    <member type='way' ref='515593377' role='' />\n    <member type='way' ref='469105571' role='' />\n    <member type='way' ref='469105570' role='' />\n    <member type='way' ref='254299115' role='' />\n    <member type='way' ref='283530828' role='' />\n    <member type='way' ref='397093353' role='' />\n    <member type='way' ref='254299138' role='' />\n    <member type='way' ref='254396723' role='' />\n    <member type='way' ref='286471135' role='' />\n    <member type='way' ref='255351736' role='' />\n    <member type='way' ref='255178156' role='' />\n    <member type='way' ref='254299119' role='' />\n    <member type='way' ref='254299137' role='' />\n    <member type='way' ref='8922884' role='' />\n    <member type='way' ref='464936238' role='' />\n    <member type='way' ref='513694377' role='' />\n    <member type='way' ref='417958443' role='' />\n    <member type='way' ref='513694378' role='' />\n    <member type='way' ref='417958437' role='' />\n    <member type='way' ref='123660737' role='' />\n    <member type='way' ref='417958440' role='' />\n    <member type='way' ref='8918118' role='' />\n    <member type='way' ref='417389716' role='' />\n    <member type='way' ref='397106013' role='' />\n    <member type='way' ref='123660741' role='' />\n    <member type='way' ref='255178076' role='' />\n    <member type='way' ref='419643707' role='' />\n    <member type='way' ref='397106015' role='' />\n    <member type='way' ref='254984067' role='' />\n    <member type='way' ref='419643710' role='' />\n    <member type='way' ref='464926131' role='' />\n    <member type='way' ref='30758162' role='' />\n    <member type='way' ref='106963928' role='' />\n    <member type='way' ref='254968939' role='' />\n    <member type='way' ref='243834646' role='' />\n    <member type='way' ref='408610939' role='' />\n    <member type='way' ref='286436511' role='' />\n    <member type='way' ref='417389860' role='' />\n    <member type='way' ref='514550014' role='' />\n    <member type='way' ref='514550021' role='' />\n    <member type='way' ref='243834647' role='' />\n    <member type='way' ref='243834648' role='' />\n    <member type='way' ref='619568402' role='' />\n    <member type='way' ref='397138671' role='' />\n    <member type='way' ref='30642925' role='' />\n    <member type='way' ref='159154464' role='' />\n    <member type='way' ref='264872662' role='' />\n    <member type='way' ref='397138663' role='' />\n    <member type='way' ref='397138668' role='' />\n    <member type='way' ref='479587333' role='' />\n    <member type='way' ref='255178078' role='' />\n    <member type='way' ref='133748397' role='' />\n    <member type='way' ref='286440418' role='' />\n    <member type='way' ref='182076317' role='' />\n    <member type='way' ref='222619426' role='' />\n    <tag k='from' v='Kearny St &amp; North Point St' />\n    <tag k='name' v='8-Bayshore: Outbound to City College' />\n    <tag k='network' v='Muni' />\n    <tag k='old_name' v='8X Bayshore Express' />\n    <tag k='old_ref' v='8X' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='8' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Phelan Loop' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3406517' timestamp='2015-06-09T06:39:18Z' uid='6585' user='Martin Atkins' version='3' changeset='31835136'>\n    <member type='relation' ref='3406516' role='' />\n    <member type='relation' ref='3406515' role='' />\n    <tag k='name' v='8-Bayshore' />\n    <tag k='nextbus:agency' v='sf-muni' />\n    <tag k='nextbus:route' v='8' />\n    <tag k='ref' v='8' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='3406710' timestamp='2018-08-24T19:08:38Z' uid='8107451' user='njtbusfan' version='47' changeset='61968343'>\n    <member type='way' ref='514592845' role='' />\n    <member type='way' ref='27571801' role='' />\n    <member type='way' ref='397153598' role='' />\n    <member type='way' ref='120256349' role='' />\n    <member type='way' ref='111560682' role='' />\n    <member type='way' ref='408575055' role='' />\n    <member type='way' ref='256400251' role='' />\n    <member type='way' ref='88572970' role='' />\n    <member type='way' ref='417101712' role='' />\n    <member type='way' ref='88572877' role='' />\n    <member type='way' ref='88559669' role='' />\n    <member type='way' ref='88572965' role='' />\n    <member type='way' ref='111560679' role='' />\n    <member type='way' ref='111560680' role='' />\n    <member type='way' ref='51392402' role='' />\n    <member type='way' ref='51392400' role='' />\n    <member type='way' ref='88572932' role='' />\n    <member type='way' ref='88572901' role='' />\n    <member type='way' ref='515486318' role='' />\n    <member type='way' ref='264874017' role='' />\n    <member type='way' ref='417396835' role='' />\n    <member type='way' ref='417396848' role='' />\n    <member type='way' ref='607903509' role='' />\n    <member type='way' ref='254305033' role='' />\n    <member type='way' ref='539195909' role='' />\n    <member type='way' ref='8915588' role='' />\n    <member type='way' ref='27397440' role='' />\n    <member type='way' ref='394443199' role='' />\n    <member type='way' ref='254299122' role='' />\n    <member type='way' ref='154966358' role='' />\n    <member type='way' ref='26943964' role='' />\n    <member type='way' ref='58841187' role='' />\n    <member type='way' ref='27183366' role='' />\n    <member type='way' ref='397267120' role='' />\n    <member type='way' ref='123741422' role='' />\n    <member type='way' ref='28326944' role='' />\n    <member type='way' ref='186398866' role='' />\n    <member type='way' ref='495831131' role='' />\n    <member type='way' ref='28656719' role='' />\n    <member type='way' ref='8915101' role='' />\n    <member type='way' ref='395797804' role='' />\n    <member type='way' ref='395797805' role='' />\n    <member type='way' ref='27629992' role='' />\n    <member type='way' ref='183198713' role='' />\n    <member type='way' ref='397127417' role='' />\n    <member type='way' ref='225854639' role='' />\n    <member type='way' ref='254610978' role='' />\n    <member type='way' ref='417389523' role='' />\n    <member type='way' ref='397127411' role='' />\n    <member type='way' ref='254448790' role='' />\n    <member type='way' ref='419633764' role='' />\n    <member type='way' ref='397127428' role='' />\n    <member type='way' ref='467628662' role='' />\n    <member type='way' ref='48582892' role='' />\n    <member type='way' ref='48582891' role='' />\n    <member type='way' ref='534959912' role='' />\n    <member type='way' ref='28715665' role='' />\n    <member type='way' ref='286463496' role='' />\n    <member type='way' ref='286461027' role='' />\n    <member type='way' ref='475732556' role='' />\n    <member type='way' ref='8917154' role='' />\n    <member type='way' ref='8921059' role='' />\n    <member type='way' ref='28952785' role='' />\n    <member type='way' ref='255178102' role='' />\n    <member type='way' ref='255178101' role='' />\n    <member type='way' ref='514614748' role='' />\n    <member type='way' ref='254750503' role='' />\n    <member type='way' ref='254968960' role='' />\n    <member type='way' ref='285975137' role='' />\n    <member type='way' ref='254968961' role='' />\n    <member type='way' ref='214514825' role='' />\n    <member type='way' ref='397103492' role='' />\n    <member type='way' ref='513660226' role='' />\n    <member type='way' ref='214514829' role='' />\n    <member type='way' ref='286434721' role='' />\n    <member type='way' ref='214514827' role='' />\n    <member type='way' ref='255330051' role='' />\n    <member type='way' ref='100132344' role='' />\n    <member type='way' ref='100134698' role='' />\n    <member type='way' ref='520645177' role='' />\n    <member type='way' ref='396985479' role='' />\n    <member type='way' ref='417957731' role='' />\n    <member type='way' ref='8938146' role='' />\n    <member type='way' ref='513982219' role='' />\n    <member type='way' ref='396985519' role='' />\n    <member type='way' ref='397122366' role='' />\n    <member type='way' ref='417385662' role='' />\n    <member type='way' ref='225799922' role='' />\n    <member type='way' ref='8934666' role='' />\n    <member type='way' ref='396985480' role='' />\n    <tag k='FIXME' v='verify exit at Crescent (not 280 to Alemany)' />\n    <tag k='from' v='Steuart St &amp; Mission St' />\n    <tag k='name' v='14X-Mission Express: Outbound to Daly City' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='14X' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Mission St &amp; Flournoy St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3406711' timestamp='2013-12-31T03:27:29Z' uid='119881' user='clay_c' version='2' changeset='19726354'>\n    <member type='relation' ref='3406710' role='' />\n    <member type='relation' ref='3406709' role='' />\n    <tag k='name' v='14X-Mission Express' />\n    <tag k='nextbus:agency' v='sf-muni' />\n    <tag k='nextbus:route' v='14X' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='ref' v='14X' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='3406808' timestamp='2018-08-26T21:39:22Z' uid='8107451' user='njtbusfan' version='41' changeset='62016715'>\n    <member type='way' ref='224373748' role='' />\n    <member type='way' ref='123660736' role='' />\n    <member type='way' ref='123660744' role='' />\n    <member type='way' ref='417330641' role='' />\n    <member type='way' ref='397099298' role='' />\n    <member type='way' ref='133755883' role='' />\n    <member type='way' ref='8919551' role='' />\n    <member type='way' ref='397097523' role='' />\n    <member type='way' ref='397097525' role='' />\n    <member type='way' ref='30758272' role='' />\n    <member type='way' ref='397096465' role='' />\n    <member type='way' ref='224373744' role='' />\n    <member type='way' ref='254299114' role='' />\n    <member type='way' ref='397093352' role='' />\n    <member type='way' ref='255178045' role='' />\n    <member type='way' ref='397093355' role='' />\n    <member type='way' ref='8917741' role='' />\n    <member type='way' ref='485458382' role='' />\n    <member type='way' ref='515593377' role='' />\n    <member type='way' ref='255178137' role='' />\n    <member type='way' ref='397166909' role='' />\n    <member type='way' ref='397166908' role='' />\n    <member type='way' ref='254968964' role='' />\n    <member type='way' ref='514904203' role='' />\n    <member type='way' ref='153227162' role='' />\n    <member type='way' ref='27041779' role='' />\n    <member type='way' ref='417389062' role='' />\n    <member type='way' ref='397120485' role='' />\n    <member type='way' ref='254299130' role='' />\n    <member type='way' ref='515583838' role='' />\n    <member type='way' ref='417353053' role='' />\n    <member type='way' ref='224373754' role='' />\n    <member type='way' ref='417353044' role='' />\n    <member type='way' ref='417353049' role='' />\n    <member type='way' ref='48201560' role='' />\n    <member type='way' ref='254299134' role='' />\n    <member type='way' ref='418522265' role='' />\n    <member type='way' ref='254299111' role='' />\n    <member type='way' ref='8915024' role='' />\n    <member type='way' ref='514615513' role='' />\n    <member type='way' ref='183198843' role='' />\n    <member type='way' ref='48190018' role='' />\n    <member type='way' ref='48190017' role='' />\n    <member type='way' ref='254299123' role='' />\n    <member type='way' ref='28326948' role='' />\n    <member type='way' ref='33645895' role='' />\n    <member type='way' ref='123741441' role='' />\n    <member type='way' ref='27183377' role='' />\n    <member type='way' ref='27183379' role='' />\n    <member type='way' ref='478345678' role='' />\n    <member type='way' ref='617895234' role='' />\n    <member type='way' ref='31129765' role='' />\n    <member type='way' ref='617730081' role='' />\n    <member type='way' ref='123741465' role='' />\n    <member type='way' ref='8915117' role='' />\n    <member type='way' ref='8915115' role='' />\n    <member type='way' ref='254299118' role='' />\n    <member type='way' ref='254308548' role='' />\n    <member type='way' ref='513705407' role='' />\n    <member type='way' ref='186400365' role='' />\n    <member type='way' ref='143666207' role='' />\n    <member type='way' ref='417396772' role='' />\n    <member type='way' ref='620169772' role='' />\n    <member type='way' ref='417396768' role='' />\n    <member type='way' ref='254299142' role='' />\n    <member type='way' ref='397104880' role='' />\n    <member type='way' ref='397144264' role='' />\n    <member type='way' ref='397144265' role='' />\n    <member type='way' ref='397144001' role='' />\n    <member type='way' ref='111552058' role='' />\n    <member type='way' ref='255330068' role='' />\n    <member type='way' ref='88597544' role='' />\n    <member type='way' ref='417396858' role='' />\n    <member type='way' ref='417166168' role='' />\n    <member type='way' ref='224316520' role='' />\n    <member type='way' ref='406944778' role='' />\n    <member type='way' ref='254297759' role='' />\n    <member type='way' ref='397269729' role='' />\n    <member type='way' ref='408575054' role='' />\n    <member type='way' ref='397269730' role='' />\n    <member type='way' ref='406944779' role='' />\n    <member type='way' ref='224316521' role='' />\n    <member type='way' ref='425516647' role='' />\n    <member type='way' ref='406944780' role='' />\n    <member type='way' ref='91182062' role='' />\n    <member type='way' ref='417099335' role='' />\n    <member type='way' ref='417099340' role='' />\n    <member type='way' ref='397094227' role='' />\n    <member type='way' ref='408572568' role='' />\n    <member type='way' ref='91182064' role='' />\n    <tag k='from' v='Schwerin St &amp; Geneva Ave' />\n    <tag k='name' v='8AX-Bayshore A Express: Inbound to North Beach' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='8AX' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Kearny St &amp; Broadway St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3406809' timestamp='2018-08-24T19:08:39Z' uid='8107451' user='njtbusfan' version='44' changeset='61968343'>\n    <member type='way' ref='29059461' role='' />\n    <member type='way' ref='87376703' role='' />\n    <member type='way' ref='514675663' role='' />\n    <member type='way' ref='406580281' role='' />\n    <member type='way' ref='87376679' role='' />\n    <member type='way' ref='87376682' role='' />\n    <member type='way' ref='87376695' role='' />\n    <member type='way' ref='588724745' role='' />\n    <member type='way' ref='397146714' role='' />\n    <member type='way' ref='397146709' role='' />\n    <member type='way' ref='397146710' role='' />\n    <member type='way' ref='397146715' role='' />\n    <member type='way' ref='397146708' role='' />\n    <member type='way' ref='28843615' role='' />\n    <member type='way' ref='397146712' role='' />\n    <member type='way' ref='397146711' role='' />\n    <member type='way' ref='417099351' role='' />\n    <member type='way' ref='417099350' role='' />\n    <member type='way' ref='8921866' role='' />\n    <member type='way' ref='417099347' role='' />\n    <member type='way' ref='28841121' role='' />\n    <member type='way' ref='415726782' role='' />\n    <member type='way' ref='417094961' role='' />\n    <member type='way' ref='254297761' role='' />\n    <member type='way' ref='254758128' role='' />\n    <member type='way' ref='254448247' role='' />\n    <member type='way' ref='85789165' role='' />\n    <member type='way' ref='85789166' role='' />\n    <member type='way' ref='88572895' role='' />\n    <member type='way' ref='417396816' role='' />\n    <member type='way' ref='397127922' role='' />\n    <member type='way' ref='397127921' role='' />\n    <member type='way' ref='408591443' role='' />\n    <member type='way' ref='214545530' role='' />\n    <member type='way' ref='513704346' role='' />\n    <member type='way' ref='513704345' role='' />\n    <member type='way' ref='143617585' role='' />\n    <member type='way' ref='513704344' role='' />\n    <member type='way' ref='254305033' role='' />\n    <member type='way' ref='539195909' role='' />\n    <member type='way' ref='8915588' role='' />\n    <member type='way' ref='27397440' role='' />\n    <member type='way' ref='394443199' role='' />\n    <member type='way' ref='254299122' role='' />\n    <member type='way' ref='154966358' role='' />\n    <member type='way' ref='26943964' role='' />\n    <member type='way' ref='58841187' role='' />\n    <member type='way' ref='27183366' role='' />\n    <member type='way' ref='397267120' role='' />\n    <member type='way' ref='123741422' role='' />\n    <member type='way' ref='28326944' role='' />\n    <member type='way' ref='28326941' role='' />\n    <member type='way' ref='48037053' role='' />\n    <member type='way' ref='48037051' role='' />\n    <member type='way' ref='395418632' role='' />\n    <member type='way' ref='23925474' role='' />\n    <member type='way' ref='515583838' role='' />\n    <member type='way' ref='254299130' role='' />\n    <member type='way' ref='397120485' role='' />\n    <member type='way' ref='417389062' role='' />\n    <member type='way' ref='27041779' role='' />\n    <member type='way' ref='153227162' role='' />\n    <member type='way' ref='514904203' role='' />\n    <member type='way' ref='254968964' role='' />\n    <member type='way' ref='397166908' role='' />\n    <member type='way' ref='397166909' role='' />\n    <member type='way' ref='255178137' role='' />\n    <member type='way' ref='515593377' role='' />\n    <member type='way' ref='469105571' role='' />\n    <member type='way' ref='469105570' role='' />\n    <member type='way' ref='254299115' role='' />\n    <member type='way' ref='283530828' role='' />\n    <member type='way' ref='397093353' role='' />\n    <member type='way' ref='254299116' role='' />\n    <member type='way' ref='397096384' role='' />\n    <member type='way' ref='224373745' role='' />\n    <member type='way' ref='397159153' role='' />\n    <member type='way' ref='8938086' role='' />\n    <member type='way' ref='224373756' role='' />\n    <member type='way' ref='224373755' role='' />\n    <member type='way' ref='619535769' role='' />\n    <tag k='from' v='Kearny St &amp; Broadway St' />\n    <tag k='name' v='8AX-Bayshore A Express: Outbound to Visitacion Valley' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='8AX' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Schwerin St &amp; Geneva Ave' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3406811' timestamp='2018-08-24T21:19:57Z' uid='8107451' user='njtbusfan' version='62' changeset='61971818'>\n    <member type='node' ref='4286837354' role='stop' />\n    <member type='node' ref='5790972437' role='stop' />\n    <member type='way' ref='254299124' role='' />\n    <member type='way' ref='399163465' role='' />\n    <member type='way' ref='254982299' role='' />\n    <member type='way' ref='254299128' role='' />\n    <member type='way' ref='30030485' role='' />\n    <member type='way' ref='148846998' role='' />\n    <member type='way' ref='415591483' role='' />\n    <member type='way' ref='148846995' role='' />\n    <member type='way' ref='415197400' role='' />\n    <member type='way' ref='148846996' role='' />\n    <member type='way' ref='206629822' role='' />\n    <member type='way' ref='28841114' role='' />\n    <member type='way' ref='406580282' role='' />\n    <member type='way' ref='397146714' role='' />\n    <member type='way' ref='397146709' role='' />\n    <member type='way' ref='397146710' role='' />\n    <member type='way' ref='397146715' role='' />\n    <member type='way' ref='397146708' role='' />\n    <member type='way' ref='28843615' role='' />\n    <member type='way' ref='397146712' role='' />\n    <member type='way' ref='397146711' role='' />\n    <member type='way' ref='417099351' role='' />\n    <member type='way' ref='417099350' role='' />\n    <member type='way' ref='8921866' role='' />\n    <member type='way' ref='417099347' role='' />\n    <member type='way' ref='28841121' role='' />\n    <member type='way' ref='415726782' role='' />\n    <member type='way' ref='417094961' role='' />\n    <member type='way' ref='254297761' role='' />\n    <member type='way' ref='254758128' role='' />\n    <member type='way' ref='254448247' role='' />\n    <member type='way' ref='85789165' role='' />\n    <member type='way' ref='85789166' role='' />\n    <member type='way' ref='88572895' role='' />\n    <member type='way' ref='417396816' role='' />\n    <member type='way' ref='397127922' role='' />\n    <member type='way' ref='397127921' role='' />\n    <member type='way' ref='408591443' role='' />\n    <member type='way' ref='214545530' role='' />\n    <member type='way' ref='513704346' role='' />\n    <member type='way' ref='513704345' role='' />\n    <member type='way' ref='143617585' role='' />\n    <member type='way' ref='513704344' role='' />\n    <member type='way' ref='254305033' role='' />\n    <member type='way' ref='539195909' role='' />\n    <member type='way' ref='8915588' role='' />\n    <member type='way' ref='27397440' role='' />\n    <member type='way' ref='394443199' role='' />\n    <member type='way' ref='254299122' role='' />\n    <member type='way' ref='154966358' role='' />\n    <member type='way' ref='26943964' role='' />\n    <member type='way' ref='58841187' role='' />\n    <member type='way' ref='27183366' role='' />\n    <member type='way' ref='397267120' role='' />\n    <member type='way' ref='123741422' role='' />\n    <member type='way' ref='28326944' role='' />\n    <member type='way' ref='28326941' role='' />\n    <member type='way' ref='48037053' role='' />\n    <member type='way' ref='48037051' role='' />\n    <member type='way' ref='48036602' role='' />\n    <member type='way' ref='48036600' role='' />\n    <member type='way' ref='48582245' role='' />\n    <member type='way' ref='48037096' role='' />\n    <member type='way' ref='123741461' role='' />\n    <member type='way' ref='48191417' role='' />\n    <member type='way' ref='48101177' role='' />\n    <member type='way' ref='8919083' role='' />\n    <member type='way' ref='525066119' role='' />\n    <member type='way' ref='397094264' role='' />\n    <member type='way' ref='397094263' role='' />\n    <member type='way' ref='397094265' role='' />\n    <member type='way' ref='254308546' role='' />\n    <member type='way' ref='397115548' role='' />\n    <member type='way' ref='515859661' role='' />\n    <member type='way' ref='397115312' role='' />\n    <member type='way' ref='133755885' role='' />\n    <member type='way' ref='27041812' role='' />\n    <member type='way' ref='254299115' role='' />\n    <member type='way' ref='283530828' role='' />\n    <member type='way' ref='397093353' role='' />\n    <member type='way' ref='254299138' role='' />\n    <member type='way' ref='254396723' role='' />\n    <member type='way' ref='286471135' role='' />\n    <member type='way' ref='255351736' role='' />\n    <member type='way' ref='255178156' role='' />\n    <member type='way' ref='254299119' role='' />\n    <member type='way' ref='254299137' role='' />\n    <member type='way' ref='8922884' role='' />\n    <member type='way' ref='464936238' role='' />\n    <member type='way' ref='513694377' role='' />\n    <member type='way' ref='417958443' role='' />\n    <member type='way' ref='513694378' role='' />\n    <member type='way' ref='417958437' role='' />\n    <member type='way' ref='123660737' role='' />\n    <member type='way' ref='417958440' role='' />\n    <member type='way' ref='8918118' role='' />\n    <member type='way' ref='417389716' role='' />\n    <member type='way' ref='397106013' role='' />\n    <member type='way' ref='123660741' role='' />\n    <member type='way' ref='255178076' role='' />\n    <member type='way' ref='419643707' role='' />\n    <member type='way' ref='397106015' role='' />\n    <member type='way' ref='254984067' role='' />\n    <member type='way' ref='419643710' role='' />\n    <member type='way' ref='464926131' role='' />\n    <member type='way' ref='30758162' role='' />\n    <member type='way' ref='106963928' role='' />\n    <member type='way' ref='254968939' role='' />\n    <member type='way' ref='243834646' role='' />\n    <member type='way' ref='408610939' role='' />\n    <member type='way' ref='286436511' role='' />\n    <member type='way' ref='417389860' role='' />\n    <member type='way' ref='514550014' role='' />\n    <member type='way' ref='514550021' role='' />\n    <member type='way' ref='243834647' role='' />\n    <member type='way' ref='243834648' role='' />\n    <member type='way' ref='619568402' role='' />\n    <member type='way' ref='397138671' role='' />\n    <member type='way' ref='30642925' role='' />\n    <member type='way' ref='159154464' role='' />\n    <member type='way' ref='264872662' role='' />\n    <member type='way' ref='397138663' role='' />\n    <member type='way' ref='397138668' role='' />\n    <member type='way' ref='479587333' role='' />\n    <member type='way' ref='255178078' role='' />\n    <member type='way' ref='133748397' role='' />\n    <member type='way' ref='286440418' role='' />\n    <member type='way' ref='182076317' role='' />\n    <member type='way' ref='222619426' role='' />\n    <tag k='from' v='Kearny St &amp; North Point St' />\n    <tag k='name' v='8BX-Bayshore B Express: Outbound to City College' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='8BX' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Phelan Loop' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3406812' timestamp='2014-01-04T23:41:06Z' uid='119881' user='clay_c' version='2' changeset='19815429'>\n    <member type='relation' ref='3406809' role='' />\n    <member type='relation' ref='3406808' role='' />\n    <tag k='name' v='8AX-Bayshore A Express' />\n    <tag k='nextbus:agency' v='sf-muni' />\n    <tag k='nextbus:route' v='8AX' />\n    <tag k='ref' v='8AX' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='3406813' timestamp='2014-01-04T23:41:06Z' uid='119881' user='clay_c' version='2' changeset='19815429'>\n    <member type='relation' ref='3406811' role='' />\n    <member type='relation' ref='3406810' role='' />\n    <tag k='name' v='8BX-Bayshore B Express' />\n    <tag k='nextbus:agency' v='sf-muni' />\n    <tag k='nextbus:route' v='8BX' />\n    <tag k='ref' v='8BX' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='3418923' timestamp='2018-10-15T22:03:42Z' uid='2237750' user='chachafish' version='10' changeset='63557245'>\n    <member type='way' ref='143617587' role='' />\n    <member type='way' ref='442572413' role='' />\n    <member type='way' ref='442572412' role='' />\n    <member type='way' ref='442572411' role='' />\n    <member type='way' ref='442572410' role='' />\n    <member type='way' ref='133755923' role='' />\n    <member type='way' ref='84823308' role='' />\n    <member type='way' ref='221434104' role='' />\n    <member type='way' ref='221434100' role='' />\n    <member type='way' ref='620728093' role='' />\n    <member type='way' ref='221434102' role='' />\n    <member type='way' ref='8919269' role='' />\n    <member type='way' ref='162931810' role='' />\n    <member type='way' ref='256787755' role='' />\n    <member type='way' ref='91181769' role='' />\n    <member type='way' ref='88572943' role='' />\n    <tag k='from' v='4th St &amp; Townsend St' />\n    <tag k='name' v='83X-Mid-Market Express: Inbound to Levi Plaza' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='83X' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='9th St &amp; Market St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3418929' timestamp='2017-10-06T16:48:42Z' uid='621202' user='dpaschich' version='3' changeset='52688409'>\n    <member type='relation' ref='3418923' role='' />\n    <member type='relation' ref='3418924' role='' />\n    <tag k='name' v='83X-Mid-Market Express' />\n    <tag k='nextbus:agency' v='sf-muni' />\n    <tag k='nextbus:route' v='83X' />\n    <tag k='ref' v='83X' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='3435871' timestamp='2018-11-04T17:00:53Z' uid='202256' user='Telecas' version='144' changeset='64171818'>\n    <member type='way' ref='8928947' role='' />\n    <member type='way' ref='417319227' role='' />\n    <member type='way' ref='417319220' role='' />\n    <member type='way' ref='417319223' role='' />\n    <member type='way' ref='417319239' role='' />\n    <member type='way' ref='417319234' role='' />\n    <member type='way' ref='417319230' role='' />\n    <member type='way' ref='24221265' role='' />\n    <member type='way' ref='27834183' role='' />\n    <member type='way' ref='417319020' role='' />\n    <member type='way' ref='256787728' role='' />\n    <member type='way' ref='573802806' role='' />\n    <member type='way' ref='44421256' role='' />\n    <member type='way' ref='44421287' role='' />\n    <member type='way' ref='417319029' role='' />\n    <member type='way' ref='417319033' role='' />\n    <member type='way' ref='44421274' role='' />\n    <member type='way' ref='521161685' role='' />\n    <member type='way' ref='512187695' role='' />\n    <member type='way' ref='417319203' role='' />\n    <member type='way' ref='417319193' role='' />\n    <member type='way' ref='574421368' role='' />\n    <member type='way' ref='417328512' role='' />\n    <member type='way' ref='574421372' role='' />\n    <member type='way' ref='573476112' role='' />\n    <member type='way' ref='417354884' role='' />\n    <member type='way' ref='138100411' role='' />\n    <member type='way' ref='394700563' role='' />\n    <member type='way' ref='394695156' role='' />\n    <member type='way' ref='394695155' role='' />\n    <member type='way' ref='394700562' role='' />\n    <member type='way' ref='394697894' role='' />\n    <member type='way' ref='394697893' role='' />\n    <member type='way' ref='417348853' role='' />\n    <member type='way' ref='417348788' role='' />\n    <member type='way' ref='417348778' role='' />\n    <member type='way' ref='417348861' role='' />\n    <member type='way' ref='417348759' role='' />\n    <member type='way' ref='417348768' role='' />\n    <member type='way' ref='417348821' role='' />\n    <member type='way' ref='585780593' role='' />\n    <member type='way' ref='44417446' role='' />\n    <member type='way' ref='417348799' role='' />\n    <member type='way' ref='417348829' role='' />\n    <member type='way' ref='417348811' role='' />\n    <member type='way' ref='256400260' role='' />\n    <member type='way' ref='44418616' role='' />\n    <member type='way' ref='573515705' role='' />\n    <member type='way' ref='417303235' role='' />\n    <member type='way' ref='417303234' role='' />\n    <member type='way' ref='417303236' role='' />\n    <member type='way' ref='573513245' role='' />\n    <member type='way' ref='573513249' role='' />\n    <member type='way' ref='417303240' role='' />\n    <member type='way' ref='417303232' role='' />\n    <member type='way' ref='44418614' role='' />\n    <member type='way' ref='256400258' role='' />\n    <member type='way' ref='573436117' role='' />\n    <member type='way' ref='8935439' role='' />\n    <member type='way' ref='417329841' role='' />\n    <member type='way' ref='417329830' role='' />\n    <member type='way' ref='417329835' role='' />\n    <member type='way' ref='8937955' role='' />\n    <member type='way' ref='417333995' role='' />\n    <member type='way' ref='417334010' role='' />\n    <member type='way' ref='59031701' role='' />\n    <member type='way' ref='59031688' role='' />\n    <member type='way' ref='417329854' role='' />\n    <member type='way' ref='47517428' role='' />\n    <member type='way' ref='8927704' role='' />\n    <member type='way' ref='573561314' role='' />\n    <member type='way' ref='573561296' role='' />\n    <member type='way' ref='573412029' role='' />\n    <member type='way' ref='417332700' role='' />\n    <member type='way' ref='417332681' role='' />\n    <member type='way' ref='417332660' role='' />\n    <member type='way' ref='417332729' role='' />\n    <member type='way' ref='417332722' role='' />\n    <member type='way' ref='417332693' role='' />\n    <member type='way' ref='417332639' role='' />\n    <member type='way' ref='417332735' role='' />\n    <member type='way' ref='417332670' role='' />\n    <member type='way' ref='417976722' role='' />\n    <member type='way' ref='417332645' role='' />\n    <member type='way' ref='417332687' role='' />\n    <member type='way' ref='481782722' role='' />\n    <member type='way' ref='31496447' role='' />\n    <member type='way' ref='417332651' role='' />\n    <member type='way' ref='340591732' role='' />\n    <member type='way' ref='417332710' role='' />\n    <member type='way' ref='417332716' role='' />\n    <member type='way' ref='340591731' role='' />\n    <member type='way' ref='573423460' role='' />\n    <member type='way' ref='417976723' role='' />\n    <member type='way' ref='42437920' role='' />\n    <member type='way' ref='256400245' role='' />\n    <member type='way' ref='417976721' role='' />\n    <member type='way' ref='124770911' role='' />\n    <member type='way' ref='309683049' role='' />\n    <member type='way' ref='513664442' role='' />\n    <member type='way' ref='392522882' role='' />\n    <member type='way' ref='51395371' role='' />\n    <member type='way' ref='518495985' role='' />\n    <member type='way' ref='96010179' role='' />\n    <member type='way' ref='417964204' role='' />\n    <member type='way' ref='417964198' role='' />\n    <member type='way' ref='396970740' role='' />\n    <member type='way' ref='396970732' role='' />\n    <member type='way' ref='417964201' role='' />\n    <member type='way' ref='396970739' role='' />\n    <member type='way' ref='396970737' role='' />\n    <member type='way' ref='396970699' role='' />\n    <member type='way' ref='131508247' role='' />\n    <member type='way' ref='396970725' role='' />\n    <member type='way' ref='396970734' role='' />\n    <member type='way' ref='396970708' role='' />\n    <member type='way' ref='396970741' role='' />\n    <member type='way' ref='396970728' role='' />\n    <member type='way' ref='396970727' role='' />\n    <member type='way' ref='396970729' role='' />\n    <member type='way' ref='396970731' role='' />\n    <member type='way' ref='417964200' role='' />\n    <member type='way' ref='396970738' role='' />\n    <member type='way' ref='587089719' role='' />\n    <member type='way' ref='587089718' role='' />\n    <member type='way' ref='396970716' role='' />\n    <member type='way' ref='396970703' role='' />\n    <member type='way' ref='50523280' role='' />\n    <member type='way' ref='517255693' role='' />\n    <member type='way' ref='410019752' role='' />\n    <member type='way' ref='517255694' role='' />\n    <member type='way' ref='256400261' role='' />\n    <member type='way' ref='389133512' role='' />\n    <member type='way' ref='396967418' role='' />\n    <member type='way' ref='396967415' role='' />\n    <member type='way' ref='396967423' role='' />\n    <member type='way' ref='396967413' role='' />\n    <member type='way' ref='396967420' role='' />\n    <member type='way' ref='409947876' role='' />\n    <member type='way' ref='8941432' role='' />\n    <member type='way' ref='406944784' role='' />\n    <member type='way' ref='165682405' role='' />\n    <member type='way' ref='256787740' role='' />\n    <member type='way' ref='93671776' role='' />\n    <member type='way' ref='370279528' role='' />\n    <member type='way' ref='256787743' role='' />\n    <member type='way' ref='370279599' role='' />\n    <member type='way' ref='38855245' role='' />\n    <member type='way' ref='38855344' role='' />\n    <member type='way' ref='512267983' role='' />\n    <member type='way' ref='38855347' role='' />\n    <member type='way' ref='370279632' role='' />\n    <member type='way' ref='93671846' role='' />\n    <member type='way' ref='370279522' role='' />\n    <member type='way' ref='256787746' role='' />\n    <member type='way' ref='165682403' role='' />\n    <member type='way' ref='417340368' role='' />\n    <member type='way' ref='417340355' role='' />\n    <member type='way' ref='256787730' role='' />\n    <member type='way' ref='417340374' role='' />\n    <member type='way' ref='417340364' role='' />\n    <member type='way' ref='417340361' role='' />\n    <member type='way' ref='417341645' role='' />\n    <member type='way' ref='417341640' role='' />\n    <member type='way' ref='417341659' role='' />\n    <member type='way' ref='417341638' role='' />\n    <member type='way' ref='417955390' role='' />\n    <member type='way' ref='417341661' role='' />\n    <member type='way' ref='417341641' role='' />\n    <member type='way' ref='417955387' role='' />\n    <member type='way' ref='417341643' role='' />\n    <member type='way' ref='266522061' role='' />\n    <member type='way' ref='417955389' role='' />\n    <member type='way' ref='417341653' role='' />\n    <member type='way' ref='417341639' role='' />\n    <member type='way' ref='417341663' role='' />\n    <member type='way' ref='417341647' role='' />\n    <member type='way' ref='417341656' role='' />\n    <member type='way' ref='417341642' role='' />\n    <member type='way' ref='396959635' role='' />\n    <member type='way' ref='417955388' role='' />\n    <member type='way' ref='396959640' role='' />\n    <member type='way' ref='396959638' role='' />\n    <member type='way' ref='396959636' role='' />\n    <member type='way' ref='396959641' role='' />\n    <member type='way' ref='396959639' role='' />\n    <member type='way' ref='396959649' role='' />\n    <member type='way' ref='396959637' role='' />\n    <member type='way' ref='165572790' role='' />\n    <member type='way' ref='396959634' role='' />\n    <member type='way' ref='396954370' role='' />\n    <member type='way' ref='165572801' role='' />\n    <member type='way' ref='396954373' role='' />\n    <member type='way' ref='165572799' role='' />\n    <member type='way' ref='619310159' role='' />\n    <member type='way' ref='289753824' role='' />\n    <member type='way' ref='289753826' role='' />\n    <member type='way' ref='396954385' role='' />\n    <member type='way' ref='396954378' role='' />\n    <member type='way' ref='396954375' role='' />\n    <member type='way' ref='396954374' role='' />\n    <member type='way' ref='165572796' role='' />\n    <member type='way' ref='417974881' role='' />\n    <member type='way' ref='165572798' role='' />\n    <member type='way' ref='417974870' role='' />\n    <member type='way' ref='396952972' role='' />\n    <member type='way' ref='396952973' role='' />\n    <member type='way' ref='513965702' role='' />\n    <member type='way' ref='513965701' role='' />\n    <member type='way' ref='165572795' role='' />\n    <member type='way' ref='396952971' role='' />\n    <member type='way' ref='396952970' role='' />\n    <member type='way' ref='396952976' role='' />\n    <member type='way' ref='396952969' role='' />\n    <member type='way' ref='417974871' role='' />\n    <member type='way' ref='165572792' role='' />\n    <member type='way' ref='396952968' role='' />\n    <member type='way' ref='157595326' role='' />\n    <member type='way' ref='396978746' role='' />\n    <member type='way' ref='8928561' role='' />\n    <member type='way' ref='396978745' role='' />\n    <member type='way' ref='217040733' role='' />\n    <member type='way' ref='396980821' role='' />\n    <member type='way' ref='157595330' role='' />\n    <member type='way' ref='396980013' role='' />\n    <member type='way' ref='256400242' role='' />\n    <member type='way' ref='217023614' role='' />\n    <member type='way' ref='217023611' role='' />\n    <member type='way' ref='396974118' role='' />\n    <member type='way' ref='396973568' role='' />\n    <member type='way' ref='396973569' role='' />\n    <member type='way' ref='396973101' role='' />\n    <member type='way' ref='406930552' role='' />\n    <member type='way' ref='406932558' role='' />\n    <member type='way' ref='406932557' role='' />\n    <member type='way' ref='396973102' role='' />\n    <member type='way' ref='396978628' role='' />\n    <member type='way' ref='396977657' role='' />\n    <member type='way' ref='396977662' role='' />\n    <member type='way' ref='396977647' role='' />\n    <member type='way' ref='396977652' role='' />\n    <member type='way' ref='397098334' role='' />\n    <member type='way' ref='397098333' role='' />\n    <member type='way' ref='309857708' role='' />\n    <member type='way' ref='397098418' role='' />\n    <member type='way' ref='397098419' role='' />\n    <member type='way' ref='397098776' role='' />\n    <member type='way' ref='397098844' role='' />\n    <member type='way' ref='397098843' role='' />\n    <member type='way' ref='397098979' role='' />\n    <member type='way' ref='515603103' role='' />\n    <member type='way' ref='397161975' role='' />\n    <member type='way' ref='397098978' role='' />\n    <member type='way' ref='217020420' role='' />\n    <member type='way' ref='396989457' role='' />\n    <member type='way' ref='514677012' role='' />\n    <member type='way' ref='397093229' role='' />\n    <member type='way' ref='396989456' role='' />\n    <member type='way' ref='217020442' role='' />\n    <member type='way' ref='397159762' role='' />\n    <member type='way' ref='397159752' role='' />\n    <member type='way' ref='217020417' role='' />\n    <member type='way' ref='217020553' role='' />\n    <member type='way' ref='417201974' role='' />\n    <member type='way' ref='417201980' role='' />\n    <member type='way' ref='217020552' role='' />\n    <member type='way' ref='415506697' role='' />\n    <member type='way' ref='417201983' role='' />\n    <member type='way' ref='415505962' role='' />\n    <member type='way' ref='123690575' role='' />\n    <member type='way' ref='397097443' role='' />\n    <member type='way' ref='415504889' role='' />\n    <member type='way' ref='138102027' role='' />\n    <member type='way' ref='397096336' role='' />\n    <member type='way' ref='397096345' role='' />\n    <member type='way' ref='397096354' role='' />\n    <member type='way' ref='123690556' role='' />\n    <member type='way' ref='309857690' role='' />\n    <member type='way' ref='397094035' role='' />\n    <member type='way' ref='397094032' role='' />\n    <member type='way' ref='417197059' role='' />\n    <member type='way' ref='397094031' role='' />\n    <member type='way' ref='417197078' role='' />\n    <member type='way' ref='397094033' role='' />\n    <member type='way' ref='417197097' role='' />\n    <member type='way' ref='417197116' role='' />\n    <member type='way' ref='397094034' role='' />\n    <member type='way' ref='397094336' role='' />\n    <member type='way' ref='534655195' role='' />\n    <member type='way' ref='397094519' role='' />\n    <member type='way' ref='534655201' role='' />\n    <member type='way' ref='397098496' role='' />\n    <member type='way' ref='417197038' role='' />\n    <member type='way' ref='309857692' role='' />\n    <member type='way' ref='397097526' role='' />\n    <member type='way' ref='133755888' role='' />\n    <member type='way' ref='133755883' role='' />\n    <member type='way' ref='8919551' role='' />\n    <member type='way' ref='397097523' role='' />\n    <member type='way' ref='397097525' role='' />\n    <member type='way' ref='30758272' role='' />\n    <member type='way' ref='397096465' role='' />\n    <member type='way' ref='224373744' role='' />\n    <member type='way' ref='254299114' role='' />\n    <member type='way' ref='397093352' role='' />\n    <member type='way' ref='255178045' role='' />\n    <member type='way' ref='397093355' role='' />\n    <member type='way' ref='224373743' role='' />\n    <member type='way' ref='515859660' role='' />\n    <member type='way' ref='397115308' role='' />\n    <member type='way' ref='513966285' role='' />\n    <member type='way' ref='27041808' role='' />\n    <member type='way' ref='415497724' role='' />\n    <member type='way' ref='397115300' role='' />\n    <member type='way' ref='61558775' role='' />\n    <member type='way' ref='27652472' role='' />\n    <member type='way' ref='397121962' role='' />\n    <member type='way' ref='27613598' role='' />\n    <member type='way' ref='27613597' role='' />\n    <member type='way' ref='549278887' role='' />\n    <member type='way' ref='27019938' role='' />\n    <member type='way' ref='417388593' role='' />\n    <member type='way' ref='397121963' role='' />\n    <member type='way' ref='397121964' role='' />\n    <member type='way' ref='397120476' role='' />\n    <member type='way' ref='469387973' role='' />\n    <member type='way' ref='224373742' role='' />\n    <member type='way' ref='397120478' role='' />\n    <member type='way' ref='397120479' role='' />\n    <member type='way' ref='397120484' role='' />\n    <member type='way' ref='397120482' role='' />\n    <member type='way' ref='397120483' role='' />\n    <member type='way' ref='397120480' role='' />\n    <member type='way' ref='397120481' role='' />\n    <member type='way' ref='397120477' role='' />\n    <member type='way' ref='418522258' role='' />\n    <member type='way' ref='469387974' role='' />\n    <member type='way' ref='469387975' role='' />\n    <member type='way' ref='469387976' role='' />\n    <member type='way' ref='23925325' role='' />\n    <member type='way' ref='406944760' role='' />\n    <member type='way' ref='397094082' role='' />\n    <member type='way' ref='186400363' role='' />\n    <member type='way' ref='225516691' role='' />\n    <member type='way' ref='469387979' role='' />\n    <member type='way' ref='397094088' role='' />\n    <member type='way' ref='397094768' role='' />\n    <member type='way' ref='397094767' role='' />\n    <member type='way' ref='513693876' role='' />\n    <member type='way' ref='225516682' role='' />\n    <member type='way' ref='397266204' role='' />\n    <member type='way' ref='469387980' role='' />\n    <member type='way' ref='225854641' role='' />\n    <member type='way' ref='397271282' role='' />\n    <member type='way' ref='397271281' role='' />\n    <member type='way' ref='8919576' role='' />\n    <member type='way' ref='525013804' role='' />\n    <member type='way' ref='133781814' role='' />\n    <member type='way' ref='8915027' role='' />\n    <member type='way' ref='483931210' role='' />\n    <member type='way' ref='43101729' role='' />\n    <member type='way' ref='397138962' role='' />\n    <member type='way' ref='397138961' role='' />\n    <member type='way' ref='23884749' role='' />\n    <member type='way' ref='24138759' role='' />\n    <member type='way' ref='397138960' role='' />\n    <member type='way' ref='224384023' role='' />\n    <member type='way' ref='397125841' role='' />\n    <member type='way' ref='224384022' role='' />\n    <member type='way' ref='283535051' role='' />\n    <member type='way' ref='415470419' role='' />\n    <member type='way' ref='283535236' role='' />\n    <member type='way' ref='513698622' role='' />\n    <member type='way' ref='397125828' role='' />\n    <member type='way' ref='397125829' role='' />\n    <member type='way' ref='397125834' role='' />\n    <member type='way' ref='397125830' role='' />\n    <member type='way' ref='397125838' role='' />\n    <member type='way' ref='397125839' role='' />\n    <member type='way' ref='397125835' role='' />\n    <member type='way' ref='417392979' role='' />\n    <member type='way' ref='397125840' role='' />\n    <member type='way' ref='24138763' role='' />\n    <member type='way' ref='397130840' role='' />\n    <member type='way' ref='397130841' role='' />\n    <member type='way' ref='110797422' role='' />\n    <member type='way' ref='397130497' role='' />\n    <member type='way' ref='397130093' role='' />\n    <member type='way' ref='397129776' role='' />\n    <member type='way' ref='397131363' role='' />\n    <member type='way' ref='397131364' role='' />\n    <member type='way' ref='397137031' role='' />\n    <member type='way' ref='254750815' role='' />\n    <member type='way' ref='397138005' role='' />\n    <member type='way' ref='397138006' role='' />\n    <member type='way' ref='186400371' role='' />\n    <member type='way' ref='417392975' role='' />\n    <member type='way' ref='8917337' role='' />\n    <member type='way' ref='162931810' role='' />\n    <member type='way' ref='256787755' role='' />\n    <member type='way' ref='91181769' role='' />\n    <member type='way' ref='417087485' role='' />\n    <member type='way' ref='88572887' role='' />\n    <member type='way' ref='88572914' role='' />\n    <member type='way' ref='88572941' role='' />\n    <member type='way' ref='88572932' role='' />\n    <member type='way' ref='51392400' role='' />\n    <member type='way' ref='51392402' role='' />\n    <member type='way' ref='111560680' role='' />\n    <member type='way' ref='86334994' role='' />\n    <member type='way' ref='88572965' role='' />\n    <member type='way' ref='88559669' role='' />\n    <member type='way' ref='88572877' role='' />\n    <member type='way' ref='417101712' role='' />\n    <member type='way' ref='88572970' role='' />\n    <tag k='from' v='Hillsdale Shopping Center' />\n    <tag k='name' v='SamTrans 292 (northbound)' />\n    <tag k='network' v='SamTrans' />\n    <tag k='operator' v='San Mateo County Transit District' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='292' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Transbay Temporary Terminal' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3435880' timestamp='2014-01-15T03:44:11Z' uid='119881' user='clay_c' version='2' changeset='20006303'>\n    <member type='relation' ref='3435871' role='' />\n    <member type='relation' ref='3435872' role='' />\n    <tag k='name' v='SamTrans 292' />\n    <tag k='operator' v='San Mateo County Transportation District' />\n    <tag k='ref' v='292' />\n    <tag k='route_master' v='bus' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='6088583' timestamp='2018-11-05T17:51:10Z' uid='119881' user='clay_c' version='20' changeset='64205910'>\n    <member type='node' ref='266947168' role='stop' />\n    <member type='node' ref='252024546' role='stop' />\n    <member type='node' ref='266944543' role='stop' />\n    <member type='node' ref='266910712' role='stop' />\n    <member type='node' ref='266910711' role='stop' />\n    <member type='node' ref='2160213362' role='stop' />\n    <member type='node' ref='2160213350' role='stop' />\n    <member type='node' ref='2160213359' role='stop' />\n    <member type='node' ref='2160213352' role='stop' />\n    <member type='node' ref='647651427' role='stop' />\n    <member type='node' ref='30236612' role='stop' />\n    <member type='node' ref='30236493' role='stop' />\n    <member type='node' ref='2160213349' role='stop' />\n    <member type='node' ref='2160213361' role='stop' />\n    <member type='node' ref='2160213354' role='stop' />\n    <member type='node' ref='2160213353' role='stop' />\n    <member type='node' ref='2160213344' role='stop' />\n    <member type='node' ref='2160213357' role='stop' />\n    <member type='node' ref='2160213346' role='stop' />\n    <member type='node' ref='564663926' role='stop' />\n    <member type='node' ref='2160213351' role='stop' />\n    <member type='node' ref='2160213358' role='stop' />\n    <member type='node' ref='2160213348' role='stop' />\n    <member type='node' ref='2160213347' role='stop' />\n    <member type='node' ref='2150077200' role='stop' />\n    <member type='node' ref='2160213360' role='stop' />\n    <member type='node' ref='2160213345' role='stop' />\n    <member type='node' ref='2160213364' role='stop' />\n    <member type='node' ref='3646391919' role='stop' />\n    <member type='node' ref='3646391941' role='stop' />\n    <member type='node' ref='3646391942' role='stop' />\n    <member type='node' ref='3646391943' role='stop' />\n    <member type='node' ref='3646391944' role='stop' />\n    <member type='node' ref='3646391945' role='stop' />\n    <member type='node' ref='3646391946' role='stop' />\n    <member type='node' ref='3721808996' role='stop' />\n    <member type='node' ref='3721808997' role='stop' />\n    <member type='node' ref='312418973' role='stop_position' />\n    <member type='node' ref='3721863783' role='stop_position' />\n    <member type='node' ref='3721890341' role='stop_position' />\n    <member type='node' ref='3721890342' role='stop_position' />\n    <member type='way' ref='119527498' role='backward' />\n    <member type='way' ref='119527512' role='backward' />\n    <member type='way' ref='119527503' role='backward' />\n    <member type='way' ref='119527499' role='backward' />\n    <member type='way' ref='119527511' role='backward' />\n    <member type='way' ref='119527505' role='backward' />\n    <member type='way' ref='119527514' role='backward' />\n    <member type='way' ref='639288347' role='forward' />\n    <member type='way' ref='119527500' role='forward' />\n    <member type='way' ref='119527508' role='forward' />\n    <member type='way' ref='119527513' role='forward' />\n    <member type='way' ref='119527506' role='backward' />\n    <member type='way' ref='119525091' role='backward' />\n    <member type='way' ref='119525085' role='backward' />\n    <member type='way' ref='119525076' role='backward' />\n    <member type='way' ref='639288350' role='backward' />\n    <member type='way' ref='119525081' role='backward' />\n    <member type='way' ref='119525088' role='backward' />\n    <member type='way' ref='119525084' role='backward' />\n    <member type='way' ref='119525079' role='backward' />\n    <member type='way' ref='119525093' role='backward' />\n    <member type='way' ref='119525092' role='backward' />\n    <member type='way' ref='119525086' role='backward' />\n    <member type='way' ref='119525080' role='backward' />\n    <member type='way' ref='119525077' role='backward' />\n    <member type='way' ref='119525075' role='backward' />\n    <member type='way' ref='119525078' role='backward' />\n    <member type='way' ref='119525087' role='backward' />\n    <member type='way' ref='119525074' role='backward' />\n    <member type='way' ref='506797114' role='backward' />\n    <member type='way' ref='639288353' role='backward' />\n    <member type='way' ref='639288352' role='backward' />\n    <member type='way' ref='506797113' role='backward' />\n    <member type='way' ref='119525082' role='backward' />\n    <member type='way' ref='119525083' role='backward' />\n    <member type='way' ref='119274434' role='forward' />\n    <member type='way' ref='119274427' role='forward' />\n    <member type='way' ref='119274430' role='forward' />\n    <member type='way' ref='119352145' role='forward' />\n    <member type='way' ref='119352143' role='backward' />\n    <member type='way' ref='119352142' role='backward' />\n    <member type='way' ref='119391946' role='backward' />\n    <member type='way' ref='368345086' role='backward' />\n    <member type='way' ref='368345085' role='backward' />\n    <member type='way' ref='119391944' role='backward' />\n    <member type='way' ref='206031847' role='forward' />\n    <member type='way' ref='367132250' role='forward' />\n    <member type='way' ref='367132249' role='forward' />\n    <member type='way' ref='206031829' role='forward' />\n    <member type='way' ref='119391941' role='forward' />\n    <member type='way' ref='119408003' role='backward' />\n    <member type='way' ref='119408002' role='forward' />\n    <member type='way' ref='639288356' role='forward' />\n    <member type='way' ref='639288354' role='backward' />\n    <member type='way' ref='119408004' role='backward' />\n    <member type='way' ref='639288359' role='forward' />\n    <member type='way' ref='119407561' role='forward' />\n    <member type='way' ref='639288361' role='forward' />\n    <member type='way' ref='119407995' role='forward' />\n    <member type='way' ref='206031822' role='backward' />\n    <member type='way' ref='119420076' role='forward' />\n    <member type='way' ref='119420067' role='forward' />\n    <member type='way' ref='119420084' role='forward' />\n    <member type='way' ref='119420071' role='forward' />\n    <member type='way' ref='119420077' role='forward' />\n    <member type='way' ref='119420081' role='forward' />\n    <member type='way' ref='459757523' role='forward' />\n    <member type='way' ref='353731500' role='forward' />\n    <member type='way' ref='119420088' role='forward' />\n    <member type='way' ref='639288363' role='forward' />\n    <member type='way' ref='213371621' role='forward' />\n    <member type='way' ref='540107872' role='forward' />\n    <member type='way' ref='540107873' role='forward' />\n    <member type='way' ref='42479145' role='forward' />\n    <member type='way' ref='639288368' role='forward' />\n    <member type='way' ref='42479151' role='' />\n    <member type='way' ref='411194873' role='' />\n    <member type='way' ref='213371623' role='' />\n    <member type='way' ref='316902613' role='' />\n    <member type='way' ref='354329683' role='' />\n    <member type='way' ref='639288386' role='' />\n    <member type='way' ref='28431980' role='' />\n    <member type='way' ref='119514255' role='' />\n    <member type='way' ref='119514252' role='' />\n    <member type='way' ref='119514264' role='' />\n    <member type='way' ref='206031844' role='' />\n    <member type='way' ref='206031825' role='' />\n    <member type='way' ref='206031831' role='' />\n    <member type='way' ref='206031848' role='' />\n    <member type='way' ref='206031843' role='' />\n    <member type='way' ref='28433401' role='' />\n    <member type='way' ref='28433400' role='' />\n    <member type='way' ref='175042624' role='' />\n    <member type='way' ref='175042623' role='' />\n    <member type='way' ref='28433438' role='' />\n    <member type='way' ref='28433442' role='' />\n    <member type='way' ref='206031833' role='' />\n    <member type='way' ref='206031853' role='backward' />\n    <member type='way' ref='641310738' role='backward' />\n    <member type='way' ref='639288379' role='backward' />\n    <member type='way' ref='206031836' role='forward' />\n    <member type='way' ref='206031834' role='' />\n    <member type='way' ref='206031839' role='' />\n    <member type='way' ref='26275242' role='' />\n    <member type='way' ref='639288371' role='' />\n    <member type='way' ref='316893345' role='' />\n    <member type='way' ref='639288370' role='' />\n    <member type='way' ref='316893356' role='' />\n    <member type='way' ref='639218776' role='' />\n    <member type='way' ref='406323588' role='' />\n    <member type='way' ref='316893349' role='' />\n    <member type='way' ref='639288381' role='' />\n    <member type='way' ref='554029486' role='' />\n    <member type='way' ref='316893363' role='' />\n    <member type='way' ref='283523737' role='' />\n    <member type='way' ref='283523740' role='' />\n    <member type='way' ref='639288373' role='' />\n    <member type='way' ref='324897089' role='' />\n    <member type='way' ref='324897091' role='' />\n    <member type='way' ref='639288374' role='' />\n    <member type='way' ref='639288382' role='' />\n    <member type='way' ref='120259635' role='' />\n    <member type='way' ref='608569202' role='forward' />\n    <member type='way' ref='639288279' role='forward' />\n    <member type='way' ref='639288277' role='forward' />\n    <member type='way' ref='110353426' role='forward' />\n    <member type='way' ref='639288278' role='forward' />\n    <member type='way' ref='8920472' role='forward' />\n    <member type='way' ref='125738663' role='forward' />\n    <member type='way' ref='125738664' role='forward' />\n    <member type='way' ref='26780771' role='forward' />\n    <member type='way' ref='23937685' role='forward' />\n    <member type='way' ref='27614456' role='forward' />\n    <member type='way' ref='27614455' role='forward' />\n    <member type='way' ref='61601587' role='forward' />\n    <member type='way' ref='61601588' role='forward' />\n    <member type='way' ref='27614412' role='forward' />\n    <member type='way' ref='27614413' role='forward' />\n    <member type='way' ref='39070563' role='forward' />\n    <member type='way' ref='39070561' role='forward' />\n    <member type='way' ref='27614350' role='forward' />\n    <member type='way' ref='27614351' role='forward' />\n    <member type='way' ref='8920487' role='forward' />\n    <member type='way' ref='23937630' role='forward' />\n    <member type='way' ref='389800282' role='forward' />\n    <member type='way' ref='389800280' role='forward' />\n    <member type='way' ref='8920480' role='forward' />\n    <member type='way' ref='8920316' role='forward' />\n    <member type='way' ref='639288288' role='backward' />\n    <member type='way' ref='119627694' role='backward' />\n    <member type='way' ref='639288326' role='backward' />\n    <member type='way' ref='32199046' role='backward' />\n    <member type='way' ref='639288308' role='backward' />\n    <member type='way' ref='639288299' role='backward' />\n    <member type='way' ref='8926837' role='backward' />\n    <member type='way' ref='23946554' role='forward' />\n    <member type='way' ref='639288336' role='forward' />\n    <member type='way' ref='639288334' role='forward' />\n    <member type='way' ref='259275843' role='forward' />\n    <member type='way' ref='259275849' role='forward' />\n    <member type='way' ref='259275845' role='forward' />\n    <member type='way' ref='259275851' role='forward' />\n    <member type='way' ref='639288340' role='forward' />\n    <member type='way' ref='639288338' role='forward' />\n    <member type='way' ref='385950999' role='forward' />\n    <member type='way' ref='385951001' role='forward' />\n    <member type='way' ref='24446834' role='forward' />\n    <member type='way' ref='24446833' role='forward' />\n    <member type='way' ref='24446837' role='forward' />\n    <member type='way' ref='24446838' role='forward' />\n    <member type='way' ref='24446848' role='forward' />\n    <member type='way' ref='24446847' role='forward' />\n    <member type='way' ref='24446861' role='forward' />\n    <member type='way' ref='639288344' role='forward' />\n    <member type='way' ref='24446860' role='forward' />\n    <member type='way' ref='119527504' role='forward' />\n    <member type='way' ref='119527510' role='forward' />\n    <member type='way' ref='44421264' role='forward' />\n    <member type='way' ref='44421265' role='forward' />\n    <member type='way' ref='43835697' role='forward' />\n    <member type='way' ref='43835695' role='forward' />\n    <member type='way' ref='639288349' role='forward' />\n    <member type='way' ref='43835699' role='forward' />\n    <member type='way' ref='59329491' role='forward' />\n    <member type='way' ref='59329477' role='forward' />\n    <member type='way' ref='43835705' role='forward' />\n    <member type='way' ref='59329481' role='forward' />\n    <member type='way' ref='59329483' role='forward' />\n    <member type='way' ref='59329465' role='forward' />\n    <member type='way' ref='59329462' role='forward' />\n    <member type='way' ref='59329469' role='forward' />\n    <member type='way' ref='59329452' role='forward' />\n    <member type='way' ref='59329454' role='forward' />\n    <member type='way' ref='59329456' role='forward' />\n    <member type='way' ref='47688463' role='forward' />\n    <member type='way' ref='506797115' role='forward' />\n    <member type='way' ref='639288351' role='forward' />\n    <member type='way' ref='506797116' role='forward' />\n    <member type='way' ref='34396089' role='forward' />\n    <member type='way' ref='34396090' role='forward' />\n    <member type='way' ref='8926810' role='forward' />\n    <member type='way' ref='44129793' role='backward' />\n    <member type='way' ref='44129792' role='backward' />\n    <member type='way' ref='8926825' role='backward' />\n    <member type='way' ref='8936043' role='forward' />\n    <member type='way' ref='66209396' role='forward' />\n    <member type='way' ref='368345084' role='forward' />\n    <member type='way' ref='368345083' role='forward' />\n    <member type='way' ref='27422567' role='forward' />\n    <member type='way' ref='32315631' role='backward' />\n    <member type='way' ref='367132252' role='backward' />\n    <member type='way' ref='367132251' role='backward' />\n    <member type='way' ref='32315626' role='backward' />\n    <member type='way' ref='32315575' role='backward' />\n    <member type='way' ref='8936124' role='forward' />\n    <member type='way' ref='51324837' role='backward' />\n    <member type='way' ref='639288357' role='backward' />\n    <member type='way' ref='639288355' role='forward' />\n    <member type='way' ref='18409986' role='forward' />\n    <member type='way' ref='639288358' role='forward' />\n    <member type='way' ref='206031841' role='forward' />\n    <member type='way' ref='206031826' role='backward' />\n    <member type='way' ref='639288360' role='backward' />\n    <member type='way' ref='38883727' role='backward' />\n    <member type='way' ref='38883735' role='backward' />\n    <member type='way' ref='28485280' role='backward' />\n    <member type='way' ref='28485281' role='backward' />\n    <member type='way' ref='38883723' role='backward' />\n    <member type='way' ref='38883722' role='backward' />\n    <member type='way' ref='8936023' role='backward' />\n    <member type='way' ref='459757519' role='backward' />\n    <member type='way' ref='119420072' role='forward' />\n    <member type='way' ref='639288362' role='forward' />\n    <member type='way' ref='8936148' role='forward' />\n    <member type='way' ref='42477721' role='forward' />\n    <member type='way' ref='540107871' role='forward' />\n    <member type='way' ref='540107870' role='forward' />\n    <member type='way' ref='206031824' role='forward' />\n    <member type='way' ref='206031842' role='forward' />\n    <member type='way' ref='37311641' role='backward' />\n    <member type='way' ref='206031832' role='backward' />\n    <member type='way' ref='608569201' role='backward' />\n    <member type='way' ref='639288276' role='backward' />\n    <member type='way' ref='119709582' role='backward' />\n    <member type='way' ref='639288274' role='backward' />\n    <member type='way' ref='119709558' role='backward' />\n    <member type='way' ref='125738662' role='backward' />\n    <member type='way' ref='125738665' role='backward' />\n    <member type='way' ref='119709579' role='backward' />\n    <member type='way' ref='119709567' role='backward' />\n    <member type='way' ref='119709589' role='backward' />\n    <member type='way' ref='119709584' role='backward' />\n    <member type='way' ref='119709562' role='backward' />\n    <member type='way' ref='119709561' role='backward' />\n    <member type='way' ref='119709573' role='backward' />\n    <member type='way' ref='119709557' role='backward' />\n    <member type='way' ref='119709578' role='backward' />\n    <member type='way' ref='119709566' role='backward' />\n    <member type='way' ref='119709587' role='backward' />\n    <member type='way' ref='119709569' role='backward' />\n    <member type='way' ref='119709596' role='backward' />\n    <member type='way' ref='119709575' role='backward' />\n    <member type='way' ref='389800281' role='backward' />\n    <member type='way' ref='389800279' role='backward' />\n    <member type='way' ref='119627699' role='backward' />\n    <member type='way' ref='119627709' role='backward' />\n    <member type='way' ref='639288280' role='forward' />\n    <member type='way' ref='119627702' role='forward' />\n    <member type='way' ref='639288330' role='forward' />\n    <member type='way' ref='119539640' role='forward' />\n    <member type='way' ref='639288304' role='forward' />\n    <member type='way' ref='639288293' role='forward' />\n    <member type='way' ref='119538866' role='forward' />\n    <member type='way' ref='259275850' role='backward' />\n    <member type='way' ref='639288335' role='backward' />\n    <member type='way' ref='639288333' role='backward' />\n    <member type='way' ref='259275842' role='backward' />\n    <member type='way' ref='259275846' role='backward' />\n    <member type='way' ref='259275848' role='backward' />\n    <member type='way' ref='385951002' role='backward' />\n    <member type='way' ref='385951000' role='backward' />\n    <member type='way' ref='639288339' role='backward' />\n    <member type='way' ref='639288341' role='backward' />\n    <member type='way' ref='119527502' role='backward' />\n    <member type='way' ref='30694882' role='forward' />\n    <member type='way' ref='28295229' role='stop' />\n    <member type='way' ref='213371654' role='' />\n    <member type='way' ref='641125035' role='stop' />\n    <member type='way' ref='188661968' role='stop' />\n    <member type='way' ref='119507157' role='' />\n    <member type='way' ref='354329695' role='' />\n    <member type='way' ref='540035964' role='' />\n    <member type='way' ref='540035965' role='platform' />\n    <member type='way' ref='301707409' role='stop' />\n    <tag k='colour' v='yellow' />\n    <tag k='name' v='Limited' />\n    <tag k='network' v='Caltrain' />\n    <tag k='operator' v='TransitAmerica Services' />\n    <tag k='operator:wikidata' v='Q166817' />\n    <tag k='operator:wikipedia' v='en:Caltrain' />\n    <tag k='passenger' v='suburban' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='206-289' />\n    <tag k='route' v='train' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='6435331' timestamp='2018-08-24T21:20:21Z' uid='8107451' user='njtbusfan' version='5' changeset='61971818'>\n    <member type='way' ref='8919269' role='street' />\n    <member type='way' ref='255330071' role='street' />\n    <member type='way' ref='255330072' role='street' />\n    <member type='way' ref='470208976' role='street' />\n    <member type='way' ref='256787755' role='street' />\n    <member type='way' ref='88572943' role='street' />\n    <member type='way' ref='619568403' role='street' />\n    <member type='node' ref='4318582010' role='house' />\n    <tag k='name' v='9th Street' />\n    <tag k='type' v='associatedStreet' />\n  </relation>\n  <relation id='6703053' timestamp='2018-11-05T17:51:11Z' uid='119881' user='clay_c' version='15' changeset='64205910'>\n    <member type='way' ref='608569202' role='' />\n    <member type='way' ref='639288279' role='' />\n    <member type='way' ref='639288277' role='' />\n    <member type='way' ref='110353426' role='' />\n    <member type='way' ref='639288278' role='' />\n    <member type='way' ref='8920472' role='' />\n    <member type='way' ref='125738663' role='' />\n    <member type='way' ref='125738664' role='' />\n    <member type='way' ref='26780771' role='' />\n    <member type='way' ref='23937685' role='' />\n    <member type='way' ref='27614456' role='' />\n    <member type='way' ref='27614455' role='' />\n    <member type='way' ref='61601587' role='' />\n    <member type='way' ref='61601588' role='' />\n    <member type='way' ref='27614412' role='' />\n    <member type='way' ref='27614413' role='' />\n    <member type='way' ref='39070563' role='' />\n    <member type='way' ref='39070561' role='' />\n    <member type='way' ref='27614350' role='' />\n    <member type='way' ref='27614351' role='' />\n    <member type='way' ref='8920487' role='' />\n    <member type='way' ref='23937630' role='' />\n    <member type='way' ref='389800282' role='' />\n    <member type='way' ref='389800280' role='' />\n    <member type='way' ref='8920480' role='' />\n    <member type='way' ref='8920316' role='' />\n    <member type='way' ref='206031838' role='' />\n    <member type='way' ref='639288281' role='' />\n    <member type='way' ref='206031845' role='' />\n    <member type='way' ref='32199046' role='' />\n    <member type='way' ref='639288308' role='' />\n    <member type='way' ref='639288299' role='' />\n    <member type='way' ref='8926837' role='' />\n    <member type='way' ref='23946554' role='' />\n    <member type='way' ref='639288336' role='' />\n    <member type='way' ref='639288334' role='' />\n    <member type='way' ref='259275843' role='' />\n    <member type='way' ref='259275849' role='' />\n    <member type='way' ref='259275845' role='' />\n    <member type='way' ref='259275851' role='' />\n    <member type='way' ref='639288340' role='' />\n    <member type='way' ref='639288338' role='' />\n    <member type='way' ref='385950999' role='' />\n    <member type='way' ref='385951001' role='' />\n    <member type='way' ref='24446834' role='' />\n    <member type='way' ref='24446833' role='' />\n    <member type='way' ref='24446837' role='' />\n    <member type='way' ref='24446838' role='' />\n    <member type='way' ref='24446848' role='' />\n    <member type='way' ref='24446847' role='' />\n    <member type='way' ref='24446861' role='' />\n    <member type='way' ref='639288344' role='' />\n    <member type='way' ref='24446860' role='' />\n    <member type='way' ref='119527504' role='' />\n    <member type='way' ref='119527510' role='' />\n    <member type='way' ref='44421264' role='' />\n    <member type='way' ref='44421265' role='' />\n    <member type='way' ref='43835697' role='' />\n    <member type='way' ref='43835695' role='' />\n    <member type='way' ref='639288349' role='' />\n    <member type='way' ref='43835699' role='' />\n    <member type='way' ref='59329491' role='' />\n    <member type='way' ref='59329477' role='' />\n    <member type='way' ref='43835705' role='' />\n    <member type='way' ref='59329481' role='' />\n    <member type='way' ref='59329483' role='' />\n    <member type='way' ref='59329465' role='' />\n    <member type='way' ref='59329462' role='' />\n    <member type='way' ref='59329469' role='' />\n    <member type='way' ref='59329452' role='' />\n    <member type='way' ref='59329454' role='' />\n    <member type='way' ref='59329456' role='' />\n    <member type='way' ref='47688463' role='' />\n    <member type='way' ref='506797115' role='' />\n    <member type='way' ref='639288351' role='' />\n    <member type='way' ref='506797116' role='' />\n    <member type='way' ref='34396089' role='' />\n    <member type='way' ref='34396090' role='' />\n    <member type='way' ref='8926810' role='' />\n    <member type='way' ref='44129793' role='' />\n    <member type='way' ref='44129792' role='' />\n    <member type='way' ref='8926825' role='' />\n    <member type='way' ref='8936043' role='' />\n    <member type='way' ref='66209396' role='' />\n    <member type='way' ref='368345084' role='' />\n    <member type='way' ref='368345083' role='' />\n    <member type='way' ref='27422567' role='' />\n    <member type='way' ref='367132251' role='' />\n    <member type='way' ref='367132252' role='' />\n    <member type='way' ref='32315631' role='' />\n    <member type='way' ref='32315626' role='' />\n    <member type='way' ref='32315575' role='' />\n    <member type='way' ref='8936124' role='' />\n    <member type='way' ref='51324837' role='' />\n    <member type='way' ref='639288357' role='' />\n    <member type='way' ref='639288355' role='' />\n    <member type='way' ref='18409986' role='' />\n    <member type='way' ref='119407994' role='' />\n    <member type='way' ref='38883727' role='' />\n    <member type='way' ref='38883735' role='' />\n    <member type='way' ref='28485280' role='' />\n    <member type='way' ref='28485281' role='' />\n    <member type='way' ref='38883723' role='' />\n    <member type='way' ref='38883722' role='' />\n    <member type='way' ref='459757519' role='' />\n    <member type='way' ref='8936023' role='' />\n    <member type='way' ref='119420072' role='' />\n    <member type='way' ref='639288362' role='' />\n    <member type='way' ref='8936148' role='' />\n    <member type='way' ref='42477721' role='' />\n    <member type='way' ref='540107871' role='' />\n    <member type='way' ref='540107870' role='' />\n    <member type='way' ref='206031824' role='' />\n    <member type='way' ref='206031842' role='' />\n    <member type='way' ref='639288368' role='' />\n    <member type='way' ref='42479145' role='' />\n    <member type='way' ref='540107873' role='' />\n    <member type='way' ref='540107872' role='' />\n    <member type='way' ref='213371621' role='' />\n    <member type='way' ref='639288363' role='' />\n    <member type='way' ref='119420088' role='' />\n    <member type='way' ref='353731500' role='' />\n    <member type='way' ref='119420081' role='' />\n    <member type='way' ref='459757523' role='' />\n    <member type='way' ref='119420077' role='' />\n    <member type='way' ref='119420071' role='' />\n    <member type='way' ref='119420084' role='' />\n    <member type='way' ref='119420067' role='' />\n    <member type='way' ref='119420076' role='' />\n    <member type='way' ref='206031822' role='' />\n    <member type='way' ref='119407555' role='' />\n    <member type='way' ref='206031840' role='' />\n    <member type='way' ref='119408004' role='' />\n    <member type='way' ref='639288354' role='' />\n    <member type='way' ref='639288356' role='' />\n    <member type='way' ref='119408002' role='' />\n    <member type='way' ref='119408003' role='' />\n    <member type='way' ref='119391941' role='' />\n    <member type='way' ref='206031829' role='' />\n    <member type='way' ref='206031847' role='' />\n    <member type='way' ref='367132250' role='' />\n    <member type='way' ref='367132249' role='' />\n    <member type='way' ref='119391944' role='' />\n    <member type='way' ref='368345085' role='' />\n    <member type='way' ref='368345086' role='' />\n    <member type='way' ref='119391946' role='' />\n    <member type='way' ref='119352142' role='' />\n    <member type='way' ref='119352143' role='' />\n    <member type='way' ref='119352145' role='' />\n    <member type='way' ref='119274430' role='' />\n    <member type='way' ref='119274427' role='' />\n    <member type='way' ref='119274434' role='' />\n    <member type='way' ref='119525083' role='' />\n    <member type='way' ref='119525082' role='' />\n    <member type='way' ref='506797113' role='' />\n    <member type='way' ref='639288352' role='' />\n    <member type='way' ref='639288353' role='' />\n    <member type='way' ref='506797114' role='' />\n    <member type='way' ref='119525074' role='' />\n    <member type='way' ref='119525087' role='' />\n    <member type='way' ref='119525078' role='' />\n    <member type='way' ref='119525075' role='' />\n    <member type='way' ref='119525077' role='' />\n    <member type='way' ref='119525080' role='' />\n    <member type='way' ref='119525086' role='' />\n    <member type='way' ref='119525092' role='' />\n    <member type='way' ref='119525093' role='' />\n    <member type='way' ref='119525079' role='' />\n    <member type='way' ref='119525084' role='' />\n    <member type='way' ref='119525088' role='' />\n    <member type='way' ref='119525081' role='' />\n    <member type='way' ref='639288350' role='' />\n    <member type='way' ref='119525076' role='' />\n    <member type='way' ref='119525085' role='' />\n    <member type='way' ref='119525091' role='' />\n    <member type='way' ref='119527506' role='' />\n    <member type='way' ref='119527513' role='' />\n    <member type='way' ref='119527508' role='' />\n    <member type='way' ref='119527500' role='' />\n    <member type='way' ref='639288347' role='' />\n    <member type='way' ref='119527514' role='' />\n    <member type='way' ref='119527505' role='' />\n    <member type='way' ref='119527511' role='' />\n    <member type='way' ref='119527499' role='' />\n    <member type='way' ref='119527503' role='' />\n    <member type='way' ref='119527512' role='' />\n    <member type='way' ref='119527498' role='' />\n    <member type='way' ref='385951002' role='' />\n    <member type='way' ref='385951000' role='' />\n    <member type='way' ref='639288339' role='' />\n    <member type='way' ref='639288341' role='' />\n    <member type='way' ref='119527502' role='' />\n    <member type='way' ref='259275848' role='' />\n    <member type='way' ref='259275846' role='' />\n    <member type='way' ref='259275842' role='' />\n    <member type='way' ref='639288333' role='' />\n    <member type='way' ref='639288335' role='' />\n    <member type='way' ref='259275850' role='' />\n    <member type='way' ref='119538866' role='' />\n    <member type='way' ref='639288293' role='' />\n    <member type='way' ref='639288304' role='' />\n    <member type='way' ref='119539640' role='' />\n    <member type='way' ref='206031846' role='' />\n    <member type='way' ref='639288284' role='' />\n    <member type='way' ref='206031837' role='' />\n    <member type='way' ref='119627709' role='' />\n    <member type='way' ref='119627699' role='' />\n    <member type='way' ref='389800279' role='' />\n    <member type='way' ref='389800281' role='' />\n    <member type='way' ref='119709575' role='' />\n    <member type='way' ref='119709596' role='' />\n    <member type='way' ref='119709569' role='' />\n    <member type='way' ref='119709587' role='' />\n    <member type='way' ref='119709566' role='' />\n    <member type='way' ref='119709578' role='' />\n    <member type='way' ref='119709557' role='' />\n    <member type='way' ref='119709573' role='' />\n    <member type='way' ref='119709561' role='' />\n    <member type='way' ref='119709562' role='' />\n    <member type='way' ref='119709584' role='' />\n    <member type='way' ref='119709589' role='' />\n    <member type='way' ref='119709567' role='' />\n    <member type='way' ref='119709579' role='' />\n    <member type='way' ref='125738665' role='' />\n    <member type='way' ref='125738662' role='' />\n    <member type='way' ref='119709558' role='' />\n    <member type='way' ref='639288274' role='' />\n    <member type='way' ref='119709582' role='' />\n    <member type='way' ref='639288276' role='' />\n    <member type='way' ref='608569201' role='' />\n    <member type='way' ref='206031832' role='' />\n    <member type='way' ref='37311641' role='' />\n    <member type='way' ref='42479151' role='' />\n    <member type='way' ref='411194873' role='' />\n    <member type='way' ref='30694882' role='' />\n    <member type='way' ref='213371623' role='' />\n    <member type='way' ref='316902613' role='' />\n    <member type='way' ref='354329683' role='' />\n    <member type='way' ref='639288386' role='' />\n    <member type='way' ref='28431980' role='' />\n    <member type='way' ref='119514255' role='' />\n    <member type='way' ref='119514252' role='' />\n    <member type='way' ref='119514264' role='' />\n    <member type='way' ref='206031844' role='' />\n    <member type='way' ref='206031825' role='' />\n    <member type='way' ref='206031831' role='' />\n    <member type='way' ref='206031848' role='' />\n    <member type='way' ref='206031843' role='' />\n    <member type='way' ref='28433401' role='' />\n    <member type='way' ref='28433400' role='' />\n    <member type='way' ref='175042624' role='' />\n    <member type='way' ref='175042623' role='' />\n    <member type='way' ref='28433438' role='' />\n    <member type='way' ref='28433442' role='' />\n    <member type='way' ref='206031833' role='' />\n    <member type='way' ref='206031853' role='' />\n    <member type='way' ref='641310738' role='' />\n    <member type='way' ref='639288379' role='' />\n    <member type='way' ref='206031836' role='' />\n    <member type='way' ref='206031834' role='' />\n    <member type='way' ref='206031839' role='' />\n    <member type='way' ref='26275242' role='' />\n    <member type='way' ref='639288371' role='' />\n    <member type='way' ref='316893345' role='' />\n    <member type='way' ref='639288370' role='' />\n    <member type='way' ref='316893356' role='' />\n    <member type='way' ref='639218776' role='' />\n    <member type='way' ref='406323588' role='' />\n    <member type='way' ref='316893349' role='' />\n    <member type='way' ref='639288381' role='' />\n    <member type='way' ref='316893363' role='' />\n    <member type='way' ref='554029486' role='' />\n    <member type='way' ref='283523737' role='' />\n    <member type='way' ref='283523740' role='' />\n    <member type='way' ref='639288373' role='' />\n    <member type='way' ref='324897089' role='' />\n    <member type='way' ref='324897091' role='' />\n    <member type='way' ref='639288374' role='' />\n    <member type='way' ref='639288382' role='' />\n    <member type='way' ref='120259635' role='' />\n    <tag k='name' v='Caltrain' />\n    <tag k='route' v='railway' />\n    <tag k='type' v='route' />\n    <tag k='wikidata' v='Q166817' />\n  </relation>\n  <relation id='7461515' timestamp='2017-08-10T21:05:49Z' uid='2508151' user='ridixcr' version='1' changeset='51015043'>\n    <member type='way' ref='215528940' role='from' />\n    <member type='way' ref='397250673' role='to' />\n    <member type='node' ref='65303732' role='via' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='7739486' timestamp='2018-10-30T06:11:06Z' uid='119881' user='clay_c' version='22' changeset='64000226'>\n    <member type='node' ref='5348315584' role='stop' />\n    <member type='node' ref='5347821024' role='stop' />\n    <member type='node' ref='5347913621' role='stop' />\n    <member type='node' ref='5347947260' role='stop' />\n    <member type='node' ref='5347886375' role='stop' />\n    <member type='node' ref='3646391919' role='stop' />\n    <member type='node' ref='2150077200' role='stop' />\n    <member type='way' ref='542375290' role='stop' />\n    <member type='way' ref='42479151' role='' />\n    <member type='way' ref='411194873' role='' />\n    <member type='way' ref='213371623' role='' />\n    <member type='way' ref='316902613' role='' />\n    <member type='way' ref='354329683' role='' />\n    <member type='way' ref='639288386' role='' />\n    <member type='way' ref='28431980' role='' />\n    <member type='way' ref='119514255' role='' />\n    <member type='way' ref='119514252' role='' />\n    <member type='way' ref='119514264' role='' />\n    <member type='way' ref='206031844' role='' />\n    <member type='way' ref='206031825' role='' />\n    <member type='way' ref='206031831' role='' />\n    <member type='way' ref='206031848' role='' />\n    <member type='way' ref='206031843' role='' />\n    <member type='way' ref='28433401' role='' />\n    <member type='way' ref='28433400' role='' />\n    <member type='way' ref='175042624' role='' />\n    <member type='way' ref='175042623' role='' />\n    <member type='way' ref='28433438' role='' />\n    <member type='way' ref='28433442' role='' />\n    <member type='way' ref='206031833' role='' />\n    <member type='way' ref='206031836' role='' />\n    <member type='way' ref='206031834' role='' />\n    <member type='way' ref='206031839' role='' />\n    <member type='way' ref='26275242' role='' />\n    <member type='way' ref='639288371' role='' />\n    <member type='way' ref='316893345' role='' />\n    <member type='way' ref='639288370' role='' />\n    <member type='way' ref='316893356' role='' />\n    <member type='way' ref='639218776' role='' />\n    <member type='way' ref='406323588' role='' />\n    <member type='way' ref='316893349' role='' />\n    <member type='way' ref='639288381' role='' />\n    <member type='way' ref='158796412' role='' />\n    <member type='way' ref='316893347' role='' />\n    <member type='way' ref='639218777' role='' />\n    <member type='way' ref='316893357' role='' />\n    <member type='way' ref='316893344' role='' />\n    <member type='way' ref='639288372' role='' />\n    <member type='way' ref='316893360' role='' />\n    <member type='way' ref='316893354' role='' />\n    <member type='way' ref='316893348' role='' />\n    <member type='way' ref='206031835' role='' />\n    <member type='way' ref='175042627' role='' />\n    <member type='way' ref='175042625' role='' />\n    <member type='way' ref='175042626' role='' />\n    <member type='way' ref='175042628' role='' />\n    <member type='way' ref='175042622' role='' />\n    <member type='way' ref='28433405' role='' />\n    <member type='way' ref='28433406' role='' />\n    <member type='way' ref='114767246' role='' />\n    <member type='way' ref='114767245' role='' />\n    <member type='way' ref='42479235' role='' />\n    <member type='way' ref='42479236' role='' />\n    <member type='way' ref='170970665' role='' />\n    <member type='way' ref='316893363' role='' />\n    <member type='way' ref='554029487' role='' />\n    <member type='way' ref='561917668' role='' />\n    <member type='way' ref='561917667' role='' />\n    <member type='way' ref='561917663' role='' />\n    <member type='way' ref='561917658' role='' />\n    <member type='way' ref='561917659' role='' />\n    <member type='way' ref='561917661' role='' />\n    <member type='way' ref='561917657' role='' />\n    <member type='way' ref='561917662' role='' />\n    <member type='way' ref='561917664' role='' />\n    <member type='way' ref='561917665' role='' />\n    <member type='way' ref='561917666' role='' />\n    <member type='way' ref='543907291' role='' />\n    <member type='way' ref='335854807' role='' />\n    <member type='way' ref='543907292' role='' />\n    <member type='way' ref='543907293' role='' />\n    <member type='way' ref='543907294' role='' />\n    <member type='way' ref='543907297' role='' />\n    <member type='way' ref='543907296' role='' />\n    <member type='way' ref='543907295' role='' />\n    <member type='way' ref='543908401' role='' />\n    <member type='way' ref='543908402' role='' />\n    <member type='way' ref='335854749' role='' />\n    <member type='way' ref='554103637' role='' />\n    <member type='way' ref='554171636' role='' />\n    <member type='way' ref='554171635' role='' />\n    <member type='way' ref='554171633' role='' />\n    <member type='way' ref='554171634' role='' />\n    <member type='way' ref='554171632' role='' />\n    <member type='way' ref='554171631' role='' />\n    <member type='way' ref='554171630' role='' />\n    <member type='way' ref='554171629' role='' />\n    <member type='way' ref='554171628' role='' />\n    <member type='way' ref='554171627' role='' />\n    <member type='way' ref='554171626' role='' />\n    <member type='way' ref='554170898' role='' />\n    <member type='way' ref='554170899' role='' />\n    <member type='way' ref='554170897' role='' />\n    <member type='way' ref='639288277' role='' />\n    <member type='way' ref='110353426' role='' />\n    <member type='way' ref='639288278' role='' />\n    <member type='way' ref='8920472' role='' />\n    <member type='way' ref='125738663' role='' />\n    <member type='way' ref='125738664' role='' />\n    <member type='way' ref='26780771' role='' />\n    <member type='way' ref='23937685' role='' />\n    <member type='way' ref='27614456' role='' />\n    <member type='way' ref='27614455' role='' />\n    <member type='way' ref='61601587' role='' />\n    <member type='way' ref='61601588' role='' />\n    <member type='way' ref='27614412' role='' />\n    <member type='way' ref='27614413' role='' />\n    <member type='way' ref='39070563' role='' />\n    <member type='way' ref='39070561' role='' />\n    <member type='way' ref='27614350' role='' />\n    <member type='way' ref='27614351' role='' />\n    <member type='way' ref='8920487' role='' />\n    <member type='way' ref='23937630' role='' />\n    <member type='way' ref='389800282' role='' />\n    <member type='way' ref='389800280' role='' />\n    <member type='way' ref='8920480' role='' />\n    <member type='way' ref='8920316' role='' />\n    <member type='way' ref='206031838' role='' />\n    <member type='way' ref='639288281' role='' />\n    <member type='way' ref='206031845' role='' />\n    <member type='way' ref='32199046' role='' />\n    <member type='way' ref='639288308' role='' />\n    <member type='way' ref='639288299' role='' />\n    <member type='way' ref='8926837' role='' />\n    <member type='way' ref='23946554' role='' />\n    <member type='way' ref='639288336' role='' />\n    <member type='way' ref='639288334' role='' />\n    <member type='way' ref='259275843' role='' />\n    <member type='way' ref='259275849' role='' />\n    <member type='way' ref='259275845' role='' />\n    <member type='way' ref='259275851' role='' />\n    <member type='way' ref='639288340' role='' />\n    <member type='way' ref='639288338' role='' />\n    <member type='way' ref='385950999' role='' />\n    <member type='way' ref='385951001' role='' />\n    <member type='way' ref='24446834' role='' />\n    <member type='way' ref='24446833' role='' />\n    <member type='way' ref='24446837' role='' />\n    <member type='way' ref='24446838' role='' />\n    <member type='way' ref='24446848' role='' />\n    <member type='way' ref='24446847' role='' />\n    <member type='way' ref='24446861' role='' />\n    <member type='way' ref='639288344' role='' />\n    <member type='way' ref='24446860' role='' />\n    <member type='way' ref='119527504' role='' />\n    <member type='way' ref='119527510' role='' />\n    <member type='way' ref='44421264' role='' />\n    <member type='way' ref='44421265' role='' />\n    <member type='way' ref='43835697' role='' />\n    <member type='way' ref='43835695' role='' />\n    <member type='way' ref='639288349' role='' />\n    <member type='way' ref='43835699' role='' />\n    <member type='way' ref='59329491' role='' />\n    <member type='way' ref='59329477' role='' />\n    <member type='way' ref='43835705' role='' />\n    <member type='way' ref='59329481' role='' />\n    <member type='way' ref='59329483' role='' />\n    <member type='way' ref='59329465' role='' />\n    <member type='way' ref='59329462' role='' />\n    <member type='way' ref='59329469' role='' />\n    <member type='way' ref='59329452' role='' />\n    <member type='way' ref='59329454' role='' />\n    <member type='way' ref='59329456' role='' />\n    <member type='way' ref='47688463' role='' />\n    <member type='way' ref='506797115' role='' />\n    <member type='way' ref='639288351' role='' />\n    <member type='way' ref='506797116' role='' />\n    <member type='way' ref='34396089' role='' />\n    <member type='way' ref='34396090' role='' />\n    <member type='way' ref='8926810' role='' />\n    <member type='way' ref='44129793' role='' />\n    <member type='way' ref='44129792' role='' />\n    <member type='way' ref='8926825' role='' />\n    <member type='way' ref='8936043' role='' />\n    <member type='way' ref='66209396' role='' />\n    <member type='way' ref='368345084' role='' />\n    <member type='way' ref='368345083' role='' />\n    <member type='way' ref='27422567' role='' />\n    <member type='way' ref='367132251' role='' />\n    <member type='way' ref='367132252' role='' />\n    <member type='way' ref='32315631' role='' />\n    <member type='way' ref='32315626' role='' />\n    <member type='way' ref='32315575' role='' />\n    <member type='way' ref='8936124' role='' />\n    <member type='way' ref='51324837' role='' />\n    <member type='way' ref='639288357' role='' />\n    <member type='way' ref='639288355' role='' />\n    <member type='way' ref='18409986' role='' />\n    <member type='way' ref='119407994' role='' />\n    <member type='way' ref='38883727' role='' />\n    <member type='way' ref='38883735' role='' />\n    <member type='way' ref='28485280' role='' />\n    <member type='way' ref='28485281' role='' />\n    <member type='way' ref='38883723' role='' />\n    <member type='way' ref='38883722' role='' />\n    <member type='way' ref='459757519' role='' />\n    <member type='way' ref='8936023' role='' />\n    <member type='way' ref='119420072' role='' />\n    <member type='way' ref='639288362' role='' />\n    <member type='way' ref='8936148' role='' />\n    <member type='way' ref='42477721' role='' />\n    <member type='way' ref='540107871' role='' />\n    <member type='way' ref='540107870' role='' />\n    <member type='way' ref='206031824' role='' />\n    <member type='way' ref='206031842' role='' />\n    <member type='way' ref='639288368' role='' />\n    <member type='way' ref='42479145' role='' />\n    <member type='way' ref='540107873' role='' />\n    <member type='way' ref='540107872' role='' />\n    <member type='way' ref='213371621' role='' />\n    <member type='way' ref='639288363' role='' />\n    <member type='way' ref='119420088' role='' />\n    <member type='way' ref='353731500' role='' />\n    <member type='way' ref='119420081' role='' />\n    <member type='way' ref='459757523' role='' />\n    <member type='way' ref='119420077' role='' />\n    <member type='way' ref='119420071' role='' />\n    <member type='way' ref='119420084' role='' />\n    <member type='way' ref='119420067' role='' />\n    <member type='way' ref='119420076' role='' />\n    <member type='way' ref='206031822' role='' />\n    <member type='way' ref='119407555' role='' />\n    <member type='way' ref='206031840' role='' />\n    <member type='way' ref='119408004' role='' />\n    <member type='way' ref='639288354' role='' />\n    <member type='way' ref='639288356' role='' />\n    <member type='way' ref='119408002' role='' />\n    <member type='way' ref='119408003' role='' />\n    <member type='way' ref='119391941' role='' />\n    <member type='way' ref='206031829' role='' />\n    <member type='way' ref='206031847' role='' />\n    <member type='way' ref='367132250' role='' />\n    <member type='way' ref='367132249' role='' />\n    <member type='way' ref='119391944' role='' />\n    <member type='way' ref='368345085' role='' />\n    <member type='way' ref='368345086' role='' />\n    <member type='way' ref='119391946' role='' />\n    <member type='way' ref='119352142' role='' />\n    <member type='way' ref='119352143' role='' />\n    <member type='way' ref='119352145' role='' />\n    <member type='way' ref='119274430' role='' />\n    <member type='way' ref='119274427' role='' />\n    <member type='way' ref='119274434' role='' />\n    <member type='way' ref='119525083' role='' />\n    <member type='way' ref='119525082' role='' />\n    <member type='way' ref='506797113' role='' />\n    <member type='way' ref='639288352' role='' />\n    <member type='way' ref='639288353' role='' />\n    <member type='way' ref='506797114' role='' />\n    <member type='way' ref='119525074' role='' />\n    <member type='way' ref='119525087' role='' />\n    <member type='way' ref='119525078' role='' />\n    <member type='way' ref='119525075' role='' />\n    <member type='way' ref='119525077' role='' />\n    <member type='way' ref='119525080' role='' />\n    <member type='way' ref='119525086' role='' />\n    <member type='way' ref='119525092' role='' />\n    <member type='way' ref='119525093' role='' />\n    <member type='way' ref='119525079' role='' />\n    <member type='way' ref='119525084' role='' />\n    <member type='way' ref='119525088' role='' />\n    <member type='way' ref='119525081' role='' />\n    <member type='way' ref='639288350' role='' />\n    <member type='way' ref='119525076' role='' />\n    <member type='way' ref='119525085' role='' />\n    <member type='way' ref='119525091' role='' />\n    <member type='way' ref='119527506' role='' />\n    <member type='way' ref='119527513' role='' />\n    <member type='way' ref='119527508' role='' />\n    <member type='way' ref='119527500' role='' />\n    <member type='way' ref='639288347' role='' />\n    <member type='way' ref='119527514' role='' />\n    <member type='way' ref='119527505' role='' />\n    <member type='way' ref='119527511' role='' />\n    <member type='way' ref='119527499' role='' />\n    <member type='way' ref='119527503' role='' />\n    <member type='way' ref='119527512' role='' />\n    <member type='way' ref='119527498' role='' />\n    <member type='way' ref='385951002' role='' />\n    <member type='way' ref='385951000' role='' />\n    <member type='way' ref='639288339' role='' />\n    <member type='way' ref='639288341' role='' />\n    <member type='way' ref='119527502' role='' />\n    <member type='way' ref='259275848' role='' />\n    <member type='way' ref='259275846' role='' />\n    <member type='way' ref='259275842' role='' />\n    <member type='way' ref='639288333' role='' />\n    <member type='way' ref='639288335' role='' />\n    <member type='way' ref='259275850' role='' />\n    <member type='way' ref='119538866' role='' />\n    <member type='way' ref='639288293' role='' />\n    <member type='way' ref='639288304' role='' />\n    <member type='way' ref='119539640' role='' />\n    <member type='way' ref='206031846' role='' />\n    <member type='way' ref='639288284' role='' />\n    <member type='way' ref='206031837' role='' />\n    <member type='way' ref='119627709' role='' />\n    <member type='way' ref='119627699' role='' />\n    <member type='way' ref='389800279' role='' />\n    <member type='way' ref='389800281' role='' />\n    <member type='way' ref='119709575' role='' />\n    <member type='way' ref='119709596' role='' />\n    <member type='way' ref='119709569' role='' />\n    <member type='way' ref='119709587' role='' />\n    <member type='way' ref='119709566' role='' />\n    <member type='way' ref='119709578' role='' />\n    <member type='way' ref='119709557' role='' />\n    <member type='way' ref='119709573' role='' />\n    <member type='way' ref='119709561' role='' />\n    <member type='way' ref='119709562' role='' />\n    <member type='way' ref='119709584' role='' />\n    <member type='way' ref='119709589' role='' />\n    <member type='way' ref='119709567' role='' />\n    <member type='way' ref='119709579' role='' />\n    <member type='way' ref='125738665' role='' />\n    <member type='way' ref='125738662' role='' />\n    <member type='way' ref='119709558' role='' />\n    <member type='way' ref='639288274' role='' />\n    <member type='way' ref='119709582' role='' />\n    <member type='way' ref='639288276' role='' />\n    <member type='way' ref='37312232' role='' />\n    <member type='way' ref='48983526' role='' />\n    <tag k='FIXME' v='Needs to fully reflect all bridges, cuttings, embankments, etc. Needs to delete station members.' />\n    <tag k='name' v='California High Speed Rail' />\n    <tag k='note' v='Must be route:railway to describe the infrastructure. Including stations with role stop in this relation is incorrect, however we might temporarily allow it so that another route:train relation can use these elements at beginning of passenger service.' />\n    <tag k='operator' v='California High Speed Rail Authority' />\n    <tag k='route' v='railway' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='7814535' timestamp='2018-03-18T22:01:06Z' uid='53073' user='Aaron Lidman' version='2' changeset='57301995'>\n    <member type='way' ref='547181915' role='outer' />\n    <member type='way' ref='547181914' role='inner' />\n    <tag k='building' v='yes' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n  <relation id='7916804' timestamp='2018-11-05T06:43:43Z' uid='119881' user='clay_c' version='8' changeset='64186583'>\n    <member type='way' ref='37311641' role='' />\n    <member type='way' ref='206031832' role='' />\n    <member type='way' ref='608569201' role='' />\n    <member type='way' ref='639288276' role='' />\n    <member type='way' ref='119709582' role='' />\n    <member type='way' ref='639288274' role='' />\n    <member type='way' ref='639288278' role='' />\n    <member type='way' ref='110353426' role='' />\n    <member type='way' ref='639288277' role='' />\n    <member type='way' ref='639288279' role='' />\n    <member type='way' ref='608569202' role='' />\n    <member type='way' ref='608569194' role='' />\n    <member type='way' ref='639288275' role='' />\n    <member type='way' ref='119709593' role='' />\n    <member type='way' ref='119709592' role='' />\n    <member type='way' ref='8920472' role='' />\n    <member type='way' ref='125738663' role='' />\n    <member type='way' ref='125738664' role='' />\n    <member type='way' ref='26780771' role='' />\n    <member type='way' ref='23937685' role='' />\n    <member type='way' ref='27614456' role='' />\n    <member type='way' ref='27614455' role='' />\n    <member type='way' ref='61601587' role='' />\n    <member type='way' ref='61601588' role='' />\n    <member type='way' ref='27614412' role='' />\n    <member type='way' ref='27614413' role='' />\n    <member type='way' ref='39070563' role='' />\n    <member type='way' ref='39070561' role='' />\n    <member type='way' ref='27614350' role='' />\n    <member type='way' ref='27614351' role='' />\n    <member type='way' ref='8920487' role='' />\n    <member type='way' ref='23937630' role='' />\n    <member type='way' ref='389800282' role='' />\n    <member type='way' ref='389800280' role='' />\n    <member type='way' ref='8920480' role='' />\n    <member type='way' ref='8920316' role='' />\n    <member type='way' ref='206031838' role='' />\n    <member type='way' ref='639288281' role='' />\n    <member type='way' ref='206031845' role='' />\n    <member type='way' ref='32199046' role='' />\n    <member type='way' ref='639288308' role='' />\n    <member type='way' ref='639288299' role='' />\n    <member type='way' ref='8926837' role='' />\n    <member type='way' ref='23946554' role='' />\n    <member type='way' ref='639288336' role='' />\n    <member type='way' ref='639288334' role='' />\n    <member type='way' ref='259275843' role='' />\n    <member type='way' ref='259275849' role='' />\n    <member type='way' ref='259275845' role='' />\n    <member type='way' ref='259275851' role='' />\n    <member type='way' ref='639288340' role='' />\n    <member type='way' ref='639288338' role='' />\n    <member type='way' ref='385950999' role='' />\n    <member type='way' ref='385951001' role='' />\n    <member type='way' ref='24446834' role='' />\n    <member type='way' ref='24446833' role='' />\n    <member type='way' ref='24446837' role='' />\n    <member type='way' ref='24446838' role='' />\n    <member type='way' ref='24446848' role='' />\n    <member type='way' ref='24446847' role='' />\n    <member type='way' ref='24446861' role='' />\n    <member type='way' ref='639288344' role='' />\n    <member type='way' ref='24446860' role='' />\n    <member type='way' ref='119527504' role='' />\n    <member type='way' ref='119527510' role='' />\n    <member type='way' ref='44421264' role='' />\n    <member type='way' ref='44421265' role='' />\n    <member type='way' ref='43835697' role='' />\n    <member type='way' ref='43835695' role='' />\n    <member type='way' ref='639288349' role='' />\n    <member type='way' ref='43835699' role='' />\n    <member type='way' ref='59329491' role='' />\n    <member type='way' ref='59329477' role='' />\n    <member type='way' ref='43835705' role='' />\n    <member type='way' ref='59329481' role='' />\n    <member type='way' ref='59329483' role='' />\n    <member type='way' ref='59329465' role='' />\n    <member type='way' ref='59329462' role='' />\n    <member type='way' ref='59329469' role='' />\n    <member type='way' ref='59329452' role='' />\n    <member type='way' ref='59329454' role='' />\n    <member type='way' ref='59329456' role='' />\n    <member type='way' ref='47688463' role='' />\n    <member type='way' ref='506797115' role='' />\n    <member type='way' ref='639288351' role='' />\n    <member type='way' ref='506797116' role='' />\n    <member type='way' ref='34396089' role='' />\n    <member type='way' ref='34396090' role='' />\n    <member type='way' ref='8926810' role='' />\n    <member type='way' ref='44129793' role='' />\n    <member type='way' ref='44129792' role='' />\n    <member type='way' ref='8926825' role='' />\n    <member type='way' ref='8936043' role='' />\n    <member type='way' ref='66209396' role='' />\n    <member type='way' ref='368345084' role='' />\n    <member type='way' ref='368345083' role='' />\n    <member type='way' ref='27422567' role='' />\n    <member type='way' ref='367132251' role='' />\n    <member type='way' ref='367132252' role='' />\n    <member type='way' ref='32315631' role='' />\n    <member type='way' ref='32315626' role='' />\n    <member type='way' ref='32315575' role='' />\n    <member type='way' ref='8936124' role='' />\n    <member type='way' ref='51324837' role='' />\n    <member type='way' ref='639288357' role='' />\n    <member type='way' ref='639288355' role='' />\n    <member type='way' ref='18409986' role='' />\n    <member type='way' ref='119407994' role='' />\n    <member type='way' ref='38883727' role='' />\n    <member type='way' ref='38883735' role='' />\n    <member type='way' ref='28485280' role='' />\n    <member type='way' ref='28485281' role='' />\n    <member type='way' ref='38883723' role='' />\n    <member type='way' ref='38883722' role='' />\n    <member type='way' ref='459757519' role='' />\n    <member type='way' ref='8936023' role='' />\n    <member type='way' ref='119420072' role='' />\n    <member type='way' ref='639288362' role='' />\n    <member type='way' ref='8936148' role='' />\n    <member type='way' ref='42477721' role='' />\n    <member type='way' ref='540107871' role='' />\n    <member type='way' ref='540107870' role='' />\n    <member type='way' ref='206031824' role='' />\n    <member type='way' ref='30694943' role='' />\n    <member type='way' ref='42479140' role='' />\n    <member type='way' ref='42479142' role='' />\n    <member type='way' ref='641125035' role='' />\n    <member type='way' ref='316902613' role='' />\n    <member type='way' ref='213371623' role='' />\n    <member type='way' ref='411194873' role='' />\n    <member type='way' ref='42479151' role='' />\n    <member type='way' ref='639288368' role='' />\n    <member type='way' ref='42479145' role='' />\n    <member type='way' ref='540107873' role='' />\n    <member type='way' ref='540107872' role='' />\n    <member type='way' ref='213371621' role='' />\n    <member type='way' ref='639288363' role='' />\n    <member type='way' ref='119420088' role='' />\n    <member type='way' ref='353731500' role='' />\n    <member type='way' ref='119420081' role='' />\n    <member type='way' ref='459757523' role='' />\n    <member type='way' ref='119420077' role='' />\n    <member type='way' ref='119420071' role='' />\n    <member type='way' ref='119420084' role='' />\n    <member type='way' ref='119420067' role='' />\n    <member type='way' ref='119420076' role='' />\n    <member type='way' ref='206031822' role='' />\n    <member type='way' ref='119407555' role='' />\n    <member type='way' ref='206031840' role='' />\n    <member type='way' ref='119408004' role='' />\n    <member type='way' ref='639288354' role='' />\n    <member type='way' ref='639288356' role='' />\n    <member type='way' ref='119408002' role='' />\n    <member type='way' ref='119408003' role='' />\n    <member type='way' ref='119391941' role='' />\n    <member type='way' ref='206031829' role='' />\n    <member type='way' ref='206031847' role='' />\n    <member type='way' ref='367132250' role='' />\n    <member type='way' ref='367132249' role='' />\n    <member type='way' ref='119391944' role='' />\n    <member type='way' ref='368345085' role='' />\n    <member type='way' ref='368345086' role='' />\n    <member type='way' ref='119391946' role='' />\n    <member type='way' ref='119352142' role='' />\n    <member type='way' ref='119352143' role='' />\n    <member type='way' ref='119352145' role='' />\n    <member type='way' ref='119274430' role='' />\n    <member type='way' ref='119274427' role='' />\n    <member type='way' ref='119274434' role='' />\n    <member type='way' ref='119525083' role='' />\n    <member type='way' ref='119525082' role='' />\n    <member type='way' ref='506797113' role='' />\n    <member type='way' ref='639288352' role='' />\n    <member type='way' ref='639288353' role='' />\n    <member type='way' ref='506797114' role='' />\n    <member type='way' ref='119525074' role='' />\n    <member type='way' ref='119525087' role='' />\n    <member type='way' ref='119525078' role='' />\n    <member type='way' ref='119525075' role='' />\n    <member type='way' ref='119525077' role='' />\n    <member type='way' ref='119525080' role='' />\n    <member type='way' ref='119525086' role='' />\n    <member type='way' ref='119525092' role='' />\n    <member type='way' ref='119525093' role='' />\n    <member type='way' ref='119525079' role='' />\n    <member type='way' ref='119525084' role='' />\n    <member type='way' ref='119525088' role='' />\n    <member type='way' ref='119525081' role='' />\n    <member type='way' ref='639288350' role='' />\n    <member type='way' ref='119525076' role='' />\n    <member type='way' ref='119525085' role='' />\n    <member type='way' ref='119525091' role='' />\n    <member type='way' ref='119527506' role='' />\n    <member type='way' ref='119527513' role='' />\n    <member type='way' ref='119527508' role='' />\n    <member type='way' ref='119527500' role='' />\n    <member type='way' ref='639288347' role='' />\n    <member type='way' ref='119527514' role='' />\n    <member type='way' ref='119527505' role='' />\n    <member type='way' ref='119527511' role='' />\n    <member type='way' ref='119527499' role='' />\n    <member type='way' ref='119527503' role='' />\n    <member type='way' ref='119527512' role='' />\n    <member type='way' ref='119527498' role='' />\n    <member type='way' ref='385951002' role='' />\n    <member type='way' ref='385951000' role='' />\n    <member type='way' ref='639288339' role='' />\n    <member type='way' ref='639288341' role='' />\n    <member type='way' ref='119527502' role='' />\n    <member type='way' ref='259275848' role='' />\n    <member type='way' ref='259275846' role='' />\n    <member type='way' ref='259275842' role='' />\n    <member type='way' ref='639288333' role='' />\n    <member type='way' ref='639288335' role='' />\n    <member type='way' ref='259275850' role='' />\n    <member type='way' ref='119538866' role='' />\n    <member type='way' ref='639288293' role='' />\n    <member type='way' ref='639288304' role='' />\n    <member type='way' ref='119539640' role='' />\n    <member type='way' ref='206031846' role='' />\n    <member type='way' ref='639288284' role='' />\n    <member type='way' ref='206031837' role='' />\n    <member type='way' ref='119627709' role='' />\n    <member type='way' ref='119627699' role='' />\n    <member type='way' ref='389800279' role='' />\n    <member type='way' ref='389800281' role='' />\n    <member type='way' ref='119709575' role='' />\n    <member type='way' ref='119709596' role='' />\n    <member type='way' ref='119709569' role='' />\n    <member type='way' ref='119709587' role='' />\n    <member type='way' ref='119709566' role='' />\n    <member type='way' ref='119709578' role='' />\n    <member type='way' ref='119709557' role='' />\n    <member type='way' ref='119709573' role='' />\n    <member type='way' ref='119709561' role='' />\n    <member type='way' ref='119709562' role='' />\n    <member type='way' ref='119709584' role='' />\n    <member type='way' ref='119709589' role='' />\n    <member type='way' ref='119709567' role='' />\n    <member type='way' ref='119709579' role='' />\n    <member type='way' ref='125738665' role='' />\n    <member type='way' ref='125738662' role='' />\n    <member type='way' ref='119709558' role='' />\n    <member type='way' ref='316893357' role='' />\n    <member type='way' ref='316893344' role='' />\n    <member type='way' ref='639288372' role='' />\n    <member type='way' ref='316893360' role='' />\n    <member type='way' ref='316893354' role='' />\n    <member type='way' ref='316893348' role='' />\n    <member type='way' ref='206031835' role='' />\n    <member type='way' ref='175042627' role='' />\n    <member type='way' ref='175042625' role='' />\n    <member type='way' ref='175042626' role='' />\n    <member type='way' ref='175042628' role='' />\n    <member type='way' ref='175042622' role='' />\n    <member type='way' ref='28433405' role='' />\n    <member type='way' ref='28433406' role='' />\n    <member type='way' ref='114767246' role='' />\n    <member type='way' ref='114767245' role='' />\n    <member type='way' ref='42479235' role='' />\n    <member type='way' ref='42479236' role='' />\n    <member type='way' ref='170970665' role='' />\n    <member type='way' ref='316895825' role='' />\n    <member type='way' ref='28432102' role='' />\n    <member type='way' ref='639288387' role='' />\n    <member type='way' ref='28431979' role='' />\n    <member type='way' ref='28431977' role='' />\n    <member type='way' ref='42479150' role='' />\n    <member type='way' ref='639288369' role='' />\n    <member type='way' ref='119507156' role='' />\n    <member type='way' ref='213371643' role='' />\n    <member type='way' ref='540107874' role='' />\n    <member type='way' ref='540107875' role='' />\n    <member type='way' ref='639288364' role='' />\n    <member type='way' ref='119507158' role='' />\n    <member type='way' ref='353731499' role='' />\n    <member type='way' ref='353731501' role='' />\n    <member type='way' ref='639218779' role='' />\n    <member type='way' ref='354329683' role='' />\n    <member type='way' ref='639288386' role='' />\n    <member type='way' ref='28431980' role='' />\n    <member type='way' ref='119514255' role='' />\n    <member type='way' ref='119514252' role='' />\n    <member type='way' ref='119514264' role='' />\n    <member type='way' ref='206031844' role='' />\n    <member type='way' ref='206031825' role='' />\n    <member type='way' ref='206031831' role='' />\n    <member type='way' ref='206031848' role='' />\n    <member type='way' ref='206031843' role='' />\n    <member type='way' ref='28433401' role='' />\n    <member type='way' ref='28433400' role='' />\n    <member type='way' ref='175042624' role='' />\n    <member type='way' ref='175042623' role='' />\n    <member type='way' ref='28433438' role='' />\n    <member type='way' ref='28433442' role='' />\n    <member type='way' ref='206031833' role='' />\n    <member type='way' ref='206031836' role='' />\n    <member type='way' ref='206031834' role='' />\n    <member type='way' ref='206031839' role='' />\n    <member type='way' ref='26275242' role='' />\n    <member type='way' ref='639288371' role='' />\n    <member type='way' ref='316893345' role='' />\n    <member type='way' ref='639288370' role='' />\n    <member type='way' ref='316893356' role='' />\n    <tag k='name' v='Peninsula Subdivision' />\n    <tag k='operator' v='Caltrain' />\n    <tag k='owner' v='Peninsula Corridor Joint Powers Board' />\n    <tag k='route' v='railway' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='7940074' timestamp='2018-01-27T12:10:30Z' uid='933797' user='oba510' version='1' changeset='55801562'>\n    <member type='way' ref='61555898' role='platform' />\n    <member type='way' ref='346766963' role='platform' />\n    <member type='way' ref='61555893' role='platform' />\n    <member type='way' ref='346766964' role='platform' />\n    <member type='way' ref='61555889' role='platform' />\n    <member type='way' ref='346766965' role='platform' />\n    <member type='way' ref='346766967' role='platform' />\n    <member type='way' ref='61555863' role='platform' />\n    <member type='way' ref='61555859' role='platform' />\n    <member type='way' ref='346766968' role='platform' />\n    <member type='way' ref='61555848' role='platform' />\n    <member type='way' ref='346766969' role='platform' />\n    <member type='way' ref='28295229' role='' />\n    <member type='node' ref='3532718854' role='' />\n    <member type='node' ref='3532718855' role='' />\n    <member type='node' ref='3532718859' role='' />\n    <member type='node' ref='3532718856' role='' />\n    <member type='node' ref='3532718857' role='' />\n    <member type='node' ref='3532718858' role='' />\n    <member type='node' ref='3478331121' role='' />\n    <member type='node' ref='4524265936' role='' />\n    <member type='node' ref='4524265937' role='' />\n    <member type='node' ref='3478331123' role='' />\n    <member type='node' ref='3478331129' role='' />\n    <member type='node' ref='3478331126' role='' />\n    <member type='node' ref='3478331127' role='' />\n    <member type='node' ref='4096811908' role='' />\n    <member type='node' ref='4096811904' role='' />\n    <member type='node' ref='4885381971' role='' />\n    <member type='node' ref='4096811905' role='' />\n    <member type='node' ref='4885381972' role='' />\n    <member type='node' ref='4096811903' role='' />\n    <member type='node' ref='4885381973' role='' />\n    <member type='node' ref='4885381974' role='' />\n    <tag k='name' v='San Francisco 4th &amp; King Street Station' />\n    <tag k='network' v='Caltrain' />\n    <tag k='operator' v='Caltrain' />\n    <tag k='public_transport' v='stop_area' />\n    <tag k='type' v='public_transport' />\n  </relation>\n  <relation id='7940075' timestamp='2018-01-27T12:10:31Z' uid='933797' user='oba510' version='1' changeset='55801562'>\n    <member type='relation' ref='7940074' role='' />\n    <member type='relation' ref='7885461' role='' />\n    <member type='relation' ref='7885465' role='' />\n    <tag k='name' v='Fourth &amp; King Street Stations' />\n    <tag k='public_transport' v='stop_area_group' />\n    <tag k='type' v='public_transport' />\n  </relation>\n  <relation id='8135631' timestamp='2018-03-22T09:53:46Z' uid='4007051' user='upendrakarukonda' version='1' changeset='57415811'>\n    <member type='way' ref='513699413' role='from' />\n    <member type='node' ref='65303732' role='via' />\n    <member type='way' ref='513699413' role='to' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='8588025' timestamp='2018-08-24T03:57:33Z' uid='8107451' user='njtbusfan' version='4' changeset='61938661'>\n    <member type='node' ref='5852223940' role='stop' />\n    <member type='way' ref='619344058' role='forward' />\n    <member type='way' ref='619344056' role='forward' />\n    <member type='way' ref='484470678' role='forward' />\n    <member type='way' ref='619344057' role='forward' />\n    <member type='way' ref='619344059' role='forward' />\n    <member type='way' ref='566858290' role='forward' />\n    <member type='way' ref='505685623' role='' />\n    <member type='way' ref='566858292' role='' />\n    <member type='way' ref='566858291' role='' />\n    <member type='way' ref='566858284' role='' />\n    <member type='way' ref='396859821' role='forward' />\n    <member type='way' ref='417327178' role='forward' />\n    <member type='way' ref='417327171' role='forward' />\n    <member type='way' ref='417328272' role='forward' />\n    <member type='way' ref='417328279' role='forward' />\n    <member type='way' ref='417328263' role='forward' />\n    <member type='way' ref='417328315' role='forward' />\n    <member type='way' ref='417328289' role='forward' />\n    <member type='way' ref='417328323' role='forward' />\n    <member type='way' ref='417328297' role='forward' />\n    <member type='way' ref='417357169' role='forward' />\n    <member type='way' ref='417357163' role='forward' />\n    <member type='way' ref='417930655' role='forward' />\n    <member type='way' ref='417930656' role='forward' />\n    <member type='way' ref='395251343' role='forward' />\n    <member type='way' ref='385242657' role='forward' />\n    <member type='way' ref='385242658' role='forward' />\n    <member type='way' ref='385241945' role='forward' />\n    <member type='way' ref='385241948' role='forward' />\n    <member type='way' ref='385241943' role='forward' />\n    <member type='way' ref='385241950' role='forward' />\n    <member type='way' ref='385241946' role='forward' />\n    <member type='way' ref='385241949' role='forward' />\n    <member type='way' ref='385241944' role='forward' />\n    <member type='way' ref='385241947' role='forward' />\n    <member type='way' ref='385243621' role='forward' />\n    <member type='way' ref='385243620' role='forward' />\n    <member type='way' ref='417381812' role='forward' />\n    <member type='way' ref='417381811' role='forward' />\n    <member type='way' ref='43194997' role='forward' />\n    <member type='way' ref='417381806' role='forward' />\n    <member type='way' ref='394715965' role='forward' />\n    <member type='way' ref='394715936' role='forward' />\n    <member type='way' ref='417081024' role='forward' />\n    <member type='way' ref='394715916' role='forward' />\n    <member type='way' ref='394715924' role='forward' />\n    <member type='way' ref='394715955' role='forward' />\n    <member type='way' ref='394715893' role='forward' />\n    <member type='way' ref='392672057' role='forward' />\n    <member type='way' ref='392672071' role='forward' />\n    <member type='way' ref='392672065' role='forward' />\n    <member type='way' ref='392672068' role='forward' />\n    <member type='way' ref='392672070' role='forward' />\n    <member type='way' ref='392672059' role='forward' />\n    <member type='way' ref='417329484' role='forward' />\n    <member type='way' ref='392672069' role='forward' />\n    <member type='way' ref='392672043' role='forward' />\n    <member type='way' ref='392672058' role='forward' />\n    <member type='way' ref='392672047' role='forward' />\n    <member type='way' ref='392672055' role='forward' />\n    <member type='way' ref='417329470' role='forward' />\n    <member type='way' ref='392672066' role='forward' />\n    <member type='way' ref='392672045' role='forward' />\n    <member type='way' ref='116519648' role='forward' />\n    <member type='way' ref='392672053' role='forward' />\n    <member type='way' ref='116519649' role='forward' />\n    <member type='way' ref='392672046' role='forward' />\n    <member type='way' ref='417329479' role='forward' />\n    <member type='way' ref='417329497' role='forward' />\n    <member type='way' ref='417329502' role='forward' />\n    <member type='way' ref='506504150' role='forward' />\n    <member type='way' ref='116519621' role='forward' />\n    <member type='way' ref='417322650' role='forward' />\n    <member type='way' ref='519838094' role='forward' />\n    <member type='way' ref='417322654' role='forward' />\n    <member type='way' ref='394711196' role='forward' />\n    <member type='way' ref='394711199' role='forward' />\n    <member type='way' ref='394711195' role='forward' />\n    <member type='way' ref='394711202' role='forward' />\n    <member type='way' ref='394711201' role='forward' />\n    <member type='way' ref='417319216' role='forward' />\n    <member type='way' ref='417319214' role='forward' />\n    <member type='way' ref='116519645' role='forward' />\n    <member type='way' ref='417319212' role='forward' />\n    <member type='way' ref='116519616' role='forward' />\n    <member type='way' ref='417319210' role='forward' />\n    <member type='way' ref='417319207' role='forward' />\n    <member type='way' ref='116519633' role='forward' />\n    <member type='way' ref='520247835' role='backward' />\n    <member type='way' ref='417934020' role='forward' />\n    <member type='way' ref='417324068' role='forward' />\n    <member type='way' ref='417327167' role='forward' />\n    <member type='way' ref='417324073' role='forward' />\n    <member type='way' ref='417327174' role='forward' />\n    <member type='way' ref='417327161' role='forward' />\n    <member type='way' ref='417328264' role='forward' />\n    <member type='way' ref='417328267' role='forward' />\n    <member type='way' ref='417328265' role='forward' />\n    <member type='way' ref='417328270' role='forward' />\n    <member type='way' ref='417328277' role='forward' />\n    <member type='way' ref='417328331' role='forward' />\n    <member type='way' ref='417328304' role='forward' />\n    <member type='way' ref='417328284' role='forward' />\n    <member type='way' ref='514307878' role='forward' />\n    <member type='way' ref='514307877' role='forward' />\n    <member type='way' ref='573923932' role='forward' />\n    <member type='way' ref='395251344' role='forward' />\n    <member type='way' ref='573923938' role='forward' />\n    <member type='way' ref='385239707' role='forward' />\n    <member type='way' ref='417381317' role='forward' />\n    <member type='way' ref='417381301' role='forward' />\n    <member type='way' ref='417381309' role='forward' />\n    <member type='way' ref='116519652' role='forward' />\n    <member type='way' ref='417081015' role='forward' />\n    <member type='way' ref='394715908' role='forward' />\n    <member type='way' ref='394715976' role='forward' />\n    <member type='way' ref='394715900' role='forward' />\n    <member type='way' ref='395238843' role='forward' />\n    <member type='way' ref='394715929' role='forward' />\n    <member type='way' ref='394715982' role='forward' />\n    <member type='way' ref='417923792' role='forward' />\n    <member type='way' ref='394715945' role='forward' />\n    <member type='way' ref='394715896' role='forward' />\n    <member type='way' ref='392672050' role='forward' />\n    <member type='way' ref='392672060' role='forward' />\n    <member type='way' ref='392672062' role='forward' />\n    <member type='way' ref='392672064' role='forward' />\n    <member type='way' ref='392672067' role='forward' />\n    <member type='way' ref='392672072' role='forward' />\n    <member type='way' ref='392672049' role='forward' />\n    <member type='way' ref='392672048' role='forward' />\n    <member type='way' ref='392672051' role='forward' />\n    <member type='way' ref='392672054' role='forward' />\n    <member type='way' ref='392672044' role='forward' />\n    <member type='way' ref='392672063' role='forward' />\n    <member type='way' ref='392672056' role='forward' />\n    <member type='way' ref='392672061' role='forward' />\n    <member type='way' ref='417329466' role='forward' />\n    <member type='way' ref='116519608' role='forward' />\n    <member type='way' ref='417329490' role='forward' />\n    <member type='way' ref='417329474' role='forward' />\n    <member type='way' ref='417329508' role='forward' />\n    <member type='way' ref='417322648' role='forward' />\n    <member type='way' ref='417322656' role='forward' />\n    <member type='way' ref='417322647' role='forward' />\n    <member type='way' ref='394711197' role='forward' />\n    <member type='way' ref='394711200' role='forward' />\n    <member type='way' ref='394711198' role='forward' />\n    <member type='way' ref='417322646' role='forward' />\n    <member type='way' ref='394711203' role='forward' />\n    <member type='way' ref='417319218' role='forward' />\n    <member type='way' ref='417319211' role='forward' />\n    <member type='way' ref='116519607' role='forward' />\n    <member type='way' ref='116519631' role='forward' />\n    <member type='way' ref='43191669' role='forward' />\n    <member type='way' ref='417319110' role='' />\n    <member type='way' ref='417319091' role='' />\n    <member type='way' ref='417319100' role='' />\n    <member type='way' ref='417319124' role='' />\n    <member type='way' ref='417320305' role='' />\n    <member type='way' ref='417320289' role='' />\n    <member type='way' ref='417320286' role='' />\n    <member type='way' ref='27145287' role='' />\n    <member type='way' ref='417320260' role='' />\n    <member type='way' ref='417320263' role='' />\n    <member type='way' ref='417320270' role='' />\n    <member type='way' ref='417320283' role='' />\n    <member type='way' ref='417320323' role='' />\n    <member type='way' ref='417320292' role='' />\n    <member type='way' ref='43196108' role='forward' />\n    <member type='way' ref='417320297' role='forward' />\n    <member type='way' ref='417320277' role='forward' />\n    <member type='way' ref='24221276' role='forward' />\n    <member type='way' ref='417320341' role='forward' />\n    <member type='way' ref='417320339' role='forward' />\n    <member type='way' ref='44421266' role='forward' />\n    <member type='way' ref='256787728' role='forward' />\n    <member type='way' ref='256750249' role='forward' />\n    <member type='way' ref='417320314' role='forward' />\n    <member type='way' ref='417320353' role='forward' />\n    <member type='way' ref='417320351' role='forward' />\n    <member type='way' ref='256750258' role='forward' />\n    <member type='way' ref='619344106' role='forward' />\n    <member type='way' ref='44421254' role='forward' />\n    <member type='way' ref='44421253' role='forward' />\n    <member type='way' ref='44421248' role='forward' />\n    <member type='way' ref='573802806' role='' />\n    <member type='way' ref='44421256' role='' />\n    <member type='way' ref='44421287' role='forward' />\n    <member type='way' ref='417319029' role='forward' />\n    <member type='way' ref='417319033' role='forward' />\n    <member type='way' ref='256400248' role='forward' />\n    <member type='way' ref='417319062' role='forward' />\n    <member type='way' ref='417319025' role='forward' />\n    <member type='way' ref='145420852' role='forward' />\n    <member type='way' ref='44421282' role='forward' />\n    <member type='way' ref='9259522' role='forward' />\n    <member type='way' ref='392675949' role='forward' />\n    <member type='way' ref='256787711' role='forward' />\n    <member type='way' ref='514264487' role='forward' />\n    <member type='way' ref='514264488' role='forward' />\n    <member type='way' ref='24054676' role='forward' />\n    <member type='way' ref='216932380' role='forward' />\n    <member type='way' ref='510436051' role='forward' />\n    <member type='way' ref='510436047' role='forward' />\n    <member type='way' ref='23335788' role='forward' />\n    <member type='way' ref='510235871' role='forward' />\n    <member type='way' ref='435668946' role='forward' />\n    <member type='way' ref='435668945' role='forward' />\n    <member type='way' ref='256787725' role='forward' />\n    <member type='way' ref='436570209' role='forward' />\n    <member type='way' ref='396936294' role='forward' />\n    <member type='way' ref='396936299' role='forward' />\n    <member type='way' ref='396939369' role='forward' />\n    <member type='way' ref='22564753' role='forward' />\n    <member type='way' ref='619248906' role='forward' />\n    <member type='way' ref='27189674' role='forward' />\n    <member type='way' ref='186398869' role='forward' />\n    <member type='way' ref='23856931' role='forward' />\n    <member type='way' ref='256787749' role='forward' />\n    <member type='way' ref='38853599' role='forward' />\n    <member type='way' ref='256787736' role='forward' />\n    <member type='way' ref='93671776' role='forward' />\n    <member type='way' ref='370279528' role='forward' />\n    <member type='way' ref='256787743' role='forward' />\n    <member type='way' ref='370279599' role='forward' />\n    <member type='way' ref='38855245' role='forward' />\n    <member type='way' ref='38855344' role='forward' />\n    <member type='way' ref='512267983' role='forward' />\n    <member type='way' ref='38855347' role='forward' />\n    <member type='way' ref='370279632' role='forward' />\n    <member type='way' ref='93671819' role='forward' />\n    <member type='way' ref='256787750' role='forward' />\n    <member type='way' ref='38852808' role='forward' />\n    <member type='way' ref='38852894' role='forward' />\n    <member type='way' ref='38852322' role='forward' />\n    <member type='way' ref='41014940' role='forward' />\n    <member type='way' ref='41014907' role='forward' />\n    <member type='way' ref='74463630' role='forward' />\n    <member type='way' ref='392505486' role='forward' />\n    <member type='way' ref='392505485' role='forward' />\n    <member type='way' ref='23801243' role='forward' />\n    <member type='way' ref='392512331' role='forward' />\n    <member type='way' ref='163421225' role='forward' />\n    <member type='way' ref='392512314' role='forward' />\n    <member type='way' ref='619248862' role='forward' />\n    <member type='way' ref='27828689' role='forward' />\n    <member type='way' ref='405283492' role='forward' />\n    <member type='way' ref='256400249' role='forward' />\n    <member type='way' ref='417319056' role='forward' />\n    <member type='way' ref='417319205' role='forward' />\n    <member type='way' ref='44421279' role='forward' />\n    <member type='way' ref='619248849' role='forward' />\n    <member type='way' ref='417922138' role='forward' />\n    <member type='way' ref='44421278' role='forward' />\n    <member type='way' ref='395851585' role='forward' />\n    <member type='way' ref='395851596' role='forward' />\n    <member type='way' ref='24054672' role='forward' />\n    <member type='way' ref='8923187' role='forward' />\n    <member type='way' ref='395851593' role='forward' />\n    <member type='way' ref='24054675' role='forward' />\n    <member type='way' ref='24054674' role='forward' />\n    <member type='way' ref='256787727' role='forward' />\n    <member type='way' ref='8927063' role='forward' />\n    <member type='way' ref='508694209' role='forward' />\n    <member type='way' ref='256787726' role='forward' />\n    <member type='way' ref='396936301' role='forward' />\n    <member type='way' ref='396936290' role='forward' />\n    <member type='way' ref='396939368' role='forward' />\n    <member type='way' ref='394222194' role='forward' />\n    <member type='way' ref='394222195' role='forward' />\n    <member type='way' ref='394222193' role='forward' />\n    <member type='way' ref='38852349' role='forward' />\n    <member type='way' ref='394222196' role='forward' />\n    <member type='way' ref='256787754' role='forward' />\n    <member type='way' ref='256787735' role='forward' />\n    <member type='way' ref='38852894' role='forward' />\n    <member type='way' ref='38852808' role='forward' />\n    <member type='way' ref='256787750' role='forward' />\n    <member type='way' ref='93671819' role='forward' />\n    <member type='way' ref='370279632' role='forward' />\n    <member type='way' ref='38855347' role='forward' />\n    <member type='way' ref='512267983' role='forward' />\n    <member type='way' ref='38855344' role='forward' />\n    <member type='way' ref='38855245' role='forward' />\n    <member type='way' ref='370279599' role='forward' />\n    <member type='way' ref='256787743' role='forward' />\n    <member type='way' ref='370279528' role='forward' />\n    <member type='way' ref='93671776' role='forward' />\n    <member type='way' ref='256787736' role='forward' />\n    <member type='way' ref='38853599' role='forward' />\n    <member type='way' ref='256787749' role='forward' />\n    <member type='way' ref='41014713' role='forward' />\n    <member type='way' ref='186398861' role='forward' />\n    <member type='way' ref='186398854' role='forward' />\n    <member type='way' ref='23856929' role='forward' />\n    <member type='way' ref='510749604' role='forward' />\n    <member type='way' ref='8921109' role='forward' />\n    <member type='way' ref='392505483' role='forward' />\n    <member type='way' ref='619248864' role='forward' />\n    <member type='way' ref='8920070' role='forward' />\n    <member type='way' ref='392505487' role='forward' />\n    <member type='way' ref='8941557' role='forward' />\n    <member type='way' ref='521157640' role='forward' />\n    <member type='way' ref='520419976' role='' />\n    <member type='way' ref='520419971' role='' />\n    <member type='way' ref='27828667' role='' />\n    <member type='way' ref='396980118' role='forward' />\n    <member type='way' ref='61857628' role='forward' />\n    <member type='way' ref='61857624' role='forward' />\n    <member type='way' ref='519627444' role='forward' />\n    <member type='way' ref='521354934' role='forward' />\n    <member type='way' ref='515546855' role='forward' />\n    <member type='way' ref='61857625' role='forward' />\n    <member type='way' ref='465112646' role='forward' />\n    <member type='way' ref='61857626' role='forward' />\n    <member type='way' ref='396980119' role='forward' />\n    <member type='way' ref='396980120' role='forward' />\n    <member type='way' ref='465114548' role='' />\n    <member type='way' ref='263551154' role='' />\n    <member type='way' ref='417331102' role='' />\n    <member type='way' ref='417331100' role='' />\n    <member type='way' ref='396979443' role='forward' />\n    <member type='way' ref='396979440' role='forward' />\n    <member type='way' ref='397155320' role='forward' />\n    <member type='way' ref='397155323' role='forward' />\n    <member type='way' ref='396973374' role='forward' />\n    <member type='way' ref='397154677' role='forward' />\n    <member type='way' ref='417956592' role='forward' />\n    <member type='way' ref='397154671' role='forward' />\n    <member type='way' ref='396973382' role='forward' />\n    <member type='way' ref='514719451' role='forward' />\n    <member type='way' ref='396973394' role='forward' />\n    <member type='way' ref='417956589' role='forward' />\n    <member type='way' ref='417956587' role='forward' />\n    <member type='way' ref='417956584' role='forward' />\n    <member type='way' ref='397107852' role='forward' />\n    <member type='way' ref='163421228' role='forward' />\n    <member type='way' ref='417964218' role='forward' />\n    <member type='way' ref='397157961' role='forward' />\n    <member type='way' ref='417297103' role='forward' />\n    <member type='way' ref='397126972' role='forward' />\n    <member type='way' ref='417297118' role='forward' />\n    <member type='way' ref='417964216' role='forward' />\n    <member type='way' ref='417297110' role='forward' />\n    <member type='way' ref='417964217' role='forward' />\n    <member type='way' ref='397160753' role='forward' />\n    <member type='way' ref='520495665' role='forward' />\n    <member type='way' ref='619248909' role='forward' />\n    <member type='way' ref='619274154' role='forward' />\n    <member type='way' ref='76841024' role='forward' />\n    <member type='way' ref='24570996' role='forward' />\n    <member type='way' ref='521215970' role='forward' />\n    <member type='way' ref='397156915' role='forward' />\n    <member type='way' ref='417964207' role='forward' />\n    <member type='way' ref='397156914' role='forward' />\n    <member type='way' ref='361646337' role='forward' />\n    <member type='way' ref='514719453' role='forward' />\n    <member type='way' ref='417331090' role='forward' />\n    <member type='way' ref='521354930' role='forward' />\n    <member type='way' ref='61857626' role='forward' />\n    <member type='way' ref='465112646' role='forward' />\n    <member type='way' ref='61857625' role='forward' />\n    <member type='way' ref='396976672' role='forward' />\n    <member type='way' ref='619248853' role='forward' />\n    <member type='way' ref='396979439' role='forward' />\n    <member type='way' ref='396979442' role='forward' />\n    <member type='way' ref='514719448' role='forward' />\n    <member type='way' ref='514719449' role='forward' />\n    <member type='way' ref='396979436' role='forward' />\n    <member type='way' ref='397155287' role='forward' />\n    <member type='way' ref='417956583' role='forward' />\n    <member type='way' ref='417956586' role='forward' />\n    <member type='way' ref='449206010' role='forward' />\n    <member type='way' ref='417956591' role='forward' />\n    <member type='way' ref='417956585' role='forward' />\n    <member type='way' ref='515683861' role='forward' />\n    <member type='way' ref='397155288' role='forward' />\n    <member type='way' ref='396973400' role='forward' />\n    <member type='way' ref='417956590' role='forward' />\n    <member type='way' ref='397107838' role='forward' />\n    <member type='way' ref='397127749' role='forward' />\n    <member type='way' ref='163421231' role='forward' />\n    <member type='way' ref='397126971' role='forward' />\n    <member type='way' ref='397158036' role='forward' />\n    <member type='way' ref='417297127' role='forward' />\n    <member type='way' ref='397158035' role='forward' />\n    <member type='way' ref='417297142' role='forward' />\n    <member type='way' ref='37308150' role='forward' />\n    <member type='way' ref='417297155' role='forward' />\n    <member type='way' ref='520495666' role='forward' />\n    <member type='way' ref='397160610' role='forward' />\n    <member type='way' ref='417964209' role='forward' />\n    <member type='way' ref='619248858' role='forward' />\n    <member type='way' ref='138102020' role='forward' />\n    <member type='way' ref='550230203' role='forward' />\n    <member type='way' ref='521215972' role='forward' />\n    <member type='way' ref='24570996' role='forward' />\n    <member type='way' ref='76841024' role='forward' />\n    <member type='way' ref='619274154' role='forward' />\n    <member type='way' ref='619248910' role='forward' />\n    <member type='way' ref='138102020' role='forward' />\n    <member type='way' ref='550230203' role='forward' />\n    <member type='way' ref='397157075' role='forward' />\n    <member type='way' ref='465130862' role='forward' />\n    <member type='way' ref='397157076' role='forward' />\n    <member type='way' ref='397156917' role='forward' />\n    <member type='way' ref='397156919' role='forward' />\n    <member type='way' ref='397156916' role='forward' />\n    <member type='way' ref='397156918' role='forward' />\n    <member type='way' ref='397156383' role='forward' />\n    <member type='way' ref='397156382' role='forward' />\n    <member type='way' ref='513977282' role='forward' />\n    <member type='way' ref='513977283' role='forward' />\n    <member type='way' ref='519627445' role='forward' />\n    <member type='way' ref='61857624' role='forward' />\n    <member type='way' ref='61857628' role='forward' />\n    <member type='way' ref='396980118' role='forward' />\n    <member type='way' ref='27828667' role='' />\n    <member type='way' ref='520419971' role='' />\n    <member type='way' ref='520419976' role='' />\n    <member type='way' ref='521157640' role='forward' />\n    <member type='way' ref='8941557' role='forward' />\n    <member type='way' ref='392512319' role='forward' />\n    <member type='way' ref='27425734' role='forward' />\n    <member type='way' ref='417955691' role='forward' />\n    <member type='way' ref='392512312' role='forward' />\n    <member type='way' ref='27828697' role='forward' />\n    <member type='way' ref='406929833' role='forward' />\n    <member type='way' ref='392512328' role='forward' />\n    <member type='way' ref='392512340' role='forward' />\n    <member type='way' ref='8921935' role='forward' />\n    <member type='way' ref='23801240' role='forward' />\n    <member type='way' ref='395450432' role='forward' />\n    <member type='way' ref='256787710' role='forward' />\n    <member type='way' ref='395575865' role='forward' />\n    <member type='way' ref='395575864' role='forward' />\n    <member type='way' ref='395575863' role='forward' />\n    <member type='way' ref='216881166' role='forward' />\n    <member type='way' ref='216881160' role='forward' />\n    <member type='way' ref='216881163' role='forward' />\n    <member type='way' ref='216881162' role='forward' />\n    <member type='way' ref='216881164' role='forward' />\n    <member type='way' ref='216881165' role='forward' />\n    <member type='way' ref='5071579' role='forward' />\n    <member type='way' ref='4311275' role='forward' />\n    <member type='way' ref='305729174' role='forward' />\n    <member type='way' ref='217582797' role='forward' />\n    <member type='way' ref='513704142' role='forward' />\n    <member type='way' ref='513704144' role='forward' />\n    <member type='way' ref='30593519' role='forward' />\n    <member type='way' ref='124019872' role='forward' />\n    <member type='way' ref='124019859' role='forward' />\n    <member type='way' ref='124019875' role='forward' />\n    <member type='way' ref='124019873' role='forward' />\n    <member type='way' ref='123741436' role='forward' />\n    <member type='way' ref='5071582' role='forward' />\n    <member type='way' ref='254308549' role='forward' />\n    <member type='way' ref='48101183' role='forward' />\n    <member type='way' ref='48101184' role='forward' />\n    <member type='way' ref='8921980' role='forward' />\n    <member type='way' ref='48101169' role='forward' />\n    <member type='way' ref='48191415' role='forward' />\n    <member type='way' ref='123741462' role='forward' />\n    <member type='way' ref='48582237' role='forward' />\n    <member type='way' ref='48037097' role='forward' />\n    <member type='way' ref='48582224' role='forward' />\n    <member type='way' ref='48037052' role='forward' />\n    <member type='way' ref='48037054' role='forward' />\n    <member type='way' ref='48037050' role='forward' />\n    <member type='way' ref='254299123' role='forward' />\n    <member type='way' ref='28326948' role='forward' />\n    <member type='way' ref='33645895' role='forward' />\n    <member type='way' ref='123741441' role='forward' />\n    <member type='way' ref='27183377' role='forward' />\n    <member type='way' ref='27183379' role='forward' />\n    <member type='way' ref='478345678' role='forward' />\n    <member type='way' ref='8915288' role='forward' />\n    <member type='way' ref='394691476' role='forward' />\n    <member type='way' ref='8915254' role='forward' />\n    <member type='way' ref='27093197' role='forward' />\n    <member type='way' ref='515521092' role='forward' />\n    <member type='way' ref='256787755' role='forward' />\n    <member type='way' ref='91181769' role='forward' />\n    <member type='way' ref='405283492' role='forward' />\n    <member type='way' ref='27828689' role='forward' />\n    <member type='way' ref='395450434' role='forward' />\n    <member type='way' ref='27425730' role='forward' />\n    <member type='way' ref='530288783' role='forward' />\n    <member type='way' ref='395575866' role='forward' />\n    <member type='way' ref='395575867' role='forward' />\n    <member type='way' ref='216932399' role='forward' />\n    <member type='way' ref='395577080' role='forward' />\n    <member type='way' ref='395577081' role='forward' />\n    <member type='way' ref='216932378' role='forward' />\n    <member type='way' ref='216932396' role='forward' />\n    <member type='way' ref='216932392' role='forward' />\n    <member type='way' ref='216932391' role='forward' />\n    <member type='way' ref='216932393' role='forward' />\n    <member type='way' ref='28947845' role='forward' />\n    <member type='way' ref='4970206' role='forward' />\n    <member type='way' ref='216932394' role='forward' />\n    <member type='way' ref='123692699' role='forward' />\n    <member type='way' ref='217582796' role='forward' />\n    <member type='way' ref='124019871' role='forward' />\n    <member type='way' ref='124019870' role='forward' />\n    <member type='way' ref='124019874' role='forward' />\n    <member type='way' ref='124019876' role='forward' />\n    <member type='way' ref='513983771' role='forward' />\n    <member type='way' ref='513983770' role='forward' />\n    <member type='way' ref='123741446' role='forward' />\n    <member type='way' ref='48101173' role='forward' />\n    <member type='way' ref='48101182' role='forward' />\n    <member type='way' ref='254308550' role='forward' />\n    <member type='way' ref='48101177' role='forward' />\n    <member type='way' ref='48191417' role='forward' />\n    <member type='way' ref='123741461' role='forward' />\n    <member type='way' ref='48037096' role='forward' />\n    <member type='way' ref='48582245' role='forward' />\n    <member type='way' ref='48036600' role='forward' />\n    <member type='way' ref='48036602' role='forward' />\n    <member type='way' ref='48037051' role='forward' />\n    <member type='way' ref='48037053' role='forward' />\n    <member type='way' ref='28326941' role='forward' />\n    <member type='way' ref='28326944' role='forward' />\n    <member type='way' ref='123741422' role='forward' />\n    <member type='way' ref='397267120' role='forward' />\n    <member type='way' ref='27183366' role='forward' />\n    <member type='way' ref='58841187' role='forward' />\n    <member type='way' ref='26943964' role='forward' />\n    <member type='way' ref='256787734' role='forward' />\n    <member type='way' ref='26944068' role='forward' />\n    <member type='way' ref='394691481' role='forward' />\n    <member type='way' ref='8915261' role='forward' />\n    <member type='way' ref='397093080' role='forward' />\n    <member type='way' ref='27029265' role='forward' />\n    <member type='way' ref='27029264' role='forward' />\n    <member type='way' ref='88572874' role='forward' />\n    <member type='way' ref='417087485' role='' />\n    <member type='way' ref='88572887' role='' />\n    <member type='way' ref='88572914' role='' />\n    <member type='way' ref='88572941' role='' />\n    <member type='way' ref='88572932' role='' />\n    <member type='way' ref='51392400' role='' />\n    <member type='way' ref='51392402' role='' />\n    <member type='way' ref='111560680' role='' />\n    <member type='way' ref='86334994' role='forward' />\n    <member type='way' ref='111560679' role='forward' />\n    <member type='way' ref='88572965' role='' />\n    <member type='way' ref='88559669' role='' />\n    <member type='way' ref='88572877' role='' />\n    <member type='way' ref='417101712' role='' />\n    <member type='way' ref='88572970' role='' />\n    <tag k='FIXME' v='verify route at San Bruno BART' />\n    <tag k='name' v='SamTrans 398' />\n    <tag k='network' v='SamTrans' />\n    <tag k='old_ref' v='KX' />\n    <tag k='operator' v='San Mateo County Transit District' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='398' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8588026' timestamp='2018-11-15T21:52:19Z' uid='8899783' user='Vernon_Langley' version='10' changeset='64545844'>\n    <member type='node' ref='5852223942' role='stop' />\n    <member type='node' ref='5852223943' role='stop' />\n    <member type='way' ref='645066411' role='forward' />\n    <member type='way' ref='619117710' role='forward' />\n    <member type='way' ref='78459876' role='forward' />\n    <member type='way' ref='521492933' role='forward' />\n    <member type='way' ref='266521973' role='forward' />\n    <member type='way' ref='521492939' role='forward' />\n    <member type='way' ref='266521971' role='forward' />\n    <member type='way' ref='8925602' role='forward' />\n    <member type='way' ref='27833060' role='forward' />\n    <member type='way' ref='27833061' role='forward' />\n    <member type='way' ref='256750256' role='forward' />\n    <member type='way' ref='8926711' role='backward' />\n    <member type='way' ref='497135197' role='backward' />\n    <member type='way' ref='609603807' role='backward' />\n    <member type='way' ref='73223807' role='forward' />\n    <member type='way' ref='73223809' role='forward' />\n    <member type='way' ref='19141867' role='forward' />\n    <member type='way' ref='574189554' role='forward' />\n    <member type='way' ref='8949001' role='forward' />\n    <member type='way' ref='256750262' role='backward' />\n    <member type='way' ref='608449611' role='backward' />\n    <member type='way' ref='94842320' role='forward' />\n    <member type='way' ref='19852487' role='forward' />\n    <member type='way' ref='27833064' role='forward' />\n    <member type='way' ref='596088313' role='forward' />\n    <member type='way' ref='27833022' role='forward' />\n    <member type='way' ref='506448454' role='forward' />\n    <member type='way' ref='240216044' role='' />\n    <member type='way' ref='596085791' role='' />\n    <member type='way' ref='526857048' role='' />\n    <member type='way' ref='619117711' role='forward' />\n    <member type='way' ref='489734404' role='forward' />\n    <member type='way' ref='619117712' role='forward' />\n    <member type='way' ref='8967469' role='forward' />\n    <member type='way' ref='396710681' role='' />\n    <member type='way' ref='396710683' role='' />\n    <member type='way' ref='396710682' role='' />\n    <member type='way' ref='396690686' role='' />\n    <member type='way' ref='413702122' role='' />\n    <member type='way' ref='489738673' role='' />\n    <member type='way' ref='113689528' role='' />\n    <member type='way' ref='408593822' role='' />\n    <member type='way' ref='392625188' role='' />\n    <member type='way' ref='392625189' role='' />\n    <member type='way' ref='48203972' role='forward' />\n    <member type='way' ref='516237835' role='forward' />\n    <member type='way' ref='185007760' role='forward' />\n    <member type='way' ref='27807380' role='forward' />\n    <member type='way' ref='392623581' role='forward' />\n    <member type='way' ref='392623580' role='forward' />\n    <member type='way' ref='37680664' role='forward' />\n    <member type='way' ref='392620087' role='forward' />\n    <member type='way' ref='392620072' role='forward' />\n    <member type='way' ref='392620089' role='forward' />\n    <member type='way' ref='392620082' role='forward' />\n    <member type='way' ref='392620084' role='forward' />\n    <member type='way' ref='392620064' role='forward' />\n    <member type='way' ref='392620067' role='forward' />\n    <member type='way' ref='392620080' role='forward' />\n    <member type='way' ref='392620081' role='forward' />\n    <member type='way' ref='392620085' role='forward' />\n    <member type='way' ref='619248867' role='forward' />\n    <member type='way' ref='28431172' role='forward' />\n    <member type='way' ref='392625190' role='forward' />\n    <member type='way' ref='450948755' role='forward' />\n    <member type='way' ref='37680625' role='forward' />\n    <member type='way' ref='392623579' role='forward' />\n    <member type='way' ref='37680696' role='forward' />\n    <member type='way' ref='392620091' role='forward' />\n    <member type='way' ref='392620066' role='forward' />\n    <member type='way' ref='392620077' role='forward' />\n    <member type='way' ref='392620069' role='forward' />\n    <member type='way' ref='392620079' role='forward' />\n    <member type='way' ref='392620073' role='forward' />\n    <member type='way' ref='392620090' role='forward' />\n    <member type='way' ref='392620086' role='forward' />\n    <member type='way' ref='185382597' role='forward' />\n    <member type='way' ref='392622221' role='' />\n    <member type='way' ref='573819201' role='' />\n    <member type='way' ref='573819204' role='' />\n    <member type='way' ref='28617707' role='' />\n    <member type='way' ref='518182010' role='' />\n    <member type='way' ref='518182012' role='' />\n    <member type='way' ref='392622229' role='' />\n    <member type='way' ref='149510726' role='' />\n    <member type='way' ref='149510724' role='forward' />\n    <member type='way' ref='149510725' role='forward' />\n    <member type='way' ref='417318111' role='' />\n    <member type='way' ref='417318112' role='' />\n    <member type='way' ref='28617710' role='forward' />\n    <member type='way' ref='312945882' role='forward' />\n    <member type='way' ref='217367578' role='forward' />\n    <member type='way' ref='312945881' role='forward' />\n    <member type='way' ref='25025056' role='forward' />\n    <member type='way' ref='30908193' role='forward' />\n    <member type='way' ref='541403375' role='forward' />\n    <member type='way' ref='520586197' role='forward' />\n    <member type='way' ref='417318113' role='forward' />\n    <member type='way' ref='166438406' role='forward' />\n    <member type='way' ref='396900152' role='forward' />\n    <member type='way' ref='506435465' role='forward' />\n    <member type='way' ref='58673712' role='forward' />\n    <member type='way' ref='63095845' role='forward' />\n    <member type='way' ref='619117704' role='forward' />\n    <member type='way' ref='619117709' role='forward' />\n    <member type='way' ref='396900153' role='forward' />\n    <member type='way' ref='217367577' role='forward' />\n    <member type='way' ref='394449985' role='forward' />\n    <member type='way' ref='632334389' role='forward' />\n    <member type='way' ref='30732291' role='forward' />\n    <member type='way' ref='30908192' role='forward' />\n    <member type='way' ref='125343988' role='forward' />\n    <member type='way' ref='541403376' role='forward' />\n    <member type='way' ref='166438408' role='forward' />\n    <member type='way' ref='417318114' role='forward' />\n    <member type='way' ref='417318115' role='forward' />\n    <member type='way' ref='573818856' role='forward' />\n    <member type='way' ref='30908194' role='forward' />\n    <member type='way' ref='506435464' role='forward' />\n    <member type='way' ref='619117707' role='forward' />\n    <member type='way' ref='619117704' role='forward' />\n    <member type='way' ref='63095845' role='forward' />\n    <member type='way' ref='58673712' role='forward' />\n    <member type='way' ref='619117706' role='forward' />\n    <member type='way' ref='396685535' role='forward' />\n    <member type='way' ref='396685539' role='forward' />\n    <member type='way' ref='396685529' role='' />\n    <member type='way' ref='128801655' role='' />\n    <member type='way' ref='128801657' role='forward' />\n    <member type='way' ref='128801658' role='forward' />\n    <member type='way' ref='396685533' role='' />\n    <member type='way' ref='128801652' role='' />\n    <member type='way' ref='396685537' role='' />\n    <member type='way' ref='396685536' role='' />\n    <member type='way' ref='396685538' role='' />\n    <member type='way' ref='408611121' role='forward' />\n    <member type='way' ref='128801650' role='forward' />\n    <member type='way' ref='333638762' role='forward' />\n    <member type='way' ref='506435118' role='forward' />\n    <member type='way' ref='396685540' role='forward' />\n    <member type='way' ref='396685532' role='forward' />\n    <member type='way' ref='128801654' role='forward' />\n    <member type='way' ref='396685534' role='forward' />\n    <member type='way' ref='396685530' role='forward' />\n    <member type='way' ref='396685531' role='' />\n    <member type='way' ref='396713314' role='' />\n    <member type='way' ref='128801656' role='' />\n    <member type='way' ref='128801660' role='forward' />\n    <member type='way' ref='396713319' role='forward' />\n    <member type='way' ref='573825803' role='forward' />\n    <member type='way' ref='396713322' role='forward' />\n    <member type='way' ref='573825800' role='forward' />\n    <member type='way' ref='573825802' role='forward' />\n    <member type='way' ref='396706323' role='forward' />\n    <member type='way' ref='619248870' role='forward' />\n    <member type='way' ref='573821959' role='forward' />\n    <member type='way' ref='396706309' role='forward' />\n    <member type='way' ref='435715047' role='forward' />\n    <member type='way' ref='85933011' role='forward' />\n    <member type='way' ref='619117714' role='forward' />\n    <member type='way' ref='85933009' role='forward' />\n    <member type='way' ref='573825806' role='forward' />\n    <member type='way' ref='573825798' role='forward' />\n    <member type='way' ref='514350103' role='forward' />\n    <member type='way' ref='396706916' role='forward' />\n    <member type='way' ref='573825805' role='forward' />\n    <member type='way' ref='264202217' role='forward' />\n    <member type='way' ref='63095593' role='forward' />\n    <member type='way' ref='627816116' role='' />\n    <member type='way' ref='168092537' role='' />\n    <member type='way' ref='417318602' role='' />\n    <member type='way' ref='396706920' role='' />\n    <member type='way' ref='396706917' role='' />\n    <member type='way' ref='396706922' role='' />\n    <member type='way' ref='396706918' role='' />\n    <member type='way' ref='87602990' role='' />\n    <member type='way' ref='396706919' role='' />\n    <member type='way' ref='396706921' role='' />\n    <member type='way' ref='396708223' role='' />\n    <member type='way' ref='397026415' role='' />\n    <member type='way' ref='397026410' role='' />\n    <member type='way' ref='417318603' role='' />\n    <member type='way' ref='417318601' role='' />\n    <member type='way' ref='396711287' role='' />\n    <member type='way' ref='396711277' role='' />\n    <member type='way' ref='396711283' role='' />\n    <member type='way' ref='396901631' role='' />\n    <member type='way' ref='396901635' role='' />\n    <member type='way' ref='573987311' role='' />\n    <member type='way' ref='85933008' role='' />\n    <member type='way' ref='328893645' role='' />\n    <member type='way' ref='396847288' role='' />\n    <member type='way' ref='573989315' role='' />\n    <member type='way' ref='95731689' role='' />\n    <member type='way' ref='397030685' role='' />\n    <member type='way' ref='397030679' role='' />\n    <member type='way' ref='397030681' role='' />\n    <member type='way' ref='417325654' role='' />\n    <member type='way' ref='397030684' role='' />\n    <member type='way' ref='417934017' role='' />\n    <member type='way' ref='397030688' role='' />\n    <member type='way' ref='394465414' role='forward' />\n    <member type='way' ref='397030691' role='forward' />\n    <member type='way' ref='397030687' role='forward' />\n    <member type='way' ref='536946477' role='forward' />\n    <member type='way' ref='397028251' role='forward' />\n    <member type='way' ref='394465413' role='forward' />\n    <member type='way' ref='417325673' role='forward' />\n    <member type='way' ref='394465415' role='forward' />\n    <member type='way' ref='580688339' role='forward' />\n    <member type='way' ref='573954483' role='forward' />\n    <member type='way' ref='397030689' role='forward' />\n    <member type='way' ref='397028253' role='forward' />\n    <member type='way' ref='397030683' role='forward' />\n    <member type='way' ref='397030680' role='forward' />\n    <member type='way' ref='397028252' role='forward' />\n    <member type='way' ref='397030686' role='forward' />\n    <member type='way' ref='397030682' role='forward' />\n    <member type='way' ref='397030692' role='forward' />\n    <member type='way' ref='580688340' role='forward' />\n    <member type='way' ref='417325686' role='forward' />\n    <member type='way' ref='95731700' role='forward' />\n    <member type='way' ref='397030690' role='forward' />\n    <member type='way' ref='396856914' role='' />\n    <member type='way' ref='396856913' role='' />\n    <member type='way' ref='396856912' role='' />\n    <member type='way' ref='396856916' role='' />\n    <member type='way' ref='396856917' role='' />\n    <member type='way' ref='396856915' role='' />\n    <member type='way' ref='417325698' role='' />\n    <member type='way' ref='580688534' role='' />\n    <member type='way' ref='417325679' role='' />\n    <member type='way' ref='417934016' role='' />\n    <member type='way' ref='417325660' role='' />\n    <member type='way' ref='417325667' role='' />\n    <member type='way' ref='95857269' role='' />\n    <member type='way' ref='417325648' role='' />\n    <member type='way' ref='417325692' role='' />\n    <member type='way' ref='451040742' role='forward' />\n    <member type='way' ref='419498337' role='forward' />\n    <member type='way' ref='417325628' role='forward' />\n    <member type='way' ref='417325620' role='forward' />\n    <member type='way' ref='396856802' role='forward' />\n    <member type='way' ref='419498336' role='' />\n    <member type='way' ref='417325622' role='' />\n    <member type='way' ref='396856799' role='' />\n    <member type='way' ref='396856796' role='' />\n    <member type='way' ref='396856798' role='' />\n    <member type='way' ref='417324072' role='forward' />\n    <member type='way' ref='417324066' role='forward' />\n    <member type='way' ref='417324067' role='forward' />\n    <member type='way' ref='396856801' role='forward' />\n    <member type='way' ref='396859816' role='forward' />\n    <member type='way' ref='396859818' role='forward' />\n    <member type='way' ref='396859820' role='forward' />\n    <member type='way' ref='520247835' role='forward' />\n    <member type='way' ref='566858284' role='' />\n    <member type='way' ref='566858291' role='' />\n    <member type='way' ref='566858292' role='' />\n    <member type='way' ref='505685623' role='' />\n    <member type='way' ref='566858290' role='forward' />\n    <member type='way' ref='619344059' role='forward' />\n    <member type='way' ref='619344057' role='forward' />\n    <member type='way' ref='484470678' role='forward' />\n    <member type='way' ref='619344056' role='forward' />\n    <member type='way' ref='619344058' role='forward' />\n    <member type='way' ref='619344058' role='forward' />\n    <member type='way' ref='619344056' role='forward' />\n    <member type='way' ref='566858289' role='forward' />\n    <member type='way' ref='619344057' role='forward' />\n    <member type='way' ref='619344059' role='forward' />\n    <member type='way' ref='566858290' role='forward' />\n    <member type='way' ref='505685623' role='' />\n    <member type='way' ref='566858292' role='' />\n    <member type='way' ref='566858291' role='' />\n    <member type='way' ref='566858284' role='' />\n    <member type='way' ref='396859821' role='forward' />\n    <member type='way' ref='417327178' role='forward' />\n    <member type='way' ref='417327171' role='forward' />\n    <member type='way' ref='417328272' role='forward' />\n    <member type='way' ref='417328279' role='forward' />\n    <member type='way' ref='417328263' role='forward' />\n    <member type='way' ref='417328315' role='forward' />\n    <member type='way' ref='417328289' role='forward' />\n    <member type='way' ref='417328323' role='forward' />\n    <member type='way' ref='417328297' role='forward' />\n    <member type='way' ref='417357169' role='forward' />\n    <member type='way' ref='417357163' role='forward' />\n    <member type='way' ref='417930655' role='forward' />\n    <member type='way' ref='417930656' role='forward' />\n    <member type='way' ref='395251343' role='forward' />\n    <member type='way' ref='385242657' role='forward' />\n    <member type='way' ref='385242658' role='forward' />\n    <member type='way' ref='385241945' role='forward' />\n    <member type='way' ref='385241948' role='forward' />\n    <member type='way' ref='385241943' role='forward' />\n    <member type='way' ref='385241950' role='forward' />\n    <member type='way' ref='385241946' role='forward' />\n    <member type='way' ref='385241949' role='forward' />\n    <member type='way' ref='385241944' role='forward' />\n    <member type='way' ref='385241947' role='forward' />\n    <member type='way' ref='385243621' role='forward' />\n    <member type='way' ref='385243620' role='forward' />\n    <member type='way' ref='417381812' role='forward' />\n    <member type='way' ref='417381811' role='forward' />\n    <member type='way' ref='43194997' role='forward' />\n    <member type='way' ref='417381806' role='forward' />\n    <member type='way' ref='394715965' role='forward' />\n    <member type='way' ref='394715936' role='forward' />\n    <member type='way' ref='417081024' role='forward' />\n    <member type='way' ref='394715916' role='forward' />\n    <member type='way' ref='394715924' role='forward' />\n    <member type='way' ref='394715955' role='forward' />\n    <member type='way' ref='394715893' role='forward' />\n    <member type='way' ref='392672057' role='forward' />\n    <member type='way' ref='392672071' role='forward' />\n    <member type='way' ref='392672065' role='forward' />\n    <member type='way' ref='392672068' role='forward' />\n    <member type='way' ref='392672070' role='forward' />\n    <member type='way' ref='392672059' role='forward' />\n    <member type='way' ref='417329484' role='forward' />\n    <member type='way' ref='392672069' role='forward' />\n    <member type='way' ref='392672043' role='forward' />\n    <member type='way' ref='392672058' role='forward' />\n    <member type='way' ref='392672047' role='forward' />\n    <member type='way' ref='392672055' role='forward' />\n    <member type='way' ref='417329470' role='forward' />\n    <member type='way' ref='392672066' role='forward' />\n    <member type='way' ref='392672045' role='forward' />\n    <member type='way' ref='116519648' role='forward' />\n    <member type='way' ref='392672053' role='forward' />\n    <member type='way' ref='116519649' role='forward' />\n    <member type='way' ref='392672046' role='forward' />\n    <member type='way' ref='417329479' role='forward' />\n    <member type='way' ref='417329497' role='forward' />\n    <member type='way' ref='417329502' role='forward' />\n    <member type='way' ref='506504150' role='forward' />\n    <member type='way' ref='116519621' role='forward' />\n    <member type='way' ref='417322650' role='forward' />\n    <member type='way' ref='519838094' role='forward' />\n    <member type='way' ref='417322654' role='forward' />\n    <member type='way' ref='394711196' role='forward' />\n    <member type='way' ref='394711199' role='forward' />\n    <member type='way' ref='394711195' role='forward' />\n    <member type='way' ref='394711202' role='forward' />\n    <member type='way' ref='394711201' role='forward' />\n    <member type='way' ref='417319216' role='forward' />\n    <member type='way' ref='417319214' role='forward' />\n    <member type='way' ref='116519645' role='forward' />\n    <member type='way' ref='417319212' role='forward' />\n    <member type='way' ref='116519616' role='forward' />\n    <member type='way' ref='417319210' role='forward' />\n    <member type='way' ref='417319207' role='forward' />\n    <member type='way' ref='116519633' role='forward' />\n    <member type='way' ref='520247835' role='backward' />\n    <member type='way' ref='417934020' role='forward' />\n    <member type='way' ref='417324068' role='forward' />\n    <member type='way' ref='417327167' role='forward' />\n    <member type='way' ref='417324073' role='forward' />\n    <member type='way' ref='417327174' role='forward' />\n    <member type='way' ref='417327161' role='forward' />\n    <member type='way' ref='417328264' role='forward' />\n    <member type='way' ref='417328267' role='forward' />\n    <member type='way' ref='417328265' role='forward' />\n    <member type='way' ref='417328270' role='forward' />\n    <member type='way' ref='417328277' role='forward' />\n    <member type='way' ref='417328331' role='forward' />\n    <member type='way' ref='417328304' role='forward' />\n    <member type='way' ref='417328284' role='forward' />\n    <member type='way' ref='514307878' role='forward' />\n    <member type='way' ref='514307877' role='forward' />\n    <member type='way' ref='573923932' role='forward' />\n    <member type='way' ref='395251344' role='forward' />\n    <member type='way' ref='573923938' role='forward' />\n    <member type='way' ref='385239707' role='forward' />\n    <member type='way' ref='417381317' role='forward' />\n    <member type='way' ref='417381301' role='forward' />\n    <member type='way' ref='417381309' role='forward' />\n    <member type='way' ref='116519652' role='forward' />\n    <member type='way' ref='417081015' role='forward' />\n    <member type='way' ref='394715908' role='forward' />\n    <member type='way' ref='394715976' role='forward' />\n    <member type='way' ref='394715900' role='forward' />\n    <member type='way' ref='395238843' role='forward' />\n    <member type='way' ref='394715929' role='forward' />\n    <member type='way' ref='394715982' role='forward' />\n    <member type='way' ref='417923792' role='forward' />\n    <member type='way' ref='394715945' role='forward' />\n    <member type='way' ref='394715896' role='forward' />\n    <member type='way' ref='392672050' role='forward' />\n    <member type='way' ref='392672060' role='forward' />\n    <member type='way' ref='392672062' role='forward' />\n    <member type='way' ref='392672064' role='forward' />\n    <member type='way' ref='392672067' role='forward' />\n    <member type='way' ref='392672072' role='forward' />\n    <member type='way' ref='392672049' role='forward' />\n    <member type='way' ref='392672048' role='forward' />\n    <member type='way' ref='392672051' role='forward' />\n    <member type='way' ref='392672054' role='forward' />\n    <member type='way' ref='392672044' role='forward' />\n    <member type='way' ref='392672063' role='forward' />\n    <member type='way' ref='392672056' role='forward' />\n    <member type='way' ref='392672061' role='forward' />\n    <member type='way' ref='417329466' role='forward' />\n    <member type='way' ref='116519608' role='forward' />\n    <member type='way' ref='417329490' role='forward' />\n    <member type='way' ref='417329474' role='forward' />\n    <member type='way' ref='417329508' role='forward' />\n    <member type='way' ref='417322648' role='forward' />\n    <member type='way' ref='417322656' role='forward' />\n    <member type='way' ref='417322647' role='forward' />\n    <member type='way' ref='394711197' role='forward' />\n    <member type='way' ref='394711200' role='forward' />\n    <member type='way' ref='394711198' role='forward' />\n    <member type='way' ref='417322646' role='forward' />\n    <member type='way' ref='394711203' role='forward' />\n    <member type='way' ref='417319218' role='forward' />\n    <member type='way' ref='417319211' role='forward' />\n    <member type='way' ref='116519607' role='forward' />\n    <member type='way' ref='116519631' role='forward' />\n    <member type='way' ref='43191669' role='forward' />\n    <member type='way' ref='417319110' role='' />\n    <member type='way' ref='417319091' role='' />\n    <member type='way' ref='417319100' role='' />\n    <member type='way' ref='417319124' role='' />\n    <member type='way' ref='417320305' role='' />\n    <member type='way' ref='417320289' role='' />\n    <member type='way' ref='417320286' role='' />\n    <member type='way' ref='27145287' role='' />\n    <member type='way' ref='417320260' role='' />\n    <member type='way' ref='417320263' role='' />\n    <member type='way' ref='417320270' role='' />\n    <member type='way' ref='417320283' role='' />\n    <member type='way' ref='417320323' role='' />\n    <member type='way' ref='417320292' role='' />\n    <member type='way' ref='43196108' role='forward' />\n    <member type='way' ref='417320297' role='forward' />\n    <member type='way' ref='417320277' role='forward' />\n    <member type='way' ref='24221276' role='forward' />\n    <member type='way' ref='417320341' role='forward' />\n    <member type='way' ref='417320339' role='forward' />\n    <member type='way' ref='417320344' role='forward' />\n    <member type='way' ref='256787737' role='forward' />\n    <member type='way' ref='520647302' role='forward' />\n    <member type='way' ref='520647303' role='forward' />\n    <member type='way' ref='256750250' role='forward' />\n    <member type='way' ref='256750249' role='forward' />\n    <member type='way' ref='417320314' role='forward' />\n    <member type='way' ref='417320353' role='forward' />\n    <member type='way' ref='417320351' role='forward' />\n    <member type='way' ref='256750258' role='forward' />\n    <member type='way' ref='619344106' role='forward' />\n    <member type='way' ref='417320335' role='forward' />\n    <member type='way' ref='417320347' role='forward' />\n    <member type='way' ref='24221259' role='forward' />\n    <member type='way' ref='417326097' role='forward' />\n    <member type='way' ref='417326057' role='forward' />\n    <member type='way' ref='218659905' role='forward' />\n    <member type='way' ref='218659907' role='' />\n    <member type='way' ref='417919993' role='' />\n    <member type='way' ref='417326086' role='' />\n    <member type='way' ref='417326151' role='' />\n    <member type='way' ref='417326066' role='' />\n    <member type='way' ref='417326077' role='' />\n    <member type='way' ref='417326163' role='' />\n    <member type='way' ref='417326136' role='' />\n    <member type='way' ref='417326108' role='' />\n    <member type='way' ref='417326122' role='' />\n    <member type='way' ref='394692838' role='' />\n    <member type='way' ref='394692837' role='forward' />\n    <member type='way' ref='394692833' role='forward' />\n    <member type='way' ref='394691031' role='forward' />\n    <member type='way' ref='394692832' role='forward' />\n    <member type='way' ref='394692835' role='forward' />\n    <member type='way' ref='394692836' role='forward' />\n    <member type='way' ref='573535745' role='forward' />\n    <member type='way' ref='449675231' role='forward' />\n    <member type='way' ref='394691029' role='forward' />\n    <member type='way' ref='573535751' role='forward' />\n    <member type='way' ref='417924361' role='forward' />\n    <member type='way' ref='218659904' role='forward' />\n    <member type='way' ref='449673240' role='forward' />\n    <member type='way' ref='449674239' role='forward' />\n    <member type='way' ref='449673243' role='forward' />\n    <member type='way' ref='449672991' role='forward' />\n    <member type='way' ref='576288948' role='forward' />\n    <member type='way' ref='449672992' role='forward' />\n    <member type='way' ref='449671669' role='forward' />\n    <member type='way' ref='449671661' role='forward' />\n    <member type='way' ref='417317636' role='forward' />\n    <member type='way' ref='417317629' role='forward' />\n    <member type='way' ref='417328051' role='forward' />\n    <member type='way' ref='417328063' role='forward' />\n    <member type='way' ref='417328071' role='forward' />\n    <member type='way' ref='417328044' role='forward' />\n    <member type='way' ref='394691024' role='forward' />\n    <member type='way' ref='394691025' role='forward' />\n    <member type='way' ref='394692831' role='forward' />\n    <member type='way' ref='394691026' role='forward' />\n    <member type='way' ref='394692834' role='forward' />\n    <member type='way' ref='394691032' role='forward' />\n    <member type='way' ref='449675230' role='forward' />\n    <member type='way' ref='394691028' role='forward' />\n    <member type='way' ref='8937488' role='forward' />\n    <member type='way' ref='449673258' role='forward' />\n    <member type='way' ref='449674238' role='forward' />\n    <member type='way' ref='449673242' role='forward' />\n    <member type='way' ref='576288947' role='forward' />\n    <member type='way' ref='449672990' role='forward' />\n    <member type='way' ref='449672993' role='forward' />\n    <member type='way' ref='449671674' role='forward' />\n    <member type='way' ref='417317642' role='forward' />\n    <member type='way' ref='417317652' role='forward' />\n    <member type='way' ref='417317624' role='forward' />\n    <member type='way' ref='417328055' role='forward' />\n    <member type='way' ref='417328046' role='forward' />\n    <member type='way' ref='417328038' role='forward' />\n    <member type='way' ref='417328075' role='forward' />\n    <member type='way' ref='394691027' role='forward' />\n    <member type='way' ref='43196323' role='' />\n    <member type='way' ref='417328077' role='' />\n    <member type='way' ref='417328078' role='' />\n    <member type='way' ref='417328079' role='' />\n    <member type='way' ref='573426245' role='' />\n    <member type='way' ref='505631162' role='' />\n    <member type='way' ref='130726875' role='' />\n    <member type='way' ref='417328082' role='' />\n    <member type='way' ref='417328081' role='' />\n    <member type='way' ref='505631082' role='' />\n    <member type='way' ref='505631079' role='' />\n    <member type='way' ref='417328080' role='' />\n    <member type='way' ref='573435405' role='' />\n    <member type='way' ref='152910615' role='' />\n    <member type='way' ref='449668164' role='' />\n    <member type='way' ref='449668165' role='' />\n    <member type='way' ref='449664010' role='' />\n    <member type='way' ref='449664015' role='' />\n    <member type='way' ref='519745241' role='' />\n    <member type='way' ref='449672632' role='' />\n    <member type='way' ref='573414711' role='' />\n    <member type='way' ref='449671496' role='' />\n    <member type='way' ref='505553862' role='' />\n    <member type='way' ref='505553863' role='' />\n    <member type='way' ref='130714451' role='' />\n    <member type='way' ref='449635435' role='' />\n    <member type='way' ref='449635436' role='' />\n    <member type='way' ref='409966438' role='' />\n    <member type='way' ref='449661909' role='' />\n    <member type='way' ref='449660155' role='' />\n    <member type='way' ref='449660154' role='' />\n    <member type='way' ref='587067838' role='' />\n    <member type='way' ref='409966433' role='' />\n    <member type='way' ref='449660156' role='' />\n    <member type='way' ref='449659279' role='' />\n    <member type='way' ref='449659280' role='' />\n    <member type='way' ref='481782738' role='' />\n    <member type='way' ref='449659281' role='' />\n    <member type='way' ref='449637410' role='' />\n    <member type='way' ref='449637414' role='' />\n    <member type='way' ref='587081713' role='' />\n    <member type='way' ref='481782739' role='' />\n    <member type='way' ref='449637151' role='' />\n    <member type='way' ref='449637152' role='' />\n    <member type='way' ref='23655685' role='forward' />\n    <member type='way' ref='513693411' role='forward' />\n    <member type='way' ref='449636628' role='forward' />\n    <member type='way' ref='449636623' role='forward' />\n    <member type='way' ref='409964622' role='forward' />\n    <member type='way' ref='410024391' role='forward' />\n    <member type='way' ref='409964623' role='forward' />\n    <member type='way' ref='409964621' role='forward' />\n    <member type='way' ref='410025059' role='forward' />\n    <member type='way' ref='410026259' role='forward' />\n    <member type='way' ref='396939378' role='forward' />\n    <member type='way' ref='410039393' role='forward' />\n    <member type='way' ref='389132845' role='forward' />\n    <member type='way' ref='25815696' role='forward' />\n    <member type='way' ref='256750236' role='forward' />\n    <member type='way' ref='25815707' role='forward' />\n    <member type='way' ref='25815708' role='forward' />\n    <member type='way' ref='417975386' role='forward' />\n    <member type='way' ref='410015138' role='forward' />\n    <member type='way' ref='420259129' role='forward' />\n    <member type='way' ref='420259131' role='forward' />\n    <member type='way' ref='77412293' role='forward' />\n    <member type='way' ref='286685347' role='forward' />\n    <member type='way' ref='550583059' role='forward' />\n    <member type='way' ref='35744092' role='forward' />\n    <member type='way' ref='256750254' role='forward' />\n    <member type='way' ref='619248866' role='forward' />\n    <member type='way' ref='420259129' role='forward' />\n    <member type='way' ref='256750239' role='forward' />\n    <member type='way' ref='410014669' role='forward' />\n    <member type='way' ref='396939375' role='forward' />\n    <member type='way' ref='96026494' role='forward' />\n    <member type='way' ref='396939372' role='forward' />\n    <member type='way' ref='24294629' role='forward' />\n    <member type='way' ref='262882951' role='forward' />\n    <member type='way' ref='396967406' role='forward' />\n    <member type='way' ref='186398834' role='forward' />\n    <member type='way' ref='517255694' role='forward' />\n    <member type='way' ref='256400261' role='forward' />\n    <member type='way' ref='513693410' role='forward' />\n    <member type='way' ref='256750245' role='forward' />\n    <member type='way' ref='409964811' role='forward' />\n    <member type='way' ref='449638583' role='forward' />\n    <member type='way' ref='409964815' role='forward' />\n    <member type='way' ref='409964620' role='forward' />\n    <member type='way' ref='409964624' role='forward' />\n    <member type='way' ref='410026258' role='forward' />\n    <member type='way' ref='396939377' role='forward' />\n    <member type='way' ref='396939376' role='forward' />\n    <member type='way' ref='410015792' role='forward' />\n    <member type='way' ref='256750244' role='forward' />\n    <member type='way' ref='410153652' role='forward' />\n    <member type='way' ref='409787122' role='forward' />\n    <member type='way' ref='286685342' role='forward' />\n    <member type='way' ref='24334763' role='forward' />\n    <member type='way' ref='256750237' role='forward' />\n    <member type='way' ref='256750254' role='forward' />\n    <member type='way' ref='35744092' role='forward' />\n    <member type='way' ref='550583059' role='forward' />\n    <member type='way' ref='286685347' role='forward' />\n    <member type='way' ref='77412293' role='forward' />\n    <member type='way' ref='396939371' role='forward' />\n    <member type='way' ref='396939370' role='forward' />\n    <member type='way' ref='396939373' role='forward' />\n    <member type='way' ref='396939374' role='forward' />\n    <member type='way' ref='24294628' role='forward' />\n    <member type='way' ref='505767579' role='forward' />\n    <member type='way' ref='262882948' role='forward' />\n    <member type='way' ref='395651961' role='forward' />\n    <member type='way' ref='256400250' role='forward' />\n    <member type='way' ref='619248865' role='forward' />\n    <member type='way' ref='389133512' role='' />\n    <member type='way' ref='396967418' role='' />\n    <member type='way' ref='396967415' role='' />\n    <member type='way' ref='396967423' role='' />\n    <member type='way' ref='396967413' role='' />\n    <member type='way' ref='396967420' role='' />\n    <member type='way' ref='409947876' role='' />\n    <member type='way' ref='8941432' role='' />\n    <member type='way' ref='406944784' role='forward' />\n    <member type='way' ref='165682405' role='forward' />\n    <member type='way' ref='256787740' role='forward' />\n    <member type='way' ref='93671776' role='forward' />\n    <member type='way' ref='370279528' role='forward' />\n    <member type='way' ref='256787743' role='forward' />\n    <member type='way' ref='370279599' role='forward' />\n    <member type='way' ref='38855245' role='forward' />\n    <member type='way' ref='38855344' role='forward' />\n    <member type='way' ref='512267983' role='forward' />\n    <member type='way' ref='38855347' role='forward' />\n    <member type='way' ref='370279632' role='forward' />\n    <member type='way' ref='93671846' role='forward' />\n    <member type='way' ref='370279522' role='forward' />\n    <member type='way' ref='256787746' role='forward' />\n    <member type='way' ref='165682403' role='forward' />\n    <member type='way' ref='417340368' role='forward' />\n    <member type='way' ref='417340355' role='forward' />\n    <member type='way' ref='256787730' role='forward' />\n    <member type='way' ref='256787733' role='forward' />\n    <member type='way' ref='256787729' role='forward' />\n    <member type='way' ref='406944785' role='forward' />\n    <member type='way' ref='397162138' role='forward' />\n    <member type='way' ref='256787744' role='forward' />\n    <member type='way' ref='131945791' role='forward' />\n    <member type='way' ref='256787746' role='forward' />\n    <member type='way' ref='370279522' role='forward' />\n    <member type='way' ref='93671846' role='forward' />\n    <member type='way' ref='370279632' role='forward' />\n    <member type='way' ref='38855347' role='forward' />\n    <member type='way' ref='512267983' role='forward' />\n    <member type='way' ref='38855344' role='forward' />\n    <member type='way' ref='38855245' role='forward' />\n    <member type='way' ref='370279599' role='forward' />\n    <member type='way' ref='256787743' role='forward' />\n    <member type='way' ref='370279528' role='forward' />\n    <member type='way' ref='93671776' role='forward' />\n    <member type='way' ref='256787740' role='forward' />\n    <member type='way' ref='256787739' role='forward' />\n    <member type='way' ref='256787729' role='forward' />\n    <member type='way' ref='406944785' role='forward' />\n    <member type='way' ref='397162138' role='forward' />\n    <member type='way' ref='256787732' role='forward' />\n    <member type='way' ref='165682404' role='forward' />\n    <member type='way' ref='417340374' role='' />\n    <member type='way' ref='417340364' role='' />\n    <member type='way' ref='417340361' role='' />\n    <member type='way' ref='417341645' role='' />\n    <member type='way' ref='417341640' role='' />\n    <member type='way' ref='417341659' role='' />\n    <member type='way' ref='417341638' role='' />\n    <member type='way' ref='417955390' role='' />\n    <member type='way' ref='417341661' role='' />\n    <member type='way' ref='417341641' role='' />\n    <member type='way' ref='417955387' role='' />\n    <member type='way' ref='417341643' role='' />\n    <member type='way' ref='266522061' role='' />\n    <member type='way' ref='417955389' role='' />\n    <member type='way' ref='417341653' role='' />\n    <member type='way' ref='417341639' role='' />\n    <member type='way' ref='417341663' role='' />\n    <member type='way' ref='417341647' role='' />\n    <member type='way' ref='417341656' role='' />\n    <member type='way' ref='417341642' role='' />\n    <member type='way' ref='396959635' role='' />\n    <member type='way' ref='417955388' role='' />\n    <member type='way' ref='396959640' role='' />\n    <member type='way' ref='396959638' role='' />\n    <member type='way' ref='396959636' role='' />\n    <member type='way' ref='396959641' role='' />\n    <member type='way' ref='396959639' role='' />\n    <member type='way' ref='396959649' role='' />\n    <member type='way' ref='396959637' role='' />\n    <member type='way' ref='165572790' role='' />\n    <member type='way' ref='396959634' role='' />\n    <member type='way' ref='396954370' role='forward' />\n    <member type='way' ref='165572801' role='forward' />\n    <member type='way' ref='396954373' role='forward' />\n    <member type='way' ref='165572799' role='forward' />\n    <member type='way' ref='619310159' role='forward' />\n    <member type='way' ref='289753824' role='forward' />\n    <member type='way' ref='289753826' role='forward' />\n    <member type='way' ref='396954385' role='forward' />\n    <member type='way' ref='396954378' role='forward' />\n    <member type='way' ref='396954375' role='forward' />\n    <member type='way' ref='396954374' role='forward' />\n    <member type='way' ref='165572796' role='forward' />\n    <member type='way' ref='417974881' role='forward' />\n    <member type='way' ref='165572798' role='forward' />\n    <member type='way' ref='417974870' role='forward' />\n    <member type='way' ref='396952972' role='forward' />\n    <member type='way' ref='396952973' role='forward' />\n    <member type='way' ref='513965702' role='forward' />\n    <member type='way' ref='513965701' role='forward' />\n    <member type='way' ref='165572795' role='forward' />\n    <member type='way' ref='396952971' role='forward' />\n    <member type='way' ref='396952970' role='forward' />\n    <member type='way' ref='396952976' role='forward' />\n    <member type='way' ref='396952969' role='forward' />\n    <member type='way' ref='417974871' role='forward' />\n    <member type='way' ref='165572792' role='forward' />\n    <member type='way' ref='396952968' role='forward' />\n    <member type='way' ref='157595326' role='forward' />\n    <member type='way' ref='396978746' role='forward' />\n    <member type='way' ref='8928561' role='forward' />\n    <member type='way' ref='396978745' role='forward' />\n    <member type='way' ref='217040733' role='forward' />\n    <member type='way' ref='396980821' role='forward' />\n    <member type='way' ref='157595330' role='forward' />\n    <member type='way' ref='396980013' role='forward' />\n    <member type='way' ref='256400242' role='forward' />\n    <member type='way' ref='217023614' role='forward' />\n    <member type='way' ref='217023611' role='forward' />\n    <member type='way' ref='396974118' role='forward' />\n    <member type='way' ref='396973568' role='forward' />\n    <member type='way' ref='396973569' role='forward' />\n    <member type='way' ref='396973101' role='forward' />\n    <member type='way' ref='406930552' role='forward' />\n    <member type='way' ref='406932558' role='forward' />\n    <member type='way' ref='406932557' role='forward' />\n    <member type='way' ref='396973102' role='forward' />\n    <member type='way' ref='396978628' role='forward' />\n    <member type='way' ref='396977657' role='forward' />\n    <member type='way' ref='396977662' role='forward' />\n    <member type='way' ref='396977647' role='forward' />\n    <member type='way' ref='396977652' role='forward' />\n    <member type='way' ref='397098334' role='forward' />\n    <member type='way' ref='397098333' role='forward' />\n    <member type='way' ref='309857708' role='forward' />\n    <member type='way' ref='397098418' role='forward' />\n    <member type='way' ref='397098419' role='forward' />\n    <member type='way' ref='397098776' role='forward' />\n    <member type='way' ref='397098844' role='forward' />\n    <member type='way' ref='397098843' role='forward' />\n    <member type='way' ref='397098979' role='forward' />\n    <member type='way' ref='515603103' role='forward' />\n    <member type='way' ref='397161975' role='forward' />\n    <member type='way' ref='397098978' role='forward' />\n    <member type='way' ref='217020420' role='forward' />\n    <member type='way' ref='396989457' role='forward' />\n    <member type='way' ref='514677012' role='forward' />\n    <member type='way' ref='397093229' role='forward' />\n    <member type='way' ref='396989456' role='forward' />\n    <member type='way' ref='217020442' role='forward' />\n    <member type='way' ref='397159762' role='forward' />\n    <member type='way' ref='397159752' role='forward' />\n    <member type='way' ref='396959645' role='forward' />\n    <member type='way' ref='396954372' role='forward' />\n    <member type='way' ref='217040726' role='forward' />\n    <member type='way' ref='417974867' role='forward' />\n    <member type='way' ref='417974866' role='forward' />\n    <member type='way' ref='396954371' role='forward' />\n    <member type='way' ref='619310160' role='forward' />\n    <member type='way' ref='289753822' role='forward' />\n    <member type='way' ref='289753823' role='forward' />\n    <member type='way' ref='417974879' role='forward' />\n    <member type='way' ref='396954376' role='forward' />\n    <member type='way' ref='165572797' role='forward' />\n    <member type='way' ref='417974873' role='forward' />\n    <member type='way' ref='417974878' role='forward' />\n    <member type='way' ref='417974880' role='forward' />\n    <member type='way' ref='417974869' role='forward' />\n    <member type='way' ref='165572793' role='forward' />\n    <member type='way' ref='417974876' role='forward' />\n    <member type='way' ref='417974875' role='forward' />\n    <member type='way' ref='165572789' role='forward' />\n    <member type='way' ref='417974874' role='forward' />\n    <member type='way' ref='396952967' role='forward' />\n    <member type='way' ref='396952975' role='forward' />\n    <member type='way' ref='417974882' role='forward' />\n    <member type='way' ref='417974872' role='forward' />\n    <member type='way' ref='165572794' role='forward' />\n    <member type='way' ref='396952974' role='forward' />\n    <member type='way' ref='417974877' role='forward' />\n    <member type='way' ref='165572791' role='forward' />\n    <member type='way' ref='51870255' role='forward' />\n    <member type='way' ref='157595328' role='forward' />\n    <member type='way' ref='396979196' role='forward' />\n    <member type='way' ref='157595336' role='forward' />\n    <member type='way' ref='417336633' role='forward' />\n    <member type='way' ref='449493705' role='forward' />\n    <member type='way' ref='396979611' role='forward' />\n    <member type='way' ref='396979612' role='forward' />\n    <member type='way' ref='396979613' role='forward' />\n    <member type='way' ref='417336601' role='forward' />\n    <member type='way' ref='217023604' role='forward' />\n    <member type='way' ref='217023607' role='forward' />\n    <member type='way' ref='309857707' role='forward' />\n    <member type='way' ref='396974012' role='forward' />\n    <member type='way' ref='396976112' role='forward' />\n    <member type='way' ref='396976113' role='forward' />\n    <member type='way' ref='514687452' role='forward' />\n    <member type='way' ref='396974013' role='forward' />\n    <member type='way' ref='396976114' role='forward' />\n    <member type='way' ref='406932559' role='forward' />\n    <member type='way' ref='514684647' role='forward' />\n    <member type='way' ref='464912453' role='forward' />\n    <member type='way' ref='515759293' role='forward' />\n    <member type='way' ref='514683513' role='forward' />\n    <member type='way' ref='256744842' role='forward' />\n    <member type='way' ref='506711642' role='forward' />\n    <member type='way' ref='28947904' role='forward' />\n    <member type='way' ref='515756367' role='forward' />\n    <member type='way' ref='309857713' role='forward' />\n    <member type='way' ref='515603104' role='forward' />\n    <member type='way' ref='464909272' role='forward' />\n    <member type='way' ref='464909273' role='forward' />\n    <member type='way' ref='396989858' role='forward' />\n    <member type='way' ref='514675272' role='forward' />\n    <member type='way' ref='396989857' role='forward' />\n    <member type='way' ref='513972613' role='forward' />\n    <member type='way' ref='217020427' role='forward' />\n    <member type='way' ref='514675731' role='forward' />\n    <member type='way' ref='397093425' role='forward' />\n    <member type='way' ref='397093426' role='forward' />\n    <member type='way' ref='397093707' role='forward' />\n    <member type='way' ref='217020443' role='forward' />\n    <member type='way' ref='397159757' role='forward' />\n    <member type='way' ref='217020417' role='' />\n    <member type='way' ref='217020553' role='forward' />\n    <member type='way' ref='217020416' role='forward' />\n    <member type='way' ref='417201974' role='' />\n    <member type='way' ref='417201980' role='' />\n    <member type='way' ref='217020552' role='' />\n    <member type='way' ref='415506697' role='' />\n    <member type='way' ref='417201983' role='' />\n    <member type='way' ref='415505962' role='' />\n    <member type='way' ref='123690575' role='forward' />\n    <member type='way' ref='397097443' role='forward' />\n    <member type='way' ref='415504889' role='forward' />\n    <member type='way' ref='138102027' role='forward' />\n    <member type='way' ref='397096336' role='forward' />\n    <member type='way' ref='397096345' role='forward' />\n    <member type='way' ref='397096354' role='forward' />\n    <member type='way' ref='123690568' role='forward' />\n    <member type='way' ref='415504890' role='forward' />\n    <member type='way' ref='397096482' role='forward' />\n    <member type='way' ref='397096359' role='forward' />\n    <member type='way' ref='631871331' role='forward' />\n    <member type='way' ref='397096341' role='forward' />\n    <member type='way' ref='397096350' role='forward' />\n    <member type='way' ref='123690556' role='' />\n    <member type='way' ref='309857690' role='' />\n    <member type='way' ref='397094035' role='' />\n    <member type='way' ref='397094032' role='' />\n    <member type='way' ref='417197059' role='' />\n    <member type='way' ref='397094031' role='' />\n    <member type='way' ref='417197078' role='' />\n    <member type='way' ref='397094033' role='' />\n    <member type='way' ref='417197097' role='' />\n    <member type='way' ref='417197116' role='' />\n    <member type='way' ref='397094034' role='' />\n    <member type='way' ref='397094336' role='' />\n    <member type='way' ref='534655195' role='' />\n    <member type='way' ref='397094519' role='' />\n    <member type='way' ref='534655201' role='' />\n    <member type='way' ref='397098496' role='' />\n    <member type='way' ref='417197038' role='' />\n    <member type='way' ref='309857692' role='' />\n    <member type='way' ref='397097526' role='forward' />\n    <member type='way' ref='133755888' role='forward' />\n    <member type='way' ref='133755883' role='forward' />\n    <member type='way' ref='8919551' role='forward' />\n    <member type='way' ref='397097523' role='forward' />\n    <member type='way' ref='397097525' role='forward' />\n    <member type='way' ref='30758272' role='forward' />\n    <member type='way' ref='397096465' role='forward' />\n    <member type='way' ref='224373744' role='forward' />\n    <member type='way' ref='254299114' role='forward' />\n    <member type='way' ref='397093352' role='forward' />\n    <member type='way' ref='255178045' role='forward' />\n    <member type='way' ref='397093355' role='forward' />\n    <member type='way' ref='224373743' role='forward' />\n    <member type='way' ref='515859660' role='forward' />\n    <member type='way' ref='397115308' role='forward' />\n    <member type='way' ref='513966285' role='forward' />\n    <member type='way' ref='27041808' role='forward' />\n    <member type='way' ref='415497724' role='forward' />\n    <member type='way' ref='397115300' role='forward' />\n    <member type='way' ref='61558775' role='forward' />\n    <member type='way' ref='27652472' role='forward' />\n    <member type='way' ref='397121962' role='forward' />\n    <member type='way' ref='27613598' role='forward' />\n    <member type='way' ref='27613597' role='forward' />\n    <member type='way' ref='549278887' role='forward' />\n    <member type='way' ref='309857710' role='forward' />\n    <member type='way' ref='397097524' role='forward' />\n    <member type='way' ref='30758299' role='forward' />\n    <member type='way' ref='397159153' role='forward' />\n    <member type='way' ref='224373745' role='forward' />\n    <member type='way' ref='397096384' role='forward' />\n    <member type='way' ref='254299116' role='forward' />\n    <member type='way' ref='397093353' role='forward' />\n    <member type='way' ref='283530828' role='forward' />\n    <member type='way' ref='254299115' role='forward' />\n    <member type='way' ref='27041812' role='forward' />\n    <member type='way' ref='133755885' role='forward' />\n    <member type='way' ref='397115312' role='forward' />\n    <member type='way' ref='515859661' role='forward' />\n    <member type='way' ref='397115548' role='forward' />\n    <member type='way' ref='254308546' role='forward' />\n    <member type='way' ref='397094265' role='forward' />\n    <member type='way' ref='397094263' role='forward' />\n    <member type='way' ref='397094264' role='forward' />\n    <member type='way' ref='525066119' role='forward' />\n    <member type='way' ref='255351707' role='forward' />\n    <member type='way' ref='26786450' role='forward' />\n    <member type='way' ref='549278886' role='forward' />\n    <member type='way' ref='27019938' role='' />\n    <member type='way' ref='417388593' role='' />\n    <member type='way' ref='397121963' role='' />\n    <member type='way' ref='397121964' role='' />\n    <member type='way' ref='397120476' role='' />\n    <member type='way' ref='469387973' role='' />\n    <member type='way' ref='224373742' role='' />\n    <member type='way' ref='397120478' role='' />\n    <member type='way' ref='397120479' role='' />\n    <member type='way' ref='397120484' role='' />\n    <member type='way' ref='397120482' role='' />\n    <member type='way' ref='397120483' role='' />\n    <member type='way' ref='397120480' role='' />\n    <member type='way' ref='397120481' role='' />\n    <member type='way' ref='397120477' role='' />\n    <member type='way' ref='418522258' role='' />\n    <member type='way' ref='469387974' role='' />\n    <member type='way' ref='469387975' role='' />\n    <member type='way' ref='469387976' role='' />\n    <member type='way' ref='23925325' role='' />\n    <member type='way' ref='406944760' role='' />\n    <member type='way' ref='397094082' role='forward' />\n    <member type='way' ref='186400363' role='forward' />\n    <member type='way' ref='225516691' role='forward' />\n    <member type='way' ref='469387979' role='forward' />\n    <member type='way' ref='397094088' role='forward' />\n    <member type='way' ref='397094768' role='forward' />\n    <member type='way' ref='397094767' role='forward' />\n    <member type='way' ref='513693876' role='forward' />\n    <member type='way' ref='225516682' role='forward' />\n    <member type='way' ref='397266204' role='forward' />\n    <member type='way' ref='469387980' role='forward' />\n    <member type='way' ref='225854641' role='forward' />\n    <member type='way' ref='397271282' role='forward' />\n    <member type='way' ref='397271281' role='forward' />\n    <member type='way' ref='8919576' role='forward' />\n    <member type='way' ref='525013804' role='forward' />\n    <member type='way' ref='133781814' role='forward' />\n    <member type='way' ref='8915027' role='forward' />\n    <member type='way' ref='483931210' role='forward' />\n    <member type='way' ref='43101729' role='forward' />\n    <member type='way' ref='397138962' role='forward' />\n    <member type='way' ref='397138961' role='forward' />\n    <member type='way' ref='23884749' role='forward' />\n    <member type='way' ref='24138759' role='forward' />\n    <member type='way' ref='397138960' role='forward' />\n    <member type='way' ref='224384023' role='forward' />\n    <member type='way' ref='397125841' role='forward' />\n    <member type='way' ref='224384022' role='forward' />\n    <member type='way' ref='283535051' role='forward' />\n    <member type='way' ref='415470419' role='forward' />\n    <member type='way' ref='283535236' role='forward' />\n    <member type='way' ref='513698622' role='forward' />\n    <member type='way' ref='397125828' role='forward' />\n    <member type='way' ref='397125829' role='forward' />\n    <member type='way' ref='397125834' role='forward' />\n    <member type='way' ref='397125830' role='forward' />\n    <member type='way' ref='397125838' role='forward' />\n    <member type='way' ref='397125839' role='forward' />\n    <member type='way' ref='397125835' role='forward' />\n    <member type='way' ref='417392979' role='forward' />\n    <member type='way' ref='397125840' role='forward' />\n    <member type='way' ref='469387977' role='forward' />\n    <member type='way' ref='477821993' role='forward' />\n    <member type='way' ref='225516688' role='forward' />\n    <member type='way' ref='133781699' role='forward' />\n    <member type='way' ref='397094086' role='forward' />\n    <member type='way' ref='186400364' role='forward' />\n    <member type='way' ref='225516679' role='forward' />\n    <member type='way' ref='225854642' role='forward' />\n    <member type='way' ref='397094765' role='forward' />\n    <member type='way' ref='397094766' role='forward' />\n    <member type='way' ref='514611457' role='forward' />\n    <member type='way' ref='395279939' role='forward' />\n    <member type='way' ref='417385440' role='forward' />\n    <member type='way' ref='397266207' role='forward' />\n    <member type='way' ref='397266206' role='forward' />\n    <member type='way' ref='397270469' role='forward' />\n    <member type='way' ref='27093595' role='forward' />\n    <member type='way' ref='31498097' role='forward' />\n    <member type='way' ref='8915454' role='forward' />\n    <member type='way' ref='186400374' role='forward' />\n    <member type='way' ref='254750816' role='forward' />\n    <member type='way' ref='397267195' role='forward' />\n    <member type='way' ref='397267194' role='forward' />\n    <member type='way' ref='397267193' role='forward' />\n    <member type='way' ref='397267192' role='forward' />\n    <member type='way' ref='283535238' role='forward' />\n    <member type='way' ref='397257567' role='forward' />\n    <member type='way' ref='397125832' role='forward' />\n    <member type='way' ref='397125833' role='forward' />\n    <member type='way' ref='397125837' role='forward' />\n    <member type='way' ref='397125831' role='forward' />\n    <member type='way' ref='415469052' role='forward' />\n    <member type='way' ref='8917043' role='forward' />\n    <member type='way' ref='397125836' role='forward' />\n    <member type='way' ref='397125842' role='forward' />\n    <member type='way' ref='24138763' role='' />\n    <member type='way' ref='397130840' role='' />\n    <member type='way' ref='397130841' role='' />\n    <member type='way' ref='110797422' role='' />\n    <member type='way' ref='397130497' role='' />\n    <member type='way' ref='397130093' role='' />\n    <member type='way' ref='397129776' role='' />\n    <member type='way' ref='397131363' role='' />\n    <member type='way' ref='397131364' role='' />\n    <member type='way' ref='397137031' role='' />\n    <member type='way' ref='254750815' role='' />\n    <member type='way' ref='397138005' role='' />\n    <member type='way' ref='397138006' role='' />\n    <member type='way' ref='186400371' role='forward' />\n    <member type='way' ref='417392975' role='forward' />\n    <member type='way' ref='8917337' role='forward' />\n    <member type='way' ref='162931810' role='forward' />\n    <member type='way' ref='256787755' role='forward' />\n    <member type='way' ref='91181769' role='forward' />\n    <member type='way' ref='88572874' role='forward' />\n    <member type='way' ref='483432850' role='forward' />\n    <member type='way' ref='254299377' role='forward' />\n    <member type='way' ref='26943711' role='forward' />\n    <member type='way' ref='397138338' role='forward' />\n    <member type='way' ref='397093080' role='forward' />\n    <member type='way' ref='27029265' role='forward' />\n    <member type='way' ref='27029264' role='forward' />\n    <member type='way' ref='27029263' role='' />\n    <member type='way' ref='24335441' role='forward' />\n    <member type='way' ref='397118327' role='forward' />\n    <member type='way' ref='34128773' role='forward' />\n    <member type='way' ref='513963921' role='forward' />\n    <member type='way' ref='224373752' role='forward' />\n    <member type='way' ref='8917791' role='forward' />\n    <member type='way' ref='417087270' role='forward' />\n    <member type='way' ref='417087260' role='forward' />\n    <member type='way' ref='417087260' role='forward' />\n    <member type='way' ref='417087270' role='forward' />\n    <member type='way' ref='8917791' role='forward' />\n    <member type='way' ref='224373752' role='forward' />\n    <member type='way' ref='513963921' role='forward' />\n    <member type='way' ref='34128773' role='forward' />\n    <member type='way' ref='397118327' role='forward' />\n    <member type='way' ref='24335441' role='forward' />\n    <member type='way' ref='27029263' role='' />\n    <member type='way' ref='88572874' role='' />\n    <member type='way' ref='417087485' role='' />\n    <member type='way' ref='88572887' role='' />\n    <member type='way' ref='88572914' role='' />\n    <member type='way' ref='88572941' role='' />\n    <member type='way' ref='88572932' role='' />\n    <member type='way' ref='51392400' role='' />\n    <member type='way' ref='51392402' role='' />\n    <member type='way' ref='111560680' role='' />\n    <member type='way' ref='86334994' role='forward' />\n    <member type='way' ref='111560679' role='forward' />\n    <member type='way' ref='88572965' role='' />\n    <member type='way' ref='88559669' role='' />\n    <member type='way' ref='88572877' role='' />\n    <member type='way' ref='417101712' role='' />\n    <member type='way' ref='88572970' role='' />\n    <tag k='FIXME' v='does it somehow use Howard in SF?' />\n    <tag k='name' v='SamTrans 397' />\n    <tag k='network' v='SamTrans' />\n    <tag k='operator' v='San Mateo County Transit District' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='397' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8588115' timestamp='2018-08-23T18:43:50Z' uid='7231011' user='jho1' version='1' changeset='61930455'>\n    <member type='way' ref='26943758' role='from' />\n    <member type='node' ref='65315504' role='via' />\n    <member type='way' ref='8919269' role='to' />\n    <tag k='restriction' v='no_left_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='8592257' timestamp='2018-10-15T22:03:43Z' uid='2237750' user='chachafish' version='7' changeset='63557245'>\n    <member type='way' ref='224384023' role='' />\n    <member type='way' ref='397125841' role='' />\n    <member type='way' ref='389359771' role='' />\n    <member type='way' ref='224384048' role='' />\n    <member type='way' ref='389359766' role='' />\n    <member type='way' ref='389359770' role='' />\n    <member type='way' ref='389359767' role='' />\n    <member type='way' ref='24547841' role='' />\n    <member type='way' ref='24547840' role='' />\n    <member type='way' ref='133781849' role='' />\n    <member type='way' ref='224384049' role='' />\n    <member type='way' ref='224384026' role='' />\n    <member type='way' ref='278713839' role='' />\n    <member type='way' ref='224384058' role='' />\n    <member type='way' ref='224384036' role='' />\n    <member type='way' ref='225847539' role='' />\n    <member type='way' ref='224384055' role='' />\n    <member type='way' ref='8917776' role='' />\n    <member type='way' ref='8920710' role='' />\n    <member type='way' ref='224384051' role='' />\n    <member type='way' ref='224384038' role='' />\n    <member type='way' ref='224384046' role='' />\n    <member type='way' ref='224384047' role='' />\n    <member type='way' ref='29482568' role='' />\n    <member type='way' ref='29482562' role='' />\n    <member type='way' ref='29482560' role='' />\n    <member type='way' ref='29482558' role='' />\n    <member type='way' ref='224384012' role='' />\n    <member type='way' ref='224384042' role='' />\n    <member type='way' ref='620728090' role='' />\n    <member type='way' ref='8916338' role='' />\n    <member type='way' ref='218146840' role='' />\n    <member type='way' ref='133735125' role='' />\n    <member type='way' ref='221434104' role='' />\n    <member type='way' ref='84823308' role='' />\n    <member type='way' ref='133755923' role='' />\n    <member type='way' ref='442572410' role='' />\n    <member type='way' ref='442572411' role='' />\n    <member type='way' ref='442572412' role='' />\n    <member type='way' ref='442572413' role='' />\n    <member type='way' ref='143617587' role='' />\n    <member type='way' ref='619568404' role='' />\n    <member type='way' ref='525564024' role='' />\n    <member type='way' ref='143617589' role='' />\n    <member type='way' ref='111550911' role='' />\n    <member type='way' ref='417396747' role='' />\n    <member type='way' ref='88559680' role='' />\n    <member type='way' ref='417396753' role='' />\n    <member type='way' ref='397094803' role='' />\n    <member type='way' ref='88559661' role='' />\n    <member type='way' ref='397094802' role='' />\n    <member type='way' ref='225518512' role='' />\n    <member type='way' ref='467979192' role='' />\n    <member type='way' ref='88559691' role='' />\n    <member type='way' ref='633791726' role='' />\n    <member type='way' ref='88597577' role='' />\n    <member type='way' ref='417101702' role='' />\n    <member type='way' ref='619979102' role='' />\n    <member type='way' ref='224384018' role='' />\n    <member type='way' ref='224384027' role='' />\n    <member type='way' ref='550652206' role='' />\n    <member type='way' ref='8920734' role='' />\n    <member type='way' ref='224316527' role='' />\n    <member type='way' ref='254971068' role='' />\n    <member type='way' ref='254396719' role='' />\n    <member type='way' ref='437935727' role='' />\n    <member type='way' ref='254756526' role='' />\n    <member type='way' ref='254971069' role='' />\n    <member type='way' ref='25372358' role='' />\n    <member type='way' ref='425516652' role='' />\n    <member type='way' ref='598020858' role='' />\n    <member type='way' ref='224384020' role='' />\n    <member type='way' ref='514675661' role='' />\n    <member type='way' ref='416878321' role='' />\n    <member type='way' ref='24335485' role='' />\n    <member type='way' ref='399142440' role='' />\n    <member type='way' ref='398849856' role='' />\n    <member type='way' ref='225518506' role='' />\n    <member type='way' ref='225518504' role='' />\n    <member type='way' ref='527857397' role='' />\n    <member type='way' ref='27050841' role='' />\n    <tag k='from' v='24th Street &amp; Potrero Avenue' />\n    <tag k='name' v='10-Townsend: Inbound to Pacific Heights (other times)' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='10' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Jackson St &amp; Webster St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8592258' timestamp='2018-10-15T22:03:42Z' uid='2237750' user='chachafish' version='6' changeset='63557245'>\n    <member type='way' ref='398849856' role='' />\n    <member type='way' ref='399142440' role='' />\n    <member type='way' ref='24335485' role='' />\n    <member type='way' ref='397264925' role='' />\n    <member type='way' ref='24335486' role='' />\n    <member type='way' ref='514671064' role='' />\n    <member type='way' ref='514671065' role='' />\n    <member type='way' ref='514671063' role='' />\n    <member type='way' ref='87376660' role='' />\n    <member type='way' ref='588724745' role='' />\n    <member type='way' ref='87376695' role='' />\n    <member type='way' ref='87376682' role='' />\n    <member type='way' ref='87376679' role='' />\n    <member type='way' ref='406580281' role='' />\n    <member type='way' ref='514675663' role='' />\n    <member type='way' ref='87376703' role='' />\n    <member type='way' ref='598020853' role='' />\n    <member type='way' ref='263819574' role='' />\n    <member type='way' ref='598020854' role='' />\n    <member type='way' ref='598020855' role='' />\n    <member type='way' ref='598020852' role='' />\n    <member type='way' ref='87376675' role='' />\n    <member type='way' ref='397094228' role='' />\n    <member type='way' ref='224384028' role='' />\n    <member type='way' ref='598020858' role='' />\n    <member type='way' ref='425516652' role='' />\n    <member type='way' ref='25372358' role='' />\n    <member type='way' ref='254971069' role='' />\n    <member type='way' ref='254756526' role='' />\n    <member type='way' ref='437935727' role='' />\n    <member type='way' ref='254396719' role='' />\n    <member type='way' ref='254971068' role='' />\n    <member type='way' ref='224316527' role='' />\n    <member type='way' ref='8920734' role='' />\n    <member type='way' ref='550652206' role='' />\n    <member type='way' ref='224384027' role='' />\n    <member type='way' ref='224384018' role='' />\n    <member type='way' ref='619979102' role='' />\n    <member type='way' ref='417101702' role='' />\n    <member type='way' ref='88597577' role='' />\n    <member type='way' ref='633791726' role='' />\n    <member type='way' ref='88559691' role='' />\n    <member type='way' ref='467979192' role='' />\n    <member type='way' ref='225518512' role='' />\n    <member type='way' ref='397094802' role='' />\n    <member type='way' ref='88559661' role='' />\n    <member type='way' ref='397094803' role='' />\n    <member type='way' ref='417396753' role='' />\n    <member type='way' ref='88559680' role='' />\n    <member type='way' ref='417396747' role='' />\n    <member type='way' ref='111550911' role='' />\n    <member type='way' ref='143617589' role='' />\n    <member type='way' ref='525564024' role='' />\n    <member type='way' ref='619568404' role='' />\n    <member type='way' ref='143617587' role='' />\n    <member type='way' ref='442572413' role='' />\n    <member type='way' ref='442572412' role='' />\n    <member type='way' ref='442572411' role='' />\n    <member type='way' ref='442572410' role='' />\n    <member type='way' ref='133755923' role='' />\n    <member type='way' ref='84823308' role='' />\n    <member type='way' ref='221434104' role='' />\n    <member type='way' ref='218146865' role='' />\n    <member type='way' ref='218146840' role='' />\n    <member type='way' ref='8916338' role='' />\n    <member type='way' ref='620728090' role='' />\n    <member type='way' ref='224384025' role='' />\n    <member type='way' ref='224384044' role='' />\n    <member type='way' ref='29482558' role='' />\n    <member type='way' ref='29482560' role='' />\n    <member type='way' ref='29482562' role='' />\n    <member type='way' ref='29482568' role='' />\n    <member type='way' ref='224384047' role='' />\n    <member type='way' ref='224384009' role='' />\n    <member type='way' ref='8920710' role='' />\n    <member type='way' ref='8917776' role='' />\n    <member type='way' ref='224384055' role='' />\n    <member type='way' ref='225847539' role='' />\n    <member type='way' ref='224384036' role='' />\n    <member type='way' ref='224384058' role='' />\n    <member type='way' ref='224384057' role='' />\n    <member type='way' ref='283080370' role='' />\n    <member type='way' ref='110472619' role='' />\n    <member type='way' ref='133781849' role='' />\n    <member type='way' ref='24547840' role='' />\n    <member type='way' ref='24547841' role='' />\n    <member type='way' ref='389359767' role='' />\n    <member type='way' ref='389359770' role='' />\n    <member type='way' ref='389359766' role='' />\n    <member type='way' ref='8919650' role='' />\n    <member type='way' ref='224384054' role='' />\n    <tag k='from' v='Jackson St &amp; Fillmore St' />\n    <tag k='name' v='10-Townsend: Outbound to San Francisco General Hospital (other times)' />\n    <tag k='network' v='Muni' />\n    <tag k='operator' v='San Francisco Municipal Railway' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='10' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='23rd St &amp; Utah St' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8610460' timestamp='2018-08-28T21:50:51Z' uid='8107451' user='njtbusfan' version='1' changeset='62086023'>\n    <member type='way' ref='396947356' role='forward' />\n    <member type='way' ref='52432936' role='forward' />\n    <member type='way' ref='160313264' role='forward' />\n    <member type='way' ref='160313263' role='forward' />\n    <member type='way' ref='396949640' role='forward' />\n    <member type='way' ref='396949646' role='forward' />\n    <member type='way' ref='46594526' role='forward' />\n    <member type='way' ref='46594933' role='forward' />\n    <member type='way' ref='375747150' role='forward' />\n    <member type='way' ref='375747145' role='forward' />\n    <member type='way' ref='375747147' role='forward' />\n    <member type='way' ref='530592671' role='forward' />\n    <member type='way' ref='619568413' role='forward' />\n    <member type='way' ref='283530638' role='forward' />\n    <member type='way' ref='437921062' role='forward' />\n    <member type='way' ref='516940745' role='forward' />\n    <member type='way' ref='27512051' role='forward' />\n    <member type='way' ref='28843966' role='forward' />\n    <member type='way' ref='396952203' role='forward' />\n    <member type='way' ref='396957211' role='forward' />\n    <member type='way' ref='620728084' role='forward' />\n    <member type='way' ref='396957209' role='forward' />\n    <member type='way' ref='283530637' role='forward' />\n    <member type='way' ref='396957213' role='forward' />\n    <member type='way' ref='396960684' role='forward' />\n    <member type='way' ref='32396142' role='forward' />\n    <member type='way' ref='417392943' role='forward' />\n    <member type='way' ref='396960685' role='forward' />\n    <member type='way' ref='397250666' role='forward' />\n    <member type='way' ref='516940743' role='forward' />\n    <member type='way' ref='283886274' role='forward' />\n    <member type='way' ref='397250674' role='forward' />\n    <member type='way' ref='251511536' role='forward' />\n    <member type='way' ref='396955704' role='forward' />\n    <member type='way' ref='396955701' role='forward' />\n    <member type='way' ref='36331085' role='forward' />\n    <member type='way' ref='215524529' role='forward' />\n    <member type='way' ref='37177689' role='forward' />\n    <member type='way' ref='620728087' role='forward' />\n    <member type='way' ref='37177789' role='forward' />\n    <member type='way' ref='133755930' role='forward' />\n    <member type='way' ref='396945233' role='forward' />\n    <member type='way' ref='27656675' role='forward' />\n    <member type='way' ref='26943623' role='forward' />\n    <member type='way' ref='396945235' role='forward' />\n    <member type='way' ref='513707473' role='forward' />\n    <member type='way' ref='133735130' role='forward' />\n    <member type='way' ref='437913409' role='forward' />\n    <member type='way' ref='417396764' role='forward' />\n    <member type='way' ref='417396772' role='forward' />\n    <member type='way' ref='620169772' role='forward' />\n    <member type='way' ref='417396768' role='forward' />\n    <member type='way' ref='254299142' role='forward' />\n    <member type='way' ref='397104880' role='forward' />\n    <member type='way' ref='397144264' role='forward' />\n    <member type='way' ref='397144265' role='forward' />\n    <member type='way' ref='397144001' role='forward' />\n    <member type='way' ref='88572965' role='forward' />\n    <member type='way' ref='88559669' role='forward' />\n    <member type='way' ref='88597577' role='forward' />\n    <member type='way' ref='417101702' role='forward' />\n    <member type='way' ref='619979102' role='forward' />\n    <member type='way' ref='224384018' role='forward' />\n    <member type='way' ref='224200967' role='forward' />\n    <member type='way' ref='619979118' role='forward' />\n    <member type='way' ref='8919020' role='forward' />\n    <member type='way' ref='417101701' role='forward' />\n    <member type='way' ref='417101700' role='forward' />\n    <member type='way' ref='88572905' role='forward' />\n    <member type='way' ref='175507524' role='forward' />\n    <member type='way' ref='175507523' role='forward' />\n    <member type='way' ref='417101699' role='forward' />\n    <member type='way' ref='406944764' role='forward' />\n    <member type='way' ref='467979192' role='backward' />\n    <member type='way' ref='225518512' role='backward' />\n    <member type='way' ref='88559702' role='forward' />\n    <member type='way' ref='25758528' role='forward' />\n    <member type='way' ref='397146078' role='forward' />\n    <member type='way' ref='143617591' role='forward' />\n    <member type='way' ref='375744879' role='forward' />\n    <member type='way' ref='396937568' role='forward' />\n    <member type='way' ref='396937567' role='forward' />\n    <member type='way' ref='513706522' role='forward' />\n    <member type='way' ref='107674372' role='forward' />\n    <member type='way' ref='396937570' role='forward' />\n    <member type='way' ref='255330069' role='forward' />\n    <member type='way' ref='26943653' role='forward' />\n    <member type='way' ref='619568405' role='forward' />\n    <member type='way' ref='396937569' role='forward' />\n    <member type='way' ref='52432939' role='forward' />\n    <member type='way' ref='396947359' role='forward' />\n    <member type='way' ref='255330070' role='forward' />\n    <tag k='name' v='Mission Bay TransBay/Caltrain Route (PM)' />\n    <tag k='network' v='Mission Bay' />\n    <tag k='operator' v='Mission Bay Transportation Management Association' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='TransBay/Caltrain' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8610461' timestamp='2018-10-14T04:37:02Z' uid='3130496' user='bhalperin28' version='2' changeset='63499215'>\n    <member type='way' ref='396947356' role='forward' />\n    <member type='way' ref='52432936' role='forward' />\n    <member type='way' ref='160313264' role='forward' />\n    <member type='way' ref='160313263' role='forward' />\n    <member type='way' ref='396949640' role='forward' />\n    <member type='way' ref='396949646' role='forward' />\n    <member type='way' ref='46594526' role='forward' />\n    <member type='way' ref='46594933' role='forward' />\n    <member type='way' ref='375747150' role='forward' />\n    <member type='way' ref='375747145' role='forward' />\n    <member type='way' ref='375747147' role='forward' />\n    <member type='way' ref='530592671' role='forward' />\n    <member type='way' ref='619568413' role='forward' />\n    <member type='way' ref='283530638' role='forward' />\n    <member type='way' ref='437921062' role='forward' />\n    <member type='way' ref='516940745' role='forward' />\n    <member type='way' ref='27512051' role='forward' />\n    <member type='way' ref='28843966' role='forward' />\n    <member type='way' ref='396952203' role='forward' />\n    <member type='way' ref='396957211' role='forward' />\n    <member type='way' ref='620728084' role='forward' />\n    <member type='way' ref='396957209' role='forward' />\n    <member type='way' ref='283530637' role='forward' />\n    <member type='way' ref='396957213' role='forward' />\n    <member type='way' ref='396960684' role='forward' />\n    <member type='way' ref='32396142' role='forward' />\n    <member type='way' ref='417392943' role='forward' />\n    <member type='way' ref='396960685' role='forward' />\n    <member type='way' ref='397250666' role='forward' />\n    <member type='way' ref='516940743' role='forward' />\n    <member type='way' ref='283886274' role='forward' />\n    <member type='way' ref='397250674' role='forward' />\n    <member type='way' ref='251511536' role='forward' />\n    <member type='way' ref='396955704' role='forward' />\n    <member type='way' ref='396955701' role='forward' />\n    <member type='way' ref='36331085' role='forward' />\n    <member type='way' ref='215524529' role='forward' />\n    <member type='way' ref='37177689' role='forward' />\n    <member type='way' ref='620728087' role='forward' />\n    <member type='way' ref='37177789' role='forward' />\n    <member type='way' ref='133755930' role='forward' />\n    <member type='way' ref='396945233' role='forward' />\n    <member type='way' ref='27656675' role='forward' />\n    <member type='way' ref='26943623' role='forward' />\n    <member type='way' ref='396945235' role='forward' />\n    <member type='way' ref='513707473' role='forward' />\n    <member type='way' ref='133735130' role='forward' />\n    <member type='way' ref='437913409' role='forward' />\n    <member type='way' ref='417396764' role='forward' />\n    <member type='way' ref='417396772' role='forward' />\n    <member type='way' ref='620169772' role='forward' />\n    <member type='way' ref='417396768' role='forward' />\n    <member type='way' ref='88559702' role='backward' />\n    <member type='way' ref='225518512' role='forward' />\n    <member type='way' ref='467979192' role='forward' />\n    <member type='way' ref='88559691' role='forward' />\n    <member type='way' ref='633791726' role='forward' />\n    <member type='way' ref='88597577' role='forward' />\n    <member type='way' ref='417101702' role='forward' />\n    <member type='way' ref='619979102' role='forward' />\n    <member type='way' ref='224384018' role='forward' />\n    <member type='way' ref='224200967' role='forward' />\n    <member type='way' ref='619979118' role='forward' />\n    <member type='way' ref='8919020' role='forward' />\n    <member type='way' ref='417101701' role='forward' />\n    <member type='way' ref='417101700' role='forward' />\n    <member type='way' ref='88572905' role='forward' />\n    <member type='way' ref='175507524' role='forward' />\n    <member type='way' ref='175507523' role='forward' />\n    <member type='way' ref='417101699' role='forward' />\n    <member type='way' ref='406944764' role='forward' />\n    <member type='way' ref='417400728' role='forward' />\n    <member type='way' ref='417400734' role='forward' />\n    <member type='way' ref='406944765' role='forward' />\n    <member type='way' ref='406944771' role='forward' />\n    <member type='way' ref='406944774' role='forward' />\n    <member type='way' ref='406944767' role='forward' />\n    <member type='way' ref='397145821' role='forward' />\n    <member type='way' ref='397145254' role='forward' />\n    <member type='way' ref='397145255' role='forward' />\n    <member type='way' ref='408600296' role='forward' />\n    <member type='way' ref='417396782' role='forward' />\n    <member type='way' ref='143617591' role='forward' />\n    <member type='way' ref='375744879' role='forward' />\n    <member type='way' ref='396937568' role='forward' />\n    <member type='way' ref='396937567' role='forward' />\n    <member type='way' ref='513706522' role='forward' />\n    <member type='way' ref='107674372' role='forward' />\n    <member type='way' ref='396937570' role='forward' />\n    <member type='way' ref='255330069' role='forward' />\n    <member type='way' ref='26943653' role='forward' />\n    <member type='way' ref='619568405' role='forward' />\n    <member type='way' ref='396937569' role='forward' />\n    <member type='way' ref='52432939' role='forward' />\n    <member type='way' ref='396947359' role='forward' />\n    <member type='way' ref='255330070' role='forward' />\n    <tag k='name' v='Mission Bay TransBay/Caltrain Route (AM)' />\n    <tag k='network' v='Mission Bay' />\n    <tag k='operator' v='Mission Bay Transportation Management Association' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='TransBay/Caltrain' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8610462' timestamp='2018-08-28T21:50:52Z' uid='8107451' user='njtbusfan' version='1' changeset='62086023'>\n    <member type='way' ref='417392943' role='forward' />\n    <member type='way' ref='32396142' role='forward' />\n    <member type='way' ref='396960684' role='forward' />\n    <member type='way' ref='396957213' role='forward' />\n    <member type='way' ref='283530637' role='forward' />\n    <member type='way' ref='396957209' role='forward' />\n    <member type='way' ref='620728084' role='forward' />\n    <member type='way' ref='620728098' role='forward' />\n    <member type='way' ref='82487553' role='backward' />\n    <member type='way' ref='396960685' role='' />\n    <member type='way' ref='397250666' role='' />\n    <member type='way' ref='397250667' role='' />\n    <member type='way' ref='397250665' role='' />\n    <member type='way' ref='514616615' role='forward' />\n    <member type='way' ref='396960687' role='forward' />\n    <member type='way' ref='28518582' role='forward' />\n    <member type='way' ref='396955699' role='forward' />\n    <member type='way' ref='285977963' role='forward' />\n    <member type='way' ref='414164637' role='forward' />\n    <member type='way' ref='437959607' role='forward' />\n    <member type='way' ref='8917436' role='forward' />\n    <member type='way' ref='8921143' role='forward' />\n    <member type='way' ref='620728089' role='forward' />\n    <member type='way' ref='620728083' role='forward' />\n    <member type='way' ref='186398845' role='forward' />\n    <member type='way' ref='221434102' role='forward' />\n    <member type='way' ref='8919269' role='forward' />\n    <member type='way' ref='162931810' role='forward' />\n    <member type='way' ref='256787755' role='forward' />\n    <member type='way' ref='91181769' role='forward' />\n    <member type='way' ref='88572943' role='forward' />\n    <member type='way' ref='619568403' role='forward' />\n    <member type='way' ref='512038661' role='forward' />\n    <member type='way' ref='419633756' role='forward' />\n    <member type='way' ref='419633755' role='forward' />\n    <member type='way' ref='417087306' role='forward' />\n    <member type='way' ref='8921141' role='forward' />\n    <member type='way' ref='397119705' role='forward' />\n    <member type='way' ref='417087312' role='forward' />\n    <member type='way' ref='417087300' role='forward' />\n    <member type='way' ref='111050389' role='forward' />\n    <member type='way' ref='620200099' role='forward' />\n    <member type='way' ref='493107462' role='forward' />\n    <member type='way' ref='419633752' role='forward' />\n    <member type='way' ref='525662693' role='forward' />\n    <member type='way' ref='394558807' role='forward' />\n    <member type='way' ref='247966219' role='forward' />\n    <member type='way' ref='519082484' role='forward' />\n    <member type='way' ref='417400705' role='forward' />\n    <member type='way' ref='417400701' role='forward' />\n    <member type='way' ref='157812260' role='forward' />\n    <member type='way' ref='28518571' role='forward' />\n    <member type='way' ref='417392966' role='forward' />\n    <member type='way' ref='417392970' role='forward' />\n    <member type='way' ref='417392953' role='forward' />\n    <member type='way' ref='417392962' role='forward' />\n    <member type='way' ref='513699413' role='forward' />\n    <member type='way' ref='8916792' role='forward' />\n    <member type='way' ref='417392949' role='forward' />\n    <member type='way' ref='514616616' role='forward' />\n    <tag k='name' v='Mission Bay South Route' />\n    <tag k='network' v='Mission Bay' />\n    <tag k='operator' v='Mission Bay Transportation Management Association' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='South' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8610463' timestamp='2018-08-28T21:50:53Z' uid='8107451' user='njtbusfan' version='1' changeset='62086023'>\n    <member type='way' ref='417400701' role='forward' />\n    <member type='way' ref='417400705' role='forward' />\n    <member type='way' ref='519082484' role='forward' />\n    <member type='way' ref='247966219' role='forward' />\n    <member type='way' ref='394558807' role='forward' />\n    <member type='way' ref='525662693' role='forward' />\n    <member type='way' ref='419633752' role='forward' />\n    <member type='way' ref='493107462' role='forward' />\n    <member type='way' ref='620200099' role='forward' />\n    <member type='way' ref='111050389' role='forward' />\n    <member type='way' ref='417087300' role='forward' />\n    <member type='way' ref='417087312' role='forward' />\n    <member type='way' ref='397119705' role='forward' />\n    <member type='way' ref='8921141' role='forward' />\n    <member type='way' ref='417087306' role='forward' />\n    <member type='way' ref='419633755' role='forward' />\n    <member type='way' ref='419633756' role='forward' />\n    <member type='way' ref='512038661' role='forward' />\n    <member type='way' ref='619568403' role='forward' />\n    <member type='way' ref='88572943' role='forward' />\n    <member type='way' ref='91181769' role='forward' />\n    <member type='way' ref='417087368' role='forward' />\n    <member type='way' ref='91181771' role='forward' />\n    <member type='way' ref='620728096' role='forward' />\n    <member type='way' ref='539195908' role='forward' />\n    <member type='way' ref='539195910' role='forward' />\n    <member type='way' ref='428511530' role='forward' />\n    <member type='way' ref='157812260' role='' />\n    <member type='way' ref='28518571' role='' />\n    <member type='way' ref='417392966' role='' />\n    <member type='way' ref='417392970' role='' />\n    <member type='way' ref='417392953' role='' />\n    <member type='way' ref='417392962' role='' />\n    <member type='way' ref='397250673' role='forward' />\n    <member type='way' ref='82487115' role='forward' />\n    <member type='way' ref='397250672' role='forward' />\n    <member type='way' ref='620728081' role='forward' />\n    <member type='way' ref='251511536' role='forward' />\n    <member type='way' ref='396955704' role='forward' />\n    <member type='way' ref='396955701' role='forward' />\n    <member type='way' ref='36331085' role='forward' />\n    <member type='way' ref='215524529' role='forward' />\n    <member type='way' ref='37177689' role='forward' />\n    <member type='way' ref='396947356' role='forward' />\n    <member type='way' ref='52432936' role='forward' />\n    <member type='way' ref='160313264' role='forward' />\n    <member type='way' ref='160313263' role='forward' />\n    <member type='way' ref='396949640' role='forward' />\n    <member type='way' ref='396949646' role='forward' />\n    <member type='way' ref='46594526' role='forward' />\n    <member type='way' ref='46594933' role='forward' />\n    <member type='way' ref='375747150' role='forward' />\n    <member type='way' ref='375747145' role='forward' />\n    <member type='way' ref='375747147' role='forward' />\n    <member type='way' ref='514605580' role='forward' />\n    <member type='way' ref='7448875' role='forward' />\n    <member type='way' ref='189149478' role='forward' />\n    <member type='way' ref='617893863' role='forward' />\n    <member type='way' ref='516940743' role='backward' />\n    <member type='way' ref='397250667' role='forward' />\n    <member type='way' ref='397250665' role='forward' />\n    <member type='way' ref='514616615' role='forward' />\n    <member type='way' ref='396960687' role='forward' />\n    <member type='way' ref='417392949' role='backward' />\n    <member type='way' ref='8916792' role='backward' />\n    <member type='way' ref='513699413' role='backward' />\n    <tag k='name' v='Mission Bay West Route' />\n    <tag k='network' v='Mission Bay' />\n    <tag k='operator' v='Mission Bay Transportation Management Association' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='West' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8610464' timestamp='2018-10-15T22:03:42Z' uid='2237750' user='chachafish' version='2' changeset='63557245'>\n    <member type='way' ref='256787755' role='forward' />\n    <member type='way' ref='91181769' role='forward' />\n    <member type='way' ref='88572943' role='forward' />\n    <member type='way' ref='619568403' role='forward' />\n    <member type='way' ref='512038661' role='forward' />\n    <member type='way' ref='419633756' role='forward' />\n    <member type='way' ref='419633755' role='forward' />\n    <member type='way' ref='417087306' role='forward' />\n    <member type='way' ref='8921141' role='forward' />\n    <member type='way' ref='397119705' role='forward' />\n    <member type='way' ref='417087312' role='forward' />\n    <member type='way' ref='417087300' role='forward' />\n    <member type='way' ref='111050389' role='forward' />\n    <member type='way' ref='620200099' role='forward' />\n    <member type='way' ref='493107462' role='forward' />\n    <member type='way' ref='419633752' role='forward' />\n    <member type='way' ref='525662693' role='forward' />\n    <member type='way' ref='394558807' role='forward' />\n    <member type='way' ref='573287808' role='forward' />\n    <member type='way' ref='516061744' role='forward' />\n    <member type='way' ref='27167281' role='forward' />\n    <member type='way' ref='516061750' role='forward' />\n    <member type='way' ref='221434104' role='forward' />\n    <member type='way' ref='218146865' role='forward' />\n    <member type='way' ref='218146840' role='forward' />\n    <member type='way' ref='8916338' role='forward' />\n    <member type='way' ref='620728089' role='backward' />\n    <member type='way' ref='8921143' role='backward' />\n    <member type='way' ref='8917436' role='backward' />\n    <member type='way' ref='396956336' role='forward' />\n    <member type='way' ref='514669211' role='forward' />\n    <member type='way' ref='224384042' role='forward' />\n    <member type='way' ref='224384041' role='forward' />\n    <member type='way' ref='620728092' role='forward' />\n    <member type='way' ref='620728082' role='forward' />\n    <member type='way' ref='186398845' role='forward' />\n    <member type='way' ref='221434102' role='forward' />\n    <member type='way' ref='8919269' role='forward' />\n    <member type='way' ref='162931810' role='forward' />\n    <tag k='name' v='Mission Bay CCA Route' />\n    <tag k='network' v='Mission Bay' />\n    <tag k='operator' v='Mission Bay Transportation Management Association' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='CCA' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8610465' timestamp='2018-10-16T00:41:01Z' uid='2237750' user='chachafish' version='3' changeset='63559070'>\n    <member type='way' ref='82487553' role='forward' />\n    <member type='way' ref='514605580' role='forward' />\n    <member type='way' ref='514606777' role='forward' />\n    <member type='way' ref='620728080' role='forward' />\n    <member type='way' ref='28843966' role='forward' />\n    <member type='way' ref='27512051' role='forward' />\n    <member type='way' ref='516940745' role='forward' />\n    <member type='way' ref='437921062' role='forward' />\n    <member type='way' ref='396957210' role='forward' />\n    <member type='way' ref='46594527' role='forward' />\n    <member type='way' ref='24280144' role='forward' />\n    <member type='way' ref='396960686' role='forward' />\n    <member type='way' ref='396960684' role='backward' />\n    <member type='way' ref='32396142' role='backward' />\n    <member type='way' ref='417392943' role='backward' />\n    <member type='way' ref='396960685' role='' />\n    <member type='way' ref='397250666' role='' />\n    <member type='way' ref='397250667' role='' />\n    <member type='way' ref='397250665' role='' />\n    <member type='way' ref='514616615' role='forward' />\n    <member type='way' ref='396960687' role='forward' />\n    <member type='way' ref='28518582' role='forward' />\n    <member type='way' ref='396955699' role='forward' />\n    <member type='way' ref='285977963' role='forward' />\n    <member type='way' ref='414164637' role='forward' />\n    <member type='way' ref='437959607' role='forward' />\n    <member type='way' ref='396956336' role='forward' />\n    <member type='way' ref='514669211' role='forward' />\n    <member type='way' ref='224384042' role='forward' />\n    <member type='way' ref='224384041' role='forward' />\n    <member type='way' ref='8918238' role='forward' />\n    <member type='way' ref='8919954' role='forward' />\n    <member type='way' ref='634412037' role='forward' />\n    <member type='way' ref='634444999' role='forward' />\n    <member type='way' ref='221434104' role='forward' />\n    <member type='way' ref='221434100' role='forward' />\n    <member type='way' ref='620728093' role='forward' />\n    <member type='way' ref='221434102' role='forward' />\n    <member type='way' ref='8919269' role='forward' />\n    <member type='way' ref='162931810' role='forward' />\n    <member type='way' ref='256787755' role='forward' />\n    <member type='way' ref='91181769' role='forward' />\n    <member type='way' ref='88572943' role='forward' />\n    <member type='way' ref='619568403' role='forward' />\n    <member type='way' ref='512038661' role='forward' />\n    <member type='way' ref='419633756' role='forward' />\n    <member type='way' ref='419633755' role='forward' />\n    <member type='way' ref='417087306' role='forward' />\n    <member type='way' ref='8921141' role='forward' />\n    <member type='way' ref='397119705' role='forward' />\n    <member type='way' ref='417087312' role='forward' />\n    <member type='way' ref='417087300' role='forward' />\n    <member type='way' ref='111050389' role='forward' />\n    <member type='way' ref='620200099' role='forward' />\n    <member type='way' ref='493107462' role='forward' />\n    <member type='way' ref='419633752' role='forward' />\n    <member type='way' ref='525662693' role='forward' />\n    <member type='way' ref='394558807' role='forward' />\n    <member type='way' ref='247966219' role='forward' />\n    <member type='way' ref='519082484' role='forward' />\n    <member type='way' ref='417400705' role='forward' />\n    <member type='way' ref='417400701' role='forward' />\n    <member type='way' ref='157812260' role='forward' />\n    <member type='way' ref='28518571' role='forward' />\n    <member type='way' ref='417392966' role='forward' />\n    <member type='way' ref='417392970' role='forward' />\n    <member type='way' ref='417392953' role='forward' />\n    <member type='way' ref='417392962' role='forward' />\n    <member type='way' ref='513699413' role='forward' />\n    <member type='way' ref='8916792' role='forward' />\n    <member type='way' ref='417392949' role='forward' />\n    <member type='way' ref='514616616' role='forward' />\n    <tag k='name' v='Mission Bay East Route' />\n    <tag k='network' v='Mission Bay' />\n    <tag k='operator' v='Mission Bay Transportation Management Association' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='East' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8610466' timestamp='2018-08-28T21:50:55Z' uid='8107451' user='njtbusfan' version='1' changeset='62086023'>\n    <member type='way' ref='397250666' role='forward' />\n    <member type='way' ref='396960685' role='forward' />\n    <member type='way' ref='82487553' role='forward' />\n    <member type='way' ref='7448875' role='backward' />\n    <member type='way' ref='189149478' role='backward' />\n    <member type='way' ref='617893863' role='backward' />\n    <member type='way' ref='516940743' role='forward' />\n    <member type='way' ref='397250667' role='' />\n    <member type='way' ref='397250665' role='' />\n    <member type='way' ref='514616615' role='forward' />\n    <member type='way' ref='396960687' role='forward' />\n    <member type='way' ref='514616616' role='forward' />\n    <member type='way' ref='417392949' role='' />\n    <member type='way' ref='8916792' role='' />\n    <member type='way' ref='513699413' role='' />\n    <member type='way' ref='417392962' role='' />\n    <member type='way' ref='417392953' role='' />\n    <member type='way' ref='417392970' role='' />\n    <member type='way' ref='417392966' role='' />\n    <member type='way' ref='28518571' role='' />\n    <member type='way' ref='157812260' role='' />\n    <member type='way' ref='428511530' role='forward' />\n    <member type='way' ref='539195910' role='forward' />\n    <member type='way' ref='539195908' role='forward' />\n    <member type='way' ref='620728096' role='forward' />\n    <member type='way' ref='91181771' role='forward' />\n    <member type='way' ref='417087368' role='forward' />\n    <member type='way' ref='91181769' role='forward' />\n    <member type='way' ref='88572943' role='forward' />\n    <member type='way' ref='619568403' role='forward' />\n    <member type='way' ref='512038661' role='forward' />\n    <member type='way' ref='419633756' role='forward' />\n    <member type='way' ref='419633755' role='forward' />\n    <member type='way' ref='417087306' role='forward' />\n    <member type='way' ref='8921141' role='forward' />\n    <member type='way' ref='397119705' role='forward' />\n    <member type='way' ref='417087312' role='forward' />\n    <member type='way' ref='417087300' role='forward' />\n    <member type='way' ref='111050389' role='forward' />\n    <member type='way' ref='620200099' role='forward' />\n    <member type='way' ref='493107462' role='forward' />\n    <member type='way' ref='419633752' role='forward' />\n    <member type='way' ref='525662693' role='forward' />\n    <member type='way' ref='394558807' role='forward' />\n    <member type='way' ref='247966219' role='forward' />\n    <member type='way' ref='519082484' role='forward' />\n    <member type='way' ref='417400705' role='forward' />\n    <member type='way' ref='417400701' role='forward' />\n    <tag k='name' v='Mission Bay Owens/Illinois Route' />\n    <tag k='network' v='Mission Bay' />\n    <tag k='operator' v='Mission Bay Transportation Management Association' />\n    <tag k='public_transport:version' v='1' />\n    <tag k='ref' v='Owens/Illinois' />\n    <tag k='route' v='bus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8713570' timestamp='2018-09-19T00:38:21Z' uid='53073' user='Aaron Lidman' version='1' changeset='62713402'>\n    <member type='way' ref='205644329' role='outer' />\n    <tag k='addr:housenumber' v='184' />\n    <tag k='addr:street' v='Hooper Street' />\n    <tag k='building' v='university' />\n    <tag k='name' v='Graduate Center' />\n    <tag k='operator' v='California College of the Arts' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/diff/DiffAtlas1.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38978' action='modify' lat='37.59269293962' lon='-122.24502370724' />\n  <node id='-38980' action='modify' lat='37.5930666147' lon='-122.24410491133' />\n  <node id='-38982' action='modify' lat='37.59269938231' lon='-122.24295844917' />\n  <node id='-38984' action='modify' lat='37.5931374839' lon='-122.24195021295' />\n  <node id='-38986' action='modify' lat='37.59206799606' lon='-122.24292592542' />\n  <node id='-38988' action='modify' lat='37.5932470089' lon='-122.24284461605' />\n  <node id='-38990' action='modify' lat='37.59387838514' lon='-122.2433568651'>\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='-38992' action='modify' lat='37.59344673053' lon='-122.24189329639' />\n  <node id='-38994' action='modify' lat='37.59354336984' lon='-122.24146235671' />\n  <node id='-38996' action='modify' lat='37.59334364847' lon='-122.2413078689' />\n  <node id='-38998' action='modify' lat='37.59304084407' lon='-122.24127534515' />\n  <node id='-39000' action='modify' lat='37.59296353211' lon='-122.24160871358' />\n  <node id='-41769' action='modify' lat='37.59333720261' lon='-122.24404392523' />\n  <node id='-41770' action='modify' lat='37.59333398129' lon='-122.2437756043' />\n  <node id='-41772' action='modify' lat='37.59320834981' lon='-122.24376747336' />\n  <node id='-41774' action='modify' lat='37.59319546452' lon='-122.24341784306' />\n  <node id='-41776' action='modify' lat='37.5932824402' lon='-122.24327148619' />\n  <node id='-41778' action='modify' lat='37.59308916078' lon='-122.24317798041' />\n  <node id='-41793' action='modify' lat='37.59273803521' lon='-122.24378780071' />\n  <node id='-41794' action='modify' lat='37.59269293639' lon='-122.24397481226' />\n  <node id='-41796' action='modify' lat='37.59256086113' lon='-122.24393009211' />\n  <node id='-41798' action='modify' lat='37.59248032732' lon='-122.24371462227' />\n  <node id='-41800' action='modify' lat='37.59258985328' lon='-122.24351134884' />\n  <node id='-41802' action='modify' lat='37.59270904312' lon='-122.24357639634' />\n  <way id='-39002' action='modify'>\n    <nd ref='-38978' />\n    <nd ref='-38980' />\n    <nd ref='-38982' />\n    <nd ref='-38984' />\n    <tag k='highway' v='primary' />\n    <tag k='name' v='primary' />\n  </way>\n  <way id='-39004' action='modify'>\n    <nd ref='-38986' />\n    <nd ref='-38982' />\n    <nd ref='-38988' />\n    <nd ref='-38990' />\n    <tag k='highway' v='motorway' />\n    <tag k='name' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-39006' action='modify'>\n    <nd ref='-38992' />\n    <nd ref='-38994' />\n    <nd ref='-38996' />\n    <nd ref='-38998' />\n    <nd ref='-39000' />\n    <nd ref='-38984' />\n    <nd ref='-38992' />\n    <tag k='highway' v='secondary' />\n    <tag k='junction' v='roundabout' />\n    <tag k='name' v='secondary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-41771' action='modify'>\n    <nd ref='-41769' />\n    <nd ref='-41770' />\n    <nd ref='-41772' />\n    <nd ref='-41774' />\n    <nd ref='-41776' />\n    <nd ref='-41778' />\n    <tag k='waterway' v='canal' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/diff/DiffAtlas2.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38978' action='modify' lat='37.59269293962' lon='-122.24502370724' />\n  <node id='-38980' action='modify' lat='37.5930666147' lon='-122.24410491133' />\n  <node id='-38982' action='modify' lat='37.59269938231' lon='-122.24295844917' />\n  <node id='-38984' action='modify' lat='37.5931374839' lon='-122.24195021295' />\n  <node id='-38986' action='modify' lat='37.59206799606' lon='-122.24292592542' />\n  <node id='-38988' action='modify' lat='37.5932470089' lon='-122.24284461605' />\n  <node id='-38990' action='modify' lat='37.59387838514' lon='-122.2433568651'>\n    <tag k='highway' v='stop' />\n  </node>\n  <node id='-38992' action='modify' lat='37.59344673053' lon='-122.24189329639' />\n  <node id='-38994' action='modify' lat='37.59354336984' lon='-122.24146235671' />\n  <node id='-38996' action='modify' lat='37.59334364847' lon='-122.2413078689' />\n  <node id='-38998' action='modify' lat='37.59304084407' lon='-122.24127534515' />\n  <node id='-39000' action='modify' lat='37.59296353211' lon='-122.24160871358' />\n  <node id='-41769' action='modify' lat='37.59333720261' lon='-122.24404392523' />\n  <node id='-41770' action='modify' lat='37.59333398129' lon='-122.2437756043' />\n  <node id='-41772' action='modify' lat='37.59320834981' lon='-122.24376747336' />\n  <node id='-41774' action='modify' lat='37.59319546452' lon='-122.24341784306' />\n  <node id='-41776' action='modify' lat='37.5932824402' lon='-122.24327148619' />\n  <node id='-41778' action='modify' lat='37.59308916078' lon='-122.24317798041' />\n  <node id='-41793' action='modify' lat='37.59273803521' lon='-122.24378780071' />\n  <node id='-41794' action='modify' lat='37.59269293639' lon='-122.24397481226' />\n  <node id='-41796' action='modify' lat='37.59256086113' lon='-122.24393009211' />\n  <node id='-41798' action='modify' lat='37.59248032732' lon='-122.24371462227' />\n  <node id='-41800' action='modify' lat='37.59258985328' lon='-122.24351134884' />\n  <node id='-41802' action='modify' lat='37.59270904312' lon='-122.24357639634' />\n  <node id='-41822' action='modify' lat='37.59261884542' lon='-122.24322270057'>\n    <tag k='natural' v='tree' />\n  </node>\n  <way id='-39002' action='modify'>\n    <nd ref='-38978' />\n    <nd ref='-38980' />\n    <nd ref='-38982' />\n    <nd ref='-38984' />\n    <tag k='highway' v='primary' />\n    <tag k='name' v='primary' />\n  </way>\n  <way id='-39004' action='modify'>\n    <nd ref='-38986' />\n    <nd ref='-38982' />\n    <nd ref='-38988' />\n    <nd ref='-38990' />\n    <tag k='highway' v='motorway' />\n    <tag k='name' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-39006' action='modify'>\n    <nd ref='-38992' />\n    <nd ref='-38994' />\n    <nd ref='-38996' />\n    <nd ref='-38998' />\n    <nd ref='-39000' />\n    <nd ref='-38984' />\n    <nd ref='-38992' />\n    <tag k='highway' v='secondary' />\n    <tag k='junction' v='roundabout' />\n    <tag k='name' v='secondary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-41771' action='modify'>\n    <nd ref='-41769' />\n    <nd ref='-41770' />\n    <nd ref='-41772' />\n    <nd ref='-41774' />\n    <nd ref='-41776' />\n    <nd ref='-41778' />\n    <tag k='waterway' v='canal' />\n  </way>\n  <way id='-41795' action='modify'>\n    <nd ref='-41793' />\n    <nd ref='-41794' />\n    <nd ref='-41796' />\n    <nd ref='-41798' />\n    <nd ref='-41800' />\n    <nd ref='-41802' />\n    <nd ref='-41793' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/nodeBoundsExpansionAtlas.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-177628' action='modify' visible='true' lat='38.82293376857' lon='4.21774333824' />\n  <node id='-177629' action='modify' visible='true' lat='38.82322294505' lon='4.21823823725' />\n  <node id='-177631' action='modify' visible='true' lat='38.82289990097' lon='4.2189103636' />\n  <node id='-177633' action='modify' visible='true' lat='38.82316563089' lon='4.2194855165' />\n  <node id='-177647' action='modify' visible='true' lat='38.82282174493' lon='4.21976974904' />\n  <node id='-177649' action='modify' visible='true' lat='38.82351472554' lon='4.21966608776' />\n  <way id='-177630' action='modify' visible='true'>\n    <nd ref='-177628' />\n    <nd ref='-177629' />\n    <nd ref='-177631' />\n    <nd ref='-177633' />\n    <tag k='highway' v='residential' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-177648' action='modify' visible='true'>\n    <nd ref='-177647' />\n    <nd ref='-177633' />\n    <nd ref='-177649' />\n    <tag k='highway' v='footway' />\n  </way>\n  <relation id='-250654' action='modify' visible='true'>\n    <member type='way' ref='-177648' role='one' />\n    <member type='way' ref='-177630' role='two' />\n    <tag k='type' v='test' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/change.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    12.49234,\n    41.890224\n  ],\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"bbox\": [\n        -122.0314121,\n        37.3901066,\n        -122.0306289,\n        37.3909505\n      ],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.0310473,\n              37.3909505\n            ],\n            [\n              -122.0314121,\n              37.3903197\n            ],\n            [\n              -122.0311332,\n              37.3901066\n            ],\n            [\n              -122.0306289,\n              37.3908482\n            ],\n            [\n              -122.0310473,\n              37.3909505\n            ]\n          ]\n        ]\n      },\n      \"properties\": {\n        \"featureChangeType\": \"ADD\",\n        \"metadata\": {},\n        \"entityType\": \"AREA\",\n        \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteArea\",\n        \"identifier\": 123,\n        \"tags\": {\n          \"tagKey1\": \"tagValue1\",\n          \"tagKey2\": \"tagValue2\"\n        },\n        \"relations\": [\n          444,\n          555\n        ],\n        \"WKT\": \"POLYGON ((-122.0310473 37.3909505, -122.0314121 37.3903197, -122.0311332 37.3901066, -122.0306289 37.3908482, -122.0310473 37.3909505, -122.0310473 37.3909505))\",\n        \"bboxWKT\": \"POLYGON ((-122.0314121 37.3901066, -122.0314121 37.3909505, -122.0306289 37.3909505, -122.0306289 37.3901066, -122.0314121 37.3901066))\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"bbox\": [\n        -122.052138,\n        37.317585,\n        -122.009566,\n        37.390535\n      ],\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.052138,\n            37.317585\n          ],\n          [\n            -122.0304871,\n            37.3314171\n          ],\n          [\n            -122.028932,\n            37.332451\n          ],\n          [\n            -122.009566,\n            37.33531\n          ],\n          [\n            -122.031007,\n            37.390535\n          ]\n        ]\n      },\n      \"properties\": {\n        \"featureChangeType\": \"ADD\",\n        \"metadata\": {},\n        \"entityType\": \"EDGE\",\n        \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n        \"identifier\": 123,\n        \"tags\": {\n          \"tagKey1\": \"tagValue1\",\n          \"tagKey2\": \"tagValue2\"\n        },\n        \"relations\": [\n          444,\n          555\n        ],\n        \"startNode\": \"456\",\n        \"endNode\": \"789\",\n        \"WKT\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535)\",\n        \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"bbox\": [\n        -122.052138,\n        37.317585,\n        -122.009566,\n        37.390535\n      ],\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -122.052138,\n            37.317585\n          ],\n          [\n            -122.0304871,\n            37.3314171\n          ],\n          [\n            -122.028932,\n            37.332451\n          ],\n          [\n            -122.009566,\n            37.33531\n          ],\n          [\n            -122.031007,\n            37.390535\n          ]\n        ]\n      },\n      \"properties\": {\n        \"featureChangeType\": \"ADD\",\n        \"metadata\": {},\n        \"entityType\": \"LINE\",\n        \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteLine\",\n        \"identifier\": 123,\n        \"tags\": {\n          \"tagKey1\": \"tagValue1\",\n          \"tagKey2\": \"tagValue2\"\n        },\n        \"relations\": [\n          444,\n          555\n        ],\n        \"WKT\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535)\",\n        \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"bbox\": [\n        12.49234,\n        41.890224,\n        12.49234,\n        41.890224\n      ],\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          12.49234,\n          41.890224\n        ]\n      },\n      \"properties\": {\n        \"featureChangeType\": \"ADD\",\n        \"metadata\": {},\n        \"entityType\": \"NODE\",\n        \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteNode\",\n        \"identifier\": 123,\n        \"tags\": {\n          \"tagKey1\": \"tagValue1\",\n          \"tagKey2\": \"tagValue2\"\n        },\n        \"relations\": [\n          444,\n          555\n        ],\n        \"inEdges\": [\n          456,\n          789\n        ],\n        \"outEdges\": [\n          456,\n          789\n        ],\n        \"WKT\": \"POINT (12.49234 41.890224)\",\n        \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"bbox\": [\n        12.49234,\n        41.890224,\n        12.49234,\n        41.890224\n      ],\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          12.49234,\n          41.890224\n        ]\n      },\n      \"properties\": {\n        \"featureChangeType\": \"ADD\",\n        \"metadata\": {},\n        \"entityType\": \"POINT\",\n        \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompletePoint\",\n        \"identifier\": 123,\n        \"tags\": {\n          \"tagKey1\": \"tagValue1\",\n          \"tagKey2\": \"tagValue2\"\n        },\n        \"relations\": [\n          444,\n          555\n        ],\n        \"WKT\": \"POINT (12.49234 41.890224)\",\n        \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"bbox\": [\n        -122.031905,\n        37.328167,\n        -122.029051,\n        37.330394\n      ],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              -122.031905,\n              37.328167\n            ],\n            [\n              -122.031905,\n              37.330394\n            ],\n            [\n              -122.029051,\n              37.330394\n            ],\n            [\n              -122.029051,\n              37.328167\n            ],\n            [\n              -122.031905,\n              37.328167\n            ]\n          ]\n        ]\n      },\n      \"properties\": {\n        \"featureChangeType\": \"ADD\",\n        \"metadata\": {},\n        \"entityType\": \"RELATION\",\n        \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation\",\n        \"identifier\": 123,\n        \"tags\": {\n          \"tagKey1\": \"tagValue1\",\n          \"tagKey2\": \"tagValue2\"\n        },\n        \"relations\": [\n          444,\n          555\n        ],\n        \"members\": [\n          \"{Member: ID = 456, Type = EDGE, Role = role1}\",\n          \"{Member: ID = 789, Type = AREA, Role = role2}\"\n        ],\n        \"WKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\",\n        \"bboxWKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\"\n      }\n    }\n  ],\n  \"properties\": {\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 41.890224, 12.49234 41.890224, 12.49234 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedAddEdgeWithDescription.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        -122.052138,\n        37.317585\n      ],\n      [\n        -122.0304871,\n        37.3314171\n      ],\n      [\n        -122.028932,\n        37.332451\n      ],\n      [\n        -122.009566,\n        37.33531\n      ],\n      [\n        -122.031007,\n        37.390535\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {},\n    \"description\": {\n      \"type\": \"ADD\",\n      \"descriptors\": [\n        {\n          \"name\": \"TAG\",\n          \"type\": \"ADD\",\n          \"key\": \"a\",\n          \"value\": \"1\"\n        },\n        {\n          \"name\": \"TAG\",\n          \"type\": \"ADD\",\n          \"key\": \"b\",\n          \"value\": \"2\"\n        },\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"ADD\",\n          \"position\": \"0/0\",\n          \"afterView\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535)\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"ADD\",\n          \"afterElement\": \"1\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"ADD\",\n          \"afterElement\": \"2\"\n        },\n        {\n          \"name\": \"START_NODE\",\n          \"type\": \"ADD\",\n          \"afterElement\": \"1\"\n        },\n        {\n          \"name\": \"END_NODE\",\n          \"type\": \"ADD\",\n          \"afterElement\": \"2\"\n        }\n      ]\n    },\n    \"entityType\": \"EDGE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"a\": \"1\",\n      \"b\": \"2\"\n    },\n    \"relations\": [\n      1,\n      2\n    ],\n    \"startNode\": \"1\",\n    \"endNode\": \"2\",\n    \"WKT\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535)\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedAddRelationWithDescription.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.031905,\n    37.328167,\n    -122.029051,\n    37.330394\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.031905,\n          37.328167\n        ],\n        [\n          -122.031905,\n          37.330394\n        ],\n        [\n          -122.029051,\n          37.330394\n        ],\n        [\n          -122.029051,\n          37.328167\n        ],\n        [\n          -122.031905,\n          37.328167\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {},\n    \"description\": {\n      \"type\": \"ADD\",\n      \"descriptors\": [\n        {\n          \"name\": \"TAG\",\n          \"type\": \"ADD\",\n          \"key\": \"a\",\n          \"value\": \"1\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"ADD\",\n          \"afterElement\": \"1\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"ADD\",\n          \"afterElement\": \"2\"\n        },\n        {\n          \"name\": \"RELATION_MEMBER\",\n          \"type\": \"ADD\",\n          \"itemType\": \"AREA\",\n          \"id\": 600,\n          \"role\": \"a\"\n        },\n        {\n          \"name\": \"RELATION_MEMBER\",\n          \"type\": \"ADD\",\n          \"itemType\": \"POINT\",\n          \"id\": 500,\n          \"role\": \"a\"\n        }\n      ]\n    },\n    \"entityType\": \"RELATION\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"a\": \"1\"\n    },\n    \"relations\": [\n      1,\n      2\n    ],\n    \"members\": [\n      \"{Member: ID = 500, Type = POINT, Role = a}\",\n      \"{Member: ID = 600, Type = AREA, Role = a}\"\n    ],\n    \"WKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\",\n    \"bboxWKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedAreaFull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.0314121,\n    37.3901066,\n    -122.0306289,\n    37.3909505\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.0310473,\n          37.3909505\n        ],\n        [\n          -122.0314121,\n          37.3903197\n        ],\n        [\n          -122.0311332,\n          37.3901066\n        ],\n        [\n          -122.0306289,\n          37.3908482\n        ],\n        [\n          -122.0310473,\n          37.3909505\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"AREA\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteArea\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"tagKey1\": \"tagValue1\",\n      \"tagKey2\": \"tagValue2\"\n    },\n    \"relations\": [\n      444,\n      555\n    ],\n    \"WKT\": \"POLYGON ((-122.0310473 37.3909505, -122.0314121 37.3903197, -122.0311332 37.3901066, -122.0306289 37.3908482, -122.0310473 37.3909505, -122.0310473 37.3909505))\",\n    \"bboxWKT\": \"POLYGON ((-122.0314121 37.3901066, -122.0314121 37.3909505, -122.0306289 37.3909505, -122.0306289 37.3901066, -122.0314121 37.3901066))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedAreaNull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.0314121,\n    37.3901066,\n    -122.0306289,\n    37.3909505\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.0310473,\n          37.3909505\n        ],\n        [\n          -122.0314121,\n          37.3903197\n        ],\n        [\n          -122.0311332,\n          37.3901066\n        ],\n        [\n          -122.0306289,\n          37.3908482\n        ],\n        [\n          -122.0310473,\n          37.3909505\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"AREA\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteArea\",\n    \"identifier\": 123,\n    \"WKT\": \"POLYGON ((-122.0310473 37.3909505, -122.0314121 37.3903197, -122.0311332 37.3901066, -122.0306289 37.3908482, -122.0310473 37.3909505, -122.0310473 37.3909505))\",\n    \"bboxWKT\": \"POLYGON ((-122.0314121 37.3901066, -122.0314121 37.3909505, -122.0306289 37.3909505, -122.0306289 37.3901066, -122.0314121 37.3901066))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedAreaRemove.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.0314121,\n    37.3901066,\n    -122.0306289,\n    37.3909505\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.0314121,\n          37.3901066\n        ],\n        [\n          -122.0314121,\n          37.3909505\n        ],\n        [\n          -122.0306289,\n          37.3909505\n        ],\n        [\n          -122.0306289,\n          37.3901066\n        ],\n        [\n          -122.0314121,\n          37.3901066\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"REMOVE\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"AREA\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteArea\",\n    \"identifier\": 123,\n    \"WKT\": \"POLYGON ((-122.0314121 37.3901066, -122.0314121 37.3909505, -122.0306289 37.3909505, -122.0306289 37.3901066, -122.0314121 37.3901066))\",\n    \"bboxWKT\": \"POLYGON ((-122.0314121 37.3901066, -122.0314121 37.3909505, -122.0306289 37.3909505, -122.0306289 37.3901066, -122.0314121 37.3901066))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedEdgeFull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        -122.052138,\n        37.317585\n      ],\n      [\n        -122.0304871,\n        37.3314171\n      ],\n      [\n        -122.028932,\n        37.332451\n      ],\n      [\n        -122.009566,\n        37.33531\n      ],\n      [\n        -122.031007,\n        37.390535\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"EDGE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"tagKey1\": \"tagValue1\",\n      \"tagKey2\": \"tagValue2\"\n    },\n    \"relations\": [\n      444,\n      555\n    ],\n    \"startNode\": \"456\",\n    \"endNode\": \"789\",\n    \"WKT\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535)\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedEdgeNull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        -122.052138,\n        37.317585\n      ],\n      [\n        -122.0304871,\n        37.3314171\n      ],\n      [\n        -122.028932,\n        37.332451\n      ],\n      [\n        -122.009566,\n        37.33531\n      ],\n      [\n        -122.031007,\n        37.390535\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"EDGE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n    \"identifier\": 123,\n    \"WKT\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535)\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedEdgeRemove.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.052138,\n          37.317585\n        ],\n        [\n          -122.052138,\n          37.390535\n        ],\n        [\n          -122.009566,\n          37.390535\n        ],\n        [\n          -122.009566,\n          37.317585\n        ],\n        [\n          -122.052138,\n          37.317585\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"REMOVE\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"EDGE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n    \"identifier\": 123,\n    \"WKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedLineFull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        -122.052138,\n        37.317585\n      ],\n      [\n        -122.0304871,\n        37.3314171\n      ],\n      [\n        -122.028932,\n        37.332451\n      ],\n      [\n        -122.009566,\n        37.33531\n      ],\n      [\n        -122.031007,\n        37.390535\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"LINE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteLine\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"tagKey1\": \"tagValue1\",\n      \"tagKey2\": \"tagValue2\"\n    },\n    \"relations\": [\n      444,\n      555\n    ],\n    \"WKT\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535)\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedLineNull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        -122.052138,\n        37.317585\n      ],\n      [\n        -122.0304871,\n        37.3314171\n      ],\n      [\n        -122.028932,\n        37.332451\n      ],\n      [\n        -122.009566,\n        37.33531\n      ],\n      [\n        -122.031007,\n        37.390535\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"LINE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteLine\",\n    \"identifier\": 123,\n    \"WKT\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535)\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedLineRemove.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.052138,\n          37.317585\n        ],\n        [\n          -122.052138,\n          37.390535\n        ],\n        [\n          -122.009566,\n          37.390535\n        ],\n        [\n          -122.009566,\n          37.317585\n        ],\n        [\n          -122.052138,\n          37.317585\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"REMOVE\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"LINE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteLine\",\n    \"identifier\": 123,\n    \"WKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedNodeFull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    12.49234,\n    41.890224,\n    12.49234,\n    41.890224\n  ],\n  \"geometry\": {\n    \"type\": \"Point\",\n    \"coordinates\": [\n      12.49234,\n      41.890224\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"NODE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteNode\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"tagKey1\": \"tagValue1\",\n      \"tagKey2\": \"tagValue2\"\n    },\n    \"relations\": [\n      444,\n      555\n    ],\n    \"inEdges\": [\n      456,\n      789\n    ],\n    \"outEdges\": [\n      456,\n      789\n    ],\n    \"WKT\": \"POINT (12.49234 41.890224)\",\n    \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedNodeNull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    12.49234,\n    41.890224,\n    12.49234,\n    41.890224\n  ],\n  \"geometry\": {\n    \"type\": \"Point\",\n    \"coordinates\": [\n      12.49234,\n      41.890224\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"NODE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteNode\",\n    \"identifier\": 123,\n    \"WKT\": \"POINT (12.49234 41.890224)\",\n    \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedNodeRemove.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    12.49234,\n    41.890224,\n    12.49234,\n    41.890224\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          12.49234,\n          41.890224\n        ],\n        [\n          12.49234,\n          41.890224\n        ],\n        [\n          12.49234,\n          41.890224\n        ],\n        [\n          12.49234,\n          41.890224\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"REMOVE\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"NODE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteNode\",\n    \"identifier\": 123,\n    \"WKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\",\n    \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedPointFull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    12.49234,\n    41.890224,\n    12.49234,\n    41.890224\n  ],\n  \"geometry\": {\n    \"type\": \"Point\",\n    \"coordinates\": [\n      12.49234,\n      41.890224\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"POINT\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompletePoint\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"tagKey1\": \"tagValue1\",\n      \"tagKey2\": \"tagValue2\"\n    },\n    \"relations\": [\n      444,\n      555\n    ],\n    \"WKT\": \"POINT (12.49234 41.890224)\",\n    \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedPointNull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    12.49234,\n    41.890224,\n    12.49234,\n    41.890224\n  ],\n  \"geometry\": {\n    \"type\": \"Point\",\n    \"coordinates\": [\n      12.49234,\n      41.890224\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"POINT\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompletePoint\",\n    \"identifier\": 123,\n    \"WKT\": \"POINT (12.49234 41.890224)\",\n    \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedPointRemove.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    12.49234,\n    41.890224,\n    12.49234,\n    41.890224\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          12.49234,\n          41.890224\n        ],\n        [\n          12.49234,\n          41.890224\n        ],\n        [\n          12.49234,\n          41.890224\n        ],\n        [\n          12.49234,\n          41.890224\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"REMOVE\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"POINT\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompletePoint\",\n    \"identifier\": 123,\n    \"WKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\",\n    \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedPointWithTags.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    12.49234,\n    41.890224,\n    12.49234,\n    41.890224\n  ],\n  \"geometry\": {\n    \"type\": \"Point\",\n    \"coordinates\": [\n      12.49234,\n      41.890224\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key1\": \"value1\",\n      \"key2\": \"value2\",\n      \"key3\": \"value3\"\n    },\n    \"entityType\": \"POINT\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompletePoint\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"tagKey1\": \"tagValue1\",\n      \"tagKey2\": \"tagValue2\",\n      \"tagKey3\": \"tagValue3\",\n      \"tagKey4\": \"tagValue4\",\n      \"tagKey5\": \"tagValue5\",\n      \"tagKey6\": \"tagValue6\"\n    },\n    \"relations\": [\n      444,\n      555\n    ],\n    \"WKT\": \"POINT (12.49234 41.890224)\",\n    \"bboxWKT\": \"POLYGON ((12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224, 12.49234 41.890224))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedRelationFull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.031905,\n    37.328167,\n    -122.029051,\n    37.330394\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.031905,\n          37.328167\n        ],\n        [\n          -122.031905,\n          37.330394\n        ],\n        [\n          -122.029051,\n          37.330394\n        ],\n        [\n          -122.029051,\n          37.328167\n        ],\n        [\n          -122.031905,\n          37.328167\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"RELATION\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"tagKey1\": \"tagValue1\",\n      \"tagKey2\": \"tagValue2\"\n    },\n    \"relations\": [\n      444,\n      555\n    ],\n    \"members\": [\n      \"{Member: ID = 456, Type = EDGE, Role = role1}\",\n      \"{Member: ID = 789, Type = AREA, Role = role2}\"\n    ],\n    \"WKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\",\n    \"bboxWKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedRelationNull.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.031905,\n    37.328167,\n    -122.029051,\n    37.330394\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.031905,\n          37.328167\n        ],\n        [\n          -122.031905,\n          37.330394\n        ],\n        [\n          -122.029051,\n          37.330394\n        ],\n        [\n          -122.029051,\n          37.328167\n        ],\n        [\n          -122.031905,\n          37.328167\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"RELATION\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation\",\n    \"identifier\": 123,\n    \"members\": [\n      \"{Member: ID = 456, Type = EDGE, Role = role1}\",\n      \"{Member: ID = 789, Type = AREA, Role = role2}\"\n    ],\n    \"WKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\",\n    \"bboxWKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedRelationRemove.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.031905,\n    37.328167,\n    -122.029051,\n    37.330394\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.031905,\n          37.328167\n        ],\n        [\n          -122.031905,\n          37.330394\n        ],\n        [\n          -122.029051,\n          37.330394\n        ],\n        [\n          -122.029051,\n          37.328167\n        ],\n        [\n          -122.031905,\n          37.328167\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"REMOVE\",\n    \"metadata\": {\n      \"key\": \"value\"\n    },\n    \"entityType\": \"RELATION\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation\",\n    \"identifier\": 123,\n    \"WKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\",\n    \"bboxWKT\": \"POLYGON ((-122.031905 37.328167, -122.031905 37.330394, -122.029051 37.330394, -122.029051 37.328167, -122.031905 37.328167))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedRemoveAreaWithDescription.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -122.052138,\n          37.317585\n        ],\n        [\n          -122.0304871,\n          37.3314171\n        ],\n        [\n          -122.028932,\n          37.332451\n        ],\n        [\n          -122.009566,\n          37.33531\n        ],\n        [\n          -122.031007,\n          37.390535\n        ],\n        [\n          -122.052138,\n          37.317585\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"REMOVE\",\n    \"metadata\": {},\n    \"description\": {\n      \"type\": \"REMOVE\",\n      \"descriptors\": []\n    },\n    \"entityType\": \"AREA\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteArea\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"a\": \"1\"\n    },\n    \"relations\": [\n      1\n    ],\n    \"WKT\": \"POLYGON ((-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451, -122.009566 37.33531, -122.031007 37.390535, -122.052138 37.317585))\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedReverseWay.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    176.6340268,\n    -38.3647737,\n    176.674926,\n    -38.359739\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        176.6340268,\n        -38.3647737\n      ],\n      [\n        176.6344755,\n        -38.3641479\n      ],\n      [\n        176.6348776,\n        -38.363705\n      ],\n      [\n        176.6353128,\n        -38.3633276\n      ],\n      [\n        176.6359503,\n        -38.3629646\n      ],\n      [\n        176.6360268,\n        -38.3628043\n      ],\n      [\n        176.63608,\n        -38.3622832\n      ],\n      [\n        176.636216,\n        -38.3621652\n      ],\n      [\n        176.6366732,\n        -38.3621479\n      ],\n      [\n        176.6368411,\n        -38.362074\n      ],\n      [\n        176.6376985,\n        -38.3620421\n      ],\n      [\n        176.6388002,\n        -38.3617976\n      ],\n      [\n        176.6392288,\n        -38.3617811\n      ],\n      [\n        176.639949,\n        -38.3618447\n      ],\n      [\n        176.6404785,\n        -38.3620721\n      ],\n      [\n        176.6407357,\n        -38.3620629\n      ],\n      [\n        176.6412033,\n        -38.3622256\n      ],\n      [\n        176.641464,\n        -38.3622605\n      ],\n      [\n        176.642463,\n        -38.3622005\n      ],\n      [\n        176.6426298,\n        -38.3621267\n      ],\n      [\n        176.6434867,\n        -38.3616204\n      ],\n      [\n        176.6442583,\n        -38.361592\n      ],\n      [\n        176.6445679,\n        -38.3614892\n      ],\n      [\n        176.6448127,\n        -38.3613471\n      ],\n      [\n        176.6451483,\n        -38.3611523\n      ],\n      [\n        176.6457163,\n        -38.3610628\n      ],\n      [\n        176.6461893,\n        -38.3608421\n      ],\n      [\n        176.6467102,\n        -38.360461\n      ],\n      [\n        176.6475488,\n        -38.360114\n      ],\n      [\n        176.6479909,\n        -38.3598491\n      ],\n      [\n        176.6482709,\n        -38.3597482\n      ],\n      [\n        176.6485281,\n        -38.359739\n      ],\n      [\n        176.6489811,\n        -38.3601276\n      ],\n      [\n        176.6493036,\n        -38.3602506\n      ],\n      [\n        176.649531,\n        -38.3602199\n      ],\n      [\n        176.6497252,\n        -38.3601217\n      ],\n      [\n        176.6500507,\n        -38.3598164\n      ],\n      [\n        176.6504496,\n        -38.3597792\n      ],\n      [\n        176.6506542,\n        -38.3598385\n      ],\n      [\n        176.6508922,\n        -38.3599877\n      ],\n      [\n        176.651242,\n        -38.3600874\n      ],\n      [\n        176.6513931,\n        -38.3602169\n      ],\n      [\n        176.6514,\n        -38.3603294\n      ],\n      [\n        176.6512962,\n        -38.360514\n      ],\n      [\n        176.6514461,\n        -38.360621\n      ],\n      [\n        176.6516222,\n        -38.3606821\n      ],\n      [\n        176.6521662,\n        -38.3606835\n      ],\n      [\n        176.6524474,\n        -38.360605\n      ],\n      [\n        176.6531058,\n        -38.3606028\n      ],\n      [\n        176.6534691,\n        -38.3604541\n      ],\n      [\n        176.6537263,\n        -38.360444\n      ],\n      [\n        176.6541608,\n        -38.3605184\n      ],\n      [\n        176.6547748,\n        -38.3607205\n      ],\n      [\n        176.6554606,\n        -38.3606939\n      ],\n      [\n        176.6562182,\n        -38.3604405\n      ],\n      [\n        176.6568853,\n        -38.3600989\n      ],\n      [\n        176.6571985,\n        -38.3600645\n      ],\n      [\n        176.6573449,\n        -38.3601265\n      ],\n      [\n        176.6578814,\n        -38.3604673\n      ],\n      [\n        176.6582919,\n        -38.3606326\n      ],\n      [\n        176.6585951,\n        -38.3608916\n      ],\n      [\n        176.6586093,\n        -38.3609168\n      ],\n      [\n        176.6587555,\n        -38.3611786\n      ],\n      [\n        176.6589053,\n        -38.3612855\n      ],\n      [\n        176.6590803,\n        -38.3613466\n      ],\n      [\n        176.6595148,\n        -38.3614201\n      ],\n      [\n        176.6603732,\n        -38.3614105\n      ],\n      [\n        176.6609424,\n        -38.3613435\n      ],\n      [\n        176.6611733,\n        -38.3613802\n      ],\n      [\n        176.6623956,\n        -38.3616943\n      ],\n      [\n        176.6629946,\n        -38.3616489\n      ],\n      [\n        176.6631695,\n        -38.3616874\n      ],\n      [\n        176.6634943,\n        -38.3618555\n      ],\n      [\n        176.6637253,\n        -38.3618923\n      ],\n      [\n        176.6638956,\n        -38.3618633\n      ],\n      [\n        176.6641469,\n        -38.3617632\n      ],\n      [\n        176.6650042,\n        -38.3617311\n      ],\n      [\n        176.6652375,\n        -38.3617894\n      ],\n      [\n        176.6655979,\n        -38.362069\n      ],\n      [\n        176.6663683,\n        -38.3620171\n      ],\n      [\n        176.6665718,\n        -38.3620548\n      ],\n      [\n        176.6671585,\n        -38.3622802\n      ],\n      [\n        176.66753,\n        -38.3622664\n      ],\n      [\n        176.6683826,\n        -38.3621668\n      ],\n      [\n        176.6690136,\n        -38.3621879\n      ],\n      [\n        176.6694504,\n        -38.3623063\n      ],\n      [\n        176.6697076,\n        -38.3622962\n      ],\n      [\n        176.670047,\n        -38.3622158\n      ],\n      [\n        176.6707614,\n        -38.3621892\n      ],\n      [\n        176.6710507,\n        -38.3622457\n      ],\n      [\n        176.671365,\n        -38.3622337\n      ],\n      [\n        176.6717172,\n        -38.3623558\n      ],\n      [\n        176.6723745,\n        -38.3623309\n      ],\n      [\n        176.6725768,\n        -38.3623677\n      ],\n      [\n        176.673317,\n        -38.3627685\n      ],\n      [\n        176.6736681,\n        -38.3628906\n      ],\n      [\n        176.6740728,\n        -38.3629425\n      ],\n      [\n        176.674926,\n        -38.3629627\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {},\n    \"description\": {\n      \"type\": \"UPDATE\",\n      \"descriptors\": [\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"ADD\",\n          \"position\": \"98/98\",\n          \"afterView\": \"LINESTRING (176.6344755 -38.3641479, 176.6348776 -38.363705, 176.6353128 -38.3633276, 176.6359503 -38.3629646, 176.6360268 -38.3628043, 176.63608 -38.3622832, 176.636216 -38.3621652, 176.6366732 -38.3621479, 176.6368411 -38.362074, 176.6376985 -38.3620421, 176.6388002 -38.3617976, 176.6392288 -38.3617811, 176.639949 -38.3618447, 176.6404785 -38.3620721, 176.6407357 -38.3620629, 176.6412033 -38.3622256, 176.641464 -38.3622605, 176.642463 -38.3622005, 176.6426298 -38.3621267, 176.6434867 -38.3616204, 176.6442583 -38.361592, 176.6445679 -38.3614892, 176.6448127 -38.3613471, 176.6451483 -38.3611523, 176.6457163 -38.3610628, 176.6461893 -38.3608421, 176.6467102 -38.360461, 176.6475488 -38.360114, 176.6479909 -38.3598491, 176.6482709 -38.3597482, 176.6485281 -38.359739, 176.6489811 -38.3601276, 176.6493036 -38.3602506, 176.649531 -38.3602199, 176.6497252 -38.3601217, 176.6500507 -38.3598164, 176.6504496 -38.3597792, 176.6506542 -38.3598385, 176.6508922 -38.3599877, 176.651242 -38.3600874, 176.6513931 -38.3602169, 176.6514 -38.3603294, 176.6512962 -38.360514, 176.6514461 -38.360621, 176.6516222 -38.3606821, 176.6521662 -38.3606835, 176.6524474 -38.360605, 176.6531058 -38.3606028, 176.6534691 -38.3604541, 176.6537263 -38.360444, 176.6541608 -38.3605184, 176.6547748 -38.3607205, 176.6554606 -38.3606939, 176.6562182 -38.3604405, 176.6568853 -38.3600989, 176.6571985 -38.3600645, 176.6573449 -38.3601265, 176.6578814 -38.3604673, 176.6582919 -38.3606326, 176.6585951 -38.3608916, 176.6586093 -38.3609168, 176.6587555 -38.3611786, 176.6589053 -38.3612855, 176.6590803 -38.3613466, 176.6595148 -38.3614201, 176.6603732 -38.3614105, 176.6609424 -38.3613435, 176.6611733 -38.3613802, 176.6623956 -38.3616943, 176.6629946 -38.3616489, 176.6631695 -38.3616874, 176.6634943 -38.3618555, 176.6637253 -38.3618923, 176.6638956 -38.3618633, 176.6641469 -38.3617632, 176.6650042 -38.3617311, 176.6652375 -38.3617894, 176.6655979 -38.362069, 176.6663683 -38.3620171, 176.6665718 -38.3620548, 176.6671585 -38.3622802, 176.66753 -38.3622664, 176.6683826 -38.3621668, 176.6690136 -38.3621879, 176.6694504 -38.3623063, 176.6697076 -38.3622962, 176.670047 -38.3622158, 176.6707614 -38.3621892, 176.6710507 -38.3622457, 176.671365 -38.3622337, 176.6717172 -38.3623558, 176.6723745 -38.3623309, 176.6725768 -38.3623677, 176.673317 -38.3627685, 176.6736681 -38.3628906, 176.6740728 -38.3629425, 176.674926 -38.3629627)\"\n        },\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"REMOVE\",\n          \"position\": \"0/98\",\n          \"beforeView\": \"LINESTRING (176.674926 -38.3629627, 176.6740728 -38.3629425, 176.6736681 -38.3628906, 176.673317 -38.3627685, 176.6725768 -38.3623677, 176.6723745 -38.3623309, 176.6717172 -38.3623558, 176.671365 -38.3622337, 176.6710507 -38.3622457, 176.6707614 -38.3621892, 176.670047 -38.3622158, 176.6697076 -38.3622962, 176.6694504 -38.3623063, 176.6690136 -38.3621879, 176.6683826 -38.3621668, 176.66753 -38.3622664, 176.6671585 -38.3622802, 176.6665718 -38.3620548, 176.6663683 -38.3620171, 176.6655979 -38.362069, 176.6652375 -38.3617894, 176.6650042 -38.3617311, 176.6641469 -38.3617632, 176.6638956 -38.3618633, 176.6637253 -38.3618923, 176.6634943 -38.3618555, 176.6631695 -38.3616874, 176.6629946 -38.3616489, 176.6623956 -38.3616943, 176.6611733 -38.3613802, 176.6609424 -38.3613435, 176.6603732 -38.3614105, 176.6595148 -38.3614201, 176.6590803 -38.3613466, 176.6589053 -38.3612855, 176.6587555 -38.3611786, 176.6586093 -38.3609168, 176.6585951 -38.3608916, 176.6582919 -38.3606326, 176.6578814 -38.3604673, 176.6573449 -38.3601265, 176.6571985 -38.3600645, 176.6568853 -38.3600989, 176.6562182 -38.3604405, 176.6554606 -38.3606939, 176.6547748 -38.3607205, 176.6541608 -38.3605184, 176.6537263 -38.360444, 176.6534691 -38.3604541, 176.6531058 -38.3606028, 176.6524474 -38.360605, 176.6521662 -38.3606835, 176.6516222 -38.3606821, 176.6514461 -38.360621, 176.6512962 -38.360514, 176.6514 -38.3603294, 176.6513931 -38.3602169, 176.651242 -38.3600874, 176.6508922 -38.3599877, 176.6506542 -38.3598385, 176.6504496 -38.3597792, 176.6500507 -38.3598164, 176.6497252 -38.3601217, 176.649531 -38.3602199, 176.6493036 -38.3602506, 176.6489811 -38.3601276, 176.6485281 -38.359739, 176.6482709 -38.3597482, 176.6479909 -38.3598491, 176.6475488 -38.360114, 176.6467102 -38.360461, 176.6461893 -38.3608421, 176.6457163 -38.3610628, 176.6451483 -38.3611523, 176.6448127 -38.3613471, 176.6445679 -38.3614892, 176.6442583 -38.361592, 176.6434867 -38.3616204, 176.6426298 -38.3621267, 176.642463 -38.3622005, 176.641464 -38.3622605, 176.6412033 -38.3622256, 176.6407357 -38.3620629, 176.6404785 -38.3620721, 176.639949 -38.3618447, 176.6392288 -38.3617811, 176.6388002 -38.3617976, 176.6376985 -38.3620421, 176.6368411 -38.362074, 176.6366732 -38.3621479, 176.636216 -38.3621652, 176.63608 -38.3622832, 176.6360268 -38.3628043, 176.6359503 -38.3629646, 176.6353128 -38.3633276, 176.6348776 -38.363705, 176.6344755 -38.3641479)\"\n        }\n      ],\n      \"osc\": \"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48b3NtQ2hhbmdlIGdlbmVyYXRvcj0iYXRsYXMgQ2hhbmdlRGVzY3JpcHRpb24gdjAuMC4xIiB2ZXJzaW9uPSIwLjYiPjxtb2RpZnk+PHdheSBhY3Rpb249Im1vZGlmeSIgaWQ9IjM3NDkwMjgzNCIgdmVyc2lvbj0iMSIgdmlzaWJsZT0idHJ1ZSI+PHRhZyBrPSJ3YXRlcndheSIgdj0ic3RyZWFtIi8+PG5kIHJlZj0iMzc4Mjg0Nzc0NCIvPjxuZCByZWY9IjM3ODI4NDc3NDAiLz48bmQgcmVmPSIzNzgyODQ3NzM3Ii8+PG5kIHJlZj0iMzc4Mjg0NzczNSIvPjxuZCByZWY9IjM3ODI4NDc3MzIiLz48bmQgcmVmPSIzNzgyODQ3NzI2Ii8+PG5kIHJlZj0iMzc4Mjg0NzcxOCIvPjxuZCByZWY9IjM3ODI4NDc3MDUiLz48bmQgcmVmPSIzNzgyODQ3NzA0Ii8+PG5kIHJlZj0iMzc4Mjg0NzcwMiIvPjxuZCByZWY9IjM3ODI4NDc2OTciLz48bmQgcmVmPSIzNzgyODQ3NjkwIi8+PG5kIHJlZj0iMzc4Mjg0NzY4OCIvPjxuZCByZWY9IjM3ODI4NDc2OTEiLz48bmQgcmVmPSIzNzgyODQ3NzAxIi8+PG5kIHJlZj0iMzc4Mjg0NzY5OSIvPjxuZCByZWY9IjM3ODI4NDc3MTEiLz48bmQgcmVmPSIzNzgyODQ3NzE0Ii8+PG5kIHJlZj0iMzc4Mjg0NzcwOSIvPjxuZCByZWY9IjM3ODI4NDc3MDMiLz48bmQgcmVmPSIzNzgyODQ3Njc5Ii8+PG5kIHJlZj0iMzc4Mjg0NzY3OCIvPjxuZCByZWY9IjM3ODI4NDc2NzciLz48bmQgcmVmPSIzNzgyODQ3NjcyIi8+PG5kIHJlZj0iMzc4Mjg0NzY2NyIvPjxuZCByZWY9IjM3ODI4NDc2NjUiLz48bmQgcmVmPSIzNzgyODQ3NjYyIi8+PG5kIHJlZj0iMzc4Mjg0NzY1MCIvPjxuZCByZWY9IjM3ODI4NDc2MzUiLz48bmQgcmVmPSIzNzgyODQ3NjI0Ii8+PG5kIHJlZj0iMzc4Mjg0NzYyMCIvPjxuZCByZWY9IjM3ODI4NDc2MTgiLz48bmQgcmVmPSIzNzgyODQ3NjM4Ii8+PG5kIHJlZj0iMzc4Mjg0NzY0MiIvPjxuZCByZWY9IjM3ODI4NDc2NDEiLz48bmQgcmVmPSIzNzgyODQ3NjM2Ii8+PG5kIHJlZj0iMzc4Mjg0NzYyMiIvPjxuZCByZWY9IjM3ODI4NDc2MjEiLz48bmQgcmVmPSIzNzgyODQ3NjIzIi8+PG5kIHJlZj0iMzc4Mjg0NzYyOCIvPjxuZCByZWY9IjM3ODI4NDc2MzIiLz48bmQgcmVmPSIzNzgyODQ3NjQwIi8+PG5kIHJlZj0iMzc4Mjg0NzY0NCIvPjxuZCByZWY9IjM3ODI4NDc2NTIiLz48bmQgcmVmPSIzNzgyODQ3NjU2Ii8+PG5kIHJlZj0iMzc4Mjg0NzY1OCIvPjxuZCByZWY9IjM3ODI4NDc2NTkiLz48bmQgcmVmPSIzNzgyODQ3NjU1Ii8+PG5kIHJlZj0iMzc4Mjg0NzY1NCIvPjxuZCByZWY9IjM3ODI4NDc2NDkiLz48bmQgcmVmPSIzNzgyODQ3NjQ4Ii8+PG5kIHJlZj0iMzc4Mjg0NzY1MyIvPjxuZCByZWY9IjM3ODI4NDc2NjEiLz48bmQgcmVmPSIzNzgyODQ3NjYwIi8+PG5kIHJlZj0iMzc4Mjg0NzY0NyIvPjxuZCByZWY9IjM3ODI4NDc2MzQiLz48bmQgcmVmPSIzNzgyODQ3NjMwIi8+PG5kIHJlZj0iMzc4Mjg0NzYzNyIvPjxuZCByZWY9IjM3ODI4NDc2NTEiLz48bmQgcmVmPSIzNzgyODQ3NjU3Ii8+PG5kIHJlZj0iMzc4Mjg0NzY2MyIvPjxuZCByZWY9IjM3ODI4NDc2NjQiLz48bmQgcmVmPSIzNzgyODQ3NjY4Ii8+PG5kIHJlZj0iMzc4Mjg0NzY2OSIvPjxuZCByZWY9IjM3ODI4NDc2NzEiLz48bmQgcmVmPSIzNzgyODQ3Njc1Ii8+PG5kIHJlZj0iMzc4Mjg0NzY3NCIvPjxuZCByZWY9IjM3ODI4NDc2NzAiLz48bmQgcmVmPSIzNzgyODQ3NjczIi8+PG5kIHJlZj0iMzc4Mjg0NzY4NSIvPjxuZCByZWY9IjM3ODI4NDc2ODEiLz48bmQgcmVmPSIzNzgyODQ3Njg0Ii8+PG5kIHJlZj0iMzc4Mjg0NzY5MiIvPjxuZCByZWY9IjM3ODI4NDc2OTQiLz48bmQgcmVmPSIzNzgyODQ3NjkzIi8+PG5kIHJlZj0iMzc4Mjg0NzY4NyIvPjxuZCByZWY9IjM3ODI4NDc2ODYiLz48bmQgcmVmPSIzNzgyODQ3Njg5Ii8+PG5kIHJlZj0iMzc4Mjg0NzcwMCIvPjxuZCByZWY9IjM3ODI4NDc2OTYiLz48bmQgcmVmPSIzNzgyODQ3Njk4Ii8+PG5kIHJlZj0iMzc4Mjg0NzcxNyIvPjxuZCByZWY9IjM3ODI4NDc3MTYiLz48bmQgcmVmPSIzNzgyODQ3NzA2Ii8+PG5kIHJlZj0iMzc4Mjg0NzcwNyIvPjxuZCByZWY9IjM3ODI4NDc3MjAiLz48bmQgcmVmPSIzNzgyODQ3NzE5Ii8+PG5kIHJlZj0iMzc4Mjg0NzcxMCIvPjxuZCByZWY9IjM3ODI4NDc3MDgiLz48bmQgcmVmPSIzNzgyODQ3NzEzIi8+PG5kIHJlZj0iMzc4Mjg0NzcxMiIvPjxuZCByZWY9IjM3ODI4NDc3MjIiLz48bmQgcmVmPSIzNzgyODQ3NzIxIi8+PG5kIHJlZj0iMzc4Mjg0NzcyMyIvPjxuZCByZWY9IjM3ODI4NDc3MjUiLz48bmQgcmVmPSIzNzgyODQ3NzI3Ii8+PG5kIHJlZj0iMzc4Mjg0NzcyOCIvPjxuZCByZWY9IjM3ODI4NDc3MjkiLz48L3dheT48L21vZGlmeT48L29zbUNoYW5nZT4=\"\n    },\n    \"entityType\": \"LINE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteLine\",\n    \"identifier\": 374902834000000,\n    \"WKT\": \"LINESTRING (176.6340268 -38.3647737, 176.6344755 -38.3641479, 176.6348776 -38.363705, 176.6353128 -38.3633276, 176.6359503 -38.3629646, 176.6360268 -38.3628043, 176.63608 -38.3622832, 176.636216 -38.3621652, 176.6366732 -38.3621479, 176.6368411 -38.362074, 176.6376985 -38.3620421, 176.6388002 -38.3617976, 176.6392288 -38.3617811, 176.639949 -38.3618447, 176.6404785 -38.3620721, 176.6407357 -38.3620629, 176.6412033 -38.3622256, 176.641464 -38.3622605, 176.642463 -38.3622005, 176.6426298 -38.3621267, 176.6434867 -38.3616204, 176.6442583 -38.361592, 176.6445679 -38.3614892, 176.6448127 -38.3613471, 176.6451483 -38.3611523, 176.6457163 -38.3610628, 176.6461893 -38.3608421, 176.6467102 -38.360461, 176.6475488 -38.360114, 176.6479909 -38.3598491, 176.6482709 -38.3597482, 176.6485281 -38.359739, 176.6489811 -38.3601276, 176.6493036 -38.3602506, 176.649531 -38.3602199, 176.6497252 -38.3601217, 176.6500507 -38.3598164, 176.6504496 -38.3597792, 176.6506542 -38.3598385, 176.6508922 -38.3599877, 176.651242 -38.3600874, 176.6513931 -38.3602169, 176.6514 -38.3603294, 176.6512962 -38.360514, 176.6514461 -38.360621, 176.6516222 -38.3606821, 176.6521662 -38.3606835, 176.6524474 -38.360605, 176.6531058 -38.3606028, 176.6534691 -38.3604541, 176.6537263 -38.360444, 176.6541608 -38.3605184, 176.6547748 -38.3607205, 176.6554606 -38.3606939, 176.6562182 -38.3604405, 176.6568853 -38.3600989, 176.6571985 -38.3600645, 176.6573449 -38.3601265, 176.6578814 -38.3604673, 176.6582919 -38.3606326, 176.6585951 -38.3608916, 176.6586093 -38.3609168, 176.6587555 -38.3611786, 176.6589053 -38.3612855, 176.6590803 -38.3613466, 176.6595148 -38.3614201, 176.6603732 -38.3614105, 176.6609424 -38.3613435, 176.6611733 -38.3613802, 176.6623956 -38.3616943, 176.6629946 -38.3616489, 176.6631695 -38.3616874, 176.6634943 -38.3618555, 176.6637253 -38.3618923, 176.6638956 -38.3618633, 176.6641469 -38.3617632, 176.6650042 -38.3617311, 176.6652375 -38.3617894, 176.6655979 -38.362069, 176.6663683 -38.3620171, 176.6665718 -38.3620548, 176.6671585 -38.3622802, 176.66753 -38.3622664, 176.6683826 -38.3621668, 176.6690136 -38.3621879, 176.6694504 -38.3623063, 176.6697076 -38.3622962, 176.670047 -38.3622158, 176.6707614 -38.3621892, 176.6710507 -38.3622457, 176.671365 -38.3622337, 176.6717172 -38.3623558, 176.6723745 -38.3623309, 176.6725768 -38.3623677, 176.673317 -38.3627685, 176.6736681 -38.3628906, 176.6740728 -38.3629425, 176.674926 -38.3629627)\",\n    \"bboxWKT\": \"POLYGON ((176.6340268 -38.3647737, 176.6340268 -38.359739, 176.674926 -38.359739, 176.674926 -38.3647737, 176.6340268 -38.3647737))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/change/serializer/serializedUpdateEdgeWithDescription.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        -122.009566,\n        37.33531\n      ],\n      [\n        -122.031007,\n        37.390535\n      ],\n      [\n        -122.028932,\n        37.332451\n      ],\n      [\n        -122.052138,\n        37.317585\n      ],\n      [\n        -122.0304871,\n        37.3314171\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {},\n    \"description\": {\n      \"type\": \"UPDATE\",\n      \"descriptors\": [\n        {\n          \"name\": \"TAG\",\n          \"type\": \"ADD\",\n          \"key\": \"c\",\n          \"value\": \"3\"\n        },\n        {\n          \"name\": \"TAG\",\n          \"type\": \"UPDATE\",\n          \"key\": \"b\",\n          \"value\": \"2a\",\n          \"originalValue\": \"2\"\n        },\n        {\n          \"name\": \"TAG\",\n          \"type\": \"REMOVE\",\n          \"key\": \"a\",\n          \"value\": \"1\"\n        },\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"ADD\",\n          \"position\": \"5/5\",\n          \"afterView\": \"LINESTRING (-122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\"\n        },\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"REMOVE\",\n          \"position\": \"0/5\",\n          \"beforeView\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451)\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"ADD\",\n          \"afterElement\": \"3\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"REMOVE\",\n          \"beforeElement\": \"1\"\n        },\n        {\n          \"name\": \"START_NODE\",\n          \"type\": \"UPDATE\",\n          \"beforeElement\": \"1\",\n          \"afterElement\": \"10\"\n        },\n        {\n          \"name\": \"END_NODE\",\n          \"type\": \"UPDATE\",\n          \"beforeElement\": \"2\",\n          \"afterElement\": \"20\"\n        }\n      ]\n    },\n    \"entityType\": \"EDGE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"b\": \"2a\",\n      \"c\": \"3\"\n    },\n    \"relations\": [\n      2,\n      3\n    ],\n    \"startNode\": \"10\",\n    \"endNode\": \"20\",\n    \"WKT\": \"LINESTRING (-122.009566 37.33531, -122.031007 37.390535, -122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/DNK_Copenhagen/DNK_1.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n# Points\n5057300880000000 && 55.666602,12.503904 && last_edit_user_name -> Niels Elgaard Larsen || last_edit_changeset -> 51420369 || last_edit_time -> 1503613687000 || last_edit_user_id -> 1288 || iso_country_code -> DNK || amenity -> fast_food || name -> Limdim || cuisine -> thai || last_edit_version -> 1 || fvst:navnelbnr -> 711461\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/DNK_Copenhagen/DNK_2.atlas.txt",
    "content": "# Nodes\n30219770000000 && 55.7612485,12.5333169 && last_edit_user_name -> Hjart || last_edit_changeset -> 14497130 || last_edit_time -> 1357121587000 || last_edit_user_id -> 207581 || iso_country_code -> DNK || last_edit_version -> 4\n5282518816000000 && 55.7611777,12.5338608 && last_edit_user_name -> Hjart || last_edit_changeset -> 54603963 || last_edit_time -> 1513189938000 || last_edit_user_id -> 207581 || iso_country_code -> DNK || last_edit_version -> 1\n# Edges\n546649246000001 && 55.7612485,12.5333169:55.7611777,12.5338608 && last_edit_user_name -> Hjart || last_edit_changeset -> 54603963 || last_edit_time -> 1513189939000 || last_edit_user_id -> 207581 || iso_country_code -> DNK || highway -> service || last_edit_version -> 1 || note -> 10% slope\n# Areas\n# Lines\n# Points\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/DNK_Copenhagen/DNK_3.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n575954012000000 && 55.6000372,12.591551:55.6000642,12.5917118:55.6000212,12.5917343:55.600006,12.5916436:55.5999627,12.5916663:55.5999531,12.5916091:55.5999826,12.5915937:55.5999815,12.5915871:55.5999953,12.5915799:55.5999943,12.5915735 && last_edit_user_name -> osmviborg || last_edit_changeset -> 57771646 || last_edit_time -> 1522759697000 || last_edit_user_id -> 379467 || iso_country_code -> DNK || last_edit_version -> 1 || building -> yes\n# Lines\n# Points\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/atlas-edge.json",
    "content": "{\n    \"filters\": [\n        \"access->!no|motor_vehicle->yes|motorcar->yes|vehicle->yes\",\n        \"oneway->!reversible\",\n        \"route->ferry|junction->roundabout|highway->MOTORWAY,TRUNK,PRIMARY,SECONDARY,TERTIARY,UNCLASSIFIED,RESIDENTIAL,SERVICE,MOTORWAY_LINK,TRUNK_LINK,PRIMARY_LINK,SECONDARY_LINK,TERTIARY_LINK,LIVING_STREET,PEDESTRIAN,TRACK,BUS_GUIDEWAY,RACEWAY,ROAD,FOOTWAY,BRIDLEWAY,STEPS,PATH,CYCLEWAY,ESCAPE\"\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/atlas-way-section.json",
    "content": "{\n    \"filters\": [\n        \"source->Bing|highway->crossing,motorway_junction|railway->crossing,level_crossing|barrier->FENCE,WALL,GATE,HEDGE,BOLLARD,LIFT_GATE,RETAINING_WALL,STILE,CYCLE_BARRIER,KERB,YES,ENTRANCE,BLOCK,TOLL_BOOTH,CATTLE_GRID,DITCH,KISSING_GATE,CITY_WALL,GUARD_RAIL,HEDGE_BANK,WIRE_FENCE,LINE,SWING_GATE,CHAIN,TURNSTILE,EMBANKMENT,FIELD_BOUNDARY,BORDER_CONTROL,SALLY_PORT,DOOR,HAMPSHIRE_GATE,WOOD_FENCE,BUMP_GATE\"\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/complex-SF.txt",
    "content": "# Nodes\n5640337260000000 && 37.7797498,-122.3873921 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337822000000 && 37.778711,-122.3871036 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n255649947000000 && 37.779285,-122.387467 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 16\n5640337570000000 && 37.7790435,-122.3870377 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652626000000 && 37.7781878,-122.3917296 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 1\n6932316479000000 && 37.7782627,-122.3876648 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n4688893431000000 && 37.7802121,-122.3881633 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n5640337318000000 && 37.7794102,-122.3870452 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946965000000 && 37.7789232,-122.3908842 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n3997831654000000 && 37.7771072,-122.3906413 && last_edit_user_name -> cdruck || last_edit_changeset -> 67775371 || last_edit_time -> 1551720937000 || last_edit_user_id -> 8762431 || last_edit_version -> 3\n5640337525000000 && 37.7792214,-122.3867232 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946978000000 && 37.7798461,-122.3897755 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 4 || crossing -> traffic_signals\n5640337118000000 && 37.7801766,-122.3871848 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n1865053219000000 && 37.7792283,-122.3880755 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255282000 || last_edit_user_id -> 4518035 || last_edit_version -> 3\n1136317650000000 && 37.7780112,-122.3915447 && last_edit_user_name -> mbuege || last_edit_changeset -> 7186612 || last_edit_time -> 1296852014000 || last_edit_user_id -> 4902 || last_edit_version -> 1\n5640337273000000 && 37.7795942,-122.3867583 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n4689017525000000 && 37.7798742,-122.3888631 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n7228926461000000 && 37.7793921,-122.3922973 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337564000000 && 37.7790167,-122.3873258 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6932348106000000 && 37.7782442,-122.3871761 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337073000000 && 37.7803815,-122.3872011 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n7209255186000000 && 37.7804776,-122.3905438 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625272000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n1865053216000000 && 37.7786439,-122.3875354 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255282000 || last_edit_user_id -> 4518035 || last_edit_version -> 2\n5640337312000000 && 37.7793844,-122.3873602 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n255649946000000 && 37.7794407,-122.3847697 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188136000 || last_edit_user_id -> 7134350 || last_edit_version -> 3\n5640337267000000 && 37.7795739,-122.3870659 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6311883023000000 && 37.7799221,-122.3896767 && last_edit_user_name -> Heidi_Baum_Lyft || last_edit_changeset -> 67703843 || last_edit_time -> 1551485903000 || last_edit_user_id -> 8778516 || last_edit_version -> 1\n5640337829000000 && 37.7785464,-122.3867977 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n257316381000000 && 37.7795153,-122.3887435 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399513000 || last_edit_user_id -> 933797 || last_edit_version -> 11\n5326946959000000 && 37.7788191,-122.3907594 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183957000 || last_edit_user_id -> 119881 || last_edit_version -> 3\n6318866709000000 && 37.7780687,-122.392029 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5640337577000000 && 37.7788785,-122.3867298 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946964000000 && 37.7789118,-122.3907615 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n5640337319000000 && 37.779205,-122.3870262 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946985000000 && 37.7800411,-122.3876564 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n6329652916000000 && 37.7758723,-122.3932348 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n260193244000000 && 37.7812398,-122.3882109 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255282000 || last_edit_user_id -> 4518035 || last_edit_version -> 10\n1142640695000000 && 37.7911224,-122.2944966 && last_edit_user_name -> clay_c || last_edit_changeset -> 76356726 || last_edit_time -> 1572368313000 || last_edit_user_id -> 119881 || amenity -> ferry_terminal || ferry -> yes || name -> Alameda Main Street Terminal || public_transport -> stop_position || last_edit_version -> 10 || operator -> Water Emergency Transportation Authority || network -> SFBF\n5640337524000000 && 37.7794295,-122.3867409 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652601000000 && 37.7769659,-122.3902647 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249934000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n1865053218000000 && 37.7795275,-122.3886443 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399512000 || last_edit_user_id -> 933797 || last_edit_version -> 6\n5640337272000000 && 37.7797963,-122.386777 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6384096453000000 && 37.7803259,-122.3901684 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404093000 || last_edit_user_id -> 8896306 || last_edit_version -> 1\n2845162690000000 && 37.7772671,-122.3908391 && last_edit_user_name -> cdruck || last_edit_changeset -> 67894144 || last_edit_time -> 1551982182000 || last_edit_user_id -> 8762431 || last_edit_version -> 6\n4688893417000000 && 37.7789773,-122.3881116 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n6391212601000000 && 37.7790694,-122.3925052 && last_edit_user_name -> Fikadu Balcha || last_edit_changeset -> 69010379 || last_edit_time -> 1554734987000 || last_edit_user_id -> 9051248 || last_edit_version -> 1\n5640337261000000 && 37.7795676,-122.3873761 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337823000000 && 37.7785294,-122.387086 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n603369653000000 && 37.7790733,-122.3908021 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249943000 || last_edit_user_id -> 119881 || last_edit_version -> 10\n6932348086000000 && 37.7779365,-122.3880178 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337072000000 && 37.7806277,-122.3872274 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5437054791000000 && 37.7773648,-122.3908154 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249945000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n5640337571000000 && 37.7788624,-122.3870196 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6932316478000000 && 37.7781587,-122.387601 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n4689493523000000 && 37.7796975,-122.3876256 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46148175 || last_edit_time -> 1487279006000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n7209260204000000 && 37.7788533,-122.3925908 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625271000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6384096452000000 && 37.7795788,-122.3901388 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404093000 || last_edit_user_id -> 8896306 || last_edit_version -> 1\n6391254602000000 && 37.7802753,-122.3911851 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149448000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> unmarked\n255649953000000 && 37.7790817,-122.3847962 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188136000 || last_edit_user_id -> 7134350 || last_edit_version -> 4\n3997831655000000 && 37.777341,-122.3925157 && last_edit_user_name -> samely || last_edit_changeset -> 39253952 || last_edit_time -> 1463004146000 || last_edit_user_id -> 2512300 || last_edit_version -> 2\n5640337266000000 && 37.7797773,-122.387084 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337828000000 && 37.7787291,-122.386814 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337119000000 && 37.7799513,-122.3871634 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6318866708000000 && 37.777882,-122.3918136 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5326946979000000 && 37.7798589,-122.3892282 && last_edit_user_name -> Heidi_Baum_Lyft || last_edit_changeset -> 67703843 || last_edit_time -> 1551485904000 || last_edit_user_id -> 8778516 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> uncontrolled\n5640337576000000 && 37.77906,-122.3867493 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n4689017524000000 && 37.7798848,-122.3890227 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n5326946984000000 && 37.7800315,-122.3878142 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n6252543479000000 && 37.7798641,-122.3896058 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249945000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 2\n2252547630000000 && 37.7795731,-122.3876175 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399512000 || last_edit_user_id -> 933797 || last_edit_version -> 2\n5640337565000000 && 37.7788563,-122.3873112 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6328181218000000 && 37.7787133,-122.3911488 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183947000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n3999197448000000 && 37.7804023,-122.3902623 && last_edit_user_name -> Heidi_Baum_Lyft || last_edit_changeset -> 67703843 || last_edit_time -> 1551485904000 || last_edit_user_id -> 8778516 || last_edit_version -> 2\n5640337313000000 && 37.7792022,-122.3873434 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n65300043000000 && 37.777537,-122.3911308 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 15\n6329652606000000 && 37.7771748,-122.3904322 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249934000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6932316484000000 && 37.7779819,-122.3878992 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6384096454000000 && 37.780253,-122.3910067 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404093000 || last_edit_user_id -> 8896306 || last_edit_version -> 1\n6328181211000000 && 37.7786239,-122.3910073 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183947000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337830000000 && 37.7787328,-122.3867188 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652605000000 && 37.7766166,-122.3899846 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249934000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337620000000 && 37.7786953,-122.3872009 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n4402491124000000 && 37.7816964,-122.3884383 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5640337223000000 && 37.7799688,-122.386918 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337265000000 && 37.7795693,-122.3871675 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946999000000 && 37.7805803,-122.3879148 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399487000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n7209255187000000 && 37.7806245,-122.3903768 && last_edit_user_name -> clay_c || last_edit_changeset -> 81233350 || last_edit_time -> 1582133935000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n6318866711000000 && 37.77825,-122.391808 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5640337262000000 && 37.7797547,-122.3872883 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n603369654000000 && 37.7797352,-122.3916622 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 8034497 || last_edit_time -> 1304371359000 || last_edit_user_id -> 14293 || last_edit_version -> 2\n6348885064000000 && 37.777137,-122.3905167 && last_edit_user_name -> ericrwolfe || last_edit_changeset -> 68319842 || last_edit_time -> 1553060772000 || last_edit_user_id -> 2014349 || last_edit_version -> 1\n945302582000000 && 37.7789723,-122.390946 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404095000 || last_edit_user_id -> 8896306 || kerb -> lowered || highway -> crossing || last_edit_version -> 8 || crossing -> traffic_signals\n6328181217000000 && 37.7787702,-122.3910764 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183947000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6932348085000000 && 37.7780326,-122.3879303 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5437054788000000 && 37.7775097,-122.3903681 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56684752 || last_edit_time -> 1519639341000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n590986805000000 && 37.778192,-122.3870464 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || amenity -> ferry_terminal || ferry -> yes || name -> Oracle Park || public_transport -> stop_position || last_edit_version -> 6 || network -> SFBF;GGF\n6932316477000000 && 37.7782335,-122.3876468 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || barrier -> gate || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n255649933000000 && 37.7800437,-122.3875357 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 15\n5326946967000000 && 37.7793766,-122.3885851 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || access -> private || barrier -> gate || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n5640337821000000 && 37.7785346,-122.3871841 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652896000000 && 37.7791228,-122.3908664 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404095000 || last_edit_user_id -> 8896306 || highway -> crossing || last_edit_version -> 2 || crossing -> unmarked\n5640337569000000 && 37.7788558,-122.3871152 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652602000000 && 37.7774283,-122.3907459 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249934000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337317000000 && 37.7792022,-122.3871342 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337275000000 && 37.779596,-122.3866555 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337264000000 && 37.7797727,-122.3871879 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337222000000 && 37.7801951,-122.3869401 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n4689017526000000 && 37.7797513,-122.3887102 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n5640337117000000 && 37.7799577,-122.3872801 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946977000000 && 37.7798499,-122.3893746 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n1588951814000000 && 37.7782866,-122.3876024 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n65351790000000 && 37.7758391,-122.3932743 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 14\n6329652898000000 && 37.778314,-122.3918889 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249936000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n1865053210000000 && 37.7797852,-122.3883274 && last_edit_user_name -> matthieun || last_edit_changeset -> 13692280 || last_edit_time -> 1351634921000 || last_edit_user_id -> 595221 || last_edit_version -> 3\n6318851268000000 && 37.7776018,-122.3912145 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5640337831000000 && 37.7785505,-122.3867031 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652625000000 && 37.7778383,-122.3917631 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n255649932000000 && 37.7801996,-122.3847962 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188135000 || last_edit_user_id -> 7134350 || last_edit_version -> 3\n7283301239000000 && 37.7782336,-122.3872386 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5326946987000000 && 37.7802803,-122.3878528 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n6329652897000000 && 37.7799283,-122.3898756 && last_edit_user_name -> clay_c || last_edit_changeset -> 81231251 || last_edit_time -> 1582130726000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n2845162691000000 && 37.7774092,-122.3908754 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || last_edit_version -> 8\n5640337568000000 && 37.7790362,-122.3871333 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6360896508000000 && 37.7771883,-122.3908489 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || last_edit_time -> 1553538169000 || last_edit_user_id -> 381909 || last_edit_version -> 1\n6329652624000000 && 37.7779861,-122.3919337 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 1\n5640337316000000 && 37.7794067,-122.3871536 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337274000000 && 37.7798022,-122.3866725 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6384096460000000 && 37.7798427,-122.3890699 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404094000 || last_edit_user_id -> 8896306 || highway -> crossing || last_edit_version -> 1 || crossing -> unmarked\n4688893429000000 && 37.778767,-122.3877951 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n1865053225000000 && 37.7795907,-122.3881108 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255282000 || last_edit_user_id -> 4518035 || last_edit_version -> 3\n6932348105000000 && 37.7781826,-122.3875386 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n7108550931000000 && 37.7771704,-122.3908716 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n5640337263000000 && 37.7795748,-122.3872726 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n4688893419000000 && 37.7790517,-122.3878327 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n5640337221000000 && 37.7799619,-122.3870407 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5437054789000000 && 37.7776104,-122.3905693 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56684752 || last_edit_time -> 1519639341000 || last_edit_user_id -> 5288591 || last_edit_version -> 1\n5640337116000000 && 37.7801602,-122.3873008 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n2845162688000000 && 37.7769393,-122.3903189 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || last_edit_version -> 3\n5326946976000000 && 37.7797876,-122.3897018 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n6328181210000000 && 37.7786817,-122.3909338 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183947000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n7108550932000000 && 37.7772307,-122.3907941 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n7283301241000000 && 37.7781535,-122.3876146 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5326946968000000 && 37.7795579,-122.3878866 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n260193156000000 && 37.777976,-122.3916917 && last_edit_user_name -> Nodestrawaymus || last_edit_changeset -> 67815924 || last_edit_time -> 1551809589000 || last_edit_user_id -> 8899783 || last_edit_version -> 16\n6318851267000000 && 37.7774539,-122.3912347 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5326946981000000 && 37.7799515,-122.389499 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n666571055000000 && 37.7769044,-122.3903898 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249943000 || last_edit_user_id -> 119881 || last_edit_version -> 9\n2101121546000000 && 37.7762976,-122.3937217 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249943000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n6329652894000000 && 37.7796301,-122.3895066 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337567000000 && 37.7788612,-122.3872129 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n2845162686000000 && 37.7765872,-122.3900419 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || last_edit_version -> 3\n255649957000000 && 37.7787335,-122.3851516 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188136000 || last_edit_user_id -> 7134350 || last_edit_version -> 3\n5640337315000000 && 37.7792078,-122.387239 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6932316482000000 && 37.7782747,-122.3876336 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6329652629000000 && 37.7759382,-122.3932794 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249938000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337522000000 && 37.7794211,-122.3868471 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946975000000 && 37.7797673,-122.3896763 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n6329652901000000 && 37.778157,-122.3921308 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249936000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337115000000 && 37.7799513,-122.3873974 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n255649962000000 && 37.7784479,-122.3853497 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792195000 || last_edit_user_id -> 53073 || last_edit_version -> 4\n5640337270000000 && 37.7797898,-122.3868774 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6318866712000000 && 37.7780674,-122.3915774 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n4688893415000000 && 37.7795971,-122.3885617 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || barrier -> lift_gate || motor_vehicle -> yes || last_edit_time -> 1554404095000 || last_edit_user_id -> 8896306 || last_edit_version -> 2\n317102017000000 && 37.7814581,-122.3876685 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 6\n7283301240000000 && 37.7780487,-122.3878883 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n4000795700000000 && 37.7793658,-122.3899768 && last_edit_user_name -> samely || last_edit_changeset -> 39253952 || last_edit_time -> 1463004147000 || last_edit_user_id -> 2512300 || last_edit_version -> 2\n5640337225000000 && 37.7799741,-122.3868013 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652623000000 && 37.7780138,-122.3919656 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 1\n7108550922000000 && 37.777197,-122.3908376 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || barrier -> kerb || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || kerb -> lowered || last_edit_version -> 1\n7209255189000000 && 37.7806228,-122.3907436 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625272000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6384096459000000 && 37.7793975,-122.3886019 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404094000 || last_edit_user_id -> 8896306 || highway -> crossing || last_edit_version -> 1 || crossing -> unmarked\n5326947001000000 && 37.7806167,-122.3877139 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399487000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n5640337619000000 && 37.7785276,-122.3872786 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6360896161000000 && 37.7770583,-122.3910135 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || barrier -> gate || last_edit_time -> 1553538168000 || last_edit_user_id -> 381909 || last_edit_version -> 1\n6329652895000000 && 37.7797938,-122.3893059 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337566000000 && 37.7790219,-122.3872287 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652622000000 && 37.7790169,-122.3910003 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n7108550923000000 && 37.7771136,-122.3909435 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n5640337314000000 && 37.7793914,-122.3872567 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n7228926464000000 && 37.7796713,-122.3915808 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || highway -> crossing || last_edit_version -> 1\n31862449000000 && 37.7786009,-122.387398 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259633000 || last_edit_user_id -> 4518035 || last_edit_version -> 5\n6932316483000000 && 37.7781844,-122.3871725 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n310688988000000 && 37.7945115,-122.2801913 && last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 76356726 || last_edit_time -> 1572368313000 || last_edit_user_id -> 119881 || amenity -> ferry_terminal || ferry -> yes || name -> Oakland Jack London Square Terminal || public_transport -> stop_position || last_edit_version -> 13 || operator -> Water Emergency Transportation Authority || network -> SFBF\n5326946969000000 && 37.7795582,-122.3888042 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 1 || crossing -> uncontrolled\n6329652617000000 && 37.777423,-122.391152 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6311883025000000 && 37.7797641,-122.3894835 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249945000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n5640337114000000 && 37.7801538,-122.3874168 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946974000000 && 37.779744,-122.389647 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n257316060000000 && 37.7799063,-122.3892986 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || last_edit_version -> 12\n317102183000000 && 37.7783532,-122.3873783 && last_edit_user_name -> gormur || last_edit_changeset -> 10383705 || last_edit_time -> 1326495939000 || last_edit_user_id -> 103253 || last_edit_version -> 5\n1865053223000000 && 37.7802905,-122.3876807 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399512000 || last_edit_user_id -> 933797 || last_edit_version -> 2\n6318866713000000 && 37.7779356,-122.3916402 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n6318889471000000 && 37.7801869,-122.3899976 && last_edit_user_name -> Nodestrawaymus || last_edit_changeset -> 67816506 || last_edit_time -> 1551810618000 || last_edit_user_id -> 8899783 || last_edit_version -> 1\n1723633782000000 && 37.7781624,-122.3916974 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249943000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2\n5640337224000000 && 37.7801988,-122.3868234 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337618000000 && 37.7786883,-122.3872971 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n4000795701000000 && 37.780222,-122.3892958 && last_edit_user_name -> samely || last_edit_changeset -> 39253952 || last_edit_time -> 1463004147000 || last_edit_user_id -> 2512300 || last_edit_version -> 2\n945304393000000 && 37.7789317,-122.3907858 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183952000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 4\n1996623058000000 && 37.7790215,-122.390622 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183954000 || last_edit_user_id -> 119881 || last_edit_version -> 3\n260193331000000 && 37.7781551,-122.3919168 && last_edit_user_name -> Nodestrawaymus || last_edit_changeset -> 67815924 || last_edit_time -> 1551809589000 || last_edit_user_id -> 8899783 || last_edit_version -> 23\n1865053212000000 && 37.7802896,-122.3881134 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399512000 || last_edit_user_id -> 933797 || last_edit_version -> 2\n5640337523000000 && 37.7792158,-122.3868271 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n255649963000000 && 37.7783469,-122.3874609 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259633000 || last_edit_user_id -> 4518035 || last_edit_version -> 31\n4689017527000000 && 37.7801901,-122.3885291 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n6318851266000000 && 37.7774668,-122.3910971 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5640337271000000 && 37.7795882,-122.386861 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n307082269000000 && 37.7794806,-122.388623 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399513000 || last_edit_user_id -> 933797 || last_edit_version -> 8\n6329652618000000 && 37.7774879,-122.3913196 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n31836210000000 && 37.7789296,-122.3874264 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 5\n1865053214000000 && 37.7799249,-122.3881338 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255282000 || last_edit_user_id -> 4518035 || last_edit_version -> 3\n5640337268000000 && 37.7797843,-122.3869871 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5640337226000000 && 37.7802079,-122.3867054 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n1270672016000000 && 37.780545,-122.3906366 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 4 || crossing -> traffic_signals\n255649939000000 && 37.7796524,-122.3874999 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 14\n5326946960000000 && 37.7788644,-122.3907018 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183957000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n7228926458000000 && 37.7803034,-122.3911495 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337320000000 && 37.7794158,-122.3869497 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n7108550929000000 && 37.777507,-122.3913424 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n6318851272000000 && 37.7772109,-122.3915389 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 2\n5640337068000000 && 37.780608,-122.387458 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946973000000 && 37.7796896,-122.3895786 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n6391300469000000 && 37.7813624,-122.3925411 && last_edit_user_name -> clay_c || last_edit_changeset -> 81335413 || last_edit_time -> 1582326678000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> uncontrolled\n5640337827000000 && 37.7785404,-122.3868928 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652902000000 && 37.7800738,-122.3896894 && last_edit_user_name -> clay_c || last_edit_changeset -> 81233350 || last_edit_time -> 1582133935000 || last_edit_user_id -> 119881 || last_edit_version -> 3\n590986802000000 && 37.778258,-122.3875849 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || barrier -> gate || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 4\n5640337575000000 && 37.7788729,-122.3868254 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652608000000 && 37.7777973,-122.3912239 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249934000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6252543480000000 && 37.7780999,-122.3918474 && last_edit_user_name -> clay_c || last_edit_changeset -> 66845961 || last_edit_time -> 1549059988000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 1\n6329652893000000 && 37.7780424,-122.3915459 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6316647619000000 && 37.776872,-122.3904556 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249945000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n5326946970000000 && 37.779565,-122.3877606 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n5640337824000000 && 37.7787166,-122.3870093 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652599000000 && 37.7776941,-122.3910899 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 2\n945302892000000 && 37.7788578,-122.3905961 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183952000 || last_edit_user_id -> 119881 || highway -> crossing || last_edit_version -> 6 || crossing -> traffic_signals\n6318851265000000 && 37.7775245,-122.3910247 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5640337572000000 && 37.7790488,-122.3869395 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652628000000 && 37.7758116,-122.3931965 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249938000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5326946983000000 && 37.7800076,-122.3897768 && last_edit_user_name -> clay_c || last_edit_changeset -> 81231251 || last_edit_time -> 1582130726000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 4 || crossing -> traffic_signals\n3997831656000000 && 37.77776,-122.3914156 && last_edit_user_name -> DannyAiquipa || last_edit_changeset -> 37107162 || last_edit_time -> 1455041094000 || last_edit_user_id -> 2226712 || last_edit_version -> 1\n6384096451000000 && 37.7791611,-122.3906712 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404093000 || last_edit_user_id -> 8896306 || last_edit_version -> 1\n7209260210000000 && 37.781654,-122.3880461 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625271000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n1996623057000000 && 37.7796386,-122.38984 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183954000 || last_edit_user_id -> 119881 || last_edit_version -> 3\n5640337120000000 && 37.7801872,-122.3870601 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n5326946980000000 && 37.7799037,-122.3894405 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n6932316480000000 && 37.7782457,-122.3876158 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || barrier -> gate || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n255649938000000 && 37.7798082,-122.3848063 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188135000 || last_edit_user_id -> 7134350 || last_edit_version -> 2\n4689017523000000 && 37.7797919,-122.3891349 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || last_edit_version -> 1\n7228926459000000 && 37.780244,-122.3912246 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n3997832257000000 && 37.7786279,-122.3913742 && last_edit_user_name -> cdruck || last_edit_changeset -> 67775371 || last_edit_time -> 1551720937000 || last_edit_user_id -> 8762431 || last_edit_version -> 2\n6252543478000000 && 37.7798182,-122.3895497 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249945000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 2\n5326946972000000 && 37.7796764,-122.3877816 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n5640337826000000 && 37.7787235,-122.3869103 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n307082268000000 && 37.7794478,-122.3886477 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399513000 || last_edit_user_id -> 933797 || last_edit_version -> 8\n5640337574000000 && 37.7790554,-122.3868453 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652609000000 && 37.7768581,-122.3904839 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249934000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6252543481000000 && 37.7780738,-122.3918146 && last_edit_user_name -> clay_c || last_edit_changeset -> 66845961 || last_edit_time -> 1549059988000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 1\n7283301242000000 && 37.7781854,-122.3875223 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n5640337521000000 && 37.7792127,-122.3869267 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188124000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n65315471000000 && 37.7814316,-122.3926239 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 8007893 || last_edit_time -> 1304123076000 || last_edit_user_id -> 14293 || last_edit_version -> 4\n1865053215000000 && 37.7802649,-122.3881086 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399512000 || last_edit_user_id -> 933797 || last_edit_version -> 2\n5640337269000000 && 37.7795803,-122.386969 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6360896163000000 && 37.7757405,-122.3926776 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || barrier -> gate || last_edit_time -> 1553538168000 || last_edit_user_id -> 381909 || last_edit_version -> 1\n5640337227000000 && 37.7799821,-122.3866826 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188123000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n1742314502000000 && 37.7783314,-122.3875034 && last_edit_user_name -> WBSKI || last_edit_changeset -> 11514063 || last_edit_time -> 1336274379000 || last_edit_user_id -> 14351 || last_edit_version -> 1\n5326946961000000 && 37.7788863,-122.3909311 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183957000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n5326946982000000 && 37.7800065,-122.3895663 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5640337069000000 && 37.7803792,-122.387437 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188122000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n7209260211000000 && 37.7817091,-122.3885556 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625271000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n1865053220000000 && 37.7804141,-122.3886956 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || last_edit_time -> 1487255282000 || last_edit_user_id -> 4518035 || last_edit_version -> 4\n65317058000000 && 37.7806182,-122.3905436 && last_edit_user_name -> DannyAiquipa || last_edit_changeset -> 37125422 || last_edit_time -> 1455113685000 || last_edit_user_id -> 2226712 || turn_restrictions -> no || highway -> traffic_signals || last_edit_version -> 13\n6932316481000000 && 37.7781707,-122.3875698 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n1723634000000000 && 37.7788875,-122.3907309 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183953000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 3\n5326946971000000 && 37.7795705,-122.3885931 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 1 || crossing -> uncontrolled\n5640337825000000 && 37.778533,-122.3869916 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188126000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n6329652619000000 && 37.7784179,-122.3910016 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6329652598000000 && 37.777585,-122.3909489 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249934000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n6311883027000000 && 37.7818004,-122.3884317 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625279000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n4689493525000000 && 37.7802692,-122.3880404 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399514000 || last_edit_user_id -> 933797 || last_edit_version -> 2\n65334469000000 && 37.7801945,-122.3910808 && last_edit_user_name -> KindredCoda || last_edit_changeset -> 8034497 || last_edit_time -> 1304371357000 || last_edit_user_id -> 14293 || last_edit_version -> 4\n5640337573000000 && 37.7788684,-122.3869236 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || last_edit_time -> 1527188125000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n# Edges\n774852533000000 && 37.7803034,-122.3911495:37.7806228,-122.3907436 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-774852533000000 && 37.7806228,-122.3907436:37.7803034,-122.3911495 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n674722764000000 && 37.7804023,-122.3902623:37.7803259,-122.3901684 && lcn_ref -> 11 || last_edit_changeset -> 81291843 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || cycleway:left -> lane || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || alt_name -> Second Street || cycleway:right -> shared_lane || last_edit_time -> 1582234974000 || last_edit_user_id -> 24555 || lanes -> 2 || name -> 2nd Street || tiger:name_base -> 2nd || placement:forward -> left_of:1 || highway -> secondary || last_edit_version -> 3 || parking:lane:both -> parallel\n-674722764000000 && 37.7803259,-122.3901684:37.7804023,-122.3902623 && lcn_ref -> 11 || last_edit_changeset -> 81291843 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || cycleway:left -> lane || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || alt_name -> Second Street || cycleway:right -> shared_lane || last_edit_time -> 1582234974000 || last_edit_user_id -> 24555 || lanes -> 2 || name -> 2nd Street || tiger:name_base -> 2nd || placement:forward -> left_of:1 || highway -> secondary || last_edit_version -> 3 || parking:lane:both -> parallel\n675874835000001 && 37.7780112,-122.3915447:37.7779356,-122.3916402 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874835000001 && 37.7779356,-122.3916402:37.7780112,-122.3915447 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874835000002 && 37.7779356,-122.3916402:37.7778383,-122.3917631 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874835000002 && 37.7778383,-122.3917631:37.7779356,-122.3916402 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n397270782000000 && 37.7795788,-122.3901388:37.779188,-122.3906368:37.7791611,-122.3906712 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 68895482 || surface -> paved || maxspeed -> 30 mph || parking:lane:right -> parallel || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> sdelgerbayar || tiger:cfcc -> A45 || cycleway:right -> lane || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 7\n590853314000000 && 37.7793914,-122.3872567:37.7792078,-122.387239 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853314000000 && 37.7792078,-122.387239:37.7793914,-122.3872567 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n675874819000000 && 37.7758116,-122.3931965:37.777423,-122.391152 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874819000000 && 37.777423,-122.391152:37.7758116,-122.3931965 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n551597292000000 && 37.7800315,-122.3878142:37.7800411,-122.3876564 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597292000000 && 37.7800411,-122.3876564:37.7800315,-122.3878142 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n590853243000000 && 37.7806277,-122.3872274:37.7803815,-122.3872011 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188128000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853243000000 && 37.7803815,-122.3872011:37.7806277,-122.3872274 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188128000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n780006765000000 && 37.7781854,-122.3875223:37.7782336,-122.3872386 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || bridge -> yes || highway -> footway || last_edit_version -> 1 || layer -> 1\n-780006765000000 && 37.7782336,-122.3872386:37.7781854,-122.3875223 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || bridge -> yes || highway -> footway || last_edit_version -> 1 || layer -> 1\n590853369000000 && 37.7786953,-122.3872009:37.7785346,-122.3871841 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853369000000 && 37.7785346,-122.3871841:37.7786953,-122.3872009 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n675874830000001 && 37.7778383,-122.3917631:37.777507,-122.3913424 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n-675874830000001 && 37.777507,-122.3913424:37.7778383,-122.3917631 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n675874830000002 && 37.777507,-122.3913424:37.7774879,-122.3913196 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n-675874830000002 && 37.7774879,-122.3913196:37.777507,-122.3913424 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n551597302000001 && 37.779565,-122.3877606:37.7796764,-122.3877816 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399489000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597302000001 && 37.7796764,-122.3877816:37.779565,-122.3877606 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399489000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n551597302000002 && 37.7796764,-122.3877816:37.7800315,-122.3878142 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399489000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597302000002 && 37.7800315,-122.3878142:37.7796764,-122.3877816 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399489000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n551597302000003 && 37.7800315,-122.3878142:37.7800808,-122.3878187:37.7802803,-122.3878528 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399489000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597302000003 && 37.7802803,-122.3878528:37.7800808,-122.3878187:37.7800315,-122.3878142 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399489000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n551597302000004 && 37.7802803,-122.3878528:37.7803431,-122.3878576:37.7804947,-122.3878723:37.7805803,-122.3879148 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399489000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597302000004 && 37.7805803,-122.3879148:37.7804947,-122.3878723:37.7803431,-122.3878576:37.7802803,-122.3878528 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399489000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n590853340000000 && 37.7790167,-122.3873258:37.7788563,-122.3873112 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853340000000 && 37.7788563,-122.3873112:37.7790167,-122.3873258 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853269000000 && 37.7801988,-122.3868234:37.7799741,-122.3868013 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853269000000 && 37.7799741,-122.3868013:37.7801988,-122.3868234 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n475096889000001 && 37.7798848,-122.3890227:37.7798742,-122.3888631 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56684752 || surface -> asphalt || last_edit_time -> 1519639354000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> no\n-475096889000001 && 37.7798742,-122.3888631:37.7798848,-122.3890227 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56684752 || surface -> asphalt || last_edit_time -> 1519639354000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> no\n475096889000002 && 37.7798742,-122.3888631:37.7797513,-122.3887102 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56684752 || surface -> asphalt || last_edit_time -> 1519639354000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> no\n-475096889000002 && 37.7797513,-122.3887102:37.7798742,-122.3888631 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56684752 || surface -> asphalt || last_edit_time -> 1519639354000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> no\n551597297000000 && 37.7797673,-122.3896763:37.7796386,-122.38984 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || footway -> ramp || highway -> footway || last_edit_version -> 1\n-551597297000000 && 37.7796386,-122.38984:37.7797673,-122.3896763 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || footway -> ramp || highway -> footway || last_edit_version -> 1\n27041554000001 && 37.7797641,-122.3894835:37.7798499,-122.3893746 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67703843 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1551485905000 || last_edit_user_id -> 8778516 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 24\n27041554000002 && 37.7798499,-122.3893746:37.7798735,-122.389342:37.7799063,-122.3892986 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67703843 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1551485905000 || last_edit_user_id -> 8778516 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 24\n27041554000003 && 37.7799063,-122.3892986:37.7801895,-122.3889403:37.7803241,-122.388789:37.7804141,-122.3886956 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67703843 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1551485905000 || last_edit_user_id -> 8778516 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 24\n27041554000004 && 37.7804141,-122.3886956:37.7804862,-122.3886312:37.7806433,-122.3885077:37.7807714,-122.388422:37.7809095,-122.3883424:37.7810545,-122.3882766:37.7812398,-122.3882109 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67703843 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1551485905000 || last_edit_user_id -> 8778516 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 24\n551597303000001 && 37.7790169,-122.3910003:37.7789723,-122.390946 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597303000001 && 37.7789723,-122.390946:37.7790169,-122.3910003 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597303000002 && 37.7789723,-122.390946:37.7789232,-122.3908842 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597303000002 && 37.7789232,-122.3908842:37.7789723,-122.390946 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597303000003 && 37.7789232,-122.3908842:37.778966,-122.3908297:37.7789317,-122.3907858 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597303000003 && 37.7789317,-122.3907858:37.778966,-122.3908297:37.7789232,-122.3908842 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597303000004 && 37.7789317,-122.3907858:37.7789118,-122.3907615 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597303000004 && 37.7789118,-122.3907615:37.7789317,-122.3907858 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597303000005 && 37.7789118,-122.3907615:37.7788875,-122.3907309 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597303000005 && 37.7788875,-122.3907309:37.7789118,-122.3907615 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597303000006 && 37.7788875,-122.3907309:37.7788644,-122.3907018 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597303000006 && 37.7788644,-122.3907018:37.7788875,-122.3907309 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597303000007 && 37.7788644,-122.3907018:37.7789029,-122.3906528:37.7788578,-122.3905961 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597303000007 && 37.7788578,-122.3905961:37.7789029,-122.3906528:37.7788644,-122.3907018 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n774722325000000 && 37.7804776,-122.3905438:37.7799535,-122.389888:37.7799283,-122.3898756 && last_edit_user_name -> clay_c || last_edit_changeset -> 81233350 || last_edit_time -> 1582133934000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-774722325000000 && 37.7799283,-122.3898756:37.7799535,-122.389888:37.7804776,-122.3905438 && last_edit_user_name -> clay_c || last_edit_changeset -> 81233350 || last_edit_time -> 1582133934000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n564269101000001 && 37.7773648,-122.3908154:37.7774283,-122.3907459 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || service -> driveway || highway -> service || last_edit_version -> 3\n-564269101000001 && 37.7774283,-122.3907459:37.7773648,-122.3908154 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || service -> driveway || highway -> service || last_edit_version -> 3\n564269101000002 && 37.7774283,-122.3907459:37.7774917,-122.3906766:37.7776104,-122.3905693 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || service -> driveway || highway -> service || last_edit_version -> 3\n-564269101000002 && 37.7776104,-122.3905693:37.7774917,-122.3906766:37.7774283,-122.3907459 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || service -> driveway || highway -> service || last_edit_version -> 3\n590853291000000 && 37.7797843,-122.3869871:37.7795803,-122.386969 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853291000000 && 37.7795803,-122.386969:37.7797843,-122.3869871 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n740440585000000 && 37.7782747,-122.3876336:37.7782457,-122.3876158 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n-740440585000000 && 37.7782457,-122.3876158:37.7782747,-122.3876336 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n675739900000000 && 37.7787133,-122.3911488:37.7787702,-122.3910764 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183951000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-675739900000000 && 37.7787702,-122.3910764:37.7787133,-122.3911488 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183951000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n23605180000000 && 37.7798082,-122.3848063:37.7796524,-122.3874999 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock D || last_edit_version -> 2\n-23605180000000 && 37.7796524,-122.3874999:37.7798082,-122.3848063 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock D || last_edit_version -> 2\n674718379000001 && 37.7772109,-122.3915389:37.7774441,-122.3912494:37.7774539,-122.3912347 && sidewalk -> separate || note -> contraflow bike track is temporary during bridge construction || last_edit_changeset -> 79205628 || bicycle -> yes || oneway:bicycle -> no || parking:lane:left -> parallel || tiger:name_type -> St || cycleway:left -> opposite_track || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|left;right || last_edit_user_name -> oba510 || tiger:cfcc -> A41 || cycleway:right -> no || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || lanes -> 2 || name -> Berry Street || tiger:name_base -> Berry || highway -> residential || last_edit_version -> 2\n674718379000002 && 37.7774539,-122.3912347:37.777537,-122.3911308 && sidewalk -> separate || note -> contraflow bike track is temporary during bridge construction || last_edit_changeset -> 79205628 || bicycle -> yes || oneway:bicycle -> no || parking:lane:left -> parallel || tiger:name_type -> St || cycleway:left -> opposite_track || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|left;right || last_edit_user_name -> oba510 || tiger:cfcc -> A41 || cycleway:right -> no || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || lanes -> 2 || name -> Berry Street || tiger:name_base -> Berry || highway -> residential || last_edit_version -> 2\n397270776000001 && 37.7793658,-122.3899768:37.7796392,-122.3896413:37.77967,-122.3896028:37.7796896,-122.3895786 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 69065019 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|none|none || last_edit_user_name -> clay_c || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1554857243000 || last_edit_user_id -> 119881 || lanes -> 3 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n397270776000002 && 37.7796896,-122.3895786:37.7797641,-122.3894835 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 69065019 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|none|none || last_edit_user_name -> clay_c || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1554857243000 || last_edit_user_id -> 119881 || lanes -> 3 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n175941560000000 && 37.7802121,-122.3881633:37.7799249,-122.3881338 && sidewalk -> both || last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46148021 || surface -> asphalt || last_edit_time -> 1487278639000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 6 || oneway -> no\n-175941560000000 && 37.7799249,-122.3881338:37.7802121,-122.3881633 && sidewalk -> both || last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46148021 || surface -> asphalt || last_edit_time -> 1487278639000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 6 || oneway -> no\n551597298000000 && 37.7789232,-122.3908842:37.7788863,-122.3909311 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || surface -> paved || last_edit_time -> 1552183966000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n-551597298000000 && 37.7788863,-122.3909311:37.7789232,-122.3908842 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || surface -> paved || last_edit_time -> 1552183966000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n674496103000001 && 37.7774092,-122.3908754:37.7775021,-122.3909952:37.7775245,-122.3910247 && lcn_ref -> 5 || last_edit_changeset -> 68000611 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || lanes -> 3 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 3\n674496103000002 && 37.7775245,-122.3910247:37.777537,-122.3911308 && lcn_ref -> 5 || last_edit_changeset -> 68000611 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || lanes -> 3 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 3\n590853346000000 && 37.77906,-122.3867493:37.7788785,-122.3867298 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853346000000 && 37.7788785,-122.3867298:37.77906,-122.3867493 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853317000000 && 37.7794158,-122.3869497:37.7792127,-122.3869267 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853317000000 && 37.7792127,-122.3869267:37.7794158,-122.3869497 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n475154469000001 && 37.7796975,-122.3876256:37.7796764,-122.3877816 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> concrete || last_edit_time -> 1515399501000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 2\n-475154469000001 && 37.7796764,-122.3877816:37.7796975,-122.3876256 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> concrete || last_edit_time -> 1515399501000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 2\n475154469000002 && 37.7796764,-122.3877816:37.7796496,-122.38798:37.7802692,-122.3880404 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> concrete || last_edit_time -> 1515399501000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 2\n-475154469000002 && 37.7802692,-122.3880404:37.7796496,-122.38798:37.7796764,-122.3877816 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> concrete || last_edit_time -> 1515399501000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 2\n774852534000000 && 37.7793921,-122.3922973:37.780244,-122.3912246 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-774852534000000 && 37.780244,-122.3912246:37.7793921,-122.3922973 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n23605183000000 && 37.7787335,-122.3851516:37.7786009,-122.387398 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock G || last_edit_version -> 3\n-23605183000000 && 37.7786009,-122.387398:37.7787335,-122.3851516 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock G || last_edit_version -> 3\n675874836000001 && 37.778314,-122.3918889:37.77825,-122.391808 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874836000001 && 37.77825,-122.391808:37.778314,-122.3918889 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874836000002 && 37.77825,-122.391808:37.7781878,-122.3917296 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874836000002 && 37.7781878,-122.3917296:37.77825,-122.391808 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874836000003 && 37.7781878,-122.3917296:37.7781624,-122.3916974 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874836000003 && 37.7781624,-122.3916974:37.7781878,-122.3917296 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874836000004 && 37.7781624,-122.3916974:37.7780674,-122.3915774 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874836000004 && 37.7780674,-122.3915774:37.7781624,-122.3916974 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874836000005 && 37.7780674,-122.3915774:37.7780424,-122.3915459 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874836000005 && 37.7780424,-122.3915459:37.7780674,-122.3915774 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n681699528000001 && 37.7806182,-122.3905436:37.780545,-122.3906366 && lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81291847 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 2 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || last_edit_time -> 1582234978000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || turn:lanes:backward -> left| || highway -> tertiary || last_edit_version -> 4 || parking:lane:both -> parallel || cycleway -> lane\n-681699528000001 && 37.780545,-122.3906366:37.7806182,-122.3905436 && lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81291847 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 2 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || last_edit_time -> 1582234978000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || turn:lanes:backward -> left| || highway -> tertiary || last_edit_version -> 4 || parking:lane:both -> parallel || cycleway -> lane\n681699528000002 && 37.780545,-122.3906366:37.780253,-122.3910067 && lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81291847 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 2 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || last_edit_time -> 1582234978000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || turn:lanes:backward -> left| || highway -> tertiary || last_edit_version -> 4 || parking:lane:both -> parallel || cycleway -> lane\n-681699528000002 && 37.780253,-122.3910067:37.780545,-122.3906366 && lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81291847 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 2 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || last_edit_time -> 1582234978000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || turn:lanes:backward -> left| || highway -> tertiary || last_edit_version -> 4 || parking:lane:both -> parallel || cycleway -> lane\n397270779000001 && 37.777976,-122.3916917:37.7780674,-122.3915774 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67815924 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n397270779000002 && 37.7780674,-122.3915774:37.7788148,-122.3906497:37.7788578,-122.3905961 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67815924 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n397270779000003 && 37.7788578,-122.3905961:37.7793658,-122.3899768 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67815924 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A41 || cycleway:right -> lane || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n590853372000000 && 37.7787235,-122.3869103:37.7785404,-122.3868928 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853372000000 && 37.7785404,-122.3868928:37.7787235,-122.3869103 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n675874820000000 && 37.7759382,-122.3932794:37.776668,-122.3923566:37.7774879,-122.3913196 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874820000000 && 37.7774879,-122.3913196:37.776668,-122.3923566:37.7759382,-122.3932794 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n590853288000000 && 37.7797547,-122.3872883:37.7795748,-122.3872726 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853288000000 && 37.7795748,-122.3872726:37.7797547,-122.3872883 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n475079950000000 && 37.7790517,-122.3878327:37.778767,-122.3877951 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || surface -> asphalt || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 1 || oneway -> no\n-475079950000000 && 37.778767,-122.3877951:37.7790517,-122.3878327 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || surface -> asphalt || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 1 || oneway -> no\n590853343000000 && 37.7790435,-122.3870377:37.7788624,-122.3870196 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853343000000 && 37.7788624,-122.3870196:37.7790435,-122.3870377 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n674496106000000 && 37.7771072,-122.3906413:37.7770702,-122.390557:37.7770137,-122.3904886:37.7769044,-122.3903898 && lcn_ref -> 5 || last_edit_changeset -> 68000611 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1552249957000 || last_edit_user_id -> 119881 || lanes -> 2 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 3\n88466051000001 && 37.7794478,-122.3886477:37.779457,-122.388636:37.7794681,-122.3886276:37.7794806,-122.388623 && sidewalk -> right || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 6\n-88466051000001 && 37.7794806,-122.388623:37.7794681,-122.3886276:37.779457,-122.388636:37.7794478,-122.3886477 && sidewalk -> right || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 6\n88466051000002 && 37.7794806,-122.388623:37.7794936,-122.3886224:37.7795063,-122.388626:37.7795178,-122.3886334:37.7795275,-122.3886443 && sidewalk -> right || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 6\n-88466051000002 && 37.7795275,-122.3886443:37.7795178,-122.3886334:37.7795063,-122.388626:37.7794936,-122.3886224:37.7794806,-122.388623 && sidewalk -> right || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 6\n88466051000003 && 37.7795275,-122.3886443:37.7795358,-122.3886603:37.77954,-122.3886785:37.7795398,-122.3886975:37.7795354,-122.3887157:37.779527,-122.3887315:37.7795153,-122.3887435 && sidewalk -> right || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 6\n-88466051000003 && 37.7795153,-122.3887435:37.779527,-122.3887315:37.7795354,-122.3887157:37.7795398,-122.3886975:37.77954,-122.3886785:37.7795358,-122.3886603:37.7795275,-122.3886443 && sidewalk -> right || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 6\n88466051000004 && 37.7795153,-122.3887435:37.7795021,-122.3887506:37.7794878,-122.3887528:37.7794736,-122.3887499:37.7794606,-122.3887422:37.7794497,-122.3887303:37.779442,-122.388715:37.7794378,-122.3886976:37.7794375,-122.3886794:37.7794413,-122.3886618:37.7794478,-122.3886477 && sidewalk -> right || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 6\n-88466051000004 && 37.7794478,-122.3886477:37.7794413,-122.3886618:37.7794375,-122.3886794:37.7794378,-122.3886976:37.779442,-122.388715:37.7794497,-122.3887303:37.7794606,-122.3887422:37.7794736,-122.3887499:37.7794878,-122.3887528:37.7795021,-122.3887506:37.7795153,-122.3887435 && sidewalk -> right || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 6\n780006768000001 && 37.7779365,-122.3880178:37.7779819,-122.3878992 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 1\n-780006768000001 && 37.7779819,-122.3878992:37.7779365,-122.3880178 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 1\n780006768000002 && 37.7779819,-122.3878992:37.777998,-122.3878573:37.7780487,-122.3878883 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 1\n-780006768000002 && 37.7780487,-122.3878883:37.777998,-122.3878573:37.7779819,-122.3878992 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 1\n780006768000003 && 37.7780487,-122.3878883:37.7780719,-122.3879025:37.7779989,-122.3880929:37.777925,-122.3880476:37.7779365,-122.3880178 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 1\n-780006768000003 && 37.7779365,-122.3880178:37.777925,-122.3880476:37.7779989,-122.3880929:37.7780719,-122.3879025:37.7780487,-122.3878883 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 1\n675874831000001 && 37.777423,-122.391152:37.7771883,-122.3908489 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || last_edit_time -> 1553538172000 || last_edit_user_id -> 381909 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n-675874831000001 && 37.7771883,-122.3908489:37.777423,-122.391152 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || last_edit_time -> 1553538172000 || last_edit_user_id -> 381909 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n675874831000002 && 37.7771883,-122.3908489:37.7770457,-122.3906647:37.7769536,-122.3905614:37.7768932,-122.3905126:37.7768581,-122.3904839 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || last_edit_time -> 1553538172000 || last_edit_user_id -> 381909 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n-675874831000002 && 37.7768581,-122.3904839:37.7768932,-122.3905126:37.7769536,-122.3905614:37.7770457,-122.3906647:37.7771883,-122.3908489 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || last_edit_time -> 1553538172000 || last_edit_user_id -> 381909 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n98763131000000 && 37.7911224,-122.2944966:37.7915957,-122.2946959:37.791832,-122.2948742:37.7920223,-122.2951257:37.7921528,-122.2954322:37.7922141,-122.2957716:37.792212,-122.3048163:37.7922817,-122.3057265:37.8001786,-122.3375223:37.8004975,-122.3394572:37.8005148,-122.3417455:37.8001435,-122.3440293:37.7993301,-122.346304:37.7966174,-122.3520855:37.7951116,-122.354189:37.7941415,-122.3551601:37.783461,-122.3641008:37.7826494,-122.3648343:37.7818449,-122.3657194:37.7810876,-122.3669448:37.7803718,-122.3683202:37.7795552,-122.3701168:37.7788862,-122.3715829:37.7785332,-122.3723815:37.7780235,-122.3740436:37.7779188,-122.3750495:37.7778022,-122.3762155:37.7778107,-122.3768378:37.7777326,-122.3856373:37.7777538,-122.3861201:37.7778089,-122.3865117:37.7779446,-122.3868926:37.778192,-122.3870464 && payment:clipper -> yes || last_edit_changeset -> 74477067 || website -> https://sanfranciscobayferry.com/ || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || source -> bing || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || duration -> 00:30 || last_edit_user_name -> JessAk71 || route -> ferry || last_edit_time -> 1568475388000 || last_edit_user_id -> 381909 || name -> Alameda Main Street - Oracle Park (Seasonal) || last_edit_version -> 13 || foot -> yes\n-98763131000000 && 37.778192,-122.3870464:37.7779446,-122.3868926:37.7778089,-122.3865117:37.7777538,-122.3861201:37.7777326,-122.3856373:37.7778107,-122.3768378:37.7778022,-122.3762155:37.7779188,-122.3750495:37.7780235,-122.3740436:37.7785332,-122.3723815:37.7788862,-122.3715829:37.7795552,-122.3701168:37.7803718,-122.3683202:37.7810876,-122.3669448:37.7818449,-122.3657194:37.7826494,-122.3648343:37.783461,-122.3641008:37.7941415,-122.3551601:37.7951116,-122.354189:37.7966174,-122.3520855:37.7993301,-122.346304:37.8001435,-122.3440293:37.8005148,-122.3417455:37.8004975,-122.3394572:37.8001786,-122.3375223:37.7922817,-122.3057265:37.792212,-122.3048163:37.7922141,-122.2957716:37.7921528,-122.2954322:37.7920223,-122.2951257:37.791832,-122.2948742:37.7915957,-122.2946959:37.7911224,-122.2944966 && payment:clipper -> yes || last_edit_changeset -> 74477067 || website -> https://sanfranciscobayferry.com/ || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || source -> bing || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || duration -> 00:30 || last_edit_user_name -> JessAk71 || route -> ferry || last_edit_time -> 1568475388000 || last_edit_user_id -> 381909 || name -> Alameda Main Street - Oracle Park (Seasonal) || last_edit_version -> 13 || foot -> yes\n47402872000001 && 37.7797352,-122.3916622:37.7796713,-122.3915808 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149448000 || last_edit_user_id -> 119881 || service -> alley || highway -> service || last_edit_version -> 4\n-47402872000001 && 37.7796713,-122.3915808:37.7797352,-122.3916622 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149448000 || last_edit_user_id -> 119881 || service -> alley || highway -> service || last_edit_version -> 4\n47402872000002 && 37.7796713,-122.3915808:37.7791228,-122.3908664 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149448000 || last_edit_user_id -> 119881 || service -> alley || highway -> service || last_edit_version -> 4\n-47402872000002 && 37.7791228,-122.3908664:37.7796713,-122.3915808 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149448000 || last_edit_user_id -> 119881 || service -> alley || highway -> service || last_edit_version -> 4\n47402872000003 && 37.7791228,-122.3908664:37.7790733,-122.3908021 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149448000 || last_edit_user_id -> 119881 || service -> alley || highway -> service || last_edit_version -> 4\n-47402872000003 && 37.7790733,-122.3908021:37.7791228,-122.3908664 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149448000 || last_edit_user_id -> 119881 || service -> alley || highway -> service || last_edit_version -> 4\n780006774000000 && 37.7782335,-122.3876468:37.7781587,-122.387601 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-780006774000000 && 37.7781587,-122.387601:37.7782335,-122.3876468 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n590853268000000 && 37.7801951,-122.3869401:37.7799688,-122.386918 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853268000000 && 37.7799688,-122.386918:37.7801951,-122.3869401 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853289000000 && 37.7797727,-122.3871879:37.7795693,-122.3871675 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853289000000 && 37.7795693,-122.3871675:37.7797727,-122.3871879 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n681699525000000 && 37.7791611,-122.3906712:37.7791164,-122.3907368:37.7790733,-122.3908021 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 68895482 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> sdelgerbayar || tiger:cfcc -> A45 || cycleway:right -> lane || last_edit_time -> 1554404094000 || last_edit_user_id -> 8896306 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 1\n675874818000001 && 37.777423,-122.391152:37.7774668,-122.3910971 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249938000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874818000001 && 37.7774668,-122.3910971:37.777423,-122.391152 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249938000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874818000002 && 37.7774668,-122.3910971:37.7775245,-122.3910247 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249938000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874818000002 && 37.7775245,-122.3910247:37.7774668,-122.3910971 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249938000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874818000003 && 37.7775245,-122.3910247:37.777585,-122.3909489 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249938000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874818000003 && 37.777585,-122.3909489:37.7775245,-122.3910247 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249938000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n780006769000001 && 37.7781854,-122.3875223:37.7782632,-122.3875716:37.778258,-122.3875849 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n-780006769000001 && 37.778258,-122.3875849:37.7782632,-122.3875716:37.7781854,-122.3875223 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n780006769000002 && 37.778258,-122.3875849:37.7782457,-122.3876158 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n-780006769000002 && 37.7782457,-122.3876158:37.778258,-122.3875849 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n780006769000003 && 37.7782457,-122.3876158:37.7782335,-122.3876468 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n-780006769000003 && 37.7782335,-122.3876468:37.7782457,-122.3876158 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n780006769000004 && 37.7782335,-122.3876468:37.7782276,-122.3876616:37.7781535,-122.3876146 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n-780006769000004 && 37.7781535,-122.3876146:37.7782276,-122.3876616:37.7782335,-122.3876468 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n780006769000005 && 37.7781535,-122.3876146:37.7781429,-122.3876079:37.7781785,-122.3875179:37.7781854,-122.3875223 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n-780006769000005 && 37.7781854,-122.3875223:37.7781785,-122.3875179:37.7781429,-122.3876079:37.7781535,-122.3876146 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n590853373000000 && 37.7787291,-122.386814:37.7785464,-122.3867977 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853373000000 && 37.7785464,-122.3867977:37.7787291,-122.386814 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n8919738000001 && 37.7801945,-122.3910808:37.7802753,-122.3911851 && last_edit_user_name -> Fikadu Balcha || tiger:cfcc -> A41 || last_edit_changeset -> 69012379 || last_edit_time -> 1554738575000 || last_edit_user_id -> 9051248 || name -> Stanford Street || tiger:name_base -> Stanford || tiger:name_type -> St || highway -> service || last_edit_version -> 11 || oneway -> yes || tiger:county -> San Francisco, CA\n8919738000002 && 37.7802753,-122.3911851:37.7806523,-122.3916518:37.7807543,-122.391779:37.7813423,-122.3925173:37.7813624,-122.3925411 && last_edit_user_name -> Fikadu Balcha || tiger:cfcc -> A41 || last_edit_changeset -> 69012379 || last_edit_time -> 1554738575000 || last_edit_user_id -> 9051248 || name -> Stanford Street || tiger:name_base -> Stanford || tiger:name_type -> St || highway -> service || last_edit_version -> 11 || oneway -> yes || tiger:county -> San Francisco, CA\n8919738000003 && 37.7813624,-122.3925411:37.7814316,-122.3926239 && last_edit_user_name -> Fikadu Balcha || tiger:cfcc -> A41 || last_edit_changeset -> 69012379 || last_edit_time -> 1554738575000 || last_edit_user_id -> 9051248 || name -> Stanford Street || tiger:name_base -> Stanford || tiger:name_type -> St || highway -> service || last_edit_version -> 11 || oneway -> yes || tiger:county -> San Francisco, CA\n774711623000000 && 37.7817091,-122.3885556:37.7814018,-122.3885831:37.7812122,-122.3886247:37.7810182,-122.3886918:37.7808374,-122.3888003:37.7806995,-122.3889084:37.7805624,-122.3890649:37.7800738,-122.3896894 && last_edit_user_name -> clay_c || last_edit_changeset -> 81231251 || last_edit_time -> 1582130725000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-774711623000000 && 37.7800738,-122.3896894:37.7805624,-122.3890649:37.7806995,-122.3889084:37.7808374,-122.3888003:37.7810182,-122.3886918:37.7812122,-122.3886247:37.7814018,-122.3885831:37.7817091,-122.3885556 && last_edit_user_name -> clay_c || last_edit_changeset -> 81231251 || last_edit_time -> 1582130725000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n551597296000000 && 37.7789118,-122.3907615:37.7790215,-122.390622 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || footway -> ramp || highway -> footway || last_edit_version -> 1\n-551597296000000 && 37.7790215,-122.390622:37.7789118,-122.3907615 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || footway -> ramp || highway -> footway || last_edit_version -> 1\n675874834000001 && 37.7778383,-122.3917631:37.777882,-122.3918136 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874834000001 && 37.777882,-122.3918136:37.7778383,-122.3917631 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874834000002 && 37.777882,-122.3918136:37.7779861,-122.3919337 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874834000002 && 37.7779861,-122.3919337:37.777882,-122.3918136 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874834000003 && 37.7779861,-122.3919337:37.7780138,-122.3919656 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874834000003 && 37.7780138,-122.3919656:37.7779861,-122.3919337 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874834000004 && 37.7780138,-122.3919656:37.7780687,-122.392029 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874834000004 && 37.7780687,-122.392029:37.7780138,-122.3919656 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874834000005 && 37.7780687,-122.392029:37.778157,-122.3921308 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874834000005 && 37.778157,-122.3921308:37.7780687,-122.392029 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n23605181000000 && 37.7794407,-122.3847697:37.779285,-122.387467 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock E || last_edit_version -> 3\n-23605181000000 && 37.779285,-122.387467:37.7794407,-122.3847697 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock E || last_edit_version -> 3\n590853294000000 && 37.7798022,-122.3866725:37.779596,-122.3866555 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188131000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853294000000 && 37.779596,-122.3866555:37.7798022,-122.3866725 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188131000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853315000000 && 37.7794067,-122.3871536:37.7792022,-122.3871342 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853315000000 && 37.7792022,-122.3871342:37.7794067,-122.3871536 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n740440588000000 && 37.7780326,-122.3879303:37.7779819,-122.3878992 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n-740440588000000 && 37.7779819,-122.3878992:37.7780326,-122.3879303 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n145480366000001 && 37.7781535,-122.3876146:37.7781587,-122.387601 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 4\n-145480366000001 && 37.7781587,-122.387601:37.7781535,-122.3876146 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 4\n145480366000002 && 37.7781587,-122.387601:37.7781707,-122.3875698 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 4\n-145480366000002 && 37.7781707,-122.3875698:37.7781587,-122.387601 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 4\n145480366000003 && 37.7781707,-122.3875698:37.7781826,-122.3875386 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 4\n-145480366000003 && 37.7781826,-122.3875386:37.7781707,-122.3875698 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 4\n145480366000004 && 37.7781826,-122.3875386:37.7781854,-122.3875223 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 4\n-145480366000004 && 37.7781854,-122.3875223:37.7781826,-122.3875386 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 4\n397270773000001 && 37.780222,-122.3892958:37.7800308,-122.3895366:37.7800065,-122.3895663 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67703843 || maxspeed -> 30 mph || parking:lane:right -> parallel || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|none|none || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A45 || cycleway:right -> lane || last_edit_time -> 1551485906000 || last_edit_user_id -> 8778516 || lanes -> 3 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 5\n397270773000002 && 37.7800065,-122.3895663:37.7799221,-122.3896767 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67703843 || maxspeed -> 30 mph || parking:lane:right -> parallel || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|none|none || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A45 || cycleway:right -> lane || last_edit_time -> 1551485906000 || last_edit_user_id -> 8778516 || lanes -> 3 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 5\n590853316000000 && 37.7794102,-122.3870452:37.779205,-122.3870262 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853316000000 && 37.779205,-122.3870262:37.7794102,-122.3870452 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n675874837000001 && 37.7799283,-122.3898756:37.7800076,-122.3897768 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874837000001 && 37.7800076,-122.3897768:37.7799283,-122.3898756 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874837000002 && 37.7800076,-122.3897768:37.7800738,-122.3896894 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874837000002 && 37.7800738,-122.3896894:37.7800076,-122.3897768 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n280398663000001 && 37.7769393,-122.3903189:37.7770657,-122.3904342:37.777137,-122.3905167 && lcn_ref -> 5 || last_edit_changeset -> 68319842 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> ericrwolfe || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1553060773000 || last_edit_user_id -> 2014349 || lanes -> 2 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 10\n280398663000002 && 37.777137,-122.3905167:37.7771752,-122.3905609:37.7773648,-122.3908154 && lcn_ref -> 5 || last_edit_changeset -> 68319842 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> ericrwolfe || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1553060773000 || last_edit_user_id -> 2014349 || lanes -> 2 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 10\n280398663000003 && 37.7773648,-122.3908154:37.7774092,-122.3908754 && lcn_ref -> 5 || last_edit_changeset -> 68319842 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> ericrwolfe || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1553060773000 || last_edit_user_id -> 2014349 || lanes -> 2 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 10\n760885773000000 && 37.7770583,-122.3910135:37.7771136,-122.3909435 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || surface -> paving_stones || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-760885773000000 && 37.7771136,-122.3909435:37.7770583,-122.3910135 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || surface -> paving_stones || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n564269100000000 && 37.7776104,-122.3905693:37.7775097,-122.3903681 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56684752 || last_edit_time -> 1519639344000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n-564269100000000 && 37.7775097,-122.3903681:37.7776104,-122.3905693 && last_edit_user_name -> Irina Karachevtseva || last_edit_changeset -> 56684752 || last_edit_time -> 1519639344000 || last_edit_user_id -> 5288591 || service -> parking_aisle || highway -> service || last_edit_version -> 1\n590853342000000 && 37.7790362,-122.3871333:37.7788558,-122.3871152 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853342000000 && 37.7788558,-122.3871152:37.7790362,-122.3871333 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n774722324000000 && 37.7800738,-122.3896894:37.7806245,-122.3903768 && last_edit_user_name -> clay_c || last_edit_changeset -> 81233350 || last_edit_time -> 1582133934000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-774722324000000 && 37.7806245,-122.3903768:37.7800738,-122.3896894 && last_edit_user_name -> clay_c || last_edit_changeset -> 81233350 || last_edit_time -> 1582133934000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n8920394000001 && 37.7818004,-122.3884317:37.7816964,-122.3884383 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67703843 || surface -> paved || maxspeed -> 30 mph || parking:lane:right -> parallel || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A45 || cycleway:right -> lane || last_edit_time -> 1551485904000 || last_edit_user_id -> 8778516 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 35\n8920394000002 && 37.7816964,-122.3884383:37.781351,-122.3884648:37.7812226,-122.3884851:37.781102,-122.3885223:37.7809748,-122.388573:37.7808455,-122.388646:37.7807232,-122.388733:37.7805908,-122.388858:37.7804769,-122.3889992:37.780222,-122.3892958 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67703843 || surface -> paved || maxspeed -> 30 mph || parking:lane:right -> parallel || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A45 || cycleway:right -> lane || last_edit_time -> 1551485904000 || last_edit_user_id -> 8778516 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 35\n37177789000001 && 37.7758391,-122.3932743:37.7758723,-122.3932348 && sidewalk -> separate || note -> contraflow bike track is temporary during bridge construction || last_edit_changeset -> 79205628 || bicycle -> yes || oneway:bicycle -> no || tiger:name_type -> St || cycleway:left -> opposite_track || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> oba510 || tiger:cfcc -> A41 || cycleway:right -> no || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || name -> Berry Street || tiger:name_base -> Berry || highway -> residential || last_edit_version -> 13 || parking:lane:both -> parallel\n37177789000002 && 37.7758723,-122.3932348:37.7766173,-122.3922909:37.7772109,-122.3915389 && sidewalk -> separate || note -> contraflow bike track is temporary during bridge construction || last_edit_changeset -> 79205628 || bicycle -> yes || oneway:bicycle -> no || tiger:name_type -> St || cycleway:left -> opposite_track || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> oba510 || tiger:cfcc -> A41 || cycleway:right -> no || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || name -> Berry Street || tiger:name_base -> Berry || highway -> residential || last_edit_version -> 13 || parking:lane:both -> parallel\n675874824000001 && 37.7777973,-122.3912239:37.7776941,-122.3910899 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874824000001 && 37.7776941,-122.3910899:37.7777973,-122.3912239 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n675874824000002 && 37.7776941,-122.3910899:37.777585,-122.3909489 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874824000002 && 37.777585,-122.3909489:37.7776941,-122.3910899 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n675874824000003 && 37.777585,-122.3909489:37.7774283,-122.3907459 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874824000003 && 37.7774283,-122.3907459:37.777585,-122.3909489 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n675874824000004 && 37.7774283,-122.3907459:37.777233,-122.3904929:37.7771748,-122.3904322 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874824000004 && 37.7771748,-122.3904322:37.777233,-122.3904929:37.7774283,-122.3907459 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n675874824000005 && 37.7771748,-122.3904322:37.7769659,-122.3902647 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874824000005 && 37.7769659,-122.3902647:37.7771748,-122.3904322 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n468012245000001 && 37.7790733,-122.3908021:37.7790233,-122.3908717:37.7789723,-122.390946 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 68895482 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> sdelgerbayar || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 4\n468012245000002 && 37.7789723,-122.390946:37.7786279,-122.3913742 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 68895482 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> sdelgerbayar || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 4\n774852538000001 && 37.7804776,-122.3905438:37.7804693,-122.3905651:37.7796713,-122.3915808 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-774852538000001 && 37.7796713,-122.3915808:37.7804693,-122.3905651:37.7804776,-122.3905438 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n774852538000002 && 37.7796713,-122.3915808:37.7788751,-122.3925781:37.7788533,-122.3925908 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-774852538000002 && 37.7788533,-122.3925908:37.7788751,-122.3925781:37.7796713,-122.3915808 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n780006764000001 && 37.7782336,-122.3872386:37.7782442,-122.3871761 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-780006764000001 && 37.7782442,-122.3871761:37.7782336,-122.3872386 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n780006764000002 && 37.7782442,-122.3871761:37.778249,-122.3870488:37.778192,-122.3870464 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-780006764000002 && 37.778192,-122.3870464:37.778249,-122.3870488:37.7782442,-122.3871761 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n551597299000001 && 37.7795731,-122.3876175:37.779565,-122.3877606 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597299000001 && 37.779565,-122.3877606:37.7795731,-122.3876175 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n551597299000002 && 37.779565,-122.3877606:37.7795579,-122.3878866 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597299000002 && 37.7795579,-122.3878866:37.779565,-122.3877606 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n280398660000001 && 37.777537,-122.3911308:37.7774668,-122.3910971 && lcn_ref -> 5 || last_edit_changeset -> 68000611 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1552249952000 || last_edit_user_id -> 119881 || lanes -> 2 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 9\n280398660000002 && 37.7774668,-122.3910971:37.7772671,-122.3908391 && lcn_ref -> 5 || last_edit_changeset -> 68000611 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1552249952000 || last_edit_user_id -> 119881 || lanes -> 2 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 9\n23605182000000 && 37.7790817,-122.3847962:37.7789296,-122.3874264 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock F || last_edit_version -> 4\n-23605182000000 && 37.7789296,-122.3874264:37.7790817,-122.3847962 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock F || last_edit_version -> 4\n740440587000000 && 37.7782442,-122.3871761:37.7781844,-122.3871725 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n-740440587000000 && 37.7781844,-122.3871725:37.7782442,-122.3871761 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n590853293000000 && 37.7797963,-122.386777:37.7795942,-122.3867583 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188131000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853293000000 && 37.7795942,-122.3867583:37.7797963,-122.386777 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188131000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853264000000 && 37.7801538,-122.3874168:37.7799513,-122.3873974 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853264000000 && 37.7799513,-122.3873974:37.7801538,-122.3874168 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n780006773000000 && 37.7782457,-122.3876158:37.7781707,-122.3875698 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-780006773000000 && 37.7781707,-122.3875698:37.7782457,-122.3876158 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n674496105000000 && 37.7771072,-122.3906413:37.777044,-122.3906032:37.776907,-122.3904856:37.776872,-122.3904556 && lcn_ref -> 5 || last_edit_changeset -> 68319842 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> ericrwolfe || tiger:cfcc -> A45 || last_edit_time -> 1553060773000 || last_edit_user_id -> 2014349 || lanes -> 1 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 2\n675240723000001 && 37.7772671,-122.3908391:37.7772307,-122.3907941 && lcn_ref -> 5 || last_edit_changeset -> 79205628 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> oba510 || tiger:cfcc -> A45 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || lanes -> 3 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 2\n675240723000002 && 37.7772307,-122.3907941:37.7771072,-122.3906413 && lcn_ref -> 5 || last_edit_changeset -> 79205628 || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || tiger:zip_right -> 94124 || last_edit_user_name -> oba510 || tiger:cfcc -> A45 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || lanes -> 3 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 2\n760885775000001 && 37.7771704,-122.3908716:37.7771883,-122.3908489 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-760885775000001 && 37.7771883,-122.3908489:37.7771704,-122.3908716 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n760885775000002 && 37.7771883,-122.3908489:37.777197,-122.3908376 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-760885775000002 && 37.777197,-122.3908376:37.7771883,-122.3908489 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n760885775000003 && 37.777197,-122.3908376:37.7772307,-122.3907941 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-760885775000003 && 37.7772307,-122.3907941:37.777197,-122.3908376 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n774852536000001 && 37.780244,-122.3912246:37.7802753,-122.3911851 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-774852536000001 && 37.7802753,-122.3911851:37.780244,-122.3912246 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n774852536000002 && 37.7802753,-122.3911851:37.7803034,-122.3911495 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-774852536000002 && 37.7803034,-122.3911495:37.7802753,-122.3911851 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n590853319000000 && 37.7794295,-122.3867409:37.7792214,-122.3867232 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853319000000 && 37.7792214,-122.3867232:37.7794295,-122.3867409 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n740440584000000 && 37.7782627,-122.3876648:37.7782335,-122.3876468 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n-740440584000000 && 37.7782335,-122.3876468:37.7782627,-122.3876648 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n175941558000001 && 37.7795275,-122.3886443:37.7795705,-122.3885931 && sidewalk -> left || last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 7 || oneway -> no\n-175941558000001 && 37.7795705,-122.3885931:37.7795275,-122.3886443 && sidewalk -> left || last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 7 || oneway -> no\n175941558000002 && 37.7795705,-122.3885931:37.7795971,-122.3885617 && sidewalk -> left || last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 7 || oneway -> no\n-175941558000002 && 37.7795971,-122.3885617:37.7795705,-122.3885931 && sidewalk -> left || last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 7 || oneway -> no\n175941558000003 && 37.7795971,-122.3885617:37.7796106,-122.3885449:37.7797852,-122.3883274 && sidewalk -> left || last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 7 || oneway -> no\n-175941558000003 && 37.7797852,-122.3883274:37.7796106,-122.3885449:37.7795971,-122.3885617 && sidewalk -> left || last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 7 || oneway -> no\n175941558000004 && 37.7797852,-122.3883274:37.7799249,-122.3881338 && sidewalk -> left || last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 7 || oneway -> no\n-175941558000004 && 37.7799249,-122.3881338:37.7797852,-122.3883274 && sidewalk -> left || last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404096000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 7 || oneway -> no\n590853290000000 && 37.7797773,-122.387084:37.7795739,-122.3870659 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853290000000 && 37.7795739,-122.3870659:37.7797773,-122.387084 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n679290104000000 && 37.7771136,-122.3909435:37.7771704,-122.3908716 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || surface -> paving_stones || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> steps || last_edit_version -> 2\n-679290104000000 && 37.7771704,-122.3908716:37.7771136,-122.3909435 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || surface -> paving_stones || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> steps || last_edit_version -> 2\n681699526000001 && 37.7799221,-122.3896767:37.7798461,-122.3897755 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 69065019 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || cycleway:right -> lane || last_edit_time -> 1554857244000 || last_edit_user_id -> 119881 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 2\n681699526000002 && 37.7798461,-122.3897755:37.7797091,-122.3899645:37.7795788,-122.3901388 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 69065019 || surface -> paved || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || cycleway:right -> lane || last_edit_time -> 1554857244000 || last_edit_user_id -> 119881 || lanes -> 2 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 2\n133755930000001 && 37.777537,-122.3911308:37.777579,-122.391184:37.7776018,-122.3912145 && lcn_ref -> 5 || last_edit_changeset -> 79177594 || turn:lanes:forward -> through|through|through|right || lanes:forward -> 4 || lanes:backward -> 2 || tiger:name_type -> St || cycleway:left -> shared_lane || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> Kerrick Staley || tiger:cfcc -> A45 || last_edit_time -> 1578096485000 || last_edit_user_id -> 6902066 || lanes -> 6 || name -> 3rd Street || tiger:name_base -> 3rd || placement:forward -> left_of:1 || turn:lanes:backward -> through|through || highway -> primary || last_edit_version -> 9\n-133755930000001 && 37.7776018,-122.3912145:37.777579,-122.391184:37.777537,-122.3911308 && lcn_ref -> 5 || last_edit_changeset -> 79177594 || turn:lanes:forward -> through|through|through|right || lanes:forward -> 4 || lanes:backward -> 2 || tiger:name_type -> St || cycleway:left -> shared_lane || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> Kerrick Staley || tiger:cfcc -> A45 || last_edit_time -> 1578096485000 || last_edit_user_id -> 6902066 || lanes -> 6 || name -> 3rd Street || tiger:name_base -> 3rd || placement:forward -> left_of:1 || turn:lanes:backward -> through|through || highway -> primary || last_edit_version -> 9\n133755930000002 && 37.7776018,-122.3912145:37.7776175,-122.3912356:37.77776,-122.3914156 && lcn_ref -> 5 || last_edit_changeset -> 79177594 || turn:lanes:forward -> through|through|through|right || lanes:forward -> 4 || lanes:backward -> 2 || tiger:name_type -> St || cycleway:left -> shared_lane || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> Kerrick Staley || tiger:cfcc -> A45 || last_edit_time -> 1578096485000 || last_edit_user_id -> 6902066 || lanes -> 6 || name -> 3rd Street || tiger:name_base -> 3rd || placement:forward -> left_of:1 || turn:lanes:backward -> through|through || highway -> primary || last_edit_version -> 9\n-133755930000002 && 37.77776,-122.3914156:37.7776175,-122.3912356:37.7776018,-122.3912145 && lcn_ref -> 5 || last_edit_changeset -> 79177594 || turn:lanes:forward -> through|through|through|right || lanes:forward -> 4 || lanes:backward -> 2 || tiger:name_type -> St || cycleway:left -> shared_lane || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> Kerrick Staley || tiger:cfcc -> A45 || last_edit_time -> 1578096485000 || last_edit_user_id -> 6902066 || lanes -> 6 || name -> 3rd Street || tiger:name_base -> 3rd || placement:forward -> left_of:1 || turn:lanes:backward -> through|through || highway -> primary || last_edit_version -> 9\n675874817000001 && 37.777507,-122.3913424:37.7776018,-122.3912145 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-675874817000001 && 37.7776018,-122.3912145:37.777507,-122.3913424 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> crossing || highway -> footway || last_edit_version -> 2\n675874817000002 && 37.7776018,-122.3912145:37.7776941,-122.3910899 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-675874817000002 && 37.7776941,-122.3910899:37.7776018,-122.3912145 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> crossing || highway -> footway || last_edit_version -> 2\n675874838000001 && 37.778314,-122.3918889:37.7790169,-122.3910003 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874838000001 && 37.7790169,-122.3910003:37.778314,-122.3918889 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n675874838000002 && 37.7790169,-122.3910003:37.7791228,-122.3908664 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874838000002 && 37.7791228,-122.3908664:37.7790169,-122.3910003 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n675874838000003 && 37.7791228,-122.3908664:37.7799283,-122.3898756 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874838000003 && 37.7799283,-122.3898756:37.7791228,-122.3908664 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n175941563000000 && 37.7794806,-122.388623:37.7794741,-122.3885153:37.7790596,-122.3879802:37.7787999,-122.3879682:37.7786375,-122.3877758:37.7786439,-122.3875354 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 5\n-175941563000000 && 37.7786439,-122.3875354:37.7786375,-122.3877758:37.7787999,-122.3879682:37.7790596,-122.3879802:37.7794741,-122.3885153:37.7794806,-122.388623 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 5\n590853374000000 && 37.7787328,-122.3867188:37.7785505,-122.3867031 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188135000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853374000000 && 37.7785505,-122.3867031:37.7787328,-122.3867188 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188135000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n675874822000000 && 37.7769659,-122.3902647:37.7766166,-122.3899846 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || bridge -> movable || highway -> footway || last_edit_version -> 1 || layer -> 1\n-675874822000000 && 37.7766166,-122.3899846:37.7769659,-122.3902647 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || bridge -> movable || highway -> footway || last_edit_version -> 1 || layer -> 1\n396945237000001 && 37.777341,-122.3925157:37.7778567,-122.3918465:37.777882,-122.3918136 && sidewalk -> right || last_edit_changeset -> 67815924 || surface -> asphalt || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|left|left|through|through;right || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A41 || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lit -> yes || lanes -> 5 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n396945237000002 && 37.777882,-122.3918136:37.777976,-122.3916917 && sidewalk -> right || last_edit_changeset -> 67815924 || surface -> asphalt || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|left|left|through|through;right || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A41 || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lit -> yes || lanes -> 5 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n590853345000000 && 37.7790554,-122.3868453:37.7788729,-122.3868254 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853345000000 && 37.7788729,-122.3868254:37.7790554,-122.3868453 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n28842113000001 && 37.7783532,-122.3873783:37.7786009,-122.387398 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n-28842113000001 && 37.7786009,-122.387398:37.7783532,-122.3873783 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n28842113000002 && 37.7786009,-122.387398:37.7789296,-122.3874264 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n-28842113000002 && 37.7789296,-122.3874264:37.7786009,-122.387398 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n28842113000003 && 37.7789296,-122.3874264:37.779285,-122.387467 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n-28842113000003 && 37.779285,-122.387467:37.7789296,-122.3874264 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n28842113000004 && 37.779285,-122.387467:37.7796524,-122.3874999 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n-28842113000004 && 37.7796524,-122.3874999:37.779285,-122.387467 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n28842113000005 && 37.7796524,-122.3874999:37.7800437,-122.3875357 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n-28842113000005 && 37.7800437,-122.3875357:37.7796524,-122.3874999 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n28842113000006 && 37.7800437,-122.3875357:37.7804831,-122.3875753:37.7809595,-122.3876215:37.7814581,-122.3876685 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n-28842113000006 && 37.7814581,-122.3876685:37.7809595,-122.3876215:37.7804831,-122.3875753:37.7800437,-122.3875357 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || access -> customers || man_made -> pier || last_edit_time -> 1419440396000 || last_edit_user_id -> 501715 || last_edit_version -> 7\n200124196000000 && 37.7778383,-122.3917631:37.7771065,-122.3926934:37.7770281,-122.3927931:37.7762976,-122.3937217 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249951000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n-200124196000000 && 37.7762976,-122.3937217:37.7770281,-122.3927931:37.7771065,-122.3926934:37.7778383,-122.3917631 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249951000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 2\n475079948000001 && 37.7794478,-122.3886477:37.7793975,-122.3886019 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || access -> private || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 3 || oneway -> no\n-475079948000001 && 37.7793975,-122.3886019:37.7794478,-122.3886477 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || access -> private || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 3 || oneway -> no\n475079948000002 && 37.7793975,-122.3886019:37.7793766,-122.3885851 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || access -> private || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 3 || oneway -> no\n-475079948000002 && 37.7793766,-122.3885851:37.7793975,-122.3886019 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || access -> private || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 3 || oneway -> no\n475079948000003 && 37.7793766,-122.3885851:37.7789773,-122.3881116 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || access -> private || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 3 || oneway -> no\n-475079948000003 && 37.7789773,-122.3881116:37.7793766,-122.3885851 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || access -> private || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || service -> parking_aisle || highway -> service || last_edit_version -> 3 || oneway -> no\n162324921000001 && 37.7783314,-122.3875034:37.7786439,-122.3875354 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n-162324921000001 && 37.7786439,-122.3875354:37.7783314,-122.3875034 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n162324921000002 && 37.7786439,-122.3875354:37.7795731,-122.3876175 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n-162324921000002 && 37.7795731,-122.3876175:37.7786439,-122.3875354 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n162324921000003 && 37.7795731,-122.3876175:37.7796975,-122.3876256 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n-162324921000003 && 37.7796975,-122.3876256:37.7795731,-122.3876175 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n162324921000004 && 37.7796975,-122.3876256:37.7800411,-122.3876564 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n-162324921000004 && 37.7800411,-122.3876564:37.7796975,-122.3876256 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n162324921000005 && 37.7800411,-122.3876564:37.7801231,-122.3876637:37.7802905,-122.3876807 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n-162324921000005 && 37.7802905,-122.3876807:37.7801231,-122.3876637:37.7800411,-122.3876564 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n162324921000006 && 37.7802905,-122.3876807:37.7806167,-122.3877139 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n-162324921000006 && 37.7806167,-122.3877139:37.7802905,-122.3876807 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n162324921000007 && 37.7806167,-122.3877139:37.7811742,-122.3877707:37.7813989,-122.3879065:37.7815532,-122.3879914:37.781654,-122.3880461 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n-162324921000007 && 37.781654,-122.3880461:37.7815532,-122.3879914:37.7813989,-122.3879065:37.7811742,-122.3877707:37.7806167,-122.3877139 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || bicycle -> yes || surface -> paved || last_edit_time -> 1581625280000 || last_edit_user_id -> 119881 || name -> Herb Caen Way || highway -> footway || last_edit_version -> 12 || foot -> yes || segregated -> no\n590853341000000 && 37.7790219,-122.3872287:37.7788612,-122.3872129 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853341000000 && 37.7788612,-122.3872129:37.7790219,-122.3872287 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853270000000 && 37.7802079,-122.3867054:37.7799821,-122.3866826 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853270000000 && 37.7799821,-122.3866826:37.7802079,-122.3867054 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853241000000 && 37.780608,-122.387458:37.7803792,-122.387437 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188128000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853241000000 && 37.7803792,-122.387437:37.780608,-122.387458 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188128000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n551597290000001 && 37.7802896,-122.3881134:37.7803132,-122.3881114:37.7804701,-122.3881308:37.7804939,-122.3881248:37.7805157,-122.3881127:37.780531,-122.388096:37.780548,-122.3880691:37.7805591,-122.3880235:37.7805591,-122.3879974:37.7805543,-122.3879598:37.7805803,-122.3879148 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597290000001 && 37.7805803,-122.3879148:37.7805543,-122.3879598:37.7805591,-122.3879974:37.7805591,-122.3880235:37.780548,-122.3880691:37.780531,-122.388096:37.7805157,-122.3881127:37.7804939,-122.3881248:37.7804701,-122.3881308:37.7803132,-122.3881114:37.7802896,-122.3881134 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n551597290000002 && 37.7805803,-122.3879148:37.7806087,-122.3878656:37.7806167,-122.3877139 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597290000002 && 37.7806167,-122.3877139:37.7806087,-122.3878656:37.7805803,-122.3879148 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n215819236000000 && 37.7799249,-122.3881338:37.7795907,-122.3881108 && sidewalk -> left || last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46148021 || surface -> asphalt || last_edit_time -> 1487278639000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> no\n-215819236000000 && 37.7795907,-122.3881108:37.7799249,-122.3881338 && sidewalk -> left || last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46148021 || surface -> asphalt || last_edit_time -> 1487278639000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> no\n27656675000001 && 37.777976,-122.3916917:37.7780738,-122.3918146 && lcn_ref -> 5 || last_edit_changeset -> 66845961 || tiger:name_type -> St || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1549060019000 || last_edit_user_id -> 119881 || lanes -> 4 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 11\n-27656675000001 && 37.7780738,-122.3918146:37.777976,-122.3916917 && lcn_ref -> 5 || last_edit_changeset -> 66845961 || tiger:name_type -> St || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1549060019000 || last_edit_user_id -> 119881 || lanes -> 4 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 11\n27656675000002 && 37.7780738,-122.3918146:37.7780999,-122.3918474 && lcn_ref -> 5 || last_edit_changeset -> 66845961 || tiger:name_type -> St || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1549060019000 || last_edit_user_id -> 119881 || lanes -> 4 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 11\n-27656675000002 && 37.7780999,-122.3918474:37.7780738,-122.3918146 && lcn_ref -> 5 || last_edit_changeset -> 66845961 || tiger:name_type -> St || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1549060019000 || last_edit_user_id -> 119881 || lanes -> 4 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 11\n27656675000003 && 37.7780999,-122.3918474:37.7781551,-122.3919168 && lcn_ref -> 5 || last_edit_changeset -> 66845961 || tiger:name_type -> St || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1549060019000 || last_edit_user_id -> 119881 || lanes -> 4 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 11\n-27656675000003 && 37.7781551,-122.3919168:37.7780999,-122.3918474 && lcn_ref -> 5 || last_edit_changeset -> 66845961 || tiger:name_type -> St || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || last_edit_time -> 1549060019000 || last_edit_user_id -> 119881 || lanes -> 4 || name -> 3rd Street || tiger:name_base -> 3rd || highway -> primary || last_edit_version -> 11\n590853367000000 && 37.7786883,-122.3872971:37.7785276,-122.3872786 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853367000000 && 37.7785276,-122.3872786:37.7786883,-122.3872971 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n28841478000001 && 37.777137,-122.3905167:37.7771748,-122.3904322 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n-28841478000001 && 37.7771748,-122.3904322:37.777137,-122.3905167 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n28841478000002 && 37.7771748,-122.3904322:37.7772128,-122.3903334:37.7773491,-122.3899824:37.777528,-122.3895061:37.7777124,-122.3890618:37.7779389,-122.3884807:37.7781839,-122.3878746:37.7782627,-122.3876648 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n-28841478000002 && 37.7782627,-122.3876648:37.7781839,-122.3878746:37.7779389,-122.3884807:37.7777124,-122.3890618:37.777528,-122.3895061:37.7773491,-122.3899824:37.7772128,-122.3903334:37.7771748,-122.3904322 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n28841478000003 && 37.7782627,-122.3876648:37.7782747,-122.3876336 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n-28841478000003 && 37.7782747,-122.3876336:37.7782627,-122.3876648 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n28841478000004 && 37.7782747,-122.3876336:37.7782866,-122.3876024 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n-28841478000004 && 37.7782866,-122.3876024:37.7782747,-122.3876336 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n28841478000005 && 37.7782866,-122.3876024:37.7783314,-122.3875034 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n-28841478000005 && 37.7783314,-122.3875034:37.7782866,-122.3876024 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n28841478000006 && 37.7783314,-122.3875034:37.7783422,-122.3874788:37.7783469,-122.3874609 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n-28841478000006 && 37.7783469,-122.3874609:37.7783422,-122.3874788:37.7783314,-122.3875034 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || bicycle -> yes || surface -> paved || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> McCovey Cove Promenade || highway -> footway || last_edit_version -> 7\n590853267000000 && 37.7801872,-122.3870601:37.7799619,-122.3870407 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853267000000 && 37.7799619,-122.3870407:37.7801872,-122.3870601 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n679290114000000 && 37.7757405,-122.3926776:37.7770583,-122.3910135 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || access -> private || last_edit_time -> 1553538170000 || last_edit_user_id -> 381909 || highway -> footway || last_edit_version -> 1\n-679290114000000 && 37.7770583,-122.3910135:37.7757405,-122.3926776 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || access -> private || last_edit_time -> 1553538170000 || last_edit_user_id -> 381909 || highway -> footway || last_edit_version -> 1\n551597295000000 && 37.7802649,-122.3881086:37.7802121,-122.3881633 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597295000000 && 37.7802121,-122.3881633:37.7802649,-122.3881086 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n590853318000000 && 37.7794211,-122.3868471:37.7792158,-122.3868271 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853318000000 && 37.7792158,-122.3868271:37.7794211,-122.3868471 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n551597301000001 && 37.7796301,-122.3895066:37.7796896,-122.3895786 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597301000001 && 37.7796896,-122.3895786:37.7796301,-122.3895066 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597301000002 && 37.7796896,-122.3895786:37.779744,-122.389647 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597301000002 && 37.779744,-122.389647:37.7796896,-122.3895786 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597301000003 && 37.779744,-122.389647:37.7797673,-122.3896763 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597301000003 && 37.7797673,-122.3896763:37.779744,-122.389647 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597301000004 && 37.7797673,-122.3896763:37.7797876,-122.3897018 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597301000004 && 37.7797876,-122.3897018:37.7797673,-122.3896763 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597301000005 && 37.7797876,-122.3897018:37.7798461,-122.3897755 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597301000005 && 37.7798461,-122.3897755:37.7797876,-122.3897018 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n551597301000006 && 37.7798461,-122.3897755:37.7799283,-122.3898756 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n-551597301000006 && 37.7799283,-122.3898756:37.7798461,-122.3897755 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || surface -> paved || last_edit_time -> 1552249956000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 2\n46274021000001 && 37.7781844,-122.3871725:37.778192,-122.3870464 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 5\n-46274021000001 && 37.778192,-122.3870464:37.7781844,-122.3871725 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 5\n46274021000002 && 37.778192,-122.3870464:37.7781938,-122.3870164:37.7782751,-122.3870243:37.778262,-122.3872413:37.7782336,-122.3872386 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 5\n-46274021000002 && 37.7782336,-122.3872386:37.778262,-122.3872413:37.7782751,-122.3870243:37.7781938,-122.3870164:37.778192,-122.3870464 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 5\n46274021000003 && 37.7782336,-122.3872386:37.7781807,-122.3872335:37.7781844,-122.3871725 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 5\n-46274021000003 && 37.7781844,-122.3871725:37.7781807,-122.3872335:37.7782336,-122.3872386 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 5\n475096890000000 && 37.7798742,-122.3888631:37.7801901,-122.3885291 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || surface -> asphalt || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 1 || oneway -> no\n-475096890000000 && 37.7801901,-122.3885291:37.7798742,-122.3888631 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46140328 || surface -> asphalt || last_edit_time -> 1487259632000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 1 || oneway -> no\n675874839000001 && 37.7800738,-122.3896894:37.7800065,-122.3895663 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874839000001 && 37.7800065,-122.3895663:37.7800738,-122.3896894 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874839000002 && 37.7800065,-122.3895663:37.7799515,-122.389499 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874839000002 && 37.7799515,-122.389499:37.7800065,-122.3895663 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874839000003 && 37.7799515,-122.389499:37.7799037,-122.3894405 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874839000003 && 37.7799037,-122.3894405:37.7799515,-122.389499 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874839000004 && 37.7799037,-122.3894405:37.7798499,-122.3893746 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874839000004 && 37.7798499,-122.3893746:37.7799037,-122.3894405 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n675874839000005 && 37.7798499,-122.3893746:37.7797938,-122.3893059 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n-675874839000005 && 37.7797938,-122.3893059:37.7798499,-122.3893746 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249940000 || last_edit_user_id -> 119881 || footway -> crossing || highway -> footway || last_edit_version -> 1\n175941562000001 && 37.7804141,-122.3886956:37.7803706,-122.3886293:37.7802698,-122.3884758:37.7802896,-122.3881134 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n-175941562000001 && 37.7802896,-122.3881134:37.7802698,-122.3884758:37.7803706,-122.3886293:37.7804141,-122.3886956 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n175941562000002 && 37.7802896,-122.3881134:37.7802649,-122.3881086 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n-175941562000002 && 37.7802649,-122.3881086:37.7802896,-122.3881134 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n175941562000003 && 37.7802649,-122.3881086:37.7802692,-122.3880404 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n-175941562000003 && 37.7802692,-122.3880404:37.7802649,-122.3881086 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n175941562000004 && 37.7802692,-122.3880404:37.7802803,-122.3878528 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n-175941562000004 && 37.7802803,-122.3878528:37.7802692,-122.3880404 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n175941562000005 && 37.7802803,-122.3878528:37.7802905,-122.3876807 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n-175941562000005 && 37.7802905,-122.3876807:37.7802803,-122.3878528 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399502000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 6\n590853344000000 && 37.7790488,-122.3869395:37.7788684,-122.3869236 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853344000000 && 37.7788684,-122.3869236:37.7790488,-122.3869395 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188133000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n215819238000000 && 37.7795907,-122.3881108:37.7792283,-122.3880755 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46148021 || surface -> asphalt || last_edit_time -> 1487278639000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> no\n-215819238000000 && 37.7792283,-122.3880755:37.7795907,-122.3881108 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46148021 || surface -> asphalt || last_edit_time -> 1487278639000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 2 || oneway -> no\n396945236000001 && 37.7786279,-122.3913742:37.7782779,-122.3917759:37.77825,-122.391808 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67815924 || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|left|through| || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 4 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n396945236000002 && 37.77825,-122.391808:37.7781551,-122.3919168 && lcn_ref -> 5 || sidewalk -> right || last_edit_changeset -> 67815924 || maxspeed -> 30 mph || tiger:name_type -> St || oneway -> yes || tiger:county -> San Francisco, CA || turn:lanes -> left|left|through| || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 4 || name -> King Street || tiger:name_base -> King || highway -> primary || last_edit_version -> 6\n475079949000001 && 37.7797852,-122.3883274:37.7793769,-122.3882779:37.7792283,-122.3880755 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || surface -> asphalt || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 1 || oneway -> no\n-475079949000001 && 37.7792283,-122.3880755:37.7793769,-122.3882779:37.7797852,-122.3883274 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || surface -> asphalt || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 1 || oneway -> no\n475079949000002 && 37.7792283,-122.3880755:37.7790517,-122.3878327 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || surface -> asphalt || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 1 || oneway -> no\n-475079949000002 && 37.7790517,-122.3878327:37.7792283,-122.3880755 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || surface -> asphalt || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || service -> parking_aisle || highway -> service || last_edit_version -> 1 || oneway -> no\n564269102000001 && 37.7797919,-122.3891349:37.7798427,-122.3890699 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || highway -> service || last_edit_version -> 2 || oneway -> no\n-564269102000001 && 37.7798427,-122.3890699:37.7797919,-122.3891349 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || highway -> service || last_edit_version -> 2 || oneway -> no\n564269102000002 && 37.7798427,-122.3890699:37.7798848,-122.3890227 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || highway -> service || last_edit_version -> 2 || oneway -> no\n-564269102000002 && 37.7798848,-122.3890227:37.7798427,-122.3890699 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || surface -> asphalt || last_edit_time -> 1554404097000 || last_edit_user_id -> 8896306 || highway -> service || last_edit_version -> 2 || oneway -> no\n98763155000000 && 37.7945115,-122.2801913:37.794526,-122.2804283:37.7944291,-122.2807429:37.7941808,-122.2813292:37.7940424,-122.2818855:37.7924867,-122.2925298:37.7923663,-122.2938428:37.7922974,-122.2948651:37.7922724,-122.2957718:37.7922742,-122.3048119:37.7923382,-122.3057071:37.8002356,-122.337502:37.8005555,-122.3394444:37.8005731,-122.3417555:37.8002,-122.3440522:37.7993839,-122.3463395:37.7966695,-122.3521317:37.7952254,-122.3542829:37.7942858,-122.35525:37.7835484,-122.3641971:37.7827362,-122.3649976:37.7819608,-122.3658122:37.7812034,-122.3670254:37.7804669,-122.368444:37.7796681,-122.3701419:37.779118,-122.37136:37.7786381,-122.3724007:37.7782054,-122.3737548:37.7780351,-122.3747789:37.7779146,-122.3759844:37.7779234,-122.3766325:37.7778328,-122.3856068:37.7778675,-122.3860842:37.7779152,-122.3864767:37.7780214,-122.3868165:37.778192,-122.3870464 && payment:clipper -> yes || last_edit_changeset -> 76407142 || website -> https://sanfranciscobayferry.com || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || source -> bing || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || duration -> 00:45 || last_edit_user_name -> clay_c || route -> ferry || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> Oakland Jack London Square - Oracle Park (Seasonal) || last_edit_version -> 17 || foot -> yes\n-98763155000000 && 37.778192,-122.3870464:37.7780214,-122.3868165:37.7779152,-122.3864767:37.7778675,-122.3860842:37.7778328,-122.3856068:37.7779234,-122.3766325:37.7779146,-122.3759844:37.7780351,-122.3747789:37.7782054,-122.3737548:37.7786381,-122.3724007:37.779118,-122.37136:37.7796681,-122.3701419:37.7804669,-122.368444:37.7812034,-122.3670254:37.7819608,-122.3658122:37.7827362,-122.3649976:37.7835484,-122.3641971:37.7942858,-122.35525:37.7952254,-122.3542829:37.7966695,-122.3521317:37.7993839,-122.3463395:37.8002,-122.3440522:37.8005731,-122.3417555:37.8005555,-122.3394444:37.8002356,-122.337502:37.7923382,-122.3057071:37.7922742,-122.3048119:37.7922724,-122.2957718:37.7922974,-122.2948651:37.7923663,-122.2938428:37.7924867,-122.2925298:37.7940424,-122.2818855:37.7941808,-122.2813292:37.7944291,-122.2807429:37.794526,-122.2804283:37.7945115,-122.2801913 && payment:clipper -> yes || last_edit_changeset -> 76407142 || website -> https://sanfranciscobayferry.com || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || source -> bing || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || duration -> 00:45 || last_edit_user_name -> clay_c || route -> ferry || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || name -> Oakland Jack London Square - Oracle Park (Seasonal) || last_edit_version -> 17 || foot -> yes\n23750452000001 && 37.7799063,-122.3892986:37.7798589,-122.3892282 && sidewalk -> both || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 9\n-23750452000001 && 37.7798589,-122.3892282:37.7799063,-122.3892986 && sidewalk -> both || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 9\n23750452000002 && 37.7798589,-122.3892282:37.7798486,-122.3892128:37.7797919,-122.3891349 && sidewalk -> both || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 9\n-23750452000002 && 37.7797919,-122.3891349:37.7798486,-122.3892128:37.7798589,-122.3892282 && sidewalk -> both || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 9\n23750452000003 && 37.7797919,-122.3891349:37.7795582,-122.3888042 && sidewalk -> both || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 9\n-23750452000003 && 37.7795582,-122.3888042:37.7797919,-122.3891349 && sidewalk -> both || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 9\n23750452000004 && 37.7795582,-122.3888042:37.7795153,-122.3887435 && sidewalk -> both || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 9\n-23750452000004 && 37.7795153,-122.3887435:37.7795582,-122.3888042 && sidewalk -> both || last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399493000 || last_edit_user_id -> 933797 || name -> 2nd Street || highway -> unclassified || last_edit_version -> 9\n111550911000001 && 37.780253,-122.3910067:37.7801945,-122.3910808 && lanes:both_ways -> 1 || lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81206964 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || turn:lanes:both_ways -> left || last_edit_time -> 1582096753000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 13 || parking:lane:both -> parallel || cycleway -> lane\n-111550911000001 && 37.7801945,-122.3910808:37.780253,-122.3910067 && lanes:both_ways -> 1 || lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81206964 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || turn:lanes:both_ways -> left || last_edit_time -> 1582096753000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 13 || parking:lane:both -> parallel || cycleway -> lane\n111550911000002 && 37.7801945,-122.3910808:37.7797352,-122.3916622 && lanes:both_ways -> 1 || lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81206964 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || turn:lanes:both_ways -> left || last_edit_time -> 1582096753000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 13 || parking:lane:both -> parallel || cycleway -> lane\n-111550911000002 && 37.7797352,-122.3916622:37.7801945,-122.3910808 && lanes:both_ways -> 1 || lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81206964 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || turn:lanes:both_ways -> left || last_edit_time -> 1582096753000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 13 || parking:lane:both -> parallel || cycleway -> lane\n111550911000003 && 37.7797352,-122.3916622:37.7792873,-122.392231:37.7790694,-122.3925052 && lanes:both_ways -> 1 || lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81206964 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || turn:lanes:both_ways -> left || last_edit_time -> 1582096753000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 13 || parking:lane:both -> parallel || cycleway -> lane\n-111550911000003 && 37.7790694,-122.3925052:37.7792873,-122.392231:37.7797352,-122.3916622 && lanes:both_ways -> 1 || lcn_ref -> 36 || sidewalk -> both || last_edit_changeset -> 81206964 || surface -> asphalt || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || trolley_wire -> yes || turn:lanes:both_ways -> left || last_edit_time -> 1582096753000 || last_edit_user_id -> 24555 || lit -> yes || lanes -> 3 || name -> Townsend Street || tiger:name_base -> Townsend || highway -> tertiary || last_edit_version -> 13 || parking:lane:both -> parallel || cycleway -> lane\n590853370000000 && 37.778711,-122.3871036:37.7785294,-122.387086 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853370000000 && 37.7785294,-122.387086:37.778711,-122.3871036 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853265000000 && 37.7801602,-122.3873008:37.7799577,-122.3872801 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853265000000 && 37.7799577,-122.3872801:37.7801602,-122.3873008 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n780006766000000 && 37.7780487,-122.3878883:37.7781535,-122.3876146 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || bridge -> yes || highway -> footway || last_edit_version -> 1 || layer -> 1\n-780006766000000 && 37.7781535,-122.3876146:37.7780487,-122.3878883 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || bridge -> yes || highway -> footway || last_edit_version -> 1 || layer -> 1\n396945233000001 && 37.77776,-122.3914156:37.7779088,-122.3916059:37.7779356,-122.3916402 && lcn_ref -> 5 || last_edit_changeset -> 67815924 || turn:lanes:forward -> left;through|through|through|through|right || lanes:forward -> 5 || lanes:backward -> 2 || tiger:name_type -> St || cycleway:left -> shared_lane || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A45 || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 7 || name -> 3rd Street || tiger:name_base -> 3rd || placement:forward -> left_of:1 || turn:lanes:backward -> through|through || highway -> primary || last_edit_version -> 6\n-396945233000001 && 37.7779356,-122.3916402:37.7779088,-122.3916059:37.77776,-122.3914156 && lcn_ref -> 5 || last_edit_changeset -> 67815924 || turn:lanes:forward -> left;through|through|through|through|right || lanes:forward -> 5 || lanes:backward -> 2 || tiger:name_type -> St || cycleway:left -> shared_lane || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A45 || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 7 || name -> 3rd Street || tiger:name_base -> 3rd || placement:forward -> left_of:1 || turn:lanes:backward -> through|through || highway -> primary || last_edit_version -> 6\n396945233000002 && 37.7779356,-122.3916402:37.777976,-122.3916917 && lcn_ref -> 5 || last_edit_changeset -> 67815924 || turn:lanes:forward -> left;through|through|through|through|right || lanes:forward -> 5 || lanes:backward -> 2 || tiger:name_type -> St || cycleway:left -> shared_lane || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A45 || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 7 || name -> 3rd Street || tiger:name_base -> 3rd || placement:forward -> left_of:1 || turn:lanes:backward -> through|through || highway -> primary || last_edit_version -> 6\n-396945233000002 && 37.777976,-122.3916917:37.7779356,-122.3916402 && lcn_ref -> 5 || last_edit_changeset -> 67815924 || turn:lanes:forward -> left;through|through|through|through|right || lanes:forward -> 5 || lanes:backward -> 2 || tiger:name_type -> St || cycleway:left -> shared_lane || tiger:county -> San Francisco, CA || tiger:zip_left -> 94124 || last_edit_user_name -> Nodestrawaymus || tiger:cfcc -> A45 || last_edit_time -> 1551809590000 || last_edit_user_id -> 8899783 || lanes -> 7 || name -> 3rd Street || tiger:name_base -> 3rd || placement:forward -> left_of:1 || turn:lanes:backward -> through|through || highway -> primary || last_edit_version -> 6\n23605184000001 && 37.7784479,-122.3853497:37.7784719,-122.3853896:37.7783532,-122.3873783 && last_edit_user_name -> rkuris || last_edit_changeset -> 61874822 || motor_vehicle -> no || man_made -> pier || last_edit_time -> 1534907890000 || last_edit_user_id -> 501715 || name -> Fishing Pier || last_edit_version -> 6 || foot -> yes\n-23605184000001 && 37.7783532,-122.3873783:37.7784719,-122.3853896:37.7784479,-122.3853497 && last_edit_user_name -> rkuris || last_edit_changeset -> 61874822 || motor_vehicle -> no || man_made -> pier || last_edit_time -> 1534907890000 || last_edit_user_id -> 501715 || name -> Fishing Pier || last_edit_version -> 6 || foot -> yes\n23605184000002 && 37.7783532,-122.3873783:37.7783469,-122.3874609 && last_edit_user_name -> rkuris || last_edit_changeset -> 61874822 || motor_vehicle -> no || man_made -> pier || last_edit_time -> 1534907890000 || last_edit_user_id -> 501715 || name -> Fishing Pier || last_edit_version -> 6 || foot -> yes\n-23605184000002 && 37.7783469,-122.3874609:37.7783532,-122.3873783 && last_edit_user_name -> rkuris || last_edit_changeset -> 61874822 || motor_vehicle -> no || man_made -> pier || last_edit_time -> 1534907890000 || last_edit_user_id -> 501715 || name -> Fishing Pier || last_edit_version -> 6 || foot -> yes\n175941559000001 && 37.7795907,-122.3881108:37.7795902,-122.38789:37.7795579,-122.3878866 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> asphalt || last_edit_time -> 1515399501000 || last_edit_user_id -> 933797 || service -> parking_aisle || highway -> service || last_edit_version -> 6 || oneway -> no\n-175941559000001 && 37.7795579,-122.3878866:37.7795902,-122.38789:37.7795907,-122.3881108 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> asphalt || last_edit_time -> 1515399501000 || last_edit_user_id -> 933797 || service -> parking_aisle || highway -> service || last_edit_version -> 6 || oneway -> no\n175941559000002 && 37.7795579,-122.3878866:37.7790517,-122.3878327 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> asphalt || last_edit_time -> 1515399501000 || last_edit_user_id -> 933797 || service -> parking_aisle || highway -> service || last_edit_version -> 6 || oneway -> no\n-175941559000002 && 37.7790517,-122.3878327:37.7795579,-122.3878866 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> asphalt || last_edit_time -> 1515399501000 || last_edit_user_id -> 933797 || service -> parking_aisle || highway -> service || last_edit_version -> 6 || oneway -> no\n681699527000000 && 37.7803259,-122.3901684:37.7801869,-122.3899976 && lcn_ref -> 11 || last_edit_changeset -> 81291843 || surface -> asphalt || parking:lane:left -> parallel || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || cycleway:left -> lane || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || alt_name -> Second Street || cycleway:right -> shared_lane || last_edit_time -> 1582234975000 || last_edit_user_id -> 24555 || lanes -> 2 || name -> 2nd Street || tiger:name_base -> 2nd || placement:forward -> left_of:1 || highway -> secondary || last_edit_version -> 2\n-681699527000000 && 37.7801869,-122.3899976:37.7803259,-122.3901684 && lcn_ref -> 11 || last_edit_changeset -> 81291843 || surface -> asphalt || parking:lane:left -> parallel || lanes:forward -> 1 || lanes:backward -> 1 || tiger:name_type -> St || cycleway:left -> lane || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || alt_name -> Second Street || cycleway:right -> shared_lane || last_edit_time -> 1582234975000 || last_edit_user_id -> 24555 || lanes -> 2 || name -> 2nd Street || tiger:name_base -> 2nd || placement:forward -> left_of:1 || highway -> secondary || last_edit_version -> 2\n590853371000000 && 37.7787166,-122.3870093:37.778533,-122.3869916 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853371000000 && 37.778533,-122.3869916:37.7787166,-122.3870093 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188134000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853266000000 && 37.7801766,-122.3871848:37.7799513,-122.3871634 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853266000000 && 37.7799513,-122.3871634:37.7801766,-122.3871848 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188129000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853287000000 && 37.7797498,-122.3873921:37.7795676,-122.3873761 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853287000000 && 37.7795676,-122.3873761:37.7797498,-122.3873921 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n675874829000001 && 37.7774879,-122.3913196:37.7774539,-122.3912347 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> crossing || highway -> footway || last_edit_version -> 3\n-675874829000001 && 37.7774539,-122.3912347:37.7774879,-122.3913196 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> crossing || highway -> footway || last_edit_version -> 3\n675874829000002 && 37.7774539,-122.3912347:37.777423,-122.391152 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> crossing || highway -> footway || last_edit_version -> 3\n-675874829000002 && 37.777423,-122.391152:37.7774539,-122.3912347 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || footway -> crossing || highway -> footway || last_edit_version -> 3\n780006767000001 && 37.7779365,-122.3880178:37.7779872,-122.3880488:37.7780326,-122.3879303 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-780006767000001 && 37.7780326,-122.3879303:37.7779872,-122.3880488:37.7779365,-122.3880178 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n780006767000002 && 37.7780326,-122.3879303:37.7780487,-122.3878883 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-780006767000002 && 37.7780487,-122.3878883:37.7780326,-122.3879303 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n675874832000001 && 37.7784179,-122.3910016:37.7796301,-122.3895066 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874832000001 && 37.7796301,-122.3895066:37.7784179,-122.3910016 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n675874832000002 && 37.7796301,-122.3895066:37.7797938,-122.3893059 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n-675874832000002 && 37.7797938,-122.3893059:37.7796301,-122.3895066 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249939000 || last_edit_user_id -> 119881 || footway -> sidewalk || highway -> footway || last_edit_version -> 1\n224384059000001 && 37.7801869,-122.3899976:37.7800271,-122.389801:37.7800076,-122.3897768 && lcn_ref -> 11 || last_edit_changeset -> 81291843 || surface -> asphalt || turn:lanes:forward -> left|right || parking:lane:left -> parallel || lanes:forward -> 2 || lanes:backward -> 1 || tiger:name_type -> St || cycleway:left -> lane || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || alt_name -> Second Street || cycleway:right -> shared_lane || last_edit_time -> 1582234972000 || last_edit_user_id -> 24555 || lanes -> 3 || name -> 2nd Street || tiger:name_base -> 2nd || placement:forward -> left_of:1 || highway -> secondary || last_edit_version -> 9\n-224384059000001 && 37.7800076,-122.3897768:37.7800271,-122.389801:37.7801869,-122.3899976 && lcn_ref -> 11 || last_edit_changeset -> 81291843 || surface -> asphalt || turn:lanes:forward -> left|right || parking:lane:left -> parallel || lanes:forward -> 2 || lanes:backward -> 1 || tiger:name_type -> St || cycleway:left -> lane || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || alt_name -> Second Street || cycleway:right -> shared_lane || last_edit_time -> 1582234972000 || last_edit_user_id -> 24555 || lanes -> 3 || name -> 2nd Street || tiger:name_base -> 2nd || placement:forward -> left_of:1 || highway -> secondary || last_edit_version -> 9\n224384059000002 && 37.7800076,-122.3897768:37.7799221,-122.3896767 && lcn_ref -> 11 || last_edit_changeset -> 81291843 || surface -> asphalt || turn:lanes:forward -> left|right || parking:lane:left -> parallel || lanes:forward -> 2 || lanes:backward -> 1 || tiger:name_type -> St || cycleway:left -> lane || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || alt_name -> Second Street || cycleway:right -> shared_lane || last_edit_time -> 1582234972000 || last_edit_user_id -> 24555 || lanes -> 3 || name -> 2nd Street || tiger:name_base -> 2nd || placement:forward -> left_of:1 || highway -> secondary || last_edit_version -> 9\n-224384059000002 && 37.7799221,-122.3896767:37.7800076,-122.3897768 && lcn_ref -> 11 || last_edit_changeset -> 81291843 || surface -> asphalt || turn:lanes:forward -> left|right || parking:lane:left -> parallel || lanes:forward -> 2 || lanes:backward -> 1 || tiger:name_type -> St || cycleway:left -> lane || tiger:county -> San Francisco, CA || last_edit_user_name -> fmarier || tiger:cfcc -> A41 || alt_name -> Second Street || cycleway:right -> shared_lane || last_edit_time -> 1582234972000 || last_edit_user_id -> 24555 || lanes -> 3 || name -> 2nd Street || tiger:name_base -> 2nd || placement:forward -> left_of:1 || highway -> secondary || last_edit_version -> 9\n397097608000001 && 37.7799221,-122.3896767:37.7798641,-122.3896058 && lcn_ref -> 11 || last_edit_changeset -> 67703843 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || alt_name -> Second Street || last_edit_time -> 1551485906000 || last_edit_user_id -> 8778516 || name -> 2nd Street || tiger:name_base -> 2nd || highway -> secondary || last_edit_version -> 4\n-397097608000001 && 37.7798641,-122.3896058:37.7799221,-122.3896767 && lcn_ref -> 11 || last_edit_changeset -> 67703843 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || alt_name -> Second Street || last_edit_time -> 1551485906000 || last_edit_user_id -> 8778516 || name -> 2nd Street || tiger:name_base -> 2nd || highway -> secondary || last_edit_version -> 4\n397097608000002 && 37.7798641,-122.3896058:37.7798182,-122.3895497 && lcn_ref -> 11 || last_edit_changeset -> 67703843 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || alt_name -> Second Street || last_edit_time -> 1551485906000 || last_edit_user_id -> 8778516 || name -> 2nd Street || tiger:name_base -> 2nd || highway -> secondary || last_edit_version -> 4\n-397097608000002 && 37.7798182,-122.3895497:37.7798641,-122.3896058 && lcn_ref -> 11 || last_edit_changeset -> 67703843 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || alt_name -> Second Street || last_edit_time -> 1551485906000 || last_edit_user_id -> 8778516 || name -> 2nd Street || tiger:name_base -> 2nd || highway -> secondary || last_edit_version -> 4\n397097608000003 && 37.7798182,-122.3895497:37.7797641,-122.3894835 && lcn_ref -> 11 || last_edit_changeset -> 67703843 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || alt_name -> Second Street || last_edit_time -> 1551485906000 || last_edit_user_id -> 8778516 || name -> 2nd Street || tiger:name_base -> 2nd || highway -> secondary || last_edit_version -> 4\n-397097608000003 && 37.7797641,-122.3894835:37.7798182,-122.3895497 && lcn_ref -> 11 || last_edit_changeset -> 67703843 || tiger:name_type -> St || tiger:county -> San Francisco, CA || last_edit_user_name -> Heidi_Baum_Lyft || tiger:cfcc -> A41 || alt_name -> Second Street || last_edit_time -> 1551485906000 || last_edit_user_id -> 8778516 || name -> 2nd Street || tiger:name_base -> 2nd || highway -> secondary || last_edit_version -> 4\n740440586000000 && 37.7782866,-122.3876024:37.778258,-122.3875849 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n-740440586000000 && 37.778258,-122.3875849:37.7782866,-122.3876024 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || bicycle -> dismount || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 2\n675739899000000 && 37.7786239,-122.3910073:37.7786817,-122.3909338 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183951000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-675739899000000 && 37.7786817,-122.3909338:37.7786239,-122.3910073 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183951000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n23605179000000 && 37.7801996,-122.3847962:37.7800437,-122.3875357 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock C || last_edit_version -> 3\n-23605179000000 && 37.7800437,-122.3875357:37.7801996,-122.3847962 && last_edit_user_name -> rkuris || last_edit_changeset -> 27676411 || man_made -> pier || last_edit_time -> 1419440397000 || last_edit_user_id -> 501715 || name -> Dock C || last_edit_version -> 3\n590853292000000 && 37.7797898,-122.3868774:37.7795882,-122.386861 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853292000000 && 37.7795882,-122.386861:37.7797898,-122.3868774 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188130000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n590853313000000 && 37.7793844,-122.3873602:37.7792022,-122.3873434 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n-590853313000000 && 37.7792022,-122.3873434:37.7793844,-122.3873602 && last_edit_user_name -> KaL3288 || last_edit_changeset -> 59250365 || man_made -> pier || last_edit_time -> 1527188132000 || last_edit_user_id -> 7134350 || last_edit_version -> 1\n26786711000001 && 37.7780424,-122.3915459:37.7780531,-122.3915423:37.7780737,-122.3915233:37.778444,-122.3910339:37.7784179,-122.3910016 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n-26786711000001 && 37.7784179,-122.3910016:37.778444,-122.3910339:37.7780737,-122.3915233:37.7780531,-122.3915423:37.7780424,-122.3915459 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n26786711000002 && 37.7784179,-122.3910016:37.7783946,-122.3909736:37.7783126,-122.3908593:37.778297,-122.3908378:37.7782611,-122.3908692:37.7782279,-122.3908241:37.778032,-122.3909972:37.777824,-122.3911809:37.7777973,-122.3912239 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n-26786711000002 && 37.7777973,-122.3912239:37.777824,-122.3911809:37.778032,-122.3909972:37.7782279,-122.3908241:37.7782611,-122.3908692:37.778297,-122.3908378:37.7783126,-122.3908593:37.7783946,-122.3909736:37.7784179,-122.3910016 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n26786711000003 && 37.7777973,-122.3912239:37.7777759,-122.3912584:37.7779933,-122.3915289:37.7780112,-122.3915447 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n-26786711000003 && 37.7780112,-122.3915447:37.7779933,-122.3915289:37.7777759,-122.3912584:37.7777973,-122.3912239 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n26786711000004 && 37.7780112,-122.3915447:37.7780293,-122.3915503:37.7780424,-122.3915459 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n-26786711000004 && 37.7780424,-122.3915459:37.7780293,-122.3915503:37.7780112,-122.3915447 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n780006772000000 && 37.778258,-122.3875849:37.7781826,-122.3875386 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n-780006772000000 && 37.7781826,-122.3875386:37.778258,-122.3875849 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || access -> customers || bicycle -> dismount || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || highway -> footway || last_edit_version -> 1\n551597291000000 && 37.7788644,-122.3907018:37.7788191,-122.3907594 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n-551597291000000 && 37.7788191,-122.3907594:37.7788644,-122.3907018 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || surface -> paved || last_edit_time -> 1515399488000 || last_edit_user_id -> 933797 || highway -> footway || last_edit_version -> 1\n280398655000000 && 37.7765872,-122.3900419:37.7769393,-122.3903189 && lcn_ref -> 5 || last_edit_changeset -> 66816409 || bridge:alt_name -> Third Street Bridge || maxspeed -> 30 mph || tiger:name_type -> St || bridge:old_name -> China Basin Bridge || layer -> 1 || oneway -> yes || tiger:county -> San Francisco, CA || last_edit_user_name -> clay_c || tiger:cfcc -> A45 || cycleway:right -> shared_lane || last_edit_time -> 1548972485000 || last_edit_user_id -> 119881 || lanes -> 2 || name -> 3rd Street || tiger:name_base -> 3rd || bridge -> movable || bridge:name -> Lefty O'Doul Bridge || wikipedia -> en:Lefty O'Doul Bridge || highway -> primary || last_edit_version -> 9\n# Areas\n112927457000000 && 37.7804693,-122.3905653:37.7802204,-122.390253:37.780048,-122.3904731:37.7802969,-122.3907854 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55665504 || addr:housenumber -> 101 || last_edit_time -> 1516652178000 || last_edit_user_id -> 3817650 || addr:street -> Townsend Street || last_edit_version -> 4 || building -> yes || height -> 14\n149167950000000 && 37.7793804,-122.3918884:37.7788756,-122.3912454:37.7788608,-122.3912266:37.7790995,-122.3909268:37.7792915,-122.3911714:37.7792594,-122.3912118:37.7794091,-122.3914025:37.7794418,-122.3913613:37.7796196,-122.3915878 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55665504 || last_edit_time -> 1516652181000 || last_edit_user_id -> 3817650 || source -> Bing || last_edit_version -> 3 || building -> yes || height -> 32\n288649074000000 && 37.7774734,-122.3919068:37.7773877,-122.3917981:37.7774934,-122.3916647:37.7775791,-122.3917734 && last_edit_user_name -> Rub21 || last_edit_changeset -> 23007178 || last_edit_time -> 1403104270000 || last_edit_user_id -> 510836 || last_edit_version -> 1\n288649066000000 && 37.777251,-122.3916653:37.7775803,-122.3920759:37.7775927,-122.3920599:37.7776079,-122.3920789:37.7777582,-122.3918859:37.7777462,-122.3918709:37.777791,-122.3918134:37.7778032,-122.3918286:37.7778434,-122.391777:37.7776122,-122.3914886:37.7776092,-122.3914924:37.777504,-122.3913612:37.7774948,-122.3913731:37.7774877,-122.3913642:37.7774872,-122.3913636:37.7773638,-122.391522:37.7773642,-122.3915224:37.7773617,-122.3915256:37.7773581,-122.3915212:37.777329,-122.3915585:37.7773323,-122.3915626:37.777327,-122.3915693:37.7773238,-122.3915652:37.7772946,-122.3916026:37.7772976,-122.3916063:37.7772933,-122.3916118:37.7772903,-122.3916081:37.7772612,-122.3916455:37.7772638,-122.3916488 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || last_edit_version -> 6\n500533911000000 && 37.7783948,-122.3898619:37.7783842,-122.3898572:37.7783672,-122.3898545:37.7783524,-122.3898558:37.7783431,-122.3898605:37.7782296,-122.3896827:37.7782432,-122.3896641:37.7782474,-122.389648:37.778246,-122.3896374:37.7783742,-122.3895073:37.778377,-122.3895108:37.77839,-122.3895152:37.7784043,-122.3895139:37.7784119,-122.3895084:37.7785118,-122.3896591:37.778513,-122.3896698:37.7785157,-122.3896811:37.7785228,-122.3896977:37.7785335,-122.3897185 && last_edit_user_name -> stadiaarcadia || last_edit_changeset -> 65662006 || last_edit_time -> 1545380846000 || last_edit_user_id -> 8570285 || last_edit_version -> 2 || leisure -> pitch || sport -> baseball\n129176912000000 && 37.779675,-122.3919536:37.7798446,-122.3917345:37.7802895,-122.3922968:37.7803194,-122.3923352:37.7801469,-122.3925527 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 148 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> Townsend Street || source -> Bing || last_edit_version -> 4 || building -> yes || height -> 9\n148547439000000 && 37.7800504,-122.3910998:37.7798039,-122.3907906:37.7796403,-122.3909994:37.7798868,-122.3913086 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 133;135 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> Townsend Street || last_edit_version -> 4 || building -> yes || height -> 22\n149167937000000 && 37.7793804,-122.3918884:37.7788756,-122.3912454:37.7788369,-122.391294:37.7788147,-122.3912657:37.7786207,-122.3915094:37.7786479,-122.391544:37.7786152,-122.3915852:37.7788049,-122.3918269:37.7788607,-122.3917567:37.7787991,-122.3916783:37.7789013,-122.3915499:37.7788747,-122.3915159:37.7789138,-122.3914667:37.7791412,-122.3917564:37.7791432,-122.3918217:37.7789698,-122.3920396:37.7789197,-122.3920417:37.7788927,-122.3920072:37.7788338,-122.3920812:37.7790291,-122.3923299 && last_edit_user_name -> fmarier || last_edit_changeset -> 81245156 || addr:housenumber -> 170 || last_edit_time -> 1582163755000 || last_edit_user_id -> 24555 || building:levels -> 11 || addr:street -> King Street || source -> Bing || last_edit_version -> 4 || building -> residential\n129176920000000 && 37.7799614,-122.3915865:37.7800865,-122.3914281:37.7803318,-122.3917382:37.7803763,-122.3917944:37.7802511,-122.3919528 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 136 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> Townsend Street || source -> Bing || last_edit_version -> 5 || building -> yes || height -> 9\n149167947000000 && 37.7801038,-122.3885476:37.7801769,-122.3884545:37.7800498,-122.3882946:37.7799766,-122.3883877:37.7799864,-122.3884001:37.78004,-122.3884673 && last_edit_user_name -> saikofish || last_edit_changeset -> 10613677 || last_edit_time -> 1328632023000 || last_edit_user_id -> 236172 || source -> Bing || last_edit_version -> 1 || building -> yes\n499568648000000 && 37.7778592,-122.3902665:37.7778442,-122.3902807:37.7775817,-122.3898387:37.7775967,-122.3898244 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148735000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 1 || height -> 70\n4786909000000 && 37.7775413,-122.3897328:37.7775967,-122.3898244:37.7778592,-122.3902665:37.7779558,-122.3904214:37.7780576,-122.3905475:37.7781509,-122.3906064:37.778266,-122.3906232:37.7783925,-122.3907676:37.7783126,-122.3908593:37.7783946,-122.3909736:37.7784745,-122.3908818:37.7788542,-122.3904119:37.7790774,-122.3901385:37.7792215,-122.3899588:37.7796411,-122.3894179:37.7797507,-122.3892878:37.7796721,-122.3891819:37.7794335,-122.3888818:37.7793835,-122.3889543:37.7793402,-122.3890078:37.7792546,-122.3888987:37.7787392,-122.3882409:37.7781811,-122.3881865:37.777622,-122.3895379 && last_edit_user_name -> dpaschich || last_edit_changeset -> 49646850 || last_edit_time -> 1497828755000 || last_edit_user_id -> 621202 || last_edit_version -> 20\n368215209000000 && 37.7793062,-122.3886733:37.7788023,-122.3880524:37.7788429,-122.3879978:37.7790497,-122.3880061:37.7794409,-122.3884978 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148737000 || last_edit_user_id -> 131996 || amenity -> parking || name -> Fan Lot || last_edit_version -> 2\n499568643000000 && 37.7796411,-122.3894179:37.7795626,-122.389312:37.7796721,-122.3891819:37.7797507,-122.3892878 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148734000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 1 || height -> 62\n129176907000000 && 37.7804898,-122.391538:37.7802445,-122.3912279:37.7800865,-122.3914281:37.7803318,-122.3917382 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55665504 || addr:housenumber -> 130 || last_edit_time -> 1516652180000 || last_edit_user_id -> 3817650 || addr:street -> Townsend Street || source -> Bing || last_edit_version -> 5 || building -> yes || height -> 7\n237055578000000 && 37.7769415,-122.3905693:37.7774027,-122.3911548:37.775814,-122.3931742:37.7752717,-122.392494:37.7752748,-122.3924899:37.775437,-122.3922851:37.7754445,-122.3922946:37.7754707,-122.3922728:37.7754981,-122.3922511:37.7755242,-122.3922253:37.775549,-122.3921963:37.7755699,-122.3921668:37.7755896,-122.3921339:37.7756081,-122.3920984:37.7756283,-122.3920625:37.7756208,-122.392053:37.7759816,-122.3915975:37.7759891,-122.391607:37.7760147,-122.3915844:37.7760427,-122.3915605:37.7760718,-122.391533:37.776096,-122.3915042:37.7761181,-122.3914729:37.7761357,-122.3914424:37.776156,-122.3914047:37.776174,-122.3913735:37.7761665,-122.391364:37.776485,-122.3909617:37.7764925,-122.3909712:37.7765177,-122.3909478:37.7765446,-122.3909263:37.7765723,-122.3909005:37.776598,-122.3908711:37.7766231,-122.3908371:37.7766436,-122.3908018:37.7766604,-122.3907692:37.7766787,-122.3907361:37.7766712,-122.3907266:37.7768563,-122.3904928 && last_edit_changeset -> 57646704 || website -> http://www.mccarthycook.com/portfolio/china-basin/ || last_edit_user_name -> Aaron Lidman || addr:housenumber -> 185 || old_name -> China Basin Landing || last_edit_time -> 1522359003000 || last_edit_user_id -> 53073 || landuse -> commercial || name -> China Basin || addr:street -> Berry Street || wikipedia -> en:China Basin Landing || last_edit_version -> 6 || wikidata -> Q5099588\n499568647000000 && 37.7790774,-122.3901385:37.7790521,-122.3901055:37.7788289,-122.3903789:37.7788542,-122.3904119 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148735000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 1 || height -> 70\n129176902000000 && 37.7805053,-122.3922741:37.7804063,-122.3921489:37.7802511,-122.3919528:37.7803763,-122.3917944:37.7803318,-122.3917382:37.7804898,-122.391538:37.7807884,-122.3919155:37.7805256,-122.3922484 && last_edit_user_name -> saikofish || last_edit_changeset -> 10573628 || last_edit_time -> 1328250784000 || last_edit_user_id -> 236172 || amenity -> parking || last_edit_version -> 2\n129176910000000 && 37.7798446,-122.3917345:37.7799614,-122.3915865:37.7802511,-122.3919528:37.7804063,-122.3921489:37.7802895,-122.3922968 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 144 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> Townsend Street || source -> Bing || last_edit_version -> 5 || building -> yes || height -> 11\n499568642000000 && 37.7792546,-122.3888987:37.7792236,-122.3889398:37.7791656,-122.3890166:37.7791741,-122.3890266:37.7793119,-122.3891883:37.7793246,-122.3892768:37.7789748,-122.3898803:37.7787268,-122.3901914:37.7785954,-122.3903336:37.7785254,-122.3903926:37.7784661,-122.3904194:37.7783749,-122.3904328:37.7782837,-122.3904167:37.7781883,-122.3903819:37.7780866,-122.3902987:37.7780399,-122.3902344:37.777622,-122.3895379:37.7775413,-122.3897328:37.7775967,-122.3898244:37.7778592,-122.3902665:37.7779558,-122.3904214:37.7780576,-122.3905475:37.7781509,-122.3906064:37.7782547,-122.3906226:37.778266,-122.3906232:37.7783713,-122.3906292:37.7785197,-122.3905999:37.7785858,-122.3905774:37.7786841,-122.3904992:37.7788356,-122.3903154:37.7791165,-122.3899775:37.7791788,-122.389891:37.779379,-122.389561:37.7795214,-122.3893274:37.7794564,-122.3890591:37.7793835,-122.3889543:37.7793402,-122.3890078 && last_edit_user_name -> nvk || last_edit_changeset -> 49514946 || last_edit_time -> 1497398222000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 3 || height -> 60\n46264108000000 && 37.7755484,-122.3928325:37.7771336,-122.3908183:37.7769379,-122.3905718:37.7753527,-122.392586 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55750134 || addr:housenumber -> 185 || last_edit_time -> 1516904718000 || last_edit_user_id -> 3817650 || name -> China Basin - Wharfside Building || addr:street -> Berry Street || last_edit_version -> 5 || addr:postcode -> 94107 || building -> yes || height -> 23\n23750468000000 && 37.7803833,-122.3885964:37.7804947,-122.3884972:37.7806186,-122.3884045:37.78073,-122.3883314:37.7808652,-122.3882505:37.7809725,-122.3881995:37.7811024,-122.388145:37.7812219,-122.3881045:37.7812922,-122.3880861:37.781352,-122.388071:37.781416,-122.38806:37.7814011,-122.3880216:37.781378,-122.3879778:37.7813464,-122.3879343:37.7813066,-122.3878891:37.7812772,-122.3878628:37.781239,-122.3878395:37.7812026,-122.3878201:37.781165,-122.3878115:37.780633,-122.3877609:37.7806248,-122.387881:37.7805685,-122.3879684:37.7805706,-122.3879906:37.7805711,-122.38802:37.780567,-122.3880514:37.7805551,-122.3880801:37.7805376,-122.3881062:37.780519,-122.3881245:37.7804958,-122.3881382:37.7804695,-122.3881473:37.7804442,-122.3881454:37.7803111,-122.388129:37.7802904,-122.3884685 && last_edit_user_name -> danrademacher || last_edit_changeset -> 66351019 || last_edit_time -> 1547612337000 || last_edit_user_id -> 2682147 || name -> South Beach Park || emergency -> assembly_point || last_edit_version -> 5 || leisure -> park\n149167945000000 && 37.7798649,-122.3887155:37.7797689,-122.3885948:37.7799278,-122.3883925:37.7799603,-122.3884334:37.7800138,-122.3885007:37.7800238,-122.3885133 && last_edit_user_name -> saikofish || last_edit_changeset -> 10613677 || last_edit_time -> 1328632023000 || last_edit_user_id -> 236172 || source -> Bing || last_edit_version -> 1 || building -> yes\n551597308000000 && 37.7788863,-122.3909311:37.7788648,-122.390904:37.7787813,-122.3910101:37.7786918,-122.3911179:37.7787104,-122.3911408:37.7787656,-122.3910706:37.7787702,-122.3910764:37.7787751,-122.3910825:37.7788903,-122.390936 && area -> yes || wheelchair -> limited || last_edit_changeset -> 76418016 || wheelchair:description -> pass through platform to reach accessible platform || public_transport -> platform || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || last_edit_time -> 1572477087000 || last_edit_user_id -> 119881 || railway -> platform || tram -> yes || last_edit_version -> 4\n149167940000000 && 37.7800138,-122.3885007:37.78004,-122.3884673:37.7799864,-122.3884001:37.7799603,-122.3884334 && last_edit_user_name -> saikofish || last_edit_changeset -> 10613677 || last_edit_time -> 1328632023000 || last_edit_user_id -> 236172 || source -> Bing || last_edit_version -> 1 || building -> yes\n368214737000000 && 37.7787721,-122.3880927:37.7787217,-122.3880893:37.7787243,-122.388027:37.7787747,-122.3880304 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148737000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 2\n382844200000000 && 37.7787467,-122.3876771:37.7787503,-122.3875904:37.7786573,-122.3875842:37.7786555,-122.387627:37.7786537,-122.3876709 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792196000 || last_edit_user_id -> 53073 || amenity -> toilets || description -> open only during baseball games || toilets:disposal -> flush || last_edit_version -> 2 || building -> yes\n499568645000000 && 37.7777159,-122.3905564:37.7777373,-122.390602:37.7777748,-122.3905695:37.778032,-122.3909972:37.7782279,-122.3908241:37.7782611,-122.3908692:37.778297,-122.3908378:37.7783126,-122.3908593:37.7783925,-122.3907676:37.778266,-122.3906232:37.7781509,-122.3906064:37.7780576,-122.3905475:37.7779558,-122.3904214:37.7778592,-122.3902665:37.7775967,-122.3898244:37.7775413,-122.3897328:37.777528,-122.389764:37.7775471,-122.3898069:37.7774683,-122.3898825:37.7773995,-122.3900541:37.7775196,-122.3902709:37.7775488,-122.3902454:37.7776372,-122.3901657:37.7775876,-122.3900769:37.7776639,-122.3900094:37.7778782,-122.3904047 && last_edit_user_name -> dpaschich || last_edit_changeset -> 49646850 || last_edit_time -> 1497828760000 || last_edit_user_id -> 621202 || building:part -> yes || last_edit_version -> 3 || height -> 30\n148547436000000 && 37.7800808,-122.3900779:37.7800402,-122.3900307:37.7800066,-122.3900692:37.7799525,-122.3900758:37.7799489,-122.3900346:37.7798684,-122.3900484:37.7798717,-122.3900914:37.7797399,-122.3901143:37.7796688,-122.390202:37.7798275,-122.3904011 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 760 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> 2nd Street || source -> Bing || last_edit_version -> 4 || building -> yes || height -> 5\n656674365000000 && 37.7783822,-122.3896705:37.7783904,-122.3896747:37.7783972,-122.3896819:37.7784017,-122.3896915:37.7784036,-122.3897025:37.7784027,-122.3897137:37.7783987,-122.3897242:37.7783923,-122.3897324:37.7783841,-122.3897375:37.778375,-122.3897389:37.778366,-122.3897364:37.7783582,-122.3897304:37.7783525,-122.3897214:37.7783494,-122.3897105:37.7783493,-122.3896989:37.7783521,-122.3896882:37.7783574,-122.3896792:37.7783648,-122.3896729:37.7783733,-122.3896699 && last_edit_user_name -> stadiaarcadia || last_edit_changeset -> 65662006 || natural -> sand || last_edit_time -> 1545380846000 || last_edit_user_id -> 8570285 || last_edit_version -> 1\n500283910000000 && 37.77835,-122.3900106:37.7783301,-122.3900051:37.7783079,-122.3899917:37.7782878,-122.3899742:37.7782724,-122.3899512:37.7782616,-122.3899322:37.7782417,-122.3898999:37.7779531,-122.389387:37.7779384,-122.3892346:37.7778903,-122.3891542:37.7781152,-122.3885719:37.7786166,-122.3886251:37.7790076,-122.3891129:37.7790178,-122.3891702:37.7787852,-122.3895534:37.778497,-122.3899122:37.7784708,-122.3899409:37.7784526,-122.389964:37.7784348,-122.389982:37.7784173,-122.389995:37.7783964,-122.3900031:37.7783739,-122.3900099 && last_edit_user_name -> nvk || last_edit_changeset -> 49514582 || last_edit_time -> 1497396447000 || last_edit_user_id -> 131996 || last_edit_version -> 1 || leisure -> pitch || sport -> baseball\n129176913000000 && 37.7799671,-122.3905763:37.780048,-122.3904731:37.7802969,-122.3907854:37.780216,-122.3908886 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 111 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> Townsend Street || source -> Bing || last_edit_version -> 4 || building -> yes || height -> 14\n368215207000000 && 37.7775488,-122.3902454:37.7776372,-122.3901657:37.7775876,-122.3900769:37.7776639,-122.3900094:37.7778782,-122.3904047:37.7777159,-122.3905564 && last_edit_user_name -> nvk || last_edit_changeset -> 49433260 || last_edit_time -> 1497153691000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 3 || height -> 25\n500533912000000 && 37.7786295,-122.3896242:37.7786342,-122.3896478:37.7784031,-122.3898892:37.7784047,-122.3899106:37.7784005,-122.3899308:37.7783904,-122.3899469:37.7783745,-122.3899556:37.7783586,-122.3899562:37.7783438,-122.3899509:37.7783305,-122.3899328:37.7783268,-122.389912:37.7783289,-122.3898852:37.7781424,-122.3895955:37.7781479,-122.3895734:37.7781563,-122.3895501:37.7781658,-122.3895286:37.7781767,-122.3895082:37.7781888,-122.3894887:37.7782021,-122.3894704:37.7782165,-122.3894534:37.7782321,-122.3894378:37.7782487,-122.3894236:37.7782663,-122.389411:37.778285,-122.3894002:37.7783045,-122.389391:37.7783246,-122.389384:37.7783448,-122.3893792:37.7783649,-122.3893763:37.7783849,-122.3893757:37.7784047,-122.3893769:37.7784243,-122.3893801:37.7784435,-122.3893851:37.7784623,-122.3893921:37.7784806,-122.3894008:37.7784982,-122.3894112:37.7785152,-122.3894234:37.7785315,-122.389437:37.7785468,-122.3894523:37.7785613,-122.3894692:37.7785747,-122.3894875:37.7785871,-122.3895071:37.7785983,-122.3895281:37.7786082,-122.3895504:37.7786168,-122.389574:37.7786239,-122.3895988 && last_edit_user_name -> nvk || last_edit_changeset -> 49542344 || last_edit_time -> 1497476406000 || last_edit_user_id -> 131996 || last_edit_version -> 1\n780006768000000 && 37.7780719,-122.3879025:37.7779989,-122.3880929:37.777925,-122.3880476:37.7779365,-122.3880178:37.7779819,-122.3878992:37.777998,-122.3878573:37.7780487,-122.3878883 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 1\n475076824000000 && 37.7796563,-122.3886197:37.7799223,-122.3882817:37.7798874,-122.3882335:37.7799181,-122.3881758:37.7799279,-122.3881818:37.7799244,-122.3882454:37.780208,-122.3882705:37.7802136,-122.3881972:37.7802413,-122.3882006:37.780247,-122.3881281:37.7802157,-122.3881241:37.780219,-122.3880633:37.7796599,-122.3880102:37.7796563,-122.3880712:37.7796287,-122.3880631:37.7796425,-122.3877869:37.7786875,-122.3876876:37.778679,-122.387803:37.7787754,-122.387921:37.779069,-122.3879505:37.7794432,-122.3884172:37.7795863,-122.3884333:37.7795863,-122.3883743:37.7796202,-122.3883796:37.7796202,-122.3884279:37.7795418,-122.3885191:37.7796033,-122.3886009:37.7796287,-122.3885822 && parking -> surface || last_edit_user_name -> rowers2 || last_edit_changeset -> 49611218 || park_ride -> no || access -> private || surface -> asphalt || last_edit_time -> 1497694651000 || last_edit_user_id -> 2445224 || amenity -> parking || last_edit_version -> 3 || operator -> South Beach Harbor\n24352572000000 && 37.7797507,-122.3892878:37.7788542,-122.3904119:37.7783946,-122.3909736:37.7783126,-122.3908593:37.778297,-122.3908378:37.7782611,-122.3908692:37.7782279,-122.3908241:37.778032,-122.3909972:37.777824,-122.3911809:37.7775203,-122.3907951:37.7775742,-122.3907473:37.777426,-122.3904984:37.777478,-122.3904531:37.7773439,-122.390193:37.7773995,-122.3900541:37.7774683,-122.3898825:37.7774579,-122.3898547:37.7776808,-122.3892315:37.7781761,-122.3879688:37.7784374,-122.3879922:37.7784529,-122.3879574:37.778468,-122.3879282:37.7784871,-122.3879:37.7785098,-122.387875:37.7785377,-122.3878517:37.7785686,-122.3878338:37.7785976,-122.3878206:37.7786285,-122.3878091:37.7789058,-122.3881451:37.7793261,-122.3887165:37.7794335,-122.3888818 && last_edit_user_name -> nvk || last_edit_changeset -> 49682480 || last_edit_time -> 1497931997000 || last_edit_user_id -> 131996 || last_edit_version -> 22\n46274021000000 && 37.7781807,-122.3872335:37.7781844,-122.3871725:37.778192,-122.3870464:37.7781938,-122.3870164:37.7782751,-122.3870243:37.778262,-122.3872413:37.7782336,-122.3872386 && area -> yes || last_edit_changeset -> 82023504 || access -> customers || man_made -> pier || public_transport -> platform || network -> SFBF;GGF || last_edit_user_name -> clay_c || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || floating -> yes || ferry -> yes || name -> Oracle Park || last_edit_version -> 5\n46264110000000 && 37.7773985,-122.3911565:37.7773261,-122.3910654:37.7773088,-122.3910876:37.7773019,-122.3910791:37.7770948,-122.391054:37.7757359,-122.3927813:37.7757544,-122.3930477:37.7757612,-122.3930567:37.7757451,-122.3930777:37.7758163,-122.3931676 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55750134 || addr:housenumber -> 185 || last_edit_time -> 1516904718000 || last_edit_user_id -> 3817650 || name -> China Basin - Berry Street Building || building:levels -> 5 || addr:street -> Berry Street || last_edit_version -> 8 || addr:postcode -> 94107 || building -> yes || height -> 25\n499568649000000 && 37.7787392,-122.3882409:37.7787414,-122.3882034:37.7781925,-122.38815:37.7781811,-122.3881865 && last_edit_user_name -> bdon || last_edit_changeset -> 54464306 || last_edit_time -> 1512752426000 || last_edit_user_id -> 898775 || building:part -> yes || last_edit_version -> 2 || height -> 65\n148547443000000 && 37.7799671,-122.3905763:37.780048,-122.3904731:37.7802204,-122.390253:37.7800808,-122.3900779:37.7798275,-122.3904011 && last_edit_user_name -> lmcslssslsssl || last_edit_changeset -> 68817476 || last_edit_time -> 1554249721000 || last_edit_user_id -> 5789664 || last_edit_version -> 2 || building -> yes\n780006769000000 && 37.7781429,-122.3876079:37.7781785,-122.3875179:37.7781854,-122.3875223:37.7782632,-122.3875716:37.778258,-122.3875849:37.7782457,-122.3876158:37.7782335,-122.3876468:37.7782276,-122.3876616:37.7781535,-122.3876146 && area -> yes || last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || man_made -> pier || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 1\n288716390000000 && 37.7797683,-122.3888045:37.7796818,-122.388696:37.7796282,-122.3887676:37.7797168,-122.388872 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55665504 || last_edit_time -> 1516652184000 || last_edit_user_id -> 3817650 || last_edit_version -> 3 || building -> yes || height -> 4\n499568644000000 && 37.7784745,-122.3908818:37.7783925,-122.3907676:37.7783126,-122.3908593:37.7783946,-122.3909736 && last_edit_user_name -> nvk || last_edit_changeset -> 49433260 || last_edit_time -> 1497153692000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 2 || height -> 65\n98224507000000 && 37.7783521,-122.390058:37.7783312,-122.3900539:37.77831,-122.3900472:37.778291,-122.3900364:37.7782724,-122.3900214:37.7782563,-122.3900024:37.7782428,-122.3899822:37.7779255,-122.3894344:37.7779151,-122.3892713:37.7778521,-122.38916:37.7781025,-122.3884973:37.7783452,-122.388521:37.7783624,-122.3882735:37.7785746,-122.3882888:37.7785624,-122.3885394:37.7786492,-122.3885493:37.7790564,-122.3890651:37.7791942,-122.3889:37.7792236,-122.3889398:37.7791656,-122.3890166:37.7791741,-122.3890266:37.7791493,-122.3890533:37.7790789,-122.3890964:37.7790865,-122.3891197:37.7790763,-122.389142:37.7790835,-122.3891814:37.7789774,-122.3892832:37.7788027,-122.3896128:37.7787852,-122.3895995:37.7784885,-122.3899824:37.7784623,-122.3900111:37.7784441,-122.3900288:37.7784263,-122.3900401:37.7784088,-122.3900491:37.7783911,-122.3900545:37.7783718,-122.3900587 && last_edit_user_name -> nvk || last_edit_changeset -> 49514946 || last_edit_time -> 1497398222000 || last_edit_user_id -> 131996 || type -> multipolygon || last_edit_version -> 7\n368215208000000 && 37.7777748,-122.3905695:37.7777373,-122.390602:37.7775866,-122.3907361:37.7775742,-122.3907473:37.7775203,-122.3907951:37.777824,-122.3911809:37.778032,-122.3909972 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148736000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 2 || height -> 25\n499568652000000 && 37.7790988,-122.3885419:37.7790914,-122.388562:37.7790776,-122.3885741:37.7790585,-122.3885822:37.7790405,-122.3885822:37.7790331,-122.3885647:37.779032,-122.3885406:37.7790204,-122.3885124:37.7790003,-122.3884789:37.7789865,-122.3884494:37.7789812,-122.3884172:37.7789801,-122.3883877:37.7789706,-122.3883542:37.7789743,-122.3883367:37.7789889,-122.388332:37.7790076,-122.3883703:37.7790264,-122.3883885:37.7790385,-122.3884021:37.7790556,-122.3884375:37.779062,-122.3884729:37.7790748,-122.3884981:37.779092,-122.3885216 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148735000 || last_edit_user_id -> 131996 || building:part -> yes || last_edit_version -> 1 || height -> 60\n675498960000000 && 37.7786743,-122.3911428:37.7786914,-122.3911212:37.7787133,-122.3911488:37.7787199,-122.391157:37.7787028,-122.3911787 && area -> yes || wheelchair -> designated || last_edit_changeset -> 67976688 || public_transport -> platform || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || last_edit_time -> 1552183967000 || last_edit_user_id -> 119881 || railway -> platform || tram -> yes || last_edit_version -> 2 || height -> 2'10\"\n189011905000000 && 37.7790215,-122.390622:37.779007,-122.3906038:37.7796229,-122.3898202:37.7796386,-122.38984:37.7796526,-122.3898577:37.7790367,-122.3906412 && area -> yes || wheelchair -> yes || last_edit_changeset -> 67976688 || light_rail -> yes || public_transport -> platform || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || last_edit_time -> 1552183964000 || last_edit_user_id -> 119881 || railway -> platform || last_edit_version -> 4 || height -> 2'10\"\n23749874000000 && 37.7817508,-122.3877722:37.7819204,-122.3846479:37.7791187,-122.3843904:37.7784877,-122.3852964:37.7783857,-122.3875056 && last_edit_user_name -> rkuris || wheelchair -> limited || last_edit_changeset -> 28983748 || website -> http://www.southbeachharbor.com || phone -> +1-415-495-4911 || last_edit_time -> 1424456484000 || last_edit_user_id -> 501715 || name -> South Beach Harbor || last_edit_version -> 7 || leisure -> marina\n129176911000000 && 37.7795032,-122.3904133:37.7796688,-122.390202:37.7798275,-122.3904011:37.7799671,-122.3905763:37.780216,-122.3908886:37.7800504,-122.3910998:37.7798039,-122.3907906:37.7797579,-122.3907328 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55665504 || last_edit_time -> 1516652180000 || last_edit_user_id -> 3817650 || name -> Townsend Building || source -> Bing || last_edit_version -> 4 || building -> yes || height -> 23\n368214735000000 && 37.7792343,-122.3888277:37.7791593,-122.3887344:37.779158,-122.3887113:37.7792187,-122.3886271:37.7792376,-122.3886271:37.7793048,-122.3887055:37.7793041,-122.3887468 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || level -> 1 || last_edit_time -> 1497148737000 || last_edit_user_id -> 131996 || last_edit_version -> 2 || leisure -> pitch || sport -> baseball\n148547438000000 && 37.7792697,-122.3907112:37.7795244,-122.3910308:37.7794408,-122.3911374:37.7791861,-122.3908179 && last_edit_changeset -> 51734223 || addr:state -> CA || tourism -> hotel || source -> Bing || addr:postcode -> 94107 || building -> yes || addr:city -> San Francisco || last_edit_user_name -> lmcslssslsssl || addr:housenumber -> 138 || last_edit_time -> 1504571927000 || last_edit_user_id -> 5789664 || name -> Via Hotel || addr:street -> King Street || last_edit_version -> 2\n149167938000000 && 37.7801882,-122.3879969:37.7801956,-122.3878532:37.7797238,-122.3878057:37.7797164,-122.3879576:37.7799892,-122.3879981 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138020 || last_edit_time -> 1487254010000 || last_edit_user_id -> 4518035 || name -> South Beach Harbor building || building:levels -> 2 || source -> Bing || last_edit_version -> 2 || building -> yes\n499568651000000 && 37.7775866,-122.3907361:37.7774458,-122.390502:37.7774945,-122.3904564:37.7774362,-122.3903424:37.7775196,-122.3902709:37.7775488,-122.3902454:37.7777159,-122.3905564:37.7777373,-122.390602 && last_edit_user_name -> nvk || last_edit_changeset -> 49514582 || last_edit_time -> 1497396447000 || last_edit_user_id -> 131996 || last_edit_version -> 2\n551597307000000 && 37.778644,-122.3910142:37.778636,-122.3910041:37.7786865,-122.3909398:37.7786817,-122.3909338:37.7786759,-122.3909265:37.7788064,-122.3907434:37.7788191,-122.3907594:37.7788318,-122.3907753 && area -> yes || wheelchair -> limited || last_edit_changeset -> 76418016 || wheelchair:description -> pass through platform to reach accessible platform || public_transport -> platform || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || last_edit_time -> 1572477087000 || last_edit_user_id -> 119881 || railway -> platform || tram -> yes || last_edit_version -> 4\n368214736000000 && 37.7786358,-122.3880685:37.7785732,-122.3880616:37.778577,-122.3880065:37.7786396,-122.3880134 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148737000 || last_edit_user_id -> 131996 || building:part -> yes || name -> Anchor Plaza || last_edit_version -> 3 || height -> 10\n112897758000000 && 37.7807876,-122.3909412:37.7806233,-122.3907345:37.7803102,-122.3911328:37.7804761,-122.3913416:37.7806589,-122.3911091:37.7806886,-122.3911466:37.7807697,-122.3910436:37.7807382,-122.391004 && gnis:county_name -> San Francisco || last_edit_changeset -> 55665504 || amenity -> fire_station || gnis:reviewed -> no || source -> USGS Geonames || building -> yes || last_edit_user_name -> bdon_import || addr:housenumber -> 698 || last_edit_time -> 1516652177000 || last_edit_user_id -> 3817650 || gnis:import_uuid -> 57871b70-0100-4405-bb30-88b2e001a944 || name -> San Francisco Fire Department Main Office || gnis:feature_id -> 2107380 || addr:street -> 2nd Street || last_edit_version -> 5 || ele -> 7 || height -> 11\n148547437000000 && 37.7798868,-122.3913086:37.7796403,-122.3909994:37.7794566,-122.3912337:37.7797031,-122.3915429 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 139 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> Townsend Street || last_edit_version -> 4 || building -> yes || height -> 26\n112927454000000 && 37.780633,-122.3902844:37.7811248,-122.3896622:37.7806118,-122.3890104:37.7805694,-122.389064:37.7805461,-122.3892089:37.7805763,-122.3893307:37.7806993,-122.3893478:37.7808129,-122.3894984:37.7808381,-122.3896288:37.7806759,-122.3898349:37.7805927,-122.3898392:37.7804156,-122.3895985:37.780457,-122.3893966:37.7803531,-122.3893618:37.7801072,-122.3896702:37.7805788,-122.390283 && last_edit_user_name -> bdon_import || last_edit_changeset -> 55665504 || last_edit_time -> 1516652178000 || last_edit_user_id -> 3817650 || last_edit_version -> 4 || building -> residential || height -> 25\n28843806000000 && 37.7803263,-122.3881019:37.7804405,-122.3881153:37.7804684,-122.3881143:37.7804885,-122.388112:37.7805081,-122.3881002:37.7805257,-122.3880825:37.7805395,-122.3880578:37.7805485,-122.3880213:37.7805492,-122.3879963:37.7805439,-122.3879638:37.7805308,-122.3879316:37.7805127,-122.3879068:37.7804839,-122.3878908:37.7803415,-122.3878706 && last_edit_user_name -> saikofish || last_edit_changeset -> 10573727 || last_edit_time -> 1328252478000 || last_edit_user_id -> 236172 || last_edit_version -> 3 || leisure -> playground\n499568646000000 && 37.7794048,-122.3895861:37.779379,-122.389561:37.7791788,-122.389891:37.7792047,-122.389916 && last_edit_user_name -> bdon || last_edit_changeset -> 54464306 || last_edit_time -> 1512752426000 || last_edit_user_id -> 898775 || building:part -> yes || last_edit_version -> 2 || height -> 70\n149167949000000 && 37.7786253,-122.3920526:37.7787189,-122.3919349:37.7788049,-122.3918269:37.7786152,-122.3915852:37.7785883,-122.3915509:37.7784087,-122.3917766 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 188;190 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> King Street || source -> Bing || last_edit_version -> 4 || building -> yes || height -> 32\n26786711000000 && 37.7780293,-122.3915503:37.7780424,-122.3915459:37.7780531,-122.3915423:37.7780737,-122.3915233:37.778444,-122.3910339:37.7784179,-122.3910016:37.7783946,-122.3909736:37.7783126,-122.3908593:37.778297,-122.3908378:37.7782611,-122.3908692:37.7782279,-122.3908241:37.778032,-122.3909972:37.777824,-122.3911809:37.7777973,-122.3912239:37.7777759,-122.3912584:37.7779933,-122.3915289:37.7780112,-122.3915447 && area -> yes || last_edit_changeset -> 69654142 || gnis:reviewed -> yes || name:etymology:wikidata -> Q561852 || gnis:county_id -> 075 || last_edit_user_name -> mapadu || gnis:created -> 02/15/2007 || last_edit_time -> 1556425316000 || last_edit_user_id -> 9755853 || name -> Willie Mays Plaza || gnis:feature_id -> 2107465 || gnis:state_id -> 06 || highway -> pedestrian || last_edit_version -> 11 || ele -> 4\n679290110000000 && 37.7775803,-122.3920759:37.777251,-122.3916653:37.777241,-122.3916782:37.7772324,-122.3916676:37.7772325,-122.3916678:37.7771367,-122.3917907:37.7771337,-122.3917869:37.7771327,-122.3917857:37.7771125,-122.3918116:37.7770912,-122.3918389:37.7770944,-122.3918428:37.7770491,-122.3919008:37.7770459,-122.3918968:37.7770452,-122.391896:37.7770247,-122.3919223:37.777004,-122.3919488:37.7770076,-122.3919534:37.776962,-122.3920119:37.7769586,-122.3920077:37.7769575,-122.3920063:37.7769375,-122.3920319:37.7769165,-122.3920588:37.7769198,-122.3920629:37.7768735,-122.3921223:37.7768708,-122.3921189:37.7768701,-122.392118:37.7768504,-122.3921432:37.7768294,-122.3921702:37.7768334,-122.3921751:37.7767879,-122.3922334:37.7767841,-122.3922286:37.7767829,-122.392227:37.7767629,-122.3922526:37.7767416,-122.39228:37.7767453,-122.3922846:37.7767223,-122.3923141:37.7767342,-122.3923289:37.7767184,-122.3923492:37.7767189,-122.3923499:37.7767184,-122.3923505:37.7767504,-122.3923905:37.776753,-122.3923872:37.7767518,-122.3923856:37.7767646,-122.3923691:37.7767969,-122.3924094:37.7767965,-122.3924089:37.7768208,-122.3923777:37.7768351,-122.3923955:37.7768365,-122.3923973:37.776856,-122.3923722:37.7768774,-122.3923449:37.7768619,-122.3923255:37.7769075,-122.3922672:37.7769224,-122.3922859:37.7769236,-122.3922873:37.7769443,-122.3922608:37.7769642,-122.3922353:37.7769483,-122.3922155:37.7769947,-122.392156:37.7770104,-122.3921756:37.7770114,-122.3921769:37.7770326,-122.3921498:37.777052,-122.3921249:37.7770361,-122.392105:37.7770823,-122.3920459:37.7770969,-122.3920642:37.7770983,-122.392066:37.777118,-122.3920408:37.777139,-122.3920139:37.777123,-122.3919939:37.7771685,-122.3919355:37.7771844,-122.3919553:37.7771852,-122.3919563:37.7772061,-122.3919295:37.7772265,-122.3919033:37.7772121,-122.3918853:37.7772529,-122.391833:37.7772901,-122.3918795:37.7772845,-122.3918867:37.77731,-122.3919186:37.777332,-122.3919461:37.7773366,-122.3919402:37.7774358,-122.3920642:37.7774139,-122.3920923:37.777384,-122.3920548:37.7773839,-122.3920547:37.7773812,-122.3920582:37.7774118,-122.3920964:37.7773983,-122.3921137:37.7773616,-122.3920678:37.7773613,-122.3920674:37.7770413,-122.3924775:37.7770439,-122.3924811:37.7770258,-122.3925043:37.7770349,-122.3925156:37.7770197,-122.392535:37.7770071,-122.3925193:37.7769279,-122.3926208:37.7770214,-122.3927376:37.7770246,-122.3927336:37.7770403,-122.3927131:37.7770604,-122.3927382:37.7770619,-122.3927401 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || building:levels -> 5 || last_edit_version -> 3 || building -> yes\n149167944000000 && 37.7786714,-122.3921113:37.7786253,-122.3920526:37.7784087,-122.3917766:37.7783158,-122.3918933:37.7785785,-122.392228 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 735;737;747;761 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> 3rd Street || source -> Bing || last_edit_version -> 4 || building -> yes || height -> 7\n148547440000000 && 37.7797579,-122.3907328:37.7795032,-122.3904133:37.7792697,-122.3907112:37.7795244,-122.3910308 && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87738695 || addr:housenumber -> 128 || last_edit_time -> 1594268930000 || last_edit_user_id -> 11362402 || addr:street -> King Street || source -> Bing || last_edit_version -> 4 || building -> yes || height -> 19\n675498957000000 && 37.7786036,-122.3910247:37.7786205,-122.3910031:37.7786239,-122.3910073:37.7786367,-122.3910234:37.7786197,-122.391045 && area -> yes || wheelchair -> designated || last_edit_changeset -> 67976688 || public_transport -> platform || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || last_edit_time -> 1552183967000 || last_edit_user_id -> 119881 || railway -> platform || tram -> yes || last_edit_version -> 2 || height -> 2'10\"\n# Lines\n48983526000000 && 37.7905851,-122.3950143:37.7885847,-122.3975406:37.788323,-122.3977965:37.7880548,-122.398018:37.7878101,-122.3981291:37.7875503,-122.3981682:37.7873225,-122.3981828:37.7870839,-122.3981521:37.7868816,-122.3980862:37.7867344,-122.3979804:37.7866897,-122.3979483:37.7865128,-122.3978126:37.7863208,-122.3975942:37.7854814,-122.3965696:37.7843749,-122.395191:37.7819286,-122.3920903:37.7815461,-122.3916972:37.7812205,-122.391452:37.7810394,-122.391378:37.7808783,-122.391331:37.7807172,-122.3913136:37.7805676,-122.3913125:37.7803971,-122.391319:37.780255,-122.3913565:37.7800982,-122.3913994:37.7799401,-122.3914895:37.7794059,-122.3921547:37.7779516,-122.3939894:37.7771695,-122.394915:37.7763997,-122.3955075:37.7761125,-122.3958585 && last_edit_user_name -> Irina Karachevtseva || note -> Planned Caltrain and High-speed rail extension || last_edit_changeset -> 56591095 || proposed -> rail || last_edit_time -> 1519331248000 || last_edit_user_id -> 5288591 || name -> Downtown Extension Project || railway -> proposed || last_edit_version -> 8 || layer -> -2 || tunnel -> proposed\n237055031000000 && 37.7679098,-122.3853462:37.7687659,-122.3850346:37.7698747,-122.3855501:37.7701577,-122.3855948:37.7702142,-122.3856126:37.7693373,-122.3834036:37.7697199,-122.3831784:37.7706551,-122.3855754:37.7706046,-122.3857261:37.7706126,-122.3857458:37.7706527,-122.3857361:37.770673,-122.3858007:37.7706873,-122.385885:37.7707093,-122.3859091:37.7707748,-122.3859255:37.7707925,-122.3859074:37.7708225,-122.3859098:37.7708368,-122.3859379:37.7708588,-122.3859621:37.7708736,-122.3859624:37.7708742,-122.3859728:37.7710202,-122.3859833:37.7710807,-122.3860208:37.7711676,-122.3860748:37.7711675,-122.3861564:37.7712137,-122.3861929:37.7712513,-122.386222:37.7714839,-122.3863871:37.7715751,-122.3863:37.7716029,-122.38633:37.7716408,-122.3863667:37.771669,-122.3863895:37.771757,-122.3864701:37.771956,-122.3864542:37.7719574,-122.3865477:37.7720222,-122.3865579:37.7722381,-122.3866086:37.772306,-122.3866297:37.7723784,-122.3866651:37.7723744,-122.3866785:37.7723968,-122.3866851:37.7723927,-122.386708:37.7724218,-122.3867187:37.7724494,-122.3867288:37.7724619,-122.3866785:37.7724793,-122.3866668:37.7725007,-122.3866629:37.7726847,-122.3866794:37.7727811,-122.3867215:37.7728259,-122.3865806:37.7732761,-122.3867755:37.7732861,-122.3867388:37.7734,-122.3849082:37.7733522,-122.3846464:37.7719452,-122.3815399:37.7719401,-122.3815127:37.7719454,-122.381492:37.7719585,-122.3814808:37.7719791,-122.3814771:37.7739552,-122.38174:37.7740825,-122.3814581:37.774451,-122.3818152:37.7745987,-122.3818349:37.7746142,-122.3818372:37.7746281,-122.381849:37.7746363,-122.3818701:37.7746379,-122.3818932:37.7743538,-122.3869477:37.7750788,-122.3870139:37.7751052,-122.3869525:37.7753395,-122.384905:37.7754234,-122.3849196:37.7754306,-122.3848487:37.776339,-122.3850142:37.7763567,-122.3850442:37.7761169,-122.3871096:37.7763414,-122.387128:37.7763346,-122.387284:37.7766122,-122.3873143:37.7767722,-122.3875505:37.7766133,-122.3898455:37.7766466,-122.3898729:37.776629,-122.3899071:37.7766762,-122.3899459:37.7766556,-122.3899859:37.7766357,-122.3899694:37.7765169,-122.3902035:37.7762619,-122.3901812:37.7759474,-122.3905758:37.7748697,-122.3919861:37.7749154,-122.3920548:37.7747568,-122.3922591:37.7746885,-122.3921924:37.7731188,-122.394124:37.7730306,-122.3943143:37.7729282,-122.3944211:37.772651,-122.3948105:37.7719846,-122.3957814:37.7712321,-122.3968349:37.7706081,-122.397668:37.7701568,-122.3981743:37.7701591,-122.3982084:37.7700241,-122.3983854:37.7700239,-122.3984197:37.7701394,-122.3985647:37.770225,-122.3985212:37.7703183,-122.398518:37.770411,-122.3985591:37.7705013,-122.398671:37.7708762,-122.3982123:37.7708851,-122.3982015:37.7713509,-122.3976384:37.7718078,-122.3970405:37.7718488,-122.3970866:37.7725192,-122.3962309:37.7732908,-122.3952282:37.7733952,-122.3950972:37.7734661,-122.3949736:37.7734518,-122.3949551:37.7735022,-122.3948793:37.7735452,-122.3948364:37.7735995,-122.3947949:37.7736818,-122.3947519:37.7746826,-122.3934308:37.7749071,-122.3932158:37.7749597,-122.3931457:37.7749936,-122.3930862:37.7750791,-122.3928519:37.7751019,-122.3928091:37.7751282,-122.3927816:37.7751587,-122.3927586:37.7751265,-122.3927194:37.7751333,-122.3927118:37.7750643,-122.392623:37.7752224,-122.392424:37.7752748,-122.3924899:37.775437,-122.3922851:37.7754445,-122.3922946:37.7754707,-122.3922728:37.7754981,-122.3922511:37.7755242,-122.3922253:37.775549,-122.3921963:37.7755699,-122.3921668:37.7755896,-122.3921339:37.7756081,-122.3920984:37.7756283,-122.3920625:37.7756208,-122.392053:37.7759816,-122.3915975:37.7759891,-122.391607:37.7760147,-122.3915844:37.7760427,-122.3915605:37.7760718,-122.391533:37.776096,-122.3915042:37.7761181,-122.3914729:37.7761357,-122.3914424:37.776156,-122.3914047:37.776174,-122.3913735:37.7761665,-122.391364:37.776485,-122.3909617:37.7764925,-122.3909712:37.7765177,-122.3909478:37.7765446,-122.3909263:37.7765723,-122.3909005:37.776598,-122.3908711:37.7766231,-122.3908371:37.7766436,-122.3908018:37.7766604,-122.3907692:37.7766787,-122.3907361:37.7766712,-122.3907266:37.7768563,-122.3904928:37.7768474,-122.3904858:37.776969,-122.3902496:37.7770483,-122.3903205:37.777082,-122.3902678:37.777114,-122.3902912:37.7771373,-122.3902444:37.7772127,-122.3902934:37.7773718,-122.3898719:37.7773495,-122.3898645:37.7774212,-122.3897295:37.7774047,-122.3897209:37.7774765,-122.3895946:37.77746,-122.3895811:37.7775269,-122.3894547:37.7775105,-122.3894424:37.7775793,-122.389321:37.7775609,-122.3893099:37.7776296,-122.3891804:37.7782276,-122.3876616:37.7782335,-122.3876468:37.7782457,-122.3876158:37.778258,-122.3875849:37.7782632,-122.3875716:37.7783059,-122.3874581:37.7783469,-122.3874609:37.7810992,-122.3876858:37.7814384,-122.3876944:37.7814669,-122.3872304:37.7815326,-122.38617:37.781574,-122.3855023 && last_edit_user_name -> clay_c || last_edit_changeset -> 82076434 || natural -> coastline || last_edit_time -> 1583954810000 || last_edit_user_id -> 119881 || source -> PGS || last_edit_version -> 18\n396435906000000 && 37.7906929,-122.3899312:37.7905462,-122.3897501:37.7899008,-122.3889368:37.789843,-122.3888634:37.7897205,-122.388709:37.7896852,-122.3886675:37.7896525,-122.3886357:37.7895545,-122.3885337:37.7894526,-122.3884444:37.7893999,-122.3883994:37.7893067,-122.3883328:37.7892158,-122.388279:37.7891216,-122.3882211:37.7890184,-122.3881754:37.7889093,-122.3881355:37.7887954,-122.3880962:37.7884185,-122.3879984:37.78821,-122.3879453:37.7880773,-122.3879159:37.7879557,-122.3878969:37.787818,-122.3878771:37.7876903,-122.3878729:37.787567,-122.3878716:37.7874243,-122.3878771:37.7871903,-122.3878945:37.7870888,-122.3879009:37.7869884,-122.3879084:37.7848528,-122.3880815:37.7847418,-122.388088:37.7846283,-122.3880962:37.7836851,-122.3881739:37.7835878,-122.3881825:37.783448,-122.3881925:37.7834234,-122.3881949:37.7819164,-122.3883127:37.7817889,-122.3883225:37.7816846,-122.3883295:37.781647,-122.3883325:37.7815238,-122.3883458:37.7814082,-122.3883592:37.7813027,-122.3883804:37.7811991,-122.388404:37.7810956,-122.3884383:37.7809991,-122.3884785:37.7809025,-122.3885263:37.7808133,-122.388579:37.7807267,-122.3886368:37.7806405,-122.3887057:37.780556,-122.3887816:37.7804804,-122.3888586:37.7803956,-122.3889498:37.7803191,-122.3890391:37.7799515,-122.389499:37.7798641,-122.3896058:37.7798111,-122.3896719:37.7797876,-122.3897018:37.7790503,-122.3906402:37.7789317,-122.3907858:37.7787639,-122.3909984:37.7786862,-122.391089:37.778659,-122.3911222:37.7785606,-122.3912442:37.778435,-122.3913965:37.7783212,-122.3915436:37.7781878,-122.3917296:37.7780999,-122.3918474:37.7780138,-122.3919656:37.7779714,-122.3920237:37.7778462,-122.3921929:37.7777242,-122.3923512:37.7772481,-122.3929574:37.7769819,-122.3933018:37.7766338,-122.3937415:37.776527,-122.3938762 && gauge -> 1435 || owner -> San Francisco Municipal Railway || last_edit_changeset -> 76418016 || operator -> San Francisco Municipal Railway || frequency -> 0 || network -> Muni || voltage -> 600 || last_edit_user_name -> clay_c || electrified -> contact_line || last_edit_time -> 1572477087000 || last_edit_user_id -> 119881 || name -> Muni Metro || railway -> light_rail || last_edit_version -> 11 || railway:preferred_direction -> forward\n396435909000000 && 37.7764812,-122.3938215:37.7765909,-122.3936872:37.7769456,-122.3932546:37.7772163,-122.3929208:37.777695,-122.3923165:37.7778194,-122.3921552:37.7779438,-122.391991:37.7779861,-122.3919337:37.7780738,-122.3918146:37.7781624,-122.3916974:37.7784042,-122.3913543:37.7785552,-122.3911612:37.7786485,-122.3910421:37.7788424,-122.3907901:37.7788875,-122.3907309:37.7789201,-122.3906879:37.7796077,-122.3898193:37.779744,-122.389647:37.7797687,-122.3896158:37.7798182,-122.3895497:37.7799037,-122.3894405:37.7800764,-122.3892169:37.7801703,-122.3891027:37.7802768,-122.3889803:37.7803559,-122.3888931:37.7804451,-122.3888035:37.780524,-122.3887298:37.7806131,-122.3886542:37.780705,-122.3885868:37.7807927,-122.3885276:37.780884,-122.3884747:37.7809838,-122.3884242:37.7810838,-122.388381:37.7811882,-122.388344:37.7812948,-122.3883127:37.7814059,-122.3882868:37.7815206,-122.3882677:37.7816447,-122.388253:37.7816761,-122.3882506:37.7817806,-122.3882427:37.7819181,-122.3882326:37.7834192,-122.3881106:37.7836538,-122.388091:37.7837574,-122.3880829:37.7844502,-122.3880303:37.7846242,-122.3880145:37.7847373,-122.3880043:37.7848501,-122.3879953:37.7869836,-122.3878256:37.7870846,-122.3878179:37.7871857,-122.3878104:37.7874187,-122.3877947:37.7875705,-122.3877973:37.7876972,-122.3878066:37.7878275,-122.3878164:37.7879603,-122.3878375:37.788092,-122.3878603:37.7882182,-122.3878929:37.7887,-122.3880155:37.7888072,-122.3880437:37.7889244,-122.3880766:37.7890304,-122.3881115:37.7891341,-122.3881507:37.7892413,-122.3882039:37.7893327,-122.3882574:37.7894328,-122.3883286:37.7894991,-122.388378:37.7896011,-122.3884679:37.7897013,-122.388571:37.7897785,-122.3886674:37.7898828,-122.3887972:37.7899642,-122.3888993:37.7905838,-122.3896751:37.7907437,-122.3898743 && gauge -> 1435 || owner -> San Francisco Municipal Railway || last_edit_changeset -> 76418016 || operator -> San Francisco Municipal Railway || frequency -> 0 || network -> Muni || voltage -> 600 || last_edit_user_name -> clay_c || electrified -> contact_line || last_edit_time -> 1572477087000 || last_edit_user_id -> 119881 || name -> Muni Metro || railway -> light_rail || last_edit_version -> 13 || railway:preferred_direction -> forward\n500286117000000 && 37.7785624,-122.3885394:37.7783452,-122.388521 && last_edit_user_name -> nvk || last_edit_changeset -> 49514946 || barrier -> fence || last_edit_time -> 1497398222000 || last_edit_user_id -> 131996 || last_edit_version -> 1\n500533913000000 && 37.7790763,-122.389142:37.7790564,-122.3890651 && last_edit_user_name -> nvk || last_edit_changeset -> 49542344 || barrier -> fence || last_edit_time -> 1497476406000 || last_edit_user_id -> 131996 || last_edit_version -> 1\n520696496000000 && 37.7799592,-122.3883387:37.7797515,-122.3885947:37.779908,-122.3887626:37.7800511,-122.388533:37.780111,-122.3885586:37.7801825,-122.3884748:37.7802534,-122.3885714 && last_edit_user_name -> lmcslssslsssl || last_edit_changeset -> 51657846 || last_edit_time -> 1504307782000 || last_edit_user_id -> 5789664 || last_edit_version -> 1\n520696497000000 && 37.7802534,-122.3885714:37.7799068,-122.3890065:37.7796474,-122.3886427:37.7799302,-122.3883007:37.7799592,-122.3883387 && last_edit_user_name -> lmcslssslsssl || last_edit_changeset -> 51657846 || last_edit_time -> 1504307782000 || last_edit_user_id -> 5789664 || last_edit_version -> 1\n679290107000000 && 37.7770045,-122.3901703:37.7769777,-122.3901605:37.7769438,-122.3901588:37.7767884,-122.3904556:37.7767785,-122.3905075:37.7767954,-122.390545 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || barrier -> wall || last_edit_time -> 1553538169000 || last_edit_user_id -> 381909 || last_edit_version -> 1\n# Points\n5184723629000000 && 37.7781018,-122.3913213 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4402491127000000 && 37.7819164,-122.3883127 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n2613221282000000 && 37.7716029,-122.38633 && last_edit_user_name -> clay_c || last_edit_changeset -> 76409384 || last_edit_time -> 1572458572000 || last_edit_user_id -> 119881 || last_edit_version -> 3 || leisure -> slipway\n1723634019000000 && 37.7906929,-122.3899312 && last_edit_user_name -> clay_c || last_edit_changeset -> 66816409 || last_edit_time -> 1548972253000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 3\n434844886000000 && 37.7761125,-122.3958585 && last_edit_user_name -> PlaneMad || last_edit_changeset -> 49676046 || last_edit_time -> 1497903191000 || last_edit_user_id -> 1306 || railway -> buffer_stop || last_edit_version -> 3\n6329652626000000 && 37.7781878,-122.3917296 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 1\n5184723419000000 && 37.7781811,-122.3912122 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723416000000 && 37.7797697,-122.3877276 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n4406691756000000 && 37.7846242,-122.3880145 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183955000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723345000000 && 37.7782936,-122.3879032 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723626000000 && 37.7780693,-122.3912836 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723387000000 && 37.7791266,-122.3880461 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4406691769000000 && 37.7848528,-122.3880815 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183955000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n4406691698000000 && 37.7834192,-122.3881106 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183955000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2\n5367527974000000 && 37.7766338,-122.3937415 && last_edit_user_name -> clay_c || last_edit_changeset -> 66788285 || last_edit_time -> 1548899860000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 2\n5326946978000000 && 37.7798461,-122.3897755 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 4 || crossing -> traffic_signals\n5184723051000000 && 37.7773963,-122.3906112 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n272166421000000 && 37.7897013,-122.388571 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197066000 || last_edit_user_id -> 933797 || railway -> crossing || last_edit_version -> 29 || crossing -> traffic_signals\n4406691782000000 && 37.7871857,-122.3878104 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197007000 || last_edit_user_id -> 933797 || railway -> crossing || last_edit_version -> 1 || crossing -> traffic_signals\n5326945389000000 && 37.7896852,-122.3886675 && last_edit_user_name -> clay_c || wheelchair -> no || last_edit_changeset -> 80926684 || last_edit_time -> 1581550313000 || last_edit_user_id -> 119881 || name -> Folsom || public_transport -> stop_position || railway -> tram_stop || tram -> yes || last_edit_version -> 7 || operator -> San Francisco Municipal Railway || network -> Muni\n5184723367000000 && 37.7802443,-122.3877625 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n5184723648000000 && 37.7791334,-122.3901745 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792195000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6392302633000000 && 37.779548,-122.3896426 && last_edit_user_name -> clay_c || bus -> yes || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473132000 || last_edit_user_id -> 119881 || name -> King Street & 2nd Street || public_transport -> platform || highway -> bus_stop || last_edit_version -> 4 || operator -> San Francisco Municipal Railway || network -> Muni\n1260356826000000 && 37.7724218,-122.3867187 && last_edit_user_name -> jambamkin || last_edit_changeset -> 70927749 || last_edit_time -> 1559662938000 || last_edit_user_id -> 53626 || last_edit_version -> 2 || leisure -> slipway\n5184723031000000 && 37.7772358,-122.3914024 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723354000000 && 37.7785219,-122.3878181 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723396000000 && 37.7792878,-122.3882436 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326947004000000 && 37.7786862,-122.391089 && last_edit_user_name -> clay_c || wheelchair -> no || last_edit_changeset -> 80926684 || last_edit_time -> 1581550313000 || last_edit_user_id -> 119881 || name -> 2nd & King || public_transport -> stop_position || railway -> tram_stop || tram -> yes || last_edit_version -> 7 || operator -> San Francisco Municipal Railway || network -> Muni\n6933256733000000 && 37.7837574,-122.3880829 && last_edit_user_name -> clay_c || wheelchair -> designated || last_edit_changeset -> 80927299 || last_edit_time -> 1581553753000 || last_edit_user_id -> 119881 || name -> Brannan || public_transport -> stop_position || railway -> stop || tram -> yes || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n4402491126000000 && 37.7817889,-122.3883225 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 3\n5184723364000000 && 37.7802401,-122.3879824 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1 || leisure -> picnic_table\n6318851270000000 && 37.7774441,-122.3912494 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || highway -> traffic_signals || last_edit_version -> 2\n4406691763000000 && 37.7847373,-122.3880043 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183955000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 2\n6318866709000000 && 37.7780687,-122.392029 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5367527980000000 && 37.7769456,-122.3932546 && last_edit_user_name -> clay_c || last_edit_changeset -> 66788285 || last_edit_time -> 1548899861000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 2\n5184723028000000 && 37.7770973,-122.3917657 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792187000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6329652916000000 && 37.7758723,-122.3932348 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5184723344000000 && 37.7783134,-122.3878502 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723365000000 && 37.7802418,-122.3878941 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1 || leisure -> picnic_table\n6318851271000000 && 37.7776175,-122.3912356 && last_edit_user_name -> Nodestrawaymus || traffic_signals:direction -> backward || last_edit_changeset -> 67815685 || last_edit_time -> 1551809194000 || last_edit_user_id -> 8899783 || highway -> traffic_signals || last_edit_version -> 1\n5184723386000000 && 37.7790847,-122.3879895 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n1142640695000000 && 37.7911224,-122.2944966 && last_edit_user_name -> clay_c || last_edit_changeset -> 76356726 || last_edit_time -> 1572368313000 || last_edit_user_id -> 119881 || amenity -> ferry_terminal || ferry -> yes || name -> Alameda Main Street Terminal || public_transport -> stop_position || last_edit_version -> 10 || operator -> Water Emergency Transportation Authority || network -> SFBF\n1425813141000000 && 37.7793475,-122.3906382 && last_edit_user_name -> saikofish || last_edit_changeset -> 10573628 || last_edit_time -> 1328250785000 || last_edit_user_id -> 236172 || amenity -> restaurant || name -> Pedro's || cuisine -> mexican || last_edit_version -> 2\n5326945383000000 && 37.7835878,-122.3881825 && last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 76418016 || last_edit_time -> 1572477085000 || last_edit_user_id -> 119881 || name -> Brannan || light_rail -> yes || public_transport -> stop_position || railway -> stop || last_edit_version -> 5 || operator -> San Francisco Municipal Railway || network -> Muni\n5184723029000000 && 37.7773391,-122.3914598 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792187000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5367527981000000 && 37.7769819,-122.3933018 && last_edit_user_name -> clay_c || last_edit_changeset -> 66788285 || last_edit_time -> 1548899861000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 2\n65317060000000 && 37.7800308,-122.3895366 && last_edit_user_name -> Heidi_Baum_Lyft || last_edit_changeset -> 67703843 || last_edit_time -> 1551485903000 || last_edit_user_id -> 8778516 || turn_restrictions -> no || highway -> traffic_signals || last_edit_version -> 16 || traffic_signals:sound -> yes || direction -> forward\n5184723050000000 && 37.7773584,-122.3905425 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4406691783000000 && 37.7871903,-122.3878945 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197007000 || last_edit_user_id -> 933797 || railway -> crossing || last_edit_version -> 1 || crossing -> traffic_signals\n6278267381000000 && 37.7776329,-122.3908147 && last_edit_user_name -> DennisL || last_edit_changeset -> 67189206 || addr:housenumber -> 913 || shop -> gift || last_edit_time -> 1550140744000 || last_edit_user_id -> 32952 || addr:state -> CA || name -> Giants Dugout Store || addr:street -> 3rd Street || last_edit_version -> 1 || addr:postcode -> 94107 || addr:city -> San Francisco\n5184723638000000 && 37.7779971,-122.3913392 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6391254602000000 && 37.7802753,-122.3911851 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149448000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> unmarked\n5184723639000000 && 37.7779732,-122.391355 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6318866708000000 && 37.777882,-122.3918136 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5326946958000000 && 37.7788148,-122.3906497 && last_edit_user_name -> cdruck || last_edit_changeset -> 67775371 || last_edit_time -> 1551720937000 || last_edit_user_id -> 8762431 || highway -> traffic_signals || last_edit_version -> 2\n5326946979000000 && 37.7798589,-122.3892282 && last_edit_user_name -> Heidi_Baum_Lyft || last_edit_changeset -> 67703843 || last_edit_time -> 1551485904000 || last_edit_user_id -> 8778516 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> uncontrolled\n5184723628000000 && 37.7780563,-122.3913378 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723649000000 && 37.7792175,-122.3900604 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792195000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6252543479000000 && 37.7798641,-122.3896058 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249945000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 2\n4406691893000000 && 37.7894526,-122.3884444 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197007000 || last_edit_user_id -> 933797 || railway -> crossing || last_edit_version -> 1 || crossing -> traffic_signals\n5184723355000000 && 37.7785696,-122.387793 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326945382000000 && 37.783448,-122.3881925 && last_edit_user_name -> clay_c || wheelchair -> no || last_edit_changeset -> 80926684 || last_edit_time -> 1581550313000 || last_edit_user_id -> 119881 || name -> Brannan || public_transport -> stop_position || railway -> tram_stop || tram -> yes || last_edit_version -> 7 || operator -> San Francisco Municipal Railway || network -> Muni\n5184723397000000 && 37.7792878,-122.3882978 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723418000000 && 37.7782163,-122.3911662 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326947005000000 && 37.7788424,-122.3907901 && last_edit_user_name -> clay_c || wheelchair -> no || last_edit_changeset -> 80926684 || last_edit_time -> 1581550313000 || last_edit_user_id -> 119881 || name -> 2nd & King || public_transport -> stop_position || railway -> tram_stop || tram -> yes || last_edit_version -> 7 || operator -> San Francisco Municipal Railway || network -> Muni\n6933256732000000 && 37.7836851,-122.3881739 && last_edit_user_name -> clay_c || wheelchair -> designated || last_edit_changeset -> 80927299 || last_edit_time -> 1581553753000 || last_edit_user_id -> 119881 || name -> Brannan || public_transport -> stop_position || railway -> stop || tram -> yes || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n5184723650000000 && 37.7793061,-122.3899567 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792195000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n1211579948000000 && 37.7773717,-122.3911686 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || addr:housenumber -> 24 || addr:full -> 24 Willie Mays Plaza || last_edit_time -> 1497148736000 || last_edit_user_id -> 131996 || addr:state -> CA || addr:country -> US || addr:street -> Willie Mays Plaza || last_edit_version -> 3 || addr:city -> San Francisco\n5184723411000000 && 37.780077,-122.3877602 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n5184723637000000 && 37.7780308,-122.3913241 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723398000000 && 37.7793297,-122.3883007 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n3989381754000000 && 37.7778508,-122.3882251 && last_edit_user_name -> Natfoot || last_edit_changeset -> 37013260 || last_edit_time -> 1454651225000 || last_edit_user_id -> 567792 || last_edit_version -> 1 || seamark:type -> pile\n4688840518000000 && 37.7799759,-122.3879103 && last_edit_changeset -> 81752271 || website -> http://www.southbeachyachtclub.org/ || level -> 2 || addr:state -> CA || facebook -> https://www.facebook.com/pages/South-Beach-Yacht-Club/138370479538772 || addr:postcode -> 94107 || addr:city -> San Francisco || last_edit_user_name -> TravGW || addr:housenumber -> 40 || phone -> +1-415-495-2295 || last_edit_time -> 1583291977000 || last_edit_user_id -> 8567408 || club -> sport || name -> South Beach Yacht Club || addr:street -> Pier || last_edit_version -> 2 || sport -> sailing\n5184723366000000 && 37.7803268,-122.3877684 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n4402491124000000 && 37.7816964,-122.3884383 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5184723340000000 && 37.7782437,-122.3878791 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n1425813142000000 && 37.7794502,-122.3905135 && last_edit_user_name -> saikofish || last_edit_changeset -> 10573628 || last_edit_time -> 1328250785000 || last_edit_user_id -> 236172 || amenity -> restaurant || name -> Pete's Tavern || cuisine -> american || last_edit_version -> 2\n1723633953000000 && 37.7884185,-122.3879984 && last_edit_user_name -> clay_c || last_edit_changeset -> 66788285 || last_edit_time -> 1548899849000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 3\n1723633974000000 && 37.7896525,-122.3886357 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197063000 || last_edit_user_id -> 933797 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723353000000 && 37.778499,-122.3877625 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723030000000 && 37.7773857,-122.3914041 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6318866711000000 && 37.77825,-122.391808 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n6392302634000000 && 37.7797802,-122.3900288 && last_edit_user_name -> clay_c || bus -> yes || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473132000 || last_edit_user_id -> 119881 || name -> King Street & 2nd Street || public_transport -> platform || highway -> bus_stop || last_edit_version -> 4 || operator -> San Francisco Municipal Railway || network -> Muni\n6933256734000000 && 37.778659,-122.3911222 && last_edit_user_name -> clay_c || wheelchair -> designated || last_edit_changeset -> 80927299 || last_edit_time -> 1581553753000 || last_edit_user_id -> 119881 || name -> 2nd & King || public_transport -> stop_position || railway -> stop || tram -> yes || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n6394993275000000 && 37.7797091,-122.3899645 && last_edit_user_name -> clay_c || bus -> yes || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473134000 || last_edit_user_id -> 119881 || name -> King Street & 2nd Street || public_transport -> stop_position || last_edit_version -> 2 || operator -> San Francisco Municipal Railway || network -> Muni\n3989381761000000 && 37.7781958,-122.3868276 && last_edit_user_name -> Natfoot || last_edit_changeset -> 37013260 || last_edit_time -> 1454651225000 || last_edit_user_id -> 567792 || last_edit_version -> 1 || seamark:type -> pile\n5184723346000000 && 37.7783498,-122.3879267 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4406691768000000 && 37.7848501,-122.3879953 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183955000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n945302582000000 && 37.7789723,-122.390946 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404095000 || last_edit_user_id -> 8896306 || kerb -> lowered || highway -> crossing || last_edit_version -> 8 || crossing -> traffic_signals\n590986805000000 && 37.778192,-122.3870464 && last_edit_user_name -> clay_c || last_edit_changeset -> 76407142 || last_edit_time -> 1572455062000 || last_edit_user_id -> 119881 || amenity -> ferry_terminal || ferry -> yes || name -> Oracle Park || public_transport -> stop_position || last_edit_version -> 6 || network -> SFBF;GGF\n5184723404000000 && 37.7794461,-122.3884529 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6932316477000000 && 37.7782335,-122.3876468 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || barrier -> gate || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n4406691894000000 && 37.7894991,-122.388378 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197007000 || last_edit_user_id -> 933797 || railway -> crossing || last_edit_version -> 1 || crossing -> traffic_signals\n4406691781000000 && 37.7870888,-122.3879009 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197007000 || last_edit_user_id -> 933797 || railway -> level_crossing || last_edit_version -> 1\n5184723417000000 && 37.778263,-122.3911154 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n1723633891000000 && 37.7772163,-122.3929208 && last_edit_user_name -> MikeN || last_edit_changeset -> 43853092 || last_edit_time -> 1479750886000 || last_edit_user_id -> 135163 || railway -> crossing || last_edit_version -> 2\n5184723049000000 && 37.7773204,-122.3904796 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n317124780000000 && 37.7786039,-122.387656 && last_edit_user_name -> Byung Kyu Park || last_edit_changeset -> 248521 || last_edit_time -> 1228630218000 || last_edit_user_id -> 77582 || amenity -> bicycle_parking || last_edit_version -> 1\n5326946967000000 && 37.7793766,-122.3885851 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || access -> private || barrier -> gate || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || last_edit_version -> 1\n4921384524000000 && 37.7790117,-122.391099 && last_edit_changeset -> 67364675 || website -> https://www.lignerosetsf.com/ || shop -> furniture || addr:postcode -> 94107 || last_edit_user_name -> b-jazz-bot || addr:housenumber -> 162 || phone -> +1-415-777-1030 || last_edit_time -> 1550604479000 || last_edit_user_id -> 9451067 || name -> ligne roset || opening_hours -> Mo-Fr 10:00-18:00 || addr:street -> King Street || last_edit_version -> 2\n5184723624000000 && 37.7780856,-122.3912128 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6329652896000000 && 37.7791228,-122.3908664 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404095000 || last_edit_user_id -> 8896306 || highway -> crossing || last_edit_version -> 2 || crossing -> unmarked\n5184723385000000 && 37.7790493,-122.3880035 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723036000000 && 37.7772072,-122.3909405 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723359000000 && 37.7802448,-122.3878475 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326945391000000 && 37.7898828,-122.3887972 && last_edit_user_name -> clay_c || wheelchair -> no || last_edit_changeset -> 80926684 || last_edit_time -> 1581550313000 || last_edit_user_id -> 119881 || name -> Folsom || public_transport -> stop_position || railway -> tram_stop || tram -> yes || last_edit_version -> 6 || operator -> San Francisco Municipal Railway || network -> Muni\n5184723625000000 && 37.7780758,-122.3912547 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5367527973000000 && 37.7765909,-122.3936872 && last_edit_user_name -> clay_c || last_edit_changeset -> 66788285 || last_edit_time -> 1548899860000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 2\n5184723037000000 && 37.7771799,-122.3909658 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6933256735000000 && 37.7786485,-122.3910421 && last_edit_user_name -> clay_c || wheelchair -> designated || last_edit_changeset -> 80927299 || last_edit_time -> 1581553753000 || last_edit_user_id -> 119881 || name -> 2nd & King || public_transport -> stop_position || railway -> stop || tram -> yes || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n1723633952000000 && 37.7907437,-122.3898743 && last_edit_user_name -> clay_c || last_edit_changeset -> 66816409 || last_edit_time -> 1548972253000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 3\n5326946977000000 && 37.7798499,-122.3893746 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n1425813028000000 && 37.7801929,-122.3913571 && last_edit_changeset -> 13991088 || website -> www.tressf.com || amenity -> restaurant || cuisine -> mexican || addr:postcode -> 94107 || last_edit_user_name -> saikofish || addr:housenumber -> 130 || old_name -> Tres Agave || phone -> +1 415 227 0500 || last_edit_time -> 1353617497000 || last_edit_user_id -> 236172 || name -> Tres || addr:street -> Townsend Street || last_edit_version -> 2\n5184723352000000 && 37.7784293,-122.3877299 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6394993274000000 && 37.7796392,-122.3896413 && last_edit_user_name -> clay_c || bus -> yes || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473134000 || last_edit_user_id -> 119881 || name -> King Street & 2nd Street || public_transport -> stop_position || last_edit_version -> 2 || operator -> San Francisco Municipal Railway || network -> Muni\n5184723651000000 && 37.7793886,-122.3898538 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792195000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723630000000 && 37.7780828,-122.3913543 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n2815191083000000 && 37.7787802,-122.3913315 && last_edit_changeset -> 67364675 || website -> https://www.bravadolounge.com/ || internet_access -> wlan || amenity -> cafe || cuisine -> coffee_shop || addr:postcode -> 94107 || addr:city -> San Francisco || last_edit_user_name -> b-jazz-bot || addr:housenumber -> 170 || phone -> +1 415 957 1100 || last_edit_time -> 1550604480000 || last_edit_user_id -> 9451067 || name -> Bravado || opening_hours -> Mo-Fr 07:00-18:00; Sa-Su 11:00-15:00 || addr:street -> King Street || last_edit_version -> 2\n5184723341000000 && 37.7782623,-122.3878192 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6318851268000000 && 37.7776018,-122.3912145 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5067631375000000 && 37.779164,-122.3911504 && last_edit_changeset -> 51559130 || addr:state -> CA || office -> company || addr:postcode -> 94107 || addr:unit -> 800 || addr:city -> San Francisco || last_edit_user_name -> lmcslssslsssl || addr:housenumber -> 153 || last_edit_time -> 1504047909000 || last_edit_user_id -> 5789664 || name -> Ancestry || addr:street -> Townsend Street || last_edit_version -> 1\n5184723399000000 && 37.7793284,-122.388354 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723048000000 && 37.7772708,-122.3904368 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723631000000 && 37.7780237,-122.3911861 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n358814343000000 && 37.7779716,-122.3888596 && last_edit_user_name -> wvdp || gnis:created -> 07/01/1994 || last_edit_changeset -> 20683290 || man_made -> tower || last_edit_time -> 1392930537000 || last_edit_user_id -> 436419 || name -> KEST-AM (San Francisco) || gnis:feature_id -> 1664960 || gnis:state_id -> 06 || last_edit_version -> 2 || ele -> 1 || gnis:county_id -> 075\n4402491125000000 && 37.7817806,-122.3882427 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 3\n4688893416000000 && 37.7798486,-122.3892128 && last_edit_user_name -> BenoitMenez || last_edit_changeset -> 46138545 || stop -> minor || last_edit_time -> 1487255281000 || last_edit_user_id -> 4518035 || highway -> stop || last_edit_version -> 1 || direction -> backward\n6318851269000000 && 37.7775021,-122.3909952 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || highway -> traffic_signals || last_edit_version -> 2\n5184723384000000 && 37.7789877,-122.388001 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723358000000 && 37.780298,-122.3878486 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723379000000 && 37.7789128,-122.3879546 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6329652624000000 && 37.7779861,-122.3919337 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 1\n6384096460000000 && 37.7798427,-122.3890699 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404094000 || last_edit_user_id -> 8896306 || highway -> crossing || last_edit_version -> 1 || crossing -> unmarked\n1723633942000000 && 37.7887,-122.3880155 && last_edit_user_name -> clay_c || last_edit_changeset -> 66788285 || last_edit_time -> 1548899849000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 3\n3989381760000000 && 37.7781921,-122.3869336 && last_edit_user_name -> Natfoot || last_edit_changeset -> 37013260 || last_edit_time -> 1454651225000 || last_edit_user_id -> 567792 || last_edit_version -> 1 || seamark:type -> pile\n5184723347000000 && 37.7783692,-122.3878786 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326945390000000 && 37.789843,-122.3888634 && last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473127000 || last_edit_user_id -> 119881 || name -> Folsom || light_rail -> yes || public_transport -> stop_position || railway -> stop || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n5184723410000000 && 37.7784112,-122.3876511 && last_edit_user_name -> Jaypep1 || last_edit_changeset -> 65617848 || last_edit_time -> 1545239563000 || last_edit_user_id -> 6591908 || name -> Baseball Sealion || tourism -> artwork || artwork_type -> statue || last_edit_version -> 2\n5326946976000000 && 37.7797876,-122.3897018 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723636000000 && 37.7779928,-122.3913117 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n3989381755000000 && 37.7778974,-122.388097 && last_edit_user_name -> Natfoot || last_edit_changeset -> 37013260 || last_edit_time -> 1454651225000 || last_edit_user_id -> 567792 || last_edit_version -> 1 || seamark:type -> pile\n4406691759000000 && 37.7846283,-122.3880962 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183955000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n4406691780000000 && 37.7870846,-122.3878179 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197006000 || last_edit_user_id -> 933797 || railway -> level_crossing || last_edit_version -> 1\n5184723025000000 && 37.7770086,-122.3916941 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792187000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723348000000 && 37.7783942,-122.3878133 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n3989381759000000 && 37.7781693,-122.3872789 && last_edit_user_name -> Natfoot || last_edit_changeset -> 37013260 || last_edit_time -> 1454651225000 || last_edit_user_id -> 567792 || last_edit_version -> 1 || seamark:type -> pile\n317091394000000 && 37.777623,-122.388473 && last_edit_user_name -> saikofish || last_edit_changeset -> 8159223 || last_edit_time -> 1305526235000 || last_edit_user_id -> 236172 || name -> McCovey Cove || place -> locality || last_edit_version -> 4\n5184723390000000 && 37.779171,-122.3881574 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n3597327597000000 && 37.7777379,-122.391738 && last_edit_changeset -> 67668283 || addr:state -> CA || addr:country -> US || addr:postcode -> 94107 || addr:unit -> 300 || addr:city -> San Francisco || last_edit_user_name -> wpfangirl || addr:housenumber -> 207 || last_edit_time -> 1551397909000 || last_edit_user_id -> 9468345 || name -> Webcor San Francisco Office || addr:street -> King Street || last_edit_version -> 3\n1281059717000000 && 37.7798139,-122.3911499 && last_edit_user_name -> oba510 || last_edit_changeset -> 45184988 || last_edit_time -> 1484467481000 || last_edit_user_id -> 933797 || is_in -> South of Market || name -> South Beach || place -> neighbourhood || last_edit_version -> 5 || name:zh -> 南滩区\n6318851267000000 && 37.7774539,-122.3912347 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n6318866706000000 && 37.7779088,-122.3916059 && last_edit_user_name -> Nodestrawaymus || traffic_signals:direction -> forward || last_edit_changeset -> 67815996 || last_edit_time -> 1551809701000 || last_edit_user_id -> 8899783 || highway -> traffic_signals || last_edit_version -> 2 || traffic_signals:sound -> yes\n3456791114000000 && 37.7815326,-122.38617 && last_edit_user_name -> malcolmh || last_edit_changeset -> 61942337 || last_edit_time -> 1535093811000 || last_edit_user_id -> 128186 || fee -> no || seamark:small_craft_facility:category -> pump-out || last_edit_version -> 4 || seamark:type -> small_craft_facility\n4921394503000000 && 37.7794389,-122.3894387 && last_edit_user_name -> Harry Cutts || last_edit_changeset -> 49625166 || shop -> gift || last_edit_time -> 1497744811000 || last_edit_user_id -> 670691 || name -> Giants Dugout || last_edit_version -> 1\n5326946981000000 && 37.7799515,-122.389499 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n4406691779000000 && 37.7869884,-122.3879084 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197006000 || last_edit_user_id -> 933797 || railway -> crossing || last_edit_version -> 1 || crossing -> traffic_signals\n1616423670000000 && 37.7799172,-122.3900822 && last_edit_changeset -> 67332089 || website -> https://www.sfmomos.com/ || amenity -> restaurant || last_edit_user_name -> b-jazz-bot || addr:housenumber -> 760 || phone -> +1 415 227 8660 || last_edit_time -> 1550528528000 || last_edit_user_id -> 9451067 || name -> Momo's || addr:street -> 2nd Street || fax -> +1 415 227 8693 || last_edit_version -> 2 || email -> info@sfmomos.com\n5326947007000000 && 37.7796077,-122.3898193 && last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473127000 || last_edit_user_id -> 119881 || name -> 2nd & King || light_rail -> yes || public_transport -> stop_position || railway -> stop || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n1723633898000000 && 37.7790233,-122.3908717 && last_edit_user_name -> sdelgerbayar || traffic_signals:direction -> forward || last_edit_changeset -> 68895482 || last_edit_time -> 1554404095000 || last_edit_user_id -> 8896306 || highway -> traffic_signals || last_edit_version -> 5\n5184723361000000 && 37.7802946,-122.3879454 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723642000000 && 37.7784725,-122.3910103 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723403000000 && 37.779416,-122.3884624 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723383000000 && 37.7788806,-122.3879981 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n3989381756000000 && 37.7780331,-122.3877436 && last_edit_user_name -> Natfoot || last_edit_changeset -> 37013260 || last_edit_time -> 1454651225000 || last_edit_user_id -> 567792 || last_edit_version -> 1 || seamark:type -> pile\n5184723635000000 && 37.778027,-122.3912849 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6318866712000000 && 37.7780674,-122.3915774 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723351000000 && 37.7784576,-122.3877957 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723632000000 && 37.7779646,-122.3912032 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n1723634009000000 && 37.7896011,-122.3884679 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197063000 || last_edit_user_id -> 933797 || railway -> level_crossing || last_edit_version -> 2\n5184723393000000 && 37.7792106,-122.3882034 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4688893415000000 && 37.7795971,-122.3885617 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || barrier -> lift_gate || motor_vehicle -> yes || last_edit_time -> 1554404095000 || last_edit_user_id -> 8896306 || last_edit_version -> 2\n5184723645000000 && 37.7788938,-122.390479 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723380000000 && 37.7789721,-122.3879579 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6329652623000000 && 37.7780138,-122.3919656 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249935000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 1\n7108550922000000 && 37.777197,-122.3908376 && last_edit_user_name -> oba510 || last_edit_changeset -> 79205628 || barrier -> kerb || last_edit_time -> 1578202377000 || last_edit_user_id -> 933797 || kerb -> lowered || last_edit_version -> 1\n6384096459000000 && 37.7793975,-122.3886019 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404094000 || last_edit_user_id -> 8896306 || highway -> crossing || last_edit_version -> 1 || crossing -> unmarked\n301505501000000 && 37.7793466,-122.3902045 && bus -> yes || wheelchair -> yes || last_edit_changeset -> 71918252 || shelter -> yes || amenity -> bus_station || light_rail -> yes || public_transport -> station || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> Timothy Smith || last_edit_time -> 1562305953000 || last_edit_user_id -> 115918 || name -> 2nd & King || station -> light_rail || railway -> station || tram -> yes || last_edit_version -> 17 || wikidata -> Q4633972 || route_ref -> N;T\n5326947006000000 && 37.7790503,-122.3906402 && last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473127000 || last_edit_user_id -> 119881 || name -> 2nd & King || light_rail -> yes || public_transport -> stop_position || railway -> stop || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n6360896161000000 && 37.7770583,-122.3910135 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || barrier -> gate || last_edit_time -> 1553538168000 || last_edit_user_id -> 381909 || last_edit_version -> 1\n4402491123000000 && 37.7816846,-122.3883295 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n1723633989000000 && 37.7905838,-122.3896751 && last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473124000 || last_edit_user_id -> 119881 || name -> Folsom || light_rail -> yes || public_transport -> stop_position || railway -> stop || last_edit_version -> 5 || operator -> San Francisco Municipal Railway || network -> Muni\n5184723360000000 && 37.7802426,-122.3879358 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723381000000 && 37.7790322,-122.3879649 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723402000000 && 37.779403,-122.3884004 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6384096458000000 && 37.7796106,-122.3885449 && last_edit_user_name -> sdelgerbayar || last_edit_changeset -> 68895482 || last_edit_time -> 1554404094000 || last_edit_user_id -> 8896306 || traffic_calming -> bump || last_edit_version -> 1 || direction -> backward\n7228926464000000 && 37.7796713,-122.3915808 && last_edit_user_name -> clay_c || last_edit_changeset -> 81240934 || last_edit_time -> 1582149447000 || last_edit_user_id -> 119881 || highway -> crossing || last_edit_version -> 1\n5184723633000000 && 37.7780286,-122.391241 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723349000000 && 37.7784115,-122.3879026 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723370000000 && 37.7803007,-122.3879 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || amenity -> drinking_water || last_edit_version -> 1\n5184723391000000 && 37.7791311,-122.3881028 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n310688988000000 && 37.7945115,-122.2801913 && last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 76356726 || last_edit_time -> 1572368313000 || last_edit_user_id -> 119881 || amenity -> ferry_terminal || ferry -> yes || name -> Oakland Jack London Square Terminal || public_transport -> stop_position || last_edit_version -> 13 || operator -> Water Emergency Transportation Authority || network -> SFBF\n5184723412000000 && 37.7800799,-122.3877302 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n3989381758000000 && 37.7781423,-122.3874519 && last_edit_user_name -> Natfoot || last_edit_changeset -> 37013260 || last_edit_time -> 1454651225000 || last_edit_user_id -> 567792 || last_edit_version -> 1 || seamark:type -> pile\n5326946969000000 && 37.7795582,-122.3888042 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 1 || crossing -> uncontrolled\n5184723643000000 && 37.7785549,-122.3909032 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723622000000 && 37.7781311,-122.3910934 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326946974000000 && 37.779744,-122.389647 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723034000000 && 37.7773531,-122.3912558 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6318866707000000 && 37.7778567,-122.3918465 && last_edit_user_name -> Nodestrawaymus || last_edit_changeset -> 67815996 || last_edit_time -> 1551809701000 || last_edit_user_id -> 8899783 || highway -> traffic_signals || last_edit_version -> 2 || traffic_signals:sound -> yes\n4406691778000000 && 37.7869836,-122.3878256 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197006000 || last_edit_user_id -> 933797 || railway -> crossing || last_edit_version -> 1 || crossing -> traffic_signals\n6318866713000000 && 37.7779356,-122.3916402 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723350000000 && 37.7784749,-122.3878459 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723392000000 && 37.7790824,-122.388042 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723413000000 && 37.779998,-122.3877218 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n3604311619000000 && 37.779942,-122.3917091 && last_edit_user_name -> pluton_od || last_edit_changeset -> 32065891 || last_edit_time -> 1434688406000 || last_edit_user_id -> 2411240 || name -> Realm || office -> company || last_edit_version -> 1\n3989381757000000 && 37.7780877,-122.3876041 && last_edit_user_name -> Natfoot || last_edit_changeset -> 37013260 || last_edit_time -> 1454651225000 || last_edit_user_id -> 567792 || last_edit_version -> 1 || seamark:type -> pile\n317077257000000 && 37.7776808,-122.3892315 && last_edit_user_name -> mpmckenna8 || last_edit_changeset -> 74547882 || last_edit_time -> 1568661998000 || last_edit_user_id -> 1213904 || amenity -> bicycle_parking || bicycle_parking -> valet || name -> Oracle Park valet bicycle parking || description -> open only during baseball games and some events. Provided by the San Francisco Bicycle Coalition. || last_edit_version -> 4\n5184723623000000 && 37.7781105,-122.3911462 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723644000000 && 37.7786461,-122.3908016 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n1723633924000000 && 37.7895545,-122.3885337 && last_edit_user_name -> oba510 || last_edit_changeset -> 42244754 || last_edit_time -> 1474197063000 || last_edit_user_id -> 933797 || railway -> level_crossing || last_edit_version -> 2\n1723633782000000 && 37.7781624,-122.3916974 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249943000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2\n2240839584000000 && 37.7795923,-122.3915835 && last_edit_user_name -> Martin Atkins || last_edit_changeset -> 15562458 || last_edit_time -> 1364792790000 || last_edit_user_id -> 6585 || amenity -> fast_food || name -> The Bagel Bakery || cuisine -> bagel || last_edit_version -> 1\n5184723035000000 && 37.7772345,-122.3909709 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n945304393000000 && 37.7789317,-122.3907858 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183952000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 4\n5184723024000000 && 37.7769487,-122.3917725 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792187000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6318851266000000 && 37.7774668,-122.3910971 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n2613221266000000 && 37.7712137,-122.3861929 && last_edit_user_name -> jambamkin || last_edit_changeset -> 70995561 || last_edit_time -> 1559837489000 || last_edit_user_id -> 53626 || last_edit_version -> 2 || leisure -> slipway\n4921394502000000 && 37.7793986,-122.3895413 && last_edit_user_name -> Harry Cutts || last_edit_changeset -> 49625166 || cash_in -> no || last_edit_time -> 1497744811000 || last_edit_user_id -> 670691 || amenity -> atm || drive_through -> no || description -> Cash withdrawals only. || last_edit_version -> 1 || brand -> Bank of America\n4908344426000000 && 37.7780358,-122.3913827 && last_edit_user_name -> nvk || last_edit_changeset -> 49432648 || last_edit_time -> 1497148733000 || last_edit_user_id -> 131996 || tourism -> artwork || artwork_type -> statue || last_edit_version -> 1\n5184723369000000 && 37.7801902,-122.3877721 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n1723633759000000 && 37.7764812,-122.3938215 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183953000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 3\n5184723356000000 && 37.7785447,-122.3877304 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723033000000 && 37.7773131,-122.391303 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n1270672016000000 && 37.780545,-122.3906366 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 4 || crossing -> traffic_signals\n6933256731000000 && 37.7899008,-122.3889368 && last_edit_user_name -> clay_c || wheelchair -> designated || last_edit_changeset -> 80927299 || last_edit_time -> 1581553753000 || last_edit_user_id -> 119881 || name -> Folsom || public_transport -> stop_position || railway -> stop || tram -> yes || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n6384096457000000 && 37.7800271,-122.389801 && last_edit_user_name -> sdelgerbayar || traffic_signals:direction -> forward || last_edit_changeset -> 68895482 || last_edit_time -> 1554404094000 || last_edit_user_id -> 8896306 || highway -> traffic_signals || last_edit_version -> 1\n5184723647000000 && 37.7790544,-122.3902696 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792195000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326946973000000 && 37.7796896,-122.3895786 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183958000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n6391300469000000 && 37.7813624,-122.3925411 && last_edit_user_name -> clay_c || last_edit_changeset -> 81335413 || last_edit_time -> 1582326678000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> uncontrolled\n6391323282000000 && 37.7813423,-122.3925173 && last_edit_user_name -> Fikadu Balcha || last_edit_changeset -> 69012379 || stop -> minor || last_edit_time -> 1554738575000 || last_edit_user_id -> 9051248 || traffic_sign -> stop || highway -> stop || last_edit_version -> 1\n5184723621000000 && 37.7781214,-122.391289 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723382000000 && 37.7789449,-122.3879998 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n590986802000000 && 37.778258,-122.3875849 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || barrier -> gate || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 4\n5184723634000000 && 37.7779781,-122.3912527 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723395000000 && 37.7792443,-122.388244 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326945384000000 && 37.7836538,-122.388091 && last_edit_user_name -> clay_c || wheelchair -> no || last_edit_changeset -> 80926684 || last_edit_time -> 1581550313000 || last_edit_user_id -> 119881 || name -> Brannan || public_transport -> stop_position || railway -> tram_stop || tram -> yes || last_edit_version -> 7 || operator -> San Francisco Municipal Railway || network -> Muni\n6252543480000000 && 37.7780999,-122.3918474 && last_edit_user_name -> clay_c || last_edit_changeset -> 66845961 || last_edit_time -> 1549059988000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 1\n5184723627000000 && 37.7780633,-122.3913076 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723388000000 && 37.7791613,-122.3880901 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n317124783000000 && 37.7797015,-122.3877718 && last_edit_user_name -> Byung Kyu Park || last_edit_changeset -> 248521 || last_edit_time -> 1228630219000 || last_edit_user_id -> 77582 || amenity -> bicycle_parking || last_edit_version -> 1\n5184723362000000 && 37.7802925,-122.3880374 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n358763532000000 && 37.7771799,-122.3866825 && last_edit_user_name -> oba510 || gnis:created -> 01/19/1981 || last_edit_changeset -> 17776817 || natural -> bay || last_edit_time -> 1378872680000 || last_edit_user_id -> 933797 || name -> China Basin || gnis:feature_id -> 221012 || gnis:state_id -> 06 || last_edit_version -> 2 || ele -> -1 || gnis:county_id -> 075\n945302892000000 && 37.7788578,-122.3905961 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183952000 || last_edit_user_id -> 119881 || highway -> crossing || last_edit_version -> 6 || crossing -> traffic_signals\n6318851265000000 && 37.7775245,-122.3910247 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249946000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723052000000 && 37.7774688,-122.3906407 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723375000000 && 37.7803752,-122.3878186 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326946983000000 && 37.7800076,-122.3897768 && last_edit_user_name -> clay_c || last_edit_changeset -> 81231251 || last_edit_time -> 1582130726000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 4 || crossing -> traffic_signals\n5184723653000000 && 37.7795496,-122.3896544 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792195000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723414000000 && 37.7799938,-122.3877523 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n5184723343000000 && 37.778335,-122.3877818 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723640000000 && 37.7779537,-122.3913241 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4406691700000000 && 37.7834234,-122.3881949 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183955000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2\n5184723401000000 && 37.7793663,-122.3883979 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n2240869184000000 && 37.7778936,-122.3910462 && last_edit_user_name -> b-jazz-bot || wheelchair -> yes || last_edit_changeset -> 80264427 || website -> https://www.publichousesf.com/ || last_edit_time -> 1580309360000 || last_edit_user_id -> 9451067 || amenity -> bar || name -> Public House || last_edit_version -> 4\n5326946980000000 && 37.7799037,-122.3894405 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n6932316480000000 && 37.7782457,-122.3876158 && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || barrier -> gate || last_edit_time -> 1583873701000 || last_edit_user_id -> 119881 || last_edit_version -> 2\n5184723394000000 && 37.7792469,-122.3881993 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723415000000 && 37.7797847,-122.3877018 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n1723633855000000 && 37.776527,-122.3938762 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183953000 || last_edit_user_id -> 119881 || railway -> switch || last_edit_version -> 3\n6252543478000000 && 37.7798182,-122.3895497 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249945000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 2\n5184723646000000 && 37.7789679,-122.3903837 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n272166386000000 && 37.7772481,-122.3929574 && last_edit_user_name -> MikeN || last_edit_changeset -> 43853092 || last_edit_time -> 1479750886000 || last_edit_user_id -> 135163 || railway -> crossing || last_edit_version -> 29\n5184723641000000 && 37.7779298,-122.391287 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n4402491128000000 && 37.7819181,-122.3882326 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723026000000 && 37.777048,-122.391646 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792187000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6252543481000000 && 37.7780738,-122.3918146 && last_edit_user_name -> clay_c || last_edit_changeset -> 66845961 || last_edit_time -> 1549059988000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 1\n6360896163000000 && 37.7757405,-122.3926776 && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || barrier -> gate || last_edit_time -> 1553538168000 || last_edit_user_id -> 381909 || last_edit_version -> 1\n5184723357000000 && 37.7786555,-122.387627 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || entrance -> yes || last_edit_version -> 1\n5184723420000000 && 37.7781512,-122.3912575 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792194000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n6933256730000000 && 37.7899642,-122.3888993 && last_edit_user_name -> clay_c || wheelchair -> designated || last_edit_changeset -> 80927299 || last_edit_time -> 1581553753000 || last_edit_user_id -> 119881 || name -> Folsom || public_transport -> stop_position || railway -> stop || tram -> yes || last_edit_version -> 3 || operator -> San Francisco Municipal Railway || network -> Muni\n5326946982000000 && 37.7800065,-122.3895663 && last_edit_user_name -> clay_c || last_edit_changeset -> 68000611 || last_edit_time -> 1552249944000 || last_edit_user_id -> 119881 || kerb -> lowered || highway -> crossing || last_edit_version -> 3 || crossing -> traffic_signals\n5184723027000000 && 37.7770866,-122.3915913 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792187000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723652000000 && 37.779471,-122.3897551 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792195000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n2613221255000000 && 37.771669,-122.3863895 && last_edit_user_name -> jambamkin || last_edit_changeset -> 70929357 || last_edit_time -> 1559665845000 || last_edit_user_id -> 53626 || last_edit_version -> 2 || leisure -> slipway\n5184723342000000 && 37.7782855,-122.3877625 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792191000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723363000000 && 37.7802359,-122.3880273 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n260193242000000 && 37.77967,-122.3896028 && last_edit_user_name -> Heidi_Baum_Lyft || last_edit_changeset -> 67703843 || last_edit_time -> 1551485903000 || last_edit_user_id -> 8778516 || turn_restrictions -> no || highway -> traffic_signals || last_edit_version -> 23 || traffic_signals:sound -> yes || direction -> forward\n4406691764000000 && 37.7847418,-122.388088 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183955000 || last_edit_user_id -> 119881 || railway -> level_crossing || last_edit_version -> 2\n5184723400000000 && 37.779365,-122.388352 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n65317058000000 && 37.7806182,-122.3905436 && last_edit_user_name -> DannyAiquipa || last_edit_changeset -> 37125422 || last_edit_time -> 1455113685000 || last_edit_user_id -> 2226712 || turn_restrictions -> no || highway -> traffic_signals || last_edit_version -> 13\n1723634000000000 && 37.7788875,-122.3907309 && last_edit_user_name -> clay_c || last_edit_changeset -> 67976688 || last_edit_time -> 1552183953000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 3\n5326945385000000 && 37.7844502,-122.3880303 && last_edit_user_name -> clay_c || wheelchair -> yes || last_edit_changeset -> 76416769 || last_edit_time -> 1572473127000 || last_edit_user_id -> 119881 || name -> Brannan || light_rail -> yes || public_transport -> stop_position || railway -> stop || last_edit_version -> 4 || operator -> San Francisco Municipal Railway || network -> Muni\n4402491122000000 && 37.7816761,-122.3882506 && last_edit_user_name -> clay_c || last_edit_changeset -> 80974351 || last_edit_time -> 1581625278000 || last_edit_user_id -> 119881 || railway -> crossing || last_edit_version -> 2 || crossing -> traffic_signals\n5184723368000000 && 37.7802097,-122.3877454 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || last_edit_time -> 1508792192000 || last_edit_user_id -> 53073 || amenity -> bench || last_edit_version -> 1\n5184723389000000 && 37.7792054,-122.3881504 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792193000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5326946971000000 && 37.7795705,-122.3885931 && last_edit_user_name -> oba510 || last_edit_changeset -> 55257437 || last_edit_time -> 1515399486000 || last_edit_user_id -> 933797 || highway -> crossing || last_edit_version -> 1 || crossing -> uncontrolled\n6318866705000000 && 37.7782779,-122.3917759 && last_edit_user_name -> Nodestrawaymus || last_edit_changeset -> 67815996 || last_edit_time -> 1551809701000 || last_edit_user_id -> 8899783 || highway -> traffic_signals || last_edit_version -> 2 || traffic_signals:sound -> yes\n5184723032000000 && 37.7772785,-122.3913519 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n5184723053000000 && 37.7775009,-122.390705 && last_edit_user_name -> Aaron Lidman || last_edit_changeset -> 53191252 || natural -> tree || last_edit_time -> 1508792188000 || last_edit_user_id -> 53073 || last_edit_version -> 1\n# Relations\n32477000000 && -681699527000000 ->  -> E || -674722764000000 ->  -> E || -397097608000003 ->  -> E || -397097608000002 ->  -> E || -397097608000001 ->  -> E || -224384059000002 ->  -> E || -224384059000001 ->  -> E || 224384059000001 ->  -> E || 224384059000002 ->  -> E || 397097608000001 ->  -> E || 397097608000002 ->  -> E || 397097608000003 ->  -> E || 674722764000000 ->  -> E || 681699527000000 ->  -> E && last_edit_user_name -> clay_c || last_edit_changeset -> 81286290 || ref -> 11 || route -> bicycle || cycle_network -> US:CA:SF || last_edit_time -> 1582222824000 || last_edit_user_id -> 119881 || type -> route || last_edit_version -> 79 || network -> lcn\n63223000000 && 189011905000000 -> platform -> A || 396435906000000 ->  -> L || 5326945383000000 -> stop -> P || 5326945390000000 -> stop -> P || 5326947006000000 -> stop -> P && payment:clipper -> yes || wheelchair -> limited || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || bicycle -> no || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> #00539b || ref -> N || route -> light_rail:suspended || public_transport:version -> 2 || passenger -> urban || last_edit_time -> 1586197827000 || last_edit_user_id -> 119881 || name -> Muni Metro N inbound: Ocean Beach => Caltrain || from -> Judah Street & La Playa Street || to -> King Street & 4th Street || last_edit_version -> 84 || wikidata -> Q6956302\n63250000000 && 189011905000000 -> platform -> A || 396435906000000 ->  -> L || 5326945383000000 -> stop -> P || 5326945390000000 -> stop -> P || 5326947006000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> #d31245 || ref -> T || route -> light_rail:suspended || public_transport:version -> 2 || passenger -> urban || last_edit_time -> 1586197827000 || last_edit_user_id -> 119881 || name -> Muni Metro T inbound: West Portal => Sunnydale || from -> West Portal || to -> Sunnydale || last_edit_version -> 84 || wikidata -> Q1755582\n109668000000 && 27041554000001 -> forward -> E || 27041554000002 -> forward -> E || 27041554000003 -> forward -> E || 27041554000004 -> forward -> E || 396945237000001 -> forward -> E || 396945237000002 -> forward -> E || 397270776000001 -> forward -> E || 397270776000002 -> forward -> E || 397270779000001 -> forward -> E || 397270779000002 -> forward -> E || 397270779000003 -> forward -> E && last_edit_user_name -> GITNE || last_edit_changeset -> 83719877 || route -> road || last_edit_time -> 1587145247000 || last_edit_user_id -> 1836535 || name -> 49 Mile Scenic Drive || wikipedia -> en:49-Mile Scenic Drive || type -> route || last_edit_version -> 455\n325779000000 && -162324921000007 ->  -> E || -162324921000006 ->  -> E || -162324921000005 ->  -> E || -162324921000004 ->  -> E || -162324921000003 ->  -> E || -162324921000002 ->  -> E || -162324921000001 ->  -> E || -28841478000006 ->  -> E || -28841478000005 ->  -> E || -28841478000004 ->  -> E || -28841478000003 ->  -> E || -28841478000002 ->  -> E || -28841478000001 ->  -> E || 28841478000001 ->  -> E || 28841478000002 ->  -> E || 28841478000003 ->  -> E || 28841478000004 ->  -> E || 28841478000005 ->  -> E || 28841478000006 ->  -> E || 162324921000001 ->  -> E || 162324921000002 ->  -> E || 162324921000003 ->  -> E || 162324921000004 ->  -> E || 162324921000005 ->  -> E || 162324921000006 ->  -> E || 162324921000007 ->  -> E && last_edit_user_name -> WileyBob || last_edit_changeset -> 88270217 || alt_name -> Bay Trail || ref -> SFB || route -> bicycle || cycle_network -> US:CA || last_edit_time -> 1595296437000 || last_edit_user_id -> 11518920 || name -> San Francisco Bay Trail || type -> route || last_edit_version -> 205 || network -> rcn\n1539483000000 && -681699528000002 ->  -> E || -681699528000001 ->  -> E || -111550911000003 ->  -> E || -111550911000002 ->  -> E || -111550911000001 ->  -> E || 111550911000001 ->  -> E || 111550911000002 ->  -> E || 111550911000003 ->  -> E || 681699528000001 ->  -> E || 681699528000002 ->  -> E && last_edit_user_name -> Grist || last_edit_changeset -> 81901389 || ref -> 36 || route -> bicycle || cycle_network -> US:CA:SF || last_edit_time -> 1583593188000 || last_edit_user_id -> 10416749 || type -> route || last_edit_version -> 39 || network -> lcn\n2981333000000 && -681699528000002 ->  -> E || -681699528000001 ->  -> E || -111550911000003 ->  -> E || -111550911000002 ->  -> E || -111550911000001 ->  -> E || 111550911000001 ->  -> E || 111550911000002 ->  -> E || 111550911000003 ->  -> E || 681699528000001 ->  -> E || 681699528000002 ->  -> E && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> 10 || route -> bus:suspended || public_transport:version -> 2 || last_edit_time -> 1586197827000 || last_edit_user_id -> 119881 || name -> Muni 10 inbound: SF General Hospital => Pacific Heights (weekday daytime) || from -> 24th Street & Potrero Avenue || to -> Jackson Street & Fillmore Street || last_edit_version -> 81\n2981334000000 && -681699528000002 ->  -> E || -681699528000001 ->  -> E || -111550911000003 ->  -> E || -111550911000002 ->  -> E || -111550911000001 ->  -> E || 111550911000001 ->  -> E || 111550911000002 ->  -> E || 111550911000003 ->  -> E || 681699528000001 ->  -> E || 681699528000002 ->  -> E && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> 10 || route -> bus:suspended || public_transport:version -> 2 || last_edit_time -> 1586197827000 || last_edit_user_id -> 119881 || name -> Muni 10 outbound: Pacific Heights => SF General Hospital (weekday daytime) || from -> Jackson Street & Fillmore Street || to -> 24th Street & Potrero Avenue || last_edit_version -> 82\n3419225000000 && -27656675000003 ->  -> E || -27656675000002 ->  -> E || -27656675000001 ->  -> E || 27656675000001 ->  -> E || 27656675000002 ->  -> E || 27656675000003 ->  -> E || 396945237000001 ->  -> E || 396945237000002 ->  -> E && payment:clipper -> yes || last_edit_changeset -> 86567868 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> KindredCoda || ref -> 91 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1591965791000 || last_edit_user_id -> 14293 || name -> Muni 91-Owl inbound: West Portal => SFSU (late nights) || from -> Ulloa Street & West Portal Avenue || to -> 19th Avenue & Holloway Avenue || last_edit_version -> 208\n3419232000000 && 8920394000001 ->  -> E || 8920394000002 ->  -> E || 396945236000001 ->  -> E || 396945236000002 ->  -> E || 397270773000001 ->  -> E || 397270773000002 ->  -> E || 397270782000000 ->  -> E || 468012245000001 ->  -> E || 468012245000002 ->  -> E || 681699525000000 ->  -> E || 681699526000001 ->  -> E || 681699526000002 ->  -> E || 6392302634000000 -> platform -> P || 6394993275000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> N-Owl || route -> bus:suspended || public_transport:version -> 2 || last_edit_time -> 1586197833000 || last_edit_user_id -> 119881 || name -> Muni N-Owl inbound: Ocean Beach => Caltrain (late nights) || from -> Judah Street & 48th Avenue || to -> Townsend Street & 5th Street || last_edit_version -> 119\n3419233000000 && 27041554000001 ->  -> E || 27041554000002 ->  -> E || 27041554000003 ->  -> E || 27041554000004 ->  -> E || 396945237000001 ->  -> E || 396945237000002 ->  -> E || 397270776000001 ->  -> E || 397270776000002 ->  -> E || 397270779000001 ->  -> E || 397270779000002 ->  -> E || 397270779000003 ->  -> E || 6392302633000000 -> platform -> P || 6394993274000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> N-Owl || route -> bus:suspended || public_transport:version -> 2 || last_edit_time -> 1586197833000 || last_edit_user_id -> 119881 || name -> Muni N-Owl outbound: Caltrain => Ocean Beach (late nights) || from -> Townsend Street & 5th Street || to -> Judah Street & La Playa Street || last_edit_version -> 114\n3419234000000 && 8920394000001 ->  -> E || 8920394000002 ->  -> E || 396945236000001 ->  -> E || 396945236000002 ->  -> E || 397270773000001 ->  -> E || 397270773000002 ->  -> E || 397270782000000 ->  -> E || 468012245000001 ->  -> E || 468012245000002 ->  -> E || 681699525000000 ->  -> E || 681699526000001 ->  -> E || 681699526000002 ->  -> E || 6392302634000000 -> platform -> P || 6394993275000000 -> stop -> P && payment:clipper -> yes || note -> all-day service during shelter-in-place order; otherwise weekend early mornings only || last_edit_changeset -> 83384853 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> KTBU || route -> bus || public_transport:version -> 2 || last_edit_time -> 1586560582000 || last_edit_user_id -> 119881 || name -> Muni T-Bus inbound: Castro => Sunnydale (weekend early mornings) || from -> Market Street & Castro Street || to -> Bayshore Boulevard & Sunnydale Avenue || last_edit_version -> 92\n3419235000000 && 27041554000001 ->  -> E || 27041554000002 ->  -> E || 27041554000003 ->  -> E || 27041554000004 ->  -> E || 396945237000001 ->  -> E || 396945237000002 ->  -> E || 397270776000001 ->  -> E || 397270776000002 ->  -> E || 397270779000001 ->  -> E || 397270779000002 ->  -> E || 397270779000003 ->  -> E || 6392302633000000 -> platform -> P || 6394993274000000 -> stop -> P && payment:clipper -> yes || note -> all-day service during shelter-in-place order; otherwise weekend early mornings only || last_edit_changeset -> 84910862 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> claytonvo || ref -> KTBU || route -> bus || public_transport:version -> 2 || last_edit_time -> 1588962842000 || last_edit_user_id -> 10313602 || name -> Muni T-Bus outbound: Sunnydale => Castro (weekend early mornings) || from -> Bayshore Boulevard & Sunnydale Avenue || to -> Market Street & Castro Street || last_edit_version -> 96\n3433316000000 && 189011905000000 -> platform -> A || 396435909000000 ->  -> L || 1723633989000000 -> stop -> P || 5326945385000000 -> stop -> P || 5326947007000000 -> stop -> P && payment:clipper -> yes || note -> Continues as K-Ingleside after Embarcadero || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> #d31245 || ref -> T || route -> light_rail:suspended || public_transport:version -> 2 || passenger -> urban || last_edit_time -> 1586197837000 || last_edit_user_id -> 119881 || name -> Muni Metro T outbound: Sunnydale => Embarcadero || from -> Sunnydale || to -> Balboa Park || last_edit_version -> 29 || wikidata -> Q1755582\n3435877000000 && 189011905000000 -> platform -> A || 396435909000000 ->  -> L || 1723633989000000 -> stop -> P || 5326945385000000 -> stop -> P || 5326947007000000 -> stop -> P && payment:clipper -> yes || wheelchair -> limited || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || bicycle -> no || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> #00539b || ref -> N || route -> light_rail:suspended || public_transport:version -> 2 || passenger -> urban || last_edit_time -> 1586197837000 || last_edit_user_id -> 119881 || name -> Muni Metro N outbound: Caltrain => Ocean Beach || from -> King Street & 4th Street || to -> Judah Street & La Playa Street || last_edit_version -> 31 || wikidata -> Q6956302\n3830962000000 && 288649074000000 -> inner -> A && last_edit_user_name -> JessAk71 || last_edit_changeset -> 68517799 || last_edit_time -> 1553538172000 || last_edit_user_id -> 381909 || last_edit_version -> 2 || building -> yes\n5953000000000 && 551597307000000 -> platform -> A || 675498957000000 -> platform -> A || 396435909000000 ->  -> L || 5326945384000000 -> stop -> P || 5326945391000000 -> stop -> P || 5326947005000000 -> stop -> P || 6933256730000000 -> stop -> P || 6933256733000000 -> stop -> P || 6933256735000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> #1E9999 || ref -> E || route -> tram:suspended || public_transport:version -> 2 || passenger -> urban || last_edit_time -> 1586197837000 || last_edit_user_id -> 119881 || name -> Muni Streetcar E inbound: Caltrain => Fisherman's Wharf || from -> King Street & 4th Street || to -> Jones Street & Beach Street || last_edit_version -> 22 || wikidata -> Q5324790\n5953001000000 && 551597308000000 -> platform -> A || 675498960000000 -> platform -> A || 396435906000000 ->  -> L || 5326945382000000 -> stop -> P || 5326945389000000 -> stop -> P || 5326947004000000 -> stop -> P || 6933256731000000 -> stop -> P || 6933256732000000 -> stop -> P || 6933256734000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> #1E9999 || ref -> E || route -> tram:suspended || public_transport:version -> 2 || passenger -> urban || last_edit_time -> 1586197837000 || last_edit_user_id -> 119881 || name -> Muni Streetcar E outbound: Fisherman's Wharf => Caltrain || from -> Jones Street & Beach Street || to -> King Street & 4th Street || last_edit_version -> 19 || wikidata -> Q5324790\n6071916000000 && 189011905000000 -> platform -> A || 396435906000000 ->  -> L || 5326945383000000 -> stop -> P || 5326945390000000 -> stop -> P || 5326947006000000 -> stop -> P && payment:clipper -> yes || wheelchair -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || bicycle -> no || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> yellow || ref -> S || route -> light_rail:suspended || public_transport:version -> 2 || passenger -> urban || last_edit_time -> 1586197837000 || last_edit_user_id -> 119881 || name -> Muni Metro S inbound: West Portal => Caltrain || from -> West Portal || to -> King Street & 4th Street || last_edit_version -> 24 || wikidata -> Q7395342\n6071917000000 && 189011905000000 -> platform -> A || 396435909000000 ->  -> L || 1723633989000000 -> stop -> P || 5326945385000000 -> stop -> P || 5326947007000000 -> stop -> P && payment:clipper -> yes || wheelchair -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || bicycle -> no || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> yellow || ref -> S || route -> light_rail:suspended || public_transport:version -> 2 || passenger -> urban || last_edit_time -> 1586197837000 || last_edit_user_id -> 119881 || name -> Muni Metro S outbound: Caltrain => West Portal || from -> King Street & 4th Street || to -> West Portal || last_edit_version -> 21 || wikidata -> Q7395342\n6172798000000 && 65300043000000 -> via -> N || 280398660000001 -> to -> E || 280398660000002 -> to -> E || 674496103000001 -> from -> E || 674496103000002 -> from -> E && last_edit_user_name -> cdruck || last_edit_changeset -> 67775371 || last_edit_time -> 1551720940000 || last_edit_user_id -> 8762431 || restriction -> no_u_turn || type -> restriction || last_edit_version -> 2\n6237244000000 && -162324921000007 ->  -> E || -162324921000006 ->  -> E || -162324921000005 ->  -> E || -162324921000004 ->  -> E || -162324921000003 ->  -> E || -162324921000002 ->  -> E || -162324921000001 ->  -> E || -28841478000006 ->  -> E || -28841478000005 ->  -> E || -28841478000004 ->  -> E || -28841478000003 ->  -> E || -28841478000002 ->  -> E || -28841478000001 ->  -> E || 28841478000001 ->  -> E || 28841478000002 ->  -> E || 28841478000003 ->  -> E || 28841478000004 ->  -> E || 28841478000005 ->  -> E || 28841478000006 ->  -> E || 162324921000001 ->  -> E || 162324921000002 ->  -> E || 162324921000003 ->  -> E || 162324921000004 ->  -> E || 162324921000005 ->  -> E || 162324921000006 ->  -> E || 162324921000007 ->  -> E && last_edit_user_name -> dchiles || last_edit_changeset -> 86799096 || route -> hiking || last_edit_time -> 1592452639000 || last_edit_user_id -> 153669 || name -> Bay Trail || type -> route || last_edit_version -> 45 || network -> rwn\n7325084000000 && 499568643000000 ->  -> A && last_edit_user_name -> nvk || last_edit_changeset -> 49514712 || last_edit_time -> 1497397063000 || last_edit_user_id -> 131996 || name -> AT&T Park || type -> building || last_edit_version -> 8\n7325085000000 && 4786909000000 -> outer -> A || 98224507000000 -> inner -> A && last_edit_changeset -> 68148761 || website -> https://www.mlb.com/giants/ballpark || type -> multipolygon || addr:postcode -> 94107 || last_edit_user_name -> Amisha Singla || name:ko -> Oracle 파크 || addr:housenumber -> 24 || last_edit_time -> 1552591608000 || last_edit_user_id -> 3460649 || building:part -> public || name -> Oracle Park || name:en -> Oracle Park || addr:street -> Willie Mays Plaza || wikipedia -> en:Oracle Park || last_edit_version -> 10 || leisure -> stadium || sport -> baseball || wikidata -> Q298585 || height -> 45\n7330762000000 && 24352572000000 -> outer -> A || 98224507000000 -> inner -> A || 499568651000000 -> inner -> A && last_edit_user_name -> nvk || last_edit_changeset -> 49517183 || last_edit_time -> 1497414659000 || last_edit_user_id -> 131996 || type -> multipolygon || last_edit_version -> 4 || leisure -> stadium || sport -> baseball || building -> public || height -> 30\n7332929000000 && 500533911000000 -> inner -> A || 500533912000000 -> outer -> A && last_edit_user_name -> Citymaps || last_edit_changeset -> 52288875 || natural -> sand || surface -> dirt || last_edit_time -> 1506111659000 || last_edit_user_id -> 5318805 || baseball -> infield || type -> multipolygon || last_edit_version -> 2 || sport -> baseball\n7532709000000 && 520696496000000 -> outer -> L || 520696497000000 -> outer -> L && last_edit_user_name -> lmcslssslsssl || last_edit_changeset -> 51657846 || access -> private || surface -> paved || last_edit_time -> 1504307782000 || last_edit_user_id -> 5789664 || amenity -> parking || type -> multipolygon || last_edit_version -> 1 || operator -> San Francisco Giants\n7885458000000 && 189011905000000 -> platform -> A || 551597307000000 -> platform -> A || 551597308000000 -> platform -> A || 675498957000000 -> platform -> A || 675498960000000 -> platform -> A || 301505501000000 ->  -> P || 5326947004000000 -> stop -> P || 5326947005000000 -> stop -> P || 5326947006000000 -> stop -> P || 5326947007000000 -> stop -> P || 6392302633000000 -> platform -> P || 6392302634000000 -> platform -> P || 6394993274000000 -> stop -> P || 6394993275000000 -> stop -> P || 6933256734000000 -> stop -> P || 6933256735000000 -> stop -> P && last_edit_user_name -> clay_c || last_edit_changeset -> 76418016 || last_edit_time -> 1572477087000 || last_edit_user_id -> 119881 || name -> 2nd & King || public_transport -> stop_area || railway -> facility || type -> public_transport || last_edit_version -> 4 || operator -> San Francisco Municipal Railway || network -> Muni\n7885460000000 && 5326945382000000 -> stop -> P || 5326945383000000 -> stop -> P || 5326945384000000 -> stop -> P || 5326945385000000 -> stop -> P || 6933256732000000 -> stop -> P || 6933256733000000 -> stop -> P && last_edit_user_name -> clay_c || last_edit_changeset -> 76418016 || last_edit_time -> 1572477087000 || last_edit_user_id -> 119881 || name -> Brannan || public_transport -> stop_area || railway -> facility || type -> public_transport || last_edit_version -> 4 || operator -> San Francisco Municipal Railway || network -> Muni\n7885464000000 && 1723633989000000 -> stop -> P || 5326945389000000 -> stop -> P || 5326945390000000 -> stop -> P || 5326945391000000 -> stop -> P || 6933256730000000 -> stop -> P || 6933256731000000 -> stop -> P && last_edit_user_name -> clay_c || last_edit_changeset -> 76418016 || last_edit_time -> 1572477087000 || last_edit_user_id -> 119881 || name -> Folsom || public_transport -> stop_area || railway -> facility || type -> public_transport || last_edit_version -> 4 || operator -> San Francisco Municipal Railway || network -> Muni\n8592257000000 && -681699528000002 ->  -> E || -681699528000001 ->  -> E || -111550911000003 ->  -> E || -111550911000002 ->  -> E || -111550911000001 ->  -> E || 111550911000001 ->  -> E || 111550911000002 ->  -> E || 111550911000003 ->  -> E || 681699528000001 ->  -> E || 681699528000002 ->  -> E && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> 10 || route -> bus:suspended || public_transport:version -> 2 || last_edit_time -> 1586197837000 || last_edit_user_id -> 119881 || name -> Muni 10 inbound: SF General Hospital => Russian Hill (evenings, weekends) || from -> 24th Street & Potrero Avenue || to -> Jackson Street & Van Ness Avenue || last_edit_version -> 41\n8592258000000 && -681699528000002 ->  -> E || -681699528000001 ->  -> E || -111550911000003 ->  -> E || -111550911000002 ->  -> E || -111550911000001 ->  -> E || 111550911000001 ->  -> E || 111550911000002 ->  -> E || 111550911000003 ->  -> E || 681699528000001 ->  -> E || 681699528000002 ->  -> E && payment:clipper -> yes || last_edit_changeset -> 83161170 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> 10 || route -> bus:suspended || public_transport:version -> 2 || last_edit_time -> 1586197837000 || last_edit_user_id -> 119881 || name -> Muni 10 outbound: Russian Hill => SF General Hospital (evenings, weekends) || from -> Pacific Avenue & Van Ness Avenue || to -> 24th Street & Potrero Avenue || last_edit_version -> 40\n8610460000000 && -396945233000002 ->  -> E || -396945233000001 ->  -> E || -133755930000002 ->  -> E || -133755930000001 ->  -> E || -27656675000003 ->  -> E || -27656675000002 ->  -> E || -27656675000001 ->  -> E || 27656675000001 ->  -> E || 27656675000002 ->  -> E || 27656675000003 ->  -> E || 37177789000001 ->  -> E || 37177789000002 ->  -> E || 133755930000001 ->  -> E || 133755930000002 ->  -> E || 396945233000001 ->  -> E || 396945233000002 ->  -> E || 674718379000001 ->  -> E || 674718379000002 ->  -> E && last_edit_changeset -> 81629287 || fee -> no || type -> route || operator -> Mission Bay Transportation Management Association || network -> Mission Bay || via -> Montgomery BART || last_edit_user_name -> clay_c || ref -> Transbay || roundtrip -> yes || route -> bus || public_transport:version -> 2 || last_edit_time -> 1583008143000 || last_edit_user_id -> 119881 || name -> Mission Bay Transbay/Caltrain Shuttle || from -> Owens Street & Gene Friend Way || to -> Owens Street & Gene Friend Way || last_edit_version -> 29\n9432801000000 && 288649066000000 -> outer -> A || 288649074000000 -> inner -> A && last_edit_user_name -> Yury_Yatsynovich_--_OSM_Imports || last_edit_changeset -> 87739654 || addr:housenumber -> 201;207;213 || last_edit_time -> 1594270289000 || last_edit_user_id -> 11362402 || building:levels -> 6 || addr:street -> King Street || type -> multipolygon || last_edit_version -> 2 || building -> yes\n9466656000000 && 48983526000000 ->  -> L && note -> This is the \"newly defined Phase 1, as originally proposed\" alignment after Governor Newsom's SotSA in February, 2019 || last_edit_changeset -> 88054330 || fixme -> Needs to fully reflect all bridges, cuttings, embankments, etc. No station members, this is (track) infrastructure only. || operator:wikidata -> Q5020545 || type -> route || operator -> California High Speed Rail Authority || last_edit_user_name -> clay_c || route -> railway || last_edit_time -> 1594858289000 || last_edit_user_id -> 119881 || name -> California High Speed Rail Proposal, Phase 1 North || wikipedia -> en:California High-Speed Rail || last_edit_version -> 9 || wikidata -> Q782093\n9599131000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 82023504 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Oakland || last_edit_user_name -> clay_c || roundtrip -> yes || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: Alameda => Oakland => South San Francisco || from -> Alameda || to -> South San Francisco Oyster Point Terminal || last_edit_version -> 5 || foot -> yes\n9599132000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 82023504 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Oakland || last_edit_user_name -> clay_c || roundtrip -> yes || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: South San Francisco => Oakland => Alameda || from -> South San Francisco Oyster Point Terminal || to -> Alameda || last_edit_version -> 5 || foot -> yes\n9599133000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 82023504 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Alameda || last_edit_user_name -> clay_c || roundtrip -> yes || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: South San Francisco => Alameda => Oakland || from -> South San Francisco Oyster Point Terminal || to -> Oakland || last_edit_version -> 5 || foot -> yes\n9599134000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76406424 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Oakland || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572454041000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: San Francisco Ferry Building => Oakland => Alameda || from -> San Francisco Ferry Building || to -> Alameda || last_edit_version -> 7 || foot -> yes\n9599135000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76406424 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Alameda || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572454041000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: San Francisco Ferry Building => Alameda => Oakland || from -> San Francisco Ferry Building || to -> Oakland || last_edit_version -> 7 || foot -> yes\n9599424000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76406424 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Oakland || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572454041000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: Alameda => Oakland => San Francisco Ferry Building || from -> Alameda || to -> San Francisco Ferry Building || last_edit_version -> 5 || foot -> yes\n9599425000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76406424 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Alameda || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572454041000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: Oakland => Alameda => San Francisco Ferry Building || from -> Oakland || to -> San Francisco Ferry Building || last_edit_version -> 5 || foot -> yes\n9599442000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76406424 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> San Francisco Ferry Building;Oakland || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572454041000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: San Francisco Pier 41 => San Francisco Ferry Building => Oakland => Alameda || from -> San Francisco Pier 41 || to -> Alameda || last_edit_version -> 6 || foot -> yes\n9599443000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76406424 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> San Francisco Ferry Building;Alameda || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572454041000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: San Francisco Pier 41 => San Francisco Ferry Building => Alameda => Oakland || from -> San Francisco Pier 41 || to -> Oakland || last_edit_version -> 6 || foot -> yes\n9599444000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76374206 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Alameda || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572409875000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: San Francisco Pier 41 => Alameda => Oakland || from -> San Francisco Pier 41 || to -> Oakland || last_edit_version -> 5 || foot -> yes\n9599445000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76406424 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Oakland;San Francisco Ferry Building || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572454041000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: Alameda => Oakland => San Francisco Ferry Building => San Francisco Pier 41 || from -> Alameda || to -> San Francisco Pier 41 || last_edit_version -> 6 || foot -> yes\n9599446000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76406424 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Alameda;San Francisco Ferry Building || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572454041000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: Oakland => Alameda => San Francisco Ferry Building => San Francisco Pier 41 || from -> Oakland || to -> San Francisco Pier 41 || last_edit_version -> 6 || foot -> yes\n9599447000000 && 310688988000000 -> stop -> N || 1142640695000000 -> stop -> N || 310688988000000 -> stop -> P || 1142640695000000 -> stop -> P && payment:clipper -> yes || last_edit_changeset -> 76374206 || payment:prepaid_ticket -> yes || bicycle -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> Water Emergency Transportation Authority || network -> SFBF || vehicle -> no || via -> Oakland || last_edit_user_name -> clay_c || route -> ferry || public_transport:version -> 2 || last_edit_time -> 1572409875000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: Alameda => Oakland => San Francisco Pier 41 || from -> Alameda || to -> San Francisco Pier 41 || last_edit_version -> 5 || foot -> yes\n10229279000000 && 310688988000000 -> stop -> N || 310688988000000 -> stop -> P && last_edit_user_name -> clay_c || last_edit_changeset -> 76356726 || last_edit_time -> 1572368313000 || last_edit_user_id -> 119881 || name -> Oakland Jack London Square Terminal || public_transport -> stop_area || type -> public_transport || last_edit_version -> 1 || operator -> Water Emergency Transportation Authority || network -> SFBF\n10229280000000 && 1142640695000000 -> stop -> N || 1142640695000000 -> stop -> P && last_edit_user_name -> clay_c || last_edit_changeset -> 76356726 || last_edit_time -> 1572368313000 || last_edit_user_id -> 119881 || name -> Alameda Main Street Terminal || public_transport -> stop_area || type -> public_transport || last_edit_version -> 1 || operator -> Water Emergency Transportation Authority || network -> SFBF\n10235566000000 && 590986805000000 -> stop -> N || -780006768000003 -> platform -> E || -780006768000002 -> platform -> E || -780006768000001 -> platform -> E || -46274021000003 -> platform -> E || -46274021000002 -> platform -> E || -46274021000001 -> platform -> E || 46274021000001 -> platform -> E || 46274021000002 -> platform -> E || 46274021000003 -> platform -> E || 780006768000001 -> platform -> E || 780006768000002 -> platform -> E || 780006768000003 -> platform -> E || 590986805000000 -> stop -> P && last_edit_user_name -> clay_c || last_edit_changeset -> 82023504 || last_edit_time -> 1583873703000 || last_edit_user_id -> 119881 || name -> Oracle Park || public_transport -> stop_area || type -> public_transport || last_edit_version -> 2 || network -> SFBF;GGF\n10758371000000 && 8920394000001 ->  -> E || 8920394000002 ->  -> E || 396945236000001 ->  -> E || 396945236000002 ->  -> E || 397270773000001 ->  -> E || 397270773000002 ->  -> E || 397270782000000 ->  -> E || 468012245000001 ->  -> E || 468012245000002 ->  -> E || 681699525000000 ->  -> E || 681699526000001 ->  -> E || 681699526000002 ->  -> E || 6392302634000000 -> platform -> P || 6394993275000000 -> stop -> P && payment:clipper -> yes || note -> all-day service during shelter-in-place order; otherwise weekend early mornings only || last_edit_changeset -> 83384853 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> NBUS || route -> bus || public_transport:version -> 2 || last_edit_time -> 1586560582000 || last_edit_user_id -> 119881 || name -> Muni N-Bus inbound: Ocean Beach => Caltrain (weekend early mornings) || from -> Judah Street & 48th Avenue || to -> Townsend Street & 5th Street || last_edit_version -> 5\n10758372000000 && 27041554000001 ->  -> E || 27041554000002 ->  -> E || 27041554000003 ->  -> E || 27041554000004 ->  -> E || 396945237000001 ->  -> E || 396945237000002 ->  -> E || 397270776000001 ->  -> E || 397270776000002 ->  -> E || 397270779000001 ->  -> E || 397270779000002 ->  -> E || 397270779000003 ->  -> E || 6392302633000000 -> platform -> P || 6394993274000000 -> stop -> P && payment:clipper -> yes || note -> all-day service during shelter-in-place order; otherwise weekend early mornings only || last_edit_changeset -> 83384853 || payment:prepaid_ticket -> yes || fee -> yes || payment:cash -> yes || type -> route || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> NBUS || route -> bus || public_transport:version -> 2 || last_edit_time -> 1586560582000 || last_edit_user_id -> 119881 || name -> Muni N-Bus outbound: Caltrain => Ocean Beach (weekend early mornings) || from -> Townsend Street & 5th Street || to -> Judah Street & La Playa Street || last_edit_version -> 5\n3419236000000 && 3419234000000 ->  -> R || 3419235000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 83384853 || ref -> KTBU || route_master -> bus || last_edit_time -> 1586560582000 || last_edit_user_id -> 119881 || name -> Muni T-Bus || type -> route_master || last_edit_version -> 7 || operator -> San Francisco Municipal Railway || network -> Muni\n3419240000000 && 3419232000000 ->  -> R || 3419233000000 ->  -> R && last_edit_changeset -> 71794092 || nextbus:route -> N-Owl || nextbus:agency -> sf-muni || type -> route_master || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> N-Owl || route_master -> bus || last_edit_time -> 1562003092000 || last_edit_user_id -> 119881 || name -> Muni N-Owl || last_edit_version -> 3\n3435884000000 && 63223000000 ->  -> R || 3435877000000 ->  -> R && last_edit_changeset -> 70144807 || nextbus:route -> N || nextbus:agency -> sf-muni || type -> route_master || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> #00539b || ref -> N || route_master -> light_rail || last_edit_time -> 1557600786000 || last_edit_user_id -> 119881 || name -> Muni Metro N-Judah || last_edit_version -> 11 || wikidata -> Q6956302\n5953002000000 && 5953000000000 ->  -> R || 5953001000000 ->  -> R && last_edit_changeset -> 71794092 || nextbus:route -> E || nextbus:agency -> sf-muni || type -> route_master || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> #1E9999 || ref -> E || route_master -> tram || last_edit_time -> 1562003092000 || last_edit_user_id -> 119881 || name -> Muni Streetcar E-Embarcadero || last_edit_version -> 6 || wikidata -> Q5324790\n6071918000000 && 6071916000000 ->  -> R || 6071917000000 ->  -> R && last_edit_changeset -> 70144807 || nextbus:route -> S || nextbus:agency -> sf-muni || type -> route_master || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || colour -> yellow || ref -> S || route_master -> light_rail || last_edit_time -> 1557600788000 || last_edit_user_id -> 119881 || name -> Muni Metro S-Castro Shuttle || last_edit_version -> 9 || wikidata -> Q7395342\n9569831000000 && 8610460000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 76315860 || ref -> Transbay || route_master -> bus || last_edit_time -> 1572296868000 || last_edit_user_id -> 119881 || name -> Mission Bay Transbay/Caltrain Shuttle || type -> route_master || last_edit_version -> 2 || network -> Mission Bay\n9599138000000 && 9599131000000 ->  -> R || 9599132000000 ->  -> R || 9599133000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 70368429 || route_master -> ferry || last_edit_time -> 1558116232000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: Alameda - Oakland - South San Francisco || type -> route_master || last_edit_version -> 1 || network -> SFBF\n10758375000000 && 10758371000000 ->  -> R || 10758372000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 81563991 || ref -> NBUS || route_master -> bus || last_edit_time -> 1582837436000 || last_edit_user_id -> 119881 || name -> Muni N-Bus || type -> route_master || last_edit_version -> 1 || operator -> San Francisco Municipal Railway || network -> Muni\n2981335000000 && 2981333000000 ->  -> R || 2981334000000 ->  -> R || 8592257000000 ->  -> R || 8592258000000 ->  -> R && last_edit_changeset -> 71794092 || nextbus:route -> 10 || nextbus:agency -> sf-muni || type -> route_master || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> 10 || route_master -> bus || last_edit_time -> 1562003090000 || last_edit_user_id -> 119881 || name -> Muni 10-Townsend || last_edit_version -> 5\n9599137000000 && 9599134000000 ->  -> R || 9599135000000 ->  -> R || 9599424000000 ->  -> R || 9599425000000 ->  -> R || 9599442000000 ->  -> R || 9599443000000 ->  -> R || 9599444000000 ->  -> R || 9599445000000 ->  -> R || 9599446000000 ->  -> R || 9599447000000 ->  -> R && last_edit_user_name -> clay_c || last_edit_changeset -> 70375010 || route_master -> ferry || last_edit_time -> 1558130227000 || last_edit_user_id -> 119881 || name -> San Francisco Bay Ferry: Alameda - Oakland - San Francisco || type -> route_master || last_edit_version -> 3 || network -> SFBF\n3419241000000 && 3419225000000 ->  -> R && last_edit_changeset -> 71802947 || nextbus:route -> 91 || nextbus:agency -> sf-muni || type -> route_master || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> 91 || route_master -> bus || last_edit_time -> 1562025400000 || last_edit_user_id -> 119881 || name -> Muni 91-3rd Street/19th Avenue Owl || last_edit_version -> 4\n3433320000000 && 63250000000 ->  -> R || 3433316000000 ->  -> R && last_edit_changeset -> 70144807 || nextbus:route -> KT || nextbus:agency -> sf-muni || type -> route_master || operator -> San Francisco Municipal Railway || network -> Muni || last_edit_user_name -> clay_c || ref -> KT || route_master -> light_rail || last_edit_time -> 1557600784000 || last_edit_user_id -> 119881 || name -> Muni Metro KT-Ingleside/Third Street || last_edit_version -> 10\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/continent_map.txt",
    "content": "NAM||POLYGON ((55.1563460585111 25.2011160213599,55.1514625834864 25.1994165249564,55.1424469372868 25.2012293202767,55.1403182430452 25.1970371901407,55.1349338987872 25.1963573716543,55.1320539006956 25.1994165249564,55.1308017276124 25.2067808380388,55.1313025968457 25.2216214074802,55.1331808564706 25.2261525183121,55.1368121584121 25.2258126908551,55.1377043317339 25.2220745261593,55.1383030269893 25.2214231675273,55.1385984615762 25.2214320175321,55.1388606353155 25.2215736175211,55.1389436651518 25.2216166506101,55.1389744803488 25.2216330230924,55.1390273064007 25.2216918755106,55.1391202411217 25.2223972173145,55.13924545843 25.2243229477605,55.1364593733199 25.228684048111,55.1368037209178 25.231572483023,55.1431898036421 25.2338378741566,55.1499515382914 25.230892857452,55.1525811017662 25.2254557161331,55.1530819709994 25.2189987950948,55.1554610998575 25.213447834639,55.1549602306242 25.209595998973,55.1547097960076 25.2055740992859,55.1547097960076 25.2055740992859,55.1563460585111 25.2011160213599))#\nSAM||POLYGON ((55.1564002296699 25.2071035697559,55.1609080527695 25.2094827078431,55.1679202220354 25.2103890339292,55.171676741285 25.2040446095986,55.17205239321 25.2016653652228,55.1729289143682 25.1996259758936,55.1759341297679 25.2003057761322,55.1749323913014 25.1949805726886,55.1730541316766 25.1941874373582,55.165791527794 25.199172773626,55.1602819662279 25.2014387680954,55.1569010989032 25.2051775667678,55.1564002296699 25.2071035697559))#\nANT||POLYGON ((55.1761845643846 25.1924878442577,55.1756836951513 25.1941874373582,55.1771863028511 25.1995126754849,55.1788141278593 25.2000791764744,55.1823202124923 25.1989461718597,55.1874541221334 25.20347812706,55.1863271663585 25.2082364984545,55.1869532529001 25.2135611220777,55.18770455675 25.215260421001,55.1910854240747 25.2174128322354,55.1924628144662 25.2233034472047,55.1933393356245 25.224209670343,55.1979723760323 25.2229636117871,55.1980975933406 25.2192253595298,55.1934645529328 25.2127681077923,55.1895828163748 25.2048376807242,55.1820697778756 25.1948672679576,55.1761845643846 25.1924878442577))#\nAFR||POLYGON ((55.184574124042 25.2122016658529,55.1830715163422 25.2112953532659,55.178313258626 25.2119750883389,55.1729289143682 25.2141275576889,55.1699236989685 25.211635221277,55.1642889200941 25.2151471351444,55.1637880508608 25.2171862644249,55.165791527794 25.2200183317296,55.1698610903144 25.2235866426607,55.1724906537891 25.2234733645574,55.1768106509262 25.2235866426607,55.178313258626 25.2252291633026,55.1795028230551 25.2246061408427,55.180254126905 25.2223405777222,55.1795654317092 25.2209245793428,55.1818819519131 25.2202448942658,55.1838854288463 25.2213777006176,55.1848871673128 25.2208679390647,55.186139340396 25.2189421539201,55.1856384711628 25.2179226082664,55.1832593423047 25.2179792499157,55.184949775967 25.2133345470949,55.184574124042 25.2122016658529))#\nEUR||POLYGON ((55.1577150114073 25.2209812195944,55.1561497950533 25.2313459415881,55.1563376210158 25.2338945083941,55.1571426042939 25.2411454221216,55.1582784892947 25.2427857564285,55.1613463133486 25.2380287364691,55.1647271806732 25.2380287364691,55.1647897893274 25.2359899566342,55.1653532672148 25.2357067900638,55.1664802229897 25.2347440187907,55.1659167451023 25.2331582612487,55.1671689181855 25.2312326707124,55.1662297883731 25.2298167758621,55.1677950047271 25.2281743171805,55.1675445701105 25.2273813981529,55.166981092223 25.225908820528,55.1676697874188 25.2248893332656,55.1678576133813 25.2233034472047,55.1639132681692 25.2192820005727,55.1629115297026 25.2186023063182,55.1577150114073 25.2209812195944))#\nASA||POLYGON ((55.158779358528 25.2461551995744,55.1682880478786 25.2557958211212,55.1707297853909 25.2572680366544,55.1808410830377 25.2557675091091,55.1819054301585 25.2544085247683,55.1804028224586 25.2516621975017,55.1854115147914 25.2442722869806,55.1842845590165 25.2427149688058,55.1824062993917 25.242488448136,55.1824062993917 25.2412992076925,55.1865541227299 25.240690425343,55.1852706453196 25.2355227314393,55.1832671683864 25.2346873849489,55.180559344094 25.23246448582,55.1834393421854 25.2295760720979,55.1821558647751 25.2280893618177,55.1780549979276 25.2285566155787,55.1775541286943 25.2254840352073,55.1763645642653 25.224492863689,55.1712463067877 25.2241247122106,55.1679437002807 25.2249318120722,55.1671141356131 25.2259796179373,55.1671454399402 25.2263265246467,55.1674076136795 25.2270061757394,55.167935874199 25.2282309540561,55.1665271794804 25.2296893445168,55.1675445701105 25.2310910819693,55.1661828318825 25.2332007371667,55.1666367446251 25.2348714448381,55.1651341369253 25.2358766900852,55.165008919617 25.236188172841,55.1648837023087 25.238255265451,55.1619723998902 25.2383402137104,55.1590924017988 25.2434370006229,55.158779358528 25.2461551995744))#\nOCA||POLYGON ((55.1979175934599 25.2313317827344,55.1957888992185 25.2309353341615,55.194035856902 25.2265743145475,55.1927210751646 25.2265743145475,55.1895906424566 25.2277636990438,55.186209775132 25.2255548328638,55.185834123207 25.2241388719034,55.1828915164615 25.2233459265651,55.1813263001075 25.22391231662,55.1813263001075 25.2247052582658,55.1822028212657 25.2263477638008,55.1847071674321 25.2299158890383,55.1824532558824 25.230991969751,55.1815767347241 25.2328042946879,55.1824532558824 25.2338803498372,55.1860219491695 25.2353528309233,55.1872115135985 25.2392604827149,55.1906549895774 25.2402798494785,55.1902167289982 25.2475284332163,55.1906549895774 25.2485477306259,55.1942862915186 25.2484344757805,55.1961019424893 25.2468488968591,55.1966028117226 25.2419787750852,55.2008602002055 25.2411859460909,55.2023001992511 25.2362589642652,55.2017367213637 25.2322945810299,55.1995454184681 25.2312185118455,55.1979175934599 25.2313317827344))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/osm-pbf-node.json",
    "content": "{\n    \"filters\": [\n    \t\"amenity->!fountain\"\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/osm-pbf-relation.json",
    "content": "{\n    \"filters\": [\n    \t\"boundary->!|natural->water\",\n    \t\"place->island\"\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/command/osm-pbf-way.json",
    "content": "{\n    \"filters\": [\n    \t\"boundary->!|highway->*|railway->*|waterway->*\",\n    \t\"hires->!yes\"\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasAgressiveRelationsTest_11-998-708.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-131089' action='modify' visible='true' lat='48.34193997112' lon='-4.39594840655' />\n  <node id='-131090' action='modify' visible='true' lat='48.34237003312' lon='-4.3950918749' />\n  <way id='-121840' action='modify' visible='true'>\n    <nd ref='-131089' />\n    <nd ref='-131090' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-99756' action='modify' visible='true'>\n    <member type='way' ref='-121840' role='member' />\n    <tag k='type' v='test' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasAgressiveRelationsTest_11-999-708.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-131094' action='modify' visible='true' lat='48.34232088336' lon='-4.39405048031' />\n  <node id='-131095' action='modify' visible='true' lat='48.34198912125' lon='-4.39300292363' />\n  <way id='-121885' action='modify' visible='true'>\n    <nd ref='-131094' />\n    <nd ref='-131095' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-99756' action='modify' visible='true'>\n    <member type='way' ref='-121885' role='member' />\n    <tag k='type' v='test' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasMovingTooFastTest.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-110684' action='modify' lat='-13.10194077495' lon='-149.08605382794' />\n  <node id='-110686' action='modify' lat='-13.19507494051' lon='-143.1754065708' />\n  <node id='-110688' action='modify' lat='-18.99972594911' lon='-141.3773455929' />\n  <node id='-110690' action='modify' lat='-17.01700072592' lon='-147.44101931624' />\n  <node id='-110692' action='modify' lat='-17.45546213362' lon='-147.11583807556' />\n  <node id='-110694' action='modify' lat='-17.19981941022' lon='-146.6185020604' />\n  <way id='-110696' action='modify'>\n    <nd ref='-110684' />\n    <nd ref='-110686' />\n    <nd ref='-110688' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-110698' action='modify'>\n    <nd ref='-110690' />\n    <nd ref='-110692' />\n    <nd ref='-110694' />\n    <tag k='highway' v='secondary' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasPartialInitialShardsTest.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-111956' action='modify' lat='6.21671550709' lon='-10.95712202083' />\n  <node id='-111957' action='modify' lat='6.17843244818' lon='-11.02789369889' />\n  <node id='-111959' action='modify' lat='6.10703233219' lon='-11.03413825872' />\n  <node id='-111961' action='modify' lat='6.01802756706' lon='-10.8998802224' />\n  <node id='-111963' action='modify' lat='6.08219527185' lon='-10.71670646741' />\n  <node id='-111965' action='modify' lat='6.18464070036' lon='-10.83014930431' />\n  <way id='-111958' action='modify'>\n    <nd ref='-111956' />\n    <nd ref='-111957' />\n    <nd ref='-111959' />\n    <nd ref='-111961' />\n    <nd ref='-111963' />\n    <nd ref='-111965' />\n    <nd ref='-111956' />\n    <tag k='natural' v='glacier' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasPreemptiveLoadTest.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-139008' action='modify' lat='7.13463380459' lon='-10.49970749297' />\n  <node id='-139009' action='modify' lat='7.03806608353' lon='-10.65738262866' />\n  <node id='-139013' action='modify' lat='6.90376746323' lon='-10.48617761334' />\n  <node id='-139015' action='modify' lat='6.93682915051' lon='-10.38626465608' />\n  <node id='-139024' action='modify' lat='7.1124301863' lon='-10.72451164682' />\n  <node id='-139025' action='modify' lat='7.06078971839' lon='-10.76145862581' />\n  <node id='-139027' action='modify' lat='6.97092158452' lon='-10.67871820807' />\n  <node id='-139029' action='modify' lat='6.90686709472' lon='-10.71514480708' />\n  <node id='-139038' action='modify' lat='6.88671912738' lon='-10.6230375496' />\n  <node id='-139039' action='modify' lat='6.85107062885' lon='-10.60534463008' />\n  <node id='-139041' action='modify' lat='6.88516924827' lon='-10.5241653523' />\n  <node id='-139043' action='modify' lat='6.8185196719' lon='-10.47785153357' />\n  <node id='-139045' action='modify' lat='6.84693729693' lon='-10.41696707524' />\n  <way id='-139010' action='modify'>\n    <nd ref='-139008' />\n    <nd ref='-139009' />\n    <nd ref='-139013' />\n    <nd ref='-139015' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-139026' action='modify'>\n    <nd ref='-139024' />\n    <nd ref='-139025' />\n    <nd ref='-139027' />\n    <nd ref='-139029' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-139040' action='modify'>\n    <nd ref='-139038' />\n    <nd ref='-139039' />\n    <nd ref='-139041' />\n    <nd ref='-139043' />\n    <nd ref='-139045' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/DynamicAtlasRestrainedExpansionWithPolygonTest.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-110564' action='modify' lat='-13.47050823382' lon='-143.1336288672' />\n  <node id='-110565' action='modify' lat='-13.00612190883' lon='-143.62472547727' />\n  <node id='-110567' action='modify' lat='-13.36132075379' lon='-147.87621898724' />\n  <node id='-110576' action='modify' lat='-13.9067562404' lon='-151.35598925283' />\n  <node id='-110577' action='modify' lat='-12.9787776583' lon='-152.49252712184' />\n  <node id='-110586' action='modify' lat='-12.88304913169' lon='-155.98632871914' />\n  <node id='-110587' action='modify' lat='-14.72249022486' lon='-154.96204150387' />\n  <way id='-110566' action='modify'>\n    <nd ref='-110564' />\n    <nd ref='-110565' />\n    <nd ref='-110567' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-110578' action='modify'>\n    <nd ref='-110576' />\n    <nd ref='-110577' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-110588' action='modify'>\n    <nd ref='-110586' />\n    <nd ref='-110587' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12all.atlas.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.336198,15.420563]},\"properties\":{\"identifier\":\"1\",\"outEdges\":\"1000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154205630\",\"inEdges\":\"-1000000, 8000000\",\"longitude\":\"-613361980\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.33285,15.429499]},\"properties\":{\"identifier\":\"2\",\"outEdges\":\"-1000000, 2000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154294990\",\"inEdges\":\"1000000\",\"longitude\":\"-613328500\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3041,15.4855]},\"properties\":{\"identifier\":\"3\",\"outEdges\":\"3000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154855000\",\"inEdges\":\"2000000\",\"longitude\":\"-613041000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3366,15.4809]},\"properties\":{\"identifier\":\"4\",\"outEdges\":\"4000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154809000\",\"inEdges\":\"3000000\",\"longitude\":\"-613366000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3816,15.4852]},\"properties\":{\"identifier\":\"5\",\"outEdges\":\"5000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154852000\",\"inEdges\":\"4000000\",\"longitude\":\"-613816000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3949,15.4781]},\"properties\":{\"identifier\":\"6\",\"outEdges\":\"6000001\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154781000\",\"inEdges\":\"5000000\",\"longitude\":\"-613949000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3826,15.4145]},\"properties\":{\"identifier\":\"7\",\"outEdges\":\"7000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154145000\",\"inEdges\":\"6000001\",\"longitude\":\"-613826000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3749,15.4073]},\"properties\":{\"identifier\":\"8\",\"outEdges\":\"8000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154073000\",\"inEdges\":\"7000000\",\"longitude\":\"-613749000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.336198,15.420563],[-61.33285,15.429499]]},\"properties\":{\"identifier\":\"1000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"1\",\"startNode\":\"1\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"2\",\"parentRelations\":\"1-a\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.33285,15.429499],[-61.336198,15.420563]]},\"properties\":{\"identifier\":\"-1000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"1\",\"startNode\":\"2\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"1\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.33285,15.429499],[-61.3041,15.4855]]},\"properties\":{\"identifier\":\"2000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"2\",\"startNode\":\"2\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"3\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3041,15.4855],[-61.3366,15.4809]]},\"properties\":{\"identifier\":\"3000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"3\",\"startNode\":\"3\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"4\",\"parentRelations\":\"1-b\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3366,15.4809],[-61.3816,15.4852]]},\"properties\":{\"identifier\":\"4000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"4\",\"startNode\":\"4\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"5\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3816,15.4852],[-61.3949,15.4781]]},\"properties\":{\"identifier\":\"5000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"5\",\"startNode\":\"5\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"6\",\"parentRelations\":\"3-a\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3949,15.4781],[-61.3826,15.4145]]},\"properties\":{\"identifier\":\"6000001\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"6\",\"startNode\":\"6\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"7\",\"parentRelations\":\"3-b\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3826,15.4145],[-61.3749,15.4073]]},\"properties\":{\"identifier\":\"7000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"7\",\"startNode\":\"7\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"8\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3749,15.4073],[-61.336198,15.420563]]},\"properties\":{\"identifier\":\"8000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"8\",\"startNode\":\"8\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"1\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3112,15.3907],[-61.336198,15.420563]]]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"AREA\",\"osmIdentifier\":\"0\",\"landuse\":\"residential\",\"shard\":\"unknown\",\"parentRelations\":\"2-b, 3-c\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3041,15.4855],[-61.336198,15.420563]]]},\"properties\":{\"landuse\":\"residential\",\"identifier\":\"2\",\"itemType\":\"AREA\",\"shard\":\"unknown\",\"osmIdentifier\":\"0\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3112,15.3907]]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"LINE\",\"power\":\"line\",\"shard\":\"unknown\",\"osmIdentifier\":\"0\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3041,15.4855]]},\"properties\":{\"identifier\":\"2\",\"itemType\":\"LINE\",\"power\":\"line\",\"shard\":\"unknown\",\"osmIdentifier\":\"0\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.336198,15.420563]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154205630\",\"longitude\":\"-613361980\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.33285,15.429499]},\"properties\":{\"identifier\":\"2\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154294990\",\"longitude\":\"-613328500\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3041,15.4855]},\"properties\":{\"identifier\":\"3\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154855000\",\"longitude\":\"-613041000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3366,15.4809]},\"properties\":{\"identifier\":\"4\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154809000\",\"longitude\":\"-613366000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3816,15.4852]},\"properties\":{\"identifier\":\"5\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154852000\",\"longitude\":\"-613816000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3949,15.4781]},\"properties\":{\"identifier\":\"6\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154781000\",\"longitude\":\"-613949000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3826,15.4145]},\"properties\":{\"identifier\":\"7\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154145000\",\"longitude\":\"-613826000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3749,15.4073]},\"properties\":{\"identifier\":\"8\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154073000\",\"parentRelations\":\"2-a\",\"longitude\":\"-613749000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=1, [Members: {Member: ID = 1000000, Type = EDGE, Role = a}, {Member: ID = 3000000, Type = EDGE, Role = b}], [Tags: [type => relation]]]\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"2\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=2, [Members: {Member: ID = 1, Type = AREA, Role = b}, {Member: ID = 8, Type = POINT, Role = a}], [Tags: [type => relation]]]\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"3\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=3, [Members: {Member: ID = 5000000, Type = EDGE, Role = a}, {Member: ID = 6000001, Type = EDGE, Role = b}, {Member: ID = 1, Type = AREA, Role = c}], [Tags: [type => relation]]]\"}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12x1349y1869.atlas.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3366,15.4809]},\"properties\":{\"identifier\":\"4\",\"outEdges\":\"4000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154809000\",\"inEdges\":\"\",\"longitude\":\"-613366000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3816,15.4852]},\"properties\":{\"identifier\":\"5\",\"outEdges\":\"5000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154852000\",\"inEdges\":\"4000000\",\"longitude\":\"-613816000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3949,15.4781]},\"properties\":{\"identifier\":\"6\",\"outEdges\":\"6000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154781000\",\"inEdges\":\"5000000\",\"longitude\":\"-613949000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3826,15.4145]},\"properties\":{\"identifier\":\"7\",\"outEdges\":\"\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154145000\",\"inEdges\":\"6000000\",\"longitude\":\"-613826000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3366,15.4809],[-61.3816,15.4852]]},\"properties\":{\"identifier\":\"4000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"4\",\"startNode\":\"4\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"5\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3816,15.4852],[-61.3949,15.4781]]},\"properties\":{\"identifier\":\"5000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"5\",\"startNode\":\"5\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"6\",\"parentRelations\":\"3-a\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3949,15.4781],[-61.3826,15.4145]]},\"properties\":{\"identifier\":\"6000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"6\",\"startNode\":\"6\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"7\",\"parentRelations\":\"3-b\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3816,15.4852]},\"properties\":{\"identifier\":\"5\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154852000\",\"longitude\":\"-613816000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3949,15.4781]},\"properties\":{\"identifier\":\"6\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154781000\",\"longitude\":\"-613949000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"3\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=3, [Members: {Member: ID = 5000000, Type = EDGE, Role = a}, {Member: ID = 6000000, Type = EDGE, Role = b}], [Tags: [type => relation]]]\"}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12x1349y1869.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.4355469,15.4536802],[-61.4355469,15.5383759],[-61.3476562,15.5383759],[-61.3476562,15.4536802]]]},\"properties\":{}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12x1349y1870.atlas.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.336198,15.420563]},\"properties\":{\"identifier\":\"1\",\"outEdges\":\"\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154205630\",\"inEdges\":\"8000000\",\"longitude\":\"-613361980\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3949,15.4781]},\"properties\":{\"identifier\":\"6\",\"outEdges\":\"6000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154781000\",\"inEdges\":\"\",\"longitude\":\"-613949000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3826,15.4145]},\"properties\":{\"identifier\":\"7\",\"outEdges\":\"7000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154145000\",\"inEdges\":\"6000000\",\"longitude\":\"-613826000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3749,15.4073]},\"properties\":{\"identifier\":\"8\",\"outEdges\":\"8000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154073000\",\"inEdges\":\"7000000\",\"longitude\":\"-613749000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3949,15.4781],[-61.3826,15.4145]]},\"properties\":{\"identifier\":\"6000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"6\",\"startNode\":\"6\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"7\",\"parentRelations\":\"3-b\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3826,15.4145],[-61.3749,15.4073]]},\"properties\":{\"identifier\":\"7000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"7\",\"startNode\":\"7\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"8\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3749,15.4073],[-61.336198,15.420563]]},\"properties\":{\"identifier\":\"8000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"8\",\"startNode\":\"8\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"1\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3826,15.4145]},\"properties\":{\"identifier\":\"7\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154145000\",\"longitude\":\"-613826000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3749,15.4073]},\"properties\":{\"identifier\":\"8\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154073000\",\"parentRelations\":\"2-a\",\"longitude\":\"-613749000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"2\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=2, [Members: {Member: ID = 8, Type = POINT, Role = a}], [Tags: [type => relation]]]\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"3\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=3, [Members: {Member: ID = 6000000, Type = EDGE, Role = b}], [Tags: [type => relation]]]\"}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12x1349y1870.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.4355469,15.3689499],[-61.4355469,15.4536802],[-61.3476562,15.4536802],[-61.3476562,15.3689499]]]},\"properties\":{}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12x1350y1869.atlas.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.33285,15.429499]},\"properties\":{\"identifier\":\"2\",\"outEdges\":\"2000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154294990\",\"inEdges\":\"\",\"longitude\":\"-613328500\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3041,15.4855]},\"properties\":{\"identifier\":\"3\",\"outEdges\":\"3000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154855000\",\"inEdges\":\"2000000\",\"longitude\":\"-613041000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3366,15.4809]},\"properties\":{\"identifier\":\"4\",\"outEdges\":\"4000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154809000\",\"inEdges\":\"3000000\",\"longitude\":\"-613366000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3816,15.4852]},\"properties\":{\"identifier\":\"5\",\"outEdges\":\"\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154852000\",\"inEdges\":\"4000000\",\"longitude\":\"-613816000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.33285,15.429499],[-61.3041,15.4855]]},\"properties\":{\"identifier\":\"2000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"2\",\"startNode\":\"2\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"3\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3041,15.4855],[-61.3366,15.4809]]},\"properties\":{\"identifier\":\"3000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"3\",\"startNode\":\"3\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"4\",\"parentRelations\":\"1-b\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3366,15.4809],[-61.3816,15.4852]]},\"properties\":{\"identifier\":\"4000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"4\",\"startNode\":\"4\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"5\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3041,15.4855],[-61.336198,15.420563]]]},\"properties\":{\"landuse\":\"residential\",\"identifier\":\"2\",\"itemType\":\"AREA\",\"shard\":\"unknown\",\"osmIdentifier\":\"0\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3041,15.4855]]},\"properties\":{\"identifier\":\"2\",\"itemType\":\"LINE\",\"power\":\"line\",\"shard\":\"unknown\",\"osmIdentifier\":\"0\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3041,15.4855]},\"properties\":{\"identifier\":\"3\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154855000\",\"longitude\":\"-613041000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3366,15.4809]},\"properties\":{\"identifier\":\"4\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154809000\",\"longitude\":\"-613366000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=1, [Members: {Member: ID = 3000000, Type = EDGE, Role = b}], [Tags: [type => relation]]]\"}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12x1350y1869.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.3476562,15.4536802],[-61.3476562,15.5383759],[-61.2597656,15.5383759],[-61.2597656,15.4536802]]]},\"properties\":{}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12x1350y1870.atlas.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.336198,15.420563]},\"properties\":{\"identifier\":\"1\",\"outEdges\":\"1000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154205630\",\"inEdges\":\"-1000000, 8000000\",\"longitude\":\"-613361980\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.33285,15.429499]},\"properties\":{\"identifier\":\"2\",\"outEdges\":\"-1000000, 2000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154294990\",\"inEdges\":\"1000000\",\"longitude\":\"-613328500\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3041,15.4855]},\"properties\":{\"identifier\":\"3\",\"outEdges\":\"\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154855000\",\"inEdges\":\"2000000\",\"longitude\":\"-613041000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.3749,15.4073]},\"properties\":{\"identifier\":\"8\",\"outEdges\":\"8000000\",\"itemType\":\"NODE\",\"osmIdentifier\":\"0\",\"latitude\":\"154073000\",\"inEdges\":\"\",\"longitude\":\"-613749000\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.336198,15.420563],[-61.33285,15.429499]]},\"properties\":{\"identifier\":\"1000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"1\",\"startNode\":\"1\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"2\",\"parentRelations\":\"1-a\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.33285,15.429499],[-61.336198,15.420563]]},\"properties\":{\"identifier\":\"-1000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"1\",\"startNode\":\"2\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"1\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.33285,15.429499],[-61.3041,15.4855]]},\"properties\":{\"identifier\":\"2000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"2\",\"startNode\":\"2\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"3\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.3749,15.4073],[-61.336198,15.420563]]},\"properties\":{\"identifier\":\"8000000\",\"itemType\":\"EDGE\",\"osmIdentifier\":\"8\",\"startNode\":\"8\",\"shard\":\"unknown\",\"highway\":\"secondary\",\"endNode\":\"1\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3112,15.3907],[-61.336198,15.420563]]]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"AREA\",\"osmIdentifier\":\"0\",\"landuse\":\"residential\",\"shard\":\"unknown\",\"parentRelations\":\"2-b, 3-c\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3041,15.4855],[-61.336198,15.420563]]]},\"properties\":{\"landuse\":\"residential\",\"identifier\":\"2\",\"itemType\":\"AREA\",\"shard\":\"unknown\",\"osmIdentifier\":\"0\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3112,15.3907]]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"LINE\",\"power\":\"line\",\"shard\":\"unknown\",\"osmIdentifier\":\"0\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-61.336198,15.420563],[-61.33285,15.429499],[-61.3041,15.4855]]},\"properties\":{\"identifier\":\"2\",\"itemType\":\"LINE\",\"power\":\"line\",\"shard\":\"unknown\",\"osmIdentifier\":\"0\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.336198,15.420563]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154205630\",\"longitude\":\"-613361980\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-61.33285,15.429499]},\"properties\":{\"identifier\":\"2\",\"itemType\":\"POINT\",\"osmIdentifier\":\"0\",\"latitude\":\"154294990\",\"longitude\":\"-613328500\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"1\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=1, [Members: {Member: ID = 1000000, Type = EDGE, Role = a}], [Tags: [type => relation]]]\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"2\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=2, [Members: {Member: ID = 1, Type = AREA, Role = b}], [Tags: [type => relation]]]\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0]},\"properties\":{\"identifier\":\"3\",\"itemType\":\"RELATION\",\"osmIdentifier\":\"0\",\"shard\":\"unknown\",\"type\":\"relation\",\"relation\":\"[Relation: id=3, [Members: {Member: ID = 1, Type = AREA, Role = c}], [Tags: [type => relation]]]\"}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/dynamic/rules/z12x1350y1870.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-61.3476562,15.3689499],[-61.3476562,15.4536802],[-61.2597656,15.4536802],[-61.2597656,15.3689499]]]},\"properties\":{}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/InnerOuterMultiPolygon.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='319527568' timestamp='2011-02-26T01:25:27Z' uid='348674' user='Cabeleira' visible='true' version='28' changeset='7396054' lat='50.382549' lon='30.4699681' />\n  <node id='319527569' timestamp='2011-02-26T01:25:24Z' uid='348674' user='Cabeleira' visible='true' version='18' changeset='7396054' lat='50.3828525' lon='30.470569' />\n  <node id='319527570' timestamp='2016-08-16T08:30:58Z' uid='195702' user='Sergey Kozyr' visible='true' version='19' changeset='41484445' lat='50.3828423' lon='30.4706753' />\n  <node id='319527571' timestamp='2011-02-26T01:25:18Z' uid='348674' user='Cabeleira' visible='true' version='18' changeset='7396054' lat='50.3830204' lon='30.4713269' />\n  <node id='319527572' timestamp='2011-02-26T01:25:15Z' uid='348674' user='Cabeleira' visible='true' version='18' changeset='7396054' lat='50.3834988' lon='30.4708626' />\n  <node id='319527573' timestamp='2011-02-26T01:25:25Z' uid='348674' user='Cabeleira' visible='true' version='18' changeset='7396054' lat='50.3833765' lon='30.4706572' />\n  <node id='319527574' timestamp='2011-02-26T01:25:22Z' uid='348674' user='Cabeleira' visible='true' version='18' changeset='7396054' lat='50.3831491' lon='30.4700956' />\n  <node id='319527575' timestamp='2011-02-26T01:25:19Z' uid='348674' user='Cabeleira' visible='true' version='18' changeset='7396054' lat='50.3829444' lon='30.4695811' />\n  <node id='500019501' timestamp='2011-02-26T01:25:17Z' uid='348674' user='Cabeleira' visible='true' version='5' changeset='7396054' lat='50.383167' lon='30.4702887' />\n  <node id='500019502' timestamp='2011-02-26T01:25:25Z' uid='348674' user='Cabeleira' visible='true' version='5' changeset='7396054' lat='50.3834057' lon='30.4706286' />\n  <node id='500019503' timestamp='2011-02-26T01:25:23Z' uid='348674' user='Cabeleira' visible='true' version='5' changeset='7396054' lat='50.383384' lon='30.4709749' />\n  <node id='500019504' timestamp='2011-02-26T01:25:15Z' uid='348674' user='Cabeleira' visible='true' version='5' changeset='7396054' lat='50.3831323' lon='30.4712174' />\n  <node id='500019505' timestamp='2011-02-26T01:25:25Z' uid='348674' user='Cabeleira' visible='true' version='5' changeset='7396054' lat='50.3834738' lon='30.4712007' />\n  <node id='500019507' timestamp='2011-02-26T01:25:19Z' uid='348674' user='Cabeleira' visible='true' version='5' changeset='7396054' lat='50.3832232' lon='30.471446' />\n  <node id='960023629' timestamp='2014-08-10T13:56:27Z' uid='440812' user='dudka' visible='true' version='7' changeset='24655476' lat='50.3833635' lon='30.4713087'>\n    <tag k='entrance' v='main' />\n  </node>\n  <node id='1145316647' timestamp='2013-07-23T15:54:07Z' uid='440812' user='dudka' visible='true' version='4' changeset='17063858' lat='50.3829672' lon='30.4711932' />\n  <node id='1145316654' timestamp='2013-07-23T15:54:07Z' uid='440812' user='dudka' visible='true' version='4' changeset='17063858' lat='50.3834396' lon='30.4707138' />\n  <node id='1173431865' timestamp='2011-02-25T23:52:40Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.383151' lon='30.4703961' />\n  <node id='1173431872' timestamp='2011-02-25T23:52:40Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3831643' lon='30.4705576' />\n  <node id='1173431875' timestamp='2011-02-26T01:25:25Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.383234' lon='30.4702991' />\n  <node id='1173431876' timestamp='2011-02-25T23:52:40Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3830373' lon='30.4700995' />\n  <node id='1173431878' timestamp='2011-02-25T23:52:40Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3828511' lon='30.4699949' />\n  <node id='1173431879' timestamp='2011-02-25T23:52:40Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829571' lon='30.4706964' />\n  <node id='1173431883' timestamp='2011-02-25T23:52:40Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.383288' lon='30.4707458' />\n  <node id='1173431885' timestamp='2011-02-25T23:52:40Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3827665' lon='30.4701652' />\n  <node id='1173431886' timestamp='2011-02-26T01:25:27Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827776' lon='30.4704494' />\n  <node id='1173431894' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3832078' lon='30.470584' />\n  <node id='1173431897' timestamp='2011-02-26T01:25:23Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3832194' lon='30.4702374' />\n  <node id='1173431898' timestamp='2011-02-26T01:25:20Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3828052' lon='30.4706466' />\n  <node id='1173431900' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3830232' lon='30.4707808' />\n  <node id='1173431902' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3827797' lon='30.4702078' />\n  <node id='1173431903' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3830237' lon='30.4707078' />\n  <node id='1173431905' timestamp='2011-02-26T01:25:15Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3828809' lon='30.4706404' />\n  <node id='1173431917' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829721' lon='30.4699986' />\n  <node id='1173431918' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3828255' lon='30.4700229' />\n  <node id='1173431921' timestamp='2011-02-26T01:25:23Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827696' lon='30.4704293' />\n  <node id='1173431924' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829543' lon='30.4699002' />\n  <node id='1173431927' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3830674' lon='30.4709568' />\n  <node id='1173431930' timestamp='2011-02-25T23:52:41Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3830604' lon='30.4705303' />\n  <node id='1173431933' timestamp='2011-02-26T01:25:21Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827308' lon='30.4705796' />\n  <node id='1173431934' timestamp='2011-02-26T01:25:18Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3831718' lon='30.4700734' />\n  <node id='1173431944' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829628' lon='30.4699511' />\n  <node id='1173431946' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3827409' lon='30.4701592' />\n  <node id='1173431947' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3831789' lon='30.4704588' />\n  <node id='1173431948' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3832286' lon='30.4705823' />\n  <node id='1173431950' timestamp='2011-02-26T01:25:17Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827265' lon='30.4706104' />\n  <node id='1173431952' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3828053' lon='30.4703161' />\n  <node id='1173431954' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829243' lon='30.4706299' />\n  <node id='1173431962' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829688' lon='30.4699287' />\n  <node id='1173431965' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3827526' lon='30.4701567' />\n  <node id='1173431967' timestamp='2011-02-26T01:25:22Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827429' lon='30.4704554' />\n  <node id='1173431970' timestamp='2011-02-26T01:25:16Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3831887' lon='30.4703434' />\n  <node id='1173431971' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829992' lon='30.4700034' />\n  <node id='1173431974' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3828433' lon='30.469973' />\n  <node id='1173431977' timestamp='2011-02-26T01:25:17Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827589' lon='30.4706919' />\n  <node id='1173431985' timestamp='2011-02-25T23:52:42Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3831707' lon='30.4708345' />\n  <node id='1173431986' timestamp='2011-02-25T23:52:43Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3827774' lon='30.4701823' />\n  <node id='1173431988' timestamp='2011-02-26T01:25:17Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3826845' lon='30.4705405' />\n  <node id='1173431990' timestamp='2011-02-26T01:25:25Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3831835' lon='30.4701472' />\n  <node id='1173431993' timestamp='2011-02-26T01:25:24Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827962' lon='30.4706241' />\n  <node id='1173431995' timestamp='2011-02-25T23:52:43Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829868' lon='30.4700047' />\n  <node id='1173431996' timestamp='2011-02-25T23:52:43Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3828177' lon='30.4699998' />\n  <node id='1173431999' timestamp='2011-02-25T23:52:43Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3830068' lon='30.4708142' />\n  <node id='1173432006' timestamp='2011-02-26T01:25:15Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827087' lon='30.4706012' />\n  <node id='1173432011' timestamp='2011-02-26T01:25:20Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3831962' lon='30.4701348' />\n  <node id='1173432012' timestamp='2011-02-25T23:52:43Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3829643' lon='30.4699767' />\n  <node id='1173432015' timestamp='2011-02-25T23:52:43Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3827254' lon='30.4701178' />\n  <node id='1173432017' timestamp='2011-02-26T01:25:24Z' uid='348674' user='Cabeleira' visible='true' version='2' changeset='7396054' lat='50.3827384' lon='30.4705988' />\n  <node id='1173432020' timestamp='2011-02-25T23:52:43Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717' lat='50.3827696' lon='30.4702285' />\n  <node id='2394101427' timestamp='2013-07-23T15:54:04Z' uid='440812' user='dudka' visible='true' version='1' changeset='17063858' lat='50.3829838' lon='30.471235' />\n  <node id='2394101428' timestamp='2013-07-23T15:54:04Z' uid='440812' user='dudka' visible='true' version='1' changeset='17063858' lat='50.3834607' lon='30.4707669' />\n  <node id='4352308595' timestamp='2016-08-16T08:30:55Z' uid='195702' user='Sergey Kozyr' visible='true' version='1' changeset='41484445' lat='50.3829303' lon='30.4710924' />\n  <node id='4352308596' timestamp='2016-08-16T08:30:55Z' uid='195702' user='Sergey Kozyr' visible='true' version='1' changeset='41484445' lat='50.3829851' lon='30.4710353' />\n  <node id='4922108291' timestamp='2017-06-18T12:14:47Z' uid='1676637' user='Kilkenni' visible='true' version='1' changeset='49633905' lat='50.3833247' lon='30.4713465'>\n    <tag k='historic' v='memorial' />\n    <tag k='memorial' v='plaque' />\n    <tag k='name' v='Глушков Віктор Михайлович' />\n  </node>\n  <node id='4922108343' timestamp='2017-06-20T02:06:53Z' uid='1676637' user='Kilkenni' visible='true' version='2' changeset='49681340' lat='50.3832823' lon='30.4704205' />\n  <node id='4922108346' timestamp='2017-06-20T02:06:53Z' uid='1676637' user='Kilkenni' visible='true' version='2' changeset='49681340' lat='50.382883' lon='30.4707861' />\n  <node id='4922108362' timestamp='2017-06-18T12:14:49Z' uid='1676637' user='Kilkenni' visible='true' version='1' changeset='49633905' lat='50.3834102' lon='30.4712629'>\n    <tag k='historic' v='memorial' />\n    <tag k='inscription' v='Академік, фундатор факультету кібернетики' />\n    <tag k='memorial' v='plaque' />\n    <tag k='name' v='Ляшко Іван Іванович' />\n  </node>\n  <way id='29050173' timestamp='2017-06-18T12:14:55Z' uid='1676637' user='Kilkenni' visible='true' version='23' changeset='49633905'>\n    <nd ref='319527573' />\n    <nd ref='4922108343' />\n    <nd ref='1173431875' />\n    <nd ref='1173431970' />\n    <nd ref='500019501' />\n    <nd ref='1173431897' />\n    <nd ref='1173431990' />\n    <nd ref='1173432011' />\n    <nd ref='1173431934' />\n    <nd ref='319527574' />\n    <nd ref='319527575' />\n    <nd ref='319527568' />\n    <nd ref='1173431967' />\n    <nd ref='1173431921' />\n    <nd ref='1173431886' />\n    <nd ref='1173431988' />\n    <nd ref='1173432006' />\n    <nd ref='1173431933' />\n    <nd ref='1173432017' />\n    <nd ref='1173431950' />\n    <nd ref='1173431977' />\n    <nd ref='1173431898' />\n    <nd ref='1173431993' />\n    <nd ref='319527569' />\n    <nd ref='1173431905' />\n    <nd ref='319527570' />\n    <nd ref='4922108346' />\n    <nd ref='4352308596' />\n  </way>\n  <way id='101626438' timestamp='2011-02-25T23:52:44Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717'>\n    <nd ref='1173431894' />\n    <nd ref='1173431930' />\n    <nd ref='1173431900' />\n    <nd ref='1173431985' />\n    <nd ref='1173431894' />\n  </way>\n  <way id='101626441' timestamp='2016-08-16T08:30:57Z' uid='195702' user='Sergey Kozyr' visible='true' version='2' changeset='41484445'>\n    <nd ref='1173431927' />\n    <nd ref='1173431999' />\n    <nd ref='1173431903' />\n    <nd ref='1173431879' />\n    <nd ref='1173431954' />\n    <nd ref='1173431865' />\n    <nd ref='1173431947' />\n    <nd ref='1173431872' />\n    <nd ref='1173431948' />\n    <nd ref='1173431883' />\n  </way>\n  <way id='101626442' timestamp='2011-02-25T23:52:44Z' uid='348674' user='Cabeleira' visible='true' version='1' changeset='7395717'>\n    <nd ref='1173431924' />\n    <nd ref='1173431962' />\n    <nd ref='1173431944' />\n    <nd ref='1173432012' />\n    <nd ref='1173431917' />\n    <nd ref='1173431995' />\n    <nd ref='1173431971' />\n    <nd ref='1173431876' />\n    <nd ref='1173431952' />\n    <nd ref='1173432020' />\n    <nd ref='1173431902' />\n    <nd ref='1173431986' />\n    <nd ref='1173431885' />\n    <nd ref='1173431965' />\n    <nd ref='1173431946' />\n    <nd ref='1173432015' />\n    <nd ref='1173431918' />\n    <nd ref='1173431996' />\n    <nd ref='1173431974' />\n    <nd ref='1173431878' />\n    <nd ref='1173431924' />\n  </way>\n  <way id='437416740' timestamp='2017-06-18T12:14:53Z' uid='1676637' user='Kilkenni' visible='true' version='2' changeset='49633905'>\n    <nd ref='4352308596' />\n    <nd ref='4352308595' />\n    <nd ref='1145316647' />\n    <nd ref='2394101427' />\n    <nd ref='319527571' />\n    <nd ref='500019504' />\n    <nd ref='500019507' />\n    <nd ref='4922108291' />\n    <nd ref='960023629' />\n    <nd ref='4922108362' />\n    <nd ref='500019505' />\n    <nd ref='500019503' />\n    <nd ref='319527572' />\n    <nd ref='2394101428' />\n    <nd ref='1145316654' />\n    <nd ref='500019502' />\n    <nd ref='319527573' />\n  </way>\n  <way id='437416746' timestamp='2016-08-16T08:30:56Z' uid='195702' user='Sergey Kozyr' visible='true' version='1' changeset='41484445'>\n    <nd ref='1173431883' />\n    <nd ref='1173431927' />\n  </way>\n  <relation id='1418946' timestamp='2018-11-11T01:50:12Z' uid='1538111' user='avinet_ua' visible='true' version='88' changeset='64365574'>\n    <member type='way' ref='101812132' role='house' />\n    <member type='way' ref='233930520' role='house' />\n    <member type='way' ref='82615342' role='house' />\n    <member type='way' ref='29050055' role='house' />\n    <member type='way' ref='82482449' role='house' />\n    <member type='way' ref='101812136' role='house' />\n    <member type='way' ref='29050036' role='house' />\n    <member type='way' ref='101646798' role='house' />\n    <member type='relation' ref='1447400' role='house' />\n    <member type='way' ref='101646794' role='house' />\n    <member type='relation' ref='1447399' role='house' />\n    <member type='relation' ref='1447306' role='house' />\n    <member type='relation' ref='1447398' role='house' />\n    <member type='relation' ref='1448570' role='house' />\n    <member type='way' ref='439798295' role='house' />\n    <member type='way' ref='442067732' role='house' />\n    <member type='way' ref='313774625' role='house' />\n    <member type='way' ref='441856730' role='house' />\n    <member type='way' ref='442080075' role='house' />\n    <member type='way' ref='149210450' role='house' />\n    <member type='way' ref='109882350' role='house' />\n    <member type='way' ref='99021011' role='house' />\n    <member type='way' ref='31875997' role='house' />\n    <member type='way' ref='149210452' role='house' />\n    <member type='way' ref='147029745' role='house' />\n    <member type='way' ref='48388854' role='house' />\n    <member type='way' ref='99021025' role='house' />\n    <member type='relation' ref='6588415' role='house' />\n    <member type='way' ref='48388832' role='house' />\n    <member type='way' ref='163626159' role='house' />\n    <member type='way' ref='149210476' role='house' />\n    <member type='way' ref='163626161' role='house' />\n    <member type='way' ref='48388839' role='house' />\n    <member type='way' ref='147029737' role='house' />\n    <member type='way' ref='50159062' role='house' />\n    <member type='way' ref='99021022' role='house' />\n    <member type='way' ref='48388831' role='house' />\n    <member type='way' ref='99020992' role='house' />\n    <member type='way' ref='99021020' role='house' />\n    <member type='relation' ref='2012827' role='house' />\n    <member type='way' ref='147029738' role='house' />\n    <member type='way' ref='48388826' role='house' />\n    <member type='way' ref='99020988' role='house' />\n    <member type='way' ref='99053865' role='house' />\n    <member type='way' ref='99053870' role='house' />\n    <member type='way' ref='99053864' role='house' />\n    <member type='way' ref='48388829' role='house' />\n    <member type='way' ref='99021015' role='house' />\n    <member type='way' ref='48388830' role='house' />\n    <member type='way' ref='163626163' role='house' />\n    <member type='way' ref='48388824' role='house' />\n    <member type='way' ref='147029742' role='house' />\n    <member type='way' ref='99021002' role='house' />\n    <member type='way' ref='99020995' role='house' />\n    <member type='way' ref='140174617' role='house' />\n    <member type='way' ref='163128935' role='house' />\n    <member type='way' ref='99020996' role='house' />\n    <member type='way' ref='139088895' role='house' />\n    <member type='relation' ref='6588414' role='house' />\n    <member type='way' ref='163129537' role='house' />\n    <member type='way' ref='147029739' role='house' />\n    <member type='relation' ref='2012826' role='house' />\n    <member type='way' ref='99021001' role='house' />\n    <member type='way' ref='163636201' role='house' />\n    <member type='way' ref='163636203' role='house' />\n    <member type='way' ref='147029735' role='house' />\n    <member type='way' ref='99020984' role='house' />\n    <member type='way' ref='171324037' role='house' />\n    <member type='relation' ref='2012824' role='house' />\n    <member type='way' ref='171324038' role='house' />\n    <member type='way' ref='99020999' role='house' />\n    <member type='way' ref='99020985' role='house' />\n    <member type='node' ref='4782015422' role='house' />\n    <member type='way' ref='242946022' role='house' />\n    <member type='way' ref='4423654' role='street' />\n    <member type='way' ref='4423655' role='street' />\n    <member type='way' ref='214118572' role='street' />\n    <member type='way' ref='316122050' role='street' />\n    <member type='way' ref='75525540' role='street' />\n    <member type='way' ref='27106794' role='street' />\n    <member type='way' ref='487501773' role='street' />\n    <member type='way' ref='487501774' role='street' />\n    <member type='way' ref='130675775' role='street' />\n    <member type='way' ref='97947894' role='street' />\n    <member type='way' ref='27106791' role='street' />\n    <member type='way' ref='31875944' role='street' />\n    <tag k='addr:street' v='Академіка Глушкова проспект' />\n    <tag k='name' v='Академіка Глушкова проспект' />\n    <tag k='type' v='associatedStreet' />\n    <tag k='wikidata' v='Q4381056' />\n    <tag k='wikipedia' v='uk:Проспект Академіка Глушкова' />\n  </relation>\n  <relation id='1447306' timestamp='2016-08-16T08:30:56Z' uid='195702' user='Sergey Kozyr' visible='true' version='13' changeset='41484445'>\n    <member type='way' ref='29050173' role='outer' />\n    <member type='way' ref='437416740' role='outer' />\n    <member type='way' ref='101626442' role='inner' />\n    <member type='way' ref='437416746' role='inner' />\n    <member type='way' ref='101626441' role='inner' />\n    <member type='way' ref='101626438' role='outer' />\n    <tag k='addr:housenumber' v='4д' />\n    <tag k='addr:street' v='Академіка Глушкова проспект' />\n    <tag k='amenity' v='university' />\n    <tag k='building' v='yes' />\n    <tag k='building:levels' v='2' />\n    <tag k='name' v='Факультет кібернетики, Факультет соціології' />\n    <tag k='name:de' v='Fakultät für Kybernetik' />\n    <tag k='name:en' v='Faculty of Cybernetics, Faculty of Sociology' />\n    <tag k='name:ru' v='Факультет кибернетики, Факультет социологии' />\n    <tag k='name:uk' v='Факультет кібернетики, Факультет соціології' />\n    <tag k='operator' v='Київський національний університет імені Тараса Шевченка' />\n    <tag k='type' v='multipolygon' />\n    <tag k='website' v='www.unicyb.kiev.ua' />\n    <tag k='website_1' v='http://www.socd.univ.kiev.ua/' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/dnk-link-road-test.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <bounds minlat='57.0319807' minlon='9.9289517' maxlat='57.0321125' maxlon='9.9300593' origin='CGImap 0.8.3 (1179882 spike-08.openstreetmap.org)' />\n  <node id='29971833' timestamp='2019-04-02T12:57:40Z' uid='2677992' user='mikkolukas' visible='true' version='11' changeset='68790000' lat='57.0321597' lon='9.9292081' />\n  <node id='29971835' timestamp='2019-04-02T12:57:40Z' uid='2677992' user='mikkolukas' visible='true' version='10' changeset='68790000' lat='57.0320924' lon='9.9294598' />\n  <node id='442610985' timestamp='2019-04-02T12:57:40Z' uid='2677992' user='mikkolukas' visible='true' version='7' changeset='68790000' lat='57.0320107' lon='9.9296425'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='1521053431' timestamp='2015-11-06T20:51:36Z' uid='207581' user='Hjart' visible='true' version='2' changeset='35135124' lat='57.031794' lon='9.9297951' />\n  <node id='1601720631' timestamp='2019-04-02T12:57:40Z' uid='2677992' user='mikkolukas' visible='true' version='2' changeset='68790000' lat='57.0315335' lon='9.928738' />\n  <node id='2797986675' timestamp='2019-04-02T12:57:40Z' uid='2677992' user='mikkolukas' visible='true' version='2' changeset='68790000' lat='57.0329951' lon='9.9297313' />\n  <node id='2797986688' timestamp='2020-09-25T16:29:28Z' uid='130539' user='pelleg' visible='true' version='5' changeset='91521244' lat='57.032195' lon='9.9290344'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='4064259483' timestamp='2016-03-16T22:31:38Z' uid='207581' user='Hjart' visible='true' version='1' changeset='37884895' lat='57.03206' lon='9.9302473' />\n  <node id='4064259485' timestamp='2016-03-16T22:31:38Z' uid='207581' user='Hjart' visible='true' version='1' changeset='37884895' lat='57.0321125' lon='9.9300593' />\n  <node id='4064259494' timestamp='2016-03-16T22:31:38Z' uid='207581' user='Hjart' visible='true' version='1' changeset='37884895' lat='57.0323262' lon='9.9304987' />\n  <node id='4064259497' timestamp='2016-03-16T22:31:38Z' uid='207581' user='Hjart' visible='true' version='1' changeset='37884895' lat='57.0323788' lon='9.9303107' />\n  <node id='4877055179' timestamp='2019-04-02T12:57:41Z' uid='2677992' user='mikkolukas' visible='true' version='2' changeset='68790000' lat='57.0311228' lon='9.9287425' />\n  <node id='4877055180' timestamp='2019-04-02T12:57:41Z' uid='2677992' user='mikkolukas' visible='true' version='3' changeset='68790000' lat='57.0318318' lon='9.9291475' />\n  <node id='4877055182' timestamp='2020-09-25T16:29:26Z' uid='130539' user='pelleg' visible='true' version='3' changeset='91521244' lat='57.0319119' lon='9.9292107'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='4877055189' timestamp='2019-04-02T12:57:41Z' uid='2677992' user='mikkolukas' visible='true' version='3' changeset='68790000' lat='57.0323004' lon='9.9294345' />\n  <node id='4877055190' timestamp='2020-09-25T16:29:19Z' uid='130539' user='pelleg' visible='true' version='3' changeset='91521244' lat='57.0322065' lon='9.929373'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='4877055196' timestamp='2019-04-02T12:57:41Z' uid='2677992' user='mikkolukas' visible='true' version='2' changeset='68790000' lat='57.0319668' lon='9.929696' />\n  <node id='4882830820' timestamp='2019-04-02T12:57:41Z' uid='2677992' user='mikkolukas' visible='true' version='3' changeset='68790000' lat='57.0321285' lon='9.9288659'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='4882835924' timestamp='2019-04-02T12:57:41Z' uid='2677992' user='mikkolukas' visible='true' version='3' changeset='68790000' lat='57.032158' lon='9.9284515' />\n  <node id='4941958052' timestamp='2017-06-29T10:36:01Z' uid='6193405' user='hyggemap' visible='true' version='1' changeset='49914723' lat='57.033044' lon='9.93004' />\n  <node id='6378459036' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0320398' lon='9.9297188' />\n  <node id='6378459037' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0321726' lon='9.9297899' />\n  <node id='6378459038' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0322997' lon='9.9297644' />\n  <node id='6378459040' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0321099' lon='9.9292582' />\n  <node id='6378459041' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0320566' lon='9.9292696' />\n  <node id='6378459042' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0320807' lon='9.9293259' />\n  <node id='6378459043' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.032031' lon='9.9293406' />\n  <node id='6378459044' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0318464' lon='9.9294761' />\n  <node id='6378459047' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0328045' lon='9.9300144' />\n  <node id='6378459048' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.032528' lon='9.9299334' />\n  <node id='6378459049' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0323821' lon='9.9298422' />\n  <node id='6378459050' timestamp='2020-09-25T16:29:29Z' uid='130539' user='pelleg' visible='true' version='2' changeset='91521244' lat='57.0321376' lon='9.9296115'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6378459053' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0316063' lon='9.928591' />\n  <node id='6378459054' timestamp='2019-04-02T12:57:36Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0317442' lon='9.9286969' />\n  <node id='6378459055' timestamp='2020-09-25T16:29:25Z' uid='130539' user='pelleg' visible='true' version='2' changeset='91521244' lat='57.0319807' lon='9.9289517'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6378459067' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0320549' lon='9.9291336' />\n  <node id='6378459068' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0322229' lon='9.9293838'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='6378459069' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0318975' lon='9.9291993'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='6378459071' timestamp='2020-09-25T16:29:16Z' uid='130539' user='pelleg' visible='true' version='2' changeset='91521244' lat='57.0321677' lon='9.9295087'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6378459072' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0320137' lon='9.9292614' />\n  <node id='6378459073' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0321266' lon='9.9293316' />\n  <node id='6378459074' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0319732' lon='9.9293872' />\n  <node id='6378459081' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0320653' lon='9.9295451' />\n  <node id='6378459082' timestamp='2020-09-25T16:29:11Z' uid='130539' user='pelleg' visible='true' version='2' changeset='91521244' lat='57.0320253' lon='9.9296201'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6378459186' timestamp='2020-09-25T16:29:08Z' uid='130539' user='pelleg' visible='true' version='2' changeset='91521244' lat='57.031926' lon='9.9295367'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6378459187' timestamp='2020-09-25T16:29:24Z' uid='130539' user='pelleg' visible='true' version='2' changeset='91521244' lat='57.0321084' lon='9.9289678'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6378459188' timestamp='2020-09-25T16:29:21Z' uid='130539' user='pelleg' visible='true' version='2' changeset='91521244' lat='57.0318822' lon='9.9293319'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6378459189' timestamp='2020-09-25T16:29:22Z' uid='130539' user='pelleg' visible='true' version='2' changeset='91521244' lat='57.0319519' lon='9.9290603'>\n    <tag k='button_operated' v='yes' />\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6378459190' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0315564' lon='9.928756'>\n    <tag k='bus' v='yes' />\n    <tag k='name' v='Vandværket (Sønderbro / Aalborg)' />\n    <tag k='public_transport' v='stop_position' />\n    <tag k='ref' v='851210201' />\n  </node>\n  <node id='6378459191' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0323135' lon='9.9296035' />\n  <node id='6378459192' timestamp='2019-04-02T12:57:37Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000' lat='57.0325524' lon='9.9297534'>\n    <tag k='bus' v='yes' />\n    <tag k='name' v='Vandværket (Sønderbro / Aalborg)' />\n    <tag k='public_transport' v='stop_position' />\n    <tag k='ref' v='851210202' />\n  </node>\n  <way id='404150994' timestamp='2016-03-16T22:31:45Z' uid='207581' user='Hjart' visible='true' version='1' changeset='37884895'>\n    <nd ref='4064259494' />\n    <nd ref='4064259483' />\n    <nd ref='4064259485' />\n    <nd ref='4064259497' />\n    <nd ref='4064259494' />\n    <tag k='building' v='commercial' />\n  </way>\n  <way id='495987822' timestamp='2020-03-30T07:15:55Z' uid='379467' user='osmviborg' visible='true' version='6' changeset='82808253'>\n    <nd ref='4877055179' />\n    <nd ref='4877055180' />\n    <nd ref='6378459069' />\n    <nd ref='4877055182' />\n    <nd ref='6378459072' />\n    <nd ref='6378459041' />\n    <nd ref='6378459040' />\n    <nd ref='29971833' />\n    <tag k='destination:lanes' v='Kærby' />\n    <tag k='highway' v='tertiary_link' />\n    <tag k='lanes' v='1' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='60' />\n    <tag k='oneway' v='yes' />\n    <tag k='source:maxspeed' v='sign' />\n    <tag k='surface' v='asphalt' />\n    <tag k='turn:lanes' v='left' />\n  </way>\n  <way id='495987823' timestamp='2020-01-12T15:12:34Z' uid='1288' user='Niels Elgaard Larsen' visible='true' version='5' changeset='79478475'>\n    <nd ref='29971835' />\n    <nd ref='6378459073' />\n    <nd ref='29971833' />\n    <tag k='highway' v='tertiary_link' />\n    <tag k='lanes' v='1' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='source:maxspeed' v='DK:urban' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='495987824' timestamp='2020-03-30T07:15:55Z' uid='379467' user='osmviborg' visible='true' version='6' changeset='82808253'>\n    <nd ref='2797986675' />\n    <nd ref='4877055189' />\n    <nd ref='6378459068' />\n    <nd ref='4877055190' />\n    <nd ref='6378459073' />\n    <nd ref='6378459042' />\n    <nd ref='6378459043' />\n    <nd ref='6378459074' />\n    <tag k='highway' v='tertiary_link' />\n    <tag k='lanes' v='1' />\n    <tag k='maxspeed' v='60' />\n    <tag k='oneway' v='yes' />\n    <tag k='source:maxspeed' v='sign' />\n    <tag k='surface' v='asphalt' />\n    <tag k='turn:lanes' v='left' />\n  </way>\n  <way id='496249591' timestamp='2019-04-02T12:57:43Z' uid='2677992' user='mikkolukas' visible='true' version='2' changeset='68790000'>\n    <nd ref='6378459067' />\n    <nd ref='6378459189' />\n    <nd ref='6378459190' />\n    <nd ref='1601720631' />\n    <tag k='bicycle' v='use_sidepath' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='60' />\n    <tag k='name' v='Sønderbro' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='496588159' timestamp='2020-07-31T20:19:05Z' uid='130539' user='pelleg' visible='true' version='6' changeset='88796988'>\n    <nd ref='4882835924' />\n    <nd ref='4882830820' />\n    <nd ref='6378459187' />\n    <nd ref='6378459067' />\n    <tag k='cycleway:right' v='lane' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='3' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Ny Kærvej' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='surface' v='asphalt' />\n    <tag k='turn:lanes' v='left||right' />\n  </way>\n  <way id='498604201' timestamp='2020-03-30T07:15:55Z' uid='379467' user='osmviborg' visible='true' version='5' changeset='82808253'>\n    <nd ref='1521053431' />\n    <nd ref='4877055196' />\n    <nd ref='442610985' />\n    <nd ref='6378459082' />\n    <nd ref='6378459081' />\n    <nd ref='29971835' />\n    <tag k='cycleway:right' v='track' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Gammel Gugvej' />\n    <tag k='oneway' v='yes' />\n    <tag k='placement' v='middle_of:2' />\n    <tag k='sidewalk' v='right' />\n    <tag k='source:maxspeed' v='DK:urban' />\n    <tag k='surface' v='asphalt' />\n    <tag k='turn:lanes' v='left|' />\n  </way>\n  <way id='681139358' timestamp='2020-07-31T20:19:44Z' uid='130539' user='pelleg' visible='true' version='3' changeset='88796988'>\n    <nd ref='6378459082' />\n    <nd ref='6378459036' />\n    <nd ref='6378459037' />\n    <nd ref='6378459038' />\n    <tag k='highway' v='footway' />\n    <tag k='lit' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='681139362' timestamp='2020-06-01T16:54:15Z' uid='107686' user='owiecc' visible='true' version='2' changeset='86055657'>\n    <nd ref='29971833' />\n    <nd ref='6378459067' />\n    <tag k='cycleway:right' v='track' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='60' />\n    <tag k='name' v='Sønderbro' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='681139365' timestamp='2019-04-02T12:57:38Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000'>\n    <nd ref='29971835' />\n    <nd ref='6378459071' />\n    <nd ref='6378459191' />\n    <nd ref='6378459192' />\n    <nd ref='4941958052' />\n    <tag k='bicycle' v='use_sidepath' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='60' />\n    <tag k='name' v='Sønderbro' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='681139367' timestamp='2020-07-31T20:19:47Z' uid='130539' user='pelleg' visible='true' version='3' changeset='88796988'>\n    <nd ref='29971835' />\n    <nd ref='6378459050' />\n    <nd ref='6378459038' />\n    <nd ref='6378459049' />\n    <nd ref='6378459048' />\n    <nd ref='6378459047' />\n    <nd ref='4941958052' />\n    <tag k='highway' v='cycleway' />\n    <tag k='lit' v='yes' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='681139368' timestamp='2020-07-31T20:20:07Z' uid='130539' user='pelleg' visible='true' version='3' changeset='88796988'>\n    <nd ref='6378459067' />\n    <nd ref='6378459055' />\n    <nd ref='6378459054' />\n    <nd ref='6378459053' />\n    <tag k='highway' v='cycleway' />\n    <tag k='lit' v='no' />\n    <tag k='oneway' v='yes' />\n    <tag k='sidewalk' v='right' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='681139380' timestamp='2020-01-12T15:12:34Z' uid='1288' user='Niels Elgaard Larsen' visible='true' version='2' changeset='79478475'>\n    <nd ref='6378459067' />\n    <nd ref='6378459072' />\n    <nd ref='6378459074' />\n    <tag k='highway' v='tertiary_link' />\n    <tag k='lanes' v='2' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n    <tag k='turn:lanes' v='left|' />\n  </way>\n  <way id='681139383' timestamp='2020-07-31T20:19:24Z' uid='130539' user='pelleg' visible='true' version='3' changeset='88796988'>\n    <nd ref='6378459044' />\n    <nd ref='6378459186' />\n    <nd ref='6378459082' />\n    <tag k='footway' v='crossing' />\n    <tag k='highway' v='footway' />\n    <tag k='lit' v='no' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='681139385' timestamp='2020-07-31T20:19:07Z' uid='130539' user='pelleg' visible='true' version='3' changeset='88796988'>\n    <nd ref='2797986688' />\n    <nd ref='6378459187' />\n    <tag k='footway' v='crossing' />\n    <tag k='highway' v='footway' />\n    <tag k='lit' v='no' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='681139387' timestamp='2020-07-31T20:19:17Z' uid='130539' user='pelleg' visible='true' version='3' changeset='88796988'>\n    <nd ref='6378459055' />\n    <nd ref='6378459189' />\n    <nd ref='4877055182' />\n    <nd ref='6378459188' />\n    <nd ref='6378459044' />\n    <tag k='footway' v='crossing' />\n    <tag k='highway' v='footway' />\n    <tag k='lit' v='no' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='681143380' timestamp='2019-04-02T13:18:13Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000'>\n    <nd ref='6378459074' />\n    <nd ref='29971835' />\n    <tag k='highway' v='tertiary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='60' />\n    <tag k='name' v='Sønderbro' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <relation id='1247020' timestamp='2020-09-27T19:24:56Z' uid='11884401' user='LarsKristensen' visible='true' version='460' changeset='91595179'>\n    <member type='way' ref='689333385' role='' />\n    <member type='way' ref='689333383' role='' />\n    <member type='way' ref='689333390' role='' />\n    <member type='way' ref='689333382' role='' />\n    <member type='way' ref='742025418' role='' />\n    <member type='way' ref='340537021' role='' />\n    <member type='way' ref='666326573' role='' />\n    <member type='way' ref='666326572' role='' />\n    <member type='way' ref='666326583' role='' />\n    <member type='way' ref='340537020' role='' />\n    <member type='way' ref='340537022' role='' />\n    <member type='way' ref='340537014' role='' />\n    <member type='way' ref='340537015' role='' />\n    <member type='way' ref='339641078' role='' />\n    <member type='way' ref='13799577' role='' />\n    <member type='way' ref='13799576' role='' />\n    <member type='way' ref='100822679' role='' />\n    <member type='way' ref='100822677' role='' />\n    <member type='way' ref='13799575' role='' />\n    <member type='way' ref='44164794' role='' />\n    <member type='way' ref='44164796' role='' />\n    <member type='way' ref='44164797' role='' />\n    <member type='way' ref='90231643' role='' />\n    <member type='way' ref='25631926' role='' />\n    <member type='way' ref='35773013' role='' />\n    <member type='way' ref='100824355' role='' />\n    <member type='way' ref='100824364' role='' />\n    <member type='way' ref='35773014' role='' />\n    <member type='way' ref='25631931' role='' />\n    <member type='way' ref='25631925' role='' />\n    <member type='way' ref='83239563' role='' />\n    <member type='way' ref='114395327' role='' />\n    <member type='way' ref='114395326' role='' />\n    <member type='way' ref='114395328' role='' />\n    <member type='way' ref='126866621' role='' />\n    <member type='way' ref='126866619' role='' />\n    <member type='way' ref='126866620' role='' />\n    <member type='way' ref='126711341' role='' />\n    <member type='way' ref='126711337' role='' />\n    <member type='way' ref='83239568' role='' />\n    <member type='way' ref='83239565' role='' />\n    <member type='way' ref='126711357' role='' />\n    <member type='way' ref='126711354' role='' />\n    <member type='way' ref='83239557' role='' />\n    <member type='way' ref='301618700' role='' />\n    <member type='way' ref='99785123' role='' />\n    <member type='way' ref='100774858' role='' />\n    <member type='way' ref='301618708' role='' />\n    <member type='way' ref='188895442' role='' />\n    <member type='way' ref='367512239' role='' />\n    <member type='way' ref='367197067' role='backward' />\n    <member type='way' ref='301904611' role='backward' />\n    <member type='way' ref='367197069' role='backward' />\n    <member type='way' ref='106879548' role='' />\n    <member type='way' ref='105008544' role='' />\n    <member type='way' ref='498599201' role='' />\n    <member type='way' ref='171052824' role='' />\n    <member type='way' ref='498599203' role='' />\n    <member type='way' ref='4579009' role='' />\n    <member type='way' ref='106879549' role='' />\n    <member type='way' ref='339319642' role='' />\n    <member type='way' ref='105008547' role='' />\n    <member type='way' ref='671174214' role='' />\n    <member type='way' ref='107101181' role='' />\n    <member type='way' ref='339323421' role='' />\n    <member type='way' ref='339324235' role='' />\n    <member type='way' ref='681993519' role='' />\n    <member type='way' ref='681994757' role='' />\n    <member type='way' ref='681994759' role='' />\n    <member type='way' ref='681994758' role='' />\n    <member type='way' ref='32126839' role='' />\n    <member type='way' ref='681278519' role='' />\n    <member type='way' ref='681278520' role='' />\n    <member type='way' ref='96927048' role='' />\n    <member type='way' ref='681278517' role='' />\n    <member type='way' ref='666342339' role='' />\n    <member type='way' ref='666342353' role='' />\n    <member type='way' ref='666342388' role='' />\n    <member type='way' ref='666342389' role='' />\n    <member type='way' ref='671174228' role='' />\n    <member type='way' ref='671174227' role='' />\n    <member type='way' ref='671174238' role='' />\n    <member type='way' ref='671174235' role='' />\n    <member type='way' ref='107092824' role='' />\n    <member type='way' ref='671174242' role='' />\n    <member type='way' ref='671174241' role='' />\n    <member type='way' ref='32425214' role='' />\n    <member type='way' ref='107092834' role='' />\n    <member type='way' ref='671174243' role='' />\n    <member type='way' ref='32425215' role='' />\n    <member type='way' ref='107092825' role='' />\n    <member type='way' ref='32425216' role='' />\n    <member type='way' ref='671174251' role='' />\n    <member type='way' ref='671174250' role='' />\n    <member type='way' ref='242403463' role='' />\n    <member type='way' ref='671174248' role='' />\n    <member type='way' ref='242403461' role='' />\n    <member type='way' ref='671174262' role='' />\n    <member type='way' ref='671174261' role='' />\n    <member type='way' ref='100958660' role='' />\n    <member type='way' ref='100958634' role='' />\n    <member type='way' ref='671174264' role='' />\n    <member type='way' ref='671174265' role='' />\n    <member type='way' ref='100958640' role='' />\n    <member type='way' ref='433053553' role='' />\n    <member type='way' ref='433053542' role='' />\n    <member type='way' ref='100958644' role='' />\n    <member type='way' ref='433053546' role='' />\n    <member type='way' ref='433053556' role='' />\n    <member type='way' ref='29378182' role='' />\n    <member type='way' ref='339536179' role='' />\n    <member type='way' ref='99756366' role='' />\n    <member type='way' ref='455047412' role='' />\n    <member type='way' ref='365281135' role='' />\n    <member type='way' ref='107105449' role='backward' />\n    <member type='way' ref='695558519' role='' />\n    <member type='way' ref='753706473' role='' />\n    <member type='way' ref='753706472' role='' />\n    <member type='way' ref='845875911' role='' />\n    <member type='way' ref='584967709' role='' />\n    <member type='way' ref='107105438' role='' />\n    <member type='way' ref='744774028' role='' />\n    <member type='way' ref='32345147' role='' />\n    <member type='way' ref='107105420' role='' />\n    <member type='way' ref='34208039' role='' />\n    <member type='way' ref='32065422' role='' />\n    <member type='way' ref='336981027' role='' />\n    <member type='way' ref='336981028' role='' />\n    <member type='way' ref='107102443' role='' />\n    <member type='way' ref='107102444' role='' />\n    <member type='way' ref='623897349' role='' />\n    <member type='way' ref='623897350' role='' />\n    <member type='way' ref='32065417' role='' />\n    <member type='way' ref='617778482' role='' />\n    <member type='way' ref='107102445' role='' />\n    <member type='way' ref='617778483' role='' />\n    <member type='way' ref='617778481' role='' />\n    <member type='way' ref='107102242' role='' />\n    <member type='way' ref='280728524' role='forward' />\n    <member type='way' ref='671174201' role='forward' />\n    <member type='way' ref='671174194' role='forward' />\n    <member type='way' ref='339309071' role='forward' />\n    <member type='way' ref='671174209' role='forward' />\n    <member type='way' ref='671174217' role='forward' />\n    <member type='way' ref='671174190' role='forward' />\n    <member type='way' ref='681931235' role='backward' />\n    <member type='way' ref='671174205' role='forward' />\n    <member type='way' ref='671174204' role='forward' />\n    <member type='way' ref='253088403' role='forward' />\n    <member type='way' ref='671174206' role='forward' />\n    <member type='way' ref='671174192' role='forward' />\n    <member type='way' ref='31858712' role='forward' />\n    <member type='way' ref='671174221' role='forward' />\n    <member type='way' ref='339311503' role='' />\n    <member type='way' ref='681512377' role='' />\n    <member type='way' ref='681512378' role='' />\n    <member type='way' ref='835804514' role='' />\n    <member type='way' ref='88842054' role='' />\n    <member type='way' ref='88842068' role='' />\n    <member type='way' ref='88294830' role='' />\n    <member type='way' ref='367563306' role='' />\n    <member type='way' ref='102782613' role='' />\n    <member type='way' ref='171026389' role='' />\n    <member type='way' ref='171026395' role='' />\n    <member type='way' ref='607937671' role='' />\n    <member type='way' ref='25577605' role='' />\n    <member type='way' ref='171026379' role='' />\n    <member type='way' ref='231678933' role='' />\n    <member type='way' ref='367563322' role='' />\n    <member type='way' ref='697706771' role='' />\n    <member type='way' ref='44768768' role='' />\n    <member type='way' ref='697706767' role='' />\n    <member type='way' ref='697706762' role='' />\n    <member type='way' ref='697706777' role='' />\n    <member type='way' ref='44768754' role='' />\n    <member type='way' ref='697706778' role='' />\n    <member type='way' ref='713510778' role='' />\n    <member type='way' ref='44768720' role='' />\n    <member type='way' ref='228395338' role='' />\n    <member type='way' ref='365185428' role='' />\n    <member type='way' ref='145005746' role='' />\n    <member type='way' ref='116021489' role='' />\n    <member type='way' ref='116021488' role='' />\n    <member type='way' ref='94787256' role='' />\n    <member type='way' ref='94783590' role='' />\n    <member type='way' ref='44847579' role='' />\n    <member type='way' ref='32609922' role='' />\n    <member type='way' ref='32609923' role='' />\n    <member type='way' ref='145009231' role='' />\n    <member type='way' ref='32609930' role='' />\n    <member type='way' ref='309053326' role='' />\n    <member type='way' ref='584926686' role='' />\n    <member type='way' ref='503156228' role='' />\n    <member type='way' ref='32608914' role='' />\n    <member type='way' ref='32610672' role='' />\n    <member type='way' ref='364644234' role='' />\n    <member type='way' ref='365185432' role='' />\n    <member type='way' ref='44848011' role='' />\n    <member type='way' ref='228392197' role='' />\n    <member type='way' ref='228392195' role='' />\n    <member type='way' ref='697706960' role='' />\n    <member type='way' ref='32593950' role='' />\n    <member type='way' ref='32593951' role='' />\n    <member type='way' ref='212042789' role='' />\n    <member type='way' ref='44848108' role='' />\n    <member type='way' ref='365183318' role='' />\n    <member type='way' ref='41129594' role='' />\n    <member type='way' ref='145040585' role='' />\n    <member type='way' ref='365183314' role='' />\n    <member type='way' ref='132572177' role='' />\n    <member type='way' ref='670260489' role='' />\n    <member type='way' ref='24677405' role='' />\n    <member type='way' ref='70288066' role='' />\n    <member type='way' ref='670260486' role='' />\n    <member type='way' ref='670260487' role='' />\n    <member type='way' ref='70288063' role='' />\n    <member type='way' ref='32668582' role='' />\n    <member type='way' ref='70288073' role='' />\n    <member type='way' ref='32668911' role='' />\n    <member type='way' ref='70288064' role='' />\n    <member type='way' ref='670264097' role='' />\n    <member type='way' ref='53348992' role='' />\n    <member type='way' ref='28819805' role='' />\n    <member type='way' ref='670264692' role='' />\n    <member type='way' ref='39704823' role='' />\n    <member type='way' ref='75073464' role='' />\n    <member type='way' ref='70288072' role='' />\n    <member type='way' ref='126083507' role='' />\n    <member type='way' ref='24677416' role='' />\n    <member type='way' ref='70091653' role='' />\n    <member type='way' ref='100821592' role='' />\n    <member type='way' ref='100821566' role='' />\n    <member type='way' ref='139574321' role='' />\n    <member type='way' ref='185063983' role='' />\n    <member type='way' ref='219829649' role='' />\n    <member type='way' ref='39704810' role='' />\n    <member type='way' ref='27832399' role='' />\n    <member type='way' ref='99795789' role='' />\n    <member type='way' ref='284838838' role='' />\n    <member type='way' ref='689336227' role='' />\n    <member type='way' ref='362418147' role='' />\n    <member type='way' ref='576470123' role='' />\n    <member type='way' ref='365181036' role='' />\n    <member type='way' ref='132609562' role='' />\n    <member type='way' ref='132609558' role='' />\n    <member type='way' ref='132609559' role='' />\n    <member type='way' ref='132609560' role='' />\n    <member type='way' ref='70091657' role='' />\n    <member type='way' ref='363002033' role='' />\n    <member type='way' ref='143982946' role='' />\n    <member type='way' ref='102848314' role='' />\n    <member type='way' ref='700925652' role='' />\n    <member type='way' ref='700925657' role='' />\n    <member type='way' ref='102718423' role='' />\n    <member type='way' ref='102718456' role='' />\n    <member type='way' ref='102718422' role='' />\n    <member type='way' ref='102718443' role='' />\n    <member type='way' ref='188724724' role='' />\n    <member type='way' ref='102844912' role='' />\n    <member type='way' ref='188733692' role='' />\n    <member type='way' ref='845005388' role='' />\n    <member type='way' ref='845005387' role='' />\n    <member type='way' ref='845005395' role='' />\n    <member type='way' ref='699034663' role='' />\n    <member type='way' ref='25029646' role='' />\n    <member type='way' ref='83238416' role='' />\n    <member type='way' ref='83238431' role='' />\n    <member type='way' ref='83238452' role='' />\n    <member type='way' ref='169097821' role='' />\n    <member type='way' ref='83238427' role='' />\n    <member type='way' ref='83238447' role='' />\n    <member type='way' ref='102319415' role='' />\n    <member type='way' ref='102319428' role='' />\n    <member type='way' ref='83238438' role='' />\n    <member type='way' ref='25029624' role='' />\n    <member type='way' ref='102319438' role='' />\n    <member type='way' ref='83238445' role='' />\n    <member type='way' ref='83238424' role='' />\n    <member type='way' ref='224649789' role='' />\n    <member type='way' ref='224649779' role='' />\n    <member type='way' ref='224649785' role='' />\n    <member type='way' ref='82107510' role='' />\n    <member type='way' ref='179142010' role='' />\n    <member type='way' ref='179142001' role='' />\n    <member type='way' ref='34545749' role='' />\n    <member type='way' ref='83238433' role='' />\n    <member type='way' ref='217890311' role='' />\n    <member type='way' ref='192938331' role='' />\n    <member type='way' ref='397968712' role='' />\n    <member type='way' ref='170162607' role='' />\n    <member type='way' ref='170162610' role='' />\n    <member type='way' ref='367423976' role='' />\n    <member type='way' ref='367709286' role='' />\n    <member type='way' ref='367497068' role='' />\n    <member type='way' ref='456557577' role='backward' />\n    <member type='way' ref='337045858' role='' />\n    <member type='way' ref='337183810' role='' />\n    <member type='way' ref='100976345' role='' />\n    <member type='way' ref='339968807' role='' />\n    <member type='way' ref='367611190' role='' />\n    <member type='way' ref='127836463' role='' />\n    <member type='way' ref='367611189' role='' />\n    <member type='way' ref='99571602' role='' />\n    <member type='way' ref='127837598' role='' />\n    <member type='way' ref='367611193' role='' />\n    <member type='way' ref='100977198' role='' />\n    <member type='way' ref='100977200' role='' />\n    <member type='way' ref='103394433' role='' />\n    <member type='way' ref='367609206' role='' />\n    <member type='way' ref='815671636' role='' />\n    <member type='way' ref='103641979' role='' />\n    <member type='way' ref='103641977' role='' />\n    <member type='way' ref='103641969' role='' />\n    <member type='way' ref='367609201' role='' />\n    <member type='way' ref='103635454' role='' />\n    <member type='way' ref='385909563' role='' />\n    <member type='way' ref='219066654' role='' />\n    <member type='way' ref='107413794' role='' />\n    <member type='way' ref='107413793' role='' />\n    <member type='way' ref='219066652' role='' />\n    <member type='way' ref='367608016' role='' />\n    <member type='way' ref='107407600' role='' />\n    <member type='way' ref='107407620' role='' />\n    <member type='way' ref='107407621' role='' />\n    <member type='way' ref='39856986' role='' />\n    <member type='way' ref='107405542' role='' />\n    <member type='way' ref='107300665' role='' />\n    <member type='way' ref='107300623' role='' />\n    <member type='way' ref='107300656' role='' />\n    <member type='way' ref='107300674' role='' />\n    <member type='way' ref='107300659' role='' />\n    <member type='way' ref='385907571' role='' />\n    <member type='way' ref='367608008' role='' />\n    <member type='way' ref='367608017' role='' />\n    <member type='way' ref='107300637' role='' />\n    <member type='way' ref='107300672' role='' />\n    <member type='way' ref='107300622' role='' />\n    <member type='way' ref='107300621' role='' />\n    <member type='way' ref='367608012' role='' />\n    <member type='way' ref='340123407' role='' />\n    <member type='way' ref='340123404' role='' />\n    <member type='way' ref='367605674' role='' />\n    <member type='way' ref='100266557' role='' />\n    <member type='way' ref='367605671' role='' />\n    <member type='way' ref='95983574' role='' />\n    <member type='way' ref='100261713' role='' />\n    <member type='way' ref='100252330' role='' />\n    <member type='way' ref='551491296' role='' />\n    <member type='way' ref='367605043' role='' />\n    <member type='way' ref='99020122' role='' />\n    <member type='way' ref='99020120' role='' />\n    <member type='way' ref='367605034' role='' />\n    <member type='way' ref='551491293' role='' />\n    <member type='way' ref='551491292' role='' />\n    <member type='way' ref='99020111' role='' />\n    <member type='way' ref='99023310' role='' />\n    <member type='way' ref='367605037' role='' />\n    <member type='way' ref='367605039' role='' />\n    <member type='way' ref='367605041' role='' />\n    <member type='way' ref='95983566' role='' />\n    <member type='way' ref='95983596' role='' />\n    <member type='way' ref='367604588' role='' />\n    <member type='way' ref='100262769' role='' />\n    <member type='way' ref='227751434' role='' />\n    <member type='way' ref='96309352' role='' />\n    <member type='way' ref='227751428' role='' />\n    <member type='way' ref='227751429' role='' />\n    <member type='way' ref='227751433' role='' />\n    <member type='way' ref='701374101' role='' />\n    <member type='way' ref='99885177' role='' />\n    <member type='way' ref='99885180' role='' />\n    <member type='way' ref='701374102' role='' />\n    <member type='way' ref='844409115' role='' />\n    <member type='way' ref='219192051' role='' />\n    <member type='way' ref='111324605' role='' />\n    <member type='way' ref='367604110' role='' />\n    <member type='way' ref='101565601' role='' />\n    <member type='way' ref='101922650' role='' />\n    <member type='way' ref='462203945' role='' />\n    <member type='way' ref='110775313' role='' />\n    <member type='way' ref='367603283' role='' />\n    <member type='way' ref='648450392' role='' />\n    <member type='way' ref='110775302' role='' />\n    <member type='way' ref='762453881' role='' />\n    <member type='way' ref='762453880' role='' />\n    <member type='way' ref='466374421' role='' />\n    <member type='way' ref='110770884' role='' />\n    <member type='way' ref='466374419' role='' />\n    <member type='way' ref='340215913' role='' />\n    <member type='way' ref='367602577' role='' />\n    <member type='way' ref='367602578' role='' />\n    <member type='way' ref='145206597' role='' />\n    <member type='way' ref='492556446' role='' />\n    <member type='way' ref='110780102' role='' />\n    <member type='way' ref='110780087' role='' />\n    <member type='way' ref='367602191' role='' />\n    <member type='way' ref='229868797' role='' />\n    <member type='way' ref='229868791' role='' />\n    <member type='way' ref='229868799' role='' />\n    <member type='way' ref='229868785' role='' />\n    <member type='way' ref='229868784' role='' />\n    <member type='way' ref='844857413' role='' />\n    <member type='way' ref='367601246' role='' />\n    <member type='way' ref='844857417' role='' />\n    <member type='way' ref='367601244' role='' />\n    <member type='way' ref='102789224' role='' />\n    <member type='way' ref='492556441' role='' />\n    <member type='way' ref='102789223' role='' />\n    <member type='way' ref='301110902' role='' />\n    <member type='way' ref='513939336' role='' />\n    <member type='way' ref='513939339' role='' />\n    <member type='way' ref='367600262' role='' />\n    <member type='way' ref='367600269' role='' />\n    <member type='way' ref='466374429' role='' />\n    <member type='way' ref='301099082' role='' />\n    <member type='way' ref='466374431' role='' />\n    <member type='way' ref='301099081' role='' />\n    <member type='way' ref='466374430' role='' />\n    <member type='way' ref='432055132' role='' />\n    <member type='way' ref='466374432' role='' />\n    <member type='way' ref='367600274' role='' />\n    <member type='way' ref='100879499' role='' />\n    <member type='way' ref='301099085' role='' />\n    <member type='way' ref='367599774' role='' />\n    <member type='way' ref='466378804' role='' />\n    <member type='way' ref='226371740' role='' />\n    <member type='way' ref='170274103' role='' />\n    <member type='way' ref='25957463' role='' />\n    <member type='way' ref='466378816' role='' />\n    <member type='way' ref='466378817' role='' />\n    <member type='way' ref='466378818' role='' />\n    <member type='way' ref='466378820' role='' />\n    <member type='way' ref='81942992' role='' />\n    <member type='way' ref='310439048' role='' />\n    <member type='way' ref='101020413' role='' />\n    <member type='way' ref='367599777' role='' />\n    <member type='way' ref='41430432' role='' />\n    <member type='way' ref='81942984' role='' />\n    <member type='way' ref='99069906' role='' />\n    <member type='way' ref='81942998' role='' />\n    <member type='way' ref='367598537' role='' />\n    <member type='way' ref='852159735' role='' />\n    <member type='way' ref='367597674' role='' />\n    <member type='way' ref='142229511' role='' />\n    <member type='way' ref='41430424' role='' />\n    <member type='way' ref='41430423' role='' />\n    <member type='way' ref='508747864' role='' />\n    <member type='way' ref='508747865' role='' />\n    <member type='way' ref='41430422' role='' />\n    <member type='way' ref='41430420' role='' />\n    <member type='way' ref='35957752' role='' />\n    <member type='way' ref='232654342' role='' />\n    <member type='way' ref='229868783' role='' />\n    <member type='way' ref='367597687' role='' />\n    <member type='way' ref='367597683' role='' />\n    <member type='way' ref='367597673' role='' />\n    <member type='way' ref='101054372' role='' />\n    <member type='way' ref='101054420' role='' />\n    <member type='way' ref='101054384' role='' />\n    <member type='way' ref='101054427' role='' />\n    <member type='way' ref='101054389' role='' />\n    <member type='way' ref='438222378' role='' />\n    <member type='way' ref='438222373' role='' />\n    <member type='way' ref='35952810' role='' />\n    <member type='way' ref='438222375' role='' />\n    <member type='way' ref='438222387' role='' />\n    <member type='way' ref='438222485' role='' />\n    <member type='way' ref='100517386' role='' />\n    <member type='way' ref='101020412' role='' />\n    <member type='way' ref='59003228' role='' />\n    <member type='way' ref='438222399' role='' />\n    <member type='way' ref='59997433' role='' />\n    <member type='way' ref='59003229' role='' />\n    <member type='way' ref='75430335' role='' />\n    <member type='way' ref='59997440' role='' />\n    <member type='way' ref='59997437' role='' />\n    <member type='way' ref='75430333' role='' />\n    <member type='way' ref='59997438' role='' />\n    <member type='way' ref='59003234' role='' />\n    <member type='way' ref='59003237' role='' />\n    <member type='way' ref='75430336' role='' />\n    <member type='way' ref='167442528' role='' />\n    <member type='way' ref='167442530' role='' />\n    <member type='way' ref='367588953' role='' />\n    <member type='way' ref='58908131' role='' />\n    <member type='way' ref='58908132' role='' />\n    <member type='way' ref='58908126' role='' />\n    <member type='way' ref='58908125' role='' />\n    <member type='way' ref='367588957' role='' />\n    <member type='way' ref='367588959' role='' />\n    <member type='way' ref='367588952' role='' />\n    <member type='way' ref='59135462' role='' />\n    <member type='way' ref='367588958' role='' />\n    <member type='way' ref='60690827' role='' />\n    <member type='way' ref='59135457' role='' />\n    <member type='way' ref='367580260' role='' />\n    <member type='way' ref='219299242' role='' />\n    <member type='way' ref='97098411' role='' />\n    <member type='way' ref='219299233' role='' />\n    <member type='way' ref='60690836' role='' />\n    <member type='way' ref='128322274' role='' />\n    <member type='way' ref='367577741' role='' />\n    <member type='way' ref='217609017' role='' />\n    <member type='way' ref='217609013' role='' />\n    <member type='way' ref='97094211' role='' />\n    <member type='way' ref='217609016' role='' />\n    <member type='way' ref='217609004' role='' />\n    <member type='way' ref='217609015' role='' />\n    <member type='way' ref='54435273' role='' />\n    <member type='way' ref='217608460' role='' />\n    <member type='way' ref='97360497' role='' />\n    <member type='way' ref='367576003' role='' />\n    <member type='way' ref='96884631' role='' />\n    <member type='way' ref='217608461' role='' />\n    <member type='way' ref='85287361' role='' />\n    <member type='way' ref='367575473' role='' />\n    <member type='way' ref='343772530' role='' />\n    <member type='way' ref='367575475' role='' />\n    <member type='way' ref='367575474' role='' />\n    <member type='way' ref='99466052' role='' />\n    <member type='way' ref='27228903' role='' />\n    <member type='way' ref='27228904' role='' />\n    <member type='way' ref='26823491' role='' />\n    <member type='way' ref='461686551' role='' />\n    <member type='way' ref='367574827' role='' />\n    <member type='way' ref='97223166' role='' />\n    <member type='way' ref='367574828' role='' />\n    <member type='way' ref='367574823' role='' />\n    <member type='way' ref='97092295' role='' />\n    <member type='way' ref='97092294' role='' />\n    <member type='way' ref='199100138' role='' />\n    <member type='way' ref='63730802' role='' />\n    <member type='way' ref='98204619' role='' />\n    <member type='way' ref='96862505' role='' />\n    <member type='way' ref='367574653' role='' />\n    <member type='way' ref='367574649' role='' />\n    <member type='way' ref='367574652' role='' />\n    <member type='way' ref='98485836' role='' />\n    <member type='way' ref='98485866' role='' />\n    <member type='way' ref='367574654' role='' />\n    <member type='way' ref='367573731' role='' />\n    <member type='way' ref='80181428' role='' />\n    <member type='way' ref='25707637' role='' />\n    <member type='way' ref='44684758' role='' />\n    <member type='way' ref='98413224' role='' />\n    <member type='way' ref='98413195' role='' />\n    <member type='way' ref='199100115' role='' />\n    <member type='way' ref='699007021' role='' />\n    <member type='way' ref='44683770' role='' />\n    <member type='way' ref='700790869' role='' />\n    <member type='way' ref='698362633' role='' />\n    <member type='way' ref='699007020' role='' />\n    <member type='way' ref='25690295' role='' />\n    <member type='way' ref='44683583' role='' />\n    <member type='way' ref='697925979' role='' />\n    <member type='way' ref='697925978' role='' />\n    <member type='way' ref='365192547' role='' />\n    <member type='way' ref='36394708' role='' />\n    <member type='way' ref='36394709' role='' />\n    <member type='way' ref='365192548' role='' />\n    <member type='way' ref='365192542' role='' />\n    <member type='way' ref='207161991' role='' />\n    <member type='way' ref='207162002' role='' />\n    <member type='way' ref='199100109' role='' />\n    <member type='way' ref='37709681' role='' />\n    <member type='way' ref='199100101' role='' />\n    <member type='way' ref='365192529' role='' />\n    <member type='way' ref='699003230' role='' />\n    <member type='way' ref='232645919' role='' />\n    <member type='way' ref='232645906' role='' />\n    <member type='way' ref='232645915' role='' />\n    <member type='way' ref='232645921' role='' />\n    <member type='way' ref='302957590' role='' />\n    <member type='way' ref='93630587' role='' />\n    <member type='way' ref='25658823' role='' />\n    <member type='way' ref='93630584' role='' />\n    <member type='way' ref='482294810' role='' />\n    <member type='way' ref='482294811' role='' />\n    <member type='way' ref='365192523' role='' />\n    <member type='way' ref='696780531' role='' />\n    <member type='way' ref='93595300' role='' />\n    <member type='way' ref='228483190' role='' />\n    <member type='way' ref='228483206' role='' />\n    <member type='way' ref='171030537' role='' />\n    <member type='way' ref='102192268' role='' />\n    <member type='way' ref='698183397' role='' />\n    <member type='way' ref='365188929' role='' />\n    <member type='way' ref='698183398' role='' />\n    <member type='way' ref='171030532' role='' />\n    <member type='way' ref='31248933' role='' />\n    <member type='way' ref='197415283' role='' />\n    <member type='way' ref='32523790' role='' />\n    <member type='way' ref='32522013' role='' />\n    <member type='way' ref='197415272' role='' />\n    <member type='way' ref='698183394' role='' />\n    <member type='way' ref='698183395' role='' />\n    <member type='way' ref='95587870' role='' />\n    <member type='way' ref='698183393' role='' />\n    <member type='way' ref='337045855' role='backward' />\n    <member type='way' ref='398634602' role='' />\n    <member type='way' ref='337045854' role='forward' />\n    <member type='way' ref='408505435' role='' />\n    <member type='way' ref='378783475' role='' />\n    <member type='way' ref='373595717' role='backward' />\n    <member type='way' ref='373595719' role='forward' />\n    <member type='way' ref='408505436' role='' />\n    <member type='way' ref='301209898' role='backward' />\n    <member type='way' ref='456574984' role='backward' />\n    <member type='way' ref='433222891' role='' />\n    <member type='way' ref='191259377' role='' />\n    <member type='way' ref='835449959' role='' />\n    <member type='way' ref='366868460' role='' />\n    <member type='way' ref='366868461' role='' />\n    <member type='way' ref='675094112' role='' />\n    <member type='way' ref='8151651' role='' />\n    <member type='way' ref='675094113' role='' />\n    <member type='way' ref='835449957' role='' />\n    <member type='way' ref='366882621' role='' />\n    <member type='way' ref='366882622' role='' />\n    <member type='way' ref='8151647' role='' />\n    <member type='way' ref='366885590' role='' />\n    <member type='way' ref='366885593' role='' />\n    <member type='way' ref='4708289' role='' />\n    <member type='way' ref='366882617' role='' />\n    <member type='way' ref='257071590' role='' />\n    <member type='way' ref='106469854' role='' />\n    <member type='way' ref='136060047' role='' />\n    <member type='way' ref='26427680' role='' />\n    <member type='way' ref='26427679' role='' />\n    <member type='way' ref='378700137' role='' />\n    <member type='way' ref='88735723' role='' />\n    <member type='way' ref='88735727' role='' />\n    <member type='way' ref='38718241' role='' />\n    <member type='way' ref='104027416' role='' />\n    <member type='way' ref='26288922' role='' />\n    <member type='way' ref='26288921' role='' />\n    <member type='way' ref='26288909' role='' />\n    <member type='way' ref='26288907' role='' />\n    <member type='way' ref='26288915' role='' />\n    <member type='way' ref='26288914' role='' />\n    <member type='way' ref='102722140' role='' />\n    <member type='way' ref='43761238' role='' />\n    <member type='way' ref='88556880' role='' />\n    <member type='way' ref='88556865' role='' />\n    <member type='way' ref='30056214' role='' />\n    <member type='way' ref='73011973' role='' />\n    <member type='way' ref='30249591' role='' />\n    <member type='way' ref='102696284' role='' />\n    <member type='way' ref='143978698' role='' />\n    <member type='way' ref='133534359' role='' />\n    <member type='way' ref='133534352' role='' />\n    <member type='way' ref='459098949' role='' />\n    <member type='way' ref='133534357' role='' />\n    <member type='way' ref='25460499' role='' />\n    <member type='way' ref='102694235' role='' />\n    <member type='way' ref='31116969' role='' />\n    <member type='way' ref='823308979' role='' />\n    <member type='way' ref='823308978' role='' />\n    <member type='way' ref='505657900' role='' />\n    <member type='way' ref='144000484' role='' />\n    <member type='way' ref='147562516' role='' />\n    <member type='way' ref='823308985' role='' />\n    <member type='way' ref='147562502' role='' />\n    <member type='way' ref='38765603' role='' />\n    <member type='way' ref='98252065' role='' />\n    <member type='way' ref='147040613' role='' />\n    <member type='way' ref='147040649' role='' />\n    <member type='way' ref='147340026' role='' />\n    <member type='way' ref='107066967' role='' />\n    <member type='way' ref='147068229' role='' />\n    <member type='way' ref='147068230' role='' />\n    <member type='way' ref='107070521' role='' />\n    <member type='way' ref='28202849' role='' />\n    <member type='way' ref='760418574' role='' />\n    <member type='way' ref='221033154' role='' />\n    <member type='way' ref='221033153' role='' />\n    <member type='way' ref='121902880' role='' />\n    <member type='way' ref='121902881' role='' />\n    <member type='way' ref='147341513' role='' />\n    <member type='way' ref='147391656' role='' />\n    <member type='way' ref='107073138' role='' />\n    <member type='way' ref='107075264' role='' />\n    <member type='way' ref='107075263' role='' />\n    <member type='way' ref='431442102' role='' />\n    <member type='way' ref='612048672' role='' />\n    <member type='way' ref='147040635' role='' />\n    <member type='way' ref='37632060' role='' />\n    <member type='way' ref='72887403' role='' />\n    <member type='way' ref='28767746' role='' />\n    <member type='way' ref='34992903' role='' />\n    <member type='way' ref='34992932' role='' />\n    <member type='way' ref='72887405' role='' />\n    <member type='way' ref='808377055' role='' />\n    <member type='way' ref='107082088' role='' />\n    <member type='way' ref='38962966' role='' />\n    <member type='way' ref='39496708' role='' />\n    <member type='way' ref='107084389' role='' />\n    <member type='way' ref='107084386' role='' />\n    <member type='way' ref='25633483' role='' />\n    <member type='way' ref='386646803' role='' />\n    <member type='way' ref='386646801' role='' />\n    <member type='way' ref='386646788' role='' />\n    <member type='way' ref='380369515' role='' />\n    <member type='way' ref='639799875' role='' />\n    <member type='way' ref='47853290' role='' />\n    <member type='way' ref='107087878' role='' />\n    <member type='way' ref='496203661' role='' />\n    <member type='way' ref='496203660' role='' />\n    <member type='way' ref='139162758' role='' />\n    <member type='way' ref='9719784' role='' />\n    <member type='way' ref='96151412' role='' />\n    <member type='way' ref='9719775' role='' />\n    <member type='way' ref='140687019' role='' />\n    <member type='way' ref='29378201' role='' />\n    <member type='way' ref='372491838' role='' />\n    <member type='way' ref='367611431' role='' />\n    <member type='way' ref='100956332' role='' />\n    <member type='way' ref='100956315' role='' />\n    <member type='way' ref='100360012' role='' />\n    <member type='way' ref='100360079' role='' />\n    <member type='way' ref='100360027' role='' />\n    <member type='way' ref='100360007' role='' />\n    <member type='way' ref='543087135' role='' />\n    <member type='way' ref='100360035' role='' />\n    <member type='way' ref='371905696' role='' />\n    <member type='way' ref='371905647' role='' />\n    <member type='way' ref='371905643' role='' />\n    <member type='way' ref='100956321' role='' />\n    <member type='way' ref='101183743' role='' />\n    <member type='way' ref='371905694' role='' />\n    <member type='way' ref='100956323' role='' />\n    <member type='way' ref='100956317' role='' />\n    <member type='way' ref='110097624' role='' />\n    <member type='way' ref='101222640' role='' />\n    <member type='way' ref='101222637' role='' />\n    <member type='way' ref='371905712' role='' />\n    <member type='way' ref='584856108' role='' />\n    <member type='way' ref='101221451' role='' />\n    <member type='way' ref='302414774' role='' />\n    <member type='way' ref='101221453' role='' />\n    <member type='way' ref='340379033' role='' />\n    <member type='way' ref='103433604' role='' />\n    <member type='way' ref='103433605' role='' />\n    <member type='way' ref='340379027' role='' />\n    <member type='way' ref='551510530' role='' />\n    <member type='way' ref='83239555' role='' />\n    <member type='way' ref='551510528' role='' />\n    <member type='way' ref='101225837' role='' />\n    <member type='way' ref='107089762' role='' />\n    <member type='way' ref='99422492' role='' />\n    <member type='way' ref='41574436' role='' />\n    <member type='way' ref='119578536' role='' />\n    <member type='way' ref='408151819' role='' />\n    <member type='way' ref='329034373' role='' />\n    <member type='way' ref='491846407' role='' />\n    <member type='way' ref='491846406' role='' />\n    <member type='way' ref='340373238' role='' />\n    <member type='way' ref='408140979' role='' />\n    <member type='way' ref='408140981' role='' />\n    <member type='way' ref='329019163' role='' />\n    <member type='way' ref='408140974' role='' />\n    <member type='way' ref='340373234' role='' />\n    <member type='way' ref='543186940' role='' />\n    <member type='way' ref='340373237' role='' />\n    <member type='way' ref='340431422' role='' />\n    <member type='way' ref='340431421' role='' />\n    <member type='way' ref='340431420' role='' />\n    <member type='way' ref='363814212' role='' />\n    <member type='way' ref='83239554' role='' />\n    <member type='way' ref='83239562' role='' />\n    <member type='way' ref='96730057' role='' />\n    <member type='way' ref='96730041' role='' />\n    <member type='way' ref='106468996' role='' />\n    <member type='way' ref='96770163' role='' />\n    <member type='way' ref='96770100' role='' />\n    <member type='way' ref='32571092' role='' />\n    <member type='way' ref='457517175' role='' />\n    <member type='way' ref='439818414' role='' />\n    <member type='way' ref='439818415' role='' />\n    <member type='way' ref='106468990' role='' />\n    <member type='way' ref='106468989' role='' />\n    <member type='way' ref='106468995' role='' />\n    <member type='way' ref='38247202' role='' />\n    <member type='way' ref='38247203' role='' />\n    <member type='way' ref='38247207' role='' />\n    <member type='way' ref='103456761' role='' />\n    <member type='way' ref='38247548' role='' />\n    <member type='way' ref='107107002' role='' />\n    <member type='way' ref='91266599' role='' />\n    <member type='way' ref='91296424' role='' />\n    <member type='way' ref='91266530' role='' />\n    <member type='way' ref='91308614' role='' />\n    <member type='way' ref='107107549' role='' />\n    <member type='way' ref='91308695' role='' />\n    <member type='way' ref='107109300' role='' />\n    <member type='way' ref='107109296' role='' />\n    <member type='way' ref='99345023' role='' />\n    <member type='way' ref='107109298' role='' />\n    <member type='way' ref='107109292' role='' />\n    <member type='way' ref='7771324' role='' />\n    <member type='way' ref='107110707' role='' />\n    <member type='way' ref='7930690' role='' />\n    <member type='way' ref='433044539' role='' />\n    <member type='way' ref='433044541' role='' />\n    <member type='way' ref='7773926' role='' />\n    <member type='way' ref='7930693' role='' />\n    <member type='way' ref='362977288' role='' />\n    <member type='way' ref='42492956' role='' />\n    <member type='way' ref='329058516' role='' />\n    <member type='way' ref='32264713' role='' />\n    <member type='way' ref='799506707' role='' />\n    <member type='way' ref='250270238' role='' />\n    <member type='way' ref='107118533' role='' />\n    <member type='way' ref='342932649' role='' />\n    <member type='way' ref='671174188' role='' />\n    <member type='way' ref='342934219' role='' />\n    <member type='way' ref='362988770' role='' />\n    <member type='way' ref='362988769' role='' />\n    <member type='way' ref='159017840' role='' />\n    <member type='way' ref='344376400' role='' />\n    <member type='way' ref='344376399' role='' />\n    <member type='way' ref='139167173' role='' />\n    <member type='way' ref='107118531' role='' />\n    <member type='way' ref='107118530' role='' />\n    <member type='way' ref='401283787' role='' />\n    <member type='way' ref='7930708' role='' />\n    <member type='way' ref='133538708' role='' />\n    <member type='way' ref='678498433' role='' />\n    <member type='way' ref='678498422' role='' />\n    <member type='way' ref='534813897' role='' />\n    <member type='way' ref='534813895' role='' />\n    <member type='way' ref='534813893' role='' />\n    <member type='way' ref='72488370' role='' />\n    <member type='way' ref='534813892' role='' />\n    <member type='way' ref='107118523' role='' />\n    <member type='way' ref='7975470' role='' />\n    <member type='way' ref='762505086' role='' />\n    <member type='way' ref='496256039' role='' />\n    <member type='way' ref='30173254' role='' />\n    <member type='way' ref='4579061' role='' />\n    <member type='way' ref='520995116' role='' />\n    <member type='way' ref='369772035' role='' />\n    <member type='way' ref='8041859' role='' />\n    <member type='way' ref='496355624' role='' />\n    <member type='way' ref='369772029' role='' />\n    <member type='way' ref='496107796' role='' />\n    <member type='way' ref='681376587' role='' />\n    <member type='way' ref='681376586' role='' />\n    <member type='way' ref='496120879' role='' />\n    <member type='way' ref='286851410' role='' />\n    <member type='way' ref='825980018' role='' />\n    <member type='way' ref='825980017' role='' />\n    <member type='way' ref='779192151' role='' />\n    <member type='way' ref='275143505' role='' />\n    <member type='way' ref='275143512' role='' />\n    <member type='way' ref='681139362' role='' />\n    <member type='way' ref='681139368' role='' />\n    <member type='way' ref='681139359' role='' />\n    <member type='way' ref='681139343' role='' />\n    <member type='way' ref='8041862' role='' />\n    <member type='way' ref='681139345' role='' />\n    <member type='way' ref='37758197' role='' />\n    <member type='way' ref='52916945' role='' />\n    <member type='way' ref='147338332' role='' />\n    <member type='way' ref='681143398' role='' />\n    <member type='way' ref='681143381' role='' />\n    <member type='way' ref='30628580' role='' />\n    <member type='way' ref='681143392' role='' />\n    <member type='way' ref='681143389' role='' />\n    <member type='way' ref='681143387' role='' />\n    <member type='way' ref='132523936' role='' />\n    <member type='way' ref='681139349' role='' />\n    <member type='way' ref='384190946' role='' />\n    <member type='way' ref='71756288' role='' />\n    <member type='way' ref='681139356' role='' />\n    <member type='way' ref='498604201' role='' />\n    <member type='way' ref='681139367' role='' />\n    <member type='way' ref='107114265' role='' />\n    <member type='way' ref='275143515' role='' />\n    <member type='way' ref='825980029' role='' />\n    <member type='way' ref='779192146' role='' />\n    <member type='way' ref='779192145' role='' />\n    <member type='way' ref='825980013' role='' />\n    <member type='way' ref='368771669' role='' />\n    <member type='way' ref='825980021' role='' />\n    <member type='way' ref='368771668' role='' />\n    <member type='way' ref='825980025' role='' />\n    <member type='way' ref='825980024' role='' />\n    <member type='way' ref='286684698' role='' />\n    <member type='way' ref='809368764' role='' />\n    <member type='way' ref='809368756' role='' />\n    <member type='way' ref='437244814' role='' />\n    <member type='way' ref='437244815' role='' />\n    <member type='way' ref='369772040' role='' />\n    <member type='way' ref='4579004' role='' />\n    <member type='way' ref='394522512' role='' />\n    <member type='way' ref='369772028' role='' />\n    <member type='way' ref='496256038' role='' />\n    <member type='way' ref='496256037' role='' />\n    <member type='way' ref='367197072' role='' />\n    <member type='way' ref='33169799' role='' />\n    <member type='way' ref='164920840' role='' />\n    <member type='way' ref='25839143' role='' />\n    <member type='way' ref='497231436' role='' />\n    <member type='way' ref='164618154' role='' />\n    <member type='way' ref='497231434' role='' />\n    <member type='way' ref='503038971' role='' />\n    <member type='way' ref='497275459' role='' />\n    <member type='way' ref='125954723' role='' />\n    <member type='way' ref='367198529' role='backward' />\n    <member type='way' ref='367325627' role='forward' />\n    <member type='way' ref='836416951' role='' />\n    <member type='way' ref='284842017' role='' />\n    <member type='way' ref='365268343' role='backward' />\n    <member type='way' ref='188888768' role='' />\n    <member type='way' ref='188888769' role='' />\n    <member type='way' ref='99020737' role='' />\n    <member type='way' ref='99297458' role='' />\n    <member type='way' ref='188888770' role='' />\n    <member type='way' ref='37541439' role='' />\n    <member type='way' ref='210049974' role='' />\n    <member type='way' ref='99268962' role='' />\n    <member type='way' ref='99268982' role='' />\n    <member type='way' ref='210049427' role='' />\n    <member type='way' ref='210039188' role='' />\n    <member type='way' ref='210039189' role='' />\n    <member type='way' ref='37542182' role='' />\n    <member type='way' ref='124648351' role='' />\n    <member type='way' ref='99472795' role='' />\n    <member type='way' ref='188728625' role='' />\n    <member type='way' ref='99472788' role='' />\n    <member type='way' ref='188728624' role='' />\n    <member type='way' ref='156621505' role='' />\n    <member type='way' ref='156621532' role='' />\n    <member type='way' ref='99416459' role='' />\n    <member type='way' ref='367332044' role='' />\n    <member type='way' ref='217900162' role='' />\n    <member type='way' ref='168172367' role='' />\n    <member type='way' ref='217900161' role='' />\n    <member type='way' ref='144127885' role='' />\n    <member type='way' ref='836372989' role='' />\n    <member type='way' ref='188728632' role='' />\n    <member type='way' ref='124447148' role='' />\n    <member type='way' ref='188728631' role='' />\n    <member type='way' ref='188728623' role='' />\n    <member type='way' ref='99416444' role='' />\n    <member type='way' ref='99416559' role='' />\n    <member type='way' ref='99416389' role='' />\n    <member type='way' ref='168549801' role='' />\n    <member type='way' ref='168549780' role='' />\n    <member type='way' ref='188728628' role='' />\n    <member type='way' ref='99430524' role='' />\n    <member type='way' ref='188728634' role='' />\n    <member type='way' ref='72310985' role='' />\n    <member type='way' ref='159194974' role='' />\n    <member type='way' ref='159195004' role='' />\n    <member type='way' ref='188728636' role='' />\n    <member type='way' ref='97149325' role='' />\n    <member type='way' ref='367430285' role='' />\n    <member type='way' ref='367423978' role='' />\n    <member type='way' ref='32651331' role='' />\n    <member type='way' ref='367449802' role='' />\n    <member type='way' ref='83238417' role='' />\n    <member type='way' ref='498065657' role='' />\n    <member type='way' ref='503079900' role='' />\n    <member type='way' ref='146366210' role='' />\n    <member type='way' ref='783150254' role='' />\n    <member type='way' ref='503079896' role='' />\n    <member type='way' ref='97149334' role='' />\n    <member type='way' ref='97149279' role='' />\n    <member type='way' ref='367500354' role='' />\n    <member type='way' ref='34510657' role='' />\n    <member type='way' ref='102319416' role='' />\n    <member type='way' ref='102319418' role='' />\n    <member type='way' ref='132609561' role='' />\n    <member type='way' ref='365181039' role='' />\n    <member type='way' ref='228392196' role='' />\n    <member type='way' ref='367577739' role='' />\n    <member type='way' ref='97094199' role='' />\n    <member type='way' ref='438222385' role='' />\n    <member type='way' ref='563898428' role='' />\n    <member type='way' ref='367594345' role='' />\n    <member type='way' ref='552428866' role='' />\n    <member type='way' ref='552428865' role='' />\n    <member type='way' ref='101054394' role='' />\n    <member type='way' ref='552428868' role='' />\n    <member type='way' ref='552428869' role='' />\n    <member type='way' ref='75430331' role='' />\n    <member type='way' ref='186299207' role='' />\n    <member type='way' ref='366882616' role='' />\n    <member type='way' ref='366885595' role='' />\n    <member type='way' ref='366885592' role='' />\n    <member type='way' ref='366885594' role='' />\n    <member type='way' ref='366885591' role='' />\n    <member type='way' ref='257071594' role='' />\n    <member type='way' ref='26317413' role='' />\n    <member type='way' ref='139162757' role='' />\n    <member type='way' ref='139162760' role='' />\n    <member type='way' ref='100958635' role='' />\n    <member type='way' ref='671174267' role='' />\n    <member type='way' ref='671174266' role='' />\n    <member type='way' ref='96927056' role='' />\n    <member type='way' ref='671174234' role='' />\n    <member type='way' ref='671174233' role='' />\n    <member type='way' ref='666342392' role='' />\n    <member type='way' ref='700962269' role='' />\n    <member type='way' ref='666342391' role='' />\n    <member type='way' ref='666342390' role='' />\n    <member type='way' ref='671174229' role='' />\n    <member type='way' ref='666342331' role='' />\n    <member type='way' ref='128572670' role='' />\n    <member type='way' ref='681751883' role='' />\n    <member type='way' ref='681751882' role='' />\n    <member type='way' ref='158843013' role='' />\n    <member type='way' ref='365196091' role='' />\n    <member type='way' ref='367611432' role='' />\n    <member type='way' ref='408501192' role='' />\n    <member type='way' ref='455047414' role='forward' />\n    <member type='way' ref='107106214' role='backward' />\n    <member type='way' ref='99756357' role='forward' />\n    <member type='way' ref='367197071' role='forward' />\n    <member type='way' ref='367197070' role='forward' />\n    <member type='way' ref='367197068' role='forward' />\n    <member type='way' ref='363873599' role='forward' />\n    <member type='way' ref='367197451' role='forward' />\n    <member type='way' ref='456557576' role='forward' />\n    <member type='way' ref='365281133' role='' />\n    <member type='way' ref='107106207' role='backward' />\n    <member type='way' ref='340537023' role='forward' />\n    <member type='way' ref='666326578' role='forward' />\n    <member type='way' ref='666326223' role='' />\n    <member type='way' ref='675094114' role='' />\n    <member type='way' ref='366868471' role='' />\n    <member type='way' ref='366870732' role='' />\n    <member type='way' ref='675094106' role='' />\n    <member type='way' ref='366868462' role='' />\n    <member type='way' ref='675094094' role='' />\n    <member type='way' ref='366882640' role='' />\n    <member type='way' ref='675094095' role='' />\n    <member type='way' ref='370411714' role='' />\n    <member type='way' ref='370411706' role='' />\n    <member type='way' ref='370411711' role='' />\n    <member type='way' ref='370411720' role='' />\n    <member type='way' ref='330387701' role='' />\n    <member type='way' ref='783150255' role='' />\n    <tag k='distance' v='610' />\n    <tag k='name' v='Limfjordsruten' />\n    <tag k='network' v='ncn' />\n    <tag k='note' v='Denne ruterelation udgør grundlaget for den rute der vises på Vejdirektoratets oversigt' />\n    <tag k='operator' v='Vejdirektoratet' />\n    <tag k='ref' v='12' />\n    <tag k='roundtrip' v='yes' />\n    <tag k='route' v='bicycle' />\n    <tag k='type' v='route' />\n    <tag k='wikidata' v='Q52666777' />\n  </relation>\n  <relation id='1473808' timestamp='2020-09-10T00:43:09Z' uid='2677992' user='mikkolukas' visible='true' version='214' changeset='90669232'>\n    <member type='way' ref='386646801' role='' />\n    <member type='way' ref='386646788' role='' />\n    <member type='way' ref='386646803' role='' />\n    <member type='way' ref='25633483' role='' />\n    <member type='way' ref='107084386' role='' />\n    <member type='way' ref='107084389' role='' />\n    <member type='way' ref='39496708' role='' />\n    <member type='way' ref='38962966' role='' />\n    <member type='way' ref='107082088' role='' />\n    <member type='way' ref='808377055' role='' />\n    <member type='way' ref='72887405' role='' />\n    <member type='way' ref='34992932' role='' />\n    <member type='way' ref='34992903' role='' />\n    <member type='way' ref='28767746' role='' />\n    <member type='way' ref='72887403' role='' />\n    <member type='way' ref='37632060' role='' />\n    <member type='way' ref='147040635' role='' />\n    <member type='way' ref='612048672' role='' />\n    <member type='way' ref='431442102' role='' />\n    <member type='way' ref='107075263' role='' />\n    <member type='way' ref='107075264' role='' />\n    <member type='way' ref='107073138' role='' />\n    <member type='way' ref='147391656' role='' />\n    <member type='way' ref='147341513' role='' />\n    <member type='way' ref='121902881' role='' />\n    <member type='way' ref='121902880' role='' />\n    <member type='way' ref='221033153' role='' />\n    <member type='way' ref='221033154' role='' />\n    <member type='way' ref='760418574' role='' />\n    <member type='way' ref='28202849' role='' />\n    <member type='way' ref='107070521' role='' />\n    <member type='way' ref='147068230' role='' />\n    <member type='way' ref='147068229' role='' />\n    <member type='way' ref='107066967' role='' />\n    <member type='way' ref='147340026' role='' />\n    <member type='way' ref='147040649' role='' />\n    <member type='way' ref='147040613' role='' />\n    <member type='way' ref='98252065' role='' />\n    <member type='way' ref='38765603' role='' />\n    <member type='way' ref='147562502' role='' />\n    <member type='way' ref='823308985' role='' />\n    <member type='way' ref='147562516' role='' />\n    <member type='way' ref='144000484' role='' />\n    <member type='way' ref='505657900' role='' />\n    <member type='way' ref='823308978' role='' />\n    <member type='way' ref='823308979' role='' />\n    <member type='way' ref='31116969' role='' />\n    <member type='way' ref='102694235' role='' />\n    <member type='way' ref='25460499' role='' />\n    <member type='way' ref='133534357' role='' />\n    <member type='way' ref='459098949' role='' />\n    <member type='way' ref='133534352' role='' />\n    <member type='way' ref='133534359' role='' />\n    <member type='way' ref='143978698' role='' />\n    <member type='way' ref='102696284' role='' />\n    <member type='way' ref='30249591' role='' />\n    <member type='way' ref='73011973' role='' />\n    <member type='way' ref='30056214' role='' />\n    <member type='way' ref='88556865' role='' />\n    <member type='way' ref='88556880' role='' />\n    <member type='way' ref='43761238' role='' />\n    <member type='way' ref='102722140' role='' />\n    <member type='way' ref='26288914' role='' />\n    <member type='way' ref='26288915' role='' />\n    <member type='way' ref='26288907' role='' />\n    <member type='way' ref='26288909' role='' />\n    <member type='way' ref='26288921' role='' />\n    <member type='way' ref='26288922' role='' />\n    <member type='way' ref='104027416' role='' />\n    <member type='way' ref='38718241' role='' />\n    <member type='way' ref='88735727' role='' />\n    <member type='way' ref='88735723' role='' />\n    <member type='way' ref='378700137' role='' />\n    <member type='way' ref='26427679' role='' />\n    <member type='way' ref='26427680' role='' />\n    <member type='way' ref='136060047' role='' />\n    <member type='way' ref='26317413' role='' />\n    <member type='way' ref='257071594' role='' />\n    <member type='way' ref='366885591' role='' />\n    <member type='way' ref='366885594' role='' />\n    <member type='way' ref='366885592' role='' />\n    <member type='way' ref='366885595' role='' />\n    <member type='way' ref='366882616' role='' />\n    <member type='way' ref='366882622' role='' />\n    <member type='way' ref='8151647' role='' />\n    <member type='way' ref='366885590' role='' />\n    <member type='way' ref='366885593' role='' />\n    <member type='way' ref='4708289' role='' />\n    <member type='way' ref='366882617' role='' />\n    <member type='way' ref='257071590' role='' />\n    <member type='way' ref='106469854' role='' />\n    <member type='way' ref='380369515' role='' />\n    <member type='way' ref='639799875' role='' />\n    <member type='way' ref='47853290' role='' />\n    <member type='way' ref='107087878' role='' />\n    <member type='way' ref='496203661' role='' />\n    <member type='way' ref='496203660' role='' />\n    <member type='way' ref='139162758' role='' />\n    <member type='way' ref='9719783' role='' />\n    <member type='way' ref='32490326' role='' />\n    <member type='way' ref='32507190' role='' />\n    <member type='way' ref='32507189' role='' />\n    <member type='way' ref='32490333' role='' />\n    <member type='way' ref='671174242' role='' />\n    <member type='way' ref='671174241' role='' />\n    <member type='way' ref='32425214' role='' />\n    <member type='way' ref='107092834' role='' />\n    <member type='way' ref='107092824' role='' />\n    <member type='way' ref='666342389' role='' />\n    <member type='way' ref='671174228' role='' />\n    <member type='way' ref='671174227' role='' />\n    <member type='way' ref='671174238' role='' />\n    <member type='way' ref='671174235' role='' />\n    <member type='way' ref='681278520' role='' />\n    <member type='way' ref='96927048' role='' />\n    <member type='way' ref='681278517' role='' />\n    <member type='way' ref='666342339' role='' />\n    <member type='way' ref='666342353' role='' />\n    <member type='way' ref='666342388' role='' />\n    <member type='way' ref='681993519' role='' />\n    <member type='way' ref='681994757' role='' />\n    <member type='way' ref='681994759' role='' />\n    <member type='way' ref='681994758' role='' />\n    <member type='way' ref='32126839' role='' />\n    <member type='way' ref='681278519' role='' />\n    <member type='way' ref='681993518' role='forward' />\n    <member type='way' ref='106879548' role='forward' />\n    <member type='way' ref='105008544' role='forward' />\n    <member type='way' ref='498599201' role='forward' />\n    <member type='way' ref='171052824' role='forward' />\n    <member type='way' ref='498599203' role='forward' />\n    <member type='way' ref='4579009' role='' />\n    <member type='way' ref='106879549' role='' />\n    <member type='way' ref='339319642' role='' />\n    <member type='way' ref='105008547' role='' />\n    <member type='way' ref='671174214' role='' />\n    <member type='way' ref='107101181' role='' />\n    <member type='way' ref='339323421' role='' />\n    <member type='way' ref='339324235' role='' />\n    <member type='way' ref='7930693' role='' />\n    <member type='way' ref='362977288' role='' />\n    <member type='way' ref='42492956' role='' />\n    <member type='way' ref='329058516' role='' />\n    <member type='way' ref='32264713' role='' />\n    <member type='way' ref='799506707' role='' />\n    <member type='way' ref='250270238' role='' />\n    <member type='way' ref='107118533' role='' />\n    <member type='way' ref='342932649' role='' />\n    <member type='way' ref='671174188' role='' />\n    <member type='way' ref='342934219' role='' />\n    <member type='way' ref='362988770' role='' />\n    <member type='way' ref='362988769' role='' />\n    <member type='way' ref='159017840' role='' />\n    <member type='way' ref='344376400' role='' />\n    <member type='way' ref='344376399' role='' />\n    <member type='way' ref='139167173' role='' />\n    <member type='way' ref='107118531' role='' />\n    <member type='way' ref='107118530' role='' />\n    <member type='way' ref='401283787' role='' />\n    <member type='way' ref='7930708' role='' />\n    <member type='way' ref='133538708' role='' />\n    <member type='way' ref='678498433' role='' />\n    <member type='way' ref='678498422' role='' />\n    <member type='way' ref='534813897' role='' />\n    <member type='way' ref='534813895' role='' />\n    <member type='way' ref='534813893' role='' />\n    <member type='way' ref='72488370' role='' />\n    <member type='way' ref='534813892' role='' />\n    <member type='way' ref='370411714' role='' />\n    <member type='way' ref='370411706' role='' />\n    <member type='way' ref='370411711' role='' />\n    <member type='way' ref='370411720' role='' />\n    <member type='way' ref='330387701' role='' />\n    <member type='way' ref='107118523' role='' />\n    <member type='way' ref='7975470' role='' />\n    <member type='way' ref='762505086' role='' />\n    <member type='way' ref='496256039' role='' />\n    <member type='way' ref='30173254' role='' />\n    <member type='way' ref='4579061' role='' />\n    <member type='way' ref='520995116' role='' />\n    <member type='way' ref='369772035' role='' />\n    <member type='way' ref='8041859' role='' />\n    <member type='way' ref='496355624' role='' />\n    <member type='way' ref='369772029' role='' />\n    <member type='way' ref='496107796' role='' />\n    <member type='way' ref='681376587' role='' />\n    <member type='way' ref='681376586' role='' />\n    <member type='way' ref='496120879' role='' />\n    <member type='way' ref='286851410' role='' />\n    <member type='way' ref='825980018' role='' />\n    <member type='way' ref='825980017' role='' />\n    <member type='way' ref='779192151' role='' />\n    <member type='way' ref='275143505' role='' />\n    <member type='way' ref='275143512' role='' />\n    <member type='way' ref='681139362' role='' />\n    <member type='way' ref='681139368' role='' />\n    <member type='way' ref='681139359' role='' />\n    <member type='way' ref='681139343' role='' />\n    <member type='way' ref='8041862' role='' />\n    <member type='way' ref='681139345' role='' />\n    <member type='way' ref='37758197' role='' />\n    <member type='way' ref='52916945' role='' />\n    <member type='way' ref='147338332' role='' />\n    <member type='way' ref='681143398' role='' />\n    <member type='way' ref='681143381' role='' />\n    <member type='way' ref='30628580' role='' />\n    <member type='way' ref='681143392' role='' />\n    <member type='way' ref='681143389' role='' />\n    <member type='way' ref='681143387' role='' />\n    <member type='way' ref='7930695' role='' />\n    <member type='way' ref='534806690' role='' />\n    <member type='way' ref='534806694' role='' />\n    <member type='way' ref='397227936' role='' />\n    <member type='way' ref='433044541' role='' />\n    <member type='way' ref='433044539' role='' />\n    <member type='way' ref='7930690' role='' />\n    <member type='way' ref='107110707' role='' />\n    <member type='way' ref='7771324' role='' />\n    <member type='way' ref='107109292' role='' />\n    <member type='way' ref='107109298' role='' />\n    <member type='way' ref='99345023' role='' />\n    <member type='way' ref='107109296' role='' />\n    <member type='way' ref='107109300' role='' />\n    <member type='way' ref='91308695' role='' />\n    <member type='way' ref='107107549' role='' />\n    <member type='way' ref='91308614' role='' />\n    <member type='way' ref='91266530' role='' />\n    <member type='way' ref='91296424' role='' />\n    <member type='way' ref='91266599' role='' />\n    <member type='way' ref='107107002' role='' />\n    <member type='way' ref='38247548' role='' />\n    <member type='way' ref='103456761' role='' />\n    <member type='way' ref='38247207' role='' />\n    <member type='way' ref='38247203' role='' />\n    <member type='way' ref='38247202' role='' />\n    <member type='way' ref='106468995' role='' />\n    <member type='way' ref='106468989' role='' />\n    <member type='way' ref='106468990' role='' />\n    <member type='way' ref='439818415' role='' />\n    <member type='way' ref='439818414' role='' />\n    <member type='way' ref='457517175' role='' />\n    <member type='way' ref='32571092' role='' />\n    <member type='way' ref='96770100' role='' />\n    <member type='way' ref='96770163' role='' />\n    <member type='way' ref='106468996' role='' />\n    <member type='way' ref='96730041' role='' />\n    <member type='way' ref='96730057' role='' />\n    <member type='way' ref='83239562' role='' />\n    <member type='way' ref='83239554' role='' />\n    <member type='way' ref='363814212' role='' />\n    <member type='way' ref='340431420' role='' />\n    <member type='way' ref='340431421' role='' />\n    <member type='way' ref='340431422' role='' />\n    <member type='way' ref='340373237' role='' />\n    <member type='way' ref='543186940' role='' />\n    <member type='way' ref='408140974' role='' />\n    <member type='way' ref='329019163' role='' />\n    <member type='way' ref='408140981' role='' />\n    <member type='way' ref='408140979' role='' />\n    <member type='way' ref='340373238' role='' />\n    <member type='way' ref='491846406' role='' />\n    <member type='way' ref='491846407' role='' />\n    <member type='way' ref='329034373' role='' />\n    <member type='way' ref='408151819' role='' />\n    <member type='way' ref='119578536' role='' />\n    <member type='way' ref='41574436' role='' />\n    <member type='way' ref='99422492' role='' />\n    <member type='way' ref='107089762' role='' />\n    <member type='way' ref='101225837' role='' />\n    <member type='way' ref='551510528' role='' />\n    <member type='way' ref='83239555' role='' />\n    <member type='way' ref='551510530' role='' />\n    <member type='way' ref='340379027' role='' />\n    <member type='way' ref='103433605' role='' />\n    <member type='way' ref='103433604' role='' />\n    <member type='way' ref='340379033' role='' />\n    <member type='way' ref='101221453' role='' />\n    <member type='way' ref='302414774' role='' />\n    <member type='way' ref='101221451' role='' />\n    <member type='way' ref='584856108' role='' />\n    <member type='way' ref='371905712' role='' />\n    <member type='way' ref='101222637' role='' />\n    <member type='way' ref='101222640' role='' />\n    <member type='way' ref='110097624' role='' />\n    <member type='way' ref='100956317' role='' />\n    <member type='way' ref='100956323' role='' />\n    <member type='way' ref='371905694' role='' />\n    <member type='way' ref='101183743' role='' />\n    <member type='way' ref='100956321' role='' />\n    <member type='way' ref='371905643' role='' />\n    <member type='way' ref='371905647' role='' />\n    <member type='way' ref='371905696' role='' />\n    <member type='way' ref='100956316' role='' />\n    <member type='way' ref='83239566' role='' />\n    <member type='way' ref='100360012' role='' />\n    <member type='way' ref='100956315' role='' />\n    <member type='way' ref='100960855' role='' />\n    <member type='way' ref='100966147' role='' />\n    <member type='way' ref='339916464' role='' />\n    <member type='way' ref='339916462' role='' />\n    <member type='way' ref='100966143' role='' />\n    <member type='way' ref='110683137' role='' />\n    <member type='way' ref='110673228' role='' />\n    <member type='way' ref='339921430' role='' />\n    <member type='way' ref='83239553' role='' />\n    <member type='way' ref='100774829' role='' />\n    <member type='way' ref='100969033' role='' />\n    <member type='way' ref='313057086' role='' />\n    <member type='way' ref='301618699' role='' />\n    <member type='way' ref='301618698' role='' />\n    <member type='way' ref='99785156' role='' />\n    <member type='way' ref='99785236' role='' />\n    <member type='way' ref='99785123' role='' />\n    <member type='way' ref='301618700' role='' />\n    <member type='way' ref='83239557' role='' />\n    <member type='way' ref='126711354' role='' />\n    <member type='way' ref='126711357' role='' />\n    <member type='way' ref='83239565' role='' />\n    <member type='way' ref='83239568' role='' />\n    <member type='way' ref='126711337' role='' />\n    <member type='way' ref='126711341' role='' />\n    <member type='way' ref='126866620' role='' />\n    <member type='way' ref='126866619' role='' />\n    <member type='way' ref='126866621' role='' />\n    <member type='way' ref='114395328' role='' />\n    <member type='way' ref='114395326' role='' />\n    <member type='way' ref='114395327' role='' />\n    <member type='way' ref='83239563' role='' />\n    <member type='way' ref='25631925' role='' />\n    <member type='way' ref='25631931' role='' />\n    <member type='way' ref='35773014' role='' />\n    <member type='way' ref='100824364' role='' />\n    <member type='way' ref='100824355' role='' />\n    <member type='way' ref='35773013' role='' />\n    <member type='way' ref='25631926' role='' />\n    <member type='way' ref='90231643' role='' />\n    <member type='way' ref='44164797' role='' />\n    <member type='way' ref='44164796' role='' />\n    <member type='way' ref='44164794' role='' />\n    <member type='way' ref='13799575' role='' />\n    <member type='way' ref='100822677' role='' />\n    <member type='way' ref='100822679' role='' />\n    <member type='way' ref='13799576' role='' />\n    <member type='way' ref='13799577' role='' />\n    <member type='way' ref='339641078' role='' />\n    <member type='way' ref='340537015' role='' />\n    <member type='way' ref='340537014' role='' />\n    <member type='way' ref='340537022' role='' />\n    <member type='way' ref='340537020' role='' />\n    <member type='way' ref='666326583' role='' />\n    <member type='way' ref='666326572' role='' />\n    <member type='way' ref='666326573' role='' />\n    <member type='way' ref='340537021' role='' />\n    <member type='way' ref='742025418' role='' />\n    <member type='way' ref='107106207' role='' />\n    <member type='way' ref='365281133' role='' />\n    <member type='way' ref='666326224' role='' />\n    <member type='way' ref='666326223' role='' />\n    <member type='way' ref='666326578' role='' />\n    <member type='way' ref='340537023' role='' />\n    <member type='way' ref='366882621' role='' />\n    <member type='way' ref='835449957' role='' />\n    <member type='way' ref='675094113' role='' />\n    <member type='way' ref='8151651' role='' />\n    <member type='way' ref='675094112' role='' />\n    <member type='way' ref='366868461' role='' />\n    <member type='way' ref='366868460' role='' />\n    <member type='way' ref='835449959' role='' />\n    <member type='way' ref='191259377' role='' />\n    <member type='way' ref='433222891' role='' />\n    <member type='way' ref='96927056' role='' />\n    <member type='way' ref='671174237' role='' />\n    <member type='way' ref='671174233' role='' />\n    <member type='way' ref='666342392' role='' />\n    <member type='way' ref='700962269' role='' />\n    <member type='way' ref='666342391' role='' />\n    <member type='way' ref='666342390' role='' />\n    <member type='way' ref='671174229' role='' />\n    <member type='way' ref='666342331' role='' />\n    <member type='way' ref='128572670' role='' />\n    <member type='way' ref='681512377' role='' />\n    <member type='way' ref='681512378' role='' />\n    <member type='way' ref='339311503' role='' />\n    <member type='way' ref='671174221' role='forward' />\n    <member type='way' ref='31858712' role='forward' />\n    <member type='way' ref='671174192' role='forward' />\n    <member type='way' ref='671174206' role='forward' />\n    <member type='way' ref='253088403' role='forward' />\n    <member type='way' ref='671174204' role='forward' />\n    <member type='way' ref='671174205' role='forward' />\n    <member type='way' ref='681931235' role='backward' />\n    <member type='way' ref='671174190' role='forward' />\n    <member type='way' ref='671174217' role='forward' />\n    <member type='way' ref='671174209' role='forward' />\n    <member type='way' ref='339309071' role='forward' />\n    <member type='way' ref='671174194' role='forward' />\n    <member type='way' ref='671174201' role='forward' />\n    <member type='way' ref='280728524' role='forward' />\n    <member type='way' ref='107102242' role='' />\n    <member type='way' ref='617778481' role='' />\n    <member type='way' ref='617778483' role='' />\n    <member type='way' ref='107102445' role='' />\n    <member type='way' ref='617778482' role='' />\n    <member type='way' ref='32065417' role='' />\n    <member type='way' ref='623897350' role='' />\n    <member type='way' ref='623897349' role='' />\n    <member type='way' ref='107102444' role='' />\n    <member type='way' ref='107102443' role='' />\n    <member type='way' ref='336981028' role='' />\n    <member type='way' ref='336981027' role='' />\n    <member type='way' ref='32065422' role='' />\n    <member type='way' ref='34208039' role='' />\n    <member type='way' ref='107105420' role='' />\n    <member type='way' ref='32345147' role='' />\n    <member type='way' ref='744774028' role='' />\n    <member type='way' ref='107105438' role='' />\n    <member type='way' ref='584967709' role='' />\n    <member type='way' ref='845875911' role='' />\n    <member type='way' ref='753706472' role='' />\n    <member type='way' ref='753706473' role='' />\n    <member type='way' ref='695558519' role='' />\n    <member type='way' ref='99756357' role='forward' />\n    <member type='way' ref='496256037' role='' />\n    <member type='way' ref='496256038' role='' />\n    <member type='way' ref='369772028' role='' />\n    <member type='way' ref='394522512' role='' />\n    <member type='way' ref='4579004' role='' />\n    <member type='way' ref='809368764' role='' />\n    <member type='way' ref='809368756' role='' />\n    <member type='way' ref='437244814' role='' />\n    <member type='way' ref='437244815' role='' />\n    <member type='way' ref='369772040' role='' />\n    <member type='way' ref='286684698' role='' />\n    <member type='way' ref='825980024' role='' />\n    <member type='way' ref='825980025' role='' />\n    <member type='way' ref='368771668' role='' />\n    <member type='way' ref='825980021' role='' />\n    <member type='way' ref='368771669' role='' />\n    <member type='way' ref='825980013' role='' />\n    <member type='way' ref='779192145' role='' />\n    <member type='way' ref='779192146' role='' />\n    <member type='way' ref='825980029' role='' />\n    <member type='way' ref='275143515' role='' />\n    <member type='way' ref='681139367' role='' />\n    <member type='way' ref='107114265' role='' />\n    <member type='way' ref='681751883' role='' />\n    <member type='way' ref='681751882' role='' />\n    <member type='way' ref='158843013' role='' />\n    <member type='way' ref='339536179' role='' />\n    <member type='way' ref='99756366' role='' />\n    <member type='way' ref='455047412' role='' />\n    <member type='way' ref='35317171' role='' />\n    <member type='way' ref='365281135' role='' />\n    <member type='way' ref='340373234' role='' />\n    <member type='way' ref='455047414' role='forward' />\n    <member type='way' ref='666326221' role='forward' />\n    <member type='way' ref='666326222' role='backward' />\n    <member type='way' ref='107106214' role='backward' />\n    <member type='way' ref='107105449' role='backward' />\n    <member type='way' ref='675094114' role='' />\n    <member type='way' ref='366868471' role='' />\n    <member type='way' ref='366870732' role='' />\n    <member type='way' ref='675094106' role='' />\n    <member type='way' ref='366868462' role='' />\n    <member type='way' ref='675094094' role='' />\n    <member type='way' ref='366882640' role='' />\n    <member type='way' ref='675094095' role='' />\n    <tag k='distance' v='180 km' />\n    <tag k='name' v='Limfjordsruten' />\n    <tag k='network' v='rcn' />\n    <tag k='ref' v='23' />\n    <tag k='route' v='bicycle' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='9452382' timestamp='2019-04-02T12:57:40Z' uid='2677992' user='mikkolukas' visible='true' version='1' changeset='68790000'>\n    <member type='node' ref='911299696' role='' />\n    <member type='way' ref='681139388' role='platform' />\n    <member type='node' ref='6378459192' role='stop' />\n    <member type='node' ref='6378459190' role='stop' />\n    <member type='node' ref='6378459195' role='' />\n    <member type='way' ref='681139386' role='platform' />\n    <tag k='bus' v='yes' />\n    <tag k='name' v='Vandværket' />\n    <tag k='network' v='Nordjyllands Trafikselskab' />\n    <tag k='network:wikidata' v='Q3246599' />\n    <tag k='public_transport' v='stop_area' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref_name' v='Vandværket (Sønderbro / Aalborg)' />\n    <tag k='type' v='public_transport' />\n  </relation>\n  <relation id='10077367' timestamp='2020-10-06T09:03:11Z' uid='811530' user='RBOCity' visible='true' version='4' changeset='92034593'>\n    <member type='relation' ref='10081258' role='' />\n    <member type='relation' ref='10081257' role='' />\n    <member type='relation' ref='10077366' role='' />\n    <member type='relation' ref='9238147' role='' />\n    <tag k='from' v='Hannover ZOB/Hamburg ZOB' />\n    <tag k='name' v='Flixbus 172: Hannover/Hamburg &lt;=&gt; Aarhus/Aalborg' />\n    <tag k='name:da' v='Flixbus 172: Hannover/Hamburg &lt;=&gt; Aarhus/Aalborg' />\n    <tag k='name:de' v='Flixbus 172: Hannover/Hamburg &lt;=&gt; Aarhus/Aalborg' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='ref' v='FLX172' />\n    <tag k='route_master' v='bus' />\n    <tag k='to' v='Aarhus, Busbahnhof/Aalborg, Busbahnhof' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='10077721' timestamp='2020-09-23T12:35:39Z' uid='11211647' user='wermak' visible='true' version='64' changeset='91367620'>\n    <member type='node' ref='5274710734' role='stop' />\n    <member type='way' ref='545607311' role='platform' />\n    <member type='node' ref='2868414792' role='stop' />\n    <member type='node' ref='6046917225' role='platform' />\n    <member type='node' ref='2868414789' role='stop' />\n    <member type='node' ref='6046917223' role='platform' />\n    <member type='node' ref='2896384055' role='stop' />\n    <member type='node' ref='6823522163' role='stop' />\n    <member type='way' ref='553699678' role='platform' />\n    <member type='node' ref='367042031' role='platform' />\n    <member type='node' ref='6223447952' role='stop' />\n    <member type='way' ref='513595672' role='platform' />\n    <member type='node' ref='5974391200' role='stop' />\n    <member type='node' ref='1987802672' role='stop' />\n    <member type='node' ref='7647740030' role='platform' />\n    <member type='node' ref='1987802673' role='stop' />\n    <member type='node' ref='7647758213' role='platform' />\n    <member type='node' ref='1987802675' role='stop' />\n    <member type='node' ref='7647758212' role='platform' />\n    <member type='node' ref='1987802677' role='stop' />\n    <member type='node' ref='7647758211' role='platform' />\n    <member type='node' ref='1987802678' role='stop' />\n    <member type='node' ref='7647758210' role='platform' />\n    <member type='node' ref='7779170659' role='stop' />\n    <member type='node' ref='7650108367' role='platform' />\n    <member type='node' ref='3393833062' role='stop' />\n    <member type='way' ref='332263376' role='platform' />\n    <member type='way' ref='641315930' role='' />\n    <member type='way' ref='633919289' role='' />\n    <member type='way' ref='633919298' role='' />\n    <member type='way' ref='843800627' role='' />\n    <member type='way' ref='633919297' role='' />\n    <member type='way' ref='684798228' role='' />\n    <member type='way' ref='671588463' role='' />\n    <member type='way' ref='684798218' role='' />\n    <member type='way' ref='684798225' role='' />\n    <member type='way' ref='684798226' role='' />\n    <member type='way' ref='671885430' role='' />\n    <member type='way' ref='844007290' role='' />\n    <member type='way' ref='213138024' role='' />\n    <member type='way' ref='9874982' role='' />\n    <member type='way' ref='29277854' role='' />\n    <member type='way' ref='9874983' role='' />\n    <member type='way' ref='4400484' role='' />\n    <member type='way' ref='335487064' role='' />\n    <member type='way' ref='4446509' role='' />\n    <member type='way' ref='84792455' role='' />\n    <member type='way' ref='4525444' role='' />\n    <member type='way' ref='62682272' role='' />\n    <member type='way' ref='24851759' role='' />\n    <member type='way' ref='24851760' role='' />\n    <member type='way' ref='459811453' role='' />\n    <member type='way' ref='4434516' role='' />\n    <member type='way' ref='459811452' role='' />\n    <member type='way' ref='305608389' role='' />\n    <member type='way' ref='4495210' role='' />\n    <member type='way' ref='428725174' role='' />\n    <member type='way' ref='428725175' role='' />\n    <member type='way' ref='4388869' role='' />\n    <member type='way' ref='328440042' role='' />\n    <member type='way' ref='328440044' role='' />\n    <member type='way' ref='4764112' role='' />\n    <member type='way' ref='460088570' role='' />\n    <member type='way' ref='4484035' role='' />\n    <member type='way' ref='460088569' role='' />\n    <member type='way' ref='4388117' role='' />\n    <member type='way' ref='183596331' role='' />\n    <member type='way' ref='304559704' role='' />\n    <member type='way' ref='463132254' role='' />\n    <member type='way' ref='24596041' role='' />\n    <member type='way' ref='4484032' role='' />\n    <member type='way' ref='24596077' role='' />\n    <member type='way' ref='24596082' role='' />\n    <member type='way' ref='4484053' role='' />\n    <member type='way' ref='183596328' role='' />\n    <member type='way' ref='334018085' role='' />\n    <member type='way' ref='28658023' role='' />\n    <member type='way' ref='334025504' role='' />\n    <member type='way' ref='463139369' role='' />\n    <member type='way' ref='4413381' role='' />\n    <member type='way' ref='28658008' role='' />\n    <member type='way' ref='463143005' role='' />\n    <member type='way' ref='4484054' role='' />\n    <member type='way' ref='463150094' role='' />\n    <member type='way' ref='4537045' role='' />\n    <member type='way' ref='463150093' role='' />\n    <member type='way' ref='24599279' role='' />\n    <member type='way' ref='210926364' role='' />\n    <member type='way' ref='4537044' role='' />\n    <member type='way' ref='463175642' role='' />\n    <member type='way' ref='206802936' role='' />\n    <member type='way' ref='43286706' role='' />\n    <member type='way' ref='4396785' role='' />\n    <member type='way' ref='24601402' role='' />\n    <member type='way' ref='215668778' role='' />\n    <member type='way' ref='215668777' role='' />\n    <member type='way' ref='215668776' role='' />\n    <member type='way' ref='4410137' role='' />\n    <member type='way' ref='25776266' role='' />\n    <member type='way' ref='25776267' role='' />\n    <member type='way' ref='215498840' role='' />\n    <member type='way' ref='25895568' role='' />\n    <member type='way' ref='25895569' role='' />\n    <member type='way' ref='215577414' role='' />\n    <member type='way' ref='4538140' role='' />\n    <member type='way' ref='4538139' role='' />\n    <member type='way' ref='215577411' role='' />\n    <member type='way' ref='215577407' role='' />\n    <member type='way' ref='4538154' role='' />\n    <member type='way' ref='4538153' role='' />\n    <member type='way' ref='4540087' role='' />\n    <member type='way' ref='215577423' role='' />\n    <member type='way' ref='4540089' role='' />\n    <member type='way' ref='215577409' role='' />\n    <member type='way' ref='4540097' role='' />\n    <member type='way' ref='4706418' role='' />\n    <member type='way' ref='215598578' role='' />\n    <member type='way' ref='215598579' role='' />\n    <member type='way' ref='4706419' role='' />\n    <member type='way' ref='4410331' role='' />\n    <member type='way' ref='18786053' role='' />\n    <member type='way' ref='18786054' role='' />\n    <member type='way' ref='18783894' role='' />\n    <member type='way' ref='18783891' role='' />\n    <member type='way' ref='628831855' role='' />\n    <member type='way' ref='628831858' role='' />\n    <member type='way' ref='4410419' role='' />\n    <member type='way' ref='25960123' role='' />\n    <member type='way' ref='390589598' role='' />\n    <member type='way' ref='390589603' role='' />\n    <member type='way' ref='390589600' role='' />\n    <member type='way' ref='390589602' role='' />\n    <member type='way' ref='309842379' role='' />\n    <member type='way' ref='309842384' role='' />\n    <member type='way' ref='25960009' role='' />\n    <member type='way' ref='5043521' role='' />\n    <member type='way' ref='5043522' role='' />\n    <member type='way' ref='5043515' role='' />\n    <member type='way' ref='285960027' role='' />\n    <member type='way' ref='285960029' role='' />\n    <member type='way' ref='285960025' role='' />\n    <member type='way' ref='285960023' role='' />\n    <member type='way' ref='308551002' role='' />\n    <member type='way' ref='308551003' role='' />\n    <member type='way' ref='262946933' role='' />\n    <member type='way' ref='361040278' role='' />\n    <member type='way' ref='4795210' role='' />\n    <member type='way' ref='755570388' role='' />\n    <member type='way' ref='755570389' role='' />\n    <member type='way' ref='755570390' role='' />\n    <member type='way' ref='262946941' role='' />\n    <member type='way' ref='172580050' role='' />\n    <member type='way' ref='153031137' role='' />\n    <member type='way' ref='152156391' role='' />\n    <member type='way' ref='262946945' role='' />\n    <member type='way' ref='152156388' role='' />\n    <member type='way' ref='4569841' role='' />\n    <member type='way' ref='4481906' role='' />\n    <member type='way' ref='4068037' role='' />\n    <member type='way' ref='262943263' role='' />\n    <member type='way' ref='262943267' role='' />\n    <member type='way' ref='26625516' role='' />\n    <member type='way' ref='262943266' role='' />\n    <member type='way' ref='700729791' role='' />\n    <member type='way' ref='850491473' role='' />\n    <member type='way' ref='850491474' role='' />\n    <member type='way' ref='850491475' role='' />\n    <member type='way' ref='309124780' role='' />\n    <member type='way' ref='545894115' role='' />\n    <member type='way' ref='355143739' role='' />\n    <member type='way' ref='317989123' role='' />\n    <member type='way' ref='355143735' role='' />\n    <member type='way' ref='166493805' role='' />\n    <member type='way' ref='409280768' role='' />\n    <member type='way' ref='409280763' role='' />\n    <member type='way' ref='25590037' role='' />\n    <member type='way' ref='315327879' role='' />\n    <member type='way' ref='337775286' role='' />\n    <member type='way' ref='266602641' role='' />\n    <member type='way' ref='409280765' role='' />\n    <member type='way' ref='409280764' role='' />\n    <member type='way' ref='355143719' role='' />\n    <member type='way' ref='355143718' role='' />\n    <member type='way' ref='266602643' role='' />\n    <member type='way' ref='337775285' role='' />\n    <member type='way' ref='315327880' role='' />\n    <member type='way' ref='322481059' role='' />\n    <member type='way' ref='322481050' role='' />\n    <member type='way' ref='322481054' role='' />\n    <member type='way' ref='322481060' role='' />\n    <member type='way' ref='337772424' role='' />\n    <member type='way' ref='337772427' role='' />\n    <member type='way' ref='337772423' role='' />\n    <member type='way' ref='337772420' role='' />\n    <member type='way' ref='337770859' role='' />\n    <member type='way' ref='337770858' role='' />\n    <member type='way' ref='337770857' role='' />\n    <member type='way' ref='337770856' role='' />\n    <member type='way' ref='266602637' role='' />\n    <member type='way' ref='266602638' role='' />\n    <member type='way' ref='337770677' role='' />\n    <member type='way' ref='337770680' role='' />\n    <member type='way' ref='337770679' role='' />\n    <member type='way' ref='527002069' role='' />\n    <member type='way' ref='527002068' role='' />\n    <member type='way' ref='337770678' role='' />\n    <member type='way' ref='337767919' role='' />\n    <member type='way' ref='266596659' role='' />\n    <member type='way' ref='266596656' role='' />\n    <member type='way' ref='266596645' role='' />\n    <member type='way' ref='337763004' role='' />\n    <member type='way' ref='4040510' role='' />\n    <member type='way' ref='4723215' role='' />\n    <member type='way' ref='337763002' role='' />\n    <member type='way' ref='322481047' role='' />\n    <member type='way' ref='796892801' role='' />\n    <member type='way' ref='172373613' role='' />\n    <member type='way' ref='337762625' role='' />\n    <member type='way' ref='796892800' role='' />\n    <member type='way' ref='337762624' role='' />\n    <member type='way' ref='337762621' role='' />\n    <member type='way' ref='337762618' role='' />\n    <member type='way' ref='59559315' role='' />\n    <member type='way' ref='59559317' role='' />\n    <member type='way' ref='337761545' role='' />\n    <member type='way' ref='337761546' role='' />\n    <member type='way' ref='337760682' role='' />\n    <member type='way' ref='337760683' role='' />\n    <member type='way' ref='337760289' role='' />\n    <member type='way' ref='285957496' role='' />\n    <member type='way' ref='285957481' role='' />\n    <member type='way' ref='337760288' role='' />\n    <member type='way' ref='337758887' role='' />\n    <member type='way' ref='285957493' role='' />\n    <member type='way' ref='44492466' role='' />\n    <member type='way' ref='337758886' role='' />\n    <member type='way' ref='337758885' role='' />\n    <member type='way' ref='285957480' role='' />\n    <member type='way' ref='285957488' role='' />\n    <member type='way' ref='285957484' role='' />\n    <member type='way' ref='285957495' role='' />\n    <member type='way' ref='4040518' role='' />\n    <member type='way' ref='265480013' role='' />\n    <member type='way' ref='265480003' role='' />\n    <member type='way' ref='265480011' role='' />\n    <member type='way' ref='337606970' role='' />\n    <member type='way' ref='190344822' role='' />\n    <member type='way' ref='4040487' role='' />\n    <member type='way' ref='108407924' role='' />\n    <member type='way' ref='439916015' role='' />\n    <member type='way' ref='4040480' role='' />\n    <member type='way' ref='95458109' role='' />\n    <member type='way' ref='337606268' role='' />\n    <member type='way' ref='315331343' role='' />\n    <member type='way' ref='22662167' role='' />\n    <member type='way' ref='22662168' role='' />\n    <member type='way' ref='30081451' role='' />\n    <member type='way' ref='22662169' role='' />\n    <member type='way' ref='22662170' role='' />\n    <member type='way' ref='4040477' role='' />\n    <member type='way' ref='817927638' role='' />\n    <member type='way' ref='4040473' role='' />\n    <member type='way' ref='390589617' role='' />\n    <member type='way' ref='390589620' role='' />\n    <member type='way' ref='390589619' role='' />\n    <member type='way' ref='390589623' role='' />\n    <member type='way' ref='4040471' role='' />\n    <member type='way' ref='152476233' role='' />\n    <member type='way' ref='152476237' role='' />\n    <member type='way' ref='265465914' role='' />\n    <member type='way' ref='62050283' role='' />\n    <member type='way' ref='265465912' role='' />\n    <member type='way' ref='4040469' role='' />\n    <member type='way' ref='44535957' role='' />\n    <member type='way' ref='44535958' role='' />\n    <member type='way' ref='390589616' role='' />\n    <member type='way' ref='390589614' role='' />\n    <member type='way' ref='390589613' role='' />\n    <member type='way' ref='390589615' role='' />\n    <member type='way' ref='4040467' role='' />\n    <member type='way' ref='322481052' role='' />\n    <member type='way' ref='322481056' role='' />\n    <member type='way' ref='5205015' role='' />\n    <member type='way' ref='322481049' role='' />\n    <member type='way' ref='322481042' role='' />\n    <member type='way' ref='5205016' role='' />\n    <member type='way' ref='390423485' role='' />\n    <member type='way' ref='390423480' role='' />\n    <member type='way' ref='390423482' role='' />\n    <member type='way' ref='390423481' role='' />\n    <member type='way' ref='4040465' role='' />\n    <member type='way' ref='322481048' role='' />\n    <member type='way' ref='322481055' role='' />\n    <member type='way' ref='322481040' role='' />\n    <member type='way' ref='322481062' role='' />\n    <member type='way' ref='346629133' role='' />\n    <member type='way' ref='346629134' role='' />\n    <member type='way' ref='4040461' role='' />\n    <member type='way' ref='10559724' role='' />\n    <member type='way' ref='10559725' role='' />\n    <member type='way' ref='4040459' role='' />\n    <member type='way' ref='79658134' role='' />\n    <member type='way' ref='79658137' role='' />\n    <member type='way' ref='322698051' role='' />\n    <member type='way' ref='796931736' role='' />\n    <member type='way' ref='322698056' role='' />\n    <member type='way' ref='796931734' role='' />\n    <member type='way' ref='322698047' role='' />\n    <member type='way' ref='322698066' role='' />\n    <member type='way' ref='4040457' role='' />\n    <member type='way' ref='390449821' role='' />\n    <member type='way' ref='390449834' role='' />\n    <member type='way' ref='390449825' role='' />\n    <member type='way' ref='390449815' role='' />\n    <member type='way' ref='390449833' role='' />\n    <member type='way' ref='390449813' role='' />\n    <member type='way' ref='390449826' role='' />\n    <member type='way' ref='390449817' role='' />\n    <member type='way' ref='4040455' role='' />\n    <member type='way' ref='233501307' role='' />\n    <member type='way' ref='233501311' role='' />\n    <member type='way' ref='152476585' role='' />\n    <member type='way' ref='322857227' role='' />\n    <member type='way' ref='322857222' role='' />\n    <member type='way' ref='162152567' role='' />\n    <member type='way' ref='162152569' role='' />\n    <member type='way' ref='322857230' role='' />\n    <member type='way' ref='119049017' role='' />\n    <member type='way' ref='119049007' role='' />\n    <member type='way' ref='61722561' role='' />\n    <member type='way' ref='152653170' role='' />\n    <member type='way' ref='4040433' role='' />\n    <member type='way' ref='44572262' role='' />\n    <member type='way' ref='92801060' role='' />\n    <member type='way' ref='92801063' role='' />\n    <member type='way' ref='44572263' role='' />\n    <member type='way' ref='142005931' role='' />\n    <member type='way' ref='685806985' role='' />\n    <member type='way' ref='49688861' role='' />\n    <member type='way' ref='235016459' role='' />\n    <member type='way' ref='235016458' role='' />\n    <member type='way' ref='188660393' role='' />\n    <member type='way' ref='297016984' role='' />\n    <member type='way' ref='59411571' role='' />\n    <member type='way' ref='297016982' role='' />\n    <member type='way' ref='297016979' role='' />\n    <member type='way' ref='61722543' role='' />\n    <member type='way' ref='44572595' role='' />\n    <member type='way' ref='390449824' role='' />\n    <member type='way' ref='390449811' role='' />\n    <member type='way' ref='390449829' role='' />\n    <member type='way' ref='390449802' role='' />\n    <member type='way' ref='61722504' role='' />\n    <member type='way' ref='154661295' role='' />\n    <member type='way' ref='322698062' role='' />\n    <member type='way' ref='322698057' role='' />\n    <member type='way' ref='322698058' role='' />\n    <member type='way' ref='322698061' role='' />\n    <member type='way' ref='797348085' role='' />\n    <member type='way' ref='797348084' role='' />\n    <member type='way' ref='797348086' role='' />\n    <member type='way' ref='797348088' role='' />\n    <member type='way' ref='797348087' role='' />\n    <member type='way' ref='797348089' role='' />\n    <member type='way' ref='255729002' role='' />\n    <member type='way' ref='255729009' role='' />\n    <member type='way' ref='322857225' role='' />\n    <member type='way' ref='322857223' role='' />\n    <member type='way' ref='797353235' role='' />\n    <member type='way' ref='24410526' role='' />\n    <member type='way' ref='24410525' role='' />\n    <member type='way' ref='322857231' role='' />\n    <member type='way' ref='154669109' role='' />\n    <member type='way' ref='154669112' role='' />\n    <member type='way' ref='269819021' role='' />\n    <member type='way' ref='390449814' role='' />\n    <member type='way' ref='390449820' role='' />\n    <member type='way' ref='269819020' role='' />\n    <member type='way' ref='269818131' role='' />\n    <member type='way' ref='269818130' role='' />\n    <member type='way' ref='322698050' role='' />\n    <member type='way' ref='322698048' role='' />\n    <member type='way' ref='322698049' role='' />\n    <member type='way' ref='322698046' role='' />\n    <member type='way' ref='327302625' role='' />\n    <member type='way' ref='845411484' role='' />\n    <member type='way' ref='121552398' role='' />\n    <member type='way' ref='121552401' role='' />\n    <member type='way' ref='390575951' role='' />\n    <member type='way' ref='390575950' role='' />\n    <member type='way' ref='390575948' role='' />\n    <member type='way' ref='390575954' role='' />\n    <member type='way' ref='845392960' role='' />\n    <member type='way' ref='845392959' role='' />\n    <member type='way' ref='44572967' role='' />\n    <member type='way' ref='44572968' role='' />\n    <member type='way' ref='324319625' role='' />\n    <member type='way' ref='26197919' role='' />\n    <member type='way' ref='154912056' role='' />\n    <member type='way' ref='324319670' role='' />\n    <member type='way' ref='324319632' role='' />\n    <member type='way' ref='324319683' role='' />\n    <member type='way' ref='43070722' role='' />\n    <member type='way' ref='43070723' role='' />\n    <member type='way' ref='43070716' role='' />\n    <member type='way' ref='43070717' role='' />\n    <member type='way' ref='38330796' role='' />\n    <member type='way' ref='38330797' role='' />\n    <member type='way' ref='38330803' role='' />\n    <member type='way' ref='38330802' role='' />\n    <member type='way' ref='43070127' role='' />\n    <member type='way' ref='43070128' role='' />\n    <member type='way' ref='324319642' role='' />\n    <member type='way' ref='324319655' role='' />\n    <member type='way' ref='324319617' role='' />\n    <member type='way' ref='324319602' role='' />\n    <member type='way' ref='845382861' role='' />\n    <member type='way' ref='845382862' role='' />\n    <member type='way' ref='845382863' role='' />\n    <member type='way' ref='845382864' role='' />\n    <member type='way' ref='236309388' role='' />\n    <member type='way' ref='297019622' role='' />\n    <member type='way' ref='297019624' role='' />\n    <member type='way' ref='86150804' role='' />\n    <member type='way' ref='297019625' role='' />\n    <member type='way' ref='360941491' role='' />\n    <member type='way' ref='297017919' role='' />\n    <member type='way' ref='297017921' role='' />\n    <member type='way' ref='324319645' role='' />\n    <member type='way' ref='324319607' role='' />\n    <member type='way' ref='324319649' role='' />\n    <member type='way' ref='324319596' role='' />\n    <member type='way' ref='360941486' role='' />\n    <member type='way' ref='10780690' role='' />\n    <member type='way' ref='10780691' role='' />\n    <member type='way' ref='360941488' role='' />\n    <member type='way' ref='154669108' role='' />\n    <member type='way' ref='155603534' role='' />\n    <member type='way' ref='324477924' role='' />\n    <member type='way' ref='324477927' role='' />\n    <member type='way' ref='324477925' role='' />\n    <member type='way' ref='324477934' role='' />\n    <member type='way' ref='844897623' role='' />\n    <member type='way' ref='844897624' role='' />\n    <member type='way' ref='154669110' role='' />\n    <member type='way' ref='154669114' role='' />\n    <member type='way' ref='844897620' role='' />\n    <member type='way' ref='844897619' role='' />\n    <member type='way' ref='5096309' role='' />\n    <member type='way' ref='10780636' role='' />\n    <member type='way' ref='10780637' role='' />\n    <member type='way' ref='324477926' role='' />\n    <member type='way' ref='324477920' role='' />\n    <member type='way' ref='324477918' role='' />\n    <member type='way' ref='324477928' role='' />\n    <member type='way' ref='4040395' role='' />\n    <member type='way' ref='4040394' role='' />\n    <member type='way' ref='360941493' role='' />\n    <member type='way' ref='4379536' role='' />\n    <member type='way' ref='386850095' role='' />\n    <member type='way' ref='386850100' role='' />\n    <member type='way' ref='142005925' role='' />\n    <member type='way' ref='152472219' role='' />\n    <member type='way' ref='318717779' role='' />\n    <member type='way' ref='319031786' role='' />\n    <member type='way' ref='319031788' role='' />\n    <member type='way' ref='666220546' role='' />\n    <member type='way' ref='30418547' role='' />\n    <member type='way' ref='666220545' role='' />\n    <member type='way' ref='316907870' role='' />\n    <member type='way' ref='4038098' role='' />\n    <member type='way' ref='10626367' role='' />\n    <member type='way' ref='10626368' role='' />\n    <member type='way' ref='316907867' role='' />\n    <member type='way' ref='316907868' role='' />\n    <member type='way' ref='30418546' role='' />\n    <member type='way' ref='308319038' role='' />\n    <member type='way' ref='666367555' role='' />\n    <member type='way' ref='85636520' role='' />\n    <member type='way' ref='362888812' role='' />\n    <member type='way' ref='362888810' role='' />\n    <member type='way' ref='308319037' role='' />\n    <member type='way' ref='666367557' role='' />\n    <member type='way' ref='221383253' role='' />\n    <member type='way' ref='362888808' role='' />\n    <member type='way' ref='221387032' role='' />\n    <member type='way' ref='53670606' role='' />\n    <member type='way' ref='506047549' role='' />\n    <member type='way' ref='85634895' role='' />\n    <member type='way' ref='666367556' role='' />\n    <member type='way' ref='157044906' role='' />\n    <member type='way' ref='221650917' role='' />\n    <member type='way' ref='27837749' role='' />\n    <member type='way' ref='27837750' role='' />\n    <member type='way' ref='458320751' role='' />\n    <member type='way' ref='458320750' role='' />\n    <member type='way' ref='49170325' role='' />\n    <member type='way' ref='458320754' role='' />\n    <member type='way' ref='4379638' role='' />\n    <member type='way' ref='458320749' role='' />\n    <member type='way' ref='151998689' role='' />\n    <member type='way' ref='74412381' role='' />\n    <member type='way' ref='473534140' role='' />\n    <member type='way' ref='683410800' role='' />\n    <member type='way' ref='74412385' role='' />\n    <member type='way' ref='396844602' role='' />\n    <member type='way' ref='653717355' role='' />\n    <member type='way' ref='74412368' role='' />\n    <member type='way' ref='74412353' role='' />\n    <member type='way' ref='228014552' role='' />\n    <member type='way' ref='74464221' role='' />\n    <member type='way' ref='239778161' role='' />\n    <member type='way' ref='74412354' role='' />\n    <member type='way' ref='74412390' role='' />\n    <member type='way' ref='147756932' role='' />\n    <member type='way' ref='147756935' role='' />\n    <member type='way' ref='45748889' role='' />\n    <member type='way' ref='239371692' role='' />\n    <member type='way' ref='756661406' role='' />\n    <member type='way' ref='252773238' role='' />\n    <member type='way' ref='48657278' role='' />\n    <member type='way' ref='27805059' role='' />\n    <member type='way' ref='27805060' role='' />\n    <member type='way' ref='60417897' role='' />\n    <member type='way' ref='528961147' role='' />\n    <member type='way' ref='60417891' role='' />\n    <member type='way' ref='147936468' role='' />\n    <member type='way' ref='528961146' role='' />\n    <member type='way' ref='27805062' role='' />\n    <member type='way' ref='528961148' role='' />\n    <member type='way' ref='528961149' role='' />\n    <member type='way' ref='528961150' role='' />\n    <member type='way' ref='27805063' role='' />\n    <member type='way' ref='49618809' role='' />\n    <member type='way' ref='27805070' role='' />\n    <member type='way' ref='640963262' role='' />\n    <member type='way' ref='486851057' role='' />\n    <member type='way' ref='528961152' role='' />\n    <member type='way' ref='528961153' role='' />\n    <member type='way' ref='640963273' role='' />\n    <member type='way' ref='640963275' role='' />\n    <member type='way' ref='528961137' role='' />\n    <member type='way' ref='39981129' role='' />\n    <member type='way' ref='39981128' role='' />\n    <member type='way' ref='53342519' role='' />\n    <member type='way' ref='43665655' role='' />\n    <member type='way' ref='53342521' role='' />\n    <member type='way' ref='486851059' role='' />\n    <member type='way' ref='27805057' role='' />\n    <member type='way' ref='572210306' role='' />\n    <member type='way' ref='292599140' role='' />\n    <member type='way' ref='78797017' role='' />\n    <member type='way' ref='27805055' role='' />\n    <member type='way' ref='477932757' role='' />\n    <member type='way' ref='572210277' role='' />\n    <member type='way' ref='477932759' role='' />\n    <member type='way' ref='477932760' role='' />\n    <member type='way' ref='477932758' role='' />\n    <member type='way' ref='496730528' role='' />\n    <member type='way' ref='607493308' role='' />\n    <member type='way' ref='591214268' role='' />\n    <member type='way' ref='142944471' role='' />\n    <member type='way' ref='450274068' role='' />\n    <member type='way' ref='31009399' role='' />\n    <member type='way' ref='187761801' role='' />\n    <member type='way' ref='572210301' role='' />\n    <member type='way' ref='142944469' role='' />\n    <member type='way' ref='33041278' role='' />\n    <member type='way' ref='143586365' role='' />\n    <member type='way' ref='27805056' role='' />\n    <member type='way' ref='39981139' role='' />\n    <member type='way' ref='528961136' role='' />\n    <member type='way' ref='528961133' role='' />\n    <member type='way' ref='23782998' role='' />\n    <member type='way' ref='640955497' role='' />\n    <member type='way' ref='659046263' role='' />\n    <member type='way' ref='647444124' role='' />\n    <member type='way' ref='659046260' role='' />\n    <member type='way' ref='659046262' role='' />\n    <member type='way' ref='282898412' role='' />\n    <member type='way' ref='282898412' role='' />\n    <member type='way' ref='659046261' role='' />\n    <member type='way' ref='680624628' role='' />\n    <member type='way' ref='282898404' role='' />\n    <member type='way' ref='282898404' role='' />\n    <member type='way' ref='680624629' role='' />\n    <member type='way' ref='659046255' role='' />\n    <member type='way' ref='659046259' role='' />\n    <member type='way' ref='685314679' role='' />\n    <member type='way' ref='28724405' role='' />\n    <member type='way' ref='685314678' role='' />\n    <member type='way' ref='659046254' role='' />\n    <member type='way' ref='659046257' role='' />\n    <member type='way' ref='659046258' role='' />\n    <member type='way' ref='659046253' role='' />\n    <member type='way' ref='659046256' role='' />\n    <member type='way' ref='86572979' role='' />\n    <member type='way' ref='39981133' role='' />\n    <member type='way' ref='39981134' role='' />\n    <member type='way' ref='43665654' role='' />\n    <member type='way' ref='39981138' role='' />\n    <member type='way' ref='27805057' role='' />\n    <member type='way' ref='572210306' role='' />\n    <member type='way' ref='292599140' role='' />\n    <member type='way' ref='78797017' role='' />\n    <member type='way' ref='27805055' role='' />\n    <member type='way' ref='477932757' role='' />\n    <member type='way' ref='572210277' role='' />\n    <member type='way' ref='572210285' role='' />\n    <member type='way' ref='477932756' role='' />\n    <member type='way' ref='187761804' role='' />\n    <member type='way' ref='142944333' role='' />\n    <member type='way' ref='186821032' role='' />\n    <member type='way' ref='186821035' role='' />\n    <member type='way' ref='186821033' role='' />\n    <member type='way' ref='142944337' role='' />\n    <member type='way' ref='486864288' role='' />\n    <member type='way' ref='551303422' role='' />\n    <member type='way' ref='551303420' role='' />\n    <member type='way' ref='43292244' role='' />\n    <member type='way' ref='560283873' role='' />\n    <member type='way' ref='75574936' role='' />\n    <member type='way' ref='186713412' role='' />\n    <member type='way' ref='551303411' role='' />\n    <member type='way' ref='40016270' role='' />\n    <member type='way' ref='23947828' role='' />\n    <member type='way' ref='186713419' role='' />\n    <member type='way' ref='199752604' role='' />\n    <member type='way' ref='199752605' role='' />\n    <member type='way' ref='142692918' role='' />\n    <member type='way' ref='142770547' role='' />\n    <member type='way' ref='199753406' role='' />\n    <member type='way' ref='199239120' role='' />\n    <member type='way' ref='199239121' role='' />\n    <member type='way' ref='381750335' role='' />\n    <member type='way' ref='284139902' role='' />\n    <member type='way' ref='24483197' role='' />\n    <member type='way' ref='199500676' role='' />\n    <member type='way' ref='199801748' role='' />\n    <member type='way' ref='139084186' role='' />\n    <member type='way' ref='538341573' role='' />\n    <member type='way' ref='156559304' role='' />\n    <member type='way' ref='242489322' role='' />\n    <member type='way' ref='538341572' role='' />\n    <member type='way' ref='26810757' role='' />\n    <member type='way' ref='92197791' role='' />\n    <member type='way' ref='521045511' role='' />\n    <member type='way' ref='24335863' role='' />\n    <member type='way' ref='566291585' role='' />\n    <member type='way' ref='495849381' role='' />\n    <member type='way' ref='26812782' role='' />\n    <member type='way' ref='391801515' role='' />\n    <member type='way' ref='391801512' role='' />\n    <member type='way' ref='391801518' role='' />\n    <member type='way' ref='391801511' role='' />\n    <member type='way' ref='391801517' role='' />\n    <member type='way' ref='129881956' role='' />\n    <member type='way' ref='391801519' role='' />\n    <member type='way' ref='129881957' role='' />\n    <member type='way' ref='391821203' role='' />\n    <member type='way' ref='137154549' role='' />\n    <member type='way' ref='391821210' role='' />\n    <member type='way' ref='391821217' role='' />\n    <member type='way' ref='238934536' role='' />\n    <member type='way' ref='391821211' role='' />\n    <member type='way' ref='391821229' role='' />\n    <member type='way' ref='558854986' role='' />\n    <member type='way' ref='391821215' role='' />\n    <member type='way' ref='391821208' role='' />\n    <member type='way' ref='391821221' role='' />\n    <member type='way' ref='26812876' role='' />\n    <member type='way' ref='46272777' role='' />\n    <member type='way' ref='3997588' role='' />\n    <member type='way' ref='114929183' role='' />\n    <member type='way' ref='20339700' role='' />\n    <member type='way' ref='391821267' role='' />\n    <member type='way' ref='391821249' role='' />\n    <member type='way' ref='391821257' role='' />\n    <member type='way' ref='20339701' role='' />\n    <member type='way' ref='50390277' role='' />\n    <member type='way' ref='418676719' role='' />\n    <member type='way' ref='142762612' role='' />\n    <member type='way' ref='682409937' role='' />\n    <member type='way' ref='218907473' role='' />\n    <member type='way' ref='682409944' role='' />\n    <member type='way' ref='682409936' role='' />\n    <member type='way' ref='30144798' role='' />\n    <member type='way' ref='30071667' role='' />\n    <member type='way' ref='3997579' role='' />\n    <member type='way' ref='51255675' role='' />\n    <member type='way' ref='142709192' role='' />\n    <member type='way' ref='143961986' role='' />\n    <member type='way' ref='143961993' role='' />\n    <member type='way' ref='143962009' role='' />\n    <member type='way' ref='308317374' role='' />\n    <member type='way' ref='144246652' role='' />\n    <member type='way' ref='30071719' role='' />\n    <member type='way' ref='597809427' role='' />\n    <member type='way' ref='129787618' role='' />\n    <member type='way' ref='650779313' role='' />\n    <member type='way' ref='650779280' role='' />\n    <member type='way' ref='650779281' role='' />\n    <member type='way' ref='650779301' role='' />\n    <member type='way' ref='650779300' role='' />\n    <member type='way' ref='650779306' role='' />\n    <member type='way' ref='650779308' role='' />\n    <member type='way' ref='650779307' role='' />\n    <member type='way' ref='650779314' role='' />\n    <member type='way' ref='650779324' role='' />\n    <member type='way' ref='650779318' role='' />\n    <member type='way' ref='586236392' role='' />\n    <member type='way' ref='586235758' role='' />\n    <member type='way' ref='605866512' role='' />\n    <member type='way' ref='586235753' role='' />\n    <member type='way' ref='220515419' role='' />\n    <member type='way' ref='220515422' role='' />\n    <member type='way' ref='605866511' role='' />\n    <member type='way' ref='605866510' role='' />\n    <member type='way' ref='605866509' role='' />\n    <member type='way' ref='30434903' role='' />\n    <member type='way' ref='30339647' role='' />\n    <member type='way' ref='30339611' role='' />\n    <member type='way' ref='30505764' role='' />\n    <member type='way' ref='30339616' role='' />\n    <member type='way' ref='136485969' role='' />\n    <member type='way' ref='30339585' role='' />\n    <member type='way' ref='30339520' role='' />\n    <member type='way' ref='605872191' role='' />\n    <member type='way' ref='605872193' role='' />\n    <member type='way' ref='153842980' role='' />\n    <member type='way' ref='605872189' role='' />\n    <member type='way' ref='605872188' role='' />\n    <member type='way' ref='146851607' role='' />\n    <member type='way' ref='674053437' role='' />\n    <member type='way' ref='674053435' role='' />\n    <member type='way' ref='674053434' role='' />\n    <member type='way' ref='674053438' role='' />\n    <member type='way' ref='674053444' role='' />\n    <member type='way' ref='674053445' role='' />\n    <member type='way' ref='674053446' role='' />\n    <member type='way' ref='674053447' role='' />\n    <member type='way' ref='23299758' role='' />\n    <member type='way' ref='4275603' role='' />\n    <member type='way' ref='90270856' role='' />\n    <member type='way' ref='599860462' role='' />\n    <member type='way' ref='674053453' role='' />\n    <member type='way' ref='23299769' role='' />\n    <member type='way' ref='23299741' role='' />\n    <member type='way' ref='23299739' role='' />\n    <member type='way' ref='10713260' role='' />\n    <member type='way' ref='491131657' role='' />\n    <member type='way' ref='329485053' role='' />\n    <member type='way' ref='276189413' role='' />\n    <member type='way' ref='276189409' role='' />\n    <member type='way' ref='757714790' role='' />\n    <member type='way' ref='296326682' role='' />\n    <member type='way' ref='323606068' role='' />\n    <member type='way' ref='850384023' role='' />\n    <member type='way' ref='296326686' role='' />\n    <member type='way' ref='702208693' role='' />\n    <member type='way' ref='4766001' role='' />\n    <member type='way' ref='4766002' role='' />\n    <member type='way' ref='46742301' role='' />\n    <member type='way' ref='3989646' role='' />\n    <member type='way' ref='58136889' role='' />\n    <member type='way' ref='701675838' role='' />\n    <member type='way' ref='296390955' role='' />\n    <member type='way' ref='683695052' role='' />\n    <member type='way' ref='683695051' role='' />\n    <member type='way' ref='412141655' role='' />\n    <member type='way' ref='540939281' role='' />\n    <member type='way' ref='540939276' role='' />\n    <member type='way' ref='540939272' role='' />\n    <member type='way' ref='540939268' role='' />\n    <member type='way' ref='296390963' role='' />\n    <member type='way' ref='34379683' role='' />\n    <member type='way' ref='324251358' role='' />\n    <member type='way' ref='324251352' role='' />\n    <member type='way' ref='324251355' role='' />\n    <member type='way' ref='4019408' role='' />\n    <member type='way' ref='690918956' role='' />\n    <member type='way' ref='10711342' role='' />\n    <member type='way' ref='10711343' role='' />\n    <member type='way' ref='10711348' role='' />\n    <member type='way' ref='500776559' role='' />\n    <member type='way' ref='233083900' role='' />\n    <member type='way' ref='233083909' role='' />\n    <member type='way' ref='233083898' role='' />\n    <member type='way' ref='233083905' role='' />\n    <member type='way' ref='690686293' role='' />\n    <member type='way' ref='690686294' role='' />\n    <member type='way' ref='296494591' role='' />\n    <member type='way' ref='473334686' role='' />\n    <member type='way' ref='473334682' role='' />\n    <member type='way' ref='515191108' role='' />\n    <member type='way' ref='233082560' role='' />\n    <member type='way' ref='296497515' role='' />\n    <member type='way' ref='233082561' role='' />\n    <member type='way' ref='233082556' role='' />\n    <member type='way' ref='35723971' role='' />\n    <member type='way' ref='296501850' role='' />\n    <member type='way' ref='34881625' role='' />\n    <member type='way' ref='4987972' role='' />\n    <member type='way' ref='690558814' role='' />\n    <member type='way' ref='690558810' role='' />\n    <member type='way' ref='233081269' role='' />\n    <member type='way' ref='233081273' role='' />\n    <member type='way' ref='233081270' role='' />\n    <member type='way' ref='233081272' role='' />\n    <member type='way' ref='51435393' role='' />\n    <member type='way' ref='412141674' role='' />\n    <member type='way' ref='462387276' role='' />\n    <member type='way' ref='412141648' role='' />\n    <member type='way' ref='462387614' role='' />\n    <member type='way' ref='51435389' role='' />\n    <member type='way' ref='83174288' role='' />\n    <member type='way' ref='515191106' role='' />\n    <member type='way' ref='233079508' role='' />\n    <member type='way' ref='554321817' role='' />\n    <member type='way' ref='233079507' role='' />\n    <member type='way' ref='515191104' role='' />\n    <member type='way' ref='390575956' role='' />\n    <member type='way' ref='544212750' role='' />\n    <member type='way' ref='603519201' role='' />\n    <member type='way' ref='690566791' role='' />\n    <member type='way' ref='690566792' role='' />\n    <member type='way' ref='744330327' role='' />\n    <member type='way' ref='744330326' role='' />\n    <member type='way' ref='412141668' role='' />\n    <member type='way' ref='660900887' role='' />\n    <member type='way' ref='659785284' role='' />\n    <member type='way' ref='4993370' role='' />\n    <member type='way' ref='233078903' role='' />\n    <member type='way' ref='233078905' role='' />\n    <member type='way' ref='4993373' role='' />\n    <member type='way' ref='60660645' role='' />\n    <member type='way' ref='60660649' role='' />\n    <member type='way' ref='4987967' role='' />\n    <member type='way' ref='233078901' role='' />\n    <member type='way' ref='233078904' role='' />\n    <member type='way' ref='30527962' role='' />\n    <member type='way' ref='30527963' role='' />\n    <member type='way' ref='30527957' role='' />\n    <member type='way' ref='30527958' role='' />\n    <member type='way' ref='659785282' role='' />\n    <member type='way' ref='233078180' role='' />\n    <member type='way' ref='660900883' role='' />\n    <member type='way' ref='46742304' role='' />\n    <member type='way' ref='233078183' role='' />\n    <member type='way' ref='316452179' role='' />\n    <member type='way' ref='233078184' role='' />\n    <member type='way' ref='32255545' role='' />\n    <member type='way' ref='633718226' role='' />\n    <member type='way' ref='59572331' role='' />\n    <member type='way' ref='59572360' role='' />\n    <member type='way' ref='32337801' role='' />\n    <member type='way' ref='32337802' role='' />\n    <member type='way' ref='531541378' role='' />\n    <member type='way' ref='233077308' role='' />\n    <member type='way' ref='503268751' role='' />\n    <member type='way' ref='233077313' role='' />\n    <member type='way' ref='46742297' role='' />\n    <member type='way' ref='690686303' role='' />\n    <member type='way' ref='233077307' role='' />\n    <member type='way' ref='683695039' role='' />\n    <member type='way' ref='606446528' role='' />\n    <member type='way' ref='58136781' role='' />\n    <member type='way' ref='296530949' role='' />\n    <member type='way' ref='5078910' role='' />\n    <member type='way' ref='69958287' role='' />\n    <member type='way' ref='240745994' role='' />\n    <member type='way' ref='240745992' role='' />\n    <member type='way' ref='32454972' role='' />\n    <member type='way' ref='32454971' role='' />\n    <member type='way' ref='240745993' role='' />\n    <member type='way' ref='240745996' role='' />\n    <member type='way' ref='26821965' role='' />\n    <member type='way' ref='26821966' role='' />\n    <member type='way' ref='31978384' role='' />\n    <member type='way' ref='31978385' role='' />\n    <member type='way' ref='485851177' role='' />\n    <member type='way' ref='485851174' role='' />\n    <member type='way' ref='485851173' role='' />\n    <member type='way' ref='142699276' role='' />\n    <member type='way' ref='142699273' role='' />\n    <member type='way' ref='142699275' role='' />\n    <member type='way' ref='22819101' role='' />\n    <member type='way' ref='22819102' role='' />\n    <member type='way' ref='26832674' role='' />\n    <member type='way' ref='26832675' role='' />\n    <member type='way' ref='142699274' role='' />\n    <member type='way' ref='22418800' role='' />\n    <member type='way' ref='22418820' role='' />\n    <member type='way' ref='157364391' role='' />\n    <member type='way' ref='157364394' role='' />\n    <member type='way' ref='344159905' role='' />\n    <member type='way' ref='76839447' role='' />\n    <member type='way' ref='749559788' role='' />\n    <member type='way' ref='34614767' role='' />\n    <member type='way' ref='733027972' role='' />\n    <member type='way' ref='678151500' role='' />\n    <member type='way' ref='76839458' role='' />\n    <member type='way' ref='733027970' role='' />\n    <member type='way' ref='4717259' role='' />\n    <member type='way' ref='296557880' role='' />\n    <member type='way' ref='153203960' role='' />\n    <member type='way' ref='76839452' role='' />\n    <member type='way' ref='274608780' role='' />\n    <member type='way' ref='274608779' role='' />\n    <member type='way' ref='274606794' role='' />\n    <member type='way' ref='144053947' role='' />\n    <member type='way' ref='274606795' role='' />\n    <member type='way' ref='274606796' role='' />\n    <member type='way' ref='274606797' role='' />\n    <member type='way' ref='37352317' role='' />\n    <member type='way' ref='7998655' role='' />\n    <member type='way' ref='29650025' role='' />\n    <member type='way' ref='296557881' role='' />\n    <member type='way' ref='779492336' role='' />\n    <member type='way' ref='562643469' role='' />\n    <member type='way' ref='433798771' role='' />\n    <member type='way' ref='379519259' role='' />\n    <member type='way' ref='236855736' role='' />\n    <member type='way' ref='236855733' role='' />\n    <member type='way' ref='236855737' role='' />\n    <member type='way' ref='121243050' role='' />\n    <member type='way' ref='39337118' role='' />\n    <member type='way' ref='733372950' role='' />\n    <member type='way' ref='733372949' role='' />\n    <member type='way' ref='30809509' role='' />\n    <member type='way' ref='275422324' role='' />\n    <member type='way' ref='275422323' role='' />\n    <member type='way' ref='275422325' role='' />\n    <member type='way' ref='121243055' role='' />\n    <member type='way' ref='8666488' role='' />\n    <member type='way' ref='498866393' role='' />\n    <member type='way' ref='28060354' role='' />\n    <member type='way' ref='29651991' role='' />\n    <member type='way' ref='28150271' role='' />\n    <member type='way' ref='314450784' role='' />\n    <member type='way' ref='776093119' role='' />\n    <member type='way' ref='776093120' role='' />\n    <member type='way' ref='138982590' role='' />\n    <member type='way' ref='67153451' role='' />\n    <member type='way' ref='46739386' role='' />\n    <member type='way' ref='40033980' role='' />\n    <member type='way' ref='8666490' role='' />\n    <member type='way' ref='314450772' role='' />\n    <member type='way' ref='314450782' role='' />\n    <member type='way' ref='314450780' role='' />\n    <member type='way' ref='682144620' role='' />\n    <member type='way' ref='280958658' role='' />\n    <member type='way' ref='682144621' role='' />\n    <member type='way' ref='682144620' role='' />\n    <member type='way' ref='682144623' role='' />\n    <member type='way' ref='314450779' role='' />\n    <member type='way' ref='314450781' role='' />\n    <member type='way' ref='314450775' role='' />\n    <member type='way' ref='138982589' role='' />\n    <member type='way' ref='183984419' role='' />\n    <member type='way' ref='314450776' role='' />\n    <member type='way' ref='47252827' role='' />\n    <member type='way' ref='40033158' role='' />\n    <member type='way' ref='67153460' role='' />\n    <member type='way' ref='314450777' role='' />\n    <member type='way' ref='314450774' role='' />\n    <member type='way' ref='314450778' role='' />\n    <member type='way' ref='28150896' role='' />\n    <member type='way' ref='543196765' role='' />\n    <member type='way' ref='520405269' role='' />\n    <member type='way' ref='379812994' role='' />\n    <member type='way' ref='28150250' role='' />\n    <member type='way' ref='379852448' role='' />\n    <member type='way' ref='37831194' role='' />\n    <member type='way' ref='8666488' role='' />\n    <member type='way' ref='121243055' role='' />\n    <member type='way' ref='275422325' role='' />\n    <member type='way' ref='275422323' role='' />\n    <member type='way' ref='275422324' role='' />\n    <member type='way' ref='30809517' role='' />\n    <member type='way' ref='13641245' role='' />\n    <member type='way' ref='275422317' role='' />\n    <member type='way' ref='578627081' role='' />\n    <member type='way' ref='275422314' role='' />\n    <member type='way' ref='36933287' role='' />\n    <member type='way' ref='28222985' role='' />\n    <member type='way' ref='433798767' role='' />\n    <member type='way' ref='36780903' role='' />\n    <member type='way' ref='379519260' role='' />\n    <member type='way' ref='531462061' role='' />\n    <member type='way' ref='68244365' role='' />\n    <member type='way' ref='46739384' role='' />\n    <member type='way' ref='531462062' role='' />\n    <member type='way' ref='236855738' role='' />\n    <member type='way' ref='779254719' role='' />\n    <member type='way' ref='779254717' role='' />\n    <member type='way' ref='779254718' role='' />\n    <member type='way' ref='779254713' role='' />\n    <member type='way' ref='779254721' role='' />\n    <member type='way' ref='779254722' role='' />\n    <member type='way' ref='601394137' role='' />\n    <member type='way' ref='144053949' role='' />\n    <member type='way' ref='274513688' role='' />\n    <member type='way' ref='274513690' role='' />\n    <member type='way' ref='274513686' role='' />\n    <member type='way' ref='274513681' role='' />\n    <member type='way' ref='48178462' role='' />\n    <member type='way' ref='5012656' role='' />\n    <member type='way' ref='37352363' role='' />\n    <member type='way' ref='47796988' role='' />\n    <member type='way' ref='144053946' role='' />\n    <member type='way' ref='153203961' role='' />\n    <member type='way' ref='274608055' role='' />\n    <member type='way' ref='19714183' role='' />\n    <member type='way' ref='69288832' role='' />\n    <member type='way' ref='235018086' role='' />\n    <member type='way' ref='5078116' role='' />\n    <member type='way' ref='69969588' role='' />\n    <member type='way' ref='240140719' role='' />\n    <member type='way' ref='168104648' role='' />\n    <member type='way' ref='324251311' role='' />\n    <member type='way' ref='324251310' role='' />\n    <member type='way' ref='324251309' role='' />\n    <member type='way' ref='324251312' role='' />\n    <member type='way' ref='324319575' role='' />\n    <member type='way' ref='324319590' role='' />\n    <member type='way' ref='324319584' role='' />\n    <member type='way' ref='324319577' role='' />\n    <member type='way' ref='33152023' role='' />\n    <member type='way' ref='33152022' role='' />\n    <member type='way' ref='19629511' role='' />\n    <member type='way' ref='26149325' role='' />\n    <member type='way' ref='26149318' role='' />\n    <member type='way' ref='5079260' role='' />\n    <member type='way' ref='233112550' role='' />\n    <member type='way' ref='233112535' role='' />\n    <member type='way' ref='233112547' role='' />\n    <member type='way' ref='233112560' role='' />\n    <member type='way' ref='44021694' role='' />\n    <member type='way' ref='44021686' role='' />\n    <member type='way' ref='31488556' role='' />\n    <member type='way' ref='31488555' role='' />\n    <member type='way' ref='233103736' role='' />\n    <member type='way' ref='233103731' role='' />\n    <member type='way' ref='233103735' role='' />\n    <member type='way' ref='233103732' role='' />\n    <member type='way' ref='233023654' role='' />\n    <member type='way' ref='11846377' role='' />\n    <member type='way' ref='233023673' role='' />\n    <member type='way' ref='47434450' role='' />\n    <member type='way' ref='233023662' role='' />\n    <member type='way' ref='233023660' role='' />\n    <member type='way' ref='233023666' role='' />\n    <member type='way' ref='233023667' role='' />\n    <member type='way' ref='22739410' role='' />\n    <member type='way' ref='697956272' role='' />\n    <member type='way' ref='697956273' role='' />\n    <member type='way' ref='28637351' role='' />\n    <member type='way' ref='479922412' role='' />\n    <member type='way' ref='479922416' role='' />\n    <member type='way' ref='28637352' role='' />\n    <member type='way' ref='479922406' role='' />\n    <member type='way' ref='479922390' role='' />\n    <member type='way' ref='27259406' role='' />\n    <member type='way' ref='33290797' role='' />\n    <member type='way' ref='33290786' role='' />\n    <member type='way' ref='33302609' role='' />\n    <member type='way' ref='232994357' role='' />\n    <member type='way' ref='31958020' role='' />\n    <member type='way' ref='31958019' role='' />\n    <member type='way' ref='124981041' role='' />\n    <member type='way' ref='232994360' role='' />\n    <member type='way' ref='232994354' role='' />\n    <member type='way' ref='4019591' role='' />\n    <member type='way' ref='144142218' role='' />\n    <member type='way' ref='546560756' role='' />\n    <member type='way' ref='546560757' role='' />\n    <member type='way' ref='546560760' role='' />\n    <member type='way' ref='546560761' role='' />\n    <member type='way' ref='144142222' role='' />\n    <member type='way' ref='23022858' role='' />\n    <member type='way' ref='72135458' role='' />\n    <member type='way' ref='72135452' role='' />\n    <member type='way' ref='535382793' role='' />\n    <member type='way' ref='535382792' role='' />\n    <member type='way' ref='232982161' role='' />\n    <member type='way' ref='232982160' role='' />\n    <member type='way' ref='232982166' role='' />\n    <member type='way' ref='232982159' role='' />\n    <member type='way' ref='535382426' role='' />\n    <member type='way' ref='535382425' role='' />\n    <member type='way' ref='479915963' role='' />\n    <member type='way' ref='479915958' role='' />\n    <member type='way' ref='226071294' role='' />\n    <member type='way' ref='479915955' role='' />\n    <member type='way' ref='226071296' role='' />\n    <member type='way' ref='479915953' role='' />\n    <member type='way' ref='56014878' role='' />\n    <member type='way' ref='56014877' role='' />\n    <member type='way' ref='231419620' role='' />\n    <member type='way' ref='231419619' role='' />\n    <member type='way' ref='232975916' role='' />\n    <member type='way' ref='232975938' role='' />\n    <member type='way' ref='232975924' role='' />\n    <member type='way' ref='232975918' role='' />\n    <member type='way' ref='4019635' role='' />\n    <member type='way' ref='232971055' role='' />\n    <member type='way' ref='232971059' role='' />\n    <member type='way' ref='232971052' role='' />\n    <member type='way' ref='546557522' role='' />\n    <member type='way' ref='546557521' role='' />\n    <member type='way' ref='546557520' role='' />\n    <member type='way' ref='546557519' role='' />\n    <member type='way' ref='232971051' role='' />\n    <member type='way' ref='98223245' role='' />\n    <member type='way' ref='98223306' role='' />\n    <member type='way' ref='306874707' role='' />\n    <member type='way' ref='324251351' role='' />\n    <member type='way' ref='324251357' role='' />\n    <member type='way' ref='58136827' role='' />\n    <member type='way' ref='324251353' role='' />\n    <member type='way' ref='324251350' role='' />\n    <member type='way' ref='232967033' role='' />\n    <member type='way' ref='26141370' role='' />\n    <member type='way' ref='26141371' role='' />\n    <member type='way' ref='232967036' role='' />\n    <member type='way' ref='232967037' role='' />\n    <member type='way' ref='38273288' role='' />\n    <member type='way' ref='38273285' role='' />\n    <member type='way' ref='222565248' role='' />\n    <member type='way' ref='222565249' role='' />\n    <member type='way' ref='232965539' role='' />\n    <member type='way' ref='27229916' role='' />\n    <member type='way' ref='232965535' role='' />\n    <member type='way' ref='232965554' role='' />\n    <member type='way' ref='46753573' role='' />\n    <member type='way' ref='229424414' role='' />\n    <member type='way' ref='139486241' role='' />\n    <member type='way' ref='23098266' role='' />\n    <member type='way' ref='25141877' role='' />\n    <member type='way' ref='24408231' role='' />\n    <member type='way' ref='248690769' role='' />\n    <member type='way' ref='26304631' role='' />\n    <member type='way' ref='377101875' role='' />\n    <member type='way' ref='377101890' role='' />\n    <member type='way' ref='377645413' role='' />\n    <member type='way' ref='61413097' role='' />\n    <member type='way' ref='377645414' role='' />\n    <member type='way' ref='377101892' role='' />\n    <member type='way' ref='377101889' role='' />\n    <member type='way' ref='377101891' role='' />\n    <member type='way' ref='377101894' role='' />\n    <member type='way' ref='169327831' role='' />\n    <member type='way' ref='377101888' role='' />\n    <member type='way' ref='377101893' role='' />\n    <member type='way' ref='214796416' role='' />\n    <member type='way' ref='5037792' role='' />\n    <member type='way' ref='23967768' role='' />\n    <member type='way' ref='23416611' role='' />\n    <member type='way' ref='87700223' role='' />\n    <member type='way' ref='23416591' role='' />\n    <member type='way' ref='424099552' role='' />\n    <member type='way' ref='514392684' role='' />\n    <member type='way' ref='32551883' role='' />\n    <member type='way' ref='198685165' role='' />\n    <member type='way' ref='198685161' role='' />\n    <member type='way' ref='32552096' role='' />\n    <member type='way' ref='32705199' role='' />\n    <member type='way' ref='728153623' role='' />\n    <member type='way' ref='26413433' role='' />\n    <member type='way' ref='129025644' role='' />\n    <member type='way' ref='32515008' role='' />\n    <member type='way' ref='23080117' role='' />\n    <member type='way' ref='198685161' role='' />\n    <member type='way' ref='198685165' role='' />\n    <member type='way' ref='32551883' role='' />\n    <member type='way' ref='514392684' role='' />\n    <member type='way' ref='32551882' role='' />\n    <member type='way' ref='120076961' role='' />\n    <member type='way' ref='228838849' role='' />\n    <member type='way' ref='5666001' role='' />\n    <member type='way' ref='424099565' role='' />\n    <member type='way' ref='5037792' role='' />\n    <member type='way' ref='214796416' role='' />\n    <member type='way' ref='377101893' role='' />\n    <member type='way' ref='377101888' role='' />\n    <member type='way' ref='169327831' role='' />\n    <member type='way' ref='377101894' role='' />\n    <member type='way' ref='377101891' role='' />\n    <member type='way' ref='377101889' role='' />\n    <member type='way' ref='377101892' role='' />\n    <member type='way' ref='377645414' role='' />\n    <member type='way' ref='61413097' role='' />\n    <member type='way' ref='377645413' role='' />\n    <member type='way' ref='377101890' role='' />\n    <member type='way' ref='60981193' role='' />\n    <member type='way' ref='169280772' role='' />\n    <member type='way' ref='167241864' role='' />\n    <member type='way' ref='23059551' role='' />\n    <member type='way' ref='315213104' role='' />\n    <member type='way' ref='536501276' role='' />\n    <member type='way' ref='536501275' role='' />\n    <member type='way' ref='248690767' role='' />\n    <member type='way' ref='670253628' role='' />\n    <member type='way' ref='5037794' role='' />\n    <member type='way' ref='377766355' role='' />\n    <member type='way' ref='47160380' role='' />\n    <member type='way' ref='25829523' role='' />\n    <member type='way' ref='25829522' role='' />\n    <member type='way' ref='22133738' role='' />\n    <member type='way' ref='22133748' role='' />\n    <member type='way' ref='313106226' role='' />\n    <member type='way' ref='55833355' role='' />\n    <member type='way' ref='55833354' role='' />\n    <member type='way' ref='26581529' role='' />\n    <member type='way' ref='26581509' role='' />\n    <member type='way' ref='26581502' role='' />\n    <member type='way' ref='576352092' role='' />\n    <member type='way' ref='22820720' role='' />\n    <member type='way' ref='22820719' role='' />\n    <member type='way' ref='640159116' role='' />\n    <member type='way' ref='640159119' role='' />\n    <member type='way' ref='640159121' role='' />\n    <member type='way' ref='842828076' role='' />\n    <member type='way' ref='473318568' role='' />\n    <member type='way' ref='473318569' role='' />\n    <member type='way' ref='660618405' role='' />\n    <member type='way' ref='473318563' role='' />\n    <member type='way' ref='473318562' role='' />\n    <member type='way' ref='660622907' role='' />\n    <member type='way' ref='473318561' role='' />\n    <member type='way' ref='4586895' role='' />\n    <member type='way' ref='169502263' role='' />\n    <member type='way' ref='23113975' role='' />\n    <member type='way' ref='23113974' role='' />\n    <member type='way' ref='4586898' role='' />\n    <member type='way' ref='660694272' role='' />\n    <member type='way' ref='660694271' role='' />\n    <member type='way' ref='4586899' role='' />\n    <member type='way' ref='4586902' role='' />\n    <member type='way' ref='659869781' role='' />\n    <member type='way' ref='161385863' role='' />\n    <member type='way' ref='833202562' role='' />\n    <member type='way' ref='161385868' role='' />\n    <member type='way' ref='659869780' role='' />\n    <member type='way' ref='4586903' role='' />\n    <member type='way' ref='665676989' role='' />\n    <member type='way' ref='665676988' role='' />\n    <member type='way' ref='4586906' role='' />\n    <member type='way' ref='204165651' role='' />\n    <member type='way' ref='204165655' role='' />\n    <member type='way' ref='4586908' role='' />\n    <member type='way' ref='833202559' role='' />\n    <member type='way' ref='833202556' role='' />\n    <member type='way' ref='4586911' role='' />\n    <member type='way' ref='4586914' role='' />\n    <member type='way' ref='4586915' role='' />\n    <member type='way' ref='165254732' role='' />\n    <member type='way' ref='833202555' role='' />\n    <member type='way' ref='833202552' role='' />\n    <member type='way' ref='4586918' role='' />\n    <member type='way' ref='29216348' role='' />\n    <member type='way' ref='29216350' role='' />\n    <member type='way' ref='4586919' role='' />\n    <member type='way' ref='4586922' role='' />\n    <member type='way' ref='30447473' role='' />\n    <member type='way' ref='30447474' role='' />\n    <member type='way' ref='660630408' role='' />\n    <member type='way' ref='28212155' role='' />\n    <member type='way' ref='122304236' role='' />\n    <member type='way' ref='675382051' role='' />\n    <member type='way' ref='62289647' role='' />\n    <member type='way' ref='58075168' role='' />\n    <member type='way' ref='219837014' role='' />\n    <member type='way' ref='509102670' role='' />\n    <member type='way' ref='366669793' role='' />\n    <member type='way' ref='366669796' role='' />\n    <member type='way' ref='578209354' role='' />\n    <member type='way' ref='135052326' role='' />\n    <member type='way' ref='24629915' role='' />\n    <member type='way' ref='24629870' role='' />\n    <member type='way' ref='25533532' role='' />\n    <member type='way' ref='53772038' role='' />\n    <member type='way' ref='40814025' role='' />\n    <member type='way' ref='40814026' role='' />\n    <member type='way' ref='664891129' role='' />\n    <member type='way' ref='30050896' role='' />\n    <member type='way' ref='213210051' role='' />\n    <member type='way' ref='40814532' role='' />\n    <member type='way' ref='40814531' role='' />\n    <member type='way' ref='23486733' role='' />\n    <member type='way' ref='449199494' role='' />\n    <member type='way' ref='449199493' role='' />\n    <member type='way' ref='23486732' role='' />\n    <member type='way' ref='52565598' role='' />\n    <member type='way' ref='29358757' role='' />\n    <member type='way' ref='664891127' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175447' role='' />\n    <member type='way' ref='369175445' role='' />\n    <member type='way' ref='369175441' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='810335209' role='' />\n    <member type='way' ref='513492326' role='' />\n    <member type='way' ref='23486731' role='' />\n    <member type='way' ref='761557032' role='' />\n    <member type='way' ref='35438709' role='' />\n    <member type='way' ref='59002627' role='' />\n    <member type='way' ref='383314127' role='' />\n    <member type='way' ref='661407753' role='' />\n    <member type='way' ref='35540883' role='' />\n    <member type='way' ref='52564295' role='' />\n    <member type='way' ref='40723871' role='' />\n    <member type='way' ref='56054538' role='' />\n    <member type='way' ref='56054536' role='' />\n    <member type='way' ref='670507676' role='' />\n    <member type='way' ref='24163693' role='' />\n    <member type='way' ref='59002626' role='' />\n    <member type='way' ref='159161919' role='' />\n    <member type='way' ref='274704232' role='' />\n    <member type='way' ref='274704230' role='' />\n    <member type='way' ref='4586892' role='' />\n    <member type='way' ref='125556138' role='' />\n    <member type='way' ref='692474886' role='' />\n    <member type='way' ref='4586021' role='' />\n    <member type='way' ref='492836256' role='' />\n    <member type='way' ref='51754193' role='' />\n    <member type='way' ref='26160620' role='' />\n    <member type='way' ref='492836257' role='' />\n    <member type='way' ref='320454527' role='' />\n    <member type='way' ref='88420879' role='' />\n    <member type='way' ref='320454475' role='' />\n    <member type='way' ref='120736599' role='' />\n    <member type='way' ref='320454480' role='' />\n    <member type='way' ref='88439118' role='' />\n    <member type='way' ref='320454519' role='' />\n    <member type='way' ref='320454485' role='' />\n    <member type='way' ref='320454474' role='' />\n    <member type='way' ref='320454492' role='' />\n    <member type='way' ref='367544799' role='' />\n    <member type='way' ref='88956579' role='' />\n    <member type='way' ref='88956568' role='' />\n    <member type='way' ref='320454518' role='' />\n    <member type='way' ref='320454511' role='' />\n    <member type='way' ref='26160622' role='' />\n    <member type='way' ref='320454522' role='' />\n    <member type='way' ref='358582375' role='' />\n    <member type='way' ref='320454473' role='' />\n    <member type='way' ref='320454496' role='' />\n    <member type='way' ref='320454510' role='' />\n    <member type='way' ref='320454497' role='' />\n    <member type='way' ref='320454513' role='' />\n    <member type='way' ref='320454521' role='' />\n    <member type='way' ref='320454512' role='' />\n    <member type='way' ref='320454488' role='' />\n    <member type='way' ref='649953006' role='' />\n    <member type='way' ref='320454509' role='' />\n    <member type='way' ref='320454481' role='' />\n    <member type='way' ref='320454490' role='' />\n    <member type='way' ref='234855460' role='' />\n    <member type='way' ref='92286836' role='' />\n    <member type='way' ref='389088214' role='' />\n    <member type='way' ref='92286826' role='' />\n    <member type='way' ref='632773391' role='' />\n    <member type='way' ref='632773395' role='' />\n    <member type='way' ref='22970260' role='' />\n    <member type='way' ref='320454494' role='' />\n    <member type='way' ref='320454515' role='' />\n    <member type='way' ref='92286886' role='' />\n    <member type='way' ref='320454514' role='' />\n    <member type='way' ref='13231751' role='' />\n    <member type='way' ref='632789451' role='' />\n    <member type='way' ref='297072036' role='' />\n    <member type='way' ref='632789452' role='' />\n    <member type='way' ref='632799782' role='' />\n    <member type='way' ref='23236478' role='' />\n    <member type='way' ref='65009892' role='' />\n    <member type='way' ref='65009884' role='' />\n    <member type='way' ref='88288530' role='' />\n    <member type='way' ref='88288521' role='' />\n    <member type='way' ref='358530815' role='' />\n    <member type='way' ref='297157693' role='' />\n    <member type='way' ref='632789458' role='' />\n    <member type='way' ref='632789477' role='' />\n    <member type='way' ref='92286866' role='' />\n    <member type='way' ref='92286833' role='' />\n    <member type='way' ref='632789481' role='' />\n    <member type='way' ref='23150406' role='' />\n    <member type='way' ref='23150408' role='' />\n    <member type='way' ref='23150407' role='' />\n    <member type='way' ref='632789483' role='' />\n    <member type='way' ref='81113481' role='' />\n    <member type='way' ref='237480738' role='' />\n    <member type='way' ref='632790909' role='' />\n    <member type='way' ref='633223273' role='' />\n    <member type='way' ref='92286843' role='' />\n    <member type='way' ref='319988205' role='' />\n    <member type='way' ref='319988235' role='' />\n    <member type='way' ref='118842812' role='' />\n    <member type='way' ref='118842811' role='' />\n    <member type='way' ref='319988220' role='' />\n    <member type='way' ref='118842799' role='' />\n    <member type='way' ref='683582711' role='' />\n    <member type='way' ref='358587896' role='' />\n    <member type='way' ref='118842796' role='' />\n    <member type='way' ref='319988173' role='' />\n    <member type='way' ref='319988181' role='' />\n    <member type='way' ref='118842817' role='' />\n    <member type='way' ref='319988198' role='' />\n    <member type='way' ref='319988221' role='' />\n    <member type='way' ref='683582709' role='' />\n    <member type='way' ref='319988212' role='' />\n    <member type='way' ref='118842807' role='' />\n    <member type='way' ref='319988227' role='' />\n    <member type='way' ref='358587877' role='' />\n    <member type='way' ref='118842791' role='' />\n    <member type='way' ref='319988207' role='' />\n    <member type='way' ref='319988186' role='' />\n    <member type='way' ref='319988195' role='' />\n    <member type='way' ref='319988179' role='' />\n    <member type='way' ref='28101295' role='' />\n    <member type='way' ref='28101296' role='' />\n    <member type='way' ref='319988184' role='' />\n    <member type='way' ref='24058526' role='' />\n    <member type='way' ref='492828702' role='' />\n    <member type='way' ref='241787608' role='' />\n    <member type='way' ref='33787292' role='' />\n    <member type='way' ref='492828703' role='' />\n    <member type='way' ref='125225779' role='' />\n    <member type='way' ref='234346248' role='' />\n    <member type='way' ref='125225780' role='' />\n    <member type='way' ref='241787617' role='' />\n    <member type='way' ref='241787614' role='' />\n    <member type='way' ref='241787609' role='' />\n    <member type='way' ref='241787613' role='' />\n    <member type='way' ref='119161423' role='' />\n    <member type='way' ref='119161417' role='' />\n    <member type='way' ref='38028832' role='' />\n    <member type='way' ref='38028833' role='' />\n    <member type='way' ref='627085346' role='' />\n    <member type='way' ref='144915856' role='' />\n    <member type='way' ref='144915857' role='' />\n    <member type='way' ref='492828704' role='' />\n    <member type='way' ref='492828706' role='' />\n    <member type='way' ref='38028161' role='' />\n    <member type='way' ref='492828708' role='' />\n    <member type='way' ref='38510311' role='' />\n    <member type='way' ref='144915852' role='' />\n    <member type='way' ref='843150468' role='' />\n    <member type='way' ref='30406545' role='' />\n    <member type='way' ref='30406549' role='' />\n    <member type='way' ref='843150467' role='' />\n    <member type='way' ref='130294856' role='' />\n    <member type='way' ref='197290015' role='' />\n    <member type='way' ref='184792914' role='' />\n    <member type='way' ref='689482243' role='' />\n    <member type='way' ref='321378455' role='' />\n    <member type='way' ref='234119941' role='' />\n    <member type='way' ref='10215146' role='' />\n    <member type='way' ref='520517117' role='' />\n    <member type='way' ref='498867438' role='' />\n    <member type='way' ref='835340624' role='' />\n    <member type='way' ref='498859729' role='' />\n    <member type='way' ref='318944792' role='' />\n    <member type='way' ref='498859743' role='' />\n    <member type='way' ref='541532867' role='' />\n    <member type='way' ref='761946430' role='' />\n    <member type='way' ref='541532868' role='' />\n    <member type='way' ref='692361513' role='' />\n    <member type='way' ref='692361511' role='' />\n    <member type='way' ref='541532866' role='' />\n    <member type='way' ref='541532865' role='' />\n    <member type='way' ref='541532861' role='' />\n    <member type='way' ref='541532864' role='' />\n    <member type='way' ref='146511185' role='' />\n    <member type='way' ref='541532863' role='' />\n    <member type='way' ref='146511187' role='' />\n    <member type='way' ref='146511189' role='' />\n    <member type='way' ref='23320471' role='' />\n    <member type='way' ref='440140529' role='' />\n    <member type='way' ref='440140532' role='' />\n    <member type='way' ref='440140531' role='' />\n    <member type='way' ref='440140528' role='' />\n    <member type='way' ref='529371826' role='' />\n    <member type='way' ref='9375303' role='' />\n    <member type='way' ref='279054989' role='' />\n    <member type='way' ref='279054987' role='' />\n    <member type='way' ref='46766460' role='' />\n    <member type='way' ref='46766459' role='' />\n    <member type='way' ref='289756324' role='' />\n    <member type='way' ref='161465009' role='' />\n    <member type='way' ref='51676172' role='' />\n    <member type='way' ref='772699194' role='' />\n    <member type='way' ref='819559515' role='' />\n    <member type='way' ref='188155922' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='819559513' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819308078' role='' />\n    <member type='way' ref='819559512' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='819559511' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='819308077' role='' />\n    <member type='way' ref='818931650' role='' />\n    <member type='way' ref='188155891' role='' />\n    <member type='way' ref='833318550' role='' />\n    <member type='way' ref='161208735' role='' />\n    <member type='way' ref='517466647' role='' />\n    <member type='way' ref='279054991' role='' />\n    <member type='way' ref='397936984' role='' />\n    <member type='way' ref='23338944' role='' />\n    <member type='way' ref='513775934' role='' />\n    <member type='way' ref='513775931' role='' />\n    <member type='way' ref='285963332' role='' />\n    <member type='way' ref='513775935' role='' />\n    <member type='way' ref='714366703' role='' />\n    <member type='way' ref='833318552' role='' />\n    <member type='way' ref='714526872' role='' />\n    <member type='way' ref='833318553' role='' />\n    <member type='way' ref='833318551' role='' />\n    <member type='way' ref='513775939' role='' />\n    <member type='way' ref='513775930' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='39461687' role='' />\n    <member type='way' ref='682688470' role='' />\n    <member type='way' ref='414177890' role='' />\n    <member type='way' ref='517427345' role='' />\n    <member type='way' ref='517427348' role='' />\n    <member type='way' ref='379126354' role='' />\n    <member type='way' ref='823646246' role='' />\n    <member type='way' ref='9654384' role='' />\n    <member type='way' ref='728210419' role='' />\n    <member type='way' ref='513775944' role='' />\n    <member type='way' ref='617119454' role='' />\n    <member type='way' ref='10999525' role='' />\n    <member type='way' ref='319841468' role='' />\n    <member type='way' ref='546295339' role='' />\n    <member type='way' ref='510335879' role='' />\n    <member type='way' ref='510335862' role='' />\n    <member type='way' ref='161190692' role='' />\n    <member type='way' ref='510335870' role='' />\n    <member type='way' ref='161486851' role='' />\n    <member type='way' ref='510335842' role='' />\n    <member type='way' ref='510335846' role='' />\n    <member type='way' ref='788083572' role='' />\n    <member type='way' ref='319841465' role='' />\n    <member type='way' ref='741996204' role='' />\n    <member type='way' ref='23444180' role='' />\n    <member type='way' ref='741996205' role='' />\n    <member type='way' ref='272706515' role='' />\n    <member type='way' ref='788083573' role='' />\n    <member type='way' ref='272706514' role='' />\n    <member type='way' ref='272706525' role='' />\n    <member type='way' ref='272706535' role='' />\n    <member type='way' ref='682518206' role='' />\n    <member type='way' ref='272706511' role='' />\n    <member type='way' ref='272706533' role='' />\n    <member type='way' ref='510581880' role='' />\n    <member type='way' ref='272706517' role='' />\n    <member type='way' ref='272706532' role='' />\n    <member type='way' ref='272706531' role='' />\n    <member type='way' ref='272706520' role='' />\n    <member type='way' ref='272706524' role='' />\n    <member type='way' ref='272706513' role='' />\n    <member type='way' ref='272706510' role='' />\n    <member type='way' ref='272706528' role='' />\n    <member type='way' ref='688687939' role='' />\n    <member type='way' ref='510624279' role='' />\n    <member type='way' ref='510624268' role='' />\n    <member type='way' ref='272706526' role='' />\n    <member type='way' ref='510624247' role='' />\n    <member type='way' ref='618993313' role='' />\n    <member type='way' ref='24430829' role='' />\n    <member type='way' ref='517466669' role='' />\n    <member type='way' ref='56323454' role='' />\n    <member type='way' ref='517466668' role='' />\n    <member type='way' ref='510632776' role='' />\n    <member type='way' ref='44829595' role='' />\n    <member type='way' ref='161466777' role='' />\n    <member type='way' ref='326276383' role='' />\n    <member type='way' ref='11003381' role='' />\n    <member type='way' ref='326276396' role='' />\n    <member type='way' ref='161187739' role='' />\n    <member type='way' ref='326276387' role='' />\n    <member type='way' ref='326276391' role='' />\n    <member type='way' ref='43076107' role='' />\n    <member type='way' ref='71391950' role='' />\n    <member type='way' ref='771999805' role='' />\n    <member type='way' ref='59638078' role='' />\n    <member type='way' ref='309700793' role='' />\n    <member type='way' ref='309700794' role='' />\n    <member type='way' ref='27064373' role='' />\n    <member type='way' ref='27064374' role='' />\n    <member type='way' ref='326156168' role='' />\n    <member type='way' ref='188163074' role='' />\n    <member type='way' ref='326156169' role='' />\n    <member type='way' ref='11003452' role='' />\n    <member type='way' ref='188163076' role='' />\n    <member type='way' ref='492828713' role='' />\n    <member type='way' ref='492828712' role='' />\n    <member type='way' ref='51772367' role='' />\n    <member type='way' ref='99529286' role='' />\n    <member type='way' ref='99529506' role='' />\n    <member type='way' ref='51772369' role='' />\n    <member type='way' ref='305777809' role='' />\n    <member type='way' ref='728708233' role='' />\n    <member type='way' ref='99529656' role='' />\n    <member type='way' ref='99529468' role='' />\n    <member type='way' ref='39131598' role='' />\n    <member type='way' ref='39131601' role='' />\n    <member type='way' ref='123954806' role='' />\n    <member type='way' ref='99529252' role='' />\n    <member type='way' ref='87788719' role='' />\n    <member type='way' ref='99529664' role='' />\n    <member type='way' ref='99529601' role='' />\n    <member type='way' ref='728708231' role='' />\n    <member type='way' ref='123954808' role='' />\n    <member type='way' ref='99529458' role='' />\n    <member type='way' ref='99529614' role='' />\n    <member type='way' ref='99529541' role='' />\n    <member type='way' ref='99529300' role='' />\n    <member type='way' ref='99529669' role='' />\n    <member type='way' ref='98240455' role='' />\n    <member type='way' ref='98240453' role='' />\n    <member type='way' ref='813756628' role='' />\n    <member type='way' ref='98234177' role='' />\n    <member type='way' ref='98234175' role='' />\n    <member type='way' ref='319062894' role='' />\n    <member type='way' ref='319062895' role='' />\n    <member type='way' ref='209196326' role='' />\n    <member type='way' ref='209196328' role='' />\n    <member type='way' ref='106467525' role='' />\n    <member type='way' ref='106467520' role='' />\n    <member type='way' ref='231059758' role='' />\n    <member type='way' ref='231059756' role='' />\n    <member type='way' ref='709615630' role='' />\n    <member type='way' ref='29314712' role='' />\n    <member type='way' ref='29314711' role='' />\n    <member type='way' ref='163600212' role='' />\n    <member type='way' ref='837149996' role='' />\n    <member type='way' ref='837149997' role='' />\n    <member type='way' ref='837149995' role='' />\n    <member type='way' ref='837149998' role='' />\n    <member type='way' ref='813756627' role='' />\n    <member type='way' ref='427314412' role='' />\n    <member type='way' ref='838292586' role='' />\n    <member type='way' ref='782010056' role='' />\n    <member type='way' ref='782010057' role='' />\n    <member type='way' ref='132688030' role='' />\n    <member type='way' ref='838292597' role='' />\n    <member type='way' ref='8041982' role='' />\n    <member type='way' ref='838292592' role='' />\n    <member type='way' ref='8041981' role='' />\n    <member type='way' ref='252104525' role='' />\n    <member type='way' ref='252104520' role='' />\n    <member type='way' ref='848360981' role='' />\n    <member type='way' ref='72256434' role='' />\n    <member type='way' ref='681143380' role='' />\n    <member type='way' ref='681139365' role='' />\n    <member type='way' ref='107114265' role='' />\n    <member type='way' ref='275143515' role='' />\n    <member type='way' ref='496588160' role='' />\n    <member type='way' ref='496120886' role='' />\n    <member type='way' ref='825980028' role='' />\n    <member type='way' ref='779192145' role='' />\n    <member type='way' ref='825980013' role='' />\n    <member type='way' ref='368771669' role='' />\n    <member type='way' ref='825980021' role='' />\n    <member type='way' ref='368771668' role='' />\n    <member type='way' ref='825980016' role='' />\n    <member type='way' ref='782014017' role='' />\n    <member type='way' ref='825980011' role='' />\n    <member type='way' ref='782014016' role='' />\n    <member type='way' ref='286203393' role='' />\n    <member type='way' ref='496107793' role='' />\n    <member type='way' ref='496107794' role='' />\n    <member type='way' ref='779192156' role='' />\n    <member type='way' ref='286552816' role='' />\n    <member type='way' ref='71973851' role='' />\n    <member type='way' ref='348766149' role='' />\n    <member type='way' ref='348766145' role='' />\n    <member type='way' ref='348771994' role='' />\n    <member type='way' ref='285709739' role='' />\n    <tag k='from' v='Berlin ZOB' />\n    <tag k='name' v='Flixbus N150: Berlin ZOB =&gt; Aalborg, Busbahnhof' />\n    <tag k='name:da' v='Flixbus N150: Berlins centrale busstation =&gt; Aalborg, busstation' />\n    <tag k='name:de' v='Flixbus N150: Berlin ZOB =&gt; Aalborg, Busbahnhof' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='N150' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Aalborg, Busbahnhof' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='10080986' timestamp='2020-09-23T12:35:39Z' uid='11211647' user='wermak' visible='true' version='49' changeset='91367620'>\n    <member type='node' ref='3393833062' role='stop' />\n    <member type='way' ref='332263376' role='platform' />\n    <member type='node' ref='7779170659' role='stop' />\n    <member type='node' ref='7650108367' role='platform' />\n    <member type='node' ref='1987802672' role='stop' />\n    <member type='node' ref='7647740030' role='platform' />\n    <member type='node' ref='1987802673' role='stop' />\n    <member type='node' ref='7647758213' role='platform' />\n    <member type='node' ref='1987802675' role='stop' />\n    <member type='node' ref='7647758212' role='platform' />\n    <member type='node' ref='1987802677' role='stop' />\n    <member type='node' ref='7647758211' role='platform' />\n    <member type='node' ref='1987802678' role='stop' />\n    <member type='node' ref='7647758210' role='platform' />\n    <member type='node' ref='5974391200' role='stop' />\n    <member type='node' ref='6223447952' role='stop' />\n    <member type='way' ref='513595672' role='platform' />\n    <member type='node' ref='6823522163' role='stop' />\n    <member type='node' ref='367042031' role='platform' />\n    <member type='way' ref='553699678' role='platform' />\n    <member type='node' ref='2896384055' role='stop' />\n    <member type='node' ref='2868414792' role='stop' />\n    <member type='node' ref='6046917225' role='platform' />\n    <member type='node' ref='2868414789' role='stop' />\n    <member type='node' ref='6046917223' role='platform' />\n    <member type='node' ref='5274710734' role='stop' />\n    <member type='way' ref='545607311' role='platform' />\n    <member type='way' ref='491845660' role='' />\n    <member type='way' ref='348763865' role='' />\n    <member type='way' ref='348763864' role='' />\n    <member type='way' ref='285709733' role='' />\n    <member type='way' ref='369152632' role='' />\n    <member type='way' ref='348766151' role='' />\n    <member type='way' ref='369151521' role='' />\n    <member type='way' ref='369151056' role='' />\n    <member type='way' ref='286552815' role='' />\n    <member type='way' ref='369151062' role='' />\n    <member type='way' ref='779192157' role='' />\n    <member type='way' ref='97574427' role='' />\n    <member type='way' ref='496580797' role='' />\n    <member type='way' ref='495987825' role='' />\n    <member type='way' ref='825980010' role='' />\n    <member type='way' ref='495987826' role='' />\n    <member type='way' ref='825980012' role='' />\n    <member type='way' ref='825980017' role='' />\n    <member type='way' ref='779192151' role='' />\n    <member type='way' ref='275143505' role='' />\n    <member type='way' ref='275143512' role='' />\n    <member type='way' ref='681139362' role='' />\n    <member type='way' ref='496249591' role='' />\n    <member type='way' ref='8169286' role='' />\n    <member type='way' ref='132688031' role='' />\n    <member type='way' ref='782010058' role='' />\n    <member type='way' ref='813756724' role='' />\n    <member type='way' ref='146514455' role='' />\n    <member type='way' ref='837149992' role='' />\n    <member type='way' ref='163600213' role='' />\n    <member type='way' ref='29314709' role='' />\n    <member type='way' ref='100211823' role='' />\n    <member type='way' ref='231059757' role='' />\n    <member type='way' ref='231059759' role='' />\n    <member type='way' ref='106467516' role='' />\n    <member type='way' ref='106467529' role='' />\n    <member type='way' ref='209196333' role='' />\n    <member type='way' ref='209196336' role='' />\n    <member type='way' ref='319062897' role='' />\n    <member type='way' ref='319062896' role='' />\n    <member type='way' ref='98234179' role='' />\n    <member type='way' ref='813756723' role='' />\n    <member type='way' ref='98234180' role='' />\n    <member type='way' ref='98240450' role='' />\n    <member type='way' ref='98240457' role='' />\n    <member type='way' ref='166899885' role='' />\n    <member type='way' ref='99529410' role='' />\n    <member type='way' ref='99529418' role='' />\n    <member type='way' ref='99529393' role='' />\n    <member type='way' ref='99529552' role='' />\n    <member type='way' ref='728708234' role='' />\n    <member type='way' ref='123954809' role='' />\n    <member type='way' ref='99529595' role='' />\n    <member type='way' ref='99529548' role='' />\n    <member type='way' ref='166899884' role='' />\n    <member type='way' ref='99529636' role='' />\n    <member type='way' ref='87788715' role='' />\n    <member type='way' ref='166899883' role='' />\n    <member type='way' ref='123954807' role='' />\n    <member type='way' ref='39131613' role='' />\n    <member type='way' ref='39131615' role='' />\n    <member type='way' ref='99529700' role='' />\n    <member type='way' ref='728708243' role='' />\n    <member type='way' ref='99529489' role='' />\n    <member type='way' ref='305777808' role='' />\n    <member type='way' ref='51772366' role='' />\n    <member type='way' ref='99529449' role='' />\n    <member type='way' ref='99529576' role='' />\n    <member type='way' ref='492828711' role='' />\n    <member type='way' ref='62227774' role='' />\n    <member type='way' ref='692148513' role='' />\n    <member type='way' ref='692148512' role='' />\n    <member type='way' ref='326285668' role='' />\n    <member type='way' ref='692148510' role='' />\n    <member type='way' ref='23376360' role='' />\n    <member type='way' ref='23376359' role='' />\n    <member type='way' ref='311697613' role='' />\n    <member type='way' ref='326156170' role='' />\n    <member type='way' ref='56045342' role='' />\n    <member type='way' ref='309700792' role='' />\n    <member type='way' ref='326285671' role='' />\n    <member type='way' ref='59638081' role='' />\n    <member type='way' ref='771999806' role='' />\n    <member type='way' ref='71391931' role='' />\n    <member type='way' ref='43076108' role='' />\n    <member type='way' ref='326276386' role='' />\n    <member type='way' ref='326276401' role='' />\n    <member type='way' ref='161187726' role='' />\n    <member type='way' ref='326285661' role='' />\n    <member type='way' ref='37343749' role='' />\n    <member type='way' ref='161466779' role='' />\n    <member type='way' ref='44829596' role='' />\n    <member type='way' ref='517466670' role='' />\n    <member type='way' ref='517466671' role='' />\n    <member type='way' ref='153531784' role='' />\n    <member type='way' ref='510624260' role='' />\n    <member type='way' ref='682500794' role='' />\n    <member type='way' ref='510624310' role='' />\n    <member type='way' ref='517466664' role='' />\n    <member type='way' ref='510624259' role='' />\n    <member type='way' ref='517466663' role='' />\n    <member type='way' ref='510624304' role='' />\n    <member type='way' ref='510624303' role='' />\n    <member type='way' ref='510624305' role='' />\n    <member type='way' ref='510624309' role='' />\n    <member type='way' ref='10999808' role='' />\n    <member type='way' ref='272706522' role='' />\n    <member type='way' ref='272706527' role='' />\n    <member type='way' ref='272706508' role='' />\n    <member type='way' ref='272706519' role='' />\n    <member type='way' ref='272706530' role='' />\n    <member type='way' ref='272706521' role='' />\n    <member type='way' ref='272706534' role='' />\n    <member type='way' ref='741996213' role='' />\n    <member type='way' ref='161469199' role='' />\n    <member type='way' ref='741996207' role='' />\n    <member type='way' ref='23444181' role='' />\n    <member type='way' ref='788083570' role='' />\n    <member type='way' ref='788083569' role='' />\n    <member type='way' ref='319841464' role='' />\n    <member type='way' ref='510335857' role='' />\n    <member type='way' ref='161486841' role='' />\n    <member type='way' ref='517427354' role='' />\n    <member type='way' ref='9654314' role='' />\n    <member type='way' ref='510335860' role='' />\n    <member type='way' ref='510335854' role='' />\n    <member type='way' ref='161190702' role='' />\n    <member type='way' ref='517427353' role='' />\n    <member type='way' ref='319841469' role='' />\n    <member type='way' ref='560552446' role='' />\n    <member type='way' ref='319841466' role='' />\n    <member type='way' ref='161464999' role='' />\n    <member type='way' ref='42424841' role='' />\n    <member type='way' ref='823646245' role='' />\n    <member type='way' ref='23629785' role='' />\n    <member type='way' ref='517427341' role='' />\n    <member type='way' ref='379126351' role='' />\n    <member type='way' ref='414177889' role='' />\n    <member type='way' ref='682688472' role='' />\n    <member type='way' ref='23338617' role='' />\n    <member type='way' ref='513775941' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='513775934' role='' />\n    <member type='way' ref='513775931' role='' />\n    <member type='way' ref='285963332' role='' />\n    <member type='way' ref='513775935' role='' />\n    <member type='way' ref='714366703' role='' />\n    <member type='way' ref='833318552' role='' />\n    <member type='way' ref='714526872' role='' />\n    <member type='way' ref='833318553' role='' />\n    <member type='way' ref='833318551' role='' />\n    <member type='way' ref='513775939' role='' />\n    <member type='way' ref='513775930' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='513775941' role='' />\n    <member type='way' ref='686698865' role='' />\n    <member type='way' ref='517427335' role='' />\n    <member type='way' ref='517427330' role='' />\n    <member type='way' ref='517466645' role='' />\n    <member type='way' ref='517466643' role='' />\n    <member type='way' ref='517466644' role='' />\n    <member type='way' ref='161208735' role='' />\n    <member type='way' ref='833318550' role='' />\n    <member type='way' ref='728192376' role='' />\n    <member type='way' ref='517466646' role='' />\n    <member type='way' ref='517466648' role='' />\n    <member type='way' ref='279054992' role='' />\n    <member type='way' ref='51676172' role='' />\n    <member type='way' ref='772699194' role='' />\n    <member type='way' ref='819559515' role='' />\n    <member type='way' ref='188155922' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='819559513' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819308078' role='' />\n    <member type='way' ref='819559512' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='819559511' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='819308077' role='' />\n    <member type='way' ref='818931650' role='' />\n    <member type='way' ref='188155891' role='' />\n    <member type='way' ref='728192376' role='' />\n    <member type='way' ref='517466646' role='' />\n    <member type='way' ref='517466648' role='' />\n    <member type='way' ref='279054992' role='' />\n    <member type='way' ref='161465009' role='' />\n    <member type='way' ref='289756324' role='' />\n    <member type='way' ref='46766459' role='' />\n    <member type='way' ref='46766460' role='' />\n    <member type='way' ref='279054987' role='' />\n    <member type='way' ref='279054989' role='' />\n    <member type='way' ref='9375303' role='' />\n    <member type='way' ref='529371826' role='' />\n    <member type='way' ref='440140528' role='' />\n    <member type='way' ref='440140531' role='' />\n    <member type='way' ref='161190693' role='' />\n    <member type='way' ref='440140529' role='' />\n    <member type='way' ref='23320471' role='' />\n    <member type='way' ref='146511189' role='' />\n    <member type='way' ref='146511188' role='' />\n    <member type='way' ref='143821024' role='' />\n    <member type='way' ref='541532857' role='' />\n    <member type='way' ref='8812730' role='' />\n    <member type='way' ref='541532855' role='' />\n    <member type='way' ref='692361519' role='' />\n    <member type='way' ref='692361505' role='' />\n    <member type='way' ref='161234020' role='' />\n    <member type='way' ref='761946431' role='' />\n    <member type='way' ref='835340625' role='' />\n    <member type='way' ref='498859739' role='' />\n    <member type='way' ref='498859736' role='' />\n    <member type='way' ref='835340627' role='' />\n    <member type='way' ref='498859744' role='' />\n    <member type='way' ref='520517114' role='' />\n    <member type='way' ref='128898677' role='' />\n    <member type='way' ref='348352697' role='' />\n    <member type='way' ref='234119942' role='' />\n    <member type='way' ref='321378453' role='' />\n    <member type='way' ref='689482242' role='' />\n    <member type='way' ref='321378452' role='' />\n    <member type='way' ref='184792919' role='' />\n    <member type='way' ref='192705441' role='' />\n    <member type='way' ref='144918016' role='' />\n    <member type='way' ref='144918017' role='' />\n    <member type='way' ref='144918015' role='' />\n    <member type='way' ref='843150465' role='' />\n    <member type='way' ref='321378456' role='' />\n    <member type='way' ref='30406550' role='' />\n    <member type='way' ref='843150466' role='' />\n    <member type='way' ref='30406546' role='' />\n    <member type='way' ref='843150464' role='' />\n    <member type='way' ref='144915853' role='' />\n    <member type='way' ref='144915854' role='' />\n    <member type='way' ref='144915851' role='' />\n    <member type='way' ref='492828707' role='' />\n    <member type='way' ref='123458827' role='' />\n    <member type='way' ref='30407255' role='' />\n    <member type='way' ref='492828705' role='' />\n    <member type='way' ref='123458828' role='' />\n    <member type='way' ref='144915855' role='' />\n    <member type='way' ref='627085345' role='' />\n    <member type='way' ref='633456015' role='' />\n    <member type='way' ref='51754191' role='' />\n    <member type='way' ref='38028830' role='' />\n    <member type='way' ref='38028831' role='' />\n    <member type='way' ref='119161422' role='' />\n    <member type='way' ref='119161418' role='' />\n    <member type='way' ref='241787612' role='' />\n    <member type='way' ref='833243911' role='' />\n    <member type='way' ref='241787611' role='' />\n    <member type='way' ref='241787610' role='' />\n    <member type='way' ref='125592653' role='' />\n    <member type='way' ref='241787616' role='' />\n    <member type='way' ref='234346247' role='' />\n    <member type='way' ref='125592652' role='' />\n    <member type='way' ref='492828700' role='' />\n    <member type='way' ref='241787618' role='' />\n    <member type='way' ref='33787295' role='' />\n    <member type='way' ref='492828701' role='' />\n    <member type='way' ref='23308640' role='' />\n    <member type='way' ref='319988267' role='' />\n    <member type='way' ref='241724478' role='' />\n    <member type='way' ref='319988209' role='' />\n    <member type='way' ref='319988213' role='' />\n    <member type='way' ref='319988203' role='' />\n    <member type='way' ref='319988225' role='' />\n    <member type='way' ref='118842804' role='' />\n    <member type='way' ref='118842790' role='' />\n    <member type='way' ref='319988231' role='' />\n    <member type='way' ref='683582708' role='' />\n    <member type='way' ref='358587890' role='' />\n    <member type='way' ref='319988188' role='' />\n    <member type='way' ref='118842803' role='' />\n    <member type='way' ref='683582710' role='' />\n    <member type='way' ref='118842805' role='' />\n    <member type='way' ref='118842812' role='' />\n    <member type='way' ref='92286848' role='' />\n    <member type='way' ref='633223284' role='' />\n    <member type='way' ref='319988236' role='' />\n    <member type='way' ref='633223273' role='' />\n    <member type='way' ref='632790909' role='' />\n    <member type='way' ref='237480738' role='' />\n    <member type='way' ref='81113481' role='' />\n    <member type='way' ref='632789483' role='' />\n    <member type='way' ref='23150407' role='' />\n    <member type='way' ref='23150408' role='' />\n    <member type='way' ref='23150406' role='' />\n    <member type='way' ref='632789481' role='' />\n    <member type='way' ref='92286833' role='' />\n    <member type='way' ref='92286866' role='' />\n    <member type='way' ref='632789477' role='' />\n    <member type='way' ref='632789458' role='' />\n    <member type='way' ref='632789460' role='' />\n    <member type='way' ref='23236479' role='' />\n    <member type='way' ref='459637986' role='' />\n    <member type='way' ref='297072044' role='' />\n    <member type='way' ref='88288530' role='' />\n    <member type='way' ref='88288521' role='' />\n    <member type='way' ref='358530815' role='' />\n    <member type='way' ref='297157693' role='' />\n    <member type='way' ref='632789460' role='' />\n    <member type='way' ref='632789457' role='' />\n    <member type='way' ref='673731742' role='' />\n    <member type='way' ref='632789456' role='' />\n    <member type='way' ref='65009887' role='' />\n    <member type='way' ref='65009888' role='' />\n    <member type='way' ref='23237104' role='' />\n    <member type='way' ref='497455256' role='' />\n    <member type='way' ref='497455255' role='' />\n    <member type='way' ref='92286832' role='' />\n    <member type='way' ref='320454515' role='' />\n    <member type='way' ref='320454494' role='' />\n    <member type='way' ref='22970260' role='' />\n    <member type='way' ref='92286869' role='' />\n    <member type='way' ref='92286876' role='' />\n    <member type='way' ref='632773389' role='' />\n    <member type='way' ref='234855460' role='' />\n    <member type='way' ref='320454517' role='' />\n    <member type='way' ref='320454487' role='' />\n    <member type='way' ref='320454499' role='' />\n    <member type='way' ref='320454512' role='' />\n    <member type='way' ref='320454501' role='' />\n    <member type='way' ref='320454477' role='' />\n    <member type='way' ref='320454483' role='' />\n    <member type='way' ref='320454482' role='' />\n    <member type='way' ref='320454491' role='' />\n    <member type='way' ref='320454513' role='' />\n    <member type='way' ref='320454507' role='' />\n    <member type='way' ref='320454506' role='' />\n    <member type='way' ref='320454473' role='' />\n    <member type='way' ref='320454498' role='' />\n    <member type='way' ref='320454493' role='' />\n    <member type='way' ref='320454505' role='' />\n    <member type='way' ref='26160622' role='' />\n    <member type='way' ref='88956595' role='' />\n    <member type='way' ref='320454476' role='' />\n    <member type='way' ref='320454502' role='' />\n    <member type='way' ref='320454520' role='' />\n    <member type='way' ref='88956579' role='' />\n    <member type='way' ref='367544799' role='' />\n    <member type='way' ref='320454492' role='' />\n    <member type='way' ref='320454474' role='' />\n    <member type='way' ref='88439022' role='' />\n    <member type='way' ref='320454500' role='' />\n    <member type='way' ref='88438981' role='' />\n    <member type='way' ref='492836255' role='' />\n    <member type='way' ref='51754192' role='' />\n    <member type='way' ref='386593765' role='' />\n    <member type='way' ref='676193412' role='' />\n    <member type='way' ref='4586020' role='' />\n    <member type='way' ref='125556146' role='' />\n    <member type='way' ref='2014317' role='' />\n    <member type='way' ref='4586931' role='' />\n    <member type='way' ref='9470519' role='' />\n    <member type='way' ref='26713012' role='' />\n    <member type='way' ref='297696236' role='' />\n    <member type='way' ref='297696237' role='' />\n    <member type='way' ref='22969810' role='' />\n    <member type='way' ref='58999665' role='' />\n    <member type='way' ref='672713513' role='' />\n    <member type='way' ref='58999666' role='' />\n    <member type='way' ref='62073052' role='' />\n    <member type='way' ref='62073051' role='' />\n    <member type='way' ref='59002628' role='' />\n    <member type='way' ref='254867496' role='' />\n    <member type='way' ref='254867498' role='' />\n    <member type='way' ref='35438709' role='' />\n    <member type='way' ref='761557032' role='' />\n    <member type='way' ref='23486731' role='' />\n    <member type='way' ref='513492326' role='' />\n    <member type='way' ref='810335209' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175447' role='' />\n    <member type='way' ref='369175445' role='' />\n    <member type='way' ref='369175441' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='664891127' role='' />\n    <member type='way' ref='29358757' role='' />\n    <member type='way' ref='52565598' role='' />\n    <member type='way' ref='23486732' role='' />\n    <member type='way' ref='449199493' role='' />\n    <member type='way' ref='449199494' role='' />\n    <member type='way' ref='23486733' role='' />\n    <member type='way' ref='40814531' role='' />\n    <member type='way' ref='40814532' role='' />\n    <member type='way' ref='213210051' role='' />\n    <member type='way' ref='30050896' role='' />\n    <member type='way' ref='664891129' role='' />\n    <member type='way' ref='40814026' role='' />\n    <member type='way' ref='40814025' role='' />\n    <member type='way' ref='53772038' role='' />\n    <member type='way' ref='53772035' role='' />\n    <member type='way' ref='24629915' role='' />\n    <member type='way' ref='135052326' role='' />\n    <member type='way' ref='578209354' role='' />\n    <member type='way' ref='366669796' role='' />\n    <member type='way' ref='366669793' role='' />\n    <member type='way' ref='219837015' role='' />\n    <member type='way' ref='497483693' role='' />\n    <member type='way' ref='219837016' role='' />\n    <member type='way' ref='675382052' role='' />\n    <member type='way' ref='672529639' role='' />\n    <member type='way' ref='672529638' role='' />\n    <member type='way' ref='173885536' role='' />\n    <member type='way' ref='173885534' role='' />\n    <member type='way' ref='539715724' role='' />\n    <member type='way' ref='322752604' role='' />\n    <member type='way' ref='660630405' role='' />\n    <member type='way' ref='53088754' role='' />\n    <member type='way' ref='30447471' role='' />\n    <member type='way' ref='30447472' role='' />\n    <member type='way' ref='4586920' role='' />\n    <member type='way' ref='4586917' role='' />\n    <member type='way' ref='29216340' role='' />\n    <member type='way' ref='833202603' role='' />\n    <member type='way' ref='4586916' role='' />\n    <member type='way' ref='833202570' role='' />\n    <member type='way' ref='165254731' role='' />\n    <member type='way' ref='165254734' role='' />\n    <member type='way' ref='4586912' role='' />\n    <member type='way' ref='833202567' role='' />\n    <member type='way' ref='125556125' role='' />\n    <member type='way' ref='27285022' role='' />\n    <member type='way' ref='204165652' role='' />\n    <member type='way' ref='204165653' role='' />\n    <member type='way' ref='661057632' role='' />\n    <member type='way' ref='661057631' role='' />\n    <member type='way' ref='661055344' role='' />\n    <member type='way' ref='4586904' role='' />\n    <member type='way' ref='4586901' role='' />\n    <member type='way' ref='661055345' role='' />\n    <member type='way' ref='833202566' role='' />\n    <member type='way' ref='161385861' role='' />\n    <member type='way' ref='161385866' role='' />\n    <member type='way' ref='23106925' role='' />\n    <member type='way' ref='23113983' role='' />\n    <member type='way' ref='4586896' role='' />\n    <member type='way' ref='665717758' role='' />\n    <member type='way' ref='23113972' role='' />\n    <member type='way' ref='23113973' role='' />\n    <member type='way' ref='4586893' role='' />\n    <member type='way' ref='473318560' role='' />\n    <member type='way' ref='169502264' role='' />\n    <member type='way' ref='169502265' role='' />\n    <member type='way' ref='473318566' role='' />\n    <member type='way' ref='473318567' role='' />\n    <member type='way' ref='473318571' role='' />\n    <member type='way' ref='473318570' role='' />\n    <member type='way' ref='2862595' role='' />\n    <member type='way' ref='22820722' role='' />\n    <member type='way' ref='22820721' role='' />\n    <member type='way' ref='576352094' role='' />\n    <member type='way' ref='22139184' role='' />\n    <member type='way' ref='38957691' role='' />\n    <member type='way' ref='26581486' role='' />\n    <member type='way' ref='56054084' role='' />\n    <member type='way' ref='22820758' role='' />\n    <member type='way' ref='56054081' role='' />\n    <member type='way' ref='5714523' role='' />\n    <member type='way' ref='710332450' role='' />\n    <member type='way' ref='22133739' role='' />\n    <member type='way' ref='22133741' role='' />\n    <member type='way' ref='25829540' role='' />\n    <member type='way' ref='5037793' role='' />\n    <member type='way' ref='595490508' role='' />\n    <member type='way' ref='313106227' role='' />\n    <member type='way' ref='377766354' role='' />\n    <member type='way' ref='47160379' role='' />\n    <member type='way' ref='248690767' role='' />\n    <member type='way' ref='536501275' role='' />\n    <member type='way' ref='536501276' role='' />\n    <member type='way' ref='315213104' role='' />\n    <member type='way' ref='23059551' role='' />\n    <member type='way' ref='167241864' role='' />\n    <member type='way' ref='169280772' role='' />\n    <member type='way' ref='60981193' role='' />\n    <member type='way' ref='377101890' role='' />\n    <member type='way' ref='377645413' role='' />\n    <member type='way' ref='61413097' role='' />\n    <member type='way' ref='377645414' role='' />\n    <member type='way' ref='377101892' role='' />\n    <member type='way' ref='377101889' role='' />\n    <member type='way' ref='377101891' role='' />\n    <member type='way' ref='377101894' role='' />\n    <member type='way' ref='169327831' role='' />\n    <member type='way' ref='377101888' role='' />\n    <member type='way' ref='377101893' role='' />\n    <member type='way' ref='214796416' role='' />\n    <member type='way' ref='5037792' role='' />\n    <member type='way' ref='23967768' role='' />\n    <member type='way' ref='23416611' role='' />\n    <member type='way' ref='87700223' role='' />\n    <member type='way' ref='23416591' role='' />\n    <member type='way' ref='424099552' role='' />\n    <member type='way' ref='514392684' role='' />\n    <member type='way' ref='32551883' role='' />\n    <member type='way' ref='198685165' role='' />\n    <member type='way' ref='198685161' role='' />\n    <member type='way' ref='32552096' role='' />\n    <member type='way' ref='32705199' role='' />\n    <member type='way' ref='728153623' role='' />\n    <member type='way' ref='26413433' role='' />\n    <member type='way' ref='129025644' role='' />\n    <member type='way' ref='32515008' role='' />\n    <member type='way' ref='23080117' role='' />\n    <member type='way' ref='198685161' role='' />\n    <member type='way' ref='198685165' role='' />\n    <member type='way' ref='32551883' role='' />\n    <member type='way' ref='514392684' role='' />\n    <member type='way' ref='32551882' role='' />\n    <member type='way' ref='120076961' role='' />\n    <member type='way' ref='228838849' role='' />\n    <member type='way' ref='5666001' role='' />\n    <member type='way' ref='424099565' role='' />\n    <member type='way' ref='5037792' role='' />\n    <member type='way' ref='214796416' role='' />\n    <member type='way' ref='377101893' role='' />\n    <member type='way' ref='377101888' role='' />\n    <member type='way' ref='169327831' role='' />\n    <member type='way' ref='377101894' role='' />\n    <member type='way' ref='377101891' role='' />\n    <member type='way' ref='377101889' role='' />\n    <member type='way' ref='377101892' role='' />\n    <member type='way' ref='377645414' role='' />\n    <member type='way' ref='61413097' role='' />\n    <member type='way' ref='377645413' role='' />\n    <member type='way' ref='377101890' role='' />\n    <member type='way' ref='60981193' role='' />\n    <member type='way' ref='169280772' role='' />\n    <member type='way' ref='167241864' role='' />\n    <member type='way' ref='23059551' role='' />\n    <member type='way' ref='315213107' role='' />\n    <member type='way' ref='456448588' role='' />\n    <member type='way' ref='26304647' role='' />\n    <member type='way' ref='26285180' role='' />\n    <member type='way' ref='118437407' role='' />\n    <member type='way' ref='23059553' role='' />\n    <member type='way' ref='23059550' role='' />\n    <member type='way' ref='25141834' role='' />\n    <member type='way' ref='23098190' role='' />\n    <member type='way' ref='118435201' role='' />\n    <member type='way' ref='229424413' role='' />\n    <member type='way' ref='225696853' role='' />\n    <member type='way' ref='232965550' role='' />\n    <member type='way' ref='46753569' role='' />\n    <member type='way' ref='232965547' role='' />\n    <member type='way' ref='22323313' role='' />\n    <member type='way' ref='22323312' role='' />\n    <member type='way' ref='27229879' role='' />\n    <member type='way' ref='232965546' role='' />\n    <member type='way' ref='232965536' role='' />\n    <member type='way' ref='232965545' role='' />\n    <member type='way' ref='232965540' role='' />\n    <member type='way' ref='222565247' role='' />\n    <member type='way' ref='222565258' role='' />\n    <member type='way' ref='38273286' role='' />\n    <member type='way' ref='38273287' role='' />\n    <member type='way' ref='232967035' role='' />\n    <member type='way' ref='26141368' role='' />\n    <member type='way' ref='26141369' role='' />\n    <member type='way' ref='232967034' role='' />\n    <member type='way' ref='25016480' role='' />\n    <member type='way' ref='324319697' role='' />\n    <member type='way' ref='324319704' role='' />\n    <member type='way' ref='324319701' role='' />\n    <member type='way' ref='324319692' role='' />\n    <member type='way' ref='98223254' role='' />\n    <member type='way' ref='23022584' role='' />\n    <member type='way' ref='232971058' role='' />\n    <member type='way' ref='232971057' role='' />\n    <member type='way' ref='232971056' role='' />\n    <member type='way' ref='232971061' role='' />\n    <member type='way' ref='232971054' role='' />\n    <member type='way' ref='232971060' role='' />\n    <member type='way' ref='232971053' role='' />\n    <member type='way' ref='232971050' role='' />\n    <member type='way' ref='232975921' role='' />\n    <member type='way' ref='4019587' role='' />\n    <member type='way' ref='232975931' role='' />\n    <member type='way' ref='232975927' role='' />\n    <member type='way' ref='231419621' role='' />\n    <member type='way' ref='231419618' role='' />\n    <member type='way' ref='56014873' role='' />\n    <member type='way' ref='56014879' role='' />\n    <member type='way' ref='226071295' role='' />\n    <member type='way' ref='479912112' role='' />\n    <member type='way' ref='479912113' role='' />\n    <member type='way' ref='479912111' role='' />\n    <member type='way' ref='226071297' role='' />\n    <member type='way' ref='535382427' role='' />\n    <member type='way' ref='535382428' role='' />\n    <member type='way' ref='232982162' role='' />\n    <member type='way' ref='232982165' role='' />\n    <member type='way' ref='232982164' role='' />\n    <member type='way' ref='232982163' role='' />\n    <member type='way' ref='232982158' role='' />\n    <member type='way' ref='535382791' role='' />\n    <member type='way' ref='535382790' role='' />\n    <member type='way' ref='72135463' role='' />\n    <member type='way' ref='72135461' role='' />\n    <member type='way' ref='144142223' role='' />\n    <member type='way' ref='546560759' role='' />\n    <member type='way' ref='546560758' role='' />\n    <member type='way' ref='144142217' role='' />\n    <member type='way' ref='546560754' role='' />\n    <member type='way' ref='546560755' role='' />\n    <member type='way' ref='82161936' role='' />\n    <member type='way' ref='82161935' role='' />\n    <member type='way' ref='232994363' role='' />\n    <member type='way' ref='232994366' role='' />\n    <member type='way' ref='4019526' role='' />\n    <member type='way' ref='124981042' role='' />\n    <member type='way' ref='232994371' role='' />\n    <member type='way' ref='31958022' role='' />\n    <member type='way' ref='697956271' role='' />\n    <member type='way' ref='31958023' role='' />\n    <member type='way' ref='22739406' role='' />\n    <member type='way' ref='33290789' role='' />\n    <member type='way' ref='374065359' role='' />\n    <member type='way' ref='33290790' role='' />\n    <member type='way' ref='479922389' role='' />\n    <member type='way' ref='479922388' role='' />\n    <member type='way' ref='22739428' role='' />\n    <member type='way' ref='479922387' role='' />\n    <member type='way' ref='479922386' role='' />\n    <member type='way' ref='28637348' role='' />\n    <member type='way' ref='24676963' role='' />\n    <member type='way' ref='233023663' role='' />\n    <member type='way' ref='22733416' role='' />\n    <member type='way' ref='233023669' role='' />\n    <member type='way' ref='144144936' role='' />\n    <member type='way' ref='144144929' role='' />\n    <member type='way' ref='683710988' role='' />\n    <member type='way' ref='144144906' role='' />\n    <member type='way' ref='233023671' role='' />\n    <member type='way' ref='531552422' role='' />\n    <member type='way' ref='233023653' role='' />\n    <member type='way' ref='233023649' role='' />\n    <member type='way' ref='233023651' role='' />\n    <member type='way' ref='233023652' role='' />\n    <member type='way' ref='233023655' role='' />\n    <member type='way' ref='233103733' role='' />\n    <member type='way' ref='233103737' role='' />\n    <member type='way' ref='233103734' role='' />\n    <member type='way' ref='233103730' role='' />\n    <member type='way' ref='31488554' role='' />\n    <member type='way' ref='31488553' role='' />\n    <member type='way' ref='44021695' role='' />\n    <member type='way' ref='44021692' role='' />\n    <member type='way' ref='233112539' role='' />\n    <member type='way' ref='233112553' role='' />\n    <member type='way' ref='233112543' role='' />\n    <member type='way' ref='233112556' role='' />\n    <member type='way' ref='9404399' role='' />\n    <member type='way' ref='26149319' role='' />\n    <member type='way' ref='26149324' role='' />\n    <member type='way' ref='19629534' role='' />\n    <member type='way' ref='33152027' role='' />\n    <member type='way' ref='33152020' role='' />\n    <member type='way' ref='324319581' role='' />\n    <member type='way' ref='324319593' role='' />\n    <member type='way' ref='324319571' role='' />\n    <member type='way' ref='324319572' role='' />\n    <member type='way' ref='73392896' role='' />\n    <member type='way' ref='240140720' role='' />\n    <member type='way' ref='240140721' role='' />\n    <member type='way' ref='73392900' role='' />\n    <member type='way' ref='29036125' role='' />\n    <member type='way' ref='19716510' role='' />\n    <member type='way' ref='448963802' role='' />\n    <member type='way' ref='448963803' role='' />\n    <member type='way' ref='240140718' role='' />\n    <member type='way' ref='749559788' role='' />\n    <member type='way' ref='34614767' role='' />\n    <member type='way' ref='733027972' role='' />\n    <member type='way' ref='678151500' role='' />\n    <member type='way' ref='76839458' role='' />\n    <member type='way' ref='733027970' role='' />\n    <member type='way' ref='4717259' role='' />\n    <member type='way' ref='296557880' role='' />\n    <member type='way' ref='153203960' role='' />\n    <member type='way' ref='76839452' role='' />\n    <member type='way' ref='274608780' role='' />\n    <member type='way' ref='274608779' role='' />\n    <member type='way' ref='274606794' role='' />\n    <member type='way' ref='144053947' role='' />\n    <member type='way' ref='274606795' role='' />\n    <member type='way' ref='274606796' role='' />\n    <member type='way' ref='274606797' role='' />\n    <member type='way' ref='37352317' role='' />\n    <member type='way' ref='7998655' role='' />\n    <member type='way' ref='29650025' role='' />\n    <member type='way' ref='296557881' role='' />\n    <member type='way' ref='779492336' role='' />\n    <member type='way' ref='562643469' role='' />\n    <member type='way' ref='433798771' role='' />\n    <member type='way' ref='379519259' role='' />\n    <member type='way' ref='236855736' role='' />\n    <member type='way' ref='236855733' role='' />\n    <member type='way' ref='236855737' role='' />\n    <member type='way' ref='121243050' role='' />\n    <member type='way' ref='39337118' role='' />\n    <member type='way' ref='733372950' role='' />\n    <member type='way' ref='733372949' role='' />\n    <member type='way' ref='30809509' role='' />\n    <member type='way' ref='275422324' role='' />\n    <member type='way' ref='275422323' role='' />\n    <member type='way' ref='275422325' role='' />\n    <member type='way' ref='121243055' role='' />\n    <member type='way' ref='8666488' role='' />\n    <member type='way' ref='498866393' role='' />\n    <member type='way' ref='28060354' role='' />\n    <member type='way' ref='29651991' role='' />\n    <member type='way' ref='28150271' role='' />\n    <member type='way' ref='314450784' role='' />\n    <member type='way' ref='776093119' role='' />\n    <member type='way' ref='776093120' role='' />\n    <member type='way' ref='138982590' role='' />\n    <member type='way' ref='67153451' role='' />\n    <member type='way' ref='46739386' role='' />\n    <member type='way' ref='40033980' role='' />\n    <member type='way' ref='8666490' role='' />\n    <member type='way' ref='314450772' role='' />\n    <member type='way' ref='314450782' role='' />\n    <member type='way' ref='314450780' role='' />\n    <member type='way' ref='682144620' role='' />\n    <member type='way' ref='280958658' role='' />\n    <member type='way' ref='682144621' role='' />\n    <member type='way' ref='682144620' role='' />\n    <member type='way' ref='682144623' role='' />\n    <member type='way' ref='314450779' role='' />\n    <member type='way' ref='314450781' role='' />\n    <member type='way' ref='314450775' role='' />\n    <member type='way' ref='138982589' role='' />\n    <member type='way' ref='183984419' role='' />\n    <member type='way' ref='314450776' role='' />\n    <member type='way' ref='47252827' role='' />\n    <member type='way' ref='40033158' role='' />\n    <member type='way' ref='67153460' role='' />\n    <member type='way' ref='314450777' role='' />\n    <member type='way' ref='314450774' role='' />\n    <member type='way' ref='314450778' role='' />\n    <member type='way' ref='28150896' role='' />\n    <member type='way' ref='543196765' role='' />\n    <member type='way' ref='520405269' role='' />\n    <member type='way' ref='379812994' role='' />\n    <member type='way' ref='28150250' role='' />\n    <member type='way' ref='379852448' role='' />\n    <member type='way' ref='37831194' role='' />\n    <member type='way' ref='8666488' role='' />\n    <member type='way' ref='121243055' role='' />\n    <member type='way' ref='275422325' role='' />\n    <member type='way' ref='275422323' role='' />\n    <member type='way' ref='275422324' role='' />\n    <member type='way' ref='30809517' role='' />\n    <member type='way' ref='13641245' role='' />\n    <member type='way' ref='275422317' role='' />\n    <member type='way' ref='578627081' role='' />\n    <member type='way' ref='275422314' role='' />\n    <member type='way' ref='36933287' role='' />\n    <member type='way' ref='28222985' role='' />\n    <member type='way' ref='433798767' role='' />\n    <member type='way' ref='36780903' role='' />\n    <member type='way' ref='379519260' role='' />\n    <member type='way' ref='531462061' role='' />\n    <member type='way' ref='68244365' role='' />\n    <member type='way' ref='46739384' role='' />\n    <member type='way' ref='531462062' role='' />\n    <member type='way' ref='236855738' role='' />\n    <member type='way' ref='779254719' role='' />\n    <member type='way' ref='779254717' role='' />\n    <member type='way' ref='779254718' role='' />\n    <member type='way' ref='779254713' role='' />\n    <member type='way' ref='779254721' role='' />\n    <member type='way' ref='779254722' role='' />\n    <member type='way' ref='601394137' role='' />\n    <member type='way' ref='144053949' role='' />\n    <member type='way' ref='274513688' role='' />\n    <member type='way' ref='274513690' role='' />\n    <member type='way' ref='274513686' role='' />\n    <member type='way' ref='274513681' role='' />\n    <member type='way' ref='48178462' role='' />\n    <member type='way' ref='5012656' role='' />\n    <member type='way' ref='37352363' role='' />\n    <member type='way' ref='47796988' role='' />\n    <member type='way' ref='144053946' role='' />\n    <member type='way' ref='153203961' role='' />\n    <member type='way' ref='274608055' role='' />\n    <member type='way' ref='19714183' role='' />\n    <member type='way' ref='69288832' role='' />\n    <member type='way' ref='235018086' role='' />\n    <member type='way' ref='34614766' role='' />\n    <member type='way' ref='339327768' role='' />\n    <member type='way' ref='122140902' role='' />\n    <member type='way' ref='122140901' role='' />\n    <member type='way' ref='122140903' role='' />\n    <member type='way' ref='339945820' role='' />\n    <member type='way' ref='22418939' role='' />\n    <member type='way' ref='22418929' role='' />\n    <member type='way' ref='274498579' role='' />\n    <member type='way' ref='27715970' role='' />\n    <member type='way' ref='22819099' role='' />\n    <member type='way' ref='26832683' role='' />\n    <member type='way' ref='22819098' role='' />\n    <member type='way' ref='274498578' role='' />\n    <member type='way' ref='274498577' role='' />\n    <member type='way' ref='485851171' role='' />\n    <member type='way' ref='485851175' role='' />\n    <member type='way' ref='274498580' role='' />\n    <member type='way' ref='485851178' role='' />\n    <member type='way' ref='485851176' role='' />\n    <member type='way' ref='31978382' role='' />\n    <member type='way' ref='31978383' role='' />\n    <member type='way' ref='26821947' role='' />\n    <member type='way' ref='26821946' role='' />\n    <member type='way' ref='32454975' role='' />\n    <member type='way' ref='32454976' role='' />\n    <member type='way' ref='240745991' role='' />\n    <member type='way' ref='5078262' role='' />\n    <member type='way' ref='240745990' role='' />\n    <member type='way' ref='240745989' role='' />\n    <member type='way' ref='240745995' role='' />\n    <member type='way' ref='153203959' role='' />\n    <member type='way' ref='153203962' role='' />\n    <member type='way' ref='142699272' role='' />\n    <member type='way' ref='531547514' role='' />\n    <member type='way' ref='466040279' role='' />\n    <member type='way' ref='233077311' role='' />\n    <member type='way' ref='46742311' role='' />\n    <member type='way' ref='233077309' role='' />\n    <member type='way' ref='233077310' role='' />\n    <member type='way' ref='233077317' role='' />\n    <member type='way' ref='32337799' role='' />\n    <member type='way' ref='32337800' role='' />\n    <member type='way' ref='59572345' role='' />\n    <member type='way' ref='690686298' role='' />\n    <member type='way' ref='233077312' role='' />\n    <member type='way' ref='233077316' role='' />\n    <member type='way' ref='639784304' role='' />\n    <member type='way' ref='233077314' role='' />\n    <member type='way' ref='655183564' role='' />\n    <member type='way' ref='233077315' role='' />\n    <member type='way' ref='32255543' role='' />\n    <member type='way' ref='32255544' role='' />\n    <member type='way' ref='233078181' role='' />\n    <member type='way' ref='233078182' role='' />\n    <member type='way' ref='46742305' role='' />\n    <member type='way' ref='316452178' role='' />\n    <member type='way' ref='605712116' role='' />\n    <member type='way' ref='690686296' role='' />\n    <member type='way' ref='30527960' role='' />\n    <member type='way' ref='30527961' role='' />\n    <member type='way' ref='30527964' role='' />\n    <member type='way' ref='30527965' role='' />\n    <member type='way' ref='690686295' role='' />\n    <member type='way' ref='4993377' role='' />\n    <member type='way' ref='60660651' role='' />\n    <member type='way' ref='60660644' role='' />\n    <member type='way' ref='60660648' role='' />\n    <member type='way' ref='4993375' role='' />\n    <member type='way' ref='473334708' role='' />\n    <member type='way' ref='655183563' role='' />\n    <member type='way' ref='376872512' role='' />\n    <member type='way' ref='744330328' role='' />\n    <member type='way' ref='744330329' role='' />\n    <member type='way' ref='690566790' role='' />\n    <member type='way' ref='690566789' role='' />\n    <member type='way' ref='473334704' role='' />\n    <member type='way' ref='233079511' role='' />\n    <member type='way' ref='462382491' role='' />\n    <member type='way' ref='744330331' role='' />\n    <member type='way' ref='744330330' role='' />\n    <member type='way' ref='527281500' role='' />\n    <member type='way' ref='462382489' role='' />\n    <member type='way' ref='462382490' role='' />\n    <member type='way' ref='462382390' role='' />\n    <member type='way' ref='462382469' role='' />\n    <member type='way' ref='83174291' role='' />\n    <member type='way' ref='83174289' role='' />\n    <member type='way' ref='51435392' role='' />\n    <member type='way' ref='51434522' role='' />\n    <member type='way' ref='473334700' role='' />\n    <member type='way' ref='233081271' role='' />\n    <member type='way' ref='473334696' role='' />\n    <member type='way' ref='233081266' role='' />\n    <member type='way' ref='690558802' role='' />\n    <member type='way' ref='690558805' role='' />\n    <member type='way' ref='34881622' role='' />\n    <member type='way' ref='34881623' role='' />\n    <member type='way' ref='35723980' role='' />\n    <member type='way' ref='233082562' role='' />\n    <member type='way' ref='473334693' role='' />\n    <member type='way' ref='80444588' role='' />\n    <member type='way' ref='473334690' role='' />\n    <member type='way' ref='544212748' role='' />\n    <member type='way' ref='473334676' role='' />\n    <member type='way' ref='473334669' role='' />\n    <member type='way' ref='473334666' role='' />\n    <member type='way' ref='473334680' role='' />\n    <member type='way' ref='690686291' role='' />\n    <member type='way' ref='690686292' role='' />\n    <member type='way' ref='473334665' role='' />\n    <member type='way' ref='233083910' role='' />\n    <member type='way' ref='473334664' role='' />\n    <member type='way' ref='603491077' role='' />\n    <member type='way' ref='690686290' role='' />\n    <member type='way' ref='10711346' role='' />\n    <member type='way' ref='118295285' role='' />\n    <member type='way' ref='10711344' role='' />\n    <member type='way' ref='690918955' role='' />\n    <member type='way' ref='10711345' role='' />\n    <member type='way' ref='473334663' role='' />\n    <member type='way' ref='324251361' role='' />\n    <member type='way' ref='80444587' role='' />\n    <member type='way' ref='473334662' role='' />\n    <member type='way' ref='527281495' role='' />\n    <member type='way' ref='540939259' role='' />\n    <member type='way' ref='34379686' role='' />\n    <member type='way' ref='527281490' role='' />\n    <member type='way' ref='760878206' role='' />\n    <member type='way' ref='760878201' role='' />\n    <member type='way' ref='760878205' role='' />\n    <member type='way' ref='683695048' role='' />\n    <member type='way' ref='683695050' role='' />\n    <member type='way' ref='683695049' role='' />\n    <member type='way' ref='655183558' role='' />\n    <member type='way' ref='655183559' role='' />\n    <member type='way' ref='690686289' role='' />\n    <member type='way' ref='46742299' role='' />\n    <member type='way' ref='760878207' role='' />\n    <member type='way' ref='842805037' role='' />\n    <member type='way' ref='682144625' role='' />\n    <member type='way' ref='10713027' role='' />\n    <member type='way' ref='127872829' role='' />\n    <member type='way' ref='842805036' role='' />\n    <member type='way' ref='323606074' role='' />\n    <member type='way' ref='10713261' role='' />\n    <member type='way' ref='10713263' role='' />\n    <member type='way' ref='10713257' role='' />\n    <member type='way' ref='491131649' role='' />\n    <member type='way' ref='103948929' role='' />\n    <member type='way' ref='10223277' role='' />\n    <member type='way' ref='10713252' role='' />\n    <member type='way' ref='282521828' role='' />\n    <member type='way' ref='23299741' role='' />\n    <member type='way' ref='674053454' role='' />\n    <member type='way' ref='23299766' role='' />\n    <member type='way' ref='149136685' role='' />\n    <member type='way' ref='149136683' role='' />\n    <member type='way' ref='30267570' role='' />\n    <member type='way' ref='23299758' role='' />\n    <member type='way' ref='674053447' role='' />\n    <member type='way' ref='674053446' role='' />\n    <member type='way' ref='674053445' role='' />\n    <member type='way' ref='674053444' role='' />\n    <member type='way' ref='674053438' role='' />\n    <member type='way' ref='674053434' role='' />\n    <member type='way' ref='674053435' role='' />\n    <member type='way' ref='674053437' role='' />\n    <member type='way' ref='146851607' role='' />\n    <member type='way' ref='605872188' role='' />\n    <member type='way' ref='605872189' role='' />\n    <member type='way' ref='153842980' role='' />\n    <member type='way' ref='605872193' role='' />\n    <member type='way' ref='24520258' role='' />\n    <member type='way' ref='30339569' role='' />\n    <member type='way' ref='42412075' role='' />\n    <member type='way' ref='30339300' role='' />\n    <member type='way' ref='24520259' role='' />\n    <member type='way' ref='30434903' role='' />\n    <member type='way' ref='605866509' role='' />\n    <member type='way' ref='605866510' role='' />\n    <member type='way' ref='605866511' role='' />\n    <member type='way' ref='220515422' role='' />\n    <member type='way' ref='220515419' role='' />\n    <member type='way' ref='220515420' role='' />\n    <member type='way' ref='220515421' role='' />\n    <member type='way' ref='605872183' role='' />\n    <member type='way' ref='586235751' role='' />\n    <member type='way' ref='650779323' role='' />\n    <member type='way' ref='650779325' role='' />\n    <member type='way' ref='650779317' role='' />\n    <member type='way' ref='650779316' role='' />\n    <member type='way' ref='650779310' role='' />\n    <member type='way' ref='650779309' role='' />\n    <member type='way' ref='650779285' role='' />\n    <member type='way' ref='650779284' role='' />\n    <member type='way' ref='650779283' role='' />\n    <member type='way' ref='650779315' role='' />\n    <member type='way' ref='650779279' role='' />\n    <member type='way' ref='144246643' role='' />\n    <member type='way' ref='230609911' role='' />\n    <member type='way' ref='27432671' role='' />\n    <member type='way' ref='347903359' role='' />\n    <member type='way' ref='128280053' role='' />\n    <member type='way' ref='490926531' role='' />\n    <member type='way' ref='234008021' role='' />\n    <member type='way' ref='142709191' role='' />\n    <member type='way' ref='261927201' role='' />\n    <member type='way' ref='309095414' role='' />\n    <member type='way' ref='51255671' role='' />\n    <member type='way' ref='682409935' role='' />\n    <member type='way' ref='682409942' role='' />\n    <member type='way' ref='682409938' role='' />\n    <member type='way' ref='142762613' role='' />\n    <member type='way' ref='218907472' role='' />\n    <member type='way' ref='42878786' role='' />\n    <member type='way' ref='26812424' role='' />\n    <member type='way' ref='391821262' role='' />\n    <member type='way' ref='50390278' role='' />\n    <member type='way' ref='391821253' role='' />\n    <member type='way' ref='236098423' role='' />\n    <member type='way' ref='137154559' role='' />\n    <member type='way' ref='27119372' role='' />\n    <member type='way' ref='114929182' role='' />\n    <member type='way' ref='114929184' role='' />\n    <member type='way' ref='137154550' role='' />\n    <member type='way' ref='82312392' role='' />\n    <member type='way' ref='558852864' role='' />\n    <member type='way' ref='391821209' role='' />\n    <member type='way' ref='391821220' role='' />\n    <member type='way' ref='391821212' role='' />\n    <member type='way' ref='391821216' role='' />\n    <member type='way' ref='391821219' role='' />\n    <member type='way' ref='391821202' role='' />\n    <member type='way' ref='391801520' role='' />\n    <member type='way' ref='391801514' role='' />\n    <member type='way' ref='31821587' role='' />\n    <member type='way' ref='495849382' role='' />\n    <member type='way' ref='391801516' role='' />\n    <member type='way' ref='391801513' role='' />\n    <member type='way' ref='391801510' role='' />\n    <member type='way' ref='49056723' role='' />\n    <member type='way' ref='521045512' role='' />\n    <member type='way' ref='391787295' role='' />\n    <member type='way' ref='391787293' role='' />\n    <member type='way' ref='391787290' role='' />\n    <member type='way' ref='391787287' role='' />\n    <member type='way' ref='39088985' role='' />\n    <member type='way' ref='39097668' role='' />\n    <member type='way' ref='381750332' role='' />\n    <member type='way' ref='26621012' role='' />\n    <member type='way' ref='521045514' role='' />\n    <member type='way' ref='24483185' role='' />\n    <member type='way' ref='521045513' role='' />\n    <member type='way' ref='381750330' role='' />\n    <member type='way' ref='44532531' role='' />\n    <member type='way' ref='199780796' role='' />\n    <member type='way' ref='141050972' role='' />\n    <member type='way' ref='381750336' role='' />\n    <member type='way' ref='51196062' role='' />\n    <member type='way' ref='199239120' role='' />\n    <member type='way' ref='199753406' role='' />\n    <member type='way' ref='142770547' role='' />\n    <member type='way' ref='142692918' role='' />\n    <member type='way' ref='199752605' role='' />\n    <member type='way' ref='199752604' role='' />\n    <member type='way' ref='199239119' role='' />\n    <member type='way' ref='41676176' role='' />\n    <member type='way' ref='158491027' role='' />\n    <member type='way' ref='158491026' role='' />\n    <member type='way' ref='186713411' role='' />\n    <member type='way' ref='186713406' role='' />\n    <member type='way' ref='551303414' role='' />\n    <member type='way' ref='186713410' role='' />\n    <member type='way' ref='186713407' role='' />\n    <member type='way' ref='51200768' role='' />\n    <member type='way' ref='551303417' role='' />\n    <member type='way' ref='186722706' role='' />\n    <member type='way' ref='551303416' role='' />\n    <member type='way' ref='551303423' role='' />\n    <member type='way' ref='551303424' role='' />\n    <member type='way' ref='189775480' role='' />\n    <member type='way' ref='186821034' role='' />\n    <member type='way' ref='186821031' role='' />\n    <member type='way' ref='186821036' role='' />\n    <member type='way' ref='142944477' role='' />\n    <member type='way' ref='187761803' role='' />\n    <member type='way' ref='591214268' role='' />\n    <member type='way' ref='142944471' role='' />\n    <member type='way' ref='450274068' role='' />\n    <member type='way' ref='31009399' role='' />\n    <member type='way' ref='187761801' role='' />\n    <member type='way' ref='572210301' role='' />\n    <member type='way' ref='142944469' role='' />\n    <member type='way' ref='33041278' role='' />\n    <member type='way' ref='143586365' role='' />\n    <member type='way' ref='27805056' role='' />\n    <member type='way' ref='39981139' role='' />\n    <member type='way' ref='528961136' role='' />\n    <member type='way' ref='528961133' role='' />\n    <member type='way' ref='23782998' role='' />\n    <member type='way' ref='640955497' role='' />\n    <member type='way' ref='659046263' role='' />\n    <member type='way' ref='647444124' role='' />\n    <member type='way' ref='659046260' role='' />\n    <member type='way' ref='659046262' role='' />\n    <member type='way' ref='282898412' role='' />\n    <member type='way' ref='282898412' role='' />\n    <member type='way' ref='659046261' role='' />\n    <member type='way' ref='680624628' role='' />\n    <member type='way' ref='282898404' role='' />\n    <member type='way' ref='282898404' role='' />\n    <member type='way' ref='680624629' role='' />\n    <member type='way' ref='659046255' role='' />\n    <member type='way' ref='282898402' role='' />\n    <member type='way' ref='282898402' role='' />\n    <member type='way' ref='659046259' role='' />\n    <member type='way' ref='282898406' role='' />\n    <member type='way' ref='282898406' role='' />\n    <member type='way' ref='685314679' role='' />\n    <member type='way' ref='282898405' role='' />\n    <member type='way' ref='282898405' role='' />\n    <member type='way' ref='28724405' role='' />\n    <member type='way' ref='282898410' role='' />\n    <member type='way' ref='282898410' role='' />\n    <member type='way' ref='685314678' role='' />\n    <member type='way' ref='659046254' role='' />\n    <member type='way' ref='282898403' role='' />\n    <member type='way' ref='282898403' role='' />\n    <member type='way' ref='659046257' role='' />\n    <member type='way' ref='659046258' role='' />\n    <member type='way' ref='659046253' role='' />\n    <member type='way' ref='659046256' role='' />\n    <member type='way' ref='86572979' role='' />\n    <member type='way' ref='39981133' role='' />\n    <member type='way' ref='39981134' role='' />\n    <member type='way' ref='27805056' role='' />\n    <member type='way' ref='39981139' role='' />\n    <member type='way' ref='528961136' role='' />\n    <member type='way' ref='528961133' role='' />\n    <member type='way' ref='215080032' role='' />\n    <member type='way' ref='147936498' role='' />\n    <member type='way' ref='187735759' role='' />\n    <member type='way' ref='39981132' role='' />\n    <member type='way' ref='640955503' role='' />\n    <member type='way' ref='147936499' role='' />\n    <member type='way' ref='147936508' role='' />\n    <member type='way' ref='40273555' role='' />\n    <member type='way' ref='528961157' role='' />\n    <member type='way' ref='531387877' role='' />\n    <member type='way' ref='40273559' role='' />\n    <member type='way' ref='528961154' role='' />\n    <member type='way' ref='49618808' role='' />\n    <member type='way' ref='143586237' role='' />\n    <member type='way' ref='143586244' role='' />\n    <member type='way' ref='528961140' role='' />\n    <member type='way' ref='528961139' role='' />\n    <member type='way' ref='147936459' role='' />\n    <member type='way' ref='147936456' role='' />\n    <member type='way' ref='528961143' role='' />\n    <member type='way' ref='49618903' role='' />\n    <member type='way' ref='528961144' role='' />\n    <member type='way' ref='27805061' role='' />\n    <member type='way' ref='60417879' role='' />\n    <member type='way' ref='528961141' role='' />\n    <member type='way' ref='60417898' role='' />\n    <member type='way' ref='143586249' role='' />\n    <member type='way' ref='48657278' role='' />\n    <member type='way' ref='168802241' role='' />\n    <member type='way' ref='243547281' role='' />\n    <member type='way' ref='149817544' role='' />\n    <member type='way' ref='243547282' role='' />\n    <member type='way' ref='45748889' role='' />\n    <member type='way' ref='147756935' role='' />\n    <member type='way' ref='147756932' role='' />\n    <member type='way' ref='74412390' role='' />\n    <member type='way' ref='147756936' role='' />\n    <member type='way' ref='39604245' role='' />\n    <member type='way' ref='39604246' role='' />\n    <member type='way' ref='653717352' role='' />\n    <member type='way' ref='4424824' role='' />\n    <member type='way' ref='185792954' role='' />\n    <member type='way' ref='147925618' role='' />\n    <member type='way' ref='39604247' role='' />\n    <member type='way' ref='74412373' role='' />\n    <member type='way' ref='473534138' role='' />\n    <member type='way' ref='39576032' role='' />\n    <member type='way' ref='30850216' role='' />\n    <member type='way' ref='158791681' role='' />\n    <member type='way' ref='458542853' role='' />\n    <member type='way' ref='458542849' role='' />\n    <member type='way' ref='458542848' role='' />\n    <member type='way' ref='79271337' role='' />\n    <member type='way' ref='27837751' role='' />\n    <member type='way' ref='27837752' role='' />\n    <member type='way' ref='397239860' role='' />\n    <member type='way' ref='85634894' role='' />\n    <member type='way' ref='553833946' role='' />\n    <member type='way' ref='85634896' role='' />\n    <member type='way' ref='409274507' role='' />\n    <member type='way' ref='4038463' role='' />\n    <member type='way' ref='4038466' role='' />\n    <member type='way' ref='362888813' role='' />\n    <member type='way' ref='30418544' role='' />\n    <member type='way' ref='316907869' role='' />\n    <member type='way' ref='10626370' role='' />\n    <member type='way' ref='10626371' role='' />\n    <member type='way' ref='4040392' role='' />\n    <member type='way' ref='316907871' role='' />\n    <member type='way' ref='456785737' role='' />\n    <member type='way' ref='30418545' role='' />\n    <member type='way' ref='653751826' role='' />\n    <member type='way' ref='318717778' role='' />\n    <member type='way' ref='4250072' role='' />\n    <member type='way' ref='4250073' role='' />\n    <member type='way' ref='4040390' role='' />\n    <member type='way' ref='319031787' role='' />\n    <member type='way' ref='85692755' role='' />\n    <member type='way' ref='319031789' role='' />\n    <member type='way' ref='319031785' role='' />\n    <member type='way' ref='4040393' role='' />\n    <member type='way' ref='319031784' role='' />\n    <member type='way' ref='4040396' role='' />\n    <member type='way' ref='324477929' role='' />\n    <member type='way' ref='324477931' role='' />\n    <member type='way' ref='324477921' role='' />\n    <member type='way' ref='257591438' role='' />\n    <member type='way' ref='10780634' role='' />\n    <member type='way' ref='10780635' role='' />\n    <member type='way' ref='4040398' role='' />\n    <member type='way' ref='844897621' role='' />\n    <member type='way' ref='154669111' role='' />\n    <member type='way' ref='154669107' role='' />\n    <member type='way' ref='844897622' role='' />\n    <member type='way' ref='324477919' role='' />\n    <member type='way' ref='324477922' role='' />\n    <member type='way' ref='324477930' role='' />\n    <member type='way' ref='324477923' role='' />\n    <member type='way' ref='178612389' role='' />\n    <member type='way' ref='242250054' role='' />\n    <member type='way' ref='258166152' role='' />\n    <member type='way' ref='258698480' role='' />\n    <member type='way' ref='178609587' role='' />\n    <member type='way' ref='106920789' role='' />\n    <member type='way' ref='324319633' role='' />\n    <member type='way' ref='324319637' role='' />\n    <member type='way' ref='324319621' role='' />\n    <member type='way' ref='324319662' role='' />\n    <member type='way' ref='106920794' role='' />\n    <member type='way' ref='242250055' role='' />\n    <member type='way' ref='297017920' role='' />\n    <member type='way' ref='297017918' role='' />\n    <member type='way' ref='281702455' role='' />\n    <member type='way' ref='360925562' role='' />\n    <member type='way' ref='481246559' role='' />\n    <member type='way' ref='297019621' role='' />\n    <member type='way' ref='86150810' role='' />\n    <member type='way' ref='297019623' role='' />\n    <member type='way' ref='297019620' role='' />\n    <member type='way' ref='162361366' role='' />\n    <member type='way' ref='236309393' role='' />\n    <member type='way' ref='481246562' role='' />\n    <member type='way' ref='162361367' role='' />\n    <member type='way' ref='845382865' role='' />\n    <member type='way' ref='845382866' role='' />\n    <member type='way' ref='845382869' role='' />\n    <member type='way' ref='845382868' role='' />\n    <member type='way' ref='324319650' role='' />\n    <member type='way' ref='324319610' role='' />\n    <member type='way' ref='324319648' role='' />\n    <member type='way' ref='324319675' role='' />\n    <member type='way' ref='43070129' role='' />\n    <member type='way' ref='43070130' role='' />\n    <member type='way' ref='38330801' role='' />\n    <member type='way' ref='38330800' role='' />\n    <member type='way' ref='38330799' role='' />\n    <member type='way' ref='38330798' role='' />\n    <member type='way' ref='43070718' role='' />\n    <member type='way' ref='43070719' role='' />\n    <member type='way' ref='43070720' role='' />\n    <member type='way' ref='43070721' role='' />\n    <member type='way' ref='324319629' role='' />\n    <member type='way' ref='324319666' role='' />\n    <member type='way' ref='324319640' role='' />\n    <member type='way' ref='26197922' role='' />\n    <member type='way' ref='154912055' role='' />\n    <member type='way' ref='324319679' role='' />\n    <member type='way' ref='44572966' role='' />\n    <member type='way' ref='44572965' role='' />\n    <member type='way' ref='390575953' role='' />\n    <member type='way' ref='845391390' role='' />\n    <member type='way' ref='845391387' role='' />\n    <member type='way' ref='390575947' role='' />\n    <member type='way' ref='390575952' role='' />\n    <member type='way' ref='390575949' role='' />\n    <member type='way' ref='162361364' role='' />\n    <member type='way' ref='121552399' role='' />\n    <member type='way' ref='121552400' role='' />\n    <member type='way' ref='327302628' role='' />\n    <member type='way' ref='327302626' role='' />\n    <member type='way' ref='327302630' role='' />\n    <member type='way' ref='327302631' role='' />\n    <member type='way' ref='327302627' role='' />\n    <member type='way' ref='327302629' role='' />\n    <member type='way' ref='322698054' role='' />\n    <member type='way' ref='322698060' role='' />\n    <member type='way' ref='322698053' role='' />\n    <member type='way' ref='322698067' role='' />\n    <member type='way' ref='269818132' role='' />\n    <member type='way' ref='269818133' role='' />\n    <member type='way' ref='390449823' role='' />\n    <member type='way' ref='390449818' role='' />\n    <member type='way' ref='269819019' role='' />\n    <member type='way' ref='269819018' role='' />\n    <member type='way' ref='62358597' role='' />\n    <member type='way' ref='154661294' role='' />\n    <member type='way' ref='322857220' role='' />\n    <member type='way' ref='322857226' role='' />\n    <member type='way' ref='24410532' role='' />\n    <member type='way' ref='24410530' role='' />\n    <member type='way' ref='322857233' role='' />\n    <member type='way' ref='255729003' role='' />\n    <member type='way' ref='797348092' role='' />\n    <member type='way' ref='797348090' role='' />\n    <member type='way' ref='797348091' role='' />\n    <member type='way' ref='797348083' role='' />\n    <member type='way' ref='797348082' role='' />\n    <member type='way' ref='797348081' role='' />\n    <member type='way' ref='255729008' role='' />\n    <member type='way' ref='322698059' role='' />\n    <member type='way' ref='322698064' role='' />\n    <member type='way' ref='322698052' role='' />\n    <member type='way' ref='322698065' role='' />\n    <member type='way' ref='61722503' role='' />\n    <member type='way' ref='390449805' role='' />\n    <member type='way' ref='390449808' role='' />\n    <member type='way' ref='390449830' role='' />\n    <member type='way' ref='390449816' role='' />\n    <member type='way' ref='154669113' role='' />\n    <member type='way' ref='61722541' role='' />\n    <member type='way' ref='152654001' role='' />\n    <member type='way' ref='360925569' role='' />\n    <member type='way' ref='360925558' role='' />\n    <member type='way' ref='297016981' role='' />\n    <member type='way' ref='218983253' role='' />\n    <member type='way' ref='297016986' role='' />\n    <member type='way' ref='297016978' role='' />\n    <member type='way' ref='781502527' role='' />\n    <member type='way' ref='235016457' role='' />\n    <member type='way' ref='49688860' role='' />\n    <member type='way' ref='360925572' role='' />\n    <member type='way' ref='44572261' role='' />\n    <member type='way' ref='92801059' role='' />\n    <member type='way' ref='92801062' role='' />\n    <member type='way' ref='44572260' role='' />\n    <member type='way' ref='4040454' role='' />\n    <member type='way' ref='61722560' role='' />\n    <member type='way' ref='152653167' role='' />\n    <member type='way' ref='119049036' role='' />\n    <member type='way' ref='119049029' role='' />\n    <member type='way' ref='322857219' role='' />\n    <member type='way' ref='638390304' role='' />\n    <member type='way' ref='162152566' role='' />\n    <member type='way' ref='162152568' role='' />\n    <member type='way' ref='322857218' role='' />\n    <member type='way' ref='4040456' role='' />\n    <member type='way' ref='233501317' role='' />\n    <member type='way' ref='390449832' role='' />\n    <member type='way' ref='390449822' role='' />\n    <member type='way' ref='390449827' role='' />\n    <member type='way' ref='390449828' role='' />\n    <member type='way' ref='233501320' role='' />\n    <member type='way' ref='390449809' role='' />\n    <member type='way' ref='390449807' role='' />\n    <member type='way' ref='390449831' role='' />\n    <member type='way' ref='390449819' role='' />\n    <member type='way' ref='4040458' role='' />\n    <member type='way' ref='322698063' role='' />\n    <member type='way' ref='796931735' role='' />\n    <member type='way' ref='4040462' role='' />\n    <member type='way' ref='796931733' role='' />\n    <member type='way' ref='322698055' role='' />\n    <member type='way' ref='322698045' role='' />\n    <member type='way' ref='79658133' role='' />\n    <member type='way' ref='79658135' role='' />\n    <member type='way' ref='10559721' role='' />\n    <member type='way' ref='10559722' role='' />\n    <member type='way' ref='10563472' role='' />\n    <member type='way' ref='346629135' role='' />\n    <member type='way' ref='346629136' role='' />\n    <member type='way' ref='322481051' role='' />\n    <member type='way' ref='322481044' role='' />\n    <member type='way' ref='322481041' role='' />\n    <member type='way' ref='322481058' role='' />\n    <member type='way' ref='390423484' role='' />\n    <member type='way' ref='390423483' role='' />\n    <member type='way' ref='390423487' role='' />\n    <member type='way' ref='390423486' role='' />\n    <member type='way' ref='5205017' role='' />\n    <member type='way' ref='5205018' role='' />\n    <member type='way' ref='322481043' role='' />\n    <member type='way' ref='322481057' role='' />\n    <member type='way' ref='4040468' role='' />\n    <member type='way' ref='322481046' role='' />\n    <member type='way' ref='322481038' role='' />\n    <member type='way' ref='390589612' role='' />\n    <member type='way' ref='390589610' role='' />\n    <member type='way' ref='390589611' role='' />\n    <member type='way' ref='390589618' role='' />\n    <member type='way' ref='4040470' role='' />\n    <member type='way' ref='44535959' role='' />\n    <member type='way' ref='44535961' role='' />\n    <member type='way' ref='265465916' role='' />\n    <member type='way' ref='4040472' role='' />\n    <member type='way' ref='265465918' role='' />\n    <member type='way' ref='322481061' role='' />\n    <member type='way' ref='152476234' role='' />\n    <member type='way' ref='152476239' role='' />\n    <member type='way' ref='390589621' role='' />\n    <member type='way' ref='390589622' role='' />\n    <member type='way' ref='4040476' role='' />\n    <member type='way' ref='651630807' role='' />\n    <member type='way' ref='4040478' role='' />\n    <member type='way' ref='4040481' role='' />\n    <member type='way' ref='22662171' role='' />\n    <member type='way' ref='22662172' role='' />\n    <member type='way' ref='22662165' role='' />\n    <member type='way' ref='22662166' role='' />\n    <member type='way' ref='243153758' role='' />\n    <member type='way' ref='95458110' role='' />\n    <member type='way' ref='95458107' role='' />\n    <member type='way' ref='4040488' role='' />\n    <member type='way' ref='243153768' role='' />\n    <member type='way' ref='4040489' role='' />\n    <member type='way' ref='4040519' role='' />\n    <member type='way' ref='4040517' role='' />\n    <member type='way' ref='265480015' role='' />\n    <member type='way' ref='265480016' role='' />\n    <member type='way' ref='265480004' role='' />\n    <member type='way' ref='4040514' role='' />\n    <member type='way' ref='285957494' role='' />\n    <member type='way' ref='285957497' role='' />\n    <member type='way' ref='285957492' role='' />\n    <member type='way' ref='285957482' role='' />\n    <member type='way' ref='44492473' role='' />\n    <member type='way' ref='44492472' role='' />\n    <member type='way' ref='337758889' role='' />\n    <member type='way' ref='337758888' role='' />\n    <member type='way' ref='285957486' role='' />\n    <member type='way' ref='285957489' role='' />\n    <member type='way' ref='265483624' role='' />\n    <member type='way' ref='265483627' role='' />\n    <member type='way' ref='265483626' role='' />\n    <member type='way' ref='265483625' role='' />\n    <member type='way' ref='4040506' role='' />\n    <member type='way' ref='337761549' role='' />\n    <member type='way' ref='337761548' role='' />\n    <member type='way' ref='337761547' role='' />\n    <member type='way' ref='59559318' role='' />\n    <member type='way' ref='4040502' role='' />\n    <member type='way' ref='337762619' role='' />\n    <member type='way' ref='337762620' role='' />\n    <member type='way' ref='337762626' role='' />\n    <member type='way' ref='337762627' role='' />\n    <member type='way' ref='322481045' role='' />\n    <member type='way' ref='322481039' role='' />\n    <member type='way' ref='337763008' role='' />\n    <member type='way' ref='337763007' role='' />\n    <member type='way' ref='337763006' role='' />\n    <member type='way' ref='337763005' role='' />\n    <member type='way' ref='337767917' role='' />\n    <member type='way' ref='293914580' role='' />\n    <member type='way' ref='322513565' role='' />\n    <member type='way' ref='337767918' role='' />\n    <member type='way' ref='266596651' role='' />\n    <member type='way' ref='527002067' role='' />\n    <member type='way' ref='527002066' role='' />\n    <member type='way' ref='266602629' role='' />\n    <member type='way' ref='27447095' role='' />\n    <member type='way' ref='158189599' role='' />\n    <member type='way' ref='101333243' role='' />\n    <member type='way' ref='266602642' role='' />\n    <member type='way' ref='337770852' role='' />\n    <member type='way' ref='337770853' role='' />\n    <member type='way' ref='337770854' role='' />\n    <member type='way' ref='337770855' role='' />\n    <member type='way' ref='337772421' role='' />\n    <member type='way' ref='337772422' role='' />\n    <member type='way' ref='337772426' role='' />\n    <member type='way' ref='337772425' role='' />\n    <member type='way' ref='337773088' role='' />\n    <member type='way' ref='337773087' role='' />\n    <member type='way' ref='337773089' role='' />\n    <member type='way' ref='337773090' role='' />\n    <member type='way' ref='340203150' role='' />\n    <member type='way' ref='266602636' role='' />\n    <member type='way' ref='409280767' role='' />\n    <member type='way' ref='170046075' role='' />\n    <member type='way' ref='409280766' role='' />\n    <member type='way' ref='337775289' role='' />\n    <member type='way' ref='409284679' role='' />\n    <member type='way' ref='310620435' role='' />\n    <member type='way' ref='25590030' role='' />\n    <member type='way' ref='551208457' role='' />\n    <member type='way' ref='264372707' role='' />\n    <member type='way' ref='355143737' role='' />\n    <member type='way' ref='355143738' role='' />\n    <member type='way' ref='340203151' role='' />\n    <member type='way' ref='850491476' role='' />\n    <member type='way' ref='262943265' role='' />\n    <member type='way' ref='700567685' role='' />\n    <member type='way' ref='26625517' role='' />\n    <member type='way' ref='262943264' role='' />\n    <member type='way' ref='700574758' role='' />\n    <member type='way' ref='262943268' role='' />\n    <member type='way' ref='4481908' role='' />\n    <member type='way' ref='4569838' role='' />\n    <member type='way' ref='4569839' role='' />\n    <member type='way' ref='4416215' role='' />\n    <member type='way' ref='152156383' role='' />\n    <member type='way' ref='515878961' role='' />\n    <member type='way' ref='152156396' role='' />\n    <member type='way' ref='262946944' role='' />\n    <member type='way' ref='138743866' role='' />\n    <member type='way' ref='361040280' role='' />\n    <member type='way' ref='262946934' role='' />\n    <member type='way' ref='5043517' role='' />\n    <member type='way' ref='189695819' role='' />\n    <member type='way' ref='285960028' role='' />\n    <member type='way' ref='285960030' role='' />\n    <member type='way' ref='285960024' role='' />\n    <member type='way' ref='285960026' role='' />\n    <member type='way' ref='5043519' role='' />\n    <member type='way' ref='5043520' role='' />\n    <member type='way' ref='5043518' role='' />\n    <member type='way' ref='4538105' role='' />\n    <member type='way' ref='309842382' role='' />\n    <member type='way' ref='309842388' role='' />\n    <member type='way' ref='390589599' role='' />\n    <member type='way' ref='390589601' role='' />\n    <member type='way' ref='390589605' role='' />\n    <member type='way' ref='390589597' role='' />\n    <member type='way' ref='390589596' role='' />\n    <member type='way' ref='27160905' role='' />\n    <member type='way' ref='26170264' role='' />\n    <member type='way' ref='4538106' role='' />\n    <member type='way' ref='4067899' role='' />\n    <member type='way' ref='628831857' role='' />\n    <member type='way' ref='628831856' role='' />\n    <member type='way' ref='18783893' role='' />\n    <member type='way' ref='18783896' role='' />\n    <member type='way' ref='18786055' role='' />\n    <member type='way' ref='18786056' role='' />\n    <member type='way' ref='4706421' role='' />\n    <member type='way' ref='4706420' role='' />\n    <member type='way' ref='215577410' role='' />\n    <member type='way' ref='215577412' role='' />\n    <member type='way' ref='26168692' role='' />\n    <member type='way' ref='4540099' role='' />\n    <member type='way' ref='4538155' role='' />\n    <member type='way' ref='215577401' role='' />\n    <member type='way' ref='4540084' role='' />\n    <member type='way' ref='215577424' role='' />\n    <member type='way' ref='4540086' role='' />\n    <member type='way' ref='4538156' role='' />\n    <member type='way' ref='4538141' role='' />\n    <member type='way' ref='215577413' role='' />\n    <member type='way' ref='215577405' role='' />\n    <member type='way' ref='134807063' role='' />\n    <member type='way' ref='4538142' role='' />\n    <member type='way' ref='4538108' role='' />\n    <member type='way' ref='215577402' role='' />\n    <member type='way' ref='25895572' role='' />\n    <member type='way' ref='25895573' role='' />\n    <member type='way' ref='25776268' role='' />\n    <member type='way' ref='25776269' role='' />\n    <member type='way' ref='4538109' role='' />\n    <member type='way' ref='215668779' role='' />\n    <member type='way' ref='215668774' role='' />\n    <member type='way' ref='215668775' role='' />\n    <member type='way' ref='4537040' role='' />\n    <member type='way' ref='231121695' role='' />\n    <member type='way' ref='4537041' role='' />\n    <member type='way' ref='210926363' role='' />\n    <member type='way' ref='24599308' role='' />\n    <member type='way' ref='4484056' role='' />\n    <member type='way' ref='698801383' role='' />\n    <member type='way' ref='4396831' role='' />\n    <member type='way' ref='28658009' role='' />\n    <member type='way' ref='28658024' role='' />\n    <member type='way' ref='334018084' role='' />\n    <member type='way' ref='183596326' role='' />\n    <member type='way' ref='4484030' role='' />\n    <member type='way' ref='24596132' role='' />\n    <member type='way' ref='24886843' role='' />\n    <member type='way' ref='24886844' role='' />\n    <member type='way' ref='233763052' role='' />\n    <member type='way' ref='24886851' role='' />\n    <member type='way' ref='460608297' role='' />\n    <member type='way' ref='4764111' role='' />\n    <member type='way' ref='4068014' role='' />\n    <member type='way' ref='460614423' role='' />\n    <member type='way' ref='460614422' role='' />\n    <member type='way' ref='233763051' role='' />\n    <member type='way' ref='4706699' role='' />\n    <member type='way' ref='460620458' role='' />\n    <member type='way' ref='4434515' role='' />\n    <member type='way' ref='4525444' role='' />\n    <member type='way' ref='4515857' role='' />\n    <member type='way' ref='35584003' role='' />\n    <member type='way' ref='335487063' role='' />\n    <member type='way' ref='84792460' role='' />\n    <member type='way' ref='631971877' role='' />\n    <member type='way' ref='35584124' role='' />\n    <member type='way' ref='711049500' role='' />\n    <member type='way' ref='213138836' role='' />\n    <member type='way' ref='633919304' role='' />\n    <member type='way' ref='844385561' role='' />\n    <member type='way' ref='633919303' role='' />\n    <member type='way' ref='633919302' role='' />\n    <member type='way' ref='545607286' role='' />\n    <tag k='from' v='Aalborg, Busbahnhof' />\n    <tag k='name' v='Flixbus N150: Aalborg, Busbahnhof =&gt; Berlin ZOB' />\n    <tag k='name:da' v='Flixbus N150: Aalborg, busstation =&gt; Berlins centrale busstation' />\n    <tag k='name:de' v='Flixbus N150: Aalborg, Busbahnhof =&gt; Berlin ZOB' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='N150' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Berlin ZOB' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='10080987' timestamp='2020-01-08T20:14:02Z' uid='77904' user='Linda_Esperanto' visible='true' version='2' changeset='79354735'>\n    <member type='relation' ref='10077721' role='' />\n    <member type='relation' ref='10080986' role='' />\n    <tag k='from' v='Berlin ZOB' />\n    <tag k='name' v='Flixbus N150: Berlin &lt;=&gt; Aalborg' />\n    <tag k='name:da' v='Flixbus N150: Berlin &lt;=&gt; Aalborg' />\n    <tag k='name:de' v='Flixbus N150: Berlin &lt;=&gt; Aalborg' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='ref' v='N150' />\n    <tag k='route_master' v='bus' />\n    <tag k='to' v='Aalborg, Busbahnhof' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='10081257' timestamp='2020-10-06T09:03:11Z' uid='811530' user='RBOCity' visible='true' version='51' changeset='92034593'>\n    <member type='node' ref='2868414783' role='stop' />\n    <member type='node' ref='6046917219' role='platform' />\n    <member type='node' ref='2868414781' role='stop' />\n    <member type='node' ref='6046917218' role='platform' />\n    <member type='node' ref='6178112069' role='stop' />\n    <member type='node' ref='6178138208' role='platform' />\n    <member type='node' ref='2896384055' role='stop' />\n    <member type='node' ref='6823522163' role='stop' />\n    <member type='way' ref='553699678' role='platform' />\n    <member type='node' ref='367042031' role='platform' />\n    <member type='node' ref='6223447952' role='stop' />\n    <member type='way' ref='513595672' role='platform' />\n    <member type='node' ref='5974391200' role='stop' />\n    <member type='node' ref='1987802672' role='stop' />\n    <member type='node' ref='7647740030' role='platform' />\n    <member type='node' ref='1987802673' role='stop' />\n    <member type='node' ref='7647758213' role='platform' />\n    <member type='node' ref='1987802675' role='stop' />\n    <member type='node' ref='7647758212' role='platform' />\n    <member type='node' ref='1987802677' role='stop' />\n    <member type='node' ref='7647758211' role='platform' />\n    <member type='node' ref='1987802678' role='stop' />\n    <member type='node' ref='7647758210' role='platform' />\n    <member type='node' ref='7779170659' role='stop' />\n    <member type='node' ref='7650108367' role='platform' />\n    <member type='node' ref='6827125634' role='stop' />\n    <member type='node' ref='3393833062' role='stop' />\n    <member type='way' ref='332263376' role='platform' />\n    <member type='way' ref='282898405' role='' />\n    <member type='way' ref='28724405' role='' />\n    <member type='way' ref='282898410' role='' />\n    <member type='way' ref='282898410' role='' />\n    <member type='way' ref='685314678' role='' />\n    <member type='way' ref='659046254' role='' />\n    <member type='way' ref='659046257' role='' />\n    <member type='way' ref='659046258' role='' />\n    <member type='way' ref='659046253' role='' />\n    <member type='way' ref='659046256' role='' />\n    <member type='way' ref='86572979' role='' />\n    <member type='way' ref='39981133' role='' />\n    <member type='way' ref='39981134' role='' />\n    <member type='way' ref='43665654' role='' />\n    <member type='way' ref='39981138' role='' />\n    <member type='way' ref='27805057' role='' />\n    <member type='way' ref='572210306' role='' />\n    <member type='way' ref='292599140' role='' />\n    <member type='way' ref='78797017' role='' />\n    <member type='way' ref='27805055' role='' />\n    <member type='way' ref='477932757' role='' />\n    <member type='way' ref='572210277' role='' />\n    <member type='way' ref='572210285' role='' />\n    <member type='way' ref='477932756' role='' />\n    <member type='way' ref='187761804' role='' />\n    <member type='way' ref='142944333' role='' />\n    <member type='way' ref='186821032' role='' />\n    <member type='way' ref='186821035' role='' />\n    <member type='way' ref='186821033' role='' />\n    <member type='way' ref='142944337' role='' />\n    <member type='way' ref='486864288' role='' />\n    <member type='way' ref='551303422' role='' />\n    <member type='way' ref='551303420' role='' />\n    <member type='way' ref='43292244' role='' />\n    <member type='way' ref='560283873' role='' />\n    <member type='way' ref='75574936' role='' />\n    <member type='way' ref='186713412' role='' />\n    <member type='way' ref='551303411' role='' />\n    <member type='way' ref='40016270' role='' />\n    <member type='way' ref='23947828' role='' />\n    <member type='way' ref='186713419' role='' />\n    <member type='way' ref='199752604' role='' />\n    <member type='way' ref='199752605' role='' />\n    <member type='way' ref='142692918' role='' />\n    <member type='way' ref='142770547' role='' />\n    <member type='way' ref='199753406' role='' />\n    <member type='way' ref='199239120' role='' />\n    <member type='way' ref='199239121' role='' />\n    <member type='way' ref='381750335' role='' />\n    <member type='way' ref='284139902' role='' />\n    <member type='way' ref='24483197' role='' />\n    <member type='way' ref='199500676' role='' />\n    <member type='way' ref='199801748' role='' />\n    <member type='way' ref='139084186' role='' />\n    <member type='way' ref='538341573' role='' />\n    <member type='way' ref='156559304' role='' />\n    <member type='way' ref='242489322' role='' />\n    <member type='way' ref='538341572' role='' />\n    <member type='way' ref='26810757' role='' />\n    <member type='way' ref='92197791' role='' />\n    <member type='way' ref='521045511' role='' />\n    <member type='way' ref='24335863' role='' />\n    <member type='way' ref='566291585' role='' />\n    <member type='way' ref='495849381' role='' />\n    <member type='way' ref='26812782' role='' />\n    <member type='way' ref='391801515' role='' />\n    <member type='way' ref='391801512' role='' />\n    <member type='way' ref='391801518' role='' />\n    <member type='way' ref='391801511' role='' />\n    <member type='way' ref='391801517' role='' />\n    <member type='way' ref='129881956' role='' />\n    <member type='way' ref='391801519' role='' />\n    <member type='way' ref='129881957' role='' />\n    <member type='way' ref='391821203' role='' />\n    <member type='way' ref='137154549' role='' />\n    <member type='way' ref='391821210' role='' />\n    <member type='way' ref='391821217' role='' />\n    <member type='way' ref='238934536' role='' />\n    <member type='way' ref='391821211' role='' />\n    <member type='way' ref='391821229' role='' />\n    <member type='way' ref='558854986' role='' />\n    <member type='way' ref='391821215' role='' />\n    <member type='way' ref='391821208' role='' />\n    <member type='way' ref='391821221' role='' />\n    <member type='way' ref='26812876' role='' />\n    <member type='way' ref='46272777' role='' />\n    <member type='way' ref='3997588' role='' />\n    <member type='way' ref='114929183' role='' />\n    <member type='way' ref='20339700' role='' />\n    <member type='way' ref='391821267' role='' />\n    <member type='way' ref='391821249' role='' />\n    <member type='way' ref='391821257' role='' />\n    <member type='way' ref='20339701' role='' />\n    <member type='way' ref='50390277' role='' />\n    <member type='way' ref='137247326' role='' />\n    <member type='way' ref='44654906' role='' />\n    <member type='way' ref='339819552' role='' />\n    <member type='way' ref='612753197' role='' />\n    <member type='way' ref='612753196' role='' />\n    <member type='way' ref='142709204' role='' />\n    <member type='way' ref='44918953' role='' />\n    <member type='way' ref='44918954' role='' />\n    <member type='way' ref='142709207' role='' />\n    <member type='way' ref='47704668' role='' />\n    <member type='way' ref='44867408' role='' />\n    <member type='way' ref='584708063' role='' />\n    <member type='way' ref='584708062' role='' />\n    <member type='way' ref='135652406' role='' />\n    <member type='way' ref='135248171' role='' />\n    <member type='way' ref='135652408' role='' />\n    <member type='way' ref='135652410' role='' />\n    <member type='way' ref='584708064' role='' />\n    <member type='way' ref='44867405' role='' />\n    <member type='way' ref='44867404' role='' />\n    <member type='way' ref='85614231' role='' />\n    <member type='way' ref='143824761' role='' />\n    <member type='way' ref='135963869' role='' />\n    <member type='way' ref='135963872' role='' />\n    <member type='way' ref='545868496' role='' />\n    <member type='way' ref='143824768' role='' />\n    <member type='way' ref='143824759' role='' />\n    <member type='way' ref='545868498' role='' />\n    <member type='way' ref='30094505' role='' />\n    <member type='way' ref='3987858' role='' />\n    <member type='way' ref='545868499' role='' />\n    <member type='way' ref='40127752' role='' />\n    <member type='way' ref='27357349' role='' />\n    <member type='way' ref='30313949' role='' />\n    <member type='way' ref='38279856' role='' />\n    <member type='way' ref='79825789' role='' />\n    <member type='way' ref='28909484' role='' />\n    <member type='way' ref='136484403' role='' />\n    <member type='way' ref='42177282' role='' />\n    <member type='way' ref='55927144' role='' />\n    <member type='way' ref='55927147' role='' />\n    <member type='way' ref='28909465' role='' />\n    <member type='way' ref='313851854' role='' />\n    <member type='way' ref='89126774' role='' />\n    <member type='way' ref='28909489' role='' />\n    <member type='way' ref='40001138' role='' />\n    <member type='way' ref='313936252' role='' />\n    <member type='way' ref='21398224' role='' />\n    <member type='way' ref='313936251' role='' />\n    <member type='way' ref='313936253' role='' />\n    <member type='way' ref='659872944' role='' />\n    <member type='way' ref='21398269' role='' />\n    <member type='way' ref='28909464' role='' />\n    <member type='way' ref='313994639' role='' />\n    <member type='way' ref='4270847' role='' />\n    <member type='way' ref='659872940' role='' />\n    <member type='way' ref='659872941' role='' />\n    <member type='way' ref='15950729' role='' />\n    <member type='way' ref='28909479' role='' />\n    <member type='way' ref='21347111' role='' />\n    <member type='way' ref='314070642' role='' />\n    <member type='way' ref='15950735' role='' />\n    <member type='way' ref='40001015' role='' />\n    <member type='way' ref='40001019' role='' />\n    <member type='way' ref='48747587' role='' />\n    <member type='way' ref='659872938' role='' />\n    <member type='way' ref='48755060' role='' />\n    <member type='way' ref='40000997' role='' />\n    <member type='way' ref='15950741' role='' />\n    <member type='way' ref='21398979' role='' />\n    <member type='way' ref='48749132' role='' />\n    <member type='way' ref='15950740' role='' />\n    <member type='way' ref='15950739' role='' />\n    <member type='way' ref='314151444' role='' />\n    <member type='way' ref='42390866' role='' />\n    <member type='way' ref='585342922' role='' />\n    <member type='way' ref='4329499' role='' />\n    <member type='way' ref='659872939' role='' />\n    <member type='way' ref='46742303' role='' />\n    <member type='way' ref='739643643' role='' />\n    <member type='way' ref='314151453' role='' />\n    <member type='way' ref='198157906' role='' />\n    <member type='way' ref='198157909' role='' />\n    <member type='way' ref='40556706' role='' />\n    <member type='way' ref='4329496' role='' />\n    <member type='way' ref='21309999' role='' />\n    <member type='way' ref='147122574' role='' />\n    <member type='way' ref='147122576' role='' />\n    <member type='way' ref='26119185' role='' />\n    <member type='way' ref='4309076' role='' />\n    <member type='way' ref='21306323' role='' />\n    <member type='way' ref='220656000' role='' />\n    <member type='way' ref='149852883' role='' />\n    <member type='way' ref='220656011' role='' />\n    <member type='way' ref='220656008' role='' />\n    <member type='way' ref='78466835' role='' />\n    <member type='way' ref='220656005' role='' />\n    <member type='way' ref='220656006' role='' />\n    <member type='way' ref='32236438' role='' />\n    <member type='way' ref='210197355' role='' />\n    <member type='way' ref='25343682' role='' />\n    <member type='way' ref='136485979' role='' />\n    <member type='way' ref='148941375' role='' />\n    <member type='way' ref='220651729' role='' />\n    <member type='way' ref='28312808' role='' />\n    <member type='way' ref='220112809' role='' />\n    <member type='way' ref='28312805' role='' />\n    <member type='way' ref='220651723' role='' />\n    <member type='way' ref='220651714' role='' />\n    <member type='way' ref='25896767' role='' />\n    <member type='way' ref='103843187' role='' />\n    <member type='way' ref='136485955' role='' />\n    <member type='way' ref='103843184' role='' />\n    <member type='way' ref='702208693' role='' />\n    <member type='way' ref='4766001' role='' />\n    <member type='way' ref='4766002' role='' />\n    <member type='way' ref='46742301' role='' />\n    <member type='way' ref='3989646' role='' />\n    <member type='way' ref='58136889' role='' />\n    <member type='way' ref='701675838' role='' />\n    <member type='way' ref='296390955' role='' />\n    <member type='way' ref='683695052' role='' />\n    <member type='way' ref='683695051' role='' />\n    <member type='way' ref='412141655' role='' />\n    <member type='way' ref='540939281' role='' />\n    <member type='way' ref='540939276' role='' />\n    <member type='way' ref='540939272' role='' />\n    <member type='way' ref='540939268' role='' />\n    <member type='way' ref='296390963' role='' />\n    <member type='way' ref='34379683' role='' />\n    <member type='way' ref='324251358' role='' />\n    <member type='way' ref='324251352' role='' />\n    <member type='way' ref='324251355' role='' />\n    <member type='way' ref='4019408' role='' />\n    <member type='way' ref='690918956' role='' />\n    <member type='way' ref='10711342' role='' />\n    <member type='way' ref='10711343' role='' />\n    <member type='way' ref='10711348' role='' />\n    <member type='way' ref='500776559' role='' />\n    <member type='way' ref='233083900' role='' />\n    <member type='way' ref='233083909' role='' />\n    <member type='way' ref='233083898' role='' />\n    <member type='way' ref='233083905' role='' />\n    <member type='way' ref='690686293' role='' />\n    <member type='way' ref='690686294' role='' />\n    <member type='way' ref='296494591' role='' />\n    <member type='way' ref='473334686' role='' />\n    <member type='way' ref='473334682' role='' />\n    <member type='way' ref='515191108' role='' />\n    <member type='way' ref='233082560' role='' />\n    <member type='way' ref='296497515' role='' />\n    <member type='way' ref='233082561' role='' />\n    <member type='way' ref='233082556' role='' />\n    <member type='way' ref='35723971' role='' />\n    <member type='way' ref='296501850' role='' />\n    <member type='way' ref='34881625' role='' />\n    <member type='way' ref='4987972' role='' />\n    <member type='way' ref='690558814' role='' />\n    <member type='way' ref='690558810' role='' />\n    <member type='way' ref='233081269' role='' />\n    <member type='way' ref='233081273' role='' />\n    <member type='way' ref='233081270' role='' />\n    <member type='way' ref='233081272' role='' />\n    <member type='way' ref='51435393' role='' />\n    <member type='way' ref='412141674' role='' />\n    <member type='way' ref='462387276' role='' />\n    <member type='way' ref='412141648' role='' />\n    <member type='way' ref='462387614' role='' />\n    <member type='way' ref='51435389' role='' />\n    <member type='way' ref='83174288' role='' />\n    <member type='way' ref='515191106' role='' />\n    <member type='way' ref='233079508' role='' />\n    <member type='way' ref='554321817' role='' />\n    <member type='way' ref='233079507' role='' />\n    <member type='way' ref='515191104' role='' />\n    <member type='way' ref='390575956' role='' />\n    <member type='way' ref='544212750' role='' />\n    <member type='way' ref='603519201' role='' />\n    <member type='way' ref='690566791' role='' />\n    <member type='way' ref='690566792' role='' />\n    <member type='way' ref='744330327' role='' />\n    <member type='way' ref='744330326' role='' />\n    <member type='way' ref='412141668' role='' />\n    <member type='way' ref='660900887' role='' />\n    <member type='way' ref='659785284' role='' />\n    <member type='way' ref='4993370' role='' />\n    <member type='way' ref='233078903' role='' />\n    <member type='way' ref='233078905' role='' />\n    <member type='way' ref='4993373' role='' />\n    <member type='way' ref='60660645' role='' />\n    <member type='way' ref='60660649' role='' />\n    <member type='way' ref='4987967' role='' />\n    <member type='way' ref='233078901' role='' />\n    <member type='way' ref='233078904' role='' />\n    <member type='way' ref='30527962' role='' />\n    <member type='way' ref='30527963' role='' />\n    <member type='way' ref='30527957' role='' />\n    <member type='way' ref='30527958' role='' />\n    <member type='way' ref='659785282' role='' />\n    <member type='way' ref='233078180' role='' />\n    <member type='way' ref='660900883' role='' />\n    <member type='way' ref='46742304' role='' />\n    <member type='way' ref='233078183' role='' />\n    <member type='way' ref='316452179' role='' />\n    <member type='way' ref='233078184' role='' />\n    <member type='way' ref='32255545' role='' />\n    <member type='way' ref='633718226' role='' />\n    <member type='way' ref='59572331' role='' />\n    <member type='way' ref='59572360' role='' />\n    <member type='way' ref='32337801' role='' />\n    <member type='way' ref='32337802' role='' />\n    <member type='way' ref='531541378' role='' />\n    <member type='way' ref='233077308' role='' />\n    <member type='way' ref='503268751' role='' />\n    <member type='way' ref='233077313' role='' />\n    <member type='way' ref='46742297' role='' />\n    <member type='way' ref='690686303' role='' />\n    <member type='way' ref='233077307' role='' />\n    <member type='way' ref='683695039' role='' />\n    <member type='way' ref='606446528' role='' />\n    <member type='way' ref='58136781' role='' />\n    <member type='way' ref='296530949' role='' />\n    <member type='way' ref='5078910' role='' />\n    <member type='way' ref='69958287' role='' />\n    <member type='way' ref='240745994' role='' />\n    <member type='way' ref='240745992' role='' />\n    <member type='way' ref='32454972' role='' />\n    <member type='way' ref='32454971' role='' />\n    <member type='way' ref='240745993' role='' />\n    <member type='way' ref='240745996' role='' />\n    <member type='way' ref='26821965' role='' />\n    <member type='way' ref='26821966' role='' />\n    <member type='way' ref='31978384' role='' />\n    <member type='way' ref='31978385' role='' />\n    <member type='way' ref='485851177' role='' />\n    <member type='way' ref='485851174' role='' />\n    <member type='way' ref='485851173' role='' />\n    <member type='way' ref='142699276' role='' />\n    <member type='way' ref='142699273' role='' />\n    <member type='way' ref='142699275' role='' />\n    <member type='way' ref='22819101' role='' />\n    <member type='way' ref='22819102' role='' />\n    <member type='way' ref='26832674' role='' />\n    <member type='way' ref='26832675' role='' />\n    <member type='way' ref='142699274' role='' />\n    <member type='way' ref='22418800' role='' />\n    <member type='way' ref='22418820' role='' />\n    <member type='way' ref='157364391' role='' />\n    <member type='way' ref='157364394' role='' />\n    <member type='way' ref='344159905' role='' />\n    <member type='way' ref='76839447' role='' />\n    <member type='way' ref='749559788' role='' />\n    <member type='way' ref='34614767' role='' />\n    <member type='way' ref='733027972' role='' />\n    <member type='way' ref='678151500' role='' />\n    <member type='way' ref='76839458' role='' />\n    <member type='way' ref='733027970' role='' />\n    <member type='way' ref='4717259' role='' />\n    <member type='way' ref='296557880' role='' />\n    <member type='way' ref='153203960' role='' />\n    <member type='way' ref='76839452' role='' />\n    <member type='way' ref='274608780' role='' />\n    <member type='way' ref='274608779' role='' />\n    <member type='way' ref='274606794' role='' />\n    <member type='way' ref='144053947' role='' />\n    <member type='way' ref='274606795' role='' />\n    <member type='way' ref='274606796' role='' />\n    <member type='way' ref='274606797' role='' />\n    <member type='way' ref='37352317' role='' />\n    <member type='way' ref='7998655' role='' />\n    <member type='way' ref='29650025' role='' />\n    <member type='way' ref='296557881' role='' />\n    <member type='way' ref='779492336' role='' />\n    <member type='way' ref='562643469' role='' />\n    <member type='way' ref='433798771' role='' />\n    <member type='way' ref='379519259' role='' />\n    <member type='way' ref='236855736' role='' />\n    <member type='way' ref='236855733' role='' />\n    <member type='way' ref='236855737' role='' />\n    <member type='way' ref='121243050' role='' />\n    <member type='way' ref='39337118' role='' />\n    <member type='way' ref='733372950' role='' />\n    <member type='way' ref='733372949' role='' />\n    <member type='way' ref='30809509' role='' />\n    <member type='way' ref='275422324' role='' />\n    <member type='way' ref='275422323' role='' />\n    <member type='way' ref='275422325' role='' />\n    <member type='way' ref='121243055' role='' />\n    <member type='way' ref='8666488' role='' />\n    <member type='way' ref='498866393' role='' />\n    <member type='way' ref='28060354' role='' />\n    <member type='way' ref='29651991' role='' />\n    <member type='way' ref='28150271' role='' />\n    <member type='way' ref='314450784' role='' />\n    <member type='way' ref='776093119' role='' />\n    <member type='way' ref='776093120' role='' />\n    <member type='way' ref='138982590' role='' />\n    <member type='way' ref='67153451' role='' />\n    <member type='way' ref='46739386' role='' />\n    <member type='way' ref='40033980' role='' />\n    <member type='way' ref='8666490' role='' />\n    <member type='way' ref='314450772' role='' />\n    <member type='way' ref='314450782' role='' />\n    <member type='way' ref='314450780' role='' />\n    <member type='way' ref='682144620' role='' />\n    <member type='way' ref='280958658' role='' />\n    <member type='way' ref='682144621' role='' />\n    <member type='way' ref='682144620' role='' />\n    <member type='way' ref='682144623' role='' />\n    <member type='way' ref='314450779' role='' />\n    <member type='way' ref='314450781' role='' />\n    <member type='way' ref='314450775' role='' />\n    <member type='way' ref='138982589' role='' />\n    <member type='way' ref='183984419' role='' />\n    <member type='way' ref='314450776' role='' />\n    <member type='way' ref='47252827' role='' />\n    <member type='way' ref='40033158' role='' />\n    <member type='way' ref='67153460' role='' />\n    <member type='way' ref='314450777' role='' />\n    <member type='way' ref='314450774' role='' />\n    <member type='way' ref='314450778' role='' />\n    <member type='way' ref='28150896' role='' />\n    <member type='way' ref='543196765' role='' />\n    <member type='way' ref='520405269' role='' />\n    <member type='way' ref='379812994' role='' />\n    <member type='way' ref='28150250' role='' />\n    <member type='way' ref='379852448' role='' />\n    <member type='way' ref='37831194' role='' />\n    <member type='way' ref='8666488' role='' />\n    <member type='way' ref='121243055' role='' />\n    <member type='way' ref='275422325' role='' />\n    <member type='way' ref='275422323' role='' />\n    <member type='way' ref='275422324' role='' />\n    <member type='way' ref='30809517' role='' />\n    <member type='way' ref='13641245' role='' />\n    <member type='way' ref='275422317' role='' />\n    <member type='way' ref='578627081' role='' />\n    <member type='way' ref='275422314' role='' />\n    <member type='way' ref='36933287' role='' />\n    <member type='way' ref='28222985' role='' />\n    <member type='way' ref='433798767' role='' />\n    <member type='way' ref='36780903' role='' />\n    <member type='way' ref='379519260' role='' />\n    <member type='way' ref='531462061' role='' />\n    <member type='way' ref='68244365' role='' />\n    <member type='way' ref='46739384' role='' />\n    <member type='way' ref='531462062' role='' />\n    <member type='way' ref='236855738' role='' />\n    <member type='way' ref='779254719' role='' />\n    <member type='way' ref='779254717' role='' />\n    <member type='way' ref='779254718' role='' />\n    <member type='way' ref='779254713' role='' />\n    <member type='way' ref='779254721' role='' />\n    <member type='way' ref='779254722' role='' />\n    <member type='way' ref='601394137' role='' />\n    <member type='way' ref='144053949' role='' />\n    <member type='way' ref='274513688' role='' />\n    <member type='way' ref='274513690' role='' />\n    <member type='way' ref='274513686' role='' />\n    <member type='way' ref='274513681' role='' />\n    <member type='way' ref='48178462' role='' />\n    <member type='way' ref='5012656' role='' />\n    <member type='way' ref='37352363' role='' />\n    <member type='way' ref='47796988' role='' />\n    <member type='way' ref='144053946' role='' />\n    <member type='way' ref='153203961' role='' />\n    <member type='way' ref='274608055' role='' />\n    <member type='way' ref='19714183' role='' />\n    <member type='way' ref='69288832' role='' />\n    <member type='way' ref='235018086' role='' />\n    <member type='way' ref='5078116' role='' />\n    <member type='way' ref='69969588' role='' />\n    <member type='way' ref='240140719' role='' />\n    <member type='way' ref='168104648' role='' />\n    <member type='way' ref='324251311' role='' />\n    <member type='way' ref='324251310' role='' />\n    <member type='way' ref='324251309' role='' />\n    <member type='way' ref='324251312' role='' />\n    <member type='way' ref='324319575' role='' />\n    <member type='way' ref='324319590' role='' />\n    <member type='way' ref='324319584' role='' />\n    <member type='way' ref='324319577' role='' />\n    <member type='way' ref='33152023' role='' />\n    <member type='way' ref='33152022' role='' />\n    <member type='way' ref='19629511' role='' />\n    <member type='way' ref='26149325' role='' />\n    <member type='way' ref='26149318' role='' />\n    <member type='way' ref='5079260' role='' />\n    <member type='way' ref='233112550' role='' />\n    <member type='way' ref='233112535' role='' />\n    <member type='way' ref='233112547' role='' />\n    <member type='way' ref='233112560' role='' />\n    <member type='way' ref='44021694' role='' />\n    <member type='way' ref='44021686' role='' />\n    <member type='way' ref='31488556' role='' />\n    <member type='way' ref='31488555' role='' />\n    <member type='way' ref='233103736' role='' />\n    <member type='way' ref='233103731' role='' />\n    <member type='way' ref='233103735' role='' />\n    <member type='way' ref='233103732' role='' />\n    <member type='way' ref='233023654' role='' />\n    <member type='way' ref='11846377' role='' />\n    <member type='way' ref='233023673' role='' />\n    <member type='way' ref='47434450' role='' />\n    <member type='way' ref='233023662' role='' />\n    <member type='way' ref='233023660' role='' />\n    <member type='way' ref='233023666' role='' />\n    <member type='way' ref='233023667' role='' />\n    <member type='way' ref='22739410' role='' />\n    <member type='way' ref='697956272' role='' />\n    <member type='way' ref='697956273' role='' />\n    <member type='way' ref='28637351' role='' />\n    <member type='way' ref='479922412' role='' />\n    <member type='way' ref='479922416' role='' />\n    <member type='way' ref='28637352' role='' />\n    <member type='way' ref='479922406' role='' />\n    <member type='way' ref='479922390' role='' />\n    <member type='way' ref='27259406' role='' />\n    <member type='way' ref='33290797' role='' />\n    <member type='way' ref='33290786' role='' />\n    <member type='way' ref='33302609' role='' />\n    <member type='way' ref='232994357' role='' />\n    <member type='way' ref='31958020' role='' />\n    <member type='way' ref='31958019' role='' />\n    <member type='way' ref='124981041' role='' />\n    <member type='way' ref='232994360' role='' />\n    <member type='way' ref='232994354' role='' />\n    <member type='way' ref='4019591' role='' />\n    <member type='way' ref='144142218' role='' />\n    <member type='way' ref='546560756' role='' />\n    <member type='way' ref='546560757' role='' />\n    <member type='way' ref='546560760' role='' />\n    <member type='way' ref='546560761' role='' />\n    <member type='way' ref='144142222' role='' />\n    <member type='way' ref='23022858' role='' />\n    <member type='way' ref='72135458' role='' />\n    <member type='way' ref='72135452' role='' />\n    <member type='way' ref='535382793' role='' />\n    <member type='way' ref='535382792' role='' />\n    <member type='way' ref='232982161' role='' />\n    <member type='way' ref='232982160' role='' />\n    <member type='way' ref='232982166' role='' />\n    <member type='way' ref='232982159' role='' />\n    <member type='way' ref='535382426' role='' />\n    <member type='way' ref='535382425' role='' />\n    <member type='way' ref='479915963' role='' />\n    <member type='way' ref='479915958' role='' />\n    <member type='way' ref='226071294' role='' />\n    <member type='way' ref='479915955' role='' />\n    <member type='way' ref='226071296' role='' />\n    <member type='way' ref='479915953' role='' />\n    <member type='way' ref='56014878' role='' />\n    <member type='way' ref='56014877' role='' />\n    <member type='way' ref='231419620' role='' />\n    <member type='way' ref='231419619' role='' />\n    <member type='way' ref='232975916' role='' />\n    <member type='way' ref='232975938' role='' />\n    <member type='way' ref='232975924' role='' />\n    <member type='way' ref='232975918' role='' />\n    <member type='way' ref='4019635' role='' />\n    <member type='way' ref='232971055' role='' />\n    <member type='way' ref='232971059' role='' />\n    <member type='way' ref='232971052' role='' />\n    <member type='way' ref='546557522' role='' />\n    <member type='way' ref='546557521' role='' />\n    <member type='way' ref='546557520' role='' />\n    <member type='way' ref='546557519' role='' />\n    <member type='way' ref='232971051' role='' />\n    <member type='way' ref='98223245' role='' />\n    <member type='way' ref='98223306' role='' />\n    <member type='way' ref='306874707' role='' />\n    <member type='way' ref='324251351' role='' />\n    <member type='way' ref='324251357' role='' />\n    <member type='way' ref='58136827' role='' />\n    <member type='way' ref='324251353' role='' />\n    <member type='way' ref='324251350' role='' />\n    <member type='way' ref='232967033' role='' />\n    <member type='way' ref='26141370' role='' />\n    <member type='way' ref='26141371' role='' />\n    <member type='way' ref='232967036' role='' />\n    <member type='way' ref='232967037' role='' />\n    <member type='way' ref='38273288' role='' />\n    <member type='way' ref='38273285' role='' />\n    <member type='way' ref='222565248' role='' />\n    <member type='way' ref='222565249' role='' />\n    <member type='way' ref='232965539' role='' />\n    <member type='way' ref='27229916' role='' />\n    <member type='way' ref='232965535' role='' />\n    <member type='way' ref='232965554' role='' />\n    <member type='way' ref='46753573' role='' />\n    <member type='way' ref='229424414' role='' />\n    <member type='way' ref='139486241' role='' />\n    <member type='way' ref='23098266' role='' />\n    <member type='way' ref='25141877' role='' />\n    <member type='way' ref='24408231' role='' />\n    <member type='way' ref='248690769' role='' />\n    <member type='way' ref='26304631' role='' />\n    <member type='way' ref='377101875' role='' />\n    <member type='way' ref='377101890' role='' />\n    <member type='way' ref='377645413' role='' />\n    <member type='way' ref='61413097' role='' />\n    <member type='way' ref='377645414' role='' />\n    <member type='way' ref='377101892' role='' />\n    <member type='way' ref='377101889' role='' />\n    <member type='way' ref='377101891' role='' />\n    <member type='way' ref='377101894' role='' />\n    <member type='way' ref='169327831' role='' />\n    <member type='way' ref='377101888' role='' />\n    <member type='way' ref='377101893' role='' />\n    <member type='way' ref='214796416' role='' />\n    <member type='way' ref='5037792' role='' />\n    <member type='way' ref='23967768' role='' />\n    <member type='way' ref='23416611' role='' />\n    <member type='way' ref='87700223' role='' />\n    <member type='way' ref='23416591' role='' />\n    <member type='way' ref='424099552' role='' />\n    <member type='way' ref='514392684' role='' />\n    <member type='way' ref='32551883' role='' />\n    <member type='way' ref='198685165' role='' />\n    <member type='way' ref='198685161' role='' />\n    <member type='way' ref='32552096' role='' />\n    <member type='way' ref='32705199' role='' />\n    <member type='way' ref='728153623' role='' />\n    <member type='way' ref='26413433' role='' />\n    <member type='way' ref='129025644' role='' />\n    <member type='way' ref='32515008' role='' />\n    <member type='way' ref='23080117' role='' />\n    <member type='way' ref='198685161' role='' />\n    <member type='way' ref='198685165' role='' />\n    <member type='way' ref='32551883' role='' />\n    <member type='way' ref='514392684' role='' />\n    <member type='way' ref='32551882' role='' />\n    <member type='way' ref='120076961' role='' />\n    <member type='way' ref='228838849' role='' />\n    <member type='way' ref='5666001' role='' />\n    <member type='way' ref='424099565' role='' />\n    <member type='way' ref='5037792' role='' />\n    <member type='way' ref='214796416' role='' />\n    <member type='way' ref='377101893' role='' />\n    <member type='way' ref='377101888' role='' />\n    <member type='way' ref='169327831' role='' />\n    <member type='way' ref='377101894' role='' />\n    <member type='way' ref='377101891' role='' />\n    <member type='way' ref='377101889' role='' />\n    <member type='way' ref='377101892' role='' />\n    <member type='way' ref='377645414' role='' />\n    <member type='way' ref='61413097' role='' />\n    <member type='way' ref='377645413' role='' />\n    <member type='way' ref='377101890' role='' />\n    <member type='way' ref='60981193' role='' />\n    <member type='way' ref='169280772' role='' />\n    <member type='way' ref='167241864' role='' />\n    <member type='way' ref='23059551' role='' />\n    <member type='way' ref='315213104' role='' />\n    <member type='way' ref='536501276' role='' />\n    <member type='way' ref='536501275' role='' />\n    <member type='way' ref='248690767' role='' />\n    <member type='way' ref='670253628' role='' />\n    <member type='way' ref='5037794' role='' />\n    <member type='way' ref='377766355' role='' />\n    <member type='way' ref='47160380' role='' />\n    <member type='way' ref='25829523' role='' />\n    <member type='way' ref='25829522' role='' />\n    <member type='way' ref='22133738' role='' />\n    <member type='way' ref='22133748' role='' />\n    <member type='way' ref='313106226' role='' />\n    <member type='way' ref='55833355' role='' />\n    <member type='way' ref='55833354' role='' />\n    <member type='way' ref='26581529' role='' />\n    <member type='way' ref='26581509' role='' />\n    <member type='way' ref='26581502' role='' />\n    <member type='way' ref='576352092' role='' />\n    <member type='way' ref='22820720' role='' />\n    <member type='way' ref='22820719' role='' />\n    <member type='way' ref='640159116' role='' />\n    <member type='way' ref='640159119' role='' />\n    <member type='way' ref='640159121' role='' />\n    <member type='way' ref='842828076' role='' />\n    <member type='way' ref='473318568' role='' />\n    <member type='way' ref='473318569' role='' />\n    <member type='way' ref='660618405' role='' />\n    <member type='way' ref='473318563' role='' />\n    <member type='way' ref='473318562' role='' />\n    <member type='way' ref='660622907' role='' />\n    <member type='way' ref='473318561' role='' />\n    <member type='way' ref='4586895' role='' />\n    <member type='way' ref='169502263' role='' />\n    <member type='way' ref='23113975' role='' />\n    <member type='way' ref='23113974' role='' />\n    <member type='way' ref='4586898' role='' />\n    <member type='way' ref='660694272' role='' />\n    <member type='way' ref='660694271' role='' />\n    <member type='way' ref='4586899' role='' />\n    <member type='way' ref='4586902' role='' />\n    <member type='way' ref='659869781' role='' />\n    <member type='way' ref='161385863' role='' />\n    <member type='way' ref='833202562' role='' />\n    <member type='way' ref='161385868' role='' />\n    <member type='way' ref='659869780' role='' />\n    <member type='way' ref='4586903' role='' />\n    <member type='way' ref='665676989' role='' />\n    <member type='way' ref='665676988' role='' />\n    <member type='way' ref='4586906' role='' />\n    <member type='way' ref='204165651' role='' />\n    <member type='way' ref='204165655' role='' />\n    <member type='way' ref='4586908' role='' />\n    <member type='way' ref='833202559' role='' />\n    <member type='way' ref='833202556' role='' />\n    <member type='way' ref='4586911' role='' />\n    <member type='way' ref='4586914' role='' />\n    <member type='way' ref='4586915' role='' />\n    <member type='way' ref='165254732' role='' />\n    <member type='way' ref='833202555' role='' />\n    <member type='way' ref='833202552' role='' />\n    <member type='way' ref='4586918' role='' />\n    <member type='way' ref='29216348' role='' />\n    <member type='way' ref='29216350' role='' />\n    <member type='way' ref='4586919' role='' />\n    <member type='way' ref='4586922' role='' />\n    <member type='way' ref='30447473' role='' />\n    <member type='way' ref='30447474' role='' />\n    <member type='way' ref='660630408' role='' />\n    <member type='way' ref='28212155' role='' />\n    <member type='way' ref='122304236' role='' />\n    <member type='way' ref='675382051' role='' />\n    <member type='way' ref='62289647' role='' />\n    <member type='way' ref='58075168' role='' />\n    <member type='way' ref='219837014' role='' />\n    <member type='way' ref='509102670' role='' />\n    <member type='way' ref='366669793' role='' />\n    <member type='way' ref='366669796' role='' />\n    <member type='way' ref='578209354' role='' />\n    <member type='way' ref='135052326' role='' />\n    <member type='way' ref='24629915' role='' />\n    <member type='way' ref='24629870' role='' />\n    <member type='way' ref='25533532' role='' />\n    <member type='way' ref='53772038' role='' />\n    <member type='way' ref='40814025' role='' />\n    <member type='way' ref='40814026' role='' />\n    <member type='way' ref='664891129' role='' />\n    <member type='way' ref='30050896' role='' />\n    <member type='way' ref='213210051' role='' />\n    <member type='way' ref='40814532' role='' />\n    <member type='way' ref='40814531' role='' />\n    <member type='way' ref='23486733' role='' />\n    <member type='way' ref='449199494' role='' />\n    <member type='way' ref='449199493' role='' />\n    <member type='way' ref='23486732' role='' />\n    <member type='way' ref='52565598' role='' />\n    <member type='way' ref='29358757' role='' />\n    <member type='way' ref='664891127' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175447' role='' />\n    <member type='way' ref='369175445' role='' />\n    <member type='way' ref='369175441' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='810335209' role='' />\n    <member type='way' ref='513492326' role='' />\n    <member type='way' ref='23486731' role='' />\n    <member type='way' ref='761557032' role='' />\n    <member type='way' ref='35438709' role='' />\n    <member type='way' ref='59002627' role='' />\n    <member type='way' ref='383314127' role='' />\n    <member type='way' ref='661407753' role='' />\n    <member type='way' ref='35540883' role='' />\n    <member type='way' ref='52564295' role='' />\n    <member type='way' ref='40723871' role='' />\n    <member type='way' ref='56054538' role='' />\n    <member type='way' ref='56054536' role='' />\n    <member type='way' ref='670507676' role='' />\n    <member type='way' ref='24163693' role='' />\n    <member type='way' ref='59002626' role='' />\n    <member type='way' ref='159161919' role='' />\n    <member type='way' ref='274704232' role='' />\n    <member type='way' ref='274704230' role='' />\n    <member type='way' ref='4586892' role='' />\n    <member type='way' ref='125556138' role='' />\n    <member type='way' ref='692474886' role='' />\n    <member type='way' ref='4586021' role='' />\n    <member type='way' ref='492836256' role='' />\n    <member type='way' ref='51754193' role='' />\n    <member type='way' ref='26160620' role='' />\n    <member type='way' ref='492836257' role='' />\n    <member type='way' ref='320454527' role='' />\n    <member type='way' ref='88420879' role='' />\n    <member type='way' ref='320454475' role='' />\n    <member type='way' ref='120736599' role='' />\n    <member type='way' ref='320454480' role='' />\n    <member type='way' ref='88439118' role='' />\n    <member type='way' ref='320454519' role='' />\n    <member type='way' ref='320454485' role='' />\n    <member type='way' ref='320454474' role='' />\n    <member type='way' ref='320454492' role='' />\n    <member type='way' ref='367544799' role='' />\n    <member type='way' ref='88956579' role='' />\n    <member type='way' ref='88956568' role='' />\n    <member type='way' ref='320454518' role='' />\n    <member type='way' ref='320454511' role='' />\n    <member type='way' ref='26160622' role='' />\n    <member type='way' ref='320454522' role='' />\n    <member type='way' ref='358582375' role='' />\n    <member type='way' ref='320454473' role='' />\n    <member type='way' ref='320454496' role='' />\n    <member type='way' ref='320454510' role='' />\n    <member type='way' ref='320454497' role='' />\n    <member type='way' ref='320454513' role='' />\n    <member type='way' ref='320454521' role='' />\n    <member type='way' ref='320454512' role='' />\n    <member type='way' ref='320454488' role='' />\n    <member type='way' ref='649953006' role='' />\n    <member type='way' ref='320454509' role='' />\n    <member type='way' ref='320454481' role='' />\n    <member type='way' ref='320454490' role='' />\n    <member type='way' ref='234855460' role='' />\n    <member type='way' ref='92286836' role='' />\n    <member type='way' ref='389088214' role='' />\n    <member type='way' ref='92286826' role='' />\n    <member type='way' ref='632773391' role='' />\n    <member type='way' ref='632773395' role='' />\n    <member type='way' ref='22970260' role='' />\n    <member type='way' ref='320454494' role='' />\n    <member type='way' ref='320454515' role='' />\n    <member type='way' ref='92286886' role='' />\n    <member type='way' ref='320454514' role='' />\n    <member type='way' ref='13231751' role='' />\n    <member type='way' ref='632789451' role='' />\n    <member type='way' ref='297072036' role='' />\n    <member type='way' ref='632789452' role='' />\n    <member type='way' ref='632799782' role='' />\n    <member type='way' ref='23236478' role='' />\n    <member type='way' ref='65009892' role='' />\n    <member type='way' ref='65009884' role='' />\n    <member type='way' ref='88288530' role='' />\n    <member type='way' ref='88288521' role='' />\n    <member type='way' ref='358530815' role='' />\n    <member type='way' ref='297157693' role='' />\n    <member type='way' ref='632789458' role='' />\n    <member type='way' ref='632789477' role='' />\n    <member type='way' ref='92286866' role='' />\n    <member type='way' ref='92286833' role='' />\n    <member type='way' ref='632789481' role='' />\n    <member type='way' ref='23150406' role='' />\n    <member type='way' ref='23150408' role='' />\n    <member type='way' ref='23150407' role='' />\n    <member type='way' ref='632789483' role='' />\n    <member type='way' ref='81113481' role='' />\n    <member type='way' ref='237480738' role='' />\n    <member type='way' ref='632790909' role='' />\n    <member type='way' ref='633223273' role='' />\n    <member type='way' ref='92286843' role='' />\n    <member type='way' ref='319988205' role='' />\n    <member type='way' ref='319988235' role='' />\n    <member type='way' ref='118842812' role='' />\n    <member type='way' ref='118842811' role='' />\n    <member type='way' ref='319988220' role='' />\n    <member type='way' ref='118842799' role='' />\n    <member type='way' ref='683582711' role='' />\n    <member type='way' ref='358587896' role='' />\n    <member type='way' ref='118842796' role='' />\n    <member type='way' ref='319988173' role='' />\n    <member type='way' ref='319988181' role='' />\n    <member type='way' ref='118842817' role='' />\n    <member type='way' ref='319988198' role='' />\n    <member type='way' ref='319988221' role='' />\n    <member type='way' ref='683582709' role='' />\n    <member type='way' ref='319988212' role='' />\n    <member type='way' ref='118842807' role='' />\n    <member type='way' ref='319988227' role='' />\n    <member type='way' ref='358587877' role='' />\n    <member type='way' ref='118842791' role='' />\n    <member type='way' ref='319988207' role='' />\n    <member type='way' ref='319988186' role='' />\n    <member type='way' ref='319988195' role='' />\n    <member type='way' ref='319988179' role='' />\n    <member type='way' ref='28101295' role='' />\n    <member type='way' ref='28101296' role='' />\n    <member type='way' ref='319988184' role='' />\n    <member type='way' ref='24058526' role='' />\n    <member type='way' ref='492828702' role='' />\n    <member type='way' ref='241787608' role='' />\n    <member type='way' ref='33787292' role='' />\n    <member type='way' ref='492828703' role='' />\n    <member type='way' ref='125225779' role='' />\n    <member type='way' ref='234346248' role='' />\n    <member type='way' ref='125225780' role='' />\n    <member type='way' ref='241787617' role='' />\n    <member type='way' ref='241787614' role='' />\n    <member type='way' ref='241787609' role='' />\n    <member type='way' ref='241787613' role='' />\n    <member type='way' ref='119161423' role='' />\n    <member type='way' ref='119161417' role='' />\n    <member type='way' ref='38028832' role='' />\n    <member type='way' ref='38028833' role='' />\n    <member type='way' ref='627085346' role='' />\n    <member type='way' ref='144915856' role='' />\n    <member type='way' ref='144915857' role='' />\n    <member type='way' ref='492828704' role='' />\n    <member type='way' ref='492828706' role='' />\n    <member type='way' ref='38028161' role='' />\n    <member type='way' ref='492828708' role='' />\n    <member type='way' ref='38510311' role='' />\n    <member type='way' ref='144915852' role='' />\n    <member type='way' ref='843150468' role='' />\n    <member type='way' ref='30406545' role='' />\n    <member type='way' ref='30406549' role='' />\n    <member type='way' ref='843150467' role='' />\n    <member type='way' ref='130294856' role='' />\n    <member type='way' ref='197290015' role='' />\n    <member type='way' ref='184792914' role='' />\n    <member type='way' ref='689482243' role='' />\n    <member type='way' ref='321378455' role='' />\n    <member type='way' ref='234119941' role='' />\n    <member type='way' ref='10215146' role='' />\n    <member type='way' ref='520517117' role='' />\n    <member type='way' ref='498867438' role='' />\n    <member type='way' ref='835340624' role='' />\n    <member type='way' ref='498859729' role='' />\n    <member type='way' ref='318944792' role='' />\n    <member type='way' ref='498859743' role='' />\n    <member type='way' ref='541532867' role='' />\n    <member type='way' ref='761946430' role='' />\n    <member type='way' ref='541532868' role='' />\n    <member type='way' ref='692361513' role='' />\n    <member type='way' ref='692361511' role='' />\n    <member type='way' ref='541532866' role='' />\n    <member type='way' ref='541532865' role='' />\n    <member type='way' ref='541532861' role='' />\n    <member type='way' ref='541532864' role='' />\n    <member type='way' ref='146511185' role='' />\n    <member type='way' ref='541532863' role='' />\n    <member type='way' ref='146511187' role='' />\n    <member type='way' ref='146511189' role='' />\n    <member type='way' ref='23320471' role='' />\n    <member type='way' ref='440140529' role='' />\n    <member type='way' ref='440140532' role='' />\n    <member type='way' ref='440140531' role='' />\n    <member type='way' ref='440140528' role='' />\n    <member type='way' ref='529371826' role='' />\n    <member type='way' ref='9375303' role='' />\n    <member type='way' ref='279054989' role='' />\n    <member type='way' ref='279054987' role='' />\n    <member type='way' ref='46766460' role='' />\n    <member type='way' ref='46766459' role='' />\n    <member type='way' ref='289756324' role='' />\n    <member type='way' ref='161465009' role='' />\n    <member type='way' ref='51676172' role='' />\n    <member type='way' ref='772699194' role='' />\n    <member type='way' ref='819559515' role='' />\n    <member type='way' ref='188155922' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='819559513' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819308078' role='' />\n    <member type='way' ref='819559512' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='819559511' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='819308077' role='' />\n    <member type='way' ref='818931650' role='' />\n    <member type='way' ref='188155891' role='' />\n    <member type='way' ref='833318550' role='' />\n    <member type='way' ref='161208735' role='' />\n    <member type='way' ref='517466647' role='' />\n    <member type='way' ref='279054991' role='' />\n    <member type='way' ref='397936984' role='' />\n    <member type='way' ref='23338944' role='' />\n    <member type='way' ref='513775934' role='' />\n    <member type='way' ref='513775931' role='' />\n    <member type='way' ref='285963332' role='' />\n    <member type='way' ref='513775935' role='' />\n    <member type='way' ref='714366703' role='' />\n    <member type='way' ref='833318552' role='' />\n    <member type='way' ref='714526872' role='' />\n    <member type='way' ref='833318553' role='' />\n    <member type='way' ref='833318551' role='' />\n    <member type='way' ref='513775939' role='' />\n    <member type='way' ref='513775930' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='39461687' role='' />\n    <member type='way' ref='682688470' role='' />\n    <member type='way' ref='414177890' role='' />\n    <member type='way' ref='517427345' role='' />\n    <member type='way' ref='517427348' role='' />\n    <member type='way' ref='379126354' role='' />\n    <member type='way' ref='823646246' role='' />\n    <member type='way' ref='9654384' role='' />\n    <member type='way' ref='728210419' role='' />\n    <member type='way' ref='513775944' role='' />\n    <member type='way' ref='617119454' role='' />\n    <member type='way' ref='10999525' role='' />\n    <member type='way' ref='319841468' role='' />\n    <member type='way' ref='546295339' role='' />\n    <member type='way' ref='510335879' role='' />\n    <member type='way' ref='510335862' role='' />\n    <member type='way' ref='161190692' role='' />\n    <member type='way' ref='510335870' role='' />\n    <member type='way' ref='161486851' role='' />\n    <member type='way' ref='510335842' role='' />\n    <member type='way' ref='510335846' role='' />\n    <member type='way' ref='788083572' role='' />\n    <member type='way' ref='319841465' role='' />\n    <member type='way' ref='741996204' role='' />\n    <member type='way' ref='23444180' role='' />\n    <member type='way' ref='741996205' role='' />\n    <member type='way' ref='272706515' role='' />\n    <member type='way' ref='788083573' role='' />\n    <member type='way' ref='272706514' role='' />\n    <member type='way' ref='272706525' role='' />\n    <member type='way' ref='272706535' role='' />\n    <member type='way' ref='682518206' role='' />\n    <member type='way' ref='272706511' role='' />\n    <member type='way' ref='272706533' role='' />\n    <member type='way' ref='510581880' role='' />\n    <member type='way' ref='272706517' role='' />\n    <member type='way' ref='272706532' role='' />\n    <member type='way' ref='272706531' role='' />\n    <member type='way' ref='272706520' role='' />\n    <member type='way' ref='272706524' role='' />\n    <member type='way' ref='272706513' role='' />\n    <member type='way' ref='272706510' role='' />\n    <member type='way' ref='272706528' role='' />\n    <member type='way' ref='688687939' role='' />\n    <member type='way' ref='510624279' role='' />\n    <member type='way' ref='510624268' role='' />\n    <member type='way' ref='272706526' role='' />\n    <member type='way' ref='510624247' role='' />\n    <member type='way' ref='618993313' role='' />\n    <member type='way' ref='24430829' role='' />\n    <member type='way' ref='517466669' role='' />\n    <member type='way' ref='56323454' role='' />\n    <member type='way' ref='517466668' role='' />\n    <member type='way' ref='510632776' role='' />\n    <member type='way' ref='44829595' role='' />\n    <member type='way' ref='161466777' role='' />\n    <member type='way' ref='326276383' role='' />\n    <member type='way' ref='11003381' role='' />\n    <member type='way' ref='326276396' role='' />\n    <member type='way' ref='161187739' role='' />\n    <member type='way' ref='326276387' role='' />\n    <member type='way' ref='326276391' role='' />\n    <member type='way' ref='43076107' role='' />\n    <member type='way' ref='71391950' role='' />\n    <member type='way' ref='771999805' role='' />\n    <member type='way' ref='59638078' role='' />\n    <member type='way' ref='309700793' role='' />\n    <member type='way' ref='309700794' role='' />\n    <member type='way' ref='27064373' role='' />\n    <member type='way' ref='27064374' role='' />\n    <member type='way' ref='326156168' role='' />\n    <member type='way' ref='188163074' role='' />\n    <member type='way' ref='326156169' role='' />\n    <member type='way' ref='11003452' role='' />\n    <member type='way' ref='188163076' role='' />\n    <member type='way' ref='492828713' role='' />\n    <member type='way' ref='492828712' role='' />\n    <member type='way' ref='51772367' role='' />\n    <member type='way' ref='99529286' role='' />\n    <member type='way' ref='99529506' role='' />\n    <member type='way' ref='51772369' role='' />\n    <member type='way' ref='305777809' role='' />\n    <member type='way' ref='23043380' role='' />\n    <member type='way' ref='99598367' role='' />\n    <member type='way' ref='12370361' role='' />\n    <member type='way' ref='653277772' role='' />\n    <member type='way' ref='653277782' role='' />\n    <member type='way' ref='653277750' role='' />\n    <member type='way' ref='653277744' role='' />\n    <member type='way' ref='653277714' role='' />\n    <member type='way' ref='653277713' role='' />\n    <member type='way' ref='843067400' role='' />\n    <member type='way' ref='653277705' role='' />\n    <member type='way' ref='843067403' role='' />\n    <member type='way' ref='653277736' role='' />\n    <member type='way' ref='653277706' role='' />\n    <member type='way' ref='653277711' role='' />\n    <member type='way' ref='653277710' role='' />\n    <member type='way' ref='694611813' role='' />\n    <member type='way' ref='69917265' role='' />\n    <member type='way' ref='69917264' role='' />\n    <member type='way' ref='694611814' role='' />\n    <member type='way' ref='790763458' role='' />\n    <member type='way' ref='682646705' role='' />\n    <member type='way' ref='495494402' role='' />\n    <member type='way' ref='69917270' role='' />\n    <member type='way' ref='69917262' role='' />\n    <member type='way' ref='790763461' role='' />\n    <member type='way' ref='789768769' role='' />\n    <member type='way' ref='789768756' role='' />\n    <member type='way' ref='789768752' role='' />\n    <member type='way' ref='789768738' role='' />\n    <member type='way' ref='789768753' role='' />\n    <member type='way' ref='790621587' role='' />\n    <member type='way' ref='172214603' role='' />\n    <member type='way' ref='790621589' role='' />\n    <member type='way' ref='172214601' role='' />\n    <member type='way' ref='728708240' role='' />\n    <member type='way' ref='728708237' role='' />\n    <member type='way' ref='790621601' role='' />\n    <member type='way' ref='789768748' role='' />\n    <member type='way' ref='39139052' role='' />\n    <member type='way' ref='789768733' role='' />\n    <member type='way' ref='728708241' role='' />\n    <member type='way' ref='495479090' role='' />\n    <member type='way' ref='741023455' role='' />\n    <member type='way' ref='790621636' role='' />\n    <member type='way' ref='59523885' role='' />\n    <member type='way' ref='495479087' role='' />\n    <member type='way' ref='495494403' role='' />\n    <member type='way' ref='495479089' role='' />\n    <member type='way' ref='787823938' role='' />\n    <member type='way' ref='787823757' role='' />\n    <member type='way' ref='495479088' role='' />\n    <member type='way' ref='787823756' role='' />\n    <member type='way' ref='787823755' role='' />\n    <member type='way' ref='789768846' role='' />\n    <member type='way' ref='128918739' role='' />\n    <member type='way' ref='789768838' role='' />\n    <member type='way' ref='789768837' role='' />\n    <member type='way' ref='59523937' role='' />\n    <member type='way' ref='789768844' role='' />\n    <member type='way' ref='789768843' role='' />\n    <member type='way' ref='789768869' role='' />\n    <member type='way' ref='206850870' role='' />\n    <member type='way' ref='787823961' role='' />\n    <member type='way' ref='787823946' role='' />\n    <member type='way' ref='787823960' role='' />\n    <member type='way' ref='785386336' role='' />\n    <member type='way' ref='785396388' role='' />\n    <member type='way' ref='785386335' role='' />\n    <member type='way' ref='785396387' role='' />\n    <member type='way' ref='785386334' role='' />\n    <member type='way' ref='785396402' role='' />\n    <member type='way' ref='785396404' role='' />\n    <member type='way' ref='785386332' role='' />\n    <member type='way' ref='785386331' role='' />\n    <member type='way' ref='495556762' role='' />\n    <member type='way' ref='12870928' role='' />\n    <member type='way' ref='233955594' role='' />\n    <member type='way' ref='495518311' role='' />\n    <member type='way' ref='503042910' role='' />\n    <member type='way' ref='123954808' role='' />\n    <member type='way' ref='99529458' role='' />\n    <member type='way' ref='99529614' role='' />\n    <member type='way' ref='99529541' role='' />\n    <member type='way' ref='99529300' role='' />\n    <member type='way' ref='99529669' role='' />\n    <member type='way' ref='98240455' role='' />\n    <member type='way' ref='98240453' role='' />\n    <member type='way' ref='813756628' role='' />\n    <member type='way' ref='98234177' role='' />\n    <member type='way' ref='98234175' role='' />\n    <member type='way' ref='319062894' role='' />\n    <member type='way' ref='319062895' role='' />\n    <member type='way' ref='209196326' role='' />\n    <member type='way' ref='209196328' role='' />\n    <member type='way' ref='106467525' role='' />\n    <member type='way' ref='106467520' role='' />\n    <member type='way' ref='231059758' role='' />\n    <member type='way' ref='231059756' role='' />\n    <member type='way' ref='709615630' role='' />\n    <member type='way' ref='29314712' role='' />\n    <member type='way' ref='29314711' role='' />\n    <member type='way' ref='163600212' role='' />\n    <member type='way' ref='837149996' role='' />\n    <member type='way' ref='837149997' role='' />\n    <member type='way' ref='837149995' role='' />\n    <member type='way' ref='837149998' role='' />\n    <member type='way' ref='813756627' role='' />\n    <member type='way' ref='427314412' role='' />\n    <member type='way' ref='838292586' role='' />\n    <member type='way' ref='782010056' role='' />\n    <member type='way' ref='782010057' role='' />\n    <member type='way' ref='132688030' role='' />\n    <member type='way' ref='838292597' role='' />\n    <member type='way' ref='8041982' role='' />\n    <member type='way' ref='838292592' role='' />\n    <member type='way' ref='8041981' role='' />\n    <member type='way' ref='252104525' role='' />\n    <member type='way' ref='252104520' role='' />\n    <member type='way' ref='848360981' role='' />\n    <member type='way' ref='72256434' role='' />\n    <member type='way' ref='681143380' role='' />\n    <member type='way' ref='681139365' role='' />\n    <member type='way' ref='107114265' role='' />\n    <member type='way' ref='275143515' role='' />\n    <member type='way' ref='496588160' role='' />\n    <member type='way' ref='496120886' role='' />\n    <member type='way' ref='825980028' role='' />\n    <member type='way' ref='779192145' role='' />\n    <member type='way' ref='825980013' role='' />\n    <member type='way' ref='368771669' role='' />\n    <member type='way' ref='825980021' role='' />\n    <member type='way' ref='368771668' role='' />\n    <member type='way' ref='825980016' role='' />\n    <member type='way' ref='782014017' role='' />\n    <member type='way' ref='825980011' role='' />\n    <member type='way' ref='782014016' role='' />\n    <member type='way' ref='286203393' role='' />\n    <member type='way' ref='496107793' role='' />\n    <member type='way' ref='496107794' role='' />\n    <member type='way' ref='779192156' role='' />\n    <member type='way' ref='286552816' role='' />\n    <member type='way' ref='71973851' role='' />\n    <member type='way' ref='348766149' role='' />\n    <member type='way' ref='348766145' role='' />\n    <member type='way' ref='348771994' role='' />\n    <member type='way' ref='285709739' role='' />\n    <tag k='from' v='Hamburg ZOB' />\n    <tag k='name' v='Flixbus 172: Hamburg ZOB =&gt; Aalborg, Busbahnhof' />\n    <tag k='name:da' v='Flixbus 172: Hamburg central busstation =&gt; Aalborg, busstation' />\n    <tag k='name:de' v='Flixbus 172: Hamburg ZOB =&gt; Aalborg, Busbahnhof' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='FLX172' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Aalborg, Busbahnhof' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='10081258' timestamp='2020-10-06T09:03:11Z' uid='811530' user='RBOCity' visible='true' version='45' changeset='92034593'>\n    <member type='node' ref='3393833062' role='stop' />\n    <member type='way' ref='332263376' role='platform' />\n    <member type='node' ref='6827125634' role='stop' />\n    <member type='node' ref='7779170659' role='stop' />\n    <member type='node' ref='7650108367' role='platform' />\n    <member type='node' ref='1987802672' role='stop' />\n    <member type='node' ref='7647740030' role='platform' />\n    <member type='node' ref='1987802673' role='stop' />\n    <member type='node' ref='7647758213' role='platform' />\n    <member type='node' ref='1987802675' role='stop' />\n    <member type='node' ref='7647758212' role='platform' />\n    <member type='node' ref='1987802677' role='stop' />\n    <member type='node' ref='7647758211' role='platform' />\n    <member type='node' ref='1987802678' role='stop' />\n    <member type='node' ref='7647758210' role='platform' />\n    <member type='node' ref='5974391200' role='stop' />\n    <member type='node' ref='6223447952' role='stop' />\n    <member type='way' ref='513595672' role='platform' />\n    <member type='node' ref='6823522163' role='stop' />\n    <member type='node' ref='367042031' role='platform' />\n    <member type='way' ref='553699678' role='platform' />\n    <member type='node' ref='2896384055' role='stop' />\n    <member type='node' ref='6178112069' role='stop' />\n    <member type='node' ref='6178138208' role='platform' />\n    <member type='node' ref='2868414783' role='stop' />\n    <member type='node' ref='6046917219' role='platform' />\n    <member type='node' ref='2868414781' role='stop' />\n    <member type='node' ref='6046917218' role='platform' />\n    <member type='way' ref='491845660' role='' />\n    <member type='way' ref='348763865' role='' />\n    <member type='way' ref='348763864' role='' />\n    <member type='way' ref='285709733' role='' />\n    <member type='way' ref='369152632' role='' />\n    <member type='way' ref='348766151' role='' />\n    <member type='way' ref='369151521' role='' />\n    <member type='way' ref='369151056' role='' />\n    <member type='way' ref='286552815' role='' />\n    <member type='way' ref='369151062' role='' />\n    <member type='way' ref='779192157' role='' />\n    <member type='way' ref='97574427' role='' />\n    <member type='way' ref='496580797' role='' />\n    <member type='way' ref='495987825' role='' />\n    <member type='way' ref='825980010' role='' />\n    <member type='way' ref='495987826' role='' />\n    <member type='way' ref='825980012' role='' />\n    <member type='way' ref='825980017' role='' />\n    <member type='way' ref='779192151' role='' />\n    <member type='way' ref='275143505' role='' />\n    <member type='way' ref='275143512' role='' />\n    <member type='way' ref='681139362' role='' />\n    <member type='way' ref='496249591' role='' />\n    <member type='way' ref='8169286' role='' />\n    <member type='way' ref='132688031' role='' />\n    <member type='way' ref='782010058' role='' />\n    <member type='way' ref='813756724' role='' />\n    <member type='way' ref='146514455' role='' />\n    <member type='way' ref='837149992' role='' />\n    <member type='way' ref='163600213' role='' />\n    <member type='way' ref='29314709' role='' />\n    <member type='way' ref='100211823' role='' />\n    <member type='way' ref='231059757' role='' />\n    <member type='way' ref='231059759' role='' />\n    <member type='way' ref='106467516' role='' />\n    <member type='way' ref='106467529' role='' />\n    <member type='way' ref='209196333' role='' />\n    <member type='way' ref='209196336' role='' />\n    <member type='way' ref='319062897' role='' />\n    <member type='way' ref='319062896' role='' />\n    <member type='way' ref='98234179' role='' />\n    <member type='way' ref='813756723' role='' />\n    <member type='way' ref='98234180' role='' />\n    <member type='way' ref='98240450' role='' />\n    <member type='way' ref='98240457' role='' />\n    <member type='way' ref='166899885' role='' />\n    <member type='way' ref='99529410' role='' />\n    <member type='way' ref='99529418' role='' />\n    <member type='way' ref='99529393' role='' />\n    <member type='way' ref='99529552' role='' />\n    <member type='way' ref='147288994' role='' />\n    <member type='way' ref='520269372' role='' />\n    <member type='way' ref='326285669' role='' />\n    <member type='way' ref='520269373' role='' />\n    <member type='way' ref='99529276' role='' />\n    <member type='way' ref='216584696' role='' />\n    <member type='way' ref='515175327' role='' />\n    <member type='way' ref='495556761' role='' />\n    <member type='way' ref='12870928' role='' />\n    <member type='way' ref='495556762' role='' />\n    <member type='way' ref='785386339' role='' />\n    <member type='way' ref='785386338' role='' />\n    <member type='way' ref='785396407' role='' />\n    <member type='way' ref='785396397' role='' />\n    <member type='way' ref='785396396' role='' />\n    <member type='way' ref='785386337' role='' />\n    <member type='way' ref='785396406' role='' />\n    <member type='way' ref='697860111' role='' />\n    <member type='way' ref='495518310' role='' />\n    <member type='way' ref='43407258' role='' />\n    <member type='way' ref='787823959' role='' />\n    <member type='way' ref='787823954' role='' />\n    <member type='way' ref='787823953' role='' />\n    <member type='way' ref='787823952' role='' />\n    <member type='way' ref='787823951' role='' />\n    <member type='way' ref='787823950' role='' />\n    <member type='way' ref='787823949' role='' />\n    <member type='way' ref='787823948' role='' />\n    <member type='way' ref='495556765' role='' />\n    <member type='way' ref='172010067' role='' />\n    <member type='way' ref='789768866' role='' />\n    <member type='way' ref='789768856' role='' />\n    <member type='way' ref='789768855' role='' />\n    <member type='way' ref='172010057' role='' />\n    <member type='way' ref='789768842' role='' />\n    <member type='way' ref='789768841' role='' />\n    <member type='way' ref='789768840' role='' />\n    <member type='way' ref='789768839' role='' />\n    <member type='way' ref='787823758' role='' />\n    <member type='way' ref='787823939' role='' />\n    <member type='way' ref='787823754' role='' />\n    <member type='way' ref='172010066' role='' />\n    <member type='way' ref='787823936' role='' />\n    <member type='way' ref='495494407' role='' />\n    <member type='way' ref='495494406' role='' />\n    <member type='way' ref='787823937' role='' />\n    <member type='way' ref='495494404' role='' />\n    <member type='way' ref='789768781' role='' />\n    <member type='way' ref='495494396' role='' />\n    <member type='way' ref='790621635' role='' />\n    <member type='way' ref='789768770' role='' />\n    <member type='way' ref='495494399' role='' />\n    <member type='way' ref='495494401' role='' />\n    <member type='way' ref='790763461' role='' />\n    <member type='way' ref='789768769' role='' />\n    <member type='way' ref='789768756' role='' />\n    <member type='way' ref='789768752' role='' />\n    <member type='way' ref='789768738' role='' />\n    <member type='way' ref='789768753' role='' />\n    <member type='way' ref='790621587' role='' />\n    <member type='way' ref='172214603' role='' />\n    <member type='way' ref='790621589' role='' />\n    <member type='way' ref='172214601' role='' />\n    <member type='way' ref='728708240' role='' />\n    <member type='way' ref='728708237' role='' />\n    <member type='way' ref='790621601' role='' />\n    <member type='way' ref='789768748' role='' />\n    <member type='way' ref='39139052' role='' />\n    <member type='way' ref='789768733' role='' />\n    <member type='way' ref='728708241' role='' />\n    <member type='way' ref='4976826' role='' />\n    <member type='way' ref='495494400' role='' />\n    <member type='way' ref='43407659' role='' />\n    <member type='way' ref='790621639' role='' />\n    <member type='way' ref='43407658' role='' />\n    <member type='way' ref='787823752' role='' />\n    <member type='way' ref='694611815' role='' />\n    <member type='way' ref='682646707' role='' />\n    <member type='way' ref='69917265' role='' />\n    <member type='way' ref='694611813' role='' />\n    <member type='way' ref='653277710' role='' />\n    <member type='way' ref='653277709' role='' />\n    <member type='way' ref='653277708' role='' />\n    <member type='way' ref='682639359' role='' />\n    <member type='way' ref='653277699' role='' />\n    <member type='way' ref='653277738' role='' />\n    <member type='way' ref='653277701' role='' />\n    <member type='way' ref='653277739' role='' />\n    <member type='way' ref='653277744' role='' />\n    <member type='way' ref='653277771' role='' />\n    <member type='way' ref='653277783' role='' />\n    <member type='way' ref='653277745' role='' />\n    <member type='way' ref='653277774' role='' />\n    <member type='way' ref='653277785' role='' />\n    <member type='way' ref='39131593' role='' />\n    <member type='way' ref='760605023' role='' />\n    <member type='way' ref='99529489' role='' />\n    <member type='way' ref='305777808' role='' />\n    <member type='way' ref='51772366' role='' />\n    <member type='way' ref='99529449' role='' />\n    <member type='way' ref='99529576' role='' />\n    <member type='way' ref='492828711' role='' />\n    <member type='way' ref='62227774' role='' />\n    <member type='way' ref='692148513' role='' />\n    <member type='way' ref='692148512' role='' />\n    <member type='way' ref='326285668' role='' />\n    <member type='way' ref='692148510' role='' />\n    <member type='way' ref='23376360' role='' />\n    <member type='way' ref='23376359' role='' />\n    <member type='way' ref='311697613' role='' />\n    <member type='way' ref='326156170' role='' />\n    <member type='way' ref='56045342' role='' />\n    <member type='way' ref='309700792' role='' />\n    <member type='way' ref='326285671' role='' />\n    <member type='way' ref='59638081' role='' />\n    <member type='way' ref='771999806' role='' />\n    <member type='way' ref='71391931' role='' />\n    <member type='way' ref='43076108' role='' />\n    <member type='way' ref='326276386' role='' />\n    <member type='way' ref='326276401' role='' />\n    <member type='way' ref='161187726' role='' />\n    <member type='way' ref='326285661' role='' />\n    <member type='way' ref='37343749' role='' />\n    <member type='way' ref='161466779' role='' />\n    <member type='way' ref='44829596' role='' />\n    <member type='way' ref='517466670' role='' />\n    <member type='way' ref='517466671' role='' />\n    <member type='way' ref='153531784' role='' />\n    <member type='way' ref='510624260' role='' />\n    <member type='way' ref='682500794' role='' />\n    <member type='way' ref='510624310' role='' />\n    <member type='way' ref='517466664' role='' />\n    <member type='way' ref='510624259' role='' />\n    <member type='way' ref='517466663' role='' />\n    <member type='way' ref='510624304' role='' />\n    <member type='way' ref='510624303' role='' />\n    <member type='way' ref='510624305' role='' />\n    <member type='way' ref='510624309' role='' />\n    <member type='way' ref='10999808' role='' />\n    <member type='way' ref='272706522' role='' />\n    <member type='way' ref='272706527' role='' />\n    <member type='way' ref='272706508' role='' />\n    <member type='way' ref='272706519' role='' />\n    <member type='way' ref='272706530' role='' />\n    <member type='way' ref='272706521' role='' />\n    <member type='way' ref='272706534' role='' />\n    <member type='way' ref='741996213' role='' />\n    <member type='way' ref='161469199' role='' />\n    <member type='way' ref='741996207' role='' />\n    <member type='way' ref='23444181' role='' />\n    <member type='way' ref='788083570' role='' />\n    <member type='way' ref='788083569' role='' />\n    <member type='way' ref='319841464' role='' />\n    <member type='way' ref='510335857' role='' />\n    <member type='way' ref='161486841' role='' />\n    <member type='way' ref='517427354' role='' />\n    <member type='way' ref='9654314' role='' />\n    <member type='way' ref='510335860' role='' />\n    <member type='way' ref='510335854' role='' />\n    <member type='way' ref='161190702' role='' />\n    <member type='way' ref='517427353' role='' />\n    <member type='way' ref='319841469' role='' />\n    <member type='way' ref='560552446' role='' />\n    <member type='way' ref='319841466' role='' />\n    <member type='way' ref='161464999' role='' />\n    <member type='way' ref='42424841' role='' />\n    <member type='way' ref='823646245' role='' />\n    <member type='way' ref='23629785' role='' />\n    <member type='way' ref='517427341' role='' />\n    <member type='way' ref='379126351' role='' />\n    <member type='way' ref='414177889' role='' />\n    <member type='way' ref='682688472' role='' />\n    <member type='way' ref='23338617' role='' />\n    <member type='way' ref='513775941' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='513775934' role='' />\n    <member type='way' ref='513775931' role='' />\n    <member type='way' ref='285963332' role='' />\n    <member type='way' ref='513775935' role='' />\n    <member type='way' ref='714366703' role='' />\n    <member type='way' ref='833318552' role='' />\n    <member type='way' ref='714526872' role='' />\n    <member type='way' ref='833318553' role='' />\n    <member type='way' ref='833318551' role='' />\n    <member type='way' ref='513775939' role='' />\n    <member type='way' ref='513775930' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='513775941' role='' />\n    <member type='way' ref='686698865' role='' />\n    <member type='way' ref='517427335' role='' />\n    <member type='way' ref='517427330' role='' />\n    <member type='way' ref='517466645' role='' />\n    <member type='way' ref='517466643' role='' />\n    <member type='way' ref='517466644' role='' />\n    <member type='way' ref='161208735' role='' />\n    <member type='way' ref='833318550' role='' />\n    <member type='way' ref='728192376' role='' />\n    <member type='way' ref='517466646' role='' />\n    <member type='way' ref='517466648' role='' />\n    <member type='way' ref='279054992' role='' />\n    <member type='way' ref='51676172' role='' />\n    <member type='way' ref='772699194' role='' />\n    <member type='way' ref='819559515' role='' />\n    <member type='way' ref='188155922' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='819559513' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819308078' role='' />\n    <member type='way' ref='819559512' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='819559511' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='819308077' role='' />\n    <member type='way' ref='818931650' role='' />\n    <member type='way' ref='188155891' role='' />\n    <member type='way' ref='728192376' role='' />\n    <member type='way' ref='517466646' role='' />\n    <member type='way' ref='517466648' role='' />\n    <member type='way' ref='279054992' role='' />\n    <member type='way' ref='161465009' role='' />\n    <member type='way' ref='289756324' role='' />\n    <member type='way' ref='46766459' role='' />\n    <member type='way' ref='46766460' role='' />\n    <member type='way' ref='279054987' role='' />\n    <member type='way' ref='279054989' role='' />\n    <member type='way' ref='9375303' role='' />\n    <member type='way' ref='529371826' role='' />\n    <member type='way' ref='440140528' role='' />\n    <member type='way' ref='440140531' role='' />\n    <member type='way' ref='161190693' role='' />\n    <member type='way' ref='440140529' role='' />\n    <member type='way' ref='23320471' role='' />\n    <member type='way' ref='146511189' role='' />\n    <member type='way' ref='146511188' role='' />\n    <member type='way' ref='143821024' role='' />\n    <member type='way' ref='541532857' role='' />\n    <member type='way' ref='8812730' role='' />\n    <member type='way' ref='541532855' role='' />\n    <member type='way' ref='692361519' role='' />\n    <member type='way' ref='692361505' role='' />\n    <member type='way' ref='161234020' role='' />\n    <member type='way' ref='761946431' role='' />\n    <member type='way' ref='835340625' role='' />\n    <member type='way' ref='498859739' role='' />\n    <member type='way' ref='498859736' role='' />\n    <member type='way' ref='835340627' role='' />\n    <member type='way' ref='498859744' role='' />\n    <member type='way' ref='520517114' role='' />\n    <member type='way' ref='128898677' role='' />\n    <member type='way' ref='348352697' role='' />\n    <member type='way' ref='234119942' role='' />\n    <member type='way' ref='321378453' role='' />\n    <member type='way' ref='689482242' role='' />\n    <member type='way' ref='321378452' role='' />\n    <member type='way' ref='184792919' role='' />\n    <member type='way' ref='192705441' role='' />\n    <member type='way' ref='144918016' role='' />\n    <member type='way' ref='144918017' role='' />\n    <member type='way' ref='144918015' role='' />\n    <member type='way' ref='843150465' role='' />\n    <member type='way' ref='321378456' role='' />\n    <member type='way' ref='30406550' role='' />\n    <member type='way' ref='843150466' role='' />\n    <member type='way' ref='30406546' role='' />\n    <member type='way' ref='843150464' role='' />\n    <member type='way' ref='144915853' role='' />\n    <member type='way' ref='144915854' role='' />\n    <member type='way' ref='144915851' role='' />\n    <member type='way' ref='492828707' role='' />\n    <member type='way' ref='123458827' role='' />\n    <member type='way' ref='30407255' role='' />\n    <member type='way' ref='492828705' role='' />\n    <member type='way' ref='123458828' role='' />\n    <member type='way' ref='144915855' role='' />\n    <member type='way' ref='627085345' role='' />\n    <member type='way' ref='633456015' role='' />\n    <member type='way' ref='51754191' role='' />\n    <member type='way' ref='38028830' role='' />\n    <member type='way' ref='38028831' role='' />\n    <member type='way' ref='119161422' role='' />\n    <member type='way' ref='119161418' role='' />\n    <member type='way' ref='241787612' role='' />\n    <member type='way' ref='833243911' role='' />\n    <member type='way' ref='241787611' role='' />\n    <member type='way' ref='241787610' role='' />\n    <member type='way' ref='125592653' role='' />\n    <member type='way' ref='241787616' role='' />\n    <member type='way' ref='234346247' role='' />\n    <member type='way' ref='125592652' role='' />\n    <member type='way' ref='492828700' role='' />\n    <member type='way' ref='241787618' role='' />\n    <member type='way' ref='33787295' role='' />\n    <member type='way' ref='492828701' role='' />\n    <member type='way' ref='23308640' role='' />\n    <member type='way' ref='319988267' role='' />\n    <member type='way' ref='241724478' role='' />\n    <member type='way' ref='319988209' role='' />\n    <member type='way' ref='319988213' role='' />\n    <member type='way' ref='319988203' role='' />\n    <member type='way' ref='319988225' role='' />\n    <member type='way' ref='118842804' role='' />\n    <member type='way' ref='118842790' role='' />\n    <member type='way' ref='319988231' role='' />\n    <member type='way' ref='683582708' role='' />\n    <member type='way' ref='358587890' role='' />\n    <member type='way' ref='319988188' role='' />\n    <member type='way' ref='118842803' role='' />\n    <member type='way' ref='683582710' role='' />\n    <member type='way' ref='118842805' role='' />\n    <member type='way' ref='118842812' role='' />\n    <member type='way' ref='92286848' role='' />\n    <member type='way' ref='633223284' role='' />\n    <member type='way' ref='319988236' role='' />\n    <member type='way' ref='633223273' role='' />\n    <member type='way' ref='632790909' role='' />\n    <member type='way' ref='237480738' role='' />\n    <member type='way' ref='81113481' role='' />\n    <member type='way' ref='632789483' role='' />\n    <member type='way' ref='23150407' role='' />\n    <member type='way' ref='23150408' role='' />\n    <member type='way' ref='23150406' role='' />\n    <member type='way' ref='632789481' role='' />\n    <member type='way' ref='92286833' role='' />\n    <member type='way' ref='92286866' role='' />\n    <member type='way' ref='632789477' role='' />\n    <member type='way' ref='632789458' role='' />\n    <member type='way' ref='632789460' role='' />\n    <member type='way' ref='23236479' role='' />\n    <member type='way' ref='459637986' role='' />\n    <member type='way' ref='297072044' role='' />\n    <member type='way' ref='88288530' role='' />\n    <member type='way' ref='88288521' role='' />\n    <member type='way' ref='358530815' role='' />\n    <member type='way' ref='297157693' role='' />\n    <member type='way' ref='632789460' role='' />\n    <member type='way' ref='632789457' role='' />\n    <member type='way' ref='673731742' role='' />\n    <member type='way' ref='632789456' role='' />\n    <member type='way' ref='65009887' role='' />\n    <member type='way' ref='65009888' role='' />\n    <member type='way' ref='23237104' role='' />\n    <member type='way' ref='497455256' role='' />\n    <member type='way' ref='497455255' role='' />\n    <member type='way' ref='92286832' role='' />\n    <member type='way' ref='320454515' role='' />\n    <member type='way' ref='320454494' role='' />\n    <member type='way' ref='22970260' role='' />\n    <member type='way' ref='92286869' role='' />\n    <member type='way' ref='92286876' role='' />\n    <member type='way' ref='632773389' role='' />\n    <member type='way' ref='234855460' role='' />\n    <member type='way' ref='320454517' role='' />\n    <member type='way' ref='320454487' role='' />\n    <member type='way' ref='320454499' role='' />\n    <member type='way' ref='320454512' role='' />\n    <member type='way' ref='320454501' role='' />\n    <member type='way' ref='320454477' role='' />\n    <member type='way' ref='320454483' role='' />\n    <member type='way' ref='320454482' role='' />\n    <member type='way' ref='320454491' role='' />\n    <member type='way' ref='320454513' role='' />\n    <member type='way' ref='320454507' role='' />\n    <member type='way' ref='320454506' role='' />\n    <member type='way' ref='320454473' role='' />\n    <member type='way' ref='320454498' role='' />\n    <member type='way' ref='320454493' role='' />\n    <member type='way' ref='320454505' role='' />\n    <member type='way' ref='26160622' role='' />\n    <member type='way' ref='88956595' role='' />\n    <member type='way' ref='320454476' role='' />\n    <member type='way' ref='320454502' role='' />\n    <member type='way' ref='320454520' role='' />\n    <member type='way' ref='88956579' role='' />\n    <member type='way' ref='367544799' role='' />\n    <member type='way' ref='320454492' role='' />\n    <member type='way' ref='320454474' role='' />\n    <member type='way' ref='88439022' role='' />\n    <member type='way' ref='320454500' role='' />\n    <member type='way' ref='88438981' role='' />\n    <member type='way' ref='492836255' role='' />\n    <member type='way' ref='51754192' role='' />\n    <member type='way' ref='386593765' role='' />\n    <member type='way' ref='676193412' role='' />\n    <member type='way' ref='4586020' role='' />\n    <member type='way' ref='125556146' role='' />\n    <member type='way' ref='2014317' role='' />\n    <member type='way' ref='4586931' role='' />\n    <member type='way' ref='9470519' role='' />\n    <member type='way' ref='26713012' role='' />\n    <member type='way' ref='297696236' role='' />\n    <member type='way' ref='297696237' role='' />\n    <member type='way' ref='22969810' role='' />\n    <member type='way' ref='58999665' role='' />\n    <member type='way' ref='672713513' role='' />\n    <member type='way' ref='58999666' role='' />\n    <member type='way' ref='62073052' role='' />\n    <member type='way' ref='62073051' role='' />\n    <member type='way' ref='59002628' role='' />\n    <member type='way' ref='254867496' role='' />\n    <member type='way' ref='254867498' role='' />\n    <member type='way' ref='35438709' role='' />\n    <member type='way' ref='761557032' role='' />\n    <member type='way' ref='23486731' role='' />\n    <member type='way' ref='513492326' role='' />\n    <member type='way' ref='810335209' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175447' role='' />\n    <member type='way' ref='369175445' role='' />\n    <member type='way' ref='369175441' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='664891127' role='' />\n    <member type='way' ref='29358757' role='' />\n    <member type='way' ref='52565598' role='' />\n    <member type='way' ref='23486732' role='' />\n    <member type='way' ref='449199493' role='' />\n    <member type='way' ref='449199494' role='' />\n    <member type='way' ref='23486733' role='' />\n    <member type='way' ref='40814531' role='' />\n    <member type='way' ref='40814532' role='' />\n    <member type='way' ref='213210051' role='' />\n    <member type='way' ref='30050896' role='' />\n    <member type='way' ref='664891129' role='' />\n    <member type='way' ref='40814026' role='' />\n    <member type='way' ref='40814025' role='' />\n    <member type='way' ref='53772038' role='' />\n    <member type='way' ref='53772035' role='' />\n    <member type='way' ref='24629915' role='' />\n    <member type='way' ref='135052326' role='' />\n    <member type='way' ref='578209354' role='' />\n    <member type='way' ref='366669796' role='' />\n    <member type='way' ref='366669793' role='' />\n    <member type='way' ref='219837015' role='' />\n    <member type='way' ref='497483693' role='' />\n    <member type='way' ref='219837016' role='' />\n    <member type='way' ref='675382052' role='' />\n    <member type='way' ref='672529639' role='' />\n    <member type='way' ref='672529638' role='' />\n    <member type='way' ref='173885536' role='' />\n    <member type='way' ref='173885534' role='' />\n    <member type='way' ref='539715724' role='' />\n    <member type='way' ref='322752604' role='' />\n    <member type='way' ref='660630405' role='' />\n    <member type='way' ref='53088754' role='' />\n    <member type='way' ref='30447471' role='' />\n    <member type='way' ref='30447472' role='' />\n    <member type='way' ref='4586920' role='' />\n    <member type='way' ref='4586917' role='' />\n    <member type='way' ref='29216340' role='' />\n    <member type='way' ref='833202603' role='' />\n    <member type='way' ref='4586916' role='' />\n    <member type='way' ref='833202570' role='' />\n    <member type='way' ref='165254731' role='' />\n    <member type='way' ref='165254734' role='' />\n    <member type='way' ref='4586912' role='' />\n    <member type='way' ref='833202567' role='' />\n    <member type='way' ref='125556125' role='' />\n    <member type='way' ref='27285022' role='' />\n    <member type='way' ref='204165652' role='' />\n    <member type='way' ref='204165653' role='' />\n    <member type='way' ref='661057632' role='' />\n    <member type='way' ref='661057631' role='' />\n    <member type='way' ref='661055344' role='' />\n    <member type='way' ref='4586904' role='' />\n    <member type='way' ref='4586901' role='' />\n    <member type='way' ref='661055345' role='' />\n    <member type='way' ref='833202566' role='' />\n    <member type='way' ref='161385861' role='' />\n    <member type='way' ref='161385866' role='' />\n    <member type='way' ref='23106925' role='' />\n    <member type='way' ref='23113983' role='' />\n    <member type='way' ref='4586896' role='' />\n    <member type='way' ref='665717758' role='' />\n    <member type='way' ref='23113972' role='' />\n    <member type='way' ref='23113973' role='' />\n    <member type='way' ref='4586893' role='' />\n    <member type='way' ref='473318560' role='' />\n    <member type='way' ref='169502264' role='' />\n    <member type='way' ref='169502265' role='' />\n    <member type='way' ref='473318566' role='' />\n    <member type='way' ref='473318567' role='' />\n    <member type='way' ref='473318571' role='' />\n    <member type='way' ref='473318570' role='' />\n    <member type='way' ref='2862595' role='' />\n    <member type='way' ref='22820722' role='' />\n    <member type='way' ref='22820721' role='' />\n    <member type='way' ref='576352094' role='' />\n    <member type='way' ref='22139184' role='' />\n    <member type='way' ref='38957691' role='' />\n    <member type='way' ref='26581486' role='' />\n    <member type='way' ref='56054084' role='' />\n    <member type='way' ref='22820758' role='' />\n    <member type='way' ref='56054081' role='' />\n    <member type='way' ref='5714523' role='' />\n    <member type='way' ref='710332450' role='' />\n    <member type='way' ref='22133739' role='' />\n    <member type='way' ref='22133741' role='' />\n    <member type='way' ref='25829540' role='' />\n    <member type='way' ref='5037793' role='' />\n    <member type='way' ref='595490508' role='' />\n    <member type='way' ref='313106227' role='' />\n    <member type='way' ref='377766354' role='' />\n    <member type='way' ref='47160379' role='' />\n    <member type='way' ref='248690767' role='' />\n    <member type='way' ref='536501275' role='' />\n    <member type='way' ref='536501276' role='' />\n    <member type='way' ref='315213104' role='' />\n    <member type='way' ref='23059551' role='' />\n    <member type='way' ref='167241864' role='' />\n    <member type='way' ref='169280772' role='' />\n    <member type='way' ref='60981193' role='' />\n    <member type='way' ref='377101890' role='' />\n    <member type='way' ref='377645413' role='' />\n    <member type='way' ref='61413097' role='' />\n    <member type='way' ref='377645414' role='' />\n    <member type='way' ref='377101892' role='' />\n    <member type='way' ref='377101889' role='' />\n    <member type='way' ref='377101891' role='' />\n    <member type='way' ref='377101894' role='' />\n    <member type='way' ref='169327831' role='' />\n    <member type='way' ref='377101888' role='' />\n    <member type='way' ref='377101893' role='' />\n    <member type='way' ref='214796416' role='' />\n    <member type='way' ref='5037792' role='' />\n    <member type='way' ref='23967768' role='' />\n    <member type='way' ref='23416611' role='' />\n    <member type='way' ref='87700223' role='' />\n    <member type='way' ref='23416591' role='' />\n    <member type='way' ref='424099552' role='' />\n    <member type='way' ref='514392684' role='' />\n    <member type='way' ref='32551883' role='' />\n    <member type='way' ref='198685165' role='' />\n    <member type='way' ref='198685161' role='' />\n    <member type='way' ref='32552096' role='' />\n    <member type='way' ref='32705199' role='' />\n    <member type='way' ref='728153623' role='' />\n    <member type='way' ref='26413433' role='' />\n    <member type='way' ref='129025644' role='' />\n    <member type='way' ref='32515008' role='' />\n    <member type='way' ref='23080117' role='' />\n    <member type='way' ref='198685161' role='' />\n    <member type='way' ref='198685165' role='' />\n    <member type='way' ref='32551883' role='' />\n    <member type='way' ref='514392684' role='' />\n    <member type='way' ref='32551882' role='' />\n    <member type='way' ref='120076961' role='' />\n    <member type='way' ref='228838849' role='' />\n    <member type='way' ref='5666001' role='' />\n    <member type='way' ref='424099565' role='' />\n    <member type='way' ref='5037792' role='' />\n    <member type='way' ref='214796416' role='' />\n    <member type='way' ref='377101893' role='' />\n    <member type='way' ref='377101888' role='' />\n    <member type='way' ref='169327831' role='' />\n    <member type='way' ref='377101894' role='' />\n    <member type='way' ref='377101891' role='' />\n    <member type='way' ref='377101889' role='' />\n    <member type='way' ref='377101892' role='' />\n    <member type='way' ref='377645414' role='' />\n    <member type='way' ref='61413097' role='' />\n    <member type='way' ref='377645413' role='' />\n    <member type='way' ref='377101890' role='' />\n    <member type='way' ref='60981193' role='' />\n    <member type='way' ref='169280772' role='' />\n    <member type='way' ref='167241864' role='' />\n    <member type='way' ref='23059551' role='' />\n    <member type='way' ref='315213107' role='' />\n    <member type='way' ref='456448588' role='' />\n    <member type='way' ref='26304647' role='' />\n    <member type='way' ref='26285180' role='' />\n    <member type='way' ref='118437407' role='' />\n    <member type='way' ref='23059553' role='' />\n    <member type='way' ref='23059550' role='' />\n    <member type='way' ref='25141834' role='' />\n    <member type='way' ref='23098190' role='' />\n    <member type='way' ref='118435201' role='' />\n    <member type='way' ref='229424413' role='' />\n    <member type='way' ref='225696853' role='' />\n    <member type='way' ref='232965550' role='' />\n    <member type='way' ref='46753569' role='' />\n    <member type='way' ref='232965547' role='' />\n    <member type='way' ref='22323313' role='' />\n    <member type='way' ref='22323312' role='' />\n    <member type='way' ref='27229879' role='' />\n    <member type='way' ref='232965546' role='' />\n    <member type='way' ref='232965536' role='' />\n    <member type='way' ref='232965545' role='' />\n    <member type='way' ref='232965540' role='' />\n    <member type='way' ref='222565247' role='' />\n    <member type='way' ref='222565258' role='' />\n    <member type='way' ref='38273286' role='' />\n    <member type='way' ref='38273287' role='' />\n    <member type='way' ref='232967035' role='' />\n    <member type='way' ref='26141368' role='' />\n    <member type='way' ref='26141369' role='' />\n    <member type='way' ref='232967034' role='' />\n    <member type='way' ref='25016480' role='' />\n    <member type='way' ref='324319697' role='' />\n    <member type='way' ref='324319704' role='' />\n    <member type='way' ref='324319701' role='' />\n    <member type='way' ref='324319692' role='' />\n    <member type='way' ref='98223254' role='' />\n    <member type='way' ref='23022584' role='' />\n    <member type='way' ref='232971058' role='' />\n    <member type='way' ref='232971057' role='' />\n    <member type='way' ref='232971056' role='' />\n    <member type='way' ref='232971061' role='' />\n    <member type='way' ref='232971054' role='' />\n    <member type='way' ref='232971060' role='' />\n    <member type='way' ref='232971053' role='' />\n    <member type='way' ref='232971050' role='' />\n    <member type='way' ref='232975921' role='' />\n    <member type='way' ref='4019587' role='' />\n    <member type='way' ref='232975931' role='' />\n    <member type='way' ref='232975927' role='' />\n    <member type='way' ref='231419621' role='' />\n    <member type='way' ref='231419618' role='' />\n    <member type='way' ref='56014873' role='' />\n    <member type='way' ref='56014879' role='' />\n    <member type='way' ref='226071295' role='' />\n    <member type='way' ref='479912112' role='' />\n    <member type='way' ref='479912113' role='' />\n    <member type='way' ref='479912111' role='' />\n    <member type='way' ref='226071297' role='' />\n    <member type='way' ref='535382427' role='' />\n    <member type='way' ref='535382428' role='' />\n    <member type='way' ref='232982162' role='' />\n    <member type='way' ref='232982165' role='' />\n    <member type='way' ref='232982164' role='' />\n    <member type='way' ref='232982163' role='' />\n    <member type='way' ref='232982158' role='' />\n    <member type='way' ref='535382791' role='' />\n    <member type='way' ref='535382790' role='' />\n    <member type='way' ref='72135463' role='' />\n    <member type='way' ref='72135461' role='' />\n    <member type='way' ref='144142223' role='' />\n    <member type='way' ref='546560759' role='' />\n    <member type='way' ref='546560758' role='' />\n    <member type='way' ref='144142217' role='' />\n    <member type='way' ref='546560754' role='' />\n    <member type='way' ref='546560755' role='' />\n    <member type='way' ref='82161936' role='' />\n    <member type='way' ref='82161935' role='' />\n    <member type='way' ref='232994363' role='' />\n    <member type='way' ref='232994366' role='' />\n    <member type='way' ref='4019526' role='' />\n    <member type='way' ref='124981042' role='' />\n    <member type='way' ref='232994371' role='' />\n    <member type='way' ref='31958022' role='' />\n    <member type='way' ref='697956271' role='' />\n    <member type='way' ref='31958023' role='' />\n    <member type='way' ref='22739406' role='' />\n    <member type='way' ref='33290789' role='' />\n    <member type='way' ref='374065359' role='' />\n    <member type='way' ref='33290790' role='' />\n    <member type='way' ref='479922389' role='' />\n    <member type='way' ref='479922388' role='' />\n    <member type='way' ref='22739428' role='' />\n    <member type='way' ref='479922387' role='' />\n    <member type='way' ref='479922386' role='' />\n    <member type='way' ref='28637348' role='' />\n    <member type='way' ref='24676963' role='' />\n    <member type='way' ref='233023663' role='' />\n    <member type='way' ref='22733416' role='' />\n    <member type='way' ref='233023669' role='' />\n    <member type='way' ref='144144936' role='' />\n    <member type='way' ref='144144929' role='' />\n    <member type='way' ref='683710988' role='' />\n    <member type='way' ref='144144906' role='' />\n    <member type='way' ref='233023671' role='' />\n    <member type='way' ref='531552422' role='' />\n    <member type='way' ref='233023653' role='' />\n    <member type='way' ref='233023649' role='' />\n    <member type='way' ref='233023651' role='' />\n    <member type='way' ref='233023652' role='' />\n    <member type='way' ref='233023655' role='' />\n    <member type='way' ref='233103733' role='' />\n    <member type='way' ref='233103737' role='' />\n    <member type='way' ref='233103734' role='' />\n    <member type='way' ref='233103730' role='' />\n    <member type='way' ref='31488554' role='' />\n    <member type='way' ref='31488553' role='' />\n    <member type='way' ref='44021695' role='' />\n    <member type='way' ref='44021692' role='' />\n    <member type='way' ref='233112539' role='' />\n    <member type='way' ref='233112553' role='' />\n    <member type='way' ref='233112543' role='' />\n    <member type='way' ref='233112556' role='' />\n    <member type='way' ref='9404399' role='' />\n    <member type='way' ref='26149319' role='' />\n    <member type='way' ref='26149324' role='' />\n    <member type='way' ref='19629534' role='' />\n    <member type='way' ref='33152027' role='' />\n    <member type='way' ref='33152020' role='' />\n    <member type='way' ref='324319581' role='' />\n    <member type='way' ref='324319593' role='' />\n    <member type='way' ref='324319571' role='' />\n    <member type='way' ref='324319572' role='' />\n    <member type='way' ref='73392896' role='' />\n    <member type='way' ref='240140720' role='' />\n    <member type='way' ref='240140721' role='' />\n    <member type='way' ref='73392900' role='' />\n    <member type='way' ref='29036125' role='' />\n    <member type='way' ref='19716510' role='' />\n    <member type='way' ref='448963802' role='' />\n    <member type='way' ref='448963803' role='' />\n    <member type='way' ref='240140718' role='' />\n    <member type='way' ref='749559788' role='' />\n    <member type='way' ref='34614767' role='' />\n    <member type='way' ref='733027972' role='' />\n    <member type='way' ref='678151500' role='' />\n    <member type='way' ref='76839458' role='' />\n    <member type='way' ref='733027970' role='' />\n    <member type='way' ref='4717259' role='' />\n    <member type='way' ref='296557880' role='' />\n    <member type='way' ref='153203960' role='' />\n    <member type='way' ref='76839452' role='' />\n    <member type='way' ref='274608780' role='' />\n    <member type='way' ref='274608779' role='' />\n    <member type='way' ref='274606794' role='' />\n    <member type='way' ref='144053947' role='' />\n    <member type='way' ref='274606795' role='' />\n    <member type='way' ref='274606796' role='' />\n    <member type='way' ref='274606797' role='' />\n    <member type='way' ref='37352317' role='' />\n    <member type='way' ref='7998655' role='' />\n    <member type='way' ref='29650025' role='' />\n    <member type='way' ref='296557881' role='' />\n    <member type='way' ref='779492336' role='' />\n    <member type='way' ref='562643469' role='' />\n    <member type='way' ref='433798771' role='' />\n    <member type='way' ref='379519259' role='' />\n    <member type='way' ref='236855736' role='' />\n    <member type='way' ref='236855733' role='' />\n    <member type='way' ref='236855737' role='' />\n    <member type='way' ref='121243050' role='' />\n    <member type='way' ref='39337118' role='' />\n    <member type='way' ref='733372950' role='' />\n    <member type='way' ref='733372949' role='' />\n    <member type='way' ref='30809509' role='' />\n    <member type='way' ref='275422324' role='' />\n    <member type='way' ref='275422323' role='' />\n    <member type='way' ref='275422325' role='' />\n    <member type='way' ref='121243055' role='' />\n    <member type='way' ref='8666488' role='' />\n    <member type='way' ref='498866393' role='' />\n    <member type='way' ref='28060354' role='' />\n    <member type='way' ref='29651991' role='' />\n    <member type='way' ref='28150271' role='' />\n    <member type='way' ref='314450784' role='' />\n    <member type='way' ref='776093119' role='' />\n    <member type='way' ref='776093120' role='' />\n    <member type='way' ref='138982590' role='' />\n    <member type='way' ref='67153451' role='' />\n    <member type='way' ref='46739386' role='' />\n    <member type='way' ref='40033980' role='' />\n    <member type='way' ref='8666490' role='' />\n    <member type='way' ref='314450772' role='' />\n    <member type='way' ref='314450782' role='' />\n    <member type='way' ref='314450780' role='' />\n    <member type='way' ref='682144620' role='' />\n    <member type='way' ref='280958658' role='' />\n    <member type='way' ref='682144621' role='' />\n    <member type='way' ref='682144620' role='' />\n    <member type='way' ref='682144623' role='' />\n    <member type='way' ref='314450779' role='' />\n    <member type='way' ref='314450781' role='' />\n    <member type='way' ref='314450775' role='' />\n    <member type='way' ref='138982589' role='' />\n    <member type='way' ref='183984419' role='' />\n    <member type='way' ref='314450776' role='' />\n    <member type='way' ref='47252827' role='' />\n    <member type='way' ref='40033158' role='' />\n    <member type='way' ref='67153460' role='' />\n    <member type='way' ref='314450777' role='' />\n    <member type='way' ref='314450774' role='' />\n    <member type='way' ref='314450778' role='' />\n    <member type='way' ref='28150896' role='' />\n    <member type='way' ref='543196765' role='' />\n    <member type='way' ref='520405269' role='' />\n    <member type='way' ref='379812994' role='' />\n    <member type='way' ref='28150250' role='' />\n    <member type='way' ref='379852448' role='' />\n    <member type='way' ref='37831194' role='' />\n    <member type='way' ref='8666488' role='' />\n    <member type='way' ref='121243055' role='' />\n    <member type='way' ref='275422325' role='' />\n    <member type='way' ref='275422323' role='' />\n    <member type='way' ref='275422324' role='' />\n    <member type='way' ref='30809517' role='' />\n    <member type='way' ref='13641245' role='' />\n    <member type='way' ref='275422317' role='' />\n    <member type='way' ref='578627081' role='' />\n    <member type='way' ref='275422314' role='' />\n    <member type='way' ref='36933287' role='' />\n    <member type='way' ref='28222985' role='' />\n    <member type='way' ref='433798767' role='' />\n    <member type='way' ref='36780903' role='' />\n    <member type='way' ref='379519260' role='' />\n    <member type='way' ref='531462061' role='' />\n    <member type='way' ref='68244365' role='' />\n    <member type='way' ref='46739384' role='' />\n    <member type='way' ref='531462062' role='' />\n    <member type='way' ref='236855738' role='' />\n    <member type='way' ref='779254719' role='' />\n    <member type='way' ref='779254717' role='' />\n    <member type='way' ref='779254718' role='' />\n    <member type='way' ref='779254713' role='' />\n    <member type='way' ref='779254721' role='' />\n    <member type='way' ref='779254722' role='' />\n    <member type='way' ref='601394137' role='' />\n    <member type='way' ref='144053949' role='' />\n    <member type='way' ref='274513688' role='' />\n    <member type='way' ref='274513690' role='' />\n    <member type='way' ref='274513686' role='' />\n    <member type='way' ref='274513681' role='' />\n    <member type='way' ref='48178462' role='' />\n    <member type='way' ref='5012656' role='' />\n    <member type='way' ref='37352363' role='' />\n    <member type='way' ref='47796988' role='' />\n    <member type='way' ref='144053946' role='' />\n    <member type='way' ref='153203961' role='' />\n    <member type='way' ref='274608055' role='' />\n    <member type='way' ref='19714183' role='' />\n    <member type='way' ref='69288832' role='' />\n    <member type='way' ref='235018086' role='' />\n    <member type='way' ref='34614766' role='' />\n    <member type='way' ref='339327768' role='' />\n    <member type='way' ref='122140902' role='' />\n    <member type='way' ref='122140901' role='' />\n    <member type='way' ref='122140903' role='' />\n    <member type='way' ref='339945820' role='' />\n    <member type='way' ref='22418939' role='' />\n    <member type='way' ref='22418929' role='' />\n    <member type='way' ref='274498579' role='' />\n    <member type='way' ref='27715970' role='' />\n    <member type='way' ref='22819099' role='' />\n    <member type='way' ref='26832683' role='' />\n    <member type='way' ref='22819098' role='' />\n    <member type='way' ref='274498578' role='' />\n    <member type='way' ref='274498577' role='' />\n    <member type='way' ref='485851171' role='' />\n    <member type='way' ref='485851175' role='' />\n    <member type='way' ref='274498580' role='' />\n    <member type='way' ref='485851178' role='' />\n    <member type='way' ref='485851176' role='' />\n    <member type='way' ref='31978382' role='' />\n    <member type='way' ref='31978383' role='' />\n    <member type='way' ref='26821947' role='' />\n    <member type='way' ref='26821946' role='' />\n    <member type='way' ref='32454975' role='' />\n    <member type='way' ref='32454976' role='' />\n    <member type='way' ref='240745991' role='' />\n    <member type='way' ref='5078262' role='' />\n    <member type='way' ref='240745990' role='' />\n    <member type='way' ref='240745989' role='' />\n    <member type='way' ref='240745995' role='' />\n    <member type='way' ref='153203959' role='' />\n    <member type='way' ref='153203962' role='' />\n    <member type='way' ref='142699272' role='' />\n    <member type='way' ref='531547514' role='' />\n    <member type='way' ref='466040279' role='' />\n    <member type='way' ref='233077311' role='' />\n    <member type='way' ref='46742311' role='' />\n    <member type='way' ref='233077309' role='' />\n    <member type='way' ref='233077310' role='' />\n    <member type='way' ref='233077317' role='' />\n    <member type='way' ref='32337799' role='' />\n    <member type='way' ref='32337800' role='' />\n    <member type='way' ref='59572345' role='' />\n    <member type='way' ref='690686298' role='' />\n    <member type='way' ref='233077312' role='' />\n    <member type='way' ref='233077316' role='' />\n    <member type='way' ref='639784304' role='' />\n    <member type='way' ref='233077314' role='' />\n    <member type='way' ref='655183564' role='' />\n    <member type='way' ref='233077315' role='' />\n    <member type='way' ref='32255543' role='' />\n    <member type='way' ref='32255544' role='' />\n    <member type='way' ref='233078181' role='' />\n    <member type='way' ref='233078182' role='' />\n    <member type='way' ref='46742305' role='' />\n    <member type='way' ref='316452178' role='' />\n    <member type='way' ref='605712116' role='' />\n    <member type='way' ref='690686296' role='' />\n    <member type='way' ref='30527960' role='' />\n    <member type='way' ref='30527961' role='' />\n    <member type='way' ref='30527964' role='' />\n    <member type='way' ref='30527965' role='' />\n    <member type='way' ref='690686295' role='' />\n    <member type='way' ref='4993377' role='' />\n    <member type='way' ref='60660651' role='' />\n    <member type='way' ref='60660644' role='' />\n    <member type='way' ref='60660648' role='' />\n    <member type='way' ref='4993375' role='' />\n    <member type='way' ref='473334708' role='' />\n    <member type='way' ref='655183563' role='' />\n    <member type='way' ref='376872512' role='' />\n    <member type='way' ref='744330328' role='' />\n    <member type='way' ref='744330329' role='' />\n    <member type='way' ref='690566790' role='' />\n    <member type='way' ref='690566789' role='' />\n    <member type='way' ref='473334704' role='' />\n    <member type='way' ref='233079511' role='' />\n    <member type='way' ref='462382491' role='' />\n    <member type='way' ref='744330331' role='' />\n    <member type='way' ref='744330330' role='' />\n    <member type='way' ref='527281500' role='' />\n    <member type='way' ref='462382489' role='' />\n    <member type='way' ref='462382490' role='' />\n    <member type='way' ref='462382390' role='' />\n    <member type='way' ref='462382469' role='' />\n    <member type='way' ref='83174291' role='' />\n    <member type='way' ref='83174289' role='' />\n    <member type='way' ref='51435392' role='' />\n    <member type='way' ref='51434522' role='' />\n    <member type='way' ref='473334700' role='' />\n    <member type='way' ref='233081271' role='' />\n    <member type='way' ref='473334696' role='' />\n    <member type='way' ref='233081266' role='' />\n    <member type='way' ref='690558802' role='' />\n    <member type='way' ref='690558805' role='' />\n    <member type='way' ref='34881622' role='' />\n    <member type='way' ref='34881623' role='' />\n    <member type='way' ref='35723980' role='' />\n    <member type='way' ref='233082562' role='' />\n    <member type='way' ref='473334693' role='' />\n    <member type='way' ref='80444588' role='' />\n    <member type='way' ref='473334690' role='' />\n    <member type='way' ref='544212748' role='' />\n    <member type='way' ref='473334676' role='' />\n    <member type='way' ref='473334669' role='' />\n    <member type='way' ref='473334666' role='' />\n    <member type='way' ref='473334680' role='' />\n    <member type='way' ref='690686291' role='' />\n    <member type='way' ref='690686292' role='' />\n    <member type='way' ref='473334665' role='' />\n    <member type='way' ref='233083910' role='' />\n    <member type='way' ref='473334664' role='' />\n    <member type='way' ref='603491077' role='' />\n    <member type='way' ref='690686290' role='' />\n    <member type='way' ref='10711346' role='' />\n    <member type='way' ref='118295285' role='' />\n    <member type='way' ref='10711344' role='' />\n    <member type='way' ref='690918955' role='' />\n    <member type='way' ref='10711345' role='' />\n    <member type='way' ref='473334663' role='' />\n    <member type='way' ref='324251361' role='' />\n    <member type='way' ref='80444587' role='' />\n    <member type='way' ref='473334662' role='' />\n    <member type='way' ref='527281495' role='' />\n    <member type='way' ref='540939259' role='' />\n    <member type='way' ref='34379686' role='' />\n    <member type='way' ref='527281490' role='' />\n    <member type='way' ref='760878206' role='' />\n    <member type='way' ref='760878201' role='' />\n    <member type='way' ref='760878205' role='' />\n    <member type='way' ref='683695048' role='' />\n    <member type='way' ref='683695050' role='' />\n    <member type='way' ref='683695049' role='' />\n    <member type='way' ref='655183558' role='' />\n    <member type='way' ref='655183559' role='' />\n    <member type='way' ref='690686289' role='' />\n    <member type='way' ref='46742299' role='' />\n    <member type='way' ref='760878207' role='' />\n    <member type='way' ref='507638867' role='' />\n    <member type='way' ref='507638866' role='' />\n    <member type='way' ref='276183954' role='' />\n    <member type='way' ref='276183953' role='' />\n    <member type='way' ref='10713029' role='' />\n    <member type='way' ref='103843191' role='' />\n    <member type='way' ref='136485976' role='' />\n    <member type='way' ref='46742298' role='' />\n    <member type='way' ref='103843186' role='' />\n    <member type='way' ref='103843194' role='' />\n    <member type='way' ref='370452150' role='' />\n    <member type='way' ref='25896789' role='' />\n    <member type='way' ref='220651711' role='' />\n    <member type='way' ref='220651734' role='' />\n    <member type='way' ref='220651717' role='' />\n    <member type='way' ref='220651731' role='' />\n    <member type='way' ref='220651720' role='' />\n    <member type='way' ref='220651726' role='' />\n    <member type='way' ref='210197354' role='' />\n    <member type='way' ref='101963185' role='' />\n    <member type='way' ref='23300343' role='' />\n    <member type='way' ref='749892950' role='' />\n    <member type='way' ref='101963180' role='' />\n    <member type='way' ref='220656004' role='' />\n    <member type='way' ref='220656009' role='' />\n    <member type='way' ref='31061293' role='' />\n    <member type='way' ref='220656010' role='' />\n    <member type='way' ref='206412995' role='' />\n    <member type='way' ref='220656007' role='' />\n    <member type='way' ref='129788406' role='' />\n    <member type='way' ref='136090925' role='' />\n    <member type='way' ref='4329503' role='' />\n    <member type='way' ref='591348087' role='' />\n    <member type='way' ref='21306265' role='' />\n    <member type='way' ref='591348092' role='' />\n    <member type='way' ref='149764730' role='' />\n    <member type='way' ref='4329497' role='' />\n    <member type='way' ref='314151452' role='' />\n    <member type='way' ref='15950730' role='' />\n    <member type='way' ref='314151437' role='' />\n    <member type='way' ref='314151447' role='' />\n    <member type='way' ref='15950733' role='' />\n    <member type='way' ref='314151443' role='' />\n    <member type='way' ref='314151445' role='' />\n    <member type='way' ref='320718318' role='' />\n    <member type='way' ref='40001005' role='' />\n    <member type='way' ref='40001019' role='' />\n    <member type='way' ref='48747587' role='' />\n    <member type='way' ref='659872938' role='' />\n    <member type='way' ref='48755060' role='' />\n    <member type='way' ref='48755061' role='' />\n    <member type='way' ref='320874036' role='' />\n    <member type='way' ref='40000996' role='' />\n    <member type='way' ref='40001025' role='' />\n    <member type='way' ref='313936248' role='' />\n    <member type='way' ref='313936246' role='' />\n    <member type='way' ref='313936247' role='' />\n    <member type='way' ref='21346118' role='' />\n    <member type='way' ref='4041761' role='' />\n    <member type='way' ref='186920101' role='' />\n    <member type='way' ref='129691424' role='' />\n    <member type='way' ref='40001135' role='' />\n    <member type='way' ref='52018436' role='' />\n    <member type='way' ref='28909487' role='' />\n    <member type='way' ref='55927147' role='' />\n    <member type='way' ref='55927144' role='' />\n    <member type='way' ref='42177282' role='' />\n    <member type='way' ref='38279832' role='' />\n    <member type='way' ref='38624778' role='' />\n    <member type='way' ref='38624776' role='' />\n    <member type='way' ref='42986369' role='' />\n    <member type='way' ref='545828292' role='' />\n    <member type='way' ref='545828291' role='' />\n    <member type='way' ref='143824763' role='' />\n    <member type='way' ref='522998641' role='' />\n    <member type='way' ref='143824766' role='' />\n    <member type='way' ref='85614232' role='' />\n    <member type='way' ref='143824758' role='' />\n    <member type='way' ref='40127753' role='' />\n    <member type='way' ref='143824791' role='' />\n    <member type='way' ref='135963890' role='' />\n    <member type='way' ref='40127754' role='' />\n    <member type='way' ref='85614231' role='' />\n    <member type='way' ref='44867404' role='' />\n    <member type='way' ref='44867405' role='' />\n    <member type='way' ref='584708064' role='' />\n    <member type='way' ref='44867406' role='' />\n    <member type='way' ref='584708066' role='' />\n    <member type='way' ref='135652407' role='' />\n    <member type='way' ref='135652411' role='' />\n    <member type='way' ref='135652406' role='' />\n    <member type='way' ref='584708062' role='' />\n    <member type='way' ref='584708063' role='' />\n    <member type='way' ref='44867408' role='' />\n    <member type='way' ref='142718460' role='' />\n    <member type='way' ref='584708047' role='' />\n    <member type='way' ref='142718459' role='' />\n    <member type='way' ref='612753195' role='' />\n    <member type='way' ref='612753194' role='' />\n    <member type='way' ref='142709208' role='' />\n    <member type='way' ref='142718455' role='' />\n    <member type='way' ref='137247325' role='' />\n    <member type='way' ref='339819551' role='' />\n    <member type='way' ref='44654907' role='' />\n    <member type='way' ref='44372254' role='' />\n    <member type='way' ref='142762608' role='' />\n    <member type='way' ref='339819560' role='' />\n    <member type='way' ref='42878786' role='' />\n    <member type='way' ref='26812424' role='' />\n    <member type='way' ref='391821262' role='' />\n    <member type='way' ref='50390278' role='' />\n    <member type='way' ref='391821253' role='' />\n    <member type='way' ref='236098423' role='' />\n    <member type='way' ref='137154559' role='' />\n    <member type='way' ref='27119372' role='' />\n    <member type='way' ref='114929182' role='' />\n    <member type='way' ref='114929184' role='' />\n    <member type='way' ref='137154550' role='' />\n    <member type='way' ref='82312392' role='' />\n    <member type='way' ref='558852864' role='' />\n    <member type='way' ref='391821209' role='' />\n    <member type='way' ref='391821220' role='' />\n    <member type='way' ref='391821212' role='' />\n    <member type='way' ref='391821216' role='' />\n    <member type='way' ref='391821219' role='' />\n    <member type='way' ref='391821202' role='' />\n    <member type='way' ref='391801520' role='' />\n    <member type='way' ref='391801514' role='' />\n    <member type='way' ref='31821587' role='' />\n    <member type='way' ref='495849382' role='' />\n    <member type='way' ref='391801516' role='' />\n    <member type='way' ref='391801513' role='' />\n    <member type='way' ref='391801510' role='' />\n    <member type='way' ref='49056723' role='' />\n    <member type='way' ref='521045512' role='' />\n    <member type='way' ref='391787295' role='' />\n    <member type='way' ref='391787293' role='' />\n    <member type='way' ref='391787290' role='' />\n    <member type='way' ref='391787287' role='' />\n    <member type='way' ref='39088985' role='' />\n    <member type='way' ref='39097668' role='' />\n    <member type='way' ref='381750332' role='' />\n    <member type='way' ref='26621012' role='' />\n    <member type='way' ref='521045514' role='' />\n    <member type='way' ref='24483185' role='' />\n    <member type='way' ref='521045513' role='' />\n    <member type='way' ref='381750330' role='' />\n    <member type='way' ref='44532531' role='' />\n    <member type='way' ref='199780796' role='' />\n    <member type='way' ref='141050972' role='' />\n    <member type='way' ref='381750336' role='' />\n    <member type='way' ref='51196062' role='' />\n    <member type='way' ref='199239120' role='' />\n    <member type='way' ref='199753406' role='' />\n    <member type='way' ref='142770547' role='' />\n    <member type='way' ref='142692918' role='' />\n    <member type='way' ref='199752605' role='' />\n    <member type='way' ref='199752604' role='' />\n    <member type='way' ref='199239119' role='' />\n    <member type='way' ref='41676176' role='' />\n    <member type='way' ref='158491027' role='' />\n    <member type='way' ref='158491026' role='' />\n    <member type='way' ref='186713411' role='' />\n    <member type='way' ref='186713406' role='' />\n    <member type='way' ref='551303414' role='' />\n    <member type='way' ref='186713410' role='' />\n    <member type='way' ref='186713407' role='' />\n    <member type='way' ref='51200768' role='' />\n    <member type='way' ref='551303417' role='' />\n    <member type='way' ref='186722706' role='' />\n    <member type='way' ref='551303416' role='' />\n    <member type='way' ref='551303423' role='' />\n    <member type='way' ref='551303424' role='' />\n    <member type='way' ref='189775480' role='' />\n    <member type='way' ref='186821034' role='' />\n    <member type='way' ref='186821031' role='' />\n    <member type='way' ref='186821036' role='' />\n    <member type='way' ref='142944477' role='' />\n    <member type='way' ref='187761803' role='' />\n    <member type='way' ref='591214268' role='' />\n    <member type='way' ref='142944471' role='' />\n    <member type='way' ref='450274068' role='' />\n    <member type='way' ref='31009399' role='' />\n    <member type='way' ref='187761801' role='' />\n    <member type='way' ref='572210301' role='' />\n    <member type='way' ref='142944469' role='' />\n    <member type='way' ref='33041278' role='' />\n    <member type='way' ref='143586365' role='' />\n    <member type='way' ref='27805056' role='' />\n    <member type='way' ref='39981139' role='' />\n    <member type='way' ref='528961136' role='' />\n    <member type='way' ref='528961133' role='' />\n    <member type='way' ref='23782998' role='' />\n    <member type='way' ref='640955497' role='' />\n    <member type='way' ref='659046263' role='' />\n    <member type='way' ref='647444124' role='' />\n    <member type='way' ref='659046260' role='' />\n    <member type='way' ref='659046262' role='' />\n    <member type='way' ref='659046261' role='' />\n    <member type='way' ref='680624628' role='' />\n    <member type='way' ref='680624629' role='' />\n    <member type='way' ref='659046255' role='' />\n    <member type='way' ref='659046259' role='' />\n    <member type='way' ref='685314679' role='' />\n    <member type='way' ref='282898405' role='' />\n    <member type='way' ref='282898405' role='' />\n    <member type='way' ref='28724405' role='' />\n    <member type='way' ref='282898410' role='' />\n    <tag k='from' v='Aalborg, Busbahnhof' />\n    <tag k='name' v='Flixbus 172: Aalborg, Busbahnhof =&gt; Hamburg ZOB' />\n    <tag k='name:da' v='Flixbus 172: Aalborg, busstation =&gt; Hamburg central busstation' />\n    <tag k='name:de' v='Flixbus 172: Aalborg, Busbahnhof =&gt; Hamburg ZOB' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='FLX172' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Hamburg ZOB' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='10751456' timestamp='2020-02-25T17:53:05Z' uid='7774062' user='LessThan3Nodes' visible='true' version='1' changeset='81467073'>\n    <member type='way' ref='681143380' role='from' />\n    <member type='way' ref='495987823' role='via' />\n    <member type='way' ref='681139362' role='to' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10751457' timestamp='2020-02-25T17:53:05Z' uid='7774062' user='LessThan3Nodes' visible='true' version='1' changeset='81467073'>\n    <member type='way' ref='495987822' role='from' />\n    <member type='node' ref='29971833' role='via' />\n    <member type='way' ref='681139362' role='to' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='11300149' timestamp='2020-08-05T00:45:18Z' uid='2677992' user='mikkolukas' visible='true' version='4' changeset='88951882'>\n    <member type='way' ref='825971667' role='' />\n    <member type='way' ref='83807619' role='' />\n    <member type='way' ref='619706757' role='' />\n    <member type='way' ref='619706756' role='' />\n    <member type='way' ref='548304978' role='' />\n    <member type='way' ref='782010053' role='' />\n    <member type='way' ref='458620850' role='' />\n    <member type='way' ref='458620855' role='forward' />\n    <member type='way' ref='458620858' role='forward' />\n    <member type='way' ref='458620851' role='forward' />\n    <member type='way' ref='458620854' role='forward' />\n    <member type='way' ref='458620858' role='forward' />\n    <member type='way' ref='28447931' role='forward' />\n    <member type='way' ref='458620852' role='' />\n    <member type='way' ref='458620853' role='' />\n    <member type='way' ref='93671580' role='' />\n    <member type='way' ref='89485855' role='forward' />\n    <member type='way' ref='433051009' role='forward' />\n    <member type='way' ref='433051006' role='forward' />\n    <member type='way' ref='765560645' role='forward' />\n    <member type='way' ref='247163321' role='forward' />\n    <member type='way' ref='433051004' role='forward' />\n    <member type='way' ref='433051011' role='forward' />\n    <member type='way' ref='7848444' role='forward' />\n    <member type='way' ref='495973348' role='forward' />\n    <member type='way' ref='93937844' role='forward' />\n    <member type='way' ref='681143394' role='forward' />\n    <member type='way' ref='495973346' role='forward' />\n    <member type='way' ref='52916848' role='forward' />\n    <member type='way' ref='681139350' role='forward' />\n    <member type='way' ref='71756291' role='forward' />\n    <member type='way' ref='384190946' role='forward' />\n    <member type='way' ref='681139356' role='forward' />\n    <member type='way' ref='71756288' role='forward' />\n    <member type='way' ref='498604201' role='forward' />\n    <member type='way' ref='681139367' role='forward' />\n    <member type='way' ref='107114265' role='forward' />\n    <member type='way' ref='275143515' role='forward' />\n    <member type='way' ref='825980029' role='forward' />\n    <member type='way' ref='779192146' role='forward' />\n    <member type='way' ref='779192145' role='forward' />\n    <member type='way' ref='825980013' role='forward' />\n    <member type='way' ref='368771669' role='forward' />\n    <member type='way' ref='825980021' role='forward' />\n    <member type='way' ref='368771668' role='forward' />\n    <member type='way' ref='825980025' role='forward' />\n    <member type='way' ref='825980024' role='forward' />\n    <member type='way' ref='286684698' role='forward' />\n    <member type='way' ref='809368764' role='forward' />\n    <member type='way' ref='809368756' role='forward' />\n    <member type='way' ref='437244814' role='forward' />\n    <member type='way' ref='437244815' role='forward' />\n    <member type='way' ref='369772040' role='forward' />\n    <member type='way' ref='4579004' role='forward' />\n    <member type='way' ref='394522512' role='forward' />\n    <member type='way' ref='4565214' role='forward' />\n    <member type='way' ref='394522514' role='forward' />\n    <member type='way' ref='369772025' role='forward' />\n    <member type='way' ref='681474611' role='forward' />\n    <member type='way' ref='286684703' role='forward' />\n    <member type='way' ref='286684707' role='forward' />\n    <member type='way' ref='286684706' role='forward' />\n    <member type='way' ref='363050272' role='forward' />\n    <member type='way' ref='128572674' role='forward' />\n    <member type='way' ref='369182777' role='forward' />\n    <member type='way' ref='332265005' role='forward' />\n    <member type='way' ref='247163331' role='forward' />\n    <member type='way' ref='433051007' role='forward' />\n    <member type='way' ref='765560644' role='forward' />\n    <member type='way' ref='433051008' role='forward' />\n    <member type='way' ref='8150204' role='forward' />\n    <member type='way' ref='433051005' role='forward' />\n    <member type='way' ref='433051010' role='forward' />\n    <member type='way' ref='7848444' role='forward' />\n    <member type='way' ref='93937341' role='forward' />\n    <member type='way' ref='681143382' role='forward' />\n    <member type='way' ref='495973345' role='forward' />\n    <member type='way' ref='52916945' role='forward' />\n    <member type='way' ref='37758197' role='forward' />\n    <member type='way' ref='681139345' role='forward' />\n    <member type='way' ref='8041862' role='forward' />\n    <member type='way' ref='681139343' role='forward' />\n    <member type='way' ref='681139359' role='forward' />\n    <member type='way' ref='681139368' role='forward' />\n    <member type='way' ref='681139362' role='forward' />\n    <member type='way' ref='275143512' role='forward' />\n    <member type='way' ref='275143505' role='forward' />\n    <member type='way' ref='779192151' role='forward' />\n    <member type='way' ref='825980017' role='forward' />\n    <member type='way' ref='825980018' role='forward' />\n    <member type='way' ref='286851410' role='forward' />\n    <member type='way' ref='496120879' role='forward' />\n    <member type='way' ref='681376586' role='forward' />\n    <member type='way' ref='681376587' role='forward' />\n    <member type='way' ref='496107796' role='forward' />\n    <member type='way' ref='369772029' role='forward' />\n    <member type='way' ref='496355624' role='forward' />\n    <member type='way' ref='8041859' role='forward' />\n    <member type='way' ref='496115147' role='forward' />\n    <member type='way' ref='286684701' role='forward' />\n    <member type='way' ref='809357629' role='forward' />\n    <member type='way' ref='286684709' role='forward' />\n    <member type='way' ref='286684697' role='forward' />\n    <member type='way' ref='834051030' role='forward' />\n    <member type='way' ref='363050791' role='forward' />\n    <member type='way' ref='834051023' role='forward' />\n    <tag k='name' v='Visseruten' />\n    <tag k='network' v='lcn' />\n    <tag k='ref' v='600' />\n    <tag k='route' v='bicycle' />\n    <tag k='source' v='http://www.aalborgcykelby.dk/media/8918992/cykelstikort2018.pdf' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='11375177' timestamp='2020-09-25T08:23:14Z' uid='36080' user='nimapper' visible='true' version='11' changeset='91493616'>\n    <member type='node' ref='6448447785' role='stop' />\n    <member type='node' ref='6735247234' role='platform' />\n    <member type='node' ref='458718203' role='stop' />\n    <member type='node' ref='1938160930' role='platform' />\n    <member type='node' ref='7778082454' role='stop' />\n    <member type='node' ref='7778082455' role='platform' />\n    <member type='node' ref='1987802672' role='stop' />\n    <member type='node' ref='7647740030' role='platform' />\n    <member type='node' ref='1987802673' role='stop' />\n    <member type='node' ref='7647758213' role='platform' />\n    <member type='node' ref='1987802675' role='stop' />\n    <member type='node' ref='7647758212' role='platform' />\n    <member type='node' ref='1987802677' role='stop' />\n    <member type='node' ref='7647758211' role='platform' />\n    <member type='node' ref='1987802678' role='stop' />\n    <member type='node' ref='7647758210' role='platform' />\n    <member type='node' ref='7779170659' role='stop' />\n    <member type='node' ref='7650108367' role='platform' />\n    <member type='node' ref='6827125634' role='stop' />\n    <member type='node' ref='3393833062' role='stop' />\n    <member type='way' ref='332263376' role='platform' />\n    <member type='way' ref='833222412' role='' />\n    <member type='way' ref='488272326' role='' />\n    <member type='way' ref='488272325' role='' />\n    <member type='way' ref='497022315' role='' />\n    <member type='way' ref='38069237' role='' />\n    <member type='way' ref='92162861' role='' />\n    <member type='way' ref='38069236' role='' />\n    <member type='way' ref='386834369' role='' />\n    <member type='way' ref='38069234' role='' />\n    <member type='way' ref='503035505' role='' />\n    <member type='way' ref='38069238' role='' />\n    <member type='way' ref='4230685' role='' />\n    <member type='way' ref='107382822' role='' />\n    <member type='way' ref='357266389' role='' />\n    <member type='way' ref='357266388' role='' />\n    <member type='way' ref='386834392' role='' />\n    <member type='way' ref='30791228' role='' />\n    <member type='way' ref='119719508' role='' />\n    <member type='way' ref='30791016' role='' />\n    <member type='way' ref='140293280' role='' />\n    <member type='way' ref='357265305' role='' />\n    <member type='way' ref='357265309' role='' />\n    <member type='way' ref='357265310' role='' />\n    <member type='way' ref='386834388' role='' />\n    <member type='way' ref='27224364' role='' />\n    <member type='way' ref='689240166' role='' />\n    <member type='way' ref='121257657' role='' />\n    <member type='way' ref='10631185' role='' />\n    <member type='way' ref='810350765' role='' />\n    <member type='way' ref='422789581' role='' />\n    <member type='way' ref='4327218' role='' />\n    <member type='way' ref='422789574' role='' />\n    <member type='way' ref='46244971' role='' />\n    <member type='way' ref='108849017' role='' />\n    <member type='way' ref='3676667' role='' />\n    <member type='way' ref='422789543' role='' />\n    <member type='way' ref='108849007' role='' />\n    <member type='way' ref='3542463' role='' />\n    <member type='way' ref='422789545' role='' />\n    <member type='way' ref='422789546' role='' />\n    <member type='way' ref='237108787' role='' />\n    <member type='way' ref='422789541' role='' />\n    <member type='way' ref='422789547' role='' />\n    <member type='way' ref='191619082' role='' />\n    <member type='way' ref='422789565' role='' />\n    <member type='way' ref='422789560' role='' />\n    <member type='way' ref='422789561' role='' />\n    <member type='way' ref='90714003' role='' />\n    <member type='way' ref='422789563' role='' />\n    <member type='way' ref='672675018' role='' />\n    <member type='way' ref='90527580' role='' />\n    <member type='way' ref='184021717' role='' />\n    <member type='way' ref='90527578' role='' />\n    <member type='way' ref='422789475' role='' />\n    <member type='way' ref='422789474' role='' />\n    <member type='way' ref='683577496' role='' />\n    <member type='way' ref='750435704' role='' />\n    <member type='way' ref='210642683' role='' />\n    <member type='way' ref='27409295' role='' />\n    <member type='way' ref='27409296' role='' />\n    <member type='way' ref='496762358' role='' />\n    <member type='way' ref='448963756' role='' />\n    <member type='way' ref='751162440' role='' />\n    <member type='way' ref='121284723' role='' />\n    <member type='way' ref='751162442' role='' />\n    <member type='way' ref='751162441' role='' />\n    <member type='way' ref='496720678' role='' />\n    <member type='way' ref='388070331' role='' />\n    <member type='way' ref='496703336' role='' />\n    <member type='way' ref='121284722' role='' />\n    <member type='way' ref='515410740' role='' />\n    <member type='way' ref='38702782' role='' />\n    <member type='way' ref='751162443' role='' />\n    <member type='way' ref='38702777' role='' />\n    <member type='way' ref='751162444' role='' />\n    <member type='way' ref='38702744' role='' />\n    <member type='way' ref='85735262' role='' />\n    <member type='way' ref='776071648' role='' />\n    <member type='way' ref='168269172' role='' />\n    <member type='way' ref='74865047' role='' />\n    <member type='way' ref='411057613' role='' />\n    <member type='way' ref='85735265' role='' />\n    <member type='way' ref='495915484' role='' />\n    <member type='way' ref='298767466' role='' />\n    <member type='way' ref='742362081' role='' />\n    <member type='way' ref='742362082' role='' />\n    <member type='way' ref='2371124' role='' />\n    <member type='way' ref='295221437' role='' />\n    <member type='way' ref='495915487' role='' />\n    <member type='way' ref='495915485' role='' />\n    <member type='way' ref='495915489' role='' />\n    <member type='way' ref='27225997' role='' />\n    <member type='way' ref='2371125' role='' />\n    <member type='way' ref='416688425' role='' />\n    <member type='way' ref='742670366' role='' />\n    <member type='way' ref='364793653' role='' />\n    <member type='way' ref='416688419' role='' />\n    <member type='way' ref='91655816' role='' />\n    <member type='way' ref='422789552' role='' />\n    <member type='way' ref='422789553' role='' />\n    <member type='way' ref='422789549' role='' />\n    <member type='way' ref='91655817' role='' />\n    <member type='way' ref='422789537' role='' />\n    <member type='way' ref='422789538' role='' />\n    <member type='way' ref='24780359' role='' />\n    <member type='way' ref='422789465' role='' />\n    <member type='way' ref='422789456' role='' />\n    <member type='way' ref='237108780' role='' />\n    <member type='way' ref='25950312' role='' />\n    <member type='way' ref='422789457' role='' />\n    <member type='way' ref='422789460' role='' />\n    <member type='way' ref='422789468' role='' />\n    <member type='way' ref='25950318' role='' />\n    <member type='way' ref='422789464' role='' />\n    <member type='way' ref='422789466' role='' />\n    <member type='way' ref='379943432' role='' />\n    <member type='way' ref='735150640' role='' />\n    <member type='way' ref='379943436' role='' />\n    <member type='way' ref='422789481' role='' />\n    <member type='way' ref='422789510' role='' />\n    <member type='way' ref='422789476' role='' />\n    <member type='way' ref='422789489' role='' />\n    <member type='way' ref='422789500' role='' />\n    <member type='way' ref='8123174' role='' />\n    <member type='way' ref='422789494' role='' />\n    <member type='way' ref='422789479' role='' />\n    <member type='way' ref='87576581' role='' />\n    <member type='way' ref='422789478' role='' />\n    <member type='way' ref='24430497' role='' />\n    <member type='way' ref='851426998' role='' />\n    <member type='way' ref='851426997' role='' />\n    <member type='way' ref='291880782' role='' />\n    <member type='way' ref='476404634' role='' />\n    <member type='way' ref='397330471' role='' />\n    <member type='way' ref='555720195' role='' />\n    <member type='way' ref='555720196' role='' />\n    <member type='way' ref='555720197' role='' />\n    <member type='way' ref='24430583' role='' />\n    <member type='way' ref='117749858' role='' />\n    <member type='way' ref='292950882' role='' />\n    <member type='way' ref='292950883' role='' />\n    <member type='way' ref='5056350' role='' />\n    <member type='way' ref='5056355' role='' />\n    <member type='way' ref='5056354' role='' />\n    <member type='way' ref='421008068' role='' />\n    <member type='way' ref='121136427' role='' />\n    <member type='way' ref='417614542' role='' />\n    <member type='way' ref='174681248' role='' />\n    <member type='way' ref='24418160' role='' />\n    <member type='way' ref='421008066' role='' />\n    <member type='way' ref='421008064' role='' />\n    <member type='way' ref='24418161' role='' />\n    <member type='way' ref='24418152' role='' />\n    <member type='way' ref='24418154' role='' />\n    <member type='way' ref='26758987' role='' />\n    <member type='way' ref='475124867' role='' />\n    <member type='way' ref='225472291' role='' />\n    <member type='way' ref='37169704' role='' />\n    <member type='way' ref='839946023' role='' />\n    <member type='way' ref='134384292' role='' />\n    <member type='way' ref='134384294' role='' />\n    <member type='way' ref='24417569' role='' />\n    <member type='way' ref='657029898' role='' />\n    <member type='way' ref='675689403' role='' />\n    <member type='way' ref='658174825' role='' />\n    <member type='way' ref='657029885' role='' />\n    <member type='way' ref='658174824' role='' />\n    <member type='way' ref='157725807' role='' />\n    <member type='way' ref='221503634' role='' />\n    <member type='way' ref='37169712' role='' />\n    <member type='way' ref='209693188' role='' />\n    <member type='way' ref='120860456' role='' />\n    <member type='way' ref='120860455' role='' />\n    <member type='way' ref='170129851' role='' />\n    <member type='way' ref='170129854' role='' />\n    <member type='way' ref='209693189' role='' />\n    <member type='way' ref='209693190' role='' />\n    <member type='way' ref='657919163' role='' />\n    <member type='way' ref='24455312' role='' />\n    <member type='way' ref='24455313' role='' />\n    <member type='way' ref='652139155' role='' />\n    <member type='way' ref='299656111' role='' />\n    <member type='way' ref='495531791' role='' />\n    <member type='way' ref='4360596' role='' />\n    <member type='way' ref='458678239' role='' />\n    <member type='way' ref='458678238' role='' />\n    <member type='way' ref='4360590' role='' />\n    <member type='way' ref='4360591' role='' />\n    <member type='way' ref='652134635' role='' />\n    <member type='way' ref='652134637' role='' />\n    <member type='way' ref='4360592' role='' />\n    <member type='way' ref='2032610' role='' />\n    <member type='way' ref='4587429' role='' />\n    <member type='way' ref='652139148' role='' />\n    <member type='way' ref='8023902' role='' />\n    <member type='way' ref='4587463' role='' />\n    <member type='way' ref='833274286' role='' />\n    <member type='way' ref='833274284' role='' />\n    <member type='way' ref='810121470' role='' />\n    <member type='way' ref='667064559' role='' />\n    <member type='way' ref='103459027' role='' />\n    <member type='way' ref='103459022' role='' />\n    <member type='way' ref='667064557' role='' />\n    <member type='way' ref='314939368' role='' />\n    <member type='way' ref='660221939' role='' />\n    <member type='way' ref='37169713' role='' />\n    <member type='way' ref='668988138' role='' />\n    <member type='way' ref='37169736' role='' />\n    <member type='way' ref='668988137' role='' />\n    <member type='way' ref='8758203' role='' />\n    <member type='way' ref='75489312' role='' />\n    <member type='way' ref='409620215' role='' />\n    <member type='way' ref='409728748' role='' />\n    <member type='way' ref='409620218' role='' />\n    <member type='way' ref='121786347' role='' />\n    <member type='way' ref='9857215' role='' />\n    <member type='way' ref='409728749' role='' />\n    <member type='way' ref='462833163' role='' />\n    <member type='way' ref='29389646' role='' />\n    <member type='way' ref='29389644' role='' />\n    <member type='way' ref='9857011' role='' />\n    <member type='way' ref='495538609' role='' />\n    <member type='way' ref='495538613' role='' />\n    <member type='way' ref='495544423' role='' />\n    <member type='way' ref='495544426' role='' />\n    <member type='way' ref='495544432' role='' />\n    <member type='way' ref='495545614' role='' />\n    <member type='way' ref='495544437' role='' />\n    <member type='way' ref='495551945' role='' />\n    <member type='way' ref='495551946' role='' />\n    <member type='way' ref='495551950' role='' />\n    <member type='way' ref='495551951' role='' />\n    <member type='way' ref='495551952' role='' />\n    <member type='way' ref='495551954' role='' />\n    <member type='way' ref='76089155' role='' />\n    <member type='way' ref='502204836' role='' />\n    <member type='way' ref='537253319' role='' />\n    <member type='way' ref='495554229' role='' />\n    <member type='way' ref='769146788' role='' />\n    <member type='way' ref='833202606' role='' />\n    <member type='way' ref='116660707' role='' />\n    <member type='way' ref='833202605' role='' />\n    <member type='way' ref='769146788' role='' />\n    <member type='way' ref='527517962' role='' />\n    <member type='way' ref='527517957' role='' />\n    <member type='way' ref='495735718' role='' />\n    <member type='way' ref='495735719' role='' />\n    <member type='way' ref='537253317' role='' />\n    <member type='way' ref='495735720' role='' />\n    <member type='way' ref='37143460' role='' />\n    <member type='way' ref='495551948' role='' />\n    <member type='way' ref='495551947' role='' />\n    <member type='way' ref='495545612' role='' />\n    <member type='way' ref='495735713' role='' />\n    <member type='way' ref='495735715' role='' />\n    <member type='way' ref='495544430' role='' />\n    <member type='way' ref='37143461' role='' />\n    <member type='way' ref='118493622' role='' />\n    <member type='way' ref='24769864' role='' />\n    <member type='way' ref='118493614' role='' />\n    <member type='way' ref='27928273' role='' />\n    <member type='way' ref='409620226' role='' />\n    <member type='way' ref='669215143' role='' />\n    <member type='way' ref='417985222' role='' />\n    <member type='way' ref='669215141' role='' />\n    <member type='way' ref='118493606' role='' />\n    <member type='way' ref='833202604' role='' />\n    <member type='way' ref='76444083' role='' />\n    <member type='way' ref='495538608' role='' />\n    <member type='way' ref='669215140' role='' />\n    <member type='way' ref='414187457' role='' />\n    <member type='way' ref='9479549' role='' />\n    <member type='way' ref='664891114' role='' />\n    <member type='way' ref='8043375' role='' />\n    <member type='way' ref='7997076' role='' />\n    <member type='way' ref='669246965' role='' />\n    <member type='way' ref='845088458' role='' />\n    <member type='way' ref='845088457' role='' />\n    <member type='way' ref='4587467' role='' />\n    <member type='way' ref='414187434' role='' />\n    <member type='way' ref='842235068' role='' />\n    <member type='way' ref='368974730' role='' />\n    <member type='way' ref='842235067' role='' />\n    <member type='way' ref='368974740' role='' />\n    <member type='way' ref='368974733' role='' />\n    <member type='way' ref='368974743' role='' />\n    <member type='way' ref='368969987' role='' />\n    <member type='way' ref='13243193' role='' />\n    <member type='way' ref='42949541' role='' />\n    <member type='way' ref='368796349' role='' />\n    <member type='way' ref='368796350' role='' />\n    <member type='way' ref='368973702' role='' />\n    <member type='way' ref='368973703' role='' />\n    <member type='way' ref='13243181' role='' />\n    <member type='way' ref='116040317' role='' />\n    <member type='way' ref='334027688' role='' />\n    <member type='way' ref='334033956' role='' />\n    <member type='way' ref='334033957' role='' />\n    <member type='way' ref='334034697' role='' />\n    <member type='way' ref='708663282' role='' />\n    <member type='way' ref='833222402' role='' />\n    <member type='way' ref='299648362' role='' />\n    <member type='way' ref='708663281' role='' />\n    <member type='way' ref='361190702' role='' />\n    <member type='way' ref='361190701' role='' />\n    <member type='way' ref='2018724' role='' />\n    <member type='way' ref='2018400' role='' />\n    <member type='way' ref='369727413' role='' />\n    <member type='way' ref='4586017' role='' />\n    <member type='way' ref='24163581' role='' />\n    <member type='way' ref='24163582' role='' />\n    <member type='way' ref='201026016' role='' />\n    <member type='way' ref='201026015' role='' />\n    <member type='way' ref='133674487' role='' />\n    <member type='way' ref='133674486' role='' />\n    <member type='way' ref='133674484' role='' />\n    <member type='way' ref='161797071' role='' />\n    <member type='way' ref='125556138' role='' />\n    <member type='way' ref='692474886' role='' />\n    <member type='way' ref='4586021' role='' />\n    <member type='way' ref='492836256' role='' />\n    <member type='way' ref='51754193' role='' />\n    <member type='way' ref='386593763' role='' />\n    <member type='way' ref='46969880' role='' />\n    <member type='way' ref='4586023' role='' />\n    <member type='way' ref='130688167' role='' />\n    <member type='way' ref='492828702' role='' />\n    <member type='way' ref='241787608' role='' />\n    <member type='way' ref='33787292' role='' />\n    <member type='way' ref='492828703' role='' />\n    <member type='way' ref='125225779' role='' />\n    <member type='way' ref='234346248' role='' />\n    <member type='way' ref='125225780' role='' />\n    <member type='way' ref='241787617' role='' />\n    <member type='way' ref='241787614' role='' />\n    <member type='way' ref='241787609' role='' />\n    <member type='way' ref='241787613' role='' />\n    <member type='way' ref='119161423' role='' />\n    <member type='way' ref='119161417' role='' />\n    <member type='way' ref='38028832' role='' />\n    <member type='way' ref='38028833' role='' />\n    <member type='way' ref='627085346' role='' />\n    <member type='way' ref='144915856' role='' />\n    <member type='way' ref='144915857' role='' />\n    <member type='way' ref='492828704' role='' />\n    <member type='way' ref='492828706' role='' />\n    <member type='way' ref='38028161' role='' />\n    <member type='way' ref='492828708' role='' />\n    <member type='way' ref='38510311' role='' />\n    <member type='way' ref='144915852' role='' />\n    <member type='way' ref='843150468' role='' />\n    <member type='way' ref='30406545' role='' />\n    <member type='way' ref='30406549' role='' />\n    <member type='way' ref='843150467' role='' />\n    <member type='way' ref='130294856' role='' />\n    <member type='way' ref='197290015' role='' />\n    <member type='way' ref='184792914' role='' />\n    <member type='way' ref='689482243' role='' />\n    <member type='way' ref='321378455' role='' />\n    <member type='way' ref='234119941' role='' />\n    <member type='way' ref='10215146' role='' />\n    <member type='way' ref='520517117' role='' />\n    <member type='way' ref='498867438' role='' />\n    <member type='way' ref='835340624' role='' />\n    <member type='way' ref='498859729' role='' />\n    <member type='way' ref='318944792' role='' />\n    <member type='way' ref='498859743' role='' />\n    <member type='way' ref='541532867' role='' />\n    <member type='way' ref='761946430' role='' />\n    <member type='way' ref='541532868' role='' />\n    <member type='way' ref='692361513' role='' />\n    <member type='way' ref='692361511' role='' />\n    <member type='way' ref='541532866' role='' />\n    <member type='way' ref='541532865' role='' />\n    <member type='way' ref='541532861' role='' />\n    <member type='way' ref='541532864' role='' />\n    <member type='way' ref='146511185' role='' />\n    <member type='way' ref='541532863' role='' />\n    <member type='way' ref='146511187' role='' />\n    <member type='way' ref='146511189' role='' />\n    <member type='way' ref='23320471' role='' />\n    <member type='way' ref='440140529' role='' />\n    <member type='way' ref='440140532' role='' />\n    <member type='way' ref='440140531' role='' />\n    <member type='way' ref='440140528' role='' />\n    <member type='way' ref='529371826' role='' />\n    <member type='way' ref='9375303' role='' />\n    <member type='way' ref='279054989' role='' />\n    <member type='way' ref='279054987' role='' />\n    <member type='way' ref='46766460' role='' />\n    <member type='way' ref='46766459' role='' />\n    <member type='way' ref='289756324' role='' />\n    <member type='way' ref='161465009' role='' />\n    <member type='way' ref='51676172' role='' />\n    <member type='way' ref='772699194' role='' />\n    <member type='way' ref='819559515' role='' />\n    <member type='way' ref='188155922' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='819559513' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819308078' role='' />\n    <member type='way' ref='819559512' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='819559511' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='819308077' role='' />\n    <member type='way' ref='818931650' role='' />\n    <member type='way' ref='819308079' role='' />\n    <member type='way' ref='161208735' role='' />\n    <member type='way' ref='517466647' role='' />\n    <member type='way' ref='279054991' role='' />\n    <member type='way' ref='397936984' role='' />\n    <member type='way' ref='23338944' role='' />\n    <member type='way' ref='513775934' role='' />\n    <member type='way' ref='513775931' role='' />\n    <member type='way' ref='285963332' role='' />\n    <member type='way' ref='513775935' role='' />\n    <member type='way' ref='714366703' role='' />\n    <member type='way' ref='833318552' role='' />\n    <member type='way' ref='714526872' role='' />\n    <member type='way' ref='833318553' role='' />\n    <member type='way' ref='833318551' role='' />\n    <member type='way' ref='513775939' role='' />\n    <member type='way' ref='513775930' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='39461687' role='' />\n    <member type='way' ref='682688470' role='' />\n    <member type='way' ref='414177890' role='' />\n    <member type='way' ref='517427345' role='' />\n    <member type='way' ref='517427348' role='' />\n    <member type='way' ref='379126354' role='' />\n    <member type='way' ref='823646246' role='' />\n    <member type='way' ref='9654384' role='' />\n    <member type='way' ref='728210419' role='' />\n    <member type='way' ref='513775944' role='' />\n    <member type='way' ref='617119454' role='' />\n    <member type='way' ref='10999525' role='' />\n    <member type='way' ref='319841468' role='' />\n    <member type='way' ref='546295339' role='' />\n    <member type='way' ref='510335879' role='' />\n    <member type='way' ref='510335862' role='' />\n    <member type='way' ref='161190692' role='' />\n    <member type='way' ref='510335870' role='' />\n    <member type='way' ref='161486851' role='' />\n    <member type='way' ref='510335842' role='' />\n    <member type='way' ref='510335846' role='' />\n    <member type='way' ref='788083572' role='' />\n    <member type='way' ref='319841465' role='' />\n    <member type='way' ref='741996204' role='' />\n    <member type='way' ref='23444180' role='' />\n    <member type='way' ref='741996205' role='' />\n    <member type='way' ref='272706515' role='' />\n    <member type='way' ref='788083573' role='' />\n    <member type='way' ref='272706514' role='' />\n    <member type='way' ref='272706525' role='' />\n    <member type='way' ref='272706535' role='' />\n    <member type='way' ref='682518206' role='' />\n    <member type='way' ref='272706511' role='' />\n    <member type='way' ref='272706533' role='' />\n    <member type='way' ref='510581880' role='' />\n    <member type='way' ref='272706517' role='' />\n    <member type='way' ref='272706532' role='' />\n    <member type='way' ref='272706531' role='' />\n    <member type='way' ref='272706520' role='' />\n    <member type='way' ref='272706524' role='' />\n    <member type='way' ref='272706513' role='' />\n    <member type='way' ref='272706510' role='' />\n    <member type='way' ref='272706528' role='' />\n    <member type='way' ref='688687939' role='' />\n    <member type='way' ref='510624279' role='' />\n    <member type='way' ref='510624268' role='' />\n    <member type='way' ref='272706526' role='' />\n    <member type='way' ref='510624247' role='' />\n    <member type='way' ref='618993313' role='' />\n    <member type='way' ref='24430829' role='' />\n    <member type='way' ref='517466669' role='' />\n    <member type='way' ref='56323454' role='' />\n    <member type='way' ref='517466668' role='' />\n    <member type='way' ref='510632776' role='' />\n    <member type='way' ref='44829595' role='' />\n    <member type='way' ref='161466777' role='' />\n    <member type='way' ref='326276383' role='' />\n    <member type='way' ref='11003381' role='' />\n    <member type='way' ref='326276396' role='' />\n    <member type='way' ref='161187739' role='' />\n    <member type='way' ref='326276387' role='' />\n    <member type='way' ref='326276391' role='' />\n    <member type='way' ref='43076107' role='' />\n    <member type='way' ref='71391950' role='' />\n    <member type='way' ref='771999805' role='' />\n    <member type='way' ref='59638078' role='' />\n    <member type='way' ref='309700793' role='' />\n    <member type='way' ref='309700794' role='' />\n    <member type='way' ref='27064373' role='' />\n    <member type='way' ref='27064374' role='' />\n    <member type='way' ref='326156168' role='' />\n    <member type='way' ref='188163074' role='' />\n    <member type='way' ref='326156169' role='' />\n    <member type='way' ref='11003452' role='' />\n    <member type='way' ref='188163076' role='' />\n    <member type='way' ref='492828713' role='' />\n    <member type='way' ref='492828712' role='' />\n    <member type='way' ref='51772367' role='' />\n    <member type='way' ref='99529286' role='' />\n    <member type='way' ref='99529506' role='' />\n    <member type='way' ref='51772369' role='' />\n    <member type='way' ref='305777809' role='' />\n    <member type='way' ref='23043380' role='' />\n    <member type='way' ref='99598367' role='' />\n    <member type='way' ref='12370361' role='' />\n    <member type='way' ref='653277772' role='' />\n    <member type='way' ref='653277782' role='' />\n    <member type='way' ref='653277750' role='' />\n    <member type='way' ref='653277744' role='' />\n    <member type='way' ref='653277714' role='' />\n    <member type='way' ref='653277713' role='' />\n    <member type='way' ref='843067400' role='' />\n    <member type='way' ref='653277705' role='' />\n    <member type='way' ref='843067403' role='' />\n    <member type='way' ref='653277736' role='' />\n    <member type='way' ref='653277706' role='' />\n    <member type='way' ref='653277711' role='' />\n    <member type='way' ref='653277710' role='' />\n    <member type='way' ref='694611813' role='' />\n    <member type='way' ref='69917265' role='' />\n    <member type='way' ref='69917264' role='' />\n    <member type='way' ref='694611814' role='' />\n    <member type='way' ref='790763458' role='' />\n    <member type='way' ref='682646705' role='' />\n    <member type='way' ref='495494402' role='' />\n    <member type='way' ref='69917270' role='' />\n    <member type='way' ref='69917262' role='' />\n    <member type='way' ref='790763461' role='' />\n    <member type='way' ref='789768769' role='' />\n    <member type='way' ref='789768756' role='' />\n    <member type='way' ref='789768752' role='' />\n    <member type='way' ref='789768738' role='' />\n    <member type='way' ref='789768753' role='' />\n    <member type='way' ref='790621587' role='' />\n    <member type='way' ref='172214603' role='' />\n    <member type='way' ref='790621589' role='' />\n    <member type='way' ref='172214601' role='' />\n    <member type='way' ref='728708240' role='' />\n    <member type='way' ref='728708237' role='' />\n    <member type='way' ref='790621601' role='' />\n    <member type='way' ref='789768748' role='' />\n    <member type='way' ref='39139052' role='' />\n    <member type='way' ref='789768733' role='' />\n    <member type='way' ref='728708241' role='' />\n    <member type='way' ref='495479090' role='' />\n    <member type='way' ref='741023455' role='' />\n    <member type='way' ref='790621636' role='' />\n    <member type='way' ref='59523885' role='' />\n    <member type='way' ref='495479087' role='' />\n    <member type='way' ref='495494403' role='' />\n    <member type='way' ref='495479089' role='' />\n    <member type='way' ref='787823938' role='' />\n    <member type='way' ref='787823757' role='' />\n    <member type='way' ref='495479088' role='' />\n    <member type='way' ref='787823756' role='' />\n    <member type='way' ref='787823755' role='' />\n    <member type='way' ref='789768846' role='' />\n    <member type='way' ref='128918739' role='' />\n    <member type='way' ref='789768838' role='' />\n    <member type='way' ref='789768837' role='' />\n    <member type='way' ref='59523937' role='' />\n    <member type='way' ref='789768844' role='' />\n    <member type='way' ref='789768843' role='' />\n    <member type='way' ref='789768869' role='' />\n    <member type='way' ref='206850870' role='' />\n    <member type='way' ref='787823961' role='' />\n    <member type='way' ref='787823946' role='' />\n    <member type='way' ref='787823960' role='' />\n    <member type='way' ref='785386336' role='' />\n    <member type='way' ref='785396388' role='' />\n    <member type='way' ref='785386335' role='' />\n    <member type='way' ref='785396387' role='' />\n    <member type='way' ref='785386334' role='' />\n    <member type='way' ref='785396402' role='' />\n    <member type='way' ref='785396404' role='' />\n    <member type='way' ref='785386332' role='' />\n    <member type='way' ref='785386331' role='' />\n    <member type='way' ref='495556762' role='' />\n    <member type='way' ref='12870928' role='' />\n    <member type='way' ref='233955594' role='' />\n    <member type='way' ref='495518311' role='' />\n    <member type='way' ref='503042910' role='' />\n    <member type='way' ref='123954808' role='' />\n    <member type='way' ref='99529458' role='' />\n    <member type='way' ref='99529614' role='' />\n    <member type='way' ref='99529541' role='' />\n    <member type='way' ref='99529300' role='' />\n    <member type='way' ref='99529669' role='' />\n    <member type='way' ref='98240455' role='' />\n    <member type='way' ref='98240453' role='' />\n    <member type='way' ref='813756628' role='' />\n    <member type='way' ref='98234177' role='' />\n    <member type='way' ref='98234175' role='' />\n    <member type='way' ref='319062894' role='' />\n    <member type='way' ref='319062895' role='' />\n    <member type='way' ref='209196326' role='' />\n    <member type='way' ref='209196328' role='' />\n    <member type='way' ref='106467525' role='' />\n    <member type='way' ref='106467520' role='' />\n    <member type='way' ref='231059758' role='' />\n    <member type='way' ref='231059756' role='' />\n    <member type='way' ref='709615630' role='' />\n    <member type='way' ref='29314712' role='' />\n    <member type='way' ref='29314711' role='' />\n    <member type='way' ref='163600212' role='' />\n    <member type='way' ref='837149996' role='' />\n    <member type='way' ref='837149997' role='' />\n    <member type='way' ref='837149995' role='' />\n    <member type='way' ref='837149998' role='' />\n    <member type='way' ref='813756627' role='' />\n    <member type='way' ref='427314412' role='' />\n    <member type='way' ref='838292586' role='' />\n    <member type='way' ref='782010056' role='' />\n    <member type='way' ref='782010057' role='' />\n    <member type='way' ref='132688030' role='' />\n    <member type='way' ref='838292597' role='' />\n    <member type='way' ref='8041982' role='' />\n    <member type='way' ref='838292592' role='' />\n    <member type='way' ref='8041981' role='' />\n    <member type='way' ref='252104525' role='' />\n    <member type='way' ref='252104520' role='' />\n    <member type='way' ref='848360981' role='' />\n    <member type='way' ref='72256434' role='' />\n    <member type='way' ref='681143380' role='' />\n    <member type='way' ref='681139365' role='' />\n    <member type='way' ref='107114265' role='' />\n    <member type='way' ref='275143515' role='' />\n    <member type='way' ref='496588160' role='' />\n    <member type='way' ref='496120886' role='' />\n    <member type='way' ref='825980028' role='' />\n    <member type='way' ref='779192145' role='' />\n    <member type='way' ref='825980013' role='' />\n    <member type='way' ref='368771669' role='' />\n    <member type='way' ref='825980021' role='' />\n    <member type='way' ref='368771668' role='' />\n    <member type='way' ref='825980016' role='' />\n    <member type='way' ref='782014017' role='' />\n    <member type='way' ref='825980011' role='' />\n    <member type='way' ref='782014016' role='' />\n    <member type='way' ref='286203393' role='' />\n    <member type='way' ref='496107793' role='' />\n    <member type='way' ref='496107794' role='' />\n    <member type='way' ref='779192156' role='' />\n    <member type='way' ref='286552816' role='' />\n    <member type='way' ref='71973851' role='' />\n    <member type='way' ref='348766149' role='' />\n    <member type='way' ref='348766145' role='' />\n    <member type='way' ref='348771994' role='' />\n    <member type='way' ref='285709739' role='' />\n    <tag k='from' v='Københavns Lufthavn' />\n    <tag k='name' v='Flixbus 621: Københavns Lufthavn =&gt; Aalborg Busterminal' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='621' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Aalborg Busterminal' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='11375178' timestamp='2020-09-07T14:23:57Z' uid='1288' user='Niels Elgaard Larsen' visible='true' version='6' changeset='90536716'>\n    <member type='node' ref='3393833062' role='stop' />\n    <member type='way' ref='332263376' role='platform' />\n    <member type='node' ref='6827125634' role='stop' />\n    <member type='node' ref='7779170659' role='stop' />\n    <member type='node' ref='7650108367' role='platform' />\n    <member type='node' ref='1987802672' role='stop' />\n    <member type='node' ref='7647740030' role='platform' />\n    <member type='node' ref='1987802673' role='stop' />\n    <member type='node' ref='7647758213' role='platform' />\n    <member type='node' ref='1987802675' role='stop' />\n    <member type='node' ref='7647758212' role='platform' />\n    <member type='node' ref='1987802677' role='stop' />\n    <member type='node' ref='7647758211' role='platform' />\n    <member type='node' ref='1987802678' role='stop' />\n    <member type='node' ref='7647758210' role='platform' />\n    <member type='node' ref='7778082454' role='stop' />\n    <member type='node' ref='7778082455' role='platform' />\n    <member type='node' ref='458718203' role='stop' />\n    <member type='node' ref='1938160930' role='platform' />\n    <member type='node' ref='6448447785' role='stop' />\n    <member type='node' ref='6735247234' role='platform' />\n    <member type='way' ref='491845660' role='' />\n    <member type='way' ref='348763865' role='' />\n    <member type='way' ref='348763864' role='' />\n    <member type='way' ref='285709733' role='' />\n    <member type='way' ref='369152632' role='' />\n    <member type='way' ref='348766151' role='' />\n    <member type='way' ref='369151521' role='' />\n    <member type='way' ref='369151056' role='' />\n    <member type='way' ref='286552815' role='' />\n    <member type='way' ref='369151062' role='' />\n    <member type='way' ref='779192157' role='' />\n    <member type='way' ref='97574427' role='' />\n    <member type='way' ref='496580797' role='' />\n    <member type='way' ref='495987825' role='' />\n    <member type='way' ref='825980010' role='' />\n    <member type='way' ref='495987826' role='' />\n    <member type='way' ref='825980012' role='' />\n    <member type='way' ref='825980017' role='' />\n    <member type='way' ref='779192151' role='' />\n    <member type='way' ref='275143505' role='' />\n    <member type='way' ref='275143512' role='' />\n    <member type='way' ref='681139362' role='' />\n    <member type='way' ref='496249591' role='' />\n    <member type='way' ref='8169286' role='' />\n    <member type='way' ref='132688031' role='' />\n    <member type='way' ref='782010058' role='' />\n    <member type='way' ref='813756724' role='' />\n    <member type='way' ref='146514455' role='' />\n    <member type='way' ref='837149992' role='' />\n    <member type='way' ref='163600213' role='' />\n    <member type='way' ref='29314709' role='' />\n    <member type='way' ref='100211823' role='' />\n    <member type='way' ref='231059757' role='' />\n    <member type='way' ref='231059759' role='' />\n    <member type='way' ref='106467516' role='' />\n    <member type='way' ref='106467529' role='' />\n    <member type='way' ref='209196333' role='' />\n    <member type='way' ref='209196336' role='' />\n    <member type='way' ref='319062897' role='' />\n    <member type='way' ref='319062896' role='' />\n    <member type='way' ref='98234179' role='' />\n    <member type='way' ref='813756723' role='' />\n    <member type='way' ref='98234180' role='' />\n    <member type='way' ref='98240450' role='' />\n    <member type='way' ref='98240457' role='' />\n    <member type='way' ref='166899885' role='' />\n    <member type='way' ref='99529410' role='' />\n    <member type='way' ref='99529418' role='' />\n    <member type='way' ref='99529393' role='' />\n    <member type='way' ref='99529552' role='' />\n    <member type='way' ref='147288994' role='' />\n    <member type='way' ref='520269372' role='' />\n    <member type='way' ref='326285669' role='' />\n    <member type='way' ref='520269373' role='' />\n    <member type='way' ref='99529276' role='' />\n    <member type='way' ref='216584696' role='' />\n    <member type='way' ref='515175327' role='' />\n    <member type='way' ref='495556761' role='' />\n    <member type='way' ref='12870928' role='' />\n    <member type='way' ref='495556762' role='' />\n    <member type='way' ref='785386339' role='' />\n    <member type='way' ref='785386338' role='' />\n    <member type='way' ref='785396407' role='' />\n    <member type='way' ref='785396397' role='' />\n    <member type='way' ref='785396396' role='' />\n    <member type='way' ref='785386337' role='' />\n    <member type='way' ref='785396406' role='' />\n    <member type='way' ref='697860111' role='' />\n    <member type='way' ref='495518310' role='' />\n    <member type='way' ref='43407258' role='' />\n    <member type='way' ref='787823959' role='' />\n    <member type='way' ref='787823954' role='' />\n    <member type='way' ref='787823953' role='' />\n    <member type='way' ref='787823952' role='' />\n    <member type='way' ref='787823951' role='' />\n    <member type='way' ref='787823950' role='' />\n    <member type='way' ref='787823949' role='' />\n    <member type='way' ref='787823948' role='' />\n    <member type='way' ref='495556765' role='' />\n    <member type='way' ref='172010067' role='' />\n    <member type='way' ref='789768866' role='' />\n    <member type='way' ref='789768856' role='' />\n    <member type='way' ref='789768855' role='' />\n    <member type='way' ref='172010057' role='' />\n    <member type='way' ref='789768842' role='' />\n    <member type='way' ref='789768841' role='' />\n    <member type='way' ref='789768840' role='' />\n    <member type='way' ref='789768839' role='' />\n    <member type='way' ref='787823758' role='' />\n    <member type='way' ref='787823939' role='' />\n    <member type='way' ref='787823754' role='' />\n    <member type='way' ref='172010066' role='' />\n    <member type='way' ref='787823936' role='' />\n    <member type='way' ref='495494407' role='' />\n    <member type='way' ref='495494406' role='' />\n    <member type='way' ref='787823937' role='' />\n    <member type='way' ref='495494404' role='' />\n    <member type='way' ref='789768781' role='' />\n    <member type='way' ref='495494396' role='' />\n    <member type='way' ref='790621635' role='' />\n    <member type='way' ref='789768770' role='' />\n    <member type='way' ref='495494399' role='' />\n    <member type='way' ref='495494401' role='' />\n    <member type='way' ref='790763461' role='' />\n    <member type='way' ref='789768769' role='' />\n    <member type='way' ref='789768756' role='' />\n    <member type='way' ref='789768752' role='' />\n    <member type='way' ref='789768738' role='' />\n    <member type='way' ref='789768753' role='' />\n    <member type='way' ref='790621587' role='' />\n    <member type='way' ref='172214603' role='' />\n    <member type='way' ref='790621589' role='' />\n    <member type='way' ref='172214601' role='' />\n    <member type='way' ref='728708240' role='' />\n    <member type='way' ref='728708237' role='' />\n    <member type='way' ref='790621601' role='' />\n    <member type='way' ref='789768748' role='' />\n    <member type='way' ref='39139052' role='' />\n    <member type='way' ref='789768733' role='' />\n    <member type='way' ref='728708241' role='' />\n    <member type='way' ref='4976826' role='' />\n    <member type='way' ref='495494400' role='' />\n    <member type='way' ref='43407659' role='' />\n    <member type='way' ref='790621639' role='' />\n    <member type='way' ref='43407658' role='' />\n    <member type='way' ref='787823752' role='' />\n    <member type='way' ref='694611815' role='' />\n    <member type='way' ref='682646707' role='' />\n    <member type='way' ref='69917265' role='' />\n    <member type='way' ref='694611813' role='' />\n    <member type='way' ref='653277710' role='' />\n    <member type='way' ref='653277709' role='' />\n    <member type='way' ref='653277708' role='' />\n    <member type='way' ref='682639359' role='' />\n    <member type='way' ref='653277699' role='' />\n    <member type='way' ref='653277738' role='' />\n    <member type='way' ref='653277701' role='' />\n    <member type='way' ref='653277739' role='' />\n    <member type='way' ref='653277744' role='' />\n    <member type='way' ref='653277771' role='' />\n    <member type='way' ref='653277783' role='' />\n    <member type='way' ref='653277745' role='' />\n    <member type='way' ref='653277774' role='' />\n    <member type='way' ref='653277785' role='' />\n    <member type='way' ref='39131593' role='' />\n    <member type='way' ref='760605023' role='' />\n    <member type='way' ref='99529489' role='' />\n    <member type='way' ref='305777808' role='' />\n    <member type='way' ref='51772366' role='' />\n    <member type='way' ref='99529449' role='' />\n    <member type='way' ref='99529576' role='' />\n    <member type='way' ref='492828711' role='' />\n    <member type='way' ref='62227774' role='' />\n    <member type='way' ref='692148513' role='' />\n    <member type='way' ref='692148512' role='' />\n    <member type='way' ref='326285668' role='' />\n    <member type='way' ref='692148510' role='' />\n    <member type='way' ref='23376360' role='' />\n    <member type='way' ref='23376359' role='' />\n    <member type='way' ref='311697613' role='' />\n    <member type='way' ref='326156170' role='' />\n    <member type='way' ref='56045342' role='' />\n    <member type='way' ref='309700792' role='' />\n    <member type='way' ref='326285671' role='' />\n    <member type='way' ref='59638081' role='' />\n    <member type='way' ref='771999806' role='' />\n    <member type='way' ref='71391931' role='' />\n    <member type='way' ref='43076108' role='' />\n    <member type='way' ref='326276386' role='' />\n    <member type='way' ref='326276401' role='' />\n    <member type='way' ref='161187726' role='' />\n    <member type='way' ref='326285661' role='' />\n    <member type='way' ref='37343749' role='' />\n    <member type='way' ref='161466779' role='' />\n    <member type='way' ref='44829596' role='' />\n    <member type='way' ref='517466670' role='' />\n    <member type='way' ref='517466671' role='' />\n    <member type='way' ref='153531784' role='' />\n    <member type='way' ref='510624260' role='' />\n    <member type='way' ref='682500794' role='' />\n    <member type='way' ref='510624310' role='' />\n    <member type='way' ref='517466664' role='' />\n    <member type='way' ref='510624259' role='' />\n    <member type='way' ref='517466663' role='' />\n    <member type='way' ref='510624304' role='' />\n    <member type='way' ref='510624303' role='' />\n    <member type='way' ref='510624305' role='' />\n    <member type='way' ref='510624309' role='' />\n    <member type='way' ref='10999808' role='' />\n    <member type='way' ref='272706522' role='' />\n    <member type='way' ref='272706527' role='' />\n    <member type='way' ref='272706508' role='' />\n    <member type='way' ref='272706519' role='' />\n    <member type='way' ref='272706530' role='' />\n    <member type='way' ref='272706521' role='' />\n    <member type='way' ref='272706534' role='' />\n    <member type='way' ref='741996213' role='' />\n    <member type='way' ref='161469199' role='' />\n    <member type='way' ref='741996207' role='' />\n    <member type='way' ref='23444181' role='' />\n    <member type='way' ref='788083570' role='' />\n    <member type='way' ref='788083569' role='' />\n    <member type='way' ref='319841464' role='' />\n    <member type='way' ref='510335857' role='' />\n    <member type='way' ref='161486841' role='' />\n    <member type='way' ref='517427354' role='' />\n    <member type='way' ref='9654314' role='' />\n    <member type='way' ref='510335860' role='' />\n    <member type='way' ref='510335854' role='' />\n    <member type='way' ref='161190702' role='' />\n    <member type='way' ref='517427353' role='' />\n    <member type='way' ref='319841469' role='' />\n    <member type='way' ref='560552446' role='' />\n    <member type='way' ref='319841466' role='' />\n    <member type='way' ref='161464999' role='' />\n    <member type='way' ref='42424841' role='' />\n    <member type='way' ref='823646245' role='' />\n    <member type='way' ref='23629785' role='' />\n    <member type='way' ref='517427341' role='' />\n    <member type='way' ref='379126351' role='' />\n    <member type='way' ref='414177889' role='' />\n    <member type='way' ref='682688472' role='' />\n    <member type='way' ref='23338617' role='' />\n    <member type='way' ref='513775941' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='513775934' role='' />\n    <member type='way' ref='513775931' role='' />\n    <member type='way' ref='285963332' role='' />\n    <member type='way' ref='513775935' role='' />\n    <member type='way' ref='714366703' role='' />\n    <member type='way' ref='833318552' role='' />\n    <member type='way' ref='714526872' role='' />\n    <member type='way' ref='833318553' role='' />\n    <member type='way' ref='833318551' role='' />\n    <member type='way' ref='513775939' role='' />\n    <member type='way' ref='513775930' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='513775941' role='' />\n    <member type='way' ref='686698865' role='' />\n    <member type='way' ref='517427335' role='' />\n    <member type='way' ref='517427330' role='' />\n    <member type='way' ref='517466645' role='' />\n    <member type='way' ref='517466643' role='' />\n    <member type='way' ref='517466644' role='' />\n    <member type='way' ref='161208735' role='' />\n    <member type='way' ref='833318550' role='' />\n    <member type='way' ref='728192376' role='' />\n    <member type='way' ref='517466646' role='' />\n    <member type='way' ref='517466648' role='' />\n    <member type='way' ref='279054992' role='' />\n    <member type='way' ref='51676172' role='' />\n    <member type='way' ref='772699194' role='' />\n    <member type='way' ref='819559515' role='' />\n    <member type='way' ref='188155922' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='819559513' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819308078' role='' />\n    <member type='way' ref='819559512' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='819559511' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='819308077' role='' />\n    <member type='way' ref='818931650' role='' />\n    <member type='way' ref='188155891' role='' />\n    <member type='way' ref='728192376' role='' />\n    <member type='way' ref='517466646' role='' />\n    <member type='way' ref='517466648' role='' />\n    <member type='way' ref='279054992' role='' />\n    <member type='way' ref='161465009' role='' />\n    <member type='way' ref='289756324' role='' />\n    <member type='way' ref='46766459' role='' />\n    <member type='way' ref='46766460' role='' />\n    <member type='way' ref='279054987' role='' />\n    <member type='way' ref='279054989' role='' />\n    <member type='way' ref='9375303' role='' />\n    <member type='way' ref='529371826' role='' />\n    <member type='way' ref='440140528' role='' />\n    <member type='way' ref='440140531' role='' />\n    <member type='way' ref='161190693' role='' />\n    <member type='way' ref='440140529' role='' />\n    <member type='way' ref='23320471' role='' />\n    <member type='way' ref='146511189' role='' />\n    <member type='way' ref='146511188' role='' />\n    <member type='way' ref='143821024' role='' />\n    <member type='way' ref='541532857' role='' />\n    <member type='way' ref='8812730' role='' />\n    <member type='way' ref='541532855' role='' />\n    <member type='way' ref='692361519' role='' />\n    <member type='way' ref='692361505' role='' />\n    <member type='way' ref='161234020' role='' />\n    <member type='way' ref='761946431' role='' />\n    <member type='way' ref='835340625' role='' />\n    <member type='way' ref='498859739' role='' />\n    <member type='way' ref='498859736' role='' />\n    <member type='way' ref='835340627' role='' />\n    <member type='way' ref='498859744' role='' />\n    <member type='way' ref='520517114' role='' />\n    <member type='way' ref='128898677' role='' />\n    <member type='way' ref='348352697' role='' />\n    <member type='way' ref='234119942' role='' />\n    <member type='way' ref='321378453' role='' />\n    <member type='way' ref='689482242' role='' />\n    <member type='way' ref='321378452' role='' />\n    <member type='way' ref='184792919' role='' />\n    <member type='way' ref='192705441' role='' />\n    <member type='way' ref='144918016' role='' />\n    <member type='way' ref='144918017' role='' />\n    <member type='way' ref='144918015' role='' />\n    <member type='way' ref='843150465' role='' />\n    <member type='way' ref='321378456' role='' />\n    <member type='way' ref='30406550' role='' />\n    <member type='way' ref='843150466' role='' />\n    <member type='way' ref='30406546' role='' />\n    <member type='way' ref='843150464' role='' />\n    <member type='way' ref='144915853' role='' />\n    <member type='way' ref='144915854' role='' />\n    <member type='way' ref='144915851' role='' />\n    <member type='way' ref='492828707' role='' />\n    <member type='way' ref='123458827' role='' />\n    <member type='way' ref='30407255' role='' />\n    <member type='way' ref='492828705' role='' />\n    <member type='way' ref='123458828' role='' />\n    <member type='way' ref='144915855' role='' />\n    <member type='way' ref='627085345' role='' />\n    <member type='way' ref='633456015' role='' />\n    <member type='way' ref='51754191' role='' />\n    <member type='way' ref='38028830' role='' />\n    <member type='way' ref='38028831' role='' />\n    <member type='way' ref='119161422' role='' />\n    <member type='way' ref='119161418' role='' />\n    <member type='way' ref='241787612' role='' />\n    <member type='way' ref='833243911' role='' />\n    <member type='way' ref='241787611' role='' />\n    <member type='way' ref='241787610' role='' />\n    <member type='way' ref='125592653' role='' />\n    <member type='way' ref='241787616' role='' />\n    <member type='way' ref='234346247' role='' />\n    <member type='way' ref='125592652' role='' />\n    <member type='way' ref='492828700' role='' />\n    <member type='way' ref='241787618' role='' />\n    <member type='way' ref='33787295' role='' />\n    <member type='way' ref='492828701' role='' />\n    <member type='way' ref='492836262' role='' />\n    <member type='way' ref='492836260' role='' />\n    <member type='way' ref='492836259' role='' />\n    <member type='way' ref='46969881' role='' />\n    <member type='way' ref='24166011' role='' />\n    <member type='way' ref='4586022' role='' />\n    <member type='way' ref='46969879' role='' />\n    <member type='way' ref='492836255' role='' />\n    <member type='way' ref='51754192' role='' />\n    <member type='way' ref='386593765' role='' />\n    <member type='way' ref='676193412' role='' />\n    <member type='way' ref='4586020' role='' />\n    <member type='way' ref='125556146' role='' />\n    <member type='way' ref='24165083' role='' />\n    <member type='way' ref='24080437' role='' />\n    <member type='way' ref='24080439' role='' />\n    <member type='way' ref='133674483' role='' />\n    <member type='way' ref='133674485' role='' />\n    <member type='way' ref='133674488' role='' />\n    <member type='way' ref='133674482' role='' />\n    <member type='way' ref='201026017' role='' />\n    <member type='way' ref='201026018' role='' />\n    <member type='way' ref='24163579' role='' />\n    <member type='way' ref='24163580' role='' />\n    <member type='way' ref='24163491' role='' />\n    <member type='way' ref='24163492' role='' />\n    <member type='way' ref='13237921' role='' />\n    <member type='way' ref='369727414' role='' />\n    <member type='way' ref='590045020' role='' />\n    <member type='way' ref='4566412' role='' />\n    <member type='way' ref='4587474' role='' />\n    <member type='way' ref='361190491' role='' />\n    <member type='way' ref='833222409' role='' />\n    <member type='way' ref='361190492' role='' />\n    <member type='way' ref='116040324' role='' />\n    <member type='way' ref='334033771' role='' />\n    <member type='way' ref='369719169' role='' />\n    <member type='way' ref='13243176' role='' />\n    <member type='way' ref='42949538' role='' />\n    <member type='way' ref='334028997' role='' />\n    <member type='way' ref='334028998' role='' />\n    <member type='way' ref='308311666' role='' />\n    <member type='way' ref='334031713' role='' />\n    <member type='way' ref='334031718' role='' />\n    <member type='way' ref='368973701' role='' />\n    <member type='way' ref='368973704' role='' />\n    <member type='way' ref='13243197' role='' />\n    <member type='way' ref='24459701' role='' />\n    <member type='way' ref='368974736' role='' />\n    <member type='way' ref='370182172' role='' />\n    <member type='way' ref='368974738' role='' />\n    <member type='way' ref='370182860' role='' />\n    <member type='way' ref='842235066' role='' />\n    <member type='way' ref='370182861' role='' />\n    <member type='way' ref='370184496' role='' />\n    <member type='way' ref='370184497' role='' />\n    <member type='way' ref='660234546' role='' />\n    <member type='way' ref='414187449' role='' />\n    <member type='way' ref='845088455' role='' />\n    <member type='way' ref='845088456' role='' />\n    <member type='way' ref='660234545' role='' />\n    <member type='way' ref='7997077' role='' />\n    <member type='way' ref='8043373' role='' />\n    <member type='way' ref='664891124' role='' />\n    <member type='way' ref='9479546' role='' />\n    <member type='way' ref='9479547' role='' />\n    <member type='way' ref='414187439' role='' />\n    <member type='way' ref='8757003' role='' />\n    <member type='way' ref='417985223' role='' />\n    <member type='way' ref='664891117' role='' />\n    <member type='way' ref='75489313' role='' />\n    <member type='way' ref='8749433' role='' />\n    <member type='way' ref='88006027' role='' />\n    <member type='way' ref='409620214' role='' />\n    <member type='way' ref='409728748' role='' />\n    <member type='way' ref='409620218' role='' />\n    <member type='way' ref='121786347' role='' />\n    <member type='way' ref='9857215' role='' />\n    <member type='way' ref='409728749' role='' />\n    <member type='way' ref='462833163' role='' />\n    <member type='way' ref='29389646' role='' />\n    <member type='way' ref='29389644' role='' />\n    <member type='way' ref='9857011' role='' />\n    <member type='way' ref='495538609' role='' />\n    <member type='way' ref='495538613' role='' />\n    <member type='way' ref='495544423' role='' />\n    <member type='way' ref='495544426' role='' />\n    <member type='way' ref='495544432' role='' />\n    <member type='way' ref='495545614' role='' />\n    <member type='way' ref='495544437' role='' />\n    <member type='way' ref='495551945' role='' />\n    <member type='way' ref='495551946' role='' />\n    <member type='way' ref='495551950' role='' />\n    <member type='way' ref='495551951' role='' />\n    <member type='way' ref='495551952' role='' />\n    <member type='way' ref='495551954' role='' />\n    <member type='way' ref='76089155' role='' />\n    <member type='way' ref='502204836' role='' />\n    <member type='way' ref='537253319' role='' />\n    <member type='way' ref='495554229' role='' />\n    <member type='way' ref='769146788' role='' />\n    <member type='way' ref='833202606' role='' />\n    <member type='way' ref='116660707' role='' />\n    <member type='way' ref='833202605' role='' />\n    <member type='way' ref='769146788' role='' />\n    <member type='way' ref='527517962' role='' />\n    <member type='way' ref='527517957' role='' />\n    <member type='way' ref='495735718' role='' />\n    <member type='way' ref='495735719' role='' />\n    <member type='way' ref='537253317' role='' />\n    <member type='way' ref='495735720' role='' />\n    <member type='way' ref='37143460' role='' />\n    <member type='way' ref='495551948' role='' />\n    <member type='way' ref='495551947' role='' />\n    <member type='way' ref='495545612' role='' />\n    <member type='way' ref='495735713' role='' />\n    <member type='way' ref='495735715' role='' />\n    <member type='way' ref='495544430' role='' />\n    <member type='way' ref='37143461' role='' />\n    <member type='way' ref='118493622' role='' />\n    <member type='way' ref='24769864' role='' />\n    <member type='way' ref='118493614' role='' />\n    <member type='way' ref='27928273' role='' />\n    <member type='way' ref='409620226' role='' />\n    <member type='way' ref='669215143' role='' />\n    <member type='way' ref='417985222' role='' />\n    <member type='way' ref='88006017' role='' />\n    <member type='way' ref='8749218' role='' />\n    <member type='way' ref='8757097' role='' />\n    <member type='way' ref='664891117' role='' />\n    <member type='way' ref='76444069' role='' />\n    <member type='way' ref='671733006' role='' />\n    <member type='way' ref='668988990' role='' />\n    <member type='way' ref='664891116' role='' />\n    <member type='way' ref='671733007' role='' />\n    <member type='way' ref='668988989' role='' />\n    <member type='way' ref='37169725' role='' />\n    <member type='way' ref='660342395' role='' />\n    <member type='way' ref='660342403' role='' />\n    <member type='way' ref='667065457' role='' />\n    <member type='way' ref='660342385' role='' />\n    <member type='way' ref='660342376' role='' />\n    <member type='way' ref='660221938' role='' />\n    <member type='way' ref='314939367' role='' />\n    <member type='way' ref='103459023' role='' />\n    <member type='way' ref='103459014' role='' />\n    <member type='way' ref='833274276' role='' />\n    <member type='way' ref='833274278' role='' />\n    <member type='way' ref='810121469' role='' />\n    <member type='way' ref='8023900' role='' />\n    <member type='way' ref='652139150' role='' />\n    <member type='way' ref='4587430' role='' />\n    <member type='way' ref='4360600' role='' />\n    <member type='way' ref='4360601' role='' />\n    <member type='way' ref='652134638' role='' />\n    <member type='way' ref='652134636' role='' />\n    <member type='way' ref='4360602' role='' />\n    <member type='way' ref='4360603' role='' />\n    <member type='way' ref='8242842' role='' />\n    <member type='way' ref='458678237' role='' />\n    <member type='way' ref='458678241' role='' />\n    <member type='way' ref='652139154' role='' />\n    <member type='way' ref='657919169' role='' />\n    <member type='way' ref='28172234' role='' />\n    <member type='way' ref='24455311' role='' />\n    <member type='way' ref='120873680' role='' />\n    <member type='way' ref='657919176' role='' />\n    <member type='way' ref='24455310' role='' />\n    <member type='way' ref='210247619' role='' />\n    <member type='way' ref='169849344' role='' />\n    <member type='way' ref='169849345' role='' />\n    <member type='way' ref='107689033' role='' />\n    <member type='way' ref='157725815' role='' />\n    <member type='way' ref='221503636' role='' />\n    <member type='way' ref='657029891' role='' />\n    <member type='way' ref='221503632' role='' />\n    <member type='way' ref='675689404' role='' />\n    <member type='way' ref='657029878' role='' />\n    <member type='way' ref='24417571' role='' />\n    <member type='way' ref='24417572' role='' />\n    <member type='way' ref='134384298' role='' />\n    <member type='way' ref='658163951' role='' />\n    <member type='way' ref='134384296' role='' />\n    <member type='way' ref='225472290' role='' />\n    <member type='way' ref='26758966' role='' />\n    <member type='way' ref='366095663' role='' />\n    <member type='way' ref='120081163' role='' />\n    <member type='way' ref='120081165' role='' />\n    <member type='way' ref='421008065' role='' />\n    <member type='way' ref='421008067' role='' />\n    <member type='way' ref='24418157' role='' />\n    <member type='way' ref='24418158' role='' />\n    <member type='way' ref='547986509' role='' />\n    <member type='way' ref='174681249' role='' />\n    <member type='way' ref='117749881' role='' />\n    <member type='way' ref='421008062' role='' />\n    <member type='way' ref='5056352' role='' />\n    <member type='way' ref='5056353' role='' />\n    <member type='way' ref='2037455' role='' />\n    <member type='way' ref='117749885' role='' />\n    <member type='way' ref='5056351' role='' />\n    <member type='way' ref='292950628' role='' />\n    <member type='way' ref='117749843' role='' />\n    <member type='way' ref='310220438' role='' />\n    <member type='way' ref='421008057' role='' />\n    <member type='way' ref='219041774' role='' />\n    <member type='way' ref='117749838' role='' />\n    <member type='way' ref='117749878' role='' />\n    <member type='way' ref='24430584' role='' />\n    <member type='way' ref='555720226' role='' />\n    <member type='way' ref='24430585' role='' />\n    <member type='way' ref='5056358' role='' />\n    <member type='way' ref='476404633' role='' />\n    <member type='way' ref='397330725' role='' />\n    <member type='way' ref='155510721' role='' />\n    <member type='way' ref='155510719' role='' />\n    <member type='way' ref='422789531' role='' />\n    <member type='way' ref='155510717' role='' />\n    <member type='way' ref='422789485' role='' />\n    <member type='way' ref='422789483' role='' />\n    <member type='way' ref='24508491' role='' />\n    <member type='way' ref='422789482' role='' />\n    <member type='way' ref='422789477' role='' />\n    <member type='way' ref='422789526' role='' />\n    <member type='way' ref='192297592' role='' />\n    <member type='way' ref='422789521' role='' />\n    <member type='way' ref='422789516' role='' />\n    <member type='way' ref='422789484' role='' />\n    <member type='way' ref='422789480' role='' />\n    <member type='way' ref='422789505' role='' />\n    <member type='way' ref='379943434' role='' />\n    <member type='way' ref='422789461' role='' />\n    <member type='way' ref='422789458' role='' />\n    <member type='way' ref='2038976' role='' />\n    <member type='way' ref='422789462' role='' />\n    <member type='way' ref='422789463' role='' />\n    <member type='way' ref='25950317' role='' />\n    <member type='way' ref='36412448' role='' />\n    <member type='way' ref='422789467' role='' />\n    <member type='way' ref='422789459' role='' />\n    <member type='way' ref='237108779' role='' />\n    <member type='way' ref='683463580' role='' />\n    <member type='way' ref='422789469' role='' />\n    <member type='way' ref='8123175' role='' />\n    <member type='way' ref='422789534' role='' />\n    <member type='way' ref='422789536' role='' />\n    <member type='way' ref='422789535' role='' />\n    <member type='way' ref='422789548' role='' />\n    <member type='way' ref='90527578' role='' />\n    <member type='way' ref='422789475' role='' />\n    <member type='way' ref='422789474' role='' />\n    <member type='way' ref='683577496' role='' />\n    <member type='way' ref='750435704' role='' />\n    <member type='way' ref='210642683' role='' />\n    <member type='way' ref='27409295' role='' />\n    <member type='way' ref='27409296' role='' />\n    <member type='way' ref='496762358' role='' />\n    <member type='way' ref='448963756' role='' />\n    <member type='way' ref='751162440' role='' />\n    <member type='way' ref='121284723' role='' />\n    <member type='way' ref='751162442' role='' />\n    <member type='way' ref='751162441' role='' />\n    <member type='way' ref='496720678' role='' />\n    <member type='way' ref='388070331' role='' />\n    <member type='way' ref='496703336' role='' />\n    <member type='way' ref='121284722' role='' />\n    <member type='way' ref='515410740' role='' />\n    <member type='way' ref='38702782' role='' />\n    <member type='way' ref='751162443' role='' />\n    <member type='way' ref='38702777' role='' />\n    <member type='way' ref='751162444' role='' />\n    <member type='way' ref='38702744' role='' />\n    <member type='way' ref='85735262' role='' />\n    <member type='way' ref='776071648' role='' />\n    <member type='way' ref='168269172' role='' />\n    <member type='way' ref='74865047' role='' />\n    <member type='way' ref='411057613' role='' />\n    <member type='way' ref='85735265' role='' />\n    <member type='way' ref='495915484' role='' />\n    <member type='way' ref='298767466' role='' />\n    <member type='way' ref='742362081' role='' />\n    <member type='way' ref='742362082' role='' />\n    <member type='way' ref='2371124' role='' />\n    <member type='way' ref='295221437' role='' />\n    <member type='way' ref='495915487' role='' />\n    <member type='way' ref='495915485' role='' />\n    <member type='way' ref='495915489' role='' />\n    <member type='way' ref='27225997' role='' />\n    <member type='way' ref='2371125' role='' />\n    <member type='way' ref='416688425' role='' />\n    <member type='way' ref='742670366' role='' />\n    <member type='way' ref='364793653' role='' />\n    <member type='way' ref='416688419' role='' />\n    <member type='way' ref='422789570' role='' />\n    <member type='way' ref='630489172' role='' />\n    <member type='way' ref='161633350' role='' />\n    <member type='way' ref='422789568' role='' />\n    <member type='way' ref='422789556' role='' />\n    <member type='way' ref='422789562' role='' />\n    <member type='way' ref='422789555' role='' />\n    <member type='way' ref='422789566' role='' />\n    <member type='way' ref='422789567' role='' />\n    <member type='way' ref='422789564' role='' />\n    <member type='way' ref='422789559' role='' />\n    <member type='way' ref='422789544' role='' />\n    <member type='way' ref='1880585' role='' />\n    <member type='way' ref='422789542' role='' />\n    <member type='way' ref='683462106' role='' />\n    <member type='way' ref='237108788' role='' />\n    <member type='way' ref='108848990' role='' />\n    <member type='way' ref='108849099' role='' />\n    <member type='way' ref='422789540' role='' />\n    <member type='way' ref='108849074' role='' />\n    <member type='way' ref='422789572' role='' />\n    <member type='way' ref='108849037' role='' />\n    <member type='way' ref='386834370' role='' />\n    <member type='way' ref='2040973' role='' />\n    <member type='way' ref='24418176' role='' />\n    <member type='way' ref='24418177' role='' />\n    <member type='way' ref='24418174' role='' />\n    <member type='way' ref='24418175' role='' />\n    <member type='way' ref='257636641' role='' />\n    <member type='way' ref='683455706' role='' />\n    <member type='way' ref='110361079' role='' />\n    <member type='way' ref='357265308' role='' />\n    <member type='way' ref='357265307' role='' />\n    <member type='way' ref='140293283' role='' />\n    <member type='way' ref='357265304' role='' />\n    <member type='way' ref='30791075' role='' />\n    <member type='way' ref='119719505' role='' />\n    <member type='way' ref='30791222' role='' />\n    <member type='way' ref='386834391' role='' />\n    <member type='way' ref='375993929' role='' />\n    <member type='way' ref='121198957' role='' />\n    <member type='way' ref='388070394' role='' />\n    <member type='way' ref='386834383' role='' />\n    <member type='way' ref='37706640' role='' />\n    <member type='way' ref='488272301' role='' />\n    <member type='way' ref='488272303' role='' />\n    <member type='way' ref='233833233' role='' />\n    <member type='way' ref='488272308' role='' />\n    <member type='way' ref='488272310' role='' />\n    <member type='way' ref='25604858' role='' />\n    <member type='way' ref='4373483' role='' />\n    <member type='way' ref='488272315' role='' />\n    <member type='way' ref='488272320' role='' />\n    <member type='way' ref='296891697' role='' />\n    <tag k='from' v='Aalborg Busterminal' />\n    <tag k='name' v='Flixbus 621: Aalborg Busterminal =&gt; Københavns Lufthavn' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='621' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Københavns Lufthavn' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='11375179' timestamp='2020-08-02T20:15:50Z' uid='1281932' user='marcoSt' visible='true' version='1' changeset='88849987'>\n    <member type='relation' ref='11375177' role='' />\n    <member type='relation' ref='11375178' role='' />\n    <tag k='from' v='Københavns Lufthavn' />\n    <tag k='name' v='Flixbus 621: Københavns Lufthavn &lt;=&gt; Aalborg' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='621' />\n    <tag k='route_master' v='bus' />\n    <tag k='to' v='Aalborg Busterminal' />\n    <tag k='type' v='route_master' />\n  </relation>\n  <relation id='11375267' timestamp='2020-09-17T09:26:01Z' uid='130539' user='pelleg' visible='true' version='6' changeset='91035938'>\n    <member type='node' ref='7778059773' role='stop' />\n    <member type='way' ref='833202564' role='platform' />\n    <member type='node' ref='7778059776' role='stop' />\n    <member type='node' ref='7778059777' role='platform' />\n    <member type='node' ref='7778082410' role='stop' />\n    <member type='way' ref='833202575' role='platform' />\n    <member type='node' ref='6223447952' role='stop' />\n    <member type='way' ref='513595672' role='platform' />\n    <member type='node' ref='1987802672' role='stop' />\n    <member type='node' ref='7647740030' role='platform' />\n    <member type='node' ref='1987802673' role='stop' />\n    <member type='node' ref='7647758213' role='platform' />\n    <member type='node' ref='1987802675' role='stop' />\n    <member type='node' ref='7647758212' role='platform' />\n    <member type='node' ref='1987802677' role='stop' />\n    <member type='node' ref='7647758211' role='platform' />\n    <member type='node' ref='1987802678' role='stop' />\n    <member type='node' ref='7647758210' role='platform' />\n    <member type='node' ref='7779170659' role='stop' />\n    <member type='node' ref='7650108367' role='platform' />\n    <member type='node' ref='3393833062' role='stop' />\n    <member type='way' ref='332263376' role='platform' />\n    <member type='way' ref='409229495' role='' />\n    <member type='way' ref='418093371' role='' />\n    <member type='way' ref='660940856' role='' />\n    <member type='way' ref='833202560' role='' />\n    <member type='way' ref='25071631' role='' />\n    <member type='way' ref='779684596' role='' />\n    <member type='way' ref='12366292' role='' />\n    <member type='way' ref='28250564' role='' />\n    <member type='way' ref='129306828' role='' />\n    <member type='way' ref='689228726' role='' />\n    <member type='way' ref='42495777' role='' />\n    <member type='way' ref='24881997' role='' />\n    <member type='way' ref='24881998' role='' />\n    <member type='way' ref='12366288' role='' />\n    <member type='way' ref='26938154' role='' />\n    <member type='way' ref='124908358' role='' />\n    <member type='way' ref='5012895' role='' />\n    <member type='way' ref='549342941' role='' />\n    <member type='way' ref='176393050' role='' />\n    <member type='way' ref='82229934' role='' />\n    <member type='way' ref='157770466' role='' />\n    <member type='way' ref='481437087' role='' />\n    <member type='way' ref='112350521' role='' />\n    <member type='way' ref='111367668' role='' />\n    <member type='way' ref='111367672' role='' />\n    <member type='way' ref='165082901' role='' />\n    <member type='way' ref='165082898' role='' />\n    <member type='way' ref='159419948' role='' />\n    <member type='way' ref='119384619' role='' />\n    <member type='way' ref='833202561' role='' />\n    <member type='way' ref='161385871' role='' />\n    <member type='way' ref='833202562' role='' />\n    <member type='way' ref='161385868' role='' />\n    <member type='way' ref='659869780' role='' />\n    <member type='way' ref='4586903' role='' />\n    <member type='way' ref='665676989' role='' />\n    <member type='way' ref='665676988' role='' />\n    <member type='way' ref='4586906' role='' />\n    <member type='way' ref='204165651' role='' />\n    <member type='way' ref='204165655' role='' />\n    <member type='way' ref='4586908' role='' />\n    <member type='way' ref='833202558' role='' />\n    <member type='way' ref='617758920' role='' />\n    <member type='way' ref='833202557' role='' />\n    <member type='way' ref='54971336' role='' />\n    <member type='way' ref='441637247' role='' />\n    <member type='way' ref='833202569' role='' />\n    <member type='way' ref='833202568' role='' />\n    <member type='way' ref='34585084' role='' />\n    <member type='way' ref='34585085' role='' />\n    <member type='way' ref='4879732' role='' />\n    <member type='way' ref='125556131' role='' />\n    <member type='way' ref='4586911' role='' />\n    <member type='way' ref='4586914' role='' />\n    <member type='way' ref='4586915' role='' />\n    <member type='way' ref='165254732' role='' />\n    <member type='way' ref='833202555' role='' />\n    <member type='way' ref='23107116' role='' />\n    <member type='way' ref='411261626' role='' />\n    <member type='way' ref='251785304' role='' />\n    <member type='way' ref='497720745' role='' />\n    <member type='way' ref='165847600' role='' />\n    <member type='way' ref='497720748' role='' />\n    <member type='way' ref='618075569' role='' />\n    <member type='way' ref='618075573' role='' />\n    <member type='way' ref='116805491' role='' />\n    <member type='way' ref='618075572' role='' />\n    <member type='way' ref='497716930' role='' />\n    <member type='way' ref='497716931' role='' />\n    <member type='way' ref='497716932' role='' />\n    <member type='way' ref='99736105' role='' />\n    <member type='way' ref='497716933' role='' />\n    <member type='way' ref='504782332' role='' />\n    <member type='way' ref='29503672' role='' />\n    <member type='way' ref='504782336' role='' />\n    <member type='way' ref='504782339' role='' />\n    <member type='way' ref='497716934' role='' />\n    <member type='way' ref='22753514' role='' />\n    <member type='way' ref='497511541' role='' />\n    <member type='way' ref='497515586' role='' />\n    <member type='way' ref='497514032' role='' />\n    <member type='way' ref='22753515' role='' />\n    <member type='way' ref='497514031' role='' />\n    <member type='way' ref='701680686' role='' />\n    <member type='way' ref='26235821' role='' />\n    <member type='way' ref='26235823' role='' />\n    <member type='way' ref='26235824' role='' />\n    <member type='way' ref='833202589' role='' />\n    <member type='way' ref='833202553' role='' />\n    <member type='way' ref='833202572' role='' />\n    <member type='way' ref='833202571' role='' />\n    <member type='way' ref='26235826' role='' />\n    <member type='way' ref='833202602' role='' />\n    <member type='way' ref='26235821' role='' />\n    <member type='way' ref='701680686' role='' />\n    <member type='way' ref='497514031' role='' />\n    <member type='way' ref='22753515' role='' />\n    <member type='way' ref='497514032' role='' />\n    <member type='way' ref='497515586' role='' />\n    <member type='way' ref='497511541' role='' />\n    <member type='way' ref='22753514' role='' />\n    <member type='way' ref='497716934' role='' />\n    <member type='way' ref='504782339' role='' />\n    <member type='way' ref='504782331' role='' />\n    <member type='way' ref='22753513' role='' />\n    <member type='way' ref='517188131' role='' />\n    <member type='way' ref='504782337' role='' />\n    <member type='way' ref='99736105' role='' />\n    <member type='way' ref='497716932' role='' />\n    <member type='way' ref='497716931' role='' />\n    <member type='way' ref='497716930' role='' />\n    <member type='way' ref='618075572' role='' />\n    <member type='way' ref='116805491' role='' />\n    <member type='way' ref='618075573' role='' />\n    <member type='way' ref='618075569' role='' />\n    <member type='way' ref='497720748' role='' />\n    <member type='way' ref='165847600' role='' />\n    <member type='way' ref='497720745' role='' />\n    <member type='way' ref='251785304' role='' />\n    <member type='way' ref='28212074' role='' />\n    <member type='way' ref='4586918' role='' />\n    <member type='way' ref='29216348' role='' />\n    <member type='way' ref='29216350' role='' />\n    <member type='way' ref='4586919' role='' />\n    <member type='way' ref='4586922' role='' />\n    <member type='way' ref='30447473' role='' />\n    <member type='way' ref='30447474' role='' />\n    <member type='way' ref='660630408' role='' />\n    <member type='way' ref='28212155' role='' />\n    <member type='way' ref='122304236' role='' />\n    <member type='way' ref='675382051' role='' />\n    <member type='way' ref='62289647' role='' />\n    <member type='way' ref='58075168' role='' />\n    <member type='way' ref='219837014' role='' />\n    <member type='way' ref='509102670' role='' />\n    <member type='way' ref='366669793' role='' />\n    <member type='way' ref='366669796' role='' />\n    <member type='way' ref='578209354' role='' />\n    <member type='way' ref='135052326' role='' />\n    <member type='way' ref='24629915' role='' />\n    <member type='way' ref='24629870' role='' />\n    <member type='way' ref='25533532' role='' />\n    <member type='way' ref='53772038' role='' />\n    <member type='way' ref='40814025' role='' />\n    <member type='way' ref='40814026' role='' />\n    <member type='way' ref='664891129' role='' />\n    <member type='way' ref='30050896' role='' />\n    <member type='way' ref='213210051' role='' />\n    <member type='way' ref='40814532' role='' />\n    <member type='way' ref='40814531' role='' />\n    <member type='way' ref='23486733' role='' />\n    <member type='way' ref='449199494' role='' />\n    <member type='way' ref='449199493' role='' />\n    <member type='way' ref='23486732' role='' />\n    <member type='way' ref='52565598' role='' />\n    <member type='way' ref='29358757' role='' />\n    <member type='way' ref='664891127' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175447' role='' />\n    <member type='way' ref='369175445' role='' />\n    <member type='way' ref='369175441' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='810335209' role='' />\n    <member type='way' ref='513492326' role='' />\n    <member type='way' ref='23486731' role='' />\n    <member type='way' ref='761557032' role='' />\n    <member type='way' ref='35438709' role='' />\n    <member type='way' ref='59002627' role='' />\n    <member type='way' ref='383314127' role='' />\n    <member type='way' ref='661407753' role='' />\n    <member type='way' ref='35540883' role='' />\n    <member type='way' ref='52564295' role='' />\n    <member type='way' ref='40723871' role='' />\n    <member type='way' ref='56054538' role='' />\n    <member type='way' ref='56054536' role='' />\n    <member type='way' ref='670507676' role='' />\n    <member type='way' ref='24163693' role='' />\n    <member type='way' ref='59002626' role='' />\n    <member type='way' ref='159161919' role='' />\n    <member type='way' ref='274704232' role='' />\n    <member type='way' ref='274704230' role='' />\n    <member type='way' ref='4586892' role='' />\n    <member type='way' ref='125556138' role='' />\n    <member type='way' ref='692474886' role='' />\n    <member type='way' ref='4586021' role='' />\n    <member type='way' ref='492836256' role='' />\n    <member type='way' ref='51754193' role='' />\n    <member type='way' ref='386593763' role='' />\n    <member type='way' ref='46969880' role='' />\n    <member type='way' ref='4586023' role='' />\n    <member type='way' ref='130688167' role='' />\n    <member type='way' ref='492828702' role='' />\n    <member type='way' ref='241787608' role='' />\n    <member type='way' ref='33787292' role='' />\n    <member type='way' ref='492828703' role='' />\n    <member type='way' ref='125225779' role='' />\n    <member type='way' ref='234346248' role='' />\n    <member type='way' ref='125225780' role='' />\n    <member type='way' ref='241787617' role='' />\n    <member type='way' ref='241787614' role='' />\n    <member type='way' ref='241787609' role='' />\n    <member type='way' ref='241787613' role='' />\n    <member type='way' ref='119161423' role='' />\n    <member type='way' ref='119161417' role='' />\n    <member type='way' ref='38028832' role='' />\n    <member type='way' ref='38028833' role='' />\n    <member type='way' ref='627085346' role='' />\n    <member type='way' ref='144915856' role='' />\n    <member type='way' ref='144915857' role='' />\n    <member type='way' ref='492828704' role='' />\n    <member type='way' ref='492828706' role='' />\n    <member type='way' ref='38028161' role='' />\n    <member type='way' ref='492828708' role='' />\n    <member type='way' ref='38510311' role='' />\n    <member type='way' ref='144915852' role='' />\n    <member type='way' ref='843150468' role='' />\n    <member type='way' ref='30406545' role='' />\n    <member type='way' ref='30406549' role='' />\n    <member type='way' ref='843150467' role='' />\n    <member type='way' ref='130294856' role='' />\n    <member type='way' ref='197290015' role='' />\n    <member type='way' ref='184792914' role='' />\n    <member type='way' ref='689482243' role='' />\n    <member type='way' ref='321378455' role='' />\n    <member type='way' ref='234119941' role='' />\n    <member type='way' ref='10215146' role='' />\n    <member type='way' ref='520517117' role='' />\n    <member type='way' ref='498867438' role='' />\n    <member type='way' ref='835340624' role='' />\n    <member type='way' ref='498859729' role='' />\n    <member type='way' ref='318944792' role='' />\n    <member type='way' ref='498859743' role='' />\n    <member type='way' ref='541532867' role='' />\n    <member type='way' ref='761946430' role='' />\n    <member type='way' ref='541532868' role='' />\n    <member type='way' ref='692361513' role='' />\n    <member type='way' ref='692361511' role='' />\n    <member type='way' ref='541532866' role='' />\n    <member type='way' ref='541532865' role='' />\n    <member type='way' ref='541532861' role='' />\n    <member type='way' ref='541532864' role='' />\n    <member type='way' ref='146511185' role='' />\n    <member type='way' ref='541532863' role='' />\n    <member type='way' ref='146511187' role='' />\n    <member type='way' ref='146511189' role='' />\n    <member type='way' ref='23320471' role='' />\n    <member type='way' ref='440140529' role='' />\n    <member type='way' ref='440140532' role='' />\n    <member type='way' ref='440140531' role='' />\n    <member type='way' ref='440140528' role='' />\n    <member type='way' ref='529371826' role='' />\n    <member type='way' ref='9375303' role='' />\n    <member type='way' ref='279054989' role='' />\n    <member type='way' ref='279054987' role='' />\n    <member type='way' ref='46766460' role='' />\n    <member type='way' ref='46766459' role='' />\n    <member type='way' ref='289756324' role='' />\n    <member type='way' ref='161465009' role='' />\n    <member type='way' ref='51676172' role='' />\n    <member type='way' ref='772699194' role='' />\n    <member type='way' ref='819559515' role='' />\n    <member type='way' ref='188155922' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='819559513' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819308078' role='' />\n    <member type='way' ref='819559512' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='819559511' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='819308077' role='' />\n    <member type='way' ref='818931650' role='' />\n    <member type='way' ref='188155891' role='' />\n    <member type='way' ref='833318550' role='' />\n    <member type='way' ref='161208735' role='' />\n    <member type='way' ref='517466647' role='' />\n    <member type='way' ref='279054991' role='' />\n    <member type='way' ref='397936984' role='' />\n    <member type='way' ref='23338944' role='' />\n    <member type='way' ref='513775934' role='' />\n    <member type='way' ref='513775931' role='' />\n    <member type='way' ref='285963332' role='' />\n    <member type='way' ref='513775935' role='' />\n    <member type='way' ref='714366703' role='' />\n    <member type='way' ref='833318552' role='' />\n    <member type='way' ref='714526872' role='' />\n    <member type='way' ref='833318553' role='' />\n    <member type='way' ref='833318551' role='' />\n    <member type='way' ref='513775939' role='' />\n    <member type='way' ref='513775930' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='39461687' role='' />\n    <member type='way' ref='682688470' role='' />\n    <member type='way' ref='414177890' role='' />\n    <member type='way' ref='517427345' role='' />\n    <member type='way' ref='517427348' role='' />\n    <member type='way' ref='379126354' role='' />\n    <member type='way' ref='823646246' role='' />\n    <member type='way' ref='9654384' role='' />\n    <member type='way' ref='728210419' role='' />\n    <member type='way' ref='513775944' role='' />\n    <member type='way' ref='617119454' role='' />\n    <member type='way' ref='10999525' role='' />\n    <member type='way' ref='319841468' role='' />\n    <member type='way' ref='546295339' role='' />\n    <member type='way' ref='510335879' role='' />\n    <member type='way' ref='510335862' role='' />\n    <member type='way' ref='161190692' role='' />\n    <member type='way' ref='510335870' role='' />\n    <member type='way' ref='161486851' role='' />\n    <member type='way' ref='510335842' role='' />\n    <member type='way' ref='510335846' role='' />\n    <member type='way' ref='788083572' role='' />\n    <member type='way' ref='319841465' role='' />\n    <member type='way' ref='741996204' role='' />\n    <member type='way' ref='23444180' role='' />\n    <member type='way' ref='741996205' role='' />\n    <member type='way' ref='272706515' role='' />\n    <member type='way' ref='788083573' role='' />\n    <member type='way' ref='272706514' role='' />\n    <member type='way' ref='272706525' role='' />\n    <member type='way' ref='272706535' role='' />\n    <member type='way' ref='682518206' role='' />\n    <member type='way' ref='272706511' role='' />\n    <member type='way' ref='272706533' role='' />\n    <member type='way' ref='510581880' role='' />\n    <member type='way' ref='272706517' role='' />\n    <member type='way' ref='272706532' role='' />\n    <member type='way' ref='272706531' role='' />\n    <member type='way' ref='272706520' role='' />\n    <member type='way' ref='272706524' role='' />\n    <member type='way' ref='272706513' role='' />\n    <member type='way' ref='272706510' role='' />\n    <member type='way' ref='272706528' role='' />\n    <member type='way' ref='688687939' role='' />\n    <member type='way' ref='510624279' role='' />\n    <member type='way' ref='510624268' role='' />\n    <member type='way' ref='272706526' role='' />\n    <member type='way' ref='510624247' role='' />\n    <member type='way' ref='618993313' role='' />\n    <member type='way' ref='24430829' role='' />\n    <member type='way' ref='517466669' role='' />\n    <member type='way' ref='56323454' role='' />\n    <member type='way' ref='517466668' role='' />\n    <member type='way' ref='510632776' role='' />\n    <member type='way' ref='44829595' role='' />\n    <member type='way' ref='161466777' role='' />\n    <member type='way' ref='326276383' role='' />\n    <member type='way' ref='11003381' role='' />\n    <member type='way' ref='326276396' role='' />\n    <member type='way' ref='161187739' role='' />\n    <member type='way' ref='326276387' role='' />\n    <member type='way' ref='326276391' role='' />\n    <member type='way' ref='43076107' role='' />\n    <member type='way' ref='71391950' role='' />\n    <member type='way' ref='771999805' role='' />\n    <member type='way' ref='59638078' role='' />\n    <member type='way' ref='309700793' role='' />\n    <member type='way' ref='309700794' role='' />\n    <member type='way' ref='27064373' role='' />\n    <member type='way' ref='27064374' role='' />\n    <member type='way' ref='326156168' role='' />\n    <member type='way' ref='188163074' role='' />\n    <member type='way' ref='326156169' role='' />\n    <member type='way' ref='11003452' role='' />\n    <member type='way' ref='188163076' role='' />\n    <member type='way' ref='492828713' role='' />\n    <member type='way' ref='492828712' role='' />\n    <member type='way' ref='51772367' role='' />\n    <member type='way' ref='99529286' role='' />\n    <member type='way' ref='99529506' role='' />\n    <member type='way' ref='51772369' role='' />\n    <member type='way' ref='305777809' role='' />\n    <member type='way' ref='728708233' role='' />\n    <member type='way' ref='99529656' role='' />\n    <member type='way' ref='99529468' role='' />\n    <member type='way' ref='39131598' role='' />\n    <member type='way' ref='39131601' role='' />\n    <member type='way' ref='123954806' role='' />\n    <member type='way' ref='99529252' role='' />\n    <member type='way' ref='87788719' role='' />\n    <member type='way' ref='99529664' role='' />\n    <member type='way' ref='99529601' role='' />\n    <member type='way' ref='728708231' role='' />\n    <member type='way' ref='123954808' role='' />\n    <member type='way' ref='99529458' role='' />\n    <member type='way' ref='99529614' role='' />\n    <member type='way' ref='99529541' role='' />\n    <member type='way' ref='99529300' role='' />\n    <member type='way' ref='99529669' role='' />\n    <member type='way' ref='98240455' role='' />\n    <member type='way' ref='98240453' role='' />\n    <member type='way' ref='813756628' role='' />\n    <member type='way' ref='98234177' role='' />\n    <member type='way' ref='98234175' role='' />\n    <member type='way' ref='319062894' role='' />\n    <member type='way' ref='319062895' role='' />\n    <member type='way' ref='209196326' role='' />\n    <member type='way' ref='209196328' role='' />\n    <member type='way' ref='106467525' role='' />\n    <member type='way' ref='106467520' role='' />\n    <member type='way' ref='231059758' role='' />\n    <member type='way' ref='231059756' role='' />\n    <member type='way' ref='709615630' role='' />\n    <member type='way' ref='29314712' role='' />\n    <member type='way' ref='29314711' role='' />\n    <member type='way' ref='163600212' role='' />\n    <member type='way' ref='837149996' role='' />\n    <member type='way' ref='837149997' role='' />\n    <member type='way' ref='837149995' role='' />\n    <member type='way' ref='837149998' role='' />\n    <member type='way' ref='813756627' role='' />\n    <member type='way' ref='427314412' role='' />\n    <member type='way' ref='838292586' role='' />\n    <member type='way' ref='782010056' role='' />\n    <member type='way' ref='782010057' role='' />\n    <member type='way' ref='132688030' role='' />\n    <member type='way' ref='838292597' role='' />\n    <member type='way' ref='8041982' role='' />\n    <member type='way' ref='838292592' role='' />\n    <member type='way' ref='8041981' role='' />\n    <member type='way' ref='252104525' role='' />\n    <member type='way' ref='252104520' role='' />\n    <member type='way' ref='848360981' role='' />\n    <member type='way' ref='72256434' role='' />\n    <member type='way' ref='681143380' role='' />\n    <member type='way' ref='681139365' role='' />\n    <member type='way' ref='107114265' role='' />\n    <member type='way' ref='275143515' role='' />\n    <member type='way' ref='496588160' role='' />\n    <member type='way' ref='496120886' role='' />\n    <member type='way' ref='825980028' role='' />\n    <member type='way' ref='779192145' role='' />\n    <member type='way' ref='825980013' role='' />\n    <member type='way' ref='368771669' role='' />\n    <member type='way' ref='825980021' role='' />\n    <member type='way' ref='368771668' role='' />\n    <member type='way' ref='825980016' role='' />\n    <member type='way' ref='782014017' role='' />\n    <member type='way' ref='825980011' role='' />\n    <member type='way' ref='782014016' role='' />\n    <member type='way' ref='286203393' role='' />\n    <member type='way' ref='496107793' role='' />\n    <member type='way' ref='496107794' role='' />\n    <member type='way' ref='779192156' role='' />\n    <member type='way' ref='286552816' role='' />\n    <member type='way' ref='71973851' role='' />\n    <member type='way' ref='348766149' role='' />\n    <member type='way' ref='348766145' role='' />\n    <member type='way' ref='348771994' role='' />\n    <member type='way' ref='285709739' role='' />\n    <tag k='from' v='Sønderborg Busstation' />\n    <tag k='name' v='Flixbus 636: Sønderborg Busstation =&gt; Aalborg, Busterminal' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='636' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Aalborg, Busterminal' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='11375268' timestamp='2020-09-01T13:26:02Z' uid='1288' user='Niels Elgaard Larsen' visible='true' version='4' changeset='90248784'>\n    <member type='node' ref='3393833062' role='stop' />\n    <member type='way' ref='332263376' role='platform' />\n    <member type='node' ref='7779170659' role='stop' />\n    <member type='node' ref='7650108367' role='platform' />\n    <member type='node' ref='1987802672' role='stop' />\n    <member type='node' ref='7647740030' role='platform' />\n    <member type='node' ref='1987802673' role='stop' />\n    <member type='node' ref='7647758213' role='platform' />\n    <member type='node' ref='1987802675' role='stop' />\n    <member type='node' ref='7647758212' role='platform' />\n    <member type='node' ref='1987802677' role='stop' />\n    <member type='node' ref='7647758211' role='platform' />\n    <member type='node' ref='1987802678' role='stop' />\n    <member type='node' ref='7647758210' role='platform' />\n    <member type='node' ref='6223447952' role='stop' />\n    <member type='way' ref='513595672' role='platform' />\n    <member type='node' ref='7778082410' role='stop' />\n    <member type='way' ref='833202575' role='platform' />\n    <member type='node' ref='7778059776' role='stop' />\n    <member type='node' ref='7778059777' role='platform' />\n    <member type='node' ref='7778059773' role='stop' />\n    <member type='way' ref='833202564' role='platform' />\n    <member type='way' ref='491845660' role='' />\n    <member type='way' ref='348763865' role='' />\n    <member type='way' ref='348763864' role='' />\n    <member type='way' ref='285709733' role='' />\n    <member type='way' ref='369152632' role='' />\n    <member type='way' ref='348766151' role='' />\n    <member type='way' ref='369151521' role='' />\n    <member type='way' ref='369151056' role='' />\n    <member type='way' ref='286552815' role='' />\n    <member type='way' ref='369151062' role='' />\n    <member type='way' ref='779192157' role='' />\n    <member type='way' ref='97574427' role='' />\n    <member type='way' ref='496580797' role='' />\n    <member type='way' ref='495987825' role='' />\n    <member type='way' ref='825980010' role='' />\n    <member type='way' ref='495987826' role='' />\n    <member type='way' ref='825980012' role='' />\n    <member type='way' ref='825980017' role='' />\n    <member type='way' ref='779192151' role='' />\n    <member type='way' ref='275143505' role='' />\n    <member type='way' ref='275143512' role='' />\n    <member type='way' ref='681139362' role='' />\n    <member type='way' ref='496249591' role='' />\n    <member type='way' ref='8169286' role='' />\n    <member type='way' ref='132688031' role='' />\n    <member type='way' ref='782010058' role='' />\n    <member type='way' ref='813756724' role='' />\n    <member type='way' ref='146514455' role='' />\n    <member type='way' ref='837149992' role='' />\n    <member type='way' ref='163600213' role='' />\n    <member type='way' ref='29314709' role='' />\n    <member type='way' ref='100211823' role='' />\n    <member type='way' ref='231059757' role='' />\n    <member type='way' ref='231059759' role='' />\n    <member type='way' ref='106467516' role='' />\n    <member type='way' ref='106467529' role='' />\n    <member type='way' ref='209196333' role='' />\n    <member type='way' ref='209196336' role='' />\n    <member type='way' ref='319062897' role='' />\n    <member type='way' ref='319062896' role='' />\n    <member type='way' ref='98234179' role='' />\n    <member type='way' ref='813756723' role='' />\n    <member type='way' ref='98234180' role='' />\n    <member type='way' ref='98240450' role='' />\n    <member type='way' ref='98240457' role='' />\n    <member type='way' ref='166899885' role='' />\n    <member type='way' ref='99529410' role='' />\n    <member type='way' ref='99529418' role='' />\n    <member type='way' ref='99529393' role='' />\n    <member type='way' ref='99529552' role='' />\n    <member type='way' ref='728708234' role='' />\n    <member type='way' ref='123954809' role='' />\n    <member type='way' ref='99529595' role='' />\n    <member type='way' ref='99529548' role='' />\n    <member type='way' ref='166899884' role='' />\n    <member type='way' ref='99529636' role='' />\n    <member type='way' ref='87788715' role='' />\n    <member type='way' ref='166899883' role='' />\n    <member type='way' ref='123954807' role='' />\n    <member type='way' ref='39131613' role='' />\n    <member type='way' ref='39131615' role='' />\n    <member type='way' ref='99529700' role='' />\n    <member type='way' ref='728708243' role='' />\n    <member type='way' ref='99529489' role='' />\n    <member type='way' ref='305777808' role='' />\n    <member type='way' ref='51772366' role='' />\n    <member type='way' ref='99529449' role='' />\n    <member type='way' ref='99529576' role='' />\n    <member type='way' ref='492828711' role='' />\n    <member type='way' ref='62227774' role='' />\n    <member type='way' ref='692148513' role='' />\n    <member type='way' ref='692148512' role='' />\n    <member type='way' ref='326285668' role='' />\n    <member type='way' ref='692148510' role='' />\n    <member type='way' ref='23376360' role='' />\n    <member type='way' ref='23376359' role='' />\n    <member type='way' ref='311697613' role='' />\n    <member type='way' ref='326156170' role='' />\n    <member type='way' ref='56045342' role='' />\n    <member type='way' ref='309700792' role='' />\n    <member type='way' ref='326285671' role='' />\n    <member type='way' ref='59638081' role='' />\n    <member type='way' ref='771999806' role='' />\n    <member type='way' ref='71391931' role='' />\n    <member type='way' ref='43076108' role='' />\n    <member type='way' ref='326276386' role='' />\n    <member type='way' ref='326276401' role='' />\n    <member type='way' ref='161187726' role='' />\n    <member type='way' ref='326285661' role='' />\n    <member type='way' ref='37343749' role='' />\n    <member type='way' ref='161466779' role='' />\n    <member type='way' ref='44829596' role='' />\n    <member type='way' ref='517466670' role='' />\n    <member type='way' ref='517466671' role='' />\n    <member type='way' ref='153531784' role='' />\n    <member type='way' ref='510624260' role='' />\n    <member type='way' ref='682500794' role='' />\n    <member type='way' ref='510624310' role='' />\n    <member type='way' ref='517466664' role='' />\n    <member type='way' ref='510624259' role='' />\n    <member type='way' ref='517466663' role='' />\n    <member type='way' ref='510624304' role='' />\n    <member type='way' ref='510624303' role='' />\n    <member type='way' ref='510624305' role='' />\n    <member type='way' ref='510624309' role='' />\n    <member type='way' ref='10999808' role='' />\n    <member type='way' ref='272706522' role='' />\n    <member type='way' ref='272706527' role='' />\n    <member type='way' ref='272706508' role='' />\n    <member type='way' ref='272706519' role='' />\n    <member type='way' ref='272706530' role='' />\n    <member type='way' ref='272706521' role='' />\n    <member type='way' ref='272706534' role='' />\n    <member type='way' ref='741996213' role='' />\n    <member type='way' ref='161469199' role='' />\n    <member type='way' ref='741996207' role='' />\n    <member type='way' ref='23444181' role='' />\n    <member type='way' ref='788083570' role='' />\n    <member type='way' ref='788083569' role='' />\n    <member type='way' ref='319841464' role='' />\n    <member type='way' ref='510335857' role='' />\n    <member type='way' ref='161486841' role='' />\n    <member type='way' ref='517427354' role='' />\n    <member type='way' ref='9654314' role='' />\n    <member type='way' ref='510335860' role='' />\n    <member type='way' ref='510335854' role='' />\n    <member type='way' ref='161190702' role='' />\n    <member type='way' ref='517427353' role='' />\n    <member type='way' ref='319841469' role='' />\n    <member type='way' ref='560552446' role='' />\n    <member type='way' ref='319841466' role='' />\n    <member type='way' ref='161464999' role='' />\n    <member type='way' ref='42424841' role='' />\n    <member type='way' ref='823646245' role='' />\n    <member type='way' ref='23629785' role='' />\n    <member type='way' ref='517427341' role='' />\n    <member type='way' ref='379126351' role='' />\n    <member type='way' ref='414177889' role='' />\n    <member type='way' ref='682688472' role='' />\n    <member type='way' ref='23338617' role='' />\n    <member type='way' ref='513775941' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='513775934' role='' />\n    <member type='way' ref='513775931' role='' />\n    <member type='way' ref='285963332' role='' />\n    <member type='way' ref='513775935' role='' />\n    <member type='way' ref='714366703' role='' />\n    <member type='way' ref='833318552' role='' />\n    <member type='way' ref='714526872' role='' />\n    <member type='way' ref='833318553' role='' />\n    <member type='way' ref='833318551' role='' />\n    <member type='way' ref='513775939' role='' />\n    <member type='way' ref='513775930' role='' />\n    <member type='way' ref='513775940' role='' />\n    <member type='way' ref='285963331' role='' />\n    <member type='way' ref='513775941' role='' />\n    <member type='way' ref='686698865' role='' />\n    <member type='way' ref='517427335' role='' />\n    <member type='way' ref='517427330' role='' />\n    <member type='way' ref='517466645' role='' />\n    <member type='way' ref='517466643' role='' />\n    <member type='way' ref='517466644' role='' />\n    <member type='way' ref='161208735' role='' />\n    <member type='way' ref='833318550' role='' />\n    <member type='way' ref='728192376' role='' />\n    <member type='way' ref='517466646' role='' />\n    <member type='way' ref='517466648' role='' />\n    <member type='way' ref='279054992' role='' />\n    <member type='way' ref='51676172' role='' />\n    <member type='way' ref='772699194' role='' />\n    <member type='way' ref='819559515' role='' />\n    <member type='way' ref='188155922' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='818931648' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='188155897' role='' />\n    <member type='way' ref='819559513' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819559514' role='' />\n    <member type='way' ref='819308078' role='' />\n    <member type='way' ref='819559512' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='188155913' role='' />\n    <member type='way' ref='819559511' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='188155893' role='' />\n    <member type='way' ref='819308077' role='' />\n    <member type='way' ref='818931650' role='' />\n    <member type='way' ref='188155891' role='' />\n    <member type='way' ref='728192376' role='' />\n    <member type='way' ref='517466646' role='' />\n    <member type='way' ref='517466648' role='' />\n    <member type='way' ref='279054992' role='' />\n    <member type='way' ref='161465009' role='' />\n    <member type='way' ref='289756324' role='' />\n    <member type='way' ref='46766459' role='' />\n    <member type='way' ref='46766460' role='' />\n    <member type='way' ref='279054987' role='' />\n    <member type='way' ref='279054989' role='' />\n    <member type='way' ref='9375303' role='' />\n    <member type='way' ref='529371826' role='' />\n    <member type='way' ref='440140528' role='' />\n    <member type='way' ref='440140531' role='' />\n    <member type='way' ref='161190693' role='' />\n    <member type='way' ref='440140529' role='' />\n    <member type='way' ref='23320471' role='' />\n    <member type='way' ref='146511189' role='' />\n    <member type='way' ref='146511188' role='' />\n    <member type='way' ref='143821024' role='' />\n    <member type='way' ref='541532857' role='' />\n    <member type='way' ref='8812730' role='' />\n    <member type='way' ref='541532855' role='' />\n    <member type='way' ref='692361519' role='' />\n    <member type='way' ref='692361505' role='' />\n    <member type='way' ref='161234020' role='' />\n    <member type='way' ref='761946431' role='' />\n    <member type='way' ref='835340625' role='' />\n    <member type='way' ref='498859739' role='' />\n    <member type='way' ref='498859736' role='' />\n    <member type='way' ref='835340627' role='' />\n    <member type='way' ref='498859744' role='' />\n    <member type='way' ref='520517114' role='' />\n    <member type='way' ref='128898677' role='' />\n    <member type='way' ref='348352697' role='' />\n    <member type='way' ref='234119942' role='' />\n    <member type='way' ref='321378453' role='' />\n    <member type='way' ref='689482242' role='' />\n    <member type='way' ref='321378452' role='' />\n    <member type='way' ref='184792919' role='' />\n    <member type='way' ref='192705441' role='' />\n    <member type='way' ref='144918016' role='' />\n    <member type='way' ref='144918017' role='' />\n    <member type='way' ref='144918015' role='' />\n    <member type='way' ref='843150465' role='' />\n    <member type='way' ref='321378456' role='' />\n    <member type='way' ref='30406550' role='' />\n    <member type='way' ref='843150466' role='' />\n    <member type='way' ref='30406546' role='' />\n    <member type='way' ref='843150464' role='' />\n    <member type='way' ref='144915853' role='' />\n    <member type='way' ref='144915854' role='' />\n    <member type='way' ref='144915851' role='' />\n    <member type='way' ref='492828707' role='' />\n    <member type='way' ref='123458827' role='' />\n    <member type='way' ref='30407255' role='' />\n    <member type='way' ref='492828705' role='' />\n    <member type='way' ref='123458828' role='' />\n    <member type='way' ref='144915855' role='' />\n    <member type='way' ref='627085345' role='' />\n    <member type='way' ref='633456015' role='' />\n    <member type='way' ref='51754191' role='' />\n    <member type='way' ref='38028830' role='' />\n    <member type='way' ref='38028831' role='' />\n    <member type='way' ref='119161422' role='' />\n    <member type='way' ref='119161418' role='' />\n    <member type='way' ref='241787612' role='' />\n    <member type='way' ref='833243911' role='' />\n    <member type='way' ref='241787611' role='' />\n    <member type='way' ref='241787610' role='' />\n    <member type='way' ref='125592653' role='' />\n    <member type='way' ref='241787616' role='' />\n    <member type='way' ref='234346247' role='' />\n    <member type='way' ref='125592652' role='' />\n    <member type='way' ref='492828700' role='' />\n    <member type='way' ref='241787618' role='' />\n    <member type='way' ref='33787295' role='' />\n    <member type='way' ref='492828701' role='' />\n    <member type='way' ref='492836262' role='' />\n    <member type='way' ref='492836260' role='' />\n    <member type='way' ref='492836259' role='' />\n    <member type='way' ref='46969881' role='' />\n    <member type='way' ref='24166011' role='' />\n    <member type='way' ref='4586022' role='' />\n    <member type='way' ref='46969879' role='' />\n    <member type='way' ref='492836255' role='' />\n    <member type='way' ref='51754192' role='' />\n    <member type='way' ref='386593765' role='' />\n    <member type='way' ref='676193412' role='' />\n    <member type='way' ref='4586020' role='' />\n    <member type='way' ref='125556146' role='' />\n    <member type='way' ref='2014317' role='' />\n    <member type='way' ref='4586931' role='' />\n    <member type='way' ref='9470519' role='' />\n    <member type='way' ref='26713012' role='' />\n    <member type='way' ref='297696236' role='' />\n    <member type='way' ref='297696237' role='' />\n    <member type='way' ref='22969810' role='' />\n    <member type='way' ref='58999665' role='' />\n    <member type='way' ref='672713513' role='' />\n    <member type='way' ref='58999666' role='' />\n    <member type='way' ref='62073052' role='' />\n    <member type='way' ref='62073051' role='' />\n    <member type='way' ref='59002628' role='' />\n    <member type='way' ref='254867496' role='' />\n    <member type='way' ref='254867498' role='' />\n    <member type='way' ref='35438709' role='' />\n    <member type='way' ref='761557032' role='' />\n    <member type='way' ref='23486731' role='' />\n    <member type='way' ref='513492326' role='' />\n    <member type='way' ref='810335209' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175447' role='' />\n    <member type='way' ref='369175445' role='' />\n    <member type='way' ref='369175441' role='' />\n    <member type='way' ref='369175444' role='' />\n    <member type='way' ref='369175446' role='' />\n    <member type='way' ref='664891127' role='' />\n    <member type='way' ref='29358757' role='' />\n    <member type='way' ref='52565598' role='' />\n    <member type='way' ref='23486732' role='' />\n    <member type='way' ref='449199493' role='' />\n    <member type='way' ref='449199494' role='' />\n    <member type='way' ref='23486733' role='' />\n    <member type='way' ref='40814531' role='' />\n    <member type='way' ref='40814532' role='' />\n    <member type='way' ref='213210051' role='' />\n    <member type='way' ref='30050896' role='' />\n    <member type='way' ref='664891129' role='' />\n    <member type='way' ref='40814026' role='' />\n    <member type='way' ref='40814025' role='' />\n    <member type='way' ref='53772038' role='' />\n    <member type='way' ref='53772035' role='' />\n    <member type='way' ref='24629915' role='' />\n    <member type='way' ref='135052326' role='' />\n    <member type='way' ref='578209354' role='' />\n    <member type='way' ref='366669796' role='' />\n    <member type='way' ref='366669793' role='' />\n    <member type='way' ref='219837015' role='' />\n    <member type='way' ref='497483693' role='' />\n    <member type='way' ref='219837016' role='' />\n    <member type='way' ref='675382052' role='' />\n    <member type='way' ref='672529639' role='' />\n    <member type='way' ref='672529638' role='' />\n    <member type='way' ref='173885536' role='' />\n    <member type='way' ref='173885534' role='' />\n    <member type='way' ref='539715724' role='' />\n    <member type='way' ref='322752604' role='' />\n    <member type='way' ref='660630405' role='' />\n    <member type='way' ref='53088754' role='' />\n    <member type='way' ref='30447471' role='' />\n    <member type='way' ref='30447472' role='' />\n    <member type='way' ref='4586920' role='' />\n    <member type='way' ref='4586917' role='' />\n    <member type='way' ref='29216340' role='' />\n    <member type='way' ref='833202603' role='' />\n    <member type='way' ref='23298345' role='' />\n    <member type='way' ref='22753447' role='' />\n    <member type='way' ref='22753448' role='' />\n    <member type='way' ref='22753450' role='' />\n    <member type='way' ref='309363783' role='' />\n    <member type='way' ref='251785304' role='' />\n    <member type='way' ref='497720745' role='' />\n    <member type='way' ref='165847600' role='' />\n    <member type='way' ref='497720748' role='' />\n    <member type='way' ref='618075569' role='' />\n    <member type='way' ref='618075573' role='' />\n    <member type='way' ref='116805491' role='' />\n    <member type='way' ref='618075572' role='' />\n    <member type='way' ref='497716930' role='' />\n    <member type='way' ref='497716931' role='' />\n    <member type='way' ref='497716932' role='' />\n    <member type='way' ref='99736105' role='' />\n    <member type='way' ref='497716933' role='' />\n    <member type='way' ref='504782332' role='' />\n    <member type='way' ref='29503672' role='' />\n    <member type='way' ref='504782336' role='' />\n    <member type='way' ref='504782339' role='' />\n    <member type='way' ref='497716934' role='' />\n    <member type='way' ref='22753514' role='' />\n    <member type='way' ref='497511541' role='' />\n    <member type='way' ref='497515586' role='' />\n    <member type='way' ref='497514032' role='' />\n    <member type='way' ref='22753515' role='' />\n    <member type='way' ref='497514031' role='' />\n    <member type='way' ref='701680686' role='' />\n    <member type='way' ref='26235821' role='' />\n    <member type='way' ref='26235823' role='' />\n    <member type='way' ref='26235824' role='' />\n    <member type='way' ref='833202589' role='' />\n    <member type='way' ref='833202553' role='' />\n    <member type='way' ref='833202572' role='' />\n    <member type='way' ref='833202571' role='' />\n    <member type='way' ref='26235826' role='' />\n    <member type='way' ref='833202602' role='' />\n    <member type='way' ref='26235821' role='' />\n    <member type='way' ref='701680686' role='' />\n    <member type='way' ref='497514031' role='' />\n    <member type='way' ref='22753515' role='' />\n    <member type='way' ref='497514032' role='' />\n    <member type='way' ref='497515586' role='' />\n    <member type='way' ref='497511541' role='' />\n    <member type='way' ref='22753514' role='' />\n    <member type='way' ref='497716934' role='' />\n    <member type='way' ref='504782339' role='' />\n    <member type='way' ref='504782331' role='' />\n    <member type='way' ref='22753513' role='' />\n    <member type='way' ref='517188131' role='' />\n    <member type='way' ref='504782337' role='' />\n    <member type='way' ref='99736105' role='' />\n    <member type='way' ref='497716932' role='' />\n    <member type='way' ref='497716931' role='' />\n    <member type='way' ref='497716930' role='' />\n    <member type='way' ref='618075572' role='' />\n    <member type='way' ref='116805491' role='' />\n    <member type='way' ref='618075573' role='' />\n    <member type='way' ref='618075569' role='' />\n    <member type='way' ref='497720748' role='' />\n    <member type='way' ref='165847600' role='' />\n    <member type='way' ref='497720745' role='' />\n    <member type='way' ref='251785304' role='' />\n    <member type='way' ref='309363783' role='' />\n    <member type='way' ref='22753450' role='' />\n    <member type='way' ref='411261634' role='' />\n    <member type='way' ref='22753447' role='' />\n    <member type='way' ref='23107117' role='' />\n    <member type='way' ref='833202570' role='' />\n    <member type='way' ref='165254731' role='' />\n    <member type='way' ref='165254734' role='' />\n    <member type='way' ref='4586912' role='' />\n    <member type='way' ref='125556133' role='' />\n    <member type='way' ref='1838201' role='' />\n    <member type='way' ref='4879733' role='' />\n    <member type='way' ref='27050053' role='' />\n    <member type='way' ref='27050045' role='' />\n    <member type='way' ref='4879732' role='' />\n    <member type='way' ref='832191187' role='' />\n    <member type='way' ref='833202557' role='' />\n    <member type='way' ref='54971336' role='' />\n    <member type='way' ref='441637247' role='' />\n    <member type='way' ref='833202569' role='' />\n    <member type='way' ref='833202568' role='' />\n    <member type='way' ref='34585084' role='' />\n    <member type='way' ref='34585085' role='' />\n    <member type='way' ref='4879732' role='' />\n    <member type='way' ref='18350887' role='' />\n    <member type='way' ref='27050038' role='' />\n    <member type='way' ref='27050057' role='' />\n    <member type='way' ref='1838201' role='' />\n    <member type='way' ref='125556129' role='' />\n    <member type='way' ref='125556125' role='' />\n    <member type='way' ref='27285022' role='' />\n    <member type='way' ref='204165652' role='' />\n    <member type='way' ref='204165653' role='' />\n    <member type='way' ref='661057632' role='' />\n    <member type='way' ref='661057631' role='' />\n    <member type='way' ref='661055344' role='' />\n    <member type='way' ref='4586904' role='' />\n    <member type='way' ref='4586901' role='' />\n    <member type='way' ref='661055345' role='' />\n    <member type='way' ref='161385872' role='' />\n    <member type='way' ref='28972849' role='' />\n    <member type='way' ref='119384634' role='' />\n    <member type='way' ref='119384635' role='' />\n    <member type='way' ref='159419942' role='' />\n    <member type='way' ref='165082900' role='' />\n    <member type='way' ref='165082902' role='' />\n    <member type='way' ref='111367678' role='' />\n    <member type='way' ref='112350520' role='' />\n    <member type='way' ref='143048972' role='' />\n    <member type='way' ref='161385860' role='' />\n    <member type='way' ref='157770467' role='' />\n    <member type='way' ref='176393053' role='' />\n    <member type='way' ref='2787485' role='' />\n    <member type='way' ref='168650815' role='' />\n    <member type='way' ref='660630206' role='' />\n    <member type='way' ref='168650816' role='' />\n    <member type='way' ref='689228726' role='' />\n    <member type='way' ref='129306828' role='' />\n    <member type='way' ref='28250564' role='' />\n    <member type='way' ref='12366292' role='' />\n    <member type='way' ref='779684596' role='' />\n    <member type='way' ref='13534581' role='' />\n    <member type='way' ref='779684597' role='' />\n    <member type='way' ref='13534583' role='' />\n    <member type='way' ref='437538127' role='' />\n    <member type='way' ref='833202563' role='' />\n    <tag k='from' v='Aalborg, Busterminal' />\n    <tag k='name' v='Flixbus 636: Aalborg, Busterminal =&gt; Sønderborg, Busstation' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='636' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Sønderborg, Busstation' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='11375269' timestamp='2020-08-09T11:43:40Z' uid='1281932' user='marcoSt' visible='true' version='2' changeset='89151310'>\n    <member type='relation' ref='11375268' role='' />\n    <member type='relation' ref='11375267' role='' />\n    <tag k='from' v='Aalborg, Busterminal' />\n    <tag k='name' v='Flixbus 636: Aalborg &lt;=&gt; Sønderborg' />\n    <tag k='network' v='Flixbus' />\n    <tag k='operator' v='Flixbus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='636' />\n    <tag k='route_master' v='bus' />\n    <tag k='to' v='Sønderborg, Busstation' />\n    <tag k='type' v='route_master' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/u-turn-shape-edge.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <bounds minlat='2.3875597' minlon='103.0193849' maxlat='2.3881848' maxlon='103.0200098' origin='CGImap 0.8.3 (2465992 spike-07.openstreetmap.org)' />\n  <node id='302093955' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.396766' lon='103.0189204' />\n  <node id='302094165' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3935738' lon='103.0192878' />\n  <node id='302094620' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84296405' lat='2.4053343' lon='103.0163964' />\n  <node id='302095128' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3957348' lon='103.0191001' />\n  <node id='302095462' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3960645' lon='103.0190653' />\n  <node id='302095471' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='6' changeset='84441283' lat='2.3851489' lon='103.0199489' />\n  <node id='457007251' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='5' changeset='84350728' lat='2.3928864' lon='103.0193511'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='457007254' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3990165' lon='103.0182602' />\n  <node id='457007259' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84350728' lat='2.3966332' lon='103.0189573' />\n  <node id='605390355' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84441747' lat='2.3845454' lon='103.0195875' />\n  <node id='605390357' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84441747' lat='2.3846845' lon='103.0196737' />\n  <node id='605390363' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84441283' lat='2.385138' lon='103.0198406' />\n  <node id='605390364' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='6' changeset='84358766' lat='2.3928777' lon='103.0192487'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='605390375' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84358766' lat='2.3971069' lon='103.0187327' />\n  <node id='605390383' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3989813' lon='103.0181817' />\n  <node id='605390385' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3982911' lon='103.0184863' />\n  <node id='605390387' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3982696' lon='103.0183897' />\n  <node id='605390392' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84296405' lat='2.4006756' lon='103.0177823' />\n  <node id='605390394' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84296405' lat='2.4049116' lon='103.0165603' />\n  <node id='605390395' timestamp='2019-12-17T09:09:18Z' uid='8072061' user='Diana IRM-ED' visible='true' version='2' changeset='78516766' lat='2.4006818' lon='103.0176958' />\n  <node id='605390397' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84296405' lat='2.4048862' lon='103.0164833' />\n  <node id='605390399' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3957132' lon='103.0190119' />\n  <node id='605390400' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3960508' lon='103.0189746' />\n  <node id='605390404' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84350728' lat='2.3950591' lon='103.0190598' />\n  <node id='605390410' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84358766' lat='2.396758' lon='103.0188326' />\n  <node id='605390415' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='5' changeset='84358766' lat='2.3966092' lon='103.0188659' />\n  <node id='3472441921' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84441283' lat='2.3862085' lon='103.0198705' />\n  <node id='3472441936' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84441747' lat='2.3897293' lon='103.0196046' />\n  <node id='3472443186' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84441747' lat='2.3897177' lon='103.0195145' />\n  <node id='3472443294' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84358766' lat='2.3908296' lon='103.0194233' />\n  <node id='3472455212' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84441283' lat='2.3854152' lon='103.0198542' />\n  <node id='3472455228' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84441283' lat='2.3870648' lon='103.0197205' />\n  <node id='3472455237' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84358766' lat='2.3911724' lon='103.0151219' />\n  <node id='3472455293' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3993847' lon='103.0180627' />\n  <node id='3472455303' timestamp='2019-12-17T09:09:18Z' uid='8072061' user='Diana IRM-ED' visible='true' version='2' changeset='78516766' lat='2.4003091' lon='103.0178061' />\n  <node id='3472455304' timestamp='2019-12-17T09:09:18Z' uid='8072061' user='Diana IRM-ED' visible='true' version='2' changeset='78516766' lat='2.40033' lon='103.0178693' />\n  <node id='3472455332' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84296405' lat='2.4022666' lon='103.017241' />\n  <node id='3472457349' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84296405' lat='2.403195' lon='103.0170624' />\n  <node id='3472457362' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84296405' lat='2.404316' lon='103.0167304' />\n  <node id='5442706489' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3811726' lon='103.0231966' />\n  <node id='5442706490' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3823096' lon='103.0239704' />\n  <node id='5442706491' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3841136' lon='103.0215275' />\n  <node id='5442706492' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3847276' lon='103.0217324' />\n  <node id='5442706493' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3847124' lon='103.0220889' />\n  <node id='5442706494' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.385152' lon='103.0220965' />\n  <node id='5442706495' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3851899' lon='103.0219069' />\n  <node id='5442706496' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3855765' lon='103.0220207' />\n  <node id='5442706497' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3860995' lon='103.0221951' />\n  <node id='5442706498' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3860995' lon='103.0224303' />\n  <node id='5442706499' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3871531' lon='103.02284' />\n  <node id='5442706500' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3874411' lon='103.0234241' />\n  <node id='5442706501' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.387335' lon='103.0238035' />\n  <node id='5442706502' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3875018' lon='103.0239021' />\n  <node id='5442706503' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3877443' lon='103.0235455' />\n  <node id='5442706504' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3900183' lon='103.0244028' />\n  <node id='5442706505' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3903063' lon='103.0248276' />\n  <node id='5442706506' timestamp='2018-02-28T11:56:38Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3905413' lon='103.0248352' />\n  <node id='5442706507' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3909051' lon='103.0240766' />\n  <node id='5442706508' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3912841' lon='103.0238186' />\n  <node id='5442706509' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3917162' lon='103.0246456' />\n  <node id='5442706510' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3909658' lon='103.0251766' />\n  <node id='5442706511' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3908123' lon='103.0256605' />\n  <node id='5442706512' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.391194' lon='103.0262522' />\n  <node id='5442706513' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.392055' lon='103.0263218' />\n  <node id='5442706514' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3923863' lon='103.0260881' />\n  <node id='5442706515' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3926777' lon='103.0264999' />\n  <node id='5442706516' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3941737' lon='103.0255066' />\n  <node id='5442706517' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3948767' lon='103.0247156' />\n  <node id='5442706518' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3944816' lon='103.0242393' />\n  <node id='5442706519' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3953315' lon='103.0237858' />\n  <node id='5442706520' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3955529' lon='103.0242119' />\n  <node id='5442706521' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3971999' lon='103.0244642' />\n  <node id='5442706522' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3971138' lon='103.0250455' />\n  <node id='5442706523' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3975457' lon='103.0250719' />\n  <node id='5442706524' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3977345' lon='103.0238262' />\n  <node id='5442706525' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3967813' lon='103.021745' />\n  <node id='5442706526' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3981803' lon='103.0212837' />\n  <node id='5442706527' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3983334' lon='103.0215603' />\n  <node id='5442706528' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3986449' lon='103.0214184' />\n  <node id='5442706529' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3993619' lon='103.0228704' />\n  <node id='5442706530' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.4001256' lon='103.0225618' />\n  <node id='5442706531' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.3999834' lon='103.0222223' />\n  <node id='5442706532' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4003041' lon='103.0220738' />\n  <node id='5442706533' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4006376' lon='103.0227565' />\n  <node id='5442706534' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4009711' lon='103.0226579' />\n  <node id='5442706535' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.4007602' lon='103.0222378' />\n  <node id='5442706536' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.401741' lon='103.0217586' />\n  <node id='5442706537' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84352039' lat='2.4009928' lon='103.0201018' />\n  <node id='5442706538' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.40235' lon='103.0194113' />\n  <node id='5442706539' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.4029921' lon='103.0207842' />\n  <node id='5442706540' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.4041159' lon='103.0208162' />\n  <node id='5442706541' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4034042' lon='103.0191757' />\n  <node id='5442706542' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.4042459' lon='103.0192733' />\n  <node id='5442706543' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.4052993' lon='103.0188058' />\n  <node id='5442706544' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.4054344' lon='103.0190634' />\n  <node id='5442706545' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.4048791' lon='103.0191504' />\n  <node id='5442706546' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.4059557' lon='103.021505' />\n  <node id='5442706547' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4071107' lon='103.0208448' />\n  <node id='5442706548' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4062769' lon='103.0190392' />\n  <node id='5442706549' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4072775' lon='103.0185916' />\n  <node id='5442706550' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4063906' lon='103.0167177' />\n  <node id='5442706551' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4069439' lon='103.0163839' />\n  <node id='5442706552' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4075352' lon='103.0174081' />\n  <node id='5442706553' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4079672' lon='103.0172412' />\n  <node id='5442706554' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4071259' lon='103.0156784' />\n  <node id='5442706555' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4083083' lon='103.0152611' />\n  <node id='5442706556' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4077929' lon='103.0142066' />\n  <node id='5442706557' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4073381' lon='103.0144039' />\n  <node id='5442706558' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4070501' lon='103.0136452' />\n  <node id='5442706559' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4064361' lon='103.0139639' />\n  <node id='5442706560' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4070501' lon='103.0157239' />\n  <node id='5442706561' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4051703' lon='103.0162095' />\n  <node id='5442706562' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4044957' lon='103.0141915' />\n  <node id='5442706563' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4038287' lon='103.0144115' />\n  <node id='5442706564' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4045033' lon='103.016437' />\n  <node id='5442706565' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.4030405' lon='103.0168507' />\n  <node id='5442706566' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.4024364' lon='103.015047' />\n  <node id='5442706567' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.4021139' lon='103.0139804' />\n  <node id='5442706568' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.4023828' lon='103.0139135' />\n  <node id='5442706569' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4020474' lon='103.0123404' />\n  <node id='5442706570' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.400865' lon='103.0125756' />\n  <node id='5442706571' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.4008574' lon='103.0129625' />\n  <node id='5442706572' timestamp='2020-04-29T23:59:53Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84368544' lat='2.3988304' lon='103.0134541' />\n  <node id='5442706573' timestamp='2020-04-29T23:59:53Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84368544' lat='2.3993695' lon='103.015829' />\n  <node id='5442706574' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3997735' lon='103.0163612' />\n  <node id='5442706575' timestamp='2018-02-28T11:56:39Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3989321' lon='103.0167633' />\n  <node id='5442706576' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3984698' lon='103.0145177' />\n  <node id='5442706577' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3982727' lon='103.0145632' />\n  <node id='5442706578' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3980832' lon='103.013888' />\n  <node id='5442706579' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3982954' lon='103.0138046' />\n  <node id='5442706580' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3981363' lon='103.013099' />\n  <node id='5442706581' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3975905' lon='103.0128031' />\n  <node id='5442706582' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.396643' lon='103.0128411' />\n  <node id='5442706583' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3964839' lon='103.0123859' />\n  <node id='5442706584' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3955819' lon='103.0126211' />\n  <node id='5442706585' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3959154' lon='103.0134707' />\n  <node id='5442706586' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3965293' lon='103.0133342' />\n  <node id='5442706587' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.396643' lon='103.013797' />\n  <node id='5442706588' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.3960965' lon='103.0138735' />\n  <node id='5442706589' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.396771' lon='103.0156397' />\n  <node id='5442706590' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3953166' lon='103.0160122' />\n  <node id='5442706591' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3952559' lon='103.0174385' />\n  <node id='5442706592' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3942933' lon='103.0174385' />\n  <node id='5442706593' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3943085' lon='103.0162626' />\n  <node id='5442706594' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3948391' lon='103.0161715' />\n  <node id='5442706595' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3946799' lon='103.0153522' />\n  <node id='5442706596' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3943918' lon='103.0154432' />\n  <node id='5442706597' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3943085' lon='103.0148742' />\n  <node id='5442706598' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.3928951' lon='103.0151143' />\n  <node id='5442706599' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3931033' lon='103.0158832' />\n  <node id='5442706600' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3923226' lon='103.0159288' />\n  <node id='5442706601' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3919511' lon='103.0151929' />\n  <node id='5442706602' timestamp='2020-04-29T23:54:42Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84368419' lat='2.3916191' lon='103.0152832' />\n  <node id='5442706603' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3918829' lon='103.0162019' />\n  <node id='5442706604' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84358766' lat='2.3915378' lon='103.016349' />\n  <node id='5442706605' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84358766' lat='2.3916305' lon='103.0165892' />\n  <node id='5442706606' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3909127' lon='103.0167708' />\n  <node id='5442706607' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84358766' lat='2.3907049' lon='103.0161636' />\n  <node id='5442706608' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84358766' lat='2.391454' lon='103.0159239' />\n  <node id='5442706609' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3896545' lon='103.0149122' />\n  <node id='5442706610' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3896924' lon='103.0152232' />\n  <node id='5442706611' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3888055' lon='103.0154432' />\n  <node id='5442706612' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.388904' lon='103.0158225' />\n  <node id='5442706613' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3887525' lon='103.0162701' />\n  <node id='5442706614' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3892755' lon='103.0166571' />\n  <node id='5442706615' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.390041' lon='103.0162246' />\n  <node id='5442706616' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3901092' lon='103.0169302' />\n  <node id='5442706617' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.389086' lon='103.0173398' />\n  <node id='5442706618' timestamp='2018-11-05T17:37:06Z' uid='3191182' user='angys' visible='true' version='2' changeset='64205586' lat='2.3894245' lon='103.0175611' />\n  <node id='5442706620' timestamp='2018-11-05T17:37:06Z' uid='3191182' user='angys' visible='true' version='2' changeset='64205586' lat='2.3893989' lon='103.0193542' />\n  <node id='5442706721' timestamp='2018-11-05T17:37:06Z' uid='3191182' user='angys' visible='true' version='2' changeset='64205586' lat='2.3881439' lon='103.0194544' />\n  <node id='5442706722' timestamp='2018-11-05T17:37:06Z' uid='3191182' user='angys' visible='true' version='2' changeset='64205586' lat='2.3879521' lon='103.0191256' />\n  <node id='5442706723' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3871379' lon='103.0192289' />\n  <node id='5442706724' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441283' lat='2.3869152' lon='103.0177912' />\n  <node id='5442706725' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.386145' lon='103.0168391' />\n  <node id='5442706726' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3855765' lon='103.0170364' />\n  <node id='5442706727' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3845911' lon='103.0186599' />\n  <node id='5442706728' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3853036' lon='103.0192289' />\n  <node id='5442706729' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3852051' lon='103.0194944' />\n  <node id='5442706730' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3843637' lon='103.0189026' />\n  <node id='5442706731' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3839696' lon='103.019464' />\n  <node id='5442706732' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3837194' lon='103.0193654' />\n  <node id='5442706733' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3826052' lon='103.0209434' />\n  <node id='5442706734' timestamp='2018-02-28T11:56:40Z' uid='257555' user='rene78' visible='true' version='1' changeset='56753879' lat='2.3819003' lon='103.0220889' />\n  <node id='6039844352' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3894232' lon='103.0176537' />\n  <node id='6039844353' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3891534' lon='103.0176617' />\n  <node id='6039844354' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.388998' lon='103.0175034' />\n  <node id='6039844355' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3885666' lon='103.0175168' />\n  <node id='6039844356' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3884808' lon='103.0177341' />\n  <node id='6039844357' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.387843' lon='103.017777' />\n  <node id='6039844358' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3880199' lon='103.0191154' />\n  <node id='6039844359' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3881565' lon='103.0194105' />\n  <node id='6039844360' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3895995' lon='103.0188742' />\n  <node id='6039844361' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3895313' lon='103.0183349' />\n  <node id='6039844393' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3894115' lon='103.0188661' />\n  <node id='6039844408' timestamp='2018-11-05T17:37:01Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3890031' lon='103.0187426' />\n  <node id='6039844409' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3887123' lon='103.018697' />\n  <node id='6039844410' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3887097' lon='103.0186017' />\n  <node id='6039844411' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3879767' lon='103.0185816' />\n  <node id='6039844412' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3880343' lon='103.01911' />\n  <node id='6039844413' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.388167' lon='103.0193957' />\n  <node id='6039844414' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3885703' lon='103.0193662' />\n  <node id='6039844415' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3885623' lon='103.0191543' />\n  <node id='6039844416' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.388853' lon='103.0191462' />\n  <node id='6039844417' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.3888517' lon='103.0193232' />\n  <node id='6039844418' timestamp='2018-11-05T17:37:02Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586' lat='2.388916' lon='103.0193273' />\n  <node id='6039844440' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84441747' lat='2.3873939' lon='103.0197712' />\n  <node id='6039844442' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441747' lat='2.3893565' lon='103.0196362' />\n  <node id='6039844445' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84441747' lat='2.388748' lon='103.0196682' />\n  <node id='6039844446' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84441747' lat='2.3893581' lon='103.0195382' />\n  <node id='6412229378' timestamp='2020-04-30T21:16:32Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84439291' lat='2.3839351' lon='103.0191483' />\n  <node id='6412230191' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441283' lat='2.3857361' lon='103.0199062' />\n  <node id='6504362358' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3955196' lon='103.0190278' />\n  <node id='6965903306' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441283' lat='2.3854426' lon='103.0199401' />\n  <node id='6965903307' timestamp='2019-11-11T09:11:15Z' uid='7445592' user='Zainab IRM-ED' visible='true' version='1' changeset='76896373' lat='2.3852054' lon='103.0199522' />\n  <node id='6965903308' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441283' lat='2.3853193' lon='103.0198556' />\n  <node id='6965903309' timestamp='2019-11-11T09:11:15Z' uid='7445592' user='Zainab IRM-ED' visible='true' version='1' changeset='76896373' lat='2.3852228' lon='103.0198503' />\n  <node id='6965903310' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441747' lat='2.3850513' lon='103.0198234' />\n  <node id='6965903311' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441747' lat='2.3849173' lon='103.0197872' />\n  <node id='6965903312' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441747' lat='2.3847806' lon='103.0197282' />\n  <node id='6965903315' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84441747' lat='2.3842956' lon='103.0194117' />\n  <node id='7021028366' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84358766' lat='2.3914062' lon='103.0194623' />\n  <node id='7021028367' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='3' changeset='84350728' lat='2.3947094' lon='103.0191943' />\n  <node id='7059428960' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84296405' lat='2.4037886' lon='103.0168852' />\n  <node id='7062770961' timestamp='2019-12-17T09:09:18Z' uid='8072061' user='Diana IRM-ED' visible='true' version='1' changeset='78516766' lat='2.4042914' lon='103.0166477' />\n  <node id='7062770962' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.4031479' lon='103.0169883' />\n  <node id='7062770963' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.4014022' lon='103.0174984' />\n  <node id='7062770964' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.401437' lon='103.0175717' />\n  <node id='7062770965' timestamp='2020-04-29T00:00:52Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84296405' lat='2.4022631' lon='103.0173352' />\n  <node id='7062770966' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.3935704' lon='103.0191757' />\n  <node id='7062770967' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84358766' lat='2.3919122' lon='103.019322' />\n  <node id='7062949740' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84350728' lat='2.396307' lon='103.0190261' />\n  <node id='7464061629' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84350728' lat='2.3943249' lon='103.019119' />\n  <node id='7464061630' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84350728' lat='2.3952055' lon='103.0191467' />\n  <node id='7464061631' timestamp='2020-04-29T18:58:04Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84358766' lat='2.3962994' lon='103.0189343' />\n  <node id='7464061632' timestamp='2020-04-29T16:04:20Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84350728' lat='2.3972007' lon='103.0188018' />\n  <node id='7464078382' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84352039' lat='2.3958767' lon='103.024748' />\n  <node id='7464078383' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84352039' lat='2.3962653' lon='103.0241954' />\n  <node id='7464078384' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84352039' lat='2.3970263' lon='103.0241954' />\n  <node id='7464152485' timestamp='2020-04-29T16:30:09Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84352039' lat='2.3969888' lon='103.0244395' />\n  <node id='7468539184' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84441283' lat='2.3870947' lon='103.0186822' />\n  <node id='7468553588' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84441283' lat='2.3870644' lon='103.0197994' />\n  <node id='7468553589' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84441283' lat='2.3858446' lon='103.0198315' />\n  <node id='7468553590' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84441283' lat='2.3862238' lon='103.0197953' />\n  <node id='7468570211' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84441747' lat='2.3882197' lon='103.0196244' />\n  <node id='7468570212' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84441747' lat='2.3880712' lon='103.0197123' />\n  <node id='7468570213' timestamp='2020-04-30T22:28:52Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84441747' lat='2.385653' lon='103.0198449' />\n  <node id='7468660485' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3876378' lon='103.01975' />\n  <node id='7468660486' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3876343' lon='103.0196731' />\n  <node id='7468660487' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3876322' lon='103.0195788' />\n  <node id='7468660488' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.387644' lon='103.0195231' />\n  <node id='7468660489' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3876609' lon='103.0194943' />\n  <node id='7468660490' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3876947' lon='103.0194825' />\n  <node id='7468660491' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.387953' lon='103.0194673' />\n  <node id='7468660492' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3879986' lon='103.0194808' />\n  <node id='7468660493' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3880256' lon='103.0195095' />\n  <node id='7468660494' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3880408' lon='103.0195552' />\n  <node id='7468660495' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3880596' lon='103.0196378' />\n  <node id='7468660510' timestamp='2020-04-30T23:38:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84443654' lat='2.3889745' lon='103.0193734'>\n    <tag k='barrier' v='entrance' />\n  </node>\n  <node id='7468660512' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.3889966' lon='103.0195655' />\n  <node id='7468660513' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532' lat='2.389005' lon='103.0196547' />\n  <node id='7468666216' timestamp='2020-04-30T23:38:20Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443654' lat='2.3893436' lon='103.0193567'>\n    <tag k='barrier' v='entrance' />\n  </node>\n  <node id='7499530867' timestamp='2020-05-08T22:58:24Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84919698' lat='2.4050701' lon='103.0164988' />\n  <node id='7499530868' timestamp='2020-05-08T22:58:24Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84919698' lat='2.4050572' lon='103.0164501' />\n  <node id='7499530878' timestamp='2020-05-08T22:58:24Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84919698' lat='2.404645' lon='103.0165491' />\n  <node id='7499530879' timestamp='2020-05-08T22:58:24Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84919698' lat='2.4046709' lon='103.016625' />\n  <way id='27518137' timestamp='2020-05-08T22:58:24Z' uid='10147140' user='HenryFjord' visible='true' version='21' changeset='84919698'>\n    <nd ref='302094620' />\n    <nd ref='7499530867' />\n    <nd ref='605390394' />\n    <nd ref='7499530879' />\n    <nd ref='3472457362' />\n    <nd ref='7059428960' />\n    <nd ref='3472457349' />\n    <nd ref='7062770965' />\n    <nd ref='7062770964' />\n    <nd ref='605390392' />\n    <nd ref='3472455304' />\n    <nd ref='457007254' />\n    <nd ref='605390385' />\n    <nd ref='7464061632' />\n    <nd ref='302093955' />\n    <nd ref='457007259' />\n    <nd ref='7062949740' />\n    <nd ref='302095462' />\n    <nd ref='302095128' />\n    <nd ref='7464061630' />\n    <nd ref='7021028367' />\n    <nd ref='302094165' />\n    <nd ref='457007251' />\n    <nd ref='7021028366' />\n    <nd ref='3472441936' />\n    <nd ref='6039844442' />\n    <nd ref='7468660513' />\n    <nd ref='6039844445' />\n    <nd ref='7468570212' />\n    <nd ref='7468660485' />\n    <nd ref='6039844440' />\n    <nd ref='7468553588' />\n    <nd ref='3472441921' />\n    <nd ref='6412230191' />\n    <nd ref='6965903306' />\n    <nd ref='6965903307' />\n    <nd ref='302095471' />\n    <tag k='highway' v='trunk' />\n    <tag k='name' v='Jalan Segamat-Labis' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='1' />\n  </way>\n  <way id='565028016' timestamp='2020-04-30T22:14:46Z' uid='10147140' user='HenryFjord' visible='true' version='4' changeset='84441283'>\n    <nd ref='5442706489' />\n    <nd ref='5442706490' />\n    <nd ref='5442706491' />\n    <nd ref='5442706492' />\n    <nd ref='5442706493' />\n    <nd ref='5442706494' />\n    <nd ref='5442706495' />\n    <nd ref='5442706496' />\n    <nd ref='5442706497' />\n    <nd ref='5442706498' />\n    <nd ref='5442706499' />\n    <nd ref='5442706500' />\n    <nd ref='5442706501' />\n    <nd ref='5442706502' />\n    <nd ref='5442706503' />\n    <nd ref='5442706504' />\n    <nd ref='5442706505' />\n    <nd ref='5442706506' />\n    <nd ref='5442706507' />\n    <nd ref='5442706508' />\n    <nd ref='5442706509' />\n    <nd ref='5442706510' />\n    <nd ref='5442706511' />\n    <nd ref='5442706512' />\n    <nd ref='5442706513' />\n    <nd ref='5442706514' />\n    <nd ref='5442706515' />\n    <nd ref='5442706516' />\n    <nd ref='5442706517' />\n    <nd ref='5442706518' />\n    <nd ref='5442706519' />\n    <nd ref='5442706520' />\n    <nd ref='7464078382' />\n    <nd ref='7464078383' />\n    <nd ref='7464078384' />\n    <nd ref='7464152485' />\n    <nd ref='5442706521' />\n    <nd ref='5442706522' />\n    <nd ref='5442706523' />\n    <nd ref='5442706524' />\n    <nd ref='5442706525' />\n    <nd ref='5442706526' />\n    <nd ref='5442706527' />\n    <nd ref='5442706528' />\n    <nd ref='5442706529' />\n    <nd ref='5442706530' />\n    <nd ref='5442706531' />\n    <nd ref='5442706532' />\n    <nd ref='5442706533' />\n    <nd ref='5442706534' />\n    <nd ref='5442706535' />\n    <nd ref='5442706536' />\n    <nd ref='5442706537' />\n    <nd ref='5442706538' />\n    <nd ref='5442706539' />\n    <nd ref='5442706540' />\n    <nd ref='5442706541' />\n    <nd ref='5442706542' />\n    <nd ref='5442706543' />\n    <nd ref='5442706544' />\n    <nd ref='5442706545' />\n    <nd ref='5442706546' />\n    <nd ref='5442706547' />\n    <nd ref='5442706548' />\n    <nd ref='5442706549' />\n    <nd ref='5442706550' />\n    <nd ref='5442706551' />\n    <nd ref='5442706552' />\n    <nd ref='5442706553' />\n    <nd ref='5442706554' />\n    <nd ref='5442706555' />\n    <nd ref='5442706556' />\n    <nd ref='5442706557' />\n    <nd ref='5442706558' />\n    <nd ref='5442706559' />\n    <nd ref='5442706560' />\n    <nd ref='5442706561' />\n    <nd ref='5442706562' />\n    <nd ref='5442706563' />\n    <nd ref='5442706564' />\n    <nd ref='5442706565' />\n    <nd ref='5442706566' />\n    <nd ref='5442706567' />\n    <nd ref='5442706568' />\n    <nd ref='5442706569' />\n    <nd ref='5442706570' />\n    <nd ref='5442706571' />\n    <nd ref='5442706572' />\n    <nd ref='5442706573' />\n    <nd ref='5442706574' />\n    <nd ref='5442706575' />\n    <nd ref='5442706576' />\n    <nd ref='5442706577' />\n    <nd ref='5442706578' />\n    <nd ref='5442706579' />\n    <nd ref='5442706580' />\n    <nd ref='5442706581' />\n    <nd ref='5442706582' />\n    <nd ref='5442706583' />\n    <nd ref='5442706584' />\n    <nd ref='5442706585' />\n    <nd ref='5442706586' />\n    <nd ref='5442706587' />\n    <nd ref='5442706588' />\n    <nd ref='5442706589' />\n    <nd ref='5442706590' />\n    <nd ref='5442706591' />\n    <nd ref='5442706592' />\n    <nd ref='5442706593' />\n    <nd ref='5442706594' />\n    <nd ref='5442706595' />\n    <nd ref='5442706596' />\n    <nd ref='5442706597' />\n    <nd ref='5442706598' />\n    <nd ref='5442706599' />\n    <nd ref='5442706600' />\n    <nd ref='5442706601' />\n    <nd ref='5442706602' />\n    <nd ref='5442706603' />\n    <nd ref='5442706604' />\n    <nd ref='5442706605' />\n    <nd ref='5442706606' />\n    <nd ref='5442706607' />\n    <nd ref='5442706608' />\n    <nd ref='3472455237' />\n    <nd ref='5442706609' />\n    <nd ref='5442706610' />\n    <nd ref='5442706611' />\n    <nd ref='5442706612' />\n    <nd ref='5442706613' />\n    <nd ref='5442706614' />\n    <nd ref='5442706615' />\n    <nd ref='5442706616' />\n    <nd ref='5442706617' />\n    <nd ref='5442706618' />\n    <nd ref='6039844352' />\n    <nd ref='6039844361' />\n    <nd ref='6039844360' />\n    <nd ref='6039844393' />\n    <nd ref='5442706620' />\n    <nd ref='5442706721' />\n    <nd ref='5442706722' />\n    <nd ref='5442706723' />\n    <nd ref='7468539184' />\n    <nd ref='5442706724' />\n    <nd ref='5442706725' />\n    <nd ref='5442706726' />\n    <nd ref='5442706727' />\n    <nd ref='5442706728' />\n    <nd ref='5442706729' />\n    <nd ref='5442706730' />\n    <nd ref='5442706731' />\n    <nd ref='5442706732' />\n    <nd ref='5442706733' />\n    <nd ref='5442706734' />\n    <nd ref='5442706489' />\n    <tag k='landuse' v='residential' />\n  </way>\n  <way id='641309223' timestamp='2020-04-30T23:38:20Z' uid='10147140' user='HenryFjord' visible='true' version='2' changeset='84443654'>\n    <nd ref='6039844352' />\n    <nd ref='6039844353' />\n    <nd ref='6039844354' />\n    <nd ref='6039844355' />\n    <nd ref='6039844356' />\n    <nd ref='6039844357' />\n    <nd ref='6039844358' />\n    <nd ref='6039844359' />\n    <nd ref='7468660510' />\n    <nd ref='7468666216' />\n    <nd ref='5442706620' />\n    <nd ref='6039844393' />\n    <nd ref='6039844360' />\n    <nd ref='6039844361' />\n    <nd ref='6039844352' />\n    <tag k='amenity' v='school' />\n    <tag k='barrier' v='wall' />\n    <tag k='full_name' v='Sekolah Jenis Kebangsaan (C) Labis' />\n    <tag k='name' v='SJK (C) Labis 拉美士华文小学' />\n    <tag k='name:en' v='SJK (C) Labis' />\n    <tag k='name:id' v='SJK (C) Labis' />\n    <tag k='name:ms' v='SJK (C) Labis' />\n    <tag k='name:zh' v='拉美士华小' />\n    <tag k='wikidata' v='Q20447178' />\n    <tag k='wikipedia' v='ms:Sekolah Jenis Kebangsaan (C) Labis' />\n  </way>\n  <way id='641309235' timestamp='2018-11-05T17:37:04Z' uid='3191182' user='angys' visible='true' version='1' changeset='64205586'>\n    <nd ref='6039844408' />\n    <nd ref='6039844409' />\n    <nd ref='6039844410' />\n    <nd ref='6039844411' />\n    <nd ref='6039844412' />\n    <nd ref='6039844413' />\n    <nd ref='6039844414' />\n    <nd ref='6039844415' />\n    <nd ref='6039844416' />\n    <nd ref='6039844417' />\n    <nd ref='6039844418' />\n    <nd ref='6039844408' />\n    <tag k='leisure' v='pitch' />\n  </way>\n  <way id='684418401' timestamp='2020-05-08T22:58:24Z' uid='10147140' user='HenryFjord' visible='true' version='10' changeset='84919698'>\n    <nd ref='6412229378' />\n    <nd ref='6965903315' />\n    <nd ref='605390355' />\n    <nd ref='605390357' />\n    <nd ref='6965903312' />\n    <nd ref='6965903311' />\n    <nd ref='6965903310' />\n    <nd ref='605390363' />\n    <nd ref='6965903309' />\n    <nd ref='6965903308' />\n    <nd ref='3472455212' />\n    <nd ref='7468570213' />\n    <nd ref='7468553589' />\n    <nd ref='7468553590' />\n    <nd ref='3472455228' />\n    <nd ref='7468660486' />\n    <nd ref='7468660495' />\n    <nd ref='7468570211' />\n    <nd ref='7468660512' />\n    <nd ref='6039844446' />\n    <nd ref='3472443186' />\n    <nd ref='3472443294' />\n    <nd ref='7062770967' />\n    <nd ref='605390364' />\n    <nd ref='7062770966' />\n    <nd ref='7464061629' />\n    <nd ref='605390404' />\n    <nd ref='6504362358' />\n    <nd ref='605390399' />\n    <nd ref='605390400' />\n    <nd ref='7464061631' />\n    <nd ref='605390415' />\n    <nd ref='605390410' />\n    <nd ref='605390375' />\n    <nd ref='605390387' />\n    <nd ref='605390383' />\n    <nd ref='3472455293' />\n    <nd ref='3472455303' />\n    <nd ref='605390395' />\n    <nd ref='7062770963' />\n    <nd ref='3472455332' />\n    <nd ref='7062770962' />\n    <nd ref='7062770961' />\n    <nd ref='7499530878' />\n    <nd ref='605390397' />\n    <nd ref='7499530868' />\n    <nd ref='302094620' />\n    <tag k='highway' v='trunk' />\n    <tag k='name' v='Jalan Segamat-Labis' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='1' />\n  </way>\n  <way id='798542598' timestamp='2020-04-30T23:33:08Z' uid='10147140' user='HenryFjord' visible='true' version='1' changeset='84443532'>\n    <nd ref='7468570212' />\n    <nd ref='7468660495' />\n    <nd ref='7468660494' />\n    <nd ref='7468660493' />\n    <nd ref='7468660492' />\n    <nd ref='7468660491' />\n    <nd ref='7468660490' />\n    <nd ref='7468660489' />\n    <nd ref='7468660488' />\n    <nd ref='7468660487' />\n    <nd ref='7468660486' />\n    <nd ref='7468660485' />\n    <tag k='highway' v='service' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/bignode/ukr-link-road-test.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <bounds minlat='49.9695956' minlon='36.3077212' maxlat='49.9701331' maxlon='36.3107808' origin='CGImap 0.8.3 (1179742 spike-08.openstreetmap.org)' />\n  <node id='256361314' timestamp='2015-03-11T19:16:12Z' uid='420193' user='headlong22' visible='true' version='3' changeset='29412742' lat='49.9639241' lon='36.3330899' />\n  <node id='256361315' timestamp='2019-10-06T18:54:42Z' uid='421494' user='Anton Shevchuk' visible='true' version='4' changeset='75348337' lat='49.9652105' lon='36.326213' />\n  <node id='256361316' timestamp='2015-03-05T19:59:29Z' uid='420193' user='headlong22' visible='true' version='4' changeset='29275114' lat='49.9655942' lon='36.3245482' />\n  <node id='256361317' timestamp='2015-03-05T19:59:29Z' uid='420193' user='headlong22' visible='true' version='3' changeset='29275114' lat='49.9661328' lon='36.322417' />\n  <node id='256361318' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='9' changeset='82484399' lat='49.9672014' lon='36.3183316' />\n  <node id='256361319' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9684598' lon='36.3142301' />\n  <node id='256592834' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='7' changeset='82481698' lat='49.9698528' lon='36.3069231' />\n  <node id='256592835' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82481698' lat='49.9700969' lon='36.3072268' />\n  <node id='256592836' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82481698' lat='49.9701489' lon='36.3074412'>\n    <tag k='highway' v='give_way' />\n  </node>\n  <node id='256592837' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9701331' lon='36.3077212' />\n  <node id='256936806' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9705045' lon='36.3065454' />\n  <node id='257750807' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698' lat='49.968904' lon='36.3089158' />\n  <node id='267142033' timestamp='2020-09-27T20:53:07Z' uid='1782528' user='deathDEN' visible='true' version='7' changeset='91597445' lat='49.9664849' lon='36.3211588'>\n    <tag k='bicycle' v='yes' />\n    <tag k='highway' v='traffic_signals' />\n    <tag k='traffic_signals:direction' v='both' />\n  </node>\n  <node id='320221610' timestamp='2020-09-27T20:53:07Z' uid='1782528' user='deathDEN' visible='true' version='6' changeset='91597445' lat='49.9636425' lon='36.3345483'>\n    <tag k='highway' v='traffic_signals' />\n    <tag k='traffic_signals' v='signal' />\n    <tag k='traffic_signals:direction' v='both' />\n  </node>\n  <node id='388306202' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82484399' lat='49.9675793' lon='36.3168992' />\n  <node id='394141233' timestamp='2020-09-27T20:53:07Z' uid='1782528' user='deathDEN' visible='true' version='6' changeset='91597445' lat='49.965335' lon='36.3255449'>\n    <tag k='highway' v='traffic_signals' />\n    <tag k='traffic_signals' v='signal' />\n    <tag k='traffic_signals:direction' v='both' />\n  </node>\n  <node id='394867165' timestamp='2015-05-14T18:27:59Z' uid='420193' user='headlong22' visible='true' version='3' changeset='31150186' lat='49.9717031' lon='36.310207' />\n  <node id='395286068' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698' lat='49.969094' lon='36.3089815'>\n    <tag k='bus' v='yes' />\n    <tag k='highway' v='bus_stop' />\n    <tag k='name' v='Видавництво &quot;Харків&quot;' />\n    <tag k='public_transport' v='platform' />\n    <tag k='share_taxi' v='yes' />\n    <tag k='trolleybus' v='yes' />\n  </node>\n  <node id='907931888' timestamp='2020-09-27T20:53:07Z' uid='1782528' user='deathDEN' visible='true' version='6' changeset='91597445' lat='49.9649972' lon='36.3273198'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='traffic_signals' />\n    <tag k='traffic_signals:direction' v='both' />\n  </node>\n  <node id='1001247631' timestamp='2015-03-05T20:14:58Z' uid='420193' user='headlong22' visible='true' version='3' changeset='29275449' lat='49.9663672' lon='36.3215855' />\n  <node id='1215941677' timestamp='2018-02-23T16:45:46Z' uid='7651759' user='Toolen' visible='true' version='2' changeset='56614784' lat='49.9699165' lon='36.3109606' />\n  <node id='1215941711' timestamp='2018-02-23T16:45:46Z' uid='7651759' user='Toolen' visible='true' version='2' changeset='56614784' lat='49.9700214' lon='36.3110442' />\n  <node id='1215941743' timestamp='2018-02-23T16:45:46Z' uid='7651759' user='Toolen' visible='true' version='2' changeset='56614784' lat='49.9702016' lon='36.310497' />\n  <node id='1215941802' timestamp='2018-02-23T16:45:46Z' uid='7651759' user='Toolen' visible='true' version='2' changeset='56614784' lat='49.9700966' lon='36.3104135' />\n  <node id='1822129267' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9700041' lon='36.3070681' />\n  <node id='1875661311' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9692651' lon='36.3125572' />\n  <node id='1875661561' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9694781' lon='36.311722' />\n  <node id='1875661639' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9695211' lon='36.3117631' />\n  <node id='1875661705' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9695601' lon='36.3128162' />\n  <node id='1875661907' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9697862' lon='36.3107542' />\n  <node id='1875661978' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9698352' lon='36.3107961' />\n  <node id='1875662052' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9699281' lon='36.3103391' />\n  <node id='1875662079' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9699661' lon='36.3103771' />\n  <node id='1875662266' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9701597' lon='36.3097222' />\n  <node id='1875662399' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.970351' lon='36.310368' />\n  <node id='1875663044' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.971175' lon='36.3106702' />\n  <node id='1875663091' timestamp='2019-10-08T16:06:24Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422223' lat='49.9712442' lon='36.3101802' />\n  <node id='1994136765' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82485517' lat='49.9695492' lon='36.3085891' />\n  <node id='1994136766' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9700852' lon='36.307882' />\n  <node id='1994136767' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9697041' lon='36.3085727' />\n  <node id='1994136769' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9698325' lon='36.30852' />\n  <node id='1994136773' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9699313' lon='36.3083914' />\n  <node id='2278582488' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82485517' lat='49.9703609' lon='36.3085443' />\n  <node id='2278582517' timestamp='2020-03-22T15:13:50Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82485957' lat='49.9676723' lon='36.3170871' />\n  <node id='2278582592' timestamp='2013-04-23T17:21:20Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.9718997' lon='36.3102725' />\n  <node id='2278582598' timestamp='2013-04-23T17:21:20Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.9716116' lon='36.3099416' />\n  <node id='2278582609' timestamp='2013-04-23T17:21:20Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.9712165' lon='36.3099308' />\n  <node id='2278582611' timestamp='2013-04-23T17:21:20Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.9713977' lon='36.310022' />\n  <node id='2278582615' timestamp='2013-04-23T17:21:20Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.971496' lon='36.3098638' />\n  <node id='2278582700' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82484399' lat='49.9690129' lon='36.3129296' />\n  <node id='2278582703' timestamp='2015-03-12T19:34:26Z' uid='420193' user='headlong22' visible='true' version='2' changeset='29433471' lat='49.9695692' lon='36.3112914' />\n  <node id='2278582776' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82485517' lat='49.9703992' lon='36.3088372' />\n  <node id='2278582817' timestamp='2015-03-12T19:13:30Z' uid='420193' user='headlong22' visible='true' version='2' changeset='29432966' lat='49.9716592' lon='36.3100955' />\n  <node id='2278582831' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9701049' lon='36.3096318' />\n  <node id='2278582836' timestamp='2013-04-23T17:21:24Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.9701418' lon='36.3096626' />\n  <node id='2278582837' timestamp='2013-04-23T17:21:24Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.9702435' lon='36.3096653' />\n  <node id='2278582841' timestamp='2013-04-23T17:21:24Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.9705316' lon='36.309727' />\n  <node id='2278582853' timestamp='2013-04-23T17:21:24Z' uid='540071' user='bakasana' visible='true' version='1' changeset='15839422' lat='49.9715771' lon='36.3098933' />\n  <node id='2462601889' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698' lat='49.9699742' lon='36.309622'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='2462601938' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698' lat='49.9700787' lon='36.3097078' />\n  <node id='3297128912' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9701289' lon='36.3073299'>\n    <tag k='crossing' v='marked' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3384933761' timestamp='2015-03-05T19:59:26Z' uid='420193' user='headlong22' visible='true' version='1' changeset='29275114' lat='49.9659861' lon='36.3230058' />\n  <node id='3391488463' timestamp='2020-03-22T15:13:50Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82485957' lat='49.9673219' lon='36.3183467' />\n  <node id='3394184260' timestamp='2019-10-06T15:20:06Z' uid='421494' user='Anton Shevchuk' visible='true' version='5' changeset='75343488' lat='49.9636852' lon='36.3343105'>\n    <tag k='crossing' v='marked' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='3395794789' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82484399' lat='49.9716321' lon='36.3099851' />\n  <node id='3395844877' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9691693' lon='36.3120767'>\n    <tag k='bus' v='yes' />\n    <tag k='name' v='Видавництво &quot;Харків&quot;' />\n    <tag k='public_transport' v='stop_position' />\n    <tag k='share_taxi' v='yes' />\n    <tag k='trolleybus' v='yes' />\n  </node>\n  <node id='3395844882' timestamp='2020-01-17T12:20:12Z' uid='4945863' user='Bodya69' visible='true' version='3' changeset='79696484' lat='49.9692765' lon='36.3121552'>\n    <tag k='bus' v='yes' />\n    <tag k='highway' v='bus_stop' />\n    <tag k='name' v='Видавництво &quot;Харків&quot;' />\n    <tag k='public_transport' v='platform' />\n    <tag k='share_taxi' v='yes' />\n    <tag k='trolleybus' v='yes' />\n  </node>\n  <node id='3395844884' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82481698' lat='49.9695079' lon='36.3093252' />\n  <node id='3395844886' timestamp='2015-03-12T19:34:22Z' uid='420193' user='headlong22' visible='true' version='1' changeset='29433471' lat='49.9697572' lon='36.3107082' />\n  <node id='5380737766' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9728084' lon='36.3002129' />\n  <node id='5380737767' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9726587' lon='36.3000773' />\n  <node id='5380737768' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='4' changeset='75422833' lat='49.9699966' lon='36.3083724' />\n  <node id='5380737769' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='4' changeset='75422833' lat='49.9702004' lon='36.3084843' />\n  <node id='5533296016' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9727973' lon='36.3000005' />\n  <node id='5533296017' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.972831' lon='36.3001005' />\n  <node id='5533296018' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9727275' lon='36.2999774' />\n  <node id='5533296019' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9726885' lon='36.3000204' />\n  <node id='5533296020' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9728237' lon='36.3001593' />\n  <node id='5533298821' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9728256' lon='36.3000453' />\n  <node id='5533298822' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.972765' lon='36.2999817' />\n  <node id='5533298846' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422833' lat='49.9700688' lon='36.3085891' />\n  <node id='5533298847' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422833' lat='49.9701427' lon='36.308576' />\n  <node id='5533298848' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422833' lat='49.9700133' lon='36.3085536' />\n  <node id='5533298849' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422833' lat='49.9699831' lon='36.3084243' />\n  <node id='5533298850' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422833' lat='49.9700388' lon='36.3085726' />\n  <node id='5533298851' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422833' lat='49.9701026' lon='36.308595' />\n  <node id='5533298852' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75422833' lat='49.970175' lon='36.3085358' />\n  <node id='6004666902' timestamp='2019-10-06T18:54:42Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75348337' lat='49.9650258' lon='36.3271491'>\n    <tag k='crossing' v='marked' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6004666905' timestamp='2019-10-21T13:20:41Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75998523' lat='49.96496' lon='36.3274977'>\n    <tag k='crossing' v='marked' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6113014083' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9699544' lon='36.3085772' />\n  <node id='6113014084' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9700605' lon='36.3086798' />\n  <node id='6113014885' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9700929' lon='36.3086844' />\n  <node id='6113014886' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.970198' lon='36.3086723' />\n  <node id='6113014889' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9696939' lon='36.3091057' />\n  <node id='6113014891' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9698089' lon='36.3092891' />\n  <node id='6113014893' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9699095' lon='36.3093237' />\n  <node id='6113014894' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82481698' lat='49.9701515' lon='36.3091159' />\n  <node id='6113014895' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9695956' lon='36.3107808' />\n  <node id='6113014897' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82481698' lat='49.9698494' lon='36.3095583'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6113014898' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698' lat='49.9698465' lon='36.3095088' />\n  <node id='6113014901' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82481698' lat='49.9696074' lon='36.3093897'>\n    <tag k='crossing' v='traffic_signals' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6113014902' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9701209' lon='36.3087915' />\n  <node id='6113014903' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75189911' lat='49.9699978' lon='36.3092036' />\n  <node id='6113014904' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75189911' lat='49.9699425' lon='36.3092291' />\n  <node id='6113014905' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75189911' lat='49.9698932' lon='36.3092313' />\n  <node id='6113014906' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75189911' lat='49.9698412' lon='36.3091792' />\n  <node id='6113014907' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75189911' lat='49.9698093' lon='36.3091402' />\n  <node id='6113014908' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75189911' lat='49.9697931' lon='36.3091018' />\n  <node id='6113014909' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75189911' lat='49.9697837' lon='36.3090205' />\n  <node id='6113014910' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9698964' lon='36.3086559' />\n  <node id='6113014911' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9699548' lon='36.3087237' />\n  <node id='6113014912' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9700002' lon='36.308761' />\n  <node id='6113014913' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75189911' lat='49.9700659' lon='36.3087834' />\n  <node id='6113014917' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9694321' lon='36.309943' />\n  <node id='6113034279' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9697911' lon='36.3088076'>\n    <tag k='highway' v='traffic_signals' />\n    <tag k='traffic_signals' v='signal' />\n    <tag k='traffic_signals:direction' v='forward' />\n  </node>\n  <node id='6113049174' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82533843' lat='49.969947' lon='36.3094044' />\n  <node id='6113049176' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82533843' lat='49.9698668' lon='36.3093777' />\n  <node id='6113049177' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82533843' lat='49.9698744' lon='36.3095271' />\n  <node id='6844719020' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9694564' lon='36.3090248' />\n  <node id='6844719021' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82485517' lat='49.969517' lon='36.308586' />\n  <node id='6844719022' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9696247' lon='36.3075462' />\n  <node id='6844719024' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82533843' lat='49.9698654' lon='36.309381' />\n  <node id='6844719025' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9698486' lon='36.3096526'>\n    <tag k='highway' v='traffic_signals' />\n    <tag k='traffic_signals' v='signal' />\n    <tag k='traffic_signals:direction' v='forward' />\n  </node>\n  <node id='6844719026' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9699012' lon='36.3095643' />\n  <node id='6844719027' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9697596' lon='36.3085567'>\n    <tag k='crossing' v='marked' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6844719028' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698' lat='49.9695911' lon='36.3090623'>\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6844719029' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.96994' lon='36.3097214'>\n    <tag k='highway' v='traffic_signals' />\n    <tag k='traffic_signals' v='signal' />\n    <tag k='traffic_signals:direction' v='forward' />\n  </node>\n  <node id='6844719031' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75189911' lat='49.9710846' lon='36.3056108' />\n  <node id='6844719032' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75189911' lat='49.9719072' lon='36.3030432' />\n  <node id='6844719033' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9698172' lon='36.3095555' />\n  <node id='6844719034' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82533843' lat='49.9696472' lon='36.3094556' />\n  <node id='6844822664' timestamp='2019-10-21T13:20:41Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75998523' lat='49.9653733' lon='36.3253984'>\n    <tag k='crossing' v='marked' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6844822668' timestamp='2019-10-21T13:20:41Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75998523' lat='49.9652938' lon='36.3257659'>\n    <tag k='crossing' v='marked' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6855551842' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82533843' lat='49.9698178' lon='36.3095891' />\n  <node id='6855551845' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9675411' lon='36.315941' />\n  <node id='6855551846' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9672875' lon='36.3167698' />\n  <node id='6855551847' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9664024' lon='36.3200609' />\n  <node id='6855551848' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9664024' lon='36.3201896' />\n  <node id='6855551849' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9662541' lon='36.3207395' />\n  <node id='6855551850' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9662489' lon='36.3208253' />\n  <node id='6855551851' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.966273' lon='36.3208843' />\n  <node id='6855551852' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9663748' lon='36.320946' />\n  <node id='6855551853' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9664111' lon='36.3209433' />\n  <node id='6855551854' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9664576' lon='36.3208977' />\n  <node id='6855551855' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.967517' lon='36.3168878' />\n  <node id='6855551856' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9689178' lon='36.3126178' />\n  <node id='6855551857' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9695682' lon='36.3106302' />\n  <node id='6855551858' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9695941' lon='36.3104318' />\n  <node id='6855551859' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9697356' lon='36.310008' />\n  <node id='6855551860' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9697839' lon='36.3098309' />\n  <node id='6855551861' timestamp='2019-10-06T15:42:19Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75344066' lat='49.9698184' lon='36.3096432' />\n  <node id='6855577788' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9699865' lon='36.30781'>\n    <tag k='crossing' v='marked' />\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6855876345' timestamp='2019-10-06T18:54:42Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75348337' lat='49.9641228' lon='36.3320173' />\n  <node id='6855876346' timestamp='2019-10-06T18:54:42Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75348337' lat='49.9645614' lon='36.3296493' />\n  <node id='6855876351' timestamp='2019-10-06T18:54:42Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75348337' lat='49.9643329' lon='36.3308826' />\n  <node id='6860854797' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9696567' lon='36.3092336' />\n  <node id='6860854798' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9697878' lon='36.3092403' />\n  <node id='6860854800' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9699771' lon='36.309329' />\n  <node id='6860854801' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9700268' lon='36.3093035' />\n  <node id='6860854802' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9695483' lon='36.309193'>\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='6860854804' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9694872' lon='36.3091695'>\n    <tag k='highway' v='traffic_signals' />\n    <tag k='traffic_signals' v='signal' />\n    <tag k='traffic_signals:direction' v='forward' />\n  </node>\n  <node id='6860854805' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9696127' lon='36.3089992' />\n  <node id='6860854806' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9696367' lon='36.309076' />\n  <node id='6860854808' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9699968' lon='36.3086426' />\n  <node id='6860854810' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9689475' lon='36.3086004' />\n  <node id='6860854811' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698' lat='49.9690989' lon='36.3084464' />\n  <node id='6860854812' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9699832' lon='36.3085245' />\n  <node id='6860854813' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9699766' lon='36.3084785' />\n  <node id='6860854814' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9679894' lon='36.315643' />\n  <node id='6860854815' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9697131' lon='36.3086361' />\n  <node id='6860854816' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9695906' lon='36.3090384' />\n  <node id='6860854817' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9694991' lon='36.3090492' />\n  <node id='6860854818' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9695371' lon='36.3087783' />\n  <node id='6860854819' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9695578' lon='36.3087059' />\n  <node id='6860854820' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833' lat='49.9696026' lon='36.3086495' />\n  <node id='6862476864' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9697382' lon='36.3086184' />\n  <node id='6862476865' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698' lat='49.9697776' lon='36.3084986' />\n  <node id='6862476866' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9697919' lon='36.3086194' />\n  <node id='6862476867' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9696661' lon='36.3090292' />\n  <node id='6862476868' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9696255' lon='36.3090353' />\n  <node id='6862476869' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9697543' lon='36.3086286' />\n  <node id='6862476872' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9697907' lon='36.3084009' />\n  <node id='6862476873' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9697577' lon='36.3085142' />\n  <node id='6862476874' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9696553' lon='36.308569' />\n  <node id='6862476875' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.969546' lon='36.3085779' />\n  <node id='6862476876' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9690258' lon='36.3083532' />\n  <node id='6862476877' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9689553' lon='36.3083744' />\n  <node id='6862476878' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9688642' lon='36.3084222' />\n  <node id='6862476879' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9688278' lon='36.308493' />\n  <node id='6862476880' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9688073' lon='36.3086097' />\n  <node id='6862476881' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9688505' lon='36.3086982' />\n  <node id='6862476882' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9688426' lon='36.3087531' />\n  <node id='6862476883' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9688107' lon='36.308792' />\n  <node id='6862476884' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9687583' lon='36.3087442' />\n  <node id='6862497385' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.968706' lon='36.3086257' />\n  <node id='6862497386' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9686809' lon='36.3085053' />\n  <node id='6862497387' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9686821' lon='36.3083213' />\n  <node id='6862497388' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9687504' lon='36.3081567' />\n  <node id='6862497389' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9688437' lon='36.30807' />\n  <node id='6862497390' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528' lat='49.9689792' lon='36.308047' />\n  <node id='7128002665' timestamp='2020-01-13T09:31:26Z' uid='4464045' user='Anton Melnichuk' visible='true' version='1' changeset='79507850' lat='49.9638232' lon='36.3336055'>\n    <tag k='bus' v='yes' />\n    <tag k='name' v='пр. Московський' />\n    <tag k='public_transport' v='stop_position' />\n    <tag k='trolleybus' v='yes' />\n  </node>\n  <node id='7128002666' timestamp='2020-01-13T09:31:26Z' uid='4464045' user='Anton Melnichuk' visible='true' version='1' changeset='79507850' lat='49.9651107' lon='36.326719'>\n    <tag k='bus' v='yes' />\n    <tag k='name' v='вул. Василя Мельникова' />\n    <tag k='public_transport' v='stop_position' />\n    <tag k='trolleybus' v='yes' />\n  </node>\n  <node id='7128002668' timestamp='2020-01-13T09:31:26Z' uid='4464045' user='Anton Melnichuk' visible='true' version='1' changeset='79507850' lat='49.9669579' lon='36.3192713'>\n    <tag k='bus' v='yes' />\n    <tag k='name' v='ст.метро Палац Спорту' />\n    <tag k='public_transport' v='stop_position' />\n    <tag k='trolleybus' v='yes' />\n  </node>\n  <node id='7319248285' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698498' lon='36.3096054' />\n  <node id='7319248286' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698181' lon='36.309819' />\n  <node id='7319248287' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.969731' lon='36.3101221' />\n  <node id='7319248288' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698298' lon='36.3097725' />\n  <node id='7319248289' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698375' lon='36.3097383' />\n  <node id='7319248290' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698421' lon='36.3097119' />\n  <node id='7319248291' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698458' lon='36.3096795' />\n  <node id='7319248292' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696729' lon='36.3103018' />\n  <node id='7319248293' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696466' lon='36.3104017' />\n  <node id='7319248294' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696233' lon='36.3105288' />\n  <node id='7319248295' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.969614' lon='36.3106038' />\n  <node id='7319248296' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696066' lon='36.3106656' />\n  <node id='7319248297' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696331' lon='36.3104681' />\n  <node id='7319248298' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696579' lon='36.3103518' />\n  <node id='7319248313' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82485517' lat='49.9695924' lon='36.3085883' />\n  <node id='7319248314' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698041' lon='36.3085383' />\n  <node id='7319248319' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9690779' lon='36.3084421' />\n  <node id='7319248320' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9690415' lon='36.3084447' />\n  <node id='7319248321' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9690198' lon='36.3084531' />\n  <node id='7319248322' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9690032' lon='36.3084662' />\n  <node id='7319248323' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9689891' lon='36.3084822' />\n  <node id='7319248324' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9689749' lon='36.3085053' />\n  <node id='7319248325' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9689612' lon='36.3085386' />\n  <node id='7319248326' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9689534' lon='36.3085652' />\n  <node id='7319248327' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9690588' lon='36.3084424' />\n  <node id='7319248328' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699538' lon='36.3093314' />\n  <node id='7319248329' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9700503' lon='36.3092797' />\n  <node id='7319248330' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9700773' lon='36.3092436' />\n  <node id='7319248331' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698432' lon='36.3094615' />\n  <node id='7319248332' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698371' lon='36.3094068' />\n  <node id='7319248333' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698307' lon='36.3093698' />\n  <node id='7319248334' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698216' lon='36.3093323' />\n  <node id='7319248335' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696542' lon='36.3094258' />\n  <node id='7319248336' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9697634' lon='36.3091894' />\n  <node id='7319248337' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9697392' lon='36.309154' />\n  <node id='7319248338' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696695' lon='36.3090901' />\n  <node id='7319248339' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9697166' lon='36.3091255' />\n  <node id='7319248396' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9701129' lon='36.3072762' />\n  <node id='7319248397' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9700763' lon='36.3071768' />\n  <node id='7319248398' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9700558' lon='36.3071388' />\n  <node id='7319248399' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699223' lon='36.3069846' />\n  <node id='7319248400' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699669' lon='36.3070262' />\n  <node id='7319248401' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9700318' lon='36.3071042' />\n  <node id='7319248402' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.970152' lon='36.3075169' />\n  <node id='7319248403' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9701485' lon='36.307584' />\n  <node id='7319248404' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9701422' lon='36.3076483' />\n  <node id='7319248405' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9701398' lon='36.3073841' />\n  <node id='7319248442' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9701002' lon='36.3092065' />\n  <node id='7319248443' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9700292' lon='36.3086672' />\n  <node id='7319248444' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699727' lon='36.3086151' />\n  <node id='7319248445' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699355' lon='36.3084909' />\n  <node id='7319248446' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699326' lon='36.3084457' />\n  <node id='7319248447' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699437' lon='36.3085366' />\n  <node id='7319248448' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698561' lon='36.3084979' />\n  <node id='7319248449' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698759' lon='36.3084751' />\n  <node id='7319248450' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9698974' lon='36.3084471' />\n  <node id='7319248451' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699316' lon='36.3093289' />\n  <node id='7319248452' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699351' lon='36.3095911' />\n  <node id='7319248453' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.969689' lon='36.3103783' />\n  <node id='7319248454' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696631' lon='36.3104648' />\n  <node id='7319248455' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696393' lon='36.3105553' />\n  <node id='7319248456' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9696206' lon='36.3106478' />\n  <node id='7319248457' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9700131' lon='36.309316' />\n  <node id='7319248458' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9699951' lon='36.309324' />\n  <node id='7319248459' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82485517' lat='49.9701403' lon='36.3086841' />\n  <node id='7319248460' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698' lat='49.9701143' lon='36.308685' />\n  <node id='7319248461' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82485517' lat='49.9701665' lon='36.3086814' />\n  <node id='7319336774' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82484399' lat='49.9674118' lon='36.3174961' />\n  <node id='7319336775' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82484399' lat='49.9674994' lon='36.3171686' />\n  <node id='7319336776' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82484399' lat='49.9675298' lon='36.3170606' />\n  <node id='7319336782' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82484399' lat='49.9688596' lon='36.3130167' />\n  <node id='7319414085' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82484399' lat='49.968913' lon='36.3128546' />\n  <node id='7319414092' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82484399' lat='49.9689598' lon='36.3130942' />\n  <node id='7319543422' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82485517' lat='49.9702482' lon='36.3092059' />\n  <node id='7319543423' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82485517' lat='49.9700088' lon='36.309704' />\n  <node id='7319543424' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82485517' lat='49.969471' lon='36.3113666' />\n  <node id='7319543425' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82485517' lat='49.9700321' lon='36.30963' />\n  <node id='7319543426' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82485517' lat='49.970153' lon='36.3092563' />\n  <node id='7323185496' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82533843' lat='49.9699147' lon='36.3095311' />\n  <node id='7323185497' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82533843' lat='49.9698977' lon='36.3095858' />\n  <node id='7323185498' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82533843' lat='49.9698817' lon='36.3096518' />\n  <node id='7323185499' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82533843' lat='49.9698772' lon='36.3095838' />\n  <way id='91323432' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698'>\n    <nd ref='256592834' />\n    <nd ref='7319248399' />\n    <nd ref='7319248400' />\n    <nd ref='1822129267' />\n    <nd ref='7319248401' />\n    <nd ref='7319248398' />\n    <nd ref='7319248397' />\n    <nd ref='256592835' />\n    <nd ref='7319248396' />\n    <nd ref='3297128912' />\n    <nd ref='7319248405' />\n    <nd ref='256592836' />\n    <nd ref='7319248402' />\n    <nd ref='7319248403' />\n    <nd ref='7319248404' />\n    <nd ref='256592837' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='2' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='105592516' timestamp='2012-01-17T09:27:37Z' uid='174295' user='vigor' visible='true' version='2' changeset='10415764'>\n    <nd ref='1215941802' />\n    <nd ref='1215941743' />\n    <nd ref='1215941711' />\n    <nd ref='1215941677' />\n    <nd ref='1215941802' />\n    <tag k='building' v='yes' />\n    <tag k='office' v='newspaper' />\n  </way>\n  <way id='146173228' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698'>\n    <nd ref='1994136773' />\n    <nd ref='7319248450' />\n    <nd ref='7319248449' />\n    <nd ref='7319248448' />\n    <nd ref='1994136769' />\n    <nd ref='7319248314' />\n    <nd ref='6844719027' />\n    <nd ref='1994136767' />\n    <nd ref='7319248313' />\n    <nd ref='1994136765' />\n    <nd ref='6844719021' />\n    <nd ref='6860854811' />\n    <nd ref='7319248319' />\n    <nd ref='7319248327' />\n    <nd ref='7319248320' />\n    <nd ref='7319248321' />\n    <nd ref='7319248322' />\n    <nd ref='7319248323' />\n    <nd ref='7319248324' />\n    <nd ref='7319248325' />\n    <nd ref='7319248326' />\n    <nd ref='6860854810' />\n    <nd ref='257750807' />\n    <tag k='highway' v='service' />\n    <tag k='maxspeed' v='10' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='177096182' timestamp='2012-09-02T22:37:19Z' uid='356014' user='_sev' visible='true' version='2' changeset='12960315'>\n    <nd ref='1875663044' />\n    <nd ref='1875662399' />\n    <nd ref='1875661705' />\n    <nd ref='1875661311' />\n    <nd ref='1875661639' />\n    <nd ref='1875661561' />\n    <nd ref='1875661907' />\n    <nd ref='1875661978' />\n    <nd ref='1875662079' />\n    <nd ref='1875662052' />\n    <nd ref='1875662266' />\n    <nd ref='1875663091' />\n    <nd ref='1875663044' />\n    <tag k='addr:housenumber' v='247' />\n    <tag k='building' v='yes' />\n    <tag k='building:entrances' v='1' />\n    <tag k='building:levels' v='1' />\n  </way>\n  <way id='188791230' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698'>\n    <nd ref='6113014895' />\n    <nd ref='7319248456' />\n    <nd ref='7319248455' />\n    <nd ref='7319248454' />\n    <nd ref='7319248453' />\n    <nd ref='7319248452' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='1' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='208884481' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='8' changeset='82481698'>\n    <nd ref='256592837' />\n    <nd ref='1994136766' />\n    <nd ref='1994136773' />\n    <tag k='highway' v='primary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxheight' v='3.8' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Московський проспект' />\n    <tag k='name:en' v='Moskovsky Avenue' />\n    <tag k='name:ru' v='Московский проспект' />\n    <tag k='name:uk' v='Московський проспект' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n    <tag k='trolley_wire' v='yes' />\n  </way>\n  <way id='218629469' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82485517'>\n    <nd ref='2278582831' />\n    <nd ref='7319543422' />\n    <nd ref='2278582776' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='218629473' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='7' changeset='82484399'>\n    <nd ref='2278582592' />\n    <nd ref='394867165' />\n    <nd ref='2278582817' />\n    <nd ref='3395794789' />\n    <nd ref='2278582598' />\n    <nd ref='2278582853' />\n    <nd ref='2278582615' />\n    <nd ref='2278582611' />\n    <nd ref='2278582609' />\n    <nd ref='2278582841' />\n    <nd ref='2278582837' />\n    <nd ref='2278582836' />\n    <nd ref='2278582831' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='329957377' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698'>\n    <nd ref='256936806' />\n    <nd ref='256592837' />\n    <tag k='highway' v='primary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Московський проспект' />\n    <tag k='name:en' v='Moskovsky Avenue' />\n    <tag k='name:ru' v='Московский проспект' />\n    <tag k='name:uk' v='Московський проспект' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n    <tag k='trolley_wire' v='yes' />\n  </way>\n  <way id='557993646' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='4' changeset='75422833'>\n    <nd ref='5380737766' />\n    <nd ref='5533296020' />\n    <nd ref='5533296017' />\n    <nd ref='5533298821' />\n    <nd ref='5533296016' />\n    <nd ref='5533298822' />\n    <nd ref='5533296018' />\n    <nd ref='5533296019' />\n    <nd ref='5380737767' />\n    <nd ref='5380737768' />\n    <nd ref='5533298849' />\n    <nd ref='6860854813' />\n    <nd ref='6860854812' />\n    <nd ref='5533298848' />\n    <nd ref='5533298850' />\n    <nd ref='5533298846' />\n    <nd ref='5533298851' />\n    <nd ref='5533298847' />\n    <nd ref='5533298852' />\n    <nd ref='5380737769' />\n    <nd ref='6844719031' />\n    <nd ref='6844719032' />\n    <nd ref='5380737766' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='637649314' timestamp='2020-03-22T14:12:06Z' uid='1730741' user='Sergey Harini' visible='true' version='14' changeset='82484399'>\n    <nd ref='320221610' />\n    <nd ref='3394184260' />\n    <nd ref='7128002665' />\n    <nd ref='256361314' />\n    <nd ref='6855876345' />\n    <nd ref='6855876351' />\n    <nd ref='6855876346' />\n    <nd ref='6004666905' />\n    <nd ref='907931888' />\n    <nd ref='6004666902' />\n    <nd ref='7128002666' />\n    <nd ref='256361315' />\n    <nd ref='6844822668' />\n    <nd ref='394141233' />\n    <nd ref='6844822664' />\n    <nd ref='256361316' />\n    <nd ref='3384933761' />\n    <nd ref='256361317' />\n    <nd ref='1001247631' />\n    <nd ref='267142033' />\n    <nd ref='7128002668' />\n    <nd ref='256361318' />\n    <nd ref='7319336774' />\n    <nd ref='7319336775' />\n    <nd ref='7319336776' />\n    <nd ref='388306202' />\n    <nd ref='6860854814' />\n    <nd ref='256361319' />\n    <nd ref='7319336782' />\n    <nd ref='7319414085' />\n    <nd ref='3395844877' />\n    <nd ref='6113014895' />\n    <tag k='highway' v='primary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Московський проспект' />\n    <tag k='name:en' v='Moskovsky Avenue' />\n    <tag k='name:ru' v='Московский проспект' />\n    <tag k='name:uk' v='Московський проспект' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n    <tag k='trolley_wire' v='yes' />\n  </way>\n  <way id='651964350' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698'>\n    <nd ref='1994136773' />\n    <nd ref='7319248446' />\n    <nd ref='7319248445' />\n    <nd ref='7319248447' />\n    <nd ref='6113014083' />\n    <nd ref='7319248444' />\n    <nd ref='6860854808' />\n    <nd ref='7319248443' />\n    <nd ref='6113014084' />\n    <nd ref='6113014885' />\n    <nd ref='7319248460' />\n    <nd ref='7319248459' />\n    <nd ref='7319248461' />\n    <nd ref='6113014886' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='2' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='651964352' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='6' changeset='82481698'>\n    <nd ref='6113014895' />\n    <nd ref='7319248296' />\n    <nd ref='7319248295' />\n    <nd ref='7319248294' />\n    <nd ref='7319248297' />\n    <nd ref='7319248293' />\n    <nd ref='7319248298' />\n    <nd ref='7319248292' />\n    <nd ref='7319248287' />\n    <nd ref='7319248286' />\n    <nd ref='7319248288' />\n    <nd ref='7319248289' />\n    <nd ref='7319248290' />\n    <nd ref='7319248291' />\n    <nd ref='6844719025' />\n    <nd ref='7319248285' />\n    <nd ref='6113014897' />\n    <nd ref='6113014898' />\n    <nd ref='7319248331' />\n    <nd ref='7319248332' />\n    <nd ref='7319248333' />\n    <nd ref='7319248334' />\n    <nd ref='6113014891' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='1' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='651964353' timestamp='2019-10-02T14:06:20Z' uid='421494' user='Anton Shevchuk' visible='true' version='3' changeset='75189911'>\n    <nd ref='6113014902' />\n    <nd ref='6113014903' />\n    <nd ref='6113014904' />\n    <nd ref='6113014905' />\n    <nd ref='6113014906' />\n    <nd ref='6113014907' />\n    <nd ref='6113014908' />\n    <nd ref='6113014909' />\n    <nd ref='6113014910' />\n    <nd ref='6113014911' />\n    <nd ref='6113014912' />\n    <nd ref='6113014913' />\n    <nd ref='6113014902' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='651964354' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='5' changeset='75422833'>\n    <nd ref='6860854797' />\n    <nd ref='6113014891' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='2' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='651964355' timestamp='2020-01-13T09:31:26Z' uid='4464045' user='Anton Melnichuk' visible='true' version='3' changeset='79507850'>\n    <nd ref='6113014889' />\n    <nd ref='6860854797' />\n    <tag k='highway' v='primary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Московський проспект' />\n    <tag k='name:en' v='Moskovsky Avenue' />\n    <tag k='name:ru' v='Московский проспект' />\n    <tag k='name:uk' v='Московський проспект' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n    <tag k='trolley_wire' v='yes' />\n  </way>\n  <way id='651969736' timestamp='2020-01-13T09:31:26Z' uid='4464045' user='Anton Melnichuk' visible='true' version='4' changeset='79507850'>\n    <nd ref='1994136773' />\n    <nd ref='6113034279' />\n    <nd ref='6113014889' />\n    <tag k='highway' v='primary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Московський проспект' />\n    <tag k='name:en' v='Moskovsky Avenue' />\n    <tag k='name:ru' v='Московский проспект' />\n    <tag k='name:uk' v='Московський проспект' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n    <tag k='trolley_wire' v='yes' />\n  </way>\n  <way id='651969739' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82533843'>\n    <nd ref='6113049174' />\n    <nd ref='6113049176' />\n    <nd ref='6844719024' />\n    <nd ref='6113049177' />\n    <nd ref='7323185496' />\n    <nd ref='6113049174' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='730783904' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='4' changeset='82481698'>\n    <nd ref='6113014889' />\n    <nd ref='7319248338' />\n    <nd ref='6860854806' />\n    <nd ref='6844719028' />\n    <nd ref='6844719020' />\n    <nd ref='395286068' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='2' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='730783905' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75422833'>\n    <nd ref='3395844884' />\n    <nd ref='6860854802' />\n    <nd ref='6844719028' />\n    <nd ref='6860854805' />\n    <tag k='crossing' v='marked' />\n    <tag k='footway' v='crossing' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='730783906' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698'>\n    <nd ref='3395844884' />\n    <nd ref='6113014901' />\n    <nd ref='7319248335' />\n    <tag k='crossing' v='marked' />\n    <tag k='footway' v='crossing' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='730783907' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='3' changeset='82481698'>\n    <nd ref='6844719033' />\n    <nd ref='6113014897' />\n    <nd ref='6844719026' />\n    <nd ref='7319248452' />\n    <nd ref='2462601889' />\n    <nd ref='2462601938' />\n    <tag k='crossing' v='marked' />\n    <tag k='footway' v='crossing' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='730783908' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698'>\n    <nd ref='7319248335' />\n    <nd ref='6844719033' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='732139555' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75443528'>\n    <nd ref='3297128912' />\n    <nd ref='6855577788' />\n    <nd ref='6862476865' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='732139858' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82533843'>\n    <nd ref='6855551842' />\n    <nd ref='6844719034' />\n    <nd ref='6855551845' />\n    <nd ref='6855551846' />\n    <nd ref='6855551847' />\n    <nd ref='6855551848' />\n    <nd ref='6855551849' />\n    <nd ref='6855551850' />\n    <nd ref='6855551851' />\n    <nd ref='6855551852' />\n    <nd ref='6855551853' />\n    <nd ref='6855551854' />\n    <nd ref='6855551855' />\n    <nd ref='6855551856' />\n    <nd ref='6855551857' />\n    <nd ref='6855551858' />\n    <nd ref='6855551859' />\n    <nd ref='6855551860' />\n    <nd ref='6855551861' />\n    <nd ref='6855551842' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='732681511' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698'>\n    <nd ref='6113014891' />\n    <nd ref='6113014893' />\n    <nd ref='7319248451' />\n    <nd ref='7319248328' />\n    <nd ref='6860854800' />\n    <nd ref='7319248458' />\n    <nd ref='7319248457' />\n    <nd ref='6860854801' />\n    <nd ref='7319248329' />\n    <nd ref='7319248330' />\n    <nd ref='7319248442' />\n    <nd ref='6113014894' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='2' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='732681512' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='2' changeset='82481698'>\n    <nd ref='6113014891' />\n    <nd ref='6860854798' />\n    <nd ref='7319248336' />\n    <nd ref='7319248337' />\n    <nd ref='7319248339' />\n    <nd ref='6113014889' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='1' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='732681514' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833'>\n    <nd ref='395286068' />\n    <nd ref='6860854804' />\n    <nd ref='6860854802' />\n    <nd ref='6860854797' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='2' />\n    <tag k='maxspeed' v='50' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n  </way>\n  <way id='732681515' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75443528'>\n    <nd ref='6860854805' />\n    <nd ref='6862476864' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='732681519' timestamp='2020-01-13T09:31:26Z' uid='4464045' user='Anton Melnichuk' visible='true' version='2' changeset='79507850'>\n    <nd ref='6860854797' />\n    <nd ref='6113014901' />\n    <nd ref='6113014917' />\n    <tag k='highway' v='primary' />\n    <tag k='lanes' v='3' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Московський проспект' />\n    <tag k='name:en' v='Moskovsky Avenue' />\n    <tag k='name:ru' v='Московский проспект' />\n    <tag k='name:uk' v='Московський проспект' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n    <tag k='trolley_wire' v='yes' />\n  </way>\n  <way id='732681522' timestamp='2020-03-22T15:13:50Z' uid='1730741' user='Sergey Harini' visible='true' version='5' changeset='82485957'>\n    <nd ref='2278582831' />\n    <nd ref='2462601938' />\n    <nd ref='3395844886' />\n    <nd ref='2278582703' />\n    <nd ref='3395844882' />\n    <nd ref='2278582700' />\n    <nd ref='7319414092' />\n    <nd ref='2278582517' />\n    <nd ref='3391488463' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='732681523' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833'>\n    <nd ref='6860854815' />\n    <nd ref='6860854816' />\n    <nd ref='6860854817' />\n    <nd ref='6860854818' />\n    <nd ref='6860854819' />\n    <nd ref='6860854820' />\n    <nd ref='6860854815' />\n    <tag k='natural' v='wood' />\n  </way>\n  <way id='732854268' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528'>\n    <nd ref='6862476864' />\n    <nd ref='6844719027' />\n    <nd ref='6862476865' />\n    <tag k='crossing' v='marked' />\n    <tag k='footway' v='crossing' />\n    <tag k='highway' v='footway' />\n  </way>\n  <way id='732854269' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528'>\n    <nd ref='6862476866' />\n    <nd ref='6862476867' />\n    <nd ref='6862476868' />\n    <nd ref='6862476869' />\n    <nd ref='6862476866' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='732854271' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528'>\n    <nd ref='6862476872' />\n    <nd ref='6862476873' />\n    <nd ref='6862476874' />\n    <nd ref='6862476875' />\n    <nd ref='6862476876' />\n    <nd ref='6862476877' />\n    <nd ref='6862476878' />\n    <nd ref='6862476879' />\n    <nd ref='6862476880' />\n    <nd ref='6862476881' />\n    <nd ref='6862476882' />\n    <nd ref='6862476883' />\n    <nd ref='6862476884' />\n    <nd ref='6862497385' />\n    <nd ref='6862497386' />\n    <nd ref='6862497387' />\n    <nd ref='6862497388' />\n    <nd ref='6862497389' />\n    <nd ref='6862497390' />\n    <nd ref='6862476872' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='783612026' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698'>\n    <nd ref='256592834' />\n    <nd ref='6844719022' />\n    <nd ref='6855577788' />\n    <nd ref='1994136766' />\n    <tag k='covered' v='yes' />\n    <tag k='highway' v='service' />\n    <tag k='maxspeed' v='5' />\n    <tag k='oneway' v='no' />\n  </way>\n  <way id='783612027' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698'>\n    <nd ref='6113014895' />\n    <nd ref='6844719029' />\n    <nd ref='2462601889' />\n    <nd ref='6113014894' />\n    <nd ref='2278582488' />\n    <tag k='highway' v='primary' />\n    <tag k='lanes' v='2' />\n    <tag k='maxspeed' v='50' />\n    <tag k='name' v='Московський проспект' />\n    <tag k='name:en' v='Moskovsky Avenue' />\n    <tag k='name:ru' v='Московский проспект' />\n    <tag k='name:uk' v='Московський проспект' />\n    <tag k='oneway' v='yes' />\n    <tag k='surface' v='asphalt' />\n    <tag k='trolley_wire' v='yes' />\n  </way>\n  <way id='783612028' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82481698'>\n    <nd ref='7319248452' />\n    <nd ref='7319248457' />\n    <nd ref='6113014886' />\n    <tag k='highway' v='primary_link' />\n    <tag k='lanes' v='1' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='783643907' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82485517'>\n    <nd ref='7319543423' />\n    <nd ref='7319543424' />\n    <tag k='barrier' v='fence' />\n    <tag k='height' v='1' />\n  </way>\n  <way id='783643908' timestamp='2020-03-22T14:56:00Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82485517'>\n    <nd ref='7319543425' />\n    <nd ref='7319543426' />\n    <tag k='barrier' v='fence' />\n    <tag k='height' v='1' />\n  </way>\n  <way id='783975708' timestamp='2020-03-23T17:20:20Z' uid='1730741' user='Sergey Harini' visible='true' version='1' changeset='82533843'>\n    <nd ref='7323185497' />\n    <nd ref='7323185498' />\n    <nd ref='7323185499' />\n    <nd ref='7323185497' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <relation id='1295889' timestamp='2020-09-17T18:41:52Z' uid='9518695' user='taxxxi' visible='true' version='151' changeset='91065156'>\n    <member type='way' ref='183413233' role='house' />\n    <member type='way' ref='183403590' role='house' />\n    <member type='way' ref='183403583' role='house' />\n    <member type='way' ref='129237439' role='house' />\n    <member type='way' ref='137873610' role='house' />\n    <member type='way' ref='181582054' role='house' />\n    <member type='way' ref='137873602' role='house' />\n    <member type='way' ref='181582117' role='house' />\n    <member type='way' ref='181582188' role='house' />\n    <member type='way' ref='183403587' role='house' />\n    <member type='way' ref='181582221' role='house' />\n    <member type='way' ref='182037814' role='house' />\n    <member type='way' ref='182018643' role='house' />\n    <member type='way' ref='181582230' role='house' />\n    <member type='way' ref='148975405' role='house' />\n    <member type='way' ref='182018665' role='house' />\n    <member type='way' ref='130742366' role='house' />\n    <member type='way' ref='183484726' role='house' />\n    <member type='way' ref='182018672' role='house' />\n    <member type='way' ref='182018676' role='house' />\n    <member type='way' ref='583340159' role='house' />\n    <member type='way' ref='182018684' role='house' />\n    <member type='way' ref='182018687' role='house' />\n    <member type='way' ref='177520790' role='house' />\n    <member type='way' ref='183494850' role='house' />\n    <member type='way' ref='150928441' role='house' />\n    <member type='way' ref='177520803' role='house' />\n    <member type='way' ref='183494856' role='house' />\n    <member type='way' ref='177520807' role='house' />\n    <member type='way' ref='183494858' role='house' />\n    <member type='way' ref='182018704' role='house' />\n    <member type='way' ref='177520811' role='house' />\n    <member type='way' ref='177520812' role='house' />\n    <member type='way' ref='177520813' role='house' />\n    <member type='way' ref='700434854' role='house' />\n    <member type='way' ref='700434853' role='house' />\n    <member type='way' ref='89527699' role='house' />\n    <member type='way' ref='183484778' role='house' />\n    <member type='way' ref='183484780' role='house' />\n    <member type='way' ref='183484782' role='house' />\n    <member type='way' ref='182018705' role='house' />\n    <member type='way' ref='177520821' role='house' />\n    <member type='way' ref='177520823' role='house' />\n    <member type='way' ref='182018710' role='house' />\n    <member type='way' ref='154837808' role='house' />\n    <member type='way' ref='163223506' role='house' />\n    <member type='way' ref='154837809' role='house' />\n    <member type='way' ref='177520839' role='house' />\n    <member type='way' ref='182018724' role='house' />\n    <member type='way' ref='150928446' role='house' />\n    <member type='way' ref='182018725' role='house' />\n    <member type='way' ref='183484805' role='house' />\n    <member type='way' ref='177520844' role='house' />\n    <member type='way' ref='181770172' role='house' />\n    <member type='way' ref='150928439' role='house' />\n    <member type='way' ref='177520857' role='house' />\n    <member type='way' ref='177520858' role='house' />\n    <member type='way' ref='177520859' role='house' />\n    <member type='way' ref='177520860' role='house' />\n    <member type='way' ref='182018732' role='house' />\n    <member type='way' ref='177519058' role='house' />\n    <member type='way' ref='177520862' role='house' />\n    <member type='way' ref='218594085' role='house' />\n    <member type='way' ref='177520863' role='house' />\n    <member type='way' ref='182018733' role='house' />\n    <member type='way' ref='182018734' role='house' />\n    <member type='way' ref='177519060' role='house' />\n    <member type='way' ref='177520865' role='house' />\n    <member type='way' ref='177519073' role='house' />\n    <member type='way' ref='177519074' role='house' />\n    <member type='way' ref='183484824' role='house' />\n    <member type='way' ref='177519076' role='house' />\n    <member type='way' ref='183484826' role='house' />\n    <member type='way' ref='181770209' role='house' />\n    <member type='way' ref='177519079' role='house' />\n    <member type='way' ref='177519080' role='house' />\n    <member type='way' ref='177519096' role='house' />\n    <member type='way' ref='181770223' role='house' />\n    <member type='way' ref='177519098' role='house' />\n    <member type='way' ref='177519099' role='house' />\n    <member type='way' ref='177519101' role='house' />\n    <member type='way' ref='177519102' role='house' />\n    <member type='way' ref='183484843' role='house' />\n    <member type='way' ref='177519104' role='house' />\n    <member type='way' ref='177519106' role='house' />\n    <member type='way' ref='177519107' role='house' />\n    <member type='way' ref='177519109' role='house' />\n    <member type='way' ref='183484844' role='house' />\n    <member type='way' ref='183484845' role='house' />\n    <member type='way' ref='183484846' role='house' />\n    <member type='way' ref='183484847' role='house' />\n    <member type='way' ref='183484848' role='house' />\n    <member type='way' ref='183484849' role='house' />\n    <member type='way' ref='181770237' role='house' />\n    <member type='way' ref='183484850' role='house' />\n    <member type='way' ref='151068681' role='house' />\n    <member type='way' ref='151068674' role='house' />\n    <member type='way' ref='151068666' role='house' />\n    <member type='way' ref='120445305' role='house' />\n    <member type='way' ref='183484852' role='house' />\n    <member type='way' ref='151068667' role='house' />\n    <member type='way' ref='181770053' role='house' />\n    <member type='way' ref='181770059' role='house' />\n    <member type='way' ref='177518859' role='house' />\n    <member type='way' ref='181791995' role='house' />\n    <member type='way' ref='183484664' role='house' />\n    <member type='way' ref='183484663' role='house' />\n    <member type='way' ref='182008850' role='house' />\n    <member type='way' ref='181792008' role='house' />\n    <member type='way' ref='385157599' role='house' />\n    <member type='way' ref='181792002' role='house' />\n    <member type='way' ref='181792007' role='house' />\n    <member type='way' ref='385157598' role='house' />\n    <member type='way' ref='85311525' role='house' />\n    <member type='way' ref='85311833' role='house' />\n    <member type='way' ref='183484666' role='house' />\n    <member type='way' ref='182013970' role='house' />\n    <member type='way' ref='152099586' role='house' />\n    <member type='way' ref='294658273' role='house' />\n    <member type='way' ref='152099589' role='house' />\n    <member type='way' ref='152099587' role='house' />\n    <member type='way' ref='181770071' role='house' />\n    <member type='way' ref='177518879' role='house' />\n    <member type='way' ref='177518880' role='house' />\n    <member type='way' ref='177518881' role='house' />\n    <member type='way' ref='131391937' role='house' />\n    <member type='way' ref='208879835' role='house' />\n    <member type='way' ref='183864523' role='house' />\n    <member type='way' ref='181734402' role='house' />\n    <member type='way' ref='182192969' role='house' />\n    <member type='way' ref='146173241' role='house' />\n    <member type='way' ref='181770073' role='house' />\n    <member type='way' ref='177095355' role='house' />\n    <member type='way' ref='177095356' role='house' />\n    <member type='way' ref='294664781' role='house' />\n    <member type='way' ref='177095350' role='house' />\n    <member type='way' ref='177095357' role='house' />\n    <member type='way' ref='177095358' role='house' />\n    <member type='way' ref='177095359' role='house' />\n    <member type='way' ref='177095360' role='house' />\n    <member type='way' ref='177095361' role='house' />\n    <member type='way' ref='177095362' role='house' />\n    <member type='way' ref='177095363' role='house' />\n    <member type='way' ref='181770081' role='house' />\n    <member type='way' ref='86538065' role='house' />\n    <member type='way' ref='181734403' role='house' />\n    <member type='way' ref='176953927' role='house' />\n    <member type='way' ref='176953936' role='house' />\n    <member type='way' ref='130922354' role='house' />\n    <member type='way' ref='181770094' role='house' />\n    <member type='way' ref='176954027' role='house' />\n    <member type='way' ref='176954031' role='house' />\n    <member type='way' ref='176954033' role='house' />\n    <member type='way' ref='176954112' role='house' />\n    <member type='way' ref='627316489' role='house' />\n    <member type='way' ref='621247909' role='house' />\n    <member type='way' ref='87676288' role='house' />\n    <member type='way' ref='176954405' role='house' />\n    <member type='way' ref='85311512' role='house' />\n    <member type='way' ref='263639046' role='house' />\n    <member type='way' ref='85311786' role='house' />\n    <member type='way' ref='182008878' role='house' />\n    <member type='way' ref='86538048' role='house' />\n    <member type='way' ref='183484686' role='house' />\n    <member type='way' ref='304057863' role='house' />\n    <member type='way' ref='86538053' role='house' />\n    <member type='way' ref='86538087' role='house' />\n    <member type='way' ref='86538026' role='house' />\n    <member type='way' ref='183484690' role='house' />\n    <member type='way' ref='678493187' role='house' />\n    <member type='way' ref='177289463' role='house' />\n    <member type='way' ref='358002926' role='house' />\n    <member type='way' ref='85311723' role='house' />\n    <member type='way' ref='85311461' role='house' />\n    <member type='way' ref='86538039' role='house' />\n    <member type='way' ref='86538044' role='house' />\n    <member type='way' ref='86538157' role='house' />\n    <member type='way' ref='183781732' role='house' />\n    <member type='way' ref='181792050' role='house' />\n    <member type='way' ref='86538024' role='house' />\n    <member type='way' ref='86538066' role='house' />\n    <member type='way' ref='86538167' role='house' />\n    <member type='way' ref='177095703' role='house' />\n    <member type='way' ref='150737984' role='house' />\n    <member type='way' ref='183781733' role='house' />\n    <member type='way' ref='146173232' role='house' />\n    <member type='way' ref='146173222' role='house' />\n    <member type='way' ref='181792063' role='house' />\n    <member type='way' ref='150737939' role='house' />\n    <member type='way' ref='563673893' role='house' />\n    <member type='way' ref='177095825' role='house' />\n    <member type='way' ref='86538047' role='house' />\n    <member type='way' ref='86538031' role='house' />\n    <member type='way' ref='150737964' role='house' />\n    <member type='way' ref='86538000' role='house' />\n    <member type='way' ref='86538109' role='house' />\n    <member type='way' ref='86538019' role='house' />\n    <member type='way' ref='86538054' role='house' />\n    <member type='way' ref='183864399' role='house' />\n    <member type='way' ref='177095851' role='house' />\n    <member type='way' ref='181734428' role='house' />\n    <member type='way' ref='86222315' role='house' />\n    <member type='way' ref='86222382' role='house' />\n    <member type='way' ref='181734429' role='house' />\n    <member type='way' ref='86222359' role='house' />\n    <member type='way' ref='86222294' role='house' />\n    <member type='way' ref='86222426' role='house' />\n    <member type='way' ref='86222335' role='house' />\n    <member type='way' ref='86222371' role='house' />\n    <member type='way' ref='86222306' role='house' />\n    <member type='way' ref='181735407' role='house' />\n    <member type='way' ref='86222434' role='house' />\n    <member type='way' ref='86222299' role='house' />\n    <member type='way' ref='177095973' role='house' />\n    <member type='way' ref='157149576' role='house' />\n    <member type='way' ref='183864400' role='house' />\n    <member type='way' ref='86222393' role='house' />\n    <member type='way' ref='208879821' role='house' />\n    <member type='way' ref='86222300' role='house' />\n    <member type='way' ref='150610581' role='house' />\n    <member type='way' ref='150610582' role='house' />\n    <member type='way' ref='86222292' role='house' />\n    <member type='way' ref='150610583' role='house' />\n    <member type='way' ref='150610584' role='house' />\n    <member type='way' ref='86222395' role='house' />\n    <member type='way' ref='181735412' role='house' />\n    <member type='way' ref='150610585' role='house' />\n    <member type='way' ref='150610586' role='house' />\n    <member type='way' ref='150610587' role='house' />\n    <member type='way' ref='181789314' role='house' />\n    <member type='way' ref='86222422' role='house' />\n    <member type='way' ref='150610589' role='house' />\n    <member type='way' ref='150610590' role='house' />\n    <member type='way' ref='86222414' role='house' />\n    <member type='way' ref='86222353' role='house' />\n    <member type='way' ref='129008659' role='house' />\n    <member type='way' ref='182201318' role='house' />\n    <member type='way' ref='182006648' role='house' />\n    <member type='way' ref='177096182' role='house' />\n    <member type='way' ref='86256708' role='house' />\n    <member type='way' ref='86256717' role='house' />\n    <member type='way' ref='86256715' role='house' />\n    <member type='way' ref='86256721' role='house' />\n    <member type='way' ref='86256774' role='house' />\n    <member type='way' ref='86256779' role='house' />\n    <member type='way' ref='86256762' role='house' />\n    <member type='way' ref='181735418' role='house' />\n    <member type='way' ref='86256804' role='house' />\n    <member type='way' ref='177096214' role='house' />\n    <member type='way' ref='86256745' role='house' />\n    <member type='way' ref='86256765' role='house' />\n    <member type='way' ref='145259428' role='house' />\n    <member type='way' ref='183864190' role='house' />\n    <member type='way' ref='151462850' role='house' />\n    <member type='way' ref='150637314' role='house' />\n    <member type='way' ref='105400589' role='house' />\n    <member type='way' ref='85078567' role='house' />\n    <member type='way' ref='86256813' role='house' />\n    <member type='way' ref='86256756' role='house' />\n    <member type='way' ref='177096221' role='house' />\n    <member type='way' ref='105400601' role='house' />\n    <member type='way' ref='673235967' role='house' />\n    <member type='way' ref='85311650' role='house' />\n    <member type='way' ref='177096254' role='house' />\n    <member type='way' ref='146997089' role='house' />\n    <member type='way' ref='146997094' role='house' />\n    <member type='way' ref='177096256' role='house' />\n    <member type='way' ref='577100669' role='house' />\n    <member type='way' ref='577100666' role='house' />\n    <member type='way' ref='627316492' role='house' />\n    <member type='way' ref='146997104' role='house' />\n    <member type='way' ref='577100681' role='house' />\n    <member type='way' ref='577100672' role='house' />\n    <member type='way' ref='146997143' role='house' />\n    <member type='way' ref='182008256' role='house' />\n    <member type='way' ref='146997157' role='house' />\n    <member type='way' ref='84934260' role='house' />\n    <member type='way' ref='84911748' role='house' />\n    <member type='way' ref='182006651' role='house' />\n    <member type='way' ref='182006652' role='house' />\n    <member type='way' ref='320026499' role='house' />\n    <member type='way' ref='182006653' role='house' />\n    <member type='way' ref='181750947' role='house' />\n    <member type='way' ref='235119939' role='house' />\n    <member type='way' ref='182006654' role='house' />\n    <member type='way' ref='53836224' role='house' />\n    <member type='way' ref='53836223' role='house' />\n    <member type='way' ref='182006656' role='house' />\n    <member type='way' ref='53836225' role='house' />\n    <member type='way' ref='53836226' role='house' />\n    <member type='way' ref='84912524' role='house' />\n    <member type='way' ref='53836212' role='house' />\n    <member type='way' ref='182006657' role='house' />\n    <member type='way' ref='281408834' role='house' />\n    <member type='way' ref='84707719' role='house' />\n    <member type='way' ref='181752500' role='house' />\n    <member type='way' ref='182006658' role='house' />\n    <member type='way' ref='320024431' role='house' />\n    <member type='way' ref='84755164' role='house' />\n    <member type='way' ref='181752501' role='house' />\n    <member type='way' ref='377981586' role='house' />\n    <member type='way' ref='317627799' role='house' />\n    <member type='way' ref='217811871' role='house' />\n    <member type='way' ref='377996815' role='house' />\n    <member type='way' ref='84755227' role='house' />\n    <member type='way' ref='182006667' role='house' />\n    <member type='way' ref='84755106' role='house' />\n    <member type='way' ref='84755223' role='house' />\n    <member type='way' ref='323993097' role='house' />\n    <member type='way' ref='182006671' role='house' />\n    <member type='way' ref='182006669' role='house' />\n    <member type='way' ref='82297014' role='house' />\n    <member type='way' ref='82297012' role='house' />\n    <member type='way' ref='158800482' role='house' />\n    <member type='way' ref='53836228' role='house' />\n    <member type='way' ref='53836208' role='house' />\n    <member type='way' ref='53836218' role='house' />\n    <member type='way' ref='181752507' role='house' />\n    <member type='way' ref='53836219' role='house' />\n    <member type='way' ref='53836220' role='house' />\n    <member type='way' ref='53836213' role='house' />\n    <member type='way' ref='53836211' role='house' />\n    <member type='way' ref='182200150' role='house' />\n    <member type='way' ref='486424733' role='street' />\n    <member type='way' ref='486424726' role='street' />\n    <member type='way' ref='486424729' role='street' />\n    <member type='way' ref='486424732' role='street' />\n    <member type='way' ref='29595763' role='street' />\n    <member type='way' ref='830704598' role='street' />\n    <member type='way' ref='29595762' role='street' />\n    <member type='way' ref='158800476' role='street' />\n    <member type='way' ref='158800477' role='street' />\n    <member type='way' ref='158800474' role='street' />\n    <member type='way' ref='158800479' role='street' />\n    <member type='way' ref='830704599' role='street' />\n    <member type='way' ref='486424731' role='street' />\n    <member type='way' ref='84603722' role='street' />\n    <member type='way' ref='29386169' role='street' />\n    <member type='way' ref='783680573' role='street' />\n    <member type='way' ref='783680571' role='street' />\n    <member type='way' ref='329957376' role='street' />\n    <member type='way' ref='108193925' role='street' />\n    <member type='way' ref='108193928' role='street' />\n    <member type='way' ref='208884482' role='street' />\n    <member type='way' ref='651969738' role='street' />\n    <member type='way' ref='783612027' role='street' />\n    <member type='way' ref='637649314' role='street' />\n    <member type='way' ref='848573771' role='street' />\n    <member type='way' ref='160212692' role='street' />\n    <member type='way' ref='23663624' role='street' />\n    <member type='way' ref='23663506' role='street' />\n    <member type='way' ref='29112848' role='street' />\n    <member type='way' ref='732334806' role='street' />\n    <member type='way' ref='732334801' role='street' />\n    <member type='way' ref='637649316' role='street' />\n    <member type='way' ref='783654878' role='street' />\n    <member type='way' ref='651964356' role='street' />\n    <member type='way' ref='732681519' role='street' />\n    <member type='way' ref='651964355' role='street' />\n    <member type='way' ref='651969736' role='street' />\n    <member type='way' ref='208884481' role='street' />\n    <member type='way' ref='329957377' role='street' />\n    <member type='way' ref='255803308' role='street' />\n    <member type='way' ref='108194335' role='street' />\n    <member type='way' ref='329957375' role='street' />\n    <member type='way' ref='783680574' role='street' />\n    <member type='way' ref='217811834' role='street' />\n    <member type='way' ref='783654876' role='street' />\n    <member type='way' ref='34392084' role='street' />\n    <member type='way' ref='845779418' role='street' />\n    <member type='way' ref='845779417' role='street' />\n    <member type='way' ref='502403426' role='street' />\n    <member type='way' ref='845779416' role='street' />\n    <member type='way' ref='255096297' role='street' />\n    <member type='way' ref='845770200' role='street' />\n    <member type='way' ref='637649317' role='street' />\n    <member type='way' ref='762819056' role='street' />\n    <member type='way' ref='255096296' role='street' />\n    <member type='way' ref='23663507' role='street' />\n    <member type='way' ref='57942075' role='street' />\n    <member type='way' ref='567517799' role='street' />\n    <member type='way' ref='531246179' role='street' />\n    <member type='way' ref='805505717' role='street' />\n    <member type='way' ref='805505715' role='street' />\n    <member type='way' ref='531246178' role='street' />\n    <member type='way' ref='38454163' role='street' />\n    <member type='way' ref='501471091' role='street' />\n    <member type='way' ref='673815656' role='street' />\n    <member type='way' ref='87082080' role='street' />\n    <member type='way' ref='537903776' role='street' />\n    <member type='way' ref='685200511' role='street' />\n    <member type='way' ref='23697347' role='street' />\n    <member type='way' ref='273442815' role='street' />\n    <member type='way' ref='286557490' role='street' />\n    <member type='way' ref='685200513' role='street' />\n    <member type='way' ref='531256799' role='street' />\n    <member type='way' ref='673815658' role='street' />\n    <member type='way' ref='531256803' role='street' />\n    <member type='way' ref='531256801' role='street' />\n    <member type='way' ref='805505716' role='street' />\n    <member type='way' ref='806453724' role='street' />\n    <member type='way' ref='805505719' role='street' />\n    <member type='way' ref='531256805' role='street' />\n    <member type='way' ref='567517800' role='street' />\n    <member type='way' ref='531256807' role='street' />\n    <member type='way' ref='255096298' role='street' />\n    <member type='way' ref='637649313' role='street' />\n    <member type='way' ref='762819052' role='street' />\n    <member type='way' ref='217811833' role='street' />\n    <member type='way' ref='217811835' role='street' />\n    <member type='way' ref='646355729' role='street' />\n    <member type='way' ref='217811836' role='street' />\n    <member type='way' ref='210437422' role='street' />\n    <member type='way' ref='259740561' role='street' />\n    <member type='way' ref='42750822' role='street' />\n    <member type='way' ref='218006713' role='street' />\n    <member type='way' ref='320649760' role='street' />\n    <member type='way' ref='637986244' role='street' />\n    <member type='way' ref='637847389' role='street' />\n    <member type='way' ref='198212063' role='street' />\n    <member type='way' ref='650735645' role='street' />\n    <member type='way' ref='158801227' role='street' />\n    <member type='way' ref='158801228' role='street' />\n    <member type='way' ref='198211509' role='street' />\n    <member type='way' ref='734585144' role='street' />\n    <member type='way' ref='105937035' role='street' />\n    <member type='way' ref='29386174' role='street' />\n    <member type='way' ref='657255775' role='house' />\n    <member type='way' ref='146786672' role='house' />\n    <member type='way' ref='151693842' role='house' />\n    <member type='way' ref='669009013' role='house' />\n    <member type='way' ref='670571136' role='house' />\n    <member type='way' ref='672538776' role='house' />\n    <member type='way' ref='680854123' role='house' />\n    <member type='way' ref='680854124' role='house' />\n    <member type='way' ref='680854125' role='house' />\n    <member type='way' ref='680867321' role='house' />\n    <member type='way' ref='680867323' role='house' />\n    <member type='way' ref='680867325' role='house' />\n    <member type='way' ref='699236692' role='' />\n    <member type='way' ref='799811768' role='street' />\n    <member type='way' ref='799811769' role='street' />\n    <tag k='name' v='Московський проспект' />\n    <tag k='name:ru' v='Московский проспект' />\n    <tag k='name:uk' v='Московський проспект' />\n    <tag k='street:id' v='1156' />\n    <tag k='type' v='associatedStreet' />\n  </relation>\n  <relation id='1537513' timestamp='2020-07-28T05:08:28Z' uid='9518695' user='taxxxi' visible='true' version='35' changeset='88596215'>\n    <member type='node' ref='1240877369' role='stop' />\n    <member type='node' ref='7083058541' role='platform' />\n    <member type='node' ref='7073941394' role='platform' />\n    <member type='node' ref='1240897121' role='stop' />\n    <member type='node' ref='388311629' role='stop' />\n    <member type='node' ref='3395794791' role='stop_exit_only' />\n    <member type='node' ref='2191392189' role='stop_entry_only' />\n    <member type='node' ref='395286068' role='' />\n    <member type='way' ref='132975218' role='' />\n    <member type='way' ref='757459062' role='' />\n    <member type='way' ref='152444324' role='' />\n    <member type='way' ref='758428561' role='' />\n    <member type='way' ref='108105463' role='' />\n    <member type='way' ref='830019631' role='' />\n    <member type='way' ref='154480462' role='' />\n    <member type='way' ref='108193927' role='' />\n    <member type='way' ref='154480460' role='' />\n    <member type='way' ref='830019629' role='' />\n    <member type='way' ref='764243524' role='' />\n    <member type='way' ref='33891641' role='' />\n    <member type='way' ref='85965180' role='' />\n    <member type='way' ref='85965208' role='' />\n    <member type='way' ref='87451168' role='' />\n    <member type='way' ref='203344300' role='' />\n    <member type='way' ref='203344302' role='' />\n    <member type='way' ref='132509608' role='' />\n    <member type='way' ref='23788523' role='' />\n    <member type='way' ref='91323401' role='' />\n    <member type='way' ref='732681513' role='' />\n    <member type='way' ref='730783904' role='' />\n    <member type='way' ref='651969736' role='' />\n    <member type='way' ref='23862335' role='' />\n    <member type='way' ref='91323386' role='' />\n    <member type='way' ref='91323538' role='' />\n    <member type='way' ref='30101517' role='' />\n    <member type='way' ref='203344301' role='' />\n    <member type='way' ref='203344299' role='' />\n    <member type='way' ref='329957377' role='' />\n    <member type='way' ref='255803308' role='' />\n    <member type='way' ref='208884481' role='' />\n    <member type='way' ref='108193928' role='' />\n    <member type='way' ref='23663763' role='' />\n    <member type='way' ref='108194335' role='' />\n    <tag k='name' v='Тролейбус №20' />\n    <tag k='ref' v='20' />\n    <tag k='route' v='trolleybus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='1537528' timestamp='2012-03-13T09:24:42Z' uid='401294' user='azure' visible='true' version='2' changeset='10964470'>\n    <member type='relation' ref='1777784' role='' />\n    <member type='relation' ref='1777785' role='' />\n    <member type='relation' ref='1537513' role='' />\n    <member type='relation' ref='1536609' role='' />\n    <member type='relation' ref='1786308' role='' />\n    <member type='relation' ref='2077914' role='' />\n    <member type='relation' ref='2080928' role='' />\n    <member type='relation' ref='2055197' role='' />\n    <tag k='name' v='Тролейбуси' />\n    <tag k='type' v='network' />\n  </relation>\n  <relation id='2077914' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='26' changeset='82481698'>\n    <member type='node' ref='1457514517' role='stop' />\n    <member type='node' ref='3395794791' role='stop_exit_only' />\n    <member type='node' ref='2191392189' role='stop_entry_only' />\n    <member type='node' ref='2578454065' role='platform' />\n    <member type='node' ref='395286068' role='platform' />\n    <member type='way' ref='30101517' role='' />\n    <member type='way' ref='203344301' role='' />\n    <member type='way' ref='203344299' role='' />\n    <member type='way' ref='23862335' role='' />\n    <member type='way' ref='91323386' role='' />\n    <member type='way' ref='91323538' role='' />\n    <member type='way' ref='108193928' role='' />\n    <member type='way' ref='23663763' role='' />\n    <member type='way' ref='108194335' role='' />\n    <member type='way' ref='255803308' role='' />\n    <member type='way' ref='329957377' role='' />\n    <member type='way' ref='208884481' role='' />\n    <member type='way' ref='651969736' role='' />\n    <member type='way' ref='730783904' role='' />\n    <member type='way' ref='732681513' role='' />\n    <member type='way' ref='91323401' role='' />\n    <member type='way' ref='23788523' role='' />\n    <member type='way' ref='132509608' role='' />\n    <member type='way' ref='203344302' role='' />\n    <member type='way' ref='203344300' role='' />\n    <member type='way' ref='251662719' role='' />\n    <member type='way' ref='89677467' role='' />\n    <member type='way' ref='152191982' role='' />\n    <member type='way' ref='251662718' role='' />\n    <member type='way' ref='251662720' role='' />\n    <member type='way' ref='127737049' role='' />\n    <member type='way' ref='127734698' role='' />\n    <member type='way' ref='89610208' role='' />\n    <member type='way' ref='329957369' role='' />\n    <member type='way' ref='89610181' role='' />\n    <member type='way' ref='329957368' role='' />\n    <member type='way' ref='89610055' role='' />\n    <member type='way' ref='132509601' role='' />\n    <member type='way' ref='132509604' role='' />\n    <member type='way' ref='132509602' role='' />\n    <member type='way' ref='132509597' role='' />\n    <member type='way' ref='626106907' role='' />\n    <member type='way' ref='151325251' role='' />\n    <member type='way' ref='481009514' role='' />\n    <member type='way' ref='481009534' role='' />\n    <member type='way' ref='481009535' role='' />\n    <member type='way' ref='481009519' role='' />\n    <member type='way' ref='481009523' role='' />\n    <member type='way' ref='152191983' role='' />\n    <member type='way' ref='88759723' role='' />\n    <member type='way' ref='696886210' role='' />\n    <member type='way' ref='88759725' role='' />\n    <member type='way' ref='421285149' role='' />\n    <member type='way' ref='154480459' role='' />\n    <member type='way' ref='108193927' role='' />\n    <member type='way' ref='85965180' role='' />\n    <member type='way' ref='87451168' role='' />\n    <member type='way' ref='108193927' role='' />\n    <member type='way' ref='154480459' role='' />\n    <member type='way' ref='154480462' role='' />\n    <member type='way' ref='764243524' role='' />\n    <member type='way' ref='33891641' role='' />\n    <member type='way' ref='85965208' role='' />\n    <tag k='name' v='Тролейбус №31' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='31' />\n    <tag k='route' v='trolleybus' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='3425722' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='17' changeset='82481698'>\n    <member type='node' ref='1457514517' role='stop' />\n    <member type='node' ref='7083077595' role='stop' />\n    <member type='node' ref='7083077594' role='stop' />\n    <member type='node' ref='1821309956' role='stop' />\n    <member type='node' ref='7073941394' role='platform' />\n    <member type='node' ref='2191392189' role='stop' />\n    <member type='node' ref='1457299727' role='stop' />\n    <member type='node' ref='1457299668' role='stop' />\n    <member type='node' ref='1457299708' role='stop' />\n    <member type='node' ref='1240897121' role='stop' />\n    <member type='node' ref='2578454054' role='platform' />\n    <member type='node' ref='2578454061' role='platform' />\n    <member type='node' ref='2578454064' role='platform' />\n    <member type='node' ref='2578454053' role='platform' />\n    <member type='node' ref='2578454079' role='platform' />\n    <member type='node' ref='2578454057' role='platform' />\n    <member type='node' ref='2578454074' role='platform' />\n    <member type='node' ref='2578454058' role='platform' />\n    <member type='node' ref='2391558034' role='platform' />\n    <member type='node' ref='2150346085' role='platform' />\n    <member type='node' ref='2150346094' role='platform' />\n    <member type='node' ref='2150346097' role='platform' />\n    <member type='node' ref='395286068' role='platform' />\n    <member type='way' ref='255803308' role='' />\n    <member type='way' ref='329957377' role='' />\n    <member type='way' ref='208884481' role='' />\n    <member type='way' ref='651969736' role='' />\n    <member type='way' ref='730783904' role='' />\n    <member type='way' ref='732681513' role='' />\n    <member type='way' ref='91323401' role='' />\n    <member type='way' ref='23788523' role='' />\n    <member type='way' ref='132509608' role='' />\n    <member type='way' ref='203344302' role='' />\n    <member type='way' ref='203344300' role='' />\n    <member type='way' ref='87451168' role='' />\n    <member type='way' ref='85965180' role='' />\n    <member type='way' ref='108193927' role='' />\n    <member type='way' ref='154480459' role='' />\n    <member type='way' ref='421285149' role='' />\n    <member type='way' ref='88759725' role='' />\n    <member type='way' ref='696886210' role='' />\n    <member type='way' ref='88759723' role='' />\n    <member type='way' ref='152191983' role='' />\n    <member type='way' ref='481009523' role='' />\n    <member type='way' ref='481009519' role='' />\n    <member type='way' ref='481009535' role='' />\n    <member type='way' ref='481009534' role='' />\n    <member type='way' ref='481009514' role='' />\n    <member type='way' ref='151325251' role='' />\n    <member type='way' ref='626106907' role='' />\n    <member type='way' ref='132509597' role='' />\n    <member type='way' ref='132509602' role='' />\n    <member type='way' ref='132509604' role='' />\n    <member type='way' ref='132509601' role='' />\n    <member type='way' ref='89610055' role='' />\n    <member type='way' ref='89610181' role='' />\n    <member type='way' ref='329957368' role='' />\n    <member type='way' ref='127734698' role='' />\n    <member type='way' ref='127737049' role='' />\n    <member type='way' ref='251662720' role='' />\n    <member type='way' ref='251662718' role='' />\n    <tag k='from' v='Московський проспект' />\n    <tag k='name' v='Автобус 294e: Московський проспект - Наталії Ужвій' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='294е' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Наталії Ужвій вулиця' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='4622830' timestamp='2020-03-22T12:23:20Z' uid='1730741' user='Sergey Harini' visible='true' version='19' changeset='82481698'>\n    <member type='node' ref='7083077592' role='stop' />\n    <member type='node' ref='1457514517' role='stop' />\n    <member type='node' ref='1821309956' role='stop' />\n    <member type='node' ref='7073941394' role='platform' />\n    <member type='node' ref='2093960049' role='platform' />\n    <member type='node' ref='1457299695' role='stop' />\n    <member type='node' ref='1457299743' role='platform' />\n    <member type='node' ref='3369072488' role='stop' />\n    <member type='node' ref='3369072482' role='stop' />\n    <member type='node' ref='3369072484' role='stop' />\n    <member type='node' ref='3369072487' role='stop' />\n    <member type='node' ref='3369072481' role='stop' />\n    <member type='node' ref='3369072491' role='stop' />\n    <member type='node' ref='7151570522' role='platform' />\n    <member type='node' ref='3369072489' role='stop' />\n    <member type='node' ref='2191392189' role='stop' />\n    <member type='node' ref='1457299727' role='stop' />\n    <member type='node' ref='1457299668' role='stop' />\n    <member type='node' ref='1457299708' role='stop' />\n    <member type='node' ref='1240897121' role='stop' />\n    <member type='node' ref='2578454054' role='platform' />\n    <member type='node' ref='2578454061' role='platform' />\n    <member type='node' ref='2578454064' role='platform' />\n    <member type='node' ref='2578454053' role='platform' />\n    <member type='node' ref='2578454079' role='platform' />\n    <member type='node' ref='2578454074' role='platform' />\n    <member type='node' ref='2578454058' role='platform' />\n    <member type='node' ref='2391558034' role='platform' />\n    <member type='node' ref='1932246587' role='platform' />\n    <member type='node' ref='2367194876' role='stop' />\n    <member type='node' ref='395286068' role='platform' />\n    <member type='way' ref='329957378' role='' />\n    <member type='way' ref='132507965' role='' />\n    <member type='way' ref='355618695' role='' />\n    <member type='way' ref='329957372' role='' />\n    <member type='way' ref='329957371' role='' />\n    <member type='way' ref='503738318' role='' />\n    <member type='way' ref='503738317' role='' />\n    <member type='way' ref='131620049' role='' />\n    <member type='way' ref='329957375' role='' />\n    <member type='way' ref='108194335' role='' />\n    <member type='way' ref='255803308' role='' />\n    <member type='way' ref='329957377' role='' />\n    <member type='way' ref='208884481' role='' />\n    <member type='way' ref='651969736' role='' />\n    <member type='way' ref='730783904' role='' />\n    <member type='way' ref='732681513' role='' />\n    <member type='way' ref='91323401' role='' />\n    <member type='way' ref='23788523' role='' />\n    <member type='way' ref='132509608' role='' />\n    <member type='way' ref='203344302' role='' />\n    <member type='way' ref='203344300' role='' />\n    <member type='way' ref='87451168' role='' />\n    <member type='way' ref='85965180' role='' />\n    <member type='way' ref='108193927' role='' />\n    <member type='way' ref='154480459' role='' />\n    <member type='way' ref='421285149' role='' />\n    <member type='way' ref='88759725' role='' />\n    <member type='way' ref='696886210' role='' />\n    <member type='way' ref='88759723' role='' />\n    <member type='way' ref='152191983' role='' />\n    <member type='way' ref='481009523' role='' />\n    <member type='way' ref='481009519' role='' />\n    <member type='way' ref='481009535' role='' />\n    <member type='way' ref='481009534' role='' />\n    <member type='way' ref='481009514' role='' />\n    <member type='way' ref='151325251' role='' />\n    <member type='way' ref='626106907' role='' />\n    <member type='way' ref='132509597' role='' />\n    <member type='way' ref='132509602' role='' />\n    <member type='way' ref='132509604' role='' />\n    <member type='way' ref='132509601' role='' />\n    <member type='way' ref='89610055' role='' />\n    <member type='way' ref='89610181' role='' />\n    <member type='way' ref='329957374' role='' />\n    <member type='way' ref='89610079' role='' />\n    <tag k='from' v='Одеська вулиця' />\n    <tag k='name' v='Автобус 259е: Одеська - Дружби народів' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='259е' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Дружби народів вулиця' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='8846268' timestamp='2020-06-05T12:20:48Z' uid='9518695' user='taxxxi' visible='true' version='15' changeset='86244667'>\n    <member type='node' ref='7128002649' role='stop_entry_only' />\n    <member type='node' ref='7128002648' role='platform_entry_only' />\n    <member type='node' ref='7128002655' role='stop' />\n    <member type='node' ref='7128002654' role='platform' />\n    <member type='node' ref='7128002659' role='stop' />\n    <member type='node' ref='7128002658' role='platform' />\n    <member type='node' ref='7128002663' role='stop' />\n    <member type='node' ref='7128002662' role='platform' />\n    <member type='node' ref='7128002665' role='stop' />\n    <member type='node' ref='6004666893' role='platform' />\n    <member type='node' ref='7128002666' role='stop' />\n    <member type='node' ref='1328503603' role='platform' />\n    <member type='node' ref='7128002668' role='stop' />\n    <member type='node' ref='3384998170' role='platform' />\n    <member type='node' ref='3395844877' role='stop' />\n    <member type='node' ref='2278582703' role='platform' />\n    <member type='node' ref='3395794775' role='stop' />\n    <member type='node' ref='3395794791' role='platform' />\n    <member type='node' ref='7128002672' role='stop' />\n    <member type='node' ref='3369072490' role='platform' />\n    <member type='node' ref='7128002677' role='stop' />\n    <member type='node' ref='7128002676' role='platform' />\n    <member type='node' ref='7128002678' role='stop' />\n    <member type='node' ref='2094827387' role='platform' />\n    <member type='node' ref='5447777185' role='stop_exit_only' />\n    <member type='node' ref='7128002680' role='platform_exit_only' />\n    <member type='way' ref='732356860' role='' />\n    <member type='way' ref='29111755' role='' />\n    <member type='way' ref='762819054' role='' />\n    <member type='way' ref='637649314' role='' />\n    <member type='way' ref='783612027' role='' />\n    <member type='way' ref='651969738' role='' />\n    <member type='way' ref='208884482' role='' />\n    <member type='way' ref='108193928' role='' />\n    <member type='way' ref='108193925' role='' />\n    <member type='way' ref='329957376' role='' />\n    <member type='way' ref='783680571' role='' />\n    <member type='way' ref='783680573' role='' />\n    <member type='way' ref='210437422' role='' />\n    <member type='way' ref='217811836' role='' />\n    <member type='way' ref='646355729' role='' />\n    <member type='way' ref='217811835' role='' />\n    <member type='way' ref='217811833' role='' />\n    <member type='way' ref='762819052' role='' />\n    <member type='way' ref='762819055' role='' />\n    <member type='way' ref='24714709' role='' />\n    <member type='way' ref='813154283' role='' />\n    <member type='way' ref='813154282' role='' />\n    <member type='way' ref='762819050' role='' />\n    <tag k='from' v='Парк &quot;Зустріч&quot;' />\n    <tag k='name' v='Тролейбус №13 Парк &quot;Зустріч&quot; - ст. м. Захисникiв України' />\n    <tag k='operator' v='Тролейбусне депо №3' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='13' />\n    <tag k='route' v='trolleybus' />\n    <tag k='to' v='ст. м. Захисникiв України' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='10105729' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75422833'>\n    <member type='node' ref='6844719020' role='via' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10105731' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='75422833'>\n    <member type='node' ref='6844719020' role='via' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10128674' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833'>\n    <member type='way' ref='651964352' role='from' />\n    <member type='node' ref='6113014891' role='via' />\n    <member type='way' ref='732681511' role='to' />\n    <tag k='restriction' v='no_right_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10128675' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833'>\n    <member type='way' ref='651964354' role='from' />\n    <member type='node' ref='6113014891' role='via' />\n    <member type='way' ref='732681512' role='to' />\n    <tag k='restriction' v='no_left_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10128676' timestamp='2019-11-01T13:27:03Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='76496779'>\n    <member type='way' ref='730783904' role='from' />\n    <member type='node' ref='395286068' role='via' />\n    <member type='way' ref='91323578' role='to' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10128677' timestamp='2019-11-01T13:27:03Z' uid='421494' user='Anton Shevchuk' visible='true' version='2' changeset='76496779'>\n    <member type='way' ref='730783904' role='from' />\n    <member type='node' ref='395286068' role='via' />\n    <member type='way' ref='732681514' role='to' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10128678' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833'>\n    <member type='way' ref='651964355' role='from' />\n    <member type='node' ref='6860854797' role='via' />\n    <member type='way' ref='651964354' role='to' />\n    <tag k='restriction' v='no_left_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10128679' timestamp='2019-10-08T16:25:27Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75422833'>\n    <member type='way' ref='732681514' role='from' />\n    <member type='node' ref='6860854797' role='via' />\n    <member type='way' ref='732681519' role='to' />\n    <tag k='restriction' v='no_right_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10130579' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528'>\n    <member type='way' ref='91323401' role='from' />\n    <member type='node' ref='257750807' role='via' />\n    <member type='way' ref='91323401' role='to' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10130580' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528'>\n    <member type='way' ref='732681513' role='from' />\n    <member type='node' ref='257750807' role='via' />\n    <member type='way' ref='732681513' role='to' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10130581' timestamp='2019-10-09T07:40:26Z' uid='421494' user='Anton Shevchuk' visible='true' version='1' changeset='75443528'>\n    <member type='way' ref='732681513' role='from' />\n    <member type='node' ref='395286068' role='via' />\n    <member type='way' ref='732681513' role='to' />\n    <tag k='restriction' v='no_u_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n  <relation id='10581730' timestamp='2020-09-09T15:50:26Z' uid='9518695' user='taxxxi' visible='true' version='9' changeset='90655525'>\n    <member type='node' ref='2594228613' role='stop_entry_only' />\n    <member type='node' ref='2594228534' role='platform_entry_only' />\n    <member type='node' ref='7128002679' role='stop' />\n    <member type='node' ref='7128002674' role='stop' />\n    <member type='node' ref='7128002673' role='platform' />\n    <member type='node' ref='7128002671' role='stop' />\n    <member type='node' ref='3369072489' role='platform' />\n    <member type='node' ref='2191392189' role='stop' />\n    <member type='node' ref='7083685219' role='platform' />\n    <member type='node' ref='7128002670' role='stop' />\n    <member type='node' ref='3391446317' role='stop' />\n    <member type='node' ref='1330704422' role='platform' />\n    <member type='node' ref='7128002667' role='stop' />\n    <member type='node' ref='3391446303' role='platform' />\n    <member type='node' ref='7128002664' role='stop' />\n    <member type='node' ref='3827745728' role='platform' />\n    <member type='node' ref='7128002661' role='stop' />\n    <member type='node' ref='7128002660' role='platform' />\n    <member type='node' ref='7128002656' role='stop' />\n    <member type='node' ref='7128002657' role='platform' />\n    <member type='node' ref='7128002653' role='stop' />\n    <member type='node' ref='7128002652' role='platform' />\n    <member type='node' ref='7128002650' role='stop_exit_only' />\n    <member type='node' ref='7128002651' role='platform_exit_only' />\n    <member type='way' ref='762819049' role='' />\n    <member type='way' ref='762819057' role='' />\n    <member type='way' ref='813154282' role='' />\n    <member type='way' ref='813154283' role='' />\n    <member type='way' ref='24714709' role='' />\n    <member type='way' ref='762819056' role='' />\n    <member type='way' ref='637649317' role='' />\n    <member type='way' ref='845770200' role='' />\n    <member type='way' ref='255096297' role='' />\n    <member type='way' ref='845779416' role='' />\n    <member type='way' ref='502403426' role='' />\n    <member type='way' ref='845779417' role='' />\n    <member type='way' ref='845779418' role='' />\n    <member type='way' ref='34392084' role='' />\n    <member type='way' ref='217811834' role='' />\n    <member type='way' ref='783680574' role='' />\n    <member type='way' ref='329957375' role='' />\n    <member type='way' ref='108194335' role='' />\n    <member type='way' ref='255803308' role='' />\n    <member type='way' ref='329957377' role='' />\n    <member type='way' ref='208884481' role='' />\n    <member type='way' ref='651969736' role='' />\n    <member type='way' ref='651964355' role='' />\n    <member type='way' ref='732681519' role='' />\n    <member type='way' ref='651964356' role='' />\n    <member type='way' ref='783654876' role='' />\n    <member type='way' ref='783654878' role='' />\n    <member type='way' ref='29111755' role='' />\n    <member type='way' ref='732356860' role='' />\n    <member type='way' ref='762819051' role='' />\n    <tag k='from' v='ст. м. Захисникiв України' />\n    <tag k='name' v='Тролейбус №13 ст. м. Захисникiв України - Парк &quot;Зустріч&quot;' />\n    <tag k='operator' v='Тролейбусне депо №3' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='13' />\n    <tag k='route' v='trolleybus' />\n    <tag k='to' v='Парк &quot;Зустріч&quot;' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='10581731' timestamp='2020-01-15T09:26:26Z' uid='4464045' user='Anton Melnichuk' visible='true' version='2' changeset='79600235'>\n    <member type='relation' ref='10581730' role='' />\n    <member type='relation' ref='8846268' role='' />\n    <tag k='name' v='Тролейбус №13' />\n    <tag k='operator' v='operator=Тролейбусне депо №3' />\n    <tag k='ref' v='13' />\n    <tag k='route_master' v='trolleybus' />\n    <tag k='type' v='route_master' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/building_block_atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n239827929000000 && 52.346592,71.8723874:52.346584,71.8796899:52.3465303,71.8797567:52.3444064,71.8797609:52.3442191,71.8797695:52.3440986,71.8797946:52.3416541,71.8814599:52.3409671,71.8787783:52.3409054,71.878822:52.3386909,71.8803888:52.3378493,71.8771887:52.3389324,71.8764438:52.3392035,71.8762553:52.3394755,71.8760651:52.3400197,71.875701:52.3402505,71.8755576:52.3433362,71.8734473:52.3448185,71.872364 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1399187086000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || name -> 1-й микрорайон || landuse -> residential || place -> quarter\n250786481000000 && 52.346066,71.875656:52.3460655,71.8760318:52.346065,71.8764076:52.3459397,71.8764074:52.3459397,71.8764363:52.3457286,71.8764355:52.3457286,71.876444:52.3456566,71.8764437:52.3456569,71.8762796:52.3458863,71.8762804:52.3458869,71.8757842:52.3457253,71.8757836:52.3457253,71.8757994:52.3456552,71.8757991:52.3456554,71.8756375:52.3459497,71.8756385:52.3459497,71.8756558 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1386518512000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || building:part -> yes\n30736305000000 && 52.3456566,71.8764437:52.3456569,71.8762796:52.3458863,71.8762804:52.3458869,71.8757842:52.3457253,71.8757836:52.3457253,71.8757994:52.3456552,71.8757991:52.3456554,71.8756375:52.3459497,71.8756385:52.3459499,71.8754652:52.346231,71.8754662:52.3462308,71.8756566:52.346066,71.875656:52.3460655,71.8760318:52.346065,71.8764076:52.3462312,71.8764082:52.346231,71.8766225:52.3459394,71.8766215:52.3459397,71.8764363:52.3457286,71.8764355:52.3457286,71.876444 && last_edit_user_name -> Alexkravkaa28 || addr:housenumber -> 57 || addr:quarter -> 1-й микрорайон || last_edit_time -> 1419539871000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || addr:postcode -> 021501 || building -> school || addr:place -> 1-й микрорайон\n240444646000000 && 52.3464608,71.8753975:52.3464658,71.8766449:52.3454493,71.8766558:52.3454443,71.8754084 && last_edit_user_name -> Alexkravkaa28 || official_name -> Государственное учреждение «Средняя школа №1 имени Н. Островского» отдела образования города Степногорска || website -> http://sc0001.stepnogorsk.akmoedu.kz || last_edit_time -> 1419539872000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || amenity -> school || name -> Средняя школа №1\n250786489000000 && 52.3462312,71.8764082:52.346065,71.8764076:52.3459397,71.8764074:52.3459397,71.8764363:52.3459394,71.8766215:52.346231,71.8766225 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1386518513000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || building:part -> yes\n250786488000000 && 52.346231,71.8754662:52.3459499,71.8754652:52.3459497,71.8756385:52.3459497,71.8756558:52.346066,71.875656:52.3462308,71.8756566 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1386518513000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || building:part -> yes\n# Points\n2571571410000000 && 52.3460655,71.8760318 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1398623371000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || entrance -> main\n# Relations\n3367576000000 && 30736305000000 -> outline -> A || 250786481000000 -> part -> A || 250786488000000 -> part -> A || 250786489000000 -> part -> A || 2571571410000000 -> entrance -> P && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1386518513000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || building:part -> yes || type -> building\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/building_with_levels.txt",
    "content": "# Areas\n100000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || building:min_level -> 0 || building:levels -> 0\n200000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || building:min_level -> -3 || building:levels -> -1\n300000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || building:min_level -> ab || building:levels -> dc\n400000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || building:min_level -> 10 || building:levels -> 20\n500000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || building:levels -> 5\n600000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || min_level -> 5"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/buildings/building_with_minheights.txt",
    "content": "# Areas\n100000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || building:levels -> 57 || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || min_height -> 0\n200000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || building:levels -> 57 || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || min_height -> -3\n300000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || building:levels -> 57 || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || min_height -> 193.4 || last_edit_time -> 1511322875000 || last_edit_user_id -> 6172634 || building:min_level -> 55 || name -> SkyPark || building:colour -> #c0bab2 || last_edit_version -> 25 || height -> 207 || building:material -> metal\n400000000 && 1.2892346,103.8563532:1.289232,103.8563748:1.2897006,103.856396:1.2897383,103.8563981:1.2897951,103.8564013:1.2897979,103.8564262:1.2898683,103.8564188:1.289964,103.8563899:1.2900244,103.8563511:1.2900692,103.8563087:1.2900885,103.8562842:1.2901344,103.8562259:1.2901713,103.8561394:1.29014,103.8561231:1.2901534,103.8560876:1.2901727,103.8560365:1.2901722,103.8559812:1.2903079,103.855937:1.2902671,103.8558132:1.2899639,103.8558897:1.2899067,103.8558885:1.2898647,103.8558706:1.2898379,103.8558452:1.2897819,103.8557637:1.289745,103.8556784:1.2897246,103.8555778:1.2897348,103.8554989:1.2897705,103.8554428:1.2898163,103.8554174:1.2902159,103.8552459:1.2901515,103.8551766:1.2900765,103.8551064:1.2899725,103.8550396:1.2899269,103.8550179:1.2898587,103.8549855:1.2897439,103.8549508:1.2895978,103.8549423:1.2897017,103.8553728:1.289703,103.8554416:1.2896775,103.8554899:1.2896101,103.8555409:1.2895375,103.8555778:1.2894217,103.8556135:1.289358,103.8556147:1.2892868,103.8555905:1.2892422,103.8555511:1.2891361,103.8553284:1.2890926,103.8553572:1.2890484,103.8554101:1.2890063,103.8555058:1.2890081,103.8556176:1.2890579,103.8557222:1.289115,103.8557741:1.2891925,103.8558137:1.2892725,103.8558227:1.2892703,103.8559289:1.2893378,103.8559742:1.2893643,103.8559486:1.2894006,103.8559324:1.2894483,103.855934:1.2894787,103.8559425:1.2895237,103.8559789:1.2895521,103.8560414:1.2895516,103.8560659:1.2895414,103.8561042:1.2895253,103.8561339:1.2895011,103.8561579:1.289468,103.8561708:1.2894352,103.856178:1.2894012,103.8561733:1.2893653,103.8561636:1.2893305,103.8561204:1.2892574,103.8561631 && last_edit_changeset -> 54116444 || website -> http://www.esplanade.com/ || iso_country_code -> SGP || amenity -> theatre || addr:country -> SG || tourism -> attraction || roof:colour -> #ad9585 || building:levels -> 4 || addr:postcode -> 039802 || building -> yes || addr:city -> Singapore || last_edit_user_name -> Ant Ko || name:ko -> 에스플라네이드 극장 || addr:housenumber -> 8 || last_edit_time -> 1511774634000 || last_edit_user_id -> 6750990 || name -> Esplanade Theaters || wikipedia -> en:Esplanade – Theatres on the Bay || addr:street -> Raffles Avenue || building:colour -> #ad9585 || last_edit_version -> 22 || wikidata -> Q559113 || height -> 13 || building:material -> brick\n500000000 && 1.2849,103.8609721:1.285036,103.8610173:1.2850909,103.8610442:1.2851351,103.8610808:1.2851541,103.8611099:1.2851574,103.8611359:1.2851505,103.8611632:1.2851262,103.8611939:1.2850935,103.8612164:1.2850442,103.8612318:1.2849657,103.8612425:1.284884,103.8612465:1.284856,103.8612473:1.2846658,103.8612302:1.2844555,103.8611969:1.284261,103.8611461:1.2840541,103.8610867:1.2838001,103.8609999:1.2835139,103.8608748:1.2832315,103.8607372:1.2829515,103.8605736:1.2826851,103.8604051:1.2822886,103.8600916:1.2823283,103.859959:1.2824039,103.8598747:1.2825055,103.8598239:1.282688,103.8599698:1.2829848,103.8601815:1.2832159,103.8603308:1.2835257,103.860497:1.2838056,103.8606228:1.2839598,103.8606844:1.2841248,103.860746:1.2844417,103.860846:1.2847022,103.8609147 && last_edit_changeset -> 53990520 || iso_country_code -> SGP || building:levels -> 57 || layer -> 2 || building -> yes || last_edit_user_name -> geoJenn || name:ko -> 스카이파크 || min_height -> ab"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/restriction/atlasBrokenTurnRestrictionRoute.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-53448' action='modify' visible='true' lat='34.62756192874' lon='18.16333885978' />\n  <node id='-53450' action='modify' visible='true' lat='34.62784735183' lon='18.16365936012' />\n  <node id='-53452' action='modify' visible='true' lat='34.62764199447' lon='18.16434643406'>\n    <tag k='highway' v='traffic_signals' />\n  </node>\n  <node id='-53454' action='modify' visible='true' lat='34.62837215166' lon='18.16493183339' />\n  <node id='-53456' action='modify' visible='true' lat='34.62822003611' lon='18.16460524219' />\n  <way id='-53458' action='modify' visible='true'>\n    <nd ref='-53448' />\n    <nd ref='-53450' />\n    <nd ref='-53452' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-53460' action='modify' visible='true'>\n    <nd ref='-53452' />\n    <nd ref='-53456' />\n    <nd ref='-53454' />\n    <tag k='highway' v='secondary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-53462' action='modify' visible='true'>\n    <member type='way' ref='-53460' role='via' />\n    <member type='way' ref='-53460' role='to' />\n    <member type='way' ref='-53458' role='from' />\n    <tag k='restriction' v='no_left_turn' />\n    <tag k='type' v='restriction' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/canalAsRelation.atlas.txt",
    "content": "# Nodes\n3558267680000000 && -27.4856442,153.2158669 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987007000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n3558267681000000 && -27.4855209,153.2163 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987007000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n# Edges\n-350079841000000 && -27.4856442,153.2158669:-27.4855209,153.2163 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987009000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || name -> Chart Street || bridge -> yes || history -> Retrieved from v2 || highway -> residential || last_edit_version -> 1\n350079841000000 && -27.4855209,153.2163:-27.4856442,153.2158669 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987009000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || name -> Chart Street || bridge -> yes || history -> Retrieved from v2 || highway -> residential || last_edit_version -> 1\n# Areas\n# Lines\n350079828000000 && -27.4829379,153.218824:-27.483696,153.2190927 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987009000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n350079829000000 && -27.483696,153.2190927:-27.4836307,153.2189526:-27.4836399,153.2187836:-27.484336,153.2163082:-27.484451,153.2161529:-27.4846126,153.2160572:-27.4847887,153.2160385:-27.4855209,153.2163 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987009000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n350079838000000 && -27.4856442,153.2158669:-27.4855436,153.2158315 && last_edit_user_name -> dle0 || last_edit_changeset -> 41287685 || last_edit_time -> 1470490849000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 3\n350079856000000 && -27.4841233,153.2151128:-27.4841092,153.2151691 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987010000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n350079857000000 && -27.4839373,153.216214:-27.4833703,153.2182306 && last_edit_user_name -> dle0 || last_edit_changeset -> 41284146 || last_edit_time -> 1470476070000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 2\n350079859000000 && -27.4838572,153.2160631:-27.4838348,153.2161396:-27.4839402,153.2162044:-27.4839373,153.216214 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987010000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n350079861000000 && -27.4841092,153.2151691:-27.4840751,153.2152901:-27.484011,153.2155174:-27.4839548,153.2157168:-27.483892,153.2159398:-27.4838572,153.2160631 && last_edit_user_name -> Warin61 || last_edit_changeset -> 47437473 || last_edit_time -> 1491299751000 || last_edit_user_id -> 1830192 || iso_country_code -> AUS || last_edit_version -> 3\n350087652000000 && -27.4846364,153.2152458:-27.4841983,153.215087:-27.4841233,153.2151128 && last_edit_user_name -> dle0 || last_edit_changeset -> 31583075 || last_edit_time -> 1432987933000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n435923979000000 && -27.4833703,153.2182306:-27.4832592,153.2186256:-27.4831842,153.218757:-27.4830581,153.2188281:-27.4829379,153.218824 && last_edit_user_name -> dle0 || last_edit_changeset -> 41284146 || last_edit_time -> 1470476069000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n435952704000000 && -27.4855436,153.2158315:-27.484711,153.2155388:-27.4846737,153.2155119:-27.484654,153.2154816:-27.4846468,153.2154431:-27.4846364,153.2152458 && last_edit_user_name -> dle0 || last_edit_changeset -> 41287685 || last_edit_time -> 1470490844000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || last_edit_version -> 1\n# Points\n# Relations\n5218860000000 && -350079841000000 -> outer -> E || 350079841000000 -> outer -> E || 350079828000000 -> outer -> L || 350079829000000 -> outer -> L || 350079838000000 -> outer -> L || 350079856000000 -> outer -> L || 350079857000000 -> outer -> L || 350079859000000 -> outer -> L || 350079861000000 -> outer -> L || 350087652000000 -> outer -> L || 435923979000000 -> outer -> L || 435952704000000 -> outer -> L && last_edit_user_name -> dle0 || last_edit_changeset -> 41287685 || natural -> water || last_edit_time -> 1470490850000 || last_edit_user_id -> 37744 || iso_country_code -> AUS || name -> John P Goleby Canal || source -> CC-BY-3.0-AU ©State of Queensland (Department of Natural Resources and Mines) 2015 || type -> multipolygon || last_edit_version -> 4 || water -> canal && MULTIPOLYGON (((153.2160631 -27.4838572, 153.2161396 -27.4838348, 153.2162044 -27.4839402, 153.216214 -27.4839373, 153.2182306 -27.4833703, 153.2186256 -27.4832592, 153.218757 -27.4831842, 153.2188281 -27.4830581, 153.218824 -27.4829379, 153.2190927 -27.483696, 153.2189526 -27.4836307, 153.2187836 -27.4836399, 153.2163082 -27.484336, 153.2161529 -27.484451, 153.2160572 -27.4846126, 153.2160385 -27.4847887, 153.2163 -27.4855209, 153.2158669 -27.4856442, 153.2158315 -27.4855436, 153.2155388 -27.484711, 153.2155119 -27.4846737, 153.2154816 -27.484654, 153.2154431 -27.4846468, 153.2152458 -27.4846364, 153.215087 -27.4841983, 153.2151128 -27.4841233, 153.2151691 -27.4841092, 153.2152901 -27.4840751, 153.2155174 -27.484011, 153.2157168 -27.4839548, 153.2159398 -27.483892, 153.2160631 -27.4838572)))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/canalAsRelationOfCanals.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n115428995000000 && -32.8248933,-60.8221442:-32.8219425,-60.8149965:-32.8213439,-60.8131992:-32.8182964,-60.8029425:-32.8170611,-60.8001208:-32.813779,-60.7910769:-32.812183,-60.787987:-32.8112632,-60.7846718:-32.8112362,-60.7842856:-32.8113174,-60.7838671:-32.8114436,-60.7833951:-32.8133642,-60.7805197:-32.8181296,-60.7733894:-32.8182093,-60.7732404:-32.8182319,-60.7730742:-32.8182277,-60.7728961 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002217000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 8 || boat -> no\n161226868000000 && -32.8310004,-60.7506797:-32.8311365,-60.7506731 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002220000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 3 || tunnel -> culvert\n161226869000000 && -32.8240348,-60.7542528:-32.8241766,-60.7541254 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002218000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 3 || tunnel -> culvert\n161226870000000 && -32.8241766,-60.7541254:-32.8281055,-60.7509406:-32.8282456,-60.7508472:-32.82845,-60.7508179:-32.8310004,-60.7506797 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002223000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 3 || boat -> no\n161226871000000 && -32.8311365,-60.7506731:-32.8378779,-60.7503598:-32.8380343,-60.7503884:-32.8381666,-60.7504617:-32.8396939,-60.7515973:-32.8406275,-60.7521835:-32.8414369,-60.752646 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002224000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 8 || boat -> no\n252891024000000 && -32.8421837,-60.7526097:-32.8448562,-60.7524586:-32.8467972,-60.7523285 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002222000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 5 || boat -> no\n262706975000000 && -32.878501,-60.7411358:-32.8786758,-60.7410616:-32.879608,-60.7406728:-32.8798699,-60.7406168:-32.8801558,-60.7406476:-32.8806865,-60.7410938 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002223000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 7 || boat -> no\n262706977000000 && -32.8695138,-60.7455178:-32.8701499,-60.7453645:-32.8710562,-60.7452824 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002222000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 4 || boat -> no\n275348124000000 && -32.8296254,-60.8313015:-32.8300373,-60.8312097:-32.830564,-60.8311873:-32.8316846,-60.8307727:-32.8338584,-60.8300219:-32.8363237,-60.8292151:-32.8372379,-60.8289396:-32.8380223,-60.8289168:-32.839042,-60.8288832:-32.8400001,-60.8288047:-32.840273,-60.8286809:-32.8407595,-60.828872:-32.8413097,-60.8290573:-32.8419237,-60.8289994:-32.8426419,-60.8288257:-32.8440088,-60.8285882:-32.8450919,-60.8284028:-32.8460244,-60.8281712:-32.8465398,-60.8278352:-32.8471177,-60.8273974 && last_edit_user_name -> mweper || last_edit_changeset -> 38939494 || motorboat -> no || last_edit_time -> 1461808324000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 3 || boat -> no\n275348126000000 && -32.8329036,-60.8353465:-32.8331053,-60.8354361:-32.8345732,-60.8370385 && last_edit_user_name -> mweper || last_edit_changeset -> 38939494 || motorboat -> no || last_edit_time -> 1461808325000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 3 || boat -> no\n275348127000000 && -32.8330492,-60.8334303:-32.8329036,-60.8353465:-32.8321864,-60.8387754 && last_edit_user_name -> mweper || last_edit_changeset -> 38939494 || motorboat -> no || last_edit_time -> 1461808325000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 3 || boat -> no\n294751739000000 && -32.8522763,-60.7520007:-32.8524969,-60.7518904:-32.852722,-60.7516491:-32.8563106,-60.7483761:-32.8591404,-60.7463501:-32.8592671,-60.746286:-32.859569,-60.7462801 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002224000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 3 || boat -> no\n301675909000000 && -32.8839772,-60.7403962:-32.8846852,-60.7398579:-32.8858629,-60.7389625:-32.8871727,-60.7379437 && last_edit_user_name -> mweper || last_edit_changeset -> 41470438 || motorboat -> no || last_edit_time -> 1471276379000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 5 || boat -> no\n301675912000000 && -32.8905995,-60.735299:-32.8943106,-60.7324339:-32.8947088,-60.7321243 && last_edit_user_name -> mweper || last_edit_changeset -> 41470438 || motorboat -> no || last_edit_time -> 1471276379000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 6 || boat -> no\n301675914000000 && -32.8947088,-60.7321243:-32.894854,-60.7320052:-32.8949807,-60.7318766:-32.8951521,-60.731624:-32.8954925,-60.7310614:-32.895811,-60.7303952:-32.8960861,-60.7298652:-32.8964395,-60.7292974:-32.8967776,-60.7289171 && last_edit_user_name -> muralito || last_edit_changeset -> 40698699 || motorboat -> no || last_edit_time -> 1468357527000 || last_edit_user_id -> 755114 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 6 || boat -> no\n301675916000000 && -32.8967776,-60.7289171:-32.8970201,-60.7286673:-32.8974495,-60.7284453:-32.8979178,-60.728288:-32.8997496,-60.7282059:-32.900236,-60.7280986:-32.9007483,-60.727814:-32.901024,-60.7275327:-32.9012635,-60.727191:-32.901477,-60.7267155 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002216000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 3 || boat -> no\n395060798000000 && -32.8414369,-60.752646:-32.8421128,-60.7526129 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002220000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 3 || boat -> no\n395060799000000 && -32.8421128,-60.7526129:-32.8421837,-60.7526097 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002218000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 3 || tunnel -> culvert\n398917481000000 && -32.8367359,-60.8353465:-32.836254,-60.8342931:-32.8360187,-60.8341699:-32.8339905,-60.8339906:-32.8330492,-60.8334303:-32.8302604,-60.8317065:-32.8296254,-60.8313015:-32.8286286,-60.8306658:-32.8281688,-60.8305263:-32.8278551,-60.8302613 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002216000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917483000000 && -32.8597415,-60.7462768:-32.8618712,-60.7462706 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002218000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917485000000 && -32.8620268,-60.7462624:-32.8664888,-60.7456994 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002217000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917500000000 && -32.8728286,-60.7451169:-32.8729983,-60.7450956 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002219000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917502000000 && -32.8666285,-60.7456818:-32.8695138,-60.7455178 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002219000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917503000000 && -32.8808227,-60.7412107:-32.8809398,-60.7413112 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002222000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917504000000 && -32.8783262,-60.7412099:-32.878501,-60.7411358 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002219000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917505000000 && -32.8182277,-60.7728961:-32.8181986,-60.7726157 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002217000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917507000000 && -32.8711169,-60.7452769:-32.8711769,-60.7452708 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002221000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917508000000 && -32.8806865,-60.7410938:-32.8808227,-60.7412107 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002217000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917512000000 && -32.8521112,-60.7520421:-32.8522763,-60.7520007 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002220000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917513000000 && -32.8664888,-60.7456994:-32.8666285,-60.7456818 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002218000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917514000000 && -32.859569,-60.7462801:-32.8597415,-60.7462768 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002219000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917516000000 && -32.8711769,-60.7452708:-32.8728286,-60.7451169 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002218000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917518000000 && -32.8618712,-60.7462706:-32.8620268,-60.7462624 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002221000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917519000000 && -32.8873258,-60.7378246:-32.8904086,-60.7354463 && last_edit_user_name -> mweper || last_edit_changeset -> 41470438 || motorboat -> no || last_edit_time -> 1471276379000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 4 || boat -> no\n398917520000000 && -32.8250327,-60.8224819:-32.8248933,-60.8221442 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002221000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917521000000 && -32.8710562,-60.7452824:-32.8711169,-60.7452769 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002219000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917523000000 && -32.8904086,-60.7354463:-32.8905995,-60.735299 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002217000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917525000000 && -32.8278551,-60.8302613:-32.8274622,-60.8299296 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002221000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917526000000 && -32.8809398,-60.7413112:-32.8810267,-60.7413859 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002217000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917527000000 && -32.8220298,-60.7580874:-32.8220954,-60.7578275 && last_edit_changeset -> 37335768 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1456002220000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 2 || tunnel -> culvert\n398917529000000 && -32.8467972,-60.7523285:-32.8519667,-60.752063:-32.8521112,-60.7520421 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002222000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917530000000 && -32.8220954,-60.7578275:-32.8232731,-60.7549028:-32.8234114,-60.7547428:-32.8240348,-60.7542528 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002223000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917531000000 && -32.8810267,-60.7413859:-32.8813069,-60.7416327:-32.881682,-60.7417938:-32.8820174,-60.7418279:-32.8823355,-60.7416991:-32.8839772,-60.7403962 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002223000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917532000000 && -32.8274622,-60.8299296:-32.8271324,-60.829651:-32.8267714,-60.8293461:-32.8264829,-60.8290672:-32.8261854,-60.8284342:-32.8251593,-60.8228476:-32.8250327,-60.8224819 && last_edit_user_name -> mweper || last_edit_changeset -> 38939494 || motorboat -> no || last_edit_time -> 1461808325000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 3 || boat -> no\n398917534000000 && -32.8729983,-60.7450956:-32.873183,-60.7450832:-32.8734245,-60.745047:-32.8736538,-60.744885:-32.8759071,-60.7423018:-32.8761076,-60.7421514:-32.8783262,-60.7412099 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002224000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n398917535000000 && -32.8181986,-60.7726157:-32.8181952,-60.7724377:-32.8182431,-60.7722331:-32.8189005,-60.7702844:-32.8193332,-60.7675593:-32.8220492,-60.7585398:-32.8220651,-60.7583861:-32.822034,-60.7582345:-32.8220298,-60.7580874 && last_edit_user_name -> mweper || last_edit_changeset -> 37335768 || motorboat -> no || last_edit_time -> 1456002224000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || canoe -> no || waterway -> canal || name -> Canal Ibarlucea || ship -> no || last_edit_version -> 2 || boat -> no\n437322183000000 && -32.8871727,-60.7379437:-32.8873258,-60.7378246 && last_edit_changeset -> 41470438 || motorboat -> no || iso_country_code -> ARG || canoe -> no || waterway -> canal || ship -> no || boat -> no || layer -> -1 || last_edit_user_name -> mweper || last_edit_time -> 1471276358000 || last_edit_user_id -> 1311281 || name -> Canal Ibarlucea || last_edit_version -> 1 || tunnel -> culvert\n# Points\n# Relations\n6006326000000 && 115428995000000 -> main_stream -> L || 161226868000000 -> main_stream -> L || 161226869000000 -> main_stream -> L || 161226870000000 -> main_stream -> L || 161226871000000 -> main_stream -> L || 252891024000000 -> main_stream -> L || 262706975000000 -> main_stream -> L || 262706977000000 -> main_stream -> L || 275348124000000 -> main_stream -> L || 275348126000000 -> main_stream -> L || 275348127000000 -> main_stream -> L || 294751739000000 -> main_stream -> L || 301675909000000 -> main_stream -> L || 301675912000000 -> main_stream -> L || 301675914000000 -> main_stream -> L || 301675916000000 -> main_stream -> L || 395060798000000 -> main_stream -> L || 395060799000000 -> main_stream -> L || 398917481000000 -> main_stream -> L || 398917483000000 -> main_stream -> L || 398917485000000 -> main_stream -> L || 398917500000000 -> main_stream -> L || 398917502000000 -> main_stream -> L || 398917503000000 -> main_stream -> L || 398917504000000 -> main_stream -> L || 398917505000000 -> main_stream -> L || 398917507000000 -> main_stream -> L || 398917508000000 -> main_stream -> L || 398917512000000 -> main_stream -> L || 398917513000000 -> main_stream -> L || 398917514000000 -> main_stream -> L || 398917516000000 -> main_stream -> L || 398917518000000 -> main_stream -> L || 398917519000000 -> main_stream -> L || 398917520000000 -> main_stream -> L || 398917521000000 -> main_stream -> L || 398917523000000 -> main_stream -> L || 398917525000000 -> main_stream -> L || 398917526000000 -> main_stream -> L || 398917527000000 -> main_stream -> L || 398917529000000 -> main_stream -> L || 398917530000000 -> main_stream -> L || 398917531000000 -> main_stream -> L || 398917532000000 -> main_stream -> L || 398917534000000 -> main_stream -> L || 398917535000000 -> main_stream -> L || 437322183000000 -> main_stream -> L && last_edit_user_name -> mweper || last_edit_changeset -> 41470438 || last_edit_time -> 1471276375000 || last_edit_user_id -> 1311281 || iso_country_code -> ARG || waterway -> canal || name -> Canal Ibarlucea || destination -> Arroyo Ludueña || type -> waterway || last_edit_version -> 10\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/harborAsArea.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n247059838000000 && -32.3049143,-58.0949621:-32.3048917,-58.0948583:-32.3043921,-58.0946523:-32.3043758,-58.0945195:-32.3044653,-58.0941653:-32.3047241,-58.0936031:-32.3055767,-58.0939227:-32.3056157,-58.094019:-32.3055474,-58.0948391:-32.305523,-58.0948968:-32.3054628,-58.0948949:-32.3054481,-58.0950027:-32.3051243,-58.0949642:-32.305077,-58.0950064 && harbour -> yes || last_edit_changeset -> 20186822 || website -> http://www.ycp.org.uy || natural -> water || iso_country_code -> URY || seamark:type -> harbour || last_edit_user_name -> muralito || last_edit_time -> 1390609954000 || last_edit_user_id -> 755114 || name -> Yatch Club Paysandú || last_edit_version -> 4 || leisure -> marina || seamark:harbour:category -> marina\n# Lines\n# Points\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/complex/water/harborAsRelation.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n473739894000000 && 21.6027403,112.5483745:21.5995149,112.5423921 && last_edit_user_name -> ff5722 || last_edit_changeset -> 45968126 || last_edit_time -> 1486718248000 || last_edit_user_id -> 3450290 || iso_country_code -> CHN || last_edit_version -> 1\n473739895000000 && 21.5995149,112.5423921:21.5995276,112.5430221:21.5994654,112.5435096:21.5994075,112.5439194:21.5993408,112.5440884:21.5991062,112.5443683:21.5987951,112.5447429:21.5985958,112.5450146:21.5982452,112.5454403:21.5980009,112.5456896:21.597645,112.546053:21.5972944,112.5463828:21.5970749,112.5466701:21.5966615,112.5473723:21.5964787,112.5477918:21.5964588,112.5481722:21.596309,112.5491521:21.5964012,112.5494624:21.5963848,112.5496262:21.5963791,112.549683:21.5963757,112.5497178:21.5964665,112.5498097:21.5963677,112.5499475:21.5962956,112.5498901:21.5961938,112.5499571:21.5959966,112.5508004:21.5960959,112.5509501:21.5961538,112.5510372:21.5957895,112.5512744:21.5958295,112.55134:21.5958936,112.5513038:21.5959657,112.5514244:21.5961875,112.5516541:21.5963953,112.5519236:21.5965349,112.5520989:21.5968633,112.5522329:21.5972613,112.5522296:21.5975613,112.5521714:21.5993743,112.5516892:21.5995502,112.5514777:21.5996528,112.5512965:21.5997046,112.5512081:21.5998612,112.5509308:21.5999167,112.5508363:21.6000965,112.550518:21.6001496,112.5504274:21.6004075,112.5499738:21.600815,112.5497067:21.6007696,112.549612:21.6009112,112.5495172:21.6009951,112.5494712:21.6010446,112.5495517:21.6027403,112.5483745 && last_edit_user_name -> ff5722 || last_edit_changeset -> 45968126 || last_edit_time -> 1486718248000 || last_edit_user_id -> 3450290 || iso_country_code -> CHN || last_edit_version -> 1\n# Points\n# Relations\n6965420000000 && 473739894000000 -> outer -> L || 473739895000000 -> outer -> L && last_edit_user_name -> ff5722 || harbour -> yes || last_edit_changeset -> 45968126 || last_edit_time -> 1486718254000 || last_edit_user_id -> 3450290 || iso_country_code -> CHN || name -> Naval base || landuse -> harbour || type -> multipolygon || last_edit_version -> 1 && MULTIPOLYGON (((112.5483745 21.6027403, 112.5423921 21.5995149, 112.5430221 21.5995276, 112.5435096 21.5994654, 112.5439194 21.5994075, 112.5440884 21.5993408, 112.5443683 21.5991062, 112.5447429 21.5987951, 112.5450146 21.5985958, 112.5454403 21.5982452, 112.5456896 21.5980009, 112.546053 21.597645, 112.5463828 21.5972944, 112.5466701 21.5970749, 112.5473723 21.5966615, 112.5477918 21.5964787, 112.5481722 21.5964588, 112.5491521 21.596309, 112.5494624 21.5964012, 112.5496262 21.5963848, 112.549683 21.5963791, 112.5497178 21.5963757, 112.5498097 21.5964665, 112.5499475 21.5963677, 112.5498901 21.5962956, 112.5499571 21.5961938, 112.5508004 21.5959966, 112.5509501 21.5960959, 112.5510372 21.5961538, 112.5512744 21.5957895, 112.55134 21.5958295, 112.5513038 21.5958936, 112.5514244 21.5959657, 112.5516541 21.5961875, 112.5519236 21.5963953, 112.5520989 21.5965349, 112.5522329 21.5968633, 112.5522296 21.5972613, 112.5521714 21.5975613, 112.5516892 21.5993743, 112.5514777 21.5995502, 112.5512965 21.5996528, 112.5512081 21.5997046, 112.5509308 21.5998612, 112.5508363 21.5999167, 112.550518 21.6000965, 112.5504274 21.6001496, 112.5499738 21.6004075, 112.5497067 21.600815, 112.549612 21.6007696, 112.5495172 21.6009112, 112.5494712 21.6009951, 112.5495517 21.6010446, 112.5483745 21.6027403)))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/items/intersectionAtlas.atlas.txt",
    "content": "# Nodes\n3558267680000000 && 47.618416,-122.289001 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987007000 || last_edit_user_id -> 37744 || last_edit_version -> 1\n3558267681000000 && 47.618671,-122.289352 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987007000 || last_edit_user_id -> 37744 || last_edit_version -> 1 \n3558267682000000 && 47.618946,-122.288925 && last_edit_user_name -> dle0 || last_edit_changeset -> 31582804 || last_edit_time -> 1432987007000 || last_edit_user_id -> 37744 || last_edit_version -> 1 \n# Edges\n1234567000001 && 47.618416,-122.289001:47.618286,-122.288635:47.618492,-122.288788:47.618671,-122.289352 && highway -> trunk\n1234567000002 && 47.618671,-122.289352:47.618759,-122.289635:47.618946,-122.288925 && highway -> trunk\n# Areas\n247059838000000 && 47.618416,-122.289001:47.618286,-122.288635:47.618492,-122.288788:47.618671,-122.289352 && last_edit_changeset -> 20186822 || last_edit_user_name -> muralito || last_edit_time -> 1390609954000 || last_edit_user_id -> 755114 || last_edit_version -> 4\n# Lines\n1234599000001 && 47.618416,-122.289001:47.618286,-122.288635:47.618492,-122.288788:47.618671,-122.289352 && power -> line\n# Points\n# Relations\n5218860000000 && 1234567000001 -> from -> E || 1234567000002 -> to -> E || 3558267681000000 -> via -> N && last_edit_user_name -> dle0 || last_edit_changeset -> 41287685 || last_edit_time -> 1470490850000 || last_edit_user_id -> 37744 || type -> restriction || last_edit_version -> 4"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/line-delimited-geojson.txt",
    "content": "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-122.472852,37.780574]},\"properties\":{\"identifier\":1,\"osmIdentifier\":0,\"itemType\":\"NODE\",\"inEdges\":[],\"outEdges\":[0]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-122.472242,37.780592]},\"properties\":{\"identifier\":2,\"osmIdentifier\":0,\"itemType\":\"NODE\",\"relations\":[1],\"inEdges\":[0],\"outEdges\":[1]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-122.472249,37.780724]},\"properties\":{\"identifier\":3,\"osmIdentifier\":0,\"itemType\":\"NODE\",\"inEdges\":[1],\"outEdges\":[]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-122.472852,37.780574],[-122.472242,37.780592]]},\"properties\":{\"highway\":\"trunk\",\"fixme\":\"please\",\"identifier\":0,\"osmIdentifier\":0,\"itemType\":\"EDGE\",\"relations\":[1],\"startNode\":1,\"endNode\":2}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-122.472242,37.780592],[-122.472249,37.780724]]},\"properties\":{\"highway\":\"trunk\",\"identifier\":1,\"osmIdentifier\":0,\"itemType\":\"EDGE\",\"relations\":[1],\"startNode\":2,\"endNode\":3}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-122.472852,37.780574],[-122.472242,37.780592],[-122.472249,37.780724],[-122.472852,37.780574]]]},\"properties\":{\"addr:housenumber\":\"25\",\"identifier\":0,\"osmIdentifier\":0,\"itemType\":\"AREA\",\"relations\":[2]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-122.472242,37.780592],[-122.472249,37.780724],[-122.471896,37.780825],[-122.472242,37.780592]]]},\"properties\":{\"natural\":\"water\",\"water\":\"lake\",\"identifier\":1,\"osmIdentifier\":0,\"itemType\":\"AREA\",\"relations\":[3]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-122.472852,37.780574],[-122.472242,37.780592]]},\"properties\":{\"FIXME\":\"0\",\"railway\":\"station\",\"identifier\":0,\"osmIdentifier\":0,\"itemType\":\"LINE\"}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-122.472242,37.780592],[-122.471896,37.780825]]},\"properties\":{\"public_transport\":\"stop_position\",\"train\":\"yes\",\"identifier\":1,\"osmIdentifier\":0,\"itemType\":\"LINE\",\"relations\":[2,3,5]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-122.472852,37.780574]},\"properties\":{\"addr:housenumber\":\"25\",\"addr:street\":\"coco\",\"identifier\":0,\"osmIdentifier\":0,\"itemType\":\"POINT\"}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-122.472242,37.780592]},\"properties\":{\"fixme\":\"wrong name\",\"identifier\":1,\"osmIdentifier\":0,\"itemType\":\"POINT\"}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-122.472249,37.780724]},\"properties\":{\"landuse\":\"basin\",\"identifier\":2,\"osmIdentifier\":0,\"itemType\":\"POINT\"}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-122.471896,37.780825]},\"properties\":{\"amenity\":\"school\",\"identifier\":3,\"osmIdentifier\":0,\"itemType\":\"POINT\"}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-122.472852,37.780574],[-122.472852,37.780724],[-122.472242,37.780724],[-122.472242,37.780574],[-122.472852,37.780574]]]},\"properties\":{\"restriction\":\"no_left_turn\",\"type\":\"restriction\",\"identifier\":1,\"osmIdentifier\":0,\"itemType\":\"RELATION\",\"relations\":[5],\"members\":[{\"identifier\":2,\"itemType\":\"NODE\",\"role\":\"via\"},{\"identifier\":0,\"itemType\":\"EDGE\",\"role\":\"from\"},{\"identifier\":1,\"itemType\":\"EDGE\",\"role\":\"to\"}]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-122.472852,37.780574],[-122.472852,37.780825],[-122.471896,37.780825],[-122.471896,37.780574],[-122.472852,37.780574]]]},\"properties\":{\"type\":\"half_inside\",\"identifier\":2,\"osmIdentifier\":0,\"itemType\":\"RELATION\",\"members\":[{\"identifier\":0,\"itemType\":\"AREA\",\"role\":\"inside\"},{\"identifier\":1,\"itemType\":\"LINE\",\"role\":\"outside\"}]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-122.472249,37.780592],[-122.472249,37.780825],[-122.471896,37.780825],[-122.471896,37.780592],[-122.472249,37.780592]]]},\"properties\":{\"type\":\"outside\",\"identifier\":3,\"osmIdentifier\":0,\"itemType\":\"RELATION\",\"members\":[{\"identifier\":1,\"itemType\":\"AREA\",\"role\":\"outside\"},{\"identifier\":1,\"itemType\":\"LINE\",\"role\":\"outside\"}]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-122.472852,37.780574],[-122.472852,37.780825],[-122.471896,37.780825],[-122.471896,37.780574],[-122.472852,37.780574]]]},\"properties\":{\"type\":\"inside_because_of_relation_inside\",\"identifier\":5,\"osmIdentifier\":0,\"itemType\":\"RELATION\",\"relations\":[4],\"members\":[{\"identifier\":1,\"itemType\":\"LINE\",\"role\":\"outside\"},{\"identifier\":1,\"itemType\":\"RELATION\",\"role\":\"inside\"}]}}\n{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-122.472852,37.780574],[-122.472852,37.780825],[-122.471896,37.780825],[-122.471896,37.780574],[-122.472852,37.780574]]]},\"properties\":{\"type\":\"inside_because_of_level_2_relation_inside\",\"identifier\":4,\"osmIdentifier\":0,\"itemType\":\"RELATION\",\"members\":[{\"identifier\":5,\"itemType\":\"RELATION\",\"role\":\"inside\"}]}}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/AIA_boundary.txt",
    "content": "AIA||POLYGON ((-62.76312 18.1617887, -62.8763889 18.1902778, -62.9486111 18.1808333, -63.2094444 18.0980556, -63.3630916 18.0609378, -63.3771792 18.0953032, -63.3789926 18.1026598, -63.4047895 18.1185063, -63.4329509 18.1438542, -63.4559282 18.1739371, -63.4741148 18.2111646, -63.4836934 18.2481463, -63.485814 18.2857672, -63.4815363 18.3189697, -63.4715459 18.3510331, -63.4550558 18.3824779, -63.4638886 18.383194, -63.4984045 18.3917593, -63.530914 18.4057613, -63.5604854 18.4247988, -63.586558 18.4486376, -63.6083528 18.4768146, -63.624694 18.508174, -63.6350824 18.5417639, -63.6391992 18.5765642, -63.6361741 18.6171373, -63.6304961 18.6424208, -63.6185484 18.6737629, -63.6013195 18.7027875, -63.5792825 18.7286932, -63.5680018 18.7396922, -63.5375893 18.7624516, -63.5038432 18.7795528, -63.4674048 18.7905631, -63.4294754 18.7951194, -63.3913054 18.7930715, -63.3541538 18.7844866, -63.3192454 18.769648, -63.2873995 18.7487832, -63.2593009 18.7219304, -63.2370085 18.6905217, -63.2213148 18.6556787, -63.2127759 18.6186448, -63.2116914 18.5807407, -63.2162068 18.5496612, -63.21887 18.5404974, -63.2267431 18.5164009, -63.2339724 18.5010857, -63.2421995 18.4870075, -63.1921975 18.4820994, -63.1749669 18.4770254, -63.1674947 18.4772384, -63.1352448 18.472554, -63.1193675 18.473714, -63.0823119 18.4737166, -63.0592699 18.4708299, -63.0336717 18.4841929, -62.9960982 18.4950196, -62.9614299 18.4989214, -62.9265625 18.4972974, -62.917916 18.4955219, -62.9110185 18.495389, -62.8694851 18.4875076, -62.8340843 18.4741697, -62.8199891 18.4675354, -62.7901606 18.4482165, -62.7642312 18.4243196, -62.7429615 18.3965477, -62.7269744 18.3657168, -62.7167364 18.3327328, -62.7125449 18.298564, -62.7145193 18.2642131, -62.7225981 18.2306875, -62.7341738 18.2034351, -62.755937 18.1699895, -62.76312 18.1617887))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/DMA_boundary.txt",
    "content": "DMA||POLYGON ((-61.3490238 15.0074943, -61.3702452 15.0074207, -61.4005587 15.0115114, -61.4232934 15.0170995, -61.4499481 15.0267559, -61.4813509 15.0421834, -61.5102395 15.0630642, -61.5388572 15.0931927, -61.5609937 15.1286379, -61.573888 15.1626946, -61.5802577 15.1983142, -61.5802519 15.2182578, -61.5896846 15.2426108, -61.5979214 15.2828215, -61.6116448 15.3052907, -61.6237522 15.3347959, -61.6350822 15.3524338, -61.6486274 15.3851843, -61.6521867 15.3997221, -61.6602503 15.4157614, -61.6719498 15.4490992, -61.6756242 15.4722623, -61.6819285 15.4920564, -61.6869184 15.5259787, -61.6832167 15.6182678, -61.6798235 15.644411, -61.4425 15.7872222, -61.3236111 15.7341667, -61.1383333 15.7016667, -61.1204868 15.6804104, -61.116797 15.6720689, -61.1106596 15.6656506, -61.0968189 15.6460962, -61.083421 15.6300299, -61.0657932 15.5989765, -61.0607846 15.5846292, -61.0567601 15.5769338, -61.055243 15.5742449, -61.0436767 15.541066, -61.036387 15.4952162, -61.0344085 15.4615026, -61.037269 15.4351561, -61.0329895 15.3942999, -61.0364022 15.3589037, -61.0354401 15.3544219, -61.0346416 15.3150813, -61.0368515 15.3029501, -61.0373217 15.2768091, -61.0420306 15.2476706, -61.0442149 15.2375483, -61.0562047 15.2043716, -61.0609591 15.196169, -61.0666737 15.1779782, -61.083955 15.144618, -61.1069788 15.1151873, -61.1349236 15.0905183, -61.1589127 15.0792459, -61.2830556 15.0308333, -61.3490238 15.0074943))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/DNK_SWE_boundary.txt",
    "content": "DNK||POLYGON ((12.76302329063 55.59512845369, 12.80636778831 55.59503145851, 12.80465117455 55.57373521447, 12.76122084618 55.57300734823, 12.76302329063 55.59512845369))\nSWE||POLYGON ((12.81217998751 55.58015672226, 12.81184618447 55.57264298618, 12.82883979379 55.57243711016, 12.82844529928 55.58034540489, 12.81217998751 55.58015672226))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/bridgeConfiguredFilter1.json",
    "content": "{\n    \"filters\": [\n        \"highway->traffic_signals\"\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/bridgeConfiguredFilter2.json",
    "content": "{\n    \"my-filter\":\n    {\n        \"taggableFilter\": \"highway->trunk\",\n        \"predicate\":\n        {\n            \"imports\": [\n                \"org.openstreetmap.atlas.geography.atlas.items\"\n            ],\n            \"command\": \"e instanceof Edge\"\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/edge-filter.json",
    "content": "{\n    \"edge-filter\":\n    {\n        \"taggableFilter\": \"highway->*\"\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/ferryRelation5831018.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='187295621' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='6' changeset='842736' lat='-38.3859238' lon='145.2383351' />\n  <node id='187295622' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='6' changeset='842736' lat='-38.390364' lon='145.2405667' />\n  <node id='187295623' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='6' changeset='842736' lat='-38.391844' lon='145.24091' />\n  <node id='187295624' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='6' changeset='842736' lat='-38.3935931' lon='145.2412534' />\n  <node id='187295626' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='6' changeset='842736' lat='-38.3949385' lon='145.241425' />\n  <node id='187303326' timestamp='2009-01-27T03:07:33Z' uid='32810' user='Yoda' version='5' changeset='842736' lat='-38.3721305' lon='145.2249455' />\n  <node id='187303327' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3721305' lon='145.225718' />\n  <node id='187303328' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3721305' lon='145.2268338' />\n  <node id='187303329' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3726688' lon='145.2281213' />\n  <node id='187303330' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3737455' lon='145.2300954' />\n  <node id='187303331' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3779846' lon='145.2335286' />\n  <node id='187307937' timestamp='2009-01-27T03:07:33Z' uid='32810' user='Yoda' version='3' changeset='842736' lat='-38.371794' lon='145.2256322' />\n  <node id='187307939' timestamp='2009-01-27T03:07:33Z' uid='32810' user='Yoda' version='3' changeset='842736' lat='-38.3716595' lon='145.2266621' />\n  <node id='187307940' timestamp='2013-10-14T05:23:39Z' uid='612392' user='s1kk4z' version='6' changeset='18343534' lat='-38.3831379' lon='145.2707115' />\n  <node id='187310328' timestamp='2009-01-27T03:06:47Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3836363' lon='145.2695775' />\n  <node id='187310329' timestamp='2009-01-27T03:06:47Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3843091' lon='145.2676892' />\n  <node id='187310330' timestamp='2009-01-27T03:06:47Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3857893' lon='145.263741' />\n  <node id='187310331' timestamp='2009-01-27T03:06:47Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3879421' lon='145.2611661' />\n  <node id='187310332' timestamp='2009-01-27T03:06:47Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.391575' lon='145.2587628' />\n  <node id='187310333' timestamp='2009-01-27T03:06:47Z' uid='32810' user='Yoda' version='4' changeset='842736' lat='-38.3941313' lon='145.2575612' />\n  <node id='279228578' timestamp='2009-01-27T03:06:59Z' uid='32810' user='Yoda' version='2' changeset='842736' lat='-38.3721786' lon='145.2251839' />\n  <node id='1155844974' timestamp='2013-12-02T11:07:58Z' uid='206929' user='Supt_of_Printing' version='2' changeset='19231173' lat='-38.4461533' lon='145.2397079' />\n  <node id='1943600337' timestamp='2012-10-03T00:23:49Z' uid='143129' user='petersfreeman' version='1' changeset='13341813' lat='-38.3722478' lon='145.2248402' />\n  <way id='18132589' timestamp='2016-01-06T05:27:49Z' uid='79475' user='AlexOnTheBus' version='14' changeset='36395753'>\n    <nd ref='1943600337' />\n    <nd ref='279228578' />\n    <nd ref='187303327' />\n    <nd ref='187303328' />\n    <nd ref='187303329' />\n    <nd ref='187303330' />\n    <nd ref='187303331' />\n    <nd ref='187295621' />\n    <nd ref='187295622' />\n    <nd ref='187295623' />\n    <nd ref='187295624' />\n    <nd ref='187295626' />\n    <nd ref='1155844974' />\n    <tag k='bicycle' v='yes' />\n    <tag k='motor_vehicle' v='no' />\n    <tag k='name' v='Stony Point - Cowes' />\n    <tag k='route' v='ferry' />\n  </way>\n  <way id='18133734' timestamp='2016-01-06T05:27:50Z' uid='79475' user='AlexOnTheBus' version='8' changeset='36395753'>\n    <nd ref='1943600337' />\n    <nd ref='187303326' />\n    <nd ref='187307937' />\n    <nd ref='187307939' />\n    <nd ref='187307940' />\n    <tag k='bicycle' v='yes' />\n    <tag k='motor_vehicle' v='no' />\n    <tag k='name' v='Stony Point - Tankerton' />\n    <tag k='route' v='ferry' />\n  </way>\n  <way id='18134009' timestamp='2016-01-06T05:27:50Z' uid='79475' user='AlexOnTheBus' version='9' changeset='36395753'>\n    <nd ref='187307940' />\n    <nd ref='187310328' />\n    <nd ref='187310329' />\n    <nd ref='187310330' />\n    <nd ref='187310331' />\n    <nd ref='187310332' />\n    <nd ref='187310333' />\n    <nd ref='1155844974' />\n    <tag k='bicycle' v='yes' />\n    <tag k='motor_vehicle' v='no' />\n    <tag k='name' v='Tankerton - Cowes' />\n    <tag k='route' v='ferry' />\n  </way>\n  <relation id='5831018' timestamp='2016-01-06T05:27:49Z' uid='79475' user='AlexOnTheBus' version='1' changeset='36395753'>\n    <member type='way' ref='18133734' role='' />\n    <member type='way' ref='18134009' role='' />\n    <member type='way' ref='18132589' role='' />\n    <tag k='bicycle' v='yes' />\n    <tag k='motor_vehicle' v='no' />\n    <tag k='name' v='French Island Ferry' />\n    <tag k='operator' v='Inter Island Ferries' />\n    <tag k='route' v='ferry' />\n    <tag k='type' v='route' />\n    <tag k='url' v='www.interislandferries.com.au' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/noRelationNoTagsAtIntersectionAtlas.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727' />\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <node id='-1410003' action='modify' lat='7.01317734799' lon='-10.54639734007' />\n  <node id='-1410005' action='modify' lat='7.01327244286' lon='-10.54497940386' />\n  <way id='-1410000' action='modify'>\n    <nd ref='-1409998' />\n    <nd ref='-1409999' />\n    <nd ref='-1410001' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-1410008' action='modify'>\n    <nd ref='-1410003' />\n    <nd ref='-1409999' />\n    <nd ref='-1410005' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/noRelationNoTagsNoIntersectionAtlas.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727' />\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <way id='-1410000' action='modify'>\n    <nd ref='-1409998' />\n    <nd ref='-1409999' />\n    <nd ref='-1410001' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/noRelationWithTagsAtIntersectionAtlas.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727'>\n      <tag k='natural' v='tree' />\n  </node>\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <node id='-1410003' action='modify' lat='7.01317734799' lon='-10.54639734007' />\n  <node id='-1410005' action='modify' lat='7.01327244286' lon='-10.54497940386' />\n  <way id='-1410000' action='modify'>\n    <nd ref='-1409998' />\n    <nd ref='-1409999' />\n    <nd ref='-1410001' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-1410008' action='modify'>\n    <nd ref='-1410003' />\n    <nd ref='-1409999' />\n    <nd ref='-1410005' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/noRelationWithTagsNoIntersectionAtlas.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <way id='-1410000' action='modify'>\n    <nd ref='-1409998' />\n    <nd ref='-1409999' />\n    <nd ref='-1410001' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/one_way_roads_in_AIA.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <bounds minlat='18.2047296' minlon='-63.0553436' maxlat='18.2071553' maxlon='-63.0527043' origin='CGImap 0.6.0 (17980 thorn-02.openstreetmap.org)' />\n  <node id='242846762' timestamp='2013-02-14T19:39:32Z' uid='496606' user='scottyc' version='2' changeset='15033051' lat='18.2062964' lon='-63.05042' />\n  <node id='242846763' timestamp='2013-02-14T19:39:32Z' uid='496606' user='scottyc' version='2' changeset='15033051' lat='18.2055737' lon='-63.0504124' />\n  <node id='242846764' timestamp='2013-02-14T19:39:32Z' uid='496606' user='scottyc' version='2' changeset='15033051' lat='18.205594' lon='-63.0533361' />\n  <node id='242846765' timestamp='2013-02-14T19:39:32Z' uid='496606' user='scottyc' version='2' changeset='15033051' lat='18.2062942' lon='-63.0533548' />\n  <node id='242846808' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='4' changeset='21076368' lat='18.2048681' lon='-63.060274'>\n    <tag k='aeroway' v='threshold' />\n    <tag k='ref' v='10' />\n  </node>\n  <node id='242846809' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='4' changeset='21076368' lat='18.2047355' lon='-63.0474112' />\n  <node id='242846935' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='4' changeset='21076368' lat='18.2047957' lon='-63.0532526' />\n  <node id='269059403' timestamp='2013-02-14T19:39:32Z' uid='496606' user='scottyc' version='3' changeset='15033051' lat='18.206841' lon='-63.0516415' />\n  <node id='2106583694' timestamp='2013-01-12T03:23:53Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2053456' lon='-63.0494928' />\n  <node id='2106583701' timestamp='2013-01-12T03:23:53Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2038525' lon='-63.0458987' />\n  <node id='2106583702' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2054026' lon='-63.0503836' />\n  <node id='2106583704' timestamp='2013-01-12T03:23:53Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2059112' lon='-63.0578077' />\n  <node id='2106583705' timestamp='2013-01-12T03:23:53Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2040207' lon='-63.0457914' />\n  <node id='2106583706' timestamp='2015-07-16T16:09:58Z' uid='2957617' user='vespax' version='2' changeset='32678142' lat='18.2081292' lon='-63.0514341' />\n  <node id='2106583707' timestamp='2015-07-16T16:09:58Z' uid='2957617' user='vespax' version='2' changeset='32678142' lat='18.2069488' lon='-63.0517022' />\n  <node id='2106583722' timestamp='2013-01-12T03:23:53Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2055036' lon='-63.0486453' />\n  <node id='2106583732' timestamp='2013-01-12T03:23:53Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2068311' lon='-63.0538005' />\n  <node id='2106583745' timestamp='2013-01-12T03:23:54Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2057635' lon='-63.0602914' />\n  <node id='2106583746' timestamp='2013-01-12T03:23:54Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2082044' lon='-63.0529422' />\n  <node id='2106583759' timestamp='2013-01-12T03:23:54Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2053558' lon='-63.045845' />\n  <node id='2106583787' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2035264' lon='-63.0534786' />\n  <node id='2106583788' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2040309' lon='-63.0505228' />\n  <node id='2106583792' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2055342' lon='-63.0471647' />\n  <node id='2106583793' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2055647' lon='-63.0610853' />\n  <node id='2106583794' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2040818' lon='-63.0563164' />\n  <node id='2106583801' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2049277' lon='-63.0620778' />\n  <node id='2106583802' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2039952' lon='-63.0563217' />\n  <node id='2106583808' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2054628' lon='-63.0619651' />\n  <node id='2106583811' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2055392' lon='-63.049466' />\n  <node id='2106583818' timestamp='2013-02-14T19:39:32Z' uid='496606' user='scottyc' version='3' changeset='15033051' lat='18.2082201' lon='-63.054505' />\n  <node id='2106583823' timestamp='2013-01-12T03:23:55Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2049481' lon='-63.0619705' />\n  <node id='2106583836' timestamp='2013-01-12T03:23:56Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2032563' lon='-63.0510914' />\n  <node id='2106583851' timestamp='2013-01-12T03:23:56Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2068387' lon='-63.051542' />\n  <node id='2106583854' timestamp='2013-02-14T19:39:32Z' uid='496606' user='scottyc' version='2' changeset='15033051' lat='18.2068312' lon='-63.0530663' />\n  <node id='2106583856' timestamp='2013-01-12T03:23:56Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2038627' lon='-63.0478728' />\n  <node id='2106583869' timestamp='2013-01-12T03:23:56Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2040258' lon='-63.0499435' />\n  <node id='2106583873' timestamp='2013-01-12T03:23:57Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2037863' lon='-63.048259' />\n  <node id='2106583882' timestamp='2013-01-12T03:23:57Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2057431' lon='-63.0552006' />\n  <node id='2106583883' timestamp='2013-02-14T19:39:32Z' uid='496606' user='scottyc' version='2' changeset='15033051' lat='18.2068393' lon='-63.0517226' />\n  <node id='2106583890' timestamp='2013-01-12T03:23:57Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.203878' lon='-63.053425' />\n  <node id='2106583896' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2067834' lon='-63.050288' />\n  <node id='2106583928' timestamp='2013-01-12T03:23:58Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.205366' lon='-63.0477655' />\n  <node id='2106583929' timestamp='2013-01-12T03:23:58Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2055087' lon='-63.0460435' />\n  <node id='2106583934' timestamp='2013-01-12T03:23:58Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2033072' lon='-63.0505818' />\n  <node id='2106583935' timestamp='2013-01-12T03:23:58Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2056666' lon='-63.0580652' />\n  <node id='2106583936' timestamp='2013-01-12T03:23:58Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2038525' lon='-63.0619597' />\n  <node id='2106583942' timestamp='2015-07-16T16:09:58Z' uid='2957617' user='vespax' version='3' changeset='32678142' lat='18.2081647' lon='-63.0530509' />\n  <node id='2106583953' timestamp='2013-01-12T03:23:58Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2038525' lon='-63.0546105' />\n  <node id='2106583959' timestamp='2013-01-12T03:23:59Z' uid='496606' user='scottyc' version='1' changeset='14617791' lat='18.2081432' lon='-63.0548465' />\n  <node id='2154893996' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2082077' lon='-63.0547008' />\n  <node id='2154893997' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2051348' lon='-63.0524684' />\n  <node id='2154894000' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2068263' lon='-63.0538442' />\n  <node id='2154894001' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2064104' lon='-63.0524456' />\n  <node id='2154894003' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2064123' lon='-63.0527697' />\n  <node id='2154894004' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049412' lon='-63.0534903' />\n  <node id='2154894005' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049667' lon='-63.053139' />\n  <node id='2154894006' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2047728' lon='-63.051205' />\n  <node id='2154894010' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062945' lon='-63.0529886' />\n  <node id='2154894011' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2081781' lon='-63.0547676' />\n  <node id='2154894012' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062708' lon='-63.054952' />\n  <node id='2154894014' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062958' lon='-63.0513149' />\n  <node id='2154894017' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055818' lon='-63.0515349' />\n  <node id='2154894018' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2067426' lon='-63.0550056' />\n  <node id='2154894019' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2068421' lon='-63.0522216' />\n  <node id='2154894020' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2067069' lon='-63.0550164' />\n  <node id='2154894022' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049233' lon='-63.0519346' />\n  <node id='2154894023' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2064107' lon='-63.0525004' />\n  <node id='2154894025' timestamp='2013-02-14T19:39:25Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055803' lon='-63.0518004' />\n  <node id='2154894031' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2072186' lon='-63.0530313' />\n  <node id='2154894033' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2064103' lon='-63.0524337' />\n  <node id='2154894038' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2071451' lon='-63.052219' />\n  <node id='2154894039' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2051756' lon='-63.0523853' />\n  <node id='2154894041' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.205537' lon='-63.0517682' />\n  <node id='2154894042' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.20687' lon='-63.0538363' />\n  <node id='2154894043' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.206898' lon='-63.0537371' />\n  <node id='2154894045' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2077868' lon='-63.0548313' />\n  <node id='2154894046' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049667' lon='-63.0535708' />\n  <node id='2154894047' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055807' lon='-63.0585193' />\n  <node id='2154894048' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2068001' lon='-63.0524923' />\n  <node id='2154894050' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.206174' lon='-63.0533516' />\n  <node id='2154894052' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2068371' lon='-63.0526105' />\n  <node id='2154894054' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2069769' lon='-63.0526077' />\n  <node id='2154894058' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2051166' lon='-63.0531737' />\n  <node id='2154894061' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2068327' lon='-63.0529537' />\n  <node id='2154894062' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2065129' lon='-63.0523822' />\n  <node id='2154894063' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2058763' lon='-63.0577227' />\n  <node id='2154894064' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049437' lon='-63.0536244' />\n  <node id='2154894067' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062949' lon='-63.0524468' />\n  <node id='2154894069' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049335' lon='-63.0524014' />\n  <node id='2154894070' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2071502' lon='-63.0529458' />\n  <node id='2154894071' timestamp='2013-02-14T19:39:26Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049361' lon='-63.0530156' />\n  <node id='2154894076' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055574' lon='-63.0580687' />\n  <node id='2154894077' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049463' lon='-63.0518139' />\n  <node id='2154894088' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2067757' lon='-63.0528922' />\n  <node id='2154894089' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2047992' lon='-63.0535948' />\n  <node id='2154894090' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049386' lon='-63.0535467' />\n  <node id='2154894094' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2067435' lon='-63.0523807' />\n  <node id='2154894096' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2051879' lon='-63.0519345' />\n  <node id='2154894098' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2064629' lon='-63.0527694' />\n  <node id='2154894100' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2081183' lon='-63.0529807' />\n  <node id='2154894102' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.206295' lon='-63.0522832' />\n  <node id='2154894103' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2063196' lon='-63.0550137' />\n  <node id='2154894104' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049565' lon='-63.053383' />\n  <node id='2154894105' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2064632' lon='-63.0528249' />\n  <node id='2154894106' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055374' lon='-63.05864' />\n  <node id='2154894108' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2067404' lon='-63.0527072' />\n  <node id='2154894111' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2069795' lon='-63.0529673' />\n  <node id='2154894112' timestamp='2013-02-14T19:39:27Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.206974' lon='-63.0522204' />\n  <node id='2154894114' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.206768' lon='-63.0538041' />\n  <node id='2154894116' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2064643' lon='-63.0523825' />\n  <node id='2154894118' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2050202' lon='-63.0617058' />\n  <node id='2154894122' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2058275' lon='-63.0578381' />\n  <node id='2154894124' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2050686' lon='-63.0533321' />\n  <node id='2154894125' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2047797' lon='-63.0516959' />\n  <node id='2154894127' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2067477' lon='-63.0531711' />\n  <node id='2154894128' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055211' lon='-63.0490484' />\n  <node id='2154894130' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2050227' lon='-63.0517684' />\n  <node id='2154894133' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055574' lon='-63.0531737' />\n  <node id='2154894134' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2057056' lon='-63.0551478' />\n  <node id='2154894135' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2065184' lon='-63.0529834' />\n  <node id='2154894137' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2068014' lon='-63.0527068' />\n  <node id='2154894144' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062504' lon='-63.0538764' />\n  <node id='2154894147' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055943' lon='-63.0531362' />\n  <node id='2154894148' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2050813' lon='-63.0523584' />\n  <node id='2154894152' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062508' lon='-63.05507' />\n  <node id='2154894153' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2061795' lon='-63.0535601' />\n  <node id='2154894154' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.208169' lon='-63.0530128' />\n  <node id='2154894157' timestamp='2013-02-14T19:39:28Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049768' lon='-63.0533562' />\n  <node id='2154894158' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2075626' lon='-63.0548447' />\n  <node id='2154894160' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2062948' lon='-63.0524992' />\n  <node id='2154894161' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055319' lon='-63.0604452' />\n  <node id='2154894163' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2067447' lon='-63.0537557' />\n  <node id='2154894164' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2068496' lon='-63.0531095' />\n  <node id='2154894167' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2061362' lon='-63.0536137' />\n  <node id='2154894168' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2068312' lon='-63.0531442' />\n  <node id='2154894170' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062381' lon='-63.0535037' />\n  <node id='2154894176' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2050941' lon='-63.0519239' />\n  <node id='2154894178' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2055192' lon='-63.0591094' />\n  <node id='2154894179' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.206281' lon='-63.053855' />\n  <node id='2154894188' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062046' lon='-63.0535868' />\n  <node id='2154894190' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2061922' lon='-63.0536432' />\n  <node id='2154894192' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062951' lon='-63.0522001' />\n  <node id='2154894193' timestamp='2013-02-14T19:39:29Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062957' lon='-63.0513498' />\n  <node id='2154894194' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2064646' lon='-63.0524333' />\n  <node id='2154894201' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2067411' lon='-63.0528231' />\n  <node id='2154894202' timestamp='2013-02-14T19:39:30Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2062402' lon='-63.0534044' />\n  <node id='2154894203' timestamp='2013-02-14T19:39:30Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2052058' lon='-63.0616897' />\n  <node id='2154894205' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2067441' lon='-63.0524926' />\n  <node id='2154894209' timestamp='2014-03-13T03:29:14Z' uid='114310' user='Joe E' version='2' changeset='21076368' lat='18.2065154' lon='-63.0528246' />\n  <node id='2154894211' timestamp='2013-02-14T19:39:30Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2074254' lon='-63.0548957' />\n  <node id='2154894213' timestamp='2013-02-14T19:39:30Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2052899' lon='-63.0615663' />\n  <node id='2154894214' timestamp='2013-02-14T19:39:30Z' uid='496606' user='scottyc' version='1' changeset='15033051' lat='18.2049386' lon='-63.0525757' />\n  <node id='2715021830' timestamp='2017-09-07T17:43:44Z' uid='6521966' user='Paul David baron Von Weenraid' version='2' changeset='51819749' lat='18.2054194' lon='-63.0532493'>\n    <tag k='aeroway' v='holding_position' />\n    <tag k='source' v='Bing' />\n  </node>\n  <node id='2715021831' timestamp='2014-03-13T03:29:11Z' uid='114310' user='Joe E' version='1' changeset='21076368' lat='18.2058114' lon='-63.0525103' />\n  <node id='2715021832' timestamp='2014-03-13T03:29:11Z' uid='114310' user='Joe E' version='1' changeset='21076368' lat='18.205813' lon='-63.0532565' />\n  <node id='2715021833' timestamp='2014-03-13T03:29:11Z' uid='114310' user='Joe E' version='1' changeset='21076368' lat='18.205961' lon='-63.0516722' />\n  <node id='2715021834' timestamp='2014-03-13T03:29:11Z' uid='114310' user='Joe E' version='1' changeset='21076368' lat='18.2059663' lon='-63.0524661' />\n  <node id='2715021835' timestamp='2014-03-13T03:29:11Z' uid='114310' user='Joe E' version='1' changeset='21076368' lat='18.2059707' lon='-63.0504831' />\n  <node id='2715021836' timestamp='2014-03-13T03:29:11Z' uid='114310' user='Joe E' version='1' changeset='21076368' lat='18.2066774' lon='-63.0502191' />\n  <node id='4332630357' timestamp='2016-08-02T17:11:54Z' uid='3693002' user='GeoCrazy' version='1' changeset='41197040' lat='18.2067731' lon='-63.0524858' />\n  <node id='4332630358' timestamp='2016-08-02T17:11:54Z' uid='3693002' user='GeoCrazy' version='1' changeset='41197040' lat='18.206775' lon='-63.0527124' />\n  <node id='4893786320' timestamp='2017-06-03T13:22:48Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.20648' lon='-63.0540103' />\n  <node id='4893787021' timestamp='2017-06-03T13:22:48Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2064855' lon='-63.0538388' />\n  <node id='4893787022' timestamp='2017-06-03T13:22:48Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2063823' lon='-63.0538388' />\n  <node id='4893787023' timestamp='2017-06-03T13:22:48Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2063877' lon='-63.054016' />\n  <node id='4893787024' timestamp='2017-06-03T13:22:48Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2065181' lon='-63.0536101' />\n  <node id='4893787025' timestamp='2017-06-03T13:22:48Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2065235' lon='-63.0534728' />\n  <node id='4893787026' timestamp='2017-06-03T13:22:48Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2063973' lon='-63.0534726' />\n  <node id='4893787027' timestamp='2017-06-03T13:22:48Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2064022' lon='-63.0536097' />\n  <node id='4893787366' timestamp='2017-06-03T13:22:51Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2064519' lon='-63.0530691' />\n  <node id='4893787367' timestamp='2017-06-03T13:22:51Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2064264' lon='-63.0530835' />\n  <node id='4893787368' timestamp='2017-06-03T13:22:51Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2064033' lon='-63.0530682' />\n  <node id='4893787369' timestamp='2017-06-03T13:22:51Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2064042' lon='-63.0530404' />\n  <node id='4893787370' timestamp='2017-06-03T13:22:51Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2064289' lon='-63.0530296' />\n  <node id='4893787371' timestamp='2017-06-03T13:22:51Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538' lat='18.2064511' lon='-63.0530413' />\n  <node id='5086672770' timestamp='2017-09-07T17:43:43Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749' lat='18.2072335' lon='-63.0562536' />\n  <node id='5086672771' timestamp='2017-09-07T17:43:43Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749' lat='18.205878' lon='-63.0562322' />\n  <node id='5086672772' timestamp='2017-09-07T17:43:43Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749' lat='18.2058321' lon='-63.0552451' />\n  <node id='5086672773' timestamp='2017-09-07T17:43:43Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749' lat='18.207197' lon='-63.0549861' />\n  <node id='5086672774' timestamp='2017-09-07T17:43:43Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749' lat='18.2072182' lon='-63.0555187' />\n  <node id='5086672775' timestamp='2017-09-07T17:43:43Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749' lat='18.2076147' lon='-63.055527' />\n  <node id='5086672776' timestamp='2017-09-07T17:43:43Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749' lat='18.2058364' lon='-63.0553364' />\n  <node id='5086672777' timestamp='2017-09-07T17:43:43Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749' lat='18.2048171' lon='-63.0553258' />\n  <node id='5092804357' timestamp='2017-09-09T20:12:55Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='51890902' lat='18.2056857' lon='-63.058504' />\n  <node id='5092804358' timestamp='2017-09-09T20:12:55Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='51890902' lat='18.205743' lon='-63.0598208' />\n  <node id='5092804414' timestamp='2017-09-09T20:12:56Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='51890902' lat='18.2040243' lon='-63.0577326' />\n  <node id='5092804415' timestamp='2017-09-09T20:12:56Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='51890902' lat='18.2057528' lon='-63.0600466' />\n  <way id='22640986' timestamp='2014-03-13T03:29:13Z' uid='114310' user='Joe E' version='5' changeset='21076368'>\n    <nd ref='242846762' />\n    <nd ref='242846763' />\n    <nd ref='2154894017' />\n    <nd ref='2154894147' />\n    <nd ref='242846764' />\n    <nd ref='2154894050' />\n    <nd ref='242846765' />\n    <nd ref='2154894010' />\n    <nd ref='2154894160' />\n    <nd ref='2154894067' />\n    <nd ref='2154894102' />\n    <nd ref='2154894192' />\n    <nd ref='2154894193' />\n    <nd ref='2154894014' />\n    <nd ref='242846762' />\n    <tag k='aeroway' v='apron' />\n    <tag k='source' v='Yahoo' />\n    <tag k='surface' v='paved' />\n  </way>\n  <way id='22641216' timestamp='2017-09-07T17:43:45Z' uid='6521966' user='Paul David baron Von Weenraid' version='9' changeset='51819749'>\n    <nd ref='242846808' />\n    <nd ref='5086672777' />\n    <nd ref='2154894089' />\n    <nd ref='242846935' />\n    <nd ref='2154894125' />\n    <nd ref='2154894006' />\n    <nd ref='242846809' />\n    <tag k='aeroway' v='runway' />\n    <tag k='ref' v='10 / 28' />\n    <tag k='source' v='Bing' />\n    <tag k='surface' v='asphalt' />\n    <tag k='width' v='30' />\n  </way>\n  <way id='200693727' timestamp='2013-02-14T20:09:21Z' uid='496606' user='scottyc' version='3' changeset='15033051'>\n    <nd ref='2106583854' />\n    <nd ref='2154894061' />\n    <nd ref='2154894052' />\n    <nd ref='2154894019' />\n    <nd ref='2106583883' />\n    <nd ref='269059403' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='200693728' timestamp='2018-06-18T00:00:56Z' uid='8249717' user='mandanf' version='7' changeset='59925980'>\n    <nd ref='2106583936' />\n    <nd ref='2106583801' />\n    <nd ref='2106583823' />\n    <nd ref='2106583808' />\n    <nd ref='2106583793' />\n    <nd ref='2106583745' />\n    <nd ref='5092804415' />\n    <nd ref='5092804358' />\n    <nd ref='5092804357' />\n    <nd ref='2106583935' />\n    <nd ref='2106583704' />\n    <nd ref='2106583882' />\n    <nd ref='5086672773' />\n    <nd ref='2106583959' />\n    <nd ref='2154894011' />\n    <nd ref='2154893996' />\n    <nd ref='2106583818' />\n    <nd ref='2106583942' />\n    <nd ref='2106583706' />\n    <nd ref='2106583707' />\n    <nd ref='2106583851' />\n    <nd ref='2106583896' />\n    <nd ref='2715021836' />\n    <nd ref='2106583702' />\n    <nd ref='2106583694' />\n    <nd ref='2106583811' />\n    <nd ref='2154894128' />\n    <nd ref='2106583722' />\n    <nd ref='2106583928' />\n    <nd ref='2106583792' />\n    <nd ref='2106583929' />\n    <nd ref='2106583759' />\n    <nd ref='2106583705' />\n    <nd ref='2106583701' />\n    <nd ref='2106583856' />\n    <nd ref='2106583873' />\n    <nd ref='2106583869' />\n    <nd ref='2106583788' />\n    <nd ref='2106583934' />\n    <nd ref='2106583836' />\n    <nd ref='2106583787' />\n    <nd ref='2106583890' />\n    <nd ref='2106583953' />\n    <nd ref='2106583802' />\n    <nd ref='2106583794' />\n    <nd ref='5092804414' />\n    <nd ref='2106583936' />\n    <tag k='aeroway' v='aerodrome' />\n    <tag k='ele' v='38.7' />\n    <tag k='iata' v='AXA' />\n    <tag k='icao' v='TQPF' />\n    <tag k='is_in' v='The Valley, Anguilla' />\n    <tag k='name' v='Clayton J. Lloyd International Airport' />\n    <tag k='name:el' v='Διεθνές Αεροδρόμιο Κλέιτον Τζ. Λόιντ' />\n    <tag k='name:en' v='Clayton J. Lloyd International Airport' />\n    <tag k='old_name' v='Anguilla Wallblake Airport' />\n    <tag k='ref' v='AXA' />\n    <tag k='source' v='Bing' />\n    <tag k='type' v='public' />\n    <tag k='vfr' v='yes' />\n    <tag k='wikidata' v='Q1657604' />\n    <tag k='wikipedia' v='en:Clayton J. Lloyd International Airport' />\n  </way>\n  <way id='200693734' timestamp='2013-02-14T20:09:21Z' uid='496606' user='scottyc' version='3' changeset='15033051'>\n    <nd ref='2106583746' />\n    <nd ref='2154894031' />\n    <nd ref='2106583854' />\n    <nd ref='2154894168' />\n    <nd ref='2106583732' />\n    <tag k='highway' v='service' />\n  </way>\n  <way id='205527843' timestamp='2013-02-14T19:39:30Z' uid='496606' user='scottyc' version='1' changeset='15033051'>\n    <nd ref='2154894118' />\n    <nd ref='2154894203' />\n    <nd ref='2154894213' />\n    <nd ref='2154894161' />\n    <nd ref='2154894178' />\n    <nd ref='2154894106' />\n    <nd ref='2154894047' />\n    <nd ref='2154894076' />\n    <nd ref='2154894122' />\n    <nd ref='2154894063' />\n    <nd ref='2154894134' />\n    <nd ref='2154894152' />\n    <nd ref='2154894144' />\n    <nd ref='2154894190' />\n    <nd ref='2154894167' />\n    <nd ref='2154894064' />\n    <nd ref='2154894118' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='205527844' timestamp='2018-01-20T00:15:57Z' uid='6172866' user='CapAhab' version='2' changeset='55592392'>\n    <nd ref='2154894020' />\n    <nd ref='2154894103' />\n    <nd ref='2154894012' />\n    <nd ref='2154894179' />\n    <nd ref='2154894188' />\n    <nd ref='2154894127' />\n    <nd ref='2154894168' />\n    <tag k='access' v='no' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='driveway' />\n  </way>\n  <way id='205527845' timestamp='2013-02-14T19:39:30Z' uid='496606' user='scottyc' version='1' changeset='15033051'>\n    <nd ref='2154894019' />\n    <nd ref='2154894112' />\n    <nd ref='2154894038' />\n    <nd ref='2154894070' />\n    <nd ref='2154894031' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='205527847' timestamp='2016-08-02T17:11:54Z' uid='3693002' user='GeoCrazy' version='2' changeset='41197040'>\n    <nd ref='2154894061' />\n    <nd ref='2154894088' />\n    <nd ref='4332630358' />\n    <tag k='highway' v='service' />\n    <tag k='oneway' v='yes' />\n    <tag k='service' v='driveway' />\n  </way>\n  <way id='205527849' timestamp='2017-09-07T17:43:45Z' uid='6521966' user='Paul David baron Von Weenraid' version='2' changeset='51819749'>\n    <nd ref='2154894104' />\n    <nd ref='2154894004' />\n    <nd ref='2154894090' />\n    <nd ref='2154894046' />\n    <nd ref='2154894153' />\n    <nd ref='2154894170' />\n    <nd ref='2154894202' />\n    <nd ref='2154894050' />\n    <nd ref='242846764' />\n    <nd ref='2715021830' />\n    <nd ref='2154894124' />\n    <nd ref='2154894157' />\n    <nd ref='2154894104' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='205527851' timestamp='2017-09-07T17:43:45Z' uid='6521966' user='Paul David baron Von Weenraid' version='3' changeset='51819749'>\n    <nd ref='2154894005' />\n    <nd ref='2154894058' />\n    <nd ref='2715021830' />\n    <nd ref='2154894133' />\n    <nd ref='2154894147' />\n    <nd ref='2154894025' />\n    <nd ref='2154894041' />\n    <nd ref='2154894130' />\n    <nd ref='2154894077' />\n    <nd ref='2154894022' />\n    <nd ref='2154894069' />\n    <nd ref='2154894148' />\n    <nd ref='2154894176' />\n    <nd ref='2154894096' />\n    <nd ref='2154894039' />\n    <nd ref='2154893997' />\n    <nd ref='2154894214' />\n    <nd ref='2154894071' />\n    <nd ref='2154894005' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='205527852' timestamp='2013-02-14T19:39:31Z' uid='496606' user='scottyc' version='1' changeset='15033051'>\n    <nd ref='2106583818' />\n    <nd ref='2154894154' />\n    <nd ref='2154894100' />\n    <nd ref='2154894164' />\n    <nd ref='2154894043' />\n    <nd ref='2154894042' />\n    <nd ref='2154894000' />\n    <nd ref='2154894114' />\n    <nd ref='2154894163' />\n    <nd ref='2154894018' />\n    <nd ref='2154894211' />\n    <nd ref='2154894158' />\n    <nd ref='2154894045' />\n    <nd ref='2154894011' />\n    <nd ref='2154893996' />\n    <nd ref='2106583818' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='205527855' timestamp='2015-04-05T18:23:54Z' uid='402624' user='bdiscoe' version='3' changeset='29998195'>\n    <nd ref='2154894201' />\n    <nd ref='2154894108' />\n    <nd ref='2154894137' />\n    <nd ref='2154894048' />\n    <nd ref='2154894205' />\n    <nd ref='2154894094' />\n    <nd ref='2154894062' />\n    <nd ref='2154894116' />\n    <nd ref='2154894194' />\n    <nd ref='2154894033' />\n    <nd ref='2154894001' />\n    <nd ref='2154894023' />\n    <nd ref='2154894003' />\n    <nd ref='2154894098' />\n    <nd ref='2154894105' />\n    <nd ref='2154894209' />\n    <nd ref='2154894201' />\n    <tag k='aeroway' v='terminal' />\n    <tag k='building' v='yes' />\n    <tag k='layer' v='1' />\n  </way>\n  <way id='205527857' timestamp='2018-01-20T00:15:57Z' uid='6172866' user='CapAhab' version='2' changeset='55592392'>\n    <nd ref='2154894188' />\n    <nd ref='2154894089' />\n    <tag k='access' v='no' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='driveway' />\n  </way>\n  <way id='205527858' timestamp='2013-02-14T19:39:31Z' uid='496606' user='scottyc' version='1' changeset='15033051'>\n    <nd ref='2154894160' />\n    <nd ref='2154894010' />\n    <nd ref='2154894135' />\n    <nd ref='2154894209' />\n    <nd ref='2154894105' />\n    <nd ref='2154894098' />\n    <nd ref='2154894003' />\n    <nd ref='2154894023' />\n    <nd ref='2154894160' />\n    <tag k='landuse' v='grass' />\n  </way>\n  <way id='205527859' timestamp='2013-02-14T19:39:31Z' uid='496606' user='scottyc' version='1' changeset='15033051'>\n    <nd ref='2154894070' />\n    <nd ref='2154894111' />\n    <nd ref='2154894054' />\n    <nd ref='2154894112' />\n    <tag k='highway' v='service' />\n    <tag k='service' v='parking_aisle' />\n  </way>\n  <way id='265894966' timestamp='2014-03-13T03:29:12Z' uid='114310' user='Joe E' version='1' changeset='21076368'>\n    <nd ref='2715021832' />\n    <nd ref='2715021830' />\n    <nd ref='242846935' />\n    <tag k='aeroway' v='taxiway' />\n    <tag k='est_width' v='17' />\n    <tag k='source' v='Bing' />\n    <tag k='surface' v='paved' />\n  </way>\n  <way id='265894967' timestamp='2014-03-13T03:29:12Z' uid='114310' user='Joe E' version='1' changeset='21076368'>\n    <nd ref='2715021835' />\n    <nd ref='2715021833' />\n    <nd ref='2715021834' />\n    <nd ref='2715021831' />\n    <nd ref='2715021832' />\n    <tag k='aeroway' v='taxiway' />\n    <tag k='est_width' v='17' />\n    <tag k='source' v='Bing' />\n    <tag k='surface' v='paved' />\n  </way>\n  <way id='435341562' timestamp='2016-08-02T17:11:54Z' uid='3693002' user='GeoCrazy' version='1' changeset='41197040'>\n    <nd ref='4332630358' />\n    <nd ref='4332630357' />\n    <tag k='covered' v='yes' />\n    <tag k='highway' v='service' />\n    <tag k='oneway' v='yes' />\n    <tag k='service' v='driveway' />\n  </way>\n  <way id='497876888' timestamp='2017-06-03T13:22:52Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538'>\n    <nd ref='4893786320' />\n    <nd ref='4893787021' />\n    <nd ref='4893787022' />\n    <nd ref='4893787023' />\n    <nd ref='4893786320' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='497876890' timestamp='2017-06-03T13:22:52Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538'>\n    <nd ref='4893787024' />\n    <nd ref='4893787025' />\n    <nd ref='4893787026' />\n    <nd ref='4893787027' />\n    <nd ref='4893787024' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='497876951' timestamp='2017-06-03T13:22:54Z' uid='6116601' user='Shaequan Hanley' version='1' changeset='49220538'>\n    <nd ref='4893787366' />\n    <nd ref='4893787367' />\n    <nd ref='4893787368' />\n    <nd ref='4893787369' />\n    <nd ref='4893787370' />\n    <nd ref='4893787371' />\n    <nd ref='4893787366' />\n    <tag k='man_made' v='tower' />\n  </way>\n  <way id='522204044' timestamp='2017-09-07T17:43:44Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749'>\n    <nd ref='5086672770' />\n    <nd ref='5086672771' />\n    <nd ref='5086672776' />\n    <nd ref='5086672772' />\n    <nd ref='5086672773' />\n    <nd ref='5086672774' />\n    <nd ref='5086672775' />\n    <nd ref='5086672770' />\n    <tag k='aeroway' v='aerodrome' />\n  </way>\n  <way id='522204045' timestamp='2017-09-07T17:43:44Z' uid='6521966' user='Paul David baron Von Weenraid' version='1' changeset='51819749'>\n    <nd ref='5086672776' />\n    <nd ref='5086672777' />\n    <tag k='aeroway' v='taxiway' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/osmPbfProcessorTest_keepOutsideWaysThatAreConnected.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38982' action='modify' lat='15.41334125229' lon='-61.47298419757' />\n  <node id='-38985' action='modify' lat='15.36492975189' lon='-61.50526321779' />\n  <node id='-38989' action='modify' lat='15.33264917035' lon='-61.54232431507' />\n  <node id='-38991' action='modify' lat='15.29575097082' lon='-61.66785383813' />\n  <node id='-38993' action='modify' lat='15.27960596378' lon='-61.519609449' />\n  <node id='-38995' action='modify' lat='15.22424222512' lon='-61.4610290049' />\n  <node id='-38997' action='modify' lat='15.17232549314' lon='-61.44070517736' />\n  <node id='-39000' action='modify' lat='15.27037968735' lon='-62.03129169537' />\n  <node id='-39001' action='modify' lat='15.28998503963' lon='-61.85076828602' />\n  <node id='-39018' action='modify' lat='15.44315360338' lon='-61.85223092531' />\n  <node id='-39020' action='modify' lat='15.16350333085' lon='-61.81780162649' />\n  <way id='-38986' action='modify'>\n    <nd ref='-38982' />\n    <nd ref='-38985' />\n    <nd ref='-38989' />\n    <nd ref='-38991' />\n    <nd ref='-38993' />\n    <nd ref='-38995' />\n    <nd ref='-38997' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-39002' action='modify'>\n    <nd ref='-39000' />\n    <nd ref='-39001' />\n    <nd ref='-38991' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-39019' action='modify'>\n    <nd ref='-39018' />\n    <nd ref='-39001' />\n    <nd ref='-39020' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/outsideConnectedOneWayWays.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='118919' timestamp='2013-05-01T20:02:30Z' uid='68904' user='Fredrik Christensson' version='4' changeset='15938546' lat='55.5908582' lon='12.7717941' />\n  <node id='118920' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='8' changeset='33971157' lat='55.5885731' lon='12.7779598' />\n  <node id='118921' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='8' changeset='33971157' lat='55.5874224' lon='12.7812217' />\n  <node id='118922' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='8' changeset='33971157' lat='55.5842981' lon='12.7907604' />\n  <node id='118923' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='8' changeset='33971157' lat='55.5815102' lon='12.8000856' />\n  <node id='118924' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='9' changeset='33971157' lat='55.5814964' lon='12.8001367' />\n  <node id='125398' timestamp='2015-09-11T22:02:38Z' uid='165061' user='mapper999' version='9' changeset='33971157' lat='55.5787981' lon='12.8105754' />\n  <node id='244466743' timestamp='2013-05-01T20:02:30Z' uid='68904' user='Fredrik Christensson' version='8' changeset='15938546' lat='55.5900813' lon='12.7738031' />\n  <node id='244466744' timestamp='2015-05-11T21:38:59Z' uid='306096' user='joakimfors' version='7' changeset='31019812' lat='55.5920236' lon='12.7690034' />\n  <node id='244466762' timestamp='2015-05-11T21:38:59Z' uid='306096' user='joakimfors' version='11' changeset='31019812' lat='55.5921007' lon='12.7691046' />\n  <node id='244466763' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='6' changeset='15938546' lat='55.5901607' lon='12.7738877' />\n  <node id='244466764' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5886527' lon='12.7780526' />\n  <node id='244466765' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5875032' lon='12.7813115' />\n  <node id='244466766' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5843807' lon='12.7908448' />\n  <node id='244466767' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5815963' lon='12.8001582' />\n  <node id='244466768' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5815825' lon='12.8002092' />\n  <node id='244466772' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5788863' lon='12.8106393' />\n  <node id='2288415943' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='1' changeset='15938546' lat='55.5909199' lon='12.7719051' />\n  <node id='2288415944' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='1' changeset='15938546' lat='55.5905035' lon='12.7729737' />\n  <node id='2288415946' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='1' changeset='15938546' lat='55.5914714' lon='12.7702711' />\n  <node id='2288415947' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='1' changeset='15938546' lat='55.5904262' lon='12.7728778' />\n  <node id='3089123457' timestamp='2015-09-11T22:02:40Z' uid='165061' user='mapper999' version='2' changeset='33971157' lat='55.5792796' lon='12.8090265' />\n  <node id='3089123458' timestamp='2015-09-11T22:02:40Z' uid='165061' user='mapper999' version='2' changeset='33971157' lat='55.5792106' lon='12.8088835' />\n  <node id='3516439688' timestamp='2015-09-11T22:02:41Z' uid='165061' user='mapper999' version='2' changeset='33971157' lat='55.5768698' lon='12.819016' />\n  <node id='3516439691' timestamp='2015-09-11T22:02:41Z' uid='165061' user='mapper999' version='2' changeset='33971157' lat='55.576958' lon='12.8190799' />\n  <node id='3739700862' timestamp='2015-09-11T22:00:32Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5768637' lon='12.8190422' />\n  <node id='3739700868' timestamp='2015-09-11T22:00:32Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.576952' lon='12.8191061' />\n  <node id='3739700877' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5773273' lon='12.8169407' />\n  <node id='3739700878' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5773385' lon='12.8168877' />\n  <node id='3739700883' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5774167' lon='12.8169997' />\n  <node id='3739700884' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5774278' lon='12.8169467' />\n  <node id='3739700893' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5778023' lon='12.8148236' />\n  <node id='3739700894' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5778144' lon='12.8147712' />\n  <node id='3739700899' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5778905' lon='12.8148875' />\n  <node id='3739700900' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5779027' lon='12.8148352' />\n  <node id='3739700909' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5782957' lon='12.812673' />\n  <node id='3739700910' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5783077' lon='12.8126206' />\n  <node id='3739700915' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5783841' lon='12.8127363' />\n  <node id='3739700916' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5783961' lon='12.8126839' />\n  <node id='3739700925' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5788102' lon='12.810523' />\n  <node id='3739700928' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5788984' lon='12.810587' />\n  <node id='3739700937' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5793114' lon='12.8084663' />\n  <node id='3739700938' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5793245' lon='12.8084147' />\n  <node id='3739700943' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5793984' lon='12.8085354' />\n  <node id='3739700944' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5794115' lon='12.8084838' />\n  <node id='3739700953' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5798358' lon='12.8063654' />\n  <node id='3739700954' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5798491' lon='12.806314' />\n  <node id='3739700959' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5799226' lon='12.8064355' />\n  <node id='3739700960' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5799358' lon='12.8063841' />\n  <node id='3739700969' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5803834' lon='12.8042547' />\n  <node id='3739700970' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5803965' lon='12.8042031' />\n  <node id='3739700975' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5804705' lon='12.8043237' />\n  <node id='3739700976' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5804835' lon='12.8042721' />\n  <node id='3739700985' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5809479' lon='12.8021324' />\n  <node id='3739700986' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5809615' lon='12.8020812' />\n  <node id='3739700991' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5810342' lon='12.8022042' />\n  <node id='3739700992' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5810478' lon='12.802153' />\n  <node id='3739701009' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5820408' lon='12.7982395' />\n  <node id='3739701010' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5820559' lon='12.7981896' />\n  <node id='3739701015' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.582125' lon='12.7983189' />\n  <node id='3739701016' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.58214' lon='12.798269' />\n  <node id='3739701125' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5825944' lon='12.7963513' />\n  <node id='3739701126' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5826093' lon='12.7963013' />\n  <node id='3739701131' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5826788' lon='12.79643' />\n  <node id='3739701132' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5826937' lon='12.7963799' />\n  <node id='3739701141' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5831563' lon='12.7944848' />\n  <node id='3739701142' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5831723' lon='12.7944358' />\n  <node id='3739701147' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5832389' lon='12.7945692' />\n  <node id='3739701148' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5832549' lon='12.7945202' />\n  <node id='3739701157' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5837129' lon='12.7926532' />\n  <node id='3739701158' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5837283' lon='12.7926036' />\n  <node id='3739701163' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5837966' lon='12.7927342' />\n  <node id='3739701164' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5838119' lon='12.7926846' />\n  <node id='3739701173' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5842821' lon='12.7908094' />\n  <node id='3739701176' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5843647' lon='12.7908938' />\n  <node id='3739701185' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.584899' lon='12.7888527' />\n  <node id='3739701186' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5849148' lon='12.7888035' />\n  <node id='3739701191' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5849821' lon='12.7889357' />\n  <node id='3739701192' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5849978' lon='12.7888864' />\n  <node id='3739701201' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5855018' lon='12.7869223' />\n  <node id='3739701202' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5855181' lon='12.7868735' />\n  <node id='3739701207' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.585584' lon='12.7870079' />\n  <node id='3739701208' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5856002' lon='12.7869592' />\n  <node id='3739701217' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5861449' lon='12.7849924' />\n  <node id='3739701218' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5861611' lon='12.7849437' />\n  <node id='3739701223' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5862272' lon='12.7850778' />\n  <node id='3739701224' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5862433' lon='12.785029' />\n  <node id='3739701233' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.586768' lon='12.7831184' />\n  <node id='3739701234' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.586785' lon='12.7830705' />\n  <node id='3739701239' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5868487' lon='12.7832082' />\n  <node id='3739701240' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5868657' lon='12.7831603' />\n  <node id='3739701249' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5874394' lon='12.7811738' />\n  <node id='3739701252' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5875202' lon='12.7812636' />\n  <node id='3739701261' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5879979' lon='12.77958' />\n  <node id='3739701262' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5880152' lon='12.7795324' />\n  <node id='3739701267' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5880781' lon='12.7796713' />\n  <node id='3739701268' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5880954' lon='12.7796237' />\n  <node id='3739701277' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5885906' lon='12.7779126' />\n  <node id='3739701280' timestamp='2015-09-11T22:00:38Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5886703' lon='12.7780053' />\n  <way id='23283662' timestamp='2017-03-08T11:12:02Z' uid='339581' user='nyuriks' version='24' changeset='46677566'>\n    <nd ref='244466744' />\n    <nd ref='2288415946' />\n    <nd ref='118919' />\n    <nd ref='2288415947' />\n    <nd ref='244466743' />\n    <nd ref='3739701277' />\n    <nd ref='118920' />\n    <nd ref='3739701262' />\n    <nd ref='3739701261' />\n    <nd ref='3739701249' />\n    <nd ref='118921' />\n    <nd ref='3739701234' />\n    <nd ref='3739701233' />\n    <nd ref='3739701218' />\n    <nd ref='3739701217' />\n    <nd ref='3739701202' />\n    <nd ref='3739701201' />\n    <nd ref='3739701186' />\n    <nd ref='3739701185' />\n    <nd ref='118922' />\n    <nd ref='3739701173' />\n    <nd ref='3739701158' />\n    <nd ref='3739701157' />\n    <nd ref='3739701142' />\n    <nd ref='3739701141' />\n    <nd ref='3739701126' />\n    <nd ref='3739701125' />\n    <nd ref='3739701010' />\n    <nd ref='3739701009' />\n    <nd ref='118923' />\n    <nd ref='118924' />\n    <nd ref='3739700986' />\n    <nd ref='3739700985' />\n    <nd ref='3739700970' />\n    <nd ref='3739700969' />\n    <nd ref='3739700954' />\n    <nd ref='3739700953' />\n    <nd ref='3739700938' />\n    <nd ref='3739700937' />\n    <nd ref='3089123458' />\n    <tag k='bridge' v='yes' />\n    <tag k='bridge:structure' v='beam' />\n    <tag k='bridge:wikipedia' v='en:Øresund Bridge' />\n    <tag k='communication' v='line' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='E 20' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='signals' />\n    <tag k='name' v='Øresundsmotorvejen' />\n    <tag k='name:bridge' v='Øresundsbroen' />\n    <tag k='name:bridge:da' v='Øresundsbroen' />\n    <tag k='name:bridge:de' v='Öresundbrücke' />\n    <tag k='name:bridge:en' v='Øresund Bridge' />\n    <tag k='name:bridge:sv' v='Öresundsbron' />\n    <tag k='official_name:bridge' v='Øresundsbron' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='E 20' />\n    <tag k='shoulder' v='right' />\n    <tag k='source:maxspeed' v='sign' />\n    <tag k='start_date' v='2000-06' />\n    <tag k='surface' v='asphalt' />\n    <tag k='toll' v='yes' />\n  </way>\n  <way id='304383858' timestamp='2017-03-08T11:12:05Z' uid='339581' user='nyuriks' version='5' changeset='46677566'>\n    <nd ref='3089123457' />\n    <nd ref='3739700943' />\n    <nd ref='3739700944' />\n    <nd ref='3739700959' />\n    <nd ref='3739700960' />\n    <nd ref='3739700975' />\n    <nd ref='3739700976' />\n    <nd ref='3739700991' />\n    <nd ref='3739700992' />\n    <nd ref='244466768' />\n    <nd ref='244466767' />\n    <nd ref='3739701015' />\n    <nd ref='3739701016' />\n    <nd ref='3739701131' />\n    <nd ref='3739701132' />\n    <nd ref='3739701147' />\n    <nd ref='3739701148' />\n    <nd ref='3739701163' />\n    <nd ref='3739701164' />\n    <nd ref='3739701176' />\n    <nd ref='244466766' />\n    <nd ref='3739701191' />\n    <nd ref='3739701192' />\n    <nd ref='3739701207' />\n    <nd ref='3739701208' />\n    <nd ref='3739701223' />\n    <nd ref='3739701224' />\n    <nd ref='3739701239' />\n    <nd ref='3739701240' />\n    <nd ref='244466765' />\n    <nd ref='3739701252' />\n    <nd ref='3739701267' />\n    <nd ref='3739701268' />\n    <nd ref='244466764' />\n    <nd ref='3739701280' />\n    <nd ref='244466763' />\n    <nd ref='2288415944' />\n    <nd ref='2288415943' />\n    <nd ref='244466762' />\n    <tag k='bridge' v='yes' />\n    <tag k='bridge:structure' v='beam' />\n    <tag k='bridge:wikipedia' v='en:Øresund Bridge' />\n    <tag k='communication' v='line' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='E 20' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='110' />\n    <tag k='name' v='Øresundsmotorvejen' />\n    <tag k='name:bridge' v='Øresundsbroen' />\n    <tag k='name:bridge:da' v='Øresundsbroen' />\n    <tag k='name:bridge:de' v='Öresundbrücke' />\n    <tag k='name:bridge:en' v='Øresund Bridge' />\n    <tag k='name:bridge:sv' v='Öresundsbron' />\n    <tag k='official_name:bridge' v='Øresundsbron' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='E 20' />\n    <tag k='shoulder' v='right' />\n    <tag k='start_date' v='2000-06' />\n    <tag k='surface' v='asphalt' />\n    <tag k='toll' v='yes' />\n  </way>\n  <way id='304383859' timestamp='2017-03-08T11:12:05Z' uid='339581' user='nyuriks' version='6' changeset='46677566'>\n    <nd ref='3089123458' />\n    <nd ref='3739700925' />\n    <nd ref='125398' />\n    <nd ref='3739700910' />\n    <nd ref='3739700909' />\n    <nd ref='3739700894' />\n    <nd ref='3739700893' />\n    <nd ref='3739700878' />\n    <nd ref='3739700877' />\n    <nd ref='3516439688' />\n    <nd ref='3739700862' />\n    <tag k='bridge' v='yes' />\n    <tag k='bridge:structure' v='beam' />\n    <tag k='bridge:wikipedia' v='en:Øresund Bridge' />\n    <tag k='communication' v='line' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='E 20' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='signals' />\n    <tag k='name:bridge' v='Öresundsbron' />\n    <tag k='name:bridge:da' v='Øresundsbroen' />\n    <tag k='name:bridge:de' v='Öresundbrücke' />\n    <tag k='name:bridge:en' v='Øresund Bridge' />\n    <tag k='name:bridge:sv' v='Öresundsbron' />\n    <tag k='name:da' v='Øresundsmotorvejen' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='E 20' />\n    <tag k='shoulder' v='right' />\n    <tag k='source:maxspeed' v='sign' />\n    <tag k='start_date' v='2000-06' />\n    <tag k='surface' v='asphalt' />\n    <tag k='toll' v='yes' />\n  </way>\n  <way id='370255937' timestamp='2017-03-08T11:12:07Z' uid='339581' user='nyuriks' version='2' changeset='46677566'>\n    <nd ref='3739700868' />\n    <nd ref='3516439691' />\n    <nd ref='3739700883' />\n    <nd ref='3739700884' />\n    <nd ref='3739700899' />\n    <nd ref='3739700900' />\n    <nd ref='3739700915' />\n    <nd ref='3739700916' />\n    <nd ref='244466772' />\n    <nd ref='3739700928' />\n    <nd ref='3089123457' />\n    <tag k='bridge' v='yes' />\n    <tag k='bridge:structure' v='beam' />\n    <tag k='bridge:wikipedia' v='en:Øresund Bridge' />\n    <tag k='communication' v='line' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='E 20' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='110' />\n    <tag k='name:bridge' v='Öresundsbron' />\n    <tag k='name:bridge:da' v='Øresundsbroen' />\n    <tag k='name:bridge:de' v='Öresundbrücke' />\n    <tag k='name:bridge:en' v='Øresund Bridge' />\n    <tag k='name:bridge:sv' v='Öresundsbron' />\n    <tag k='name:da' v='Øresundsmotorvejen' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='E 20' />\n    <tag k='shoulder' v='right' />\n    <tag k='start_date' v='2000-06' />\n    <tag k='surface' v='asphalt' />\n    <tag k='toll' v='yes' />\n  </way>\n  <relation id='2527989' timestamp='2018-05-22T07:13:00Z' uid='649780' user='Lytter1' version='90' changeset='59171438'>\n    <member type='way' ref='376078641' role='' />\n    <member type='way' ref='4818489' role='' />\n    <member type='way' ref='24863369' role='' />\n    <member type='way' ref='1212314' role='' />\n    <member type='way' ref='104375005' role='' />\n    <member type='way' ref='498651687' role='' />\n    <member type='way' ref='168424779' role='' />\n    <member type='way' ref='2953355' role='' />\n    <member type='way' ref='445296135' role='' />\n    <member type='way' ref='242700158' role='forward' />\n    <member type='way' ref='179942505' role='forward' />\n    <member type='way' ref='128770707' role='' />\n    <member type='way' ref='128770706' role='' />\n    <member type='way' ref='137307990' role='forward' />\n    <member type='way' ref='137307986' role='forward' />\n    <member type='way' ref='128768514' role='' />\n    <member type='way' ref='132454679' role='forward' />\n    <member type='way' ref='137726107' role='forward' />\n    <member type='way' ref='128828751' role='forward' />\n    <member type='way' ref='4825750' role='' />\n    <member type='way' ref='7382944' role='forward' />\n    <member type='way' ref='139170766' role='forward' />\n    <member type='way' ref='54938550' role='forward' />\n    <member type='way' ref='8133164' role='forward' />\n    <member type='way' ref='25612631' role='forward' />\n    <member type='way' ref='500769231' role='forward' />\n    <member type='way' ref='30427258' role='forward' />\n    <member type='way' ref='184108776' role='forward' />\n    <member type='way' ref='184108777' role='forward' />\n    <member type='way' ref='4048633' role='forward' />\n    <member type='way' ref='51752796' role='forward' />\n    <member type='way' ref='51752798' role='forward' />\n    <member type='way' ref='8133170' role='forward' />\n    <member type='way' ref='479360542' role='forward' />\n    <member type='way' ref='122301304' role='forward' />\n    <member type='way' ref='122301302' role='forward' />\n    <member type='way' ref='417962858' role='forward' />\n    <member type='way' ref='51752799' role='forward' />\n    <member type='way' ref='25612725' role='forward' />\n    <member type='way' ref='274704228' role='forward' />\n    <member type='way' ref='291364554' role='forward' />\n    <member type='way' ref='291364555' role='forward' />\n    <member type='way' ref='24163722' role='forward' />\n    <member type='way' ref='24163723' role='forward' />\n    <member type='way' ref='274704230' role='forward' />\n    <member type='way' ref='4586711' role='forward' />\n    <member type='way' ref='4586713' role='forward' />\n    <member type='way' ref='13237921' role='forward' />\n    <member type='way' ref='369727414' role='forward' />\n    <member type='way' ref='590045020' role='forward' />\n    <member type='way' ref='4566412' role='forward' />\n    <member type='way' ref='4587474' role='forward' />\n    <member type='way' ref='361190491' role='forward' />\n    <member type='way' ref='361190492' role='forward' />\n    <member type='way' ref='116040324' role='forward' />\n    <member type='way' ref='334033771' role='forward' />\n    <member type='way' ref='369719169' role='forward' />\n    <member type='way' ref='13243176' role='forward' />\n    <member type='way' ref='42949538' role='forward' />\n    <member type='way' ref='334028997' role='forward' />\n    <member type='way' ref='334028998' role='forward' />\n    <member type='way' ref='308311666' role='forward' />\n    <member type='way' ref='334031713' role='forward' />\n    <member type='way' ref='334031718' role='forward' />\n    <member type='way' ref='368973701' role='forward' />\n    <member type='way' ref='368973704' role='forward' />\n    <member type='way' ref='13243197' role='forward' />\n    <member type='way' ref='24459701' role='forward' />\n    <member type='way' ref='368974736' role='forward' />\n    <member type='way' ref='370182172' role='forward' />\n    <member type='way' ref='368974738' role='forward' />\n    <member type='way' ref='370182860' role='forward' />\n    <member type='way' ref='370182861' role='forward' />\n    <member type='way' ref='370184496' role='forward' />\n    <member type='way' ref='370184497' role='forward' />\n    <member type='way' ref='414187449' role='forward' />\n    <member type='way' ref='7997077' role='forward' />\n    <member type='way' ref='8043373' role='forward' />\n    <member type='way' ref='9479546' role='forward' />\n    <member type='way' ref='9479547' role='forward' />\n    <member type='way' ref='414187439' role='forward' />\n    <member type='way' ref='37169725' role='forward' />\n    <member type='way' ref='314939367' role='forward' />\n    <member type='way' ref='103459023' role='forward' />\n    <member type='way' ref='103459014' role='forward' />\n    <member type='way' ref='8023900' role='forward' />\n    <member type='way' ref='4587430' role='forward' />\n    <member type='way' ref='4360600' role='forward' />\n    <member type='way' ref='4360601' role='forward' />\n    <member type='way' ref='4360602' role='forward' />\n    <member type='way' ref='4360603' role='forward' />\n    <member type='way' ref='8242842' role='forward' />\n    <member type='way' ref='458678237' role='forward' />\n    <member type='way' ref='458678241' role='forward' />\n    <member type='way' ref='28172234' role='forward' />\n    <member type='way' ref='24455311' role='forward' />\n    <member type='way' ref='120873680' role='forward' />\n    <member type='way' ref='24455310' role='forward' />\n    <member type='way' ref='210247619' role='forward' />\n    <member type='way' ref='169849344' role='forward' />\n    <member type='way' ref='169849345' role='forward' />\n    <member type='way' ref='107689033' role='forward' />\n    <member type='way' ref='157725815' role='forward' />\n    <member type='way' ref='221503636' role='forward' />\n    <member type='way' ref='221503632' role='forward' />\n    <member type='way' ref='24417571' role='forward' />\n    <member type='way' ref='24417572' role='forward' />\n    <member type='way' ref='134384298' role='forward' />\n    <member type='way' ref='134384296' role='forward' />\n    <member type='way' ref='225472290' role='forward' />\n    <member type='way' ref='26758966' role='forward' />\n    <member type='way' ref='366095663' role='forward' />\n    <member type='way' ref='120081163' role='forward' />\n    <member type='way' ref='120081165' role='forward' />\n    <member type='way' ref='421008065' role='forward' />\n    <member type='way' ref='421008067' role='forward' />\n    <member type='way' ref='24418157' role='forward' />\n    <member type='way' ref='24418158' role='forward' />\n    <member type='way' ref='547986509' role='forward' />\n    <member type='way' ref='174681249' role='forward' />\n    <member type='way' ref='117749881' role='forward' />\n    <member type='way' ref='421008062' role='forward' />\n    <member type='way' ref='117749816' role='forward' />\n    <member type='way' ref='117749852' role='forward' />\n    <member type='way' ref='26213722' role='forward' />\n    <member type='way' ref='26213727' role='forward' />\n    <member type='way' ref='10653126' role='forward' />\n    <member type='way' ref='10653127' role='forward' />\n    <member type='way' ref='158408255' role='forward' />\n    <member type='way' ref='158408257' role='forward' />\n    <member type='way' ref='421008063' role='forward' />\n    <member type='way' ref='163967203' role='forward' />\n    <member type='way' ref='2041265' role='forward' />\n    <member type='way' ref='219657806' role='forward' />\n    <member type='way' ref='219657811' role='forward' />\n    <member type='way' ref='23000641' role='forward' />\n    <member type='way' ref='98479074' role='forward' />\n    <member type='way' ref='98479020' role='forward' />\n    <member type='way' ref='23000640' role='forward' />\n    <member type='way' ref='29057944' role='forward' />\n    <member type='way' ref='386834390' role='forward' />\n    <member type='way' ref='110361079' role='forward' />\n    <member type='way' ref='357265308' role='forward' />\n    <member type='way' ref='357265307' role='forward' />\n    <member type='way' ref='140293283' role='forward' />\n    <member type='way' ref='357265304' role='forward' />\n    <member type='way' ref='30791075' role='forward' />\n    <member type='way' ref='119719505' role='forward' />\n    <member type='way' ref='30791222' role='forward' />\n    <member type='way' ref='386834391' role='forward' />\n    <member type='way' ref='219657896' role='forward' />\n    <member type='way' ref='107382804' role='forward' />\n    <member type='way' ref='357266386' role='forward' />\n    <member type='way' ref='357266387' role='forward' />\n    <member type='way' ref='386834393' role='forward' />\n    <member type='way' ref='219657899' role='forward' />\n    <member type='way' ref='370256037' role='forward' />\n    <member type='way' ref='22771793' role='forward' />\n    <member type='way' ref='22771518' role='forward' />\n    <member type='way' ref='370256030' role='forward' />\n    <member type='way' ref='23283662' role='forward' />\n    <member type='way' ref='30826997' role='forward' />\n    <member type='way' ref='25612637' role='forward' />\n    <member type='way' ref='303576289' role='forward' />\n    <member type='way' ref='500769236' role='forward' />\n    <member type='way' ref='51752793' role='forward' />\n    <member type='way' ref='484621252' role='forward' />\n    <member type='way' ref='184108774' role='forward' />\n    <member type='way' ref='184108775' role='forward' />\n    <member type='way' ref='4048475' role='forward' />\n    <member type='way' ref='51752795' role='forward' />\n    <member type='way' ref='28173859' role='forward' />\n    <member type='way' ref='4048632' role='forward' />\n    <member type='way' ref='417962853' role='forward' />\n    <member type='way' ref='291364553' role='forward' />\n    <member type='way' ref='286336652' role='forward' />\n    <member type='way' ref='286336653' role='forward' />\n    <member type='way' ref='4586925' role='forward' />\n    <member type='way' ref='24163724' role='forward' />\n    <member type='way' ref='26713014' role='forward' />\n    <member type='way' ref='4586931' role='forward' />\n    <member type='way' ref='28173456' role='forward' />\n    <member type='way' ref='28173444' role='forward' />\n    <member type='way' ref='4586712' role='forward' />\n    <member type='way' ref='369727413' role='forward' />\n    <member type='way' ref='2018400' role='forward' />\n    <member type='way' ref='2018724' role='forward' />\n    <member type='way' ref='361190701' role='forward' />\n    <member type='way' ref='361190702' role='forward' />\n    <member type='way' ref='299648362' role='forward' />\n    <member type='way' ref='334034697' role='forward' />\n    <member type='way' ref='334033957' role='forward' />\n    <member type='way' ref='334033956' role='forward' />\n    <member type='way' ref='334027688' role='forward' />\n    <member type='way' ref='116040317' role='forward' />\n    <member type='way' ref='13243181' role='forward' />\n    <member type='way' ref='368973703' role='forward' />\n    <member type='way' ref='368973702' role='forward' />\n    <member type='way' ref='368796350' role='forward' />\n    <member type='way' ref='368796349' role='forward' />\n    <member type='way' ref='42949541' role='forward' />\n    <member type='way' ref='13243193' role='forward' />\n    <member type='way' ref='368969987' role='forward' />\n    <member type='way' ref='368974743' role='forward' />\n    <member type='way' ref='368974733' role='forward' />\n    <member type='way' ref='368974740' role='forward' />\n    <member type='way' ref='368974730' role='forward' />\n    <member type='way' ref='414187434' role='forward' />\n    <member type='way' ref='4587467' role='forward' />\n    <member type='way' ref='7997076' role='forward' />\n    <member type='way' ref='8043375' role='forward' />\n    <member type='way' ref='9479549' role='forward' />\n    <member type='way' ref='414187457' role='forward' />\n    <member type='way' ref='37169736' role='forward' />\n    <member type='way' ref='495538607' role='forward' />\n    <member type='way' ref='495538608' role='forward' />\n    <member type='way' ref='37169713' role='forward' />\n    <member type='way' ref='314939368' role='forward' />\n    <member type='way' ref='103459022' role='forward' />\n    <member type='way' ref='103459027' role='forward' />\n    <member type='way' ref='4587463' role='forward' />\n    <member type='way' ref='8023902' role='forward' />\n    <member type='way' ref='4587429' role='forward' />\n    <member type='way' ref='2032610' role='forward' />\n    <member type='way' ref='4360592' role='forward' />\n    <member type='way' ref='4360591' role='forward' />\n    <member type='way' ref='4360590' role='forward' />\n    <member type='way' ref='458678238' role='forward' />\n    <member type='way' ref='458678239' role='forward' />\n    <member type='way' ref='4360596' role='forward' />\n    <member type='way' ref='495531791' role='forward' />\n    <member type='way' ref='299656111' role='forward' />\n    <member type='way' ref='24455313' role='forward' />\n    <member type='way' ref='24455312' role='forward' />\n    <member type='way' ref='209693190' role='forward' />\n    <member type='way' ref='209693189' role='forward' />\n    <member type='way' ref='170129854' role='forward' />\n    <member type='way' ref='170129851' role='forward' />\n    <member type='way' ref='120860455' role='forward' />\n    <member type='way' ref='120860456' role='forward' />\n    <member type='way' ref='209693188' role='forward' />\n    <member type='way' ref='37169712' role='forward' />\n    <member type='way' ref='221503634' role='forward' />\n    <member type='way' ref='157725807' role='forward' />\n    <member type='way' ref='24417569' role='forward' />\n    <member type='way' ref='134384294' role='forward' />\n    <member type='way' ref='134384292' role='forward' />\n    <member type='way' ref='37169704' role='forward' />\n    <member type='way' ref='475124867' role='forward' />\n    <member type='way' ref='225472291' role='forward' />\n    <member type='way' ref='26758987' role='forward' />\n    <member type='way' ref='24418154' role='forward' />\n    <member type='way' ref='24418152' role='forward' />\n    <member type='way' ref='24418161' role='forward' />\n    <member type='way' ref='421008064' role='forward' />\n    <member type='way' ref='421008066' role='forward' />\n    <member type='way' ref='24418160' role='forward' />\n    <member type='way' ref='174681248' role='forward' />\n    <member type='way' ref='417614542' role='forward' />\n    <member type='way' ref='180308200' role='forward' />\n    <member type='way' ref='26213730' role='forward' />\n    <member type='way' ref='10653129' role='forward' />\n    <member type='way' ref='10653128' role='forward' />\n    <member type='way' ref='158408256' role='forward' />\n    <member type='way' ref='158408254' role='forward' />\n    <member type='way' ref='5055938' role='forward' />\n    <member type='way' ref='271780205' role='forward' />\n    <member type='way' ref='23000642' role='forward' />\n    <member type='way' ref='98479068' role='forward' />\n    <member type='way' ref='98479044' role='forward' />\n    <member type='way' ref='23000643' role='forward' />\n    <member type='way' ref='25614079' role='forward' />\n    <member type='way' ref='27224363' role='forward' />\n    <member type='way' ref='386834388' role='forward' />\n    <member type='way' ref='357265310' role='forward' />\n    <member type='way' ref='357265309' role='forward' />\n    <member type='way' ref='357265305' role='forward' />\n    <member type='way' ref='140293280' role='forward' />\n    <member type='way' ref='30791016' role='forward' />\n    <member type='way' ref='119719508' role='forward' />\n    <member type='way' ref='30791228' role='forward' />\n    <member type='way' ref='386834392' role='forward' />\n    <member type='way' ref='357266388' role='forward' />\n    <member type='way' ref='357266389' role='forward' />\n    <member type='way' ref='107382822' role='forward' />\n    <member type='way' ref='386801138' role='forward' />\n    <member type='way' ref='370256035' role='forward' />\n    <member type='way' ref='4517829' role='forward' />\n    <member type='way' ref='22771792' role='forward' />\n    <member type='way' ref='370256031' role='forward' />\n    <member type='way' ref='23283660' role='forward' />\n    <member type='way' ref='304383858' role='forward' />\n    <tag k='name' v='E 20 Denmark' />\n    <tag k='network' v='e-road' />\n    <tag k='ref' v='E 20' />\n    <tag k='route' v='road' />\n    <tag k='section' v='Denmark' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='5794315' timestamp='2018-08-18T21:14:24Z' uid='35601' user='magol' version='87' changeset='61780464'>\n    <member type='node' ref='1938160930' role='platform' />\n    <member type='node' ref='1310612418' role='platform' />\n    <member type='node' ref='1998995667' role='stop' />\n    <member type='node' ref='221169938' role='stop' />\n    <member type='node' ref='3418254623' role='stop' />\n    <member type='node' ref='4128416085' role='stop' />\n    <member type='node' ref='416908979' role='stop' />\n    <member type='node' ref='394660560' role='stop' />\n    <member type='way' ref='189255217' role='platform' />\n    <member type='way' ref='166526295' role='platform' />\n    <member type='way' ref='334753833' role='platform' />\n    <member type='way' ref='411108616' role='platform' />\n    <member type='node' ref='5313132106' role='stop' />\n    <member type='way' ref='391105368' role='platform' />\n    <member type='way' ref='435653432' role='' />\n    <member type='way' ref='515410740' role='' />\n    <member type='way' ref='38702782' role='' />\n    <member type='way' ref='38702777' role='' />\n    <member type='way' ref='38702744' role='' />\n    <member type='way' ref='85735262' role='' />\n    <member type='way' ref='168269172' role='' />\n    <member type='way' ref='74865047' role='' />\n    <member type='way' ref='411057613' role='' />\n    <member type='way' ref='85735265' role='' />\n    <member type='way' ref='495915484' role='' />\n    <member type='way' ref='298767466' role='' />\n    <member type='way' ref='2371124' role='' />\n    <member type='way' ref='295221437' role='' />\n    <member type='way' ref='495915487' role='' />\n    <member type='way' ref='495915485' role='' />\n    <member type='way' ref='495915489' role='' />\n    <member type='way' ref='27225997' role='' />\n    <member type='way' ref='2371125' role='' />\n    <member type='way' ref='416688425' role='' />\n    <member type='way' ref='364793653' role='' />\n    <member type='way' ref='416688419' role='' />\n    <member type='way' ref='422789570' role='' />\n    <member type='way' ref='1880588' role='' />\n    <member type='way' ref='161633350' role='' />\n    <member type='way' ref='422789568' role='' />\n    <member type='way' ref='422789556' role='' />\n    <member type='way' ref='422789562' role='' />\n    <member type='way' ref='422789555' role='' />\n    <member type='way' ref='422789566' role='' />\n    <member type='way' ref='422789567' role='' />\n    <member type='way' ref='422789564' role='' />\n    <member type='way' ref='422789559' role='' />\n    <member type='way' ref='422789544' role='' />\n    <member type='way' ref='1880585' role='' />\n    <member type='way' ref='422789542' role='' />\n    <member type='way' ref='237108788' role='' />\n    <member type='way' ref='108848990' role='' />\n    <member type='way' ref='108849099' role='' />\n    <member type='way' ref='422789540' role='' />\n    <member type='way' ref='108849074' role='' />\n    <member type='way' ref='422789572' role='' />\n    <member type='way' ref='108849037' role='' />\n    <member type='way' ref='386834370' role='' />\n    <member type='way' ref='2040973' role='' />\n    <member type='way' ref='24418176' role='' />\n    <member type='way' ref='24418177' role='' />\n    <member type='way' ref='24418174' role='' />\n    <member type='way' ref='24418175' role='' />\n    <member type='way' ref='257636641' role='' />\n    <member type='way' ref='110361079' role='' />\n    <member type='way' ref='357265308' role='' />\n    <member type='way' ref='357265307' role='' />\n    <member type='way' ref='140293283' role='' />\n    <member type='way' ref='357265304' role='' />\n    <member type='way' ref='30791075' role='' />\n    <member type='way' ref='119719505' role='' />\n    <member type='way' ref='30791222' role='' />\n    <member type='way' ref='386834391' role='' />\n    <member type='way' ref='375993929' role='' />\n    <member type='way' ref='375993994' role='' />\n    <member type='way' ref='121198957' role='' />\n    <member type='way' ref='388070394' role='' />\n    <member type='way' ref='386834382' role='' />\n    <member type='way' ref='557874837' role='' />\n    <member type='way' ref='1880523' role='' />\n    <member type='way' ref='4373482' role='' />\n    <member type='way' ref='488272306' role='' />\n    <member type='way' ref='233833233' role='' />\n    <member type='way' ref='488272308' role='' />\n    <member type='way' ref='488272310' role='' />\n    <member type='way' ref='25604858' role='' />\n    <member type='way' ref='4373483' role='' />\n    <member type='way' ref='488272315' role='' />\n    <member type='way' ref='488272320' role='' />\n    <member type='way' ref='296891697' role='' />\n    <member type='way' ref='488272326' role='' />\n    <member type='way' ref='488272325' role='' />\n    <member type='way' ref='4373480' role='' />\n    <member type='way' ref='497022315' role='' />\n    <member type='way' ref='38069237' role='' />\n    <member type='way' ref='92162861' role='' />\n    <member type='way' ref='38069236' role='' />\n    <member type='way' ref='107382801' role='' />\n    <member type='way' ref='219657899' role='' />\n    <member type='way' ref='370256037' role='' />\n    <member type='way' ref='22771793' role='' />\n    <member type='way' ref='22771518' role='' />\n    <member type='way' ref='370256030' role='' />\n    <member type='way' ref='23283662' role='' />\n    <member type='way' ref='304383859' role='' />\n    <member type='way' ref='370255938' role='' />\n    <member type='way' ref='370255940' role='' />\n    <member type='way' ref='148500436' role='' />\n    <member type='way' ref='219657906' role='' />\n    <member type='way' ref='148899471' role='' />\n    <member type='way' ref='148899453' role='' />\n    <member type='way' ref='219657860' role='' />\n    <member type='way' ref='148899506' role='' />\n    <member type='way' ref='219657822' role='' />\n    <member type='way' ref='28289483' role='' />\n    <member type='way' ref='28289485' role='' />\n    <member type='way' ref='25046566' role='' />\n    <member type='way' ref='25046567' role='' />\n    <member type='way' ref='119253153' role='' />\n    <member type='way' ref='119253150' role='' />\n    <member type='way' ref='148504529' role='' />\n    <member type='way' ref='148504691' role='' />\n    <member type='way' ref='148504690' role='' />\n    <member type='way' ref='460408774' role='' />\n    <member type='way' ref='15729150' role='' />\n    <member type='way' ref='4518071' role='' />\n    <member type='way' ref='15729146' role='' />\n    <member type='way' ref='149178397' role='' />\n    <member type='way' ref='149178399' role='' />\n    <member type='way' ref='23423532' role='' />\n    <member type='way' ref='23423535' role='' />\n    <member type='way' ref='23423536' role='' />\n    <member type='way' ref='32090567' role='' />\n    <member type='way' ref='23423527' role='' />\n    <member type='way' ref='388070341' role='' />\n    <member type='way' ref='28274464' role='' />\n    <member type='way' ref='154229121' role='' />\n    <member type='way' ref='231390456' role='' />\n    <member type='way' ref='388070255' role='' />\n    <member type='way' ref='71933892' role='' />\n    <member type='way' ref='71933999' role='' />\n    <member type='way' ref='200184219' role='' />\n    <member type='way' ref='388070368' role='' />\n    <member type='way' ref='23423400' role='' />\n    <member type='way' ref='28552686' role='' />\n    <member type='way' ref='28552687' role='' />\n    <member type='way' ref='52008666' role='' />\n    <member type='way' ref='28552872' role='' />\n    <member type='way' ref='453128278' role='' />\n    <member type='way' ref='388070410' role='' />\n    <member type='way' ref='28562024' role='' />\n    <member type='way' ref='585774520' role='' />\n    <member type='way' ref='22763845' role='' />\n    <member type='way' ref='585774519' role='' />\n    <member type='way' ref='136913725' role='' />\n    <member type='way' ref='136913721' role='' />\n    <member type='way' ref='139667343' role='' />\n    <member type='way' ref='608281812' role='' />\n    <member type='way' ref='24161692' role='' />\n    <member type='way' ref='28302725' role='' />\n    <member type='way' ref='28302737' role='' />\n    <member type='way' ref='139669589' role='' />\n    <member type='way' ref='189255272' role='' />\n    <member type='way' ref='4640795' role='' />\n    <member type='way' ref='24214517' role='' />\n    <member type='way' ref='189255264' role='' />\n    <member type='way' ref='28338448' role='' />\n    <member type='way' ref='139679569' role='' />\n    <member type='way' ref='139659116' role='' />\n    <member type='way' ref='39460624' role='' />\n    <member type='way' ref='267133182' role='' />\n    <member type='way' ref='249170713' role='' />\n    <member type='way' ref='267133185' role='' />\n    <member type='way' ref='244457557' role='' />\n    <member type='way' ref='267133187' role='' />\n    <member type='way' ref='186634110' role='' />\n    <member type='way' ref='4642565' role='' />\n    <member type='way' ref='166514567' role='' />\n    <member type='way' ref='139667341' role='' />\n    <member type='way' ref='28290415' role='' />\n    <member type='way' ref='28290416' role='' />\n    <member type='way' ref='203023143' role='' />\n    <member type='way' ref='139679569' role='' />\n    <member type='way' ref='139659116' role='' />\n    <member type='way' ref='139659168' role='' />\n    <member type='way' ref='27647130' role='' />\n    <member type='way' ref='184362450' role='' />\n    <member type='way' ref='421889250' role='' />\n    <member type='way' ref='148521204' role='' />\n    <member type='way' ref='386655217' role='' />\n    <member type='way' ref='148521201' role='' />\n    <member type='way' ref='136775917' role='' />\n    <member type='way' ref='136775919' role='' />\n    <member type='way' ref='267133193' role='' />\n    <member type='way' ref='136773614' role='' />\n    <member type='way' ref='160904778' role='' />\n    <member type='way' ref='160904780' role='' />\n    <member type='way' ref='160904785' role='' />\n    <member type='way' ref='136773630' role='' />\n    <member type='way' ref='389936829' role='' />\n    <member type='way' ref='136531746' role='' />\n    <member type='way' ref='4453820' role='' />\n    <member type='way' ref='299536952' role='' />\n    <member type='way' ref='364258995' role='' />\n    <member type='way' ref='27742622' role='' />\n    <member type='way' ref='23401300' role='' />\n    <member type='way' ref='268727924' role='' />\n    <member type='way' ref='301890310' role='' />\n    <member type='way' ref='4453848' role='' />\n    <member type='way' ref='25031742' role='' />\n    <member type='way' ref='25031743' role='' />\n    <member type='way' ref='301850669' role='' />\n    <member type='way' ref='284982062' role='' />\n    <member type='way' ref='28113365' role='' />\n    <member type='way' ref='28113364' role='' />\n    <member type='way' ref='4453889' role='' />\n    <member type='way' ref='28846609' role='' />\n    <member type='way' ref='27405304' role='' />\n    <member type='way' ref='136754001' role='' />\n    <member type='way' ref='136754000' role='' />\n    <member type='way' ref='4453934' role='' />\n    <member type='way' ref='52290587' role='' />\n    <member type='way' ref='242886410' role='' />\n    <member type='way' ref='25707299' role='' />\n    <member type='way' ref='4454227' role='' />\n    <member type='way' ref='51874399' role='' />\n    <member type='way' ref='45874984' role='' />\n    <member type='way' ref='45875120' role='' />\n    <member type='way' ref='45875006' role='' />\n    <member type='way' ref='173657136' role='' />\n    <member type='way' ref='173657135' role='' />\n    <member type='way' ref='52087179' role='' />\n    <member type='way' ref='29986641' role='' />\n    <member type='way' ref='37878911' role='' />\n    <member type='way' ref='37878913' role='' />\n    <member type='way' ref='4454239' role='' />\n    <member type='way' ref='23367235' role='' />\n    <member type='way' ref='384729224' role='' />\n    <member type='way' ref='51874402' role='' />\n    <member type='way' ref='202953147' role='' />\n    <member type='way' ref='244503056' role='' />\n    <member type='way' ref='157425720' role='' />\n    <member type='way' ref='202953146' role='' />\n    <member type='way' ref='87990595' role='' />\n    <member type='way' ref='85132102' role='' />\n    <member type='way' ref='202953145' role='' />\n    <member type='way' ref='23294052' role='' />\n    <member type='way' ref='387809392' role='' />\n    <member type='way' ref='467758794' role='' />\n    <member type='way' ref='467758793' role='' />\n    <member type='way' ref='20691438' role='' />\n    <member type='way' ref='88308018' role='' />\n    <member type='way' ref='388070319' role='' />\n    <member type='way' ref='88308014' role='' />\n    <member type='way' ref='185123359' role='' />\n    <member type='way' ref='220784310' role='' />\n    <member type='way' ref='185123358' role='' />\n    <member type='way' ref='29983679' role='' />\n    <member type='way' ref='90962729' role='' />\n    <member type='way' ref='532521847' role='' />\n    <member type='way' ref='147007342' role='' />\n    <member type='way' ref='23392587' role='' />\n    <member type='way' ref='232389294' role='' />\n    <member type='way' ref='95927588' role='' />\n    <member type='way' ref='232389292' role='' />\n    <member type='way' ref='232389291' role='' />\n    <member type='way' ref='45211087' role='' />\n    <member type='way' ref='4454210' role='' />\n    <member type='way' ref='60320562' role='' />\n    <member type='way' ref='386655222' role='' />\n    <member type='way' ref='23193748' role='' />\n    <member type='way' ref='39077151' role='' />\n    <member type='way' ref='20602750' role='' />\n    <member type='way' ref='220784321' role='' />\n    <member type='way' ref='247131759' role='' />\n    <member type='way' ref='95961399' role='' />\n    <member type='way' ref='95961398' role='' />\n    <member type='way' ref='272386928' role='' />\n    <member type='way' ref='29979411' role='' />\n    <member type='way' ref='187916322' role='' />\n    <member type='way' ref='187916321' role='' />\n    <member type='way' ref='23193748' role='' />\n    <member type='way' ref='386655222' role='' />\n    <member type='way' ref='60320562' role='' />\n    <member type='way' ref='18936035' role='' />\n    <member type='way' ref='30099570' role='' />\n    <member type='way' ref='23237790' role='' />\n    <member type='way' ref='146874387' role='' />\n    <member type='way' ref='14035891' role='' />\n    <member type='way' ref='45267407' role='' />\n    <member type='way' ref='146874522' role='' />\n    <member type='way' ref='146874515' role='' />\n    <member type='way' ref='388070423' role='' />\n    <member type='way' ref='515410697' role='' />\n    <member type='way' ref='146874517' role='' />\n    <member type='way' ref='515410698' role='' />\n    <member type='way' ref='96034253' role='' />\n    <member type='way' ref='88308013' role='' />\n    <member type='way' ref='88308022' role='' />\n    <member type='way' ref='515410739' role='' />\n    <member type='way' ref='515410738' role='' />\n    <member type='way' ref='515410737' role='' />\n    <member type='way' ref='30117361' role='' />\n    <member type='way' ref='361480628' role='' />\n    <member type='way' ref='88284539' role='' />\n    <member type='way' ref='88284549' role='' />\n    <member type='way' ref='23330554' role='' />\n    <member type='way' ref='23330555' role='' />\n    <member type='way' ref='109830394' role='' />\n    <member type='way' ref='182725109' role='' />\n    <member type='way' ref='182725119' role='' />\n    <member type='way' ref='40031815' role='' />\n    <member type='way' ref='24511922' role='' />\n    <member type='way' ref='244502594' role='' />\n    <member type='way' ref='27777184' role='' />\n    <member type='way' ref='27777185' role='' />\n    <member type='way' ref='244502596' role='' />\n    <member type='way' ref='27523733' role='' />\n    <member type='way' ref='24524471' role='' />\n    <member type='way' ref='37502548' role='' />\n    <member type='way' ref='37502549' role='' />\n    <member type='way' ref='23842882' role='' />\n    <member type='way' ref='20581098' role='' />\n    <member type='way' ref='227763719' role='' />\n    <member type='way' ref='227763718' role='' />\n    <member type='way' ref='227763720' role='' />\n    <member type='way' ref='432736170' role='' />\n    <member type='way' ref='8136880' role='' />\n    <member type='way' ref='241985604' role='' />\n    <member type='way' ref='43444062' role='' />\n    <member type='way' ref='241985603' role='' />\n    <member type='way' ref='37995158' role='' />\n    <member type='way' ref='37995159' role='' />\n    <member type='way' ref='23947678' role='' />\n    <member type='way' ref='388070286' role='' />\n    <member type='way' ref='30951356' role='' />\n    <member type='way' ref='362348447' role='' />\n    <member type='way' ref='38554684' role='' />\n    <member type='way' ref='38554685' role='' />\n    <member type='way' ref='38554705' role='' />\n    <member type='way' ref='38554706' role='' />\n    <member type='way' ref='362348448' role='' />\n    <member type='way' ref='25731764' role='' />\n    <member type='way' ref='25731774' role='' />\n    <member type='way' ref='25731775' role='' />\n    <member type='way' ref='229026963' role='' />\n    <member type='way' ref='31056243' role='' />\n    <member type='way' ref='31056245' role='' />\n    <member type='way' ref='55849841' role='' />\n    <member type='way' ref='86960803' role='' />\n    <member type='way' ref='56082365' role='' />\n    <member type='way' ref='86960791' role='' />\n    <member type='way' ref='457461057' role='' />\n    <member type='way' ref='86960810' role='' />\n    <member type='way' ref='432736165' role='' />\n    <member type='way' ref='23948280' role='' />\n    <member type='way' ref='432736167' role='' />\n    <member type='way' ref='8281101' role='' />\n    <member type='way' ref='8397742' role='' />\n    <member type='way' ref='228439393' role='' />\n    <member type='way' ref='246384285' role='' />\n    <member type='way' ref='31051287' role='' />\n    <member type='way' ref='31051288' role='' />\n    <member type='way' ref='56082371' role='' />\n    <member type='way' ref='71408057' role='' />\n    <member type='way' ref='52333298' role='' />\n    <member type='way' ref='23948286' role='' />\n    <member type='way' ref='25303089' role='' />\n    <member type='way' ref='59102610' role='' />\n    <member type='way' ref='388313098' role='' />\n    <member type='way' ref='410957046' role='' />\n    <member type='way' ref='59102614' role='' />\n    <member type='way' ref='386817387' role='' />\n    <member type='way' ref='397437373' role='' />\n    <member type='way' ref='59102613' role='' />\n    <member type='way' ref='356858730' role='' />\n    <member type='way' ref='356858719' role='' />\n    <member type='way' ref='356858731' role='' />\n    <member type='way' ref='356858717' role='' />\n    <member type='way' ref='334753858' role='' />\n    <member type='way' ref='410957042' role='' />\n    <member type='way' ref='410957045' role='' />\n    <member type='way' ref='334753878' role='' />\n    <member type='way' ref='334753882' role='' />\n    <member type='way' ref='386817387' role='' />\n    <member type='way' ref='397437373' role='' />\n    <member type='way' ref='388070400' role='' />\n    <member type='way' ref='59102606' role='' />\n    <member type='way' ref='25303089' role='' />\n    <member type='way' ref='50939620' role='' />\n    <member type='way' ref='356858715' role='' />\n    <member type='way' ref='47379980' role='' />\n    <member type='way' ref='228796382' role='' />\n    <member type='way' ref='88573402' role='' />\n    <member type='way' ref='145991346' role='' />\n    <member type='way' ref='262098088' role='' />\n    <member type='way' ref='262098086' role='' />\n    <member type='way' ref='25303091' role='' />\n    <member type='way' ref='290731086' role='' />\n    <member type='way' ref='262098090' role='' />\n    <member type='way' ref='89353017' role='' />\n    <member type='way' ref='316216449' role='' />\n    <member type='way' ref='228796371' role='' />\n    <member type='way' ref='228796380' role='' />\n    <member type='way' ref='30104747' role='' />\n    <member type='way' ref='245877617' role='' />\n    <member type='way' ref='71954439' role='' />\n    <member type='way' ref='47360347' role='' />\n    <member type='way' ref='8144077' role='' />\n    <member type='way' ref='59195863' role='' />\n    <member type='way' ref='66245183' role='' />\n    <member type='way' ref='59195862' role='' />\n    <member type='way' ref='26291391' role='' />\n    <member type='way' ref='26291400' role='' />\n    <member type='way' ref='27090597' role='' />\n    <member type='way' ref='28165008' role='' />\n    <member type='way' ref='25724534' role='' />\n    <member type='way' ref='278784725' role='' />\n    <member type='way' ref='278784728' role='' />\n    <member type='way' ref='278784726' role='' />\n    <member type='way' ref='278784731' role='' />\n    <member type='way' ref='329830321' role='' />\n    <member type='way' ref='329830320' role='' />\n    <member type='way' ref='24496386' role='' />\n    <member type='way' ref='24496387' role='' />\n    <member type='way' ref='61411040' role='' />\n    <member type='way' ref='38443331' role='' />\n    <member type='way' ref='38443341' role='' />\n    <member type='way' ref='24935251' role='' />\n    <member type='way' ref='24935252' role='' />\n    <member type='way' ref='24935205' role='' />\n    <member type='way' ref='572541273' role='' />\n    <member type='way' ref='572541272' role='' />\n    <member type='way' ref='24935206' role='' />\n    <member type='way' ref='182731877' role='' />\n    <member type='way' ref='24741614' role='' />\n    <member type='way' ref='24741615' role='' />\n    <member type='way' ref='24544548' role='' />\n    <member type='way' ref='24544549' role='' />\n    <member type='way' ref='24928238' role='' />\n    <member type='way' ref='24928239' role='' />\n    <member type='way' ref='224447171' role='' />\n    <member type='way' ref='224447172' role='' />\n    <member type='way' ref='80234454' role='' />\n    <member type='way' ref='80234415' role='' />\n    <member type='way' ref='191646744' role='' />\n    <member type='way' ref='191646742' role='' />\n    <member type='way' ref='23983044' role='' />\n    <member type='way' ref='23983045' role='' />\n    <member type='way' ref='23635859' role='' />\n    <member type='way' ref='388313086' role='' />\n    <member type='way' ref='23624341' role='' />\n    <member type='way' ref='218506099' role='' />\n    <member type='way' ref='218506102' role='' />\n    <member type='way' ref='388313089' role='' />\n    <member type='way' ref='23635835' role='' />\n    <member type='way' ref='24490162' role='' />\n    <member type='way' ref='372687462' role='' />\n    <member type='way' ref='372687453' role='' />\n    <member type='way' ref='184497381' role='' />\n    <member type='way' ref='450080910' role='' />\n    <member type='way' ref='375226731' role='' />\n    <member type='way' ref='95914917' role='' />\n    <member type='way' ref='388070279' role='' />\n    <member type='way' ref='531226944' role='' />\n    <member type='way' ref='388070402' role='' />\n    <member type='way' ref='411108638' role='' />\n    <member type='way' ref='411108638' role='' />\n    <member type='way' ref='491895463' role='' />\n    <member type='way' ref='386808188' role='' />\n    <member type='way' ref='388070279' role='' />\n    <member type='way' ref='95914917' role='' />\n    <member type='way' ref='375226731' role='' />\n    <member type='way' ref='375226733' role='' />\n    <member type='way' ref='439906258' role='' />\n    <member type='way' ref='372830909' role='' />\n    <member type='way' ref='214712272' role='' />\n    <member type='way' ref='153767713' role='' />\n    <member type='way' ref='23624136' role='' />\n    <member type='way' ref='23624139' role='' />\n    <member type='way' ref='23679127' role='' />\n    <member type='way' ref='23576857' role='' />\n    <member type='way' ref='8176503' role='' />\n    <member type='way' ref='90400241' role='' />\n    <member type='way' ref='68019853' role='' />\n    <member type='way' ref='68019861' role='' />\n    <member type='way' ref='68019854' role='' />\n    <member type='way' ref='68019860' role='' />\n    <member type='way' ref='388313112' role='' />\n    <member type='way' ref='319793717' role='' />\n    <member type='way' ref='23628679' role='' />\n    <member type='way' ref='388313090' role='' />\n    <member type='way' ref='49071513' role='' />\n    <member type='way' ref='319793714' role='' />\n    <member type='way' ref='388313087' role='' />\n    <member type='way' ref='23636198' role='' />\n    <member type='way' ref='572558597' role='' />\n    <member type='way' ref='572558602' role='' />\n    <member type='way' ref='23636199' role='' />\n    <member type='way' ref='24327371' role='' />\n    <member type='way' ref='145296229' role='' />\n    <member type='way' ref='145296219' role='' />\n    <member type='way' ref='24392408' role='' />\n    <member type='way' ref='24392409' role='' />\n    <member type='way' ref='31072809' role='' />\n    <member type='way' ref='31072812' role='' />\n    <member type='way' ref='38963994' role='' />\n    <member type='way' ref='38963985' role='' />\n    <member type='way' ref='146196149' role='' />\n    <member type='way' ref='146196147' role='' />\n    <member type='way' ref='44043362' role='' />\n    <member type='way' ref='44043363' role='' />\n    <member type='way' ref='146196152' role='' />\n    <member type='way' ref='146196146' role='' />\n    <member type='way' ref='211026511' role='' />\n    <member type='way' ref='8136826' role='' />\n    <member type='way' ref='31072799' role='' />\n    <member type='way' ref='31072802' role='' />\n    <member type='way' ref='38964054' role='' />\n    <member type='way' ref='40069919' role='' />\n    <member type='way' ref='279073009' role='' />\n    <member type='way' ref='279073016' role='' />\n    <member type='way' ref='279073006' role='' />\n    <member type='way' ref='279073018' role='' />\n    <member type='way' ref='279073010' role='' />\n    <member type='way' ref='279073013' role='' />\n    <member type='way' ref='182731879' role='' />\n    <member type='way' ref='279073004' role='' />\n    <member type='way' ref='279073012' role='' />\n    <member type='way' ref='8136825' role='' />\n    <member type='way' ref='40069980' role='' />\n    <member type='way' ref='8136824' role='' />\n    <member type='way' ref='24875950' role='' />\n    <member type='way' ref='24875951' role='' />\n    <member type='way' ref='4080342' role='' />\n    <member type='way' ref='38819879' role='' />\n    <member type='way' ref='211026516' role='' />\n    <member type='way' ref='40071076' role='' />\n    <member type='way' ref='31072789' role='' />\n    <member type='way' ref='31072790' role='' />\n    <member type='way' ref='24596435' role='' />\n    <member type='way' ref='572610325' role='' />\n    <member type='way' ref='23314628' role='' />\n    <member type='way' ref='143334979' role='' />\n    <member type='way' ref='143334972' role='' />\n    <member type='way' ref='29680423' role='' />\n    <member type='way' ref='29680433' role='' />\n    <member type='way' ref='4211019' role='' />\n    <member type='way' ref='28888764' role='' />\n    <member type='way' ref='28888765' role='' />\n    <member type='way' ref='28886478' role='' />\n    <member type='way' ref='28886480' role='' />\n    <member type='way' ref='30267410' role='' />\n    <member type='way' ref='30267412' role='' />\n    <member type='way' ref='40153712' role='' />\n    <member type='way' ref='28520255' role='' />\n    <member type='way' ref='28520315' role='' />\n    <member type='way' ref='28520313' role='' />\n    <member type='way' ref='28520731' role='' />\n    <member type='way' ref='28520727' role='' />\n    <member type='way' ref='28523774' role='' />\n    <member type='way' ref='28523771' role='' />\n    <member type='way' ref='28679381' role='' />\n    <member type='way' ref='28679380' role='' />\n    <member type='way' ref='28679386' role='' />\n    <member type='way' ref='28679383' role='' />\n    <member type='way' ref='220533699' role='' />\n    <member type='way' ref='220533701' role='' />\n    <member type='way' ref='40082916' role='' />\n    <member type='way' ref='40082510' role='' />\n    <member type='way' ref='4211001' role='' />\n    <member type='way' ref='40127084' role='' />\n    <member type='way' ref='40127082' role='' />\n    <member type='way' ref='40127233' role='' />\n    <member type='way' ref='40127370' role='' />\n    <member type='way' ref='40127431' role='' />\n    <member type='way' ref='40127432' role='' />\n    <member type='way' ref='40127441' role='' />\n    <member type='way' ref='40127518' role='' />\n    <member type='way' ref='40127541' role='' />\n    <member type='way' ref='40127540' role='' />\n    <member type='way' ref='24940724' role='' />\n    <member type='way' ref='40127593' role='' />\n    <member type='way' ref='40127844' role='' />\n    <member type='way' ref='40127843' role='' />\n    <member type='way' ref='40127864' role='' />\n    <member type='way' ref='40127863' role='' />\n    <member type='way' ref='40127925' role='' />\n    <member type='way' ref='40127904' role='' />\n    <member type='way' ref='40127977' role='' />\n    <member type='way' ref='40127976' role='' />\n    <member type='way' ref='24960396' role='' />\n    <member type='way' ref='24960397' role='' />\n    <member type='way' ref='24936413' role='' />\n    <member type='way' ref='147756249' role='' />\n    <member type='way' ref='296512986' role='' />\n    <member type='way' ref='40128411' role='' />\n    <member type='way' ref='38816887' role='' />\n    <member type='way' ref='40128706' role='' />\n    <member type='way' ref='38816767' role='' />\n    <member type='way' ref='38816718' role='' />\n    <member type='way' ref='39887926' role='' />\n    <member type='way' ref='440338460' role='' />\n    <member type='way' ref='39887921' role='' />\n    <member type='way' ref='440338457' role='' />\n    <member type='way' ref='38815440' role='' />\n    <member type='way' ref='182731875' role='' />\n    <member type='way' ref='8135078' role='' />\n    <member type='way' ref='8132654' role='' />\n    <member type='way' ref='292644143' role='' />\n    <member type='way' ref='292644144' role='' />\n    <member type='way' ref='10958440' role='' />\n    <member type='way' ref='10958453' role='' />\n    <member type='way' ref='361204267' role='' />\n    <member type='way' ref='361204264' role='' />\n    <member type='way' ref='4683336' role='' />\n    <member type='way' ref='296093118' role='' />\n    <member type='way' ref='296093125' role='' />\n    <member type='way' ref='164691754' role='' />\n    <member type='way' ref='4683335' role='' />\n    <member type='way' ref='164689857' role='' />\n    <member type='way' ref='164689856' role='' />\n    <member type='way' ref='40814689' role='' />\n    <member type='way' ref='40814690' role='' />\n    <member type='way' ref='286430791' role='' />\n    <member type='way' ref='164689406' role='' />\n    <member type='way' ref='286430792' role='' />\n    <member type='way' ref='164688607' role='' />\n    <member type='way' ref='4040443' role='' />\n    <member type='way' ref='111626846' role='' />\n    <member type='way' ref='50898254' role='' />\n    <member type='way' ref='286430790' role='' />\n    <member type='way' ref='4498560' role='' />\n    <member type='way' ref='50898242' role='' />\n    <member type='way' ref='27452181' role='' />\n    <member type='way' ref='27452182' role='' />\n    <member type='way' ref='46827888' role='' />\n    <member type='way' ref='46828002' role='' />\n    <member type='way' ref='284411437' role='' />\n    <member type='way' ref='456169126' role='' />\n    <member type='way' ref='470968483' role='' />\n    <member type='way' ref='72114686' role='' />\n    <member type='way' ref='284412972' role='' />\n    <member type='way' ref='456169130' role='' />\n    <member type='way' ref='438161904' role='' />\n    <member type='way' ref='455835779' role='' />\n    <member type='way' ref='455835782' role='' />\n    <member type='way' ref='455835783' role='' />\n    <member type='way' ref='455835784' role='' />\n    <member type='way' ref='450833445' role='' />\n    <member type='way' ref='451835953' role='' />\n    <member type='way' ref='266160718' role='' />\n    <member type='way' ref='144049466' role='' />\n    <member type='way' ref='144049459' role='' />\n    <member type='way' ref='165275030' role='' />\n    <member type='way' ref='165525371' role='' />\n    <member type='way' ref='165525377' role='' />\n    <member type='way' ref='165525374' role='' />\n    <member type='way' ref='165525370' role='' />\n    <member type='way' ref='550053403' role='' />\n    <member type='way' ref='165525369' role='' />\n    <member type='way' ref='116023686' role='' />\n    <member type='way' ref='165275032' role='' />\n    <member type='way' ref='165275029' role='' />\n    <member type='way' ref='165275047' role='' />\n    <member type='way' ref='116023731' role='' />\n    <member type='way' ref='23936453' role='' />\n    <member type='way' ref='386788586' role='' />\n    <member type='way' ref='471031928' role='' />\n    <member type='way' ref='386788588' role='' />\n    <member type='way' ref='471031925' role='' />\n    <member type='way' ref='266160716' role='' />\n    <member type='way' ref='451835953' role='' />\n    <member type='way' ref='450833445' role='' />\n    <member type='way' ref='455835784' role='' />\n    <member type='way' ref='455835783' role='' />\n    <member type='way' ref='455835782' role='' />\n    <member type='way' ref='455835779' role='' />\n    <member type='way' ref='284412712' role='' />\n    <member type='way' ref='130989059' role='' />\n    <member type='way' ref='469381404' role='' />\n    <member type='way' ref='455835777' role='' />\n    <member type='way' ref='168975965' role='' />\n    <member type='way' ref='46827875' role='' />\n    <member type='way' ref='46827873' role='' />\n    <member type='way' ref='617882270' role='' />\n    <member type='way' ref='116605087' role='' />\n    <member type='way' ref='116605088' role='' />\n    <member type='way' ref='116605086' role='' />\n    <member type='way' ref='58703987' role='' />\n    <member type='way' ref='116615256' role='' />\n    <member type='way' ref='116614210' role='' />\n    <member type='way' ref='116613456' role='' />\n    <member type='way' ref='116613464' role='' />\n    <member type='way' ref='116613466' role='' />\n    <member type='way' ref='4498563' role='' />\n    <member type='way' ref='58703991' role='' />\n    <member type='way' ref='506393763' role='' />\n    <member type='way' ref='504318420' role='' />\n    <member type='way' ref='538390979' role='' />\n    <member type='way' ref='540043597' role='' />\n    <member type='way' ref='540043600' role='' />\n    <member type='way' ref='540043598' role='' />\n    <member type='way' ref='540043596' role='' />\n    <member type='way' ref='540043601' role='' />\n    <member type='way' ref='539199603' role='' />\n    <member type='way' ref='214287033' role='' />\n    <member type='way' ref='540043595' role='' />\n    <member type='way' ref='214287031' role='' />\n    <member type='way' ref='214860684' role='' />\n    <member type='way' ref='214860680' role='' />\n    <member type='way' ref='165528067' role='' />\n    <member type='way' ref='440330697' role='' />\n    <member type='way' ref='440330706' role='' />\n    <member type='way' ref='440330703' role='' />\n    <member type='way' ref='440330699' role='' />\n    <member type='way' ref='440330694' role='' />\n    <member type='way' ref='142028779' role='' />\n    <member type='way' ref='213579174' role='' />\n    <member type='way' ref='226048678' role='' />\n    <member type='way' ref='8132639' role='' />\n    <member type='way' ref='226048675' role='' />\n    <member type='way' ref='236557696' role='' />\n    <member type='way' ref='284404463' role='' />\n    <member type='way' ref='4865150' role='' />\n    <member type='way' ref='214860674' role='' />\n    <member type='way' ref='284402509' role='' />\n    <member type='way' ref='284402514' role='' />\n    <member type='way' ref='237772652' role='' />\n    <member type='way' ref='13865741' role='' />\n    <member type='way' ref='13865742' role='' />\n    <member type='way' ref='226048674' role='' />\n    <member type='way' ref='284402024' role='' />\n    <member type='way' ref='4865153' role='' />\n    <member type='way' ref='4865151' role='' />\n    <member type='way' ref='284399987' role='' />\n    <member type='way' ref='165529478' role='' />\n    <member type='way' ref='41291142' role='' />\n    <member type='way' ref='284398486' role='' />\n    <member type='way' ref='41291146' role='' />\n    <member type='way' ref='236367089' role='' />\n    <member type='way' ref='156820995' role='' />\n    <member type='way' ref='4040295' role='' />\n    <member type='way' ref='142028783' role='' />\n    <member type='way' ref='284395943' role='' />\n    <member type='way' ref='284395941' role='' />\n    <member type='way' ref='440235414' role='' />\n    <member type='way' ref='4865180' role='' />\n    <member type='way' ref='284395947' role='' />\n    <member type='way' ref='284395942' role='' />\n    <member type='way' ref='142028782' role='' />\n    <member type='way' ref='238469679' role='' />\n    <member type='way' ref='4865183' role='' />\n    <member type='way' ref='4865181' role='' />\n    <member type='way' ref='8132601' role='' />\n    <member type='way' ref='571218330' role='' />\n    <member type='way' ref='8132602' role='' />\n    <member type='way' ref='8132180' role='' />\n    <member type='way' ref='290069111' role='' />\n    <member type='way' ref='290069112' role='' />\n    <member type='way' ref='238322472' role='' />\n    <member type='way' ref='571218310' role='' />\n    <member type='way' ref='571218307' role='' />\n    <member type='way' ref='285585340' role='' />\n    <member type='way' ref='285585344' role='' />\n    <member type='way' ref='285585343' role='' />\n    <member type='way' ref='285585346' role='' />\n    <member type='way' ref='194368196' role='' />\n    <member type='way' ref='290069109' role='' />\n    <member type='way' ref='290069107' role='' />\n    <member type='way' ref='290069105' role='' />\n    <member type='way' ref='290043857' role='' />\n    <member type='way' ref='290043858' role='' />\n    <member type='way' ref='4590392' role='' />\n    <member type='way' ref='290036517' role='' />\n    <member type='way' ref='194368195' role='' />\n    <member type='way' ref='290036516' role='' />\n    <member type='way' ref='58834820' role='' />\n    <member type='way' ref='571218268' role='' />\n    <member type='way' ref='58834836' role='' />\n    <member type='way' ref='571218275' role='' />\n    <member type='way' ref='4590390' role='' />\n    <member type='way' ref='290036518' role='' />\n    <member type='way' ref='64033127' role='' />\n    <member type='way' ref='64033140' role='' />\n    <member type='way' ref='440224290' role='' />\n    <member type='way' ref='4360773' role='' />\n    <member type='way' ref='440187056' role='' />\n    <member type='way' ref='388313104' role='' />\n    <member type='way' ref='4010208' role='' />\n    <member type='way' ref='4010205' role='' />\n    <member type='way' ref='4010206' role='' />\n    <member type='way' ref='388313100' role='' />\n    <member type='way' ref='386785831' role='' />\n    <member type='way' ref='388313099' role='' />\n    <member type='way' ref='388313101' role='' />\n    <member type='way' ref='388313114' role='' />\n    <member type='way' ref='388313103' role='' />\n    <member type='way' ref='550263971' role='' />\n    <member type='way' ref='550263972' role='' />\n    <member type='way' ref='550263973' role='' />\n    <member type='way' ref='550263974' role='' />\n    <member type='way' ref='388313110' role='' />\n    <member type='way' ref='550298875' role='' />\n    <member type='way' ref='550298874' role='' />\n    <member type='way' ref='388313115' role='' />\n    <member type='way' ref='388313103' role='' />\n    <member type='way' ref='550263971' role='' />\n    <member type='way' ref='550263972' role='' />\n    <member type='way' ref='550263973' role='' />\n    <member type='way' ref='550263974' role='' />\n    <member type='way' ref='388313110' role='' />\n    <member type='way' ref='388313113' role='' />\n    <member type='way' ref='568549581' role='' />\n    <member type='way' ref='198256718' role='' />\n    <member type='way' ref='440224316' role='' />\n    <member type='way' ref='388313099' role='' />\n    <member type='way' ref='497861437' role='' />\n    <member type='way' ref='310851705' role='' />\n    <member type='way' ref='8132020' role='' />\n    <member type='way' ref='8132019' role='' />\n    <member type='way' ref='8132021' role='' />\n    <member type='way' ref='440190404' role='' />\n    <member type='way' ref='310851703' role='' />\n    <member type='way' ref='481451924' role='' />\n    <member type='way' ref='440187053' role='' />\n    <member type='way' ref='4923112' role='' />\n    <member type='way' ref='440187058' role='' />\n    <member type='way' ref='8137991' role='' />\n    <member type='way' ref='550263975' role='' />\n    <member type='way' ref='8142468' role='' />\n    <member type='way' ref='8142466' role='' />\n    <member type='way' ref='367358070' role='' />\n    <member type='way' ref='25307097' role='' />\n    <member type='way' ref='571203997' role='' />\n    <member type='way' ref='571203995' role='' />\n    <member type='way' ref='367358072' role='' />\n    <member type='way' ref='25307107' role='' />\n    <member type='way' ref='25307106' role='' />\n    <member type='way' ref='25307099' role='' />\n    <member type='way' ref='25307098' role='' />\n    <member type='way' ref='440066749' role='' />\n    <member type='way' ref='41291157' role='' />\n    <member type='way' ref='41291158' role='' />\n    <member type='way' ref='440066748' role='' />\n    <member type='way' ref='4822010' role='' />\n    <member type='way' ref='323437399' role='' />\n    <member type='way' ref='323437400' role='' />\n    <member type='way' ref='4822011' role='' />\n    <member type='way' ref='4360825' role='' />\n    <member type='way' ref='27946887' role='' />\n    <member type='way' ref='379351539' role='' />\n    <member type='way' ref='288468826' role='' />\n    <member type='way' ref='27946888' role='' />\n    <member type='way' ref='475555585' role='' />\n    <member type='way' ref='552940512' role='' />\n    <member type='way' ref='552940511' role='' />\n    <member type='way' ref='475555584' role='' />\n    <member type='way' ref='4923345' role='' />\n    <member type='way' ref='4923342' role='' />\n    <member type='way' ref='4923338' role='' />\n    <member type='way' ref='475717741' role='' />\n    <member type='way' ref='475717740' role='' />\n    <member type='way' ref='552939707' role='' />\n    <member type='way' ref='552939706' role='' />\n    <member type='way' ref='552939142' role='' />\n    <member type='way' ref='552939141' role='' />\n    <member type='way' ref='4361340' role='' />\n    <member type='way' ref='41139067' role='' />\n    <member type='way' ref='41139070' role='' />\n    <member type='way' ref='41139072' role='' />\n    <member type='way' ref='133953313' role='' />\n    <member type='way' ref='263960541' role='' />\n    <member type='way' ref='263960544' role='' />\n    <member type='way' ref='263960539' role='' />\n    <member type='way' ref='263960528' role='' />\n    <member type='way' ref='263960535' role='' />\n    <member type='way' ref='263960546' role='' />\n    <member type='way' ref='362669439' role='' />\n    <member type='way' ref='362669438' role='' />\n    <member type='way' ref='362669441' role='' />\n    <member type='way' ref='68294294' role='' />\n    <member type='way' ref='68294289' role='' />\n    <member type='way' ref='68294291' role='' />\n    <member type='way' ref='139184460' role='' />\n    <member type='way' ref='230824477' role='' />\n    <member type='way' ref='324703901' role='' />\n    <member type='way' ref='324703902' role='' />\n    <member type='way' ref='169363259' role='' />\n    <member type='way' ref='169363242' role='' />\n    <member type='way' ref='68295083' role='' />\n    <member type='way' ref='169363262' role='' />\n    <member type='way' ref='169363224' role='' />\n    <member type='way' ref='169363233' role='' />\n    <member type='way' ref='169363273' role='' />\n    <member type='way' ref='169363257' role='' />\n    <member type='way' ref='169363282' role='' />\n    <member type='way' ref='169363255' role='' />\n    <member type='way' ref='169363286' role='' />\n    <member type='way' ref='169363248' role='' />\n    <member type='way' ref='169363285' role='' />\n    <member type='way' ref='230824480' role='' />\n    <member type='way' ref='68290020' role='' />\n    <member type='way' ref='476747418' role='' />\n    <member type='way' ref='476747417' role='' />\n    <member type='way' ref='259750246' role='' />\n    <member type='way' ref='259750247' role='' />\n    <member type='way' ref='68290014' role='' />\n    <member type='way' ref='68290038' role='' />\n    <member type='way' ref='169363230' role='' />\n    <member type='way' ref='169363264' role='' />\n    <member type='way' ref='68290030' role='' />\n    <member type='way' ref='153309162' role='' />\n    <member type='way' ref='153309178' role='' />\n    <member type='way' ref='68297017' role='' />\n    <member type='way' ref='571446813' role='' />\n    <member type='way' ref='571446812' role='' />\n    <member type='way' ref='571446824' role='' />\n    <member type='way' ref='571446823' role='' />\n    <member type='way' ref='477661488' role='' />\n    <member type='way' ref='477661487' role='' />\n    <member type='way' ref='4361446' role='' />\n    <member type='way' ref='32544465' role='' />\n    <member type='way' ref='68301277' role='' />\n    <member type='way' ref='68301266' role='' />\n    <member type='way' ref='68301273' role='' />\n    <member type='way' ref='68301280' role='' />\n    <member type='way' ref='68301257' role='' />\n    <member type='way' ref='68301262' role='' />\n    <member type='way' ref='68301269' role='' />\n    <member type='way' ref='571446831' role='' />\n    <member type='way' ref='571446830' role='' />\n    <member type='way' ref='571446836' role='' />\n    <member type='way' ref='571446835' role='' />\n    <member type='way' ref='259734075' role='' />\n    <member type='way' ref='259734073' role='' />\n    <member type='way' ref='259734132' role='' />\n    <member type='way' ref='259734133' role='' />\n    <member type='way' ref='259734119' role='' />\n    <member type='way' ref='259734125' role='' />\n    <member type='way' ref='471643305' role='' />\n    <member type='way' ref='4817721' role='' />\n    <member type='way' ref='4361570' role='' />\n    <member type='way' ref='195852545' role='' />\n    <member type='way' ref='298081599' role='' />\n    <member type='way' ref='298082971' role='' />\n    <member type='way' ref='195852542' role='' />\n    <member type='way' ref='298081597' role='' />\n    <member type='way' ref='571453991' role='' />\n    <member type='way' ref='4361569' role='' />\n    <member type='way' ref='160489664' role='' />\n    <member type='way' ref='4948289' role='' />\n    <member type='way' ref='121203167' role='' />\n    <member type='way' ref='121203168' role='' />\n    <member type='way' ref='471643302' role='' />\n    <member type='way' ref='471643289' role='' />\n    <member type='way' ref='195852543' role='' />\n    <member type='way' ref='571453996' role='' />\n    <member type='way' ref='571453998' role='' />\n    <member type='way' ref='285529812' role='' />\n    <member type='way' ref='285529814' role='' />\n    <member type='way' ref='288842424' role='' />\n    <member type='way' ref='288842425' role='' />\n    <member type='way' ref='25545749' role='' />\n    <member type='way' ref='4888789' role='' />\n    <member type='way' ref='288842423' role='' />\n    <member type='way' ref='288842422' role='' />\n    <member type='way' ref='288842513' role='' />\n    <member type='way' ref='288842514' role='' />\n    <member type='way' ref='288842417' role='' />\n    <member type='way' ref='288842416' role='' />\n    <member type='way' ref='28641852' role='' />\n    <member type='way' ref='25545451' role='' />\n    <member type='way' ref='288842415' role='' />\n    <member type='way' ref='288842414' role='' />\n    <member type='way' ref='571454003' role='' />\n    <member type='way' ref='571454005' role='' />\n    <member type='way' ref='96271019' role='' />\n    <member type='way' ref='96271021' role='' />\n    <member type='way' ref='96269618' role='' />\n    <member type='way' ref='96269611' role='' />\n    <member type='way' ref='288842411' role='' />\n    <member type='way' ref='38893289' role='' />\n    <member type='way' ref='471643288' role='' />\n    <member type='way' ref='4888859' role='' />\n    <member type='way' ref='471643290' role='' />\n    <member type='way' ref='471643291' role='' />\n    <member type='way' ref='96286400' role='' />\n    <member type='way' ref='29056787' role='' />\n    <member type='way' ref='370108006' role='' />\n    <member type='way' ref='4941500' role='' />\n    <member type='way' ref='370108007' role='' />\n    <tag k='fixme' v='Set platforms atOslo Bussterminal' />\n    <tag k='from' v='København' />\n    <tag k='from:da' v='København' />\n    <tag k='from:en' v='Copenhagen' />\n    <tag k='from:nb' v='København' />\n    <tag k='from:sv' v='Köpenhamn' />\n    <tag k='importance' v='national' />\n    <tag k='name' v='Buss 820: København - Oslo' />\n    <tag k='name:da' v='Bus 820: København - Oslo' />\n    <tag k='name:en' v='Bus 820: Copenhagen - Oslo' />\n    <tag k='name:nb' v='Buss 820: Købehnavn - Oslo' />\n    <tag k='name:sv' v='Buss 820: Köpenhamn - Oslo' />\n    <tag k='network' v='Swebus' />\n    <tag k='operator' v='Swebus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='820' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Oslo' />\n    <tag k='to:da' v='Oslo' />\n    <tag k='to:en' v='Oslo' />\n    <tag k='to:nb' v='Oslo' />\n    <tag k='to:sv' v='Oslo' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='5794316' timestamp='2018-07-16T09:19:28Z' uid='953001' user='tomasy' version='84' changeset='60755417'>\n    <member type='node' ref='339884586' role='stop' />\n    <member type='node' ref='416908979' role='stop' />\n    <member type='node' ref='4128416085' role='stop' />\n    <member type='node' ref='3418254623' role='stop' />\n    <member type='node' ref='1998995667' role='stop' />\n    <member type='node' ref='1310612418' role='platform' />\n    <member type='node' ref='1938160930' role='stop' />\n    <member type='way' ref='435653432' role='stop' />\n    <member type='way' ref='391105367' role='platform' />\n    <member type='node' ref='5313132106' role='stop' />\n    <member type='way' ref='411108616' role='platform' />\n    <member type='way' ref='334753833' role='platform' />\n    <member type='way' ref='189255217' role='platform' />\n    <member type='way' ref='25545167' role='' />\n    <member type='way' ref='4886308' role='' />\n    <member type='way' ref='25545236' role='' />\n    <member type='way' ref='30731949' role='' />\n    <member type='way' ref='370108008' role='' />\n    <member type='way' ref='471643292' role='' />\n    <member type='way' ref='25545320' role='' />\n    <member type='way' ref='573542329' role='' />\n    <member type='way' ref='96279154' role='' />\n    <member type='way' ref='25545576' role='' />\n    <member type='way' ref='288842410' role='' />\n    <member type='way' ref='96269621' role='' />\n    <member type='way' ref='96269629' role='' />\n    <member type='way' ref='96271017' role='' />\n    <member type='way' ref='96271015' role='' />\n    <member type='way' ref='571454006' role='' />\n    <member type='way' ref='571454004' role='' />\n    <member type='way' ref='288842412' role='' />\n    <member type='way' ref='288842413' role='' />\n    <member type='way' ref='25545468' role='' />\n    <member type='way' ref='25545735' role='' />\n    <member type='way' ref='288842418' role='' />\n    <member type='way' ref='288842419' role='' />\n    <member type='way' ref='288842511' role='' />\n    <member type='way' ref='288842512' role='' />\n    <member type='way' ref='288842421' role='' />\n    <member type='way' ref='288842420' role='' />\n    <member type='way' ref='25545798' role='' />\n    <member type='way' ref='25545834' role='' />\n    <member type='way' ref='288842426' role='' />\n    <member type='way' ref='288842427' role='' />\n    <member type='way' ref='285529811' role='' />\n    <member type='way' ref='285529813' role='' />\n    <member type='way' ref='4333216' role='' />\n    <member type='way' ref='571453997' role='' />\n    <member type='way' ref='571453995' role='' />\n    <member type='way' ref='195852539' role='' />\n    <member type='way' ref='471643301' role='' />\n    <member type='way' ref='195852536' role='' />\n    <member type='way' ref='194321905' role='' />\n    <member type='way' ref='194321890' role='' />\n    <member type='way' ref='121203166' role='' />\n    <member type='way' ref='121203169' role='' />\n    <member type='way' ref='4817723' role='' />\n    <member type='way' ref='160489663' role='' />\n    <member type='way' ref='4817722' role='' />\n    <member type='way' ref='571453990' role='' />\n    <member type='way' ref='440328542' role='' />\n    <member type='way' ref='440328544' role='' />\n    <member type='way' ref='440328543' role='' />\n    <member type='way' ref='195852541' role='' />\n    <member type='way' ref='4361564' role='' />\n    <member type='way' ref='4361565' role='' />\n    <member type='way' ref='259734124' role='' />\n    <member type='way' ref='259734123' role='' />\n    <member type='way' ref='259734137' role='' />\n    <member type='way' ref='259734136' role='' />\n    <member type='way' ref='259734070' role='' />\n    <member type='way' ref='259734071' role='' />\n    <member type='way' ref='571446833' role='' />\n    <member type='way' ref='571446834' role='' />\n    <member type='way' ref='571446828' role='' />\n    <member type='way' ref='571446829' role='' />\n    <member type='way' ref='68301259' role='' />\n    <member type='way' ref='68301260' role='' />\n    <member type='way' ref='68301267' role='' />\n    <member type='way' ref='68301275' role='' />\n    <member type='way' ref='68301278' role='' />\n    <member type='way' ref='68301254' role='' />\n    <member type='way' ref='68301256' role='' />\n    <member type='way' ref='37878862' role='' />\n    <member type='way' ref='477661486' role='' />\n    <member type='way' ref='477661485' role='' />\n    <member type='way' ref='37878863' role='' />\n    <member type='way' ref='571446822' role='' />\n    <member type='way' ref='571446825' role='' />\n    <member type='way' ref='571446815' role='' />\n    <member type='way' ref='571446814' role='' />\n    <member type='way' ref='68297027' role='' />\n    <member type='way' ref='153309159' role='' />\n    <member type='way' ref='153309157' role='' />\n    <member type='way' ref='550305950' role='' />\n    <member type='way' ref='68290009' role='' />\n    <member type='way' ref='169363234' role='' />\n    <member type='way' ref='169363222' role='' />\n    <member type='way' ref='68290029' role='' />\n    <member type='way' ref='68290037' role='' />\n    <member type='way' ref='259750245' role='' />\n    <member type='way' ref='476747416' role='' />\n    <member type='way' ref='476747415' role='' />\n    <member type='way' ref='259750229' role='' />\n    <member type='way' ref='132032815' role='' />\n    <member type='way' ref='230824478' role='' />\n    <member type='way' ref='169363245' role='' />\n    <member type='way' ref='169363283' role='' />\n    <member type='way' ref='169363250' role='' />\n    <member type='way' ref='169363281' role='' />\n    <member type='way' ref='169363229' role='' />\n    <member type='way' ref='169363270' role='' />\n    <member type='way' ref='169363235' role='' />\n    <member type='way' ref='169363228' role='' />\n    <member type='way' ref='169363240' role='' />\n    <member type='way' ref='169363267' role='' />\n    <member type='way' ref='154247750' role='' />\n    <member type='way' ref='169363253' role='' />\n    <member type='way' ref='169363265' role='' />\n    <member type='way' ref='324703904' role='' />\n    <member type='way' ref='324703903' role='' />\n    <member type='way' ref='230824479' role='' />\n    <member type='way' ref='68294283' role='' />\n    <member type='way' ref='139184461' role='' />\n    <member type='way' ref='169363276' role='' />\n    <member type='way' ref='167349501' role='' />\n    <member type='way' ref='362669440' role='' />\n    <member type='way' ref='362669437' role='' />\n    <member type='way' ref='263960530' role='' />\n    <member type='way' ref='263960526' role='' />\n    <member type='way' ref='263960540' role='' />\n    <member type='way' ref='263960543' role='' />\n    <member type='way' ref='263960542' role='' />\n    <member type='way' ref='263960545' role='' />\n    <member type='way' ref='61941290' role='' />\n    <member type='way' ref='41139073' role='' />\n    <member type='way' ref='41139075' role='' />\n    <member type='way' ref='41139063' role='' />\n    <member type='way' ref='552939140' role='' />\n    <member type='way' ref='552939139' role='' />\n    <member type='way' ref='552939709' role='' />\n    <member type='way' ref='552939708' role='' />\n    <member type='way' ref='475717743' role='' />\n    <member type='way' ref='475717742' role='' />\n    <member type='way' ref='4923340' role='' />\n    <member type='way' ref='4923343' role='' />\n    <member type='way' ref='4923344' role='' />\n    <member type='way' ref='475555583' role='' />\n    <member type='way' ref='552940510' role='' />\n    <member type='way' ref='552940509' role='' />\n    <member type='way' ref='475555582' role='' />\n    <member type='way' ref='4822013' role='' />\n    <member type='way' ref='288468828' role='' />\n    <member type='way' ref='288468820' role='' />\n    <member type='way' ref='27946885' role='' />\n    <member type='way' ref='27946886' role='' />\n    <member type='way' ref='4822012' role='' />\n    <member type='way' ref='440066747' role='' />\n    <member type='way' ref='440066746' role='' />\n    <member type='way' ref='4360782' role='' />\n    <member type='way' ref='288393977' role='' />\n    <member type='way' ref='288393978' role='' />\n    <member type='way' ref='4360775' role='' />\n    <member type='way' ref='41291159' role='' />\n    <member type='way' ref='41291160' role='' />\n    <member type='way' ref='25307100' role='' />\n    <member type='way' ref='25307103' role='' />\n    <member type='way' ref='25307108' role='' />\n    <member type='way' ref='25307109' role='' />\n    <member type='way' ref='367358069' role='' />\n    <member type='way' ref='571203996' role='' />\n    <member type='way' ref='571203998' role='' />\n    <member type='way' ref='4360769' role='' />\n    <member type='way' ref='367358071' role='' />\n    <member type='way' ref='4360771' role='' />\n    <member type='way' ref='4270348' role='' />\n    <member type='way' ref='550298870' role='' />\n    <member type='way' ref='4360772' role='' />\n    <member type='way' ref='198256718' role='' />\n    <member type='way' ref='440224316' role='' />\n    <member type='way' ref='388313099' role='' />\n    <member type='way' ref='388313101' role='' />\n    <member type='way' ref='388313114' role='' />\n    <member type='way' ref='388313103' role='' />\n    <member type='way' ref='550263971' role='' />\n    <member type='way' ref='550263972' role='' />\n    <member type='way' ref='550263973' role='' />\n    <member type='way' ref='550263974' role='' />\n    <member type='way' ref='388313110' role='' />\n    <member type='way' ref='388313113' role='' />\n    <member type='way' ref='568549581' role='' />\n    <member type='way' ref='198256718' role='' />\n    <member type='way' ref='440224316' role='' />\n    <member type='way' ref='388313099' role='' />\n    <member type='way' ref='497861437' role='' />\n    <member type='way' ref='310851705' role='' />\n    <member type='way' ref='388313107' role='' />\n    <member type='way' ref='388313100' role='' />\n    <member type='way' ref='440190702' role='' />\n    <member type='way' ref='4010207' role='' />\n    <member type='way' ref='440224291' role='' />\n    <member type='way' ref='41291150' role='' />\n    <member type='way' ref='64033121' role='' />\n    <member type='way' ref='64033153' role='' />\n    <member type='way' ref='4590389' role='' />\n    <member type='way' ref='4590391' role='' />\n    <member type='way' ref='571218279' role='' />\n    <member type='way' ref='58834835' role='' />\n    <member type='way' ref='571218271' role='' />\n    <member type='way' ref='58834826' role='' />\n    <member type='way' ref='290036519' role='' />\n    <member type='way' ref='238166899' role='' />\n    <member type='way' ref='290036515' role='' />\n    <member type='way' ref='4014727' role='' />\n    <member type='way' ref='290043856' role='' />\n    <member type='way' ref='290043859' role='' />\n    <member type='way' ref='290069106' role='' />\n    <member type='way' ref='290069108' role='' />\n    <member type='way' ref='290069113' role='' />\n    <member type='way' ref='198256714' role='' />\n    <member type='way' ref='285585342' role='' />\n    <member type='way' ref='285585345' role='' />\n    <member type='way' ref='285585338' role='' />\n    <member type='way' ref='285585337' role='' />\n    <member type='way' ref='571218314' role='' />\n    <member type='way' ref='571218318' role='' />\n    <member type='way' ref='238166900' role='' />\n    <member type='way' ref='290069110' role='' />\n    <member type='way' ref='290069114' role='' />\n    <member type='way' ref='4038677' role='' />\n    <member type='way' ref='4038675' role='' />\n    <member type='way' ref='571218322' role='' />\n    <member type='way' ref='571218326' role='' />\n    <member type='way' ref='4038676' role='' />\n    <member type='way' ref='4038626' role='' />\n    <member type='way' ref='4040271' role='' />\n    <member type='way' ref='165275003' role='' />\n    <member type='way' ref='288160092' role='' />\n    <member type='way' ref='288160090' role='' />\n    <member type='way' ref='284395939' role='' />\n    <member type='way' ref='284395948' role='' />\n    <member type='way' ref='165275004' role='' />\n    <member type='way' ref='4865179' role='' />\n    <member type='way' ref='284395944' role='' />\n    <member type='way' ref='284395945' role='' />\n    <member type='way' ref='165275005' role='' />\n    <member type='way' ref='440235413' role='' />\n    <member type='way' ref='288160093' role='' />\n    <member type='way' ref='4865152' role='' />\n    <member type='way' ref='156820997' role='' />\n    <member type='way' ref='236367090' role='' />\n    <member type='way' ref='288160094' role='' />\n    <member type='way' ref='41291147' role='' />\n    <member type='way' ref='41291143' role='' />\n    <member type='way' ref='288160091' role='' />\n    <member type='way' ref='284399988' role='' />\n    <member type='way' ref='4865154' role='' />\n    <member type='way' ref='4040302' role='' />\n    <member type='way' ref='284402021' role='' />\n    <member type='way' ref='226048677' role='' />\n    <member type='way' ref='13865743' role='' />\n    <member type='way' ref='13865744' role='' />\n    <member type='way' ref='284402510' role='' />\n    <member type='way' ref='284402511' role='' />\n    <member type='way' ref='215041207' role='' />\n    <member type='way' ref='4040303' role='' />\n    <member type='way' ref='183156782' role='' />\n    <member type='way' ref='236557699' role='' />\n    <member type='way' ref='4077750' role='' />\n    <member type='way' ref='290070332' role='' />\n    <member type='way' ref='213798704' role='' />\n    <member type='way' ref='165275007' role='' />\n    <member type='way' ref='284407459' role='' />\n    <member type='way' ref='284408726' role='' />\n    <member type='way' ref='165275002' role='' />\n    <member type='way' ref='284408727' role='' />\n    <member type='way' ref='284408724' role='' />\n    <member type='way' ref='215041210' role='' />\n    <member type='way' ref='284408723' role='' />\n    <member type='way' ref='284409597' role='' />\n    <member type='way' ref='214290381' role='' />\n    <member type='way' ref='284409595' role='' />\n    <member type='way' ref='284409596' role='' />\n    <member type='way' ref='183028746' role='' />\n    <member type='way' ref='165275006' role='' />\n    <member type='way' ref='538390974' role='' />\n    <member type='way' ref='541564166' role='' />\n    <member type='way' ref='541564165' role='' />\n    <member type='way' ref='541564164' role='' />\n    <member type='way' ref='8147694' role='' />\n    <member type='way' ref='116613465' role='' />\n    <member type='way' ref='471650441' role='' />\n    <member type='way' ref='116613461' role='' />\n    <member type='way' ref='471650443' role='' />\n    <member type='way' ref='116613463' role='' />\n    <member type='way' ref='116614208' role='' />\n    <member type='way' ref='116614211' role='' />\n    <member type='way' ref='8147693' role='' />\n    <member type='way' ref='4361872' role='' />\n    <member type='way' ref='569069243' role='' />\n    <member type='way' ref='284411437' role='' />\n    <member type='way' ref='456169126' role='' />\n    <member type='way' ref='470968483' role='' />\n    <member type='way' ref='72114686' role='' />\n    <member type='way' ref='284412972' role='' />\n    <member type='way' ref='456169130' role='' />\n    <member type='way' ref='438161904' role='' />\n    <member type='way' ref='455835779' role='' />\n    <member type='way' ref='455835782' role='' />\n    <member type='way' ref='455835783' role='' />\n    <member type='way' ref='455835784' role='' />\n    <member type='way' ref='450833445' role='' />\n    <member type='way' ref='451835953' role='' />\n    <member type='way' ref='266160716' role='' />\n    <member type='way' ref='471031925' role='' />\n    <member type='way' ref='386788588' role='' />\n    <member type='way' ref='471031928' role='' />\n    <member type='way' ref='386788586' role='' />\n    <member type='way' ref='23936453' role='' />\n    <member type='way' ref='116023731' role='' />\n    <member type='way' ref='165275047' role='' />\n    <member type='way' ref='165275029' role='' />\n    <member type='way' ref='165275032' role='' />\n    <member type='way' ref='116023686' role='' />\n    <member type='way' ref='165525369' role='' />\n    <member type='way' ref='550053403' role='' />\n    <member type='way' ref='165525370' role='' />\n    <member type='way' ref='165525374' role='' />\n    <member type='way' ref='165525377' role='' />\n    <member type='way' ref='165525371' role='' />\n    <member type='way' ref='165275030' role='' />\n    <member type='way' ref='144049459' role='' />\n    <member type='way' ref='144049466' role='' />\n    <member type='way' ref='266160718' role='' />\n    <member type='way' ref='451835953' role='' />\n    <member type='way' ref='450833445' role='' />\n    <member type='way' ref='455835784' role='' />\n    <member type='way' ref='455835783' role='' />\n    <member type='way' ref='455835782' role='' />\n    <member type='way' ref='455835779' role='' />\n    <member type='way' ref='284412712' role='' />\n    <member type='way' ref='130989059' role='' />\n    <member type='way' ref='469381404' role='' />\n    <member type='way' ref='455835777' role='' />\n    <member type='way' ref='4498657' role='' />\n    <member type='way' ref='307341994' role='' />\n    <member type='way' ref='204412193' role='' />\n    <member type='way' ref='116615261' role='' />\n    <member type='way' ref='286430805' role='' />\n    <member type='way' ref='28605282' role='' />\n    <member type='way' ref='27881413' role='' />\n    <member type='way' ref='36861884' role='' />\n    <member type='way' ref='168975966' role='' />\n    <member type='way' ref='160489662' role='' />\n    <member type='way' ref='285893666' role='' />\n    <member type='way' ref='237642411' role='' />\n    <member type='way' ref='285893664' role='' />\n    <member type='way' ref='285893668' role='' />\n    <member type='way' ref='285893665' role='' />\n    <member type='way' ref='285893667' role='' />\n    <member type='way' ref='40814687' role='' />\n    <member type='way' ref='40814688' role='' />\n    <member type='way' ref='165346635' role='' />\n    <member type='way' ref='285893669' role='' />\n    <member type='way' ref='165346636' role='' />\n    <member type='way' ref='182725088' role='' />\n    <member type='way' ref='4077779' role='' />\n    <member type='way' ref='296093122' role='' />\n    <member type='way' ref='4040978' role='' />\n    <member type='way' ref='361204262' role='' />\n    <member type='way' ref='361204265' role='' />\n    <member type='way' ref='296093116' role='' />\n    <member type='way' ref='10958445' role='' />\n    <member type='way' ref='10958444' role='' />\n    <member type='way' ref='296093117' role='' />\n    <member type='way' ref='4182320' role='' />\n    <member type='way' ref='296093115' role='' />\n    <member type='way' ref='4182405' role='' />\n    <member type='way' ref='296533439' role='' />\n    <member type='way' ref='182725091' role='' />\n    <member type='way' ref='440338458' role='' />\n    <member type='way' ref='4182406' role='' />\n    <member type='way' ref='440338459' role='' />\n    <member type='way' ref='38816759' role='' />\n    <member type='way' ref='39887915' role='' />\n    <member type='way' ref='38816819' role='' />\n    <member type='way' ref='38816840' role='' />\n    <member type='way' ref='40129164' role='' />\n    <member type='way' ref='38816970' role='' />\n    <member type='way' ref='39952969' role='' />\n    <member type='way' ref='296512985' role='' />\n    <member type='way' ref='24936411' role='' />\n    <member type='way' ref='24936412' role='' />\n    <member type='way' ref='24960394' role='' />\n    <member type='way' ref='24960395' role='' />\n    <member type='way' ref='40127972' role='' />\n    <member type='way' ref='40127973' role='' />\n    <member type='way' ref='40127905' role='' />\n    <member type='way' ref='40127922' role='' />\n    <member type='way' ref='40127867' role='' />\n    <member type='way' ref='40127868' role='' />\n    <member type='way' ref='40127850' role='' />\n    <member type='way' ref='40127849' role='' />\n    <member type='way' ref='24940721' role='' />\n    <member type='way' ref='24940722' role='' />\n    <member type='way' ref='40127513' role='' />\n    <member type='way' ref='40127514' role='' />\n    <member type='way' ref='40127470' role='' />\n    <member type='way' ref='40127471' role='' />\n    <member type='way' ref='40127428' role='' />\n    <member type='way' ref='40127427' role='' />\n    <member type='way' ref='40127253' role='' />\n    <member type='way' ref='40127236' role='' />\n    <member type='way' ref='40191763' role='' />\n    <member type='way' ref='40127106' role='' />\n    <member type='way' ref='4845988' role='' />\n    <member type='way' ref='40082302' role='' />\n    <member type='way' ref='40082288' role='' />\n    <member type='way' ref='220533700' role='' />\n    <member type='way' ref='220533702' role='' />\n    <member type='way' ref='28679387' role='' />\n    <member type='way' ref='28679388' role='' />\n    <member type='way' ref='28679378' role='' />\n    <member type='way' ref='28679384' role='' />\n    <member type='way' ref='28523773' role='' />\n    <member type='way' ref='28523772' role='' />\n    <member type='way' ref='28520724' role='' />\n    <member type='way' ref='28586426' role='' />\n    <member type='way' ref='28520311' role='' />\n    <member type='way' ref='28586447' role='' />\n    <member type='way' ref='40153713' role='' />\n    <member type='way' ref='40076967' role='' />\n    <member type='way' ref='30267414' role='' />\n    <member type='way' ref='30267415' role='' />\n    <member type='way' ref='28886481' role='' />\n    <member type='way' ref='28886477' role='' />\n    <member type='way' ref='28888779' role='' />\n    <member type='way' ref='28888759' role='' />\n    <member type='way' ref='23314626' role='' />\n    <member type='way' ref='29680521' role='' />\n    <member type='way' ref='29680368' role='' />\n    <member type='way' ref='143334982' role='' />\n    <member type='way' ref='143334975' role='' />\n    <member type='way' ref='24596433' role='' />\n    <member type='way' ref='572610326' role='' />\n    <member type='way' ref='8134212' role='' />\n    <member type='way' ref='31072786' role='' />\n    <member type='way' ref='31072787' role='' />\n    <member type='way' ref='40070887' role='' />\n    <member type='way' ref='211026514' role='' />\n    <member type='way' ref='8134211' role='' />\n    <member type='way' ref='4247977' role='' />\n    <member type='way' ref='40070460' role='' />\n    <member type='way' ref='40070054' role='' />\n    <member type='way' ref='40070255' role='' />\n    <member type='way' ref='40070349' role='' />\n    <member type='way' ref='4247979' role='' />\n    <member type='way' ref='279073005' role='' />\n    <member type='way' ref='279073014' role='' />\n    <member type='way' ref='279073011' role='' />\n    <member type='way' ref='279073020' role='' />\n    <member type='way' ref='279073008' role='' />\n    <member type='way' ref='279073017' role='' />\n    <member type='way' ref='279073007' role='' />\n    <member type='way' ref='279073015' role='' />\n    <member type='way' ref='182725101' role='' />\n    <member type='way' ref='38964045' role='' />\n    <member type='way' ref='38964052' role='' />\n    <member type='way' ref='31072806' role='' />\n    <member type='way' ref='31072808' role='' />\n    <member type='way' ref='40069603' role='' />\n    <member type='way' ref='211026513' role='' />\n    <member type='way' ref='146196150' role='' />\n    <member type='way' ref='146196155' role='' />\n    <member type='way' ref='146196151' role='' />\n    <member type='way' ref='146196148' role='' />\n    <member type='way' ref='146196153' role='' />\n    <member type='way' ref='146196154' role='' />\n    <member type='way' ref='38963978' role='' />\n    <member type='way' ref='40069619' role='' />\n    <member type='way' ref='31072815' role='' />\n    <member type='way' ref='31072817' role='' />\n    <member type='way' ref='24392405' role='' />\n    <member type='way' ref='146193754' role='' />\n    <member type='way' ref='145296226' role='' />\n    <member type='way' ref='145296214' role='' />\n    <member type='way' ref='24327374' role='' />\n    <member type='way' ref='23636200' role='' />\n    <member type='way' ref='572558601' role='' />\n    <member type='way' ref='572558593' role='' />\n    <member type='way' ref='386805395' role='' />\n    <member type='way' ref='8176505' role='' />\n    <member type='way' ref='386805409' role='' />\n    <member type='way' ref='319793722' role='' />\n    <member type='way' ref='450080908' role='' />\n    <member type='way' ref='68019855' role='' />\n    <member type='way' ref='386805401' role='' />\n    <member type='way' ref='68019862' role='' />\n    <member type='way' ref='68019859' role='' />\n    <member type='way' ref='90400249' role='' />\n    <member type='way' ref='386805398' role='' />\n    <member type='way' ref='370522523' role='' />\n    <member type='way' ref='90400324' role='' />\n    <member type='way' ref='23679124' role='' />\n    <member type='way' ref='386805405' role='' />\n    <member type='way' ref='153767709' role='' />\n    <member type='way' ref='214712272' role='' />\n    <member type='way' ref='372830909' role='' />\n    <member type='way' ref='372830910' role='' />\n    <member type='way' ref='439909730' role='' />\n    <member type='way' ref='375226731' role='' />\n    <member type='way' ref='95914917' role='' />\n    <member type='way' ref='388070279' role='' />\n    <member type='way' ref='531226944' role='' />\n    <member type='way' ref='388070402' role='' />\n    <member type='way' ref='411108638' role='' />\n    <member type='way' ref='411108638' role='' />\n    <member type='way' ref='491895463' role='' />\n    <member type='way' ref='386808188' role='' />\n    <member type='way' ref='388070279' role='' />\n    <member type='way' ref='95914917' role='' />\n    <member type='way' ref='95914921' role='' />\n    <member type='way' ref='95914916' role='' />\n    <member type='way' ref='184497382' role='' />\n    <member type='way' ref='95915498' role='' />\n    <member type='way' ref='95969733' role='' />\n    <member type='way' ref='372687461' role='' />\n    <member type='way' ref='24764634' role='' />\n    <member type='way' ref='95969777' role='' />\n    <member type='way' ref='23635861' role='' />\n    <member type='way' ref='23635862' role='' />\n    <member type='way' ref='23983046' role='' />\n    <member type='way' ref='145296224' role='' />\n    <member type='way' ref='145296211' role='' />\n    <member type='way' ref='191646743' role='' />\n    <member type='way' ref='191646745' role='' />\n    <member type='way' ref='97144610' role='' />\n    <member type='way' ref='80234416' role='' />\n    <member type='way' ref='224447170' role='' />\n    <member type='way' ref='224447173' role='' />\n    <member type='way' ref='24928236' role='' />\n    <member type='way' ref='24928237' role='' />\n    <member type='way' ref='24544550' role='' />\n    <member type='way' ref='24544551' role='' />\n    <member type='way' ref='24741612' role='' />\n    <member type='way' ref='4248177' role='' />\n    <member type='way' ref='182725105' role='' />\n    <member type='way' ref='24935207' role='' />\n    <member type='way' ref='572541274' role='' />\n    <member type='way' ref='572541275' role='' />\n    <member type='way' ref='24935551' role='' />\n    <member type='way' ref='24935249' role='' />\n    <member type='way' ref='4248230' role='' />\n    <member type='way' ref='277557870' role='' />\n    <member type='way' ref='61411041' role='' />\n    <member type='way' ref='24496384' role='' />\n    <member type='way' ref='25303191' role='' />\n    <member type='way' ref='329858065' role='' />\n    <member type='way' ref='278784724' role='' />\n    <member type='way' ref='278784729' role='' />\n    <member type='way' ref='278784727' role='' />\n    <member type='way' ref='278784730' role='' />\n    <member type='way' ref='28164970' role='' />\n    <member type='way' ref='61411039' role='' />\n    <member type='way' ref='26291277' role='' />\n    <member type='way' ref='8133701' role='' />\n    <member type='way' ref='59195864' role='' />\n    <member type='way' ref='66245186' role='' />\n    <member type='way' ref='59195865' role='' />\n    <member type='way' ref='101784056' role='' />\n    <member type='way' ref='101784053' role='' />\n    <member type='way' ref='4066921' role='' />\n    <member type='way' ref='297960129' role='' />\n    <member type='way' ref='297960128' role='' />\n    <member type='way' ref='71954443' role='' />\n    <member type='way' ref='30104745' role='' />\n    <member type='way' ref='228796379' role='' />\n    <member type='way' ref='89353017' role='' />\n    <member type='way' ref='262098090' role='' />\n    <member type='way' ref='262098087' role='' />\n    <member type='way' ref='292341778' role='' />\n    <member type='way' ref='32338890' role='' />\n    <member type='way' ref='262098089' role='' />\n    <member type='way' ref='88573407' role='' />\n    <member type='way' ref='188751900' role='' />\n    <member type='way' ref='145991345' role='' />\n    <member type='way' ref='31784369' role='' />\n    <member type='way' ref='228796373' role='' />\n    <member type='way' ref='47379982' role='' />\n    <member type='way' ref='356858716' role='' />\n    <member type='way' ref='59102606' role='' />\n    <member type='way' ref='388070400' role='' />\n    <member type='way' ref='397437373' role='' />\n    <member type='way' ref='386817387' role='' />\n    <member type='way' ref='334753882' role='' />\n    <member type='way' ref='334753878' role='' />\n    <member type='way' ref='410957045' role='' />\n    <member type='way' ref='410957042' role='' />\n    <member type='way' ref='334753858' role='' />\n    <member type='way' ref='356858717' role='' />\n    <member type='way' ref='356858731' role='' />\n    <member type='way' ref='356858719' role='' />\n    <member type='way' ref='356858730' role='' />\n    <member type='way' ref='59102613' role='' />\n    <member type='way' ref='397437373' role='' />\n    <member type='way' ref='386817387' role='' />\n    <member type='way' ref='59102614' role='' />\n    <member type='way' ref='410957046' role='' />\n    <member type='way' ref='59102602' role='' />\n    <member type='way' ref='386817385' role='' />\n    <member type='way' ref='23948285' role='' />\n    <member type='way' ref='342571575' role='' />\n    <member type='way' ref='71408052' role='' />\n    <member type='way' ref='245122603' role='' />\n    <member type='way' ref='31051285' role='' />\n    <member type='way' ref='31051286' role='' />\n    <member type='way' ref='246308600' role='' />\n    <member type='way' ref='246384284' role='' />\n    <member type='way' ref='148825033' role='' />\n    <member type='way' ref='457461058' role='' />\n    <member type='way' ref='236141153' role='' />\n    <member type='way' ref='149398555' role='' />\n    <member type='way' ref='457461060' role='' />\n    <member type='way' ref='56082366' role='' />\n    <member type='way' ref='86960806' role='' />\n    <member type='way' ref='56082367' role='' />\n    <member type='way' ref='86960838' role='' />\n    <member type='way' ref='55849842' role='' />\n    <member type='way' ref='31056241' role='' />\n    <member type='way' ref='46739877' role='' />\n    <member type='way' ref='229026962' role='' />\n    <member type='way' ref='25731772' role='' />\n    <member type='way' ref='25731773' role='' />\n    <member type='way' ref='25731762' role='' />\n    <member type='way' ref='25731765' role='' />\n    <member type='way' ref='25731766' role='' />\n    <member type='way' ref='386817381' role='' />\n    <member type='way' ref='23947676' role='' />\n    <member type='way' ref='4248350' role='' />\n    <member type='way' ref='37995156' role='' />\n    <member type='way' ref='37995157' role='' />\n    <member type='way' ref='29265047' role='' />\n    <member type='way' ref='47361639' role='' />\n    <member type='way' ref='29265046' role='' />\n    <member type='way' ref='432736168' role='' />\n    <member type='way' ref='47361640' role='' />\n    <member type='way' ref='432736169' role='' />\n    <member type='way' ref='20581096' role='' />\n    <member type='way' ref='227763717' role='' />\n    <member type='way' ref='227763721' role='' />\n    <member type='way' ref='4248393' role='' />\n    <member type='way' ref='23842884' role='' />\n    <member type='way' ref='23842885' role='' />\n    <member type='way' ref='37502550' role='' />\n    <member type='way' ref='23846284' role='' />\n    <member type='way' ref='27523735' role='' />\n    <member type='way' ref='23712484' role='' />\n    <member type='way' ref='244502595' role='' />\n    <member type='way' ref='27777182' role='' />\n    <member type='way' ref='27777183' role='' />\n    <member type='way' ref='244502597' role='' />\n    <member type='way' ref='341187586' role='' />\n    <member type='way' ref='23318016' role='' />\n    <member type='way' ref='23318017' role='' />\n    <member type='way' ref='24823524' role='' />\n    <member type='way' ref='24823525' role='' />\n    <member type='way' ref='23318026' role='' />\n    <member type='way' ref='27356556' role='' />\n    <member type='way' ref='27647137' role='' />\n    <member type='way' ref='572528705' role='' />\n    <member type='way' ref='25417012' role='' />\n    <member type='way' ref='28113396' role='' />\n    <member type='way' ref='301830283' role='' />\n    <member type='way' ref='301830284' role='' />\n    <member type='way' ref='301830282' role='' />\n    <member type='way' ref='284982061' role='' />\n    <member type='way' ref='301850672' role='' />\n    <member type='way' ref='301850668' role='' />\n    <member type='way' ref='301850671' role='' />\n    <member type='way' ref='25031744' role='' />\n    <member type='way' ref='25031745' role='' />\n    <member type='way' ref='4453822' role='' />\n    <member type='way' ref='301890309' role='' />\n    <member type='way' ref='301850670' role='' />\n    <member type='way' ref='268727953' role='' />\n    <member type='way' ref='23401298' role='' />\n    <member type='way' ref='23401299' role='' />\n    <member type='way' ref='364258990' role='' />\n    <member type='way' ref='299536951' role='' />\n    <member type='way' ref='315077764' role='' />\n    <member type='way' ref='136531747' role='' />\n    <member type='way' ref='136531742' role='' />\n    <member type='way' ref='301850675' role='' />\n    <member type='way' ref='136531743' role='' />\n    <member type='way' ref='136773636' role='' />\n    <member type='way' ref='467651806' role='' />\n    <member type='way' ref='364258988' role='' />\n    <member type='way' ref='28480283' role='' />\n    <member type='way' ref='28480284' role='' />\n    <member type='way' ref='184362451' role='' />\n    <member type='way' ref='184362448' role='' />\n    <member type='way' ref='386655216' role='' />\n    <member type='way' ref='386820947' role='' />\n    <member type='way' ref='27647131' role='' />\n    <member type='way' ref='27647130' role='' />\n    <member type='way' ref='139659168' role='' />\n    <member type='way' ref='189255262' role='' />\n    <member type='way' ref='189255267' role='' />\n    <member type='way' ref='203023142' role='' />\n    <member type='way' ref='280989617' role='' />\n    <member type='way' ref='189255257' role='' />\n    <member type='way' ref='189255260' role='' />\n    <member type='way' ref='189255263' role='' />\n    <member type='way' ref='58711277' role='' />\n    <member type='way' ref='189255272' role='' />\n    <member type='way' ref='139669589' role='' />\n    <member type='way' ref='28302737' role='' />\n    <member type='way' ref='28302725' role='' />\n    <member type='way' ref='24161692' role='' />\n    <member type='way' ref='139667343' role='' />\n    <member type='way' ref='608281812' role='' />\n    <member type='way' ref='136913721' role='' />\n    <member type='way' ref='136913724' role='' />\n    <member type='way' ref='585774518' role='' />\n    <member type='way' ref='386834377' role='' />\n    <member type='way' ref='585774517' role='' />\n    <member type='way' ref='136913720' role='' />\n    <member type='way' ref='28552696' role='' />\n    <member type='way' ref='585774522' role='' />\n    <member type='way' ref='386834387' role='' />\n    <member type='way' ref='585774521' role='' />\n    <member type='way' ref='220465450' role='' />\n    <member type='way' ref='30032550' role='' />\n    <member type='way' ref='23618294' role='' />\n    <member type='way' ref='28552688' role='' />\n    <member type='way' ref='28552689' role='' />\n    <member type='way' ref='386834379' role='' />\n    <member type='way' ref='28545230' role='' />\n    <member type='way' ref='386834371' role='' />\n    <member type='way' ref='326226436' role='' />\n    <member type='way' ref='125768393' role='' />\n    <member type='way' ref='23423531' role='' />\n    <member type='way' ref='231390463' role='' />\n    <member type='way' ref='231390462' role='' />\n    <member type='way' ref='386834374' role='' />\n    <member type='way' ref='23423525' role='' />\n    <member type='way' ref='32090567' role='' />\n    <member type='way' ref='23423536' role='' />\n    <member type='way' ref='23101094' role='' />\n    <member type='way' ref='386834376' role='' />\n    <member type='way' ref='23618453' role='' />\n    <member type='way' ref='149178397' role='' />\n    <member type='way' ref='149178398' role='' />\n    <member type='way' ref='386834373' role='' />\n    <member type='way' ref='4518070' role='' />\n    <member type='way' ref='148505199' role='' />\n    <member type='way' ref='460408778' role='' />\n    <member type='way' ref='119253155' role='' />\n    <member type='way' ref='119253158' role='' />\n    <member type='way' ref='25046564' role='' />\n    <member type='way' ref='25046565' role='' />\n    <member type='way' ref='28289490' role='' />\n    <member type='way' ref='148899495' role='' />\n    <member type='way' ref='148899457' role='' />\n    <member type='way' ref='171277138' role='' />\n    <member type='way' ref='171277143' role='' />\n    <member type='way' ref='148899483' role='' />\n    <member type='way' ref='8123176' role='' />\n    <member type='way' ref='148500437' role='' />\n    <member type='way' ref='370255939' role='' />\n    <member type='way' ref='370255937' role='' />\n    <member type='way' ref='304383858' role='' />\n    <member type='way' ref='23283660' role='' />\n    <member type='way' ref='370256031' role='' />\n    <member type='way' ref='22771792' role='' />\n    <member type='way' ref='4517829' role='' />\n    <member type='way' ref='370256035' role='' />\n    <member type='way' ref='503035490' role='' />\n    <member type='way' ref='375989578' role='' />\n    <member type='way' ref='375989707' role='' />\n    <member type='way' ref='503035487' role='' />\n    <member type='way' ref='503035488' role='' />\n    <member type='way' ref='493061268' role='' />\n    <member type='way' ref='38069230' role='' />\n    <member type='way' ref='34434314' role='' />\n    <member type='way' ref='503035492' role='' />\n    <member type='way' ref='34434316' role='' />\n    <member type='way' ref='324884579' role='' />\n    <member type='way' ref='24470344' role='' />\n    <member type='way' ref='92162860' role='' />\n    <member type='way' ref='386834386' role='' />\n    <member type='way' ref='92162862' role='' />\n    <member type='way' ref='488272332' role='' />\n    <member type='way' ref='488272331' role='' />\n    <member type='way' ref='488272323' role='' />\n    <member type='way' ref='488272322' role='' />\n    <member type='way' ref='488272318' role='' />\n    <member type='way' ref='488272319' role='' />\n    <member type='way' ref='37706758' role='' />\n    <member type='way' ref='37706761' role='' />\n    <member type='way' ref='488272313' role='' />\n    <member type='way' ref='488272307' role='' />\n    <member type='way' ref='388083006' role='' />\n    <member type='way' ref='488272304' role='' />\n    <member type='way' ref='488272305' role='' />\n    <member type='way' ref='121198958' role='' />\n    <member type='way' ref='1698015' role='' />\n    <member type='way' ref='388313109' role='' />\n    <member type='way' ref='388070394' role='' />\n    <member type='way' ref='386834382' role='' />\n    <member type='way' ref='557874837' role='' />\n    <member type='way' ref='1880523' role='' />\n    <member type='way' ref='4373482' role='' />\n    <member type='way' ref='488272306' role='' />\n    <member type='way' ref='233833234' role='' />\n    <member type='way' ref='388083006' role='' />\n    <member type='way' ref='488272304' role='' />\n    <member type='way' ref='488272305' role='' />\n    <member type='way' ref='121198958' role='' />\n    <member type='way' ref='1698015' role='' />\n    <member type='way' ref='121198960' role='' />\n    <member type='way' ref='121198955' role='' />\n    <member type='way' ref='24418625' role='' />\n    <member type='way' ref='24418626' role='' />\n    <member type='way' ref='121198956' role='' />\n    <member type='way' ref='386834392' role='' />\n    <member type='way' ref='30791228' role='' />\n    <member type='way' ref='119719508' role='' />\n    <member type='way' ref='30791016' role='' />\n    <member type='way' ref='140293280' role='' />\n    <member type='way' ref='357265305' role='' />\n    <member type='way' ref='357265309' role='' />\n    <member type='way' ref='357265310' role='' />\n    <member type='way' ref='386834388' role='' />\n    <member type='way' ref='8147809' role='' />\n    <member type='way' ref='27224364' role='' />\n    <member type='way' ref='121257657' role='' />\n    <member type='way' ref='10631185' role='' />\n    <member type='way' ref='422789453' role='' />\n    <member type='way' ref='422789581' role='' />\n    <member type='way' ref='4327218' role='' />\n    <member type='way' ref='422789574' role='' />\n    <member type='way' ref='46244971' role='' />\n    <member type='way' ref='108849017' role='' />\n    <member type='way' ref='3676667' role='' />\n    <member type='way' ref='422789543' role='' />\n    <member type='way' ref='108849007' role='' />\n    <member type='way' ref='3542463' role='' />\n    <member type='way' ref='422789545' role='' />\n    <member type='way' ref='422789546' role='' />\n    <member type='way' ref='237108787' role='' />\n    <member type='way' ref='422789541' role='' />\n    <member type='way' ref='422789547' role='' />\n    <member type='way' ref='191619082' role='' />\n    <member type='way' ref='422789565' role='' />\n    <member type='way' ref='422789560' role='' />\n    <member type='way' ref='422789561' role='' />\n    <member type='way' ref='210643712' role='' />\n    <member type='way' ref='90714001' role='' />\n    <member type='way' ref='422789539' role='' />\n    <member type='way' ref='422789569' role='' />\n    <member type='way' ref='416688412' role='' />\n    <member type='way' ref='3542462' role='' />\n    <member type='way' ref='416688430' role='' />\n    <member type='way' ref='4892386' role='' />\n    <member type='way' ref='46156153' role='' />\n    <member type='way' ref='90583368' role='' />\n    <member type='way' ref='149443272' role='' />\n    <member type='way' ref='575128631' role='' />\n    <member type='way' ref='233878940' role='' />\n    <member type='way' ref='496703336' role='' />\n    <member type='way' ref='121284722' role='' />\n    <tag k='from' v='Oslo' />\n    <tag k='from:da' v='Oslo' />\n    <tag k='from:en' v='Oslo' />\n    <tag k='from:nb' v='Oslo' />\n    <tag k='importance' v='national' />\n    <tag k='name' v='Buss 820: Oslo - København' />\n    <tag k='name:da' v='Bus 820: Oslo - København' />\n    <tag k='name:en' v='Bus 820: Oslo - Copenhagen' />\n    <tag k='name:nb' v='Buss 820: Oslo - København' />\n    <tag k='network' v='Swebus' />\n    <tag k='operator' v='Swebus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='820' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='København' />\n    <tag k='to:da' v='København' />\n    <tag k='to:en' v='Copenhagen' />\n    <tag k='to:nb' v='København' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='7084215' timestamp='2018-03-24T08:19:48Z' uid='5853411' user='j1scad' version='3' changeset='57477338'>\n    <member type='way' ref='304383859' role='forward' />\n    <member type='way' ref='370255938' role='forward' />\n    <member type='way' ref='370255940' role='forward' />\n    <member type='way' ref='148500436' role='forward' />\n    <member type='way' ref='219657906' role='forward' />\n    <member type='way' ref='148899471' role='forward' />\n    <member type='way' ref='148899453' role='forward' />\n    <member type='way' ref='219657860' role='forward' />\n    <member type='way' ref='148899506' role='forward' />\n    <member type='way' ref='219657822' role='forward' />\n    <member type='way' ref='28289483' role='forward' />\n    <member type='way' ref='28289485' role='forward' />\n    <member type='way' ref='25046566' role='forward' />\n    <member type='way' ref='25046567' role='forward' />\n    <member type='way' ref='119253153' role='forward' />\n    <member type='way' ref='119253150' role='forward' />\n    <member type='way' ref='388070386' role='forward' />\n    <member type='way' ref='15729157' role='forward' />\n    <member type='way' ref='107382808' role='forward' />\n    <member type='way' ref='107382797' role='forward' />\n    <member type='way' ref='28289473' role='forward' />\n    <member type='way' ref='28289477' role='forward' />\n    <member type='way' ref='326419705' role='forward' />\n    <member type='way' ref='301821089' role='forward' />\n    <member type='way' ref='28166520' role='forward' />\n    <member type='way' ref='301821087' role='forward' />\n    <member type='way' ref='28841846' role='forward' />\n    <member type='way' ref='28841847' role='forward' />\n    <member type='way' ref='211687634' role='forward' />\n    <member type='way' ref='211687633' role='forward' />\n    <member type='way' ref='301822759' role='forward' />\n    <member type='way' ref='301822762' role='forward' />\n    <member type='way' ref='28288769' role='forward' />\n    <member type='way' ref='28288770' role='forward' />\n    <member type='way' ref='28945196' role='forward' />\n    <member type='way' ref='28945197' role='forward' />\n    <member type='way' ref='28612815' role='forward' />\n    <member type='way' ref='28612816' role='forward' />\n    <member type='way' ref='28287284' role='forward' />\n    <member type='way' ref='28287285' role='forward' />\n    <member type='way' ref='28287281' role='forward' />\n    <member type='way' ref='301827524' role='forward' />\n    <member type='way' ref='28287275' role='forward' />\n    <member type='way' ref='136325302' role='forward' />\n    <member type='way' ref='136325301' role='forward' />\n    <member type='way' ref='301827523' role='forward' />\n    <member type='way' ref='136325311' role='forward' />\n    <member type='way' ref='136325313' role='forward' />\n    <member type='way' ref='301827522' role='forward' />\n    <member type='way' ref='28442171' role='forward' />\n    <member type='way' ref='301830280' role='forward' />\n    <member type='way' ref='28165774' role='forward' />\n    <member type='way' ref='284982064' role='forward' />\n    <member type='way' ref='27647139' role='forward' />\n    <member type='way' ref='572528704' role='forward' />\n    <member type='way' ref='27647140' role='forward' />\n    <member type='way' ref='23318024' role='forward' />\n    <member type='way' ref='23318025' role='forward' />\n    <member type='way' ref='24823526' role='forward' />\n    <member type='way' ref='24823527' role='forward' />\n    <member type='way' ref='23318014' role='forward' />\n    <member type='way' ref='23318015' role='forward' />\n    <member type='way' ref='341187587' role='forward' />\n    <member type='way' ref='244502594' role='forward' />\n    <member type='way' ref='27777184' role='forward' />\n    <member type='way' ref='27777185' role='forward' />\n    <member type='way' ref='244502596' role='forward' />\n    <member type='way' ref='27523733' role='forward' />\n    <member type='way' ref='24524471' role='forward' />\n    <member type='way' ref='37502548' role='forward' />\n    <member type='way' ref='37502549' role='forward' />\n    <member type='way' ref='23842882' role='forward' />\n    <member type='way' ref='20581098' role='forward' />\n    <member type='way' ref='227763719' role='forward' />\n    <member type='way' ref='227763718' role='forward' />\n    <member type='way' ref='227763720' role='forward' />\n    <member type='way' ref='432736170' role='forward' />\n    <member type='way' ref='8136880' role='forward' />\n    <member type='way' ref='241985604' role='forward' />\n    <member type='way' ref='43444062' role='forward' />\n    <member type='way' ref='241985603' role='forward' />\n    <member type='way' ref='37995158' role='forward' />\n    <member type='way' ref='37995159' role='forward' />\n    <member type='way' ref='23947678' role='forward' />\n    <member type='way' ref='388070286' role='forward' />\n    <member type='way' ref='23947679' role='forward' />\n    <member type='way' ref='191655598' role='forward' />\n    <member type='way' ref='182731881' role='forward' />\n    <member type='way' ref='182731882' role='forward' />\n    <member type='way' ref='191655601' role='forward' />\n    <member type='way' ref='28165162' role='forward' />\n    <member type='way' ref='25303194' role='forward' />\n    <member type='way' ref='191655600' role='forward' />\n    <member type='way' ref='25724534' role='forward' />\n    <member type='way' ref='278784725' role='forward' />\n    <member type='way' ref='278784728' role='forward' />\n    <member type='way' ref='278784726' role='forward' />\n    <member type='way' ref='278784731' role='forward' />\n    <member type='way' ref='329830321' role='forward' />\n    <member type='way' ref='329830320' role='forward' />\n    <member type='way' ref='24496386' role='forward' />\n    <member type='way' ref='24496387' role='forward' />\n    <member type='way' ref='61411040' role='forward' />\n    <member type='way' ref='38443331' role='forward' />\n    <member type='way' ref='38443341' role='forward' />\n    <member type='way' ref='24935251' role='forward' />\n    <member type='way' ref='24935252' role='forward' />\n    <member type='way' ref='370255937' role='forward' />\n    <member type='way' ref='370255939' role='forward' />\n    <member type='way' ref='148500437' role='forward' />\n    <member type='way' ref='8123176' role='forward' />\n    <member type='way' ref='148899483' role='forward' />\n    <member type='way' ref='171277143' role='forward' />\n    <member type='way' ref='171277138' role='forward' />\n    <member type='way' ref='148899457' role='forward' />\n    <member type='way' ref='148899495' role='forward' />\n    <member type='way' ref='28289490' role='forward' />\n    <member type='way' ref='25046565' role='forward' />\n    <member type='way' ref='25046564' role='forward' />\n    <member type='way' ref='119253158' role='forward' />\n    <member type='way' ref='119253155' role='forward' />\n    <member type='way' ref='119253143' role='forward' />\n    <member type='way' ref='15729154' role='forward' />\n    <member type='way' ref='15729153' role='forward' />\n    <member type='way' ref='107382830' role='forward' />\n    <member type='way' ref='28289471' role='forward' />\n    <member type='way' ref='28289474' role='forward' />\n    <member type='way' ref='326419704' role='forward' />\n    <member type='way' ref='301821086' role='forward' />\n    <member type='way' ref='28165804' role='forward' />\n    <member type='way' ref='301821083' role='forward' />\n    <member type='way' ref='301821084' role='forward' />\n    <member type='way' ref='27405024' role='forward' />\n    <member type='way' ref='28841848' role='forward' />\n    <member type='way' ref='211687636' role='forward' />\n    <member type='way' ref='211687635' role='forward' />\n    <member type='way' ref='301822761' role='forward' />\n    <member type='way' ref='301822760' role='forward' />\n    <member type='way' ref='28288774' role='forward' />\n    <member type='way' ref='28288772' role='forward' />\n    <member type='way' ref='28945195' role='forward' />\n    <member type='way' ref='28945194' role='forward' />\n    <member type='way' ref='28612819' role='forward' />\n    <member type='way' ref='28612818' role='forward' />\n    <member type='way' ref='28287287' role='forward' />\n    <member type='way' ref='28287286' role='forward' />\n    <member type='way' ref='28287280' role='forward' />\n    <member type='way' ref='28287277' role='forward' />\n    <member type='way' ref='136325303' role='forward' />\n    <member type='way' ref='136325306' role='forward' />\n    <member type='way' ref='301830278' role='forward' />\n    <member type='way' ref='136325307' role='forward' />\n    <member type='way' ref='136325308' role='forward' />\n    <member type='way' ref='301830281' role='forward' />\n    <member type='way' ref='28442173' role='forward' />\n    <member type='way' ref='4248455' role='forward' />\n    <member type='way' ref='284982063' role='forward' />\n    <member type='way' ref='25417012' role='forward' />\n    <member type='way' ref='572528705' role='forward' />\n    <member type='way' ref='27647137' role='forward' />\n    <member type='way' ref='27356556' role='forward' />\n    <member type='way' ref='23318026' role='forward' />\n    <member type='way' ref='24823525' role='forward' />\n    <member type='way' ref='24823524' role='forward' />\n    <member type='way' ref='23318017' role='forward' />\n    <member type='way' ref='23318016' role='forward' />\n    <member type='way' ref='341187586' role='forward' />\n    <member type='way' ref='244502597' role='forward' />\n    <member type='way' ref='27777183' role='forward' />\n    <member type='way' ref='27777182' role='forward' />\n    <member type='way' ref='244502595' role='forward' />\n    <member type='way' ref='23712484' role='forward' />\n    <member type='way' ref='27523735' role='forward' />\n    <member type='way' ref='23846284' role='forward' />\n    <member type='way' ref='37502550' role='forward' />\n    <member type='way' ref='23842885' role='forward' />\n    <member type='way' ref='23842884' role='forward' />\n    <member type='way' ref='4248393' role='forward' />\n    <member type='way' ref='227763721' role='forward' />\n    <member type='way' ref='227763717' role='forward' />\n    <member type='way' ref='20581096' role='forward' />\n    <member type='way' ref='432736169' role='forward' />\n    <member type='way' ref='47361640' role='forward' />\n    <member type='way' ref='432736168' role='forward' />\n    <member type='way' ref='29265046' role='forward' />\n    <member type='way' ref='47361639' role='forward' />\n    <member type='way' ref='29265047' role='forward' />\n    <member type='way' ref='37995157' role='forward' />\n    <member type='way' ref='37995156' role='forward' />\n    <member type='way' ref='4248350' role='forward' />\n    <member type='way' ref='23947676' role='forward' />\n    <member type='way' ref='386817381' role='forward' />\n    <member type='way' ref='191655599' role='forward' />\n    <member type='way' ref='182725095' role='forward' />\n    <member type='way' ref='191655597' role='forward' />\n    <member type='way' ref='8136878' role='forward' />\n    <member type='way' ref='28165144' role='forward' />\n    <member type='way' ref='61411034' role='forward' />\n    <member type='way' ref='278784730' role='forward' />\n    <member type='way' ref='278784727' role='forward' />\n    <member type='way' ref='278784729' role='forward' />\n    <member type='way' ref='278784724' role='forward' />\n    <member type='way' ref='329858065' role='forward' />\n    <member type='way' ref='25303191' role='forward' />\n    <member type='way' ref='24496384' role='forward' />\n    <member type='way' ref='61411041' role='forward' />\n    <member type='way' ref='277557870' role='forward' />\n    <member type='way' ref='4248230' role='forward' />\n    <member type='way' ref='24935249' role='forward' />\n    <member type='way' ref='24935551' role='forward' />\n    <tag k='name' v='E 20 Skåne län' />\n    <tag k='network' v='e-road' />\n    <tag k='ref' v='E 20' />\n    <tag k='route' v='road' />\n    <tag k='section' v='Skåne län' />\n    <tag k='type' v='route' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/partOfRelationNoTagsAtIntersectionAtlas.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727' />\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <way id='-1410000' action='modify'>\n    <nd ref='-1409998' />\n    <nd ref='-1409999' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-1410008' action='modify'>\n    <nd ref='-1409999' />\n    <nd ref='-1410001' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-1410065' action='modify'>\n    <member type='way' ref='-1410000' role='from' />\n    <member type='way' ref='-1410008' role='to' />\n    <member type='node' ref='-1409999' role='via' />\n    <tag k='restriction' v='no_u_turn' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/partOfRelationNoTagsNoIntersectionAtlas.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843'>\\\n            <tag k='name' v='Some Road' />\n            <tag k='maxspeed' v='70' />\n  </node>\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727' />\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' >\n  \t          <tag k='highway' v='speed_camera' />\n              <tag k='maxspeed' v='70' />\n  </node>\n  <relation id='-1410065' action='modify'>\n    <member type='node' ref='-1409998' role='from' />\n    <member type='node' ref='-1409999' role='to' />\n    <member type='node' ref='-1410001' role='device' />\n    <tag k='type' v='enforcement' />\n    <tag k='enforcement' v='enforcement' />\n    <tag k='maxspeed' v='70' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/partOfRelationWithTagsAtIntersectionAtlas.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727'>\n        <tag k='barrier' v='border_control' />\n  </node>\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <way id='-1410000' action='modify'>\n    <nd ref='-1409998' />\n    <nd ref='-1409999' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-1410008' action='modify'>\n    <nd ref='-1409999' />\n    <nd ref='-1410001' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-1410065' action='modify'>\n    <member type='way' ref='-1410000' role='from' />\n    <member type='way' ref='-1410008' role='to' />\n    <member type='node' ref='-1409999' role='via' />\n    <tag k='restriction' v='no_u_turn' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/partOfRelationWithTagsNoIntersectionAtlas.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727'>\n          <tag k='addr:city' v='FunVille' />\n          <tag k='addr:country' v='Funtopia' />\n          <tag k='addr:housenumber' v='123' />\n          <tag k='addr:street' v='Fun Avenue' />\n  </node>\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <way id='-1410000' action='modify'>\n    <nd ref='-1409998' />\n    <nd ref='-1410001' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-1410065' action='modify'>\n    <member type='way' ref='-1410000' role='street' />\n    <member type='node' ref='-1409999' role='house' />\n    <tag k='type' v='associatedStreet' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/pbf/partialRelation4451979.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='3415739198' timestamp='2017-05-20T13:23:01Z' uid='1665943' user='tomlee721' version='2' changeset='48842799' lat='22.1637556' lon='113.5741442' />\n  <node id='3415739205' timestamp='2015-03-24T11:54:23Z' uid='1665943' user='tomlee721' version='1' changeset='29701609' lat='22.163799' lon='113.5750108' />\n  <node id='4874277047' timestamp='2017-05-24T13:49:11Z' uid='1665943' user='tomlee721' version='1' changeset='48948348' lat='22.163747' lon='113.575233'>\n    <tag k='highway' v='crossing' />\n  </node>\n  <node id='5171821828' timestamp='2017-10-16T19:43:52Z' uid='4351550' user='MyWayOrNoHighway' version='1' changeset='52993388' lat='22.1637368' lon='113.5752765' />\n  <node id='3415739207' timestamp='2015-03-24T11:54:23Z' uid='1665943' user='tomlee721' version='1' changeset='29701609' lat='22.1638592' lon='113.5743462' />\n  <node id='3415739210' timestamp='2017-05-24T13:49:13Z' uid='1665943' user='tomlee721' version='2' changeset='48948348' lat='22.1638968' lon='113.5744353' />\n  <node id='3415739213' timestamp='2015-03-24T11:54:23Z' uid='1665943' user='tomlee721' version='1' changeset='29701609' lat='22.1638986' lon='113.5745278' />\n  <node id='3415739211' timestamp='2017-05-24T13:49:13Z' uid='1665943' user='tomlee721' version='2' changeset='48948348' lat='22.1638783' lon='113.574658' />\n  <node id='3415739212' timestamp='2015-03-24T11:54:23Z' uid='1665943' user='tomlee721' version='1' changeset='29701609' lat='22.1638861' lon='113.5746082' />\n  <node id='3415739209' timestamp='2017-05-20T13:23:01Z' uid='1665943' user='tomlee721' version='2' changeset='48842799' lat='22.1638618' lon='113.5747311' /> \n  <way id='498346222' timestamp='2017-10-16T19:44:04Z' uid='4351550' user='MyWayOrNoHighway' version='2' changeset='52993388'>\n    <nd ref='3415739209' />\n    <nd ref='3415739205' />\n    <nd ref='4874277047' />\n    <nd ref='5171821828' />\n    <tag k='highway' v='service' />\n    <tag k='oneway' v='yes' />\n    <tag k='psv' v='yes' />\n    <tag k='vehicle' v='no' />\n  </way>\n  <way id='498346223' timestamp='2017-06-05T18:08:37Z' uid='1665943' user='tomlee721' version='1' changeset='49280138'>\n    <nd ref='3415739198' />\n    <nd ref='3415739207' />\n    <nd ref='3415739210' />\n    <nd ref='3415739213' />\n    <nd ref='3415739212' />\n    <nd ref='3415739211' />\n    <nd ref='3415739209' />\n    <tag k='highway' v='service' />\n    <tag k='oneway' v='yes' />\n    <tag k='psv' v='yes' />\n    <tag k='vehicle' v='no' />\n  </way>\n  <relation id='4451979' timestamp='2017-12-22T09:53:45Z' uid='1665943' user='tomlee721' version='96' changeset='54836931'>\n    <member type='way' ref='498346223' role='' />\n    <member type='way' ref='498346222' role='' />\n    <tag k='fax' v='+853 2823 4416' />\n    <tag k='name' v='26 路線 Carreira n.º 26' />\n    <tag k='name:en' v='Line 26' />\n    <tag k='name:pt' v='Carreira n.º 26' />\n    <tag k='name:zh' v='26 路線' />\n    <tag k='network' v='Macau' />\n    <tag k='operator' v='新福利 Transmac' />\n    <tag k='phone' v='+853 2827 1122' />\n    <tag k='ref' v='26' />\n    <tag k='route' v='bus' />\n    <tag k='service_times' v='Mo-Su 06:00-00:00' />\n    <tag k='type' v='route' />\n    <tag k='website' v='http://transmac.com.mo/' />\n    <tag k='wheelchair' v='yes' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/DMA_boundary.txt",
    "content": "DMA||POLYGON ((-61.3490238 15.0074943, -61.3702452 15.0074207, -61.4005587 15.0115114, -61.4232934 15.0170995, -61.4499481 15.0267559, -61.4813509 15.0421834, -61.5102395 15.0630642, -61.5388572 15.0931927, -61.5609937 15.1286379, -61.573888 15.1626946, -61.5802577 15.1983142, -61.5802519 15.2182578, -61.5896846 15.2426108, -61.5979214 15.2828215, -61.6116448 15.3052907, -61.6237522 15.3347959, -61.6350822 15.3524338, -61.6486274 15.3851843, -61.6521867 15.3997221, -61.6602503 15.4157614, -61.6719498 15.4490992, -61.6756242 15.4722623, -61.6819285 15.4920564, -61.6869184 15.5259787, -61.6832167 15.6182678, -61.6798235 15.644411, -61.4425 15.7872222, -61.3236111 15.7341667, -61.1383333 15.7016667, -61.1204868 15.6804104, -61.116797 15.6720689, -61.1106596 15.6656506, -61.0968189 15.6460962, -61.083421 15.6300299, -61.0657932 15.5989765, -61.0607846 15.5846292, -61.0567601 15.5769338, -61.055243 15.5742449, -61.0436767 15.541066, -61.036387 15.4952162, -61.0344085 15.4615026, -61.037269 15.4351561, -61.0329895 15.3942999, -61.0364022 15.3589037, -61.0354401 15.3544219, -61.0346416 15.3150813, -61.0368515 15.3029501, -61.0373217 15.2768091, -61.0420306 15.2476706, -61.0442149 15.2375483, -61.0562047 15.2043716, -61.0609591 15.196169, -61.0666737 15.1779782, -61.083955 15.144618, -61.1069788 15.1151873, -61.1349236 15.0905183, -61.1589127 15.0792459, -61.2830556 15.0308333, -61.3490238 15.0074943))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/DNK_SWE_boundary.txt",
    "content": "DNK||POLYGON ((12.76302329063 55.59512845369, 12.80636778831 55.59503145851, 12.80465117455 55.57373521447, 12.76122084618 55.57300734823, 12.76302329063 55.59512845369))\nSWE||POLYGON ((12.81217998751 55.58015672226, 12.81184618447 55.57264298618, 12.82883979379 55.57243711016, 12.82844529928 55.58034540489, 12.81217998751 55.58015672226))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/creation/nestedSingleRelations.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-126692' action='modify' lat='4.64106693493' lon='62.20467537641'>\n    <tag k='duplicate' v='1' />\n  </node>\n  <node id='-126693' action='modify' lat='4.64106693493' lon='62.20467537641'>\n    <tag k='duplicate' v='2' />\n  </node>\n  <node id='-126707' action='modify' lat='5.2548712486' lon='62.26990983733' />\n  <node id='-126708' action='modify' lat='5.26930670118' lon='62.55259250132' />\n  <node id='-126709' action='modify' lat='5.11700779075' lon='62.56043489553' />\n  <node id='-126710' action='modify' lat='5.10256885516' lon='62.27775223154' />\n  <way id='-126711' action='modify'>\n    <nd ref='-126707' />\n    <nd ref='-126708' />\n    <nd ref='-126709' />\n    <nd ref='-126710' />\n    <nd ref='-126707' />\n    <tag k='building' v='yes' />\n  </way>\n  <relation id='-126699' action='modify'>\n    <member type='node' ref='-126692' role='child' />\n    <tag k='type' v='parent1' />\n  </relation>\n  <relation id='-126702' action='modify'>\n    <member type='relation' ref='-126699' role='childrelation' />\n    <tag k='type' v='parent1-1' />\n  </relation>\n  <relation id='-126718' action='modify'>\n    <member type='node' ref='-126693' role='child' />\n    <tag k='type' v='parent2' />\n  </relation>\n  <relation id='-126725' action='modify'>\n    <member type='relation' ref='-126718' role='childrelation' />\n    <tag k='type' v='parent2-2' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/creation/nestedSingleRelations.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM' timestamp='2017-12-19T21:43:02Z'>\n  <node id='126692' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='4.64106693493' lon='62.20467537641'>\n    <tag k='duplicate' v='1' />\n  </node>\n  <node id='126693' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='4.64106693493' lon='62.20467537641'>\n    <tag k='duplicate' v='2' />\n  </node>\n  <node id='126707' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='5.2548712486' lon='62.26990983733' />\n  <node id='126708' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='5.26930670118' lon='62.55259250132' />\n  <node id='126709' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='5.11700779075' lon='62.56043489553' />\n  <node id='126710' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='5.10256885516' lon='62.27775223154' />\n  <way id='126711' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <nd ref='126707' />\n    <nd ref='126708' />\n    <nd ref='126709' />\n    <nd ref='126710' />\n    <nd ref='126707' />\n    <tag k='building' v='yes' />\n  </way>\n  <relation id='126699' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <member type='node' ref='126692' role='child' />\n    <tag k='type' v='parent1' />\n  </relation>\n  <relation id='126702' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <member type='relation' ref='126699' role='childrelation' />\n    <tag k='type' v='parent1-1' />\n  </relation>\n  <relation id='126718' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <member type='node' ref='126693' role='child' />\n    <tag k='type' v='parent2' />\n  </relation>\n  <relation id='126725' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <member type='relation' ref='126718' role='childrelation' />\n    <tag k='type' v='parent2-2' />\n  </relation>\n</osm>"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/osmPbfProcessorTest_keepOutsideWaysThatAreConnected.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38982' action='modify' lat='15.41334125229' lon='-61.47298419757' />\n  <node id='-38985' action='modify' lat='15.36492975189' lon='-61.50526321779' />\n  <node id='-38989' action='modify' lat='15.33264917035' lon='-61.54232431507' />\n  <node id='-38991' action='modify' lat='15.29575097082' lon='-61.66785383813' />\n  <node id='-38993' action='modify' lat='15.27960596378' lon='-61.519609449' />\n  <node id='-38995' action='modify' lat='15.22424222512' lon='-61.4610290049' />\n  <node id='-38997' action='modify' lat='15.17232549314' lon='-61.44070517736' />\n  <node id='-39000' action='modify' lat='15.27037968735' lon='-62.03129169537' />\n  <node id='-39001' action='modify' lat='15.28998503963' lon='-61.85076828602' />\n  <node id='-39018' action='modify' lat='15.44315360338' lon='-61.85223092531' />\n  <node id='-39020' action='modify' lat='15.16350333085' lon='-61.81780162649' />\n  <way id='-38986' action='modify'>\n    <nd ref='-38982' />\n    <nd ref='-38985' />\n    <nd ref='-38989' />\n    <nd ref='-38991' />\n    <nd ref='-38993' />\n    <nd ref='-38995' />\n    <nd ref='-38997' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-39002' action='modify'>\n    <nd ref='-39000' />\n    <nd ref='-39001' />\n    <nd ref='-38991' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-39019' action='modify'>\n    <nd ref='-39018' />\n    <nd ref='-39001' />\n    <nd ref='-39020' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/outsideConnectedOneWayWays.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='118919' timestamp='2013-05-01T20:02:30Z' uid='68904' user='Fredrik Christensson' version='4' changeset='15938546' lat='55.5908582' lon='12.7717941' />\n  <node id='118920' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='8' changeset='33971157' lat='55.5885731' lon='12.7779598' />\n  <node id='118921' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='8' changeset='33971157' lat='55.5874224' lon='12.7812217' />\n  <node id='118922' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='8' changeset='33971157' lat='55.5842981' lon='12.7907604' />\n  <node id='118923' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='8' changeset='33971157' lat='55.5815102' lon='12.8000856' />\n  <node id='118924' timestamp='2015-09-11T22:02:37Z' uid='165061' user='mapper999' version='9' changeset='33971157' lat='55.5814964' lon='12.8001367' />\n  <node id='125398' timestamp='2015-09-11T22:02:38Z' uid='165061' user='mapper999' version='9' changeset='33971157' lat='55.5787981' lon='12.8105754' />\n  <node id='244466743' timestamp='2013-05-01T20:02:30Z' uid='68904' user='Fredrik Christensson' version='8' changeset='15938546' lat='55.5900813' lon='12.7738031' />\n  <node id='244466744' timestamp='2015-05-11T21:38:59Z' uid='306096' user='joakimfors' version='7' changeset='31019812' lat='55.5920236' lon='12.7690034' />\n  <node id='244466762' timestamp='2015-05-11T21:38:59Z' uid='306096' user='joakimfors' version='11' changeset='31019812' lat='55.5921007' lon='12.7691046' />\n  <node id='244466763' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='6' changeset='15938546' lat='55.5901607' lon='12.7738877' />\n  <node id='244466764' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5886527' lon='12.7780526' />\n  <node id='244466765' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5875032' lon='12.7813115' />\n  <node id='244466766' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5843807' lon='12.7908448' />\n  <node id='244466767' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5815963' lon='12.8001582' />\n  <node id='244466768' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5815825' lon='12.8002092' />\n  <node id='244466772' timestamp='2015-09-11T22:02:39Z' uid='165061' user='mapper999' version='5' changeset='33971157' lat='55.5788863' lon='12.8106393' />\n  <node id='2288415943' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='1' changeset='15938546' lat='55.5909199' lon='12.7719051' />\n  <node id='2288415944' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='1' changeset='15938546' lat='55.5905035' lon='12.7729737' />\n  <node id='2288415946' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='1' changeset='15938546' lat='55.5914714' lon='12.7702711' />\n  <node id='2288415947' timestamp='2013-05-01T20:02:29Z' uid='68904' user='Fredrik Christensson' version='1' changeset='15938546' lat='55.5904262' lon='12.7728778' />\n  <node id='3089123457' timestamp='2015-09-11T22:02:40Z' uid='165061' user='mapper999' version='2' changeset='33971157' lat='55.5792796' lon='12.8090265' />\n  <node id='3089123458' timestamp='2015-09-11T22:02:40Z' uid='165061' user='mapper999' version='2' changeset='33971157' lat='55.5792106' lon='12.8088835' />\n  <node id='3516439688' timestamp='2015-09-11T22:02:41Z' uid='165061' user='mapper999' version='2' changeset='33971157' lat='55.5768698' lon='12.819016' />\n  <node id='3516439691' timestamp='2015-09-11T22:02:41Z' uid='165061' user='mapper999' version='2' changeset='33971157' lat='55.576958' lon='12.8190799' />\n  <node id='3739700862' timestamp='2015-09-11T22:00:32Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5768637' lon='12.8190422' />\n  <node id='3739700868' timestamp='2015-09-11T22:00:32Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.576952' lon='12.8191061' />\n  <node id='3739700877' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5773273' lon='12.8169407' />\n  <node id='3739700878' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5773385' lon='12.8168877' />\n  <node id='3739700883' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5774167' lon='12.8169997' />\n  <node id='3739700884' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5774278' lon='12.8169467' />\n  <node id='3739700893' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5778023' lon='12.8148236' />\n  <node id='3739700894' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5778144' lon='12.8147712' />\n  <node id='3739700899' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5778905' lon='12.8148875' />\n  <node id='3739700900' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5779027' lon='12.8148352' />\n  <node id='3739700909' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5782957' lon='12.812673' />\n  <node id='3739700910' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5783077' lon='12.8126206' />\n  <node id='3739700915' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5783841' lon='12.8127363' />\n  <node id='3739700916' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5783961' lon='12.8126839' />\n  <node id='3739700925' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5788102' lon='12.810523' />\n  <node id='3739700928' timestamp='2015-09-11T22:00:33Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5788984' lon='12.810587' />\n  <node id='3739700937' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5793114' lon='12.8084663' />\n  <node id='3739700938' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5793245' lon='12.8084147' />\n  <node id='3739700943' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5793984' lon='12.8085354' />\n  <node id='3739700944' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5794115' lon='12.8084838' />\n  <node id='3739700953' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5798358' lon='12.8063654' />\n  <node id='3739700954' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5798491' lon='12.806314' />\n  <node id='3739700959' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5799226' lon='12.8064355' />\n  <node id='3739700960' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5799358' lon='12.8063841' />\n  <node id='3739700969' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5803834' lon='12.8042547' />\n  <node id='3739700970' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5803965' lon='12.8042031' />\n  <node id='3739700975' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5804705' lon='12.8043237' />\n  <node id='3739700976' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5804835' lon='12.8042721' />\n  <node id='3739700985' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5809479' lon='12.8021324' />\n  <node id='3739700986' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5809615' lon='12.8020812' />\n  <node id='3739700991' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5810342' lon='12.8022042' />\n  <node id='3739700992' timestamp='2015-09-11T22:00:34Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5810478' lon='12.802153' />\n  <node id='3739701009' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5820408' lon='12.7982395' />\n  <node id='3739701010' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5820559' lon='12.7981896' />\n  <node id='3739701015' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.582125' lon='12.7983189' />\n  <node id='3739701016' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.58214' lon='12.798269' />\n  <node id='3739701125' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5825944' lon='12.7963513' />\n  <node id='3739701126' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5826093' lon='12.7963013' />\n  <node id='3739701131' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5826788' lon='12.79643' />\n  <node id='3739701132' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5826937' lon='12.7963799' />\n  <node id='3739701141' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5831563' lon='12.7944848' />\n  <node id='3739701142' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5831723' lon='12.7944358' />\n  <node id='3739701147' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5832389' lon='12.7945692' />\n  <node id='3739701148' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5832549' lon='12.7945202' />\n  <node id='3739701157' timestamp='2015-09-11T22:00:35Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5837129' lon='12.7926532' />\n  <node id='3739701158' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5837283' lon='12.7926036' />\n  <node id='3739701163' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5837966' lon='12.7927342' />\n  <node id='3739701164' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5838119' lon='12.7926846' />\n  <node id='3739701173' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5842821' lon='12.7908094' />\n  <node id='3739701176' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5843647' lon='12.7908938' />\n  <node id='3739701185' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.584899' lon='12.7888527' />\n  <node id='3739701186' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5849148' lon='12.7888035' />\n  <node id='3739701191' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5849821' lon='12.7889357' />\n  <node id='3739701192' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5849978' lon='12.7888864' />\n  <node id='3739701201' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5855018' lon='12.7869223' />\n  <node id='3739701202' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5855181' lon='12.7868735' />\n  <node id='3739701207' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.585584' lon='12.7870079' />\n  <node id='3739701208' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5856002' lon='12.7869592' />\n  <node id='3739701217' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5861449' lon='12.7849924' />\n  <node id='3739701218' timestamp='2015-09-11T22:00:36Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5861611' lon='12.7849437' />\n  <node id='3739701223' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5862272' lon='12.7850778' />\n  <node id='3739701224' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5862433' lon='12.785029' />\n  <node id='3739701233' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.586768' lon='12.7831184' />\n  <node id='3739701234' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.586785' lon='12.7830705' />\n  <node id='3739701239' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5868487' lon='12.7832082' />\n  <node id='3739701240' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5868657' lon='12.7831603' />\n  <node id='3739701249' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5874394' lon='12.7811738' />\n  <node id='3739701252' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5875202' lon='12.7812636' />\n  <node id='3739701261' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5879979' lon='12.77958' />\n  <node id='3739701262' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5880152' lon='12.7795324' />\n  <node id='3739701267' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5880781' lon='12.7796713' />\n  <node id='3739701268' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5880954' lon='12.7796237' />\n  <node id='3739701277' timestamp='2015-09-11T22:00:37Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5885906' lon='12.7779126' />\n  <node id='3739701280' timestamp='2015-09-11T22:00:38Z' uid='165061' user='mapper999' version='1' changeset='33971157' lat='55.5886703' lon='12.7780053' />\n  <way id='23283662' timestamp='2017-03-08T11:12:02Z' uid='339581' user='nyuriks' version='24' changeset='46677566'>\n    <nd ref='244466744' />\n    <nd ref='2288415946' />\n    <nd ref='118919' />\n    <nd ref='2288415947' />\n    <nd ref='244466743' />\n    <nd ref='3739701277' />\n    <nd ref='118920' />\n    <nd ref='3739701262' />\n    <nd ref='3739701261' />\n    <nd ref='3739701249' />\n    <nd ref='118921' />\n    <nd ref='3739701234' />\n    <nd ref='3739701233' />\n    <nd ref='3739701218' />\n    <nd ref='3739701217' />\n    <nd ref='3739701202' />\n    <nd ref='3739701201' />\n    <nd ref='3739701186' />\n    <nd ref='3739701185' />\n    <nd ref='118922' />\n    <nd ref='3739701173' />\n    <nd ref='3739701158' />\n    <nd ref='3739701157' />\n    <nd ref='3739701142' />\n    <nd ref='3739701141' />\n    <nd ref='3739701126' />\n    <nd ref='3739701125' />\n    <nd ref='3739701010' />\n    <nd ref='3739701009' />\n    <nd ref='118923' />\n    <nd ref='118924' />\n    <nd ref='3739700986' />\n    <nd ref='3739700985' />\n    <nd ref='3739700970' />\n    <nd ref='3739700969' />\n    <nd ref='3739700954' />\n    <nd ref='3739700953' />\n    <nd ref='3739700938' />\n    <nd ref='3739700937' />\n    <nd ref='3089123458' />\n    <tag k='bridge' v='yes' />\n    <tag k='bridge:structure' v='beam' />\n    <tag k='bridge:wikipedia' v='en:Øresund Bridge' />\n    <tag k='communication' v='line' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='E 20' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='signals' />\n    <tag k='name' v='Øresundsmotorvejen' />\n    <tag k='name:bridge' v='Øresundsbroen' />\n    <tag k='name:bridge:da' v='Øresundsbroen' />\n    <tag k='name:bridge:de' v='Öresundbrücke' />\n    <tag k='name:bridge:en' v='Øresund Bridge' />\n    <tag k='name:bridge:sv' v='Öresundsbron' />\n    <tag k='official_name:bridge' v='Øresundsbron' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='E 20' />\n    <tag k='shoulder' v='right' />\n    <tag k='source:maxspeed' v='sign' />\n    <tag k='start_date' v='2000-06' />\n    <tag k='surface' v='asphalt' />\n    <tag k='toll' v='yes' />\n  </way>\n  <way id='304383858' timestamp='2017-03-08T11:12:05Z' uid='339581' user='nyuriks' version='5' changeset='46677566'>\n    <nd ref='3089123457' />\n    <nd ref='3739700943' />\n    <nd ref='3739700944' />\n    <nd ref='3739700959' />\n    <nd ref='3739700960' />\n    <nd ref='3739700975' />\n    <nd ref='3739700976' />\n    <nd ref='3739700991' />\n    <nd ref='3739700992' />\n    <nd ref='244466768' />\n    <nd ref='244466767' />\n    <nd ref='3739701015' />\n    <nd ref='3739701016' />\n    <nd ref='3739701131' />\n    <nd ref='3739701132' />\n    <nd ref='3739701147' />\n    <nd ref='3739701148' />\n    <nd ref='3739701163' />\n    <nd ref='3739701164' />\n    <nd ref='3739701176' />\n    <nd ref='244466766' />\n    <nd ref='3739701191' />\n    <nd ref='3739701192' />\n    <nd ref='3739701207' />\n    <nd ref='3739701208' />\n    <nd ref='3739701223' />\n    <nd ref='3739701224' />\n    <nd ref='3739701239' />\n    <nd ref='3739701240' />\n    <nd ref='244466765' />\n    <nd ref='3739701252' />\n    <nd ref='3739701267' />\n    <nd ref='3739701268' />\n    <nd ref='244466764' />\n    <nd ref='3739701280' />\n    <nd ref='244466763' />\n    <nd ref='2288415944' />\n    <nd ref='2288415943' />\n    <nd ref='244466762' />\n    <tag k='bridge' v='yes' />\n    <tag k='bridge:structure' v='beam' />\n    <tag k='bridge:wikipedia' v='en:Øresund Bridge' />\n    <tag k='communication' v='line' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='E 20' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='110' />\n    <tag k='name' v='Øresundsmotorvejen' />\n    <tag k='name:bridge' v='Øresundsbroen' />\n    <tag k='name:bridge:da' v='Øresundsbroen' />\n    <tag k='name:bridge:de' v='Öresundbrücke' />\n    <tag k='name:bridge:en' v='Øresund Bridge' />\n    <tag k='name:bridge:sv' v='Öresundsbron' />\n    <tag k='official_name:bridge' v='Øresundsbron' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='E 20' />\n    <tag k='shoulder' v='right' />\n    <tag k='start_date' v='2000-06' />\n    <tag k='surface' v='asphalt' />\n    <tag k='toll' v='yes' />\n  </way>\n  <way id='304383859' timestamp='2017-03-08T11:12:05Z' uid='339581' user='nyuriks' version='6' changeset='46677566'>\n    <nd ref='3089123458' />\n    <nd ref='3739700925' />\n    <nd ref='125398' />\n    <nd ref='3739700910' />\n    <nd ref='3739700909' />\n    <nd ref='3739700894' />\n    <nd ref='3739700893' />\n    <nd ref='3739700878' />\n    <nd ref='3739700877' />\n    <nd ref='3516439688' />\n    <nd ref='3739700862' />\n    <tag k='bridge' v='yes' />\n    <tag k='bridge:structure' v='beam' />\n    <tag k='bridge:wikipedia' v='en:Øresund Bridge' />\n    <tag k='communication' v='line' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='E 20' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='signals' />\n    <tag k='name:bridge' v='Öresundsbron' />\n    <tag k='name:bridge:da' v='Øresundsbroen' />\n    <tag k='name:bridge:de' v='Öresundbrücke' />\n    <tag k='name:bridge:en' v='Øresund Bridge' />\n    <tag k='name:bridge:sv' v='Öresundsbron' />\n    <tag k='name:da' v='Øresundsmotorvejen' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='E 20' />\n    <tag k='shoulder' v='right' />\n    <tag k='source:maxspeed' v='sign' />\n    <tag k='start_date' v='2000-06' />\n    <tag k='surface' v='asphalt' />\n    <tag k='toll' v='yes' />\n  </way>\n  <way id='370255937' timestamp='2017-03-08T11:12:07Z' uid='339581' user='nyuriks' version='2' changeset='46677566'>\n    <nd ref='3739700868' />\n    <nd ref='3516439691' />\n    <nd ref='3739700883' />\n    <nd ref='3739700884' />\n    <nd ref='3739700899' />\n    <nd ref='3739700900' />\n    <nd ref='3739700915' />\n    <nd ref='3739700916' />\n    <nd ref='244466772' />\n    <nd ref='3739700928' />\n    <nd ref='3089123457' />\n    <tag k='bridge' v='yes' />\n    <tag k='bridge:structure' v='beam' />\n    <tag k='bridge:wikipedia' v='en:Øresund Bridge' />\n    <tag k='communication' v='line' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='E 20' />\n    <tag k='lanes' v='2' />\n    <tag k='layer' v='2' />\n    <tag k='lit' v='yes' />\n    <tag k='maxspeed' v='110' />\n    <tag k='name:bridge' v='Öresundsbron' />\n    <tag k='name:bridge:da' v='Øresundsbroen' />\n    <tag k='name:bridge:de' v='Öresundbrücke' />\n    <tag k='name:bridge:en' v='Øresund Bridge' />\n    <tag k='name:bridge:sv' v='Öresundsbron' />\n    <tag k='name:da' v='Øresundsmotorvejen' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='E 20' />\n    <tag k='shoulder' v='right' />\n    <tag k='start_date' v='2000-06' />\n    <tag k='surface' v='asphalt' />\n    <tag k='toll' v='yes' />\n  </way>\n  <relation id='2527989' timestamp='2018-05-22T07:13:00Z' uid='649780' user='Lytter1' version='90' changeset='59171438'>\n    <member type='way' ref='376078641' role='' />\n    <member type='way' ref='4818489' role='' />\n    <member type='way' ref='24863369' role='' />\n    <member type='way' ref='1212314' role='' />\n    <member type='way' ref='104375005' role='' />\n    <member type='way' ref='498651687' role='' />\n    <member type='way' ref='168424779' role='' />\n    <member type='way' ref='2953355' role='' />\n    <member type='way' ref='445296135' role='' />\n    <member type='way' ref='242700158' role='forward' />\n    <member type='way' ref='179942505' role='forward' />\n    <member type='way' ref='128770707' role='' />\n    <member type='way' ref='128770706' role='' />\n    <member type='way' ref='137307990' role='forward' />\n    <member type='way' ref='137307986' role='forward' />\n    <member type='way' ref='128768514' role='' />\n    <member type='way' ref='132454679' role='forward' />\n    <member type='way' ref='137726107' role='forward' />\n    <member type='way' ref='128828751' role='forward' />\n    <member type='way' ref='4825750' role='' />\n    <member type='way' ref='7382944' role='forward' />\n    <member type='way' ref='139170766' role='forward' />\n    <member type='way' ref='54938550' role='forward' />\n    <member type='way' ref='8133164' role='forward' />\n    <member type='way' ref='25612631' role='forward' />\n    <member type='way' ref='500769231' role='forward' />\n    <member type='way' ref='30427258' role='forward' />\n    <member type='way' ref='184108776' role='forward' />\n    <member type='way' ref='184108777' role='forward' />\n    <member type='way' ref='4048633' role='forward' />\n    <member type='way' ref='51752796' role='forward' />\n    <member type='way' ref='51752798' role='forward' />\n    <member type='way' ref='8133170' role='forward' />\n    <member type='way' ref='479360542' role='forward' />\n    <member type='way' ref='122301304' role='forward' />\n    <member type='way' ref='122301302' role='forward' />\n    <member type='way' ref='417962858' role='forward' />\n    <member type='way' ref='51752799' role='forward' />\n    <member type='way' ref='25612725' role='forward' />\n    <member type='way' ref='274704228' role='forward' />\n    <member type='way' ref='291364554' role='forward' />\n    <member type='way' ref='291364555' role='forward' />\n    <member type='way' ref='24163722' role='forward' />\n    <member type='way' ref='24163723' role='forward' />\n    <member type='way' ref='274704230' role='forward' />\n    <member type='way' ref='4586711' role='forward' />\n    <member type='way' ref='4586713' role='forward' />\n    <member type='way' ref='13237921' role='forward' />\n    <member type='way' ref='369727414' role='forward' />\n    <member type='way' ref='590045020' role='forward' />\n    <member type='way' ref='4566412' role='forward' />\n    <member type='way' ref='4587474' role='forward' />\n    <member type='way' ref='361190491' role='forward' />\n    <member type='way' ref='361190492' role='forward' />\n    <member type='way' ref='116040324' role='forward' />\n    <member type='way' ref='334033771' role='forward' />\n    <member type='way' ref='369719169' role='forward' />\n    <member type='way' ref='13243176' role='forward' />\n    <member type='way' ref='42949538' role='forward' />\n    <member type='way' ref='334028997' role='forward' />\n    <member type='way' ref='334028998' role='forward' />\n    <member type='way' ref='308311666' role='forward' />\n    <member type='way' ref='334031713' role='forward' />\n    <member type='way' ref='334031718' role='forward' />\n    <member type='way' ref='368973701' role='forward' />\n    <member type='way' ref='368973704' role='forward' />\n    <member type='way' ref='13243197' role='forward' />\n    <member type='way' ref='24459701' role='forward' />\n    <member type='way' ref='368974736' role='forward' />\n    <member type='way' ref='370182172' role='forward' />\n    <member type='way' ref='368974738' role='forward' />\n    <member type='way' ref='370182860' role='forward' />\n    <member type='way' ref='370182861' role='forward' />\n    <member type='way' ref='370184496' role='forward' />\n    <member type='way' ref='370184497' role='forward' />\n    <member type='way' ref='414187449' role='forward' />\n    <member type='way' ref='7997077' role='forward' />\n    <member type='way' ref='8043373' role='forward' />\n    <member type='way' ref='9479546' role='forward' />\n    <member type='way' ref='9479547' role='forward' />\n    <member type='way' ref='414187439' role='forward' />\n    <member type='way' ref='37169725' role='forward' />\n    <member type='way' ref='314939367' role='forward' />\n    <member type='way' ref='103459023' role='forward' />\n    <member type='way' ref='103459014' role='forward' />\n    <member type='way' ref='8023900' role='forward' />\n    <member type='way' ref='4587430' role='forward' />\n    <member type='way' ref='4360600' role='forward' />\n    <member type='way' ref='4360601' role='forward' />\n    <member type='way' ref='4360602' role='forward' />\n    <member type='way' ref='4360603' role='forward' />\n    <member type='way' ref='8242842' role='forward' />\n    <member type='way' ref='458678237' role='forward' />\n    <member type='way' ref='458678241' role='forward' />\n    <member type='way' ref='28172234' role='forward' />\n    <member type='way' ref='24455311' role='forward' />\n    <member type='way' ref='120873680' role='forward' />\n    <member type='way' ref='24455310' role='forward' />\n    <member type='way' ref='210247619' role='forward' />\n    <member type='way' ref='169849344' role='forward' />\n    <member type='way' ref='169849345' role='forward' />\n    <member type='way' ref='107689033' role='forward' />\n    <member type='way' ref='157725815' role='forward' />\n    <member type='way' ref='221503636' role='forward' />\n    <member type='way' ref='221503632' role='forward' />\n    <member type='way' ref='24417571' role='forward' />\n    <member type='way' ref='24417572' role='forward' />\n    <member type='way' ref='134384298' role='forward' />\n    <member type='way' ref='134384296' role='forward' />\n    <member type='way' ref='225472290' role='forward' />\n    <member type='way' ref='26758966' role='forward' />\n    <member type='way' ref='366095663' role='forward' />\n    <member type='way' ref='120081163' role='forward' />\n    <member type='way' ref='120081165' role='forward' />\n    <member type='way' ref='421008065' role='forward' />\n    <member type='way' ref='421008067' role='forward' />\n    <member type='way' ref='24418157' role='forward' />\n    <member type='way' ref='24418158' role='forward' />\n    <member type='way' ref='547986509' role='forward' />\n    <member type='way' ref='174681249' role='forward' />\n    <member type='way' ref='117749881' role='forward' />\n    <member type='way' ref='421008062' role='forward' />\n    <member type='way' ref='117749816' role='forward' />\n    <member type='way' ref='117749852' role='forward' />\n    <member type='way' ref='26213722' role='forward' />\n    <member type='way' ref='26213727' role='forward' />\n    <member type='way' ref='10653126' role='forward' />\n    <member type='way' ref='10653127' role='forward' />\n    <member type='way' ref='158408255' role='forward' />\n    <member type='way' ref='158408257' role='forward' />\n    <member type='way' ref='421008063' role='forward' />\n    <member type='way' ref='163967203' role='forward' />\n    <member type='way' ref='2041265' role='forward' />\n    <member type='way' ref='219657806' role='forward' />\n    <member type='way' ref='219657811' role='forward' />\n    <member type='way' ref='23000641' role='forward' />\n    <member type='way' ref='98479074' role='forward' />\n    <member type='way' ref='98479020' role='forward' />\n    <member type='way' ref='23000640' role='forward' />\n    <member type='way' ref='29057944' role='forward' />\n    <member type='way' ref='386834390' role='forward' />\n    <member type='way' ref='110361079' role='forward' />\n    <member type='way' ref='357265308' role='forward' />\n    <member type='way' ref='357265307' role='forward' />\n    <member type='way' ref='140293283' role='forward' />\n    <member type='way' ref='357265304' role='forward' />\n    <member type='way' ref='30791075' role='forward' />\n    <member type='way' ref='119719505' role='forward' />\n    <member type='way' ref='30791222' role='forward' />\n    <member type='way' ref='386834391' role='forward' />\n    <member type='way' ref='219657896' role='forward' />\n    <member type='way' ref='107382804' role='forward' />\n    <member type='way' ref='357266386' role='forward' />\n    <member type='way' ref='357266387' role='forward' />\n    <member type='way' ref='386834393' role='forward' />\n    <member type='way' ref='219657899' role='forward' />\n    <member type='way' ref='370256037' role='forward' />\n    <member type='way' ref='22771793' role='forward' />\n    <member type='way' ref='22771518' role='forward' />\n    <member type='way' ref='370256030' role='forward' />\n    <member type='way' ref='23283662' role='forward' />\n    <member type='way' ref='30826997' role='forward' />\n    <member type='way' ref='25612637' role='forward' />\n    <member type='way' ref='303576289' role='forward' />\n    <member type='way' ref='500769236' role='forward' />\n    <member type='way' ref='51752793' role='forward' />\n    <member type='way' ref='484621252' role='forward' />\n    <member type='way' ref='184108774' role='forward' />\n    <member type='way' ref='184108775' role='forward' />\n    <member type='way' ref='4048475' role='forward' />\n    <member type='way' ref='51752795' role='forward' />\n    <member type='way' ref='28173859' role='forward' />\n    <member type='way' ref='4048632' role='forward' />\n    <member type='way' ref='417962853' role='forward' />\n    <member type='way' ref='291364553' role='forward' />\n    <member type='way' ref='286336652' role='forward' />\n    <member type='way' ref='286336653' role='forward' />\n    <member type='way' ref='4586925' role='forward' />\n    <member type='way' ref='24163724' role='forward' />\n    <member type='way' ref='26713014' role='forward' />\n    <member type='way' ref='4586931' role='forward' />\n    <member type='way' ref='28173456' role='forward' />\n    <member type='way' ref='28173444' role='forward' />\n    <member type='way' ref='4586712' role='forward' />\n    <member type='way' ref='369727413' role='forward' />\n    <member type='way' ref='2018400' role='forward' />\n    <member type='way' ref='2018724' role='forward' />\n    <member type='way' ref='361190701' role='forward' />\n    <member type='way' ref='361190702' role='forward' />\n    <member type='way' ref='299648362' role='forward' />\n    <member type='way' ref='334034697' role='forward' />\n    <member type='way' ref='334033957' role='forward' />\n    <member type='way' ref='334033956' role='forward' />\n    <member type='way' ref='334027688' role='forward' />\n    <member type='way' ref='116040317' role='forward' />\n    <member type='way' ref='13243181' role='forward' />\n    <member type='way' ref='368973703' role='forward' />\n    <member type='way' ref='368973702' role='forward' />\n    <member type='way' ref='368796350' role='forward' />\n    <member type='way' ref='368796349' role='forward' />\n    <member type='way' ref='42949541' role='forward' />\n    <member type='way' ref='13243193' role='forward' />\n    <member type='way' ref='368969987' role='forward' />\n    <member type='way' ref='368974743' role='forward' />\n    <member type='way' ref='368974733' role='forward' />\n    <member type='way' ref='368974740' role='forward' />\n    <member type='way' ref='368974730' role='forward' />\n    <member type='way' ref='414187434' role='forward' />\n    <member type='way' ref='4587467' role='forward' />\n    <member type='way' ref='7997076' role='forward' />\n    <member type='way' ref='8043375' role='forward' />\n    <member type='way' ref='9479549' role='forward' />\n    <member type='way' ref='414187457' role='forward' />\n    <member type='way' ref='37169736' role='forward' />\n    <member type='way' ref='495538607' role='forward' />\n    <member type='way' ref='495538608' role='forward' />\n    <member type='way' ref='37169713' role='forward' />\n    <member type='way' ref='314939368' role='forward' />\n    <member type='way' ref='103459022' role='forward' />\n    <member type='way' ref='103459027' role='forward' />\n    <member type='way' ref='4587463' role='forward' />\n    <member type='way' ref='8023902' role='forward' />\n    <member type='way' ref='4587429' role='forward' />\n    <member type='way' ref='2032610' role='forward' />\n    <member type='way' ref='4360592' role='forward' />\n    <member type='way' ref='4360591' role='forward' />\n    <member type='way' ref='4360590' role='forward' />\n    <member type='way' ref='458678238' role='forward' />\n    <member type='way' ref='458678239' role='forward' />\n    <member type='way' ref='4360596' role='forward' />\n    <member type='way' ref='495531791' role='forward' />\n    <member type='way' ref='299656111' role='forward' />\n    <member type='way' ref='24455313' role='forward' />\n    <member type='way' ref='24455312' role='forward' />\n    <member type='way' ref='209693190' role='forward' />\n    <member type='way' ref='209693189' role='forward' />\n    <member type='way' ref='170129854' role='forward' />\n    <member type='way' ref='170129851' role='forward' />\n    <member type='way' ref='120860455' role='forward' />\n    <member type='way' ref='120860456' role='forward' />\n    <member type='way' ref='209693188' role='forward' />\n    <member type='way' ref='37169712' role='forward' />\n    <member type='way' ref='221503634' role='forward' />\n    <member type='way' ref='157725807' role='forward' />\n    <member type='way' ref='24417569' role='forward' />\n    <member type='way' ref='134384294' role='forward' />\n    <member type='way' ref='134384292' role='forward' />\n    <member type='way' ref='37169704' role='forward' />\n    <member type='way' ref='475124867' role='forward' />\n    <member type='way' ref='225472291' role='forward' />\n    <member type='way' ref='26758987' role='forward' />\n    <member type='way' ref='24418154' role='forward' />\n    <member type='way' ref='24418152' role='forward' />\n    <member type='way' ref='24418161' role='forward' />\n    <member type='way' ref='421008064' role='forward' />\n    <member type='way' ref='421008066' role='forward' />\n    <member type='way' ref='24418160' role='forward' />\n    <member type='way' ref='174681248' role='forward' />\n    <member type='way' ref='417614542' role='forward' />\n    <member type='way' ref='180308200' role='forward' />\n    <member type='way' ref='26213730' role='forward' />\n    <member type='way' ref='10653129' role='forward' />\n    <member type='way' ref='10653128' role='forward' />\n    <member type='way' ref='158408256' role='forward' />\n    <member type='way' ref='158408254' role='forward' />\n    <member type='way' ref='5055938' role='forward' />\n    <member type='way' ref='271780205' role='forward' />\n    <member type='way' ref='23000642' role='forward' />\n    <member type='way' ref='98479068' role='forward' />\n    <member type='way' ref='98479044' role='forward' />\n    <member type='way' ref='23000643' role='forward' />\n    <member type='way' ref='25614079' role='forward' />\n    <member type='way' ref='27224363' role='forward' />\n    <member type='way' ref='386834388' role='forward' />\n    <member type='way' ref='357265310' role='forward' />\n    <member type='way' ref='357265309' role='forward' />\n    <member type='way' ref='357265305' role='forward' />\n    <member type='way' ref='140293280' role='forward' />\n    <member type='way' ref='30791016' role='forward' />\n    <member type='way' ref='119719508' role='forward' />\n    <member type='way' ref='30791228' role='forward' />\n    <member type='way' ref='386834392' role='forward' />\n    <member type='way' ref='357266388' role='forward' />\n    <member type='way' ref='357266389' role='forward' />\n    <member type='way' ref='107382822' role='forward' />\n    <member type='way' ref='386801138' role='forward' />\n    <member type='way' ref='370256035' role='forward' />\n    <member type='way' ref='4517829' role='forward' />\n    <member type='way' ref='22771792' role='forward' />\n    <member type='way' ref='370256031' role='forward' />\n    <member type='way' ref='23283660' role='forward' />\n    <member type='way' ref='304383858' role='forward' />\n    <tag k='name' v='E 20 Denmark' />\n    <tag k='network' v='e-road' />\n    <tag k='ref' v='E 20' />\n    <tag k='route' v='road' />\n    <tag k='section' v='Denmark' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='5794315' timestamp='2018-08-18T21:14:24Z' uid='35601' user='magol' version='87' changeset='61780464'>\n    <member type='node' ref='1938160930' role='platform' />\n    <member type='node' ref='1310612418' role='platform' />\n    <member type='node' ref='1998995667' role='stop' />\n    <member type='node' ref='221169938' role='stop' />\n    <member type='node' ref='3418254623' role='stop' />\n    <member type='node' ref='4128416085' role='stop' />\n    <member type='node' ref='416908979' role='stop' />\n    <member type='node' ref='394660560' role='stop' />\n    <member type='way' ref='189255217' role='platform' />\n    <member type='way' ref='166526295' role='platform' />\n    <member type='way' ref='334753833' role='platform' />\n    <member type='way' ref='411108616' role='platform' />\n    <member type='node' ref='5313132106' role='stop' />\n    <member type='way' ref='391105368' role='platform' />\n    <member type='way' ref='435653432' role='' />\n    <member type='way' ref='515410740' role='' />\n    <member type='way' ref='38702782' role='' />\n    <member type='way' ref='38702777' role='' />\n    <member type='way' ref='38702744' role='' />\n    <member type='way' ref='85735262' role='' />\n    <member type='way' ref='168269172' role='' />\n    <member type='way' ref='74865047' role='' />\n    <member type='way' ref='411057613' role='' />\n    <member type='way' ref='85735265' role='' />\n    <member type='way' ref='495915484' role='' />\n    <member type='way' ref='298767466' role='' />\n    <member type='way' ref='2371124' role='' />\n    <member type='way' ref='295221437' role='' />\n    <member type='way' ref='495915487' role='' />\n    <member type='way' ref='495915485' role='' />\n    <member type='way' ref='495915489' role='' />\n    <member type='way' ref='27225997' role='' />\n    <member type='way' ref='2371125' role='' />\n    <member type='way' ref='416688425' role='' />\n    <member type='way' ref='364793653' role='' />\n    <member type='way' ref='416688419' role='' />\n    <member type='way' ref='422789570' role='' />\n    <member type='way' ref='1880588' role='' />\n    <member type='way' ref='161633350' role='' />\n    <member type='way' ref='422789568' role='' />\n    <member type='way' ref='422789556' role='' />\n    <member type='way' ref='422789562' role='' />\n    <member type='way' ref='422789555' role='' />\n    <member type='way' ref='422789566' role='' />\n    <member type='way' ref='422789567' role='' />\n    <member type='way' ref='422789564' role='' />\n    <member type='way' ref='422789559' role='' />\n    <member type='way' ref='422789544' role='' />\n    <member type='way' ref='1880585' role='' />\n    <member type='way' ref='422789542' role='' />\n    <member type='way' ref='237108788' role='' />\n    <member type='way' ref='108848990' role='' />\n    <member type='way' ref='108849099' role='' />\n    <member type='way' ref='422789540' role='' />\n    <member type='way' ref='108849074' role='' />\n    <member type='way' ref='422789572' role='' />\n    <member type='way' ref='108849037' role='' />\n    <member type='way' ref='386834370' role='' />\n    <member type='way' ref='2040973' role='' />\n    <member type='way' ref='24418176' role='' />\n    <member type='way' ref='24418177' role='' />\n    <member type='way' ref='24418174' role='' />\n    <member type='way' ref='24418175' role='' />\n    <member type='way' ref='257636641' role='' />\n    <member type='way' ref='110361079' role='' />\n    <member type='way' ref='357265308' role='' />\n    <member type='way' ref='357265307' role='' />\n    <member type='way' ref='140293283' role='' />\n    <member type='way' ref='357265304' role='' />\n    <member type='way' ref='30791075' role='' />\n    <member type='way' ref='119719505' role='' />\n    <member type='way' ref='30791222' role='' />\n    <member type='way' ref='386834391' role='' />\n    <member type='way' ref='375993929' role='' />\n    <member type='way' ref='375993994' role='' />\n    <member type='way' ref='121198957' role='' />\n    <member type='way' ref='388070394' role='' />\n    <member type='way' ref='386834382' role='' />\n    <member type='way' ref='557874837' role='' />\n    <member type='way' ref='1880523' role='' />\n    <member type='way' ref='4373482' role='' />\n    <member type='way' ref='488272306' role='' />\n    <member type='way' ref='233833233' role='' />\n    <member type='way' ref='488272308' role='' />\n    <member type='way' ref='488272310' role='' />\n    <member type='way' ref='25604858' role='' />\n    <member type='way' ref='4373483' role='' />\n    <member type='way' ref='488272315' role='' />\n    <member type='way' ref='488272320' role='' />\n    <member type='way' ref='296891697' role='' />\n    <member type='way' ref='488272326' role='' />\n    <member type='way' ref='488272325' role='' />\n    <member type='way' ref='4373480' role='' />\n    <member type='way' ref='497022315' role='' />\n    <member type='way' ref='38069237' role='' />\n    <member type='way' ref='92162861' role='' />\n    <member type='way' ref='38069236' role='' />\n    <member type='way' ref='107382801' role='' />\n    <member type='way' ref='219657899' role='' />\n    <member type='way' ref='370256037' role='' />\n    <member type='way' ref='22771793' role='' />\n    <member type='way' ref='22771518' role='' />\n    <member type='way' ref='370256030' role='' />\n    <member type='way' ref='23283662' role='' />\n    <member type='way' ref='304383859' role='' />\n    <member type='way' ref='370255938' role='' />\n    <member type='way' ref='370255940' role='' />\n    <member type='way' ref='148500436' role='' />\n    <member type='way' ref='219657906' role='' />\n    <member type='way' ref='148899471' role='' />\n    <member type='way' ref='148899453' role='' />\n    <member type='way' ref='219657860' role='' />\n    <member type='way' ref='148899506' role='' />\n    <member type='way' ref='219657822' role='' />\n    <member type='way' ref='28289483' role='' />\n    <member type='way' ref='28289485' role='' />\n    <member type='way' ref='25046566' role='' />\n    <member type='way' ref='25046567' role='' />\n    <member type='way' ref='119253153' role='' />\n    <member type='way' ref='119253150' role='' />\n    <member type='way' ref='148504529' role='' />\n    <member type='way' ref='148504691' role='' />\n    <member type='way' ref='148504690' role='' />\n    <member type='way' ref='460408774' role='' />\n    <member type='way' ref='15729150' role='' />\n    <member type='way' ref='4518071' role='' />\n    <member type='way' ref='15729146' role='' />\n    <member type='way' ref='149178397' role='' />\n    <member type='way' ref='149178399' role='' />\n    <member type='way' ref='23423532' role='' />\n    <member type='way' ref='23423535' role='' />\n    <member type='way' ref='23423536' role='' />\n    <member type='way' ref='32090567' role='' />\n    <member type='way' ref='23423527' role='' />\n    <member type='way' ref='388070341' role='' />\n    <member type='way' ref='28274464' role='' />\n    <member type='way' ref='154229121' role='' />\n    <member type='way' ref='231390456' role='' />\n    <member type='way' ref='388070255' role='' />\n    <member type='way' ref='71933892' role='' />\n    <member type='way' ref='71933999' role='' />\n    <member type='way' ref='200184219' role='' />\n    <member type='way' ref='388070368' role='' />\n    <member type='way' ref='23423400' role='' />\n    <member type='way' ref='28552686' role='' />\n    <member type='way' ref='28552687' role='' />\n    <member type='way' ref='52008666' role='' />\n    <member type='way' ref='28552872' role='' />\n    <member type='way' ref='453128278' role='' />\n    <member type='way' ref='388070410' role='' />\n    <member type='way' ref='28562024' role='' />\n    <member type='way' ref='585774520' role='' />\n    <member type='way' ref='22763845' role='' />\n    <member type='way' ref='585774519' role='' />\n    <member type='way' ref='136913725' role='' />\n    <member type='way' ref='136913721' role='' />\n    <member type='way' ref='139667343' role='' />\n    <member type='way' ref='608281812' role='' />\n    <member type='way' ref='24161692' role='' />\n    <member type='way' ref='28302725' role='' />\n    <member type='way' ref='28302737' role='' />\n    <member type='way' ref='139669589' role='' />\n    <member type='way' ref='189255272' role='' />\n    <member type='way' ref='4640795' role='' />\n    <member type='way' ref='24214517' role='' />\n    <member type='way' ref='189255264' role='' />\n    <member type='way' ref='28338448' role='' />\n    <member type='way' ref='139679569' role='' />\n    <member type='way' ref='139659116' role='' />\n    <member type='way' ref='39460624' role='' />\n    <member type='way' ref='267133182' role='' />\n    <member type='way' ref='249170713' role='' />\n    <member type='way' ref='267133185' role='' />\n    <member type='way' ref='244457557' role='' />\n    <member type='way' ref='267133187' role='' />\n    <member type='way' ref='186634110' role='' />\n    <member type='way' ref='4642565' role='' />\n    <member type='way' ref='166514567' role='' />\n    <member type='way' ref='139667341' role='' />\n    <member type='way' ref='28290415' role='' />\n    <member type='way' ref='28290416' role='' />\n    <member type='way' ref='203023143' role='' />\n    <member type='way' ref='139679569' role='' />\n    <member type='way' ref='139659116' role='' />\n    <member type='way' ref='139659168' role='' />\n    <member type='way' ref='27647130' role='' />\n    <member type='way' ref='184362450' role='' />\n    <member type='way' ref='421889250' role='' />\n    <member type='way' ref='148521204' role='' />\n    <member type='way' ref='386655217' role='' />\n    <member type='way' ref='148521201' role='' />\n    <member type='way' ref='136775917' role='' />\n    <member type='way' ref='136775919' role='' />\n    <member type='way' ref='267133193' role='' />\n    <member type='way' ref='136773614' role='' />\n    <member type='way' ref='160904778' role='' />\n    <member type='way' ref='160904780' role='' />\n    <member type='way' ref='160904785' role='' />\n    <member type='way' ref='136773630' role='' />\n    <member type='way' ref='389936829' role='' />\n    <member type='way' ref='136531746' role='' />\n    <member type='way' ref='4453820' role='' />\n    <member type='way' ref='299536952' role='' />\n    <member type='way' ref='364258995' role='' />\n    <member type='way' ref='27742622' role='' />\n    <member type='way' ref='23401300' role='' />\n    <member type='way' ref='268727924' role='' />\n    <member type='way' ref='301890310' role='' />\n    <member type='way' ref='4453848' role='' />\n    <member type='way' ref='25031742' role='' />\n    <member type='way' ref='25031743' role='' />\n    <member type='way' ref='301850669' role='' />\n    <member type='way' ref='284982062' role='' />\n    <member type='way' ref='28113365' role='' />\n    <member type='way' ref='28113364' role='' />\n    <member type='way' ref='4453889' role='' />\n    <member type='way' ref='28846609' role='' />\n    <member type='way' ref='27405304' role='' />\n    <member type='way' ref='136754001' role='' />\n    <member type='way' ref='136754000' role='' />\n    <member type='way' ref='4453934' role='' />\n    <member type='way' ref='52290587' role='' />\n    <member type='way' ref='242886410' role='' />\n    <member type='way' ref='25707299' role='' />\n    <member type='way' ref='4454227' role='' />\n    <member type='way' ref='51874399' role='' />\n    <member type='way' ref='45874984' role='' />\n    <member type='way' ref='45875120' role='' />\n    <member type='way' ref='45875006' role='' />\n    <member type='way' ref='173657136' role='' />\n    <member type='way' ref='173657135' role='' />\n    <member type='way' ref='52087179' role='' />\n    <member type='way' ref='29986641' role='' />\n    <member type='way' ref='37878911' role='' />\n    <member type='way' ref='37878913' role='' />\n    <member type='way' ref='4454239' role='' />\n    <member type='way' ref='23367235' role='' />\n    <member type='way' ref='384729224' role='' />\n    <member type='way' ref='51874402' role='' />\n    <member type='way' ref='202953147' role='' />\n    <member type='way' ref='244503056' role='' />\n    <member type='way' ref='157425720' role='' />\n    <member type='way' ref='202953146' role='' />\n    <member type='way' ref='87990595' role='' />\n    <member type='way' ref='85132102' role='' />\n    <member type='way' ref='202953145' role='' />\n    <member type='way' ref='23294052' role='' />\n    <member type='way' ref='387809392' role='' />\n    <member type='way' ref='467758794' role='' />\n    <member type='way' ref='467758793' role='' />\n    <member type='way' ref='20691438' role='' />\n    <member type='way' ref='88308018' role='' />\n    <member type='way' ref='388070319' role='' />\n    <member type='way' ref='88308014' role='' />\n    <member type='way' ref='185123359' role='' />\n    <member type='way' ref='220784310' role='' />\n    <member type='way' ref='185123358' role='' />\n    <member type='way' ref='29983679' role='' />\n    <member type='way' ref='90962729' role='' />\n    <member type='way' ref='532521847' role='' />\n    <member type='way' ref='147007342' role='' />\n    <member type='way' ref='23392587' role='' />\n    <member type='way' ref='232389294' role='' />\n    <member type='way' ref='95927588' role='' />\n    <member type='way' ref='232389292' role='' />\n    <member type='way' ref='232389291' role='' />\n    <member type='way' ref='45211087' role='' />\n    <member type='way' ref='4454210' role='' />\n    <member type='way' ref='60320562' role='' />\n    <member type='way' ref='386655222' role='' />\n    <member type='way' ref='23193748' role='' />\n    <member type='way' ref='39077151' role='' />\n    <member type='way' ref='20602750' role='' />\n    <member type='way' ref='220784321' role='' />\n    <member type='way' ref='247131759' role='' />\n    <member type='way' ref='95961399' role='' />\n    <member type='way' ref='95961398' role='' />\n    <member type='way' ref='272386928' role='' />\n    <member type='way' ref='29979411' role='' />\n    <member type='way' ref='187916322' role='' />\n    <member type='way' ref='187916321' role='' />\n    <member type='way' ref='23193748' role='' />\n    <member type='way' ref='386655222' role='' />\n    <member type='way' ref='60320562' role='' />\n    <member type='way' ref='18936035' role='' />\n    <member type='way' ref='30099570' role='' />\n    <member type='way' ref='23237790' role='' />\n    <member type='way' ref='146874387' role='' />\n    <member type='way' ref='14035891' role='' />\n    <member type='way' ref='45267407' role='' />\n    <member type='way' ref='146874522' role='' />\n    <member type='way' ref='146874515' role='' />\n    <member type='way' ref='388070423' role='' />\n    <member type='way' ref='515410697' role='' />\n    <member type='way' ref='146874517' role='' />\n    <member type='way' ref='515410698' role='' />\n    <member type='way' ref='96034253' role='' />\n    <member type='way' ref='88308013' role='' />\n    <member type='way' ref='88308022' role='' />\n    <member type='way' ref='515410739' role='' />\n    <member type='way' ref='515410738' role='' />\n    <member type='way' ref='515410737' role='' />\n    <member type='way' ref='30117361' role='' />\n    <member type='way' ref='361480628' role='' />\n    <member type='way' ref='88284539' role='' />\n    <member type='way' ref='88284549' role='' />\n    <member type='way' ref='23330554' role='' />\n    <member type='way' ref='23330555' role='' />\n    <member type='way' ref='109830394' role='' />\n    <member type='way' ref='182725109' role='' />\n    <member type='way' ref='182725119' role='' />\n    <member type='way' ref='40031815' role='' />\n    <member type='way' ref='24511922' role='' />\n    <member type='way' ref='244502594' role='' />\n    <member type='way' ref='27777184' role='' />\n    <member type='way' ref='27777185' role='' />\n    <member type='way' ref='244502596' role='' />\n    <member type='way' ref='27523733' role='' />\n    <member type='way' ref='24524471' role='' />\n    <member type='way' ref='37502548' role='' />\n    <member type='way' ref='37502549' role='' />\n    <member type='way' ref='23842882' role='' />\n    <member type='way' ref='20581098' role='' />\n    <member type='way' ref='227763719' role='' />\n    <member type='way' ref='227763718' role='' />\n    <member type='way' ref='227763720' role='' />\n    <member type='way' ref='432736170' role='' />\n    <member type='way' ref='8136880' role='' />\n    <member type='way' ref='241985604' role='' />\n    <member type='way' ref='43444062' role='' />\n    <member type='way' ref='241985603' role='' />\n    <member type='way' ref='37995158' role='' />\n    <member type='way' ref='37995159' role='' />\n    <member type='way' ref='23947678' role='' />\n    <member type='way' ref='388070286' role='' />\n    <member type='way' ref='30951356' role='' />\n    <member type='way' ref='362348447' role='' />\n    <member type='way' ref='38554684' role='' />\n    <member type='way' ref='38554685' role='' />\n    <member type='way' ref='38554705' role='' />\n    <member type='way' ref='38554706' role='' />\n    <member type='way' ref='362348448' role='' />\n    <member type='way' ref='25731764' role='' />\n    <member type='way' ref='25731774' role='' />\n    <member type='way' ref='25731775' role='' />\n    <member type='way' ref='229026963' role='' />\n    <member type='way' ref='31056243' role='' />\n    <member type='way' ref='31056245' role='' />\n    <member type='way' ref='55849841' role='' />\n    <member type='way' ref='86960803' role='' />\n    <member type='way' ref='56082365' role='' />\n    <member type='way' ref='86960791' role='' />\n    <member type='way' ref='457461057' role='' />\n    <member type='way' ref='86960810' role='' />\n    <member type='way' ref='432736165' role='' />\n    <member type='way' ref='23948280' role='' />\n    <member type='way' ref='432736167' role='' />\n    <member type='way' ref='8281101' role='' />\n    <member type='way' ref='8397742' role='' />\n    <member type='way' ref='228439393' role='' />\n    <member type='way' ref='246384285' role='' />\n    <member type='way' ref='31051287' role='' />\n    <member type='way' ref='31051288' role='' />\n    <member type='way' ref='56082371' role='' />\n    <member type='way' ref='71408057' role='' />\n    <member type='way' ref='52333298' role='' />\n    <member type='way' ref='23948286' role='' />\n    <member type='way' ref='25303089' role='' />\n    <member type='way' ref='59102610' role='' />\n    <member type='way' ref='388313098' role='' />\n    <member type='way' ref='410957046' role='' />\n    <member type='way' ref='59102614' role='' />\n    <member type='way' ref='386817387' role='' />\n    <member type='way' ref='397437373' role='' />\n    <member type='way' ref='59102613' role='' />\n    <member type='way' ref='356858730' role='' />\n    <member type='way' ref='356858719' role='' />\n    <member type='way' ref='356858731' role='' />\n    <member type='way' ref='356858717' role='' />\n    <member type='way' ref='334753858' role='' />\n    <member type='way' ref='410957042' role='' />\n    <member type='way' ref='410957045' role='' />\n    <member type='way' ref='334753878' role='' />\n    <member type='way' ref='334753882' role='' />\n    <member type='way' ref='386817387' role='' />\n    <member type='way' ref='397437373' role='' />\n    <member type='way' ref='388070400' role='' />\n    <member type='way' ref='59102606' role='' />\n    <member type='way' ref='25303089' role='' />\n    <member type='way' ref='50939620' role='' />\n    <member type='way' ref='356858715' role='' />\n    <member type='way' ref='47379980' role='' />\n    <member type='way' ref='228796382' role='' />\n    <member type='way' ref='88573402' role='' />\n    <member type='way' ref='145991346' role='' />\n    <member type='way' ref='262098088' role='' />\n    <member type='way' ref='262098086' role='' />\n    <member type='way' ref='25303091' role='' />\n    <member type='way' ref='290731086' role='' />\n    <member type='way' ref='262098090' role='' />\n    <member type='way' ref='89353017' role='' />\n    <member type='way' ref='316216449' role='' />\n    <member type='way' ref='228796371' role='' />\n    <member type='way' ref='228796380' role='' />\n    <member type='way' ref='30104747' role='' />\n    <member type='way' ref='245877617' role='' />\n    <member type='way' ref='71954439' role='' />\n    <member type='way' ref='47360347' role='' />\n    <member type='way' ref='8144077' role='' />\n    <member type='way' ref='59195863' role='' />\n    <member type='way' ref='66245183' role='' />\n    <member type='way' ref='59195862' role='' />\n    <member type='way' ref='26291391' role='' />\n    <member type='way' ref='26291400' role='' />\n    <member type='way' ref='27090597' role='' />\n    <member type='way' ref='28165008' role='' />\n    <member type='way' ref='25724534' role='' />\n    <member type='way' ref='278784725' role='' />\n    <member type='way' ref='278784728' role='' />\n    <member type='way' ref='278784726' role='' />\n    <member type='way' ref='278784731' role='' />\n    <member type='way' ref='329830321' role='' />\n    <member type='way' ref='329830320' role='' />\n    <member type='way' ref='24496386' role='' />\n    <member type='way' ref='24496387' role='' />\n    <member type='way' ref='61411040' role='' />\n    <member type='way' ref='38443331' role='' />\n    <member type='way' ref='38443341' role='' />\n    <member type='way' ref='24935251' role='' />\n    <member type='way' ref='24935252' role='' />\n    <member type='way' ref='24935205' role='' />\n    <member type='way' ref='572541273' role='' />\n    <member type='way' ref='572541272' role='' />\n    <member type='way' ref='24935206' role='' />\n    <member type='way' ref='182731877' role='' />\n    <member type='way' ref='24741614' role='' />\n    <member type='way' ref='24741615' role='' />\n    <member type='way' ref='24544548' role='' />\n    <member type='way' ref='24544549' role='' />\n    <member type='way' ref='24928238' role='' />\n    <member type='way' ref='24928239' role='' />\n    <member type='way' ref='224447171' role='' />\n    <member type='way' ref='224447172' role='' />\n    <member type='way' ref='80234454' role='' />\n    <member type='way' ref='80234415' role='' />\n    <member type='way' ref='191646744' role='' />\n    <member type='way' ref='191646742' role='' />\n    <member type='way' ref='23983044' role='' />\n    <member type='way' ref='23983045' role='' />\n    <member type='way' ref='23635859' role='' />\n    <member type='way' ref='388313086' role='' />\n    <member type='way' ref='23624341' role='' />\n    <member type='way' ref='218506099' role='' />\n    <member type='way' ref='218506102' role='' />\n    <member type='way' ref='388313089' role='' />\n    <member type='way' ref='23635835' role='' />\n    <member type='way' ref='24490162' role='' />\n    <member type='way' ref='372687462' role='' />\n    <member type='way' ref='372687453' role='' />\n    <member type='way' ref='184497381' role='' />\n    <member type='way' ref='450080910' role='' />\n    <member type='way' ref='375226731' role='' />\n    <member type='way' ref='95914917' role='' />\n    <member type='way' ref='388070279' role='' />\n    <member type='way' ref='531226944' role='' />\n    <member type='way' ref='388070402' role='' />\n    <member type='way' ref='411108638' role='' />\n    <member type='way' ref='411108638' role='' />\n    <member type='way' ref='491895463' role='' />\n    <member type='way' ref='386808188' role='' />\n    <member type='way' ref='388070279' role='' />\n    <member type='way' ref='95914917' role='' />\n    <member type='way' ref='375226731' role='' />\n    <member type='way' ref='375226733' role='' />\n    <member type='way' ref='439906258' role='' />\n    <member type='way' ref='372830909' role='' />\n    <member type='way' ref='214712272' role='' />\n    <member type='way' ref='153767713' role='' />\n    <member type='way' ref='23624136' role='' />\n    <member type='way' ref='23624139' role='' />\n    <member type='way' ref='23679127' role='' />\n    <member type='way' ref='23576857' role='' />\n    <member type='way' ref='8176503' role='' />\n    <member type='way' ref='90400241' role='' />\n    <member type='way' ref='68019853' role='' />\n    <member type='way' ref='68019861' role='' />\n    <member type='way' ref='68019854' role='' />\n    <member type='way' ref='68019860' role='' />\n    <member type='way' ref='388313112' role='' />\n    <member type='way' ref='319793717' role='' />\n    <member type='way' ref='23628679' role='' />\n    <member type='way' ref='388313090' role='' />\n    <member type='way' ref='49071513' role='' />\n    <member type='way' ref='319793714' role='' />\n    <member type='way' ref='388313087' role='' />\n    <member type='way' ref='23636198' role='' />\n    <member type='way' ref='572558597' role='' />\n    <member type='way' ref='572558602' role='' />\n    <member type='way' ref='23636199' role='' />\n    <member type='way' ref='24327371' role='' />\n    <member type='way' ref='145296229' role='' />\n    <member type='way' ref='145296219' role='' />\n    <member type='way' ref='24392408' role='' />\n    <member type='way' ref='24392409' role='' />\n    <member type='way' ref='31072809' role='' />\n    <member type='way' ref='31072812' role='' />\n    <member type='way' ref='38963994' role='' />\n    <member type='way' ref='38963985' role='' />\n    <member type='way' ref='146196149' role='' />\n    <member type='way' ref='146196147' role='' />\n    <member type='way' ref='44043362' role='' />\n    <member type='way' ref='44043363' role='' />\n    <member type='way' ref='146196152' role='' />\n    <member type='way' ref='146196146' role='' />\n    <member type='way' ref='211026511' role='' />\n    <member type='way' ref='8136826' role='' />\n    <member type='way' ref='31072799' role='' />\n    <member type='way' ref='31072802' role='' />\n    <member type='way' ref='38964054' role='' />\n    <member type='way' ref='40069919' role='' />\n    <member type='way' ref='279073009' role='' />\n    <member type='way' ref='279073016' role='' />\n    <member type='way' ref='279073006' role='' />\n    <member type='way' ref='279073018' role='' />\n    <member type='way' ref='279073010' role='' />\n    <member type='way' ref='279073013' role='' />\n    <member type='way' ref='182731879' role='' />\n    <member type='way' ref='279073004' role='' />\n    <member type='way' ref='279073012' role='' />\n    <member type='way' ref='8136825' role='' />\n    <member type='way' ref='40069980' role='' />\n    <member type='way' ref='8136824' role='' />\n    <member type='way' ref='24875950' role='' />\n    <member type='way' ref='24875951' role='' />\n    <member type='way' ref='4080342' role='' />\n    <member type='way' ref='38819879' role='' />\n    <member type='way' ref='211026516' role='' />\n    <member type='way' ref='40071076' role='' />\n    <member type='way' ref='31072789' role='' />\n    <member type='way' ref='31072790' role='' />\n    <member type='way' ref='24596435' role='' />\n    <member type='way' ref='572610325' role='' />\n    <member type='way' ref='23314628' role='' />\n    <member type='way' ref='143334979' role='' />\n    <member type='way' ref='143334972' role='' />\n    <member type='way' ref='29680423' role='' />\n    <member type='way' ref='29680433' role='' />\n    <member type='way' ref='4211019' role='' />\n    <member type='way' ref='28888764' role='' />\n    <member type='way' ref='28888765' role='' />\n    <member type='way' ref='28886478' role='' />\n    <member type='way' ref='28886480' role='' />\n    <member type='way' ref='30267410' role='' />\n    <member type='way' ref='30267412' role='' />\n    <member type='way' ref='40153712' role='' />\n    <member type='way' ref='28520255' role='' />\n    <member type='way' ref='28520315' role='' />\n    <member type='way' ref='28520313' role='' />\n    <member type='way' ref='28520731' role='' />\n    <member type='way' ref='28520727' role='' />\n    <member type='way' ref='28523774' role='' />\n    <member type='way' ref='28523771' role='' />\n    <member type='way' ref='28679381' role='' />\n    <member type='way' ref='28679380' role='' />\n    <member type='way' ref='28679386' role='' />\n    <member type='way' ref='28679383' role='' />\n    <member type='way' ref='220533699' role='' />\n    <member type='way' ref='220533701' role='' />\n    <member type='way' ref='40082916' role='' />\n    <member type='way' ref='40082510' role='' />\n    <member type='way' ref='4211001' role='' />\n    <member type='way' ref='40127084' role='' />\n    <member type='way' ref='40127082' role='' />\n    <member type='way' ref='40127233' role='' />\n    <member type='way' ref='40127370' role='' />\n    <member type='way' ref='40127431' role='' />\n    <member type='way' ref='40127432' role='' />\n    <member type='way' ref='40127441' role='' />\n    <member type='way' ref='40127518' role='' />\n    <member type='way' ref='40127541' role='' />\n    <member type='way' ref='40127540' role='' />\n    <member type='way' ref='24940724' role='' />\n    <member type='way' ref='40127593' role='' />\n    <member type='way' ref='40127844' role='' />\n    <member type='way' ref='40127843' role='' />\n    <member type='way' ref='40127864' role='' />\n    <member type='way' ref='40127863' role='' />\n    <member type='way' ref='40127925' role='' />\n    <member type='way' ref='40127904' role='' />\n    <member type='way' ref='40127977' role='' />\n    <member type='way' ref='40127976' role='' />\n    <member type='way' ref='24960396' role='' />\n    <member type='way' ref='24960397' role='' />\n    <member type='way' ref='24936413' role='' />\n    <member type='way' ref='147756249' role='' />\n    <member type='way' ref='296512986' role='' />\n    <member type='way' ref='40128411' role='' />\n    <member type='way' ref='38816887' role='' />\n    <member type='way' ref='40128706' role='' />\n    <member type='way' ref='38816767' role='' />\n    <member type='way' ref='38816718' role='' />\n    <member type='way' ref='39887926' role='' />\n    <member type='way' ref='440338460' role='' />\n    <member type='way' ref='39887921' role='' />\n    <member type='way' ref='440338457' role='' />\n    <member type='way' ref='38815440' role='' />\n    <member type='way' ref='182731875' role='' />\n    <member type='way' ref='8135078' role='' />\n    <member type='way' ref='8132654' role='' />\n    <member type='way' ref='292644143' role='' />\n    <member type='way' ref='292644144' role='' />\n    <member type='way' ref='10958440' role='' />\n    <member type='way' ref='10958453' role='' />\n    <member type='way' ref='361204267' role='' />\n    <member type='way' ref='361204264' role='' />\n    <member type='way' ref='4683336' role='' />\n    <member type='way' ref='296093118' role='' />\n    <member type='way' ref='296093125' role='' />\n    <member type='way' ref='164691754' role='' />\n    <member type='way' ref='4683335' role='' />\n    <member type='way' ref='164689857' role='' />\n    <member type='way' ref='164689856' role='' />\n    <member type='way' ref='40814689' role='' />\n    <member type='way' ref='40814690' role='' />\n    <member type='way' ref='286430791' role='' />\n    <member type='way' ref='164689406' role='' />\n    <member type='way' ref='286430792' role='' />\n    <member type='way' ref='164688607' role='' />\n    <member type='way' ref='4040443' role='' />\n    <member type='way' ref='111626846' role='' />\n    <member type='way' ref='50898254' role='' />\n    <member type='way' ref='286430790' role='' />\n    <member type='way' ref='4498560' role='' />\n    <member type='way' ref='50898242' role='' />\n    <member type='way' ref='27452181' role='' />\n    <member type='way' ref='27452182' role='' />\n    <member type='way' ref='46827888' role='' />\n    <member type='way' ref='46828002' role='' />\n    <member type='way' ref='284411437' role='' />\n    <member type='way' ref='456169126' role='' />\n    <member type='way' ref='470968483' role='' />\n    <member type='way' ref='72114686' role='' />\n    <member type='way' ref='284412972' role='' />\n    <member type='way' ref='456169130' role='' />\n    <member type='way' ref='438161904' role='' />\n    <member type='way' ref='455835779' role='' />\n    <member type='way' ref='455835782' role='' />\n    <member type='way' ref='455835783' role='' />\n    <member type='way' ref='455835784' role='' />\n    <member type='way' ref='450833445' role='' />\n    <member type='way' ref='451835953' role='' />\n    <member type='way' ref='266160718' role='' />\n    <member type='way' ref='144049466' role='' />\n    <member type='way' ref='144049459' role='' />\n    <member type='way' ref='165275030' role='' />\n    <member type='way' ref='165525371' role='' />\n    <member type='way' ref='165525377' role='' />\n    <member type='way' ref='165525374' role='' />\n    <member type='way' ref='165525370' role='' />\n    <member type='way' ref='550053403' role='' />\n    <member type='way' ref='165525369' role='' />\n    <member type='way' ref='116023686' role='' />\n    <member type='way' ref='165275032' role='' />\n    <member type='way' ref='165275029' role='' />\n    <member type='way' ref='165275047' role='' />\n    <member type='way' ref='116023731' role='' />\n    <member type='way' ref='23936453' role='' />\n    <member type='way' ref='386788586' role='' />\n    <member type='way' ref='471031928' role='' />\n    <member type='way' ref='386788588' role='' />\n    <member type='way' ref='471031925' role='' />\n    <member type='way' ref='266160716' role='' />\n    <member type='way' ref='451835953' role='' />\n    <member type='way' ref='450833445' role='' />\n    <member type='way' ref='455835784' role='' />\n    <member type='way' ref='455835783' role='' />\n    <member type='way' ref='455835782' role='' />\n    <member type='way' ref='455835779' role='' />\n    <member type='way' ref='284412712' role='' />\n    <member type='way' ref='130989059' role='' />\n    <member type='way' ref='469381404' role='' />\n    <member type='way' ref='455835777' role='' />\n    <member type='way' ref='168975965' role='' />\n    <member type='way' ref='46827875' role='' />\n    <member type='way' ref='46827873' role='' />\n    <member type='way' ref='617882270' role='' />\n    <member type='way' ref='116605087' role='' />\n    <member type='way' ref='116605088' role='' />\n    <member type='way' ref='116605086' role='' />\n    <member type='way' ref='58703987' role='' />\n    <member type='way' ref='116615256' role='' />\n    <member type='way' ref='116614210' role='' />\n    <member type='way' ref='116613456' role='' />\n    <member type='way' ref='116613464' role='' />\n    <member type='way' ref='116613466' role='' />\n    <member type='way' ref='4498563' role='' />\n    <member type='way' ref='58703991' role='' />\n    <member type='way' ref='506393763' role='' />\n    <member type='way' ref='504318420' role='' />\n    <member type='way' ref='538390979' role='' />\n    <member type='way' ref='540043597' role='' />\n    <member type='way' ref='540043600' role='' />\n    <member type='way' ref='540043598' role='' />\n    <member type='way' ref='540043596' role='' />\n    <member type='way' ref='540043601' role='' />\n    <member type='way' ref='539199603' role='' />\n    <member type='way' ref='214287033' role='' />\n    <member type='way' ref='540043595' role='' />\n    <member type='way' ref='214287031' role='' />\n    <member type='way' ref='214860684' role='' />\n    <member type='way' ref='214860680' role='' />\n    <member type='way' ref='165528067' role='' />\n    <member type='way' ref='440330697' role='' />\n    <member type='way' ref='440330706' role='' />\n    <member type='way' ref='440330703' role='' />\n    <member type='way' ref='440330699' role='' />\n    <member type='way' ref='440330694' role='' />\n    <member type='way' ref='142028779' role='' />\n    <member type='way' ref='213579174' role='' />\n    <member type='way' ref='226048678' role='' />\n    <member type='way' ref='8132639' role='' />\n    <member type='way' ref='226048675' role='' />\n    <member type='way' ref='236557696' role='' />\n    <member type='way' ref='284404463' role='' />\n    <member type='way' ref='4865150' role='' />\n    <member type='way' ref='214860674' role='' />\n    <member type='way' ref='284402509' role='' />\n    <member type='way' ref='284402514' role='' />\n    <member type='way' ref='237772652' role='' />\n    <member type='way' ref='13865741' role='' />\n    <member type='way' ref='13865742' role='' />\n    <member type='way' ref='226048674' role='' />\n    <member type='way' ref='284402024' role='' />\n    <member type='way' ref='4865153' role='' />\n    <member type='way' ref='4865151' role='' />\n    <member type='way' ref='284399987' role='' />\n    <member type='way' ref='165529478' role='' />\n    <member type='way' ref='41291142' role='' />\n    <member type='way' ref='284398486' role='' />\n    <member type='way' ref='41291146' role='' />\n    <member type='way' ref='236367089' role='' />\n    <member type='way' ref='156820995' role='' />\n    <member type='way' ref='4040295' role='' />\n    <member type='way' ref='142028783' role='' />\n    <member type='way' ref='284395943' role='' />\n    <member type='way' ref='284395941' role='' />\n    <member type='way' ref='440235414' role='' />\n    <member type='way' ref='4865180' role='' />\n    <member type='way' ref='284395947' role='' />\n    <member type='way' ref='284395942' role='' />\n    <member type='way' ref='142028782' role='' />\n    <member type='way' ref='238469679' role='' />\n    <member type='way' ref='4865183' role='' />\n    <member type='way' ref='4865181' role='' />\n    <member type='way' ref='8132601' role='' />\n    <member type='way' ref='571218330' role='' />\n    <member type='way' ref='8132602' role='' />\n    <member type='way' ref='8132180' role='' />\n    <member type='way' ref='290069111' role='' />\n    <member type='way' ref='290069112' role='' />\n    <member type='way' ref='238322472' role='' />\n    <member type='way' ref='571218310' role='' />\n    <member type='way' ref='571218307' role='' />\n    <member type='way' ref='285585340' role='' />\n    <member type='way' ref='285585344' role='' />\n    <member type='way' ref='285585343' role='' />\n    <member type='way' ref='285585346' role='' />\n    <member type='way' ref='194368196' role='' />\n    <member type='way' ref='290069109' role='' />\n    <member type='way' ref='290069107' role='' />\n    <member type='way' ref='290069105' role='' />\n    <member type='way' ref='290043857' role='' />\n    <member type='way' ref='290043858' role='' />\n    <member type='way' ref='4590392' role='' />\n    <member type='way' ref='290036517' role='' />\n    <member type='way' ref='194368195' role='' />\n    <member type='way' ref='290036516' role='' />\n    <member type='way' ref='58834820' role='' />\n    <member type='way' ref='571218268' role='' />\n    <member type='way' ref='58834836' role='' />\n    <member type='way' ref='571218275' role='' />\n    <member type='way' ref='4590390' role='' />\n    <member type='way' ref='290036518' role='' />\n    <member type='way' ref='64033127' role='' />\n    <member type='way' ref='64033140' role='' />\n    <member type='way' ref='440224290' role='' />\n    <member type='way' ref='4360773' role='' />\n    <member type='way' ref='440187056' role='' />\n    <member type='way' ref='388313104' role='' />\n    <member type='way' ref='4010208' role='' />\n    <member type='way' ref='4010205' role='' />\n    <member type='way' ref='4010206' role='' />\n    <member type='way' ref='388313100' role='' />\n    <member type='way' ref='386785831' role='' />\n    <member type='way' ref='388313099' role='' />\n    <member type='way' ref='388313101' role='' />\n    <member type='way' ref='388313114' role='' />\n    <member type='way' ref='388313103' role='' />\n    <member type='way' ref='550263971' role='' />\n    <member type='way' ref='550263972' role='' />\n    <member type='way' ref='550263973' role='' />\n    <member type='way' ref='550263974' role='' />\n    <member type='way' ref='388313110' role='' />\n    <member type='way' ref='550298875' role='' />\n    <member type='way' ref='550298874' role='' />\n    <member type='way' ref='388313115' role='' />\n    <member type='way' ref='388313103' role='' />\n    <member type='way' ref='550263971' role='' />\n    <member type='way' ref='550263972' role='' />\n    <member type='way' ref='550263973' role='' />\n    <member type='way' ref='550263974' role='' />\n    <member type='way' ref='388313110' role='' />\n    <member type='way' ref='388313113' role='' />\n    <member type='way' ref='568549581' role='' />\n    <member type='way' ref='198256718' role='' />\n    <member type='way' ref='440224316' role='' />\n    <member type='way' ref='388313099' role='' />\n    <member type='way' ref='497861437' role='' />\n    <member type='way' ref='310851705' role='' />\n    <member type='way' ref='8132020' role='' />\n    <member type='way' ref='8132019' role='' />\n    <member type='way' ref='8132021' role='' />\n    <member type='way' ref='440190404' role='' />\n    <member type='way' ref='310851703' role='' />\n    <member type='way' ref='481451924' role='' />\n    <member type='way' ref='440187053' role='' />\n    <member type='way' ref='4923112' role='' />\n    <member type='way' ref='440187058' role='' />\n    <member type='way' ref='8137991' role='' />\n    <member type='way' ref='550263975' role='' />\n    <member type='way' ref='8142468' role='' />\n    <member type='way' ref='8142466' role='' />\n    <member type='way' ref='367358070' role='' />\n    <member type='way' ref='25307097' role='' />\n    <member type='way' ref='571203997' role='' />\n    <member type='way' ref='571203995' role='' />\n    <member type='way' ref='367358072' role='' />\n    <member type='way' ref='25307107' role='' />\n    <member type='way' ref='25307106' role='' />\n    <member type='way' ref='25307099' role='' />\n    <member type='way' ref='25307098' role='' />\n    <member type='way' ref='440066749' role='' />\n    <member type='way' ref='41291157' role='' />\n    <member type='way' ref='41291158' role='' />\n    <member type='way' ref='440066748' role='' />\n    <member type='way' ref='4822010' role='' />\n    <member type='way' ref='323437399' role='' />\n    <member type='way' ref='323437400' role='' />\n    <member type='way' ref='4822011' role='' />\n    <member type='way' ref='4360825' role='' />\n    <member type='way' ref='27946887' role='' />\n    <member type='way' ref='379351539' role='' />\n    <member type='way' ref='288468826' role='' />\n    <member type='way' ref='27946888' role='' />\n    <member type='way' ref='475555585' role='' />\n    <member type='way' ref='552940512' role='' />\n    <member type='way' ref='552940511' role='' />\n    <member type='way' ref='475555584' role='' />\n    <member type='way' ref='4923345' role='' />\n    <member type='way' ref='4923342' role='' />\n    <member type='way' ref='4923338' role='' />\n    <member type='way' ref='475717741' role='' />\n    <member type='way' ref='475717740' role='' />\n    <member type='way' ref='552939707' role='' />\n    <member type='way' ref='552939706' role='' />\n    <member type='way' ref='552939142' role='' />\n    <member type='way' ref='552939141' role='' />\n    <member type='way' ref='4361340' role='' />\n    <member type='way' ref='41139067' role='' />\n    <member type='way' ref='41139070' role='' />\n    <member type='way' ref='41139072' role='' />\n    <member type='way' ref='133953313' role='' />\n    <member type='way' ref='263960541' role='' />\n    <member type='way' ref='263960544' role='' />\n    <member type='way' ref='263960539' role='' />\n    <member type='way' ref='263960528' role='' />\n    <member type='way' ref='263960535' role='' />\n    <member type='way' ref='263960546' role='' />\n    <member type='way' ref='362669439' role='' />\n    <member type='way' ref='362669438' role='' />\n    <member type='way' ref='362669441' role='' />\n    <member type='way' ref='68294294' role='' />\n    <member type='way' ref='68294289' role='' />\n    <member type='way' ref='68294291' role='' />\n    <member type='way' ref='139184460' role='' />\n    <member type='way' ref='230824477' role='' />\n    <member type='way' ref='324703901' role='' />\n    <member type='way' ref='324703902' role='' />\n    <member type='way' ref='169363259' role='' />\n    <member type='way' ref='169363242' role='' />\n    <member type='way' ref='68295083' role='' />\n    <member type='way' ref='169363262' role='' />\n    <member type='way' ref='169363224' role='' />\n    <member type='way' ref='169363233' role='' />\n    <member type='way' ref='169363273' role='' />\n    <member type='way' ref='169363257' role='' />\n    <member type='way' ref='169363282' role='' />\n    <member type='way' ref='169363255' role='' />\n    <member type='way' ref='169363286' role='' />\n    <member type='way' ref='169363248' role='' />\n    <member type='way' ref='169363285' role='' />\n    <member type='way' ref='230824480' role='' />\n    <member type='way' ref='68290020' role='' />\n    <member type='way' ref='476747418' role='' />\n    <member type='way' ref='476747417' role='' />\n    <member type='way' ref='259750246' role='' />\n    <member type='way' ref='259750247' role='' />\n    <member type='way' ref='68290014' role='' />\n    <member type='way' ref='68290038' role='' />\n    <member type='way' ref='169363230' role='' />\n    <member type='way' ref='169363264' role='' />\n    <member type='way' ref='68290030' role='' />\n    <member type='way' ref='153309162' role='' />\n    <member type='way' ref='153309178' role='' />\n    <member type='way' ref='68297017' role='' />\n    <member type='way' ref='571446813' role='' />\n    <member type='way' ref='571446812' role='' />\n    <member type='way' ref='571446824' role='' />\n    <member type='way' ref='571446823' role='' />\n    <member type='way' ref='477661488' role='' />\n    <member type='way' ref='477661487' role='' />\n    <member type='way' ref='4361446' role='' />\n    <member type='way' ref='32544465' role='' />\n    <member type='way' ref='68301277' role='' />\n    <member type='way' ref='68301266' role='' />\n    <member type='way' ref='68301273' role='' />\n    <member type='way' ref='68301280' role='' />\n    <member type='way' ref='68301257' role='' />\n    <member type='way' ref='68301262' role='' />\n    <member type='way' ref='68301269' role='' />\n    <member type='way' ref='571446831' role='' />\n    <member type='way' ref='571446830' role='' />\n    <member type='way' ref='571446836' role='' />\n    <member type='way' ref='571446835' role='' />\n    <member type='way' ref='259734075' role='' />\n    <member type='way' ref='259734073' role='' />\n    <member type='way' ref='259734132' role='' />\n    <member type='way' ref='259734133' role='' />\n    <member type='way' ref='259734119' role='' />\n    <member type='way' ref='259734125' role='' />\n    <member type='way' ref='471643305' role='' />\n    <member type='way' ref='4817721' role='' />\n    <member type='way' ref='4361570' role='' />\n    <member type='way' ref='195852545' role='' />\n    <member type='way' ref='298081599' role='' />\n    <member type='way' ref='298082971' role='' />\n    <member type='way' ref='195852542' role='' />\n    <member type='way' ref='298081597' role='' />\n    <member type='way' ref='571453991' role='' />\n    <member type='way' ref='4361569' role='' />\n    <member type='way' ref='160489664' role='' />\n    <member type='way' ref='4948289' role='' />\n    <member type='way' ref='121203167' role='' />\n    <member type='way' ref='121203168' role='' />\n    <member type='way' ref='471643302' role='' />\n    <member type='way' ref='471643289' role='' />\n    <member type='way' ref='195852543' role='' />\n    <member type='way' ref='571453996' role='' />\n    <member type='way' ref='571453998' role='' />\n    <member type='way' ref='285529812' role='' />\n    <member type='way' ref='285529814' role='' />\n    <member type='way' ref='288842424' role='' />\n    <member type='way' ref='288842425' role='' />\n    <member type='way' ref='25545749' role='' />\n    <member type='way' ref='4888789' role='' />\n    <member type='way' ref='288842423' role='' />\n    <member type='way' ref='288842422' role='' />\n    <member type='way' ref='288842513' role='' />\n    <member type='way' ref='288842514' role='' />\n    <member type='way' ref='288842417' role='' />\n    <member type='way' ref='288842416' role='' />\n    <member type='way' ref='28641852' role='' />\n    <member type='way' ref='25545451' role='' />\n    <member type='way' ref='288842415' role='' />\n    <member type='way' ref='288842414' role='' />\n    <member type='way' ref='571454003' role='' />\n    <member type='way' ref='571454005' role='' />\n    <member type='way' ref='96271019' role='' />\n    <member type='way' ref='96271021' role='' />\n    <member type='way' ref='96269618' role='' />\n    <member type='way' ref='96269611' role='' />\n    <member type='way' ref='288842411' role='' />\n    <member type='way' ref='38893289' role='' />\n    <member type='way' ref='471643288' role='' />\n    <member type='way' ref='4888859' role='' />\n    <member type='way' ref='471643290' role='' />\n    <member type='way' ref='471643291' role='' />\n    <member type='way' ref='96286400' role='' />\n    <member type='way' ref='29056787' role='' />\n    <member type='way' ref='370108006' role='' />\n    <member type='way' ref='4941500' role='' />\n    <member type='way' ref='370108007' role='' />\n    <tag k='fixme' v='Set platforms atOslo Bussterminal' />\n    <tag k='from' v='København' />\n    <tag k='from:da' v='København' />\n    <tag k='from:en' v='Copenhagen' />\n    <tag k='from:nb' v='København' />\n    <tag k='from:sv' v='Köpenhamn' />\n    <tag k='importance' v='national' />\n    <tag k='name' v='Buss 820: København - Oslo' />\n    <tag k='name:da' v='Bus 820: København - Oslo' />\n    <tag k='name:en' v='Bus 820: Copenhagen - Oslo' />\n    <tag k='name:nb' v='Buss 820: Købehnavn - Oslo' />\n    <tag k='name:sv' v='Buss 820: Köpenhamn - Oslo' />\n    <tag k='network' v='Swebus' />\n    <tag k='operator' v='Swebus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='820' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='Oslo' />\n    <tag k='to:da' v='Oslo' />\n    <tag k='to:en' v='Oslo' />\n    <tag k='to:nb' v='Oslo' />\n    <tag k='to:sv' v='Oslo' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='5794316' timestamp='2018-07-16T09:19:28Z' uid='953001' user='tomasy' version='84' changeset='60755417'>\n    <member type='node' ref='339884586' role='stop' />\n    <member type='node' ref='416908979' role='stop' />\n    <member type='node' ref='4128416085' role='stop' />\n    <member type='node' ref='3418254623' role='stop' />\n    <member type='node' ref='1998995667' role='stop' />\n    <member type='node' ref='1310612418' role='platform' />\n    <member type='node' ref='1938160930' role='stop' />\n    <member type='way' ref='435653432' role='stop' />\n    <member type='way' ref='391105367' role='platform' />\n    <member type='node' ref='5313132106' role='stop' />\n    <member type='way' ref='411108616' role='platform' />\n    <member type='way' ref='334753833' role='platform' />\n    <member type='way' ref='189255217' role='platform' />\n    <member type='way' ref='25545167' role='' />\n    <member type='way' ref='4886308' role='' />\n    <member type='way' ref='25545236' role='' />\n    <member type='way' ref='30731949' role='' />\n    <member type='way' ref='370108008' role='' />\n    <member type='way' ref='471643292' role='' />\n    <member type='way' ref='25545320' role='' />\n    <member type='way' ref='573542329' role='' />\n    <member type='way' ref='96279154' role='' />\n    <member type='way' ref='25545576' role='' />\n    <member type='way' ref='288842410' role='' />\n    <member type='way' ref='96269621' role='' />\n    <member type='way' ref='96269629' role='' />\n    <member type='way' ref='96271017' role='' />\n    <member type='way' ref='96271015' role='' />\n    <member type='way' ref='571454006' role='' />\n    <member type='way' ref='571454004' role='' />\n    <member type='way' ref='288842412' role='' />\n    <member type='way' ref='288842413' role='' />\n    <member type='way' ref='25545468' role='' />\n    <member type='way' ref='25545735' role='' />\n    <member type='way' ref='288842418' role='' />\n    <member type='way' ref='288842419' role='' />\n    <member type='way' ref='288842511' role='' />\n    <member type='way' ref='288842512' role='' />\n    <member type='way' ref='288842421' role='' />\n    <member type='way' ref='288842420' role='' />\n    <member type='way' ref='25545798' role='' />\n    <member type='way' ref='25545834' role='' />\n    <member type='way' ref='288842426' role='' />\n    <member type='way' ref='288842427' role='' />\n    <member type='way' ref='285529811' role='' />\n    <member type='way' ref='285529813' role='' />\n    <member type='way' ref='4333216' role='' />\n    <member type='way' ref='571453997' role='' />\n    <member type='way' ref='571453995' role='' />\n    <member type='way' ref='195852539' role='' />\n    <member type='way' ref='471643301' role='' />\n    <member type='way' ref='195852536' role='' />\n    <member type='way' ref='194321905' role='' />\n    <member type='way' ref='194321890' role='' />\n    <member type='way' ref='121203166' role='' />\n    <member type='way' ref='121203169' role='' />\n    <member type='way' ref='4817723' role='' />\n    <member type='way' ref='160489663' role='' />\n    <member type='way' ref='4817722' role='' />\n    <member type='way' ref='571453990' role='' />\n    <member type='way' ref='440328542' role='' />\n    <member type='way' ref='440328544' role='' />\n    <member type='way' ref='440328543' role='' />\n    <member type='way' ref='195852541' role='' />\n    <member type='way' ref='4361564' role='' />\n    <member type='way' ref='4361565' role='' />\n    <member type='way' ref='259734124' role='' />\n    <member type='way' ref='259734123' role='' />\n    <member type='way' ref='259734137' role='' />\n    <member type='way' ref='259734136' role='' />\n    <member type='way' ref='259734070' role='' />\n    <member type='way' ref='259734071' role='' />\n    <member type='way' ref='571446833' role='' />\n    <member type='way' ref='571446834' role='' />\n    <member type='way' ref='571446828' role='' />\n    <member type='way' ref='571446829' role='' />\n    <member type='way' ref='68301259' role='' />\n    <member type='way' ref='68301260' role='' />\n    <member type='way' ref='68301267' role='' />\n    <member type='way' ref='68301275' role='' />\n    <member type='way' ref='68301278' role='' />\n    <member type='way' ref='68301254' role='' />\n    <member type='way' ref='68301256' role='' />\n    <member type='way' ref='37878862' role='' />\n    <member type='way' ref='477661486' role='' />\n    <member type='way' ref='477661485' role='' />\n    <member type='way' ref='37878863' role='' />\n    <member type='way' ref='571446822' role='' />\n    <member type='way' ref='571446825' role='' />\n    <member type='way' ref='571446815' role='' />\n    <member type='way' ref='571446814' role='' />\n    <member type='way' ref='68297027' role='' />\n    <member type='way' ref='153309159' role='' />\n    <member type='way' ref='153309157' role='' />\n    <member type='way' ref='550305950' role='' />\n    <member type='way' ref='68290009' role='' />\n    <member type='way' ref='169363234' role='' />\n    <member type='way' ref='169363222' role='' />\n    <member type='way' ref='68290029' role='' />\n    <member type='way' ref='68290037' role='' />\n    <member type='way' ref='259750245' role='' />\n    <member type='way' ref='476747416' role='' />\n    <member type='way' ref='476747415' role='' />\n    <member type='way' ref='259750229' role='' />\n    <member type='way' ref='132032815' role='' />\n    <member type='way' ref='230824478' role='' />\n    <member type='way' ref='169363245' role='' />\n    <member type='way' ref='169363283' role='' />\n    <member type='way' ref='169363250' role='' />\n    <member type='way' ref='169363281' role='' />\n    <member type='way' ref='169363229' role='' />\n    <member type='way' ref='169363270' role='' />\n    <member type='way' ref='169363235' role='' />\n    <member type='way' ref='169363228' role='' />\n    <member type='way' ref='169363240' role='' />\n    <member type='way' ref='169363267' role='' />\n    <member type='way' ref='154247750' role='' />\n    <member type='way' ref='169363253' role='' />\n    <member type='way' ref='169363265' role='' />\n    <member type='way' ref='324703904' role='' />\n    <member type='way' ref='324703903' role='' />\n    <member type='way' ref='230824479' role='' />\n    <member type='way' ref='68294283' role='' />\n    <member type='way' ref='139184461' role='' />\n    <member type='way' ref='169363276' role='' />\n    <member type='way' ref='167349501' role='' />\n    <member type='way' ref='362669440' role='' />\n    <member type='way' ref='362669437' role='' />\n    <member type='way' ref='263960530' role='' />\n    <member type='way' ref='263960526' role='' />\n    <member type='way' ref='263960540' role='' />\n    <member type='way' ref='263960543' role='' />\n    <member type='way' ref='263960542' role='' />\n    <member type='way' ref='263960545' role='' />\n    <member type='way' ref='61941290' role='' />\n    <member type='way' ref='41139073' role='' />\n    <member type='way' ref='41139075' role='' />\n    <member type='way' ref='41139063' role='' />\n    <member type='way' ref='552939140' role='' />\n    <member type='way' ref='552939139' role='' />\n    <member type='way' ref='552939709' role='' />\n    <member type='way' ref='552939708' role='' />\n    <member type='way' ref='475717743' role='' />\n    <member type='way' ref='475717742' role='' />\n    <member type='way' ref='4923340' role='' />\n    <member type='way' ref='4923343' role='' />\n    <member type='way' ref='4923344' role='' />\n    <member type='way' ref='475555583' role='' />\n    <member type='way' ref='552940510' role='' />\n    <member type='way' ref='552940509' role='' />\n    <member type='way' ref='475555582' role='' />\n    <member type='way' ref='4822013' role='' />\n    <member type='way' ref='288468828' role='' />\n    <member type='way' ref='288468820' role='' />\n    <member type='way' ref='27946885' role='' />\n    <member type='way' ref='27946886' role='' />\n    <member type='way' ref='4822012' role='' />\n    <member type='way' ref='440066747' role='' />\n    <member type='way' ref='440066746' role='' />\n    <member type='way' ref='4360782' role='' />\n    <member type='way' ref='288393977' role='' />\n    <member type='way' ref='288393978' role='' />\n    <member type='way' ref='4360775' role='' />\n    <member type='way' ref='41291159' role='' />\n    <member type='way' ref='41291160' role='' />\n    <member type='way' ref='25307100' role='' />\n    <member type='way' ref='25307103' role='' />\n    <member type='way' ref='25307108' role='' />\n    <member type='way' ref='25307109' role='' />\n    <member type='way' ref='367358069' role='' />\n    <member type='way' ref='571203996' role='' />\n    <member type='way' ref='571203998' role='' />\n    <member type='way' ref='4360769' role='' />\n    <member type='way' ref='367358071' role='' />\n    <member type='way' ref='4360771' role='' />\n    <member type='way' ref='4270348' role='' />\n    <member type='way' ref='550298870' role='' />\n    <member type='way' ref='4360772' role='' />\n    <member type='way' ref='198256718' role='' />\n    <member type='way' ref='440224316' role='' />\n    <member type='way' ref='388313099' role='' />\n    <member type='way' ref='388313101' role='' />\n    <member type='way' ref='388313114' role='' />\n    <member type='way' ref='388313103' role='' />\n    <member type='way' ref='550263971' role='' />\n    <member type='way' ref='550263972' role='' />\n    <member type='way' ref='550263973' role='' />\n    <member type='way' ref='550263974' role='' />\n    <member type='way' ref='388313110' role='' />\n    <member type='way' ref='388313113' role='' />\n    <member type='way' ref='568549581' role='' />\n    <member type='way' ref='198256718' role='' />\n    <member type='way' ref='440224316' role='' />\n    <member type='way' ref='388313099' role='' />\n    <member type='way' ref='497861437' role='' />\n    <member type='way' ref='310851705' role='' />\n    <member type='way' ref='388313107' role='' />\n    <member type='way' ref='388313100' role='' />\n    <member type='way' ref='440190702' role='' />\n    <member type='way' ref='4010207' role='' />\n    <member type='way' ref='440224291' role='' />\n    <member type='way' ref='41291150' role='' />\n    <member type='way' ref='64033121' role='' />\n    <member type='way' ref='64033153' role='' />\n    <member type='way' ref='4590389' role='' />\n    <member type='way' ref='4590391' role='' />\n    <member type='way' ref='571218279' role='' />\n    <member type='way' ref='58834835' role='' />\n    <member type='way' ref='571218271' role='' />\n    <member type='way' ref='58834826' role='' />\n    <member type='way' ref='290036519' role='' />\n    <member type='way' ref='238166899' role='' />\n    <member type='way' ref='290036515' role='' />\n    <member type='way' ref='4014727' role='' />\n    <member type='way' ref='290043856' role='' />\n    <member type='way' ref='290043859' role='' />\n    <member type='way' ref='290069106' role='' />\n    <member type='way' ref='290069108' role='' />\n    <member type='way' ref='290069113' role='' />\n    <member type='way' ref='198256714' role='' />\n    <member type='way' ref='285585342' role='' />\n    <member type='way' ref='285585345' role='' />\n    <member type='way' ref='285585338' role='' />\n    <member type='way' ref='285585337' role='' />\n    <member type='way' ref='571218314' role='' />\n    <member type='way' ref='571218318' role='' />\n    <member type='way' ref='238166900' role='' />\n    <member type='way' ref='290069110' role='' />\n    <member type='way' ref='290069114' role='' />\n    <member type='way' ref='4038677' role='' />\n    <member type='way' ref='4038675' role='' />\n    <member type='way' ref='571218322' role='' />\n    <member type='way' ref='571218326' role='' />\n    <member type='way' ref='4038676' role='' />\n    <member type='way' ref='4038626' role='' />\n    <member type='way' ref='4040271' role='' />\n    <member type='way' ref='165275003' role='' />\n    <member type='way' ref='288160092' role='' />\n    <member type='way' ref='288160090' role='' />\n    <member type='way' ref='284395939' role='' />\n    <member type='way' ref='284395948' role='' />\n    <member type='way' ref='165275004' role='' />\n    <member type='way' ref='4865179' role='' />\n    <member type='way' ref='284395944' role='' />\n    <member type='way' ref='284395945' role='' />\n    <member type='way' ref='165275005' role='' />\n    <member type='way' ref='440235413' role='' />\n    <member type='way' ref='288160093' role='' />\n    <member type='way' ref='4865152' role='' />\n    <member type='way' ref='156820997' role='' />\n    <member type='way' ref='236367090' role='' />\n    <member type='way' ref='288160094' role='' />\n    <member type='way' ref='41291147' role='' />\n    <member type='way' ref='41291143' role='' />\n    <member type='way' ref='288160091' role='' />\n    <member type='way' ref='284399988' role='' />\n    <member type='way' ref='4865154' role='' />\n    <member type='way' ref='4040302' role='' />\n    <member type='way' ref='284402021' role='' />\n    <member type='way' ref='226048677' role='' />\n    <member type='way' ref='13865743' role='' />\n    <member type='way' ref='13865744' role='' />\n    <member type='way' ref='284402510' role='' />\n    <member type='way' ref='284402511' role='' />\n    <member type='way' ref='215041207' role='' />\n    <member type='way' ref='4040303' role='' />\n    <member type='way' ref='183156782' role='' />\n    <member type='way' ref='236557699' role='' />\n    <member type='way' ref='4077750' role='' />\n    <member type='way' ref='290070332' role='' />\n    <member type='way' ref='213798704' role='' />\n    <member type='way' ref='165275007' role='' />\n    <member type='way' ref='284407459' role='' />\n    <member type='way' ref='284408726' role='' />\n    <member type='way' ref='165275002' role='' />\n    <member type='way' ref='284408727' role='' />\n    <member type='way' ref='284408724' role='' />\n    <member type='way' ref='215041210' role='' />\n    <member type='way' ref='284408723' role='' />\n    <member type='way' ref='284409597' role='' />\n    <member type='way' ref='214290381' role='' />\n    <member type='way' ref='284409595' role='' />\n    <member type='way' ref='284409596' role='' />\n    <member type='way' ref='183028746' role='' />\n    <member type='way' ref='165275006' role='' />\n    <member type='way' ref='538390974' role='' />\n    <member type='way' ref='541564166' role='' />\n    <member type='way' ref='541564165' role='' />\n    <member type='way' ref='541564164' role='' />\n    <member type='way' ref='8147694' role='' />\n    <member type='way' ref='116613465' role='' />\n    <member type='way' ref='471650441' role='' />\n    <member type='way' ref='116613461' role='' />\n    <member type='way' ref='471650443' role='' />\n    <member type='way' ref='116613463' role='' />\n    <member type='way' ref='116614208' role='' />\n    <member type='way' ref='116614211' role='' />\n    <member type='way' ref='8147693' role='' />\n    <member type='way' ref='4361872' role='' />\n    <member type='way' ref='569069243' role='' />\n    <member type='way' ref='284411437' role='' />\n    <member type='way' ref='456169126' role='' />\n    <member type='way' ref='470968483' role='' />\n    <member type='way' ref='72114686' role='' />\n    <member type='way' ref='284412972' role='' />\n    <member type='way' ref='456169130' role='' />\n    <member type='way' ref='438161904' role='' />\n    <member type='way' ref='455835779' role='' />\n    <member type='way' ref='455835782' role='' />\n    <member type='way' ref='455835783' role='' />\n    <member type='way' ref='455835784' role='' />\n    <member type='way' ref='450833445' role='' />\n    <member type='way' ref='451835953' role='' />\n    <member type='way' ref='266160716' role='' />\n    <member type='way' ref='471031925' role='' />\n    <member type='way' ref='386788588' role='' />\n    <member type='way' ref='471031928' role='' />\n    <member type='way' ref='386788586' role='' />\n    <member type='way' ref='23936453' role='' />\n    <member type='way' ref='116023731' role='' />\n    <member type='way' ref='165275047' role='' />\n    <member type='way' ref='165275029' role='' />\n    <member type='way' ref='165275032' role='' />\n    <member type='way' ref='116023686' role='' />\n    <member type='way' ref='165525369' role='' />\n    <member type='way' ref='550053403' role='' />\n    <member type='way' ref='165525370' role='' />\n    <member type='way' ref='165525374' role='' />\n    <member type='way' ref='165525377' role='' />\n    <member type='way' ref='165525371' role='' />\n    <member type='way' ref='165275030' role='' />\n    <member type='way' ref='144049459' role='' />\n    <member type='way' ref='144049466' role='' />\n    <member type='way' ref='266160718' role='' />\n    <member type='way' ref='451835953' role='' />\n    <member type='way' ref='450833445' role='' />\n    <member type='way' ref='455835784' role='' />\n    <member type='way' ref='455835783' role='' />\n    <member type='way' ref='455835782' role='' />\n    <member type='way' ref='455835779' role='' />\n    <member type='way' ref='284412712' role='' />\n    <member type='way' ref='130989059' role='' />\n    <member type='way' ref='469381404' role='' />\n    <member type='way' ref='455835777' role='' />\n    <member type='way' ref='4498657' role='' />\n    <member type='way' ref='307341994' role='' />\n    <member type='way' ref='204412193' role='' />\n    <member type='way' ref='116615261' role='' />\n    <member type='way' ref='286430805' role='' />\n    <member type='way' ref='28605282' role='' />\n    <member type='way' ref='27881413' role='' />\n    <member type='way' ref='36861884' role='' />\n    <member type='way' ref='168975966' role='' />\n    <member type='way' ref='160489662' role='' />\n    <member type='way' ref='285893666' role='' />\n    <member type='way' ref='237642411' role='' />\n    <member type='way' ref='285893664' role='' />\n    <member type='way' ref='285893668' role='' />\n    <member type='way' ref='285893665' role='' />\n    <member type='way' ref='285893667' role='' />\n    <member type='way' ref='40814687' role='' />\n    <member type='way' ref='40814688' role='' />\n    <member type='way' ref='165346635' role='' />\n    <member type='way' ref='285893669' role='' />\n    <member type='way' ref='165346636' role='' />\n    <member type='way' ref='182725088' role='' />\n    <member type='way' ref='4077779' role='' />\n    <member type='way' ref='296093122' role='' />\n    <member type='way' ref='4040978' role='' />\n    <member type='way' ref='361204262' role='' />\n    <member type='way' ref='361204265' role='' />\n    <member type='way' ref='296093116' role='' />\n    <member type='way' ref='10958445' role='' />\n    <member type='way' ref='10958444' role='' />\n    <member type='way' ref='296093117' role='' />\n    <member type='way' ref='4182320' role='' />\n    <member type='way' ref='296093115' role='' />\n    <member type='way' ref='4182405' role='' />\n    <member type='way' ref='296533439' role='' />\n    <member type='way' ref='182725091' role='' />\n    <member type='way' ref='440338458' role='' />\n    <member type='way' ref='4182406' role='' />\n    <member type='way' ref='440338459' role='' />\n    <member type='way' ref='38816759' role='' />\n    <member type='way' ref='39887915' role='' />\n    <member type='way' ref='38816819' role='' />\n    <member type='way' ref='38816840' role='' />\n    <member type='way' ref='40129164' role='' />\n    <member type='way' ref='38816970' role='' />\n    <member type='way' ref='39952969' role='' />\n    <member type='way' ref='296512985' role='' />\n    <member type='way' ref='24936411' role='' />\n    <member type='way' ref='24936412' role='' />\n    <member type='way' ref='24960394' role='' />\n    <member type='way' ref='24960395' role='' />\n    <member type='way' ref='40127972' role='' />\n    <member type='way' ref='40127973' role='' />\n    <member type='way' ref='40127905' role='' />\n    <member type='way' ref='40127922' role='' />\n    <member type='way' ref='40127867' role='' />\n    <member type='way' ref='40127868' role='' />\n    <member type='way' ref='40127850' role='' />\n    <member type='way' ref='40127849' role='' />\n    <member type='way' ref='24940721' role='' />\n    <member type='way' ref='24940722' role='' />\n    <member type='way' ref='40127513' role='' />\n    <member type='way' ref='40127514' role='' />\n    <member type='way' ref='40127470' role='' />\n    <member type='way' ref='40127471' role='' />\n    <member type='way' ref='40127428' role='' />\n    <member type='way' ref='40127427' role='' />\n    <member type='way' ref='40127253' role='' />\n    <member type='way' ref='40127236' role='' />\n    <member type='way' ref='40191763' role='' />\n    <member type='way' ref='40127106' role='' />\n    <member type='way' ref='4845988' role='' />\n    <member type='way' ref='40082302' role='' />\n    <member type='way' ref='40082288' role='' />\n    <member type='way' ref='220533700' role='' />\n    <member type='way' ref='220533702' role='' />\n    <member type='way' ref='28679387' role='' />\n    <member type='way' ref='28679388' role='' />\n    <member type='way' ref='28679378' role='' />\n    <member type='way' ref='28679384' role='' />\n    <member type='way' ref='28523773' role='' />\n    <member type='way' ref='28523772' role='' />\n    <member type='way' ref='28520724' role='' />\n    <member type='way' ref='28586426' role='' />\n    <member type='way' ref='28520311' role='' />\n    <member type='way' ref='28586447' role='' />\n    <member type='way' ref='40153713' role='' />\n    <member type='way' ref='40076967' role='' />\n    <member type='way' ref='30267414' role='' />\n    <member type='way' ref='30267415' role='' />\n    <member type='way' ref='28886481' role='' />\n    <member type='way' ref='28886477' role='' />\n    <member type='way' ref='28888779' role='' />\n    <member type='way' ref='28888759' role='' />\n    <member type='way' ref='23314626' role='' />\n    <member type='way' ref='29680521' role='' />\n    <member type='way' ref='29680368' role='' />\n    <member type='way' ref='143334982' role='' />\n    <member type='way' ref='143334975' role='' />\n    <member type='way' ref='24596433' role='' />\n    <member type='way' ref='572610326' role='' />\n    <member type='way' ref='8134212' role='' />\n    <member type='way' ref='31072786' role='' />\n    <member type='way' ref='31072787' role='' />\n    <member type='way' ref='40070887' role='' />\n    <member type='way' ref='211026514' role='' />\n    <member type='way' ref='8134211' role='' />\n    <member type='way' ref='4247977' role='' />\n    <member type='way' ref='40070460' role='' />\n    <member type='way' ref='40070054' role='' />\n    <member type='way' ref='40070255' role='' />\n    <member type='way' ref='40070349' role='' />\n    <member type='way' ref='4247979' role='' />\n    <member type='way' ref='279073005' role='' />\n    <member type='way' ref='279073014' role='' />\n    <member type='way' ref='279073011' role='' />\n    <member type='way' ref='279073020' role='' />\n    <member type='way' ref='279073008' role='' />\n    <member type='way' ref='279073017' role='' />\n    <member type='way' ref='279073007' role='' />\n    <member type='way' ref='279073015' role='' />\n    <member type='way' ref='182725101' role='' />\n    <member type='way' ref='38964045' role='' />\n    <member type='way' ref='38964052' role='' />\n    <member type='way' ref='31072806' role='' />\n    <member type='way' ref='31072808' role='' />\n    <member type='way' ref='40069603' role='' />\n    <member type='way' ref='211026513' role='' />\n    <member type='way' ref='146196150' role='' />\n    <member type='way' ref='146196155' role='' />\n    <member type='way' ref='146196151' role='' />\n    <member type='way' ref='146196148' role='' />\n    <member type='way' ref='146196153' role='' />\n    <member type='way' ref='146196154' role='' />\n    <member type='way' ref='38963978' role='' />\n    <member type='way' ref='40069619' role='' />\n    <member type='way' ref='31072815' role='' />\n    <member type='way' ref='31072817' role='' />\n    <member type='way' ref='24392405' role='' />\n    <member type='way' ref='146193754' role='' />\n    <member type='way' ref='145296226' role='' />\n    <member type='way' ref='145296214' role='' />\n    <member type='way' ref='24327374' role='' />\n    <member type='way' ref='23636200' role='' />\n    <member type='way' ref='572558601' role='' />\n    <member type='way' ref='572558593' role='' />\n    <member type='way' ref='386805395' role='' />\n    <member type='way' ref='8176505' role='' />\n    <member type='way' ref='386805409' role='' />\n    <member type='way' ref='319793722' role='' />\n    <member type='way' ref='450080908' role='' />\n    <member type='way' ref='68019855' role='' />\n    <member type='way' ref='386805401' role='' />\n    <member type='way' ref='68019862' role='' />\n    <member type='way' ref='68019859' role='' />\n    <member type='way' ref='90400249' role='' />\n    <member type='way' ref='386805398' role='' />\n    <member type='way' ref='370522523' role='' />\n    <member type='way' ref='90400324' role='' />\n    <member type='way' ref='23679124' role='' />\n    <member type='way' ref='386805405' role='' />\n    <member type='way' ref='153767709' role='' />\n    <member type='way' ref='214712272' role='' />\n    <member type='way' ref='372830909' role='' />\n    <member type='way' ref='372830910' role='' />\n    <member type='way' ref='439909730' role='' />\n    <member type='way' ref='375226731' role='' />\n    <member type='way' ref='95914917' role='' />\n    <member type='way' ref='388070279' role='' />\n    <member type='way' ref='531226944' role='' />\n    <member type='way' ref='388070402' role='' />\n    <member type='way' ref='411108638' role='' />\n    <member type='way' ref='411108638' role='' />\n    <member type='way' ref='491895463' role='' />\n    <member type='way' ref='386808188' role='' />\n    <member type='way' ref='388070279' role='' />\n    <member type='way' ref='95914917' role='' />\n    <member type='way' ref='95914921' role='' />\n    <member type='way' ref='95914916' role='' />\n    <member type='way' ref='184497382' role='' />\n    <member type='way' ref='95915498' role='' />\n    <member type='way' ref='95969733' role='' />\n    <member type='way' ref='372687461' role='' />\n    <member type='way' ref='24764634' role='' />\n    <member type='way' ref='95969777' role='' />\n    <member type='way' ref='23635861' role='' />\n    <member type='way' ref='23635862' role='' />\n    <member type='way' ref='23983046' role='' />\n    <member type='way' ref='145296224' role='' />\n    <member type='way' ref='145296211' role='' />\n    <member type='way' ref='191646743' role='' />\n    <member type='way' ref='191646745' role='' />\n    <member type='way' ref='97144610' role='' />\n    <member type='way' ref='80234416' role='' />\n    <member type='way' ref='224447170' role='' />\n    <member type='way' ref='224447173' role='' />\n    <member type='way' ref='24928236' role='' />\n    <member type='way' ref='24928237' role='' />\n    <member type='way' ref='24544550' role='' />\n    <member type='way' ref='24544551' role='' />\n    <member type='way' ref='24741612' role='' />\n    <member type='way' ref='4248177' role='' />\n    <member type='way' ref='182725105' role='' />\n    <member type='way' ref='24935207' role='' />\n    <member type='way' ref='572541274' role='' />\n    <member type='way' ref='572541275' role='' />\n    <member type='way' ref='24935551' role='' />\n    <member type='way' ref='24935249' role='' />\n    <member type='way' ref='4248230' role='' />\n    <member type='way' ref='277557870' role='' />\n    <member type='way' ref='61411041' role='' />\n    <member type='way' ref='24496384' role='' />\n    <member type='way' ref='25303191' role='' />\n    <member type='way' ref='329858065' role='' />\n    <member type='way' ref='278784724' role='' />\n    <member type='way' ref='278784729' role='' />\n    <member type='way' ref='278784727' role='' />\n    <member type='way' ref='278784730' role='' />\n    <member type='way' ref='28164970' role='' />\n    <member type='way' ref='61411039' role='' />\n    <member type='way' ref='26291277' role='' />\n    <member type='way' ref='8133701' role='' />\n    <member type='way' ref='59195864' role='' />\n    <member type='way' ref='66245186' role='' />\n    <member type='way' ref='59195865' role='' />\n    <member type='way' ref='101784056' role='' />\n    <member type='way' ref='101784053' role='' />\n    <member type='way' ref='4066921' role='' />\n    <member type='way' ref='297960129' role='' />\n    <member type='way' ref='297960128' role='' />\n    <member type='way' ref='71954443' role='' />\n    <member type='way' ref='30104745' role='' />\n    <member type='way' ref='228796379' role='' />\n    <member type='way' ref='89353017' role='' />\n    <member type='way' ref='262098090' role='' />\n    <member type='way' ref='262098087' role='' />\n    <member type='way' ref='292341778' role='' />\n    <member type='way' ref='32338890' role='' />\n    <member type='way' ref='262098089' role='' />\n    <member type='way' ref='88573407' role='' />\n    <member type='way' ref='188751900' role='' />\n    <member type='way' ref='145991345' role='' />\n    <member type='way' ref='31784369' role='' />\n    <member type='way' ref='228796373' role='' />\n    <member type='way' ref='47379982' role='' />\n    <member type='way' ref='356858716' role='' />\n    <member type='way' ref='59102606' role='' />\n    <member type='way' ref='388070400' role='' />\n    <member type='way' ref='397437373' role='' />\n    <member type='way' ref='386817387' role='' />\n    <member type='way' ref='334753882' role='' />\n    <member type='way' ref='334753878' role='' />\n    <member type='way' ref='410957045' role='' />\n    <member type='way' ref='410957042' role='' />\n    <member type='way' ref='334753858' role='' />\n    <member type='way' ref='356858717' role='' />\n    <member type='way' ref='356858731' role='' />\n    <member type='way' ref='356858719' role='' />\n    <member type='way' ref='356858730' role='' />\n    <member type='way' ref='59102613' role='' />\n    <member type='way' ref='397437373' role='' />\n    <member type='way' ref='386817387' role='' />\n    <member type='way' ref='59102614' role='' />\n    <member type='way' ref='410957046' role='' />\n    <member type='way' ref='59102602' role='' />\n    <member type='way' ref='386817385' role='' />\n    <member type='way' ref='23948285' role='' />\n    <member type='way' ref='342571575' role='' />\n    <member type='way' ref='71408052' role='' />\n    <member type='way' ref='245122603' role='' />\n    <member type='way' ref='31051285' role='' />\n    <member type='way' ref='31051286' role='' />\n    <member type='way' ref='246308600' role='' />\n    <member type='way' ref='246384284' role='' />\n    <member type='way' ref='148825033' role='' />\n    <member type='way' ref='457461058' role='' />\n    <member type='way' ref='236141153' role='' />\n    <member type='way' ref='149398555' role='' />\n    <member type='way' ref='457461060' role='' />\n    <member type='way' ref='56082366' role='' />\n    <member type='way' ref='86960806' role='' />\n    <member type='way' ref='56082367' role='' />\n    <member type='way' ref='86960838' role='' />\n    <member type='way' ref='55849842' role='' />\n    <member type='way' ref='31056241' role='' />\n    <member type='way' ref='46739877' role='' />\n    <member type='way' ref='229026962' role='' />\n    <member type='way' ref='25731772' role='' />\n    <member type='way' ref='25731773' role='' />\n    <member type='way' ref='25731762' role='' />\n    <member type='way' ref='25731765' role='' />\n    <member type='way' ref='25731766' role='' />\n    <member type='way' ref='386817381' role='' />\n    <member type='way' ref='23947676' role='' />\n    <member type='way' ref='4248350' role='' />\n    <member type='way' ref='37995156' role='' />\n    <member type='way' ref='37995157' role='' />\n    <member type='way' ref='29265047' role='' />\n    <member type='way' ref='47361639' role='' />\n    <member type='way' ref='29265046' role='' />\n    <member type='way' ref='432736168' role='' />\n    <member type='way' ref='47361640' role='' />\n    <member type='way' ref='432736169' role='' />\n    <member type='way' ref='20581096' role='' />\n    <member type='way' ref='227763717' role='' />\n    <member type='way' ref='227763721' role='' />\n    <member type='way' ref='4248393' role='' />\n    <member type='way' ref='23842884' role='' />\n    <member type='way' ref='23842885' role='' />\n    <member type='way' ref='37502550' role='' />\n    <member type='way' ref='23846284' role='' />\n    <member type='way' ref='27523735' role='' />\n    <member type='way' ref='23712484' role='' />\n    <member type='way' ref='244502595' role='' />\n    <member type='way' ref='27777182' role='' />\n    <member type='way' ref='27777183' role='' />\n    <member type='way' ref='244502597' role='' />\n    <member type='way' ref='341187586' role='' />\n    <member type='way' ref='23318016' role='' />\n    <member type='way' ref='23318017' role='' />\n    <member type='way' ref='24823524' role='' />\n    <member type='way' ref='24823525' role='' />\n    <member type='way' ref='23318026' role='' />\n    <member type='way' ref='27356556' role='' />\n    <member type='way' ref='27647137' role='' />\n    <member type='way' ref='572528705' role='' />\n    <member type='way' ref='25417012' role='' />\n    <member type='way' ref='28113396' role='' />\n    <member type='way' ref='301830283' role='' />\n    <member type='way' ref='301830284' role='' />\n    <member type='way' ref='301830282' role='' />\n    <member type='way' ref='284982061' role='' />\n    <member type='way' ref='301850672' role='' />\n    <member type='way' ref='301850668' role='' />\n    <member type='way' ref='301850671' role='' />\n    <member type='way' ref='25031744' role='' />\n    <member type='way' ref='25031745' role='' />\n    <member type='way' ref='4453822' role='' />\n    <member type='way' ref='301890309' role='' />\n    <member type='way' ref='301850670' role='' />\n    <member type='way' ref='268727953' role='' />\n    <member type='way' ref='23401298' role='' />\n    <member type='way' ref='23401299' role='' />\n    <member type='way' ref='364258990' role='' />\n    <member type='way' ref='299536951' role='' />\n    <member type='way' ref='315077764' role='' />\n    <member type='way' ref='136531747' role='' />\n    <member type='way' ref='136531742' role='' />\n    <member type='way' ref='301850675' role='' />\n    <member type='way' ref='136531743' role='' />\n    <member type='way' ref='136773636' role='' />\n    <member type='way' ref='467651806' role='' />\n    <member type='way' ref='364258988' role='' />\n    <member type='way' ref='28480283' role='' />\n    <member type='way' ref='28480284' role='' />\n    <member type='way' ref='184362451' role='' />\n    <member type='way' ref='184362448' role='' />\n    <member type='way' ref='386655216' role='' />\n    <member type='way' ref='386820947' role='' />\n    <member type='way' ref='27647131' role='' />\n    <member type='way' ref='27647130' role='' />\n    <member type='way' ref='139659168' role='' />\n    <member type='way' ref='189255262' role='' />\n    <member type='way' ref='189255267' role='' />\n    <member type='way' ref='203023142' role='' />\n    <member type='way' ref='280989617' role='' />\n    <member type='way' ref='189255257' role='' />\n    <member type='way' ref='189255260' role='' />\n    <member type='way' ref='189255263' role='' />\n    <member type='way' ref='58711277' role='' />\n    <member type='way' ref='189255272' role='' />\n    <member type='way' ref='139669589' role='' />\n    <member type='way' ref='28302737' role='' />\n    <member type='way' ref='28302725' role='' />\n    <member type='way' ref='24161692' role='' />\n    <member type='way' ref='139667343' role='' />\n    <member type='way' ref='608281812' role='' />\n    <member type='way' ref='136913721' role='' />\n    <member type='way' ref='136913724' role='' />\n    <member type='way' ref='585774518' role='' />\n    <member type='way' ref='386834377' role='' />\n    <member type='way' ref='585774517' role='' />\n    <member type='way' ref='136913720' role='' />\n    <member type='way' ref='28552696' role='' />\n    <member type='way' ref='585774522' role='' />\n    <member type='way' ref='386834387' role='' />\n    <member type='way' ref='585774521' role='' />\n    <member type='way' ref='220465450' role='' />\n    <member type='way' ref='30032550' role='' />\n    <member type='way' ref='23618294' role='' />\n    <member type='way' ref='28552688' role='' />\n    <member type='way' ref='28552689' role='' />\n    <member type='way' ref='386834379' role='' />\n    <member type='way' ref='28545230' role='' />\n    <member type='way' ref='386834371' role='' />\n    <member type='way' ref='326226436' role='' />\n    <member type='way' ref='125768393' role='' />\n    <member type='way' ref='23423531' role='' />\n    <member type='way' ref='231390463' role='' />\n    <member type='way' ref='231390462' role='' />\n    <member type='way' ref='386834374' role='' />\n    <member type='way' ref='23423525' role='' />\n    <member type='way' ref='32090567' role='' />\n    <member type='way' ref='23423536' role='' />\n    <member type='way' ref='23101094' role='' />\n    <member type='way' ref='386834376' role='' />\n    <member type='way' ref='23618453' role='' />\n    <member type='way' ref='149178397' role='' />\n    <member type='way' ref='149178398' role='' />\n    <member type='way' ref='386834373' role='' />\n    <member type='way' ref='4518070' role='' />\n    <member type='way' ref='148505199' role='' />\n    <member type='way' ref='460408778' role='' />\n    <member type='way' ref='119253155' role='' />\n    <member type='way' ref='119253158' role='' />\n    <member type='way' ref='25046564' role='' />\n    <member type='way' ref='25046565' role='' />\n    <member type='way' ref='28289490' role='' />\n    <member type='way' ref='148899495' role='' />\n    <member type='way' ref='148899457' role='' />\n    <member type='way' ref='171277138' role='' />\n    <member type='way' ref='171277143' role='' />\n    <member type='way' ref='148899483' role='' />\n    <member type='way' ref='8123176' role='' />\n    <member type='way' ref='148500437' role='' />\n    <member type='way' ref='370255939' role='' />\n    <member type='way' ref='370255937' role='' />\n    <member type='way' ref='304383858' role='' />\n    <member type='way' ref='23283660' role='' />\n    <member type='way' ref='370256031' role='' />\n    <member type='way' ref='22771792' role='' />\n    <member type='way' ref='4517829' role='' />\n    <member type='way' ref='370256035' role='' />\n    <member type='way' ref='503035490' role='' />\n    <member type='way' ref='375989578' role='' />\n    <member type='way' ref='375989707' role='' />\n    <member type='way' ref='503035487' role='' />\n    <member type='way' ref='503035488' role='' />\n    <member type='way' ref='493061268' role='' />\n    <member type='way' ref='38069230' role='' />\n    <member type='way' ref='34434314' role='' />\n    <member type='way' ref='503035492' role='' />\n    <member type='way' ref='34434316' role='' />\n    <member type='way' ref='324884579' role='' />\n    <member type='way' ref='24470344' role='' />\n    <member type='way' ref='92162860' role='' />\n    <member type='way' ref='386834386' role='' />\n    <member type='way' ref='92162862' role='' />\n    <member type='way' ref='488272332' role='' />\n    <member type='way' ref='488272331' role='' />\n    <member type='way' ref='488272323' role='' />\n    <member type='way' ref='488272322' role='' />\n    <member type='way' ref='488272318' role='' />\n    <member type='way' ref='488272319' role='' />\n    <member type='way' ref='37706758' role='' />\n    <member type='way' ref='37706761' role='' />\n    <member type='way' ref='488272313' role='' />\n    <member type='way' ref='488272307' role='' />\n    <member type='way' ref='388083006' role='' />\n    <member type='way' ref='488272304' role='' />\n    <member type='way' ref='488272305' role='' />\n    <member type='way' ref='121198958' role='' />\n    <member type='way' ref='1698015' role='' />\n    <member type='way' ref='388313109' role='' />\n    <member type='way' ref='388070394' role='' />\n    <member type='way' ref='386834382' role='' />\n    <member type='way' ref='557874837' role='' />\n    <member type='way' ref='1880523' role='' />\n    <member type='way' ref='4373482' role='' />\n    <member type='way' ref='488272306' role='' />\n    <member type='way' ref='233833234' role='' />\n    <member type='way' ref='388083006' role='' />\n    <member type='way' ref='488272304' role='' />\n    <member type='way' ref='488272305' role='' />\n    <member type='way' ref='121198958' role='' />\n    <member type='way' ref='1698015' role='' />\n    <member type='way' ref='121198960' role='' />\n    <member type='way' ref='121198955' role='' />\n    <member type='way' ref='24418625' role='' />\n    <member type='way' ref='24418626' role='' />\n    <member type='way' ref='121198956' role='' />\n    <member type='way' ref='386834392' role='' />\n    <member type='way' ref='30791228' role='' />\n    <member type='way' ref='119719508' role='' />\n    <member type='way' ref='30791016' role='' />\n    <member type='way' ref='140293280' role='' />\n    <member type='way' ref='357265305' role='' />\n    <member type='way' ref='357265309' role='' />\n    <member type='way' ref='357265310' role='' />\n    <member type='way' ref='386834388' role='' />\n    <member type='way' ref='8147809' role='' />\n    <member type='way' ref='27224364' role='' />\n    <member type='way' ref='121257657' role='' />\n    <member type='way' ref='10631185' role='' />\n    <member type='way' ref='422789453' role='' />\n    <member type='way' ref='422789581' role='' />\n    <member type='way' ref='4327218' role='' />\n    <member type='way' ref='422789574' role='' />\n    <member type='way' ref='46244971' role='' />\n    <member type='way' ref='108849017' role='' />\n    <member type='way' ref='3676667' role='' />\n    <member type='way' ref='422789543' role='' />\n    <member type='way' ref='108849007' role='' />\n    <member type='way' ref='3542463' role='' />\n    <member type='way' ref='422789545' role='' />\n    <member type='way' ref='422789546' role='' />\n    <member type='way' ref='237108787' role='' />\n    <member type='way' ref='422789541' role='' />\n    <member type='way' ref='422789547' role='' />\n    <member type='way' ref='191619082' role='' />\n    <member type='way' ref='422789565' role='' />\n    <member type='way' ref='422789560' role='' />\n    <member type='way' ref='422789561' role='' />\n    <member type='way' ref='210643712' role='' />\n    <member type='way' ref='90714001' role='' />\n    <member type='way' ref='422789539' role='' />\n    <member type='way' ref='422789569' role='' />\n    <member type='way' ref='416688412' role='' />\n    <member type='way' ref='3542462' role='' />\n    <member type='way' ref='416688430' role='' />\n    <member type='way' ref='4892386' role='' />\n    <member type='way' ref='46156153' role='' />\n    <member type='way' ref='90583368' role='' />\n    <member type='way' ref='149443272' role='' />\n    <member type='way' ref='575128631' role='' />\n    <member type='way' ref='233878940' role='' />\n    <member type='way' ref='496703336' role='' />\n    <member type='way' ref='121284722' role='' />\n    <tag k='from' v='Oslo' />\n    <tag k='from:da' v='Oslo' />\n    <tag k='from:en' v='Oslo' />\n    <tag k='from:nb' v='Oslo' />\n    <tag k='importance' v='national' />\n    <tag k='name' v='Buss 820: Oslo - København' />\n    <tag k='name:da' v='Bus 820: Oslo - København' />\n    <tag k='name:en' v='Bus 820: Oslo - Copenhagen' />\n    <tag k='name:nb' v='Buss 820: Oslo - København' />\n    <tag k='network' v='Swebus' />\n    <tag k='operator' v='Swebus' />\n    <tag k='public_transport:version' v='2' />\n    <tag k='ref' v='820' />\n    <tag k='route' v='bus' />\n    <tag k='to' v='København' />\n    <tag k='to:da' v='København' />\n    <tag k='to:en' v='Copenhagen' />\n    <tag k='to:nb' v='København' />\n    <tag k='type' v='route' />\n  </relation>\n  <relation id='7084215' timestamp='2018-03-24T08:19:48Z' uid='5853411' user='j1scad' version='3' changeset='57477338'>\n    <member type='way' ref='304383859' role='forward' />\n    <member type='way' ref='370255938' role='forward' />\n    <member type='way' ref='370255940' role='forward' />\n    <member type='way' ref='148500436' role='forward' />\n    <member type='way' ref='219657906' role='forward' />\n    <member type='way' ref='148899471' role='forward' />\n    <member type='way' ref='148899453' role='forward' />\n    <member type='way' ref='219657860' role='forward' />\n    <member type='way' ref='148899506' role='forward' />\n    <member type='way' ref='219657822' role='forward' />\n    <member type='way' ref='28289483' role='forward' />\n    <member type='way' ref='28289485' role='forward' />\n    <member type='way' ref='25046566' role='forward' />\n    <member type='way' ref='25046567' role='forward' />\n    <member type='way' ref='119253153' role='forward' />\n    <member type='way' ref='119253150' role='forward' />\n    <member type='way' ref='388070386' role='forward' />\n    <member type='way' ref='15729157' role='forward' />\n    <member type='way' ref='107382808' role='forward' />\n    <member type='way' ref='107382797' role='forward' />\n    <member type='way' ref='28289473' role='forward' />\n    <member type='way' ref='28289477' role='forward' />\n    <member type='way' ref='326419705' role='forward' />\n    <member type='way' ref='301821089' role='forward' />\n    <member type='way' ref='28166520' role='forward' />\n    <member type='way' ref='301821087' role='forward' />\n    <member type='way' ref='28841846' role='forward' />\n    <member type='way' ref='28841847' role='forward' />\n    <member type='way' ref='211687634' role='forward' />\n    <member type='way' ref='211687633' role='forward' />\n    <member type='way' ref='301822759' role='forward' />\n    <member type='way' ref='301822762' role='forward' />\n    <member type='way' ref='28288769' role='forward' />\n    <member type='way' ref='28288770' role='forward' />\n    <member type='way' ref='28945196' role='forward' />\n    <member type='way' ref='28945197' role='forward' />\n    <member type='way' ref='28612815' role='forward' />\n    <member type='way' ref='28612816' role='forward' />\n    <member type='way' ref='28287284' role='forward' />\n    <member type='way' ref='28287285' role='forward' />\n    <member type='way' ref='28287281' role='forward' />\n    <member type='way' ref='301827524' role='forward' />\n    <member type='way' ref='28287275' role='forward' />\n    <member type='way' ref='136325302' role='forward' />\n    <member type='way' ref='136325301' role='forward' />\n    <member type='way' ref='301827523' role='forward' />\n    <member type='way' ref='136325311' role='forward' />\n    <member type='way' ref='136325313' role='forward' />\n    <member type='way' ref='301827522' role='forward' />\n    <member type='way' ref='28442171' role='forward' />\n    <member type='way' ref='301830280' role='forward' />\n    <member type='way' ref='28165774' role='forward' />\n    <member type='way' ref='284982064' role='forward' />\n    <member type='way' ref='27647139' role='forward' />\n    <member type='way' ref='572528704' role='forward' />\n    <member type='way' ref='27647140' role='forward' />\n    <member type='way' ref='23318024' role='forward' />\n    <member type='way' ref='23318025' role='forward' />\n    <member type='way' ref='24823526' role='forward' />\n    <member type='way' ref='24823527' role='forward' />\n    <member type='way' ref='23318014' role='forward' />\n    <member type='way' ref='23318015' role='forward' />\n    <member type='way' ref='341187587' role='forward' />\n    <member type='way' ref='244502594' role='forward' />\n    <member type='way' ref='27777184' role='forward' />\n    <member type='way' ref='27777185' role='forward' />\n    <member type='way' ref='244502596' role='forward' />\n    <member type='way' ref='27523733' role='forward' />\n    <member type='way' ref='24524471' role='forward' />\n    <member type='way' ref='37502548' role='forward' />\n    <member type='way' ref='37502549' role='forward' />\n    <member type='way' ref='23842882' role='forward' />\n    <member type='way' ref='20581098' role='forward' />\n    <member type='way' ref='227763719' role='forward' />\n    <member type='way' ref='227763718' role='forward' />\n    <member type='way' ref='227763720' role='forward' />\n    <member type='way' ref='432736170' role='forward' />\n    <member type='way' ref='8136880' role='forward' />\n    <member type='way' ref='241985604' role='forward' />\n    <member type='way' ref='43444062' role='forward' />\n    <member type='way' ref='241985603' role='forward' />\n    <member type='way' ref='37995158' role='forward' />\n    <member type='way' ref='37995159' role='forward' />\n    <member type='way' ref='23947678' role='forward' />\n    <member type='way' ref='388070286' role='forward' />\n    <member type='way' ref='23947679' role='forward' />\n    <member type='way' ref='191655598' role='forward' />\n    <member type='way' ref='182731881' role='forward' />\n    <member type='way' ref='182731882' role='forward' />\n    <member type='way' ref='191655601' role='forward' />\n    <member type='way' ref='28165162' role='forward' />\n    <member type='way' ref='25303194' role='forward' />\n    <member type='way' ref='191655600' role='forward' />\n    <member type='way' ref='25724534' role='forward' />\n    <member type='way' ref='278784725' role='forward' />\n    <member type='way' ref='278784728' role='forward' />\n    <member type='way' ref='278784726' role='forward' />\n    <member type='way' ref='278784731' role='forward' />\n    <member type='way' ref='329830321' role='forward' />\n    <member type='way' ref='329830320' role='forward' />\n    <member type='way' ref='24496386' role='forward' />\n    <member type='way' ref='24496387' role='forward' />\n    <member type='way' ref='61411040' role='forward' />\n    <member type='way' ref='38443331' role='forward' />\n    <member type='way' ref='38443341' role='forward' />\n    <member type='way' ref='24935251' role='forward' />\n    <member type='way' ref='24935252' role='forward' />\n    <member type='way' ref='370255937' role='forward' />\n    <member type='way' ref='370255939' role='forward' />\n    <member type='way' ref='148500437' role='forward' />\n    <member type='way' ref='8123176' role='forward' />\n    <member type='way' ref='148899483' role='forward' />\n    <member type='way' ref='171277143' role='forward' />\n    <member type='way' ref='171277138' role='forward' />\n    <member type='way' ref='148899457' role='forward' />\n    <member type='way' ref='148899495' role='forward' />\n    <member type='way' ref='28289490' role='forward' />\n    <member type='way' ref='25046565' role='forward' />\n    <member type='way' ref='25046564' role='forward' />\n    <member type='way' ref='119253158' role='forward' />\n    <member type='way' ref='119253155' role='forward' />\n    <member type='way' ref='119253143' role='forward' />\n    <member type='way' ref='15729154' role='forward' />\n    <member type='way' ref='15729153' role='forward' />\n    <member type='way' ref='107382830' role='forward' />\n    <member type='way' ref='28289471' role='forward' />\n    <member type='way' ref='28289474' role='forward' />\n    <member type='way' ref='326419704' role='forward' />\n    <member type='way' ref='301821086' role='forward' />\n    <member type='way' ref='28165804' role='forward' />\n    <member type='way' ref='301821083' role='forward' />\n    <member type='way' ref='301821084' role='forward' />\n    <member type='way' ref='27405024' role='forward' />\n    <member type='way' ref='28841848' role='forward' />\n    <member type='way' ref='211687636' role='forward' />\n    <member type='way' ref='211687635' role='forward' />\n    <member type='way' ref='301822761' role='forward' />\n    <member type='way' ref='301822760' role='forward' />\n    <member type='way' ref='28288774' role='forward' />\n    <member type='way' ref='28288772' role='forward' />\n    <member type='way' ref='28945195' role='forward' />\n    <member type='way' ref='28945194' role='forward' />\n    <member type='way' ref='28612819' role='forward' />\n    <member type='way' ref='28612818' role='forward' />\n    <member type='way' ref='28287287' role='forward' />\n    <member type='way' ref='28287286' role='forward' />\n    <member type='way' ref='28287280' role='forward' />\n    <member type='way' ref='28287277' role='forward' />\n    <member type='way' ref='136325303' role='forward' />\n    <member type='way' ref='136325306' role='forward' />\n    <member type='way' ref='301830278' role='forward' />\n    <member type='way' ref='136325307' role='forward' />\n    <member type='way' ref='136325308' role='forward' />\n    <member type='way' ref='301830281' role='forward' />\n    <member type='way' ref='28442173' role='forward' />\n    <member type='way' ref='4248455' role='forward' />\n    <member type='way' ref='284982063' role='forward' />\n    <member type='way' ref='25417012' role='forward' />\n    <member type='way' ref='572528705' role='forward' />\n    <member type='way' ref='27647137' role='forward' />\n    <member type='way' ref='27356556' role='forward' />\n    <member type='way' ref='23318026' role='forward' />\n    <member type='way' ref='24823525' role='forward' />\n    <member type='way' ref='24823524' role='forward' />\n    <member type='way' ref='23318017' role='forward' />\n    <member type='way' ref='23318016' role='forward' />\n    <member type='way' ref='341187586' role='forward' />\n    <member type='way' ref='244502597' role='forward' />\n    <member type='way' ref='27777183' role='forward' />\n    <member type='way' ref='27777182' role='forward' />\n    <member type='way' ref='244502595' role='forward' />\n    <member type='way' ref='23712484' role='forward' />\n    <member type='way' ref='27523735' role='forward' />\n    <member type='way' ref='23846284' role='forward' />\n    <member type='way' ref='37502550' role='forward' />\n    <member type='way' ref='23842885' role='forward' />\n    <member type='way' ref='23842884' role='forward' />\n    <member type='way' ref='4248393' role='forward' />\n    <member type='way' ref='227763721' role='forward' />\n    <member type='way' ref='227763717' role='forward' />\n    <member type='way' ref='20581096' role='forward' />\n    <member type='way' ref='432736169' role='forward' />\n    <member type='way' ref='47361640' role='forward' />\n    <member type='way' ref='432736168' role='forward' />\n    <member type='way' ref='29265046' role='forward' />\n    <member type='way' ref='47361639' role='forward' />\n    <member type='way' ref='29265047' role='forward' />\n    <member type='way' ref='37995157' role='forward' />\n    <member type='way' ref='37995156' role='forward' />\n    <member type='way' ref='4248350' role='forward' />\n    <member type='way' ref='23947676' role='forward' />\n    <member type='way' ref='386817381' role='forward' />\n    <member type='way' ref='191655599' role='forward' />\n    <member type='way' ref='182725095' role='forward' />\n    <member type='way' ref='191655597' role='forward' />\n    <member type='way' ref='8136878' role='forward' />\n    <member type='way' ref='28165144' role='forward' />\n    <member type='way' ref='61411034' role='forward' />\n    <member type='way' ref='278784730' role='forward' />\n    <member type='way' ref='278784727' role='forward' />\n    <member type='way' ref='278784729' role='forward' />\n    <member type='way' ref='278784724' role='forward' />\n    <member type='way' ref='329858065' role='forward' />\n    <member type='way' ref='25303191' role='forward' />\n    <member type='way' ref='24496384' role='forward' />\n    <member type='way' ref='61411041' role='forward' />\n    <member type='way' ref='277557870' role='forward' />\n    <member type='way' ref='4248230' role='forward' />\n    <member type='way' ref='24935249' role='forward' />\n    <member type='way' ref='24935551' role='forward' />\n    <tag k='name' v='E 20 Skåne län' />\n    <tag k='network' v='e-road' />\n    <tag k='ref' v='E 20' />\n    <tag k='route' v='road' />\n    <tag k='section' v='Skåne län' />\n    <tag k='type' v='route' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/bidirectionalRing.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n317579533000000 && 7.1760807,-8.1106785:7.176148,-8.1104474:7.1763131,-8.1105182:7.1762153,-8.1107679:7.1760807,-8.1106785 && last_edit_user_name -> KGeisler || last_edit_changeset -> 27516438 || last_edit_time -> 1418764039000 || last_edit_user_id -> 2242339 || iso_country_code -> CIV || highway -> path || last_edit_version -> 1\n# Points\n3238599653000000 && 7.1760807,-8.1106785 && last_edit_user_name -> KGeisler || last_edit_changeset -> 27516438 || last_edit_time -> 1418764038000 || last_edit_user_id -> 2242339 || iso_country_code -> CIV || last_edit_version -> 1\n3238599654000000 && 7.176148,-8.1104474 && last_edit_user_name -> KGeisler || last_edit_changeset -> 27516438 || last_edit_time -> 1418764038000 || last_edit_user_id -> 2242339 || iso_country_code -> CIV || last_edit_version -> 1\n3238599655000000 && 7.1762153,-8.1107679 && last_edit_user_name -> KGeisler || last_edit_changeset -> 27516438 || last_edit_time -> 1418764038000 || last_edit_user_id -> 2242339 || iso_country_code -> CIV || last_edit_version -> 1\n3238599656000000 && 7.1763131,-8.1105182 && last_edit_user_name -> KGeisler || last_edit_changeset -> 27516438 || last_edit_time -> 1418764038000 || last_edit_user_id -> 2242339 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/lineWithBarrier.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n460419996000000 && 7.4125734,-7.5718219:7.4133357,-7.5718406:7.4133995,-7.5718191:7.4135218,-7.5717253:7.4135804,-7.5717065:7.4137548,-7.5717122:7.4144075,-7.5717337:7.4145614,-7.5717365 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503315 || access -> private || surface -> asphalt || last_edit_time -> 1482104309000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || highway -> service || last_edit_version -> 2\n# Points\n4560902555000000 && 7.4145614,-7.5717365 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44553998 || last_edit_time -> 1482277549000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n4560902556000000 && 7.4135804,-7.5717065 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103964000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902557000000 && 7.4135218,-7.5717253 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103964000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902558000000 && 7.4133995,-7.5718191 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103964000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902559000000 && 7.4133357,-7.5718406 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103964000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902615000000 && 7.4125734,-7.5718219 && motorcycle -> yes || last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || access -> private || barrier -> gate || bicycle -> yes || last_edit_time -> 1482103960000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || motorcar -> yes || last_edit_version -> 1 || foot -> yes\n4560902695000000 && 7.4137548,-7.5717122 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902697000000 && 7.4144075,-7.5717337 && motorcycle -> yes || last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || access -> permissive || barrier -> gate || bicycle -> yes || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || motorcar -> yes || last_edit_version -> 1 || foot -> yes\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/lineWithInvalidOverlappingGeometry.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n858888719000000 && 14.9039776,49.992244:14.9046362,49.9925557:14.9051264,49.9927792:14.9054263,49.9929567:14.9058701,49.9930323 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || highway -> living_street || last_edit_version -> 1\n858888726000000 && 14.9056073,49.9933986:14.9053719,49.9933775:14.9054263,49.9929567:14.9053719,49.9933775:14.9056073,49.9933986:14.9053719,49.9933775:14.9056073,49.9933986 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || surface -> ground || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || highway -> living_street || last_edit_version -> 1\n# Points\n8006501246000000 && 14.9039776,49.992244 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n5540849673000000 && 14.9040683,49.9924772 && last_edit_user_name -> حبيشان || last_edit_changeset -> 73438683 || last_edit_time -> 1566060322000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || amenity -> place_of_worship || name -> مسجد الفتح || last_edit_version -> 2 || building -> mosque || religion -> muslim\n8006501224000000 && 14.9042206,49.9927927 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n1301207571000000 && 14.9044997,49.9928754 && last_edit_user_name -> حبيشان || last_edit_changeset -> 87219479 || last_edit_time -> 1593267072000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 2\n8006501226000000 && 14.9046362,49.9925557 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n1301206445000000 && 14.9047924,49.9921897 && last_edit_user_name -> حبيشان || last_edit_changeset -> 87219479 || last_edit_time -> 1593267072000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 3\n8006501234000000 && 14.9049684,49.9931234 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n8006501238000000 && 14.9051264,49.9927792 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n8006501218000000 && 14.9053719,49.9933775 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n8006501219000000 && 14.9054263,49.9929567 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n1301203138000000 && 14.905509,49.9925236 && last_edit_user_name -> brogo || last_edit_changeset -> 23380038 || last_edit_time -> 1404154677000 || last_edit_user_id -> 63107 || iso_country_code -> YEM || last_edit_version -> 2\n8006501230000000 && 14.9056073,49.9933986 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n1301204598000000 && 14.9056374,49.9922284 && last_edit_user_name -> Nautic || last_edit_changeset -> 8257313 || last_edit_time -> 1306443298000 || last_edit_user_id -> 449569 || iso_country_code -> YEM || last_edit_version -> 1\n7575126277000000 && 14.9056697,49.9921604 && last_edit_user_name -> حبيشان || last_edit_changeset -> 86008411 || last_edit_time -> 1590955714000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n8006501235000000 && 14.9058701,49.9930323 && last_edit_user_name -> حبيشان || last_edit_changeset -> 92471276 || last_edit_time -> 1602676511000 || last_edit_user_id -> 1147414 || iso_country_code -> YEM || last_edit_version -> 1\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/lineWithLessThanTwoNodesDueToRepeatedLocationAtEndOfLine.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n488453376000000 && 22.539148,113.9448557:22.539148,113.9448557:22.5392348,113.9448542:22.539495,113.9448495:22.5395425,113.9448487:22.5395455,113.9451532:22.5395009,113.9451538:22.5392333,113.9451574:22.5391521,113.9451585:22.5391521,113.9451585 && last_edit_user_name -> ulrichm || last_edit_changeset -> 58630764 || last_edit_time -> 1525301434000 || last_edit_user_id -> 3331814 || iso_country_code -> CHN || footway -> crossing || highway -> footway || last_edit_version -> 4 || crossing -> zebra\n# Points\n5178935335000000 && 22.539148,113.9448557 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n5178935343000000 && 22.5392348,113.9448542 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4807503663000000 && 22.539495,113.9448495 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533153000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 2\n5178935349000000 && 22.5395425,113.9448487 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n5178935333000000 && 22.5395455,113.9451532 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4808413267000000 && 22.5395009,113.9451538 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533154000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 3\n5178935332000000 && 22.5392333,113.9451574 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4807503687000000 && 22.5391521,113.9451585 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533153000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 3\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/lineWithLoopAtEnd.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n461101743000000 && 7.4043546,-7.555948:7.4039597,-7.5565101:7.4039344,-7.5566201:7.4038985,-7.5566912:7.4038666,-7.5567233:7.4038267,-7.5567368:7.4037815,-7.5567287:7.4037576,-7.5567059:7.4037522,-7.5566684:7.4037642,-7.5566308:7.4037895,-7.5565892:7.4038374,-7.5565503:7.4039597,-7.5565101 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || access -> private || last_edit_time -> 1482395843000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || service -> driveway || highway -> service || last_edit_version -> 1\n# Points\n4566499434000000 && 7.4038374,-7.5565503 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499435000000 && 7.4037895,-7.5565892 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499610000000 && 7.4037642,-7.5566308 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499611000000 && 7.4037522,-7.5566684 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499612000000 && 7.4037576,-7.5567059 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499613000000 && 7.4037815,-7.5567287 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499614000000 && 7.4038267,-7.5567368 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499615000000 && 7.4038666,-7.5567233 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499616000000 && 7.4038985,-7.5566912 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499617000000 && 7.4039344,-7.5566201 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499618000000 && 7.4039597,-7.5565101 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4566499619000000 && 7.4043546,-7.555948 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44585235 || last_edit_time -> 1482395836000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/lineWithLoopAtStart.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n460419987000000 && 7.4132419,-7.5711621:7.4132362,-7.5713217:7.4130964,-7.5713167:7.4131021,-7.571157:7.4132419,-7.5711621:7.4135471,-7.571173:7.413606,-7.5711972:7.4136352,-7.5712535:7.4137548,-7.5717122 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503315 || access -> private || surface -> asphalt || last_edit_time -> 1482104309000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || highway -> service || last_edit_version -> 2 || oneway -> no\n# Points\n4560902689000000 && 7.4132419,-7.5711621 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902690000000 && 7.4132362,-7.5713217 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902691000000 && 7.4130964,-7.5713167 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902692000000 && 7.4131021,-7.571157 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902693000000 && 7.4135471,-7.571173 && motorcycle -> yes || last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || access -> private || barrier -> gate || bicycle -> yes || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || motorcar -> yes || last_edit_version -> 1 || foot -> yes\n4560902694000000 && 7.413606,-7.5711972 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902695000000 && 7.4137548,-7.5717122 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902696000000 && 7.4136352,-7.5712535 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/lineWithLoopInMiddle.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n460419987000000 && 7.4132788,-7.5708163:7.4132419,-7.5711621:7.4132362,-7.5713217:7.4130964,-7.5713167:7.4131021,-7.571157:7.4132419,-7.5711621:7.4135471,-7.571173:7.413606,-7.5711972:7.4136352,-7.5712535:7.4137548,-7.5717122 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503315 || access -> private || surface -> asphalt || last_edit_time -> 1482104309000 || last_edit_user_id -> 192829 || highway -> service || last_edit_version -> 2 || oneway -> no\n# Points\n4560902689000000 && 7.4132419,-7.5711621 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || last_edit_version -> 1\n4560902695000000 && 7.4137548,-7.5717122 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || last_edit_version -> 1\n4560902696000000 && 7.4136352,-7.5712535 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || last_edit_version -> 1\n4560902694000000 && 7.413606,-7.5711972 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || last_edit_version -> 1\n4560902692000000 && 7.4131021,-7.571157 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || last_edit_version -> 1\n4560902691000000 && 7.4130964,-7.5713167 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || last_edit_version -> 1\n4560902690000000 && 7.4132362,-7.5713217 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || last_edit_version -> 1\n4560902612000000 && 7.4132788,-7.5708163 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || last_edit_version -> 1\n4560902693000000 && 7.4135471,-7.571173 && motorcycle -> yes || last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || access -> private || barrier -> gate || bicycle -> yes || last_edit_time -> 1482103959000 || last_edit_user_id -> 192829 || motorcar -> yes || last_edit_version -> 1 || foot -> yes\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/lineWithRepeatedLocation.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n488453376000000 && 22.539148,113.9448557:22.539148,113.9448557:22.5392348,113.9448542:22.539495,113.9448495:22.5395425,113.9448487:22.5395455,113.9451532:22.5395009,113.9451538:22.5392333,113.9451574:22.5391521,113.9451585:22.539148,113.9448557 && last_edit_user_name -> ulrichm || last_edit_changeset -> 58630764 || last_edit_time -> 1525301434000 || last_edit_user_id -> 3331814 || iso_country_code -> CHN || footway -> crossing || highway -> footway || last_edit_version -> 4 || crossing -> zebra\n# Points\n5178935335000000 && 22.539148,113.9448557 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n5178935343000000 && 22.5392348,113.9448542 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4807503663000000 && 22.539495,113.9448495 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533153000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 2\n5178935349000000 && 22.5395425,113.9448487 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n5178935333000000 && 22.5395455,113.9451532 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4808413267000000 && 22.5395009,113.9451538 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533154000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 3\n5178935332000000 && 22.5392333,113.9451574 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4807503687000000 && 22.5391521,113.9451585 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533153000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 3\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/loopWithRepeatedLocation.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n488453376000000 && 22.539148,113.9448557:22.539148,113.9448557:22.5392348,113.9448542:22.539495,113.9448495:22.5395425,113.9448487:22.5395455,113.9451532:22.5395009,113.9451538:22.5392333,113.9451574:22.5391521,113.9451585:22.539148,113.9448557 && last_edit_user_name -> ulrichm || last_edit_changeset -> 58630764 || last_edit_time -> 1525301434000 || last_edit_user_id -> 3331814 || iso_country_code -> CHN || footway -> crossing || highway -> footway || last_edit_version -> 4 || crossing -> zebra\n386313688000000 && 22.5392333,113.9451574:22.5392462,113.9471391 && last_edit_user_name -> ulrichm || last_edit_changeset -> 58630764 || last_edit_time -> 1525301434000 || last_edit_user_id -> 3331814 || iso_country_code -> CHN || footway -> crossing || highway -> footway || last_edit_version -> 4\n# Points\n5178935335000000 && 22.539148,113.9448557 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n5178935343000000 && 22.5392348,113.9448542 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4807503663000000 && 22.539495,113.9448495 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533153000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 2\n5178935349000000 && 22.5395425,113.9448487 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n5178935333000000 && 22.5395455,113.9451532 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4808413267000000 && 22.5395009,113.9451538 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533154000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 3\n5178935332000000 && 22.5392333,113.9451574 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533148000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 1\n4807503687000000 && 22.5391521,113.9451585 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533153000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 3\n3896278676000000 && 22.5392462,113.9471391 && last_edit_user_name -> Triomphe346 || last_edit_changeset -> 53112141 || last_edit_time -> 1508533156000 || last_edit_user_id -> 4892967 || iso_country_code -> CHN || last_edit_version -> 2\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/loopingWayWithIntersection.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n310540517000000 && 7.7254788,-8.4052646:7.7255426,-8.405234:7.7256581,-8.4051493:7.7257196,-8.4051245:7.7258409,-8.4050727:7.7259463,-8.4050246:7.7259522,-8.4050219:7.7261225,-8.405021:7.7261408,-8.4050372:7.7261934,-8.4050836:7.7262715,-8.4050809:7.7263052,-8.4052107:7.7262661,-8.4052734:7.7262449,-8.4053566:7.7262998,-8.4053808:7.7261384,-8.4053924:7.7260355,-8.4053736:7.7259664,-8.4053181:7.7259557,-8.4052859:7.7259025,-8.4052582:7.7259362,-8.4051928:7.7259788,-8.4051024:7.7259862,-8.4050973:7.7259463,-8.4050246 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779023000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || highway -> path || last_edit_version -> 1\n310540519000000 && 7.7262662,-8.4049719:7.7262005,-8.4049806:7.7261563,-8.4050048:7.7261408,-8.4050372 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26475322 || last_edit_time -> 1414815655000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || highway -> path || last_edit_version -> 2\n# Points\n3159679692000000 && 7.7254788,-8.4052646 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679693000000 && 7.7255426,-8.405234 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679694000000 && 7.7256581,-8.4051493 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679695000000 && 7.7257196,-8.4051245 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679696000000 && 7.7258409,-8.4050727 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679697000000 && 7.7259522,-8.4050219 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679698000000 && 7.7261225,-8.405021 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679699000000 && 7.7261934,-8.4050836 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679700000000 && 7.7262715,-8.4050809 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679701000000 && 7.7263052,-8.4052107 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679702000000 && 7.7262661,-8.4052734 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679703000000 && 7.7262449,-8.4053566 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679704000000 && 7.7262998,-8.4053808 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679705000000 && 7.7261384,-8.4053924 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679706000000 && 7.7260355,-8.4053736 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679707000000 && 7.7259664,-8.4053181 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679708000000 && 7.7259557,-8.4052859 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679709000000 && 7.7259025,-8.4052582 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679710000000 && 7.7259362,-8.4051928 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679711000000 && 7.7259788,-8.4051024 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679712000000 && 7.7259862,-8.4050973 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679713000000 && 7.7259463,-8.4050246 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679741000000 && 7.7261408,-8.4050372 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679738000000 && 7.7262662,-8.4049719 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679739000000 && 7.7262005,-8.4049806 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n3159679740000000 && 7.7261563,-8.4050048 && last_edit_user_name -> rjeeves || last_edit_changeset -> 26467735 || last_edit_time -> 1414779021000 || last_edit_user_id -> 2363746 || iso_country_code -> GIN || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/malformedPolyLine.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n100000000 && -2.5184858,32.4625827:-2.5184958,32.4625727:-2.5184978,32.4625686:-2.5185019,32.4625707:-2.5185019,32.4625707:-2.5185039,32.4625666:-2.5185039,32.4625666:-2.5184978,32.4625666:-2.5184978,32.4625666:-2.5185039,32.4625666:-2.5185039,32.4625666:-2.5185039,32.4625666:-2.5184998,32.4625666:-2.5184978,32.4625686:-2.5185039,32.4625666 && last_edit_user_name -> 1 || last_edit_changeset -> 0 || last_edit_time -> 1530212492000 || last_edit_user_id -> 1 || iso_country_code -> TZA || highway -> road || last_edit_version -> 1\n# Points\n1000000 && -2.5184858,32.4625827 && last_edit_user_name -> 1 || last_edit_changeset -> 0 || last_edit_time -> 1515321311000 || last_edit_user_id -> 1 || iso_country_code -> TZA || last_edit_version -> 1\n2000000 && -2.5184958,32.4625727 && last_edit_user_name -> 1 || last_edit_changeset -> 0 || last_edit_time -> 1515321311000 || last_edit_user_id -> 1 || iso_country_code -> TZA || last_edit_version -> 1\n3000000 && -2.5184978,32.4625686 && last_edit_user_name -> 1 || last_edit_changeset -> 0 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1515321311000 || last_edit_user_id -> 1 || iso_country_code -> TZA || last_edit_version -> 1\n4000000 && -2.5185019,32.4625707 && last_edit_user_name -> 1 || last_edit_changeset -> 0 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1515321311000 || last_edit_user_id -> 1 || iso_country_code -> TZA || last_edit_version -> 1\n6000000 && -2.5185039,32.4625666 && last_edit_user_name -> 1 || last_edit_changeset -> 0 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1515321311000 || last_edit_user_id -> 1 || iso_country_code -> TZA || last_edit_version -> 1\n8000000 && -2.5184978,32.4625666 && last_edit_user_name -> 1 || last_edit_changeset -> 0 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1515321311000 || last_edit_user_id -> 1 || iso_country_code -> TZA || last_edit_version -> 1\n13000000 && -2.5184998,32.4625666 && last_edit_user_name -> 1 || last_edit_changeset -> 0 || last_edit_time -> 1515321311000 || last_edit_user_id -> 1 || iso_country_code -> TZA || last_edit_version -> 1\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/malformedPolyLineBoundaryMap.txt",
    "content": "TZA||POLYGON ((30.933931 -1.007137, 33.933931 -1.007137, 33.933931 -6.007137, 30.933931 -6.007137, 30.933931 -1.007137))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/nestedRelationRemoval.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n450151832000000 && -23.567877,-46.692725:-23.5677831,-46.6928133 && last_edit_changeset -> 52667882 || surface -> asphalt || iso_country_code -> BRA || maxspeed -> 50 || oneway -> yes || last_edit_user_name -> O Fim || last_edit_time -> 1507241402000 || last_edit_user_id -> 1842197 || lit -> yes || busway:right -> lane || lanes -> 4 || name -> Avenida Brigadeiro Faria Lima || highway -> trunk || last_edit_version -> 2 || foot -> yes\n183853720000000 && -23.5701751,-46.6454978:-23.5703835,-46.6452311:-23.57036,-46.6452093:-23.5701516,-46.645476:-23.5701751,-46.6454978 && last_edit_user_name -> MCPicoli || last_edit_changeset -> 13334949 || last_edit_time -> 1349184985000 || last_edit_user_id -> 713822 || iso_country_code -> BRA || public_transport -> platform || highway -> bus_stop || last_edit_version -> 1\n# Points\n1942703708000000 && -23.5701751,-46.6454978 && last_edit_user_name -> cxs || last_edit_changeset -> 18755211 || last_edit_time -> 1383774093000 || last_edit_user_id -> 251808 || iso_country_code -> BRA || last_edit_version -> 2\n1942703894000000 && -23.5703835,-46.6452311 && last_edit_user_name -> cxs || last_edit_changeset -> 18755211 || last_edit_time -> 1383774093000 || last_edit_user_id -> 251808 || iso_country_code -> BRA || last_edit_version -> 2\n1942703699000000 && -23.57036,-46.6452093 && last_edit_user_name -> cxs || last_edit_changeset -> 18755211 || last_edit_time -> 1383774093000 || last_edit_user_id -> 251808 || iso_country_code -> BRA || last_edit_version -> 2\n1942703683000000 && -23.5701516,-46.645476 && last_edit_user_name -> cxs || last_edit_changeset -> 18755211 || last_edit_time -> 1383774092000 || last_edit_user_id -> 251808 || iso_country_code -> BRA || last_edit_version -> 2\n173361748000000 && -23.5677831,-46.6928133 && last_edit_user_name -> Bonix-Mapper || last_edit_changeset -> 43434223 || last_edit_time -> 1478403510000 || last_edit_user_id -> 2030995 || iso_country_code -> BRA || highway -> traffic_signals || last_edit_version -> 19\n2448588901000000 && -23.567877,-46.692725 && last_edit_user_name -> O Fim || last_edit_changeset -> 52667882 || last_edit_time -> 1507241397000 || last_edit_user_id -> 1842197 || iso_country_code -> BRA || last_edit_version -> 3\n# Relations\n2448165000000 && 183853720000000 -> platform -> L && last_edit_user_name -> doenza || last_edit_changeset -> 13419147 || last_edit_time -> 1349730880000 || last_edit_user_id -> 615332 || iso_country_code -> BRA || public_transport -> stop_area || type -> public_transport || last_edit_version -> 2\n2446626000000 && 450151832000000 ->  -> L || 2448165000000 ->  -> R && last_edit_changeset -> 61598424 || iso_country_code -> BRA || type -> route || operator -> Via Sul || network -> SPTrans || last_edit_user_name -> carlosassuncao92 || ref -> 477A-10 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1534103404000 || last_edit_user_id -> 6251065 || name -> 477A-10 Pinheiros || from -> Sacomã || to -> Pinheiros || last_edit_version -> 62\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/nestedRelationRemovalBoundaryMap.txt",
    "content": "BRA||POLYGON((-46.70496711184 -23.55204821537, -46.59128899332 -23.55176920987, -46.59144117287 -23.61257839182, -46.70588018909 -23.61271783003, -46.70496711184 -23.55204821537))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/nodeAndPointAsRelationMember.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n125508462000000 && 55.9257003,37.6560565:55.9253399,37.6564714:55.9252591,37.6566225:55.9252572,37.6569671:55.9253556,37.657839:55.9255128,37.6592395:55.9257493,37.6613014 && last_edit_user_name -> xmd5a || last_edit_changeset -> 46553526 || surface -> asphalt || last_edit_time -> 1488561413000 || last_edit_user_id -> 672536 || iso_country_code -> RUS || name -> Центральная улица || highway -> tertiary || last_edit_version -> 3\n# Points\n556595133000000 && 55.9257493,37.6613014 && last_edit_user_name -> Dima M || last_edit_changeset -> 9898421 || last_edit_time -> 1321890252000 || last_edit_user_id -> 433361 || iso_country_code -> RUS || highway -> traffic_signals || last_edit_version -> 4\n1184545034000000 && 55.9257003,37.6560565 && last_edit_user_name -> esaulenka || last_edit_changeset -> 33534429 || last_edit_time -> 1440369565000 || last_edit_user_id -> 336354 || iso_country_code -> RUS || last_edit_version -> 2\n3711172160000000 && 55.9253399,37.6564714 && last_edit_user_name -> esaulenka || last_edit_changeset -> 33534429 || last_edit_time -> 1440369382000 || last_edit_user_id -> 336354 || iso_country_code -> RUS || last_edit_version -> 1\n259592015000000 && 55.9252591,37.6566225 && last_edit_user_name -> esaulenka || last_edit_changeset -> 33534429 || last_edit_time -> 1440369573000 || last_edit_user_id -> 336354 || iso_country_code -> RUS || last_edit_version -> 2\n3711172157000000 && 55.9252572,37.6569671 && last_edit_user_name -> esaulenka || last_edit_changeset -> 33534429 || last_edit_time -> 1440369382000 || last_edit_user_id -> 336354 || iso_country_code -> RUS || last_edit_version -> 1\n3711172161000000 && 55.9253556,37.657839 && last_edit_user_name -> esaulenka || last_edit_changeset -> 33534429 || last_edit_time -> 1440369382000 || last_edit_user_id -> 336354 || iso_country_code -> RUS || last_edit_version -> 1\n1184566669000000 && 55.9255128,37.6592395 && last_edit_user_name -> esaulenka || last_edit_changeset -> 33534429 || last_edit_time -> 1440369565000 || last_edit_user_id -> 336354 || iso_country_code -> RUS || last_edit_version -> 2\n# Relations\n578254000000 && 125508462000000 -> from -> L || 125508462000000 -> to -> L || 556595133000000 -> via -> P && last_edit_user_name -> Torx || last_edit_changeset -> 8977626 || last_edit_time -> 1312990671000 || last_edit_user_id -> 131244 || iso_country_code -> RUS || restriction -> no_u_turn || type -> restriction || last_edit_version -> 3\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/nodeAndPointRelationMemberBoundaryMap.txt",
    "content": "RUS||POLYGON((37.65492080569 55.92645524618, 37.66290305972 55.9265454096, 37.66296743274 55.92468800095, 37.654834975 55.92464592271, 37.65492080569 55.92645524618))"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/oneWayRing.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n460257372000000 && 7.4056228,-7.544423:7.4054606,-7.5440298:7.4054581,-7.5439855:7.4054698,-7.5439414:7.4055053,-7.5438961:7.4055438,-7.5438649:7.4055868,-7.5438658:7.4056928,-7.5439353:7.4057793,-7.5440219:7.4057738,-7.5440787:7.4056629,-7.5442744:7.405644,-7.5443205:7.4056228,-7.544423 && last_edit_changeset -> 44482187 || access -> customers || surface -> asphalt || iso_country_code -> CIV || source -> Cnes / Spot Image || oneway -> yes || last_edit_user_name -> ulrichm || last_edit_time -> 1482025768000 || last_edit_user_id -> 192829 || service -> driveway || lanes -> 1 || incline -> up || highway -> service || last_edit_version -> 1\n# Points\n1253867987000000 && 7.4056228,-7.544423 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025782000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 4\n3847137297000000 && 7.4054606,-7.5440298 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137302000000 && 7.4054698,-7.5439414 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137303000000 && 7.4054581,-7.5439855 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137304000000 && 7.4055053,-7.5438961 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137305000000 && 7.4055868,-7.5438658 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137310000000 && 7.4057738,-7.5440787 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137311000000 && 7.4056928,-7.5439353 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137312000000 && 7.405644,-7.5443205 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137314000000 && 7.4056629,-7.5442744 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n4559344028000000 && 7.4055438,-7.5438649 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025736000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4559344273000000 && 7.4057793,-7.5440219 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025735000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/oneWaySimpleLine.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n109486264000000 && 7.4040186,-7.5556377:7.4040214,-7.5558108:7.4040139,-7.5558868:7.4040034,-7.5559234:7.403982,-7.5559575 && last_edit_changeset -> 56335930 || surface -> asphalt || iso_country_code -> CIV || maxspeed -> 55 || motorroad -> yes || oneway -> yes || last_edit_user_name -> whatmeinfallible || ref -> A8 || last_edit_time -> 1518559734000 || last_edit_user_id -> 7150022 || lit -> yes || lanes -> 2 || highway -> primary || last_edit_version -> 3\n# Points\n1253114487000000 && 7.4040186,-7.5556377 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025782000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 3\n4559338263000000 && 7.4040214,-7.5558108 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025745000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4559344069000000 && 7.4040139,-7.5558868 && last_edit_user_name -> whatmeinfallible || last_edit_changeset -> 56335930 || last_edit_time -> 1518559734000 || last_edit_user_id -> 7150022 || iso_country_code -> CIV || last_edit_version -> 2\n4559344074000000 && 7.4040034,-7.5559234 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025743000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4559344427000000 && 7.403982,-7.5559575 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025744000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/pedestrianRing.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n460257372000000 && 7.4056228,-7.544423:7.4054606,-7.5440298:7.4054581,-7.5439855:7.4054698,-7.5439414:7.4055053,-7.5438961:7.4055438,-7.5438649:7.4055868,-7.5438658:7.4056928,-7.5439353:7.4057793,-7.5440219:7.4057738,-7.5440787:7.4056629,-7.5442744:7.405644,-7.5443205:7.4056228,-7.544423 && last_edit_changeset -> 44482187 || access -> customers || surface -> asphalt || iso_country_code -> CIV || source -> Cnes / Spot Image || oneway -> yes || last_edit_user_name -> ulrichm || last_edit_time -> 1482025768000 || last_edit_user_id -> 192829 || service -> driveway || lanes -> 1 || incline -> up || highway -> pedestrian || area -> yes || last_edit_version -> 1\n# Points\n1253867987000000 && 7.4056228,-7.544423 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025782000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 4\n3847137297000000 && 7.4054606,-7.5440298 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137302000000 && 7.4054698,-7.5439414 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137303000000 && 7.4054581,-7.5439855 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137304000000 && 7.4055053,-7.5438961 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137305000000 && 7.4055868,-7.5438658 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137310000000 && 7.4057738,-7.5440787 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137311000000 && 7.4056928,-7.5439353 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137312000000 && 7.405644,-7.5443205 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n3847137314000000 && 7.4056629,-7.5442744 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025831000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 2\n4559344028000000 && 7.4055438,-7.5438649 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025736000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4559344273000000 && 7.4057793,-7.5440219 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44482187 || last_edit_time -> 1482025735000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/rawAtlasSpanningOutsideBoundary.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n112429000000 && 7.0133271,-7.0304936:7.0128905,-7.0306545:7.0123688,-7.0304453 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || highway -> motorway || last_edit_version -> 1 || oneway -> yes\n112440000000 && 7.0128905,-7.0306545:7.0131088,-7.0316684:7.012832,-7.0324623:7.0130236,-7.0330041 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || highway -> motorway || last_edit_version -> 1 || oneway -> yes\n112452000000 && 7.0133697,-7.0323443:7.012832,-7.0324623:7.0122357,-7.0323121 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || highway -> motorway || last_edit_version -> 1 || oneway -> yes\n# Points\n112404000000 && 6.9272056,-6.7232943 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n112427000000 && 7.0133271,-7.0304936 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n112428000000 && 7.0128905,-7.0306545 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n112430000000 && 7.0123688,-7.0304453 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n112439000000 && 7.0131088,-7.0316684 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n112441000000 && 7.012832,-7.0324623 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n112449000000 && 7.0130236,-7.0330041 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n112451000000 && 7.0133697,-7.0323443 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n112453000000 && 7.0122357,-7.0323121 && last_edit_user_name -> myself || last_edit_changeset -> 1 || last_edit_time -> 1513719782000 || last_edit_user_id -> 1 || last_edit_version -> 1\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/reversedOneWayLine.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n333112568000000 && 5.3171532,-4.0144607:5.3175314,-4.0147445:5.31833,-4.0154003 && last_edit_user_name -> sommerluk || last_edit_changeset -> 33677527 || last_edit_time -> 1440921858000 || last_edit_user_id -> 1700634 || iso_country_code -> CIV || name -> Rue A 49 || highway -> residential || last_edit_version -> 2 || oneway -> -1\n# Points\n243775455000000 && 5.31833,-4.0154003 && last_edit_user_name -> ulrichm || last_edit_changeset -> 31012166 || last_edit_time -> 1431363114000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 6\n779158167000000 && 5.3175314,-4.0147445 && last_edit_user_name -> sommerluk || last_edit_changeset -> 33677527 || last_edit_time -> 1440921858000 || last_edit_user_id -> 1700634 || iso_country_code -> CIV || last_edit_version -> 4\n2991180873000000 && 5.3171532,-4.0144607 && last_edit_user_name -> sommerluk || last_edit_changeset -> 24486226 || last_edit_time -> 1406917522000 || last_edit_user_id -> 1700634 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/ringWithSingleIntersection.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n460419994000000 && 7.4118037,-7.5719267:7.4118073,-7.571803 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || access -> private || surface -> asphalt || last_edit_time -> 1482103980000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || highway -> service || last_edit_version -> 1\n460419995000000 && 7.4117021,-7.5720236:7.4117107,-7.5719857:7.411733,-7.5719539:7.4117656,-7.5719332:7.4118037,-7.5719267:7.4118413,-7.5719353:7.4118728,-7.5719578:7.4118933,-7.5719907:7.4118998,-7.5720291:7.4118912,-7.572067:7.4118689,-7.5720988:7.4118363,-7.5721195:7.4117983,-7.572126:7.4117606,-7.5721174:7.4117292,-7.5720949:7.4117086,-7.572062:7.4117021,-7.5720236 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || access -> private || surface -> asphalt || last_edit_time -> 1482103980000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || highway -> service || last_edit_version -> 1 || oneway -> yes\n# Points\n4560902549000000 && 7.4117086,-7.572062 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103962000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902550000000 && 7.4117292,-7.5720949 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103962000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902551000000 && 7.4117606,-7.5721174 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103962000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902552000000 && 7.4117983,-7.572126 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103962000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902553000000 && 7.4118998,-7.5720291 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103964000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902554000000 && 7.4117021,-7.5720236 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103964000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902668000000 && 7.4118363,-7.5721195 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902669000000 && 7.4118689,-7.5720988 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902670000000 && 7.4118912,-7.572067 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902671000000 && 7.4118933,-7.5719907 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902672000000 && 7.4118728,-7.5719578 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902673000000 && 7.4118413,-7.5719353 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902674000000 && 7.4118037,-7.5719267 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902675000000 && 7.4117656,-7.5719332 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902676000000 && 7.411733,-7.5719539 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902677000000 && 7.4117107,-7.5719857 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103963000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4560902548000000 && 7.4118073,-7.571803 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44503236 || last_edit_time -> 1482103962000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/roundAbout.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n426558829000000 && 7.2564978,-7.2374367:7.2565075,-7.2374378:7.2565161,-7.2374424:7.2565223,-7.2374501:7.2565251,-7.2374595:7.256524,-7.2374693:7.2565194,-7.237478:7.2565118,-7.2374842:7.2565024,-7.237487:7.2564927,-7.237486:7.2564841,-7.2374813:7.256478,-7.2374737:7.2564752,-7.2374642:7.2564762,-7.2374544:7.2564808,-7.2374458:7.2564884,-7.2374396:7.2564978,-7.2374367 && junction -> roundabout || last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40482040 || access -> permissive || projet:eboci -> yes || last_edit_time -> 1467648706000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || highway -> service || last_edit_version -> 4\n426558831000000 && 7.2564118,-7.2373325:7.2564884,-7.2374396 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40482040 || access -> permissive || projet:eboci -> yes || last_edit_time -> 1467648709000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || highway -> service || last_edit_version -> 4\n426558827000000 && 7.2571036,-7.2375035:7.2570406,-7.2375253:7.2569554,-7.2375507:7.2568716,-7.2375802:7.2567798,-7.2376124:7.256716,-7.2376232:7.2566521,-7.2376272:7.2566109,-7.2376232:7.2565177,-7.2375038:7.2565118,-7.2374842 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40482040 || access -> permissive || projet:eboci -> yes || last_edit_time -> 1467648705000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || highway -> service || last_edit_version -> 4\n# Points\n4258458972000000 && 7.2564752,-7.2374642 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458973000000 && 7.2564762,-7.2374544 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458974000000 && 7.256478,-7.2374737 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458975000000 && 7.2564808,-7.2374458 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458976000000 && 7.2564841,-7.2374813 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458977000000 && 7.2564884,-7.2374396 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458978000000 && 7.2564927,-7.237486 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458979000000 && 7.2564978,-7.2374367 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458980000000 && 7.2565024,-7.237487 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458981000000 && 7.2565075,-7.2374378 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458982000000 && 7.2565118,-7.2374842 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458983000000 && 7.2565161,-7.2374424 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458985000000 && 7.2565194,-7.237478 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458986000000 && 7.2565223,-7.2374501 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458987000000 && 7.256524,-7.2374693 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458988000000 && 7.2565251,-7.2374595 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458984000000 && 7.2565177,-7.2375038 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258459292000000 && 7.2566109,-7.2376232 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258459293000000 && 7.2566521,-7.2376272 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258459297000000 && 7.256716,-7.2376232 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258459301000000 && 7.2567798,-7.2376124 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258459306000000 && 7.2568716,-7.2375802 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258459310000000 && 7.2569554,-7.2375507 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258459313000000 && 7.2570406,-7.2375253 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258459317000000 && 7.2571036,-7.2375035 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595140000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n4258458971000000 && 7.2564118,-7.2373325 && last_edit_user_name -> Anebo Philippe || last_edit_changeset -> 40202668 || last_edit_time -> 1466595139000 || last_edit_user_id -> 2231043 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/selfIntersectingLoop.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n373705333000000 && 47.1458148,47.2824511:47.1454972,47.2832076 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913657000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || highway -> track || last_edit_version -> 1\n373706224000000 && 47.1448746,47.2838708:47.1444123,47.2847417 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417759 || last_edit_time -> 1443914914000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || highway -> track || last_edit_version -> 1\n373705334000000 && 47.1453511,47.2827126:47.1453956,47.2824698:47.1452367,47.2824324:47.1453511,47.2827126:47.1454972,47.2832076:47.1453765,47.2835532:47.1452876,47.2840109:47.1450398,47.283796:47.1448746,47.2838708:47.1452622,47.2831329:47.1453511,47.2827126 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913657000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || highway -> track || last_edit_version -> 1\n373773345000000 && 47.1452367,47.2824324:47.145071,47.2823274 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34429007 || last_edit_time -> 1443969324000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || highway -> track || last_edit_version -> 1\n373705335000000 && 47.1453956,47.2824698:47.1455861,47.281816 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913657000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || highway -> track || last_edit_version -> 1\n# Points\n3771250158000000 && 47.1453511,47.2827126 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250159000000 && 47.1453956,47.2824698 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250160000000 && 47.1452367,47.2824324 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250157000000 && 47.1454972,47.2832076 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250161000000 && 47.1453765,47.2835532 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250162000000 && 47.1452876,47.2840109 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250163000000 && 47.1450398,47.283796 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250164000000 && 47.1448746,47.2838708 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250165000000 && 47.1452622,47.2831329 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250166000000 && 47.1455861,47.281816 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771860116000000 && 47.145071,47.2823274 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34429007 || last_edit_time -> 1443969324000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771256499000000 && 47.1444123,47.2847417 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417759 || last_edit_time -> 1443914914000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n3771250056000000 && 47.1458148,47.2824511 && last_edit_user_name -> A-lex-W || last_edit_changeset -> 34417557 || last_edit_time -> 1443913656000 || last_edit_user_id -> 3255141 || iso_country_code -> RUS || last_edit_version -> 1\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/simpleBiDirectionalLine.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n460834514000000 && 7.4149685,-7.5700756:7.4154655,-7.570464:7.4161236,-7.5709812:7.41669,-7.5714213 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44553998 || surface -> ground || last_edit_time -> 1482277540000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || noname -> yes || highway -> residential || last_edit_version -> 1 || oneway -> no\n# Points\n4564344487000000 && 7.4161236,-7.5709812 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44553998 || last_edit_time -> 1482277532000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4564344630000000 && 7.4154655,-7.570464 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44553998 || last_edit_time -> 1482277533000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4564344631000000 && 7.41669,-7.5714213 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44553998 || last_edit_time -> 1482277533000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n4564344632000000 && 7.4149685,-7.5700756 && last_edit_user_name -> ulrichm || last_edit_changeset -> 44553998 || last_edit_time -> 1482277533000 || last_edit_user_id -> 192829 || iso_country_code -> CIV || last_edit_version -> 1\n# Relations"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/raw/sectioning/wayExceedingSectioningLimit.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n608901269000000 && 34.1154094,66.5064452:34.1153535,66.5063416:34.1149659,66.5058599:34.1144702,66.5051891:34.1142428,66.504802:34.1141832,66.5046759:34.1141198,66.5045454:34.1140229,66.5044463:34.1138142,66.5042843:34.1136614,66.5041897:34.1135868,66.5041222:34.1134154,66.5038251:34.1133483,66.503735:34.1132477,66.5036045:34.1130613,66.5033974:34.1129122,66.5032488:34.1127408,66.5030463:34.1126029,66.5028482:34.1124985,66.5026321:34.1124463,66.502407:34.1123867,66.5020964:34.1123047,66.5017542:34.1122301,66.5015336:34.112096,66.5013041:34.1118984,66.5010565:34.1118313,66.5009259:34.1117456,66.5007413:34.1116822,66.5005162:34.1116114,66.5002551:34.1114772,66.499958:34.1112871,66.4996519:34.111153,66.4994538:34.1110001,66.4992782:34.110948,66.4992242:34.110892,66.4991432:34.1107131,66.4988325:34.1105193,66.4985894:34.1104336,66.4985174:34.1103889,66.4984814:34.1103031,66.4983553:34.1100348,66.4979817:34.1098484,66.4976801:34.10958,66.4971894:34.1093899,66.4968787:34.1091812,66.4966086:34.1089575,66.4963115:34.1086072,66.4959289:34.1081673,66.4955147:34.1080108,66.4954021:34.1078505,66.4953166:34.10772,66.4952086:34.1076716,66.4950735:34.1076231,66.4948484:34.1075598,66.4946233:34.1074852,66.4944252:34.1073808,66.4942902:34.1071982,66.4941461:34.107023,66.4939255:34.1068217,66.4936149:34.1066875,66.4934438:34.1065459,66.4932413:34.1064229,66.4930882:34.10627,66.4929306:34.1060949,66.4927641:34.1058414,66.4926695:34.1057445,66.4926425:34.1056401,66.492611:34.1055693,66.492548:34.1054761,66.4924489:34.1053344,66.4923004:34.1049393,66.4918502:34.1047753,66.4916746:34.1045964,66.4915576:34.1044212,66.4914945:34.104164,66.4913415:34.1038322,66.4911794:34.1034632,66.4910894:34.1033812,66.4909993:34.1033588,66.4909408:34.1033663,66.4907923:34.1034147,66.4905987:34.1034557,66.4904276:34.1034893,66.4903556:34.1035154,66.490252:34.1035191,66.490153:34.1034632,66.4900044:34.1033961,66.4898379:34.1033141,66.4896848:34.1032656,66.4895678:34.1032283,66.4894372:34.1032358,66.4892796:34.1032246,66.4890861:34.1032358,66.4888925:34.1032582,66.4887304:34.1033625,66.4885774:34.103452,66.4884828:34.1034967,66.4884468:34.1035266,66.4883973:34.1035266,66.4883343:34.1034669,66.4882847:34.1033588,66.4882262:34.1031575,66.4881227:34.1030308,66.4880732:34.1029488,66.4880101:34.1028705,66.4879066:34.1027885,66.487722:34.1027363,66.4872898:34.1027288,66.4868216:34.1027587,66.4866146:34.1027736,66.4864975:34.1027587,66.4864075:34.1027326,66.4863805:34.1026953,66.4863715:34.1025984,66.486448:34.1024865,66.4864705:34.1023188,66.4864255:34.1021436,66.4864075:34.1019982,66.486394:34.101905,66.4863805:34.1018155,66.4862949:34.1016292,66.4861058:34.1013496,66.4859033:34.101193,66.4857907:34.1010961,66.4856872:34.1010476,66.4855521:34.1010029,66.4854171:34.1009917,66.4850074:34.100947,66.4844132:34.1009507,66.4842511:34.1009694,66.4841656:34.1010365,66.484017:34.1010663,66.4839405:34.1010775,66.4838234:34.1010849,66.4835713:34.1010849,66.4829005:34.1010849,66.4827745:34.1010924,66.4826124:34.1011408,66.4823378:34.1011744,66.4820902:34.1011744,66.4818741:34.1011483,66.4815905:34.1010998,66.4811583:34.1010737,66.4810458:34.1010476,66.4809557:34.1010178,66.4808567:34.1009954,66.4806676:34.1009843,66.480447:34.1009805,66.4803525:34.1009544,66.4802625:34.100865,66.4800644:34.1008314,66.4799518:34.1008165,66.4798528:34.1008277,66.4797222:34.1008463,66.4795827:34.1008463,66.4794836:34.1008128,66.4793576:34.1007494,66.4792631:34.1006376,66.4791955:34.100522,66.4791055:34.1003878,66.4789839:34.1002723,66.4788399:34.0999815,66.4782636:34.0998361,66.4780205:34.0996907,66.4777729:34.0994671,66.4775147:34.0993515,66.4772822:34.0992024,66.4770797:34.0991316,66.4768906:34.0990533,66.476706:34.0989675,66.4765214:34.0988669,66.4764134:34.0987289,66.4762783:34.0985724,66.4761388:34.0983972,66.4760217:34.0982406,66.4759857:34.0980989,66.4759722:34.0980505,66.4758822:34.0980766,66.4758146:34.0981288,66.4757606:34.0981586,66.4756526:34.0982145,66.475531:34.0983077,66.475486:34.0984643,66.475459:34.0985276,66.4754365:34.098591,66.475387:34.0986059,66.4752654:34.0986357,66.4749818:34.0986171,66.4746487:34.0986059,66.4745136:34.0985015,66.4742255:34.0983785,66.4738158:34.0982406,66.4731721:34.0981138,66.4725418:34.0980244,66.4721907:34.0979796,66.4719521:34.0979685,66.471709:34.0978566,66.4714839:34.0976889,66.4712453:34.0975323,66.4710427:34.0973757,66.4707366:34.0972564,66.4704935:34.0972229,66.4704349:34.0971371,66.4703629:34.0970514,66.4702369:34.0969284,66.4700343:34.096824,66.4698812:34.0967532,66.4697642:34.0967233,66.4696651:34.0966525,66.469359:34.0966078,66.4691789:34.0965742,66.4690799:34.0964512,66.4688548:34.0962797,66.4684136:34.096015,66.4676843:34.0959479,66.4674322:34.095892,66.4672837:34.0958324,66.4671981:34.0957354,66.4671711:34.0956758,66.4671531:34.0956422,66.4671126:34.0956087,66.4670271:34.0955639,66.466811:34.095385,66.4663608:34.0951613,66.4658881:34.0950793,66.465762:34.0949078,66.4654874:34.0948295,66.4653029:34.0947773,66.4651813:34.094714,66.4650327:34.0946319,66.4649247:34.0945052,66.4648302:34.0943486,66.4647671:34.0941398,66.4647446:34.0939982,66.4647041:34.093808,66.4646096:34.093726,66.4645646:34.0936105,66.4645375:34.0935061,66.464479:34.0933271,66.4643124:34.0931034,66.4641549:34.0928462,66.4639703:34.092753,66.4638668:34.0926971,66.4637182:34.0925927,66.4634886:34.0925069,66.4632455:34.0924398,66.4630879:34.0922087,66.4626963:34.0919813,66.4623541:34.0918843,66.4621966:34.0917576,66.4620795:34.0915898,66.46194:34.0915376,66.461895:34.0915339,66.4618364:34.0915302,66.4617464:34.0915525,66.4616113:34.0915786,66.4614223:34.0915674,66.4612467:34.0915302,66.4610846:34.0914891,66.4608956:34.0914332,66.4606885:34.0913587,66.4604499:34.0912282,66.4601618:34.0910455,66.4597746:34.0908479,66.4593604:34.090695,66.4590678:34.0905459,66.4588472:34.0903856,66.4586852:34.0902029,66.4584871:34.0901246,66.4584375:34.0900389,66.4583745:34.0899979,66.458325:34.0899606,66.458208:34.0899457,66.4581089:34.089927,66.4580369:34.0899121,66.4579559:34.0898338,66.4577713:34.0897704,66.4575642:34.0896959,66.4573031:34.0896847,66.457141:34.0896772,66.4570015:34.0896922,66.4568574:34.0897481,66.4567314:34.0898376,66.4565558:34.0900277,66.4562452:34.0900687,66.4561641:34.0900948,66.4561056:34.0900948,66.4560516:34.0900762,66.4560066:34.0900128,66.4559795:34.0899494,66.455948:34.0898935,66.4558985:34.089845,66.4557995:34.0898264,66.4556554:34.0898003,66.4553988:34.0897928,66.4552052:34.0898077,66.4550792:34.0898711,66.4550071:34.0899904,66.4549801:34.0903968,66.4549846:34.0907082,66.4549611:34.090851,66.4548906:34.0910555,66.4547064:34.0912632,66.4545379:34.091578,66.4543576:34.0924836,66.453852:34.0927756,66.4537149:34.0930677,66.4535777:34.0932105,66.4535503:34.0934215,66.453515:34.0935383,66.453468:34.0936584,66.4533269:34.0938207,66.4530878:34.0939083,66.4528801:34.0939375,66.4528135:34.0940706,66.452692:34.0941615,66.4525626:34.0943295,66.4524018:34.0944333,66.4522528:34.0945047,66.4520294:34.0945599,66.4515905:34.0945891,66.4504618:34.0945566,66.4498544:34.0945372,66.4495251:34.0945372,66.4492312:34.0945534,66.4488903:34.0945956,66.4486669:34.0946378,66.4484788:34.0946508,66.4483808:34.0946378,66.4482867:34.0945923,66.4480908:34.0945015,66.4477655:34.0944203,66.4475029:34.0943327,66.4471619:34.0942743,66.4468798:34.0942451,66.4465898:34.094248,66.446392:34.0943227,66.4459609:34.0944395,66.4454789:34.0944784,66.4453064:34.0944979,66.4451771:34.0945077,66.4449498:34.0945174,66.444648:34.0945498,66.4441699:34.0945661,66.4439348:34.0946505,66.4435233:34.0947413,66.4430922:34.0947608,66.4429315:34.0947933,66.4428021:34.0948679,66.4425827:34.0950594,66.4421241:34.0952509,66.4417989:34.0954618,66.4415441:34.0955786,66.441454:34.0956987,66.4413756:34.0958383,66.4412894:34.0959713,66.4411248:34.0960979,66.4408544:34.096218,66.44058:34.0964192,66.4401528:34.096575,66.4397923:34.0966788,66.4395375:34.09676,66.4391731:34.0968249,66.43884:34.0968476,66.4387145:34.0969255,66.438448:34.0970456,66.4380953:34.0970618,66.4379778:34.0970585,66.4378014:34.0970163,66.4375427:34.0969612,66.4372057:34.0969222,66.4370215:34.09688,66.4368647:34.0968573,66.4367472:34.0968995,66.4365826:34.0969287,66.4363905:34.0969449,66.4361985:34.0969287,66.4358771:34.0968898,66.4356224:34.0968184,66.4353481:34.0967697,66.435156:34.0967535,66.4349993:34.0967437,66.4346113:34.0967048,66.4343918:34.0965912,66.4339411:34.096523,66.4337726:34.0964646,66.433655:34.0964387,66.4335727:34.0964062,66.4334434:34.0963608,66.4332122:34.0962634,66.4329221:34.096179,66.4327262:34.0961011,66.4325538:34.0960525,66.4324205:34.0960265,66.4322833:34.0959876,66.4320991:34.0959713,66.4319737:34.0959876,66.4317817:34.09602,66.4316171:34.0960395,66.4315113:34.0960395,66.431378:34.0960265,66.4312448:34.0960233,66.431135:34.0960395,66.4310645:34.0960849,66.4310018:34.0961141,66.430943:34.0961304,66.430845:34.0961336,66.4307118:34.0961368,66.4305629:34.0961725,66.4303591:34.0961855,66.4301474:34.0961758,66.4299868:34.0961368,66.4298026:34.0960362,66.4295243:34.0959843,66.429344:34.0959551,66.429152:34.0959616,66.429007:34.0959648,66.4289208:34.0959584,66.4288385:34.0959032,66.4286778:34.0958577,66.4285171:34.0958383,66.4283643:34.095887,66.4281213:34.0959389,66.427941:34.0959908,66.4276275:34.0960038,66.4274825:34.0959876,66.4273571:34.0959162,66.4271925:34.095861,66.4270122:34.0957831,66.4267966:34.0957279,66.4266007:34.0957085,66.4264047:34.0957085,66.4262715:34.0957377,66.426099:34.0957539,66.4258521:34.0957377,66.4256091:34.0957117,66.4254289:34.095676,66.4252956:34.0955981,66.4251585:34.0954521,66.4249939:34.095173,66.4246607:34.0949685,66.4244452:34.0948582,66.424355:34.0948225,66.4242492:34.0947673,66.4241238:34.0947,66.4239555:34.0946553,66.4238385:34.0946478,66.423726:34.0946478,66.4236089:34.0946702,66.4234874:34.0946627,66.4233253:34.0946143,66.4231407:34.0945472,66.4229877:34.0939693,66.4221503:34.0938388,66.4220513:34.0937382,66.4219882:34.0936748,66.4218937:34.0936114,66.4217677:34.0935592,66.4216686:34.0934697,66.4215831:34.0933803,66.4214885:34.0932908,66.421331:34.0931864,66.4211194:34.0931752,66.4210384:34.0931976,66.4209483:34.0932945,66.4208673:34.093425,66.4207682:34.093507,66.4206917:34.0935928,66.4205477:34.0936711,66.4203856:34.0937605,66.420237:34.093809,66.420174:34.0938612,66.420174:34.0939618,66.420201:34.0940588,66.4202595:34.0941482,66.4203496:34.0942079,66.4204081:34.0942564,66.4204126:34.0942862,66.4203676:34.094316,66.4203001:34.0943309,66.420219:34.0943458,66.4200299:34.0943533,66.4198679:34.0943719,66.4197328:34.0944241,66.4196203:34.0945173,66.4194852:34.0947373,66.4191926:34.0948156,66.4190621:34.094905,66.418999:34.0950132,66.418954:34.0951138,66.418828:34.0951884,66.4187064:34.0952517,66.4186254:34.0953151,66.4184813:34.0953673,66.4183463:34.0954083,66.4182607:34.0953971,66.4181932:34.0953673,66.4181572:34.0953263,66.4181527:34.0952592,66.4182247:34.0951623,66.4183733:34.0951101,66.4184453:34.0950132,66.4184858:34.0949349,66.4185353:34.0948678,66.4185488:34.094782,66.4184948:34.0946963,66.4184543:34.0945173,66.4184138:34.0943719,66.4183913:34.094152,66.4184678:34.0939358,66.4185939:34.0938351,66.4186209:34.0937904,66.4185804:34.0937493,66.4184813:34.0937009,66.4182562:34.0936412,66.4180446:34.0936189,66.4179231:34.093604,66.417752:34.0936151,66.4175494:34.0936077,66.4172973:34.0935779,66.4170542:34.093548,66.4167841:34.093507,66.4165275:34.0934586,66.4162439:34.0934548,66.4161088:34.0934847,66.4159198:34.0935406,66.4157667:34.0935853,66.4156677:34.0936114,66.4155731:34.0936189,66.4154606:34.0936077,66.4153525:34.0935667,66.4153165:34.0935182,66.415339:34.0934623,66.415366:34.0933915,66.415375:34.0933355,66.4153976:34.0932833,66.4154516:34.0932349,66.4155461:34.093123,66.4157802:34.0930373,66.4159378:34.0930037,66.4159873:34.092959,66.4159783:34.0928993,66.4159468:34.0928658,66.4158838:34.0928658,66.4157757:34.0928882,66.4156452:34.0929105,66.4155191:34.0929143,66.4153886:34.0929068,66.4152625:34.0928509,66.4150374:34.0928061,66.4148168:34.0927204,66.4146052:34.0926346,66.4144792:34.0924893,66.4143576:34.0924147,66.4142631:34.0923662,66.4141505:34.0923401,66.4138984:34.0923476,66.4137544:34.09237,66.4136193:34.0923737,66.4135158:34.0923625,66.4134618:34.0923066,66.4134122:34.0922544,66.4133672:34.0921873,66.4132637:34.0921127,66.4131421:34.0920829,66.4130251:34.0921053,66.4128225:34.09215,66.4126154:34.0921835,66.4125029:34.0922096,66.4124353:34.0922357,66.4123543:34.0922246,66.4123048:34.09215,66.4122418:34.0920903,66.4121517:34.0920792,66.4120797:34.0920903,66.4119582:34.0920903,66.4118006:34.0921015,66.4117331:34.092068,66.4115755:34.0920381,66.4114404:34.0919822,66.4112604:34.0919524,66.4111433:34.0919375,66.4110038:34.0919412,66.4107562:34.091951,66.4105479:34.0919217,66.4104029:34.091899,66.4102579:34.0918925,66.4101324:34.0919023,66.410007:34.0919153,66.4099208:34.0919639,66.4097053:34.0919899,66.4095446:34.0920224,66.40938:34.0920483,66.4092546:34.0920613,66.4091919:34.0920386,66.4091096:34.0919737,66.4089332:34.0918731,66.4087255:34.0917465,66.4084472:34.0916069,66.4081455:34.0914349,66.4077575:34.0913505,66.4075615:34.0912402,66.4072166:34.0911623,66.406958:34.0910422,66.4066249:34.0909611,66.4063936:34.090828,66.4061546:34.0907274,66.4059939:34.090617,66.4058606:34.0904775,66.4057352:34.0903768,66.4056647:34.0902405,66.4054883:34.0900393,66.405214:34.0898473,66.4048682:34.0896201,66.4043548:34.0893183,66.403763:34.0890456,66.4032849:34.0887957,66.4029087:34.0885945,66.4025129:34.0883998,66.4021758:34.0883154,66.4020739:34.0881693,66.4019093:34.0880836,66.4017905:34.0880127,66.4015969:34.0879493,66.4013809:34.0879121,66.4012323:34.0877965,66.4010612:34.0877145,66.4009352:34.0876809,66.4008631:34.0875952,66.4007776:34.0875094,66.4007101:34.0873864,66.400575:34.0873043,66.4004535:34.0872,66.4003139:34.0871403,66.4002509:34.087062,66.4002284:34.0867227,66.4002374:34.0866258,66.4002239:34.0865624,66.4001924:34.0865102,66.4001248:34.0864543,66.4000033:34.0862567,66.3993595:34.0861187,66.3990129:34.085992,66.3986302:34.0859509,66.3984727:34.085936,66.3983016:34.0859211,66.3981035:34.0858876,66.3978559:34.0858428,66.3976488:34.0857123,66.3972662:34.0855408,66.3967169:34.0854215,66.3962758:34.0853879,66.3961632:34.0853693,66.3960057:34.0853358,66.3956905:34.0852612,66.3952629:34.0851754,66.3949387:34.0851158,66.3947992:34.0850114,66.3946551:34.0848473,66.3943085:34.0846833,66.3940293:34.0845469,66.3937354:34.084482,66.3935277:34.08431,66.3928223:34.0841802,66.3922854:34.0841445,66.3921717:34.0840925,66.392062:34.0840146,66.3919601:34.0839335,66.3918268:34.0838653,66.3917014:34.0838166,66.3915838:34.0837744,66.3914741:34.0837322,66.3913291:34.0836121,66.3910861:34.0834466,66.3907726:34.0832519,66.3904042:34.083135,66.3901769:34.0830571,66.3900554:34.0829695,66.3899457:34.0829045,66.3898085:34.0828007,66.3895302:34.0826643,66.3892598:34.0825475,66.3889698:34.0824599,66.3887151:34.0823982,66.3884878:34.0822911,66.3881468:34.0822164,66.3878842:34.0821223,66.3876373:34.0820184,66.3873904:34.0819632,66.3872454:34.0818723,66.3870142:34.0817587,66.3868104:34.0815737,66.3865361:34.0813887,66.386297:34.0811095,66.3860109:34.0809753,66.385874:34.0808784,66.3856444:34.0808523,66.3855453:34.080856,66.3854058:34.080856,66.3853472:34.0808523,66.3852887:34.0808187,66.3852572:34.0807031,66.3851447:34.0805801,66.3850276:34.0804682,66.3848926:34.080416,66.3847665:34.0803787,66.3845954:34.0803638,66.3844289:34.0803564,66.3843208:34.0803675,66.3842578:34.0803675,66.3842083:34.0803526,66.3841362:34.0803153,66.3840282:34.0802557,66.3838751:34.080237,66.3837581:34.0801923,66.383533:34.0801401,66.3832674:34.080155,66.3831548:34.0802035,66.3830108:34.0802818,66.3826776:34.0803042,66.3825516:34.0803489,66.382304:34.0804309,66.3821059:34.0805167,66.3819168:34.0806509,66.3817143:34.0807479,66.3815522:34.0808187,66.3814576:34.0808635,66.3813271:34.0808858,66.381165:34.0808746,66.381048:34.0808374,66.3809219:34.0807777,66.3808634:34.0806957,66.3808364:34.0805577,66.3808814:34.0803564,66.3809715:34.080237,66.3809895:34.0801177,66.3809715:34.0799984,66.3809084:34.0799276,66.3808184:34.0798604,66.3807419:34.079812,66.3806383:34.079771,66.3805123:34.079756,66.3804087:34.0797299,66.3803322:34.0796405,66.3802286:34.079495,66.3800216:34.0793049,66.3796794:34.0791893,66.3794363:34.0790812,66.3792112:34.0790439,66.3791032:34.0790103,66.3788421:34.0789917,66.3786935:34.0789395,66.3786125:34.07885,66.378626:34.0787568,66.3787025:34.0786486,66.3788241:34.0785218,66.3790627:34.0784174,66.3793328:34.0783652,66.3793733:34.0783093,66.3793688:34.0782757,66.3792923:34.0782646,66.3791797:34.0782496,66.3790807:34.0782198,66.3789816:34.0781863,66.3788601:34.0781751,66.3787115:34.0782198,66.3784774:34.0783056,66.3782884:34.0784659,66.3780363:34.0785666,66.3778427:34.0786337,66.3776536:34.0786374,66.3775546:34.0786151,66.377424:34.0785479,66.3772169:34.0785144,66.3770639:34.0785144,66.3768253:34.0785256,66.3765912:34.0785144,66.3764066:34.0784808,66.376294:34.0783839,66.3760825:34.0782981,66.3758934:34.0782347,66.3757133:34.0781452,66.3755602:34.0780185,66.3754072:34.0779141,66.3752811:34.0778768,66.3752271:34.0778544,66.3751371:34.0778656,66.3750425:34.0779029,66.3749345:34.0779625,66.3748264:34.0780185,66.3746464:34.0780334,66.3745518:34.0780073,66.3744483:34.0779774,66.3743673:34.077929,66.3742727:34.0778693,66.3741692:34.0778581,66.3740791:34.077888,66.3738405:34.0779066,66.373737:34.0779103,66.373647:34.077888,66.3735254:34.0778134,66.3730032:34.0778134,66.3728861:34.0777985,66.3728096:34.0777724,66.3727511:34.0777164,66.3727241:34.0776642,66.3727466:34.0776008,66.3728186:34.0775113,66.3729042:34.0774181,66.3729942:34.077351,66.3730347:34.0772802,66.3730257:34.0772578,66.3729717:34.0772503,66.3728726:34.0772652,66.3727196:34.0772615,66.372553:34.0772354,66.372436:34.0772056,66.3723144:34.0771907,66.3721929:34.0771907,66.3720713:34.077213,66.3718147:34.0772354,66.3715941:34.0772242,66.3714951:34.0771981,66.371414:34.0771571,66.3713105:34.0770975,66.3711935:34.0770154,66.3710629:34.0769744,66.3708828:34.0769371,66.3706262:34.0768961,66.3703201:34.0768364,66.3701085:34.0767283,66.3698879:34.0765829,66.3697124:34.0764412,66.3696088:34.0762436,66.3695368:34.0761205,66.3694512:34.0760273,66.3693567:34.0758259,66.3692892:34.075412,66.3692937:34.0752927,66.3693252:34.075151,66.3692982:34.0749795,66.3692126:34.0747371,66.3689515:34.0745991,66.368794:34.0744462,66.3685779:34.0741852,66.3682898:34.0740696,66.3681682:34.0739838,66.3681052:34.0739093,66.3680242:34.0738123,66.3677856:34.073652,66.3674074:34.0736184,66.3672769:34.0735774,66.3669617:34.0735177,66.3667682:34.0734655,66.3666106:34.0733984,66.36648:34.0732791,66.366318:34.0731038,66.3661019:34.0729584,66.3659308:34.0728987,66.3658453:34.0728391,66.3657417:34.0724923,66.365251:34.072399,66.3651745:34.0723095,66.3651025:34.0722238,66.3650259:34.0721492,66.3648684:34.0721156,66.3647378:34.0721044,66.3644992:34.0721305,66.3640761:34.0721343,66.3638194:34.0721268,66.3636619:34.0720821,66.3634548:34.0719963,66.3632342:34.0719665,66.3631352:34.0719739,66.3629731:34.0719851,66.362802:34.0719777,66.362613:34.0719888,66.3623609:34.0720224,66.3622033:34.0721268,66.3620232:34.0722089,66.3618972:34.072235,66.3617666:34.0722238,66.3614245:34.0721604,66.3612174:34.072041,66.3609428:34.0718185,66.360584:34.0716096,66.360201:34.0714239,66.3598648:34.0712924,66.3595659:34.0712537,66.3592857:34.0712847,66.3589681:34.0713233,66.3586506:34.0714471,66.3584358:34.0715941,66.3582676:34.0716638,66.3580902:34.071656,66.3579127:34.0715941,66.3575765:34.0714935,66.3573617:34.0713156,66.3571001:34.0711531,66.3569507:34.0708957,66.3566393:34.0706027,66.3561735:34.070341,66.355815:34.0700686,66.355285:34.0699409,66.3550821:34.0698659,66.35498:34.06982,66.3548763:34.0698037,66.354652:34.0698043,66.3544277:34.0697727,66.3542553:34.0697323,66.3541414:34.0696716,66.3540805:34.0696009,66.3540032:34.0695134,66.3539056:34.0694157,66.3538447:34.069281,66.3538203:34.0690722,66.3538203:34.0689139,66.3537674:34.0687691,66.3536902:34.0686647,66.3535885:34.0685367,66.3533812:34.0684817,66.3532339:34.0684379,66.3531079:34.0684312,66.3529616:34.068411,66.3527908:34.0683638,66.3526323:34.0683167,66.3525265:34.068256,66.3524412:34.0681651,66.3523639:34.0680641,66.3522704:34.0680169,66.3521403:34.0680001,66.3520387:34.0679395,66.3518476:34.0678654,66.3516484:34.0677778,66.3514289:34.0676734,66.3509654:34.0675657,66.350319:34.0675118,66.3497336:34.0674747,66.349327:34.0674646,66.3492498:34.0674815,66.3491197:34.0675118,66.348892:34.0675118,66.3487619:34.0674646,66.3486278:34.0674074,66.3485424:34.06734,66.3484936:34.0672558,66.3484164:34.0672121,66.3483594:34.0670134,66.3482415:34.0664779,66.3479122:34.0662792,66.347774:34.0662219,66.347713:34.066195,66.3476439:34.0661579,66.3475626:34.0661377,66.3474691:34.0661209,66.3473431:34.0660906,66.3472211:34.0660468,66.3471357:34.0659929,66.3470707:34.065582,66.3468349:34.0654675,66.3468105:34.0653732,66.3468105:34.0652722,66.346843:34.0651981,66.3468593:34.065087,66.346843:34.0648108,66.3467333:34.0646054,66.3466682:34.0642888,66.3466235:34.064016,66.3465747:34.0638072,66.3465259:34.0637264,66.3464975:34.0635681,66.3463999:34.0630326,66.346095:34.0629079,66.3460218:34.0628271,66.3459893:34.0627059,66.3459527:34.0625981,66.345912:34.0624903,66.3458267:34.0623859,66.3457128:34.0623017,66.3456315:34.0622478,66.3456193:34.0622209,66.3456478:34.0621973,66.3457006:34.0621838,66.3458185:34.0621569,66.3460259:34.0621266,66.346156:34.0620626,66.3462901:34.0620491,66.3463511:34.0620525,66.3464446:34.0620862,66.3465219:34.0621367,66.3465869:34.0621805,66.3466438:34.0622007,66.3467211:34.0621973,66.3467942:34.0621771,66.3468837:34.062167,66.3469731:34.0621906,66.3470666:34.0622142,66.3471601:34.062268,66.3472577:34.0624028,66.3473065:34.0626149,66.3472374:34.0627328,66.3472821:34.0628305,66.3474041:34.0628844,66.3474894:34.0629349,66.347526:34.0629719,66.3475748:34.0629517,66.3476195:34.0628945,66.3476561:34.0628035,66.3476561:34.0626756,66.3476236:34.062625,66.3476114:34.0625611,66.3476277:34.0624196,66.3476683:34.0622748,66.3476724:34.0621333,66.3476683:34.0619548,66.3476236:34.0616955,66.3475179:34.0614698,66.3473268:34.0611802,66.3470341:34.060968,66.3467251:34.0608569,66.3465462:34.0607962,66.3464365:34.0607659,66.3462779:34.0607727,66.3460543:34.0607895,66.3459405:34.060776,66.3457982:34.0607087,66.3455461:34.060483,66.3447818:34.0604561,66.3446802:34.0604426,66.3446111:34.0603988,66.3445257:34.060355,66.3444525:34.060291,66.344355:34.0601226,66.3440297:34.0600283,66.3437736:34.0599475,66.3435703:34.0598161,66.3433549:34.0597454,66.3431922:34.059705,66.3430418:34.0596983,66.3428751:34.059806,66.3425377:34.0598464,66.3424361:34.0599138,66.3423913:34.0600283,66.3423669:34.0602978,66.3422247:34.0604325,66.3421352:34.0605032,66.3420661:34.0605201,66.3419767:34.0605099,66.3419075:34.0604662,66.3418669:34.0604257,66.3418384:34.0603483,66.3418181:34.0601327,66.3418222:34.0599711,66.3418181:34.059806,66.3417653:34.0595568,66.3416474:34.0593716,66.341562:34.0591526,66.3414644:34.0590078,66.341379:34.0588933,66.3413384:34.0587687,66.341314:34.0586272,66.3412774:34.0584857,66.3412083:34.0584083,66.3411798:34.0583207,66.3411554:34.0581119,66.3411107:34.0579906,66.3410741:34.0578727,66.3410091:34.0577245,66.3408749:34.0575258,66.3406513:34.0573406,66.3404684:34.057216,66.3403708:34.0569836,66.3401269:34.0568421,66.3399764:34.0566804,66.3398423:34.0565356,66.3397447:34.0564177,66.3396837:34.0562661,66.339639:34.0561584,66.3395862:34.0561011,66.3395374:34.056064,66.3394764:34.0560674,66.3393544:34.0560539,66.3392487:34.0559798,66.3391268:34.0558788,66.3390577:34.0557542,66.3390211:34.0556464,66.3390211:34.0555723,66.3390251:34.0554813,66.3390495:34.0553298,66.3391715:34.0551445,66.3392934:34.0549492,66.339391:34.0548279,66.339391:34.0546763,66.3393788:34.0545147,66.33933:34.0543563,66.3392609:34.0541441,66.3391756:34.0537298,66.3389967:34.0536389,66.3389357:34.053548,66.3388381:34.0534941,66.3386836:34.0534873,66.3385169:34.0534806,66.3384316:34.0534435,66.3383421:34.0533661,66.3382771:34.0532549,66.3381795:34.0531909,66.3380779:34.0531101,66.3379518:34.0529012,66.3376307:34.0528844,66.3375453:34.052881,66.3372648:34.0528911,66.3369599:34.0528642,66.3366915:34.0528474,66.3362687:34.0528575,66.3359516:34.052881,66.3358663:34.0529383,66.3357606:34.0529585,66.3356711:34.0529417,66.3355573:34.0529181,66.3354394:34.0529282,66.3353499:34.0529551,66.3352401:34.0529753,66.3351344:34.0529753,66.3350165:34.0529484,66.3348742:34.0529282,66.3347807:34.0528776,66.3346222:34.0528675,66.3345408:34.0528675,66.3344555:34.0528675,66.3343864:34.0528608,66.3343172:34.052844,66.3342685:34.0528069,66.3341872:34.0527901,66.334118:34.0527833,66.3340205:34.0527598,66.3338172:34.0527193,66.3335123:34.0527193,66.3334269:34.0527362,66.3333375:34.0527598,66.3332196:34.0527833,66.3330976:34.0528103,66.3329065:34.0528136,66.3327277:34.0527867,66.3324553:34.052753,66.3321869:34.0526991,66.3319796:34.0526823,66.3318576:34.0527126,66.3317235:34.0527025,66.3316422:34.0526587,66.3315243:34.0526183,66.3314104:34.0525711,66.3312803:34.0525341,66.3311462:34.052497,66.3309714:34.0524061,66.3307234:34.0523758,66.3306055:34.0523589,66.3304794:34.0523993,66.3303412:34.0524229,66.3302477:34.0524229,66.3301217:34.0523993,66.3299347:34.0523421,66.3297964:34.0522875,66.3296628:34.0522707,66.3295205:34.0522572,66.3292928:34.0522572,66.3292075:34.0522808,66.3291505:34.0523448,66.3290977:34.052392,66.3290326:34.052392,66.3289513:34.0523583,66.3289025:34.0522741,66.3288619:34.0522168,66.3287928:34.0521191,66.3285732:34.0520518,66.3284594:34.0520282,66.3283984:34.0519878,66.3282887:34.0519878,66.3281952:34.0519878,66.3281138:34.0519675,66.3280285:34.0519103,66.3279553:34.0518261,66.3278862:34.0517486,66.3277886:34.0516745,66.3276748:34.0516543,66.3276057:34.0516543,66.3275162:34.0516543,66.327443:34.0516374,66.3273821:34.0515937,66.3273373:34.0515263,66.3272926:34.0514421,66.3271869:34.0513478,66.3270202:34.0512602,66.3268414:34.0511625,66.3265364:34.0510783,66.3262519:34.0510513,66.3261055:34.0510614,66.3259673:34.0511052,66.3257518:34.0511524,66.3256136:34.0511996,66.3255241:34.051213,66.3254347:34.0511591,66.3252883:34.0510749,66.3251461:34.0509638,66.3250444:34.0508593,66.3249265:34.0507516,66.3247639:34.0506404,66.3245688:34.0505629,66.3244427:34.0505292,66.3243614:34.0505326,66.3243086:34.0505764,66.3242801:34.0506471,66.3242882:34.0508897,66.3243045:34.051149,66.3243452:34.0512905,66.3243452:34.0513747,66.3243004:34.0514556,66.3241866:34.0514892,66.3240768:34.0515768,66.3239264:34.0516644,66.3238614:34.0517722,66.3238695:34.0519574,66.3239711:34.0520518,66.3240443:34.0521259,66.3240687:34.0521831,66.3240524:34.052237,66.3239793:34.0522875,66.3238532:34.052328,66.3237719:34.0523515,66.3236987:34.0523515,66.3236378:34.052328,66.3235565:34.0522707,66.323406:34.0522303,66.3232312:34.0521696,66.3230157:34.0520922,66.322784:34.0520416,66.3226377:34.0520282,66.322536:34.0520315,66.3224222:34.0520383,66.3222067:34.052072,66.3220034:34.0521663,66.321853:34.0522572,66.3217229:34.0524155,66.3215969:34.0525536,66.3215197:34.0526648,66.321479:34.0527793,66.321479:34.0529275,66.3214831:34.0530589,66.3214383:34.0533182,66.3213245:34.0534126,66.3212839:34.0534597,66.3212473:34.0535136,66.3211497:34.0536248,66.3210399:34.0537864,66.3209058:34.0540155,66.3207513:34.0542309,66.3206932:34.0544475,66.3206091:34.0546178,66.3205904:34.054699,66.3205998:34.0547532,66.3205671:34.0547687,66.3205157:34.0547764,66.3204223:34.0547803,66.3203476:34.0548267,66.3202635:34.0549428,66.3201841:34.0550356,66.3201468:34.0550821,66.3201141:34.0551053,66.3200347:34.0551208,66.319946:34.0551478,66.3198479:34.0551749,66.3197779:34.0552059,66.3196985:34.0552446,66.3195163:34.0552987,66.3192175:34.0553297,66.3190167:34.055349,66.3188392:34.0553839,66.3186197:34.0554342,66.3184189:34.0554419,66.3183208:34.0554342,66.3181294:34.0554264,66.3179659:34.0554225,66.3177978:34.0554612,66.317639:34.055527,66.3174148:34.055558,66.3172794:34.055585,66.3171066:34.0556315,66.3167844:34.0557205,66.3163781:34.0558443,66.3159298:34.055883,66.3157897:34.0559333,66.3156449:34.0560493,66.3154815:34.0561228,66.31536:34.0561731,66.3151966:34.0562002,66.3150472:34.0562157,66.3149164:34.056235,66.3148183:34.0562815,66.3147016:34.0563705,66.3145615:34.0564672,66.3143513:34.0565484,66.3141552:34.0566529,66.313903:34.0567535,66.3136555:34.0568193,66.3135247:34.0569779,66.3133566:34.0571056,66.3132072:34.0572333,66.3130624:34.0573764,66.313025:34.0575234,66.3130764:34.0577324,66.3132118:34.0578794,66.3133846:34.0580341,66.3135434:34.0581502,66.3135854:34.0582701,66.3136508:34.0583978,66.3137582:34.0586338,66.3138516:34.0587344,66.3138423:34.0588543,66.3137255:34.0589627,66.3135948:34.0590517,66.313478:34.0591948,66.3132399:34.0592645,66.3131465:34.0593341,66.3131231:34.0594192,66.3131605:34.0594966,66.3132399:34.0595469,66.3133426:34.0595933,66.3134734:34.0596513,66.3136602:34.0597519,66.3138423:34.0598912,66.3140198:34.0599415,66.3141458:34.0599957,66.3142533:34.0600614,66.3142533:34.0601272,66.3142159:34.0601814,66.3141038:34.0602201,66.3139964:34.0602858,66.3137769:34.0603245,66.3136135:34.0603748,66.3134967:34.0604483,66.3134313:34.0605644,66.3133426:34.0606302,66.3132912:34.0606882,66.3131978:34.0606998,66.3131138:34.060754,66.3130344:34.0608507,66.3130391:34.0610635,66.3131138:34.0613188,66.3131745:34.0614774,66.3131978:34.0615509,66.3131698:34.0615548,66.3130951:34.0615161,66.3129503:34.0614774,66.3127448:34.0614155,66.3125254:34.0613923,66.3124086:34.0614078,66.3123386:34.0614929,66.3123339:34.0615974,66.3123526:34.0617482,66.3123199:34.061872,66.3122638:34.0619572,66.3121704:34.0620345,66.3119416:34.0621506,66.3115493:34.0624253,66.3110029:34.0626342,66.3105499:34.0626535,66.3104612:34.0627309,66.3103725:34.0628199,66.3102978:34.0628857,66.3102557:34.0629746,66.310181:34.063052,66.3100689:34.0631255,66.3099382:34.0631797,66.3098354:34.0632687,66.3097701:34.0634195,66.309742:34.0635898,66.3097047:34.0637213,66.3096953:34.0638722,66.3097234:34.0640269,66.3097841:34.0641623,66.3098448:34.0642358,66.3098588:34.0643016,66.3098308:34.0644177,66.309742:34.0644989,66.3096253:34.0645569,66.3094899:34.0645956,66.3093451:34.0646072,66.3091349:34.0646188,66.3088874:34.0645741,66.3085292:34.0645606,66.3083585:34.064564,66.3082081:34.0646145,66.308082:34.064665,66.3080129:34.0647222,66.3079397:34.0647627,66.3078747:34.0647728,66.3078137:34.064702,66.3076795:34.0645673,66.307338:34.0644393,66.3070006:34.0642945,66.3065371:34.0642339,66.3063501:34.0642204,66.3062241:34.0642305,66.3061225:34.0642777,66.3059761:34.0643787,66.3057769:34.0644899,66.3056184:34.0646953,66.3053866:34.0648233,66.3052809:34.0648974,66.3052077:34.064931,66.3051468:34.0650152,66.3049435:34.0650557,66.304789:34.0650725,66.3046508:34.0651297,66.304476:34.065187,66.304354:34.0652409,66.3042442:34.0652981,66.3040369:34.0653217,66.3038946:34.0653386,66.3037279:34.0653183,66.3035246:34.0652746,66.303301:34.0652038,66.303053:34.0652005,66.3029189:34.065288,66.3028864:34.0654025,66.3029026:34.0661805,66.3030449:34.0662849,66.3030937:34.0663758,66.3031343:34.0664466,66.3031506:34.0665038,66.303114:34.0665476,66.3030449:34.0666049,66.3030002:34.0667901,66.3029107:34.0669383,66.302866:34.0670191,66.3028254:34.0671134,66.3027725:34.0672178,66.3026587:34.0672986,66.3025652:34.0673592,66.3025083:34.0674064,66.3024188:34.0673727,66.3023335:34.0673289,66.3022806:34.0672548,66.3022196:34.0671639,66.3021749:34.0670932,66.3021302:34.0670292,66.3020773:34.0669686,66.301996:34.0669282,66.3018659:34.0668743,66.3017318:34.0668642,66.3016627:34.0669147,66.3016139:34.0669955,66.3015651:34.067302,66.3014553:34.0673693,66.3014106:34.0674232,66.3013699:34.067467,66.3012846:34.0674872,66.3011626:34.0675007,66.3010162:34.0675344,66.3007154:34.067595,66.3003942:34.0676354,66.3002926:34.0677297,66.3001462:34.0678947,66.2999877:34.0679486,66.2999145:34.0680126,66.2997844:34.0680631,66.2996502:34.0680934,66.2995649:34.068154,66.299447:34.068218,66.2993779:34.0683258,66.2992925:34.0683797,66.2992071:34.0684032,66.2990486:34.0683224,66.2988819:34.0682483,66.2987274:34.0682247,66.298642:34.068255,66.2985566:34.0683258,66.2984591:34.0683864,66.2983412:34.0683763,66.2982029:34.0682618,66.297959:34.0681877,66.297837:34.0680361,66.2976582:34.0678812,66.2974468:34.0677768,66.2972638:34.0676994,66.2970687:34.0677027,66.296967:34.0677465,66.296841:34.0679014,66.296593:34.0680025,66.296471:34.0681271,66.2963979:34.0682012,66.2963491:34.0682382,66.2962922:34.068282,66.296219:34.0683056,66.2961174:34.0682685,66.2960116:34.0681102,66.2958572:34.0679418,66.2956295:34.0678307,66.2953774:34.0677263,66.2950603:34.0675714,66.2946619:34.0674535,66.2942269:34.0674165,66.2939748:34.0674434,66.2937147:34.0674805,66.2934423:34.0675007,66.2932919:34.0674939,66.2931211:34.0674939,66.2930439:34.0674468,66.2929341:34.0673053,66.2928812:34.0671471,66.2928772:34.0668911,66.2928162:34.0667295,66.2927755:34.0665947,66.2926454:34.0664466,66.292495:34.0662647,66.2923365:34.0661367,66.292243:34.0660727,66.2922023:34.0659852,66.2921942:34.0658303,66.292186:34.0657191,66.2921535:34.0655541,66.2920275:34.0654025,66.2918852:34.0653487,66.2917348:34.0653082,66.2915315:34.0652645,66.2912672:34.0652139,66.2909176:34.0651331,66.2906534:34.0650321,66.290442:34.0648536,66.2902346:34.064564,66.290137:34.0643686,66.2900598:34.0642507,66.2899826:34.0641362,66.2899175:34.064015,66.2898484:34.0639173,66.2897712:34.0637422,66.2896533:34.0635772,66.289576:34.0633818,66.2895272:34.0632471,66.2895232:34.0631629,66.2895272:34.0630956,66.2894866:34.0629878,66.2894337:34.0628362,66.2893158:34.0627183,66.289202:34.0626544,66.2891369:34.0625803,66.2890719:34.0624354,66.2890719:34.0623041,66.2890678:34.062203,66.2890556:34.0620953,66.2890068:34.0620178,66.2889174:34.0619639,66.2888402:34.0618831,66.2887954:34.0617181,66.2887385:34.0615699,66.2886572:34.0615193,66.288584:34.0614857,66.2884865:34.0614419,66.2884092:34.0613947,66.2883117:34.0613543,66.2882669:34.061223,66.2882344:34.0610074,66.2882019:34.0608289,66.2881938:34.0606033,66.2882263:34.0604045,66.2883198:34.0602563,66.2883523:34.0600812,66.2883564:34.0599094,66.2883767:34.0597983,66.2884539:34.0597276,66.2885881:34.0596939,66.2888727:34.0596097,66.2891125:34.0595457,66.2892223:34.0594345,66.289328:34.059192,66.2894906:34.0589125,66.2896289:34.0587373,66.2896695:34.0585083,66.2896329:34.0582894,66.2895923:34.0580604,66.2894175:34.057801,66.2891939:34.057508,66.2889337:34.0571779,66.2887385:34.0569657,66.2885718:34.0568916,66.2884987:34.0568377,66.2884174:34.0567771,66.2883482:34.0566693,66.2882669:34.0565817,66.2882141:34.0564706,66.2881694:34.0563931,66.2881002:34.0562247,66.287958:34.056026,66.2878319:34.0554938,66.2875067:34.0550728,66.2873237:34.0548943,66.2872424:34.0547629,66.287092:34.054581,66.2868603:34.054389,66.2866042:34.0542375,66.2863684:34.0541095,66.2861732:34.0539343,66.2860309:34.0537491,66.2858439:34.0534358,66.285539:34.0533179,66.285352:34.0532337,66.2851568:34.0530215,66.2848194:34.0528295,66.2845958:34.0524994,66.2843925:34.0522973,66.2842868:34.0520817,66.2842055:34.0519167,66.2841893:34.0517112,66.2841486:34.0515866,66.284108:34.0514653,66.2840266:34.051307,66.2838478:34.0511083,66.2836242:34.0508523,66.2833762:34.0505356,66.2831566:34.0501752,66.2828883:34.049775,66.2826347:34.0492697,66.2824274:34.0490238,66.2823339:34.0486095,66.28209:34.0484646,66.2820412:34.0481918,66.2820818:34.0479998,66.2821388:34.0478684,66.2821753:34.0477573,66.2821713:34.0474676,66.2820168:34.0472284,66.281842:34.0470532,66.2817078:34.0468713,66.2816021:34.0467534,66.2815696:34.0466928,66.2815574:34.0466557,66.2815371:34.0466793,66.2814598:34.04674,66.2813216:34.0467837,66.2812281:34.0468848,66.2809557:34.0469185,66.2808337:34.0469421,66.2807565:34.0469151,66.2807158:34.0468376,66.2806671:34.0461538,66.2803093:34.0460528,66.2802361:34.046009,66.2801873:34.0460056,66.2800735:34.046009,66.2798621:34.0459888,66.2797401:34.0459685,66.2796751:34.0459012,66.2795978:34.0457024,66.2794555:34.045534,66.2793458:34.0454296,66.2792604:34.0452847,66.2791669:34.0451163,66.27911:34.044958,66.2791059:34.0447929,66.2790937:34.0446346,66.2790978:34.0445099,66.2791059:34.0443887,66.2791425:34.044291,66.2792116:34.0441731,66.2792279:34.0440787,66.2791994:34.0440046,66.2791669:34.0439204,66.279171:34.0438059,66.2792645:34.0436071,66.2793458:34.0434151,66.2793376:34.0431995,66.2793011:34.0429873,66.2791872:34.0426976,66.2790327:34.0424415,66.2788701:34.0423135,66.2787888:34.0422495,66.2787238:34.0421788,66.2786425:34.0420845,66.2784595:34.0419295,66.2782684:34.041751,66.2780652:34.0416768,66.2779879:34.0415893,66.2779229:34.0414815,66.2778781:34.0413501,66.2778253:34.041249,66.2777237:34.0410907,66.277557:34.0409526,66.2774187:34.0408414,66.2773293:34.0407134,66.2772805:34.0405854,66.2772358:34.0404439,66.2771342:34.040326,66.2769797:34.0401676,66.2768171:34.0400059,66.2766504:34.039915,66.2765975:34.0398004,66.2765609:34.0396252,66.2765365:34.0394871,66.2764796:34.0392883,66.2763617:34.0389852,66.2762682:34.0388437,66.2762235:34.0387527,66.2761625:34.0386483,66.2760609:34.0385304,66.2759186:34.0383754,66.2757763:34.0382608,66.275634:34.0380284,66.2753291:34.0378397,66.2751177:34.037678,66.274951:34.0375163,66.2748372:34.0374085,66.2747233:34.0372535,66.2745445:34.0370312,66.2742721:34.0369908,66.2742152:34.036947,66.2741867:34.0368829,66.2741623:34.0367886,66.2741664:34.0366437,66.2741257:34.0365023,66.2740403:34.0363473,66.2739062:34.0362327,66.2738249:34.0361519,66.2737192:34.0361114,66.2736541:34.0360946,66.2735891:34.0360778,66.2734874:34.0360643,66.2734346:34.0359666,66.273333:34.0358554,66.2732191:34.0356971,66.2730809:34.0355522,66.2729874:34.0353703,66.2728776:34.0352524,66.2728044:34.0351715,66.2727435:34.0351109,66.2727191:34.0350199,66.2727028:34.034966,66.2726865:34.0349458,66.2726337:34.0349795,66.2725849:34.0350199,66.2725361:34.0351243,66.2725524:34.0353399,66.2726012:34.0355522,66.2726215:34.0357779,66.2726256:34.0359565,66.2726378:34.036098,66.2726784:34.0363035,66.2727353:34.0366033,66.2727719:34.0368358,66.2728288:34.0370413,66.2729183:34.037112,66.2729549:34.0371895,66.2729305:34.0372468,66.2728898:34.0372636,66.2728044:34.0372299,66.2727353:34.0371592,66.2727313:34.0370851,66.2727475:34.0370143,66.2727109:34.0368931,66.272593:34.0367549,66.2723735:34.0366505,66.2722556:34.0365359,66.2721336:34.0364483,66.2720117:34.0364281,66.2719426:34.0364483,66.2718653:34.0364854,66.2718043:34.0364854,66.2717149:34.0364618,66.2716255:34.0363271,66.2714506:34.0362496,66.2713815:34.0361755,66.2713287:34.0360277,66.2712613:34.0356976,66.270993:34.0356302,66.2709767:34.035546,66.2709645:34.0354382,66.2709482:34.0353506,66.2709157:34.0351787,66.2707287:34.0350406,66.2706149:34.0348418,66.270436:34.0345858,66.2702368:34.0342556,66.2700091:34.0340737,66.2698912:34.0339356,66.2698343:34.0337031,66.2697083:34.033538,66.2695944:34.033164,66.2692977:34.0329619,66.2691228:34.0328911,66.2690334:34.0328777,66.2689399:34.032908,66.2689033:34.0329754,66.2689155:34.0330933,66.2689887:34.0335313,66.2692367:34.0336189,66.2692692:34.0336896,66.2692692:34.0337166,66.2692285:34.0336896,66.2691513:34.0335953,66.2689765:34.0334572,66.2687244:34.0333729,66.2685781:34.0333157,66.2684724:34.0332449,66.2683301:34.0331674,66.2682366:34.0330731,66.2681309:34.032945,66.268013:34.0328979,66.267952:34.0328676,66.2678585:34.0328507,66.267704:34.0328204,66.2675739:34.0328069,66.2674682:34.0328575,66.2673666:34.032945,66.2673137:34.0330428,66.2673137:34.0331337,66.2673422:34.0332045,66.2673869:34.0332752,66.2674845:34.03341,66.2675576:34.0335279,66.2675658:34.033629,66.2675536:34.033693,66.267517:34.0337772,66.2674723:34.033949,66.2674804:34.0340333,66.2675089:34.0341209,66.267578:34.0342253,66.2675902:34.0344342,66.2676024:34.0345049,66.2675861:34.0345622,66.2675454:34.0346801,66.2674194:34.0347812,66.2673381:34.0348722,66.2673096:34.0350002,66.2673503:34.0351922,66.2674357:34.0352731,66.267456:34.0353405,66.2674357:34.0353775,66.2673788:34.0353708,66.2672771:34.0352866,66.2671023:34.0351821,66.2669234:34.035081,66.2667689:34.0350305,66.2666063:34.0350069,66.266525:34.0350069,66.2664356:34.035044,66.2663095:34.0350709,66.2661998:34.0350911,66.2660859:34.0351046,66.265903:34.0351114,66.2657322:34.035108,66.2655615:34.0350911,66.2653013:34.0350541,66.265098:34.0350069,66.2649151:34.0349193,66.2646833:34.0348452,66.2644191:34.0348082,66.2642361:34.0347778,66.2640776:34.0347542,66.263976:34.0347037,66.2638987:34.03467,66.2638337:34.0346431,66.2637361:34.0346397,66.2636467:34.0346397,66.2635491:34.0346296,66.2634637:34.0346161,66.2634027:34.0345555,66.2633011:34.0344948,66.2631344:34.0344106,66.2629189:34.0343028,66.2626872:34.0342725,66.2625896:34.0342624,66.2624677:34.0342691,66.2623498:34.0342523,66.2622034:34.0342085,66.2620205:34.0341141,66.261679:34.0339861,66.2613659:34.0338783,66.2610285:34.0337806,66.2608171:34.0336391,66.2605691:34.0335447,66.2604065:34.0335043,66.2602845:34.0334673,66.2601666:34.0334908,66.2600325:34.033538,66.259882:34.0335751,66.2597357:34.0336222,66.2595161:34.0336458,66.2593251:34.0336593,66.2590567:34.0336458,66.2587518:34.0336323,66.2586177:34.0336492,66.2584185:34.0336863,66.2582558:34.0337435,66.2580648:34.0337671,66.257894:34.0337907,66.2577355:34.0338547,66.2575281:34.0339726,66.2571704:34.0340905,66.2567841:34.0341984,66.2564914:34.0343196,66.2562922:34.0343971,66.2561581:34.0345218,66.2560646:34.0346902,66.2560158:34.0348789,66.2560158:34.0350372,66.2560646:34.0352293,66.2560686:34.0355089,66.2561296:34.0356066,66.2561743:34.0356875,66.2561703:34.0357616,66.2561662:34.0358458,66.2561947:34.0359233,66.2562068:34.0360008,66.2561743:34.0360749,66.2561743:34.0362063,66.256219:34.0363276,66.2562597:34.0364286,66.2562475:34.0365095,66.2561987:34.0365735,66.2561418:34.0366544,66.2561052:34.0367352,66.2560442:34.0367858,66.2559711:34.0368329,66.2558775:34.0368632,66.255719:34.0368565,66.255593:34.03687,66.2555198:34.0369239,66.2554425:34.0369946,66.2553572:34.0371597,66.2552067:34.0373585,66.255097:34.0374292,66.2550645:34.0374932,66.2550157:34.0375303,66.2549506:34.0375977,66.2548856:34.0377021,66.254853:34.0378503,66.2548246:34.0379615,66.2548002:34.0380424,66.2548165:34.0381097,66.2548449:34.0381738,66.2548409:34.0382209,66.2547758:34.0382883,66.2547473:34.0383826,66.254727:34.0384702,66.2546864:34.0385376,66.2546254:34.0386252,66.2544912:34.0386926,66.2543408:34.0387465,66.2542636:34.0388172,66.2541619 && last_edit_changeset -> 60829864 || surface -> gravel || iso_country_code -> AFG || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> Qaderi || ref -> V220214 || condition -> fair || last_edit_time -> 1531907624000 || last_edit_user_id -> 8164544 || name -> سرک ولسوالی از شیخمیران الی لیلی مجنون || lanes -> 1 || width -> 3 || highway -> tertiary || last_edit_version -> 1\n608903805000000 && 34.1154094,66.5064452:34.1153535,66.5063416:34.1149659,66.5058599:34.1144702,66.5051891:34.1142428,66.504802:34.1141832,66.5046759:34.1141198,66.5045454:34.1140229,66.5044463:34.1138142,66.5042843:34.1136614,66.5041897:34.1135868,66.5041222:34.1134154,66.5038251:34.1133483,66.503735:34.1132477,66.5036045:34.1130613,66.5033974:34.1129122,66.5032488:34.1127408,66.5030463:34.1126029,66.5028482:34.1124985,66.5026321:34.1124463,66.502407:34.1123867,66.5020964:34.1123047,66.5017542:34.1122301,66.5015336:34.112096,66.5013041:34.1118984,66.5010565:34.1118313,66.5009259:34.1117456,66.5007413:34.1116822,66.5005162:34.1116114,66.5002551:34.1114772,66.499958:34.1112871,66.4996519:34.111153,66.4994538:34.1110001,66.4992782:34.110948,66.4992242:34.110892,66.4991432:34.1107131,66.4988325:34.1105193,66.4985894:34.1104336,66.4985174:34.1103889,66.4984814:34.1103031,66.4983553:34.1100348,66.4979817:34.1098484,66.4976801:34.10958,66.4971894:34.1093899,66.4968787:34.1091812,66.4966086:34.1089575,66.4963115:34.1086072,66.4959289:34.1081673,66.4955147:34.1080108,66.4954021:34.1078505,66.4953166:34.10772,66.4952086:34.1076716,66.4950735:34.1076231,66.4948484:34.1075598,66.4946233:34.1074852,66.4944252:34.1073808,66.4942902:34.1071982,66.4941461:34.107023,66.4939255:34.1068217,66.4936149:34.1066875,66.4934438:34.1065459,66.4932413:34.1064229,66.4930882:34.10627,66.4929306:34.1060949,66.4927641:34.1058414,66.4926695:34.1057445,66.4926425:34.1056401,66.492611:34.1055693,66.492548:34.1054761,66.4924489:34.1053344,66.4923004:34.1049393,66.4918502:34.1047753,66.4916746:34.1045964,66.4915576:34.1044212,66.4914945:34.104164,66.4913415:34.1038322,66.4911794:34.1034632,66.4910894:34.1033812,66.4909993:34.1033588,66.4909408:34.1033663,66.4907923:34.1034147,66.4905987:34.1034557,66.4904276:34.1034893,66.4903556:34.1035154,66.490252:34.1035191,66.490153:34.1034632,66.4900044:34.1033961,66.4898379:34.1033141,66.4896848:34.1032656,66.4895678:34.1032283,66.4894372:34.1032358,66.4892796:34.1032246,66.4890861:34.1032358,66.4888925:34.1032582,66.4887304:34.1033625,66.4885774:34.103452,66.4884828:34.1034967,66.4884468:34.1035266,66.4883973:34.1035266,66.4883343:34.1034669,66.4882847:34.1033588,66.4882262:34.1031575,66.4881227:34.1030308,66.4880732:34.1029488,66.4880101:34.1028705,66.4879066:34.1027885,66.487722:34.1027363,66.4872898:34.1027288,66.4868216:34.1027587,66.4866146:34.1027736,66.4864975:34.1027587,66.4864075:34.1027326,66.4863805:34.1026953,66.4863715:34.1025984,66.486448:34.1024865,66.4864705:34.1023188,66.4864255:34.1021436,66.4864075:34.1019982,66.486394:34.101905,66.4863805:34.1018155,66.4862949:34.1016292,66.4861058:34.1013496,66.4859033:34.101193,66.4857907:34.1010961,66.4856872:34.1010476,66.4855521:34.1010029,66.4854171:34.1009917,66.4850074:34.100947,66.4844132:34.1009507,66.4842511:34.1009694,66.4841656:34.1010365,66.484017:34.1010663,66.4839405:34.1010775,66.4838234:34.1010849,66.4835713:34.1010849,66.4829005:34.1010849,66.4827745:34.1010924,66.4826124:34.1011408,66.4823378:34.1011744,66.4820902:34.1011744,66.4818741:34.1011483,66.4815905:34.1010998,66.4811583:34.1010737,66.4810458:34.1010476,66.4809557:34.1010178,66.4808567:34.1009954,66.4806676:34.1009843,66.480447:34.1009805,66.4803525:34.1009544,66.4802625:34.100865,66.4800644:34.1008314,66.4799518:34.1008165,66.4798528:34.1008277,66.4797222:34.1008463,66.4795827:34.1008463,66.4794836:34.1008128,66.4793576:34.1007494,66.4792631:34.1006376,66.4791955:34.100522,66.4791055:34.1003878,66.4789839:34.1002723,66.4788399:34.0999815,66.4782636:34.0998361,66.4780205:34.0996907,66.4777729:34.0994671,66.4775147:34.0993515,66.4772822:34.0992024,66.4770797:34.0991316,66.4768906:34.0990533,66.476706:34.0989675,66.4765214:34.0988669,66.4764134:34.0987289,66.4762783:34.0985724,66.4761388:34.0983972,66.4760217:34.0982406,66.4759857:34.0980989,66.4759722:34.0980505,66.4758822:34.0980766,66.4758146:34.0981288,66.4757606:34.0981586,66.4756526:34.0982145,66.475531:34.0983077,66.475486:34.0984643,66.475459:34.0985276,66.4754365:34.098591,66.475387:34.0986059,66.4752654:34.0986357,66.4749818:34.0986171,66.4746487:34.0986059,66.4745136:34.0985015,66.4742255:34.0983785,66.4738158:34.0982406,66.4731721:34.0981138,66.4725418:34.0980244,66.4721907:34.0979796,66.4719521:34.0979685,66.471709:34.0978566,66.4714839:34.0976889,66.4712453:34.0975323,66.4710427:34.0973757,66.4707366:34.0972564,66.4704935:34.0972229,66.4704349:34.0971371,66.4703629:34.0970514,66.4702369:34.0969284,66.4700343:34.096824,66.4698812:34.0967532,66.4697642:34.0967233,66.4696651:34.0966525,66.469359:34.0966078,66.4691789:34.0965742,66.4690799:34.0964512,66.4688548:34.0962797,66.4684136:34.096015,66.4676843:34.0959479,66.4674322:34.095892,66.4672837:34.0958324,66.4671981:34.0957354,66.4671711:34.0956758,66.4671531:34.0956422,66.4671126:34.0956087,66.4670271:34.0955639,66.466811:34.095385,66.4663608:34.0951613,66.4658881:34.0950793,66.465762:34.0949078,66.4654874:34.0948295,66.4653029:34.0947773,66.4651813:34.094714,66.4650327:34.0946319,66.4649247:34.0945052,66.4648302:34.0943486,66.4647671:34.0941398,66.4647446:34.0939982,66.4647041:34.093808,66.4646096:34.093726,66.4645646:34.0936105,66.4645375:34.0935061,66.464479:34.0933271,66.4643124:34.0931034,66.4641549:34.0928462,66.4639703:34.092753,66.4638668:34.0926971,66.4637182:34.0925927,66.4634886:34.0925069,66.4632455:34.0924398,66.4630879:34.0922087,66.4626963:34.0919813,66.4623541:34.0918843,66.4621966:34.0917576,66.4620795:34.0915898,66.46194:34.0915376,66.461895:34.0915339,66.4618364:34.0915302,66.4617464:34.0915525,66.4616113:34.0915786,66.4614223:34.0915674,66.4612467:34.0915302,66.4610846:34.0914891,66.4608956:34.0914332,66.4606885:34.0913587,66.4604499:34.0912282,66.4601618:34.0910455,66.4597746:34.0908479,66.4593604:34.090695,66.4590678:34.0905459,66.4588472:34.0903856,66.4586852:34.0902029,66.4584871:34.0901246,66.4584375:34.0900389,66.4583745:34.0899979,66.458325:34.0899606,66.458208:34.0899457,66.4581089:34.089927,66.4580369:34.0899121,66.4579559:34.0898338,66.4577713:34.0897704,66.4575642:34.0896959,66.4573031:34.0896847,66.457141:34.0896772,66.4570015:34.0896922,66.4568574:34.0897481,66.4567314:34.0898376,66.4565558:34.0899312,66.4564028:34.0900277,66.4562452:34.0900687,66.4561641:34.0900948,66.4561056:34.0900948,66.4560516:34.0900762,66.4560066:34.0900128,66.4559795:34.0899494,66.455948:34.0898935,66.4558985:34.089845,66.4557995:34.0898264,66.4556554:34.0898003,66.4553988:34.0897928,66.4552052:34.0898077,66.4550792:34.0898711,66.4550071:34.0899904,66.4549801:34.0903968,66.4549846:34.0907082,66.4549611:34.090851,66.4548906:34.0910555,66.4547064:34.0912632,66.4545379:34.091578,66.4543576:34.0924836,66.453852:34.0927756,66.4537149:34.0930677,66.4535777:34.0932105,66.4535503:34.0934215,66.453515:34.0935383,66.453468:34.0936584,66.4533269:34.0938207,66.4530878:34.0939083,66.4528801:34.0939375,66.4528135:34.0940706,66.452692:34.0941615,66.4525626:34.0943295,66.4524018:34.0944333,66.4522528:34.0945047,66.4520294:34.0945599,66.4515905:34.0945891,66.4504618:34.0945566,66.4498544:34.0945372,66.4495251:34.0945372,66.4492312:34.0945534,66.4488903:34.0945956,66.4486669:34.0946378,66.4484788:34.0946508,66.4483808:34.0946378,66.4482867:34.0945923,66.4480908:34.0945015,66.4477655:34.0944203,66.4475029:34.0943327,66.4471619:34.0942743,66.4468798:34.0942451,66.4465898:34.094248,66.446392:34.0943227,66.4459609:34.0944395,66.4454789:34.0944784,66.4453064:34.0944979,66.4451771:34.0945077,66.4449498:34.0945174,66.444648:34.0945498,66.4441699:34.0945661,66.4439348:34.0946505,66.4435233:34.0947413,66.4430922:34.0947608,66.4429315:34.0947933,66.4428021:34.0948679,66.4425827:34.0950594,66.4421241:34.0952509,66.4417989:34.0954618,66.4415441:34.0955786,66.441454:34.0956987,66.4413756:34.0958383,66.4412894:34.0959713,66.4411248:34.0960979,66.4408544:34.096218,66.44058:34.0964192,66.4401528:34.096575,66.4397923:34.0966788,66.4395375:34.09676,66.4391731:34.0968249,66.43884:34.0968476,66.4387145:34.0969255,66.438448:34.0970456,66.4380953:34.0970618,66.4379778:34.0970585,66.4378014:34.0970163,66.4375427:34.0969612,66.4372057:34.0969222,66.4370215:34.09688,66.4368647:34.0968573,66.4367472:34.0968995,66.4365826:34.0969287,66.4363905:34.0969449,66.4361985:34.0969287,66.4358771:34.0968898,66.4356224:34.0968184,66.4353481:34.0967697,66.435156:34.0967535,66.4349993:34.0967437,66.4346113:34.0967048,66.4343918:34.0965912,66.4339411:34.096523,66.4337726:34.0964646,66.433655:34.0964387,66.4335727:34.0964062,66.4334434:34.0963608,66.4332122:34.0962634,66.4329221:34.096179,66.4327262:34.0961011,66.4325538:34.0960525,66.4324205:34.0960265,66.4322833:34.0959876,66.4320991:34.0959713,66.4319737:34.0959876,66.4317817:34.09602,66.4316171:34.0960395,66.4315113:34.0960395,66.431378:34.0960265,66.4312448:34.0960233,66.431135:34.0960395,66.4310645:34.0960849,66.4310018:34.0961141,66.430943:34.0961304,66.430845:34.0961336,66.4307118:34.0961368,66.4305629:34.0961725,66.4303591:34.0961855,66.4301474:34.0961758,66.4299868:34.0961368,66.4298026:34.0960362,66.4295243:34.0959843,66.429344:34.0959551,66.429152:34.0959616,66.429007:34.0959648,66.4289208:34.0959584,66.4288385:34.0959032,66.4286778:34.0958577,66.4285171:34.0958383,66.4283643:34.095887,66.4281213:34.0959389,66.427941:34.0959908,66.4276275:34.0960038,66.4274825:34.0959876,66.4273571:34.0959162,66.4271925:34.095861,66.4270122:34.0957831,66.4267966:34.0957279,66.4266007:34.0957085,66.4264047:34.0957085,66.4262715:34.0957377,66.426099:34.0957539,66.4258521:34.0957377,66.4256091:34.0957117,66.4254289:34.095676,66.4252956:34.0955981,66.4251585:34.0954521,66.4249939:34.095173,66.4246607:34.0949685,66.4244452:34.0948582,66.424355:34.0948225,66.4242492:34.0947673,66.4241238:34.0947,66.4239555:34.0946553,66.4238385:34.0946478,66.423726:34.0946478,66.4236089:34.0946702,66.4234874:34.0946627,66.4233253:34.0946143,66.4231407:34.0945472,66.4229877:34.0939693,66.4221503:34.0938388,66.4220513:34.0937382,66.4219882:34.0936748,66.4218937:34.0936114,66.4217677:34.0935592,66.4216686:34.0934697,66.4215831:34.0933803,66.4214885:34.0932908,66.421331:34.0931864,66.4211194:34.0931752,66.4210384:34.0931976,66.4209483:34.0932945,66.4208673:34.093425,66.4207682:34.093507,66.4206917:34.0935928,66.4205477:34.0936711,66.4203856:34.0937605,66.420237:34.093809,66.420174:34.0938612,66.420174:34.0939618,66.420201:34.0940588,66.4202595:34.0941482,66.4203496:34.0942079,66.4204081:34.0942564,66.4204126:34.0942862,66.4203676:34.094316,66.4203001:34.0943309,66.420219:34.0943458,66.4200299:34.0943533,66.4198679:34.0943719,66.4197328:34.0944241,66.4196203:34.0945173,66.4194852:34.0947373,66.4191926:34.0948156,66.4190621:34.094905,66.418999:34.0950132,66.418954:34.0951138,66.418828:34.0951884,66.4187064:34.0952517,66.4186254:34.0953151,66.4184813:34.0953673,66.4183463:34.0954083,66.4182607:34.0953971,66.4181932:34.0953673,66.4181572:34.0953263,66.4181527:34.0952592,66.4182247:34.0951623,66.4183733:34.0951101,66.4184453:34.0950132,66.4184858:34.0949349,66.4185353:34.0948678,66.4185488:34.094782,66.4184948:34.0946963,66.4184543:34.0945173,66.4184138:34.0943719,66.4183913:34.094152,66.4184678:34.0939358,66.4185939:34.0938351,66.4186209:34.0937904,66.4185804:34.0937493,66.4184813:34.0937009,66.4182562:34.0936412,66.4180446:34.0936189,66.4179231:34.093604,66.417752:34.0936151,66.4175494:34.0936077,66.4172973:34.0935779,66.4170542:34.093548,66.4167841:34.093507,66.4165275:34.0934586,66.4162439:34.0934548,66.4161088:34.0934847,66.4159198:34.0935406,66.4157667:34.0935853,66.4156677:34.0936114,66.4155731:34.0936189,66.4154606:34.0936077,66.4153525:34.0935667,66.4153165:34.0935182,66.415339:34.0934623,66.415366:34.0933915,66.415375:34.0933355,66.4153976:34.0932833,66.4154516:34.0932349,66.4155461:34.093123,66.4157802:34.0930373,66.4159378:34.0930037,66.4159873:34.092959,66.4159783:34.0928993,66.4159468:34.0928658,66.4158838:34.0928658,66.4157757:34.0928882,66.4156452:34.0929105,66.4155191:34.0929143,66.4153886:34.0929068,66.4152625:34.0928509,66.4150374:34.0928061,66.4148168:34.0927204,66.4146052:34.0926346,66.4144792:34.0924893,66.4143576:34.0924147,66.4142631:34.0923662,66.4141505:34.0923401,66.4138984:34.0923476,66.4137544:34.09237,66.4136193:34.0923737,66.4135158:34.0923625,66.4134618:34.0923066,66.4134122:34.0922544,66.4133672:34.0921873,66.4132637:34.0921127,66.4131421:34.0920829,66.4130251:34.0921053,66.4128225:34.09215,66.4126154:34.0921835,66.4125029:34.0922096,66.4124353:34.0922357,66.4123543:34.0922246,66.4123048:34.09215,66.4122418:34.0920903,66.4121517:34.0920792,66.4120797:34.0920903,66.4119582:34.0920903,66.4118006:34.0921015,66.4117331:34.092068,66.4115755:34.0920381,66.4114404:34.0919822,66.4112604:34.0919524,66.4111433:34.0919375,66.4110038:34.0919412,66.4107562:34.091951,66.4105479:34.0919217,66.4104029:34.091899,66.4102579:34.0918925,66.4101324:34.0919023,66.410007:34.0919153,66.4099208:34.0919639,66.4097053:34.0919899,66.4095446:34.0920224,66.40938:34.0920483,66.4092546:34.0920613,66.4091919:34.0920386,66.4091096:34.0919737,66.4089332:34.0918731,66.4087255:34.0917465,66.4084472:34.0916069,66.4081455:34.0914349,66.4077575:34.0913505,66.4075615:34.0912402,66.4072166:34.0911623,66.406958:34.0910422,66.4066249:34.0909611,66.4063936:34.090828,66.4061546:34.0907274,66.4059939:34.090617,66.4058606:34.0904775,66.4057352:34.0903768,66.4056647:34.0902405,66.4054883:34.0900393,66.405214:34.0898473,66.4048682:34.0896201,66.4043548:34.0893183,66.403763:34.0890456,66.4032849:34.0887957,66.4029087:34.0885945,66.4025129:34.0883998,66.4021758:34.0883154,66.4020739:34.0881693,66.4019093:34.0880836,66.4017905:34.0880127,66.4015969:34.0879493,66.4013809:34.0879121,66.4012323:34.0877965,66.4010612:34.0877145,66.4009352:34.0876809,66.4008631:34.0875952,66.4007776:34.0875094,66.4007101:34.0873864,66.400575:34.0873043,66.4004535:34.0872,66.4003139:34.0871403,66.4002509:34.087062,66.4002284:34.0867227,66.4002374:34.0866258,66.4002239:34.0865624,66.4001924:34.0865102,66.4001248:34.0864543,66.4000033:34.0862567,66.3993595:34.0861187,66.3990129:34.085992,66.3986302:34.0859509,66.3984727:34.085936,66.3983016:34.0859211,66.3981035:34.0858876,66.3978559:34.0858428,66.3976488:34.0857123,66.3972662:34.0855408,66.3967169:34.0854215,66.3962758:34.0853879,66.3961632:34.0853693,66.3960057:34.0853358,66.3956905:34.0852612,66.3952629:34.0851754,66.3949387:34.0851158,66.3947992:34.0850114,66.3946551:34.0848473,66.3943085:34.0846833,66.3940293:34.0845469,66.3937354:34.084482,66.3935277:34.08431,66.3928223:34.0841802,66.3922854:34.0841445,66.3921717:34.0840925,66.392062:34.0840146,66.3919601:34.0839335,66.3918268:34.0838653,66.3917014:34.0838166,66.3915838:34.0837744,66.3914741:34.0837322,66.3913291:34.0836121,66.3910861:34.0834466,66.3907726:34.0832519,66.3904042:34.083135,66.3901769:34.0830571,66.3900554:34.0829695,66.3899457:34.0829045,66.3898085:34.0828007,66.3895302:34.0826643,66.3892598:34.0825475,66.3889698:34.0824599,66.3887151:34.0823982,66.3884878:34.0822911,66.3881468:34.0822164,66.3878842:34.0821223,66.3876373:34.0820184,66.3873904:34.0819632,66.3872454:34.0818723,66.3870142:34.0817587,66.3868104:34.0815737,66.3865361:34.0813887,66.386297:34.0811095,66.3860109:34.0809753,66.385874:34.0808784,66.3856444:34.0808523,66.3855453:34.080856,66.3854058:34.080856,66.3853472:34.0808523,66.3852887:34.0808187,66.3852572:34.0807031,66.3851447:34.0805801,66.3850276:34.0804682,66.3848926:34.080416,66.3847665:34.0803787,66.3845954:34.0803638,66.3844289:34.0803564,66.3843208:34.0803675,66.3842578:34.0803675,66.3842083:34.0803526,66.3841362:34.0803153,66.3840282:34.0802557,66.3838751:34.080237,66.3837581:34.0801923,66.383533:34.0801401,66.3832674:34.080155,66.3831548:34.0802035,66.3830108:34.0802818,66.3826776:34.0803042,66.3825516:34.0803489,66.382304:34.0804309,66.3821059:34.0805167,66.3819168:34.0806509,66.3817143:34.0807479,66.3815522:34.0808187,66.3814576:34.0808635,66.3813271:34.0808858,66.381165:34.0808746,66.381048:34.0808374,66.3809219:34.0807777,66.3808634:34.0806957,66.3808364:34.0805577,66.3808814:34.0803564,66.3809715:34.080237,66.3809895:34.0801177,66.3809715:34.0799984,66.3809084:34.0799276,66.3808184:34.0798604,66.3807419:34.079812,66.3806383:34.079771,66.3805123:34.079756,66.3804087:34.0797299,66.3803322:34.0796405,66.3802286:34.079495,66.3800216:34.0793049,66.3796794:34.0791893,66.3794363:34.0790812,66.3792112:34.0790439,66.3791032:34.0790103,66.3788421:34.0789917,66.3786935:34.0789395,66.3786125:34.07885,66.378626:34.0787568,66.3787025:34.0786486,66.3788241:34.0785218,66.3790627:34.0784174,66.3793328:34.0783652,66.3793733:34.0783093,66.3793688:34.0782757,66.3792923:34.0782646,66.3791797:34.0782496,66.3790807:34.0782198,66.3789816:34.0781863,66.3788601:34.0781751,66.3787115:34.0782198,66.3784774:34.0783056,66.3782884:34.0784659,66.3780363:34.0785666,66.3778427:34.0786337,66.3776536:34.0786374,66.3775546:34.0786151,66.377424:34.0785479,66.3772169:34.0785144,66.3770639:34.0785144,66.3768253:34.0785256,66.3765912:34.0785144,66.3764066:34.0784808,66.376294:34.0783839,66.3760825:34.0782981,66.3758934:34.0782347,66.3757133:34.0781452,66.3755602:34.0780185,66.3754072:34.0779141,66.3752811:34.0778768,66.3752271:34.0778544,66.3751371:34.0778656,66.3750425:34.0779029,66.3749345:34.0779625,66.3748264:34.0780185,66.3746464:34.0780334,66.3745518:34.0780073,66.3744483:34.0779774,66.3743673:34.077929,66.3742727:34.0778693,66.3741692:34.0778581,66.3740791:34.077888,66.3738405:34.0779066,66.373737:34.0779103,66.373647:34.077888,66.3735254:34.0778134,66.3730032:34.0778134,66.3728861:34.0777985,66.3728096:34.0777724,66.3727511:34.0777164,66.3727241:34.0776642,66.3727466:34.0776008,66.3728186:34.0775113,66.3729042:34.0774181,66.3729942:34.077351,66.3730347:34.0772802,66.3730257:34.0772578,66.3729717:34.0772503,66.3728726:34.0772652,66.3727196:34.0772615,66.372553:34.0772354,66.372436:34.0772056,66.3723144:34.0771907,66.3721929:34.0771907,66.3720713:34.077213,66.3718147:34.0772354,66.3715941:34.0772242,66.3714951:34.0771981,66.371414:34.0771571,66.3713105:34.0770975,66.3711935:34.0770154,66.3710629:34.0769744,66.3708828:34.0769371,66.3706262:34.0768961,66.3703201:34.0768364,66.3701085:34.0767283,66.3698879:34.0765829,66.3697124:34.0764412,66.3696088:34.0762436,66.3695368:34.0761205,66.3694512:34.0760273,66.3693567:34.0758259,66.3692892:34.075412,66.3692937:34.0752927,66.3693252:34.075151,66.3692982:34.0749795,66.3692126:34.0747371,66.3689515:34.0745991,66.368794:34.0744462,66.3685779:34.0741852,66.3682898:34.0740696,66.3681682:34.0739838,66.3681052:34.0739093,66.3680242:34.0738123,66.3677856:34.073652,66.3674074:34.0736184,66.3672769:34.0735774,66.3669617:34.0735177,66.3667682:34.0734655,66.3666106:34.0733984,66.36648:34.0732791,66.366318:34.0731038,66.3661019:34.0729584,66.3659308:34.0728987,66.3658453:34.0728391,66.3657417:34.0724923,66.365251:34.072399,66.3651745:34.0723095,66.3651025:34.0722238,66.3650259:34.0721492,66.3648684:34.0721156,66.3647378:34.0721044,66.3644992:34.0721305,66.3640761:34.0721343,66.3638194:34.0721268,66.3636619:34.0720821,66.3634548:34.0719963,66.3632342:34.0719665,66.3631352:34.0719739,66.3629731:34.0719851,66.362802:34.0719777,66.362613:34.0719888,66.3623609:34.0720224,66.3622033:34.0721268,66.3620232:34.0722089,66.3618972:34.072235,66.3617666:34.0722238,66.3614245:34.0721604,66.3612174:34.072041,66.3609428:34.0718185,66.360584:34.0716096,66.360201:34.0714239,66.3598648:34.0712924,66.3595659:34.0712537,66.3592857:34.0712847,66.3589681:34.0713233,66.3586506:34.0714471,66.3584358:34.0715941,66.3582676:34.0716638,66.3580902:34.071656,66.3579127:34.0715941,66.3575765:34.0714935,66.3573617:34.0713156,66.3571001:34.0711531,66.3569507:34.0708957,66.3566393:34.0706027,66.3561735:34.070341,66.355815:34.0700686,66.355285:34.0699409,66.3550821:34.0698659,66.35498:34.06982,66.3548763:34.0698037,66.354652:34.0698043,66.3544277:34.0697727,66.3542553:34.0697323,66.3541414:34.0696716,66.3540805:34.0696009,66.3540032:34.0695134,66.3539056:34.0694157,66.3538447:34.069281,66.3538203:34.0690722,66.3538203:34.0689139,66.3537674:34.0687691,66.3536902:34.0686647,66.3535885:34.0685367,66.3533812:34.0684817,66.3532339:34.0684379,66.3531079:34.0684312,66.3529616:34.068411,66.3527908:34.0683638,66.3526323:34.0683167,66.3525265:34.068256,66.3524412:34.0681651,66.3523639:34.0680641,66.3522704:34.0680169,66.3521403:34.0680001,66.3520387:34.0679395,66.3518476:34.0678654,66.3516484:34.0677778,66.3514289:34.0676734,66.3509654:34.0675657,66.350319:34.0675118,66.3497336:34.0674747,66.349327:34.0674646,66.3492498:34.0674815,66.3491197:34.0675118,66.348892:34.0675118,66.3487619:34.0674646,66.3486278:34.0674074,66.3485424:34.06734,66.3484936:34.0672558,66.3484164:34.0672121,66.3483594:34.0670134,66.3482415:34.0664779,66.3479122:34.0662792,66.347774:34.0662219,66.347713:34.066195,66.3476439:34.0661579,66.3475626:34.0661377,66.3474691:34.0661209,66.3473431:34.0660906,66.3472211:34.0660468,66.3471357:34.0659929,66.3470707:34.065582,66.3468349:34.0654675,66.3468105:34.0653732,66.3468105:34.0652722,66.346843:34.0651981,66.3468593:34.065087,66.346843:34.0648108,66.3467333:34.0646054,66.3466682:34.0642888,66.3466235:34.064016,66.3465747:34.0638072,66.3465259:34.0637264,66.3464975:34.0635681,66.3463999:34.0630326,66.346095:34.0629079,66.3460218:34.0628271,66.3459893:34.0627059,66.3459527:34.0625981,66.345912:34.0624903,66.3458267:34.0623859,66.3457128:34.0623017,66.3456315:34.0622478,66.3456193:34.0622209,66.3456478:34.0621973,66.3457006:34.0621838,66.3458185:34.0621569,66.3460259:34.0621266,66.346156:34.0620626,66.3462901:34.0620491,66.3463511:34.0620525,66.3464446:34.0620862,66.3465219:34.0621367,66.3465869:34.0621805,66.3466438:34.0622007,66.3467211:34.0621973,66.3467942:34.0621771,66.3468837:34.062167,66.3469731:34.0621906,66.3470666:34.0622142,66.3471601:34.062268,66.3472577:34.0624028,66.3473065:34.0626149,66.3472374:34.0627328,66.3472821:34.0628305,66.3474041:34.0628844,66.3474894:34.0629349,66.347526:34.0629719,66.3475748:34.0629517,66.3476195:34.0628945,66.3476561:34.0628035,66.3476561:34.0626756,66.3476236:34.062625,66.3476114:34.0625611,66.3476277:34.0624196,66.3476683:34.0622748,66.3476724:34.0621333,66.3476683:34.0619548,66.3476236:34.0616955,66.3475179:34.0614698,66.3473268:34.0611802,66.3470341:34.060968,66.3467251:34.0608569,66.3465462:34.0607962,66.3464365:34.0607659,66.3462779:34.0607727,66.3460543:34.0607895,66.3459405:34.060776,66.3457982:34.0607087,66.3455461:34.060483,66.3447818:34.0604561,66.3446802:34.0604426,66.3446111:34.0603988,66.3445257:34.060355,66.3444525:34.060291,66.344355:34.0601226,66.3440297:34.0600283,66.3437736:34.0599475,66.3435703:34.0598161,66.3433549:34.0597454,66.3431922:34.059705,66.3430418:34.0596983,66.3428751:34.059806,66.3425377:34.0598464,66.3424361:34.0599138,66.3423913:34.0600283,66.3423669:34.0602978,66.3422247:34.0604325,66.3421352:34.0605032,66.3420661:34.0605201,66.3419767:34.0605099,66.3419075:34.0604662,66.3418669:34.0604257,66.3418384:34.0603483,66.3418181:34.0601327,66.3418222:34.0599711,66.3418181:34.059806,66.3417653:34.0595568,66.3416474:34.0593716,66.341562:34.0591526,66.3414644:34.0590078,66.341379:34.0588933,66.3413384:34.0587687,66.341314:34.0586272,66.3412774:34.0584857,66.3412083:34.0584083,66.3411798:34.0583207,66.3411554:34.0581119,66.3411107:34.0579906,66.3410741:34.0578727,66.3410091:34.0577245,66.3408749:34.0575258,66.3406513:34.0573406,66.3404684:34.057216,66.3403708:34.0569836,66.3401269:34.0568421,66.3399764:34.0566804,66.3398423:34.0565356,66.3397447:34.0564177,66.3396837:34.0562661,66.339639:34.0561584,66.3395862:34.0561011,66.3395374:34.056064,66.3394764:34.0560674,66.3393544:34.0560539,66.3392487:34.0559798,66.3391268:34.0558788,66.3390577:34.0557542,66.3390211:34.0556464,66.3390211:34.0555723,66.3390251:34.0554813,66.3390495:34.0553298,66.3391715:34.0551445,66.3392934:34.0549492,66.339391:34.0548279,66.339391:34.0546763,66.3393788:34.0545147,66.33933:34.0543563,66.3392609:34.0541441,66.3391756:34.0537298,66.3389967:34.0536389,66.3389357:34.053548,66.3388381:34.0534941,66.3386836:34.0534873,66.3385169:34.0534806,66.3384316:34.0534435,66.3383421:34.0533661,66.3382771:34.0532549,66.3381795:34.0531909,66.3380779:34.0531101,66.3379518:34.0529012,66.3376307:34.0528844,66.3375453:34.052881,66.3372648:34.0528911,66.3369599:34.0528642,66.3366915:34.0528474,66.3362687:34.0528575,66.3359516:34.052881,66.3358663:34.0529383,66.3357606:34.0529585,66.3356711:34.0529417,66.3355573:34.0529181,66.3354394:34.0529282,66.3353499:34.0529551,66.3352401:34.0529753,66.3351344:34.0529753,66.3350165:34.0529484,66.3348742:34.0529282,66.3347807:34.0528776,66.3346222:34.0528675,66.3345408:34.0528675,66.3344555:34.0528675,66.3343864:34.0528608,66.3343172:34.052844,66.3342685:34.0528069,66.3341872:34.0527901,66.334118:34.0527833,66.3340205:34.0527598,66.3338172:34.0527193,66.3335123:34.0527193,66.3334269:34.0527362,66.3333375:34.0527598,66.3332196:34.0527833,66.3330976:34.0528103,66.3329065:34.0528136,66.3327277:34.0527867,66.3324553:34.052753,66.3321869:34.0526991,66.3319796:34.0526823,66.3318576:34.0527126,66.3317235:34.0527025,66.3316422:34.0526587,66.3315243:34.0526183,66.3314104:34.0525711,66.3312803:34.0525341,66.3311462:34.052497,66.3309714:34.0524061,66.3307234:34.0523758,66.3306055:34.0523589,66.3304794:34.0523993,66.3303412:34.0524229,66.3302477:34.0524229,66.3301217:34.0523993,66.3299347:34.0523421,66.3297964:34.0522875,66.3296628:34.0522707,66.3295205:34.0522572,66.3292928:34.0522572,66.3292075:34.0522808,66.3291505:34.0523448,66.3290977:34.052392,66.3290326:34.052392,66.3289513:34.0523583,66.3289025:34.0522741,66.3288619:34.0522168,66.3287928:34.0521191,66.3285732:34.0520518,66.3284594:34.0520282,66.3283984:34.0519878,66.3282887:34.0519878,66.3281952:34.0519878,66.3281138:34.0519675,66.3280285:34.0519103,66.3279553:34.0518261,66.3278862:34.0517486,66.3277886:34.0516745,66.3276748:34.0516543,66.3276057:34.0516543,66.3275162:34.0516543,66.327443:34.0516374,66.3273821:34.0515937,66.3273373:34.0515263,66.3272926:34.0514421,66.3271869:34.0513478,66.3270202:34.0512602,66.3268414:34.0511625,66.3265364:34.0510783,66.3262519:34.0510513,66.3261055:34.0510614,66.3259673:34.0511052,66.3257518:34.0511524,66.3256136:34.0511996,66.3255241:34.051213,66.3254347:34.0511591,66.3252883:34.0510749,66.3251461:34.0509638,66.3250444:34.0508593,66.3249265:34.0507516,66.3247639:34.0506404,66.3245688:34.0505629,66.3244427:34.0505292,66.3243614:34.0505326,66.3243086:34.0505764,66.3242801:34.0506471,66.3242882:34.0508897,66.3243045:34.051149,66.3243452:34.0512905,66.3243452:34.0513747,66.3243004:34.0514556,66.3241866:34.0514892,66.3240768:34.0515768,66.3239264:34.0516644,66.3238614:34.0517722,66.3238695:34.0519574,66.3239711:34.0520518,66.3240443:34.0521259,66.3240687:34.0521831,66.3240524:34.052237,66.3239793:34.0522875,66.3238532:34.052328,66.3237719:34.0523515,66.3236987:34.0523515,66.3236378:34.052328,66.3235565:34.0522707,66.323406:34.0522303,66.3232312:34.0521696,66.3230157:34.0520922,66.322784:34.0520416,66.3226377:34.0520282,66.322536:34.0520315,66.3224222:34.0520383,66.3222067:34.052072,66.3220034:34.0521663,66.321853:34.0522572,66.3217229:34.0524155,66.3215969:34.0525536,66.3215197:34.0526648,66.321479:34.0527793,66.321479:34.0529275,66.3214831:34.0530589,66.3214383:34.0533182,66.3213245:34.0534126,66.3212839:34.0534597,66.3212473:34.0535136,66.3211497:34.0536248,66.3210399:34.0537864,66.3209058:34.0540155,66.3207513:34.0542309,66.3206932:34.0544475,66.3206091:34.0546178,66.3205904:34.054699,66.3205998:34.0547532,66.3205671:34.0547687,66.3205157:34.0547764,66.3204223:34.0547803,66.3203476:34.0548267,66.3202635:34.0549428,66.3201841:34.0550356,66.3201468:34.0550821,66.3201141:34.0551053,66.3200347:34.0551208,66.319946:34.0551478,66.3198479:34.0551749,66.3197779:34.0552059,66.3196985:34.0552446,66.3195163:34.0552987,66.3192175:34.0553297,66.3190167:34.055349,66.3188392:34.0553839,66.3186197:34.0554342,66.3184189:34.0554419,66.3183208:34.0554342,66.3181294:34.0554264,66.3179659:34.0554225,66.3177978:34.0554612,66.317639:34.055527,66.3174148:34.055558,66.3172794:34.055585,66.3171066:34.0556315,66.3167844:34.0557205,66.3163781:34.0558443,66.3159298:34.055883,66.3157897:34.0559333,66.3156449:34.0560493,66.3154815:34.0561228,66.31536:34.0561731,66.3151966:34.0562002,66.3150472:34.0562157,66.3149164:34.056235,66.3148183:34.0562815,66.3147016:34.0563705,66.3145615:34.0564672,66.3143513:34.0565484,66.3141552:34.0566529,66.313903:34.0567535,66.3136555:34.0568193,66.3135247:34.0569779,66.3133566:34.0571056,66.3132072:34.0572333,66.3130624:34.0573764,66.313025:34.0575234,66.3130764:34.0577324,66.3132118:34.0578794,66.3133846:34.0580341,66.3135434:34.0581502,66.3135854:34.0582701,66.3136508:34.0583978,66.3137582:34.0586338,66.3138516:34.0587344,66.3138423:34.0588543,66.3137255:34.0589627,66.3135948:34.0590517,66.313478:34.0591948,66.3132399:34.0592645,66.3131465:34.0593341,66.3131231:34.0594192,66.3131605:34.0594966,66.3132399:34.0595469,66.3133426:34.0595933,66.3134734:34.0596513,66.3136602:34.0597519,66.3138423:34.0598912,66.3140198:34.0599415,66.3141458:34.0599957,66.3142533:34.0600614,66.3142533:34.0601272,66.3142159:34.0601814,66.3141038:34.0602201,66.3139964:34.0602858,66.3137769:34.0603245,66.3136135:34.0603748,66.3134967:34.0604483,66.3134313:34.0605644,66.3133426:34.0606302,66.3132912:34.0606882,66.3131978:34.0606998,66.3131138:34.060754,66.3130344:34.0608507,66.3130391:34.0610635,66.3131138:34.0613188,66.3131745:34.0614774,66.3131978:34.0615509,66.3131698:34.0615548,66.3130951:34.0615161,66.3129503:34.0614774,66.3127448:34.0614155,66.3125254:34.0613923,66.3124086:34.0614078,66.3123386:34.0614929,66.3123339:34.0615974,66.3123526:34.0617482,66.3123199:34.061872,66.3122638:34.0619572,66.3121704:34.0620345,66.3119416:34.0621506,66.3115493:34.0624253,66.3110029:34.0626342,66.3105499:34.0626535,66.3104612:34.0627309,66.3103725:34.0628199,66.3102978:34.0628857,66.3102557:34.0629746,66.310181:34.063052,66.3100689:34.0631255,66.3099382:34.0631797,66.3098354:34.0632687,66.3097701:34.0634195,66.309742:34.0635898,66.3097047:34.0637213,66.3096953:34.0638722,66.3097234:34.0640269,66.3097841:34.0641623,66.3098448:34.0642358,66.3098588:34.0643016,66.3098308:34.0644177,66.309742:34.0644989,66.3096253:34.0645569,66.3094899:34.0645956,66.3093451:34.0646072,66.3091349:34.0646188,66.3088874:34.0645741,66.3085292:34.0645606,66.3083585:34.064564,66.3082081:34.0646145,66.308082:34.064665,66.3080129:34.0647222,66.3079397:34.0647627,66.3078747:34.0647728,66.3078137:34.064702,66.3076795:34.0645673,66.307338:34.0644393,66.3070006:34.0642945,66.3065371:34.0642339,66.3063501:34.0642204,66.3062241:34.0642305,66.3061225:34.0642777,66.3059761:34.0643787,66.3057769:34.0644899,66.3056184:34.0646953,66.3053866:34.0648233,66.3052809:34.0648974,66.3052077:34.064931,66.3051468:34.0650152,66.3049435:34.0650557,66.304789:34.0650725,66.3046508:34.0651297,66.304476:34.065187,66.304354:34.0652409,66.3042442:34.0652981,66.3040369:34.0653217,66.3038946:34.0653386,66.3037279:34.0653183,66.3035246:34.0652746,66.303301:34.0652038,66.303053:34.0652005,66.3029189:34.065288,66.3028864:34.0654025,66.3029026:34.0661805,66.3030449:34.0662849,66.3030937:34.0663758,66.3031343:34.0664466,66.3031506:34.0665038,66.303114:34.0665476,66.3030449:34.0666049,66.3030002:34.0667901,66.3029107:34.0669383,66.302866:34.0670191,66.3028254:34.0671134,66.3027725:34.0672178,66.3026587:34.0672986,66.3025652:34.0673592,66.3025083:34.0674064,66.3024188:34.0673727,66.3023335:34.0673289,66.3022806:34.0672548,66.3022196:34.0671639,66.3021749:34.0670932,66.3021302:34.0670292,66.3020773:34.0669686,66.301996:34.0669282,66.3018659:34.0668743,66.3017318:34.0668642,66.3016627:34.0669147,66.3016139:34.0669955,66.3015651:34.067302,66.3014553:34.0673693,66.3014106:34.0674232,66.3013699:34.067467,66.3012846:34.0674872,66.3011626:34.0675007,66.3010162:34.0675344,66.3007154:34.067595,66.3003942:34.0676354,66.3002926:34.0677297,66.3001462:34.0678947,66.2999877:34.0679486,66.2999145:34.0680126,66.2997844:34.0680631,66.2996502:34.0680934,66.2995649:34.068154,66.299447:34.068218,66.2993779:34.0683258,66.2992925:34.0683797,66.2992071:34.0684032,66.2990486:34.0683224,66.2988819:34.0682483,66.2987274:34.0682247,66.298642:34.068255,66.2985566:34.0683258,66.2984591:34.0683864,66.2983412:34.0683763,66.2982029:34.0682618,66.297959:34.0681877,66.297837:34.0680361,66.2976582:34.0678812,66.2974468:34.0677768,66.2972638:34.0676994,66.2970687:34.0677027,66.296967:34.0677465,66.296841:34.0679014,66.296593:34.0680025,66.296471:34.0681271,66.2963979:34.0682012,66.2963491:34.0682382,66.2962922:34.068282,66.296219:34.0683056,66.2961174:34.0682685,66.2960116:34.0681102,66.2958572:34.0679418,66.2956295:34.0678307,66.2953774:34.0677263,66.2950603:34.0675714,66.2946619:34.0674535,66.2942269:34.0674165,66.2939748:34.0674434,66.2937147:34.0674805,66.2934423:34.0675007,66.2932919:34.0674939,66.2931211:34.0674939,66.2930439:34.0674468,66.2929341:34.0673053,66.2928812:34.0671471,66.2928772:34.0668911,66.2928162:34.0667295,66.2927755:34.0665947,66.2926454:34.0664466,66.292495:34.0662647,66.2923365:34.0661367,66.292243:34.0660727,66.2922023:34.0659852,66.2921942:34.0658303,66.292186:34.0657191,66.2921535:34.0655541,66.2920275:34.0654025,66.2918852:34.0653487,66.2917348:34.0653082,66.2915315:34.0652645,66.2912672:34.0652139,66.2909176:34.0651331,66.2906534:34.0650321,66.290442:34.0648536,66.2902346:34.064564,66.290137:34.0643686,66.2900598:34.0642507,66.2899826:34.0641362,66.2899175:34.064015,66.2898484:34.0639173,66.2897712:34.0637422,66.2896533:34.0635772,66.289576:34.0633818,66.2895272:34.0632471,66.2895232:34.0631629,66.2895272:34.0630956,66.2894866:34.0629878,66.2894337:34.0628362,66.2893158:34.0627183,66.289202:34.0626544,66.2891369:34.0625803,66.2890719:34.0624354,66.2890719:34.0623041,66.2890678:34.062203,66.2890556:34.0620953,66.2890068:34.0620178,66.2889174:34.0619639,66.2888402:34.0618831,66.2887954:34.0617181,66.2887385:34.0615699,66.2886572:34.0615193,66.288584:34.0614857,66.2884865:34.0614419,66.2884092:34.0613947,66.2883117:34.0613543,66.2882669:34.061223,66.2882344:34.0610074,66.2882019:34.0608289,66.2881938:34.0606033,66.2882263:34.0604045,66.2883198:34.0602563,66.2883523:34.0600812,66.2883564:34.0599094,66.2883767:34.0597983,66.2884539:34.0597276,66.2885881:34.0596939,66.2888727:34.0596097,66.2891125:34.0595457,66.2892223:34.0594345,66.289328:34.059192,66.2894906:34.0589125,66.2896289:34.0587373,66.2896695:34.0585083,66.2896329:34.0582894,66.2895923:34.0580604,66.2894175:34.057801,66.2891939:34.057508,66.2889337:34.0571779,66.2887385:34.0569657,66.2885718:34.0568916,66.2884987:34.0568377,66.2884174:34.0567771,66.2883482:34.0566693,66.2882669:34.0565817,66.2882141:34.0564706,66.2881694:34.0563931,66.2881002:34.0562247,66.287958:34.056026,66.2878319:34.0554938,66.2875067:34.0550728,66.2873237:34.0548943,66.2872424:34.0547629,66.287092:34.054581,66.2868603:34.054389,66.2866042:34.0542375,66.2863684:34.0541095,66.2861732:34.0539343,66.2860309:34.0537491,66.2858439:34.0534358,66.285539:34.0533179,66.285352:34.0532337,66.2851568:34.0530215,66.2848194:34.0528295,66.2845958:34.0524994,66.2843925:34.0522973,66.2842868:34.0520817,66.2842055:34.0519167,66.2841893:34.0517112,66.2841486:34.0515866,66.284108:34.0514653,66.2840266:34.051307,66.2838478:34.0511083,66.2836242:34.0508523,66.2833762:34.0505356,66.2831566:34.0501752,66.2828883:34.049775,66.2826347:34.0492697,66.2824274:34.0490238,66.2823339:34.0486095,66.28209:34.0484646,66.2820412:34.0481918,66.2820818:34.0479998,66.2821388:34.0478684,66.2821753:34.0477573,66.2821713:34.0474676,66.2820168:34.0472284,66.281842:34.0470532,66.2817078:34.0468713,66.2816021:34.0467534,66.2815696:34.0466928,66.2815574:34.0466557,66.2815371:34.0466793,66.2814598:34.04674,66.2813216:34.0467837,66.2812281:34.0468848,66.2809557:34.0469185,66.2808337:34.0469421,66.2807565:34.0469151,66.2807158:34.0468376,66.2806671:34.0461538,66.2803093:34.0460528,66.2802361:34.046009,66.2801873:34.0460056,66.2800735:34.046009,66.2798621:34.0459888,66.2797401:34.0459685,66.2796751:34.0459012,66.2795978:34.0457024,66.2794555:34.045534,66.2793458:34.0454296,66.2792604:34.0452847,66.2791669:34.0451163,66.27911:34.044958,66.2791059:34.0447929,66.2790937:34.0446346,66.2790978:34.0445099,66.2791059:34.0443887,66.2791425:34.044291,66.2792116:34.0441731,66.2792279:34.0440787,66.2791994:34.0440046,66.2791669:34.0439204,66.279171:34.0438059,66.2792645:34.0436071,66.2793458:34.0434151,66.2793376:34.0431995,66.2793011:34.0429873,66.2791872:34.0426976,66.2790327:34.0424415,66.2788701:34.0423135,66.2787888:34.0422495,66.2787238:34.0421788,66.2786425:34.0420845,66.2784595:34.0419295,66.2782684:34.041751,66.2780652:34.0416768,66.2779879:34.0415893,66.2779229:34.0414815,66.2778781:34.0413501,66.2778253:34.041249,66.2777237:34.0410907,66.277557:34.0409526,66.2774187:34.0408414,66.2773293:34.0407134,66.2772805:34.0405854,66.2772358:34.0404439,66.2771342:34.040326,66.2769797:34.0401676,66.2768171:34.0400059,66.2766504:34.039915,66.2765975:34.0398004,66.2765609:34.0396252,66.2765365:34.0394871,66.2764796:34.0392883,66.2763617:34.0389852,66.2762682:34.0388437,66.2762235:34.0387527,66.2761625:34.0386483,66.2760609:34.0385304,66.2759186:34.0383754,66.2757763:34.0382608,66.275634:34.0380284,66.2753291:34.0378397,66.2751177:34.037678,66.274951:34.0375163,66.2748372:34.0374085,66.2747233:34.0372535,66.2745445:34.0370312,66.2742721:34.0369908,66.2742152:34.036947,66.2741867:34.0368829,66.2741623:34.0367886,66.2741664:34.0366437,66.2741257:34.0365023,66.2740403:34.0363473,66.2739062:34.0362327,66.2738249:34.0361519,66.2737192:34.0361114,66.2736541:34.0360946,66.2735891:34.0360778,66.2734874:34.0360643,66.2734346:34.0359666,66.273333:34.0358554,66.2732191:34.0356971,66.2730809:34.0355522,66.2729874:34.0353703,66.2728776:34.0352524,66.2728044:34.0351715,66.2727435:34.0351109,66.2727191:34.0350199,66.2727028:34.034966,66.2726865:34.0349458,66.2726337:34.0349795,66.2725849:34.0350199,66.2725361:34.0351243,66.2725524:34.0353399,66.2726012:34.0355522,66.2726215:34.0357779,66.2726256:34.0359565,66.2726378:34.036098,66.2726784:34.0363035,66.2727353:34.0366033,66.2727719:34.0368358,66.2728288:34.0370413,66.2729183:34.037112,66.2729549:34.0371895,66.2729305:34.0372468,66.2728898:34.0372636,66.2728044:34.0372299,66.2727353:34.0371592,66.2727313:34.0370851,66.2727475:34.0370143,66.2727109:34.0368931,66.272593:34.0367549,66.2723735:34.0366505,66.2722556:34.0365359,66.2721336:34.0364483,66.2720117:34.0364281,66.2719426:34.0364483,66.2718653:34.0364854,66.2718043:34.0364854,66.2717149:34.0364618,66.2716255:34.0363271,66.2714506:34.0362496,66.2713815:34.0361755,66.2713287:34.0360277,66.2712613:34.0356976,66.270993:34.0356302,66.2709767:34.035546,66.2709645:34.0354382,66.2709482:34.0353506,66.2709157:34.0351787,66.2707287:34.0350406,66.2706149:34.0348418,66.270436:34.0345858,66.2702368:34.0342556,66.2700091:34.0340737,66.2698912:34.0339356,66.2698343:34.0337031,66.2697083:34.033538,66.2695944:34.033164,66.2692977:34.0329619,66.2691228:34.0328911,66.2690334:34.0328777,66.2689399:34.032908,66.2689033:34.0329754,66.2689155:34.0330933,66.2689887:34.0335313,66.2692367:34.0336189,66.2692692:34.0336896,66.2692692:34.0337166,66.2692285:34.0336896,66.2691513:34.0335953,66.2689765:34.0334572,66.2687244:34.0333729,66.2685781:34.0333157,66.2684724:34.0332449,66.2683301:34.0331674,66.2682366:34.0330731,66.2681309:34.032945,66.268013:34.0328979,66.267952:34.0328676,66.2678585:34.0328507,66.267704:34.0328204,66.2675739:34.0328069,66.2674682:34.0328575,66.2673666:34.032945,66.2673137:34.0330428,66.2673137:34.0331337,66.2673422:34.0332045,66.2673869:34.0332752,66.2674845:34.03341,66.2675576:34.0335279,66.2675658:34.033629,66.2675536:34.033693,66.267517:34.0337772,66.2674723:34.033949,66.2674804:34.0340333,66.2675089:34.0341209,66.267578:34.0342253,66.2675902:34.0344342,66.2676024:34.0345049,66.2675861:34.0345622,66.2675454:34.0346801,66.2674194:34.0347812,66.2673381:34.0348722,66.2673096:34.0350002,66.2673503:34.0351922,66.2674357:34.0352731,66.267456:34.0353405,66.2674357:34.0353775,66.2673788:34.0353708,66.2672771:34.0352866,66.2671023:34.0351821,66.2669234:34.035081,66.2667689:34.0350305,66.2666063:34.0350069,66.266525:34.0350069,66.2664356:34.035044,66.2663095:34.0350709,66.2661998:34.0350911,66.2660859:34.0351046,66.265903:34.0351114,66.2657322:34.035108,66.2655615:34.0350911,66.2653013:34.0350541,66.265098:34.0350069,66.2649151:34.0349193,66.2646833:34.0348452,66.2644191:34.0348082,66.2642361:34.0347778,66.2640776:34.0347542,66.263976:34.0347037,66.2638987:34.03467,66.2638337:34.0346431,66.2637361:34.0346397,66.2636467:34.0346397,66.2635491:34.0346296,66.2634637:34.0346161,66.2634027:34.0345555,66.2633011:34.0344948,66.2631344:34.0344106,66.2629189:34.0343028,66.2626872:34.0342725,66.2625896:34.0342624,66.2624677:34.0342691,66.2623498:34.0342523,66.2622034:34.0342085,66.2620205:34.0341141,66.261679:34.0339861,66.2613659:34.0338783,66.2610285:34.0337806,66.2608171:34.0336391,66.2605691:34.0335447,66.2604065:34.0335043,66.2602845:34.0334673,66.2601666:34.0334908,66.2600325:34.033538,66.259882:34.0335751,66.2597357:34.0336222,66.2595161:34.0336458,66.2593251:34.0336593,66.2590567:34.0336458,66.2587518:34.0336323,66.2586177:34.0336492,66.2584185:34.0336863,66.2582558:34.0337435,66.2580648:34.0337671,66.257894:34.0337907,66.2577355:34.0338547,66.2575281:34.0339726,66.2571704:34.0340905,66.2567841:34.0341984,66.2564914:34.0343196,66.2562922:34.0343971,66.2561581:34.0345218,66.2560646:34.0346902,66.2560158:34.0348789,66.2560158:34.0350372,66.2560646:34.0352293,66.2560686:34.0355089,66.2561296:34.0356066,66.2561743:34.0356875,66.2561703:34.0357616,66.2561662:34.0358458,66.2561947:34.0359233,66.2562068:34.0360008,66.2561743:34.0360749,66.2561743:34.0362063,66.256219:34.0363276,66.2562597:34.0364286,66.2562475:34.0365095,66.2561987:34.0365735,66.2561418:34.0366544,66.2561052:34.0367352,66.2560442:34.0367858,66.2559711:34.0368329,66.2558775:34.0368632,66.255719:34.0368565,66.255593:34.03687,66.2555198:34.0369239,66.2554425:34.0369946,66.2553572:34.0371597,66.2552067:34.0373585,66.255097:34.0374292,66.2550645:34.0374932,66.2550157:34.0375303,66.2549506:34.0375977,66.2548856:34.0377021,66.254853:34.0378503,66.2548246:34.0379615,66.2548002:34.0380424,66.2548165:34.0381097,66.2548449:34.0381738,66.2548409:34.0382209,66.2547758:34.0382883,66.2547473:34.0383826,66.254727:34.0384702,66.2546864:34.0385376,66.2546254:34.0386252,66.2544912:34.0386926,66.2543408:34.0387465,66.2542636:34.0388172,66.2541619 && last_edit_changeset -> 60832604 || surface -> gravel || iso_country_code -> AFG || maxspeed -> 40 || oneway -> yes || last_edit_user_name -> Qaderi || ref -> V220214 || condition -> fair || last_edit_time -> 1531912427000 || last_edit_user_id -> 8164544 || name -> سرک ولسوالی از شیخمیران الی لیلی مجنون || lanes -> 1 || width -> 3 || highway -> tertiary || last_edit_version -> 2\n# Points\n5767870656000000 && 34.1154094,66.5064452 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870657000000 && 34.1153535,66.5063416 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870658000000 && 34.1149659,66.5058599 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870659000000 && 34.1144702,66.5051891 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870660000000 && 34.1142428,66.504802 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870661000000 && 34.1141832,66.5046759 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870662000000 && 34.1141198,66.5045454 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870663000000 && 34.1140229,66.5044463 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870664000000 && 34.1138142,66.5042843 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870665000000 && 34.1136614,66.5041897 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870666000000 && 34.1135868,66.5041222 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870667000000 && 34.1134154,66.5038251 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870668000000 && 34.1133483,66.503735 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870669000000 && 34.1132477,66.5036045 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870670000000 && 34.1130613,66.5033974 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870671000000 && 34.1129122,66.5032488 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870672000000 && 34.1127408,66.5030463 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870673000000 && 34.1126029,66.5028482 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870674000000 && 34.1124985,66.5026321 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870675000000 && 34.1124463,66.502407 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870676000000 && 34.1123867,66.5020964 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870677000000 && 34.1123047,66.5017542 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870678000000 && 34.1122301,66.5015336 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870679000000 && 34.112096,66.5013041 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870680000000 && 34.1118984,66.5010565 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870681000000 && 34.1118313,66.5009259 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870682000000 && 34.1117456,66.5007413 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870683000000 && 34.1116822,66.5005162 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870684000000 && 34.1116114,66.5002551 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870685000000 && 34.1114772,66.499958 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870686000000 && 34.1112871,66.4996519 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870687000000 && 34.111153,66.4994538 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870688000000 && 34.1110001,66.4992782 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870689000000 && 34.110948,66.4992242 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870690000000 && 34.110892,66.4991432 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870691000000 && 34.1107131,66.4988325 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870692000000 && 34.1105193,66.4985894 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870693000000 && 34.1104336,66.4985174 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870694000000 && 34.1103889,66.4984814 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870695000000 && 34.1103031,66.4983553 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870696000000 && 34.1100348,66.4979817 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870697000000 && 34.1098484,66.4976801 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870698000000 && 34.10958,66.4971894 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870699000000 && 34.1093899,66.4968787 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870700000000 && 34.1091812,66.4966086 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870701000000 && 34.1089575,66.4963115 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907593000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870702000000 && 34.1086072,66.4959289 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870703000000 && 34.1081673,66.4955147 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870704000000 && 34.1080108,66.4954021 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870705000000 && 34.1078505,66.4953166 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870706000000 && 34.10772,66.4952086 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870707000000 && 34.1076716,66.4950735 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870708000000 && 34.1076231,66.4948484 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870709000000 && 34.1075598,66.4946233 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870710000000 && 34.1074852,66.4944252 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870711000000 && 34.1073808,66.4942902 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870712000000 && 34.1071982,66.4941461 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870713000000 && 34.107023,66.4939255 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870714000000 && 34.1068217,66.4936149 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870715000000 && 34.1066875,66.4934438 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870716000000 && 34.1065459,66.4932413 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870717000000 && 34.1064229,66.4930882 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870718000000 && 34.10627,66.4929306 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870719000000 && 34.1060949,66.4927641 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870720000000 && 34.1058414,66.4926695 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870721000000 && 34.1057445,66.4926425 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870722000000 && 34.1056401,66.492611 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870723000000 && 34.1055693,66.492548 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870724000000 && 34.1054761,66.4924489 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870725000000 && 34.1053344,66.4923004 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870726000000 && 34.1049393,66.4918502 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870727000000 && 34.1047753,66.4916746 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870728000000 && 34.1045964,66.4915576 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870729000000 && 34.1044212,66.4914945 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870730000000 && 34.104164,66.4913415 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870731000000 && 34.1038322,66.4911794 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870732000000 && 34.1034632,66.4910894 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870733000000 && 34.1033812,66.4909993 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870734000000 && 34.1033588,66.4909408 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870735000000 && 34.1033663,66.4907923 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870736000000 && 34.1034147,66.4905987 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870737000000 && 34.1034557,66.4904276 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870738000000 && 34.1034893,66.4903556 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870739000000 && 34.1035154,66.490252 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870740000000 && 34.1035191,66.490153 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870741000000 && 34.1034632,66.4900044 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870742000000 && 34.1033961,66.4898379 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870743000000 && 34.1033141,66.4896848 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870744000000 && 34.1032656,66.4895678 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870745000000 && 34.1032283,66.4894372 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870746000000 && 34.1032358,66.4892796 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870747000000 && 34.1032246,66.4890861 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870748000000 && 34.1032358,66.4888925 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870749000000 && 34.1032582,66.4887304 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870750000000 && 34.1033625,66.4885774 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870751000000 && 34.103452,66.4884828 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870752000000 && 34.1034967,66.4884468 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870753000000 && 34.1035266,66.4883973 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870754000000 && 34.1035266,66.4883343 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870755000000 && 34.1034669,66.4882847 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870756000000 && 34.1033588,66.4882262 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870757000000 && 34.1031575,66.4881227 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870758000000 && 34.1030308,66.4880732 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870759000000 && 34.1029488,66.4880101 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870760000000 && 34.1028705,66.4879066 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870761000000 && 34.1027885,66.487722 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870762000000 && 34.1027363,66.4872898 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870763000000 && 34.1027288,66.4868216 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870764000000 && 34.1027587,66.4866146 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870765000000 && 34.1027736,66.4864975 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870766000000 && 34.1027587,66.4864075 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870767000000 && 34.1027326,66.4863805 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870768000000 && 34.1026953,66.4863715 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907594000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870769000000 && 34.1025984,66.486448 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870770000000 && 34.1024865,66.4864705 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870771000000 && 34.1023188,66.4864255 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870772000000 && 34.1021436,66.4864075 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870773000000 && 34.1019982,66.486394 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870774000000 && 34.101905,66.4863805 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870775000000 && 34.1018155,66.4862949 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870776000000 && 34.1016292,66.4861058 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870777000000 && 34.1013496,66.4859033 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870778000000 && 34.101193,66.4857907 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870779000000 && 34.1010961,66.4856872 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870780000000 && 34.1010476,66.4855521 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870781000000 && 34.1010029,66.4854171 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870782000000 && 34.1009917,66.4850074 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870783000000 && 34.100947,66.4844132 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870784000000 && 34.1009507,66.4842511 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870785000000 && 34.1009694,66.4841656 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870786000000 && 34.1010365,66.484017 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870787000000 && 34.1010663,66.4839405 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870788000000 && 34.1010775,66.4838234 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870789000000 && 34.1010849,66.4835713 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870790000000 && 34.1010849,66.4829005 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870791000000 && 34.1010849,66.4827745 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870792000000 && 34.1010924,66.4826124 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870793000000 && 34.1011408,66.4823378 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870794000000 && 34.1011744,66.4820902 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870795000000 && 34.1011744,66.4818741 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870796000000 && 34.1011483,66.4815905 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870797000000 && 34.1010998,66.4811583 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870798000000 && 34.1010737,66.4810458 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870799000000 && 34.1010476,66.4809557 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870800000000 && 34.1010178,66.4808567 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870801000000 && 34.1009954,66.4806676 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870802000000 && 34.1009843,66.480447 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870803000000 && 34.1009805,66.4803525 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870804000000 && 34.1009544,66.4802625 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870805000000 && 34.100865,66.4800644 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870806000000 && 34.1008314,66.4799518 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870807000000 && 34.1008165,66.4798528 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870808000000 && 34.1008277,66.4797222 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870809000000 && 34.1008463,66.4795827 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870810000000 && 34.1008463,66.4794836 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870811000000 && 34.1008128,66.4793576 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870812000000 && 34.1007494,66.4792631 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870813000000 && 34.1006376,66.4791955 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870814000000 && 34.100522,66.4791055 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870815000000 && 34.1003878,66.4789839 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870816000000 && 34.1002723,66.4788399 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870817000000 && 34.0999815,66.4782636 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870818000000 && 34.0998361,66.4780205 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870819000000 && 34.0996907,66.4777729 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870820000000 && 34.0994671,66.4775147 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870821000000 && 34.0993515,66.4772822 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870822000000 && 34.0992024,66.4770797 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870823000000 && 34.0991316,66.4768906 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870824000000 && 34.0990533,66.476706 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870825000000 && 34.0989675,66.4765214 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870826000000 && 34.0988669,66.4764134 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870827000000 && 34.0987289,66.4762783 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870828000000 && 34.0985724,66.4761388 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870829000000 && 34.0983972,66.4760217 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870830000000 && 34.0982406,66.4759857 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870831000000 && 34.0980989,66.4759722 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870832000000 && 34.0980505,66.4758822 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870833000000 && 34.0980766,66.4758146 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870834000000 && 34.0981288,66.4757606 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870835000000 && 34.0981586,66.4756526 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870836000000 && 34.0982145,66.475531 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870837000000 && 34.0983077,66.475486 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907595000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870838000000 && 34.0984643,66.475459 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870839000000 && 34.0985276,66.4754365 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870840000000 && 34.098591,66.475387 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870841000000 && 34.0986059,66.4752654 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870842000000 && 34.0986357,66.4749818 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870843000000 && 34.0986171,66.4746487 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870844000000 && 34.0986059,66.4745136 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870845000000 && 34.0985015,66.4742255 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870846000000 && 34.0983785,66.4738158 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870847000000 && 34.0982406,66.4731721 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870848000000 && 34.0981138,66.4725418 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870849000000 && 34.0980244,66.4721907 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870850000000 && 34.0979796,66.4719521 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870851000000 && 34.0979685,66.471709 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870852000000 && 34.0978566,66.4714839 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870853000000 && 34.0976889,66.4712453 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870854000000 && 34.0975323,66.4710427 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870855000000 && 34.0973757,66.4707366 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870856000000 && 34.0972564,66.4704935 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870857000000 && 34.0972229,66.4704349 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870858000000 && 34.0971371,66.4703629 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870859000000 && 34.0970514,66.4702369 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870860000000 && 34.0969284,66.4700343 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870861000000 && 34.096824,66.4698812 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870862000000 && 34.0967532,66.4697642 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870863000000 && 34.0967233,66.4696651 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870864000000 && 34.0966525,66.469359 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870865000000 && 34.0966078,66.4691789 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870866000000 && 34.0965742,66.4690799 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870867000000 && 34.0964512,66.4688548 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870868000000 && 34.0962797,66.4684136 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870869000000 && 34.096015,66.4676843 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870870000000 && 34.0959479,66.4674322 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870871000000 && 34.095892,66.4672837 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870872000000 && 34.0958324,66.4671981 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870873000000 && 34.0957354,66.4671711 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870874000000 && 34.0956758,66.4671531 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870875000000 && 34.0956422,66.4671126 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870876000000 && 34.0956087,66.4670271 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870877000000 && 34.0955639,66.466811 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870878000000 && 34.095385,66.4663608 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870879000000 && 34.0951613,66.4658881 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870880000000 && 34.0950793,66.465762 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870881000000 && 34.0949078,66.4654874 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870882000000 && 34.0948295,66.4653029 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870883000000 && 34.0947773,66.4651813 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870884000000 && 34.094714,66.4650327 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870885000000 && 34.0946319,66.4649247 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870886000000 && 34.0945052,66.4648302 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870887000000 && 34.0943486,66.4647671 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870888000000 && 34.0941398,66.4647446 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870889000000 && 34.0939982,66.4647041 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870890000000 && 34.093808,66.4646096 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870891000000 && 34.093726,66.4645646 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870892000000 && 34.0936105,66.4645375 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870893000000 && 34.0935061,66.464479 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870894000000 && 34.0933271,66.4643124 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870895000000 && 34.0931034,66.4641549 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870896000000 && 34.0928462,66.4639703 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870897000000 && 34.092753,66.4638668 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870898000000 && 34.0926971,66.4637182 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870899000000 && 34.0925927,66.4634886 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907596000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870900000000 && 34.0925069,66.4632455 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870901000000 && 34.0924398,66.4630879 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870902000000 && 34.0922087,66.4626963 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870903000000 && 34.0919813,66.4623541 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870904000000 && 34.0918843,66.4621966 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870905000000 && 34.0917576,66.4620795 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870906000000 && 34.0915898,66.46194 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870907000000 && 34.0915376,66.461895 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870908000000 && 34.0915339,66.4618364 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870909000000 && 34.0915302,66.4617464 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870910000000 && 34.0915525,66.4616113 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870911000000 && 34.0915786,66.4614223 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870912000000 && 34.0915674,66.4612467 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870913000000 && 34.0915302,66.4610846 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870914000000 && 34.0914891,66.4608956 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870915000000 && 34.0914332,66.4606885 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870916000000 && 34.0913587,66.4604499 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870917000000 && 34.0912282,66.4601618 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870918000000 && 34.0910455,66.4597746 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870919000000 && 34.0908479,66.4593604 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870920000000 && 34.090695,66.4590678 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870921000000 && 34.0905459,66.4588472 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870922000000 && 34.0903856,66.4586852 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870923000000 && 34.0902029,66.4584871 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870924000000 && 34.0901246,66.4584375 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870925000000 && 34.0900389,66.4583745 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870926000000 && 34.0899979,66.458325 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870927000000 && 34.0899606,66.458208 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870928000000 && 34.0899457,66.4581089 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870929000000 && 34.089927,66.4580369 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870930000000 && 34.0899121,66.4579559 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870931000000 && 34.0898338,66.4577713 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870932000000 && 34.0897704,66.4575642 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870933000000 && 34.0896959,66.4573031 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870934000000 && 34.0896847,66.457141 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870935000000 && 34.0896772,66.4570015 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870936000000 && 34.0896922,66.4568574 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870937000000 && 34.0897481,66.4567314 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870938000000 && 34.0898376,66.4565558 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870939000000 && 34.0900277,66.4562452 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870940000000 && 34.0900687,66.4561641 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870941000000 && 34.0900948,66.4561056 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870942000000 && 34.0900948,66.4560516 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870943000000 && 34.0900762,66.4560066 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870944000000 && 34.0900128,66.4559795 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870945000000 && 34.0899494,66.455948 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870946000000 && 34.0898935,66.4558985 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870947000000 && 34.089845,66.4557995 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870948000000 && 34.0898264,66.4556554 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870949000000 && 34.0898003,66.4553988 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870950000000 && 34.0897928,66.4552052 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870951000000 && 34.0898077,66.4550792 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767870952000000 && 34.0898711,66.4550071 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871153000000 && 34.0899904,66.4549801 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871154000000 && 34.0903968,66.4549846 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871155000000 && 34.0907082,66.4549611 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871156000000 && 34.090851,66.4548906 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871157000000 && 34.0910555,66.4547064 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871158000000 && 34.0912632,66.4545379 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871159000000 && 34.091578,66.4543576 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871160000000 && 34.0924836,66.453852 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871161000000 && 34.0927756,66.4537149 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907597000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871162000000 && 34.0930677,66.4535777 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871163000000 && 34.0932105,66.4535503 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871164000000 && 34.0934215,66.453515 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871165000000 && 34.0935383,66.453468 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871166000000 && 34.0936584,66.4533269 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871167000000 && 34.0938207,66.4530878 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871168000000 && 34.0939083,66.4528801 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871169000000 && 34.0939375,66.4528135 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871170000000 && 34.0940706,66.452692 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871171000000 && 34.0941615,66.4525626 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871172000000 && 34.0943295,66.4524018 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871173000000 && 34.0944333,66.4522528 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871174000000 && 34.0945047,66.4520294 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871175000000 && 34.0945599,66.4515905 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871176000000 && 34.0945891,66.4504618 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871177000000 && 34.0945566,66.4498544 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871178000000 && 34.0945372,66.4495251 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871179000000 && 34.0945372,66.4492312 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871180000000 && 34.0945534,66.4488903 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871181000000 && 34.0945956,66.4486669 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871182000000 && 34.0946378,66.4484788 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871183000000 && 34.0946508,66.4483808 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871184000000 && 34.0946378,66.4482867 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871185000000 && 34.0945923,66.4480908 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871186000000 && 34.0945015,66.4477655 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871187000000 && 34.0944203,66.4475029 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871188000000 && 34.0943327,66.4471619 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871189000000 && 34.0942743,66.4468798 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871190000000 && 34.0942451,66.4465898 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871191000000 && 34.094248,66.446392 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871192000000 && 34.0943227,66.4459609 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871193000000 && 34.0944395,66.4454789 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871194000000 && 34.0944784,66.4453064 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871195000000 && 34.0944979,66.4451771 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871196000000 && 34.0945077,66.4449498 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871197000000 && 34.0945174,66.444648 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871198000000 && 34.0945498,66.4441699 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871199000000 && 34.0945661,66.4439348 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871200000000 && 34.0946505,66.4435233 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871201000000 && 34.0947413,66.4430922 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871202000000 && 34.0947608,66.4429315 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871203000000 && 34.0947933,66.4428021 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871204000000 && 34.0948679,66.4425827 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871205000000 && 34.0950594,66.4421241 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871206000000 && 34.0952509,66.4417989 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871207000000 && 34.0954618,66.4415441 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871208000000 && 34.0955786,66.441454 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871209000000 && 34.0956987,66.4413756 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871210000000 && 34.0958383,66.4412894 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871211000000 && 34.0959713,66.4411248 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871212000000 && 34.0960979,66.4408544 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871213000000 && 34.096218,66.44058 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871214000000 && 34.0964192,66.4401528 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871215000000 && 34.096575,66.4397923 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871216000000 && 34.0966788,66.4395375 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871217000000 && 34.09676,66.4391731 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871218000000 && 34.0968249,66.43884 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871219000000 && 34.0968476,66.4387145 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871220000000 && 34.0969255,66.438448 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871221000000 && 34.0970456,66.4380953 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871222000000 && 34.0970618,66.4379778 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871223000000 && 34.0970585,66.4378014 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871224000000 && 34.0970163,66.4375427 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871225000000 && 34.0969612,66.4372057 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871226000000 && 34.0969222,66.4370215 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871227000000 && 34.09688,66.4368647 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907598000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871228000000 && 34.0968573,66.4367472 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871229000000 && 34.0968995,66.4365826 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871230000000 && 34.0969287,66.4363905 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871231000000 && 34.0969449,66.4361985 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871232000000 && 34.0969287,66.4358771 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871233000000 && 34.0968898,66.4356224 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871234000000 && 34.0968184,66.4353481 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871235000000 && 34.0967697,66.435156 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871236000000 && 34.0967535,66.4349993 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871237000000 && 34.0967437,66.4346113 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871238000000 && 34.0967048,66.4343918 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871239000000 && 34.0965912,66.4339411 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871240000000 && 34.096523,66.4337726 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871241000000 && 34.0964646,66.433655 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871242000000 && 34.0964387,66.4335727 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871243000000 && 34.0964062,66.4334434 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871244000000 && 34.0963608,66.4332122 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871245000000 && 34.0962634,66.4329221 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871246000000 && 34.096179,66.4327262 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871247000000 && 34.0961011,66.4325538 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871248000000 && 34.0960525,66.4324205 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871249000000 && 34.0960265,66.4322833 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871250000000 && 34.0959876,66.4320991 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871251000000 && 34.0959713,66.4319737 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871252000000 && 34.0959876,66.4317817 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871253000000 && 34.09602,66.4316171 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871254000000 && 34.0960395,66.4315113 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871255000000 && 34.0960395,66.431378 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871256000000 && 34.0960265,66.4312448 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871257000000 && 34.0960233,66.431135 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871258000000 && 34.0960395,66.4310645 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871259000000 && 34.0960849,66.4310018 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871260000000 && 34.0961141,66.430943 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871261000000 && 34.0961304,66.430845 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871262000000 && 34.0961336,66.4307118 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871263000000 && 34.0961368,66.4305629 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871264000000 && 34.0961725,66.4303591 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871265000000 && 34.0961855,66.4301474 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871266000000 && 34.0961758,66.4299868 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871267000000 && 34.0961368,66.4298026 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871268000000 && 34.0960362,66.4295243 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871269000000 && 34.0959843,66.429344 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871270000000 && 34.0959551,66.429152 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871271000000 && 34.0959616,66.429007 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871272000000 && 34.0959648,66.4289208 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871273000000 && 34.0959584,66.4288385 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871274000000 && 34.0959032,66.4286778 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871275000000 && 34.0958577,66.4285171 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871276000000 && 34.0958383,66.4283643 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871277000000 && 34.095887,66.4281213 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871278000000 && 34.0959389,66.427941 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871279000000 && 34.0959908,66.4276275 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871280000000 && 34.0960038,66.4274825 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871281000000 && 34.0959876,66.4273571 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871282000000 && 34.0959162,66.4271925 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871283000000 && 34.095861,66.4270122 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871284000000 && 34.0957831,66.4267966 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871285000000 && 34.0957279,66.4266007 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871286000000 && 34.0957085,66.4264047 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871287000000 && 34.0957085,66.4262715 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871288000000 && 34.0957377,66.426099 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871289000000 && 34.0957539,66.4258521 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871290000000 && 34.0957377,66.4256091 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871291000000 && 34.0957117,66.4254289 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871292000000 && 34.095676,66.4252956 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871293000000 && 34.0955981,66.4251585 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871294000000 && 34.0954521,66.4249939 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871295000000 && 34.095173,66.4246607 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871296000000 && 34.0949685,66.4244452 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871297000000 && 34.0948582,66.424355 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907599000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871298000000 && 34.0948225,66.4242492 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871299000000 && 34.0947673,66.4241238 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871300000000 && 34.0947,66.4239555 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871301000000 && 34.0946553,66.4238385 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871302000000 && 34.0946478,66.423726 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871303000000 && 34.0946478,66.4236089 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871304000000 && 34.0946702,66.4234874 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871305000000 && 34.0946627,66.4233253 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871306000000 && 34.0946143,66.4231407 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871307000000 && 34.0945472,66.4229877 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871308000000 && 34.0939693,66.4221503 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871309000000 && 34.0938388,66.4220513 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871310000000 && 34.0937382,66.4219882 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871311000000 && 34.0936748,66.4218937 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871312000000 && 34.0936114,66.4217677 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871313000000 && 34.0935592,66.4216686 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871314000000 && 34.0934697,66.4215831 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871315000000 && 34.0933803,66.4214885 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871316000000 && 34.0932908,66.421331 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871317000000 && 34.0931864,66.4211194 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871318000000 && 34.0931752,66.4210384 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871319000000 && 34.0931976,66.4209483 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871320000000 && 34.0932945,66.4208673 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871321000000 && 34.093425,66.4207682 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871322000000 && 34.093507,66.4206917 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871323000000 && 34.0935928,66.4205477 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871324000000 && 34.0936711,66.4203856 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871325000000 && 34.0937605,66.420237 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871326000000 && 34.093809,66.420174 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871327000000 && 34.0938612,66.420174 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871328000000 && 34.0939618,66.420201 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871329000000 && 34.0940588,66.4202595 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871330000000 && 34.0941482,66.4203496 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871331000000 && 34.0942079,66.4204081 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871332000000 && 34.0942564,66.4204126 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871333000000 && 34.0942862,66.4203676 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871334000000 && 34.094316,66.4203001 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871335000000 && 34.0943309,66.420219 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871336000000 && 34.0943458,66.4200299 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871337000000 && 34.0943533,66.4198679 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871338000000 && 34.0943719,66.4197328 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871339000000 && 34.0944241,66.4196203 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871340000000 && 34.0945173,66.4194852 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871341000000 && 34.0947373,66.4191926 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871342000000 && 34.0948156,66.4190621 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871343000000 && 34.094905,66.418999 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871344000000 && 34.0950132,66.418954 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871345000000 && 34.0951138,66.418828 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871346000000 && 34.0951884,66.4187064 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871347000000 && 34.0952517,66.4186254 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871348000000 && 34.0953151,66.4184813 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871349000000 && 34.0953673,66.4183463 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871350000000 && 34.0954083,66.4182607 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871351000000 && 34.0953971,66.4181932 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871352000000 && 34.0953673,66.4181572 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871353000000 && 34.0953263,66.4181527 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871354000000 && 34.0952592,66.4182247 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871355000000 && 34.0951623,66.4183733 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871356000000 && 34.0951101,66.4184453 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871357000000 && 34.0950132,66.4184858 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871358000000 && 34.0949349,66.4185353 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871359000000 && 34.0948678,66.4185488 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871360000000 && 34.094782,66.4184948 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871361000000 && 34.0946963,66.4184543 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871362000000 && 34.0945173,66.4184138 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871363000000 && 34.0943719,66.4183913 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871364000000 && 34.094152,66.4184678 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871365000000 && 34.0939358,66.4185939 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871366000000 && 34.0938351,66.4186209 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907600000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871367000000 && 34.0937904,66.4185804 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871368000000 && 34.0937493,66.4184813 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871369000000 && 34.0937009,66.4182562 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871370000000 && 34.0936412,66.4180446 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871371000000 && 34.0936189,66.4179231 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871372000000 && 34.093604,66.417752 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871373000000 && 34.0936151,66.4175494 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871374000000 && 34.0936077,66.4172973 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871375000000 && 34.0935779,66.4170542 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871376000000 && 34.093548,66.4167841 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871377000000 && 34.093507,66.4165275 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871378000000 && 34.0934586,66.4162439 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871379000000 && 34.0934548,66.4161088 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871380000000 && 34.0934847,66.4159198 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871381000000 && 34.0935406,66.4157667 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871382000000 && 34.0935853,66.4156677 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871383000000 && 34.0936114,66.4155731 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871384000000 && 34.0936189,66.4154606 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871385000000 && 34.0936077,66.4153525 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871386000000 && 34.0935667,66.4153165 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871387000000 && 34.0935182,66.415339 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871388000000 && 34.0934623,66.415366 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871389000000 && 34.0933915,66.415375 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871390000000 && 34.0933355,66.4153976 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871391000000 && 34.0932833,66.4154516 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871392000000 && 34.0932349,66.4155461 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871393000000 && 34.093123,66.4157802 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871394000000 && 34.0930373,66.4159378 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871395000000 && 34.0930037,66.4159873 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871396000000 && 34.092959,66.4159783 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871397000000 && 34.0928993,66.4159468 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871398000000 && 34.0928658,66.4158838 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871399000000 && 34.0928658,66.4157757 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871400000000 && 34.0928882,66.4156452 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871401000000 && 34.0929105,66.4155191 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871402000000 && 34.0929143,66.4153886 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871403000000 && 34.0929068,66.4152625 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871404000000 && 34.0928509,66.4150374 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871405000000 && 34.0928061,66.4148168 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871406000000 && 34.0927204,66.4146052 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871407000000 && 34.0926346,66.4144792 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871408000000 && 34.0924893,66.4143576 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871409000000 && 34.0924147,66.4142631 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871410000000 && 34.0923662,66.4141505 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871411000000 && 34.0923401,66.4138984 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871412000000 && 34.0923476,66.4137544 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871413000000 && 34.09237,66.4136193 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871414000000 && 34.0923737,66.4135158 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871415000000 && 34.0923625,66.4134618 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871416000000 && 34.0923066,66.4134122 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871417000000 && 34.0922544,66.4133672 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871418000000 && 34.0921873,66.4132637 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871419000000 && 34.0921127,66.4131421 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871420000000 && 34.0920829,66.4130251 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871421000000 && 34.0921053,66.4128225 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871422000000 && 34.09215,66.4126154 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871423000000 && 34.0921835,66.4125029 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871424000000 && 34.0922096,66.4124353 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871425000000 && 34.0922357,66.4123543 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871426000000 && 34.0922246,66.4123048 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871427000000 && 34.09215,66.4122418 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871428000000 && 34.0920903,66.4121517 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871429000000 && 34.0920792,66.4120797 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871430000000 && 34.0920903,66.4119582 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871431000000 && 34.0920903,66.4118006 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871432000000 && 34.0921015,66.4117331 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871433000000 && 34.092068,66.4115755 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871434000000 && 34.0920381,66.4114404 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871435000000 && 34.0919822,66.4112604 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871436000000 && 34.0919524,66.4111433 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871437000000 && 34.0919375,66.4110038 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871438000000 && 34.0919412,66.4107562 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871439000000 && 34.091951,66.4105479 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907601000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871440000000 && 34.0919217,66.4104029 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871441000000 && 34.091899,66.4102579 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871442000000 && 34.0918925,66.4101324 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871443000000 && 34.0919023,66.410007 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871444000000 && 34.0919153,66.4099208 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871445000000 && 34.0919639,66.4097053 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871446000000 && 34.0919899,66.4095446 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871447000000 && 34.0920224,66.40938 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871448000000 && 34.0920483,66.4092546 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871449000000 && 34.0920613,66.4091919 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871450000000 && 34.0920386,66.4091096 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871451000000 && 34.0919737,66.4089332 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871452000000 && 34.0918731,66.4087255 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871453000000 && 34.0917465,66.4084472 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871454000000 && 34.0916069,66.4081455 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871455000000 && 34.0914349,66.4077575 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871456000000 && 34.0913505,66.4075615 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871457000000 && 34.0912402,66.4072166 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871458000000 && 34.0911623,66.406958 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871459000000 && 34.0910422,66.4066249 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871460000000 && 34.0909611,66.4063936 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871461000000 && 34.090828,66.4061546 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871462000000 && 34.0907274,66.4059939 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871463000000 && 34.090617,66.4058606 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871464000000 && 34.0904775,66.4057352 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871465000000 && 34.0903768,66.4056647 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871466000000 && 34.0902405,66.4054883 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871467000000 && 34.0900393,66.405214 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871468000000 && 34.0898473,66.4048682 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871469000000 && 34.0896201,66.4043548 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871470000000 && 34.0893183,66.403763 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871471000000 && 34.0890456,66.4032849 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871472000000 && 34.0887957,66.4029087 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871473000000 && 34.0885945,66.4025129 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871474000000 && 34.0883998,66.4021758 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871475000000 && 34.0883154,66.4020739 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871476000000 && 34.0881693,66.4019093 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871477000000 && 34.0880836,66.4017905 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871478000000 && 34.0880127,66.4015969 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871479000000 && 34.0879493,66.4013809 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871480000000 && 34.0879121,66.4012323 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871481000000 && 34.0877965,66.4010612 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871482000000 && 34.0877145,66.4009352 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871483000000 && 34.0876809,66.4008631 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871484000000 && 34.0875952,66.4007776 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871485000000 && 34.0875094,66.4007101 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871486000000 && 34.0873864,66.400575 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871487000000 && 34.0873043,66.4004535 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871488000000 && 34.0872,66.4003139 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871489000000 && 34.0871403,66.4002509 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871490000000 && 34.087062,66.4002284 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871491000000 && 34.0867227,66.4002374 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871492000000 && 34.0866258,66.4002239 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871493000000 && 34.0865624,66.4001924 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871494000000 && 34.0865102,66.4001248 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871495000000 && 34.0864543,66.4000033 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871496000000 && 34.0862567,66.3993595 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871497000000 && 34.0861187,66.3990129 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871498000000 && 34.085992,66.3986302 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871499000000 && 34.0859509,66.3984727 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871500000000 && 34.085936,66.3983016 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871501000000 && 34.0859211,66.3981035 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871502000000 && 34.0858876,66.3978559 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871503000000 && 34.0858428,66.3976488 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871504000000 && 34.0857123,66.3972662 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907602000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871505000000 && 34.0855408,66.3967169 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871506000000 && 34.0854215,66.3962758 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871507000000 && 34.0853879,66.3961632 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871508000000 && 34.0853693,66.3960057 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871509000000 && 34.0853358,66.3956905 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871510000000 && 34.0852612,66.3952629 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871511000000 && 34.0851754,66.3949387 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871512000000 && 34.0851158,66.3947992 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871513000000 && 34.0850114,66.3946551 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871514000000 && 34.0848473,66.3943085 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871515000000 && 34.0846833,66.3940293 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871516000000 && 34.0845469,66.3937354 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871517000000 && 34.084482,66.3935277 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871518000000 && 34.08431,66.3928223 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871519000000 && 34.0841802,66.3922854 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871520000000 && 34.0841445,66.3921717 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871521000000 && 34.0840925,66.392062 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871522000000 && 34.0840146,66.3919601 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871523000000 && 34.0839335,66.3918268 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871524000000 && 34.0838653,66.3917014 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871525000000 && 34.0838166,66.3915838 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871526000000 && 34.0837744,66.3914741 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871527000000 && 34.0837322,66.3913291 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871528000000 && 34.0836121,66.3910861 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871529000000 && 34.0834466,66.3907726 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871530000000 && 34.0832519,66.3904042 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871531000000 && 34.083135,66.3901769 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871532000000 && 34.0830571,66.3900554 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871533000000 && 34.0829695,66.3899457 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871534000000 && 34.0829045,66.3898085 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871535000000 && 34.0828007,66.3895302 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871536000000 && 34.0826643,66.3892598 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871537000000 && 34.0825475,66.3889698 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871538000000 && 34.0824599,66.3887151 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871539000000 && 34.0823982,66.3884878 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871540000000 && 34.0822911,66.3881468 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871541000000 && 34.0822164,66.3878842 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871542000000 && 34.0821223,66.3876373 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871543000000 && 34.0820184,66.3873904 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871544000000 && 34.0819632,66.3872454 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871545000000 && 34.0818723,66.3870142 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871546000000 && 34.0817587,66.3868104 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871547000000 && 34.0815737,66.3865361 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871548000000 && 34.0813887,66.386297 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871549000000 && 34.0811095,66.3860109 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871550000000 && 34.0809753,66.385874 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871551000000 && 34.0808784,66.3856444 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871552000000 && 34.0808523,66.3855453 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871553000000 && 34.080856,66.3854058 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871554000000 && 34.080856,66.3853472 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871555000000 && 34.0808523,66.3852887 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871556000000 && 34.0808187,66.3852572 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871557000000 && 34.0807031,66.3851447 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871558000000 && 34.0805801,66.3850276 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871559000000 && 34.0804682,66.3848926 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871560000000 && 34.080416,66.3847665 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871561000000 && 34.0803787,66.3845954 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871562000000 && 34.0803638,66.3844289 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871563000000 && 34.0803564,66.3843208 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907603000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871564000000 && 34.0803675,66.3842578 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871565000000 && 34.0803675,66.3842083 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871566000000 && 34.0803526,66.3841362 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871567000000 && 34.0803153,66.3840282 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871568000000 && 34.0802557,66.3838751 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871569000000 && 34.080237,66.3837581 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871570000000 && 34.0801923,66.383533 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871571000000 && 34.0801401,66.3832674 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871572000000 && 34.080155,66.3831548 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871573000000 && 34.0802035,66.3830108 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871574000000 && 34.0802818,66.3826776 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871575000000 && 34.0803042,66.3825516 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871576000000 && 34.0803489,66.382304 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871577000000 && 34.0804309,66.3821059 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871578000000 && 34.0805167,66.3819168 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871579000000 && 34.0806509,66.3817143 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871580000000 && 34.0807479,66.3815522 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871581000000 && 34.0808187,66.3814576 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871582000000 && 34.0808635,66.3813271 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871583000000 && 34.0808858,66.381165 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871584000000 && 34.0808746,66.381048 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871585000000 && 34.0808374,66.3809219 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871586000000 && 34.0807777,66.3808634 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871587000000 && 34.0806957,66.3808364 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871588000000 && 34.0805577,66.3808814 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871589000000 && 34.0803564,66.3809715 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871590000000 && 34.080237,66.3809895 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871591000000 && 34.0801177,66.3809715 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871592000000 && 34.0799984,66.3809084 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871593000000 && 34.0799276,66.3808184 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871594000000 && 34.0798604,66.3807419 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871595000000 && 34.079812,66.3806383 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871596000000 && 34.079771,66.3805123 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871597000000 && 34.079756,66.3804087 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871598000000 && 34.0797299,66.3803322 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871599000000 && 34.0796405,66.3802286 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871600000000 && 34.079495,66.3800216 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871601000000 && 34.0793049,66.3796794 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871602000000 && 34.0791893,66.3794363 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871603000000 && 34.0790812,66.3792112 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871604000000 && 34.0790439,66.3791032 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871605000000 && 34.0790103,66.3788421 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871606000000 && 34.0789917,66.3786935 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871607000000 && 34.0789395,66.3786125 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871608000000 && 34.07885,66.378626 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871609000000 && 34.0787568,66.3787025 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871610000000 && 34.0786486,66.3788241 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871611000000 && 34.0785218,66.3790627 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871612000000 && 34.0784174,66.3793328 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871613000000 && 34.0783652,66.3793733 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871614000000 && 34.0783093,66.3793688 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907604000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871615000000 && 34.0782757,66.3792923 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871616000000 && 34.0782646,66.3791797 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871617000000 && 34.0782496,66.3790807 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871618000000 && 34.0782198,66.3789816 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871619000000 && 34.0781863,66.3788601 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871620000000 && 34.0781751,66.3787115 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871621000000 && 34.0782198,66.3784774 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871622000000 && 34.0783056,66.3782884 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871623000000 && 34.0784659,66.3780363 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871624000000 && 34.0785666,66.3778427 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871625000000 && 34.0786337,66.3776536 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871626000000 && 34.0786374,66.3775546 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871627000000 && 34.0786151,66.377424 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871628000000 && 34.0785479,66.3772169 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871629000000 && 34.0785144,66.3770639 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871630000000 && 34.0785144,66.3768253 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871631000000 && 34.0785256,66.3765912 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871632000000 && 34.0785144,66.3764066 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871633000000 && 34.0784808,66.376294 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871634000000 && 34.0783839,66.3760825 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871635000000 && 34.0782981,66.3758934 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871636000000 && 34.0782347,66.3757133 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871637000000 && 34.0781452,66.3755602 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871638000000 && 34.0780185,66.3754072 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871639000000 && 34.0779141,66.3752811 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871640000000 && 34.0778768,66.3752271 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871641000000 && 34.0778544,66.3751371 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871642000000 && 34.0778656,66.3750425 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871643000000 && 34.0779029,66.3749345 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871644000000 && 34.0779625,66.3748264 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871645000000 && 34.0780185,66.3746464 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871646000000 && 34.0780334,66.3745518 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871647000000 && 34.0780073,66.3744483 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871648000000 && 34.0779774,66.3743673 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871649000000 && 34.077929,66.3742727 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871650000000 && 34.0778693,66.3741692 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871651000000 && 34.0778581,66.3740791 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871652000000 && 34.077888,66.3738405 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871653000000 && 34.0779066,66.373737 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871654000000 && 34.0779103,66.373647 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871655000000 && 34.077888,66.3735254 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871656000000 && 34.0778134,66.3730032 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871657000000 && 34.0778134,66.3728861 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871658000000 && 34.0777985,66.3728096 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871659000000 && 34.0777724,66.3727511 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871660000000 && 34.0777164,66.3727241 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871661000000 && 34.0776642,66.3727466 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871662000000 && 34.0776008,66.3728186 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871663000000 && 34.0775113,66.3729042 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871664000000 && 34.0774181,66.3729942 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871665000000 && 34.077351,66.3730347 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871666000000 && 34.0772802,66.3730257 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871667000000 && 34.0772578,66.3729717 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871668000000 && 34.0772503,66.3728726 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871669000000 && 34.0772652,66.3727196 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871670000000 && 34.0772615,66.372553 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871671000000 && 34.0772354,66.372436 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871672000000 && 34.0772056,66.3723144 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871673000000 && 34.0771907,66.3721929 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871674000000 && 34.0771907,66.3720713 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871675000000 && 34.077213,66.3718147 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871676000000 && 34.0772354,66.3715941 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871677000000 && 34.0772242,66.3714951 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871678000000 && 34.0771981,66.371414 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871679000000 && 34.0771571,66.3713105 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871680000000 && 34.0770975,66.3711935 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871681000000 && 34.0770154,66.3710629 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907605000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871682000000 && 34.0769744,66.3708828 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871683000000 && 34.0769371,66.3706262 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871684000000 && 34.0768961,66.3703201 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871685000000 && 34.0768364,66.3701085 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871686000000 && 34.0767283,66.3698879 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871687000000 && 34.0765829,66.3697124 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871688000000 && 34.0764412,66.3696088 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871689000000 && 34.0762436,66.3695368 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871690000000 && 34.0761205,66.3694512 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871691000000 && 34.0760273,66.3693567 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871692000000 && 34.0758259,66.3692892 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871693000000 && 34.075412,66.3692937 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871694000000 && 34.0752927,66.3693252 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871695000000 && 34.075151,66.3692982 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871696000000 && 34.0749795,66.3692126 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871697000000 && 34.0747371,66.3689515 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871698000000 && 34.0745991,66.368794 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871699000000 && 34.0744462,66.3685779 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871700000000 && 34.0741852,66.3682898 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871701000000 && 34.0740696,66.3681682 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871702000000 && 34.0739838,66.3681052 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871703000000 && 34.0739093,66.3680242 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871704000000 && 34.0738123,66.3677856 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871705000000 && 34.073652,66.3674074 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871706000000 && 34.0736184,66.3672769 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871707000000 && 34.0735774,66.3669617 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871708000000 && 34.0735177,66.3667682 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871709000000 && 34.0734655,66.3666106 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871710000000 && 34.0733984,66.36648 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871711000000 && 34.0732791,66.366318 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871712000000 && 34.0731038,66.3661019 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871713000000 && 34.0729584,66.3659308 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871714000000 && 34.0728987,66.3658453 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871715000000 && 34.0728391,66.3657417 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871716000000 && 34.0724923,66.365251 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871717000000 && 34.072399,66.3651745 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871718000000 && 34.0723095,66.3651025 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871719000000 && 34.0722238,66.3650259 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871720000000 && 34.0721492,66.3648684 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871721000000 && 34.0721156,66.3647378 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871722000000 && 34.0721044,66.3644992 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871723000000 && 34.0721305,66.3640761 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871724000000 && 34.0721343,66.3638194 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871725000000 && 34.0721268,66.3636619 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871726000000 && 34.0720821,66.3634548 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871727000000 && 34.0719963,66.3632342 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871728000000 && 34.0719665,66.3631352 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871729000000 && 34.0719739,66.3629731 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871730000000 && 34.0719851,66.362802 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871731000000 && 34.0719777,66.362613 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871732000000 && 34.0719888,66.3623609 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871733000000 && 34.0720224,66.3622033 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871734000000 && 34.0721268,66.3620232 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871735000000 && 34.0722089,66.3618972 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871736000000 && 34.072235,66.3617666 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871737000000 && 34.0722238,66.3614245 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871738000000 && 34.0721604,66.3612174 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871739000000 && 34.072041,66.3609428 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871740000000 && 34.0718185,66.360584 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871741000000 && 34.0716096,66.360201 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907606000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871742000000 && 34.0714239,66.3598648 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871743000000 && 34.0712924,66.3595659 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871744000000 && 34.0712537,66.3592857 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871745000000 && 34.0712847,66.3589681 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871746000000 && 34.0713233,66.3586506 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871747000000 && 34.0714471,66.3584358 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871748000000 && 34.0715941,66.3582676 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871749000000 && 34.0716638,66.3580902 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871750000000 && 34.071656,66.3579127 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871751000000 && 34.0715941,66.3575765 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871752000000 && 34.0714935,66.3573617 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871753000000 && 34.0713156,66.3571001 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871754000000 && 34.0711531,66.3569507 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871755000000 && 34.0708957,66.3566393 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871756000000 && 34.0706027,66.3561735 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871757000000 && 34.070341,66.355815 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871758000000 && 34.0700686,66.355285 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871759000000 && 34.0699409,66.3550821 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871760000000 && 34.0698659,66.35498 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871761000000 && 34.0698043,66.3544277 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871762000000 && 34.06982,66.3548763 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871763000000 && 34.0698037,66.354652 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871764000000 && 34.0697727,66.3542553 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871765000000 && 34.0697323,66.3541414 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871766000000 && 34.0696716,66.3540805 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871767000000 && 34.0696009,66.3540032 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871768000000 && 34.0695134,66.3539056 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871769000000 && 34.0694157,66.3538447 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871770000000 && 34.069281,66.3538203 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871771000000 && 34.0690722,66.3538203 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871772000000 && 34.0689139,66.3537674 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871773000000 && 34.0687691,66.3536902 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871774000000 && 34.0686647,66.3535885 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871775000000 && 34.0685367,66.3533812 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871776000000 && 34.0684817,66.3532339 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871777000000 && 34.0684379,66.3531079 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871778000000 && 34.0684312,66.3529616 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871779000000 && 34.068411,66.3527908 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871780000000 && 34.0683638,66.3526323 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871781000000 && 34.0683167,66.3525265 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871782000000 && 34.068256,66.3524412 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871783000000 && 34.0681651,66.3523639 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871784000000 && 34.0680641,66.3522704 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871785000000 && 34.0680169,66.3521403 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871786000000 && 34.0680001,66.3520387 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871787000000 && 34.0679395,66.3518476 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871788000000 && 34.0678654,66.3516484 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871789000000 && 34.0677778,66.3514289 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871790000000 && 34.0676734,66.3509654 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871791000000 && 34.0675657,66.350319 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871792000000 && 34.0675118,66.3497336 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871793000000 && 34.0674747,66.349327 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871794000000 && 34.0674646,66.3492498 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871795000000 && 34.0674815,66.3491197 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871796000000 && 34.0675118,66.348892 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871797000000 && 34.0675118,66.3487619 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871798000000 && 34.0674646,66.3486278 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871799000000 && 34.0674074,66.3485424 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871800000000 && 34.06734,66.3484936 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907607000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871801000000 && 34.0672558,66.3484164 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871802000000 && 34.0672121,66.3483594 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871803000000 && 34.0670134,66.3482415 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871804000000 && 34.0664779,66.3479122 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871805000000 && 34.0662792,66.347774 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871806000000 && 34.0662219,66.347713 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871807000000 && 34.066195,66.3476439 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871808000000 && 34.0661579,66.3475626 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871809000000 && 34.0661377,66.3474691 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871810000000 && 34.0661209,66.3473431 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871811000000 && 34.0660906,66.3472211 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871812000000 && 34.0660468,66.3471357 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871813000000 && 34.0659929,66.3470707 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871814000000 && 34.065582,66.3468349 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871815000000 && 34.0654675,66.3468105 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871816000000 && 34.0653732,66.3468105 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871817000000 && 34.0652722,66.346843 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871818000000 && 34.0651981,66.3468593 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871819000000 && 34.065087,66.346843 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871820000000 && 34.0648108,66.3467333 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871821000000 && 34.0646054,66.3466682 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871822000000 && 34.0642888,66.3466235 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871823000000 && 34.064016,66.3465747 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871824000000 && 34.0638072,66.3465259 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871825000000 && 34.0637264,66.3464975 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871826000000 && 34.0635681,66.3463999 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871827000000 && 34.0630326,66.346095 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871828000000 && 34.0629079,66.3460218 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871829000000 && 34.0628271,66.3459893 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871830000000 && 34.0627059,66.3459527 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871831000000 && 34.0625981,66.345912 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871832000000 && 34.0624903,66.3458267 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871833000000 && 34.0623859,66.3457128 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871834000000 && 34.0623017,66.3456315 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871835000000 && 34.0622478,66.3456193 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871836000000 && 34.0622209,66.3456478 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871837000000 && 34.0621973,66.3457006 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871838000000 && 34.0621838,66.3458185 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871839000000 && 34.0621569,66.3460259 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871840000000 && 34.0621266,66.346156 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871841000000 && 34.0620626,66.3462901 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871842000000 && 34.0620491,66.3463511 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871843000000 && 34.0620525,66.3464446 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871844000000 && 34.0620862,66.3465219 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871845000000 && 34.0621367,66.3465869 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871846000000 && 34.0621805,66.3466438 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871847000000 && 34.0622007,66.3467211 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871848000000 && 34.0621973,66.3467942 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871849000000 && 34.0621771,66.3468837 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871850000000 && 34.062167,66.3469731 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871851000000 && 34.0621906,66.3470666 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871852000000 && 34.0622142,66.3471601 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871853000000 && 34.062268,66.3472577 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871854000000 && 34.0624028,66.3473065 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871855000000 && 34.0626149,66.3472374 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871856000000 && 34.0627328,66.3472821 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871857000000 && 34.0628305,66.3474041 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871858000000 && 34.0628844,66.3474894 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871859000000 && 34.0629349,66.347526 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871860000000 && 34.0629719,66.3475748 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871861000000 && 34.0629517,66.3476195 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871862000000 && 34.0628945,66.3476561 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871863000000 && 34.0628035,66.3476561 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907608000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871864000000 && 34.0626756,66.3476236 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871865000000 && 34.062625,66.3476114 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871866000000 && 34.0625611,66.3476277 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871867000000 && 34.0624196,66.3476683 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871868000000 && 34.0622748,66.3476724 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871869000000 && 34.0621333,66.3476683 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871870000000 && 34.0619548,66.3476236 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871871000000 && 34.0616955,66.3475179 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871872000000 && 34.0614698,66.3473268 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871873000000 && 34.0611802,66.3470341 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871874000000 && 34.060968,66.3467251 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871875000000 && 34.0608569,66.3465462 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871876000000 && 34.0607962,66.3464365 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871877000000 && 34.0607659,66.3462779 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871878000000 && 34.0607727,66.3460543 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871879000000 && 34.0607895,66.3459405 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871880000000 && 34.060776,66.3457982 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871881000000 && 34.0607087,66.3455461 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871882000000 && 34.060483,66.3447818 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871883000000 && 34.0604561,66.3446802 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871884000000 && 34.0604426,66.3446111 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871885000000 && 34.0603988,66.3445257 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871886000000 && 34.060355,66.3444525 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871887000000 && 34.060291,66.344355 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871888000000 && 34.0601226,66.3440297 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871889000000 && 34.0600283,66.3437736 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871890000000 && 34.0599475,66.3435703 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871891000000 && 34.0598161,66.3433549 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871892000000 && 34.0597454,66.3431922 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871893000000 && 34.059705,66.3430418 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871894000000 && 34.0596983,66.3428751 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871895000000 && 34.059806,66.3425377 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871896000000 && 34.0598464,66.3424361 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871897000000 && 34.0599138,66.3423913 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871898000000 && 34.0600283,66.3423669 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871899000000 && 34.0602978,66.3422247 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871900000000 && 34.0604325,66.3421352 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871901000000 && 34.0605032,66.3420661 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871902000000 && 34.0605201,66.3419767 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871903000000 && 34.0605099,66.3419075 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871904000000 && 34.0604662,66.3418669 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871905000000 && 34.0604257,66.3418384 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871906000000 && 34.0603483,66.3418181 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871907000000 && 34.0601327,66.3418222 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871908000000 && 34.0599711,66.3418181 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871909000000 && 34.059806,66.3417653 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871910000000 && 34.0595568,66.3416474 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871911000000 && 34.0593716,66.341562 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871912000000 && 34.0591526,66.3414644 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871913000000 && 34.0590078,66.341379 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871914000000 && 34.0588933,66.3413384 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871915000000 && 34.0587687,66.341314 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907609000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871916000000 && 34.0586272,66.3412774 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871917000000 && 34.0584857,66.3412083 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871918000000 && 34.0584083,66.3411798 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871919000000 && 34.0583207,66.3411554 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871920000000 && 34.0581119,66.3411107 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871921000000 && 34.0579906,66.3410741 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871922000000 && 34.0578727,66.3410091 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871923000000 && 34.0577245,66.3408749 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871924000000 && 34.0575258,66.3406513 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871925000000 && 34.0573406,66.3404684 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871926000000 && 34.057216,66.3403708 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871927000000 && 34.0569836,66.3401269 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871928000000 && 34.0568421,66.3399764 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871929000000 && 34.0566804,66.3398423 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871930000000 && 34.0565356,66.3397447 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871931000000 && 34.0564177,66.3396837 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871932000000 && 34.0562661,66.339639 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871933000000 && 34.0561584,66.3395862 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871934000000 && 34.0561011,66.3395374 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871935000000 && 34.056064,66.3394764 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871936000000 && 34.0560674,66.3393544 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871937000000 && 34.0560539,66.3392487 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871938000000 && 34.0559798,66.3391268 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871939000000 && 34.0558788,66.3390577 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871940000000 && 34.0557542,66.3390211 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871941000000 && 34.0556464,66.3390211 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871942000000 && 34.0555723,66.3390251 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871943000000 && 34.0554813,66.3390495 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871944000000 && 34.0553298,66.3391715 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871945000000 && 34.0551445,66.3392934 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871946000000 && 34.0549492,66.339391 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871947000000 && 34.0548279,66.339391 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871948000000 && 34.0546763,66.3393788 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871949000000 && 34.0545147,66.33933 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871950000000 && 34.0543563,66.3392609 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871951000000 && 34.0541441,66.3391756 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871952000000 && 34.0537298,66.3389967 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871953000000 && 34.0536389,66.3389357 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871954000000 && 34.053548,66.3388381 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871955000000 && 34.0534941,66.3386836 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871956000000 && 34.0534873,66.3385169 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871957000000 && 34.0534806,66.3384316 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871958000000 && 34.0534435,66.3383421 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871959000000 && 34.0533661,66.3382771 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871960000000 && 34.0532549,66.3381795 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871961000000 && 34.0531909,66.3380779 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871962000000 && 34.0531101,66.3379518 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871963000000 && 34.0529012,66.3376307 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871964000000 && 34.0528844,66.3375453 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871965000000 && 34.052881,66.3372648 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871966000000 && 34.0528911,66.3369599 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871967000000 && 34.0528642,66.3366915 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871968000000 && 34.0528474,66.3362687 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871969000000 && 34.0528575,66.3359516 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907610000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871970000000 && 34.052881,66.3358663 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871971000000 && 34.0529383,66.3357606 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871972000000 && 34.0529585,66.3356711 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871973000000 && 34.0529417,66.3355573 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871974000000 && 34.0529181,66.3354394 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871975000000 && 34.0529282,66.3353499 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871976000000 && 34.0529551,66.3352401 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871977000000 && 34.0529753,66.3351344 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871978000000 && 34.0529753,66.3350165 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871979000000 && 34.0529484,66.3348742 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871980000000 && 34.0529282,66.3347807 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871981000000 && 34.0528776,66.3346222 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871982000000 && 34.0528675,66.3345408 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871983000000 && 34.0528675,66.3344555 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871984000000 && 34.0528675,66.3343864 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871985000000 && 34.0528608,66.3343172 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871986000000 && 34.052844,66.3342685 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871987000000 && 34.0528069,66.3341872 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871988000000 && 34.0527901,66.334118 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871989000000 && 34.0527833,66.3340205 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871990000000 && 34.0527598,66.3338172 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871991000000 && 34.0527193,66.3335123 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871992000000 && 34.0527193,66.3334269 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871993000000 && 34.0527362,66.3333375 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871994000000 && 34.0527598,66.3332196 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871995000000 && 34.0527833,66.3330976 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871996000000 && 34.0528103,66.3329065 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871997000000 && 34.0528136,66.3327277 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871998000000 && 34.0527867,66.3324553 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767871999000000 && 34.052753,66.3321869 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872000000000 && 34.0526991,66.3319796 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872001000000 && 34.0526823,66.3318576 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872002000000 && 34.0527126,66.3317235 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872003000000 && 34.0527025,66.3316422 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872004000000 && 34.0526587,66.3315243 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872005000000 && 34.0526183,66.3314104 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872006000000 && 34.0525711,66.3312803 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872007000000 && 34.0525341,66.3311462 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872008000000 && 34.052497,66.3309714 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872009000000 && 34.0524061,66.3307234 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872010000000 && 34.0523758,66.3306055 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872011000000 && 34.0523589,66.3304794 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872012000000 && 34.0523993,66.3303412 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872013000000 && 34.0524229,66.3302477 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872014000000 && 34.0524229,66.3301217 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872015000000 && 34.0523993,66.3299347 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872016000000 && 34.0523421,66.3297964 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872017000000 && 34.0522875,66.3296628 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872018000000 && 34.0522707,66.3295205 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872019000000 && 34.0522572,66.3292928 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872020000000 && 34.0522572,66.3292075 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872021000000 && 34.0522808,66.3291505 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872022000000 && 34.0523448,66.3290977 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872023000000 && 34.052392,66.3290326 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872024000000 && 34.052392,66.3289513 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872025000000 && 34.0523583,66.3289025 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872026000000 && 34.0522741,66.3288619 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872027000000 && 34.0522168,66.3287928 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872028000000 && 34.0521191,66.3285732 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872029000000 && 34.0520518,66.3284594 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872030000000 && 34.0520282,66.3283984 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872031000000 && 34.0519878,66.3282887 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872032000000 && 34.0519878,66.3281952 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872033000000 && 34.0519878,66.3281138 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872034000000 && 34.0519675,66.3280285 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872035000000 && 34.0519103,66.3279553 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872036000000 && 34.0518261,66.3278862 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907611000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872037000000 && 34.0517486,66.3277886 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872038000000 && 34.0516745,66.3276748 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872039000000 && 34.0516543,66.3276057 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872040000000 && 34.0516543,66.3275162 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872041000000 && 34.0516543,66.327443 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872042000000 && 34.0516374,66.3273821 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872043000000 && 34.0515937,66.3273373 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872044000000 && 34.0515263,66.3272926 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872045000000 && 34.0514421,66.3271869 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872046000000 && 34.0513478,66.3270202 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872047000000 && 34.0512602,66.3268414 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872048000000 && 34.0511625,66.3265364 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872049000000 && 34.0510783,66.3262519 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872050000000 && 34.0510513,66.3261055 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872051000000 && 34.0510614,66.3259673 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872052000000 && 34.0511052,66.3257518 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872153000000 && 34.0511524,66.3256136 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872154000000 && 34.0511996,66.3255241 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872155000000 && 34.051213,66.3254347 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872156000000 && 34.0511591,66.3252883 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872157000000 && 34.0510749,66.3251461 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872158000000 && 34.0509638,66.3250444 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872159000000 && 34.0508593,66.3249265 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872160000000 && 34.0507516,66.3247639 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872161000000 && 34.0506404,66.3245688 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872162000000 && 34.0505629,66.3244427 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872163000000 && 34.0505292,66.3243614 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872164000000 && 34.0505326,66.3243086 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872165000000 && 34.0505764,66.3242801 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872166000000 && 34.0506471,66.3242882 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872167000000 && 34.0508897,66.3243045 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872168000000 && 34.051149,66.3243452 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872169000000 && 34.0512905,66.3243452 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872170000000 && 34.0513747,66.3243004 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872171000000 && 34.0514556,66.3241866 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872172000000 && 34.0514892,66.3240768 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872173000000 && 34.0515768,66.3239264 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872174000000 && 34.0516644,66.3238614 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872175000000 && 34.0517722,66.3238695 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872176000000 && 34.0519574,66.3239711 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872177000000 && 34.0520518,66.3240443 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872178000000 && 34.0521259,66.3240687 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872179000000 && 34.0521831,66.3240524 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872180000000 && 34.052237,66.3239793 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872181000000 && 34.0522875,66.3238532 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872182000000 && 34.052328,66.3237719 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872183000000 && 34.0523515,66.3236987 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872184000000 && 34.0523515,66.3236378 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872185000000 && 34.052328,66.3235565 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872186000000 && 34.0522707,66.323406 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872187000000 && 34.0522303,66.3232312 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872188000000 && 34.0521696,66.3230157 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872189000000 && 34.0520922,66.322784 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872190000000 && 34.0520416,66.3226377 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872191000000 && 34.0520282,66.322536 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872192000000 && 34.0520315,66.3224222 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872193000000 && 34.0520383,66.3222067 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872194000000 && 34.052072,66.3220034 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872195000000 && 34.0521663,66.321853 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872196000000 && 34.0522572,66.3217229 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872197000000 && 34.0524155,66.3215969 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872198000000 && 34.0525536,66.3215197 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872199000000 && 34.0526648,66.321479 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907612000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872200000000 && 34.0527793,66.321479 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872201000000 && 34.0529275,66.3214831 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872202000000 && 34.0530589,66.3214383 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872203000000 && 34.0533182,66.3213245 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872204000000 && 34.0534126,66.3212839 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872205000000 && 34.0534597,66.3212473 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872206000000 && 34.0535136,66.3211497 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872207000000 && 34.0536248,66.3210399 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872208000000 && 34.0537864,66.3209058 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872209000000 && 34.0540155,66.3207513 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872210000000 && 34.0542309,66.3206932 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872211000000 && 34.0544475,66.3206091 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872212000000 && 34.0546178,66.3205904 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872213000000 && 34.054699,66.3205998 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872214000000 && 34.0547532,66.3205671 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872215000000 && 34.0547687,66.3205157 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872216000000 && 34.0547764,66.3204223 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872217000000 && 34.0547803,66.3203476 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872218000000 && 34.0548267,66.3202635 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872219000000 && 34.0549428,66.3201841 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872220000000 && 34.0550356,66.3201468 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872221000000 && 34.0550821,66.3201141 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872222000000 && 34.0551053,66.3200347 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872223000000 && 34.0551208,66.319946 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872224000000 && 34.0551478,66.3198479 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872225000000 && 34.0551749,66.3197779 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872226000000 && 34.0552059,66.3196985 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872227000000 && 34.0552446,66.3195163 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872228000000 && 34.0552987,66.3192175 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872229000000 && 34.0553297,66.3190167 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872230000000 && 34.055349,66.3188392 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872231000000 && 34.0553839,66.3186197 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872232000000 && 34.0554342,66.3184189 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872233000000 && 34.0554419,66.3183208 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872234000000 && 34.0554342,66.3181294 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872235000000 && 34.0554264,66.3179659 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872236000000 && 34.0554225,66.3177978 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872237000000 && 34.0554612,66.317639 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872238000000 && 34.055527,66.3174148 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872239000000 && 34.055558,66.3172794 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872240000000 && 34.055585,66.3171066 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872241000000 && 34.0556315,66.3167844 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872242000000 && 34.0557205,66.3163781 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872243000000 && 34.0558443,66.3159298 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872244000000 && 34.055883,66.3157897 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872245000000 && 34.0559333,66.3156449 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872246000000 && 34.0560493,66.3154815 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872247000000 && 34.0561228,66.31536 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872248000000 && 34.0561731,66.3151966 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872249000000 && 34.0562002,66.3150472 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872250000000 && 34.0562157,66.3149164 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872251000000 && 34.056235,66.3148183 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872252000000 && 34.0562815,66.3147016 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872453000000 && 34.0563705,66.3145615 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872454000000 && 34.0564672,66.3143513 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872455000000 && 34.0565484,66.3141552 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872456000000 && 34.0566529,66.313903 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872457000000 && 34.0567535,66.3136555 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872458000000 && 34.0568193,66.3135247 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872459000000 && 34.0569779,66.3133566 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872460000000 && 34.0571056,66.3132072 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872461000000 && 34.0572333,66.3130624 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872462000000 && 34.0573764,66.313025 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872463000000 && 34.0575234,66.3130764 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872464000000 && 34.0577324,66.3132118 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872465000000 && 34.0578794,66.3133846 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872466000000 && 34.0580341,66.3135434 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872467000000 && 34.0581502,66.3135854 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907613000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872468000000 && 34.0582701,66.3136508 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872469000000 && 34.0583978,66.3137582 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872470000000 && 34.0586338,66.3138516 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872471000000 && 34.0587344,66.3138423 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872472000000 && 34.0588543,66.3137255 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872473000000 && 34.0589627,66.3135948 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872474000000 && 34.0590517,66.313478 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872475000000 && 34.0591948,66.3132399 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872476000000 && 34.0592645,66.3131465 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872477000000 && 34.0593341,66.3131231 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872478000000 && 34.0594192,66.3131605 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872479000000 && 34.0594966,66.3132399 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872480000000 && 34.0595469,66.3133426 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872481000000 && 34.0595933,66.3134734 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872482000000 && 34.0596513,66.3136602 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872483000000 && 34.0597519,66.3138423 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872484000000 && 34.0598912,66.3140198 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872485000000 && 34.0599415,66.3141458 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872486000000 && 34.0599957,66.3142533 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872487000000 && 34.0600614,66.3142533 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872488000000 && 34.0601272,66.3142159 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872489000000 && 34.0601814,66.3141038 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872490000000 && 34.0602201,66.3139964 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872491000000 && 34.0602858,66.3137769 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872492000000 && 34.0603245,66.3136135 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872493000000 && 34.0603748,66.3134967 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872494000000 && 34.0604483,66.3134313 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872495000000 && 34.0605644,66.3133426 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872496000000 && 34.0606302,66.3132912 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872497000000 && 34.0606882,66.3131978 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872498000000 && 34.0606998,66.3131138 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872499000000 && 34.060754,66.3130344 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872500000000 && 34.0608507,66.3130391 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872501000000 && 34.0610635,66.3131138 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872502000000 && 34.0613188,66.3131745 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872503000000 && 34.0614774,66.3131978 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872504000000 && 34.0615509,66.3131698 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872505000000 && 34.0615548,66.3130951 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872506000000 && 34.0615161,66.3129503 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872507000000 && 34.0614774,66.3127448 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872508000000 && 34.0614155,66.3125254 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872509000000 && 34.0613923,66.3124086 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872510000000 && 34.0614078,66.3123386 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872511000000 && 34.0614929,66.3123339 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872512000000 && 34.0615974,66.3123526 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872513000000 && 34.0617482,66.3123199 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872514000000 && 34.061872,66.3122638 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872515000000 && 34.0619572,66.3121704 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872516000000 && 34.0620345,66.3119416 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872517000000 && 34.0621506,66.3115493 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872518000000 && 34.0624253,66.3110029 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872519000000 && 34.0626342,66.3105499 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872520000000 && 34.0626535,66.3104612 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872521000000 && 34.0627309,66.3103725 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872522000000 && 34.0628199,66.3102978 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872523000000 && 34.0628857,66.3102557 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872524000000 && 34.0629746,66.310181 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872525000000 && 34.063052,66.3100689 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872526000000 && 34.0631255,66.3099382 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872527000000 && 34.0631797,66.3098354 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872528000000 && 34.0632687,66.3097701 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872529000000 && 34.0634195,66.309742 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872530000000 && 34.0635898,66.3097047 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872531000000 && 34.0637213,66.3096953 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872532000000 && 34.0638722,66.3097234 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872533000000 && 34.0640269,66.3097841 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872534000000 && 34.0641623,66.3098448 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872535000000 && 34.0642358,66.3098588 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872536000000 && 34.0643016,66.3098308 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907614000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872537000000 && 34.0644177,66.309742 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872538000000 && 34.0644989,66.3096253 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872539000000 && 34.0645569,66.3094899 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872540000000 && 34.0645956,66.3093451 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872541000000 && 34.0646072,66.3091349 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872542000000 && 34.0646188,66.3088874 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872543000000 && 34.0645741,66.3085292 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872544000000 && 34.0645606,66.3083585 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872545000000 && 34.064564,66.3082081 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872546000000 && 34.0646145,66.308082 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872547000000 && 34.064665,66.3080129 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872548000000 && 34.0647222,66.3079397 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872549000000 && 34.0647627,66.3078747 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872550000000 && 34.0647728,66.3078137 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872551000000 && 34.064702,66.3076795 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872552000000 && 34.0645673,66.307338 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872753000000 && 34.0644393,66.3070006 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872754000000 && 34.0642945,66.3065371 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872755000000 && 34.0642339,66.3063501 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872756000000 && 34.0642204,66.3062241 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872757000000 && 34.0642305,66.3061225 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872758000000 && 34.0642777,66.3059761 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872759000000 && 34.0643787,66.3057769 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872760000000 && 34.0644899,66.3056184 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872761000000 && 34.0646953,66.3053866 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872762000000 && 34.0648233,66.3052809 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872763000000 && 34.0648974,66.3052077 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872764000000 && 34.064931,66.3051468 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872765000000 && 34.0650152,66.3049435 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872766000000 && 34.0650557,66.304789 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872767000000 && 34.0650725,66.3046508 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872768000000 && 34.0651297,66.304476 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872769000000 && 34.065187,66.304354 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872770000000 && 34.0652409,66.3042442 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872771000000 && 34.0652981,66.3040369 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872772000000 && 34.0653217,66.3038946 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872773000000 && 34.0653386,66.3037279 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872774000000 && 34.0653183,66.3035246 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872775000000 && 34.0652746,66.303301 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872776000000 && 34.0652038,66.303053 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872777000000 && 34.0652005,66.3029189 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872778000000 && 34.065288,66.3028864 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872779000000 && 34.0654025,66.3029026 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872780000000 && 34.0661805,66.3030449 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872781000000 && 34.0662849,66.3030937 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872782000000 && 34.0663758,66.3031343 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872783000000 && 34.0664466,66.3031506 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872784000000 && 34.0665038,66.303114 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872785000000 && 34.0665476,66.3030449 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872786000000 && 34.0666049,66.3030002 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872787000000 && 34.0667901,66.3029107 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872788000000 && 34.0669383,66.302866 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872789000000 && 34.0670191,66.3028254 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872790000000 && 34.0671134,66.3027725 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872791000000 && 34.0672178,66.3026587 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872792000000 && 34.0672986,66.3025652 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872793000000 && 34.0673592,66.3025083 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872794000000 && 34.0674064,66.3024188 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872795000000 && 34.0673727,66.3023335 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872796000000 && 34.0673289,66.3022806 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872797000000 && 34.0672548,66.3022196 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872798000000 && 34.0671639,66.3021749 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872799000000 && 34.0670932,66.3021302 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872800000000 && 34.0670292,66.3020773 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872801000000 && 34.0669686,66.301996 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872802000000 && 34.0669282,66.3018659 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872803000000 && 34.0668743,66.3017318 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872804000000 && 34.0668642,66.3016627 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907615000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872805000000 && 34.0669147,66.3016139 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872806000000 && 34.0669955,66.3015651 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872807000000 && 34.067302,66.3014553 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872808000000 && 34.0673693,66.3014106 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872809000000 && 34.0674232,66.3013699 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872810000000 && 34.067467,66.3012846 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872811000000 && 34.0674872,66.3011626 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872812000000 && 34.0675007,66.3010162 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872813000000 && 34.0675344,66.3007154 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872814000000 && 34.067595,66.3003942 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872815000000 && 34.0676354,66.3002926 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872816000000 && 34.0677297,66.3001462 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872817000000 && 34.0678947,66.2999877 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872818000000 && 34.0679486,66.2999145 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872819000000 && 34.0680126,66.2997844 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872820000000 && 34.0680631,66.2996502 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872821000000 && 34.0680934,66.2995649 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872822000000 && 34.068154,66.299447 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872823000000 && 34.068218,66.2993779 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872824000000 && 34.0683258,66.2992925 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872825000000 && 34.0683797,66.2992071 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872826000000 && 34.0684032,66.2990486 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872827000000 && 34.0683224,66.2988819 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872828000000 && 34.0682483,66.2987274 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872829000000 && 34.0682247,66.298642 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872830000000 && 34.068255,66.2985566 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872831000000 && 34.0683258,66.2984591 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872832000000 && 34.0683864,66.2983412 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872833000000 && 34.0683763,66.2982029 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872834000000 && 34.0682618,66.297959 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872835000000 && 34.0681877,66.297837 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872836000000 && 34.0680361,66.2976582 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872837000000 && 34.0678812,66.2974468 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872838000000 && 34.0677768,66.2972638 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872839000000 && 34.0676994,66.2970687 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872840000000 && 34.0677027,66.296967 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872841000000 && 34.0677465,66.296841 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872842000000 && 34.0679014,66.296593 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872843000000 && 34.0680025,66.296471 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872844000000 && 34.0681271,66.2963979 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872845000000 && 34.0682012,66.2963491 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872846000000 && 34.0682382,66.2962922 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872847000000 && 34.068282,66.296219 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872848000000 && 34.0683056,66.2961174 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872849000000 && 34.0682685,66.2960116 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872850000000 && 34.0681102,66.2958572 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872851000000 && 34.0679418,66.2956295 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872852000000 && 34.0678307,66.2953774 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872953000000 && 34.0677263,66.2950603 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872954000000 && 34.0675714,66.2946619 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872955000000 && 34.0674535,66.2942269 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872956000000 && 34.0674165,66.2939748 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872957000000 && 34.0674434,66.2937147 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872958000000 && 34.0674805,66.2934423 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872959000000 && 34.0675007,66.2932919 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872960000000 && 34.0674939,66.2931211 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872961000000 && 34.0674939,66.2930439 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872962000000 && 34.0674468,66.2929341 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872963000000 && 34.0673053,66.2928812 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872964000000 && 34.0671471,66.2928772 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872965000000 && 34.0668911,66.2928162 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872966000000 && 34.0667295,66.2927755 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872967000000 && 34.0665947,66.2926454 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872968000000 && 34.0664466,66.292495 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907616000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872969000000 && 34.0662647,66.2923365 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872970000000 && 34.0661367,66.292243 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872971000000 && 34.0660727,66.2922023 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872972000000 && 34.0659852,66.2921942 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872973000000 && 34.0658303,66.292186 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872974000000 && 34.0657191,66.2921535 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872975000000 && 34.0655541,66.2920275 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872976000000 && 34.0654025,66.2918852 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872977000000 && 34.0653487,66.2917348 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872978000000 && 34.0653082,66.2915315 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872979000000 && 34.0652645,66.2912672 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872980000000 && 34.0652139,66.2909176 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872981000000 && 34.0651331,66.2906534 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872982000000 && 34.0650321,66.290442 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872983000000 && 34.0648536,66.2902346 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872984000000 && 34.064564,66.290137 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872985000000 && 34.0643686,66.2900598 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872986000000 && 34.0642507,66.2899826 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872987000000 && 34.0641362,66.2899175 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872988000000 && 34.064015,66.2898484 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872989000000 && 34.0639173,66.2897712 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872990000000 && 34.0637422,66.2896533 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872991000000 && 34.0635772,66.289576 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872992000000 && 34.0633818,66.2895272 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872993000000 && 34.0632471,66.2895232 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872994000000 && 34.0631629,66.2895272 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872995000000 && 34.0630956,66.2894866 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872996000000 && 34.0629878,66.2894337 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872997000000 && 34.0628362,66.2893158 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872998000000 && 34.0627183,66.289202 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767872999000000 && 34.0626544,66.2891369 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873000000000 && 34.0625803,66.2890719 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873001000000 && 34.0624354,66.2890719 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873002000000 && 34.0623041,66.2890678 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873003000000 && 34.062203,66.2890556 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873004000000 && 34.0620953,66.2890068 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873005000000 && 34.0620178,66.2889174 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873006000000 && 34.0619639,66.2888402 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873007000000 && 34.0618831,66.2887954 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873008000000 && 34.0617181,66.2887385 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873009000000 && 34.0615699,66.2886572 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873010000000 && 34.0615193,66.288584 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873011000000 && 34.0614857,66.2884865 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873012000000 && 34.0614419,66.2884092 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873013000000 && 34.0613947,66.2883117 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873014000000 && 34.0613543,66.2882669 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873015000000 && 34.061223,66.2882344 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873016000000 && 34.0610074,66.2882019 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873017000000 && 34.0608289,66.2881938 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873018000000 && 34.0606033,66.2882263 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873019000000 && 34.0604045,66.2883198 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873020000000 && 34.0602563,66.2883523 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873021000000 && 34.0600812,66.2883564 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873022000000 && 34.0599094,66.2883767 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873023000000 && 34.0597983,66.2884539 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873024000000 && 34.0597276,66.2885881 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873025000000 && 34.0596939,66.2888727 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873026000000 && 34.0596097,66.2891125 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873027000000 && 34.0595457,66.2892223 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873028000000 && 34.0594345,66.289328 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873029000000 && 34.059192,66.2894906 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873030000000 && 34.0589125,66.2896289 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873031000000 && 34.0587373,66.2896695 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873032000000 && 34.0585083,66.2896329 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873033000000 && 34.0582894,66.2895923 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873034000000 && 34.0580604,66.2894175 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873035000000 && 34.057801,66.2891939 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873036000000 && 34.057508,66.2889337 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907617000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873037000000 && 34.0571779,66.2887385 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873038000000 && 34.0569657,66.2885718 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873039000000 && 34.0568916,66.2884987 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873040000000 && 34.0568377,66.2884174 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873041000000 && 34.0567771,66.2883482 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873042000000 && 34.0566693,66.2882669 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873043000000 && 34.0565817,66.2882141 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873044000000 && 34.0564706,66.2881694 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873045000000 && 34.0563931,66.2881002 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873046000000 && 34.0562247,66.287958 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873047000000 && 34.056026,66.2878319 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873048000000 && 34.0554938,66.2875067 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873049000000 && 34.0550728,66.2873237 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873050000000 && 34.0548943,66.2872424 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873051000000 && 34.0547629,66.287092 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873052000000 && 34.054581,66.2868603 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873153000000 && 34.054389,66.2866042 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873154000000 && 34.0542375,66.2863684 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873155000000 && 34.0541095,66.2861732 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873156000000 && 34.0539343,66.2860309 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873157000000 && 34.0537491,66.2858439 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873158000000 && 34.0534358,66.285539 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873159000000 && 34.0533179,66.285352 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873160000000 && 34.0532337,66.2851568 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873161000000 && 34.0530215,66.2848194 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873162000000 && 34.0528295,66.2845958 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873163000000 && 34.0524994,66.2843925 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873164000000 && 34.0522973,66.2842868 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873165000000 && 34.0520817,66.2842055 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873166000000 && 34.0519167,66.2841893 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873167000000 && 34.0517112,66.2841486 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873168000000 && 34.0515866,66.284108 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873169000000 && 34.0514653,66.2840266 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873170000000 && 34.051307,66.2838478 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873171000000 && 34.0511083,66.2836242 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873172000000 && 34.0508523,66.2833762 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873173000000 && 34.0505356,66.2831566 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873174000000 && 34.0501752,66.2828883 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873175000000 && 34.049775,66.2826347 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873176000000 && 34.0492697,66.2824274 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873177000000 && 34.0490238,66.2823339 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873178000000 && 34.0486095,66.28209 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873179000000 && 34.0484646,66.2820412 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873180000000 && 34.0481918,66.2820818 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873181000000 && 34.0479998,66.2821388 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873182000000 && 34.0478684,66.2821753 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873183000000 && 34.0477573,66.2821713 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873184000000 && 34.0474676,66.2820168 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873185000000 && 34.0472284,66.281842 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873186000000 && 34.0470532,66.2817078 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873187000000 && 34.0468713,66.2816021 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873188000000 && 34.0467534,66.2815696 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873189000000 && 34.0466928,66.2815574 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873190000000 && 34.0466557,66.2815371 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873191000000 && 34.0466793,66.2814598 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873192000000 && 34.04674,66.2813216 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873193000000 && 34.0467837,66.2812281 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873194000000 && 34.0468848,66.2809557 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873195000000 && 34.0469185,66.2808337 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873196000000 && 34.0469421,66.2807565 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873197000000 && 34.0469151,66.2807158 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873198000000 && 34.0468376,66.2806671 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873199000000 && 34.0461538,66.2803093 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907618000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873200000000 && 34.0460528,66.2802361 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873201000000 && 34.046009,66.2801873 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873202000000 && 34.0460056,66.2800735 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873203000000 && 34.046009,66.2798621 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873204000000 && 34.0459888,66.2797401 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873205000000 && 34.0459685,66.2796751 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873206000000 && 34.0459012,66.2795978 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873207000000 && 34.0457024,66.2794555 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873208000000 && 34.045534,66.2793458 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873209000000 && 34.0454296,66.2792604 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873210000000 && 34.0452847,66.2791669 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873211000000 && 34.0451163,66.27911 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873212000000 && 34.044958,66.2791059 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873213000000 && 34.0447929,66.2790937 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873214000000 && 34.0446346,66.2790978 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873215000000 && 34.0445099,66.2791059 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873216000000 && 34.0443887,66.2791425 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873217000000 && 34.044291,66.2792116 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873218000000 && 34.0441731,66.2792279 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873219000000 && 34.0440787,66.2791994 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873220000000 && 34.0440046,66.2791669 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873221000000 && 34.0439204,66.279171 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873222000000 && 34.0438059,66.2792645 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873223000000 && 34.0436071,66.2793458 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873224000000 && 34.0434151,66.2793376 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873225000000 && 34.0431995,66.2793011 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873226000000 && 34.0429873,66.2791872 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873227000000 && 34.0426976,66.2790327 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873228000000 && 34.0424415,66.2788701 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873229000000 && 34.0423135,66.2787888 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873230000000 && 34.0422495,66.2787238 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873231000000 && 34.0421788,66.2786425 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873232000000 && 34.0420845,66.2784595 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873233000000 && 34.0419295,66.2782684 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873234000000 && 34.041751,66.2780652 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873235000000 && 34.0416768,66.2779879 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873236000000 && 34.0415893,66.2779229 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873237000000 && 34.0414815,66.2778781 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873238000000 && 34.0413501,66.2778253 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873239000000 && 34.041249,66.2777237 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873240000000 && 34.0410907,66.277557 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873241000000 && 34.0409526,66.2774187 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873242000000 && 34.0408414,66.2773293 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873243000000 && 34.0407134,66.2772805 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873244000000 && 34.0405854,66.2772358 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873245000000 && 34.0404439,66.2771342 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873246000000 && 34.040326,66.2769797 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873247000000 && 34.0401676,66.2768171 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873248000000 && 34.0400059,66.2766504 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873249000000 && 34.039915,66.2765975 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873250000000 && 34.0398004,66.2765609 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873251000000 && 34.0396252,66.2765365 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873252000000 && 34.0394871,66.2764796 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907619000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873253000000 && 34.0392883,66.2763617 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873254000000 && 34.0389852,66.2762682 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873255000000 && 34.0388437,66.2762235 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873256000000 && 34.0387527,66.2761625 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873257000000 && 34.0386483,66.2760609 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873258000000 && 34.0385304,66.2759186 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873259000000 && 34.0383754,66.2757763 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873260000000 && 34.0382608,66.275634 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873261000000 && 34.0380284,66.2753291 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873262000000 && 34.0378397,66.2751177 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873263000000 && 34.037678,66.274951 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873264000000 && 34.0375163,66.2748372 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873265000000 && 34.0374085,66.2747233 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873266000000 && 34.0372535,66.2745445 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873267000000 && 34.0370312,66.2742721 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873268000000 && 34.0369908,66.2742152 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873269000000 && 34.036947,66.2741867 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873270000000 && 34.0368829,66.2741623 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873271000000 && 34.0367886,66.2741664 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873272000000 && 34.0366437,66.2741257 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873273000000 && 34.0365023,66.2740403 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873274000000 && 34.0363473,66.2739062 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873275000000 && 34.0362327,66.2738249 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873276000000 && 34.0361519,66.2737192 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873277000000 && 34.0361114,66.2736541 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873278000000 && 34.0360946,66.2735891 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873279000000 && 34.0360778,66.2734874 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873280000000 && 34.0360643,66.2734346 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873281000000 && 34.0359666,66.273333 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873282000000 && 34.0358554,66.2732191 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873283000000 && 34.0356971,66.2730809 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873284000000 && 34.0355522,66.2729874 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873285000000 && 34.0353703,66.2728776 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873286000000 && 34.0352524,66.2728044 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873287000000 && 34.0351715,66.2727435 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873288000000 && 34.0351109,66.2727191 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873289000000 && 34.0350199,66.2727028 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873290000000 && 34.034966,66.2726865 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873291000000 && 34.0349458,66.2726337 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873292000000 && 34.0349795,66.2725849 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873293000000 && 34.0350199,66.2725361 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873294000000 && 34.0351243,66.2725524 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873295000000 && 34.0353399,66.2726012 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873296000000 && 34.0355522,66.2726215 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873297000000 && 34.0357779,66.2726256 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873298000000 && 34.0359565,66.2726378 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873299000000 && 34.036098,66.2726784 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873300000000 && 34.0363035,66.2727353 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873301000000 && 34.0366033,66.2727719 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873302000000 && 34.0368358,66.2728288 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873303000000 && 34.0370413,66.2729183 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873304000000 && 34.037112,66.2729549 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873305000000 && 34.0371895,66.2729305 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873306000000 && 34.0372468,66.2728898 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873307000000 && 34.0372636,66.2728044 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873308000000 && 34.0372299,66.2727353 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873309000000 && 34.0371592,66.2727313 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907620000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873310000000 && 34.0370851,66.2727475 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873311000000 && 34.0370143,66.2727109 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873312000000 && 34.0368931,66.272593 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873313000000 && 34.0367549,66.2723735 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873314000000 && 34.0366505,66.2722556 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873315000000 && 34.0365359,66.2721336 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873316000000 && 34.0364483,66.2720117 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873317000000 && 34.0364281,66.2719426 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873318000000 && 34.0364483,66.2718653 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873319000000 && 34.0364854,66.2718043 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873320000000 && 34.0364854,66.2717149 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873321000000 && 34.0364618,66.2716255 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873322000000 && 34.0363271,66.2714506 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873323000000 && 34.0362496,66.2713815 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873324000000 && 34.0361755,66.2713287 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873325000000 && 34.0360277,66.2712613 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873326000000 && 34.0356976,66.270993 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873327000000 && 34.0356302,66.2709767 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873328000000 && 34.035546,66.2709645 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873329000000 && 34.0354382,66.2709482 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873330000000 && 34.0353506,66.2709157 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873331000000 && 34.0351787,66.2707287 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873332000000 && 34.0350406,66.2706149 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873333000000 && 34.0348418,66.270436 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873334000000 && 34.0345858,66.2702368 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873335000000 && 34.0342556,66.2700091 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873336000000 && 34.0340737,66.2698912 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873337000000 && 34.0339356,66.2698343 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873338000000 && 34.0337031,66.2697083 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873339000000 && 34.033538,66.2695944 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873340000000 && 34.033164,66.2692977 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873341000000 && 34.0329619,66.2691228 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873342000000 && 34.0328911,66.2690334 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873343000000 && 34.0328777,66.2689399 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873344000000 && 34.032908,66.2689033 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873345000000 && 34.0329754,66.2689155 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873346000000 && 34.0330933,66.2689887 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873347000000 && 34.0335313,66.2692367 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873348000000 && 34.0336189,66.2692692 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873349000000 && 34.0336896,66.2692692 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873350000000 && 34.0337166,66.2692285 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873351000000 && 34.0336896,66.2691513 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873352000000 && 34.0335953,66.2689765 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873453000000 && 34.0334572,66.2687244 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873454000000 && 34.0333729,66.2685781 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873455000000 && 34.0333157,66.2684724 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873456000000 && 34.0332449,66.2683301 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873457000000 && 34.0331674,66.2682366 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873458000000 && 34.0330731,66.2681309 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873459000000 && 34.032945,66.268013 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873460000000 && 34.0328979,66.267952 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873461000000 && 34.0328676,66.2678585 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873462000000 && 34.0328507,66.267704 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873463000000 && 34.0328204,66.2675739 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873464000000 && 34.0328069,66.2674682 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873465000000 && 34.0328575,66.2673666 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873466000000 && 34.032945,66.2673137 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873467000000 && 34.0330428,66.2673137 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873468000000 && 34.0331337,66.2673422 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873469000000 && 34.0332045,66.2673869 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873470000000 && 34.0332752,66.2674845 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907621000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873471000000 && 34.03341,66.2675576 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873472000000 && 34.0335279,66.2675658 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873473000000 && 34.033629,66.2675536 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873474000000 && 34.033693,66.267517 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873475000000 && 34.0337772,66.2674723 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873476000000 && 34.033949,66.2674804 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873477000000 && 34.0340333,66.2675089 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873478000000 && 34.0341209,66.267578 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873479000000 && 34.0342253,66.2675902 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873480000000 && 34.0344342,66.2676024 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873481000000 && 34.0345049,66.2675861 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873482000000 && 34.0345622,66.2675454 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873483000000 && 34.0346801,66.2674194 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873484000000 && 34.0347812,66.2673381 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873485000000 && 34.0348722,66.2673096 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873486000000 && 34.0350002,66.2673503 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873487000000 && 34.0351922,66.2674357 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873488000000 && 34.0352731,66.267456 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873489000000 && 34.0353405,66.2674357 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873490000000 && 34.0353775,66.2673788 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873491000000 && 34.0353708,66.2672771 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873492000000 && 34.0352866,66.2671023 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873493000000 && 34.0351821,66.2669234 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873494000000 && 34.035081,66.2667689 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873495000000 && 34.0350305,66.2666063 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873496000000 && 34.0350069,66.266525 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873497000000 && 34.0350069,66.2664356 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873498000000 && 34.035044,66.2663095 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873499000000 && 34.0350709,66.2661998 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873500000000 && 34.0350911,66.2660859 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873501000000 && 34.0351046,66.265903 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873502000000 && 34.0351114,66.2657322 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873503000000 && 34.035108,66.2655615 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873504000000 && 34.0350911,66.2653013 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873505000000 && 34.0350541,66.265098 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873506000000 && 34.0350069,66.2649151 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873507000000 && 34.0349193,66.2646833 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873508000000 && 34.0348452,66.2644191 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873509000000 && 34.0348082,66.2642361 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873510000000 && 34.0347778,66.2640776 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873511000000 && 34.0347542,66.263976 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873512000000 && 34.0347037,66.2638987 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873513000000 && 34.03467,66.2638337 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873514000000 && 34.0346431,66.2637361 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873515000000 && 34.0346397,66.2636467 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873516000000 && 34.0346397,66.2635491 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873517000000 && 34.0346296,66.2634637 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873518000000 && 34.0346161,66.2634027 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873519000000 && 34.0345555,66.2633011 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873520000000 && 34.0344948,66.2631344 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873521000000 && 34.0344106,66.2629189 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873522000000 && 34.0343028,66.2626872 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873523000000 && 34.0342725,66.2625896 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873524000000 && 34.0342624,66.2624677 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873525000000 && 34.0342691,66.2623498 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873526000000 && 34.0342523,66.2622034 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873527000000 && 34.0342085,66.2620205 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873528000000 && 34.0341141,66.261679 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873529000000 && 34.0339861,66.2613659 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873530000000 && 34.0338783,66.2610285 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873531000000 && 34.0337806,66.2608171 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873532000000 && 34.0336391,66.2605691 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873533000000 && 34.0335447,66.2604065 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873534000000 && 34.0335043,66.2602845 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873535000000 && 34.0334673,66.2601666 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873536000000 && 34.0334908,66.2600325 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873537000000 && 34.033538,66.259882 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907622000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873538000000 && 34.0335751,66.2597357 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873539000000 && 34.0336222,66.2595161 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873540000000 && 34.0336458,66.2593251 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873541000000 && 34.0336593,66.2590567 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873542000000 && 34.0336458,66.2587518 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873543000000 && 34.0336323,66.2586177 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873544000000 && 34.0336492,66.2584185 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873545000000 && 34.0336863,66.2582558 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873546000000 && 34.0337435,66.2580648 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873547000000 && 34.0337671,66.257894 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873548000000 && 34.0337907,66.2577355 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873549000000 && 34.0338547,66.2575281 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873550000000 && 34.0339726,66.2571704 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873551000000 && 34.0340905,66.2567841 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873552000000 && 34.0341984,66.2564914 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873653000000 && 34.0343196,66.2562922 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873654000000 && 34.0343971,66.2561581 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873655000000 && 34.0345218,66.2560646 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873656000000 && 34.0346902,66.2560158 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873657000000 && 34.0348789,66.2560158 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873658000000 && 34.0350372,66.2560646 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873659000000 && 34.0352293,66.2560686 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873660000000 && 34.0355089,66.2561296 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873661000000 && 34.0356066,66.2561743 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873662000000 && 34.0356875,66.2561703 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873663000000 && 34.0357616,66.2561662 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873664000000 && 34.0358458,66.2561947 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873665000000 && 34.0359233,66.2562068 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873666000000 && 34.0360008,66.2561743 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873667000000 && 34.0360749,66.2561743 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873668000000 && 34.0362063,66.256219 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873669000000 && 34.0363276,66.2562597 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873670000000 && 34.0364286,66.2562475 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873671000000 && 34.0365095,66.2561987 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873672000000 && 34.0365735,66.2561418 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873673000000 && 34.0366544,66.2561052 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873674000000 && 34.0367352,66.2560442 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873675000000 && 34.0367858,66.2559711 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873676000000 && 34.0368329,66.2558775 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873677000000 && 34.0368632,66.255719 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873678000000 && 34.0368565,66.255593 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873679000000 && 34.03687,66.2555198 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873680000000 && 34.0369239,66.2554425 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873681000000 && 34.0369946,66.2553572 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873682000000 && 34.0371597,66.2552067 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873683000000 && 34.0373585,66.255097 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873684000000 && 34.0374292,66.2550645 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873685000000 && 34.0374932,66.2550157 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873686000000 && 34.0375303,66.2549506 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873687000000 && 34.0375977,66.2548856 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873688000000 && 34.0377021,66.254853 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873689000000 && 34.0378503,66.2548246 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873690000000 && 34.0379615,66.2548002 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873691000000 && 34.0380424,66.2548165 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873692000000 && 34.0381097,66.2548449 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873693000000 && 34.0381738,66.2548409 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873694000000 && 34.0382209,66.2547758 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873695000000 && 34.0382883,66.2547473 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873696000000 && 34.0383826,66.254727 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873697000000 && 34.0384702,66.2546864 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873698000000 && 34.0385376,66.2546254 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873699000000 && 34.0386252,66.2544912 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873700000000 && 34.0386926,66.2543408 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873701000000 && 34.0387465,66.2542636 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907623000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5767873702000000 && 34.0388172,66.2541619 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60829864 || synthetic_duplicate_osm_node -> YES || last_edit_time -> 1531907624000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n5768021156000000 && 34.0899312,66.4564028 && last_edit_user_name -> Qaderi || last_edit_changeset -> 60832604 || last_edit_time -> 1531912419000 || last_edit_user_id -> 8164544 || iso_country_code -> AFG || last_edit_version -> 1\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/statistics/addressAtlas.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-39285' action='modify' lat='41.30731119272' lon='9.12956725891' />\n  <node id='-39288' action='modify' lat='41.31604986514' lon='9.18162466422' />\n  <node id='-39292' action='modify' lat='41.30020090018' lon='9.22106209248' />\n  <node id='-39294' action='modify' lat='41.31486502905' lon='9.2741054335' />\n  <node id='-39302' action='modify' lat='41.3264162622' lon='9.14711691448' />\n  <node id='-39303' action='modify' lat='41.31604986514' lon='9.14987753446' />\n  <node id='-39304' action='modify' lat='41.31908590931' lon='9.17008921645' />\n  <node id='-39305' action='modify' lat='41.32945182345' lon='9.16732859647' />\n  <node id='-39308' action='modify' lat='41.32182563264' lon='9.19976588122' />\n  <node id='-39309' action='modify' lat='41.31560555413' lon='9.19523057697' />\n  <node id='-39310' action='modify' lat='41.31082483702' lon='9.20685335293' />\n  <node id='-39311' action='modify' lat='41.31704537174' lon='9.21138865718' />\n  <node id='-39363' action='modify' lat='41.3163460708' lon='9.23171019811' />\n  <node id='-39364' action='modify' lat='41.30834804561' lon='9.23604831522' />\n  <node id='-39365' action='modify' lat='41.31355785108' lon='9.25307275705' />\n  <node id='-39366' action='modify' lat='41.32155523709' lon='9.24873463994' />\n  <node id='-39431' action='modify' lat='41.30657057342' lon='9.26759825783'>\n    <tag k='addr:blocknumber' v='7' />\n    <tag k='addr:housenumber' v='8' />\n  </node>\n  <node id='-39442' action='modify' lat='41.30834804561' lon='9.26089389503' />\n  <node id='-39443' action='modify' lat='41.30108972914' lon='9.26523201214' />\n  <node id='-39444' action='modify' lat='41.30475305544' lon='9.2760931619' />\n  <node id='-39445' action='modify' lat='41.31201096414' lon='9.27175504479' />\n  <node id='-39539' action='modify' lat='41.3103082413' lon='9.18216688156'>\n    <tag k='addr:housenumber' v='1' />\n  </node>\n  <node id='-39540' action='modify' lat='41.30231330685' lon='9.13290006638'>\n    <tag k='addr:housenumber' v='7' />\n  </node>\n  <way id='-39289' action='modify'>\n    <nd ref='-39294' />\n    <nd ref='-39292' />\n    <nd ref='-39288' />\n    <nd ref='-39285' />\n    <tag k='highway' v='primary' />\n    <tag k='name' v='Main Street' />\n  </way>\n  <way id='-39306' action='modify'>\n    <nd ref='-39302' />\n    <nd ref='-39305' />\n    <nd ref='-39304' />\n    <nd ref='-39303' />\n    <nd ref='-39302' />\n    <tag k='addr:housename' v='Some House' />\n    <tag k='addr:housenumber' v='11' />\n    <tag k='addr:street' v='Main Street' />\n    <tag k='building' v='yes' />\n    <tag k='name' v='Some House' />\n  </way>\n  <way id='-39312' action='modify'>\n    <nd ref='-39308' />\n    <nd ref='-39311' />\n    <nd ref='-39310' />\n    <nd ref='-39309' />\n    <nd ref='-39308' />\n    <tag k='addr:blocknumber' v='7' />\n    <tag k='addr:housenumber' v='10' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='-39367' action='modify'>\n    <nd ref='-39363' />\n    <nd ref='-39366' />\n    <nd ref='-39365' />\n    <nd ref='-39364' />\n    <nd ref='-39363' />\n    <tag k='addr:housenumber' v='9' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='-39446' action='modify'>\n    <nd ref='-39442' />\n    <nd ref='-39445' />\n    <nd ref='-39444' />\n    <nd ref='-39443' />\n    <nd ref='-39442' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='-39541' action='modify'>\n    <nd ref='-39539' />\n    <nd ref='-39540' />\n    <tag k='addr:interpolation' v='odd' />\n  </way>\n  <relation id='-39412' action='modify'>\n    <member type='node' ref='-39431' role='house' />\n    <member type='way' ref='-39289' role='street' />\n    <member type='way' ref='-39367' role='house' />\n    <tag k='type' v='associatedStreet' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/statistics/ferryAtlas.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-53054' action='modify' lat='41.33786514198' lon='9.15603359849' />\n  <node id='-53055' action='modify' lat='41.34190434765' lon='9.16619551226' />\n  <node id='-53057' action='modify' lat='41.34111896616' lon='9.18487550081' />\n  <node id='-53059' action='modify' lat='41.33348905315' lon='9.19249693614' />\n  <node id='-53061' action='modify' lat='41.33079592928' lon='9.20519932835' />\n  <node id='-53094' action='modify' lat='41.315701154' lon='9.12995633447' />\n  <node id='-53095' action='modify' lat='41.31974173394' lon='9.14011824824' />\n  <node id='-53096' action='modify' lat='41.31895608523' lon='9.15879823679' />\n  <node id='-53097' action='modify' lat='41.3113235764' lon='9.16641967212' />\n  <node id='-53098' action='modify' lat='41.30862953637' lon='9.17912206434' />\n  <node id='-53100' action='modify' lat='41.31368077009' lon='9.18644461985' />\n  <node id='-53101' action='modify' lat='41.31772147528' lon='9.19660653362' />\n  <node id='-53102' action='modify' lat='41.31693580222' lon='9.21528652217' />\n  <node id='-53103' action='modify' lat='41.30930305681' lon='9.2229079575' />\n  <node id='-53104' action='modify' lat='41.3066089333' lon='9.23561034971' />\n  <way id='-53056' action='modify'>\n    <nd ref='-53054' />\n    <nd ref='-53055' />\n    <nd ref='-53057' />\n    <nd ref='-53059' />\n    <nd ref='-53061' />\n    <tag k='name' v='ferry1' />\n    <tag k='oneway' v='yes' />\n    <tag k='route' v='ferry' />\n  </way>\n  <way id='-53093' action='modify'>\n    <nd ref='-53094' />\n    <nd ref='-53095' />\n    <nd ref='-53096' />\n    <nd ref='-53097' />\n    <nd ref='-53098' />\n  </way>\n  <way id='-53099' action='modify'>\n    <nd ref='-53100' />\n    <nd ref='-53101' />\n    <nd ref='-53102' />\n    <nd ref='-53103' />\n    <nd ref='-53104' />\n  </way>\n  <relation id='-53117' action='modify'>\n    <member type='way' ref='-53099' role='' />\n    <member type='way' ref='-53093' role='' />\n    <tag k='route' v='ferry' />\n    <tag k='type' v='route' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/statistics/refsAtlas.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-76095' action='modify' lat='41.32546523825' lon='9.17466918531' />\n  <node id='-76096' action='modify' lat='41.32564296684' lon='9.17662446684'>\n    <tag k='highway' v='motorway_junction' />\n    <tag k='ref' v='55' />\n  </node>\n  <node id='-76098' action='modify' lat='41.32566029685' lon='9.17846109715'>\n    <tag k='highway' v='motorway_junction' />\n    <tag k='int_ref' v='something' />\n    <tag k='ref' v='56' />\n  </node>\n  <node id='-76100' action='modify' lat='41.32556465659' lon='9.17992374965' />\n  <node id='-76102' action='modify' lat='41.32506288673' lon='9.18085255422' />\n  <node id='-78105' action='modify' lat='41.3264366607' lon='9.17762469409' />\n  <node id='-78107' action='modify' lat='41.32668090044' lon='9.17918583405' />\n  <node id='-78109' action='modify' lat='41.32599092083' lon='9.17900695343' />\n  <node id='-78111' action='modify' lat='41.32627179836' lon='9.18067379557' />\n  <node id='-78206' action='modify' lat='41.32568497158' lon='9.17758868094'>\n    <tag k='barrier' v='toll_booth' />\n  </node>\n  <way id='-76097' action='modify'>\n    <nd ref='-76098' />\n    <nd ref='-76100' />\n    <nd ref='-76102' />\n    <tag k='highway' v='motorway' />\n    <tag k='int_ref' v='something' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-76130' action='modify'>\n    <nd ref='-76095' />\n    <nd ref='-76096' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n    <tag k='ref' v='110' />\n    <tag k='turn:lanes' v='2' />\n  </way>\n  <way id='-76133' action='modify'>\n    <nd ref='-76096' />\n    <nd ref='-78206' />\n    <nd ref='-76098' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n    <tag k='turn:lanes' v='1' />\n  </way>\n  <way id='-78106' action='modify'>\n    <nd ref='-76096' />\n    <nd ref='-78105' />\n    <nd ref='-78107' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-78110' action='modify'>\n    <nd ref='-76098' />\n    <nd ref='-78109' />\n    <nd ref='-78111' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-78052' action='modify'>\n    <member type='way' ref='-76130' role='' />\n    <member type='way' ref='-76133' role='' />\n    <member type='way' ref='-76097' role='' />\n    <tag k='int_ref' v='something' />\n    <tag k='ref' v='110' />\n    <tag k='type' v='route' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/statistics/waterAtlas.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-40923' action='modify' lat='41.33596669689' lon='9.14987753446' />\n  <node id='-40925' action='modify' lat='41.31775302928' lon='9.14041255168' />\n  <node id='-40927' action='modify' lat='41.29938612965' lon='9.15599033584' />\n  <node id='-40929' action='modify' lat='41.28620024612' lon='9.14711691448' />\n  <node id='-40931' action='modify' lat='41.33433803962' lon='9.18083591565' />\n  <node id='-40933' action='modify' lat='41.31819732565' lon='9.17215968143' />\n  <node id='-40935' action='modify' lat='41.30205261352' lon='9.18241341278' />\n  <node id='-40937' action='modify' lat='41.28723743465' lon='9.17097655858' />\n  <node id='-40939' action='modify' lat='41.3321170777' lon='9.19937150694' />\n  <node id='-40941' action='modify' lat='41.31790112841' lon='9.18951214987' />\n  <node id='-40943' action='modify' lat='41.30234888278' lon='9.20311806262' />\n  <node id='-40945' action='modify' lat='41.28768193898' lon='9.19049808558' />\n  <node id='-40947' action='modify' lat='41.3300441116' lon='9.22145646676' />\n  <node id='-40949' action='modify' lat='41.31849352155' lon='9.2125830454' />\n  <node id='-40951' action='modify' lat='41.31271745875' lon='9.21987896963' />\n  <node id='-40953' action='modify' lat='41.31464286989' lon='9.22855520385' />\n  <node id='-40955' action='modify' lat='41.32249203442' lon='9.23171019811' />\n  <node id='-40957' action='modify' lat='41.30531795966' lon='9.2131196435' />\n  <node id='-40959' action='modify' lat='41.30801213652' lon='9.21625788157' />\n  <node id='-40961' action='modify' lat='41.30599151431' lon='9.22298267745' />\n  <node id='-40963' action='modify' lat='41.30655280454' lon='9.23120187241' />\n  <node id='-40965' action='modify' lat='41.30419535312' lon='9.23583450957' />\n  <node id='-40967' action='modify' lat='41.30312647207' lon='9.22268903429' />\n  <node id='-40969' action='modify' lat='41.30368196667' lon='9.22875253888' />\n  <node id='-40971' action='modify' lat='41.30220063722' lon='9.23151315886' />\n  <node id='-40973' action='modify' lat='41.30446564732' lon='9.21745375567' />\n  <node id='-40975' action='modify' lat='41.30382755884' lon='9.21558506018' />\n  <node id='-40977' action='modify' lat='41.29285594366' lon='9.20654428753' />\n  <node id='-40979' action='modify' lat='41.29431558215' lon='9.21566012194' />\n  <node id='-40981' action='modify' lat='41.29105942067' lon='9.22432763663' />\n  <node id='-40983' action='modify' lat='41.29476469512' lon='9.23777722838' />\n  <node id='-40985' action='modify' lat='41.34730246891' lon='9.15617862117' />\n  <node id='-40987' action='modify' lat='41.34925794861' lon='9.1573111639' />\n  <node id='-40989' action='modify' lat='41.34925794861' lon='9.16523896297' />\n  <node id='-40991' action='modify' lat='41.3461971718' lon='9.16761730269' />\n  <node id='-40993' action='modify' lat='41.34390149477' lon='9.16773055696' />\n  <node id='-40995' action='modify' lat='41.34262608367' lon='9.16478594588' />\n  <node id='-40997' action='modify' lat='41.34288116789' lon='9.1593497408' />\n  <node id='-40999' action='modify' lat='41.34449667807' lon='9.15674489253' />\n  <node id='-41001' action='modify' lat='41.34669327866' lon='9.17065134265' />\n  <node id='-41003' action='modify' lat='41.34907387714' lon='9.17212364819' />\n  <node id='-41005' action='modify' lat='41.35000908844' lon='9.17744659899' />\n  <node id='-41007' action='modify' lat='41.34873379697' lon='9.1815237528' />\n  <node id='-41009' action='modify' lat='41.34507782297' lon='9.1827695498' />\n  <node id='-41011' action='modify' lat='41.34278210648' lon='9.18118398998' />\n  <node id='-41013' action='modify' lat='41.34167673263' lon='9.17393571655' />\n  <node id='-41015' action='modify' lat='41.34320724527' lon='9.17065134265' />\n  <node id='-41017' action='modify' lat='41.34716086057' lon='9.17365263749' />\n  <node id='-41019' action='modify' lat='41.34465265388' lon='9.17201045054' />\n  <node id='-41021' action='modify' lat='41.34427003759' lon='9.17433216313' />\n  <node id='-41023' action='modify' lat='41.35290979365' lon='9.16362753868' />\n  <node id='-41025' action='modify' lat='41.35458493073' lon='9.16620245934' />\n  <node id='-41027' action='modify' lat='41.35239435818' lon='9.16912070274' />\n  <node id='-41029' action='modify' lat='41.3548426403' lon='9.17332640648' />\n  <node id='-41031' action='modify' lat='41.35168562775' lon='9.17504302025' />\n  <node id='-41033' action='modify' lat='41.35020371192' lon='9.17032233238' />\n  <node id='-41035' action='modify' lat='41.3518144884' lon='9.16671744347' />\n  <node id='-41037' action='modify' lat='41.35059030191' lon='9.16414252281' />\n  <node id='-41039' action='modify' lat='41.34010917606' lon='9.18905981824' />\n  <node id='-41041' action='modify' lat='41.34369946975' lon='9.19996893156' />\n  <node id='-41043' action='modify' lat='41.34010917606' lon='9.20370492927' />\n  <node id='-41045' action='modify' lat='41.34257752423' lon='9.21072860496' />\n  <node id='-41047' action='modify' lat='41.3369675067' lon='9.21042972515' />\n  <node id='-41049' action='modify' lat='41.35256215977' lon='9.19817565266' />\n  <node id='-41051' action='modify' lat='41.34897235475' lon='9.21102748478' />\n  <node id='-41053' action='modify' lat='41.34134336184' lon='9.21715452102' />\n  <node id='-41055' action='modify' lat='41.33707971179' lon='9.23179963205' />\n  <node id='-41057' action='modify' lat='41.32675603472' lon='9.24046714673' />\n  <node id='-41059' action='modify' lat='41.3283271346' lon='9.21297020359'>\n    <tag k='seamark:type' v='harbour' />\n  </node>\n  <node id='-41061' action='modify' lat='41.34493358749' lon='9.13735360994' />\n  <node id='-41063' action='modify' lat='41.34919672336' lon='9.14363008609' />\n  <node id='-41065' action='modify' lat='41.34358727607' lon='9.14811328334' />\n  <node id='-41067' action='modify' lat='41.35144036697' lon='9.14482560536' />\n  <node id='-41069' action='modify' lat='41.34684081436' lon='9.14796384343' />\n  <node id='-41071' action='modify' lat='41.35166472707' lon='9.15199872096' />\n  <node id='-41073' action='modify' lat='41.3238381212' lon='9.21117699941' />\n  <node id='-41075' action='modify' lat='41.32731709006' lon='9.21580963657' />\n  <node id='-41077' action='modify' lat='41.33130084238' lon='9.21625795629' />\n  <node id='-41079' action='modify' lat='41.34721212447' lon='9.13338420745' />\n  <node id='-41081' action='modify' lat='41.35204635853' lon='9.13949867228' />\n  <node id='-41083' action='modify' lat='41.35434127328' lon='9.14639370709' />\n  <node id='-41085' action='modify' lat='41.35360886247' lon='9.15452464437' />\n  <node id='-41087' action='modify' lat='41.34862825041' lon='9.15400426438' />\n  <node id='-41089' action='modify' lat='41.3419868416' lon='9.15127226946' />\n  <node id='-41091' action='modify' lat='41.33998451929' lon='9.14457237714' />\n  <node id='-41093' action='modify' lat='41.34027754591' lon='9.13819772231' />\n  <node id='-41095' action='modify' lat='41.34311006862' lon='9.13260363747' />\n  <node id='-41097' action='modify' lat='41.35391613251' lon='9.21736498147' />\n  <node id='-41099' action='modify' lat='41.35517426565' lon='9.21820302682' />\n  <node id='-41101' action='modify' lat='41.35510025849' lon='9.2206185693' />\n  <node id='-41103' action='modify' lat='41.35406414944' lon='9.22155520822' />\n  <node id='-41105' action='modify' lat='41.35291700946' lon='9.22155520822' />\n  <node id='-41107' action='modify' lat='41.35199188185' lon='9.22017489824' />\n  <node id='-41109' action='modify' lat='41.35217690842' lon='9.21825232361' />\n  <node id='-41111' action='modify' lat='41.35021559998' lon='9.21832626879' />\n  <node id='-41113' action='modify' lat='41.35147380464' lon='9.21916431414' />\n  <node id='-41115' action='modify' lat='41.35139979328' lon='9.22157985662' />\n  <node id='-41117' action='modify' lat='41.35036362532' lon='9.22251649554' />\n  <node id='-41119' action='modify' lat='41.34921642013' lon='9.22251649554' />\n  <node id='-41121' action='modify' lat='41.34829123993' lon='9.22113618555' />\n  <node id='-41123' action='modify' lat='41.34847627702' lon='9.21921361092' />\n  <node id='-41125' action='modify' lat='41.35336469929' lon='9.21596203896' />\n  <node id='-41127' action='modify' lat='41.3516312877' lon='9.21560427772' />\n  <node id='-41129' action='modify' lat='41.35014198173' lon='9.21609213395' />\n  <node id='-41131' action='modify' lat='41.35624547841' lon='9.20688791296' />\n  <node id='-41133' action='modify' lat='41.35543984964' lon='9.20646510422' />\n  <node id='-41135' action='modify' lat='41.35390180337' lon='9.21160385658' />\n  <node id='-41137' action='modify' lat='41.35470745118' lon='9.21205918907' />\n  <node id='-41139' action='modify' lat='41.35556191524' lon='9.21241695031' />\n  <node id='-41141' action='modify' lat='41.35702668468' lon='9.2072456742' />\n  <node id='-41143' action='modify' lat='41.35617335406' lon='9.140314106' />\n  <node id='-41145' action='modify' lat='41.35532227972' lon='9.13991973172' />\n  <node id='-41147' action='modify' lat='41.35465621377' lon='9.14060988671' />\n  <node id='-41149' action='modify' lat='41.35476722524' lon='9.14317331955' />\n  <node id='-41151' action='modify' lat='41.35621035704' lon='9.14312402276' />\n  <node id='-41153' action='modify' lat='41.35665439115' lon='9.14445503597' />\n  <node id='-41155' action='modify' lat='41.35743144357' lon='9.14687057845' />\n  <node id='-41157' action='modify' lat='41.35691340966' lon='9.14849737236' />\n  <node id='-41159' action='modify' lat='41.35584032629' lon='9.14844807558' />\n  <node id='-41161' action='modify' lat='41.3552112692' lon='9.14682128166' />\n  <node id='-41163' action='modify' lat='41.3552112692' lon='9.14489870704' />\n  <node id='-41165' action='modify' lat='41.31732854172' lon='9.23622692009' />\n  <node id='-41167' action='modify' lat='41.31967148891' lon='9.2385058787' />\n  <node id='-41169' action='modify' lat='41.31631838282' lon='9.24181223667' />\n  <node id='-41171' action='modify' lat='41.31386307022' lon='9.24054199745' />\n  <node id='-41173' action='modify' lat='41.31238983827' lon='9.23701147961' />\n  <node id='-41175' action='modify' lat='41.31491535837' lon='9.23503140083' />\n  <node id='-41177' action='modify' lat='41.33477572311' lon='9.1607309998' />\n  <node id='-41179' action='modify' lat='41.33480220057' lon='9.16460989546' />\n  <node id='-41181' action='modify' lat='41.3330375813' lon='9.1646312599' />\n  <node id='-41183' action='modify' lat='41.33301110313' lon='9.16075236425' />\n  <node id='-41185' action='modify' lat='41.33204380437' lon='9.16077347015' />\n  <node id='-41187' action='modify' lat='41.33207028294' lon='9.16465236581' />\n  <node id='-41189' action='modify' lat='41.33030558967' lon='9.16467373026' />\n  <node id='-41191' action='modify' lat='41.33027911039' lon='9.1607948346' />\n  <node id='-41297' action='modify' lat='41.34973868638' lon='9.23145267945' />\n  <node id='-41298' action='modify' lat='41.35143944045' lon='9.23618987368' />\n  <node id='-41300' action='modify' lat='41.3486563652' lon='9.24000022556' />\n  <node id='-41302' action='modify' lat='41.34394032721' lon='9.24133899785' />\n  <node id='-41304' action='modify' lat='41.34285790964' lon='9.23660180362' />\n  <node id='-41306' action='modify' lat='41.34548660681' lon='9.23207057435' />\n  <node id='-41310' action='modify' lat='41.34796057779' lon='9.2367047861' />\n  <node id='-41311' action='modify' lat='41.34811519786' lon='9.2354689963' />\n  <node id='-41313' action='modify' lat='41.34726478296' lon='9.2348511014' />\n  <node id='-41315' action='modify' lat='41.34610510837' lon='9.23526303133' />\n  <node id='-41317' action='modify' lat='41.34595048353' lon='9.2367047861' />\n  <node id='-41319' action='modify' lat='41.34664629241' lon='9.23763162845' />\n  <way id='-41193' action='modify'>\n    <nd ref='-40923' />\n    <nd ref='-40925' />\n    <nd ref='-40927' />\n    <nd ref='-40929' />\n    <tag k='waterway' v='stream' />\n  </way>\n  <way id='-41195' action='modify'>\n    <nd ref='-40931' />\n    <nd ref='-40933' />\n    <nd ref='-40935' />\n    <nd ref='-40937' />\n    <tag k='natural' v='stream' />\n  </way>\n  <way id='-41197' action='modify'>\n    <nd ref='-40939' />\n    <nd ref='-40941' />\n    <nd ref='-40943' />\n    <nd ref='-40945' />\n    <tag k='stream' v='something' />\n  </way>\n  <way id='-41199' action='modify'>\n    <nd ref='-40947' />\n    <nd ref='-40949' />\n    <nd ref='-40951' />\n    <nd ref='-40953' />\n    <nd ref='-40955' />\n    <nd ref='-40947' />\n    <tag k='salt' v='yes' />\n    <tag k='water' v='cove' />\n    <tag k='waterway' v='riverbank' />\n  </way>\n  <way id='-41201' action='modify'>\n    <nd ref='-40957' />\n    <nd ref='-40959' />\n    <nd ref='-40961' />\n    <nd ref='-40963' />\n    <nd ref='-40965' />\n  </way>\n  <way id='-41203' action='modify'>\n    <nd ref='-40957' />\n    <nd ref='-40975' />\n    <nd ref='-40973' />\n    <nd ref='-40967' />\n    <nd ref='-40969' />\n    <nd ref='-40971' />\n    <nd ref='-40965' />\n  </way>\n  <way id='-41205' action='modify'>\n    <nd ref='-40977' />\n    <nd ref='-40979' />\n    <nd ref='-40981' />\n    <nd ref='-40983' />\n    <tag k='waterway' v='derelict_canal' />\n  </way>\n  <way id='-41207' action='modify'>\n    <nd ref='-40985' />\n    <nd ref='-40987' />\n    <nd ref='-40989' />\n    <nd ref='-40991' />\n    <nd ref='-40993' />\n    <nd ref='-40995' />\n    <nd ref='-40997' />\n    <nd ref='-40999' />\n    <nd ref='-40985' />\n  </way>\n  <way id='-41209' action='modify'>\n    <nd ref='-41001' />\n    <nd ref='-41003' />\n    <nd ref='-41005' />\n    <nd ref='-41007' />\n    <nd ref='-41009' />\n    <nd ref='-41011' />\n    <nd ref='-41013' />\n    <nd ref='-41015' />\n    <nd ref='-41001' />\n  </way>\n  <way id='-41211' action='modify'>\n    <nd ref='-41017' />\n    <nd ref='-41019' />\n    <nd ref='-41021' />\n    <nd ref='-41017' />\n  </way>\n  <way id='-41213' action='modify'>\n    <nd ref='-41023' />\n    <nd ref='-41025' />\n    <nd ref='-41027' />\n    <nd ref='-41029' />\n    <nd ref='-41031' />\n    <nd ref='-41033' />\n    <nd ref='-41035' />\n    <nd ref='-41037' />\n    <nd ref='-41023' />\n    <tag k='water' v='oxbow' />\n  </way>\n  <way id='-41215' action='modify'>\n    <nd ref='-41039' />\n    <nd ref='-41041' />\n    <nd ref='-41043' />\n    <nd ref='-41045' />\n    <nd ref='-41047' />\n    <nd ref='-41039' />\n    <tag k='wetland' v='mangrove' />\n  </way>\n  <way id='-41217' action='modify'>\n    <nd ref='-41049' />\n    <nd ref='-41051' />\n    <nd ref='-41053' />\n    <nd ref='-41055' />\n    <nd ref='-41057' />\n    <tag k='natural' v='coastline' />\n  </way>\n  <way id='-41219' action='modify'>\n    <nd ref='-41061' />\n    <nd ref='-41063' />\n    <nd ref='-41065' />\n    <nd ref='-41061' />\n    <tag k='natural' v='bay' />\n  </way>\n  <way id='-41221' action='modify'>\n    <nd ref='-41067' />\n    <nd ref='-41069' />\n    <nd ref='-41071' />\n    <nd ref='-41067' />\n    <tag k='natural' v='bay' />\n  </way>\n  <way id='-41223' action='modify'>\n    <nd ref='-41073' />\n    <nd ref='-41075' />\n    <nd ref='-41077' />\n    <tag k='harbour' v='yes' />\n  </way>\n  <way id='-41225' action='modify'>\n    <nd ref='-41079' />\n    <nd ref='-41081' />\n    <nd ref='-41083' />\n    <nd ref='-41085' />\n    <nd ref='-41087' />\n    <nd ref='-41089' />\n    <nd ref='-41091' />\n    <nd ref='-41093' />\n    <nd ref='-41095' />\n    <nd ref='-41079' />\n  </way>\n  <way id='-41227' action='modify'>\n    <nd ref='-41097' />\n    <nd ref='-41099' />\n    <nd ref='-41101' />\n    <nd ref='-41103' />\n    <nd ref='-41105' />\n    <nd ref='-41107' />\n    <nd ref='-41109' />\n    <nd ref='-41097' />\n    <tag k='place' v='island' />\n  </way>\n  <way id='-41229' action='modify'>\n    <nd ref='-41111' />\n    <nd ref='-41113' />\n    <nd ref='-41115' />\n    <nd ref='-41117' />\n    <nd ref='-41119' />\n    <nd ref='-41121' />\n    <nd ref='-41123' />\n    <nd ref='-41111' />\n    <tag k='natural' v='not_coastline' />\n    <tag k='place' v='island' />\n  </way>\n  <way id='-41231' action='modify'>\n    <nd ref='-41097' />\n    <nd ref='-41125' />\n    <nd ref='-41127' />\n    <nd ref='-41129' />\n    <nd ref='-41111' />\n    <tag k='man_made' v='pier' />\n  </way>\n  <way id='-41233' action='modify'>\n    <nd ref='-41131' />\n    <nd ref='-41133' />\n    <nd ref='-41135' />\n    <nd ref='-41137' />\n  </way>\n  <way id='-41235' action='modify'>\n    <nd ref='-41137' />\n    <nd ref='-41139' />\n    <nd ref='-41141' />\n    <nd ref='-41131' />\n  </way>\n  <way id='-41237' action='modify'>\n    <nd ref='-41143' />\n    <nd ref='-41145' />\n    <nd ref='-41147' />\n    <nd ref='-41149' />\n    <nd ref='-41151' />\n    <nd ref='-41143' />\n  </way>\n  <way id='-41239' action='modify'>\n    <nd ref='-41153' />\n    <nd ref='-41155' />\n    <nd ref='-41157' />\n    <nd ref='-41159' />\n    <nd ref='-41161' />\n    <nd ref='-41163' />\n    <nd ref='-41153' />\n  </way>\n  <way id='-41241' action='modify'>\n    <nd ref='-41165' />\n    <nd ref='-41167' />\n    <nd ref='-41169' />\n    <nd ref='-41171' />\n    <nd ref='-41173' />\n    <nd ref='-41175' />\n    <nd ref='-41165' />\n    <tag k='water' v='lagoon' />\n  </way>\n  <way id='-41243' action='modify'>\n    <nd ref='-41177' />\n    <nd ref='-41179' />\n    <nd ref='-41181' />\n    <nd ref='-41183' />\n    <nd ref='-41177' />\n    <tag k='leisure' v='swimming_pool' />\n  </way>\n  <way id='-41245' action='modify'>\n    <nd ref='-41185' />\n    <nd ref='-41187' />\n    <nd ref='-41189' />\n    <nd ref='-41191' />\n    <nd ref='-41185' />\n    <tag k='covered' v='yes' />\n    <tag k='leisure' v='swimming_pool' />\n  </way>\n  <way id='-41299' action='modify'>\n    <nd ref='-41297' />\n    <nd ref='-41298' />\n    <nd ref='-41300' />\n    <nd ref='-41302' />\n    <nd ref='-41304' />\n    <nd ref='-41306' />\n    <nd ref='-41297' />\n  </way>\n  <way id='-41312' action='modify'>\n    <nd ref='-41310' />\n    <nd ref='-41311' />\n    <nd ref='-41313' />\n    <nd ref='-41315' />\n    <nd ref='-41317' />\n    <nd ref='-41319' />\n    <nd ref='-41310' />\n  </way>\n  <relation id='-41247' action='modify'>\n    <member type='way' ref='-41201' role='outer' />\n    <member type='way' ref='-41203' role='outer' />\n    <tag k='natural' v='water' />\n    <tag k='seamark:type' v='dam' />\n    <tag k='type' v='multipolygon' />\n    <tag k='water' v='moat' />\n  </relation>\n  <relation id='-41249' action='modify'>\n    <member type='way' ref='-41211' role='inner' />\n    <member type='way' ref='-41209' role='outer' />\n    <member type='way' ref='-41207' role='outer' />\n    <tag k='name' v='Some Spring' />\n    <tag k='natural' v='spring' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n  <relation id='-41251' action='modify'>\n    <member type='way' ref='-41221' role='inner' />\n    <member type='way' ref='-41219' role='inner' />\n    <member type='way' ref='-41225' role='outer' />\n    <tag k='natural' v='beach' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n  <relation id='-41253' action='modify'>\n    <member type='way' ref='-41235' role='outer' />\n    <member type='way' ref='-41233' role='outer' />\n    <tag k='man_made' v='pier' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n  <relation id='-41255' action='modify'>\n    <member type='way' ref='-41239' role='some_role' />\n    <member type='way' ref='-41237' role='some_role' />\n    <tag k='natural' v='beach' />\n  </relation>\n  <relation id='-41326' action='modify'>\n    <member type='way' ref='-41299' role='inner' />\n    <member type='way' ref='-41312' role='outer' />\n    <tag k='name' v='broken spring' />\n    <tag k='natural' v='spring' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/walker/OsmWayWalker-Way169884263.atlas.txt",
    "content": "# Nodes\n1810513641000000 && -2.8789555,32.2288203 && last_edit_user_name -> GeoCrazy || last_edit_time -> 1476400892000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA\n1810513586000000 && -2.8767905,32.2269775 && last_edit_user_name -> GeoCrazy || last_edit_time -> 1476400892000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA\n47878101000000 && -2.8767569,32.226709 && last_edit_user_name -> GeoCrazy || last_edit_time -> 1476400895000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA\n3868871079000000 && -2.8779374,32.2249132 && last_edit_user_name -> macabe5387 || last_edit_time -> 1448886703000 || last_edit_user_id -> 2698880 || iso_country_code -> TZA\n1810513557000000 && -2.8765678,32.2270026 && last_edit_user_name -> GeoCrazy || last_edit_time -> 1476400892000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA\n1810513555000000 && -2.876528,32.2267477 && last_edit_user_name -> GeoCrazy || last_edit_time -> 1476400892000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA\n4453107562000000 && -2.8754807,32.225868 && last_edit_user_name -> tonny john || last_edit_time -> 1476783203000 || last_edit_user_id -> 2780523 || iso_country_code -> TZA\n1810513522000000 && -2.8753901,32.2287045 && last_edit_user_name -> FischersFritz || last_edit_time -> 1341264355000 || last_edit_user_id -> 74900 || iso_country_code -> TZA\n# Edges\n360672007000018 && -2.8789555,32.2288203:-2.8767905,32.2269775 && last_edit_user_name -> GeoCrazy || ref -> T4 || surface -> paved || last_edit_time -> 1476400886000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || highway -> primary\n-360672007000018 && -2.8767905,32.2269775:-2.8789555,32.2288203 && last_edit_user_name -> GeoCrazy || ref -> T4 || surface -> paved || last_edit_time -> 1476400886000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || highway -> primary\n169884260000001 && -2.8767569,32.226709:-2.8779374,32.2249132 && last_edit_user_name -> macabe5387 || surface -> paved || last_edit_time -> 1448887762000 || last_edit_user_id -> 2698880 || iso_country_code -> TZA || highway -> residential\n-169884260000001 && -2.8779374,32.2249132:-2.8767569,32.226709 && last_edit_user_name -> macabe5387 || surface -> paved || last_edit_time -> 1448887762000 || last_edit_user_id -> 2698880 || iso_country_code -> TZA || highway -> residential\n169884263000004 && -2.8767905,32.2269775:-2.8768318,32.2269053:-2.8768342,32.2268178:-2.8767951,32.2267415:-2.8767569,32.226709 && junction -> roundabout || last_edit_user_name -> GeoCrazy || ref -> T4 || surface -> paved || last_edit_time -> 1476400887000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || highway -> primary\n169884263000003 && -2.8765678,32.2270026:-2.8766553,32.2270314:-2.8767418,32.2270134:-2.8767905,32.2269775 && junction -> roundabout || last_edit_user_name -> GeoCrazy || ref -> T4 || surface -> paved || last_edit_time -> 1476400887000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || highway -> primary\n169884263000001 && -2.8767569,32.226709:-2.8767118,32.2266889:-2.8766503,32.2266828:-2.876587,32.2267003:-2.876528,32.2267477 && junction -> roundabout || last_edit_user_name -> GeoCrazy || ref -> T4 || surface -> paved || last_edit_time -> 1476400887000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || highway -> primary\n169884263000002 && -2.876528,32.2267477:-2.8764993,32.2267999:-2.8764916,32.2268825:-2.8765158,32.2269488:-2.8765678,32.2270026 && junction -> roundabout || last_edit_user_name -> GeoCrazy || ref -> T4 || surface -> paved || last_edit_time -> 1476400887000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || highway -> primary\n448290129000001 && -2.876528,32.2267477:-2.8757683,32.2261481:-2.8754807,32.225868 && last_edit_user_name -> tonny john || surface -> unpaved || last_edit_time -> 1476783208000 || last_edit_user_id -> 2780523 || iso_country_code -> TZA || source -> msd.or.tz & deliver.jsi.com || highway -> residential || source:date -> 2014..2015\n-448290129000001 && -2.8754807,32.225868:-2.8757683,32.2261481:-2.876528,32.2267477 && last_edit_user_name -> tonny john || surface -> unpaved || last_edit_time -> 1476783208000 || last_edit_user_id -> 2780523 || iso_country_code -> TZA || source -> msd.or.tz & deliver.jsi.com || highway -> residential || source:date -> 2014..2015\n5885686000021 && -2.8753901,32.2287045:-2.8765678,32.2270026 && last_edit_user_name -> johanespeter9 || ref -> T4 || surface -> paved || last_edit_time -> 1476798053000 || last_edit_user_id -> 2780502 || iso_country_code -> TZA || highway -> primary\n-5885686000021 && -2.8765678,32.2270026:-2.8753901,32.2287045 && last_edit_user_name -> johanespeter9 || ref -> T4 || surface -> paved || last_edit_time -> 1476798053000 || last_edit_user_id -> 2780502 || iso_country_code -> TZA || highway -> primary\n# Areas\n328683338001000 && -1.0184815,32.1464921:-1.0026429,32.1465008:-1.0026429,32.2109555:-1.0026429,32.2507214:-1.0026429,32.3075476:-1.0026428,32.4041396:-1.0026428,32.5007268:-1.0026428,32.5007317:-1.0027297,32.5359601:-1.0031859,32.720835:-1.0031621,32.7221422:-1.0028203,32.7409585:-1.0026427,32.7507322:-1.0026427,32.7759738:-1.0026427,32.8012202:-1.0026427,32.8264666:-1.0026427,32.8427599:-1.0149602,32.8427897:-1.0181418,32.9812107:-1.9806629,32.9806802:-1.9809412,32.9703384:-1.9791921,32.9703782:-1.9811002,32.821337:-1.9840021,32.8214961:-1.9850493,32.7121538:-1.9829822,32.7119947:-1.9828232,32.5649821:-2.1056683,32.5648566:-2.1105972,32.4369365:-1.9795786,32.437732:-1.9797376,32.2857872:-1.9819637,32.2857872:-1.9821228,32.1289101:-1.9880061,32.1290692:-1.9903912,31.9793518:-2.9725644,31.97967:-2.97463,31.8740246:-2.9693866,31.8740246:-2.9712933,31.7220797:-4.0457612,31.7236707:-4.0444916,31.8627281:-4.3631136,31.8628872:-4.3588302,31.7200113:-4.229524,31.7212842:-4.2204797,31.5529515:-4.2303174,31.5532697:-4.2227011,31.41819:-4.9027753,31.416758:-4.9099088,31.5728396:-4.9037265,31.5729987:-4.9094332,31.711897:-4.9049946,31.7123743:-4.907848,31.8641601:-5.0444792,31.864001:-5.040834,31.9984443:-5.2427174,31.9982852:-5.240024,31.8552502:-5.2435096,31.8554093:-5.2443018,31.7790391:-4.9709365,31.7782436:-4.9721405,31.6395908:-4.971031,31.639551:-4.9674685,31.4879976:-5.481946,31.4867607:-5.4860638,31.2571728:-5.4863806,31.1481861:-5.2457601,31.1497772:-5.2415106,31.0153857:-4.9049009,31.014398:-4.9008545,31.1385326:-4.9038528,31.1385656:-4.9019827,31.2942475:-4.2333321,31.2926565:-4.2296771,31.155493:-4.2271384,31.1553339:-4.2306292,31.0146855:-3.9722705,31.0143673:-3.9735403,30.881674:-3.9689373,30.8815149:-3.9667152,30.832988:-3.2610163,30.8328651:-3.2606962,30.8331615:-3.2605818,30.8332179:-3.2603195,30.8333471:-3.2600809,30.8334646:-3.2599428,30.8335327:-3.25934,30.8338297:-3.2592236,30.8340211:-3.259113,30.8342029:-3.2589861,30.8346015:-3.2589559,30.8349046:-3.2589527,30.8349373:-3.258934,30.8351251:-3.2588432,30.8355851:-3.2586739,30.8364434:-3.2586714,30.836456:-3.2585505,30.8370689:-3.2584338,30.8373429:-3.2582217,30.8378408:-3.2578172,30.8387371:-3.2577796,30.8388198:-3.2577324,30.8389237:-3.2577145,30.8389632:-3.2573874,30.8396832:-3.2571915,30.8399221:-3.2569593,30.8402054:-3.2565571,30.8405031:-3.2561549,30.8407759:-3.2554978,30.8410503:-3.2553264,30.8411219:-3.2548691,30.8413602:-3.2545028,30.841551:-3.2543777,30.8416964:-3.254012,30.8419274:-3.2535499,30.8422193:-3.2534351,30.8419705:-3.2532885,30.8416528:-3.2532885,30.8392688:-3.2532788,30.8387367:-3.2532686,30.8381773:-3.2532384,30.8365251:-3.2530563,30.8360567:-3.253015,30.8359506:-3.2527408,30.8355256:-3.2523672,30.8349007:-3.2520945,30.8341015:-3.2518713,30.8334772:-3.2516888,30.8328634:-3.2101871,30.8328562:-3.2097579,30.8335933:-3.2096225,30.8339326:-3.2094796,30.8342905:-3.20932,30.8346511:-3.2092721,30.8347594:-3.2090498,30.8352616:-3.2088634,30.8357215:-3.2087908,30.8359005:-3.2085029,30.8366108:-3.2076277,30.8387697:-3.2071521,30.839943:-3.2067715,30.8411386:-3.2067322,30.8415874:-3.206693,30.8420361:-3.2066951,30.8420678:-3.2067404,30.8427595:-3.2069628,30.8435833:-3.2071354,30.8443571:-3.2072334,30.844956:-3.2072562,30.8455546:-3.2072291,30.8461031:-3.2071556,30.846391:-3.2071273,30.8465018:-3.2070263,30.8467009:-3.2070112,30.8467094:-3.2069756,30.8467295:-3.206906,30.8467687:-3.2067979,30.8467805:-3.2066892,30.8467583:-3.2066722,30.8467486:-3.2066152,30.8467163:-3.2064255,30.8464742:-3.2060512,30.8460239:-3.2059577,30.8458891:-3.2056523,30.8454488:-3.205403,30.8451727:-3.205178,30.8449234:-3.204728,30.8446224:-3.203228,30.8435444:-3.2022103,30.842935:-3.2019699,30.8427847:-3.2018322,30.8426986:-3.2002517,30.8415696:-3.1988029,30.8404355:-3.1983523,30.8402593:-3.1978515,30.8401327:-3.1972249,30.8401055:-3.1966989,30.8400038:-3.196173,30.8398522:-3.1960019,30.8397923:-3.1955973,30.8396505:-3.1952219,30.8394745:-3.194697,30.8390736:-3.1944003,30.8388505:-3.1940971,30.8386224:-3.1937404,30.8384879:-3.1934963,30.8383958:-3.1933511,30.8383712:-3.1930454,30.8383193:-3.1925691,30.8383175:-3.1923863,30.8383289:-3.1921931,30.838341:-3.1915342,30.8384437:-3.1901614,30.8386577:-3.1898111,30.8385068:-3.1893605,30.8383305:-3.1890785,30.8382999:-3.1888844,30.8382789:-3.1886241,30.8382931:-3.1884582,30.8383022:-3.1882826,30.8383265:-3.1878304,30.8385992:-3.1873526,30.8389964:-3.1872517,30.839085:-3.1871263,30.8391951:-3.1869913,30.8393135:-3.1868999,30.8393937:-3.1866487,30.8395424:-3.1862471,30.8396906:-3.1858711,30.8396892:-3.1858164,30.8396748:-3.1852951,30.8395374:-3.1846942,30.8393606:-3.1843014,30.8393033:-3.184168,30.8392838:-3.1835413,30.8393065:-3.1832973,30.8394196:-3.1831144,30.8395044:-3.1816316,30.8401954:-3.1812562,30.8403703:-3.1799498,30.8410071:-3.1789207,30.8415088:-3.1788942,30.8415235:-3.1786638,30.8416513:-3.1785188,30.8417318:-3.177843,30.8420772:-3.1771626,30.842425:-3.1768358,30.8426732:-3.1765091,30.8428964:-3.1763407,30.8430002:-3.1762653,30.8430467:-3.1762061,30.8430832:-3.1761071,30.8431443:-3.175781,30.843193:-3.17538,30.8431915:-3.175278,30.8431573:-3.1751546,30.8431159:-3.1745798,30.8426648:-3.1743658,30.8425501:-3.1743157,30.8425233:-3.1742766,30.8425023:-3.1737946,30.8424629:-3.1731601,30.8424276:-3.1730041,30.8424431:-3.1729438,30.8424491:-3.1726657,30.8424767:-3.1713332,30.8428729:-3.1692218,30.8438375:-3.1681704,30.8443178:-3.1678848,30.8444483:-3.167804,30.8444852:-3.167516,30.8446156:-3.1659261,30.8453357:-3.1654948,30.845531:-3.1645798,30.8456617:-3.1643246,30.8455867:-3.1638391,30.8454439:-3.1633433,30.8451546:-3.1628294,30.8448548:-3.1624777,30.8446496:-3.1621007,30.8444297:-3.1617478,30.8442238:-3.1610751,30.8436726:-3.1600092,30.8428025:-3.1591046,30.842064:-3.1586543,30.8416964:-3.1573213,30.8413143:-3.156377,30.8410436:-3.1554356,30.8407738:-3.1548254,30.8406314:-3.1547845,30.8406218:-3.1541587,30.8403701:-3.1540462,30.8403056:-3.1538164,30.8401738:-3.1536334,30.8400689:-3.1533485,30.8398903:-3.1532281,30.8398143:-3.1531495,30.8397649:-3.1511576,30.8385137:-3.148806,30.8372332:-3.1484562,30.8369327:-3.1479819,30.8363824:-3.1477791,30.8360824:-3.1475324,30.8359194:-3.147365,30.8358249:-3.1473323,30.8358064:-3.1471722,30.8357119:-3.1469071,30.8355555:-3.1442789,30.8345234:-3.1441397,30.834442:-3.1439787,30.8343478:-3.1439287,30.8343125:-3.1437817,30.8342087:-3.143735,30.8341581:-3.1436788,30.8340973:-3.1436238,30.8339591:-3.1435795,30.8338476:-3.1435552,30.833623:-3.1435847,30.8334739:-3.1436021,30.8333859:-3.1438336,30.8329009:-3.1438668,30.8328447:-3.1157978,30.8328398:-3.1157818,30.8328519:-3.1155742,30.8330096:-3.1155573,30.8330224:-3.1153738,30.8331532:-3.1152997,30.8332061:-3.1151049,30.8333449:-3.114853,30.8334608:-3.1147283,30.8335181:-3.1145643,30.833562:-3.1144523,30.8335919:-3.1143534,30.8335731:-3.1140515,30.8335157:-3.1139898,30.8334627:-3.113962,30.8334388:-3.1137016,30.8332152:-3.1134523,30.8328402:-3.1134513,30.8328394:-3.1085853,30.8328385:-3.1084508,30.8328809:-3.1080108,30.8329001:-3.1078498,30.8328782:-3.1076967,30.8328573:-3.1076571,30.8328383:-3.1014641,30.8328373:-3.1011326,30.8330038:-3.1000685,30.8335384:-3.0999704,30.8335813:-3.0995507,30.8337651:-3.0994178,30.8338233:-3.0993131,30.8338691:-3.0991752,30.8339528:-3.0990402,30.8340348:-3.0988616,30.8341696:-3.0987321,30.8342675:-3.0986831,30.8343045:-3.0986543,30.8343368:-3.0983675,30.8346576:-3.098178,30.8349278:-3.0979255,30.8352395:-3.0977268,30.8355247:-3.0976938,30.8355721:-3.0973787,30.8358002:-3.0972516,30.8358629:-3.097186,30.8358952:-3.0971545,30.8359108:-3.0971269,30.8359244:-3.09684,30.8359853:-3.0966447,30.8360268:-3.096167,30.8360408:-3.0959162,30.8360481:-3.0947381,30.8360825:-3.094361,30.8360603:-3.0938588,30.8359126:-3.0935245,30.8356613:-3.0933198,30.8355097:-3.093044,30.8353053:-3.0928808,30.8351768:-3.0927515,30.835075:-3.0927258,30.8350566:-3.0925728,30.8349471:-3.0923127,30.8347608:-3.0922976,30.8347504:-3.0920361,30.8345702:-3.0919966,30.834543:-3.0919784,30.8345304:-3.0918013,30.8344036:-3.0916858,30.8343209:-3.0911628,30.8341107:-3.0910215,30.8340612:-3.0906816,30.8339422:-3.0902528,30.8338396:-3.0898862,30.8337518:-3.0896681,30.8336792:-3.0895599,30.8336432:-3.0891674,30.8335126:-3.0889337,30.8334349:-3.0888766,30.8334159:-3.0886133,30.8333283:-3.0882555,30.8332092:-3.0874799,30.8329512:-3.087426,30.832929:-3.0874065,30.832921:-3.0872933,30.8328743:-3.0871974,30.8328348:-3.0534311,30.8328289:-3.0532722,30.8388749:-3.0419918,30.8385567:-3.0392908,30.9685451:-2.0484228,30.966795:-2.0488998,30.9908198:-2.0506488,30.9908198:-2.0411086,31.1518336:-2.0118517,31.1518336:-2.0242541,31.2622522:-2.0452427,31.2632068:-2.0339534,31.3968546:-3.0434104,31.3948257:-3.0373729,31.4444663:-3.1371456,31.4444663:-3.1388931,31.4293514:-3.2758264,31.4293514:-3.2747145,31.4454209:-4.0433638,31.4481257:-4.0468553,31.6008661:-2.9706318,31.600077:-2.9698535,31.5626674:-2.0466163,31.5665642:-2.0349332,31.754781:-2.017019,31.7543913:-2.0174085,31.8297949:-1.0014372,31.8305621:-1.0009441,31.8474966:-1.0004269,31.8652607:-0.999889,31.8837329:-0.9993791,31.9012432:-0.9988461,31.9195492:-0.99864,31.9266269:-0.9984414,31.9334451:-0.9982349,31.9405362:-0.9980527,31.9467945:-0.9978547,31.9535952:-0.9985163,31.9631884:-0.999219,31.9733781:-1.0003263,31.9825714:-1.0014773,31.9921268:-1.0021778,31.997943:-1.0178461,31.997943:-1.0172616,32.0059315:-1.0425869,32.0057367:-1.0451194,32.0281434:-1.0178461,32.0285331 && last_edit_user_name -> Johnwhelan || hires -> no || last_edit_time -> 1465090309000 || last_edit_user_id -> 186592 || iso_country_code -> TZA || hires:imagery -> Bing || hires:checkdate -> 2015-06-14\n# Lines\n# Points\n# Relations\n5150140000000 && -360672007000018 ->  -> E || -5885686000021 ->  -> E || 5885686000021 ->  -> E || 169884263000001 ->  -> E || 169884263000002 ->  -> E || 169884263000003 ->  -> E || 169884263000004 ->  -> E || 360672007000018 ->  -> E && last_edit_user_name -> Peter14 || ref -> T4 || route -> road || last_edit_time -> 1477168319000 || last_edit_user_id -> 13832 || iso_country_code -> TZA || type -> route || operator -> United Republic of Tanzania || network -> TANROADS\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/atlas/walker/OsmWayWalker-Way30647513.atlas.txt",
    "content": "# Nodes\n3987651430000000 && -6.8452479,39.2677181 && last_edit_user_name -> kombe1207 || last_edit_time -> 1454580659000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA\n338938343000000 && -6.8444837,39.2673575 && last_edit_user_name -> kombe1207 || last_edit_time -> 1454580675000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA\n338938468000000 && -6.8446033,39.2671123 && last_edit_user_name -> kombe1207 || last_edit_time -> 1454580675000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA\n338938371000000 && -6.8439303,39.2670632 && last_edit_user_name -> kombe1207 || last_edit_time -> 1454580675000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA\n338938370000000 && -6.8448148,39.2653526 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902590000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n338938366000000 && -6.8433662,39.2667784 && last_edit_user_name -> kombe1207 || last_edit_time -> 1454580675000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA\n338938365000000 && -6.8442739,39.2650222 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476903245000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n4455524732000000 && -6.8428459,39.2665016 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n4455524734000000 && -6.8432236,39.2657506 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n338938344000000 && -6.8418225,39.2659523 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902590000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n338937761000000 && -6.8416131,39.2663468 && last_edit_user_name -> kombe1207 || last_edit_time -> 1454580675000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA\n338937669000000 && -6.8451463,39.25939 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476906164000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n338937935000000 && -6.8473713,39.266965 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902590000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n338937998000000 && -6.8457843,39.268217 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902590000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n3698468103000000 && -6.8449839,39.2679666 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476485292000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA\n# Edges\n30647513000001 && -6.8452479,39.2677181:-6.8444837,39.2673575 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647513000001 && -6.8444837,39.2673575:-6.8452479,39.2677181 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n30647526000002 && -6.8446033,39.2671123:-6.8444837,39.2673575 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902589000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647526000002 && -6.8444837,39.2673575:-6.8446033,39.2671123 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902589000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n30647513000002 && -6.8444837,39.2673575:-6.8439303,39.2670632 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647513000002 && -6.8439303,39.2670632:-6.8444837,39.2673575 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n30647522000000 && -6.8448148,39.2653526:-6.8439303,39.2670632 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902589000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647522000000 && -6.8439303,39.2670632:-6.8448148,39.2653526 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902589000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n30647513000003 && -6.8439303,39.2670632:-6.8433662,39.2667784 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647513000003 && -6.8433662,39.2667784:-6.8439303,39.2670632 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n30647521000001 && -6.8433662,39.2667784:-6.8433744,39.2667538:-6.8442739,39.2650222 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476906164000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647521000001 && -6.8442739,39.2650222:-6.8433744,39.2667538:-6.8433662,39.2667784 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476906164000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n30647513000004 && -6.8433662,39.2667784:-6.8428459,39.2665016 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647513000004 && -6.8428459,39.2665016:-6.8433662,39.2667784 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n448547628000000 && -6.8428459,39.2665016:-6.8432236,39.2657506 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> service\n-448547628000000 && -6.8432236,39.2657506:-6.8428459,39.2665016 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> service\n30647513000005 && -6.8428459,39.2665016:-6.8423426,39.2662339:-6.8418225,39.2659523 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647513000005 && -6.8418225,39.2659523:-6.8423426,39.2662339:-6.8428459,39.2665016 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476904347000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n30647448000003 && -6.8416131,39.2663468:-6.8416341,39.266294:-6.8418225,39.2659523 && last_edit_user_name -> RoadMapsYou || smoothness -> intermediate || surface -> unpaved || last_edit_time -> 1476916512000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || width -> 10 || highway -> service\n-30647448000003 && -6.8418225,39.2659523:-6.8416341,39.266294:-6.8416131,39.2663468 && last_edit_user_name -> RoadMapsYou || smoothness -> intermediate || surface -> unpaved || last_edit_time -> 1476916512000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || width -> 10 || highway -> service\n30647448000004 && -6.8418225,39.2659523:-6.8451463,39.25939 && last_edit_user_name -> RoadMapsYou || smoothness -> intermediate || surface -> unpaved || last_edit_time -> 1476916512000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || width -> 10 || highway -> service\n-30647448000004 && -6.8451463,39.25939:-6.8418225,39.2659523 && last_edit_user_name -> RoadMapsYou || smoothness -> intermediate || surface -> unpaved || last_edit_time -> 1476916512000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || width -> 10 || highway -> service\n365845535000002 && -6.8452479,39.2677181:-6.8454876,39.2674968:-6.8457516,39.2672956:-6.8460108,39.2671612:-6.8463174,39.2670486:-6.8468003,39.2669618:-6.8473713,39.266965 && last_edit_user_name -> RoadMapsYou || last_edit_time -> 1477067462000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || name -> Changombe Road || highway -> secondary\n-365845535000002 && -6.8473713,39.266965:-6.8468003,39.2669618:-6.8463174,39.2670486:-6.8460108,39.2671612:-6.8457516,39.2672956:-6.8454876,39.2674968:-6.8452479,39.2677181 && last_edit_user_name -> RoadMapsYou || last_edit_time -> 1477067462000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || name -> Changombe Road || highway -> secondary\n30647537000000 && -6.8457843,39.268217:-6.8452479,39.2677181 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902589000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n-30647537000000 && -6.8452479,39.2677181:-6.8457843,39.268217 && last_edit_user_name -> hulagirl14 || last_edit_time -> 1476902589000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || highway -> residential\n365845535000001 && -6.8449839,39.2679666:-6.8452479,39.2677181 && last_edit_user_name -> RoadMapsYou || last_edit_time -> 1477067462000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || name -> Changombe Road || highway -> secondary\n-365845535000001 && -6.8452479,39.2677181:-6.8449839,39.2679666 && last_edit_user_name -> RoadMapsYou || last_edit_time -> 1477067462000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || name -> Changombe Road || highway -> secondary\n# Areas\n365676688000000 && -6.8444603,39.2671592:-6.8444239,39.2672328:-6.8443915,39.2672165:-6.8443716,39.2672567:-6.844445,39.2672935:-6.8445013,39.2671797 && last_edit_user_name -> mkessy || last_edit_time -> 1439553838000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365676673000000 && -6.84446,39.2671297:-6.8443609,39.2671979:-6.8443266,39.2671473:-6.8444257,39.2670791 && last_edit_user_name -> mkessy || last_edit_time -> 1439553838000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365676663000000 && -6.8442543,39.2671325:-6.8442319,39.2671687:-6.8443227,39.2672257:-6.8443451,39.2671895 && last_edit_user_name -> mkessy || last_edit_time -> 1439553837000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365651953000000 && -6.8441079,39.2674307:-6.8441677,39.267464:-6.8442375,39.2673368:-6.8441777,39.2673035 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543438000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365676697000000 && -6.8441769,39.2669505:-6.8441125,39.2669531:-6.8441173,39.2670716:-6.8442135,39.2670676:-6.8442116,39.2670211:-6.8441798,39.2670224 && last_edit_user_name -> mkessy || last_edit_time -> 1439553839000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365651936000000 && -6.8440421,39.2673856:-6.8440985,39.2674173:-6.8441506,39.2673233:-6.8440942,39.2672916 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543438000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365651992000000 && -6.8439757,39.267337:-6.8440326,39.2673716:-6.8441023,39.2672554:-6.844065,39.2672327:-6.8440476,39.2672616:-6.8440281,39.2672497 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543440000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365651994000000 && -6.8439061,39.267207:-6.8439939,39.2672587:-6.8439091,39.267405:-6.8438745,39.2673847:-6.8438646,39.2674018:-6.8438114,39.2673705 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543441000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365641378000000 && -6.8432059,39.2665674:-6.8433043,39.2664961:-6.8433587,39.2665723:-6.8433121,39.2666061:-6.8432939,39.2665806:-6.8432421,39.2666181 && last_edit_user_name -> Hawa Adinani || last_edit_time -> 1439538983000 || last_edit_user_id -> 3044490 || iso_country_code -> TZA || building -> yes\n365641351000000 && -6.8432746,39.2666099:-6.8432349,39.2666547:-6.8432804,39.2666956:-6.8433201,39.2666508 && last_edit_user_name -> Hawa Adinani || last_edit_time -> 1439538981000 || last_edit_user_id -> 3044490 || iso_country_code -> TZA || building -> yes\n365641361000000 && -6.8432479,39.2661266:-6.8433104,39.2661584:-6.8431251,39.2665275:-6.8430626,39.2664957 && last_edit_user_name -> Hawa Adinani || last_edit_time -> 1439538982000 || last_edit_user_id -> 3044490 || iso_country_code -> TZA || building -> yes\n365641323000000 && -6.8431147,39.2665443:-6.8430958,39.2665772:-6.8431148,39.2665883:-6.8431047,39.2666058:-6.8431419,39.2666276:-6.8431516,39.2666107:-6.8431847,39.26663:-6.843176,39.2666451:-6.8432076,39.2666635:-6.8432236,39.2666358:-6.8432123,39.2666292:-6.8432243,39.2666083 && last_edit_user_name -> Hawa Adinani || last_edit_time -> 1439538980000 || last_edit_user_id -> 3044490 || iso_country_code -> TZA || building -> yes\n365652001000000 && -6.8429789,39.2666648:-6.8428986,39.2668155:-6.8432061,39.2669818:-6.843291,39.2668226:-6.8431499,39.2667463:-6.8430828,39.2668723:-6.8430495,39.2668543:-6.8431121,39.2667369 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543441000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365651986000000 && -6.8428055,39.2667507:-6.8428796,39.266786:-6.8429367,39.2666643:-6.8428626,39.266629 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543440000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365674696000000 && -6.8438305,39.2669268:-6.8438103,39.2669576:-6.8438729,39.2669991:-6.8439137,39.2669368:-6.8438753,39.2669113:-6.8438546,39.2669429 && last_edit_user_name -> mkessy || last_edit_time -> 1439552924000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365651950000000 && -6.84344,39.2672131:-6.8435315,39.2672601:-6.8436087,39.2671078:-6.8435172,39.2670608 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543438000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365652000000000 && -6.8433137,39.2671595:-6.843421,39.2672119:-6.843514,39.2670189:-6.8434572,39.2669911:-6.8434355,39.2670361:-6.8434273,39.2670321:-6.8434562,39.2669721:-6.8434139,39.2669514 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543441000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365651962000000 && -6.8431863,39.2671146:-6.8432856,39.2671656:-6.8434019,39.2669357:-6.8433026,39.2668847 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543439000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365674674000000 && -6.8438504,39.2667881:-6.8437582,39.2668634:-6.8437986,39.2669136:-6.8438908,39.2668384 && last_edit_user_name -> mkessy || last_edit_time -> 1439552923000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365674689000000 && -6.8437752,39.2666912:-6.8437481,39.2667316:-6.8437754,39.2667502:-6.8437637,39.2667677:-6.8438219,39.2668074:-6.8438607,39.2667495 && last_edit_user_name -> mkessy || last_edit_time -> 1439552924000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365674693000000 && -6.8436051,39.2666627:-6.8435513,39.2666673:-6.843561,39.2667824:-6.8437036,39.2667702:-6.8436994,39.2667206:-6.8436106,39.2667282 && last_edit_user_name -> mkessy || last_edit_time -> 1439552924000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365674697000000 && -6.8434993,39.2666398:-6.8434372,39.2667673:-6.8436664,39.2668805:-6.8437021,39.2668072:-6.8436663,39.2667895:-6.8436476,39.2668279:-6.8434903,39.2667502:-6.8435354,39.2666576 && last_edit_user_name -> mkessy || last_edit_time -> 1439552924000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365641325000000 && -6.842846,39.2660771:-6.8428092,39.2661506:-6.8428447,39.2661687:-6.8428218,39.2662144:-6.8428408,39.2662241:-6.8428603,39.2661851:-6.8428668,39.2661884:-6.8428414,39.266239:-6.8428557,39.2662463:-6.8428716,39.2662147:-6.84288,39.266219:-6.8428553,39.2662682:-6.8428699,39.2662756:-6.842895,39.2662255:-6.8429048,39.2662304:-6.8428818,39.2662764:-6.8429048,39.2662881:-6.8429771,39.2661437 && last_edit_user_name -> Hawa Adinani || last_edit_time -> 1439538980000 || last_edit_user_id -> 3044490 || iso_country_code -> TZA || building -> yes\n365641368000000 && -6.8427804,39.2659975:-6.8427348,39.2660943:-6.8427873,39.2661195:-6.8427955,39.266102:-6.8428029,39.2661056:-6.8428404,39.2660262 && last_edit_user_name -> Hawa Adinani || last_edit_time -> 1439538982000 || last_edit_user_id -> 3044490 || iso_country_code -> TZA || building -> yes\n365685491000000 && -6.8425719,39.2657465:-6.8424835,39.2659051:-6.8427325,39.2660458:-6.8428208,39.2658872 && last_edit_user_name -> mkessy || last_edit_time -> 1439556915000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365685480000000 && -6.8422802,39.2658334:-6.8421669,39.266049:-6.8426072,39.2662837:-6.8427205,39.2660681 && last_edit_user_name -> mkessy || last_edit_time -> 1439556915000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365685478000000 && -6.8420442,39.2657603:-6.8419506,39.2659568:-6.8420359,39.265998:-6.8421295,39.2658015 && last_edit_user_name -> mkessy || last_edit_time -> 1439556915000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365685476000000 && -6.8419089,39.2661464:-6.841852,39.2662711:-6.8422906,39.2664741:-6.8423475,39.2663494 && last_edit_user_name -> mkessy || last_edit_time -> 1439556915000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365685498000000 && -6.8418486,39.2663353:-6.8417933,39.2664427:-6.8421454,39.2666264:-6.8422187,39.266484:-6.8418666,39.2663003 && last_edit_user_name -> mkessy || last_edit_time -> 1439556916000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365650777000000 && -6.8453244,39.2673203:-6.8452306,39.2674097:-6.8452846,39.2674671:-6.8453783,39.2673776 && last_edit_user_name -> mkessy || last_edit_time -> 1439542909000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365650760000000 && -6.8452631,39.2672408:-6.8451752,39.2673206:-6.8452329,39.2673851:-6.8453208,39.2673053 && last_edit_user_name -> mkessy || last_edit_time -> 1439542909000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365650799000000 && -6.8452424,39.2674822:-6.8451321,39.2676245:-6.8451723,39.2676562:-6.8452826,39.2675138 && last_edit_user_name -> mkessy || last_edit_time -> 1439542910000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365650775000000 && -6.8450701,39.2674764:-6.845037,39.2675344:-6.8451434,39.267596:-6.8451765,39.2675381 && last_edit_user_name -> mkessy || last_edit_time -> 1439542909000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365652003000000 && -6.8446275,39.2678698:-6.8446871,39.2679022:-6.8447751,39.2677379:-6.8447146,39.267705:-6.8446871,39.2677562:-6.8447013,39.2677639:-6.8446876,39.2677895:-6.8446744,39.2677823 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543441000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365651934000000 && -6.8445799,39.2677748:-6.8446406,39.2678097:-6.8447164,39.2676762:-6.8446557,39.2676412 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543437000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365650759000000 && -6.844922,39.2673188:-6.8448558,39.2673266:-6.8448669,39.267422:-6.8449332,39.2674142 && last_edit_user_name -> mkessy || last_edit_time -> 1439542909000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA || building -> yes\n365651977000000 && -6.8445168,39.2675998:-6.8446327,39.2676488:-6.8446789,39.2675381:-6.844563,39.2674891 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543439000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365651937000000 && -6.844412,39.267742:-6.844536,39.2678157:-6.844609,39.2676909:-6.844485,39.2676173 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543438000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n365651989000000 && -6.8443966,39.2676032:-6.8444538,39.267635:-6.8445207,39.2675125:-6.8444635,39.2674808 && last_edit_user_name -> Kiconco Jovia || last_edit_time -> 1439543440000 || last_edit_user_id -> 3045272 || iso_country_code -> TZA || building -> yes\n# Lines\n391532976000000 && -6.8451075,39.2677416:-6.8422801,39.2662997:-6.8422308,39.2662746:-6.842007,39.2661674:-6.841836,39.2660798:-6.8417862,39.2661485:-6.841754,39.2662241:-6.8416958,39.2663473 && last_edit_user_name -> kombe1207 || depth -> 0.5 || last_edit_time -> 1454580673000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA || covered -> no || waterway -> drain || width -> 0.5\n381328473000000 && -6.8262643,39.2808988:-6.8264864,39.2806033:-6.8266598,39.2803224:-6.8267808,39.2800292:-6.8268758,39.2797284:-6.8269365,39.2793944:-6.8269548,39.2790782:-6.8269102,39.277786:-6.8268928,39.2772833:-6.826873,39.2770871:-6.8267694,39.2756725:-6.8267678,39.2756251:-6.8267625,39.2754729:-6.8267509,39.2754164:-6.826734,39.2753336:-6.826714,39.2749949:-6.8267068,39.2746356:-6.8267163,39.2743765:-6.8267339,39.2741816:-6.8267607,39.2739475:-6.8268209,39.2736375:-6.8268878,39.2733746:-6.8270377,39.2729435:-6.8271699,39.2726347:-6.8279412,39.2710872:-6.8282191,39.2705938:-6.828809,39.2695403:-6.8296602,39.2679318:-6.8297897,39.2676871:-6.8298094,39.267661:-6.8298633,39.2675726:-6.8301005,39.2670738:-6.8307832,39.2657215:-6.8325676,39.2621899:-6.8327187,39.2618909:-6.8335567,39.2602639:-6.833799,39.2597926:-6.833978,39.2594321:-6.8341594,39.2589455:-6.8342795,39.2586219:-6.8346383,39.2578978:-6.8351114,39.256943:-6.8365335,39.2541412:-6.8377873,39.2516902:-6.8380385,39.2512481:-6.8388611,39.2495616:-6.8403346,39.2466056:-6.8411946,39.2449816:-6.8412397,39.244879:-6.841694,39.243982:-6.8419439,39.2435021:-6.8427921,39.2418579:-6.844182,39.2391203:-6.8449017,39.2376935:-6.8457983,39.2359077:-6.8471274,39.2332451:-6.8491509,39.2293418:-6.8492453,39.2291228:-6.8500542,39.2274887:-6.8508657,39.2259275:-6.8519139,39.2238466:-6.8530043,39.2216787:-6.8547714,39.2182248:-6.8589044,39.2100194:-6.8613842,39.2050927:-6.8614841,39.2048932:-6.862813,39.2022407:-6.8636268,39.2006164:-6.8652104,39.1975138:-6.8664205,39.1950844:-6.8674807,39.1932265:-6.868406,39.1911881:-6.8694427,39.1891568:-6.8696872,39.1886654:-6.8697488,39.1885267:-6.8700213,39.1880045:-6.8701025,39.187842:-6.8702747,39.187512:-6.8709612,39.186149:-6.8711301,39.1858197:-6.8716697,39.184768:-6.8717536,39.1845173:-6.871822,39.1842756:-6.8718655,39.1839675:-6.8718311,39.1836505:-6.8717856,39.1834444:-6.8717112,39.1832348:-6.8713544,39.1826651:-6.8710561,39.1821931:-6.8709507,39.1818203:-6.8709709,39.1814635:-6.8711036,39.1808831:-6.8716697,39.1795151:-6.8724097,39.1774791:-6.8726653,39.1769812:-6.8743113,39.1744425:-6.874797,39.1736529:-6.8750149,39.1730437:-6.8750911,39.1725933:-6.8751101,39.171975:-6.8751379,39.1703227:-6.8751379,39.1678336:-6.8751634,39.1673014:-6.8752657,39.1667864:-6.8753728,39.1663253:-6.8760043,39.1650966:-6.8778221,39.1617568:-6.8783094,39.1608649:-6.8799183,39.1579201:-6.8813669,39.1553195:-6.8828082,39.1527016:-6.8831734,39.1520236:-6.8835214,39.1513093:-6.8837345,39.1506141:-6.8841489,39.1486096:-6.8845333,39.1467193:-6.8847447,39.1456867:-6.8849871,39.1445359 && gauge -> 1000 || last_edit_user_name -> RoadMapsYou || electrified -> no || passenger_lines -> 1 || last_edit_time -> 1477000439000 || last_edit_user_id -> 3789238 || iso_country_code -> TZA || usage -> main || cutting -> yes || railway -> rail\n395962046000000 && -6.8434132,39.2667704:-6.8433744,39.2667538:-6.8433122,39.2667256 && last_edit_user_name -> kombe1207 || blockage -> no || diameter -> 0.5 || last_edit_time -> 1454580664000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA || covered -> yes || waterway -> drain || layer -> -1 || tunnel -> culvert\n395962066000000 && -6.8434039,39.2665828:-6.8433122,39.2667256:-6.8429447,39.2665276 && last_edit_user_name -> Menrado mushi || blockage -> no || depth -> 0.5 || diameter -> -1 || last_edit_time -> 1458566564000 || last_edit_user_id -> 2788784 || iso_country_code -> TZA || covered -> no || waterway -> drain || width -> 0.5 || layer -> -1\n404927596000000 && -6.8429447,39.2665276:-6.8427886,39.2664456 && last_edit_user_name -> Menrado mushi || blockage -> no || depth -> 0.5 || diameter -> 1 || last_edit_time -> 1458566562000 || last_edit_user_id -> 2788784 || iso_country_code -> TZA || covered -> yes || waterway -> drain || width -> 0.5 || layer -> -1 || tunnel -> culvert\n371798292000000 && -6.8430211,39.2482031:-6.8426658,39.2489336:-6.8421671,39.2498337:-6.8417397,39.2506961:-6.8414164,39.2513206:-6.8408373,39.2524899:-6.8401434,39.2538904:-6.8395483,39.2550874:-6.839261,39.2556906:-6.8389568,39.2562618:-6.8389128,39.2563657:-6.8386748,39.2568435:-6.8385381,39.2573407:-6.8381218,39.2581534:-6.8374537,39.2594801:-6.8369654,39.2605359:-6.8363894,39.2617171:-6.8359522,39.2626091:-6.8354352,39.2635364:-6.8349772,39.2644717:-6.8340015,39.2661256:-6.8334215,39.2671166:-6.832836,39.2679307:-6.8323029,39.2685721 && last_edit_user_name -> GeoCrazy || blockage -> dirt || depth -> 1 || last_edit_time -> 1475878858000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || covered -> no || waterway -> drain || width -> 1\n395962057000000 && -6.8439331,39.2669281:-6.8438793,39.267014:-6.8434132,39.2667704 && last_edit_user_name -> innocent maholi || blockage -> no || depth -> 0.5 || last_edit_time -> 1458545297000 || last_edit_user_id -> 2780504 || iso_country_code -> TZA || covered -> no || waterway -> drain || width -> 0.5\n404927601000000 && -6.8427886,39.2664456:-6.8423854,39.26623 && last_edit_user_name -> Menrado mushi || blockage -> no || depth -> 0.5 || diameter -> -1 || last_edit_time -> 1458566562000 || last_edit_user_id -> 2788784 || iso_country_code -> TZA || covered -> no || waterway -> drain || width -> 0.5 || layer -> -1\n395962042000000 && -6.8423854,39.26623:-6.8423426,39.2662339:-6.8422801,39.2662997 && last_edit_user_name -> Menrado mushi || blockage -> no || depth -> 0.5 || diameter -> 1 || last_edit_time -> 1458566563000 || last_edit_user_id -> 2788784 || iso_country_code -> TZA || covered -> yes || waterway -> drain || width -> 0.5 || layer -> -1 || tunnel -> culvert\n395962053000000 && -6.8419136,39.2656471:-6.8416397,39.2661547:-6.8415674,39.2662606 && last_edit_user_name -> kombe1207 || depth -> 0.5 || last_edit_time -> 1454580665000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA || covered -> no || waterway -> drain || width -> 0.5\n395962050000000 && -6.8451785,39.2678492:-6.8452996,39.2677335:-6.8453542,39.2676895 && last_edit_user_name -> kombe1207 || blockage -> no || depth -> 1.5 || diameter -> 0.5 || last_edit_time -> 1454580664000 || last_edit_user_id -> 3046993 || iso_country_code -> TZA || covered -> yes || waterway -> drain || width -> 2 || layer -> -1 || tunnel -> culvert\n# Points\n3696625627000000 && -6.8441839,39.2672601 && last_edit_user_name -> salehehamisi || natural -> tree || last_edit_time -> 1439545778000 || last_edit_user_id -> 3043955 || iso_country_code -> TZA\n3696620724000000 && -6.8436888,39.2670182 && last_edit_user_name -> salehehamisi || natural -> tree || last_edit_time -> 1439545778000 || last_edit_user_id -> 3043955 || iso_country_code -> TZA\n3696620720000000 && -6.843482,39.2669256 && last_edit_user_name -> salehehamisi || natural -> tree || last_edit_time -> 1439545778000 || last_edit_user_id -> 3043955 || iso_country_code -> TZA\n3696552277000000 && -6.8428199,39.2662625 && last_edit_user_name -> Hawa Adinani || natural -> tree || last_edit_time -> 1439543415000 || last_edit_user_id -> 3044490 || iso_country_code -> TZA\n3696554465000000 && -6.8449153,39.2674883 && last_edit_user_name -> mkessy || natural -> tree || last_edit_time -> 1439543489000 || last_edit_user_id -> 3043891 || iso_country_code -> TZA\n# Relations\n5529913000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> samtwesa || ref -> 162 || route -> bus || last_edit_time -> 1448394689000 || last_edit_user_id -> 3085436 || iso_country_code -> TZA || name -> Temeke - Mwenge/Makumbusho (via Kawawa Rd) || from -> Temeke || to -> Mwenge/Makumbusho || type -> route || via -> Kawawa Rd || network -> Daladala\n5530001000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> samtwesa || ref -> 162 || route -> bus || last_edit_time -> 1448394678000 || last_edit_user_id -> 3085436 || iso_country_code -> TZA || name -> Mwenge/Makumbusho - Temeke (via Kawawa Rd) || from -> Mwenge/Makumbusho || to -> Temeke || type -> route || via -> (via Kawawa Rd) || network -> Daladala\n5529914000000 && 5529913000000 ->  -> R || 5530001000000 ->  -> R && last_edit_user_name -> sabinekoch || ref -> 162 || route_master -> bus || last_edit_time -> 1443101880000 || last_edit_user_id -> 2860766 || iso_country_code -> TZA || name -> Daladala - 162 || type -> route_master || network -> Daladala\n5529893000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> hulagirl14 || ref -> 161 || route -> bus || last_edit_time -> 1476741631000 || last_edit_user_id -> 3695979 || iso_country_code -> TZA || name -> Muhimbili - Temeke (via Changombe Rd) || from -> Muhimbili || to -> Temeke || type -> route || via -> Changombe Rd || network -> Daladala\n5690604000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> GeoCrazy || ref -> 82 || route -> bus || last_edit_time -> 1475624262000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || name -> Mbagala Rangi 3 - Makumbusho || from -> Mbagala Rangi 3 || to -> Makumbusho || type -> route || network -> Daladala\n5690603000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> GeoCrazy || ref -> 82 || route -> bus || public_transport:version -> 2 || last_edit_time -> 1475624261000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || name -> Makumbusho - Mbagala Rangi 3 || from -> Makumbusho || to -> Mbagala rangi 3 || type -> route || network -> Daladala\n5690605000000 && 5690603000000 ->  -> R || 5690604000000 ->  -> R && last_edit_user_name -> samtwesa || ref -> 82 || route_master -> bus || last_edit_time -> 1448434997000 || last_edit_user_id -> 3085436 || iso_country_code -> TZA || name -> Daladala - 82 || type -> route_master || network -> Daladala\n5529876000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> GeoCrazy || ref -> 161 || route -> bus || last_edit_time -> 1475878844000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || name -> Temeke - Muhimbili (via Changombe Rd) || from -> Temeke || to -> Muhimbili || type -> route || via -> Changombe Rd || network -> Daladala\n5529877000000 && 5529876000000 ->  -> R || 5529893000000 ->  -> R && last_edit_user_name -> sabinekoch || ref -> 162 || route_master -> bus || last_edit_time -> 1443100410000 || last_edit_user_id -> 2860766 || iso_country_code -> TZA || name -> Daladala - 161 || type -> route_master || network -> Daladala\n5690112000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> GeoCrazy || ref -> 77 || route -> bus || last_edit_time -> 1475878838000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || name -> Kariakoo - Mbagala Rangi 3 || from -> Kariakoo || to -> Mbagala Rangi 3 || type -> route || network -> Daladala\n5690113000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> GeoCrazy || ref -> 77 || route -> bus || last_edit_time -> 1475878840000 || last_edit_user_id -> 3693002 || iso_country_code -> TZA || name -> Mbagala Rangi 3 - Kariakoo || from -> Mbagala Rangi 3 || to -> Kariakoo || type -> route || network -> Daladala\n5456567000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> TheNodeWarrior || ref -> 158 || route -> bus || last_edit_time -> 1477188785000 || last_edit_user_id -> 4759993 || iso_country_code -> TZA || name -> Temeke - Makumbusho || from -> Temeke || to -> Makumbusho || type -> route || network -> Daladala\n5690114000000 && 5690112000000 ->  -> R || 5690113000000 ->  -> R && last_edit_user_name -> samtwesa || ref -> 77 || route_master -> bus || last_edit_time -> 1448394690000 || last_edit_user_id -> 3085436 || iso_country_code -> TZA || name -> Daladala - 77 || type -> route_master || network -> Daladala\n5513119000000 && -365845535000002 ->  -> E || -365845535000001 ->  -> E || 365845535000001 ->  -> E || 365845535000002 ->  -> E && last_edit_user_name -> TheNodeWarrior || ref -> 158 || route -> bus || last_edit_time -> 1477188782000 || last_edit_user_id -> 4759993 || iso_country_code -> TZA || name -> Makumbusho - Temeke || from -> Makumbusho || to -> Temeke || type -> route || network -> Daladala\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/AAA_boundary.expected.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            27.2363281,\n            26.2550928\n          ],\n          [\n            31.015625,\n            26.2550928\n          ],\n          [\n            31.015625,\n            23.5449523\n          ],\n          [\n            27.2363281,\n            23.5449523\n          ],\n          [\n            27.2363281,\n            26.2550928\n          ]\n        ]\n      },\n      \"properties\": {\n        \"iso_country_code\": \"AAA\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/AAA_boundary.txt",
    "content": "AAA||POLYGON((27.236328125 26.255092778502082,31.015625 26.255092778502082,31.015625 23.544952263601573,27.236328125 23.544952263601573,27.236328125 26.255092778502082))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/DMA_boundary.txt",
    "content": "DMA||POLYGON ((-61.3490238 15.0074943, -61.3702452 15.0074207, -61.4005587 15.0115114, -61.4232934 15.0170995, -61.4499481 15.0267559, -61.4813509 15.0421834, -61.5102395 15.0630642, -61.5388572 15.0931927, -61.5609937 15.1286379, -61.573888 15.1626946, -61.5802577 15.1983142, -61.5802519 15.2182578, -61.5896846 15.2426108, -61.5979214 15.2828215, -61.6116448 15.3052907, -61.6237522 15.3347959, -61.6350822 15.3524338, -61.6486274 15.3851843, -61.6521867 15.3997221, -61.6602503 15.4157614, -61.6719498 15.4490992, -61.6756242 15.4722623, -61.6819285 15.4920564, -61.6869184 15.5259787, -61.6832167 15.6182678, -61.6798235 15.644411, -61.4425 15.7872222, -61.3236111 15.7341667, -61.1383333 15.7016667, -61.1204868 15.6804104, -61.116797 15.6720689, -61.1106596 15.6656506, -61.0968189 15.6460962, -61.083421 15.6300299, -61.0657932 15.5989765, -61.0607846 15.5846292, -61.0567601 15.5769338, -61.055243 15.5742449, -61.0436767 15.541066, -61.036387 15.4952162, -61.0344085 15.4615026, -61.037269 15.4351561, -61.0329895 15.3942999, -61.0364022 15.3589037, -61.0354401 15.3544219, -61.0346416 15.3150813, -61.0368515 15.3029501, -61.0373217 15.2768091, -61.0420306 15.2476706, -61.0442149 15.2375483, -61.0562047 15.2043716, -61.0609591 15.196169, -61.0666737 15.1779782, -61.083955 15.144618, -61.1069788 15.1151873, -61.1349236 15.0905183, -61.1589127 15.0792459, -61.2830556 15.0308333, -61.3490238 15.0074943))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/DMA_snake_polyline.wkt",
    "content": "LINESTRING(-61.84616088867186 15.432500881886057,-61.50283813476562 15.541022107176659,-60.81619262695312 15.464269084198364,-60.81344604492187 15.374246553214803,-61.18148803710937 15.371598241700937,-61.50283813476562 15.374246553214803,-61.77749633789061 15.29478259026095,-61.78298950195312 15.164926674810914,-61.65939331054686 15.130461682940435,-61.43966674804687 15.228539583518852)"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/HTI_boundary.txt",
    "content": "POLYGON ((-71.7372633 19.0496966, -71.7343017 19.0499778, -71.7322925 19.0500469, -71.7287854 19.0511174, -71.7246572 19.0526022, -71.7227575 19.0538798, -71.7222095 19.0550194, -71.7223191 19.0563315, -71.7222095 19.0572984, -71.7214424 19.0586105, -71.7205656 19.0595428, -71.7191408 19.059232, -71.7174238 19.0583343, -71.7168758 19.0587832, -71.7160283 19.061308, -71.7162475 19.0626547, -71.7168686 19.0630345, -71.717307 19.0632762, -71.7192432 19.0635524, -71.7203757 19.064312, -71.7204122 19.0659004, -71.7194624 19.0671779, -71.716211 19.070389, -71.7147132 19.0710795, -71.713398 19.0707688, -71.7118271 19.070389, -71.710585 19.0701128, -71.7101831 19.0693531, -71.7100005 19.0686626, -71.7103279 19.0678448, -71.7102197 19.067247, -71.7097754 19.0669322, -71.7088314 19.0675922, -71.7075623 19.0681781, -71.7072606 19.0683173, -71.7053243 19.0695603, -71.7031715 19.0714733, -71.7020007 19.0722479, -71.7012612 19.0734884, -71.7016283 19.0747719, -71.7023396 19.0754684, -71.7031653 19.076045, -71.7054145 19.0760508, -71.707713 19.0764061, -71.7096355 19.0772738, -71.7113094 19.0780864, -71.7121065 19.0784676, -71.7122976 19.0809427, -71.7121373 19.0822413, -71.7116998 19.0836622, -71.7099806 19.0852171, -71.7090316 19.0853452, -71.7036583 19.0844018, -71.7017258 19.0846515, -71.6999292 19.0861914, -71.6991438 19.0867717, -71.6980843 19.0867026, -71.6973172 19.0866336, -71.697171 19.0870651, -71.6966961 19.0878419, -71.6965134 19.0888776, -71.6968592 19.0907888, -71.6981637 19.0929126, -71.699197 19.0939867, -71.6988224 19.0946092, -71.6987708 19.0948777, -71.6988095 19.0952683, -71.6993391 19.0959762, -71.7010182 19.0966109, -71.7020127 19.0966475, -71.7026327 19.0966597, -71.7034206 19.0967085, -71.7044519 19.0973428, -71.704735 19.0981023, -71.7053926 19.0985338, -71.7061415 19.0990085, -71.7063881 19.0991293, -71.706452 19.0995522, -71.7065159 19.1000614, -71.706178 19.1004066, -71.7052999 19.1014213, -71.7050287 19.1030202, -71.7046412 19.1033741, -71.7036079 19.1039599, -71.7033883 19.1044359, -71.70313 19.1050828, -71.7024842 19.1059859, -71.7014767 19.1065961, -71.7010505 19.1068158, -71.6995264 19.1077589, -71.6984027 19.1084546, -71.6968786 19.1086498, -71.6954966 19.1081861, -71.6945408 19.1075758, -71.6940241 19.1071609, -71.6932621 19.1066117, -71.6924563 19.1061095, -71.6920544 19.1056435, -71.6920808 19.1054541, -71.6919847 19.1053896, -71.6913275 19.1053145, -71.6908173 19.1050665, -71.6890458 19.1053416, -71.687287 19.1034484, -71.6849042 19.1029542, -71.6821375 19.1074262, -71.6831239 19.1084278, -71.6815456 19.1091027, -71.6813619 19.1091812, -71.6797646 19.1088094, -71.6792488 19.109441, -71.6796705 19.1097969, -71.6802758 19.1103079, -71.6798029 19.1121225, -71.6805717 19.1135624, -71.6825746 19.1145372, -71.6818494 19.1156704, -71.6825614 19.1171003, -71.6796017 19.1159781, -71.6785504 19.1171955, -71.6779481 19.1173453, -71.6777145 19.1173136, -71.6772545 19.1170426, -71.6769411 19.116774, -71.676787 19.1164048, -71.6767242 19.1146122, -71.6748778 19.1134054, -71.6729624 19.1128022, -71.6724775 19.1156726, -71.6713136 19.1159493, -71.671078 19.1168115, -71.6705628 19.1174069, -71.6692752 19.1162734, -71.6680738 19.1160441, -71.6672716 19.1143869, -71.6651113 19.1141582, -71.6631131 19.1129487, -71.6581459 19.1149072, -71.6571865 19.1163429, -71.6557234 19.1173272, -71.6542978 19.1155052, -71.6537368 19.1164878, -71.6537417 19.1185998, -71.6516177 19.1208084, -71.6511783 19.1228222, -71.6498472 19.1247926, -71.6493832 19.1256686, -71.6491825 19.1266757, -71.6483743 19.1286105, -71.6477975 19.128961, -71.6481387 19.1308716, -71.6475356 19.1315826, -71.646987 19.1328813, -71.6464634 19.1334223, -71.6460591 19.1355719, -71.6472574 19.1359549, -71.6487774 19.1373904, -71.6489442 19.1375379, -71.6503086 19.1376892, -71.6515852 19.1388948, -71.6527927 19.1388266, -71.6543091 19.1408578, -71.6560992 19.1420366, -71.6565077 19.1434093, -71.655971 19.1437088, -71.6531075 19.1425236, -71.6529193 19.1426609, -71.6523071 19.1431228, -71.6499076 19.1429707, -71.6490283 19.1437218, -71.6487189 19.1449346, -71.6481845 19.1446163, -71.6475555 19.1442274, -71.6463781 19.1451537, -71.64558 19.1447059, -71.6449007 19.1454245, -71.6443013 19.1450074, -71.6433365 19.1457569, -71.6427705 19.1451059, -71.6411746 19.1460604, -71.6404276 19.146841, -71.6406206 19.1480991, -71.6417415 19.1485527, -71.6429341 19.1496846, -71.6440661 19.1496894, -71.6441412 19.1510425, -71.6450992 19.1515744, -71.6457491 19.1527777, -71.6467788 19.1525798, -71.6470255 19.153532, -71.6460646 19.1536137, -71.6450203 19.154064, -71.6440746 19.1538661, -71.6435282 19.1541393, -71.6441458 19.1550408, -71.6454346 19.1556509, -71.645825 19.1570028, -71.6437445 19.1560984, -71.6423791 19.1569308, -71.6432758 19.1581386, -71.6433418 19.1595659, -71.6439107 19.160551, -71.6438289 19.1613076, -71.6455924 19.1614572, -71.6446343 19.1628115, -71.6451033 19.1635692, -71.6466321 19.1631188, -71.6476695 19.1639501, -71.644907 19.1657588, -71.6462596 19.1679135, -71.6463432 19.1686078, -71.6452238 19.1681175, -71.6443861 19.1682104, -71.6429846 19.1665963, -71.6417469 19.1667451, -71.6412998 19.168181, -71.6404941 19.1681031, -71.6373777 19.1662861, -71.6361347 19.1666965, -71.636492 19.1687427, -71.6358361 19.168952, -71.6357176 19.1689903, -71.6342519 19.1682052, -71.633726 19.1688455, -71.6362213 19.1708497, -71.6390136 19.1731933, -71.6390864 19.1742867, -71.6400057 19.1748541, -71.6429728 19.1746738, -71.6434885 19.1754775, -71.6450918 19.1760393, -71.6472407 19.1760835, -71.6472935 19.176603, -71.6454896 19.1775087, -71.6438478 19.1774335, -71.6436069 19.1780699, -71.6444496 19.1800709, -71.642168 19.1792349, -71.6411288 19.1784848, -71.637958 19.1783996, -71.6370863 19.179232, -71.6370645 19.1803056, -71.6365092 19.1806225, -71.6363522 19.1821698, -71.6353999 19.1832263, -71.6349955 19.1820908, -71.6339956 19.1817475, -71.6322301 19.1826176, -71.6316275 19.1832925, -71.6305425 19.1824603, -71.6300686 19.1814767, -71.6285868 19.1814767, -71.6276311 19.1817659, -71.6278569 19.1823387, -71.6284978 19.1835147, -71.6275345 19.1846432, -71.6268943 19.1862289, -71.627937 19.1867986, -71.6298833 19.1868434, -71.629726 19.1885102, -71.6305715 19.1894373, -71.6323092 19.1889999, -71.633434 19.1883436, -71.6340908 19.1887438, -71.6352584 19.189713, -71.6358994 19.1899593, -71.6370798 19.1893673, -71.6372209 19.1899024, -71.6371677 19.1911198, -71.6375001 19.1920555, -71.6378571 19.1922038, -71.6396202 19.1929067, -71.6386556 19.1936291, -71.6377781 19.1938025, -71.6373889 19.1942556, -71.6374182 19.1951497, -71.6382708 19.1952646, -71.6392156 19.1969245, -71.6391473 19.1969606, -71.638535 19.197228, -71.6384945 19.1982831, -71.637859 19.1986957, -71.6372143 19.200507, -71.6383426 19.2010782, -71.6378658 19.2020983, -71.6355621 19.2018665, -71.6349522 19.2028932, -71.6351222 19.2034108, -71.6336365 19.2049899, -71.6329876 19.2052562, -71.6314139 19.2059058, -71.630554 19.2052131, -71.6294886 19.2055134, -71.6290357 19.2056411, -71.6276397 19.2060349, -71.6277945 19.2054331, -71.626377 19.2041344, -71.6248282 19.2028535, -71.6238362 19.2038799, -71.6240687 19.2050575, -71.6241422 19.2061058, -71.6221297 19.2060326, -71.6227785 19.2077593, -71.6238045 19.2077602, -71.6254983 19.2085495, -71.6258161 19.2078785, -71.6263819 19.2076182, -71.6268348 19.2091698, -71.6277506 19.2089791, -71.627949 19.2081073, -71.6302714 19.2078883, -71.6329586 19.2070262, -71.6336831 19.2078338, -71.6303306 19.2095349, -71.6291905 19.2101158, -71.6285812 19.2111063, -71.6292305 19.2118764, -71.6299834 19.2122332, -71.6300265 19.2129109, -71.6312991 19.2147935, -71.6338274 19.216609, -71.6339599 19.2171216, -71.6340021 19.2172752, -71.6340966 19.2176198, -71.6349593 19.2207363, -71.6362219 19.2226548, -71.6367459 19.2239822, -71.6360576 19.2246554, -71.6358202 19.2251203, -71.6369457 19.2253667, -71.6382228 19.2242439, -71.6386633 19.2245408, -71.6384776 19.2252595, -71.6392308 19.2255982, -71.6397377 19.2250591, -71.640192 19.224587, -71.6407034 19.2246685, -71.6405078 19.2254141, -71.6409078 19.2258276, -71.6407869 19.2261683, -71.6393016 19.2263215, -71.6383787 19.2277847, -71.6394109 19.2290479, -71.6397414 19.2294523, -71.6411055 19.2291886, -71.6442399 19.2294047, -71.6445498 19.2294233, -71.6457979 19.228327, -71.6465989 19.2281792, -71.6491943 19.2295084, -71.6512805 19.2292487, -71.6528014 19.230152, -71.6545206 19.2297142, -71.6571471 19.2299788, -71.6573331 19.2292421, -71.6577775 19.2288802, -71.6583385 19.2293327, -71.6597426 19.2293046, -71.6599354 19.2292987, -71.6625448 19.229942, -71.6630997 19.2297536, -71.6637823 19.2298292, -71.6650693 19.2292286, -71.6651444 19.2291952, -71.6653314 19.2292862, -71.6660482 19.2296088, -71.6660336 19.2303305, -71.6668564 19.2306322, -71.6672693 19.2307772, -71.6669466 19.2322997, -71.6667064 19.2333748, -71.6665979 19.2339149, -71.6672695 19.2340625, -71.6681029 19.2337257, -71.6686229 19.2343309, -71.6689585 19.2342357, -71.6695499 19.234068, -71.6699147 19.2329374, -71.6705475 19.2331294, -71.6707082 19.2339125, -71.6709124 19.2348239, -71.671547 19.235395, -71.6737545 19.2352455, -71.6748381 19.2356986, -71.6750207 19.2355733, -71.6755319 19.2352227, -71.6755607 19.2352062, -71.6756491 19.2352719, -71.6763577 19.2357352, -71.676921 19.2351317, -71.6771681 19.2332408, -71.6778945 19.232559, -71.6791245 19.2323466, -71.6801829 19.2345231, -71.6810036 19.235296, -71.6808029 19.2364497, -71.6808231 19.2367093, -71.6819995 19.2368992, -71.6826856 19.2371859, -71.6830593 19.2370838, -71.6848183 19.2365025, -71.6854507 19.2368139, -71.684933 19.2374161, -71.6847811 19.238352, -71.6856525 19.2389636, -71.6884147 19.2391129, -71.6888602 19.2386967, -71.6900596 19.2385921, -71.69094 19.2392308, -71.6921416 19.2409132, -71.6925396 19.2419132, -71.6935001 19.2418765, -71.6956173 19.2405519, -71.696992 19.2407121, -71.6974782 19.2415783, -71.6974357 19.2427509, -71.6977929 19.2434253, -71.6982353 19.2441101, -71.6996361 19.2448666, -71.7003135 19.2452037, -71.7016073 19.2463318, -71.7024336 19.2470561, -71.7027578 19.2474772, -71.7045589 19.2476991, -71.7060437 19.250912, -71.7080839 19.2510661, -71.7089496 19.2529229, -71.7075617 19.2553259, -71.7079915 19.2566424, -71.7073368 19.2575422, -71.7086738 19.2595971, -71.7099346 19.2602335, -71.7106671 19.2606709, -71.7115489 19.2607771, -71.7130616 19.2630788, -71.7135812 19.2637109, -71.714049 19.263602, -71.7159875 19.2645755, -71.7157734 19.2662595, -71.7167249 19.2671521, -71.7180177 19.2671483, -71.7198627 19.2661344, -71.7209816 19.2667412, -71.7225814 19.2675371, -71.7237775 19.2676127, -71.7240622 19.2685926, -71.7220196 19.2699821, -71.7219102 19.2707021, -71.7226688 19.2717447, -71.7235387 19.2757949, -71.7241034 19.2765542, -71.725542 19.2782949, -71.7268638 19.2773077, -71.7276645 19.2771957, -71.7274205 19.2751244, -71.7284292 19.2741045, -71.7283206 19.2733534, -71.7295142 19.2725986, -71.7292331 19.2719166, -71.7294332 19.2709364, -71.7301142 19.2701453, -71.7309144 19.2690945, -71.7310341 19.267851, -71.7322024 19.2664549, -71.7310753 19.2624182, -71.7316788 19.2621853, -71.7338434 19.2651755, -71.7353292 19.2650216, -71.7358882 19.2655911, -71.7365635 19.2674806, -71.7384068 19.2689486, -71.7390049 19.272344, -71.7396981 19.2738186, -71.7395288 19.2746911, -71.7386438 19.2752168, -71.7382462 19.2780168, -71.7388886 19.2786962, -71.7394619 19.278544, -71.7400936 19.2768854, -71.7430527 19.2767307, -71.7452209 19.2757133, -71.7463445 19.2741719, -71.7471831 19.2740604, -71.7481812 19.2745376, -71.7486117 19.2756336, -71.7486242 19.2779604, -71.7499966 19.2788678, -71.751089 19.2792499, -71.7530406 19.2795825, -71.7548338 19.2802281, -71.7551827 19.2813355, -71.7547874 19.2825831, -71.7542975 19.2828452, -71.7537787 19.2831248, -71.7533779 19.2841647, -71.753392 19.284896, -71.7536417 19.2862183, -71.7536239 19.2871206, -71.7532404 19.288242, -71.7542089 19.2892612, -71.7551113 19.2897737, -71.7558451 19.2901658, -71.7561667 19.2907311, -71.7564406 19.2913046, -71.7579526 19.2912773, -71.7586078 19.2908287, -71.759739 19.2908307, -71.7604691 19.2909249, -71.7610667 19.291468, -71.7610861 19.2928945, -71.7607438 19.2943507, -71.7602875 19.2957958, -71.7577299 19.2972667, -71.7561451 19.2976086, -71.7560706 19.2989792, -71.7563247 19.2995885, -71.7565519 19.3001161, -71.7572082 19.3010664, -71.7581256 19.300821, -71.75917 19.3013721, -71.7598006 19.3021776, -71.7590387 19.3027237, -71.7590943 19.3032843, -71.7595071 19.3035624, -71.7615491 19.3036619, -71.762064 19.3045465, -71.7625632 19.3047811, -71.7628789 19.3041999, -71.7629472 19.3031451, -71.7631984 19.302942, -71.7642503 19.3035925, -71.7644771 19.3041471, -71.763843 19.3049663, -71.7629334 19.3062589, -71.7625523 19.3067846, -71.7626915 19.307437, -71.7644753 19.3081005, -71.7650728 19.3096275, -71.7654161 19.3100578, -71.7653853 19.310653, -71.7654077 19.3114477, -71.7659455 19.3116558, -71.7664745 19.3118277, -71.7664823 19.3133622, -71.7664357 19.3142821, -71.7667417 19.3146756, -71.7674214 19.3158881, -71.7673231 19.3165363, -71.7680322 19.3167384, -71.7691369 19.3171281, -71.7696891 19.3175711, -71.769764 19.318114, -71.7690691 19.3191217, -71.7686631 19.3199449, -71.7694839 19.3207538, -71.7706579 19.3205218, -71.7714996 19.3202659, -71.7728513 19.3201815, -71.7737408 19.3204049, -71.7739458 19.3215819, -71.774238 19.3226793, -71.7753925 19.3229615, -71.7763483 19.323186, -71.7767098 19.3236617, -71.7765354 19.3257618, -71.7767087 19.3266223, -71.7771574 19.3270184, -71.7780521 19.3279368, -71.7791241 19.32903, -71.7796477 19.3294857, -71.7801696 19.3295358, -71.7815517 19.3291958, -71.7827107 19.329502, -71.7843358 19.3301271, -71.7848151 19.330743, -71.7853666 19.330867, -71.7857705 19.3311719, -71.785936 19.3316482, -71.78336 19.3331575, -71.7787914 19.335391, -71.7746522 19.3374083, -71.7722246 19.3385206, -71.7683215 19.3387493, -71.7631704 19.339091, -71.7562382 19.3395904, -71.7526332 19.3396526, -71.751787 19.3406123, -71.7510882 19.3437228, -71.7482404 19.3535463, -71.7440398 19.3562874, -71.7375271 19.3581293, -71.7349221 19.3595267, -71.7349748 19.3607011, -71.7316 19.3606404, -71.7310128 19.3614783, -71.7303573 19.3628745, -71.7291786 19.3633227, -71.724435 19.3665588, -71.7226631 19.3652813, -71.7215992 19.3652261, -71.7197648 19.3642181, -71.7187481 19.3627557, -71.7181665 19.3628264, -71.7185183 19.3647282, -71.7169318 19.3684452, -71.7140716 19.3716429, -71.7054864 19.3658465, -71.7033014 19.3647869, -71.7028044 19.3634872, -71.7030094 19.3622816, -71.702354 19.3608256, -71.7012768 19.3595424, -71.7000494 19.3595743, -71.6973201 19.3600483, -71.6967792 19.359515, -71.6961952 19.359712, -71.694513 19.3596634, -71.6937104 19.3603257, -71.692095 19.3630943, -71.6936195 19.3643585, -71.6939363 19.3675051, -71.6939706 19.368634, -71.6936032 19.3689342, -71.6925384 19.3693932, -71.6916481 19.3706225, -71.6910065 19.3717841, -71.6913023 19.372674, -71.693379 19.3748598, -71.6939583 19.3796089, -71.6946402 19.3811646, -71.6943524 19.3817551, -71.6945792 19.3823008, -71.6952518 19.3852553, -71.6952526 19.3861579, -71.6956702 19.3866709, -71.695809 19.38828, -71.6964886 19.388536, -71.696425 19.3888598, -71.6973826 19.3899513, -71.6970371 19.3910461, -71.6977249 19.3913655, -71.6984072 19.3929123, -71.697525 19.3942049, -71.6981355 19.3960031, -71.6967721 19.3971064, -71.6972503 19.3983877, -71.6967016 19.3987117, -71.6964309 19.3998711, -71.6948873 19.4014135, -71.6943241 19.4019809, -71.6939539 19.4028857, -71.692709 19.4032963, -71.6924836 19.4040954, -71.6917013 19.4042075, -71.6904367 19.4070005, -71.6899617 19.4074431, -71.6900068 19.4084998, -71.6897888 19.4091241, -71.6897547 19.4101469, -71.6895785 19.4113351, -71.6888121 19.4120703, -71.6888439 19.414255, -71.6891243 19.4145038, -71.6898884 19.4143463, -71.6904099 19.414428, -71.6906191 19.4149011, -71.6892272 19.4202188, -71.6894067 19.4221535, -71.6890606 19.4223368, -71.6866749 19.4226994, -71.6853287 19.4271604, -71.6844644 19.4280291, -71.6837037 19.4303438, -71.6826523 19.431525, -71.6837608 19.4326824, -71.6836588 19.4330236, -71.6814797 19.4330468, -71.6802483 19.43325, -71.6797049 19.4342418, -71.6797584 19.4358223, -71.6808079 19.4366087, -71.6809079 19.438244, -71.680711 19.4404337, -71.6799758 19.4415032, -71.6804294 19.4421163, -71.6802692 19.4429798, -71.6784688 19.4440208, -71.6784594 19.4449502, -71.6791527 19.4454774, -71.6797666 19.445236, -71.6797268 19.4462551, -71.6802035 19.4471393, -71.6799036 19.4492548, -71.6799058 19.4514841, -71.6801206 19.4516776, -71.6814176 19.4510695, -71.681577 19.4521194, -71.6846039 19.4548644, -71.6855193 19.4557025, -71.6870265 19.459259, -71.6869302 19.4597808, -71.6862187 19.4601467, -71.6853891 19.4602309, -71.6854246 19.4612965, -71.6863272 19.4618275, -71.6871224 19.4625009, -71.6876846 19.4629264, -71.6873491 19.4649239, -71.686931 19.4667665, -71.6869895 19.467634, -71.6877716 19.46754, -71.6894859 19.4670027, -71.6903505 19.467533, -71.690631 19.4682421, -71.6898642 19.4699249, -71.688521 19.4718679, -71.6888419 19.4724694, -71.6894726 19.4728059, -71.6899028 19.4736441, -71.6911158 19.4743613, -71.6927401 19.4745083, -71.6932837 19.4749153, -71.6935256 19.4756598, -71.694219 19.4771256, -71.6938075 19.4786433, -71.692922 19.4805406, -71.6920105 19.4818417, -71.6928733 19.4834099, -71.6925368 19.4845138, -71.6926774 19.485085, -71.6933437 19.4855485, -71.6932688 19.4873612, -71.6938904 19.4881488, -71.6941492 19.4894622, -71.6939198 19.4899815, -71.6917296 19.4914487, -71.6907788 19.4923429, -71.6908209 19.4930747, -71.6919385 19.4938082, -71.6915526 19.4950015, -71.6904681 19.4991875, -71.688106 19.5030529, -71.6883126 19.5033992, -71.6884905 19.5038276, -71.6887359 19.5046888, -71.6889287 19.505312, -71.6894541 19.5062386, -71.6895328 19.5067444, -71.6892627 19.5077489, -71.6886661 19.5088453, -71.6878675 19.5102681, -71.6878 19.511322, -71.6880701 19.5125599, -71.6886104 19.5134865, -71.6890007 19.5139463, -71.6893534 19.5140736, -71.6897436 19.5140524, -71.6901939 19.513727, -71.6905841 19.5132885, -71.6909969 19.5126023, -71.6914771 19.5116262, -71.6918073 19.5112089, -71.6923326 19.5109825, -71.6928279 19.5107137, -71.6931731 19.5106005, -71.6940962 19.5108481, -71.6947041 19.5114423, -71.6952069 19.5118879, -71.6957547 19.5120011, -71.6970154 19.5116899, -71.6982612 19.5115272, -71.6989215 19.5118384, -71.6998521 19.5131328, -71.7003474 19.5140241, -71.70052 19.5149649, -71.7003249 19.5168676, -71.6998146 19.5186854, -71.6998971 19.5201566, -71.7002048 19.5206234, -71.7008502 19.5208498, -71.7016457 19.5207649, -71.7025612 19.5206093, -71.7031015 19.5207649, -71.7032741 19.5210407, -71.7033192 19.5220876, -71.7035368 19.5233253, -71.7037619 19.5251219, -71.7037319 19.5265647, -71.7034843 19.5273357, -71.7034767 19.5281985, -71.7034242 19.5287431, -71.7029514 19.5299738, -71.7028839 19.5303769, -71.7026287 19.5307235, -71.7024862 19.531742, -71.7025462 19.5327746, -71.7028089 19.5333191, -71.7031616 19.533793, -71.7038295 19.534281, -71.7044523 19.5344507, -71.7049026 19.5343729, -71.705563 19.534083, -71.7059757 19.5337223, -71.706381 19.5331635, -71.7066511 19.5324846, -71.7069063 19.5318622, -71.7071389 19.5315298, -71.7076417 19.5313247, -71.7083321 19.5315086, -71.7096154 19.5316359, -71.7108161 19.5319046, -71.7120093 19.5323856, -71.7125196 19.5329938, -71.7127447 19.5336303, -71.7129248 19.5345851, -71.7130749 19.5356884, -71.7129624 19.5365795, -71.7126772 19.5381355, -71.7124596 19.5400167, -71.7124371 19.541219, -71.7124871 19.5418925, -71.7125721 19.5426758, -71.7125672 19.5435447, -71.7126097 19.5442954, -71.7127477 19.5447086, -71.7129409 19.5456964, -71.7128934 19.5463589, -71.712773 19.5469558, -71.7123328 19.5475407, -71.7121428 19.5479764, -71.7120415 19.5483345, -71.7120447 19.5484927, -71.7121354 19.5492166, -71.7123477 19.5496441, -71.7125531 19.5500592, -71.7128435 19.5506309, -71.7133154 19.5518477, -71.713834 19.5524537, -71.7144771 19.5530108, -71.7148781 19.5533109, -71.7151823 19.5535385, -71.7165148 19.5543265, -71.7171964 19.5547802, -71.7180915 19.5555372, -71.7185479 19.5562115, -71.7194227 19.557049, -71.7199542 19.5574614, -71.7207672 19.5579152, -71.7214986 19.5580196, -71.7222455 19.5579326, -71.7226706 19.5577948, -71.7230751 19.5578339, -71.7232623 19.5584277, -71.7231788 19.5587965, -71.7230491 19.5590555, -71.7228521 19.5592021, -71.7221987 19.559163, -71.7218305 19.5593829, -71.7214812 19.5597139, -71.7212808 19.5602332, -71.7213067 19.5605606, -71.7214519 19.5609417, -71.7216956 19.5610688, -71.7233797 19.5612121, -71.724053 19.5612958, -71.7240745 19.5612985, -71.7242869 19.561644, -71.7245378 19.5622442, -71.7244413 19.5630263, -71.7247115 19.5639903, -71.7249431 19.5650088, -71.7248466 19.5654817, -71.7238622 19.5659364, -71.743137 19.5846797, -71.743441 19.588494, -71.7436894 19.5927135, -71.7452958 19.6146301, -71.745748 19.6337819, -71.7372208 19.6417367, -71.7330457 19.6456052, -71.7324345 19.6461715, -71.7327828 19.6482538, -71.7349932 19.6615297, -71.735187 19.6615714, -71.7353836 19.6617337, -71.7355948 19.6619746, -71.735813 19.6622155, -71.7360187 19.6624021, -71.7364598 19.662579, -71.73677 19.662661, -71.7369548 19.6627724, -71.7370599 19.6628994, -71.7371333 19.6630568, -71.7371748 19.6632295, -71.7373283 19.6634088, -71.737505 19.6634977, -71.7377449 19.6635182, -71.7378966 19.6635019, -71.7381756 19.6634319, -71.7383191 19.6634006, -71.7384154 19.6634449, -71.7385048 19.6636022, -71.7386107 19.6638195, -71.7386768 19.6640522, -71.7386811 19.6644249, -71.7387007 19.6645858, -71.738765 19.6647753, -71.7388576 19.6649148, -71.7390806 19.6651211, -71.7392884 19.6652461, -71.7394668 19.6653125, -71.7396829 19.6653181, -71.7399924 19.6652242, -71.7404563 19.6650468, -71.7411242 19.6649042, -71.7418019 19.6647798, -71.7422956 19.6646843, -71.7426538 19.6646199, -71.7428282 19.6645886, -71.7432059 19.6645397, -71.7433806 19.6645474, -71.7435074 19.6646194, -71.7436451 19.6648283, -71.7438596 19.6649452, -71.7440054 19.6649806, -71.7440936 19.6650803, -71.7441445 19.6653356, -71.7442257 19.6657185, -71.7443342 19.6659094, -71.7445004 19.666045, -71.7449965 19.6662641, -71.7454062 19.6665072, -71.7457086 19.6666965, -71.7459144 19.666914, -71.7461008 19.6671407, -71.7462473 19.6672491, -71.7464124 19.6672751, -71.7466164 19.6673008, -71.7467632 19.6674457, -71.7468134 19.6676279, -71.7467856 19.6677652, -71.7466126 19.667931, -71.7463345 19.6682621, -71.7461529 19.6685468, -71.7461347 19.6686748, -71.7462041 19.6688386, -71.7463995 19.6689831, -71.7470279 19.6692043, -71.7472711 19.6692845, -71.7474367 19.6693653, -71.7475057 19.6694743, -71.7474586 19.6696208, -71.7473921 19.6697766, -71.7473837 19.6699046, -71.7474823 19.6700773, -71.7476286 19.6701674, -71.7478809 19.6701744, -71.7480951 19.6702639, -71.7483392 19.6704354, -71.7484766 19.6706078, -71.7485558 19.6707806, -71.7487316 19.670907, -71.7490434 19.6710597, -71.7493654 19.6712579, -71.7496856 19.6715352, -71.7500765 19.6718516, -71.7505166 19.6722315, -71.7509377 19.6726572, -71.7510257 19.6731566, -71.7513147 19.6736699, -71.7517983 19.6741786, -71.75213 19.6746847, -71.7527374 19.6750369, -71.7530102 19.6754202, -71.7534695 19.6757649, -71.7539155 19.6759684, -71.7541507 19.6760568, -71.7544567 19.6760624, -71.7550887 19.676025, -71.755505 19.6759695, -71.7557109 19.6759637, -71.7559538 19.6759484, -71.7560848 19.6759241, -71.756425 19.6762891, -71.7565759 19.6766693, -71.7566603 19.6772425, -71.7567418 19.6776637, -71.7567657 19.6780042, -71.7567418 19.6783219, -71.7566714 19.678535, -71.7564954 19.6786866, -71.7560629 19.6787718, -71.7558607 19.6789189, -71.75493 19.6795825, -71.7546497 19.6798041, -71.7543761 19.6800618, -71.7543781 19.6804529, -71.7543859 19.6808282, -71.7543904 19.6815918, -71.7546849 19.6818356, -71.7553174 19.681735, -71.7558839 19.6818799, -71.7561295 19.6820946, -71.7563563 19.6825623, -71.7562978 19.6828287, -71.756029 19.6830208, -71.7557458 19.6829924, -71.7550715 19.6834062, -71.7548725 19.6839516, -71.7551743 19.6842301, -71.7554982 19.6843742, -71.756011 19.6845182, -71.756182 19.6846876, -71.756155 19.6849926, -71.7554172 19.6850095, -71.7548594 19.6849756, -71.7543915 19.6850519, -71.7540856 19.6854077, -71.7541846 19.6859837, -71.7543645 19.6862718, -71.7546884 19.6863311, -71.7551473 19.6862125, -71.7555252 19.6859498, -71.756164 19.6858228, -71.7565149 19.6858228, -71.7570637 19.6858482, -71.7575316 19.686043, -71.7576575 19.6862294, -71.7576035 19.6864412, -71.7573876 19.6866784, -71.7568298 19.6867885, -71.7565509 19.6867885, -71.7562629 19.6869325, -71.7562289 19.6873267, -71.7567124 19.687758, -71.7575904 19.6876023, -71.7579721 19.6876741, -71.7586592 19.6878898, -71.7591936 19.6878059, -71.759499 19.6878898, -71.7597026 19.6881414, -71.7595881 19.6885487, -71.7588246 19.6891118, -71.7574759 19.6899743, -71.756827 19.6901301, -71.7560623 19.6897313, -71.7557456 19.6898828, -71.7557099 19.6904523, -71.7557454 19.6909327, -71.7558232 19.6913227, -71.7555911 19.6916845, -71.7555144 19.6917102, -71.7554437 19.6917526, -71.7553085 19.6918302, -71.7551907 19.6918604, -71.7550407 19.6920892, -71.7550381 19.6922639, -71.7551006 19.6926311, -71.7552701 19.692797, -71.7554879 19.692851, -71.7556849 19.6928028, -71.7558119 19.6926623, -71.7559187 19.6924662, -71.7560359 19.6923257, -71.7562125 19.6922033, -71.7565004 19.692043, -71.756737 19.6919946, -71.7569348 19.6920301, -71.7572026 19.6921487, -71.7574708 19.6923138, -71.7577915 19.692508, -71.7580803 19.6927176, -71.758345 19.6927984, -71.7586701 19.6928479, -71.7589962 19.6929825, -71.7592113 19.693135, -71.7592897 19.6933495, -71.7593315 19.6935304, -71.7592633 19.693731, -71.758884 19.6942074, -71.7588562 19.694444, -71.7588741 19.6946068, -71.7591102 19.694754, -71.7593382 19.6949015, -71.7598334 19.6950223, -71.7602693 19.6947472, -71.7607953 19.6937967, -71.7612572 19.6932431, -71.7616354 19.6931155, -71.7618781 19.6933919, -71.7619628 19.6938437, -71.7616241 19.6952467, -71.760828 19.6958932, -71.7603703 19.6963012, -71.7600725 19.6965671, -71.760016 19.6968651, -71.7600476 19.6970694, -71.7605025 19.6977509, -71.7603201 19.6984514, -71.7599502 19.6987462, -71.759633 19.6989505, -71.7592166 19.6989962, -71.7585025 19.6991742, -71.7582541 19.6999927, -71.7584243 19.7016449, -71.7586772 19.7019452, -71.7736624 19.7312615, -71.7958731 19.7319628, -71.8038176 19.7331793, -71.9138432 19.7964561, -72.0313557 19.9245878, -72.1250044 19.9360405, -72.2507913 19.9336273, -72.4561576 20.0711243, -72.6846277 20.1952505, -72.8425031 20.2181368, -73.0157809 20.1530828, -73.2288485 20.0675075, -73.4971083 19.9662032, -73.6716697 19.8756979, -73.7281455 19.7005746, -73.6639685 19.547033, -73.4804223 19.4151366, -73.2211473 19.4284521, -73.1171806 19.4441872, -72.9439027 19.3715509, -72.9593052 19.1898187, -73.0209151 19.0709784, -73.2827572 19.1061543, -73.3546354 19.0806828, -73.4457667 18.9726888, -73.4460982 18.868908, -73.3435427 18.7637437, -73.4332788 18.6929231, -73.7751306 18.7586861, -74.1351434 18.8193676, -74.4460149 18.7647552, -74.550707 18.7050659, -74.6180091 18.6423188, -74.6575357 18.4225219, -74.6329651 18.3454757, -74.5891653 18.2470905, -74.4759269 18.182146, -74.3808494 18.1364674, -74.2686792 18.1659061, -74.2024454 18.1750412, -74.0635681 18.0135837, -74.0144269 17.9536341, -73.9278957 17.9099291, -73.7719258 17.9099291, -73.690736 17.9658289, -73.6180925 17.9952961, -73.5336978 17.9983442, -73.4760103 18.073513, -73.4941712 18.1628609, -73.352089 18.1171773, -73.1672753 18.1019468, -72.8884524 18.0410115, -72.5038691 18.0724974, -72.2635045 18.0897614, -72.0979201 18.1070238, -72.0199351 18.1009314, -71.9772036 18.0907769, -71.8295092 17.953787, -71.7587643 18.0315043, -71.7587182 18.0317174, -71.7586814 18.0318674, -71.7586143 18.0320151, -71.7585057 18.0322219, -71.7583562 18.0324243, -71.758087 18.0327657, -71.7575172 18.0337278, -71.7573575 18.0341633, -71.7572329 18.0344241, -71.7571095 18.0347812, -71.7569647 18.0349419, -71.7567072 18.0351204, -71.756667 18.0352887, -71.7567379 18.0354309, -71.7568052 18.0355056, -71.7569245 18.0355603, -71.7572678 18.0355973, -71.7574503 18.0356306, -71.7576713 18.0356949, -71.7578632 18.0358447, -71.7579598 18.0360449, -71.7580147 18.0362064, -71.7580361 18.0364575, -71.7580119 18.036624, -71.757949 18.0369101, -71.7578239 18.0371964, -71.7577312 18.0374097, -71.7574657 18.0377169, -71.7573524 18.0378558, -71.7572628 18.0380183, -71.75716 18.0382448, -71.7571051 18.0383804, -71.7570189 18.0387156, -71.7569598 18.0390773, -71.7569303 18.0393087, -71.7569525 18.0395575, -71.7569474 18.0399519, -71.7568729 18.0403159, -71.7568334 18.0407221, -71.7568461 18.0409764, -71.7568128 18.041123, -71.7567081 18.0412707, -71.7564276 18.0415435, -71.7560438 18.0418564, -71.7555137 18.0423239, -71.755173 18.0426951, -71.7551101 18.0429014, -71.7550809 18.0431396, -71.7550296 18.04326, -71.754942 18.0433525, -71.7547238 18.0434177, -71.7545119 18.0433684, -71.7543647 18.0433537, -71.7542509 18.0433811, -71.7537381 18.0436719, -71.7532947 18.0439184, -71.7529508 18.0441437, -71.7527155 18.0442966, -71.7523177 18.044649, -71.7519821 18.0450873, -71.7515772 18.0453856, -71.7509769 18.0457393, -71.7506071 18.0460038, -71.7503688 18.0460989, -71.750113 18.0461387, -71.7498031 18.0461364, -71.7494515 18.0461408, -71.7491697 18.0462699, -71.7487898 18.0465403, -71.748495 18.0469152, -71.7482525 18.0473899, -71.7479386 18.0479291, -71.7478229 18.0482743, -71.7474906 18.0488527, -71.7470052 18.0493847, -71.7464907 18.0497172, -71.7461395 18.050066, -71.7457476 18.0504259, -71.7455237 18.0505789, -71.7452124 18.0509648, -71.744904 18.051434, -71.744727 18.0519543, -71.7445998 18.0522249, -71.7440045 18.0531093, -71.7440135 18.0537404, -71.7438244 18.0543731, -71.7437686 18.0549315, -71.7436795 18.0558364, -71.7436633 18.0563903, -71.7436283 18.0570117, -71.7437405 18.0573577, -71.7437877 18.0576229, -71.7440113 18.058677, -71.7441996 18.0591904, -71.744259 18.0595353, -71.7444188 18.0598056, -71.7445193 18.0602937, -71.74449 18.0606037, -71.7440587 18.0612232, -71.7439272 18.0617386, -71.7439008 18.0628967, -71.7439974 18.0638428, -71.7440752 18.0646518, -71.7438338 18.0651076, -71.743638 18.0654901, -71.7434449 18.0657935, -71.7428736 18.066148, -71.7423479 18.0661097, -71.7416397 18.0658573, -71.7411006 18.0659669, -71.7405561 18.0664616, -71.7401484 18.0668518, -71.7396254 18.0673286, -71.7391775 18.0676933, -71.7384613 18.0684557, -71.7383728 18.0690447, -71.7383996 18.0708093, -71.738456 18.0716584, -71.7386196 18.0721607, -71.7386732 18.0726784, -71.7386142 18.0737544, -71.7385472 18.0740808, -71.738456 18.0745653, -71.7387002 18.0756439, -71.7390006 18.0765211, -71.7391079 18.0770922, -71.7387216 18.0773982, -71.7381423 18.0775614, -71.7368977 18.077949, -71.7363184 18.0779286, -71.7358892 18.0782549, -71.7356746 18.0784997, -71.7355182 18.0788195, -71.7350549 18.0797669, -71.7353099 18.0809067, -71.7358463 18.0816614, -71.736769 18.0827425, -71.737075 18.0831062, -71.7371981 18.0832525, -71.737284 18.0839256, -71.7370371 18.0849862, -71.7372178 18.0854618, -71.736903 18.087133, -71.736844 18.0881835, -71.7372248 18.089239, -71.7383835 18.0895909, -71.7394081 18.0898458, -71.7397032 18.0893716, -71.7400143 18.0893716, -71.740127 18.089647, -71.7398212 18.0904731, -71.7397354 18.0911105, -71.7388985 18.0919875, -71.7381207 18.0925841, -71.7369888 18.0931501, -71.736624 18.0935122, -71.7362592 18.0942872, -71.7356316 18.0947513, -71.7353848 18.0955008, -71.7359535 18.0968623, -71.7375091 18.0975099, -71.7375735 18.098545, -71.737257 18.0997075, -71.7374394 18.1005591, -71.738008 18.1009772, -71.7390756 18.1009721, -71.7401914 18.1005285, -71.7413286 18.100763, -71.7418919 18.1006814, -71.742541 18.100401, -71.7426536 18.0999676, -71.7430399 18.0994526, -71.7434905 18.0990498, -71.7441396 18.0989631, -71.7448155 18.0992741, -71.7456201 18.1000849, -71.7460815 18.1008395, -71.7462156 18.1013443, -71.7459849 18.1020276, -71.745942 18.1028791, -71.7459098 18.1035725, -71.7461727 18.1045158, -71.7464302 18.1051634, -71.7464677 18.1058262, -71.7473689 18.1067032, -71.7480502 18.1076669, -71.7479161 18.1082889, -71.7488334 18.1098593, -71.7488334 18.1103998, -71.7490373 18.1114042, -71.7497347 18.1121945, -71.7497937 18.1128114, -71.7495147 18.1134793, -71.7490963 18.1137393, -71.7490105 18.1141268, -71.747841 18.1141931, -71.7475191 18.1144073, -71.7470632 18.1158807, -71.7469237 18.1180017, -71.746634 18.1186033, -71.7470095 18.119725, -71.7467306 18.1215553, -71.7464194 18.121749, -71.7457167 18.1217337, -71.7455129 18.1219631, -71.7455933 18.1227789, -71.7463282 18.1234365, -71.7466233 18.1240687, -71.748018 18.12543, -71.7481682 18.1259755, -71.7477981 18.1262916, -71.7467413 18.1259449, -71.7454807 18.1255625, -71.7449711 18.1255421, -71.7448155 18.1257664, -71.7448906 18.1270716, -71.7461673 18.1290395, -71.747487 18.1300693, -71.747326 18.1306709, -71.7466608 18.1309971, -71.7451803 18.1309869, -71.7444292 18.1307524, -71.7435495 18.131252, -71.7429165 18.1317211, -71.7434744 18.1328018, -71.7445365 18.1335971, -71.7452554 18.1342803, -71.7455236 18.1351469, -71.7452232 18.1363296, -71.7448906 18.1370739, -71.7452768 18.1377264, -71.7466716 18.1377264, -71.7481522 18.1381241, -71.7502121 18.1387154, -71.7502121 18.1395617, -71.7496542 18.1402142, -71.7486564 18.1402346, -71.7486886 18.1412235, -71.7485169 18.1421004, -71.7476264 18.1418761, -71.7470793 18.1410298, -71.7464034 18.1409483, -71.7461137 18.1419474, -71.7459313 18.1432728, -71.7465643 18.14521, -71.7469398 18.1456891, -71.746972 18.1463722, -71.7467359 18.1477791, -71.7468647 18.1487273, -71.7471141 18.1491886, -71.7478517 18.1492625, -71.748077 18.1494282, -71.7481817 18.1497162, -71.7487476 18.1494486, -71.7490695 18.1489032, -71.749512 18.1481207, -71.7498044 18.1473917, -71.7503435 18.1466016, -71.7507002 18.1453527, -71.7512045 18.1438999, -71.7517141 18.1434309, -71.7526475 18.142814, -71.7532913 18.1423247, -71.7538978 18.1419529, -71.7540479 18.1419202, -71.7542247 18.1419678, -71.7543914 18.1422231, -71.7545469 18.1426106, -71.754627 18.1431046, -71.7545575 18.1436802, -71.754332 18.1442312, -71.7539457 18.1449041, -71.7534576 18.1455005, -71.7533934 18.1457396, -71.7535007 18.1459537, -71.7538993 18.1461751, -71.7547679 18.1464606, -71.7555338 18.1468101, -71.756 18.1472229, -71.7563868 18.1472842, -71.7568993 18.1473032, -71.7572051 18.1471008, -71.7574167 18.1468254, -71.7574436 18.1464992, -71.7573707 18.1459286, -71.7573094 18.1454491, -71.7573416 18.1452655, -71.757574 18.1451518, -71.7578673 18.1451636, -71.7581838 18.1453369, -71.7585325 18.1456912, -71.7586666 18.1461067, -71.7587039 18.146673, -71.7585754 18.1471058, -71.7581846 18.1475164, -71.7575455 18.1482018, -71.7572609 18.1490229, -71.7573739 18.150673, -71.7578629 18.1521464, -71.7580783 18.1527082, -71.7582268 18.1528226, -71.7584676 18.1529019, -71.7587096 18.1529067, -71.7589647 18.1528409, -71.7594498 18.152425, -71.7597154 18.1522466, -71.7599486 18.1521511, -71.76017 18.1521049, -71.760394 18.1521294, -71.7606415 18.1522746, -71.760858 18.152476, -71.7609808 18.1527407, -71.7610506 18.1532451, -71.7610686 18.1538434, -71.7611416 18.1542781, -71.7612797 18.1547564, -71.7615391 18.1551944, -71.7617149 18.1555144, -71.762101 18.1558532, -71.7626248 18.1561597, -71.7639835 18.1563422, -71.764631 18.1565946, -71.7648563 18.1568622, -71.7649095 18.1570305, -71.7648867 18.1572051, -71.7648549 18.1575235, -71.7646793 18.1580855, -71.7645358 18.1586896, -71.7644218 18.1590743, -71.7644178 18.1593483, -71.7644821 18.1595714, -71.7645625 18.1596644, -71.7647168 18.1597179, -71.7649824 18.1597663, -71.7653981 18.1597256, -71.7660714 18.1596058, -71.7666211 18.1595395, -71.7668921 18.1595267, -71.7670968 18.1595613, -71.7672381 18.1596414, -71.7675126 18.1598595, -71.767619 18.1600543, -71.7677727 18.1604839, -71.7679166 18.1607908, -71.7681456 18.161274, -71.7683029 18.1615682, -71.7684272 18.1617964, -71.7685184 18.1620105, -71.7685533 18.1622475, -71.7685014 18.162478, -71.7684406 18.1626808, -71.7684031 18.1629051, -71.7684049 18.1632146, -71.7683941 18.1636172, -71.768387 18.1638072, -71.7684236 18.1639995, -71.7684665 18.16416, -71.7685586 18.1643858, -71.7685989 18.1645132, -71.7685711 18.1647029, -71.7683325 18.1650418, -71.7680347 18.165233, -71.767576 18.165312, -71.7668546 18.1652483, -71.7662707 18.1652268, -71.7659757 18.1652064, -71.7658192 18.1652662, -71.765669 18.1654343, -71.7656555 18.1656076, -71.7657012 18.1657707, -71.7658809 18.1659721, -71.7661115 18.166255, -71.7662027 18.1664079, -71.7663512 18.1665877, -71.7663556 18.1669048, -71.7661679 18.1671087, -71.7659882 18.1673636, -71.7659319 18.1675878, -71.7659292 18.1678172, -71.7660365 18.1680695, -71.7663047 18.168253, -71.7666113 18.1682621, -71.7669135 18.1681995, -71.7672757 18.1680899, -71.7677879 18.1679319, -71.7684236 18.1677764, -71.7689851 18.1677116, -71.7695556 18.1677637, -71.7701241 18.1678605, -71.7704996 18.1679344, -71.7709074 18.1679726, -71.7713151 18.1680058, -71.7718086 18.1679879, -71.7728493 18.1679038, -71.7737478 18.1677076, -71.7742306 18.1675266, -71.7745525 18.1673329, -71.7747751 18.167045, -71.7749574 18.1667173, -71.7750754 18.1663491, -71.7752377 18.1661248, -71.7755072 18.1660012, -71.7757782 18.1659502, -71.7762609 18.1659145, -71.7766176 18.1659553, -71.776843 18.1659272, -71.7770753 18.165876, -71.7771833 18.1658684, -71.7772802 18.165884, -71.7773905 18.1659455, -71.7774729 18.1660257, -71.777536 18.1661468, -71.7775624 18.1662802, -71.7775881 18.1665594, -71.7777048 18.1667544, -71.7779556 18.1670335, -71.7782264 18.1672539, -71.7784867 18.1674221, -71.7788581 18.1675177, -71.7791881 18.1676706, -71.7795394 18.1678299, -71.7798291 18.1679853, -71.7801603 18.1682198, -71.7804192 18.1684275, -71.7807037 18.1687323, -71.7810348 18.1690365, -71.7812976 18.1691742, -71.7818395 18.1693564, -71.7821023 18.1693449, -71.7824739 18.1692075, -71.7828586 18.1690213, -71.7830479 18.1689552, -71.7832476 18.168927, -71.7834127 18.1689502, -71.7835522 18.1690827, -71.7836675 18.1693961, -71.7836863 18.169707, -71.7837143 18.1700738, -71.7837077 18.1702448, -71.7836419 18.1704153, -71.7834958 18.1706219, -71.7834032 18.1708434, -71.7833403 18.1711189, -71.7833227 18.1713913, -71.7831993 18.1718679, -71.783182 18.1720898, -71.7832422 18.1722884, -71.7834246 18.1725024, -71.7836285 18.1726477, -71.7839128 18.172821, -71.7842615 18.1730172, -71.784865 18.1732976, -71.7853504 18.1735193, -71.7858198 18.1738276, -71.7860413 18.1740598, -71.7862275 18.1742863, -71.7863229 18.1744955, -71.7863589 18.1747018, -71.7863283 18.1750715, -71.7863531 18.1754563, -71.7863242 18.1758188, -71.7863376 18.1759443, -71.7863678 18.1760711, -71.786396 18.1761953, -71.7864271 18.1762998, -71.7864711 18.1763826, -71.7865723 18.1764495, -71.7869633 18.1764839, -71.7872871 18.1766183, -71.7873904 18.1766336, -71.7874816 18.1766107, -71.7875835 18.1765438, -71.787664 18.1765444, -71.7877471 18.1765878, -71.787833 18.1766935, -71.7879054 18.1769063, -71.7878987 18.1773969, -71.7879242 18.1778976, -71.7879805 18.178048, -71.7879805 18.1782238, -71.7878974 18.1785449, -71.7878142 18.178866, -71.7877659 18.1791871, -71.7877284 18.1794037, -71.7877471 18.1796891, -71.7878222 18.1799847, -71.7880073 18.1802727, -71.7880636 18.1804485, -71.7880658 18.1805856, -71.7879161 18.1809174, -71.7877364 18.1811722, -71.7876868 18.1812958, -71.7876801 18.1814245, -71.7876989 18.1815723, -71.7877525 18.1817354, -71.7877552 18.181915, -71.7877015 18.1820603, -71.7875812 18.1824352, -71.7874172 18.1827776, -71.7872625 18.1833612, -71.7871643 18.1837218, -71.7871367 18.1839137, -71.7870627 18.1849961, -71.7865082 18.1862613, -71.786467 18.1866556, -71.7859631 18.187388, -71.7849148 18.1879547, -71.7845661 18.188157, -71.7842265 18.188551, -71.7840913 18.189308, -71.7838282 18.1897459, -71.7827305 18.1903874, -71.7823747 18.1906007, -71.7821574 18.1908555, -71.7819215 18.1913906, -71.781549 18.1923475, -71.7812347 18.192866, -71.7807021 18.1932499, -71.78013 18.1933722, -71.7791694 18.1934126, -71.777782 18.1935692, -71.7771202 18.1937476, -71.7765355 18.1939921, -71.7759401 18.1943541, -71.7754885 18.1947635, -71.7747802 18.1956017, -71.7744649 18.1961072, -71.7742318 18.1967032, -71.7740406 18.1972709, -71.7737793 18.1975832, -71.7735542 18.1976936, -71.7731793 18.1978266, -71.7723529 18.1979205, -71.7721045 18.1980997, -71.7719677 18.1982603, -71.7719142 18.1984158, -71.7719087 18.198566, -71.7719442 18.1987156, -71.7722526 18.1992145, -71.7725313 18.199663, -71.7729011 18.2003809, -71.7730634 18.2005689, -71.7735461 18.2008664, -71.7736682 18.2010504, -71.7737041 18.2012592, -71.7737031 18.2016211, -71.7735635 18.20197, -71.773172 18.2023944, -71.7728645 18.2027037, -71.7728051 18.2030489, -71.7729221 18.2034628, -71.7733115 18.2038379, -71.7735046 18.2040544, -71.7735924 18.2043891, -71.7735019 18.204722, -71.7732908 18.2050772, -71.7723435 18.2066173, -71.7717637 18.2074791, -71.7711083 18.2083823, -71.7709109 18.2087452, -71.770817 18.2091274, -71.7707768 18.2094178, -71.770691 18.2096981, -71.7706212 18.2098892, -71.7705756 18.2101898, -71.7706695 18.2104472, -71.7706856 18.2106637, -71.7706334 18.2108539, -71.769978 18.2118261, -71.7697099 18.2124679, -71.7694974 18.2130001, -71.7694615 18.2133923, -71.769535 18.2138256, -71.7694932 18.2142392, -71.769196 18.2148579, -71.7690091 18.2154301, -71.7691715 18.2160254, -71.7687893 18.2173874, -71.7688242 18.2176218, -71.7687142 18.2178664, -71.7689073 18.2183556, -71.7688912 18.218551, -71.768682 18.2189339, -71.7686552 18.219083, -71.7686579 18.2192524, -71.7686847 18.2195008, -71.7686364 18.2196594, -71.768446 18.2198575, -71.768315 18.2200959, -71.7681112 18.220442, -71.7676064 18.2208829, -71.7671722 18.221115, -71.7663018 18.2212972, -71.7654123 18.2212437, -71.764532 18.2211121, -71.7641464 18.2211606, -71.7638715 18.2212562, -71.7635507 18.2214692, -71.7633095 18.2215453, -71.7631164 18.2216523, -71.7630199 18.2218256, -71.7630547 18.2221721, -71.7630547 18.2230841, -71.7625988 18.2239809, -71.7624271 18.2243937, -71.7621802 18.2251372, -71.7619658 18.2253006, -71.7615688 18.2254586, -71.7608124 18.2253567, -71.7602009 18.2253974, -71.7599099 18.2255302, -71.7595044 18.2259841, -71.7593318 18.2264878, -71.759041 18.2267719, -71.7584842 18.22687, -71.7578727 18.2267681, -71.757642 18.2268204, -71.7574328 18.2269821, -71.7574407 18.2273085, -71.7574865 18.2276139, -71.7574221 18.2281794, -71.7573845 18.2287807, -71.7575508 18.2295195, -71.7576313 18.2302124, -71.7575562 18.2308188, -71.7573738 18.2315219, -71.7575168 18.2322088, -71.757803 18.2327039, -71.7581785 18.2329281, -71.7584574 18.2332491, -71.7586596 18.2335897, -71.7587525 18.2339522, -71.7586505 18.2343904, -71.7585111 18.234742, -71.7585594 18.2349814, -71.7586952 18.2352517, -71.7587605 18.2354807, -71.7587471 18.2357304, -71.7586345 18.2360972, -71.7585647 18.2364997, -71.7583072 18.2367902, -71.7581302 18.2370908, -71.7580229 18.2375748, -71.7582273 18.2383935, -71.7580658 18.2388485, -71.7578405 18.2392052, -71.7573829 18.2397695, -71.7570466 18.2398624, -71.7568099 18.2399477, -71.7565799 18.2400815, -71.7563318 18.240326, -71.7562312 18.2404661, -71.7561306 18.2406292, -71.7561065 18.2407999, -71.7561279 18.241047, -71.7561024 18.2412278, -71.7559093 18.2417118, -71.7558771 18.2420124, -71.7549265 18.2433466, -71.7307712 18.2826443, -71.7259467 18.290529, -71.724932 18.2921845, -71.7098421 18.3166392, -71.7002963 18.3321079, -71.6989707 18.3347206, -71.6960202 18.3406367, -71.7088588 18.3455194, -71.795323 18.3788104, -71.8056384 18.3895637, -71.8033353 18.3924227, -71.8173053 18.3978568, -71.8298162 18.3988604, -71.8367658 18.4175181, -71.8440231 18.4292391, -71.8507123 18.4327354, -71.8656267 18.4385343, -71.8762192 18.4459131, -71.8806772 18.4473317, -71.9045459 18.4571839, -71.9137207 18.4852653, -71.8807337 18.4902312, -71.8812582 18.4973083, -71.8808014 18.5037193, -71.9536133 18.5774642, -71.9551275 18.5790867, -71.9651288 18.603425, -71.9674918 18.6052597, -71.9693188 18.6047474, -71.9755572 18.6053624, -71.9780287 18.6074966, -71.9805086 18.6107594, -71.9819011 18.6112693, -71.9820121 18.6124898, -71.9848042 18.6152431, -71.9851336 18.6169727, -71.9863115 18.6189055, -71.9876048 18.6186012, -71.9898585 18.6186012, -71.9905108 18.6175825, -71.9958871 18.6171809, -71.9983614 18.6180962, -71.9995894 18.6177275, -72.0003985 18.6174876, -72.0029869 18.6205443, -72.0054559 18.6217664, -72.0082306 18.6250156, -72.0079557 18.625571, -72.0038507 18.6279884, -72.0008373 18.6275611, -71.998694 18.6293506, -71.9978014 18.6311058, -71.9958508 18.6310927, -71.9937674 18.6321699, -71.9921015 18.6321162, -71.9891411 18.6340549, -71.9823693 18.6362304, -71.9793642 18.6390802, -71.975652 18.6476151, -71.9663612 18.6566647, -71.963774 18.6546278, -71.9624301 18.6535046, -71.9552821 18.6506985, -71.9531852 18.6519735, -71.9464513 18.648271, -71.9445725 18.6479426, -71.9411309 18.6439233, -71.9316149 18.6375029, -71.9276399 18.6352623, -71.9256975 18.6347972, -71.9215067 18.637184, -71.916617 18.6447051, -71.9146536 18.6464423, -71.9143002 18.6476552, -71.9133366 18.6485692, -71.9117199 18.6484163, -71.9104338 18.6483139, -71.9082807 18.6490187, -71.9065609 18.6488098, -71.9057282 18.6482454, -71.904675 18.6483545, -71.9035277 18.6473877, -71.9021518 18.6475365, -71.8971499 18.6439693, -71.8956971 18.6436654, -71.8842455 18.6365247, -71.8830273 18.6337422, -71.8823088 18.6331615, -71.8808022 18.628027, -71.8805349 18.6271108, -71.8798919 18.627064, -71.8760754 18.62812, -71.8733805 18.6279127, -71.8693008 18.6355812, -71.8693023 18.6370527, -71.8685448 18.6375636, -71.8651755 18.6364332, -71.8603694 18.6367051, -71.856068 18.635288, -71.8541893 18.6360058, -71.8523 18.6357576, -71.8506333 18.6347366, -71.8443314 18.6293592, -71.8361643 18.629619, -71.8275655 18.6332474, -71.8153522 18.6306125, -71.8121345 18.6340785, -71.80713 18.6353476, -71.8069303 18.6450119, -71.8089308 18.651473, -71.8095417 18.6613317, -71.8029898 18.6698318, -71.8016109 18.6807218, -71.8041962 18.6852883, -71.7976899 18.6917939, -71.7948059 18.6910224, -71.7940075 18.6891583, -71.7888867 18.6894768, -71.785856 18.6919704, -71.7852303 18.7025299, -71.7837985 18.7061069, -71.7751742 18.7103185, -71.7730989 18.7098585, -71.7495812 18.7181081, -71.7379549 18.7221933, -71.7287578 18.7722367, -71.7266904 18.7820527, -71.7263339 18.7837454, -71.7220164 18.8042426, -71.7220126 18.807862, -71.7222537 18.825729, -71.7208094 18.8327439, -71.725526 18.8393077, -71.7260779 18.8416462, -71.725358 18.8450364, -71.7248838 18.8469957, -71.7257574 18.8488796, -71.7252781 18.8506132, -71.7263967 18.8515986, -71.7265612 18.854842, -71.7258591 18.8563369, -71.7263992 18.8573302, -71.7256754 18.8589603, -71.7258312 18.8636295, -71.7251653 18.8754511, -71.7222981 18.8784483, -71.7258288 18.8789331, -71.7273133 18.8771732, -71.7346813 18.8783461, -71.7374525 18.8780807, -71.7365937 18.8850935, -71.7391372 18.8913483, -71.7435337 18.893005, -71.7479324 18.8969994, -71.7603184 18.9058181, -71.7681602 18.9068755, -71.7720596 18.9191471, -71.7731187 18.9233718, -71.7743395 18.9247413, -71.7762835 18.9298447, -71.7787542 18.937524, -71.7816744 18.9430558, -71.7828834 18.948256, -71.7827586 18.9493957, -71.7827565 18.9497892, -71.782772 18.949921, -71.7827471 18.9500835, -71.782546 18.9508594, -71.7824202 18.9512725, -71.7820494 18.9517689, -71.7817948 18.952197, -71.7817005 18.9525745, -71.7817665 18.9530025, -71.7817382 18.9532938, -71.7814648 18.9539686, -71.7812134 18.954462, -71.7812291 18.9546106, -71.7813045 18.9547206, -71.7815182 18.9547979, -71.7817508 18.9548217, -71.7820462 18.9547325, -71.7821751 18.9547652, -71.7826245 18.9550684, -71.7830394 18.9552527, -71.7835014 18.9554072, -71.784526 18.955431, -71.7855411 18.9554697, -71.7862263 18.9555588, -71.7867731 18.9556777, -71.7870371 18.9557996, -71.7874551 18.9558739, -71.7880648 18.9559869, -71.7886934 18.9562484, -71.7892686 18.9566497, -71.7896897 18.9571134, -71.7900008 18.9575117, -71.79029 18.9577317, -71.7904974 18.9578327, -71.7909091 18.9578119, -71.791434 18.9576455, -71.7918834 18.957372, -71.7923611 18.9569262, -71.7928671 18.9563971, -71.7932412 18.9558977, -71.7935586 18.9550743, -71.7936372 18.9547503, -71.793788 18.9536416, -71.7941306 18.952949, -71.7943977 18.9528063, -71.7947686 18.9527231, -71.795284 18.9528509, -71.7957366 18.953172, -71.7959597 18.9535019, -71.7961735 18.953817, -71.7962646 18.9542094, -71.7962835 18.954679, -71.7962489 18.9555856, -71.7962583 18.9560909, -71.7961892 18.9563882, -71.7962457 18.9565962, -71.79645 18.9567775, -71.7967706 18.9568697, -71.7975029 18.956944, -71.7979492 18.956831, -71.7983892 18.9567775, -71.799354 18.9564387, -71.7996086 18.9560196, -71.8002372 18.9556272, -71.8007778 18.9552794, -71.8012021 18.9549703, -71.8013969 18.9547593, -71.8017332 18.9545542, -71.8023146 18.9544561, -71.8026635 18.9544739, -71.8028803 18.9545007, -71.8031726 18.9546701, -71.8034649 18.9550357, -71.8034618 18.9552646, -71.8034523 18.9557788, -71.8034209 18.956076, -71.8033675 18.9563971, -71.8032261 18.9566408, -71.8031946 18.9568459, -71.8031475 18.9572769, -71.8031632 18.957589, -71.8032355 18.957702, -71.8035215 18.9579189, -71.8038484 18.9582697, -71.8041878 18.9585848, -71.8046152 18.9589504, -71.8048792 18.9590276, -71.8050741 18.9590247, -71.8053066 18.9589355, -71.8054826 18.9587304, -71.8057718 18.9586888, -71.8061206 18.9587007, -71.8062589 18.9587601, -71.8067209 18.9591138, -71.8068749 18.9592327, -71.8078052 18.9596162, -71.8081352 18.959747, -71.8084527 18.9598213, -71.8090561 18.9599491, -71.8097475 18.9600056, -71.8103384 18.9600115, -71.8108161 18.9599312, -71.8114227 18.9599015, -71.8117558 18.9598094, -71.8120355 18.9598242, -71.8122335 18.9599431, -71.8124378 18.9601393, -71.8125761 18.960386, -71.8126735 18.9606654, -71.812617 18.9610667, -71.8126201 18.9615244, -71.8127238 18.961786, -71.8127741 18.9621902, -71.8127207 18.962856, -71.8125981 18.9633821, -71.8126641 18.9636229, -71.8129973 18.9639201, -71.8133084 18.9642649, -71.8138333 18.9644611, -71.8142544 18.9646037, -71.8148138 18.9647048, -71.8151501 18.9647256, -71.8155147 18.9647137, -71.8160144 18.9644343, -71.8163978 18.9641549, -71.8169824 18.9636437, -71.817545 18.9627223, -71.8176707 18.9625974, -71.8184219 18.9626182, -71.8186481 18.9626569, -71.819063 18.9629065, -71.8194967 18.9630254, -71.8201724 18.9632127, -71.820735 18.9632305, -71.8217313 18.9632513, -71.822341 18.9632513, -71.823243 18.9634059, -71.824145 18.9635396, -71.8248962 18.963614, -71.8256096 18.9637566, -71.8259679 18.9639944, -71.826191 18.9642887, -71.8262445 18.9644848, -71.8262633 18.9647196, -71.8261973 18.9649812, -71.8258233 18.9652071, -71.825443 18.9653141, -71.8249339 18.9655846, -71.8245127 18.9657391, -71.8242362 18.9657956, -71.8237113 18.9658075, -71.8235385 18.9659115, -71.8232462 18.9662504, -71.8230419 18.9665268, -71.822737 18.9668032, -71.822165 18.9672342, -71.8219042 18.9675492, -71.8218413 18.967897, -71.8218162 18.9683666, -71.8218727 18.968646, -71.821989 18.9688362, -71.8221493 18.9689194, -71.8222782 18.9688778, -71.8225139 18.9688005, -71.822803 18.9687381, -71.8232996 18.9686846, -71.8237679 18.9685657, -71.8240947 18.9683785, -71.8244562 18.9681021, -71.8246636 18.9680248, -71.8248302 18.9681199, -71.8251225 18.9683874, -71.8253927 18.9685271, -71.8260873 18.968539, -71.8264582 18.9685271, -71.8267128 18.968429, -71.826917 18.9682655, -71.8272596 18.9678583, -71.8276148 18.9673174, -71.827885 18.9668626, -71.8283156 18.9662771, -71.8286645 18.9659888, -71.8293433 18.9653527, -71.8301982 18.9646602, -71.8308645 18.9643214, -71.8314396 18.9635753, -71.8318388 18.9629303, -71.8320431 18.9627877, -71.8323259 18.9627579, -71.8325459 18.962859, -71.8329199 18.963076, -71.8332436 18.9633464, -71.8335485 18.9637418, -71.8340514 18.9642857, -71.8343217 18.9648148, -71.8345071 18.9654746, -71.8346328 18.9658194, -71.8346988 18.9662949, -71.8346517 18.9666338, -71.8346925 18.9676443, -71.8347491 18.9678435, -71.8347365 18.9683398, -71.8348622 18.9687976, -71.8351262 18.9697338, -71.8352834 18.9700221, -71.8355159 18.9702064, -71.8359654 18.9703312, -71.8365122 18.9703342, -71.8369428 18.9701707, -71.8373231 18.969921, -71.8377537 18.9696803, -71.838354 18.9695495, -71.8387122 18.9694455, -71.8390045 18.9694128, -71.8392308 18.9692285, -71.8397934 18.9685687, -71.8399788 18.9680307, -71.8401014 18.9677008, -71.840004 18.9674422, -71.8399128 18.9667973, -71.839938 18.9664911, -71.840114 18.9663098, -71.8404 18.9661731, -71.8406074 18.966179, -71.8408337 18.9662831, -71.8410474 18.9663306, -71.8417797 18.9663603, -71.8423077 18.9663395, -71.8425906 18.9663455, -71.8427351 18.9662771, -71.8428483 18.9660156, -71.8429426 18.9649426, -71.8430243 18.964687, -71.843238 18.9642946, -71.8432883 18.9639617, -71.8435051 18.9637447, -71.8437974 18.9635456, -71.8440646 18.9629184, -71.8440677 18.9624399, -71.8438634 18.9620238, -71.843568 18.9617355, -71.8430683 18.9615363, -71.8423674 18.9613639, -71.8416948 18.9611618, -71.8414528 18.960921, -71.8410034 18.960389, -71.8402523 18.9595419, -71.8397148 18.9590693, -71.8395891 18.9588255, -71.8396394 18.9584986, -71.8397588 18.9583826, -71.8400731 18.9583916, -71.8404911 18.9583499, -71.840664 18.9583172, -71.8410851 18.9583262, -71.8415157 18.9580943, -71.841698 18.9579071, -71.8417483 18.9576217, -71.8418991 18.9573155, -71.8425466 18.9568727, -71.8428388 18.9567181, -71.8431028 18.9566349, -71.8434108 18.9566676, -71.8438194 18.9568281, -71.84436 18.9572085, -71.8447686 18.9575028, -71.8452117 18.9580378, -71.845526 18.9586977, -71.8457617 18.9590276, -71.8459377 18.9595121, -71.8460634 18.9601631, -71.8461703 18.9605941, -71.8463526 18.9610607, -71.8463243 18.9614471, -71.8461357 18.9620624, -71.8461263 18.9623388, -71.8462929 18.9626777, -71.8464311 18.9628471, -71.846802 18.962963, -71.847088 18.9630314, -71.8477574 18.9630789, -71.8480089 18.9631384, -71.8485243 18.9633197, -71.849024 18.9633821, -71.8492786 18.963287, -71.8494326 18.9631503, -71.8494923 18.9628976, -71.8494703 18.9624013, -71.8494232 18.9620802, -71.8492189 18.9616225, -71.8488449 18.9610161, -71.848782 18.9607665, -71.8488229 18.960597, -71.8490334 18.9603563, -71.8492534 18.9601661, -71.8496091 18.9599873, -71.8498807 18.9599143, -71.8502415 18.9596549, -71.8506532 18.9591525, -71.8507507 18.9587661, -71.8511435 18.9581538, -71.8517124 18.9574434, -71.8523661 18.9564417, -71.8527558 18.955764, -71.8530764 18.95544, -71.8538275 18.9549406, -71.8542424 18.9547415, -71.8545347 18.954688, -71.8548427 18.9547415, -71.8550375 18.954899, -71.8551978 18.9551636, -71.8552984 18.9555143, -71.8553864 18.9556837, -71.8553393 18.9559423, -71.8552827 18.9562544, -71.855135 18.9566171, -71.8549275 18.9570748, -71.8547453 18.9573215, -71.8545818 18.957598, -71.8545693 18.957812, -71.8547547 18.95824, -71.8551318 18.9585075, -71.8554681 18.9589177, -71.8559238 18.9594319, -71.8562507 18.9599016, -71.8566027 18.9603118, -71.8574513 18.9612153, -71.8578316 18.9618871, -71.8580201 18.9622735, -71.858171 18.962651, -71.8585481 18.9630612, -71.8587053 18.9632335, -71.8590258 18.9632871, -71.8593244 18.9632662, -71.8596041 18.963296, -71.8600284 18.9633257, -71.8605313 18.9632722, -71.8608613 18.963189, -71.8610121 18.9630939, -71.8610467 18.9628769, -71.8609996 18.9626629, -71.8607796 18.9624578, -71.8606947 18.9623121, -71.8607261 18.9621308, -71.8607104 18.9619673, -71.8605564 18.9617563, -71.8604559 18.9613015, -71.8603899 18.9610311, -71.8604056 18.9606476, -71.8604087 18.9603771, -71.860063 18.9596876, -71.8600944 18.9595003, -71.8602579 18.9593755, -71.8604307 18.9592744, -71.8606193 18.9593368, -71.8609053 18.9593517, -71.8612981 18.9590931, -71.8617916 18.9587394, -71.8620304 18.9584689, -71.8621656 18.9580498, -71.8621782 18.9577823, -71.8620116 18.9575772, -71.8617821 18.9574345, -71.8617727 18.9572205, -71.8617004 18.9567924, -71.8618576 18.9563525, -71.8620524 18.9559631, -71.8622159 18.9557016, -71.8622944 18.9552498, -71.8622002 18.9548931, -71.8620807 18.9545988, -71.8618293 18.9544918, -71.8616219 18.9541945, -71.8615024 18.9538913, -71.8614019 18.9536357, -71.8610121 18.9533206, -71.8606759 18.9530353, -71.8605847 18.9527202, -71.860547 18.9525032, -71.8603647 18.9520752, -71.8603804 18.9517036, -71.8604653 18.9512072, -71.8604716 18.9504403, -71.8603836 18.9496852, -71.8602767 18.9490699, -71.8601039 18.9484189, -71.8601636 18.948193, -71.8603364 18.9480414, -71.8605564 18.9478987, -71.8608927 18.9477798, -71.8612259 18.9476996, -71.861471 18.9477174, -71.8617947 18.9478898, -71.8620587 18.9480414, -71.8624516 18.9481128, -71.8628602 18.948199, -71.8632027 18.9481663, -71.8634479 18.9480028, -71.8638219 18.9478304, -71.8640544 18.9474647, -71.8642493 18.9473488, -71.864595 18.947224, -71.8655976 18.9467513, -71.8664996 18.9462014, -71.8670465 18.9459903, -71.8673325 18.9459844, -71.8676122 18.9460409, -71.8679673 18.9461598, -71.8684262 18.9462727, -71.8689039 18.9462608, -71.8691176 18.9463797, -71.8692528 18.9465908, -71.8692685 18.9468791, -71.8694068 18.9470932, -71.8695985 18.9474202, -71.869853 18.9475509, -71.8706859 18.9476936, -71.8713491 18.9477888, -71.8721276 18.9477314, -71.8727864 18.9478836, -71.8731308 18.9480072, -71.8733646 18.9481904, -71.8734384 18.9483226, -71.873733 18.9484527, -71.8743812 18.9487546, -71.8749754 18.9488103, -71.8754862 18.948801, -71.8759526 18.9486605, -71.8763554 18.9484337, -71.8764226 18.948089, -71.8764418 18.9477443, -71.8763554 18.9473542, -71.8761061 18.9471637, -71.8758183 18.947073, -71.8753579 18.9469369, -71.8752237 18.9467283, -71.8752333 18.9464561, -71.8753004 18.9461749, -71.8756169 18.9457848, -71.8760869 18.9453766, -71.8763075 18.9452768, -71.8766432 18.9453585, -71.8768638 18.9453585, -71.8776023 18.944796, -71.8783021 18.943899, -71.8783835 18.9434333, -71.8779986 18.9426594, -71.8781349 18.9424529, -71.8787336 18.9422595, -71.8797385 18.9417046, -71.8827586 18.9421039, -71.8833821 18.9444251, -71.8841239 18.9467376, -71.8842394 18.9482089, -71.8839602 18.9492559, -71.883279 18.950341, -71.8831234 18.950767, -71.8831231 18.9513012, -71.8849892 18.9533972, -71.885437 18.9545481, -71.8857439 18.9560463, -71.8857258 18.9568248, -71.8859351 18.9574496, -71.8865583 18.958578, -71.8864571 18.9592834, -71.8856067 18.9598916, -71.8848003 18.9600945, -71.884264 18.9599782, -71.8831873 18.9590379, -71.8821573 18.9579014, -71.8810238 18.9568294, -71.8795499 18.9566943, -71.8781114 18.9568285, -71.8761537 18.9572633, -71.8754292 18.9576071, -71.8748823 18.9582733, -71.8743935 18.9613175, -71.8736944 18.9636644, -71.8736232 18.9643074, -71.8738964 18.9650854, -71.8759129 18.9675263, -71.8765232 18.9695895, -71.8764944 18.9706286, -71.8761779 18.9710993, -71.8755248 18.9713813, -71.8746492 18.9715664, -71.8729551 18.9725403, -71.8682977 18.9745629, -71.866889 18.9756266, -71.8668133 18.9763974, -71.8677287 18.9783584, -71.8683594 18.9790593, -71.8686426 18.9801332, -71.8684666 18.9809284, -71.8681351 18.9813109, -71.867915 18.9815166, -71.8677843 18.9816417, -71.8668873 18.9821349, -71.8647663 18.9828495, -71.8640654 18.983454, -71.8637035 18.9836619, -71.8633162 18.9835568, -71.8630581 18.9833273, -71.8628528 18.9828937, -71.8627816 18.9824649, -71.8628762 18.9818471, -71.8633384 18.979556, -71.8632108 18.9788331, -71.8627677 18.9782868, -71.8621389 18.9777868, -71.860809 18.9770528, -71.8585685 18.9765067, -71.8574664 18.9766549, -71.8563785 18.9771263, -71.8555901 18.9778748, -71.8552818 18.9783352, -71.8553291 18.9788501, -71.8563569 18.9811588, -71.8563079 18.9818908, -71.8558545 18.9826936, -71.8522084 18.9862803, -71.8512816 18.9868821, -71.8511735 18.9872707, -71.8513263 18.9887556, -71.8517819 18.9893422, -71.8522759 18.9903734, -71.8523546 18.9916764, -71.8522836 18.9923006, -71.8520665 18.9925826, -71.8516766 18.9927956, -71.8509627 18.9927934, -71.8489098 18.9922498, -71.8480089 18.9922041, -71.846657 18.991781, -71.8462161 18.991461, -71.8446699 18.9911645, -71.8442909 18.9912481, -71.8439592 18.9916149, -71.8436925 18.9920651, -71.8433119 18.9934776, -71.8430559 18.9964289, -71.8426086 18.9978703, -71.8419184 18.9991533, -71.841373 18.9997366, -71.8405347 19.0001295, -71.8393783 19.000217, -71.8384008 18.9998979, -71.8371394 18.9999535, -71.8367198 19.0004795, -71.8361866 19.0006544, -71.8357187 19.0007088, -71.8350569 19.0005443, -71.8346041 19.0006545, -71.834311 19.0010552, -71.8341247 19.0028304, -71.8338276 19.0039345, -71.8338216 19.005075, -71.833459 19.0054866, -71.8328613 19.0055238, -71.8317831 19.0052832, -71.8302137 19.0044198, -71.8299304 19.0036876, -71.8298841 19.0008914, -71.8293963 18.9988802, -71.8283933 18.9965207, -71.8285037 18.9950447, -71.8281083 18.9941376, -71.8274669 18.9937214, -71.8267544 18.99373, -71.8239198 18.9940389, -71.8204823 18.9960555, -71.8191314 18.996683, -71.8181619 18.9967633, -71.8143287 18.9960239, -71.8095659 18.9980453, -71.8069382 18.998384, -71.8048252 18.9988598, -71.8045763 18.998956, -71.8044295 18.9992673, -71.8053382 19.0020158, -71.8052567 19.0027912, -71.8049128 19.0034941, -71.8040314 19.0043128, -71.8012051 19.0051309, -71.7987757 19.0052137, -71.7966579 19.0040487, -71.7962124 19.0027058, -71.7961002 19.000202, -71.7965877 18.99876, -71.7974812 18.9978407, -71.7983716 18.9965396, -71.7983144 18.9961154, -71.7980566 18.995752, -71.7963796 18.9948405, -71.7953422 18.9946865, -71.7942305 18.9946992, -71.7938939 18.9943783, -71.7935539 18.9939026, -71.79265 18.9933817, -71.7910095 18.9929743, -71.7890564 18.9928896, -71.7884108 18.9930737, -71.7875643 18.9942907, -71.7865192 18.9968259, -71.7872679 18.9985739, -71.7887257 18.9996918, -71.7901736 19.0003251, -71.7910708 19.0008846, -71.7913786 19.0018565, -71.7908772 19.0071335, -71.7904983 19.0080885, -71.7900147 19.0085821, -71.78642 19.0105976, -71.7855251 19.0121109, -71.7849028 19.012312, -71.7844514 19.0122629, -71.782582 19.0108913, -71.7813816 19.0106859, -71.7796111 19.0108425, -71.7774293 19.0114292, -71.7762153 19.012183, -71.7757282 19.0126236, -71.7756107 19.013312, -71.7757996 19.0142354, -71.7769838 19.0158447, -71.7780199 19.0161596, -71.7809063 19.0160896, -71.7819424 19.0165794, -71.7823125 19.017524, -71.7820164 19.0185386, -71.7806235 19.0198825, -71.779683 19.0210256, -71.7783395 19.022029, -71.7772378 19.023261, -71.7753032 19.0253694, -71.7735029 19.0279732, -71.7724818 19.028786, -71.7721191 19.029637, -71.7718504 19.0303482, -71.7714239 19.0309595, -71.7709217 19.0313171, -71.7702063 19.0315967, -71.769374 19.0318243, -71.7687343 19.0318764, -71.7677438 19.0316683, -71.767331 19.0312001, -71.7667257 19.02988, -71.7659897 19.0278901, -71.765632 19.0271358, -71.7651289 19.0263291, -71.7643034 19.0260527, -71.7631598 19.0261909, -71.7618185 19.0265486, -71.7611908 19.0271013, -71.7604599 19.0284344, -71.7600128 19.0296375, -71.7601762 19.0303121, -71.7606405 19.0309218, -71.7616895 19.0319703, -71.7620678 19.0325881, -71.7627385 19.0331489, -71.7630223 19.0336123, -71.7632028 19.0343519, -71.7632458 19.0349209, -71.7631032 19.0352712, -71.7625854 19.035734, -71.7619761 19.0361535, -71.7612788 19.0369525, -71.7604441 19.037898, -71.7596658 19.0385506, -71.7581901 19.0395161, -71.7572497 19.0402452, -71.7566052 19.0405248, -71.7556508 19.0407146, -71.754883 19.0408877, -71.7545555 19.0411307, -71.7542631 19.04168, -71.7535024 19.0439173, -71.7530023 19.0448294, -71.7521711 19.0460712, -71.7517908 19.0464807, -71.7511568 19.0467903, -71.7504841 19.0470633, -71.7497445 19.0471066, -71.7492022 19.0469801, -71.7488169 19.046967, -71.7481646 19.0469887, -71.7447643 19.0457838, -71.7422849 19.0435101, -71.7412817 19.0432576, -71.740177 19.0434686, -71.7394859 19.044126, -71.7387692 19.0445261, -71.7380288 19.0449533, -71.7373113 19.0466849, -71.7371838 19.0473381, -71.7374082 19.0482287, -71.7377952 19.0484054, -71.7379879 19.0487955, -71.7378709 19.0491076, -71.7372633 19.0496966))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/USA_HTI_overlapping.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n296777074000000 && 18.3520806,-75.2288309:18.3664654,-75.2336999:18.398702,-75.2384618:18.4312495,-75.2377069:18.4610773,-75.2322562:18.4886399,-75.222672:18.4999677,-75.2172678:18.5289778,-75.1993364:18.5547196,-75.1765697:18.5688721,-75.1596528:18.5767252,-75.1492275:18.5933921,-75.1198508:18.6053034,-75.0880291:18.6121442,-75.0546028:18.6137166,-75.0199034:18.6098363,-74.9832495:18.6044011,-74.952276:18.5946192,-74.9225014:18.5801641,-74.893673:18.5623586,-74.8681576:18.5411084,-74.8457299:18.5159259,-74.825893:18.4854595,-74.8084096:18.4525742,-74.7966939:18.418,-74.7907319:18.3896744,-74.790449:18.3693394,-74.7920482:18.320668,-74.804815:18.285888,-74.8230994:18.2551258,-74.848054:18.2283852,-74.8801877:18.2093121,-74.9134925:18.1964118,-74.9498941:18.1892548,-74.9941897:18.1907252,-75.0374985:18.201251,-75.0814085:18.2195619,-75.1219061:18.2370322,-75.1482084:18.2582151,-75.1721527:18.2826809,-75.1923834:18.3013364,-75.2052088:18.3256833,-75.2192144 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1486154603000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Île de la Navasse / Navassa Island (Disputed area USA-HAITI) || maritime -> yes || source -> 12 nm buffer around coastline || last_edit_version -> 2 || border_type -> territorial\n# Lines\n537853867000000 && 18.2921845,-71.724932:18.290529,-71.7259467 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672082000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || source -> US Census Bureau, boundary import, 16/01/2010 || last_edit_version -> 1 || border_type -> nation\n48435492000000 && 18.290529,-71.7259467:18.2826443,-71.7307712:18.2433466,-71.7549265:18.2420124,-71.7558771:18.2417118,-71.7559093:18.2412278,-71.7561024:18.241047,-71.7561279:18.2407999,-71.7561065:18.2406292,-71.7561306:18.2404661,-71.7562312:18.240326,-71.7563318:18.2400815,-71.7565799:18.2399477,-71.7568099:18.2398624,-71.7570466:18.2397695,-71.7573829:18.2392052,-71.7578405:18.2388485,-71.7580658:18.2383935,-71.7582273:18.2375748,-71.7580229:18.2370908,-71.7581302:18.2367902,-71.7583072:18.2364997,-71.7585647:18.2360972,-71.7586345:18.2357304,-71.7587471:18.2354807,-71.7587605:18.2352517,-71.7586952:18.2349814,-71.7585594:18.234742,-71.7585111:18.2343904,-71.7586505:18.2339522,-71.7587525:18.2335897,-71.7586596:18.2332491,-71.7584574:18.2329281,-71.7581785:18.2327039,-71.757803:18.2322088,-71.7575168:18.2315219,-71.7573738:18.2308188,-71.7575562:18.2302124,-71.7576313:18.2295195,-71.7575508:18.2287807,-71.7573845:18.2281794,-71.7574221:18.2276139,-71.7574865:18.2273085,-71.7574407:18.2269821,-71.7574328:18.2268204,-71.757642:18.2267681,-71.7578727:18.22687,-71.7584842:18.2267719,-71.759041:18.2264878,-71.7593318:18.2259841,-71.7595044:18.2255302,-71.7599099:18.2253974,-71.7602009:18.2253567,-71.7608124:18.2254586,-71.7615688:18.2253006,-71.7619658:18.2251372,-71.7621802:18.2243937,-71.7624271:18.2239809,-71.7625988:18.2230841,-71.7630547:18.2221721,-71.7630547:18.2218256,-71.7630199:18.2216523,-71.7631164:18.2215453,-71.7633095:18.2214692,-71.7635507:18.2212562,-71.7638715:18.2211606,-71.7641464:18.2211121,-71.764532:18.2212437,-71.7654123:18.2212972,-71.7663018:18.221115,-71.7671722:18.2208829,-71.7676064:18.220442,-71.7681112:18.2200959,-71.768315:18.2198575,-71.768446:18.2196594,-71.7686364:18.2195008,-71.7686847:18.2192524,-71.7686579:18.219083,-71.7686552:18.2189339,-71.768682:18.218551,-71.7688912:18.2183556,-71.7689073:18.2178664,-71.7687142:18.2176218,-71.7688242:18.2173874,-71.7687893:18.2160254,-71.7691715:18.2154301,-71.7690091:18.2148579,-71.769196:18.2142392,-71.7694932:18.2138256,-71.769535:18.2133923,-71.7694615:18.2130001,-71.7694974:18.2124679,-71.7697099:18.2118261,-71.769978:18.2108539,-71.7706334:18.2106637,-71.7706856:18.2104472,-71.7706695:18.2101898,-71.7705756:18.2098892,-71.7706212:18.2096981,-71.770691:18.2094178,-71.7707768:18.2091274,-71.770817:18.2087452,-71.7709109:18.2083823,-71.7711083:18.2074791,-71.7717637:18.2066173,-71.7723435:18.2050772,-71.7732908:18.204722,-71.7735019:18.2043891,-71.7735924:18.2040544,-71.7735046:18.2038379,-71.7733115:18.2034628,-71.7729221:18.2030489,-71.7728051:18.2027037,-71.7728645:18.2023944,-71.773172:18.20197,-71.7735635:18.2016211,-71.7737031:18.2012592,-71.7737041:18.2010504,-71.7736682:18.2008664,-71.7735461:18.2005689,-71.7730634:18.2003809,-71.7729011:18.199663,-71.7725313:18.1992145,-71.7722526:18.1987156,-71.7719442:18.198566,-71.7719087:18.1984158,-71.7719142:18.1982603,-71.7719677:18.1980997,-71.7721045:18.1979205,-71.7723529:18.1978266,-71.7731793:18.1976936,-71.7735542:18.1975832,-71.7737793:18.1972709,-71.7740406:18.1967032,-71.7742318:18.1961072,-71.7744649:18.1956017,-71.7747802:18.1947635,-71.7754885:18.1943541,-71.7759401:18.1939921,-71.7765355:18.1937476,-71.7771202:18.1935692,-71.777782:18.1934126,-71.7791694:18.1933722,-71.78013:18.1932499,-71.7807021:18.192866,-71.7812347:18.1923475,-71.781549:18.1913906,-71.7819215:18.1908555,-71.7821574:18.1906007,-71.7823747:18.1903874,-71.7827305:18.1897459,-71.7838282:18.189308,-71.7840913:18.188551,-71.7842265:18.188157,-71.7845661:18.1879547,-71.7849148:18.187388,-71.7859631:18.1866556,-71.786467:18.1862613,-71.7865082:18.1849961,-71.7870627:18.1839137,-71.7871367:18.1837218,-71.7871643:18.1833612,-71.7872625:18.1827776,-71.7874172:18.1824352,-71.7875812:18.1820603,-71.7877015:18.181915,-71.7877552:18.1817354,-71.7877525:18.1815723,-71.7876989:18.1814245,-71.7876801:18.1812958,-71.7876868:18.1811722,-71.7877364:18.1809174,-71.7879161:18.1805856,-71.7880658:18.1804485,-71.7880636:18.1802727,-71.7880073:18.1799847,-71.7878222:18.1796891,-71.7877471:18.1794037,-71.7877284:18.1791871,-71.7877659:18.178866,-71.7878142:18.1785449,-71.7878974:18.1782238,-71.7879805:18.178048,-71.7879805:18.1778976,-71.7879242:18.1773969,-71.7878987:18.1769063,-71.7879054 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672090000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || source -> US Census Bureau, boundary import, 16/01/2010 || last_edit_version -> 10 || border_type -> nation\n50553732000000 && 19.4014135,-71.6948873:19.3998711,-71.6964309:19.3987117,-71.6967016:19.3983877,-71.6972503:19.3971064,-71.6967721:19.3960031,-71.6981355:19.3942049,-71.697525:19.3929123,-71.6984072:19.3913655,-71.6977249:19.3910461,-71.6970371:19.3899513,-71.6973826:19.3888598,-71.696425:19.388536,-71.6964886:19.38828,-71.695809:19.3866709,-71.6956702:19.3861579,-71.6952526:19.3852553,-71.6952518:19.3823008,-71.6945792 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672098000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 7 || border_type -> nation\n48435450000000 && 18.4473317,-71.8806772:18.4459131,-71.8762192:18.4385343,-71.8656267:18.4327354,-71.8507123:18.4292391,-71.8440231:18.4175181,-71.8367658:18.3988604,-71.8298162:18.3978568,-71.8173053:18.3924227,-71.8033353:18.3895637,-71.8056384:18.3788104,-71.795323:18.3455194,-71.7088588 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672090000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 7 || border_type -> nation\n180241392000000 && 19.9245878,-72.0313557:19.7964561,-71.9138432:19.7331793,-71.8038176:19.7319628,-71.7958731:19.7312615,-71.7736624:19.7019452,-71.7586772 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672100000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || maritime -> yes || last_edit_version -> 7 || border_type -> territorial\n48420027000000 && 18.6275611,-72.0008373:18.6279884,-72.0038507:18.625571,-72.0079557:18.6250156,-72.0082306:18.6217664,-72.0054559:18.6205443,-72.0029869:18.6174876,-72.0003985:18.6177275,-71.9995894:18.6180962,-71.9983614:18.6171809,-71.9958871 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672089000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 7 || border_type -> nation\n180241396000000 && 19.9245878,-72.0313557:19.9360405,-72.1250044:19.9336273,-72.2507913:20.0711243,-72.4561576:20.1952505,-72.6846277:20.2181368,-72.8425031:20.1530828,-73.0157809:20.0675075,-73.2288485:19.9662032,-73.4971083:19.8756979,-73.6716697:19.7005746,-73.7281455:19.547033,-73.6639685:19.4151366,-73.4804223:19.4284521,-73.2211473:19.4441872,-73.1171806:19.3715509,-72.9439027:19.1898187,-72.9593052:19.0709784,-73.0209151:19.1061543,-73.2827572:19.0806828,-73.3546354:18.9726888,-73.4457667:18.868908,-73.4460982:18.7637437,-73.3435427:18.6929231,-73.4332788:18.7586861,-73.7751306:18.8193676,-74.1351434:18.7647552,-74.4460149:18.7050659,-74.550707:18.6423188,-74.6180091:18.4225219,-74.6575357:18.3454757,-74.6329651:18.2470905,-74.5891653:18.182146,-74.4759269:18.1364674,-74.3808494:18.1659061,-74.2686792:18.1750412,-74.2024454:18.0135837,-74.0635681:17.9536341,-74.0144269:17.9099291,-73.9278957:17.9099291,-73.7719258:17.9658289,-73.690736:17.9952961,-73.6180925:17.9983442,-73.5336978:18.073513,-73.4760103:18.1628609,-73.4941712:18.1171773,-73.352089:18.1019468,-73.1672753:18.0410115,-72.8884524:18.0724974,-72.5038691:18.0897614,-72.2635045:18.1070238,-72.0979201:18.1009314,-72.0199351:18.0907769,-71.9772036:17.953787,-71.8295092 && last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672100000 || last_edit_user_id -> 0 || iso_country_code -> AAA || maritime -> yes || last_edit_version -> 4\n48997920000000 && 19.5484927,-71.7120447:19.5492166,-71.7121354:19.5496441,-71.7123477:19.5500592,-71.7125531:19.5506309,-71.7128435:19.5518477,-71.7133154:19.5524537,-71.713834:19.5530108,-71.7144771:19.5533109,-71.7148781:19.5535385,-71.7151823:19.5543265,-71.7165148:19.5547802,-71.7171964:19.5555372,-71.7180915:19.5562115,-71.7185479:19.557049,-71.7194227:19.5574614,-71.7199542:19.5579152,-71.7207672:19.5580196,-71.7214986:19.5579326,-71.7222455:19.5577948,-71.7226706:19.5578339,-71.7230751:19.5584277,-71.7232623:19.5587965,-71.7231788:19.5590555,-71.7230491:19.5592021,-71.7228521:19.559163,-71.7221987:19.5593829,-71.7218305:19.5597139,-71.7214812:19.5602332,-71.7212808:19.5605606,-71.7213067:19.5609417,-71.7214519:19.5610688,-71.7216956:19.5612121,-71.7233797:19.5612958,-71.724053:19.5612985,-71.7240745:19.561644,-71.7242869:19.5622442,-71.7245378:19.5630263,-71.7244413:19.5639903,-71.7247115:19.5650088,-71.7249431:19.5654817,-71.7248466:19.5659364,-71.7238622 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || source -> DigitalGlobe || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672094000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Río Dajabón || name:fr -> Rivière du Massacre || last_edit_version -> 17\n50633852000000 && 18.5790867,-71.9551275:18.5774642,-71.9536133:18.5037193,-71.8808014:18.4973083,-71.8812582:18.4902312,-71.8807337:18.4852653,-71.9137207:18.4571839,-71.9045459:18.4473317,-71.8806772 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672098000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 5 || border_type -> nation\n48435426000000 && 18.6171809,-71.9958871:18.6175825,-71.9905108:18.6186012,-71.9898585:18.6186012,-71.9876048:18.6189055,-71.9863115:18.6169727,-71.9851336:18.6152431,-71.9848042:18.6124898,-71.9820121:18.6112693,-71.9819011:18.6107594,-71.9805086:18.6074966,-71.9780287:18.6053624,-71.9755572:18.6047474,-71.9693188:18.6052597,-71.9674918:18.603425,-71.9651288:18.5790867,-71.9551275 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672090000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 8 || border_type -> nation\n537853868000000 && 18.3455194,-71.7088588:18.3406367,-71.6960202:18.3347206,-71.6989707:18.3321079,-71.7002963:18.3166392,-71.7098421:18.2921845,-71.724932 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672082000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 1 || border_type -> nation\n50634189000000 && 18.6546278,-71.963774:18.6566647,-71.9663612:18.6476151,-71.975652:18.6390802,-71.9793642:18.6362304,-71.9823693:18.6340549,-71.9891411:18.6321162,-71.9921015:18.6321699,-71.9937674:18.6310927,-71.9958508:18.6311058,-71.9978014:18.6293506,-71.998694:18.6275611,-72.0008373 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672098000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 5 || border_type -> nation\n48679069000000 && 18.7722367,-71.7287578:18.7221933,-71.7379549:18.7181081,-71.7495812:18.7098585,-71.7730989:18.7103185,-71.7751742:18.7061069,-71.7837985:18.7025299,-71.7852303:18.6919704,-71.785856:18.6894768,-71.7888867:18.6891583,-71.7940075:18.6910224,-71.7948059:18.6917939,-71.7976899:18.6852883,-71.8041962 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672093000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 8 || border_type -> nation\n48582329000000 && 19.0496966,-71.7372633:19.0491076,-71.7378709:19.0487955,-71.7379879:19.0484054,-71.7377952:19.0482287,-71.7374082:19.0473381,-71.7371838:19.0466849,-71.7373113:19.0449533,-71.7380288:19.0445261,-71.7387692:19.044126,-71.7394859:19.0434686,-71.740177:19.0432576,-71.7412817:19.0435101,-71.7422849:19.0457838,-71.7447643:19.0469887,-71.7481646:19.046967,-71.7488169:19.0469801,-71.7492022:19.0471066,-71.7497445:19.0470633,-71.7504841:19.0467903,-71.7511568:19.0464807,-71.7517908:19.0460712,-71.7521711:19.0448294,-71.7530023:19.0439173,-71.7535024:19.04168,-71.7542631:19.0411307,-71.7545555:19.0408877,-71.754883:19.0407146,-71.7556508:19.0405248,-71.7566052:19.0402452,-71.7572497:19.0395161,-71.7581901:19.0385506,-71.7596658:19.037898,-71.7604441:19.0369525,-71.7612788:19.0361535,-71.7619761:19.035734,-71.7625854:19.0352712,-71.7631032:19.0349209,-71.7632458:19.0343519,-71.7632028:19.0336123,-71.7630223:19.0331489,-71.7627385:19.0325881,-71.7620678:19.0319703,-71.7616895:19.0309218,-71.7606405:19.0303121,-71.7601762:19.0296375,-71.7600128:19.0284344,-71.7604599:19.0271013,-71.7611908:19.0265486,-71.7618185:19.0261909,-71.7631598:19.0260527,-71.7643034:19.0263291,-71.7651289:19.0271358,-71.765632:19.0278901,-71.7659897:19.02988,-71.7667257:19.0312001,-71.767331:19.0316683,-71.7677438:19.0318764,-71.7687343:19.0318243,-71.769374:19.0315967,-71.7702063:19.0313171,-71.7709217:19.0309595,-71.7714239:19.0303482,-71.7718504:19.029637,-71.7721191:19.028786,-71.7724818:19.0279732,-71.7735029:19.0253694,-71.7753032:19.023261,-71.7772378:19.022029,-71.7783395:19.0210256,-71.779683:19.0198825,-71.7806235:19.0185386,-71.7820164:19.017524,-71.7823125:19.0165794,-71.7819424:19.0160896,-71.7809063:19.0161596,-71.7780199:19.0158447,-71.7769838:19.0142354,-71.7757996:19.013312,-71.7756107:19.0126236,-71.7757282:19.012183,-71.7762153:19.0114292,-71.7774293:19.0108425,-71.7796111:19.0106859,-71.7813816:19.0108913,-71.782582:19.0122629,-71.7844514:19.012312,-71.7849028:19.0121109,-71.7855251:19.0105976,-71.78642:19.0085821,-71.7900147:19.0080885,-71.7904983:19.0071335,-71.7908772:19.0018565,-71.7913786:19.0008846,-71.7910708:19.0003251,-71.7901736:18.9996918,-71.7887257:18.9985739,-71.7872679:18.9968259,-71.7865192:18.9942907,-71.7875643:18.9930737,-71.7884108:18.9928896,-71.7890564:18.9929743,-71.7910095:18.9933817,-71.79265:18.9939026,-71.7935539:18.9943783,-71.7938939:18.9946992,-71.7942305:18.9946865,-71.7953422:18.9948405,-71.7963796:18.995752,-71.7980566:18.9961154,-71.7983144:18.9965396,-71.7983716:18.9978407,-71.7974812:18.99876,-71.7965877:19.000202,-71.7961002:19.0027058,-71.7962124:19.0040487,-71.7966579:19.0052137,-71.7987757:19.0051309,-71.8012051:19.0043128,-71.8040314:19.0034941,-71.8049128:19.0027912,-71.8052567:19.0020158,-71.8053382:18.9992673,-71.8044295:18.998956,-71.8045763:18.9988598,-71.8048252:18.998384,-71.8069382:18.9980453,-71.8095659:18.9960239,-71.8143287:18.9967633,-71.8181619:18.996683,-71.8191314:18.9960555,-71.8204823:18.9940389,-71.8239198:18.99373,-71.8267544:18.9937214,-71.8274669:18.9941376,-71.8281083:18.9950447,-71.8285037:18.9965207,-71.8283933:18.9988802,-71.8293963:19.0008914,-71.8298841:19.0036876,-71.8299304:19.0044198,-71.8302137:19.0052832,-71.8317831:19.0055238,-71.8328613:19.0054866,-71.833459:19.005075,-71.8338216:19.0039345,-71.8338276:19.0028304,-71.8341247:19.0010552,-71.834311:19.0006545,-71.8346041:19.0005443,-71.8350569:19.0007088,-71.8357187:19.0006544,-71.8361866:19.0004795,-71.8367198:18.9999535,-71.8371394:18.9998979,-71.8384008:19.000217,-71.8393783:19.0001295,-71.8405347:18.9997366,-71.841373:18.9991533,-71.8419184:18.9978703,-71.8426086:18.9964289,-71.8430559:18.9934776,-71.8433119:18.9920651,-71.8436925:18.9916149,-71.8439592:18.9912481,-71.8442909:18.9911645,-71.8446699:18.991461,-71.8462161:18.991781,-71.846657:18.9922041,-71.8480089:18.9922498,-71.8489098:18.9927934,-71.8509627:18.9927956,-71.8516766:18.9925826,-71.8520665:18.9923006,-71.8522836:18.9916764,-71.8523546:18.9903734,-71.8522759:18.9893422,-71.8517819:18.9887556,-71.8513263:18.9872707,-71.8511735:18.9868821,-71.8512816:18.9862803,-71.8522084:18.9826936,-71.8558545:18.9818908,-71.8563079:18.9811588,-71.8563569:18.9788501,-71.8553291:18.9783352,-71.8552818:18.9778748,-71.8555901:18.9771263,-71.8563785:18.9766549,-71.8574664:18.9765067,-71.8585685:18.9770528,-71.860809:18.9777868,-71.8621389:18.9782868,-71.8627677:18.9788331,-71.8632108:18.979556,-71.8633384:18.9818471,-71.8628762 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || source -> digitalglobe || boat -> no || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672092000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Artibonite || name:fr -> Rivière de l'Artibonite || name:ht -> Latibonit || name:en -> Artibonite River || last_edit_version -> 10 || name:es -> Río Artibonite\n50448375000000 && 19.1686078,-71.6463432:19.1679135,-71.6462596:19.1657588,-71.644907:19.1639501,-71.6476695:19.1631188,-71.6466321:19.1635692,-71.6451033:19.1628115,-71.6446343:19.1614572,-71.6455924:19.1613076,-71.6438289:19.160551,-71.6439107:19.1595659,-71.6433418:19.1581386,-71.6432758:19.1569308,-71.6423791:19.1560984,-71.6437445:19.1570028,-71.645825:19.1556509,-71.6454346:19.1550408,-71.6441458:19.1541393,-71.6435282:19.1538661,-71.6440746:19.154064,-71.6450203:19.1536137,-71.6460646:19.153532,-71.6470255:19.1525798,-71.6467788:19.1527777,-71.6457491:19.1515744,-71.6450992:19.1510425,-71.6441412:19.1496894,-71.6440661:19.1496846,-71.6429341:19.1485527,-71.6417415:19.1480991,-71.6406206:19.146841,-71.6404276:19.1460604,-71.6411746:19.1451059,-71.6427705:19.1457569,-71.6433365:19.1450074,-71.6443013:19.1454245,-71.6449007:19.1447059,-71.64558:19.1451537,-71.6463781:19.1442274,-71.6475555:19.1446163,-71.6481845:19.1449346,-71.6487189:19.1437218,-71.6490283:19.1429707,-71.6499076:19.1431228,-71.6523071:19.1426609,-71.6529193:19.1425236,-71.6531075:19.1437088,-71.655971:19.1434093,-71.6565077:19.1420366,-71.6560992:19.1408578,-71.6543091:19.1388266,-71.6527927:19.1388948,-71.6515852:19.1376892,-71.6503086:19.1375379,-71.6489442:19.1373904,-71.6487774:19.1359549,-71.6472574:19.1355719,-71.6460591:19.1334223,-71.6464634:19.1328813,-71.646987:19.1315826,-71.6475356:19.1308716,-71.6481387:19.128961,-71.6477975:19.1286105,-71.6483743:19.1266757,-71.6491825:19.1256686,-71.6493832:19.1247926,-71.6498472:19.1228222,-71.6511783:19.1208084,-71.6516177:19.1185998,-71.6537417:19.1164878,-71.6537368:19.1155052,-71.6542978:19.1173272,-71.6557234:19.1163429,-71.6571865:19.1149072,-71.6581459:19.1129487,-71.6631131:19.1141582,-71.6651113:19.1143869,-71.6672716:19.1160441,-71.6680738:19.1162734,-71.6692752:19.1174069,-71.6705628:19.1168115,-71.671078:19.1159493,-71.6713136:19.1156726,-71.6724775:19.1128022,-71.6729624:19.1134054,-71.6748778:19.1146122,-71.6767242:19.1164048,-71.676787:19.116774,-71.6769411:19.1170426,-71.6772545:19.1173136,-71.6777145:19.1173453,-71.6779481:19.1171955,-71.6785504:19.1159781,-71.6796017:19.1171003,-71.6825614:19.1156704,-71.6818494:19.1145372,-71.6825746:19.1135624,-71.6805717:19.1121225,-71.6798029:19.1103079,-71.6802758:19.1097969,-71.6796705:19.109441,-71.6792488:19.1088094,-71.6797646:19.1091812,-71.6813619:19.1091027,-71.6815456:19.1084278,-71.6831239:19.1074262,-71.6821375:19.1029542,-71.6849042:19.1034484,-71.687287:19.1053416,-71.6890458:19.1050665,-71.6908173:19.1053145,-71.6913275:19.1053896,-71.6919847:19.1054541,-71.6920808 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || fixme -> what is right? || last_edit_time -> 1509672097000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 14 || border_type -> nation\n48686950000000 && 19.588494,-71.743441:19.5846797,-71.743137 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672093000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 10 || border_type -> nation\n48997923000000 && 19.6615297,-71.7349932:19.6482538,-71.7327828:19.6461715,-71.7324345:19.6456052,-71.7330457 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672094000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 9 || border_type -> nation\n48429583000000 && 19.3823008,-71.6945792:19.3817551,-71.6943524:19.3811646,-71.6946402:19.3796089,-71.6939583:19.3748598,-71.693379:19.372674,-71.6913023:19.3717841,-71.6910065:19.3706225,-71.6916481:19.3693932,-71.6925384:19.3689342,-71.6936032:19.368634,-71.6939706:19.3675051,-71.6939363:19.3643585,-71.6936195:19.3630943,-71.692095:19.3603257,-71.6937104:19.3596634,-71.694513:19.359712,-71.6961952:19.359515,-71.6967792:19.3600483,-71.6973201:19.3595743,-71.7000494:19.3595424,-71.7012768:19.3608256,-71.702354:19.3622816,-71.7030094:19.3634872,-71.7028044:19.3647869,-71.7033014:19.3658465,-71.7054864:19.3716429,-71.7140716:19.3684452,-71.7169318:19.3647282,-71.7185183:19.3628264,-71.7181665:19.3627557,-71.7187481:19.3642181,-71.7197648:19.3652261,-71.7215992:19.3652813,-71.7226631:19.3665588,-71.724435:19.3633227,-71.7291786:19.3628745,-71.7303573:19.3614783,-71.7310128:19.3606404,-71.7316:19.3607011,-71.7349748:19.3595267,-71.7349221:19.3581293,-71.7375271:19.3562874,-71.7440398:19.3535463,-71.7482404:19.3437228,-71.7510882:19.3406123,-71.751787:19.3396526,-71.7526332:19.3395904,-71.7562382:19.339091,-71.7631704:19.3387493,-71.7683215:19.3385206,-71.7722246:19.3374083,-71.7746522:19.335391,-71.7787914:19.3331575,-71.78336:19.3316482,-71.785936 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672089000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 9 || border_type -> nation\n339640154000000 && 19.2795825,-71.7530406:19.2792499,-71.751089:19.2788678,-71.7499966:19.2779604,-71.7486242:19.2756336,-71.7486117:19.2745376,-71.7481812:19.2740604,-71.7471831:19.2741719,-71.7463445:19.2757133,-71.7452209:19.2767307,-71.7430527:19.2768854,-71.7400936:19.278544,-71.7394619:19.2786962,-71.7388886:19.2780168,-71.7382462:19.2752168,-71.7386438:19.2746911,-71.7395288:19.2738186,-71.7396981:19.272344,-71.7390049:19.2689486,-71.7384068:19.2674806,-71.7365635:19.2655911,-71.7358882:19.2650216,-71.7353292:19.2651755,-71.7338434:19.2621853,-71.7316788:19.2624182,-71.7310753:19.2664549,-71.7322024:19.267851,-71.7310341:19.2690945,-71.7309144:19.2701453,-71.7301142:19.2709364,-71.7294332:19.2719166,-71.7292331:19.2725986,-71.7295142:19.2733534,-71.7283206:19.2741045,-71.7284292:19.2751244,-71.7274205:19.2771957,-71.7276645:19.2773077,-71.7268638:19.2782949,-71.725542:19.2765542,-71.7241034:19.2757949,-71.7235387:19.2717447,-71.7226688:19.2707021,-71.7219102:19.2699821,-71.7220196:19.2685926,-71.7240622:19.2676127,-71.7237775:19.2675371,-71.7225814:19.2667412,-71.7209816:19.2661344,-71.7198627:19.2671483,-71.7180177:19.2671521,-71.7167249:19.2662595,-71.7157734:19.2645755,-71.7159875:19.263602,-71.714049:19.2637109,-71.7135812:19.2630788,-71.7130616:19.2607771,-71.7115489:19.2606709,-71.7106671:19.2602335,-71.7099346:19.2595971,-71.7086738:19.2575422,-71.7073368:19.2566424,-71.7079915:19.2553259,-71.7075617:19.2529229,-71.7089496:19.2510661,-71.7080839:19.250912,-71.7060437:19.2476991,-71.7045589:19.2474772,-71.7027578:19.2470561,-71.7024336:19.2463318,-71.7016073:19.2452037,-71.7003135:19.2448666,-71.6996361:19.2441101,-71.6982353:19.2434253,-71.6977929:19.2427509,-71.6974357:19.2415783,-71.6974782:19.2407121,-71.696992:19.2405519,-71.6956173:19.2418765,-71.6935001:19.2419132,-71.6925396:19.2409132,-71.6921416:19.2392308,-71.69094:19.2385921,-71.6900596:19.2386967,-71.6888602:19.2391129,-71.6884147:19.2389636,-71.6856525:19.238352,-71.6847811 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672102000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || waterway -> river || name -> Rio Libon || last_edit_version -> 6 || border_type -> nation\n204483815000000 && 18.9247413,-71.7743395:18.9233718,-71.7731187:18.9191471,-71.7720596:18.9068755,-71.7681602:18.9058181,-71.7603184:18.8969994,-71.7479324:18.893005,-71.7435337:18.8913483,-71.7391372:18.8850935,-71.7365937:18.8780807,-71.7374525:18.8783461,-71.7346813:18.8771732,-71.7273133:18.8789331,-71.7258288:18.8784483,-71.7222981:18.8754511,-71.7251653:18.8636295,-71.7258312:18.8589603,-71.7256754:18.8573302,-71.7263992:18.8563369,-71.7258591:18.854842,-71.7265612:18.8515986,-71.7263967:18.8506132,-71.7252781:18.8488796,-71.7257574:18.8469957,-71.7248838:18.8450364,-71.725358:18.8416462,-71.7260779:18.8393077,-71.725526:18.8327439,-71.7208094:18.825729,-71.7222537:18.807862,-71.7220126:18.8042426,-71.7220164:18.7837454,-71.7263339:18.7820527,-71.7266904:18.7722367,-71.7287578 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672101000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 7 || border_type -> nation\n48997919000000 && 19.5418925,-71.7124871:19.5426758,-71.7125721:19.5435447,-71.7125672:19.5442954,-71.7126097:19.5447086,-71.7127477:19.5456964,-71.7129409:19.5463589,-71.7128934:19.5469558,-71.712773:19.5475407,-71.7123328:19.5479764,-71.7121428:19.5483345,-71.7120415:19.5484927,-71.7120447 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || source -> DigitalGlobe || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672094000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Río Dajabón || name:fr -> Rivière du Massacre || last_edit_version -> 11\n48998242000000 && 18.1769063,-71.7879054:18.1766935,-71.787833:18.1765878,-71.7877471:18.1765444,-71.787664:18.1765438,-71.7875835:18.1766107,-71.7874816:18.1766336,-71.7873904:18.1766183,-71.7872871:18.1764839,-71.7869633:18.1764495,-71.7865723:18.1763826,-71.7864711:18.1762998,-71.7864271:18.1761953,-71.786396:18.1760711,-71.7863678:18.1759443,-71.7863376:18.1758188,-71.7863242:18.1754563,-71.7863531:18.1750715,-71.7863283:18.1747018,-71.7863589:18.1744955,-71.7863229:18.1742863,-71.7862275:18.1740598,-71.7860413:18.1738276,-71.7858198:18.1735193,-71.7853504:18.1732976,-71.784865:18.1730172,-71.7842615:18.172821,-71.7839128:18.1726477,-71.7836285:18.1725024,-71.7834246:18.1722884,-71.7832422:18.1720898,-71.783182:18.1718679,-71.7831993:18.1713913,-71.7833227:18.1711189,-71.7833403:18.1708434,-71.7834032:18.1706219,-71.7834958:18.1704153,-71.7836419:18.1702448,-71.7837077:18.1700738,-71.7837143:18.169707,-71.7836863:18.1693961,-71.7836675:18.1690827,-71.7835522:18.1689502,-71.7834127:18.168927,-71.7832476:18.1689552,-71.7830479:18.1690213,-71.7828586:18.1692075,-71.7824739:18.1693449,-71.7821023:18.1693564,-71.7818395:18.1691742,-71.7812976:18.1690365,-71.7810348:18.1687323,-71.7807037:18.1684275,-71.7804192:18.1682198,-71.7801603:18.1679853,-71.7798291:18.1678299,-71.7795394:18.1676706,-71.7791881:18.1675177,-71.7788581:18.1674221,-71.7784867:18.1672539,-71.7782264:18.1670335,-71.7779556:18.1667544,-71.7777048:18.1665594,-71.7775881:18.1662802,-71.7775624 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672095000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || waterway -> stream || name -> Haïti - República Dominicana || source -> US Census Bureau, boundary import, 16/01/2010 || last_edit_version -> 6 || border_type -> nation\n48997917000000 && 19.5030529,-71.688106:19.5033992,-71.6883126:19.5038276,-71.6884905:19.5046888,-71.6887359:19.505312,-71.6889287:19.5062386,-71.6894541:19.5067444,-71.6895328:19.5077489,-71.6892627:19.5088453,-71.6886661:19.5102681,-71.6878675:19.511322,-71.6878:19.5125599,-71.6880701:19.5134865,-71.6886104:19.5139463,-71.6890007:19.5140736,-71.6893534:19.5140524,-71.6897436:19.513727,-71.6901939:19.5132885,-71.6905841:19.5126023,-71.6909969:19.5116262,-71.6914771:19.5112089,-71.6918073:19.5109825,-71.6923326:19.5107137,-71.6928279:19.5106005,-71.6931731:19.5108481,-71.6940962:19.5114423,-71.6947041:19.5118879,-71.6952069:19.5120011,-71.6957547:19.5116899,-71.6970154:19.5115272,-71.6982612:19.5118384,-71.6989215:19.5131328,-71.6998521:19.5140241,-71.7003474:19.5149649,-71.70052:19.5168676,-71.7003249:19.5186854,-71.6998146:19.5201566,-71.6998971:19.5206234,-71.7002048:19.5208498,-71.7008502:19.5207649,-71.7016457:19.5206093,-71.7025612:19.5207649,-71.7031015:19.5210407,-71.7032741:19.5220876,-71.7033192:19.5233253,-71.7035368:19.5251219,-71.7037619:19.5265647,-71.7037319:19.5273357,-71.7034843:19.5281985,-71.7034767:19.5287431,-71.7034242:19.5299738,-71.7029514:19.5303769,-71.7028839:19.5307235,-71.7026287:19.531742,-71.7024862:19.5327746,-71.7025462:19.5333191,-71.7028089:19.533793,-71.7031616:19.534281,-71.7038295:19.5344507,-71.7044523:19.5343729,-71.7049026:19.534083,-71.705563:19.5337223,-71.7059757:19.5331635,-71.706381:19.5324846,-71.7066511:19.5318622,-71.7069063:19.5315298,-71.7071389:19.5313247,-71.7076417:19.5315086,-71.7083321:19.5316359,-71.7096154:19.5319046,-71.7108161:19.5323856,-71.7120093:19.5329938,-71.7125196:19.5336303,-71.7127447:19.5345851,-71.7129248:19.5356884,-71.7130749:19.5365795,-71.7129624:19.5381355,-71.7126772:19.5400167,-71.7124596:19.541219,-71.7124371:19.5418925,-71.7124871 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || source -> DigitalGlobe || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672094000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Río Dajabón || name:fr -> Rivière du Massacre || last_edit_version -> 13\n50447457000000 && 18.949921,-71.782772:18.9497892,-71.7827565:18.9493957,-71.7827586:18.948256,-71.7828834:18.9430558,-71.7816744:18.937524,-71.7787542:18.9298447,-71.7762835:18.9247413,-71.7743395 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672097000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 9 || border_type -> nation\n48360222000000 && 18.9813109,-71.8681351:18.9809284,-71.8684666:18.9801332,-71.8686426:18.9790593,-71.8683594:18.9783584,-71.8677287:18.9763974,-71.8668133:18.9756266,-71.866889:18.9745629,-71.8682977:18.9725403,-71.8729551:18.9715664,-71.8746492:18.9713813,-71.8755248:18.9710993,-71.8761779:18.9706286,-71.8764944:18.9695895,-71.8765232:18.9675263,-71.8759129:18.9650854,-71.8738964:18.9643074,-71.8736232:18.9636644,-71.8736944:18.9613175,-71.8743935:18.9582733,-71.8748823:18.9576071,-71.8754292:18.9572633,-71.8761537:18.9568285,-71.8781114:18.9566943,-71.8795499:18.9568294,-71.8810238:18.9579014,-71.8821573:18.9590379,-71.8831873:18.9599782,-71.884264:18.9600945,-71.8848003:18.9598916,-71.8856067:18.9592834,-71.8864571:18.958578,-71.8865583:18.9574496,-71.8859351:18.9568248,-71.8857258:18.9560463,-71.8857439:18.9545481,-71.885437:18.9533972,-71.8849892:18.9513012,-71.8831231 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || boat -> no || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672089000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Artibonite || name:fr -> Rivière de l'Artibonite || name:ht -> Latibonit || name:en -> Artibonite River || last_edit_version -> 17 || name:es -> Río Artibonite\n48419971000000 && 18.6852883,-71.8041962:18.6807218,-71.8016109:18.6698318,-71.8029898:18.6613317,-71.8095417:18.651473,-71.8089308 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672089000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 9 || border_type -> nation\n364609689000000 && 17.8410354,-71.9396881:17.594635,-71.8969767:17.5922837,-71.8965544:17.5899812,-71.8961127:17.5876887,-71.8956465:17.5854123,-71.8951519:17.5831353,-71.8946218:17.5808594,-71.894076:17.5785831,-71.8934912:17.576295,-71.8928709:17.42685,-71.8515952:17.4261794,-71.8513945:17.4249494,-71.8510465:17.4237215,-71.8506905:17.4224965,-71.8503265:17.4212735,-71.8499535:17.4200525,-71.8495735:17.4188336,-71.8491855:17.4176176,-71.8487885:17.4164036,-71.8483845:17.4162256,-71.8483235:17.4155506,-71.8481055:17.4143357,-71.8477035:17.4131237,-71.8472935:17.4119137,-71.8468755:17.4107067,-71.8464495:17.4095028,-71.8460155:17.4083008,-71.8455745:17.4071018,-71.8451245:17.4059058,-71.8446665:17.4047119,-71.8442015:17.4035219,-71.8437275:17.4023339,-71.8432465:17.4011489,-71.8427575:17.399967,-71.8422605:17.398789,-71.8417555:17.397613,-71.8412435:17.3964411,-71.8407225:17.3952711,-71.8401945:17.3941051,-71.8396595:17.3929431,-71.8391155:17.3917832,-71.8385645:17.3906272,-71.8380055:17.3894742,-71.8374395:17.3883252,-71.8368655:17.3871803,-71.8362845:17.3860383,-71.8356955:17.3848993,-71.8350985:17.3837643,-71.8344945:17.3826333,-71.8338825:17.3815064,-71.8332635:17.3803824,-71.8326375:17.3792634,-71.8320035:17.3781474,-71.8313625:17.3770355,-71.8307135:17.3759275,-71.8300575:17.3748235,-71.8293945:17.3737235,-71.8287235:17.3726286,-71.8280465:17.3715366,-71.8273615:17.3704496,-71.8266685:17.3693656,-71.8259695:17.3682877,-71.8252635:17.3672127,-71.8245495:17.3661427,-71.8238285:17.3652147,-71.8231955:17.3649687,-71.8230325:17.3638968,-71.8223135:17.3628298,-71.8215885:17.3617668,-71.8208555:17.3607088,-71.8201155:17.3596558,-71.8193695:17.3586069,-71.8186155:17.3575619,-71.8178555:17.3565219,-71.8170875:17.3554869,-71.8163135:17.354457,-71.8155325:17.353432,-71.8147445:17.352411,-71.8139495:17.351395,-71.8131485:17.350384,-71.8123405:17.3493781,-71.8115255:17.3483771,-71.8107045:17.3473811,-71.8098765:17.3463901,-71.8090425:17.3454042,-71.8082015:17.3444232,-71.8073545:17.3434482,-71.8065005:17.3424772,-71.8056395:17.3415122,-71.8047735:17.3405533,-71.8039005:17.3395983,-71.8030205:17.3386493,-71.8021355:17.3377053,-71.8012435:17.3367673,-71.8003455:17.3358354,-71.7994405:17.3349074,-71.7985305:17.3339864,-71.7976135:17.3339834,-71.7976115:17.3319294,-71.7955555:17.3315534,-71.7951775:17.3311775,-71.7947995:17.3310955,-71.7947155:17.3310135,-71.7946325:17.3306395,-71.7942525:17.3302665,-71.7938715:17.3301845,-71.7937875:17.3301025,-71.7937045:17.3297315,-71.7933205:17.3293605,-71.7929385:17.3292795,-71.7928535:17.3291985,-71.7927695:17.3288295,-71.7923835:17.3284605,-71.7919985:17.3283805,-71.7919135:17.3282995,-71.7918295:17.3279325,-71.7914415:17.3275665,-71.7910535:17.3274865,-71.7909675:17.3274055,-71.7908825:17.3270425,-71.7904925:17.3266776,-71.7901025:17.3265986,-71.7900165:17.3265186,-71.7899305:17.3261576,-71.7895375:17.3257956,-71.7891455:17.3257166,-71.7890585:17.3256366,-71.7889725:17.3252776,-71.7885765:17.3249186,-71.7881825:17.3248396,-71.7880945:17.3247616,-71.7880085:17.3244046,-71.7876105:17.3240476,-71.7872135:17.3239696,-71.7871255:17.3238916,-71.7870385:17.3235376,-71.7866385:17.3231826,-71.7862395:17.3231056,-71.7861505:17.3230276,-71.7860635:17.3226756,-71.7856605:17.3223236,-71.7852595:17.3222466,-71.7851705:17.3221697,-71.7850825:17.3218197,-71.7846775:17.3214707,-71.7842735:17.3213937,-71.7841845:17.3213177,-71.7840955:17.3209707,-71.7836885:17.3206237,-71.7832815:17.3205477,-71.7831925:17.3204717,-71.7831025:17.3201267,-71.7826935:17.3197827,-71.7822855:17.3197067,-71.7821945:17.3196317,-71.7821055:17.3192897,-71.7816935:17.3189477,-71.7812825:17.3188727,-71.7811915:17.3187977,-71.7811015:17.3184587,-71.7806875:17.3181187,-71.7802745:17.3180447,-71.7801835:17.3179697,-71.7800925:17.3176327,-71.7796765:17.3172958,-71.7792615:17.3172228,-71.7791695:17.3171488,-71.7790785:17.3168138,-71.7786605:17.3164798,-71.7782425:17.3164068,-71.7781505:17.3163328,-71.7780585:17.3160018,-71.7776385:17.3156688,-71.7772185:17.3155968,-71.7771255:17.3155238,-71.7770335:17.3151948,-71.7766115:17.3148658,-71.7761895:17.3147938,-71.7760955:17.3147208,-71.7760035:17.3143948,-71.7755785:17.3140678,-71.7751545:17.3139968,-71.7750605:17.3139248,-71.7749675:17.3136008,-71.7745405:17.3132758,-71.7741145:17.3132058,-71.7740205:17.3131348,-71.7739265:17.3128129,-71.7734975:17.3124909,-71.7730695:17.3124209,-71.7729745:17.3123509,-71.7728805:17.3120319,-71.7724495:17.3117129,-71.7720185:17.3116429,-71.7719235:17.3115729,-71.7718295:17.3112569,-71.7713965:17.3109409,-71.7709635:17.3108719,-71.7708675:17.3108019,-71.7707725:17.3104889,-71.7703375:17.3101749,-71.7699025:17.3101059,-71.7698065:17.3100379,-71.7697115:17.3097269,-71.7692745:17.3094149,-71.7688375:17.3093479,-71.7687405:17.3092789,-71.7686445:17.3089709,-71.7682055:17.3086629,-71.7677665:17.3085949,-71.7676695:17.3085279,-71.7675735:17.3082219,-71.7671325:17.307916,-71.7666915:17.30785,-71.7665945:17.307783,-71.7664975:17.30748,-71.7660535:17.307177,-71.7656105:17.307111,-71.7655135:17.307044,-71.7654155:17.306744,-71.7649705:17.306444,-71.7645255:17.306378,-71.7644275:17.306312,-71.7643295:17.306015,-71.7638825:17.305717,-71.7634355:17.305652,-71.7633375:17.305587,-71.7632385:17.305292,-71.7627895:17.304997,-71.7623405:17.304933,-71.7622415:17.304868,-71.7621435:17.304576,-71.7616925:17.304284,-71.7612415:17.30422,-71.7611415:17.304156,-71.7610425:17.303867,-71.7605895:17.303578,-71.7601365:17.3035151,-71.7600375:17.3034511,-71.7599375:17.3031651,-71.7594825:17.3028781,-71.7590285:17.3028161,-71.7589275:17.3027531,-71.7588285:17.3024691,-71.7583715:17.3021851,-71.7579145:17.3021231,-71.7578145:17.3020611,-71.7577135:17.3017801,-71.7572555:17.3014991,-71.7567965:17.3014381,-71.7566955:17.3013761,-71.7565945:17.3010981,-71.7561345:17.3008201,-71.7556745:17.3007591,-71.7555725:17.3006981,-71.7554715:17.3004231,-71.7550095:17.3001471,-71.7545475:17.3000871,-71.7544455:17.3000271,-71.7543435:17.2997541,-71.7538795:17.2994821,-71.7534155:17.2994221,-71.7533135:17.2993621,-71.7532115:17.2990931,-71.7527455:17.2988232,-71.7522805:17.2987642,-71.7521775:17.2987052,-71.7520755:17.2984382,-71.7516075:17.2981712,-71.7511395:17.2981132,-71.7510375:17.2980552,-71.7509345:17.2977912,-71.7504645:17.2975262,-71.7499955:17.2974692,-71.7498925:17.2974112,-71.7497895:17.2971502,-71.7493185:17.2968892,-71.7488475:17.2968322,-71.7487435:17.2967742,-71.7486395:17.2965162,-71.7481665:17.2962582,-71.7476945:17.2962022,-71.7475905:17.2961452,-71.7474865:17.2958902,-71.7470115:17.2956342,-71.7465375:17.2955782,-71.7464325:17.2955222,-71.7463285:17.2952702,-71.7458525:17.2950172,-71.7453765:17.2949622,-71.7452715:17.2949072,-71.7451675:17.2946582,-71.7446895:17.2944082,-71.7442115:17.2943532,-71.7441065:17.2942992,-71.7440015:17.2940523,-71.7435215:17.2938053,-71.7430425:17.2937523,-71.7429365:17.2936973,-71.7428315:17.2934543,-71.7423505:17.2932103,-71.7418695:17.2931573,-71.7417635:17.2931033,-71.7416575:17.2928633,-71.7411755:17.2926223,-71.7406925:17.2925693,-71.7405865:17.2925163,-71.7404805:17.2922793,-71.7399965:17.2920413,-71.7395115:17.2919893,-71.7394055:17.2919373,-71.7392995:17.2917023,-71.7388135:17.2914673,-71.7383275:17.2914163,-71.7382205:17.2913643,-71.7381135:17.2911323,-71.7376265:17.2909003,-71.7371395:17.2908503,-71.7370315:17.2907993,-71.7369245:17.2905703,-71.7364355:17.2903413,-71.7359475:17.2902913,-71.7358395:17.2902413,-71.7357325:17.2900153,-71.7352415:17.2897893,-71.7347515:17.2897403,-71.7346435:17.2896903,-71.7345355:17.2894674,-71.7340435:17.2892444,-71.7335525:17.2891954,-71.7334435:17.2891474,-71.7333355:17.2889274,-71.7328425:17.2887064,-71.7323495:17.2886594,-71.7322405:17.2886104,-71.7321325:17.2883944,-71.7316375:17.2881764,-71.7311425:17.2881294,-71.7310345:17.2880824,-71.7309255:17.2878684,-71.7304295:17.2876534,-71.7299335:17.2876074,-71.7298235:17.2875604,-71.7297155:17.2873494,-71.7292175:17.2871384,-71.7287195:17.2870924,-71.7286105:17.2870464,-71.7285015:17.2868384,-71.7280025:17.2866304,-71.7275035:17.2865854,-71.7273935:17.2865394,-71.7272835:17.2863354,-71.7267835:17.2861294,-71.7262835:17.2860854,-71.7261735:17.2860404,-71.7260635:17.2858394,-71.7255615:17.2856364,-71.7250605:17.2855934,-71.7249495:17.2855484,-71.7248395:17.2853504,-71.7243365:17.2851514,-71.7238335:17.2851084,-71.7237225:17.2850644,-71.7236125:17.2848695,-71.7231085:17.2846725,-71.7226035:17.2846305,-71.7224935:17.2845875,-71.7223825:17.2843955,-71.7218765:17.2842025,-71.7213715:17.2841605,-71.7212595:17.2841185,-71.7211485:17.2839285,-71.7206425:17.2837395,-71.7201355:17.2836985,-71.7200235:17.2836565,-71.7199125:17.2834705,-71.7194045:17.2832835,-71.7188965:17.2832435,-71.7187845:17.2832025,-71.7186735:17.2830195,-71.7181635:17.2828355,-71.7176545:17.2827955,-71.7175425:17.2827555,-71.7174305:17.2825755,-71.7169205:17.2823945,-71.7164095:17.2823555,-71.7162975:17.2823165,-71.7161855:17.2821395,-71.7156735:17.2819615,-71.7151625:17.2819235,-71.7150495:17.2818845,-71.7149375:17.2817105,-71.7144245:17.2815365,-71.7139115:17.2814995,-71.7137985:17.2814605,-71.7136865:17.2812905,-71.7131725:17.2811185,-71.7126585:17.2810825,-71.7125455:17.2810445,-71.7124325:17.2808775,-71.7119175:17.2807085,-71.7114025:17.2806725,-71.7112895:17.2806355,-71.7111765:17.2804715,-71.7106595:17.2803065,-71.7101435:17.2802705,-71.7100305:17.2802346,-71.7099165:17.2800736,-71.7093995:17.2799116,-71.7088825:17.2798766,-71.7087685:17.2798416,-71.7086555:17.2796836,-71.7081365:17.2795246,-71.7076185:17.2794906,-71.7075045:17.2794556,-71.7073905:17.2793006,-71.7068715:17.2791456,-71.7063525:17.2791126,-71.7062375:17.2790786,-71.7061235:17.2789266,-71.7056035:17.2787736,-71.7050835:17.2787416,-71.7049685:17.2787076,-71.7048545:17.2785596,-71.7043335:17.2784106,-71.7038115:17.2783776,-71.7036975:17.2783456,-71.7035825:17.2781996,-71.7030605:17.2780536,-71.7025385:17.2780226,-71.7024235:17.2779906,-71.7023085:17.2778486,-71.7017855:17.2777056,-71.7012625:17.2776746,-71.7011475:17.2776436,-71.7010325:17.2775046,-71.7005085:17.2773646,-71.6999845:17.2773356,-71.6998685:17.2773046,-71.6997535:17.2771686,-71.6992285:17.2770326,-71.6987035:17.2770026,-71.6985885:17.2769736,-71.6984725:17.2768406,-71.6979465:17.2767076,-71.6974205:17.2766786,-71.6973055:17.2766496,-71.6971895:17.2765206,-71.6966625:17.2763906,-71.6961365:17.2763626,-71.6960205:17.2763336,-71.6959045:17.2762076,-71.6953765:17.2760806,-71.6948495:17.2760536,-71.6947335:17.2760256,-71.6946175:17.2759026,-71.6940885:17.2757796,-71.6935605:17.2757526,-71.6934445:17.2757256,-71.6933285:17.2756066,-71.6927985:17.2754857,-71.6922695:17.2754607,-71.6921535:17.2754337,-71.6920375:17.2753177,-71.6915065:17.2752007,-71.6909765:17.2751747,-71.6908605:17.2751497,-71.6907445:17.2750367,-71.6902135:17.2749227,-71.6896825:17.2748977,-71.6895655:17.2748727,-71.6894495:17.2747627,-71.6889175:17.2746527,-71.6883855:17.2746287,-71.6882695:17.2746047,-71.6881525:17.2744977,-71.6876205:17.2743907,-71.6870875:17.2743677,-71.6869705:17.2743437,-71.6868535:17.2742407,-71.6863205:17.2741357,-71.6857875:17.2741137,-71.6856705:17.2740917,-71.6855535:17.2739907,-71.6850205:17.2738897,-71.6844865:17.2738687,-71.6843695:17.2738467,-71.6842525:17.2737497,-71.6837175:17.2736517,-71.6831835:17.2736307,-71.6830665:17.2736097,-71.6829485:17.2735157,-71.6824135:17.2734217,-71.6818785:17.2734017,-71.6817615:17.2733807,-71.6816435:17.2732897,-71.6811085:17.2731987,-71.6805725:17.2731797,-71.6804545:17.2731597,-71.6803375:17.2730727,-71.6798015:17.2729847,-71.6792655:17.2729657,-71.6791475:17.2729467,-71.6790295:17.2728627,-71.6784925:17.2727777,-71.6779565:17.2727597,-71.6778385:17.2727417,-71.6777205:17.2726607,-71.6771835:17.2725797,-71.6766455:17.2725627,-71.6765275:17.2725447,-71.6764095:17.2724667,-71.6758725:17.2723887,-71.6753345:17.2723727,-71.6752165:17.2723557,-71.6750985:17.2722817,-71.6745595:17.2722067,-71.6740215:17.2721907,-71.6739035:17.2721747,-71.6737855:17.2721037,-71.6732465:17.2720317,-71.6727075:17.2720167,-71.6725895:17.2720017,-71.6724705:17.2719337,-71.6719315:17.2718657,-71.6713925:17.2718507,-71.6712735:17.2718367,-71.6711555:17.2717717,-71.6706155:17.2717067,-71.6700765:17.2716937,-71.6699575:17.2716787,-71.6698395:17.2716177,-71.6692995:17.2715567,-71.6687595:17.2715437,-71.6686405:17.2715297,-71.6685215:17.2714727,-71.6679815:17.2714137,-71.6674405:17.2714017,-71.6673225:17.2713887,-71.6672035:17.2713347,-71.6666625:17.2712797,-71.6661215:17.2712677,-71.6660035:17.2712557,-71.6658845:17.2712047,-71.6653435:17.2711527,-71.6648015:17.2711427,-71.6646835:17.2711317,-71.6645645:17.2710837,-71.6640225:17.2710347,-71.6634815:17.2710247,-71.6633625:17.2710147,-71.6632435:17.2709697,-71.6627015:17.2709247,-71.6621595:17.2709157,-71.6620405:17.2709058,-71.6619215:17.2708638,-71.6613795:17.2708228,-71.6608375:17.2708138,-71.6607185:17.2708048,-71.6605995:17.2707668,-71.6600575:17.2707278,-71.6595145:17.2707208,-71.6593955:17.2707118,-71.6592765:17.2706778,-71.6587335:17.2706418,-71.6581915:17.2706348,-71.6580725:17.2706278,-71.6579535:17.2705958,-71.6574105:17.2705638,-71.6568675:17.2705578,-71.6567485:17.2705508,-71.6566295:17.2705228,-71.6560855:17.2704938,-71.6555425:17.2704888,-71.6554235:17.2704818,-71.6553045:17.2704578,-71.6547615:17.2704318,-71.6542185:17.2704268,-71.6540985:17.2704218,-71.6539795:17.2703998,-71.6534365:17.2703778,-71.6528925:17.2703738,-71.6527735:17.2703698,-71.6526545:17.2703508,-71.6521105:17.2703328,-71.6515675:17.2703288,-71.6514475:17.2703248,-71.6513285:17.2703098,-71.6507845:17.2702948,-71.6502415:17.2702918,-71.6501215:17.2702888,-71.6500025:17.2702768,-71.6494585:17.2702648,-71.6489155:17.2702628,-71.6487955:17.2702608,-71.6486765:17.2702528,-71.6481325:17.2702438,-71.6475885:17.2702438,-71.6475855:17.2701888,-71.6434225:17.2701788,-71.6425345:17.2701708,-71.6412075:17.2701708,-71.6398815:17.2701788,-71.6385555:17.2701948,-71.6372285:17.2702188,-71.6359025:17.2702518,-71.6345765:17.2702918,-71.6332505:17.2703398,-71.6319255:17.2703968,-71.6306005:17.2704618,-71.6292755:17.2705338,-71.6279515:17.2706148,-71.6266275:17.2707038,-71.6253045:17.2708008,-71.6239815:17.2709058,-71.6226595:17.2710187,-71.6213385:17.2711397,-71.6200175:17.2712687,-71.6186985:17.2714057,-71.6173795:17.2715507,-71.6160615:17.2717037,-71.6147445:17.2718647,-71.6134285:17.2720337,-71.6121145:17.2722117,-71.6108005:17.2723967,-71.6094885:17.2725897,-71.6081765:17.2727907,-71.6068675:17.2729997,-71.6055585:17.2732167,-71.6042515:17.2734427,-71.6029455:17.2736757,-71.6016415:17.2739157,-71.6003395:17.2741647,-71.5990385:17.2744217,-71.5977395:17.2746867,-71.5964415:17.2749587,-71.5951455:17.2752397,-71.5938515:17.2755277,-71.5925595:17.2758246,-71.5912695:17.2761286,-71.5899815:17.2764406,-71.5886955:17.2767596,-71.5874115:17.2770876,-71.5861295:17.2774226,-71.5848495:17.2777656,-71.5835715:17.2781166,-71.5822965:17.2784756,-71.5810235:17.2788416,-71.5797535:17.2792166,-71.5784855:17.2795976,-71.5772195:17.2799876,-71.5759565:17.2803845,-71.5746965:17.2807895,-71.5734385:17.2812025,-71.5721835:17.2816225,-71.5709305:17.2820505,-71.5696815:17.2824855,-71.5684345:17.2829285,-71.5671905:17.2833795,-71.5659505:17.2838375,-71.5647125:17.2843035,-71.5634775:17.2847765,-71.5622455:17.2852574,-71.5610175:17.2857454,-71.5597915:17.2862414,-71.5585695:17.2867444,-71.5573505:17.2872244,-71.5562075:17.2874394,-71.5556675:17.2879344,-71.5544445:17.2884374,-71.5532255:17.2889474,-71.5520105:17.2894644,-71.5507975:17.2899893,-71.5495885:17.2905213,-71.5483835:17.2910613,-71.5471815:17.2916073,-71.5459835:17.2921613,-71.5447885:17.2927233,-71.5435975:17.2932913,-71.5424105:17.2938673,-71.5412265:17.2944502,-71.5400475:17.2950402,-71.5388715:17.2956382,-71.5376995:17.2957223,-71.537524:18.0230727,-70.1122542:17.9374762,-68.7724204:17.9328141,-68.757632:17.9323881,-68.756377:17.9319701,-68.75512:17.9315602,-68.753859:17.9311572,-68.752596:17.9307622,-68.75133:17.9303752,-68.750062:17.9299962,-68.748791:17.9296242,-68.747518:17.9292603,-68.746242:17.9289033,-68.744964:17.9285553,-68.743684:17.9285543,-68.74368:17.9260844,-68.734495:17.9260124,-68.734228:17.9256724,-68.732944:17.9253414,-68.731659:17.9250174,-68.730371:17.9247024,-68.729082:17.9243945,-68.72779:17.9240945,-68.726496:17.9238015,-68.7252:17.9235175,-68.723903:17.9232405,-68.722603:17.9229725,-68.721302:17.9227115,-68.719999:17.9224585,-68.718694:17.9222135,-68.717388:17.9219766,-68.71608:17.9217476,-68.71477:17.9215266,-68.713459:17.9213136,-68.712147:17.9211086,-68.710833:17.9209116,-68.709518:17.9207226,-68.708201:17.9205416,-68.706884:17.9203686,-68.705565:17.9202026,-68.704245:17.9200456,-68.702924:17.9198966,-68.701602:17.9197556,-68.700279:17.9196227,-68.698955:17.9194977,-68.69763:17.9193807,-68.696305:17.9192717,-68.694978:17.9191707,-68.693652:17.9190777,-68.692324:17.9189937,-68.690996:17.9189167,-68.689667:17.9188477,-68.688338:17.9187877,-68.687008:17.9187347,-68.685678:17.9186907,-68.684348:17.9186537,-68.683017:17.9186257,-68.681686:17.9186057,-68.680355:17.9185937,-68.679024:17.9185897,-68.677693:17.9185897,-68.677689:17.9185897,-68.67103:17.9185927,-68.669858:17.9186037,-68.668527:17.9186227,-68.667196:17.9186507,-68.665865:17.9186857,-68.664535:17.9187287,-68.663204:17.9187807,-68.661874:17.9188397,-68.660544:17.9189077,-68.659215:17.9189837,-68.657886:17.9190677,-68.656558:17.9191587,-68.65523:17.9192587,-68.653903:17.9193667,-68.652577:17.9194827,-68.651251:17.9196067,-68.649926:17.9197386,-68.648602:17.9198786,-68.647279:17.9200276,-68.645957:17.9201836,-68.644636:17.9203476,-68.643316:17.9205196,-68.641997:17.9206996,-68.640679:17.9208876,-68.639362:17.9210846,-68.638047:17.9212886,-68.636733:17.9215006,-68.63542:17.9217206,-68.634109:17.9219486,-68.6328:17.9221845,-68.631491:17.9224285,-68.630185:17.9226805,-68.62888:17.9229395,-68.627577:17.9230735,-68.626928:17.9228305,-68.626087:17.9224695,-68.624811:17.9221165,-68.623532:17.9217716,-68.62225:17.9214336,-68.620966:17.9211036,-68.619681:17.9207816,-68.618393:17.9204676,-68.617102:17.9201616,-68.61581:17.9198636,-68.614516:17.9195727,-68.61322:17.9192907,-68.611922:17.9190157,-68.610622:17.9187487,-68.60932:17.9184897,-68.608017:17.9182387,-68.606712:17.9179957,-68.605405:17.9177597,-68.604097:17.9175327,-68.602787:17.9173137,-68.601476:17.9171028,-68.600163:17.9168988,-68.598849:17.9167038,-68.597534:17.9165158,-68.596217:17.9163368,-68.594899:17.9161658,-68.59358:17.9160018,-68.59226:17.9158468,-68.590939:17.9156998,-68.589616:17.9155598,-68.588293:17.9154288,-68.586969:17.9153058,-68.585644:17.9151898,-68.584319:17.9150828,-68.582992:17.9149838,-68.581665:17.9149668,-68.581417:17.9148688,-68.580542:17.9147299,-68.579218:17.9145979,-68.577894:17.9144749,-68.57657:17.9143589,-68.575244:17.9142519,-68.573918:17.9141529,-68.57259:17.9140609,-68.571263:17.9139779,-68.569934:17.9139029,-68.568606:17.9138359,-68.567276:17.9137769,-68.565947:17.9137259,-68.564617:17.9136829,-68.563286:17.9136479,-68.561956:17.9136219,-68.560625:17.9136029,-68.559294:17.9135919,-68.557963:17.9135899,-68.556632:17.9135949,-68.5553:17.9136089,-68.553969:17.9136309,-68.552638:17.9136609,-68.551308:17.9136989,-68.549977:17.9137449,-68.548647:17.9137989,-68.547317:17.9138609,-68.545987:17.9139309,-68.544658:17.9140089,-68.54333:17.9140959,-68.542002:17.9141899,-68.540674:17.9142919,-68.539347:17.9144029,-68.538021:17.9145209,-68.536696:17.9146479,-68.535371:17.9147819,-68.534048:17.9149248,-68.532725:17.9150758,-68.531403:17.9152338,-68.530082:17.9154008,-68.528762:17.9155758,-68.527444:17.9157588,-68.526126:17.9159488,-68.52481:17.9161478,-68.523495:17.9163548,-68.522182:17.9165688,-68.52087:17.9167918,-68.519559:17.9170218,-68.51825:17.9172608,-68.5169421:17.9186481,-68.5119411:17.9219749,-68.501089:17.9258147,-68.4905478:17.9299439,-68.4807683:17.9345218,-68.4712513:17.9398522,-68.4614819:17.9455367,-68.4522361:17.951534,-68.4435152:17.9578731,-68.4352139:17.9647647,-68.4270841:17.9720001,-68.4193834:17.979634,-68.4120611:17.9876087,-68.4051773:17.9963573,-68.3983986:18.0055647,-68.3920549:18.0142518,-68.3867257:18.0232971,-68.3818153:18.5102398,-68.1329466:18.514971,-68.1306088:18.5197522,-68.1284071:18.5246277,-68.1263167:18.5295297,-68.1243789:18.5343859,-68.1226038:18.5393025,-68.120955:18.5442657,-68.1194374:18.5491241,-68.1180899:18.5541584,-68.1168479:18.5591662,-68.1157475:18.5641057,-68.1147943:18.5692328,-68.1139595:18.574446,-68.1132451:18.5797457,-68.1126795:18.5847379,-68.112281:18.5896089,-68.1120131:18.6309977,-68.1102984:18.6354107,-68.1101725:18.6398527,-68.1101463:18.6441726,-68.1102162:18.6485806,-68.1104012:18.6528536,-68.1106731:18.6571334,-68.1110362:18.6614186,-68.1115068:18.6655482,-68.1120538:18.6698104,-68.1127106:18.6740143,-68.1134631:18.6782564,-68.1143148:18.6826181,-68.1152993:18.686729,-68.1163226:18.6907308,-68.1174211:18.6950528,-68.1187048:18.6993321,-68.120091:19.9026658,-68.5255564:19.9168591,-68.5310036:19.9760007,-68.5501797:19.9944356,-68.5575167:20.0144152,-68.5681309:20.0336124,-68.5814216:20.0514077,-68.5971162:20.7865892,-69.3268296:20.8349063,-69.3874371:20.8639499,-69.4285053:20.9012834,-69.4862229:20.934461,-69.5561499:20.9790318,-69.6438361:21.0132282,-69.7059934:21.0505245,-69.7648209:21.0726164,-69.8147227:21.291227,-70.4504474:21.3011326,-70.4913582:21.303433,-70.5311188:21.2984021,-70.5726568:21.2854647,-70.6134573:21.2662868,-70.6489881:21.240777,-70.6802818:21.2090402,-70.706417:21.198835,-70.7127484:21.1977415,-70.7488569:21.1961409,-70.7902668:21.1917502,-70.8295096:21.1829686,-70.875816:21.1690633,-70.9268316:21.1489191,-70.9790513 && boundary -> administrative || last_edit_user_name ->  || note -> 12 nm measured from archipelagic baselines || last_edit_changeset -> 0 || last_edit_time -> 1509672103000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> República Dominicana || maritime -> yes || last_edit_version -> 6 || border_type -> territorial\n50634190000000 && 18.6484163,-71.9117199:18.6485692,-71.9133366:18.6476552,-71.9143002:18.6464423,-71.9146536:18.6447051,-71.916617:18.637184,-71.9215067:18.6347972,-71.9256975:18.6352623,-71.9276399:18.6375029,-71.9316149:18.6439233,-71.9411309:18.6479426,-71.9445725:18.648271,-71.9464513:18.6519735,-71.9531852:18.6506985,-71.9552821:18.6535046,-71.9624301:18.6546278,-71.963774 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672098000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 5 || border_type -> nation\n254684884000000 && 19.2290479,-71.6394109:19.2277847,-71.6383787:19.2263215,-71.6393016:19.2261683,-71.6407869:19.2258276,-71.6409078:19.2254141,-71.6405078:19.2246685,-71.6407034:19.224587,-71.640192:19.2250591,-71.6397377:19.2255982,-71.6392308:19.2252595,-71.6384776:19.2245408,-71.6386633:19.2242439,-71.6382228:19.2253667,-71.6369457:19.2251203,-71.6358202:19.2246554,-71.6360576:19.2239822,-71.6367459:19.2226548,-71.6362219:19.2207363,-71.6349593:19.2176198,-71.6340966:19.2172752,-71.6340021:19.2171216,-71.6339599:19.216609,-71.6338274:19.2147935,-71.6312991:19.2129109,-71.6300265:19.2122332,-71.6299834:19.2118764,-71.6292305:19.2111063,-71.6285812:19.2101158,-71.6291905:19.2095349,-71.6303306:19.2078338,-71.6336831:19.2070262,-71.6329586:19.2078883,-71.6302714:19.2081073,-71.627949:19.2089791,-71.6277506:19.2091698,-71.6268348:19.2076182,-71.6263819:19.2078785,-71.6258161:19.2085495,-71.6254983:19.2077602,-71.6238045:19.2077593,-71.6227785:19.2060326,-71.6221297:19.2061058,-71.6241422:19.2050575,-71.6240687:19.2038799,-71.6238362:19.2028535,-71.6248282:19.2041344,-71.626377:19.2054331,-71.6277945:19.2060349,-71.6276397:19.2056411,-71.6290357:19.2055134,-71.6294886:19.2052131,-71.630554:19.2059058,-71.6314139:19.2052562,-71.6329876:19.2049899,-71.6336365:19.2034108,-71.6351222:19.2028932,-71.6349522:19.2018665,-71.6355621:19.2020983,-71.6378658:19.2010782,-71.6383426:19.200507,-71.6372143:19.1986957,-71.637859:19.1982831,-71.6384945:19.197228,-71.638535:19.1969606,-71.6391473:19.1969245,-71.6392156:19.1952646,-71.6382708:19.1951497,-71.6374182:19.1942556,-71.6373889:19.1938025,-71.6377781:19.1936291,-71.6386556:19.1929067,-71.6396202:19.1922038,-71.6378571:19.1920555,-71.6375001:19.1911198,-71.6371677:19.1899024,-71.6372209:19.1893673,-71.6370798:19.1899593,-71.6358994:19.189713,-71.6352584:19.1887438,-71.6340908:19.1883436,-71.633434:19.1889999,-71.6323092:19.1894373,-71.6305715:19.1885102,-71.629726:19.1868434,-71.6298833:19.1867986,-71.627937:19.1862289,-71.6268943:19.1846432,-71.6275345:19.1835147,-71.6284978:19.1823387,-71.6278569:19.1817659,-71.6276311:19.1814767,-71.6285868:19.1814767,-71.6300686:19.1824603,-71.6305425:19.1832925,-71.6316275:19.1826176,-71.6322301:19.1817475,-71.6339956:19.1820908,-71.6349955:19.1832263,-71.6353999:19.1821698,-71.6363522:19.1806225,-71.6365092:19.1803056,-71.6370645:19.179232,-71.6370863:19.1783996,-71.637958:19.1784848,-71.6411288:19.1792349,-71.642168:19.1800709,-71.6444496:19.1780699,-71.6436069:19.1774335,-71.6438478:19.1775087,-71.6454896:19.176603,-71.6472935:19.1760835,-71.6472407:19.1760393,-71.6450918:19.1754775,-71.6434885:19.1746738,-71.6429728:19.1748541,-71.6400057:19.1742867,-71.6390864:19.1731933,-71.6390136:19.1708497,-71.6362213:19.1688455,-71.633726:19.1682052,-71.6342519:19.1689903,-71.6357176:19.168952,-71.6358361:19.1687427,-71.636492:19.1666965,-71.6361347:19.1662861,-71.6373777:19.1681031,-71.6404941:19.168181,-71.6412998:19.1667451,-71.6417469:19.1665963,-71.6429846:19.1682104,-71.6443861:19.1681175,-71.6452238:19.1686078,-71.6463432 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672101000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 7 || border_type -> nation\n48612170000000 && 18.0315043,-71.7587643:17.953787,-71.8295092 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672093000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> República Dominicana || maritime -> yes || last_edit_version -> 10 || border_type -> territorial\n48998245000000 && 18.9513012,-71.8831231:18.950767,-71.8831234:18.950341,-71.883279:18.9492559,-71.8839602:18.9482089,-71.8842394:18.9467376,-71.8841239:18.9444251,-71.8833821:18.9421039,-71.8827586 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || boat -> no || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672096000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Artibonite || name:fr -> Rivière de l'Artibonite || name:ht -> Latibonit || name:en -> Artibonite River || last_edit_version -> 6 || name:es -> Río Artibonite\n385474749000000 && 17.953787,-71.8295092:17.881115,-71.900531:17.8410354,-71.9396881 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672106000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> República Dominicana || maritime -> yes || last_edit_version -> 2 || border_type -> territorial\n180241398000000 && 19.5030529,-71.688106:19.4991875,-71.6904681:19.4950015,-71.6915526:19.4938082,-71.6919385:19.4930747,-71.6908209:19.4923429,-71.6907788:19.4914487,-71.6917296:19.4899815,-71.6939198:19.4894622,-71.6941492:19.4881488,-71.6938904:19.4873612,-71.6932688:19.4855485,-71.6933437:19.485085,-71.6926774:19.4845138,-71.6925368:19.4834099,-71.6928733:19.4818417,-71.6920105:19.4805406,-71.692922:19.4786433,-71.6938075:19.4771256,-71.694219:19.4756598,-71.6935256:19.4749153,-71.6932837:19.4745083,-71.6927401:19.4743613,-71.6911158:19.4736441,-71.6899028:19.4728059,-71.6894726:19.4724694,-71.6888419:19.4718679,-71.688521:19.4699249,-71.6898642:19.4682421,-71.690631:19.467533,-71.6903505:19.4670027,-71.6894859:19.46754,-71.6877716:19.467634,-71.6869895:19.4667665,-71.686931:19.4649239,-71.6873491:19.4629264,-71.6876846:19.4625009,-71.6871224:19.4618275,-71.6863272:19.4612965,-71.6854246:19.4602309,-71.6853891:19.4601467,-71.6862187:19.4597808,-71.6869302:19.459259,-71.6870265:19.4557025,-71.6855193:19.4548644,-71.6846039:19.4521194,-71.681577:19.4510695,-71.6814176:19.4516776,-71.6801206:19.4514841,-71.6799058:19.4492548,-71.6799036:19.4471393,-71.6802035:19.4462551,-71.6797268:19.445236,-71.6797666:19.4454774,-71.6791527:19.4449502,-71.6784594:19.4440208,-71.6784688:19.4429798,-71.6802692:19.4421163,-71.6804294:19.4415032,-71.6799758:19.4404337,-71.680711:19.438244,-71.6809079:19.4366087,-71.6808079:19.4358223,-71.6797584:19.4342418,-71.6797049:19.43325,-71.6802483:19.4330468,-71.6814797:19.4330236,-71.6836588:19.4326824,-71.6837608:19.431525,-71.6826523:19.4303438,-71.6837037:19.4280291,-71.6844644:19.4271604,-71.6853287:19.4226994,-71.6866749:19.4223368,-71.6890606:19.4221535,-71.6894067:19.4202188,-71.6892272:19.4149011,-71.6906191:19.414428,-71.6904099:19.4143463,-71.6898884:19.4145038,-71.6891243:19.414255,-71.6888439:19.4120703,-71.6888121:19.4113351,-71.6895785:19.4101469,-71.6897547:19.4091241,-71.6897888:19.4084998,-71.6900068:19.4074431,-71.6899617:19.4070005,-71.6904367:19.4042075,-71.6917013:19.4040954,-71.6924836:19.4032963,-71.692709:19.4028857,-71.6939539:19.4019809,-71.6943241:19.4014135,-71.6948873 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672101000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 8 || border_type -> nation\n385491702000000 && 20.9793764,-71.1800461:20.0002137,-72.0294134:19.9850956,-72.0411041:19.9697898,-72.0501299:19.9530577,-72.0574706 && boundary -> administrative || last_edit_user_name ->  || note -> 12 nm measured from archipelagic baselines || last_edit_changeset -> 0 || last_edit_time -> 1509672106000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> República Dominicana || maritime -> yes || last_edit_version -> 2 || border_type -> territorial\n180241394000000 && 19.5659364,-71.7238622:19.5846797,-71.743137 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672100000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 7 || border_type -> nation\n48581237000000 && 18.1662802,-71.7775624:18.1661468,-71.777536:18.1660257,-71.7774729:18.1659455,-71.7773905:18.165884,-71.7772802:18.1658684,-71.7771833:18.165876,-71.7770753:18.1659272,-71.776843:18.1659553,-71.7766176:18.1659145,-71.7762609:18.1659502,-71.7757782:18.1660012,-71.7755072:18.1661248,-71.7752377:18.1663491,-71.7750754:18.1667173,-71.7749574:18.167045,-71.7747751:18.1673329,-71.7745525:18.1675266,-71.7742306:18.1677076,-71.7737478:18.1679038,-71.7728493:18.1679879,-71.7718086:18.1680058,-71.7713151:18.1679726,-71.7709074:18.1679344,-71.7704996:18.1678605,-71.7701241:18.1677637,-71.7695556:18.1677116,-71.7689851:18.1677764,-71.7684236:18.1679319,-71.7677879:18.1680899,-71.7672757:18.1681995,-71.7669135:18.1682621,-71.7666113:18.168253,-71.7663047:18.1680695,-71.7660365:18.1678172,-71.7659292:18.1675878,-71.7659319:18.1673636,-71.7659882:18.1671087,-71.7661679:18.1669048,-71.7663556:18.1665877,-71.7663512:18.1664079,-71.7662027:18.166255,-71.7661115:18.1659721,-71.7658809:18.1657707,-71.7657012:18.1656076,-71.7656555:18.1654343,-71.765669:18.1652662,-71.7658192:18.1652064,-71.7659757:18.1652268,-71.7662707:18.1652483,-71.7668546:18.165312,-71.767576:18.165233,-71.7680347:18.1650418,-71.7683325:18.1647029,-71.7685711:18.1645132,-71.7685989:18.1643858,-71.7685586:18.16416,-71.7684665:18.1639995,-71.7684236:18.1638072,-71.768387:18.1636172,-71.7683941:18.1632146,-71.7684049:18.1629051,-71.7684031:18.1626808,-71.7684406:18.162478,-71.7685014:18.1622475,-71.7685533:18.1620105,-71.7685184:18.1617964,-71.7684272:18.1615682,-71.7683029:18.161274,-71.7681456:18.1607908,-71.7679166:18.1604839,-71.7677727:18.1600543,-71.767619:18.1598595,-71.7675126:18.1596414,-71.7672381:18.1595613,-71.7670968:18.1595267,-71.7668921:18.1595395,-71.7666211:18.1596058,-71.7660714:18.1597256,-71.7653981:18.1597663,-71.7649824:18.1597179,-71.7647168:18.1596644,-71.7645625:18.1595714,-71.7644821:18.1593483,-71.7644178:18.1590743,-71.7644218:18.1586896,-71.7645358:18.1580855,-71.7646793:18.1575235,-71.7648549:18.1572051,-71.7648867:18.1570305,-71.7649095:18.1568622,-71.7648563:18.1565946,-71.764631:18.1563422,-71.7639835:18.1561597,-71.7626248:18.1558532,-71.762101:18.1555144,-71.7617149:18.1551944,-71.7615391:18.1547564,-71.7612797:18.1542781,-71.7611416:18.1538434,-71.7610686:18.1532451,-71.7610506:18.1527407,-71.7609808:18.152476,-71.760858:18.1522746,-71.7606415:18.1521294,-71.760394:18.1521049,-71.76017:18.1521511,-71.7599486:18.1522466,-71.7597154:18.152425,-71.7594498:18.1528409,-71.7589647:18.1529067,-71.7587096:18.1529019,-71.7584676:18.1528226,-71.7582268:18.1527082,-71.7580783:18.1521464,-71.7578629:18.150673,-71.7573739:18.1490229,-71.7572609:18.1482018,-71.7575455:18.1475164,-71.7581846:18.1471058,-71.7585754:18.146673,-71.7587039:18.1461067,-71.7586666:18.1456912,-71.7585325:18.1453369,-71.7581838:18.1451636,-71.7578673:18.1451518,-71.757574:18.1452655,-71.7573416:18.1454491,-71.7573094:18.1459286,-71.7573707:18.1464992,-71.7574436:18.1468254,-71.7574167:18.1471008,-71.7572051:18.1473032,-71.7568993:18.1472842,-71.7563868:18.1472229,-71.756:18.1468101,-71.7555338:18.1464606,-71.7547679:18.1461751,-71.7538993:18.1459537,-71.7535007:18.1457396,-71.7533934:18.1455005,-71.7534576:18.1449041,-71.7539457:18.1442312,-71.754332:18.1436802,-71.7545575:18.1431046,-71.754627:18.1426106,-71.7545469:18.1422231,-71.7543914:18.1419678,-71.7542247:18.1419202,-71.7540479:18.1419529,-71.7538978:18.1423247,-71.7532913:18.142814,-71.7526475:18.1434309,-71.7517141:18.1438999,-71.7512045:18.1453527,-71.7507002:18.1466016,-71.7503435:18.1473917,-71.7498044:18.1481207,-71.749512:18.1489032,-71.7490695:18.1494486,-71.7487476:18.1497162,-71.7481817:18.1494282,-71.748077:18.1492625,-71.7478517:18.1491886,-71.7471141:18.1487273,-71.7468647:18.1477791,-71.7467359:18.1463722,-71.746972:18.1456891,-71.7469398:18.14521,-71.7465643:18.1432728,-71.7459313:18.1419474,-71.7461137:18.1409483,-71.7464034:18.1410298,-71.7470793:18.1418761,-71.7476264:18.1421004,-71.7485169:18.1412235,-71.7486886:18.1402346,-71.7486564:18.1402142,-71.7496542:18.1395617,-71.7502121:18.1387154,-71.7502121:18.1381241,-71.7481522:18.1377264,-71.7466716:18.1377264,-71.7452768:18.1370739,-71.7448906:18.1363296,-71.7452232:18.1351469,-71.7455236:18.1342803,-71.7452554:18.1335971,-71.7445365:18.1328018,-71.7434744:18.1317211,-71.7429165:18.131252,-71.7435495:18.1307524,-71.7444292:18.1309869,-71.7451803:18.1309971,-71.7466608:18.1306709,-71.747326:18.1300693,-71.747487:18.1290395,-71.7461673:18.1270716,-71.7448906:18.1257664,-71.7448155:18.1255421,-71.7449711:18.1255625,-71.7454807:18.1259449,-71.7467413:18.1262916,-71.7477981:18.1259755,-71.7481682:18.12543,-71.748018:18.1240687,-71.7466233:18.1234365,-71.7463282:18.1227789,-71.7455933:18.1219631,-71.7455129:18.1217337,-71.7457167:18.121749,-71.7464194:18.1215553,-71.7467306:18.119725,-71.7470095:18.1186033,-71.746634:18.1180017,-71.7469237:18.1158807,-71.7470632:18.1144073,-71.7475191:18.1141931,-71.747841:18.1141268,-71.7490105:18.1137393,-71.7490963:18.1134793,-71.7495147:18.1128114,-71.7497937:18.1121945,-71.7497347:18.1114042,-71.7490373:18.1103998,-71.7488334:18.1098593,-71.7488334:18.1082889,-71.7479161:18.1076669,-71.7480502:18.1067032,-71.7473689:18.1058262,-71.7464677:18.1051634,-71.7464302:18.1045158,-71.7461727:18.1035725,-71.7459098:18.1028791,-71.745942:18.1020276,-71.7459849:18.1013443,-71.7462156:18.1008395,-71.7460815:18.1000849,-71.7456201:18.0992741,-71.7448155:18.0989631,-71.7441396:18.0990498,-71.7434905:18.0994526,-71.7430399:18.0999676,-71.7426536:18.100401,-71.742541:18.1006814,-71.7418919:18.100763,-71.7413286:18.1005285,-71.7401914:18.1009721,-71.7390756:18.1009772,-71.738008:18.1005591,-71.7374394:18.0997075,-71.737257:18.098545,-71.7375735:18.0975099,-71.7375091:18.0968623,-71.7359535:18.0955008,-71.7353848:18.0947513,-71.7356316:18.0942872,-71.7362592:18.0935122,-71.736624:18.0931501,-71.7369888:18.0925841,-71.7381207:18.0919875,-71.7388985:18.0911105,-71.7397354:18.0904731,-71.7398212:18.089647,-71.740127:18.0893716,-71.7400143:18.0893716,-71.7397032:18.0898458,-71.7394081:18.0895909,-71.7383835:18.089239,-71.7372248:18.0881835,-71.736844:18.087133,-71.736903:18.0854618,-71.7372178:18.0849862,-71.7370371:18.0839256,-71.737284:18.0832525,-71.7371981:18.0831062,-71.737075:18.0827425,-71.736769:18.0816614,-71.7358463:18.0809067,-71.7353099:18.0797669,-71.7350549:18.0788195,-71.7355182:18.0784997,-71.7356746:18.0782549,-71.7358892:18.0779286,-71.7363184:18.077949,-71.7368977:18.0775614,-71.7381423:18.0773982,-71.7387216:18.0770922,-71.7391079:18.0765211,-71.7390006:18.0756439,-71.7387002:18.0745653,-71.738456:18.0740808,-71.7385472:18.0737544,-71.7386142:18.0726784,-71.7386732:18.0721607,-71.7386196:18.0716584,-71.738456:18.0708093,-71.7383996:18.0690447,-71.7383728:18.0684557,-71.7384613:18.0676933,-71.7391775:18.0673286,-71.7396254:18.0668518,-71.7401484:18.0664616,-71.7405561:18.0659669,-71.7411006:18.0658573,-71.7416397:18.0661097,-71.7423479:18.066148,-71.7428736:18.0657935,-71.7434449:18.0654901,-71.743638:18.0651076,-71.7438338:18.0646518,-71.7440752:18.0638428,-71.7439974:18.0628967,-71.7439008:18.0617386,-71.7439272:18.0612232,-71.7440587:18.0606037,-71.74449:18.0602937,-71.7445193:18.0598056,-71.7444188:18.0595353,-71.744259:18.0591904,-71.7441996:18.058677,-71.7440113:18.0576229,-71.7437877:18.0573577,-71.7437405:18.0570117,-71.7436283:18.0563903,-71.7436633:18.0558364,-71.7436795:18.0549315,-71.7437686:18.0543731,-71.7438244:18.0537404,-71.7440135:18.0531093,-71.7440045:18.0522249,-71.7445998:18.0519543,-71.744727:18.051434,-71.744904:18.0509648,-71.7452124:18.0505789,-71.7455237:18.0504259,-71.7457476:18.050066,-71.7461395:18.0497172,-71.7464907:18.0493847,-71.7470052:18.0488527,-71.7474906:18.0482743,-71.7478229:18.0479291,-71.7479386:18.0473899,-71.7482525:18.0469152,-71.748495:18.0465403,-71.7487898:18.0462699,-71.7491697:18.0461408,-71.7494515:18.0461364,-71.7498031:18.0461387,-71.750113:18.0460989,-71.7503688:18.0460038,-71.7506071:18.0457393,-71.7509769:18.0453856,-71.7515772:18.0450873,-71.7519821:18.044649,-71.7523177:18.0442966,-71.7527155:18.0441437,-71.7529508:18.0439184,-71.7532947:18.0436719,-71.7537381:18.0433811,-71.7542509:18.0433537,-71.7543647:18.0433684,-71.7545119:18.0434177,-71.7547238:18.0433525,-71.754942:18.04326,-71.7550296:18.0431396,-71.7550809:18.0429014,-71.7551101:18.0426951,-71.755173:18.0423239,-71.7555137:18.0418564,-71.7560438:18.0415435,-71.7564276:18.0412707,-71.7567081:18.041123,-71.7568128:18.0409764,-71.7568461:18.0407221,-71.7568334:18.0403159,-71.7568729:18.0399519,-71.7569474:18.0395575,-71.7569525:18.0393087,-71.7569303:18.0390773,-71.7569598:18.0387156,-71.7570189:18.0383804,-71.7571051:18.0382448,-71.75716:18.0380183,-71.7572628:18.0378558,-71.7573524:18.0377169,-71.7574657:18.0374097,-71.7577312:18.0371964,-71.7578239:18.0369101,-71.757949:18.036624,-71.7580119:18.0364575,-71.7580361:18.0362064,-71.7580147:18.0360449,-71.7579598:18.0358447,-71.7578632:18.0356949,-71.7576713:18.0356306,-71.7574503:18.0355973,-71.7572678:18.0355603,-71.7569245:18.0355056,-71.7568052:18.0354309,-71.7567379:18.0352887,-71.756667:18.0351204,-71.7567072:18.0349419,-71.7569647:18.0347812,-71.7571095:18.0344241,-71.7572329:18.0341633,-71.7573575:18.0337278,-71.7575172:18.0327657,-71.758087:18.0324243,-71.7583562:18.0322219,-71.7585057:18.0320151,-71.7586143:18.0318674,-71.7586814:18.0317174,-71.7587182:18.0315043,-71.7587643 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672091000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Rivière des Pedernales || name:fr -> Rivière des Pedernales || last_edit_version -> 20 || name:es -> Rio Pedernales\n385486819000000 && 19.9530577,-72.0574706:19.9245878,-72.0313557 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672106000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> República Dominicana || maritime -> yes || last_edit_version -> 2 || border_type -> territorial\n28460950000000 && 19.6759241,-71.7560848:19.6762891,-71.756425:19.6766693,-71.7565759:19.6772425,-71.7566603:19.6776637,-71.7567418:19.6780042,-71.7567657:19.6783219,-71.7567418:19.678535,-71.7566714:19.6786866,-71.7564954:19.6787718,-71.7560629:19.6789189,-71.7558607:19.6795825,-71.75493:19.6798041,-71.7546497:19.6800618,-71.7543761:19.6804529,-71.7543781:19.6808282,-71.7543859:19.6815918,-71.7543904:19.6818356,-71.7546849:19.681735,-71.7553174:19.6818799,-71.7558839:19.6820946,-71.7561295:19.6825623,-71.7563563:19.6828287,-71.7562978:19.6830208,-71.756029:19.6829924,-71.7557458:19.6834062,-71.7550715:19.6839516,-71.7548725:19.6842301,-71.7551743:19.6843742,-71.7554982:19.6845182,-71.756011:19.6846876,-71.756182:19.6849926,-71.756155:19.6850095,-71.7554172:19.6849756,-71.7548594:19.6850519,-71.7543915:19.6854077,-71.7540856:19.6859837,-71.7541846:19.6862718,-71.7543645:19.6863311,-71.7546884:19.6862125,-71.7551473:19.6859498,-71.7555252:19.6858228,-71.756164:19.6858228,-71.7565149:19.6858482,-71.7570637:19.686043,-71.7575316:19.6862294,-71.7576575:19.6864412,-71.7576035:19.6866784,-71.7573876:19.6867885,-71.7568298:19.6867885,-71.7565509:19.6869325,-71.7562629:19.6873267,-71.7562289:19.687758,-71.7567124:19.6876023,-71.7575904:19.6876741,-71.7579721:19.6878898,-71.7586592:19.6878059,-71.7591936:19.6878898,-71.759499:19.6881414,-71.7597026:19.6885487,-71.7595881:19.6891118,-71.7588246:19.6899743,-71.7574759:19.6901301,-71.756827:19.6897313,-71.7560623:19.6898828,-71.7557456:19.6904523,-71.7557099:19.6909327,-71.7557454:19.6913227,-71.7558232:19.6916845,-71.7555911:19.6917102,-71.7555144:19.6917526,-71.7554437:19.6918302,-71.7553085:19.6918604,-71.7551907:19.6920892,-71.7550407:19.6922639,-71.7550381:19.6926311,-71.7551006:19.692797,-71.7552701:19.692851,-71.7554879:19.6928028,-71.7556849:19.6926623,-71.7558119:19.6924662,-71.7559187:19.6923257,-71.7560359:19.6922033,-71.7562125:19.692043,-71.7565004:19.6919946,-71.756737:19.6920301,-71.7569348:19.6921487,-71.7572026:19.6923138,-71.7574708:19.692508,-71.7577915:19.6927176,-71.7580803:19.6927984,-71.758345:19.6928479,-71.7586701:19.6929825,-71.7589962:19.693135,-71.7592113:19.6933495,-71.7592897:19.6935304,-71.7593315:19.693731,-71.7592633:19.6942074,-71.758884:19.694444,-71.7588562:19.6946068,-71.7588741:19.694754,-71.7591102:19.6949015,-71.7593382:19.6950223,-71.7598334:19.6947472,-71.7602693:19.6937967,-71.7607953:19.6932431,-71.7612572:19.6931155,-71.7616354:19.6933919,-71.7618781:19.6938437,-71.7619628:19.6952467,-71.7616241:19.6958932,-71.760828:19.6963012,-71.7603703:19.6965671,-71.7600725:19.6968651,-71.760016:19.6970694,-71.7600476:19.6977509,-71.7605025:19.6984514,-71.7603201:19.6987462,-71.7599502:19.6989505,-71.759633:19.6989962,-71.7592166:19.6991742,-71.7585025:19.6999927,-71.7582541:19.7016449,-71.7584243:19.7019452,-71.7586772 && boundary -> administrative || last_edit_changeset -> 0 || source:name -> haiti-tlm-50 || iso_country_code -> AAA || waterway -> river || source -> WorldView-2, DigitalGlobe, 2010-01-20 || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672088000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Río Dajabón || name:fr -> Rivière du Massacre || last_edit_version -> 18\n254605672000000 && 19.6456052,-71.7330457:19.6417367,-71.7372208:19.6337819,-71.745748:19.6146301,-71.7452958:19.5927135,-71.7436894:19.588494,-71.743441 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672101000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 7 || border_type -> nation\n180241391000000 && 19.6615297,-71.7349932:19.6615714,-71.735187:19.6617337,-71.7353836:19.6619746,-71.7355948:19.6622155,-71.735813:19.6624021,-71.7360187:19.662579,-71.7364598:19.662661,-71.73677:19.6627724,-71.7369548:19.6628994,-71.7370599:19.6630568,-71.7371333:19.6632295,-71.7371748:19.6634088,-71.7373283:19.6634977,-71.737505:19.6635182,-71.7377449:19.6635019,-71.7378966:19.6634319,-71.7381756:19.6634006,-71.7383191:19.6634449,-71.7384154:19.6636022,-71.7385048:19.6638195,-71.7386107:19.6640522,-71.7386768:19.6644249,-71.7386811:19.6645858,-71.7387007:19.6647753,-71.738765:19.6649148,-71.7388576:19.6651211,-71.7390806:19.6652461,-71.7392884:19.6653125,-71.7394668:19.6653181,-71.7396829:19.6652242,-71.7399924:19.6650468,-71.7404563:19.6649042,-71.7411242:19.6647798,-71.7418019:19.6646843,-71.7422956:19.6646199,-71.7426538:19.6645886,-71.7428282:19.6645397,-71.7432059:19.6645474,-71.7433806:19.6646194,-71.7435074:19.6648283,-71.7436451:19.6649452,-71.7438596:19.6649806,-71.7440054:19.6650803,-71.7440936:19.6653356,-71.7441445:19.6657185,-71.7442257:19.6659094,-71.7443342:19.666045,-71.7445004:19.6662641,-71.7449965:19.6665072,-71.7454062:19.6666965,-71.7457086:19.666914,-71.7459144:19.6671407,-71.7461008:19.6672491,-71.7462473:19.6672751,-71.7464124:19.6673008,-71.7466164:19.6674457,-71.7467632:19.6676279,-71.7468134:19.6677652,-71.7467856:19.667931,-71.7466126:19.6682621,-71.7463345:19.6685468,-71.7461529:19.6686748,-71.7461347:19.6688386,-71.7462041:19.6689831,-71.7463995:19.6692043,-71.7470279:19.6692845,-71.7472711:19.6693653,-71.7474367:19.6694743,-71.7475057:19.6696208,-71.7474586:19.6697766,-71.7473921:19.6699046,-71.7473837:19.6700773,-71.7474823:19.6701674,-71.7476286:19.6701744,-71.7478809:19.6702639,-71.7480951:19.6704354,-71.7483392:19.6706078,-71.7484766:19.6707806,-71.7485558:19.670907,-71.7487316:19.6710597,-71.7490434:19.6712579,-71.7493654:19.6715352,-71.7496856:19.6718516,-71.7500765:19.6722315,-71.7505166:19.6726572,-71.7509377:19.6731566,-71.7510257:19.6736699,-71.7513147:19.6741786,-71.7517983:19.6746847,-71.75213:19.6750369,-71.7527374:19.6754202,-71.7530102:19.6757649,-71.7534695:19.6759684,-71.7539155:19.6760568,-71.7541507:19.6760624,-71.7544567:19.676025,-71.7550887:19.6759695,-71.755505:19.6759637,-71.7557109:19.6759484,-71.7559538:19.6759241,-71.7560848 && boundary -> administrative || last_edit_changeset -> 0 || source:name -> haiti-tlm-50 || iso_country_code -> AAA || waterway -> river || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672099000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Río Dajabón || name:fr -> Rivière du Massacre || last_edit_version -> 8\n48429589000000 && 19.2795825,-71.7530406:19.2802281,-71.7548338:19.2813355,-71.7551827:19.2825831,-71.7547874:19.2828452,-71.7542975:19.2831248,-71.7537787:19.2841647,-71.7533779:19.284896,-71.753392:19.2862183,-71.7536417:19.2871206,-71.7536239:19.288242,-71.7532404:19.2892612,-71.7542089:19.2897737,-71.7551113:19.2901658,-71.7558451:19.2907311,-71.7561667:19.2913046,-71.7564406:19.2912773,-71.7579526:19.2908287,-71.7586078:19.2908307,-71.759739:19.2909249,-71.7604691:19.291468,-71.7610667:19.2928945,-71.7610861:19.2943507,-71.7607438:19.2957958,-71.7602875:19.2972667,-71.7577299:19.2976086,-71.7561451:19.2989792,-71.7560706:19.2995885,-71.7563247:19.3001161,-71.7565519:19.3010664,-71.7572082:19.300821,-71.7581256:19.3013721,-71.75917:19.3021776,-71.7598006:19.3027237,-71.7590387:19.3032843,-71.7590943:19.3035624,-71.7595071:19.3036619,-71.7615491:19.3045465,-71.762064:19.3047811,-71.7625632:19.3041999,-71.7628789:19.3031451,-71.7629472:19.302942,-71.7631984:19.3035925,-71.7642503:19.3041471,-71.7644771:19.3049663,-71.763843:19.3062589,-71.7629334:19.3067846,-71.7625523:19.307437,-71.7626915:19.3081005,-71.7644753:19.3096275,-71.7650728:19.3100578,-71.7654161:19.310653,-71.7653853:19.3114477,-71.7654077:19.3116558,-71.7659455:19.3118277,-71.7664745:19.3133622,-71.7664823:19.3142821,-71.7664357:19.3146756,-71.7667417:19.3158881,-71.7674214:19.3165363,-71.7673231:19.3167384,-71.7680322:19.3171281,-71.7691369:19.3175711,-71.7696891:19.318114,-71.769764:19.3191217,-71.7690691:19.3199449,-71.7686631:19.3207538,-71.7694839:19.3205218,-71.7706579:19.3202659,-71.7714996:19.3201815,-71.7728513:19.3204049,-71.7737408:19.3215819,-71.7739458:19.3226793,-71.774238:19.3229615,-71.7753925:19.323186,-71.7763483:19.3236617,-71.7767098:19.3257618,-71.7765354:19.3266223,-71.7767087:19.3270184,-71.7771574:19.3279368,-71.7780521:19.32903,-71.7791241:19.3294857,-71.7796477:19.3295358,-71.7801696:19.3291958,-71.7815517:19.329502,-71.7827107:19.3301271,-71.7843358:19.330743,-71.7848151:19.330867,-71.7853666:19.3311719,-71.7857705:19.3316482,-71.785936 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672090000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || waterway -> river || name -> Rio Libon || last_edit_version -> 11 || border_type -> nation\n48998248000000 && 19.1054541,-71.6920808:19.1056435,-71.6920544:19.1061095,-71.6924563:19.1066117,-71.6932621:19.1071609,-71.6940241:19.1075758,-71.6945408:19.1081861,-71.6954966:19.1086498,-71.6968786:19.1084546,-71.6984027:19.1077589,-71.6995264:19.1068158,-71.7010505:19.1065961,-71.7014767:19.1059859,-71.7024842:19.1050828,-71.70313:19.1044359,-71.7033883:19.1039599,-71.7036079:19.1033741,-71.7046412:19.1030202,-71.7050287:19.1014213,-71.7052999:19.1004066,-71.706178:19.1000614,-71.7065159:19.0995522,-71.706452:19.0991293,-71.7063881:19.0990085,-71.7061415:19.0985338,-71.7053926:19.0981023,-71.704735:19.0973428,-71.7044519:19.0967085,-71.7034206:19.0966597,-71.7026327:19.0966475,-71.7020127:19.0966109,-71.7010182:19.0959762,-71.6993391:19.0952683,-71.6988095:19.0948777,-71.6987708:19.0946092,-71.6988224:19.0939867,-71.699197:19.0929126,-71.6981637:19.0907888,-71.6968592:19.0888776,-71.6965134:19.0878419,-71.6966961:19.0870651,-71.697171:19.0866336,-71.6973172:19.0867026,-71.6980843:19.0867717,-71.6991438:19.0861914,-71.6999292:19.0846515,-71.7017258:19.0844018,-71.7036583:19.0853452,-71.7090316:19.0852171,-71.7099806:19.0836622,-71.7116998:19.0822413,-71.7121373:19.0809427,-71.7122976:19.0784676,-71.7121065:19.0780864,-71.7113094:19.0772738,-71.7096355:19.0764061,-71.707713:19.0760508,-71.7054145:19.076045,-71.7031653:19.0754684,-71.7023396:19.0747719,-71.7016283:19.0734884,-71.7012612:19.0722479,-71.7020007:19.0714733,-71.7031715:19.0695603,-71.7053243:19.0683173,-71.7072606:19.0681781,-71.7075623:19.0675922,-71.7088314:19.0669322,-71.7097754:19.067247,-71.7102197:19.0678448,-71.7103279:19.0686626,-71.7100005:19.0693531,-71.7101831:19.0701128,-71.710585:19.070389,-71.7118271:19.0707688,-71.713398:19.0710795,-71.7147132:19.070389,-71.716211:19.0671779,-71.7194624:19.0659004,-71.7204122:19.064312,-71.7203757:19.0635524,-71.7192432:19.0632762,-71.717307:19.0630345,-71.7168686:19.0626547,-71.7162475:19.061308,-71.7160283:19.0587832,-71.7168758:19.0583343,-71.7174238:19.059232,-71.7191408:19.0595428,-71.7205656:19.0586105,-71.7214424:19.0572984,-71.7222095:19.0563315,-71.7223191:19.0550194,-71.7222095:19.0538798,-71.7227575:19.0526022,-71.7246572:19.0511174,-71.7287854:19.0500469,-71.7322925:19.0499778,-71.7343017:19.0496966,-71.7372633 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || boat -> no || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672097000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Artibonite || name:fr -> Rivière de l'Artibonite || name:ht -> Latibonit || name:en -> Artibonite River || last_edit_version -> 11 || name:es -> Río Artibonite\n50448587000000 && 18.9818471,-71.8628762:18.9824649,-71.8627816:18.9828937,-71.8628528:18.9833273,-71.8630581:18.9835568,-71.8633162:18.9836619,-71.8637035:18.983454,-71.8640654:18.9828495,-71.8647663:18.9821349,-71.8668873:18.9816417,-71.8677843:18.9815166,-71.867915:18.9813109,-71.8681351 && boundary -> administrative || last_edit_changeset -> 0 || iso_country_code -> AAA || waterway -> river || source -> digitalglobe || boat -> no || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672098000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Artibonite || name:fr -> Rivière de l'Artibonite || name:ht -> Latibonit || name:en -> Artibonite River || last_edit_version -> 8 || name:es -> Río Artibonite\n50634191000000 && 18.651473,-71.8089308:18.6450119,-71.8069303:18.6353476,-71.80713:18.6340785,-71.8121345:18.6306125,-71.8153522:18.6332474,-71.8275655:18.629619,-71.8361643:18.6293592,-71.8443314:18.6347366,-71.8506333:18.6357576,-71.8523:18.6360058,-71.8541893:18.635288,-71.856068:18.6367051,-71.8603694:18.6364332,-71.8651755:18.6375636,-71.8685448:18.6370527,-71.8693023:18.6355812,-71.8693008:18.6279127,-71.8733805:18.62812,-71.8760754:18.627064,-71.8798919:18.6271108,-71.8805349:18.628027,-71.8808022:18.6331615,-71.8823088:18.6337422,-71.8830273:18.6365247,-71.8842455:18.6436654,-71.8956971:18.6439693,-71.8971499:18.6475365,-71.9021518:18.6473877,-71.9035277:18.6483545,-71.904675:18.6482454,-71.9057282:18.6488098,-71.9065609:18.6490187,-71.9082807:18.6483139,-71.9104338:18.6484163,-71.9117199 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672098000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 6 || border_type -> nation\n48998244000000 && 18.949921,-71.782772:18.9500835,-71.7827471:18.9508594,-71.782546:18.9512725,-71.7824202:18.9517689,-71.7820494:18.952197,-71.7817948:18.9525745,-71.7817005:18.9530025,-71.7817665:18.9532938,-71.7817382:18.9539686,-71.7814648:18.954462,-71.7812134:18.9546106,-71.7812291:18.9547206,-71.7813045:18.9547979,-71.7815182:18.9548217,-71.7817508:18.9547325,-71.7820462:18.9547652,-71.7821751:18.9550684,-71.7826245:18.9552527,-71.7830394:18.9554072,-71.7835014:18.955431,-71.784526:18.9554697,-71.7855411:18.9555588,-71.7862263:18.9556777,-71.7867731:18.9557996,-71.7870371:18.9558739,-71.7874551:18.9559869,-71.7880648:18.9562484,-71.7886934:18.9566497,-71.7892686:18.9571134,-71.7896897:18.9575117,-71.7900008:18.9577317,-71.79029:18.9578327,-71.7904974:18.9578119,-71.7909091:18.9576455,-71.791434:18.957372,-71.7918834:18.9569262,-71.7923611:18.9563971,-71.7928671:18.9558977,-71.7932412:18.9550743,-71.7935586:18.9547503,-71.7936372:18.9536416,-71.793788:18.952949,-71.7941306:18.9528063,-71.7943977:18.9527231,-71.7947686:18.9528509,-71.795284:18.953172,-71.7957366:18.9535019,-71.7959597:18.953817,-71.7961735:18.9542094,-71.7962646:18.954679,-71.7962835:18.9555856,-71.7962489:18.9560909,-71.7962583:18.9563882,-71.7961892:18.9565962,-71.7962457:18.9567775,-71.79645:18.9568697,-71.7967706:18.956944,-71.7975029:18.956831,-71.7979492:18.9567775,-71.7983892:18.9564387,-71.799354:18.9560196,-71.7996086:18.9556272,-71.8002372:18.9552794,-71.8007778:18.9549703,-71.8012021:18.9547593,-71.8013969:18.9545542,-71.8017332:18.9544561,-71.8023146:18.9544739,-71.8026635:18.9545007,-71.8028803:18.9546701,-71.8031726:18.9550357,-71.8034649:18.9552646,-71.8034618:18.9557788,-71.8034523:18.956076,-71.8034209:18.9563971,-71.8033675:18.9566408,-71.8032261:18.9568459,-71.8031946:18.9572769,-71.8031475:18.957589,-71.8031632:18.957702,-71.8032355:18.9579189,-71.8035215:18.9582697,-71.8038484:18.9585848,-71.8041878:18.9589504,-71.8046152:18.9590276,-71.8048792:18.9590247,-71.8050741:18.9589355,-71.8053066:18.9587304,-71.8054826:18.9586888,-71.8057718:18.9587007,-71.8061206:18.9587601,-71.8062589:18.9591138,-71.8067209:18.9592327,-71.8068749:18.9596162,-71.8078052:18.959747,-71.8081352:18.9598213,-71.8084527:18.9599491,-71.8090561:18.9600056,-71.8097475:18.9600115,-71.8103384:18.9599312,-71.8108161:18.9599015,-71.8114227:18.9598094,-71.8117558:18.9598242,-71.8120355:18.9599431,-71.8122335:18.9601393,-71.8124378:18.960386,-71.8125761:18.9606654,-71.8126735:18.9610667,-71.812617:18.9615244,-71.8126201:18.961786,-71.8127238:18.9621902,-71.8127741:18.962856,-71.8127207:18.9633821,-71.8125981:18.9636229,-71.8126641:18.9639201,-71.8129973:18.9642649,-71.8133084:18.9644611,-71.8138333:18.9646037,-71.8142544:18.9647048,-71.8148138:18.9647256,-71.8151501:18.9647137,-71.8155147:18.9644343,-71.8160144:18.9641549,-71.8163978:18.9636437,-71.8169824:18.9627223,-71.817545:18.9625974,-71.8176707:18.9626182,-71.8184219:18.9626569,-71.8186481:18.9629065,-71.819063:18.9630254,-71.8194967:18.9632127,-71.8201724:18.9632305,-71.820735:18.9632513,-71.8217313:18.9632513,-71.822341:18.9634059,-71.823243:18.9635396,-71.824145:18.963614,-71.8248962:18.9637566,-71.8256096:18.9639944,-71.8259679:18.9642887,-71.826191:18.9644848,-71.8262445:18.9647196,-71.8262633:18.9649812,-71.8261973:18.9652071,-71.8258233:18.9653141,-71.825443:18.9655846,-71.8249339:18.9657391,-71.8245127:18.9657956,-71.8242362:18.9658075,-71.8237113:18.9659115,-71.8235385:18.9662504,-71.8232462:18.9665268,-71.8230419:18.9668032,-71.822737:18.9672342,-71.822165:18.9675492,-71.8219042:18.967897,-71.8218413:18.9683666,-71.8218162:18.968646,-71.8218727:18.9688362,-71.821989:18.9689194,-71.8221493:18.9688778,-71.8222782:18.9688005,-71.8225139:18.9687381,-71.822803:18.9686846,-71.8232996:18.9685657,-71.8237679:18.9683785,-71.8240947:18.9681021,-71.8244562:18.9680248,-71.8246636:18.9681199,-71.8248302:18.9683874,-71.8251225:18.9685271,-71.8253927:18.968539,-71.8260873:18.9685271,-71.8264582:18.968429,-71.8267128:18.9682655,-71.826917:18.9678583,-71.8272596:18.9673174,-71.8276148:18.9668626,-71.827885:18.9662771,-71.8283156:18.9659888,-71.8286645:18.9653527,-71.8293433:18.9646602,-71.8301982:18.9643214,-71.8308645:18.9635753,-71.8314396:18.9629303,-71.8318388:18.9627877,-71.8320431:18.9627579,-71.8323259:18.962859,-71.8325459:18.963076,-71.8329199:18.9633464,-71.8332436:18.9637418,-71.8335485:18.9642857,-71.8340514:18.9648148,-71.8343217:18.9654746,-71.8345071:18.9658194,-71.8346328:18.9662949,-71.8346988:18.9666338,-71.8346517:18.9676443,-71.8346925:18.9678435,-71.8347491:18.9683398,-71.8347365:18.9687976,-71.8348622:18.9697338,-71.8351262:18.9700221,-71.8352834:18.9702064,-71.8355159:18.9703312,-71.8359654:18.9703342,-71.8365122:18.9701707,-71.8369428:18.969921,-71.8373231:18.9696803,-71.8377537:18.9695495,-71.838354:18.9694455,-71.8387122:18.9694128,-71.8390045:18.9692285,-71.8392308:18.9685687,-71.8397934:18.9680307,-71.8399788:18.9677008,-71.8401014:18.9674422,-71.840004:18.9667973,-71.8399128:18.9664911,-71.839938:18.9663098,-71.840114:18.9661731,-71.8404:18.966179,-71.8406074:18.9662831,-71.8408337:18.9663306,-71.8410474:18.9663603,-71.8417797:18.9663395,-71.8423077:18.9663455,-71.8425906:18.9662771,-71.8427351:18.9660156,-71.8428483:18.9649426,-71.8429426:18.964687,-71.8430243:18.9642946,-71.843238:18.9639617,-71.8432883:18.9637447,-71.8435051:18.9635456,-71.8437974:18.9629184,-71.8440646:18.9624399,-71.8440677:18.9620238,-71.8438634:18.9617355,-71.843568:18.9615363,-71.8430683:18.9613639,-71.8423674:18.9611618,-71.8416948:18.960921,-71.8414528:18.960389,-71.8410034:18.9595419,-71.8402523:18.9590693,-71.8397148:18.9588255,-71.8395891:18.9584986,-71.8396394:18.9583826,-71.8397588:18.9583916,-71.8400731:18.9583499,-71.8404911:18.9583172,-71.840664:18.9583262,-71.8410851:18.9580943,-71.8415157:18.9579071,-71.841698:18.9576217,-71.8417483:18.9573155,-71.8418991:18.9568727,-71.8425466:18.9567181,-71.8428388:18.9566349,-71.8431028:18.9566676,-71.8434108:18.9568281,-71.8438194:18.9572085,-71.84436:18.9575028,-71.8447686:18.9580378,-71.8452117:18.9586977,-71.845526:18.9590276,-71.8457617:18.9595121,-71.8459377:18.9601631,-71.8460634:18.9605941,-71.8461703:18.9610607,-71.8463526:18.9614471,-71.8463243:18.9620624,-71.8461357:18.9623388,-71.8461263:18.9626777,-71.8462929:18.9628471,-71.8464311:18.962963,-71.846802:18.9630314,-71.847088:18.9630789,-71.8477574:18.9631384,-71.8480089:18.9633197,-71.8485243:18.9633821,-71.849024:18.963287,-71.8492786:18.9631503,-71.8494326:18.9628976,-71.8494923:18.9624013,-71.8494703:18.9620802,-71.8494232:18.9616225,-71.8492189:18.9610161,-71.8488449:18.9607665,-71.848782:18.960597,-71.8488229:18.9603563,-71.8490334:18.9601661,-71.8492534:18.9599873,-71.8496091:18.9599143,-71.8498807:18.9596549,-71.8502415:18.9591525,-71.8506532:18.9587661,-71.8507507:18.9581538,-71.8511435:18.9574434,-71.8517124:18.9564417,-71.8523661:18.955764,-71.8527558:18.95544,-71.8530764:18.9549406,-71.8538275:18.9547415,-71.8542424:18.954688,-71.8545347:18.9547415,-71.8548427:18.954899,-71.8550375:18.9551636,-71.8551978:18.9555143,-71.8552984:18.9556837,-71.8553864:18.9559423,-71.8553393:18.9562544,-71.8552827:18.9566171,-71.855135:18.9570748,-71.8549275:18.9573215,-71.8547453:18.957598,-71.8545818:18.957812,-71.8545693:18.95824,-71.8547547:18.9585075,-71.8551318:18.9589177,-71.8554681:18.9594319,-71.8559238:18.9599016,-71.8562507:18.9603118,-71.8566027:18.9612153,-71.8574513:18.9618871,-71.8578316:18.9622735,-71.8580201:18.962651,-71.858171:18.9630612,-71.8585481:18.9632335,-71.8587053:18.9632871,-71.8590258:18.9632662,-71.8593244:18.963296,-71.8596041:18.9633257,-71.8600284:18.9632722,-71.8605313:18.963189,-71.8608613:18.9630939,-71.8610121:18.9628769,-71.8610467:18.9626629,-71.8609996:18.9624578,-71.8607796:18.9623121,-71.8606947:18.9621308,-71.8607261:18.9619673,-71.8607104:18.9617563,-71.8605564:18.9613015,-71.8604559:18.9610311,-71.8603899:18.9606476,-71.8604056:18.9603771,-71.8604087:18.9596876,-71.860063:18.9595003,-71.8600944:18.9593755,-71.8602579:18.9592744,-71.8604307:18.9593368,-71.8606193:18.9593517,-71.8609053:18.9590931,-71.8612981:18.9587394,-71.8617916:18.9584689,-71.8620304:18.9580498,-71.8621656:18.9577823,-71.8621782:18.9575772,-71.8620116:18.9574345,-71.8617821:18.9572205,-71.8617727:18.9567924,-71.8617004:18.9563525,-71.8618576:18.9559631,-71.8620524:18.9557016,-71.8622159:18.9552498,-71.8622944:18.9548931,-71.8622002:18.9545988,-71.8620807:18.9544918,-71.8618293:18.9541945,-71.8616219:18.9538913,-71.8615024:18.9536357,-71.8614019:18.9533206,-71.8610121:18.9530353,-71.8606759:18.9527202,-71.8605847:18.9525032,-71.860547:18.9520752,-71.8603647:18.9517036,-71.8603804:18.9512072,-71.8604653:18.9504403,-71.8604716:18.9496852,-71.8603836:18.9490699,-71.8602767:18.9484189,-71.8601039:18.948193,-71.8601636:18.9480414,-71.8603364:18.9478987,-71.8605564:18.9477798,-71.8608927:18.9476996,-71.8612259:18.9477174,-71.861471:18.9478898,-71.8617947:18.9480414,-71.8620587:18.9481128,-71.8624516:18.948199,-71.8628602:18.9481663,-71.8632027:18.9480028,-71.8634479:18.9478304,-71.8638219:18.9474647,-71.8640544:18.9473488,-71.8642493:18.947224,-71.864595:18.9467513,-71.8655976:18.9462014,-71.8664996:18.9459903,-71.8670465:18.9459844,-71.8673325:18.9460409,-71.8676122:18.9461598,-71.8679673:18.9462727,-71.8684262:18.9462608,-71.8689039:18.9463797,-71.8691176:18.9465908,-71.8692528:18.9468791,-71.8692685:18.9470932,-71.8694068:18.9474202,-71.8695985:18.9475509,-71.869853:18.9476936,-71.8706859:18.9477888,-71.8713491:18.9477314,-71.8721276:18.9478836,-71.8727864:18.9480072,-71.8731308:18.9481904,-71.8733646:18.9483226,-71.8734384:18.9484527,-71.873733:18.9487546,-71.8743812:18.9488103,-71.8749754:18.948801,-71.8754862:18.9486605,-71.8759526:18.9484337,-71.8763554:18.948089,-71.8764226:18.9477443,-71.8764418:18.9473542,-71.8763554:18.9471637,-71.8761061:18.947073,-71.8758183:18.9469369,-71.8753579:18.9467283,-71.8752237:18.9464561,-71.8752333:18.9461749,-71.8753004:18.9457848,-71.8756169:18.9453766,-71.8760869:18.9452768,-71.8763075:18.9453585,-71.8766432:18.9453585,-71.8768638:18.944796,-71.8776023:18.943899,-71.8783021:18.9434333,-71.8783835:18.9426594,-71.8779986:18.9424529,-71.8781349:18.9422595,-71.8787336:18.9417046,-71.8797385:18.9421039,-71.8827586 && boundary -> administrative || last_edit_changeset -> 0 || source:name -> haiti-tlm-50 || iso_country_code -> AAA || waterway -> river || source -> digitalglobe || boat -> no || border_type -> nation || last_edit_user_name ->  || last_edit_time -> 1509672095000 || last_edit_user_id -> 0 || admin_level -> 2 || name -> Rivière Macassie || last_edit_version -> 7\n180241395000000 && 19.2290479,-71.6394109:19.2294523,-71.6397414:19.2291886,-71.6411055:19.2294047,-71.6442399:19.2294233,-71.6445498:19.228327,-71.6457979:19.2281792,-71.6465989:19.2295084,-71.6491943:19.2292487,-71.6512805:19.230152,-71.6528014:19.2297142,-71.6545206:19.2299788,-71.6571471:19.2292421,-71.6573331:19.2288802,-71.6577775:19.2293327,-71.6583385:19.2293046,-71.6597426:19.2292987,-71.6599354:19.229942,-71.6625448:19.2297536,-71.6630997:19.2298292,-71.6637823:19.2292286,-71.6650693:19.2291952,-71.6651444:19.2292862,-71.6653314:19.2296088,-71.6660482:19.2303305,-71.6660336:19.2306322,-71.6668564:19.2307772,-71.6672693:19.2322997,-71.6669466:19.2333748,-71.6667064:19.2339149,-71.6665979:19.2340625,-71.6672695:19.2337257,-71.6681029:19.2343309,-71.6686229:19.2342357,-71.6689585:19.234068,-71.6695499:19.2329374,-71.6699147:19.2331294,-71.6705475:19.2339125,-71.6707082:19.2348239,-71.6709124:19.235395,-71.671547:19.2352455,-71.6737545:19.2356986,-71.6748381:19.2355733,-71.6750207:19.2352227,-71.6755319:19.2352062,-71.6755607:19.2352719,-71.6756491:19.2357352,-71.6763577:19.2351317,-71.676921:19.2332408,-71.6771681:19.232559,-71.6778945:19.2323466,-71.6791245:19.2345231,-71.6801829:19.235296,-71.6810036:19.2364497,-71.6808029:19.2367093,-71.6808231:19.2368992,-71.6819995:19.2371859,-71.6826856:19.2370838,-71.6830593:19.2365025,-71.6848183:19.2368139,-71.6854507:19.2374161,-71.684933:19.238352,-71.6847811 && boundary -> administrative || last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1509672100000 || last_edit_user_id -> 0 || iso_country_code -> AAA || admin_level -> 2 || name -> Haïti - República Dominicana || last_edit_version -> 10 || border_type -> nation\n# Points\n614558469000000 && 18.0415435,-71.7564276 && last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1492716545000 || last_edit_user_id -> 0 || iso_country_code -> AAA || ford -> yes || last_edit_version -> 5\n614788123000000 && 18.6546278,-71.963774 && last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1295303227000 || last_edit_user_id -> 0 || iso_country_code -> AAA || source -> hot_iom_c.osm-ha || last_edit_version -> 2\n614788541000000 && 18.651473,-71.8089308 && last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1295303000000 || last_edit_user_id -> 0 || iso_country_code -> AAA || source -> hot_iom_c.osm-ha || last_edit_version -> 2\n614788176000000 && 18.6484163,-71.9117199 && last_edit_user_name ->  || last_edit_changeset -> 0 || last_edit_time -> 1295303025000 || last_edit_user_id -> 0 || iso_country_code -> AAA || source -> hot_iom_c.osm-ha || last_edit_version -> 2\n# Relations\n148838000000 && 296777074000000 -> outer -> A && ISO3166-1 -> US || iso_country_code -> AAA || name:ka -> ამერიკის შეერთებული შტატები || name:nap -> State Aunite d'Amereca || name:kk -> Америка Құрама Штаттары || name:ki -> United States || name:nah -> Tlacetilīlli Tlahtohcāyōtl Ixachitlān || name:kn -> ಅಮೇರಿಕ ಸಂಯುಕ್ತ ಸಂಸ್ಥಾನ || name:ko -> 미국 || name:kl -> Naalagaaffeqatigiit || name:km -> សហរដ្ឋអាមេរិក || name:nrm -> Êtats Unnis d'Améthique || name:ks -> संयुक्त राज्‍य अमेरिका || last_edit_user_id -> 0 || name:kv -> Америкаса Ӧтувтӧм Штатъяс || name:kw -> Statys Unys Amerika || name:ku -> Dewletên Yekbûyî yên Amerîkayê || name:frp -> Ètats-Unis d’Amèrica || name:frr -> Feriind Stoote foon Ameerika || name:ky -> Америка Кошмо Штаттары || name:ckb -> وڵاتە یەکگرتووەکان || name:ja -> アメリカ合衆国 || short_name:vi -> Mỹ || name:zu -> IMelika || name:pdc -> Vereenichde Schtaate vun Amerikaa || default_language -> en || name:lmo -> Stat Ünì d'America || name:jv -> Amérika Sarékat || gnis:feature_id -> 1890467 || name:wuu -> 美国 || name:zh-yue -> 即美利堅合眾國 || name:mi -> Hononga-o-Amerika || name:mg -> Etazonia || name:ml -> അമേരിക്കന്‍ ഐക്യനാടുകള്‍ || name:mk -> Соединети Американски Држави || name:mn -> Америкийн Нэгдсэн Улс || name:mt -> Stati Uniti tal-Amerika || name:cbk-zam -> Estados Unidos de America || name:mr -> अमेरिकेची संयुक्त संस्थाने || name:ms -> Amerika Syarikat || name:tpi -> Yunaitet Stet bilong Amerika || name:my -> အမေရိကန်ပြည်ထောင်စု || wikipedia -> en:United States || name:mrj -> Америкын Ушымы Штатвлӓжӹ || name:la -> Civitates Foederatae Americae || name:lb -> Vereenegt Staate vun Amerika || name:lg -> Amereka || name:li -> Vereinegde State van Amerika || name:pfl -> Verainischde Schdaade vun Ameriga || name:lo -> ສະຫະລັດອາເມລິກາ || name:nso -> United States of America || name:ln -> Lisangá lya Ameríka || name:lt -> Jungtinės Amerikos Valstijos || name:lv -> Amerikas Savienotās Valstis || name:fy -> Feriene Steaten fan Amearika || name:pam -> United States ning America || name:wo -> Diwaan-yu-Bennoo yu Aamerig || name:haw -> ‘Amelika Hui Pū ‘ia || name:hak -> Mî-li-kiên Ha̍p-chung-koet || name:ga -> Stáit Aontaithe Mheiriceá || name:gd -> Na Stàitean Aonaichte || name:pag -> Estados Unidos || name:zea -> Vereênigde Staeten || name:gn -> Tetã peteĩ reko Amérikagua || name:gl -> Estados Unidos de América || name:chr -> ᏌᏊᎢᏳᎾᎵᏍᏔᏅᏍᎦᏚᎩ ᎾᎿ ᎠᎺᏰᏟ || name:pap -> Estadonan Uni di Merka || name:gv -> Steatyn Unnaneysit America || name:xh -> IYunayithedi Steyitsi || name:xal -> Америкин Ниицәтә Орн Нутгуд || name:tzl -> Estats Viensiçeschti d'America || name:gu -> સંયુક્ત રાજ્ય અમેરિકા || name:vi -> Hoa Kỳ || last_edit_changeset -> 0 || name:vls -> Verênigde Stoaten van Amerika || name:fa -> ایالات متحده آمریکا || name:vo -> Lamerikän || name:ff -> Dowlaaji Dentuɗi || name:fi -> Amerikan yhdysvallat || name:diq -> Dewletê Amerikayê Yewbiyayey || name:lij -> Stati Unïi d'America || name -> United States of America || name:fr -> États-Unis d'Amérique || name:fo -> Sambandsríkið Amerika || name:ast -> Estaos Xuníos d'América || name:rue -> Споєны Штаты Америцькы || name:wa -> Estats Unis d' Amerike || alt_name:vi -> Hoa Kì;Mỹ;Mĩ || name:fiu-vro -> Ameeriga Ütisriigiq || name:ace -> Amirika Carékat || name:hy -> Ամերիկայի Միացյալ Նահանգներ || name:bxr -> Американ Нэгэдэһэн Улас || name:ia -> Statos Unite de America || name:yo -> Àwọn Ìpínlẹ̀ Aṣọ̀kan Amẹ́ríkà || name:id -> Amerika Serikat || name:ie -> Unit States de America || name:pcd -> États-Unis Anmérikes || name:gan -> 美國 || name:ig -> Njikota Obodo Amerika || name:nov -> Unionati States de Amerika || name:war -> Estados Unidos || name:ik -> United States of America || name:za -> Meijgoz || last_edit_time -> 1528291058000 || name:io -> Unionita Stati di Amerika || name:it -> Stati Uniti d'America || name:iu -> ᐊᒥᐊᓕᑲ || name:is -> Bandaríki Norður-Ameríku || official_name:pl -> Stany Zjednoczone Ameryki || name:zh -> 美利坚合众国 || name:szl -> Zjednoczůne Sztaty Ameriki || name:hsb -> Zjednoćene staty Ameriki || name:ha -> kunkiyar taraiyar Amurika || name:chy -> United States || name:he -> ארצות הברית || name:hi -> संयुक्त राज्य अमेरिका || name:gag -> Amerika Birleşik Devletläri || name:ht -> Etazini || name:hr -> Sjedinjene Američke Države || name:eml -> Stat Unî || name:yi -> פאראייניקטע שטאטן פון אמעריקע || name:hu -> Amerikai Egyesült Államok || name:sg -> ÂKödörö-ôko tî Amerîka || name:bs -> Sjedinjene Američke Države || name:sd -> آمريڪا || name:se -> Amerihká ovttastuvvan stáhtat || name:sk -> Spojené štáty americké || name:sh -> Sjedinjene Američke Države || name:scn -> Stati Uniti || name:si -> අ'මෙරිකා‍වේ එක්සත් රාජ්‍යයන් || name:sn -> United States of America || name:so -> Mareykanka || name:sl -> Združene države Amerike || name:sm -> Iunaite Sitete o Amerika || name:xmf -> ამერიკაშ აკოართაფილი შტატეფი || type -> boundary || name:sr -> Сједињене Америчке Државе || name:ss -> IMelika || name:bar -> Vaeinigtn Staatn vo Amerika || name:pms -> Stat Unì d'América || name:ca -> Estats Units d'Amèrica || name:sq -> Shtetet e Bashkuara të Amerikës || name:sv -> Amerikas Förenta Stater || name:sw -> Muungano wa Madola ya Amerika || name:ce -> Iамерка пачхьалк || name:su -> Amérika Sarikat || name:roa-tara -> Statère Aunìte d'Americhe || name:cdo -> Mī-lé-giĕng Hăk-cé̤ṳng-guók || name:co -> Stati Uniti d'America || name:sco -> Unitit States o Americae || name:ext -> Estaus Uñíus d'América || name:lez -> Америкадин Садхьанвай Штатар || name:pnb -> امریکہ || last_edit_version -> 502 || name:cs -> Spojené státy americké || name:tet -> Estadu Naklibur Sira Amérika Nian || name:ta -> அமெரிக்க ஐக்கிய நாடு || name:glk -> امریکا || name:ar -> الولايات المتّحدة الأمريكيّة || name:as -> মাৰ্কিন যুক্তৰাষ্ট্ৰ || name:zh-classical -> 美利堅合眾國 || name:ay -> Istadus Unidus || name:av -> Америкалъул Цолъарал Штатал || name:rm -> Stadis Unids da l'America || name:rn -> Leta Zunze Ubumwe za Amerika || name:srn -> Kondre Makandrameki || name:az -> Amerika Birləşmiş Ştatları || name:csb -> Zjednóné Kraje Americzi || name:ba -> Америка Ҡушма Штаттары || name:ro -> Statele Unite ale Americii || name:be -> Злучаныя Штаты Амерыкі || name:ru -> Соединённые Штаты Америки || name:bi -> Yunaeted Stet blong Amerika || name:bg -> Съединени американски щати || name:rw -> Leta Zunze Ubumwe z’Amerika || name:bm -> Amerika ka Kelenyalen Jamanaw || name:ksh -> Vereenichde Staate van Amerika || name:bn -> মার্কিন যুক্তরাষ্ট্র || name:mwl -> Stados Ounidos de la América || name:be-tarask -> Злучаныя Штаты Амэрыкі || name:br -> Stadoù-Unanet Amerika || name:sc -> Istados Unidos de America || name:bo -> ཨ་མེ་རི་ཁ་རྒྱལ་ཕྲན་མཉམ་འབྲེལ་རྒྱལ་ཁབ། || name:sa -> संयुक्तानि राज्यानि || flag -> http://upload.wikimedia.org/wikipedia/commons/a/a4/Flag_of_the_United_States.svg || name:dv -> އެމެރިކާ || name:ug -> ئامېرىكا قوشما شتاتلىرى || name:dz -> ཡུ་ནའིཊེཊ་སི་ཊེས || name:uk -> Сполучені Штати Америки || official_name:cs -> Spojené státy americké || name:arc -> ܐܬܪܘܬܐ ܡܚܝܕܐ ܕܐܡܪܝܟܐ || name:min -> Amerika Sarikat || name:ee -> United States || name:ur -> ریاستہائے متحدہ امریکہ || name:arz -> امريكا || name:el -> Ηνωμένες Πολιτείες της Αμερικής || name:uz -> Amerika Qoʻshma Shtatlari || name:en -> United States of America || name:eo -> Usono || name:mzn -> متحده ایالات آمریکا || name:et -> Ameerika Ühendriigid || name:eu -> Ameriketako Estatu Batuak || name:es -> Estados Unidos de América || name:cv -> Америкăри Пĕрлешӳллĕ Штатсем || name:tg -> Иёлоти Муттаҳидаи Амрико || name:th -> สหรัฐอเมริกา || name:jbo -> .iunaited.steits. || name:mhr -> Америкысе Ушымо Штатвлак || name:te -> అమెరికా సంయుక్త రాష్ట్రాలు || name:cu -> Амєрика́ньскꙑ Ѥдьнѥнꙑ́ Дрьжа́вꙑ || name:tk -> Amerikanyň Birleşen Ştatlary || name:tl -> Estados Unidos || name:cy -> Unol Daleithiau America || name:to -> Puleʻanga Fakataha 'o 'Amelika || name:stq -> Fereende Stoaten fon Amerikoa || old_name:ru -> Северо-Американские Соединённые Штаты || name:tn -> USA || name:zh-min-nan -> America Ha̍p-chiòng-kok || name:ts -> United States || name:tt -> Америка Кушма Штатлары || name:da -> Amerikas Forenede Stater || name:tr -> Amerika Birleşik Devletleri || name:tw -> USA || name:de -> Vereinigte Staaten von Amerika || name:myv -> Американь Вейтьсэндявкс Штаттнэ || name:bcl -> Estados Unidos || name:ty -> Fenua Marite || name:ceb -> Estados Unidos sa Amerika || name:nds-nl -> Verienigde Staoten van Amerika || name:oc -> Estats Units d'America || official_name:vi -> Hợp chúng quốc Hoa Kỳ;Hợp chúng quốc Hoa Kì;Hợp chủng quốc Hoa Kỳ;Hợp chủng quốc Hoa Kì || name:lad -> Estatos Unitos d'Amerika || name:pih -> Yunitid Staits || official_name:eo -> Unuiĝintaj Ŝtatoj de Ameriko || name:vec -> Stati Unìi de la Mèrica || name:om -> USA || name:or -> ଯୁକ୍ତରାଷ୍ଟ୍ର ଆମେରିକା || name:als -> Vereinigte Schtaate vo Amerika || name:os -> Америкæйы Иугонд Штаттæ || name:ilo -> stados Unidos iti Amerika || admin_level -> 2 || name:new -> संयुक्त राज्य अमेरिका || name:vep -> Amerikan Ühtenzoittud Valdkundad || name:na -> Eben Merika || name:ne -> संयुक्त राज्य अमेरिका || name:nds -> Verenigten Staten vun Amerika || name:nn -> USA || name:nl -> Verenigde Staten van Amerika || name:no -> Amerikas forente stater || name:nv -> Wááshindoon Bikéyah Ałhidadiidzooígíí || name:map-bms -> Amerika Serikat || name:fur -> Stâts Unîts di Americhe || name:hif -> United States || name:koi -> Америкаись Ӧтлаасьӧм Штаттэз || boundary -> administrative || name:bat-smg -> JAV || name:ang -> Geānlǣtan Rīcu American || name:sah -> Америка Холбоһуктаах Штаттара || name:crh -> Amerika Qoşma Ştatları || name:krc -> Американы Бирлешген Штатлары || border_type -> national || ISO3166-1:numeric -> 840 || name:qu -> Hukllachasqa Amirika Suyukuna || name:ab -> Америка Еиду Аштатқәа || name:bpy -> তিলপারাষ্ট্র || name:af -> Verenigde State van Amerika || short_name:ru -> США || name:kbd -> Америкэ Штат Зэгуэт || name:am -> የተባበሩት የአሜሪካ ግዛቶች || name:ltg -> Amerikys Saškierstuos Vaļsteibys || name:an -> Estatos Unitos d'America || name:pa -> ਸੰਯੁਕਤ ਰਾਜ ਅਮਰੀਕਾ || short_name:cs -> Spojené státy || name:udm -> Америкалэн Огазеяськем Штатъёсыз || name:pl -> Stany Zjednoczone || name:lbe -> Американал ЦачӀунхьу Штатру || name:mdf -> Америконь Аймакнень Соткссна || name:dsb -> Zjadnośone staty Ameriki || name:ps -> د امريکا متحده ايالات || last_edit_user_name ->  || name:pt -> Estados Unidos da América || ISO3166-1:alpha3 -> USA || name:kab -> Iwunak Yedduklen || name:kaa -> Amerika Qurama Shtatları || old_name:vi -> Mỹ Lợi Kiên;Ma Ly Căn;Nhã Di Lý || old_short_name:ru -> САСШ || wikidata -> Q30 || ISO3166-1:alpha2 -> US\n307828000000 && 28460950000000 -> outer -> L || 48360222000000 -> outer -> L || 48419971000000 -> outer -> L || 48420027000000 -> outer -> L || 48429583000000 -> outer -> L || 48429589000000 -> outer -> L || 48435426000000 -> outer -> L || 48435450000000 -> outer -> L || 48435492000000 -> outer -> L || 48581237000000 -> outer -> L || 48582329000000 -> outer -> L || 48612170000000 -> outer -> L || 48679069000000 -> outer -> L || 48686950000000 -> outer -> L || 48997917000000 -> outer -> L || 48997919000000 -> outer -> L || 48997920000000 -> outer -> L || 48997923000000 -> outer -> L || 48998242000000 -> outer -> L || 48998244000000 -> outer -> L || 48998245000000 -> outer -> L || 48998248000000 -> outer -> L || 50447457000000 -> outer -> L || 50448375000000 -> outer -> L || 50448587000000 -> outer -> L || 50553732000000 -> outer -> L || 50633852000000 -> outer -> L || 50634189000000 -> outer -> L || 50634190000000 -> outer -> L || 50634191000000 -> outer -> L || 180241391000000 -> outer -> L || 180241392000000 -> outer -> L || 180241394000000 -> outer -> L || 180241395000000 -> outer -> L || 180241398000000 -> outer -> L || 204483815000000 -> outer -> L || 254605672000000 -> outer -> L || 254684884000000 -> outer -> L || 339640154000000 -> outer -> L || 364609689000000 -> outer -> L || 385474749000000 -> outer -> L || 385486819000000 -> outer -> L || 385491702000000 -> outer -> L || 537853867000000 -> outer -> L || 537853868000000 -> outer -> L && name:bs -> Dominikanska Republika || name:se -> Dominikána dásseváldi || ISO3166-1 -> DO || name:sk -> Dominikánska republika || iso_country_code -> AAA || name:sh -> Dominikanska Republika || name:scn -> Ripùbbrica Duminicana || name:ka -> დომინიკელთა რესპუბლიკა || name:sl -> Dominikanska republika || type -> boundary || name:sr -> Доминиканска Република || name:kk -> Доминикан Республикасы || name:ss -> Tibuse weDomonokha || name:pms -> Repùblica Duminican-a || name:ca -> República Dominicana || name:sq -> Republika Dominikane || name:nah -> Dominicanatlācatlahtohcāyōtl || name:kn -> ಡೊಮಿನಿಕ ಗಣರಾಜ್ಯ || name:sv -> Dominikanska Republiken || name:ko -> 도미니카 공화국 || name:sw -> Jamhuri ya Dominika || name:ce -> Доминикан Пачхьалкх || name:su -> Républik Dominika || last_edit_user_id -> 0 || name:kw -> Repoblek Dhominikanek || name:sco -> Dominican Republic || name:ku -> Komara Domînîk || name:ext -> Repúbrica Dominicana || name:frp -> Rèpublica domeniquêna || name:pnb -> ڈومینیکن || last_edit_version -> 65 || name:cs -> Dominikánská republika || name:ta -> டொமினிக்கன் குடியரசு || name:frr -> Dominikaans Republiik || name:ar -> جمهورية الدومينيكان || name:ckb -> کۆماری دۆمینیکان || name:ja -> ドミニカ共和国 || name:ay -> Republika Duminikana || name:az -> Dominikan Respublikası || short_name:en -> D.R. || default_language -> es || name:ro -> Republica Dominicană || population -> 10464474 || name:be -> Дамініканская Рэспубліка || name:ru -> Доминиканская Республика || name:bg -> Доминиканска република || name:rw -> Repubulika ya Dominikani || name:lmo -> Repübliga Dominicana || name:bn -> ডোমিনিকান প্রজাতন্ত্র || name:jv -> Republik Dominika || short_name -> R.D. || name:be-tarask -> Дамініканская Рэспубліка || name:br -> Republik Dominikan || name:bo -> ཌོ་མི་ནི་ཀན་སྤྱི་མཐུན་རྒྱལ་ཁབ། || name:sa -> डोमोनिकन रिपब्लिक || name:zh-yue -> 多明尼加 || flag -> http://upload.wikimedia.org/wikipedia/commons/9/9f/Flag_of_the_Dominican_Republic.svg || name:dv -> ޑޮމިނިކަން ޖުމްހޫރިއްޔާ || name:ug -> دومىنىكان جۇمھۇرىيەتى || name:uk -> Домініканська Республіка || name:ml -> ഡൊമനിക്കൻ റിപ്പബ്ലിക് || name:ee -> Dominican Republic || name:ur -> جمہوریہ ڈومینیکن || name:mk -> Доминиканска Република || name:arz -> جمهورية الدومينيكان || name:el -> Δομινικανή Δημοκρατία || name:mt -> Repubblika Dominikana || name:mr -> डॉमिनिकन प्रजासत्ताक || name:uz -> Dominika Respublikasi || name:ms -> Republik Dominika || name:my -> ဒိုမီနီကန်သမ္မတနိုင်ငံ || name:en -> Dominican Republic || name:eo -> Domingo || name:mzn -> دومینیکن || wikipedia -> es:República Dominicana || name:et -> Dominikaani Vabariik || name:eu -> Dominikar Errepublika || name:es -> República Dominicana || name:tg -> Ҷумҳурии Доминикана || name:th -> สาธารณรัฐโดมินิกัน || name:te -> డొమెనికన్ రిపబ్లిక్ || name:tl -> Republikang Dominikano || name:la -> Respublica Dominicana || name:cy -> Gweriniaeth Dominica || name:lb -> Dominikanesch Republik || name:zh-min-nan -> Dominic Kiōng-hô-kok || name:tt -> Доминикан Җөмһүрияте || is_in:continent -> North America || name:da -> Dominikanske Republik || name:li -> Dominicaanse Rippubliek || name:tr -> Dominik Cumhuriyeti || name:de -> Dominikanische Republik || name:ln -> Republiki Dominikani || name:lt -> Dominikos Respublika || name:bcl -> Dominikanang Republika || name:ceb -> Republikang Dominikano || name:nds-nl -> Dominikaanse Reppubliek || name:lv -> Dominikāna || name:fy -> Dominikaanske Republyk || name:oc -> Republica Dominicana || name:pam -> Dominikana Republika || name:wo -> Réewum Dominik || official_name:es -> Republica Dominicana || official_name:en -> Republica Dominicana || name:pih -> Dominikan Repablik || official_name:eo -> Dominika Respubliko || name:hak -> Tô-mí-nì-kâ Khiung-fò-koet || name:ga -> An Phoblacht Dhoiminiceach || name:vec -> Repùblica Dominicana || name:gd -> Poblachd Dhoiminicia || name:pag -> Republika na Dominikana || name:or -> ଡୋମେନିକାନ ରିପବ୍ଲିକ || name:als -> Dominikanische Republik || name:os -> Доминиканæйы Республикæ || name:ilo -> Republica Dominicana || name:gl -> República Dominicana || admin_level -> 2 || name:pap -> Republika Dominikano || name:gv -> Yn Phobblaght Ghominicagh || name:xal -> Доминиканмудин Орн || name:na -> Ripubrikin Dominika || name:vi -> Cộng hòa Dominica || last_edit_changeset -> 0 || name:ne -> डोमिनिकन गणतन्त्र || name:nds -> Dominikaansche Republiek || name:fa -> جمهوری دومینیکن || name:vo -> Sandominän || name:nn -> Den dominikanske republikken || name:nl -> Dominicaanse Republiek || name:fi -> Dominikaaninen tasavalta || name:no -> Den dominikanske republikk || name:diq -> Cumhuriyetê Dominika || name:nv -> Domingo Bikéyah || name:lij -> Repubbrica Dominicann-a || name -> República Dominicana || name:fr -> République dominicaine || name:fo -> Dominikanalýðveldið || name:ast -> República Dominicana || name:fiu-vro -> Dominikaani Vabariik || name:hif -> Dominican Republic || boundary -> administrative || name:bat-smg -> Duomėnėkas Respoblėka || name:hy -> Դոմինիկյան Հանրապետություն || name:sah -> Доминика Республиката || name:crh -> Dominikan Cumhuriyeti || name:bxr -> Доминикана Бүгэдэ Найрамдаха Улас || name:ia -> Republica Dominican || timezone -> America/Santo_Domingo || name:yo -> Orílẹ̀òmìnira Dómíníkì || name:id -> Republik Dominika || name:nov -> Dominikal Republike || border_type -> nation || ISO3166-1:numeric -> 214 || name:war -> Republika Dominicana || name:qu -> Duminikana || name:bpy -> ডোমিনিকান প্রজাতন্ত্র || last_edit_time -> 1525469092000 || name:af -> Dominikaanse Republiek || name:io -> Dominikana Republiko || name:it -> Repubblica Dominicana || name:am -> ዶሚኒካን ሪፐብሊክ || name:is -> Dóminíska lýðveldið || official_name:pl -> Republika Dominikany || name:an -> Republica Dominicana || name:zh -> 多明尼加共和國 || name:szl -> Důmińikana || name:hsb -> Dominikanska republika || name:pa -> ਡੋਮਿਨਿਕਾਈ ਗਣਰਾਜ || name:pl -> Dominikana || name:dsb -> Dominikańska republika || name:he -> הרפובליקה הדומיניקנית || name:ps -> ډومنيکان جمهوريت || last_edit_user_name ->  || name:pt -> República Dominicana || name:hi -> डोमिनिकन गणराज्य || ISO3166-1:alpha3 -> DOM || name:gag -> Dominikan Respublikası || name:ht -> Dominikani || name:hr -> Dominikanska Republika || name:yi -> דאמיניקאנישע רעפובליק || wikidata -> Q786 || ISO3166-1:alpha2 -> DO || name:hu -> Dominikai Köztársaság\n307829000000 && 296777074000000 -> outer -> A || 28460950000000 -> outer -> L || 48360222000000 -> outer -> L || 48419971000000 -> outer -> L || 48420027000000 -> outer -> L || 48429583000000 -> outer -> L || 48429589000000 -> outer -> L || 48435426000000 -> outer -> L || 48435450000000 -> outer -> L || 48435492000000 -> outer -> L || 48581237000000 -> outer -> L || 48582329000000 -> outer -> L || 48612170000000 -> outer -> L || 48679069000000 -> outer -> L || 48686950000000 -> outer -> L || 48997917000000 -> outer -> L || 48997919000000 -> outer -> L || 48997920000000 -> outer -> L || 48997923000000 -> outer -> L || 48998242000000 -> outer -> L || 48998244000000 -> outer -> L || 48998245000000 -> outer -> L || 48998248000000 -> outer -> L || 50447457000000 -> outer -> L || 50448375000000 -> outer -> L || 50448587000000 -> outer -> L || 50553732000000 -> outer -> L || 50633852000000 -> outer -> L || 50634189000000 -> outer -> L || 50634190000000 -> outer -> L || 50634191000000 -> outer -> L || 180241391000000 -> outer -> L || 180241392000000 -> outer -> L || 180241394000000 -> outer -> L || 180241395000000 -> outer -> L || 180241396000000 -> outer -> L || 180241398000000 -> outer -> L || 204483815000000 -> outer -> L || 254605672000000 -> outer -> L || 254684884000000 -> outer -> L || 339640154000000 -> outer -> L || 537853867000000 -> outer -> L || 537853868000000 -> outer -> L && ISO3166-1 -> HT || iso_country_code -> AAA || name:ka -> ჰაიტი || name:kg -> Ayiti || name:kk -> Гаити || name:ki -> Haiti || name:nah -> Haiti || name:ko -> 아이티 || last_edit_user_id -> 0 || name:kw -> Hayti || name:ku -> Haîtî || name:frp -> Hayiti || name:bjn -> Haiti || name:frr -> Haiti || name:ckb -> ھایتی || name:ja -> ハイチ || official_name:ht -> Repiblik Ayiti || name:lmo -> Haiti || name:jv -> Haiti || name:zh-yue -> 海地 || name:tok -> ma Awisi || name:mg -> Haiti || name:ml -> ഹെയ്റ്റി || name:mk -> Хаити || name:mn -> Гайти || name:mt -> Ħaiti || name:mr -> हैती || name:ms -> Haiti || name:my -> ဟေတီနိုင်ငံ || wikipedia -> fr:Haïti || name:mrj -> Гаити || name:la -> Haitia || name:lb -> Haiti || is_in:continent -> North America || name:li -> Haïti || name:ln -> Ayiti || name:lt -> Haitis || name:lv -> Haiti || name:fy -> Haïty || name:pam -> Haiti || name:wo -> Ayiti || name:haw -> Heiti || name:hak -> Hói-thi || name:ga -> Háítí || name:gd -> Haiti || name:gl -> Haití - Haïti || name:pap -> Haiti || name:gv -> Haiti || name:tzl -> Haïtì || name:gu -> હૈતી || name:vi -> Haiti || last_edit_changeset -> 0 || name:fa -> هائیتی || name:vo -> Haitiyän || name:fi -> Haiti || name:diq -> Haiti || name:lij -> Haiti || name -> Ayiti || name:fr -> Haïti || name:fo -> Haiti || name:ast -> Haití || name:wa -> Ayiti || name:fiu-vro -> Haiti || official_name -> République d'Haïti || name:hy -> Հաիթի || name:bxr -> Һаити Улас || name:ia -> Haiti || name:yo -> Hàítì || name:id -> Haiti || name:nov -> Haiti || name:war -> Haiti || last_edit_time -> 1523028911000 || name:io -> Haiti || name:it -> Haiti || name:is -> Haítí || official_name:pl -> Republika Haiti || name:zh -> 海地 || name:szl -> Hajiti || name:hsb -> Haiti || name:he -> האיטי || name:hi -> हैती || name:gag -> Haiti || name:ht -> Ayiti || name:hr -> Haiti || name:yi -> האיטי || name:hu -> Haiti || name:bs -> Haiti || name:se -> Haiti || name:sk -> Haiti || name:sh -> Haiti || name:scn -> Aiti || name:si -> හෙයිටි || name:so -> Haiti || name:sl -> Haiti || type -> boundary || name:sr -> Хаити || name:ss -> IHayithi || name:pms -> Haiti || name:ca -> Haití || name:sq -> Haitia || name:sv -> Haiti || name:sw -> Haiti || name:ce -> Гаити Пачхьалкх || name:su -> Haiti || name:co -> Haiti || name:sco -> Haiti || name:ext -> Aití || name:pnb -> ہیٹی || last_edit_version -> 62 || name:cs -> Haiti || name:ta -> எயிட்டி || name:ar -> هايتي || name:zh-classical -> 海地 || name:ay -> Ayti || name:az -> Haiti Respublikası || name:ro -> Haiti || name:be -> Гаіці || name:ru -> Гаити || name:bg -> Хаити || name:rw -> Hayiti || name:bm -> Ayiti || name:bn -> হাইতি || name:be-tarask -> Гаіці || name:br -> Republik Haiti || name:bo -> ཧའི་ཏི། || flag -> http://upload.wikimedia.org/wikipedia/commons/5/56/Flag_of_Haiti.svg || name:dv -> ހެއިޓީ || name:ug -> ھايتى || name:uk -> Гаїті || official_name:cs -> Republika Haiti || name:ee -> Haiti || name:ur -> ہیٹی || name:arz -> هاييتى || name:el -> Αϊτή || name:uz -> Gaiti || name:en -> Haiti || name:eo -> Haitio || name:et -> Haiti || name:eu -> Haiti || name:es -> Haití || name:cv -> Гаити || name:th -> ประเทศเฮติ || name:jbo -> aitas || name:mhr -> Гаити || name:tl -> Hayti || name:cy -> Haiti || name:zh-min-nan -> Haiti || name:tt -> Гаити || name:da -> Haiti || name:tr -> Haiti || name:de -> Haiti || name:bcl -> Haiti || name:ceb -> Haiti || name:nds-nl -> Haïti || official_name:sk -> Haitská republika || name:oc -> Haití (estat) || official_name:ast -> República d'Haití || official_name:es -> República de Haití || official_name:en -> Republic of Haiti || name:lad -> Ayti || name:pih -> Haiti || official_name:eo -> Haitia Respubliko || name:vec -> Haiti || name:or -> ହିଟି || name:als -> Haiti || name:os -> Гаити || name:ilo -> Haiti || admin_level -> 2 || name:new -> हेइटी || name:na -> Aiti || name:ne -> हाइटी || name:nds -> Haiti || name:nn -> Haiti || name:nl -> Haïti || name:no -> Haiti || name:nv -> Héítii || name:hif -> Haiti || boundary -> administrative || name:bat-smg -> Haitės || name:sah -> Хаити || name:crh -> Haiti || timezone -> America/Port-au-Prince || border_type -> nation || ISO3166-1:numeric -> 332 || name:qu -> Ayti (mama llaqta) || name:bpy -> হাইতি || name:af -> Haïti || name:am -> ሃይቲ || name:an -> Haití || name:pa -> ਹੈਤੀ || name:pl -> Haiti || official_name:fr -> République d'Haïti || name:dsb -> Haiti || name:ps -> هایتي || last_edit_user_name ->  || name:pt -> Haiti || ISO3166-1:alpha3 -> HTI || wikidata -> Q790 || ISO3166-1:alpha2 -> HT\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/USA_boundary_reduced.txt",
    "content": "POLYGON ((-75.2288309 18.3520806, -75.2336999 18.3664654, -75.2384618 18.398702, -75.2377069 18.4312495, -75.2322562 18.4610773, -75.222672 18.4886399, -75.2172678 18.4999677, -75.1993364 18.5289778, -75.1765697 18.5547196, -75.1596528 18.5688721, -75.1492275 18.5767252, -75.1198508 18.5933921, -75.0880291 18.6053034, -75.0546028 18.6121442, -75.0199034 18.6137166, -74.9832495 18.6098363, -74.952276 18.6044011, -74.9225014 18.5946192, -74.893673 18.5801641, -74.8681576 18.5623586, -74.8457299 18.5411084, -74.825893 18.5159259, -74.8084096 18.4854595, -74.7966939 18.4525742, -74.7907319 18.418, -74.790449 18.3896744, -74.7920482 18.3693394, -74.804815 18.320668, -74.8230994 18.285888, -74.848054 18.2551258, -74.8801877 18.2283852, -74.9134925 18.2093121, -74.9498941 18.1964118, -74.9941897 18.1892548, -75.0374985 18.1907252, -75.0814085 18.201251, -75.1219061 18.2195619, -75.1482084 18.2370322, -75.1721527 18.2582151, -75.1923834 18.2826809, -75.2052088 18.3013364, -75.2192144 18.3256833, -75.2288309 18.3520806))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/ZAF_osm_shards_with_14.txt",
    "content": "ZAF_8-147-151\nZAF_9-283-306\nZAF_9-283-307\nZAF_9-301-294\nZAF_9-301-296\nZAF_9-301-295\nZAF_9-301-297\nZAF_9-295-292\nZAF_9-295-293\nZAF_10-590-602\nZAF_10-590-603\nZAF_9-295-298\nZAF_10-590-598\nZAF_7-73-76\nZAF_7-73-77\nZAF_8-148-144\nZAF_8-148-145\nZAF_8-148-146\nZAF_8-148-147\nZAF_8-148-148\nZAF_8-148-151\nZAF_8-140-152\nZAF_8-140-153\nZAF_9-292-293\nZAF_9-292-292\nZAF_10-601-588\nZAF_8-141-152\nZAF_7-75-72\nZAF_7-75-75\nZAF_8-149-144\nZAF_8-149-145\nZAF_8-149-146\nZAF_8-149-147\nZAF_8-149-148\nZAF_8-149-149\nZAF_8-149-150\nZAF_8-149-151\nZAF_10-592-596\nZAF_10-565-614\nZAF_10-592-601\nZAF_10-565-615\nZAF_12-2361-2396\nZAF_9-302-295\nZAF_9-296-301\nZAF_7-70-77\nZAF_8-150-146\nZAF_8-150-149\nZAF_9-293-292\nZAF_9-293-293\nZAF_6-37-38\nZAF_10-589-601\nZAF_10-589-600\nZAF_7-72-73\nZAF_10-600-591\nZAF_7-72-74\nZAF_10-600-588\nZAF_7-72-75\nZAF_10-600-589\nZAF_7-72-76\nZAF_7-72-77\nZAF_8-151-148\nZAF_11-1180-1199\nZAF_8-151-149\nZAF_6-34-37\nZAF_11-1184-1194\nZAF_9-300-297\nZAF_9-300-296\nZAF_10-591-603\nZAF_9-297-298\nZAF_9-297-300\nZAF_9-297-299\nZAF_5-19-20\nZAF_9-297-301\nZAF_9-294-293\nZAF_9-294-292\nZAF_9-303-295\nZAF_9-294-299\nZAF_9-294-298\nZAF_9-294-301\nZAF_8-146-145\nZAF_9-282-306\nZAF_8-146-147\nZAF_8-146-148\nZAF_8-146-149\nZAF_8-146-150\nZAF_8-146-151\nZAF_6-35-36\nZAF_6-35-37\nZAF_10-588-600\nZAF_10-588-601\nZAF_10-593-597\nZAF_10-564-615\nZAF_11-1185-1194\nZAF_10-564-614\nZAF_10-593-596\nZAF_12-2360-2397\nZAF_8-147-144\nZAF_12-2360-2396\nZAF_8-147-145\nZAF_8-147-147\nZAF_10-593-601\nZAF_7-71-76\nZAF_8-147-148\nZAF_7-71-77\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/countryToShardList.txt",
    "content": "DMA||9-168-233, 9-169-233, 9-168-234, 10-338-468"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/duplicate_shape.prj",
    "content": "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]]"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/duplicate_shape.qpj",
    "content": "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/boundary/slicing-filter.json",
    "content": "{\n\t\"filters\": [\n    \t\"IShouldBeSliced->yes\"\n  ]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/clipping/testHuggingPolygons.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104857' action='modify' visible='true' lat='43.1026704744' lon='5.93621768147' />\n  <node id='-104858' action='modify' visible='true' lat='43.10216969879' lon='5.93623411168' />\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-102477' action='modify' visible='true'>\n    <nd ref='-104857' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104858' />\n    <nd ref='-104857' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-102477' role='outer' />\n    <member type='way' ref='-101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/clipping/testOverlappingPolygonsToMultiPolygon.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10236635848' lon='5.93449677274' />\n  <node id='-104787' action='modify' visible='true' lat='43.10181118988' lon='5.93559596954' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104857' action='modify' visible='true' lat='43.10267497353' lon='5.93620535728' />\n  <node id='-104858' action='modify' visible='true' lat='43.10217419796' lon='5.93622178748' />\n  <node id='-104884' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104885' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104912' action='modify' visible='true' lat='43.10219684256' lon='5.93546235171' />\n  <node id='-104920' action='modify' visible='true' lat='43.10197638283' lon='5.93472289993' />\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104885' />\n    <nd ref='-104884' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-102477' action='modify' visible='true'>\n    <nd ref='-104857' />\n    <nd ref='-104786' />\n    <nd ref='-104912' />\n    <nd ref='-104920' />\n    <nd ref='-104787' />\n    <nd ref='-104858' />\n    <nd ref='-104857' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-102477' role='outer' />\n    <member type='way' ref='-101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/clipping/testOverlappingPolygonsToPolygon.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10236635848' lon='5.93449677274' />\n  <node id='-104787' action='modify' visible='true' lat='43.10181118988' lon='5.93559596954' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104857' action='modify' visible='true' lat='43.10267497353' lon='5.93620535728' />\n  <node id='-104858' action='modify' visible='true' lat='43.10217419796' lon='5.93622178748' />\n  <node id='-104884' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104885' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104885' />\n    <nd ref='-104884' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-102477' action='modify' visible='true'>\n    <nd ref='-104857' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104858' />\n    <nd ref='-104857' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-102477' role='outer' />\n    <member type='way' ref='-101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/clipping/testTouchingPolygons.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10180669068' lon='5.93560829374' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104857' action='modify' visible='true' lat='43.1026704744' lon='5.93621768147' />\n  <node id='-104858' action='modify' visible='true' lat='43.10216969879' lon='5.93623411168' />\n  <node id='-104884' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104884' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-102477' action='modify' visible='true'>\n    <nd ref='-104857' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104858' />\n    <nd ref='-104857' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-102477' role='outer' />\n    <member type='way' ref='-101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterTest_crossingPolyLines.txt",
    "content": "POLYGON((0 100, 100 100, 0 0, 100 0, 0 100))\n    POLYGON((0 0, 0 100, 100 100, 100 0, 0 0))\n    MULTIPOLYGON(((0 0, 0 100, 100 100, 100 0, 0 0)),((50 50, 50 150, 150 150, 150 50, 50 50)))\n    POLYGON((0 0, 50 50, 100 0, 150 0, 200 50, 250 0, 0 0))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterTest_expectedPolygons.txt",
    "content": "POLYGON ((151.3153064 -33.628584, 151.3151369 -33.6288225, 151.3149089 -33.6287574, 151.3148758 -33.6288311, 151.3148066 -33.6289855, 151.3150598 -33.6290501, 151.3149568 -33.6292855, 151.3147978 -33.6300451, 151.3147907 -33.6302479, 151.3147888 -33.6303689, 151.3148952 -33.6304604, 151.3150125 -33.6305612, 151.3150125 -33.6306625, 151.3150125 -33.6307596, 151.3150125 -33.6309478, 151.3147582 -33.6313124, 151.3143534 -33.6317683, 151.3139233 -33.6321342, 151.3137146 -33.6322936, 151.3129607 -33.6328694, 151.312812 -33.6330188, 151.3125737 -33.6333676, 151.3123623 -33.6336771, 151.312327 -33.633802, 151.3123007 -33.6338679, 151.312244 -33.6339137, 151.3121032 -33.6339881, 151.3120018 -33.6340482, 151.3119399 -33.6341283, 151.3118764 -33.6342785, 151.3118111 -33.6343171, 151.3117407 -33.6343242, 151.3116548 -33.6343714, 151.3115774 -33.6344301, 151.3115396 -33.6344873, 151.3114211 -33.634513, 151.3113052 -33.6346045, 151.3110998 -33.6347877, 151.3109985 -33.6348234, 151.3109005 -33.6347963, 151.3106909 -33.6349348, 151.3105071 -33.6350309, 151.3103208 -33.6351179, 151.310069 -33.6351825, 151.3097696 -33.6352322, 151.3096086 -33.635244, 151.3093948 -33.6352435, 151.3090441 -33.6352125, 151.3086597 -33.6351697, 151.3082187 -33.6351067, 151.3078408 -33.6351545, 151.307524 -33.6352841, 151.3073975 -33.6353055, 151.3072775 -33.6353217, 151.3071209 -33.6353398, 151.3069885 -33.6353453, 151.306433 -33.6353639, 151.3059323 -33.6353869, 151.3056289 -33.6353986, 151.3054064 -33.6353666, 151.3052206 -33.6353457, 151.305045 -33.6353321, 151.3047852 -33.6352663, 151.3047211 -33.6354579, 151.3045869 -33.6354314, 151.3045102 -33.6356867, 151.3044966 -33.6357646, 151.3045468 -33.6358315, 151.3045997 -33.6360481, 151.3046201 -33.6363115, 151.3046954 -33.6365056, 151.3047436 -33.6366812, 151.3047748 -33.6367665, 151.3048075 -33.6368408, 151.3048717 -33.6369826, 151.3050031 -33.637252, 151.305065 -33.6374454, 151.3051008 -33.6375467, 151.3051413 -33.6376584, 151.3052487 -33.6378446, 151.3053474 -33.6379617, 151.3054934 -33.6379886, 151.305708 -33.6379651, 151.3058468 -33.6379607, 151.3059827 -33.637996, 151.306168 -33.6381341, 151.3062532 -33.6381587, 151.3064123 -33.6382735, 151.3065793 -33.638357, 151.3066676 -33.6384086, 151.3068558 -33.6385177, 151.3070249 -33.6386121, 151.3071352 -33.638641, 151.3073417 -33.6386696, 151.3075253 -33.6386838, 151.3076612 -33.6386748, 151.3078826 -33.6386944, 151.3080466 -33.6387006, 151.3082183 -33.6386509, 151.308299 -33.6386617, 151.3084814 -33.6387019, 151.3088506 -33.6387642, 151.3092776 -33.6389231, 151.309478 -33.6389727, 151.3095448 -33.6390979, 151.3095687 -33.6393541, 151.3091655 -33.639364, 151.3091822 -33.6395785, 151.3091059 -33.6396501, 151.3089681 -33.6397051, 151.3087253 -33.639853, 151.3086621 -33.6399851, 151.308612 -33.6400933, 151.3085208 -33.6401939, 151.308443 -33.6402455, 151.3081598 -33.6401743, 151.3078342 -33.6401616, 151.3074555 -33.6403128, 151.3072122 -33.6404601, 151.30689 -33.640609, 151.3066784 -33.640845, 151.3066167 -33.6409278, 151.3065441 -33.6412396, 151.3065669 -33.6416211, 151.3065741 -33.6417425, 151.3065779 -33.6418955, 151.3065289 -33.6420612, 151.3064069 -33.642226, 151.3063231 -33.6423393, 151.3061454 -33.6425287, 151.3060404 -33.6430769, 151.3059808 -33.6433132, 151.3057137 -33.6434893, 151.3055298 -33.6436801, 151.3052748 -33.6437907, 151.3052086 -33.6441075, 151.305339 -33.644371, 151.3056619 -33.6446621, 151.3060117 -33.6449718, 151.3064384 -33.6451874, 151.3068441 -33.6454211, 151.3070366 -33.6455238, 151.3070844 -33.6456381, 151.3070835 -33.6457407, 151.3069782 -33.645841, 151.3065488 -33.6461092, 151.3063446 -33.6462847, 151.3062687 -33.6464603, 151.3062594 -33.6466402, 151.3063084 -33.6467683, 151.306374 -33.6468996, 151.3065484 -33.6471678, 151.3069039 -33.6474756, 151.306923 -33.6476028, 151.3069812 -33.6475873, 151.307061 -33.6477047, 151.306982 -33.6479471, 151.3067159 -33.6478836, 151.306377 -33.6479158, 151.3061511 -33.6480247, 151.3059579 -33.6479777, 151.3047621 -33.6478162, 151.3044746 -33.6477698, 151.3044096 -33.6477466, 151.3043378 -33.647721, 151.3043081 -33.6477104, 151.3041962 -33.6477232, 151.3039921 -33.6477467, 151.3038728 -33.6477604, 151.3036729 -33.6477833, 151.3035247 -33.6478003, 151.3032758 -33.6478289, 151.3031221 -33.6478465, 151.3030926 -33.6478624, 151.3028059 -33.6480171, 151.3026113 -33.6481221, 151.3023792 -33.6482473, 151.302269 -33.6483068, 151.3021579 -33.6486711, 151.3021258 -33.6487765, 151.3020767 -33.6490497, 151.3020459 -33.6492209, 151.3020534 -33.6492626, 151.3020731 -33.6493722, 151.3020979 -33.64951, 151.3021166 -33.6496137, 151.3021534 -33.6498187, 151.3021861 -33.6499963, 151.3022319 -33.6502266, 151.302263 -33.6503828, 151.3022692 -33.6504761, 151.3022906 -33.6507976, 151.3022302 -33.6510054, 151.3021068 -33.6511712, 151.3020046 -33.6513175, 151.3019961 -33.6514769, 151.3019574 -33.6517837, 151.3019571 -33.6519717, 151.3026986 -33.6520273, 151.3027111 -33.6519688, 151.302885 -33.6519075, 151.3030319 -33.6518462, 151.3032822 -33.6518058, 151.3033494 -33.6518063, 151.303516 -33.6518077, 151.3036634 -33.6518254, 151.3038112 -33.6519038, 151.3039071 -33.6519546, 151.3039437 -33.6520266, 151.3040103 -33.6521577, 151.3040212 -33.6522759, 151.3040098 -33.6524009, 151.3039882 -33.6525006, 151.3039425 -33.6527111, 151.303911 -33.6528562, 151.3038914 -33.6529464, 151.3039915 -33.6529672, 151.3039388 -33.6530958, 151.3038982 -33.6532507, 151.3038481 -33.6534374, 151.3038045 -33.653657, 151.3037818 -33.653771, 151.3038466 -33.6539812, 151.3040727 -33.6543293, 151.3041864 -33.6545045, 151.3043552 -33.6547643, 151.3045648 -33.655087, 151.3045771 -33.6551217, 151.3046349 -33.6552835, 151.3046603 -33.6553546, 151.3048189 -33.6558061, 151.3049082 -33.6562101, 151.3048923 -33.6566351, 151.3048568 -33.657061, 151.3048838 -33.6571195, 151.3050206 -33.6571332, 151.3051852 -33.6571246, 151.3054287 -33.6570124, 151.305597 -33.6568343, 151.305788 -33.6566219, 151.3058438 -33.6565599, 151.3062314 -33.6561288, 151.3059952 -33.6558395, 151.306255 -33.6556345, 151.3063979 -33.6558568, 151.3064981 -33.6557303, 151.306516 -33.6555324, 151.3066183 -33.6554536, 151.3066373 -33.655197, 151.3068687 -33.6551851, 151.3071765 -33.6552764, 151.307356 -33.6553778, 151.3075773 -33.6555028, 151.3077565 -33.6555294, 151.3081498 -33.6555878, 151.3081928 -33.6555942, 151.3084362 -33.6555147, 151.3085369 -33.655866, 151.3086329 -33.6562012, 151.3087442 -33.656441, 151.3088847 -33.6566824, 151.3086583 -33.6567767, 151.3083789 -33.6568929, 151.3080165 -33.6569506, 151.3078852 -33.6568886, 151.3076566 -33.6568997, 151.3074463 -33.6570471, 151.3073354 -33.6572661, 151.3072939 -33.6574034, 151.3070973 -33.6575534, 151.3068734 -33.6577958, 151.3068261 -33.657926, 151.3068207 -33.6580654, 151.3068316 -33.6581472, 151.3069462 -33.658226, 151.3071428 -33.6583851, 151.307333 -33.6585343, 151.3073996 -33.6585929, 151.3077489 -33.6586684, 151.3077962 -33.6587456, 151.3077887 -33.6588404, 151.3082011 -33.6589686, 151.3083636 -33.6592103, 151.3083064 -33.6593704, 151.308246 -33.6595396, 151.3079716 -33.6595427, 151.3079753 -33.6597261, 151.3073257 -33.6597538, 151.3075779 -33.6599779, 151.307721 -33.6601923, 151.3078838 -33.6602593, 151.3079776 -33.6604967, 151.3080274 -33.6606608, 151.308148 -33.6608059, 151.3082399 -33.6609509, 151.3084122 -33.6610927, 151.3087317 -33.6613046, 151.3089093 -33.6614114, 151.308969 -33.6615048, 151.3092043 -33.6615578, 151.3095173 -33.6616284, 151.3096546 -33.6616594, 151.3097473 -33.6616802, 151.3098684 -33.6617076, 151.3099831 -33.6616768, 151.3100386 -33.6617621, 151.3101625 -33.6618285, 151.3103292 -33.6618332, 151.3104801 -33.6619233, 151.3105528 -33.661966, 151.3106154 -33.6619731, 151.3107963 -33.661947, 151.3109216 -33.6619731, 151.3110174 -33.6621307, 151.3110751 -33.6622093, 151.3112299 -33.662298, 151.311598 -33.6623017, 151.3118726 -33.6623163, 151.3118915 -33.6623207, 151.3120389 -33.6623555, 151.3121808 -33.6623889, 151.3122654 -33.66245, 151.3122663 -33.6625694, 151.3122247 -33.6626436, 151.3120754 -33.662714, 151.3120648 -33.6627562, 151.3120274 -33.662784, 151.3119583 -33.6628051, 151.3118189 -33.6628326, 151.3116242 -33.6628575, 151.3112931 -33.6628401, 151.3109994 -33.6628201, 151.3107807 -33.6628039, 151.3104901 -33.6628326, 151.3103388 -33.662865, 151.3101414 -33.662913, 151.3099402 -33.6630398, 151.3097946 -33.663208, 151.3097642 -33.6634232, 151.3098055 -33.6637234, 151.3098315 -33.6640508, 151.3098457 -33.6641589, 151.3098502 -33.6641996, 151.3099552 -33.6641867, 151.3100385 -33.6645799, 151.310089 -33.6648407, 151.3101474 -33.6650913, 151.3101843 -33.6652512, 151.3102314 -33.6653861, 151.3102605 -33.6654883, 151.3103606 -33.6656616, 151.310437 -33.6658005, 151.3105789 -33.6659527, 151.3109004 -33.6661035, 151.3110264 -33.6661914, 151.3112423 -33.6663435, 151.3112854 -33.6663678, 151.3114031 -33.666434, 151.311556 -33.666497, 151.3116458 -33.6665259, 151.3117277 -33.6665298, 151.3118617 -33.6665442, 151.3120303 -33.6666387, 151.3120949 -33.6666636, 151.3121611 -33.6666649, 151.3121596 -33.6667178, 151.312158 -33.6667738, 151.312061 -33.6667689, 151.3120004 -33.6667659, 151.3118522 -33.6667147, 151.3115244 -33.6666977, 151.3112721 -33.6666678, 151.3112471 -33.6666649, 151.3107743 -33.6663921, 151.3106986 -33.6663658, 151.3106246 -33.666375, 151.310571 -33.6664249, 151.310541 -33.6665363, 151.3105505 -33.6666255, 151.3106876 -33.6667475, 151.3105891 -33.6667724, 151.3105631 -33.666779, 151.3105836 -33.6668459, 151.3105573 -33.6668654, 151.3105095 -33.666901, 151.3104686 -33.6669495, 151.3102538 -33.6669233, 151.3101308 -33.6669083, 151.310114 -33.6669062, 151.310073 -33.6668039, 151.3101549 -33.6667383, 151.3101212 -33.6667061, 151.3100478 -33.666636, 151.309948 -33.6665233, 151.3098617 -33.6664258, 151.3098051 -33.6663619, 151.3097822 -33.6663217, 151.3097103 -33.6661953, 151.3096632 -33.6661127, 151.3096397 -33.666059, 151.3095541 -33.6658643, 151.3095025 -33.6657467, 151.3093354 -33.6655893, 151.3092035 -33.6655898, 151.3090139 -33.6653742, 151.3089273 -33.6652916, 151.3089026 -33.6650931, 151.3088926 -33.6650122, 151.30877 -33.6648115, 151.3087035 -33.6647027, 151.3086688 -33.6646004, 151.3085711 -33.6645203, 151.308549 -33.6643538, 151.308478 -33.6642612, 151.308422 -33.6641882, 151.3082776 -33.6639998, 151.3081909 -33.6638868, 151.3081238 -33.6637994, 151.3079877 -33.663622, 151.3078586 -33.6634537, 151.3077634 -33.6633296, 151.3076908 -33.6632349, 151.3076224 -33.6631457, 151.3073595 -33.6628759, 151.3071433 -33.6626866, 151.3068115 -33.6624852, 151.3056013 -33.6628924, 151.3050863 -33.6632996, 151.3050312 -33.6650835, 151.3041574 -33.6656485, 151.3028787 -33.665576, 151.3023671 -33.665723, 151.30243 -33.665693, 151.302456 -33.665661, 151.302497 -33.665583, 151.302544 -33.665509, 151.302579 -33.665478, 151.302601 -33.665464, 151.302651 -33.66544, 151.302699 -33.665426, 151.302793 -33.665413, 151.302876 -33.665385, 151.302901 -33.665373, 151.302922 -33.665358, 151.302942 -33.665339, 151.302973 -33.665294, 151.302993 -33.665245, 151.302997 -33.665195, 151.302991 -33.665118, 151.30299 -33.664997, 151.302981 -33.664922, 151.302968 -33.664873, 151.302952 -33.664831, 151.302933 -33.664815, 151.302902 -33.664805, 151.302834 -33.664791, 151.30278 -33.664785, 151.302801 -33.664783, 151.302826 -33.664774, 151.302851 -33.66476, 151.30287 -33.664745, 151.30288 -33.664729, 151.302881 -33.664708, 151.302875 -33.664687, 151.302855 -33.664672, 151.302777 -33.664673, 151.302759 -33.664667, 151.302744 -33.664635, 151.302741 -33.664615, 151.302746 -33.664591, 151.302768 -33.66459, 151.302797 -33.664596, 151.302875 -33.664605, 151.302938 -33.664606, 151.302966 -33.6646, 151.302987 -33.664591, 151.303025 -33.664566, 151.303055 -33.664535, 151.303072 -33.664498, 151.303087 -33.664436, 151.30312 -33.664334, 151.303132 -33.66431, 151.3032004 -33.6642871, 151.3032329 -33.6642238, 151.3032259 -33.6641794, 151.303159 -33.664135, 151.303146 -33.66409, 151.303142 -33.664056, 151.303143 -33.664021, 151.303149 -33.663987, 151.303162 -33.663957, 151.303179 -33.663936, 151.303254 -33.66388, 151.303295 -33.663843, 151.303328 -33.663806, 151.303355 -33.663766, 151.303375 -33.663708, 151.303393 -33.663689, 151.303441 -33.663666, 151.303456 -33.663648, 151.303467 -33.663628, 151.303467 -33.663609, 151.30345 -33.663574, 151.303459 -33.663554, 151.303463 -33.663514, 151.303474 -33.663468, 151.303496 -33.663418, 151.303524 -33.663376, 151.303551 -33.663357, 151.303601 -33.663361, 151.303616 -33.66335, 151.303622 -33.663325, 151.303618 -33.663303, 151.303594 -33.663294, 151.30356 -33.66329, 151.303531 -33.663265, 151.303526 -33.663229, 151.303527 -33.663184, 151.303542 -33.663098, 151.303559 -33.663046, 151.303585 -33.662988, 151.3036342 -33.6629696, 151.3036589 -33.6629551, 151.303679 -33.66288, 151.303743 -33.662893, 151.303757 -33.662875, 151.303756 -33.662855, 151.303734 -33.662837, 151.30371 -33.66283, 151.303688 -33.662801, 151.303688 -33.66276, 151.303699 -33.662711, 151.303716 -33.662662, 151.303748 -33.662605, 151.303791 -33.662561, 151.303819 -33.662521, 151.303844 -33.662471, 151.30391 -33.662315, 151.303921 -33.662279, 151.303931 -33.662211, 151.303924 -33.662111, 151.303929 -33.66202, 151.303932 -33.66199, 151.303938 -33.661968, 151.303961 -33.661957, 151.304019 -33.661969, 151.30404 -33.661958, 151.304061 -33.661934, 151.304079 -33.661904, 151.304089 -33.661875, 151.304088 -33.661855, 151.304052 -33.661835, 151.304008 -33.66182, 151.303994 -33.661796, 151.304001 -33.661765, 151.304019 -33.66174, 151.304003 -33.661663, 151.303978 -33.661576, 151.303927 -33.661436, 151.303874 -33.661332, 151.303864 -33.661301, 151.303869 -33.661279, 151.303887 -33.661266, 151.303975 -33.661245, 151.304002 -33.661235, 151.304045 -33.661208, 151.304089 -33.661173, 151.304127 -33.661134, 151.304153 -33.661096, 151.3041845 -33.661063, 151.3041963 -33.6610446, 151.3042879 -33.6609517, 151.304294 -33.660918, 151.30432 -33.660894, 151.30435 -33.660845, 151.304457 -33.660704, 151.304598 -33.660549, 151.304603 -33.660531, 151.304586 -33.66052, 151.304516 -33.660502, 151.304494 -33.660493, 151.304469 -33.660475, 151.304401 -33.66041, 151.304375 -33.6604, 151.304356 -33.660399, 151.304276 -33.660412, 151.304226 -33.660415, 151.304074 -33.660416, 151.303851 -33.660429, 151.303736 -33.660442, 151.303627 -33.660458, 151.303528 -33.660477, 151.303462 -33.660484, 151.30333 -33.660473, 151.303283 -33.660474, 151.303236 -33.66048, 151.303191 -33.660494, 151.303126 -33.660533, 151.303103 -33.660543, 151.303041 -33.660556, 151.302966 -33.660588, 151.302912 -33.660629, 151.302806 -33.660722, 151.302748 -33.660759, 151.302657 -33.660807, 151.302628 -33.660816, 151.302604 -33.660816, 151.302565 -33.660792, 151.302533 -33.660797, 151.302438 -33.660894, 151.30241 -33.660905, 151.302387 -33.660899, 151.302349 -33.66091, 151.302274 -33.661023, 151.3022532 -33.6610312, 151.3019519 -33.6609581, 151.3016101 -33.6608064, 151.3013467 -33.6606214, 151.3011704 -33.6604271, 151.3008898 -33.6601085, 151.3005607 -33.6596138, 151.3002855 -33.6593947, 151.2998268 -33.6593615, 151.2996155 -33.6594152, 151.2992842 -33.6593283, 151.2992922 -33.6591158, 151.2987432 -33.6590742, 151.298426 -33.659065, 151.2981297 -33.6590478, 151.297595 -33.6590135, 151.2975523 -33.659156, 151.2972489 -33.6591009, 151.2968294 -33.6585842, 151.2965021 -33.6581848, 151.2965457 -33.6579805, 151.2963914 -33.6574896, 151.2964095 -33.6572932, 151.2963246 -33.6571422, 151.296241 -33.656984, 151.2961291 -33.6567455, 151.2961688 -33.6566973, 151.2959987 -33.6564111, 151.2959691 -33.6563467, 151.2958996 -33.6561957, 151.2959226 -33.6560837, 151.2956479 -33.6558449, 151.2954425 -33.6556663, 151.2953975 -33.6556272, 151.2951511 -33.655592, 151.295093 -33.6556341, 151.2950289 -33.6556281, 151.2949527 -33.655576, 151.2949928 -33.6554497, 151.2949087 -33.6554237, 151.2948666 -33.6554277, 151.2947945 -33.6555058, 151.2943231 -33.6554028, 151.2942924 -33.6553956, 151.2939168 -33.6553075, 151.2938793 -33.655287, 151.2936343 -33.6551532, 151.293636 -33.6551104, 151.2936383 -33.655055, 151.2935263 -33.6549293, 151.2934345 -33.6548261, 151.2934099 -33.6547985, 151.2932562 -33.6546539, 151.2931705 -33.6545732, 151.2930507 -33.6544604, 151.2930352 -33.6544459, 151.2929727 -33.6544026, 151.2927559 -33.6542525, 151.2925636 -33.6541194, 151.2924433 -33.6540747, 151.2923525 -33.6540409, 151.2922236 -33.653993, 151.2918737 -33.653921, 151.2916986 -33.6539289, 151.2913681 -33.654112, 151.2909043 -33.6542585, 151.290423 -33.6541989, 151.2898425 -33.65394, 151.2897702 -33.6537842, 151.2894556 -33.6536283, 151.2891723 -33.6534186, 151.289004 -33.6532328, 151.288853 -33.6529797, 151.2887387 -33.6528377, 151.2887006 -33.652698, 151.2887572 -33.6524584, 151.2888789 -33.6522278, 151.2891971 -33.6516584, 151.2896529 -33.6516353, 151.2896101 -33.6512403, 151.2895401 -33.6510137, 151.2891535 -33.6507449, 151.2890422 -33.6506899, 151.2887039 -33.6510817, 151.2883733 -33.6510111, 151.2879831 -33.6508166, 151.2871055 -33.6500486, 151.2868473 -33.6497111, 151.2868016 -33.64952, 151.2868226 -33.6492937, 151.2868833 -33.6492947, 151.2869595 -33.6490754, 151.2870216 -33.64907, 151.2872709 -33.6487015, 151.2873628 -33.6486356, 151.2874368 -33.6482657, 151.2871706 -33.6476511, 151.2862758 -33.6469087, 151.2861835 -33.6467463, 151.2861933 -33.6464802, 151.2862269 -33.6462601, 151.2864559 -33.6462379, 151.2864742 -33.6460869, 151.2864112 -33.6460013, 151.2863772 -33.6459552, 151.2863006 -33.6459424, 151.2854315 -33.6448484, 151.2852894 -33.6446925, 151.2850596 -33.6445565, 151.2848669 -33.6443281, 151.2843444 -33.6445478, 151.2841912 -33.6446106, 151.283931 -33.6447775, 151.2836067 -33.6448392, 151.2828647 -33.6452115, 151.2821754 -33.6455399, 151.2818985 -33.6456554, 151.2816227 -33.6455959, 151.2806777 -33.6457641, 151.2803664 -33.6458361, 151.280103 -33.6459935, 151.2796668 -33.6465206, 151.2787393 -33.6473377, 151.2784408 -33.6475492, 151.278357 -33.6478915, 151.2782024 -33.6482054, 151.2778594 -33.6486589, 151.2777258 -33.6489227, 151.2777023 -33.6492565, 151.2777304 -33.6496334, 151.2778191 -33.6499152, 151.2779668 -33.6502238, 151.2781656 -33.6505256, 151.2783993 -33.6507447, 151.2788496 -33.6512516, 151.2790855 -33.6514242, 151.2796715 -33.6516868, 151.2800097 -33.6519457, 151.279848 -33.6521906, 151.2799206 -33.6524905, 151.2801588 -33.6527967, 151.2800272 -33.6527838, 151.2795446 -33.6525627, 151.2792711 -33.6523697, 151.2783705 -33.6520382, 151.2778504 -33.6518205, 151.2775383 -33.6516819, 151.2774194 -33.6516646, 151.2771817 -33.6516423, 151.2769379 -33.6516522, 151.2767774 -33.6517215, 151.276718 -33.6518452, 151.276715 -33.6519887, 151.2767361 -33.6523671, 151.276667 -33.652792, 151.2763667 -33.6536812, 151.2761509 -33.6544856, 151.2758034 -33.6546226, 151.2755259 -33.6548637, 151.2753796 -33.6550921, 151.2750371 -33.655557, 151.274706 -33.6559888, 151.2748456 -33.6560217, 151.274683 -33.6563002, 151.2745079 -33.6567713, 151.2742913 -33.6568879, 151.274131 -33.6569733, 151.2740316 -33.6570828, 151.273977 -33.657339, 151.273512 -33.6574298, 151.2733581 -33.6575899, 151.2732331 -33.6577421, 151.2729829 -33.6582386, 151.2726109 -33.6587857, 151.2723608 -33.6588445, 151.2723416 -33.6586977, 151.2725692 -33.6582332, 151.2726302 -33.6579182, 151.2728418 -33.6576113, 151.2730054 -33.6574805, 151.2730374 -33.6572589, 151.2731753 -33.6570374, 151.2732978 -33.6569069, 151.273358 -33.6567796, 151.273313 -33.6565985, 151.2730581 -33.6564588, 151.2719197 -33.6564524, 151.2712553 -33.65631, 151.2706961 -33.6561512, 151.2700604 -33.6561654, 151.269402 -33.656206, 151.2685866 -33.6564918, 151.2680865 -33.6567754, 151.2675413 -33.6569457, 151.2672603 -33.6570816, 151.2665509 -33.6574545, 151.2657542 -33.6575991, 151.2648745 -33.6578313, 151.264227 -33.6582748, 151.2640964 -33.6581619, 151.2640651 -33.6580647, 151.2644899 -33.6576425, 151.2654465 -33.6574621, 151.2661933 -33.6571088, 151.2664817 -33.6567413, 151.2673146 -33.6565792, 151.2679464 -33.6564147, 151.2684858 -33.6559834, 151.2690359 -33.6558918, 151.2699918 -33.6556011, 151.2705938 -33.655493, 151.2721421 -33.6558659, 151.2734569 -33.6556366, 151.2736918 -33.6555048, 151.2738351 -33.6552046, 151.2738338 -33.6550499, 151.2739265 -33.6544924, 151.27404 -33.6542544, 151.2741709 -33.653957, 151.2745078 -33.6532126, 151.274565 -33.6523193, 151.2743024 -33.6518642, 151.2742592 -33.6517194, 151.2745719 -33.6511564, 151.2743207 -33.6501633, 151.2742079 -33.649944, 151.2737407 -33.6496073, 151.2734067 -33.6492594, 151.2732407 -33.6491365, 151.2735085 -33.6489203, 151.273891 -33.6488359, 151.2747696 -33.6480957, 151.2755047 -33.6466385, 151.275553 -33.6463605, 151.2754601 -33.64606, 151.2752667 -33.6457678, 151.2745753 -33.6451575, 151.2747575 -33.6450529, 151.2749132 -33.645035, 151.2753929 -33.6449246, 151.2755449 -33.6448555, 151.2756743 -33.644739, 151.2757635 -33.6447027, 151.2759937 -33.6446206, 151.2763882 -33.6445196, 151.2767388 -33.6443156, 151.2770621 -33.6441848, 151.2771942 -33.6441291, 151.2774976 -33.6439172, 151.2776152 -33.6437404, 151.2777613 -33.6436143, 151.2778818 -33.6434646, 151.2781432 -33.6433287, 151.2782067 -33.6432685, 151.2783009 -33.6430923, 151.2786018 -33.6429413, 151.2789187 -33.6427584, 151.2789905 -33.6426805, 151.279146 -33.6426825, 151.2792692 -33.6426164, 151.2795129 -33.6425302, 151.2800261 -33.6423893, 151.2801598 -33.6423063, 151.2802234 -33.6422816, 151.2805351 -33.6422489, 151.280683 -33.6422715, 151.2807225 -33.6422737, 151.2807371 -33.642198, 151.2808645 -33.6421206, 151.2808802 -33.6421342, 151.2810615 -33.6420043, 151.2810915 -33.641947, 151.2811231 -33.6418661, 151.2811194 -33.6416256, 151.280983 -33.6413034, 151.2807091 -33.6410562, 151.2805248 -33.6409217, 151.2803974 -33.6408387, 151.2801726 -33.6406924, 151.2799855 -33.6406138, 151.2799709 -33.6406074, 151.2796005 -33.6404439, 151.2795156 -33.6404536, 151.2792593 -33.6404176, 151.2792201 -33.6404375, 151.279102 -33.6404127, 151.2788965 -33.6402732, 151.2787234 -33.6400981, 151.2783481 -33.6398361, 151.2779032 -33.6394975, 151.2778783 -33.6394324, 151.2778771 -33.6393464, 151.2777868 -33.6392296, 151.2777116 -33.6392214, 151.2775649 -33.6392944, 151.2775388 -33.6392883, 151.2772559 -33.6391101, 151.2770361 -33.6391371, 151.2768827 -33.6388691, 151.2767912 -33.6387882, 151.2767169 -33.6384256, 151.2767392 -33.6383383, 151.2767142 -33.6382198, 151.2767465 -33.6380891, 151.2769316 -33.6381236, 151.2770504 -33.6382334, 151.2771661 -33.6382596, 151.277688 -33.638106, 151.2779191 -33.6380201, 151.2780889 -33.6380101, 151.2782181 -33.638042, 151.2784989 -33.6381412, 151.2787375 -33.6381092, 151.279047 -33.6382226, 151.279171 -33.6382104, 151.2792472 -33.6381224, 151.2793965 -33.6381902, 151.2795083 -33.6382082, 151.2795485 -33.6382074, 151.2795984 -33.6381675, 151.2796387 -33.6381637, 151.2797412 -33.6382398, 151.2798299 -33.6382535, 151.2799914 -33.6382443, 151.2800075 -33.6382235, 151.2799464 -33.6381717, 151.2800289 -33.6380818, 151.2800682 -33.6380864, 151.2801516 -33.6381552, 151.2802145 -33.6381727, 151.2803256 -33.6381459, 151.2805726 -33.6381475, 151.2809499 -33.638084, 151.2812197 -33.6380791, 151.281425 -33.6381766, 151.2815454 -33.6381481, 151.2816234 -33.6380905, 151.2818824 -33.6380801, 151.2821281 -33.6379636, 151.2822325 -33.6378728, 151.2823629 -33.6375367, 151.2824736 -33.637357, 151.2825066 -33.6371665, 151.2825888 -33.6369888, 151.2826525 -33.6367635, 151.2826609 -33.6365795, 151.2826194 -33.6363878, 151.2825016 -33.6362631, 151.2822833 -33.6361512, 151.281965 -33.6361086, 151.2813285 -33.6360606, 151.2808453 -33.6361208, 151.2803916 -33.6362361, 151.2802011 -33.6362497, 151.2798708 -33.6363695, 151.2796614 -33.6364673, 151.2795378 -33.6365833, 151.2794291 -33.6366759, 151.279064 -33.6369827, 151.2789425 -33.6369448, 151.2787369 -33.6371992, 151.2785462 -33.6372088, 151.2784001 -33.6372623, 151.2782489 -33.6371554, 151.2781024 -33.6371384, 151.2780047 -33.6370695, 151.2777917 -33.6370109, 151.277537 -33.6367554, 151.2773851 -33.6365375, 151.277404 -33.636266, 151.277035 -33.6357881, 151.2766693 -33.6357324, 151.2761383 -33.6353261, 151.2759104 -33.6353024, 151.275647 -33.6351929, 151.2751342 -33.6351074, 151.2749569 -33.635049, 151.274573 -33.6350594, 151.2741146 -33.6349165, 151.2744498 -33.6346539, 151.2747241 -33.6341771, 151.274593 -33.6338576, 151.2742354 -33.6334995, 151.2737233 -33.6332694, 151.2735013 -33.6330091, 151.2732451 -33.632654, 151.2729629 -33.6325025, 151.2726986 -33.6325266, 151.2723067 -33.6322728, 151.2719746 -33.6321556, 151.2713942 -33.6318135, 151.2704009 -33.6315437, 151.2706747 -33.6314471, 151.2709903 -33.6314293, 151.2713299 -33.6314856, 151.2722679 -33.6317144, 151.2731086 -33.6320455, 151.2741706 -33.6322437, 151.274507 -33.6323487, 151.2746874 -33.6323433, 151.2751517 -33.632337, 151.2754074 -33.6324652, 151.2758791 -33.6329542, 151.2761275 -33.6330689, 151.2764368 -33.6331425, 151.2765345 -33.6332249, 151.2767188 -33.6333822, 151.2768237 -33.6333528, 151.2769244 -33.633416, 151.277013 -33.633422, 151.2772205 -33.6334233, 151.2774145 -33.6334572, 151.2778456 -33.6335085, 151.2784328 -33.6334419, 151.2786631 -33.6333291, 151.27887 -33.6331683, 151.2789482 -33.6328656, 151.2792317 -33.6327245, 151.279412 -33.6326901, 151.2796238 -33.6326984, 151.2799302 -33.6327686, 151.2801291 -33.6328674, 151.2803875 -33.6330171, 151.2807102 -33.6330801, 151.2813236 -33.6329445, 151.281918 -33.6323147, 151.2818445 -33.6321027, 151.2819542 -33.6319521, 151.2819593 -33.6318298, 151.2820565 -33.6317016, 151.2825805 -33.6316638, 151.282838 -33.6316379, 151.2834115 -33.6316682, 151.2835362 -33.6316603, 151.2836981 -33.6316261, 151.2838431 -33.6317499, 151.2839717 -33.6317794, 151.28423 -33.6316129, 151.2842738 -33.6315222, 151.2842175 -33.6311955, 151.2841443 -33.6311056, 151.2840521 -33.6308644, 151.2837956 -33.6304948, 151.2836434 -33.6294083, 151.2853034 -33.6293965, 151.2870276 -33.6301756, 151.2875896 -33.6302564, 151.2879176 -33.6301111, 151.2883916 -33.6292782, 151.2890507 -33.6288222, 151.2900708 -33.6287097, 151.2904036 -33.6285038, 151.2905151 -33.6283612, 151.2906139 -33.6282349, 151.2900713 -33.6278987, 151.2897104 -33.6279315, 151.2894119 -33.6279771, 151.2888912 -33.6281419, 151.2883747 -33.6280592, 151.2878773 -33.6276897, 151.2878555 -33.6276613, 151.2875528 -33.627266, 151.2872473 -33.6268784, 151.285099 -33.6251804, 151.2847778 -33.6248202, 151.2835553 -33.6240911, 151.2832806 -33.6240873, 151.2828571 -33.6228272, 151.2826775 -33.6221065, 151.2828838 -33.6218413, 151.2833013 -33.6218359, 151.2840669 -33.6223538, 151.2846508 -33.6225255, 151.2851617 -33.6227115, 151.2857312 -33.6228127, 151.2870026 -33.6228439, 151.2874513 -33.6227474, 151.2887165 -33.6216808, 151.2889754 -33.6206379, 151.2899847 -33.6198408, 151.2900623 -33.6197129, 151.29031 -33.6196005, 151.2904797 -33.6196188, 151.2908397 -33.6194757, 151.2908756 -33.6197554, 151.2909231 -33.6201767, 151.2910474 -33.6208501, 151.2915305 -33.62155, 151.2922757 -33.6221461, 151.2926567 -33.6224952, 151.2932121 -33.6226937, 151.2940076 -33.6227947, 151.2946884 -33.622872, 151.2955129 -33.6225568, 151.2960984 -33.6224318, 151.2964385 -33.6222533, 151.2971315 -33.6218048, 151.2981551 -33.6212885, 151.29827 -33.621085, 151.2987385 -33.6208986, 151.299133 -33.6208873, 151.2995244 -33.6209654, 151.2999189 -33.6211445, 151.3003976 -33.6213991, 151.3008262 -33.6215155, 151.3011173 -33.6215424, 151.3015128 -33.6214606, 151.3020546 -33.6212685, 151.302674 -33.620833, 151.3028596 -33.6205246, 151.3028642 -33.6203104, 151.3028108 -33.6200191, 151.3027376 -33.6197841, 151.3025092 -33.6194666, 151.3023572 -33.6191049, 151.302316 -33.6186368, 151.3022237 -33.6182349, 151.30215 -33.6180382, 151.3020011 -33.61787, 151.3018339 -33.6176635, 151.3016443 -33.6173771, 151.3013479 -33.617045, 151.3003336 -33.6155172, 151.3002006 -33.6151876, 151.3005532 -33.6141993, 151.3010425 -33.6138569, 151.3022816 -33.6132847, 151.3030493 -33.6125048, 151.3032034 -33.6121156, 151.3031856 -33.6120063, 151.3031256 -33.6118804, 151.3027999 -33.6115676, 151.3026855 -33.6114889, 151.302481 -33.6114306, 151.3024332 -33.6114037, 151.3023765 -33.6113695, 151.3023487 -33.6112992, 151.3022531 -33.6112445, 151.3022031 -33.6112538, 151.302158 -33.6112585, 151.3021086 -33.6112455, 151.3020897 -33.6112205, 151.3019664 -33.6111714, 151.3017488 -33.6109498, 151.3016104 -33.6108339, 151.3016047 -33.6106249, 151.3016104 -33.6103847, 151.3016568 -33.6101267, 151.3022913 -33.6096276, 151.3029617 -33.6088513, 151.3030981 -33.6086329, 151.3031441 -33.6083453, 151.3030137 -33.6078947, 151.3030559 -33.6077765, 151.3031902 -33.6076454, 151.3042491 -33.6066353, 151.3043566 -33.6064918, 151.3043068 -33.6061562, 151.3043758 -33.6059645, 151.3041341 -33.6053765, 151.3039058 -33.6052414, 151.3042569 -33.6048971, 151.3045792 -33.6044241, 151.3046615 -33.6042011, 151.3046905 -33.6040662, 151.3047058 -33.6038201, 151.3044208 -33.603204, 151.3041648 -33.6030275, 151.3039039 -33.602791, 151.303405 -33.6026632, 151.3026905 -33.6025687, 151.3025884 -33.6025687, 151.3022011 -33.6025687, 151.3013238 -33.6027527, 151.3006255 -33.6031404, 151.3003391 -33.6034685, 151.2976235 -33.6045471, 151.2973192 -33.6047559, 151.2968954 -33.6051535, 151.2965851 -33.605591, 151.2964124 -33.6057449, 151.2962131 -33.6058083, 151.2960976 -33.605831, 151.2958055 -33.6058264, 151.2950274 -33.6059687, 151.2943888 -33.6060582, 151.2941322 -33.6061725, 151.2939173 -33.6064161, 151.2934757 -33.6063465, 151.2931176 -33.6061129, 151.293046 -33.6058345, 151.2927953 -33.6056606, 151.2924014 -33.6056953, 151.2912018 -33.6059936, 151.2908139 -33.6060085, 151.290414 -33.6061328, 151.2903104 -33.6061348, 151.2897456 -33.6061526, 151.2892287 -33.605849, 151.2890473 -33.6057252, 151.2879611 -33.6057053, 151.2874598 -33.6055661, 151.2869525 -33.6056755, 151.2867794 -33.6056407, 151.2862542 -33.6057252, 151.2859558 -33.6059588, 151.2855082 -33.6062421, 151.2853948 -33.6059886, 151.2846905 -33.6058146, 151.2845115 -33.6057301, 151.2842012 -33.6057103, 151.2838132 -33.6055214, 151.2839027 -33.605253, 151.2838252 -33.6050193, 151.2837714 -33.6048454, 151.2843793 -33.6050984, 151.2845951 -33.6051834, 151.2851859 -33.605243, 151.285353 -33.6052132, 151.2855141 -33.6051337, 151.2858424 -33.6048304, 151.2859498 -33.6046565, 151.2858126 -33.6038959, 151.2858126 -33.6035679, 151.285908 -33.6034784, 151.2863318 -33.6037866, 151.2868033 -33.6036573, 151.2870062 -33.6038413, 151.2872091 -33.6039606, 151.2872986 -33.6039655, 151.2874359 -33.60405, 151.2877104 -33.6041495, 151.2880745 -33.6041992, 151.2884385 -33.6042737, 151.2887489 -33.6043682, 151.2891189 -33.6043682, 151.2894949 -33.6042787, 151.2899127 -33.6041693, 151.2901216 -33.6040401, 151.2902171 -33.603881, 151.2904976 -33.6037518, 151.2905871 -33.603538, 151.2906885 -33.6034386, 151.2908139 -33.6035132, 151.2910108 -33.6034685, 151.2913689 -33.6038114, 151.2915301 -33.6040799, 151.2914465 -33.6047459, 151.2914704 -33.6051287, 151.29149 -33.6052979, 151.291632 -33.6054201, 151.2919267 -33.6052104, 151.2924122 -33.6050528, 151.2925923 -33.604733, 151.2928386 -33.6044225, 151.2930521 -33.6042504, 151.2932734 -33.6040441, 151.2934195 -33.6039269, 151.2936714 -33.6035002, 151.2937557 -33.6034571, 151.2938457 -33.6034111, 151.2941434 -33.603126, 151.2944404 -33.6023615, 151.2943841 -33.602182, 151.294405 -33.6020296, 151.2944806 -33.6019311, 151.2946107 -33.6018682, 151.2946608 -33.6018584, 151.295051 -33.6017826, 151.2955173 -33.60161, 151.2963206 -33.6012283, 151.296558 -33.6009547, 151.2967407 -33.5999614, 151.2968677 -33.5997368, 151.2988671 -33.5973697, 151.2989488 -33.5972686, 151.2991532 -33.5969813, 151.2995534 -33.5963031, 151.2999658 -33.5956811, 151.3001855 -33.5951315, 151.3005874 -33.5945197, 151.302552 -33.5949155, 151.3028632 -33.5948618, 151.3030429 -33.5947233, 151.3031287 -33.5945535, 151.3030904 -33.5943391, 151.3030686 -33.5942316, 151.3029705 -33.593747, 151.3019702 -33.5928784, 151.3014432 -33.5923201, 151.3011255 -33.5918395, 151.3008497 -33.591104, 151.3007828 -33.5906568, 151.3008915 -33.5902556, 151.3010461 -33.5896747, 151.3012258 -33.5891105, 151.3014264 -33.58868, 151.3017023 -33.5884, 151.3018903 -33.5881994, 151.3020282 -33.5879403, 151.3020032 -33.5877355, 151.3018037 -33.5875557, 151.301999 -33.5874722, 151.3023082 -33.5875725, 151.3033566 -33.5878625, 151.3042083 -33.5879292, 151.3046441 -33.5878983, 151.3050303 -33.587648, 151.305438 -33.5874156, 151.3056955 -33.5872369, 151.3060389 -33.5870581, 151.3064895 -33.5869509, 151.3068757 -33.5867721, 151.3071761 -33.5864325, 151.3072405 -33.5859856, 151.3071456 -33.5853211, 151.3072137 -33.5849481, 151.3073155 -33.5844306, 151.3078953 -33.5843836, 151.3088015 -33.5840048, 151.3093136 -33.5837157, 151.3095579 -33.58334, 151.3096223 -33.5829825, 151.3096008 -33.5827143, 151.3094506 -33.5822138, 151.3093919 -33.5818465, 151.3093085 -33.5815606, 151.309429 -33.5812029, 151.3097111 -33.5810775, 151.3101373 -33.5809267, 151.3105298 -33.5805499, 151.310781 -33.580158, 151.3109126 -33.5794561, 151.3213948 -33.5797474, 151.3224698 -33.5797773, 151.3225128 -33.580192, 151.3228132 -33.5807069, 151.3233968 -33.5809929, 151.3242551 -33.5811002, 151.3248395 -33.580856, 151.3249462 -33.5810756, 151.3255922 -33.5813402, 151.3257685 -33.5814864, 151.3260011 -33.5819709, 151.3260516 -33.5823199, 151.3260804 -33.5825195, 151.3260428 -33.5830435, 151.3257784 -33.5841651, 151.3250265 -33.5857962, 151.324164 -33.5870972, 151.3239808 -33.5873826, 151.3239408 -33.5874404, 151.3238635 -33.5875905, 151.3227649 -33.589042, 151.3211685 -33.5911369, 151.3203357 -33.5920974, 151.319223 -33.5933806, 151.3191294 -33.5934028, 151.3190456 -33.5935137, 151.3188733 -33.593728, 151.3188297 -33.5938636, 151.3188459 -33.5939265, 151.3188608 -33.5940457, 151.3189079 -33.5942421, 151.3190831 -33.5944526, 151.3194936 -33.5948088, 151.3196794 -33.5952523, 151.3199447 -33.5954489, 151.3198046 -33.5962628, 151.3193634 -33.5971878, 151.3185291 -33.5985726, 151.3180572 -33.599092, 151.3177033 -33.5993634, 151.3176415 -33.5994196, 151.3168943 -33.6003882, 151.3161472 -33.6011322, 151.316018 -33.6016235, 151.3161477 -33.6021049, 151.3161354 -33.6022005, 151.3162541 -33.6023804, 151.3167197 -33.6026848, 151.3168564 -33.6025713, 151.3175515 -33.6030216, 151.3182295 -33.6033765, 151.3186399 -33.6036086, 151.31898 -33.603801, 151.3197994 -33.6043548, 151.3198829 -33.6044126, 151.3207929 -33.6051225, 151.320737 -33.6053584, 151.3208042 -33.6055117, 151.321049 -33.6057884, 151.3212134 -33.6059481, 151.3214503 -33.6062261, 151.3215606 -33.606518, 151.3216886 -33.6067608, 151.3219761 -33.6070231, 151.3222438 -33.6072445, 151.3224998 -33.6073577, 151.3228582 -33.6074561, 151.3229704 -33.6074643, 151.323061 -33.6077053, 151.3230216 -33.607912, 151.3230433 -33.6081826, 151.3230334 -33.6086302, 151.323187 -33.6088861, 151.3234134 -33.6091435, 151.32363 -33.6092977, 151.324014 -33.6094125, 151.3242877 -33.6094371, 151.3249388 -33.6093487, 151.3253886 -33.6092765, 151.3256547 -33.6092435, 151.3256314 -33.6093754, 151.3257116 -33.6095253, 151.3258096 -33.6097346, 151.325843 -33.6099043, 151.3257687 -33.610063, 151.3257583 -33.6103258, 151.3255744 -33.6107456, 151.3255414 -33.6111517, 151.3256013 -33.6112996, 151.3257422 -33.6113868, 151.326247 -33.6119288, 151.3265774 -33.6124436, 151.3265404 -33.6130105, 151.3267369 -33.6134091, 151.3267756 -33.6138395, 151.3273522 -33.6148229, 151.3271927 -33.616296, 151.3271129 -33.6168819, 151.3269724 -33.6174476, 151.3266909 -33.6183205, 151.326721 -33.618555, 151.3269491 -33.6183581, 151.3270974 -33.6184228, 151.327231 -33.6185179, 151.3272723 -33.6187041, 151.3275067 -33.6189656, 151.3276569 -33.6191825, 151.3276975 -33.6192147, 151.3278499 -33.6194417, 151.3278919 -33.6195923, 151.3278214 -33.6197481, 151.3279737 -33.6199241, 151.3273018 -33.6198171, 151.3271196 -33.6199061, 151.3270093 -33.6199966, 151.3270262 -33.6201563, 151.3270382 -33.6202694, 151.3268705 -33.6206193, 151.3270666 -33.6208198, 151.3277237 -33.6209815, 151.3282395 -33.6209736, 151.3277114 -33.6213101, 151.3269487 -33.6214526, 151.3265322 -33.6213316, 151.3263336 -33.6215806, 151.3262149 -33.6215561, 151.3256354 -33.621395, 151.3253099 -33.6212858, 151.3246957 -33.6210409, 151.3241504 -33.6209225, 151.3235377 -33.6209175, 151.3235291 -33.6208092, 151.3231952 -33.6208354, 151.3231392 -33.6203957, 151.3231375 -33.620382, 151.3230436 -33.6203962, 151.3229407 -33.6204117, 151.3229854 -33.6209915, 151.3226153 -33.621109, 151.3219379 -33.6211734, 151.3215763 -33.621184, 151.3213648 -33.6211877, 151.3212165 -33.6211516, 151.3211132 -33.6210586, 151.3208881 -33.6207148, 151.3207596 -33.6204216, 151.3205602 -33.6202188, 151.3204076 -33.6201121, 151.3202298 -33.6200164, 151.320205 -33.6200044, 151.3199487 -33.6198806, 151.3196383 -33.6197143, 151.3194963 -33.6196039, 151.3194093 -33.6194561, 151.3193055 -33.6192656, 151.3192266 -33.6190821, 151.3192233 -33.6189876, 151.319177 -33.6188394, 151.3191081 -33.618689, 151.3190654 -33.6185109, 151.3190079 -33.6183804, 151.3189535 -33.6181972, 151.3188795 -33.6180233, 151.3187797 -33.6178305, 151.3187469 -33.617767, 151.3186788 -33.6175932, 151.3186624 -33.6174891, 151.3186442 -33.6173107, 151.3186089 -33.6171598, 151.3185949 -33.6171001, 151.3185894 -33.6169557, 151.3185844 -33.6168259, 151.3185752 -33.616585, 151.3186591 -33.6165672, 151.3186425 -33.6165379, 151.3185207 -33.6163168, 151.3184424 -33.6161766, 151.3183774 -33.6160557, 151.3182859 -33.6158857, 151.3182199 -33.6157632, 151.3181387 -33.615636, 151.3179341 -33.6154505, 151.3177955 -33.6152807, 151.3176384 -33.6151511, 151.3175324 -33.6150659, 151.3174962 -33.6151043, 151.3173054 -33.6149451, 151.3172281 -33.6148632, 151.3172792 -33.6148167, 151.3172119 -33.6147447, 151.3170741 -33.6146201, 151.3170126 -33.6145838, 151.3168944 -33.6145456, 151.3166754 -33.6144879, 151.3165659 -33.6143601, 151.3163958 -33.6142852, 151.316307 -33.6142715, 151.3162247 -33.6143043, 151.3161611 -33.6143794, 151.3161491 -33.6144454, 151.3161341 -33.6147564, 151.3160774 -33.6149408, 151.3161924 -33.6152777, 151.3162365 -33.6155156, 151.3162445 -33.6155591, 151.3161425 -33.615578, 151.3161584 -33.6157045, 151.3161662 -33.6158401, 151.3161804 -33.6160864, 151.3161702 -33.6161774, 151.3161524 -33.6163348, 151.3161341 -33.6164977, 151.3161245 -33.6166013, 151.3161175 -33.6166451, 151.3162225 -33.6167122, 151.3162158 -33.6167687, 151.3161992 -33.6169076, 151.3161619 -33.6171767, 151.3161169 -33.6175019, 151.3160938 -33.6176687, 151.3160822 -33.6177521, 151.3162105 -33.6179728, 151.3163006 -33.6181455, 151.3163489 -33.6184205, 151.3163729 -33.6186229, 151.3163341 -33.6189527, 151.316263 -33.6191962, 151.3160198 -33.6195172, 151.3159605 -33.6196859, 151.3159557 -33.6197981, 151.3159573 -33.6200064, 151.3159749 -33.6201037, 151.315999 -33.6202187, 151.3159829 -33.6203042, 151.3160134 -33.6204404, 151.3160423 -33.6205459, 151.3160869 -33.6207909, 151.3161031 -33.620918, 151.3160764 -33.6211944, 151.3160663 -33.6214607, 151.3160679 -33.6215502, 151.3160117 -33.621723, 151.3159225 -33.6219498, 151.3159155 -33.6219676, 151.315853 -33.6221536, 151.3159174 -33.6223026, 151.3160098 -33.6224735, 151.3160221 -33.6224963, 151.3161909 -33.6227127, 151.3163164 -33.6228115, 151.3164993 -33.622843, 151.316742 -33.6230148, 151.3168882 -33.623301, 151.3169393 -33.6235399, 151.3169494 -33.6237662, 151.31694 -33.6240894, 151.3169281 -33.6241772, 151.3169227 -33.6242175, 151.3167891 -33.6245976, 151.3167582 -33.6247623, 151.3167798 -33.6248594, 151.3167107 -33.6249752, 151.3165428 -33.6252564, 151.3164848 -33.6253246, 151.3160911 -33.6254869, 151.3159255 -33.6255719, 151.31579 -33.6256271, 151.3156515 -33.6257694, 151.3155846 -33.625838, 151.3155627 -33.6260864, 151.3155725 -33.6262396, 151.3155821 -33.6263887, 151.315585 -33.626514, 151.3155866 -33.6265827, 151.3155913 -33.6267836, 151.3155965 -33.6270013, 151.3155823 -33.6270788, 151.3155674 -33.6271597, 151.3155021 -33.6275159, 151.3154435 -33.6278358, 151.3154272 -33.627925, 151.3153798 -33.6281834, 151.3153411 -33.6283945, 151.3153064 -33.628584))\nPOLYGON ((151.2864639 -33.6387861, 151.2864727 -33.6388036, 151.2864955 -33.6388488, 151.2865696 -33.638996, 151.2866446 -33.6391449, 151.2866927 -33.6392402, 151.2866921 -33.6394316, 151.2866916 -33.6395906, 151.2865776 -33.6397318, 151.2864293 -33.6399157, 151.2863754 -33.6400108, 151.2863242 -33.6401011, 151.2863126 -33.6401216, 151.2862922 -33.6403582, 151.2862881 -33.6404897, 151.2863738 -33.6405997, 151.2864835 -33.6407403, 151.2865581 -33.6408361, 151.286654 -33.6409591, 151.2866619 -33.6409692, 151.2868763 -33.641138, 151.2870098 -33.6412373, 151.2871512 -33.6413647, 151.2872669 -33.6414688, 151.2873632 -33.6415445, 151.2875729 -33.6417043, 151.2876775 -33.641781, 151.2877994 -33.6418705, 151.2880315 -33.6420407, 151.2882087 -33.6421708, 151.2882965 -33.6422352, 151.2884121 -33.6422986, 151.2885301 -33.6423633, 151.2887985 -33.6424168, 151.2889513 -33.642349, 151.2890715 -33.6422743, 151.2892977 -33.6422353, 151.2895761 -33.6422229, 151.2898153 -33.6422775, 151.290019 -33.6423948, 151.2901929 -33.6425462, 151.2903852 -33.6429276, 151.2905387 -33.6430559, 151.2906691 -33.6431649, 151.2910386 -33.6433968, 151.2912257 -33.6434682, 151.2913921 -33.6435317, 151.2916422 -33.6436273, 151.2917963 -33.6436728, 151.2919095 -33.6436481, 151.2919422 -33.6436753, 151.2919943 -33.6437185, 151.2921852 -33.6439305, 151.2923815 -33.6441486, 151.2924659 -33.6441441, 151.2926921 -33.6441321, 151.2931344 -33.6439943, 151.2932021 -33.6439511, 151.2933917 -33.6438302, 151.2935963 -33.6436998, 151.293749 -33.6435671, 151.2938392 -33.6434888, 151.2939583 -33.6433852, 151.2940541 -33.643302, 151.294173 -33.6432358, 151.2942802 -33.643176, 151.2943818 -33.6431193, 151.294604 -33.6429955, 151.2948452 -33.6428375, 151.2950286 -33.6426221, 151.2951182 -33.6423778, 151.2951231 -33.64208, 151.2950591 -33.6418672, 151.2950926 -33.6417697, 151.2951374 -33.6416393, 151.2952375 -33.6415169, 151.2953849 -33.6413388, 151.2954557 -33.6412534, 151.295601 -33.6410778, 151.2956083 -33.640943, 151.2956134 -33.6408477, 151.2956596 -33.6407595, 151.2957275 -33.6406301, 151.2956964 -33.640394, 151.2956806 -33.6402743, 151.2956806 -33.6399324, 151.2956925 -33.6397544, 151.2955884 -33.6395412, 151.295447 -33.6394534, 151.295215 -33.6393094, 151.2950651 -33.6392163, 151.2949226 -33.6391279, 151.2949072 -33.6390898, 151.2948047 -33.6388369, 151.2947596 -33.6387256, 151.2947555 -33.6387154, 151.2947012 -33.6385815, 151.2946637 -33.6384496, 151.2946263 -33.6383186, 151.2945843 -33.638171, 151.2945332 -33.6379915, 151.2945108 -33.6379131, 151.294485 -33.6378225, 151.2944484 -33.6376938, 151.2943966 -33.637512, 151.2943503 -33.6373496, 151.2943487 -33.6371117, 151.2943474 -33.6369161, 151.2943463 -33.636765, 151.294346 -33.6367205, 151.2941157 -33.6364547, 151.2940865 -33.636421, 151.2938792 -33.6363547, 151.2936982 -33.6362967, 151.2936348 -33.6362764, 151.2935332 -33.6362691, 151.2933071 -33.6362527, 151.2930235 -33.6363284, 151.2928143 -33.6365268, 151.2927256 -33.6366136, 151.2926959 -33.63668, 151.2926537 -33.6367742, 151.2926181 -33.6368223, 151.2925135 -33.6369574, 151.2923928 -33.6369685, 151.2921876 -33.6370824, 151.2920266 -33.6370824, 151.2919475 -33.637059, 151.2918764 -33.6369998, 151.2917984 -33.6368587, 151.2916351 -33.6366715, 151.2915463 -33.6366042, 151.2914403 -33.6365675, 151.2913954 -33.6365488, 151.291244 -33.6364866, 151.2911843 -33.6364621, 151.2909422 -33.6364027, 151.2909214 -33.6363976, 151.2907316 -33.6364095, 151.2905652 -33.6364198, 151.2904269 -33.6364285, 151.2903117 -33.6364357, 151.2902911 -33.636437, 151.2901756 -33.6364188, 151.2900099 -33.6363929, 151.2899345 -33.6363567, 151.289803 -33.6362937, 151.2896207 -33.6362504, 151.2894147 -33.6362015, 151.28926 -33.6361197, 151.2890522 -33.6361265, 151.2889102 -33.6362982, 151.2889014 -33.6368628, 151.2888248 -33.6369535, 151.2887141 -33.6370844, 151.2885061 -33.6372089, 151.2883935 -33.6372303, 151.2882724 -33.6372533, 151.2882514 -33.6372535, 151.2880871 -33.6372557, 151.2878969 -33.6372582, 151.2877574 -33.6372601, 151.2876061 -33.6372854, 151.2875158 -33.6373005, 151.2873014 -33.6373859, 151.2870435 -33.63756, 151.2870025 -33.6376025, 151.2868556 -33.6377552, 151.2868149 -33.6377975, 151.2866028 -33.6380766, 151.2865396 -33.6380967, 151.2865143 -33.6382522, 151.2865126 -33.6382702, 151.286469 -33.638723, 151.2864639 -33.6387861, 151.2864639 -33.6387861))\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterTest_jtsErrorInner1.wkt",
    "content": "LINESTRING (30.2802355 -2.4261657, 30.2801666 -2.4258831, 30.2801795 -2.4257551, 30.2802169 -2.4254564, 30.2802542 -2.4250272, 30.2802355 -2.4247659, 30.2801608 -2.4244486, 30.2801581 -2.4242415, 30.2802136 -2.4240468, 30.2804475 -2.4237655, 30.2806928 -2.4234798, 30.2810324 -2.4232056, 30.2812804 -2.4230534, 30.2815032 -2.4228999, 30.2815913 -2.4228645, 30.2817543 -2.4228797, 30.2818589 -2.4229627, 30.2819149 -2.423116, 30.2820278 -2.4233298, 30.2821806 -2.4234987, 30.2826527 -2.4239649, 30.2828833 -2.4244432, 30.2829182 -2.4245746, 30.282933 -2.4247434, 30.2829223 -2.4250583, 30.2828695 -2.4254762, 30.2828382 -2.4256995, 30.2827865 -2.4260517, 30.2827546 -2.4261757, 30.282607 -2.4264652, 30.2823227 -2.4269341, 30.2818185 -2.4282338, 30.2816605 -2.4285808, 30.2814849 -2.4289894, 30.2812029 -2.4294893, 30.280746 -2.4300306, 30.2806013 -2.4301885, 30.2804522 -2.4303491, 30.2802822 -2.4305285, 30.2800953 -2.4307179, 30.2798711 -2.4309185, 30.2796592 -2.4310357, 30.2794853 -2.4310785, 30.2793574 -2.4310044, 30.2792276 -2.4308686, 30.2791531 -2.4307612, 30.2790718 -2.4305575, 30.2789558 -2.4303726, 30.2788714 -2.4302493, 30.2787785 -2.430064, 30.2786226 -2.4298194, 30.2785827 -2.4296543, 30.2785914 -2.4295366, 30.2786547 -2.4294255, 30.2787381 -2.4293537, 30.2788887 -2.4292681, 30.2791912 -2.4290166, 30.2794653 -2.428787, 30.2797296 -2.4285937, 30.28007 -2.4283259, 30.2803825 -2.4279441, 30.2805291 -2.427512, 30.2805357 -2.4269216, 30.2804026 -2.4266071, 30.2803279 -2.4264387, 30.2801712 -2.4265704, 30.2801989 -2.4270919, 30.2800289 -2.4276964, 30.2796453 -2.4282416, 30.2794385 -2.4283832, 30.2791708 -2.4286761, 30.2786743 -2.4289197, 30.2783105 -2.4292586, 30.2782233 -2.4294883, 30.2782399 -2.4297711, 30.2782045 -2.42996, 30.278228 -2.4301475, 30.2782775 -2.4302456, 30.2783938 -2.4303364, 30.2785058 -2.430389, 30.2786127 -2.4304331, 30.2786878 -2.4304706, 30.2787959 -2.4305529, 30.2788335 -2.4306522, 30.2788816 -2.4311814, 30.2787887 -2.4316679, 30.2786442 -2.4317832, 30.2784046 -2.4318623, 30.2782825 -2.431976, 30.2782313 -2.4321038, 30.2782947 -2.4322349, 30.2783733 -2.4324002, 30.2785367 -2.4325431, 30.2788847 -2.4327724, 30.2791135 -2.4327359, 30.2793802 -2.4326346, 30.2795878 -2.4323966, 30.2796838 -2.4319434, 30.2797648 -2.4317484, 30.2800408 -2.4313547, 30.2809819 -2.4302446, 30.2815908 -2.4294544, 30.2822512 -2.4277991, 30.2825715 -2.4269931, 30.2830579 -2.4263136, 30.2834098 -2.4256736, 30.2834217 -2.4249308, 30.2834059 -2.4244765, 30.2831963 -2.4239905, 30.2829393 -2.4234967, 30.2827548 -2.4231307, 30.2825913 -2.4229278, 30.2822512 -2.4225248, 30.2819111 -2.42211, 30.2816185 -2.4219164, 30.2814168 -2.4218532, 30.2812429 -2.4219362, 30.2808395 -2.422426, 30.2804283 -2.4229238, 30.2801159 -2.4234572, 30.2798589 -2.4240459, 30.2798747 -2.4243738, 30.279886 -2.4245731, 30.2798668 -2.4248479, 30.2798786 -2.4251797, 30.279806 -2.4256623, 30.2796868 -2.4257836, 30.2794683 -2.4258517, 30.2791868 -2.4260764, 30.2791858 -2.4263126, 30.279362 -2.4265799, 30.279591 -2.4266133, 30.2796814 -2.4265208, 30.2797916 -2.4263774, 30.2798549 -2.4262887, 30.2799592 -2.4262506, 30.2800283 -2.4263324, 30.2800724 -2.4264835, 30.2802949 -2.426371, 30.2802355 -2.4261657)\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterTest_jtsErrorInner2.wkt",
    "content": "LINESTRING (30.2815032 -2.4228999, 30.2812804 -2.4230534, 30.2810324 -2.4232056, 30.2806928 -2.4234798, 30.2804475 -2.4237655, 30.2802136 -2.4240468, 30.2801581 -2.4242415, 30.2801608 -2.4244486, 30.2802355 -2.4247659, 30.2802542 -2.4250272, 30.2802169 -2.4254564, 30.2801795 -2.4257551, 30.2801666 -2.4258831, 30.2802355 -2.4261657, 30.2802949 -2.426371, 30.2803279 -2.4264387, 30.2804026 -2.4266071, 30.2805357 -2.4269216, 30.2805291 -2.427512, 30.2803825 -2.4279441, 30.28007 -2.4283259, 30.2797296 -2.4285937, 30.2794653 -2.428787, 30.2791912 -2.4290166, 30.2788887 -2.4292681, 30.2787381 -2.4293537, 30.2786547 -2.4294255, 30.2785914 -2.4295366, 30.2785827 -2.4296543, 30.2786226 -2.4298194, 30.2787785 -2.430064, 30.2788714 -2.4302493, 30.2789558 -2.4303726, 30.2790718 -2.4305575, 30.2791531 -2.4307612, 30.2792276 -2.4308686, 30.2793574 -2.4310044, 30.2794853 -2.4310785, 30.2796592 -2.4310357, 30.2798711 -2.4309185, 30.2800953 -2.4307179, 30.2802822 -2.4305285, 30.2804522 -2.4303491, 30.2806013 -2.4301885, 30.280746 -2.4300306, 30.2812029 -2.4294893, 30.2814849 -2.4289894, 30.2816605 -2.4285808, 30.2818185 -2.4282338, 30.2823227 -2.4269341, 30.282607 -2.4264652, 30.2827546 -2.4261757, 30.2827865 -2.4260517, 30.2828382 -2.4256995, 30.2828695 -2.4254762, 30.2829223 -2.4250583, 30.282933 -2.4247434, 30.2829182 -2.4245746, 30.2828833 -2.4244432, 30.2826527 -2.4239649, 30.2821806 -2.4234987, 30.2820278 -2.4233298, 30.2819149 -2.423116, 30.2818589 -2.4229627, 30.2817543 -2.4228797, 30.2815913 -2.4228645, 30.2815032 -2.4228999)\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterTest_jtsErrorOuter.wkt",
    "content": "LINESTRING (30.3584049 -2.402721, 30.3583034 -2.4028764, 30.3582019 -2.4032543, 30.3581724 -2.4034178, 30.3580613 -2.4035496, 30.357948 -2.403697, 30.3578747 -2.4037823, 30.3578452 -2.4038626, 30.3578263 -2.4040288, 30.3578424 -2.4043665, 30.3579202 -2.405283, 30.357939 -2.405894, 30.3579551 -2.4067569, 30.3579604 -2.4074456, 30.3578102 -2.408748, 30.3577164 -2.4093135, 30.3575608 -2.4097904, 30.357424 -2.4102621, 30.3572228 -2.4106748, 30.35707 -2.4109374, 30.3569063 -2.4112323, 30.3563189 -2.4119584, 30.3560078 -2.4121755, 30.3556994 -2.4122773, 30.3554848 -2.4123229, 30.3553185 -2.41228, 30.3551822 -2.4122019, 30.3550008 -2.412059, 30.3548967 -2.4119609, 30.3548286 -2.4118804, 30.3547364 -2.4117146, 30.3546849 -2.4113358, 30.3546339 -2.4110621, 30.3545872 -2.4109497, 30.3545322 -2.4109178, 30.3544575 -2.4109187, 30.3543569 -2.4109843, 30.3542724 -2.4110875, 30.3542058 -2.4112374, 30.3541731 -2.4113274, 30.3541538 -2.4114513, 30.3541437 -2.4118352, 30.3542088 -2.4124175, 30.3543911 -2.412962, 30.3544854 -2.4130667, 30.3546701 -2.4132914, 30.3547508 -2.4133985, 30.3548226 -2.4134427, 30.354951 -2.4135074, 30.3551146 -2.4134913, 30.3553426 -2.41336, 30.3554794 -2.4131108, 30.3556001 -2.4128589, 30.3557315 -2.4126981, 30.3558764 -2.412623, 30.3560722 -2.412607, 30.3563592 -2.412674, 30.356448 -2.4127138, 30.3566495 -2.4128364, 30.3568661 -2.4130036, 30.3571504 -2.4133439, 30.3573784 -2.4137657, 30.3575481 -2.4141462, 30.3576332 -2.4144775, 30.3576896 -2.4151045, 30.3577026 -2.4155067, 30.3577915 -2.4159782, 30.3578954 -2.4166707, 30.3580022 -2.4171723, 30.3581026 -2.4180121, 30.3581804 -2.4186258, 30.3582769 -2.4190948, 30.3582931 -2.4194781, 30.3582608 -2.4196576, 30.3582448 -2.419754, 30.3582126 -2.4199899, 30.3578881 -2.4219622, 30.3575528 -2.4225598, 30.3573328 -2.4226831, 30.3570044 -2.4227007, 30.3567693 -2.4226628, 30.3563305 -2.4223857, 30.3558562 -2.42201, 30.3555069 -2.4217081, 30.3552648 -2.4215428, 30.354939 -2.4213914, 30.3545581 -2.4212789, 30.3543073 -2.4212521, 30.3539624 -2.4212675, 30.3535502 -2.4212889, 30.3531823 -2.4212534, 30.3529863 -2.421172, 30.3522058 -2.4207983, 30.3513927 -2.4204408, 30.3511852 -2.4203677, 30.3509626 -2.4202981, 30.3506649 -2.4202471, 30.3502396 -2.4202293, 30.3497818 -2.4202785, 30.3490439 -2.420385, 30.3487055 -2.4205107, 30.3483337 -2.4207243, 30.3479934 -2.4210037, 30.3475854 -2.4213816, 30.346958 -2.4221713, 30.3465236 -2.4227286, 30.3458605 -2.423299, 30.3452737 -2.4236123, 30.3440773 -2.4242615, 30.3434711 -2.424492, 30.3421274 -2.4250842, 30.342012 -2.425178, 30.3415829 -2.4254942, 30.341031 -2.4259562, 30.3407889 -2.4261883, 30.3405154 -2.4265045, 30.3401827 -2.4270673, 30.3399467 -2.4275041, 30.3398261 -2.4279033, 30.3398134 -2.4283063, 30.3397938 -2.4287448, 30.3395686 -2.4292915, 30.3393137 -2.4296103, 30.3390134 -2.4298355, 30.3387424 -2.4300311, 30.3384018 -2.4301705, 30.3381523 -2.4303205, 30.3377983 -2.4305778, 30.3374389 -2.430894, 30.3370661 -2.4312289, 30.336079 -2.4317034, 30.3353977 -2.4318534, 30.3346211 -2.4318899, 30.3338734 -2.4319011, 30.3331338 -2.4317797, 30.3328581 -2.4316632, 30.332686 -2.431505, 30.3325385 -2.4313416, 30.3324714 -2.4312343, 30.33245 -2.4310896, 30.3325009 -2.4307788, 30.3325117 -2.4305751, 30.3324446 -2.4302991, 30.3322998 -2.4301437, 30.332112 -2.4300579, 30.3318572 -2.4300713, 30.3314629 -2.4301276, 30.3311598 -2.4302214, 30.3310499 -2.4302723, 30.3308149 -2.4304137, 30.3306937 -2.4305129, 30.3305044 -2.4306778, 30.3302887 -2.4309305, 30.33022 -2.4311062, 30.3301367 -2.4313176, 30.3301015 -2.4316053, 30.3300866 -2.4318756, 30.3301088 -2.4320713, 30.3301734 -2.4322585, 30.3303462 -2.432608, 30.3305912 -2.4330646, 30.3307522 -2.433188, 30.3308755 -2.4332817, 30.3309882 -2.4334103, 30.331133 -2.4336247, 30.3312135 -2.433823, 30.3313154 -2.4341071, 30.3313315 -2.434493, 30.3312403 -2.4351844, 30.3310311 -2.4359401, 30.3308916 -2.4364921, 30.3306502 -2.4368619, 30.3305322 -2.4369905, 30.3303552 -2.4372156, 30.3301734 -2.4374423, 30.3300951 -2.4376788, 30.329985 -2.4379285, 30.3297597 -2.4383626, 30.3294861 -2.438727, 30.329083 -2.4389863, 30.3286127 -2.4392309, 30.328003 -2.4394801, 30.3273233 -2.4397069, 30.326761 -2.4398418, 30.3262997 -2.4399383, 30.3257109 -2.4399913, 30.3251679 -2.4400392, 30.3245294 -2.4400562, 30.3240062 -2.4401174, 30.3235906 -2.4402009, 30.3232607 -2.4402378, 30.3228396 -2.4402063, 30.3222388 -2.4400562, 30.3217989 -2.4399437, 30.3216165 -2.4399115, 30.3213698 -2.4399705, 30.3211659 -2.4400723, 30.3209943 -2.4402652, 30.320785 -2.4408441, 30.3206027 -2.4411442, 30.3203827 -2.4413103, 30.3201714 -2.4414102, 30.3198248 -2.4414818, 30.3194418 -2.4415185, 30.3193048 -2.4414516, 30.3189903 -2.4412569, 30.3186001 -2.4410904, 30.3183696 -2.4411045, 30.3180449 -2.4412633, 30.317666 -2.441478, 30.3171937 -2.4417665, 30.3166787 -2.4422737, 30.3162897 -2.4429289, 30.316108 -2.4434016, 30.3159302 -2.444033, 30.3157318 -2.4445314, 30.315544 -2.4450727, 30.3152597 -2.4456355, 30.3148378 -2.4463438, 30.3144121 -2.4466484, 30.3138168 -2.4469603, 30.3132625 -2.4471345, 30.3127279 -2.4472789, 30.3120451 -2.4473854, 30.3112398 -2.4475766, 30.3105916 -2.4475267, 30.3096592 -2.4473666, 30.3085327 -2.4472487, 30.3069019 -2.4467663, 30.3060597 -2.446418, 30.3055836 -2.4461684, 30.3047861 -2.445754, 30.3043687 -2.4454595, 30.3039858 -2.444918, 30.3037624 -2.4443713, 30.3036435 -2.4438353, 30.3035725 -2.4433043, 30.3036019 -2.4430583, 30.3037468 -2.4426945, 30.3038715 -2.4422818, 30.3038675 -2.4419661, 30.3038156 -2.441861, 30.303747 -2.441817, 30.3036672 -2.4418115, 30.3035116 -2.4418945, 30.3033954 -2.4420321, 30.3031925 -2.4422617, 30.3030235 -2.4423447, 30.3024602 -2.4426288, 30.3019747 -2.4429423, 30.3010789 -2.4433496, 30.3008494 -2.4434675, 30.3006171 -2.4436276, 30.2999738 -2.4443867, 30.2996331 -2.4448905, 30.2994851 -2.4451509, 30.2994427 -2.4452268, 30.29944 -2.445271, 30.2994508 -2.4453153, 30.2994615 -2.4453555, 30.2994695 -2.445401, 30.2994722 -2.445468, 30.2994333 -2.4456703, 30.2992938 -2.44602, 30.2989948 -2.4466431, 30.2987896 -2.4471616, 30.2986321 -2.4476068, 30.2982786 -2.4486931, 30.2981011 -2.4492529, 30.2980345 -2.449564, 30.2980238 -2.4497998, 30.2980322 -2.4500956, 30.2979809 -2.4507994, 30.2979004 -2.4514264, 30.2979139 -2.4517714, 30.2979566 -2.4520825, 30.2979758 -2.4522543, 30.2980083 -2.4524745, 30.2980485 -2.4528723, 30.298056 -2.4532192, 30.2980614 -2.4536158, 30.2981257 -2.4539588, 30.2981794 -2.4545484, 30.2981472 -2.4549074, 30.2980828 -2.4552933, 30.2978307 -2.4558078, 30.2976185 -2.4562342, 30.2974458 -2.4565864, 30.2970408 -2.4571073, 30.2964025 -2.457561, 30.2951163 -2.4579356, 30.293317 -2.4582689, 30.2923703 -2.45821, 30.291784 -2.4580319, 30.2914578 -2.4578016, 30.2913064 -2.4575619, 30.2911592 -2.4571233, 30.2910565 -2.4565601, 30.2909156 -2.4559106, 30.2907954 -2.4555926, 30.2907074 -2.4553454, 30.2905313 -2.4549662, 30.2901626 -2.4545561, 30.2899055 -2.4544051, 30.2896845 -2.4543859, 30.2894724 -2.4543898, 30.289187 -2.4544449, 30.2888756 -2.4544371, 30.2882031 -2.4544769, 30.2876625 -2.4545931, 30.287295 -2.4547145, 30.2871501 -2.4548512, 30.2870724 -2.4549664, 30.2870428 -2.455087, 30.2869999 -2.4552585, 30.2870455 -2.4555693, 30.2870938 -2.4557033, 30.2871208 -2.455851, 30.2872259 -2.4560415, 30.2873058 -2.4562406, 30.2873522 -2.4563646, 30.2874331 -2.4564871, 30.2876785 -2.4567471, 30.2880125 -2.4572107, 30.2882606 -2.4575845, 30.2884349 -2.4579624, 30.2885663 -2.4583536, 30.2886173 -2.4586109, 30.2886307 -2.4588735, 30.2885751 -2.4591244, 30.2884725 -2.4595246, 30.2883303 -2.4598838, 30.2880702 -2.4602213, 30.28781 -2.4605912, 30.2875552 -2.4610842, 30.2874371 -2.461647, 30.2872682 -2.4622928, 30.2871072 -2.4629681, 30.2868095 -2.4637077, 30.2865761 -2.4642115, 30.2862623 -2.4647153, 30.2860665 -2.4649725, 30.2856615 -2.4654629, 30.285219 -2.4660337, 30.2848864 -2.4665643, 30.2842989 -2.4671713, 30.2841367 -2.4672744, 30.2838229 -2.467387, 30.2835077 -2.4674727, 30.2828318 -2.4675772, 30.2825207 -2.467588, 30.282113 -2.4675183, 30.2817079 -2.4674486, 30.2807397 -2.4673709, 30.2798384 -2.4672851, 30.2789506 -2.4672021, 30.2782183 -2.4672851, 30.2775961 -2.4674272, 30.2769363 -2.4675933, 30.2763703 -2.467663, 30.2756676 -2.4676228, 30.2752143 -2.4675585, 30.2747288 -2.4674244, 30.2743667 -2.467261, 30.2739027 -2.4668939, 30.2736425 -2.4666259, 30.2733985 -2.4663392, 30.2730417 -2.4658461, 30.2728674 -2.4652941, 30.2727923 -2.4647662, 30.2727225 -2.4644178, 30.2725616 -2.4642678, 30.2724302 -2.4642195, 30.2722692 -2.4642008, 30.2721137 -2.464249, 30.2716765 -2.4643482, 30.271588 -2.4643696, 30.2714668 -2.4643278, 30.2712213 -2.4642569, 30.2710343 -2.4641742, 30.2708785 -2.4640395, 30.270825 -2.4638547, 30.2708169 -2.4637394, 30.2708652 -2.4634769, 30.271281 -2.460237, 30.2712568 -2.4599047, 30.27112 -2.4595108, 30.2709523 -2.4591307, 30.270853 -2.4588547, 30.270794 -2.4585144, 30.2707753 -2.4582116, 30.2707618 -2.4580347, 30.2707418 -2.4578977, 30.2706814 -2.4576676, 30.2706009 -2.4574264, 30.2705768 -2.457279, 30.2705406 -2.4568123, 30.2705071 -2.4564938, 30.2704789 -2.4558503, 30.2705219 -2.4555823, 30.2706331 -2.4551057, 30.2707779 -2.4547279, 30.270786 -2.4543527, 30.2707954 -2.4540066, 30.2707015 -2.4536288, 30.2705848 -2.4531576, 30.27055 -2.4528226, 30.2705594 -2.4524255, 30.2705473 -2.4519382, 30.27055 -2.4512764, 30.2704909 -2.4509923, 30.2701717 -2.4500517, 30.2699733 -2.449631, 30.2696836 -2.4489852, 30.2695414 -2.4486073, 30.2694878 -2.4483554, 30.2695012 -2.4480366, 30.2696058 -2.4475864, 30.2697533 -2.4469593, 30.2697426 -2.4465841, 30.2698231 -2.4463269, 30.270043 -2.445941, 30.2708477 -2.4450755, 30.2713492 -2.4447243, 30.2717382 -2.4444269, 30.2720024 -2.444263, 30.2721231 -2.4441398, 30.2722183 -2.4439633, 30.2724919 -2.4430951, 30.2726367 -2.4427038, 30.2727064 -2.4424894, 30.2728486 -2.4423394, 30.2730417 -2.4420982, 30.2733502 -2.4416266, 30.2735004 -2.4413479, 30.2735326 -2.4412165, 30.2738142 -2.4408494, 30.2739161 -2.4405171, 30.2739402 -2.4401259, 30.2738517 -2.4397695, 30.2736493 -2.4394448, 30.2732684 -2.4388392, 30.272819 -2.4383465, 30.272626 -2.437899, 30.2725187 -2.4377248, 30.2723041 -2.4374809, 30.2721861 -2.4374407, 30.2719983 -2.4373979, 30.2718428 -2.4374086, 30.2716792 -2.4374542, 30.2714713 -2.4375154, 30.2712059 -2.4375528, 30.2710395 -2.4375689, 30.2707913 -2.4376364, 30.2704862 -2.4377628, 30.2695962 -2.4369456, 30.2691658 -2.4364718, 30.2686695 -2.4360182, 30.2682905 -2.4355248, 30.2680287 -2.4351669, 30.2677853 -2.434785, 30.2676792 -2.4344635, 30.2676308 -2.434231, 30.2676151 -2.4340407, 30.2676839 -2.4332663, 30.2678561 -2.432394, 30.2678681 -2.4317651, 30.2678543 -2.4309074, 30.2676451 -2.4301088, 30.2674658 -2.4298035, 30.2672136 -2.4294176, 30.2668542 -2.4290854, 30.2664894 -2.428753, 30.2661407 -2.4283243, 30.2655721 -2.4276758, 30.2651805 -2.4271827, 30.2649177 -2.4268825, 30.2645529 -2.4264109, 30.2643168 -2.4259821, 30.2642632 -2.4255641, 30.2642203 -2.4254408, 30.2640915 -2.4252961, 30.2639681 -2.425221, 30.2638555 -2.4251246, 30.2637268 -2.4250173, 30.2635336 -2.4248727, 30.2633405 -2.4248352, 30.2629834 -2.4249207, 30.2626938 -2.4249047, 30.2623397 -2.4248135, 30.2620423 -2.4246958, 30.2616668 -2.4243314, 30.2609265 -2.4234202, 30.2602077 -2.4223698, 30.2595854 -2.421255, 30.2592046 -2.4204939, 30.2590115 -2.4200758, 30.2589256 -2.4198132, 30.2588612 -2.4194648, 30.2588636 -2.419261, 30.2588719 -2.419095, 30.2589226 -2.4189072, 30.2590812 -2.4185966, 30.2593386 -2.4181946, 30.2595371 -2.417873, 30.259738 -2.4174976, 30.2598215 -2.4173693, 30.2598828 -2.4170635, 30.2600307 -2.4168493, 30.2602077 -2.4166081, 30.2604085 -2.4162917, 30.2606208 -2.4157345, 30.2608246 -2.4149574, 30.2608246 -2.4147966, 30.2608085 -2.4146197, 30.2607924 -2.4145071, 30.2607549 -2.4143089, 30.2607441 -2.4141694, 30.2606959 -2.4140034, 30.2606637 -2.413864, 30.2606261 -2.4137247, 30.2605886 -2.4135906, 30.2605725 -2.4134138, 30.260658 -2.4132796, 30.260779 -2.4132021, 30.2609856 -2.4131002, 30.2611867 -2.4130091, 30.2612725 -2.4129367, 30.2613289 -2.4128349, 30.2614039 -2.4125134, 30.2615219 -2.4122508, 30.2617312 -2.4120417, 30.2622837 -2.4116451, 30.262949 -2.4111842, 30.2635766 -2.4107982, 30.2641398 -2.4105517, 30.2645767 -2.4104711, 30.2649093 -2.4103693, 30.2651882 -2.4101549, 30.265274 -2.4097797, 30.2652794 -2.4094957, 30.2651399 -2.4084291, 30.2650756 -2.4073625, 30.2651346 -2.4067033, 30.2652526 -2.4061084, 30.2654779 -2.4046344, 30.265658 -2.4041683, 30.2657729 -2.403927, 30.2662802 -2.4032465, 30.2674604 -2.4020673, 30.2679116 -2.4015939, 30.2680805 -2.4013339, 30.2682298 -2.4010944, 30.2683273 -2.4009775, 30.2684632 -2.4008612, 30.2687314 -2.4007647, 30.2689192 -2.4005557, 30.269013 -2.4002984, 30.2690157 -2.4001109, 30.2689862 -2.4000492, 30.2689684 -2.3999779, 30.2688932 -2.3998975, 30.268802 -2.3998412, 30.268718 -2.3997705, 30.26871 -2.3996767, 30.2687904 -2.3993659, 30.2689755 -2.3990898, 30.2693108 -2.3987897, 30.2696326 -2.3985351, 30.270094 -2.3983314, 30.2706599 -2.3980849, 30.2712473 -2.3978249, 30.2721861 -2.397506, 30.2725106 -2.3973801, 30.2728513 -2.3970773, 30.2730551 -2.3968816, 30.2731177 -2.3968023, 30.2732867 -2.3966415, 30.2733913 -2.3965825, 30.2735361 -2.396545, 30.2738196 -2.3964689, 30.2743962 -2.3960937, 30.27493 -2.3955122, 30.2752009 -2.3951049, 30.2753842 -2.3948514, 30.2754217 -2.3947683, 30.2754405 -2.3946671, 30.2754354 -2.3945992, 30.2753737 -2.3945609, 30.2752584 -2.3946194, 30.275175 -2.3946156, 30.2750672 -2.3945952, 30.2749782 -2.3945354, 30.2749209 -2.3944819, 30.2748419 -2.394401, 30.2748331 -2.3943015, 30.2748737 -2.3940838, 30.274998 -2.3937829, 30.2750604 -2.3936645, 30.2751905 -2.3935666, 30.2753428 -2.3935086, 30.2754425 -2.393486, 30.275504 -2.3934341, 30.2755299 -2.3933513, 30.2755201 -2.3933013, 30.2754421 -2.3932603, 30.2753623 -2.3932375, 30.2752799 -2.3932317, 30.2751877 -2.3932153, 30.2750988 -2.3932174, 30.2750107 -2.3932552, 30.2749318 -2.3933002, 30.2748685 -2.3933081, 30.2747951 -2.3932777, 30.2747327 -2.393227, 30.274671 -2.3931869, 30.2745937 -2.3931279, 30.2744508 -2.3931014, 30.2742899 -2.3931094, 30.2740556 -2.3930923, 30.2739358 -2.3930988, 30.2737525 -2.3930977, 30.2734655 -2.393162, 30.2731249 -2.3932129, 30.2727789 -2.3931915, 30.272457 -2.3930441, 30.2720305 -2.3927412, 30.2718937 -2.3927225, 30.2718008 -2.3927396, 30.2716398 -2.3927825, 30.271436 -2.3928817, 30.270972 -2.3931603, 30.2705669 -2.3934149, 30.2702343 -2.3936213, 30.2696121 -2.3940635, 30.2690533 -2.3944429, 30.2686349 -2.3947645, 30.2682567 -2.395121, 30.2680323 -2.3953606, 30.2678659 -2.3956312, 30.2677399 -2.3958938, 30.2676505 -2.3960777, 30.2675602 -2.3962557, 30.2674877 -2.3964379, 30.2674073 -2.3966388, 30.2673188 -2.3969229, 30.2672186 -2.3971496, 30.2671123 -2.3973543, 30.2670023 -2.3975688, 30.2668736 -2.3978287, 30.2667797 -2.3980431, 30.266667 -2.3983084, 30.2665678 -2.398504, 30.2665007 -2.398646, 30.2664498 -2.3987721, 30.2664059 -2.3989666, 30.2663523 -2.3991515, 30.2663157 -2.3993053, 30.2662727 -2.3994714, 30.2662209 -2.3996151, 30.2661766 -2.4000894, 30.2661458 -2.4003011, 30.2661404 -2.4004056, 30.2661391 -2.4004767, 30.2661163 -2.4005557, 30.266068 -2.4006066, 30.2659661 -2.4006388, 30.2658865 -2.400656, 30.265798 -2.4006747, 30.2657095 -2.4007149, 30.2656576 -2.4007889, 30.2656505 -2.4008784, 30.2656237 -2.4009963, 30.2655718 -2.4010662, 30.2654538 -2.4011882, 30.2653371 -2.4012619, 30.2652512 -2.4013289, 30.2652124 -2.4013865, 30.2651735 -2.4015057, 30.2651252 -2.4016504, 30.2650528 -2.4018514, 30.264861 -2.4020872, 30.2646813 -2.4022507, 30.2645928 -2.4023244, 30.2643715 -2.4024892, 30.2642682 -2.4025254, 30.2641274 -2.4025924, 30.2639423 -2.402713, 30.2635896 -2.4030801, 30.2632195 -2.4034057, 30.2628024 -2.4037354, 30.2626911 -2.4037916, 30.2624175 -2.404073, 30.2623048 -2.4042834, 30.2620836 -2.4046411, 30.2619401 -2.4048582, 30.261865 -2.4050257, 30.2617456 -2.4052562, 30.2615672 -2.4056501, 30.2613325 -2.4059596, 30.2609986 -2.4063509, 30.2607398 -2.4066564, 30.2601027 -2.4072433, 30.2600035 -2.4073384, 30.2599552 -2.4074041, 30.2599002 -2.4075113, 30.2597406 -2.4079508, 30.2595864 -2.4084251, 30.2595435 -2.408606, 30.2594724 -2.4087828, 30.2593651 -2.4089356, 30.2590084 -2.4094072, 30.2584639 -2.410037, 30.2576941 -2.4109696, 30.2571255 -2.4115377, 30.2563906 -2.4122023, 30.2558836 -2.4126364, 30.2552935 -2.4130197, 30.2548948 -2.4132673, 30.2544228 -2.413688, 30.2539578 -2.4143087, 30.2537763 -2.4145697, 30.2535742 -2.414925, 30.2532765 -2.4156003, 30.2527428 -2.4175673, 30.2525977 -2.4180302, 30.252502 -2.4183771, 30.2519327 -2.4182319, 30.2514338 -2.418039, 30.2511589 -2.4179117, 30.2509833 -2.4176397, 30.2504905 -2.4167751, 30.2512095 -2.4159096, 30.2514564 -2.4154626, 30.2516922 -2.414977, 30.2523619 -2.4139442, 30.2532881 -2.4125008, 30.2539372 -2.4113164, 30.2547312 -2.4095316, 30.2559582 -2.4063964, 30.2562243 -2.4050364, 30.2564141 -2.4036992, 30.2565536 -2.4028831, 30.2568573 -2.4012846, 30.2572739 -2.3994982, 30.2574579 -2.398584, 30.2576946 -2.3971165, 30.2578187 -2.3960623, 30.257949 -2.3950058, 30.2580202 -2.3945571, 30.2581344 -2.3936139, 30.2582413 -2.3929074, 30.258649 -2.3921356, 30.2588046 -2.3916532, 30.2586329 -2.3909832, 30.2584237 -2.3907742, 30.2582681 -2.3905116, 30.2581501 -2.3901739, 30.2580965 -2.3896004, 30.2582037 -2.3891609, 30.2585524 -2.3886732, 30.2589866 -2.3882211, 30.2593598 -2.3879684, 30.2597339 -2.3877995, 30.2602831 -2.387589, 30.2608563 -2.3874068, 30.2610307 -2.3872958, 30.2612693 -2.3871396, 30.2617496 -2.3868401, 30.2622356 -2.3864789, 30.2632172 -2.3855869, 30.264113 -2.3847899, 30.2645792 -2.3844756, 30.2651399 -2.3841067, 30.2654153 -2.3839117, 30.2656778 -2.3836005, 30.2659302 -2.3832529, 30.2660358 -2.3831097, 30.2663979 -2.3828069, 30.2665615 -2.3826944, 30.2670604 -2.3825014, 30.2677122 -2.3822254, 30.2680602 -2.3821224, 30.2685812 -2.3820029, 30.2690318 -2.3818502, 30.2695307 -2.3816197, 30.2700269 -2.3815929, 30.2703407 -2.3815393, 30.2705848 -2.3814241, 30.2711803 -2.3808345, 30.2715119 -2.3805273, 30.2717391 -2.3803481, 30.2720225 -2.3800868, 30.2723685 -2.3798296, 30.2727118 -2.3796634, 30.2729157 -2.3796125, 30.2733421 -2.3795508, 30.2734903 -2.3795441, 30.2735654 -2.3795468, 30.2736211 -2.3795254, 30.2736479 -2.379504, 30.2736821 -2.3794711, 30.2736982 -2.379441, 30.2736895 -2.3793978, 30.2736472 -2.3793264, 30.2735403 -2.3792475, 30.2733553 -2.379161, 30.2731356 -2.3791783, 30.2727252 -2.3791569, 30.2723524 -2.3789881, 30.2717489 -2.3784494, 30.2712446 -2.3779375, 30.2704212 -2.3770773, 30.2700779 -2.3766994, 30.2698687 -2.3765011, 30.2696139 -2.3763242, 30.2688012 -2.3758553, 30.2681038 -2.3754694, 30.2677149 -2.3752576, 30.2670148 -2.3750701, 30.2665749 -2.3749334, 30.2662826 -2.3748664, 30.2658078 -2.3748717, 30.2652553 -2.3748825, 30.2642789 -2.3749227, 30.2634662 -2.3749682, 30.2627099 -2.3749548, 30.2611139 -2.3750861, 30.260792 -2.3750897, 30.2600947 -2.3750406, 30.2591981 -2.3749825, 30.2578575 -2.3749221, 30.256927 -2.3749843, 30.2554598 -2.3749334, 30.2545199 -2.3748233, 30.2540264 -2.3746901, 30.2533543 -2.3744858, 30.2529386 -2.3743974, 30.2522868 -2.3741267, 30.2519466 -2.3739631, 30.251752 -2.3738518, 30.2509824 -2.3734827, 30.250648 -2.3733281, 30.2505287 -2.3732217, 30.2503455 -2.3729767, 30.2500954 -2.3726313, 30.249787 -2.372149, 30.2496502 -2.3718166, 30.2496073 -2.3715128, 30.2495885 -2.3711574, 30.2493578 -2.370399, 30.2492425 -2.3696057, 30.2492318 -2.3690644, 30.2492344 -2.3684641, 30.2491727 -2.3679361, 30.2491486 -2.3677171, 30.2490856 -2.3672809, 30.2490869 -2.3671, 30.2491829 -2.3669138, 30.2494248 -2.3668611, 30.2495563 -2.3668588, 30.2497573 -2.3669052, 30.2499855 -2.3669982, 30.2502912 -2.3671456, 30.2506584 -2.3673186, 30.2510396 -2.3675395, 30.2516726 -2.367979, 30.25226 -2.3685096, 30.2527884 -2.3690027, 30.2532034 -2.3693885, 30.2540712 -2.3701422, 30.254222 -2.3702556, 30.2545787 -2.3705155, 30.2551566 -2.3708017, 30.255602 -2.3709671, 30.2558595 -2.3710368, 30.2561551 -2.3710998, 30.2565595 -2.3711775, 30.2574123 -2.3712478, 30.2582949 -2.3713209, 30.2595153 -2.3713637, 30.2601608 -2.3713508, 30.2610067 -2.371286, 30.2626482 -2.3714977, 30.2635555 -2.3714881, 30.2678597 -2.3710823, 30.2692723 -2.3708523, 30.2705748 -2.3707189, 30.2709335 -2.3705517, 30.2717489 -2.3699246, 30.272634 -2.3693136, 30.2735299 -2.3689116, 30.2743131 -2.3686865, 30.2750158 -2.3686919, 30.2755254 -2.3689116, 30.2761906 -2.3692171, 30.2771294 -2.3693672, 30.2780092 -2.3694047, 30.2787119 -2.3697209, 30.2792752 -2.3699997, 30.2799189 -2.3701605, 30.2805734 -2.3702087, 30.2811427 -2.3701186, 30.2814746 -2.3699836, 30.2819788 -2.3697424, 30.2835506 -2.3694369, 30.2846235 -2.3691957, 30.2854013 -2.3690349, 30.285809 -2.3690188, 30.2865761 -2.3689867, 30.2870858 -2.368917, 30.2876543 -2.3687749, 30.287869 -2.3686463, 30.287987 -2.3684962, 30.2880809 -2.3683462, 30.2881398 -2.3681371, 30.2881587 -2.3679013, 30.2881854 -2.367703, 30.2882445 -2.367159, 30.288341 -2.3668641, 30.2894032 -2.3652133, 30.2899208 -2.3643316, 30.2901086 -2.3641333, 30.2908945 -2.3635786, 30.2917635 -2.3629032, 30.2926218 -2.3622815, 30.2932763 -2.3619867, 30.2938771 -2.3616919, 30.2943546 -2.3612953, 30.2946925 -2.3607968, 30.2949715 -2.3603627, 30.2951699 -2.3597623, 30.2952021 -2.3588083, 30.2950412 -2.3580311, 30.294714 -2.357463, 30.2941722 -2.3567555, 30.2939315 -2.3562473, 30.293684 -2.3557692, 30.2934909 -2.3551046, 30.2933997 -2.3546383, 30.2933997 -2.3540702, 30.2935982 -2.3534109, 30.2939522 -2.3529178, 30.2942102 -2.3525935, 30.2947093 -2.3521576, 30.2951946 -2.3518043, 30.2956954 -2.3514551, 30.2965003 -2.351074, 30.2972943 -2.3506559, 30.298174 -2.3500556, 30.2989519 -2.3492409, 30.2990216 -2.3490051, 30.2990323 -2.3484208, 30.2991182 -2.3479652, 30.2996653 -2.3473381, 30.2999818 -2.3471773, 30.3005451 -2.3472041, 30.300985 -2.3471023, 30.3012156 -2.346904, 30.3014624 -2.3461804, 30.301685 -2.3443687, 30.3015482 -2.3434897, 30.3009716 -2.3427286, 30.3008026 -2.3425249, 30.3006202 -2.3423614, 30.3001696 -2.3421765, 30.2998665 -2.3421524, 30.2993407 -2.3421631, 30.2989411 -2.3423025, 30.2984127 -2.3426241, 30.2979621 -2.3428465, 30.2976081 -2.3428813, 30.2970797 -2.3428706, 30.2966988 -2.3427795, 30.2960739 -2.3424981, 30.2956071 -2.3421202, 30.2950975 -2.3416914, 30.2945182 -2.3414208, 30.2940434 -2.3412305, 30.2937671 -2.3410831, 30.2937966 -2.3409544, 30.2938771 -2.3408097, 30.2940273 -2.3407025, 30.2948508 -2.3402737, 30.2951646 -2.3400406, 30.2952397 -2.3399092, 30.2953201 -2.3397377, 30.2953734 -2.3396074, 30.295426 -2.3394362, 30.2954551 -2.3392902, 30.2955118 -2.3391211, 30.2955422 -2.338945, 30.2955803 -2.3386953, 30.29565 -2.3384781, 30.295768 -2.3382343, 30.2958539 -2.3381217, 30.2959344 -2.338052, 30.2960632 -2.3379529, 30.2962133 -2.3378644, 30.2963421 -2.3377921, 30.2964252 -2.3377117, 30.2965594 -2.3376098, 30.296672 -2.3374999, 30.2968222 -2.3374115, 30.2970153 -2.3372347, 30.2972057 -2.3370551, 30.2973291 -2.3369479, 30.2975463 -2.33683, 30.2977878 -2.336637, 30.298056 -2.3364495, 30.2982706 -2.3362833, 30.2984664 -2.3361439, 30.2985576 -2.3360581, 30.2986085 -2.3359589, 30.2986836 -2.335841, 30.298747 -2.3357174, 30.2987775 -2.3356293, 30.2987727 -2.3355116, 30.2987791 -2.3354418, 30.2987829 -2.3352863, 30.2988142 -2.3351431, 30.2988151 -2.335029, 30.2987856 -2.3348602, 30.2987572 -2.3346711, 30.2986871 -2.3344843, 30.2986237 -2.3343655, 30.2985628 -2.3342846, 30.2984468 -2.33413, 30.2982427 -2.3338245, 30.2981906 -2.3337105, 30.2981713 -2.3336113, 30.2981536 -2.3334727, 30.2981338 -2.3333594, 30.298111 -2.33322, 30.2980724 -2.3330916, 30.2980107 -2.3329954, 30.2979459 -2.3328837, 30.2977931 -2.3325405, 30.2977352 -2.3323208, 30.2977181 -2.3320086, 30.2977636 -2.3317279, 30.2978253 -2.3314673, 30.2978253 -2.3311082, 30.2977663 -2.3308616, 30.2976453 -2.3305999, 30.2972835 -2.3301327, 30.2969617 -2.3297521, 30.2967745 -2.329572, 30.2964735 -2.3291411, 30.2961677 -2.3287713, 30.2959034 -2.3285215, 30.2955884 -2.3282835, 30.2952676 -2.3280299, 30.2950734 -2.3278815, 30.2947724 -2.3276798, 30.2943867 -2.3273401, 30.2941132 -2.327056, 30.2938932 -2.326772, 30.2937591 -2.3266004, 30.2936196 -2.3262628, 30.2935713 -2.3255445, 30.2935874 -2.32518, 30.2937162 -2.3243545, 30.2938273 -2.3239931, 30.2938985 -2.3238025, 30.2940824 -2.3235777, 30.2946067 -2.3233362, 30.2952021 -2.3229342, 30.2954274 -2.322559, 30.2956313 -2.3221892, 30.2956796 -2.3219587, 30.2958566 -2.3216692, 30.2959692 -2.321353, 30.2959049 -2.3208706, 30.2957493 -2.32049, 30.2955662 -2.3202777, 30.2950186 -2.3197905, 30.2948159 -2.3196324, 30.294714 -2.3191929, 30.2947303 -2.3189894, 30.2947864 -2.318874, 30.2951297 -2.318732, 30.2958459 -2.3185551, 30.2963081 -2.3184677, 30.2966291 -2.3183621, 30.2971734 -2.318038, 30.2977926 -2.3175918, 30.2987401 -2.3170853, 30.2997509 -2.3166215, 30.3044136 -2.3132711, 30.30495 -2.313003, 30.3056681 -2.3127233, 30.3064299 -2.3125947, 30.3074605 -2.3126065, 30.3083183 -2.3126165, 30.3094559 -2.3124722, 30.3103478 -2.3122975, 30.3109537 -2.3120055, 30.3113833 -2.3117033, 30.3119662 -2.311161, 30.312582 -2.310671, 30.3133175 -2.3104603, 30.3140369 -2.3105165, 30.3148163 -2.3107862, 30.3154624 -2.3110984, 30.3167773 -2.3117448, 30.3176629 -2.3120909, 30.3184891 -2.3124339, 30.3214502 -2.3135274, 30.3222978 -2.3138811, 30.322684 -2.3141277, 30.3232205 -2.3145029, 30.3235745 -2.3150389, 30.3239822 -2.3155963, 30.3243256 -2.3160037, 30.3254843 -2.3167005, 30.3260315 -2.3168935, 30.3265357 -2.3170221, 30.3272545 -2.3171722, 30.3296041 -2.3172258, 30.3313959 -2.3171078, 30.3328023 -2.3168544, 30.3345716 -2.3164432, 30.3375006 -2.3151247, 30.3386271 -2.314728, 30.3389436 -2.3146583, 30.3394318 -2.3146315, 30.339759 -2.3146422, 30.3400647 -2.3147119, 30.3402686 -2.3148084, 30.3404725 -2.3149317, 30.3406602 -2.3151139, 30.3410786 -2.3155588, 30.3413361 -2.3158643, 30.341717 -2.3161859, 30.3422427 -2.3165236, 30.3426343 -2.3167863, 30.3428918 -2.3169899, 30.3430688 -2.317199, 30.3433531 -2.3175849, 30.3436428 -2.3178636, 30.3439861 -2.3181263, 30.3444313 -2.3184289, 30.3447305 -2.3186143, 30.3449293 -2.3188011, 30.3450966 -2.3189624, 30.3454238 -2.3191715, 30.3455874 -2.3193296, 30.3456679 -2.3194207, 30.3457712 -2.3195898, 30.3459701 -2.3198364, 30.3460193 -2.3200559, 30.3460392 -2.3203459, 30.3459763 -2.3205972, 30.3457564 -2.3208813, 30.3454399 -2.3211761, 30.3452307 -2.3213798, 30.3451073 -2.3215942, 30.3449694 -2.3218266, 30.3448112 -2.3221329, 30.3446674 -2.3224036, 30.3445655 -2.3226233, 30.344544 -2.3228806, 30.3445333 -2.3232075, 30.3444153 -2.3238347, 30.3442544 -2.3246816, 30.3441578 -2.3251372, 30.3441055 -2.3252765, 30.3440398 -2.3255446, 30.3438359 -2.326638, 30.343793 -2.3271364, 30.3437823 -2.3274259, 30.3437796 -2.328455, 30.3437904 -2.3288007, 30.3437448 -2.3300362, 30.3437876 -2.3303793, 30.3439272 -2.3307223, 30.3441846 -2.3310975, 30.3450751 -2.3322231, 30.3456652 -2.3329306, 30.3460327 -2.3337828, 30.3463626 -2.3347637, 30.3464403 -2.3350719, 30.3467914 -2.3365911, 30.3474355 -2.3387086, 30.3487658 -2.3420693, 30.3491865 -2.3429401, 30.350182 -2.3446582, 30.3503966 -2.3450976, 30.3506434 -2.3454568, 30.3514534 -2.3463037, 30.3524029 -2.3473381, 30.3530627 -2.3482493, 30.3535831 -2.3489943, 30.3543127 -2.3497501, 30.3549296 -2.350404, 30.3551978 -2.3505916, 30.3554714 -2.3507417, 30.3558468 -2.3508971, 30.3578324 -2.3519487, 30.359021 -2.3526777, 30.3598437 -2.3530916, 30.3606963 -2.3534698, 30.3614739 -2.353621, 30.3626695 -2.3537146, 30.3647089 -2.3538665, 30.3665462 -2.3536309, 30.3716361 -2.3525585, 30.3733772 -2.3524821, 30.3750386 -2.3531255, 30.3773004 -2.3538245, 30.378209 -2.3542528, 30.3793801 -2.3547902, 30.3799005 -2.3550153, 30.3800937 -2.3553422, 30.3801477 -2.356064, 30.3797288 -2.3575679, 30.3795871 -2.3580043, 30.3795683 -2.358543, 30.379622 -2.3588324, 30.379791 -2.3590736, 30.3798552 -2.3591337, 30.3800342 -2.359381, 30.3801155 -2.3597168, 30.380318 -2.3601613, 30.3808999 -2.3607548, 30.381162 -2.3609107, 30.3814649 -2.3612426, 30.3816195 -2.3615971, 30.3817118 -2.3620141, 30.3817757 -2.3624229, 30.3818362 -2.3629793, 30.3818563 -2.3637501, 30.3818388 -2.3644575, 30.3816765 -2.365015, 30.3813386 -2.3654974, 30.381124 -2.3658592, 30.3809352 -2.3661552, 30.380719 -2.3665131, 30.3804652 -2.3669462, 30.3804272 -2.3670944, 30.380381 -2.3673573, 30.3803891 -2.3675556, 30.380424 -2.3677191, 30.3804696 -2.3678611, 30.380609 -2.3680406, 30.3808182 -2.3681612, 30.3813037 -2.3682926, 30.3818482 -2.3682926, 30.3827709 -2.3683247, 30.3832993 -2.3683756, 30.3843373 -2.3684936, 30.3850776 -2.3685498, 30.3856918 -2.3685257, 30.3862417 -2.3685552, 30.3865287 -2.3685847, 30.3867674 -2.3685579, 30.3872716 -2.3684212, 30.3876927 -2.368239, 30.388188 -2.3680589, 30.3888306 -2.3678426, 30.3895207 -2.3675991, 30.389979 -2.3674351, 30.3903571 -2.3672763, 30.3911469 -2.3668962, 30.3916066 -2.3667445, 30.3919883 -2.3666232, 30.3923476 -2.3664001, 30.3925401 -2.3661997, 30.3927263 -2.3659989, 30.3929066 -2.3658255, 30.3930545 -2.3656019, 30.3931215 -2.3654465, 30.3931886 -2.36535, 30.3933763 -2.3652616, 30.3935346 -2.3652562, 30.3936902 -2.3652964, 30.3939544 -2.3654971, 30.3943862 -2.3657588, 30.3948093 -2.3659938, 30.3952824 -2.3661178, 30.3964743 -2.3662256, 30.3978835 -2.3663498, 30.3983764 -2.36649, 30.3987181 -2.3666358, 30.3997421 -2.3667463, 30.4009397 -2.36664, 30.4027512 -2.3665824, 30.4029864 -2.3666641, 30.4033828 -2.3666641, 30.4040655 -2.3663616, 30.4043627 -2.3663228, 30.4052263 -2.3654063, 30.405736 -2.3650043, 30.4060578 -2.3647149, 30.4063368 -2.3643879, 30.4065245 -2.3642914, 30.4069644 -2.3642432, 30.4073507 -2.3642539, 30.407635 -2.3643557, 30.40793 -2.3647363, 30.4079246 -2.3649132, 30.4079246 -2.3651544, 30.407989 -2.3653098, 30.40815 -2.3655296, 30.4084128 -2.3656528, 30.4096734 -2.3659423, 30.4098558 -2.3681023, 30.4107785 -2.3705249, 30.4111433 -2.3727117, 30.4118192 -2.3749521, 30.4111111 -2.3754667, 30.4094696 -2.3757132, 30.4084718 -2.3758311, 30.4075813 -2.3758204, 30.4066479 -2.3753916, 30.4051351 -2.3747806, 30.4031001 -2.3742553, 30.4025971 -2.3742032, 30.4020587 -2.3741374, 30.4014096 -2.3738936, 30.4008248 -2.3735291, 30.3999666 -2.3730146, 30.3991431 -2.372567, 30.398089 -2.3721221, 30.3972012 -2.3717014, 30.3970134 -2.3716692, 30.3967479 -2.3716666, 30.3965601 -2.3716746, 30.3963617 -2.3717148, 30.3961552 -2.3717898, 30.3957501 -2.3720337, 30.3952002 -2.3723901, 30.3945324 -2.3728993, 30.3942239 -2.3732156, 30.39402 -2.3735023, 30.3938082 -2.3738695, 30.3936338 -2.3741294, 30.3934756 -2.3742688, 30.3927594 -2.3747297, 30.3923598 -2.3749655, 30.3919092 -2.3750727, 30.39159 -2.3751906, 30.391319 -2.375271, 30.3909516 -2.3752871, 30.3906512 -2.3752362, 30.3902623 -2.3752228, 30.3896991 -2.375137, 30.3886154 -2.3751933, 30.3882292 -2.3752523, 30.387792 -2.3753782, 30.3874084 -2.3754238, 30.3868049 -2.3753943, 30.3861076 -2.3753434, 30.3853188 -2.3751913, 30.3850637 -2.3751787, 30.3848523 -2.3751853, 30.3842756 -2.3752657, 30.3832591 -2.3754372, 30.3823471 -2.3756677, 30.3816926 -2.3760134, 30.3812018 -2.3764073, 30.3809151 -2.3767154, 30.3806829 -2.3771667, 30.3804159 -2.3777928, 30.3802417 -2.3784927, 30.3801584 -2.3789666, 30.3801134 -2.3796927, 30.3801384 -2.3803093, 30.3801813 -2.380672, 30.3803426 -2.3821083, 30.3804409 -2.3832547, 30.3806856 -2.384457, 30.3810275 -2.3856932, 30.3812192 -2.3865813, 30.3813374 -2.3868942, 30.3815317 -2.3872796, 30.3817624 -2.3876977, 30.3819179 -2.3880407, 30.3820574 -2.3888715, 30.3821593 -2.3893378, 30.3822505 -2.3896004, 30.3823632 -2.3898202, 30.3825724 -2.3900399, 30.3827454 -2.3902691, 30.3828245 -2.3904741, 30.3829278 -2.3907045, 30.3830418 -2.3913222, 30.3830659 -2.3914549, 30.3832242 -2.3918569, 30.3835744 -2.3941752, 30.3834761 -2.3942575, 30.3833854 -2.3942894, 30.3833267 -2.3943108, 30.3832121 -2.3943251, 30.3830203 -2.3943358, 30.3822049 -2.3943599, 30.3818777 -2.3943652, 30.3817114 -2.394325, 30.3815907 -2.3942286, 30.3814911 -2.3940837, 30.3813976 -2.3940008, 30.3813171 -2.3939713, 30.3811589 -2.3939499, 30.3810248 -2.3939767, 30.380888 -2.3940624, 30.3805661 -2.3943116, 30.3799492 -2.3948208, 30.379333 -2.3952855, 30.378805 -2.3954989, 30.3747082 -2.3961741, 30.3745875 -2.3963403, 30.3743005 -2.3969701, 30.3739464 -2.3975543, 30.3734207 -2.3982832, 30.373123 -2.3984359, 30.3729433 -2.3984547, 30.3727609 -2.3984279, 30.3725516 -2.3983769, 30.3723936 -2.3984469, 30.3721493 -2.3986959, 30.3718607 -2.3990225, 30.3717285 -2.399292, 30.3716451 -2.3995266, 30.3716062 -2.3999051, 30.3716048 -2.4001693, 30.3716907 -2.4005423, 30.3717926 -2.4009202, 30.3718945 -2.4011319, 30.3719804 -2.4012793, 30.372026 -2.4014025, 30.3720367 -2.4014642, 30.3719777 -2.4016866, 30.3718623 -2.4020484, 30.3715861 -2.4027103, 30.3712964 -2.403868, 30.3709665 -2.4043075, 30.3707626 -2.404538, 30.3704381 -2.4047443, 30.3701082 -2.4048944, 30.3692445 -2.4051061, 30.368987 -2.4051034, 30.3686438 -2.4051544, 30.3684076 -2.4052052, 30.3681421 -2.405283, 30.3679302 -2.4053687, 30.3678229 -2.4054465, 30.3677183 -2.4055215, 30.3676219 -2.4056394, 30.3674877 -2.40576, 30.3672704 -2.4058833, 30.3670907 -2.4059449, 30.3667581 -2.4060146, 30.3664497 -2.406036, 30.366219 -2.4060306, 30.3657442 -2.4059261, 30.3654814 -2.4058055, 30.3652882 -2.4056287, 30.3652292 -2.4054062, 30.3652213 -2.4050391, 30.3652614 -2.4048167, 30.3652798 -2.404604, 30.3652909 -2.4043959, 30.3653163 -2.4041824, 30.3652883 -2.4039645, 30.3652313 -2.4038288, 30.3650833 -2.4037117, 30.3648705 -2.4036118, 30.364666 -2.4034982, 30.3644326 -2.4033562, 30.3641564 -2.4031766, 30.3639445 -2.4030158, 30.3637004 -2.4027505, 30.3634858 -2.40237, 30.3633786 -2.4022306, 30.3631747 -2.4020618, 30.3628528 -2.4018796, 30.362134 -2.4015794, 30.3617236 -2.401424, 30.3614258 -2.4013678, 30.3612381 -2.4013676, 30.3610036 -2.4013995, 30.3606695 -2.4014884, 30.360125 -2.4016824, 30.3591241 -2.4021203, 30.3585732 -2.4025073, 30.3584049 -2.402721)\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/converters/MultiplePolyLineToPolygonsConverterTest_multiplePolyLines.txt",
    "content": "LINESTRING(151.3066784 -33.640845,151.3066167 -33.6409278,151.3065441 -33.6412396,151.3065669 -33.6416211,151.3065741 -33.6417425,151.3065779 -33.6418955,151.3065289 -33.6420612,151.3064069 -33.642226)\nLINESTRING(151.3064069 -33.642226,151.3063231 -33.6423393,151.3061454 -33.6425287,151.3060404 -33.6430769,151.3059808 -33.6433132,151.3057137 -33.6434893,151.3055298 -33.6436801,151.3052748 -33.6437907,151.3052086 -33.6441075,151.305339 -33.644371,151.3056619 -33.6446621,151.3060117 -33.6449718,151.3064384 -33.6451874,151.3068441 -33.6454211,151.3070366 -33.6455238,151.3070844 -33.6456381,151.3070835 -33.6457407,151.3069782 -33.645841,151.3065488 -33.6461092,151.3063446 -33.6462847,151.3062687 -33.6464603,151.3062594 -33.6466402,151.3063084 -33.6467683,151.306374 -33.6468996,151.3065484 -33.6471678,151.3069039 -33.6474756,151.306923 -33.6476028,151.3069812 -33.6475873,151.307061 -33.6477047,151.306982 -33.6479471,151.3067159 -33.6478836,151.306377 -33.6479158,151.3061511 -33.6480247,151.3059579 -33.6479777,151.3047621 -33.6478162,151.3044746 -33.6477698,151.3044096 -33.6477466,151.3043378 -33.647721,151.3043081 -33.6477104,151.3041962 -33.6477232,151.3039921 -33.6477467,151.3038728 -33.6477604,151.3036729 -33.6477833,151.3035247 -33.6478003,151.3032758 -33.6478289,151.3031221 -33.6478465,151.3030926 -33.6478624,151.3028059 -33.6480171,151.3026113 -33.6481221,151.3023792 -33.6482473,151.302269 -33.6483068,151.3021579 -33.6486711,151.3021258 -33.6487765,151.3020767 -33.6490497,151.3020459 -33.6492209,151.3020534 -33.6492626,151.3020731 -33.6493722,151.3020979 -33.64951,151.3021166 -33.6496137,151.3021534 -33.6498187,151.3021861 -33.6499963,151.3022319 -33.6502266,151.302263 -33.6503828,151.3022692 -33.6504761,151.3022906 -33.6507976,151.3022302 -33.6510054,151.3021068 -33.6511712,151.3020046 -33.6513175,151.3019961 -33.6514769,151.3019574 -33.6517837,151.3019571 -33.6519717,151.3026986 -33.6520273,151.3027111 -33.6519688,151.302885 -33.6519075,151.3030319 -33.6518462,151.3032822 -33.6518058,151.3033494 -33.6518063,151.303516 -33.6518077,151.3036634 -33.6518254,151.3038112 -33.6519038,151.3039071 -33.6519546,151.3039437 -33.6520266,151.3040103 -33.6521577,151.3040212 -33.6522759,151.3040098 -33.6524009,151.3039882 -33.6525006,151.3039425 -33.6527111,151.303911 -33.6528562,151.3038914 -33.6529464,151.3039915 -33.6529672,151.3039388 -33.6530958,151.3038982 -33.6532507,151.3038481 -33.6534374,151.3038045 -33.653657,151.3037818 -33.653771,151.3038466 -33.6539812,151.3040727 -33.6543293,151.3041864 -33.6545045,151.3043552 -33.6547643,151.3045648 -33.655087,151.3045771 -33.6551217,151.3046349 -33.6552835,151.3046603 -33.6553546,151.3048189 -33.6558061,151.3049082 -33.6562101,151.3048923 -33.6566351,151.3048568 -33.657061,151.3048838 -33.6571195,151.3050206 -33.6571332,151.3051852 -33.6571246,151.3054287 -33.6570124,151.305597 -33.6568343,151.305788 -33.6566219,151.3058438 -33.6565599,151.3062314 -33.6561288,151.3059952 -33.6558395,151.306255 -33.6556345,151.3063979 -33.6558568,151.3064981 -33.6557303,151.306516 -33.6555324,151.3066183 -33.6554536,151.3066373 -33.655197,151.3068687 -33.6551851,151.3071765 -33.6552764,151.307356 -33.6553778,151.3075773 -33.6555028,151.3077565 -33.6555294,151.3081498 -33.6555878,151.3081928 -33.6555942,151.3084362 -33.6555147,151.3085369 -33.655866,151.3086329 -33.6562012,151.3087442 -33.656441,151.3088847 -33.6566824,151.3086583 -33.6567767,151.3083789 -33.6568929,151.3080165 -33.6569506,151.3078852 -33.6568886,151.3076566 -33.6568997,151.3074463 -33.6570471,151.3073354 -33.6572661,151.3072939 -33.6574034,151.3070973 -33.6575534,151.3068734 -33.6577958,151.3068261 -33.657926,151.3068207 -33.6580654,151.3068316 -33.6581472,151.3069462 -33.658226,151.3071428 -33.6583851,151.307333 -33.6585343,151.3073996 -33.6585929,151.3077489 -33.6586684,151.3077962 -33.6587456,151.3077887 -33.6588404,151.3082011 -33.6589686,151.3083636 -33.6592103,151.3083064 -33.6593704,151.308246 -33.6595396,151.3079716 -33.6595427,151.3079753 -33.6597261,151.3073257 -33.6597538,151.3075779 -33.6599779,151.307721 -33.6601923,151.3078838 -33.6602593,151.3079776 -33.6604967,151.3080274 -33.6606608,151.308148 -33.6608059,151.3082399 -33.6609509,151.3084122 -33.6610927,151.3087317 -33.6613046,151.3089093 -33.6614114,151.308969 -33.6615048,151.3092043 -33.6615578,151.3095173 -33.6616284,151.3096546 -33.6616594,151.3097473 -33.6616802,151.3098684 -33.6617076,151.3099831 -33.6616768,151.3100386 -33.6617621,151.3101625 -33.6618285,151.3103292 -33.6618332,151.3104801 -33.6619233,151.3105528 -33.661966,151.3106154 -33.6619731,151.3107963 -33.661947,151.3109216 -33.6619731,151.3110174 -33.6621307,151.3110751 -33.6622093,151.3112299 -33.662298,151.311598 -33.6623017,151.3118726 -33.6623163,151.3118915 -33.6623207,151.3120389 -33.6623555,151.3121808 -33.6623889,151.3122654 -33.66245,151.3122663 -33.6625694,151.3122247 -33.6626436,151.3120754 -33.662714,151.3120648 -33.6627562,151.3120274 -33.662784,151.3119583 -33.6628051,151.3118189 -33.6628326,151.3116242 -33.6628575,151.3112931 -33.6628401,151.3109994 -33.6628201,151.3107807 -33.6628039,151.3104901 -33.6628326,151.3103388 -33.662865,151.3101414 -33.662913,151.3099402 -33.6630398,151.3097946 -33.663208,151.3097642 -33.6634232,151.3098055 -33.6637234,151.3098315 -33.6640508,151.3098457 -33.6641589,151.3098502 -33.6641996,151.3099552 -33.6641867,151.3100385 -33.6645799,151.310089 -33.6648407,151.3101474 -33.6650913,151.3101843 -33.6652512,151.3102314 -33.6653861,151.3102605 -33.6654883,151.3103606 -33.6656616,151.310437 -33.6658005,151.3105789 -33.6659527,151.3109004 -33.6661035,151.3110264 -33.6661914,151.3112423 -33.6663435,151.3112854 -33.6663678,151.3114031 -33.666434,151.311556 -33.666497,151.3116458 -33.6665259,151.3117277 -33.6665298,151.3118617 -33.6665442,151.3120303 -33.6666387,151.3120949 -33.6666636,151.3121611 -33.6666649,151.3121596 -33.6667178,151.312158 -33.6667738,151.312061 -33.6667689,151.3120004 -33.6667659,151.3118522 -33.6667147,151.3115244 -33.6666977,151.3112721 -33.6666678,151.3112471 -33.6666649,151.3107743 -33.6663921,151.3106986 -33.6663658,151.3106246 -33.666375,151.310571 -33.6664249,151.310541 -33.6665363,151.3105505 -33.6666255,151.3106876 -33.6667475,151.3105891 -33.6667724,151.3105631 -33.666779,151.3105836 -33.6668459,151.3105573 -33.6668654)\nLINESTRING(151.3105573 -33.6668654,151.3105095 -33.666901,151.3104686 -33.6669495,151.3102538 -33.6669233,151.3101308 -33.6669083,151.310114 -33.6669062,151.310073 -33.6668039,151.3101549 -33.6667383,151.3101212 -33.6667061,151.3100478 -33.666636,151.309948 -33.6665233,151.3098617 -33.6664258,151.3098051 -33.6663619,151.3097822 -33.6663217,151.3097103 -33.6661953,151.3096632 -33.6661127,151.3096397 -33.666059,151.3095541 -33.6658643,151.3095025 -33.6657467,151.3093354 -33.6655893,151.3092035 -33.6655898,151.3090139 -33.6653742,151.3089273 -33.6652916,151.3089026 -33.6650931,151.3088926 -33.6650122,151.30877 -33.6648115,151.3087035 -33.6647027,151.3086688 -33.6646004,151.3085711 -33.6645203,151.308549 -33.6643538,151.308478 -33.6642612,151.308422 -33.6641882,151.3082776 -33.6639998,151.3081909 -33.6638868,151.3081238 -33.6637994,151.3079877 -33.663622,151.3078586 -33.6634537,151.3077634 -33.6633296,151.3076908 -33.6632349,151.3076224 -33.6631457,151.3073595 -33.6628759,151.3071433 -33.6626866,151.3068115 -33.6624852,151.3056013 -33.6628924,151.3050863 -33.6632996,151.3050312 -33.6650835,151.3041574 -33.6656485,151.3028787 -33.665576,151.3023671 -33.665723)\nLINESTRING(151.3022532 -33.6610312,151.302274 -33.661023,151.302349 -33.66091,151.302387 -33.660899,151.30241 -33.660905,151.302438 -33.660894,151.302533 -33.660797,151.302565 -33.660792,151.302604 -33.660816,151.302628 -33.660816,151.302657 -33.660807,151.302748 -33.660759,151.302806 -33.660722,151.302912 -33.660629,151.302966 -33.660588,151.303041 -33.660556,151.303103 -33.660543,151.303126 -33.660533,151.303191 -33.660494,151.303236 -33.66048,151.303283 -33.660474,151.30333 -33.660473,151.303462 -33.660484,151.303528 -33.660477,151.303627 -33.660458,151.303736 -33.660442,151.303851 -33.660429,151.304074 -33.660416,151.304226 -33.660415,151.304276 -33.660412,151.304356 -33.660399,151.304375 -33.6604,151.304401 -33.66041,151.304469 -33.660475,151.304494 -33.660493,151.304516 -33.660502,151.304586 -33.66052,151.304603 -33.660531,151.304598 -33.660549,151.304457 -33.660704,151.30435 -33.660845,151.30432 -33.660894,151.304294 -33.660918,151.3042879 -33.6609517,151.3041963 -33.6610446,151.3041845 -33.661063,151.304153 -33.661096,151.304127 -33.661134,151.304089 -33.661173,151.304045 -33.661208,151.304002 -33.661235,151.303975 -33.661245,151.303887 -33.661266,151.303869 -33.661279,151.303864 -33.661301,151.303874 -33.661332,151.303927 -33.661436,151.303978 -33.661576,151.304003 -33.661663,151.304019 -33.66174,151.304001 -33.661765,151.303994 -33.661796,151.304008 -33.66182,151.304052 -33.661835,151.304088 -33.661855,151.304089 -33.661875,151.304079 -33.661904,151.304061 -33.661934,151.30404 -33.661958,151.304019 -33.661969,151.303961 -33.661957,151.303938 -33.661968,151.303932 -33.66199,151.303929 -33.66202,151.303924 -33.662111,151.303931 -33.662211,151.303921 -33.662279,151.30391 -33.662315,151.303844 -33.662471,151.303819 -33.662521,151.303791 -33.662561,151.303748 -33.662605,151.303716 -33.662662,151.303699 -33.662711,151.303688 -33.66276,151.303688 -33.662801,151.30371 -33.66283,151.303734 -33.662837,151.303756 -33.662855,151.303757 -33.662875,151.303743 -33.662893,151.303679 -33.66288,151.3036589 -33.6629551,151.3036342 -33.6629696,151.303585 -33.662988,151.303559 -33.663046,151.303542 -33.663098,151.303527 -33.663184,151.303526 -33.663229,151.303531 -33.663265,151.30356 -33.66329,151.303594 -33.663294,151.303618 -33.663303,151.303622 -33.663325,151.303616 -33.66335,151.303601 -33.663361,151.303551 -33.663357,151.303524 -33.663376,151.303496 -33.663418,151.303474 -33.663468,151.303463 -33.663514,151.303459 -33.663554,151.30345 -33.663574,151.303467 -33.663609,151.303467 -33.663628,151.303456 -33.663648,151.303441 -33.663666,151.303393 -33.663689,151.303375 -33.663708,151.303355 -33.663766,151.303328 -33.663806,151.303295 -33.663843,151.303254 -33.66388,151.303179 -33.663936,151.303162 -33.663957,151.303149 -33.663987,151.303143 -33.664021,151.303142 -33.664056,151.303146 -33.66409,151.303159 -33.664135,151.3032259 -33.6641794,151.3032329 -33.6642238,151.3032004 -33.6642871,151.303132 -33.66431,151.30312 -33.664334,151.303087 -33.664436,151.303072 -33.664498,151.303055 -33.664535,151.303025 -33.664566,151.302987 -33.664591,151.302966 -33.6646,151.302938 -33.664606,151.302875 -33.664605,151.302797 -33.664596,151.302768 -33.66459,151.302746 -33.664591,151.302741 -33.664615,151.302744 -33.664635,151.302759 -33.664667,151.302777 -33.664673,151.302855 -33.664672,151.302875 -33.664687,151.302881 -33.664708,151.30288 -33.664729,151.30287 -33.664745,151.302851 -33.66476,151.302826 -33.664774,151.302801 -33.664783,151.30278 -33.664785,151.302834 -33.664791,151.302902 -33.664805,151.302933 -33.664815,151.302952 -33.664831,151.302968 -33.664873,151.302981 -33.664922,151.30299 -33.664997,151.302991 -33.665118,151.302997 -33.665195,151.302993 -33.665245,151.302973 -33.665294,151.302942 -33.665339,151.302922 -33.665358,151.302901 -33.665373,151.302876 -33.665385,151.302793 -33.665413,151.302699 -33.665426,151.302651 -33.66544,151.302601 -33.665464,151.302579 -33.665478,151.302544 -33.665509,151.302497 -33.665583,151.302456 -33.665661,151.30243 -33.665693,151.3023671 -33.665723)\nLINESTRING(151.3022532 -33.6610312,151.3019519 -33.6609581,151.3016101 -33.6608064,151.3013467 -33.6606214,151.3011704 -33.6604271,151.3008898 -33.6601085,151.3005607 -33.6596138,151.3002855 -33.6593947,151.2998268 -33.6593615,151.2996155 -33.6594152,151.2992842 -33.6593283,151.2992922 -33.6591158,151.2987432 -33.6590742,151.298426 -33.659065,151.2981297 -33.6590478,151.297595 -33.6590135,151.2975523 -33.659156,151.2972489 -33.6591009,151.2968294 -33.6585842,151.2965021 -33.6581848,151.2965457 -33.6579805,151.2963914 -33.6574896,151.2964095 -33.6572932,151.2963246 -33.6571422,151.296241 -33.656984,151.2961291 -33.6567455,151.2961688 -33.6566973,151.2959987 -33.6564111,151.2959691 -33.6563467,151.2958996 -33.6561957,151.2959226 -33.6560837,151.2956479 -33.6558449,151.2954425 -33.6556663,151.2953975 -33.6556272,151.2951511 -33.655592,151.295093 -33.6556341,151.2950289 -33.6556281,151.2949527 -33.655576,151.2949928 -33.6554497,151.2949087 -33.6554237,151.2948666 -33.6554277,151.2947945 -33.6555058,151.2943231 -33.6554028,151.2942924 -33.6553956,151.2939168 -33.6553075,151.2938793 -33.655287,151.2936343 -33.6551532,151.293636 -33.6551104,151.2936383 -33.655055,151.2935263 -33.6549293,151.2934345 -33.6548261,151.2934099 -33.6547985,151.2932562 -33.6546539,151.2931705 -33.6545732,151.2930507 -33.6544604,151.2930352 -33.6544459,151.2929727 -33.6544026,151.2927559 -33.6542525,151.2925636 -33.6541194,151.2924433 -33.6540747,151.2923525 -33.6540409,151.2922236 -33.653993,151.2918737 -33.653921,151.2916986 -33.6539289,151.2913681 -33.654112,151.2909043 -33.6542585,151.290423 -33.6541989,151.2898425 -33.65394,151.2897702 -33.6537842,151.2894556 -33.6536283,151.2891723 -33.6534186,151.289004 -33.6532328)\nLINESTRING(151.289004 -33.6532328,151.288853 -33.6529797,151.2887387 -33.6528377,151.2887006 -33.652698,151.2887572 -33.6524584,151.2888789 -33.6522278,151.2891971 -33.6516584,151.2896529 -33.6516353,151.2896101 -33.6512403,151.2895401 -33.6510137,151.2891535 -33.6507449,151.2890422 -33.6506899,151.2887039 -33.6510817,151.2883733 -33.6510111,151.2879831 -33.6508166,151.2871055 -33.6500486,151.2868473 -33.6497111,151.2868016 -33.64952,151.2868226 -33.6492937,151.2868833 -33.6492947,151.2869595 -33.6490754,151.2870216 -33.64907,151.2872709 -33.6487015,151.2873628 -33.6486356,151.2874368 -33.6482657,151.2871706 -33.6476511,151.2862758 -33.6469087,151.2861835 -33.6467463,151.2861933 -33.6464802,151.2862269 -33.6462601,151.2864559 -33.6462379,151.2864742 -33.6460869,151.2864112 -33.6460013,151.2863772 -33.6459552,151.2863006 -33.6459424,151.2854315 -33.6448484,151.2852894 -33.6446925,151.2850596 -33.6445565,151.2848669 -33.6443281,151.2843444 -33.6445478,151.2841912 -33.6446106,151.283931 -33.6447775,151.2836067 -33.6448392,151.2828647 -33.6452115,151.2821754 -33.6455399,151.2818985 -33.6456554,151.2816227 -33.6455959,151.2806777 -33.6457641,151.2803664 -33.6458361,151.280103 -33.6459935,151.2796668 -33.6465206,151.2787393 -33.6473377,151.2784408 -33.6475492,151.278357 -33.6478915,151.2782024 -33.6482054,151.2778594 -33.6486589,151.2777258 -33.6489227,151.2777023 -33.6492565,151.2777304 -33.6496334,151.2778191 -33.6499152,151.2779668 -33.6502238,151.2781656 -33.6505256,151.2783993 -33.6507447,151.2788496 -33.6512516,151.2790855 -33.6514242,151.2796715 -33.6516868,151.2800097 -33.6519457)\nLINESTRING(151.2800097 -33.6519457,151.279848 -33.6521906,151.2799206 -33.6524905,151.2801588 -33.6527967)\nLINESTRING(151.2801588 -33.6527967,151.2800272 -33.6527838,151.2795446 -33.6525627,151.2792711 -33.6523697,151.2783705 -33.6520382,151.2778504 -33.6518205,151.2775383 -33.6516819,151.2774194 -33.6516646,151.2771817 -33.6516423,151.2769379 -33.6516522,151.2767774 -33.6517215,151.276718 -33.6518452,151.276715 -33.6519887,151.2767361 -33.6523671,151.276667 -33.652792,151.2763667 -33.6536812,151.2761509 -33.6544856,151.2758034 -33.6546226,151.2755259 -33.6548637,151.2753796 -33.6550921,151.2750371 -33.655557,151.274706 -33.6559888,151.2748456 -33.6560217,151.274683 -33.6563002,151.2745079 -33.6567713,151.2742913 -33.6568879,151.274131 -33.6569733,151.2740316 -33.6570828,151.273977 -33.657339,151.273512 -33.6574298,151.2733581 -33.6575899,151.2732331 -33.6577421,151.2729829 -33.6582386,151.2726109 -33.6587857,151.2723608 -33.6588445,151.2723416 -33.6586977,151.2725692 -33.6582332,151.2726302 -33.6579182,151.2728418 -33.6576113,151.2730054 -33.6574805,151.2730374 -33.6572589,151.2731753 -33.6570374,151.2732978 -33.6569069,151.273358 -33.6567796,151.273313 -33.6565985,151.2730581 -33.6564588,151.2719197 -33.6564524,151.2712553 -33.65631,151.2706961 -33.6561512,151.2700604 -33.6561654,151.269402 -33.656206,151.2685866 -33.6564918,151.2680865 -33.6567754,151.2675413 -33.6569457,151.2672603 -33.6570816)\nLINESTRING(151.2672603 -33.6570816,151.2665509 -33.6574545,151.2657542 -33.6575991,151.2648745 -33.6578313,151.264227 -33.6582748)\nLINESTRING(151.264227 -33.6582748,151.2640964 -33.6581619,151.2640651 -33.6580647,151.2644899 -33.6576425,151.2654465 -33.6574621,151.2661933 -33.6571088,151.2664817 -33.6567413,151.2673146 -33.6565792)\nLINESTRING(151.2673146 -33.6565792,151.2679464 -33.6564147,151.2684858 -33.6559834,151.2690359 -33.6558918,151.2699918 -33.6556011,151.2705938 -33.655493,151.2721421 -33.6558659,151.2734569 -33.6556366,151.2736918 -33.6555048,151.2738351 -33.6552046,151.2738338 -33.6550499,151.2739265 -33.6544924,151.27404 -33.6542544,151.2741709 -33.653957,151.2745078 -33.6532126,151.274565 -33.6523193,151.2743024 -33.6518642,151.2742592 -33.6517194,151.2745719 -33.6511564,151.2743207 -33.6501633,151.2742079 -33.649944,151.2737407 -33.6496073,151.2734067 -33.6492594,151.2732407 -33.6491365,151.2735085 -33.6489203,151.273891 -33.6488359,151.2747696 -33.6480957,151.2755047 -33.6466385,151.275553 -33.6463605,151.2754601 -33.64606,151.2752667 -33.6457678,151.2745753 -33.6451575,151.2747575 -33.6450529)\nLINESTRING(151.2747575 -33.6450529,151.2749132 -33.645035,151.2753929 -33.6449246,151.2755449 -33.6448555,151.2756743 -33.644739,151.2757635 -33.6447027,151.2759937 -33.6446206,151.2763882 -33.6445196,151.2767388 -33.6443156)\nLINESTRING(151.2767388 -33.6443156,151.2770621 -33.6441848,151.2771942 -33.6441291,151.2774976 -33.6439172,151.2776152 -33.6437404,151.2777613 -33.6436143,151.2778818 -33.6434646,151.2781432 -33.6433287,151.2782067 -33.6432685,151.2783009 -33.6430923,151.2786018 -33.6429413,151.2789187 -33.6427584,151.2789905 -33.6426805,151.279146 -33.6426825,151.2792692 -33.6426164,151.2795129 -33.6425302,151.2800261 -33.6423893,151.2801598 -33.6423063,151.2802234 -33.6422816,151.2805351 -33.6422489,151.280683 -33.6422715)\nLINESTRING(151.280683 -33.6422715,151.2807225 -33.6422737,151.2807371 -33.642198,151.2808645 -33.6421206,151.2808802 -33.6421342,151.2810615 -33.6420043,151.2810915 -33.641947,151.2811231 -33.6418661,151.2811194 -33.6416256,151.280983 -33.6413034,151.2807091 -33.6410562,151.2805248 -33.6409217,151.2803974 -33.6408387)\nLINESTRING(151.2803974 -33.6408387,151.2801726 -33.6406924,151.2799855 -33.6406138,151.2799709 -33.6406074)\nLINESTRING(151.2799709 -33.6406074,151.2796005 -33.6404439,151.2795156 -33.6404536,151.2792593 -33.6404176,151.2792201 -33.6404375,151.279102 -33.6404127,151.2788965 -33.6402732,151.2787234 -33.6400981,151.2783481 -33.6398361,151.2779032 -33.6394975,151.2778783 -33.6394324,151.2778771 -33.6393464,151.2777868 -33.6392296,151.2777116 -33.6392214,151.2775649 -33.6392944,151.2775388 -33.6392883,151.2772559 -33.6391101,151.2770361 -33.6391371,151.2768827 -33.6388691,151.2767912 -33.6387882,151.2767169 -33.6384256,151.2767392 -33.6383383,151.2767142 -33.6382198,151.2767465 -33.6380891,151.2769316 -33.6381236,151.2770504 -33.6382334,151.2771661 -33.6382596,151.277688 -33.638106,151.2779191 -33.6380201,151.2780889 -33.6380101,151.2782181 -33.638042,151.2784989 -33.6381412,151.2787375 -33.6381092,151.279047 -33.6382226,151.279171 -33.6382104,151.2792472 -33.6381224,151.2793965 -33.6381902,151.2795083 -33.6382082,151.2795485 -33.6382074,151.2795984 -33.6381675,151.2796387 -33.6381637,151.2797412 -33.6382398,151.2798299 -33.6382535,151.2799914 -33.6382443,151.2800075 -33.6382235,151.2799464 -33.6381717,151.2800289 -33.6380818,151.2800682 -33.6380864,151.2801516 -33.6381552,151.2802145 -33.6381727,151.2803256 -33.6381459,151.2805726 -33.6381475,151.2809499 -33.638084,151.2812197 -33.6380791,151.281425 -33.6381766,151.2815454 -33.6381481,151.2816234 -33.6380905,151.2818824 -33.6380801,151.2821281 -33.6379636,151.2822325 -33.6378728,151.2823629 -33.6375367,151.2824736 -33.637357,151.2825066 -33.6371665,151.2825888 -33.6369888,151.2826525 -33.6367635)\nLINESTRING(151.2826525 -33.6367635,151.2826609 -33.6365795,151.2826194 -33.6363878,151.2825016 -33.6362631,151.2822833 -33.6361512,151.281965 -33.6361086,151.2813285 -33.6360606,151.2808453 -33.6361208,151.2803916 -33.6362361,151.2802011 -33.6362497,151.2798708 -33.6363695,151.2796614 -33.6364673,151.2795378 -33.6365833,151.2794291 -33.6366759,151.279064 -33.6369827,151.2789425 -33.6369448,151.2787369 -33.6371992,151.2785462 -33.6372088,151.2784001 -33.6372623,151.2782489 -33.6371554,151.2781024 -33.6371384,151.2780047 -33.6370695,151.2777917 -33.6370109,151.277537 -33.6367554,151.2773851 -33.6365375,151.277404 -33.636266,151.277035 -33.6357881,151.2766693 -33.6357324,151.2761383 -33.6353261,151.2759104 -33.6353024,151.275647 -33.6351929,151.2751342 -33.6351074,151.2749569 -33.635049,151.274573 -33.6350594,151.2741146 -33.6349165)\nLINESTRING(151.2741146 -33.6349165,151.2744498 -33.6346539,151.2747241 -33.6341771,151.274593 -33.6338576,151.2742354 -33.6334995,151.2737233 -33.6332694,151.2735013 -33.6330091,151.2732451 -33.632654,151.2729629 -33.6325025,151.2726986 -33.6325266,151.2723067 -33.6322728,151.2719746 -33.6321556,151.2713942 -33.6318135,151.2704009 -33.6315437,151.2706747 -33.6314471,151.2709903 -33.6314293,151.2713299 -33.6314856,151.2722679 -33.6317144,151.2731086 -33.6320455,151.2741706 -33.6322437,151.274507 -33.6323487,151.2746874 -33.6323433,151.2751517 -33.632337,151.2754074 -33.6324652,151.2758791 -33.6329542)\nLINESTRING(151.2758791 -33.6329542,151.2761275 -33.6330689,151.2764368 -33.6331425,151.2765345 -33.6332249,151.2767188 -33.6333822,151.2768237 -33.6333528,151.2769244 -33.633416,151.277013 -33.633422,151.2772205 -33.6334233,151.2774145 -33.6334572,151.2778456 -33.6335085,151.2784328 -33.6334419,151.2786631 -33.6333291,151.27887 -33.6331683,151.2789482 -33.6328656,151.2792317 -33.6327245,151.279412 -33.6326901,151.2796238 -33.6326984,151.2799302 -33.6327686,151.2801291 -33.6328674,151.2803875 -33.6330171,151.2807102 -33.6330801,151.2813236 -33.6329445,151.281918 -33.6323147,151.2818445 -33.6321027,151.2819542 -33.6319521,151.2819593 -33.6318298,151.2820565 -33.6317016,151.2825805 -33.6316638,151.282838 -33.6316379,151.2834115 -33.6316682,151.2835362 -33.6316603,151.2836981 -33.6316261,151.2838431 -33.6317499,151.2839717 -33.6317794,151.28423 -33.6316129,151.2842738 -33.6315222,151.2842175 -33.6311955,151.2841443 -33.6311056,151.2840521 -33.6308644,151.2837956 -33.6304948,151.2836434 -33.6294083,151.2853034 -33.6293965,151.2870276 -33.6301756,151.2875896 -33.6302564,151.2879176 -33.6301111,151.2883916 -33.6292782,151.2890507 -33.6288222,151.2900708 -33.6287097,151.2904036 -33.6285038,151.2905151 -33.6283612)\nLINESTRING(151.2905151 -33.6283612,151.2906139 -33.6282349,151.2900713 -33.6278987,151.2897104 -33.6279315,151.2894119 -33.6279771,151.2888912 -33.6281419,151.2883747 -33.6280592,151.2878773 -33.6276897,151.2878555 -33.6276613,151.2875528 -33.627266,151.2872473 -33.6268784,151.285099 -33.6251804,151.2847778 -33.6248202,151.2835553 -33.6240911,151.2832806 -33.6240873,151.2828571 -33.6228272,151.2826775 -33.6221065,151.2828838 -33.6218413,151.2833013 -33.6218359,151.2840669 -33.6223538,151.2846508 -33.6225255)\nLINESTRING(151.2846508 -33.6225255,151.2851617 -33.6227115,151.2857312 -33.6228127,151.2870026 -33.6228439,151.2874513 -33.6227474,151.2887165 -33.6216808,151.2889754 -33.6206379,151.2899847 -33.6198408,151.2900623 -33.6197129,151.29031 -33.6196005,151.2904797 -33.6196188)\nLINESTRING(151.3022011 -33.6025687,151.3025884 -33.6025687)\nLINESTRING(151.3025884 -33.6025687,151.3026905 -33.6025687,151.303405 -33.6026632,151.3039039 -33.602791,151.3041648 -33.6030275,151.3044208 -33.603204,151.3047058 -33.6038201,151.3046905 -33.6040662,151.3046615 -33.6042011,151.3045792 -33.6044241,151.3042569 -33.6048971,151.3039058 -33.6052414,151.3041341 -33.6053765,151.3043758 -33.6059645,151.3043068 -33.6061562,151.3043566 -33.6064918,151.3042491 -33.6066353,151.3031902 -33.6076454,151.3030559 -33.6077765,151.3030137 -33.6078947,151.3031441 -33.6083453,151.3030981 -33.6086329,151.3029617 -33.6088513,151.3022913 -33.6096276,151.3016568 -33.6101267,151.3016104 -33.6103847,151.3016047 -33.6106249,151.3016104 -33.6108339,151.3017488 -33.6109498,151.3019664 -33.6111714,151.3020897 -33.6112205,151.3021086 -33.6112455,151.302158 -33.6112585,151.3022031 -33.6112538,151.3022531 -33.6112445,151.3023487 -33.6112992,151.3023765 -33.6113695,151.3024332 -33.6114037,151.302481 -33.6114306,151.3026855 -33.6114889,151.3027999 -33.6115676,151.3031256 -33.6118804,151.3031856 -33.6120063,151.3032034 -33.6121156,151.3030493 -33.6125048,151.3022816 -33.6132847,151.3010425 -33.6138569,151.3005532 -33.6141993,151.3002006 -33.6151876,151.3003336 -33.6155172,151.3013479 -33.617045,151.3016443 -33.6173771,151.3018339 -33.6176635,151.3020011 -33.61787,151.30215 -33.6180382,151.3022237 -33.6182349,151.302316 -33.6186368,151.3023572 -33.6191049,151.3025092 -33.6194666,151.3027376 -33.6197841,151.3028108 -33.6200191,151.3028642 -33.6203104,151.3028596 -33.6205246,151.302674 -33.620833,151.3020546 -33.6212685,151.3015128 -33.6214606,151.3011173 -33.6215424,151.3008262 -33.6215155,151.3003976 -33.6213991,151.2999189 -33.6211445,151.2995244 -33.6209654,151.299133 -33.6208873,151.2987385 -33.6208986,151.29827 -33.621085,151.2981551 -33.6212885,151.2971315 -33.6218048,151.2964385 -33.6222533,151.2960984 -33.6224318,151.2955129 -33.6225568,151.2946884 -33.622872,151.2940076 -33.6227947,151.2932121 -33.6226937,151.2926567 -33.6224952,151.2922757 -33.6221461,151.2915305 -33.62155,151.2910474 -33.6208501,151.2909231 -33.6201767,151.2908756 -33.6197554,151.2908397 -33.6194757,151.2904797 -33.6196188)\nLINESTRING(151.2950274 -33.6059687,151.2958055 -33.6058264,151.2960976 -33.605831,151.2962131 -33.6058083,151.2964124 -33.6057449,151.2965851 -33.605591,151.2968954 -33.6051535,151.2973192 -33.6047559,151.2976235 -33.6045471,151.3003391 -33.6034685,151.3006255 -33.6031404,151.3013238 -33.6027527,151.3022011 -33.6025687)\nLINESTRING(151.2950274 -33.6059687,151.2943888 -33.6060582,151.2941322 -33.6061725,151.2939173 -33.6064161,151.2934757 -33.6063465)\nLINESTRING(151.2934757 -33.6063465,151.2931176 -33.6061129,151.293046 -33.6058345,151.2927953 -33.6056606,151.2924014 -33.6056953,151.2912018 -33.6059936,151.2908139 -33.6060085,151.290414 -33.6061328,151.2903104 -33.6061348,151.2897456 -33.6061526,151.2892287 -33.605849,151.2890473 -33.6057252,151.2879611 -33.6057053,151.2874598 -33.6055661,151.2869525 -33.6056755,151.2867794 -33.6056407,151.2862542 -33.6057252,151.2859558 -33.6059588,151.2855082 -33.6062421,151.2853948 -33.6059886,151.2846905 -33.6058146,151.2845115 -33.6057301,151.2842012 -33.6057103,151.2838132 -33.6055214,151.2839027 -33.605253,151.2838252 -33.6050193,151.2837714 -33.6048454,151.2843793 -33.6050984,151.2845951 -33.6051834,151.2851859 -33.605243,151.285353 -33.6052132,151.2855141 -33.6051337,151.2858424 -33.6048304,151.2859498 -33.6046565,151.2858126 -33.6038959,151.2858126 -33.6035679,151.285908 -33.6034784,151.2863318 -33.6037866,151.2868033 -33.6036573,151.2870062 -33.6038413,151.2872091 -33.6039606,151.2872986 -33.6039655,151.2874359 -33.60405,151.2877104 -33.6041495,151.2880745 -33.6041992,151.2884385 -33.6042737,151.2887489 -33.6043682,151.2891189 -33.6043682,151.2894949 -33.6042787,151.2899127 -33.6041693,151.2901216 -33.6040401,151.2902171 -33.603881,151.2904976 -33.6037518,151.2905871 -33.603538,151.2906885 -33.6034386,151.2908139 -33.6035132,151.2910108 -33.6034685,151.2913689 -33.6038114,151.2915301 -33.6040799,151.2914465 -33.6047459,151.2914704 -33.6051287,151.29149 -33.6052979,151.291632 -33.6054201,151.2919267 -33.6052104,151.2924122 -33.6050528,151.2925923 -33.604733,151.2928386 -33.6044225,151.2930521 -33.6042504,151.2932734 -33.6040441,151.2934195 -33.6039269,151.2936714 -33.6035002,151.2937557 -33.6034571,151.2938457 -33.6034111,151.2941434 -33.603126,151.2944404 -33.6023615,151.2943841 -33.602182,151.294405 -33.6020296,151.2944806 -33.6019311,151.2946107 -33.6018682,151.2946608 -33.6018584,151.295051 -33.6017826,151.2955173 -33.60161,151.2963206 -33.6012283,151.296558 -33.6009547,151.2967407 -33.5999614,151.2968677 -33.5997368,151.2988671 -33.5973697)\nLINESTRING(151.2988671 -33.5973697,151.2989488 -33.5972686,151.2991532 -33.5969813,151.2995534 -33.5963031,151.2999658 -33.5956811,151.3001855 -33.5951315,151.3005874 -33.5945197)\nLINESTRING(151.3005874 -33.5945197,151.302552 -33.5949155,151.3028632 -33.5948618,151.3030429 -33.5947233,151.3031287 -33.5945535,151.3030904 -33.5943391)\nLINESTRING(151.3030904 -33.5943391,151.3030686 -33.5942316)\nLINESTRING(151.3030686 -33.5942316,151.3029705 -33.593747,151.3019702 -33.5928784)\nLINESTRING(151.3019702 -33.5928784,151.3014432 -33.5923201,151.3011255 -33.5918395,151.3008497 -33.591104,151.3007828 -33.5906568,151.3008915 -33.5902556,151.3010461 -33.5896747,151.3012258 -33.5891105)\nLINESTRING(151.3012258 -33.5891105,151.3014264 -33.58868,151.3017023 -33.5884,151.3018903 -33.5881994,151.3020282 -33.5879403,151.3020032 -33.5877355,151.3018037 -33.5875557,151.301999 -33.5874722,151.3023082 -33.5875725,151.3033566 -33.5878625,151.3042083 -33.5879292,151.3046441 -33.5878983,151.3050303 -33.587648,151.305438 -33.5874156,151.3056955 -33.5872369,151.3060389 -33.5870581,151.3064895 -33.5869509,151.3068757 -33.5867721,151.3071761 -33.5864325,151.3072405 -33.5859856,151.3071456 -33.5853211,151.3072137 -33.5849481,151.3073155 -33.5844306,151.3078953 -33.5843836,151.3088015 -33.5840048,151.3093136 -33.5837157,151.3095579 -33.58334,151.3096223 -33.5829825,151.3096008 -33.5827143,151.3094506 -33.5822138,151.3093919 -33.5818465,151.3093085 -33.5815606,151.309429 -33.5812029,151.3097111 -33.5810775,151.3101373 -33.5809267,151.3105298 -33.5805499,151.310781 -33.580158,151.3109126 -33.5794561)\nLINESTRING(151.3109126 -33.5794561,151.3213948 -33.5797474,151.3224698 -33.5797773)\nLINESTRING(151.3224698 -33.5797773,151.3225128 -33.580192,151.3228132 -33.5807069,151.3233968 -33.5809929,151.3242551 -33.5811002,151.3248395 -33.580856,151.3249462 -33.5810756,151.3255922 -33.5813402)\nLINESTRING(151.3255922 -33.5813402,151.3257685 -33.5814864,151.3260011 -33.5819709,151.3260516 -33.5823199)\nLINESTRING(151.3260516 -33.5823199,151.3260804 -33.5825195,151.3260428 -33.5830435,151.3257784 -33.5841651,151.3250265 -33.5857962,151.324164 -33.5870972,151.3239808 -33.5873826,151.3239408 -33.5874404)\nLINESTRING(151.3239408 -33.5874404,151.3238635 -33.5875905,151.3227649 -33.589042,151.3211685 -33.5911369,151.3203357 -33.5920974)\nLINESTRING(151.3203357 -33.5920974,151.319223 -33.5933806)\nLINESTRING(151.319223 -33.5933806,151.3191294 -33.5934028,151.3190456 -33.5935137,151.3188733 -33.593728,151.3188297 -33.5938636,151.3188459 -33.5939265,151.3188608 -33.5940457,151.3189079 -33.5942421,151.3190831 -33.5944526,151.3194936 -33.5948088,151.3196794 -33.5952523,151.3199447 -33.5954489,151.3198046 -33.5962628,151.3193634 -33.5971878,151.3185291 -33.5985726,151.3180572 -33.599092,151.3177033 -33.5993634,151.3176415 -33.5994196,151.3168943 -33.6003882,151.3161472 -33.6011322,151.316018 -33.6016235,151.3161477 -33.6021049,151.3161354 -33.6022005,151.3162541 -33.6023804,151.3167197 -33.6026848,151.3168564 -33.6025713,151.3175515 -33.6030216,151.3182295 -33.6033765,151.3186399 -33.6036086,151.31898 -33.603801,151.3197994 -33.6043548,151.3198829 -33.6044126,151.3207929 -33.6051225,151.320737 -33.6053584,151.3208042 -33.6055117,151.321049 -33.6057884,151.3212134 -33.6059481,151.3214503 -33.6062261,151.3215606 -33.606518,151.3216886 -33.6067608,151.3219761 -33.6070231,151.3222438 -33.6072445,151.3224998 -33.6073577,151.3228582 -33.6074561,151.3229704 -33.6074643,151.323061 -33.6077053,151.3230216 -33.607912,151.3230433 -33.6081826,151.3230334 -33.6086302,151.323187 -33.6088861,151.3234134 -33.6091435,151.32363 -33.6092977,151.324014 -33.6094125,151.3242877 -33.6094371,151.3249388 -33.6093487,151.3253886 -33.6092765,151.3256547 -33.6092435,151.3256314 -33.6093754,151.3257116 -33.6095253,151.3258096 -33.6097346,151.325843 -33.6099043,151.3257687 -33.610063,151.3257583 -33.6103258,151.3255744 -33.6107456,151.3255414 -33.6111517,151.3256013 -33.6112996,151.3257422 -33.6113868,151.326247 -33.6119288,151.3265774 -33.6124436,151.3265404 -33.6130105,151.3267369 -33.6134091,151.3267756 -33.6138395,151.3273522 -33.6148229,151.3271927 -33.616296,151.3271129 -33.6168819,151.3269724 -33.6174476,151.3266909 -33.6183205,151.326721 -33.618555,151.3269491 -33.6183581,151.3270974 -33.6184228,151.327231 -33.6185179,151.3272723 -33.6187041,151.3275067 -33.6189656,151.3276569 -33.6191825,151.3276975 -33.6192147)\nLINESTRING(151.3276975 -33.6192147,151.3278499 -33.6194417,151.3278919 -33.6195923,151.3278214 -33.6197481,151.3279737 -33.6199241)\nLINESTRING(151.3279737 -33.6199241,151.3273018 -33.6198171,151.3271196 -33.6199061,151.3270093 -33.6199966,151.3270262 -33.6201563,151.3270382 -33.6202694,151.3268705 -33.6206193,151.3270666 -33.6208198,151.3277237 -33.6209815,151.3282395 -33.6209736,151.3277114 -33.6213101,151.3269487 -33.6214526,151.3265322 -33.6213316,151.3263336 -33.6215806)\nLINESTRING(151.3263336 -33.6215806,151.3262149 -33.6215561,151.3256354 -33.621395,151.3253099 -33.6212858,151.3246957 -33.6210409,151.3241504 -33.6209225,151.3235377 -33.6209175,151.3235291 -33.6208092,151.3231952 -33.6208354,151.3231392 -33.6203957,151.3231375 -33.620382,151.3230436 -33.6203962,151.3229407 -33.6204117,151.3229854 -33.6209915,151.3226153 -33.621109,151.3219379 -33.6211734,151.3215763 -33.621184,151.3213648 -33.6211877,151.3212165 -33.6211516,151.3211132 -33.6210586,151.3208881 -33.6207148,151.3207596 -33.6204216,151.3205602 -33.6202188,151.3204076 -33.6201121,151.3202298 -33.6200164,151.320205 -33.6200044,151.3199487 -33.6198806,151.3196383 -33.6197143,151.3194963 -33.6196039,151.3194093 -33.6194561,151.3193055 -33.6192656,151.3192266 -33.6190821,151.3192233 -33.6189876,151.319177 -33.6188394,151.3191081 -33.618689,151.3190654 -33.6185109,151.3190079 -33.6183804,151.3189535 -33.6181972,151.3188795 -33.6180233,151.3187797 -33.6178305,151.3187469 -33.617767,151.3186788 -33.6175932,151.3186624 -33.6174891,151.3186442 -33.6173107,151.3186089 -33.6171598,151.3185949 -33.6171001,151.3185894 -33.6169557,151.3185844 -33.6168259,151.3185752 -33.616585,151.3186591 -33.6165672,151.3186425 -33.6165379,151.3185207 -33.6163168,151.3184424 -33.6161766,151.3183774 -33.6160557,151.3182859 -33.6158857,151.3182199 -33.6157632,151.3181387 -33.615636,151.3179341 -33.6154505,151.3177955 -33.6152807,151.3176384 -33.6151511,151.3175324 -33.6150659,151.3174962 -33.6151043,151.3173054 -33.6149451,151.3172281 -33.6148632,151.3172792 -33.6148167,151.3172119 -33.6147447,151.3170741 -33.6146201,151.3170126 -33.6145838,151.3168944 -33.6145456,151.3166754 -33.6144879,151.3165659 -33.6143601,151.3163958 -33.6142852,151.316307 -33.6142715,151.3162247 -33.6143043,151.3161611 -33.6143794,151.3161491 -33.6144454,151.3161341 -33.6147564,151.3160774 -33.6149408,151.3161924 -33.6152777,151.3162365 -33.6155156,151.3162445 -33.6155591,151.3161425 -33.615578,151.3161584 -33.6157045,151.3161662 -33.6158401,151.3161804 -33.6160864,151.3161702 -33.6161774,151.3161524 -33.6163348,151.3161341 -33.6164977,151.3161245 -33.6166013,151.3161175 -33.6166451,151.3162225 -33.6167122,151.3162158 -33.6167687,151.3161992 -33.6169076,151.3161619 -33.6171767,151.3161169 -33.6175019,151.3160938 -33.6176687,151.3160822 -33.6177521,151.3162105 -33.6179728,151.3163006 -33.6181455,151.3163489 -33.6184205,151.3163729 -33.6186229,151.3163341 -33.6189527,151.316263 -33.6191962,151.3160198 -33.6195172,151.3159605 -33.6196859,151.3159557 -33.6197981,151.3159573 -33.6200064,151.3159749 -33.6201037,151.315999 -33.6202187,151.3159829 -33.6203042,151.3160134 -33.6204404,151.3160423 -33.6205459,151.3160869 -33.6207909,151.3161031 -33.620918,151.3160764 -33.6211944,151.3160663 -33.6214607,151.3160679 -33.6215502,151.3160117 -33.621723,151.3159225 -33.6219498,151.3159155 -33.6219676,151.315853 -33.6221536,151.3159174 -33.6223026,151.3160098 -33.6224735,151.3160221 -33.6224963,151.3161909 -33.6227127,151.3163164 -33.6228115,151.3164993 -33.622843,151.316742 -33.6230148,151.3168882 -33.623301,151.3169393 -33.6235399,151.3169494 -33.6237662,151.31694 -33.6240894,151.3169281 -33.6241772,151.3169227 -33.6242175,151.3167891 -33.6245976,151.3167582 -33.6247623,151.3167798 -33.6248594,151.3167107 -33.6249752,151.3165428 -33.6252564,151.3164848 -33.6253246,151.3160911 -33.6254869,151.3159255 -33.6255719,151.31579 -33.6256271,151.3156515 -33.6257694,151.3155846 -33.625838,151.3155627 -33.6260864,151.3155725 -33.6262396,151.3155821 -33.6263887,151.315585 -33.626514,151.3155866 -33.6265827,151.3155913 -33.6267836,151.3155965 -33.6270013,151.3155823 -33.6270788,151.3155674 -33.6271597,151.3155021 -33.6275159,151.3154435 -33.6278358,151.3154272 -33.627925,151.3153798 -33.6281834,151.3153411 -33.6283945,151.3153064 -33.628584)\nLINESTRING(151.3153064 -33.628584,151.3151369 -33.6288225,151.3149089 -33.6287574,151.3148758 -33.6288311,151.3148066 -33.6289855,151.3150598 -33.6290501,151.3149568 -33.6292855,151.3147978 -33.6300451,151.3147907 -33.6302479,151.3147888 -33.6303689,151.3148952 -33.6304604,151.3150125 -33.6305612,151.3150125 -33.6306625,151.3150125 -33.6307596,151.3150125 -33.6309478,151.3147582 -33.6313124,151.3143534 -33.6317683,151.3139233 -33.6321342,151.3137146 -33.6322936,151.3129607 -33.6328694,151.312812 -33.6330188,151.3125737 -33.6333676,151.3123623 -33.6336771,151.312327 -33.633802,151.3123007 -33.6338679,151.312244 -33.6339137,151.3121032 -33.6339881,151.3120018 -33.6340482,151.3119399 -33.6341283,151.3118764 -33.6342785,151.3118111 -33.6343171,151.3117407 -33.6343242,151.3116548 -33.6343714,151.3115774 -33.6344301,151.3115396 -33.6344873,151.3114211 -33.634513,151.3113052 -33.6346045,151.3110998 -33.6347877,151.3109985 -33.6348234,151.3109005 -33.6347963,151.3106909 -33.6349348,151.3105071 -33.6350309,151.3103208 -33.6351179,151.310069 -33.6351825,151.3097696 -33.6352322,151.3096086 -33.635244,151.3093948 -33.6352435,151.3090441 -33.6352125,151.3086597 -33.6351697,151.3082187 -33.6351067,151.3078408 -33.6351545,151.307524 -33.6352841,151.3073975 -33.6353055,151.3072775 -33.6353217,151.3071209 -33.6353398,151.3069885 -33.6353453,151.306433 -33.6353639,151.3059323 -33.6353869,151.3056289 -33.6353986,151.3054064 -33.6353666,151.3052206 -33.6353457,151.305045 -33.6353321,151.3047852 -33.6352663,151.3047211 -33.6354579,151.3045869 -33.6354314,151.3045102 -33.6356867,151.3044966 -33.6357646,151.3045468 -33.6358315,151.3045997 -33.6360481,151.3046201 -33.6363115,151.3046954 -33.6365056,151.3047436 -33.6366812,151.3047748 -33.6367665,151.3048075 -33.6368408,151.3048717 -33.6369826,151.3050031 -33.637252,151.305065 -33.6374454,151.3051008 -33.6375467,151.3051413 -33.6376584,151.3052487 -33.6378446,151.3053474 -33.6379617,151.3054934 -33.6379886,151.305708 -33.6379651,151.3058468 -33.6379607,151.3059827 -33.637996,151.306168 -33.6381341,151.3062532 -33.6381587,151.3064123 -33.6382735,151.3065793 -33.638357,151.3066676 -33.6384086,151.3068558 -33.6385177,151.3070249 -33.6386121,151.3071352 -33.638641,151.3073417 -33.6386696,151.3075253 -33.6386838,151.3076612 -33.6386748,151.3078826 -33.6386944,151.3080466 -33.6387006,151.3082183 -33.6386509,151.308299 -33.6386617,151.3084814 -33.6387019,151.3088506 -33.6387642,151.3092776 -33.6389231,151.309478 -33.6389727,151.3095448 -33.6390979,151.3095687 -33.6393541,151.3091655 -33.639364,151.3091822 -33.6395785,151.3091059 -33.6396501,151.3089681 -33.6397051,151.3087253 -33.639853,151.3086621 -33.6399851,151.308612 -33.6400933,151.3085208 -33.6401939,151.308443 -33.6402455,151.3081598 -33.6401743,151.3078342 -33.6401616,151.3074555 -33.6403128,151.3072122 -33.6404601,151.30689 -33.640609,151.3066784 -33.640845)\nLINESTRING(151.2864639 -33.6387861,151.2864727 -33.6388036,151.2864955 -33.6388488,151.2865696 -33.638996,151.2866446 -33.6391449,151.2866927 -33.6392402,151.2866921 -33.6394316,151.2866916 -33.6395906,151.2865776 -33.6397318,151.2864293 -33.6399157,151.2863754 -33.6400108,151.2863242 -33.6401011,151.2863126 -33.6401216,151.2862922 -33.6403582,151.2862881 -33.6404897,151.2863738 -33.6405997,151.2864835 -33.6407403,151.2865581 -33.6408361,151.286654 -33.6409591,151.2866619 -33.6409692,151.2868763 -33.641138,151.2870098 -33.6412373,151.2871512 -33.6413647,151.2872669 -33.6414688,151.2873632 -33.6415445,151.2875729 -33.6417043,151.2876775 -33.641781,151.2877994 -33.6418705,151.2880315 -33.6420407,151.2882087 -33.6421708,151.2882965 -33.6422352,151.2884121 -33.6422986,151.2885301 -33.6423633,151.2887985 -33.6424168,151.2889513 -33.642349,151.2890715 -33.6422743,151.2892977 -33.6422353,151.2895761 -33.6422229,151.2898153 -33.6422775,151.290019 -33.6423948,151.2901929 -33.6425462,151.2903852 -33.6429276,151.2905387 -33.6430559,151.2906691 -33.6431649,151.2910386 -33.6433968,151.2912257 -33.6434682,151.2913921 -33.6435317,151.2916422 -33.6436273,151.2917963 -33.6436728,151.2919095 -33.6436481,151.2919422 -33.6436753,151.2919943 -33.6437185,151.2921852 -33.6439305,151.2923815 -33.6441486,151.2924659 -33.6441441,151.2926921 -33.6441321,151.2931344 -33.6439943,151.2932021 -33.6439511,151.2933917 -33.6438302,151.2935963 -33.6436998,151.293749 -33.6435671,151.2938392 -33.6434888,151.2939583 -33.6433852,151.2940541 -33.643302,151.294173 -33.6432358,151.2942802 -33.643176,151.2943818 -33.6431193,151.294604 -33.6429955,151.2948452 -33.6428375,151.2950286 -33.6426221,151.2951182 -33.6423778,151.2951231 -33.64208,151.2950591 -33.6418672,151.2950926 -33.6417697,151.2951374 -33.6416393,151.2952375 -33.6415169,151.2953849 -33.6413388,151.2954557 -33.6412534,151.295601 -33.6410778,151.2956083 -33.640943,151.2956134 -33.6408477,151.2956596 -33.6407595,151.2957275 -33.6406301,151.2956964 -33.640394,151.2956806 -33.6402743,151.2956806 -33.6399324,151.2956925 -33.6397544,151.2955884 -33.6395412,151.295447 -33.6394534,151.295215 -33.6393094,151.2950651 -33.6392163,151.2949226 -33.6391279,151.2949072 -33.6390898,151.2948047 -33.6388369,151.2947596 -33.6387256,151.2947555 -33.6387154,151.2947012 -33.6385815,151.2946637 -33.6384496,151.2946263 -33.6383186,151.2945843 -33.638171,151.2945332 -33.6379915,151.2945108 -33.6379131,151.294485 -33.6378225,151.2944484 -33.6376938,151.2943966 -33.637512,151.2943503 -33.6373496,151.2943487 -33.6371117,151.2943474 -33.6369161,151.2943463 -33.636765,151.294346 -33.6367205,151.2941157 -33.6364547,151.2940865 -33.636421,151.2938792 -33.6363547,151.2936982 -33.6362967,151.2936348 -33.6362764,151.2935332 -33.6362691,151.2933071 -33.6362527,151.2930235 -33.6363284,151.2928143 -33.6365268,151.2927256 -33.6366136,151.2926959 -33.63668,151.2926537 -33.6367742,151.2926181 -33.6368223,151.2925135 -33.6369574,151.2923928 -33.6369685,151.2921876 -33.6370824,151.2920266 -33.6370824,151.2919475 -33.637059,151.2918764 -33.6369998,151.2917984 -33.6368587,151.2916351 -33.6366715,151.2915463 -33.6366042,151.2914403 -33.6365675,151.2913954 -33.6365488,151.291244 -33.6364866,151.2911843 -33.6364621,151.2909422 -33.6364027,151.2909214 -33.6363976,151.2907316 -33.6364095,151.2905652 -33.6364198,151.2904269 -33.6364285,151.2903117 -33.6364357,151.2902911 -33.636437,151.2901756 -33.6364188,151.2900099 -33.6363929,151.2899345 -33.6363567,151.289803 -33.6362937,151.2896207 -33.6362504,151.2894147 -33.6362015,151.28926 -33.6361197,151.2890522 -33.6361265,151.2889102 -33.6362982,151.2889014 -33.6368628,151.2888248 -33.6369535,151.2887141 -33.6370844,151.2885061 -33.6372089,151.2883935 -33.6372303,151.2882724 -33.6372533,151.2882514 -33.6372535,151.2880871 -33.6372557,151.2878969 -33.6372582,151.2877574 -33.6372601,151.2876061 -33.6372854,151.2875158 -33.6373005,151.2873014 -33.6373859,151.2870435 -33.63756,151.2870025 -33.6376025,151.2868556 -33.6377552,151.2868149 -33.6377975,151.2866028 -33.6380766,151.2865396 -33.6380967,151.2865143 -33.6382522,151.2865126 -33.6382702,151.286469 -33.638723,151.2864639 -33.6387861)\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/fourSelfIntersectingPolygon.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38979' action='modify' lat='0.02380535434' lon='-0.03808856805' />\n  <node id='-38982' action='modify' lat='0.03341732667' lon='-0.03817839958' />\n  <node id='-38986' action='modify' lat='0.03377665273' lon='-0.02551215407' />\n  <node id='-38988' action='modify' lat='-0.0263206369' lon='-0.02470367031' />\n  <node id='-38990' action='modify' lat='-0.02685962601' lon='-0.03683092665' />\n  <node id='-38992' action='modify' lat='-0.01419338134' lon='-0.03728008429' />\n  <node id='-38994' action='modify' lat='-0.01320523456' lon='0.02703929005' />\n  <node id='-38996' action='modify' lat='-0.02461383803' lon='0.02739861617' />\n  <node id='-38998' action='modify' lat='-0.02488333259' lon='0.01715782193' />\n  <node id='-39000' action='modify' lat='0.03422581029' lon='0.01536119136' />\n  <node id='-39002' action='modify' lat='0.03449530483' lon='0.02533249101' />\n  <node id='-39004' action='modify' lat='0.02569181626' lon='0.02578164865' />\n  <way id='-38983' action='modify'>\n    <nd ref='-38979' />\n    <nd ref='-38982' />\n    <nd ref='-38986' />\n    <nd ref='-38988' />\n    <nd ref='-38990' />\n    <nd ref='-38992' />\n    <nd ref='-38994' />\n    <nd ref='-38996' />\n    <nd ref='-38998' />\n    <nd ref='-39000' />\n    <nd ref='-39002' />\n    <nd ref='-39004' />\n    <nd ref='-38979' />\n    <tag k='area' v='yes' />\n    <tag k='building' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/concatenated_geojson_files_expected",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-71.0,-37.0],[-72.0,-38.0]]},\"properties\":{\"prop1\":\"foo\",\"prop2\":1.99,\"prop3\":[1,2,3]}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-72.0,-38.0],[-73.0,-39.0]]},\"properties\":{\"prop1\":\"bar\",\"prop2\":2.99,\"prop3\":[2,3,4]}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"MultiLineString\",\"coordinates\":[[[27.0,42.0],[26.0,43.0]],[[26.0,43.0],[25.0,44.0]]]},\"properties\":{\"prop1\":\"foo\",\"prop2\":1.99}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/beanA.json",
    "content": "{\n  \"id\": 10,\n  \"name\": \"Hello\",\n  \"score\": 10.5,\n  \"ids\": [1,2,3],\n  \"names\": [\"hello\", \"hola\", \"bonjour\"],\n  \"scores\": [1.414, 3.14159, 2.718],\n  \"result\": true,\n  \"results\": [true, false, true],\n  \"tags\": {\"access\": \"private\", \"foot\": \"no\"}\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/beanBWithArray.json",
    "content": "{\n  \"name\": \"outer\",\n  \"beanA\": {\n    \"id\": 10,\n    \"name\": \"Hello\",\n    \"score\": 10.5,\n    \"ids\": [1,2,3],\n    \"names\": [\"hello\", \"hola\", \"bonjour\"],\n    \"scores\": [1.414, 3.14159, 2.718],\n    \"result\": true,\n    \"results\": [true, false, true],\n    \"tags\": {\"access\": \"private\", \"foot\": \"no\"}\n  },\n  \"beanAs\": [\n    {\n      \"id\": 11,\n      \"name\": \"Hello1\",\n      \"score\": 10.51,\n      \"ids\": [11,21,31],\n      \"names\": [\"hello1\", \"hola1\", \"bonjour1\"],\n      \"scores\": [1.414, 3.14159, 2.718],\n      \"result\": true,\n      \"results\": [true, false, true],\n      \"tags\": {\"access1\": \"private\", \"foot1\": \"no\"}\n    },\n    {\n      \"id\": 12,\n      \"name\": \"Hello2\",\n      \"score\": 10.52,\n      \"ids\": [12,22,32],\n      \"names\": [\"hello\", \"hola\", \"bonjour\"],\n      \"scores\": [1.414, 3.14159, 2.718],\n      \"result\": true,\n      \"results\": [true, false, true],\n      \"tags\": {\"access2\": \"private\", \"foot2\": \"no\"}\n    }\n  ]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/beanBWithoutArray.json",
    "content": "{\n  \"name\": \"outer\",\n  \"beanA\": {\n    \"id\": 10,\n    \"name\": \"Hello\",\n    \"score\": 10.5,\n    \"ids\": [1,2,3],\n    \"names\": [\"hello\", \"hola\", \"bonjour\"],\n    \"scores\": [1.414, 3.14159, 2.718],\n    \"result\": true,\n    \"results\": [true, false, true],\n    \"tags\": {\"access\": \"private\", \"foot\": \"no\"}\n  }\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/description1.json",
    "content": "{\n  \"type\": \"UPDATE\",\n  \"descriptors\": [\n    {\n      \"name\": \"TAG\",\n      \"type\": \"ADD\",\n      \"key\": \"c\",\n      \"value\": \"3\"\n    },\n    {\n      \"name\": \"TAG\",\n      \"type\": \"UPDATE\",\n      \"key\": \"b\",\n      \"value\": \"2a\",\n      \"originalValue\": \"2\"\n    },\n    {\n      \"name\": \"TAG\",\n      \"type\": \"REMOVE\",\n      \"key\": \"a\",\n      \"value\": \"1\"\n    },\n    {\n      \"name\": \"GEOMETRY\",\n      \"type\": \"ADD\",\n      \"position\": \"5/5\",\n      \"afterView\": \"LINESTRING (-122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\"\n    },\n    {\n      \"name\": \"GEOMETRY\",\n      \"type\": \"REMOVE\",\n      \"position\": \"0/5\",\n      \"beforeView\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451)\"\n    },\n    {\n      \"name\": \"PARENT_RELATION\",\n      \"type\": \"ADD\",\n      \"afterView\": \"3\"\n    },\n    {\n      \"name\": \"PARENT_RELATION\",\n      \"type\": \"REMOVE\",\n      \"beforeView\": \"1\"\n    },\n    {\n      \"name\": \"START_NODE\",\n      \"type\": \"UPDATE\",\n      \"beforeView\": \"1\",\n      \"afterView\": \"10\"\n    },\n    {\n      \"name\": \"END_NODE\",\n      \"type\": \"UPDATE\",\n      \"beforeView\": \"2\",\n      \"afterView\": \"20\"\n    }\n  ]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/descriptor1.json",
    "content": "{\n  \"name\": \"TAG\",\n  \"type\": \"ADD\",\n  \"key\": \"c\",\n  \"value\": \"3\"\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/descriptor2.json",
    "content": "{\n  \"name\": \"START_NODE\",\n  \"type\": \"UPDATE\",\n  \"beforeView\": \"1\",\n  \"afterView\": \"10\"\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/descriptor3.json",
    "content": "{\n  \"name\": \"TAG\",\n  \"type\": \"UPDATE\",\n  \"key\": \"b\",\n  \"value\": \"2a\",\n  \"originalValue\": \"2\"\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/feature1.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        -122.009566,\n        37.33531\n      ],\n      [\n        -122.031007,\n        37.390535\n      ],\n      [\n        -122.028932,\n        37.332451\n      ],\n      [\n        -122.052138,\n        37.317585\n      ],\n      [\n        -122.0304871,\n        37.3314171\n      ]\n    ]\n  },\n  \"properties\": {}\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/feature2.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\":\n    [[\n      [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],\n      [100.0, 1.0], [100.0, 0.0]\n    ]]\n\n  },\n  \"properties\": {\n    \"prop0\": \"value0\",\n    \"prop1\": { \"this\": \"that\" }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/featureChangeProperties.json",
    "content": "{\n  \"featureChangeType\": \"ADD\",\n  \"metadata\": {\n    \"somekey1\": \"some value 1\",\n    \"somekey2\": \"some value 2\"\n  },\n  \"description\": {\n    \"type\": \"UPDATE\",\n    \"descriptors\": [\n      {\n        \"name\": \"TAG\",\n        \"type\": \"ADD\",\n        \"key\": \"c\",\n        \"value\": \"3\"\n      },\n      {\n        \"name\": \"TAG\",\n        \"type\": \"UPDATE\",\n        \"key\": \"b\",\n        \"value\": \"2a\",\n        \"originalValue\": \"2\"\n      },\n      {\n        \"name\": \"TAG\",\n        \"type\": \"REMOVE\",\n        \"key\": \"a\",\n        \"value\": \"1\"\n      },\n      {\n        \"name\": \"GEOMETRY\",\n        \"type\": \"ADD\",\n        \"position\": \"5/5\",\n        \"afterView\": \"LINESTRING (-122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\"\n      },\n      {\n        \"name\": \"GEOMETRY\",\n        \"type\": \"REMOVE\",\n        \"position\": \"0/5\",\n        \"beforeView\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451)\"\n      },\n      {\n        \"name\": \"PARENT_RELATION\",\n        \"type\": \"ADD\",\n        \"afterView\": \"3\"\n      },\n      {\n        \"name\": \"PARENT_RELATION\",\n        \"type\": \"REMOVE\",\n        \"beforeView\": \"1\"\n      },\n      {\n        \"name\": \"START_NODE\",\n        \"type\": \"UPDATE\",\n        \"beforeView\": \"1\",\n        \"afterView\": \"10\"\n      },\n      {\n        \"name\": \"END_NODE\",\n        \"type\": \"UPDATE\",\n        \"beforeView\": \"2\",\n        \"afterView\": \"20\"\n      }\n    ]\n  },\n  \"entityType\": \"EDGE\",\n  \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n  \"identifier\": 123,\n  \"tags\": {\n    \"b\": \"2a\",\n    \"c\": \"3\"\n  },\n  \"relations\": [\n    2,\n    3\n  ],\n  \"startNode\": 10,\n  \"endNode\": 20,\n  \"WKT\": \"LINESTRING (-122.009566 37.33531, -122.031007 37.390535, -122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\",\n  \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/featureChangePropertiesBad1.json",
    "content": "{\n  \"completeEdgeClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\"\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/featureChangePropertiesBad2.json",
    "content": "{\n  \"relations\": \"bad\"\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/featureChangePropertiesExample1.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -61.2794632,\n    15.5327818,\n    -61.2781381,\n    15.5336917\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          -61.2794632,\n          15.5327818\n        ],\n        [\n          -61.2794632,\n          15.5336917\n        ],\n        [\n          -61.2781381,\n          15.5336917\n        ],\n        [\n          -61.2781381,\n          15.5327818\n        ],\n        [\n          -61.2794632,\n          15.5327818\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"meta-data\": {\n      \"mutator\": \"LegacyFixmeCorrection|9-168-233\"\n    },\n    \"description\": {\n      \"type\": \"UPDATE\",\n      \"descriptors\": [\n        {\n          \"name\": \"TAG\",\n          \"type\": \"REMOVE\",\n          \"key\": \"fixme\",\n          \"value\": \"inaccurate\"\n        }\n      ]\n    },\n    \"entityType\": \"LINE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteLine\",\n    \"identifier\": 548018879000000,\n    \"tags\": {\n      \"last_edit_user_name\": \"happy-camper\",\n      \"last_edit_changeset\": \"54773103\",\n      \"last_edit_time\": \"1513723955000\",\n      \"last_edit_user_id\": \"6346054\",\n      \"iso_country_code\": \"DMA\",\n      \"waterway\": \"stream\",\n      \"last_edit_version\": \"1\"\n    },\n    \"relations\": null,\n    \"WKT\": \"POLYGON ((-61.2794632 15.5327818, -61.2794632 15.5336917, -61.2781381 15.5336917, -61.2781381 15.5327818, -61.2794632 15.5327818))\",\n    \"bboxWKT\": \"POLYGON ((-61.2794632 15.5327818, -61.2794632 15.5336917, -61.2781381 15.5336917, -61.2781381 15.5327818, -61.2794632 15.5327818))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/featureChangePropertiesExample2.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    12.3984012,\n    55.7784144,\n    12.3984012,\n    55.7784144\n  ],\n  \"geometry\": {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n      [\n        [\n          12.3984012,\n          55.7784144\n        ],\n        [\n          12.3984012,\n          55.7784144\n        ],\n        [\n          12.3984012,\n          55.7784144\n        ],\n        [\n          12.3984012,\n          55.7784144\n        ]\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"mutator\": \"LegacyFixmeCorrection|9-168-233\"\n    },\n    \"description\": {\n      \"type\": \"ADD\",\n      \"descriptors\": [\n        {\n          \"name\": \"RELATION_MEMBER\",\n          \"type\": \"ADD\",\n          \"itemType\": \"NODE\",\n          \"id\": -1374305525422343588,\n          \"role\": \"via\",\n          \"beforeElement\": 9087654321,\n          \"afterElement\": 1234567890\n        }\n      ]\n    },\n    \"entityType\": \"RELATION\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation\",\n    \"identifier\": 4454872000000,\n    \"tags\": {\n      \"last_edit_user_name\": \"nbrummerstedt\",\n      \"last_edit_changeset\": \"27885435\",\n      \"last_edit_time\": \"1420291176000\",\n      \"last_edit_user_id\": \"718031\",\n      \"iso_country_code\": \"DNK\",\n      \"restriction\": \"no_left_turn\",\n      \"type\": \"restriction\",\n      \"last_edit_version\": \"1\"\n    },\n    \"relations\": [],\n    \"members\": [\n      \"{Member: ID = -1374305525422343588, Type = NODE, Role = via}\"\n    ],\n    \"WKT\": \"POLYGON ((12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144))\",\n    \"bboxWKT\": \"POLYGON ((12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/featureChangePropertiesRelationMemberDescriptor.json",
    "content": "{\n  \"featureChangeType\": \"ADD\",\n  \"metadata\": {\n    \"mutator\": \"LegacyFixmeCorrection|9-168-233\"\n  },\n  \"description\": {\n    \"type\": \"ADD\",\n    \"descriptors\": [\n      {\n        \"name\": \"RELATION_MEMBER\",\n        \"type\": \"ADD\",\n        \"itemType\": \"NODE\",\n        \"id\": 402306209000000,\n        \"role\": \"via\"\n      }\n    ]\n  },\n  \"entityType\": \"RELATION\",\n  \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteRelation\",\n  \"identifier\": 4454872000000,\n  \"tags\": {\n    \"last_edit_user_name\": \"nbrummerstedt\",\n    \"last_edit_changeset\": \"27885435\",\n    \"last_edit_time\": \"1420291176000\",\n    \"last_edit_user_id\": \"718031\",\n    \"iso_country_code\": \"DNK\",\n    \"restriction\": \"no_left_turn\",\n    \"type\": \"restriction\",\n    \"last_edit_version\": \"1\"\n  },\n  \"relations\": [],\n  \"members\": [\n    \"{Member: ID = 402306209000000, Type = NODE, Role = via}\"\n  ],\n  \"WKT\": \"POLYGON ((12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144))\",\n  \"bboxWKT\": \"POLYGON ((12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144, 12.3984012 55.7784144))\"\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/featureCollection1.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [102.0, 0.5]\n      },\n      \"properties\": {\n        \"prop0\": \"value0\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]\n        ]\n      },\n      \"properties\": {\n        \"prop0\": \"value0\",\n        \"prop1\": 0.0\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],\n            [100.0, 1.0], [100.0, 0.0]\n          ]\n        ]\n      },\n      \"properties\": {\n        \"prop0\": \"value0\",\n        \"prop1\": { \"this\": \"that\" }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/featureWithExtendedProperties.json",
    "content": "{\n  \"type\": \"Feature\",\n  \"bbox\": [\n    -122.052138,\n    37.317585,\n    -122.009566,\n    37.390535\n  ],\n  \"geometry\": {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n      [\n        -122.009566,\n        37.33531\n      ],\n      [\n        -122.031007,\n        37.390535\n      ],\n      [\n        -122.028932,\n        37.332451\n      ],\n      [\n        -122.052138,\n        37.317585\n      ],\n      [\n        -122.0304871,\n        37.3314171\n      ]\n    ]\n  },\n  \"properties\": {\n    \"featureChangeType\": \"ADD\",\n    \"metadata\": {\n      \"somekey1\": \"some value 1\",\n      \"somekey2\": \"some value 2\"\n    },\n    \"description\": {\n      \"type\": \"UPDATE\",\n      \"descriptors\": [\n        {\n          \"name\": \"TAG\",\n          \"type\": \"ADD\",\n          \"key\": \"c\",\n          \"value\": \"3\"\n        },\n        {\n          \"name\": \"TAG\",\n          \"type\": \"UPDATE\",\n          \"key\": \"b\",\n          \"value\": \"2a\",\n          \"originalValue\": \"2\"\n        },\n        {\n          \"name\": \"TAG\",\n          \"type\": \"REMOVE\",\n          \"key\": \"a\",\n          \"value\": \"1\"\n        },\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"ADD\",\n          \"position\": \"5/5\",\n          \"afterView\": \"LINESTRING (-122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\"\n        },\n        {\n          \"name\": \"GEOMETRY\",\n          \"type\": \"REMOVE\",\n          \"position\": \"0/5\",\n          \"beforeView\": \"LINESTRING (-122.052138 37.317585, -122.0304871 37.3314171, -122.028932 37.332451)\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"ADD\",\n          \"afterView\": \"3\"\n        },\n        {\n          \"name\": \"PARENT_RELATION\",\n          \"type\": \"REMOVE\",\n          \"beforeView\": \"1\"\n        },\n        {\n          \"name\": \"START_NODE\",\n          \"type\": \"UPDATE\",\n          \"beforeView\": \"1\",\n          \"afterView\": \"10\"\n        },\n        {\n          \"name\": \"END_NODE\",\n          \"type\": \"UPDATE\",\n          \"beforeView\": \"2\",\n          \"afterView\": \"20\"\n        }\n      ]\n    },\n    \"entityType\": \"EDGE\",\n    \"completeEntityClass\": \"org.openstreetmap.atlas.geography.atlas.complete.CompleteEdge\",\n    \"identifier\": 123,\n    \"tags\": {\n      \"b\": \"2a\",\n      \"c\": \"3\"\n    },\n    \" relations\": [\n      2,\n      3\n    ],\n    \"startNode\": 10,\n    \"endNode\": 20,\n    \"WKT\": \"LINESTRING (-122.009566 37.33531, -122.031007 37.390535, -122.028932 37.332451, -122.052138 37.317585, -122.0304871 37.3314171)\",\n    \"bboxWKT\": \"POLYGON ((-122.052138 37.317585, -122.052138 37.390535, -122.009566 37.390535, -122.009566 37.317585, -122.052138 37.317585))\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/foreignFieldsNested.json",
    "content": "{\n  \"type\": \"Point\",\n  \"unknown\": {\n    \"unknown_key1\": \"unknown_value1\",\n    \"unknown_key2\": \"unknown_value2\"\n  },\n  \"coordinates\": [\n    30,\n    10\n  ]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/foreignFieldsSimple.json",
    "content": "{\n  \"type\": \"Point\",\n  \"unknown_key1\": \"unknown_value1\",\n  \"unknown_key2\": \"unknown_value2\",\n  \"coordinates\": [\n    30,\n    10\n  ]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/geometryCollectionBasic.json",
    "content": "{\n  \"type\": \"GeometryCollection\",\n  \"geometries\": [\n    {\n      \"type\": \"Point\",\n      \"coordinates\": [40, 10]\n    },\n    {\n      \"type\": \"LineString\",\n      \"coordinates\": [\n        [10, 10], [20, 20], [10, 40]\n      ]\n    },\n    {\n      \"type\": \"Polygon\",\n      \"coordinates\": [\n        [[40, 40], [20, 45], [45, 30], [40, 40]]\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/geometryCollectionChildConversion.json",
    "content": "{\n  \"type\": \"GeometryCollection\",\n  \"geometries\": [\n    {\n      \"type\": \"Point\",\n      \"coordinates\": [40, 10]\n    },\n    {\n      \"type\": \"LineString\",\n      \"coordinates\": [\n        [10, 10], [20, 20], [10, 40]\n      ]\n    },\n    {\n      \"type\": \"Polygon\",\n      \"coordinates\": [\n        [[40, 40], [20, 45], [45, 30], [40, 40]]\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/geometryCollectionRecursiveNested.json",
    "content": "{\n  \"type\": \"GeometryCollection\",\n  \"geometries\": [\n    {\n      \"type\": \"Point\",\n      \"coordinates\": [40, 10]\n    },\n    {\n      \"type\": \"LineString\",\n      \"coordinates\": [\n        [10, 10], [20, 20], [10, 40]\n      ]\n    },\n    {\n      \"type\": \"Polygon\",\n      \"coordinates\": [\n        [[40, 40], [20, 45], [45, 30], [40, 40]]\n      ]\n    },\n    {\n      \"type\": \"GeometryCollection\",\n      \"geometries\": [\n        {\n          \"type\": \"Point\",\n          \"coordinates\": [40, 10]\n        },\n        {\n          \"type\": \"LineString\",\n          \"coordinates\": [\n            [10, 10], [20, 20], [10, 40]\n          ]\n        },\n        {\n          \"type\": \"Polygon\",\n          \"coordinates\": [\n            [[40, 40], [20, 45], [45, 30], [40, 40]]\n          ]\n        },\n        {\n          \"type\": \"GeometryCollection\",\n          \"geometries\": [\n            {\n              \"type\": \"Point\",\n              \"coordinates\": [40, 10]\n            },\n            {\n              \"type\": \"LineString\",\n              \"coordinates\": [\n                [10, 10], [20, 20], [10, 40]\n              ]\n            },\n            {\n              \"type\": \"Polygon\",\n              \"coordinates\": [\n                [[40, 40], [20, 45], [45, 30], [40, 40]]\n              ]\n            },\n            {\n              \"type\": \"GeometryCollection\",\n              \"geometries\": [\n                {\n                  \"type\": \"Point\",\n                  \"coordinates\": [40, 10]\n                },\n                {\n                  \"type\": \"LineString\",\n                  \"coordinates\": [\n                    [10, 10], [20, 20], [10, 40]\n                  ]\n                },\n                {\n                  \"type\": \"Polygon\",\n                  \"coordinates\": [\n                    [[40, 40], [20, 45], [45, 30], [40, 40]]\n                  ]\n                }\n              ]\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/lineString.json",
    "content": "{\n  \"type\": \"LineString\",\n  \"bbox\": [-1.1, -2.1, 3.1, 4.1, 5.1, 6.1],\n  \"coordinates\": [\n    [30, 10], [10, 30], [40, 40]\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/lineStringConversion.json",
    "content": "{\n  \"type\": \"LineString\",\n  \"bbox\": [-1.1, -2.1, 3.1, 4.1, 5.1, 6.1],\n  \"coordinates\": [\n    [31, 11], [10, 30], [40, 40]\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/multiLineString.json",
    "content": "{\n  \"type\": \"MultiLineString\",\n  \"bbox\": [-1.1, -2.1, 3.1, 4.1, 5.1, 6.1],\n  \"coordinates\": [\n    [[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]],\n    [[20, 30], [35, 35], [30, 20], [20, 30]]\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/multiLineStringConversion.json",
    "content": "{\n  \"type\": \"MultiLineString\",\n  \"bbox\": [-1.1, -2.1, 3.1, 4.1, 5.1, 6.1],\n  \"coordinates\": [\n    [[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]],\n    [[20, 30], [35, 35], [30, 20], [20, 30]]\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/multiPoint.json",
    "content": "{\n  \"type\": \"MultiPoint\",\n  \"bbox\": [-1.1, -2.1, 3.1, 4.1, 5.1, 6.1],\n  \"coordinates\": [\n    [30, 10], [10, 30], [40, 40]\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/multiPointConversion.json",
    "content": "{\n  \"type\": \"MultiPoint\",\n  \"bbox\": [-1.1, -2.1, 3.1, 4.1, 5.1, 6.1],\n  \"coordinates\": [\n    [31, 11], [10, 30], [40, 40]\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/multiPolygon.json",
    "content": "{\n  \"type\": \"MultiPolygon\",\n  \"bbox\": [-1.1, -2.1, 3.1, 4.1, 5.1, 6.1],\n  \"coordinates\": [[\n    [[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]]\n  ]]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/multiPolygonConversion.json",
    "content": "{\n  \"type\": \"MultiPolygon\",\n  \"bbox\": [\n    -123.008338722353,\n    47.4515507435753,\n    -122.960624259116,\n    47.4761279770882\n  ],\n  \"coordinates\": [\n    [\n      [\n        [\n          -122.97448802567412,\n          47.462669199371533\n        ],\n        [\n          -122.973756326883546,\n          47.45160282613179\n        ],\n        [\n          -123.008338722353002,\n          47.451550743575261\n        ],\n        [\n          -123.005720010892063,\n          47.462851449552005\n        ],\n        [\n          -122.97448802567412,\n          47.462669199371533\n        ]\n      ],\n      [\n        [\n          -123.003293851744431,\n          47.45865953554965\n        ],\n        [\n          -123.003409383132421,\n          47.455274516369577\n        ],\n        [\n          -122.997902386971916,\n          47.455300555810339\n        ],\n        [\n          -122.998171960210527,\n          47.458711611065731\n        ],\n        [\n          -123.003293851744431,\n          47.45865953554965\n        ]\n      ],\n      [\n        [\n          -122.990546888603674,\n          47.452305935596712\n        ],\n        [\n          -122.975720360479272,\n          47.452305935596712\n        ],\n        [\n          -122.976490569732476,\n          47.462460912677322\n        ],\n        [\n          -122.990277315365063,\n          47.462356769020758\n        ],\n        [\n          -122.990546888603674,\n          47.452305935596712\n        ]\n      ]\n    ],\n    [\n      [\n        [\n          -122.991355608319608,\n          47.47612797708819\n        ],\n        [\n          -122.991663692020879,\n          47.463788726214275\n        ],\n        [\n          -122.960624259116244,\n          47.463476302516959\n        ],\n        [\n          -122.961163405593496,\n          47.475971802142269\n        ],\n        [\n          -122.991355608319608,\n          47.47612797708819\n        ]\n      ],\n      [\n        [\n          -122.975489297703305,\n          47.47012792263763\n        ],\n        [\n          -122.975335255852656,\n          47.464478655301477\n        ],\n        [\n          -122.988236260844062,\n          47.464686934001243\n        ],\n        [\n          -122.988043708530753,\n          47.469919665496874\n        ],\n        [\n          -122.975489297703305,\n          47.47012792263763\n        ]\n      ],\n      [\n        [\n          -122.970482937557406,\n          47.473772289019308\n        ],\n        [\n          -122.970405916632075,\n          47.469112661281976\n        ],\n        [\n          -122.964552326307626,\n          47.468956465486571\n        ],\n        [\n          -122.965322535560873,\n          47.473850379818884\n        ],\n        [\n          -122.970482937557406,\n          47.473772289019308\n        ]\n      ]\n    ]\n  ]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/multiPolygonDonut.json",
    "content": "{\n  \"type\": \"MultiPolygon\",\n  \"bbox\": [\n    -1.1,\n    -2.1,\n    3.1,\n    4.1,\n    5.1,\n    6.1\n  ],\n  \"coordinates\": [[\n    [\n      [\n        100.0,\n        0.0\n      ],\n      [\n        102.0,\n        0.0\n      ],\n      [\n        102.0,\n        1.0\n      ],\n      [\n        100.0,\n        1.0\n      ],\n      [\n        100.0,\n        0.0\n      ]\n    ],\n    [\n      [\n        100.45,\n        0.45\n      ],\n      [\n        100.75,\n        0.45\n      ],\n      [\n        100.75,\n        0.75\n      ],\n      [\n        100.45,\n        0.75\n      ],\n      [\n        100.45,\n        0.45\n      ]\n    ]\n  ]]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/point.json",
    "content": "{\n  \"type\": \"Point\",\n  \"coordinates\": [30, 10]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/pointConversion.json",
    "content": "{\n  \"type\": \"Point\",\n  \"coordinates\": [30, 10]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/pointWithBbox2D.json",
    "content": "{\n  \"bbox\": [-1.0, -2.0, 3.0, 4.0],\n  \"type\": \"Point\",\n  \"coordinates\": [30, 10]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/pointWithBbox3D.json",
    "content": "{\n  \"bbox\": [-1.0, -2.0, 3.0, 4.0, 5.0, 6.0],\n  \"type\": \"Point\",\n  \"coordinates\": [30, 10]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/polygon.json",
    "content": "{\n  \"type\": \"Polygon\",\n  \"coordinates\": [\n    [[30, 10], [40, 40], [20, 40], [10, 20], [30, 10]]\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/polygonConversion.json",
    "content": "{\n  \"type\": \"Polygon\",\n  \"coordinates\": [\n    [[30, 10], [40, 40], [20, 40], [10, 20], [30, 10]]\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/propertiesNested.json",
    "content": "{\n  \"type\": \"Point\",\n  \"coordinates\": [102.0, 0.5],\n  \"properties\": {\n    \"prop0\": {\"a\": 123, \"b\": \"hello\", \"c\": 10.5},\n    \"prop1\": \"value1\"\n  }\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/parser/propertiesSimple.json",
    "content": "{\n  \"type\": \"Point\",\n  \"coordinates\": [102.0, 0.5],\n  \"properties\": {\n    \"prop0\": \"value0\",\n    \"prop1\": \"value1\"\n  }\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/test1.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-71,-37],[-72,-38]]},\"properties\":{\"prop1\":\"foo\",\"prop2\":1.99,\"prop3\":[1,2,3]}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/test2.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-72,-38],[-73,-39]]},\"properties\":{\"prop1\":\"bar\",\"prop2\":2.99,\"prop3\":[2,3,4]}}]}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/geojson/test3.geojson",
    "content": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"MultiLineString\",\"coordinates\":[[[27,42],[26,43]],[[26,43],[25,44]]]},\"properties\":{\"prop1\":\"foo\",\"prop2\":1.99}}]}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/polygonWithFlatZeroAreaPart.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38979' action='modify' lat='0.0091179001' lon='-0.00004491576' />\n  <node id='-38982' action='modify' lat='0.00076356799' lon='-0.00633312275' />\n  <node id='-38986' action='modify' lat='0.00076356799' lon='-0.01747223228' />\n  <node id='-38988' action='modify' lat='0.00076356799' lon='0.01351964503' />\n  <way id='-38983' action='modify'>\n    <nd ref='-38979' />\n    <nd ref='-38982' />\n    <nd ref='-38986' />\n    <nd ref='-38988' />\n    <nd ref='-38979' />\n    <tag k='area' v='yes' />\n    <tag k='building' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/polygonWithInclinedZeroArea.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38979' action='modify' lat='0.02146973479' lon='-0.00651278581' />\n  <node id='-38982' action='modify' lat='0.00547972322' lon='-0.00929756319' />\n  <node id='-38986' action='modify' lat='-0.00853399517' lon='-0.02331128162' />\n  <node id='-38988' action='modify' lat='0.01751714777' lon='0.00273986162' />\n  <way id='-38983' action='modify'>\n    <nd ref='-38979' />\n    <nd ref='-38982' />\n    <nd ref='-38986' />\n    <nd ref='-38988' />\n    <nd ref='-38979' />\n    <tag k='area' v='yes' />\n    <tag k='building' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/selfIntersectingPolygon.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38979' action='modify' lat='0.00547972322' lon='-0.02569181713' />\n  <node id='-38982' action='modify' lat='0.01203742472' lon='-0.02209855599' />\n  <node id='-38986' action='modify' lat='0.01212725625' lon='-0.01194759328' />\n  <node id='-38988' action='modify' lat='0.00691702767' lon='-0.00547972323' />\n  <node id='-38990' action='modify' lat='-0.01284590846' lon='-0.00799500603' />\n  <node id='-38992' action='modify' lat='-0.01949344129' lon='-0.00071865223' />\n  <node id='-38994' action='modify' lat='-0.01868495758' lon='0.01033062577' />\n  <node id='-38996' action='modify' lat='-0.00988146808' lon='0.01257641398' />\n  <node id='-38998' action='modify' lat='-0.00485090253' lon='0.00260511432' />\n  <node id='-39000' action='modify' lat='-0.00503056558' lon='-0.01383405538' />\n  <node id='-39002' action='modify' lat='-0.00089831528' lon='-0.02425451267' />\n  <way id='-38983' action='modify'>\n    <nd ref='-38979' />\n    <nd ref='-38982' />\n    <nd ref='-38986' />\n    <nd ref='-38988' />\n    <nd ref='-38990' />\n    <nd ref='-38992' />\n    <nd ref='-38994' />\n    <nd ref='-38996' />\n    <nd ref='-38998' />\n    <nd ref='-39000' />\n    <nd ref='-39002' />\n    <nd ref='-38979' />\n    <tag k='area' v='yes' />\n    <tag k='building' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/sharding/testDynamicSharding.txt",
    "content": "0-0-0+\n1-0-0+\n2-0-0+\n3-0-0+\n4-0-0+\n5-0-0\n5-0-1\n5-1-0\n5-1-1\n4-0-1\n4-1-0\n4-1-1\n3-0-1+\n4-0-2+\n5-0-4\n5-0-5\n5-1-4+\n6-2-8\n6-2-9\n6-3-8\n6-3-9+\n7-6-18\n7-6-19+\n8-12-38\n8-12-39+\n9-24-78\n9-24-79\n9-25-78\n9-25-79\n8-13-38\n8-13-39\n7-7-18\n7-7-19+\n8-14-38\n8-14-39\n8-15-38\n8-15-39+\n9-30-78\n9-30-79\n9-31-78\n9-31-79+\n10-62-158\n10-62-159\n10-63-158\n10-63-159\n5-1-5+\n6-2-10\n6-2-11\n6-3-10\n6-3-11\n4-0-3+\n5-0-6\n5-0-7\n5-1-6\n5-1-7\n4-1-2\n4-1-3+\n5-2-6\n5-2-7\n5-3-6\n5-3-7\n3-1-0\n3-1-1\n2-0-1+\n3-0-2+\n4-0-4\n4-0-5+\n5-0-10\n5-0-11+\n6-0-22\n6-0-23+\n7-0-46+\n8-0-92+\n9-0-184\n9-0-185\n9-1-184\n9-1-185\n8-0-93\n8-1-92\n8-1-93\n7-0-47\n7-1-46\n7-1-47\n6-1-22\n6-1-23+\n7-2-46\n7-2-47\n7-3-46\n7-3-47\n5-1-10\n5-1-11\n4-1-4+\n5-2-8+\n6-4-16\n6-4-17\n6-5-16\n6-5-17\n5-2-9\n5-3-8\n5-3-9\n4-1-5\n3-0-3+\n4-0-6\n4-0-7\n4-1-6\n4-1-7\n3-1-2\n3-1-3\n2-1-0+\n3-2-0\n3-2-1\n3-3-0+\n4-6-0+\n5-12-0\n5-12-1+\n6-24-2+\n7-48-4\n7-48-5+\n8-96-10+\n9-192-20\n9-192-21\n9-193-20\n9-193-21\n8-96-11\n8-97-10\n8-97-11\n7-49-4\n7-49-5\n6-24-3\n6-25-2\n6-25-3\n5-13-0+\n6-26-0\n6-26-1\n6-27-0\n6-27-1\n5-13-1\n4-6-1\n4-7-0\n4-7-1+\n5-14-2\n5-14-3\n5-15-2+\n6-30-4+\n7-60-8\n7-60-9+\n8-120-18\n8-120-19\n8-121-18\n8-121-19\n7-61-8\n7-61-9\n6-30-5\n6-31-4\n6-31-5\n5-15-3\n3-3-1+\n4-6-2\n4-6-3+\n5-12-6\n5-12-7+\n6-24-14\n6-24-15\n6-25-14\n6-25-15+\n7-50-30\n7-50-31\n7-51-30\n7-51-31+\n8-102-62+\n9-204-124\n9-204-125\n9-205-124\n9-205-125\n8-102-63\n8-103-62\n8-103-63\n5-13-6+\n6-26-12\n6-26-13+\n7-52-26\n7-52-27\n7-53-26\n7-53-27\n6-27-12+\n7-54-24\n7-54-25\n7-55-24\n7-55-25\n6-27-13+\n7-54-26\n7-54-27\n7-55-26\n7-55-27\n5-13-7\n4-7-2\n4-7-3+\n5-14-6+\n6-28-12+\n7-56-24\n7-56-25+\n8-112-50\n8-112-51\n8-113-50\n8-113-51+\n9-226-102\n9-226-103\n9-227-102\n9-227-103\n7-57-24\n7-57-25\n6-28-13\n6-29-12+\n7-58-24+\n8-116-48\n8-116-49+\n9-232-98\n9-232-99\n9-233-98\n9-233-99\n8-117-48\n8-117-49\n7-58-25\n7-59-24+\n8-118-48\n8-118-49\n8-119-48\n8-119-49\n7-59-25\n6-29-13+\n7-58-26\n7-58-27\n7-59-26\n7-59-27\n5-14-7\n5-15-6+\n6-30-12\n6-30-13+\n7-60-26+\n8-120-52\n8-120-53\n8-121-52\n8-121-53+\n9-242-106\n9-242-107\n9-243-106\n9-243-107\n7-60-27+\n8-120-54\n8-120-55\n8-121-54\n8-121-55+\n9-242-110\n9-242-111\n9-243-110\n9-243-111\n7-61-26+\n8-122-52\n8-122-53+\n9-244-106\n9-244-107\n9-245-106\n9-245-107\n8-123-52\n8-123-53\n7-61-27\n6-31-12\n6-31-13\n5-15-7+\n6-30-14+\n7-60-28\n7-60-29\n7-61-28\n7-61-29\n6-30-15+\n7-60-30\n7-60-31\n7-61-30\n7-61-31\n6-31-14+\n7-62-28+\n8-124-56\n8-124-57\n8-125-56+\n9-250-112\n9-250-113\n9-251-112\n9-251-113\n8-125-57\n7-62-29+\n8-124-58\n8-124-59\n8-125-58\n8-125-59\n7-63-28\n7-63-29+\n8-126-58\n8-126-59\n8-127-58\n8-127-59\n6-31-15+\n7-62-30\n7-62-31\n7-63-30\n7-63-31\n2-1-1+\n3-2-2+\n4-4-4+\n5-8-8+\n6-16-16+\n7-32-32+\n8-64-64\n8-64-65\n8-65-64\n8-65-65\n7-32-33+\n8-64-66\n8-64-67\n8-65-66\n8-65-67\n7-33-32\n7-33-33+\n8-66-66\n8-66-67\n8-67-66\n8-67-67+\n9-134-134\n9-134-135\n9-135-134\n9-135-135\n6-16-17\n6-17-16\n6-17-17\n5-8-9\n5-9-8+\n6-18-16\n6-18-17\n6-19-16\n6-19-17\n5-9-9\n4-4-5\n4-5-4\n4-5-5+\n5-10-10+\n6-20-20+\n7-40-40\n7-40-41+\n8-80-82\n8-80-83\n8-81-82+\n9-162-164\n9-162-165\n9-163-164\n9-163-165\n8-81-83\n7-41-40\n7-41-41\n6-20-21+\n7-40-42+\n8-80-84\n8-80-85+\n9-160-170\n9-160-171+\n10-320-342\n10-320-343\n10-321-342\n10-321-343\n9-161-170\n9-161-171\n8-81-84\n8-81-85\n7-40-43\n7-41-42\n7-41-43\n6-21-20\n6-21-21\n5-10-11+\n6-20-22+\n7-40-44+\n8-80-88\n8-80-89\n8-81-88+\n9-162-176\n9-162-177\n9-163-176\n9-163-177\n8-81-89\n7-40-45\n7-41-44+\n8-82-88\n8-82-89\n8-83-88\n8-83-89\n7-41-45\n6-20-23+\n7-40-46\n7-40-47\n7-41-46+\n8-82-92+\n9-164-184\n9-164-185\n9-165-184+\n10-330-368\n10-330-369\n10-331-368\n10-331-369\n9-165-185\n8-82-93+\n9-164-186\n9-164-187\n9-165-186+\n10-330-372\n10-330-373\n10-331-372\n10-331-373\n9-165-187\n8-83-92\n8-83-93+\n9-166-186\n9-166-187\n9-167-186+\n10-334-372\n10-334-373\n10-335-372\n10-335-373\n9-167-187\n7-41-47\n6-21-22\n6-21-23\n5-11-10\n5-11-11+\n6-22-22+\n7-44-44+\n8-88-88+\n9-176-176\n9-176-177\n9-177-176\n9-177-177\n8-88-89\n8-89-88\n8-89-89\n7-44-45\n7-45-44\n7-45-45\n6-22-23\n6-23-22\n6-23-23+\n7-46-46\n7-46-47\n7-47-46+\n8-94-92\n8-94-93\n8-95-92\n8-95-93+\n9-190-186\n9-190-187+\n10-380-374\n10-380-375\n10-381-374\n10-381-375\n9-191-186\n9-191-187\n7-47-47\n3-2-3+\n4-4-6+\n5-8-12+\n6-16-24\n6-16-25\n6-17-24\n6-17-25\n5-8-13\n5-9-12\n5-9-13+\n6-18-26\n6-18-27\n6-19-26\n6-19-27+\n7-38-54+\n8-76-108+\n9-152-216\n9-152-217\n9-153-216\n9-153-217\n8-76-109+\n9-152-218\n9-152-219\n9-153-218+\n10-306-436\n10-306-437\n10-307-436\n10-307-437\n9-153-219\n8-77-108\n8-77-109\n7-38-55+\n8-76-110\n8-76-111\n8-77-110\n8-77-111\n7-39-54+\n8-78-108\n8-78-109+\n9-156-218\n9-156-219\n9-157-218\n9-157-219\n8-79-108\n8-79-109+\n9-158-218\n9-158-219\n9-159-218\n9-159-219\n7-39-55\n4-4-7\n4-5-6+\n5-10-12+\n6-20-24+\n7-40-48\n7-40-49\n7-41-48+\n8-82-96\n8-82-97\n8-83-96+\n9-166-192\n9-166-193\n9-167-192\n9-167-193\n8-83-97\n7-41-49\n6-20-25\n6-21-24+\n7-42-48\n7-42-49\n7-43-48+\n8-86-96\n8-86-97\n8-87-96\n8-87-97\n7-43-49\n6-21-25\n5-10-13\n5-11-12\n5-11-13\n4-5-7\n3-3-2+\n4-6-4\n4-6-5\n4-7-4+\n5-14-8\n5-14-9\n5-15-8+\n6-30-16+\n7-60-32\n7-60-33\n7-61-32+\n8-122-64\n8-122-65\n8-123-64\n8-123-65\n7-61-33\n6-30-17\n6-31-16\n6-31-17\n5-15-9\n4-7-5\n3-3-3\n1-0-1+\n2-0-2+\n3-0-4\n3-0-5+\n4-0-10\n4-0-11\n4-1-10\n4-1-11\n3-1-4+\n4-2-8\n4-2-9+\n5-4-18+\n6-8-36+\n7-16-72\n7-16-73+\n8-32-146\n8-32-147+\n9-64-294\n9-64-295\n9-65-294\n9-65-295+\n10-130-590\n10-130-591\n10-131-590\n10-131-591\n8-33-146\n8-33-147\n7-17-72\n7-17-73+\n8-34-146\n8-34-147\n8-35-146\n8-35-147\n6-8-37\n6-9-36\n6-9-37+\n7-18-74\n7-18-75\n7-19-74\n7-19-75\n5-4-19\n5-5-18+\n6-10-36+\n7-20-72\n7-20-73+\n8-40-146\n8-40-147+\n9-80-294\n9-80-295\n9-81-294\n9-81-295\n8-41-146\n8-41-147\n7-21-72\n7-21-73\n6-10-37\n6-11-36+\n7-22-72+\n8-44-144\n8-44-145\n8-45-144\n8-45-145+\n9-90-290\n9-90-291\n9-91-290\n9-91-291+\n10-182-582\n10-182-583\n10-183-582\n10-183-583\n7-22-73\n7-23-72\n7-23-73+\n8-46-146\n8-46-147\n8-47-146\n8-47-147\n6-11-37\n5-5-19+\n6-10-38+\n7-20-76\n7-20-77\n7-21-76+\n8-42-152+\n9-84-304\n9-84-305\n9-85-304\n9-85-305\n8-42-153\n8-43-152\n8-43-153+\n9-86-306\n9-86-307\n9-87-306\n9-87-307\n7-21-77\n6-10-39+\n7-20-78\n7-20-79\n7-21-78\n7-21-79\n6-11-38+\n7-22-76\n7-22-77\n7-23-76\n7-23-77\n6-11-39\n4-3-8+\n5-6-16\n5-6-17\n5-7-16\n5-7-17\n4-3-9+\n5-6-18\n5-6-19+\n6-12-38+\n7-24-76\n7-24-77\n7-25-76\n7-25-77\n6-12-39\n6-13-38\n6-13-39\n5-7-18\n5-7-19\n3-1-5+\n4-2-10\n4-2-11\n4-3-10\n4-3-11+\n5-6-22+\n6-12-44\n6-12-45\n6-13-44\n6-13-45\n5-6-23+\n6-12-46\n6-12-47\n6-13-46+\n7-26-92\n7-26-93\n7-27-92+\n8-54-184\n8-54-185\n8-55-184\n8-55-185+\n9-110-370\n9-110-371\n9-111-370\n9-111-371\n7-27-93\n6-13-47\n5-7-22+\n6-14-44\n6-14-45+\n7-28-90+\n8-56-180\n8-56-181\n8-57-180\n8-57-181+\n9-114-362\n9-114-363\n9-115-362\n9-115-363\n7-28-91\n7-29-90+\n8-58-180\n8-58-181+\n9-116-362\n9-116-363\n9-117-362\n9-117-363\n8-59-180\n8-59-181\n7-29-91\n6-15-44+\n7-30-88+\n8-60-176\n8-60-177\n8-61-176\n8-61-177\n7-30-89\n7-31-88\n7-31-89+\n8-62-178+\n9-124-356+\n10-248-712\n10-248-713\n10-249-712\n10-249-713\n9-124-357\n9-125-356\n9-125-357\n8-62-179\n8-63-178\n8-63-179\n6-15-45+\n7-30-90\n7-30-91\n7-31-90\n7-31-91+\n8-62-182\n8-62-183\n8-63-182\n8-63-183\n5-7-23+\n6-14-46\n6-14-47\n6-15-46+\n7-30-92\n7-30-93\n7-31-92+\n8-62-184\n8-62-185\n8-63-184\n8-63-185\n7-31-93\n6-15-47\n2-0-3+\n3-0-6+\n4-0-12+\n5-0-24+\n6-0-48\n6-0-49\n6-1-48\n6-1-49\n5-0-25\n5-1-24+\n6-2-48\n6-2-49\n6-3-48\n6-3-49\n5-1-25+\n6-2-50\n6-2-51\n6-3-50\n6-3-51+\n7-6-102\n7-6-103\n7-7-102+\n8-14-204\n8-14-205\n8-15-204\n8-15-205\n7-7-103\n4-0-13\n4-1-12\n4-1-13\n3-0-7+\n4-0-14+\n5-0-28+\n6-0-56+\n7-0-112\n7-0-113\n7-1-112\n7-1-113\n6-0-57\n6-1-56\n6-1-57\n5-0-29+\n6-0-58\n6-0-59\n6-1-58\n6-1-59\n5-1-28+\n6-2-56+\n7-4-112\n7-4-113\n7-5-112\n7-5-113\n6-2-57\n6-3-56\n6-3-57+\n7-6-114\n7-6-115\n7-7-114\n7-7-115+\n8-14-230\n8-14-231\n8-15-230\n8-15-231\n5-1-29+\n6-2-58+\n7-4-116+\n8-8-232\n8-8-233\n8-9-232\n8-9-233\n7-4-117+\n8-8-234\n8-8-235\n8-9-234\n8-9-235\n7-5-116\n7-5-117\n6-2-59\n6-3-58\n6-3-59+\n7-6-118\n7-6-119\n7-7-118\n7-7-119\n4-0-15+\n5-0-30\n5-0-31\n5-1-30\n5-1-31\n4-1-14+\n5-2-28+\n6-4-56\n6-4-57\n6-5-56\n6-5-57\n5-2-29\n5-3-28+\n6-6-56\n6-6-57\n6-7-56\n6-7-57+\n7-14-114\n7-14-115\n7-15-114\n7-15-115\n5-3-29\n4-1-15+\n5-2-30\n5-2-31+\n6-4-62\n6-4-63\n6-5-62\n6-5-63\n5-3-30\n5-3-31\n3-1-6+\n4-2-12\n4-2-13+\n5-4-26\n5-4-27+\n6-8-54+\n7-16-108+\n8-32-216+\n9-64-432\n9-64-433\n9-65-432\n9-65-433\n8-32-217\n8-33-216\n8-33-217+\n9-66-434+\n10-132-868\n10-132-869\n10-133-868\n10-133-869\n9-66-435\n9-67-434\n9-67-435\n7-16-109\n7-17-108\n7-17-109\n6-8-55\n6-9-54+\n7-18-108\n7-18-109\n7-19-108\n7-19-109\n6-9-55\n5-5-26\n5-5-27+\n6-10-54\n6-10-55\n6-11-54\n6-11-55+\n7-22-110+\n8-44-220\n8-44-221\n8-45-220\n8-45-221\n7-22-111\n7-23-110\n7-23-111\n4-3-12+\n5-6-24+\n6-12-48\n6-12-49\n6-13-48\n6-13-49\n5-6-25+\n6-12-50\n6-12-51+\n7-24-102\n7-24-103\n7-25-102\n7-25-103\n6-13-50+\n7-26-100\n7-26-101+\n8-52-202\n8-52-203+\n9-104-406\n9-104-407\n9-105-406\n9-105-407\n8-53-202\n8-53-203+\n9-106-406+\n10-212-812\n10-212-813\n10-213-812\n10-213-813\n9-106-407\n9-107-406\n9-107-407\n7-27-100\n7-27-101\n6-13-51\n5-7-24+\n6-14-48+\n7-28-96\n7-28-97\n7-29-96\n7-29-97+\n8-58-194\n8-58-195\n8-59-194\n8-59-195\n6-14-49\n6-15-48\n6-15-49+\n7-30-98\n7-30-99\n7-31-98\n7-31-99\n5-7-25\n4-3-13\n3-1-7+\n4-2-14+\n5-4-28\n5-4-29\n5-5-28\n5-5-29+\n6-10-58+\n7-20-116+\n8-40-232\n8-40-233\n8-41-232\n8-41-233\n7-20-117\n7-21-116\n7-21-117\n6-10-59\n6-11-58+\n7-22-116+\n8-44-232\n8-44-233\n8-45-232\n8-45-233+\n9-90-466\n9-90-467\n9-91-466\n9-91-467\n7-22-117+\n8-44-234\n8-44-235\n8-45-234\n8-45-235\n7-23-116+\n8-46-232\n8-46-233\n8-47-232\n8-47-233\n7-23-117\n6-11-59\n4-2-15+\n5-4-30+\n6-8-60+\n7-16-120+\n8-32-240\n8-32-241\n8-33-240\n8-33-241+\n9-66-482\n9-66-483\n9-67-482\n9-67-483\n7-16-121\n7-17-120\n7-17-121+\n8-34-242\n8-34-243\n8-35-242\n8-35-243\n6-8-61+\n7-16-122+\n8-32-244+\n9-64-488\n9-64-489\n9-65-488\n9-65-489+\n10-130-978\n10-130-979\n10-131-978\n10-131-979\n8-32-245\n8-33-244+\n9-66-488+\n10-132-976\n10-132-977\n10-133-976\n10-133-977\n9-66-489\n9-67-488\n9-67-489\n8-33-245\n7-16-123\n7-17-122\n7-17-123+\n8-34-246\n8-34-247\n8-35-246+\n9-70-492\n9-70-493\n9-71-492\n9-71-493\n8-35-247\n6-9-60\n6-9-61\n5-4-31+\n6-8-62+\n7-16-124\n7-16-125+\n8-32-250+\n9-64-500\n9-64-501\n9-65-500\n9-65-501\n8-32-251+\n9-64-502\n9-64-503\n9-65-502\n9-65-503\n8-33-250\n8-33-251\n7-17-124+\n8-34-248\n8-34-249\n8-35-248\n8-35-249\n7-17-125+\n8-34-250+\n9-68-500\n9-68-501+\n10-136-1002\n10-136-1003\n10-137-1002\n10-137-1003\n9-69-500\n9-69-501\n8-34-251\n8-35-250\n8-35-251+\n9-70-502\n9-70-503\n9-71-502\n9-71-503+\n10-142-1006\n10-142-1007\n10-143-1006\n10-143-1007\n6-8-63\n6-9-62+\n7-18-124\n7-18-125\n7-19-124+\n8-38-248\n8-38-249+\n9-76-498\n9-76-499\n9-77-498\n9-77-499\n8-39-248\n8-39-249\n7-19-125+\n8-38-250+\n9-76-500\n9-76-501\n9-77-500\n9-77-501\n8-38-251\n8-39-250\n8-39-251\n6-9-63+\n7-18-126\n7-18-127\n7-19-126\n7-19-127\n5-5-30\n5-5-31\n4-3-14+\n5-6-28\n5-6-29\n5-7-28\n5-7-29+\n6-14-58\n6-14-59\n6-15-58+\n7-30-116\n7-30-117\n7-31-116\n7-31-117\n6-15-59\n4-3-15\n2-1-2+\n3-2-4+\n4-4-8+\n5-8-16\n5-8-17+\n6-16-34\n6-16-35\n6-17-34+\n7-34-68\n7-34-69\n7-35-68\n7-35-69+\n8-70-138\n8-70-139\n8-71-138\n8-71-139+\n9-142-278\n9-142-279\n9-143-278\n9-143-279\n6-17-35\n5-9-16\n5-9-17+\n6-18-34+\n7-36-68\n7-36-69\n7-37-68+\n8-74-136\n8-74-137\n8-75-136\n8-75-137\n7-37-69\n6-18-35\n6-19-34\n6-19-35\n4-4-9\n4-5-8\n4-5-9+\n5-10-18+\n6-20-36\n6-20-37+\n7-40-74\n7-40-75+\n8-80-150\n8-80-151\n8-81-150\n8-81-151+\n9-162-302\n9-162-303\n9-163-302\n9-163-303+\n10-326-606\n10-326-607\n10-327-606\n10-327-607\n7-41-74\n7-41-75\n6-21-36+\n7-42-72\n7-42-73+\n8-84-146\n8-84-147\n8-85-146\n8-85-147\n7-43-72+\n8-86-144\n8-86-145\n8-87-144\n8-87-145\n7-43-73\n6-21-37\n5-10-19+\n6-20-38\n6-20-39+\n7-40-78+\n8-80-156\n8-80-157\n8-81-156+\n9-162-312\n9-162-313\n9-163-312\n9-163-313\n8-81-157\n7-40-79+\n8-80-158\n8-80-159\n8-81-158\n8-81-159\n7-41-78\n7-41-79\n6-21-38\n6-21-39\n5-11-18+\n6-22-36\n6-22-37\n6-23-36+\n7-46-72\n7-46-73+\n8-92-146\n8-92-147+\n9-184-294+\n10-368-588\n10-368-589\n10-369-588\n10-369-589\n9-184-295\n9-185-294\n9-185-295\n8-93-146+\n9-186-292\n9-186-293\n9-187-292\n9-187-293\n8-93-147\n7-47-72+\n8-94-144\n8-94-145\n8-95-144\n8-95-145\n7-47-73\n6-23-37\n5-11-19\n3-2-5+\n4-4-10\n4-4-11+\n5-8-22\n5-8-23\n5-9-22+\n6-18-44\n6-18-45+\n7-36-90+\n8-72-180+\n9-144-360\n9-144-361\n9-145-360\n9-145-361\n8-72-181\n8-73-180\n8-73-181\n7-36-91+\n8-72-182\n8-72-183\n8-73-182+\n9-146-364\n9-146-365\n9-147-364\n9-147-365\n8-73-183\n7-37-90\n7-37-91+\n8-74-182\n8-74-183\n8-75-182\n8-75-183\n6-19-44\n6-19-45+\n7-38-90\n7-38-91\n7-39-90\n7-39-91\n5-9-23\n4-5-10\n4-5-11+\n5-10-22\n5-10-23\n5-11-22+\n6-22-44\n6-22-45+\n7-44-90\n7-44-91+\n8-88-182\n8-88-183+\n9-176-366\n9-176-367\n9-177-366\n9-177-367\n8-89-182\n8-89-183\n7-45-90+\n8-90-180\n8-90-181\n8-91-180\n8-91-181+\n9-182-362\n9-182-363\n9-183-362\n9-183-363\n7-45-91\n6-23-44\n6-23-45\n5-11-23+\n6-22-46+\n7-44-92\n7-44-93+\n8-88-186\n8-88-187\n8-89-186\n8-89-187\n7-45-92\n7-45-93\n6-22-47\n6-23-46+\n7-46-92\n7-46-93+\n8-92-186\n8-92-187\n8-93-186+\n9-186-372+\n10-372-744\n10-372-745\n10-373-744\n10-373-745\n9-186-373\n9-187-372\n9-187-373\n8-93-187+\n9-186-374\n9-186-375\n9-187-374\n9-187-375\n7-47-92\n7-47-93\n6-23-47\n3-3-4+\n4-6-8\n4-6-9+\n5-12-18\n5-12-19\n5-13-18+\n6-26-36\n6-26-37\n6-27-36\n6-27-37\n5-13-19\n4-7-8+\n5-14-16\n5-14-17+\n6-28-34\n6-28-35\n6-29-34\n6-29-35\n5-15-16+\n6-30-32+\n7-60-64\n7-60-65\n7-61-64\n7-61-65\n6-30-33+\n7-60-66\n7-60-67+\n8-120-134\n8-120-135\n8-121-134\n8-121-135+\n9-242-270\n9-242-271+\n10-484-542\n10-484-543\n10-485-542\n10-485-543\n9-243-270\n9-243-271\n7-61-66\n7-61-67+\n8-122-134\n8-122-135\n8-123-134\n8-123-135+\n9-246-270\n9-246-271+\n10-492-542\n10-492-543\n10-493-542\n10-493-543\n9-247-270\n9-247-271\n6-31-32+\n7-62-64\n7-62-65\n7-63-64\n7-63-65\n6-31-33+\n7-62-66\n7-62-67\n7-63-66+\n8-126-132\n8-126-133\n8-127-132\n8-127-133+\n9-254-266\n9-254-267\n9-255-266\n9-255-267\n7-63-67+\n8-126-134+\n9-252-268\n9-252-269\n9-253-268\n9-253-269\n8-126-135\n8-127-134\n8-127-135\n5-15-17\n4-7-9\n3-3-5\n2-1-3+\n3-2-6+\n4-4-12+\n5-8-24+\n6-16-48\n6-16-49\n6-17-48\n6-17-49+\n7-34-98\n7-34-99\n7-35-98+\n8-70-196+\n9-140-392+\n10-280-784\n10-280-785\n10-281-784\n10-281-785\n9-140-393\n9-141-392\n9-141-393\n8-70-197\n8-71-196\n8-71-197\n7-35-99+\n8-70-198+\n9-140-396\n9-140-397\n9-141-396\n9-141-397\n8-70-199+\n9-140-398\n9-140-399\n9-141-398\n9-141-399\n8-71-198\n8-71-199\n5-8-25\n5-9-24\n5-9-25\n4-4-13+\n5-8-26\n5-8-27+\n6-16-54\n6-16-55\n6-17-54\n6-17-55\n5-9-26+\n6-18-52\n6-18-53\n6-19-52+\n7-38-104\n7-38-105\n7-39-104+\n8-78-208\n8-78-209\n8-79-208\n8-79-209\n7-39-105\n6-19-53+\n7-38-106\n7-38-107+\n8-76-214\n8-76-215\n8-77-214\n8-77-215\n7-39-106+\n8-78-212\n8-78-213\n8-79-212\n8-79-213\n7-39-107\n5-9-27+\n6-18-54\n6-18-55+\n7-36-110\n7-36-111\n7-37-110+\n8-74-220\n8-74-221\n8-75-220\n8-75-221\n7-37-111\n6-19-54+\n7-38-108+\n8-76-216\n8-76-217+\n9-152-434+\n10-304-868\n10-304-869\n10-305-868\n10-305-869\n9-152-435\n9-153-434\n9-153-435\n8-77-216\n8-77-217\n7-38-109+\n8-76-218\n8-76-219\n8-77-218\n8-77-219+\n9-154-438\n9-154-439\n9-155-438\n9-155-439\n7-39-108+\n8-78-216\n8-78-217\n8-79-216+\n9-158-432\n9-158-433\n9-159-432\n9-159-433\n8-79-217+\n9-158-434\n9-158-435+\n10-316-870\n10-316-871\n10-317-870\n10-317-871\n9-159-434\n9-159-435\n7-39-109\n6-19-55\n4-5-12\n4-5-13+\n5-10-26\n5-10-27\n5-11-26\n5-11-27+\n6-22-54+\n7-44-108\n7-44-109+\n8-88-218\n8-88-219+\n9-176-438+\n10-352-876\n10-352-877\n10-353-876\n10-353-877\n9-176-439+\n10-352-878\n10-352-879\n10-353-878\n10-353-879\n9-177-438\n9-177-439\n8-89-218+\n9-178-436\n9-178-437\n9-179-436\n9-179-437\n8-89-219\n7-45-108+\n8-90-216+\n9-180-432\n9-180-433\n9-181-432\n9-181-433\n8-90-217\n8-91-216+\n9-182-432\n9-182-433\n9-183-432\n9-183-433\n8-91-217\n7-45-109\n6-22-55\n6-23-54+\n7-46-108\n7-46-109+\n8-92-218\n8-92-219\n8-93-218\n8-93-219\n7-47-108\n7-47-109\n6-23-55\n3-2-7\n3-3-6\n3-3-7+\n4-6-14+\n5-12-28+\n6-24-56\n6-24-57\n6-25-56+\n7-50-112\n7-50-113\n7-51-112\n7-51-113\n6-25-57\n5-12-29\n5-13-28+\n6-26-56\n6-26-57+\n7-52-114\n7-52-115\n7-53-114\n7-53-115+\n8-106-230\n8-106-231\n8-107-230\n8-107-231\n6-27-56\n6-27-57\n5-13-29\n4-6-15\n4-7-14+\n5-14-28+\n6-28-56\n6-28-57+\n7-56-114+\n8-112-228\n8-112-229\n8-113-228\n8-113-229\n7-56-115\n7-57-114\n7-57-115+\n8-114-230+\n9-228-460\n9-228-461\n9-229-460\n9-229-461\n8-114-231+\n9-228-462\n9-228-463\n9-229-462\n9-229-463\n8-115-230\n8-115-231+\n9-230-462+\n10-460-924\n10-460-925\n10-461-924\n10-461-925\n9-230-463\n9-231-462\n9-231-463\n6-29-56\n6-29-57+\n7-58-114\n7-58-115\n7-59-114\n7-59-115\n5-14-29\n5-15-28+\n6-30-56+\n7-60-112\n7-60-113\n7-61-112+\n8-122-224\n8-122-225\n8-123-224\n8-123-225\n7-61-113\n6-30-57+\n7-60-114\n7-60-115+\n8-120-230\n8-120-231\n8-121-230\n8-121-231\n7-61-114\n7-61-115\n6-31-56+\n7-62-112\n7-62-113+\n8-124-226\n8-124-227\n8-125-226\n8-125-227\n7-63-112\n7-63-113\n6-31-57+\n7-62-114\n7-62-115+\n8-124-230\n8-124-231\n8-125-230\n8-125-231\n7-63-114\n7-63-115+\n8-126-230\n8-126-231\n8-127-230\n8-127-231\n5-15-29\n4-7-15+\n5-14-30+\n6-28-60+\n7-56-120+\n8-112-240\n8-112-241+\n9-224-482\n9-224-483\n9-225-482\n9-225-483\n8-113-240\n8-113-241\n7-56-121\n7-57-120\n7-57-121\n6-28-61+\n7-56-122\n7-56-123+\n8-112-246\n8-112-247\n8-113-246\n8-113-247\n7-57-122\n7-57-123+\n8-114-246\n8-114-247\n8-115-246\n8-115-247\n6-29-60\n6-29-61\n5-14-31\n5-15-30+\n6-30-60+\n7-60-120+\n8-120-240\n8-120-241\n8-121-240\n8-121-241\n7-60-121\n7-61-120\n7-61-121\n6-30-61\n6-31-60\n6-31-61\n5-15-31\n1-1-0+\n2-2-0+\n3-4-0\n3-4-1\n3-5-0+\n4-10-0\n4-10-1+\n5-20-2+\n6-40-4\n6-40-5+\n7-80-10+\n8-160-20\n8-160-21+\n9-320-42\n9-320-43\n9-321-42\n9-321-43+\n10-642-86\n10-642-87\n10-643-86\n10-643-87\n8-161-20\n8-161-21\n7-80-11\n7-81-10\n7-81-11\n6-41-4\n6-41-5\n5-20-3\n5-21-2\n5-21-3+\n6-42-6\n6-42-7\n6-43-6+\n7-86-12\n7-86-13\n7-87-12\n7-87-13\n6-43-7\n4-11-0+\n5-22-0\n5-22-1\n5-23-0\n5-23-1+\n6-46-2\n6-46-3\n6-47-2\n6-47-3+\n7-94-6+\n8-188-12+\n9-376-24\n9-376-25\n9-377-24\n9-377-25\n8-188-13+\n9-376-26\n9-376-27\n9-377-26\n9-377-27\n8-189-12\n8-189-13\n7-94-7+\n8-188-14\n8-188-15\n8-189-14\n8-189-15\n7-95-6\n7-95-7+\n8-190-14\n8-190-15\n8-191-14+\n9-382-28\n9-382-29\n9-383-28+\n10-766-56\n10-766-57\n10-767-56\n10-767-57\n9-383-29\n8-191-15\n4-11-1+\n5-22-2\n5-22-3+\n6-44-6+\n7-88-12\n7-88-13\n7-89-12+\n8-178-24\n8-178-25\n8-179-24+\n9-358-48\n9-358-49\n9-359-48\n9-359-49+\n10-718-98\n10-718-99\n10-719-98\n10-719-99\n8-179-25\n7-89-13+\n8-178-26+\n9-356-52\n9-356-53\n9-357-52\n9-357-53+\n10-714-106\n10-714-107\n10-715-106\n10-715-107\n8-178-27\n8-179-26\n8-179-27+\n9-358-54\n9-358-55\n9-359-54\n9-359-55\n6-44-7\n6-45-6\n6-45-7\n5-23-2\n5-23-3\n3-5-1+\n4-10-2+\n5-20-4+\n6-40-8\n6-40-9\n6-41-8+\n7-82-16\n7-82-17\n7-83-16\n7-83-17\n6-41-9\n5-20-5+\n6-40-10+\n7-80-20+\n8-160-40\n8-160-41\n8-161-40+\n9-322-80\n9-322-81\n9-323-80\n9-323-81\n8-161-41\n7-80-21\n7-81-20\n7-81-21\n6-40-11\n6-41-10+\n7-82-20\n7-82-21+\n8-164-42+\n9-328-84\n9-328-85\n9-329-84\n9-329-85\n8-164-43\n8-165-42\n8-165-43\n7-83-20\n7-83-21\n6-41-11\n5-21-4+\n6-42-8+\n7-84-16\n7-84-17\n7-85-16\n7-85-17\n6-42-9\n6-43-8\n6-43-9+\n7-86-18\n7-86-19\n7-87-18\n7-87-19\n5-21-5+\n6-42-10+\n7-84-20\n7-84-21\n7-85-20\n7-85-21+\n8-170-42\n8-170-43\n8-171-42\n8-171-43\n6-42-11\n6-43-10+\n7-86-20\n7-86-21+\n8-172-42\n8-172-43\n8-173-42\n8-173-43\n7-87-20\n7-87-21+\n8-174-42\n8-174-43\n8-175-42\n8-175-43\n6-43-11\n4-10-3\n4-11-2\n4-11-3\n2-2-1+\n3-4-2+\n4-8-4+\n5-16-8\n5-16-9+\n6-32-18+\n7-64-36\n7-64-37\n7-65-36+\n8-130-72\n8-130-73\n8-131-72\n8-131-73\n7-65-37+\n8-130-74\n8-130-75\n8-131-74\n8-131-75\n6-32-19+\n7-64-38\n7-64-39+\n8-128-78\n8-128-79\n8-129-78\n8-129-79\n7-65-38\n7-65-39+\n8-130-78\n8-130-79\n8-131-78+\n9-262-156\n9-262-157\n9-263-156\n9-263-157\n8-131-79+\n9-262-158\n9-262-159\n9-263-158\n9-263-159\n6-33-18+\n7-66-36\n7-66-37\n7-67-36\n7-67-37\n6-33-19+\n7-66-38\n7-66-39\n7-67-38\n7-67-39+\n8-134-78\n8-134-79\n8-135-78\n8-135-79+\n9-270-158\n9-270-159\n9-271-158\n9-271-159\n5-17-8+\n6-34-16\n6-34-17\n6-35-16+\n7-70-32+\n8-140-64\n8-140-65\n8-141-64\n8-141-65\n7-70-33\n7-71-32\n7-71-33+\n8-142-66+\n9-284-132\n9-284-133\n9-285-132\n9-285-133\n8-142-67+\n9-284-134\n9-284-135\n9-285-134\n9-285-135\n8-143-66\n8-143-67\n6-35-17\n5-17-9\n4-8-5+\n5-16-10+\n6-32-20+\n7-64-40+\n8-128-80\n8-128-81\n8-129-80\n8-129-81\n7-64-41\n7-65-40\n7-65-41\n6-32-21\n6-33-20\n6-33-21\n5-16-11+\n6-32-22\n6-32-23+\n7-64-46\n7-64-47\n7-65-46\n7-65-47+\n8-130-94\n8-130-95\n8-131-94\n8-131-95\n6-33-22\n6-33-23\n5-17-10+\n6-34-20\n6-34-21+\n7-68-42\n7-68-43\n7-69-42+\n8-138-84\n8-138-85\n8-139-84\n8-139-85\n7-69-43\n6-35-20\n6-35-21\n5-17-11+\n6-34-22\n6-34-23\n6-35-22+\n7-70-44\n7-70-45\n7-71-44+\n8-142-88\n8-142-89\n8-143-88+\n9-286-176\n9-286-177\n9-287-176\n9-287-177\n8-143-89\n7-71-45\n6-35-23\n4-9-4+\n5-18-8\n5-18-9\n5-19-8\n5-19-9+\n6-38-18\n6-38-19\n6-39-18+\n7-78-36+\n8-156-72\n8-156-73\n8-157-72\n8-157-73\n7-78-37\n7-79-36\n7-79-37+\n8-158-74\n8-158-75\n8-159-74\n8-159-75\n6-39-19\n4-9-5+\n5-18-10+\n6-36-20\n6-36-21\n6-37-20\n6-37-21\n5-18-11+\n6-36-22\n6-36-23\n6-37-22\n6-37-23+\n7-74-46\n7-74-47\n7-75-46\n7-75-47\n5-19-10+\n6-38-20+\n7-76-40\n7-76-41\n7-77-40+\n8-154-80\n8-154-81+\n9-308-162\n9-308-163\n9-309-162+\n10-618-324\n10-618-325\n10-619-324\n10-619-325\n9-309-163\n8-155-80\n8-155-81\n7-77-41+\n8-154-82\n8-154-83\n8-155-82\n8-155-83\n6-38-21+\n7-76-42\n7-76-43+\n8-152-86\n8-152-87\n8-153-86\n8-153-87+\n9-306-174\n9-306-175\n9-307-174\n9-307-175\n7-77-42\n7-77-43\n6-39-20+\n7-78-40\n7-78-41\n7-79-40\n7-79-41\n6-39-21\n5-19-11+\n6-38-22+\n7-76-44+\n8-152-88\n8-152-89\n8-153-88\n8-153-89+\n9-306-178\n9-306-179\n9-307-178\n9-307-179\n7-76-45\n7-77-44+\n8-154-88\n8-154-89\n8-155-88\n8-155-89+\n9-310-178\n9-310-179\n9-311-178+\n10-622-356\n10-622-357\n10-623-356\n10-623-357\n9-311-179+\n10-622-358\n10-622-359\n10-623-358\n10-623-359\n7-77-45\n6-38-23\n6-39-22\n6-39-23+\n7-78-46\n7-78-47\n7-79-46+\n8-158-92\n8-158-93\n8-159-92\n8-159-93\n7-79-47\n3-4-3\n3-5-2+\n4-10-4+\n5-20-8+\n6-40-16\n6-40-17+\n7-80-34+\n8-160-68\n8-160-69\n8-161-68\n8-161-69\n7-80-35+\n8-160-70\n8-160-71\n8-161-70\n8-161-71\n7-81-34\n7-81-35\n6-41-16\n6-41-17\n5-20-9+\n6-40-18\n6-40-19+\n7-80-38\n7-80-39\n7-81-38+\n8-162-76\n8-162-77\n8-163-76\n8-163-77+\n9-326-154\n9-326-155\n9-327-154\n9-327-155\n7-81-39\n6-41-18\n6-41-19+\n7-82-38+\n8-164-76\n8-164-77\n8-165-76\n8-165-77\n7-82-39+\n8-164-78\n8-164-79\n8-165-78\n8-165-79\n7-83-38+\n8-166-76\n8-166-77\n8-167-76+\n9-334-152\n9-334-153\n9-335-152\n9-335-153\n8-167-77\n7-83-39+\n8-166-78\n8-166-79\n8-167-78\n8-167-79\n5-21-8+\n6-42-16\n6-42-17+\n7-84-34\n7-84-35\n7-85-34+\n8-170-68\n8-170-69\n8-171-68\n8-171-69\n7-85-35\n6-43-16\n6-43-17\n5-21-9+\n6-42-18+\n7-84-36\n7-84-37\n7-85-36+\n8-170-72\n8-170-73\n8-171-72\n8-171-73\n7-85-37\n6-42-19+\n7-84-38\n7-84-39+\n8-168-78\n8-168-79\n8-169-78+\n9-338-156\n9-338-157\n9-339-156\n9-339-157+\n10-678-314\n10-678-315\n10-679-314\n10-679-315\n8-169-79\n7-85-38\n7-85-39\n6-43-18+\n7-86-36\n7-86-37+\n8-172-74+\n9-344-148\n9-344-149\n9-345-148\n9-345-149\n8-172-75\n8-173-74\n8-173-75\n7-87-36+\n8-174-72\n8-174-73\n8-175-72+\n9-350-144\n9-350-145\n9-351-144\n9-351-145\n8-175-73\n7-87-37\n6-43-19\n4-10-5+\n5-20-10\n5-20-11\n5-21-10\n5-21-11+\n6-42-22\n6-42-23+\n7-84-46\n7-84-47\n7-85-46\n7-85-47\n6-43-22+\n7-86-44\n7-86-45\n7-87-44\n7-87-45\n6-43-23\n4-11-4+\n5-22-8+\n6-44-16\n6-44-17+\n7-88-34\n7-88-35\n7-89-34\n7-89-35\n6-45-16\n6-45-17\n5-22-9\n5-23-8+\n6-46-16+\n7-92-32\n7-92-33\n7-93-32\n7-93-33+\n8-186-66\n8-186-67\n8-187-66\n8-187-67\n6-46-17+\n7-92-34+\n8-184-68\n8-184-69\n8-185-68\n8-185-69\n7-92-35+\n8-184-70\n8-184-71\n8-185-70\n8-185-71\n7-93-34\n7-93-35+\n8-186-70+\n9-372-140\n9-372-141\n9-373-140\n9-373-141\n8-186-71\n8-187-70\n8-187-71\n6-47-16\n6-47-17+\n7-94-34\n7-94-35\n7-95-34+\n8-190-68\n8-190-69\n8-191-68\n8-191-69+\n9-382-138\n9-382-139\n9-383-138\n9-383-139\n7-95-35+\n8-190-70\n8-190-71+\n9-380-142+\n10-760-284\n10-760-285\n10-761-284\n10-761-285\n9-380-143+\n10-760-286\n10-760-287\n10-761-286\n10-761-287\n9-381-142\n9-381-143\n8-191-70\n8-191-71\n5-23-9\n4-11-5+\n5-22-10\n5-22-11\n5-23-10+\n6-46-20+\n7-92-40+\n8-184-80\n8-184-81+\n9-368-162\n9-368-163\n9-369-162\n9-369-163+\n10-738-326\n10-738-327\n10-739-326\n10-739-327\n8-185-80\n8-185-81\n7-92-41\n7-93-40\n7-93-41\n6-46-21+\n7-92-42\n7-92-43\n7-93-42+\n8-186-84\n8-186-85\n8-187-84\n8-187-85\n7-93-43+\n8-186-86\n8-186-87\n8-187-86\n8-187-87\n6-47-20\n6-47-21+\n7-94-42+\n8-188-84\n8-188-85+\n9-376-170\n9-376-171\n9-377-170\n9-377-171\n8-189-84\n8-189-85\n7-94-43+\n8-188-86+\n9-376-172\n9-376-173\n9-377-172\n9-377-173\n8-188-87\n8-189-86\n8-189-87\n7-95-42\n7-95-43\n5-23-11\n3-5-3+\n4-10-6\n4-10-7\n4-11-6+\n5-22-12\n5-22-13\n5-23-12+\n6-46-24+\n7-92-48\n7-92-49\n7-93-48\n7-93-49+\n8-186-98\n8-186-99\n8-187-98\n8-187-99\n6-46-25+\n7-92-50\n7-92-51\n7-93-50+\n8-186-100\n8-186-101\n8-187-100\n8-187-101\n7-93-51\n6-47-24\n6-47-25\n5-23-13\n4-11-7\n2-3-0+\n3-6-0\n3-6-1+\n4-12-2\n4-12-3+\n5-24-6\n5-24-7\n5-25-6+\n6-50-12\n6-50-13\n6-51-12+\n7-102-24+\n8-204-48\n8-204-49\n8-205-48+\n9-410-96\n9-410-97\n9-411-96\n9-411-97\n8-205-49\n7-102-25\n7-103-24\n7-103-25+\n8-206-50\n8-206-51\n8-207-50\n8-207-51\n6-51-13+\n7-102-26\n7-102-27\n7-103-26\n7-103-27+\n8-206-54\n8-206-55\n8-207-54\n8-207-55\n5-25-7\n4-13-2+\n5-26-4\n5-26-5+\n6-52-10+\n7-104-20\n7-104-21\n7-105-20\n7-105-21\n6-52-11+\n7-104-22\n7-104-23+\n8-208-46+\n9-416-92\n9-416-93\n9-417-92\n9-417-93\n8-208-47\n8-209-46\n8-209-47\n7-105-22+\n8-210-44\n8-210-45+\n9-420-90+\n10-840-180\n10-840-181\n10-841-180\n10-841-181\n9-420-91\n9-421-90\n9-421-91\n8-211-44\n8-211-45\n7-105-23\n6-53-10\n6-53-11\n5-27-4+\n6-54-8\n6-54-9\n6-55-8+\n7-110-16\n7-110-17+\n8-220-34\n8-220-35\n8-221-34\n8-221-35+\n9-442-70\n9-442-71\n9-443-70+\n10-886-140\n10-886-141\n10-887-140\n10-887-141\n9-443-71\n7-111-16+\n8-222-32+\n9-444-64\n9-444-65\n9-445-64\n9-445-65\n8-222-33+\n9-444-66\n9-444-67\n9-445-66\n9-445-67+\n10-890-134\n10-890-135\n10-891-134\n10-891-135\n8-223-32\n8-223-33\n7-111-17\n6-55-9+\n7-110-18\n7-110-19\n7-111-18\n7-111-19\n5-27-5\n4-13-3\n3-7-0+\n4-14-0+\n5-28-0\n5-28-1+\n6-56-2+\n7-112-4\n7-112-5+\n8-224-10\n8-224-11+\n9-448-22\n9-448-23\n9-449-22\n9-449-23\n8-225-10\n8-225-11\n7-113-4\n7-113-5+\n8-226-10\n8-226-11\n8-227-10\n8-227-11+\n9-454-22\n9-454-23\n9-455-22\n9-455-23\n6-56-3+\n7-112-6\n7-112-7+\n8-224-14\n8-224-15\n8-225-14\n8-225-15+\n9-450-30\n9-450-31\n9-451-30\n9-451-31\n7-113-6\n7-113-7+\n8-226-14\n8-226-15+\n9-452-30\n9-452-31\n9-453-30\n9-453-31\n8-227-14+\n9-454-28\n9-454-29\n9-455-28\n9-455-29\n8-227-15\n6-57-2\n6-57-3\n5-29-0\n5-29-1\n4-14-1+\n5-28-2+\n6-56-4\n6-56-5+\n7-112-10\n7-112-11\n7-113-10\n7-113-11+\n8-226-22\n8-226-23\n8-227-22\n8-227-23\n6-57-4\n6-57-5\n5-28-3\n5-29-2+\n6-58-4+\n7-116-8+\n8-232-16\n8-232-17\n8-233-16\n8-233-17\n7-116-9\n7-117-8\n7-117-9\n6-58-5+\n7-116-10\n7-116-11\n7-117-10\n7-117-11+\n8-234-22\n8-234-23\n8-235-22\n8-235-23\n6-59-4\n6-59-5\n5-29-3\n4-15-0+\n5-30-0\n5-30-1\n5-31-0\n5-31-1\n4-15-1+\n5-30-2\n5-30-3+\n6-60-6\n6-60-7\n6-61-6\n6-61-7\n5-31-2\n5-31-3+\n6-62-6+\n7-124-12\n7-124-13\n7-125-12\n7-125-13\n6-62-7\n6-63-6+\n7-126-12\n7-126-13\n7-127-12+\n8-254-24\n8-254-25\n8-255-24\n8-255-25+\n9-510-50\n9-510-51\n9-511-50\n9-511-51\n7-127-13\n6-63-7\n3-7-1\n2-3-1+\n3-6-2\n3-6-3\n3-7-2+\n4-14-4+\n5-28-8+\n6-56-16\n6-56-17\n6-57-16\n6-57-17+\n7-114-34\n7-114-35\n7-115-34\n7-115-35\n5-28-9+\n6-56-18\n6-56-19\n6-57-18\n6-57-19\n5-29-8+\n6-58-16\n6-58-17\n6-59-16+\n7-118-32\n7-118-33\n7-119-32+\n8-238-64\n8-238-65+\n9-476-130\n9-476-131+\n10-952-262\n10-952-263\n10-953-262\n10-953-263\n9-477-130\n9-477-131\n8-239-64\n8-239-65\n7-119-33\n6-59-17\n5-29-9+\n6-58-18+\n7-116-36\n7-116-37+\n8-232-74\n8-232-75\n8-233-74\n8-233-75\n7-117-36\n7-117-37+\n8-234-74\n8-234-75\n8-235-74\n8-235-75\n6-58-19+\n7-116-38\n7-116-39\n7-117-38+\n8-234-76\n8-234-77\n8-235-76\n8-235-77+\n9-470-154+\n10-940-308\n10-940-309\n10-941-308\n10-941-309\n9-470-155\n9-471-154\n9-471-155\n7-117-39+\n8-234-78\n8-234-79+\n9-468-158\n9-468-159\n9-469-158\n9-469-159\n8-235-78\n8-235-79\n6-59-18\n6-59-19+\n7-118-38\n7-118-39+\n8-236-78\n8-236-79+\n9-472-158+\n10-944-316\n10-944-317\n10-945-316\n10-945-317\n9-472-159\n9-473-158\n9-473-159\n8-237-78\n8-237-79\n7-119-38\n7-119-39+\n8-238-78\n8-238-79\n8-239-78\n8-239-79+\n9-478-158\n9-478-159\n9-479-158\n9-479-159\n4-14-5\n4-15-4+\n5-30-8+\n6-60-16\n6-60-17\n6-61-16+\n7-122-32\n7-122-33+\n8-244-66\n8-244-67+\n9-488-134\n9-488-135+\n10-976-270\n10-976-271\n10-977-270\n10-977-271\n9-489-134\n9-489-135\n8-245-66+\n9-490-132\n9-490-133\n9-491-132\n9-491-133\n8-245-67\n7-123-32\n7-123-33\n6-61-17\n5-30-9\n5-31-8+\n6-62-16+\n7-124-32+\n8-248-64\n8-248-65\n8-249-64\n8-249-65\n7-124-33\n7-125-32\n7-125-33\n6-62-17\n6-63-16\n6-63-17\n5-31-9+\n6-62-18\n6-62-19\n6-63-18+\n7-126-36+\n8-252-72+\n9-504-144\n9-504-145\n9-505-144\n9-505-145\n8-252-73\n8-253-72\n8-253-73\n7-126-37+\n8-252-74\n8-252-75\n8-253-74\n8-253-75\n7-127-36\n7-127-37\n6-63-19+\n7-126-38+\n8-252-76\n8-252-77\n8-253-76\n8-253-77\n7-126-39\n7-127-38\n7-127-39\n4-15-5\n3-7-3+\n4-14-6\n4-14-7\n4-15-6\n4-15-7+\n5-30-14+\n6-60-28+\n7-120-56\n7-120-57\n7-121-56\n7-121-57\n6-60-29\n6-61-28+\n7-122-56\n7-122-57\n7-123-56\n7-123-57+\n8-246-114\n8-246-115\n8-247-114\n8-247-115\n6-61-29\n5-30-15\n5-31-14\n5-31-15\n1-1-1+\n2-2-2+\n3-4-4+\n4-8-8+\n5-16-16\n5-16-17\n5-17-16\n5-17-17\n4-8-9+\n5-16-18+\n6-32-36\n6-32-37+\n7-64-74\n7-64-75+\n8-128-150\n8-128-151\n8-129-150\n8-129-151+\n9-258-302\n9-258-303\n9-259-302\n9-259-303\n7-65-74\n7-65-75+\n8-130-150\n8-130-151\n8-131-150\n8-131-151\n6-33-36+\n7-66-72\n7-66-73\n7-67-72\n7-67-73\n6-33-37\n5-16-19\n5-17-18\n5-17-19\n4-9-8+\n5-18-16\n5-18-17+\n6-36-34+\n7-72-68\n7-72-69\n7-73-68+\n8-146-136\n8-146-137\n8-147-136\n8-147-137\n7-73-69\n6-36-35+\n7-72-70+\n8-144-140\n8-144-141+\n9-288-282\n9-288-283\n9-289-282\n9-289-283\n8-145-140\n8-145-141+\n9-290-282\n9-290-283\n9-291-282\n9-291-283\n7-72-71+\n8-144-142+\n9-288-284\n9-288-285\n9-289-284\n9-289-285\n8-144-143\n8-145-142+\n9-290-284\n9-290-285\n9-291-284\n9-291-285\n8-145-143\n7-73-70\n7-73-71+\n8-146-142\n8-146-143+\n9-292-286\n9-292-287\n9-293-286\n9-293-287\n8-147-142\n8-147-143\n6-37-34\n6-37-35\n5-19-16\n5-19-17\n4-9-9+\n5-18-18+\n6-36-36+\n7-72-72\n7-72-73\n7-73-72\n7-73-73\n6-36-37\n6-37-36\n6-37-37+\n7-74-74+\n8-148-148+\n9-296-296+\n10-592-592\n10-592-593\n10-593-592\n10-593-593\n9-296-297\n9-297-296\n9-297-297\n8-148-149\n8-149-148\n8-149-149\n7-74-75\n7-75-74+\n8-150-148\n8-150-149+\n9-300-298\n9-300-299\n9-301-298\n9-301-299\n8-151-148\n8-151-149\n7-75-75\n5-18-19\n5-19-18\n5-19-19\n3-4-5+\n4-8-10+\n5-16-20\n5-16-21+\n6-32-42\n6-32-43+\n7-64-86+\n8-128-172\n8-128-173\n8-129-172\n8-129-173\n7-64-87\n7-65-86\n7-65-87\n6-33-42\n6-33-43+\n7-66-86\n7-66-87\n7-67-86\n7-67-87+\n8-134-174+\n9-268-348\n9-268-349+\n10-536-698\n10-536-699\n10-537-698\n10-537-699\n9-269-348\n9-269-349\n8-134-175+\n9-268-350\n9-268-351\n9-269-350\n9-269-351\n8-135-174\n8-135-175\n5-17-20\n5-17-21\n4-8-11\n4-9-10+\n5-18-20\n5-18-21+\n6-36-42+\n7-72-84\n7-72-85+\n8-144-170\n8-144-171\n8-145-170\n8-145-171\n7-73-84+\n8-146-168\n8-146-169\n8-147-168\n8-147-169\n7-73-85\n6-36-43\n6-37-42+\n7-74-84+\n8-148-168\n8-148-169\n8-149-168\n8-149-169+\n9-298-338\n9-298-339\n9-299-338+\n10-598-676\n10-598-677\n10-599-676\n10-599-677\n9-299-339\n7-74-85\n7-75-84+\n8-150-168\n8-150-169\n8-151-168\n8-151-169\n7-75-85\n6-37-43+\n7-74-86\n7-74-87\n7-75-86\n7-75-87\n5-19-20\n5-19-21+\n6-38-42\n6-38-43\n6-39-42\n6-39-43+\n7-78-86\n7-78-87+\n8-156-174\n8-156-175+\n9-312-350\n9-312-351\n9-313-350\n9-313-351\n8-157-174\n8-157-175\n7-79-86\n7-79-87\n4-9-11\n3-5-4+\n4-10-8+\n5-20-16\n5-20-17+\n6-40-34+\n7-80-68\n7-80-69+\n8-160-138+\n9-320-276\n9-320-277\n9-321-276\n9-321-277\n8-160-139+\n9-320-278\n9-320-279\n9-321-278\n9-321-279\n8-161-138\n8-161-139\n7-81-68\n7-81-69\n6-40-35\n6-41-34+\n7-82-68\n7-82-69\n7-83-68\n7-83-69\n6-41-35\n5-21-16+\n6-42-32\n6-42-33+\n7-84-66\n7-84-67\n7-85-66+\n8-170-132\n8-170-133\n8-171-132\n8-171-133\n7-85-67\n6-43-32+\n7-86-64\n7-86-65\n7-87-64\n7-87-65\n6-43-33\n5-21-17\n4-10-9+\n5-20-18+\n6-40-36\n6-40-37\n6-41-36\n6-41-37\n5-20-19\n5-21-18+\n6-42-36\n6-42-37\n6-43-36\n6-43-37\n5-21-19+\n6-42-38\n6-42-39\n6-43-38\n6-43-39\n4-11-8\n4-11-9\n3-5-5+\n4-10-10\n4-10-11\n4-11-10+\n5-22-20\n5-22-21+\n6-44-42\n6-44-43\n6-45-42\n6-45-43\n5-23-20+\n6-46-40+\n7-92-80\n7-92-81\n7-93-80\n7-93-81\n6-46-41\n6-47-40+\n7-94-80+\n8-188-160\n8-188-161\n8-189-160\n8-189-161\n7-94-81\n7-95-80\n7-95-81\n6-47-41\n5-23-21\n4-11-11\n2-2-3+\n3-4-6\n3-4-7+\n4-8-14\n4-8-15+\n5-16-30+\n6-32-60\n6-32-61+\n7-64-122\n7-64-123\n7-65-122\n7-65-123+\n8-130-246\n8-130-247\n8-131-246\n8-131-247\n6-33-60\n6-33-61\n5-16-31+\n6-32-62\n6-32-63\n6-33-62+\n7-66-124\n7-66-125+\n8-132-250\n8-132-251\n8-133-250\n8-133-251\n7-67-124+\n8-134-248\n8-134-249\n8-135-248\n8-135-249\n7-67-125+\n8-134-250\n8-134-251\n8-135-250\n8-135-251\n6-33-63+\n7-66-126+\n8-132-252\n8-132-253\n8-133-252\n8-133-253\n7-66-127\n7-67-126\n7-67-127\n5-17-30\n5-17-31+\n6-34-62\n6-34-63+\n7-68-126\n7-68-127\n7-69-126\n7-69-127\n6-35-62\n6-35-63\n4-9-14+\n5-18-28\n5-18-29\n5-19-28+\n6-38-56\n6-38-57\n6-39-56+\n7-78-112\n7-78-113+\n8-156-226\n8-156-227\n8-157-226\n8-157-227\n7-79-112\n7-79-113\n6-39-57\n5-19-29+\n6-38-58\n6-38-59\n6-39-58\n6-39-59+\n7-78-118\n7-78-119\n7-79-118\n7-79-119\n4-9-15\n3-5-6\n3-5-7+\n4-10-14+\n5-20-28+\n6-40-56\n6-40-57\n6-41-56\n6-41-57\n5-20-29\n5-21-28+\n6-42-56+\n7-84-112\n7-84-113\n7-85-112\n7-85-113\n6-42-57\n6-43-56\n6-43-57+\n7-86-114+\n8-172-228\n8-172-229\n8-173-228\n8-173-229\n7-86-115\n7-87-114\n7-87-115\n5-21-29\n4-10-15\n4-11-14+\n5-22-28+\n6-44-56\n6-44-57+\n7-88-114+\n8-176-228\n8-176-229\n8-177-228\n8-177-229\n7-88-115\n7-89-114\n7-89-115\n6-45-56\n6-45-57\n5-22-29+\n6-44-58\n6-44-59\n6-45-58\n6-45-59\n5-23-28\n5-23-29\n4-11-15+\n5-22-30\n5-22-31+\n6-44-62\n6-44-63\n6-45-62\n6-45-63+\n7-90-126\n7-90-127+\n8-180-254\n8-180-255\n8-181-254\n8-181-255\n7-91-126\n7-91-127\n5-23-30+\n6-46-60\n6-46-61\n6-47-60\n6-47-61\n5-23-31\n2-3-2+\n3-6-4+\n4-12-8+\n5-24-16+\n6-48-32\n6-48-33\n6-49-32\n6-49-33\n5-24-17+\n6-48-34+\n7-96-68\n7-96-69\n7-97-68+\n8-194-136\n8-194-137\n8-195-136\n8-195-137+\n9-390-274\n9-390-275\n9-391-274\n9-391-275\n7-97-69+\n8-194-138\n8-194-139+\n9-388-278+\n10-776-556\n10-776-557\n10-777-556\n10-777-557\n9-388-279\n9-389-278+\n10-778-556\n10-778-557\n10-779-556\n10-779-557\n9-389-279\n8-195-138+\n9-390-276\n9-390-277\n9-391-276\n9-391-277\n8-195-139\n6-48-35\n6-49-34+\n7-98-68\n7-98-69+\n8-196-138\n8-196-139\n8-197-138\n8-197-139\n7-99-68\n7-99-69\n6-49-35+\n7-98-70+\n8-196-140\n8-196-141\n8-197-140\n8-197-141\n7-98-71\n7-99-70\n7-99-71+\n8-198-142\n8-198-143\n8-199-142\n8-199-143+\n9-398-286\n9-398-287\n9-399-286+\n10-798-572\n10-798-573\n10-799-572\n10-799-573\n9-399-287\n5-25-16\n5-25-17+\n6-50-34\n6-50-35\n6-51-34\n6-51-35\n4-12-9\n4-13-8+\n5-26-16\n5-26-17\n5-27-16+\n6-54-32\n6-54-33\n6-55-32+\n7-110-64\n7-110-65\n7-111-64+\n8-222-128\n8-222-129\n8-223-128+\n9-446-256\n9-446-257\n9-447-256\n9-447-257\n8-223-129\n7-111-65\n6-55-33\n5-27-17\n4-13-9+\n5-26-18+\n6-52-36\n6-52-37+\n7-104-74+\n8-208-148\n8-208-149\n8-209-148\n8-209-149\n7-104-75+\n8-208-150\n8-208-151\n8-209-150\n8-209-151+\n9-418-302\n9-418-303\n9-419-302\n9-419-303\n7-105-74\n7-105-75\n6-53-36\n6-53-37+\n7-106-74\n7-106-75+\n8-212-150\n8-212-151\n8-213-150\n8-213-151\n7-107-74\n7-107-75\n5-26-19+\n6-52-38+\n7-104-76\n7-104-77\n7-105-76+\n8-210-152\n8-210-153+\n9-420-306\n9-420-307\n9-421-306\n9-421-307\n8-211-152\n8-211-153\n7-105-77\n6-52-39\n6-53-38+\n7-106-76+\n8-212-152\n8-212-153\n8-213-152\n8-213-153\n7-106-77\n7-107-76+\n8-214-152\n8-214-153\n8-215-152\n8-215-153+\n9-430-306\n9-430-307\n9-431-306\n9-431-307\n7-107-77\n6-53-39+\n7-106-78\n7-106-79\n7-107-78\n7-107-79+\n8-214-158\n8-214-159\n8-215-158\n8-215-159\n5-27-18\n5-27-19+\n6-54-38\n6-54-39\n6-55-38+\n7-110-76\n7-110-77\n7-111-76\n7-111-77\n6-55-39+\n7-110-78+\n8-220-156\n8-220-157\n8-221-156\n8-221-157\n7-110-79\n7-111-78\n7-111-79\n3-6-5+\n4-12-10+\n5-24-20+\n6-48-40+\n7-96-80\n7-96-81+\n8-192-162\n8-192-163+\n9-384-326\n9-384-327\n9-385-326\n9-385-327\n8-193-162\n8-193-163\n7-97-80\n7-97-81+\n8-194-162\n8-194-163\n8-195-162\n8-195-163\n6-48-41+\n7-96-82\n7-96-83+\n8-192-166+\n9-384-332\n9-384-333\n9-385-332\n9-385-333\n8-192-167\n8-193-166\n8-193-167\n7-97-82\n7-97-83\n6-49-40+\n7-98-80\n7-98-81\n7-99-80+\n8-198-160\n8-198-161\n8-199-160\n8-199-161\n7-99-81\n6-49-41\n5-24-21+\n6-48-42+\n7-96-84\n7-96-85+\n8-192-170\n8-192-171\n8-193-170+\n9-386-340\n9-386-341\n9-387-340\n9-387-341\n8-193-171\n7-97-84\n7-97-85+\n8-194-170\n8-194-171\n8-195-170+\n9-390-340\n9-390-341\n9-391-340\n9-391-341\n8-195-171+\n9-390-342\n9-390-343\n9-391-342\n9-391-343+\n10-782-686\n10-782-687\n10-783-686\n10-783-687\n6-48-43\n6-49-42\n6-49-43\n5-25-20+\n6-50-40\n6-50-41\n6-51-40\n6-51-41\n5-25-21+\n6-50-42\n6-50-43\n6-51-42+\n7-102-84+\n8-204-168+\n9-408-336\n9-408-337\n9-409-336+\n10-818-672\n10-818-673\n10-819-672\n10-819-673\n9-409-337\n8-204-169\n8-205-168\n8-205-169\n7-102-85\n7-103-84+\n8-206-168+\n9-412-336\n9-412-337\n9-413-336\n9-413-337\n8-206-169\n8-207-168\n8-207-169\n7-103-85\n6-51-43\n4-12-11+\n5-24-22+\n6-48-44\n6-48-45\n6-49-44\n6-49-45\n5-24-23\n5-25-22+\n6-50-44+\n7-100-88\n7-100-89\n7-101-88\n7-101-89\n6-50-45\n6-51-44+\n7-102-88\n7-102-89+\n8-204-178+\n9-408-356\n9-408-357+\n10-816-714\n10-816-715\n10-817-714\n10-817-715\n9-409-356\n9-409-357\n8-204-179\n8-205-178+\n9-410-356\n9-410-357\n9-411-356+\n10-822-712\n10-822-713\n10-823-712\n10-823-713\n9-411-357+\n10-822-714\n10-822-715\n10-823-714\n10-823-715\n8-205-179+\n9-410-358\n9-410-359\n9-411-358\n9-411-359\n7-103-88+\n8-206-176+\n9-412-352\n9-412-353\n9-413-352\n9-413-353\n8-206-177\n8-207-176\n8-207-177+\n9-414-354\n9-414-355\n9-415-354\n9-415-355\n7-103-89+\n8-206-178\n8-206-179\n8-207-178+\n9-414-356\n9-414-357+\n10-828-714\n10-828-715\n10-829-714\n10-829-715\n9-415-356\n9-415-357\n8-207-179\n6-51-45\n5-25-23\n4-13-10+\n5-26-20\n5-26-21+\n6-52-42+\n7-104-84\n7-104-85\n7-105-84\n7-105-85+\n8-210-170\n8-210-171\n8-211-170\n8-211-171\n6-52-43\n6-53-42\n6-53-43\n5-27-20\n5-27-21+\n6-54-42\n6-54-43+\n7-108-86+\n8-216-172\n8-216-173\n8-217-172+\n9-434-344+\n10-868-688\n10-868-689\n10-869-688\n10-869-689\n9-434-345\n9-435-344\n9-435-345\n8-217-173\n7-108-87\n7-109-86\n7-109-87+\n8-218-174\n8-218-175\n8-219-174\n8-219-175+\n9-438-350\n9-438-351\n9-439-350\n9-439-351\n6-55-42\n6-55-43\n4-13-11+\n5-26-22\n5-26-23\n5-27-22+\n6-54-44+\n7-108-88+\n8-216-176\n8-216-177\n8-217-176\n8-217-177\n7-108-89\n7-109-88\n7-109-89\n6-54-45\n6-55-44\n6-55-45+\n7-110-90\n7-110-91+\n8-220-182\n8-220-183\n8-221-182\n8-221-183\n7-111-90\n7-111-91\n5-27-23\n3-7-4\n3-7-5\n2-3-3+\n3-6-6\n3-6-7\n3-7-6\n3-7-7+\n4-14-14+\n5-28-28+\n6-56-56\n6-56-57+\n7-112-114\n7-112-115+\n8-224-230\n8-224-231+\n9-448-462\n9-448-463\n9-449-462\n9-449-463\n8-225-230\n8-225-231\n7-113-114\n7-113-115\n6-57-56+\n7-114-112\n7-114-113\n7-115-112\n7-115-113+\n8-230-226+\n9-460-452\n9-460-453\n9-461-452\n9-461-453\n8-230-227+\n9-460-454\n9-460-455\n9-461-454\n9-461-455+\n10-922-910\n10-922-911\n10-923-910\n10-923-911\n8-231-226\n8-231-227\n6-57-57\n5-28-29+\n6-56-58+\n7-112-116+\n8-224-232+\n9-448-464\n9-448-465\n9-449-464\n9-449-465\n8-224-233\n8-225-232\n8-225-233+\n9-450-466\n9-450-467\n9-451-466\n9-451-467\n7-112-117\n7-113-116\n7-113-117\n6-56-59+\n7-112-118\n7-112-119\n7-113-118\n7-113-119\n6-57-58+\n7-114-116\n7-114-117\n7-115-116\n7-115-117\n6-57-59\n5-29-28\n5-29-29\n4-14-15\n4-15-14+\n5-30-28+\n6-60-56\n6-60-57+\n7-120-114\n7-120-115\n7-121-114\n7-121-115\n6-61-56\n6-61-57\n5-30-29+\n6-60-58+\n7-120-116+\n8-240-232\n8-240-233\n8-241-232+\n9-482-464\n9-482-465\n9-483-464\n9-483-465\n8-241-233\n7-120-117\n7-121-116+\n8-242-232\n8-242-233\n8-243-232\n8-243-233\n7-121-117\n6-60-59\n6-61-58\n6-61-59+\n7-122-118\n7-122-119+\n8-244-238\n8-244-239+\n9-488-478\n9-488-479\n9-489-478\n9-489-479\n8-245-238\n8-245-239+\n9-490-478\n9-490-479\n9-491-478\n9-491-479\n7-123-118+\n8-246-236\n8-246-237+\n9-492-474\n9-492-475\n9-493-474\n9-493-475\n8-247-236\n8-247-237\n7-123-119\n5-31-28+\n6-62-56\n6-62-57\n6-63-56\n6-63-57+\n7-126-114+\n8-252-228\n8-252-229\n8-253-228\n8-253-229\n7-126-115+\n8-252-230+\n9-504-460\n9-504-461\n9-505-460\n9-505-461\n8-252-231+\n9-504-462\n9-504-463\n9-505-462\n9-505-463\n8-253-230\n8-253-231\n7-127-114\n7-127-115+\n8-254-230\n8-254-231\n8-255-230\n8-255-231\n5-31-29+\n6-62-58\n6-62-59\n6-63-58+\n7-126-116+\n8-252-232+\n9-504-464\n9-504-465\n9-505-464\n9-505-465\n8-252-233+\n9-504-466\n9-504-467\n9-505-466\n9-505-467\n8-253-232+\n9-506-464\n9-506-465\n9-507-464\n9-507-465\n8-253-233\n7-126-117\n7-127-116\n7-127-117\n6-63-59+\n7-126-118\n7-126-119\n7-127-118\n7-127-119\n4-15-15+\n5-30-30+\n6-60-60\n6-60-61\n6-61-60\n6-61-61+\n7-122-122\n7-122-123\n7-123-122\n7-123-123\n5-30-31\n5-31-30\n5-31-31+\n6-62-62+\n7-124-124\n7-124-125\n7-125-124+\n8-250-248+\n9-500-496\n9-500-497\n9-501-496\n9-501-497\n8-250-249\n8-251-248\n8-251-249\n7-125-125+\n8-250-250\n8-250-251\n8-251-250\n8-251-251\n6-62-63+\n7-124-126+\n8-248-252\n8-248-253\n8-249-252\n8-249-253\n7-124-127\n7-125-126\n7-125-127\n6-63-62\n6-63-63+\n7-126-126\n7-126-127\n7-127-126\n7-127-127\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/sharding/testDynamicShardingChildOrdering.txt",
    "content": "0-0-0+\n1-0-0+\n2-0-0+\n3-0-0+\n4-0-0+\n5-0-0\n5-0-1\n5-1-0\n5-1-1\n4-0-1\n4-1-0\n4-1-1\n3-0-1+\n4-0-2+\n5-0-4\n5-0-5\n5-1-4+\n6-2-8\n6-2-9\n6-3-8\n6-3-9+\n7-6-18\n7-6-19+\n8-12-38\n8-12-39+\n9-24-78\n9-24-79\n9-25-78\n9-25-79\n8-13-38\n8-13-39\n7-7-18\n7-7-19+\n8-14-38\n8-14-39\n8-15-38\n8-15-39+\n9-31-78\n9-30-78\n9-30-79\n9-31-79+\n10-62-158\n10-62-159\n10-63-158\n10-63-159\n5-1-5+\n6-2-10\n6-2-11\n6-3-10\n6-3-11\n4-0-3+\n5-0-6\n5-0-7\n5-1-6\n5-1-7\n4-1-2\n4-1-3+\n5-2-6\n5-2-7\n5-3-6\n5-3-7\n3-1-0\n3-1-1\n2-0-1+\n3-0-2+\n4-0-4\n4-0-5+\n5-0-10\n5-0-11+\n6-0-22\n6-0-23+\n7-0-46+\n8-0-92+\n9-0-184\n9-0-185\n9-1-184\n9-1-185\n8-0-93\n8-1-92\n8-1-93\n7-0-47\n7-1-46\n7-1-47\n6-1-22\n6-1-23+\n7-2-46\n7-2-47\n7-3-46\n7-3-47\n5-1-10\n5-1-11\n4-1-4+\n5-2-8+\n6-4-16\n6-4-17\n6-5-16\n6-5-17\n5-2-9\n5-3-8\n5-3-9\n4-1-5\n3-0-3+\n4-0-6\n4-0-7\n4-1-6\n4-1-7\n3-1-2\n3-1-3\n2-1-0+\n3-2-0\n3-2-1\n3-3-0+\n4-6-0+\n5-12-0\n5-12-1+\n6-24-2+\n7-48-4\n7-48-5+\n8-96-10+\n9-192-20\n9-192-21\n9-193-20\n9-193-21\n8-96-11\n8-97-10\n8-97-11\n7-49-4\n7-49-5\n6-24-3\n6-25-2\n6-25-3\n5-13-0+\n6-26-0\n6-26-1\n6-27-0\n6-27-1\n5-13-1\n4-6-1\n4-7-0\n4-7-1+\n5-14-2\n5-14-3\n5-15-2+\n6-30-4+\n7-60-8\n7-60-9+\n8-120-18\n8-120-19\n8-121-18\n8-121-19\n7-61-8\n7-61-9\n6-30-5\n6-31-4\n6-31-5\n5-15-3\n3-3-1+\n4-6-2\n4-6-3+\n5-12-6\n5-12-7+\n6-24-14\n6-24-15\n6-25-14\n6-25-15+\n7-50-30\n7-50-31\n7-51-30\n7-51-31+\n8-102-62+\n9-204-124\n9-204-125\n9-205-124\n9-205-125\n8-102-63\n8-103-62\n8-103-63\n5-13-6+\n6-26-12\n6-26-13+\n7-52-26\n7-52-27\n7-53-26\n7-53-27\n6-27-12+\n7-54-24\n7-54-25\n7-55-24\n7-55-25\n6-27-13+\n7-54-26\n7-54-27\n7-55-26\n7-55-27\n5-13-7\n4-7-2\n4-7-3+\n5-14-6+\n6-28-12+\n7-56-24\n7-56-25+\n8-112-50\n8-112-51\n8-113-50\n8-113-51+\n9-226-102\n9-226-103\n9-227-102\n9-227-103\n7-57-24\n7-57-25\n6-28-13\n6-29-12+\n7-58-24+\n8-116-48\n8-116-49+\n9-232-98\n9-232-99\n9-233-98\n9-233-99\n8-117-48\n8-117-49\n7-58-25\n7-59-24+\n8-118-48\n8-118-49\n8-119-48\n8-119-49\n7-59-25\n6-29-13+\n7-58-26\n7-58-27\n7-59-26\n7-59-27\n5-14-7\n5-15-6+\n6-30-12\n6-30-13+\n7-60-26+\n8-120-52\n8-120-53\n8-121-52\n8-121-53+\n9-242-106\n9-242-107\n9-243-106\n9-243-107\n7-60-27+\n8-120-54\n8-120-55\n8-121-54\n8-121-55+\n9-242-110\n9-242-111\n9-243-110\n9-243-111\n7-61-26+\n8-122-52\n8-122-53+\n9-244-106\n9-244-107\n9-245-106\n9-245-107\n8-123-52\n8-123-53\n7-61-27\n6-31-12\n6-31-13\n5-15-7+\n6-30-14+\n7-60-28\n7-60-29\n7-61-28\n7-61-29\n6-30-15+\n7-60-30\n7-60-31\n7-61-30\n7-61-31\n6-31-14+\n7-62-28+\n8-124-56\n8-124-57\n8-125-56+\n9-250-112\n9-250-113\n9-251-112\n9-251-113\n8-125-57\n7-62-29+\n8-124-58\n8-124-59\n8-125-58\n8-125-59\n7-63-28\n7-63-29+\n8-126-58\n8-126-59\n8-127-58\n8-127-59\n6-31-15+\n7-62-30\n7-62-31\n7-63-30\n7-63-31\n2-1-1+\n3-2-2+\n4-4-4+\n5-8-8+\n6-16-16+\n7-32-32+\n8-64-64\n8-64-65\n8-65-64\n8-65-65\n7-32-33+\n8-64-66\n8-64-67\n8-65-66\n8-65-67\n7-33-32\n7-33-33+\n8-66-66\n8-66-67\n8-67-66\n8-67-67+\n9-134-134\n9-134-135\n9-135-134\n9-135-135\n6-16-17\n6-17-16\n6-17-17\n5-8-9\n5-9-8+\n6-18-16\n6-18-17\n6-19-16\n6-19-17\n5-9-9\n4-4-5\n4-5-4\n4-5-5+\n5-10-10+\n6-20-20+\n7-40-40\n7-40-41+\n8-80-82\n8-80-83\n8-81-82+\n9-162-164\n9-162-165\n9-163-164\n9-163-165\n8-81-83\n7-41-40\n7-41-41\n6-20-21+\n7-40-42+\n8-80-84\n8-80-85+\n9-160-170\n9-160-171+\n10-320-342\n10-320-343\n10-321-342\n10-321-343\n9-161-170\n9-161-171\n8-81-84\n8-81-85\n7-40-43\n7-41-42\n7-41-43\n6-21-20\n6-21-21\n5-10-11+\n6-20-22+\n7-40-44+\n8-80-88\n8-80-89\n8-81-88+\n9-162-176\n9-162-177\n9-163-176\n9-163-177\n8-81-89\n7-40-45\n7-41-44+\n8-82-88\n8-82-89\n8-83-88\n8-83-89\n7-41-45\n6-20-23+\n7-40-46\n7-40-47\n7-41-46+\n8-82-92+\n9-164-184\n9-164-185\n9-165-184+\n10-330-368\n10-330-369\n10-331-368\n10-331-369\n9-165-185\n8-82-93+\n9-164-186\n9-164-187\n9-165-186+\n10-330-372\n10-330-373\n10-331-372\n10-331-373\n9-165-187\n8-83-92\n8-83-93+\n9-166-186\n9-166-187\n9-167-186+\n10-334-372\n10-334-373\n10-335-372\n10-335-373\n9-167-187\n7-41-47\n6-21-22\n6-21-23\n5-11-10\n5-11-11+\n6-22-22+\n7-44-44+\n8-88-88+\n9-176-176\n9-176-177\n9-177-176\n9-177-177\n8-88-89\n8-89-88\n8-89-89\n7-44-45\n7-45-44\n7-45-45\n6-22-23\n6-23-22\n6-23-23+\n7-46-46\n7-46-47\n7-47-46+\n8-94-92\n8-94-93\n8-95-92\n8-95-93+\n9-190-186\n9-190-187+\n10-380-374\n10-380-375\n10-381-374\n10-381-375\n9-191-186\n9-191-187\n7-47-47\n3-2-3+\n4-4-6+\n5-8-12+\n6-16-24\n6-16-25\n6-17-24\n6-17-25\n5-8-13\n5-9-12\n5-9-13+\n6-18-26\n6-18-27\n6-19-26\n6-19-27+\n7-38-54+\n8-76-108+\n9-152-216\n9-152-217\n9-153-216\n9-153-217\n8-76-109+\n9-152-218\n9-152-219\n9-153-218+\n10-306-436\n10-306-437\n10-307-436\n10-307-437\n9-153-219\n8-77-108\n8-77-109\n7-38-55+\n8-76-110\n8-76-111\n8-77-110\n8-77-111\n7-39-54+\n8-78-108\n8-78-109+\n9-156-218\n9-156-219\n9-157-218\n9-157-219\n8-79-108\n8-79-109+\n9-158-218\n9-158-219\n9-159-218\n9-159-219\n7-39-55\n4-4-7\n4-5-6+\n5-10-12+\n6-20-24+\n7-40-48\n7-40-49\n7-41-48+\n8-82-96\n8-82-97\n8-83-96+\n9-166-192\n9-166-193\n9-167-192\n9-167-193\n8-83-97\n7-41-49\n6-20-25\n6-21-24+\n7-42-48\n7-42-49\n7-43-48+\n8-86-96\n8-86-97\n8-87-96\n8-87-97\n7-43-49\n6-21-25\n5-10-13\n5-11-12\n5-11-13\n4-5-7\n3-3-2+\n4-6-4\n4-6-5\n4-7-4+\n5-14-8\n5-14-9\n5-15-8+\n6-30-16+\n7-60-32\n7-60-33\n7-61-32+\n8-122-64\n8-122-65\n8-123-64\n8-123-65\n7-61-33\n6-30-17\n6-31-16\n6-31-17\n5-15-9\n4-7-5\n3-3-3\n1-0-1+\n2-0-2+\n3-0-4\n3-0-5+\n4-0-10\n4-0-11\n4-1-10\n4-1-11\n3-1-4+\n4-2-8\n4-2-9+\n5-4-18+\n6-8-36+\n7-16-72\n7-16-73+\n8-32-146\n8-32-147+\n9-64-294\n9-64-295\n9-65-294\n9-65-295+\n10-130-590\n10-130-591\n10-131-590\n10-131-591\n8-33-146\n8-33-147\n7-17-72\n7-17-73+\n8-34-146\n8-34-147\n8-35-146\n8-35-147\n6-8-37\n6-9-36\n6-9-37+\n7-18-74\n7-18-75\n7-19-74\n7-19-75\n5-4-19\n5-5-18+\n6-10-36+\n7-20-72\n7-20-73+\n8-40-146\n8-40-147+\n9-80-294\n9-80-295\n9-81-294\n9-81-295\n8-41-146\n8-41-147\n7-21-72\n7-21-73\n6-10-37\n6-11-36+\n7-22-72+\n8-44-144\n8-44-145\n8-45-144\n8-45-145+\n9-90-290\n9-90-291\n9-91-290\n9-91-291+\n10-182-582\n10-182-583\n10-183-582\n10-183-583\n7-22-73\n7-23-72\n7-23-73+\n8-46-146\n8-46-147\n8-47-146\n8-47-147\n6-11-37\n5-5-19+\n6-10-38+\n7-20-76\n7-20-77\n7-21-76+\n8-42-152+\n9-84-304\n9-84-305\n9-85-304\n9-85-305\n8-42-153\n8-43-152\n8-43-153+\n9-86-306\n9-86-307\n9-87-306\n9-87-307\n7-21-77\n6-10-39+\n7-20-78\n7-20-79\n7-21-78\n7-21-79\n6-11-38+\n7-22-76\n7-22-77\n7-23-76\n7-23-77\n6-11-39\n4-3-8+\n5-6-16\n5-6-17\n5-7-16\n5-7-17\n4-3-9+\n5-6-18\n5-6-19+\n6-12-38+\n7-24-76\n7-24-77\n7-25-76\n7-25-77\n6-12-39\n6-13-38\n6-13-39\n5-7-18\n5-7-19\n3-1-5+\n4-2-10\n4-2-11\n4-3-10\n4-3-11+\n5-6-22+\n6-12-44\n6-12-45\n6-13-44\n6-13-45\n5-6-23+\n6-12-46\n6-12-47\n6-13-46+\n7-26-92\n7-26-93\n7-27-92+\n8-54-184\n8-54-185\n8-55-184\n8-55-185+\n9-110-370\n9-110-371\n9-111-370\n9-111-371\n7-27-93\n6-13-47\n5-7-22+\n6-14-44\n6-14-45+\n7-28-90+\n8-56-180\n8-56-181\n8-57-180\n8-57-181+\n9-114-362\n9-114-363\n9-115-362\n9-115-363\n7-28-91\n7-29-90+\n8-58-180\n8-58-181+\n9-116-362\n9-116-363\n9-117-362\n9-117-363\n8-59-180\n8-59-181\n7-29-91\n6-15-44+\n7-30-88+\n8-60-176\n8-60-177\n8-61-176\n8-61-177\n7-30-89\n7-31-88\n7-31-89+\n8-62-178+\n9-124-356+\n10-248-712\n10-248-713\n10-249-712\n10-249-713\n9-124-357\n9-125-356\n9-125-357\n8-62-179\n8-63-178\n8-63-179\n6-15-45+\n7-30-90\n7-30-91\n7-31-90\n7-31-91+\n8-62-182\n8-62-183\n8-63-182\n8-63-183\n5-7-23+\n6-14-46\n6-14-47\n6-15-46+\n7-30-92\n7-30-93\n7-31-92+\n8-62-184\n8-62-185\n8-63-184\n8-63-185\n7-31-93\n6-15-47\n2-0-3+\n3-0-6+\n4-0-12+\n5-0-24+\n6-0-48\n6-0-49\n6-1-48\n6-1-49\n5-0-25\n5-1-24+\n6-2-48\n6-2-49\n6-3-48\n6-3-49\n5-1-25+\n6-2-50\n6-2-51\n6-3-50\n6-3-51+\n7-6-102\n7-6-103\n7-7-102+\n8-14-204\n8-14-205\n8-15-204\n8-15-205\n7-7-103\n4-0-13\n4-1-12\n4-1-13\n3-0-7+\n4-0-14+\n5-0-28+\n6-0-56+\n7-0-112\n7-0-113\n7-1-112\n7-1-113\n6-0-57\n6-1-56\n6-1-57\n5-0-29+\n6-0-58\n6-0-59\n6-1-58\n6-1-59\n5-1-28+\n6-2-56+\n7-4-112\n7-4-113\n7-5-112\n7-5-113\n6-2-57\n6-3-56\n6-3-57+\n7-6-114\n7-6-115\n7-7-114\n7-7-115+\n8-14-230\n8-14-231\n8-15-230\n8-15-231\n5-1-29+\n6-2-58+\n7-4-116+\n8-8-232\n8-8-233\n8-9-232\n8-9-233\n7-4-117+\n8-8-234\n8-8-235\n8-9-234\n8-9-235\n7-5-116\n7-5-117\n6-2-59\n6-3-58\n6-3-59+\n7-6-118\n7-6-119\n7-7-118\n7-7-119\n4-0-15+\n5-0-30\n5-0-31\n5-1-30\n5-1-31\n4-1-14+\n5-2-28+\n6-4-56\n6-4-57\n6-5-56\n6-5-57\n5-2-29\n5-3-28+\n6-6-56\n6-6-57\n6-7-56\n6-7-57+\n7-14-114\n7-14-115\n7-15-114\n7-15-115\n5-3-29\n4-1-15+\n5-2-30\n5-2-31+\n6-4-62\n6-4-63\n6-5-62\n6-5-63\n5-3-30\n5-3-31\n3-1-6+\n4-2-12\n4-2-13+\n5-4-26\n5-4-27+\n6-8-54+\n7-16-108+\n8-32-216+\n9-64-432\n9-64-433\n9-65-432\n9-65-433\n8-32-217\n8-33-216\n8-33-217+\n9-66-434+\n10-132-868\n10-132-869\n10-133-868\n10-133-869\n9-66-435\n9-67-434\n9-67-435\n7-16-109\n7-17-108\n7-17-109\n6-8-55\n6-9-54+\n7-18-108\n7-18-109\n7-19-108\n7-19-109\n6-9-55\n5-5-26\n5-5-27+\n6-10-54\n6-10-55\n6-11-54\n6-11-55+\n7-22-110+\n8-44-220\n8-44-221\n8-45-220\n8-45-221\n7-22-111\n7-23-110\n7-23-111\n4-3-12+\n5-6-24+\n6-12-48\n6-12-49\n6-13-48\n6-13-49\n5-6-25+\n6-12-50\n6-12-51+\n7-24-102\n7-24-103\n7-25-102\n7-25-103\n6-13-50+\n7-26-100\n7-26-101+\n8-52-202\n8-52-203+\n9-104-406\n9-104-407\n9-105-406\n9-105-407\n8-53-202\n8-53-203+\n9-106-406+\n10-212-812\n10-212-813\n10-213-812\n10-213-813\n9-106-407\n9-107-406\n9-107-407\n7-27-100\n7-27-101\n6-13-51\n5-7-24+\n6-14-48+\n7-28-96\n7-28-97\n7-29-96\n7-29-97+\n8-58-194\n8-58-195\n8-59-194\n8-59-195\n6-14-49\n6-15-48\n6-15-49+\n7-30-98\n7-30-99\n7-31-98\n7-31-99\n5-7-25\n4-3-13\n3-1-7+\n4-2-14+\n5-4-28\n5-4-29\n5-5-28\n5-5-29+\n6-10-58+\n7-20-116+\n8-40-232\n8-40-233\n8-41-232\n8-41-233\n7-20-117\n7-21-116\n7-21-117\n6-10-59\n6-11-58+\n7-22-116+\n8-44-232\n8-44-233\n8-45-232\n8-45-233+\n9-90-466\n9-90-467\n9-91-466\n9-91-467\n7-22-117+\n8-44-234\n8-44-235\n8-45-234\n8-45-235\n7-23-116+\n8-46-232\n8-46-233\n8-47-232\n8-47-233\n7-23-117\n6-11-59\n4-2-15+\n5-4-30+\n6-8-60+\n7-16-120+\n8-32-240\n8-32-241\n8-33-240\n8-33-241+\n9-66-482\n9-66-483\n9-67-482\n9-67-483\n7-16-121\n7-17-120\n7-17-121+\n8-34-242\n8-34-243\n8-35-242\n8-35-243\n6-8-61+\n7-16-122+\n8-32-244+\n9-64-488\n9-64-489\n9-65-488\n9-65-489+\n10-130-978\n10-130-979\n10-131-978\n10-131-979\n8-32-245\n8-33-244+\n9-66-488+\n10-132-976\n10-132-977\n10-133-976\n10-133-977\n9-66-489\n9-67-488\n9-67-489\n8-33-245\n7-16-123\n7-17-122\n7-17-123+\n8-34-246\n8-34-247\n8-35-246+\n9-70-492\n9-70-493\n9-71-492\n9-71-493\n8-35-247\n6-9-60\n6-9-61\n5-4-31+\n6-8-62+\n7-16-124\n7-16-125+\n8-32-250+\n9-64-500\n9-64-501\n9-65-500\n9-65-501\n8-32-251+\n9-64-502\n9-64-503\n9-65-502\n9-65-503\n8-33-250\n8-33-251\n7-17-124+\n8-34-248\n8-34-249\n8-35-248\n8-35-249\n7-17-125+\n8-34-250+\n9-68-500\n9-68-501+\n10-136-1002\n10-136-1003\n10-137-1002\n10-137-1003\n9-69-500\n9-69-501\n8-34-251\n8-35-250\n8-35-251+\n9-70-502\n9-70-503\n9-71-502\n9-71-503+\n10-142-1006\n10-142-1007\n10-143-1006\n10-143-1007\n6-8-63\n6-9-62+\n7-18-124\n7-18-125\n7-19-124+\n8-38-248\n8-38-249+\n9-76-498\n9-76-499\n9-77-498\n9-77-499\n8-39-248\n8-39-249\n7-19-125+\n8-38-250+\n9-76-500\n9-76-501\n9-77-500\n9-77-501\n8-38-251\n8-39-250\n8-39-251\n6-9-63+\n7-18-126\n7-18-127\n7-19-126\n7-19-127\n5-5-30\n5-5-31\n4-3-14+\n5-6-28\n5-6-29\n5-7-28\n5-7-29+\n6-14-58\n6-14-59\n6-15-58+\n7-30-116\n7-30-117\n7-31-116\n7-31-117\n6-15-59\n4-3-15\n2-1-2+\n3-2-4+\n4-4-8+\n5-8-16\n5-8-17+\n6-16-34\n6-16-35\n6-17-34+\n7-34-68\n7-34-69\n7-35-68\n7-35-69+\n8-70-138\n8-70-139\n8-71-138\n8-71-139+\n9-142-278\n9-142-279\n9-143-278\n9-143-279\n6-17-35\n5-9-16\n5-9-17+\n6-18-34+\n7-36-68\n7-36-69\n7-37-68+\n8-74-136\n8-74-137\n8-75-136\n8-75-137\n7-37-69\n6-18-35\n6-19-34\n6-19-35\n4-4-9\n4-5-8\n4-5-9+\n5-10-18+\n6-20-36\n6-20-37+\n7-40-74\n7-40-75+\n8-80-150\n8-80-151\n8-81-150\n8-81-151+\n9-162-302\n9-162-303\n9-163-302\n9-163-303+\n10-326-606\n10-326-607\n10-327-606\n10-327-607\n7-41-74\n7-41-75\n6-21-36+\n7-42-72\n7-42-73+\n8-84-146\n8-84-147\n8-85-146\n8-85-147\n7-43-72+\n8-86-144\n8-86-145\n8-87-144\n8-87-145\n7-43-73\n6-21-37\n5-10-19+\n6-20-38\n6-20-39+\n7-40-78+\n8-80-156\n8-80-157\n8-81-156+\n9-162-312\n9-162-313\n9-163-312\n9-163-313\n8-81-157\n7-40-79+\n8-80-158\n8-80-159\n8-81-158\n8-81-159\n7-41-78\n7-41-79\n6-21-38\n6-21-39\n5-11-18+\n6-22-36\n6-22-37\n6-23-36+\n7-46-72\n7-46-73+\n8-92-146\n8-92-147+\n9-184-294+\n10-368-588\n10-368-589\n10-369-588\n10-369-589\n9-184-295\n9-185-294\n9-185-295\n8-93-146+\n9-186-292\n9-186-293\n9-187-292\n9-187-293\n8-93-147\n7-47-72+\n8-94-144\n8-94-145\n8-95-144\n8-95-145\n7-47-73\n6-23-37\n5-11-19\n3-2-5+\n4-4-10\n4-4-11+\n5-8-22\n5-8-23\n5-9-22+\n6-18-44\n6-18-45+\n7-36-90+\n8-72-180+\n9-144-360\n9-144-361\n9-145-360\n9-145-361\n8-72-181\n8-73-180\n8-73-181\n7-36-91+\n8-72-182\n8-72-183\n8-73-182+\n9-146-364\n9-146-365\n9-147-364\n9-147-365\n8-73-183\n7-37-90\n7-37-91+\n8-74-182\n8-74-183\n8-75-182\n8-75-183\n6-19-44\n6-19-45+\n7-38-90\n7-38-91\n7-39-90\n7-39-91\n5-9-23\n4-5-10\n4-5-11+\n5-10-22\n5-10-23\n5-11-22+\n6-22-44\n6-22-45+\n7-44-90\n7-44-91+\n8-88-182\n8-88-183+\n9-176-366\n9-176-367\n9-177-366\n9-177-367\n8-89-182\n8-89-183\n7-45-90+\n8-90-180\n8-90-181\n8-91-180\n8-91-181+\n9-182-362\n9-182-363\n9-183-362\n9-183-363\n7-45-91\n6-23-44\n6-23-45\n5-11-23+\n6-22-46+\n7-44-92\n7-44-93+\n8-88-186\n8-88-187\n8-89-186\n8-89-187\n7-45-92\n7-45-93\n6-22-47\n6-23-46+\n7-46-92\n7-46-93+\n8-92-186\n8-92-187\n8-93-186+\n9-186-372+\n10-372-744\n10-372-745\n10-373-744\n10-373-745\n9-186-373\n9-187-372\n9-187-373\n8-93-187+\n9-186-374\n9-186-375\n9-187-374\n9-187-375\n7-47-92\n7-47-93\n6-23-47\n3-3-4+\n4-6-8\n4-6-9+\n5-12-18\n5-12-19\n5-13-18+\n6-26-36\n6-26-37\n6-27-36\n6-27-37\n5-13-19\n4-7-8+\n5-14-16\n5-14-17+\n6-28-34\n6-28-35\n6-29-34\n6-29-35\n5-15-16+\n6-30-32+\n7-60-64\n7-60-65\n7-61-64\n7-61-65\n6-30-33+\n7-60-66\n7-60-67+\n8-120-134\n8-120-135\n8-121-134\n8-121-135+\n9-242-270\n9-242-271+\n10-484-542\n10-484-543\n10-485-542\n10-485-543\n9-243-270\n9-243-271\n7-61-66\n7-61-67+\n8-122-134\n8-122-135\n8-123-134\n8-123-135+\n9-246-270\n9-246-271+\n10-492-542\n10-492-543\n10-493-542\n10-493-543\n9-247-270\n9-247-271\n6-31-32+\n7-62-64\n7-62-65\n7-63-64\n7-63-65\n6-31-33+\n7-62-66\n7-62-67\n7-63-66+\n8-126-132\n8-126-133\n8-127-132\n8-127-133+\n9-254-266\n9-254-267\n9-255-266\n9-255-267\n7-63-67+\n8-126-134+\n9-252-268\n9-252-269\n9-253-268\n9-253-269\n8-126-135\n8-127-134\n8-127-135\n5-15-17\n4-7-9\n3-3-5\n2-1-3+\n3-2-6+\n4-4-12+\n5-8-24+\n6-16-48\n6-16-49\n6-17-48\n6-17-49+\n7-34-98\n7-34-99\n7-35-98+\n8-70-196+\n9-140-392+\n10-280-784\n10-280-785\n10-281-784\n10-281-785\n9-140-393\n9-141-392\n9-141-393\n8-70-197\n8-71-196\n8-71-197\n7-35-99+\n8-70-198+\n9-140-396\n9-140-397\n9-141-396\n9-141-397\n8-70-199+\n9-140-398\n9-140-399\n9-141-398\n9-141-399\n8-71-198\n8-71-199\n5-8-25\n5-9-24\n5-9-25\n4-4-13+\n5-8-26\n5-8-27+\n6-16-54\n6-16-55\n6-17-54\n6-17-55\n5-9-26+\n6-18-52\n6-18-53\n6-19-52+\n7-38-104\n7-38-105\n7-39-104+\n8-78-208\n8-78-209\n8-79-208\n8-79-209\n7-39-105\n6-19-53+\n7-38-106\n7-38-107+\n8-76-214\n8-76-215\n8-77-214\n8-77-215\n7-39-106+\n8-78-212\n8-78-213\n8-79-212\n8-79-213\n7-39-107\n5-9-27+\n6-18-54\n6-18-55+\n7-36-110\n7-36-111\n7-37-110+\n8-74-220\n8-74-221\n8-75-220\n8-75-221\n7-37-111\n6-19-54+\n7-38-108+\n8-76-216\n8-76-217+\n9-152-434+\n10-304-868\n10-304-869\n10-305-868\n10-305-869\n9-152-435\n9-153-434\n9-153-435\n8-77-216\n8-77-217\n7-38-109+\n8-76-218\n8-76-219\n8-77-218\n8-77-219+\n9-154-438\n9-154-439\n9-155-438\n9-155-439\n7-39-108+\n8-78-216\n8-78-217\n8-79-216+\n9-158-432\n9-158-433\n9-159-432\n9-159-433\n8-79-217+\n9-158-434\n9-158-435+\n10-316-870\n10-316-871\n10-317-870\n10-317-871\n9-159-434\n9-159-435\n7-39-109\n6-19-55\n4-5-12\n4-5-13+\n5-10-26\n5-10-27\n5-11-26\n5-11-27+\n6-22-54+\n7-44-108\n7-44-109+\n8-88-218\n8-88-219+\n9-176-438+\n10-352-876\n10-352-877\n10-353-876\n10-353-877\n9-176-439+\n10-352-878\n10-352-879\n10-353-878\n10-353-879\n9-177-438\n9-177-439\n8-89-218+\n9-178-436\n9-178-437\n9-179-436\n9-179-437\n8-89-219\n7-45-108+\n8-90-216+\n9-180-432\n9-180-433\n9-181-432\n9-181-433\n8-90-217\n8-91-216+\n9-182-432\n9-182-433\n9-183-432\n9-183-433\n8-91-217\n7-45-109\n6-22-55\n6-23-54+\n7-46-108\n7-46-109+\n8-92-218\n8-92-219\n8-93-218\n8-93-219\n7-47-108\n7-47-109\n6-23-55\n3-2-7\n3-3-6\n3-3-7+\n4-6-14+\n5-12-28+\n6-24-56\n6-24-57\n6-25-56+\n7-50-112\n7-50-113\n7-51-112\n7-51-113\n6-25-57\n5-12-29\n5-13-28+\n6-26-56\n6-26-57+\n7-52-114\n7-52-115\n7-53-114\n7-53-115+\n8-106-230\n8-106-231\n8-107-230\n8-107-231\n6-27-56\n6-27-57\n5-13-29\n4-6-15\n4-7-14+\n5-14-28+\n6-28-56\n6-28-57+\n7-56-114+\n8-112-228\n8-112-229\n8-113-228\n8-113-229\n7-56-115\n7-57-114\n7-57-115+\n8-114-230+\n9-228-460\n9-228-461\n9-229-460\n9-229-461\n8-114-231+\n9-228-462\n9-228-463\n9-229-462\n9-229-463\n8-115-230\n8-115-231+\n9-230-462+\n10-460-924\n10-460-925\n10-461-924\n10-461-925\n9-230-463\n9-231-462\n9-231-463\n6-29-56\n6-29-57+\n7-58-114\n7-58-115\n7-59-114\n7-59-115\n5-14-29\n5-15-28+\n6-30-56+\n7-60-112\n7-60-113\n7-61-112+\n8-122-224\n8-122-225\n8-123-224\n8-123-225\n7-61-113\n6-30-57+\n7-60-114\n7-60-115+\n8-120-230\n8-120-231\n8-121-230\n8-121-231\n7-61-114\n7-61-115\n6-31-56+\n7-62-112\n7-62-113+\n8-124-226\n8-124-227\n8-125-226\n8-125-227\n7-63-112\n7-63-113\n6-31-57+\n7-62-114\n7-62-115+\n8-124-230\n8-124-231\n8-125-230\n8-125-231\n7-63-114\n7-63-115+\n8-126-230\n8-126-231\n8-127-230\n8-127-231\n5-15-29\n4-7-15+\n5-14-30+\n6-28-60+\n7-56-120+\n8-112-240\n8-112-241+\n9-224-482\n9-224-483\n9-225-482\n9-225-483\n8-113-240\n8-113-241\n7-56-121\n7-57-120\n7-57-121\n6-28-61+\n7-56-122\n7-56-123+\n8-112-246\n8-112-247\n8-113-246\n8-113-247\n7-57-122\n7-57-123+\n8-114-246\n8-114-247\n8-115-246\n8-115-247\n6-29-60\n6-29-61\n5-14-31\n5-15-30+\n6-30-60+\n7-60-120+\n8-120-240\n8-120-241\n8-121-240\n8-121-241\n7-60-121\n7-61-120\n7-61-121\n6-30-61\n6-31-60\n6-31-61\n5-15-31\n1-1-0+\n2-2-0+\n3-4-0\n3-4-1\n3-5-0+\n4-10-0\n4-10-1+\n5-20-2+\n6-40-4\n6-40-5+\n7-80-10+\n8-160-20\n8-160-21+\n9-320-42\n9-320-43\n9-321-42\n9-321-43+\n10-642-86\n10-642-87\n10-643-86\n10-643-87\n8-161-20\n8-161-21\n7-80-11\n7-81-10\n7-81-11\n6-41-4\n6-41-5\n5-20-3\n5-21-2\n5-21-3+\n6-42-6\n6-42-7\n6-43-6+\n7-86-12\n7-86-13\n7-87-12\n7-87-13\n6-43-7\n4-11-0+\n5-22-0\n5-22-1\n5-23-0\n5-23-1+\n6-46-2\n6-46-3\n6-47-2\n6-47-3+\n7-94-6+\n8-188-12+\n9-376-24\n9-376-25\n9-377-24\n9-377-25\n8-188-13+\n9-376-26\n9-376-27\n9-377-26\n9-377-27\n8-189-12\n8-189-13\n7-94-7+\n8-188-14\n8-188-15\n8-189-14\n8-189-15\n7-95-6\n7-95-7+\n8-190-14\n8-190-15\n8-191-14+\n9-382-28\n9-382-29\n9-383-28+\n10-766-56\n10-766-57\n10-767-56\n10-767-57\n9-383-29\n8-191-15\n4-11-1+\n5-22-2\n5-22-3+\n6-44-6+\n7-88-12\n7-88-13\n7-89-12+\n8-178-24\n8-178-25\n8-179-24+\n9-358-48\n9-358-49\n9-359-48\n9-359-49+\n10-718-98\n10-718-99\n10-719-98\n10-719-99\n8-179-25\n7-89-13+\n8-178-26+\n9-356-52\n9-356-53\n9-357-52\n9-357-53+\n10-714-106\n10-714-107\n10-715-106\n10-715-107\n8-178-27\n8-179-26\n8-179-27+\n9-358-54\n9-358-55\n9-359-54\n9-359-55\n6-44-7\n6-45-6\n6-45-7\n5-23-2\n5-23-3\n3-5-1+\n4-10-2+\n5-20-4+\n6-40-8\n6-40-9\n6-41-8+\n7-82-16\n7-82-17\n7-83-16\n7-83-17\n6-41-9\n5-20-5+\n6-40-10+\n7-80-20+\n8-160-40\n8-160-41\n8-161-40+\n9-322-80\n9-322-81\n9-323-80\n9-323-81\n8-161-41\n7-80-21\n7-81-20\n7-81-21\n6-40-11\n6-41-10+\n7-82-20\n7-82-21+\n8-164-42+\n9-328-84\n9-328-85\n9-329-84\n9-329-85\n8-164-43\n8-165-42\n8-165-43\n7-83-20\n7-83-21\n6-41-11\n5-21-4+\n6-42-8+\n7-84-16\n7-84-17\n7-85-16\n7-85-17\n6-42-9\n6-43-8\n6-43-9+\n7-86-18\n7-86-19\n7-87-18\n7-87-19\n5-21-5+\n6-42-10+\n7-84-20\n7-84-21\n7-85-20\n7-85-21+\n8-170-42\n8-170-43\n8-171-42\n8-171-43\n6-42-11\n6-43-10+\n7-86-20\n7-86-21+\n8-172-42\n8-172-43\n8-173-42\n8-173-43\n7-87-20\n7-87-21+\n8-174-42\n8-174-43\n8-175-42\n8-175-43\n6-43-11\n4-10-3\n4-11-2\n4-11-3\n2-2-1+\n3-4-2+\n4-8-4+\n5-16-8\n5-16-9+\n6-32-18+\n7-64-36\n7-64-37\n7-65-36+\n8-130-72\n8-130-73\n8-131-72\n8-131-73\n7-65-37+\n8-130-74\n8-130-75\n8-131-74\n8-131-75\n6-32-19+\n7-64-38\n7-64-39+\n8-128-78\n8-128-79\n8-129-78\n8-129-79\n7-65-38\n7-65-39+\n8-130-78\n8-130-79\n8-131-78+\n9-262-156\n9-262-157\n9-263-156\n9-263-157\n8-131-79+\n9-262-158\n9-262-159\n9-263-158\n9-263-159\n6-33-18+\n7-66-36\n7-66-37\n7-67-36\n7-67-37\n6-33-19+\n7-66-38\n7-66-39\n7-67-38\n7-67-39+\n8-134-78\n8-134-79\n8-135-78\n8-135-79+\n9-270-158\n9-270-159\n9-271-158\n9-271-159\n5-17-8+\n6-34-16\n6-34-17\n6-35-16+\n7-70-32+\n8-140-64\n8-140-65\n8-141-64\n8-141-65\n7-70-33\n7-71-32\n7-71-33+\n8-142-66+\n9-284-132\n9-284-133\n9-285-132\n9-285-133\n8-142-67+\n9-284-134\n9-284-135\n9-285-134\n9-285-135\n8-143-66\n8-143-67\n6-35-17\n5-17-9\n4-8-5+\n5-16-10+\n6-32-20+\n7-64-40+\n8-128-80\n8-128-81\n8-129-80\n8-129-81\n7-64-41\n7-65-40\n7-65-41\n6-32-21\n6-33-20\n6-33-21\n5-16-11+\n6-32-22\n6-32-23+\n7-64-46\n7-64-47\n7-65-46\n7-65-47+\n8-130-94\n8-130-95\n8-131-94\n8-131-95\n6-33-22\n6-33-23\n5-17-10+\n6-34-20\n6-34-21+\n7-68-42\n7-68-43\n7-69-42+\n8-138-84\n8-138-85\n8-139-84\n8-139-85\n7-69-43\n6-35-20\n6-35-21\n5-17-11+\n6-34-22\n6-34-23\n6-35-22+\n7-70-44\n7-70-45\n7-71-44+\n8-142-88\n8-142-89\n8-143-88+\n9-286-176\n9-286-177\n9-287-176\n9-287-177\n8-143-89\n7-71-45\n6-35-23\n4-9-4+\n5-18-8\n5-18-9\n5-19-8\n5-19-9+\n6-38-18\n6-38-19\n6-39-18+\n7-78-36+\n8-156-72\n8-156-73\n8-157-72\n8-157-73\n7-78-37\n7-79-36\n7-79-37+\n8-158-74\n8-158-75\n8-159-74\n8-159-75\n6-39-19\n4-9-5+\n5-18-10+\n6-36-20\n6-36-21\n6-37-20\n6-37-21\n5-18-11+\n6-36-22\n6-36-23\n6-37-22\n6-37-23+\n7-74-46\n7-74-47\n7-75-46\n7-75-47\n5-19-10+\n6-38-20+\n7-76-40\n7-76-41\n7-77-40+\n8-154-80\n8-154-81+\n9-308-162\n9-308-163\n9-309-162+\n10-618-324\n10-618-325\n10-619-324\n10-619-325\n9-309-163\n8-155-80\n8-155-81\n7-77-41+\n8-154-82\n8-154-83\n8-155-82\n8-155-83\n6-38-21+\n7-76-42\n7-76-43+\n8-152-86\n8-152-87\n8-153-86\n8-153-87+\n9-306-174\n9-306-175\n9-307-174\n9-307-175\n7-77-42\n7-77-43\n6-39-20+\n7-78-40\n7-78-41\n7-79-40\n7-79-41\n6-39-21\n5-19-11+\n6-38-22+\n7-76-44+\n8-152-88\n8-152-89\n8-153-88\n8-153-89+\n9-306-178\n9-306-179\n9-307-178\n9-307-179\n7-76-45\n7-77-44+\n8-154-88\n8-154-89\n8-155-88\n8-155-89+\n9-310-178\n9-310-179\n9-311-178+\n10-622-356\n10-622-357\n10-623-356\n10-623-357\n9-311-179+\n10-622-358\n10-622-359\n10-623-358\n10-623-359\n7-77-45\n6-38-23\n6-39-22\n6-39-23+\n7-78-46\n7-78-47\n7-79-46+\n8-158-92\n8-158-93\n8-159-92\n8-159-93\n7-79-47\n3-4-3\n3-5-2+\n4-10-4+\n5-20-8+\n6-40-16\n6-40-17+\n7-80-34+\n8-160-68\n8-160-69\n8-161-68\n8-161-69\n7-80-35+\n8-160-70\n8-160-71\n8-161-70\n8-161-71\n7-81-34\n7-81-35\n6-41-16\n6-41-17\n5-20-9+\n6-40-18\n6-40-19+\n7-80-38\n7-80-39\n7-81-38+\n8-162-76\n8-162-77\n8-163-76\n8-163-77+\n9-326-154\n9-326-155\n9-327-154\n9-327-155\n7-81-39\n6-41-18\n6-41-19+\n7-82-38+\n8-164-76\n8-164-77\n8-165-76\n8-165-77\n7-82-39+\n8-164-78\n8-164-79\n8-165-78\n8-165-79\n7-83-38+\n8-166-76\n8-166-77\n8-167-76+\n9-334-152\n9-334-153\n9-335-152\n9-335-153\n8-167-77\n7-83-39+\n8-166-78\n8-166-79\n8-167-78\n8-167-79\n5-21-8+\n6-42-16\n6-42-17+\n7-84-34\n7-84-35\n7-85-34+\n8-170-68\n8-170-69\n8-171-68\n8-171-69\n7-85-35\n6-43-16\n6-43-17\n5-21-9+\n6-42-18+\n7-84-36\n7-84-37\n7-85-36+\n8-170-72\n8-170-73\n8-171-72\n8-171-73\n7-85-37\n6-42-19+\n7-84-38\n7-84-39+\n8-168-78\n8-168-79\n8-169-78+\n9-338-156\n9-338-157\n9-339-156\n9-339-157+\n10-678-314\n10-678-315\n10-679-314\n10-679-315\n8-169-79\n7-85-38\n7-85-39\n6-43-18+\n7-86-36\n7-86-37+\n8-172-74+\n9-344-148\n9-344-149\n9-345-148\n9-345-149\n8-172-75\n8-173-74\n8-173-75\n7-87-36+\n8-174-72\n8-174-73\n8-175-72+\n9-350-144\n9-350-145\n9-351-144\n9-351-145\n8-175-73\n7-87-37\n6-43-19\n4-10-5+\n5-20-10\n5-20-11\n5-21-10\n5-21-11+\n6-42-22\n6-42-23+\n7-84-46\n7-84-47\n7-85-46\n7-85-47\n6-43-22+\n7-86-44\n7-86-45\n7-87-44\n7-87-45\n6-43-23\n4-11-4+\n5-22-8+\n6-44-16\n6-44-17+\n7-88-34\n7-88-35\n7-89-34\n7-89-35\n6-45-16\n6-45-17\n5-22-9\n5-23-8+\n6-46-16+\n7-92-32\n7-92-33\n7-93-32\n7-93-33+\n8-186-66\n8-186-67\n8-187-66\n8-187-67\n6-46-17+\n7-92-34+\n8-184-68\n8-184-69\n8-185-68\n8-185-69\n7-92-35+\n8-184-70\n8-184-71\n8-185-70\n8-185-71\n7-93-34\n7-93-35+\n8-186-70+\n9-372-140\n9-372-141\n9-373-140\n9-373-141\n8-186-71\n8-187-70\n8-187-71\n6-47-16\n6-47-17+\n7-94-34\n7-94-35\n7-95-34+\n8-190-68\n8-190-69\n8-191-68\n8-191-69+\n9-382-138\n9-382-139\n9-383-138\n9-383-139\n7-95-35+\n8-190-70\n8-190-71+\n9-380-142+\n10-760-284\n10-760-285\n10-761-284\n10-761-285\n9-380-143+\n10-760-286\n10-760-287\n10-761-286\n10-761-287\n9-381-142\n9-381-143\n8-191-70\n8-191-71\n5-23-9\n4-11-5+\n5-22-10\n5-22-11\n5-23-10+\n6-46-20+\n7-92-40+\n8-184-80\n8-184-81+\n9-368-162\n9-368-163\n9-369-162\n9-369-163+\n10-738-326\n10-738-327\n10-739-326\n10-739-327\n8-185-80\n8-185-81\n7-92-41\n7-93-40\n7-93-41\n6-46-21+\n7-92-42\n7-92-43\n7-93-42+\n8-186-84\n8-186-85\n8-187-84\n8-187-85\n7-93-43+\n8-186-86\n8-186-87\n8-187-86\n8-187-87\n6-47-20\n6-47-21+\n7-94-42+\n8-188-84\n8-188-85+\n9-376-170\n9-376-171\n9-377-170\n9-377-171\n8-189-84\n8-189-85\n7-94-43+\n8-188-86+\n9-376-172\n9-376-173\n9-377-172\n9-377-173\n8-188-87\n8-189-86\n8-189-87\n7-95-42\n7-95-43\n5-23-11\n3-5-3+\n4-10-6\n4-10-7\n4-11-6+\n5-22-12\n5-22-13\n5-23-12+\n6-46-24+\n7-92-48\n7-92-49\n7-93-48\n7-93-49+\n8-186-98\n8-186-99\n8-187-98\n8-187-99\n6-46-25+\n7-92-50\n7-92-51\n7-93-50+\n8-186-100\n8-186-101\n8-187-100\n8-187-101\n7-93-51\n6-47-24\n6-47-25\n5-23-13\n4-11-7\n2-3-0+\n3-6-0\n3-6-1+\n4-12-2\n4-12-3+\n5-24-6\n5-24-7\n5-25-6+\n6-50-12\n6-50-13\n6-51-12+\n7-102-24+\n8-204-48\n8-204-49\n8-205-48+\n9-410-96\n9-410-97\n9-411-96\n9-411-97\n8-205-49\n7-102-25\n7-103-24\n7-103-25+\n8-206-50\n8-206-51\n8-207-50\n8-207-51\n6-51-13+\n7-102-26\n7-102-27\n7-103-26\n7-103-27+\n8-206-54\n8-206-55\n8-207-54\n8-207-55\n5-25-7\n4-13-2+\n5-26-4\n5-26-5+\n6-52-10+\n7-104-20\n7-104-21\n7-105-20\n7-105-21\n6-52-11+\n7-104-22\n7-104-23+\n8-208-46+\n9-416-92\n9-416-93\n9-417-92\n9-417-93\n8-208-47\n8-209-46\n8-209-47\n7-105-22+\n8-210-44\n8-210-45+\n9-420-90+\n10-840-180\n10-840-181\n10-841-180\n10-841-181\n9-420-91\n9-421-90\n9-421-91\n8-211-44\n8-211-45\n7-105-23\n6-53-10\n6-53-11\n5-27-4+\n6-54-8\n6-54-9\n6-55-8+\n7-110-16\n7-110-17+\n8-220-34\n8-220-35\n8-221-34\n8-221-35+\n9-442-70\n9-442-71\n9-443-70+\n10-886-140\n10-886-141\n10-887-140\n10-887-141\n9-443-71\n7-111-16+\n8-222-32+\n9-444-64\n9-444-65\n9-445-64\n9-445-65\n8-222-33+\n9-444-66\n9-444-67\n9-445-66\n9-445-67+\n10-890-134\n10-890-135\n10-891-134\n10-891-135\n8-223-32\n8-223-33\n7-111-17\n6-55-9+\n7-110-18\n7-110-19\n7-111-18\n7-111-19\n5-27-5\n4-13-3\n3-7-0+\n4-14-0+\n5-28-0\n5-28-1+\n6-56-2+\n7-112-4\n7-112-5+\n8-224-10\n8-224-11+\n9-448-22\n9-448-23\n9-449-22\n9-449-23\n8-225-10\n8-225-11\n7-113-4\n7-113-5+\n8-226-10\n8-226-11\n8-227-10\n8-227-11+\n9-454-22\n9-454-23\n9-455-22\n9-455-23\n6-56-3+\n7-112-6\n7-112-7+\n8-224-14\n8-224-15\n8-225-14\n8-225-15+\n9-450-30\n9-450-31\n9-451-30\n9-451-31\n7-113-6\n7-113-7+\n8-226-14\n8-226-15+\n9-452-30\n9-452-31\n9-453-30\n9-453-31\n8-227-14+\n9-454-28\n9-454-29\n9-455-28\n9-455-29\n8-227-15\n6-57-2\n6-57-3\n5-29-0\n5-29-1\n4-14-1+\n5-28-2+\n6-56-4\n6-56-5+\n7-112-10\n7-112-11\n7-113-10\n7-113-11+\n8-226-22\n8-226-23\n8-227-22\n8-227-23\n6-57-4\n6-57-5\n5-28-3\n5-29-2+\n6-58-4+\n7-116-8+\n8-232-16\n8-232-17\n8-233-16\n8-233-17\n7-116-9\n7-117-8\n7-117-9\n6-58-5+\n7-116-10\n7-116-11\n7-117-10\n7-117-11+\n8-234-22\n8-234-23\n8-235-22\n8-235-23\n6-59-4\n6-59-5\n5-29-3\n4-15-0+\n5-30-0\n5-30-1\n5-31-0\n5-31-1\n4-15-1+\n5-30-2\n5-30-3+\n6-60-6\n6-60-7\n6-61-6\n6-61-7\n5-31-2\n5-31-3+\n6-62-6+\n7-124-12\n7-124-13\n7-125-12\n7-125-13\n6-62-7\n6-63-6+\n7-126-12\n7-126-13\n7-127-12+\n8-254-24\n8-254-25\n8-255-24\n8-255-25+\n9-510-50\n9-510-51\n9-511-50\n9-511-51\n7-127-13\n6-63-7\n3-7-1\n2-3-1+\n3-6-2\n3-6-3\n3-7-2+\n4-14-4+\n5-28-8+\n6-56-16\n6-56-17\n6-57-16\n6-57-17+\n7-114-34\n7-114-35\n7-115-34\n7-115-35\n5-28-9+\n6-56-18\n6-56-19\n6-57-18\n6-57-19\n5-29-8+\n6-58-16\n6-58-17\n6-59-16+\n7-118-32\n7-118-33\n7-119-32+\n8-238-64\n8-238-65+\n9-476-130\n9-476-131+\n10-952-262\n10-952-263\n10-953-262\n10-953-263\n9-477-130\n9-477-131\n8-239-64\n8-239-65\n7-119-33\n6-59-17\n5-29-9+\n6-58-18+\n7-116-36\n7-116-37+\n8-232-74\n8-232-75\n8-233-74\n8-233-75\n7-117-36\n7-117-37+\n8-234-74\n8-234-75\n8-235-74\n8-235-75\n6-58-19+\n7-116-38\n7-116-39\n7-117-38+\n8-234-76\n8-234-77\n8-235-76\n8-235-77+\n9-470-154+\n10-940-308\n10-940-309\n10-941-308\n10-941-309\n9-470-155\n9-471-154\n9-471-155\n7-117-39+\n8-234-78\n8-234-79+\n9-468-158\n9-468-159\n9-469-158\n9-469-159\n8-235-78\n8-235-79\n6-59-18\n6-59-19+\n7-118-38\n7-118-39+\n8-236-78\n8-236-79+\n9-472-158+\n10-944-316\n10-944-317\n10-945-316\n10-945-317\n9-472-159\n9-473-158\n9-473-159\n8-237-78\n8-237-79\n7-119-38\n7-119-39+\n8-238-78\n8-238-79\n8-239-78\n8-239-79+\n9-478-158\n9-478-159\n9-479-158\n9-479-159\n4-14-5\n4-15-4+\n5-30-8+\n6-60-16\n6-60-17\n6-61-16+\n7-122-32\n7-122-33+\n8-244-66\n8-244-67+\n9-488-134\n9-488-135+\n10-976-270\n10-976-271\n10-977-270\n10-977-271\n9-489-134\n9-489-135\n8-245-66+\n9-490-132\n9-490-133\n9-491-132\n9-491-133\n8-245-67\n7-123-32\n7-123-33\n6-61-17\n5-30-9\n5-31-8+\n6-62-16+\n7-124-32+\n8-248-64\n8-248-65\n8-249-64\n8-249-65\n7-124-33\n7-125-32\n7-125-33\n6-62-17\n6-63-16\n6-63-17\n5-31-9+\n6-62-18\n6-62-19\n6-63-18+\n7-126-36+\n8-252-72+\n9-504-144\n9-504-145\n9-505-144\n9-505-145\n8-252-73\n8-253-72\n8-253-73\n7-126-37+\n8-252-74\n8-252-75\n8-253-74\n8-253-75\n7-127-36\n7-127-37\n6-63-19+\n7-126-38+\n8-252-76\n8-252-77\n8-253-76\n8-253-77\n7-126-39\n7-127-38\n7-127-39\n4-15-5\n3-7-3+\n4-14-6\n4-14-7\n4-15-6\n4-15-7+\n5-30-14+\n6-60-28+\n7-120-56\n7-120-57\n7-121-56\n7-121-57\n6-60-29\n6-61-28+\n7-122-56\n7-122-57\n7-123-56\n7-123-57+\n8-246-114\n8-246-115\n8-247-114\n8-247-115\n6-61-29\n5-30-15\n5-31-14\n5-31-15\n1-1-1+\n2-2-2+\n3-4-4+\n4-8-8+\n5-16-16\n5-16-17\n5-17-16\n5-17-17\n4-8-9+\n5-16-18+\n6-32-36\n6-32-37+\n7-64-74\n7-64-75+\n8-128-150\n8-128-151\n8-129-150\n8-129-151+\n9-258-302\n9-258-303\n9-259-302\n9-259-303\n7-65-74\n7-65-75+\n8-130-150\n8-130-151\n8-131-150\n8-131-151\n6-33-36+\n7-66-72\n7-66-73\n7-67-72\n7-67-73\n6-33-37\n5-16-19\n5-17-18\n5-17-19\n4-9-8+\n5-18-16\n5-18-17+\n6-36-34+\n7-72-68\n7-72-69\n7-73-68+\n8-146-136\n8-146-137\n8-147-136\n8-147-137\n7-73-69\n6-36-35+\n7-72-70+\n8-144-140\n8-144-141+\n9-288-282\n9-288-283\n9-289-282\n9-289-283\n8-145-140\n8-145-141+\n9-290-282\n9-290-283\n9-291-282\n9-291-283\n7-72-71+\n8-144-142+\n9-288-284\n9-288-285\n9-289-284\n9-289-285\n8-144-143\n8-145-142+\n9-290-284\n9-290-285\n9-291-284\n9-291-285\n8-145-143\n7-73-70\n7-73-71+\n8-146-142\n8-146-143+\n9-292-286\n9-292-287\n9-293-286\n9-293-287\n8-147-142\n8-147-143\n6-37-34\n6-37-35\n5-19-16\n5-19-17\n4-9-9+\n5-18-18+\n6-36-36+\n7-72-72\n7-72-73\n7-73-72\n7-73-73\n6-36-37\n6-37-36\n6-37-37+\n7-74-74+\n8-148-148+\n9-296-296+\n10-592-592\n10-592-593\n10-593-592\n10-593-593\n9-296-297\n9-297-296\n9-297-297\n8-148-149\n8-149-148\n8-149-149\n7-74-75\n7-75-74+\n8-150-148\n8-150-149+\n9-300-298\n9-300-299\n9-301-298\n9-301-299\n8-151-148\n8-151-149\n7-75-75\n5-18-19\n5-19-18\n5-19-19\n3-4-5+\n4-8-10+\n5-16-20\n5-16-21+\n6-32-42\n6-32-43+\n7-64-86+\n8-128-172\n8-128-173\n8-129-172\n8-129-173\n7-64-87\n7-65-86\n7-65-87\n6-33-42\n6-33-43+\n7-66-86\n7-66-87\n7-67-86\n7-67-87+\n8-134-174+\n9-268-348\n9-268-349+\n10-536-698\n10-536-699\n10-537-698\n10-537-699\n9-269-348\n9-269-349\n8-134-175+\n9-268-350\n9-268-351\n9-269-350\n9-269-351\n8-135-174\n8-135-175\n5-17-20\n5-17-21\n4-8-11\n4-9-10+\n5-18-20\n5-18-21+\n6-36-42+\n7-72-84\n7-72-85+\n8-144-170\n8-144-171\n8-145-170\n8-145-171\n7-73-84+\n8-146-168\n8-146-169\n8-147-168\n8-147-169\n7-73-85\n6-36-43\n6-37-42+\n7-74-84+\n8-148-168\n8-148-169\n8-149-168\n8-149-169+\n9-298-338\n9-298-339\n9-299-338+\n10-598-676\n10-598-677\n10-599-676\n10-599-677\n9-299-339\n7-74-85\n7-75-84+\n8-150-168\n8-150-169\n8-151-168\n8-151-169\n7-75-85\n6-37-43+\n7-74-86\n7-74-87\n7-75-86\n7-75-87\n5-19-20\n5-19-21+\n6-38-42\n6-38-43\n6-39-42\n6-39-43+\n7-78-86\n7-78-87+\n8-156-174\n8-156-175+\n9-312-350\n9-312-351\n9-313-350\n9-313-351\n8-157-174\n8-157-175\n7-79-86\n7-79-87\n4-9-11\n3-5-4+\n4-10-8+\n5-20-16\n5-20-17+\n6-40-34+\n7-80-68\n7-80-69+\n8-160-138+\n9-320-276\n9-320-277\n9-321-276\n9-321-277\n8-160-139+\n9-320-278\n9-320-279\n9-321-278\n9-321-279\n8-161-138\n8-161-139\n7-81-68\n7-81-69\n6-40-35\n6-41-34+\n7-82-68\n7-82-69\n7-83-68\n7-83-69\n6-41-35\n5-21-16+\n6-42-32\n6-42-33+\n7-84-66\n7-84-67\n7-85-66+\n8-170-132\n8-170-133\n8-171-132\n8-171-133\n7-85-67\n6-43-32+\n7-86-64\n7-86-65\n7-87-64\n7-87-65\n6-43-33\n5-21-17\n4-10-9+\n5-20-18+\n6-40-36\n6-40-37\n6-41-36\n6-41-37\n5-20-19\n5-21-18+\n6-42-36\n6-42-37\n6-43-36\n6-43-37\n5-21-19+\n6-42-38\n6-42-39\n6-43-38\n6-43-39\n4-11-8\n4-11-9\n3-5-5+\n4-10-10\n4-10-11\n4-11-10+\n5-22-20\n5-22-21+\n6-44-42\n6-44-43\n6-45-42\n6-45-43\n5-23-20+\n6-46-40+\n7-92-80\n7-92-81\n7-93-80\n7-93-81\n6-46-41\n6-47-40+\n7-94-80+\n8-188-160\n8-188-161\n8-189-160\n8-189-161\n7-94-81\n7-95-80\n7-95-81\n6-47-41\n5-23-21\n4-11-11\n2-2-3+\n3-4-6\n3-4-7+\n4-8-14\n4-8-15+\n5-16-30+\n6-32-60\n6-32-61+\n7-64-122\n7-64-123\n7-65-122\n7-65-123+\n8-130-246\n8-130-247\n8-131-246\n8-131-247\n6-33-60\n6-33-61\n5-16-31+\n6-32-62\n6-32-63\n6-33-62+\n7-66-124\n7-66-125+\n8-132-250\n8-132-251\n8-133-250\n8-133-251\n7-67-124+\n8-134-248\n8-134-249\n8-135-248\n8-135-249\n7-67-125+\n8-134-250\n8-134-251\n8-135-250\n8-135-251\n6-33-63+\n7-66-126+\n8-132-252\n8-132-253\n8-133-252\n8-133-253\n7-66-127\n7-67-126\n7-67-127\n5-17-30\n5-17-31+\n6-34-62\n6-34-63+\n7-68-126\n7-68-127\n7-69-126\n7-69-127\n6-35-62\n6-35-63\n4-9-14+\n5-18-28\n5-18-29\n5-19-28+\n6-38-56\n6-38-57\n6-39-56+\n7-78-112\n7-78-113+\n8-156-226\n8-156-227\n8-157-226\n8-157-227\n7-79-112\n7-79-113\n6-39-57\n5-19-29+\n6-38-58\n6-38-59\n6-39-58\n6-39-59+\n7-78-118\n7-78-119\n7-79-118\n7-79-119\n4-9-15\n3-5-6\n3-5-7+\n4-10-14+\n5-20-28+\n6-40-56\n6-40-57\n6-41-56\n6-41-57\n5-20-29\n5-21-28+\n6-42-56+\n7-84-112\n7-84-113\n7-85-112\n7-85-113\n6-42-57\n6-43-56\n6-43-57+\n7-86-114+\n8-172-228\n8-172-229\n8-173-228\n8-173-229\n7-86-115\n7-87-114\n7-87-115\n5-21-29\n4-10-15\n4-11-14+\n5-22-28+\n6-44-56\n6-44-57+\n7-88-114+\n8-176-228\n8-176-229\n8-177-228\n8-177-229\n7-88-115\n7-89-114\n7-89-115\n6-45-56\n6-45-57\n5-22-29+\n6-44-58\n6-44-59\n6-45-58\n6-45-59\n5-23-28\n5-23-29\n4-11-15+\n5-22-30\n5-22-31+\n6-44-62\n6-44-63\n6-45-62\n6-45-63+\n7-90-126\n7-90-127+\n8-180-254\n8-180-255\n8-181-254\n8-181-255\n7-91-126\n7-91-127\n5-23-30+\n6-46-60\n6-46-61\n6-47-60\n6-47-61\n5-23-31\n2-3-2+\n3-6-4+\n4-12-8+\n5-24-16+\n6-48-32\n6-48-33\n6-49-32\n6-49-33\n5-24-17+\n6-48-34+\n7-96-68\n7-96-69\n7-97-68+\n8-194-136\n8-194-137\n8-195-136\n8-195-137+\n9-390-274\n9-390-275\n9-391-274\n9-391-275\n7-97-69+\n8-194-138\n8-194-139+\n9-388-278+\n10-776-556\n10-776-557\n10-777-556\n10-777-557\n9-388-279\n9-389-278+\n10-778-556\n10-778-557\n10-779-556\n10-779-557\n9-389-279\n8-195-138+\n9-390-276\n9-390-277\n9-391-276\n9-391-277\n8-195-139\n6-48-35\n6-49-34+\n7-98-68\n7-98-69+\n8-196-138\n8-196-139\n8-197-138\n8-197-139\n7-99-68\n7-99-69\n6-49-35+\n7-98-70+\n8-196-140\n8-196-141\n8-197-140\n8-197-141\n7-98-71\n7-99-70\n7-99-71+\n8-198-142\n8-198-143\n8-199-142\n8-199-143+\n9-398-286\n9-398-287\n9-399-286+\n10-798-572\n10-798-573\n10-799-572\n10-799-573\n9-399-287\n5-25-16\n5-25-17+\n6-50-34\n6-50-35\n6-51-34\n6-51-35\n4-12-9\n4-13-8+\n5-26-16\n5-26-17\n5-27-16+\n6-54-32\n6-54-33\n6-55-32+\n7-110-64\n7-110-65\n7-111-64+\n8-222-128\n8-222-129\n8-223-128+\n9-446-256\n9-446-257\n9-447-256\n9-447-257\n8-223-129\n7-111-65\n6-55-33\n5-27-17\n4-13-9+\n5-26-18+\n6-52-36\n6-52-37+\n7-104-74+\n8-208-148\n8-208-149\n8-209-148\n8-209-149\n7-104-75+\n8-208-150\n8-208-151\n8-209-150\n8-209-151+\n9-418-302\n9-418-303\n9-419-302\n9-419-303\n7-105-74\n7-105-75\n6-53-36\n6-53-37+\n7-106-74\n7-106-75+\n8-212-150\n8-212-151\n8-213-150\n8-213-151\n7-107-74\n7-107-75\n5-26-19+\n6-52-38+\n7-104-76\n7-104-77\n7-105-76+\n8-210-152\n8-210-153+\n9-420-306\n9-420-307\n9-421-306\n9-421-307\n8-211-152\n8-211-153\n7-105-77\n6-52-39\n6-53-38+\n7-106-76+\n8-212-152\n8-212-153\n8-213-152\n8-213-153\n7-106-77\n7-107-76+\n8-214-152\n8-214-153\n8-215-152\n8-215-153+\n9-430-306\n9-430-307\n9-431-306\n9-431-307\n7-107-77\n6-53-39+\n7-106-78\n7-106-79\n7-107-78\n7-107-79+\n8-214-158\n8-214-159\n8-215-158\n8-215-159\n5-27-18\n5-27-19+\n6-54-38\n6-54-39\n6-55-38+\n7-110-76\n7-110-77\n7-111-76\n7-111-77\n6-55-39+\n7-110-78+\n8-220-156\n8-220-157\n8-221-156\n8-221-157\n7-110-79\n7-111-78\n7-111-79\n3-6-5+\n4-12-10+\n5-24-20+\n6-48-40+\n7-96-80\n7-96-81+\n8-192-162\n8-192-163+\n9-384-326\n9-384-327\n9-385-326\n9-385-327\n8-193-162\n8-193-163\n7-97-80\n7-97-81+\n8-194-162\n8-194-163\n8-195-162\n8-195-163\n6-48-41+\n7-96-82\n7-96-83+\n8-192-166+\n9-384-332\n9-384-333\n9-385-332\n9-385-333\n8-192-167\n8-193-166\n8-193-167\n7-97-82\n7-97-83\n6-49-40+\n7-98-80\n7-98-81\n7-99-80+\n8-198-160\n8-198-161\n8-199-160\n8-199-161\n7-99-81\n6-49-41\n5-24-21+\n6-48-42+\n7-96-84\n7-96-85+\n8-192-170\n8-192-171\n8-193-170+\n9-386-340\n9-386-341\n9-387-340\n9-387-341\n8-193-171\n7-97-84\n7-97-85+\n8-194-170\n8-194-171\n8-195-170+\n9-390-340\n9-390-341\n9-391-340\n9-391-341\n8-195-171+\n9-390-342\n9-390-343\n9-391-342\n9-391-343+\n10-782-686\n10-782-687\n10-783-686\n10-783-687\n6-48-43\n6-49-42\n6-49-43\n5-25-20+\n6-50-40\n6-50-41\n6-51-40\n6-51-41\n5-25-21+\n6-50-42\n6-50-43\n6-51-42+\n7-102-84+\n8-204-168+\n9-408-336\n9-408-337\n9-409-336+\n10-818-672\n10-818-673\n10-819-672\n10-819-673\n9-409-337\n8-204-169\n8-205-168\n8-205-169\n7-102-85\n7-103-84+\n8-206-168+\n9-412-336\n9-412-337\n9-413-336\n9-413-337\n8-206-169\n8-207-168\n8-207-169\n7-103-85\n6-51-43\n4-12-11+\n5-24-22+\n6-48-44\n6-48-45\n6-49-44\n6-49-45\n5-24-23\n5-25-22+\n6-50-44+\n7-100-88\n7-100-89\n7-101-88\n7-101-89\n6-50-45\n6-51-44+\n7-102-88\n7-102-89+\n8-204-178+\n9-408-356\n9-408-357+\n10-816-714\n10-816-715\n10-817-714\n10-817-715\n9-409-356\n9-409-357\n8-204-179\n8-205-178+\n9-410-356\n9-410-357\n9-411-356+\n10-822-712\n10-822-713\n10-823-712\n10-823-713\n9-411-357+\n10-822-714\n10-822-715\n10-823-714\n10-823-715\n8-205-179+\n9-410-358\n9-410-359\n9-411-358\n9-411-359\n7-103-88+\n8-206-176+\n9-412-352\n9-412-353\n9-413-352\n9-413-353\n8-206-177\n8-207-176\n8-207-177+\n9-414-354\n9-414-355\n9-415-354\n9-415-355\n7-103-89+\n8-206-178\n8-206-179\n8-207-178+\n9-414-356\n9-414-357+\n10-828-714\n10-828-715\n10-829-714\n10-829-715\n9-415-356\n9-415-357\n8-207-179\n6-51-45\n5-25-23\n4-13-10+\n5-26-20\n5-26-21+\n6-52-42+\n7-104-84\n7-104-85\n7-105-84\n7-105-85+\n8-210-170\n8-210-171\n8-211-170\n8-211-171\n6-52-43\n6-53-42\n6-53-43\n5-27-20\n5-27-21+\n6-54-42\n6-54-43+\n7-108-86+\n8-216-172\n8-216-173\n8-217-172+\n9-434-344+\n10-868-688\n10-868-689\n10-869-688\n10-869-689\n9-434-345\n9-435-344\n9-435-345\n8-217-173\n7-108-87\n7-109-86\n7-109-87+\n8-218-174\n8-218-175\n8-219-174\n8-219-175+\n9-438-350\n9-438-351\n9-439-350\n9-439-351\n6-55-42\n6-55-43\n4-13-11+\n5-26-22\n5-26-23\n5-27-22+\n6-54-44+\n7-108-88+\n8-216-176\n8-216-177\n8-217-176\n8-217-177\n7-108-89\n7-109-88\n7-109-89\n6-54-45\n6-55-44\n6-55-45+\n7-110-90\n7-110-91+\n8-220-182\n8-220-183\n8-221-182\n8-221-183\n7-111-90\n7-111-91\n5-27-23\n3-7-4\n3-7-5\n2-3-3+\n3-6-6\n3-6-7\n3-7-6\n3-7-7+\n4-14-14+\n5-28-28+\n6-56-56\n6-56-57+\n7-112-114\n7-112-115+\n8-224-230\n8-224-231+\n9-448-462\n9-448-463\n9-449-462\n9-449-463\n8-225-230\n8-225-231\n7-113-114\n7-113-115\n6-57-56+\n7-114-112\n7-114-113\n7-115-112\n7-115-113+\n8-230-226+\n9-460-452\n9-460-453\n9-461-452\n9-461-453\n8-230-227+\n9-460-454\n9-460-455\n9-461-454\n9-461-455+\n10-922-910\n10-922-911\n10-923-910\n10-923-911\n8-231-226\n8-231-227\n6-57-57\n5-28-29+\n6-56-58+\n7-112-116+\n8-224-232+\n9-448-464\n9-448-465\n9-449-464\n9-449-465\n8-224-233\n8-225-232\n8-225-233+\n9-450-466\n9-450-467\n9-451-466\n9-451-467\n7-112-117\n7-113-116\n7-113-117\n6-56-59+\n7-112-118\n7-112-119\n7-113-118\n7-113-119\n6-57-58+\n7-114-116\n7-114-117\n7-115-116\n7-115-117\n6-57-59\n5-29-28\n5-29-29\n4-14-15\n4-15-14+\n5-30-28+\n6-60-56\n6-60-57+\n7-120-114\n7-120-115\n7-121-114\n7-121-115\n6-61-56\n6-61-57\n5-30-29+\n6-60-58+\n7-120-116+\n8-240-232\n8-240-233\n8-241-232+\n9-482-465\n9-483-464\n9-482-464\n9-483-465\n8-241-233\n7-120-117\n7-121-116+\n8-242-232\n8-242-233\n8-243-232\n8-243-233\n7-121-117\n6-60-59\n6-61-58\n6-61-59+\n7-122-118\n7-122-119+\n8-244-238\n8-244-239+\n9-488-478\n9-488-479\n9-489-478\n9-489-479\n8-245-238\n8-245-239+\n9-490-478\n9-490-479\n9-491-478\n9-491-479\n7-123-118+\n8-246-236\n8-246-237+\n9-492-474\n9-492-475\n9-493-474\n9-493-475\n8-247-236\n8-247-237\n7-123-119\n5-31-28+\n6-62-56\n6-62-57\n6-63-56\n6-63-57+\n7-126-114+\n8-252-228\n8-252-229\n8-253-228\n8-253-229\n7-126-115+\n8-252-230+\n9-504-460\n9-504-461\n9-505-460\n9-505-461\n8-252-231+\n9-504-462\n9-504-463\n9-505-462\n9-505-463\n8-253-230\n8-253-231\n7-127-114\n7-127-115+\n8-254-230\n8-254-231\n8-255-230\n8-255-231\n5-31-29+\n6-62-58\n6-62-59\n6-63-58+\n7-126-116+\n8-252-232+\n9-504-464\n9-504-465\n9-505-464\n9-505-465\n8-252-233+\n9-504-466\n9-504-467\n9-505-466\n9-505-467\n8-253-232+\n9-506-464\n9-506-465\n9-507-464\n9-507-465\n8-253-233\n7-126-117\n7-127-116\n7-127-117\n6-63-59+\n7-126-118\n7-126-119\n7-127-118\n7-127-119\n4-15-15+\n5-30-30+\n6-60-60\n6-60-61\n6-61-60\n6-61-61+\n7-122-122\n7-122-123\n7-123-122\n7-123-123\n5-30-31\n5-31-30\n5-31-31+\n6-62-62+\n7-124-124\n7-124-125\n7-125-124+\n8-250-248+\n9-500-496\n9-500-497\n9-501-496\n9-501-497\n8-250-249\n8-251-248\n8-251-249\n7-125-125+\n8-250-250\n8-250-251\n8-251-250\n8-251-251\n6-62-63+\n7-124-126+\n8-248-252\n8-248-253\n8-249-252\n8-249-253\n7-124-127\n7-125-126\n7-125-127\n6-63-62\n6-63-63+\n7-126-126\n7-126-127\n7-127-126\n7-127-127\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/geography/sharding/testDynamicShardingMissingChildren.txt",
    "content": "0-0-0+\n1-0-0+\n2-0-0+\n3-0-0+\n4-0-0+\n5-0-0\n5-0-1\n5-1-0\n5-1-1\n4-0-1\n4-1-0\n4-1-1\n3-0-1+\n4-0-2+\n5-0-4\n5-0-5\n5-1-4+\n6-2-8\n6-2-9\n6-3-8\n6-3-9+\n7-6-18\n7-6-19+\n8-12-38\n8-12-39+\n9-24-78\n9-24-79\n9-25-78\n9-25-79\n8-13-38\n8-13-39\n7-7-18\n7-7-19+\n8-14-38\n8-14-39\n8-15-38\n8-15-39+\n9-30-78\n9-30-79\n9-31-78\n9-31-79+\n10-62-158\n10-62-159\n10-63-158\n10-63-159\n5-1-5+\n6-2-10\n6-2-11\n6-3-10\n6-3-11\n4-0-3+\n5-0-6\n5-0-7\n5-1-6\n5-1-7\n4-1-2\n4-1-3+\n5-2-6\n5-2-7\n5-3-7\n5-3-6\n3-1-0\n3-1-1\n2-0-1+\n3-0-2+\n4-0-4\n4-0-5+\n5-0-10\n5-0-11+\n6-0-22\n6-0-23+\n7-0-46+\n8-0-92+\n9-0-184\n9-0-185\n9-1-184\n9-1-185\n8-0-93\n8-1-92\n8-1-93\n7-0-47\n7-1-46\n7-1-47\n6-1-22\n6-1-23+\n7-2-46\n7-2-47\n7-3-46\n7-3-47\n5-1-10\n5-1-11\n4-1-4+\n5-2-8+\n6-4-16\n6-4-17\n6-5-16\n6-5-17\n5-2-9\n5-3-8\n5-3-9\n4-1-5\n3-0-3+\n4-0-6\n4-0-7\n4-1-6\n4-1-7\n3-1-2\n3-1-3\n2-1-0+\n3-2-0\n3-2-1\n3-3-0+\n4-6-0+\n5-12-0\n5-12-1+\n6-24-2+\n7-48-4\n7-48-5+\n8-96-10+\n9-192-20\n9-192-21\n9-193-20\n9-193-21\n8-96-11\n8-97-10\n8-97-11\n7-49-4\n7-49-5\n6-24-3\n6-25-2\n6-25-3\n5-13-0+\n6-26-0\n6-26-1\n6-27-0\n6-27-1\n5-13-1\n4-6-1\n4-7-0\n4-7-1+\n5-14-2\n5-14-3\n5-15-2+\n6-30-4+\n7-60-8\n7-60-9+\n8-120-18\n8-120-19\n8-121-18\n8-121-19\n7-61-8\n7-61-9\n6-30-5\n6-31-4\n6-31-5\n5-15-3\n3-3-1+\n4-6-2\n4-6-3+\n5-12-6\n5-12-7+\n6-24-14\n6-24-15\n6-25-14\n6-25-15+\n7-50-30\n7-50-31\n7-51-30\n7-51-31+\n8-102-62+\n9-204-124\n9-204-125\n9-205-124\n9-205-125\n8-102-63\n8-103-62\n8-103-63\n5-13-6+\n6-26-12\n6-26-13+\n7-52-26\n7-52-27\n7-53-26\n7-53-27\n6-27-12+\n7-54-24\n7-54-25\n7-55-24\n7-55-25\n6-27-13+\n7-54-26\n7-54-27\n7-55-26\n7-55-27\n5-13-7\n4-7-2\n4-7-3+\n5-14-6+\n6-28-12+\n7-56-24\n7-56-25+\n8-112-50\n8-112-51\n8-113-50\n8-113-51+\n9-226-102\n9-226-103\n9-227-102\n9-227-103\n7-57-24\n7-57-25\n6-28-13\n6-29-12+\n7-58-24+\n8-116-48\n8-116-49+\n9-232-98\n9-232-99\n9-233-98\n9-233-99\n8-117-48\n8-117-49\n7-58-25\n7-59-24+\n8-118-48\n8-118-49\n8-119-48\n8-119-49\n7-59-25\n6-29-13+\n7-58-26\n7-58-27\n7-59-26\n7-59-27\n5-14-7\n5-15-6+\n6-30-12\n6-30-13+\n7-60-26+\n8-120-52\n8-120-53\n8-121-52\n8-121-53+\n9-242-106\n9-242-107\n9-243-106\n9-243-107\n7-60-27+\n8-120-54\n8-120-55\n8-121-54\n8-121-55+\n9-242-110\n9-242-111\n9-243-110\n9-243-111\n7-61-26+\n8-122-52\n8-122-53+\n9-244-106\n9-244-107\n9-245-106\n9-245-107\n8-123-52\n8-123-53\n7-61-27\n6-31-12\n6-31-13\n5-15-7+\n6-30-14+\n7-60-28\n7-60-29\n7-61-28\n7-61-29\n6-30-15+\n7-60-30\n7-60-31\n7-61-30\n7-61-31\n6-31-14+\n7-62-28+\n8-124-56\n8-124-57\n8-125-56+\n9-250-112\n9-250-113\n9-251-112\n9-251-113\n8-125-57\n7-62-29+\n8-124-58\n8-124-59\n8-125-58\n8-125-59\n7-63-28\n7-63-29+\n8-126-58\n8-126-59\n8-127-58\n8-127-59\n6-31-15+\n7-62-30\n7-62-31\n7-63-30\n7-63-31\n2-1-1+\n3-2-2+\n4-4-4+\n5-8-8+\n6-16-16+\n7-32-32+\n8-64-64\n8-64-65\n8-65-64\n8-65-65\n7-32-33+\n8-64-66\n8-64-67\n8-65-66\n8-65-67\n7-33-32\n7-33-33+\n8-66-66\n8-66-67\n8-67-66\n8-67-67+\n9-134-134\n9-134-135\n9-135-134\n9-135-135\n6-16-17\n6-17-16\n6-17-17\n5-8-9\n5-9-8+\n6-18-16\n6-18-17\n6-19-16\n6-19-17\n5-9-9\n4-4-5\n4-5-4\n4-5-5+\n5-10-10+\n6-20-20+\n7-40-40\n7-40-41+\n8-80-82\n8-80-83\n8-81-82+\n9-162-164\n9-162-165\n9-163-164\n9-163-165\n8-81-83\n7-41-40\n7-41-41\n6-20-21+\n7-40-42+\n8-80-84\n8-80-85+\n9-160-170\n9-160-171+\n10-320-342\n10-320-343\n10-321-342\n10-321-343\n9-161-170\n9-161-171\n8-81-84\n8-81-85\n7-40-43\n7-41-42\n7-41-43\n6-21-20\n6-21-21\n5-10-11+\n6-20-22+\n7-40-44+\n8-80-88\n8-80-89\n8-81-88+\n9-162-176\n9-162-177\n9-163-176\n9-163-177\n8-81-89\n7-40-45\n7-41-44+\n8-82-88\n8-82-89\n8-83-88\n8-83-89\n7-41-45\n6-20-23+\n7-40-46\n7-40-47\n7-41-46+\n8-82-92+\n9-164-184\n9-164-185\n9-165-184\n9-165-185\n8-82-93+\n9-164-186\n9-164-187\n9-165-186+\n10-330-372\n10-330-373\n10-331-372\n10-331-373\n9-165-187\n8-83-92\n8-83-93+\n9-166-186\n9-166-187\n9-167-186+\n10-334-372\n10-334-373\n10-335-372\n10-335-373\n9-167-187\n7-41-47\n6-21-22\n6-21-23\n5-11-10\n5-11-11+\n6-22-22+\n7-44-44+\n8-88-88+\n9-176-176\n9-176-177\n9-177-176\n9-177-177\n8-88-89\n8-89-88\n8-89-89\n7-44-45\n7-45-44\n7-45-45\n6-22-23\n6-23-22\n6-23-23+\n7-46-46\n7-46-47\n7-47-46+\n8-94-92\n8-94-93\n8-95-92\n8-95-93+\n9-190-186\n9-190-187+\n10-380-374\n10-380-375\n10-381-374\n10-381-375\n9-191-186\n9-191-187\n7-47-47\n3-2-3+\n4-4-6+\n5-8-12+\n6-16-24\n6-16-25\n6-17-24\n6-17-25\n5-8-13\n5-9-12\n5-9-13+\n6-18-26\n6-18-27\n6-19-26\n6-19-27+\n7-38-54+\n8-76-108+\n9-152-216\n9-152-217\n9-153-216\n9-153-217\n8-76-109+\n9-152-218\n9-152-219\n9-153-218+\n10-306-436\n10-306-437\n10-307-436\n10-307-437\n9-153-219\n8-77-108\n8-77-109\n7-38-55+\n8-76-110\n8-76-111\n8-77-110\n8-77-111\n7-39-54+\n8-78-108\n8-78-109+\n9-156-218\n9-156-219\n9-157-218\n9-157-219\n8-79-108\n8-79-109+\n9-158-218\n9-158-219\n9-159-218\n9-159-219\n7-39-55\n4-4-7\n4-5-6+\n5-10-12+\n6-20-24+\n7-40-48\n7-40-49\n7-41-48+\n8-82-96\n8-82-97\n8-83-96+\n9-166-192\n9-166-193\n9-167-192\n9-167-193\n8-83-97\n7-41-49\n6-20-25\n6-21-24+\n7-42-48\n7-42-49\n7-43-48+\n8-86-96\n8-86-97\n8-87-96\n8-87-97\n7-43-49\n6-21-25\n5-10-13\n5-11-12\n5-11-13\n4-5-7\n3-3-2+\n4-6-4\n4-6-5\n4-7-4+\n5-14-8\n5-14-9\n5-15-8+\n6-30-16+\n7-60-32\n7-60-33\n7-61-32+\n8-122-64\n8-122-65\n8-123-64\n8-123-65\n7-61-33\n6-30-17\n6-31-16\n6-31-17\n5-15-9\n4-7-5\n3-3-3\n1-0-1+\n2-0-2+\n3-0-4\n3-0-5+\n4-0-10\n4-0-11\n4-1-10\n4-1-11\n3-1-4+\n4-2-8\n4-2-9+\n5-4-18+\n6-8-36+\n7-16-72\n7-16-73+\n8-32-146\n8-32-147+\n9-64-294\n9-64-295\n9-65-294\n9-65-295+\n10-130-590\n10-130-591\n10-131-590\n10-131-591\n8-33-146\n8-33-147\n7-17-72\n7-17-73+\n8-34-146\n8-34-147\n8-35-146\n8-35-147\n6-8-37\n6-9-36\n6-9-37+\n7-18-74\n7-18-75\n7-19-74\n7-19-75\n5-4-19\n5-5-18+\n6-10-36+\n7-20-72\n7-20-73+\n8-40-146\n8-40-147+\n9-80-294\n9-80-295\n9-81-294\n9-81-295\n8-41-146\n8-41-147\n7-21-72\n7-21-73\n6-10-37\n6-11-36+\n7-22-72+\n8-44-144\n8-44-145\n8-45-144\n8-45-145+\n9-90-290\n9-90-291\n9-91-290\n9-91-291+\n10-182-582\n10-182-583\n10-183-582\n10-183-583\n7-22-73\n7-23-72\n7-23-73+\n8-46-146\n8-46-147\n8-47-146\n8-47-147\n6-11-37\n5-5-19+\n6-10-38+\n7-20-76\n7-20-77\n7-21-76+\n8-42-152+\n9-84-304\n9-84-305\n9-85-304\n9-85-305\n8-42-153\n8-43-152\n8-43-153+\n9-86-306\n9-86-307\n9-87-306\n9-87-307\n7-21-77\n6-10-39+\n7-20-78\n7-20-79\n7-21-78\n7-21-79\n6-11-38+\n7-22-76\n7-22-77\n7-23-76\n7-23-77\n6-11-39\n4-3-8+\n5-6-16\n5-6-17\n5-7-16\n5-7-17\n4-3-9+\n5-6-18\n5-6-19+\n6-12-38+\n7-24-76\n7-24-77\n7-25-76\n7-25-77\n6-12-39\n6-13-38\n6-13-39\n5-7-18\n5-7-19\n3-1-5+\n4-2-10\n4-2-11\n4-3-10\n4-3-11+\n5-6-22+\n6-12-44\n6-12-45\n6-13-44\n6-13-45\n5-6-23+\n6-12-46\n6-12-47\n6-13-46+\n7-26-92\n7-26-93\n7-27-92+\n8-54-184\n8-54-185\n8-55-184\n8-55-185+\n9-110-370\n9-110-371\n9-111-370\n9-111-371\n7-27-93\n6-13-47\n5-7-22+\n6-14-44\n6-14-45+\n7-28-90+\n8-56-180\n8-56-181\n8-57-180\n8-57-181+\n9-114-362\n9-114-363\n9-115-362\n9-115-363\n7-28-91\n7-29-90+\n8-58-180\n8-58-181+\n9-116-362\n9-116-363\n9-117-362\n9-117-363\n8-59-180\n8-59-181\n7-29-91\n6-15-44+\n7-30-88+\n8-60-176\n8-60-177\n8-61-176\n8-61-177\n7-30-89\n7-31-88\n7-31-89+\n8-62-178+\n9-124-356+\n10-248-712\n10-248-713\n10-249-712\n10-249-713\n9-124-357\n9-125-356\n9-125-357\n8-62-179\n8-63-178\n8-63-179\n6-15-45+\n7-30-90\n7-30-91\n7-31-90\n7-31-91+\n8-62-182\n8-62-183\n8-63-182\n8-63-183\n5-7-23+\n6-14-46\n6-14-47\n6-15-46+\n7-30-92\n7-30-93\n7-31-92+\n8-62-184\n8-62-185\n8-63-184\n8-63-185\n7-31-93\n6-15-47\n2-0-3+\n3-0-6+\n4-0-12+\n5-0-24+\n6-0-48\n6-0-49\n6-1-48\n6-1-49\n5-0-25\n5-1-24+\n6-2-48\n6-2-49\n6-3-48\n6-3-49\n5-1-25+\n6-2-50\n6-2-51\n6-3-50\n6-3-51+\n7-6-102\n7-6-103\n7-7-102+\n8-14-204\n8-14-205\n8-15-204\n8-15-205\n7-7-103\n4-0-13\n4-1-12\n4-1-13\n3-0-7+\n4-0-14+\n5-0-28+\n6-0-56+\n7-0-112\n7-0-113\n7-1-112\n7-1-113\n6-0-57\n6-1-56\n6-1-57\n5-0-29+\n6-0-58\n6-0-59\n6-1-58\n6-1-59\n5-1-28+\n6-2-56+\n7-4-112\n7-4-113\n7-5-112\n7-5-113\n6-2-57\n6-3-56\n6-3-57+\n7-6-114\n7-6-115\n7-7-114\n7-7-115+\n8-14-230\n8-14-231\n8-15-230\n8-15-231\n5-1-29+\n6-2-58+\n7-4-116+\n8-8-232\n8-8-233\n8-9-232\n8-9-233\n7-4-117+\n8-8-234\n8-8-235\n8-9-234\n8-9-235\n7-5-116\n7-5-117\n6-2-59\n6-3-58\n6-3-59+\n7-6-118\n7-6-119\n7-7-118\n7-7-119\n4-0-15+\n5-0-30\n5-0-31\n5-1-30\n5-1-31\n4-1-14+\n5-2-28+\n6-4-56\n6-4-57\n6-5-56\n6-5-57\n5-2-29\n5-3-28+\n6-6-56\n6-6-57\n6-7-56\n6-7-57+\n7-14-114\n7-14-115\n7-15-114\n7-15-115\n5-3-29\n4-1-15+\n5-2-30\n5-2-31+\n6-4-62\n6-4-63\n6-5-62\n6-5-63\n5-3-30\n5-3-31\n3-1-6+\n4-2-12\n4-2-13+\n5-4-26\n5-4-27+\n6-8-54+\n7-16-108+\n8-32-216+\n9-64-432\n9-64-433\n9-65-432\n9-65-433\n8-32-217\n8-33-216\n8-33-217+\n9-66-434+\n10-132-868\n10-132-869\n10-133-868\n10-133-869\n9-66-435\n9-67-434\n9-67-435\n7-16-109\n7-17-108\n7-17-109\n6-8-55\n6-9-54+\n7-18-108\n7-18-109\n7-19-108\n7-19-109\n6-9-55\n5-5-26\n5-5-27+\n6-10-54\n6-10-55\n6-11-54\n6-11-55+\n7-22-110+\n8-44-220\n8-44-221\n8-45-220\n8-45-221\n7-22-111\n7-23-110\n7-23-111\n4-3-12+\n5-6-24+\n6-12-48\n6-12-49\n6-13-48\n6-13-49\n5-6-25+\n6-12-50\n6-12-51+\n7-24-102\n7-24-103\n7-25-102\n7-25-103\n6-13-50+\n7-26-100\n7-26-101+\n8-52-202\n8-52-203+\n9-104-406\n9-104-407\n9-105-406\n9-105-407\n8-53-202\n8-53-203+\n9-106-406+\n10-212-812\n10-212-813\n10-213-812\n10-213-813\n9-106-407\n9-107-406\n9-107-407\n7-27-100\n7-27-101\n6-13-51\n5-7-24+\n6-14-48+\n7-28-96\n7-28-97\n7-29-96\n7-29-97+\n8-58-194\n8-58-195\n8-59-194\n8-59-195\n6-14-49\n6-15-48\n6-15-49+\n7-30-98\n7-30-99\n7-31-98\n7-31-99\n5-7-25\n4-3-13\n3-1-7+\n4-2-14+\n5-4-28\n5-4-29\n5-5-28\n5-5-29+\n6-10-58+\n7-20-116+\n8-40-232\n8-40-233\n8-41-232\n8-41-233\n7-20-117\n7-21-116\n7-21-117\n6-10-59\n6-11-58+\n7-22-116+\n8-44-232\n8-44-233\n8-45-232\n8-45-233+\n9-90-466\n9-90-467\n9-91-466\n9-91-467\n7-22-117+\n8-44-234\n8-44-235\n8-45-234\n8-45-235\n7-23-116+\n8-46-232\n8-46-233\n8-47-232\n8-47-233\n7-23-117\n6-11-59\n4-2-15+\n5-4-30+\n6-8-60+\n7-16-120+\n8-32-240\n8-32-241\n8-33-240\n8-33-241+\n9-66-482\n9-66-483\n9-67-482\n9-67-483\n7-16-121\n7-17-120\n7-17-121+\n8-34-242\n8-34-243\n8-35-242\n8-35-243\n6-8-61+\n7-16-122+\n8-32-244+\n9-64-488\n9-64-489\n9-65-488\n9-65-489+\n10-130-978\n10-130-979\n10-131-978\n10-131-979\n8-32-245\n8-33-244+\n9-66-488+\n10-132-976\n10-132-977\n10-133-976\n10-133-977\n9-66-489\n9-67-488\n9-67-489\n8-33-245\n7-16-123\n7-17-122\n7-17-123+\n8-34-246\n8-34-247\n8-35-246+\n9-70-492\n9-70-493\n9-71-492\n9-71-493\n8-35-247\n6-9-60\n6-9-61\n5-4-31+\n6-8-62+\n7-16-124\n7-16-125+\n8-32-250+\n9-64-500\n9-64-501\n9-65-500\n9-65-501\n8-32-251+\n9-64-502\n9-64-503\n9-65-502\n9-65-503\n8-33-250\n8-33-251\n7-17-124+\n8-34-248\n8-34-249\n8-35-248\n8-35-249\n7-17-125+\n8-34-250+\n9-68-500\n9-68-501+\n10-136-1002\n10-136-1003\n10-137-1002\n10-137-1003\n9-69-500\n9-69-501\n8-34-251\n8-35-250\n8-35-251+\n9-70-502\n9-70-503\n9-71-502\n9-71-503+\n10-142-1006\n10-142-1007\n10-143-1006\n10-143-1007\n6-8-63\n6-9-62+\n7-18-124\n7-18-125\n7-19-124+\n8-38-248\n8-38-249+\n9-76-498\n9-76-499\n9-77-498\n9-77-499\n8-39-248\n8-39-249\n7-19-125+\n8-38-250+\n9-76-500\n9-76-501\n9-77-500\n9-77-501\n8-38-251\n8-39-250\n8-39-251\n6-9-63+\n7-18-126\n7-18-127\n7-19-126\n7-19-127\n5-5-30\n5-5-31\n4-3-14+\n5-6-28\n5-6-29\n5-7-28\n5-7-29+\n6-14-58\n6-14-59\n6-15-58+\n7-30-116\n7-30-117\n7-31-116\n7-31-117\n6-15-59\n4-3-15\n2-1-2+\n3-2-4+\n4-4-8+\n5-8-16\n5-8-17+\n6-16-34\n6-16-35\n6-17-34+\n7-34-68\n7-34-69\n7-35-68\n7-35-69+\n8-70-138\n8-70-139\n8-71-138\n8-71-139+\n9-142-278\n9-142-279\n9-143-278\n9-143-279\n6-17-35\n5-9-16\n5-9-17+\n6-18-34+\n7-36-68\n7-36-69\n7-37-68+\n8-74-136\n8-74-137\n8-75-136\n8-75-137\n7-37-69\n6-18-35\n6-19-34\n6-19-35\n4-4-9\n4-5-8\n4-5-9+\n5-10-18+\n6-20-36\n6-20-37+\n7-40-74\n7-40-75+\n8-80-150\n8-80-151\n8-81-150\n8-81-151+\n9-162-302\n9-162-303\n9-163-302\n9-163-303+\n10-326-606\n10-326-607\n10-327-606\n10-327-607\n7-41-74\n7-41-75\n6-21-36+\n7-42-72\n7-42-73+\n8-84-146\n8-84-147\n8-85-146\n8-85-147\n7-43-72+\n8-86-144\n8-86-145\n8-87-144\n8-87-145\n7-43-73\n6-21-37\n5-10-19+\n6-20-38\n6-20-39+\n7-40-78+\n8-80-156\n8-80-157\n8-81-156+\n9-162-312\n9-162-313\n9-163-312\n9-163-313\n8-81-157\n7-40-79+\n8-80-158\n8-80-159\n8-81-158\n8-81-159\n7-41-78\n7-41-79\n6-21-38\n6-21-39\n5-11-18+\n6-22-36\n6-22-37\n6-23-36+\n7-46-72\n7-46-73+\n8-92-146\n8-92-147+\n9-184-294+\n10-368-588\n10-368-589\n10-369-588\n10-369-589\n9-184-295\n9-185-294\n9-185-295\n8-93-146+\n9-186-292\n9-186-293\n9-187-292\n9-187-293\n8-93-147\n7-47-72+\n8-94-144\n8-94-145\n8-95-144\n8-95-145\n7-47-73\n6-23-37\n5-11-19\n3-2-5+\n4-4-10\n4-4-11+\n5-8-22\n5-8-23\n5-9-22+\n6-18-44\n6-18-45+\n7-36-90+\n8-72-180+\n9-144-360\n9-144-361\n9-145-360\n9-145-361\n8-72-181\n8-73-180\n8-73-181\n7-36-91+\n8-72-182\n8-72-183\n8-73-182+\n9-146-364\n9-146-365\n9-147-364\n9-147-365\n8-73-183\n7-37-90\n7-37-91+\n8-74-182\n8-74-183\n8-75-182\n8-75-183\n6-19-44\n6-19-45+\n7-38-90\n7-38-91\n7-39-90\n7-39-91\n5-9-23\n4-5-10\n4-5-11+\n5-10-22\n5-10-23\n5-11-22+\n6-22-44\n6-22-45+\n7-44-90\n7-44-91+\n8-88-182\n8-88-183+\n9-176-366\n9-176-367\n9-177-366\n9-177-367\n8-89-182\n8-89-183\n7-45-90+\n8-90-180\n8-90-181\n8-91-180\n8-91-181+\n9-182-362\n9-182-363\n9-183-362\n9-183-363\n7-45-91\n6-23-44\n6-23-45\n5-11-23+\n6-22-46+\n7-44-92\n7-44-93+\n8-88-186\n8-88-187\n8-89-186\n8-89-187\n7-45-92\n7-45-93\n6-22-47\n6-23-46+\n7-46-92\n7-46-93+\n8-92-186\n8-92-187\n8-93-186+\n9-186-372+\n10-372-744\n10-372-745\n10-373-744\n10-373-745\n9-186-373\n9-187-372\n9-187-373\n8-93-187+\n9-186-374\n9-186-375\n9-187-374\n9-187-375\n7-47-92\n7-47-93\n6-23-47\n3-3-4+\n4-6-8\n4-6-9+\n5-12-18\n5-12-19\n5-13-18+\n6-26-36\n6-26-37\n6-27-36\n6-27-37\n5-13-19\n4-7-8+\n5-14-16\n5-14-17+\n6-28-34\n6-28-35\n6-29-34\n6-29-35\n5-15-16+\n6-30-32+\n7-60-64\n7-60-65\n7-61-64\n7-61-65\n6-30-33+\n7-60-66\n7-60-67+\n8-120-134\n8-120-135\n8-121-134\n8-121-135+\n9-242-270\n9-242-271+\n10-484-542\n10-484-543\n10-485-542\n10-485-543\n9-243-270\n9-243-271\n7-61-66\n7-61-67+\n8-122-134\n8-122-135\n8-123-134\n8-123-135+\n9-246-270\n9-246-271+\n10-492-542\n10-492-543\n10-493-542\n10-493-543\n9-247-270\n9-247-271\n6-31-32+\n7-62-64\n7-62-65\n7-63-64\n7-63-65\n6-31-33+\n7-62-66\n7-62-67\n7-63-66+\n8-126-132\n8-126-133\n8-127-132\n8-127-133+\n9-254-266\n9-254-267\n9-255-266\n9-255-267\n7-63-67+\n8-126-134+\n9-252-268\n9-252-269\n9-253-268\n9-253-269\n8-126-135\n8-127-134\n8-127-135\n5-15-17\n4-7-9\n3-3-5\n2-1-3+\n3-2-6+\n4-4-12+\n5-8-24+\n6-16-48\n6-16-49\n6-17-48\n6-17-49+\n7-34-98\n7-34-99\n7-35-98+\n8-70-196+\n9-140-392+\n10-280-784\n10-280-785\n10-281-784\n10-281-785\n9-140-393\n9-141-392\n9-141-393\n8-70-197\n8-71-196\n8-71-197\n7-35-99+\n8-70-198+\n9-140-396\n9-140-397\n9-141-396\n9-141-397\n8-70-199+\n9-140-398\n9-140-399\n9-141-398\n9-141-399\n8-71-198\n8-71-199\n5-8-25\n5-9-24\n5-9-25\n4-4-13+\n5-8-26\n5-8-27+\n6-16-54\n6-16-55\n6-17-54\n6-17-55\n5-9-26+\n6-18-52\n6-18-53\n6-19-52+\n7-38-104\n7-38-105\n7-39-104+\n8-78-208\n8-78-209\n8-79-208\n8-79-209\n7-39-105\n6-19-53+\n7-38-106\n7-38-107+\n8-76-214\n8-76-215\n8-77-214\n8-77-215\n7-39-106+\n8-78-212\n8-78-213\n8-79-212\n8-79-213\n7-39-107\n5-9-27+\n6-18-54\n6-18-55+\n7-36-110\n7-36-111\n7-37-110+\n8-74-220\n8-74-221\n8-75-220\n8-75-221\n7-37-111\n6-19-54+\n7-38-108+\n8-76-216\n8-76-217+\n9-152-434+\n10-304-868\n10-304-869\n10-305-868\n10-305-869\n9-152-435\n9-153-434\n9-153-435\n8-77-216\n8-77-217\n7-38-109+\n8-76-218\n8-76-219\n8-77-218\n8-77-219+\n9-154-438\n9-154-439\n9-155-438\n9-155-439\n7-39-108+\n8-78-216\n8-78-217\n8-79-216+\n9-158-432\n9-158-433\n9-159-432\n9-159-433\n8-79-217+\n9-158-434\n9-158-435+\n10-316-870\n10-316-871\n10-317-870\n10-317-871\n9-159-434\n9-159-435\n7-39-109\n6-19-55\n4-5-12\n4-5-13+\n5-10-26\n5-10-27\n5-11-26\n5-11-27+\n6-22-54+\n7-44-108\n7-44-109+\n8-88-218\n8-88-219+\n9-176-438+\n10-352-876\n10-352-877\n10-353-876\n10-353-877\n9-176-439+\n10-352-878\n10-352-879\n10-353-878\n10-353-879\n9-177-438\n9-177-439\n8-89-218+\n9-178-436\n9-178-437\n9-179-436\n9-179-437\n8-89-219\n7-45-108+\n8-90-216+\n9-180-432\n9-180-433\n9-181-432\n9-181-433\n8-90-217\n8-91-216+\n9-182-432\n9-182-433\n9-183-432\n9-183-433\n8-91-217\n7-45-109\n6-22-55\n6-23-54+\n7-46-108\n7-46-109+\n8-92-218\n8-92-219\n8-93-218\n8-93-219\n7-47-108\n7-47-109\n6-23-55\n3-2-7\n3-3-6\n3-3-7+\n4-6-14+\n5-12-28+\n6-24-56\n6-24-57\n6-25-56+\n7-50-112\n7-50-113\n7-51-112\n7-51-113\n6-25-57\n5-12-29\n5-13-28+\n6-26-56\n6-26-57+\n7-52-114\n7-52-115\n7-53-114\n7-53-115+\n8-106-230\n8-106-231\n8-107-230\n8-107-231\n6-27-56\n6-27-57\n5-13-29\n4-6-15\n4-7-14+\n5-14-28+\n6-28-56\n6-28-57+\n7-56-114+\n8-112-228\n8-112-229\n8-113-228\n8-113-229\n7-56-115\n7-57-114\n7-57-115+\n8-114-230+\n9-228-460\n9-228-461\n9-229-460\n9-229-461\n8-114-231+\n9-228-462\n9-228-463\n9-229-462\n9-229-463\n8-115-230\n8-115-231+\n9-230-462+\n10-460-924\n10-460-925\n10-461-924\n10-461-925\n9-230-463\n9-231-462\n9-231-463\n6-29-56\n6-29-57+\n7-58-114\n7-58-115\n7-59-114\n7-59-115\n5-14-29\n5-15-28+\n6-30-56+\n7-60-112\n7-60-113\n7-61-112+\n8-122-224\n8-122-225\n8-123-224\n8-123-225\n7-61-113\n6-30-57+\n7-60-114\n7-60-115+\n8-120-230\n8-120-231\n8-121-230\n8-121-231\n7-61-114\n7-61-115\n6-31-56+\n7-62-112\n7-62-113+\n8-124-226\n8-124-227\n8-125-226\n8-125-227\n7-63-112\n7-63-113\n6-31-57+\n7-62-114\n7-62-115+\n8-124-230\n8-124-231\n8-125-230\n8-125-231\n7-63-114\n7-63-115+\n8-126-230\n8-126-231\n8-127-230\n8-127-231\n5-15-29\n4-7-15+\n5-14-30+\n6-28-60+\n7-56-120+\n8-112-240\n8-112-241+\n9-224-482\n9-224-483\n9-225-482\n9-225-483\n8-113-240\n8-113-241\n7-56-121\n7-57-120\n7-57-121\n6-28-61+\n7-56-122\n7-56-123+\n8-112-246\n8-112-247\n8-113-246\n8-113-247\n7-57-122\n7-57-123+\n8-114-246\n8-114-247\n8-115-246\n8-115-247\n6-29-60\n6-29-61\n5-14-31\n5-15-30+\n6-30-60+\n7-60-120+\n8-120-240\n8-120-241\n8-121-240\n8-121-241\n7-60-121\n7-61-120\n7-61-121\n6-30-61\n6-31-60\n6-31-61\n5-15-31\n1-1-0+\n2-2-0+\n3-4-0\n3-4-1\n3-5-0+\n4-10-0\n4-10-1+\n5-20-2+\n6-40-4\n6-40-5+\n7-80-10+\n8-160-20\n8-160-21+\n9-320-42\n9-320-43\n9-321-42\n9-321-43+\n10-642-86\n10-642-87\n10-643-86\n10-643-87\n8-161-20\n8-161-21\n7-80-11\n7-81-10\n7-81-11\n6-41-4\n6-41-5\n5-20-3\n5-21-2\n5-21-3+\n6-42-6\n6-42-7\n6-43-6+\n7-86-12\n7-86-13\n7-87-12\n7-87-13\n6-43-7\n4-11-0+\n5-22-0\n5-22-1\n5-23-0\n5-23-1+\n6-46-2\n6-46-3\n6-47-2\n6-47-3+\n7-94-6+\n8-188-12+\n9-376-24\n9-376-25\n9-377-24\n9-377-25\n8-188-13+\n9-376-26\n9-376-27\n9-377-26\n9-377-27\n8-189-12\n8-189-13\n7-94-7+\n8-188-14\n8-188-15\n8-189-14\n8-189-15\n7-95-6\n7-95-7+\n8-190-14\n8-190-15\n8-191-14+\n9-382-28\n9-382-29\n9-383-28+\n10-766-56\n10-766-57\n10-767-56\n10-767-57\n9-383-29\n8-191-15\n4-11-1+\n5-22-2\n5-22-3+\n6-44-6+\n7-88-12\n7-88-13\n7-89-12+\n8-178-24\n8-178-25\n8-179-24+\n9-358-48\n9-358-49\n9-359-48\n9-359-49+\n10-718-98\n10-718-99\n10-719-98\n10-719-99\n8-179-25\n7-89-13+\n8-178-26+\n9-356-52\n9-356-53\n9-357-52\n9-357-53+\n10-714-106\n10-714-107\n10-715-106\n10-715-107\n8-178-27\n8-179-26\n8-179-27+\n9-358-54\n9-358-55\n9-359-54\n9-359-55\n6-44-7\n6-45-6\n6-45-7\n5-23-2\n5-23-3\n3-5-1+\n4-10-2+\n5-20-4+\n6-40-8\n6-40-9\n6-41-8+\n7-82-16\n7-82-17\n7-83-16\n7-83-17\n6-41-9\n5-20-5+\n6-40-10+\n7-80-20+\n8-160-40\n8-160-41\n8-161-40+\n9-322-80\n9-322-81\n9-323-80\n9-323-81\n8-161-41\n7-80-21\n7-81-20\n7-81-21\n6-40-11\n6-41-10+\n7-82-20\n7-82-21+\n8-164-42+\n9-328-84\n9-328-85\n9-329-84\n9-329-85\n8-164-43\n8-165-42\n8-165-43\n7-83-20\n7-83-21\n6-41-11\n5-21-4+\n6-42-8+\n7-84-16\n7-84-17\n7-85-16\n7-85-17\n6-42-9\n6-43-8\n6-43-9+\n7-86-18\n7-86-19\n7-87-18\n7-87-19\n5-21-5+\n6-42-10+\n7-84-20\n7-84-21\n7-85-20\n7-85-21+\n8-170-42\n8-170-43\n8-171-42\n8-171-43\n6-42-11\n6-43-10+\n7-86-20\n7-86-21+\n8-172-42\n8-172-43\n8-173-42\n8-173-43\n7-87-20\n7-87-21+\n8-174-42\n8-174-43\n8-175-42\n8-175-43\n6-43-11\n4-10-3\n4-11-2\n4-11-3\n2-2-1+\n3-4-2+\n4-8-4+\n5-16-8\n5-16-9+\n6-32-18+\n7-64-36\n7-64-37\n7-65-36+\n8-130-72\n8-130-73\n8-131-72\n8-131-73\n7-65-37+\n8-130-74\n8-130-75\n8-131-74\n8-131-75\n6-32-19+\n7-64-38\n7-64-39+\n8-128-78\n8-128-79\n8-129-78\n8-129-79\n7-65-38\n7-65-39+\n8-130-78\n8-130-79\n8-131-78+\n9-262-156\n9-262-157\n9-263-156\n9-263-157\n8-131-79+\n9-262-158\n9-262-159\n9-263-158\n9-263-159\n6-33-18+\n7-66-36\n7-66-37\n7-67-36\n7-67-37\n6-33-19+\n7-66-38\n7-66-39\n7-67-38\n7-67-39+\n8-134-78\n8-134-79\n8-135-78\n8-135-79+\n9-270-158\n9-270-159\n9-271-158\n9-271-159\n5-17-8+\n6-34-16\n6-34-17\n6-35-16+\n7-70-32+\n8-140-64\n8-140-65\n8-141-64\n8-141-65\n7-70-33\n7-71-32\n7-71-33+\n8-142-66+\n9-284-132\n9-284-133\n9-285-132\n9-285-133\n8-142-67+\n9-284-134\n9-284-135\n9-285-134\n9-285-135\n8-143-66\n8-143-67\n6-35-17\n5-17-9\n4-8-5+\n5-16-10+\n6-32-20+\n7-64-40+\n8-128-80\n8-128-81\n8-129-80\n8-129-81\n7-64-41\n7-65-40\n7-65-41\n6-32-21\n6-33-20\n6-33-21\n5-16-11+\n6-32-22\n6-32-23+\n7-64-46\n7-64-47\n7-65-46\n7-65-47+\n8-130-94\n8-130-95\n8-131-94\n8-131-95\n6-33-22\n6-33-23\n5-17-10+\n6-34-20\n6-34-21+\n7-68-42\n7-68-43\n7-69-42+\n8-138-84\n8-138-85\n8-139-84\n8-139-85\n7-69-43\n6-35-20\n6-35-21\n5-17-11+\n6-34-22\n6-34-23\n6-35-22+\n7-70-44\n7-70-45\n7-71-44+\n8-142-88\n8-142-89\n8-143-88+\n9-286-176\n9-286-177\n9-287-176\n9-287-177\n8-143-89\n7-71-45\n6-35-23\n4-9-4+\n5-18-8\n5-18-9\n5-19-8\n5-19-9+\n6-38-18\n6-38-19\n6-39-18+\n7-78-36+\n8-156-72\n8-156-73\n8-157-72\n8-157-73\n7-78-37\n7-79-36\n7-79-37+\n8-158-74\n8-158-75\n8-159-74\n8-159-75\n6-39-19\n4-9-5+\n5-18-10+\n6-36-20\n6-36-21\n6-37-20\n6-37-21\n5-18-11+\n6-36-22\n6-36-23\n6-37-22\n6-37-23+\n7-74-46\n7-74-47\n7-75-46\n7-75-47\n5-19-10+\n6-38-20+\n7-76-40\n7-76-41\n7-77-40+\n8-154-80\n8-154-81+\n9-308-162\n9-308-163\n9-309-162+\n10-618-324\n10-618-325\n10-619-324\n10-619-325\n9-309-163\n8-155-80\n8-155-81\n7-77-41+\n8-154-82\n8-154-83\n8-155-82\n8-155-83\n6-38-21+\n7-76-42\n7-76-43+\n8-152-86\n8-152-87\n8-153-86\n8-153-87+\n9-306-174\n9-306-175\n9-307-174\n9-307-175\n7-77-42\n7-77-43\n6-39-20+\n7-78-40\n7-78-41\n7-79-40\n7-79-41\n6-39-21\n5-19-11+\n6-38-22+\n7-76-44+\n8-152-88\n8-152-89\n8-153-88\n8-153-89+\n9-306-178\n9-306-179\n9-307-178\n9-307-179\n7-76-45\n7-77-44+\n8-154-88\n8-154-89\n8-155-88\n8-155-89+\n9-310-178\n9-310-179\n9-311-178+\n10-622-356\n10-622-357\n10-623-356\n10-623-357\n9-311-179+\n10-622-358\n10-622-359\n10-623-358\n10-623-359\n7-77-45\n6-38-23\n6-39-22\n6-39-23+\n7-78-46\n7-78-47\n7-79-46+\n8-158-92\n8-158-93\n8-159-92\n8-159-93\n7-79-47\n3-4-3\n3-5-2+\n4-10-4+\n5-20-8+\n6-40-16\n6-40-17+\n7-80-34+\n8-160-68\n8-160-69\n8-161-68\n8-161-69\n7-80-35+\n8-160-70\n8-160-71\n8-161-70\n8-161-71\n7-81-34\n7-81-35\n6-41-16\n6-41-17\n5-20-9+\n6-40-18\n6-40-19+\n7-80-38\n7-80-39\n7-81-38+\n8-162-76\n8-162-77\n8-163-76\n8-163-77+\n9-326-154\n9-326-155\n9-327-154\n9-327-155\n7-81-39\n6-41-18\n6-41-19+\n7-82-38+\n8-164-76\n8-164-77\n8-165-76\n8-165-77\n7-82-39+\n8-164-78\n8-164-79\n8-165-78\n8-165-79\n7-83-38+\n8-166-76\n8-166-77\n8-167-76+\n9-334-152\n9-334-153\n9-335-152\n9-335-153\n8-167-77\n7-83-39+\n8-166-78\n8-166-79\n8-167-78\n8-167-79\n5-21-8+\n6-42-16\n6-42-17+\n7-84-34\n7-84-35\n7-85-34+\n8-170-68\n8-170-69\n8-171-68\n8-171-69\n7-85-35\n6-43-16\n6-43-17\n5-21-9+\n6-42-18+\n7-84-36\n7-84-37\n7-85-36+\n8-170-72\n8-170-73\n8-171-72\n8-171-73\n7-85-37\n6-42-19+\n7-84-38\n7-84-39+\n8-168-78\n8-168-79\n8-169-78+\n9-338-156\n9-338-157\n9-339-156\n9-339-157+\n10-678-314\n10-678-315\n10-679-314\n10-679-315\n8-169-79\n7-85-38\n7-85-39\n6-43-18+\n7-86-36\n7-86-37+\n8-172-74+\n9-344-148\n9-344-149\n9-345-148\n9-345-149\n8-172-75\n8-173-74\n8-173-75\n7-87-36+\n8-174-72\n8-174-73\n8-175-72+\n9-350-144\n9-350-145\n9-351-144\n9-351-145\n8-175-73\n7-87-37\n6-43-19\n4-10-5+\n5-20-10\n5-20-11\n5-21-10\n5-21-11+\n6-42-22\n6-42-23+\n7-84-46\n7-84-47\n7-85-46\n7-85-47\n6-43-22+\n7-86-44\n7-86-45\n7-87-44\n7-87-45\n6-43-23\n4-11-4+\n5-22-8+\n6-44-16\n6-44-17+\n7-88-34\n7-88-35\n7-89-34\n7-89-35\n6-45-16\n6-45-17\n5-22-9\n5-23-8+\n6-46-16+\n7-92-32\n7-92-33\n7-93-32\n7-93-33+\n8-186-66\n8-186-67\n8-187-66\n8-187-67\n6-46-17+\n7-92-34+\n8-184-68\n8-184-69\n8-185-68\n8-185-69\n7-92-35+\n8-184-70\n8-184-71\n8-185-70\n8-185-71\n7-93-34\n7-93-35+\n8-186-70+\n9-372-140\n9-372-141\n9-373-140\n9-373-141\n8-186-71\n8-187-70\n8-187-71\n6-47-16\n6-47-17+\n7-94-34\n7-94-35\n7-95-34+\n8-190-68\n8-190-69\n8-191-68\n8-191-69+\n9-382-138\n9-382-139\n9-383-138\n9-383-139\n7-95-35+\n8-190-70\n8-190-71+\n9-380-142+\n10-760-284\n10-760-285\n10-761-284\n10-761-285\n9-380-143+\n10-760-286\n10-760-287\n10-761-286\n10-761-287\n9-381-142\n9-381-143\n8-191-70\n8-191-71\n5-23-9\n4-11-5+\n5-22-10\n5-22-11\n5-23-10+\n6-46-20+\n7-92-40+\n8-184-80\n8-184-81+\n9-368-162\n9-368-163\n9-369-162\n9-369-163+\n10-738-326\n10-738-327\n10-739-326\n10-739-327\n8-185-80\n8-185-81\n7-92-41\n7-93-40\n7-93-41\n6-46-21+\n7-92-42\n7-92-43\n7-93-42+\n8-186-84\n8-186-85\n8-187-84\n8-187-85\n7-93-43+\n8-186-86\n8-186-87\n8-187-86\n8-187-87\n6-47-20\n6-47-21+\n7-94-42+\n8-188-84\n8-188-85+\n9-376-170\n9-376-171\n9-377-170\n9-377-171\n8-189-84\n8-189-85\n7-94-43+\n8-188-86+\n9-376-172\n9-376-173\n9-377-172\n9-377-173\n8-188-87\n8-189-86\n8-189-87\n7-95-42\n7-95-43\n5-23-11\n3-5-3+\n4-10-6\n4-10-7\n4-11-6+\n5-22-12\n5-22-13\n5-23-12+\n6-46-24+\n7-92-48\n7-92-49\n7-93-48\n7-93-49+\n8-186-98\n8-186-99\n8-187-98\n8-187-99\n6-46-25+\n7-92-50\n7-92-51\n7-93-50+\n8-186-100\n8-186-101\n8-187-100\n8-187-101\n7-93-51\n6-47-24\n6-47-25\n5-23-13\n4-11-7\n2-3-0+\n3-6-0\n3-6-1+\n4-12-2\n4-12-3+\n5-24-6\n5-24-7\n5-25-6+\n6-50-12\n6-50-13\n6-51-12+\n7-102-24+\n8-204-48\n8-204-49\n8-205-48+\n9-410-96\n9-410-97\n9-411-96\n9-411-97\n8-205-49\n7-102-25\n7-103-24\n7-103-25+\n8-206-50\n8-206-51\n8-207-50\n8-207-51\n6-51-13+\n7-102-26\n7-102-27\n7-103-26\n7-103-27+\n8-206-54\n8-206-55\n8-207-54\n8-207-55\n5-25-7\n4-13-2+\n5-26-4\n5-26-5+\n6-52-10+\n7-104-20\n7-104-21\n7-105-20\n7-105-21\n6-52-11+\n7-104-22\n7-104-23+\n8-208-46+\n9-416-92\n9-416-93\n9-417-92\n9-417-93\n8-208-47\n8-209-46\n8-209-47\n7-105-22+\n8-210-44\n8-210-45+\n9-420-90+\n10-840-180\n10-840-181\n10-841-180\n10-841-181\n9-420-91\n9-421-90\n9-421-91\n8-211-44\n8-211-45\n7-105-23\n6-53-10\n6-53-11\n5-27-4+\n6-54-8\n6-54-9\n6-55-8+\n7-110-16\n7-110-17+\n8-220-34\n8-220-35\n8-221-34\n8-221-35+\n9-442-70\n9-442-71\n9-443-70+\n10-886-140\n10-886-141\n10-887-140\n10-887-141\n9-443-71\n7-111-16+\n8-222-32+\n9-444-64\n9-444-65\n9-445-64\n9-445-65\n8-222-33+\n9-444-66\n9-444-67\n9-445-66\n9-445-67+\n10-890-134\n10-890-135\n10-891-134\n10-891-135\n8-223-32\n8-223-33\n7-111-17\n6-55-9+\n7-110-18\n7-110-19\n7-111-18\n7-111-19\n5-27-5\n4-13-3\n3-7-0+\n4-14-0+\n5-28-0\n5-28-1+\n6-56-2+\n7-112-4\n7-112-5+\n8-224-10\n8-224-11+\n9-448-22\n9-448-23\n9-449-22\n9-449-23\n8-225-10\n8-225-11\n7-113-4\n7-113-5+\n8-226-10\n8-226-11\n8-227-10\n8-227-11+\n9-454-22\n9-454-23\n9-455-22\n9-455-23\n6-56-3+\n7-112-6\n7-112-7+\n8-224-14\n8-224-15\n8-225-14\n8-225-15+\n9-450-30\n9-450-31\n9-451-30\n9-451-31\n7-113-6\n7-113-7+\n8-226-14\n8-226-15+\n9-452-30\n9-452-31\n9-453-30\n9-453-31\n8-227-14+\n9-454-28\n9-454-29\n9-455-28\n9-455-29\n8-227-15\n6-57-2\n6-57-3\n5-29-0\n5-29-1\n4-14-1+\n5-28-2+\n6-56-4\n6-56-5+\n7-112-10\n7-112-11\n7-113-10\n7-113-11+\n8-226-22\n8-226-23\n8-227-22\n8-227-23\n6-57-4\n6-57-5\n5-28-3\n5-29-2+\n6-58-4+\n7-116-8+\n8-232-16\n8-232-17\n8-233-16\n8-233-17\n7-116-9\n7-117-8\n7-117-9\n6-58-5+\n7-116-10\n7-116-11\n7-117-10\n7-117-11+\n8-234-22\n8-234-23\n8-235-22\n8-235-23\n6-59-4\n6-59-5\n5-29-3\n4-15-0+\n5-30-0\n5-30-1\n5-31-0\n5-31-1\n4-15-1+\n5-30-2\n5-30-3+\n6-60-6\n6-60-7\n6-61-6\n6-61-7\n5-31-2\n5-31-3+\n6-62-6+\n7-124-12\n7-124-13\n7-125-12\n7-125-13\n6-62-7\n6-63-6+\n7-126-12\n7-126-13\n7-127-12+\n8-254-24\n8-254-25\n8-255-24\n8-255-25+\n9-510-50\n9-510-51\n9-511-50\n9-511-51\n7-127-13\n6-63-7\n3-7-1\n2-3-1+\n3-6-2\n3-6-3\n3-7-2+\n4-14-4+\n5-28-8+\n6-56-16\n6-56-17\n6-57-16\n6-57-17+\n7-114-34\n7-114-35\n7-115-34\n7-115-35\n5-28-9+\n6-56-18\n6-56-19\n6-57-18\n6-57-19\n5-29-8+\n6-58-16\n6-58-17\n6-59-16+\n7-118-32\n7-118-33\n7-119-32+\n8-238-64\n8-238-65+\n9-476-130\n9-476-131+\n10-952-262\n10-952-263\n10-953-262\n10-953-263\n9-477-130\n9-477-131\n8-239-64\n8-239-65\n7-119-33\n6-59-17\n5-29-9+\n6-58-18+\n7-116-36\n7-116-37+\n8-232-74\n8-232-75\n8-233-74\n8-233-75\n7-117-36\n7-117-37+\n8-234-74\n8-234-75\n8-235-74\n8-235-75\n6-58-19+\n7-116-38\n7-116-39\n7-117-38+\n8-234-76\n8-234-77\n8-235-76\n8-235-77+\n9-470-154+\n10-940-308\n10-940-309\n10-941-308\n10-941-309\n9-470-155\n9-471-154\n9-471-155\n7-117-39+\n8-234-78\n8-234-79+\n9-468-158\n9-468-159\n9-469-158\n9-469-159\n8-235-78\n8-235-79\n6-59-18\n6-59-19+\n7-118-38\n7-118-39+\n8-236-78\n8-236-79+\n9-472-158+\n10-944-316\n10-944-317\n10-945-316\n10-945-317\n9-472-159\n9-473-158\n9-473-159\n8-237-78\n8-237-79\n7-119-38\n7-119-39+\n8-238-78\n8-238-79\n8-239-78\n8-239-79+\n9-478-158\n9-478-159\n9-479-158\n9-479-159\n4-14-5\n4-15-4+\n5-30-8+\n6-60-16\n6-60-17\n6-61-16+\n7-122-32\n7-122-33+\n8-244-66\n8-244-67+\n9-488-134\n9-488-135+\n10-976-270\n10-976-271\n10-977-270\n10-977-271\n9-489-134\n9-489-135\n8-245-66+\n9-490-132\n9-490-133\n9-491-132\n9-491-133\n8-245-67\n7-123-32\n7-123-33\n6-61-17\n5-30-9\n5-31-8+\n6-62-16+\n7-124-32+\n8-248-64\n8-248-65\n8-249-64\n8-249-65\n7-124-33\n7-125-32\n7-125-33\n6-62-17\n6-63-16\n6-63-17\n5-31-9+\n6-62-18\n6-62-19\n6-63-18+\n7-126-36+\n8-252-72+\n9-504-144\n9-504-145\n9-505-144\n9-505-145\n8-252-73\n8-253-72\n8-253-73\n7-126-37+\n8-252-74\n8-252-75\n8-253-74\n8-253-75\n7-127-36\n7-127-37\n6-63-19+\n7-126-38+\n8-252-76\n8-252-77\n8-253-76\n8-253-77\n7-126-39\n7-127-38\n7-127-39\n4-15-5\n3-7-3+\n4-14-6\n4-14-7\n4-15-6\n4-15-7+\n5-30-14+\n6-60-28+\n7-120-56\n7-120-57\n7-121-56\n7-121-57\n6-60-29\n6-61-28+\n7-122-56\n7-122-57\n7-123-56\n7-123-57+\n8-246-114\n8-246-115\n8-247-114\n8-247-115\n6-61-29\n5-30-15\n5-31-14\n5-31-15\n1-1-1+\n2-2-2+\n3-4-4+\n4-8-8+\n5-16-16\n5-16-17\n5-17-16\n5-17-17\n4-8-9+\n5-16-18+\n6-32-36\n6-32-37+\n7-64-74\n7-64-75+\n8-128-150\n8-128-151\n8-129-150\n8-129-151+\n9-258-302\n9-258-303\n9-259-302\n9-259-303\n7-65-74\n7-65-75+\n8-130-150\n8-130-151\n8-131-150\n8-131-151\n6-33-36+\n7-66-72\n7-66-73\n7-67-72\n7-67-73\n6-33-37\n5-16-19\n5-17-18\n5-17-19\n4-9-8+\n5-18-16\n5-18-17+\n6-36-34+\n7-72-68\n7-72-69\n7-73-68+\n8-146-136\n8-146-137\n8-147-136\n8-147-137\n7-73-69\n6-36-35+\n7-72-70+\n8-144-140\n8-144-141+\n9-288-282\n9-288-283\n9-289-282\n9-289-283\n8-145-140\n8-145-141+\n9-290-282\n9-290-283\n9-291-282\n9-291-283\n7-72-71+\n8-144-142+\n9-288-284\n9-288-285\n9-289-284\n9-289-285\n8-144-143\n8-145-142+\n9-290-284\n9-290-285\n9-291-284\n9-291-285\n8-145-143\n7-73-70\n7-73-71+\n8-146-142\n8-146-143+\n9-292-286\n9-292-287\n9-293-286\n9-293-287\n8-147-142\n8-147-143\n6-37-34\n6-37-35\n5-19-16\n5-19-17\n4-9-9+\n5-18-18+\n6-36-36+\n7-72-72\n7-72-73\n7-73-72\n7-73-73\n6-36-37\n6-37-36\n6-37-37+\n7-74-74+\n8-148-148+\n9-296-296+\n10-592-592\n10-592-593\n10-593-592\n10-593-593\n9-296-297\n9-297-296\n9-297-297\n8-148-149\n8-149-148\n8-149-149\n7-74-75\n7-75-74+\n8-150-148\n8-150-149+\n9-300-298\n9-300-299\n9-301-298\n9-301-299\n8-151-148\n8-151-149\n7-75-75\n5-18-19\n5-19-18\n5-19-19\n3-4-5+\n4-8-10+\n5-16-20\n5-16-21+\n6-32-42\n6-32-43+\n7-64-86+\n8-128-172\n8-128-173\n8-129-172\n8-129-173\n7-64-87\n7-65-86\n7-65-87\n6-33-42\n6-33-43+\n7-66-86\n7-66-87\n7-67-86\n7-67-87+\n8-134-174+\n9-268-348\n9-268-349+\n10-536-698\n10-536-699\n10-537-698\n10-537-699\n9-269-348\n9-269-349\n8-134-175+\n9-268-350\n9-268-351\n9-269-350\n9-269-351\n8-135-174\n8-135-175\n5-17-20\n5-17-21\n4-8-11\n4-9-10+\n5-18-20\n5-18-21+\n6-36-42+\n7-72-84\n7-72-85+\n8-144-170\n8-144-171\n8-145-170\n8-145-171\n7-73-84+\n8-146-168\n8-146-169\n8-147-168\n8-147-169\n7-73-85\n6-36-43\n6-37-42+\n7-74-84+\n8-148-168\n8-148-169\n8-149-168\n8-149-169+\n9-298-338\n9-298-339\n9-299-338+\n10-598-676\n10-598-677\n10-599-676\n10-599-677\n9-299-339\n7-74-85\n7-75-84+\n8-150-168\n8-150-169\n8-151-168\n8-151-169\n7-75-85\n6-37-43+\n7-74-86\n7-74-87\n7-75-86\n7-75-87\n5-19-20\n5-19-21+\n6-38-42\n6-38-43\n6-39-42\n6-39-43+\n7-78-86\n7-78-87+\n8-156-174\n8-156-175+\n9-312-350\n9-312-351\n9-313-350\n9-313-351\n8-157-174\n8-157-175\n7-79-86\n7-79-87\n4-9-11\n3-5-4+\n4-10-8+\n5-20-16\n5-20-17+\n6-40-34+\n7-80-68\n7-80-69+\n8-160-138+\n9-320-276\n9-320-277\n9-321-276\n9-321-277\n8-160-139+\n9-320-278\n9-320-279\n9-321-278\n9-321-279\n8-161-138\n8-161-139\n7-81-68\n7-81-69\n6-40-35\n6-41-34+\n7-82-68\n7-82-69\n7-83-68\n7-83-69\n6-41-35\n5-21-16+\n6-42-32\n6-42-33+\n7-84-66\n7-84-67\n7-85-66+\n8-170-132\n8-170-133\n8-171-132\n8-171-133\n7-85-67\n6-43-32+\n7-86-64\n7-86-65\n7-87-64\n7-87-65\n6-43-33\n5-21-17\n4-10-9+\n5-20-18+\n6-40-36\n6-40-37\n6-41-36\n6-41-37\n5-20-19\n5-21-18+\n6-42-36\n6-42-37\n6-43-36\n6-43-37\n5-21-19+\n6-42-38\n6-42-39\n6-43-38\n6-43-39\n4-11-8\n4-11-9\n3-5-5+\n4-10-10\n4-10-11\n4-11-10+\n5-22-20\n5-22-21+\n6-44-42\n6-44-43\n6-45-42\n6-45-43\n5-23-20+\n6-46-40+\n7-92-80\n7-92-81\n7-93-80\n7-93-81\n6-46-41\n6-47-40+\n7-94-80+\n8-188-160\n8-188-161\n8-189-160\n8-189-161\n7-94-81\n7-95-80\n7-95-81\n6-47-41\n5-23-21\n4-11-11\n2-2-3+\n3-4-6\n3-4-7+\n4-8-14\n4-8-15+\n5-16-30+\n6-32-60\n6-32-61+\n7-64-122\n7-64-123\n7-65-122\n7-65-123+\n8-130-246\n8-130-247\n8-131-246\n8-131-247\n6-33-60\n6-33-61\n5-16-31+\n6-32-62\n6-32-63\n6-33-62+\n7-66-124\n7-66-125+\n8-132-250\n8-132-251\n8-133-250\n8-133-251\n7-67-124+\n8-134-248\n8-134-249\n8-135-248\n8-135-249\n7-67-125+\n8-134-250\n8-134-251\n8-135-250\n8-135-251\n6-33-63+\n7-66-126+\n8-132-252\n8-132-253\n8-133-252\n8-133-253\n7-66-127\n7-67-126\n7-67-127\n5-17-30\n5-17-31+\n6-34-62\n6-34-63+\n7-68-126\n7-68-127\n7-69-126\n7-69-127\n6-35-62\n6-35-63\n4-9-14+\n5-18-28\n5-18-29\n5-19-28+\n6-38-56\n6-38-57\n6-39-56+\n7-78-112\n7-78-113+\n8-156-226\n8-156-227\n8-157-226\n8-157-227\n7-79-112\n7-79-113\n6-39-57\n5-19-29+\n6-38-58\n6-38-59\n6-39-58\n6-39-59+\n7-78-118\n7-78-119\n7-79-118\n7-79-119\n4-9-15\n3-5-6\n3-5-7+\n4-10-14+\n5-20-28+\n6-40-56\n6-40-57\n6-41-56\n6-41-57\n5-20-29\n5-21-28+\n6-42-56+\n7-84-112\n7-84-113\n7-85-112\n7-85-113\n6-42-57\n6-43-56\n6-43-57+\n7-86-114+\n8-172-228\n8-172-229\n8-173-228\n8-173-229\n7-86-115\n7-87-114\n7-87-115\n5-21-29\n4-10-15\n4-11-14+\n5-22-28+\n6-44-56\n6-44-57+\n7-88-114+\n8-176-228\n8-176-229\n8-177-228\n8-177-229\n7-88-115\n7-89-114\n7-89-115\n6-45-56\n6-45-57\n5-22-29+\n6-44-58\n6-44-59\n6-45-58\n6-45-59\n5-23-28\n5-23-29\n4-11-15+\n5-22-30\n5-22-31+\n6-44-62\n6-44-63\n6-45-62\n6-45-63+\n7-90-126\n7-90-127+\n8-180-254\n8-180-255\n8-181-254\n8-181-255\n7-91-126\n7-91-127\n5-23-30+\n6-46-60\n6-46-61\n6-47-60\n6-47-61\n5-23-31\n2-3-2+\n3-6-4+\n4-12-8+\n5-24-16+\n6-48-32\n6-48-33\n6-49-32\n6-49-33\n5-24-17+\n6-48-34+\n7-96-68\n7-96-69\n7-97-68+\n8-194-136\n8-194-137\n8-195-136\n8-195-137+\n9-390-274\n9-390-275\n9-391-274\n9-391-275\n7-97-69+\n8-194-138\n8-194-139+\n9-388-278+\n10-776-556\n10-776-557\n10-777-556\n10-777-557\n9-388-279\n9-389-278+\n10-778-556\n10-778-557\n10-779-556\n10-779-557\n9-389-279\n8-195-138+\n9-390-276\n9-390-277\n9-391-276\n9-391-277\n8-195-139\n6-48-35\n6-49-34+\n7-98-68\n7-98-69+\n8-196-138\n8-196-139\n8-197-138\n8-197-139\n7-99-68\n7-99-69\n6-49-35+\n7-98-70+\n8-196-140\n8-196-141\n8-197-140\n8-197-141\n7-98-71\n7-99-70\n7-99-71+\n8-198-142\n8-198-143\n8-199-142\n8-199-143+\n9-398-286\n9-398-287\n9-399-286+\n10-798-572\n10-798-573\n10-799-572\n10-799-573\n9-399-287\n5-25-16\n5-25-17+\n6-50-34\n6-50-35\n6-51-34\n6-51-35\n4-12-9\n4-13-8+\n5-26-16\n5-26-17\n5-27-16+\n6-54-32\n6-54-33\n6-55-32+\n7-110-64\n7-110-65\n7-111-64+\n8-222-128\n8-222-129\n8-223-128+\n9-446-256\n9-446-257\n9-447-256\n9-447-257\n8-223-129\n7-111-65\n6-55-33\n5-27-17\n4-13-9+\n5-26-18+\n6-52-36\n6-52-37+\n7-104-74+\n8-208-148\n8-208-149\n8-209-148\n8-209-149\n7-104-75+\n8-208-150\n8-208-151\n8-209-150\n8-209-151+\n9-418-302\n9-418-303\n9-419-302\n9-419-303\n7-105-74\n7-105-75\n6-53-36\n6-53-37+\n7-106-74\n7-106-75+\n8-212-150\n8-212-151\n8-213-150\n8-213-151\n7-107-74\n7-107-75\n5-26-19+\n6-52-38+\n7-104-76\n7-104-77\n7-105-76+\n8-210-152\n8-210-153+\n9-420-306\n9-420-307\n9-421-306\n9-421-307\n8-211-152\n8-211-153\n7-105-77\n6-52-39\n6-53-38+\n7-106-76+\n8-212-152\n8-212-153\n8-213-152\n8-213-153\n7-106-77\n7-107-76+\n8-214-152\n8-214-153\n8-215-152\n8-215-153+\n9-430-306\n9-430-307\n9-431-306\n9-431-307\n7-107-77\n6-53-39+\n7-106-78\n7-106-79\n7-107-78\n7-107-79+\n8-214-158\n8-214-159\n8-215-158\n8-215-159\n5-27-18\n5-27-19+\n6-54-38\n6-54-39\n6-55-38+\n7-110-76\n7-110-77\n7-111-76\n7-111-77\n6-55-39+\n7-110-78+\n8-220-156\n8-220-157\n8-221-156\n8-221-157\n7-110-79\n7-111-78\n7-111-79\n3-6-5+\n4-12-10+\n5-24-20+\n6-48-40+\n7-96-80\n7-96-81+\n8-192-162\n8-192-163+\n9-384-326\n9-384-327\n9-385-326\n9-385-327\n8-193-162\n8-193-163\n7-97-80\n7-97-81+\n8-194-162\n8-194-163\n8-195-162\n8-195-163\n6-48-41+\n7-96-82\n7-96-83+\n8-192-166+\n9-384-332\n9-384-333\n9-385-332\n9-385-333\n8-192-167\n8-193-166\n8-193-167\n7-97-82\n7-97-83\n6-49-40+\n7-98-80\n7-98-81\n7-99-80+\n8-198-160\n8-198-161\n8-199-160\n8-199-161\n7-99-81\n6-49-41\n5-24-21+\n6-48-42+\n7-96-84\n7-96-85+\n8-192-170\n8-192-171\n8-193-170+\n9-386-340\n9-386-341\n9-387-340\n9-387-341\n8-193-171\n7-97-84\n7-97-85+\n8-194-170\n8-194-171\n8-195-170+\n9-390-340\n9-390-341\n9-391-340\n9-391-341\n8-195-171+\n9-390-342\n9-390-343\n9-391-342\n9-391-343+\n10-782-686\n10-782-687\n10-783-686\n10-783-687\n6-48-43\n6-49-42\n6-49-43\n5-25-20+\n6-50-40\n6-50-41\n6-51-40\n6-51-41\n5-25-21+\n6-50-42\n6-50-43\n6-51-42+\n7-102-84+\n8-204-168+\n9-408-336\n9-408-337\n9-409-336+\n10-818-672\n10-818-673\n10-819-672\n10-819-673\n9-409-337\n8-204-169\n8-205-168\n8-205-169\n7-102-85\n7-103-84+\n8-206-168+\n9-412-336\n9-412-337\n9-413-336\n9-413-337\n8-206-169\n8-207-168\n8-207-169\n7-103-85\n6-51-43\n4-12-11+\n5-24-22+\n6-48-44\n6-48-45\n6-49-44\n6-49-45\n5-24-23\n5-25-22+\n6-50-44+\n7-100-88\n7-100-89\n7-101-88\n7-101-89\n6-50-45\n6-51-44+\n7-102-88\n7-102-89+\n8-204-178+\n9-408-356\n9-408-357+\n10-816-714\n10-816-715\n10-817-714\n10-817-715\n9-409-356\n9-409-357\n8-204-179\n8-205-178+\n9-410-356\n9-410-357\n9-411-356+\n10-822-712\n10-822-713\n10-823-712\n10-823-713\n9-411-357+\n10-822-714\n10-822-715\n10-823-714\n10-823-715\n8-205-179+\n9-410-358\n9-410-359\n9-411-358\n9-411-359\n7-103-88+\n8-206-176+\n9-412-352\n9-412-353\n9-413-352\n9-413-353\n8-206-177\n8-207-176\n8-207-177+\n9-414-354\n9-414-355\n9-415-354\n9-415-355\n7-103-89+\n8-206-178\n8-206-179\n8-207-178+\n9-414-356\n9-414-357+\n10-828-714\n10-828-715\n10-829-714\n10-829-715\n9-415-356\n9-415-357\n8-207-179\n6-51-45\n5-25-23\n4-13-10+\n5-26-20\n5-26-21+\n6-52-42+\n7-104-84\n7-104-85\n7-105-84\n7-105-85+\n8-210-170\n8-210-171\n8-211-170\n8-211-171\n6-52-43\n6-53-42\n6-53-43\n5-27-20\n5-27-21+\n6-54-42\n6-54-43+\n7-108-86+\n8-216-172\n8-216-173\n8-217-172+\n9-434-344+\n10-868-688\n10-868-689\n10-869-688\n10-869-689\n9-434-345\n9-435-344\n9-435-345\n8-217-173\n7-108-87\n7-109-86\n7-109-87+\n8-218-174\n8-218-175\n8-219-174\n8-219-175+\n9-438-350\n9-438-351\n9-439-350\n9-439-351\n6-55-42\n6-55-43\n4-13-11+\n5-26-22\n5-26-23\n5-27-22+\n6-54-44+\n7-108-88+\n8-216-176\n8-216-177\n8-217-176\n8-217-177\n7-108-89\n7-109-88\n7-109-89\n6-54-45\n6-55-44\n6-55-45+\n7-110-90\n7-110-91+\n8-220-182\n8-220-183\n8-221-182\n8-221-183\n7-111-90\n7-111-91\n5-27-23\n3-7-4\n3-7-5\n2-3-3+\n3-6-6\n3-6-7\n3-7-6\n3-7-7+\n4-14-14+\n5-28-28+\n6-56-56\n6-56-57+\n7-112-114\n7-112-115+\n8-224-230\n8-224-231+\n9-448-462\n9-448-463\n9-449-462\n9-449-463\n8-225-230\n8-225-231\n7-113-114\n7-113-115\n6-57-56+\n7-114-112\n7-114-113\n7-115-112\n7-115-113+\n8-230-226+\n9-460-452\n9-460-453\n9-461-452\n9-461-453\n8-230-227+\n9-460-454\n9-460-455\n9-461-454\n9-461-455+\n10-922-910\n10-922-911\n10-923-910\n10-923-911\n8-231-226\n8-231-227\n6-57-57\n5-28-29+\n6-56-58+\n7-112-116+\n8-224-232+\n9-448-464\n9-448-465\n9-449-464\n9-449-465\n8-224-233\n8-225-232\n8-225-233+\n9-450-466\n9-450-467\n9-451-466\n9-451-467\n7-112-117\n7-113-116\n7-113-117\n6-56-59+\n7-112-118\n7-112-119\n7-113-118\n7-113-119\n6-57-58+\n7-114-116\n7-114-117\n7-115-116\n7-115-117\n6-57-59\n5-29-28\n5-29-29\n4-14-15\n4-15-14+\n5-30-28+\n6-60-56\n6-60-57+\n7-120-114\n7-120-115\n7-121-114\n7-121-115\n6-61-56\n6-61-57\n5-30-29+\n6-60-58+\n7-120-116+\n8-240-232\n8-240-233\n8-241-232+\n9-482-464\n9-482-465\n9-483-464\n9-483-465\n8-241-233\n7-120-117\n7-121-116+\n8-242-232\n8-242-233\n8-243-232\n8-243-233\n7-121-117\n6-60-59\n6-61-58\n6-61-59+\n7-122-118\n7-122-119+\n8-244-238\n8-244-239+\n9-488-478\n9-488-479\n9-489-478\n9-489-479\n8-245-238\n8-245-239+\n9-490-478\n9-490-479\n9-491-478\n9-491-479\n7-123-118+\n8-246-236\n8-246-237+\n9-492-474\n9-492-475\n9-493-474\n9-493-475\n8-247-236\n8-247-237\n7-123-119\n5-31-28+\n6-62-56\n6-62-57\n6-63-56\n6-63-57+\n7-126-114+\n8-252-228\n8-252-229\n8-253-228\n8-253-229\n7-126-115+\n8-252-230+\n9-504-460\n9-504-461\n9-505-460\n9-505-461\n8-252-231+\n9-504-462\n9-504-463\n9-505-462\n9-505-463\n8-253-230\n8-253-231\n7-127-114\n7-127-115+\n8-254-230\n8-254-231\n8-255-230\n8-255-231\n5-31-29+\n6-62-58\n6-62-59\n6-63-58+\n7-126-116+\n8-252-232+\n9-504-464\n9-504-465\n9-505-464\n9-505-465\n8-252-233+\n9-504-466\n9-504-467\n9-505-466\n9-505-467\n8-253-232+\n9-506-464\n9-506-465\n9-507-464\n9-507-465\n8-253-233\n7-126-117\n7-127-116\n7-127-117\n6-63-59+\n7-126-118\n7-126-119\n7-127-118\n7-127-119\n4-15-15+\n5-30-30+\n6-60-60\n6-60-61\n6-61-60\n6-61-61+\n7-122-122\n7-122-123\n7-123-122\n7-123-123\n5-30-31\n5-31-30\n5-31-31+\n6-62-62+\n7-124-124\n7-124-125\n7-125-124+\n8-250-248+\n9-500-496\n9-500-497\n9-501-496\n9-501-497\n8-250-249\n8-251-248\n8-251-249\n7-125-125+\n8-250-250\n8-250-251\n8-251-250\n8-251-251\n6-62-63+\n7-124-126+\n8-248-252\n8-248-253\n8-249-252\n8-249-253\n7-124-127\n7-125-126\n7-125-127\n6-63-62\n6-63-63+\n7-126-126\n7-126-127\n7-127-126\n7-127-127\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/streaming/readers/data.csv",
    "content": "\"Hello,world\",5,25.2\n#Comment\nhello-world,6,36.3"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/streaming/readers/geojson-multipolygon.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"id\": 193548155000002,\n        \"tags\": \"\\\"ref\\\"=>\\\"NIC-1\\\", \\\"name\\\"=>\\\"Carretera Norte\\\", \\\"lanes\\\"=>\\\"\\\", \\\"oneway\\\"=>\\\"yes\\\", \\\"highway\\\"=>\\\"trunk\\\", \\\"surface\\\"=>\\\"paved\\\"\",\n        \"countries\": [\n          \"NIC\"\n        ]\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                102.0,\n                2.0\n              ],\n              [\n                103.0,\n                2.0\n              ],\n              [\n                103.0,\n                3.0\n              ],\n              [\n                102.0,\n                3.0\n              ],\n              [\n                102.0,\n                2.0\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                100.0,\n                0.0\n              ],\n              [\n                101.0,\n                0.0\n              ],\n              [\n                101.0,\n                1.0\n              ],\n              [\n                100.0,\n                1.0\n              ],\n              [\n                100.0,\n                0.0\n              ]\n            ],\n            [\n              [\n                100.2,\n                0.2\n              ],\n              [\n                100.8,\n                0.2\n              ],\n              [\n                100.8,\n                0.8\n              ],\n              [\n                100.2,\n                0.8\n              ],\n              [\n                100.2,\n                0.2\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/streaming/readers/geojson-point.json",
    "content": "{\n\t\"type\": \"FeatureCollection\",\n\t\"features\": \n\t[\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"id\": 193548155000002,\n\t\t\t\t\"tags\": \"\\\"ref\\\"=>\\\"NIC-1\\\", \\\"name\\\"=>\\\"Carretera Norte\\\", \\\"lanes\\\"=>\\\"\\\", \\\"oneway\\\"=>\\\"yes\\\", \\\"highway\\\"=>\\\"trunk\\\", \\\"surface\\\"=>\\\"paved\\\"\",\n\t\t\t\t\"countries\": \n\t\t\t\t[\n\t\t\t\t\t\"NIC\"\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"Point\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t-86.19759590000001,\n\t\t\t\t\t12.1482481\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/streaming/readers/geojson-polygon.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"generator\": \"test\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"id\": 193548155000002,\n        \"tags\": \"\\\"ref\\\"=>\\\"NIC-1\\\", \\\"name\\\"=>\\\"Carretera Norte\\\", \\\"lanes\\\"=>\\\"\\\", \\\"oneway\\\"=>\\\"yes\\\", \\\"highway\\\"=>\\\"trunk\\\", \\\"surface\\\"=>\\\"paved\\\"\",\n        \"countries\": [\n          \"NIC\"\n        ]\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\":[\n          [\n            [\n              102.0,\n              2.0\n            ],\n            [\n              103.0,\n              2.0\n            ],\n            [\n              103.0,\n              3.0\n            ],\n            [\n              102.0,\n              3.0\n            ],\n            [\n              102.0,\n              2.0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/streaming/readers/geojson-sample.json",
    "content": "{\n\t\"type\": \"FeatureCollection\",\n\t\"crs\": \n\t{\n\t\t\"type\": \"name\",\n\t\t\"properties\": \n\t\t{\n\t\t\t\"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\"\n\t\t}\n\t},\n\n\t\"features\": \n\t[\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"id\": 193548155000002,\n\t\t\t\t\"tags\": \"\\\"ref\\\"=>\\\"NIC-1\\\", \\\"name\\\"=>\\\"Carretera Norte\\\", \\\"lanes\\\"=>\\\"\\\", \\\"oneway\\\"=>\\\"yes\\\", \\\"highway\\\"=>\\\"trunk\\\", \\\"surface\\\"=>\\\"paved\\\"\",\n\t\t\t\t\"countries\": \n\t\t\t\t[\n\t\t\t\t\t\"NIC\"\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-86.19759590000001,\n\t\t\t\t\t\t12.1482481\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-86.1985711,\n\t\t\t\t\t\t12.1483444\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"id\": 195288102000002,\n\t\t\t\t\"tags\": \"\\\"source\\\"=>\\\"Bing\\\", \\\"highway\\\"=>\\\"residential\\\"\",\n\t\t\t\t\"countries\": \n\t\t\t\t[\n\t\t\t\t\t\"NIC\"\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-86.2567625,\n\t\t\t\t\t\t12.1271112\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-86.25715430000001,\n\t\t\t\t\t\t12.12714\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"id\": 188168828000008,\n\t\t\t\t\"tags\": \"\\\"oneway\\\"=>\\\"-1\\\", \\\"highway\\\"=>\\\"tertiary\\\"\",\n\t\t\t\t\"countries\": \n\t\t\t\t[\n\t\t\t\t\t\"NIC\"\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-86.1990763,\n\t\t\t\t\t\t11.8500611\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-86.198287,\n\t\t\t\t\t\t11.8503743\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"id\": 193543402000002,\n\t\t\t\t\"tags\": \"\\\"highway\\\"=>\\\"residential\\\"\",\n\t\t\t\t\"countries\": \n\t\t\t\t[\n\t\t\t\t\t\"NIC\"\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-86.29639510000001,\n\t\t\t\t\t\t12.1598271\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-86.2957164,\n\t\t\t\t\t\t12.1598603\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\n\t\t{\n\t\t\t\"type\": \"Feature\",\n\t\t\t\"properties\": \n\t\t\t{\n\t\t\t\t\"id\": 171845220003000,\n\t\t\t\t\"tags\": \"\\\"highway\\\"=>\\\"residential\\\"\",\n\t\t\t\t\"countries\": \n\t\t\t\t[\n\t\t\t\t\t\"CRI\"\n\t\t\t\t]\n\t\t\t},\n\n\t\t\t\"geometry\": \n\t\t\t{\n\t\t\t\t\"type\": \"LineString\",\n\t\t\t\t\"coordinates\": \n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t-82.83993,\n\t\t\t\t\t\t8.537404\n\t\t\t\t\t],\n\n\t\t\t\t\t[\n\t\t\t\t\t\t-82.8396608,\n\t\t\t\t\t\t8.5374043\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/streaming/readers/wrongData.csv",
    "content": "\"Hello,world\",5\n#Comment\nhello-world,6,36.3"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/tags/filters/matcher/test-matching.json",
    "content": "{\n    \"matchers\": [\n        \"foo = bar | baz = bat\",\n        \"hello != world\"\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/tags/filters/test-filtering.json",
    "content": "{\n    \"filters\": [\n        \"barrier->*|highway->crossing,motorway_junction|railway->crossing,level_crossing\",\n        \"noexit->!\",\n        \"oneway->!reversed\"\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckRight.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    interface MyInterface\n    {\n        String string;\n\n        void doSomething();\n    }\n\n    public enum MyPublicEnum\n    {\n        ENUM1,\n        ENUM2\n    }\n\n    private enum MyPrivateEnum\n    {\n        ENUM1,\n        ENUM2\n    }\n\n    class SomeClass\n    {\n        protected void doSomethingElse()\n        {\n            // Something else\n        }\n    }\n\n    public static Integer staticField1;\n    protected static Long staticField2;\n    static boolean staticField3;\n    private static final String staticField4;\n\n    static\n    {\n        // My static block\n    }\n\n    public final int someValue1;\n    protected String someValue2;\n    final Boolean someValue3;\n    private final String someValue4;\n\n    public static final boolean getSomeBoolean()\n    {\n        return false;\n    }\n\n    static void saySomething()\n    {\n        System.out.println(\"Something!\");\n    }\n\n    private static void doNothing()\n    {\n    }\n\n    {\n        // My initializer block\n    }\n\n    public MyClass()\n    {\n        this.someValue1 = 0;\n        this.someValue3 = true;\n        this.someValue4 = \"Yes\";\n    }\n\n    private MyClass()\n    {\n        this.someValue1 = 0;\n        this.someValue3 = true;\n        this.someValue4 = \"Yes\";\n    }\n\n    public void methodA()\n    {\n    }\n\n    public void methodB()\n    {\n    }\n\n    protected void methodC()\n    {\n    }\n\n    protected void methodD()\n    {\n    }\n\n    void methodE()\n    {\n    }\n\n    void methodF()\n    {\n    }\n\n    private void methodG()\n    {\n    }\n\n    private void methodH()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongField0.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    public void method()\n    {\n    }\n\n    private boolean field;\n\n    public void methodB()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongField1.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    private boolean field;\n    private static boolean FIELD;\n\n    public void method()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongField2.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    private boolean fieldA;\n    boolean fieldB;\n\n    public void method()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongInitializerBlock.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    public void method()\n    {\n    }\n\n    {\n        // Do something\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongInitializerStaticBlock.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    public void method()\n    {\n    }\n\n    static\n    {\n        // Do something\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongMethodModifier.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    void methodA()\n    {\n    }\n\n    public void methodB()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongMethodName.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    public void methodB()\n    {\n    }\n\n    public void methodA()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongMethodVisibility1.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    private void methodA()\n    {\n    }\n\n    public void methodB()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/checkstyle/ArrangementCheckWrongMethodVisibility2.java",
    "content": "package org.openstreetmap.atlas.utilities.checkstyle;\n\n/**\n * @author matthieun\n */\npublic class MyClass\n{\n    void methodA()\n    {\n    }\n\n    public void methodB()\n    {\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommandAtlas1.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-101752' action='modify' visible='true' lat='43.10269078969' lon='5.93247948909' />\n  <node id='-101754' action='modify' visible='true' lat='43.10292605895' lon='5.93316597867' />\n  <node id='-101755' action='modify' visible='true' lat='43.10288514262' lon='5.9336142984' />\n  <node id='-101756' action='modify' visible='true' lat='43.10274875466' lon='5.93418403805' />\n  <node id='-101758' action='modify' visible='true' lat='43.10327043695' lon='5.93423073802' />\n  <node id='-101759' action='modify' visible='true' lat='43.10342728173' lon='5.93446890787' />\n  <node id='-101760' action='modify' visible='true' lat='43.1033322667' lon='5.93487059863' />\n  <node id='-101761' action='modify' visible='true' lat='43.10312723049' lon='5.93482849765' />\n  <node id='-101762' action='modify' visible='true' lat='43.10298743339' lon='5.93504331752' />\n  <node id='-101763' action='modify' visible='true' lat='43.10306585619' lon='5.93542625729' />\n  <node id='-101764' action='modify' visible='true' lat='43.10292264925' lon='5.93570178712' />\n  <way id='-101782' action='modify' visible='true'>\n    <nd ref='-101752' />\n    <nd ref='-101754' />\n    <nd ref='-101755' />\n    <nd ref='-101756' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-101826' action='modify' visible='true'>\n    <nd ref='-101758' />\n    <nd ref='-101759' />\n    <nd ref='-101760' />\n    <nd ref='-101761' />\n    <nd ref='-101758' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <way id='-101872' action='modify' visible='true'>\n    <nd ref='-101761' />\n    <nd ref='-101762' />\n    <nd ref='-101763' />\n    <nd ref='-101764' />\n    <tag k='waterway' v='river' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommandAtlas2.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-101752' action='modify' visible='true' lat='43.10269078969' lon='5.93247948909' />\n  <node id='-101754' action='modify' visible='true' lat='43.10292605895' lon='5.93316597867' />\n  <node id='-101755' action='modify' visible='true' lat='43.10288514262' lon='5.9336142984' />\n  <node id='-101756' action='modify' visible='true' lat='43.10274875466' lon='5.93418403805' />\n  <node id='-101758' action='modify' visible='true' lat='43.10327043695' lon='5.93423073802' />\n  <node id='-101759' action='modify' visible='true' lat='43.10342728173' lon='5.93446890787' />\n  <node id='-101760' action='modify' visible='true' lat='43.1033322667' lon='5.93487059863' />\n  <node id='-101761' action='modify' visible='true' lat='43.10312723049' lon='5.93482849765' />\n  <node id='-101762' action='modify' visible='true' lat='43.10298743339' lon='5.93504331752' />\n  <node id='-101763' action='modify' visible='true' lat='43.10306585619' lon='5.93542625729' />\n  <node id='-101764' action='modify' visible='true' lat='43.10292264925' lon='5.93570178712' />\n  <node id='-101765' action='modify' visible='true' lat='43.1023293598' lon='5.93405794813' />\n  <way id='-101782' action='modify' visible='true'>\n    <nd ref='-101752' />\n    <nd ref='-101754' />\n    <nd ref='-101755' />\n    <nd ref='-101756' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-101826' action='modify' visible='true'>\n    <nd ref='-101758' />\n    <nd ref='-101759' />\n    <nd ref='-101760' />\n    <nd ref='-101761' />\n    <nd ref='-101758' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <way id='-101872' action='modify' visible='true'>\n    <nd ref='-101761' />\n    <nd ref='-101762' />\n    <nd ref='-101763' />\n    <nd ref='-101764' />\n    <tag k='waterway' v='river' />\n  </way>\n  <way id='-101910' action='modify' visible='true'>\n    <nd ref='-101765' />\n    <nd ref='-101756' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommandAtlas3.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-101752' action='modify' visible='true' lat='43.10269078969' lon='5.93247948909' />\n  <node id='-101754' action='modify' visible='true' lat='43.10292605895' lon='5.93316597867' />\n  <node id='-101755' action='modify' visible='true' lat='43.10288514262' lon='5.9336142984' />\n  <node id='-101756' action='modify' visible='true' lat='43.10274875466' lon='5.93418403805' />\n  <node id='-101758' action='modify' visible='true' lat='43.10327043695' lon='5.93423073802' />\n  <node id='-101759' action='modify' visible='true' lat='43.10342728173' lon='5.93446890787' />\n  <node id='-101760' action='modify' visible='true' lat='43.1033322667' lon='5.93487059863' />\n  <node id='-101761' action='modify' visible='true' lat='43.10312723049' lon='5.93482849765' />\n  <node id='-101762' action='modify' visible='true' lat='43.10298743339' lon='5.93504331752' />\n  <node id='-101763' action='modify' visible='true' lat='43.10306585619' lon='5.93542625729' />\n  <node id='-101764' action='modify' visible='true' lat='43.10292264925' lon='5.93570178712' />\n  <node id='-101765' action='modify' visible='true' lat='43.1023293598' lon='5.93405794813' />\n  <way id='-101782' action='modify' visible='true'>\n    <nd ref='-101752' />\n    <nd ref='-101754' />\n    <nd ref='-101755' />\n    <nd ref='-101756' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-101826' action='modify' visible='true'>\n    <nd ref='-101758' />\n    <nd ref='-101759' />\n    <nd ref='-101760' />\n    <nd ref='-101761' />\n    <nd ref='-101758' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <way id='-101872' action='modify' visible='true'>\n    <nd ref='-101761' />\n    <nd ref='-101762' />\n    <nd ref='-101763' />\n    <nd ref='-101764' />\n    <tag k='waterway' v='river' />\n  </way>\n  <way id='-101910' action='modify' visible='true'>\n    <nd ref='-101765' />\n    <nd ref='-101756' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-99752' action='modify' visible='true'>\n    <member type='way' ref='-101872' role='two' />\n    <member type='way' ref='-101826' role='one' />\n    <tag k='type' v='waterway' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/AtlasDiffCommandAtlas4.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-101756' action='modify' visible='true' lat='43.10274875466' lon='5.93418403805' />\n  <node id='-101758' action='modify' visible='true' lat='43.10327043695' lon='5.93423073802' />\n  <node id='-101759' action='modify' visible='true' lat='43.10342728173' lon='5.93446890787' />\n  <node id='-101760' action='modify' visible='true' lat='43.1033322667' lon='5.93487059863' />\n  <node id='-101761' action='modify' visible='true' lat='43.10312723049' lon='5.93482849765' />\n  <node id='-101762' action='modify' visible='true' lat='43.10298743339' lon='5.93504331752' />\n  <node id='-101763' action='modify' visible='true' lat='43.10306585619' lon='5.93542625729' />\n  <node id='-101764' action='modify' visible='true' lat='43.10292264925' lon='5.93570178712' />\n  <node id='-101765' action='modify' visible='true' lat='43.1023293598' lon='5.93405794813' />\n  <way id='-101826' action='modify' visible='true'>\n    <nd ref='-101758' />\n    <nd ref='-101759' />\n    <nd ref='-101760' />\n    <nd ref='-101761' />\n    <nd ref='-101758' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <way id='-101872' action='modify' visible='true'>\n    <nd ref='-101761' />\n    <nd ref='-101762' />\n    <nd ref='-101763' />\n    <nd ref='-101764' />\n    <tag k='waterway' v='river' />\n  </way>\n  <way id='-101910' action='modify' visible='true'>\n    <nd ref='-101765' />\n    <nd ref='-101756' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-99752' action='modify' visible='true'>\n    <member type='way' ref='-101872' role='two' />\n    <member type='way' ref='-101826' role='one' />\n    <tag k='type' v='waterway' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/CountryBoundaryMapPrinterCommandTestBoundaries.txt",
    "content": "XYZ||POLYGON ((4.2038896 38.8273784, 4.2132564 38.8330031, 4.2308843 38.8337631, 4.2451948 38.8205877, 4.2422676 38.811769, 4.226396 38.8072578, 4.2102643 38.8082716, 4.2021333 38.8176483, 4.2038896 38.8273784))#\nJKL||POLYGON ((4.2038896 38.8273784, 4.2132564 38.8330031, 4.2308843 38.8337631, 4.2451948 38.8205877, 4.2422676 38.811769, 4.226396 38.8072578, 4.2102643 38.8082716, 4.2021333 38.8176483, 4.2038896 38.8273784))#\nABC||POLYGON ((4.2038896 38.8273784, 4.2132564 38.8330031, 4.2308843 38.8337631, 4.2451948 38.8205877, 4.2422676 38.811769, 4.226396 38.8072578, 4.2102643 38.8082716, 4.2021333 38.8176483, 4.2038896 38.8273784))#\nABC||POLYGON((9.143751725029889 41.34213127890892,9.186323746514264 41.359141175884226,9.214476212334576 41.33800699823009,9.199370011162701 41.317381677280416,9.173964127373639 41.335944759949086,9.161604508233014 41.31944450318208,9.143751725029889 41.34213127890892))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/MAF_AIA_osm_boundaries_with_grid_index.txt",
    "content": "MAF||POLYGON ((-63.0194647 18.0558888, -63.0191827 18.0559174, -63.0190903 18.0561275, -63.019259 18.056293, -63.0190608 18.0564757, -63.0189276 18.0567053, -63.0186556 18.0569373, -63.0184057 18.0570803, -63.0178687 18.0572294, -63.0177679 18.0572187, -63.0175613 18.0571575, -63.0169747 18.0570334, -63.016819 18.0570643, -63.0166597 18.0570504, -63.0162215 18.0569109, -63.0162399 18.0568714, -63.0160378 18.0568184, -63.0160002 18.056846, -63.0158548 18.0568474, -63.0156498 18.0567914, -63.0150291 18.0565082, -63.0149928 18.0565437, -63.014899 18.0564512, -63.0147902 18.0562901, -63.0147123 18.0561438, -63.0146476 18.055991, -63.0146051 18.0558922, -63.0146046 18.0558065, -63.0146227 18.0557575, -63.0146404 18.0557312, -63.014648 18.0556488, -63.0145836 18.0554319, -63.0146395 18.0554033, -63.0145834 18.055254, -63.0146447 18.0552133, -63.014625 18.0551173, -63.0144825 18.0552033, -63.0143513 18.0552762, -63.0141805 18.0553744, -63.0140274 18.0555243, -63.0139171 18.0555242, -63.0138677 18.055589, -63.0137532 18.055607, -63.0137021 18.0556968, -63.0134479 18.0557924, -63.0130989 18.0557534, -63.0125669 18.0554714, -63.0124559 18.0552924, -63.0124638 18.0551294, -63.0124699 18.0550054, -63.0125104 18.0549077, -63.0126049 18.0547504, -63.0127367 18.0545906, -63.0128309 18.0544764, -63.0128759 18.0542744, -63.0128612 18.0542511, -63.0127559 18.0540843, -63.0109875 18.0513771, -62.9390435 18.0293243, -63.0647541 17.8069828, -63.0650717 17.807, -63.073681 17.808, -63.0802774 17.809, -63.0858225 17.81, -63.0906916 17.811, -63.0950781 17.812, -63.0990972 17.813, -63.1028241 17.814, -63.1045459 17.814, -63.1014224 17.812, -63.0984153 17.81, -63.0955161 17.808, -63.0913037 17.805, -63.0885993 17.803, -63.0847067 17.8, -63.0822138 17.798, -63.0786165 17.795, -63.0763076 17.793, -63.0729695 17.79, -63.0708233 17.788, -63.067716 17.785, -63.0657156 17.783, -63.0628162 17.78, -63.0600323 17.777, -63.0573579 17.774, -63.0547878 17.771, -63.052317 17.768, -63.0499412 17.765, -63.0476565 17.762, -63.0454592 17.759, -63.043346 17.756, -63.0413139 17.753, -63.0387155 17.749, -63.0368376 17.746, -63.0350333 17.743, -63.0333003 17.74, -63.0310971 17.736, -63.0280143 17.73, -63.0261014 17.726, -63.0249377 17.7234398, -63.0201227 17.724, -63.001651 17.725, -62.9903418 17.725, -62.9836891 17.7246398, -62.8527919 17.6748165, -62.8503576 17.673, -62.8452944 17.669, -62.8416997 17.666, -62.8371525 17.662, -62.8328624 17.658, -62.828808 17.654, -62.827618 17.653, -62.8247725 17.651, -62.8220242 17.649, -62.8167952 17.645, -62.81189 17.641, -62.8095482 17.639, -62.8061637 17.636, -62.8018742 17.632, -62.7957619 17.626, -62.792865 17.623, -62.7900531 17.62, -62.7864181 17.616, -62.7862552 17.6158146, -63.051129 17.2791599, -63.0559997 17.281, -63.0585031 17.282, -63.0632562 17.284, -63.0655194 17.285, -63.0698465 17.287, -63.075903 17.29, -63.0815179 17.293, -63.0867587 17.296, -63.0949392 17.301, -63.0995734 17.304, -63.1025213 17.306, -63.1053653 17.308, -63.1108293 17.312, -63.1159717 17.316, -63.1207998 17.32, -63.1253453 17.324, -63.1275204 17.326, -63.1306688 17.329, -63.1346666 17.333, -63.1365847 17.335, -63.1393664 17.338, -63.143866 17.343, -63.1473136 17.347, -63.1514252 17.352, -63.1545305 17.356, -63.1574786 17.36, -63.1609547 17.365, -63.1616922 17.366, -63.1641619 17.369, -63.1665368 17.372, -63.1688209 17.375, -63.1717313 17.379, -63.173817 17.382, -63.1764739 17.386, -63.178377 17.389, -63.1807996 17.393, -63.1825333 17.396, -63.1841978 17.399, -63.1873611 17.405, -63.1903174 17.411, -63.193474 17.418074, -63.1993665 17.417, -63.2059486 17.416, -63.214539 17.415, -63.2329995 17.414, -63.2437543 17.414, -63.2622148 17.415, -63.2708052 17.416, -63.2786081 17.417, -63.2851902 17.418, -63.2907233 17.419, -63.2955818 17.42, -63.2999587 17.421, -63.3039691 17.422, -63.3076879 17.423, -63.3111674 17.424, -63.3239527 17.428, -63.3269088 17.429, -63.3324395 17.431, -63.337546 17.433, -63.342303 17.435, -63.3467651 17.437, -63.3509735 17.439, -63.3549603 17.441, -63.3587513 17.443, -63.364171 17.446, -63.3693033 17.449, -63.3725507 17.451, -63.3771887 17.454, -63.3801391 17.456, -63.3843718 17.459, -63.388393 17.462, -63.3922215 17.465, -63.3958732 17.468, -63.3993619 17.471, -63.4026991 17.474, -63.4069451 17.478, -63.4100089 17.481, -63.4129482 17.484, -63.41577 17.487, -63.4193602 17.491, -63.4219309 17.494, -63.4252184 17.498, -63.427595 17.501, -63.4306231 17.505, -63.433498 17.509, -63.4362272 17.513, -63.4388172 17.517, -63.441274 17.521, -63.443603 17.525, -63.4458088 17.529, -63.4483994 17.534, -63.4503434 17.538, -63.4521767 17.542, -63.4539026 17.546, -63.4562961 17.552, -63.4584625 17.558, -63.4604093 17.564, -63.4615881 17.568, -63.4626739 17.572, -63.4641309 17.578, -63.4649897 17.582, -63.4657599 17.586, -63.466751 17.592, -63.4673035 17.596, -63.4677702 17.6, -63.4681518 17.604, -63.4684486 17.608, -63.468661 17.612, -63.4687893 17.616, -63.4688243 17.622, -63.4686703 17.628, -63.4686822 17.636, -63.4686006 17.64, -63.4684349 17.644, -63.4681849 17.648, -63.4676513 17.654, -63.4669258 17.66, -63.4660065 17.666, -63.4652847 17.67, -63.4644747 17.674, -63.4630919 17.68, -63.461504 17.686, -63.4597062 17.692, -63.4583883 17.696, -63.4569726 17.7, -63.4546612 17.706, -63.452117 17.712, -63.4493306 17.718, -63.4462913 17.724, -63.4446729 17.727, -63.4424242 17.731, -63.4403692 17.735, -63.4387507 17.738, -63.435901 17.743, -63.4328531 17.748, -63.4309249 17.751, -63.4289193 17.754, -63.4268335 17.757, -63.4246646 17.76, -63.4224094 17.763, -63.4200644 17.766, -63.4176257 17.769, -63.4150893 17.772, -63.4124502 17.775, -63.4097035 17.778, -63.4068433 17.781, -63.4048703 17.783, -63.4018063 17.786, -63.3996905 17.788, -63.3964007 17.791, -63.3941258 17.793, -63.3905831 17.796, -63.3881289 17.798, -63.38298 17.802, -63.3788885 17.805, -63.3760405 17.807, -63.3730884 17.809, -63.3700243 17.811, -63.366839 17.813, -63.3635222 17.815, -63.3582731 17.818, -63.3526489 17.821, -63.346582 17.824, -63.3422473 17.826, -63.3376393 17.828, -63.3310606 17.831, -63.3264524 17.833, -63.3215235 17.835, -63.3189197 17.836, -63.3133831 17.838, -63.3104237 17.839, -63.3040321 17.841, -63.3005485 17.842, -63.2968253 17.843, -63.2928102 17.844, -63.288428 17.845, -63.2835635 17.846, -63.2780236 17.847, -63.2714332 17.848, -63.2628317 17.849, -63.2443471 17.85, -63.237785 17.85, -63.2193004 17.849, -63.208195 17.848, -63.1995936 17.847, -63.1930792 17.847, -63.1981976 17.849, -63.2029657 17.851, -63.2074381 17.853, -63.2136804 17.856, -63.2194522 17.859, -63.2248283 17.862, -63.2298646 17.865, -63.2346043 17.868, -63.2376166 17.87, -63.2419347 17.873, -63.2460336 17.876, -63.2499335 17.879, -63.2536512 17.882, -63.2608286 17.888, -63.2642736 17.891, -63.2675705 17.894, -63.2689123 17.895, -63.2735618 17.898, -63.2779589 17.901, -63.2821291 17.904, -63.2873731 17.908, -63.2898713 17.91, -63.2934763 17.913, -63.2969218 17.916, -63.3002191 17.919, -63.3023398 17.921, -63.3054112 17.924, -63.3083578 17.927, -63.3111867 17.93, -63.3139039 17.933, -63.3165152 17.936, -63.3190255 17.939, -63.3222231 17.943, -63.3245146 17.946, -63.3267186 17.949, -63.3288383 17.952, -63.3315388 17.956, -63.1388353 18.0529736, -63.1388083 18.052994, -63.1387464 18.053076, -63.1386684 18.053146, -63.1385744 18.053196, -63.1385244 18.05321, -63.1384264 18.05322, -63.1382314 18.053223, -63.1380804 18.053251, -63.1379514 18.053286, -63.1378604 18.05332, -63.1374094 18.053331, -63.1372404 18.053343, -63.1370734 18.053372, -63.1368944 18.053431, -63.1367044 18.053508, -63.1362365 18.053718, -63.1359975 18.053815, -63.1355845 18.053927, -63.1354675 18.053972, -63.1352105 18.054096, -63.1350795 18.05415, -63.1349415 18.054187, -63.1348005 18.054199, -63.1346625 18.054197, -63.1345235 18.054186, -63.1343845 18.054165, -63.1341516 18.054112, -63.1338656 18.054018, -63.1335646 18.053955, -63.1334666 18.053917, -63.1333696 18.053844, -63.1333506 18.053836, -63.1333185 18.0538355, -63.1333146 18.053835, -63.1332106 18.053847, -63.1316007 18.053873, -63.1266339 18.053992, -63.1260829 18.053996, -63.1140224 18.053487, -63.1131545 18.053455, -63.1124485 18.053418, -63.1121585 18.053412, -63.1121195 18.053402, -63.1120245 18.053362, -63.1116995 18.053182, -63.1086767 18.051573, -63.1072877 18.0508211, -63.1029959 18.0485611, -63.101897 18.0479611, -63.101231 18.0476131, -63.10091 18.0474591, -63.100522 18.0472911, -63.099992 18.0470771, -63.0993291 18.0468151, -63.0979701 18.0463951, -63.0975391 18.0462841, -63.0972342 18.0462231, -63.0969252 18.0461741, -63.0948513 18.0459301, -63.0941863 18.0458811, -63.0939713 18.0458801, -63.0924644 18.0459531, -63.0912634 18.0460201, -63.0906424 18.0460401, -63.0903455 18.0460561, -63.0899945 18.0460581, -63.0896685 18.0460681, -63.0895375 18.0460911, -63.0894415 18.0461201, -63.0891565 18.0462261, -63.0880959 18.046592, -63.0880726 18.0466521, -63.0878716 18.0467141, -63.0869456 18.0469721, -63.0862056 18.0472011, -63.0855207 18.0473821, -63.0849477 18.0475511, -63.0838137 18.0478991, -63.0820978 18.0484021, -63.0814789 18.0486131, -63.0812819 18.0486661, -63.0808989 18.0487551, -63.0802569 18.0488921, -63.078858 18.0492121, -63.077965 18.0494371, -63.077906 18.0494611, -63.077874 18.0494881, -63.077847 18.0495291, -63.077547 18.0500911, -63.077485 18.0502191, -63.077459 18.0502591, -63.077428 18.0502891, -63.077315 18.0503601, -63.0771261 18.0504511, -63.0767791 18.0505851, -63.0766031 18.0506391, -63.0765211 18.0506802, -63.0763241 18.0508612, -63.0762191 18.0509372, -63.0755801 18.0512902, -63.0753641 18.0514332, -63.0751481 18.0515572, -63.0750751 18.0516172, -63.0750411 18.0516682, -63.0750201 18.0517222, -63.0749882 18.0518802, -63.0749622 18.0519602, -63.0749632 18.0519822, -63.0750371 18.0522482, -63.0750461 18.0523382, -63.0750322 18.0524342, -63.0749402 18.0527312, -63.0745832 18.0537642, -63.0742432 18.0548112, -63.0741822 18.0549482, -63.0741482 18.0549852, -63.0738722 18.0551802, -63.0733432 18.0555102, -63.0732962 18.0555472, -63.0732132 18.0556372, -63.0729723 18.0560012, -63.0729343 18.0560402, -63.0728013 18.0561292, -63.0727613 18.0561652, -63.0726503 18.0563092, -63.0725873 18.0563532, -63.0725023 18.0563912, -63.0719693 18.0565572, -63.0717933 18.0566062, -63.0716183 18.0566372, -63.0715743 18.0566562, -63.0715073 18.0567132, -63.0714923 18.0567072, -63.0712543 18.0567112, -63.0711383 18.0566842, -63.0711023 18.0566692, -63.0710613 18.0566652, -63.0708753 18.0566882, -63.0703004 18.0566952, -63.0700884 18.0567142, -63.0697704 18.0567852, -63.0695544 18.0568122, -63.0690724 18.0568542, -63.0685065 18.0569232, -63.0683085 18.0569272, -63.0681615 18.0569442, -63.0679965 18.0569732, -63.0677975 18.0570192, -63.0676405 18.0570652, -63.0667665 18.0573832, -63.0665105 18.0574822, -63.0664595 18.0575142, -63.0664255 18.0575752, -63.0663945 18.0577022, -63.0662826 18.0580802, -63.0660276 18.0591692, -63.0659656 18.0595022, -63.0659346 18.0595912, -63.0658926 18.0596622, -63.0656876 18.0599202, -63.0656396 18.0600382, -63.0655626 18.0601122, -63.0653056 18.0603152, -63.0652146 18.0604142, -63.0651056 18.0605492, -63.0648496 18.0608952, -63.0647276 18.0610482, -63.0645996 18.0611722, -63.0645786 18.0612022, -63.0645306 18.0613172, -63.0645176 18.0613332, -63.0644686 18.0613472, -63.0644336 18.0613712, -63.0643887 18.0614382, -63.0643597 18.0615302, -63.0643547 18.0616282, -63.0643627 18.0617282, -63.0643967 18.0619362, -63.0644267 18.0620512, -63.0644207 18.0620802, -63.0643847 18.0621442, -63.0642457 18.0623222, -63.0641697 18.0624302, -63.0641147 18.0625292, -63.0640197 18.0627942, -63.0639827 18.0628812, -63.0639287 18.0629692, -63.0638207 18.0630982, -63.0636657 18.0632592, -63.0635937 18.0633452, -63.0634627 18.0635452, -63.0634047 18.0636222, -63.0633357 18.0636862, -63.0632407 18.0637372, -63.0631487 18.0637632, -63.0630927 18.0637612, -63.0628777 18.0637032, -63.0624107 18.0636402, -63.0621708 18.0635922, -63.0614538 18.0634022, -63.0609118 18.0632502, -63.0603498 18.0630582, -63.0600798 18.0629762, -63.0598059 18.0629092, -63.0595269 18.0628632, -63.0592399 18.0628382, -63.0590229 18.0628422, -63.0588109 18.0628762, -63.057254 18.0629702, -63.056014 18.0630312, -63.055836 18.0630522, -63.055658 18.0630872, -63.055486 18.0631392, -63.0553551 18.0631952, -63.0552721 18.0632392, -63.0551631 18.0633152, -63.0549861 18.0634892, -63.0549321 18.0635322, -63.0548731 18.0635632, -63.0548071 18.0635822, -63.0546371 18.0636122, -63.0545551 18.0636402, -63.0544411 18.0637092, -63.0543391 18.0637512, -63.0540541 18.0638352, -63.0537911 18.0639242, -63.0536381 18.0639612, -63.0534771 18.0639782, -63.0532242 18.0639862, -63.0530752 18.0639662, -63.0528792 18.0639182, -63.0527252 18.0638902, -63.0524412 18.0638712, -63.0523302 18.0638522, -63.0522252 18.0638152, -63.0522022 18.0638002, -63.0521532 18.0637362, -63.0519242 18.0636172, -63.0518432 18.0635662, -63.0516062 18.0633732, -63.0515102 18.0633062, -63.0510412 18.0630112, -63.0508992 18.0629302, -63.0503853 18.0626702, -63.0502233 18.0625802, -63.0495183 18.0621472, -63.0492633 18.0619972, -63.0489773 18.0618422, -63.0486833 18.0617113, -63.0483924 18.0615393, -63.0479874 18.0613243, -63.0477344 18.0611483, -63.0473184 18.0608293, -63.0464444 18.0602153, -63.0461414 18.0600173, -63.0456725 18.0596743, -63.0447095 18.0589393, -63.0440065 18.0584233, -63.0439392 18.0584331, -63.0435876 18.0581813, -63.0430876 18.0578133, -63.0422568 18.0572244, -63.0420044 18.0570431, -63.0417783 18.0568823, -63.0410711 18.0563795, -63.0410497 18.0563643, -63.0408147 18.0561903, -63.0406847 18.0561083, -63.0406077 18.0560993, -63.0405647 18.0560823, -63.0401737 18.0557983, -63.0401387 18.0557833, -63.0401077 18.0557863, -63.0400727 18.0558043, -63.0396957 18.0560823, -63.0387898 18.0567203, -63.0382278 18.0571323, -63.0380578 18.0572633, -63.0379498 18.0573563, -63.0378578 18.0574473, -63.0377878 18.0575373, -63.0375218 18.0579853, -63.0374948 18.0580163, -63.0374678 18.0580283, -63.0374398 18.0580253, -63.0371608 18.0579463, -63.0370728 18.0579013, -63.0368368 18.0577513, -63.0356879 18.0572133, -63.0351629 18.0569583, -63.0347459 18.0567683, -63.033518 18.0561563, -63.032731 18.0557933, -63.032598 18.0557223, -63.0319461 18.0553243, -63.0317951 18.0552603, -63.0307031 18.0549123, -63.0303431 18.0547913, -63.0303071 18.0547743, -63.0302451 18.0547243, -63.0299721 18.0546243, -63.0293032 18.0544093, -63.0290412 18.0543153, -63.0289092 18.0542873, -63.0288272 18.0542843, -63.0287492 18.0542943, -63.0286722 18.0543123, -63.0284462 18.0543813, -63.0278002 18.0546073, -63.0274732 18.0547043, -63.0273063 18.0547393, -63.0268603 18.0547993, -63.0267963 18.0548243, -63.0267253 18.0548733, -63.0265653 18.0550023, -63.0264003 18.0551083, -63.0263073 18.0551853, -63.0261523 18.0553643, -63.0260633 18.0554563, -63.0259023 18.0556063, -63.0257753 18.0557093, -63.0255773 18.0558293, -63.0252993 18.0559713, -63.0251644 18.0560483, -63.0250684 18.0561213, -63.0248994 18.0562783, -63.0246814 18.0565433, -63.0246094 18.0566083, -63.0244604 18.0567053, -63.0241964 18.0568473, -63.0241574 18.0568773, -63.0240964 18.0569663, -63.0240054 18.0571583, -63.0239494 18.0572323, -63.0239134 18.0572613, -63.0237924 18.0572973, -63.0236814 18.0573163, -63.0235694 18.0573223, -63.0234574 18.0573173, -63.0233464 18.0573023, -63.0231254 18.0572493, -63.0229135 18.0571713, -63.0226435 18.0570533, -63.0225175 18.0569813, -63.0221825 18.0567573, -63.0220645 18.0566913, -63.0218835 18.0566153, -63.0217605 18.0565753, -63.0210445 18.0563803, -63.0209155 18.0563623, -63.0207406 18.0563743, -63.0205686 18.0563593, -63.0204261 18.0563761, -63.0202446 18.0562713, -63.0200486 18.0561703, -63.0198976 18.0561093, -63.0198823 18.0561004, -63.0194647 18.0558888))#\nAIA||POLYGON ((-62.76312 18.1617887, -62.8763889 18.1902778, -62.9486111 18.1808333, -63.2094444 18.0980556, -63.3630916 18.0609378, -63.3771792 18.0953032, -63.3789926 18.1026598, -63.4047895 18.1185063, -63.4329509 18.1438542, -63.4559282 18.1739371, -63.4741148 18.2111646, -63.4836934 18.2481463, -63.485814 18.2857672, -63.4815363 18.3189697, -63.4715459 18.3510331, -63.4550558 18.3824779, -63.4638886 18.383194, -63.4984045 18.3917593, -63.530914 18.4057613, -63.5604854 18.4247988, -63.586558 18.4486376, -63.6083528 18.4768146, -63.624694 18.508174, -63.6350824 18.5417639, -63.6391992 18.5765642, -63.6361741 18.6171373, -63.6304961 18.6424208, -63.6185484 18.6737629, -63.6013195 18.7027875, -63.5792825 18.7286932, -63.5680018 18.7396922, -63.5375893 18.7624516, -63.5038432 18.7795528, -63.4674048 18.7905631, -63.4294754 18.7951194, -63.3913054 18.7930715, -63.3541538 18.7844866, -63.3192454 18.769648, -63.2873995 18.7487832, -63.2593009 18.7219304, -63.2370085 18.6905217, -63.2213148 18.6556787, -63.2127759 18.6186448, -63.2116914 18.5807407, -63.2162068 18.5496612, -63.21887 18.5404974, -63.2267431 18.5164009, -63.2339724 18.5010857, -63.2421995 18.4870075, -63.1921975 18.4820994, -63.1749669 18.4770254, -63.1674947 18.4772384, -63.1352448 18.472554, -63.1193675 18.473714, -63.0823119 18.4737166, -63.0592699 18.4708299, -63.0336717 18.4841929, -62.9960982 18.4950196, -62.9614299 18.4989214, -62.9265625 18.4972974, -62.917916 18.4955219, -62.9110185 18.495389, -62.8694851 18.4875076, -62.8340843 18.4741697, -62.8199891 18.4675354, -62.7901606 18.4482165, -62.7642312 18.4243196, -62.7429615 18.3965477, -62.7269744 18.3657168, -62.7167364 18.3327328, -62.7125449 18.298564, -62.7145193 18.2642131, -62.7225981 18.2306875, -62.7341738 18.2034351, -62.755937 18.1699895, -62.76312 18.1617887))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/MultiPolygonTest.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-104784' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='-104785' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='-104786' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='-104787' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='-104788' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='-104838' action='modify' visible='true' lat='43.10248790611' lon='5.93329533407' />\n  <node id='-104853' action='modify' visible='true' lat='43.1022980005' lon='5.93287680911' />\n  <node id='-104854' action='modify' visible='true' lat='43.10198712901' lon='5.93331176428' />\n  <node id='-104857' action='modify' visible='true' lat='43.10250424812' lon='5.93422966731' />\n  <node id='-104858' action='modify' visible='true' lat='43.10200347115' lon='5.93424609751' />\n  <node id='-104862' action='modify' visible='true' lat='43.10239526312' lon='5.93454751396' />\n  <node id='-104863' action='modify' visible='true' lat='43.10228246626' lon='5.93397834835' />\n  <node id='-104864' action='modify' visible='true' lat='43.10207765037' lon='5.93447027005' />\n  <node id='-104865' action='modify' visible='true' lat='43.10217857423' lon='5.93471826364' />\n  <node id='-104866' action='modify' visible='true' lat='43.10233292803' lon='5.93469387083' />\n  <way id='-101793' action='modify' visible='true'>\n    <nd ref='-104784' />\n    <nd ref='-104785' />\n    <nd ref='-104786' />\n    <nd ref='-104787' />\n    <nd ref='-104788' />\n    <nd ref='-104784' />\n  </way>\n  <way id='-102192' action='modify' visible='true'>\n    <nd ref='-104853' />\n    <nd ref='-104838' />\n    <nd ref='-104854' />\n    <nd ref='-104853' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-102477' action='modify' visible='true'>\n    <nd ref='-104857' />\n    <nd ref='-104838' />\n    <nd ref='-104854' />\n    <nd ref='-104858' />\n    <nd ref='-104857' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='-102506' action='modify' visible='true'>\n    <nd ref='-104862' />\n    <nd ref='-104863' />\n    <nd ref='-104864' />\n    <nd ref='-104865' />\n    <nd ref='-104866' />\n    <nd ref='-104862' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <relation id='-99753' action='modify' visible='true'>\n    <member type='way' ref='-102506' role='inner' />\n    <member type='way' ref='-102477' role='inner' />\n    <member type='way' ref='-102192' role='inner' />\n    <member type='way' ref='-101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/MultiPolygonTest.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM' timestamp='2017-12-19T21:43:02Z'>\n  <node id='104784' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10221667001' lon='5.93210756242' />\n  <node id='104785' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10298826566' lon='5.93324481905' />\n  <node id='104786' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10285901427' lon='5.93499898374' />\n  <node id='104787' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10155473502' lon='5.93529402673' />\n  <node id='104788' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10137456213' lon='5.93290149629' />\n  <node id='104838' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10248790611' lon='5.93329533407' />\n  <node id='104853' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.1022980005' lon='5.93287680911' />\n  <node id='104854' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10198712901' lon='5.93331176428' />\n  <node id='104857' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10250424812' lon='5.93422966731' />\n  <node id='104858' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10200347115' lon='5.93424609751' />\n  <node id='104862' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10239526312' lon='5.93454751396' />\n  <node id='104863' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10228246626' lon='5.93397834835' />\n  <node id='104864' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10207765037' lon='5.93447027005' />\n  <node id='104865' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10217857423' lon='5.93471826364' />\n  <node id='104866' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true' lat='43.10233292803' lon='5.93469387083' />\n  <way id='101793' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true'>\n    <nd ref='104784' />\n    <nd ref='104785' />\n    <nd ref='104786' />\n    <nd ref='104787' />\n    <nd ref='104788' />\n    <nd ref='104784' />\n  </way>\n  <way id='102192' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true'>\n    <nd ref='104853' />\n    <nd ref='104838' />\n    <nd ref='104854' />\n    <nd ref='104853' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='102477' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true'>\n    <nd ref='104857' />\n    <nd ref='104838' />\n    <nd ref='104854' />\n    <nd ref='104858' />\n    <nd ref='104857' />\n    <tag k='natural' v='wetland' />\n  </way>\n  <way id='102506' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true'>\n    <nd ref='104862' />\n    <nd ref='104863' />\n    <nd ref='104864' />\n    <nd ref='104865' />\n    <nd ref='104866' />\n    <nd ref='104862' />\n    <tag k='natural' v='water' />\n    <tag k='water' v='pond' />\n  </way>\n  <relation id='99753' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' visible='true'>\n    <member type='way' ref='102506' role='inner' />\n    <member type='way' ref='102477' role='inner' />\n    <member type='way' ref='102192' role='inner' />\n    <member type='way' ref='101793' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/expected-json-features.txt",
    "content": "{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":8000000,\"type\":\"NODE\",\"geometry\":\"POINT (30 30)\",\"tags\":{\"foo\":\"bar\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((30 30, 30 30, 30 30, 30 30, 30 30))\",\"inEdges\":[],\"outEdges\":[11000000]}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":9000000,\"type\":\"NODE\",\"geometry\":\"POINT (32 32)\",\"tags\":{\"baz\":\"bat\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((32 32, 32 32, 32 32, 32 32, 32 32))\",\"inEdges\":[11000000],\"outEdges\":[11000001]}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":10000000,\"type\":\"NODE\",\"geometry\":\"POINT (34 34)\",\"tags\":{\"another\":\"tag2\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((34 34, 34 34, 34 34, 34 34, 34 34))\",\"inEdges\":[11000001],\"outEdges\":[]}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":11000000,\"type\":\"EDGE\",\"geometry\":\"LINESTRING (30 30, 31 30, 32 32)\",\"tags\":{\"foo\":\"bar\"},\"parentRelations\":[12000000],\"bounds\":\"POLYGON ((30 30, 30 32, 32 32, 32 30, 30 30))\",\"startNode\":8000000,\"endNode\":9000000}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":11000001,\"type\":\"EDGE\",\"geometry\":\"LINESTRING (32 32, 32 33, 34 34)\",\"tags\":{\"baz\":\"bat\"},\"parentRelations\":[12000000],\"bounds\":\"POLYGON ((32 32, 32 34, 34 34, 34 32, 32 32))\",\"startNode\":9000000,\"endNode\":10000000}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":7000000,\"type\":\"AREA\",\"geometry\":\"POLYGON ((20 20, 21 20, 21 21, 20 20))\",\"tags\":{\"baz\":\"bat\"},\"parentRelations\":[12000000],\"bounds\":\"POLYGON ((20 20, 20 21, 21 21, 21 20, 20 20))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":4000000,\"type\":\"LINE\",\"geometry\":\"LINESTRING (10 10, 11 11, 12 12)\",\"tags\":{\"this_is\":\"a_line\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((10 10, 10 12, 12 12, 12 10, 10 10))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":5000000,\"type\":\"LINE\",\"geometry\":\"LINESTRING (13 13, 14 14, 15 15)\",\"tags\":{\"foo\":\"bar\",\"this_is\":\"a_line\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((13 13, 13 15, 15 15, 15 13, 13 13))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":6000000,\"type\":\"LINE\",\"geometry\":\"LINESTRING (16 16, 17 17, 18 18)\",\"tags\":{\"another\":\"tag1\",\"hello\":\"world\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((16 16, 16 18, 18 18, 18 16, 16 16))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":1000000,\"type\":\"POINT\",\"geometry\":\"POINT (1 1)\",\"tags\":{\"foo\":\"bar\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((1 1, 1 1, 1 1, 1 1, 1 1))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":2000000,\"type\":\"POINT\",\"geometry\":\"POINT (2 2)\",\"tags\":{\"baz\":\"bat\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((2 2, 2 2, 2 2, 2 2, 2 2))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":3000000,\"type\":\"POINT\",\"geometry\":\"POINT (3 3)\",\"tags\":{\"baz\":\"bat\",\"foo\":\"bar\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((3 3, 3 3, 3 3, 3 3, 3 3))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":4000000,\"type\":\"POINT\",\"geometry\":\"POINT (4 5)\",\"tags\":{\"a\":\"b\"},\"parentRelations\":[1300000],\"bounds\":\"POLYGON ((4 5, 4 5, 4 5, 4 5, 4 5))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":5000000,\"type\":\"POINT\",\"geometry\":\"POINT (30 30)\",\"tags\":{\"a\":\"b\"},\"parentRelations\":[1400000],\"bounds\":\"POLYGON ((30 30, 30 30, 30 30, 30 30, 30 30))\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":12000000,\"type\":\"RELATION\",\"geometry\":\"null\",\"tags\":{\"route\":\"bike\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((20 20, 20 34, 34 34, 34 20, 20 20))\",\"members\":[{\"type\":\"EDGE\",\"identifier\":11000000,\"role\":\"role0\"},{\"type\":\"EDGE\",\"identifier\":11000001,\"role\":\"role1\"},{\"type\":\"AREA\",\"identifier\":7000000,\"role\":\"role2\"}],\"multiPolygonGeometry\":\"null\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":1300000,\"type\":\"RELATION\",\"geometry\":\"null\",\"tags\":{\"some\":\"relation\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((4 5, 4 5, 4 5, 4 5, 4 5))\",\"members\":[{\"type\":\"POINT\",\"identifier\":4000000,\"role\":\"*\"}],\"multiPolygonGeometry\":\"null\"}}\n{\"shard\":\"unknown_unknown\",\"path\":\"/Users/foo/test.atlas\",\"entity\":{\"identifier\":1400000,\"type\":\"RELATION\",\"geometry\":\"null\",\"tags\":{\"some2\":\"relation2\"},\"parentRelations\":[],\"bounds\":\"POLYGON ((30 30, 30 30, 30 30, 30 30, 30 30))\",\"members\":[{\"type\":\"POINT\",\"identifier\":5000000,\"role\":\"💯\"}],\"multiPolygonGeometry\":\"null\"}}\n\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/sharding-tree-1-expected.geojson",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -180.0,\n            0.0\n          ],\n          [\n            -180.0,\n            85.0511288\n          ],\n          [\n            0.0,\n            85.0511288\n          ],\n          [\n            0.0,\n            0.0\n          ],\n          [\n            -180.0,\n            0.0\n          ]\n        ]\n      },\n      \"properties\": {\n        \"shard\": \"1-0-0\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -180.0,\n            -85.0511288\n          ],\n          [\n            -180.0,\n            0.0\n          ],\n          [\n            0.0,\n            0.0\n          ],\n          [\n            0.0,\n            -85.0511288\n          ],\n          [\n            -180.0,\n            -85.0511288\n          ]\n        ]\n      },\n      \"properties\": {\n        \"shard\": \"1-0-1\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0.0,\n            0.0\n          ],\n          [\n            0.0,\n            85.0511288\n          ],\n          [\n            180.0,\n            85.0511288\n          ],\n          [\n            180.0,\n            0.0\n          ],\n          [\n            0.0,\n            0.0\n          ]\n        ]\n      },\n      \"properties\": {\n        \"shard\": \"1-1-0\"\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0.0,\n            -85.0511288\n          ],\n          [\n            0.0,\n            0.0\n          ],\n          [\n            180.0,\n            0.0\n          ],\n          [\n            180.0,\n            -85.0511288\n          ],\n          [\n            0.0,\n            -85.0511288\n          ]\n        ]\n      },\n      \"properties\": {\n        \"shard\": \"1-1-1\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/sharding-tree-1.txt",
    "content": "0-0-0+\n1-0-0\n1-0-1\n1-1-0\n1-1-1\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/shardingConverter.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-194075' action='modify' visible='true' lat='48.34286153221' lon='-4.44799965592' />\n  <node id='-194076' action='modify' visible='true' lat='48.34124777257' lon='-4.44881305288' />\n  <node id='-194078' action='modify' visible='true' lat='48.34024017084' lon='-4.44782711717' />\n  <node id='-194080' action='modify' visible='true' lat='48.3386590971' lon='-4.44855424476' />\n  <node id='-194088' action='modify' visible='true' lat='48.34091190754' lon='-4.44546087148' />\n  <node id='-194090' action='modify' visible='true' lat='48.33946192824' lon='-4.44361224203' />\n  <node id='-194092' action='modify' visible='true' lat='48.33849525246' lon='-4.44048189616' />\n  <node id='-194101' action='modify' visible='true' lat='48.34333663952' lon='-4.44896094324' />\n  <node id='-194103' action='modify' visible='true' lat='48.34333663952' lon='-4.44654540076' />\n  <node id='-194112' action='modify' visible='true' lat='48.33820033077' lon='-4.4497496918' />\n  <node id='-194114' action='modify' visible='true' lat='48.33815936929' lon='-4.44737112191' />\n  <node id='-194122' action='modify' visible='true' lat='48.34243775021' lon='-4.43492532849' />\n  <node id='-194123' action='modify' visible='true' lat='48.33967789637' lon='-4.43547249913' />\n  <node id='-194125' action='modify' visible='true' lat='48.33880783395' lon='-4.43802596211' />\n  <node id='-194134' action='modify' visible='true' lat='48.34267510401' lon='-4.43550725999' />\n  <node id='-194136' action='modify' visible='true' lat='48.34261302345' lon='-4.43390078098' />\n  <way id='-194077' action='modify' visible='true'>\n    <nd ref='-194075' />\n    <nd ref='-194076' />\n    <nd ref='-194078' />\n    <nd ref='-194080' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-194089' action='modify' visible='true'>\n    <nd ref='-194076' />\n    <nd ref='-194088' />\n    <nd ref='-194090' />\n    <nd ref='-194092' />\n    <tag k='highway' v='secondary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-194102' action='modify' visible='true'>\n    <nd ref='-194101' />\n    <nd ref='-194075' />\n    <nd ref='-194103' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-194113' action='modify' visible='true'>\n    <nd ref='-194112' />\n    <nd ref='-194080' />\n    <nd ref='-194114' />\n    <tag k='highway' v='residential' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-194124' action='modify' visible='true'>\n    <nd ref='-194122' />\n    <nd ref='-194123' />\n    <nd ref='-194125' />\n    <nd ref='-194092' />\n    <tag k='highway' v='unclassified' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-194135' action='modify' visible='true'>\n    <nd ref='-194134' />\n    <nd ref='-194122' />\n    <nd ref='-194136' />\n    <tag k='highway' v='track' />\n    <tag k='oneway' v='yes' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/atlas1.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n# Points\n1 && 1.0,1.0 && a -> b\n2 && 2.0,2.0 && c -> d\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/atlas2.atlas.txt",
    "content": "# Nodes\n# Edges\n# Areas\n# Lines\n# Points\n1 && 1.0,1.0 && a -> b\n2 && 2.0,2.0 && c -> d\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/templates/atlas_malformed.atlas.txt",
    "content": "# Nodes\nadsadasd\n# Edges\n# Areas\n# Lines\n# Points\n# Relations\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/testPbf2Atlas.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-103386' action='modify' visible='true' lat='41.29311806411' lon='9.27041177599' />\n  <node id='-103389' action='modify' visible='true' lat='41.29321251761' lon='9.27099556612' />\n  <node id='-103393' action='modify' visible='true' lat='41.29274969112' lon='9.27112821451' />\n  <node id='-103395' action='modify' visible='true' lat='41.29265523695' lon='9.27054442438' />\n  <node id='-103406' action='modify' visible='true' lat='41.29369173381' lon='9.26865886483' />\n  <node id='-103407' action='modify' visible='true' lat='41.29388319076' lon='9.26945164474' />\n  <node id='-103409' action='modify' visible='true' lat='41.29361727818' lon='9.26991174022' />\n  <node id='-103411' action='modify' visible='true' lat='41.29381405359' lon='9.27076822565' />\n  <node id='-103413' action='modify' visible='true' lat='41.29353218592' lon='9.271171694' />\n  <node id='-103421' action='modify' visible='true' lat='41.29254298032' lon='9.26856684573' />\n  <node id='-103422' action='modify' visible='true' lat='41.29248447844' lon='9.26906233317' />\n  <node id='-103424' action='modify' visible='true' lat='41.29254298032' lon='9.26970646685' />\n  <node id='-103426' action='modify' visible='true' lat='41.29228238065' lon='9.27050632515' />\n  <node id='-103434' action='modify' visible='true' lat='41.29288867215' lon='9.26909064674' />\n  <node id='-103436' action='modify' visible='true' lat='41.29208560061' lon='9.2692180578' />\n  <node id='-103497' action='modify' visible='true' lat='41.29331413651' lon='9.2696639965'>\n    <tag k='natural' v='tree' />\n  </node>\n  <way id='-103390' action='modify' visible='true'>\n    <nd ref='-103386' />\n    <nd ref='-103389' />\n    <nd ref='-103393' />\n    <nd ref='-103395' />\n    <nd ref='-103386' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='-103408' action='modify' visible='true'>\n    <nd ref='-103406' />\n    <nd ref='-103407' />\n    <nd ref='-103409' />\n    <nd ref='-103411' />\n    <nd ref='-103413' />\n    <tag k='waterway' v='river' />\n  </way>\n  <way id='-103423' action='modify' visible='true'>\n    <nd ref='-103421' />\n    <nd ref='-103422' />\n    <nd ref='-103424' />\n    <nd ref='-103426' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-103435' action='modify' visible='true'>\n    <nd ref='-103434' />\n    <nd ref='-103422' />\n    <nd ref='-103436' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <relation id='-103508' action='modify' visible='true'>\n    <member type='way' ref='-103408' role='river' />\n    <member type='node' ref='-103497' role='tree' />\n    <tag k='type' v='natural' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/test_boundary.txt",
    "content": "AIA||POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))#\nMAF||POLYGON ((0 0, 0 2, 2 2, 2 0, 0 0))#\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/command/subcommands/test_boundary_expected.geojson",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0.0,\n            0.0\n          ],\n          [\n            0.0,\n            1.0\n          ],\n          [\n            1.0,\n            1.0\n          ],\n          [\n            1.0,\n            0.0\n          ],\n          [\n            0.0,\n            0.0\n          ]\n        ]\n      },\n      \"properties\": {\n        \"iso_country_code\": \"AIA\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/ConfiguredFilterTest.json",
    "content": "{\n    \"global\":\n    {\n        \"filters\":\n        {\n            \"junctionRoundaboutFilter\":\n            {\n                \"predicate\":\n                {\n                    \"imports\": [\n                        \"org.openstreetmap.atlas.geography.atlas.items\"\n                    ],\n                    \"command\": \"e instanceof Node || e instanceof Edge\"\n                },\n                \"taggableFilter\": \"junction->roundabout\"\n            },\n            \"dummyFilter\":\n            {\n                \"predicate\":\n                {\n                    \"imports\": [\n                        \"org.openstreetmap.atlas.geography.atlas.items\"\n                    ],\n                    \"command\": \"\\\"yes\\\".equals(e.getTag(\\\"dummy\\\"))\"\n                }\n            },\n            \"tagFilterOnly\":\n            {\n                \"taggableFilter\": \"junction->roundabout\"\n            },\n            \"nothingGoesThroughFilter\":\n            {\n                \"predicate\":\n                {\n                    \"command\": \"false\"\n                },\n                \"hint.noExpansion\": true\n            },\n            \"unsafePredicateFilter\":\n            {\n                \"predicate\":\n                {\n                    \"imports\": [\n                        \"org.openstreetmap.atlas.geography.atlas.items\"\n                    ],\n                    \"unsafeCommand\": \"e instanceof Edge\"\n                }\n            },\n            \"geometryFilter1\":\n            {\n                \"geometry\":\n                {\n                    \"wkt\": [\n                        \"POLYGON ((5.9321289 43.1022153, 5.9332662 43.1029869, 5.9350204 43.1028576, 5.9353154 43.1015533, 5.9329229 43.1013732, 5.9321289 43.1022153), (5.9328725 43.1021904, 5.9342832 43.1027158, 5.9348849 43.1017956, 5.9328725 43.1021904))\",\n                        \"MULTIPOLYGON (((5.9354283 43.102112, 5.9357891 43.1024922, 5.9364067 43.1021797, 5.936046 43.1017996, 5.9354283 43.102112)), ((5.9321289 43.1022153, 5.9332662 43.1029869, 5.9350204 43.1028576, 5.9353154 43.1015533, 5.9329229 43.1013732, 5.9321289 43.1022153), (5.9328725 43.1021904, 5.9342832 43.1027158, 5.9348849 43.1017956, 5.9328725 43.1021904)))\"\n                    ]\n                }\n            },\n            \"geometryFilter2\":\n            {\n                \"geometry\":\n                {\n                    \"wkb\": [\n                        \"0103000000020000000600000036A094FF7FBA17404E531564158D45404AD97D22AABB17404DB5BBAC2E8D4540115FDCFC75BD1740911216702A8D4540D8C7F951C3BD17408326D3B2FF8C454007C2042450BB17406E5809CCF98C454036A094FF7FBA17404E531564158D454004000000DE59BBED42BB17408AFB3493148D454015CF34BCB4BC174028E494CA258D4540D0189C7752BD1740387062A3078D4540DE59BBED42BB17408AFB3493148D4540\",\n                        \"01060000000200000001030000000100000005000000F20291EAE0BD1740BB438A01128D454027D0717F3FBE17402C8CE3761E8D45406905E165E1BE174055E87239148D4540DB34B6D782BE1740795FF0C4078D4540F20291EAE0BD1740BB438A01128D45400103000000020000000600000036A094FF7FBA17404E531564158D45404AD97D22AABB17404DB5BBAC2E8D4540115FDCFC75BD1740911216702A8D4540D8C7F951C3BD17408326D3B2FF8C454007C2042450BB17406E5809CCF98C454036A094FF7FBA17404E531564158D454004000000DE59BBED42BB17408AFB3493148D454015CF34BCB4BC174028E494CA258D4540D0189C7752BD1740387062A3078D4540DE59BBED42BB17408AFB3493148D4540\"\n                    ]\n                }\n            },\n            \"regexFilterOnly\":\n            {\n                \"regexTaggableFilter\": \"source,highway|.*illegal.*,.*secondary.*\"\n            },\n            \"taggableMatcherOnly\":\n            {\n                \"taggableMatcher\": \"(highway = primary & name = 1st) | source = local\\\\ knowledge\"\n            },\n            \"taggableMatcherAndFilter\":\n            {\n                \"taggableFilter\": \"junction->roundabout\",\n                \"taggableMatcher\": \"source = illegal\\\\ source\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/ConfiguredFilterTestOtherRoot.json",
    "content": "{\n    \"my\":\n    {\n        \"root\":\n        {\n            \"geometryFilter3\":\n            {\n                \"geometry\":\n                {\n                    \"wkt\": \"POLYGON ((5.9321289 43.1022153, 5.9332662 43.1029869, 5.9350204 43.1028576, 5.9353154 43.1015533, 5.9329229 43.1013732, 5.9321289 43.1022153), (5.9328725 43.1021904, 5.9342832 43.1027158, 5.9348849 43.1017956, 5.9328725 43.1021904))\",\n                    \"wkb\": \"0103000000020000000600000036A094FF7FBA17404E531564158D45404AD97D22AABB17404DB5BBAC2E8D4540115FDCFC75BD1740911216702A8D4540D8C7F951C3BD17408326D3B2FF8C454007C2042450BB17406E5809CCF98C454036A094FF7FBA17404E531564158D454004000000DE59BBED42BB17408AFB3493148D454015CF34BCB4BC174028E494CA258D4540D0189C7752BD1740387062A3078D4540DE59BBED42BB17408AFB3493148D4540\"\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/application.json",
    "content": "{\n  \"feature\": {\n    \"ABC\": {\n      \"enable\": true,\n      \"list\": [\n        \"nodes\",\n        \"edges\"\n      ],\n      \"range\": {\n        \"max\": 20.0,\n        \"min\": 10.0\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/application.yml",
    "content": "feature:\n  ABC:\n    enable: true,\n    list:\n      - nodes\n      - edges\n    range:\n      max: 20.0\n      min: 10.0\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/development.json",
    "content": "{\n  \"feature.ABC.range\": {\n    \"max\": 30.0,\n    \"min\": 5.0\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/development.yml",
    "content": "feature.ABC.range:\n  max: 30.0\n  min: 5.0\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/developmentOverriding.json",
    "content": "{\n  \"feature.range\": {\n    \"max\": 35.0,\n    \"override.ABC.max\":70.0\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/developmentOverriding.yml",
    "content": "feature.range:\n  max: 35.0\n  override.ABC.max: 70.0\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/feature.json",
    "content": "{\n  \"feature\": {\n    \"XYZ\": {\n      \"range\": {\n        \"max\": 40.0,\n        \"min\": 20.0\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/feature.yml",
    "content": "feature:\n  XYZ:\n    range:\n      max: 40.0\n      min: 20.0\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/geometryFilter.josm.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-113425' action='modify' visible='true' lat='43.1022153092' lon='5.93212899167' />\n  <node id='-113426' action='modify' visible='true' lat='43.10298690487' lon='5.9332662483' />\n  <node id='-113427' action='modify' visible='true' lat='43.10285765347' lon='5.93502041299' />\n  <node id='-113428' action='modify' visible='true' lat='43.10155337419' lon='5.93531545598' />\n  <node id='-113429' action='modify' visible='true' lat='43.1013732013' lon='5.93292292554' />\n  <node id='-113431' action='modify' visible='true' lat='43.10219044761' lon='5.93287254088' />\n  <node id='-113432' action='modify' visible='true' lat='43.10271584253' lon='5.9342832585' />\n  <node id='-113433' action='modify' visible='true' lat='43.10179565637' lon='5.93488494785' />\n  <node id='-113434' action='modify' visible='true' lat='43.10280192376' lon='5.93254730339' />\n  <node id='-113435' action='modify' visible='true' lat='43.10245759813' lon='5.9330514215' />\n  <node id='-113436' action='modify' visible='true' lat='43.10227059289' lon='5.93360025976' />\n  <node id='-113438' action='modify' visible='true' lat='43.10221716271' lon='5.93438896068' />\n  <node id='-113439' action='modify' visible='true' lat='43.10220232099' lon='5.93397428288' />\n  <node id='-113440' action='modify' visible='true' lat='43.10234480139' lon='5.93388484257' />\n  <node id='-113443' action='modify' visible='true' lat='43.10243978814' lon='5.93534028034' />\n  <node id='-113444' action='modify' visible='true' lat='43.10199750484' lon='5.93602327907' />\n  <node id='-113445' action='modify' visible='true' lat='43.102315118' lon='5.93586066033' />\n  <node id='-113446' action='modify' visible='true' lat='43.10211205296' lon='5.93542837645' />\n  <node id='-113447' action='modify' visible='true' lat='43.10249223667' lon='5.93578914955' />\n  <node id='-113448' action='modify' visible='true' lat='43.10217979184' lon='5.93640677733' />\n  <node id='-113449' action='modify' visible='true' lat='43.10179960619' lon='5.93604600423' />\n  <way id='-102570' action='modify' visible='true'>\n    <nd ref='-113425' />\n    <nd ref='-113426' />\n    <nd ref='-113427' />\n    <nd ref='-113428' />\n    <nd ref='-113429' />\n    <nd ref='-113425' />\n  </way>\n  <way id='-102581' action='modify' visible='true'>\n    <nd ref='-113431' />\n    <nd ref='-113432' />\n    <nd ref='-113433' />\n    <nd ref='-113431' />\n    <tag k='natural' v='beach' />\n  </way>\n  <way id='-102603' action='modify' visible='true'>\n    <nd ref='-113434' />\n    <nd ref='-113435' />\n    <nd ref='-113436' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-102646' action='modify' visible='true'>\n    <nd ref='-113436' />\n    <nd ref='-113440' />\n    <nd ref='-113439' />\n    <nd ref='-113438' />\n    <tag k='highway' v='trunk' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-102706' action='modify' visible='true'>\n    <nd ref='-113438' />\n    <nd ref='-113443' />\n    <tag k='highway' v='primary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-102734' action='modify' visible='true'>\n    <nd ref='-113443' />\n    <nd ref='-113445' />\n    <nd ref='-113444' />\n    <tag k='highway' v='secondary' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-102763' action='modify' visible='true'>\n    <nd ref='-113446' />\n    <nd ref='-113447' />\n    <nd ref='-113448' />\n    <nd ref='-113449' />\n    <nd ref='-113446' />\n    <tag k='landuse' v='meadow' />\n  </way>\n  <relation id='-99768' action='modify' visible='true'>\n    <member type='way' ref='-102570' role='outer' />\n    <member type='way' ref='-102581' role='inner' />\n    <member type='way' ref='-102763' role='outer' />\n    <tag k='landuse' v='forest' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/keywordOverridingApplication.json",
    "content": "{\n  \"feature\":{\n    \"enable\":true,\n    \"list\":[\n      \"nodes\",\n      \"edges\"\n    ],\n    \"range\":{\n      \"max\":20.0,\n      \"min\":10.0\n    },\n    \"override\": {\n      \"XYZ\":{\n        \"range\":{\n          \"max\": 30.0\n        }\n      },\n      \"ABC.list\":[\"nodes\"]\n    }\n  }\n}"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/keywordOverridingApplication.yml",
    "content": "feature:\n  enable: true\n  list:\n    - nodes\n    - edges\n  range:\n    max: 20.0\n    min: 10.0\n  override:\n    XYZ:\n      range:\n        max: 30.0\n    ABC.list:\n      - nodes\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/yaml_dot_compressed.yml",
    "content": "a.b.c: 0\na.b.d: 1\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/configuration/yaml_dot_expanded.yml",
    "content": "a:\n  b:\n    c: 0\n    d: 1\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/filters/includeExcludePolygonArrangements.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-38986' action='modify' lat='26.97532157261' lon='-45.28701816559' />\n  <node id='-38988' action='modify' lat='26.97470963111' lon='-45.17200504303' />\n  <node id='-38990' action='modify' lat='26.87491874122' lon='-45.17440830231' />\n  <node id='-38992' action='modify' lat='26.87614370399' lon='-45.29045139313' />\n  <node id='-38994' action='modify' lat='26.95910399899' lon='-45.21869693756' />\n  <node id='-38996' action='modify' lat='26.95910399899' lon='-45.19363437653' />\n  <node id='-38998' action='modify' lat='26.93706883755' lon='-45.19397769928' />\n  <node id='-39000' action='modify' lat='26.93676276331' lon='-45.21869693756' />\n  <node id='-39002' action='modify' lat='26.88808639533' lon='-45.30864749908' />\n  <node id='-39004' action='modify' lat='26.88533050164' lon='-45.12874637604' />\n  <node id='-39006' action='modify' lat='26.78116977658' lon='-45.13114963531' />\n  <node id='-39008' action='modify' lat='26.78392820597' lon='-45.31585727692' />\n  <node id='-39010' action='modify' lat='26.98266461115' lon='-45.38589511871' />\n  <node id='-39012' action='modify' lat='26.98235866077' lon='-45.33439670563' />\n  <node id='-39014' action='modify' lat='26.93278372258' lon='-45.33508335114' />\n  <node id='-39016' action='modify' lat='26.93339589183' lon='-45.38658176422' />\n  <node id='-39018' action='modify' lat='27.05178835252' lon='-45.426407547' />\n  <node id='-39020' action='modify' lat='27.05056529863' lon='-45.0487525177' />\n  <node id='-39022' action='modify' lat='26.69286497728' lon='-45.04463264465' />\n  <node id='-39024' action='modify' lat='26.70513367403' lon='-45.44563362122' />\n  <node id='-39026' action='modify' lat='26.95436052931' lon='-45.23019807816' />\n  <node id='-39028' action='modify' lat='26.95436052931' lon='-45.20891206741' />\n  <node id='-39030' action='modify' lat='26.94808680378' lon='-45.24942415237' />\n  <node id='-39032' action='modify' lat='26.94823982563' lon='-45.23019807816' />\n  <node id='-39034' action='modify' lat='26.94833588924' lon='-45.16047971726' />\n  <node id='-39036' action='modify' lat='26.94848891076' lon='-45.14588850021' />\n  <node id='-39038' action='modify' lat='26.90462034578' lon='-45.26744876862' />\n  <node id='-39040' action='modify' lat='26.89237320746' lon='-45.26744876862' />\n  <node id='-39042' action='modify' lat='26.89145461855' lon='-45.257149086' />\n  <node id='-39044' action='modify' lat='26.87116721019' lon='-45.257149086' />\n  <node id='-39046' action='modify' lat='26.86955921994' lon='-45.26916521072' />\n  <node id='-39048' action='modify' lat='26.850110237' lon='-45.26950853348' />\n  <node id='-39050' action='modify' lat='26.96124607654' lon='-45.27774845123' />\n  <node id='-39052' action='modify' lat='26.96430611665' lon='-45.34469638824' />\n  <node id='-39054' action='modify' lat='26.9523714901' lon='-45.27362857819' />\n  <node id='-39056' action='modify' lat='26.95267752196' lon='-45.28427158356' />\n  <node id='-39058' action='modify' lat='26.97623947862' lon='-45.30590091705' />\n  <node id='-39060' action='modify' lat='26.97654544563' lon='-45.32203708649' />\n  <node id='-39062' action='modify' lat='26.9572679001' lon='-45.25680576324' />\n  <node id='-39064' action='modify' lat='26.96644809526' lon='-45.25680576324' />\n  <node id='-39066' action='modify' lat='26.96736607362' lon='-45.24616275787' />\n  <node id='-39068' action='modify' lat='27.0001024073' lon='-45.24547611237' />\n  <node id='-39070' action='modify' lat='26.99979650435' lon='-45.26195560455' />\n  <node id='-39072' action='modify' lat='27.02579528372' lon='-45.26195560455' />\n  <way id='-39074' action='modify'>\n    <nd ref='-38986' />\n    <nd ref='-38988' />\n    <nd ref='-38990' />\n    <nd ref='-38992' />\n    <nd ref='-38986' />\n    <tag k='name' v='include' />\n  </way>\n  <way id='-39076' action='modify'>\n    <nd ref='-38994' />\n    <nd ref='-38996' />\n    <nd ref='-38998' />\n    <nd ref='-39000' />\n    <nd ref='-38994' />\n    <tag k='name' v='exclude_case_1' />\n  </way>\n  <way id='-39078' action='modify'>\n    <nd ref='-39002' />\n    <nd ref='-39004' />\n    <nd ref='-39006' />\n    <nd ref='-39008' />\n    <nd ref='-39002' />\n    <tag k='name' v='exclude_case_2' />\n  </way>\n  <way id='-39080' action='modify'>\n    <nd ref='-39010' />\n    <nd ref='-39012' />\n    <nd ref='-39014' />\n    <nd ref='-39016' />\n    <nd ref='-39010' />\n    <tag k='name' v='exclude_case_3' />\n  </way>\n  <way id='-39082' action='modify'>\n    <nd ref='-39018' />\n    <nd ref='-39020' />\n    <nd ref='-39022' />\n    <nd ref='-39024' />\n    <nd ref='-39018' />\n    <tag k='name' v='exclude_case_4' />\n  </way>\n  <way id='-39084' action='modify'>\n    <nd ref='-39026' />\n    <nd ref='-39028' />\n    <tag k='name' v='case_1_included' />\n  </way>\n  <way id='-39086' action='modify'>\n    <nd ref='-39030' />\n    <nd ref='-39032' />\n    <tag k='name' v='case_1_included' />\n  </way>\n  <way id='-39088' action='modify'>\n    <nd ref='-39034' />\n    <nd ref='-39036' />\n    <tag k='name' v='case_1_excluded' />\n  </way>\n  <way id='-39090' action='modify'>\n    <nd ref='-39038' />\n    <nd ref='-39040' />\n    <tag k='name' v='case_2_included' />\n  </way>\n  <way id='-39092' action='modify'>\n    <nd ref='-39042' />\n    <nd ref='-39044' />\n    <tag k='name' v='case_2_included' />\n  </way>\n  <way id='-39094' action='modify'>\n    <nd ref='-39046' />\n    <nd ref='-39048' />\n    <tag k='name' v='case_2_excluded' />\n  </way>\n  <way id='-39096' action='modify'>\n    <nd ref='-39050' />\n    <nd ref='-39052' />\n    <tag k='name' v='case_3_included' />\n  </way>\n  <way id='-39098' action='modify'>\n    <nd ref='-39054' />\n    <nd ref='-39056' />\n    <tag k='name' v='case_3_included' />\n  </way>\n  <way id='-39100' action='modify'>\n    <nd ref='-39058' />\n    <nd ref='-39060' />\n    <tag k='name' v='case_3_excluded' />\n  </way>\n  <way id='-39102' action='modify'>\n    <nd ref='-39062' />\n    <nd ref='-39064' />\n    <tag k='name' v='case_4_included' />\n  </way>\n  <way id='-39104' action='modify'>\n    <nd ref='-39066' />\n    <nd ref='-39068' />\n    <tag k='name' v='case_4_included' />\n  </way>\n  <way id='-39106' action='modify'>\n    <nd ref='-39070' />\n    <nd ref='-39072' />\n    <tag k='name' v='case_4_excluded' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/filters/multiPolygons.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-39906' action='modify' lat='34.27750317356' lon='-33.60906618118' />\n  <node id='-39909' action='modify' lat='34.27750317356' lon='-33.58554857254' />\n  <node id='-39913' action='modify' lat='34.25877726571' lon='-33.58486192703' />\n  <node id='-39915' action='modify' lat='34.25920290083' lon='-33.60975282669' />\n  <node id='-39919' action='modify' lat='34.28232583805' lon='-33.53920000076' />\n  <node id='-39920' action='modify' lat='34.28190031996' lon='-33.50709932327' />\n  <node id='-39922' action='modify' lat='34.24175009505' lon='-33.5069276619' />\n  <node id='-39924' action='modify' lat='34.24231772291' lon='-33.55190294266' />\n  <node id='-39926' action='modify' lat='34.28190031996' lon='-33.55052965164' />\n  <node id='-39930' action='modify' lat='34.26104722387' lon='-33.54520806313' />\n  <node id='-39931' action='modify' lat='34.26083441124' lon='-33.53035935402' />\n  <node id='-39933' action='modify' lat='34.2477808744' lon='-33.53061684608' />\n  <node id='-39935' action='modify' lat='34.24820656516' lon='-33.54529389381' />\n  <node id='-39939' action='modify' lat='34.27757402618' lon='-33.52769860268' />\n  <node id='-39940' action='modify' lat='34.26402654409' lon='-33.52821358681' />\n  <node id='-39942' action='modify' lat='34.263813739' lon='-33.51284989357' />\n  <node id='-39944' action='modify' lat='34.27771587308' lon='-33.51267823219' />\n  <node id='-39948' action='modify' lat='34.22826795032' lon='-33.61301456451' />\n  <node id='-39949' action='modify' lat='34.22628090452' lon='-33.58417545319' />\n  <node id='-39951' action='modify' lat='34.21975170967' lon='-33.57524906158' />\n  <node id='-39953' action='modify' lat='34.20839538286' lon='-33.57421909332' />\n  <node id='-39955' action='modify' lat='34.1953337151' lon='-33.58486209869' />\n  <node id='-39957' action='modify' lat='34.18539413402' lon='-33.62159763336' />\n  <node id='-39959' action='modify' lat='34.19817338024' lon='-33.65181003571' />\n  <node id='-39961' action='modify' lat='34.21577716939' lon='-33.68476902008' />\n  <node id='-39963' action='modify' lat='34.2004450435' lon='-33.69541202545' />\n  <node id='-39965' action='modify' lat='34.181986008' lon='-33.68476902008' />\n  <node id='-39967' action='modify' lat='34.17090864758' lon='-33.66073642731' />\n  <node id='-39969' action='modify' lat='34.16522738588' lon='-33.69712863922' />\n  <node id='-39971' action='modify' lat='34.18738214395' lon='-33.71704135895' />\n  <node id='-39973' action='modify' lat='34.22997109513' lon='-33.68648563385' />\n  <node id='-39975' action='modify' lat='34.22968724005' lon='-33.67446933746' />\n  <node id='-39977' action='modify' lat='34.22315830926' lon='-33.65627323151' />\n  <node id='-39979' action='modify' lat='34.21492545779' lon='-33.64837680817' />\n  <node id='-39981' action='modify' lat='34.21492545779' lon='-33.63327060699' />\n  <node id='-39983' action='modify' lat='34.22684863668' lon='-33.63018070221' />\n  <node id='-39985' action='modify' lat='34.23394496572' lon='-33.62571750641' />\n  <node id='-39987' action='modify' lat='34.23422880644' lon='-33.61713443756' />\n  <node id='-39989' action='modify' lat='34.23053880241' lon='-33.61507450104' />\n  <node id='-39993' action='modify' lat='34.21776446297' lon='-33.5989383316' />\n  <node id='-39994' action='modify' lat='34.21180244161' lon='-33.62228427887' />\n  <node id='-39996' action='modify' lat='34.20328453649' lon='-33.62262760162' />\n  <node id='-39998' action='modify' lat='34.2004450435' lon='-33.60992465973' />\n  <node id='-40000' action='modify' lat='34.20356848053' lon='-33.59687839508' />\n  <node id='-40002' action='modify' lat='34.20953108441' lon='-33.58863864899' />\n  <node id='-40004' action='modify' lat='34.21322200876' lon='-33.58760868073' />\n  <node id='-40019' action='modify' lat='34.27665208639' lon='-33.5448648262'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40020' action='modify' lat='34.26927563672' lon='-33.53920000076'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40021' action='modify' lat='34.27253837697' lon='-33.51842897415'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40022' action='modify' lat='34.2548045674' lon='-33.53851335526'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40023' action='modify' lat='34.25210870099' lon='-33.51757066727'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40024' action='modify' lat='34.27168723955' lon='-33.6037446785'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40025' action='modify' lat='34.26941749763' lon='-33.59241502762'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40026' action='modify' lat='34.26402661502' lon='-33.60134141922'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40031' action='modify' lat='34.25792598901' lon='-33.53576677322' />\n  <node id='-40032' action='modify' lat='34.25125735681' lon='-33.5359384346' />\n  <node id='-40034' action='modify' lat='34.25806786906' lon='-33.54194658279' />\n  <node id='-40035' action='modify' lat='34.25210870099' lon='-33.54228990555' />\n  <node id='-40037' action='modify' lat='34.27324765158' lon='-33.52357881546' />\n  <node id='-40038' action='modify' lat='34.26728955893' lon='-33.52357881546' />\n  <node id='-40040' action='modify' lat='34.2675732872' lon='-33.51980226517' />\n  <node id='-40042' action='modify' lat='34.27310579714' lon='-33.52066057205' />\n  <node id='-40045' action='modify' lat='34.25707470369' lon='-33.52151887894' />\n  <node id='-40046' action='modify' lat='34.25707470369' lon='-33.51396577835' />\n  <node id='-40048' action='modify' lat='34.26133104413' lon='-33.51602571487' />\n  <node id='-40050' action='modify' lat='34.26076354452' lon='-33.52306383133' />\n  <node id='-40054' action='modify' lat='34.27126166761' lon='-33.54280488968' />\n  <node id='-40055' action='modify' lat='34.26601276987' lon='-33.54125993729' />\n  <node id='-40057' action='modify' lat='34.26743142319' lon='-33.5337068367' />\n  <node id='-40059' action='modify' lat='34.27282208753' lon='-33.53628175735' />\n  <node id='-40062' action='modify' lat='34.24586531032' lon='-33.52272050858' />\n  <node id='-40063' action='modify' lat='34.24629101076' lon='-33.51448076248' />\n  <node id='-40065' action='modify' lat='34.24969653678' lon='-33.51259248734' />\n  <node id='-40067' action='modify' lat='34.26743156505' lon='-33.6034015274' />\n  <node id='-40068' action='modify' lat='34.26374301666' lon='-33.59310184479' />\n  <node id='-40071' action='modify' lat='34.20754359665' lon='-33.64940677643' />\n  <node id='-40072' action='modify' lat='34.19675355962' lon='-33.62983737946' />\n  <node id='-40074' action='modify' lat='34.19419782226' lon='-33.60958133698' />\n  <node id='-40076' action='modify' lat='34.20328453649' lon='-33.58623538971' />\n  <node id='-40078' action='modify' lat='34.21890003825' lon='-33.5855487442' />\n  <node id='-40080' action='modify' lat='34.22173890951' lon='-33.61438785553' />\n  <node id='-40082' action='modify' lat='34.20811145508' lon='-33.63464389801' />\n  <node id='-40161' action='modify' lat='34.21833225252' lon='-33.66485630035' />\n  <node id='-40162' action='modify' lat='34.21265418477' lon='-33.66725955963' />\n  <node id='-40164' action='modify' lat='34.21066677066' lon='-33.6600497818' />\n  <node id='-40166' action='modify' lat='34.21606107134' lon='-33.65764652252' />\n  <node id='-40170' action='modify' lat='34.21066677066' lon='-33.64563022614'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <node id='-40171' action='modify' lat='34.20640786832' lon='-33.63979373932'>\n    <tag k='name' v='aPoint' />\n  </node>\n  <way id='-39910' action='modify'>\n    <nd ref='-39906' />\n    <nd ref='-39909' />\n    <nd ref='-39913' />\n    <nd ref='-39915' />\n    <nd ref='-39906' />\n    <tag k='name' v='polygon1' />\n  </way>\n  <way id='-39921' action='modify'>\n    <nd ref='-39919' />\n    <nd ref='-39920' />\n    <nd ref='-39922' />\n    <nd ref='-39924' />\n    <nd ref='-39926' />\n    <nd ref='-39919' />\n    <tag k='name' v='multipolygon1-outer1' />\n  </way>\n  <way id='-39932' action='modify'>\n    <nd ref='-39930' />\n    <nd ref='-39931' />\n    <nd ref='-39933' />\n    <nd ref='-39935' />\n    <nd ref='-39930' />\n    <tag k='name' v='multipolygon1-inner1' />\n  </way>\n  <way id='-39941' action='modify'>\n    <nd ref='-39939' />\n    <nd ref='-39940' />\n    <nd ref='-39942' />\n    <nd ref='-39944' />\n    <nd ref='-39939' />\n    <tag k='name' v='multipolygon1-inner2' />\n  </way>\n  <way id='-39950' action='modify'>\n    <nd ref='-39948' />\n    <nd ref='-39949' />\n    <nd ref='-39951' />\n    <nd ref='-39953' />\n    <nd ref='-39955' />\n    <nd ref='-39957' />\n    <nd ref='-39959' />\n    <nd ref='-39961' />\n    <nd ref='-39963' />\n    <nd ref='-39965' />\n    <nd ref='-39967' />\n    <nd ref='-39969' />\n    <nd ref='-39971' />\n    <nd ref='-39973' />\n    <nd ref='-39975' />\n    <nd ref='-39977' />\n    <nd ref='-39979' />\n    <nd ref='-39981' />\n    <nd ref='-39983' />\n    <nd ref='-39985' />\n    <nd ref='-39987' />\n    <nd ref='-39989' />\n    <nd ref='-39948' />\n    <tag k='name' v='multipolygon2-outer1' />\n  </way>\n  <way id='-39995' action='modify'>\n    <nd ref='-39993' />\n    <nd ref='-39994' />\n    <nd ref='-39996' />\n    <nd ref='-39998' />\n    <nd ref='-40000' />\n    <nd ref='-40002' />\n    <nd ref='-40004' />\n    <nd ref='-39993' />\n    <tag k='name' v='multipolygon2-inner1' />\n  </way>\n  <way id='-40033' action='modify'>\n    <nd ref='-40031' />\n    <nd ref='-40032' />\n  </way>\n  <way id='-40036' action='modify'>\n    <nd ref='-40034' />\n    <nd ref='-40035' />\n  </way>\n  <way id='-40039' action='modify'>\n    <nd ref='-40037' />\n    <nd ref='-40038' />\n    <nd ref='-40040' />\n    <nd ref='-40042' />\n    <nd ref='-40037' />\n  </way>\n  <way id='-40047' action='modify'>\n    <nd ref='-40045' />\n    <nd ref='-40046' />\n    <nd ref='-40048' />\n    <nd ref='-40050' />\n    <nd ref='-40045' />\n  </way>\n  <way id='-40056' action='modify'>\n    <nd ref='-40054' />\n    <nd ref='-40055' />\n    <nd ref='-40057' />\n    <nd ref='-40059' />\n    <nd ref='-40054' />\n  </way>\n  <way id='-40064' action='modify'>\n    <nd ref='-40062' />\n    <nd ref='-40063' />\n    <nd ref='-40065' />\n  </way>\n  <way id='-40069' action='modify'>\n    <nd ref='-40067' />\n    <nd ref='-40068' />\n  </way>\n  <way id='-40073' action='modify'>\n    <nd ref='-40071' />\n    <nd ref='-40072' />\n    <nd ref='-40074' />\n    <nd ref='-40076' />\n    <nd ref='-40078' />\n    <nd ref='-40080' />\n    <nd ref='-40082' />\n  </way>\n  <way id='-40163' action='modify'>\n    <nd ref='-40161' />\n    <nd ref='-40162' />\n    <nd ref='-40164' />\n    <nd ref='-40166' />\n    <nd ref='-40161' />\n  </way>\n  <relation id='-40242' action='modify'>\n    <member type='node' ref='-40025' role='memeber1' />\n    <member type='way' ref='-40163' role='memeber2' />\n    <member type='way' ref='-40064' role='memeber3' />\n    <tag k='name' v='relation1' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/filters/testCounts.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-39463' action='modify' lat='22.51657258543' lon='-81.15236564257' />\n  <node id='-39464' action='modify' lat='22.52022381399' lon='-81.11409741146' />\n  <node id='-39466' action='modify' lat='22.50727811384' lon='-81.07870378927' />\n  <node id='-39468' action='modify' lat='22.47092409354' lon='-81.0769071587' />\n  <node id='-39470' action='modify' lat='22.44933956686' lon='-81.11194145478' />\n  <node id='-39472' action='modify' lat='22.44817722775' lon='-81.17320655716' />\n  <node id='-39474' action='modify' lat='22.47407847354' lon='-81.19710174372' />\n  <node id='-39476' action='modify' lat='22.50910386294' lon='-81.19243050424' />\n  <node id='-39478' action='modify' lat='22.52885360709' lon='-81.15775553427' />\n  <node id='-39482' action='modify' lat='22.45564973583' lon='-81.34783958738' />\n  <node id='-39483' action='modify' lat='22.50213318921' lon='-81.30400180151' />\n  <node id='-39485' action='modify' lat='22.53466231595' lon='-81.25225884115' />\n  <node id='-39487' action='modify' lat='22.52204988998' lon='-81.21560757756' />\n  <node id='-39489' action='modify' lat='22.57249268064' lon='-81.17967496619' />\n  <node id='-39491' action='modify' lat='22.41380196055' lon='-81.18758085934' />\n  <node id='-39492' action='modify' lat='22.46428422097' lon='-81.14302442125' />\n  <node id='-39494' action='modify' lat='22.37925089053' lon='-81.02804006489' />\n  <node id='-39496' action='modify' lat='22.48951746297' lon='-81.16170830118' />\n  <node id='-39497' action='modify' lat='22.48785745705' lon='-81.12290108091' />\n  <node id='-39530' action='modify' lat='22.50279679349' lon='-81.17392538905'>\n    <tag k='name' v='point1' />\n  </node>\n  <node id='-39531' action='modify' lat='22.49648927042' lon='-81.10313814466'>\n    <tag k='name' v='point2' />\n  </node>\n  <node id='-39532' action='modify' lat='22.34003765932' lon='-81.20195318524' />\n  <node id='-39533' action='modify' lat='22.29217009178' lon='-81.34783958738' />\n  <node id='-39535' action='modify' lat='22.24495129088' lon='-81.27022514683' />\n  <node id='-39537' action='modify' lat='22.24495129088' lon='-81.05534813087' />\n  <node id='-39539' action='modify' lat='22.26490484086' lon='-81.00360517051' />\n  <node id='-39541' action='modify' lat='22.32408362748' lon='-81.09056209001' />\n  <node id='-39543' action='modify' lat='22.33006660327' lon='-81.16602057387' />\n  <node id='-39546' action='modify' lat='22.4682682159' lon='-81.16817653056' />\n  <node id='-39547' action='modify' lat='22.42310203991' lon='-81.25800805897' />\n  <node id='-39549' action='modify' lat='22.31830515826' lon='-81.26819371853' />\n  <node id='-39552' action='modify' lat='22.28352558777' lon='-81.23860444883' />\n  <node id='-39625' action='modify' lat='22.332726286' lon='-81.00648049807' />\n  <node id='-39626' action='modify' lat='22.33139678335' lon='-80.87137387933' />\n  <node id='-39628' action='modify' lat='22.19172859815' lon='-80.88143501052' />\n  <node id='-39630' action='modify' lat='22.20104414329' lon='-81.01941623816' />\n  <node id='-39768' action='modify' lat='22.38757524055' lon='-81.40228557444'>\n    <tag k='name' v='point3' />\n  </node>\n  <way id='-39465' action='modify'>\n    <nd ref='-39463' />\n    <nd ref='-39464' />\n    <nd ref='-39466' />\n    <nd ref='-39468' />\n    <nd ref='-39470' />\n    <nd ref='-39472' />\n    <nd ref='-39474' />\n    <nd ref='-39476' />\n    <nd ref='-39478' />\n    <nd ref='-39463' />\n    <tag k='name' v='polygon1' />\n  </way>\n  <way id='-39484' action='modify'>\n    <nd ref='-39482' />\n    <nd ref='-39483' />\n    <nd ref='-39485' />\n    <nd ref='-39487' />\n    <nd ref='-39489' />\n  </way>\n  <way id='-39493' action='modify'>\n    <nd ref='-39491' />\n    <nd ref='-39492' />\n    <nd ref='-39494' />\n  </way>\n  <way id='-39498' action='modify'>\n    <nd ref='-39496' />\n    <nd ref='-39497' />\n  </way>\n  <way id='-39534' action='modify'>\n    <nd ref='-39532' />\n    <nd ref='-39549' />\n    <nd ref='-39533' />\n    <nd ref='-39535' />\n    <nd ref='-39537' />\n    <nd ref='-39539' />\n    <nd ref='-39541' />\n    <nd ref='-39543' />\n    <nd ref='-39532' />\n    <tag k='name' v='polygon2' />\n  </way>\n  <way id='-39548' action='modify'>\n    <nd ref='-39546' />\n    <nd ref='-39547' />\n    <nd ref='-39549' />\n    <nd ref='-39552' />\n  </way>\n  <way id='-39627' action='modify'>\n    <nd ref='-39625' />\n    <nd ref='-39626' />\n    <nd ref='-39628' />\n  </way>\n  <way id='-39631' action='modify'>\n    <nd ref='-39630' />\n    <nd ref='-39625' />\n  </way>\n  <relation id='-39636' action='modify'>\n    <member type='way' ref='-39631' role='barrier' />\n    <member type='way' ref='-39627' role='barrier' />\n    <tag k='name' v='relation1' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/filters/testForms.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-39232' action='modify' lat='22.47789683741' lon='-81.11176179172' />\n  <node id='-39233' action='modify' lat='22.47806285085' lon='-81.06864265809' />\n  <node id='-39235' action='modify' lat='22.50860593376' lon='-81.06702569057' />\n  <node id='-39237' action='modify' lat='22.50727811384' lon='-81.11122280255' />\n  <node id='-39252' action='modify' lat='22.49350122996' lon='-81.10259897583' />\n  <node id='-39253' action='modify' lat='22.49798301747' lon='-81.08984289879' />\n  <node id='-39255' action='modify' lat='22.4886872965' lon='-81.09487346438' />\n  <node id='-39256' action='modify' lat='22.49433120156' lon='-81.08193772429' />\n  <node id='-39258' action='modify' lat='22.48769128936' lon='-81.08283603958'>\n    <tag k='name' v='hey' />\n  </node>\n  <node id='-39259' action='modify' lat='22.49964290192' lon='-81.10008369303'>\n    <tag k='name' v='hey' />\n  </node>\n  <node id='-39260' action='modify' lat='22.50254756879' lon='-81.10583282102' />\n  <node id='-39261' action='modify' lat='22.50296252808' lon='-81.07457144913' />\n  <node id='-39263' action='modify' lat='22.48320908553' lon='-81.07627824817' />\n  <node id='-39265' action='modify' lat='22.48337509259' lon='-81.10915658757' />\n  <node id='-39286' action='modify' lat='22.51790023309' lon='-81.11562445761' />\n  <node id='-39287' action='modify' lat='22.51980882353' lon='-81.0896631459' />\n  <node id='-39289' action='modify' lat='22.51524475905' lon='-81.08696820005' />\n  <node id='-39290' action='modify' lat='22.52196632882' lon='-81.07016970424' />\n  <node id='-39292' action='modify' lat='22.5008877192' lon='-81.12308047447'>\n    <tag k='name' v='hey' />\n  </node>\n  <node id='-39299' action='modify' lat='22.46893173352' lon='-81.06262385585' />\n  <node id='-39300' action='modify' lat='22.46926378168' lon='-81.04977794729' />\n  <node id='-39302' action='modify' lat='22.46303774628' lon='-81.05346103995' />\n  <node id='-39304' action='modify' lat='22.46278869905' lon='-81.06603745393' />\n  <way id='-39234' action='modify'>\n    <nd ref='-39232' />\n    <nd ref='-39233' />\n    <nd ref='-39235' />\n    <nd ref='-39237' />\n    <nd ref='-39232' />\n    <tag k='name' v='include' />\n  </way>\n  <way id='-39254' action='modify'>\n    <nd ref='-39252' />\n    <nd ref='-39253' />\n  </way>\n  <way id='-39257' action='modify'>\n    <nd ref='-39255' />\n    <nd ref='-39256' />\n  </way>\n  <way id='-39262' action='modify'>\n    <nd ref='-39260' />\n    <nd ref='-39261' />\n    <nd ref='-39263' />\n    <nd ref='-39265' />\n    <nd ref='-39260' />\n  </way>\n  <way id='-39288' action='modify'>\n    <nd ref='-39286' />\n    <nd ref='-39287' />\n  </way>\n  <way id='-39291' action='modify'>\n    <nd ref='-39289' />\n    <nd ref='-39290' />\n  </way>\n  <way id='-39301' action='modify'>\n    <nd ref='-39299' />\n    <nd ref='-39300' />\n    <nd ref='-39302' />\n    <nd ref='-39304' />\n    <nd ref='-39299' />\n  </way>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/testing/josmOsmFile.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM'>\n  <node id='-1409998' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='-1409999' action='modify' lat='7.01400307686' lon='-10.54665453727' />\n  <node id='-1410001' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <node id='-1410003' action='modify' lat='7.01317734799' lon='-10.54639734007' />\n  <node id='-1410005' action='modify' lat='7.01283872058' lon='-10.54584357876' />\n  <node id='-1410007' action='modify' lat='7.01390669848' lon='-10.54582520753' />\n  <node id='-1410009' action='modify' lat='7.01386502134' lon='-10.54528719299' />\n  <node id='-1410011' action='modify' lat='7.01352899926' lon='-10.54497750656' />\n  <node id='-1410013' action='modify' lat='7.01375561883' lon='-10.54479379428' />\n  <node id='-1410038' action='modify' lat='7.0143390986' lon='-10.54653381206' />\n  <node id='-1410039' action='modify' lat='7.01445631542' lon='-10.54617426087' />\n  <node id='-1410040' action='modify' lat='7.0142161719' lon='-10.5460947868' />\n  <node id='-1410041' action='modify' lat='7.01409895502' lon='-10.54645433798' />\n  <node id='-1410044' action='modify' lat='7.01328016806' lon='-10.54558045762' />\n  <node id='-1410045' action='modify' lat='7.01327244286' lon='-10.54497940386' />\n  <node id='-1410047' action='modify' lat='7.01296382279' lon='-10.54498343051' />\n  <node id='-1410049' action='modify' lat='7.01297154799' lon='-10.54558448427' />\n  <node id='-1410053' action='modify' lat='7.01316627317' lon='-10.54543612693' />\n  <node id='-1410054' action='modify' lat='7.01316237444' lon='-10.54513562601' />\n  <node id='-1410056' action='modify' lat='7.01306208877' lon='-10.54513694681' />\n  <node id='-1410058' action='modify' lat='7.0130659875' lon='-10.54543744773' />\n  <node id='-1410070' action='modify' lat='7.01345866903' lon='-10.54661517035'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='-1410095' action='modify' lat='7.0145452395' lon='-10.54658593494' />\n  <node id='-1410096' action='modify' lat='7.01453752104' lon='-10.54579789937' />\n  <node id='-1410098' action='modify' lat='7.01414130659' lon='-10.54548164825' />\n  <node id='-1410100' action='modify' lat='7.01401781111' lon='-10.54494505824' />\n  <way id='-1410000' action='modify'>\n    <nd ref='-1409998' />\n    <nd ref='-1409999' />\n    <nd ref='-1410001' />\n    <nd ref='-1410003' />\n    <nd ref='-1410005' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='-1410008' action='modify'>\n    <nd ref='-1410001' />\n    <nd ref='-1410007' />\n    <nd ref='-1410009' />\n    <nd ref='-1410011' />\n    <nd ref='-1410013' />\n    <tag k='highway' v='primary' />\n  </way>\n  <way id='-1410042' action='modify'>\n    <nd ref='-1410038' />\n    <nd ref='-1410039' />\n    <nd ref='-1410040' />\n    <nd ref='-1410041' />\n    <nd ref='-1410038' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='-1410046' action='modify'>\n    <nd ref='-1410044' />\n    <nd ref='-1410045' />\n    <nd ref='-1410047' />\n    <nd ref='-1410049' />\n    <nd ref='-1410044' />\n  </way>\n  <way id='-1410055' action='modify'>\n    <nd ref='-1410053' />\n    <nd ref='-1410054' />\n    <nd ref='-1410056' />\n    <nd ref='-1410058' />\n    <nd ref='-1410053' />\n  </way>\n  <way id='-1410097' action='modify'>\n    <nd ref='-1410095' />\n    <nd ref='-1410096' />\n    <nd ref='-1410098' />\n    <nd ref='-1410100' />\n    <tag k='waterway' v='canal' />\n  </way>\n  <relation id='-1410065' action='modify'>\n    <member type='way' ref='-1410046' role='outer' />\n    <member type='way' ref='-1410055' role='inner' />\n    <tag k='building' v='yes' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/testing/osmFile.osm",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='JOSM' timestamp='2017-12-19T21:43:02Z'>\n  <node id='1409998' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01378166705' lon='-10.54718467843' />\n  <node id='1409999' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01400307686' lon='-10.54665453727' />\n  <node id='1410001' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01357328126' lon='-10.54613226949' />\n  <node id='1410003' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01317734799' lon='-10.54639734007' />\n  <node id='1410005' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01283872058' lon='-10.54584357876' />\n  <node id='1410007' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01390669848' lon='-10.54582520753' />\n  <node id='1410009' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01386502134' lon='-10.54528719299' />\n  <node id='1410011' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01352899926' lon='-10.54497750656' />\n  <node id='1410013' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01375561883' lon='-10.54479379428' />\n  <node id='1410038' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.0143390986' lon='-10.54653381206' />\n  <node id='1410039' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01445631542' lon='-10.54617426087' />\n  <node id='1410040' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.0142161719' lon='-10.5460947868' />\n  <node id='1410041' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01409895502' lon='-10.54645433798' />\n  <node id='1410044' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01328016806' lon='-10.54558045762' />\n  <node id='1410045' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01327244286' lon='-10.54497940386' />\n  <node id='1410047' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01296382279' lon='-10.54498343051' />\n  <node id='1410049' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01297154799' lon='-10.54558448427' />\n  <node id='1410053' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01316627317' lon='-10.54543612693' />\n  <node id='1410054' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01316237444' lon='-10.54513562601' />\n  <node id='1410056' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01306208877' lon='-10.54513694681' />\n  <node id='1410058' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.0130659875' lon='-10.54543744773' />\n  <node id='1410070' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01345866903' lon='-10.54661517035'>\n    <tag k='natural' v='tree' />\n  </node>\n  <node id='1410095' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.0145452395' lon='-10.54658593494' />\n  <node id='1410096' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01453752104' lon='-10.54579789937' />\n  <node id='1410098' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01414130659' lon='-10.54548164825' />\n  <node id='1410100' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify' lat='7.01401781111' lon='-10.54494505824' />\n  <way id='1410000' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <nd ref='1409998' />\n    <nd ref='1409999' />\n    <nd ref='1410001' />\n    <nd ref='1410003' />\n    <nd ref='1410005' />\n    <tag k='highway' v='motorway' />\n    <tag k='oneway' v='yes' />\n  </way>\n  <way id='1410008' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <nd ref='1410001' />\n    <nd ref='1410007' />\n    <nd ref='1410009' />\n    <nd ref='1410011' />\n    <nd ref='1410013' />\n    <tag k='highway' v='primary' />\n  </way>\n  <way id='1410042' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <nd ref='1410038' />\n    <nd ref='1410039' />\n    <nd ref='1410040' />\n    <nd ref='1410041' />\n    <nd ref='1410038' />\n    <tag k='building' v='yes' />\n  </way>\n  <way id='1410046' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <nd ref='1410044' />\n    <nd ref='1410045' />\n    <nd ref='1410047' />\n    <nd ref='1410049' />\n    <nd ref='1410044' />\n  </way>\n  <way id='1410055' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <nd ref='1410053' />\n    <nd ref='1410054' />\n    <nd ref='1410056' />\n    <nd ref='1410058' />\n    <nd ref='1410053' />\n  </way>\n  <way id='1410097' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <nd ref='1410095' />\n    <nd ref='1410096' />\n    <nd ref='1410098' />\n    <nd ref='1410100' />\n    <tag k='waterway' v='canal' />\n  </way>\n  <relation id='1410065' uid='1' version='1' changeset='1' user='myself' timestamp='2017-12-19T21:43:02Z' action='modify'>\n    <member type='way' ref='1410046' role='outer' />\n    <member type='way' ref='1410055' role='inner' />\n    <tag k='building' v='yes' />\n    <tag k='type' v='multipolygon' />\n  </relation>\n</osm>\n"
  },
  {
    "path": "src/test/resources/org/openstreetmap/atlas/utilities/testing/test.txt",
    "content": "# Nodes\n# Edges\n# Areas\n239827929000000 && 52.346592,71.8723874:52.346584,71.8796899:52.3465303,71.8797567:52.3444064,71.8797609:52.3442191,71.8797695:52.3440986,71.8797946:52.3416541,71.8814599:52.3409671,71.8787783:52.3409054,71.878822:52.3386909,71.8803888:52.3378493,71.8771887:52.3389324,71.8764438:52.3392035,71.8762553:52.3394755,71.8760651:52.3400197,71.875701:52.3402505,71.8755576:52.3433362,71.8734473:52.3448185,71.872364 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1399187086000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || name -> 1-й микрорайон || landuse -> residential || place -> quarter\n250786481000000 && 52.346066,71.875656:52.3460655,71.8760318:52.346065,71.8764076:52.3459397,71.8764074:52.3459397,71.8764363:52.3457286,71.8764355:52.3457286,71.876444:52.3456566,71.8764437:52.3456569,71.8762796:52.3458863,71.8762804:52.3458869,71.8757842:52.3457253,71.8757836:52.3457253,71.8757994:52.3456552,71.8757991:52.3456554,71.8756375:52.3459497,71.8756385:52.3459497,71.8756558 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1386518512000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || building:part -> yes\n30736305000000 && 52.3456566,71.8764437:52.3456569,71.8762796:52.3458863,71.8762804:52.3458869,71.8757842:52.3457253,71.8757836:52.3457253,71.8757994:52.3456552,71.8757991:52.3456554,71.8756375:52.3459497,71.8756385:52.3459499,71.8754652:52.346231,71.8754662:52.3462308,71.8756566:52.346066,71.875656:52.3460655,71.8760318:52.346065,71.8764076:52.3462312,71.8764082:52.346231,71.8766225:52.3459394,71.8766215:52.3459397,71.8764363:52.3457286,71.8764355:52.3457286,71.876444 && last_edit_user_name -> Alexkravkaa28 || addr:housenumber -> 57 || addr:quarter -> 1-й микрорайон || last_edit_time -> 1419539871000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || addr:postcode -> 021501 || building -> school || addr:place -> 1-й микрорайон\n240444646000000 && 52.3464608,71.8753975:52.3464658,71.8766449:52.3454493,71.8766558:52.3454443,71.8754084 && last_edit_user_name -> Alexkravkaa28 || official_name -> Государственное учреждение «Средняя школа №1 имени Н. Островского» отдела образования города Степногорска || website -> http://sc0001.stepnogorsk.akmoedu.kz || last_edit_time -> 1419539872000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || amenity -> school || name -> Средняя школа №1\n250786489000000 && 52.3462312,71.8764082:52.346065,71.8764076:52.3459397,71.8764074:52.3459397,71.8764363:52.3459394,71.8766215:52.346231,71.8766225 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1386518513000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || building:part -> yes\n250786488000000 && 52.346231,71.8754662:52.3459499,71.8754652:52.3459497,71.8756385:52.3459497,71.8756558:52.346066,71.875656:52.3462308,71.8756566 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1386518513000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || building:part -> yes\n# Lines\n143036812000000 && 53.3152381,68.9218283:53.3136845,68.9223195:53.310899,68.9232135:53.309391,68.9232144:53.3062417,68.9256717:53.3033049,68.9279479:53.3005592,68.9301003:53.2977231,68.9322798:53.2953613,68.9340881:53.2926259,68.9361922:53.2898651,68.9382932:53.2871382,68.9404107:53.2843027,68.9425795:53.281638,68.9446244:53.2790033,68.9466386:53.2761459,68.9488393:53.274339,68.950246:53.2716759,68.9522816:53.2699765,68.9535936:53.2686116,68.957361:53.2668531,68.9621669:53.2651414,68.9668762:53.2633719,68.9717848:53.2624113,68.9744111:53.2609703,68.9783838:53.2598399,68.9815355:53.2581316,68.9862266:53.256481,68.9908212:53.254787,68.9954339:53.2530315,69.00027:53.2513952,69.0048465:53.2497082,69.0094652:53.2480031,69.0141504:53.2462726,69.01892:53.2445529,69.0236354:53.2428187,69.0283809:53.2410628,69.033217:53.2393898,69.0378237:53.2377024,69.0424786:53.2359101,69.0474415:53.2341899,69.0521327:53.2331456,69.0550247:53.2314036,69.0597762:53.2297484,69.0643165:53.2278943,69.0694605:53.2261594,69.0741939:53.2245257,69.0786557:53.2228593,69.0832382:53.2212001,69.0877422:53.219436,69.0926327:53.217737,69.0972695:53.2160233,69.1019607:53.2143422,69.1065311:53.2125923,69.1114034:53.2111207,69.1153641:53.2094177,69.1200492:53.2077327,69.1246559:53.2059572,69.1295222:53.204272,69.1341168:53.2025579,69.1387838:53.2008292,69.1435172:53.1991221,69.1481963:53.1973535,69.1530566:53.1957223,69.1575063:53.1939752,69.1622458:53.1922715,69.1669369:53.1911176,69.1700704:53.189591,69.1742726:53.1881259,69.1782695:53.186422,69.1829003:53.1848554,69.1871567:53.1831224,69.1918842:53.1815666,69.1960923:53.1799239,69.2005722:53.1785345,69.2043457:53.176812,69.2090972:53.175133,69.2136858:53.1738266,69.2172178:53.1720569,69.2220478:53.1703668,69.2266364:53.169111,69.2301019:53.1673049,69.2349561:53.1657124,69.2393454:53.1648545,69.2416941:53.163204,69.2461498:53.1615316,69.2506719:53.1602031,69.2542763:53.1585849,69.2586958:53.1569884,69.2630489:53.1556127,69.2667439:53.1541102,69.2708434:53.1528828,69.2741641:53.1510363,69.2792235:53.1496459,69.2830031:53.1480432,69.2873414:53.1460226,69.2928296:53.1448113,69.296114:53.1427268,69.3017645:53.1410052,69.3064644:53.1394966,69.3105585:53.1377473,69.3152877:53.1360506,69.3198956:53.1343664,69.324466:53.1325743,69.3293163:53.1310854,69.3333728:53.129391,69.3379725:53.1276112,69.342781:53.1259718,69.3472093:53.1245488,69.3510815:53.1248722,69.3539181:53.1252382,69.3574775:53.1255917,69.3611371:53.1261432,69.3662757:53.126617,69.3709839:53.1271058,69.3757381:53.1276122,69.3805216:53.1281085,69.3853802:53.1286224,69.3901553:53.1291438,69.3954192:53.1296473,69.4002766:53.1301719,69.4054167:53.1307507,69.4110154:53.1312412,69.4157608:53.1317634,69.4208121:53.1322516,69.4255349:53.1326484,69.4293728:53.1332097,69.434803:53.1337259,69.4397967:53.1342686,69.4449049:53.1345492,69.4478068:53.1341181,69.4528475:53.133716,69.4575493:53.1331979,69.4636067:53.1327459,69.4688914:53.1322967,69.4741429:53.1319125,69.4786347:53.1313725,69.4849482:53.1309837,69.4894947:53.1305971,69.4940139:53.130184,69.4988433:53.1297448,69.5039781:53.1293375,69.5087404:53.1288753,69.5141436:53.1283908,69.5198081:53.127911,69.5254172:53.1276103,69.5289323:53.1281995,69.5323209:53.1286619,69.5349803:53.1294377,69.5394425:53.1302257,69.5439748:53.131084,69.5489119:53.1319321,69.5537898:53.1328663,69.5591634:53.1337788,69.5644124:53.1347847,69.5701983:53.1355822,69.5747862:53.1365601,69.5804117:53.1374018,69.5852533:53.1383544,69.5907334:53.139218,69.5957016:53.1400941,69.6007418:53.1410456,69.6062162:53.14191,69.6111892:53.1428347,69.6165094:53.1437639,69.6218555:53.1444462,69.6257814:53.1450029,69.6289845:53.145969,69.6345432:53.1469085,69.6399489:53.1478564,69.6454029:53.1488195,69.6509451:53.1497144,69.6560942:53.1506016,69.6611997:53.1515287,69.6665345:53.1524495,69.6718337:53.15338,69.6771885:53.1542717,69.6823199:53.1551046,69.6871136:53.1558949,69.6916616:53.1567277,69.696455:53.1575701,69.701303:53.1583985,69.7060709:53.1592549,69.7109999:53.1610254,69.7128117:53.1624222,69.7180858:53.163744,69.723077:53.1650091,69.7278542:53.1663126,69.7327766:53.1675874,69.7375907:53.1688797,69.742471:53.1697877,69.7459003:53.1708927,69.7500733:53.1717451,69.7532926:53.1728869,69.757605:53.1734181,69.7596115:53.1746161,69.7641364:53.1759003,69.7689867:53.1771439,69.7736843:53.1783556,69.7782612:53.1795966,69.7829491:53.1808247,69.7875882:53.1816887,69.7908521:53.1828866,69.7953774:53.1840979,69.7999535:53.1853453,69.8046661:53.1865584,69.8092491:53.1878284,69.8140474:53.1887943,69.817697:53.1901054,69.8226508:53.1912866,69.8271137:53.1924987,69.8316939:53.1936999,69.8362326:53.1949066,69.8407926:53.1960237,69.8450139:53.1971627,69.8493182:53.1972059,69.8539362:53.1972631,69.8600513:53.1973011,69.8641139:53.197351,69.8694526:53.197404,69.8751231:53.1974487,69.8798967:53.1974956,69.8849182:53.1975173,69.8872298:53.1975629,69.8921083:53.1976087,69.8970029:53.1976516,69.9015905:53.1976953,69.906262:53.1977455,69.9116318:53.1977898,69.9163668:53.1962026,69.9193053:53.1940461,69.9232977:53.1922235,69.9266716:53.1901482,69.9305134:53.1881921,69.9341342:53.1860004,69.9381908:53.1838088,69.9422474:53.181648,69.9462463:53.1798845,69.9495101:53.1797241,69.9543007:53.1795565,69.9593049:53.1793744,69.9647442:53.1792137,69.9695426:53.1790099,69.9756286:53.1788371,69.9807872:53.1786699,69.9857818:53.1785068,69.9906531:53.1783526,69.9952559:53.1782039,69.9996961:53.177386,70.0033528:53.176242,70.0084669:53.1750231,70.0139159:53.173823,70.0192806:53.1727032,70.024286:53.1715321,70.0295207:53.17042,70.0344921:53.169369,70.0391896:53.1682858,70.0440309:53.1671079,70.0492957:53.1658768,70.054798:53.1648989,70.0591686:53.1648289,70.0627096:53.1647815,70.0661549:53.1647341,70.0698953:53.1646678,70.0757902:53.1646046,70.0798624:53.1645509,70.0848249:53.1644562,70.0908516:53.1643899,70.0963093:53.1643141,70.1014772:53.1642193,70.106935:53.1641688,70.1129774:53.164093,70.1174236:53.164033,70.1222913:53.163973,70.1266585:53.1638908,70.1319266:53.163833,70.1370247:53.1637351,70.1436414:53.1636593,70.1492835:53.1635898,70.1551521:53.1634856,70.1619163:53.1634256,70.1668366:53.1633656,70.1712987:53.1632898,70.1768196:53.1632234,70.1820771:53.1631445,70.1880932:53.1630813,70.1925658:53.1630055,70.1983238:53.1629265,70.2036234:53.1628571,70.2088599:53.1627876,70.2135537:53.162737,70.2175522:53.1626549,70.2238686:53.1625807,70.2296492:53.1624749,70.2353861:53.1623343,70.2409834:53.1622048,70.2461856:53.162088,70.2506898:53.1620005,70.2543632:53.1618836,70.2596259:53.1617478,70.2643988:53.1616246,70.2693481:53.1614825,70.2753827:53.161394,70.2790466:53.1613072,70.2824498:53.1611953,70.2867954:53.1610807,70.2907276:53.1609318,70.2968515:53.1608168,70.3013872:53.1606668,70.3073928:53.1605468,70.3117178:53.160447,70.3152277:53.1602326,70.322797:53.1601701,70.3265817:53.1600539,70.3309177:53.1598395,70.3384126:53.1596525,70.3446248:53.1595467,70.3492555:53.1594251,70.3537623:53.1592791,70.3597665:53.1602511,70.3662401:53.1610949,70.3718604:53.161586,70.3751311:53.1623576,70.3802704:53.1631652,70.3856498:53.1639401,70.3908108:53.1647148,70.3959712:53.1653879,70.4004548:53.1660746,70.4050288:53.1668879,70.4104467:53.1674938,70.4144825:53.1679887,70.4177795:53.1686471,70.4221651:53.1693302,70.4267162:53.1698473,70.4301605:53.1705717,70.4349868:53.1713103,70.4399069:53.1720466,70.4448122:53.1725843,70.4483948:53.1712953,70.4517738:53.1696086,70.4561952:53.1679674,70.4604969:53.1663005,70.464866:53.1646567,70.4691743:53.1630963,70.4732638:53.1613805,70.4777603:53.1596562,70.4822792:53.1579335,70.4867936:53.1562254,70.4912696:53.1545398,70.4956865:53.153161,70.4992992:53.1524778,70.5031731:53.1515921,70.5081951:53.150646,70.5135593:53.1496306,70.5193161:53.148699,70.5245982:53.1478866,70.5292041:53.1470177,70.53413:53.1461309,70.5391579:53.145217,70.5443389:53.1443046,70.5495112:53.1434141,70.5545592:53.1424146,70.5602256:53.1415062,70.5653748:53.1405955,70.5705371:53.1397094,70.5755598:53.1387465,70.5810179:53.1380462,70.5849875:53.1370078,70.590873:53.1362089,70.5954014:53.135395,70.6000144:53.1346793,70.6040706:53.1332133,70.6067474:53.1317248,70.609465:53.1298212,70.6129406:53.127793,70.6166433:53.125941,70.6200241:53.1235481,70.6243923:53.1212362,70.6286123:53.119119,70.6324768:53.1167984,70.6367124:53.114742,70.6404655:53.1126791,70.6442301:53.110508,70.6481922:53.1086534,70.6515765:53.106205,70.6560442:53.1040521,70.6599725:53.1018457,70.6639981:53.1000045,70.6673574:53.0975933,70.6717561:53.0952161,70.6760929:53.0935949,70.6790502:53.093587,70.6827588:53.093575,70.6884266:53.0935634,70.6939107:53.0935516,70.6994685:53.0935394,70.7052175:53.0935266,70.7112816:53.0935146,70.7169015:53.093503,70.7223931:53.0934912,70.7279349:53.0934794,70.7335057:53.0934679,70.738947:53.0934563,70.7443935:53.0934486,70.748041:53.0934366,70.7537092:53.0934247,70.7593255:53.0934134,70.7646232:53.0934021,70.7699465:53.0933911,70.7751688:53.09338,70.7803658:53.0933693,70.785452:53.09336,70.7898134:53.0916165,70.7935609:53.0893775,70.7983731:53.0871175,70.8032302:53.0850213,70.8077351:53.0827324,70.812654:53.0804984,70.8174545:53.0786811,70.8213594:53.0781516,70.8277597:53.077644,70.8338967:53.0771671,70.8396615:53.0766925,70.8453987:53.0761621,70.8518112:53.0756781,70.857662:53.0751767,70.8637235:53.0746722,70.8698211:53.0741626,70.8759822:53.0736602,70.8820547:53.0731628,70.8880678:53.0726915,70.8937646:53.0722006,70.8996979:53.0717068,70.905667:53.0711986,70.91181:53.0706887,70.9179726:53.0701934,70.9239592:53.0697092,70.9298124:53.0692059,70.9358951:53.068729,70.9416597:53.0682391,70.9475813:53.0677465,70.953534:53.0672569,70.9594522:53.0667639,70.9654108:53.0662756,70.9713114:53.0657804,70.9772966:53.0653504,70.9824934:53.0649574,70.9872434:53.0641822,70.9914576:53.0632551,70.9964975:53.0622628,71.0018912:53.0612813,71.0072267:53.0603025,71.0125471:53.0593402,71.017778:53.0582854,71.0235115:53.0572995,71.0288701:53.0562994,71.0343055:53.055346,71.0394876:53.0543587,71.0448536:53.0533855,71.0501427:53.0527128,71.0537988:53.051245,71.0561946:53.048932,71.0599697:53.0466969,71.0636175:53.0450357,71.0663287:53.0450301,71.0690212:53.0450197,71.0740079:53.0450076,71.0798402:53.0449951,71.0858751:53.0449823,71.0920317:53.0449688,71.0985025:53.0449558,71.1047737:53.0449431,71.11091:53.0449329,71.1158016:53.0425925,71.118572:53.0398779,71.1217098:53.036699,71.1254076:53.0337414,71.1288481:53.0306682,71.1324298:53.0275949,71.1360014:53.0246244,71.139452:53.0215299,71.1430332:53.0185166,71.1465342:53.015497,71.1500352:53.0124924,71.1535312:53.0095118,71.1569868:53.0064916,71.1605029:53.0034924,71.1639585:53.0016861,71.1660672:52.9986167,71.1696086:52.9956109,71.1730995:52.9924166,71.1768124:52.9894772,71.1802277:52.9864191,71.183769:52.983373,71.1872801:52.9803327,71.1908316:52.9773104,71.1943225:52.9744003,71.1976974:52.9714809,71.2010622:52.9686373,71.2043665:52.9656993,71.2077616:52.9625423,71.211419:52.9598682,71.2145164:52.9570421,71.2178056:52.953848,71.2214832:52.9510609,71.2247118:52.9480913,71.2281472:52.9451215,71.2315927:52.9422305,71.2349172:52.939391,71.2385695:52.9192566,71.2615286:52.8815592,71.2869023:52.8185103,71.3291203:52.8114167,71.3338642:52.8080875,71.3361044:52.804689,71.3383517:52.8010661,71.3407845:52.7974341,71.3432387:52.7941082,71.3454574:52.7906353,71.3477547:52.7871794,71.3500804:52.7838182,71.3523135:52.7804955,71.3545465:52.777151,71.3567581:52.7735948,71.3591552:52.7699001,71.3616237:52.7669951,71.3635357:52.7638309,71.3656831:52.7607122,71.3677717:52.7575154,71.3698967:52.7561871,71.3744694:52.7548616,71.3790639:52.7534885,71.3838153:52.7518414,71.3894979:52.7505386,71.3943087:52.7491834,71.3987519:52.7477634,71.4037064:52.746464,71.40823:52.7451088,71.4129222:52.7437613,71.4176058:52.742363,71.4224227:52.7404114,71.4291791:52.7389072,71.4344153:52.7373712,71.4397093:52.7357431,71.4453544:52.7343911,71.4499775:52.7328042,71.4555702:52.7313203,71.4606466:52.7298349,71.4657702:52.7282716,71.4712057:52.7270174,71.4758264:52.6879498,71.6098617:52.6864865,71.6150983:52.6851969,71.6195645:52.6836074,71.6249242:52.6722283,71.6636239:52.6728464,71.6672503:52.6647413,71.6880378:52.6625886,71.6934993:52.6606866,71.6983982:52.6586632,71.703495:52.656674,71.7086073:52.6546209,71.7138628:52.6528723,71.71832:52.6509227,71.7232468:52.649028,71.7282239:52.6471894,71.7328816:52.6450562,71.7382311:52.6430845,71.7432146:52.6412088,71.7480143:52.6392162,71.7530809:52.6372999,71.757988:52.635424,71.762784:52.6332331,71.768362:52.6310781,71.7738548:52.6291502,71.7788027:52.6276227,71.7826499:52.6258724,71.7871012:52.6236833,71.7926792:52.62225,71.7963337:52.6201665,71.8016374:52.6181166,71.8068263:52.6160824,71.8121597:52.6141336,71.8172892:52.6121419,71.8225707:52.6102222,71.8276558:52.6082461,71.8328743:52.6063037,71.8380558:52.6043539,71.8432676:52.6024434,71.8482556:52.6004332,71.8535816:52.5984972,71.8587111:52.5965319,71.8639037:52.5945349,71.8691889:52.5926234,71.8742629:52.5908785,71.8788365:52.5893226,71.8829838:52.5873726,71.8881319:52.5856364,71.8927537:52.5837966,71.8975719:52.5819483,71.9024459:52.5802668,71.9069106:52.5785085,71.9115242:52.5768681,71.9158747:52.5749601,71.9208908:52.5731666,71.9256449:52.5714592,71.9301316:52.569758,71.9346446:52.568095,71.9389846:52.5662089,71.944006:52.5642367,71.9491637:52.5623046,71.9543:52.5604007,71.959311:52.5589185,71.9631905:52.5590271,71.9675696:52.559142,71.9735331:52.5592412,71.9780103:52.5593493,71.9838033:52.5594732,71.989726:52.5595972,71.9952225:52.5597346,72.0012268:52.5598338,72.0061117:52.5599554,72.0122864:52.5600546,72.0173085:52.5601672,72.0228087:52.5602551,72.0274638:52.5603723,72.0331605:52.5604962,72.038883:52.5606043,72.0443795:52.5607238,72.0501984:52.560837,72.0557643:52.5609294,72.0613204:52.5610537,72.0670284:52.5611589,72.0719397:52.5612672,72.0775587:52.5613883,72.0833715:52.5614934,72.0890219:52.5616081,72.0949553:52.5617324,72.101025:52.5618216,72.1059887:52.56193,72.1116601:52.5620319,72.1169907:52.5621435,72.1222689:52.5622518,72.1278145:52.5623601,72.1334596:52.5624716,72.1393092:52.5625736,72.1445822:52.5626628,72.150285:52.5627266,72.1532359:52.5628158,72.158315:52.5629241,72.1632263:52.5630388,72.1694952:52.5631408,72.1750433:52.5632507,72.1806544:52.5633542,72.1865721:52.5634881,72.1925029:52.5635741,72.1980851:52.5636856,72.2038141:52.5637862,72.2093947:52.5638853,72.2148764:52.5639822,72.2200949:52.5640881,72.2256284:52.564167,72.2303095:52.5642075,72.2338157:52.5642683,72.2389119:52.5643494,72.2445529:52.564395,72.2502245:52.5644775,72.2555723:52.5645515,72.2618627:52.5646038,72.2663536:52.5646699,72.2715742:52.5647113,72.2775077:52.5648077,72.2836603:52.5648641,72.289158:52.5649449,72.2947922:52.5650159,72.3007023:52.5651118,72.3065278:52.5651511,72.3123889:52.565208,72.3173835:52.5652922,72.3230922:52.5653583,72.3289853:52.5654088,72.3345988:52.565487,72.3405718:52.5655473,72.3463667:52.5656248,72.3520861:52.5656758,72.3577096:52.5657654,72.3634944:52.5658079,72.3675:52.5658956,72.373498:52.5659472,72.3787359:52.5660119,72.3847062:52.5660634,72.3901254:52.566122,72.3958083:52.5661987,72.4016958:52.5662555,72.4075335:52.5662931,72.4132777:52.5663461,72.4176295:52.5664132,72.4227259:52.5664795,72.4283174:52.5665392,72.4342579:52.5665922,72.4396058:52.5666641,72.4454191:52.566722,72.450576:52.5667751,72.4562342:52.5668501,72.462026:52.5669036,72.4677591:52.5669364,72.4736615:52.567009,72.4783181:52.5654002,72.4821914:52.5632875,72.4873175:52.5611846,72.4923944:52.5591435,72.4973482:52.5570974,72.5023144:52.5549702,72.5074713:52.5528854,72.5125176:52.550681,72.5179149:52.5488677,72.5223212:52.5464697,72.5280899:52.5443029,72.5335296:52.5423142,72.538198:52.5404099,72.5427945:52.5382678,72.5479565:52.5361851,72.5530355:52.5340587,72.5581597:52.5320241,72.5630859:52.5299496,72.5681139:52.5278705,72.5731491:52.5260236,72.57757:52.5239112,72.5826816:52.5215775,72.588324:52.5195158,72.5933447:52.5176221,72.5978892:52.5155115,72.6029789:52.5133977,72.6081498:52.5113201,72.6130618:52.5092419,72.618191:52.5071305,72.6231908:52.5052087,72.6277898:52.5031462,72.6328105:52.5015638,72.6366642:52.4991982,72.6383994:52.4957972,72.6408914:52.492464,72.6434631:52.4890041,72.6458515:52.4854781,72.6484782:52.4827402,72.6504916:52.4823771,72.6558213:52.4819852,72.6616273:52.4815845,72.6675242:52.4811926,72.6736756:52.4807741,72.6796161:52.4804396,72.6846711:52.47945,72.6892213:52.4781849,72.6950261:52.4768854,72.7008359:52.4756515,72.7064915:52.4744686,72.7119172:52.4731877,72.7176756:52.4719412,72.7234084:52.4707698,72.7287092:52.4493108,72.8255247:52.4434712,72.9976109:52.443286,73.0031313:52.4432371,73.0049962:52.4430222,73.0105199:52.4428283,73.016329:52.4426379,73.0219746:52.4424228,73.027871:52.4422373,73.0335185:52.4420288,73.0392443:52.4418274,73.0451893:52.4416352,73.0506978:52.4414476,73.0562782:52.4412515,73.0619258:52.4410844,73.0668281:52.4398174,73.0716803:52.4383453,73.077318:52.4369904,73.0825065:52.4356193,73.0877569:52.4342288,73.0930815:52.4327884,73.0985969:52.4313614,73.1040608:52.4299823,73.109341:52.428518,73.1149477:52.4270857,73.1204312:52.4256284,73.1260104:52.4239707,73.132357:52.4225731,73.1377074:52.4211991,73.142967:52.4197464,73.1485279:52.4182626,73.1542077:52.4168893,73.1594645:52.4155982,73.164406:52.4144219,73.1689083:52.4117341,73.1730814:52.4088884,73.1774992:52.4062544,73.1815882:52.403549,73.1857876:52.4007143,73.1901877:52.3982765,73.1939713:52.3958509,73.1977359:52.3934628,73.2014421:52.3908136,73.2055531:52.3881383,73.2097044:52.3854763,73.213835:52.3828671,73.2178833:52.3803183,73.2218377:52.37759,73.2260702:52.3749217,73.2302095:52.3722487,73.2343558:52.369513,73.2385991:52.3667627,73.2428648:52.3641113,73.2469768:52.3615314,73.2509777:52.3592396,73.2545318:52.3564849,73.2588031:52.3538187,73.2629372:52.3510102,73.2672916:52.3484044,73.2713316:52.3459909,73.2750731:52.3433171,73.2792179:52.3406466,73.2833573:52.3379956,73.2874664:52.3379018,73.2930353:52.3378017,73.2991421:52.3377006,73.3051112:52.3376137,73.3107548:52.3375148,73.3169541:52.3374215,73.3229614:52.3373257,73.3289723:52.3372313,73.3349605:52.3371257,73.3409737:52.3370271,73.3468909:52.3369257,73.3530273:52.3368336,73.359023:52.3367285,73.365184:52.3366214,73.3713279:52.3365263,73.3772027:52.3364327,73.3831916:52.3363328,73.3891308:52.3362336,73.3951879:52.336124,73.4010433:52.336043,73.4067505:52.3359427,73.4126271:52.3358383,73.4187312:52.3357385,73.4245944:52.3356298,73.4304559:52.3355338,73.4363948:52.3354293,73.4423406:52.3353332,73.4482582:52.3352202,73.4544115:52.3351051,73.4604637:52.3349973,73.4665248:52.3349021,73.4728112:52.3347986,73.4787458:52.334692,73.4846779:52.3345889,73.4906094:52.3344689,73.496792:52.3343661,73.502877:52.3342637,73.5086372:52.3341567,73.5146267:52.3340438,73.5205999:52.33394,73.5263717:52.3338304,73.5325073:52.3337134,73.5386982:52.3336049,73.5446993:52.3334969,73.5507922:52.3333744,73.5568669:52.333275,73.563181:52.3331642,73.5692609:52.3330502,73.5754247:52.3329605,73.5802414:52.3319248,73.58406:52.3304891,73.5894813:52.329039,73.5948526:52.3276581,73.5999199:52.3261777,73.6054913:52.3246781,73.6110918:52.3231962,73.6165817:52.3217562,73.6219015:52.3203744,73.6270755:52.3187756,73.6330378:52.3174385,73.6380173:52.3159751,73.6433788:52.3143843,73.6493733:52.3130026,73.6545313:52.3114626,73.6601621:52.3099654,73.6657558:52.3085531,73.6709934:52.3071188,73.6763751:52.3056161,73.6819103:52.3040949,73.6875934:52.3027343,73.6925682:52.3014022,73.6975561:52.3000663,73.7024558:52.2985444,73.7081023:52.2972192,73.7130261:52.2958286,73.7181236:52.2943525,73.7235469:52.2930291,73.7284337:52.2916508,73.7334896:52.2903438,73.7383207:52.2890537,73.7432038:52.2877585,73.7479992:52.2864834,73.7527448:52.2852295,73.7573519:52.2838383,73.7624927:52.2825352,73.7672168:52.2812452,73.7720431:52.27989,73.7770529:52.2785979,73.781714:52.277354,73.7864267:52.2760636,73.7911892:52.274707,73.7962541:52.2733266,73.8012423:52.2720065,73.806192:52.2706294,73.8113218:52.2692701,73.8162733:52.2678655,73.8214271:52.2664322,73.8267093:52.2649948,73.8318495:52.2637084,73.8367201:52.2623874,73.8415586:52.2610554,73.8464399:52.259757,73.8512995:52.2582701,73.8566368:52.2568044,73.8620409:52.2553606,73.867483:52.2538944,73.872891:52.2523685,73.8783529:52.2510042,73.8833321:52.2496095,73.8885989:52.2481411,73.8940121:52.2467367,73.8991426:52.2453403,73.9043321:52.2439015,73.9096081:52.2426455,73.9144908:52.2410314,73.9200443:52.2396794,73.9251636:52.2382169,73.9305533:52.2368194,73.9356165:52.2353996,73.9410059:52.233869,73.9466623:52.2324468,73.9517358:52.2311514,73.9566861:52.2296822,73.9620965:52.2282322,73.9674992:52.2268049,73.9727703:52.2253962,73.9779391:52.2241017,73.9827332:52.2227723,73.9876194:52.2215022,73.9923602:52.2202397,73.9967209:52.2193105,74.000389:52.2177214,74.0042699:52.2159389,74.0085629:52.2140645,74.0133462:52.2122247,74.0179212:52.2101356,74.0230122:52.2082699,74.0276527:52.2062831,74.0325309:52.2042668,74.0375719:52.2022972,74.0423651:52.2004353,74.0469622:52.1984153,74.0518984:52.1964819,74.0567844:52.1944037,74.0618819:52.1923854,74.0668384:52.1904047,74.0718002:52.1883403,74.0768052:52.1863223,74.0818247:52.1843944,74.0866141:52.1823619,74.0916783:52.1803134,74.0967032:52.1782234,74.1018351:52.1763526,74.1064527:52.1743111,74.1114728:52.1723394,74.1163786:52.1703654,74.1212487:52.1681282,74.1267804:52.1661339,74.1316383:52.1641664,74.1365073:52.1621408,74.1415041:52.1601871,74.1463197:52.1582941,74.1509524:52.156565,74.1552241:52.154807,74.1595762:52.1533403,74.1631738:52.1526171,74.1676469:52.1516642,74.173191:52.1507618,74.1785433:52.1498914,74.1838862:52.1295192,74.3046518:52.1286409,74.3098849:52.1276678,74.3156764:52.1266947,74.3215005:52.1257215,74.3272447:52.124735,74.3329744:52.1237596,74.3387586:52.1227998,74.3444191:52.1218444,74.3502106:52.1208154,74.3561548:52.1198443,74.3619753:52.1188532,74.367774:52.1178879,74.3734926:52.1169042,74.37926:52.1159509,74.3849133:52.1149084,74.3910793:52.1139216,74.396918:52.1129459,74.4026876:52.1119658,74.4084536:52.1109946,74.4141433:52.1100099,74.4199566:52.1090408,74.4256305:52.1078149,74.4328253:52.1069061,74.4381878:52.1059235,74.4439774:52.1049177,74.4498695:52.1039292,74.4556614:52.1029527,74.4614825:52.1019858,74.4671803:52.10102,74.4727722:52.100048,74.478581:52.0990581,74.4842844:52.0982891,74.4887515:52.0979222,74.4934642:52.0974729,74.4993284:52.0970055,74.5053182:52.0965443,74.511272:52.0960895,74.517195:52.0956251,74.5231436:52.0951514,74.5292157:52.0947186,74.5347685:52.0943332,74.5394884:52.0940174,74.5436941:52.0935404,74.5496325:52.0930836,74.5556113:52.0925989,74.5616754:52.0921231,74.5677323:52.0916741,74.5738691:52.0912296,74.5794897:52.0907538,74.5855793:52.0902713,74.5915816:52.0898067,74.5975112:52.0893281,74.6035071:52.0888691,74.6094331:52.0883733,74.6156605:52.0879461,74.6210951:52.0874836,74.626817:52.0870988,74.6317295:52.0866604,74.6370991:52.0862359,74.642689:52.0858421,74.6475583:52.0854294,74.6529609:52.0850061,74.6581847:52.0845795,74.6635267:52.0841341,74.6691129:52.0836532,74.6751317:52.0832907,74.6797468:52.0828839,74.6849564:52.0824596,74.6901807:52.0820179,74.6955241:52.0816036,74.7010565:52.0812172,74.7059007:52.0808151,74.7109433:52.0804243,74.7158486:52.0799913,74.7211431:52.0795821,74.726359:52.0791558,74.7315953:52.078723,74.7370225:52.0783032,74.7423434:52.0778653,74.74786:52.0774199,74.7529302:52.0770123,74.7581659:52.0765684,74.7637969:52.0761264,74.7692056:52.0756864,74.7746294:52.0753249,74.7789688:52.0754181,74.7834523:52.0754624,74.7893701:52.0755615,74.7952482:52.075623,74.8011038:52.0757169,74.8069604:52.0757861,74.8128259:52.0758767,74.8186608:52.0759635,74.8246023:52.0760191,74.8304693:52.0761004,74.8364046:52.0761727,74.8423293:52.0762435,74.8481061:52.0763132,74.8541736:52.0763865,74.860177:52.0764565,74.8661817:52.0765419,74.8716558:52.0766058,74.8771419:52.0766663,74.8822567:52.0767275,74.8866364:52.076827,74.8914525:52.0769306,74.8970623:52.0770407,74.9029754:52.0771385,74.9084525:52.0772341,74.9140455:52.0773397,74.9198549:52.0774526,74.9255837:52.0775344,74.9313376:52.0776524,74.9370984:52.0777551,74.9427173:52.077866,74.9482752:52.0779584,74.9538909:52.0780723,74.9594395:52.0781771,74.9650509:52.0782764,74.9709799:52.0784139,74.9767366:52.0784913,74.982574:52.0785874,74.9878467:52.0786956,74.9932691:52.0787825,74.9985441:52.0788762,75.0038523:52.0789878,75.0091033:52.0790625,75.014554:52.0791295,75.0187515:52.0771217,75.021018:52.0745317,75.0240024:52.0720218,75.0269232:52.0694068,75.0299018:52.0667651,75.0329098:52.0640888,75.0359539:52.0613577,75.0390663:52.0585823,75.0422148:52.0558122,75.0453609:52.0530272,75.0485464:52.0500862,75.0518755:52.0470109,75.0553691:52.0440238,75.0587712:52.0409136,75.0622946:52.0378586,75.0657698:52.034993,75.0690334:52.0317879,75.0726708:52.0287482,75.0761246:52.0257306,75.0795493:52.0225633,75.0831321:52.0195263,75.0865817:52.0165239,75.0899959:52.0134815,75.0934471:52.0106358,75.0966734:52.0074693,75.1002399:52.0045057,75.103596:52.0015366,75.1068893:51.9984356,75.1104649:51.9954993,75.1137935:51.9924836,75.1172724:51.9894115,75.1207158:51.9863726,75.1241393:51.9834107,75.1274186:51.9804451,75.1309398:51.9774171,75.1343242:51.9742996,75.1378186:51.9712455,75.1412937:51.9682697,75.1446588:51.9652241,75.1481269:51.962157,75.1515193:51.9591549,75.1549735:51.9561268,75.158396:51.9532314,75.1616441:51.9502445,75.1649681:51.9480444,75.1674579:51.9450486,75.1684128:51.9415764,75.1695715:51.9380841,75.1706551:51.9346114,75.1717602:51.9311516,75.172876:51.9273475,75.1740884:51.9239203,75.1751827:51.9205318,75.1761775:51.9169877,75.1773924:51.9132995,75.1785516:51.9098785,75.1796672:51.9064889,75.1807724:51.9029144,75.1818882:51.8994588,75.1829826:51.8958706,75.1841627:51.8924409,75.1852142:51.8889479,75.1863927:51.8859028,75.1873661:51.8846101,75.1915896:51.8831746,75.1962792:51.8817257,75.2010126:51.8802302,75.205823:51.8810826,75.2113012:51.8812558,75.2123768 && note -> по проекту напряжение 1150 кВ, однако фактически эксплуатируется на 500 кВ || cables -> 3 || iso_country_code -> KAZ || description -> Экибастуз-Кокчетав (1150кВ) || description:en -> Ekibastus-Kokshetau || voltage -> 500000 || frequency -> 50 || last_edit_user_name -> parukhin || ref -> ВЛ-1101 || last_edit_time -> 1454703522000 || last_edit_user_id -> 519501 || name -> ВЛ 1104 (ПС «Экибастузская» - ПС «Алтай») || voltage:design -> 1150000 || power -> line || circuits -> 1\n# Points\n2571571410000000 && 52.3460655,71.8760318 && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1398623371000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || entrance -> main\n# Relations\n3367576000000 && 30736305000000 -> outline -> A || 250786481000000 -> part -> A || 250786488000000 -> part -> A || 250786489000000 -> part -> A || 2571571410000000 -> entrance -> P && last_edit_user_name -> Alexkravkaa28 || last_edit_time -> 1386518513000 || last_edit_user_id -> 1725868 || iso_country_code -> KAZ || building:part -> yes || type -> building\n"
  }
]